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/admin/js/ai-settings.js +268 -0 51.1.2 → 51.1.86 View file →
@@ -1,0 +1,268 @@
1 +(function($) {
2 + 'use strict';
3 +
4 + $(function() {
5 + // Initialize modern animations and interactions
6 + initModernAnimations();
7 +
8 + // Auto-save feedback for AI settings form specifically
9 + $('.king-addons-settings-form').on('submit', function(e) {
10 + // Ensure the form is not prevented from submitting
11 + showSaveAnimation();
12 +
13 + // Explicitly allow form submission
14 + return true;
15 + });
16 +
17 + // Input focus animations
18 + $('input, select, textarea').on('focus', function() {
19 + $(this).closest('tr').addClass('focused');
20 + }).on('blur', function() {
21 + $(this).closest('tr').removeClass('focused');
22 + });
23 +
24 + // Checkbox animations
25 + $('input[type="checkbox"]').on('change', function() {
26 + if ($(this).is(':checked')) {
27 + $(this).addClass('checked-animation');
28 + setTimeout(() => $(this).removeClass('checked-animation'), 300);
29 + }
30 + });
31 +
32 + // ---- Provider switching -------------------------------------------
33 + var $provider = $('#king-addons-ai-provider');
34 +
35 + function activeProvider() {
36 + return $provider.length ? $provider.val() : 'openai';
37 + }
38 +
39 + // Show only the rows belonging to the selected provider. The rows are
40 + // hidden by a class rather than an inline style so this stays in step
41 + // with the server-rendered initial state.
42 + function applyProviderVisibility() {
43 + var provider = activeProvider();
44 + $('.ka-ai-provider-row').each(function() {
45 + var $row = $(this);
46 + $row.toggleClass('ka-ai-row-hidden', !$row.hasClass('ka-ai-provider-' + provider));
47 + });
48 + }
49 +
50 + function apiKeyField() {
51 + return activeProvider() === 'openrouter'
52 + ? $('#king-addons-openrouter-api-key')
53 + : $('input[name="king_addons_ai_options[openai_api_key]"]');
54 + }
55 +
56 + // ---- Shared helpers -----------------------------------------------
57 + var $refreshButton = $('#king-addons-ai-refresh-models-button');
58 + var $spinner = $('#king-addons-ai-refresh-models-spinner');
59 + var $statusSpan = $('#king-addons-ai-refresh-models-status');
60 + var $testButton = $('#king-addons-ai-test-connection-button');
61 + var $testSpinner = $('#king-addons-ai-test-connection-spinner');
62 + var $testStatus = $('#king-addons-ai-test-connection-status');
63 +
64 + function showSpinner($el, visible) {
65 + if (visible) {
66 + $el.css({ visibility: 'visible', display: 'inline-block' }).addClass('is-active');
67 + } else {
68 + $el.css({ visibility: 'hidden', display: 'none' }).removeClass('is-active');
69 + }
70 + }
71 +
72 + function errorMessage(jqXHR, fallback) {
73 + try {
74 + var parsed = JSON.parse(jqXHR.responseText);
75 + if (parsed.data && parsed.data.message) {
76 + return parsed.data.message;
77 + }
78 + } catch (e) {
79 + // ignore JSON parse errors
80 + }
81 + return fallback;
82 + }
83 +
84 + // Rebuilds one model <select>, keeping the current selection when the
85 + // model is still offered, and grouping free models above paid ones.
86 + function fillModelSelect($select, models) {
87 + if (!$select.length || !Array.isArray(models)) {
88 + return;
89 + }
90 +
91 + var currentValue = $select.val();
92 + var free = [];
93 + var paid = [];
94 +
95 + models.forEach(function(model) {
96 + (model.free ? free : paid).push(model);
97 + });
98 +
99 + function optionsFor(list, $target) {
100 + list.forEach(function(model) {
101 + var $option = $('<option></option>').val(model.id).text(model.label);
102 + if (model.id === currentValue) {
103 + $option.prop('selected', true);
104 + }
105 + $target.append($option);
106 + });
107 + }
108 +
109 + $select.empty().prop('disabled', false);
110 +
111 + // Keep a saved-but-no-longer-offered model selectable.
112 + var stillOffered = models.some(function(model) {
113 + return model.id === currentValue;
114 + });
115 + if (currentValue && !stillOffered) {
116 + $select.append($('<option></option>').val(currentValue).text(currentValue).prop('selected', true));
117 + }
118 +
119 + if (free.length && paid.length) {
120 + var $freeGroup = $('<optgroup></optgroup>').attr('label', KingAddonsAiSettings.free_models_label);
121 + var $paidGroup = $('<optgroup></optgroup>').attr('label', KingAddonsAiSettings.paid_models_label);
122 + optionsFor(free, $freeGroup);
123 + optionsFor(paid, $paidGroup);
124 + $select.append($freeGroup, $paidGroup);
125 + } else {
126 + optionsFor(models, $select);
127 + }
128 + }
129 +
130 + // ---- Refresh model list -------------------------------------------
131 + $refreshButton.on('click', function() {
132 + if ($refreshButton.prop('disabled')) {
133 + return;
134 + }
135 +
136 + var provider = activeProvider();
137 + var $selects = $('.ka-ai-provider-' + provider + ' .ka-ai-model-select');
138 + if (!$selects.length) {
139 + $selects = $('.ka-ai-model-select');
140 + }
141 +
142 + $refreshButton.prop('disabled', true);
143 + showSpinner($spinner, true);
144 + $statusSpan.text(KingAddonsAiSettings.refreshing_text).css('color', '');
145 + $selects.prop('disabled', true);
146 +
147 + $.ajax({
148 + url: KingAddonsAiSettings.ajax_url,
149 + type: 'POST',
150 + data: {
151 + action: 'king_addons_ai_refresh_models',
152 + nonce: KingAddonsAiSettings.nonce,
153 + provider: provider,
154 + api_key: apiKeyField().val() || ''
155 + },
156 + dataType: 'json'
157 + }).done(function(response) {
158 + if (response.success && response.data && response.data.models) {
159 + $selects.each(function() {
160 + var $select = $(this);
161 + var type = $select.data('ka-model-type') || 'text';
162 + fillModelSelect($select, response.data.models[type]);
163 + });
164 +
165 + $statusSpan.text(KingAddonsAiSettings.refreshed_text).css('color', 'green');
166 + setTimeout(function() {
167 + $statusSpan.text('');
168 + }, 3000);
169 + } else {
170 + var message = (response.data && response.data.message) ? response.data.message : KingAddonsAiSettings.error_text;
171 + $statusSpan.text(message).css('color', 'red');
172 + }
173 + }).fail(function(jqXHR) {
174 + $statusSpan.text(errorMessage(jqXHR, KingAddonsAiSettings.error_text)).css('color', 'red');
175 + }).always(function() {
176 + showSpinner($spinner, false);
177 + $refreshButton.prop('disabled', false);
178 + $selects.prop('disabled', false);
179 + });
180 + });
181 +
182 + // ---- Test connection ----------------------------------------------
183 + $testButton.on('click', function() {
184 + if ($testButton.prop('disabled')) {
185 + return;
186 + }
187 +
188 + $testButton.prop('disabled', true);
189 + showSpinner($testSpinner, true);
190 + $testStatus.text(KingAddonsAiSettings.testing_text).css('color', '');
191 +
192 + $.ajax({
193 + url: KingAddonsAiSettings.ajax_url,
194 + type: 'POST',
195 + data: {
196 + action: 'king_addons_ai_test_connection',
197 + nonce: KingAddonsAiSettings.nonce,
198 + provider: activeProvider(),
199 + api_key: apiKeyField().val() || ''
200 + },
201 + dataType: 'json'
202 + }).done(function(response) {
203 + if (response.success && response.data && response.data.message) {
204 + $testStatus.text(response.data.message).css('color', 'green');
205 + } else {
206 + var message = (response.data && response.data.message) ? response.data.message : KingAddonsAiSettings.test_failed_text;
207 + $testStatus.text(message).css('color', 'red');
208 + }
209 + }).fail(function(jqXHR) {
210 + $testStatus.text(errorMessage(jqXHR, KingAddonsAiSettings.test_failed_text)).css('color', 'red');
211 + }).always(function() {
212 + showSpinner($testSpinner, false);
213 + $testButton.prop('disabled', false);
214 + });
215 + });
216 +
217 + $provider.on('change', function() {
218 + applyProviderVisibility();
219 + $statusSpan.text('');
220 + $testStatus.text('');
221 + });
222 +
223 + applyProviderVisibility();
224 +
225 + // Modern animation functions
226 + function initModernAnimations() {
227 + // Stagger animation for form sections
228 + $('.king-addons-ai-settings h2').each(function(index) {
229 + $(this).css({
230 + 'animation-delay': (index * 0.1) + 's',
231 + 'animation-fill-mode': 'forwards'
232 + }).addClass('slide-in-from-left');
233 + });
234 +
235 + // Stagger animation for form rows
236 + $('.king-addons-ai-settings .form-table tr').each(function(index) {
237 + $(this).css({
238 + 'animation-delay': (index * 0.05) + 's',
239 + 'animation-fill-mode': 'forwards'
240 + }).addClass('fade-in-up');
241 + });
242 +
243 + // Header icon rotation on hover
244 + $('.king-addons-settings-header-icon').on('mouseenter', function() {
245 + $(this).css('transform', 'rotate(10deg) scale(1.05)');
246 + }).on('mouseleave', function() {
247 + $(this).css('transform', 'rotate(0deg) scale(1)');
248 + });
249 + }
250 +
251 + function showSaveAnimation() {
252 + var $saveButton = $('.king-addons-save-button');
253 +
254 + // Only show animation if button exists and is not already disabled
255 + if ($saveButton.length && !$saveButton.prop('disabled')) {
256 + // Show saving state immediately
257 + $saveButton.text('Saving...')
258 + .prop('disabled', true)
259 + .css({
260 + 'opacity': '0.8',
261 + 'transform': 'scale(0.98)'
262 + });
263 + }
264 +
265 + // No need for timeouts since page will reload on successful save
266 + }
267 + });
268 +})(jQuery);