drf-to-mkdoc 0.2.2__py3-none-any.whl → 0.2.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of drf-to-mkdoc might be problematic. Click here for more details.

Files changed (47) hide show
  1. drf_to_mkdoc/conf/defaults.py +1 -0
  2. drf_to_mkdoc/static/drf-to-mkdoc/javascripts/field-sections-loader.js +29 -0
  3. drf_to_mkdoc/static/drf-to-mkdoc/javascripts/query-parameters-loader.js +16 -0
  4. drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out/field-extractor.js +200 -0
  5. drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out/form-manager.js +465 -0
  6. drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out/main.js +50 -0
  7. drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out/modal.js +359 -0
  8. drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out/query-parameters-extractor.js +94 -0
  9. drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out/request-executor.js +327 -0
  10. drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out/response-modal.js +173 -0
  11. drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out/suggestions.js +123 -0
  12. drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out/tabs.js +77 -0
  13. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/badges.css +13 -5
  14. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/theme-toggle.css +297 -25
  15. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/try-out/fab.css +204 -0
  16. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/try-out/response.css +323 -0
  17. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/try-out/variables.css +139 -0
  18. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/field-sections.css +136 -0
  19. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/try-out/buttons.css +71 -0
  20. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/try-out/fab.css +47 -0
  21. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/try-out/form.css +663 -0
  22. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/try-out/key-value.css +161 -0
  23. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/try-out/main.css +57 -0
  24. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/try-out/modal.css +334 -0
  25. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/try-out/response.css +618 -0
  26. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/try-out/tabs.css +114 -0
  27. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/try-out/variables.css +94 -0
  28. drf_to_mkdoc/templates/endpoints/detail/base.html +3 -1
  29. drf_to_mkdoc/templates/endpoints/detail/query_parameters.html +1 -8
  30. drf_to_mkdoc/templates/endpoints/detail/request_body.html +2 -0
  31. drf_to_mkdoc/templates/endpoints/detail/responses.html +4 -4
  32. drf_to_mkdoc/templates/try-out/fab.html +68 -0
  33. drf_to_mkdoc/templates/try-out/form.html +260 -0
  34. drf_to_mkdoc/templates/try-out/main.html +4 -0
  35. drf_to_mkdoc/templates/try-out/modal.html +82 -0
  36. drf_to_mkdoc/templates/try-out/response-modal.html +149 -0
  37. drf_to_mkdoc/templatetags/custom_filters.py +33 -1
  38. drf_to_mkdoc/utils/commons/schema_utils.py +5 -14
  39. drf_to_mkdoc/utils/endpoint_detail_generator.py +141 -21
  40. drf_to_mkdoc/utils/extractors/query_parameter_extractors.py +0 -15
  41. {drf_to_mkdoc-0.2.2.dist-info → drf_to_mkdoc-0.2.4.dist-info}/METADATA +68 -9
  42. {drf_to_mkdoc-0.2.2.dist-info → drf_to_mkdoc-0.2.4.dist-info}/RECORD +45 -18
  43. drf_to_mkdoc/static/drf-to-mkdoc/javascripts/try-out-sidebar.js +0 -879
  44. drf_to_mkdoc/static/drf-to-mkdoc/stylesheets/endpoints/try-out-sidebar.css +0 -728
  45. {drf_to_mkdoc-0.2.2.dist-info → drf_to_mkdoc-0.2.4.dist-info}/WHEEL +0 -0
  46. {drf_to_mkdoc-0.2.2.dist-info → drf_to_mkdoc-0.2.4.dist-info}/licenses/LICENSE +0 -0
  47. {drf_to_mkdoc-0.2.2.dist-info → drf_to_mkdoc-0.2.4.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,327 @@
1
+ // Request execution functionality
2
+ const RequestExecutor = {
3
+ // Show validation error in the appropriate section
4
+ showValidationError: function(message, section) {
5
+ let errorContainer;
6
+
7
+ if (section === 'json') {
8
+ // Show error in JSON validation status
9
+ errorContainer = document.querySelector('.validation-status');
10
+ if (errorContainer) {
11
+ errorContainer.textContent = '✗ ' + message;
12
+ errorContainer.className = 'validation-status invalid';
13
+ }
14
+ } else if (section === 'parameters') {
15
+ // Show error in parameters section
16
+ const parametersTab = document.querySelector('[data-tab="parameters"]');
17
+ if (parametersTab) {
18
+ window.TabManager?.switchTab(parametersTab);
19
+ }
20
+
21
+ // Create or update error message
22
+ errorContainer = document.querySelector('#parametersError');
23
+ if (!errorContainer) {
24
+ errorContainer = document.createElement('div');
25
+ errorContainer.id = 'parametersError';
26
+ errorContainer.className = 'error-message';
27
+ const parametersSection = document.querySelector('#parametersTab .form-section');
28
+ if (parametersSection) {
29
+ parametersSection.insertBefore(errorContainer, parametersSection.firstChild);
30
+ }
31
+ }
32
+ errorContainer.textContent = message;
33
+ errorContainer.style.display = 'block';
34
+ }
35
+ },
36
+
37
+ // Clear validation errors
38
+ clearValidationErrors: function() {
39
+ // Clear JSON validation status
40
+ const jsonStatus = document.querySelector('.validation-status');
41
+ if (jsonStatus) {
42
+ jsonStatus.textContent = '';
43
+ jsonStatus.className = 'validation-status';
44
+ }
45
+
46
+ // Clear parameters error
47
+ const paramsError = document.querySelector('#parametersError');
48
+ if (paramsError) {
49
+ paramsError.style.display = 'none';
50
+ }
51
+
52
+ // Clear all input validation states
53
+ document.querySelectorAll('.error').forEach(el => {
54
+ el.classList.remove('error');
55
+ });
56
+ document.querySelectorAll('.validation-message').forEach(msg => {
57
+ msg.style.display = 'none';
58
+ });
59
+ },
60
+
61
+ // Form validation
62
+ validateInput: function(input) {
63
+ const isValid = input.value.trim() !== '';
64
+ const validationMessage = input.parentElement.querySelector('.validation-message');
65
+
66
+ if (!isValid) {
67
+ input.classList.add('error');
68
+ if (validationMessage) {
69
+ validationMessage.textContent = 'This field is required';
70
+ validationMessage.style.display = 'block';
71
+ }
72
+ } else {
73
+ input.classList.remove('error');
74
+ if (validationMessage) {
75
+ validationMessage.textContent = '';
76
+ validationMessage.style.display = 'none';
77
+ }
78
+ }
79
+
80
+ return isValid;
81
+ },
82
+
83
+ // Build complete request data
84
+ buildRequestData: function() {
85
+ const baseUrl = document.getElementById('baseUrl')?.value || '';
86
+ const pathDisplay = document.querySelector('.path-display')?.textContent || '';
87
+
88
+ // Build full URL
89
+ let fullUrl = baseUrl + pathDisplay;
90
+
91
+ // Replace path parameters
92
+ const pathParams = {};
93
+ document.querySelectorAll('#pathParams input').forEach(input => {
94
+ const param = input.dataset.param;
95
+ if (param && input.value) {
96
+ pathParams[param] = input.value;
97
+ fullUrl = fullUrl.replace(`{${param}}`, encodeURIComponent(input.value));
98
+ }
99
+ });
100
+
101
+ // Collect query parameters
102
+ const queryParams = {};
103
+ document.querySelectorAll('#queryParams .parameter-item').forEach(item => {
104
+ const nameInput = item.querySelector('.name-input');
105
+ const valueInput = item.querySelector('.value-input');
106
+ if (nameInput?.value && valueInput?.value) {
107
+ queryParams[nameInput.value] = valueInput.value;
108
+ }
109
+ });
110
+
111
+ // Add query parameters to URL
112
+ if (Object.keys(queryParams).length > 0) {
113
+ const queryString = new URLSearchParams(queryParams).toString();
114
+ fullUrl += (fullUrl.includes('?') ? '&' : '?') + queryString;
115
+ }
116
+
117
+ // Collect headers
118
+ const headers = {};
119
+ document.querySelectorAll('#requestHeaders .header-item').forEach(item => {
120
+ const nameInput = item.querySelector('.name-input');
121
+ const valueInput = item.querySelector('.value-input');
122
+ if (nameInput?.value && valueInput?.value) {
123
+ headers[nameInput.value] = valueInput.value;
124
+ }
125
+ });
126
+
127
+ // Add default headers if not already set
128
+ if (!headers['Accept']) {
129
+ headers['Accept'] = '*/*';
130
+ }
131
+ if (!headers['User-Agent']) {
132
+ headers['User-Agent'] = navigator.userAgent;
133
+ }
134
+ if (!headers['Accept-Language']) {
135
+ headers['Accept-Language'] = navigator.language || 'en-US,en;q=0.9';
136
+ }
137
+ if (!headers['Accept-Encoding']) {
138
+ headers['Accept-Encoding'] = 'gzip, deflate, br';
139
+ }
140
+ if (!headers['Connection']) {
141
+ headers['Connection'] = 'keep-alive';
142
+ }
143
+ if (!headers['Cache-Control']) {
144
+ headers['Cache-Control'] = 'no-cache';
145
+ }
146
+
147
+ // Add cookies if available
148
+ if (document.cookie) {
149
+ headers['Cookie'] = document.cookie;
150
+ }
151
+
152
+ // Get request body
153
+ const bodyEditor = document.getElementById('requestBody');
154
+ let body = null;
155
+ if (bodyEditor?.value.trim()) {
156
+ try {
157
+ body = JSON.parse(bodyEditor.value);
158
+ } catch (e) {
159
+ body = bodyEditor.value;
160
+ }
161
+ }
162
+
163
+ // Get method
164
+ const methodBadge = document.querySelector('.method-badge');
165
+ const method = methodBadge?.dataset.method || 'GET';
166
+
167
+ return {
168
+ url: fullUrl,
169
+ method,
170
+ headers,
171
+ body,
172
+ pathParams,
173
+ queryParams
174
+ };
175
+ },
176
+ async executeRequest() {
177
+ const executeBtn = document.querySelector('[data-action="send"], .primary-button, .primary-btn, #executeBtn');
178
+ if (!executeBtn) {
179
+ console.warn('Execute button not found');
180
+ return;
181
+ }
182
+
183
+ // Validate required fields
184
+ const requiredInputs = document.querySelectorAll('#pathParams input[required]');
185
+ let emptyFields = [];
186
+
187
+ requiredInputs.forEach(input => {
188
+ if (!input.value.trim()) {
189
+ const paramName = input.dataset.param || 'parameter';
190
+ emptyFields.push(paramName);
191
+ this.validateInput(input);
192
+ }
193
+ });
194
+
195
+ if (emptyFields.length > 0) {
196
+ this.showValidationError(`Please fill in required fields: ${emptyFields.join(', ')}`, 'parameters');
197
+ return;
198
+ }
199
+
200
+ // Show loading state
201
+ this.setLoadingState(executeBtn, true);
202
+
203
+ const startTime = Date.now();
204
+
205
+ try {
206
+ const requestData = this.buildRequestData();
207
+
208
+ const requestOptions = {
209
+ method: requestData.method.toUpperCase(),
210
+ headers: requestData.headers
211
+ };
212
+
213
+ // Add body for non-GET requests
214
+ if (requestData.body && !['GET', 'HEAD'].includes(requestData.method.toUpperCase())) {
215
+ if (typeof requestData.body === 'string') {
216
+ requestOptions.body = requestData.body;
217
+ } else {
218
+ requestOptions.body = JSON.stringify(requestData.body);
219
+ if (!requestData.headers['Content-Type']) {
220
+ requestOptions.headers['Content-Type'] = 'application/json';
221
+ }
222
+ }
223
+ }
224
+
225
+ // Show response section
226
+ const responseSection = document.querySelector('.response-section');
227
+ if (responseSection) {
228
+ responseSection.hidden = false;
229
+ }
230
+
231
+ const response = await fetch(requestData.url, requestOptions);
232
+ const responseTime = Date.now() - startTime;
233
+ const responseText = await response.text();
234
+
235
+ // Convert response headers to object
236
+ const responseHeaders = {};
237
+ response.headers.forEach((value, key) => {
238
+ // Handle multiple headers with the same name (like Set-Cookie)
239
+ if (responseHeaders[key]) {
240
+ if (Array.isArray(responseHeaders[key])) {
241
+ responseHeaders[key].push(value);
242
+ } else {
243
+ responseHeaders[key] = [responseHeaders[key], value];
244
+ }
245
+ } else {
246
+ responseHeaders[key] = value;
247
+ }
248
+ });
249
+
250
+ ModalManager.showResponseModal(response.status, responseText, responseTime, responseHeaders, requestData.headers);
251
+
252
+ } catch (error) {
253
+ console.error('Request failed:', error);
254
+ const requestData = this.buildRequestData();
255
+ const errorTime = Date.now() - startTime;
256
+ ModalManager.showResponseModal('Error', error.message || 'Unknown error occurred', errorTime, null, requestData.headers);
257
+ } finally {
258
+ this.setLoadingState(executeBtn, false);
259
+ }
260
+ },
261
+
262
+ setLoadingState(button, loading) {
263
+ button.disabled = loading;
264
+
265
+ if (loading) {
266
+ button.classList.add('loading');
267
+ const spinner = button.querySelector('.loading-spinner');
268
+ if (spinner) {
269
+ spinner.style.display = 'inline-block';
270
+ }
271
+ } else {
272
+ button.classList.remove('loading');
273
+ const spinner = button.querySelector('.loading-spinner');
274
+ if (spinner) {
275
+ spinner.style.display = 'none';
276
+ }
277
+ }
278
+ },
279
+
280
+ // JSON formatting and validation
281
+ formatJson: function() {
282
+ const editor = document.getElementById('requestBody');
283
+ if (!editor) return;
284
+
285
+ try {
286
+ const formatted = JSON.stringify(JSON.parse(editor.value), null, 2);
287
+ editor.value = formatted;
288
+ this.validateJson();
289
+ } catch (e) {
290
+ this.showValidationError('Invalid JSON format', 'json');
291
+ }
292
+ },
293
+
294
+ validateJson: function() {
295
+ const editor = document.getElementById('requestBody');
296
+ const status = document.querySelector('.validation-status');
297
+
298
+ if (!editor || !status) return true;
299
+
300
+ if (!editor.value.trim()) {
301
+ status.textContent = '';
302
+ status.className = 'validation-status';
303
+ return true;
304
+ }
305
+
306
+ try {
307
+ JSON.parse(editor.value);
308
+ status.textContent = '✓ Valid JSON';
309
+ status.className = 'validation-status valid';
310
+ return true;
311
+ } catch (e) {
312
+ status.textContent = '✗ ' + e.message;
313
+ status.className = 'validation-status invalid';
314
+ return false;
315
+ }
316
+ },
317
+
318
+ // This method is now handled by the main showValidationError method above
319
+ };
320
+
321
+ // Global functions for onclick handlers and backward compatibility
322
+ window.executeRequest = () => RequestExecutor.executeRequest();
323
+ window.formatJson = () => RequestExecutor.formatJson();
324
+ window.validateJson = () => RequestExecutor.validateJson();
325
+
326
+ // Export for global access
327
+ window.RequestExecutor = RequestExecutor;
@@ -0,0 +1,173 @@
1
+ // Response modal functionality
2
+ const ResponseModalManager = {
3
+ init: function() {
4
+ // Tab switching
5
+ const tabs = document.querySelectorAll('.response-tabs .tab');
6
+ tabs.forEach(tab => {
7
+ tab.addEventListener('click', () => this.switchTab(tab));
8
+ });
9
+
10
+ // Initialize syntax highlighting
11
+ this.initializePrism();
12
+ },
13
+
14
+ switchTab: function(tab) {
15
+ if (!tab) return;
16
+
17
+ // Remove active class from all tabs and content
18
+ document.querySelectorAll('.response-tabs .tab').forEach(t => {
19
+ if (t && t.classList) {
20
+ t.classList.remove('active');
21
+ t.setAttribute('aria-selected', 'false');
22
+ }
23
+ });
24
+ document.querySelectorAll('.tab-content').forEach(c => {
25
+ if (c && c.classList) {
26
+ c.classList.remove('active');
27
+ }
28
+ });
29
+
30
+ // Add active class to clicked tab and its content
31
+ if (tab.classList) {
32
+ tab.classList.add('active');
33
+ tab.setAttribute('aria-selected', 'true');
34
+ }
35
+ const contentId = tab.getAttribute('aria-controls');
36
+ const contentElement = document.getElementById(contentId);
37
+ if (contentElement && contentElement.classList) {
38
+ contentElement.classList.add('active');
39
+ }
40
+ },
41
+
42
+ copyResponse: function() {
43
+ const responseBody = document.getElementById('modalResponseBody');
44
+ if (!responseBody) return;
45
+
46
+ const responseText = responseBody.textContent;
47
+ if (navigator.clipboard) {
48
+ navigator.clipboard.writeText(responseText).then(() => {
49
+ this.showToast('Response copied to clipboard');
50
+ }).catch(() => {
51
+ this.showToast('Failed to copy response');
52
+ });
53
+ } else {
54
+ this.showToast('Clipboard not supported');
55
+ }
56
+ },
57
+
58
+ downloadResponse: function() {
59
+ const responseBody = document.getElementById('modalResponseBody');
60
+ if (!responseBody) return;
61
+
62
+ const responseText = responseBody.textContent;
63
+ const blob = new Blob([responseText], { type: 'application/json' });
64
+ const url = URL.createObjectURL(blob);
65
+ const a = document.createElement('a');
66
+ a.href = url;
67
+ a.download = 'response.json';
68
+ document.body.appendChild(a);
69
+ a.click();
70
+ document.body.removeChild(a);
71
+ URL.revokeObjectURL(url);
72
+ this.showToast('Response downloaded');
73
+ },
74
+
75
+ formatResponse: function() {
76
+ try {
77
+ const responseBody = document.getElementById('modalResponseBody');
78
+ if (!responseBody) return;
79
+
80
+ const content = responseBody.textContent;
81
+ if (!content.trim()) return;
82
+
83
+ try {
84
+ const parsed = JSON.parse(content);
85
+ responseBody.textContent = JSON.stringify(parsed, null, 2);
86
+ } catch (e) {
87
+ // If not JSON, just return as is
88
+ console.log('Response is not valid JSON, skipping formatting');
89
+ }
90
+ } catch (error) {
91
+ console.error('Error formatting response:', error);
92
+ }
93
+ },
94
+
95
+ collapseAll: function() {
96
+ const responseBody = document.getElementById('modalResponseBody');
97
+ if (!responseBody) return;
98
+
99
+ // Simple collapse - replace newlines with spaces for basic collapse
100
+ const content = responseBody.textContent;
101
+ if (content) {
102
+ responseBody.textContent = content.replace(/\n\s*/g, ' ').trim();
103
+ }
104
+ },
105
+
106
+ expandAll: function() {
107
+ const responseBody = document.getElementById('modalResponseBody');
108
+ if (!responseBody) return;
109
+
110
+ const content = responseBody.textContent;
111
+ if (!content.trim()) return;
112
+
113
+ try {
114
+ // Try to format as JSON first
115
+ const parsed = JSON.parse(content);
116
+ responseBody.textContent = JSON.stringify(parsed, null, 2);
117
+ } catch (e) {
118
+ // If not JSON, just restore original formatting
119
+ responseBody.textContent = content;
120
+ }
121
+ },
122
+
123
+ showToast: function(message) {
124
+ // Create toast element
125
+ const toast = document.createElement('div');
126
+ toast.className = 'toast';
127
+ toast.textContent = message;
128
+ toast.style.cssText = `
129
+ position: fixed;
130
+ top: 20px;
131
+ right: 20px;
132
+ background: #333;
133
+ color: white;
134
+ padding: 12px 20px;
135
+ border-radius: 4px;
136
+ z-index: 10000;
137
+ font-size: 14px;
138
+ opacity: 0;
139
+ transition: opacity 0.3s ease;
140
+ `;
141
+
142
+ document.body.appendChild(toast);
143
+
144
+ // Show toast
145
+ setTimeout(() => {
146
+ toast.style.opacity = '1';
147
+ }, 10);
148
+
149
+ // Hide toast after 3 seconds
150
+ setTimeout(() => {
151
+ toast.style.opacity = '0';
152
+ setTimeout(() => {
153
+ if (toast.parentNode) {
154
+ toast.parentNode.removeChild(toast);
155
+ }
156
+ }, 300);
157
+ }, 3000);
158
+ },
159
+
160
+ initializePrism: function() {
161
+ // Basic syntax highlighting placeholder
162
+ // This would integrate with Prism.js if available
163
+ console.log('Syntax highlighting initialized');
164
+ }
165
+ };
166
+
167
+ // Export for global access
168
+ window.ResponseModalManager = ResponseModalManager;
169
+
170
+ // Initialize when DOM is loaded
171
+ document.addEventListener('DOMContentLoaded', function() {
172
+ ResponseModalManager.init();
173
+ });
@@ -0,0 +1,123 @@
1
+ // Query parameter suggestions functionality
2
+ const TryOutSuggestions = {
3
+ init: function() {
4
+ this.suggestions = this.getAvailableSuggestions();
5
+ this.updateDatalist();
6
+
7
+ // Re-initialize when window.queryParametersData changes
8
+ if (window.MutationObserver) {
9
+ this.setupDataObserver();
10
+ }
11
+ },
12
+
13
+ setupDataObserver: function() {
14
+ // Check for changes in queryParametersData every second
15
+ setInterval(() => {
16
+ if (window.queryParametersData && window.queryParametersData._lastUpdate !== this._lastDataUpdate) {
17
+ this._lastDataUpdate = window.queryParametersData._lastUpdate;
18
+ this.suggestions = this.getAvailableSuggestions();
19
+ this.updateDatalist();
20
+ }
21
+ }, 1000);
22
+ },
23
+
24
+ setupAutocomplete: function() {
25
+ // Using native datalist, just make sure all inputs are properly initialized
26
+ this.setupExistingInputs();
27
+
28
+ // Setup for the add button to mark new inputs as initialized
29
+ const addBtn = document.querySelector('.add-btn');
30
+ if (addBtn) {
31
+ addBtn.addEventListener('click', () => {
32
+ // Wait for DOM to update
33
+ setTimeout(() => {
34
+ this.setupExistingInputs();
35
+ }, 10);
36
+ });
37
+ }
38
+ },
39
+
40
+ updateDatalist: function() {
41
+ // Update the datalist with our suggestions
42
+ const datalist = document.getElementById('paramSuggestions');
43
+ if (datalist && this.suggestions.length > 0) {
44
+ // Clear existing options
45
+ datalist.innerHTML = '';
46
+
47
+ // Add new options from our suggestions - without descriptions
48
+ this.suggestions.forEach(suggestion => {
49
+ const option = document.createElement('option');
50
+ option.value = suggestion.name;
51
+ // No description text as per requirement
52
+ datalist.appendChild(option);
53
+ });
54
+ }
55
+ },
56
+
57
+ setupExistingInputs: function() {
58
+ // Find all parameter name inputs
59
+ const paramInputs = document.querySelectorAll('#queryParams .name-input');
60
+ paramInputs.forEach(input => {
61
+ // Skip if already initialized
62
+ if (input.dataset.autocompleteInitialized) return;
63
+
64
+ // Mark as initialized
65
+ input.dataset.autocompleteInitialized = 'true';
66
+
67
+ // We're using the native datalist for autocomplete
68
+ // No need for custom suggestions dropdown
69
+ });
70
+ },
71
+
72
+ getAvailableSuggestions: function() {
73
+ // Get query parameters only from window.queryParametersData
74
+ const suggestions = [];
75
+
76
+ if (window.queryParametersData) {
77
+ const data = window.queryParametersData;
78
+
79
+ // Add filter fields
80
+ if (data.filter_fields && data.filter_fields.length > 0) {
81
+ suggestions.push(...data.filter_fields.map(field => ({
82
+ name: field
83
+ })));
84
+ }
85
+
86
+ // Add search fields - only add the 'search' key, not individual fields
87
+ // The 'search' key will be added via special_keys
88
+
89
+ // Add ordering fields - only add the 'ordering' key, not individual fields
90
+ // The 'ordering' key will be added via special_keys
91
+
92
+ // Add special keys
93
+ if (data.special_keys && data.special_keys.length > 0) {
94
+ suggestions.push(...data.special_keys.map(key => ({
95
+ name: key
96
+ })));
97
+ }
98
+
99
+ // Add pagination fields
100
+ if (data.pagination_fields && data.pagination_fields.length > 0) {
101
+ suggestions.push(...data.pagination_fields.map(field => ({
102
+ name: field
103
+ })));
104
+ }
105
+ }
106
+
107
+ return suggestions;
108
+ },
109
+
110
+ selectSuggestion: function(input, suggestion) {
111
+ // Set input value
112
+ input.value = suggestion;
113
+
114
+ // Focus on value input
115
+ const valueInput = input.nextElementSibling;
116
+ if (valueInput) {
117
+ valueInput.focus();
118
+ }
119
+ },
120
+ };
121
+
122
+ // Export for global access
123
+ window.TryOutSuggestions = TryOutSuggestions;
@@ -0,0 +1,77 @@
1
+ // Tab management functionality
2
+ const TabManager = {
3
+ init: function() {
4
+ document.querySelectorAll('.try-out-form .tab, .smart-tabs .tab, .response-tabs .tab').forEach(tab => {
5
+ tab.addEventListener('click', () => {
6
+ this.switchTab(tab);
7
+ });
8
+
9
+ // Add keyboard support
10
+ tab.addEventListener('keydown', (e) => {
11
+ if (e.key === 'Enter' || e.key === ' ') {
12
+ e.preventDefault();
13
+ this.switchTab(tab);
14
+ }
15
+ });
16
+ });
17
+ },
18
+
19
+ switchTab: function(activeTab) {
20
+ const tabContainer = activeTab.closest('.smart-tabs, .response-tabs, .try-out-form');
21
+ if (!tabContainer) return;
22
+
23
+ // Remove active class from all tabs in this container
24
+ tabContainer.querySelectorAll('.tab').forEach(t => {
25
+ t.classList.remove('active');
26
+ t.setAttribute('aria-selected', 'false');
27
+ });
28
+
29
+ // Remove active class from all tab content
30
+ const contentContainer = tabContainer.parentElement || document;
31
+ contentContainer.querySelectorAll('.tab-content').forEach(c => {
32
+ c.classList.remove('active');
33
+ });
34
+
35
+ // Add active class to clicked tab and its content
36
+ activeTab.classList.add('active');
37
+ activeTab.setAttribute('aria-selected', 'true');
38
+
39
+ // Show corresponding content
40
+ const contentId = activeTab.getAttribute('aria-controls') || activeTab.getAttribute('data-tab');
41
+ let content;
42
+
43
+ if (contentId) {
44
+ content = document.getElementById(contentId) || document.getElementById(contentId + 'Tab');
45
+ if (content) {
46
+ content.classList.add('active');
47
+ }
48
+ }
49
+
50
+ // Debug logging
51
+ console.log('Tab switched to:', contentId, 'Content element:', content);
52
+ }
53
+ };
54
+
55
+ // Initialize tabs when DOM is ready
56
+ document.addEventListener('DOMContentLoaded', function() {
57
+ TabManager.init();
58
+
59
+ // Also initialize when modal is shown (for dynamic content)
60
+ const modal = document.getElementById('tryOutModal');
61
+ if (modal) {
62
+ const observer = new MutationObserver(function(mutations) {
63
+ mutations.forEach(function(mutation) {
64
+ if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
65
+ if (modal.classList.contains('show')) {
66
+ // Re-initialize tabs when modal is shown
67
+ setTimeout(() => TabManager.init(), 100);
68
+ }
69
+ }
70
+ });
71
+ });
72
+ observer.observe(modal, { attributes: true });
73
+ }
74
+ });
75
+
76
+ // Export for global access
77
+ window.TabManager = TabManager;