docula 0.50.0 → 1.0.0

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.
@@ -0,0 +1,300 @@
1
+ document.addEventListener('DOMContentLoaded', function() {
2
+ // Toggle operations
3
+ document.querySelectorAll('[data-toggle="operation"]').forEach(function(header) {
4
+ header.addEventListener('click', function() {
5
+ this.closest('.api-operation').classList.toggle('api-operation--collapsed');
6
+ });
7
+ });
8
+
9
+ // Toggle sidebar groups
10
+ document.querySelectorAll('.api-sidebar__group-toggle').forEach(function(btn) {
11
+ btn.addEventListener('click', function() {
12
+ this.closest('.api-sidebar__group').classList.toggle('api-sidebar__group--collapsed');
13
+ });
14
+ });
15
+
16
+ // Code example tabs
17
+ document.querySelectorAll('.api-code-tabs').forEach(function(tabs) {
18
+ var container = tabs.closest('.api-code-examples');
19
+ tabs.querySelectorAll('.api-code-tab').forEach(function(tab) {
20
+ tab.addEventListener('click', function() {
21
+ var target = this.getAttribute('data-tab');
22
+ container.querySelectorAll('.api-code-tab').forEach(function(t) { t.classList.remove('api-code-tab--active'); });
23
+ container.querySelectorAll('.api-code-panel').forEach(function(p) { p.classList.remove('api-code-panel--active'); });
24
+ this.classList.add('api-code-tab--active');
25
+ var panel = container.querySelector('[data-panel="' + target + '"]');
26
+ if (panel) panel.classList.add('api-code-panel--active');
27
+ });
28
+ });
29
+ });
30
+
31
+ // Copy to clipboard
32
+ document.querySelectorAll('[data-copy]').forEach(function(btn) {
33
+ btn.addEventListener('click', function() {
34
+ var code = this.closest('.api-code-panel').querySelector('code');
35
+ if (code) {
36
+ navigator.clipboard.writeText(code.textContent).then(function() {
37
+ btn.textContent = 'Copied!';
38
+ setTimeout(function() { btn.textContent = 'Copy'; }, 2000);
39
+ });
40
+ }
41
+ });
42
+ });
43
+
44
+ // Search
45
+ var searchInput = document.getElementById('api-search');
46
+ if (searchInput) {
47
+ searchInput.addEventListener('input', function() {
48
+ var query = this.value.toLowerCase();
49
+ document.querySelectorAll('.api-sidebar__item').forEach(function(item) {
50
+ var path = (item.getAttribute('data-path') || '').toLowerCase();
51
+ var method = (item.getAttribute('data-method') || '').toLowerCase();
52
+ var text = item.textContent.toLowerCase();
53
+ var match = !query || path.indexOf(query) !== -1 || method.indexOf(query) !== -1 || text.indexOf(query) !== -1;
54
+ item.style.display = match ? '' : 'none';
55
+ });
56
+ document.querySelectorAll('.api-operation').forEach(function(op) {
57
+ var path = (op.querySelector('.api-operation__path') || {}).textContent || '';
58
+ var summary = (op.querySelector('.api-operation__summary') || {}).textContent || '';
59
+ var method = (op.querySelector('.method-badge') || {}).textContent || '';
60
+ var match = !query || path.toLowerCase().indexOf(query) !== -1 || summary.toLowerCase().indexOf(query) !== -1 || method.toLowerCase().indexOf(query) !== -1;
61
+ op.style.display = match ? '' : 'none';
62
+ });
63
+ document.querySelectorAll('.api-group').forEach(function(group) {
64
+ var visible = group.querySelectorAll('.api-operation:not([style*="display: none"])');
65
+ group.style.display = visible.length > 0 || !query ? '' : 'none';
66
+ });
67
+ });
68
+ }
69
+
70
+ // Scroll spy
71
+ var sidebarLinks = document.querySelectorAll('.api-sidebar__item');
72
+ if (sidebarLinks.length > 0) {
73
+ var observer = new IntersectionObserver(function(entries) {
74
+ entries.forEach(function(entry) {
75
+ if (entry.isIntersecting) {
76
+ sidebarLinks.forEach(function(l) { l.classList.remove('api-sidebar__item--active'); });
77
+ var link = document.querySelector('.api-sidebar__item[href="#' + entry.target.id + '"]');
78
+ if (link) link.classList.add('api-sidebar__item--active');
79
+ }
80
+ });
81
+ }, { rootMargin: '-80px 0px -70% 0px' });
82
+
83
+ document.querySelectorAll('.api-operation').forEach(function(op) {
84
+ observer.observe(op);
85
+ });
86
+ }
87
+
88
+ // Mobile sidebar toggle
89
+ var sidebarToggle = document.getElementById('api-sidebar-toggle');
90
+ var sidebar = document.getElementById('api-sidebar');
91
+ if (sidebarToggle && sidebar) {
92
+ sidebarToggle.addEventListener('click', function() {
93
+ sidebar.classList.toggle('api-sidebar--mobile-open');
94
+ });
95
+ sidebar.querySelectorAll('.api-sidebar__item').forEach(function(link) {
96
+ link.addEventListener('click', function() {
97
+ sidebar.classList.remove('api-sidebar--mobile-open');
98
+ });
99
+ });
100
+ }
101
+
102
+ // Collapse all sidebar groups by default
103
+ document.querySelectorAll('.api-sidebar__group').forEach(function(group) {
104
+ group.classList.add('api-sidebar__group--collapsed');
105
+ });
106
+
107
+ // Auth type selector: show/hide value input
108
+ var authTypeSelect = document.getElementById('api-auth-type');
109
+ var authValueInput = document.getElementById('api-auth-value');
110
+ if (authTypeSelect && authValueInput) {
111
+ authTypeSelect.addEventListener('change', function() {
112
+ if (this.value === 'none') {
113
+ authValueInput.classList.add('api-auth__value--hidden');
114
+ authValueInput.value = '';
115
+ } else {
116
+ authValueInput.classList.remove('api-auth__value--hidden');
117
+ authValueInput.placeholder = this.value === 'apikey' ? 'Enter API key...' : 'Enter token...';
118
+ }
119
+ });
120
+ }
121
+
122
+ // Helper: expand an operation and its corresponding sidebar group
123
+ function expandOperationAndGroup(operationEl) {
124
+ if (!operationEl) return;
125
+ operationEl.classList.remove('api-operation--collapsed');
126
+ var contentGroup = operationEl.closest('.api-group');
127
+ if (contentGroup) {
128
+ var groupId = contentGroup.id.replace(/^group-/, '');
129
+ var sidebarGroup = document.querySelector('.api-sidebar__group[data-group="' + groupId + '"]');
130
+ if (sidebarGroup) sidebarGroup.classList.remove('api-sidebar__group--collapsed');
131
+ }
132
+ }
133
+
134
+ // Expand operation via hash, or expand the first operation by default
135
+ if (window.location.hash) {
136
+ var target = document.querySelector(window.location.hash);
137
+ if (target && target.classList.contains('api-operation')) {
138
+ expandOperationAndGroup(target);
139
+ }
140
+ } else {
141
+ expandOperationAndGroup(document.querySelector('.api-operation'));
142
+ }
143
+
144
+ window.addEventListener('hashchange', function() {
145
+ if (window.location.hash) {
146
+ var target = document.querySelector(window.location.hash);
147
+ if (target && target.classList.contains('api-operation')) {
148
+ expandOperationAndGroup(target);
149
+ }
150
+ }
151
+ });
152
+
153
+ // Try It - Response tab switching
154
+ document.querySelectorAll('.api-try-it__response-tabs').forEach(function(tabs) {
155
+ var container = tabs.closest('.api-try-it__response');
156
+ tabs.querySelectorAll('.api-try-it__rtab').forEach(function(tab) {
157
+ tab.addEventListener('click', function() {
158
+ var target = this.getAttribute('data-try-rtab');
159
+ container.querySelectorAll('.api-try-it__rtab').forEach(function(t) { t.classList.remove('api-try-it__rtab--active'); });
160
+ container.querySelectorAll('.api-try-it__rpanel').forEach(function(p) { p.classList.remove('api-try-it__rpanel--active'); });
161
+ this.classList.add('api-try-it__rtab--active');
162
+ var panel = container.querySelector('[data-try-rpanel="' + target + '"]');
163
+ if (panel) panel.classList.add('api-try-it__rpanel--active');
164
+ });
165
+ });
166
+ });
167
+
168
+ // Try It - Helpers
169
+ function getStatusClass(status) {
170
+ if (status >= 200 && status < 300) return '2xx';
171
+ if (status >= 300 && status < 400) return '3xx';
172
+ if (status >= 400 && status < 500) return '4xx';
173
+ if (status >= 500) return '5xx';
174
+ return 'error';
175
+ }
176
+
177
+ function formatBody(text, contentType) {
178
+ if (contentType && contentType.indexOf('json') !== -1) {
179
+ try { return JSON.stringify(JSON.parse(text), null, 2); } catch(e) { /* ignore */ }
180
+ }
181
+ return text;
182
+ }
183
+
184
+ function resetResponseTabs(responseArea) {
185
+ var tabs = responseArea.querySelectorAll('.api-try-it__rtab');
186
+ var panels = responseArea.querySelectorAll('.api-try-it__rpanel');
187
+ tabs.forEach(function(t) { t.classList.remove('api-try-it__rtab--active'); });
188
+ panels.forEach(function(p) { p.classList.remove('api-try-it__rpanel--active'); });
189
+ tabs[0].classList.add('api-try-it__rtab--active');
190
+ panels[0].classList.add('api-try-it__rpanel--active');
191
+ }
192
+
193
+ // Try It - Send request
194
+ document.querySelectorAll('[data-try-send]').forEach(function(btn) {
195
+ btn.addEventListener('click', function() {
196
+ var tryIt = this.closest('.api-try-it');
197
+ var method = tryIt.getAttribute('data-method');
198
+ var pathTemplate = tryIt.getAttribute('data-path');
199
+ var serverSelect = tryIt.querySelector('[data-try-server]');
200
+ var baseUrl = serverSelect ? serverSelect.value : '';
201
+
202
+ // Substitute path params
203
+ var path = pathTemplate;
204
+ tryIt.querySelectorAll('[data-param-in="path"]').forEach(function(row) {
205
+ var name = row.getAttribute('data-param-name');
206
+ var value = row.querySelector('[data-try-param]').value;
207
+ if (value) {
208
+ path = path.replace('{' + name + '}', encodeURIComponent(value));
209
+ }
210
+ });
211
+
212
+ // Build query string
213
+ var queryParts = [];
214
+ tryIt.querySelectorAll('[data-param-in="query"]').forEach(function(row) {
215
+ var name = row.getAttribute('data-param-name');
216
+ var value = row.querySelector('[data-try-param]').value;
217
+ if (value) queryParts.push(encodeURIComponent(name) + '=' + encodeURIComponent(value));
218
+ });
219
+ var queryString = queryParts.length ? '?' + queryParts.join('&') : '';
220
+
221
+ // Build headers
222
+ var headers = {};
223
+ tryIt.querySelectorAll('[data-param-in="header"]').forEach(function(row) {
224
+ var name = row.getAttribute('data-param-name');
225
+ var value = row.querySelector('[data-try-param]').value;
226
+ if (value) headers[name] = value;
227
+ });
228
+
229
+ // Inject global auth header
230
+ var authType = document.getElementById('api-auth-type');
231
+ var authValue = document.getElementById('api-auth-value');
232
+ if (authType && authValue && authValue.value.trim()) {
233
+ var authVal = authValue.value.trim();
234
+ if (authType.value === 'apikey') {
235
+ headers['x-api-key'] = authVal;
236
+ } else if (authType.value === 'bearer') {
237
+ headers['Authorization'] = 'Bearer ' + authVal;
238
+ }
239
+ }
240
+
241
+ // Body
242
+ var bodyTextarea = tryIt.querySelector('[data-try-body]');
243
+ var body = bodyTextarea ? bodyTextarea.value.trim() : '';
244
+ if (body && !headers['Content-Type']) {
245
+ headers['Content-Type'] = tryIt.getAttribute('data-content-type') || 'application/json';
246
+ }
247
+
248
+ var url = baseUrl + path + queryString;
249
+ var fetchOptions = { method: method, headers: headers };
250
+ if (body && method !== 'GET' && method !== 'HEAD') {
251
+ fetchOptions.body = body;
252
+ }
253
+
254
+ // Loading state
255
+ btn.disabled = true;
256
+ btn.textContent = 'Sending...';
257
+ var startTime = performance.now();
258
+
259
+ var responseArea = tryIt.querySelector('[data-try-response]');
260
+ var statusEl = tryIt.querySelector('[data-try-status]');
261
+ var timeEl = tryIt.querySelector('[data-try-time]');
262
+ var bodyEl = tryIt.querySelector('[data-try-response-body]');
263
+ var headersEl = tryIt.querySelector('[data-try-response-headers]');
264
+
265
+ fetch(url, fetchOptions).then(function(response) {
266
+ var elapsed = Math.round(performance.now() - startTime);
267
+ var statusClass = getStatusClass(response.status);
268
+
269
+ statusEl.textContent = response.status + ' ' + response.statusText;
270
+ statusEl.className = 'api-try-it__response-status api-try-it__response-status--' + statusClass;
271
+ timeEl.textContent = elapsed + 'ms';
272
+
273
+ // Collect response headers
274
+ var headerLines = [];
275
+ response.headers.forEach(function(value, key) {
276
+ headerLines.push(key + ': ' + value);
277
+ });
278
+ headersEl.textContent = headerLines.join('\n') || 'No headers';
279
+
280
+ var ct = response.headers.get('content-type') || '';
281
+ return response.text().then(function(text) {
282
+ bodyEl.textContent = formatBody(text, ct);
283
+ responseArea.classList.remove('api-try-it__response--hidden');
284
+ resetResponseTabs(responseArea);
285
+ });
286
+ }).catch(function(err) {
287
+ statusEl.textContent = 'Error';
288
+ statusEl.className = 'api-try-it__response-status api-try-it__response-status--error';
289
+ timeEl.textContent = '';
290
+ headersEl.textContent = '';
291
+ bodyEl.textContent = 'Request failed: ' + err.message + '\n\nThis may be caused by CORS restrictions. The API server must include appropriate CORS headers to allow browser requests.';
292
+ responseArea.classList.remove('api-try-it__response--hidden');
293
+ resetResponseTabs(responseArea);
294
+ }).finally(function() {
295
+ btn.disabled = false;
296
+ btn.textContent = 'Send Request';
297
+ });
298
+ });
299
+ });
300
+ });