PluginProbe
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder / 51.1.49
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder v51.1.49
51.1.83 51.1.82 51.1.81 51.1.79 51.1.78 51.1.77 51.1.76 51.1.74 51.1.75 51.1.65 51.1.64 51.1.63 trunk 51.1.14 51.1.2 51.1.35 51.1.36 51.1.37 51.1.38 51.1.39 51.1.44 51.1.45 51.1.46 51.1.47 51.1.49 All 37 releases
king-addons / includes / widgets / Charts / script.js

script.js in King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder 51.1.49, at includes/widgets/Charts/script.js

418 lines 25.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 // noinspection JSUnresolvedReference
2
3 "use strict";
4 (function ($) {
5 $(window).on("elementor/frontend/init", function () {
6 elementorFrontend.hooks.addAction(
7 "frontend/element_ready/king-addons-charts.default",
8 function ($scope) {
9 const BaseHandler = elementorModules.frontend.handlers.Base.extend({
10 onInit() {
11 const $container = this.$element.find(".king-addons-charts-container");
12 const chartSettings = JSON.parse($container.attr("data-settings"));
13 const labels = chartSettings.chart_labels;
14 const customDatasets = chartSettings.chart_datasets
15 ? JSON.parse(chartSettings.chart_datasets)
16 : "";
17
18 // Helper for common grid/ticks setup
19 const axisConfig = (axis, gridColor, lineWidth, dashed = false) => ({
20 display: chartSettings[`display_${axis}_axis`],
21 drawBorder: chartSettings[`display_${axis}_axis`],
22 drawOnChartArea: chartSettings[`display_${axis}_axis`],
23 drawTicks: chartSettings[`display_${axis}_axis`],
24 color: gridColor,
25 borderDash: dashed
26 ? [chartSettings[`border_dash_length_${axis[0]}`], chartSettings[`border_dash_spacing_${axis[0]}`]]
27 : undefined,
28 borderDashOffset: dashed
29 ? chartSettings[`border_dash_offset_${axis[0]}`]
30 : undefined,
31 lineWidth: lineWidth,
32 });
33
34 // Reusable tick/font setup
35 const tickConfig = (axis) => {
36 const stepSizeVal = chartSettings[`${axis}_step_size`];
37 return {
38 stepSize: (stepSizeVal && stepSizeVal > 0) ? stepSizeVal : undefined,
39 display: chartSettings[`display_${axis}_ticks`],
40 padding: chartSettings[`ticks_padding_${axis}`],
41 autoSkip: false,
42 maxRotation: chartSettings[`rotation_${axis}`],
43 minRotation: chartSettings[`rotation_${axis}`],
44 color: chartSettings[`ticks_color_${axis}`],
45 font: {
46 size: chartSettings[`ticks_font_size_${axis}`],
47 family: chartSettings[`ticks_font_family_${axis}`],
48 style: chartSettings[`ticks_font_style_${axis}`],
49 weight: chartSettings[`ticks_font_weight_${axis}`],
50 },
51 };
52 };
53
54 // Configure scales based on chart type
55 const scales =
56 ["bar", "bar_horizontal", "line"].includes(chartSettings.chart_type)
57 ? {
58 x: {
59 reverse: chartSettings.reverse_x === "yes",
60 stacked: chartSettings.stacked_bar_chart === "yes",
61 type:
62 chartSettings.chart_type === "bar_horizontal"
63 ? chartSettings.data_type
64 : "category",
65 min: chartSettings.min_value || null,
66 max: chartSettings.max_value || null,
67 grid: axisConfig("x", chartSettings.axis_grid_line_color_x, chartSettings.grid_line_width_x, true),
68 title: {
69 display: chartSettings.display_x_axis_title,
70 text: chartSettings.x_axis_title,
71 color: chartSettings.axis_title_color_x,
72 font: {
73 size: chartSettings.axis_title_font_size_x,
74 family: chartSettings.axis_title_font_family_x,
75 style: chartSettings.axis_title_font_style_x,
76 weight: chartSettings.axis_title_font_weight_x,
77 },
78 },
79 ticks: {
80 stepSize:
81 chartSettings.chart_type === "bar_horizontal" && chartSettings.x_step_size > 0
82 ? chartSettings.x_step_size
83 : undefined,
84 ...tickConfig("x"),
85 },
86 },
87 y: {
88 reverse: chartSettings.reverse_y === "yes",
89 stacked: chartSettings.stacked_bar_chart === "yes",
90 type:
91 ["bar", "line"].includes(chartSettings.chart_type)
92 ? chartSettings.data_type
93 : "category",
94 min: chartSettings.min_value || null,
95 max: chartSettings.max_value || null,
96 grid: axisConfig("y", chartSettings.axis_grid_line_color_y, chartSettings.grid_line_width_y, true),
97 title: {
98 display: chartSettings.display_y_axis_title,
99 text: chartSettings.y_axis_title,
100 color: chartSettings.axis_title_color_y,
101 font: {
102 size: chartSettings.axis_title_font_size_y,
103 family: chartSettings.axis_title_font_family_y,
104 style: chartSettings.axis_title_font_style_y,
105 weight: chartSettings.axis_title_font_weight_y,
106 },
107 },
108 ticks: tickConfig("y"),
109 },
110 }
111 : {};
112
113 // Global options
114 const globalOptions = {
115 responsive: true,
116 showLine: chartSettings.show_lines,
117 animation: chartSettings.chart_animation === "yes",
118 animations: {
119 tension: {
120 duration: chartSettings.chart_animation_duration,
121 easing: chartSettings.animation_transition_type,
122 from: 1,
123 to: 0,
124 loop: chartSettings.chart_animation_loop === "yes",
125 },
126 },
127 events: [
128 chartSettings.trigger_tooltip_on,
129 chartSettings.exclude_dataset_on_click === "yes" ? "click" : "",
130 ],
131 interaction: {
132 mode: chartSettings.chart_interaction_mode || "nearest",
133 },
134 elements: {
135 point: {
136 radius:
137 chartSettings.line_dots === "yes"
138 ? window.innerWidth >= 768
139 ? chartSettings.line_dots_radius
140 : chartSettings.line_dots_radius_mobile
141 : 0,
142 },
143 },
144 scales,
145 plugins: {
146 datalabels: {
147 color: chartSettings.inner_datalabels_color,
148 font: {
149 size: chartSettings.inner_datalabels_font_size,
150 style: chartSettings.inner_datalabels_font_style,
151 weight: chartSettings.inner_datalabels_font_weight,
152 },
153 },
154 legend: {
155 onHover: (event) => {
156 event.native.target.style.cursor = "pointer";
157 },
158 onLeave: (event) => {
159 event.native.target.style.cursor = "default";
160 },
161 onClick: (e, legendItem, legend) => {
162 if (
163 ["bar", "bar_horizontal", "line"].includes(chartSettings.chart_type) ||
164 chartSettings.chart_type === "radar"
165 ) {
166 const ci = legend.chart;
167 const index = legendItem.datasetIndex;
168 legendItem.hidden
169 ? (ci.show(index), (legendItem.hidden = false))
170 : (ci.hide(index), (legendItem.hidden = true));
171 }
172 },
173 reverse: chartSettings.reverse_legend === "yes",
174 display: chartSettings.show_chart_legend === "yes",
175 position: chartSettings.legend_position || "top",
176 align: chartSettings.legend_align || "center",
177 labels: {
178 usePointStyle: chartSettings.legend_shape === "point",
179 padding: chartSettings.legend_padding,
180 boxWidth: chartSettings.legend_box_width,
181 boxHeight: chartSettings.legend_font_size,
182 color: chartSettings.legend_text_color,
183 font: {
184 family: chartSettings.legend_font_family,
185 size: chartSettings.legend_font_size,
186 style: chartSettings.legend_font_style,
187 weight: chartSettings.legend_font_weight,
188 },
189 },
190 },
191 title: {
192 display: chartSettings.show_chart_title === "yes",
193 text: chartSettings.chart_title,
194 align: chartSettings.chart_title_align || "center",
195 position: chartSettings.chart_title_position || "top",
196 color: chartSettings.chart_title_color || "#000",
197 padding: chartSettings.title_padding,
198 font: {
199 family: chartSettings.title_font_family,
200 size: chartSettings.title_font_size,
201 style: chartSettings.title_font_style,
202 weight: chartSettings.title_font_weight,
203 },
204 },
205 tooltip: {
206 callbacks: {
207 footer(tooltipItems) {
208 let sum = 0;
209 tooltipItems.forEach((item) => {
210 sum += item.parsed.y;
211 });
212 if (chartSettings.chart_type === "bar_horizontal") {
213 sum = 0;
214 tooltipItems.forEach((item) => {
215 sum += item.parsed.x;
216 });
217 }
218 if (
219 ["radar", "pie", "doughnut", "polarArea"].includes(
220 chartSettings.chart_type
221 )
222 ) {
223 return false;
224 }
225 return "Sum: " + sum;
226 },
227 },
228 enabled: chartSettings.show_chart_tooltip === "yes",
229 position: chartSettings.tooltip_position || "nearest",
230 padding: chartSettings.tooltip_padding || 10,
231 caretSize:
232 window.innerWidth >= 768
233 ? chartSettings.tooltip_caret_size
234 : chartSettings.chart_tooltip_caret_size_mobile,
235 backgroundColor:
236 chartSettings.chart_tooltip_bg_color || "rbga(0, 0, 0, 0.2)",
237 titleColor: chartSettings.chart_tooltip_title_color || "#FFF",
238 titleFont: {
239 family: chartSettings.chart_tooltip_title_font,
240 size: chartSettings.chart_tooltip_title_font_size,
241 },
242 titleAlign: chartSettings.chart_tooltip_title_align,
243 titleMarginBottom: chartSettings.chart_tooltip_title_margin_bottom,
244 bodyColor: chartSettings.chart_tooltip_item_color || "#FFF",
245 bodyFont: {
246 family: chartSettings.chart_tooltip_item_font,
247 size: chartSettings.chart_tooltip_item_font_size,
248 },
249 bodyAlign: chartSettings.chart_tooltip_item_align,
250 bodySpacing: chartSettings.chart_tooltip_item_spacing,
251 boxPadding: 3,
252 },
253 },
254 };
255
256 // Adjust scales for non-standard chart types
257 if (
258 !["bar", "bar_horizontal", "line"].includes(chartSettings.chart_type) &&
259 !["doughnut", "pie", "polarArea"].includes(chartSettings.chart_type)
260 ) {
261 globalOptions.scales = {
262 r: {
263 angleLines: { color: chartSettings.angle_lines_color },
264 pointLabels: {
265 color: chartSettings.point_labels_color_r,
266 font: {
267 size: chartSettings.point_labels_font_size_r,
268 family: chartSettings.point_labels_font_family_r,
269 style: chartSettings.point_labels_font_style_r,
270 weight: chartSettings.point_labels_font_weight_r,
271 },
272 },
273 ticks: {
274 stepSize: chartSettings.r_step_size > 0 ? chartSettings.r_step_size : undefined,
275 display: chartSettings.display_r_ticks,
276 backdropColor: chartSettings.axis_labels_bg_color,
277 backdropPadding: +chartSettings.axis_labels_padding,
278 color: chartSettings.axis_labels_color,
279 },
280 grid: axisConfig("r", chartSettings.axis_grid_line_color_r, chartSettings.grid_line_width_r, true),
281 },
282 };
283 }
284
285 // Chart creation
286 let config;
287 if (chartSettings.data_source === "custom") {
288 const data = {
289 labels,
290 datasets: JSON.parse(chartSettings.chart_datasets),
291 };
292 config = {
293 type:
294 chartSettings.chart_type === "bar_horizontal"
295 ? "bar"
296 : chartSettings.chart_type,
297 data,
298 options: globalOptions,
299 plugins: chartSettings.inner_datalabels ? [ChartDataLabels] : [],
300 };
301 if (chartSettings.chart_type === "bar_horizontal") {
302 config.options.indexAxis = "y";
303 }
304 if (
305 chartSettings.tooltips_percent ||
306 ["pie", "doughnut", "polarArea"].includes(chartSettings.chart_type)
307 ) {
308 config.options.plugins.tooltip.callbacks.label = (data) => {
309 let prefix = data.dataset.label + ": ";
310 if (["pie", "doughnut", "polarArea"].includes(chartSettings.chart_type)) {
311 prefix = `${data.label} (${data.dataset.label}): `;
312 }
313 const total = data.dataset.data.reduce(
314 (sum, val) => parseFloat(sum) + parseFloat(val),
315 0
316 );
317 const percentage = ((data.formattedValue / total) * 100).toPrecision(3);
318 return prefix + (chartSettings.tooltips_percent ? percentage + "%" : data.formattedValue);
319 };
320 }
321 new Chart($scope.find(".king-addons-chart"), config);
322 } else {
323 // Handle CSV data source or errors
324 if (chartSettings.url && (["bar", "bar_horizontal", "line", "radar"].includes(chartSettings.chart_type))) {
325 $.ajax({
326 url: chartSettings.url,
327 type: "GET",
328 success(res) {
329 $scope.find(".king-addons-rotating-plane").remove();
330 renderCSVChart(res);
331 },
332 error(err) {
333 console.error(err);
334 },
335 });
336 } else {
337 $scope.find(".king-addons-rotating-plane").remove();
338 const errorMsg =
339 !chartSettings.url
340 ? '<p class="king-addons-charts-error-notice">Provide a csv file or remote URL</p>'
341 : '<p class="king-addons-charts-error-notice">doughnut, pie and polarArea charts only work with custom data source</p>';
342 $scope.find(".king-addons-charts-container").html(errorMsg);
343 }
344 }
345
346 // Window resize handler
347 $(window).resize(() => {
348 const newRadius =
349 window.innerWidth >= 768
350 ? chartSettings.line_dots_radius
351 : chartSettings.line_dots_radius_mobile;
352 config.options.elements.point.radius = newRadius;
353 config.options.plugins.tooltip.caretSize =
354 window.innerWidth >= 768
355 ? chartSettings.tooltip_caret_size
356 : chartSettings.chart_tooltip_caret_size_mobile;
357 });
358
359 // Render chart from CSV data
360 function renderCSVChart(csvData) {
361 const ctx = $scope.find(".king-addons-chart");
362 const rows = csvData.split(/\r?\n|\r/);
363 const csvLabels = rows.shift().split(chartSettings.separator);
364 const data = { labels: csvLabels, datasets: [] };
365 const csvConfig = {
366 type: chartSettings.chart_type === "bar_horizontal" ? "bar" : chartSettings.chart_type,
367 data,
368 options: globalOptions,
369 plugins: [
370 ...(chartSettings.inner_datalabels ? [ChartDataLabels] : []),
371 {
372 beforeInit(chart) {
373 chart.legend.afterFit = function () {
374 this.height += 50;
375 };
376 },
377 },
378 ],
379 };
380 if (chartSettings.chart_type === "bar_horizontal") {
381 csvConfig.options.indexAxis = "y";
382 }
383 if (chartSettings.tooltips_percent) {
384 csvConfig.options.plugins.tooltip.callbacks.label = (data) => {
385 let prefix = data.dataset.label + ": ";
386 if (["pie", "doughnut", "polarArea"].includes(chartSettings.chart_type)) {
387 prefix = `${data.label} (${data.dataset.label}): `;
388 }
389 const total = data.dataset.data.reduce(
390 (sum, val) => parseFloat(sum) + parseFloat(val),
391 0
392 );
393 const percentage = ((data.formattedValue / total) * 100).toPrecision(3);
394 return prefix + (chartSettings.tooltips_percent ? percentage + "%" : data.formattedValue);
395 };
396 }
397 const csvChart = new Chart(ctx, csvConfig);
398 rows.forEach((row, idx) => {
399 if (row) {
400 const dataset = { data: row.split(chartSettings.separator) };
401 if (customDatasets[idx]) {
402 Object.assign(dataset, customDatasets[idx]);
403 }
404 data.datasets.push(dataset);
405 csvChart.update();
406 }
407 });
408 }
409 },
410 });
411
412 elementorFrontend.elementsHandler.addHandler(BaseHandler, {
413 $element: $scope,
414 });
415 }
416 );
417 });
418 })(jQuery);