var azureBaseUrl = "https://bsiforms-nsbs-prod.burstingsilver.com/api/v2"; var ssoUrl = "/BSIFormsSSO"; var staticFileUrl = "https://bsiforms-nsbs-prod.burstingsilver.com"; const SelectSearchDebounceTime = 500; var customerFrontEndMessages = "{ \"Enable\" : false }"; const imisClientVersion = "20.2"; var appType; var formSubmissionId; var formName; var selectedImisId; var loggedInImisId; var parentSubmissionId; var entities; var identifiers; var readOnlyEntities; var readOnlyIdentifiers; var pageId; const bsiFormsArrayLimit = 10; const hostUrl = window.location.host; let isRedirecting = false; let isSaving = false; const apiKeyList = []; const bsiFormsAuthorization = 'BsiFormsAuthorization'; const QueryNameRegex = /\/query\/([^\\?]+).*/i; const bsiFormsTimeZoneHeader = 'x-TimezoneOffset'; const bsiTimezoneOffset = -(new Date().getTimezoneOffset()); const contentKey = jQuery(document.currentScript).attr("data-asi-contentkey"); const contentItemKey = jQuery(document.currentScript).attr("data-asi-contentitemkey"); let resolveFormReadyPromise; const formReadyPromise = new Promise((resolve) => { resolveFormReadyPromise = resolve; }); function guid() { function s4() { return Math.floor((1 + Math.random()) * 0x10000) .toString(16) .substring(1); } return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); } function getParameterByName(name, url) { if (!url) url = window.location.href; name = name.replace(/[\[\]]/g, '\\$&'); const regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'), results = regex.exec(url); if (!results) return ''; if (!results[2]) return ''; return decodeURIComponent(results[2].replace(/\+/g, ' ')); } function ensureUrlParamsInitialized() { if (!appType) appType = getParameterByName("appType"); if (!formSubmissionId) formSubmissionId = getParameterByName("formSubmissionId"); if (!parentSubmissionId) parentSubmissionId = getParameterByName("parentSubmissionId"); if (!formName) formName = getParameterByName("formName"); if (!selectedImisId) selectedImisId = getParameterByName("ID"); if (!entities) entities = getParameterByName("entities"); if (!identifiers) identifiers = getParameterByName("identifiers"); if(!readOnlyEntities) readOnlyEntities = getParameterByName("readOnlyEntities"); if(!readOnlyIdentifiers) readOnlyIdentifiers = getParameterByName("readOnlyIdentifiers"); if (!pageId) pageId = getParameterByName("pageId"); } function updateUrlParameter(uri, key, value) { // remove the hash part before operating on the uri const i = uri.indexOf('#'); const hash = i === -1 ? '' : uri.substr(i); uri = i === -1 ? uri : uri.substr(0, i); const re = new RegExp("([?&])" + key + "=.*?(&|$)", "i"); const separator = uri.indexOf('?') !== -1 ? "&" : "?"; if (uri.match(re)) { uri = uri.replace(re, '$1' + key + "=" + value + '$2'); } else { uri = uri + separator + key + "=" + value; } return uri + hash; // finally append the hash as well } function checkLang() { if (jQuery('.LanguageSelector').val() == 'en-US') { hideLanguageDiv("FR"); } else if (jQuery('.LanguageSelector').val() == 'fr') { hideLanguageDiv("EN"); } } function hideLanguageDiv(langCode) { jQuery("[id*=LANG" + langCode + "]").hide(); } function fetchFormData(formName, imisId, appType, loggedInImisId, action, queryString, fetchOptions) { const urlFormName = encodeUrlArgument(formName); const urlImisId = encodeUrlArgument(imisId); const urlAppType = encodeUrlArgument(appType); const urlLoggedInImisId = encodeUrlArgument(loggedInImisId); const finalQueryString = buildQueryString(queryString); const fetchPath = "" .concat("/FormData/") .concat(urlFormName, "/") .concat(urlImisId, "/") .concat(urlAppType, "/") .concat(urlLoggedInImisId, "/") .concat(action) .concat(finalQueryString); return fetchFromService(fetchPath, fetchOptions); } function fetchFormDefinition(formName, imisId, appType, formSubmissionId, fetchOptions) { const fetchUrl = "".concat("/form/definition/").concat(formName); return fetchFromService(fetchUrl, fetchOptions); } function fetchFormInitialProperties(formName, imisId, appType, formSubmissionId, fetchOptions){ const fetchUrl = "".concat("/form/initialparameters/").concat(formName); return fetchFromService(fetchUrl, fetchOptions); } function buildQueryString(originalQueries) { let result = originalQueries == null ? "" : originalQueries; //Ensure query string starts with '?' if (result.indexOf("?") !== 0) { result = "?" + result; } //for each of the parameters we want to add. Add them if they do not already exist in the current query string. if (formSubmissionId != null && formSubmissionId !== "" && getParameterByName("formSubmissionId", result) === "") result = addToQueryString(result, "formSubmissionId", formSubmissionId); if (parentSubmissionId != null && parentSubmissionId !== "" && getParameterByName("parentSubmissionId", result) === "") result = addToQueryString(result, "parentSubmissionId", parentSubmissionId); if (entities != null && entities !== "" && getParameterByName("entities", result) === "") result = addToQueryString(result, "entities", entities); if (identifiers != null && identifiers !== "" && getParameterByName("identifiers", result) === "") result = addToQueryString(result, "identifiers", identifiers); if (readOnlyEntities != null && readOnlyEntities !== "" && getParameterByName("readOnlyEntities", result) === "") result = addToQueryString(result, "readOnlyEntities", readOnlyEntities); if (readOnlyIdentifiers != null && readOnlyIdentifiers !== "" && getParameterByName("readOnlyIdentifiers", result) === "") result = addToQueryString(result, "readOnlyIdentifiers", readOnlyIdentifiers); if (result === "?") return ""; return result; } function addToQueryString(queryString, key, value) { let decoded = decodeURIComponent(value); let keyValue = ""; // If the value is already encoded, we don't want to encode it again. if (decoded !== value) { keyValue = key + "=" + value; } else { keyValue = key + "=" + encodeURIComponent(value); } if (queryString == null) return "?" + keyValue; if (queryString === "" || queryString === "?") return queryString + keyValue; return queryString + "&" + keyValue; } function encodeUrlArgument(arg) { let result = arg == null || arg == "" ? arg = " " : arg; result = encodeURIComponent(result); return result; } function PrePopulateFormData(selectedImisId, loggedInImisId, formName, form) { ShowFancyAnimation(); ensureUrlParamsInitialized(); if (!selectedImisId) { console.log("Selected iMIS ID is not present. Not pre-populating form."); HideFancyAnimation(); return; } return fetchFormData(formName, selectedImisId, appType, loggedInImisId, "GetFormJson", "", { headers: { 'content-type': 'application/json' }, mode: 'cors', credentials: 'include' }) .then(function (response) { return response.json(); }) .then(function (formJson) { console.log("Received form json:\n" + JSON.stringify(formJson)); let alertMessage = ""; let warnings = formJson.warnings; for (let i = 0; i < warnings.length; i++) { if (i > 0) alertMessage += "\n\n"; alertMessage += "Warning " + (i + 1) + ":\n" + warnings[i]; } if (alertMessage !== "") alert(alertMessage); CombineData(form, formJson.formData); HideFancyAnimation(); }) .catch(function (res) { console.log("Failed to pre-populate data from the database. Error is: " + res); FancyAlert("Failed to pre-populate data from the database.", "Form Pre-Population", false); HideFancyAnimation(); }); } function CombineData(form, formDataJson) { //HACK : Convert string to array for all multiple value keys and update the JSON //Action : display OR submit //TODO: What is this? It looks like it gets data from 'DetectMutation.js' apiKeyList.forEach(function (element) { formDataJson = updateJSON(formDataJson, element, "display"); }); form.submission = { data: formDataJson }; } //TODO: Only used with the apiKeyList, need to investigate what this is function updateJSON(object, findAndUpdateKey, action) { const returnObj = object; Object.keys(object).forEach(function (k) { if (object[k] && typeof object[k] === 'string') { if (k === findAndUpdateKey) { object[k] = convertArrayOrString(object[k]); return object; } } if (object[k] && typeof object[k] === 'object') { if (k == findAndUpdateKey) { object[k] = convertArrayOrString(object[k]); return object; } return updateJSON(object[k], findAndUpdateKey, action); } }); function convertArrayOrString(input) { let output = input; action = (typeof action === 'string') ? action.toUpperCase() : action; switch (action) { case "SUBMIT": if (Array.isArray(output)) { output = input.toString(); } break; case "DISPLAY": if (typeof output === 'string') { output = input.split(","); } break; default: output = input; } return output; } return returnObj; } function SaveReview(formName, data, formObject, formComponent) { SaveReviewAndRedirect(formName, data, null, formObject, formComponent); } function SaveReviewAndRedirect(formName, data, redirectUrl, formObject, formComponent) { // console.log("Saving the review data to iMIS", data); console.log("Saving the review data to iMIS"); //Temporarily replace form Submission Id and parent Id to save the review and avoid collisions. //Parent ID will match the form you are saving to if an id already exists. ensureUrlParamsInitialized(); const tempSubmissionId = formSubmissionId; const tempParentId = parentSubmissionId; formSubmissionId = "new"; parentSubmissionId = tempSubmissionId; const submission = GetFormattedSubmission(data, formComponent); if (formName != null && formName !== "" && formName !== formObject.name) { submission.data = submission.data[formName].data; formObject = FormioUtils.getComponent(formObject.components, formName); } //save the promise since the .then and .catch are the same accross them //using this to consolidate SaveReview and SaveReviewAndRedirect let submissionPromise = null; if (redirectUrl == null) { //No RedirectUrl, just save the review. submissionPromise = SubmitFormDataToService(formName, submission, selectedImisId, null, false, null, null, formObject); } else { //RedirectUrl exists, use redirect option. submissionPromise = SubmitFormDataToService(formName, submission, selectedImisId, null, true, redirectUrl, null, formObject) } //On success or failure, bring back the ids to their original values return submissionPromise.then(function (result) { formSubmissionId = tempSubmissionId; parentSubmissionId = tempParentId; }) .catch(function (exception) { formSubmissionId = tempSubmissionId; parentSubmissionId = tempParentId; }); } function SubmitFormDataToService(formName, submission, imisId, next, redirectUser, redirectUrl, newAppStatus, formObject) { if (isSaving) return; isSaving = true; ShowFancyAnimation(); let formSubmissionData = submission; if (submission.data) formSubmissionData = submission.data; if (formObject != null) { CleanFormSubmissionData(formObject, formSubmissionData); } console.log("submission data: " + JSON.stringify(submission)); //HACK : Convert array to string for all multiple value keys and update the JSON //Action : display OR submit apiKeyList.forEach(function (element) { submission = updateJSON(submission, element, "submit"); }); ensureUrlParamsInitialized(); console.log('Sending form JSON data to Azure'); //TODO: Review this application status stuff because it's garbage const defaultAppStatus = formSubmissionData["applicationStatus"]; if (newAppStatus) { // If there is already a status on the application and new app status is Started do not update if ((!formSubmissionData["applicationStatus"] && newAppStatus === "Started") || (newAppStatus !== "Started")) formSubmissionData["applicationStatus"] = newAppStatus; } //make the different status match if (formSubmissionData.reviewResource !== undefined && formSubmissionData.panelColumnsApplicationStatus != formSubmissionData.reviewResource.data.panelStatus && formSubmissionData.reviewResource.data.panelStatus != '') { formSubmissionData.panelColumnsApplicationStatus = formSubmissionData.reviewResource.data.panelStatus; } return fetchFormData(formName, imisId, appType, loggedInImisId, "SaveFormJson", "isBackgroundSync=false", { headers: { 'Content-Type': 'application/json' }, mode: "cors", method: "POST", credentials: 'include', body: JSON.stringify(submission) } ) .then(function (response) { return response.json(); }) .then(function (myJson) { const responseId = myJson.formSubmissionId; console.log("Data returned from SaveFormJson service: " + JSON.stringify(responseId)); if (parseInt(responseId) > 0) { //Replace state if formSubmissionId matches url param, neither are empty and don't match new id if (responseId.toString() != formSubmissionId && formSubmissionId != "" && formSubmissionId.toLowerCase() == getParameterByName("formSubmissionId").toLowerCase()) { window.history.replaceState(window.history.state, "Title", updateUrlParameter(location.href, "formSubmissionId", responseId)); } formSubmissionId = responseId.toString(); } console.log("Data returned from SaveFormDataToImis service: " + JSON.stringify(myJson)); if (myJson.Error) { FancyAlert("Failed to save form data: " + myJson.Error); console.log("Failed to save form data: " + myJson.Error); if (next) FancyAlert("Failed to save data to database. Check console log file for error message"); return next("Failed to save data to database. Check console log file for error message"); } const partyId = myJson.partyId; const authToken = myJson.authToken; if (selectedImisId === "new") { window.history.replaceState(window.history.state, "Title", updateUrlParameter(location.href, "ID", partyId)); selectedImisId = partyId; } if (!redirectUrl) redirectUrl = myJson.redirectUrl; if (partyId && authToken) { redirectUrl = "https://" + hostUrl + rootWebsiteApp + "/Authentication.aspx?MEMBER_ID=" + partyId + "&AUTH_TOKEN=" + authToken + "&RESET_PASSWORD=FALSE&RETURN_URL=" + redirectUrl; } FancyAlert("Successfully saved form"); console.log("Successfully saved form. RedirectUrl is: " + redirectUrl + " redirecting user? " + redirectUser); console.log(hostUrl); //Variable needs to be set to prevent redirect on postSubmissionSteps isRedirecting = redirectUrl && redirectUser if (isRedirecting) { // Check if website is included in url, if not we will use the same site as the user is currently on, WIP //const sitePath = location.pathname.replace(gWebRoot, ""); //const url = new URL(location.protocol + "//" + location.hostname + redirectUrl); //const redirectSitePath = url.pathname.replace(gWebRoot, ""); location.href = redirectUrl; } // Set the application status back to the default now that it has been submitted formSubmissionData["applicationStatus"] = defaultAppStatus; if (!redirectUser) FancyAlert("Form data saved to server successfully"); if (next && next instanceof Function) { let nextResult = next(); if (nextResult instanceof Promise) return nextResult.then(function() { isSaving = false }); } isSaving = false; }) .catch(function (res) { isSaving = false; formSubmissionData["applicationStatus"] = defaultAppStatus; FancyAlert("Failed to save data to database. Error is " + res); console.log("Failed to save data to database. Error is " + res); if (next) FancyAlert("Failed to save data to database. Check console log file for error message"); return next("Failed to save data to database. Check console log file for error message"); }); } function GetUserContext(loggedInId, selectedId) { return fetchFromService("/user/context?loggedInImisId=" + loggedInId + "&selectedImisId=" + selectedId, { mode: 'cors', credentials: 'include' }).then(function(response) { return response.json(); }).then(function(contextInfo) { loggedInUser = contextInfo.loggedInUserInfo; selectedUser = contextInfo.selectedUserInfo; loggedInUser.memberGroups = loggedInUser.memberGroups.map(function (x) { return x.toUpperCase(); }); selectedUser.memberGroups = selectedUser.memberGroups.map(function (x) { return x.toUpperCase(); }); }); } function GetMemberType(userInfo) { return userInfo.memberType; } function UserHasGroup(userInfo, groupName) { return userInfo.memberGroups.includes(groupName.toUpperCase()); } function ShowFancyAnimation() { const darkOverlayDiv = jQuery(""); const fancySpinnerDiv = jQuery("
"); jQuery("#formio").append(darkOverlayDiv); jQuery("#formio").append(fancySpinnerDiv); } function HideFancyAnimation() { jQuery("#darkOverlayDivId").remove(); jQuery("#fancySpinnerDivId").remove(); } //TODO: Review uses function FancyAlert(body) { const title = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "Form Submission"; HideFancyAnimation(); jQuery('#fancyAlertTitleId').text(title); jQuery('#fancyAlertBodyId').text(body); jQuery('#fancyModalId').modal('hide'); } function GetCurrentLanguage() { if (jQuery('.LanguageSelector').val() !== undefined) { return jQuery('.LanguageSelector').val(); } else { return jQuery('.LanguageSelector', parent.document).val(); } } function SoftDeleteRow(dataRow, dataGridName, form) { form.loading = true ensureUrlParamsInitialized(); let rowIndex = -1; const submissionData = form.submission.data; const datagrid = GetDataByComponentName(submissionData, dataGridName); if (datagrid != null) { const fail = false; for (let i = 0; i < datagrid.length; i++) { const row = datagrid[i]; rowIndex = i; for (key in row) { try { if (row[key] != dataRow[key] && !(row[key] instanceof Object && typeof (row[key]) === typeof (dataRow[key]))) { //If type is object, don't care about equality, just that the types are the same rowIndex = -1; break } } catch (err) { //key does not exist in dataRow console.log("Failed") return; } } if (rowIndex >= 0) { break; } } } else { //TODO: Error console.log("Failed") return; } if (rowIndex < 0) { //TODO: Error console.log("Failed") return; } let queryString = "dataGridName=" + encodeURIComponent(dataGridName) + "&dataRow=" + encodeURIComponent(rowIndex); let formioName = null; if (form.name) { formioName = form.name } else { formioName = form.formio.formId; } fetchFormData(formioName, selectedImisId, appType, loggedInImisId, "DeleteDataRow", queryString, { mode: "cors", method: "GET", credentials: 'include' }) .then(function (response) { FormioDeleteRow(dataGridName, rowIndex, form); form.loading = false; }) .catch(function (exception) { FancyAlert("Failed to delete row."); }); } function SoftDeleteSubmission(formName, submissionId) { fetchFormData(formName, selectedImisId, appType, loggedInImisId, "DeleteFormSubmission", "", { mode: "cors", method: "GET", credentials: 'include' }) .catch(function (exception) { FancyAlert("Failed to delete submission."); }); } function GetDataByComponentName(formData, componentName) { for (let key in formData) { const component = formData[key]; if (key == componentName) { return component; } else if (component instanceof Array) { //Need to continue before hitting the next else since Arrays are also Objects continue; } else if (component instanceof Object) { const result = GetDataByComponentName(component, componentName); if (result != null) return result; } } return null; } function FormioDeleteRow(dataGridName, rowIndex, form) { const gridId = GetComponentId(dataGridName, form.components); if (gridId != null) { jQuery("#" + gridId + " td:last-child button")[rowIndex].click(); } } function GetComponentId(componentName, baseComponents) { for (let i = 0; i < baseComponents.length; i++) { const nestedComponent = baseComponents[i]; if (nestedComponent.key == componentName || (nestedComponent.component != null && nestedComponent.component.key == componentName)) { return nestedComponent.id; } else if (nestedComponent.components != null && nestedComponent.components.length > 0) { const id = GetComponentId(componentName, nestedComponent.components); if (id != null) return id; } } return null; } //This function removes hidden data that is meant to be cleared by formio function CleanFormSubmissionData(form, submissionData) { //Ensure there are components on the given form/component if (!('components' in form) || form.components.length < 1) return; for (let i = 0; i < form.components.length; i++) { const component = form.components[i]; //continue if component not found in data. //For arrays, check if there is any data in the array, and if so, check if the first row has the component, since either all will have it, or all will not. if (component.key in submissionData && !(submissionData instanceof Array && (submissionData.length < 1 || !(component.key in submissionData)))) { //If component is hidden and should clear on hide, delete data. if (component.originalComponent.clearOnHide && !component.visible) { //if submissionData is an array, we need to delete the component from each row if (submissionData instanceof Array) { for (let j = 0; j < submissionData.length; j++) { delete submissionData[j][component.key]; } } else { delete submissionData[component.key]; } continue; } } //Recurse for nested objects to deal with panels. datagrids (arrays) cannot have nested panels if (submissionData instanceof Array) continue; //For nested forms, the data will be nested if (component.type == "form") { if ('data' in submissionData[component.key]) CleanFormSubmissionData(component, submissionData[component.key].data); else CleanFormSubmissionData(component, submissionData[component.key]); continue; } //Recurse on the component that may have sub-components, but is still within the same form (so no nested object in submission) CleanFormSubmissionData(component, submissionData); } } function GetFormattedSubmission(submission, formComponent) { //Check submission data structure and add component if exists. let result = submission; if (!result.hasOwnProperty("data")) { result = { "data": submission, "component": formComponent } } else if (formComponent != null) { result.component = formComponent } return result; } function GetComponentFromEvent(eventData, formObject) { if (eventData == null || !'currentTarget' in eventData) return null; if (eventData.currentTarget == null || !'id' in eventData.currentTarget || eventData.currentTarget.id == null) return null; return FormioUtils.getComponent(formObject.components, eventData.currentTarget.id).component; } function fetchFromService(url, options) { if (options == null) { options = {}; } if (!('headers' in options)) { options.headers = {}; } options.headers[bsiFormsAuthorization] = localStorage.getItem(bsiFormsAuthorization); options.headers[bsiFormsTimeZoneHeader] = bsiTimezoneOffset; return fetch(azureBaseUrl + url, options).then(function (response) { if (response.status === 401) { //TODO: Improve this flow to allow data to persist after re-authenticating. localStorage.removeItem(bsiFormsAuthorization); if(GetAlertBoxMessages().Enable){ alert(GetAlertBoxMessages().SessionExpiredMessage); } RedirectSSO(); } return response; }); } function GetTranslationData() { //LoadTranslations function must be loaded externally and checked if it exists. if (typeof LoadTranslations !== "function") return null; return LoadTranslations(); } function GetAlertBoxMessages() { const defaultCreateSecureConnectionMessage = "Please click “ok” to create a secure connection."; const defaultSessionExpiredMessage = "Your session has expired, please click “ok” to refresh the page."; const defaultFlag = true; if(customerFrontEndMessages !== null && customerFrontEndMessages !== "null" && customerFrontEndMessages.trim() !== "" ){ try { var messages = JSON.parse(customerFrontEndMessages); if(!messages.hasOwnProperty('CreateSecureConnectionMessage')){ messages['CreateSecureConnectionMessage'] = defaultCreateSecureConnectionMessage; } if(!messages.hasOwnProperty('SessionExpiredMessage')){ messages['SessionExpiredMessage'] = defaultSessionExpiredMessage; } if(!messages.hasOwnProperty('Enable')){ messages['Enable'] = defaultFlag; } return messages; } catch(err) { console.log(customerFrontEndMessages +" : "+ err); } } return { "Enable":true, "CreateSecureConnectionMessage" : defaultCreateSecureConnectionMessage , "SessionExpiredMessage": defaultSessionExpiredMessage }; } function FetchPdfArchiveUrl() { ensureUrlParamsInitialized(); let bsiFormsId = getParameterByName("BSIFormsId"); let formName = getParameterByName("formName"); let formSubmissionId = getParameterByName("formSubmissionId"); let authKey = localStorage.getItem(bsiFormsAuthorization); if (bsiFormsId == null || bsiFormsId == "") { RedirectSSO(); return { success: false, data: 'Authenticating...' }; } if (authKey === "" || authKey == null) { fetchFromService("/user/authenticate?oneTimeSignOn=" + bsiFormsId) .then(function (response) { switch (response.status) { case 200: //OK -> Can Access return response.json().then(function (authResult) { localStorage.setItem(bsiFormsAuthorization, authResult.authKey); window.location.reload();//need to have a better way then a page reload, reason for this is because the function doesnt not return a promise }); case 400: //Bad Request GetAuthKeyOrRedirectSSO(); break; default: //Probably a 500 error if the server broke. FancyAlert("The server has encountered an error.", "Error"); break; } }); return { success: false, data: 'Authenticating...' }; } if (formName == null || formName == "") { let error = "'formName' missing from URL parameters" console.error(error); return { success: false, data: error }; } if (formSubmissionId == null || formSubmissionId == "") { let error = "'formSubmissionId' missing from URL parameters"; console.error(error); return { success: false, data: error }; } var clientContext = JSON.parse(jQuery("#__ClientContext").val()); var isAnonymous = clientContext.isAnonymous; loggedInImisId = isAnonymous ? "" : clientContext.loggedInPartyId; if (selectedImisId == null || selectedImisId == "") { selectedImisId = isAnonymous ? "" : clientContext.selectedPartyId; } var queryString = "formSubmissionId=" + encodeURIComponent(formSubmissionId); var fetchOptions = { mode: "cors", method: "GET", credentials: 'include', }; var fetchResponse = fetchFormData(formName, selectedImisId, appType, loggedInImisId, "GetArchivePdfTempUrl", queryString, fetchOptions); return { success: true, data: fetchResponse }; } var buttonSettings; var bsiFormsInitialized = false; function LoadForm() { ensureUrlParamsInitialized(); let bsiFormsId = getParameterByName("BSIFormsId"); if (bsiFormsId != null && bsiFormsId !== "") { fetchFromService("/user/authenticate?oneTimeSignOn=" + bsiFormsId) .then(function (response) { switch (response.status) { case 200: //OK -> Can Access return response.json().then(function (authResult) { localStorage.setItem(bsiFormsAuthorization, authResult.authKey); CheckPermissionsAndDisplayForm(); }); case 400: //Bad Request CheckForAuthKey(); break; default: //Probably a 500 error if the server broke. FancyAlert("The server has encountered an error.", "Error"); break; } }); } else { CheckForAuthKey(); } } function GetFormInitialParameters(Callback) { fetchFormInitialProperties(formName, selectedImisId, appType, formSubmissionId, { headers: { 'content-type': 'application/json', 'ImisId': selectedImisId, 'AppType': appType, 'FormSubmissionId': formSubmissionId }, mode: 'cors', credentials: 'include' }).then(function (response) { switch (response.status) { case 200: //OK -> Can Access return response.json(); case 401: //Unauthorized -> Not logged in //Allow to submit the form unless it has to be disabled intentionally Callback(false); break; case 403: //Forbidden -> Logged in, still no access. Callback(false); break; default: //Probably a 500 error if the server broke. Callback(false); break; } }).then(function (data) { Callback(data.readOnlyForm); }).catch(function(error) { console.log("Failed to fetch application status. ERROR: " + error); Callback(false); }); } function CheckPermissionsAndDisplayForm() { // If form name was not provided in querystring params if (!formName) { var className = jQuery('[class^="formio_id_"]').attr('class'); if (!className) { className = jQuery('[class^="bsiforms_id_"]').attr('class'); fullstring = className.substr(12, (className.length - 12)).split('_'); formName = fullstring[0] } else { fullstring = className.substr(10, (className.length - 10)).split('_'); formName = fullstring[0]; } if(className.includes("customWizard", className.Length)){ buttonSettings = { showCancel: false, showPrevious: false, showNext: false, showSubmit: false }; } } var clientContext = JSON.parse(jQuery("#__ClientContext").val()); var isAnonymous = clientContext.isAnonymous; loggedInImisId = isAnonymous ? "" : clientContext.loggedInPartyId; if (selectedImisId == null || selectedImisId == "") { selectedImisId = isAnonymous ? "" : clientContext.selectedPartyId; } console.log("selected iMIS ID: " + selectedImisId + " loggedInImisId is :" + loggedInImisId + " azureBaseUrl: " + azureBaseUrl); fetchFormData(formName, selectedImisId, appType, loggedInImisId, "CanOpenForm", "", { headers: { 'content-type': 'application/json', }, mode: 'cors', credentials: 'include' }) .then(function (response) { switch (response.status) { case 200: //OK -> Can Access GetUserContext(loggedInImisId, selectedImisId).then(function() { GetFormInitialParameters(function(readonly) { isReadOnlyForm = readonly; CreateForm(); }); }); break; case 401: //Unauthorized -> Not logged in RedirectSSO(); break; case 403: //Forbidden -> Logged in, still no access. FancyAlert("You do not have access to open this form.", "Access Denied"); break; default: //Probably a 500 error if the server broke. FancyAlert("The server has encountered an error.", "Error"); break; } }); } function getHttpHeaders() { var token = document.getElementById("__RequestVerificationToken").value; return { "Content-Type": "application/json", "RequestVerificationToken": token } } function getIpartSettings() { return new Promise(function (isSuccess, isFailure) { //Content keys added to CommonV2.js if (!contentKey || !contentItemKey) { isSuccess(""); return; } if (sessionStorage.getItem("display-form-settings")) { isSuccess(JSON.parse(sessionStorage.getItem("display-form-settings"))); return; } // Get Settings var response = jQuery.ajax({ type: "GET", headers: getHttpHeaders(), url: gWebRoot + "/api/ContentItem", data: jQuery.param({ contentKey: contentKey, contentItemKey: contentItemKey }), success: function () { var settings = imisClientVersion === "20.3" ? response.responseJSON.Items.$values[0].Data.Settings : response.responseJSON.Settings; sessionStorage.setItem("display-form-settings", JSON.stringify(settings)); isSuccess(settings); }, error: function (jqXHR, textStatus, errorThrown) { isFailure(textStatus); } }); }); }; let isReadOnlyForm = false; function CreateForm() { let formInstance; var fileRequest = { form: '', file: null }; var executeIqaQueryHelper = function (data) { if ((data.type !== "select" && data.type !== undefined && data.type !== null) || (!data.url.startsWith(azureBaseUrl) && data.url.toLowerCase().indexOf("/query/") === -1)) return null; let relativePath = data.url.substr(data.url.toLowerCase().indexOf("/query/")); let queryPromise = ExecuteIqaQuery(relativePath, data.opts, "select."); let componentValue; // Parse the value based on the type we only want to do this if it is a string, number, boolean, date object or an array of the same types if (data.component && data.component.value && !Array.isArray(data.component.value)) { switch (typeof data.component.value) { case "string": case "number": case "boolean": componentValue = data.component.value; break; case "object": { if (Object.prototype.toString.call(data.component.value) === '[object Date]') componentValue = data.component.value; } default: break; } } else if (data.component && data.component.value && Array.isArray(data.component.value) && data.component.value.length > 0) { // If the value is an array we will join into a comma seperated string switch (typeof data.component.value[0]) { case "string": case "number": case "boolean": componentValue = data.component.value.join(); break; case "object": { if (Object.prototype.toString.call(data.component.value[0]) === '[object Date]') componentValue = data.component.value.join(); } default: break; } } // if component has a value, and the value is not in the normal lookup, perform a direct lookup against the component value // otherwise return the normal lookup results if (componentValue) { queryPromise = queryPromise.then((iqaResults) => { if (!Array.isArray(iqaResults)) return Promise.resolve([]); let isMissing = false; let isMultiSelect = typeof componentValue === "string" && componentValue.includes(","); if (isMultiSelect) { // handle multi select's differently isMissing = !iqaResults.some(r => componentValue.split(",").some(cv => r.hasOwnProperty("value") && String(r.value) === cv)); } else { isMissing = !iqaResults.some(result => result.hasOwnProperty("value") && String(result.value) == componentValue); } if (!isMissing) return Promise.resolve(iqaResults); let relativePathDirect = relativePath; relativePathDirect = relativePathDirect.replace(/\?.*$/, ""); // strip existing query parameters relativePathDirect = relativePathDirect.concat("?selectedValue=", encodeURIComponent(componentValue)); // append ?selectedValue=value return ExecuteIqaQuery(relativePathDirect, data.opts, "select.").then((filteredResults) => { return iqaResults.concat(filteredResults); }); }); } return queryPromise; }; var HttpPlugin = { fileRequest: function (request) { if (request.method === 'download') { let encodedFormName = encodeURIComponent(formName); let encodedFileName = encodeURIComponent(request.file.name); return fetchFromService("/Form/GetFileFromStorage?formName=" + encodedFormName + "&fileName=" + encodedFileName).then(function (response) { if (!response.ok) throw "Failed to download file: " + request.file.name; return response.json(); }).then(function (responseBody) { return responseBody; }); } else if (request.method === 'upload') { let requestDir = request.dir == null ? "" : request.dir; let requestFile = request.fileName == null ? "" : request.fileName; requestDir = requestDir.replace(/^\/*/i, "").replace(/\/*$/i, ""); requestFile = requestFile.replace(/^\/*/i, "").replace(/\/*$/i, ""); let fileName = ""; if (requestDir == null || requestDir === "") fileName = requestFile; else if (requestFile == null || requestFile === "") fileName = requestDir; else fileName = requestDir + "/" + requestFile; let requestBody = { name: fileName, size: request.file.size, type: request.file.type }; return fetchFromService("/Form/SaveFileToStorage/" + formName, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(requestBody) }).then(function (response) { if (!response.ok) throw "Oh no V1"; return response.json(); }).then(function (responseBody) { let uploadUrl = responseBody.url; return fetch(uploadUrl, { method: "PUT", body: request.file, headers: { "Content-Type": request.file.type, "x-ms-blob-type": "BlockBlob" } }); }).then(function (response) { if (!response.ok) throw "Oh no V2"; return { storage: 'azure', name: fileName, size: request.file.size, type: request.file.type }; }); } return null; }, wrapFileRequestPromise: function (data) { formInstance.loading = true; return data.then( function (v) { formInstance.loading = false; return v; }); }, wrapFileRequestPromise: function (data) { formInstance.loading = true; return data.then( function (v) { formInstance.loading = false; return v; }); }, request: executeIqaQueryHelper, staticRequest: executeIqaQueryHelper, }; let formDefinitionUrl = azureBaseUrl + "/form/definition"; Formio.registerPlugin(HttpPlugin, 'download-proxy'); Formio.setProjectUrl(formDefinitionUrl); Formio.setApiUrl(formDefinitionUrl); let lastSaveState = ""; Formio.createForm(document.getElementById('formio'), formDefinitionUrl + "/" + formName, { hooks: { beforeSubmit: function(submission, next) { //Before submitting, save the submission state. lastSaveState = submission.state; if (next) next(); }, customValidation: function (submission, next) { return SubmitFormDataToService(formName, submission, selectedImisId, next, true, null, null, this); } }, buttonSettings: buttonSettings, readOnly: isReadOnlyForm, language: GetCurrentLanguage(), i18n: GetTranslationData() }).then(function (form, next) { form.nosubmit = true; formObj = form; fileRequest.form = form.formio.formUrl; formInstance = form; findWorkflowForm(form); //Overide show errors to allow changes to validation. Need to bind(form) or errors are thrown when we call it again. let oldShowErrors = form.showErrors.bind(form); form.showErrors = function(e, t) { //If draft, make sure draft is enabled and return no errors. if (lastSaveState === "draft") { form.draftEnabled = true; return []; } //Otherwise disable draft and use default functionality. if (form.draftEnabled) form.draftEnabled = false; return oldShowErrors(e, t); }; //Scroll to top to make invalid errors visible to member form.on('error', function (errors) { if (errors.length > 0) { window.scrollTo(0, 0); } }); // Store and set proper wizard page numbers window.addEventListener("beforeunload", function(event) { storeFormInfo(form); }); // Register for the submit event to get the completed submission. form.on('submit', function (submission) { postSubmissionSteps(form); }); form.on('SubmitFormForReview', function (data) { var formComponent = GetComponentFromEvent(event, form); var submission = GetFormattedSubmission(data, formComponent); SubmitFormDataToService(formName, submission, selectedImisId, next, true, null, "Submitted", form) .then(function (response) { postSubmissionSteps(form); }); }); form.on('SaveReview', function (data) { var formComponent = GetComponentFromEvent(event, form); var submission = GetFormattedSubmission(data, formComponent); SaveReview(formName, submission, form) .then(function (response) { postSubmissionSteps(form); }); }); form.on('SaveReviewAndRedirect', function (data) { var formComponent = GetComponentFromEvent(event, form); var submission = GetFormattedSubmission(data, formComponent); SaveReviewAndRedirect(formName, submission, form) .then(function (response) { postSubmissionSteps(form); }); }); form.on('SaveFormAndContinue', function (data) { if (!form.checkValidity()) { // I'm triggering the default formIO submitt, because // It will properly fire the validations in screen. form.submit(); document.getElementsByClassName("has-error alert alert-danger")[0].scrollIntoView(true); return; } var formComponent = GetComponentFromEvent(event, form); var submission = GetFormattedSubmission(data, formComponent); SubmitFormDataToService(formName, submission, selectedImisId, next, false, "", "Started", form) .then(function (response) { postSubmissionSteps(form); form.onChange(); form.nextPage(); }); }); form.on('GoToPreviousPage', function() { form.prevPage(); }); form.on('GoToNextPage', function() { if (!form.checkValidity()) { // I'm triggering the default formIO submitt, because // It will properly fire the validations in screen. form.submit(); return; } form.nextPage(); }); form.on('SaveFormAndApprove', function (data) { var formComponent = GetComponentFromEvent(event, form); var submission = GetFormattedSubmission(data, formComponent); SubmitFormDataToService(formName, submission, selectedImisId, next, false, "", "Approved", form) .then(function (response) { postSubmissionSteps(form); }); }); form.on('SaveFormAndDecline', function (data) { var formComponent = GetComponentFromEvent(event, form); var submission = GetFormattedSubmission(data, formComponent); SubmitFormDataToService(formName, submission, selectedImisId, next, false, "", "Declined", form) .then(function (response) { postSubmissionSteps(form); }); }); form.on('CustomSubmitForm', function (submission) { var defaultAppStatus = submission["applicationStatus"]; submission["applicationStatus"] = "Submitted"; form.emit('submitButton', {state: 'submitted'}); }); // Triggered when button is clicked and CheckIfEmailExists event is called form.on('CheckIfEmailExists', function (submission) { CheckIfEmailExists(form, submission); }); PrePopulateFormData(selectedImisId, loggedInImisId, formName, form).then(function () { refreshForm(form); if (form.page != null) { let formInfo = fetchFormInfo(); const arbitraryDelayBeforeSetPage = 500; // In the case that the data[] object is used in conditionals to show/hide tabs, we need to wait for form.io // to first run the conditionals and show/hide the tabs, otherwise the form.setPage will fail saying it could // not find the tab. Using setTimeout does the job but ideally we should use an event provided by form.io. if (formInfo != null) { setTimeout(function () { form.setPage(formInfo.pageId); }.bind(this), arbitraryDelayBeforeSetPage); } form.on("pagesChanged", () => { formInitialized(); }); } form.on("change", (e, t, n) => { formInitialized(); }); form.on("render", () => { formInitialized(); }); form.on("redraw", () => { formInitialized(); }); form.on("initialized", () => { formInitialized(); }); resolveFormReadyPromise(); }); }); } function disableBreadcrumbs(settings) { if (!settings || !settings.disableBreadcrumbNavStatus) return; var disableBreadcrumbNavStatus = settings.disableBreadcrumbNavStatus?.split(",") ?? []; isBreadcrumbsDisabled = disableBreadcrumbNavStatus.some(function (status) { return status === formObj.data.applicationStatus; }); if (!isBreadcrumbsDisabled) return; //Disable point events for pagination jQuery('#formio .pagination li, .page-item').css('pointer-events', 'none'); } function postSubmissionSteps(form) { if (isRedirecting) return; return PrePopulateFormData(selectedImisId, loggedInImisId, formName, form).then(function () { refreshForm(form); }); } function refreshForm(form) { form.refresh(); form.triggerChange(); form.triggerRedraw(); window.scrollTo(0,0); } function RedirectSSO() { window.location.href = staticFileUrl + "/Redirects/StoreRedirect.html?returnUri=" + encodeURIComponent(gWebSiteRoot + ssoUrl) + "&redirectUri=" + encodeURIComponent(window.location.href); } function GetAuthKeyOrRedirectSSO() { let bsiFormsAuth = localStorage.getItem(bsiFormsAuthorization); if (bsiFormsAuth == null) { if (GetAlertBoxMessages().Enable){ alert(GetAlertBoxMessages().CreateSecureConnectionMessage); } RedirectSSO(); } return bsiFormsAuth; } function CheckForAuthKey() { GetAuthKeyOrRedirectSSO(); CheckPermissionsAndDisplayForm(); } function storeFormInfo(form) { if (!form.page) return; const pageId = form.page; const formInfo = { "pageId": pageId, "formName": formName, }; const bsiFormsInformationCacheKey = getBsiFormsInformationCacheKey(); let formsInfoCache; if (bsiFormsInformationCacheKey) formsInfoCache = JSON.parse(window.localStorage.getItem(bsiFormsInformationCacheKey)); if (!formsInfoCache) { formsInfoCache = []; formsInfoCache.push(formInfo); } else { let found = false; for (let x = 0; x < formsInfoCache.length; x++) { // Remove earliest formInfo object when the array hits 10 items if (formsInfoCache.length === bsiFormsArrayLimit) formsInfoCache.shift(); // Set the wizard page number when it exists and the form names match if (formsInfoCache[x].formName === formName && formsInfoCache[x].pageId) { found = true; formsInfoCache[x].pageId = pageId; } } if (!found) formsInfoCache.push(formInfo); } if (bsiFormsInformationCacheKey) window.localStorage.setItem(bsiFormsInformationCacheKey, JSON.stringify(formsInfoCache)); } function fetchFormInfo() { let formsInfoCache; const bsiFormsInformationCacheKey = getBsiFormsInformationCacheKey(); if (bsiFormsInformationCacheKey) formsInfoCache = JSON.parse(window.localStorage.getItem(bsiFormsInformationCacheKey)); if (!formsInfoCache) return null; for (let i = 0; i < formsInfoCache.length; i++) { const formInfo = formsInfoCache[i]; if (!formInfo.formName) continue; if (formInfo.formName === formName) return formInfo; } } function findWorkflowForm(form) { if (form.wizard == null || form.wizard.properties == null || form.wizard.properties.workflow == null) { return null; } let workflowId = form.wizard.properties.workflow; if (workflowId === "true") { form.options.breadcrumbSettings = {clickable: !workflowId}; } } function formInitialized() { getIpartSettings().then((settings) => { disableBreadcrumbs(settings); }); } /** * This will return a client side cache key for form information * * @returns BSI Forms Information Cache Key */ function getBsiFormsInformationCacheKey() { if (formSubmissionId && formSubmissionId != "new") return `BsiFormsInformation-${formSubmissionId}`; return ""; } function autorun() { var target = document.querySelector('#formio'); // create an observer instance var observer = new MutationObserver(function (mutations) { //Traverse through the newly added element and find "multiple" attribute to stack in an array for (var k = 0; k < jQuery("[multiple]").length; k++) { var apiName = jQuery("[multiple]")[k].getAttribute("name"); apiName = apiName.replace('data', ''); apiName = apiName.replace('[', ''); apiName = apiName.replace(']', ''); if (!apiKeyList.includes(apiName)) { apiKeyList.push(apiName); } } //Traverse through the newly added element and find "pattern" attribute and remove it if it is set to blank for (var k = 0; k < jQuery("[pattern]").length; k++) { if (jQuery("[pattern]")[k].pattern.toString() === "\\d*") { jQuery("[pattern]")[k].removeAttribute("pattern"); } } for (var m = 0; m < mutations.length; m++) { var added = mutations[m].addedNodes; for (var i = 0; i < added.length; i++) { checkLang(); var node = added[i]; if (node.nodeType != 1) // not Node.ELEMENT_NODE continue; if (node.getAttribute("class") != null) { var classList = node.getAttribute("class").split(" "); for (var j = 0; j < classList.length; j++) { var className = classList[j]; //TODO : Pass alert body to modal body (All alerts will appear inside alert message with default style) if (className === "alert-danger") { //console.log("Danger"); //console.log(node); } else if (className === "alert-success") { //console.log("Success"); //console.log(node.childNodes[0]); node.childNodes[0].innerHTML = "Submission Saved"; } } } } } }); // configuration of the observer: var config = {attributes: true, childList: true, characterData: true, subtree: true}; // pass in the target node, as well as the observer options observer.observe(target, config); function inputChangeValidator(event) { //console.log(event); } target.addEventListener( "div", inputChangeValidator ); } //OnPageLoad if (window.addEventListener) window.addEventListener("load", autorun, false); else if (window.attachEvent) window.attachEvent("onload", autorun); else window.onload = autorun; //OnDomLoad //if (document.addEventListener) document.addEventListener("DOMContentLoaded", autorun, false); //else if (document.attachEvent) document.attachEvent("onreadystatechange", autorun); //else window.onload = autorun; var ua = window.navigator.userAgent; var isIE = /MSIE|Trident/.test(ua); if (isIE) { (function (self) { 'use strict'; if (self.fetch) { return } var support = { searchParams: 'URLSearchParams' in self, iterable: 'Symbol' in self && 'iterator' in Symbol, blob: 'FileReader' in self && 'Blob' in self && (function () { try { new Blob() return true } catch (e) { return false } })(), formData: 'FormData' in self, arrayBuffer: 'ArrayBuffer' in self } if (support.arrayBuffer) { var viewClasses = [ '[object Int8Array]', '[object Uint8Array]', '[object Uint8ClampedArray]', '[object Int16Array]', '[object Uint16Array]', '[object Int32Array]', '[object Uint32Array]', '[object Float32Array]', '[object Float64Array]' ] var isDataView = function (obj) { return obj && DataView.prototype.isPrototypeOf(obj) } var isArrayBufferView = ArrayBuffer.isView || function (obj) { return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1 } } function normalizeName(name) { if (typeof name !== 'string') { name = String(name) } if (/[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(name)) { throw new TypeError('Invalid character in header field name') } return name.toLowerCase() } function normalizeValue(value) { if (typeof value !== 'string') { value = String(value) } return value } // Build a destructive iterator for the value list function iteratorFor(items) { var iterator = { next: function () { var value = items.shift() return { done: value === undefined, value: value } } } if (support.iterable) { iterator[Symbol.iterator] = function () { return iterator } } return iterator } function Headers(headers) { this.map = {} if (headers instanceof Headers) { headers.forEach(function (value, name) { this.append(name, value) }, this) } else if (Array.isArray(headers)) { headers.forEach(function (header) { this.append(header[0], header[1]) }, this) } else if (headers) { Object.getOwnPropertyNames(headers).forEach(function (name) { this.append(name, headers[name]) }, this) } } Headers.prototype.append = function (name, value) { name = normalizeName(name) value = normalizeValue(value) var oldValue = this.map[name] this.map[name] = oldValue ? oldValue + ',' + value : value } Headers.prototype['delete'] = function (name) { delete this.map[normalizeName(name)] } Headers.prototype.get = function (name) { name = normalizeName(name) return this.has(name) ? this.map[name] : null } Headers.prototype.has = function (name) { return this.map.hasOwnProperty(normalizeName(name)) } Headers.prototype.set = function (name, value) { this.map[normalizeName(name)] = normalizeValue(value) } Headers.prototype.forEach = function (callback, thisArg) { for (var name in this.map) { if (this.map.hasOwnProperty(name)) { callback.call(thisArg, this.map[name], name, this) } } } Headers.prototype.keys = function () { var items = [] this.forEach(function (value, name) { items.push(name) }) return iteratorFor(items) } Headers.prototype.values = function () { var items = [] this.forEach(function (value) { items.push(value) }) return iteratorFor(items) } Headers.prototype.entries = function () { var items = [] this.forEach(function (value, name) { items.push([name, value]) }) return iteratorFor(items) } if (support.iterable) { Headers.prototype[Symbol.iterator] = Headers.prototype.entries } function consumed(body) { if (body.bodyUsed) { return Promise.reject(new TypeError('Already read')) } body.bodyUsed = true } function fileReaderReady(reader) { return new Promise(function (resolve, reject) { reader.onload = function () { resolve(reader.result) } reader.onerror = function () { reject(reader.error) } }) } function readBlobAsArrayBuffer(blob) { var reader = new FileReader() var promise = fileReaderReady(reader) reader.readAsArrayBuffer(blob) return promise } function readBlobAsText(blob) { var reader = new FileReader() var promise = fileReaderReady(reader) reader.readAsText(blob) return promise } function readArrayBufferAsText(buf) { var view = new Uint8Array(buf) var chars = new Array(view.length) for (var i = 0; i < view.length; i++) { chars[i] = String.fromCharCode(view[i]) } return chars.join('') } function bufferClone(buf) { if (buf.slice) { return buf.slice(0) } else { var view = new Uint8Array(buf.byteLength) view.set(new Uint8Array(buf)) return view.buffer } } function Body() { this.bodyUsed = false this._initBody = function (body) { this._bodyInit = body if (!body) { this._bodyText = '' } else if (typeof body === 'string') { this._bodyText = body } else if (support.blob && Blob.prototype.isPrototypeOf(body)) { this._bodyBlob = body } else if (support.formData && FormData.prototype.isPrototypeOf(body)) { this._bodyFormData = body } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { this._bodyText = body.toString() } else if (support.arrayBuffer && support.blob && isDataView(body)) { this._bodyArrayBuffer = bufferClone(body.buffer) // IE 10-11 can't handle a DataView body. this._bodyInit = new Blob([this._bodyArrayBuffer]) } else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) { this._bodyArrayBuffer = bufferClone(body) } else { throw new Error('unsupported BodyInit type') } if (!this.headers.get('content-type')) { if (typeof body === 'string') { this.headers.set('content-type', 'text/plain;charset=UTF-8') } else if (this._bodyBlob && this._bodyBlob.type) { this.headers.set('content-type', this._bodyBlob.type) } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8') } } } if (support.blob) { this.blob = function () { var rejected = consumed(this) if (rejected) { return rejected } if (this._bodyBlob) { return Promise.resolve(this._bodyBlob) } else if (this._bodyArrayBuffer) { return Promise.resolve(new Blob([this._bodyArrayBuffer])) } else if (this._bodyFormData) { throw new Error('could not read FormData body as blob') } else { return Promise.resolve(new Blob([this._bodyText])) } } this.arrayBuffer = function () { if (this._bodyArrayBuffer) { return consumed(this) || Promise.resolve(this._bodyArrayBuffer) } else { return this.blob().then(readBlobAsArrayBuffer) } } } this.text = function () { var rejected = consumed(this) if (rejected) { return rejected } if (this._bodyBlob) { return readBlobAsText(this._bodyBlob) } else if (this._bodyArrayBuffer) { return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer)) } else if (this._bodyFormData) { throw new Error('could not read FormData body as text') } else { return Promise.resolve(this._bodyText) } } if (support.formData) { this.formData = function () { return this.text().then(decode) } } this.json = function () { return this.text().then(JSON.parse) } return this } // HTTP methods whose capitalization should be normalized var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'] function normalizeMethod(method) { var upcased = method.toUpperCase() return (methods.indexOf(upcased) > -1) ? upcased : method } function Request(input, options) { options = options || {} var body = options.body if (input instanceof Request) { if (input.bodyUsed) { throw new TypeError('Already read') } this.url = input.url this.credentials = input.credentials if (!options.headers) { this.headers = new Headers(input.headers) } this.method = input.method this.mode = input.mode if (!body && input._bodyInit != null) { body = input._bodyInit input.bodyUsed = true } } else { this.url = String(input) } this.credentials = options.credentials || this.credentials || 'omit' if (options.headers || !this.headers) { this.headers = new Headers(options.headers) } this.method = normalizeMethod(options.method || this.method || 'GET') this.mode = options.mode || this.mode || null this.referrer = null if ((this.method === 'GET' || this.method === 'HEAD') && body) { throw new TypeError('Body not allowed for GET or HEAD requests') } this._initBody(body) } Request.prototype.clone = function () { return new Request(this, { body: this._bodyInit }) } function decode(body) { var form = new FormData() body.trim().split('&').forEach(function (bytes) { if (bytes) { var split = bytes.split('=') var name = split.shift().replace(/\+/g, ' ') var value = split.join('=').replace(/\+/g, ' ') form.append(decodeURIComponent(name), decodeURIComponent(value)) } }) return form } function parseHeaders(rawHeaders) { var headers = new Headers() rawHeaders.split(/\r?\n/).forEach(function (line) { var parts = line.split(':') var key = parts.shift().trim() if (key) { var value = parts.join(':').trim() headers.append(key, value) } }) return headers } Body.call(Request.prototype) function Response(bodyInit, options) { if (!options) { options = {} } this.type = 'default' this.status = 'status' in options ? options.status : 200 this.ok = this.status >= 200 && this.status < 300 this.statusText = 'statusText' in options ? options.statusText : 'OK' this.headers = new Headers(options.headers) this.url = options.url || '' this._initBody(bodyInit) } Body.call(Response.prototype) Response.prototype.clone = function () { return new Response(this._bodyInit, { status: this.status, statusText: this.statusText, headers: new Headers(this.headers), url: this.url }) } Response.error = function () { var response = new Response(null, { status: 0, statusText: '' }) response.type = 'error' return response } var redirectStatuses = [301, 302, 303, 307, 308] Response.redirect = function (url, status) { if (redirectStatuses.indexOf(status) === -1) { throw new RangeError('Invalid status code') } return new Response(null, { status: status, headers: { location: url } }) } self.Headers = Headers self.Request = Request self.Response = Response self.fetch = function (input, init) { return new Promise(function (resolve, reject) { var request = new Request(input, init) var xhr = new XMLHttpRequest() xhr.onload = function () { var options = { status: xhr.status, statusText: xhr.statusText, headers: parseHeaders(xhr.getAllResponseHeaders() || '') } options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL') var body = 'response' in xhr ? xhr.response : xhr.responseText resolve(new Response(body, options)) } xhr.onerror = function () { reject(new TypeError('Network request failed')) } xhr.ontimeout = function () { reject(new TypeError('Network request failed')) } xhr.open(request.method, request.url, true) if (request.credentials === 'include') { xhr.withCredentials = true } if ('responseType' in xhr && support.blob) { xhr.responseType = 'blob' } request.headers.forEach(function (value, name) { xhr.setRequestHeader(name, value) }) xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit) }) } self.fetch.polyfill = true })(typeof self !== 'undefined' ? self : this); } //This is used to manage all the queries that are happening :D let selectQueryManager = {}; //This function is being exposed to form builders so they can populate fields using iqas in a calculated value. //form.io does not check if data returned is a promise, so we need to make sure we handle all the promises ourselves. //valueProperty and displayValueFunction are optional. function UseIqaData(instance, iqaPath, parameters, valueProperty, displayValueFunction) { //Create object to represent cancellable running query. let runningQueryInfo = { 'instance': instance }; if (displayValueFunction == null) displayValueFunction = DefaultIqaValue; let parametersQuery = ""; if (parameters != null) { for (let i = 0; i < parameters.length; i++) { if (i > 0) parametersQuery += "&"; parametersQuery += "Parameter=" + parameters[i]; } } let fullPath = iqaPath; if (fullPath.includes("?")) { fullPath += "&" + parametersQuery; } else { fullPath += "?" + parametersQuery; } var queryIdentity = instance.id; if(instance.inDataGrid) queryIdentity = queryIdentity + instance.row; let queryPromise = ExecuteIqaQuery(fullPath, null, queryIdentity); if (!queryPromise) return; return queryPromise.then(function (jsonBody) { let displayValue = displayValueFunction(jsonBody, valueProperty); if (!this.isCancelled) this.instance.setValue(displayValue, { //Use noUpdateEvent to prevent this from causing onChangeEvents and calling itself all the time. noUpdateEvent: true }); }.bind(runningQueryInfo)); } function DefaultIqaValue(iqaResults, valueProperty) { if (iqaResults.length === 0 || iqaResults[0].value == null) return ""; let resultValue = iqaResults[0].value; if (!(resultValue instanceof Object)) return resultValue; if (valueProperty == null) return ""; let result = resultValue[valueProperty]; if (result == null) return ""; return result; } function ExecuteIqaQuery(path, options, instanceName) { const matches = path.match(QueryNameRegex); let queryPath = matches[0]; let queryParams = new URLSearchParams(); if (matches[0].includes("?")) { queryPath = matches[0].split("?")[0]; queryParams = new URLSearchParams(matches[0].split("?")[1]); } // Delete limit from query params as we don't want this in our cache key if (queryParams.has("limit")) queryParams.delete("limit"); // If offset is not provided add offset=0 so we don't unnecessarily pull already cached records if (!queryParams.has("offset")) queryParams.append("offset", "0"); if (queryParams && queryParams.size > 0) { queryKey = `${queryPath}?${decodeURIComponent(queryParams.toString())}${instanceName}`; } else { queryKey = `${queryPath}${instanceName}`; } if (!(queryKey in selectQueryManager)) { selectQueryManager[queryKey] = { promise: undefined, queryPath: "", results: [] } } const currentQuery = selectQueryManager[queryKey]; let queryPromise; // If this query is cached, return the promise if (currentQuery.queryPath === queryPath && currentQuery.promise) { queryPromise = currentQuery.promise; } else { // No cached promise found pull from api let debounceTime = SelectSearchDebounceTime; // If non-zero offset, user is scrolling. Make query immediately if (queryPath.includes("offset") && !queryPath.includes("offset=0")) debounceTime = 0; queryPromise = new Promise((resolve, reject) => { window.setTimeout(() => { fetchFromService(path, options).then(function (response) { switch (response.status) { case 200: case 206: return response.json(); default: return []; } }).then(function (responseData) { currentQuery.results = responseData; resolve(responseData); }).catch(function (err) { reject(err); }); }, debounceTime); }); currentQuery.queryPath = queryPath; currentQuery.promise = queryPromise; } return queryPromise; }