openapi-explorer 0.11.465 → 0.11.470

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.
@@ -1 +1 @@
1
- {"version":3,"file":"openapi-explorer.min.js","mappings":";;AA8BA;ACusBA","sources":["webpack://openapi-explorer/./src/templates/code-samples-template.js","webpack://openapi-explorer/./src/components/api-request.js"],"sourcesContent":["import { html } from 'lit';\nimport { unsafeHTML } from 'lit/directives/unsafe-html.js';\nimport Prism from 'prismjs';\nimport { copyToClipboard } from '../utils/common-utils';\nimport { getI18nText } from '../languages';\n\n/* eslint-disable indent */\nexport default function codeSamplesTemplate(xCodeSamples) {\n return html`\n <section class=\"table-title top-gap\"> CODE SAMPLES </div>\n <div class=\"tab-panel col\"\n @click=\"${\n (e) => {\n if (!e.target.classList.contains('tab-btn')) { return; }\n const clickedTab = e.target.dataset.tab;\n\n const tabButtons = [...e.currentTarget.querySelectorAll('.tab-btn')];\n const tabContents = [...e.currentTarget.querySelectorAll('.tab-content')];\n tabButtons.forEach((tabBtnEl) => tabBtnEl.classList[tabBtnEl.dataset.tab === clickedTab ? 'add' : 'remove']('active'));\n tabContents.forEach((tabBodyEl) => { tabBodyEl.style.display = (tabBodyEl.dataset.tab === clickedTab ? 'block' : 'none'); });\n }\n }\">\n <div class=\"tab-buttons row\" style=\"width:100; overflow\">\n ${xCodeSamples.map((v, i) => html`<button class=\"tab-btn ${i === 0 ? 'active' : ''}\" data-tab = '${v.lang}${i}'> ${v.label || v.lang} </button>`)}\n </div>\n ${xCodeSamples.map((v, i) => {\n const paddingToRemove = Math.min(...v.source.split('\\n').slice(1).map(l => l.match(/^(\\s+).*$/)?.[1].length).filter(l => typeof l !== 'undefined'));\n const sanitizedSource = v.source.split('\\n').map(s => s.substring(0, paddingToRemove).match(/^\\s+$/) ? s.substring(paddingToRemove) : s);\n const fullSource = sanitizedSource.join('\\n');\n return html`\n <div class=\"tab-content m-markdown code-sample-wrapper\" style= \"display:${i === 0 ? 'block' : 'none'}\" data-tab = '${v.lang}${i}'>\n <button class=\"m-btn outline-primary toolbar-copy-btn\" @click='${(e) => { copyToClipboard(v.source, e); }}'>${getI18nText('operations.copy')}</button>\n <pre><code>${Prism.languages[v.lang?.toLowerCase()] ? unsafeHTML(Prism.highlight(fullSource, Prism.languages[v.lang?.toLowerCase()], v.lang?.toLowerCase())) : fullSource}\n </code></pre>\n </div>`;\n })\n }\n </section>`;\n}\n/* eslint-enable indent */\n","import { LitElement, html } from 'lit';\nimport { marked } from 'marked';\nimport Prism from 'prismjs';\nimport mimeTypeResolver from './mime-types';\n\nimport { unsafeHTML } from 'lit/directives/unsafe-html.js';\nimport formatXml from 'xml-but-prettier';\n\nimport { copyToClipboard } from '../utils/common-utils';\nimport { getI18nText } from '../languages';\nimport { schemaInObjectNotation, getTypeInfo, generateExample } from '../utils/schema-utils';\nimport './json-tree';\nimport './schema-tree';\nimport './tag-input';\n\nconst textFileRegex = RegExp('^font/|tar$|zip$|7z$|rtf$|msword$|excel$|/pdf$|/octet-stream$|^application/vnd.');\nconst mediaFileRegex = RegExp('^audio/|^image/|^video/');\nconst truncateString = (str, length) => (str && str.length > length ? `${str.substring(0, length - 1)}…` : str);\n\nexport default class ApiRequest extends LitElement {\n createRenderRoot() { return this; }\n\n constructor() {\n super();\n this.responseMessage = '';\n this.responseStatus = '';\n this.responseHeaders = '';\n this.responseText = '';\n this.responseUrl = '';\n this.responseElapsedMs = 0;\n this.curlSyntax = '';\n this.activeResponseTab = 'response'; // allowed values: response, headers, curl\n this.selectedRequestBodyType = '';\n this.selectedRequestBodyExample = '';\n }\n\n static get properties() {\n return {\n serverUrl: { type: String, attribute: 'server-url' },\n servers: { type: Array },\n method: { type: String },\n path: { type: String },\n elementId: { type: String, attribute: 'element-id' },\n parameters: { type: Array },\n request_body: { type: Object },\n api_keys: { type: Array },\n parser: { type: Object },\n accept: { type: String },\n callback: { type: String },\n responseMessage: { type: String, attribute: false },\n responseText: { type: String, attribute: false },\n responseHeaders: { type: String, attribute: false },\n responseStatus: { type: String, attribute: false },\n responseUrl: { type: String, attribute: false },\n responseElapsedMs: { type: Number, attribute: false },\n fillRequestWithDefault: { type: String, attribute: 'fill-defaults' },\n includeNulls: { type: Boolean, attribute: 'display-nulls', converter(value) { return value === 'true'; } },\n allowTry: { type: String, attribute: 'enable-console' },\n renderStyle: { type: String, attribute: 'render-style' },\n schemaStyle: { type: String, attribute: 'schema-style' },\n activeSchemaTab: { type: String, attribute: 'active-schema-tab' },\n schemaExpandLevel: { type: Number, attribute: 'schema-expand-level' },\n schemaHideReadOnly: { type: String, attribute: 'schema-hide-read-only' },\n fetchCredentials: { type: String, attribute: 'fetch-credentials' },\n\n // properties for internal tracking\n activeResponseTab: { type: String }, // internal tracking of response-tab not exposed as a attribute\n selectedRequestBodyType: { type: String, attribute: 'selected-request-body-type' }, // internal tracking of selected request-body type\n selectedRequestBodyExample: { type: String, attribute: 'selected-request-body-example' }, // internal tracking of selected request-body example\n };\n }\n\n render() {\n return html`\n <div class=\"api-request col regular-font request-panel ${(this.renderStyle === 'focused' || this.callback === 'true') ? 'focused-mode' : 'view-mode'}\">\n <div class=\" ${this.callback === 'true' ? 'tiny-title' : 'req-res-title'} \"> \n ${this.callback === 'true' ? 'CALLBACK REQUEST' : getI18nText('operations.request')}\n </div>\n <div>\n ${this.inputParametersTemplate('path')}\n ${this.inputParametersTemplate('query')}\n ${this.requestBodyTemplate()}\n ${this.inputParametersTemplate('header')}\n ${this.inputParametersTemplate('cookie')}\n ${this.allowTry === 'false' ? '' : html`${this.apiCallTemplate()}`}\n </div> \n </div>\n `;\n }\n\n updated(changedProperties) {\n // In focused mode after rendering the request component, update the text-areas(which contains examples) using the original values from hidden textareas.\n // This is done coz, user may update the dom by editing the textarea's and once the DOM is updated externally change detection wont happen, therefore update the values manually\n if (this.renderStyle !== 'focused') {\n return;\n }\n\n // dont update example as only tabs is switched\n if (changedProperties.size === 1 && changedProperties.has('activeSchemaTab')) {\n return;\n }\n\n const exampleTextAreaEls = [...this.querySelectorAll('textarea[data-ptype=\"form-data\"]')];\n exampleTextAreaEls.forEach((el) => {\n const origExampleEl = this.querySelector(`textarea[data-pname='hidden-${el.dataset.pname}']`);\n if (origExampleEl) {\n el.value = origExampleEl.value;\n }\n });\n }\n\n /* eslint-disable indent */\n inputParametersTemplate(paramType) {\n const filteredParams = this.parameters ? this.parameters.filter((param) => param.in === paramType) : [];\n if (filteredParams.length === 0) {\n return '';\n }\n let title = '';\n if (paramType === 'path') {\n title = 'PATH PARAMETERS';\n } else if (paramType === 'query') {\n title = 'QUERY-STRING PARAMETERS';\n } else if (paramType === 'header') {\n title = 'REQUEST HEADERS';\n } else if (paramType === 'cookie') {\n title = 'COOKIES';\n }\n\n const tableRows = [];\n for (const param of filteredParams) {\n if (!param.schema) {\n continue;\n }\n const paramSchema = getTypeInfo(param.schema, { includeNulls: this.includeNulls });\n if (!paramSchema) {\n continue;\n }\n const defaultVal = Array.isArray(paramSchema.default) ? paramSchema.default : `${paramSchema.default}`;\n let paramStyle = 'form';\n let paramExplode = true;\n if (paramType === 'query') {\n if (param.style && 'form spaceDelimited pipeDelimited'.includes(param.style)) {\n paramStyle = param.style;\n }\n if (typeof param.explode === 'boolean') {\n paramExplode = param.explode;\n }\n }\n\n tableRows.push(html`\n <tr> \n <td style=\"width:160px; min-width:50px;\">\n <div class=\"param-name ${paramSchema.deprecated ? 'deprecated' : ''}\">\n ${param.name}${!paramSchema.deprecated && param.required ? html`<span style='color:var(--red);'>*</span>` : ''}\n </div>\n <div class=\"param-type\">\n ${paramSchema.type === 'array'\n ? `${paramSchema.arrayType}`\n : `${paramSchema.format ? paramSchema.format : paramSchema.type}`\n }${!paramSchema.deprecated && param.required ? html`<span style='opacity: 0;'>*</span>` : ''}\n </div>\n </td> \n ${this.allowTry === 'true'\n ? html`\n <td style=\"min-width:160px;\">\n ${paramSchema.type === 'array'\n ? html`\n <tag-input class=\"request-param\" \n style = \"width:100%;\" \n data-ptype = \"${paramType}\"\n data-pname = \"${param.name}\"\n data-default = \"${Array.isArray(defaultVal) ? defaultVal.join('~|~') : defaultVal}\"\n data-param-serialize-style = \"${paramStyle}\"\n data-param-serialize-explode = \"${paramExplode}\"\n data-array = \"true\"\n placeholder=\"${paramSchema.example || (Array.isArray(defaultVal) ? defaultVal[0] : defaultVal) || 'add-multiple ↩'}\"\n .value = \"${Array.isArray(defaultVal) ? defaultVal : defaultVal.split(',')}\"\n >\n </tag-input>`\n : paramSchema.type === 'object'\n ? html`\n <textarea \n class = \"textarea request-param\"\n part = \"textarea textarea-param\"\n data-ptype = \"${paramType}-object\"\n data-pname = \"${param.name}\"\n data-default = \"${defaultVal}\"\n data-param-serialize-style = \"${paramStyle}\"\n data-param-serialize-explode = \"${paramExplode}\"\n spellcheck = \"false\"\n placeholder=\"${paramSchema.example || defaultVal || ''}\"\n style = \"resize:vertical; width:100%; height: ${'read focused'.includes(this.renderStyle) ? '180px' : '120px'};\"\n .value=\"${this.fillRequestWithDefault === 'true' ? defaultVal : ''}\"\n ></textarea>`\n : html`\n <input type=\"${paramSchema.format === 'password' ? 'password' : 'text'}\" spellcheck=\"false\" style=\"width:100%\" placeholder=\"${paramSchema.example || defaultVal || ''}\"\n class=\"request-param\"\n part=\"textbox textbox-param\"\n data-ptype=\"${paramType}\"\n data-pname=\"${param.name}\" \n data-default=\"${Array.isArray(defaultVal) ? defaultVal.join('~|~') : defaultVal}\"\n data-array=\"false\"\n @keyup=\"${this.requestParamFunction}\"\n .value=\"${this.fillRequestWithDefault === 'true' ? defaultVal : ''}\"\n />`\n }\n </td>`\n : ''\n }\n ${this.renderStyle === 'focused'\n ? html`\n <td>\n ${paramSchema.default || paramSchema.constraint || paramSchema.allowedValues || paramSchema.pattern\n ? html`\n <div class=\"param-constraint\">\n ${paramSchema.constraint ? html`<span style=\"font-weight:bold\">Constraints: </span>${paramSchema.constraint}<br>` : ''}\n ${paramSchema.pattern ? html`<span style=\"font-weight:bold\">Pattern: </span>${truncateString(paramSchema.pattern, 60)}<br>` : ''}\n ${paramSchema.allowedValues && paramSchema.allowedValues.split('┃').map((v, i) => html`\n ${i > 0 ? '|' : html`<span style=\"font-weight:bold\">Allowed: </span>`}\n ${html`\n <a part=\"anchor anchor-param-constraint\" class = \"${this.allowTry === 'true' ? '' : 'inactive-link'}\"\n data-type=\"${paramSchema.type === 'array' ? 'array' : 'string'}\"\n data-enum=\"${v.trim()}\"\n @click=\"${(e) => {\n const inputEl = e.target.closest('table').querySelector(`[data-pname=\"${param.name}\"]`);\n if (inputEl) {\n if (e.target.dataset.type === 'array') {\n inputEl.value = [e.target.dataset.enum];\n } else {\n inputEl.value = e.target.dataset.enum;\n }\n }\n }}\"\n >\n ${v} \n </a>`\n }`)}\n </div>`\n : ''\n }\n </td> \n </tr>`\n : ''\n }\n `);\n }\n\n return html`\n <div class=\"table-title top-gap\">${title}${paramType === 'path' ? html`<span style='color:var(--red);'>*</span>` : ''}</div>\n <div style=\"display:block; overflow-x:auto; max-width:100%;\">\n <table role=\"presentation\" class=\"m-table\" style=\"width:100%; word-break:break-word;\">\n ${tableRows}\n </table>\n </div>`;\n }\n\n resetRequestBodySelection() {\n this.selectedRequestBodyType = '';\n this.selectedRequestBodyExample = '';\n this.clearResponseData();\n }\n\n // Request-Body Event Handlers\n onSelectExample(e) {\n this.selectedRequestBodyExample = e.target.value;\n const exampleDropdownEl = e.target;\n window.setTimeout((selectEl) => {\n const exampleTextareaEl = selectEl.closest('.example-panel').querySelector('.request-body-param');\n const userInputExampleTextareaEl = selectEl.closest('.example-panel').querySelector('.request-body-param-user-input');\n userInputExampleTextareaEl.value = exampleTextareaEl.value;\n }, 0, exampleDropdownEl);\n }\n\n onMimeTypeChange(e) {\n this.selectedRequestBodyType = e.target.value;\n const mimeDropdownEl = e.target;\n this.selectedRequestBodyExample = '';\n window.setTimeout((selectEl) => {\n const exampleTextareaEl = selectEl.closest('.request-body-container').querySelector('.request-body-param');\n if (exampleTextareaEl) {\n const userInputExampleTextareaEl = selectEl.closest('.request-body-container').querySelector('.request-body-param-user-input');\n userInputExampleTextareaEl.value = exampleTextareaEl.value;\n }\n }, 0, mimeDropdownEl);\n }\n\n requestBodyTemplate() {\n if (!this.request_body) {\n return '';\n }\n if (Object.keys(this.request_body).length === 0) {\n return '';\n }\n\n // Variable to store partial HTMLs\n let reqBodyTypeSelectorHtml = '';\n let reqBodyFileInputHtml = '';\n let reqBodyFormHtml = '';\n let reqBodySchemaHtml = '';\n let reqBodyDefaultHtml = '';\n\n const requestBodyTypes = [];\n const content = this.request_body.content;\n for (const mimeType in content) {\n requestBodyTypes.push({\n mimeType,\n schema: content[mimeType].schema,\n example: content[mimeType].example,\n examples: content[mimeType].examples,\n });\n if (!this.selectedRequestBodyType) {\n this.selectedRequestBodyType = mimeType;\n }\n }\n // MIME Type selector\n reqBodyTypeSelectorHtml = requestBodyTypes.length === 1\n ? ''\n : html`\n <select aria-label=\"mime type\" style=\"min-width:100px; max-width:100%; margin-bottom:-1px;\" @change = '${(e) => this.onMimeTypeChange(e)}'>\n ${requestBodyTypes.map((reqBody) => html`\n <option value = '${reqBody.mimeType}' ?selected = '${reqBody.mimeType === this.selectedRequestBodyType}'>\n ${reqBody.mimeType}\n </option> `)\n }\n </select>\n `;\n\n // For Loop - Main\n requestBodyTypes.forEach((reqBody) => {\n let reqBodyExamples = [];\n\n if (this.selectedRequestBodyType.includes('json') || this.selectedRequestBodyType.includes('xml') || this.selectedRequestBodyType.includes('text')) {\n // Generate Example\n if (reqBody.mimeType === this.selectedRequestBodyType) {\n reqBodyExamples = generateExample(\n reqBody.examples ? reqBody.examples : '',\n reqBody.example ? reqBody.example : '',\n reqBody.schema,\n reqBody.mimeType,\n false,\n true,\n 'text',\n true\n );\n\n if (!this.selectedRequestBodyExample) {\n this.selectedRequestBodyExample = (reqBodyExamples.length > 0 ? reqBodyExamples[0].exampleId : '');\n }\n reqBodyDefaultHtml = html`\n ${reqBodyDefaultHtml}\n <div class = 'example-panel border-top pad-top-8'>\n ${reqBodyExamples.length === 1\n ? ''\n : html`\n <select aria-label='request body example' style=\"min-width:100px; max-width:100%; margin-bottom:-1px;\" @change='${(e) => this.onSelectExample(e)}'>\n ${reqBodyExamples.map((v) => html`<option value=\"${v.exampleId}\" ?selected=${v.exampleId === this.selectedRequestBodyExample} > \n ${v.exampleSummary.length > 80 ? v.exampleId : v.exampleSummary ? v.exampleSummary : v.exampleId} \n </option>`)}\n </select>\n `\n }\n ${reqBodyExamples\n .filter((v) => v.exampleId === this.selectedRequestBodyExample)\n .map((v) => html`\n <div class=\"example ${v.exampleId === this.selectedRequestBodyExample ? 'example-selected' : ''}\" data-default = '${v.exampleId}'>\n ${v.exampleSummary && v.exampleSummary.length > 80 ? html`<div style=\"padding: 4px 0\"> ${v.exampleSummary} </div>` : ''}\n ${v.exampleDescription ? html`<div class=\"m-markdown-small\" style=\"padding: 4px 0\"> ${unsafeHTML(marked(v.exampleDescription || ''))} </div>` : ''}\n <!-- this textarea is for user to edit the example -->\n <slot name=\"${this.elementId}--request-body\">\n <textarea \n class = \"textarea request-body-param-user-input\"\n part = \"textarea textarea-param\"\n spellcheck = \"false\"\n data-ptype = \"${reqBody.mimeType}\" \n data-default = \"${v.exampleFormat === 'text' ? v.exampleValue : JSON.stringify(v.exampleValue, null, 8)}\"\n data-default-format = \"${v.exampleFormat}\"\n style=\"width:100%; resize:vertical;\"\n .value=\"${this.fillRequestWithDefault === 'true' ? (v.exampleFormat === 'text' ? v.exampleValue : JSON.stringify(v.exampleValue, null, 8)) : ''}\"\n ></textarea>\n </slot>\n\n <!-- This textarea(hidden) is to store the original example value, this will remain unchanged when users switches from one example to another, its is used to populate the editable textarea -->\n <textarea \n class = \"textarea is-hidden request-body-param ${reqBody.mimeType.substring(reqBody.mimeType.indexOf('/') + 1)}\" \n spellcheck = \"false\"\n data-ptype = \"${reqBody.mimeType}\" \n style=\"width:100%; resize:vertical; display:none\"\n .value=\"${(v.exampleFormat === 'text' ? v.exampleValue : JSON.stringify(v.exampleValue, null, 8))}\"\n ></textarea>\n </div> \n `)}\n\n </div>\n `;\n }\n } else if (this.selectedRequestBodyType.includes('form-urlencoded') || this.selectedRequestBodyType.includes('form-data')) {\n if (reqBody.mimeType === this.selectedRequestBodyType) {\n const ex = generateExample(\n reqBody.examples ? reqBody.examples : '',\n reqBody.example ? reqBody.example : '',\n reqBody.schema,\n reqBody.mimeType,\n false,\n true,\n 'text',\n true\n );\n if (reqBody.schema) {\n reqBodyFormHtml = this.formDataTemplate(reqBody.schema, reqBody.mimeType, (ex[0] ? ex[0].exampleValue : ''));\n }\n }\n } else if (mediaFileRegex.test(this.selectedRequestBodyType) || textFileRegex.test(this.selectedRequestBodyType)) {\n if (reqBody.mimeType === this.selectedRequestBodyType) {\n reqBodyFileInputHtml = html`\n <div class = \"small-font-size bold-text row\">\n <input type=\"file\" part=\"file-input\" style=\"max-width:100%\" class=\"request-body-param-file\" data-ptype=\"${reqBody.mimeType}\" spellcheck=\"false\" />\n </div> \n `;\n }\n }\n\n // Generate Schema\n if (reqBody.mimeType.includes('json') || reqBody.mimeType.includes('xml') || reqBody.mimeType.includes('text')) {\n const schemaAsObj = schemaInObjectNotation(reqBody.schema, { includeNulls: this.includeNulls });\n if (this.schemaStyle === 'table') {\n reqBodySchemaHtml = html`\n ${reqBodySchemaHtml}\n <schema-table\n class = '${reqBody.mimeType.substring(reqBody.mimeType.indexOf('/') + 1)} pad-top-8'\n style = 'display: ${this.selectedRequestBodyType === reqBody.mimeType ? 'block' : 'none'};'\n .data = '${schemaAsObj}'\n schema-expand-level = \"${this.schemaExpandLevel}\"\n schema-hide-read-only = \"${this.schemaHideReadOnly.includes(this.method)}\"\n schema-hide-write-only = false\n > </schema-table>\n `;\n } else {\n reqBodySchemaHtml = html`\n ${reqBodySchemaHtml}\n <schema-tree\n class = '${reqBody.mimeType.substring(reqBody.mimeType.indexOf('/') + 1)} pad-top-8'\n style = 'display: ${this.selectedRequestBodyType === reqBody.mimeType ? 'block' : 'none'};'\n .data = '${schemaAsObj}'\n schema-expand-level = \"${this.schemaExpandLevel}\"\n schema-hide-read-only = \"${this.schemaHideReadOnly.includes(this.method)}\"\n schema-hide-write-only = false\n > </schema-tree>\n `;\n }\n }\n });\n\n return html`\n <div class='request-body-container' data-selected-request-body-type=\"${this.selectedRequestBodyType}\">\n <div class=\"table-title top-gap row\">\n REQUEST BODY ${this.request_body.required ? html`<span class=\"mono-font\" style='color:var(--red)'>*</span>` : ''} \n <span style = \"font-weight:normal; margin-left:5px\"> ${this.selectedRequestBodyType}</span>\n <span style=\"flex:1\"></span>\n ${reqBodyTypeSelectorHtml}\n </div>\n ${this.request_body.description ? html`<div class=\"m-markdown\" style=\"margin-bottom:12px\">${unsafeHTML(marked(this.request_body.description))}</div>` : ''}\n \n ${(this.selectedRequestBodyType.includes('json') || this.selectedRequestBodyType.includes('xml') || this.selectedRequestBodyType.includes('text'))\n ? html`\n <div class=\"tab-panel col\" style=\"border-width:0 0 1px 0;\">\n <div class=\"tab-buttons row\" @click=\"${(e) => { if (e.target.tagName.toLowerCase() === 'button') { this.activeSchemaTab = e.target.dataset.tab; } }}\">\n <button class=\"tab-btn ${this.activeSchemaTab === 'model' ? 'active' : ''}\" data-tab=\"model\" >${getI18nText('operations.model')}</button>\n <button class=\"tab-btn ${this.activeSchemaTab === 'body' ? 'active' : ''}\" data-tab=\"body\">${getI18nText('operations.body')}</button>\n </div>\n ${html`<div class=\"tab-content col\" style=\"display: ${this.activeSchemaTab === 'model' ? 'block' : 'none'}\"> ${reqBodySchemaHtml}</div>`}\n ${html`<div class=\"tab-content col\" style=\"display: ${this.activeSchemaTab === 'model' ? 'none' : 'block'}\"> ${reqBodyDefaultHtml}</div>`}\n </div>`\n : html` \n ${reqBodyFileInputHtml}\n ${reqBodyFormHtml}`\n }\n </div> \n `;\n }\n\n formDataTemplate(schema, mimeType, exampleValue = '') {\n const formDataTableRows = [];\n if (schema.properties) {\n for (const fieldName in schema.properties) {\n const fieldSchema = schema.properties[fieldName];\n if (fieldSchema.readOnly) {\n continue;\n }\n\n const fieldType = fieldSchema.type;\n const formdataPartSchema = schemaInObjectNotation(fieldSchema, { includeNulls: this.includeNulls });\n const paramSchema = getTypeInfo(fieldSchema, { includeNulls: this.includeNulls });\n const formdataPartExample = generateExample(\n '',\n fieldSchema.example ? fieldSchema.example : '',\n fieldSchema,\n 'json',\n false,\n true,\n 'text',\n true\n );\n\n formDataTableRows.push(html`\n <tr> \n <td style=\"width:160px; min-width:100px;\">\n <div class=\"param-name ${fieldSchema.deprecated ? 'deprecated' : ''}\">\n ${fieldName}${!fieldSchema.deprecated && (schema.required && schema.required.includes(fieldName) || fieldSchema.required) ? html`<span style='color:var(--red);'>*</span>` : ''}\n </div>\n <div class=\"param-type\">\n ${paramSchema.type === 'array' ? html`[<span>${paramSchema.format || paramSchema.type}</span>]` : `${paramSchema.format || paramSchema.type}`}\n </div>\n </td> \n <td \n style=\"${fieldType === 'object' ? 'width:100%; padding:0;' : this.allowTry === 'true' ? '' : 'display:none;'} min-width:100px;\" \n colspan=\"${fieldType === 'object' ? 2 : 1}\">\n ${fieldType === 'array'\n ? (fieldSchema.items && fieldSchema.items.format === 'binary')\n ? html`\n <div class=\"file-input-container col\" style='align-items:flex-end;' @click=\"${(e) => this.onAddRemoveFileInput(e, fieldName, mimeType)}\">\n <div class='input-set row'>\n <input \n type = \"file\"\n part = \"file-input\"\n style = \"width:100%\" \n data-pname = \"${fieldName}\" \n data-ptype = \"${mimeType.includes('form-urlencode') ? 'form-urlencode' : 'form-data'}\"\n data-array = \"false\" \n data-file-array = \"true\" \n />\n <button class=\"file-input-remove-btn\"> &#x2715; </button>\n </div> \n <button class=\"m-btn primary file-input-add-btn\" part=\"btn btn-fill\" style=\"margin:2px 25px 0 0; padding:2px 6px;\">ADD</button>\n </div> \n `\n : html`\n <tag-input\n style = \"width:100%;\" \n data-ptype = \"${mimeType.includes('form-urlencode') ? 'form-urlencode' : 'form-data'}\"\n data-pname = \"${fieldName}\"\n data-default = \"${paramSchema.default || ''}\"\n data-array = \"true\"\n placeholder=\"${(Array.isArray(paramSchema.example) ? paramSchema.example[0] : paramSchema.example) || 'add-multiple ↩'}\"\n .value = \"${paramSchema.default || ''}\"\n >\n </tag-input>\n `\n : html`\n ${fieldType === 'object'\n ? html`\n <div class=\"tab-panel row\" style=\"min-height:300px; border-left: 6px solid var(--light-border-color); align-items: stretch;\">\n <div style=\"width:24px; background-color:var(--light-border-color)\">\n <div class=\"row\" style=\"flex-direction:row-reverse; width:260px; height:24px; transform:rotate(270deg) translateX(-260px); transform-origin:top left; display:block;\" @click=\"${(e) => {\n if (e.target.classList.contains('v-tab-btn')) {\n const tab = e.target.dataset.tab;\n if (tab) {\n const tabPanelEl = e.target.closest('.tab-panel');\n const selectedTabBtnEl = tabPanelEl.querySelector(`.v-tab-btn[data-tab=\"${tab}\"]`);\n const otherTabBtnEl = [...tabPanelEl.querySelectorAll(`.v-tab-btn:not([data-tab=\"${tab}\"])`)];\n const selectedTabContentEl = tabPanelEl.querySelector(`.tab-content[data-tab=\"${tab}\"]`);\n const otherTabContentEl = [...tabPanelEl.querySelectorAll(`.tab-content:not([data-tab=\"${tab}\"])`)];\n selectedTabBtnEl.classList.add('active');\n selectedTabContentEl.style.display = 'block';\n otherTabBtnEl.forEach((el) => { el.classList.remove('active'); });\n otherTabContentEl.forEach((el) => { el.style.display = 'none'; });\n }\n }\n if (e.target.tagName.toLowerCase() === 'button') { this.activeSchemaTab = e.target.dataset.tab; }\n }}\">\n <button class=\"v-tab-btn ${this.activeSchemaTab === 'model' ? 'active' : ''}\" data-tab = 'model'>${getI18nText('operations.model')}</button>\n <button class=\"v-tab-btn ${this.activeSchemaTab === 'body' ? 'active' : ''}\" data-tab = 'body'>${getI18nText('operations.request-body')}</button>\n </div>\n </div> \n ${html`\n <div class=\"tab-content col\" data-tab = 'model' style=\"display:${this.activeSchemaTab === 'model' ? 'block' : 'none'}; padding-left:5px; width:100%;\"> \n <schema-tree\n .data = '${formdataPartSchema}'\n schema-expand-level = \"${this.schemaExpandLevel}\"> </schema-tree>\n </div>`\n }\n ${html`\n <div class=\"tab-content col\" data-tab = 'body' style=\"display:${this.activeSchemaTab === 'body' ? 'block' : 'none'}; padding-left:5px; width:100%\"> \n <textarea \n class = \"textarea\" placeholder=\"${formdataPartExample[0] && formdataPartExample[0].exampleValue || paramSchema.default || ''}\"\n part = \"textarea textarea-param\"\n style = \"width:100%; border:none; resize:vertical;\" \n data-array = \"false\" \n data-ptype = \"${mimeType.includes('form-urlencode') ? 'form-urlencode' : 'form-data'}\"\n data-pname = \"${fieldName}\"\n data-default = \"${paramSchema.default || ''}\"\n spellcheck = \"false\"\n .value=\"${this.fillRequestWithDefault === 'true' ? paramSchema.default : ''}\"\n ></textarea>\n <!-- This textarea(hidden) is to store the original example value, in focused mode on navbar change it is used to update the example text -->\n <textarea data-pname = \"hidden-${fieldName}\" data-ptype = \"${mimeType.includes('form-urlencode') ? 'hidden-form-urlencode' : 'hidden-form-data'}\" class=\"is-hidden\" style=\"display:none\" .value=\"${paramSchema.default}\"></textarea>\n </div>`\n }\n </div>`\n : html`\n ${this.allowTry === 'true'\n ? html`<input placeholder=\"${paramSchema.example || paramSchema.default || ''}\"\n .value = \"${this.fillRequestWithDefault === 'true' ? (paramSchema.default || '') : ''}\"\n spellcheck = \"false\"\n type = \"${fieldSchema.format === 'binary' ? 'file' : fieldSchema.format === 'password' ? 'password' : 'text'}\"\n part = \"textbox textbox-param\"\n style = \"width:100%\"\n data-ptype = \"${mimeType.includes('form-urlencode') ? 'form-urlencode' : 'form-data'}\"\n data-pname = \"${fieldName}\"\n data-default = \"${paramSchema.default || ''}\"\n data-array = \"false\"\n />`\n : ''\n }\n `\n }`\n }\n </td>\n ${fieldType === 'object'\n ? ''\n : html`\n <td>\n ${paramSchema.default || paramSchema.constraint || paramSchema.allowedValues || paramSchema.pattern\n ? html`\n <div class=\"param-constraint\">\n ${paramSchema.pattern ? html`<span style=\"font-weight:bold\">Pattern: </span>${paramSchema.pattern}<br/>` : ''}\n ${paramSchema.constraint ? html`<span style=\"font-weight:bold\">Constraints: </span>${paramSchema.constraint}<br/>` : ''}\n ${paramSchema.allowedValues && paramSchema.allowedValues.split('┃').map((v, i) => html`\n ${i > 0 ? '|' : html`<span style=\"font-weight:bold\">Allowed: </span>`}\n ${html`\n <a part=\"anchor anchor-param-constraint\" class = \"${this.allowTry === 'true' ? '' : 'inactive-link'}\"\n data-type=\"${paramSchema.type === 'array' ? paramSchema.type : 'string'}\"\n data-enum=\"${v.trim()}\"\n @click=\"${(e) => {\n const inputEl = e.target.closest('table').querySelector(`[data-pname=\"${fieldName}\"]`);\n if (inputEl) {\n if (e.target.dataset.type === 'array') {\n inputEl.value = [e.target.dataset.enum];\n } else {\n inputEl.value = e.target.dataset.enum;\n }\n }\n }}\"\n > \n ${v} \n </a>`\n }`)\n }\n </div>`\n : ''\n }\n </td>`\n }\n </tr>\n ${fieldType === 'object'\n ? ''\n : html`\n <tr>\n <td style=\"border:none\"> </td>\n <td colspan=\"2\" style=\"border:none; margin-top:0; padding:0 5px 8px 5px;\"> \n <span class=\"m-markdown-small\">${unsafeHTML(marked(fieldSchema.description || ''))}</span>\n ${paramSchema.example\n ? html`\n <span>\n <span style=\"font-weight:bold\"> Example: </span>\n ${paramSchema.type === 'array' ? '[ ' : ''}\n <a part=\"anchor anchor-param-example\" class = \"${this.allowTry === 'true' ? '' : 'inactive-link'}\"\n data-default-type=\"${paramSchema.type === 'array' ? paramSchema.type : 'string'}\"\n data-default = \"${Array.isArray(paramSchema.example) && paramSchema.example.join('~|~') || paramSchema.example || ''}\"\n @click=\"${(e) => {\n const inputEl = e.target.closest('table').querySelector(`[data-pname=\"${fieldName}\"]`);\n if (inputEl) {\n if (e.target.dataset.exampleType === 'array') {\n inputEl.value = e.target.dataset.example.split('~|~');\n } else {\n inputEl.value = e.target.dataset.example;\n }\n }\n }}\"\n >\n ${paramSchema.type === 'array' ? paramSchema.example.join(', ') : paramSchema.example}\n </a>\n ${paramSchema.type === 'array' ? '] ' : ''}\n </span>`\n : ''\n }\n </td>\n </tr>\n `\n }`);\n }\n return html`\n <table role=\"presentation\" style=\"width:100%;\" class=\"m-table\">\n ${formDataTableRows}\n </table>\n `;\n }\n\n return html`\n <textarea\n class = \"textarea dynamic-form-param ${mimeType}\"\n part = \"textarea textarea-param\"\n spellcheck = \"false\"\n data-pname=\"dynamic-form\" \n data-ptype=\"${mimeType}\" \n style=\"width:100%\"\n >${exampleValue}</textarea>\n ${schema.description ? html`<span class=\"m-markdown-small\">${unsafeHTML(marked(schema.description))}</span>` : ''}\n `;\n }\n\n apiResponseTabTemplate() {\n const responseFormat = this.responseHeaders.includes('json') ? 'json' : (this.responseHeaders.includes('html') || this.responseHeaders.includes('xml')) ? 'html' : '';\n return html`\n <div class=\"row\" style=\"font-size:var(--font-size-small); margin:5px 0\">\n ${this.responseMessage\n ? html`<div class=\"response-message ${this.responseStatus}\">Response Status: ${this.responseMessage}\n ${this.responseElapsedMs ? html`<span><br>Execution Time: ${this.responseElapsedMs}ms</span>` : ''}\n </div>` : ''\n }\n <div style=\"flex:1\"></div>\n <button class=\"m-btn\" part=\"btn btn-outline\" @click=\"${this.clearResponseData}\">CLEAR RESPONSE</button>\n </div>\n <div class=\"tab-panel col\" style=\"border-width:0 0 1px 0;\">\n <div id=\"tab_buttons\" class=\"tab-buttons row\" @click=\"${(e) => {\n if (e.target.classList.contains('tab-btn') === false) { return; }\n this.activeResponseTab = e.target.dataset.tab;\n }}\">\n <button class=\"tab-btn ${this.activeResponseTab === 'response' ? 'active' : ''}\" data-tab = 'response'>${getI18nText('operations.response')}</button>\n <button class=\"tab-btn ${this.activeResponseTab === 'headers' ? 'active' : ''}\" data-tab = 'headers'>${getI18nText('operations.response-headers')}</button>\n <button class=\"tab-btn ${this.activeResponseTab === 'curl' ? 'active' : ''}\" data-tab = 'curl'>CURL</button>\n </div>\n ${this.responseIsBlob\n ? html`\n <div class=\"tab-content col\" style=\"flex:1; display:${this.activeResponseTab === 'response' ? 'flex' : 'none'};\">\n <button class=\"m-btn thin-border mar-top-8\" style=\"width:135px\" @click=\"${this.downloadResponseBlob}\" part=\"btn btn-outline\">DOWNLOAD</button>\n ${this.responseBlobType === 'view'\n ? html`<button class=\"m-btn thin-border mar-top-8\" style=\"width:135px\" @click=\"${this.viewResponseBlob}\" part=\"btn btn-outline\">VIEW (NEW TAB)</button>`\n : ''\n }\n </div>`\n : html`\n <div class=\"tab-content col m-markdown\" style=\"flex:1; display:${this.activeResponseTab === 'response' ? 'flex' : 'none'};\" >\n ${this.responseText\n ? html`<button class=\"m-btn outline-primary toolbar-copy-btn\" @click='${(e) => { copyToClipboard(this.responseText, e); }}' part=\"btn btn-fill\">${getI18nText('operations.copy')}</button>`\n : ''\n }\n <pre style=\"min-height: 60px\">${responseFormat\n ? html`<code>${unsafeHTML(Prism.highlight(this.responseText, Prism.languages[responseFormat], responseFormat))}</code>`\n : `${this.responseText}`\n }\n </pre>\n </div>`\n }\n <div class=\"tab-content col m-markdown\" style=\"flex:1;display:${this.activeResponseTab === 'headers' ? 'flex' : 'none'};\" >\n <button class=\"m-btn outline-primary toolbar-copy-btn\" @click='${(e) => { copyToClipboard(this.responseHeaders, e); }}' part=\"btn btn-fill\">${getI18nText('operations.copy')}</button>\n <pre><code>${unsafeHTML(Prism.highlight(this.responseHeaders, Prism.languages.css, 'css'))}</code></pre>\n </div>\n <div class=\"tab-content col m-markdown\" style=\"flex:1;display:${this.activeResponseTab === 'curl' ? 'flex' : 'none'};\">\n <button class=\"m-btn outline-primary toolbar-copy-btn\" @click='${(e) => { copyToClipboard(this.curlSyntax.replace(/\\\\$/, ''), e); }}' part=\"btn btn-fill\">${getI18nText('operations.copy')}</button>\n <pre><code>${unsafeHTML(Prism.highlight(this.curlSyntax.trim().replace(/\\\\$/, ''), Prism.languages.shell, 'shell'))}</code></pre>\n </div>\n </div>`;\n }\n\n apiCallTemplate() {\n return html`\n <div style=\"display:flex; align-items:flex-end; margin:16px 0; font-size:var(--font-size-small);\">\n ${\n this.parameters.length > 0 || this.request_body\n ? html`\n <button class=\"m-btn thin-border\" part=\"btn btn-outline\" style=\"margin-right:5px;\" @click=\"${this.onClearRequestData}\">\n ${getI18nText('operations.clear')}\n </button>`\n : ''\n }\n <button class=\"m-btn primary btn-execute thin-border\" part=\"btn btn-fill btn-try\" @click=\"${this.onTryClick}\">${getI18nText('operations.execute')}</button>\n </div>\n ${this.responseMessage === '' ? '' : this.apiResponseTabTemplate()}\n `;\n }\n /* eslint-enable indent */\n\n onClearRequestData(e) {\n const requestPanelEl = e.target.closest('.request-panel');\n const requestPanelInputEls = [...requestPanelEl.querySelectorAll('input, tag-input, textarea:not(.is-hidden)')];\n requestPanelInputEls.forEach((el) => { el.value = ''; });\n\n const event = { bubbles: true, composed: true, detail: { explorerLocation: this.elementId, type: 'RequestCleared' } };\n this.dispatchEvent(new CustomEvent('event', event));\n }\n\n async onTryClick() {\n const tryBtnEl = this.querySelectorAll('.btn-execute')[0];\n let fetchUrl;\n let curlUrl;\n let curl = '';\n let curlHeaders = '';\n let curlData = '';\n let curlForm = '';\n const closestRespContainer = this.closest('.expanded-req-resp-container, .req-resp-container');\n const respEl = closestRespContainer && closestRespContainer.getElementsByTagName('api-response')[0];\n const acceptHeader = respEl && respEl.selectedMimeType;\n const requestPanelEl = this.closest('.request-panel');\n const pathParamEls = [...requestPanelEl.querySelectorAll(\"[data-ptype='path']\")];\n const queryParamEls = [...requestPanelEl.querySelectorAll(\"[data-ptype='query']\")];\n const queryParamObjTypeEls = [...requestPanelEl.querySelectorAll(\"[data-ptype='query-object']\")];\n const headerParamEls = [...requestPanelEl.querySelectorAll(\"[data-ptype='header']\")];\n const requestBodyContainerEl = requestPanelEl.querySelector('.request-body-container');\n\n fetchUrl = this.path.replaceAll(' ', '');\n const fetchOptions = {\n method: this.method.toUpperCase(),\n headers: new Headers()\n };\n // Generate URL using Path Params\n pathParamEls.map((el) => {\n fetchUrl = fetchUrl.replace(`{${el.dataset.pname}}`, encodeURIComponent(el.value));\n });\n\n // Query Params\n const urlQueryParam = new URLSearchParams();\n if (queryParamEls.length > 0) {\n queryParamEls.forEach((el) => {\n if (el.dataset.array === 'false') {\n if (el.value !== '') {\n urlQueryParam.append(el.dataset.pname, el.value);\n }\n } else {\n const paramSerializeStyle = el.dataset.paramSerializeStyle;\n const paramSerializeExplode = el.dataset.paramSerializeExplode;\n let vals = ((el.value && Array.isArray(el.value)) ? el.value : []);\n vals = Array.isArray(vals) ? vals.filter((v) => v !== '') : [];\n if (vals.length > 0) {\n if (paramSerializeStyle === 'spaceDelimited') {\n urlQueryParam.append(el.dataset.pname, vals.join(' ').replace(/^\\s|\\s$/g, ''));\n } else if (paramSerializeStyle === 'pipeDelimited') {\n urlQueryParam.append(el.dataset.pname, vals.join('|').replace(/^\\||\\|$/g, ''));\n } else {\n if (paramSerializeExplode === 'true') { // eslint-disable-line no-lonely-if\n vals.forEach((v) => { urlQueryParam.append(el.dataset.pname, v); });\n } else {\n urlQueryParam.append(el.dataset.pname, vals.join(',').replace(/^,|,$/g, ''));\n }\n }\n }\n }\n });\n }\n\n // Query Params (Dynamic - create from JSON)\n if (queryParamObjTypeEls.length > 0) {\n queryParamObjTypeEls.map((el) => {\n try {\n let queryParamObj = {};\n const paramSerializeStyle = el.dataset.paramSerializeStyle;\n const paramSerializeExplode = el.dataset.paramSerializeExplode;\n queryParamObj = Object.assign(queryParamObj, JSON.parse(el.value.replace(/\\s+/g, ' ')));\n for (const key in queryParamObj) {\n if (typeof queryParamObj[key] === 'object') {\n if (Array.isArray(queryParamObj[key])) {\n if (paramSerializeStyle === 'spaceDelimited') {\n urlQueryParam.append(key, queryParamObj[key].join(' '));\n } else if (paramSerializeStyle === 'pipeDelimited') {\n urlQueryParam.append(key, queryParamObj[key].join('|'));\n } else {\n if (paramSerializeExplode === 'true') { // eslint-disable-line no-lonely-if\n queryParamObj[key].forEach((v) => {\n urlQueryParam.append(key, v);\n });\n } else {\n urlQueryParam.append(key, queryParamObj[key]);\n }\n }\n }\n } else {\n urlQueryParam.append(key, queryParamObj[key]);\n }\n }\n } catch (err) {\n console.log('OpenAPI Explorer: unable to parse %s into object', el.value); // eslint-disable-line no-console\n }\n });\n }\n\n // Add Authentication api keys if provided\n this.api_keys.filter((v) => v.finalKeyValue).forEach((v) => {\n if (v.in === 'query') {\n urlQueryParam.append(v.name, v.finalKeyValue);\n return;\n }\n\n // Otherwise put it in the header\n fetchOptions.headers.append(v.name, v.finalKeyValue);\n curlHeaders += ` -H \"${v.name}: ${v.finalKeyValue}\" \\\\\\n`;\n });\n\n fetchUrl = `${fetchUrl}${urlQueryParam.toString() ? '?' : ''}${urlQueryParam.toString()}`;\n\n // Final URL for API call\n fetchUrl = `${this.serverUrl.replace(/\\/$/, '')}${fetchUrl}`;\n if (fetchUrl.startsWith('http') === false) {\n const url = new URL(fetchUrl, window.location.href);\n curlUrl = url.href;\n } else {\n curlUrl = fetchUrl;\n }\n curl = `curl -X ${this.method.toUpperCase()} \"${curlUrl}\" \\\\\\n`;\n\n if (acceptHeader) {\n // Uses the acceptHeader from Response panel\n fetchOptions.headers.append('Accept', acceptHeader);\n curlHeaders += ` -H \"Accept: ${acceptHeader}\" \\\\\\n`;\n } else if (this.accept) {\n fetchOptions.headers.append('Accept', this.accept);\n curlHeaders += ` -H \"Accept: ${this.accept}\" \\\\\\n`;\n }\n\n // Add Header Params\n headerParamEls.map((el) => {\n if (el.value) {\n fetchOptions.headers.append(el.dataset.pname, el.value);\n curlHeaders += ` -H \"${el.dataset.pname}: ${el.value}\" \\\\\\n`;\n }\n });\n\n // Request Body Params\n if (requestBodyContainerEl) {\n const requestBodyType = requestBodyContainerEl.dataset.selectedRequestBodyType;\n if (requestBodyType.includes('form-urlencoded')) {\n // url-encoded Form Params (dynamic) - Parse JSON and generate Params\n const formUrlDynamicTextAreaEl = requestPanelEl.querySelector(\"[data-ptype='dynamic-form']\");\n if (formUrlDynamicTextAreaEl) {\n const val = formUrlDynamicTextAreaEl.value;\n const formUrlDynParams = new URLSearchParams();\n let proceed = true;\n let tmpObj;\n if (val) {\n try {\n tmpObj = JSON.parse(val);\n } catch (err) {\n proceed = false;\n console.warn('OpenAPI Explorer: Invalid JSON provided', err); // eslint-disable-line no-console\n }\n } else {\n proceed = false;\n }\n if (proceed) {\n for (const prop in tmpObj) {\n formUrlDynParams.append(prop, JSON.stringify(tmpObj[prop]));\n }\n fetchOptions.body = formUrlDynParams;\n curlData = ` -d ${formUrlDynParams.toString()} \\\\\\n`;\n }\n } else {\n // url-encoded Form Params (regular)\n const formUrlEls = [...requestPanelEl.querySelectorAll(\"[data-ptype='form-urlencode']\")];\n const formUrlParams = new URLSearchParams();\n formUrlEls\n .filter((v) => (v.type !== 'file'))\n .forEach((el) => {\n if (el.dataset.array === 'false') {\n if (el.value) {\n formUrlParams.append(el.dataset.pname, el.value);\n }\n } else {\n const vals = (el.value && Array.isArray(el.value)) ? el.value.join(',') : '';\n formUrlParams.append(el.dataset.pname, vals);\n }\n });\n fetchOptions.body = formUrlParams;\n curlData = ` -d ${formUrlParams.toString()} \\\\\\n`;\n }\n } else if (requestBodyType.includes('form-data')) {\n const formDataParams = new FormData();\n const formDataEls = [...requestPanelEl.querySelectorAll(\"[data-ptype='form-data']\")];\n formDataEls.forEach((el) => {\n if (el.dataset.array === 'false') {\n if (el.type === 'file' && el.files[0]) {\n formDataParams.append(el.dataset.pname, el.files[0], el.files[0].name);\n curlForm += ` -F \"${el.dataset.pname}=@${el.files[0].name}\" \\\\\\n`;\n } else if (el.value) {\n formDataParams.append(el.dataset.pname, el.value);\n curlForm += ` -F \"${el.dataset.pname}=${el.value}\" \\\\\\n`;\n }\n } else if (el.value && Array.isArray(el.value)) {\n el.value.forEach((v) => {\n curlForm = `${curlForm} -F \"${el.dataset.pname}[]=${v}\" \\\\\\n`;\n });\n formDataParams.append(el.dataset.pname, el.value.join(','));\n }\n });\n fetchOptions.body = formDataParams;\n } else if (mediaFileRegex.test(requestBodyType) || textFileRegex.test(requestBodyType)) {\n const bodyParamFileEl = requestPanelEl.querySelector('.request-body-param-file');\n if (bodyParamFileEl && bodyParamFileEl.files[0]) {\n fetchOptions.body = bodyParamFileEl.files[0];\n curlData = ` --data-binary @${bodyParamFileEl.files[0].name} \\\\\\n`;\n }\n } else if (requestBodyType.includes('json') || requestBodyType.includes('xml') || requestBodyType.includes('text')) {\n const exampleTextAreaEl = requestPanelEl.querySelector('.request-body-param-user-input');\n if (exampleTextAreaEl && exampleTextAreaEl.value) {\n fetchOptions.body = exampleTextAreaEl.value;\n if (requestBodyType.includes('json')) {\n try {\n curlData = ` -d '${JSON.stringify(JSON.parse(exampleTextAreaEl.value))}' \\\\\\n`;\n } catch (err) { /* Ignore unparseable JSON */ }\n }\n\n if (!curlData) {\n // Save single quotes wrapped => 'text' => `\"'\"text\"'\"`\n curlData = ` -d '${exampleTextAreaEl.value.replace(/'/g, '\\'\"\\'\"\\'')}' \\\\\\n`;\n }\n }\n }\n // Common for all request-body\n if (!requestBodyType.includes('form-data')) {\n // For multipart/form-data don't set the content-type to allow creation of browser generated part boundaries\n fetchOptions.headers.append('Content-Type', requestBodyType);\n }\n curlHeaders += ` -H \"Content-Type: ${requestBodyType}\" \\\\\\n`;\n }\n this.curlSyntax = '';\n this.responseIsBlob = false;\n\n this.respContentDisposition = '';\n if (this.responseBlobUrl) {\n URL.revokeObjectURL(this.responseBlobUrl);\n this.responseBlobUrl = '';\n }\n this.curlSyntax = `${curl}${curlHeaders}${curlData}${curlForm}`;\n if (this.fetchCredentials) {\n fetchOptions.credentials = this.fetchCredentials;\n }\n\n // Options is legacy usage, documentation has been updated to reference properties of the fetch option directly, but older usages may still be using options\n const fetchRequest = { explorerLocation: this.elementId, url: fetchUrl, options: fetchOptions, ...fetchOptions };\n const event = {\n bubbles: true,\n composed: true,\n detail: {\n request: fetchRequest,\n },\n };\n this.dispatchEvent(new CustomEvent('before-try', event));\n this.dispatchEvent(new CustomEvent('request', event));\n const newFetchOptions = {\n method: fetchRequest.method || fetchOptions.method,\n headers: fetchRequest.headers || fetchOptions.headers,\n credentials: fetchRequest.credentials || fetchOptions.credentials,\n body: fetchRequest.body || fetchOptions.body\n };\n const fetchRequestObject = new Request(fetchRequest.url, newFetchOptions);\n\n let fetchResponse;\n try {\n let respBlob;\n let respJson;\n let respText;\n tryBtnEl.disabled = true;\n const fetchStart = new Date();\n\n this.responseStatus = '';\n this.responseMessage = '';\n this.responseUrl = '';\n this.responseHeaders = '';\n this.responseText = '⌛';\n\n this.requestUpdate();\n const awaiter = new Promise(resolve => setTimeout(resolve, 200));\n fetchResponse = await fetch(fetchRequestObject);\n this.responseElapsedMs = new Date() - fetchStart;\n await awaiter;\n\n tryBtnEl.disabled = false;\n this.responseStatus = fetchResponse.ok ? 'success' : 'error';\n this.responseMessage = fetchResponse.statusText ? `${fetchResponse.statusText} (${fetchResponse.status})` : fetchResponse.status;\n this.responseUrl = fetchResponse.url;\n this.responseHeaders = '';\n const headers = {};\n fetchResponse.headers.forEach((hdrVal, hdr) => {\n this.responseHeaders = `${this.responseHeaders}${hdr.trim()}: ${hdrVal}\\n`;\n headers[hdr.trim()] = hdrVal && hdrVal.trim();\n });\n const contentType = fetchResponse.headers.get('content-type');\n const respEmpty = (await fetchResponse.clone().text()).length === 0;\n if (respEmpty) {\n this.responseText = '';\n } else if (contentType) {\n if (contentType.includes('json')) {\n if ((/charset=[^\"']+/).test(contentType)) {\n const encoding = contentType.split('charset=')[1];\n const buffer = await fetchResponse.arrayBuffer();\n try {\n respText = new TextDecoder(encoding).decode(buffer);\n } catch {\n respText = new TextDecoder('utf-8').decode(buffer);\n }\n try {\n this.responseText = JSON.stringify(JSON.parse(respText), null, 8);\n } catch {\n this.responseText = respText;\n }\n } else {\n respJson = await fetchResponse.json();\n this.responseText = JSON.stringify(respJson, null, 8);\n }\n } else if (textFileRegex.test(contentType)) {\n this.responseIsBlob = true;\n this.responseBlobType = 'download';\n } else if (mediaFileRegex.test(contentType)) {\n this.responseIsBlob = true;\n this.responseBlobType = 'view';\n } else {\n respText = await fetchResponse.text();\n if (contentType.includes('xml')) {\n this.responseText = formatXml(respText, { textNodesOnSameLine: true, indentor: ' ' });\n } else {\n this.responseText = respText;\n }\n }\n if (this.responseIsBlob) {\n const contentDisposition = fetchResponse.headers.get('content-disposition');\n const filenameRegex = /filename[^;=\\n]*=((['\"]).*?\\2|[^;\\n]*)/;\n const filename = filenameRegex.exec(contentDisposition);\n this.respContentDisposition = filename && filename[1] && filename[1].replace(/['\"]/g, '') || `download.${mimeTypeResolver.extension(contentType) || 'file'}`;\n respBlob = await fetchResponse.blob();\n this.responseBlobUrl = URL.createObjectURL(respBlob);\n }\n } else {\n respText = await fetchResponse.text();\n this.responseText = respText;\n }\n const responseEvent = {\n bubbles: true,\n composed: true,\n detail: {\n explorerLocation: this.elementId,\n request: fetchRequest,\n response: {\n headers,\n body: respJson || respText || respBlob || fetchResponse.body,\n status: fetchResponse.status,\n },\n },\n };\n this.dispatchEvent(new CustomEvent('after-try', responseEvent));\n this.dispatchEvent(new CustomEvent('response', responseEvent));\n } catch (error) {\n tryBtnEl.disabled = false;\n this.responseMessage = `${error.message} (Check the browser network tab for more information.)`;\n this.responseStatus = 'error';\n const responseEvent = {\n bubbles: true,\n composed: true,\n detail: {\n explorerLocation: this.elementId,\n error,\n request: fetchRequest,\n },\n };\n document.dispatchEvent(new CustomEvent('after-try', responseEvent));\n document.dispatchEvent(new CustomEvent('response', responseEvent));\n }\n }\n\n onAddRemoveFileInput(e, pname, ptype) {\n if (e.target.tagName.toLowerCase() !== 'button') {\n return;\n }\n\n if (e.target.classList.contains('file-input-remove-btn')) {\n // Remove File Input Set\n const el = e.target.closest('.input-set');\n el.remove();\n return;\n }\n const el = e.target.closest('.file-input-container');\n\n // Add File Input Set\n\n // Container\n const newInputContainerEl = document.createElement('div');\n newInputContainerEl.setAttribute('class', 'input-set row');\n\n // File Input\n const newInputEl = document.createElement('input');\n newInputEl.type = 'file';\n newInputEl.style = 'width:200px; margin-top:2px;';\n newInputEl.setAttribute('data-pname', pname);\n newInputEl.setAttribute('data-ptype', ptype.includes('form-urlencode') ? 'form-urlencode' : 'form-data');\n newInputEl.setAttribute('data-array', 'false');\n newInputEl.setAttribute('data-file-array', 'true');\n\n // Remover Button\n const newRemoveBtnEl = document.createElement('button');\n newRemoveBtnEl.setAttribute('class', 'file-input-remove-btn');\n newRemoveBtnEl.innerHTML = '&#x2715;';\n\n newInputContainerEl.appendChild(newInputEl);\n newInputContainerEl.appendChild(newRemoveBtnEl);\n el.insertBefore(newInputContainerEl, e.target);\n // el.appendChild(newInputContainerEl);\n }\n\n downloadResponseBlob() {\n if (this.responseBlobUrl) {\n const a = document.createElement('a');\n document.body.appendChild(a);\n a.style = 'display: none';\n a.href = this.responseBlobUrl;\n a.download = this.respContentDisposition;\n a.click();\n a.remove();\n }\n }\n\n viewResponseBlob() {\n if (this.responseBlobUrl) {\n const a = document.createElement('a');\n document.body.appendChild(a);\n a.style = 'display: none';\n a.href = this.responseBlobUrl;\n a.target = '_blank';\n a.click();\n a.remove();\n }\n }\n\n clearResponseData() {\n this.responseUrl = '';\n this.responseHeaders = '';\n this.responseText = '';\n this.responseStatus = '';\n this.responseMessage = '';\n this.responseElapsedMs = 0;\n this.responseIsBlob = false;\n this.responseBlobType = '';\n this.respContentDisposition = '';\n if (this.responseBlobUrl) {\n URL.revokeObjectURL(this.responseBlobUrl);\n this.responseBlobUrl = '';\n }\n }\n\n requestParamFunction(event) {\n if (event.key === 'Enter') {\n this.onTryClick();\n event.preventDefault();\n }\n }\n\n disconnectedCallback() {\n // Cleanup ObjectURL forthe blob data if this component created one\n if (this.responseBlobUrl) {\n URL.revokeObjectURL(this.responseBlobUrl);\n this.responseBlobUrl = '';\n }\n super.disconnectedCallback();\n }\n}\n\n// Register the element with the browser\nif (!customElements.get('openapi-explorer')) {\n customElements.define('api-request', ApiRequest);\n}\n"],"names":[],"sourceRoot":""}
1
+ {"version":3,"file":"openapi-explorer.min.js","mappings":";;AA8BA;ACusBA","sources":["webpack://openapi-explorer/./src/templates/code-samples-template.js","webpack://openapi-explorer/./src/components/api-request.js"],"sourcesContent":["import { html } from 'lit';\nimport { unsafeHTML } from 'lit/directives/unsafe-html.js';\nimport Prism from 'prismjs';\nimport { copyToClipboard } from '../utils/common-utils';\nimport { getI18nText } from '../languages';\n\n/* eslint-disable indent */\nexport default function codeSamplesTemplate(xCodeSamples) {\n return html`\n <section class=\"table-title top-gap\"> CODE SAMPLES </div>\n <div class=\"tab-panel col\"\n @click=\"${\n (e) => {\n if (!e.target.classList.contains('tab-btn')) { return; }\n const clickedTab = e.target.dataset.tab;\n\n const tabButtons = [...e.currentTarget.querySelectorAll('.tab-btn')];\n const tabContents = [...e.currentTarget.querySelectorAll('.tab-content')];\n tabButtons.forEach((tabBtnEl) => tabBtnEl.classList[tabBtnEl.dataset.tab === clickedTab ? 'add' : 'remove']('active'));\n tabContents.forEach((tabBodyEl) => { tabBodyEl.style.display = (tabBodyEl.dataset.tab === clickedTab ? 'block' : 'none'); });\n }\n }\">\n <div class=\"tab-buttons row\" style=\"width:100; overflow\">\n ${xCodeSamples.map((v, i) => html`<button class=\"tab-btn ${i === 0 ? 'active' : ''}\" data-tab = '${v.lang}${i}'> ${v.label || v.lang} </button>`)}\n </div>\n ${xCodeSamples.map((v, i) => {\n const paddingToRemove = Math.min(...v.source.split('\\n').slice(1).map(l => l.match(/^(\\s+).*$/)?.[1].length).filter(l => typeof l !== 'undefined'));\n const sanitizedSource = v.source.split('\\n').map(s => s.substring(0, paddingToRemove).match(/^\\s+$/) ? s.substring(paddingToRemove) : s);\n const fullSource = sanitizedSource.join('\\n');\n return html`\n <div class=\"tab-content m-markdown code-sample-wrapper\" style= \"display:${i === 0 ? 'block' : 'none'}\" data-tab = '${v.lang}${i}'>\n <button class=\"m-btn outline-primary toolbar-copy-btn\" @click='${(e) => { copyToClipboard(v.source, e); }}'>${getI18nText('operations.copy')}</button>\n <pre><code>${Prism.languages[v.lang?.toLowerCase()] ? unsafeHTML(Prism.highlight(fullSource, Prism.languages[v.lang?.toLowerCase()], v.lang?.toLowerCase())) : fullSource}\n </code></pre>\n </div>`;\n })\n }\n </section>`;\n}\n/* eslint-enable indent */\n","import { LitElement, html } from 'lit';\nimport { marked } from 'marked';\nimport Prism from 'prismjs';\nimport mimeTypeResolver from './mime-types';\n\nimport { unsafeHTML } from 'lit/directives/unsafe-html.js';\nimport formatXml from 'xml-but-prettier';\n\nimport { copyToClipboard } from '../utils/common-utils';\nimport { getI18nText } from '../languages';\nimport { schemaInObjectNotation, getTypeInfo, generateExample } from '../utils/schema-utils';\nimport './json-tree';\nimport './schema-tree';\nimport './tag-input';\n\nconst textFileRegex = RegExp('^font/|tar$|zip$|7z$|rtf$|msword$|excel$|/pdf$|/octet-stream$|^application/vnd.');\nconst mediaFileRegex = RegExp('^audio/|^image/|^video/');\nconst truncateString = (str, length) => (str && str.length > length ? `${str.substring(0, length - 1)}…` : str);\n\nexport default class ApiRequest extends LitElement {\n createRenderRoot() { return this; }\n\n constructor() {\n super();\n this.responseMessage = '';\n this.responseStatus = '';\n this.responseHeaders = '';\n this.responseText = '';\n this.responseUrl = '';\n this.responseElapsedMs = 0;\n this.curlSyntax = '';\n this.activeResponseTab = 'response'; // allowed values: response, headers, curl\n this.selectedRequestBodyType = '';\n this.selectedRequestBodyExample = '';\n }\n\n static get properties() {\n return {\n serverUrl: { type: String, attribute: 'server-url' },\n servers: { type: Array },\n method: { type: String },\n path: { type: String },\n elementId: { type: String, attribute: 'element-id' },\n parameters: { type: Array },\n request_body: { type: Object },\n api_keys: { type: Array },\n parser: { type: Object },\n accept: { type: String },\n callback: { type: String },\n responseMessage: { type: String, attribute: false },\n responseText: { type: String, attribute: false },\n responseHeaders: { type: String, attribute: false },\n responseStatus: { type: String, attribute: false },\n responseUrl: { type: String, attribute: false },\n responseElapsedMs: { type: Number, attribute: false },\n fillRequestWithDefault: { type: String, attribute: 'fill-defaults' },\n includeNulls: { type: Boolean, attribute: 'display-nulls', converter(value) { return value === 'true'; } },\n allowTry: { type: String, attribute: 'enable-console' },\n renderStyle: { type: String, attribute: 'render-style' },\n schemaStyle: { type: String, attribute: 'schema-style' },\n activeSchemaTab: { type: String, attribute: 'active-schema-tab' },\n schemaExpandLevel: { type: Number, attribute: 'schema-expand-level' },\n schemaHideReadOnly: { type: String, attribute: 'schema-hide-read-only' },\n fetchCredentials: { type: String, attribute: 'fetch-credentials' },\n\n // properties for internal tracking\n activeResponseTab: { type: String }, // internal tracking of response-tab not exposed as a attribute\n selectedRequestBodyType: { type: String, attribute: 'selected-request-body-type' }, // internal tracking of selected request-body type\n selectedRequestBodyExample: { type: String, attribute: 'selected-request-body-example' }, // internal tracking of selected request-body example\n };\n }\n\n render() {\n return html`\n <div class=\"api-request col regular-font request-panel ${(this.renderStyle === 'focused' || this.callback === 'true') ? 'focused-mode' : 'view-mode'}\">\n <div class=\" ${this.callback === 'true' ? 'tiny-title' : 'req-res-title'} \"> \n ${this.callback === 'true' ? 'CALLBACK REQUEST' : getI18nText('operations.request')}\n </div>\n <div>\n ${this.inputParametersTemplate('path')}\n ${this.inputParametersTemplate('query')}\n ${this.requestBodyTemplate()}\n ${this.inputParametersTemplate('header')}\n ${this.inputParametersTemplate('cookie')}\n ${this.allowTry === 'false' ? '' : html`${this.apiCallTemplate()}`}\n </div> \n </div>\n `;\n }\n\n updated(changedProperties) {\n // In focused mode after rendering the request component, update the text-areas(which contains examples) using the original values from hidden textareas.\n // This is done coz, user may update the dom by editing the textarea's and once the DOM is updated externally change detection wont happen, therefore update the values manually\n if (this.renderStyle !== 'focused') {\n return;\n }\n\n // dont update example as only tabs is switched\n if (changedProperties.size === 1 && changedProperties.has('activeSchemaTab')) {\n return;\n }\n\n const exampleTextAreaEls = [...this.querySelectorAll('textarea[data-ptype=\"form-data\"]')];\n exampleTextAreaEls.forEach((el) => {\n const origExampleEl = this.querySelector(`textarea[data-pname='hidden-${el.dataset.pname}']`);\n if (origExampleEl) {\n el.value = origExampleEl.value;\n }\n });\n }\n\n /* eslint-disable indent */\n inputParametersTemplate(paramType) {\n const filteredParams = this.parameters ? this.parameters.filter((param) => param.in === paramType) : [];\n if (filteredParams.length === 0) {\n return '';\n }\n let title = '';\n if (paramType === 'path') {\n title = 'PATH PARAMETERS';\n } else if (paramType === 'query') {\n title = 'QUERY-STRING PARAMETERS';\n } else if (paramType === 'header') {\n title = 'REQUEST HEADERS';\n } else if (paramType === 'cookie') {\n title = 'COOKIES';\n }\n\n const tableRows = [];\n for (const param of filteredParams) {\n if (!param.schema) {\n continue;\n }\n const paramSchema = getTypeInfo(param.schema, { includeNulls: this.includeNulls });\n if (!paramSchema) {\n continue;\n }\n const defaultVal = Array.isArray(paramSchema.default) ? paramSchema.default : `${paramSchema.default}`;\n let paramStyle = 'form';\n let paramExplode = true;\n if (paramType === 'query') {\n if (param.style && 'form spaceDelimited pipeDelimited'.includes(param.style)) {\n paramStyle = param.style;\n }\n if (typeof param.explode === 'boolean') {\n paramExplode = param.explode;\n }\n }\n\n tableRows.push(html`\n <tr> \n <td style=\"width:160px; min-width:50px;\">\n <div class=\"param-name ${paramSchema.deprecated ? 'deprecated' : ''}\">\n ${param.name}${!paramSchema.deprecated && param.required ? html`<span style='color:var(--red);'>*</span>` : ''}\n </div>\n <div class=\"param-type\">\n ${paramSchema.type === 'array'\n ? `${paramSchema.arrayType}`\n : `${paramSchema.format ? paramSchema.format : paramSchema.type}`\n }${!paramSchema.deprecated && param.required ? html`<span style='opacity: 0;'>*</span>` : ''}\n </div>\n </td> \n ${this.allowTry === 'true'\n ? html`\n <td style=\"min-width:160px;\">\n ${paramSchema.type === 'array'\n ? html`\n <tag-input class=\"request-param\" \n style = \"width:100%;\" \n data-ptype = \"${paramType}\"\n data-pname = \"${param.name}\"\n data-default = \"${Array.isArray(defaultVal) ? defaultVal.join('~|~') : defaultVal}\"\n data-param-serialize-style = \"${paramStyle}\"\n data-param-serialize-explode = \"${paramExplode}\"\n data-array = \"true\"\n placeholder=\"${paramSchema.example || (Array.isArray(defaultVal) ? defaultVal[0] : defaultVal) || 'add-multiple ↩'}\"\n .value = \"${Array.isArray(defaultVal) ? defaultVal : defaultVal.split(',')}\"\n >\n </tag-input>`\n : paramSchema.type === 'object'\n ? html`\n <textarea \n class = \"textarea request-param\"\n part = \"textarea textarea-param\"\n data-ptype = \"${paramType}-object\"\n data-pname = \"${param.name}\"\n data-default = \"${defaultVal}\"\n data-param-serialize-style = \"${paramStyle}\"\n data-param-serialize-explode = \"${paramExplode}\"\n spellcheck = \"false\"\n placeholder=\"${paramSchema.example || defaultVal || ''}\"\n style = \"resize:vertical; width:100%; height: ${'read focused'.includes(this.renderStyle) ? '180px' : '120px'};\"\n .value=\"${this.fillRequestWithDefault === 'true' ? defaultVal : ''}\"\n ></textarea>`\n : html`\n <input type=\"${paramSchema.format === 'password' ? 'password' : 'text'}\" spellcheck=\"false\" style=\"width:100%\" placeholder=\"${paramSchema.example || defaultVal || ''}\"\n class=\"request-param\"\n part=\"textbox textbox-param\"\n data-ptype=\"${paramType}\"\n data-pname=\"${param.name}\" \n data-default=\"${Array.isArray(defaultVal) ? defaultVal.join('~|~') : defaultVal}\"\n data-array=\"false\"\n @keyup=\"${this.requestParamFunction}\"\n .value=\"${this.fillRequestWithDefault === 'true' ? defaultVal : ''}\"\n />`\n }\n </td>`\n : ''\n }\n ${this.renderStyle === 'focused'\n ? html`\n <td>\n ${paramSchema.default || paramSchema.constraint || paramSchema.allowedValues || paramSchema.pattern\n ? html`\n <div class=\"param-constraint\">\n ${paramSchema.constraint ? html`<span style=\"font-weight:bold\">Constraints: </span>${paramSchema.constraint}<br>` : ''}\n ${paramSchema.pattern ? html`<span style=\"font-weight:bold\">Pattern: </span>${truncateString(paramSchema.pattern, 60)}<br>` : ''}\n ${paramSchema.allowedValues && paramSchema.allowedValues.split('┃').map((v, i) => html`\n ${i > 0 ? '|' : html`<span style=\"font-weight:bold\">Allowed: </span>`}\n ${html`\n <a part=\"anchor anchor-param-constraint\" class = \"${this.allowTry === 'true' ? '' : 'inactive-link'}\"\n data-type=\"${paramSchema.type === 'array' ? 'array' : 'string'}\"\n data-enum=\"${v.trim()}\"\n @click=\"${(e) => {\n const inputEl = e.target.closest('table').querySelector(`[data-pname=\"${param.name}\"]`);\n if (inputEl) {\n if (e.target.dataset.type === 'array') {\n inputEl.value = [e.target.dataset.enum];\n } else {\n inputEl.value = e.target.dataset.enum;\n }\n }\n }}\"\n >\n ${v} \n </a>`\n }`)}\n </div>`\n : ''\n }\n </td> \n </tr>`\n : ''\n }\n `);\n }\n\n return html`\n <div class=\"table-title top-gap\">${title}${paramType === 'path' ? html`<span style='color:var(--red);'>*</span>` : ''}</div>\n <div style=\"display:block; overflow-x:auto; max-width:100%;\">\n <table role=\"presentation\" class=\"m-table\" style=\"width:100%; word-break:break-word;\">\n ${tableRows}\n </table>\n </div>`;\n }\n\n resetRequestBodySelection() {\n this.selectedRequestBodyType = '';\n this.selectedRequestBodyExample = '';\n this.clearResponseData();\n }\n\n // Request-Body Event Handlers\n onSelectExample(e) {\n this.selectedRequestBodyExample = e.target.value;\n const exampleDropdownEl = e.target;\n window.setTimeout((selectEl) => {\n const exampleTextareaEl = selectEl.closest('.example-panel').querySelector('.request-body-param');\n const userInputExampleTextareaEl = selectEl.closest('.example-panel').querySelector('.request-body-param-user-input');\n userInputExampleTextareaEl.value = exampleTextareaEl.value;\n }, 0, exampleDropdownEl);\n }\n\n onMimeTypeChange(e) {\n this.selectedRequestBodyType = e.target.value;\n const mimeDropdownEl = e.target;\n this.selectedRequestBodyExample = '';\n window.setTimeout((selectEl) => {\n const exampleTextareaEl = selectEl.closest('.request-body-container').querySelector('.request-body-param');\n if (exampleTextareaEl) {\n const userInputExampleTextareaEl = selectEl.closest('.request-body-container').querySelector('.request-body-param-user-input');\n userInputExampleTextareaEl.value = exampleTextareaEl.value;\n }\n }, 0, mimeDropdownEl);\n }\n\n requestBodyTemplate() {\n if (!this.request_body) {\n return '';\n }\n if (Object.keys(this.request_body).length === 0) {\n return '';\n }\n\n // Variable to store partial HTMLs\n let reqBodyTypeSelectorHtml = '';\n let reqBodyFileInputHtml = '';\n let reqBodyFormHtml = '';\n let reqBodySchemaHtml = '';\n let reqBodyDefaultHtml = '';\n\n const requestBodyTypes = [];\n const content = this.request_body.content;\n for (const mimeType in content) {\n requestBodyTypes.push({\n mimeType,\n schema: content[mimeType].schema,\n example: content[mimeType].example,\n examples: content[mimeType].examples,\n });\n if (!this.selectedRequestBodyType) {\n this.selectedRequestBodyType = mimeType;\n }\n }\n // MIME Type selector\n reqBodyTypeSelectorHtml = requestBodyTypes.length === 1\n ? ''\n : html`\n <select aria-label=\"mime type\" style=\"min-width:100px; max-width:100%; margin-bottom:-1px;\" @change = '${(e) => this.onMimeTypeChange(e)}'>\n ${requestBodyTypes.map((reqBody) => html`\n <option value = '${reqBody.mimeType}' ?selected = '${reqBody.mimeType === this.selectedRequestBodyType}'>\n ${reqBody.mimeType}\n </option> `)\n }\n </select>\n `;\n\n // For Loop - Main\n requestBodyTypes.forEach((reqBody) => {\n let reqBodyExamples = [];\n\n if (this.selectedRequestBodyType.includes('json') || this.selectedRequestBodyType.includes('xml') || this.selectedRequestBodyType.includes('text')) {\n // Generate Example\n if (reqBody.mimeType === this.selectedRequestBodyType) {\n reqBodyExamples = generateExample(\n reqBody.examples ? reqBody.examples : '',\n reqBody.example ? reqBody.example : '',\n reqBody.schema,\n reqBody.mimeType,\n false,\n true,\n 'text',\n true\n );\n\n if (!this.selectedRequestBodyExample) {\n this.selectedRequestBodyExample = (reqBodyExamples.length > 0 ? reqBodyExamples[0].exampleId : '');\n }\n reqBodyDefaultHtml = html`\n ${reqBodyDefaultHtml}\n <div class = 'example-panel border-top pad-top-8'>\n ${reqBodyExamples.length === 1\n ? ''\n : html`\n <select aria-label='request body example' style=\"min-width:100px; max-width:100%; margin-bottom:-1px;\" @change='${(e) => this.onSelectExample(e)}'>\n ${reqBodyExamples.map((v) => html`<option value=\"${v.exampleId}\" ?selected=${v.exampleId === this.selectedRequestBodyExample} > \n ${v.exampleSummary.length > 80 ? v.exampleId : v.exampleSummary ? v.exampleSummary : v.exampleId} \n </option>`)}\n </select>\n `\n }\n ${reqBodyExamples\n .filter((v) => v.exampleId === this.selectedRequestBodyExample)\n .map((v) => html`\n <div class=\"example ${v.exampleId === this.selectedRequestBodyExample ? 'example-selected' : ''}\" data-default = '${v.exampleId}'>\n ${v.exampleSummary && v.exampleSummary.length > 80 ? html`<div style=\"padding: 4px 0\"> ${v.exampleSummary} </div>` : ''}\n ${v.exampleDescription ? html`<div class=\"m-markdown-small\" style=\"padding: 4px 0\"> ${unsafeHTML(marked(v.exampleDescription || ''))} </div>` : ''}\n <!-- this textarea is for user to edit the example -->\n <slot name=\"${this.elementId}--request-body\">\n <textarea \n class = \"textarea request-body-param-user-input\"\n part = \"textarea textarea-param\"\n spellcheck = \"false\"\n data-ptype = \"${reqBody.mimeType}\" \n data-default = \"${v.exampleFormat === 'text' ? v.exampleValue : JSON.stringify(v.exampleValue, null, 8)}\"\n data-default-format = \"${v.exampleFormat}\"\n style=\"width:100%; resize:vertical;\"\n .value=\"${this.fillRequestWithDefault === 'true' ? (v.exampleFormat === 'text' ? v.exampleValue : JSON.stringify(v.exampleValue, null, 8)) : ''}\"\n ></textarea>\n </slot>\n\n <!-- This textarea(hidden) is to store the original example value, this will remain unchanged when users switches from one example to another, its is used to populate the editable textarea -->\n <textarea \n class = \"textarea is-hidden request-body-param ${reqBody.mimeType.substring(reqBody.mimeType.indexOf('/') + 1)}\" \n spellcheck = \"false\"\n data-ptype = \"${reqBody.mimeType}\" \n style=\"width:100%; resize:vertical; display:none\"\n .value=\"${(v.exampleFormat === 'text' ? v.exampleValue : JSON.stringify(v.exampleValue, null, 8))}\"\n ></textarea>\n </div> \n `)}\n\n </div>\n `;\n }\n } else if (this.selectedRequestBodyType.includes('form-urlencoded') || this.selectedRequestBodyType.includes('form-data')) {\n if (reqBody.mimeType === this.selectedRequestBodyType) {\n const ex = generateExample(\n reqBody.examples ? reqBody.examples : '',\n reqBody.example ? reqBody.example : '',\n reqBody.schema,\n reqBody.mimeType,\n false,\n true,\n 'text',\n true\n );\n if (reqBody.schema) {\n reqBodyFormHtml = this.formDataTemplate(reqBody.schema, reqBody.mimeType, (ex[0] ? ex[0].exampleValue : ''));\n }\n }\n } else if (mediaFileRegex.test(this.selectedRequestBodyType) || textFileRegex.test(this.selectedRequestBodyType)) {\n if (reqBody.mimeType === this.selectedRequestBodyType) {\n reqBodyFileInputHtml = html`\n <div class = \"small-font-size bold-text row\">\n <input type=\"file\" part=\"file-input\" style=\"max-width:100%\" class=\"request-body-param-file\" data-ptype=\"${reqBody.mimeType}\" spellcheck=\"false\" />\n </div> \n `;\n }\n }\n\n // Generate Schema\n if (reqBody.mimeType.includes('json') || reqBody.mimeType.includes('xml') || reqBody.mimeType.includes('text')) {\n const schemaAsObj = schemaInObjectNotation(reqBody.schema, { includeNulls: this.includeNulls });\n if (this.schemaStyle === 'table') {\n reqBodySchemaHtml = html`\n ${reqBodySchemaHtml}\n <schema-table\n class = '${reqBody.mimeType.substring(reqBody.mimeType.indexOf('/') + 1)} pad-top-8'\n style = 'display: ${this.selectedRequestBodyType === reqBody.mimeType ? 'block' : 'none'};'\n .data = '${schemaAsObj}'\n schema-expand-level = \"${this.schemaExpandLevel}\"\n schema-hide-read-only = \"${this.schemaHideReadOnly.includes(this.method)}\"\n schema-hide-write-only = false\n > </schema-table>\n `;\n } else {\n reqBodySchemaHtml = html`\n ${reqBodySchemaHtml}\n <schema-tree\n class = '${reqBody.mimeType.substring(reqBody.mimeType.indexOf('/') + 1)} pad-top-8'\n style = 'display: ${this.selectedRequestBodyType === reqBody.mimeType ? 'block' : 'none'};'\n .data = '${schemaAsObj}'\n schema-expand-level = \"${this.schemaExpandLevel}\"\n schema-hide-read-only = \"${this.schemaHideReadOnly.includes(this.method)}\"\n schema-hide-write-only = false\n > </schema-tree>\n `;\n }\n }\n });\n\n return html`\n <div class='request-body-container' data-selected-request-body-type=\"${this.selectedRequestBodyType}\">\n <div class=\"table-title top-gap row\">\n REQUEST BODY ${this.request_body.required ? html`<span class=\"mono-font\" style='color:var(--red)'>*</span>` : ''} \n <span style = \"font-weight:normal; margin-left:5px\"> ${this.selectedRequestBodyType}</span>\n <span style=\"flex:1\"></span>\n ${reqBodyTypeSelectorHtml}\n </div>\n ${this.request_body.description ? html`<div class=\"m-markdown\" style=\"margin-bottom:12px\">${unsafeHTML(marked(this.request_body.description))}</div>` : ''}\n \n ${(this.selectedRequestBodyType.includes('json') || this.selectedRequestBodyType.includes('xml') || this.selectedRequestBodyType.includes('text'))\n ? html`\n <div class=\"tab-panel col\" style=\"border-width:0 0 1px 0;\">\n <div class=\"tab-buttons row\" @click=\"${(e) => { if (e.target.tagName.toLowerCase() === 'button') { this.activeSchemaTab = e.target.dataset.tab; } }}\">\n <button class=\"tab-btn ${this.activeSchemaTab === 'model' ? 'active' : ''}\" data-tab=\"model\" >${getI18nText('operations.model')}</button>\n <button class=\"tab-btn ${this.activeSchemaTab === 'body' ? 'active' : ''}\" data-tab=\"body\">${getI18nText('operations.body')}</button>\n </div>\n ${html`<div class=\"tab-content col\" style=\"display: ${this.activeSchemaTab === 'model' ? 'block' : 'none'}\"> ${reqBodySchemaHtml}</div>`}\n ${html`<div class=\"tab-content col\" style=\"display: ${this.activeSchemaTab === 'model' ? 'none' : 'block'}\"> ${reqBodyDefaultHtml}</div>`}\n </div>`\n : html` \n ${reqBodyFileInputHtml}\n ${reqBodyFormHtml}`\n }\n </div> \n `;\n }\n\n formDataTemplate(schema, mimeType, exampleValue = '') {\n const formDataTableRows = [];\n if (schema.properties) {\n for (const fieldName in schema.properties) {\n const fieldSchema = schema.properties[fieldName];\n if (fieldSchema.readOnly) {\n continue;\n }\n\n const fieldType = fieldSchema.type;\n const formdataPartSchema = schemaInObjectNotation(fieldSchema, { includeNulls: this.includeNulls });\n const paramSchema = getTypeInfo(fieldSchema, { includeNulls: this.includeNulls });\n const formdataPartExample = generateExample(\n '',\n fieldSchema.example ? fieldSchema.example : '',\n fieldSchema,\n 'json',\n false,\n true,\n 'text',\n true\n );\n\n formDataTableRows.push(html`\n <tr> \n <td style=\"width:160px; min-width:100px;\">\n <div class=\"param-name ${fieldSchema.deprecated ? 'deprecated' : ''}\">\n ${fieldName}${!fieldSchema.deprecated && (schema.required && schema.required.includes(fieldName) || fieldSchema.required) ? html`<span style='color:var(--red);'>*</span>` : ''}\n </div>\n <div class=\"param-type\">\n ${paramSchema.type === 'array' ? html`[<span>${paramSchema.format || paramSchema.type}</span>]` : `${paramSchema.format || paramSchema.type}`}\n </div>\n </td> \n <td \n style=\"${fieldType === 'object' ? 'width:100%; padding:0;' : this.allowTry === 'true' ? '' : 'display:none;'} min-width:100px;\" \n colspan=\"${fieldType === 'object' ? 2 : 1}\">\n ${fieldType === 'array'\n ? (fieldSchema.items && fieldSchema.items.format === 'binary')\n ? html`\n <div class=\"file-input-container col\" style='align-items:flex-end;' @click=\"${(e) => this.onAddRemoveFileInput(e, fieldName, mimeType)}\">\n <div class='input-set row'>\n <input \n type = \"file\"\n part = \"file-input\"\n style = \"width:100%\" \n data-pname = \"${fieldName}\" \n data-ptype = \"${mimeType.includes('form-urlencode') ? 'form-urlencode' : 'form-data'}\"\n data-array = \"false\" \n data-file-array = \"true\" \n />\n <button class=\"file-input-remove-btn\"> &#x2715; </button>\n </div> \n <button class=\"m-btn primary file-input-add-btn\" part=\"btn btn-fill\" style=\"margin:2px 25px 0 0; padding:2px 6px;\">ADD</button>\n </div> \n `\n : html`\n <tag-input\n style = \"width:100%;\" \n data-ptype = \"${mimeType.includes('form-urlencode') ? 'form-urlencode' : 'form-data'}\"\n data-pname = \"${fieldName}\"\n data-default = \"${paramSchema.default || ''}\"\n data-array = \"true\"\n placeholder=\"${(Array.isArray(paramSchema.example) ? paramSchema.example[0] : paramSchema.example) || 'add-multiple ↩'}\"\n .value = \"${paramSchema.default || ''}\"\n >\n </tag-input>\n `\n : html`\n ${fieldType === 'object'\n ? html`\n <div class=\"tab-panel row\" style=\"min-height:300px; border-left: 6px solid var(--light-border-color); align-items: stretch;\">\n <div style=\"width:24px; background-color:var(--light-border-color)\">\n <div class=\"row\" style=\"flex-direction:row-reverse; width:260px; height:24px; transform:rotate(270deg) translateX(-260px); transform-origin:top left; display:block;\" @click=\"${(e) => {\n if (e.target.classList.contains('v-tab-btn')) {\n const tab = e.target.dataset.tab;\n if (tab) {\n const tabPanelEl = e.target.closest('.tab-panel');\n const selectedTabBtnEl = tabPanelEl.querySelector(`.v-tab-btn[data-tab=\"${tab}\"]`);\n const otherTabBtnEl = [...tabPanelEl.querySelectorAll(`.v-tab-btn:not([data-tab=\"${tab}\"])`)];\n const selectedTabContentEl = tabPanelEl.querySelector(`.tab-content[data-tab=\"${tab}\"]`);\n const otherTabContentEl = [...tabPanelEl.querySelectorAll(`.tab-content:not([data-tab=\"${tab}\"])`)];\n selectedTabBtnEl.classList.add('active');\n selectedTabContentEl.style.display = 'block';\n otherTabBtnEl.forEach((el) => { el.classList.remove('active'); });\n otherTabContentEl.forEach((el) => { el.style.display = 'none'; });\n }\n }\n if (e.target.tagName.toLowerCase() === 'button') { this.activeSchemaTab = e.target.dataset.tab; }\n }}\">\n <button class=\"v-tab-btn ${this.activeSchemaTab === 'model' ? 'active' : ''}\" data-tab = 'model'>${getI18nText('operations.model')}</button>\n <button class=\"v-tab-btn ${this.activeSchemaTab === 'body' ? 'active' : ''}\" data-tab = 'body'>${getI18nText('operations.request-body')}</button>\n </div>\n </div> \n ${html`\n <div class=\"tab-content col\" data-tab = 'model' style=\"display:${this.activeSchemaTab === 'model' ? 'block' : 'none'}; padding-left:5px; width:100%;\"> \n <schema-tree\n .data = '${formdataPartSchema}'\n schema-expand-level = \"${this.schemaExpandLevel}\"> </schema-tree>\n </div>`\n }\n ${html`\n <div class=\"tab-content col\" data-tab = 'body' style=\"display:${this.activeSchemaTab === 'body' ? 'block' : 'none'}; padding-left:5px; width:100%\"> \n <textarea \n class = \"textarea\" placeholder=\"${formdataPartExample[0] && formdataPartExample[0].exampleValue || paramSchema.default || ''}\"\n part = \"textarea textarea-param\"\n style = \"width:100%; border:none; resize:vertical;\" \n data-array = \"false\" \n data-ptype = \"${mimeType.includes('form-urlencode') ? 'form-urlencode' : 'form-data'}\"\n data-pname = \"${fieldName}\"\n data-default = \"${paramSchema.default || ''}\"\n spellcheck = \"false\"\n .value=\"${this.fillRequestWithDefault === 'true' ? paramSchema.default : ''}\"\n ></textarea>\n <!-- This textarea(hidden) is to store the original example value, in focused mode on navbar change it is used to update the example text -->\n <textarea data-pname = \"hidden-${fieldName}\" data-ptype = \"${mimeType.includes('form-urlencode') ? 'hidden-form-urlencode' : 'hidden-form-data'}\" class=\"is-hidden\" style=\"display:none\" .value=\"${paramSchema.default}\"></textarea>\n </div>`\n }\n </div>`\n : html`\n ${this.allowTry === 'true'\n ? html`<input placeholder=\"${paramSchema.example || paramSchema.default || ''}\"\n .value = \"${this.fillRequestWithDefault === 'true' ? (paramSchema.default || '') : ''}\"\n spellcheck = \"false\"\n type = \"${fieldSchema.format === 'binary' ? 'file' : fieldSchema.format === 'password' ? 'password' : 'text'}\"\n part = \"textbox textbox-param\"\n style = \"width:100%\"\n data-ptype = \"${mimeType.includes('form-urlencode') ? 'form-urlencode' : 'form-data'}\"\n data-pname = \"${fieldName}\"\n data-default = \"${paramSchema.default || ''}\"\n data-array = \"false\"\n />`\n : ''\n }\n `\n }`\n }\n </td>\n ${fieldType === 'object'\n ? ''\n : html`\n <td>\n ${paramSchema.default || paramSchema.constraint || paramSchema.allowedValues || paramSchema.pattern\n ? html`\n <div class=\"param-constraint\">\n ${paramSchema.pattern ? html`<span style=\"font-weight:bold\">Pattern: </span>${paramSchema.pattern}<br/>` : ''}\n ${paramSchema.constraint ? html`<span style=\"font-weight:bold\">Constraints: </span>${paramSchema.constraint}<br/>` : ''}\n ${paramSchema.allowedValues && paramSchema.allowedValues.split('┃').map((v, i) => html`\n ${i > 0 ? '|' : html`<span style=\"font-weight:bold\">Allowed: </span>`}\n ${html`\n <a part=\"anchor anchor-param-constraint\" class = \"${this.allowTry === 'true' ? '' : 'inactive-link'}\"\n data-type=\"${paramSchema.type === 'array' ? paramSchema.type : 'string'}\"\n data-enum=\"${v.trim()}\"\n @click=\"${(e) => {\n const inputEl = e.target.closest('table').querySelector(`[data-pname=\"${fieldName}\"]`);\n if (inputEl) {\n if (e.target.dataset.type === 'array') {\n inputEl.value = [e.target.dataset.enum];\n } else {\n inputEl.value = e.target.dataset.enum;\n }\n }\n }}\"\n > \n ${v} \n </a>`\n }`)\n }\n </div>`\n : ''\n }\n </td>`\n }\n </tr>\n ${fieldType === 'object'\n ? ''\n : html`\n <tr>\n <td style=\"border:none\"> </td>\n <td colspan=\"2\" style=\"border:none; margin-top:0; padding:0 5px 8px 5px;\"> \n <span class=\"m-markdown-small\">${unsafeHTML(marked(fieldSchema.description || ''))}</span>\n ${paramSchema.example\n ? html`\n <span>\n <span style=\"font-weight:bold\"> Example: </span>\n ${paramSchema.type === 'array' ? '[ ' : ''}\n <a part=\"anchor anchor-param-example\" class = \"${this.allowTry === 'true' ? '' : 'inactive-link'}\"\n data-default-type=\"${paramSchema.type === 'array' ? paramSchema.type : 'string'}\"\n data-default = \"${Array.isArray(paramSchema.example) && paramSchema.example.join('~|~') || paramSchema.example || ''}\"\n @click=\"${(e) => {\n const inputEl = e.target.closest('table').querySelector(`[data-pname=\"${fieldName}\"]`);\n if (inputEl) {\n if (e.target.dataset.exampleType === 'array') {\n inputEl.value = e.target.dataset.example.split('~|~');\n } else {\n inputEl.value = e.target.dataset.example;\n }\n }\n }}\"\n >\n ${paramSchema.type === 'array' ? paramSchema.example.join(', ') : paramSchema.example}\n </a>\n ${paramSchema.type === 'array' ? '] ' : ''}\n </span>`\n : ''\n }\n </td>\n </tr>\n `\n }`);\n }\n return html`\n <table role=\"presentation\" style=\"width:100%;\" class=\"m-table\">\n ${formDataTableRows}\n </table>\n `;\n }\n\n return html`\n <textarea\n class = \"textarea dynamic-form-param ${mimeType}\"\n part = \"textarea textarea-param\"\n spellcheck = \"false\"\n data-pname=\"dynamic-form\" \n data-ptype=\"${mimeType}\" \n style=\"width:100%\"\n >${exampleValue}</textarea>\n ${schema.description ? html`<span class=\"m-markdown-small\">${unsafeHTML(marked(schema.description))}</span>` : ''}\n `;\n }\n\n apiResponseTabTemplate() {\n const responseFormat = this.responseHeaders.includes('json') ? 'json' : (this.responseHeaders.includes('html') || this.responseHeaders.includes('xml')) ? 'html' : '';\n return html`\n <div class=\"row\" style=\"font-size:var(--font-size-small); margin:5px 0\">\n ${this.responseMessage\n ? html`<div class=\"response-message ${this.responseStatus}\">Response Status: ${this.responseMessage}\n ${this.responseElapsedMs ? html`<span><br>Execution Time: ${this.responseElapsedMs}ms</span>` : ''}\n </div>` : ''\n }\n <div style=\"flex:1\"></div>\n <button class=\"m-btn\" part=\"btn btn-outline\" @click=\"${this.clearResponseData}\">CLEAR RESPONSE</button>\n </div>\n <div class=\"tab-panel col\" style=\"border-width:0 0 1px 0;\">\n <div id=\"tab_buttons\" class=\"tab-buttons row\" @click=\"${(e) => {\n if (e.target.classList.contains('tab-btn') === false) { return; }\n this.activeResponseTab = e.target.dataset.tab;\n }}\">\n <button class=\"tab-btn ${this.activeResponseTab === 'response' ? 'active' : ''}\" data-tab = 'response'>${getI18nText('operations.response')}</button>\n <button class=\"tab-btn ${this.activeResponseTab === 'headers' ? 'active' : ''}\" data-tab = 'headers'>${getI18nText('operations.response-headers')}</button>\n <button class=\"tab-btn ${this.activeResponseTab === 'curl' ? 'active' : ''}\" data-tab = 'curl'>CURL</button>\n </div>\n ${this.responseIsBlob\n ? html`\n <div class=\"tab-content col\" style=\"flex:1; display:${this.activeResponseTab === 'response' ? 'flex' : 'none'};\">\n <button class=\"m-btn thin-border mar-top-8\" style=\"width:135px\" @click=\"${this.downloadResponseBlob}\" part=\"btn btn-outline\">DOWNLOAD</button>\n ${this.responseBlobType === 'view'\n ? html`<button class=\"m-btn thin-border mar-top-8\" style=\"width:135px\" @click=\"${this.viewResponseBlob}\" part=\"btn btn-outline\">VIEW (NEW TAB)</button>`\n : ''\n }\n </div>`\n : html`\n <div class=\"tab-content col m-markdown\" style=\"flex:1; display:${this.activeResponseTab === 'response' ? 'flex' : 'none'};\" >\n ${this.responseText\n ? html`<button class=\"m-btn outline-primary toolbar-copy-btn\" @click='${(e) => { copyToClipboard(this.responseText, e); }}' part=\"btn btn-fill\">${getI18nText('operations.copy')}</button>`\n : ''\n }\n <pre style=\"min-height: 60px\">${responseFormat\n ? html`<code>${unsafeHTML(Prism.highlight(this.responseText, Prism.languages[responseFormat], responseFormat))}</code>`\n : `${this.responseText}`\n }\n </pre>\n </div>`\n }\n <div class=\"tab-content col m-markdown\" style=\"flex:1;display:${this.activeResponseTab === 'headers' ? 'flex' : 'none'};\" >\n <button class=\"m-btn outline-primary toolbar-copy-btn\" @click='${(e) => { copyToClipboard(this.responseHeaders, e); }}' part=\"btn btn-fill\">${getI18nText('operations.copy')}</button>\n <pre><code>${unsafeHTML(Prism.highlight(this.responseHeaders, Prism.languages.css, 'css'))}</code></pre>\n </div>\n <div class=\"tab-content col m-markdown\" style=\"flex:1;display:${this.activeResponseTab === 'curl' ? 'flex' : 'none'};\">\n <button class=\"m-btn outline-primary toolbar-copy-btn\" @click='${(e) => { copyToClipboard(this.curlSyntax, e); }}' part=\"btn btn-fill\">${getI18nText('operations.copy')}</button>\n <pre><code>${unsafeHTML(Prism.highlight(this.curlSyntax.trim(), Prism.languages.shell, 'shell'))}</code></pre>\n </div>\n </div>`;\n }\n\n apiCallTemplate() {\n return html`\n <div style=\"display:flex; align-items:flex-end; margin:16px 0; font-size:var(--font-size-small);\">\n ${\n this.parameters.length > 0 || this.request_body\n ? html`\n <button class=\"m-btn thin-border\" part=\"btn btn-outline\" style=\"margin-right:5px;\" @click=\"${this.onClearRequestData}\">\n ${getI18nText('operations.clear')}\n </button>`\n : ''\n }\n <button class=\"m-btn primary btn-execute thin-border\" part=\"btn btn-fill btn-try\" @click=\"${this.onTryClick}\">${getI18nText('operations.execute')}</button>\n </div>\n ${this.responseMessage === '' ? '' : this.apiResponseTabTemplate()}\n `;\n }\n /* eslint-enable indent */\n\n onClearRequestData(e) {\n const requestPanelEl = e.target.closest('.request-panel');\n const requestPanelInputEls = [...requestPanelEl.querySelectorAll('input, tag-input, textarea:not(.is-hidden)')];\n requestPanelInputEls.forEach((el) => { el.value = ''; });\n\n const event = { bubbles: true, composed: true, detail: { explorerLocation: this.elementId, type: 'RequestCleared' } };\n this.dispatchEvent(new CustomEvent('event', event));\n }\n\n async onTryClick() {\n const tryBtnEl = this.querySelectorAll('.btn-execute')[0];\n let curlData = '';\n let curlForm = '';\n const closestRespContainer = this.closest('.expanded-req-resp-container, .req-resp-container');\n const respEl = closestRespContainer && closestRespContainer.getElementsByTagName('api-response')[0];\n const acceptHeader = respEl?.selectedMimeType;\n const requestPanelEl = this.closest('.request-panel');\n const pathParamEls = [...requestPanelEl.querySelectorAll(\"[data-ptype='path']\")];\n const queryParamEls = [...requestPanelEl.querySelectorAll(\"[data-ptype='query']\")];\n const queryParamObjTypeEls = [...requestPanelEl.querySelectorAll(\"[data-ptype='query-object']\")];\n const headerParamEls = [...requestPanelEl.querySelectorAll(\"[data-ptype='header']\")];\n const requestBodyContainerEl = requestPanelEl.querySelector('.request-body-container');\n\n let pathUrl = `${this.serverUrl.replace(/\\/$/, '')}${this.path.replaceAll(' ', '')}`;\n\n // Generate URL using Path Params\n pathParamEls.map((el) => {\n pathUrl = pathUrl.replace(`{${el.dataset.pname}}`, encodeURIComponent(el.value) || '-');\n });\n\n // Handle relative serverUrls\n if (!pathUrl.startsWith('http')) {\n const newUrl = new URL(pathUrl, window.location.href);\n pathUrl = newUrl.toString();\n }\n\n const fetchUrl = new URL(pathUrl);\n\n const fetchOptions = {\n method: this.method.toUpperCase(),\n headers: new Headers()\n };\n\n // Query Params\n if (queryParamEls.length > 0) {\n queryParamEls.forEach((el) => {\n if (el.dataset.array === 'false') {\n if (el.value !== '') {\n fetchUrl.searchParams.append(el.dataset.pname, el.value);\n }\n } else {\n const paramSerializeStyle = el.dataset.paramSerializeStyle;\n const paramSerializeExplode = el.dataset.paramSerializeExplode;\n let vals = ((el.value && Array.isArray(el.value)) ? el.value : []);\n vals = Array.isArray(vals) ? vals.filter((v) => v !== '') : [];\n if (vals.length > 0) {\n if (paramSerializeStyle === 'spaceDelimited') {\n fetchUrl.searchParams.append(el.dataset.pname, vals.join(' ').replace(/^\\s|\\s$/g, ''));\n } else if (paramSerializeStyle === 'pipeDelimited') {\n fetchUrl.searchParams.append(el.dataset.pname, vals.join('|').replace(/^\\||\\|$/g, ''));\n } else {\n if (paramSerializeExplode === 'true') { // eslint-disable-line no-lonely-if\n vals.forEach((v) => { fetchUrl.searchParams.append(el.dataset.pname, v); });\n } else {\n fetchUrl.searchParams.append(el.dataset.pname, vals.join(',').replace(/^,|,$/g, ''));\n }\n }\n }\n }\n });\n }\n\n // Query Params (Dynamic - create from JSON)\n if (queryParamObjTypeEls.length > 0) {\n queryParamObjTypeEls.map((el) => {\n try {\n let queryParamObj = {};\n const paramSerializeStyle = el.dataset.paramSerializeStyle;\n const paramSerializeExplode = el.dataset.paramSerializeExplode;\n queryParamObj = Object.assign(queryParamObj, JSON.parse(el.value.replace(/\\s+/g, ' ')));\n for (const key in queryParamObj) {\n if (typeof queryParamObj[key] === 'object') {\n if (Array.isArray(queryParamObj[key])) {\n if (paramSerializeStyle === 'spaceDelimited') {\n fetchUrl.searchParams.append(key, queryParamObj[key].join(' '));\n } else if (paramSerializeStyle === 'pipeDelimited') {\n fetchUrl.searchParams.append(key, queryParamObj[key].join('|'));\n } else {\n if (paramSerializeExplode === 'true') { // eslint-disable-line no-lonely-if\n queryParamObj[key].forEach((v) => {\n fetchUrl.searchParams.append(key, v);\n });\n } else {\n fetchUrl.searchParams.append(key, queryParamObj[key]);\n }\n }\n }\n } else {\n fetchUrl.searchParams.append(key, queryParamObj[key]);\n }\n }\n } catch (err) {\n console.log('OpenAPI Explorer: unable to parse %s into object', el.value); // eslint-disable-line no-console\n }\n });\n }\n\n // Add Authentication api keys if provided\n this.api_keys.filter((v) => v.finalKeyValue).forEach((v) => {\n if (v.in === 'query') {\n fetchUrl.searchParams.append(v.name, v.finalKeyValue);\n return;\n }\n\n // Otherwise put it in the header\n fetchOptions.headers.append(v.name, v.finalKeyValue);\n });\n\n if (acceptHeader) {\n // Uses the acceptHeader from Response panel\n fetchOptions.headers.append('Accept', acceptHeader);\n } else if (this.accept) {\n fetchOptions.headers.append('Accept', this.accept);\n }\n\n // Add Header Params\n headerParamEls.map((el) => {\n if (el.value) {\n fetchOptions.headers.append(el.dataset.pname, el.value);\n }\n });\n\n // Request Body Params\n if (requestBodyContainerEl) {\n const requestBodyType = requestBodyContainerEl.dataset.selectedRequestBodyType;\n if (requestBodyType.includes('form-urlencoded')) {\n // url-encoded Form Params (dynamic) - Parse JSON and generate Params\n const formUrlDynamicTextAreaEl = requestPanelEl.querySelector(\"[data-ptype='dynamic-form']\");\n if (formUrlDynamicTextAreaEl) {\n const val = formUrlDynamicTextAreaEl.value;\n const formUrlDynParams = new URLSearchParams();\n let proceed = true;\n let tmpObj;\n if (val) {\n try {\n tmpObj = JSON.parse(val);\n } catch (err) {\n proceed = false;\n console.warn('OpenAPI Explorer: Invalid JSON provided', err); // eslint-disable-line no-console\n }\n } else {\n proceed = false;\n }\n if (proceed) {\n for (const prop in tmpObj) {\n formUrlDynParams.append(prop, JSON.stringify(tmpObj[prop]));\n }\n fetchOptions.body = formUrlDynParams;\n curlData = ` \\\\\\n -d ${formUrlDynParams.toString()}`;\n }\n } else {\n // url-encoded Form Params (regular)\n const formUrlEls = [...requestPanelEl.querySelectorAll(\"[data-ptype='form-urlencode']\")];\n const formUrlParams = new URLSearchParams();\n formUrlEls\n .filter((v) => (v.type !== 'file'))\n .forEach((el) => {\n if (el.dataset.array === 'false') {\n if (el.value) {\n formUrlParams.append(el.dataset.pname, el.value);\n }\n } else {\n const vals = (el.value && Array.isArray(el.value)) ? el.value.join(',') : '';\n formUrlParams.append(el.dataset.pname, vals);\n }\n });\n fetchOptions.body = formUrlParams;\n curlData = ` \\\\\\n -d ${formUrlParams.toString()}`;\n }\n } else if (requestBodyType.includes('form-data')) {\n const formDataParams = new FormData();\n const formDataEls = [...requestPanelEl.querySelectorAll(\"[data-ptype='form-data']\")];\n formDataEls.forEach((el) => {\n if (el.dataset.array === 'false') {\n if (el.type === 'file' && el.files[0]) {\n formDataParams.append(el.dataset.pname, el.files[0], el.files[0].name);\n curlForm += ` \\\\\\n -F \"${el.dataset.pname}=@${el.files[0].name}\"`;\n } else if (el.value) {\n formDataParams.append(el.dataset.pname, el.value);\n curlForm += ` \\\\\\n -F \"${el.dataset.pname}=${el.value}\"`;\n }\n } else if (el.value && Array.isArray(el.value)) {\n el.value.forEach((v) => {\n curlForm += ` \\\\\\n -F \"${el.dataset.pname}[]=${v}\"`;\n });\n formDataParams.append(el.dataset.pname, el.value.join(','));\n }\n });\n fetchOptions.body = formDataParams;\n } else if (mediaFileRegex.test(requestBodyType) || textFileRegex.test(requestBodyType)) {\n const bodyParamFileEl = requestPanelEl.querySelector('.request-body-param-file');\n if (bodyParamFileEl && bodyParamFileEl.files[0]) {\n fetchOptions.body = bodyParamFileEl.files[0];\n curlData = ` \\\\\\n --data-binary @${bodyParamFileEl.files[0].name}`;\n }\n } else if (requestBodyType.includes('json') || requestBodyType.includes('xml') || requestBodyType.includes('text')) {\n const exampleTextAreaEl = requestPanelEl.querySelector('.request-body-param-user-input');\n if (exampleTextAreaEl && exampleTextAreaEl.value) {\n fetchOptions.body = exampleTextAreaEl.value;\n if (requestBodyType.includes('json')) {\n try {\n curlData = ` \\\\\\n -d '${JSON.stringify(JSON.parse(exampleTextAreaEl.value))}'`;\n } catch (err) { /* Ignore unparseable JSON */ }\n }\n\n if (!curlData) {\n // Save single quotes wrapped => 'text' => `\"'\"text\"'\"`\n curlData = ` \\\\\\n -d '${exampleTextAreaEl.value.replace(/'/g, '\\'\"\\'\"\\'')}'`;\n }\n }\n }\n // Common for all request-body\n if (!requestBodyType.includes('form-data')) {\n // For multipart/form-data don't set the content-type to allow creation of browser generated part boundaries\n fetchOptions.headers.append('Content-Type', requestBodyType);\n }\n }\n\n this.responseIsBlob = false;\n this.respContentDisposition = '';\n if (this.responseBlobUrl) {\n URL.revokeObjectURL(this.responseBlobUrl);\n this.responseBlobUrl = '';\n }\n\n if (this.fetchCredentials) {\n fetchOptions.credentials = this.fetchCredentials;\n }\n\n // Options is legacy usage, documentation has been updated to reference properties of the fetch option directly, but older usages may still be using options\n const fetchRequest = { explorerLocation: this.elementId, url: fetchUrl.toString(), options: fetchOptions, ...fetchOptions };\n const event = {\n bubbles: true,\n composed: true,\n detail: {\n request: fetchRequest,\n },\n };\n this.dispatchEvent(new CustomEvent('before-try', event));\n this.dispatchEvent(new CustomEvent('request', event));\n const newFetchOptions = {\n method: fetchRequest.method || fetchOptions.method,\n headers: fetchRequest.headers || fetchOptions.headers,\n credentials: fetchRequest.credentials || fetchOptions.credentials,\n body: fetchRequest.body || fetchOptions.body\n };\n const fetchRequestObject = new Request(fetchRequest.url, newFetchOptions);\n\n const curl = `curl -X ${this.method.toUpperCase()} \"${fetchUrl.toString()}\"`;\n const curlHeaders = [...newFetchOptions.headers.entries()].reduce((acc, [key, value]) => `${acc} \\\\\\n -H \"${key}: ${value}\"`, '');\n this.curlSyntax = `${curl}${curlHeaders}${curlData}${curlForm}`;\n\n let fetchResponse;\n try {\n let respBlob;\n let respJson;\n let respText;\n tryBtnEl.disabled = true;\n const fetchStart = new Date();\n\n this.responseStatus = '';\n this.responseMessage = '';\n this.responseUrl = '';\n this.responseHeaders = '';\n this.responseText = '⌛';\n\n this.requestUpdate();\n const awaiter = new Promise(resolve => setTimeout(resolve, 200));\n fetchResponse = await fetch(fetchRequestObject);\n this.responseElapsedMs = new Date() - fetchStart;\n await awaiter;\n\n tryBtnEl.disabled = false;\n this.responseStatus = fetchResponse.ok ? 'success' : 'error';\n this.responseMessage = fetchResponse.statusText ? `${fetchResponse.statusText} (${fetchResponse.status})` : fetchResponse.status;\n this.responseUrl = fetchResponse.url;\n this.responseHeaders = '';\n const headers = {};\n fetchResponse.headers.forEach((hdrVal, hdr) => {\n this.responseHeaders = `${this.responseHeaders}${hdr.trim()}: ${hdrVal}\\n`;\n headers[hdr.trim()] = hdrVal && hdrVal.trim();\n });\n const contentType = fetchResponse.headers.get('content-type');\n const respEmpty = (await fetchResponse.clone().text()).length === 0;\n if (respEmpty) {\n this.responseText = '';\n } else if (contentType) {\n if (contentType.includes('json')) {\n if ((/charset=[^\"']+/).test(contentType)) {\n const encoding = contentType.split('charset=')[1];\n const buffer = await fetchResponse.arrayBuffer();\n try {\n respText = new TextDecoder(encoding).decode(buffer);\n } catch {\n respText = new TextDecoder('utf-8').decode(buffer);\n }\n try {\n this.responseText = JSON.stringify(JSON.parse(respText), null, 8);\n } catch {\n this.responseText = respText;\n }\n } else {\n respJson = await fetchResponse.json();\n this.responseText = JSON.stringify(respJson, null, 8);\n }\n } else if (textFileRegex.test(contentType)) {\n this.responseIsBlob = true;\n this.responseBlobType = 'download';\n } else if (mediaFileRegex.test(contentType)) {\n this.responseIsBlob = true;\n this.responseBlobType = 'view';\n } else {\n respText = await fetchResponse.text();\n if (contentType.includes('xml')) {\n this.responseText = formatXml(respText, { textNodesOnSameLine: true, indentor: ' ' });\n } else {\n this.responseText = respText;\n }\n }\n if (this.responseIsBlob) {\n const contentDisposition = fetchResponse.headers.get('content-disposition');\n const filenameRegex = /filename[^;=\\n]*=((['\"]).*?\\2|[^;\\n]*)/;\n const filename = filenameRegex.exec(contentDisposition);\n this.respContentDisposition = filename && filename[1] && filename[1].replace(/['\"]/g, '') || `download.${mimeTypeResolver.extension(contentType) || 'file'}`;\n respBlob = await fetchResponse.blob();\n this.responseBlobUrl = URL.createObjectURL(respBlob);\n }\n } else {\n respText = await fetchResponse.text();\n this.responseText = respText;\n }\n const responseEvent = {\n bubbles: true,\n composed: true,\n detail: {\n explorerLocation: this.elementId,\n request: fetchRequest,\n response: {\n headers,\n body: respJson || respText || respBlob || fetchResponse.body,\n status: fetchResponse.status,\n },\n },\n };\n this.dispatchEvent(new CustomEvent('after-try', responseEvent));\n this.dispatchEvent(new CustomEvent('response', responseEvent));\n } catch (error) {\n tryBtnEl.disabled = false;\n this.responseMessage = `${error.message} (Check the browser network tab for more information.)`;\n this.responseStatus = 'error';\n const responseEvent = {\n bubbles: true,\n composed: true,\n detail: {\n explorerLocation: this.elementId,\n error,\n request: fetchRequest,\n },\n };\n document.dispatchEvent(new CustomEvent('after-try', responseEvent));\n document.dispatchEvent(new CustomEvent('response', responseEvent));\n }\n }\n\n onAddRemoveFileInput(e, pname, ptype) {\n if (e.target.tagName.toLowerCase() !== 'button') {\n return;\n }\n\n if (e.target.classList.contains('file-input-remove-btn')) {\n // Remove File Input Set\n const el = e.target.closest('.input-set');\n el.remove();\n return;\n }\n const el = e.target.closest('.file-input-container');\n\n // Add File Input Set\n\n // Container\n const newInputContainerEl = document.createElement('div');\n newInputContainerEl.setAttribute('class', 'input-set row');\n\n // File Input\n const newInputEl = document.createElement('input');\n newInputEl.type = 'file';\n newInputEl.style = 'width:200px; margin-top:2px;';\n newInputEl.setAttribute('data-pname', pname);\n newInputEl.setAttribute('data-ptype', ptype.includes('form-urlencode') ? 'form-urlencode' : 'form-data');\n newInputEl.setAttribute('data-array', 'false');\n newInputEl.setAttribute('data-file-array', 'true');\n\n // Remover Button\n const newRemoveBtnEl = document.createElement('button');\n newRemoveBtnEl.setAttribute('class', 'file-input-remove-btn');\n newRemoveBtnEl.innerHTML = '&#x2715;';\n\n newInputContainerEl.appendChild(newInputEl);\n newInputContainerEl.appendChild(newRemoveBtnEl);\n el.insertBefore(newInputContainerEl, e.target);\n // el.appendChild(newInputContainerEl);\n }\n\n downloadResponseBlob() {\n if (this.responseBlobUrl) {\n const a = document.createElement('a');\n document.body.appendChild(a);\n a.style = 'display: none';\n a.href = this.responseBlobUrl;\n a.download = this.respContentDisposition;\n a.click();\n a.remove();\n }\n }\n\n viewResponseBlob() {\n if (this.responseBlobUrl) {\n const a = document.createElement('a');\n document.body.appendChild(a);\n a.style = 'display: none';\n a.href = this.responseBlobUrl;\n a.target = '_blank';\n a.click();\n a.remove();\n }\n }\n\n clearResponseData() {\n this.responseUrl = '';\n this.responseHeaders = '';\n this.responseText = '';\n this.responseStatus = '';\n this.responseMessage = '';\n this.responseElapsedMs = 0;\n this.responseIsBlob = false;\n this.responseBlobType = '';\n this.respContentDisposition = '';\n if (this.responseBlobUrl) {\n URL.revokeObjectURL(this.responseBlobUrl);\n this.responseBlobUrl = '';\n }\n }\n\n requestParamFunction(event) {\n if (event.key === 'Enter') {\n this.onTryClick();\n event.preventDefault();\n }\n }\n\n disconnectedCallback() {\n // Cleanup ObjectURL forthe blob data if this component created one\n if (this.responseBlobUrl) {\n URL.revokeObjectURL(this.responseBlobUrl);\n this.responseBlobUrl = '';\n }\n super.disconnectedCallback();\n }\n}\n\n// Register the element with the browser\nif (!customElements.get('openapi-explorer')) {\n customElements.define('api-request', ApiRequest);\n}\n"],"names":[],"sourceRoot":""}
@@ -446,8 +446,8 @@ export default class ApiRequest extends LitElement {
446
446
  </pre> </div>`} <div class="tab-content col m-markdown" style="flex:1;display:${this.activeResponseTab === 'headers' ? 'flex' : 'none'}"> <button class="m-btn outline-primary toolbar-copy-btn" @click="${e => {
447
447
  copyToClipboard(this.responseHeaders, e);
448
448
  }}" part="btn btn-fill">${getI18nText('operations.copy')}</button> <pre><code>${unsafeHTML(Prism.highlight(this.responseHeaders, Prism.languages.css, 'css'))}</code></pre> </div> <div class="tab-content col m-markdown" style="flex:1;display:${this.activeResponseTab === 'curl' ? 'flex' : 'none'}"> <button class="m-btn outline-primary toolbar-copy-btn" @click="${e => {
449
- copyToClipboard(this.curlSyntax.replace(/\\$/, ''), e);
450
- }}" part="btn btn-fill">${getI18nText('operations.copy')}</button> <pre><code>${unsafeHTML(Prism.highlight(this.curlSyntax.trim().replace(/\\$/, ''), Prism.languages.shell, 'shell'))}</code></pre> </div> </div>`;
449
+ copyToClipboard(this.curlSyntax, e);
450
+ }}" part="btn btn-fill">${getI18nText('operations.copy')}</button> <pre><code>${unsafeHTML(Prism.highlight(this.curlSyntax.trim(), Prism.languages.shell, 'shell'))}</code></pre> </div> </div>`;
451
451
  }
452
452
 
453
453
  apiCallTemplate() {
@@ -475,38 +475,39 @@ export default class ApiRequest extends LitElement {
475
475
 
476
476
  async onTryClick() {
477
477
  const tryBtnEl = this.querySelectorAll('.btn-execute')[0];
478
- let fetchUrl;
479
- let curlUrl;
480
- let curl = '';
481
- let curlHeaders = '';
482
478
  let curlData = '';
483
479
  let curlForm = '';
484
480
  const closestRespContainer = this.closest('.expanded-req-resp-container, .req-resp-container');
485
481
  const respEl = closestRespContainer && closestRespContainer.getElementsByTagName('api-response')[0];
486
- const acceptHeader = respEl && respEl.selectedMimeType;
482
+ const acceptHeader = respEl === null || respEl === void 0 ? void 0 : respEl.selectedMimeType;
487
483
  const requestPanelEl = this.closest('.request-panel');
488
484
  const pathParamEls = [...requestPanelEl.querySelectorAll("[data-ptype='path']")];
489
485
  const queryParamEls = [...requestPanelEl.querySelectorAll("[data-ptype='query']")];
490
486
  const queryParamObjTypeEls = [...requestPanelEl.querySelectorAll("[data-ptype='query-object']")];
491
487
  const headerParamEls = [...requestPanelEl.querySelectorAll("[data-ptype='header']")];
492
488
  const requestBodyContainerEl = requestPanelEl.querySelector('.request-body-container');
493
- fetchUrl = this.path.replaceAll(' ', '');
494
- const fetchOptions = {
495
- method: this.method.toUpperCase(),
496
- headers: new Headers()
497
- }; // Generate URL using Path Params
489
+ let pathUrl = `${this.serverUrl.replace(/\/$/, '')}${this.path.replaceAll(' ', '')}`; // Generate URL using Path Params
498
490
 
499
491
  pathParamEls.map(el => {
500
- fetchUrl = fetchUrl.replace(`{${el.dataset.pname}}`, encodeURIComponent(el.value));
501
- }); // Query Params
492
+ pathUrl = pathUrl.replace(`{${el.dataset.pname}}`, encodeURIComponent(el.value) || '-');
493
+ }); // Handle relative serverUrls
502
494
 
503
- const urlQueryParam = new URLSearchParams();
495
+ if (!pathUrl.startsWith('http')) {
496
+ const newUrl = new URL(pathUrl, window.location.href);
497
+ pathUrl = newUrl.toString();
498
+ }
499
+
500
+ const fetchUrl = new URL(pathUrl);
501
+ const fetchOptions = {
502
+ method: this.method.toUpperCase(),
503
+ headers: new Headers()
504
+ }; // Query Params
504
505
 
505
506
  if (queryParamEls.length > 0) {
506
507
  queryParamEls.forEach(el => {
507
508
  if (el.dataset.array === 'false') {
508
509
  if (el.value !== '') {
509
- urlQueryParam.append(el.dataset.pname, el.value);
510
+ fetchUrl.searchParams.append(el.dataset.pname, el.value);
510
511
  }
511
512
  } else {
512
513
  const paramSerializeStyle = el.dataset.paramSerializeStyle;
@@ -516,17 +517,17 @@ export default class ApiRequest extends LitElement {
516
517
 
517
518
  if (vals.length > 0) {
518
519
  if (paramSerializeStyle === 'spaceDelimited') {
519
- urlQueryParam.append(el.dataset.pname, vals.join(' ').replace(/^\s|\s$/g, ''));
520
+ fetchUrl.searchParams.append(el.dataset.pname, vals.join(' ').replace(/^\s|\s$/g, ''));
520
521
  } else if (paramSerializeStyle === 'pipeDelimited') {
521
- urlQueryParam.append(el.dataset.pname, vals.join('|').replace(/^\||\|$/g, ''));
522
+ fetchUrl.searchParams.append(el.dataset.pname, vals.join('|').replace(/^\||\|$/g, ''));
522
523
  } else {
523
524
  if (paramSerializeExplode === 'true') {
524
525
  // eslint-disable-line no-lonely-if
525
526
  vals.forEach(v => {
526
- urlQueryParam.append(el.dataset.pname, v);
527
+ fetchUrl.searchParams.append(el.dataset.pname, v);
527
528
  });
528
529
  } else {
529
- urlQueryParam.append(el.dataset.pname, vals.join(',').replace(/^,|,$/g, ''));
530
+ fetchUrl.searchParams.append(el.dataset.pname, vals.join(',').replace(/^,|,$/g, ''));
530
531
  }
531
532
  }
532
533
  }
@@ -547,22 +548,22 @@ export default class ApiRequest extends LitElement {
547
548
  if (typeof queryParamObj[key] === 'object') {
548
549
  if (Array.isArray(queryParamObj[key])) {
549
550
  if (paramSerializeStyle === 'spaceDelimited') {
550
- urlQueryParam.append(key, queryParamObj[key].join(' '));
551
+ fetchUrl.searchParams.append(key, queryParamObj[key].join(' '));
551
552
  } else if (paramSerializeStyle === 'pipeDelimited') {
552
- urlQueryParam.append(key, queryParamObj[key].join('|'));
553
+ fetchUrl.searchParams.append(key, queryParamObj[key].join('|'));
553
554
  } else {
554
555
  if (paramSerializeExplode === 'true') {
555
556
  // eslint-disable-line no-lonely-if
556
557
  queryParamObj[key].forEach(v => {
557
- urlQueryParam.append(key, v);
558
+ fetchUrl.searchParams.append(key, v);
558
559
  });
559
560
  } else {
560
- urlQueryParam.append(key, queryParamObj[key]);
561
+ fetchUrl.searchParams.append(key, queryParamObj[key]);
561
562
  }
562
563
  }
563
564
  }
564
565
  } else {
565
- urlQueryParam.append(key, queryParamObj[key]);
566
+ fetchUrl.searchParams.append(key, queryParamObj[key]);
566
567
  }
567
568
  }
568
569
  } catch (err) {
@@ -574,41 +575,25 @@ export default class ApiRequest extends LitElement {
574
575
 
575
576
  this.api_keys.filter(v => v.finalKeyValue).forEach(v => {
576
577
  if (v.in === 'query') {
577
- urlQueryParam.append(v.name, v.finalKeyValue);
578
+ fetchUrl.searchParams.append(v.name, v.finalKeyValue);
578
579
  return;
579
580
  } // Otherwise put it in the header
580
581
 
581
582
 
582
583
  fetchOptions.headers.append(v.name, v.finalKeyValue);
583
- curlHeaders += ` -H "${v.name}: ${v.finalKeyValue}" \\\n`;
584
584
  });
585
- fetchUrl = `${fetchUrl}${urlQueryParam.toString() ? '?' : ''}${urlQueryParam.toString()}`; // Final URL for API call
586
-
587
- fetchUrl = `${this.serverUrl.replace(/\/$/, '')}${fetchUrl}`;
588
-
589
- if (fetchUrl.startsWith('http') === false) {
590
- const url = new URL(fetchUrl, window.location.href);
591
- curlUrl = url.href;
592
- } else {
593
- curlUrl = fetchUrl;
594
- }
595
-
596
- curl = `curl -X ${this.method.toUpperCase()} "${curlUrl}" \\\n`;
597
585
 
598
586
  if (acceptHeader) {
599
587
  // Uses the acceptHeader from Response panel
600
588
  fetchOptions.headers.append('Accept', acceptHeader);
601
- curlHeaders += ` -H "Accept: ${acceptHeader}" \\\n`;
602
589
  } else if (this.accept) {
603
590
  fetchOptions.headers.append('Accept', this.accept);
604
- curlHeaders += ` -H "Accept: ${this.accept}" \\\n`;
605
591
  } // Add Header Params
606
592
 
607
593
 
608
594
  headerParamEls.map(el => {
609
595
  if (el.value) {
610
596
  fetchOptions.headers.append(el.dataset.pname, el.value);
611
- curlHeaders += ` -H "${el.dataset.pname}: ${el.value}" \\\n`;
612
597
  }
613
598
  }); // Request Body Params
614
599
 
@@ -642,7 +627,7 @@ export default class ApiRequest extends LitElement {
642
627
  }
643
628
 
644
629
  fetchOptions.body = formUrlDynParams;
645
- curlData = ` -d ${formUrlDynParams.toString()} \\\n`;
630
+ curlData = ` \\\n -d ${formUrlDynParams.toString()}`;
646
631
  }
647
632
  } else {
648
633
  // url-encoded Form Params (regular)
@@ -659,7 +644,7 @@ export default class ApiRequest extends LitElement {
659
644
  }
660
645
  });
661
646
  fetchOptions.body = formUrlParams;
662
- curlData = ` -d ${formUrlParams.toString()} \\\n`;
647
+ curlData = ` \\\n -d ${formUrlParams.toString()}`;
663
648
  }
664
649
  } else if (requestBodyType.includes('form-data')) {
665
650
  const formDataParams = new FormData();
@@ -668,14 +653,14 @@ export default class ApiRequest extends LitElement {
668
653
  if (el.dataset.array === 'false') {
669
654
  if (el.type === 'file' && el.files[0]) {
670
655
  formDataParams.append(el.dataset.pname, el.files[0], el.files[0].name);
671
- curlForm += ` -F "${el.dataset.pname}=@${el.files[0].name}" \\\n`;
656
+ curlForm += ` \\\n -F "${el.dataset.pname}=@${el.files[0].name}"`;
672
657
  } else if (el.value) {
673
658
  formDataParams.append(el.dataset.pname, el.value);
674
- curlForm += ` -F "${el.dataset.pname}=${el.value}" \\\n`;
659
+ curlForm += ` \\\n -F "${el.dataset.pname}=${el.value}"`;
675
660
  }
676
661
  } else if (el.value && Array.isArray(el.value)) {
677
662
  el.value.forEach(v => {
678
- curlForm = `${curlForm} -F "${el.dataset.pname}[]=${v}" \\\n`;
663
+ curlForm += ` \\\n -F "${el.dataset.pname}[]=${v}"`;
679
664
  });
680
665
  formDataParams.append(el.dataset.pname, el.value.join(','));
681
666
  }
@@ -686,7 +671,7 @@ export default class ApiRequest extends LitElement {
686
671
 
687
672
  if (bodyParamFileEl && bodyParamFileEl.files[0]) {
688
673
  fetchOptions.body = bodyParamFileEl.files[0];
689
- curlData = ` --data-binary @${bodyParamFileEl.files[0].name} \\\n`;
674
+ curlData = ` \\\n --data-binary @${bodyParamFileEl.files[0].name}`;
690
675
  }
691
676
  } else if (requestBodyType.includes('json') || requestBodyType.includes('xml') || requestBodyType.includes('text')) {
692
677
  const exampleTextAreaEl = requestPanelEl.querySelector('.request-body-param-user-input');
@@ -696,7 +681,7 @@ export default class ApiRequest extends LitElement {
696
681
 
697
682
  if (requestBodyType.includes('json')) {
698
683
  try {
699
- curlData = ` -d '${JSON.stringify(JSON.parse(exampleTextAreaEl.value))}' \\\n`;
684
+ curlData = ` \\\n -d '${JSON.stringify(JSON.parse(exampleTextAreaEl.value))}'`;
700
685
  } catch (err) {
701
686
  /* Ignore unparseable JSON */
702
687
  }
@@ -704,7 +689,7 @@ export default class ApiRequest extends LitElement {
704
689
 
705
690
  if (!curlData) {
706
691
  // Save single quotes wrapped => 'text' => `"'"text"'"`
707
- curlData = ` -d '${exampleTextAreaEl.value.replace(/'/g, '\'"\'"\'')}' \\\n`;
692
+ curlData = ` \\\n -d '${exampleTextAreaEl.value.replace(/'/g, '\'"\'"\'')}'`;
708
693
  }
709
694
  }
710
695
  } // Common for all request-body
@@ -714,11 +699,8 @@ export default class ApiRequest extends LitElement {
714
699
  // For multipart/form-data don't set the content-type to allow creation of browser generated part boundaries
715
700
  fetchOptions.headers.append('Content-Type', requestBodyType);
716
701
  }
717
-
718
- curlHeaders += ` -H "Content-Type: ${requestBodyType}" \\\n`;
719
702
  }
720
703
 
721
- this.curlSyntax = '';
722
704
  this.responseIsBlob = false;
723
705
  this.respContentDisposition = '';
724
706
 
@@ -727,8 +709,6 @@ export default class ApiRequest extends LitElement {
727
709
  this.responseBlobUrl = '';
728
710
  }
729
711
 
730
- this.curlSyntax = `${curl}${curlHeaders}${curlData}${curlForm}`;
731
-
732
712
  if (this.fetchCredentials) {
733
713
  fetchOptions.credentials = this.fetchCredentials;
734
714
  } // Options is legacy usage, documentation has been updated to reference properties of the fetch option directly, but older usages may still be using options
@@ -736,7 +716,7 @@ export default class ApiRequest extends LitElement {
736
716
 
737
717
  const fetchRequest = {
738
718
  explorerLocation: this.elementId,
739
- url: fetchUrl,
719
+ url: fetchUrl.toString(),
740
720
  options: fetchOptions,
741
721
  ...fetchOptions
742
722
  };
@@ -756,6 +736,9 @@ export default class ApiRequest extends LitElement {
756
736
  body: fetchRequest.body || fetchOptions.body
757
737
  };
758
738
  const fetchRequestObject = new Request(fetchRequest.url, newFetchOptions);
739
+ const curl = `curl -X ${this.method.toUpperCase()} "${fetchUrl.toString()}"`;
740
+ const curlHeaders = [...newFetchOptions.headers.entries()].reduce((acc, [key, value]) => `${acc} \\\n -H "${key}: ${value}"`, '');
741
+ this.curlSyntax = `${curl}${curlHeaders}${curlData}${curlForm}`;
759
742
  let fetchResponse;
760
743
 
761
744
  try {