const { isEmpty: _isEmpty, isEqual: _isEqual, isNaN: _isNaN, isNumber: _isNumber, merge, omit, omitBy, parseInt: _parseInt, pick } = require("lodash"); const fetcher = (url) => fetch(url).then((res) => res.json()); const parse = (value) => { try { return JSON.parse(value); } catch (_) { return value; } }; const stringify = (value, opts) => { try { return opts ? JSON.stringify(value, null, opts) : JSON.stringify(value); } catch (_) { return value; } }; const btoa = (value, options = {}) => { const { urlsafe = true } = options; let result = Buffer.from(value, "binary").toString("base64"); if (urlsafe) result = result.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); return result; }; const atob = (value, options = {}) => { const { urlsafe = true } = options; if (urlsafe) value = value.replace(/-/g, "+").replace(/_/g, "/"); if (urlsafe && value && value.toString().length % 4 !== 0) value += "===".slice(0, 4 - (value.toString().length % 4)); let result = Buffer.from(value, "base64").toString("binary"); return result.toString(); }; const querystring = (params = {}, options = {}) => { if (!isObject(params)) return; const { exclude = [], compress = false } = options; const encodeAndCompress = (param) => { const json = stringify(param); return compress ? pako.deflate(json, { to: "string" }) : json; // return compress ? encodeURI(pako.deflate(json, { to: "string" })) : encodeURI(json); }; const props = {}; for (let key of Object.keys(params)) { props[key] = isObject(params[key]) ? encodeAndCompress(params[key]) : params[key]; } return new URLSearchParams(omit(props, exclude)).toString(); }; const safeEncodeURIComponent = (v) => { v = parse(v); // try to parse the value try { if (v && (Array.isArray(v) || isObject(v))) v = stringify(v); // is it an array? or object? stringify } catch (err) { // do nothing use valu v } try { return encodeURIComponent(v); // encodeURIComponent the value and resurn it } catch (err) { return v; // fail and return v } }; const language = () => { return ( (navigator.languages && navigator.languages[0]) || navigator.language || navigator.userLanguage ); }; const uuid = () => Math.random().toString(36).substr(2, 9); const uniqueId = (x = 4, j = "-") => [...Array(x).keys()].map(() => uuid()).join(j); const normalizeUrl = (url) => { let pattern = /^((http|https|ftp):\/\/)/; if (url && url !== "") { url = url.toString(); if (!pattern.test(url) && !url.startsWith("/")) url = "http://" + url; } return url; }; const normalizeJSON = (value, fallback = null) => { if (isString(value)) return parse(value) || fallback; else if (isObject(value)) return value || fallback; else return fallback; }; const safeSearchName = (text = "") => encodeURIComponent(text || "") .toString() .trim() .replace(/%20/g, "+"); const kebabCase = (text = "") => (text || "") .toString() // convert to string to avoid issues .replace(/\s+/, "-") // spaces to dash .replace(/([a-z])([A-Z])/g, "$1-$2") // kebab the string .replace(/[\s_]+/g, "-") .replace(/-+/g, "-") // only one dash per kebab .replace(/^-/g, "") // no starting dash .replace(/-$/g, "") // no ending dash .replace(/[^0-9a-z-_]/gi, "") // only alpha numeric .toLowerCase(); // lowercase only const safeIdName = (text = "") => kebabCase((text || "").toString().replace(/\s\s+/g, " ")); const slugify = (text = "") => kebabCase(text || ""); const baseUrl = (path) => { if (!hasWindow()) return `${BASE_URL}${path}`; const { location: { origin } = {} } = window; return `${origin || BASE_URL}${path || ""}`; }; const toBoolean = (value) => { switch (value) { case true: case "true": case "True": case "TRUE": case 1: case "1": case "on": case "On": case "ON": case "yes": case "Yes": case "YES": return true; default: return false; } }; const isBlank = (value) => (_isEmpty(value) && !_isNumber(value)) || _isNaN(value); const isEqual = (v1, v2) => _isEqual(v1, v2); const isEmpty = (value) => { let result, type = typeof value; switch (type.toLowerCase()) { case "null": case "undefined": result = true; break; case "boolean": case "number": case "bigint": result = value === undefined || value === null ? true : false; break; case "symbol": case "object": try { if (typeofDate(value)) { result = false; } else if (isArray(value)) { result = !value || value.length === 0 ? true : false; } else if (value) { let entries = Object.entries(value); result = !entries || entries.length === 0 ? true : false; } else { result = true; } } catch (err) { log.error("isEmpty", type, err.message || err); result = true; } break; case "date": result = false; break; case "string": default: result = !value || value.trim() === "" || value === undefined || value === null ? true : false; break; } return result; }; const isFunction = (o) => { return o && typeof o === "function" ? true : false; }; const isJson = (o) => { return isArray(o) || isObject(o) ? true : hasJsonStructure(o); }; const isReactElement = (o) => { return o?.["$$typeof"] && o["$$typeof"] === Symbol.for("react.element"); }; const hasJsonStructure = (o) => { if (typeof o !== "string") return false; try { const result = JSON.parse(o); const type = Object.prototype.toString.call(result); return type === "[object Object]" || type === "[object Array]"; } catch (err) { return false; } }; const isString = (o) => { return o && typeof o === "string" ? true : false; }; const isArray = (o) => { return o && Array.isArray(o) ? true : false; }; const isObject = (o) => { return o && typeof o === "object" ? true : false; }; const isUndefined = (o) => { return typeof o === "undefined" ? true : false; }; const hasWindow = () => { return typeof window !== "undefined" && window ? true : false; }; const hasNavigator = () => { return typeof navigator !== "undefined" && navigator ? true : false; }; const hasDocument = () => { return hasWindow() && typeof document !== "undefined" && document ? true : false; }; const typeofDate = (value) => { return ( value && Object.prototype.toString.call(value) === "[object Date]" && !isNaN(value) ); }; const isDate = (value) => { const regex = /^\d{1,2}\/\d{1,2}\/\d{4}$/; return value && regex.test(value) ? true : false; }; const isHex = (h) => { try { return /^#[0-9A-F]{6}$/i.test(h); } catch (err) { return false; } }; const isNullOrFalse = (value) => { return value === null || value === undefined || value === false ? true : false; }; const isNullOrZero = (value) => { return value === null || value === undefined || value == 0 ? true : false; }; const formatCurrency = ( value, formatter = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" }) ) => { return (formatter && value && formatter.format(value)) || 0; }; const isInt = (value) => { if (value === null || value === undefined || value === "") return false; return /^-?[0-9]+$/.test(value); }; const isNumber = (value) => { return value % 1 === 0 ? true : false; }; const isUrl = (value) => { let url; try { url = new URL(value); } catch (_) { return false; } return ( url && url.protocol && ["http:", "https:", "ftp:", "ftps:"].includes(url.protocol) ); }; const clone = (obj = {}) => { let result = {}; try { result = JSON.parse(JSON.stringify(obj)); } catch (err) { log.error("clone error", err.message || err); } return result; }; const isValidUrl = (str) => { let url; try { url = new URL(str); } catch (_) { return false; } return url.protocol === "http:" || url.protocol === "https:"; }; const asyncSome = async (arr, predicate) => { for (let a of arr) { if (await predicate(a)) return true; } return false; }; const asyncEvery = async (arr, predicate) => { for (let a of arr) { if (!(await predicate(a))) return false; } return true; }; const objectToString = (value, delim = ",") => { try { if (!value) return null; if (isString(value)) return value; else if (isArray(value)) return value.join(delim); else return stringify(value, null, 2); } catch (err) { return null; } }; const stringToArray = (value, delim = ",", clean = true) => { if (!value) return []; if (value && Array.isArray(value)) return value; let result = []; try { result = (value && value.toString().split(delim)) || []; if (clean) result = (result && result.map((v) => v.trim()).filter((v) => v && !isEmpty(v))) || []; } catch (err) { result = []; } return result; }; const stringToObject = (value, delim = ",", clean = true) => { try { // if (!value) return {}; if (isObject(value)) return value; else if (value.includes("[") && value.includes("]")) return parse(value); else if (value.includes("{") && value.includes("}")) return parse(value); else { if (clean) value = stripNewlines(value, delim) || ""; return ( value && value .toString() .split(delim) .map((v) => v.trim()) .filter((v) => !isEmpty(v)) ); } } catch (err) { return {}; } }; const stripNewlines = (value, subs = "") => { if (!value) return; let result; try { result = value.toString().replace(/(\r\n|\r|\n)/g, subs); } catch (err) { // do nothing } return result; }; const capitalizeFirstLetter = (s) => s.charAt(0).toUpperCase() + s.slice(1); const reload = () => { if (hasWindow() && window.location) window.location.reload(false); }; const hasOwnProperty = (obj, key) => { return obj && key && Object.prototype.hasOwnProperty.call(obj, key); }; const hasKey = (needle = "", haystack = {}) => { if (!needle || isEmpty(haystack)) return false; let keys = Object.keys(haystack) || []; return needle instanceof RegExp ? keys.some((o) => o && needle.test(o)) : keys.some((o) => o && ciEquals(needle, o)); }; const isIterable = (obj) => { if (obj) return typeof obj[Symbol.iterator] === "function"; return false; }; const hasHtml = (value) => value && /<\/?[^>]*>/.test(value); const firstOfList = (value) => (isArray(value) ? value[0] : value); const truncateArray = (list = [], max = 7) => { if (list.length < max) return list; return [...list.slice(0, max), "..."]; }; module.exports = { atob, baseUrl, btoa, capitalizeFirstLetter, clone, fetcher, firstOfList, formatCurrency, hasDocument, hasJsonStructure, hasHtml, hasKey, hasNavigator, hasOwnProperty, hasWindow, isArray, isBlank, isDate, isEqual, isEmpty, isFunction, isHex, isInt, isIterable, isJson, isNumber, isNullOrFalse, isNullOrZero, isObject, isReactElement, isValidUrl, isUrl, isUndefined, isString, language, merge, normalizeUrl, normalizeJSON, objectToString, omit, omitBy, parse, pick, querystring, reload, safeIdName, safeEncodeURIComponent, safeSearchName, slugify, stringify, stringToArray, stringToObject, stripNewlines, toBoolean, truncateArray, typeofDate, uuid, uniqueId };