utils.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. const {
  2. isEmpty: _isEmpty,
  3. isEqual: _isEqual,
  4. isNaN: _isNaN,
  5. isNumber: _isNumber,
  6. merge,
  7. omit,
  8. omitBy,
  9. parseInt: _parseInt,
  10. pick
  11. } = require("lodash");
  12. const parse = (value) => {
  13. try {
  14. return JSON.parse(value);
  15. } catch (_) {
  16. return value;
  17. }
  18. };
  19. const stringify = (value, opts) => {
  20. try {
  21. return opts ? JSON.stringify(value, null, opts) : JSON.stringify(value);
  22. } catch (_) {
  23. return value;
  24. }
  25. };
  26. const btoa = (value, options = {}) => {
  27. const { urlsafe = true } = options;
  28. let result = Buffer.from(value, "binary").toString("base64");
  29. if (urlsafe)
  30. result = result.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
  31. return result;
  32. };
  33. const atob = (value, options = {}) => {
  34. const { urlsafe = true } = options;
  35. if (urlsafe) value = value.replace(/-/g, "+").replace(/_/g, "/");
  36. if (urlsafe && value && value.toString().length % 4 !== 0)
  37. value += "===".slice(0, 4 - (value.toString().length % 4));
  38. let result = Buffer.from(value, "base64").toString("binary");
  39. return result.toString();
  40. };
  41. const querystring = (params = {}, options = {}) => {
  42. if (!isObject(params)) return;
  43. const { exclude = [], compress = false } = options;
  44. const encodeAndCompress = (param) => {
  45. const json = stringify(param);
  46. return compress ? pako.deflate(json, { to: "string" }) : json;
  47. // return compress ? encodeURI(pako.deflate(json, { to: "string" })) : encodeURI(json);
  48. };
  49. const props = {};
  50. for (let key of Object.keys(params)) {
  51. props[key] = isObject(params[key])
  52. ? encodeAndCompress(params[key])
  53. : params[key];
  54. }
  55. return new URLSearchParams(omit(props, exclude)).toString();
  56. };
  57. const safeEncodeURIComponent = (v) => {
  58. v = parse(v); // try to parse the value
  59. try {
  60. if (v && (Array.isArray(v) || isObject(v))) v = stringify(v); // is it an array? or object? stringify
  61. } catch (err) {
  62. // do nothing use valu v
  63. }
  64. try {
  65. return encodeURIComponent(v); // encodeURIComponent the value and resurn it
  66. } catch (err) {
  67. return v; // fail and return v
  68. }
  69. };
  70. const language = () => {
  71. return (
  72. (navigator.languages && navigator.languages[0]) ||
  73. navigator.language ||
  74. navigator.userLanguage
  75. );
  76. };
  77. const uuid = () => Math.random().toString(36).substr(2, 9);
  78. const uniqueId = (x = 4, j = "-") =>
  79. [...Array(x).keys()].map(() => uuid()).join(j);
  80. const normalizeUrl = (url) => {
  81. let pattern = /^((http|https|ftp):\/\/)/;
  82. if (url && url !== "") {
  83. url = url.toString();
  84. if (!pattern.test(url) && !url.startsWith("/")) url = "http://" + url;
  85. }
  86. return url;
  87. };
  88. const normalizeJSON = (value, fallback = null) => {
  89. if (isString(value)) return parse(value) || fallback;
  90. else if (isObject(value)) return value || fallback;
  91. else return fallback;
  92. };
  93. const safeSearchName = (text = "") =>
  94. encodeURIComponent(text || "")
  95. .toString()
  96. .trim()
  97. .replace(/%20/g, "+");
  98. const kebabCase = (text = "") =>
  99. (text || "")
  100. .toString() // convert to string to avoid issues
  101. .replace(/\s+/, "-") // spaces to dash
  102. .replace(/([a-z])([A-Z])/g, "$1-$2") // kebab the string
  103. .replace(/[\s_]+/g, "-")
  104. .replace(/-+/g, "-") // only one dash per kebab
  105. .replace(/^-/g, "") // no starting dash
  106. .replace(/-$/g, "") // no ending dash
  107. .replace(/[^0-9a-z-_]/gi, "") // only alpha numeric
  108. .toLowerCase(); // lowercase only
  109. const safeIdName = (text = "") =>
  110. kebabCase((text || "").toString().replace(/\s\s+/g, " "));
  111. const slugify = (text = "") => kebabCase(text || "");
  112. const baseUrl = (path) => {
  113. if (!hasWindow()) return `${BASE_URL}${path}`;
  114. const { location: { origin } = {} } = window;
  115. return `${origin || BASE_URL}${path || ""}`;
  116. };
  117. const toBoolean = (value) => {
  118. switch (value) {
  119. case true:
  120. case "true":
  121. case "True":
  122. case "TRUE":
  123. case 1:
  124. case "1":
  125. case "on":
  126. case "On":
  127. case "ON":
  128. case "yes":
  129. case "Yes":
  130. case "YES":
  131. return true;
  132. default:
  133. return false;
  134. }
  135. };
  136. const isBlank = (value) =>
  137. (_isEmpty(value) && !_isNumber(value)) || _isNaN(value);
  138. const isEqual = (v1, v2) => _isEqual(v1, v2);
  139. const isEmpty = (value) => {
  140. let result,
  141. type = typeof value;
  142. switch (type.toLowerCase()) {
  143. case "null":
  144. case "undefined":
  145. result = true;
  146. break;
  147. case "boolean":
  148. case "number":
  149. case "bigint":
  150. result = value === undefined || value === null ? true : false;
  151. break;
  152. case "symbol":
  153. case "object":
  154. try {
  155. if (typeofDate(value)) {
  156. result = false;
  157. } else if (isArray(value)) {
  158. result = !value || value.length === 0 ? true : false;
  159. } else if (value) {
  160. let entries = Object.entries(value);
  161. result = !entries || entries.length === 0 ? true : false;
  162. } else {
  163. result = true;
  164. }
  165. } catch (err) {
  166. log.error("isEmpty", type, err.message || err);
  167. result = true;
  168. }
  169. break;
  170. case "date":
  171. result = false;
  172. break;
  173. case "string":
  174. default:
  175. result =
  176. !value || value.trim() === "" || value === undefined || value === null
  177. ? true
  178. : false;
  179. break;
  180. }
  181. return result;
  182. };
  183. const isFunction = (o) => {
  184. return o && typeof o === "function" ? true : false;
  185. };
  186. const isJson = (o) => {
  187. return isArray(o) || isObject(o) ? true : hasJsonStructure(o);
  188. };
  189. const isReactElement = (o) => {
  190. return o?.["$$typeof"] && o["$$typeof"] === Symbol.for("react.element");
  191. };
  192. const hasJsonStructure = (o) => {
  193. if (typeof o !== "string") return false;
  194. try {
  195. const result = JSON.parse(o);
  196. const type = Object.prototype.toString.call(result);
  197. return type === "[object Object]" || type === "[object Array]";
  198. } catch (err) {
  199. return false;
  200. }
  201. };
  202. const isString = (o) => {
  203. return o && typeof o === "string" ? true : false;
  204. };
  205. const isArray = (o) => {
  206. return o && Array.isArray(o) ? true : false;
  207. };
  208. const isObject = (o) => {
  209. return o && typeof o === "object" ? true : false;
  210. };
  211. const isUndefined = (o) => {
  212. return typeof o === "undefined" ? true : false;
  213. };
  214. const hasWindow = () => {
  215. return typeof window !== "undefined" && window ? true : false;
  216. };
  217. const hasNavigator = () => {
  218. return typeof navigator !== "undefined" && navigator ? true : false;
  219. };
  220. const hasDocument = () => {
  221. return typeof document !== "undefined" && document ? true : false;
  222. };
  223. const typeofDate = (value) => {
  224. return (
  225. value &&
  226. Object.prototype.toString.call(value) === "[object Date]" &&
  227. !isNaN(value)
  228. );
  229. };
  230. const isDate = (value) => {
  231. const regex = /^\d{1,2}\/\d{1,2}\/\d{4}$/;
  232. return value && regex.test(value) ? true : false;
  233. };
  234. const isHex = (h) => {
  235. try {
  236. return /^#[0-9A-F]{6}$/i.test(h);
  237. } catch (err) {
  238. return false;
  239. }
  240. };
  241. const isNullOrFalse = (value) => {
  242. return value === null || value === undefined || value === false
  243. ? true
  244. : false;
  245. };
  246. const isNullOrZero = (value) => {
  247. return value === null || value === undefined || value == 0 ? true : false;
  248. };
  249. const formatCurrency = (
  250. value,
  251. formatter = new Intl.NumberFormat("en-US", {
  252. style: "currency",
  253. currency: "USD"
  254. })
  255. ) => {
  256. return (formatter && value && formatter.format(value)) || 0;
  257. };
  258. const isInt = (value) => {
  259. if (value === null || value === undefined || value === "") return false;
  260. return /^-?[0-9]+$/.test(value);
  261. };
  262. const isNumber = (value) => {
  263. return value % 1 === 0 ? true : false;
  264. };
  265. const isUrl = (value) => {
  266. let url;
  267. try {
  268. url = new URL(value);
  269. } catch (_) {
  270. return false;
  271. }
  272. return (
  273. url &&
  274. url.protocol &&
  275. ["http:", "https:", "ftp:", "ftps:"].includes(url.protocol)
  276. );
  277. };
  278. const clone = (obj = {}) => {
  279. let result = {};
  280. try {
  281. result = JSON.parse(JSON.stringify(obj));
  282. } catch (err) {
  283. log.error("clone error", err.message || err);
  284. }
  285. return result;
  286. };
  287. const isValidUrl = (str) => {
  288. let url;
  289. try {
  290. url = new URL(str);
  291. } catch (_) {
  292. return false;
  293. }
  294. return url.protocol === "http:" || url.protocol === "https:";
  295. };
  296. const asyncSome = async (arr, predicate) => {
  297. for (let a of arr) {
  298. if (await predicate(a)) return true;
  299. }
  300. return false;
  301. };
  302. const asyncEvery = async (arr, predicate) => {
  303. for (let a of arr) {
  304. if (!(await predicate(a))) return false;
  305. }
  306. return true;
  307. };
  308. const objectToString = (value, delim = ",") => {
  309. try {
  310. if (!value) return null;
  311. if (isString(value)) return value;
  312. else if (isArray(value)) return value.join(delim);
  313. else return stringify(value, null, 2);
  314. } catch (err) {
  315. return null;
  316. }
  317. };
  318. const stringToArray = (value, delim = ",", clean = true) => {
  319. if (!value) return [];
  320. if (value && Array.isArray(value)) return value;
  321. let result = [];
  322. try {
  323. result = (value && value.toString().split(delim)) || [];
  324. if (clean)
  325. result =
  326. (result &&
  327. result.map((v) => v.trim()).filter((v) => v && !isEmpty(v))) ||
  328. [];
  329. } catch (err) {
  330. result = [];
  331. }
  332. return result;
  333. };
  334. const stringToObject = (value, delim = ",", clean = true) => {
  335. try {
  336. // if (!value) return {};
  337. if (isObject(value)) return value;
  338. else if (value.includes("[") && value.includes("]")) return parse(value);
  339. else if (value.includes("{") && value.includes("}")) return parse(value);
  340. else {
  341. if (clean) value = stripNewlines(value, delim) || "";
  342. return (
  343. value &&
  344. value
  345. .toString()
  346. .split(delim)
  347. .map((v) => v.trim())
  348. .filter((v) => !isEmpty(v))
  349. );
  350. }
  351. } catch (err) {
  352. return {};
  353. }
  354. };
  355. const stripNewlines = (value, subs = "") => {
  356. if (!value) return;
  357. let result;
  358. try {
  359. result = value.toString().replace(/(\r\n|\r|\n)/g, subs);
  360. } catch (err) {
  361. // do nothing
  362. }
  363. return result;
  364. };
  365. const capitalizeFirstLetter = (s) => s.charAt(0).toUpperCase() + s.slice(1);
  366. const reload = () => {
  367. if (hasWindow() && window.location) window.location.reload(false);
  368. };
  369. const hasOwnProperty = (obj, key) => {
  370. return obj && key && Object.prototype.hasOwnProperty.call(obj, key);
  371. };
  372. const hasKey = (needle = "", haystack = {}) => {
  373. if (!needle || isEmpty(haystack)) return false;
  374. let keys = Object.keys(haystack) || [];
  375. return needle instanceof RegExp
  376. ? keys.some((o) => o && needle.test(o))
  377. : keys.some((o) => o && ciEquals(needle, o));
  378. };
  379. const isIterable = (obj) => {
  380. if (obj) return typeof obj[Symbol.iterator] === "function";
  381. return false;
  382. };
  383. const hasHtml = (value) => value && /<\/?[^>]*>/.test(value);
  384. const firstOfList = (value) => (isArray(value) ? value[0] : value);
  385. const truncateArray = (list = [], max = 7) => {
  386. if (list.length < max) return list;
  387. return [...list.slice(0, max), "..."];
  388. };
  389. module.exports = {
  390. atob,
  391. baseUrl,
  392. btoa,
  393. capitalizeFirstLetter,
  394. clone,
  395. firstOfList,
  396. formatCurrency,
  397. hasDocument,
  398. hasJsonStructure,
  399. hasHtml,
  400. hasKey,
  401. hasNavigator,
  402. hasOwnProperty,
  403. hasWindow,
  404. isArray,
  405. isBlank,
  406. isDate,
  407. isEqual,
  408. isEmpty,
  409. isFunction,
  410. isHex,
  411. isInt,
  412. isIterable,
  413. isJson,
  414. isNumber,
  415. isNullOrFalse,
  416. isNullOrZero,
  417. isObject,
  418. isReactElement,
  419. isValidUrl,
  420. isUrl,
  421. isUndefined,
  422. isString,
  423. language,
  424. merge,
  425. normalizeUrl,
  426. normalizeJSON,
  427. objectToString,
  428. omit,
  429. omitBy,
  430. parse,
  431. pick,
  432. querystring,
  433. reload,
  434. safeIdName,
  435. safeEncodeURIComponent,
  436. safeSearchName,
  437. slugify,
  438. stringify,
  439. stringToArray,
  440. stringToObject,
  441. stripNewlines,
  442. toBoolean,
  443. truncateArray,
  444. typeofDate,
  445. uuid,
  446. uniqueId
  447. };