PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 4.0.8
Visualizer – Tables & Charts Manager with Built-in AI Generator v4.0.8
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
← All changes | js/frame.js +732 -54 3.1.24.0.8 View file →
@@ -1,29 +1,62 @@
1 1 /* global prompt */
2 2 /* global visualizer */
3 3 /* global alert */
4 +/* global confirm */
4 5 /* global ajaxurl */
5 6 /* global CodeMirror */
7 +/* global vizHaveSettingsChanged */
6 8
7 9 (function ($) {
8 - $(window).load(function(){
9 - // scroll to the selected chart type.
10 - if($('label.type-label.type-label-selected').length > 0) {
11 - $('label.type-label.type-label-selected')[0].scrollIntoView();
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();
12 15 }
13 16 });
14 17
15 18 $(document).ready(function () {
16 - // open the correct source tab.
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.
17 43 var source = $('#visualizer-chart-id').attr('data-chart-source');
18 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();
19 47
20 48 init_permissions();
21 49
22 50 if(typeof visualizer !== 'undefined' && visualizer.is_pro) {
23 51 init_db_import();
52 + init_filter_import();
24 53 }
25 54
55 + init_json_import();
56 +
57 + init_editor_table();
58 +
26 59 // update the manual configuation link to point to the correct chart type.
27 60 var type = $('#visualizer-chart-id').attr('data-chart-type');
28 61 var chart_type_in_api_link = type + 'chart';
29 62 switch (type) {
@@ -40,24 +73,13 @@
40 73
41 74 $('.type-radio').change(function () {
42 75 $('.type-label-selected').removeClass('type-label-selected');
43 76 $(this).parent().addClass('type-label-selected');
77 + updateTypeAriaChecked();
44 78 });
45 79
46 - $('#vz-chart-settings h2').click(function () {
47 - $("#vz-chart-source").hide();
48 - $("#vz-chart-permissions").removeClass('open').addClass('bottom-fixed');
49 - $(this).parent().removeClass('bottom-fixed').addClass('open');
50 - $("#vz-chart-permissions .viz-group-header").hide();
51 - return false;
52 - });
53 - $('#vz-chart-settings .customize-section-back').click(function () {
54 - $("#vz-chart-source").show();
55 - $(this).parent().parent().removeClass('open').addClass('bottom-fixed');
56 -
57 - return false;
58 - });
59 - $('.viz-group-title').click(function () {
80 + // collapse other open sections of this group
81 + $(document).on('click', '.viz-group-title', function () {
60 82 var parent = $(this).parent();
61 83
62 84 if (parent.hasClass('open')) {
63 85 parent.removeClass('open');
@@ -64,14 +86,35 @@
64 86 } else {
65 87 parent.parent().find('.viz-group.open').removeClass('open');
66 88 parent.addClass('open');
67 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));
68 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 +
69 112 $('#view-remote-file').click(function () {
70 - var url = $(this).parent().find('#remote-data').val();
113 + var url = $(this).parent().find('#vz-schedule-url').val();
71 114
72 115 if (url !== '') {
73 - if (/^([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)) {
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)) {
74 117 if (url.substr(url.length - 8) === '/pubhtml') {
75 118 url = url.substring(0, url.length - 8) + '/export?format=csv';
76 119 }
77 120
@@ -90,17 +133,17 @@
90 133 $(this).parent().submit();
91 134 }
92 135 });
93 136
94 - $('#thehole').load(function () {
137 + $( window ).on( 'load', function() {
95 138 $('#canvas').unlock();
96 - });
139 + } );
97 140
98 - $('.viz-section-title').click(function () {
141 + $(document).on('click', '.viz-section-title', function () {
99 142 $(this).toggleClass('open').parent().find('.viz-section-items').toggle();
100 143 });
101 144
102 - $('.more-info').click(function () {
145 + $(document).on('click', '.more-info', function () {
103 146 $(this).parent().find('.viz-section-description:first').toggle();
104 147 return false;
105 148 });
106 149
@@ -107,45 +150,235 @@
107 150 $('#cancel-button').click(function () {
108 151 $('#cancel-form').submit();
109 152 });
110 153
111 - });
154 + init_available_chart_list();
112 155
113 - function init_permissions(){
114 - $('#vz-chart-permissions h2').click(function () {
115 - $("#vz-chart-source").hide();
116 - $("#vz-chart-permissions .viz-group-header").show();
117 - $("#vz-chart-settings").removeClass('open').addClass('bottom-fixed');
156 + $('.viz-abort').on('click', function(e){
157 + e.preventDefault();
158 + window.parent.postMessage('visualizer:mediaframe:close', '*');
159 + });
118 160
119 - $('#settings-button').click(function(e) {
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 === ' '){
120 205 e.preventDefault();
121 - $('#permissions-form').submit();
122 - $('#settings-form').submit();
123 - });
206 + $(this).trigger('click');
207 + }
208 + });
124 209
125 - $(this).parent().removeClass('bottom-fixed').addClass('open');
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 + }
126 230
127 - return false;
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 + }
128 238 });
129 - $('#vz-chart-permissions .customize-section-back').click(function () {
130 - $("#vz-chart-source").show();
239 + }
131 240
132 - $("#vz-chart-permissions .viz-group-header").hide();
133 - $('#settings-button').click(function(e) {
134 - e.preventDefault();
135 - $('#settings-form').submit();
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 + }
136 304 });
305 + });
137 306
138 - $(this).parent().parent().removeClass('open').addClass('bottom-fixed');
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 + });
139 313
140 - return false;
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 + }
141 358 });
142 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(){
143 376 $('.visualizer-permission').chosen({
144 377 width : '50%',
145 378 search_contains : true
146 379 });
147 -
380 +
148 381 $('.visualizer-permission-type').each(function(x, y){
149 382 var type = $(y).attr('data-visualizer-permission-type');
150 383 var child = $('.visualizer-permission-' + type + '-specific');
151 384 if($(y).val() === 'all'){
@@ -152,9 +385,9 @@
152 385 child.next('div.chosen-container').hide();
153 386 return;
154 387 }
155 388 });
156 -
389 +
157 390 $('.visualizer-permission-type').on('change', function(evt, params) {
158 391 var type = $(this).attr('data-visualizer-permission-type');
159 392 var child = $('.visualizer-permission-' + type + '-specific');
160 393 child.empty();
@@ -197,9 +430,9 @@
197 430 lineWrapping: true,
198 431 dragDrop: false,
199 432 matchBrackets: true,
200 433 autoCloseBrackets: true,
201 - extraKeys: {"Ctrl-Space": "autocomplete"},
434 + extraKeys: {"Shift-Space": "autocomplete"},
202 435 hintOptions: { tables: table_columns }
203 436 });
204 437
205 438 // force refresh so that the query shows on first time load. Otherwise you have to click on the editor for it to show.
@@ -207,9 +440,9 @@
207 440 cm.refresh();
208 441 });
209 442
210 443 cm.focus();
211 -
444 +
212 445 // update text area.
213 446 cm.on('inputRead', function(x, y){
214 447 cm.save();
215 448 });
@@ -218,15 +451,63 @@
218 451 // from the editor. Let's force this.
219 452 $('body').on('visualizer:db:query:update', function(event, data){
220 453 cm.save();
221 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 + });
222 467 }
223 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 +
224 477 function init_db_import(){
225 478 $( '#visualizer-db-query' ).css("z-index", "-1").hide();
226 479
227 480 init_db_import_component();
228 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 +
229 510 $('#visualizer-query-fetch').on('click', function(e){
230 511
231 512 $('body').trigger('visualizer:db:query:update', {});
232 513 if($('.visualizer-db-query').val() === ''){
@@ -231,9 +512,9 @@
231 512 $('body').trigger('visualizer:db:query:update', {});
232 513 if($('.visualizer-db-query').val() === ''){
233 514 return;
234 515 }
235 -
516 +
236 517 start_ajax($('#visualizer-db-query'));
237 518 $('.db-wizard-results').empty();
238 519 $('.db-wizard-error').empty();
239 520 $.ajax({
@@ -260,9 +541,19 @@
260 541 });
261 542 });
262 543
263 544 $( '#db-chart-button' ).on( 'click', function(){
264 - $('#content').css('width', 'calc(100% - 300px)');
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)');
265 556 if( $(this).attr( 'data-current' ) === 'chart'){
266 557 $(this).val( $(this).attr( 'data-t-filter' ) );
267 558 $(this).html( $(this).attr( 'data-t-filter' ) );
268 559 $(this).attr( 'data-current', 'filter' );
@@ -281,14 +572,299 @@
281 572 $( '#db-chart-save-button' ).trigger('click');
282 573 }
283 574 } );
284 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 +
285 590 $( '#db-chart-save-button' ).on( 'click', function(){
286 - $('#viz-db-wizard-params').val($('#db-query-form').serialize());
287 - $('#vz-db-wizard').submit();
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 + }
288 600 });
289 601 }
290 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 +
291 867 function start_ajax(element){
292 868 element.lock();
293 869 }
294 870
@@ -344,5 +920,107 @@
344 920 });
345 921
346 922 return $(this);
347 923 };
348 -})(jQuery);
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 +}