216 lines
4.7 KiB
JavaScript
216 lines
4.7 KiB
JavaScript
/*
|
|
* class for string iterations
|
|
* that is used by MarkdownParser.js
|
|
*/
|
|
|
|
export default class MString {
|
|
constructor(text, start) {
|
|
this.txt = text;
|
|
this.iter = start || 0;
|
|
}
|
|
|
|
done() {
|
|
return (this.iter >= this.txt.length);
|
|
}
|
|
|
|
moveForward() {
|
|
this.iter += 1;
|
|
return (this.iter < this.txt.length);
|
|
}
|
|
|
|
setIter(iter) {
|
|
this.iter = iter;
|
|
}
|
|
|
|
getChar() {
|
|
return this.txt[this.iter];
|
|
}
|
|
|
|
slice(start, end) {
|
|
return this.txt.slice(start, end || this.iter);
|
|
}
|
|
|
|
has(str) {
|
|
return this.txt.startsWith(str, this.iter);
|
|
}
|
|
|
|
move(cnt) {
|
|
this.iter += cnt;
|
|
return (this.iter < this.txt.length);
|
|
}
|
|
|
|
skipSpaces(skipNewlines = false) {
|
|
for (;this.iter < this.txt.length; this.iter += 1) {
|
|
const chr = this.txt[this.iter];
|
|
if (chr !== ' ' && chr !== '\t' && (!skipNewlines || chr !== '\n')) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
countRepeatingCharacters() {
|
|
const chr = this.getChar();
|
|
let newIter = this.iter + 1;
|
|
for (;newIter < this.txt.length && this.txt[newIter] === chr;
|
|
newIter += 1
|
|
);
|
|
return newIter - this.iter;
|
|
}
|
|
|
|
moveToNextLine() {
|
|
const lineEnd = this.txt.indexOf('\n', this.iter);
|
|
if (lineEnd === -1) {
|
|
this.iter = this.txt.length;
|
|
} else {
|
|
this.iter = lineEnd + 1;
|
|
}
|
|
}
|
|
|
|
getLine() {
|
|
const startLine = this.iter;
|
|
this.moveToNextLine();
|
|
return this.txt.slice(startLine, this.iter);
|
|
}
|
|
|
|
getIndent(tabWidth) {
|
|
let indent = 0;
|
|
while (this.iter < this.txt.length) {
|
|
const chr = this.getChar();
|
|
if (chr === '\t') {
|
|
indent += tabWidth;
|
|
} else if (chr === ' ') {
|
|
indent += 1;
|
|
} else {
|
|
break;
|
|
}
|
|
this.iter += 1;
|
|
}
|
|
return indent;
|
|
}
|
|
|
|
goToCharInLine(chr) {
|
|
let { iter } = this;
|
|
for (;
|
|
iter < this.txt.length && this.txt[iter] !== '\n'
|
|
&& this.txt[iter] !== chr;
|
|
iter += 1
|
|
);
|
|
if (this.txt[iter] === chr) {
|
|
this.iter = iter;
|
|
return iter;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static isWhiteSpace(chr) {
|
|
return (chr === ' ' || chr === '\t' || chr === '\n');
|
|
}
|
|
|
|
/*
|
|
* check if the current '[' is part of a [y](z) enclosure
|
|
* returns [y, z] if it is enclosure, null otherwise
|
|
* moves iter to last closing braked if it is enclosure
|
|
*/
|
|
checkIfEnclosure(zIsLink) {
|
|
const yStart = this.iter + 1;
|
|
|
|
let yEnd = yStart;
|
|
while (this.txt[yEnd] !== ']') {
|
|
const chr = this.txt[yEnd];
|
|
if (yEnd >= this.txt.length
|
|
|| chr === '\n'
|
|
|| chr === '['
|
|
|| chr === '('
|
|
) {
|
|
return null;
|
|
}
|
|
yEnd += 1;
|
|
}
|
|
|
|
let zStart = yEnd + 1;
|
|
if (this.txt[zStart] !== '(') {
|
|
return null;
|
|
}
|
|
zStart += 1;
|
|
|
|
let zEnd = zStart;
|
|
let z = null;
|
|
while (this.txt[zEnd] !== ')') {
|
|
const chr = this.txt[zEnd];
|
|
if (zEnd >= this.txt.length
|
|
|| chr === '\n'
|
|
|| chr == '['
|
|
|| chr == '('
|
|
) {
|
|
return null;
|
|
}
|
|
if (zIsLink && chr === ':') {
|
|
// set this.iter temporarily to be able to use thischeckIfLink
|
|
const oldIter = this.iter;
|
|
this.iter = zEnd;
|
|
z = this.checkIfLink();
|
|
zEnd = this.iter;
|
|
this.iter = oldIter;
|
|
if (z === null) {
|
|
return null;
|
|
}
|
|
continue;
|
|
}
|
|
zEnd += 1;
|
|
}
|
|
if (zEnd < zStart + 1 || ( !z && zIsLink )) {
|
|
return null;
|
|
}
|
|
|
|
if (!zIsLink) {
|
|
z = this.txt.slice(zStart, zEnd);
|
|
}
|
|
const y = this.txt.slice(yStart, yEnd);
|
|
|
|
this.iter = zEnd;
|
|
return [y, z];
|
|
}
|
|
|
|
/*
|
|
* Convoluted way to check if the current ':' is part of a link
|
|
* we do not check for a 'http' because we might support application links
|
|
* like tg://... or discord://..
|
|
* returns the link or false if there is none
|
|
* moves iter forward to after the link, if there's one
|
|
*/
|
|
checkIfLink() {
|
|
let cIter = this.iter;
|
|
if (!this.txt.startsWith('://', cIter) || cIter < 3) {
|
|
return null;
|
|
}
|
|
|
|
let linkStart = cIter - 1;
|
|
for (; linkStart >= 0
|
|
&& !MString.isWhiteSpace(this.txt[linkStart])
|
|
&& this.txt[linkStart] !== '('; linkStart -= 1);
|
|
linkStart += 1;
|
|
|
|
cIter += 3;
|
|
for (; cIter < this.txt.length
|
|
&& !MString.isWhiteSpace(this.txt[cIter])
|
|
&& this.txt[cIter] !== ')'; cIter += 1
|
|
);
|
|
if (cIter < this.iter + 4) {
|
|
return null;
|
|
}
|
|
|
|
/* special case where someone pasted a http link after a text
|
|
* without space in between
|
|
*/
|
|
let link = this.txt.slice(linkStart, cIter);
|
|
const httpOc = link.indexOf('http');
|
|
if (httpOc !== -1 && httpOc !== 0) {
|
|
linkStart += httpOc;
|
|
link = this.txt.slice(linkStart, cIter);
|
|
}
|
|
|
|
this.iter = cIter;
|
|
return link;
|
|
}
|
|
}
|