diff --git a/src/core/MString.js b/src/core/MString.js index 9af2869..18a27a5 100644 --- a/src/core/MString.js +++ b/src/core/MString.js @@ -106,10 +106,75 @@ export default class MString { 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 telegram://... or discord://.. + * like tg://... or discord://.. * returns the link or false if there is none * moves iter forward to after the link, if there's one */ @@ -126,26 +191,14 @@ export default class MString { linkStart += 1; cIter += 3; - /* just some most basic test */ - let dots = 0; - let slashes = 0; for (; cIter < this.txt.length && !MString.isWhiteSpace(this.txt[cIter]) && this.txt[cIter] !== ')'; cIter += 1 - ) { - if (this.txt[cIter] === '.') { - if (slashes !== 0) { - return null; - } - dots += 1; - } else if (this.txt[cIter] === '/') { - slashes += 1; - } - } - if (!dots || (!slashes && this.txt[cIter - 1] === '.')) { + ); + if (cIter < this.iter + 4) { return null; } - + /* special case where someone pasted a http link after a text * without space in between */ diff --git a/src/core/MarkdownParser.js b/src/core/MarkdownParser.js index 272f72e..16f86f5 100644 --- a/src/core/MarkdownParser.js +++ b/src/core/MarkdownParser.js @@ -75,15 +75,45 @@ function parseMParagraph(text, opts, breakChar) { * pure link */ const link = text.checkIfLink(); - if (link) { + if (link !== null) { const startLink = text.iter - link.length; if (pStart < startLink) { pArray.push(text.slice(pStart, startLink)); } - pArray.push(['l', link, link]); + pArray.push(['l', null, link]); pStart = text.iter; continue; } + } else if (chr === '[') { + /* + * x[y](z) enclosure + */ + let oldPos = text.iter; + let x = null; + if (text.iter > 0) { + text.move(-1); + x = text.getChar(); + text.setIter(oldPos); + } + /* + * x decides what element it is + * defaults to ordinary link + */ + let tag = 'l' + let zIsLink = true; + if (x === '!') { + tag = 'img'; + oldPos -= 1; + } + + const encArr = text.checkIfEnclosure(zIsLink); + if (encArr !== null) { + if (pStart < oldPos) { + pArray.push(text.slice(pStart, oldPos)); + } + pArray.push([tag, encArr[0], encArr[1]]); + pStart = text.iter + 1; + } } text.moveForward(); diff --git a/utils/markdown-test/Markdown.jsx b/utils/markdown-test/Markdown.jsx index 3bef674..839be6f 100644 --- a/utils/markdown-test/Markdown.jsx +++ b/utils/markdown-test/Markdown.jsx @@ -3,6 +3,34 @@ */ import React from 'react'; +/* + * gets a descriptive text of the domain of the link + * Example: + * https://www.youtube.com/watch?v=G8APgeFfkAk returns 'youtube' + * http://www.google.at returns 'google.at' + * (www. and .com are split) + */ +function getLinkDesc(link) { + let domainStart = link.indexOf('://') + 3; + if (domainStart < 3) { + domainStart = 0; + } + if (link.startsWith('www.', domainStart)) { + domainStart += 4; + } + let domainEnd = link.indexOf('/', domainStart); + if (domainEnd === -1) { + domainEnd = link.length; + } + if (link.endsWith('.com', domainEnd)) { + domainEnd -= 4; + } + if (domainEnd <= domainStart) { + return link; + } + return link.slice(domainStart, domainEnd); +} + const MarkdownParagraph = ({ pArray }) => pArray.map((part) => { if (!Array.isArray(part)) { return part; @@ -35,19 +63,32 @@ const MarkdownParagraph = ({ pArray }) => pArray.map((part) => { ); - case 'l': + case 'l': { + let title = getLinkDesc(part[2]); + if (part[1]) { + title += ` | ${part[1]}`; + } return ( - {part[1]} + {title} ); + } + case 'img': { + let title = getLinkDesc(part[2]); + if (part[1]) { + title += ` | ${part[1]}`; + } + return ( + {title} + ); + } default: return type; } }); -const Markdown = ({ mdArray }) => { - return mdArray.map((part) => { +const Markdown = ({ mdArray }) => mdArray.map((part) => { const type = part[0]; switch (type) { /* Heading */ @@ -127,7 +168,7 @@ const Markdown = ({ mdArray }) => { default: return part[0]; } -})}; +}); const MarkdownArticle = ({ mdArray }) => (
diff --git a/utils/markdown-test/README.md b/utils/markdown-test/README.md index 14b01cb..138d37f 100644 --- a/utils/markdown-test/README.md +++ b/utils/markdown-test/README.md @@ -1,3 +1,5 @@ # Markdown parser testing Code to test markdown parsing, run `npx webpack` to build and open index.html + +No npm install in this folder neccessary, it takes the packages from the parent project diff --git a/utils/markdown-test/webpack.config.js b/utils/markdown-test/webpack.config.js index ac2e291..0092ae7 100644 --- a/utils/markdown-test/webpack.config.js +++ b/utils/markdown-test/webpack.config.js @@ -22,7 +22,7 @@ var babelPlugins = [ module.exports = { name: 'script', target: 'web', - mode: 'production', + mode: 'development', entry: [ path.resolve(__dirname, './mdtest.js') ],