let Utils = {}; let locale; let format; Utils.translate = function (key) { if (!Lang.hasOwnProperty(locale)) { console.warn(`Language '${locale}' is not available. Using default 'en'.`); locale = "en"; } let langObj = Lang[locale]; const keys = key.split("."); for (const k of keys) { if (!langObj.hasOwnProperty(k)) { console.warn(`Translation key '${key}' not found for language '${locale}'.`); return "missing_translation"; } langObj = langObj[k]; } return langObj; }; Utils.setLocale = function (current_locale) { locale = current_locale; }; Utils.setFormat = function (current_format) { format = current_format; }; Utils.loadLanguageFile = async function () { try { await new Promise((resolve, reject) => { let fileUrl = `lang/${locale}.js`; const script = document.createElement("script"); script.src = fileUrl; script.onload = () => { resolve(); }; script.onerror = (event) => { reject(new Error("Failed to load language file: " + fileUrl)); }; document.body.appendChild(script); const timeoutDuration = 10000; // 10 seconds setTimeout(() => { reject(new Error("Timeout: The script took too long to load the language file: " + fileUrl)); }, timeoutDuration); }); } catch (error) { if (locale !== "en") { console.warn(`Language '${locale}' is not available. Using default 'en'.`); Utils.setLocale("en"); await Utils.loadLanguageFile(); } else { throw error; } } }; Utils.loadLanguageModules = async function (utils_module) { Utils.setLocale(utils_module.config.locale); Utils.setFormat(utils_module.config.format); await Utils.loadLanguageFile(); Utils.deepMerge(Lang,utils_module.lang); }; Utils.timeConverter = function (UNIX_timestamp, options = {}) { const timestampMillis = UNIX_timestamp * 1000; const formattedTime = new Date(timestampMillis).toLocaleString(locale, options); return formattedTime; }; Utils.currencyFormat = function (number, decimalPlaces = null) { const options = { style: "currency", currency: format.currency, }; if (decimalPlaces != null) { options.minimumFractionDigits = decimalPlaces; options.maximumFractionDigits = decimalPlaces; } return new Intl.NumberFormat(format.location, options).format(number); }; Utils.numberFormat = function (number, decimalPlaces = null) { const options = {}; if (decimalPlaces != null) { options.minimumFractionDigits = decimalPlaces; options.maximumFractionDigits = decimalPlaces; } return new Intl.NumberFormat(format.location, options).format(number); }; Utils.getCurrencySymbol = function () { const options = { style: "currency", currency: format.currency, minimumFractionDigits: 0, maximumFractionDigits: 0, }; return new Intl.NumberFormat(locale, options).format(0).replace(/\d/g, "").trim(); }; const requestQueue = []; let isProcessing = false; const processQueue = async () => { if (!isProcessing && requestQueue.length > 0) { isProcessing = true; const { event, data, route, cb } = requestQueue.shift(); try { const response = await fetch(Utils.getRoute(route), { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ event, data }), }); if (!response.ok) { throw new Error(`Request failed with status: ${response.status}`); } const responseData = await response.json(); if (cb) { cb(responseData); } else { if (responseData !== 200) { console.log(responseData); } } } catch (error) { console.error(`Error occurred while making a POST request with event: "${event}", data: "${JSON.stringify(data)}", and route: "${Utils.getRoute(route)}": ${error.message}`); } finally { isProcessing = false; setTimeout(function() { processQueue(); }, 200); } } }; Utils.post = function (event, data, route = "post", cb) { requestQueue.push({ event, data, route, cb }); processQueue(); }; let resource_name; Utils.getRoute = function (name) { return `https://${resource_name}/${name}`; }; Utils.setResourceName = function (current_resource_name) { resource_name = current_resource_name; }; const modalTemplate = `
Custom Modal Body Text that accept HTML
", { id: "modal-body-text", text: mergedConfig.body }); $modalBody.append($p); } if (mergedConfig.bodyHtml) { $modalBody.append(mergedConfig.bodyHtml); } // Set modal inputs const $form = $modal.find("#form-confirmation-modal"); mergedConfig.inputs.forEach(inputConfig => { const $inputContainer = $("
", { id: "modal-footer-text", text: mergedConfig.footerText }); $modalBody.append($p); } // Set modal buttons const $footer = $modal.find(".modal-footer"); $footer.empty(); mergedConfig.buttons.forEach(button => { const $button = $("", { class: button.class, text: button.text, type: button.type ?? "button" }); if (button.dismiss) { $button.attr("data-dismiss", "modal"); } if (button.action) { $button.on("click", button.action); } $footer.append($button); }); // Set modal form submit $form.on("submit", function (e) { e.preventDefault(); if (config.onSubmit) { config.onSubmit(new FormData(e.target)); } $modal.modal("hide"); }); // Show the modal $modal.modal({ show: true }); // Remove the modal from the DOM when hidden $modal.on("hidden.bs.modal", function () { setTimeout(() => { $(this).remove(); }, 50); }); }; Utils.deepMerge = function (target, source) { for (const key in source) { if (source.hasOwnProperty(key)) { if (typeof source[key] === "function") { target[key] = source[key]; } else if (source[key] instanceof Object && source[key] !== null) { if (!target.hasOwnProperty(key)) { target[key] = {}; } Utils.deepMerge(target[key], source[key]); } else { target[key] = source[key]; } } } }; if (!String.prototype.format) { String.prototype.format = function() { let args = arguments; return this.replace(/{(\d+)}/g, function(match, number) { return typeof args[number] != "undefined" ? args[number] : match ; }); }; } /** * DEPRECATED: Use Utils.onInvalidInput(this) on input events instead. */ Utils.invalidMsg = function (textbox, min = null, max = null) { textbox.setCustomValidity(""); if (textbox.value === "") { textbox.setCustomValidity(Utils.translate("custom_validity.fill_field")); } else if (textbox.validity.typeMismatch) { textbox.setCustomValidity(Utils.translate("custom_validity.invalid_value")); } else if (textbox.validity.rangeUnderflow && min !== null) { textbox.setCustomValidity(Utils.translate("custom_validity.more_than").format(min)); } else if (textbox.validity.rangeOverflow && max !== null) { textbox.setCustomValidity(Utils.translate("custom_validity.less_than").format(max)); } else if (textbox.validity.stepMismatch) { textbox.setCustomValidity(Utils.translate("custom_validity.invalid_value")); } else if (textbox.validity.patternMismatch) { textbox.setCustomValidity(Utils.translate("custom_validity.pattern_mismatch")); } else if (textbox.validity.tooLong) { textbox.setCustomValidity(Utils.translate("custom_validity.too_long")); } else if (textbox.validity.tooShort) { textbox.setCustomValidity(Utils.translate("custom_validity.too_short")); } textbox.reportValidity(); return true; }; Utils.onInvalidInput = function (textbox) { // oninvalid="Utils.onInvalidInput(this)" textbox.setCustomValidity(""); const elementType = textbox.tagName.toLowerCase(); // 'input', 'select', 'textarea' const inputType = elementType === "input" ? textbox.type.toLowerCase() : elementType; // 'text', 'email', 'select', etc. if (textbox.validity.valueMissing || textbox.value === "") { if (inputType == "select") { textbox.setCustomValidity(Utils.translate("custom_validity.select_fill_field")); } else { textbox.setCustomValidity(Utils.translate("custom_validity.fill_field")); } } else if (textbox.validity.typeMismatch || textbox.validity.badInput) { textbox.setCustomValidity(Utils.translate("custom_validity.invalid_value")); } else if (textbox.validity.rangeUnderflow) { const min = $(textbox).attr("min"); textbox.setCustomValidity(Utils.translate("custom_validity.more_than").format(Utils.numberFormat(min))); } else if (textbox.validity.rangeOverflow) { const max = $(textbox).attr("max"); textbox.setCustomValidity(Utils.translate("custom_validity.less_than").format(Utils.numberFormat(max))); } else if (textbox.validity.stepMismatch) { textbox.setCustomValidity(Utils.translate("custom_validity.invalid_value")); } else if (textbox.validity.patternMismatch) { textbox.setCustomValidity(Utils.translate("custom_validity.pattern_mismatch")); } else if (textbox.validity.tooLong) { textbox.setCustomValidity(Utils.translate("custom_validity.too_long")); } else if (textbox.validity.tooShort) { textbox.setCustomValidity(Utils.translate("custom_validity.too_short")); } return true; }; /** * Sorts an object or an array of objects based on specified property paths. * If the input is an object, it converts it to an array of objects, including the original object's key as an `.id` property. * @param {Object|Array} input - The object or array to sort. * @param {Array|String} propertyPath - The path(s) to the property for sorting, can be a single path or an array of paths for multiple criteria. * @param {Boolean} ascending - Whether the sorting should be in ascending order. Defaults to true. * @returns {Array} - The sorted array of objects. */ Utils.sortElement = function(input, propertyPath, ascending = true) { let arrayToSort; // Convert input to an array of objects if it's not already one, including `.id` if (!Array.isArray(input)) { arrayToSort = Object.entries(input).map(([index, item]) => { if (item !== null && (typeof item === "object" || Array.isArray(item))) { return { ...item, id: item.id || index }; } else { return { value: item, id: index }; } }); } else { arrayToSort = input.map((item, index) => ({ ...item, id: item.id || index, })); } // Convert propertyPath to an array if it's not already one if (!Array.isArray(propertyPath)) { propertyPath = [propertyPath]; } // A helper function to safely access nested properties const resolvePath = (object, path) => { return path.split(".").reduce((accumulator, currentValue) => { return accumulator ? accumulator[currentValue] : undefined; }, object); }; // The sorting function return arrayToSort.sort((a, b) => { for (let i = 0; i < propertyPath.length; i++) { const aValue = resolvePath(a, propertyPath[i]); const bValue = resolvePath(b, propertyPath[i]); if (typeof aValue === "string" && typeof bValue === "string") { const comparison = aValue.localeCompare(bValue); if (comparison !== 0) return ascending ? comparison : -comparison; } else { if (aValue < bValue) return ascending ? -1 : 1; if (aValue > bValue) return ascending ? 1 : -1; } } return 0; // if all criteria are equal }); }; Utils.convertFileToBase64 = function (file, callback) { const reader = new FileReader(); reader.onload = function(e) { callback(e.target.result); }; reader.readAsDataURL(file); }; $(function () { window.addEventListener("message", function (event) { let item = event.data; if (item.notification) { vt.showNotification(item.notification, { position: item.position, duration: item.duration, title: item.title, closable: true, focusable: false, callback: undefined, }, item.notification_type); } if (item.dark_theme != undefined) { if(item.dark_theme == 0){ // Light theme $("#utils-css-light").prop("disabled", false); $("#utils-css-dark").prop("disabled", true); } else if(item.dark_theme == 1){ // Dark theme $("#utils-css-dark").prop("disabled", false); $("#utils-css-light").prop("disabled", true); } } }); document.onkeyup = function(data){ if (data.key == "Escape"){ if ($("#confirmation-modal").is(":visible")){ $("#confirmation-modal").modal("hide"); } else if ($(".main").is(":visible")){ $(".modal").modal("hide"); Utils.post("close",""); } } }; // Notification (() => { const toastPosition = { TopLeft: "top-left", TopCenter: "top-center", TopRight: "top-right", MiddleLeft: "middle-left", MiddleRight: "middle-right", BottomLeft: "bottom-left", BottomCenter: "bottom-center", BottomRight: "bottom-right", }; const toastPositionIndex = [ [toastPosition.TopLeft, toastPosition.TopCenter, toastPosition.TopRight], [toastPosition.MiddleLeft, "", toastPosition.MiddleRight], [toastPosition.BottomLeft, toastPosition.BottomCenter, toastPosition.BottomRight], ]; const svgs = { success: "", warning: " ", info: " ", error: " ", }; const styles = ` .vt-container { position: fixed; width: 100%; height: 100vh; top: 0; left: 0; z-index: 9999; display: flex; flex-direction: column; justify-content: space-between; pointer-events: none; } .vt-row { display: flex; justify-content: space-between; } .vt-col { flex: 1; margin: 10px 20px; display: flex; flex-direction: column; align-items: center; } .vt-col.top-left, .vt-col.bottom-left { align-items: flex-start; } .vt-col.top-right, .vt-col.bottom-right { align-items: flex-end; } .vt-col.middle-left { justify-content: center; align-items: flex-start; } .vt-col.middle-right { justify-content: center; align-items: flex-end; } .vt-card { display: flex; justify-content: center; align-items: center; padding: 12px 20px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); border-radius: 4px; margin: 0px; transition: 0.3s all ease-in-out; pointer-events: all; border-left: 3px solid #8b8b8b; cursor: pointer; } .vt-card.success { border-left: 3px solid #6ec05f; } .vt-card.warning { border-left: 3px solid #fed953; } .vt-card.info { border-left: 3px solid #1271ec; } .vt-card.error { border-left: 3px solid #d60a2e; } .vt-card .text-group { margin-left: 15px; } .vt-card h4 { margin: 0; margin-bottom: 2px; font-size: 16px; font-weight: 600; } .vt-card p { margin: 0; font-size: 14px; } `; const styleSheet = document.createElement("style"); styleSheet.innerText = styles.replace((/ |\r\n|\n|\r/gm), ""); document.head.appendChild(styleSheet); const vtContainer = document.createElement("div"); vtContainer.className = "vt-container"; for (const ri of [0, 1, 2]) { const row = document.createElement("div"); row.className = "vt-row"; for (const ci of [0, 1, 2]) { const col = document.createElement("div"); col.className = `vt-col ${toastPositionIndex[ri][ci]}`; row.appendChild(col); } vtContainer.appendChild(row); } document.body.appendChild(vtContainer); window.vt = { options: { title: undefined, position: toastPosition.TopCenter, duration: 10000, closable: true, focusable: true, callback: undefined, }, showNotification(message, options, type) { show(message, options, type); }, }; function show(message = "", options, type) { options = { ...window.vt.options, ...options }; const col = document.getElementsByClassName(options.position)[0]; const vtCard = document.createElement("div"); vtCard.className = `vt-card ${type}`; vtCard.innerHTML += svgs[type]; vtCard.options = { ...options, ...{ message, type: type, yPos: options.position.indexOf("top") > -1 ? "top" : "bottom", isFocus: false, }, }; setVTCardContent(vtCard); setVTCardIntroAnim(vtCard); setVTCardBindEvents(vtCard); autoDestroy(vtCard); col.appendChild(vtCard); } function setVTCardContent(vtCard) { const textGroupDiv = document.createElement("div"); textGroupDiv.className = "text-group"; if (vtCard.options.title) { textGroupDiv.innerHTML = `${vtCard.options.title}`; } textGroupDiv.innerHTML += `${vtCard.options.message}`; vtCard.appendChild(textGroupDiv); } function setVTCardIntroAnim(vtCard) { vtCard.style.setProperty(`margin-${vtCard.options.yPos}`, "-15px"); vtCard.style.setProperty("opacity", "0"); setTimeout(() => { vtCard.style.setProperty(`margin-${vtCard.options.yPos}`, "15px"); vtCard.style.setProperty("opacity", "1"); }, 50); } function setVTCardBindEvents(vtCard) { vtCard.addEventListener("click", () => { if (vtCard.options.closable) { destroy(vtCard); } }); vtCard.addEventListener("mouseover", () => { vtCard.options.isFocus = vtCard.options.focusable; }); vtCard.addEventListener("mouseout", () => { vtCard.options.isFocus = false; autoDestroy(vtCard, vtCard.options.duration); }); } function destroy(vtCard) { vtCard.style.setProperty(`margin-${vtCard.options.yPos}`, `-${vtCard.offsetHeight}px`); vtCard.style.setProperty("opacity", "0"); setTimeout(() => { if(typeof x !== "undefined"){ vtCard.parentNode.removeChild(v); if (typeof vtCard.options.callback === "function") { vtCard.options.callback(); } } }, 500); } function autoDestroy(vtCard) { if (vtCard.options.duration !== 0) { setTimeout(() => { if (!vtCard.options.isFocus) { destroy(vtCard); } }, vtCard.options.duration); } } })(); });
${vtCard.options.message}