apphouse
Version:
Component library for React that uses observable state management and theme-able components.
302 lines (284 loc) • 8.55 kB
text/typescript
import orderedJson from 'json-order';
import beautify_js from 'js-beautify';
interface JSResponse<T> {
/** results wanted, it will always be of the desired type */
response: T | undefined;
/** if true, response if valid, if false: response was forced
* casted clean and it will not be correct, expect error */
valid: boolean;
/**
* it will contain the error message if valid is false
*/
error: string | undefined;
/**
* original value being operated on
*/
value: any;
}
export default class JS {
/**
* @param srcObj an object with the properties in any order
* @param map [optional]: the property map generated by parse above.
* @param separator [optional]: a non-empty string that controls what the key separator is in the generated map. Defaults to ~.
* If the map is unset, the response is a standard JSON.stringify.
* @param space [optional]: a number used to insert white space into the output JSON string
* for readability purposes, as per the JSON.stringify
* @returns
*/
static jsonStringify = (srcObj: object, map?: any, separator?: string) => {
return orderedJson.stringify(srcObj, map, separator);
};
static jsonParse = (src: string) => {
return orderedJson.parse(src);
};
static stringify(
value: object,
pretty?: boolean,
jsonNotation?: boolean
): JSResponse<string> {
if (typeof value === 'object') {
try {
const objectString = JS.objectToString(value, jsonNotation);
// remove last dangling comma
const str = objectString.replace(/,\s*$/, '');
const prettyStr = JS.prettify(str);
return {
response: pretty ? prettyStr : str.replaceAll('\\', ' '),
valid: true,
error: undefined,
value
};
} catch (e) {
return {
response: undefined,
valid: false,
error: JSON.stringify(e),
value
};
}
}
return {
response: undefined,
valid: false,
error: 'value is not an object',
value
};
}
static jsSingleQuotesToDouble(value: string): string {
return value.replace(/'/g, '"');
}
static jsStringObjectToJSONObject(value: string) {
const jsonStr = value.replace(/(\w+:)|(\w+ :)/g, function (matchedStr) {
return '"' + matchedStr.substring(0, matchedStr.length - 1) + '":';
});
if (!jsonStr) {
return '';
}
if (jsonStr.startsWith('{')) {
return jsonStr;
} else {
return `{${jsonStr}}`;
}
}
/**
* Check if a string has valid JSON object syntax
* @param text string to check if valid json
* @returns false if not valid json, true if valid json
*/
static testJSON = (text: string): boolean => {
if (typeof text !== 'string') {
return false;
}
try {
JSON.parse(text);
return true;
} catch (error) {
return false;
}
};
static jsStringToObject(value: string, retry?: boolean): JSResponse<object> {
if (typeof value === 'string') {
const jsonStr = value.replace(/(\w+:)|(\w+ :)/g, function (matchedStr) {
return '"' + matchedStr.substring(0, matchedStr.length - 1) + '":';
});
try {
//converts to a regular object
const response = {
response: JSON.parse(jsonStr),
valid: true,
error: undefined,
value
};
return response;
} catch (error: any) {
if (retry) {
// has already tried to fix the string
return {
response: undefined,
valid: false,
error: String(error),
value
};
} else {
// retrying
return JS.jsStringToObject(JS.escapeJSON(value), true);
}
}
}
return {
response: undefined,
valid: false,
error: 'value is not a string',
value
};
}
static prettify(value: string): string {
if (!value) {
return '';
}
if (value.startsWith('{')) {
return beautify_js(value, {
indent_size: 2,
js: {
preserve_newlines: true,
escape_quotes: true
}
});
}
return beautify_js(`{${value}}`, {
indent_size: 2,
quote_keys: true,
js: {
preserve_newlines: true,
escape_quotes: true,
quote_keys: true
}
});
}
static objectToString(obj: any, jsonNotation?: boolean) {
var str = '';
var i = 0;
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
if (typeof obj[key] == 'object') {
if (obj[key] instanceof Array) {
if (jsonNotation) {
str += `"${key}":[`;
} else {
//let's check if the key has special characters
//so we can keep the quotes
if (key.match(/[^a-zA-Z0-9]/)) {
str += `"${key}": [`;
} else {
str += `${key}:[`;
}
}
for (var j = 0; j < obj[key].length; j++) {
const item = obj[key][j];
if (typeof item == 'object') {
const strObj =
JS.objectToString(item, jsonNotation) +
(j <= item.length - 1 ? ',' : '');
const newStr = strObj.replace(/,\s*$/, '');
// substitute string with cleaned up string
str += '{' + newStr + '},';
} else if (typeof item == 'string') {
// let's double quote it
str += `"${item}",`; //non objects would be represented as strings
} else {
str += `${item},`;
}
}
// remove last comma
const newStr = str.replace(/,\s*$/, '');
// substitute string with cleaned up string
str = newStr;
str += '],';
} else {
const objStr = JS.objectToString(obj[key], jsonNotation);
// remove last comma
const newStr = objStr.replace(/,\s*$/, '');
// substitute string with cleaned up string
str += `"${key}"` + ':{' + newStr + '},';
}
} else {
const item = obj[key];
if (typeof item == 'string') {
// let's double quote it
if (jsonNotation) {
str += `"${key}":"${obj[key]}",`;
} else {
//let's check if the key has special characters
//so we can keep the quotes
if (key.match(/[^a-zA-Z0-9]/)) {
str += `"${key}":"${obj[key]}",`;
} else {
str += `${key}:"${obj[key]}",`;
}
}
} else {
if (jsonNotation) {
str += `"${key}":${obj[key]},`;
} else {
//let's check if the key has special characters
//so we can keep the quotes
if (key.match(/[^a-zA-Z0-9]/)) {
str += `"${key}":${obj[key]},`;
} else {
str += `${key}:${obj[key]},`;
}
}
}
}
i++;
}
}
return str;
}
static escapeJSON(string: string) {
return string.replace(/[\n"\&\r\t\b\f\\\/]/g, '\\$&');
}
static stringToObject(str: string): { [id: string]: any } {
const obj: { [id: string]: any } = {};
const markers: number[] = [];
const chars = str.split('');
chars.forEach((char, index) => {
if (char === ':') {
markers.push(index);
}
});
markers.forEach((marker) => {
// detect if : is indeed a marker
if (chars[marker + 1] !== '\\') {
// it is a marker
const key = str.substring(0, marker);
const value = str.substring(marker + 1);
obj[key] = value;
}
});
return obj;
}
/**
* Order the CSSProperties by applying a map to it
* @param obj CSS Properties object
* @param map map with the css properties in sequence to render
* @returns obj, if no Map is provided, it will return the same object
*/
static applyMapToObj(obj: any, map?: any) {
if (!map) {
return obj;
}
const newObj: any = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
const element = obj[key];
if (map[key]) {
newObj[map[key]] = element;
} else {
newObj[key] = element;
}
}
}
return newObj;
}
}