@qikdev/sdk
Version:
Promise based Javascript SDK
1,468 lines (1,116 loc) • 42.9 kB
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>qik.utils.js - Documentation</title>
<script src="scripts/prettify/prettify.js"></script>
<script src="scripts/prettify/lang-css.js"></script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="styles/prettify.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc.css">
<script src="scripts/nav.js" defer></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<input type="checkbox" id="nav-trigger" class="nav-trigger" />
<label for="nav-trigger" class="navicon-button x">
<div class="navicon"></div>
</label>
<label for="nav-trigger" class="overlay"></label>
<nav >
<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#.hashPermissions">hashPermissions</a></li><li data-type='method'><a href="access.html#.isAdministrator">isAdministrator</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#.delete">delete</a></li><li data-type='method'><a href="api.html#.generateEndpointURL">generateEndpointURL</a></li><li data-type='method'><a href="api.html#.get">get</a></li><li data-type='method'><a href="api.html#.patch">patch</a></li><li data-type='method'><a href="api.html#.post">post</a></li><li data-type='method'><a href="api.html#.put">put</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#.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#.activeFilterKeys">activeFilterKeys</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="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></ul>
</nav>
<div id="main">
<h1 class="page-title">qik.utils.js</h1>
<section>
<article>
<pre class="prettyprint source linenums"><code>import _get from 'lodash/get.js';
import _isDate from 'lodash/isDate.js';
import _startsWith from 'lodash/startsWith.js';
import _isObject from 'lodash/isObject.js';
import _camelCase from 'lodash/camelCase.js';
import axios from 'axios';
import { isBrowser, isNode } from 'browser-or-node';
///////////////////////////////////////////////////////////////////////////////
const service = {};
////////////////////////////////////
const loadedExternalScripts = {}
/**
* A function that dynamically include an external javascript resource
* ensuring that it will only be included once
* @alias utils.loadExternalScript
* @param {String} url The url of the external script
* @return {Promise} A promise that will be resolved once the script has been loaded
* @example
*
* await sdk.utils.loadExternalScript('https://cdn.javascript.com/external/script.js');
*/
service.loadExternalScript = function(url) {
const promise = new Promise(createNewScript);
function createNewScript(resolve, reject) {
if (!document) {
return reject('document is undefined')
}
if (loadedExternalScripts[url]) {
return resolve(url);
}
// Avoid duplicate scripts
loadedExternalScripts[url] = true;
//////////////////////////////////////
var script = document.createElement('script');
script.type = 'text/javascript';
script.async = true;
script.onload = function() {
console.log('Loaded external script', url);
return resolve(url);
};
script.src = url;
// Inject it into the document's <head> tag
document.head.appendChild(script);
}
return promise;
}
const loadedExternalStyles = {}
/**
* A function that dynamically include an external css resource
* ensuring that it will only be included once
* @alias utils.loadExternalStyle
* @param {String} url The url of the external css
* @return {Promise} A promise that will be resolved once the css has been loaded
* @example
*
* await sdk.utils.loadExternalStyle('https://cdn.css.com/external/style.css');
*/
service.loadExternalStyle = function(url) {
const promise = new Promise(createNewScript);
function createNewScript(resolve, reject) {
if (!document) {
return reject('document is undefined')
}
if (loadedExternalStyles[url]) {
return resolve(url);
}
// Avoid duplicate scripts
loadedExternalStyles[url] = true;
//////////////////////////////////////
var script = document.createElement('link');
script.setAttribute('rel', 'stylesheet')
script.setAttribute('type', 'text/css')
script.setAttribute('href', url)
script.onload = function() {
console.log('Loaded external stylesheet', url);
return resolve(url);
};
// Inject it into the document's <head> tag
document.head.appendChild(script);
}
return promise;
}
///////////////////////////////////////////////////////////////////////////////
service.exists = function(value) {
var isUndefinedOrNull;
////////////////////////
if (Array.isArray(value)) {
return true;
}
////////////////////////
if (value === 0) {
return true;
}
////////////////////////
switch (typeof value) {
case 'undefined':
case 'null':
isUndefinedOrNull = true;
break;
default:
var string = String(value).toLowerCase();
switch (string) {
case '':
case 'undefined':
case 'null':
isUndefinedOrNull = true;
break;
}
break;
}
return !!!isUndefinedOrNull;
}
//////////////////////////////////
service.parseDate = function(input) {
if (!input) {
return;
}
if (_isDate(input)) {
return input;
}
//Attempt to create a date
var d = new Date(input);
var isValid = d instanceof Date && !isNaN(d);
if (isValid) {
return d;
}
return;
}
service.parseURL = function(string) {
if (!string) {
return false;
}
const relative = _startsWith(string, '/');
if (relative) {
return string;
}
if (_startsWith(string, '://')) {
return string;
}
if (_startsWith(string, 'mailto:')) {
return string;
}
if (_startsWith(string, 'tel:')) {
return string;
}
if (_startsWith(string, 'sms:')) {
return string;
}
//////////////////////////////////
//If someone entered an email by accident
var email = service.parseEmail(string);
//Convert it to a mailto link
if (email && email === string) {
return `mailto:${email}`
}
//////////////////////////////////
const withHttp = string => !/^https?:\/\//i.test(string) ? `http://${string}` : string;
string = withHttp(string);
var valid = RegExp('(https?:\\/\\/)?((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|((\\d{1,3}\\.){3}\\d{1,3}))(\\:\\d+)?(\\/[-a-z\\d%_.~+@]*)*(\\?[;&a-z\\d%_.~+=-@]*)?(\\#[-a-z\\d_@]*)?$', 'i').test(string);
if (!valid) {
return false;
}
///////////////////////////
return withHttp(string)
}
///////////////////////////////////////////////
service.parseNumber = function(input, decimalPoints) {
if (!input) {
return 0;
}
input = Number(input);
if (isNaN(input)) {
return 0;
}
if (decimalPoints) {
var str = input.toFixed(decimalPoints);
input = service.parseNumber(str);
}
return input;
}
service.isValidEmailAddress = function(email) {
var tester = /^[-!#$%&'*+\/0-9=?A-Z^_a-z`{|}~](\.?[-!#$%&'*+\/0-9=?A-Z^_a-z`{|}~])*@[a-zA-Z0-9](-*\.?[a-zA-Z0-9])*\.[a-zA-Z](-?[a-zA-Z0-9])+$/;
if (!email) return false;
var emailParts = email.split('@');
if (emailParts.length !== 2) return false
var account = emailParts[0];
var address = emailParts[1];
if (account.length > 64) return false
else if (address.length > 255) return false
var domainParts = address.split('.');
if (domainParts.some(function(part) {
return part.length > 63;
})) return false;
if (!tester.test(email)) return false;
return true;
}
service.parseEmail = function(input) {
if (!input) {
return false;
}
var lowercase = String(input).toLowerCase();
var valid = service.isValidEmailAddress(lowercase);
if (!valid) {
return false;
} else {
return lowercase;
}
}
//////////////////////////////////
service.parseInt = function(input) {
if (!input) {
return 0;
}
input = parseInt(input);
if (isNaN(input)) {
return 0;
}
return input;
}
service.cleanValue = function(data, type, options) {
if (!options) {
options = {};
}
////////////////////////
var noInputValue = service.exists(data);
if (options.strict && noInputValue && type != 'boolean') {
return data;
}
////////////////////////
switch (type) {
case 'reference':
return service.id(data, true);
break;
case 'boolean':
return service.parseBoolean(data);
break;
case 'url':
return service.parseURL(data);
break;
case 'key':
return service.machineName(data);
break;
case 'email':
return service.parseEmail(data);
break;
case 'date':
var parsed = service.parseDate(data, options);
if (parsed === undefined) {
return;
} else {
return parsed;
}
break;
case 'number':
case 'decimal':
case 'float':
var val = service.parseNumber(data);
if (String(val) == String(Number(data))) {
return val;
}
return;
break;
case 'integer':
var val = service.parseInt(data);
if (String(val) == String(parseInt(data))) {
return val;
}
return;
break;
case 'group':
return data;
break;
case 'object':
if (_isObject(data) && !Array.isArray(data)) {
return data
}
return;
break;
case 'string':
if (service.exists(data)) {
var stringed = String(data);
if (stringed == '[object Object]') {
return;
}
return stringed
} else {
return;
}
// if(!data) {
// return '';
// }
// if(Array.isArray(data)) {
// return JSON.stringify(data);
// }
// if(Object.isObject(data)) {
// return JSON.stringify(data);
// }
// return String(data);
// break;
default:
return String(data)
break;
}
}
service.isValidValue = function(value, dataType, strict) {
var isValue = service.exists(value);
var valueIsNumber = (typeof value == 'number')
////////////////////////
var isValidEntry = false;
if (!isValue) {
return false;
}
////////////////////////
switch (dataType) {
case 'url':
if (strict) {
isValidEntry = _startsWith(value, '/') || _startsWith(value, '://') || _startsWith(value, 'http://') || _startsWith(value, 'https://');
} else {
isValidEntry = String(value) === service.parseURL(value);
}
break;
case 'key':
isValidEntry = String(value) === service.machineName(value);
break;
case 'date':
//If we are being strict
if (strict) {
//And the input is not a javascript date object
if (typeof value != 'object' || !_isDate(value)) {
return false;
}
}
///////////////////////////////
var parsed = service.parseDate(value);
return parsed !== undefined;
break;
case 'email':
if (strict) {
isValidEntry = value === service.parseEmail(value);
} else {
isValidEntry = String(value).toLowerCase() === service.parseEmail(value);
}
break;
case 'number':
case 'decimal':
case 'float':
if (strict) {
isValidEntry = valueIsNumber && (Number(value) === service.parseNumber(value));
} else {
isValidEntry = Number(value) === service.parseNumber(value);
}
break;
case 'integer':
if (strict) {
isValidEntry = valueIsNumber && (Number(value) === service.parseInt(value));
} else {
isValidEntry = Number(value) === service.parseInt(value);
}
break;
case 'boolean':
var parsed = service.parseBoolean(value);
//Only accept true booleans
if (strict) {
if (value === true || value === false) {
isValidEntry = value === parsed;
}
} else {
isValidEntry = parsed === true || parsed === false;
}
break;
case 'reference':
if (strict) {
isValidEntry = String(value) === service.id(value);
} else {
isValidEntry = !!service.id(value);
}
break;
case 'string':
var checkString = String(value);
if (strict) {
if (typeof value != 'string') {
return false;
}
}
if (checkString == '[object Object]') {
return false;
}
isValidEntry = true;
break;
// case 'group':
// isValidEntry = _isObject(value);
// break;
case 'object':
isValidEntry = _isObject(value) && !Array.isArray(value);
break;
case 'array':
isValidEntry = Array.isArray(value);
break;
}
////////////////////////
return isValidEntry
}
service.parseBoolean = function(value) {
switch (String(value).toLowerCase()) {
case 'true':
case 'y':
case 'yes':
case '1':
case 't':
value = true;
break;
case 'false':
case 'n':
case 'no':
case '0':
case 'f':
case 'undefined':
case 'null':
case '':
case '-1':
value = false;
break;
}
return !!value;
}
///////////////////////////////////////////////////////////////////////////////
service.clone = function(input) {
return JSON.parse(JSON.stringify(input));
}
///////////////////////////////////////////////////////////////////////////////
service.mapFields = function(fields, options) {
if (!options) {
options = {};
}
//////////////////////////////
var output = [];
var trail = [];
var titles = [];
var currentDepth = 1;
var depthLimit = options.depth;
var delimiter = options.arrayDelimeter || '[]';
//Loop through each field
fields.forEach(mapField);
//////////////////////////////
//Recursively map the fields
function mapField(field, i) {
var fieldKey = field.key;
var isGroup = field.type == 'group';
var singleValue = (field.minimum === field.maximum) && field.minimum === 1;
var asObject = field.asObject; // || (isGroup && singleValue);
var isLayout = isGroup && !asObject;
//////////////////////////////////
// Whether to clone the field or use the existing reference
var mapped = options.original ? field : service.clone(field);
mapped.trail = trail.slice();
mapped.trail.push(fieldKey)
mapped.path = mapped.trail.join('.');
mapped.titles = titles.slice();
mapped.titles.push(field.title || '')
const isNotLayoutOrIsAllowed = (!isLayout || options.includeLayout);
//If its an actual element or we've asked to include
//layout only elements
if (isNotLayoutOrIsAllowed) {
//Add it to the mix
output.push(mapped);
}
//////////////////////////////////
//Now see if there are child fields and should we go further
var limitHit = depthLimit && (currentDepth >= depthLimit);
//If there are child fields for this field
if (field.fields && field.fields.length) {
//If it's just a group with no extra key
if (isLayout) {
//Loop through fields as if they are at the same depth
field.fields.forEach(mapField)
} else {
//We don't need to traverse any further
//because we hit the limit
if (limitHit) {
return;
}
//Move down a level and add to the object
//Include the key in the path trail
currentDepth++;
let injectKey = fieldKey;
if (options.includeArrayDelimeter) {
if (!singleValue) {
injectKey = `${fieldKey}${delimiter}`
}
}
trail.push(injectKey)
titles.push(field.title);
//Loop through each field
field.fields.forEach(mapField)
//Move back up a level before we go to the next field
currentDepth--;
trail.pop();
titles.pop();
}
}
}
return output;
}
///////////////////////////////////////////////////////////////////////////////
/**
* A helpful function that can take a keyed object literal and map it to url query string parameters
* @alias utils.mapParameters
* @param {Object} parameters The object you want to transalte
* @return {String} The query string
* @example
* //Returns &this=that&hello=world
* sdk.utils.mapParameters({"this":"that", "hello":"world"})
*/
service.mapParameters = function(parameters) {
parameters = parameters || {}
var array = [];
Object.entries(parameters)
.forEach(function([key, value]) {
if (value === undefined || value === null || value == false) {
return;
}
if (Array.isArray(value)) {
value.forEach(function(v) {
array.push(`${key}=${encodeURIComponent(v)}`);
})
} else {
array.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));
}
})
return array.join('&');
}
///////////////////////////////////////////////////////////////////////////////
/**
* A function that will take an integer and a currency string and return a formatted numeric amount rounded to 2 decimal places
* @alias utils.formatCurrency
* @param {Integer} value The amount in cents
* @param {String} currency The currency to format
* @return {String} The formatted value
* @example
*
* //Returns £10.00
* sdk.utils.formatCurrency(1000, 'gbp');
*
* //Returns $10.00
* sdk.utils.formatCurrency(1000, 'usd');
*
*/
service.formatCurrency = function(value, currency) {
if (!value || isNaN(value)) {
value = 0;
}
var currencyPrefix = service.currencySymbol(currency);
return `${currencyPrefix}${parseFloat(parseInt(value) / 100).toFixed(2)}`;
}
/**
* A function that will take an id and return the type key
* @alias utils.getTypeFromID
* @param {String} id The id of an object
* @return {String} The key
* @example
*
* // Returns 'user'
* sdk.utils.getTypeFromID('52b523f775beea960013f6cd');
*
* // Returns 'role'
* sdk.utils.getTypeFromID('62b59cb572fb4772e7b5fa93');
*
*/
service.getTypeFromID = function(id) {
id = service.id(id);
if (!id) {
return;
}
const lookup = {
"21": "organisation",
"23": "comment",
"24": "persona",
"25": "audio",
"26": "campaign",
"28": "smartlist",
"29": "cache",
"40": "application",
"41": "action",
"42": "billinginvoice",
"43": "componentsnapshot",
"44": "diff",
"45": "email",
"46": "submission",
"49": "import",
"53": "socket",
"54": "timetrigger",
"61": "article",
"63": "component",
"65": "event",
"66": "file",
"67": "tag",
"69": "image",
"70": "profile",
"72": "role",
"73": "scope",
"74": "transaction",
"75": "user",
"76": "video",
"77": "workflowcard",
"78": "variable",
"3c": "patoken",
"7b": "uatoken",
"7d": "urtoken",
"2d": "systemtask",
"3d": "export",
"5f": "systemflag",
"2b": "batch",
"3a": "log",
"2e": "stat",
"2f": "unsubscribe",
"6b": "intent",
"7c": "upload",
"2c": "systemtrigger",
"6e": "interface",
"4e": "interfacesnapshot",
"4f": "notification",
"2a": "definition",
"7e": "definitionsnapshot",
"5e": "integration",
"4c": "listener",
"6d": "topic",
"7a": "code",
"4d": "paymentmethod",
"6c": "sslcertificate"
}
var hexKey = id.slice(8, 10);
return lookup[hexKey];
}
/**
* A function that will take a currency string and return the symbol
* @alias utils.currencySymbol
* @param {String} currency The currency
* @return {String} The symbol
* @example
*
* //Returns £
* sdk.utils.currencySymbol('gbp');
*
* //Returns $
* sdk.utils.currencySymbol('usd');
*
*/
service.currencySymbol = function(currency) {
//Ensure lowercase currency
currency = String(currency).toLowerCase();
switch (String(currency).toLowerCase()) {
case 'gbp':
return '£';
break;
case 'eur':
return '€';
break;
default:
return '$';
break;
}
}
service.getAvailableCurrencies = function(DefaultCountryID) {
var array = [];
array.push({
name: `USD (${service.currencySymbol("usd")})`,
value: "usd",
countryCode: { 'US': true },
});
array.push({
name: `GBP (${service.currencySymbol("gbp")})`,
value: "gbp",
countryCode: { 'GB': true, 'UK': true },
});
array.push({
name: `CAD (${service.currencySymbol("cad")})`,
value: "cad",
countryCode: { 'CA': true },
});
array.push({
name: `AUD (${service.currencySymbol("aud")})`,
value: "aud",
countryCode: { 'AU': true },
});
array.push({
name: `NZD (${service.currencySymbol("nzd")})`,
value: "nzd",
countryCode: { 'NZ': true },
});
array.push({
name: `SGD (${service.currencySymbol("sgd")})`,
value: "sgd",
countryCode: { 'SG': true },
});
if (DefaultCountryID) {
var findMatch = array.findIndex(function(currency) {
return currency.countryCode[DefaultCountryID];
})
const moveArrayItem = (array, fromIndex, toIndex) => {
const arr = [...array];
arr.splice(toIndex, 0, ...arr.splice(fromIndex, 1));
return arr;
}
if (findMatch != -1) {
array = moveArrayItem(array, findMatch, 0)
}
}
return array;
}
///////////////////////////////////////////////////////////////////////////////
/**
* Creates a fast keyed hash object from an array of items
* @alias utils.hash
* @param {Array} array The array of items to convert into a hash
* @param {String} key The key or path to the property on each item to use as the hashed key
* @return {Object} A key/value paired object
* @example
* //Returns { jimbo:{id:'jimbo', title:'Jim Jones'}, {id:'roger', title:'Roger Fellow'} }
* sdk.utils.hash([{id:'jimbo', title:'Jim Jones'}, {id:'roger', title:'Roger Fellow'}], 'id');
*
*/
service.hash = function(items, key) {
items = !Array.isArray(items) ? [] : items;
return items.reduce(function(memo, item) {
const k = _get(item, key);
memo[k] = item;
return memo;
}, {});
}
//////////////////////////////////////////////////
/**
* Returns a subset of values in an array that match a provided rule
* @alias utils.extractFromArray
* @param {Array} array The array you want to extract values from
* @param {String} key The path to the child property you want to extract
* @param {Boolean} sum Whether to sum the extracted values together in total
* @param {Boolean} flatten Whether to flatten nested child arrays
* @param {Boolean} unique Whether to only return unique values
* @param {Boolean} exclude Whether to exclude null or undefined values
* @param {Object} options Pass through extra options for how to extract the values
* @return {Array} An array of all values retrieved from the array, unless provided arguments require otherwise
* @example
* //Returns [12, 45] as all the values
* sdk.utils.extractFromArray([{name:'Wendy', age:12}, {name:'Roger', age:45}], 'age');
*
* //Returns 32
* sdk.utils.extractFromArray([{name:'Wendy', age:12}, {name:'Roger', age:20}], 'age', {sum:true});
*
*/
service.extractFromArray = function(array, key, sum, flatten, unique, exclude, options) {
options = options || {}
if (sum) {
options.sum = sum;
}
if (flatten) {
options.flatten = true;
}
if (unique) {
options.unique = true;
}
if (exclude) {
options.excludeNull = true;
}
/////////////////
//Filter the array options by a certain value and operator
var extractedValues = array.reduce(function(set, entry) {
//Get the value from the object
var retrievedValue = _get(entry, key);
var isNull = (!retrievedValue && (retrievedValue !== false && retrievedValue !== 0));
if (options.excludeNull && isNull) {
return set;
}
set.push(retrievedValue);
return set;
}, [])
if (options.flatten) {
extractedValues = _.flatten(extractedValues);
}
if (options.unique) {
extractedValues = _.uniq(extractedValues);
}
if (options.sum) {
extractedValues = extractedValues.reduce(function(a, b) {
return a + b;
}, 0);
}
return extractedValues;
}
//////////////////////////////////////////////////////
/**
* A function that can return a selection of values that were found in an array that match a specific rule,
* This is often used to evaluate expressions within form fields
* @alias utils.matchInArray
* @param {Array} array The array to check
* @param {String} key The javascript dot notation path to extract
* @param {String} value The value to compare against
* @param {String} comparator The logical operator to use to compare the extracted value with the provided value ('>', '<', '>=', '<=', 'in', '==') Defaults to '==' (Is equal to)
* @return {Array} Returns an array of matching values
* @example
* //Returns [{name:'Michael', age:45}] as that is only item in the array that matches the criteria
* sdk.utils.matchInArray([{name:'Wendy', age:12}, {name:'Michael', age:45}], 'age', 45, '>=');
*
*/
service.matchInArray = function(array, key, v, comparator) {
//Filter the array options by a certain v and comparator
var matches = array.filter(function(entry) {
//Get the v from the object
var extracted = _get(entry, key);
var found;
switch (comparator) {
case 'in':
found = extracted.includes(v);
break;
case '>=':
found = (extracted >= v);
break;
case '<=':
found = (extracted <= v);
break;
case '<':
found = (extracted < v);
break;
case '>':
found = (extracted > v);
break;
case '==':
default:
if (v === undefined) {
found = extracted;
} else {
found = (extracted == v);
}
break;
}
return found;
})
return matches;
}
///////////////////////////////////////////////////////////////////////////////
/**
* A helpful class that can take an array of values and return them as a comma seperated
* string, If the values are objects, then a property to use as the string representation can be specified
* @alias utils.comma
* @param {Array} array The array of values to translate
* @param {String} path An optional property key to use for each value
* @return {String} The resulting comma seperated string
* @example
* //Returns 'cat, dog, bird'
* sdk.utils.comma(['cat', 'dog', 'bird']);
*
* //Returns 'cat, dog, bird'
* sdk.utils.comma([{title:'cat'}, {title:'dog'}, {title:'bird'}], 'title');
*/
service.comma = function(array, path, limit) {
if (limit) {
array = array.slice(0, limit);
}
return _.chain(array)
.compact()
.map(function(item) {
if (path && path.length) {
return _get(item, path);
}
return item;
})
.value()
.join(', ');
}
///////////////////////////////////////////////////////////////////////////////
//Helper function to get an id of an object
/**
* Returns a specified _id for an object
* @alias utils.id
* @param {Object} input An object that is or has an _id property
* @param {Boolean} asObjectID Whether to convert to a Mongo ObjectId
* @return {String} Will return either a string or a Mongo ObjectId
*
* @example
*
* //Returns '5cb3d8b3a2219970e6f86927'
* sdk.utils.id('5cb3d8b3a2219970e6f86927')
*
* //Returns true
* typeof service.id({_id:'5cb3d8b3a2219970e6f86927', title, ...}) == 'string';
* //Returns true
* typeof service.id({_id:'5cb3d8b3a2219970e6f86927'}, true) == 'object';
*/
service.id = function(source) {
if (!source) {
return;
}
/////////////////////////////////
var output;
if (source._id) {
output = String(source._id);
} else {
output = String(source);
}
var isValid = service.isValidID(output);
if (!isValid) {
return;
}
return output;
}
///////////////////////////////////////////////////////////////////////////////
/**
* Cleans and maps an array of objects to an array of IDs
* @alias utils.ids
* @param {Array} array An array of objects or object ids
* @param {Boolean} asObjectID Whether or not to map the ids as Mongo ObjectIds
* @return {Array} An array of Ids
*
* @example
* //Returns ['5cb3d8b3a2219970e6f86927', '5cb3d8b3a2219970e6f86927', '5cb3d8b3a2219970e6f86927']
* sdk.utils.ids([{_id:'5cb3d8b3a2219970e6f86927'}, {_id:'5cb3d8b3a2219970e6f86927'}, null, '5cb3d8b3a2219970e6f86927'])
*/
service.ids = function(array) {
if (!array) {
return [];
}
/////////////////////////////////
var ids = Object.keys(array.reduce(function(set, entry) {
if (!entry) {
return set;
}
var cleaned = service.id(entry)
if (cleaned) {
set[cleaned] = 1;
}
return set;
}, {}))
///////////////////////////////
return ids;
}
///////////////////////////////////////////////////////////////////////////////
service.isValidID = function(input) {
var checkForHexRegExp = new RegExp("^[0-9a-fA-F]{24}$")
return checkForHexRegExp.test(String(input));
}
///////////////////////////////////////////////////////////////////////////////
/**
* Helper function for retrieving a human readable error message from server error response objects
* @alias utils.errorMessage
* @param {Object} error The error object to translate
* @return {String} The resulting human readable error message
*/
service.errorMessage = function(err) {
if (!err) {
return;
}
if (Array.isArray(err)) {
err = err[0];
}
////////////////////////////////////
var candidates = [
'response.data.message',
'response.data',
'message',
]
var message;
candidates.forEach(function(path) {
if (message) {
return;
}
var m = _get(err, path);
if (m) {
message = m;
}
})
if (Array.isArray(message)) {
message = message[0];
}
////////////////////////////////////
message = message || JSON.stringify(err);
////////////////////////////////////
return message;
}
////////////////////////////////////
/**
* Generates a globally unique ID, helpful for adding unique keys for iterable loops
* @alias utils.guid
* @return {String} The new globally unique identifier
* @example
* //Returns 4323a78br-z16h-289j-zwl1-938lda334asd
* sdk.utils.guid()
*/
service.guid = function() {
var u = (new Date()).getTime().toString(16) + Math.random().toString(16).substring(2) + "0".repeat(16);
return u.substr(0, 8) + '-' + u.substr(8, 4) + '-4000-8' + u.substr(12, 3) + '-' + u.substr(15, 12);
}
////////////////////////////////////
////////////////////////////////////
////////////////////////////////////
/**
* Helper function for cleaning strings to use as database ids
* @alias utils.machineName
* @param {String} string The string to clean eg. (Awesome Event!)
* @return {String} A cleaned and formatted string eg. (awesomeEvent)
*/
service.machineName = function(string) {
if (!string || !string.length) {
return;
}
var regexp = /[^a-zA-Z0-9-_]+/g;
var cleaned = String(string).replace(regexp, ' ');
return _camelCase(cleaned);
}
/////////////////////////////////////////////
/////////////////////////////////////////////
/////////////////////////////////////////////
export default service;
/////////////////////////////////////////////
//Export the event dispatcher
export function EventDispatcher() {
var listeners = {};
/////////////////////////////////////////////
var dispatcher = {}
/////////////////////////////////////////////
//Remove all listeners
dispatcher.removeAllListeners = function() {
listeners = {};
}
/////////////////////////////////////////////
dispatcher.dispatch = function(event, details) {
if (listeners[event]) {
//For each listener
listeners[event].forEach(function(callback) {
return callback(details);
});
}
}
/////////////////////////////////////////////
dispatcher.addEventListener = function(event, callback) {
if (!listeners[event]) {
listeners[event] = [];
}
const type = typeof callback;
if (type !== 'function') {
throw new Error(`addEventListener() requires a function but ${type} was added`);
} else {
if (listeners[event].indexOf(callback) == -1) {
//Add to the listeners
listeners[event].push(callback)
} else {
//A
//lready listening
}
}
}
/////////////////////////////////////////////
dispatcher.removeEventListener = function(event, callback) {
if (!listeners[event]) {
listeners[event] = [];
}
//Get the index of the listener
var index = listeners[event].indexOf(callback);
if (index != -1) {
//Remove from the listeners
listeners[event].splice(index, 1);
}
}
/////////////////////////////////////////////
//Wrap the event listener functionality
dispatcher.bootstrap = function(service) {
if (!service) {
// console.log('No service to bootstrap to')
return;
}
service.dispatch = dispatcher.dispatch;
service.addEventListener = dispatcher.addEventListener;
service.removeEventListener = dispatcher.removeEventListener;
service.removeAllListeners = dispatcher.removeAllListeners;
}
/////////////////////////////////////////////
return dispatcher;
}</code></pre>
</article>
</section>
</div>
<br class="clear">
<footer>
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.6.11</a> on Thu Sep 28 2023 07:24:39 GMT+1000 (Australian Eastern Standard Time) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
</footer>
<script>prettyPrint();</script>
<script src="scripts/polyfill.js"></script>
<script src="scripts/linenumber.js"></script>
</body>
</html>