diff options
Diffstat (limited to 'src/services')
| -rw-r--r-- | src/services/tiny_post_html_processor/tiny_post_html_processor.service.js | 84 |
1 files changed, 84 insertions, 0 deletions
diff --git a/src/services/tiny_post_html_processor/tiny_post_html_processor.service.js b/src/services/tiny_post_html_processor/tiny_post_html_processor.service.js new file mode 100644 index 00000000..c9ff81e1 --- /dev/null +++ b/src/services/tiny_post_html_processor/tiny_post_html_processor.service.js @@ -0,0 +1,84 @@ +/** + * This is a tiny purpose-built HTML parser/processor. This basically detects any type of visual newline and + * allows it to be processed, useful for greentexting, mostly + * + * @param {Object} input - input data + * @param {(string) => string} processor - function that will be called on every line + * @return {string} processed html + */ +export const processHtml = (html, processor) => { + const handledTags = new Set(['p', 'br', 'div']) + const openCloseTags = new Set(['p', 'div']) + const tagRegex = /(?:<\/(\w+)>|<(\w+)\s?[^/]*?\/?>)/gi + + let buffer = '' // Current output buffer + const level = [] // How deep we are in tags and which tags were there + let textBuffer = '' // Current line content + let tagBuffer = null // Current tag buffer, if null = we are not currently reading a tag + + // Extracts tagname from tag, i.e. <span a="b"> => span + const getTagName = (tag) => { + // eslint-disable-next-line no-unused-vars + const result = tagRegex.exec(tag) + return result && (result[1] || result[2]) + } + + const flush = () => { // Processes current line buffer, adds it to output buffer and clears line buffer + buffer += processor(textBuffer) + textBuffer = '' + } + + const handleBr = (tag) => { // handles single newlines/linebreaks + flush() + buffer += tag + } + + const handleOpen = (tag) => { // handles opening tags + flush() + buffer += tag + level.push(tag) + } + + const handleClose = (tag) => { // handles closing tags + flush() + buffer += tag + if (level[level.length - 1] === tag) { + level.pop() + } + } + + for (let i = 0; i < html.length; i++) { + const char = html[i] + if (char === '<' && tagBuffer !== null) { + tagBuffer = char + } else if (char !== '>' && tagBuffer !== null) { + tagBuffer += char + } else if (char === '>' && tagBuffer !== null) { + tagBuffer += char + const tagName = getTagName(tagBuffer) + if (handledTags.has(tagName)) { + if (tagName === 'br') { + handleBr(tagBuffer) + } + if (openCloseTags.has(tagBuffer)) { + if (tagBuffer[1] === '/') { + handleClose(tagBuffer) + } else { + handleOpen(tagBuffer) + } + } + } else { + textBuffer += tagBuffer + } + tagBuffer = null + } else if (char === '\n') { + handleBr(char) + } else { + textBuffer += char + } + } + + flush() + + return buffer +} |
