PluginProbe
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder / 51.1.87
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder v51.1.87
51.1.87 51.1.86 51.1.84 51.1.85 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 All 41 releases
← All changes | includes/widgets/Data_Table/script.js +386 -0 51.1.2 → 51.1.87 View file →
@@ -1,0 +1,386 @@
1 +"use strict";
2 +(function ($) {
3 + $(window).on('elementor/frontend/init', function () {
4 + elementorFrontend.hooks.addAction('frontend/element_ready/king-addons-data-table.default', function ($scope) {
5 + // Add elementor handler
6 + elementorFrontend.elementsHandler.addHandler(
7 + elementorModules.frontend.handlers.Base.extend({
8 + onInit: function onInit() {
9 + const $scope = this.$element;
10 + const $tableContainer = $scope.find('.king-addons-table-inner-container');
11 + const $tableBody = $tableContainer.find('tbody');
12 + const initialRows = $tableBody.find('tr');
13 + const paginationListItems = $scope.find('.king-addons-table-custom-pagination-list-item');
14 + let beforeFilter = $tableBody.find('.king-addons-table-row');
15 + let value = "";
16 +
17 + // Initialize Perfect Scrollbar if needed
18 + new PerfectScrollbar($tableContainer[0], {});
19 +
20 + // Helper: get number of items per page (as integer)
21 + const getItemsPerPage = () => +$tableContainer.attr('data-rows-per-page') || 0;
22 +
23 + /**
24 + * Updates the table with the filtered set of rows for a given page index (1-based).
25 + * Also optionally checks the live-search value for resetting the table if empty.
26 + */
27 + const displayRowsForPage = (pageIndex, checkSearch) => {
28 + // Hide table while updating
29 + $tableBody.hide();
30 +
31 + const itemsPerPage = getItemsPerPage();
32 + // Filter rows that should appear on the given page
33 + const newRows = initialRows.filter((i) => {
34 + // Original code uses index++ inside filter; equivalently use (i+1) here
35 + const rowNumber = i + 1;
36 + return (
37 + rowNumber > itemsPerPage * (pageIndex - 1) &&
38 + rowNumber <= itemsPerPage * pageIndex
39 + );
40 + });
41 +
42 + // Update the table with these new rows
43 + $tableBody.html(newRows);
44 +
45 + // If checkSearch is true and the current search is empty, revert back to beforeFilter
46 + if (checkSearch && value === "") {
47 + $tableBody.html(beforeFilter);
48 + }
49 +
50 + $tableBody.show();
51 + // Refresh the 'beforeFilter' cache
52 + beforeFilter = $tableBody.find('.king-addons-table-row');
53 + // Remove highlight classes if any
54 + beforeFilter
55 + .find('.king-addons-table-tr-before-remove')
56 + .removeClass('king-addons-table-tr-before-remove');
57 +
58 + updateEntryInfo();
59 + };
60 +
61 + /**
62 + * Removes 'active' class from all pagination items,
63 + * sets the correct item as active, and adjusts their visibility.
64 + */
65 + const setActivePaginationItem = (pageIndex) => {
66 + paginationListItems.removeClass('king-addons-active-pagination-item');
67 + paginationListItems.each((i, el) => {
68 + // Page indices in UI are basically i+1 if user numbered them from 1..n
69 + if (i + 1 === pageIndex) {
70 + $(el).addClass('king-addons-active-pagination-item');
71 + }
72 + });
73 + adjustPaginationList();
74 + };
75 +
76 + /**
77 + * Called by previous/next clicks to move forward/back by `delta` pages.
78 + * If the new page is out of range, does nothing.
79 + */
80 + const changePage = (delta) => {
81 + const currentPage = +$scope.find('.king-addons-active-pagination-item').text() || 1;
82 + const totalPages = paginationListItems.length;
83 + const newPage = currentPage + delta;
84 + if (newPage < 1 || newPage > totalPages) return;
85 +
86 + setActivePaginationItem(newPage);
87 + // For Prev/Next we do want to check if search is empty
88 + displayRowsForPage(newPage, true);
89 + };
90 +
91 + /**
92 + * Adjusts pagination list items to show/hide neighbors correctly.
93 + */
94 + const adjustPaginationList = () => {
95 + const paginationIndex = $scope.find('.king-addons-active-pagination-item').index();
96 + paginationListItems.each((i, el) => {
97 + // Always display the first/last, and the 2 neighbors around current item
98 + if (
99 + i === 0 ||
100 + i === paginationListItems.length - 1 ||
101 + (i <= paginationIndex && i >= paginationIndex - 2)
102 + ) {
103 + $(el).css('display', 'flex');
104 + } else {
105 + $(el).css('display', 'none');
106 + }
107 + });
108 + };
109 +
110 + /**
111 + * Updates the "Showing X to Y of Z Entries" text (if enabled).
112 + */
113 + const updateEntryInfo = () => {
114 + if ($tableContainer.attr('data-entry-info') !== 'yes') return;
115 +
116 + const itemsPerPage = getItemsPerPage();
117 + const entryPage = +$scope.find('.king-addons-active-pagination-item').text() || 1;
118 + const rowsOnPage = $tableBody.find('tr').length;
119 +
120 + const lastEntry = itemsPerPage * entryPage - (itemsPerPage - rowsOnPage);
121 + const firstEntry = lastEntry - rowsOnPage + 1;
122 +
123 + const info = `Showing ${firstEntry} to ${lastEntry} of ${initialRows.length} Entries.`;
124 + $scope.find('.king-addons-entry-info').html(info);
125 + };
126 +
127 + /**
128 + * Live search logic: filters rows by matching text in any table cell.
129 + */
130 + const initLiveSearch = () => {
131 + $scope.find(".king-addons-table-live-search").on("keyup", function () {
132 + value = this.value.toLowerCase().trim();
133 +
134 + if (value !== "") {
135 + $scope.find('.king-addons-table-pagination-cont')
136 + .addClass('king-addons-hide-pagination-on-search');
137 + } else {
138 + $scope.find('.king-addons-table-pagination-cont')
139 + .removeClass('king-addons-hide-pagination-on-search');
140 + }
141 +
142 + const filteredRows = [];
143 + initialRows.each((i, row) => {
144 + const $row = $(row);
145 + $row.find("td").each((_, cell) => {
146 + const cellText = $(cell).text().toLowerCase().trim();
147 + // If found a match, add row to `filteredRows` once
148 + if (cellText.indexOf(value) !== -1) {
149 + filteredRows.push($row);
150 + return false; // break out of .each loop for this row
151 + }
152 + });
153 + });
154 +
155 + // If user cleared search, revert to all filtered items
156 + $tableBody.html(value === "" ? beforeFilter : filteredRows);
157 + updateEntryInfo();
158 + });
159 + };
160 +
161 + /**
162 + * Table sorting logic by clicking on <th>.
163 + */
164 + const initTableSorting = () => {
165 + // Only if data-table-sorting is 'yes'
166 + if ($tableContainer.attr('data-table-sorting') !== 'yes') return;
167 +
168 + // Remove highlight class if user clicks outside any TH or highlight cell
169 + $(window).on('click', (e) => {
170 + const isTh = $(e.target).hasClass('king-addons-table-th');
171 + const inTh = $(e.target).closest('.king-addons-table-th').length > 0;
172 + const isActiveTd = $(e.target).hasClass('king-addons-active-td-bg-color');
173 + const inActiveTd = $(e.target).closest('.king-addons-active-td-bg-color').length > 0;
174 +
175 + if (!isTh && !inTh && !isActiveTd && !inActiveTd) {
176 + $scope.find('td.king-addons-active-td-bg-color').removeClass('king-addons-active-td-bg-color');
177 + }
178 + });
179 +
180 + const getCellValue = (row, idx) =>
181 + $(row).children('td').eq(idx).text();
182 +
183 + const comparer = (idx) => (a, b) => {
184 + const valA = getCellValue(a, idx);
185 + const valB = getCellValue(b, idx);
186 + // Numeric vs. text comparison
187 + if ($.isNumeric(valA) && $.isNumeric(valB)) {
188 + return valA - valB;
189 + }
190 + return valA.toString().localeCompare(valB);
191 + };
192 +
193 + // Handle clicks on <th> for sorting
194 + $scope.find('th').on('click', function () {
195 + const $this = $(this);
196 + const indexOfTr = $this.index();
197 +
198 + // Highlight the column's cells
199 + $scope.find('td').each(function () {
200 + $(this).toggleClass(
201 + 'king-addons-active-td-bg-color',
202 + $(this).index() === indexOfTr
203 + );
204 + });
205 +
206 + // Reset the sorting icon on every TH
207 + $scope.find('th .king-addons-sorting-icon').html('<i class="fas fa-sort" aria-hidden="true"></i>');
208 +
209 + // Sort the rows
210 + const table = $this.closest('table');
211 + let rows = table.find('tr:gt(0)').toArray().sort(comparer(indexOfTr));
212 +
213 + // Toggle ascending/descending
214 + this.asc = !this.asc;
215 + const isCustom = $scope.hasClass('king-addons-data-table-type-custom');
216 +
217 + if ((isCustom && !this.asc) || (!isCustom && this.asc)) {
218 + // Sort reversed
219 + rows = rows.reverse();
220 + $this.find('.king-addons-sorting-icon').html(
221 + isCustom
222 + ? '<i class="fas fa-sort-down" aria-hidden="true"></i>'
223 + : '<i class="fas fa-sort-up" aria-hidden="true"></i>'
224 + );
225 + } else {
226 + // Normal ascending
227 + $this.find('.king-addons-sorting-icon').html(
228 + isCustom
229 + ? '<i class="fas fa-sort-up" aria-hidden="true"></i>'
230 + : '<i class="fas fa-sort-down" aria-hidden="true"></i>'
231 + );
232 + }
233 +
234 + // Re-append sorted rows
235 + for (let i = 0; i < rows.length; i++) {
236 + table.append(rows[i]);
237 + }
238 +
239 + // Remove leftover classes from "expanded" or appended rows
240 + beforeFilter.find('.king-addons-table-tr-before-remove').each(function () {
241 + $(this)
242 + .closest('.king-addons-table-row')
243 + .next('.king-addons-table-appended-tr')
244 + .remove();
245 + $(this).removeClass('king-addons-table-tr-before-remove');
246 + });
247 + });
248 + };
249 +
250 + /**
251 + * If data-row-pagination="yes", prepend an index column (#).
252 + */
253 + const initRowPagination = () => {
254 + if ($tableContainer.attr('data-row-pagination') !== 'yes') return;
255 +
256 + $scope.find('.king-addons-table-head-row').prepend(
257 + '<th class="king-addons-table-th-pag" style="vertical-align: middle;">#</th>'
258 + );
259 + initialRows.each(function (index) {
260 + $(this).prepend(
261 + `<td class="king-addons-table-td-pag" style="vertical-align: middle;">
262 + <span style="vertical-align: middle;">${index + 1}</span>
263 + </td>`
264 + );
265 + });
266 + };
267 +
268 + /**
269 + * Export buttons logic (CSV/XLS).
270 + */
271 + const initTableExport = () => {
272 + if (!$scope.find('.king-addons-table-export-button-cont').length) return;
273 +
274 + const exportBtn = $scope.find('.king-addons-table-export-button-cont .king-addons-button');
275 +
276 + const csvEscape = (value) => {
277 + const text = String(value == null ? '' : value).replace(/\r?\n/g, ' ').trim();
278 + if (/[",]/.test(text)) {
279 + return '"' + text.replace(/"/g, '""') + '"';
280 + }
281 + return text;
282 + };
283 +
284 + const tableRowsToCSV = ($view) => {
285 + const rows = $view.find(".king-addons-table-row");
286 + const data = [];
287 + rows.each((_, row) => {
288 + const cols = row.querySelectorAll(".king-addons-table-text");
289 + const rowData = Array.from(cols).map(cell => csvEscape(cell.innerText));
290 + data.push(rowData.join(","));
291 + });
292 + return data.join("\r\n");
293 + };
294 +
295 + const triggerDownload = (blob, filename) => {
296 + const url = URL.createObjectURL(blob);
297 + const downloadLink = document.createElement("a");
298 + downloadLink.download = filename;
299 + downloadLink.href = url;
300 + downloadLink.rel = "noopener";
301 + downloadLink.style.position = "fixed";
302 + downloadLink.style.left = "-9999px";
303 + document.body.appendChild(downloadLink);
304 + downloadLink.click();
305 + setTimeout(() => {
306 + URL.revokeObjectURL(url);
307 + downloadLink.remove();
308 + }, 1500);
309 + };
310 +
311 + exportBtn.each(function () {
312 + const $btn = $(this);
313 + if ($btn.hasClass('king-addons-xls')) {
314 + $btn.on('click', (event) => {
315 + event.preventDefault();
316 + const table = $scope.find('table').get(0);
317 + if (!table) {
318 + return;
319 + }
320 + const html = '<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel"><head><meta charset="UTF-8"></head><body>' + table.outerHTML + '</body></html>';
321 + triggerDownload(new Blob(['\uFEFF' + html], { type: 'application/vnd.ms-excel' }), 'export.xls');
322 + });
323 + } else if ($btn.hasClass('king-addons-csv')) {
324 + $btn.on('click', (event) => {
325 + event.preventDefault();
326 + const csv = tableRowsToCSV($scope.find('.king-addons-data-table'));
327 + triggerDownload(new Blob(['\uFEFF' + csv], { type: 'text/csv;charset=utf-8;' }), 'export.csv');
328 + });
329 + }
330 + });
331 + };
332 +
333 + /**
334 + * Initializes custom pagination if enabled.
335 + */
336 + const initCustomPagination = () => {
337 + if ($tableContainer.attr('data-custom-pagination') !== 'yes') return;
338 +
339 + // Show only the first set of rows (page 1) initially
340 + const itemsPerPage = getItemsPerPage();
341 + const firstPageRows = initialRows.filter((i) => i < itemsPerPage);
342 + $tableBody.html(firstPageRows);
343 +
344 + adjustPaginationList();
345 +
346 + // When clicking on a specific page number
347 + paginationListItems.on('click', function () {
348 + // Remove the old active, set the new active
349 + paginationListItems.removeClass('king-addons-active-pagination-item');
350 + $(this).addClass('king-addons-active-pagination-item');
351 + adjustPaginationList();
352 +
353 + const pageIndex = +$(this).text();
354 + // For direct page clicks, we do NOT revert to beforeFilter if search is empty
355 + displayRowsForPage(pageIndex, false);
356 + });
357 +
358 + // Handle prev/next clicks
359 + $scope.find('.king-addons-table-prev-next').each(function () {
360 + if ($(this).hasClass('king-addons-table-custom-pagination-prev')) {
361 + $(this).on('click', () => changePage(-1));
362 + } else {
363 + $(this).on('click', () => changePage(1));
364 + }
365 + });
366 + };
367 +
368 + // ---------------------------------------------------
369 + // Execute initializations
370 + // ---------------------------------------------------
371 + initCustomPagination();
372 + initLiveSearch();
373 + initTableSorting();
374 + initRowPagination();
375 + initTableExport();
376 +
377 + // Finally, remove "hidden" class so the table becomes visible
378 + $tableContainer.removeClass('king-addons-hide-table-before-arrange');
379 + updateEntryInfo();
380 + },
381 + }),
382 + { $element: $scope }
383 + );
384 + });
385 + });
386 +})(jQuery);