rapidoc 9.1.2 → 9.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.html +1 -1
- package/dist/index.html.gz +0 -0
- package/dist/rapidoc-min.js +2 -5
- package/dist/rapidoc-min.js.LICENSE.txt +1 -1
- package/dist/rapidoc-min.js.LICENSE.txt.gz +0 -0
- package/dist/rapidoc-min.js.gz +0 -0
- package/dist/report.html +2 -2
- package/docs/api.html +26 -35
- package/docs/examples/auth-test1.html +113 -0
- package/docs/examples/slots.html +7 -0
- package/docs/list.html +9 -1
- package/docs/rapidoc-min.js +2 -5
- package/docs/rapidoc-mini.html +10 -10
- package/docs/specs/auth-test1.yaml +35 -0
- package/docs/specs/auth.yaml +2 -2
- package/docs/specs/dynamic-query-params.yaml +41 -0
- package/docs/specs/multipart-formdata.yaml +3 -0
- package/docs/specs/server.yaml +4 -4
- package/index.html +3 -1
- package/package.json +4 -4
- package/src/components/api-request.js +24 -23
- package/src/components/json-tree.js +1 -1
- package/src/json-schema-viewer.js +1 -0
- package/src/rapidoc.js +1 -0
- package/src/templates/overview-template.js +1 -1
- package/src/templates/security-scheme-template.js +23 -18
- package/src/utils/schema-utils.js +8 -8
- package/src/utils/spec-parser.js +8 -0
- package/webpack.config.js +0 -12
- package/dist/rapidoc-min.js.map +0 -1
- package/dist/rapidoc-min.js.map.gz +0 -0
package/dist/rapidoc-min.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"rapidoc-min.js","mappings":";;AAglBA;AAoVA","sources":["webpack://rapidoc/./src/components/api-request.js"],"sourcesContent":["import { LitElement, html, css } from 'lit-element';\nimport marked from 'marked';\nimport Prism from 'prismjs';\n\nimport { unsafeHTML } from 'lit-html/directives/unsafe-html';\n// import { live } from 'lit-html/directives/live';\nimport TableStyles from '~/styles/table-styles';\nimport FlexStyles from '~/styles/flex-styles';\nimport InputStyles from '~/styles/input-styles';\nimport FontStyles from '~/styles/font-styles';\nimport BorderStyles from '~/styles/border-styles';\nimport TabStyles from '~/styles/tab-styles';\nimport PrismStyles from '~/styles/prism-styles';\nimport CustomStyles from '~/styles/custom-styles';\nimport { copyToClipboard, prettyXml, downloadResource, viewResource } from '~/utils/common-utils';\nimport { schemaInObjectNotation, getTypeInfo, generateExample, normalizeExamples, getSchemaFromParam, json2xml, nestExampleIfPresent } from '~/utils/schema-utils';\nimport '~/components/json-tree';\nimport '~/components/schema-tree';\nimport '~/components/tag-input';\n\nexport default class ApiRequest extends LitElement {\n constructor() {\n super();\n this.responseMessage = '';\n this.responseStatus = 'success';\n this.responseHeaders = '';\n this.responseText = '';\n this.responseUrl = '';\n this.curlSyntax = '';\n this.activeResponseTab = 'response'; // allowed values: response, headers, curl\n this.selectedRequestBodyType = '';\n this.selectedRequestBodyExample = '';\n this.activeParameterSchemaTabs = {};\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 security: { type: Array },\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 fillRequestFieldsWithExample: { type: String, attribute: 'fill-request-fields-with-example' },\n allowTry: { type: String, attribute: 'allow-try' },\n renderStyle: { type: String, attribute: 'render-style' },\n schemaStyle: { type: String, attribute: 'schema-style' },\n activeSchemaTab: { type: String, attribute: 'active-schema-tab' },\n activeParameterSchemaTabs: {\n type: Object,\n converter: {\n fromAttribute: (attr) => JSON.parse(attr),\n toAttribute: (prop) => JSON.stringify(prop),\n },\n attribute: 'active-parameter-schema-tabs',\n },\n schemaExpandLevel: { type: Number, attribute: 'schema-expand-level' },\n schemaDescriptionExpanded: { type: String, attribute: 'schema-description-expanded' },\n allowSchemaDescriptionExpandToggle: { type: String, attribute: 'allow-schema-description-expand-toggle' },\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 static get styles() {\n return [\n TableStyles,\n InputStyles,\n FontStyles,\n FlexStyles,\n BorderStyles,\n TabStyles,\n PrismStyles,\n css`\n *, *:before, *:after { box-sizing: border-box; }\n \n .read-mode {\n margin-top: 24px;\n }\n .param-name,\n .param-type {\n margin: 1px 0;\n text-align: right;\n line-height: var(--font-size-small);\n }\n .param-name{\n color: var(--fg); \n font-family: var(--font-mono);\n }\n .param-type{\n color: var(--light-fg); \n font-family: var(--font-regular);\n }\n .param-constraint{\n min-width:100px;\n }\n .param-constraint:empty{\n display:none;\n }\n .top-gap{margin-top:24px;}\n\n .textarea {\n min-height:220px; \n padding:5px;\n resize:vertical;\n direction: ltr;\n }\n .example:first-child {\n margin-top: -9px;\n }\n\n .response-message{\n font-weight:bold;\n text-overflow: ellipsis;\n }\n .response-message.error {\n color:var(--red);\n }\n .response-message.success {\n color:var(--blue);\n }\n\n .file-input-container {\n align-items:flex-end;\n }\n .file-input-container .input-set:first-child .file-input-remove-btn{\n visibility:hidden;\n }\n\n .file-input-remove-btn{\n font-size:16px;\n color:var(--red);\n outline: none;\n border: none;\n background:none;\n cursor:pointer;\n }\n\n .v-tab-btn {\n font-size: var(--smal-font-size);\n height:24px; \n border:none; \n background:none; \n opacity: 0.3;\n cursor: pointer;\n padding: 4px 8px;\n }\n .v-tab-btn.active {\n font-weight: bold;\n background: var(--bg);\n opacity: 1;\n }\n\n @media only screen and (min-width: 768px) {\n .textarea {\n padding:8px;\n }\n }\n\n @media only screen and (max-width: 470px) {\n .hide-in-small-screen {\n display:none;\n }\n }\n `,\n CustomStyles,\n ];\n }\n\n render() {\n return html`\n <div class=\"col regular-font request-panel ${'read focused'.includes(this.renderStyle) || this.callback === 'true' ? 'read-mode' : 'view-mode'}\">\n <div class=\" ${this.callback === 'true' ? 'tiny-title' : 'req-res-title'} \"> \n ${this.callback === 'true' ? 'CALLBACK REQUEST' : '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\n // 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 if (changedProperties.size === 1 && changedProperties.has('activeSchemaTab')) {\n // dont update example as only tabs is switched\n } else {\n const exampleTextAreaEls = [...this.shadowRoot.querySelectorAll('textarea[data-ptype=\"form-data\"]')];\n exampleTextAreaEls.forEach((el) => {\n const origExampleEl = this.shadowRoot.querySelector(`textarea[data-pname='hidden-${el.dataset.pname}']`);\n if (origExampleEl) {\n el.value = origExampleEl.value;\n }\n });\n }\n }\n }\n\n /* eslint-disable indent */\n\n exampleListTemplate(paramName, paramType, exampleList = []) {\n return html`\n ${(exampleList.length > 0\n ? html`<span style=\"font-weight:bold\">Example: </span>\n ${exampleList.map((v, i) => html`\n ${i === 0 ? '' : '┃'}\n ${paramType === 'array' ? '[' : ''}\n <a part=\"anchor anchor-param-example\" class = \"${this.allowTry === 'true' ? '' : 'inactive-link'}\"\n data-example-type=\"${paramType === 'array' ? paramType : 'string'}\"\n data-example = \"${v.value && Array.isArray(v.value) ? (v.value?.join('~|~')) : (v.value || '')}\"\n @click=\"${(e) => {\n const inputEl = e.target.closest('table').querySelector(`[data-pname=\"${paramName}\"]`);\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 >${v.description || v.summary || v.value}</a>\n ${paramType === 'array' ? '] ' : ''}\n `)}\n `\n : ''\n )}`;\n }\n\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 const [declaredParamSchema, serializeStyle, mimeTypeElem] = getSchemaFromParam(param);\n if (!declaredParamSchema) {\n continue;\n }\n const paramSchema = getTypeInfo(declaredParamSchema);\n if (!paramSchema) {\n continue;\n }\n const schemaAsObj = schemaInObjectNotation(declaredParamSchema, {});\n // let exampleVal = '';\n // let exampleList = [];\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 } else if (serializeStyle) {\n paramStyle = serializeStyle;\n }\n if (typeof param.explode === 'boolean') {\n paramExplode = param.explode;\n }\n }\n\n // openapi 3.1.0 spec based examples (which must be Object(string : { value:any, summary?: string, description?: string})\n const example = normalizeExamples(\n (param.examples\n || nestExampleIfPresent(param.example)\n || nestExampleIfPresent(mimeTypeElem?.example)\n || mimeTypeElem?.examples\n || paramSchema.examples\n || nestExampleIfPresent(paramSchema.example)\n ),\n paramSchema.type,\n );\n if (!example.exampleVal && paramSchema.type === 'object') {\n example.exampleVal = generateExample(\n '',\n '',\n declaredParamSchema,\n serializeStyle,\n true,\n true,\n 'text',\n false,\n )[0].exampleValue;\n }\n const labelColWidth = 'read focused'.includes(this.renderStyle) ? '200px' : '160px';\n tableRows.push(html`\n <tr> \n <td rowspan=\"${this.allowTry === 'true' ? '1' : '2'}\" style=\"width:${labelColWidth}; min-width:100px;\">\n <div class=\"param-name\">\n ${param.required ? html`<span style='color:var(--red)'>*</span>` : ''}${param.name}\n </div>\n <div class=\"param-type\">\n ${paramSchema.type === 'array'\n ? `${paramSchema.arrayType}`\n : `${paramSchema.format ? paramSchema.format : paramSchema.type}`\n }\n </div>\n </td> \n ${this.allowTry === 'true'\n ? html`\n <td style=\"min-width:100px;\">\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-example = \"${Array.isArray(example.exampleVal) ? example.exampleVal.join('~|~') : example.exampleVal}\"\n data-param-serialize-style = \"${paramStyle}\"\n data-param-serialize-explode = \"${paramExplode}\"\n data-array = \"true\"\n placeholder = \"add-multiple ↩\"\n .value = \"${Array.isArray(example.exampleVal) ? example.exampleVal : example.exampleVal}\"\n >\n </tag-input>`\n : paramSchema.type === 'object'\n ? html`\n <div class=\"tab-panel col\" style=\"border-width:0 0 1px 0;\">\n <div class=\"tab-buttons row\" @click=\"${(e) => {\n if (e.target.tagName.toLowerCase() === 'button') {\n const newState = { ...this.activeParameterSchemaTabs };\n newState[param.name] = e.target.dataset.tab;\n this.activeParameterSchemaTabs = newState;\n }\n }}\">\n <button class=\"tab-btn ${this.activeParameterSchemaTabs[param.name] !== 'example' ? 'active' : ''}\" data-tab = 'schema'>SCHEMA</button>\n <button class=\"tab-btn ${this.activeParameterSchemaTabs[param.name] === 'example' ? 'active' : ''}\" data-tab = 'example'>EXAMPLE </button>\n </div>\n ${this.activeParameterSchemaTabs[param.name] === 'example'\n ? html`<div class=\"tab-content col\">\n <textarea \n class = \"textarea request-param\"\n part = \"textarea textarea-param\"\n data-ptype = \"${paramType}-object\"\n data-pname = \"${param.name}\"\n data-example = \"${example.exampleVal}\"\n data-param-serialize-style = \"${paramStyle}\"\n data-param-serialize-explode = \"${paramExplode}\"\n spellcheck = \"false\"\n .textContent = \"${this.fillRequestFieldsWithExample === 'true' ? example.exampleVal : ''}\"\n style = \"resize:vertical; width:100%; height: ${'read focused'.includes(this.renderStyle) ? '180px' : '120px'};\"\n ></textarea>\n </div>`\n : html`\n <div class=\"tab-content col\"> \n <schema-tree\n class = 'json'\n style = 'display: block'\n .data = '${schemaAsObj}'\n schema-expand-level = \"${this.schemaExpandLevel}\"\n schema-description-expanded = \"${this.schemaDescriptionExpanded}\"\n allow-schema-description-expand-toggle = \"${this.allowSchemaDescriptionExpandToggle}\",\n schema-hide-read-only = \"${this.schemaHideReadOnly.includes(this.method)}\"\n schema-hide-write-only = false\n > </schema-tree>\n </div>`\n }\n </div>`\n : html`\n <input type=\"${paramSchema.format === 'password' ? 'password' : 'text'}\" spellcheck=\"false\" style=\"width:100%\" \n class=\"request-param\"\n part=\"textbox textbox-param\"\n data-ptype=\"${paramType}\"\n data-pname=\"${param.name}\" \n data-example=\"${Array.isArray(example.exampleVal) ? example.exampleVal.join('~|~') : example.exampleVal}\"\n data-array=\"false\"\n .value=\"${this.fillRequestFieldsWithExample === 'true' ? example.exampleVal : ''}\"\n />`\n }\n </td>`\n : ''\n }\n <td colspan=\"${(this.allowTry === 'true') ? '1' : '2'}\">\n ${paramSchema.default || paramSchema.constrain || paramSchema.allowedValues || paramSchema.pattern\n ? html`\n <div class=\"param-constraint\">\n ${paramSchema.default ? html`<span style=\"font-weight:bold\">Default: </span>${paramSchema.default}<br/>` : ''}\n ${paramSchema.pattern ? html`<span style=\"font-weight:bold\">Pattern: </span>${paramSchema.pattern}<br/>` : ''}\n ${paramSchema.constrain ? html`${paramSchema.constrain}<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=\"${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 >${v}</a>`\n }`)}\n </div>`\n : ''\n }\n </td>\n </tr>\n <tr>\n ${this.allowTry === 'true' ? html`<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(param.description || ''))}</span>\n ${this.exampleListTemplate.call(this, param.name, paramSchema.type, example.exampleList)}\n </td>\n </tr>\n `);\n }\n\n return html`\n <div class=\"table-title top-gap\">${title}</div>\n <div style=\"display:block; overflow-x:auto; max-width:100%;\">\n <table 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 readOnlyExampleEl = selectEl.closest('.example-panel').querySelector('.request-body-param');\n const userInputExampleTextareaEl = selectEl.closest('.example-panel').querySelector('.request-body-param-user-input');\n userInputExampleTextareaEl.value = readOnlyExampleEl.innerText;\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 readOnlyExampleEl = selectEl.closest('.request-body-container').querySelector('.request-body-param');\n if (readOnlyExampleEl) {\n const userInputExampleTextareaEl = selectEl.closest('.request-body-container').querySelector('.request-body-param-user-input');\n userInputExampleTextareaEl.value = readOnlyExampleEl.innerText;\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 reqBodyExampleHtml = '';\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 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 schemaAsObj;\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,\n reqBody.example,\n reqBody.schema,\n reqBody.mimeType,\n false,\n true,\n 'text',\n false,\n );\n if (!this.selectedRequestBodyExample) {\n this.selectedRequestBodyExample = (reqBodyExamples.length > 0 ? reqBodyExamples[0].exampleId : '');\n }\n reqBodyExampleHtml = html`\n ${reqBodyExampleHtml}\n <div class = 'example-panel border-top pad-top-8'>\n ${reqBodyExamples.length === 1\n ? ''\n : html`\n <select 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-example = '${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 pre(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 <pre \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 >${(v.exampleFormat === 'text' ? v.exampleValue : JSON.stringify(v.exampleValue, null, 2))}</pre>\n\n <!-- this textarea is for user to edit the example -->\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-example = \"${v.exampleFormat === 'text' ? v.exampleValue : JSON.stringify(v.exampleValue, null, 2)}\"\n data-example-format = \"${v.exampleFormat}\"\n style=\"width:100%; resize:vertical;\"\n .textContent = \"${this.fillRequestFieldsWithExample === 'true' ? (v.exampleFormat === 'text' ? v.exampleValue : JSON.stringify(v.exampleValue, null, 2)) : ''}\"\n >\n </textarea>\n\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,\n reqBody.example,\n reqBody.schema,\n reqBody.mimeType,\n false,\n true,\n 'text',\n false,\n );\n if (reqBody.schema) {\n reqBodyFormHtml = this.formDataTemplate(reqBody.schema, reqBody.mimeType, (ex[0] ? ex[0].exampleValue : ''));\n }\n }\n } else if ((RegExp('^audio/|^image/|^video/|^font/|tar$|zip$|7z$|rtf$|msword$|excel$|/pdf$|/octet-stream$').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 schemaAsObj = schemaInObjectNotation(reqBody.schema, {});\n if (this.schemaStyle === 'table') {\n reqBodySchemaHtml = html`\n ${reqBodySchemaHtml}\n <schema-table\n class = '${reqBody.mimeType.substring(reqBody.mimeType.indexOf('/') + 1)}'\n style = 'display: ${this.selectedRequestBodyType === reqBody.mimeType ? 'block' : 'none'};'\n .data = '${schemaAsObj}'\n schema-expand-level = \"${this.schemaExpandLevel}\"\n schema-description-expanded = \"${this.schemaDescriptionExpanded}\"\n allow-schema-description-expand-toggle = \"${this.allowSchemaDescriptionExpandToggle}\",\n schema-hide-read-only = \"${this.schemaHideReadOnly.includes(this.method)}\"\n schema-hide-write-only = false\n > </schema-table>\n `;\n } else if (this.schemaStyle === 'tree') {\n reqBodySchemaHtml = html`\n ${reqBodySchemaHtml}\n <schema-tree\n class = '${reqBody.mimeType.substring(reqBody.mimeType.indexOf('/') + 1)}'\n style = 'display: ${this.selectedRequestBodyType === reqBody.mimeType ? 'block' : 'none'};'\n .data = '${schemaAsObj}'\n schema-expand-level = \"${this.schemaExpandLevel}\"\n schema-description-expanded = \"${this.schemaDescriptionExpanded}\"\n allow-schema-description-expand-toggle = \"${this.allowSchemaDescriptionExpandToggle}\",\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 !== 'example' ? 'active' : ''}\" data-tab = 'schema'>SCHEMA</button>\n <button class=\"tab-btn ${this.activeSchemaTab === 'example' ? 'active' : ''}\" data-tab = 'example'>EXAMPLE</button>\n </div>\n ${this.activeSchemaTab === 'example'\n ? html`<div class=\"tab-content col\"> ${reqBodyExampleHtml}</div>`\n : html`<div class=\"tab-content col\"> ${reqBodySchemaHtml}</div>`\n }\n </div>`\n : html` \n ${reqBodyFileInputHtml}\n ${reqBodyFormHtml}`\n }\n </div> \n `;\n }\n\n formDataParamAsObjectTemplate(fieldName, fieldSchema, mimeType) {\n // This template is used when form-data param should be send as a object (application/json, application/xml)\n const formdataPartSchema = schemaInObjectNotation(fieldSchema, {});\n const formdataPartExample = generateExample(\n fieldSchema.examples,\n fieldSchema.example,\n fieldSchema,\n 'json',\n false,\n true,\n 'text',\n false,\n );\n\n return html`\n <div class=\"tab-panel row\" style=\"min-height:220px; 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:160px; height:24px; transform:rotate(270deg) translateX(-160px); 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 !== 'example' ? 'active' : ''}\" data-tab = 'schema'>SCHEMA</button>\n <button class=\"v-tab-btn ${this.activeSchemaTab === 'example' ? 'active' : ''}\" data-tab = 'example'>EXAMPLE</button>\n </div>\n </div>\n ${html`\n <div class=\"tab-content col\" data-tab = 'schema' style=\"display:${this.activeSchemaTab !== 'example' ? 'block' : 'none'}; padding-left:5px; width:100%;\"> \n <schema-tree\n .data = '${formdataPartSchema}'\n schema-expand-level = \"${this.schemaExpandLevel}\"\n schema-description-expanded = \"${this.schemaDescriptionExpanded}\"\n allow-schema-description-expand-toggle = \"${this.allowSchemaDescriptionExpandToggle}\",\n > </schema-tree>\n </div>`\n }\n ${html`\n <div class=\"tab-content col\" data-tab = 'example' style=\"display:${this.activeSchemaTab === 'example' ? 'block' : 'none'}; padding-left:5px; width:100%\"> \n <textarea \n class = \"textarea\"\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-example = \"${formdataPartExample[0]?.exampleValue || ''}\"\n .textContent = \"${this.fillRequestFieldsWithExample === 'true' ? formdataPartExample[0].exampleValue : ''}\"\n spellcheck = \"false\"\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\">${formdataPartExample[0].exampleValue}</textarea>\n </div>`\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 const fieldExamples = fieldSchema.examples || fieldSchema.example || '';\n const fieldType = fieldSchema.type;\n const paramSchema = getTypeInfo(fieldSchema);\n const labelColWidth = 'read focused'.includes(this.renderStyle) ? '200px' : '160px';\n const example = normalizeExamples((paramSchema.examples || paramSchema.example), paramSchema.type);\n formDataTableRows.push(html`\n <tr> \n <td style=\"width:${labelColWidth}; min-width:100px;\">\n <div class=\"param-name\">\n ${fieldSchema.required\n ? html`<span style='color:var(--red);'>*</span>${fieldName}`\n : html`${fieldName}`\n }\n </div>\n <div class=\"param-type\">${paramSchema.type}</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?.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\"> ✕ </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-example = \"${Array.isArray(fieldExamples) ? fieldExamples.join('~|~') : fieldExamples}\"\n data-array = \"true\"\n placeholder = \"add-multiple ↩\"\n .value = \"${Array.isArray(fieldExamples) ? Array.isArray(fieldExamples[0]) ? fieldExamples[0] : [fieldExamples[0]] : [fieldExamples]}\"\n >\n </tag-input>\n `\n : html`\n ${fieldType === 'object'\n ? this.formDataParamAsObjectTemplate.call(this, fieldName, fieldSchema, mimeType)\n : html`\n ${this.allowTry === 'true'\n ? html`<input\n .value = \"${this.fillRequestFieldsWithExample === 'true' ? example.exampleVal : ''}\"\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-example = \"${Array.isArray(fieldExamples) ? fieldExamples[0] : fieldExamples}\"\n data-array = \"false\"\n />`\n : ''\n }\n `\n }`\n }\n </td>\n ${fieldType === 'object'\n ? ''\n : html`\n <td>\n ${paramSchema.default || paramSchema.constrain || paramSchema.allowedValues || paramSchema.pattern\n ? html`\n <div class=\"param-constraint\">\n ${paramSchema.default ? html`<span style=\"font-weight:bold\">Default: </span>${paramSchema.default}<br/>` : ''}\n ${paramSchema.pattern ? html`<span style=\"font-weight:bold\">Pattern: </span>${paramSchema.pattern}<br/>` : ''}\n ${paramSchema.constrain ? html`${paramSchema.constrain}<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 ${this.exampleListTemplate.call(this, fieldName, paramSchema.type, example.exampleList)}\n </td>\n </tr>\n `\n }`);\n }\n return html`\n <table 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 .textContent = \"${exampleValue}\"\n style=\"width:100%\"\n ></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 <div class=\"response-message ${this.responseStatus}\">Response Status: ${this.responseMessage}</div>\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' > RESPONSE</button>\n <button class=\"tab-btn ${this.activeResponseTab === 'headers' ? 'active' : ''}\" data-tab = 'headers' > 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='${(e) => { downloadResource(this.responseBlobUrl, this.respContentDisposition, e); }}' part=\"btn btn-outline\">\n DOWNLOAD\n </button>\n ${this.responseBlobType === 'view'\n ? html`<button class=\"m-btn thin-border mar-top-8\" style=\"width:135px\" @click='${(e) => { viewResource(this.responseBlobUrl, e); }}' 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 <button class=\"toolbar-btn\" style=\"position:absolute; top:12px; right:8px\" @click='${(e) => { copyToClipboard(this.responseText, e); }}' part=\"btn btn-fill\"> Copy </button>\n <pre style=\"white-space:pre; max-height:400px; overflow:auto\">${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=\"toolbar-btn\" style = \"position:absolute; top:12px; right:8px\" @click='${(e) => { copyToClipboard(this.responseHeaders, e); }}' part=\"btn btn-fill\"> Copy </button>\n <pre style=\"white-space: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=\"toolbar-btn\" style = \"position:absolute; top:12px; right:8px\" @click='${(e) => { copyToClipboard(this.curlSyntax.replace(/\\\\$/, ''), e); }}' part=\"btn btn-fill\"> Copy </button>\n <pre style=\"white-space:pre\"><code>${unsafeHTML(Prism.highlight(this.curlSyntax.trim().replace(/\\\\$/, ''), Prism.languages.shell, 'shell'))}</code></pre>\n </div>\n </div>`;\n }\n\n apiCallTemplate() {\n let selectServerDropdownHtml = '';\n\n if (this.servers && this.servers.length > 0) {\n selectServerDropdownHtml = html`\n <select style=\"min-width:100px;\" @change='${(e) => { this.serverUrl = e.target.value; }}'>\n ${this.servers.map((v) => html`<option value = \"${v.url}\"> ${v.url} - ${v.description} </option>`)}\n </select>\n `;\n }\n const selectedServerHtml = html`\n <div style=\"display:flex; flex-direction:column;\">\n ${selectServerDropdownHtml}\n ${this.serverUrl\n ? html`\n <div style=\"display:flex; align-items:baseline;\">\n <div style=\"font-weight:bold; padding-right:5px;\">API Server</div> \n <span class = \"gray-text\"> ${this.serverUrl} </span>\n </div>\n `\n : ''\n }\n </div> \n `;\n\n return html`\n <div style=\"display:flex; align-items:flex-end; margin:16px 0; font-size:var(--font-size-small);\">\n <div class=\"hide-in-small-screen\" style=\"flex-direction:column; margin:0; width:calc(100% - 60px);\">\n <div style=\"display:flex; flex-direction:row; align-items:center; overflow:hidden;\"> \n ${selectedServerHtml}\n </div>\n <div style=\"display:flex;\">\n <div style=\"font-weight:bold; padding-right:5px;\">Authentication</div>\n ${this.security?.length > 0\n ? html`\n ${this.api_keys.length > 0\n ? html`<div style=\"color:var(--blue); overflow:hidden;\"> \n ${this.api_keys.length === 1\n ? `${this.api_keys[0]?.typeDisplay} in ${this.api_keys[0].in}`\n : `${this.api_keys.length} API keys applied`\n } \n </div>`\n : html`<div class=\"gray-text\">Required <span style=\"color:var(--red)\">(None Applied)</span>`\n }`\n : html`<span class=\"gray-text\"> Not Required </span>`\n }\n </div>\n </div>\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.onFillRequestData}\" title=\"Fills with example data (if provided)\">\n FILL EXAMPLE\n </button>\n <button class=\"m-btn thin-border\" part=\"btn btn-outline\" style=\"margin-right:5px;\" @click=\"${this.onClearRequestData}\">\n CLEAR\n </button>`\n : ''\n }\n <button class=\"m-btn primary thin-border\" part=\"btn btn-fill btn-try\" @click=\"${this.onTryClick}\">TRY</button>\n </div>\n ${this.responseMessage === '' ? '' : this.apiResponseTabTemplate()}\n `;\n }\n /* eslint-enable indent */\n\n async onFillRequestData(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) => {\n if (el.dataset.example) {\n if (el.tagName.toUpperCase() === 'TAG-INPUT') {\n el.value = el.dataset.example.split('~|~');\n } else {\n el.value = el.dataset.example;\n }\n }\n });\n }\n\n async 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\n async onTryClick(e) {\n // const me = this;\n const tryBtnEl = e.target;\n let fetchUrl;\n let curlUrl;\n let curl = '';\n let curlHeaders = '';\n let curlData = '';\n let curlForm = '';\n const respEl = this.closest('.expanded-req-resp-container, .req-resp-container')?.getElementsByTagName('api-response')[0];\n const acceptHeader = respEl?.selectedMimeType;\n const requestPanelEl = e.target.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;\n const fetchOptions = {\n method: this.method.toUpperCase(),\n 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 if ('json xml'.includes(paramSerializeStyle)) {\n if (paramSerializeStyle === 'json') {\n urlQueryParam.append(el.dataset.pname, JSON.stringify(queryParamObj));\n } else if (paramSerializeStyle === 'xml') {\n urlQueryParam.append(el.dataset.pname, json2xml(queryParamObj));\n }\n } else {\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 }\n } catch (err) {\n console.log('RapiDoc: unable to parse %s into object', el.value); // eslint-disable-line no-console\n }\n });\n }\n fetchUrl = `${fetchUrl}${urlQueryParam.toString() ? '?' : ''}${urlQueryParam.toString()}`;\n\n // Add authentication Query-Param if provided\n this.api_keys\n .filter((v) => (v.in === 'query'))\n .forEach((v) => {\n fetchUrl = `${fetchUrl}${fetchUrl.includes('?') ? '&' : '?'}${v.name}=${encodeURIComponent(v.finalKeyValue)}`;\n });\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.Accept = acceptHeader;\n curlHeaders += ` -H \"Accept: ${acceptHeader}\" \\\\\\n`;\n } else if (this.accept) {\n fetchOptions.headers.Accept = this.accept;\n curlHeaders += ` -H \"Accept: ${this.accept}\" \\\\\\n`;\n }\n\n // Add Authentication Header if provided\n this.api_keys\n .filter((v) => (v.in === 'header'))\n .forEach((v) => {\n fetchOptions.headers[v.name] = v.finalKeyValue;\n curlHeaders += ` -H \"${v.name}: ${v.finalKeyValue}\" \\\\\\n`;\n });\n\n // Add Header Params\n headerParamEls.map((el) => {\n if (el.value) {\n fetchOptions.headers[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('RapiDoc: 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 ((RegExp('^audio/|^image/|^video/|^font/|tar$|zip$|7z$|rtf$|msword$|excel$|/pdf$|/octet-stream$').test(requestBodyType))) {\n const bodyParamFileEl = requestPanelEl.querySelector('.request-body-param-file');\n if (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?.value) {\n fetchOptions.body = exampleTextAreaEl.value;\n // curlData = ` -d ${JSON.stringify(exampleTextAreaEl.value.replace(/(\\r\\n|\\n|\\r)/gm, '')).replace(/\\\\\"/g, \"'\")} \\\\ \\n`;\n try {\n curlData = ` -d '${JSON.stringify(JSON.parse(exampleTextAreaEl.value))}' \\\\\\n`;\n } catch (err) {\n curlData = ` -d '${exampleTextAreaEl.value.replace(/(\\r\\n|\\n|\\r)/gm, '')}' \\\\\\n`;\n }\n }\n }\n // Common for all request-body\n if (!requestBodyType.includes('form-data')) {\n // For multipart/form-data dont set the content-type to allow creation of browser generated part boundaries\n fetchOptions.headers['Content-Type'] = requestBodyType;\n }\n curlHeaders += ` -H \"Content-Type: ${requestBodyType}\" \\\\\\n`;\n }\n this.responseUrl = '';\n this.responseHeaders = [];\n this.curlSyntax = '';\n this.responseStatus = 'success';\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 const fetchRequest = new Request(fetchUrl, fetchOptions);\n this.dispatchEvent(new CustomEvent('before-try', {\n bubbles: true,\n composed: true,\n detail: {\n request: fetchRequest,\n },\n }));\n\n let fetchResponse;\n let responseClone;\n try {\n let respBlob;\n let respJson;\n let respText;\n tryBtnEl.disabled = true;\n const startTime = performance.now();\n fetchResponse = await fetch(fetchRequest);\n const endTime = performance.now();\n responseClone = fetchResponse.clone(); // create a response clone to allow reading response body again (response.json, response.text etc)\n tryBtnEl.disabled = false;\n this.responseMessage = html`${fetchResponse.statusText ? `${fetchResponse.statusText}:${fetchResponse.status}` : fetchResponse.status} <div style=\"color:var(--light-fg)\"> Took ${Math.round(endTime - startTime)} milliseconds </div>`;\n this.responseUrl = fetchResponse.url;\n const respHeadersObj = {};\n fetchResponse.headers.forEach((hdrVal, hdr) => {\n respHeadersObj[hdr] = hdrVal;\n this.responseHeaders = `${this.responseHeaders}${hdr}: ${hdrVal}\\n`;\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 respJson = JSON.parse(respText);\n this.responseText = JSON.stringify(respJson, null, 2);\n } catch {\n this.responseText = respText;\n }\n } else {\n respJson = await fetchResponse.json();\n this.responseText = JSON.stringify(respJson, null, 2);\n }\n // eslint-disable-next-line no-useless-escape\n } else if (RegExp('^font/|tar$|zip$|7z$|rtf$|msword$|excel$|/pdf$|/octet-stream$|^application\\/vnd\\.').test(contentType)) {\n this.responseIsBlob = true;\n this.responseBlobType = 'download';\n } else if (RegExp('^audio|^image|^video').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 = prettyXml(respText);\n }\n this.responseText = respText;\n }\n if (this.responseIsBlob) {\n const contentDisposition = fetchResponse.headers.get('content-disposition');\n this.respContentDisposition = contentDisposition ? contentDisposition.split('filename=')[1].replace(/\\\\\"/g, '') : 'filename';\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 this.dispatchEvent(new CustomEvent('after-try', {\n bubbles: true,\n composed: true,\n detail: {\n request: fetchRequest,\n response: responseClone,\n responseHeaders: respHeadersObj,\n responseBody: respJson || respText || respBlob,\n responseStatus: responseClone.ok,\n },\n }));\n } catch (err) {\n tryBtnEl.disabled = false;\n this.responseMessage = `${err.message} (CORS or Network Issue)`;\n document.dispatchEvent(new CustomEvent('after-try', {\n bubbles: true,\n composed: true,\n detail: {\n err,\n request: fetchRequest,\n response: responseClone,\n responseStatus: responseClone.ok,\n },\n }));\n }\n this.requestUpdate();\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 = '✕';\n\n newInputContainerEl.appendChild(newInputEl);\n newInputContainerEl.appendChild(newRemoveBtnEl);\n el.insertBefore(newInputContainerEl, e.target);\n // el.appendChild(newInputContainerEl);\n }\n\n clearResponseData() {\n this.responseUrl = '';\n this.responseHeaders = '';\n this.responseText = '';\n this.responseStatus = 'success';\n this.responseMessage = '';\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 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\ncustomElements.define('api-request', ApiRequest);\n"],"names":[],"sourceRoot":""}
|
|
Binary file
|