PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 4.0.7
Visualizer – Tables & Charts Manager with Built-in AI Generator v4.0.7
4.0.8 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 All 149 releases
visualizer / js / frame.js

frame.js in Visualizer – Tables & Charts Manager with Built-in AI Generator 4.0.7, at js/frame.js

1,027 lines 40.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /* global prompt */
2 /* global visualizer */
3 /* global alert */
4 /* global confirm */
5 /* global ajaxurl */
6 /* global CodeMirror */
7 /* global vizHaveSettingsChanged */
8
9 (function ($) {
10 $(window).on('load', function(){
11 let chart_select = $('#chart-select');
12 if(chart_select.length > 0){
13 // scroll to the selected chart type.
14 $('#chart-select')[0].scrollIntoView();
15 }
16 });
17
18 $(document).ready(function () {
19 onReady();
20 });
21
22 function initTabs(){
23 $( "#sidebar" ).lock();
24 $( "#viz-tabs" ).tabs({
25 create: function( event, ui ) {
26 $(this).addClass('done');
27 $( "#sidebar" ).unlock();
28 },
29 beforeActivate: function( event, ui ) {
30 // if settings have changed in tab #2 and tab #1 is being activated, warn the user.
31 if(vizHaveSettingsChanged() && ui.newTab.find( 'a' ).attr( 'id' ).includes('viz-tab-basic')){
32 return confirm(visualizer.l10n.save_settings);
33 }
34 }
35 });
36 }
37
38 function onReady() {
39 initAccessibility();
40 initTabs();
41
42 // open the correct source tab/sub-tab.
43 var source = $('#visualizer-chart-id').attr('data-chart-source');
44 $('li.viz-group.' + source).addClass('open');
45 $('li.viz-group.' + source + ' span.viz-section-title.' + source).addClass('open');
46 $('li.viz-group.' + source + ' span.viz-section-title.' + source + '.open').parent().find('div.viz-section-items').show();
47
48 init_permissions();
49
50 if(typeof visualizer !== 'undefined' && visualizer.is_pro) {
51 init_db_import();
52 init_filter_import();
53 }
54
55 init_json_import();
56
57 init_editor_table();
58
59 // update the manual configuation link to point to the correct chart type.
60 var type = $('#visualizer-chart-id').attr('data-chart-type');
61 var chart_type_in_api_link = type + 'chart';
62 switch (type) {
63 case "gauge":
64 case "table":
65 case "timeline":
66 chart_type_in_api_link = type;
67 break;
68 }
69
70 if($('span.viz-gvlink').length > 0) {
71 $('span.viz-gvlink').html($('span.viz-gvlink').html().replace('?', chart_type_in_api_link));
72 }
73
74 $('.type-radio').change(function () {
75 $('.type-label-selected').removeClass('type-label-selected');
76 $(this).parent().addClass('type-label-selected');
77 updateTypeAriaChecked();
78 });
79
80 // collapse other open sections of this group
81 $(document).on('click', '.viz-group-title', function () {
82 var parent = $(this).parent();
83
84 if (parent.hasClass('open')) {
85 parent.removeClass('open');
86 } else {
87 parent.parent().find('.viz-group.open').removeClass('open');
88 parent.addClass('open');
89 }
90
91 /*
92 * if the user wants to perform an action and click that tab to change the source
93 * and the chart is no longer showing because that particular LHS screen is showing
94 * e.g. create parameters, import json, db query box etc.
95 * we need to make sure we cancel the current process WITHOUT saving
96 * and then show the chart as it was
97 * let's close the LHS window and show the chart that is hidden
98 */
99 $('body').trigger('visualizer:change:action');
100 updateGroupAriaState($(this));
101 });
102
103 // collapse other open subsections of this section
104 $(document).on('click', '.viz-section-title', function () {
105 var grandparent = $(this).parent().parent();
106 grandparent.find('.viz-section-title.open').not(this).each(function () {
107 $(this).removeClass('open').siblings('.viz-section-items').hide();
108 });
109 updateSectionAriaState($(this));
110 });
111
112 $('#view-remote-file').click(function () {
113 var url = $(this).parent().find('#vz-schedule-url').val();
114
115 if (url !== '') {
116 if (url.indexOf('localhost') !== -1 || /^([a-z]([a-z]|\d|\+|-|\.)*):(\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?((\[(|(v[\da-f]{1,}\.(([a-z]|\d|-|\.|_|~)|[!\$&'\(\)\*\+,;=]|:)+))\])|((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=])*)(:\d*)?)(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*|(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)|((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)|((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)){0})(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(url)) {
117 if (url.substr(url.length - 8) === '/pubhtml') {
118 url = url.substring(0, url.length - 8) + '/export?format=csv';
119 }
120
121 $('#canvas').lock();
122 $(this).parent().submit();
123 } else {
124 alert(visualizer.l10n.invalid_source);
125 }
126 }
127 });
128
129 $('#vz-import-file').click(function (e) {
130 e.preventDefault();
131 if ($.trim($(this).parent().find("#csv-file").val()) !== '') {
132 $('#canvas').lock();
133 $(this).parent().submit();
134 }
135 });
136
137 $( window ).on( 'load', function() {
138 $('#canvas').unlock();
139 } );
140
141 $(document).on('click', '.viz-section-title', function () {
142 $(this).toggleClass('open').parent().find('.viz-section-items').toggle();
143 });
144
145 $(document).on('click', '.more-info', function () {
146 $(this).parent().find('.viz-section-description:first').toggle();
147 return false;
148 });
149
150 $('#cancel-button').click(function () {
151 $('#cancel-form').submit();
152 });
153
154 init_available_chart_list();
155
156 $('.viz-abort').on('click', function(e){
157 e.preventDefault();
158 window.parent.postMessage('visualizer:mediaframe:close', '*');
159 });
160
161 }
162
163 function initAccessibility(){
164 // Chart type picker: treat tiles as radios with keyboard support.
165 var $typePicker = $('#type-picker');
166 if($typePicker.length){
167 $typePicker.attr('role', 'radiogroup');
168 $('.type-label').each(function(){
169 var $label = $(this);
170 var $input = $label.find('input[type="radio"]');
171 $label.attr('tabindex', '0').attr('role', 'radio');
172 if($input.length){
173 $label.attr('aria-checked', $input.is(':checked') ? 'true' : 'false');
174 }else{
175 $label.attr('aria-disabled', 'true');
176 }
177 });
178 $(document).on('keydown', '.type-label', function(e){
179 if(e.key === 'Enter' || e.key === ' '){
180 e.preventDefault();
181 var $label = $(this);
182 var $input = $label.find('input[type="radio"]');
183 if($input.length){
184 $input.prop('checked', true).trigger('change');
185 }
186 }
187 });
188 }
189
190 // Collapsible groups: add button roles + keyboard activation.
191 $('.viz-group-title').each(function(index){
192 var $title = $(this);
193 $title.attr('role', 'button').attr('tabindex', '0');
194 var $content = $title.siblings('.viz-group-content');
195 if($content.length){
196 if(!$content.attr('id')){
197 $content.attr('id', 'viz-group-content-' + index);
198 }
199 $title.attr('aria-controls', $content.attr('id'));
200 updateGroupAriaState($title);
201 }
202 });
203 $(document).on('keydown', '.viz-group-title', function(e){
204 if(e.key === 'Enter' || e.key === ' '){
205 e.preventDefault();
206 $(this).trigger('click');
207 }
208 });
209
210 // Subsections in source panel.
211 $('.viz-section-title').each(function(index){
212 var $title = $(this);
213 $title.attr('role', 'button').attr('tabindex', '0');
214 var $items = $title.siblings('.viz-section-items');
215 if($items.length){
216 if(!$items.attr('id')){
217 $items.attr('id', 'viz-section-items-' + index);
218 }
219 $title.attr('aria-controls', $items.attr('id'));
220 updateSectionAriaState($title);
221 }
222 });
223 $(document).on('keydown', '.viz-section-title', function(e){
224 if(e.key === 'Enter' || e.key === ' '){
225 e.preventDefault();
226 $(this).trigger('click');
227 }
228 });
229 }
230
231 function updateTypeAriaChecked(){
232 $('.type-label').each(function(){
233 var $label = $(this);
234 var $input = $label.find('input[type="radio"]');
235 if($input.length){
236 $label.attr('aria-checked', $input.is(':checked') ? 'true' : 'false');
237 }
238 });
239 }
240
241 function updateGroupAriaState($title){
242 if(!$title || $title.length === 0){
243 return;
244 }
245 var isOpen = $title.parent().hasClass('open');
246 $title.attr('aria-expanded', isOpen ? 'true' : 'false');
247 }
248
249 function updateSectionAriaState($title){
250 if(!$title || $title.length === 0){
251 return;
252 }
253 var isOpen = $title.hasClass('open');
254 $title.attr('aria-expanded', isOpen ? 'true' : 'false');
255 }
256
257 /**
258 * Initialize/Update the available chart list based on their supported renderer libraries.
259 *
260 * @returns {void}
261 */
262 function init_available_chart_list() {
263 const rendererSelect = document.querySelector('select.viz-select-library');
264 if ( ! rendererSelect ) {
265 return;
266 }
267
268 const rendererTypesMappingData = rendererSelect?.getAttribute('data-type-vs-library');
269 if( ! rendererTypesMappingData ) {
270 return;
271 }
272
273 /** @type {Record<string, ('GoogleCharts' | 'ChartJS' | 'DataTable')[]>} */
274 const rendererTypesMapping = JSON.parse( rendererTypesMappingData );
275
276 document.querySelectorAll('input.type-radio').forEach( chartTypeRadio => {
277
278 // Init the lib based on the default selected chart type.
279 if ( chartTypeRadio.checked ) {
280 const chartType = chartTypeRadio.value;
281 if ( ! chartType ) {
282 return;
283 }
284
285 const selectedRenderer = rendererSelect.value;
286 if ( ! rendererTypesMapping[chartType]?.includes( selectedRenderer ) ) {
287 disable_renderer_select_placeholder();
288 rendererSelect.value = rendererTypesMapping[chartType][0]
289 }
290 }
291
292 chartTypeRadio.addEventListener('click', (event) => {
293 // Check if the chart type is supported by the selected renderer. If not, select the first supported renderer.
294 const chartType = event.target.value;
295 if ( ! chartType ) {
296 return;
297 }
298
299 const selectedRenderer = rendererSelect.value;
300 if ( ! rendererTypesMapping[chartType]?.includes( selectedRenderer ) ) {
301 disable_renderer_select_placeholder();
302 rendererSelect.value = rendererTypesMapping[chartType][0]
303 }
304 });
305 });
306
307 // Update the chart list on user interaction.
308 rendererSelect.addEventListener('change', (event) => {
309 disable_renderer_select_placeholder();
310 toggle_renderer_type( event.target.value );
311 toggle_chart_types_by_render( event.target.value );
312 });
313
314 }
315
316 /**
317 * Disable the placeholder option in the renderer select.
318 */
319 function disable_renderer_select_placeholder() {
320 const placeholderOption = document.querySelector('#library-select-placeholder');
321 if ( placeholderOption && placeholderOption.disabled === false ) {
322 placeholderOption.disabled = true;
323 }
324 }
325
326 /**
327 * Toggle the renderer type class based on the given renderer type.
328 *
329 * The class is used to style the chart type picker based on the given renderer library.
330 *
331 * @param {('GoogleCharts' | 'ChartJS' | 'DataTable')} rendererType The renderer type to toggle the class.
332 */
333 function toggle_renderer_type( rendererType ) {
334 const typePicker = document.querySelector('#type-picker');
335 if ( ! typePicker ) {
336 return;
337 }
338
339 typePicker.classList.remove('lib-GoogleCharts', 'lib-ChartJS', 'lib-DataTable');
340 typePicker.classList.add(`lib-${rendererType}`);
341 }
342
343 /**
344 * Toggle chart types based on the given renderer type.
345 *
346 * @param {('GoogleCharts' | 'ChartJS' | 'DataTable')} rendererType The renderer type to filter the chart types.
347 */
348 function toggle_chart_types_by_render( rendererType ) {
349 document.querySelectorAll('.type-box').forEach( typeBox => {
350 const hasLib = typeBox.classList.contains(`type-lib-${rendererType}`);
351 typeBox.classList.toggle('viz-hidden', ! hasLib );
352
353 // Reset unavailable chart type selection.
354 const chartOption = typeBox.querySelector('input[type="radio"]');
355 if ( ! hasLib && chartOption?.checked ) {
356 chartOption.checked = false;
357 }
358 });
359
360 enable_libraries_for($('input.type-radio:checked').val(), $typeVsLibrary);
361 }
362
363 function enable_libraries_for($type, $typeVsLibrary) {
364 $('select.viz-select-library option').addClass('disabled').attr('disabled', 'disabled');
365 $('select.viz-select-library option .premium-label').remove();
366 $('select.viz-select-library option').append('<span class="premium-label"> (PREMIUM)</span>');
367 var $libs = $typeVsLibrary[$type];
368 $.each($libs, function( i, $lib ) {
369 $('select.viz-select-library option[value="' + $lib + '"]').removeClass('disabled').removeAttr('disabled');
370 $('select.viz-select-library option[value="' + $lib + '"] .premium-label').remove();
371 });
372 $('select.viz-select-library').val( $('select.viz-select-library option:not(.disabled)').val() );
373 }
374
375 function init_permissions(){
376 $('.visualizer-permission').chosen({
377 width : '50%',
378 search_contains : true
379 });
380
381 $('.visualizer-permission-type').each(function(x, y){
382 var type = $(y).attr('data-visualizer-permission-type');
383 var child = $('.visualizer-permission-' + type + '-specific');
384 if($(y).val() === 'all'){
385 child.next('div.chosen-container').hide();
386 return;
387 }
388 });
389
390 $('.visualizer-permission-type').on('change', function(evt, params) {
391 var type = $(this).attr('data-visualizer-permission-type');
392 var child = $('.visualizer-permission-' + type + '-specific');
393 child.empty();
394 if(params.selected === 'all'){
395 child.next('div.chosen-container').hide();
396 return;
397 } else {
398 child.next('div.chosen-container').show();
399 }
400 child.append('<option value="">' + visualizer.l10n['loading'] + '</option>').trigger('chosen:updated');
401 $.ajax({
402 url : visualizer.ajax['url'],
403 method : 'post',
404 data : {
405 'action' : visualizer.ajax['actions']['permissions'],
406 'nonce' : visualizer.ajax['nonces']['permissions'],
407 'type' : params.selected
408 },
409 success : function(d, textStatus, XMLHttpRequest){
410 if(d.success) {
411 child.empty();
412 $.each(d.data, function(k, v){
413 child.append('<option value="' + k + '">' + v + '</option>');
414 });
415 child.trigger('chosen:updated');
416 }
417 }
418 });
419 });
420 }
421
422 // https://codemirror.net/
423 function init_db_import_component(){
424 var table_columns = visualizer.db_query.tables;
425 var code_mirror = wp.CodeMirror || CodeMirror;
426 var cm = code_mirror.fromTextArea($('.visualizer-db-query').get(0), {
427 value: $('.visualizer-db-query').val(),
428 autofocus: true,
429 mode: 'text/x-mysql',
430 lineWrapping: true,
431 dragDrop: false,
432 matchBrackets: true,
433 autoCloseBrackets: true,
434 extraKeys: {"Shift-Space": "autocomplete"},
435 hintOptions: { tables: table_columns }
436 });
437
438 // force refresh so that the query shows on first time load. Otherwise you have to click on the editor for it to show.
439 $('body').on('visualizer:db:query:focus', function(event, data){
440 cm.refresh();
441 });
442
443 cm.focus();
444
445 // update text area.
446 cm.on('inputRead', function(x, y){
447 cm.save();
448 });
449
450 // backspace and delete do not register so the text box does not get empty if the entire query is deleted
451 // from the editor. Let's force this.
452 $('body').on('visualizer:db:query:update', function(event, data){
453 cm.save();
454 });
455
456 // clear the editor.
457 $('body').on('visualizer:db:query:setvalue', function(event, data){
458 cm.setValue(data.value);
459 cm.clearHistory();
460 cm.refresh();
461 });
462
463 // set an option at runtime?
464 $('body').on('visualizer:db:query:changeoption', function(event, data){
465 cm.setOption(data.name, data.value);
466 });
467 }
468
469 function init_filter_import() {
470 $( '#db-filter-save-button' ).on( 'click', function(){
471 $('#vz-filter-wizard').submit();
472 $( '#vz-wp-sync-options' ).hide();
473 $( '#vz-wp-sync-btn' ).attr( 'aria-expanded', false );
474 });
475 }
476
477 function init_db_import(){
478 $( '#visualizer-db-query' ).css("z-index", "-1").hide();
479
480 init_db_import_component();
481
482 var settings_button = document.querySelector( '#settings-button' );
483 if ( settings_button ) {
484 settings_button.addEventListener( 'click', function( event ){
485 $('body').trigger('visualizer:db:query:update', {});
486 if( $( '#db-chart-button' ).attr( 'data-current' ) !== 'filter' || $( '.visualizer-db-query' ).val().length === 0 ){
487 return;
488 }
489
490 event.preventDefault();
491 event.stopImmediatePropagation();
492
493 var query_saved = false;
494 var resume_save = function(){
495 query_saved = true;
496 settings_button.click();
497 };
498
499 $('body').one( 'visualizer:render:currentchart:update', resume_save );
500 $( '#thehole' ).one( 'load', function(){
501 $('body').off( 'visualizer:render:currentchart:update', resume_save );
502 if ( ! query_saved ) {
503 $( '#db-chart-button' ).trigger( 'click' );
504 }
505 } );
506 $( '#db-chart-button' ).trigger( 'click' );
507 }, true );
508 }
509
510 $('#visualizer-query-fetch').on('click', function(e){
511
512 $('body').trigger('visualizer:db:query:update', {});
513 if($('.visualizer-db-query').val() === ''){
514 return;
515 }
516
517 start_ajax($('#visualizer-db-query'));
518 $('.db-wizard-results').empty();
519 $('.db-wizard-error').empty();
520 $.ajax({
521 url : ajaxurl,
522 method : 'post',
523 data : {
524 'action' : visualizer.ajax['actions']['db_get_data'],
525 'security' : visualizer.ajax['nonces']['db_get_data'],
526 'params' : $('#db-query-form').serialize()
527 },
528 success : function(data){
529 if(data.success){
530 $('.db-wizard-results').html(data.data.table);
531 $('#results').DataTable({
532 "paging": false
533 });
534 }else{
535 $('.db-wizard-error').html(data.data.msg);
536 }
537 },
538 complete: function(){
539 end_ajax($('#visualizer-db-query'));
540 }
541 });
542 });
543
544 $( '#db-chart-button' ).on( 'click', function(){
545
546 $('body').off('visualizer:change:action').on('visualizer:change:action', function(e){
547 var filter_button = $( '#db-chart-button' );
548 $( '#visualizer-db-query' ).css("z-index", "-1").hide();
549 filter_button.val( filter_button.attr( 'data-t-chart' ) );
550 filter_button.html( filter_button.attr( 'data-t-chart' ) );
551 filter_button.attr( 'data-current', 'chart' );
552 $( '#canvas' ).css("z-index", "1").show();
553 });
554
555 $('#content').css('width', 'calc(100% - 350px)');
556 if( $(this).attr( 'data-current' ) === 'chart'){
557 $(this).val( $(this).attr( 'data-t-filter' ) );
558 $(this).html( $(this).attr( 'data-t-filter' ) );
559 $(this).attr( 'data-current', 'filter' );
560 $( '.visualizer-editor-lhs' ).hide();
561 $( '#visualizer-db-query' ).css("z-index", "9999").show();
562 $('body').trigger('visualizer:db:query:focus', {});
563 $( '#canvas' ).hide();
564 }else{
565 var filter_button = $(this);
566 $( '#visualizer-db-query' ).css("z-index", "-1").hide();
567 $('#canvas').lock();
568 filter_button.val( filter_button.attr( 'data-t-chart' ) );
569 filter_button.html( filter_button.attr( 'data-t-chart' ) );
570 filter_button.attr( 'data-current', 'chart' );
571 $( '#canvas' ).css("z-index", "1").show();
572 $( '#db-chart-save-button' ).trigger('click');
573 }
574 } );
575
576 $( document ).on( 'click', '#vz-db-sync-btn', function(){
577 var $options = $( '#vz-db-sync-options' );
578 var isOpen = $options.is( ':visible' );
579 $options.toggle();
580 $( this ).attr( 'aria-expanded', ! isOpen );
581 } );
582
583 $( document ).on( 'click', '#vz-wp-sync-btn', function(){
584 var $options = $( '#vz-wp-sync-options' );
585 var isOpen = $options.is( ':visible' );
586 $options.toggle();
587 $( this ).attr( 'aria-expanded', ! isOpen );
588 } );
589
590 $( '#db-chart-save-button' ).on( 'click', function(){
591 // submit only if a query has been provided.
592 if($('#db-query-form .visualizer-db-query').val().length > 0){
593 $('#viz-db-wizard-params').val($('#db-query-form').serialize());
594 $('#vz-db-wizard').submit();
595 $( '#vz-db-sync-options' ).hide();
596 $( '#vz-db-sync-btn' ).attr( 'aria-expanded', false );
597 }else{
598 $('#canvas').unlock();
599 }
600 });
601 }
602
603 function init_json_import(){
604 if(typeof visualizer === 'undefined'){
605 return;
606 }
607
608 var regex = new RegExp(visualizer.json_tag_separator, 'g');
609
610 $( '#visualizer-json-screen' ).css("z-index", "-1").hide();
611 $('.visualizer-json-form').accordion({
612 heightStyle: 'content',
613 active: 0
614 });
615 $('.visualizer-json-subform').accordion({
616 heightStyle: 'content',
617 active: false,
618 collapsible: true
619 });
620
621 // open the accordions by default if they are indicated with the 'open' class.
622 $('.visualizer-json-subform .viz-substep.open').each(function(i, e){
623 $('.visualizer-json-subform').accordion( "option", "active", i );
624 });
625
626 // toggle between chart and create/modify parameters
627 $( '#json-chart-button, #woo-chart-button' ).on( 'click', function(){
628
629 $('body').off('visualizer:change:action').on('visualizer:change:action', function(e){
630 var filter_button = $( '#json-chart-button, #woo-chart-button' );
631 $( '#visualizer-json-screen' ).css("z-index", "-1").hide();
632 filter_button.val( filter_button.attr( 'data-t-chart' ) );
633 filter_button.html( filter_button.attr( 'data-t-chart' ) );
634 filter_button.attr( 'data-current', 'chart' );
635 $( '#canvas' ).css("z-index", "1").show();
636 $( '#vz-import-woo-report' ).find( 'select, input' ).removeAttr( 'disabled' );
637 });
638
639 if ( 'json-chart-button' === $( this ).attr( 'id' ) ) {
640 $( '#vz-import-woo-report' ).find( 'select, input' ).attr( 'disabled', true );
641 } else {
642 $( '#vz-import-woo-report' ).find( 'select, input' ).removeAttr( 'disabled' );
643 }
644 $('#content').css('width', 'calc(100% - 100px)');
645 if( $(this).attr( 'data-current' ) === 'chart'){
646 // toggle from chart to LHS form
647 $(this).val( $(this).attr( 'data-t-filter' ) );
648 $(this).html( $(this).attr( 'data-t-filter' ) );
649 $(this).attr( 'data-current', 'filter' );
650 $( '.visualizer-editor-lhs' ).hide();
651 $( '#visualizer-json-screen' ).css("z-index", "9999").show();
652 if ( 'woo-chart-button' === $( this ).attr( 'id' ) && '' !== $( '#vz-woo-source:visible' ).val() ) {
653 $( '#vz-import-json-url' ).val( visualizer.rest_base + $( '#vz-woo-source' ).val() ).attr( 'readonly', true );
654 } else {
655 if ( 'undefined' !== $( '#vz-import-json-url' ).attr( 'readonly' ) ) {
656 $( '#vz-import-json-url' ).val( '' ).removeAttr( 'readonly' ).removeAttr( 'value' );
657 $('#json-endpoint-form')[0].reset();
658 $('.visualizer-json-form h3.viz-step:not(.step1)').addClass('ui-state-disabled');
659 }
660 }
661 $( '#canvas' ).hide();
662 }else{
663 // toggle from LHS form to chart
664 $( '#json-conclude-form' ).trigger('submit');
665 }
666 } );
667
668 $('body').on('visualizer:json:form:submit', function() {
669 var filter_button = $( '#json-chart-button, #woo-chart-button' );
670 $( '#visualizer-json-screen' ).css("z-index", "-1").hide();
671 $('#canvas').lock();
672 filter_button.val( filter_button.attr( 'data-t-chart' ) );
673 filter_button.html( filter_button.attr( 'data-t-chart' ) );
674 filter_button.attr( 'data-current', 'chart' );
675 end_ajax( $( '#visualizer-json-screen' ) );
676 $( '#canvas' ).removeClass('visualizer-chart-loaded').css("z-index", "1").show();
677 });
678
679
680 // fetch the roots for the provided endpoint
681 $( '#visualizer-json-fetch' ).on( 'click', function(e){
682 e.preventDefault();
683 $('.visualizer-json-form').accordion( 'option', 'active', 0 );
684 $('.visualizer-json-form h3.viz-step:not(.step1)').addClass('ui-state-disabled');
685 $('.json-table').html('');
686 start_ajax( $( '#visualizer-json-screen' ) );
687 $.ajax({
688 url : ajaxurl,
689 method : 'post',
690 data : {
691 'action' : visualizer.ajax['actions']['json_get_roots'],
692 'security' : visualizer.ajax['nonces']['json_get_roots'],
693 'params' : $('#json-endpoint-form').serialize()
694 },
695 success : function(data){
696 if(data.success){
697 $('#vz-import-json-root').empty();
698 $.each(data.data.roots, function(i, name){
699 $('#vz-import-json-root').append('<option value="' + name + '">' + name.replace(regex, visualizer.json_tag_separator_view) + '</option>');
700 });
701 $('#json-root-form').fadeIn('medium');
702 json_accordion_activate(1, true);
703 }else{
704 alert(data.data.msg);
705 }
706 },
707 complete: function(){
708 end_ajax($('#visualizer-json-screen'));
709 }
710 });
711 });
712
713 // fetch the data for the chosen root
714 $( '#visualizer-json-parse' ).on( 'click', function(e){
715 e.preventDefault();
716 $('.visualizer-json-form h3.viz-step:not(.step1):not(.step2)').addClass('ui-state-disabled');
717 $('.json-table').html('');
718 start_ajax( $( '#visualizer-json-screen' ) );
719 $.ajax({
720 url : ajaxurl,
721 method : 'post',
722 data : {
723 'action' : visualizer.ajax['actions']['json_get_data'],
724 'security' : visualizer.ajax['nonces']['json_get_data'],
725 'params' : $('#json-root-form, #json-endpoint-form').serialize()
726 },
727 success : function(data){
728 if(data.success){
729 $('#vz-import-json-paging option:not(.static)').remove();
730 if(data.data.paging.length > 0){
731 var $template = $('#vz-import-json-paging').attr('data-template');
732 $.each(data.data.paging, function(i, name){
733 var display = name.replace(regex, visualizer.json_tag_separator_view);
734 display = $template.replace('?', display);
735 $('#vz-import-json-paging').append('<option value="' + name + '">' + display + '</option>');
736 });
737 $('.json-pagination').show();
738 }
739 $('#json-conclude-form .json-table').html(data.data.table);
740
741 var $table = create_editor_table( '#json-conclude-form' );
742
743 json_accordion_activate(3, true);
744 json_accordion_activate(2, false);
745 $table.columns.adjust().draw();
746 }else{
747 alert(data.data.msg);
748 }
749 },
750 complete: function(){
751 end_ajax($('#visualizer-json-screen'));
752 }
753 });
754 });
755
756 // when the data is set and the chart is updated, toggle the screen so that the chart is shown
757 $('#json-conclude-form').on( 'submit', function(e){
758 // at least one column has to be selected as non-excluded.
759 var count_selected = 0;
760 $('select.viz-select-data-type').each(function(i, element){
761 if($(element).prop('selectedIndex') > 0){
762 count_selected++;
763 }
764 });
765 if(count_selected === 0){
766 alert(visualizer.l10n.select_columns);
767 return false;
768 }
769
770 // populate the form elements that are in the other tabs.
771 $('#json-conclude-form-helper .json-form-element, #json-endpoint-form .json-form-element, #json-root-form .json-form-element, #vz-import-json .json-form-element, #vz-import-woo-report .json-form-element:not(:disabled)').each(function(x, y){
772 $('#json-conclude-form').append('<input type="hidden" name="' + y.name + '" value="' + y.value + '">');
773 });
774
775 $('body').trigger('visualizer:json:form:submit');
776 });
777
778 // update the schedule
779 $('#json-chart-save-button, #woo-chart-save-button').on('click', function(e){
780 e.preventDefault();
781 $('#canvas').lock();
782 var btnID = jQuery( this ).attr( 'id' );
783 $.ajax({
784 url : ajaxurl,
785 method : 'post',
786 data : {
787 'action' : visualizer.ajax['actions']['json_set_schedule'],
788 'security' : visualizer.ajax['nonces']['json_set_schedule'],
789 'chart' : $('#vz-json-time, #vz-woo-time').attr('data-chart'),
790 'time' : $('#vz-json-time, #vz-woo-time').val(),
791 'is_woocommerce_report': 'woo-chart-save-button' === btnID,
792 },
793 success : function(data){
794 // do nothing.
795 },
796 complete: function(){
797 $('#canvas').unlock();
798 }
799 });
800 });
801
802 // Select WooCommerce report endpoint.
803 $( '#vz-woo-source' ).on( 'click', function( e ) {
804 // Trigger change action.
805 $( 'body' ).trigger( 'visualizer:change:action' );
806
807 if ( '' !== $( this ).val() ) {
808 $( '#woo-chart-button, #woo-chart-save-button' ).removeAttr( 'disabled' );
809 } else {
810 $( '#woo-chart-button, #woo-chart-save-button' ).attr( 'disabled', true );
811 }
812 } );
813
814 }
815
816 function init_editor_table() {
817 $('body').on('visualizer:db:editor:table:init', function(event, data){
818 var $table = create_editor_table('.viz-table-editor', data.config);
819 $('body').on('visualizer:db:editor:table:redraw', function(event, data){
820 $table.draw();
821 });
822 });
823 }
824
825 function create_editor_table(element, config) {
826 var settings = {
827 paging: false,
828 searching: false,
829 ordering: false,
830 select: false,
831 scrollX: "100%",
832 scrollY: "400px",
833 info: false,
834 colReorder: {
835 fixedColumnsLeft: 1
836 }
837 };
838
839 // show column visibility button only when more than 6 columns are found (including the Label column)
840 if($(element + ' table.viz-editor-table thead tr th').length > 6){
841 $.extend( settings, {
842 dom: 'Bt',
843 buttons: [
844 {
845 extend: 'colvis',
846 columns: ':gt(0)',
847 collectionLayout: 'four-column'
848 }
849 ]
850 } );
851 }
852 if(config){
853 $.extend( settings, config );
854 }
855
856 var $table = $(element + ' .viz-editor-table').DataTable(settings);
857 return $table;
858 }
859
860 function json_accordion_activate($step, $activate){
861 $('.visualizer-json-form h3.viz-step.step' + ( $step + 1 )).removeClass('ui-state-disabled');
862 if($activate){
863 $('.visualizer-json-form').accordion( 'option', 'active', $step );
864 }
865 }
866
867 function start_ajax(element){
868 element.lock();
869 }
870
871 function end_ajax(element){
872 element.unlock();
873 }
874
875 })(jQuery);
876
877 (function ($) {
878 $.fn.lock = function () {
879 $(this).each(function () {
880 var $this = $(this);
881 var position = $this.css('position');
882
883 if (!position) {
884 position = 'static';
885 }
886
887 switch (position) {
888 case 'absolute':
889 case 'relative':
890 break;
891 default:
892 $this.css('position', 'relative');
893 break;
894 }
895 $this.data('position', position);
896
897 var width = $this.width(),
898 height = $this.height();
899
900 var locker = $('<div class="locker"></div>');
901 locker.width(width).height(height);
902
903 var loader = $('<div class="locker-loader"></div>');
904 loader.width(width).height(height);
905
906 locker.append(loader);
907 $this.append(locker);
908 $(window).resize(function () {
909 $this.find('.locker,.locker-loader').width($this.width()).height($this.height());
910 });
911 });
912
913 return $(this);
914 };
915
916 $.fn.unlock = function () {
917 $(this).each(function () {
918 $(this).find('.locker').remove();
919 $(this).css('position', $(this).data('position'));
920 });
921
922 return $(this);
923 };
924
925 // Telemetry
926 function getChartID() {
927 const urlParams = new URLSearchParams(window.location.search);
928 return urlParams.get('chart') ?? '';
929 }
930
931 const groupId = getChartID();
932
933 const chartTypes = document.querySelector('#viz-types-form')
934 if( chartTypes ) {
935 document.querySelector('.push-right[type=submit]')?.addEventListener('click', async function (event) {
936 if ( typeof window.tiTrk !== 'undefined' ) {
937 event.preventDefault();
938 try {
939 const formData = new FormData(document.querySelector('#viz-types-form'));
940 const savedData = Object.fromEntries(formData);
941
942 tiTrk?.with('visualizer')?.add({
943 feature: 'chart-create',
944 featureComponent: 'saved-data',
945 featureData: {
946 type: savedData?.['type'],
947 library: savedData?.['chart-library']
948 },
949 groupId
950 });
951
952 // Do not make the user to wait too long for the event to be uploaded.
953 const timer = new Promise((resolve) => setTimeout(resolve, 500));
954 await Promise.race([timer, tiTrk?.uploadEvents()]);
955 } catch (e) {
956 console.warn(e);
957 }
958 $('#viz-types-form').submit();
959 }
960 });
961 }
962
963 [
964 {
965 selector: '#editor-button',
966 featureComponent: 'source-manual-data'
967 },
968 {
969 selector: '#vz-import-file',
970 featureComponent: 'source-import-csv-file'
971 },
972 {
973 selector: '#vz-save-schedule',
974 featureComponent: 'source-import-csv-remote'
975 },
976 {
977 selector: '#visualizer-json-fetch',
978 featureComponent: 'source-import-json-remote'
979 },
980 {
981 selector: '#existing-chart',
982 featureComponent: 'source-import-chart'
983 },
984 {
985 selector: '#filter-chart-button',
986 featureComponent: 'source-import-wordpress',
987 },
988 {
989 selector: '#woo-chart-button',
990 featureComponent: 'source-import-woocommerce'
991 },
992 {
993 selector: '#db-chart-button',
994 featureComponent: 'source-import-database'
995 }
996 ].forEach(({ selector, featureComponent }) => {
997 document.querySelector(selector)?.addEventListener('click', function () {
998 window?.tiTrk?.with('visualizer')?.set('used-source',{
999 feature: 'chart-edit-used-source',
1000 featureComponent,
1001 groupId
1002 });
1003 });
1004 });
1005 })(jQuery);
1006
1007
1008 document.querySelector('#viz-copy-shortcode')?.addEventListener('click', function() {
1009 var copyText = document.querySelector('#viz-shortcode')?.value;
1010 if ( !copyText ) {
1011 return;
1012 }
1013
1014 navigator.clipboard.writeText(copyText);
1015 alert(visualizer.l10n.copied);
1016 });
1017
1018 // Connect Chart Backend Title Info Panel with the field from the settings form.
1019 const interactiveBackendTitleField = document.querySelector( '#viz-backend-name' );
1020 const backendTitleSave = document.querySelector( '#settings-form input[name="backend-title"]' );
1021
1022 if ( interactiveBackendTitleField && backendTitleSave ) {
1023 interactiveBackendTitleField.addEventListener('input', function() {
1024 backendTitleSave.value = this.value;
1025 });
1026 }
1027