UNPKG

damon-utils

Version:
1,017 lines (1,003 loc) 163 kB
const DOMPurify = require('dompurify'); const Papa = require('papaparse'); const jsonLogic = require('json-logic-js'); //#### Utils module.exports = class DamonUtils { /** * Creates an instance of DamonUtils. * @param {object} damon */ constructor(damon) { let $ = this; $.damon = damon; $.domPurify = DOMPurify; $.websiteRegex = /^(https?:\/\/)?[-a-zA-Z0-9]*[a-zA-Z0-9]+(\.[-a-zA-Z0-9]*[a-zA-Z0-9]+)+/; $.httpRegex = /^https?:\/\//; $.pathRegex = /^\.*\//; } /** * @param {string} damonString * @returns {string} */ damonToHtmlTable(damonString) { const $ = this; return $.mapToHtmlTable($.damon.damonToMap(damonString, 0, true)); } /** * @param {string} damonString * @returns {string} */ damonToSExpression(damonString) { const $ = this; return $.prefixedKeysMapToSExpression($.damon.damonToMap(damonString, 0, true)); } /** * @param {string} jsonString * @returns {string} */ sExpressionToDamon(jsonString) { const $ = this; return $.damon.mapToDamon($.sExpressionToPrefixedKeysMap(jsonString), false, true); } /** * @param {string} string * @returns {string} */ escape(string) { return string .replace(/&/g, "&amp;") .replace(/</g, "&lt;") .replace(/>/g, "&gt;") .replace(/"/g, "&quot;") .replace(/'/g, "&#039;"); } /** * @typedef {Map<string, damonValue>} damonMap * @typedef {Array<damonValue>} damonArray * @typedef {damonMap|damonArray|string|number|boolean|null} damonValue * @param {damonValue} jsonMap * @param {boolean} safeHTML * @param {string} jsonContext * @returns {object} DOM */ mapToHtmlList(jsonMap, safeHTML = false, jsonContext = undefined) { let $ = this; // Parsing check try { $.damon.mapToJSON(jsonMap); } catch (error) { throw new Error("Provided map value doesn't passes JSON.parse()"); } var jsonItemIndex = 0, list = document.createElement('ul'), schema; if (jsonContext !== undefined) { schema = JSON.parse(jsonContext); } list.className = 'DAMON-List'; recurse(jsonMap, list); return list; /** * @param {damonValue} jsonMap * @param {object} listItem * @returns {object} DOM */ function recurse(jsonMap, listItem, path = []) {// imitate renderDiff if ( typeof listItem !== 'object' || listItem == null || Array.isArray(listItem) ) { throw new Error("Error List Item number " + jsonItemIndex + ": @param { {} } list"); } if ( typeof jsonMap === 'object' && jsonMap !== null && !Array.isArray(jsonMap) && jsonMap instanceof Map && jsonMap.constructor === Map ) { if (listItem.tagName == "UL") { for (const [key, value] of jsonMap) { let newList = document.createElement('ul'), newDiv = document.createElement('code'), keySpan = document.createElement('span'), newListItem = document.createElement('li'); newListItem.dataset.graphArbo = Array.from(jsonMap.keys()).indexOf(key); if (path.length > 0) newListItem.dataset.graphArbo = path.join('-') + '-' + Array.from(jsonMap.keys()).indexOf(key); keySpan.className = "type-key"; if ( $.websiteRegex.test(key) || $.pathRegex.test(key) ) { let fullUrl = key; if ( $.websiteRegex.test(key) && !$.httpRegex.test(key) ) fullUrl = ('https://' + key); let keyLink = DOMPurify.sanitize(`<a href="${ fullUrl }"><span>${ fullUrl }</span></a>`); keySpan.innerHTML = keyLink; } else { if ( jsonContext !== undefined && key in schema['@context'] ) { keySpan.innerHTML = DOMPurify.sanitize( `<a href="${ schema['@context'][key]['@id'] }"><span>${ key }</span></a>` ); } else { keySpan.textContent = key; } } if ( typeof value === 'object' && value !== null ) { if (Array.isArray(value)) { if ( jsonMap.damonInlineArrays !== undefined && jsonMap.damonInlineArrays.indexOf(key) > -1 ) { newDiv.innerHTML = keySpan.outerHTML + '<span class="operator">: </span>['; for (let j = 0, k = value.length; j < k; j++) { let childValueSpan = document.createElement('span'), childValue = value[j]; if (childValue === true) { childValueSpan.textContent = "true"; childValueSpan.className = "type-boolean"; } else if (childValue === false) { childValueSpan.textContent = "false"; childValueSpan.className = "type-boolean"; } else if (childValue === null) { childValueSpan.textContent = "null"; childValueSpan.className = "type-null"; } else if ( Number.isFinite(childValue) && !Number.isNaN(childValue) ) { childValueSpan.textContent = childValue + ""; childValueSpan.className = "type-number"; } else { if (safeHTML) { if ( $.websiteRegex.test(childValue) || $.pathRegex.test(childValue) ) { let fullUrl = childValue; if ( $.websiteRegex.test(childValue) && !$.httpRegex.test(childValue) ) fullUrl = 'https://' + childValue; childValueSpan.innerHTML = DOMPurify.sanitize(`<a href="${ fullUrl }">"${ fullUrl }"</a>`); } else { childValueSpan.innerHTML = `"${childValue}"`; } } else { if ( $.websiteRegex.test(childValue) || $.pathRegex.test(childValue) ) { let fullUrl = childValue; if ( !$.httpRegex.test(childValue) && $.websiteRegex.test(childValue) ) fullUrl = 'https://' + childValue; childValueSpan.innerHTML = DOMPurify.sanitize(`<a href="${ fullUrl }">"${ fullUrl }"</a>`); } else { childValueSpan.textContent = `"${childValue}"`; } } childValueSpan.className = "type-string"; } if (j !== 0) { newDiv.innerHTML += ', '; } newDiv.appendChild(childValueSpan); } newDiv.innerHTML += ']'; newListItem.appendChild(newDiv); newListItem.appendChild(newList); listItem.appendChild(newListItem); } else { newDiv.innerHTML = keySpan.outerHTML + '<span class="operator">: </span>[]'; newListItem.appendChild(newDiv); newListItem.appendChild(newList); listItem.appendChild(newListItem); recurse(value, newList, path.concat([Array.from(jsonMap.keys()).indexOf(key)])); } } else { if ( jsonMap.implicitMaps !== undefined && jsonMap.implicitMaps.indexOf(key) > -1 ) { newDiv.innerHTML = keySpan.outerHTML; } else { newDiv.innerHTML = keySpan.outerHTML + '<span class="operator">: </span>{}'; } newListItem.appendChild(newDiv); newListItem.appendChild(newList); listItem.appendChild(newListItem); recurse(value, newList, path.concat([Array.from(jsonMap.keys()).indexOf(key)])); } } else { jsonItemIndex++; newDiv.innerHTML = keySpan.outerHTML + '<span class="operator">: </span>'; let valueSpan = document.createElement('span'); let childText = value; if (childText === true) { valueSpan.textContent = "true"; valueSpan.className = "type-boolean"; } else if (childText === false) { valueSpan.textContent = "false"; valueSpan.className = "type-boolean"; } else if (childText === null) { valueSpan.textContent = "null"; valueSpan.className = "type-null"; } else if ( Number.isFinite(childText) && !Number.isNaN(childText) ) { valueSpan.textContent = childText + ""; valueSpan.className = "type-number"; } else { if (safeHTML) { if ( $.websiteRegex.test(childText) || $.pathRegex.test(childText) ) { let fullUrl = childText; if ( !$.httpRegex.test(childText) && $.websiteRegex.test(childText) ) fullUrl = 'https://' + childText; valueSpan.innerHTML = DOMPurify.sanitize(`<a href="${ fullUrl }">"${ fullUrl }"</a>`); } else { valueSpan.innerHTML = `"${childText}"`; } } else { if ( $.websiteRegex.test(childText) || $.pathRegex.test(childText) ) { let fullUrl = childText; if ( !$.httpRegex.test(childText) && $.websiteRegex.test(childText) ) fullUrl = 'https://' + childText; valueSpan.innerHTML = DOMPurify.sanitize(`<a href="${ fullUrl }">"${ fullUrl }"</a>`); } else { valueSpan.textContent = `"${childText}"`; } } valueSpan.className = "type-string"; } if ( jsonMap.implicitNulls === undefined || jsonMap.implicitNulls.indexOf(key) == -1 ) { newDiv.appendChild(valueSpan); } else { newDiv.innerHTML = newDiv.innerHTML.slice(0, -2); } newListItem.appendChild(newDiv); listItem.appendChild(newListItem); } } } } else if (Array.isArray(jsonMap)) { for (var i = 0, c = jsonMap.length; i < c; i++) { let newList = document.createElement('ul'), newDiv = document.createElement('code'), newListItem = document.createElement('li'); newListItem.dataset.graphArbo = i; if (path.length > 0) newListItem.dataset.graphArbo = path.join('-') + '-' + i; if ( typeof jsonMap[i] === 'object' && jsonMap[i] !== null ) { if (Array.isArray(jsonMap[i])) { if ( jsonMap.damonInlineArrays !== undefined && jsonMap.damonInlineArrays.indexOf(i) > -1 ) { newDiv.innerHTML += '['; for (let j = 0, k = jsonMap[i].length; j < k; j++) { let valueSpan = document.createElement('span'), value = jsonMap[i][j]; if (value === true) { valueSpan.textContent = "true"; valueSpan.className = "type-boolean"; } else if (value === false) { valueSpan.textContent = "false"; valueSpan.className = "type-boolean"; } else if (value === null) { valueSpan.textContent = "null"; valueSpan.className = "type-null"; } else if ( Number.isFinite(value) && !Number.isNaN(value) ) { valueSpan.textContent = value + ""; valueSpan.className = "type-number"; } else { if (safeHTML) { if ( $.websiteRegex.test(value) || $.pathRegex.test(value) ) { let fullUrl = value; if ( !$.httpRegex.test(value) && $.websiteRegex.test(value) ) fullUrl = ('https://' + value); valueSpan.innerHTML = DOMPurify.sanitize(`<a href="${ fullUrl }">"${ fullUrl }"</a>`); } else { valueSpan.innerHTML = `"${value}"`; } } else { if ( $.websiteRegex.test(value) || $.pathRegex.test(value) ) { let fullUrl = value; if ( !$.httpRegex.test(value) && $.websiteRegex.test(value) ) fullUrl = ('https://' + value); valueSpan.innerHTML = DOMPurify.sanitize(`<a href="${ fullUrl }">"${ fullUrl }"</a>`); } else { valueSpan.textContent = `"${value}"`; } } valueSpan.className = "type-string"; } if (j !== 0) { newDiv.innerHTML += ', '; } newDiv.appendChild(valueSpan); } newDiv.innerHTML += ']'; newListItem.appendChild(newDiv); newListItem.appendChild(newList); listItem.appendChild(newListItem); } else { newDiv.textContent = "[]"; newListItem.appendChild(newDiv); newListItem.appendChild(newList); listItem.appendChild(newListItem); recurse(jsonMap[i], newList, path.concat(i)); } } else { newDiv.textContent = "{}"; newListItem.appendChild(newDiv); newListItem.appendChild(newList); listItem.appendChild(newListItem); recurse(jsonMap[i], newList, path.concat(i)); } } else { jsonItemIndex++; let childText = jsonMap[i]; if (childText === true) { newDiv.textContent = "true"; newDiv.className = "type-boolean"; } else if (childText === false) { newDiv.textContent = "false"; newDiv.className = "type-boolean"; } else if (childText === null) { newDiv.textContent = "null"; newDiv.className = "type-null"; } else if ( Number.isFinite(childText) && !Number.isNaN(childText) ) { newDiv.textContent = childText + ""; newDiv.className = "type-number"; } else { if (safeHTML) { if ( $.websiteRegex.test(childText) || $.pathRegex.test(childText) ) { let fullUrl = childText; if ( !$.httpRegex.test(childText) && $.websiteRegex.test(childText) ) fullUrl = 'https://' + childText; newDiv.innerHTML = DOMPurify.sanitize(`<a href="${ fullUrl }">"${ fullUrl }"</a>`); } else { newDiv.innerHTML = `"${childText}"`; } } else { if ( $.websiteRegex.test(childText) || $.pathRegex.test(childText) ) { let fullUrl = childText; if ( !$.httpRegex.test(childText) && $.websiteRegex.test(childText) ) fullUrl = 'https://' + childText; newDiv.innerHTML = DOMPurify.sanitize(`<a href="${ fullUrl }">"${ fullUrl }"</a>`); } else { newDiv.textContent = `"${childText}"`; } } newDiv.className = "type-string"; } newListItem.appendChild(newDiv); newListItem.appendChild(newList); listItem.appendChild(newListItem); } } } } } sliceIjsonKey(key) { let indexPrefixMatch = key.match(/^[0-9]+-/); if (indexPrefixMatch[0] !== null) { return key.slice(indexPrefixMatch[0].length); } else { throw new Error(`Key "${ key }" fails IJSON validation.`); } } /** * @param {damonValue} jsonMap * @param {boolean} [safeHTML=false] * @returns {object} DOM */ mapToHtmlTable(jsonMap, safeHTML = false) { let $ = this; // Parsing check try { $.damon.mapToJSON(jsonMap); } catch (error) { throw new Error("Provided value doesn't passes JSON.parse()."); } var jsonItemIndex = 0, table = document.createElement('table'), tHead = document.createElement('thead'), tBody = document.createElement('tbody'), headingsEncountered = false, columnsLength = 0; table.className = 'DAMON-Table'; if ( typeof jsonMap !== 'object' || jsonMap == null || Array.isArray(jsonMap) || !(jsonMap instanceof Map) || jsonMap.constructor !== Map ) { throw new Error("Error: expected an Object value, saw otherwise."); } for (const [key, value] of jsonMap) { if ( typeof value === 'object' && value !== null && !Array.isArray(value) && value instanceof Map && value.constructor === Map ) { if (jsonItemIndex == 0) { let slicedSubMapKeys = Array.from(value.keys()).map($.sliceIjsonKey), slicedSubMapKeysSet = new Set(slicedSubMapKeys); if (slicedSubMapKeys.length !== slicedSubMapKeysSet.size) { throw new Error('Duplicate in headings.'); } let row = document.createElement('tr'); columnsLength = value.length; for (const [cK, childValue] of value) { let childKey = $.sliceIjsonKey(cK); if (childValue === null) { let headerCell = document.createElement('th'); headerCell.dataset.graphArbo = jsonItemIndex + '-' + row.children.length; if (safeHTML) { if ( $.websiteRegex.test(childKey) || $.pathRegex.test(childKey) ) { headerCell.innerHTML = DOMPurify.sanitize(`<a href="${ childKey }">${ childKey }</a>`); } else { headerCell.innerHTML = `${childKey}`; } } else { if ( $.websiteRegex.test(childKey) || $.pathRegex.test(childKey) ) { headerCell.innerHTML = DOMPurify.sanitize(`<a href="${ childKey }">${ childKey }</a>`); } else { headerCell.textContent = `${childKey}`; } } row.appendChild(headerCell); } else { throw new Error( "Error row " + jsonItemIndex + ": expected implicit null property, saw otherwise." ); } } tHead.appendChild(row); headingsEncountered = true; } else { if (value.length != columnsLength) { // unmatching columns length throw new Error( "Error row " + jsonItemIndex + ": cells total doesn't match the header's." ); } let row = document.createElement('tr'); for (const [cK, childValue] of value) { let childKey = $.sliceIjsonKey(cK); if (childValue === null) { let dataCell = document.createElement('td'); dataCell.dataset.graphArbo = jsonItemIndex + '-' + row.children.length; if (safeHTML) { if ( $.websiteRegex.test(childKey) || $.pathRegex.test(childKey) ) { dataCell.innerHTML = DOMPurify.sanitize(`<a href="${ childKey }">${ childKey }</a>`); } else { dataCell.innerHTML = `${childKey}`; } } else { if ( $.websiteRegex.test(childKey) || $.pathRegex.test(childKey) ) { dataCell.innerHTML = DOMPurify.sanitize(`<a href="${ childKey }">${ childKey }</a>`); } else { dataCell.textContent = `${childKey}`; } } row.appendChild(dataCell); } else { throw new Error( "Error row " + jsonItemIndex + ": expected implicit null property, saw otherwise." ); } } tBody.appendChild(row); } } else { throw new Error("Error row " + jsonItemIndex + ": expected an Object value, saw otherwise."); } jsonItemIndex++; } if (headingsEncountered) { table.appendChild(tHead); } table.appendChild(tBody); return table; } /** * @param {damonMap} jsonMap * @returns {string} */ prefixedKeysMapToSExpression(jsonMap) { const $ = this; var list = ``; if ( typeof jsonMap === 'object' && jsonMap !== null && jsonMap instanceof Map && jsonMap.constructor === Map ) { list += "[\r\n"; _recurse(jsonMap); list += "]"; JSON.parse(list); return list; } else { if (typeof jsonMap == 'string') { jsonMap = '"' + jsonMap + '"'; } JSON.parse(jsonMap); return jsonMap; } /** * @param {Map<string, any>|Array<any>} jsonMap * @param {number} [level=1] * @returns {string} */ function _recurse(jsonMap, level = 1) { if ( typeof jsonMap === 'object' && jsonMap !== null && !Array.isArray(jsonMap) && jsonMap instanceof Map && jsonMap.constructor === Map ) { let i = -1; for (const [k, value] of jsonMap) { i++; let key = $.sliceIjsonKey(k); if ( typeof value === 'object' && value !== null ) { if (Array.isArray(value)) { if (value.length > 0) { list += ' '.repeat(level) + `${JSON.stringify(key)}, [\r\n`; _recurse(value, level + 1); list += ' '.repeat(level) + `]`; } else { list += ' '.repeat(level) + `${JSON.stringify(key)}, []`; } } else { if (Array.from(value.keys()).length > 0) { if (level == 1) { if (i == 0) { list = ""; } else { throw new Error("Multiple S-Expression roots"); } } list += ' '.repeat(level) + `[${JSON.stringify(key)}, \r\n`; _recurse(value, level + 1); if (level != 1) { list += ' '.repeat(level) + `]`; } } else { list += ' '.repeat(level) + `${JSON.stringify(key)}, []`; } } } else { if (value === true) { throw new Error('Booleans require quotes'); } else if (value === false) { throw new Error('Booleans require quotes'); } else if (value === null) { list += ' '.repeat(level) + `${JSON.stringify(key)}`; } else if ( Number.isFinite(value) && !Number.isNaN(value) ) { list += ' '.repeat(level) + `${JSON.stringify(key)}` + ', ' + value; } else { list += ' '.repeat(level) + `${JSON.stringify(key)}` + ', ' + JSON.stringify(value); } } let lastKey = Array.from(jsonMap.keys())[Array.from(jsonMap.keys()).length - 1]; if (k != lastKey) { list += ",\r\n"; } else { list += "\r\n"; } } } else if (Array.isArray(jsonMap)) { for (var i = 0, c = jsonMap.length; i < c; i++) { if ( typeof jsonMap[i] === 'object' && jsonMap[i] !== null ) { if (Array.isArray(jsonMap[i])) { if (jsonMap[i].length > 0) { list += ' '.repeat(level) + `[\r\n`; _recurse(jsonMap[i], level + 1); list += ' '.repeat(level) + `]`; } else { list += ' '.repeat(level) + `[]`; } } else { if (Array.from(jsonMap[i].keys()).length > 0) { list += ' '.repeat(level) + `[\r\n`; _recurse(jsonMap[i], level + 1); list += ' '.repeat(level) + `]`; } else { list += ' '.repeat(level) + `[]`; } } } else { if (jsonMap[i] === true) { throw new Error('Booleans require quotes'); } else if (jsonMap[i] === false) { throw new Error('Booleans require quotes'); } else if (jsonMap[i] === null) { throw new Error('Array-nulls require quotes'); } else if ( Number.isFinite(jsonMap[i]) && !Number.isNaN(jsonMap[i]) ) { list += ' '.repeat(level) + jsonMap[i]; } else { list += ' '.repeat(level) + JSON.stringify(jsonMap[i]); } } if (i != c - 1) { list += ",\r\n"; } else { list += "\r\n"; } } } } } /** * * * @param {String} sExpression * @returns {Map} */ sExpressionToPrefixedKeysMap(sExpression) { const $ = this; let damonMap = new Map(); damonMap.headless = true; _recurse(JSON.parse(sExpression), damonMap, 0); return damonMap; function _recurse(sExpressionArray, map, i) { let childMap = new Map(); map.set(i + '-' + sExpressionArray[0], childMap); for (let i = 1, c = sExpressionArray.length; i < c; i++) { if (Array.isArray(sExpressionArray[i])) { _recurse(sExpressionArray[i], childMap, i - 1); } else { let indexPrefixedKey = (i - 1) + '-' + sExpressionArray[i]; childMap.set(indexPrefixedKey, null); } } } } /** * Arrays of inline-arrays produce array-parameters * @param {string} damonString * @return {string} mathJs */ prefixedKeysMapToMathJs(damonMap) { const $ = this; let mathJs = ''; if (Array.isArray(damonMap)) { _recurse(damonMap); return mathJs.slice(0, -1); } else if ( typeof damonMap === 'object' && damonMap !== null && damonMap instanceof Map && damonMap.constructor === Map ) { _recurse(damonMap); return mathJs.slice(0, -1); } else {8 if (typeof damonMap == 'string') { damonMap = JSON.stringify(damonMap); } JSON.parse(damonMap); return damonMap; } function _minusculize(string) { return string[0].toLowerCase() + string.slice(1); } /** * @param {Map<string, any>|Array<any>} damonMap * @param {number} [level=1] * @returns {string} */ function _recurse(damonMap, level = 0) { if ( typeof damonMap === 'object' && damonMap !== null && !Array.isArray(damonMap) && damonMap instanceof Map && damonMap.constructor === Map ) { for (const [k, value] of damonMap) { let key = $.sliceIjsonKey(k); // Normalizing to katex if (key == 'Power') key = 'Pow'; if (key == 'e') key = '2.718281828459045'; if (key == 'i') key = 'sqrt(-1)'; if (key == 'LN2') key = '0.6931471805599453'; if (key == 'LN10') key = '2.302585092994046'; if (key == 'LOG2E') key = '1.4426950408889634'; if (key == 'LOG10E') key = '0.4342944819032518'; if (key == 'phi') key = '1.618033988749895'; if (key == 'pi') key = '3.141592653589793'; if (key == 'SQRT1_2') key = '0.7071067811865476'; if (key == 'SQRT2') key = '1.4142135623730951'; if (key == 'tau') key = '6.283185307179586'; if ( typeof value === 'object' && value !== null ) { if (Array.isArray(value)) { if (value.length > 0) { mathJs += ' '.repeat(level) + `${JSON.stringify(_minusculize(key)).slice(1, -1)}(\n`; _recurse(value, level + 1); mathJs += ' '.repeat(level) + `)`; } else { mathJs += ' '.repeat(level) + `${JSON.stringify(_minusculize(key)).slice(1, -1)}()`; } } else { if (Array.from(value.keys()).length > 0) { mathJs += ' '.repeat(level) + `${JSON.stringify(_minusculize(key)).slice(1, -1)}(\n`; _recurse(value, level + 1); mathJs += ' '.repeat(level) + `)`; } else { mathJs += ' '.repeat(level) + `${JSON.stringify(_minusculize(key)).slice(1, -1)}()`; } } } else { if (value === true) { mathJs += ' '.repeat(level) + `${JSON.stringify(_minusculize(key)).slice(1, -1)}` + "(true)"; } else if (value === false) { mathJs += ' '.repeat(level) + `${JSON.stringify(_minusculize(key)).slice(1, -1)}` + "(false)"; } else if (value === null) { mathJs += ' '.repeat(level) + `${JSON.stringify(_minusculize(key)).slice(1, -1)}`; } else if ( Number.isFinite(value) && !Number.isNaN(value) ) { mathJs += ' '.repeat(level) + `${JSON.stringify(_minusculize(key)).slice(1, -1)}` + '(' + value + ')'; } else { mathJs += ' '.repeat(level) + `${JSON.stringify(_minusculize(key)).slice(1, -1)}` + '(' + JSON.stringify(value) + ')'; } } let lastKey = Array.from(damonMap.keys())[Array.from(damonMap.keys()).length - 1]; if (k != lastKey) { mathJs += ",\n"; } else { mathJs += "\n"; } } } else if (Array.isArray(damonMap)) { for (var i = 0, c = damonMap.length; i < c; i++) { if ( typeof damonMap[i] === 'object' && damonMap[i] !== null ) { if (Array.isArray(damonMap[i])) { if (damonMap[i].length > 0) { if ( damonMap.damonInlineArrays !== undefined && damonMap.damonInlineArrays.indexOf(i) > -1 ) { if (damonMap[i] === true) { mathJs += ' '.repeat(level) + "true"; } else if (damonMap[i] === false) { mathJs += ' '.repeat(level) + "false"; } else if (damonMap[i] === null) { mathJs += ' '.repeat(level) + "null"; } else if ( Number.isFinite(damonMap[i]) && !Number.isNaN(damonMap[i]) ) { mathJs += ' '.repeat(level) + damonMap[i]; } else { mathJs += ' '.repeat(level) + JSON.stringify(damonMap[i]).slice(1, -1); } } else { mathJs += ' '.repeat(level) + `(\n`; _recurse(damonMap[i], level + 1); mathJs += ' '.repeat(level) + `)`; } } else { mathJs += ' '.repeat(level) + `()`; } } else { if (Array.from(damonMap[i].keys()).length > 0) { mathJs += ' '.repeat(level) + `(\n`; _recurse(damonMap[i], level + 1); mathJs += ' '.repeat(level) + `)`; } else { mathJs += ' '.repeat(level) + `()`; } } } else { if (damonMap[i] === true) { mathJs += ' '.repeat(level) + "true"; } else if (damonMap[i] === false) { mathJs += ' '.repeat(level) + "false"; } else if (damonMap[i] === null) { mathJs += ' '.repeat(level) + "null"; } else if ( Number.isFinite(damonMap[i]) && !Number.isNaN(damonMap[i]) ) { mathJs += ' '.repeat(level) + damonMap[i]; } else { mathJs += ' '.repeat(level) + JSON.stringify(damonMap[i]).slice(1, -1); } } if (i != c - 1) { mathJs += ",\n"; } else { mathJs += "\n"; } } } } } /** * @param {damonValue} firstMap * @param {damonValue} secondMap */ _mapsDiff(firstMap, secondMap) { let $ = this; // Parsing check try { $.damon.mapToJSON(firstMap); $.damon.mapToJSON(secondMap); } catch (error) { throw new Error("Provided map value doesn't passes JSON.parse()"); } if ( typeof firstMap !== typeof secondMap || ( ( typeof firstMap === 'object' && firstMap !== null && !Array.isArray(firstMap) && firstMap instanceof Map && firstMap.constructor === Map ) && ( secondMap == null || Array.isArray(secondMap) || !(secondMap instanceof Map) || secondMap.constructor !== Map ) ) ) { // fully red render throw new Error("Different root types."); } var diffMap; if ( typeof firstMap === 'object' && firstMap !== null && !Array.isArray(firstMap) && firstMap instanceof Map && firstMap.constructor === Map ) { diffMap = new Map(); _walkAndDiff(firstMap); return diffMap; // # Red-Green-Yellow diffMap // - Checks key, value-structure and position: "green" && recurse // - Fails key or value-structure: red // - Fails position: yellow // - Check value structure: green // # Rendering // - Render from Red-Green-Yellow // - optimization: lookahead and group // Intersecti