1397 lines
29 KiB
JavaScript
1397 lines
29 KiB
JavaScript
const XTypeTools = require('./x-type-detector.tools');
|
|
|
|
/**
|
|
* validate a value as an argument ...
|
|
*
|
|
* @param {string} value the value which going to validate ...
|
|
* @returns
|
|
*/
|
|
function isValidArg(value) {
|
|
//
|
|
let result = false;
|
|
|
|
//
|
|
result = value &&
|
|
value.length > 0;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* validate a collection of valuea as an arguments ...
|
|
*
|
|
* @param {string|string[]} values the value collection which going to checked ...
|
|
* @returns
|
|
*/
|
|
function isValidArgs(values) {
|
|
//
|
|
// Normalize Values ...
|
|
const normalValues = toNormalArray(values);
|
|
if (!hasChildArray(normalValues)) {
|
|
return false;
|
|
}
|
|
|
|
//
|
|
let result = normalValues.every(nv => isValidArg(nv));
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* converts content to an array object ...
|
|
*
|
|
* @param {string|string[]} content the content which required to normalized ...
|
|
* @returns {string[]} array object ...
|
|
*/
|
|
function toNormalArray(content = '') {
|
|
//
|
|
// Validate Args ...
|
|
if (!isValidArg(content)) {
|
|
return [];
|
|
}
|
|
|
|
//
|
|
const result = Array.isArray(content) ?
|
|
[...content] :
|
|
content.includes(',') &&
|
|
content !== ',' ?
|
|
[...content.split(',')] :
|
|
[content];
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* determines value is array and has atleast one child ...
|
|
*
|
|
* @param {any} value the value which going to checked ...
|
|
* @returns {boolean} result of checking ...
|
|
*/
|
|
function hasChildArray(value) {
|
|
//
|
|
let result = false;
|
|
result = value &&
|
|
Array.isArray(value) &&
|
|
value.length > 0;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* check an string is ends with specified symbol ...
|
|
*
|
|
* @param {string} symbol specified search symbol ...
|
|
* @param {string} value specified search content ...
|
|
* @param {boolean} ignoreCase specified search case sensitive or not ...
|
|
* @returns
|
|
*/
|
|
function endsWidth(
|
|
symbol = '',
|
|
value = '',
|
|
ignoreCase = true
|
|
) {
|
|
//
|
|
if (!isValidArg(value)) {
|
|
return false;
|
|
}
|
|
|
|
//
|
|
if (!isValidArg(symbol)) {
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// TODO: uncomment it if necessary ...
|
|
// symbol = symbol.trim();
|
|
|
|
//
|
|
let result = false;
|
|
const subtractedContent = value.substring(value.length - symbol.length);
|
|
result = !!ignoreCase ?
|
|
subtractedContent
|
|
.toLowerCase() === symbol
|
|
.toLowerCase() :
|
|
subtractedContent === symbol;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* surround specified content by provided symbol ...
|
|
*
|
|
* @param {string} symbol surround string ...
|
|
* @param {string} content used content to surrounded by symbol ...
|
|
* @returns surrounded content ...
|
|
*/
|
|
function surroundBy(
|
|
symbol = '',
|
|
content = ''
|
|
) {
|
|
//
|
|
// Validate Args ...
|
|
if (!isValidArg(content)) {
|
|
content = '';
|
|
}
|
|
|
|
//
|
|
if (!isValidArg(symbol)) {
|
|
symbol = '';
|
|
}
|
|
|
|
//
|
|
if (isSurrounded(symbol, content)) {
|
|
return content;
|
|
}
|
|
|
|
//
|
|
const result = `${symbol}${content}${symbol}`;
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* convert a parsed sign to clear string ...
|
|
*
|
|
* @param {string} value a parsed content ...
|
|
* @returns clear parsed signs ...
|
|
*/
|
|
function toStringExpression(value) {
|
|
//
|
|
let result = '';
|
|
result = value;
|
|
|
|
//
|
|
if (
|
|
!value ||
|
|
!isValidArg(value) ||
|
|
!isSurroundedString(value)
|
|
) {
|
|
return result;
|
|
}
|
|
|
|
//
|
|
const startSymbol = value.charAt(0);
|
|
const endSymbol = value.charAt(value.length - 1);
|
|
|
|
//
|
|
if (startSymbol !== endSymbol) {
|
|
return result;
|
|
}
|
|
|
|
//
|
|
if (
|
|
startSymbol === "\"" ||
|
|
startSymbol === "\'"
|
|
) {
|
|
result = clearSurround(startSymbol, value);
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* find and cleare proposed candidates from specified content ...
|
|
*
|
|
* @param {string|string[]} candidates the proposed string(s) whic going to cleared from content ...
|
|
* @param {string} content the destination content which used to find and replace candidates on it ...
|
|
* @returns cleared content ...
|
|
*/
|
|
function clearContent(
|
|
candidates,
|
|
content
|
|
) {
|
|
//
|
|
// Validate Args ...
|
|
if (
|
|
!candidates ||
|
|
!isValidArg(content)
|
|
) {
|
|
return '';
|
|
}
|
|
|
|
//
|
|
// Normalize candidates ...
|
|
const normalCandidates = toNormalArray(candidates)
|
|
.filter(c => isValidArg(c));
|
|
if (!hasChildArray(normalCandidates)) {
|
|
return content;
|
|
}
|
|
|
|
//
|
|
// define regexp for content ...
|
|
const regExpExpression = normalCandidates.join('|');
|
|
const regExp = new RegExp(
|
|
regExpExpression,
|
|
'gi'
|
|
);
|
|
|
|
//
|
|
let result = '';
|
|
result = content.replace(regExp, '');
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* clear surrounded symbol from content ...
|
|
*
|
|
* @param {string} symbol specified search symbol ...
|
|
* @param {string} content specified content to clear ...
|
|
* @returns
|
|
*/
|
|
function clearSurround(
|
|
symbol = '',
|
|
content = ''
|
|
) {
|
|
//
|
|
// Validate Args ...
|
|
if (
|
|
!isValidArg(content) ||
|
|
(isValidArg(symbol) &&
|
|
symbol.length >= content.length - 1)
|
|
) {
|
|
return '';
|
|
}
|
|
|
|
//
|
|
// Validate Surround ...
|
|
if (!isSurrounded(symbol, content)) {
|
|
return content;
|
|
}
|
|
|
|
//
|
|
if (!isValidArg(symbol)) {
|
|
return content;
|
|
}
|
|
|
|
//
|
|
let result = content.substring(symbol.length, content.length - symbol.length);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* clear object sign ...
|
|
*
|
|
* @param {string} content
|
|
* @returns cleared content ...
|
|
*/
|
|
function clearObjectSurround(content = '') {
|
|
//
|
|
// Validate Args ...
|
|
if (
|
|
!isValidArg(content) ||
|
|
!isSurroundedObject(content)
|
|
) {
|
|
return content;
|
|
}
|
|
|
|
//
|
|
const endIndex = content.length - (
|
|
endsWidth('}', content) ?
|
|
1 :
|
|
endsWidth('},', content) ||
|
|
endsWidth('};', content) ||
|
|
endsWidth('}\n', content) ?
|
|
2 :
|
|
0
|
|
);
|
|
|
|
//
|
|
const result = content.substring(1, endIndex);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* clear array sign ...
|
|
*
|
|
* @param {string} content
|
|
* @returns cleared content ...
|
|
*/
|
|
function clearArraySurround(content = '') {
|
|
//
|
|
// Validate Args ...
|
|
if (
|
|
!isValidArg(content) ||
|
|
!isSurroundedArray(content)
|
|
) {
|
|
return content;
|
|
}
|
|
|
|
//
|
|
const endIndex = content.length - (
|
|
endsWidth(']', content) ?
|
|
1 :
|
|
endsWidth('],', content) ||
|
|
endsWidth('];', content) ||
|
|
endsWidth(']\n', content) ?
|
|
2 :
|
|
0
|
|
);
|
|
|
|
//
|
|
const result = content.substring(1, endIndex);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* slice a content to individual parts ...
|
|
*
|
|
* @param {number|number[]} indexes which indexes used to slice ...
|
|
* @param {strng} content the content which going to sliced ...
|
|
* @returns sliced parts of content ...
|
|
*/
|
|
function sliceContent(indexes, content) {
|
|
//
|
|
// Validate Args ...
|
|
if (
|
|
!isValidArg(indexes) ||
|
|
!isValidArg(content)
|
|
) {
|
|
return [];
|
|
}
|
|
|
|
//
|
|
// Normalize indexes ...
|
|
const normalIndexes = toNormalArray(indexes)
|
|
.filter(index => index > -1 && index < content.length);
|
|
if (!hasChildArray(normalIndexes)) {
|
|
return [];
|
|
}
|
|
|
|
//
|
|
let result = [''];
|
|
result.pop();
|
|
|
|
//
|
|
let lastIndex = 0;
|
|
for (const index of normalIndexes) {
|
|
//
|
|
const part = content.substring(lastIndex, index);
|
|
if (isValidArg(part)) {
|
|
result.push(part.trim());
|
|
}
|
|
|
|
//
|
|
lastIndex = index + 1;
|
|
}
|
|
|
|
//
|
|
// check remained parts ...
|
|
if (lastIndex < content.length) {
|
|
//
|
|
const part = content.substring(lastIndex);
|
|
if (isValidArg(part)) {
|
|
result.push(part.trim());
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* check an string is starts and ends with specified symbol ...
|
|
*
|
|
* @param {string} symbol specified search symbol ...
|
|
* @param {string} content specified search content ...
|
|
* @param {boolean} ignoreCase specified search case sensitive or not ...
|
|
* @returns
|
|
*/
|
|
function isSurrounded(
|
|
symbol = '',
|
|
content = '',
|
|
ignoreCase = true
|
|
) {
|
|
//
|
|
// Validate Args ...
|
|
if (!isValidArg(content)) {
|
|
return false;
|
|
}
|
|
|
|
//
|
|
if (!isValidArg(symbol)) {
|
|
return true;
|
|
}
|
|
|
|
//
|
|
symbol = symbol.trim();
|
|
|
|
//
|
|
let result = false;
|
|
result = (!!ignoreCase ?
|
|
content
|
|
.toLowerCase()
|
|
.startsWith(symbol.toLowerCase()) :
|
|
content.startsWith(symbol)
|
|
) &&
|
|
endsWidth(symbol, content, ignoreCase);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* determines a content is an string surrounded value or not ...
|
|
*
|
|
* @param {string} content a content which going to check ...
|
|
* @returns
|
|
*/
|
|
function isSurroundedString(content = '') {
|
|
//
|
|
// Validate Args ...
|
|
if (!isValidArg(content)) {
|
|
return false;
|
|
}
|
|
|
|
//
|
|
let result = false;
|
|
result = (
|
|
content.startsWith('\'') &&
|
|
(
|
|
endsWidth('\'', content) ||
|
|
endsWidth('\',', content) ||
|
|
endsWidth('\';', content) ||
|
|
endsWidth('\'\n', content)
|
|
) ||
|
|
content.startsWith('\"') &&
|
|
(
|
|
endsWidth('\"', content) ||
|
|
endsWidth('\",', content) ||
|
|
endsWidth('\";', content) ||
|
|
endsWidth('\"\n', content)
|
|
) ||
|
|
content.startsWith('`') &&
|
|
(
|
|
endsWidth('\`', content) ||
|
|
endsWidth('\`,', content) ||
|
|
endsWidth('\`;', content) ||
|
|
endsWidth('\`\n', content)
|
|
)
|
|
);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* check a content is an array content or not ...
|
|
*
|
|
* @param {string} content
|
|
* @returns
|
|
*/
|
|
function isSurroundedArray(content = '') {
|
|
//
|
|
// Validate Args ...
|
|
if (!isValidArg(content)) {
|
|
return false;
|
|
}
|
|
|
|
//
|
|
let result = false;
|
|
result = content.startsWith('[') &&
|
|
(
|
|
endsWidth(']', content) ||
|
|
endsWidth('],', content) ||
|
|
endsWidth('];', content) ||
|
|
endsWidth(']\n', content)
|
|
);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* check a content is an object content or not ...
|
|
*
|
|
* @param {string} content
|
|
* @returns
|
|
*/
|
|
function isSurroundedObject(content = '') {
|
|
//
|
|
// Validate Args ...
|
|
if (!isValidArg(content)) {
|
|
return false;
|
|
}
|
|
|
|
//
|
|
let result = false;
|
|
result = content.startsWith('{') &&
|
|
(
|
|
endsWidth('}', content) ||
|
|
endsWidth('},', content) ||
|
|
endsWidth('};', content) ||
|
|
endsWidth('}\n', content)
|
|
);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* check an index model is contains inside anothers or not ...
|
|
*
|
|
* @param {{ start: number, end: number}} source the source index model to check ...
|
|
* @param {...{ start: number, end: number}} dest the collection of index models which going to check ...
|
|
* @returns
|
|
*/
|
|
function isIndexInside(source, ...dest) {
|
|
//
|
|
let result = false;
|
|
|
|
//
|
|
// Validate Args ...
|
|
if (!source || !dest) {
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check idx is standard ...
|
|
if (
|
|
!source ||
|
|
!source.end ||
|
|
!source.start ||
|
|
source.start > source.end
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// Check types of destinations ...
|
|
for (let index of dest) {
|
|
//
|
|
// Check idx is standard ...
|
|
if (
|
|
!index ||
|
|
!index.end ||
|
|
!index.start ||
|
|
index.start > index.end
|
|
) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//
|
|
if (dest.includes(source)) {
|
|
dest = dest.filter(d => d !== source);
|
|
}
|
|
|
|
//
|
|
const insideContentIndex = dest.find(dIndex => {
|
|
//
|
|
const result = source.start >= dIndex.start &&
|
|
source.start <= dIndex.end &&
|
|
source.end >= dIndex.start &&
|
|
source.end <= dIndex.end;
|
|
return result;
|
|
});
|
|
|
|
//
|
|
result = insideContentIndex;
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* check a number exists in
|
|
* @param {number} idx the number which going to check ...
|
|
* @param {{ start: number, end: number}} index the index model
|
|
* @returns
|
|
*/
|
|
function isInsideIndex(idx = -1, index) {
|
|
//
|
|
let result = false;
|
|
|
|
//
|
|
// Validate Args ...
|
|
if (
|
|
!index ||
|
|
!index.hasOwnProperty('end') ||
|
|
!index.hasOwnProperty('start')
|
|
) {
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = idx > index.start && idx < index.end;
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* determines an object is key/value and type ...
|
|
*
|
|
* @param {any} content the object which we are going to check ...
|
|
* @returns
|
|
*/
|
|
function isKeyValueType(content) {
|
|
//
|
|
const result = content &&
|
|
content.hasOwnProperty('key') &&
|
|
content.hasOwnProperty('type') &&
|
|
content.hasOwnProperty('value');
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* find all indexes of token(s) in content ...
|
|
*
|
|
* @param {string|string[]} tokens the tokens which require to search ...
|
|
* @param {string} content the content which using to search ...
|
|
* @returns {number[]} all occured indexes ...
|
|
*/
|
|
function findAllIndexes(
|
|
tokens,
|
|
content
|
|
) {
|
|
//
|
|
// Validate Args ...
|
|
if (
|
|
!isValidArg(tokens) ||
|
|
!isValidArg(content)
|
|
) {
|
|
return [];
|
|
}
|
|
|
|
//
|
|
// normalize tokens ...
|
|
const normalTokens = toNormalArray(tokens);
|
|
if (!hasChildArray(normalTokens)) {
|
|
return [];
|
|
}
|
|
|
|
//
|
|
// Parse contents ...
|
|
let match;
|
|
const result = [];
|
|
const regExp = new RegExp(
|
|
normalTokens.join('|'),
|
|
'g'
|
|
);
|
|
while ((match = regExp.exec(content)) !== null) {
|
|
result.push(match.index);
|
|
}
|
|
if (!hasChildArray(result)) {
|
|
return [];
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* find nearest symbol in a content from specified index ...
|
|
*
|
|
* @param {string|string[]} candidates specifies which symbols to find ...
|
|
* @param {string} content te content for seasrch ...
|
|
* @param {number} startFromIndex the index of start position ...
|
|
* @returns
|
|
*/
|
|
function findNearest(
|
|
candidates,
|
|
content = '',
|
|
startFromIndex = 0
|
|
) {
|
|
//
|
|
// Validate Args ...
|
|
if (
|
|
!isValidArg(content) ||
|
|
!isValidArg(candidates) ||
|
|
!isValidArg(content.trim())
|
|
) {
|
|
return '';
|
|
}
|
|
|
|
//
|
|
// Normalize Content ...
|
|
content = content.trim();
|
|
|
|
//
|
|
// Normalize Starts From ...
|
|
startFromIndex = startFromIndex < 0 || startFromIndex > content.length - 1 ? 0 : startFromIndex;
|
|
|
|
//
|
|
// Normalize Symbols ...
|
|
const normalCandidates = toNormalArray(candidates);
|
|
if (!hasChildArray(normalCandidates)) {
|
|
return '';
|
|
}
|
|
|
|
//
|
|
let nearestIndex = findNearestIndex(
|
|
normalCandidates,
|
|
content,
|
|
startFromIndex
|
|
);
|
|
if (nearestIndex < 0) {
|
|
return '';
|
|
}
|
|
|
|
//
|
|
let result = content.substring(
|
|
nearestIndex,
|
|
nearestIndex + Math.max(...normalCandidates.map(nc => nc.length))
|
|
).trim();
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* search candidates in content and find nearest one and return it ...
|
|
*
|
|
* @param {string|string[]} candidates which candidates required to check ...
|
|
* @param {string} content the content which required to search candidates ...
|
|
* @param {number} startFromIndex the proposed index to start searching content from on ...
|
|
* @returns
|
|
*/
|
|
function findNearestIndex(
|
|
candidates = [''],
|
|
content = '',
|
|
startFromIndex = 0,
|
|
ignoreClosedItems = false
|
|
) {
|
|
//
|
|
// Validate Args ...
|
|
if (
|
|
!isValidArg(content) ||
|
|
!isValidArg(candidates)
|
|
) {
|
|
return -1;
|
|
}
|
|
|
|
//
|
|
// Mormalize startFromIndex value ...
|
|
startFromIndex = startFromIndex < 0 ?
|
|
0 :
|
|
startFromIndex > content.length - 1 ?
|
|
content.length - 1 :
|
|
startFromIndex;
|
|
|
|
//
|
|
// Normalize candidates ...
|
|
let normalCandidates = toNormalArray(candidates);
|
|
|
|
//
|
|
if (!!ignoreClosedItems) {
|
|
normalCandidates = normalCandidates
|
|
.map(c => content.indexOf(
|
|
c,
|
|
startFromIndex
|
|
)
|
|
).map(c => +c)
|
|
.filter(c => +c > -1);
|
|
} else {
|
|
normalCandidates = findAllIndexesOutOfCloseds(normalCandidates, content)
|
|
.filter(c => c >= startFromIndex);
|
|
}
|
|
if (!hasChildArray(normalCandidates)) {
|
|
return -1;
|
|
}
|
|
|
|
//
|
|
let result = -1;
|
|
result = Math.min(...normalCandidates);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* find close index of specific sign in destination content ...
|
|
*
|
|
* @param {string} openSymbol open of area sign ...
|
|
* @param {string} closeSymbol close of area sign ...
|
|
* @param {string} content the destination content for parsing ...
|
|
* @param {number} startsFrom an start index of content for start parsing ...
|
|
* @returns
|
|
*/
|
|
function findCloseIndex(
|
|
openSymbol = '',
|
|
closeSymbol = '',
|
|
content = '',
|
|
startsFrom = -1
|
|
) {
|
|
//
|
|
// Validate Args ...
|
|
if (
|
|
startsFrom < 0 ||
|
|
!isValidArg(content) ||
|
|
!isValidArg(openSymbol) ||
|
|
!isValidArg(closeSymbol) ||
|
|
openSymbol === closeSymbol ||
|
|
!content.includes(openSymbol) ||
|
|
!content.includes(closeSymbol) ||
|
|
startsFrom > content.length - 1
|
|
) {
|
|
return -1;
|
|
}
|
|
|
|
//
|
|
let result = -1;
|
|
|
|
//
|
|
let index = startsFrom;
|
|
const openSignStack = [];
|
|
while (index < content.length) {
|
|
//
|
|
const openCandidate = content.substring(index, index + openSymbol.length);
|
|
const closeCandidate = content.substring(index, index + closeSymbol.length);
|
|
|
|
//
|
|
if (openCandidate === openSymbol) {
|
|
openSignStack.push(index);
|
|
} else if (closeCandidate === closeSymbol) {
|
|
//
|
|
if (openSignStack.length > 0) {
|
|
openSignStack.pop();
|
|
}
|
|
|
|
//
|
|
if (openSignStack.length === 0) {
|
|
result = index;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
index++;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* find closed contents of specific sign in destination content ...
|
|
*
|
|
* @param {string} openSymbol open of area sign ...
|
|
* @param {string} closeSymbol close of area sign ...
|
|
* @param {string} content the destination content for parsing ...
|
|
* @param {number} startsFrom an start index of content for start parsing ...
|
|
* @returns
|
|
*/
|
|
function findClosedContent(
|
|
openSymbol = '',
|
|
closeSymbol = '',
|
|
content = '',
|
|
startsFrom = 0,
|
|
) {
|
|
//
|
|
let result = [{
|
|
start: -1,
|
|
end: -1,
|
|
content: ''
|
|
}];
|
|
result.pop();
|
|
|
|
//
|
|
// Validate Args ...
|
|
if (
|
|
!isValidArg(content) ||
|
|
!isValidArg(openSymbol) ||
|
|
!isValidArg(closeSymbol) ||
|
|
!content.includes(openSymbol) ||
|
|
!content.includes(closeSymbol) ||
|
|
openSymbol.length >= content.length ||
|
|
closeSymbol.length >= content.length
|
|
) {
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Normalize Start From ...
|
|
startsFrom = startsFrom < 0 || startsFrom >= content.length ? 0 : startsFrom;
|
|
|
|
//
|
|
let canContinue = true;
|
|
while (!!canContinue) {
|
|
//
|
|
let openSymbolIndex = content.indexOf(openSymbol, startsFrom);
|
|
if (openSymbolIndex < 0) {
|
|
//
|
|
canContinue = false;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
let closeSymbolIndex = findCloseIndex(
|
|
openSymbol,
|
|
closeSymbol,
|
|
content,
|
|
openSymbolIndex
|
|
);
|
|
if (closeSymbolIndex < 0) {
|
|
//
|
|
canContinue = false;
|
|
continue;
|
|
}
|
|
if (content.charAt(closeSymbolIndex) !== closeSymbol) {
|
|
closeSymbolIndex++;
|
|
}
|
|
|
|
//
|
|
// Prevent ${} string interpolations ...
|
|
if (
|
|
openSymbol === '{' &&
|
|
openSymbolIndex - 1 >= 0
|
|
) {
|
|
//
|
|
const prevChar = content.charAt(openSymbolIndex - 1);
|
|
if (prevChar === '$') {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
const item = content
|
|
.substring(openSymbolIndex, closeSymbolIndex + 1)
|
|
.trim();
|
|
startsFrom = closeSymbolIndex + 1;
|
|
result.push({
|
|
start: openSymbolIndex,
|
|
end: closeSymbolIndex,
|
|
content: item
|
|
});
|
|
}
|
|
|
|
//
|
|
if (!hasChildArray(result)) {
|
|
return result;
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* extract a collection of closed contents exists in a content ...
|
|
*
|
|
* @param symbols a collection of open and close contents ...
|
|
* @param {string} content a content for searching inside it ...
|
|
* @returns a collection of closed content ...
|
|
*/
|
|
function findClosedContents(
|
|
symbols = [
|
|
{
|
|
openSymbol: '',
|
|
closeSymbol: ''
|
|
}
|
|
],
|
|
content = ''
|
|
) {
|
|
//
|
|
let result = [{
|
|
start: -1,
|
|
end: -1,
|
|
content: ''
|
|
}];
|
|
result.pop();
|
|
|
|
//
|
|
if (
|
|
!isValidArg(content) ||
|
|
!hasChildArray(symbols)
|
|
) {
|
|
return [];
|
|
}
|
|
|
|
//
|
|
symbols.forEach(symbol => {
|
|
//
|
|
const closedContent = findClosedContent(
|
|
symbol.openSymbol,
|
|
symbol.closeSymbol,
|
|
content
|
|
);
|
|
|
|
//
|
|
if (hasChildArray(closedContent)) {
|
|
result.push(
|
|
...closedContent
|
|
);
|
|
}
|
|
});
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* find all string content closed items ...
|
|
*
|
|
* @param {string} content a content which going to search ...
|
|
* @returns a collection of index descriptors ...
|
|
*/
|
|
function findClosedStrings(content = '') {
|
|
//
|
|
// Validate Args ...
|
|
if (!isValidArg(content)) {
|
|
return [];
|
|
}
|
|
|
|
//
|
|
// there are 3 types of strings ...
|
|
// ' " and `
|
|
|
|
//
|
|
const token1 = '\'';
|
|
const token1Stack = [0];
|
|
token1Stack.pop();
|
|
|
|
//
|
|
const token2 = '"';
|
|
const token2Stack = [0];
|
|
token2Stack.pop();
|
|
|
|
//
|
|
const token3 = '`';
|
|
const token3Stack = [0];
|
|
token3Stack.pop();
|
|
|
|
//
|
|
const result = [
|
|
{
|
|
start: -1,
|
|
end: -1,
|
|
content: ''
|
|
}
|
|
];
|
|
result.pop();
|
|
|
|
//
|
|
for (let i = 0; i < content.length; i++) {
|
|
//
|
|
const currentChart = content.charAt(i);
|
|
|
|
//
|
|
//#region Token 1 ...
|
|
if (currentChart === token1) {
|
|
//
|
|
if (!hasChildArray(token1Stack)) {
|
|
token1Stack.push(i);
|
|
} else {
|
|
//
|
|
const start = token1Stack.pop();
|
|
const end = i + 1;
|
|
const pContent = content.substring(start, end);
|
|
result.push({
|
|
start,
|
|
end,
|
|
content: pContent
|
|
});
|
|
}
|
|
}
|
|
//#endregion
|
|
|
|
//
|
|
//#region Token 2 ...
|
|
if (currentChart === token2) {
|
|
//
|
|
if (!hasChildArray(token2Stack)) {
|
|
token2Stack.push(i);
|
|
} else {
|
|
//
|
|
const start = token2Stack.pop();
|
|
const end = i + 1;
|
|
const pContent = content.substring(start, end);
|
|
result.push({
|
|
start,
|
|
end,
|
|
content: pContent
|
|
});
|
|
}
|
|
}
|
|
//#endregion
|
|
|
|
//
|
|
//#region Token 3 ...
|
|
if (currentChart === token3) {
|
|
//
|
|
if (!hasChildArray(token3Stack)) {
|
|
token3Stack.push(i);
|
|
} else {
|
|
//
|
|
const start = token3Stack.pop();
|
|
const end = i + 1;
|
|
const pContent = content.substring(start, end);
|
|
result.push({
|
|
start,
|
|
end,
|
|
content: pContent
|
|
});
|
|
}
|
|
}
|
|
//#endregion
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* find container indexes from within indexes ...
|
|
*
|
|
* @param {...{start: number, end: number}} index the collection of indexes to find beiggers inside ...
|
|
* @returns
|
|
*/
|
|
function findContainerIndexes(...index) {
|
|
//
|
|
let result = [
|
|
{
|
|
start: -1,
|
|
end: -1,
|
|
content: ''
|
|
}
|
|
];
|
|
result.pop();
|
|
|
|
//
|
|
let concatedIndexes = [];
|
|
for (let idx of index) {
|
|
//
|
|
// Index Childs ...
|
|
for (let iix of idx) {
|
|
//
|
|
// Check idx is standard ...
|
|
if (
|
|
!iix ||
|
|
!iix.end ||
|
|
!iix.start ||
|
|
iix.start > iix.end
|
|
) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check index inside another index ...
|
|
concatedIndexes.push(iix);
|
|
}
|
|
}
|
|
|
|
//
|
|
if (!hasChildArray(concatedIndexes)) {
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (let ccIdx of concatedIndexes) {
|
|
//
|
|
const isInside = isIndexInside(ccIdx, ...concatedIndexes);
|
|
if (isInside) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
if (hasChildArray(result)) {
|
|
//
|
|
const isInsideResult = isIndexInside(ccIdx, ...result);
|
|
if (isInsideResult) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
result.push(ccIdx);
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* find all tokens indexes inside a content which they are not inside closed items ...
|
|
*
|
|
* @param {string|string[]} tokens the tokens which require to search ...
|
|
* @param {string} content the content which using to search ...
|
|
* @returns
|
|
*/
|
|
function findAllIndexesOutOfCloseds1(
|
|
tokens,
|
|
content,
|
|
) {
|
|
//
|
|
// Validate Args ...
|
|
if (!isValidArg(content)) {
|
|
return [];
|
|
}
|
|
|
|
//
|
|
// Extract token Indexes ...
|
|
const tokensIndexes = findAllIndexes(tokens, content);
|
|
if (!hasChildArray(tokensIndexes)) {
|
|
return [];
|
|
}
|
|
|
|
//
|
|
// Extract Closed Objects Indexes ...
|
|
const closedArrays = findClosedContent('[', ']', content);
|
|
const closedObjects = findClosedContent('{', '}', content);
|
|
const closedStrings = findClosedStrings(content);
|
|
if (
|
|
!hasChildArray(closedObjects) &&
|
|
!hasChildArray(closedArrays) &&
|
|
!hasChildArray(closedStrings)
|
|
) {
|
|
return tokensIndexes;
|
|
}
|
|
|
|
//
|
|
// find bigger sloded ...
|
|
let containerClosedItems = findContainerIndexes(
|
|
closedArrays,
|
|
closedObjects,
|
|
closedStrings
|
|
).filter(i => !isSurroundedString(i.content));
|
|
|
|
//
|
|
let result = [-1];
|
|
result.pop();
|
|
|
|
//
|
|
for (let tIdx of tokensIndexes) {
|
|
//
|
|
let canAdd = true;
|
|
for (let ccIndex of containerClosedItems) {
|
|
//
|
|
canAdd = isInsideIndex(tIdx, ccIndex);
|
|
if (canAdd) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
if (!canAdd) {
|
|
result.push(tIdx);
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* find all tokens indexes inside a content which they are not inside closed items ...
|
|
*
|
|
* @param {string|string[]} tokens the tokens which require to search ...
|
|
* @param {string} content the content which using to search ...
|
|
* @returns
|
|
*/
|
|
function findAllIndexesOutOfCloseds(
|
|
tokens,
|
|
content,
|
|
) {
|
|
//
|
|
// Define Result ...
|
|
let result = [-1];
|
|
result.pop();
|
|
|
|
//
|
|
// Validate Args ...
|
|
if (!isValidArg(content)) {
|
|
return [];
|
|
}
|
|
|
|
//
|
|
// Normalize Tokens ...
|
|
const normalizeTokens = toNormalArray(tokens);
|
|
if (!hasChildArray(normalizeTokens)) {
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Extract Closed Objects Indexes ...
|
|
let openSymbols = [
|
|
'{',
|
|
'[',
|
|
'\'',
|
|
'"',
|
|
'`'
|
|
];
|
|
let closeSymbols = [
|
|
'}',
|
|
']',
|
|
'\'',
|
|
'"',
|
|
'`'
|
|
];
|
|
let openStack = [];
|
|
let lastOpenSymbol = '';
|
|
let lastClosedIndex = -1;
|
|
for (let i = 0; i < content.length; i++) {
|
|
//
|
|
const prevChar = content.charAt(i - 1) || '';
|
|
const char = content.charAt(i);
|
|
const nextChar = content.charAt(i + 1) || '';
|
|
|
|
//
|
|
const isToken = normalizeTokens.includes(char);
|
|
const isOpenSymbol = openSymbols.includes(char);
|
|
const isCloseSymbol = closeSymbols.includes(char);
|
|
|
|
//
|
|
if (isOpenSymbol && lastOpenSymbol !== char) {
|
|
openStack.push(i);
|
|
lastOpenSymbol = char === '\'' || char === '"' || char === '`' ? char : lastOpenSymbol;
|
|
} else if (isCloseSymbol) {
|
|
//
|
|
lastOpenSymbol = char === '\'' || char === '"' || char === '`' ? '' : lastOpenSymbol;
|
|
let lastPop = openStack.pop();
|
|
if (openStack.length === 0) {
|
|
lastClosedIndex = lastPop;
|
|
}
|
|
} else if (isToken) {
|
|
if (
|
|
i > lastClosedIndex &&
|
|
openStack.length === 0
|
|
) {
|
|
result.push(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Module Exports ...
|
|
module.exports = {
|
|
//
|
|
endsWidth,
|
|
surroundBy,
|
|
isValidArg,
|
|
isValidArgs,
|
|
toNormalArray,
|
|
hasChildArray,
|
|
toStringExpression,
|
|
|
|
//
|
|
sliceContent,
|
|
clearContent,
|
|
clearSurround,
|
|
clearArraySurround,
|
|
clearObjectSurround,
|
|
|
|
//
|
|
isSurrounded,
|
|
isIndexInside,
|
|
isInsideIndex,
|
|
isKeyValueType,
|
|
isSurroundedArray,
|
|
isSurroundedString,
|
|
isSurroundedObject,
|
|
|
|
//
|
|
findNearest,
|
|
findAllIndexes,
|
|
findCloseIndex,
|
|
findNearestIndex,
|
|
findClosedContent,
|
|
findClosedStrings,
|
|
findClosedContents,
|
|
findAllIndexesOutOfCloseds,
|
|
|
|
//
|
|
} |