serverest 3.0.1 → 3.1.1
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.
- package/docs/flags/flag_peru.svg +10 -0
- package/docs/images/serverest_logo.svg +116 -0
- package/package.json +4 -1
- package/src/app.js +1 -1
- package/src/controllers/produtos-controller.js +65 -65
- package/src/swagger/index.js +62 -0
- package/src/swagger/scripts/01-config.js +10 -0
- package/src/swagger/scripts/02-language-storage.js +55 -0
- package/src/swagger/scripts/03-release-toast.js +158 -0
- package/src/swagger/scripts/04-ui-translations.js +92 -0
- package/src/swagger/scripts/05-swagger-spec.js +110 -0
- package/src/swagger/scripts/06-language-switcher.js +68 -0
- package/src/swagger/scripts/07-doc-lifecycle.js +163 -0
- package/src/swagger/styles/language-switcher.css +64 -0
- package/src/swagger/{custom-css.css → styles/release-toast.css} +1 -60
- package/src/swagger/styles/topbar.css +56 -0
- package/src/swagger/swagger-sw.js +2 -2
- package/src/swagger/translations/en.js +23 -0
- package/src/swagger/translations/es.js +23 -0
- package/src/swagger/translations/pt-BR.js +23 -0
- package/docs/flags/flag_spain.svg +0 -2552
- package/docs/images/serverest_logo.png +0 -0
- package/src/swagger/custom-js.js +0 -609
- package/src/swagger/customization.js +0 -25
|
Binary file
|
package/src/swagger/custom-js.js
DELETED
|
@@ -1,609 +0,0 @@
|
|
|
1
|
-
/* global __RELEASE_INFO__, __CURRENT_VERSION__, __FORCE_BANNER__, NodeFilter, MutationObserver, caches */
|
|
2
|
-
(function () {
|
|
3
|
-
const releaseInfo = __RELEASE_INFO__
|
|
4
|
-
const currentVersion = __CURRENT_VERSION__
|
|
5
|
-
const forceBanner = __FORCE_BANNER__
|
|
6
|
-
|
|
7
|
-
function normalizeVersion (version) {
|
|
8
|
-
if (!version) return ''
|
|
9
|
-
return version.toString().trim().replace(/^v/i, '')
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const toastTranslations = {
|
|
13
|
-
'pt-BR': {
|
|
14
|
-
title: 'Nova release disponível',
|
|
15
|
-
link: 'Ver release',
|
|
16
|
-
closeLabel: 'Fechar aviso de release',
|
|
17
|
-
meta: (latest, current) => 'v' + latest + ' (v' + current + ' instalada)'
|
|
18
|
-
},
|
|
19
|
-
en: {
|
|
20
|
-
title: 'New release available',
|
|
21
|
-
link: 'View release',
|
|
22
|
-
closeLabel: 'Close release notice',
|
|
23
|
-
meta: (latest, current) => 'v' + latest + ' (v' + current + ' installed)'
|
|
24
|
-
},
|
|
25
|
-
es: {
|
|
26
|
-
title: 'Nueva versión disponible',
|
|
27
|
-
link: 'Ver versión',
|
|
28
|
-
closeLabel: 'Cerrar aviso de versión',
|
|
29
|
-
meta: (latest, current) => 'v' + latest + ' (v' + current + ' instalada)'
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function getToastTranslation (language) {
|
|
34
|
-
return toastTranslations[language] || toastTranslations['pt-BR']
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function renderReleaseToast (releaseDataOverride) {
|
|
38
|
-
const info = releaseDataOverride !== undefined ? releaseDataOverride : releaseInfo
|
|
39
|
-
const current = normalizeVersion(currentVersion)
|
|
40
|
-
let latestVersion = normalizeVersion(info && (info.version || info.tag))
|
|
41
|
-
if (forceBanner) {
|
|
42
|
-
latestVersion = normalizeVersion('999.999.999')
|
|
43
|
-
}
|
|
44
|
-
if (!latestVersion || (!current && !forceBanner)) return
|
|
45
|
-
if (latestVersion === current && !forceBanner) return
|
|
46
|
-
if (document.querySelector('.release-toast')) return
|
|
47
|
-
const root = document.querySelector('.swagger-ui') || document.body
|
|
48
|
-
|
|
49
|
-
const toast = document.createElement('div')
|
|
50
|
-
toast.className = 'release-toast'
|
|
51
|
-
toast.setAttribute('aria-live', 'polite')
|
|
52
|
-
toast.dataset.latestVersion = latestVersion
|
|
53
|
-
toast.dataset.currentVersion = current
|
|
54
|
-
|
|
55
|
-
const content = document.createElement('div')
|
|
56
|
-
content.className = 'release-toast__content'
|
|
57
|
-
|
|
58
|
-
const title = document.createElement('div')
|
|
59
|
-
title.className = 'release-toast__title'
|
|
60
|
-
const titleIcon = document.createElement('span')
|
|
61
|
-
titleIcon.className = 'release-toast__title-icon'
|
|
62
|
-
titleIcon.textContent = '⚡'
|
|
63
|
-
const translation = getToastTranslation(getPreferredLanguage())
|
|
64
|
-
const titleText = document.createElement('span')
|
|
65
|
-
titleText.textContent = translation.title
|
|
66
|
-
title.appendChild(titleIcon)
|
|
67
|
-
title.appendChild(titleText)
|
|
68
|
-
|
|
69
|
-
const meta = document.createElement('div')
|
|
70
|
-
meta.className = 'release-toast__meta'
|
|
71
|
-
meta.textContent = translation.meta(latestVersion, current)
|
|
72
|
-
|
|
73
|
-
const link = document.createElement('a')
|
|
74
|
-
link.className = 'release-toast__link'
|
|
75
|
-
link.href = (info && info.url) ? info.url : 'https://github.com/ServeRest/ServeRest/releases'
|
|
76
|
-
link.textContent = translation.link
|
|
77
|
-
link.target = '_blank'
|
|
78
|
-
link.rel = 'noopener noreferrer'
|
|
79
|
-
|
|
80
|
-
const close = document.createElement('button')
|
|
81
|
-
close.type = 'button'
|
|
82
|
-
close.className = 'release-toast__close'
|
|
83
|
-
close.setAttribute('aria-label', translation.closeLabel)
|
|
84
|
-
close.textContent = '×'
|
|
85
|
-
close.addEventListener('click', function () {
|
|
86
|
-
toast.remove()
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
content.appendChild(title)
|
|
90
|
-
content.appendChild(meta)
|
|
91
|
-
content.appendChild(link)
|
|
92
|
-
toast.appendChild(content)
|
|
93
|
-
toast.appendChild(close)
|
|
94
|
-
root.appendChild(toast)
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function updateReleaseToastLanguage (language) {
|
|
98
|
-
const toast = document.querySelector('.release-toast')
|
|
99
|
-
if (!toast) return
|
|
100
|
-
const translation = getToastTranslation(language)
|
|
101
|
-
const latestVersion = toast.dataset.latestVersion || ''
|
|
102
|
-
const current = toast.dataset.currentVersion || ''
|
|
103
|
-
const title = toast.querySelector('.release-toast__title span:last-child')
|
|
104
|
-
const meta = toast.querySelector('.release-toast__meta')
|
|
105
|
-
const link = toast.querySelector('.release-toast__link')
|
|
106
|
-
const close = toast.querySelector('.release-toast__close')
|
|
107
|
-
if (title) title.textContent = translation.title
|
|
108
|
-
if (meta) meta.textContent = translation.meta(latestVersion, current)
|
|
109
|
-
if (link) link.textContent = translation.link
|
|
110
|
-
if (close) close.setAttribute('aria-label', translation.closeLabel)
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const uiLabelTranslations = [
|
|
114
|
-
{ 'pt-BR': 'Parâmetros', en: 'Parameters', es: 'Parámetros' },
|
|
115
|
-
{ 'pt-BR': 'Parâmetro', en: 'Parameter', es: 'Parámetro' },
|
|
116
|
-
{ 'pt-BR': 'Respostas', en: 'Responses', es: 'Respuestas' },
|
|
117
|
-
{ 'pt-BR': 'Resposta', en: 'Response', es: 'Respuesta' },
|
|
118
|
-
{ 'pt-BR': 'Corpo da requisição', en: 'Request body', es: 'Cuerpo de la solicitud' },
|
|
119
|
-
{ 'pt-BR': 'Corpo da resposta', en: 'Response body', es: 'Cuerpo de la respuesta' },
|
|
120
|
-
{ 'pt-BR': 'Exemplo', en: 'Example Value', es: 'Ejemplo' },
|
|
121
|
-
{ 'pt-BR': 'Modelo', en: 'Model', es: 'Modelo' },
|
|
122
|
-
{ 'pt-BR': 'Valor', en: 'Value', es: 'Valor' },
|
|
123
|
-
{ 'pt-BR': 'Descrição', en: 'Description', es: 'Descripción' },
|
|
124
|
-
{ 'pt-BR': 'Obrigatório', en: 'Required', es: 'Obligatorio' },
|
|
125
|
-
{ 'pt-BR': 'Testar', en: 'Try it out', es: 'Probar' },
|
|
126
|
-
{ 'pt-BR': 'Executar', en: 'Execute', es: 'Ejecutar' },
|
|
127
|
-
{ 'pt-BR': 'Cancelar', en: 'Cancel', es: 'Cancelar' },
|
|
128
|
-
{ 'pt-BR': 'Limpar', en: 'Clear', es: 'Limpiar' },
|
|
129
|
-
{ 'pt-BR': 'Autorizar', en: 'Authorize', es: 'Autorizar' },
|
|
130
|
-
{ 'pt-BR': 'Servidores', en: 'Servers', es: 'Servidores' },
|
|
131
|
-
{ 'pt-BR': 'Sem parâmetros', en: 'No parameters', es: 'Sin parámetros' },
|
|
132
|
-
{ 'pt-BR': 'Sem respostas', en: 'No responses', es: 'Sin respuestas' },
|
|
133
|
-
{ 'pt-BR': 'Carregando', en: 'LOADING', es: 'Cargando' },
|
|
134
|
-
{ 'pt-BR': 'Baixar', en: 'Download', es: 'Descargar' },
|
|
135
|
-
{ 'pt-BR': 'Autenticações disponíveis', en: 'Available authorizations', es: 'Autenticaciones disponibles' },
|
|
136
|
-
{ 'pt-BR': 'exemplo', en: 'example', es: 'ejemplo' }
|
|
137
|
-
]
|
|
138
|
-
|
|
139
|
-
const buildExactMap = (targetLang) => {
|
|
140
|
-
const map = new Map()
|
|
141
|
-
uiLabelTranslations.forEach(labels => {
|
|
142
|
-
const target = labels[targetLang]
|
|
143
|
-
if (!target) return
|
|
144
|
-
Object.keys(labels).forEach(sourceLang => {
|
|
145
|
-
if (sourceLang === targetLang) return
|
|
146
|
-
const source = labels[sourceLang]
|
|
147
|
-
if (!source || source === target) return
|
|
148
|
-
map.set(source, target)
|
|
149
|
-
})
|
|
150
|
-
})
|
|
151
|
-
return map
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const translations = {
|
|
155
|
-
'pt-BR': {
|
|
156
|
-
exact: buildExactMap('pt-BR'),
|
|
157
|
-
replace: [
|
|
158
|
-
{ regex: /\binteger\b/g, value: 'inteiro' }
|
|
159
|
-
]
|
|
160
|
-
},
|
|
161
|
-
en: {
|
|
162
|
-
exact: buildExactMap('en'),
|
|
163
|
-
replace: []
|
|
164
|
-
},
|
|
165
|
-
es: {
|
|
166
|
-
exact: buildExactMap('es'),
|
|
167
|
-
replace: [
|
|
168
|
-
{ regex: /\binteger\b/g, value: 'entero' }
|
|
169
|
-
]
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const flagUrls = {
|
|
174
|
-
'pt-BR': '/flags/flag_brazil.svg',
|
|
175
|
-
en: '/flags/flag_uk.svg',
|
|
176
|
-
es: '/flags/flag_spain.svg'
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
function preloadFlags () {
|
|
180
|
-
if (typeof document === 'undefined' || !document.head) return
|
|
181
|
-
Object.values(flagUrls).forEach(href => {
|
|
182
|
-
const link = document.createElement('link')
|
|
183
|
-
link.rel = 'preload'
|
|
184
|
-
link.as = 'image'
|
|
185
|
-
link.href = href
|
|
186
|
-
document.head.appendChild(link)
|
|
187
|
-
})
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
const supportedLanguages = [
|
|
191
|
-
{ code: 'pt-BR', label: 'Português (Brasil)' },
|
|
192
|
-
{ code: 'en', label: 'English (UK)' },
|
|
193
|
-
{ code: 'es', label: 'Español' }
|
|
194
|
-
]
|
|
195
|
-
|
|
196
|
-
let originalTextNodes = new WeakMap()
|
|
197
|
-
|
|
198
|
-
function getPreferredLanguage () {
|
|
199
|
-
return window.localStorage.getItem('swaggerLanguage') || 'pt-BR'
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
function setPreferredLanguage (code) {
|
|
203
|
-
window.localStorage.setItem('swaggerLanguage', code)
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
let endpointBlockOpen = false
|
|
207
|
-
|
|
208
|
-
function getLanguageFromQuery () {
|
|
209
|
-
const url = new URL(window.location.href)
|
|
210
|
-
return url.searchParams.get('lang') || ''
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
function getCurrentLanguageCode () {
|
|
214
|
-
const queryLang = getLanguageFromQuery()
|
|
215
|
-
const preferred = getPreferredLanguage()
|
|
216
|
-
return supportedLanguages.some(l => l.code === queryLang)
|
|
217
|
-
? queryLang
|
|
218
|
-
: (supportedLanguages.some(l => l.code === preferred) ? preferred : 'pt-BR')
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
function preloadSwaggerJson () {
|
|
222
|
-
if (typeof document === 'undefined' || !document.head) return
|
|
223
|
-
const code = getCurrentLanguageCode()
|
|
224
|
-
const href = '/swagger.json?lang=' + encodeURIComponent(code)
|
|
225
|
-
const link = document.createElement('link')
|
|
226
|
-
link.rel = 'preload'
|
|
227
|
-
link.as = 'fetch'
|
|
228
|
-
link.href = href
|
|
229
|
-
link.crossOrigin = ''
|
|
230
|
-
document.head.appendChild(link)
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
function resetTranslationCache () {
|
|
234
|
-
originalTextNodes = new WeakMap()
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
const swaggerSpecCache = {}
|
|
238
|
-
|
|
239
|
-
function updateSpecUrlInDom (specUrlString) {
|
|
240
|
-
const root = document.querySelector('.swagger-ui')
|
|
241
|
-
if (!root) return
|
|
242
|
-
root.querySelectorAll('input').forEach(input => {
|
|
243
|
-
if (input.value && input.value.includes('swagger.json')) {
|
|
244
|
-
input.value = specUrlString
|
|
245
|
-
}
|
|
246
|
-
})
|
|
247
|
-
root.querySelectorAll('a[href*="swagger.json"]').forEach(a => {
|
|
248
|
-
if (a.getAttribute('href') && a.getAttribute('href').includes('swagger.json')) {
|
|
249
|
-
a.setAttribute('href', specUrlString)
|
|
250
|
-
}
|
|
251
|
-
})
|
|
252
|
-
if (root.querySelectorAll) {
|
|
253
|
-
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null, false)
|
|
254
|
-
const textNodes = []
|
|
255
|
-
while (walker.nextNode()) textNodes.push(walker.currentNode)
|
|
256
|
-
textNodes.forEach(node => {
|
|
257
|
-
if (node.nodeValue && node.nodeValue.includes('swagger.json?lang=')) {
|
|
258
|
-
node.nodeValue = node.nodeValue.replace(
|
|
259
|
-
/https?:\/\/[^"\s]+\/swagger\.json\?lang=[^"\s&]+/g,
|
|
260
|
-
specUrlString
|
|
261
|
-
)
|
|
262
|
-
}
|
|
263
|
-
})
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
function prefetchSwaggerSpecs () {
|
|
268
|
-
const code = getCurrentLanguageCode()
|
|
269
|
-
const url = '/swagger.json?lang=' + encodeURIComponent(code)
|
|
270
|
-
fetch(url)
|
|
271
|
-
.then(r => r.json())
|
|
272
|
-
.then(spec => { swaggerSpecCache[code] = spec })
|
|
273
|
-
.catch(() => {})
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
function updateSwaggerSpec (language, shouldRefreshOpenOps = false) {
|
|
277
|
-
const specUrl = new URL('/swagger.json', window.location.origin)
|
|
278
|
-
specUrl.searchParams.set('lang', language)
|
|
279
|
-
updateSpecUrlInDom(specUrl.toString())
|
|
280
|
-
const applySpec = (spec) => {
|
|
281
|
-
if (!window.ui || !window.ui.specActions) return
|
|
282
|
-
resetTranslationCache()
|
|
283
|
-
window.ui.specActions.updateSpec(JSON.stringify(spec))
|
|
284
|
-
if (window.ui.specActions.updateUrl) {
|
|
285
|
-
window.ui.specActions.updateUrl(specUrl.toString())
|
|
286
|
-
}
|
|
287
|
-
setTimeout(() => updateSpecUrlInDom(specUrl.toString()), 0)
|
|
288
|
-
if (window.ui.specActions.updateJsonSpec) {
|
|
289
|
-
window.ui.specActions.updateJsonSpec(spec)
|
|
290
|
-
}
|
|
291
|
-
let parsedSpec = null
|
|
292
|
-
if (window.ui.specActions.parseToJson) {
|
|
293
|
-
parsedSpec = window.ui.specActions.parseToJson(JSON.stringify(spec))
|
|
294
|
-
}
|
|
295
|
-
if (parsedSpec && window.ui.specActions.updateJsonSpec) {
|
|
296
|
-
window.ui.specActions.updateJsonSpec(parsedSpec)
|
|
297
|
-
}
|
|
298
|
-
if (window.ui.specActions.updateResolved) {
|
|
299
|
-
window.ui.specActions.updateResolved(parsedSpec || spec)
|
|
300
|
-
}
|
|
301
|
-
if (window.ui.specActions.invalidateResolvedSubtreeCache) {
|
|
302
|
-
window.ui.specActions.invalidateResolvedSubtreeCache()
|
|
303
|
-
}
|
|
304
|
-
if (window.ui.specActions.resolveSpec) {
|
|
305
|
-
const resolveResult = window.ui.specActions.resolveSpec(specUrl.toString())
|
|
306
|
-
if (resolveResult && typeof resolveResult.catch === 'function') {
|
|
307
|
-
resolveResult.catch(() => {})
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
if (shouldRefreshOpenOps) {
|
|
311
|
-
refreshOpenOperations()
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
const cached = swaggerSpecCache[language]
|
|
315
|
-
if (cached) {
|
|
316
|
-
applySpec(cached)
|
|
317
|
-
return
|
|
318
|
-
}
|
|
319
|
-
fetch(specUrl.toString())
|
|
320
|
-
.then(response => response.json())
|
|
321
|
-
.then(spec => {
|
|
322
|
-
swaggerSpecCache[language] = spec
|
|
323
|
-
applySpec(spec)
|
|
324
|
-
})
|
|
325
|
-
.catch(() => {})
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
function refreshOpenOperations () {
|
|
329
|
-
const openOperations = document.querySelectorAll('.opblock.is-open .opblock-summary')
|
|
330
|
-
if (!openOperations.length) return
|
|
331
|
-
const requestResolvedSubtree = window.ui?.specActions?.requestResolvedSubtree
|
|
332
|
-
if (requestResolvedSubtree) {
|
|
333
|
-
openOperations.forEach(summary => {
|
|
334
|
-
const opblock = summary.closest('.opblock')
|
|
335
|
-
if (!opblock) return
|
|
336
|
-
const methodText = opblock.querySelector('.opblock-summary-method')?.textContent?.trim() || ''
|
|
337
|
-
const pathText = opblock.querySelector('.opblock-summary-path')?.textContent?.trim() || ''
|
|
338
|
-
if (!methodText || !pathText) return
|
|
339
|
-
requestResolvedSubtree(['paths', pathText, methodText.toLowerCase()])
|
|
340
|
-
})
|
|
341
|
-
}
|
|
342
|
-
openOperations.forEach(summary => summary.click())
|
|
343
|
-
setTimeout(() => {
|
|
344
|
-
openOperations.forEach(summary => summary.click())
|
|
345
|
-
}, 0)
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
function shouldTranslateNode (node) {
|
|
349
|
-
if (!node || !node.nodeValue || !node.nodeValue.trim()) return false
|
|
350
|
-
const parent = node.parentElement
|
|
351
|
-
if (!parent) return false
|
|
352
|
-
if (parent.closest('.lang-switcher')) return false
|
|
353
|
-
return !parent.closest('code, pre, textarea, input')
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
function translateNodeText (node, language) {
|
|
357
|
-
const config = translations[language] || translations['pt-BR']
|
|
358
|
-
if (!originalTextNodes.has(node)) {
|
|
359
|
-
originalTextNodes.set(node, node.nodeValue)
|
|
360
|
-
}
|
|
361
|
-
const original = originalTextNodes.get(node)
|
|
362
|
-
const trimmed = original.trim()
|
|
363
|
-
if (config.exact.has(trimmed)) {
|
|
364
|
-
const updated = original.replace(trimmed, config.exact.get(trimmed))
|
|
365
|
-
if (updated !== node.nodeValue) {
|
|
366
|
-
node.nodeValue = updated
|
|
367
|
-
}
|
|
368
|
-
return
|
|
369
|
-
}
|
|
370
|
-
let updated = original
|
|
371
|
-
config.replace.forEach(({ regex, value }) => {
|
|
372
|
-
updated = updated.replace(regex, value)
|
|
373
|
-
})
|
|
374
|
-
if (updated !== original) {
|
|
375
|
-
node.nodeValue = updated
|
|
376
|
-
return
|
|
377
|
-
}
|
|
378
|
-
if (node.nodeValue !== original) {
|
|
379
|
-
node.nodeValue = original
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
function applyTranslations (language) {
|
|
384
|
-
const root = document.querySelector('.swagger-ui')
|
|
385
|
-
if (!root || !window.NodeFilter || !document.createTreeWalker) return
|
|
386
|
-
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
|
|
387
|
-
acceptNode: node => (shouldTranslateNode(node) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT)
|
|
388
|
-
})
|
|
389
|
-
const nodes = []
|
|
390
|
-
while (walker.nextNode()) {
|
|
391
|
-
nodes.push(walker.currentNode)
|
|
392
|
-
}
|
|
393
|
-
nodes.forEach(node => translateNodeText(node, language))
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
function renderLanguageSwitcher (parent) {
|
|
397
|
-
if (document.querySelector('.lang-switcher')) return
|
|
398
|
-
const root = parent || document.querySelector('.swagger-ui')
|
|
399
|
-
if (!root) return
|
|
400
|
-
const wrapper = document.createElement('div')
|
|
401
|
-
wrapper.className = 'lang-switcher'
|
|
402
|
-
wrapper.setAttribute('aria-label', 'Seleção de idioma')
|
|
403
|
-
const currentLang = getPreferredLanguage()
|
|
404
|
-
supportedLanguages.forEach(language => {
|
|
405
|
-
const button = document.createElement('button')
|
|
406
|
-
button.type = 'button'
|
|
407
|
-
button.className = 'lang-switcher__button'
|
|
408
|
-
button.setAttribute('data-lang', language.code)
|
|
409
|
-
button.setAttribute('aria-label', language.label)
|
|
410
|
-
const flagUrl = flagUrls[language.code]
|
|
411
|
-
if (flagUrl) {
|
|
412
|
-
const img = document.createElement('img')
|
|
413
|
-
img.className = 'lang-switcher__flag'
|
|
414
|
-
img.src = flagUrl
|
|
415
|
-
img.alt = ''
|
|
416
|
-
img.width = 20
|
|
417
|
-
img.height = 14
|
|
418
|
-
button.appendChild(img)
|
|
419
|
-
} else {
|
|
420
|
-
button.textContent = language.code
|
|
421
|
-
}
|
|
422
|
-
if (language.code === currentLang) {
|
|
423
|
-
button.classList.add('is-active')
|
|
424
|
-
}
|
|
425
|
-
button.addEventListener('click', () => {
|
|
426
|
-
if (language.code === getPreferredLanguage()) return
|
|
427
|
-
const previousLanguage = getPreferredLanguage()
|
|
428
|
-
window.sessionStorage.setItem(lastLanguageKey, previousLanguage)
|
|
429
|
-
setPreferredLanguage(language.code)
|
|
430
|
-
const url = new URL(window.location.href)
|
|
431
|
-
url.searchParams.set('lang', language.code)
|
|
432
|
-
window.history.replaceState({}, '', url.toString())
|
|
433
|
-
wrapper.querySelectorAll('.lang-switcher__button').forEach(el => {
|
|
434
|
-
el.classList.toggle('is-active', el.getAttribute('data-lang') === language.code)
|
|
435
|
-
})
|
|
436
|
-
resetTranslationCache()
|
|
437
|
-
const shouldRefresh = setRefreshFlagFromState(language.code)
|
|
438
|
-
updateSwaggerSpec(language.code, shouldRefresh)
|
|
439
|
-
applyTranslations(language.code)
|
|
440
|
-
updateReleaseToastLanguage(language.code)
|
|
441
|
-
})
|
|
442
|
-
wrapper.appendChild(button)
|
|
443
|
-
})
|
|
444
|
-
root.appendChild(wrapper)
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
const lastLanguageKey = 'swaggerLastLanguage'
|
|
448
|
-
|
|
449
|
-
function hasOpenOperationHash () {
|
|
450
|
-
return typeof window.location.hash === 'string' && window.location.hash.includes('/')
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
function isEndpointBlockOpen () {
|
|
454
|
-
return endpointBlockOpen
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
function setEndpointBlockOpen (open) {
|
|
458
|
-
endpointBlockOpen = !!open
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
function shouldRefreshEndpointBlockNow (currentLanguage) {
|
|
462
|
-
if (!isEndpointBlockOpen()) return false
|
|
463
|
-
const lastLang = window.sessionStorage.getItem(lastLanguageKey)
|
|
464
|
-
return !lastLang || lastLang !== currentLanguage
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
function setRefreshFlagFromState (currentLanguage) {
|
|
468
|
-
return shouldRefreshEndpointBlockNow(currentLanguage)
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
const docsVersionKey = 'serverest-docs-version'
|
|
472
|
-
|
|
473
|
-
function clearCachesAndReloadForNewVersion (newVersion) {
|
|
474
|
-
const unregister = typeof navigator !== 'undefined' && navigator.serviceWorker && navigator.serviceWorker.getRegistrations
|
|
475
|
-
? navigator.serviceWorker.getRegistrations().then(regs => Promise.all(regs.map(r => r.unregister())))
|
|
476
|
-
: Promise.resolve()
|
|
477
|
-
const clearCaches = typeof caches !== 'undefined' && caches.keys
|
|
478
|
-
? caches.keys().then(keys => Promise.all(keys.map(k => caches.delete(k))))
|
|
479
|
-
: Promise.resolve()
|
|
480
|
-
Promise.all([unregister, clearCaches]).then(() => {
|
|
481
|
-
window.sessionStorage.setItem(docsVersionKey, newVersion)
|
|
482
|
-
window.location.reload()
|
|
483
|
-
})
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
function checkVersionThenObserve () {
|
|
487
|
-
const root = document.querySelector('.swagger-ui')
|
|
488
|
-
if (root) {
|
|
489
|
-
runVersionCheckAndObserve()
|
|
490
|
-
return
|
|
491
|
-
}
|
|
492
|
-
const mo = new MutationObserver(function () {
|
|
493
|
-
if (document.querySelector('.swagger-ui')) {
|
|
494
|
-
mo.disconnect()
|
|
495
|
-
runVersionCheckAndObserve()
|
|
496
|
-
}
|
|
497
|
-
})
|
|
498
|
-
mo.observe(document.documentElement, { childList: true, subtree: true })
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
function runVersionCheckAndObserve () {
|
|
502
|
-
const normalizedCurrent = normalizeVersion(currentVersion)
|
|
503
|
-
const storedVersion = window.sessionStorage.getItem(docsVersionKey)
|
|
504
|
-
if (storedVersion && storedVersion !== normalizedCurrent) {
|
|
505
|
-
clearCachesAndReloadForNewVersion(normalizedCurrent)
|
|
506
|
-
return
|
|
507
|
-
}
|
|
508
|
-
if (!storedVersion) {
|
|
509
|
-
window.sessionStorage.setItem(docsVersionKey, normalizedCurrent)
|
|
510
|
-
}
|
|
511
|
-
observe()
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
function registerServiceWorker () {
|
|
515
|
-
if (typeof navigator !== 'undefined' && 'serviceWorker' in navigator) {
|
|
516
|
-
navigator.serviceWorker.register('/swagger-sw.js').catch(() => {})
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
function observe () {
|
|
521
|
-
const root = document.querySelector('.swagger-ui')
|
|
522
|
-
if (!root) {
|
|
523
|
-
setTimeout(observe, 500)
|
|
524
|
-
return
|
|
525
|
-
}
|
|
526
|
-
registerServiceWorker()
|
|
527
|
-
let shouldRefreshEndpointBlock = false
|
|
528
|
-
if (window.performance && window.performance.getEntriesByType) {
|
|
529
|
-
const navEntries = window.performance.getEntriesByType('navigation')
|
|
530
|
-
if (navEntries && navEntries[0] && navEntries[0].type === 'reload') {
|
|
531
|
-
window.sessionStorage.removeItem(lastLanguageKey)
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
const queryLanguage = getLanguageFromQuery()
|
|
535
|
-
const preferred = getPreferredLanguage()
|
|
536
|
-
const language = queryLanguage || preferred
|
|
537
|
-
if (language !== preferred) {
|
|
538
|
-
window.sessionStorage.setItem(lastLanguageKey, preferred)
|
|
539
|
-
setPreferredLanguage(language)
|
|
540
|
-
}
|
|
541
|
-
if (!queryLanguage) {
|
|
542
|
-
const url = new URL(window.location.href)
|
|
543
|
-
url.searchParams.set('lang', language)
|
|
544
|
-
window.history.replaceState({}, '', url.toString())
|
|
545
|
-
}
|
|
546
|
-
let translationScheduled = false
|
|
547
|
-
const scheduleTranslations = () => {
|
|
548
|
-
if (translationScheduled) return
|
|
549
|
-
translationScheduled = true
|
|
550
|
-
setTimeout(() => {
|
|
551
|
-
translationScheduled = false
|
|
552
|
-
applyTranslations(getPreferredLanguage())
|
|
553
|
-
}, 0)
|
|
554
|
-
}
|
|
555
|
-
const observer = new MutationObserver(scheduleTranslations)
|
|
556
|
-
observer.observe(root, { childList: true, subtree: true })
|
|
557
|
-
root.addEventListener('click', event => {
|
|
558
|
-
if (!event.isTrusted) return
|
|
559
|
-
const summary = event.target.closest ? event.target.closest('.opblock-summary') : null
|
|
560
|
-
if (!summary) return
|
|
561
|
-
const opblock = summary.closest('.opblock')
|
|
562
|
-
if (!opblock) return
|
|
563
|
-
setTimeout(() => {
|
|
564
|
-
if (opblock.classList.contains('is-open')) {
|
|
565
|
-
setEndpointBlockOpen(true)
|
|
566
|
-
window.sessionStorage.setItem(lastLanguageKey, getPreferredLanguage())
|
|
567
|
-
} else {
|
|
568
|
-
setEndpointBlockOpen(false)
|
|
569
|
-
}
|
|
570
|
-
}, 0)
|
|
571
|
-
})
|
|
572
|
-
renderLanguageSwitcher(root)
|
|
573
|
-
scheduleTranslations()
|
|
574
|
-
if (hasOpenOperationHash()) {
|
|
575
|
-
setEndpointBlockOpen(true)
|
|
576
|
-
}
|
|
577
|
-
shouldRefreshEndpointBlock = setRefreshFlagFromState(language)
|
|
578
|
-
updateSwaggerSpec(language, shouldRefreshEndpointBlock)
|
|
579
|
-
renderReleaseToast()
|
|
580
|
-
fetch('https://api.github.com/repos/ServeRest/ServeRest/releases/latest', {
|
|
581
|
-
headers: { Accept: 'application/json' }
|
|
582
|
-
})
|
|
583
|
-
.then(r => r.json())
|
|
584
|
-
.then(data => {
|
|
585
|
-
if (data.tag_name && data.html_url) {
|
|
586
|
-
renderReleaseToast({
|
|
587
|
-
tag: data.tag_name,
|
|
588
|
-
version: normalizeVersion(data.tag_name),
|
|
589
|
-
url: data.html_url
|
|
590
|
-
})
|
|
591
|
-
}
|
|
592
|
-
})
|
|
593
|
-
.catch(() => {})
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
function init () {
|
|
597
|
-
preloadFlags()
|
|
598
|
-
preloadSwaggerJson()
|
|
599
|
-
prefetchSwaggerSpecs()
|
|
600
|
-
if (document.body) renderReleaseToast()
|
|
601
|
-
checkVersionThenObserve()
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
if (document.readyState === 'loading') {
|
|
605
|
-
document.addEventListener('DOMContentLoaded', init)
|
|
606
|
-
} else {
|
|
607
|
-
init()
|
|
608
|
-
}
|
|
609
|
-
})()
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const { readFileSync } = require('fs')
|
|
4
|
-
const { join } = require('path')
|
|
5
|
-
|
|
6
|
-
const customCss = readFileSync(join(__dirname, 'custom-css.css'), 'utf8')
|
|
7
|
-
const customJsTemplate = readFileSync(join(__dirname, 'custom-js.js'), 'utf8')
|
|
8
|
-
|
|
9
|
-
function buildCustomJsStr (releaseInfo, currentVersion, forceBanner) {
|
|
10
|
-
const replacements = {
|
|
11
|
-
__RELEASE_INFO__: JSON.stringify(releaseInfo),
|
|
12
|
-
__CURRENT_VERSION__: JSON.stringify(currentVersion),
|
|
13
|
-
__FORCE_BANNER__: JSON.stringify(forceBanner)
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const customJsStr = Object.keys(replacements).reduce((output, key) => {
|
|
17
|
-
return output.split(key).join(replacements[key])
|
|
18
|
-
}, customJsTemplate)
|
|
19
|
-
return customJsStr
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
module.exports = {
|
|
23
|
-
buildCustomJsStr,
|
|
24
|
-
customCss
|
|
25
|
-
}
|