@qikdev/sdk 1.0.10 → 1.0.13
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.
- package/dist/main.js +1294 -1229
- package/package.json +1 -1
- package/src/api/qik.access.js +177 -201
- package/src/api/qik.api.js +55 -28
- package/src/api/qik.auth.js +181 -113
- package/src/api/qik.cache.js +29 -7
- package/src/api/qik.content.js +275 -147
- package/src/api/qik.core.js +19 -28
- package/src/api/qik.files.js +48 -38
- package/src/api/qik.filter.js +148 -207
- package/src/api/qik.geo.js +59 -42
- package/src/api/qik.socket.js +228 -62
- package/src/api/qik.storage.js +17 -8
- package/src/api/qik.system.js +6 -5
- package/src/api/qik.utils.js +244 -199
- package/src/version.js +1 -1
package/src/api/qik.utils.js
CHANGED
|
@@ -5,9 +5,15 @@ import { isBrowser, isNode } from "browser-or-node";
|
|
|
5
5
|
///////////////////////////////////////////////////////////////////////////////
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* @
|
|
9
|
-
*
|
|
8
|
+
* @classdesc General helpers that run locally: ids (id, ids, isValidID, getTypeFromID), parsing and cleaning
|
|
9
|
+
* values (parseDate, parseURL, parseEmail, parseNumber, parseInt, parseBoolean, cleanValue, isValidValue),
|
|
10
|
+
* working with arrays (hash, extractFromArray, matchInArray, comma), money (formatCurrency, currencySymbol),
|
|
11
|
+
* field lists (mapFields, getAllFields), error messages, unique ids and loading external scripts or styles.
|
|
12
|
+
* @alias utils
|
|
10
13
|
* @class
|
|
14
|
+
* @example
|
|
15
|
+
* const ids = sdk.utils.ids(records); // ['61eca...', ...]
|
|
16
|
+
* const byID = sdk.utils.hash(records, '_id');
|
|
11
17
|
*/
|
|
12
18
|
|
|
13
19
|
const service = {};
|
|
@@ -17,14 +23,13 @@ const service = {};
|
|
|
17
23
|
const loadedExternalScripts = {};
|
|
18
24
|
|
|
19
25
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
26
|
+
* Adds a <script> tag for an external JavaScript file, once per URL: later calls with the same URL return the
|
|
27
|
+
* same promise. Browser only.
|
|
22
28
|
* @alias utils.loadExternalScript
|
|
23
|
-
* @param
|
|
24
|
-
* @
|
|
29
|
+
* @param {String} url The script's URL.
|
|
30
|
+
* @returns {Promise<String>} Resolves with the URL once the script has loaded. It never settles if the script fails to load.
|
|
25
31
|
* @example
|
|
26
|
-
*
|
|
27
|
-
* await sdk.utils.loadExternalScript('https://cdn.javascript.com/external/script.js');
|
|
32
|
+
* await sdk.utils.loadExternalScript('https://cdn.example.com/library.js');
|
|
28
33
|
*/
|
|
29
34
|
|
|
30
35
|
service.loadExternalScript = function (url) {
|
|
@@ -64,14 +69,12 @@ service.loadExternalScript = function (url) {
|
|
|
64
69
|
const loadedExternalStyles = {};
|
|
65
70
|
|
|
66
71
|
/**
|
|
67
|
-
*
|
|
68
|
-
* ensuring that it will only be included once
|
|
72
|
+
* Adds a <link rel="stylesheet"> tag for an external CSS file, once per URL. Browser only.
|
|
69
73
|
* @alias utils.loadExternalStyle
|
|
70
|
-
* @param
|
|
71
|
-
* @
|
|
74
|
+
* @param {String} url The stylesheet's URL.
|
|
75
|
+
* @returns {Promise<String>} Resolves with the URL once loaded (straight away if it was added before). It never settles if the stylesheet fails to load.
|
|
72
76
|
* @example
|
|
73
|
-
*
|
|
74
|
-
* await sdk.utils.loadExternalStyle('https://cdn.css.com/external/style.css');
|
|
77
|
+
* await sdk.utils.loadExternalStyle('https://cdn.example.com/theme.css');
|
|
75
78
|
*/
|
|
76
79
|
service.loadExternalStyle = function (url) {
|
|
77
80
|
const promise = new Promise(createNewScript);
|
|
@@ -109,16 +112,17 @@ service.loadExternalStyle = function (url) {
|
|
|
109
112
|
///////////////////////////////////////////////////////////////////////////////
|
|
110
113
|
|
|
111
114
|
/**
|
|
112
|
-
*
|
|
115
|
+
* Checks whether a value counts as "provided": false for undefined, null, '' and the strings 'undefined'
|
|
116
|
+
* and 'null' (in any case); true for everything else, including 0, false and empty arrays.
|
|
113
117
|
* @alias utils.exists
|
|
114
|
-
* @param
|
|
115
|
-
* @
|
|
118
|
+
* @param {*} value The value to check.
|
|
119
|
+
* @returns {Boolean} Whether the value is provided.
|
|
116
120
|
* @example
|
|
117
|
-
*
|
|
118
121
|
* sdk.utils.exists('undefined'); // false
|
|
119
|
-
* sdk.utils.exists([]); // true
|
|
120
122
|
* sdk.utils.exists(''); // false
|
|
121
123
|
* sdk.utils.exists(undefined); // false
|
|
124
|
+
* sdk.utils.exists([]); // true
|
|
125
|
+
* sdk.utils.exists(0); // true
|
|
122
126
|
*/
|
|
123
127
|
service.exists = function (value) {
|
|
124
128
|
var isUndefinedOrNull;
|
|
@@ -160,14 +164,13 @@ service.exists = function (value) {
|
|
|
160
164
|
//////////////////////////////////
|
|
161
165
|
|
|
162
166
|
/**
|
|
163
|
-
*
|
|
164
|
-
* returns undefined if it can not be parsed
|
|
167
|
+
* Converts a value to a JavaScript Date.
|
|
165
168
|
* @alias utils.parseDate
|
|
166
|
-
* @param
|
|
167
|
-
* @
|
|
169
|
+
* @param {String|Number|Date} input An ISO string, timestamp or Date.
|
|
170
|
+
* @returns {Date|undefined} The Date, or undefined when the input is empty or not a valid date.
|
|
168
171
|
* @example
|
|
169
|
-
*
|
|
170
|
-
* sdk.utils.parseDate(
|
|
172
|
+
* sdk.utils.parseDate('2026-09-29T10:00:00Z'); // Date
|
|
173
|
+
* sdk.utils.parseDate('not a date'); // undefined
|
|
171
174
|
*/
|
|
172
175
|
service.parseDate = function (input) {
|
|
173
176
|
if (!input) {
|
|
@@ -190,16 +193,18 @@ service.parseDate = function (input) {
|
|
|
190
193
|
};
|
|
191
194
|
|
|
192
195
|
/**
|
|
193
|
-
*
|
|
196
|
+
* Turns user input into a usable link: adds http:// when no scheme is given, turns a bare email address into
|
|
197
|
+
* a mailto: link, and leaves relative paths ('/about'), '://', mailto:, tel: and sms: links as they are.
|
|
198
|
+
* Spaces are removed. Returns false when the host isn't a valid domain name or IPv4 address.
|
|
194
199
|
* @alias utils.parseURL
|
|
195
|
-
* @param
|
|
196
|
-
* @
|
|
200
|
+
* @param {String} string The input.
|
|
201
|
+
* @returns {String|Boolean} The link, or false when it can't be made into one.
|
|
197
202
|
* @example
|
|
198
|
-
*
|
|
199
|
-
* sdk.utils.parseURL('
|
|
200
|
-
* sdk.utils.parseURL('
|
|
201
|
-
* sdk.utils.parseURL('
|
|
202
|
-
* sdk.utils.parseURL('
|
|
203
|
+
* sdk.utils.parseURL('example.com'); // 'http://example.com'
|
|
204
|
+
* sdk.utils.parseURL('https://example.com/a?b=c'); // 'https://example.com/a?b=c'
|
|
205
|
+
* sdk.utils.parseURL('hello@example.com'); // 'mailto:hello@example.com'
|
|
206
|
+
* sdk.utils.parseURL('/about'); // '/about'
|
|
207
|
+
* sdk.utils.parseURL('not a url'); // false
|
|
203
208
|
*/
|
|
204
209
|
service.parseURL = function (string) {
|
|
205
210
|
if (!string) {
|
|
@@ -294,18 +299,16 @@ service.parseURL = function (string) {
|
|
|
294
299
|
///////////////////////////////////////////////
|
|
295
300
|
|
|
296
301
|
/**
|
|
297
|
-
*
|
|
302
|
+
* Converts a value to a number, returning 0 when it isn't numeric.
|
|
298
303
|
* @alias utils.parseNumber
|
|
299
|
-
* @param
|
|
300
|
-
* @param
|
|
301
|
-
* @
|
|
304
|
+
* @param {String|Number} input The value.
|
|
305
|
+
* @param {Number} [decimalPoints] Round to this many decimal places.
|
|
306
|
+
* @returns {Number} The number, or 0.
|
|
302
307
|
* @example
|
|
303
|
-
*
|
|
304
|
-
* sdk.utils.parseNumber('
|
|
305
|
-
* sdk.utils.parseNumber('
|
|
306
|
-
* sdk.utils.parseNumber(
|
|
307
|
-
* sdk.utils.parseNumber(null); // Returns 0
|
|
308
|
-
* sdk.utils.parseNumber(); // Returns 0
|
|
308
|
+
* sdk.utils.parseNumber('123'); // 123
|
|
309
|
+
* sdk.utils.parseNumber('75.501', 2); // 75.5
|
|
310
|
+
* sdk.utils.parseNumber('abc'); // 0
|
|
311
|
+
* sdk.utils.parseNumber(null); // 0
|
|
309
312
|
*/
|
|
310
313
|
service.parseNumber = function (input, decimalPoints) {
|
|
311
314
|
if (!input) {
|
|
@@ -326,15 +329,13 @@ service.parseNumber = function (input, decimalPoints) {
|
|
|
326
329
|
};
|
|
327
330
|
|
|
328
331
|
/**
|
|
329
|
-
*
|
|
332
|
+
* Checks whether a string is a valid email address.
|
|
330
333
|
* @alias utils.isValidEmailAddress
|
|
331
|
-
* @param
|
|
332
|
-
* @
|
|
334
|
+
* @param {String} email The text to check.
|
|
335
|
+
* @returns {Boolean} Whether it is a valid email address.
|
|
333
336
|
* @example
|
|
334
|
-
*
|
|
335
|
-
* sdk.utils.isValidEmailAddress('
|
|
336
|
-
* sdk.utils.isValidEmailAddress('hello@world.com'); // Returns true
|
|
337
|
-
* sdk.utils.isValidEmailAddress('something@special.io'); // Returns true
|
|
337
|
+
* sdk.utils.isValidEmailAddress('123.com'); // false
|
|
338
|
+
* sdk.utils.isValidEmailAddress('hello@example.com'); // true
|
|
338
339
|
*/
|
|
339
340
|
service.isValidEmailAddress = function (email) {
|
|
340
341
|
var tester =
|
|
@@ -366,14 +367,13 @@ service.isValidEmailAddress = function (email) {
|
|
|
366
367
|
};
|
|
367
368
|
|
|
368
369
|
/**
|
|
369
|
-
*
|
|
370
|
+
* Lowercases an email address and checks it's valid.
|
|
370
371
|
* @alias utils.parseEmail
|
|
371
|
-
* @param
|
|
372
|
-
* @
|
|
372
|
+
* @param {String} input The text to parse.
|
|
373
|
+
* @returns {String|Boolean} The lowercased address, or false when it isn't a valid email address.
|
|
373
374
|
* @example
|
|
374
|
-
*
|
|
375
|
-
* sdk.utils.parseEmail('
|
|
376
|
-
* sdk.utils.isValidEmailAddress('hello.world.com'); // Returns false
|
|
375
|
+
* sdk.utils.parseEmail('ToMack@Example.com'); // 'tomack@example.com'
|
|
376
|
+
* sdk.utils.parseEmail('hello.world.com'); // false
|
|
377
377
|
*/
|
|
378
378
|
service.parseEmail = function (input) {
|
|
379
379
|
if (!input) {
|
|
@@ -393,15 +393,14 @@ service.parseEmail = function (input) {
|
|
|
393
393
|
//////////////////////////////////
|
|
394
394
|
|
|
395
395
|
/**
|
|
396
|
-
*
|
|
396
|
+
* Converts a value to a whole number (dropping any decimals), returning 0 when it isn't numeric.
|
|
397
397
|
* @alias utils.parseInt
|
|
398
|
-
* @param
|
|
399
|
-
* @
|
|
398
|
+
* @param {String|Number} input The value.
|
|
399
|
+
* @returns {Number} The integer, or 0.
|
|
400
400
|
* @example
|
|
401
|
-
*
|
|
402
|
-
* sdk.utils.parseInt('
|
|
403
|
-
* sdk.utils.parseInt('cows'); //
|
|
404
|
-
* sdk.utils.parseInt(); // returns 0
|
|
401
|
+
* sdk.utils.parseInt('134'); // 134
|
|
402
|
+
* sdk.utils.parseInt('12.9'); // 12
|
|
403
|
+
* sdk.utils.parseInt('cows'); // 0
|
|
405
404
|
*/
|
|
406
405
|
service.parseInt = function (input) {
|
|
407
406
|
if (!input) {
|
|
@@ -417,19 +416,20 @@ service.parseInt = function (input) {
|
|
|
417
416
|
};
|
|
418
417
|
|
|
419
418
|
/**
|
|
420
|
-
*
|
|
421
|
-
* a
|
|
419
|
+
* Converts a value to a field data type: 'reference' gives the id, 'boolean' reads 'yes'/'true'/'1' style
|
|
420
|
+
* strings, 'url' and 'email' are parsed as by parseURL / parseEmail, 'key' gives a camelCase key, 'date' a Date,
|
|
421
|
+
* number types a number, 'object' only accepts plain objects, 'string' a string. Other types become strings.
|
|
422
422
|
* @alias utils.cleanValue
|
|
423
|
-
* @param
|
|
424
|
-
* @param
|
|
425
|
-
* @param
|
|
426
|
-
* @
|
|
423
|
+
* @param {*} data The value.
|
|
424
|
+
* @param {String} type The data type.
|
|
425
|
+
* @param {Object} [options] Options.
|
|
426
|
+
* @param {Boolean} [options.strict] Return any provided value unchanged (booleans are still converted).
|
|
427
|
+
* @returns {*} The converted value; undefined (or false, for url and email) when it can't be converted.
|
|
427
428
|
* @example
|
|
428
|
-
*
|
|
429
|
-
* sdk.utils.cleanValue(
|
|
430
|
-
* sdk.utils.cleanValue('
|
|
431
|
-
* sdk.utils.cleanValue('
|
|
432
|
-
* sdk.utils.cleanValue('Hello.World@email.COM', 'email'); // returns 'hello.world@email.com';
|
|
429
|
+
* sdk.utils.cleanValue({ _id: '61eca4746971e75c1fc670cf', title: 'Item' }, 'reference'); // '61eca4746971e75c1fc670cf'
|
|
430
|
+
* sdk.utils.cleanValue('true', 'boolean'); // true
|
|
431
|
+
* sdk.utils.cleanValue('Mr Rogers House', 'key'); // 'mrRogersHouse'
|
|
432
|
+
* sdk.utils.cleanValue('Hello.World@Example.COM', 'email'); // 'hello.world@example.com'
|
|
433
433
|
*/
|
|
434
434
|
service.cleanValue = function (data, type, options) {
|
|
435
435
|
if (!options) {
|
|
@@ -529,6 +529,20 @@ service.cleanValue = function (data, type, options) {
|
|
|
529
529
|
}
|
|
530
530
|
};
|
|
531
531
|
|
|
532
|
+
/**
|
|
533
|
+
* Checks whether a value is valid for a field data type ('url', 'key', 'date', 'email', 'number', 'decimal',
|
|
534
|
+
* 'float', 'integer', 'boolean', 'reference', 'string', 'object', 'array'). Values that aren't provided (see
|
|
535
|
+
* exists) are never valid; unknown types are never valid.
|
|
536
|
+
* @alias utils.isValidValue
|
|
537
|
+
* @param {*} value The value.
|
|
538
|
+
* @param {String} dataType The data type.
|
|
539
|
+
* @param {Boolean} [strict] Require the right JavaScript type (a real number, Date, boolean or string) rather than a convertible value.
|
|
540
|
+
* @returns {Boolean} Whether the value is valid.
|
|
541
|
+
* @example
|
|
542
|
+
* sdk.utils.isValidValue('42', 'integer'); // true
|
|
543
|
+
* sdk.utils.isValidValue('42', 'integer', true); // false: not a number
|
|
544
|
+
* sdk.utils.isValidValue('yes', 'boolean'); // true
|
|
545
|
+
*/
|
|
532
546
|
service.isValidValue = function (value, dataType, strict) {
|
|
533
547
|
var isValue = service.exists(value);
|
|
534
548
|
var valueIsNumber = typeof value == "number";
|
|
@@ -649,23 +663,15 @@ service.isValidValue = function (value, dataType, strict) {
|
|
|
649
663
|
};
|
|
650
664
|
|
|
651
665
|
/**
|
|
652
|
-
*
|
|
666
|
+
* Converts a value to true or false. 'true', 'y', 'yes', '1' and 't' (any case) are true; 'false', 'n', 'no',
|
|
667
|
+
* '0', 'f', '-1', 'null', 'undefined' and '' are false; anything else is judged by JavaScript truthiness.
|
|
653
668
|
* @alias utils.parseBoolean
|
|
654
|
-
* @param
|
|
655
|
-
* @
|
|
669
|
+
* @param {*} value The value.
|
|
670
|
+
* @returns {Boolean} The boolean.
|
|
656
671
|
* @example
|
|
657
|
-
*
|
|
658
|
-
* sdk.utils.parseBoolean('
|
|
659
|
-
* sdk.utils.parseBoolean('
|
|
660
|
-
* sdk.utils.parseBoolean('YES'); // returns true
|
|
661
|
-
* sdk.utils.parseBoolean('1'); // returns true
|
|
662
|
-
* sdk.utils.parseBoolean('t'); // returns true
|
|
663
|
-
* sdk.utils.parseBoolean(''); // returns false
|
|
664
|
-
* sdk.utils.parseBoolean('0'); // returns false
|
|
665
|
-
* sdk.utils.parseBoolean('n'); // returns false
|
|
666
|
-
* sdk.utils.parseBoolean('no'); // returns false
|
|
667
|
-
* sdk.utils.parseBoolean('f'); // returns false
|
|
668
|
-
* sdk.utils.parseBoolean('null'); // returns false
|
|
672
|
+
* sdk.utils.parseBoolean('YES'); // true
|
|
673
|
+
* sdk.utils.parseBoolean('0'); // false
|
|
674
|
+
* sdk.utils.parseBoolean('no'); // false
|
|
669
675
|
*/
|
|
670
676
|
service.parseBoolean = function (value) {
|
|
671
677
|
switch (String(value).toLowerCase()) {
|
|
@@ -694,12 +700,32 @@ service.parseBoolean = function (value) {
|
|
|
694
700
|
|
|
695
701
|
///////////////////////////////////////////////////////////////////////////////
|
|
696
702
|
|
|
703
|
+
/**
|
|
704
|
+
* Deep-copies plain data through JSON. Dates become strings, and functions and undefined values are dropped.
|
|
705
|
+
* @alias utils.clone
|
|
706
|
+
* @param {*} input JSON-safe data.
|
|
707
|
+
* @returns {*} The copy.
|
|
708
|
+
* @example
|
|
709
|
+
* const copy = sdk.utils.clone(record);
|
|
710
|
+
*/
|
|
697
711
|
service.clone = function (input) {
|
|
698
712
|
return JSON.parse(JSON.stringify(input));
|
|
699
713
|
};
|
|
700
714
|
|
|
701
715
|
///////////////////////////////////////////////////////////////////////////////
|
|
702
716
|
|
|
717
|
+
/**
|
|
718
|
+
* Lists every field of a content type or definition as a flat, filterable list sorted by title: its own
|
|
719
|
+
* fields, its definition fields (under `data`, or `formData` and `data` for form submissions), and for profiles
|
|
720
|
+
* the computed '_age' and '_dob'. Each entry is a field with `path` (e.g. 'data.shirtSize'), `trail` and a `title`
|
|
721
|
+
* that includes its parent groups ('Address › City'). Single-value object groups themselves are left out.
|
|
722
|
+
* @alias utils.getAllFields
|
|
723
|
+
* @param {Object} actualDefinition A glossary entry (see sdk.content.glossary) with `fields` and optionally `definedFields`.
|
|
724
|
+
* @returns {Array<Object>} The fields.
|
|
725
|
+
* @example
|
|
726
|
+
* const { profile } = await sdk.content.glossary({ hash: true });
|
|
727
|
+
* const paths = sdk.utils.getAllFields(profile).map((field) => field.path);
|
|
728
|
+
*/
|
|
703
729
|
service.getAllFields = function (actualDefinition) {
|
|
704
730
|
const self = this;
|
|
705
731
|
const isProfile =
|
|
@@ -800,6 +826,23 @@ service.getAllFields = function (actualDefinition) {
|
|
|
800
826
|
};
|
|
801
827
|
///////////////////////////////////////////////////////////////////////////////
|
|
802
828
|
|
|
829
|
+
/**
|
|
830
|
+
* Flattens a nested field list, adding to each field its `trail` (array of keys), `path` (dot path) and
|
|
831
|
+
* `titles` (titles of the field and its parent groups). Layout-only groups (groups without asObject) are
|
|
832
|
+
* walked through but not listed themselves.
|
|
833
|
+
* @alias utils.mapFields
|
|
834
|
+
* @param {Array<Object>} fields The fields.
|
|
835
|
+
* @param {Object} [options] Options.
|
|
836
|
+
* @param {Number} [options.depth] Stop descending after this many levels.
|
|
837
|
+
* @param {Boolean} [options.includeLayout] Also list layout-only groups.
|
|
838
|
+
* @param {Boolean} [options.includeArrayDelimeter] Add '[]' after the key of multi-value groups in paths, e.g. 'items[].title'.
|
|
839
|
+
* @param {String} [options.arrayDelimeter] The marker to use instead of '[]'.
|
|
840
|
+
* @param {Boolean} [options.original] Annotate and return the original field objects instead of copies.
|
|
841
|
+
* @returns {Array<Object>} The flattened fields.
|
|
842
|
+
* @example
|
|
843
|
+
* const flat = sdk.utils.mapFields(definition.fields, { includeArrayDelimeter: true });
|
|
844
|
+
* const paths = flat.map((field) => field.path);
|
|
845
|
+
*/
|
|
803
846
|
service.mapFields = function (fields, options) {
|
|
804
847
|
if (!options) {
|
|
805
848
|
options = {};
|
|
@@ -896,13 +939,14 @@ service.mapFields = function (fields, options) {
|
|
|
896
939
|
///////////////////////////////////////////////////////////////////////////////
|
|
897
940
|
|
|
898
941
|
/**
|
|
899
|
-
*
|
|
942
|
+
* Builds a URL query string from an object. Entries whose value is undefined, null, false, 0 or '' are left
|
|
943
|
+
* out; arrays become repeated keys. No leading '?' or '&'.
|
|
900
944
|
* @alias utils.mapParameters
|
|
901
|
-
* @param
|
|
902
|
-
* @
|
|
945
|
+
* @param {Object} parameters The values.
|
|
946
|
+
* @returns {String} The query string.
|
|
903
947
|
* @example
|
|
904
|
-
*
|
|
905
|
-
*
|
|
948
|
+
* sdk.utils.mapParameters({ this: 'that', hello: 'world', tags: ['a', 'b'] });
|
|
949
|
+
* // 'this=that&hello=world&tags=a&tags=b'
|
|
906
950
|
*/
|
|
907
951
|
service.mapParameters = function (parameters) {
|
|
908
952
|
parameters = parameters || {};
|
|
@@ -929,19 +973,15 @@ service.mapParameters = function (parameters) {
|
|
|
929
973
|
///////////////////////////////////////////////////////////////////////////////
|
|
930
974
|
|
|
931
975
|
/**
|
|
932
|
-
*
|
|
976
|
+
* Formats an amount in cents as money with a currency symbol ('£' for gbp, '€' for eur, '$' for everything else).
|
|
933
977
|
* @alias utils.formatCurrency
|
|
934
|
-
* @param
|
|
935
|
-
* @param
|
|
936
|
-
* @
|
|
978
|
+
* @param {Number} value The amount in cents (1000 = 10.00).
|
|
979
|
+
* @param {String} [currency] Currency code, e.g. 'aud', 'gbp'.
|
|
980
|
+
* @param {Number} [decimalPoints] Decimal places, default 2.
|
|
981
|
+
* @returns {String} The formatted amount.
|
|
937
982
|
* @example
|
|
938
|
-
*
|
|
939
|
-
* //
|
|
940
|
-
* sdk.utils.formatCurrency(1000, 'gbp');
|
|
941
|
-
*
|
|
942
|
-
* //Returns $10.00
|
|
943
|
-
* sdk.utils.formatCurrency(1000, 'usd');
|
|
944
|
-
*
|
|
983
|
+
* sdk.utils.formatCurrency(1000, 'gbp'); // '£10.00'
|
|
984
|
+
* sdk.utils.formatCurrency(1999, 'aud'); // '$19.99'
|
|
945
985
|
*/
|
|
946
986
|
service.formatCurrency = function (value, currency, decimalPoints) {
|
|
947
987
|
if (!value || isNaN(value)) {
|
|
@@ -955,18 +995,13 @@ service.formatCurrency = function (value, currency, decimalPoints) {
|
|
|
955
995
|
};
|
|
956
996
|
|
|
957
997
|
/**
|
|
958
|
-
*
|
|
998
|
+
* Works out the base type of a record from its id (characters 9-10 of every Qik id encode the type).
|
|
959
999
|
* @alias utils.getTypeFromID
|
|
960
|
-
* @param
|
|
961
|
-
* @
|
|
1000
|
+
* @param {String|Object} id The id, or an object with an _id.
|
|
1001
|
+
* @returns {String|undefined} The type key, e.g. 'profile', or undefined for an invalid or unknown id. Organisation definitions share their base type's id prefix.
|
|
962
1002
|
* @example
|
|
963
|
-
*
|
|
964
|
-
* //
|
|
965
|
-
* sdk.utils.getTypeFromID('52b523f775beea960013f6cd');
|
|
966
|
-
*
|
|
967
|
-
* // Returns 'role'
|
|
968
|
-
* sdk.utils.getTypeFromID('62b59cb572fb4772e7b5fa93');
|
|
969
|
-
*
|
|
1003
|
+
* sdk.utils.getTypeFromID('52b523f775beea960013f6cd'); // 'user'
|
|
1004
|
+
* sdk.utils.getTypeFromID('62b59cb572fb4772e7b5fa93'); // 'role'
|
|
970
1005
|
*/
|
|
971
1006
|
service.getTypeFromID = function (id) {
|
|
972
1007
|
id = service.id(id);
|
|
@@ -1038,18 +1073,13 @@ service.getTypeFromID = function (id) {
|
|
|
1038
1073
|
};
|
|
1039
1074
|
|
|
1040
1075
|
/**
|
|
1041
|
-
*
|
|
1076
|
+
* Returns the symbol for a currency code: '£' for gbp, '€' for eur, '$' for anything else.
|
|
1042
1077
|
* @alias utils.currencySymbol
|
|
1043
|
-
* @param
|
|
1044
|
-
* @
|
|
1078
|
+
* @param {String} currency The currency code (any case).
|
|
1079
|
+
* @returns {String} The symbol.
|
|
1045
1080
|
* @example
|
|
1046
|
-
*
|
|
1047
|
-
* //
|
|
1048
|
-
* sdk.utils.currencySymbol('gbp');
|
|
1049
|
-
*
|
|
1050
|
-
* //Returns $
|
|
1051
|
-
* sdk.utils.currencySymbol('usd');
|
|
1052
|
-
*
|
|
1081
|
+
* sdk.utils.currencySymbol('gbp'); // '£'
|
|
1082
|
+
* sdk.utils.currencySymbol('usd'); // '$'
|
|
1053
1083
|
*/
|
|
1054
1084
|
const CURRENCY_SYMBOLS = { gbp: "\u00A3", eur: "\u20AC" };
|
|
1055
1085
|
|
|
@@ -1066,6 +1096,15 @@ const SUPPORTED_CURRENCIES = [
|
|
|
1066
1096
|
{ code: "sgd", countries: ["SG"] },
|
|
1067
1097
|
];
|
|
1068
1098
|
|
|
1099
|
+
/**
|
|
1100
|
+
* Lists the currencies Qik supports (usd, gbp, cad, aud, nzd, sgd) as picker options, optionally with the one
|
|
1101
|
+
* for a country first.
|
|
1102
|
+
* @alias utils.getAvailableCurrencies
|
|
1103
|
+
* @param {String} [defaultCountryID] A two-letter country code (e.g. 'AU') whose currency should come first.
|
|
1104
|
+
* @returns {Array<Object>} Options { name, value, countryCode }, e.g. { name: 'AUD ($)', value: 'aud', countryCode: { AU: true } }.
|
|
1105
|
+
* @example
|
|
1106
|
+
* const currencies = sdk.utils.getAvailableCurrencies('AU'); // AUD first
|
|
1107
|
+
*/
|
|
1069
1108
|
service.getAvailableCurrencies = function (defaultCountryID) {
|
|
1070
1109
|
let currencies = SUPPORTED_CURRENCIES.map(({ code, countries }) => ({
|
|
1071
1110
|
name: `${code.toUpperCase()} (${service.currencySymbol(code)})`,
|
|
@@ -1087,15 +1126,17 @@ service.getAvailableCurrencies = function (defaultCountryID) {
|
|
|
1087
1126
|
///////////////////////////////////////////////////////////////////////////////
|
|
1088
1127
|
|
|
1089
1128
|
/**
|
|
1090
|
-
*
|
|
1129
|
+
* Turns an array into an object for quick lookups, keyed by a property of each item (later items win when keys
|
|
1130
|
+
* repeat). Without a key the items themselves are the keys, e.g. for a set of ids.
|
|
1091
1131
|
* @alias utils.hash
|
|
1092
|
-
* @param
|
|
1093
|
-
* @param
|
|
1094
|
-
* @
|
|
1132
|
+
* @param {Array} items The items. Anything that isn't an array is treated as empty.
|
|
1133
|
+
* @param {String} [key] The property (or dot path) to key by.
|
|
1134
|
+
* @returns {Object} The lookup.
|
|
1095
1135
|
* @example
|
|
1096
|
-
*
|
|
1097
|
-
*
|
|
1136
|
+
* sdk.utils.hash([{ id: 'jim', title: 'Jim Jones' }, { id: 'roger', title: 'Roger Fellow' }], 'id');
|
|
1137
|
+
* // { jim: { id: 'jim', title: 'Jim Jones' }, roger: { id: 'roger', title: 'Roger Fellow' } }
|
|
1098
1138
|
*
|
|
1139
|
+
* sdk.utils.hash(['a', 'b']); // { a: 'a', b: 'b' }
|
|
1099
1140
|
*/
|
|
1100
1141
|
service.hash = function (items, key) {
|
|
1101
1142
|
items = !Array.isArray(items) ? [] : items;
|
|
@@ -1109,23 +1150,20 @@ service.hash = function (items, key) {
|
|
|
1109
1150
|
//////////////////////////////////////////////////
|
|
1110
1151
|
|
|
1111
1152
|
/**
|
|
1112
|
-
*
|
|
1153
|
+
* Pulls one property (dot path) out of every item of an array, optionally flattening, de-duplicating,
|
|
1154
|
+
* dropping empty values or adding them up.
|
|
1113
1155
|
* @alias utils.extractFromArray
|
|
1114
|
-
* @param
|
|
1115
|
-
* @param
|
|
1116
|
-
* @param
|
|
1117
|
-
* @param
|
|
1118
|
-
* @param
|
|
1119
|
-
* @param
|
|
1120
|
-
* @param
|
|
1121
|
-
* @
|
|
1156
|
+
* @param {Array} array The items.
|
|
1157
|
+
* @param {String} key The property or dot path to read from each item.
|
|
1158
|
+
* @param {Boolean} [sum] Add the values together and return the total.
|
|
1159
|
+
* @param {Boolean} [flatten] Flatten values that are arrays.
|
|
1160
|
+
* @param {Boolean} [unique] Remove duplicate values.
|
|
1161
|
+
* @param {Boolean} [exclude] Drop null, undefined and other empty values (0 and false are kept).
|
|
1162
|
+
* @param {Object} [options] The same settings as an object: { sum, flatten, unique, excludeNull }.
|
|
1163
|
+
* @returns {Array|Number} The values, or their total when summing.
|
|
1122
1164
|
* @example
|
|
1123
|
-
*
|
|
1124
|
-
* sdk.utils.extractFromArray([{name:'Wendy', age:12}, {name:'Roger', age:
|
|
1125
|
-
*
|
|
1126
|
-
* //Returns 32
|
|
1127
|
-
* sdk.utils.extractFromArray([{name:'Wendy', age:12}, {name:'Roger', age:20}], 'age', {sum:true});
|
|
1128
|
-
*
|
|
1165
|
+
* sdk.utils.extractFromArray([{ name: 'Wendy', age: 12 }, { name: 'Roger', age: 45 }], 'age'); // [12, 45]
|
|
1166
|
+
* sdk.utils.extractFromArray([{ name: 'Wendy', age: 12 }, { name: 'Roger', age: 20 }], 'age', true); // 32
|
|
1129
1167
|
*/
|
|
1130
1168
|
service.extractFromArray = function (
|
|
1131
1169
|
array,
|
|
@@ -1191,18 +1229,17 @@ service.extractFromArray = function (
|
|
|
1191
1229
|
//////////////////////////////////////////////////////
|
|
1192
1230
|
|
|
1193
1231
|
/**
|
|
1194
|
-
*
|
|
1195
|
-
*
|
|
1232
|
+
* Returns the items of an array whose property (dot path) matches a value, for example when evaluating
|
|
1233
|
+
* form field conditions.
|
|
1196
1234
|
* @alias utils.matchInArray
|
|
1197
|
-
* @param
|
|
1198
|
-
* @param
|
|
1199
|
-
* @param
|
|
1200
|
-
* @param
|
|
1201
|
-
* @
|
|
1235
|
+
* @param {Array} array The items.
|
|
1236
|
+
* @param {String} key The property or dot path to compare.
|
|
1237
|
+
* @param {*} v The value to compare with. With the default comparator and no value, items whose property is truthy match.
|
|
1238
|
+
* @param {String} [comparator] '==' (default, loose equality), '>', '<', '>=', '<=', or 'in' (the property, an array or string, includes the value).
|
|
1239
|
+
* @returns {Array} The matching items.
|
|
1202
1240
|
* @example
|
|
1203
|
-
*
|
|
1204
|
-
*
|
|
1205
|
-
*
|
|
1241
|
+
* sdk.utils.matchInArray([{ name: 'Wendy', age: 12 }, { name: 'Michael', age: 45 }], 'age', 45, '>=');
|
|
1242
|
+
* // [{ name: 'Michael', age: 45 }]
|
|
1206
1243
|
*/
|
|
1207
1244
|
service.matchInArray = function (array, key, v, comparator) {
|
|
1208
1245
|
//Filter the array options by a certain v and comparator
|
|
@@ -1246,18 +1283,16 @@ service.matchInArray = function (array, key, v, comparator) {
|
|
|
1246
1283
|
///////////////////////////////////////////////////////////////////////////////
|
|
1247
1284
|
|
|
1248
1285
|
/**
|
|
1249
|
-
*
|
|
1250
|
-
*
|
|
1286
|
+
* Joins values into a comma-separated string, skipping null, undefined and empty strings. For objects, give a
|
|
1287
|
+
* property to use.
|
|
1251
1288
|
* @alias utils.comma
|
|
1252
|
-
* @param
|
|
1253
|
-
* @param
|
|
1254
|
-
* @
|
|
1289
|
+
* @param {Array} array The values.
|
|
1290
|
+
* @param {String} [path] The property (dot path) to use for each value.
|
|
1291
|
+
* @param {Number} [limit] Only use the first this many values.
|
|
1292
|
+
* @returns {String} e.g. 'cat, dog, bird'.
|
|
1255
1293
|
* @example
|
|
1256
|
-
* //
|
|
1257
|
-
* sdk.utils.comma(['cat', 'dog', '
|
|
1258
|
-
*
|
|
1259
|
-
* //Returns 'cat, dog, bird'
|
|
1260
|
-
* sdk.utils.comma([{title:'cat'}, {title:'dog'}, {title:'bird'}], 'title');
|
|
1294
|
+
* sdk.utils.comma(['cat', 'dog', 'bird']); // 'cat, dog, bird'
|
|
1295
|
+
* sdk.utils.comma([{ title: 'cat' }, { title: 'dog' }], 'title'); // 'cat, dog'
|
|
1261
1296
|
*/
|
|
1262
1297
|
service.comma = function (array, path, limit) {
|
|
1263
1298
|
if (limit) {
|
|
@@ -1283,22 +1318,14 @@ service.comma = function (array, path, limit) {
|
|
|
1283
1318
|
//Helper function to get an id of an object
|
|
1284
1319
|
|
|
1285
1320
|
/**
|
|
1286
|
-
*
|
|
1321
|
+
* Gets a record id from either an id string or an object with an _id, and checks it's a valid 24-character id.
|
|
1287
1322
|
* @alias utils.id
|
|
1288
|
-
* @param
|
|
1289
|
-
* @
|
|
1290
|
-
* @return {String} Will return either a string or a Mongo ObjectId
|
|
1291
|
-
*
|
|
1323
|
+
* @param {String|Object} source An id, or an object with an _id.
|
|
1324
|
+
* @returns {String|undefined} The id, or undefined when there isn't a valid one.
|
|
1292
1325
|
* @example
|
|
1293
|
-
*
|
|
1294
|
-
* //
|
|
1295
|
-
* sdk.utils.id('
|
|
1296
|
-
*
|
|
1297
|
-
* //Returns true
|
|
1298
|
-
* typeof service.id({_id:'5cb3d8b3a2219970e6f86927', title, ...}) == 'string';
|
|
1299
|
-
|
|
1300
|
-
* //Returns true
|
|
1301
|
-
* typeof service.id({_id:'5cb3d8b3a2219970e6f86927'}, true) == 'object';
|
|
1326
|
+
* sdk.utils.id('5cb3d8b3a2219970e6f86927'); // '5cb3d8b3a2219970e6f86927'
|
|
1327
|
+
* sdk.utils.id({ _id: '5cb3d8b3a2219970e6f86927', title: 'Item' }); // '5cb3d8b3a2219970e6f86927'
|
|
1328
|
+
* sdk.utils.id('not an id'); // undefined
|
|
1302
1329
|
*/
|
|
1303
1330
|
service.id = function (source) {
|
|
1304
1331
|
if (!source) {
|
|
@@ -1326,15 +1353,13 @@ service.id = function (source) {
|
|
|
1326
1353
|
///////////////////////////////////////////////////////////////////////////////
|
|
1327
1354
|
|
|
1328
1355
|
/**
|
|
1329
|
-
*
|
|
1356
|
+
* Maps an array of ids and/or objects with an _id to a list of unique valid ids; invalid and empty entries are dropped.
|
|
1330
1357
|
* @alias utils.ids
|
|
1331
|
-
* @param
|
|
1332
|
-
* @
|
|
1333
|
-
* @return {Array} An array of Ids
|
|
1334
|
-
*
|
|
1358
|
+
* @param {Array} array Ids and/or objects with an _id.
|
|
1359
|
+
* @returns {Array<String>} The unique ids (an empty array for no input).
|
|
1335
1360
|
* @example
|
|
1336
|
-
*
|
|
1337
|
-
*
|
|
1361
|
+
* sdk.utils.ids([{ _id: '5cb3d8b3a2219970e6f86927' }, null, '5cb3d8b3a2219970e6f86927', '5cb3d8b3a2219970e6f86928']);
|
|
1362
|
+
* // ['5cb3d8b3a2219970e6f86927', '5cb3d8b3a2219970e6f86928']
|
|
1338
1363
|
*/
|
|
1339
1364
|
|
|
1340
1365
|
service.ids = function (array) {
|
|
@@ -1367,6 +1392,15 @@ service.ids = function (array) {
|
|
|
1367
1392
|
|
|
1368
1393
|
///////////////////////////////////////////////////////////////////////////////
|
|
1369
1394
|
|
|
1395
|
+
/**
|
|
1396
|
+
* Checks whether a value is a 24-character hexadecimal record id.
|
|
1397
|
+
* @alias utils.isValidID
|
|
1398
|
+
* @param {*} input The value.
|
|
1399
|
+
* @returns {Boolean} Whether it's a valid id.
|
|
1400
|
+
* @example
|
|
1401
|
+
* sdk.utils.isValidID('5cb3d8b3a2219970e6f86927'); // true
|
|
1402
|
+
* sdk.utils.isValidID('hello'); // false
|
|
1403
|
+
*/
|
|
1370
1404
|
service.isValidID = function (input) {
|
|
1371
1405
|
var checkForHexRegExp = new RegExp("^[0-9a-fA-F]{24}$");
|
|
1372
1406
|
return checkForHexRegExp.test(String(input));
|
|
@@ -1375,10 +1409,17 @@ service.isValidID = function (input) {
|
|
|
1375
1409
|
///////////////////////////////////////////////////////////////////////////////
|
|
1376
1410
|
|
|
1377
1411
|
/**
|
|
1378
|
-
*
|
|
1412
|
+
* Gets a readable message from an error, such as a failed sdk.api request: the server's message when there is
|
|
1413
|
+
* one, otherwise the error's own message, otherwise the error as JSON.
|
|
1379
1414
|
* @alias utils.errorMessage
|
|
1380
|
-
* @param
|
|
1381
|
-
* @
|
|
1415
|
+
* @param {Object|Array} err The error (for an array, its first entry).
|
|
1416
|
+
* @returns {String|undefined} The message, or undefined when no error is given.
|
|
1417
|
+
* @example
|
|
1418
|
+
* try {
|
|
1419
|
+
* await sdk.content.get(id);
|
|
1420
|
+
* } catch (err) {
|
|
1421
|
+
* alert(sdk.utils.errorMessage(err));
|
|
1422
|
+
* }
|
|
1382
1423
|
*/
|
|
1383
1424
|
service.errorMessage = function (err) {
|
|
1384
1425
|
if (!err) {
|
|
@@ -1406,12 +1447,11 @@ service.errorMessage = function (err) {
|
|
|
1406
1447
|
////////////////////////////////////
|
|
1407
1448
|
|
|
1408
1449
|
/**
|
|
1409
|
-
* Generates a
|
|
1450
|
+
* Generates a random unique id (a UUID), e.g. for keys in rendered lists.
|
|
1410
1451
|
* @alias utils.guid
|
|
1411
|
-
* @
|
|
1452
|
+
* @returns {String} e.g. '3b241101-e2bb-4255-8caf-4136c566a962'.
|
|
1412
1453
|
* @example
|
|
1413
|
-
*
|
|
1414
|
-
* sdk.utils.guid()
|
|
1454
|
+
* const key = sdk.utils.guid();
|
|
1415
1455
|
*/
|
|
1416
1456
|
service.guid = function () {
|
|
1417
1457
|
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
@@ -1429,10 +1469,13 @@ service.guid = function () {
|
|
|
1429
1469
|
////////////////////////////////////
|
|
1430
1470
|
|
|
1431
1471
|
/**
|
|
1432
|
-
*
|
|
1472
|
+
* Turns text into a key: non-alphanumeric characters become word breaks and each part between underscores is
|
|
1473
|
+
* camelCased.
|
|
1433
1474
|
* @alias utils.machineName
|
|
1434
|
-
* @param
|
|
1435
|
-
* @
|
|
1475
|
+
* @param {String} string The text, e.g. 'Awesome Event!'.
|
|
1476
|
+
* @returns {String|undefined} The key, e.g. 'awesomeEvent'; undefined for empty input.
|
|
1477
|
+
* @example
|
|
1478
|
+
* sdk.utils.machineName('Awesome Event!'); // 'awesomeEvent'
|
|
1436
1479
|
*/
|
|
1437
1480
|
service.machineName = function (string) {
|
|
1438
1481
|
if (!string || !string.length) {
|
|
@@ -1458,16 +1501,18 @@ export default service;
|
|
|
1458
1501
|
/**
|
|
1459
1502
|
* A lightweight event emitter that can be attached to any service object
|
|
1460
1503
|
* to provide pub/sub event capabilities. Can be called with or without `new`.
|
|
1504
|
+
* dispatch(event, ...details) passes every argument after the event name on to each listener.
|
|
1461
1505
|
*/
|
|
1462
1506
|
export function EventDispatcher() {
|
|
1463
1507
|
const handlers = new Map();
|
|
1464
1508
|
|
|
1465
1509
|
const emitter = {
|
|
1466
|
-
dispatch(event, details) {
|
|
1510
|
+
dispatch(event, ...details) {
|
|
1467
1511
|
const fns = handlers.get(event);
|
|
1468
1512
|
if (fns) {
|
|
1469
1513
|
for (const fn of fns) {
|
|
1470
|
-
|
|
1514
|
+
// Every argument after the event name is passed on to the listener
|
|
1515
|
+
fn(...details);
|
|
1471
1516
|
}
|
|
1472
1517
|
}
|
|
1473
1518
|
},
|