cv-dialog-sdk
Version:
Catavolt Dialog Javascript API
372 lines (371 loc) • 13.6 kB
JavaScript
import moment from 'moment';
import numeral from 'numeral';
import { CodeRef } from './CodeRef';
import { GpsReadingProperty } from './GpsReadingProperty';
import { MapLocationProperty } from './MapLocationProperty';
import { ObjectRef } from './ObjectRef';
// Chose the locales to load based on this list:
// https://stackoverflow.com/questions/9711066/most-common-locales-for-worldwide-compatibility
// Best effort for now. Need to dynamically load these from Globalize???
import 'moment/locale/de';
import 'moment/locale/en-ca';
import 'moment/locale/en-gb';
import 'moment/locale/es';
import 'moment/locale/fr';
import 'moment/locale/it';
import 'moment/locale/ja';
import 'moment/locale/pt';
import 'moment/locale/pt-br';
import 'moment/locale/ru';
import 'moment/locale/zh-cn';
// we must use these to prevent giving moment non-existent locales that cause it hurl in RN
// @see https://github.com/moment/moment/issues/3624#issuecomment-288713419
const supportedLocales = ['en', 'de', 'en-ca', 'en-gb', 'es', 'fr', 'it', 'ja', 'pt', 'pt-br', 'ru', 'zh-cn'];
import { DateTimeValue } from '../util/DateTimeValue';
import { DateValue } from '../util/DateValue';
import { TimeValue } from '../util/TimeValue';
/**
* Helper for transforming values to and from formats suitable for reading and writing to the server
* (i.e. object to string and string to object)
*/
class PrivatePropFormats {
}
PrivatePropFormats.decimalFormat = [
'0,0',
'0,0.0',
'0,0.00',
'0,0.000',
'0,0.0000',
'0,0.00000',
'0,0.000000',
'0,0.0000000',
'0,0.00000000',
'0,0.000000000',
'0,0.0000000000'
];
PrivatePropFormats.decimalFormatGeneric = '0,0.[0000000000000000000000000]';
PrivatePropFormats.moneyFormat = [
'$0,0',
'$0,0.0',
'$0,0.00',
'$0,0.000',
'$0,0.0000',
'$0,0.00000',
'$0,0.000000',
'$0,0.0000000',
'$0,0.00000000',
'$0,0.000000000',
'$0,0.0000000000'
];
PrivatePropFormats.moneyFormatGeneric = '$0,0.00[0000000000000000000000000]';
PrivatePropFormats.percentFormat = [
'0,0%',
'0,0%',
'0,0%',
'0,0.0%',
'0,0.00%',
'0,0.000%',
'0,0.0000%',
'0,0.00000%',
'0,0.000000%',
'0,0.0000000%',
'0,0.00000000%'
];
PrivatePropFormats.percentFormatGeneric = '0,0.[0000000000000000000000000]%';
PrivatePropFormats.wholeFormat = '0,0';
export class PropertyFormatter {
constructor(_catavoltApi) {
this._catavoltApi = _catavoltApi;
if (PropertyFormatter._singleton) {
throw new Error('Singleton instance already created');
}
this.resetFormats();
PropertyFormatter._singleton = this;
}
static singleton(catavoltApi) {
if (!PropertyFormatter._singleton) {
PropertyFormatter._singleton = new PropertyFormatter(catavoltApi);
}
return PropertyFormatter._singleton;
}
/**
* Get a string representation of this property suitable for 'reading'
* @param prop
* @param propDef
* @returns {string}
*/
formatForRead(prop, propDef) {
if (prop === null || prop === undefined) {
return '';
}
else {
return this.formatValueForRead(prop.value, propDef);
}
}
formatValueForRead(value, propDef) {
const locale = this._catavoltApi.locale;
const locales = [];
locales.push(this.getSupportedLocale(locale.langCountryString));
if (value === null || value === undefined) {
return '';
}
else if ((propDef && propDef.isCodeRefType) || value instanceof CodeRef) {
return value.description;
}
else if ((propDef && propDef.isObjRefType) || value instanceof ObjectRef) {
return value.description;
}
else if ((propDef && propDef.isDateTimeType) || value instanceof DateTimeValue) {
const dateValue = (value instanceof DateTimeValue) ? value.dateObj : value;
return moment(dateValue)
.locale(locales)
.format('lll');
}
else if ((propDef && propDef.isDateType) || value instanceof Date || value instanceof DateValue) {
const dateValue = (value instanceof DateValue) ? value.dateObj : value;
return moment(dateValue)
.locale(locales)
.format('ll');
}
else if ((propDef && propDef.isTimeType) || value instanceof TimeValue) {
return moment(value)
.locale(locales)
.format('LT');
}
else if (propDef && propDef.isPasswordType) {
return value.replace(/./g, '*');
}
else if ((propDef && propDef.isListType) || Array.isArray(value)) {
return value.reduce((prev, current) => {
return (prev ? prev + ', ' : '') + this.formatValueForRead(current, null);
}, '');
}
else {
return this.toString(value, propDef);
}
}
/**
* Get a string representation of this property suitable for 'writing'
* @param prop
* @param propDef
* @returns {string}
*/
formatForWrite(prop, propDef) {
if (prop === null || prop === undefined || prop.value === null || prop.value === undefined) {
return null;
}
else if ((propDef && propDef.isCodeRefType) || prop.value instanceof CodeRef) {
return prop.value.description;
}
else if ((propDef && propDef.isObjRefType) || prop.value instanceof ObjectRef) {
return prop.value.description;
}
else {
return this.toStringWrite(prop.value, propDef);
}
}
/**
* Attempt to construct (or preserve) the appropriate data type given primitive (or already constructed) value.
* Note this must be done here and not at 'write' time because it requires the knowledge of the PropDef
* @param value
* @param propDef
* @returns {}
*/
parse(value, propDef) {
let propValue = value;
if (propDef.isDecimalType) {
const newVal = typeof value === 'string' ? value.replace(',', '') : value;
propValue = Number(newVal);
}
else if (propDef.isLongType) {
const newVal = typeof value === 'string' ? value.replace(',', '') : value;
propValue = Number(newVal);
}
else if (propDef.isBooleanType) {
if (typeof value === 'string') {
propValue = value !== 'false' && value !== 'no' && value !== '0';
}
else {
propValue = !!value;
}
}
else if (propDef.isDateType) {
// this could be a DateValue, a Date, or a string
if (value instanceof DateValue) {
propValue = value;
}
else if (typeof value === 'object') {
propValue = new DateValue(value);
}
else {
// parse as local time
propValue = new DateValue(moment(value).toDate());
}
}
else if (propDef.isDateTimeType) {
// this could be a DateTimeValue, a Date, or a string
if (value instanceof DateTimeValue) {
propValue = value;
}
else if (typeof value === 'object') {
propValue = new DateTimeValue(value);
}
else {
// parse as local time
propValue = new DateTimeValue(moment(value).toDate());
}
}
else if (propDef.isTimeType) {
if (value instanceof TimeValue) {
propValue = value;
}
else if (typeof value === 'object') {
propValue = TimeValue.fromDateValue(value);
}
else {
propValue = TimeValue.fromString(value);
}
}
else if (propDef.isCodeRefType) {
if (typeof value === 'string') {
propValue = CodeRef.fromString(value);
}
}
else if (propDef.isObjRefType) {
if (typeof value === 'string') {
propValue = ObjectRef.fromString(value);
}
}
return propValue;
}
resetFormats() {
this.decimalFormat = PrivatePropFormats.decimalFormat.slice(0);
this.decimalFormatGeneric = PrivatePropFormats.decimalFormatGeneric;
this.moneyFormat = PrivatePropFormats.moneyFormat.slice(0);
this.moneyFormatGeneric = PrivatePropFormats.moneyFormatGeneric;
this.percentFormat = PrivatePropFormats.percentFormat.slice(0);
this.percentFormatGeneric = PrivatePropFormats.percentFormatGeneric;
this.wholeFormat = PrivatePropFormats.wholeFormat;
}
toString(o, propDef) {
return this.toStringRead(o, propDef);
}
/**
* Render this value as a string
*
* @param o
* @param {PropertyDef} propDef
* @returns {string}
*/
toStringRead(o, propDef) {
if (typeof o === 'number') {
if (propDef && !propDef.isUnformattedNumericType) {
if (propDef.isMoneyType) {
let f = propDef.scale < this.moneyFormat.length
? this.moneyFormat[propDef.scale]
: this.moneyFormatGeneric;
// If there is a currency symbol, remove it noting it's position pre/post
// Necessary because numeral will replace $ with the symbol based on the locale of the browser.
// This may be desired down the road, but for now, the server provides the symbol to use.
const atStart = f.length > 0 && f[0] === '$';
const atEnd = f.length > 0 && f[f.length - 1] === '$';
if (this._catavoltApi.currencySymbol) {
f = f.replace('$', ''); // Format this as a number, and slam in Extender currency symbol
let formatted = numeral(o).format(f);
if (atStart) {
formatted = this._catavoltApi.currencySymbol + formatted;
}
if (atEnd) {
formatted = formatted + this._catavoltApi.currencySymbol;
}
return formatted;
}
else {
return numeral(o).format(f); // Should substitute browsers locale currency symbol
}
}
else if (propDef.isPercentType) {
const f = propDef.scale < this.percentFormat.length
? this.percentFormat[propDef.scale]
: this.percentFormatGeneric;
return numeral(o).format(f); // numeral accomplishs * 100, relevant if we use some other symbol
}
else if (propDef.isIntType || propDef.isLongType) {
return numeral(o).format(this.wholeFormat);
}
else if (propDef.isDecimalType || propDef.isDoubleType) {
const f = propDef.scale < this.decimalFormat.length
? this.decimalFormat[propDef.scale]
: this.decimalFormatGeneric;
return numeral(o).format(f);
}
}
else {
return String(o);
}
}
else if (typeof o === 'object') {
if (o instanceof Date) {
return o.toISOString();
}
else if (o instanceof DateValue) {
return o.dateObj.toISOString();
}
else if (o instanceof DateTimeValue) {
return o.dateObj.toISOString();
}
else if (o instanceof TimeValue) {
return o.toString();
}
else if (o instanceof CodeRef) {
return o.toString();
}
else if (o instanceof ObjectRef) {
return o.toString();
}
else if (o instanceof GpsReadingProperty) {
return o.toString();
}
else if (o instanceof MapLocationProperty) {
return o.toString();
}
else {
return String(o);
}
}
else {
return String(o);
}
}
toStringWrite(o, propDef) {
if (typeof o === 'number' && propDef) {
if (propDef.isMoneyType) {
return o.toFixed(2);
}
else if (propDef.isIntType || propDef.isLongType) {
return o.toFixed(0);
}
else if (propDef.isDecimalType || propDef.isDoubleType) {
return o.toFixed(Math.max(2, (o.toString().split('.')[1] || []).length));
}
}
else {
return this.toStringRead(o, propDef);
}
}
getSupportedLocale(locale) {
if (supportedLocales.indexOf(locale)) {
return locale;
}
else {
const sepIndex = locale.indexOf('-');
if (sepIndex > -1) {
const lang = locale.substring(0, sepIndex);
if (supportedLocales.indexOf(lang)) {
return lang;
}
}
return this._catavoltApi.DEFAULT_LOCALE.language;
}
}
}