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

656 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( 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 // Single-pass: walk columns, skip annotation/helper roles, apply formats in order.
356 var k = 0; // index into settings.series (visible series)
357 for (var c = 1; c < series.length && k < settings.series.length; c++) { // skip label at 0
358 if (table.getColumnProperty(c, 'role')) continue; // helper/annotation column
359 var s = settings.series[k++];
360 if (!s || !s.format) continue;
361 if (!series[c]) continue;
362 format_data(id, table, series[c].type, s.format, c);
363 }
364 break;
365 }
366 } else if (chart.type === 'pie' && settings.format && settings.format !== '') {
367 format_data(id, table, 'number', settings.format, 1);
368 }
369
370 if(settings.hAxis && series[0]) {
371 format_data(id, table, series[0].type, settings.hAxis.format, 0);
372 }
373
374 override(settings);
375
376 gv.events.addListener(render, 'ready', function () {
377 var arr = id.split('-');
378 render = typeof render.getChart !== 'undefined' ? render.getChart() : render;
379 __visualizer_chart_images[ arr[0] + '-' + arr[1] ] = '';
380
381 if (render.container && $(render.container).is(':visible')) {
382 if ($(render.container).parents('div').next( '#sidebar' ).length === 0) {
383 $(render.container).addClass( 'visualizer-chart-loaded' );
384 }
385 }
386
387 try{
388 if ( typeof render.getImageURI !== 'undefined' ) {
389 var img = render.getImageURI();
390 __visualizer_chart_images[ arr[0] + '-' + arr[1] ] = img;
391 $('body').trigger('visualizer:render:chart', {id: arr[1], image: img});
392 if ( $( '#chart-img' ).length ) {
393 $( '#chart-img' ).val( img );
394 }
395 }
396 }catch(error){
397 var canvas = document.getElementById( 'canvas' );
398 domtoimage.toPng(canvas)
399 .then(function ( img ) {
400 __visualizer_chart_images[ arr[0] + '-' + arr[1] ] = img;
401 $('body').trigger('visualizer:render:chart', {id: arr[1], image: img});
402 if ( $( '#chart-img' ).length ) {
403 $( '#chart-img' ).val( img );
404 }
405 })
406 .catch(function (error) {
407 console.warn('render.getImageURI not defined for ' + arr[0] + '-' + arr[1]);
408 });
409 }
410 });
411
412 if ( withControlMode ) {
413 // alert( chart.is_library_page );
414 // Create a control wrapper, passing some options.
415 var controlWrapper = new gv.ControlWrapper( {
416 containerId: 'control_wrapper_' + id,
417 controlType: settings.controls.controlType,
418 'options': settings.controls,
419 } );
420
421 $('body').trigger('visualizer:chart:settings:extend', {id: id, chart: chart, settings: settings, data: table});
422 render.setOptions(settings);
423 chartWrapper.bind(controlWrapper, render);
424 chartWrapper.draw(table);
425
426 gv.events.addListener(controlWrapper, 'ready', function ( err ) {
427 if ( 'vertical' === settings.controls.ui.orientation ) {
428 if ( 'canvas' === id ) {
429 jQuery( '#' + id ).addClass( 'vz-vertical' );
430 }
431 } else {
432 jQuery( '#' + id ).removeClass( 'vz-vertical' );
433 }
434 });
435 } else {
436 $('body').trigger('visualizer:chart:settings:extend', {id: id, chart: chart, settings: settings, data: table});
437 render.draw(table, settings);
438 }
439 }
440
441 function format_data(id, table, type, format, index) {
442 if (!format || format === '') {
443 return;
444 }
445
446 var formatter = null;
447 switch (type) {
448 case 'number':
449 formatter = new gv.NumberFormat({pattern: format});
450 break;
451 case 'date':
452 case 'datetime':
453 case 'timeofday':
454 formatter = new gv.DateFormat({pattern: format});
455 break;
456 }
457
458 if (formatter) {
459 formatter.format(table, index);
460 }
461
462 var arr = id.split('-');
463 $('body').trigger('visualizer:format:chart', {id: parseInt(arr[1]), data: table, column: index});
464
465 }
466
467 function override(settings) {
468 if (settings.manual) {
469 try{
470 var options = JSON.parse(settings.manual);
471 $.extend(settings, options);
472 delete settings.manual;
473 }catch(error){
474 console.error("Error while adding manual configuration override " + settings.manual);
475 }
476 }
477 }
478
479 function render() {
480 for (var id in (all_charts || {})) {
481 var chartElement = document.getElementById( id );
482 if (chartElement && chartElement.offsetParent) {
483 renderChart(id);
484 }
485 }
486 }
487
488 var resizeTimeout;
489
490 $(document).ready(function() {
491 $(window).resize(function(e) {
492 clearTimeout(resizeTimeout);
493 window.isResizeRequest = 'undefined' !== typeof e.originalEvent ? true : false;
494 resizeTimeout = setTimeout(render, 100);
495 });
496
497 resizeHiddenContainers(true);
498
499 if ( $( '.visualizer-hidden-container' ).length ) {
500 setInterval( function() {
501 $( '.visualizer-hidden-container' ).find(".visualizer-front:not(.visualizer-chart-loaded)").resize();
502 }, 500 );
503 }
504
505 window.addEventListener( 'orientationchange', function() {
506 $( '.visualizer-chart-loaded' ).removeClass( 'visualizer-chart-loaded' ).resize();
507 }, false);
508 });
509
510 $(window).on('load', function(){
511 $( '.visualizer-front:not(.visualizer-chart-loaded)' ).resize();
512 resizeHiddenContainers(true);
513 });
514
515 function resizeHiddenContainers(everytime){
516 $(".visualizer-front").parents().each(function(){
517 if(!$(this).is(":visible") && !$(this).hasClass("visualizer-hidden-container")){
518 $(this).addClass("visualizer-hidden-container");
519 }
520 });
521
522 var mutateObserver = new MutationObserver(function(records) {
523 records.forEach(function(record) {
524 if(record.attributeName === "style" || record.attributeName === "class"){
525 var element = $(record.target);
526 var displayStyle = window.getComputedStyle(element[0]).getPropertyValue("display");
527 if(element.hasClass("visualizer-hidden-container-resized") || displayStyle === "none") { return ; }
528 element.find(".visualizer-front").resize();
529 if(!everytime) {
530 element.addClass("visualizer-hidden-container-resized");
531 }
532 }
533 });
534 });
535
536 $('.visualizer-hidden-container').each(function(){
537 mutateObserver.observe($(this)[0], {attributes: true});
538 });
539 }
540
541 $('body').on('visualizer:render:chart:start', function(event, v){
542 var $chart_types = ['corechart', 'geochart', 'gauge', 'table', 'timeline'];
543 if(v.is_front == true){ // jshint ignore:line
544 // check what all chart types to load.
545 $chart_types = [];
546 $.each(v.charts, function(i, c){
547 var $type = c.type;
548 switch($type){
549 case 'bar':
550 case 'column':
551 case 'line':
552 case 'area':
553 case 'stepped area':
554 case 'bubble':
555 case 'pie':
556 case 'donut':
557 case 'combo':
558 case 'candlestick':
559 case 'histogram':
560 case 'scatter':
561 case 'bubble':
562 $type = 'corechart';
563 break;
564 case 'geo':
565 $type = 'geochart';
566 break;
567 case 'tabular':
568 case 'table':
569 $type = 'table';
570 break;
571 case 'dataTable':
572 case 'polarArea':
573 case 'radar':
574 $type = null;
575 break;
576 }
577 if($type != null){
578 $chart_types.push($type);
579 }
580 });
581 }
582
583 objects = {};
584 if ( 'object' === typeof google ) {
585 $chart_types.push( 'controls' );
586 google.load( 'visualization', '51', {packages: $chart_types, mapsApiKey: v.map_api_key, 'language' : v.language,
587 callback: function () {
588 gv = google.visualization;
589 all_charts = v.charts;
590 if(v.is_front == true && typeof v.id !== 'undefined'){ // jshint ignore:line
591 if ( document.getElementById( v.id ).offsetParent !== null ) {
592 renderChart(v.id);
593 }
594 } else {
595 render();
596 }
597 }
598 });
599 }
600 });
601
602 $('body').on('visualizer:render:specificchart:start', function(event, v){
603 objects = {};
604 gv = google.visualization;
605 renderSpecificChart(v.id, v.chart);
606 });
607
608 $('body').on('visualizer:render:currentchart:update', function(event, v){
609 renderChart('canvas');
610 });
611
612 // front end actions
613 // 'image' is also called from the library
614 $('body').on('visualizer:action:specificchart', function(event, v){
615 var id = v.id;
616 if(typeof rendered_charts[id] === 'undefined'){
617 return;
618 }
619 var arr = id.split('-');
620 var img = __visualizer_chart_images[ arr[0] + '-' + arr[1] ];
621 switch(v.action){
622 case 'print':
623 // for charts that have no rendered image defined, we print the data instead.
624 var html = v.data;
625 if(img !== ''){
626 html = "<html><body><img src='" + img + "' /></body></html>";
627 }
628 $('body').trigger('visualizer:action:specificchart:defaultprint', {data: html});
629 break;
630 case 'image':
631 if(img !== ''){
632 var $a = $("<a>"); // jshint ignore:line
633 $a.attr("href", img);
634 $("body").append($a);
635 $a.attr("download", v.dataObj.name);
636 $a[0].click();
637 $a.remove();
638 }else{
639 console.warn("No image generated");
640 }
641 break;
642 }
643 });
644
645 var timer = 0;
646 $( document ).on( 'input', '.series-linewidth', function() {
647 var seriesLineWidth = $( this );
648 clearTimeout( timer );
649 timer = setTimeout( function() {
650 if ( seriesLineWidth.val() != '' && seriesLineWidth.val() <= 0 ) {
651 seriesLineWidth.val( '0.1' );
652 }
653 }, 700 );
654 } );
655 })(jQuery);
656