PluginProbe
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder / 51.1.14
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder v51.1.14
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
king-addons / includes / widgets / Dynamic_Posts_Grid / script.js

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

401 lines 17.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Dynamic Posts Grid Widget JavaScript
3 * King Addons for Elementor
4 */
5
6 (function ($) {
7 'use strict';
8
9 $(window).on("elementor/frontend/init", () => {
10 elementorFrontend.hooks.addAction("frontend/element_ready/king-addons-dynamic-posts-grid.default", ($scope) => {
11
12 const gridHandler = {
13 init() {
14 this.wrapper = $scope.find('.king-addons-dpg-wrapper');
15 this.grid = $scope.find('.king-addons-dpg-grid');
16 this.filterBar = $scope.find('.king-addons-dpg-filter-bar');
17 this.filterSelect = $scope.find('.king-addons-dpg-posts-filter');
18 this.searchInput = $scope.find('.king-addons-dpg-posts-search');
19 this.searchBtn = $scope.find('.king-addons-dpg-search-btn');
20 this.loadMoreBtn = $scope.find('.king-addons-dpg-load-more-btn');
21 this.pagination = $scope.find('.king-addons-dpg-pagination');
22 this.loadingDiv = $scope.find('.king-addons-dpg-pagination-loading');
23 this.finishDiv = $scope.find('.king-addons-dpg-pagination-finish');
24
25 this.settings = this.getSettings();
26 this.currentPage = 1;
27 this.isLoading = false;
28 this.currentFilter = '*';
29 this.currentSearch = '';
30
31 this.bindEvents();
32 this.initIsotope();
33 },
34
35 getSettings() {
36 return {
37 widgetId: this.wrapper.data('widget-id'),
38 postsPerPage: this.wrapper.data('posts-per-page'),
39 postTypes: this.wrapper.data('post-types'),
40 orderby: this.wrapper.data('orderby'),
41 order: this.wrapper.data('order'),
42 filterTaxonomy: this.wrapper.data('filter-taxonomy'),
43 showExcerpt: this.wrapper.data('show-excerpt'),
44 cardClickable: this.wrapper.data('card-clickable')
45 };
46 },
47
48 bindEvents() {
49 // Filter dropdown change
50 this.filterSelect.on('change', (e) => {
51 this.currentFilter = $(e.target).val();
52 this.currentPage = 1;
53 this.filterAndSearch();
54 });
55
56 // Search input events
57 this.searchInput.on('keyup', this.debounce((e) => {
58 this.currentSearch = $(e.target).val();
59 this.currentPage = 1;
60 this.filterAndSearch();
61 }, 500));
62
63 this.searchBtn.on('click', () => {
64 this.currentSearch = this.searchInput.val();
65 this.currentPage = 1;
66 this.filterAndSearch();
67 });
68
69 // Search on Enter key
70 this.searchInput.on('keypress', (e) => {
71 if (e.which === 13) {
72 this.currentSearch = this.searchInput.val();
73 this.currentPage = 1;
74 this.filterAndSearch();
75 }
76 });
77
78 // Load More button
79 this.loadMoreBtn.on('click', () => {
80 this.loadMore();
81 });
82
83 // Card click functionality
84 this.bindCardClickEvents();
85 },
86
87 bindCardClickEvents() {
88 // Only enable card clicking if setting is enabled and not in Elementor editor
89 if (this.settings.cardClickable === 1 && !elementorFrontend.isEditMode()) {
90 // Use event delegation for dynamically loaded content
91 this.wrapper.on('click', '.king-addons-dpg-card', (e) => {
92 // Don't trigger if clicking on a link or button inside the card
93 if ($(e.target).closest('a, button, .king-addons-dpg-button').length > 0) {
94 return;
95 }
96
97 // Find the post link within the card
98 const postLink = $(e.currentTarget).find('.king-addons-dpg-title a');
99 if (postLink.length > 0) {
100 const postUrl = postLink.attr('href');
101 if (postUrl) {
102 // Navigate to post
103 window.location.href = postUrl;
104 }
105 }
106 });
107
108 // Add cursor pointer style to cards when clickable
109 this.wrapper.addClass('king-addons-dpg-cards-clickable');
110 }
111 },
112
113 initIsotope() {
114 // Skip Isotope initialization in Elementor editor
115 if (elementorFrontend.isEditMode()) {
116 return;
117 }
118
119 // Initialize isotope if available
120 if (typeof $.fn.isotopekng !== 'undefined') {
121 this.grid.isotopekng({
122 itemSelector: '.king-addons-dpg-card',
123 layoutMode: 'masonry',
124 masonry: {
125 columnWidth: '.king-addons-dpg-card'
126 },
127 transitionDuration: '0.3s'
128 });
129 }
130
131 // Images loaded callback (skip in editor)
132 if (typeof $.fn.imagesLoaded !== 'undefined' && !elementorFrontend.isEditMode()) {
133 this.grid.imagesLoaded(() => {
134 this.relayoutGrid();
135 });
136 }
137 },
138
139 relayoutGrid() {
140 // Skip in Elementor editor
141 if (elementorFrontend.isEditMode()) {
142 return;
143 }
144
145 if (typeof $.fn.isotopekng !== 'undefined') {
146 this.grid.isotopekng('layout');
147 }
148 },
149
150 filterAndSearch() {
151 // Skip AJAX calls in Elementor editor
152 if (elementorFrontend.isEditMode()) {
153 return;
154 }
155
156 if (this.isLoading) return;
157
158 this.isLoading = true;
159 this.showLoading();
160
161 const ajaxData = {
162 action: 'king_addons_dynamic_posts_grid_filter',
163 nonce: window.KingAddonsDynamicPostsGrid?.nonce || '',
164 widget_id: this.settings.widgetId,
165 posts_per_page: this.settings.postsPerPage,
166 post_types: this.settings.postTypes,
167 orderby: this.settings.orderby,
168 order: this.settings.order,
169 filter_taxonomy: this.settings.filterTaxonomy,
170 filter_term: this.currentFilter,
171 search_query: this.currentSearch,
172 page: this.currentPage,
173 show_excerpt: this.settings.showExcerpt
174 };
175
176 $.ajax({
177 url: window.KingAddonsDynamicPostsGrid?.ajaxUrl || '/wp-admin/admin-ajax.php',
178 type: 'POST',
179 data: ajaxData,
180 success: (response) => {
181 this.handleFilterResponse(response);
182 },
183 error: (xhr, status, error) => {
184 console.error('Dynamic Posts Grid AJAX Error:', error);
185 this.hideLoading();
186 this.isLoading = false;
187 }
188 });
189 },
190
191 loadMore() {
192 // Skip AJAX calls in Elementor editor
193 if (elementorFrontend.isEditMode()) {
194 return;
195 }
196
197 if (this.isLoading) return;
198
199 this.currentPage++;
200 this.isLoading = true;
201 this.showLoading();
202
203 const ajaxData = {
204 action: 'king_addons_dynamic_posts_grid_load_more',
205 nonce: window.KingAddonsDynamicPostsGrid?.nonce || '',
206 widget_id: this.settings.widgetId,
207 posts_per_page: this.settings.postsPerPage,
208 post_types: this.settings.postTypes,
209 orderby: this.settings.orderby,
210 order: this.settings.order,
211 filter_taxonomy: this.settings.filterTaxonomy,
212 filter_term: this.currentFilter,
213 search_query: this.currentSearch,
214 page: this.currentPage,
215 show_excerpt: this.settings.showExcerpt
216 };
217
218 $.ajax({
219 url: window.KingAddonsDynamicPostsGrid?.ajaxUrl || '/wp-admin/admin-ajax.php',
220 type: 'POST',
221 data: ajaxData,
222 success: (response) => {
223 this.handleLoadMoreResponse(response);
224 },
225 error: (xhr, status, error) => {
226 console.error('Dynamic Posts Grid Load More Error:', error);
227 this.hideLoading();
228 this.isLoading = false;
229 this.currentPage--; // Revert page increment on error
230 }
231 });
232 },
233
234 handleFilterResponse(response) {
235 this.hideLoading();
236 this.isLoading = false;
237
238 if (response.success && response.data) {
239 // Fade out current content
240 this.grid.addClass('king-addons-dpg-zero-opacity');
241
242 setTimeout(() => {
243 // Replace grid content
244 if (typeof $.fn.isotopekng !== 'undefined' && !elementorFrontend.isEditMode()) {
245 this.grid.isotopekng('destroy');
246 }
247
248 this.grid.html(response.data.posts_html);
249
250 // Re-initialize isotope
251 this.initIsotope();
252
253 // Update pagination
254 this.updatePagination(response.data);
255
256 // Fade in new content
257 setTimeout(() => {
258 this.grid.removeClass('king-addons-dpg-zero-opacity');
259 this.animateNewItems();
260 }, 100);
261
262 }, 300);
263 } else {
264 this.showError(response.data?.message || 'Failed to load posts');
265 }
266 },
267
268 handleLoadMoreResponse(response) {
269 this.hideLoading();
270 this.isLoading = false;
271
272 if (response.success && response.data) {
273 const newItems = $(response.data.posts_html);
274
275 // Add new items to grid
276 this.grid.append(newItems);
277
278 // Animate new items
279 newItems.addClass('king-addons-dpg-fade-in');
280
281 // Re-layout isotope
282 if (typeof $.fn.isotopekng !== 'undefined' && !elementorFrontend.isEditMode()) {
283 this.grid.isotopekng('appended', newItems);
284
285 // Re-layout after images load
286 if (typeof $.fn.imagesLoaded !== 'undefined') {
287 newItems.imagesLoaded(() => {
288 this.relayoutGrid();
289 });
290 }
291 }
292
293 // Update pagination
294 this.updatePagination(response.data);
295
296 } else {
297 this.showError(response.data?.message || 'Failed to load more posts');
298 this.currentPage--; // Revert page increment
299 }
300 },
301
302 updatePagination(data) {
303 if (data.current_page >= data.max_pages) {
304 this.loadMoreBtn.hide();
305 this.showFinished(data.total_posts, data.current_count);
306 } else {
307 this.loadMoreBtn.attr('data-page', data.current_page);
308 this.loadMoreBtn.attr('data-max-pages', data.max_pages);
309 this.loadMoreBtn.show();
310 this.finishDiv.hide();
311 }
312
313 // Update counts
314 $scope.find('.king-addons-dpg-current-count').text(data.current_count);
315 $scope.find('.king-addons-dpg-total-count').text(data.total_posts);
316 },
317
318 showLoading() {
319 this.loadingDiv.show();
320 this.loadMoreBtn.prop('disabled', true);
321
322 // Add loading spinner to button
323 if (!this.loadMoreBtn.find('.king-addons-dpg-loading-spinner').length) {
324 this.loadMoreBtn.prepend('<span class="king-addons-dpg-loading-spinner"></span>');
325 }
326 },
327
328 hideLoading() {
329 this.loadingDiv.hide();
330 this.loadMoreBtn.prop('disabled', false);
331 this.loadMoreBtn.find('.king-addons-dpg-loading-spinner').remove();
332 },
333
334 showFinished(total, current) {
335 this.finishDiv.find('.king-addons-dpg-current-count').text(current);
336 this.finishDiv.find('.king-addons-dpg-total-count').text(total);
337 this.finishDiv.fadeIn(1000);
338
339 setTimeout(() => {
340 this.finishDiv.fadeOut(1000);
341 }, 3000);
342 },
343
344 showError(message) {
345 // Create and show error message
346 const errorDiv = $('<div class="king-addons-dpg-error-message">' + message + '</div>');
347 this.wrapper.prepend(errorDiv);
348
349 setTimeout(() => {
350 errorDiv.fadeOut(() => {
351 errorDiv.remove();
352 });
353 }, 5000);
354 },
355
356 animateNewItems() {
357 this.grid.find('.king-addons-dpg-card').each((index, element) => {
358 setTimeout(() => {
359 $(element).addClass('king-addons-dpg-fade-in');
360 }, index * 100);
361 });
362 },
363
364 debounce(func, wait, immediate) {
365 let timeout;
366 return function() {
367 const context = this;
368 const args = arguments;
369 const later = function() {
370 timeout = null;
371 if (!immediate) func.apply(context, args);
372 };
373 const callNow = immediate && !timeout;
374 clearTimeout(timeout);
375 timeout = setTimeout(later, wait);
376 if (callNow) func.apply(context, args);
377 };
378 }
379 };
380
381 // Initialize the grid handler
382 gridHandler.init();
383
384 // Handle responsive behavior
385 $(window).on('resize', gridHandler.debounce(() => {
386 gridHandler.relayoutGrid();
387 }, 250));
388
389 // Handle Elementor editor mode
390 if (window.elementorFrontend?.isEditMode()) {
391 // Re-initialize when settings change in editor
392 elementorFrontend.hooks.addAction('panel/open_editor/widget/king-addons-dynamic-posts-grid', () => {
393 setTimeout(() => {
394 gridHandler.init();
395 }, 100);
396 });
397 }
398 });
399 });
400
401 })(jQuery);