PluginProbe
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder / 51.1.86
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder v51.1.86
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 51.1.45 All 40 releases
← All changes | includes/extensions/Custom_Code_Manager/assets/admin.js +131 -142 51.1.44 → 51.1.86 View file →
@@ -64,8 +64,15 @@
64 64 $(document).on('click', '.kng-cc-export-btn', handleExportSingle);
65 65
66 66 // Delete button
67 67 $(document).on('click', '.kng-cc-delete-btn', handleDelete);
68 +
69 + // Pro upgrade popup
70 + $('#kng-cc-add-new-limit').on('click', showProPopup);
71 + $('#kng-cc-pro-popup-close, .kng-cc-pro-popup-dismiss').on('click', hideProPopup);
72 + $('#kng-cc-pro-popup').on('click', function(e) {
73 + if (e.target === this) hideProPopup();
74 + });
68 75 }
69 76
70 77 function filterSnippets() {
71 78 const search = $('#kng-cc-search').val().toLowerCase();
@@ -173,8 +180,17 @@
173 180 function handleDuplicate(e) {
174 181 e.preventDefault();
175 182 const id = $(this).data('id');
176 183
184 + // Check limit client-side first
185 + if (!kngCCAdmin.hasPro) {
186 + const currentCount = $('.kng-cc-row').length;
187 + if (currentCount >= kngCCAdmin.freeLimit) {
188 + showProPopup();
189 + return;
190 + }
191 + }
192 +
177 193 $.ajax({
178 194 url: kngCCAdmin.ajaxUrl,
179 195 method: 'POST',
180 196 data: {
@@ -186,9 +202,14 @@
186 202 if (response.success) {
187 203 showNotice(kngCCAdmin.strings.duplicated, 'success');
188 204 location.reload();
189 205 } else {
190 - showNotice(response.data.message || kngCCAdmin.strings.error, 'error');
206 + // If server returns limit error, show popup instead of error
207 + if (!kngCCAdmin.hasPro && response.data && (response.data.limit || (response.data.message && response.data.message.indexOf('limit') !== -1))) {
208 + showProPopup();
209 + } else {
210 + showNotice(response.data.message || kngCCAdmin.strings.error, 'error');
211 + }
191 212 }
192 213 },
193 214 error: function() {
194 215 showNotice(kngCCAdmin.strings.error, 'error');
@@ -208,10 +229,29 @@
208 229 nonce: kngCCAdmin.nonce,
209 230 id: id
210 231 },
211 232 success: function(response) {
212 - if (response.success) {
213 - downloadJSON(response.data.export, 'snippet-' + id + '.json');
233 + if (response.success && response.data.export.snippets && response.data.export.snippets.length) {
234 + var snippet = response.data.export.snippets[0];
235 + var code = snippet.code || '';
236 + var type = snippet.type || 'txt';
237 + var title = (snippet.title || 'snippet').replace(/[^a-zA-Z0-9_-]/g, '_');
238 +
239 + var extMap = { css: '.css', js: '.js', html: '.html' };
240 + var mimeMap = { css: 'text/css', js: 'application/javascript', html: 'text/html' };
241 + var ext = extMap[type] || '.txt';
242 + var mime = mimeMap[type] || 'text/plain';
243 +
244 + var blob = new Blob([code], { type: mime });
245 + var url = URL.createObjectURL(blob);
246 + var a = document.createElement('a');
247 + a.href = url;
248 + a.download = title + ext;
249 + document.body.appendChild(a);
250 + a.click();
251 + document.body.removeChild(a);
252 + URL.revokeObjectURL(url);
253 +
214 254 showNotice(kngCCAdmin.strings.exportReady, 'success');
215 255 } else {
216 256 showNotice(response.data.message || kngCCAdmin.strings.error, 'error');
217 257 }
@@ -449,10 +489,8 @@
449 489 }
450 490
451 491 function saveSnippet() {
452 492 const $btn = $('#kng-cc-save-btn');
453 - const $saveIcon = $btn.find('.kng-cc-save-icon');
454 - const $spinner = $btn.find('.kng-cc-spinner');
455 493 const $text = $btn.find('.kng-cc-save-text');
456 494
457 495 // Collect form data
458 496 collectRulesFromForm();
@@ -482,11 +520,10 @@
482 520 $('#kng-cc-title').focus();
483 521 return;
484 522 }
485 523
486 - // Show loading
487 - $saveIcon.hide();
488 - $spinner.show();
524 + // Show saving state
525 + $btn.removeClass('is-saved').addClass('is-saving');
489 526 $text.text(kngCCAdmin.strings.saving);
490 527 $btn.prop('disabled', true);
491 528
492 529 $.ajax({
@@ -498,8 +535,10 @@
498 535 snippet: JSON.stringify(snippet)
499 536 },
500 537 success: function(response) {
501 538 if (response.success) {
539 + // Show success state: green + checkmark
540 + $btn.removeClass('is-saving').addClass('is-saved');
502 541 $text.text(kngCCAdmin.strings.saved);
503 542
504 543 // Update ID if new snippet
505 544 if (!snippet.id && response.data.id) {
@@ -509,14 +548,14 @@
509 548 const newUrl = window.location.href.replace('view=new', 'view=edit&id=' + response.data.id);
510 549 window.history.replaceState({}, '', newUrl);
511 550 }
512 551
552 + // Return to normal after delay
513 553 setTimeout(function() {
514 - $spinner.hide();
515 - $saveIcon.show();
554 + $btn.removeClass('is-saved');
516 555 $text.text('Save Snippet');
517 556 $btn.prop('disabled', false);
518 - }, 1500);
557 + }, 2000);
519 558 } else {
520 559 showNotice(response.data.message || kngCCAdmin.strings.error, 'error');
521 560 resetSaveButton();
522 561 }
@@ -527,10 +566,9 @@
527 566 }
528 567 });
529 568
530 569 function resetSaveButton() {
531 - $spinner.hide();
532 - $saveIcon.show();
570 + $btn.removeClass('is-saving is-saved');
533 571 $text.text('Save Snippet');
534 572 $btn.prop('disabled', false);
535 573 }
536 574 }
@@ -646,9 +684,9 @@
646 684 switch (type) {
647 685 case 'page':
648 686 case 'post':
649 687 html = '<div class="kng-cc-rule-search-wrap">' +
650 - '<input type="text" class="kng-v3-input kng-cc-rule-search" placeholder="Search ' + type + 's..." data-type="' + type + '" />' +
688 + '<input type="text" class="kng-v3-input kng-cc-rule-search" placeholder="Search by title or ID..." data-type="' + type + '" />' +
651 689 '<input type="hidden" class="kng-cc-rule-value-input" name="rules[' + index + '][value]" value="' + (rule.value || '') + '" />' +
652 690 '<div class="kng-cc-rule-search-results"></div>' +
653 691 '</div>';
654 692 break;
@@ -759,9 +797,9 @@
759 797 if (response.success && response.data.length) {
760 798 let html = '';
761 799 response.data.forEach(function(item) {
762 800 html += '<div class="kng-cc-search-result" data-id="' + item.id + '" data-title="' + escapeHtml(item.title) + '">' +
763 - escapeHtml(item.title) +
801 + escapeHtml(item.title) + ' <span style="color:var(--kng-v3-muted);font-size:12px;">(ID: ' + item.id + ')</span>' +
764 802 '</div>';
765 803 });
766 804 $results.html(html).show();
767 805 } else {
@@ -1004,8 +1042,16 @@
1004 1042 // =========================================================================
1005 1043 // Utilities
1006 1044 // =========================================================================
1007 1045
1046 + function showProPopup() {
1047 + $('#kng-cc-pro-popup').fadeIn(200);
1048 + }
1049 +
1050 + function hideProPopup() {
1051 + $('#kng-cc-pro-popup').fadeOut(150);
1052 + }
1053 +
1008 1054 function showNotice(message, type) {
1009 1055 // Remove existing notices
1010 1056 $('.kng-cc-notice').remove();
1011 1057
@@ -1162,140 +1208,83 @@
1162 1208 document.head.appendChild(style);
1163 1209 })();
1164 1210
1165 1211 // =========================================================================
1166 -// Theme Switcher
1212 +// Theme Switcher (ka-v3-theme-segment)
1167 1213 // =========================================================================
1168 1214
1169 -(function initThemeSwitcher() {
1215 +(function initThemeSwitcher($) {
1170 1216 'use strict';
1171 -
1172 - const THEME_KEY = 'kng_cc_theme';
1173 - const $admin = $('.kng-cc-admin');
1174 -
1175 - if (!$admin.length) return;
1176 -
1177 - // Get saved theme or default to auto
1178 - let currentTheme = localStorage.getItem(THEME_KEY) || 'auto';
1179 -
1180 - // Apply saved theme
1181 - applyTheme(currentTheme);
1182 -
1183 - // Create theme switcher if in header
1184 - const $headerRight = $('.kng-cc-header-right');
1185 - if ($headerRight.length && !$('.kng-cc-theme-switcher').length) {
1186 - const switcher = `
1187 - <div class="kng-cc-theme-switcher">
1188 - <button type="button" class="kng-cc-theme-btn" data-theme="light" title="Light Theme">
1189 - <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1190 - <circle cx="12" cy="12" r="5"/>
1191 - <line x1="12" y1="1" x2="12" y2="3"/>
1192 - <line x1="12" y1="21" x2="12" y2="23"/>
1193 - <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/>
1194 - <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/>
1195 - <line x1="1" y1="12" x2="3" y2="12"/>
1196 - <line x1="21" y1="12" x2="23" y2="12"/>
1197 - <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/>
1198 - <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/>
1199 - </svg>
1200 - </button>
1201 - <button type="button" class="kng-cc-theme-btn" data-theme="auto" title="Auto Theme">
1202 - <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1203 - <rect x="2" y="3" width="20" height="14" rx="2" ry="2"/>
1204 - <line x1="8" y1="21" x2="16" y2="21"/>
1205 - <line x1="12" y1="17" x2="12" y2="21"/>
1206 - </svg>
1207 - </button>
1208 - <button type="button" class="kng-cc-theme-btn" data-theme="dark" title="Dark Theme">
1209 - <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1210 - <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
1211 - </svg>
1212 - </button>
1213 - </div>
1214 - `;
1215 - $headerRight.prepend(switcher);
1216 -
1217 - // Add event listeners
1218 - $('.kng-cc-theme-btn').on('click', function() {
1219 - const theme = $(this).data('theme');
1220 - setTheme(theme);
1217 +
1218 + var $segment = $('#ka-v3-theme-segment');
1219 + if (!$segment.length) return;
1220 +
1221 + var $buttons = $segment.find('.ka-v3-segmented-btn');
1222 + var mql = window.matchMedia ? window.matchMedia('(prefers-color-scheme: dark)') : null;
1223 + var mode = ($segment.attr('data-active') || 'dark').toString();
1224 + var mqlHandler = null;
1225 +
1226 + function saveUISetting(value) {
1227 + if (!window.kngCCAdmin) return;
1228 + $.post(kngCCAdmin.ajaxUrl, {
1229 + action: 'king_addons_save_dashboard_ui',
1230 + nonce: kngCCAdmin.themeNonce,
1231 + key: 'theme_mode',
1232 + value: value
1221 1233 });
1222 1234 }
1223 -
1224 - function applyTheme(theme) {
1225 - currentTheme = theme;
1226 -
1227 - // Remove all theme classes
1228 - $admin.removeClass('kng-theme-light kng-theme-dark').removeAttr('data-theme');
1229 -
1230 - // Update active button
1231 - $('.kng-cc-theme-btn').removeClass('is-active');
1232 - $(`.kng-cc-theme-btn[data-theme="${theme}"]`).addClass('is-active');
1233 -
1234 - if (theme === 'light') {
1235 - $admin.attr('data-theme', 'light');
1236 - } else if (theme === 'dark') {
1237 - $admin.attr('data-theme', 'dark').addClass('kng-theme-dark');
1238 - }
1239 - // 'auto' - no class, uses prefers-color-scheme
1235 +
1236 + function updateSegment(activeMode) {
1237 + $segment.attr('data-active', activeMode);
1238 + $buttons.each(function() {
1239 + var theme = ($(this).data('theme') || 'dark').toString();
1240 + $(this).attr('aria-pressed', theme === activeMode ? 'true' : 'false');
1241 + });
1240 1242 }
1241 -
1242 - function setTheme(theme) {
1243 - applyTheme(theme);
1244 - localStorage.setItem(THEME_KEY, theme);
1245 -
1246 - // Show subtle feedback
1247 - showThemeNotice(theme);
1243 +
1244 + function applyTheme(isDark) {
1245 + $('body').toggleClass('ka-v3-dark', isDark);
1246 + document.documentElement.classList.toggle('ka-v3-dark', isDark);
1248 1247 }
1249 -
1250 - function showThemeNotice(theme) {
1251 - const themeNames = {
1252 - light: 'Light Theme',
1253 - dark: 'Dark Theme',
1254 - auto: 'Auto Theme'
1255 - };
1256 -
1257 - const $notice = $(`
1258 - <div class="kng-cc-theme-notice">
1259 - ${themeNames[theme]} activated
1260 - </div>
1261 - `);
1262 -
1263 - $('body').append($notice);
1264 -
1265 - setTimeout(() => {
1266 - $notice.addClass('is-visible');
1267 - }, 10);
1268 -
1269 - setTimeout(() => {
1270 - $notice.removeClass('is-visible');
1271 - setTimeout(() => $notice.remove(), 300);
1272 - }, 2000);
1273 - }
1274 -
1275 - // Add notice styles
1276 - const style = document.createElement('style');
1277 - style.textContent = `
1278 - .kng-cc-theme-notice {
1279 - position: fixed;
1280 - bottom: 32px;
1281 - right: 32px;
1282 - background: var(--kng-v3-card-bg);
1283 - color: var(--kng-v3-text);
1284 - padding: 12px 20px;
1285 - border-radius: 100px;
1286 - font-size: 13px;
1287 - font-weight: 500;
1288 - box-shadow: var(--kng-v3-shadow-lg);
1289 - opacity: 0;
1290 - transform: translateY(20px);
1291 - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
1292 - z-index: 10000;
1293 - border: 1px solid var(--kng-v3-border);
1248 +
1249 + function setThemeMode(nextMode, save) {
1250 + mode = (nextMode || 'dark').toString();
1251 + updateSegment(mode);
1252 +
1253 + if (mqlHandler && mql) {
1254 + if (mql.removeEventListener) {
1255 + mql.removeEventListener('change', mqlHandler);
1256 + } else if (mql.removeListener) {
1257 + mql.removeListener(mqlHandler);
1258 + }
1259 + mqlHandler = null;
1294 1260 }
1295 - .kng-cc-theme-notice.is-visible {
1296 - opacity: 1;
1297 - transform: translateY(0);
1261 +
1262 + if (mode === 'auto') {
1263 + applyTheme(!!(mql && mql.matches));
1264 + mqlHandler = function(e) {
1265 + if (mode !== 'auto') return;
1266 + applyTheme(!!e.matches);
1267 + };
1268 + if (mql) {
1269 + if (mql.addEventListener) {
1270 + mql.addEventListener('change', mqlHandler);
1271 + } else if (mql.addListener) {
1272 + mql.addListener(mqlHandler);
1273 + }
1274 + }
1275 + } else {
1276 + applyTheme(mode === 'dark');
1298 1277 }
1299 - `;
1300 - document.head.appendChild(style);
1301 -})();
1278 +
1279 + if (save) {
1280 + saveUISetting(mode);
1281 + }
1282 + }
1283 +
1284 + $segment.on('click', '.ka-v3-segmented-btn', function(e) {
1285 + e.preventDefault();
1286 + setThemeMode(($(this).data('theme') || 'dark').toString(), true);
1287 + });
1288 +
1289 + setThemeMode(mode, false);
1290 +})(jQuery);