home-erp

git clone git://archive.git.mtrnord.blog/MTRNord/home-erp.git
Log | Files | Refs | README

tryitout-4.25.0.js (10372B)


      1 window.abortControllers = {};
      2 
      3 function cacheAuthValue() {
      4     // Whenever the auth header is set for one endpoint, cache it for the others
      5     window.lastAuthValue = '';
      6     let authInputs = document.querySelectorAll(`.auth-value`)
      7     authInputs.forEach(el => {
      8         el.addEventListener('input', (event) => {
      9             window.lastAuthValue = event.target.value;
     10             authInputs.forEach(otherInput => {
     11                 if (otherInput === el) return;
     12                 // Don't block the main thread
     13                 setTimeout(() => {
     14                     otherInput.value = window.lastAuthValue;
     15                 }, 0);
     16             });
     17         });
     18     });
     19 }
     20 
     21 window.addEventListener('DOMContentLoaded', cacheAuthValue);
     22 
     23 function getCookie(name) {
     24     if (!document.cookie) {
     25         return null;
     26     }
     27 
     28     const cookies = document.cookie.split(';')
     29         .map(c => c.trim())
     30         .filter(c => c.startsWith(name + '='));
     31 
     32     if (cookies.length === 0) {
     33         return null;
     34     }
     35 
     36     return decodeURIComponent(cookies[0].split('=')[1]);
     37 }
     38 
     39 function tryItOut(endpointId) {
     40     document.querySelector(`#btn-tryout-${endpointId}`).hidden = true;
     41     document.querySelector(`#btn-canceltryout-${endpointId}`).hidden = false;
     42     const executeBtn = document.querySelector(`#btn-executetryout-${endpointId}`).hidden = false;
     43     executeBtn.disabled = false;
     44 
     45     // Show all input fields
     46     document.querySelectorAll(`input[data-endpoint=${endpointId}],label[data-endpoint=${endpointId}]`)
     47         .forEach(el => el.style.display = 'block');
     48 
     49     if (document.querySelector(`#form-${endpointId}`).dataset.authed === "1") {
     50         const authElement = document.querySelector(`#auth-${endpointId}`);
     51         authElement && (authElement.hidden = false);
     52     }
     53     // Expand all nested fields
     54     document.querySelectorAll(`#form-${endpointId} details`)
     55         .forEach(el => el.open = true);
     56 }
     57 
     58 function cancelTryOut(endpointId) {
     59     if (window.abortControllers[endpointId]) {
     60         window.abortControllers[endpointId].abort();
     61         delete window.abortControllers[endpointId];
     62     }
     63 
     64     document.querySelector(`#btn-tryout-${endpointId}`).hidden = false;
     65     const executeBtn = document.querySelector(`#btn-executetryout-${endpointId}`);
     66     executeBtn.hidden = true;
     67     executeBtn.textContent = executeBtn.dataset.initialText;
     68     document.querySelector(`#btn-canceltryout-${endpointId}`).hidden = true;
     69     // Hide inputs
     70     document.querySelectorAll(`input[data-endpoint=${endpointId}],label[data-endpoint=${endpointId}]`)
     71         .forEach(el => el.style.display = 'none');
     72     document.querySelectorAll(`#form-${endpointId} details`)
     73         .forEach(el => el.open = false);
     74     const authElement = document.querySelector(`#auth-${endpointId}`);
     75     authElement && (authElement.hidden = true);
     76 
     77     document.querySelector('#execution-results-' + endpointId).hidden = true;
     78     document.querySelector('#execution-error-' + endpointId).hidden = true;
     79 
     80     // Revert to sample code blocks
     81     document.querySelector('#example-requests-' + endpointId).hidden = false;
     82     document.querySelector('#example-responses-' + endpointId).hidden = false;
     83 }
     84 
     85 function makeAPICall(method, path, body = {}, query = {}, headers = {}, endpointId = null) {
     86     console.log({endpointId, path, body, query, headers});
     87 
     88     if (!(body instanceof FormData) && typeof body !== "string") {
     89         body = JSON.stringify(body)
     90     }
     91 
     92     const url = new URL(window.tryItOutBaseUrl + '/' + path.replace(/^\//, ''));
     93 
     94     // We need this function because if you try to set an array or object directly to a URLSearchParams object,
     95     // you'll get [object Object] or the array.toString()
     96     function addItemToSearchParamsObject(key, value, searchParams) {
     97             if (Array.isArray(value)) {
     98                 value.forEach((v, i) => {
     99                     // Append {filters: [first, second]} as filters[0]=first&filters[1]second
    100                     addItemToSearchParamsObject(key + '[' + i + ']', v, searchParams);
    101                 })
    102             } else if (typeof value === 'object' && value !== null) {
    103                 Object.keys(value).forEach((i) => {
    104                     // Append {filters: {name: first}} as filters[name]=first
    105                     addItemToSearchParamsObject(key + '[' + i + ']', value[i], searchParams);
    106                 });
    107             } else {
    108                 searchParams.append(key, value);
    109             }
    110     }
    111 
    112     Object.keys(query)
    113         .forEach(key => addItemToSearchParamsObject(key, query[key], url.searchParams));
    114 
    115     window.abortControllers[endpointId] = new AbortController();
    116 
    117     return fetch(url, {
    118         method,
    119         headers,
    120         body: method === 'GET' ? undefined : body,
    121         signal: window.abortControllers[endpointId].signal,
    122         referrer: window.tryItOutBaseUrl,
    123         mode: 'cors',
    124         credentials: 'same-origin',
    125     })
    126         .then(response => Promise.all([response.status, response.statusText, response.text(), response.headers]));
    127 }
    128 
    129 function hideCodeSamples(endpointId) {
    130     document.querySelector('#example-requests-' + endpointId).hidden = true;
    131     document.querySelector('#example-responses-' + endpointId).hidden = true;
    132 }
    133 
    134 function handleResponse(endpointId, response, status, headers) {
    135     hideCodeSamples(endpointId);
    136 
    137     // Hide error views
    138     document.querySelector('#execution-error-' + endpointId).hidden = true;
    139 
    140     const responseContentEl = document.querySelector('#execution-response-content-' + endpointId);
    141 
    142     // Prettify it if it's JSON
    143     let isJson = false;
    144     try {
    145         const jsonParsed = JSON.parse(response);
    146         if (jsonParsed !== null) {
    147             isJson = true;
    148             response = JSON.stringify(jsonParsed, null, 4);
    149         }
    150     } catch (e) {
    151 
    152     }
    153     responseContentEl.textContent = response === '' ? responseContentEl.dataset.emptyResponseText : response;
    154     isJson && window.hljs.highlightElement(responseContentEl);
    155     const statusEl = document.querySelector('#execution-response-status-' + endpointId);
    156     statusEl.textContent = ` (${status})`;
    157     document.querySelector('#execution-results-' + endpointId).hidden = false;
    158     statusEl.scrollIntoView({behavior: "smooth", block: "center"});
    159 }
    160 
    161 function handleError(endpointId, err) {
    162     hideCodeSamples(endpointId);
    163     // Hide response views
    164     document.querySelector('#execution-results-' + endpointId).hidden = true;
    165 
    166     // Show error views
    167     let errorMessage = err.message || err;
    168     const $errorMessageEl = document.querySelector('#execution-error-message-' + endpointId);
    169     $errorMessageEl.textContent = errorMessage + $errorMessageEl.textContent;
    170     const errorEl = document.querySelector('#execution-error-' + endpointId);
    171     errorEl.hidden = false;
    172     errorEl.scrollIntoView({behavior: "smooth", block: "center"});
    173 
    174 }
    175 
    176 async function executeTryOut(endpointId, form) {
    177     const executeBtn = document.querySelector(`#btn-executetryout-${endpointId}`);
    178     executeBtn.textContent = executeBtn.dataset.loadingText;
    179     executeBtn.disabled = true;
    180     executeBtn.scrollIntoView({behavior: "smooth", block: "center"});
    181 
    182     let body;
    183     let setter;
    184     if (form.dataset.hasfiles === "1") {
    185         body = new FormData();
    186         setter = (name, value) => body.append(name, value);
    187     } else if (form.dataset.isarraybody === "1") {
    188         body = [];
    189         setter = (name, value) => _.set(body, name, value);
    190     } else {
    191         body = {};
    192         setter = (name, value) => _.set(body, name, value);
    193     }
    194     const bodyParameters = form.querySelectorAll('input[data-component=body]');
    195     bodyParameters.forEach(el => {
    196         let value = el.value;
    197         if (el.type === 'file' && el.files[0]) {
    198             setter(el.name, el.files[0]);
    199             return;
    200         }
    201 
    202         if (el.type !== 'radio') {
    203             if (value === "" && el.required === false) {
    204                 // Don't include empty optional values in the request
    205                 return;
    206             }
    207             setter(el.name, value);
    208             return;
    209         }
    210 
    211         if (el.checked) {
    212             value = (value === 'false') ? false : true;
    213             setter(el.name, value);
    214         }
    215     });
    216 
    217     const query = {};
    218     const queryParameters = form.querySelectorAll('input[data-component=query]');
    219     queryParameters.forEach(el => {
    220         if (el.type !== 'radio' || (el.type === 'radio' && el.checked)) {
    221             if (el.value === '') {
    222                 // Don't include empty values in the request
    223                 return;
    224             }
    225 
    226             _.set(query, el.name, el.value);
    227         }
    228     });
    229 
    230     let path = form.dataset.path;
    231     const urlParameters = form.querySelectorAll('input[data-component=url]');
    232     urlParameters.forEach(el => (path = path.replace(new RegExp(`\\{${el.name}\\??}`), el.value)));
    233 
    234     const headers = Object.fromEntries(Array.from(form.querySelectorAll('input[data-component=header]'))
    235         .map(el => [el.name, el.value]));
    236 
    237     // When using FormData, the browser sets the correct content-type + boundary
    238     let method = form.dataset.method;
    239     if (body instanceof FormData) {
    240         delete headers['Content-Type'];
    241 
    242         // When using FormData with PUT or PATCH, use method spoofing so PHP can access the post body
    243         if (['PUT', 'PATCH'].includes(form.dataset.method)) {
    244             method = 'POST';
    245             setter('_method', form.dataset.method);
    246         }
    247     }
    248 
    249     let preflightPromise = Promise.resolve();
    250     if (window.useCsrf && window.csrfUrl) {
    251         preflightPromise = makeAPICall('GET', window.csrfUrl).then(() => {
    252             headers['X-XSRF-TOKEN'] = getCookie('XSRF-TOKEN');
    253         });
    254     }
    255 
    256     return preflightPromise.then(() => makeAPICall(method, path, body, query, headers, endpointId))
    257         .then(([responseStatus, statusText, responseContent, responseHeaders]) => {
    258             handleResponse(endpointId, responseContent, responseStatus, responseHeaders)
    259         })
    260         .catch(err => {
    261             if (err.name === "AbortError") {
    262                 console.log("Request cancelled");
    263                 return;
    264             }
    265             console.log("Error while making request: ", err);
    266             handleError(endpointId, err);
    267         })
    268         .finally(() => {
    269             executeBtn.disabled = false;
    270             executeBtn.textContent = executeBtn.dataset.initialText;
    271         });
    272 }