PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.10.12
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.10.12
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.12, at js/render-google.js

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