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.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 +159 -32 51.1.39 → 51.1.83 View file →
@@ -28,45 +28,139 @@
28 28 setTimeout(() => $(this).removeClass('checked-animation'), 300);
29 29 }
30 30 });
31 31
32 - // Initialize existing functionality
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 -----------------------------------------------
33 57 var $refreshButton = $('#king-addons-ai-refresh-models-button');
34 58 var $spinner = $('#king-addons-ai-refresh-models-spinner');
35 59 var $statusSpan = $('#king-addons-ai-refresh-models-status');
36 - var $modelSelect = $('select[name="king_addons_ai_options[openai_model]"]');
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');
37 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 -------------------------------------------
38 131 $refreshButton.on('click', function() {
39 132 if ($refreshButton.prop('disabled')) {
40 133 return;
41 134 }
42 135
43 - // Disable button and show spinner
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 +
44 142 $refreshButton.prop('disabled', true);
45 - $spinner.css({ visibility: 'visible', display: 'inline-block' }).addClass('is-active');
143 + showSpinner($spinner, true);
46 144 $statusSpan.text(KingAddonsAiSettings.refreshing_text).css('color', '');
47 - $modelSelect.prop('disabled', true);
145 + $selects.prop('disabled', true);
48 146
49 - // AJAX request to refresh models
50 147 $.ajax({
51 148 url: KingAddonsAiSettings.ajax_url,
52 149 type: 'POST',
53 150 data: {
54 151 action: 'king_addons_ai_refresh_models',
55 - nonce: KingAddonsAiSettings.nonce
152 + nonce: KingAddonsAiSettings.nonce,
153 + provider: provider,
154 + api_key: apiKeyField().val() || ''
56 155 },
57 156 dataType: 'json'
58 157 }).done(function(response) {
59 - if (response.success && response.data.models) {
60 - var currentValue = $modelSelect.val();
61 - $modelSelect.empty();
62 -
63 - $.each(response.data.models, function(modelId, modelLabel) {
64 - var $option = $('<option></option>').val(modelId).text(modelLabel);
65 - if (modelId === currentValue) {
66 - $option.prop('selected', true);
67 - }
68 - $modelSelect.append($option);
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]);
69 163 });
70 164
71 165 $statusSpan.text(KingAddonsAiSettings.refreshed_text).css('color', 'green');
72 166 setTimeout(function() {
@@ -72,30 +166,63 @@
72 166 setTimeout(function() {
73 167 $statusSpan.text('');
74 168 }, 3000);
75 169 } else {
76 - var message = response.data && response.data.message ? response.data.message : KingAddonsAiSettings.error_text;
170 + var message = (response.data && response.data.message) ? response.data.message : KingAddonsAiSettings.error_text;
77 171 $statusSpan.text(message).css('color', 'red');
78 172 }
79 173 }).fail(function(jqXHR) {
80 - var message = KingAddonsAiSettings.error_text;
81 - try {
82 - var errorResponse = JSON.parse(jqXHR.responseText);
83 - if (errorResponse.data && errorResponse.data.message) {
84 - message = errorResponse.data.message;
85 - }
86 - } catch (e) {
87 - // ignore JSON parse errors
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');
88 208 }
89 - $statusSpan.text(message).css('color', 'red');
209 + }).fail(function(jqXHR) {
210 + $testStatus.text(errorMessage(jqXHR, KingAddonsAiSettings.test_failed_text)).css('color', 'red');
90 211 }).always(function() {
91 - // Re-enable button and hide spinner
92 - $spinner.css({ visibility: 'hidden', display: 'none' }).removeClass('is-active');
93 - $refreshButton.prop('disabled', false);
94 - $modelSelect.prop('disabled', false);
212 + showSpinner($testSpinner, false);
213 + $testButton.prop('disabled', false);
95 214 });
96 215 });
97 -
216 +
217 + $provider.on('change', function() {
218 + applyProviderVisibility();
219 + $statusSpan.text('');
220 + $testStatus.text('');
221 + });
222 +
223 + applyProviderVisibility();
224 +
98 225 // Modern animation functions
99 226 function initModernAnimations() {
100 227 // Stagger animation for form sections
101 228 $('.king-addons-ai-settings h2').each(function(index) {