PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.10.7
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.10.7
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-google.js

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

660 lines 24.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /* global google */
2 /* global visualizer */
3 /* global console */
4
5 // this will store the images for each chart rendered.
6 var __visualizer_chart_images = [];
7 var chartWrapperError = [];
8 var isResizeRequest = false;
9
10 (function($) {
11 var gv;
12 var all_charts, objects;
13 // so that we know which charts belong to our library.
14 var rendered_charts = [];
15
16 function renderChart(id) {
17
18 if ( ! all_charts || 0 === Object.keys( all_charts ).length ) {
19 return;
20 }
21
22 var chart = all_charts[id];
23 var hasAnnotation = false;
24
25 if ( ! chart ) {
26 return;
27 }
28
29 // re-render the chart only if it doesn't have annotations and it is on the front-end
30 // this is to prevent the chart from showing "All series on a given axis must be of the same data type" during resize.
31 // remember, some charts do not support annotations so they should not be included in this.
32 var no_annotation_charts = ['tabular', 'timeline', 'gauge', 'geo', 'bubble', 'candlestick'];
33 if ( undefined !== chart.settings && undefined !== chart.settings.series && undefined === chart.settings.series.length ) {
34 var chartSeries = [];
35 var chartSeriesValue = Object.values( chart.settings.series );
36 $.each( Object.keys( chart.settings.series ), function( index, element ) {
37 chartSeries[element] = chartSeriesValue[index];
38 } );
39 chart.settings.series = chartSeries;
40 }
41 if(id !== 'canvas' && typeof chart.series !== 'undefined' && typeof chart.settings.series !== 'undefined' && ! no_annotation_charts.includes(chart.type) ) {
42 hasAnnotation = chart.series.length - chart.settings.series.length > 1;
43 }
44
45 if(! hasAnnotation){
46 renderSpecificChart(id, all_charts[id]);
47 }
48 }
49
50 function renderSpecificChart(id, chart) {
51 var render, container, series, data, table, settings, i, j, row, date, axis, property, format, formatter;
52
53 if ( ! window.isResizeRequest && ( $('#' + id).hasClass('visualizer-chart-loaded') || ( 'canvas' !== id && $('#' + id).children( ':not(.loader)' ).length > 0 ) ) ) {
54 return;
55 }
56
57 if(chart.library !== 'google'){
58 return;
59 }
60 rendered_charts[id] = 'yes';
61
62 series = chart.series;
63 data = chart.data;
64 settings = chart.settings;
65
66 container = document.getElementById(id);
67 if (container == null) {
68 return;
69 }
70 table = new gv.DataTable({cols: series});
71
72 render = objects[id] || null;
73 if (!render) {
74 switch (chart.type) {
75 case "tabular":
76 render = "Table";
77 break;
78 case "gauge":
79 case "table":
80 case "timeline":
81 render = chart.type.charAt(0).toUpperCase() + chart.type.slice(1);
82 break;
83 default:
84 render = chart.type.charAt(0).toUpperCase() + chart.type.slice(1) + 'Chart';
85 break;
86 }
87
88 var controlWrapperElement = document.getElementById( "control_wrapper_" + id ) || null;
89 var withControlMode = ( typeof settings.controls !== 'undefined' && '' !== settings.controls.controlType ) && controlWrapperElement ? true : false;
90
91 if ( $( controlWrapperElement ).hasClass( 'no-filter' ) ) {
92 withControlMode = false;
93 }
94 if ( withControlMode ) {
95 // Chart wrapper.
96 render = new gv.ChartWrapper({
97 'chartType': render,
98 'containerId': id,
99 });
100 // Chart dashboard wrapper.
101 var chartWrapperElement = document.getElementById( "chart_wrapper_" + id ) || null;
102 var chartWrapper = new gv.Dashboard( chartWrapperElement );
103
104 // Error Handler.
105 gv.events.addListener(chartWrapper, 'error', function ( err ) {
106 if ( chartWrapperError.length === 0 ) {
107 chartWrapperError.push( err );
108 gv.errors.removeError( err.id );
109 var chartContainer = $( chartWrapperElement ).find( '.visualizer-front' );
110 if ( chartContainer && chartContainer.is(':visible')) {
111 if (chartContainer.parents('div').next( '#sidebar' ).length === 0) {
112 chartContainer.addClass( 'visualizer-chart-loaded' ).addClass( 'visualizer-cw-error' );
113 $( chartWrapperElement ).addClass( 'visualizer-cw-error' );
114 }
115 }
116 } else {
117 gv.errors.removeError( err.id );
118 }
119 } );
120 } else {
121 render = new gv[render](container);
122 }
123 }
124
125 if (settings['animation'] && parseInt(settings['animation']['startup']) === 1)
126 {
127 settings['animation']['startup'] = true;
128 settings['animation']['duration'] = parseInt(settings['animation']['duration']);
129 }
130 if ( settings['controls'] ) {
131 settings['controls']['ui']['allowMultiple'] = 'true' === settings['controls']['allowMultiple'] ? true : false;
132 settings['controls']['ui']['allowTyping'] = 'true' === settings['controls']['allowTyping'] ? true : false;
133 settings['controls']['ui']['showRangeValues'] = 'true' === settings['controls']['showRangeValues'] ? true : false;
134 }
135
136 // mark roles for series that have specified a role
137 // and then remove them from future processing
138 // and also adjust the indices of the series array so that
139 // the ones with a role are ignored
140 // e.g. if there are 6 columns (0-5) out of which 1, 3 and 5 are annotations
141 // the final series will only include 0, 2, 4 (reindexed as 0, 1, 2)
142
143 // this will capture all the series indexes that became annotations.
144 var series_annotations = [];
145 if (settings.series) {
146 var adjusted_series = [];
147 for (i = 0; i < settings.series.length; i++) {
148 if (!series[i + 1] || typeof settings.series[i] === 'undefined') {
149 continue;
150 }
151 if(typeof settings.series[i].role !== 'undefined'){
152 table.setColumnProperty(i + 1, 'role', settings.series[i].role);
153 if(settings.series[i].role === '') {
154 adjusted_series.push(settings.series[i]);
155 }else{
156 series_annotations.push(i);
157 }
158 }
159 }
160 if(adjusted_series.length > 0){
161 settings.series = adjusted_series;
162 }
163 }
164
165 if ( settings['explorer_enabled'] && settings['explorer_enabled'] == 'true' ) { // jshint ignore:line
166 var $explorer = {};
167 $explorer['keepInBounds'] = true;
168
169 if ( settings['explorer_actions'] ) {
170 $explorer['actions'] = settings['explorer_actions'];
171 }
172 settings['explorer'] = $explorer;
173 }
174
175 switch (chart.type) {
176 case 'pie':
177 if (settings.slices) {
178 for (i in settings.slices) {
179 if (settings.slices[i]['color'] === '') {
180 delete settings.slices[i]['color'];
181 }
182 }
183 }
184 break;
185 case 'line':
186 case 'bar':
187 case 'column':
188 case 'area':
189 case 'scatter':
190 case 'candlestick':
191 if (settings.series) {
192 for (i in settings.series) {
193 if (settings.series[i]['color'] === '') {
194 delete settings.series[i]['color'];
195 }
196 }
197 }
198
199 if (settings.series && settings.legend && settings.legend.position === "left")
200 {
201 settings.targetAxisIndex = 1;
202 }
203 break;
204 case 'geo':
205 if (settings.region !== undefined && settings.region.replace(/^\s+|\s+$/g, '') === '') {
206 settings['region'] = 'world';
207 }
208 break;
209 case 'table':
210 case 'tabular':
211 if (parseInt(settings['pagination']) !== 1)
212 {
213 delete settings['pageSize'];
214 }
215 break;
216 case 'gauge':
217 break;
218 case 'bubble':
219 settings.sortBubblesBySize = settings.sortBubblesBySize ? settings.sortBubblesBySize == 1 : false; // jshint ignore:line
220 break;
221 case 'timeline':
222 settings['timeline'] = {};
223 settings['timeline']['groupByRowLabel'] = settings['groupByRowLabel'] ? true : false;
224 settings['timeline']['colorByRowLabel'] = settings['colorByRowLabel'] ? true : false;
225 settings['timeline']['showRowLabels'] = settings['showRowLabels'] ? true : false;
226 if(settings['singleColor'] !== '') {
227 settings['timeline']['singleColor'] = settings['singleColor'];
228 }
229 break;
230 case 'combo':
231 if (settings.series) {
232 for (i in settings.series) {
233 if (settings.series[i]['type'] === '') {
234 delete settings.series[i]['type'];
235 }
236 if (settings.series[i]['color'] === '') {
237 delete settings.series[i]['color'];
238 }
239 }
240 }
241
242 if (settings.series && settings.legend && settings.legend.position === "left")
243 {
244 settings.targetAxisIndex = 1;
245 }
246 break;
247 default:
248 return;
249 }
250
251 if (series[0] && (series[0].type === 'date' || series[0].type === 'datetime' || series[0].type === 'timeofday')) {
252 axis = false;
253 switch (chart.type) {
254 case 'line':
255 case 'area':
256 case 'scatter':
257 case 'candlestick':
258 case 'column':
259 case 'combo':
260 axis = settings.hAxis;
261 break;
262 case 'bar':
263 axis = settings.vAxis;
264 break;
265 }
266
267 if (axis) {
268 for (property in axis.viewWindow) {
269 date = new Date(axis.viewWindow[property]);
270 if (Object.prototype.toString.call(date) === "[object Date]") {
271 if (!isNaN(date.getTime())) {
272 axis.viewWindow[property] = date;
273 continue;
274 }
275 }
276
277 delete axis.viewWindow[property];
278 }
279 }
280
281 if(settings.hAxis && settings.hAxis.format == ''){
282 settings.hAxis.format = 'yyyy-MM-dd';
283 }
284 }
285
286 if(settings.hAxis){
287 if(settings.hAxis.textStyle && settings.hAxis.textStyle !== ''){
288 if(typeof(settings.hAxis.textStyle) === "string") {
289 settings.hAxis.textStyle = {color: settings.hAxis.textStyle};
290 }
291 }
292 }
293
294 if(settings.vAxis){
295 if(settings.vAxis.textStyle && settings.vAxis.textStyle !== ''){
296 if(typeof(settings.vAxis.textStyle) === "string") {
297 settings.vAxis.textStyle = {color: settings.vAxis.textStyle};
298 }
299 }
300 }
301
302 for (i = 0; i < data.length; i++) {
303 row = [];
304 for (j = 0; j < series.length; j++) {
305 switch(series[j].type) {
306 case 'string':
307 if(data[i][j] === ''){
308 data[i][j] = null;
309 }
310 break;
311 case 'boolean':
312 if (typeof data[i][j] === 'string'){
313 data[i][j] = data[i][j] === 'true';
314 }
315 break;
316 case 'date':
317 // fall-through.
318 case 'datetime':
319 if (typeof data[i][j] === 'string') {
320 var dateParse = Date.parse(data[i][j].replace(/-/g, '/'));
321 if ( isNaN( dateParse ) ) {
322 dateParse = Date.parse(data[i][j]);
323 }
324 date = new Date( dateParse );
325 } else if(typeof data[i][j] === 'object') {
326 date = data[i][j];
327 }
328 data[i][j] = null;
329 if (Object.prototype.toString.call(date) === "[object Date]") {
330 if (!isNaN(date.getTime())) {
331 data[i][j] = date;
332 }
333 }
334 break;
335 }
336 row.push(data[i][j]);
337 }
338 table.addRow(row);
339 }
340
341 if (settings.series) {
342 switch(chart.type){
343 case 'table':
344 case 'tabular':
345 for(i in settings.series){
346 i = parseInt(i);
347 if (!series[i + 1]) {
348 continue;
349 }
350 format_data(id, table, series[i + 1].type, settings.series[i].format, i + 1);
351 }
352 break;
353 default:
354 for (i = 0; i < settings.series.length; i++) {
355 if (!series[i + 1] || typeof settings.series[i] === 'undefined') {
356 continue;
357 }
358 var seriesIndexToUse = i + 1;
359
360 // if an annotation "swallowed" a series, use the following one.
361 if(series_annotations.includes(i)){
362 seriesIndexToUse++;
363 }
364 if ( series[seriesIndexToUse] ) {
365 format_data(id, table, series[seriesIndexToUse].type, settings.series[i].format, seriesIndexToUse);
366 }
367 }
368 break;
369 }
370 } else if (chart.type === 'pie' && settings.format && settings.format !== '') {
371 format_data(id, table, 'number', settings.format, 1);
372 }
373
374 if(settings.hAxis && series[0]) {
375 format_data(id, table, series[0].type, settings.hAxis.format, 0);
376 }
377
378 override(settings);
379
380 gv.events.addListener(render, 'ready', function () {
381 var arr = id.split('-');
382 render = typeof render.getChart !== 'undefined' ? render.getChart() : render;
383 __visualizer_chart_images[ arr[0] + '-' + arr[1] ] = '';
384
385 if (render.container && $(render.container).is(':visible')) {
386 if ($(render.container).parents('div').next( '#sidebar' ).length === 0) {
387 $(render.container).addClass( 'visualizer-chart-loaded' );
388 }
389 }
390
391 try{
392 if ( typeof render.getImageURI !== 'undefined' ) {
393 var img = render.getImageURI();
394 __visualizer_chart_images[ arr[0] + '-' + arr[1] ] = img;
395 $('body').trigger('visualizer:render:chart', {id: arr[1], image: img});
396 if ( $( '#chart-img' ).length ) {
397 $( '#chart-img' ).val( img );
398 }
399 }
400 }catch(error){
401 var canvas = document.getElementById( 'canvas' );
402 domtoimage.toPng(canvas)
403 .then(function ( img ) {
404 __visualizer_chart_images[ arr[0] + '-' + arr[1] ] = img;
405 $('body').trigger('visualizer:render:chart', {id: arr[1], image: img});
406 if ( $( '#chart-img' ).length ) {
407 $( '#chart-img' ).val( img );
408 }
409 })
410 .catch(function (error) {
411 console.warn('render.getImageURI not defined for ' + arr[0] + '-' + arr[1]);
412 });
413 }
414 });
415
416 if ( withControlMode ) {
417 // alert( chart.is_library_page );
418 // Create a control wrapper, passing some options.
419 var controlWrapper = new gv.ControlWrapper( {
420 containerId: 'control_wrapper_' + id,
421 controlType: settings.controls.controlType,
422 'options': settings.controls,
423 } );
424
425 $('body').trigger('visualizer:chart:settings:extend', {id: id, chart: chart, settings: settings, data: table});
426 render.setOptions(settings);
427 chartWrapper.bind(controlWrapper, render);
428 chartWrapper.draw(table);
429
430 gv.events.addListener(controlWrapper, 'ready', function ( err ) {
431 if ( 'vertical' === settings.controls.ui.orientation ) {
432 if ( 'canvas' === id ) {
433 jQuery( '#' + id ).addClass( 'vz-vertical' );
434 }
435 } else {
436 jQuery( '#' + id ).removeClass( 'vz-vertical' );
437 }
438 });
439 } else {
440 $('body').trigger('visualizer:chart:settings:extend', {id: id, chart: chart, settings: settings, data: table});
441 render.draw(table, settings);
442 }
443 }
444
445 function format_data(id, table, type, format, index) {
446 if (!format || format === '') {
447 return;
448 }
449
450 var formatter = null;
451 switch (type) {
452 case 'number':
453 formatter = new gv.NumberFormat({pattern: format});
454 break;
455 case 'date':
456 case 'datetime':
457 case 'timeofday':
458 formatter = new gv.DateFormat({pattern: format});
459 break;
460 }
461
462 if (formatter) {
463 formatter.format(table, index);
464 }
465
466 var arr = id.split('-');
467 $('body').trigger('visualizer:format:chart', {id: parseInt(arr[1]), data: table, column: index});
468
469 }
470
471 function override(settings) {
472 if (settings.manual) {
473 try{
474 var options = JSON.parse(settings.manual);
475 $.extend(settings, options);
476 delete settings.manual;
477 }catch(error){
478 console.error("Error while adding manual configuration override " + settings.manual);
479 }
480 }
481 }
482
483 function render() {
484 for (var id in (all_charts || {})) {
485 var chartElement = document.getElementById( id );
486 if (chartElement && chartElement.offsetParent) {
487 renderChart(id);
488 }
489 }
490 }
491
492 var resizeTimeout;
493
494 $(document).ready(function() {
495 $(window).resize(function(e) {
496 clearTimeout(resizeTimeout);
497 window.isResizeRequest = 'undefined' !== typeof e.originalEvent ? true : false;
498 resizeTimeout = setTimeout(render, 100);
499 });
500
501 resizeHiddenContainers(true);
502
503 if ( $( '.visualizer-hidden-container' ).length ) {
504 setInterval( function() {
505 $( '.visualizer-hidden-container' ).find(".visualizer-front:not(.visualizer-chart-loaded)").resize();
506 }, 500 );
507 }
508
509 window.addEventListener( 'orientationchange', function() {
510 $( '.visualizer-chart-loaded' ).removeClass( 'visualizer-chart-loaded' ).resize();
511 }, false);
512 });
513
514 $(window).on('load', function(){
515 $( '.visualizer-front:not(.visualizer-chart-loaded)' ).resize();
516 resizeHiddenContainers(true);
517 });
518
519 function resizeHiddenContainers(everytime){
520 $(".visualizer-front").parents().each(function(){
521 if(!$(this).is(":visible") && !$(this).hasClass("visualizer-hidden-container")){
522 $(this).addClass("visualizer-hidden-container");
523 }
524 });
525
526 var mutateObserver = new MutationObserver(function(records) {
527 records.forEach(function(record) {
528 if(record.attributeName === "style" || record.attributeName === "class"){
529 var element = $(record.target);
530 var displayStyle = window.getComputedStyle(element[0]).getPropertyValue("display");
531 if(element.hasClass("visualizer-hidden-container-resized") || displayStyle === "none") { return ; }
532 element.find(".visualizer-front").resize();
533 if(!everytime) {
534 element.addClass("visualizer-hidden-container-resized");
535 }
536 }
537 });
538 });
539
540 $('.visualizer-hidden-container').each(function(){
541 mutateObserver.observe($(this)[0], {attributes: true});
542 });
543 }
544
545 $('body').on('visualizer:render:chart:start', function(event, v){
546 var $chart_types = ['corechart', 'geochart', 'gauge', 'table', 'timeline'];
547 if(v.is_front == true){ // jshint ignore:line
548 // check what all chart types to load.
549 $chart_types = [];
550 $.each(v.charts, function(i, c){
551 var $type = c.type;
552 switch($type){
553 case 'bar':
554 case 'column':
555 case 'line':
556 case 'area':
557 case 'stepped area':
558 case 'bubble':
559 case 'pie':
560 case 'donut':
561 case 'combo':
562 case 'candlestick':
563 case 'histogram':
564 case 'scatter':
565 case 'bubble':
566 $type = 'corechart';
567 break;
568 case 'geo':
569 $type = 'geochart';
570 break;
571 case 'tabular':
572 case 'table':
573 $type = 'table';
574 break;
575 case 'dataTable':
576 case 'polarArea':
577 case 'radar':
578 $type = null;
579 break;
580 }
581 if($type != null){
582 $chart_types.push($type);
583 }
584 });
585 }
586
587 objects = {};
588 if ( 'object' === typeof google ) {
589 $chart_types.push( 'controls' );
590 google.load( 'visualization', '51', {packages: $chart_types, mapsApiKey: v.map_api_key, 'language' : v.language,
591 callback: function () {
592 gv = google.visualization;
593 all_charts = v.charts;
594 if(v.is_front == true && typeof v.id !== 'undefined'){ // jshint ignore:line
595 if ( document.getElementById( v.id ).offsetParent !== null ) {
596 renderChart(v.id);
597 }
598 } else {
599 render();
600 }
601 }
602 });
603 }
604 });
605
606 $('body').on('visualizer:render:specificchart:start', function(event, v){
607 objects = {};
608 gv = google.visualization;
609 renderSpecificChart(v.id, v.chart);
610 });
611
612 $('body').on('visualizer:render:currentchart:update', function(event, v){
613 renderChart('canvas');
614 });
615
616 // front end actions
617 // 'image' is also called from the library
618 $('body').on('visualizer:action:specificchart', function(event, v){
619 var id = v.id;
620 if(typeof rendered_charts[id] === 'undefined'){
621 return;
622 }
623 var arr = id.split('-');
624 var img = __visualizer_chart_images[ arr[0] + '-' + arr[1] ];
625 switch(v.action){
626 case 'print':
627 // for charts that have no rendered image defined, we print the data instead.
628 var html = v.data;
629 if(img !== ''){
630 html = "<html><body><img src='" + img + "' /></body></html>";
631 }
632 $('body').trigger('visualizer:action:specificchart:defaultprint', {data: html});
633 break;
634 case 'image':
635 if(img !== ''){
636 var $a = $("<a>"); // jshint ignore:line
637 $a.attr("href", img);
638 $("body").append($a);
639 $a.attr("download", v.dataObj.name);
640 $a[0].click();
641 $a.remove();
642 }else{
643 console.warn("No image generated");
644 }
645 break;
646 }
647 });
648
649 var timer = 0;
650 $( document ).on( 'input', '.series-linewidth', function() {
651 var seriesLineWidth = $( this );
652 clearTimeout( timer );
653 timer = setTimeout( function() {
654 if ( seriesLineWidth.val() != '' && seriesLineWidth.val() <= 0 ) {
655 seriesLineWidth.val( '0.1' );
656 }
657 }, 700 );
658 } );
659 })(jQuery);
660