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