summaryrefslogtreecommitdiffstats
path: root/src/util-frontend.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/util-frontend.js')
-rw-r--r--src/util-frontend.js215
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;
+}