diff options
Diffstat (limited to 'src/util-frontend.js')
-rw-r--r-- | src/util-frontend.js | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/src/util-frontend.js b/src/util-frontend.js new file mode 100644 index 0000000..d9bf378 --- /dev/null +++ b/src/util-frontend.js @@ -0,0 +1,215 @@ +import dayjs from "dayjs"; +import { getTimeZones } from "@vvo/tzdb"; +import { localeDirection, currentLocale } from "./i18n"; +import { POSITION } from "vue-toastification"; + +/** + * Returns the offset from UTC in hours for the current locale. + * @param {string} timeZone Timezone to get offset for + * @returns {number} The offset from UTC in hours. + * + * Generated by Trelent + */ +function getTimezoneOffset(timeZone) { + const now = new Date(); + const tzString = now.toLocaleString("en-US", { + timeZone, + }); + const localString = now.toLocaleString("en-US"); + const diff = (Date.parse(localString) - Date.parse(tzString)) / 3600000; + const offset = diff + now.getTimezoneOffset() / 60; + return -offset; +} + +/** + * Returns a list of timezones sorted by their offset from UTC. + * @returns {object[]} A list of the given timezones sorted by their offset from UTC. + * + * Generated by Trelent + */ +export function timezoneList() { + let result = []; + const timeZones = getTimeZones(); + + for (let timezone of timeZones) { + try { + let display = dayjs().tz(timezone.name).format("Z"); + + result.push({ + name: `(UTC${display}) ${timezone.name}`, + value: timezone.name, + time: getTimezoneOffset(timezone.name), + }); + } catch (e) { + // Skipping not supported timezone.name by dayjs + } + } + + result.sort((a, b) => { + if (a.time > b.time) { + return 1; + } + + if (b.time > a.time) { + return -1; + } + + return 0; + }); + + return result; +} + +/** + * Set the locale of the HTML page + * @returns {void} + */ +export function setPageLocale() { + const html = document.documentElement; + html.setAttribute("lang", currentLocale() ); + html.setAttribute("dir", localeDirection() ); +} + +/** + * Get the base URL + * Mainly used for dev, because the backend and the frontend are in different ports. + * @returns {string} Base URL + */ +export function getResBaseURL() { + const env = process.env.NODE_ENV; + if (env === "development" && isDevContainer()) { + return location.protocol + "//" + getDevContainerServerHostname(); + } else if (env === "development" || localStorage.dev === "dev") { + return location.protocol + "//" + location.hostname + ":3001"; + } else { + return ""; + } +} + +/** + * Are we currently running in a dev container? + * @returns {boolean} Running in dev container? + */ +export function isDevContainer() { + // eslint-disable-next-line no-undef + return (typeof DEVCONTAINER === "string" && DEVCONTAINER === "1"); +} + +/** + * Supports GitHub Codespaces only currently + * @returns {string} Dev container server hostname + */ +export function getDevContainerServerHostname() { + if (!isDevContainer()) { + return ""; + } + + // eslint-disable-next-line no-undef + return CODESPACE_NAME + "-3001." + GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN; +} + +/** + * Regex pattern fr identifying hostnames and IP addresses + * @param {boolean} mqtt whether or not the regex should take into + * account the fact that it is an mqtt uri + * @returns {RegExp} The requested regex + */ +export function hostNameRegexPattern(mqtt = false) { + // mqtt, mqtts, ws and wss schemes accepted by mqtt.js (https://github.com/mqttjs/MQTT.js/#connect) + const mqttSchemeRegexPattern = "((mqtt|ws)s?:\\/\\/)?"; + // Source: https://digitalfortress.tech/tips/top-15-commonly-used-regex/ + const ipRegexPattern = `((^${mqtt ? mqttSchemeRegexPattern : ""}((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))$)|(^((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?$))`; + // Source: https://stackoverflow.com/questions/106179/regular-expression-to-match-dns-hostname-or-ip-address + const hostNameRegexPattern = `^${mqtt ? mqttSchemeRegexPattern : ""}([a-zA-Z0-9])?(([a-zA-Z0-9_]|[a-zA-Z0-9_][a-zA-Z0-9\\-_]*[a-zA-Z0-9_])\\.)*([A-Za-z0-9_]|[A-Za-z0-9_][A-Za-z0-9\\-_]*[A-Za-z0-9_])(\\.)?$`; + + return `${ipRegexPattern}|${hostNameRegexPattern}`; +} + +/** + * Get the tag color options + * Shared between components + * @param {any} self Component + * @returns {object[]} Colour options + */ +export function colorOptions(self) { + return [ + { name: self.$t("Gray"), + color: "#4B5563" }, + { name: self.$t("Red"), + color: "#DC2626" }, + { name: self.$t("Orange"), + color: "#D97706" }, + { name: self.$t("Green"), + color: "#059669" }, + { name: self.$t("Blue"), + color: "#2563EB" }, + { name: self.$t("Indigo"), + color: "#4F46E5" }, + { name: self.$t("Purple"), + color: "#7C3AED" }, + { name: self.$t("Pink"), + color: "#DB2777" }, + ]; +} + +/** + * Loads the toast timeout settings from storage. + * @returns {object} The toast plugin options object. + */ +export function loadToastSettings() { + return { + position: POSITION.BOTTOM_RIGHT, + containerClassName: "toast-container mb-5", + showCloseButtonOnHover: true, + + filterBeforeCreate: (toast, toasts) => { + if (toast.timeout === 0) { + return false; + } else { + return toast; + } + }, + }; +} + +/** + * Get timeout for success toasts + * @returns {(number|boolean)} Timeout in ms. If false timeout disabled. + */ +export function getToastSuccessTimeout() { + let successTimeout = 20000; + + if (localStorage.toastSuccessTimeout !== undefined) { + const parsedTimeout = parseInt(localStorage.toastSuccessTimeout); + if (parsedTimeout != null && !Number.isNaN(parsedTimeout)) { + successTimeout = parsedTimeout; + } + } + + if (successTimeout === -1) { + successTimeout = false; + } + + return successTimeout; +} + +/** + * Get timeout for error toasts + * @returns {(number|boolean)} Timeout in ms. If false timeout disabled. + */ +export function getToastErrorTimeout() { + let errorTimeout = -1; + + if (localStorage.toastErrorTimeout !== undefined) { + const parsedTimeout = parseInt(localStorage.toastErrorTimeout); + if (parsedTimeout != null && !Number.isNaN(parsedTimeout)) { + errorTimeout = parsedTimeout; + } + } + + if (errorTimeout === -1) { + errorTimeout = false; + } + + return errorTimeout; +} |