@qikdev/sdk 1.0.2 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/README.md +1 -1
  2. package/dist/{docs/access.html → access.html} +353 -20
  3. package/dist/api.html +444 -0
  4. package/dist/{docs/auth.html → auth.html} +14 -14
  5. package/dist/cache.html +351 -0
  6. package/dist/{docs/content.html → content.html} +46 -21
  7. package/dist/{docs/files.html → files.html} +147 -6
  8. package/dist/{docs/filter.html → filter.html} +9 -9
  9. package/dist/geo.html +1029 -0
  10. package/dist/global.html +240 -0
  11. package/dist/index.html +96 -0
  12. package/dist/main.js +2 -3
  13. package/dist/module-QikStorage-CookieStorageAdapter.html +407 -0
  14. package/dist/module-QikStorage-LocalStorageAdapter.html +193 -0
  15. package/dist/module-QikStorage-StorageAdapter.html +977 -0
  16. package/dist/module-QikStorage.html +433 -0
  17. package/dist/qik.access.js.html +904 -0
  18. package/dist/qik.api.js.html +409 -0
  19. package/dist/{docs/qik.auth.js.html → qik.auth.js.html} +142 -104
  20. package/dist/qik.cache.js.html +125 -0
  21. package/dist/qik.content.js.html +967 -0
  22. package/dist/qik.core.js.html +322 -0
  23. package/dist/qik.files.js.html +249 -0
  24. package/dist/{docs/qik.filter.js.html → qik.filter.js.html} +3 -3
  25. package/dist/qik.geo.js.html +963 -0
  26. package/dist/{docs/qik.socket.js.html → qik.socket.js.html} +53 -12
  27. package/dist/qik.storage.js.html +375 -0
  28. package/dist/qik.system.js.html +117 -0
  29. package/dist/qik.utils.js.html +1548 -0
  30. package/dist/{docs/socket.html → socket.html} +7 -7
  31. package/dist/{docs/styles → styles}/jsdoc.css +2 -2
  32. package/dist/system.html +197 -0
  33. package/dist/utils.html +4422 -0
  34. package/package.json +2 -2
  35. package/src/api/qik.filter.js +1 -1
  36. package/src/api/qik.geo.js +1 -1
  37. package/src/api/qik.socket.js +18 -12
  38. package/src/api/qik.utils.js +6 -0
  39. package/src/version.js +1 -1
  40. package/dist/docs/api.html +0 -1356
  41. package/dist/docs/cache.html +0 -351
  42. package/dist/docs/index.html +0 -96
  43. package/dist/docs/qik.access.js.html +0 -790
  44. package/dist/docs/qik.api.js.html +0 -511
  45. package/dist/docs/qik.cache.js.html +0 -131
  46. package/dist/docs/qik.content.js.html +0 -918
  47. package/dist/docs/qik.core.js.html +0 -301
  48. package/dist/docs/qik.files.js.html +0 -219
  49. package/dist/docs/qik.system.js.html +0 -117
  50. package/dist/docs/qik.utils.js.html +0 -1467
  51. package/dist/docs/system.html +0 -197
  52. /package/dist/{docs/fonts → fonts}/Montserrat/Montserrat-Bold.eot +0 -0
  53. /package/dist/{docs/fonts → fonts}/Montserrat/Montserrat-Bold.ttf +0 -0
  54. /package/dist/{docs/fonts → fonts}/Montserrat/Montserrat-Bold.woff +0 -0
  55. /package/dist/{docs/fonts → fonts}/Montserrat/Montserrat-Bold.woff2 +0 -0
  56. /package/dist/{docs/fonts → fonts}/Montserrat/Montserrat-Regular.eot +0 -0
  57. /package/dist/{docs/fonts → fonts}/Montserrat/Montserrat-Regular.ttf +0 -0
  58. /package/dist/{docs/fonts → fonts}/Montserrat/Montserrat-Regular.woff +0 -0
  59. /package/dist/{docs/fonts → fonts}/Montserrat/Montserrat-Regular.woff2 +0 -0
  60. /package/dist/{docs/fonts → fonts}/Source-Sans-Pro/sourcesanspro-light-webfont.eot +0 -0
  61. /package/dist/{docs/fonts → fonts}/Source-Sans-Pro/sourcesanspro-light-webfont.svg +0 -0
  62. /package/dist/{docs/fonts → fonts}/Source-Sans-Pro/sourcesanspro-light-webfont.ttf +0 -0
  63. /package/dist/{docs/fonts → fonts}/Source-Sans-Pro/sourcesanspro-light-webfont.woff +0 -0
  64. /package/dist/{docs/fonts → fonts}/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 +0 -0
  65. /package/dist/{docs/fonts → fonts}/Source-Sans-Pro/sourcesanspro-regular-webfont.eot +0 -0
  66. /package/dist/{docs/fonts → fonts}/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +0 -0
  67. /package/dist/{docs/fonts → fonts}/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf +0 -0
  68. /package/dist/{docs/fonts → fonts}/Source-Sans-Pro/sourcesanspro-regular-webfont.woff +0 -0
  69. /package/dist/{docs/fonts → fonts}/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 +0 -0
  70. /package/dist/{docs/scripts → scripts}/collapse.js +0 -0
  71. /package/dist/{docs/scripts → scripts}/linenumber.js +0 -0
  72. /package/dist/{docs/scripts → scripts}/nav.js +0 -0
  73. /package/dist/{docs/scripts → scripts}/polyfill.js +0 -0
  74. /package/dist/{docs/scripts → scripts}/prettify/Apache-License-2.0.txt +0 -0
  75. /package/dist/{docs/scripts → scripts}/prettify/lang-css.js +0 -0
  76. /package/dist/{docs/scripts → scripts}/prettify/prettify.js +0 -0
  77. /package/dist/{docs/scripts → scripts}/search.js +0 -0
  78. /package/dist/{docs/styles → styles}/prettify.css +0 -0
@@ -0,0 +1,1548 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+
5
+ <meta charset="utf-8">
6
+ <title>qik.utils.js - Documentation</title>
7
+
8
+
9
+ <script src="scripts/prettify/prettify.js"></script>
10
+ <script src="scripts/prettify/lang-css.js"></script>
11
+ <!--[if lt IE 9]>
12
+ <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
13
+ <![endif]-->
14
+ <link type="text/css" rel="stylesheet" href="styles/prettify.css">
15
+ <link type="text/css" rel="stylesheet" href="styles/jsdoc.css">
16
+ <script src="scripts/nav.js" defer></script>
17
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
18
+ </head>
19
+ <body>
20
+
21
+ <input type="checkbox" id="nav-trigger" class="nav-trigger" />
22
+ <label for="nav-trigger" class="navicon-button x">
23
+ <div class="navicon"></div>
24
+ </label>
25
+
26
+ <label for="nav-trigger" class="overlay"></label>
27
+
28
+ <nav >
29
+
30
+ <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="access.html">access</a><ul class='methods'><li data-type='method'><a href="access.html#.actionableScopes">actionableScopes</a></li><li data-type='method'><a href="access.html#.allPermissions">allPermissions</a></li><li data-type='method'><a href="access.html#.allPermissionTypes">allPermissionTypes</a></li><li data-type='method'><a href="access.html#.allUserScopes">allUserScopes</a></li><li data-type='method'><a href="access.html#.canCreate">canCreate</a></li><li data-type='method'><a href="access.html#.canDeleteItem">canDeleteItem</a></li><li data-type='method'><a href="access.html#.canEditItem">canEditItem</a></li><li data-type='method'><a href="access.html#.canEraseItem">canEraseItem</a></li><li data-type='method'><a href="access.html#.canKnowOf">canKnowOf</a></li><li data-type='method'><a href="access.html#.canListItem">canListItem</a></li><li data-type='method'><a href="access.html#.canRestoreItem">canRestoreItem</a></li><li data-type='method'><a href="access.html#.canViewItem">canViewItem</a></li><li data-type='method'><a href="access.html#.checkActionAccess">checkActionAccess</a></li><li data-type='method'><a href="access.html#.getAllDescendants">getAllDescendants</a></li><li data-type='method'><a href="access.html#.has">has</a></li><li data-type='method'><a href="access.html#.hasFeature">hasFeature</a></li><li data-type='method'><a href="access.html#.hashPermissions">hashPermissions</a></li><li data-type='method'><a href="access.html#.isAdministrator">isAdministrator</a></li><li data-type='method'><a href="access.html#.isAssignee">isAssignee</a></li><li data-type='method'><a href="access.html#.isOwner">isOwner</a></li></ul></li><li><a href="api.html">api</a><ul class='methods'><li data-type='method'><a href="api.html#.generateEndpointURL">generateEndpointURL</a></li></ul></li><li><a href="auth.html">auth</a><ul class='methods'><li data-type='method'><a href="auth.html#.changeOrganisation">changeOrganisation</a></li><li data-type='method'><a href="auth.html#.ensureValidToken">ensureValidToken</a></li><li data-type='method'><a href="auth.html#.getCurrentToken">getCurrentToken</a></li><li data-type='method'><a href="auth.html#.getCurrentUser">getCurrentUser</a></li><li data-type='method'><a href="auth.html#.impersonate">impersonate</a></li><li data-type='method'><a href="auth.html#.login">login</a></li><li data-type='method'><a href="auth.html#.logout">logout</a></li><li data-type='method'><a href="auth.html#.retrieveUserFromResetToken">retrieveUserFromResetToken</a></li><li data-type='method'><a href="auth.html#.sendResetPasswordRequest">sendResetPasswordRequest</a></li><li data-type='method'><a href="auth.html#.set">set</a></li><li data-type='method'><a href="auth.html#.sync">sync</a></li><li data-type='method'><a href="auth.html#.updateUserWithToken">updateUserWithToken</a></li></ul></li><li><a href="cache.html">cache</a><ul class='methods'><li data-type='method'><a href="cache.html#.get">get</a></li><li data-type='method'><a href="cache.html#.reset">reset</a></li></ul></li><li><a href="content.html">content</a><ul class='methods'><li data-type='method'><a href="content.html#.create">create</a></li><li data-type='method'><a href="content.html#.delete">delete</a></li><li data-type='method'><a href="content.html#.get">get</a></li><li data-type='method'><a href="content.html#.getFromSlug">getFromSlug</a></li><li data-type='method'><a href="content.html#.glossary">glossary</a></li><li data-type='method'><a href="content.html#.list">list</a></li><li data-type='method'><a href="content.html#.patch">patch</a></li><li data-type='method'><a href="content.html#.restore">restore</a></li><li data-type='method'><a href="content.html#.scopeGlossary">scopeGlossary</a></li><li data-type='method'><a href="content.html#.update">update</a></li><li data-type='method'><a href="content.html#.validateField">validateField</a></li><li data-type='method'><a href="content.html#.variables">variables</a></li></ul></li><li><a href="files.html">files</a><ul class='methods'><li data-type='method'><a href="files.html#.downloadUrl">downloadUrl</a></li><li data-type='method'><a href="files.html#.duration">duration</a></li><li data-type='method'><a href="files.html#.filesize">filesize</a></li><li data-type='method'><a href="files.html#.getBinaryTypeFromMime">getBinaryTypeFromMime</a></li><li data-type='method'><a href="files.html#.mediaUrl">mediaUrl</a></li></ul></li><li><a href="filter.html">filter</a><ul class='methods'><li data-type='method'><a href="filter.html#.activeFilterComparators">activeFilterComparators</a></li><li data-type='method'><a href="filter.html#.activeFilterKeys">activeFilterKeys</a></li><li data-type='method'><a href="filter.html#.activeFilterOperators">activeFilterOperators</a></li><li data-type='method'><a href="filter.html#.activeFilters">activeFilters</a></li><li data-type='method'><a href="filter.html#.activeFilterValues">activeFilterValues</a></li><li data-type='method'><a href="filter.html#.filterChangeString">filterChangeString</a></li><li data-type='method'><a href="filter.html#.isValidFilter">isValidFilter</a></li></ul></li><li><a href="geo.html">geo</a><ul class='methods'><li data-type='method'><a href="geo.html#.distance">distance</a></li><li data-type='method'><a href="geo.html#.getDefaultCountry">getDefaultCountry</a></li><li data-type='method'><a href="geo.html#.getDefaultTimezone">getDefaultTimezone</a></li><li data-type='method'><a href="geo.html#.getPosition">getPosition</a></li><li data-type='method'><a href="geo.html#.stopWatchPosition">stopWatchPosition</a></li><li data-type='method'><a href="geo.html#.watchPosition">watchPosition</a></li></ul></li><li><a href="module-QikStorage-CookieStorageAdapter.html">CookieStorageAdapter</a><ul class='methods'><li data-type='method'><a href="module-QikStorage-CookieStorageAdapter.html#getCookieOptions">getCookieOptions</a></li><li data-type='method'><a href="module-QikStorage-CookieStorageAdapter.html#isCookieMode">isCookieMode</a></li></ul></li><li><a href="module-QikStorage-LocalStorageAdapter.html">LocalStorageAdapter</a></li><li><a href="module-QikStorage-StorageAdapter.html">StorageAdapter</a><ul class='methods'><li data-type='method'><a href="module-QikStorage-StorageAdapter.html#clearUser">clearUser</a></li><li data-type='method'><a href="module-QikStorage-StorageAdapter.html#getAccessToken">getAccessToken</a></li><li data-type='method'><a href="module-QikStorage-StorageAdapter.html#getRefreshToken">getRefreshToken</a></li><li data-type='method'><a href="module-QikStorage-StorageAdapter.html#getTokenExpiry">getTokenExpiry</a></li><li data-type='method'><a href="module-QikStorage-StorageAdapter.html#getUser">getUser</a></li><li data-type='method'><a href="module-QikStorage-StorageAdapter.html#isAvailable">isAvailable</a></li><li data-type='method'><a href="module-QikStorage-StorageAdapter.html#setUser">setUser</a></li></ul></li><li><a href="socket.html">socket</a><ul class='methods'><li data-type='method'><a href="socket.html#.channel">channel</a></li><li data-type='method'><a href="socket.html#.close">close</a></li><li data-type='method'><a href="socket.html#.connect">connect</a></li><li data-type='method'><a href="socket.html#.reconnect">reconnect</a></li><li data-type='method'><a href="socket.html#.subscribe">subscribe</a></li><li data-type='method'><a href="socket.html#.unsubscribe">unsubscribe</a></li></ul></li><li><a href="system.html">system</a><ul class='methods'><li data-type='method'><a href="system.html#.countries">countries</a></li></ul></li><li><a href="utils.html">utils</a><ul class='methods'><li data-type='method'><a href="utils.html#.cleanValue">cleanValue</a></li><li data-type='method'><a href="utils.html#.comma">comma</a></li><li data-type='method'><a href="utils.html#.errorMessage">errorMessage</a></li><li data-type='method'><a href="utils.html#.exists">exists</a></li><li data-type='method'><a href="utils.html#.extractFromArray">extractFromArray</a></li><li data-type='method'><a href="utils.html#.formatCurrency">formatCurrency</a></li><li data-type='method'><a href="utils.html#.getTypeFromID">getTypeFromID</a></li><li data-type='method'><a href="utils.html#.guid">guid</a></li><li data-type='method'><a href="utils.html#.hash">hash</a></li><li data-type='method'><a href="utils.html#.id">id</a></li><li data-type='method'><a href="utils.html#.ids">ids</a></li><li data-type='method'><a href="utils.html#.isValidEmailAddress">isValidEmailAddress</a></li><li data-type='method'><a href="utils.html#.loadExternalScript">loadExternalScript</a></li><li data-type='method'><a href="utils.html#.loadExternalStyle">loadExternalStyle</a></li><li data-type='method'><a href="utils.html#.machineName">machineName</a></li><li data-type='method'><a href="utils.html#.mapParameters">mapParameters</a></li><li data-type='method'><a href="utils.html#.matchInArray">matchInArray</a></li><li data-type='method'><a href="utils.html#.parseBoolean">parseBoolean</a></li><li data-type='method'><a href="utils.html#.parseDate">parseDate</a></li><li data-type='method'><a href="utils.html#.parseEmail">parseEmail</a></li><li data-type='method'><a href="utils.html#.parseInt">parseInt</a></li><li data-type='method'><a href="utils.html#.parseNumber">parseNumber</a></li><li data-type='method'><a href="utils.html#.parseURL">parseURL</a></li></ul></li></ul><h3>Modules</h3><ul><li><a href="module-QikStorage.html">QikStorage</a><ul class='methods'><li data-type='method'><a href="module-QikStorage.html#.createStorageAdapter">createStorageAdapter</a></li></ul></li></ul><h3>Global</h3><ul><li><a href="global.html#EventDispatcher">EventDispatcher</a></li></ul>
31
+ </nav>
32
+
33
+ <div id="main">
34
+
35
+ <h1 class="page-title">qik.utils.js</h1>
36
+
37
+
38
+
39
+
40
+
41
+
42
+
43
+ <section>
44
+ <article>
45
+ <pre class="prettyprint source linenums"><code>import _get from "lodash.get";
46
+ import _camelCase from "lodash.camelcase";
47
+ import { isBrowser, isNode } from "browser-or-node";
48
+
49
+ ///////////////////////////////////////////////////////////////////////////////
50
+
51
+ /**
52
+ * @name utils
53
+ * @classdesc Utility helper functions — id, ids, hashing, parsing, cleaning, and other general-purpose helpers
54
+ * @class
55
+ */
56
+
57
+ const service = {};
58
+
59
+ ////////////////////////////////////
60
+
61
+ const loadedExternalScripts = {};
62
+
63
+ /**
64
+ * A function that dynamically include an external javascript resource
65
+ * ensuring that it will only be included once
66
+ * @alias utils.loadExternalScript
67
+ * @param {String} url The url of the external script
68
+ * @return {Promise} A promise that will be resolved once the script has been loaded
69
+ * @example
70
+ *
71
+ * await sdk.utils.loadExternalScript('https://cdn.javascript.com/external/script.js');
72
+ */
73
+
74
+ service.loadExternalScript = function (url) {
75
+ // If we already have a promise
76
+ if (loadedExternalScripts[url]) {
77
+ // return the existing promise
78
+ return loadedExternalScripts[url];
79
+ }
80
+
81
+ // Create a new promise
82
+ const promise = (loadedExternalScripts[url] = new Promise(createNewScript));
83
+
84
+ function createNewScript(resolve, reject) {
85
+ if (!document) {
86
+ delete loadedExternalScripts[url];
87
+ return reject("document is undefined");
88
+ }
89
+
90
+ //////////////////////////////////////
91
+
92
+ var script = document.createElement("script");
93
+ script.type = "text/javascript";
94
+ script.async = true;
95
+ script.onload = function () {
96
+ console.log("Loaded external script", url);
97
+ return resolve(url);
98
+ };
99
+ script.src = url;
100
+
101
+ // Inject it into the document's &lt;head> tag
102
+ document.head.appendChild(script);
103
+ }
104
+
105
+ return promise;
106
+ };
107
+
108
+ const loadedExternalStyles = {};
109
+
110
+ /**
111
+ * A function that dynamically include an external css resource
112
+ * ensuring that it will only be included once
113
+ * @alias utils.loadExternalStyle
114
+ * @param {String} url The url of the external css
115
+ * @return {Promise} A promise that will be resolved once the css has been loaded
116
+ * @example
117
+ *
118
+ * await sdk.utils.loadExternalStyle('https://cdn.css.com/external/style.css');
119
+ */
120
+ service.loadExternalStyle = function (url) {
121
+ const promise = new Promise(createNewScript);
122
+
123
+ function createNewScript(resolve, reject) {
124
+ if (!document) {
125
+ return reject("document is undefined");
126
+ }
127
+
128
+ if (loadedExternalStyles[url]) {
129
+ return resolve(url);
130
+ }
131
+
132
+ // Avoid duplicate scripts
133
+ loadedExternalStyles[url] = true;
134
+
135
+ //////////////////////////////////////
136
+
137
+ var script = document.createElement("link");
138
+ script.setAttribute("rel", "stylesheet");
139
+ script.setAttribute("type", "text/css");
140
+ script.setAttribute("href", url);
141
+ script.onload = function () {
142
+ console.log("Loaded external stylesheet", url);
143
+ return resolve(url);
144
+ };
145
+
146
+ // Inject it into the document's &lt;head> tag
147
+ document.head.appendChild(script);
148
+ }
149
+
150
+ return promise;
151
+ };
152
+
153
+ ///////////////////////////////////////////////////////////////////////////////
154
+
155
+ /**
156
+ * A helper function for checking whether a value is truthy/falsy
157
+ * @alias utils.exists
158
+ * @param {Anything} value The value to check
159
+ * @return {Boolean} whether the value is truthy
160
+ * @example
161
+ *
162
+ * sdk.utils.exists('undefined'); // false
163
+ * sdk.utils.exists([]); // true
164
+ * sdk.utils.exists(''); // false
165
+ * sdk.utils.exists(undefined); // false
166
+ */
167
+ service.exists = function (value) {
168
+ var isUndefinedOrNull;
169
+
170
+ ////////////////////////
171
+
172
+ if (Array.isArray(value)) {
173
+ return true;
174
+ }
175
+
176
+ ////////////////////////
177
+
178
+ if (value === 0) {
179
+ return true;
180
+ }
181
+
182
+ ////////////////////////
183
+
184
+ switch (typeof value) {
185
+ case "undefined":
186
+ case "null":
187
+ isUndefinedOrNull = true;
188
+ break;
189
+ default:
190
+ var string = String(value).toLowerCase();
191
+ switch (string) {
192
+ case "":
193
+ case "undefined":
194
+ case "null":
195
+ isUndefinedOrNull = true;
196
+ break;
197
+ }
198
+ break;
199
+ }
200
+
201
+ return !!!isUndefinedOrNull;
202
+ };
203
+
204
+ //////////////////////////////////
205
+
206
+ /**
207
+ * A helper function for getting a javascript date object from some input
208
+ * returns undefined if it can not be parsed
209
+ * @alias utils.parseDate
210
+ * @param {String|Number} input The value to parse as a date
211
+ * @return {Date} the javascript date object
212
+ * @example
213
+ *
214
+ * sdk.utils.parseDate(input);
215
+ */
216
+ service.parseDate = function (input) {
217
+ if (!input) {
218
+ return;
219
+ }
220
+
221
+ if (input instanceof Date) {
222
+ return input;
223
+ }
224
+
225
+ //Attempt to create a date
226
+ var d = new Date(input);
227
+ var isValid = d instanceof Date &amp;&amp; !isNaN(d);
228
+
229
+ if (isValid) {
230
+ return d;
231
+ }
232
+
233
+ return;
234
+ };
235
+
236
+ /**
237
+ * A helper function for getting a full url string from some input
238
+ * @alias utils.parseURL
239
+ * @param {String} input The input value
240
+ * @return {String} the fully parsed URL
241
+ * @example
242
+ *
243
+ * sdk.utils.parseURL('google.com'); // Returns https://google.com
244
+ * sdk.utils.parseURL('mailto:hello@qik.dev'); // Returns 'mailto:hello@qik.dev'
245
+ * sdk.utils.parseURL('://hello.com'); // Returns '://hello.com'
246
+ * sdk.utils.parseURL('hello@email.com'); // Returns 'mailto:hello@email.com'
247
+ */
248
+ service.parseURL = function (string) {
249
+ if (!string) {
250
+ return false;
251
+ }
252
+
253
+ const relative = string.startsWith("/");
254
+ if (relative) {
255
+ return string;
256
+ }
257
+
258
+ if (string.startsWith("://")) {
259
+ return string;
260
+ }
261
+
262
+ if (string.startsWith("mailto:")) {
263
+ return string;
264
+ }
265
+
266
+ if (string.startsWith("tel:")) {
267
+ return string;
268
+ }
269
+
270
+ if (string.startsWith("sms:")) {
271
+ return string;
272
+ }
273
+
274
+ //////////////////////////////////
275
+ //If someone entered an email by accident
276
+ var email = service.parseEmail(string);
277
+ //Convert it to a mailto link
278
+ if (email &amp;&amp; email === string) {
279
+ return `mailto:${email}`;
280
+ }
281
+
282
+ //////////////////////////////////
283
+
284
+ const withHttp = (string) =>
285
+ !/^https?:\/\//i.test(string) ? `http://${string}` : string;
286
+ string = withHttp(string);
287
+ //TODO: test this more.
288
+ string = string.replace(/\s/g, "");
289
+
290
+ var valid = RegExp(
291
+ "(https?:\\/\\/)?((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|((\\d{1,3}\\.){3}\\d{1,3}))(\\:\\d+)?(\\/[-a-z\\d%_.~+@]*)*(\\?[;&amp;a-z\\d%_.~+=-@]*)?(\\#[-a-z\\d_@]*)?$",
292
+ "i",
293
+ ).test(string);
294
+
295
+ if (!valid) {
296
+ return false;
297
+ }
298
+
299
+ ///////////////////////////
300
+
301
+ return withHttp(string);
302
+ };
303
+
304
+ ///////////////////////////////////////////////
305
+
306
+ /**
307
+ * A helper function for getting a number from some input
308
+ * @alias utils.parseNumber
309
+ * @param {String} value The input value
310
+ * @param {Number} decimalPoints The number of decimal points to round to
311
+ * @return {Number} the parsed number value
312
+ * @example
313
+ *
314
+ * sdk.utils.parseNumber('123'); // Returns 123
315
+ * sdk.utils.parseNumber('75.501', 2); // Returns 75.5
316
+ * sdk.utils.parseNumber(''); // Returns 0
317
+ * sdk.utils.parseNumber(null); // Returns 0
318
+ * sdk.utils.parseNumber(); // Returns 0
319
+ */
320
+ service.parseNumber = function (input, decimalPoints) {
321
+ if (!input) {
322
+ return 0;
323
+ }
324
+
325
+ input = Number(input);
326
+ if (isNaN(input)) {
327
+ return 0;
328
+ }
329
+
330
+ if (decimalPoints) {
331
+ var str = input.toFixed(decimalPoints);
332
+ input = service.parseNumber(str);
333
+ }
334
+
335
+ return input;
336
+ };
337
+
338
+ /**
339
+ * A helper function for getting a number from some input
340
+ * @alias utils.isValidEmailAddress
341
+ * @param {String} emailAddress The email to validate
342
+ * @return {Boolean} whether or not the input is a valid email address
343
+ * @example
344
+ *
345
+ * sdk.utils.isValidEmailAddress('123.com'); // Returns false
346
+ * sdk.utils.isValidEmailAddress('hello@world.com'); // Returns true
347
+ * sdk.utils.isValidEmailAddress('something@special.io'); // Returns true
348
+ */
349
+ service.isValidEmailAddress = function (email) {
350
+ var tester =
351
+ /^[-!#$%&amp;'*+\/0-9=?A-Z^_a-z`{|}~](\.?[-!#$%&amp;'*+\/0-9=?A-Z^_a-z`{|}~])*@[a-zA-Z0-9](-*\.?[a-zA-Z0-9])*\.[a-zA-Z](-?[a-zA-Z0-9])+$/;
352
+
353
+ if (!email) return false;
354
+
355
+ var emailParts = email.split("@");
356
+
357
+ if (emailParts.length !== 2) return false;
358
+
359
+ var account = emailParts[0];
360
+ var address = emailParts[1];
361
+
362
+ if (account.length > 64) return false;
363
+ else if (address.length > 255) return false;
364
+
365
+ var domainParts = address.split(".");
366
+ if (
367
+ domainParts.some(function (part) {
368
+ return part.length > 63;
369
+ })
370
+ )
371
+ return false;
372
+
373
+ if (!tester.test(email)) return false;
374
+
375
+ return true;
376
+ };
377
+
378
+ /**
379
+ * A helper function for getting a formatted email address
380
+ * @alias utils.parseEmail
381
+ * @param {String} emailAddress The input to parse as an email
382
+ * @return {String|Boolean} a valid email address in lowercase, or false if parse is not possible
383
+ * @example
384
+ *
385
+ * sdk.utils.parseEmail('ToMack@gmail.com'); // returns 'tomack@gmail.com'
386
+ * sdk.utils.isValidEmailAddress('hello.world.com'); // Returns false
387
+ */
388
+ service.parseEmail = function (input) {
389
+ if (!input) {
390
+ return false;
391
+ }
392
+
393
+ var lowercase = String(input).toLowerCase();
394
+ var valid = service.isValidEmailAddress(lowercase);
395
+
396
+ if (!valid) {
397
+ return false;
398
+ } else {
399
+ return lowercase;
400
+ }
401
+ };
402
+
403
+ //////////////////////////////////
404
+
405
+ /**
406
+ * A helper function for getting an integer
407
+ * @alias utils.parseInt
408
+ * @param {String|Number} input The input to parse as an integer
409
+ * @return {Integer} the resulting integer or 0
410
+ * @example
411
+ *
412
+ * sdk.utils.parseInt('134'); // returns 134
413
+ * sdk.utils.parseInt('cows'); // returns 0
414
+ * sdk.utils.parseInt(); // returns 0
415
+ */
416
+ service.parseInt = function (input) {
417
+ if (!input) {
418
+ return 0;
419
+ }
420
+
421
+ input = parseInt(input);
422
+ if (isNaN(input)) {
423
+ return 0;
424
+ }
425
+
426
+ return input;
427
+ };
428
+
429
+ /**
430
+ * A helper function for cleaning an input value to match
431
+ * a required type
432
+ * @alias utils.cleanValue
433
+ * @param {*} data The input to clean
434
+ * @param {String} type The data type to parse
435
+ * @param {Object} options Additional options for parsing
436
+ * @return {*} the resulting cleaned value
437
+ * @example
438
+ *
439
+ * sdk.utils.cleanValue({_id:'1234', title:'Item'...}, 'reference'); // returns '1234'
440
+ * sdk.utils.cleanValue('true', 'boolean'); // returns true
441
+ * sdk.utils.cleanValue('Mr Rogers House', 'key'); // returns 'mrRogersHouse';
442
+ * sdk.utils.cleanValue('Hello.World@email.COM', 'email'); // returns 'hello.world@email.com';
443
+ */
444
+ service.cleanValue = function (data, type, options) {
445
+ if (!options) {
446
+ options = {};
447
+ }
448
+
449
+ ////////////////////////
450
+
451
+ var noInputValue = service.exists(data);
452
+ if (options.strict &amp;&amp; noInputValue &amp;&amp; type != "boolean") {
453
+ return data;
454
+ }
455
+
456
+ ////////////////////////
457
+
458
+ switch (type) {
459
+ case "reference":
460
+ return service.id(data, true);
461
+ break;
462
+ case "boolean":
463
+ return service.parseBoolean(data);
464
+ break;
465
+ case "url":
466
+ return service.parseURL(data);
467
+ break;
468
+ case "key":
469
+ return service.machineName(data);
470
+ break;
471
+ case "email":
472
+ return service.parseEmail(data);
473
+ break;
474
+ case "date":
475
+ var parsed = service.parseDate(data, options);
476
+
477
+ if (parsed === undefined) {
478
+ return;
479
+ } else {
480
+ return parsed;
481
+ }
482
+ break;
483
+ case "number":
484
+ case "decimal":
485
+ case "float":
486
+ var val = service.parseNumber(data);
487
+ if (String(val) == String(Number(data))) {
488
+ return val;
489
+ }
490
+ return;
491
+ break;
492
+ case "integer":
493
+ var val = service.parseInt(data);
494
+ if (String(val) == String(parseInt(data))) {
495
+ return val;
496
+ }
497
+ return;
498
+ break;
499
+ case "group":
500
+ return data;
501
+ break;
502
+ case "object":
503
+ if (typeof data === "object" &amp;&amp; data !== null &amp;&amp; !Array.isArray(data)) {
504
+ return data;
505
+ }
506
+
507
+ return;
508
+ break;
509
+ case "string":
510
+ if (service.exists(data)) {
511
+ var stringed = String(data);
512
+ if (stringed == "[object Object]") {
513
+ return;
514
+ }
515
+
516
+ return stringed;
517
+ } else {
518
+ return;
519
+ }
520
+
521
+ // if(!data) {
522
+ // return '';
523
+ // }
524
+
525
+ // if(Array.isArray(data)) {
526
+ // return JSON.stringify(data);
527
+ // }
528
+
529
+ // if(Object.isObject(data)) {
530
+ // return JSON.stringify(data);
531
+ // }
532
+
533
+ // return String(data);
534
+
535
+ // break;
536
+ default:
537
+ return String(data);
538
+ break;
539
+ }
540
+ };
541
+
542
+ service.isValidValue = function (value, dataType, strict) {
543
+ var isValue = service.exists(value);
544
+ var valueIsNumber = typeof value == "number";
545
+
546
+ ////////////////////////
547
+
548
+ var isValidEntry = false;
549
+
550
+ if (!isValue) {
551
+ return false;
552
+ }
553
+
554
+ ////////////////////////
555
+
556
+ switch (dataType) {
557
+ case "url":
558
+ if (strict) {
559
+ isValidEntry =
560
+ value.startsWith("/") ||
561
+ value.startsWith("://") ||
562
+ value.startsWith("http://") ||
563
+ value.startsWith("https://");
564
+ } else {
565
+ isValidEntry = String(value) === service.parseURL(value);
566
+ }
567
+ break;
568
+ case "key":
569
+ isValidEntry = String(value) === service.machineName(value);
570
+ break;
571
+ case "date":
572
+ //If we are being strict
573
+ if (strict) {
574
+ //And the input is not a javascript date object
575
+ if (typeof value != "object" || !(value instanceof Date)) {
576
+ return false;
577
+ }
578
+ }
579
+
580
+ ///////////////////////////////
581
+ var parsed = service.parseDate(value);
582
+ return parsed !== undefined;
583
+ break;
584
+ case "email":
585
+ if (strict) {
586
+ isValidEntry = value === service.parseEmail(value);
587
+ } else {
588
+ isValidEntry =
589
+ String(value).toLowerCase() === service.parseEmail(value);
590
+ }
591
+
592
+ break;
593
+ case "number":
594
+ case "decimal":
595
+ case "float":
596
+ if (strict) {
597
+ isValidEntry =
598
+ valueIsNumber &amp;&amp; Number(value) === service.parseNumber(value);
599
+ } else {
600
+ isValidEntry = Number(value) === service.parseNumber(value);
601
+ }
602
+ break;
603
+ case "integer":
604
+ if (strict) {
605
+ isValidEntry =
606
+ valueIsNumber &amp;&amp; Number(value) === service.parseInt(value);
607
+ } else {
608
+ isValidEntry = Number(value) === service.parseInt(value);
609
+ }
610
+ break;
611
+ case "boolean":
612
+ var parsed = service.parseBoolean(value);
613
+
614
+ //Only accept true booleans
615
+ if (strict) {
616
+ if (value === true || value === false) {
617
+ isValidEntry = value === parsed;
618
+ }
619
+ } else {
620
+ isValidEntry = parsed === true || parsed === false;
621
+ }
622
+ break;
623
+ case "reference":
624
+ if (strict) {
625
+ isValidEntry = String(value) === service.id(value);
626
+ } else {
627
+ isValidEntry = !!service.id(value);
628
+ }
629
+ break;
630
+ case "string":
631
+ var checkString = String(value);
632
+ if (strict) {
633
+ if (typeof value != "string") {
634
+ return false;
635
+ }
636
+ }
637
+
638
+ if (checkString == "[object Object]") {
639
+ return false;
640
+ }
641
+
642
+ isValidEntry = true;
643
+ break;
644
+ // case 'group':
645
+ // isValidEntry = _isObject(value);
646
+ // break;
647
+ case "object":
648
+ isValidEntry =
649
+ typeof value === "object" &amp;&amp; value !== null &amp;&amp; !Array.isArray(value);
650
+ break;
651
+ case "array":
652
+ isValidEntry = Array.isArray(value);
653
+ break;
654
+ }
655
+
656
+ ////////////////////////
657
+
658
+ return isValidEntry;
659
+ };
660
+
661
+ /**
662
+ * A helper function for parsing input as boolean values
663
+ * @alias utils.parseBoolean
664
+ * @param {*} value The input to parse
665
+ * @return {Boolean} the resulting true/false value
666
+ * @example
667
+ *
668
+ * sdk.utils.parseBoolean('true'); // returns true
669
+ * sdk.utils.parseBoolean('y'); // returns true
670
+ * sdk.utils.parseBoolean('YES'); // returns true
671
+ * sdk.utils.parseBoolean('1'); // returns true
672
+ * sdk.utils.parseBoolean('t'); // returns true
673
+ * sdk.utils.parseBoolean(''); // returns false
674
+ * sdk.utils.parseBoolean('0'); // returns false
675
+ * sdk.utils.parseBoolean('n'); // returns false
676
+ * sdk.utils.parseBoolean('no'); // returns false
677
+ * sdk.utils.parseBoolean('f'); // returns false
678
+ * sdk.utils.parseBoolean('null'); // returns false
679
+ */
680
+ service.parseBoolean = function (value) {
681
+ switch (String(value).toLowerCase()) {
682
+ case "true":
683
+ case "y":
684
+ case "yes":
685
+ case "1":
686
+ case "t":
687
+ value = true;
688
+ break;
689
+ case "false":
690
+ case "n":
691
+ case "no":
692
+ case "0":
693
+ case "f":
694
+ case "undefined":
695
+ case "null":
696
+ case "":
697
+ case "-1":
698
+ value = false;
699
+ break;
700
+ }
701
+
702
+ return !!value;
703
+ };
704
+
705
+ ///////////////////////////////////////////////////////////////////////////////
706
+
707
+ service.clone = function (input) {
708
+ return JSON.parse(JSON.stringify(input));
709
+ };
710
+
711
+ ///////////////////////////////////////////////////////////////////////////////
712
+
713
+ service.getAllFields = function (actualDefinition) {
714
+ const self = this;
715
+ const isProfile =
716
+ actualDefinition.definesType === "profile" ||
717
+ actualDefinition.key === "profile";
718
+ const isFormSubmission = actualDefinition.definesType === "submission";
719
+ var allFields = [...actualDefinition.fields];
720
+ var definedFields = actualDefinition.definedFields || [];
721
+
722
+ if (definedFields.length) {
723
+ if (isFormSubmission) {
724
+ var formDataFields = {
725
+ title: `Form Data`,
726
+ minimum: 1,
727
+ maximum: 1,
728
+ key: "formData",
729
+ asObject: true,
730
+ type: "group",
731
+ fields: definedFields,
732
+ };
733
+
734
+ allFields.push(formDataFields);
735
+
736
+ const cleanedDataFields = definedFields.map(function (field) {
737
+ if (field.type === "reference") {
738
+ field = JSON.parse(JSON.stringify(field));
739
+ delete field.fields;
740
+ }
741
+
742
+ return field;
743
+ });
744
+
745
+ var dataFields = {
746
+ title: `Data`,
747
+ minimum: 1,
748
+ maximum: 1,
749
+ key: "data",
750
+ asObject: true,
751
+ type: "group",
752
+ fields: cleanedDataFields,
753
+ };
754
+
755
+ allFields.push(dataFields);
756
+ } else {
757
+ var dataFields = {
758
+ title: `${actualDefinition.title}`,
759
+ minimum: 1,
760
+ maximum: 1,
761
+ key: "data",
762
+ asObject: true,
763
+ type: "group",
764
+ fields: definedFields,
765
+ };
766
+
767
+ allFields.push(dataFields);
768
+ }
769
+ }
770
+
771
+ if (isProfile) {
772
+ allFields.push({
773
+ title: "Age",
774
+ key: "_age",
775
+ minimum: 1,
776
+ maximum: 1,
777
+ type: "integer",
778
+ });
779
+
780
+ allFields.push({
781
+ title: "Date of birth",
782
+ key: "_dob",
783
+ minimum: 1,
784
+ maximum: 1,
785
+ type: "date",
786
+ });
787
+ }
788
+
789
+ var mapped = service
790
+ .mapFields(allFields)
791
+ .filter(function (field) {
792
+ var isObject =
793
+ field.type == "group" &amp;&amp;
794
+ field.asObject &amp;&amp;
795
+ field.minimum == 1 &amp;&amp;
796
+ field.maximum == 1;
797
+ return !isObject;
798
+ })
799
+ .map(function (field) {
800
+ field.title =
801
+ field.filterTitle || field.titles.filter(Boolean).join(" › ");
802
+
803
+ return field;
804
+ })
805
+ .sort(function (a, b) {
806
+ return a.title &lt; b.title ? -1 : 1;
807
+ });
808
+
809
+ return mapped;
810
+ };
811
+ ///////////////////////////////////////////////////////////////////////////////
812
+
813
+ service.mapFields = function (fields, options) {
814
+ if (!options) {
815
+ options = {};
816
+ }
817
+
818
+ //////////////////////////////
819
+
820
+ var output = [];
821
+ var trail = [];
822
+ var titles = [];
823
+ var currentDepth = 1;
824
+ var depthLimit = options.depth;
825
+ var delimiter = options.arrayDelimeter || "[]";
826
+
827
+ //Loop through each field
828
+ fields.forEach(mapField);
829
+
830
+ //////////////////////////////
831
+
832
+ //Recursively map the fields
833
+ function mapField(field, i) {
834
+ var fieldKey = field.key;
835
+ var isGroup = field.type == "group";
836
+ var singleValue = field.minimum === field.maximum &amp;&amp; field.minimum === 1;
837
+ var asObject = field.asObject; // || (isGroup &amp;&amp; singleValue);
838
+ var isLayout = isGroup &amp;&amp; !asObject;
839
+
840
+ //////////////////////////////////
841
+
842
+ // Whether to clone the field or use the existing reference
843
+ var mapped = options.original ? field : service.clone(field);
844
+
845
+ mapped.trail = trail.slice();
846
+ mapped.trail.push(fieldKey);
847
+
848
+ mapped.path = mapped.trail.join(".");
849
+ mapped.titles = titles.slice();
850
+ mapped.titles.push(field.filterTitle || field.title || "");
851
+
852
+ const isNotLayoutOrIsAllowed = !isLayout || options.includeLayout;
853
+
854
+ //If its an actual element or we've asked to include
855
+ //layout only elements
856
+ if (isNotLayoutOrIsAllowed) {
857
+ //Add it to the mix
858
+ output.push(mapped);
859
+ }
860
+
861
+ //////////////////////////////////
862
+
863
+ //Now see if there are child fields and should we go further
864
+ var limitHit = depthLimit &amp;&amp; currentDepth >= depthLimit;
865
+
866
+ //If there are child fields for this field
867
+ if (field.fields &amp;&amp; field.fields.length) {
868
+ //If it's just a group with no extra key
869
+ if (isLayout) {
870
+ //Loop through fields as if they are at the same depth
871
+ field.fields.forEach(mapField);
872
+ } else {
873
+ //We don't need to traverse any further
874
+ //because we hit the limit
875
+ if (limitHit) {
876
+ return;
877
+ }
878
+
879
+ //Move down a level and add to the object
880
+ //Include the key in the path trail
881
+ currentDepth++;
882
+
883
+ let injectKey = fieldKey;
884
+ if (options.includeArrayDelimeter) {
885
+ if (!singleValue) {
886
+ injectKey = `${fieldKey}${delimiter}`;
887
+ }
888
+ }
889
+ trail.push(injectKey);
890
+ titles.push(field.filterTitle || field.title);
891
+
892
+ //Loop through each field
893
+ field.fields.forEach(mapField);
894
+
895
+ //Move back up a level before we go to the next field
896
+ currentDepth--;
897
+ trail.pop();
898
+ titles.pop();
899
+ }
900
+ }
901
+ }
902
+
903
+ return output;
904
+ };
905
+
906
+ ///////////////////////////////////////////////////////////////////////////////
907
+
908
+ /**
909
+ * A helpful function that can take a keyed object literal and map it to url query string parameters
910
+ * @alias utils.mapParameters
911
+ * @param {Object} parameters The object you want to transalte
912
+ * @return {String} The query string
913
+ * @example
914
+ * //Returns &amp;this=that&amp;hello=world
915
+ * sdk.utils.mapParameters({"this":"that", "hello":"world"})
916
+ */
917
+ service.mapParameters = function (parameters) {
918
+ parameters = parameters || {};
919
+
920
+ var array = [];
921
+
922
+ Object.entries(parameters).forEach(function ([key, value]) {
923
+ if (value === undefined || value === null || value == false) {
924
+ return;
925
+ }
926
+
927
+ if (Array.isArray(value)) {
928
+ value.forEach(function (v) {
929
+ array.push(`${key}=${encodeURIComponent(v)}`);
930
+ });
931
+ } else {
932
+ array.push(encodeURIComponent(key) + "=" + encodeURIComponent(value));
933
+ }
934
+ });
935
+
936
+ return array.join("&amp;");
937
+ };
938
+
939
+ ///////////////////////////////////////////////////////////////////////////////
940
+
941
+ /**
942
+ * A function that will take an integer and a currency string and return a formatted numeric amount rounded to 2 decimal places
943
+ * @alias utils.formatCurrency
944
+ * @param {Integer} value The amount in cents
945
+ * @param {String} currency The currency to format
946
+ * @return {String} The formatted value
947
+ * @example
948
+ *
949
+ * //Returns £10.00
950
+ * sdk.utils.formatCurrency(1000, 'gbp');
951
+ *
952
+ * //Returns $10.00
953
+ * sdk.utils.formatCurrency(1000, 'usd');
954
+ *
955
+ */
956
+ service.formatCurrency = function (value, currency, decimalPoints) {
957
+ if (!value || isNaN(value)) {
958
+ value = 0;
959
+ }
960
+
961
+ decimalPoints = decimalPoints || 2;
962
+
963
+ var currencyPrefix = service.currencySymbol(currency);
964
+ return `${currencyPrefix}${parseFloat(Number(value) / 100).toFixed(decimalPoints)}`;
965
+ };
966
+
967
+ /**
968
+ * A function that will take an id and return the type key
969
+ * @alias utils.getTypeFromID
970
+ * @param {String} id The id of an object
971
+ * @return {String} The key
972
+ * @example
973
+ *
974
+ * // Returns 'user'
975
+ * sdk.utils.getTypeFromID('52b523f775beea960013f6cd');
976
+ *
977
+ * // Returns 'role'
978
+ * sdk.utils.getTypeFromID('62b59cb572fb4772e7b5fa93');
979
+ *
980
+ */
981
+ service.getTypeFromID = function (id) {
982
+ id = service.id(id);
983
+
984
+ if (!id) {
985
+ return;
986
+ }
987
+
988
+ const lookup = {
989
+ 21: "organisation",
990
+ 23: "comment",
991
+ 24: "persona",
992
+ 25: "audio",
993
+ 26: "campaign",
994
+ 28: "smartlist",
995
+ 29: "cache",
996
+ 40: "application",
997
+ 41: "action",
998
+ 42: "billinginvoice",
999
+ 43: "componentsnapshot",
1000
+ 44: "diff",
1001
+ 45: "email",
1002
+ 46: "submission",
1003
+ 49: "import",
1004
+ 53: "socket",
1005
+ 54: "timetrigger",
1006
+ 61: "article",
1007
+ 63: "component",
1008
+ 65: "event",
1009
+ 66: "file",
1010
+ 67: "tag",
1011
+ 69: "image",
1012
+ 70: "profile",
1013
+ 72: "role",
1014
+ 73: "scope",
1015
+ 74: "transaction",
1016
+ 75: "user",
1017
+ 76: "video",
1018
+ 77: "workflowcard",
1019
+ 78: "variable",
1020
+ "3c": "patoken",
1021
+ "7b": "uatoken",
1022
+ "7d": "urtoken",
1023
+ "2d": "systemtask",
1024
+ "3d": "export",
1025
+ "5f": "systemflag",
1026
+ "2b": "batch",
1027
+ "3a": "log",
1028
+ "2e": "stat",
1029
+ "2f": "unsubscribe",
1030
+ "6b": "intent",
1031
+ "7c": "upload",
1032
+ "2c": "systemtrigger",
1033
+ "6e": "interface",
1034
+ "4e": "interfacesnapshot",
1035
+ "4f": "notification",
1036
+ "2a": "definition",
1037
+ "7e": "definitionsnapshot",
1038
+ "5e": "integration",
1039
+ "4c": "listener",
1040
+ "6d": "topic",
1041
+ "7a": "code",
1042
+ "4d": "paymentmethod",
1043
+ "6c": "sslcertificate",
1044
+ };
1045
+
1046
+ var hexKey = id.slice(8, 10);
1047
+ return lookup[hexKey];
1048
+ };
1049
+
1050
+ /**
1051
+ * A function that will take a currency string and return the symbol
1052
+ * @alias utils.currencySymbol
1053
+ * @param {String} currency The currency
1054
+ * @return {String} The symbol
1055
+ * @example
1056
+ *
1057
+ * //Returns £
1058
+ * sdk.utils.currencySymbol('gbp');
1059
+ *
1060
+ * //Returns $
1061
+ * sdk.utils.currencySymbol('usd');
1062
+ *
1063
+ */
1064
+ const CURRENCY_SYMBOLS = { gbp: "\u00A3", eur: "\u20AC" };
1065
+
1066
+ service.currencySymbol = function (currency) {
1067
+ return CURRENCY_SYMBOLS[String(currency).toLowerCase()] || "$";
1068
+ };
1069
+
1070
+ const SUPPORTED_CURRENCIES = [
1071
+ { code: "usd", countries: ["US"] },
1072
+ { code: "gbp", countries: ["GB", "UK"] },
1073
+ { code: "cad", countries: ["CA"] },
1074
+ { code: "aud", countries: ["AU"] },
1075
+ { code: "nzd", countries: ["NZ"] },
1076
+ { code: "sgd", countries: ["SG"] },
1077
+ ];
1078
+
1079
+ service.getAvailableCurrencies = function (defaultCountryID) {
1080
+ let currencies = SUPPORTED_CURRENCIES.map(({ code, countries }) => ({
1081
+ name: `${code.toUpperCase()} (${service.currencySymbol(code)})`,
1082
+ value: code,
1083
+ countryCode: Object.fromEntries(countries.map((c) => [c, true])),
1084
+ }));
1085
+
1086
+ if (defaultCountryID) {
1087
+ const idx = currencies.findIndex((c) => c.countryCode[defaultCountryID]);
1088
+ if (idx > 0) {
1089
+ const [match] = currencies.splice(idx, 1);
1090
+ currencies.unshift(match);
1091
+ }
1092
+ }
1093
+
1094
+ return currencies;
1095
+ };
1096
+
1097
+ ///////////////////////////////////////////////////////////////////////////////
1098
+
1099
+ /**
1100
+ * Creates a fast keyed hash object from an array of items
1101
+ * @alias utils.hash
1102
+ * @param {Array} array The array of items to convert into a hash
1103
+ * @param {String} key The key or path to the property on each item to use as the hashed key
1104
+ * @return {Object} A key/value paired object
1105
+ * @example
1106
+ * //Returns { jimbo:{id:'jimbo', title:'Jim Jones'}, {id:'roger', title:'Roger Fellow'} }
1107
+ * sdk.utils.hash([{id:'jimbo', title:'Jim Jones'}, {id:'roger', title:'Roger Fellow'}], 'id');
1108
+ *
1109
+ */
1110
+ service.hash = function (items, key) {
1111
+ items = !Array.isArray(items) ? [] : items;
1112
+ return items.reduce(function (memo, item) {
1113
+ const k = key ? _get(item, key) : item;
1114
+ memo[k] = item;
1115
+ return memo;
1116
+ }, {});
1117
+ };
1118
+
1119
+ //////////////////////////////////////////////////
1120
+
1121
+ /**
1122
+ * Returns a subset of values in an array that match a provided rule
1123
+ * @alias utils.extractFromArray
1124
+ * @param {Array} array The array you want to extract values from
1125
+ * @param {String} key The path to the child property you want to extract
1126
+ * @param {Boolean} sum Whether to sum the extracted values together in total
1127
+ * @param {Boolean} flatten Whether to flatten nested child arrays
1128
+ * @param {Boolean} unique Whether to only return unique values
1129
+ * @param {Boolean} exclude Whether to exclude null or undefined values
1130
+ * @param {Object} options Pass through extra options for how to extract the values
1131
+ * @return {Array} An array of all values retrieved from the array, unless provided arguments require otherwise
1132
+ * @example
1133
+ * //Returns [12, 45] as all the values
1134
+ * sdk.utils.extractFromArray([{name:'Wendy', age:12}, {name:'Roger', age:45}], 'age');
1135
+ *
1136
+ * //Returns 32
1137
+ * sdk.utils.extractFromArray([{name:'Wendy', age:12}, {name:'Roger', age:20}], 'age', {sum:true});
1138
+ *
1139
+ */
1140
+ service.extractFromArray = function (
1141
+ array,
1142
+ key,
1143
+ sum,
1144
+ flatten,
1145
+ unique,
1146
+ exclude,
1147
+ options,
1148
+ ) {
1149
+ options = options || {};
1150
+
1151
+ if (sum) {
1152
+ options.sum = sum;
1153
+ }
1154
+
1155
+ if (flatten) {
1156
+ options.flatten = true;
1157
+ }
1158
+
1159
+ if (unique) {
1160
+ options.unique = true;
1161
+ }
1162
+
1163
+ if (exclude) {
1164
+ options.excludeNull = true;
1165
+ }
1166
+
1167
+ /////////////////
1168
+
1169
+ //Filter the array options by a certain value and operator
1170
+ var extractedValues = array.reduce(function (set, entry) {
1171
+ //Get the value from the object
1172
+ var retrievedValue = _get(entry, key);
1173
+
1174
+ var isNull =
1175
+ !retrievedValue &amp;&amp; retrievedValue !== false &amp;&amp; retrievedValue !== 0;
1176
+ if (options.excludeNull &amp;&amp; isNull) {
1177
+ return set;
1178
+ }
1179
+
1180
+ set.push(retrievedValue);
1181
+ return set;
1182
+ }, []);
1183
+
1184
+ if (options.flatten) {
1185
+ extractedValues = extractedValues.flat();
1186
+ }
1187
+
1188
+ if (options.unique) {
1189
+ extractedValues = [...new Set(extractedValues)];
1190
+ }
1191
+
1192
+ if (options.sum) {
1193
+ extractedValues = extractedValues.reduce(function (a, b) {
1194
+ return a + b;
1195
+ }, 0);
1196
+ }
1197
+
1198
+ return extractedValues;
1199
+ };
1200
+
1201
+ //////////////////////////////////////////////////////
1202
+
1203
+ /**
1204
+ * A function that can return a selection of values that were found in an array that match a specific rule,
1205
+ * This is often used to evaluate expressions within form fields
1206
+ * @alias utils.matchInArray
1207
+ * @param {Array} array The array to check
1208
+ * @param {String} key The javascript dot notation path to extract
1209
+ * @param {String} value The value to compare against
1210
+ * @param {String} comparator The logical operator to use to compare the extracted value with the provided value ('>', '&lt;', '>=', '&lt;=', 'in', '==') Defaults to '==' (Is equal to)
1211
+ * @return {Array} Returns an array of matching values
1212
+ * @example
1213
+ * //Returns [{name:'Michael', age:45}] as that is only item in the array that matches the criteria
1214
+ * sdk.utils.matchInArray([{name:'Wendy', age:12}, {name:'Michael', age:45}], 'age', 45, '>=');
1215
+ *
1216
+ */
1217
+ service.matchInArray = function (array, key, v, comparator) {
1218
+ //Filter the array options by a certain v and comparator
1219
+ var matches = array.filter(function (entry) {
1220
+ //Get the v from the object
1221
+ var extracted = _get(entry, key);
1222
+ var found;
1223
+
1224
+ switch (comparator) {
1225
+ case "in":
1226
+ found = extracted.includes(v);
1227
+ break;
1228
+ case ">=":
1229
+ found = extracted >= v;
1230
+ break;
1231
+ case "&lt;=":
1232
+ found = extracted &lt;= v;
1233
+ break;
1234
+ case "&lt;":
1235
+ found = extracted &lt; v;
1236
+ break;
1237
+ case ">":
1238
+ found = extracted > v;
1239
+ break;
1240
+ case "==":
1241
+ default:
1242
+ if (v === undefined) {
1243
+ found = extracted;
1244
+ } else {
1245
+ found = extracted == v;
1246
+ }
1247
+ break;
1248
+ }
1249
+
1250
+ return found;
1251
+ });
1252
+
1253
+ return matches;
1254
+ };
1255
+
1256
+ ///////////////////////////////////////////////////////////////////////////////
1257
+
1258
+ /**
1259
+ * A helpful class that can take an array of values and return them as a comma seperated
1260
+ * string, If the values are objects, then a property to use as the string representation can be specified
1261
+ * @alias utils.comma
1262
+ * @param {Array} array The array of values to translate
1263
+ * @param {String} path An optional property key to use for each value
1264
+ * @return {String} The resulting comma seperated string
1265
+ * @example
1266
+ * //Returns 'cat, dog, bird'
1267
+ * sdk.utils.comma(['cat', 'dog', 'bird']);
1268
+ *
1269
+ * //Returns 'cat, dog, bird'
1270
+ * sdk.utils.comma([{title:'cat'}, {title:'dog'}, {title:'bird'}], 'title');
1271
+ */
1272
+ service.comma = function (array, path, limit) {
1273
+ if (limit) {
1274
+ array = array.slice(0, limit);
1275
+ }
1276
+
1277
+ return array
1278
+ .filter(function (item) {
1279
+ return item != null &amp;&amp; item !== undefined &amp;&amp; item !== "";
1280
+ })
1281
+ .map(function (item) {
1282
+ if (path &amp;&amp; path.length) {
1283
+ return _get(item, path);
1284
+ }
1285
+
1286
+ return item;
1287
+ })
1288
+ .join(", ");
1289
+ };
1290
+
1291
+ ///////////////////////////////////////////////////////////////////////////////
1292
+
1293
+ //Helper function to get an id of an object
1294
+
1295
+ /**
1296
+ * Returns a specified _id for an object
1297
+ * @alias utils.id
1298
+ * @param {Object} input An object that is or has an _id property
1299
+ * @param {Boolean} asObjectID Whether to convert to a Mongo ObjectId
1300
+ * @return {String} Will return either a string or a Mongo ObjectId
1301
+ *
1302
+ * @example
1303
+ *
1304
+ * //Returns '5cb3d8b3a2219970e6f86927'
1305
+ * sdk.utils.id('5cb3d8b3a2219970e6f86927')
1306
+ *
1307
+ * //Returns true
1308
+ * typeof service.id({_id:'5cb3d8b3a2219970e6f86927', title, ...}) == 'string';
1309
+
1310
+ * //Returns true
1311
+ * typeof service.id({_id:'5cb3d8b3a2219970e6f86927'}, true) == 'object';
1312
+ */
1313
+ service.id = function (source) {
1314
+ if (!source) {
1315
+ return;
1316
+ }
1317
+
1318
+ /////////////////////////////////
1319
+
1320
+ var output;
1321
+
1322
+ if (source._id) {
1323
+ output = String(source._id);
1324
+ } else {
1325
+ output = String(source);
1326
+ }
1327
+
1328
+ var isValid = service.isValidID(output);
1329
+ if (!isValid) {
1330
+ return;
1331
+ }
1332
+
1333
+ return output;
1334
+ };
1335
+
1336
+ ///////////////////////////////////////////////////////////////////////////////
1337
+
1338
+ /**
1339
+ * Cleans and maps an array of objects to an array of IDs
1340
+ * @alias utils.ids
1341
+ * @param {Array} array An array of objects or object ids
1342
+ * @param {Boolean} asObjectID Whether or not to map the ids as Mongo ObjectIds
1343
+ * @return {Array} An array of Ids
1344
+ *
1345
+ * @example
1346
+ * //Returns ['5cb3d8b3a2219970e6f86927', '5cb3d8b3a2219970e6f86927', '5cb3d8b3a2219970e6f86927']
1347
+ * sdk.utils.ids([{_id:'5cb3d8b3a2219970e6f86927'}, {_id:'5cb3d8b3a2219970e6f86927'}, null, '5cb3d8b3a2219970e6f86927'])
1348
+ */
1349
+
1350
+ service.ids = function (array) {
1351
+ if (!array) {
1352
+ return [];
1353
+ }
1354
+
1355
+ /////////////////////////////////
1356
+
1357
+ var ids = Object.keys(
1358
+ array.reduce(function (set, entry) {
1359
+ if (!entry) {
1360
+ return set;
1361
+ }
1362
+
1363
+ var cleaned = service.id(entry);
1364
+
1365
+ if (cleaned) {
1366
+ set[cleaned] = 1;
1367
+ }
1368
+
1369
+ return set;
1370
+ }, {}),
1371
+ );
1372
+
1373
+ ///////////////////////////////
1374
+
1375
+ return ids;
1376
+ };
1377
+
1378
+ ///////////////////////////////////////////////////////////////////////////////
1379
+
1380
+ service.isValidID = function (input) {
1381
+ var checkForHexRegExp = new RegExp("^[0-9a-fA-F]{24}$");
1382
+ return checkForHexRegExp.test(String(input));
1383
+ };
1384
+
1385
+ ///////////////////////////////////////////////////////////////////////////////
1386
+
1387
+ /**
1388
+ * Helper function for retrieving a human readable error message from server error response objects
1389
+ * @alias utils.errorMessage
1390
+ * @param {Object} error The error object to translate
1391
+ * @return {String} The resulting human readable error message
1392
+ */
1393
+ service.errorMessage = function (err) {
1394
+ if (!err) {
1395
+ return;
1396
+ }
1397
+
1398
+ if (Array.isArray(err)) {
1399
+ err = err[0];
1400
+ }
1401
+
1402
+ // Walk through the error object to find the most useful message
1403
+ let message =
1404
+ err?.response?.data?.message ||
1405
+ (typeof err?.response?.data === "string" ? err.response.data : null) ||
1406
+ err?.message ||
1407
+ null;
1408
+
1409
+ if (Array.isArray(message)) {
1410
+ message = message[0];
1411
+ }
1412
+
1413
+ return message || JSON.stringify(err);
1414
+ };
1415
+
1416
+ ////////////////////////////////////
1417
+
1418
+ /**
1419
+ * Generates a globally unique ID, helpful for adding unique keys for iterable loops
1420
+ * @alias utils.guid
1421
+ * @return {String} The new globally unique identifier
1422
+ * @example
1423
+ * //Returns 4323a78br-z16h-289j-zwl1-938lda334asd
1424
+ * sdk.utils.guid()
1425
+ */
1426
+ service.guid = function () {
1427
+ if (typeof crypto !== "undefined" &amp;&amp; crypto.randomUUID) {
1428
+ return crypto.randomUUID();
1429
+ }
1430
+ // Fallback for environments without crypto.randomUUID
1431
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
1432
+ const r = (Math.random() * 16) | 0;
1433
+ return (c === "x" ? r : (r &amp; 0x3) | 0x8).toString(16);
1434
+ });
1435
+ };
1436
+
1437
+ ////////////////////////////////////
1438
+ ////////////////////////////////////
1439
+ ////////////////////////////////////
1440
+
1441
+ /**
1442
+ * Helper function for cleaning strings to use as database ids
1443
+ * @alias utils.machineName
1444
+ * @param {String} string The string to clean eg. (Awesome Event!)
1445
+ * @return {String} A cleaned and formatted string eg. (awesomeEvent)
1446
+ */
1447
+ service.machineName = function (string) {
1448
+ if (!string || !string.length) {
1449
+ return;
1450
+ }
1451
+
1452
+ var regexp = /[^a-zA-Z0-9-_]+/g;
1453
+ return String(string)
1454
+ .replace(regexp, " ")
1455
+ .split("_")
1456
+ .map((part) => _camelCase(part))
1457
+ .join("_");
1458
+ };
1459
+
1460
+ /////////////////////////////////////////////
1461
+ /////////////////////////////////////////////
1462
+ /////////////////////////////////////////////
1463
+
1464
+ export default service;
1465
+
1466
+ /////////////////////////////////////////////
1467
+
1468
+ /**
1469
+ * A lightweight event emitter that can be attached to any service object
1470
+ * to provide pub/sub event capabilities. Can be called with or without `new`.
1471
+ */
1472
+ export function EventDispatcher() {
1473
+ const handlers = new Map();
1474
+
1475
+ const emitter = {
1476
+ dispatch(event, details) {
1477
+ const fns = handlers.get(event);
1478
+ if (fns) {
1479
+ for (const fn of fns) {
1480
+ fn(details);
1481
+ }
1482
+ }
1483
+ },
1484
+
1485
+ addEventListener(event, callback) {
1486
+ if (typeof callback !== "function") {
1487
+ throw new TypeError(
1488
+ `Expected a function for addEventListener, received ${typeof callback}`,
1489
+ );
1490
+ }
1491
+
1492
+ if (!handlers.has(event)) {
1493
+ handlers.set(event, new Set());
1494
+ }
1495
+
1496
+ handlers.get(event).add(callback);
1497
+ },
1498
+
1499
+ removeEventListener(event, callback) {
1500
+ const fns = handlers.get(event);
1501
+ if (fns) {
1502
+ fns.delete(callback);
1503
+ if (fns.size === 0) {
1504
+ handlers.delete(event);
1505
+ }
1506
+ }
1507
+ },
1508
+
1509
+ removeAllListeners() {
1510
+ handlers.clear();
1511
+ },
1512
+
1513
+ bootstrap(target) {
1514
+ if (!target) return;
1515
+ target.dispatch = emitter.dispatch;
1516
+ target.addEventListener = emitter.addEventListener;
1517
+ target.removeEventListener = emitter.removeEventListener;
1518
+ target.removeAllListeners = emitter.removeAllListeners;
1519
+ },
1520
+ };
1521
+
1522
+ return emitter;
1523
+ }
1524
+ </code></pre>
1525
+ </article>
1526
+ </section>
1527
+
1528
+
1529
+
1530
+
1531
+
1532
+
1533
+ </div>
1534
+
1535
+ <br class="clear">
1536
+
1537
+ <footer>
1538
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.6.11</a> on Mon Jul 13 2026 18:47:22 GMT+1000 (Australian Eastern Standard Time) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
1539
+ </footer>
1540
+
1541
+ <script>prettyPrint();</script>
1542
+ <script src="scripts/polyfill.js"></script>
1543
+ <script src="scripts/linenumber.js"></script>
1544
+
1545
+
1546
+
1547
+ </body>
1548
+ </html>