PluginProbe
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder / 51.1.83
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder v51.1.83
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 51.1.47 51.1.49 All 37 releases
king-addons / includes / admin / js / ai-settings.js

ai-settings.js in King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder 51.1.83, at includes/admin/js/ai-settings.js

268 lines 10.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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);