PluginProbe
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder / 51.1.83
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder v51.1.83
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 51.1.45 51.1.46 All 39 releases
king-addons / includes / extensions / Pricing_Table_Builder / assets / admin.js

admin.js in King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder 51.1.83, at includes/extensions/Pricing_Table_Builder/assets/admin.js

667 lines 22.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Pricing Table Builder - Admin JavaScript
3 * Handles editor interactions and live preview
4 */
5
6 (function($) {
7 'use strict';
8
9 // Config state
10 let config = {};
11 let previewTimeout = null;
12
13 /**
14 * Initialize the editor
15 */
16 function init() {
17 // Get initial config from window (set by inline script in template)
18 config = window.kngPTInitialConfig || getDefaultConfig();
19
20 initTabs();
21 initPlans();
22 initBillingToggle();
23 initAlignmentButtons();
24 initPresetSelector();
25 initDeviceToggle();
26 initCopyButtons();
27 initSaveButtons();
28 initColorPickers();
29 bindChangeEvents();
30 updatePreview();
31 }
32
33 /**
34 * Get default config if none provided
35 */
36 function getDefaultConfig() {
37 return {
38 schema_version: 1,
39 table: {
40 name: '',
41 status: 'draft',
42 layout: {
43 mode: 'cards',
44 columns_desktop: 3,
45 columns_tablet: 2,
46 columns_mobile: 1,
47 gap: 24,
48 max_width: 1200,
49 alignment: 'center'
50 }
51 },
52 billing: {
53 enabled: true,
54 type: 'segmented',
55 periods: [
56 { key: 'monthly', label: 'Monthly', suffix: '/mo', is_default: false },
57 { key: 'annual', label: 'Annual', suffix: '/yr', is_default: true, badge: { enabled: true, text: 'Save 16%' } }
58 ],
59 currency: '$',
60 currency_position: 'before'
61 },
62 plans: [],
63 features: {
64 mode: 'per_plan',
65 show_icons: true
66 },
67 style: {
68 preset_id: 'free_modern_cards',
69 tokens: {},
70 overrides: { custom_css_class: '' }
71 },
72 advanced: {
73 hide_toggle: false,
74 force_period: '',
75 disable_animations: false
76 }
77 };
78 }
79
80 /**
81 * Tab switching
82 */
83 function initTabs() {
84 $('.kng-pt-tab').on('click', function() {
85 const tab = $(this).data('tab');
86
87 $('.kng-pt-tab').removeClass('is-active');
88 $(this).addClass('is-active');
89
90 $('.kng-pt-tab-panel').removeClass('is-active');
91 $(`.kng-pt-tab-panel[data-panel="${tab}"]`).addClass('is-active');
92 });
93 }
94
95 /**
96 * Initialize plans list
97 */
98 function initPlans() {
99 renderPlans();
100
101 // Make plans sortable
102 $('#kng-pt-plans-list').sortable({
103 handle: '.kng-pt-plan-drag',
104 update: function() {
105 updatePlansOrder();
106 schedulePreview();
107 }
108 });
109
110 // Add plan button
111 $('#kng-pt-add-plan').on('click', function() {
112 const newPlan = createDefaultPlan();
113 config.plans = config.plans || [];
114 config.plans.push(newPlan);
115 renderPlans();
116 schedulePreview();
117 });
118 }
119
120 /**
121 * Render plans list
122 */
123 function renderPlans() {
124 const $list = $('#kng-pt-plans-list');
125 const template = $('#kng-pt-plan-template').html();
126
127 $list.empty();
128
129 (config.plans || []).forEach((plan, index) => {
130 const $plan = $(template);
131 $plan.attr('data-plan-id', plan.id);
132 $plan.find('.kng-pt-plan-name-input').val(plan.name);
133 $plan.find('.kng-pt-plan-subtitle').val(plan.subtitle || '');
134 $plan.find('.kng-pt-plan-highlight').prop('checked', plan.highlight?.enabled);
135 $plan.find('.kng-pt-plan-badge-enabled').prop('checked', plan.badge?.enabled);
136 $plan.find('.kng-pt-plan-badge-text').val(plan.badge?.text || '');
137
138 if (plan.badge?.enabled) {
139 $plan.find('.kng-pt-plan-badge-fields').show();
140 }
141
142 // Pricing
143 const periods = config.billing?.periods || [];
144 periods.forEach(period => {
145 const $periodEl = $plan.find(`.kng-pt-plan-pricing-period[data-period="${period.key}"]`);
146 const priceData = plan.pricing?.[period.key] || {};
147 $periodEl.find('.kng-pt-plan-price').val(priceData.price || '');
148 $periodEl.find('.kng-pt-plan-note').val(priceData.note || '');
149 });
150
151 // CTA
152 $plan.find('.kng-pt-plan-cta-text').val(plan.cta?.text || 'Get Started');
153 $plan.find('.kng-pt-plan-cta-url').val(plan.cta?.url || '#');
154 $plan.find('.kng-pt-plan-cta-style').val(plan.cta?.style || 'primary');
155 $plan.find('.kng-pt-plan-cta-target').val(plan.cta?.target || '_self');
156
157 // Features
158 renderPlanFeatures($plan, plan.features || []);
159
160 $list.append($plan);
161 });
162
163 // Bind plan events
164 bindPlanEvents();
165 }
166
167 /**
168 * Render features for a plan
169 */
170 function renderPlanFeatures($plan, features) {
171 const $list = $plan.find('.kng-pt-plan-features-list');
172 const template = $('#kng-pt-feature-template').html();
173
174 $list.empty();
175
176 features.forEach((feature, index) => {
177 const $feature = $(template);
178 $feature.attr('data-index', index);
179 $feature.find('.kng-pt-feature-text').val(feature.text);
180 $feature.find('.kng-pt-feature-state').val(feature.state);
181 $list.append($feature);
182 });
183
184 // Make features sortable
185 $list.sortable({
186 handle: '.kng-pt-feature-drag',
187 update: function() {
188 updatePlanFromDOM($plan);
189 schedulePreview();
190 }
191 });
192 }
193
194 /**
195 * Bind events to plan items
196 */
197 function bindPlanEvents() {
198 // Toggle plan body
199 $('.kng-pt-plan-toggle').off('click').on('click', function(e) {
200 e.stopPropagation();
201 $(this).closest('.kng-pt-plan-item').toggleClass('is-collapsed');
202 });
203
204 // Delete plan
205 $('.kng-pt-plan-delete').off('click').on('click', function(e) {
206 e.stopPropagation();
207 if (confirm(kngPTAdmin.i18n.confirmDeletePlan)) {
208 const planId = $(this).closest('.kng-pt-plan-item').data('plan-id');
209 config.plans = config.plans.filter(p => p.id !== planId);
210 renderPlans();
211 schedulePreview();
212 }
213 });
214
215 // Badge toggle
216 $('.kng-pt-plan-badge-enabled').off('change').on('change', function() {
217 const $fields = $(this).closest('.kng-pt-plan-body').find('.kng-pt-plan-badge-fields');
218 $fields.toggle(this.checked);
219 updatePlanFromDOM($(this).closest('.kng-pt-plan-item'));
220 schedulePreview();
221 });
222
223 // Add feature
224 $('.kng-pt-add-feature').off('click').on('click', function() {
225 const $plan = $(this).closest('.kng-pt-plan-item');
226 const planId = $plan.data('plan-id');
227 const plan = config.plans.find(p => p.id === planId);
228
229 if (plan) {
230 plan.features = plan.features || [];
231 plan.features.push({ text: '', state: 'enabled' });
232 renderPlanFeatures($plan, plan.features);
233 bindFeatureEvents();
234 schedulePreview();
235 }
236 });
237
238 // Delete feature
239 bindFeatureEvents();
240
241 // Plan field changes
242 $('.kng-pt-plan-name-input, .kng-pt-plan-subtitle, .kng-pt-plan-highlight, .kng-pt-plan-badge-text, .kng-pt-plan-price, .kng-pt-plan-note, .kng-pt-plan-cta-text, .kng-pt-plan-cta-url, .kng-pt-plan-cta-style, .kng-pt-plan-cta-target')
243 .off('input change')
244 .on('input change', function() {
245 updatePlanFromDOM($(this).closest('.kng-pt-plan-item'));
246 schedulePreview();
247 });
248 }
249
250 /**
251 * Bind feature item events
252 */
253 function bindFeatureEvents() {
254 $('.kng-pt-feature-delete').off('click').on('click', function() {
255 const $plan = $(this).closest('.kng-pt-plan-item');
256 $(this).closest('.kng-pt-feature-item').remove();
257 updatePlanFromDOM($plan);
258 schedulePreview();
259 });
260
261 $('.kng-pt-feature-text, .kng-pt-feature-state').off('input change').on('input change', function() {
262 const $plan = $(this).closest('.kng-pt-plan-item');
263 updatePlanFromDOM($plan);
264 schedulePreview();
265 });
266 }
267
268 /**
269 * Update plan object from DOM
270 */
271 function updatePlanFromDOM($plan) {
272 const planId = $plan.data('plan-id');
273 const plan = config.plans.find(p => p.id === planId);
274
275 if (!plan) return;
276
277 plan.name = $plan.find('.kng-pt-plan-name-input').val();
278 plan.subtitle = $plan.find('.kng-pt-plan-subtitle').val();
279 plan.highlight = {
280 enabled: $plan.find('.kng-pt-plan-highlight').prop('checked'),
281 style: 'border'
282 };
283 plan.badge = {
284 enabled: $plan.find('.kng-pt-plan-badge-enabled').prop('checked'),
285 text: $plan.find('.kng-pt-plan-badge-text').val(),
286 style: 'pill'
287 };
288
289 // Pricing
290 const periods = config.billing?.periods || [];
291 plan.pricing = plan.pricing || {};
292 periods.forEach(period => {
293 const $periodEl = $plan.find(`.kng-pt-plan-pricing-period[data-period="${period.key}"]`);
294 plan.pricing[period.key] = {
295 price: $periodEl.find('.kng-pt-plan-price').val(),
296 note: $periodEl.find('.kng-pt-plan-note').val()
297 };
298 });
299
300 // CTA
301 plan.cta = {
302 enabled: true,
303 text: $plan.find('.kng-pt-plan-cta-text').val(),
304 url: $plan.find('.kng-pt-plan-cta-url').val(),
305 style: $plan.find('.kng-pt-plan-cta-style').val(),
306 target: $plan.find('.kng-pt-plan-cta-target').val()
307 };
308
309 // Features
310 plan.features = [];
311 $plan.find('.kng-pt-feature-item').each(function() {
312 plan.features.push({
313 text: $(this).find('.kng-pt-feature-text').val(),
314 state: $(this).find('.kng-pt-feature-state').val()
315 });
316 });
317 }
318
319 /**
320 * Update plans order after sorting
321 */
322 function updatePlansOrder() {
323 const newOrder = [];
324 $('#kng-pt-plans-list .kng-pt-plan-item').each(function(index) {
325 const planId = $(this).data('plan-id');
326 const plan = config.plans.find(p => p.id === planId);
327 if (plan) {
328 plan.order = index + 1;
329 newOrder.push(plan);
330 }
331 });
332 config.plans = newOrder;
333 }
334
335 /**
336 * Create default plan object
337 */
338 function createDefaultPlan() {
339 const id = 'plan_' + Date.now();
340 const order = (config.plans?.length || 0) + 1;
341
342 return {
343 id: id,
344 order: order,
345 name: kngPTAdmin.i18n.planName + ' ' + order,
346 subtitle: '',
347 badge: { enabled: false, text: '', style: 'pill' },
348 highlight: { enabled: false, style: 'border' },
349 pricing: {
350 monthly: { price: '0', note: '' },
351 annual: { price: '0', note: '' }
352 },
353 cta: {
354 enabled: true,
355 text: 'Get Started',
356 url: '#',
357 style: 'secondary',
358 target: '_self'
359 },
360 features: []
361 };
362 }
363
364 /**
365 * Initialize billing toggle
366 */
367 function initBillingToggle() {
368 $('#kng-pt-billing-enabled').on('change', function() {
369 config.billing = config.billing || {};
370 config.billing.enabled = this.checked;
371 $('.kng-pt-billing-options').toggle(this.checked);
372 schedulePreview();
373 });
374
375 // Period badge toggle
376 $('.kng-pt-period-badge-enabled').on('change', function() {
377 $(this).closest('.kng-pt-period-item').find('.kng-pt-period-badge-text-wrap').toggle(this.checked);
378 updatePeriodsFromDOM();
379 schedulePreview();
380 });
381
382 // Period fields
383 $('.kng-pt-period-key, .kng-pt-period-label, .kng-pt-period-suffix, .kng-pt-period-badge-text').on('input', function() {
384 updatePeriodsFromDOM();
385 schedulePreview();
386 });
387
388 // Default period radio
389 $('input[name="default_period"]').on('change', function() {
390 updatePeriodsFromDOM();
391 schedulePreview();
392 });
393
394 // Billing type
395 $('input[name="billing_type"]').on('change', function() {
396 config.billing = config.billing || {};
397 config.billing.type = this.value;
398 schedulePreview();
399 });
400
401 // Currency
402 $('#kng-pt-currency, #kng-pt-currency-pos').on('input change', function() {
403 config.billing = config.billing || {};
404 config.billing.currency = $('#kng-pt-currency').val();
405 config.billing.currency_position = $('#kng-pt-currency-pos').val();
406 schedulePreview();
407 });
408 }
409
410 /**
411 * Update periods config from DOM
412 */
413 function updatePeriodsFromDOM() {
414 const periods = [];
415 const defaultPeriod = $('input[name="default_period"]:checked').val();
416
417 $('.kng-pt-period-item').each(function() {
418 const key = $(this).find('.kng-pt-period-key').val();
419 periods.push({
420 key: key,
421 label: $(this).find('.kng-pt-period-label').val(),
422 suffix: $(this).find('.kng-pt-period-suffix').val(),
423 is_default: key === defaultPeriod,
424 badge: {
425 enabled: $(this).find('.kng-pt-period-badge-enabled').prop('checked'),
426 text: $(this).find('.kng-pt-period-badge-text').val()
427 }
428 });
429 });
430
431 config.billing = config.billing || {};
432 config.billing.periods = periods;
433 }
434
435 /**
436 * Initialize alignment buttons
437 */
438 function initAlignmentButtons() {
439 $('.kng-v3-btn-group-item').on('click', function() {
440 const value = $(this).data('value');
441 $(this).siblings().removeClass('is-active');
442 $(this).addClass('is-active');
443 $('#kng-pt-alignment').val(value);
444
445 config.table = config.table || {};
446 config.table.layout = config.table.layout || {};
447 config.table.layout.alignment = value;
448 schedulePreview();
449 });
450 }
451
452 /**
453 * Initialize preset selector
454 */
455 function initPresetSelector() {
456 $('.kng-pt-preset-card:not(.is-disabled)').on('click', function() {
457 const presetId = $(this).data('preset');
458
459 $('.kng-pt-preset-card').removeClass('is-active');
460 $(this).addClass('is-active');
461
462 config.style = config.style || {};
463 config.style.preset_id = presetId;
464 schedulePreview();
465 });
466 }
467
468 /**
469 * Initialize device toggle for preview
470 */
471 function initDeviceToggle() {
472 $('.kng-pt-device-btn').on('click', function() {
473 const device = $(this).data('device');
474
475 $('.kng-pt-device-btn').removeClass('is-active');
476 $(this).addClass('is-active');
477
478 $('#kng-pt-preview-frame').attr('data-device', device);
479 });
480 }
481
482 /**
483 * Initialize copy buttons
484 */
485 function initCopyButtons() {
486 $(document).on('click', '.kng-pt-copy-btn', function() {
487 const text = $(this).data('copy') || $(this).siblings('.kng-pt-shortcode, .kng-pt-shortcode-code').text();
488
489 navigator.clipboard.writeText(text).then(() => {
490 const $btn = $(this);
491 const originalHtml = $btn.html();
492 $btn.html('<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16"><polyline points="20 6 9 17 4 12"/></svg>');
493 setTimeout(() => $btn.html(originalHtml), 1500);
494 });
495 });
496 }
497
498 /**
499 * Initialize save buttons
500 */
501 function initSaveButtons() {
502 $('#kng-pt-save-draft').on('click', function() {
503 config.table = config.table || {};
504 config.table.status = 'draft';
505 saveTable();
506 });
507
508 $('#kng-pt-publish').on('click', function() {
509 config.table = config.table || {};
510 config.table.status = 'publish';
511 saveTable();
512 });
513
514 $('#kng-pt-preview-btn').on('click', function() {
515 updatePreview();
516 });
517 }
518
519 /**
520 * Save table
521 */
522 function saveTable() {
523 // Update config from form
524 collectConfigFromForm();
525
526 // Set config JSON to hidden input
527 $('#kng-pt-config-input').val(JSON.stringify(config));
528
529 // Submit form
530 $('#kng-pt-form').submit();
531 }
532
533 /**
534 * Collect all config values from form
535 */
536 function collectConfigFromForm() {
537 // Table settings
538 config.table = config.table || {};
539 config.table.name = $('#kng-pt-name').val();
540 config.table.layout = config.table.layout || {};
541 config.table.layout.mode = $('input[name="layout_mode"]:checked').val();
542 config.table.layout.columns_desktop = parseInt($('#kng-pt-cols-desktop').val());
543 config.table.layout.columns_tablet = parseInt($('#kng-pt-cols-tablet').val());
544 config.table.layout.columns_mobile = parseInt($('#kng-pt-cols-mobile').val());
545 config.table.layout.gap = parseInt($('#kng-pt-gap').val());
546 config.table.layout.max_width = parseInt($('#kng-pt-max-width').val());
547 config.table.layout.alignment = $('#kng-pt-alignment').val();
548
549 // Features settings
550 config.features = config.features || {};
551 config.features.mode = $('input[name="features_mode"]:checked').val();
552 config.features.show_icons = $('#kng-pt-show-icons').prop('checked');
553
554 // Style settings
555 config.style = config.style || {};
556 config.style.tokens = config.style.tokens || {};
557 config.style.tokens.colors = config.style.tokens.colors || {};
558
559 const accentColor = $('#kng-pt-color-accent').val();
560 const cardBg = $('#kng-pt-color-card-bg').val();
561 const textColor = $('#kng-pt-color-text').val();
562
563 if (accentColor) config.style.tokens.colors.accent = accentColor;
564 if (cardBg) config.style.tokens.colors.card_bg = cardBg;
565 if (textColor) config.style.tokens.colors.text = textColor;
566
567 config.style.overrides = config.style.overrides || {};
568 config.style.overrides.custom_css_class = $('#kng-pt-custom-class').val();
569
570 // Advanced settings
571 config.advanced = config.advanced || {};
572 config.advanced.hide_toggle = $('#kng-pt-hide-toggle').prop('checked');
573 config.advanced.force_period = $('#kng-pt-force-period').val();
574 config.advanced.disable_animations = $('#kng-pt-disable-animations').prop('checked');
575 }
576
577 /**
578 * Initialize color pickers
579 */
580 function initColorPickers() {
581 if ($.fn.wpColorPicker) {
582 $('.kng-v3-color-input').wpColorPicker({
583 change: function() {
584 schedulePreview();
585 },
586 clear: function() {
587 schedulePreview();
588 }
589 });
590 }
591 }
592
593 /**
594 * Bind change events to form fields
595 */
596 function bindChangeEvents() {
597 // General tab
598 $('#kng-pt-name, #kng-pt-cols-desktop, #kng-pt-cols-tablet, #kng-pt-cols-mobile, #kng-pt-gap, #kng-pt-max-width')
599 .on('input change', function() {
600 collectConfigFromForm();
601 schedulePreview();
602 });
603
604 $('input[name="layout_mode"]').on('change', function() {
605 collectConfigFromForm();
606 schedulePreview();
607 });
608
609 // Features tab
610 $('input[name="features_mode"], #kng-pt-show-icons').on('change', function() {
611 collectConfigFromForm();
612 schedulePreview();
613 });
614
615 // Advanced tab
616 $('#kng-pt-hide-toggle, #kng-pt-force-period, #kng-pt-disable-animations').on('change', function() {
617 collectConfigFromForm();
618 schedulePreview();
619 });
620
621 // Custom class
622 $('#kng-pt-custom-class').on('input', function() {
623 collectConfigFromForm();
624 schedulePreview();
625 });
626 }
627
628 /**
629 * Schedule preview update (debounced)
630 */
631 function schedulePreview() {
632 clearTimeout(previewTimeout);
633 previewTimeout = setTimeout(updatePreview, 300);
634 }
635
636 /**
637 * Update preview via AJAX
638 */
639 function updatePreview() {
640 collectConfigFromForm();
641
642 $.ajax({
643 url: kngPTAdmin.ajaxUrl,
644 method: 'POST',
645 data: {
646 action: 'kng_pt_preview',
647 nonce: kngPTAdmin.nonce,
648 config: JSON.stringify(config)
649 },
650 success: function(response) {
651 if (response.success && response.data.html) {
652 $('#kng-pt-preview-content').html(response.data.html);
653
654 // Reinitialize frontend JS for the preview
655 if (window.kngPTFrontend) {
656 window.kngPTFrontend.init();
657 }
658 }
659 }
660 });
661 }
662
663 // Initialize when DOM is ready
664 $(document).ready(init);
665
666 })(jQuery);
667