From 663ebc5e2dc1604a1a23c6f28c50b06a2db3f6ad Mon Sep 17 00:00:00 2001 From: hartleyk1 Date: Sat, 9 Mar 2024 20:54:01 -0500 Subject: [PATCH 01/10] Adding row column --- README.md | 2 +- assets/bundled/bbcode-parser.min.js | 16 ++++++ .../lib/discourse-markdown/bbcode-plugin.js | 10 ++++ assets/stylesheets/common/rowcolumn.scss | 55 ++++++++++++++++++ bbcode-src/preset.js | 4 +- bbcode-src/tags/progress.js | 57 +++++++++---------- bbcode-src/tags/rowcolumn.js | 16 ++++++ 7 files changed, 129 insertions(+), 31 deletions(-) create mode 100644 assets/stylesheets/common/rowcolumn.scss create mode 100644 bbcode-src/tags/rowcolumn.js diff --git a/README.md b/README.md index e899dd1..ff90769 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ The goal of this repo and plugin is to provide users with the BBCode suite that - [x] Scroll Box - [ ] Div Box - [ ] Anchors -- [ ] Rows & Columns +- [x] Rows & Columns ## Media & Embeds diff --git a/assets/bundled/bbcode-parser.min.js b/assets/bundled/bbcode-parser.min.js index c78e3e6..0cb6619 100644 --- a/assets/bundled/bbcode-parser.min.js +++ b/assets/bundled/bbcode-parser.min.js @@ -763,6 +763,21 @@ return fontSize; } + + /** + * @file Adds [row][column] to bbcode + * @example Adds [row][column][/column][/row] + */ + const rowcolumn = { + row: (node) => toNode("div", { class: "bb-row" }, node.content), + column: (node) => { + const columnAttrs = preprocessAttr(node.attrs)._default || ""; + const columnStyle = columnAttrs.startsWith("span") ? `column-width-${columnAttrs}` : `column-width-span${columnAttrs}`; + console.log({ class: `bb-column ${columnStyle}` }); + return toNode("div", { class: `bb-column` }, node.content); + } + } + const size = (node) => { const input = preprocessAttr(node.attrs)._default; const fontSize = parseFontSize(input); @@ -884,6 +899,7 @@ pindent, plain, progress, + ...rowcolumn, thinprogress, savenl, sh, diff --git a/assets/javascripts/lib/discourse-markdown/bbcode-plugin.js b/assets/javascripts/lib/discourse-markdown/bbcode-plugin.js index f55e7ec..f546ec8 100644 --- a/assets/javascripts/lib/discourse-markdown/bbcode-plugin.js +++ b/assets/javascripts/lib/discourse-markdown/bbcode-plugin.js @@ -106,6 +106,15 @@ export function setup(helper) { "div.bb-border", "div.bb-center", "div.bb-check", + "div.bb-column", + "div.column-width-span1", + "div.column-width-span2", + "div.column-width-span3", + "div.column-width-span4", + "div.column-width-span5", + "div.column-width-span6", + "div.column-width-span7", + "div.column-width-span8", "div.bb-email", "div.bb-email-address", "div.bb-email-button", @@ -131,6 +140,7 @@ export function setup(helper) { "div.bb-progress-thin", "div.bb-ooc", "div.bb-right", + "div.bb-row", "div.bb-scroll", "div.bb-side", "div.bb-spoiler-content", diff --git a/assets/stylesheets/common/rowcolumn.scss b/assets/stylesheets/common/rowcolumn.scss new file mode 100644 index 0000000..1d5f0de --- /dev/null +++ b/assets/stylesheets/common/rowcolumn.scss @@ -0,0 +1,55 @@ +.bb-row { + width: 100%; + clear: both; + overflow: hidden; + margin: 0; + + .bb-column { + margin: 0 0.5%; + padding: 0 0.5%; + float: left; + } + + .column-width-span1 { + width: 10.5%; + } /*12.5*/ + .column-width-span2 { + width: 23%; + } /*25*/ + .column-width-span3 { + width: 35.5%; + } /*37.5*/ + .column-width-span4 { + width: 48%; + } /*50*/ + .column-width-span5 { + width: 60.5%; + } /*62.5*/ + .column-width-span6 { + width: 73%; + } /*75*/ + .column-width-span7 { + width: 85.5%; + } /*87.5*/ + .column-width-span8 { + width: 98%; + } /*100*/ +} +@media screen and (max-width: 900px) { + .bb-row .column-width-span1, + .bb-row .column-width-span2, + .bb-row .column-width-span3, + .bb-row .column-width-span4, + .bb-row .column-width-span5, + .bb-row .column-width-span6, + .bb-row .column-width-span7, + .bb-row .column-width-span8 { + width: 100%; + } /*12.5*/ + + .bb-row .bb-column { + margin: 0; + padding: 0; + float: none; + } +} diff --git a/bbcode-src/preset.js b/bbcode-src/preset.js index 866c82c..1c97d1f 100644 --- a/bbcode-src/preset.js +++ b/bbcode-src/preset.js @@ -3,7 +3,7 @@ import { alignment } from "./tags/alignment"; import { bg } from "./tags/background"; import { blockquote } from "./tags/blockquote"; import { border } from "./tags/border"; -import { centerblock } from "/tags/centerblock"; +import { centerblock } from "./tags/centerblock"; import { check } from "./tags/check"; import { code, icode, savenl } from "./tags/code"; import { color } from "./tags/color"; @@ -23,6 +23,7 @@ import { ooc } from "./tags/ooc"; import { pindent } from "./tags/pindent"; import { plain } from "./tags/plain"; import { progress } from "./tags/progress"; +import { rowcolumn } from './tags/rowcolumn'; import { thinprogress } from "./tags/thinprogress"; import { scroll } from "./tags/scroll"; import { side } from "./tags/side"; @@ -65,6 +66,7 @@ const tags = { pindent, plain, progress, + ...rowcolumn, thinprogress, savenl, sh, diff --git a/bbcode-src/tags/progress.js b/bbcode-src/tags/progress.js index 38f99a2..186ce9f 100644 --- a/bbcode-src/tags/progress.js +++ b/bbcode-src/tags/progress.js @@ -1,38 +1,37 @@ - -import { preprocessAttr, toNode } from "../utils/common"; +import { preprocessAttr } from "../utils/common"; /** * @file Adds [progress] to bbcode * @exmaple [progress=percentageInt]content[/progress] */ export const progress = (node) => { - const percentageInt = preprocessAttr(node.attrs)._default; - return { + const percentageInt = preprocessAttr(node.attrs)._default; + return { + tag: "div", + attrs: { + class: "bb-progress", + }, + content: [ + { + tag: "div", + attrs: { + class: "bb-progress-text", + }, + content: node.content, + }, + { + tag: "div", + attrs: { + class: "bb-progress-bar", + style: `width: calc(${percentageInt}% - 6px)`, + }, + }, + { tag: "div", attrs: { - class: "bb-progress", + class: "bb-progress-bar-other", }, - content: [ - { - tag: "div", - attrs: { - class: "bb-progress-text" - }, - content: node.content - }, - { - tag: "div", - attrs: { - class: "bb-progress-bar", - style: `width: calc(${percentageInt}% - 6px)` - } - }, - { - tag: "div", - attrs: { - class: "bb-progress-bar-other" - } - } - ], - }; -} \ No newline at end of file + }, + ], + }; +}; diff --git a/bbcode-src/tags/rowcolumn.js b/bbcode-src/tags/rowcolumn.js new file mode 100644 index 0000000..788c5e0 --- /dev/null +++ b/bbcode-src/tags/rowcolumn.js @@ -0,0 +1,16 @@ +import { preprocessAttr, toNode } from "../utils/common"; + +/** + * @file Adds [row][column] to bbcode + * @example Adds [row][column][/column][/row] + */ +export const rowcolumn = { + row: (node) => toNode("div", { class: "bb-row" }, node.content), + column: (node) => { + const columnAttrs = preprocessAttr(node.attrs)._default || ""; + const columnStyle = columnAttrs.startsWith("span") + ? `bb-column column-width-${columnAttrs}` + : `bb-column column-width-span${columnAttrs}`; + return toNode("div", { class: `bb-column ${columnStyle}` }, node.content); + }, +}; From 4b67933647c2052919e5eeefd45db43002e361e3 Mon Sep 17 00:00:00 2001 From: hartleyk1 Date: Sat, 13 Apr 2024 19:08:26 -0400 Subject: [PATCH 02/10] Workaround for multi class --- assets/bundled/bbcode-parser.min.js | 5 +- assets/stylesheets/common/index.scss | 1 + assets/stylesheets/common/rowcolumn.scss | 79 ++++++++++++------------ bbcode-src/tags/rowcolumn.js | 10 ++- 4 files changed, 47 insertions(+), 48 deletions(-) diff --git a/assets/bundled/bbcode-parser.min.js b/assets/bundled/bbcode-parser.min.js index f6b819e..00dbc1c 100644 --- a/assets/bundled/bbcode-parser.min.js +++ b/assets/bundled/bbcode-parser.min.js @@ -864,10 +864,9 @@ const rowcolumn = { row: (node) => toNode("div", { class: "bb-row" }, node.content), column: (node) => { - const columnAttrs = preprocessAttr(node.attrs)._default || ""; + const columnAttrs = preprocessAttr(node.attrs)._default || "1"; const columnStyle = columnAttrs.startsWith("span") ? `column-width-${columnAttrs}` : `column-width-span${columnAttrs}`; - console.log({ class: `bb-column ${columnStyle}` }); - return toNode("div", { class: `bb-column` }, node.content); + return toNode("div", { class: `bb-column`, "data-span": columnStyle }, node.content); } } diff --git a/assets/stylesheets/common/index.scss b/assets/stylesheets/common/index.scss index baa6f54..14cae1d 100644 --- a/assets/stylesheets/common/index.scss +++ b/assets/stylesheets/common/index.scss @@ -18,6 +18,7 @@ @import "pindent.scss"; @import "print.scss"; @import "progress.scss"; +@import "rowcolumn.scss"; @import "scroll.scss"; @import "side.scss"; @import "size.scss"; diff --git a/assets/stylesheets/common/rowcolumn.scss b/assets/stylesheets/common/rowcolumn.scss index 1d5f0de..9d307c2 100644 --- a/assets/stylesheets/common/rowcolumn.scss +++ b/assets/stylesheets/common/rowcolumn.scss @@ -3,47 +3,48 @@ clear: both; overflow: hidden; margin: 0; +} - .bb-column { - margin: 0 0.5%; - padding: 0 0.5%; - float: left; - } - - .column-width-span1 { - width: 10.5%; - } /*12.5*/ - .column-width-span2 { - width: 23%; - } /*25*/ - .column-width-span3 { - width: 35.5%; - } /*37.5*/ - .column-width-span4 { - width: 48%; - } /*50*/ - .column-width-span5 { - width: 60.5%; - } /*62.5*/ - .column-width-span6 { - width: 73%; - } /*75*/ - .column-width-span7 { - width: 85.5%; - } /*87.5*/ - .column-width-span8 { - width: 98%; - } /*100*/ +.bb-row .bb-column { + margin: 0 0.5%; + padding: 0 0.5%; + float: left; } + +.bb-row [data-span="column-width-span1"] { + width: 10.5%; +} /*12.5*/ +.bb-row [data-span="column-width-span2"] { + width: 23%; +} /*25*/ +.bb-row [data-span="column-width-span3"] { + width: 35.5%; +} /*37.5*/ +.bb-row [data-span="column-width-span4"] { + width: 48%; +} /*50*/ +.bb-row [data-span="column-width-span5"] { + width: 60.5%; +} /*62.5*/ +.bb-row [data-span="column-width-span6"] { + width: 73%; +} /*75*/ +.bb-row [data-span="column-width-span7"] { + width: 85.5%; +} /*87.5*/ +.bb-row [data-span="column-width-span8"] { + width: 98%; +} /*100*/ + @media screen and (max-width: 900px) { - .bb-row .column-width-span1, - .bb-row .column-width-span2, - .bb-row .column-width-span3, - .bb-row .column-width-span4, - .bb-row .column-width-span5, - .bb-row .column-width-span6, - .bb-row .column-width-span7, - .bb-row .column-width-span8 { + .bb-row [data-span="column-width-span1"], + .bb-row [data-span="column-width-span2"], + .bb-row [data-span="column-width-span3"], + .bb-row [data-span="column-width-span4"], + .bb-row [data-span="column-width-span5"], + .bb-row [data-span="column-width-span6"], + .bb-row [data-span="column-width-span7"], + .bb-row [data-span="column-width-span8"] { width: 100%; } /*12.5*/ @@ -52,4 +53,4 @@ padding: 0; float: none; } -} +} \ No newline at end of file diff --git a/bbcode-src/tags/rowcolumn.js b/bbcode-src/tags/rowcolumn.js index 788c5e0..ff60452 100644 --- a/bbcode-src/tags/rowcolumn.js +++ b/bbcode-src/tags/rowcolumn.js @@ -7,10 +7,8 @@ import { preprocessAttr, toNode } from "../utils/common"; export const rowcolumn = { row: (node) => toNode("div", { class: "bb-row" }, node.content), column: (node) => { - const columnAttrs = preprocessAttr(node.attrs)._default || ""; - const columnStyle = columnAttrs.startsWith("span") - ? `bb-column column-width-${columnAttrs}` - : `bb-column column-width-span${columnAttrs}`; - return toNode("div", { class: `bb-column ${columnStyle}` }, node.content); - }, + const columnAttrs = preprocessAttr(node.attrs)._default || "1"; + const columnStyle = columnAttrs.startsWith("span") ? `column-width-${columnAttrs}` : `column-width-span${columnAttrs}`; + return toNode("div", { class: `bb-column`, "data-span": columnStyle }, node.content); + } }; From 5b65e3f7e9a23eed789ffd975089e53a71e375ab Mon Sep 17 00:00:00 2001 From: hartleyk1 Date: Sat, 13 Apr 2024 19:49:12 -0400 Subject: [PATCH 03/10] Adjustments --- assets/stylesheets/common/rowcolumn.scss | 51 ++++++++++++------------ bbcode-src/tags/rowcolumn.js | 8 ++-- 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/assets/stylesheets/common/rowcolumn.scss b/assets/stylesheets/common/rowcolumn.scss index 9d307c2..a10c454 100644 --- a/assets/stylesheets/common/rowcolumn.scss +++ b/assets/stylesheets/common/rowcolumn.scss @@ -3,6 +3,30 @@ clear: both; overflow: hidden; margin: 0; + &[data-span="column-width-span1"] { + width: 10.5%; + } /*12.5*/ + &[data-span="column-width-span2"] { + width: 23%; + } /*25*/ + &[data-span="column-width-span3"] { + width: 35.5%; + } /*37.5*/ + &[data-span="column-width-span4"] { + width: 48%; + } /*50*/ + &[data-span="column-width-span5"] { + width: 60.5%; + } /*62.5*/ + &[data-span="column-width-span6"] { + width: 73%; + } /*75*/ + &[data-span="column-width-span7"] { + width: 85.5%; + } /*87.5*/ + &[data-span="column-width-span8"] { + width: 98%; + } /*100*/ } .bb-row .bb-column { @@ -11,31 +35,6 @@ float: left; } -.bb-row [data-span="column-width-span1"] { - width: 10.5%; -} /*12.5*/ -.bb-row [data-span="column-width-span2"] { - width: 23%; -} /*25*/ -.bb-row [data-span="column-width-span3"] { - width: 35.5%; -} /*37.5*/ -.bb-row [data-span="column-width-span4"] { - width: 48%; -} /*50*/ -.bb-row [data-span="column-width-span5"] { - width: 60.5%; -} /*62.5*/ -.bb-row [data-span="column-width-span6"] { - width: 73%; -} /*75*/ -.bb-row [data-span="column-width-span7"] { - width: 85.5%; -} /*87.5*/ -.bb-row [data-span="column-width-span8"] { - width: 98%; -} /*100*/ - @media screen and (max-width: 900px) { .bb-row [data-span="column-width-span1"], .bb-row [data-span="column-width-span2"], @@ -53,4 +52,4 @@ padding: 0; float: none; } -} \ No newline at end of file +} diff --git a/bbcode-src/tags/rowcolumn.js b/bbcode-src/tags/rowcolumn.js index ff60452..7ab6df0 100644 --- a/bbcode-src/tags/rowcolumn.js +++ b/bbcode-src/tags/rowcolumn.js @@ -8,7 +8,9 @@ export const rowcolumn = { row: (node) => toNode("div", { class: "bb-row" }, node.content), column: (node) => { const columnAttrs = preprocessAttr(node.attrs)._default || "1"; - const columnStyle = columnAttrs.startsWith("span") ? `column-width-${columnAttrs}` : `column-width-span${columnAttrs}`; - return toNode("div", { class: `bb-column`, "data-span": columnStyle }, node.content); - } + const columnStyle = columnAttrs.startsWith("span") + ? `column-width-${columnAttrs}` + : `column-width-span${columnAttrs}`; + return toNode("div", { class: `bb-column`, "data-span": columnStyle }, node.content); + }, }; From f89c64e7a069f87920cfeea27e5a7c8fb3ade0ff Mon Sep 17 00:00:00 2001 From: hartleyk1 Date: Sat, 13 Apr 2024 19:50:46 -0400 Subject: [PATCH 04/10] Re-format again --- assets/stylesheets/common/rowcolumn.scss | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/assets/stylesheets/common/rowcolumn.scss b/assets/stylesheets/common/rowcolumn.scss index a10c454..e12f029 100644 --- a/assets/stylesheets/common/rowcolumn.scss +++ b/assets/stylesheets/common/rowcolumn.scss @@ -3,28 +3,28 @@ clear: both; overflow: hidden; margin: 0; - &[data-span="column-width-span1"] { + &[data-span="column-width-span1"] { width: 10.5%; } /*12.5*/ - &[data-span="column-width-span2"] { + &[data-span="column-width-span2"] { width: 23%; } /*25*/ - &[data-span="column-width-span3"] { + &[data-span="column-width-span3"] { width: 35.5%; } /*37.5*/ - &[data-span="column-width-span4"] { + &[data-span="column-width-span4"] { width: 48%; } /*50*/ - &[data-span="column-width-span5"] { + &[data-span="column-width-span5"] { width: 60.5%; } /*62.5*/ - &[data-span="column-width-span6"] { + &[data-span="column-width-span6"] { width: 73%; } /*75*/ - &[data-span="column-width-span7"] { + &[data-span="column-width-span7"] { width: 85.5%; } /*87.5*/ - &[data-span="column-width-span8"] { + &[data-span="column-width-span8"] { width: 98%; } /*100*/ } From c115d08e67be97d652495f2fdc727f2f672010d8 Mon Sep 17 00:00:00 2001 From: hartleyk1 Date: Wed, 17 Apr 2024 17:17:41 -0400 Subject: [PATCH 05/10] Set default to span 8 --- assets/bundled/bbcode-parser.min.js | 2 +- bbcode-src/tags/rowcolumn.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/bundled/bbcode-parser.min.js b/assets/bundled/bbcode-parser.min.js index 00dbc1c..7425061 100644 --- a/assets/bundled/bbcode-parser.min.js +++ b/assets/bundled/bbcode-parser.min.js @@ -864,7 +864,7 @@ const rowcolumn = { row: (node) => toNode("div", { class: "bb-row" }, node.content), column: (node) => { - const columnAttrs = preprocessAttr(node.attrs)._default || "1"; + const columnAttrs = preprocessAttr(node.attrs)._default || "8"; const columnStyle = columnAttrs.startsWith("span") ? `column-width-${columnAttrs}` : `column-width-span${columnAttrs}`; return toNode("div", { class: `bb-column`, "data-span": columnStyle }, node.content); } diff --git a/bbcode-src/tags/rowcolumn.js b/bbcode-src/tags/rowcolumn.js index 7ab6df0..46dbeab 100644 --- a/bbcode-src/tags/rowcolumn.js +++ b/bbcode-src/tags/rowcolumn.js @@ -7,7 +7,7 @@ import { preprocessAttr, toNode } from "../utils/common"; export const rowcolumn = { row: (node) => toNode("div", { class: "bb-row" }, node.content), column: (node) => { - const columnAttrs = preprocessAttr(node.attrs)._default || "1"; + const columnAttrs = preprocessAttr(node.attrs)._default || "8"; const columnStyle = columnAttrs.startsWith("span") ? `column-width-${columnAttrs}` : `column-width-span${columnAttrs}`; From 0c9f60aa2510694c442f54733ff99a066be06197 Mon Sep 17 00:00:00 2001 From: hartleyk1 Date: Thu, 25 Apr 2024 17:47:07 -0400 Subject: [PATCH 06/10] Unused column-width-span whitelists removed --- .../javascripts/lib/discourse-markdown/bbcode-plugin.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/assets/javascripts/lib/discourse-markdown/bbcode-plugin.js b/assets/javascripts/lib/discourse-markdown/bbcode-plugin.js index 8ca535f..2ab67ee 100644 --- a/assets/javascripts/lib/discourse-markdown/bbcode-plugin.js +++ b/assets/javascripts/lib/discourse-markdown/bbcode-plugin.js @@ -111,14 +111,6 @@ export function setup(helper) { "div.bb-center", "div.bb-check", "div.bb-column", - "div.column-width-span1", - "div.column-width-span2", - "div.column-width-span3", - "div.column-width-span4", - "div.column-width-span5", - "div.column-width-span6", - "div.column-width-span7", - "div.column-width-span8", "div.bb-email", "div.bb-email-address", "div.bb-email-button", From 2aa2b2f34a51f1c0c005cef67c1a18aa344058e9 Mon Sep 17 00:00:00 2001 From: blythechan Date: Wed, 26 Jun 2024 18:37:09 -0400 Subject: [PATCH 07/10] Adjusted structure of scss, certain widths to match closer to current RPN row column, and removed overflow on rows. bbcode-parser.min is temporarily unminified. --- assets/bundled/bbcode-parser.min.js | 2823 +++++++++++++++++++++- assets/stylesheets/common/rowcolumn.scss | 85 +- 2 files changed, 2866 insertions(+), 42 deletions(-) diff --git a/assets/bundled/bbcode-parser.min.js b/assets/bundled/bbcode-parser.min.js index 815c24c..f5c3d7e 100644 --- a/assets/bundled/bbcode-parser.min.js +++ b/assets/bundled/bbcode-parser.min.js @@ -1,3 +1,2820 @@ -/* Source code in bbcode-src */ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).bbcodeParser={})}(this,(function(t){"use strict";const e=t=>"object"==typeof t&&!!t.tag;function n(t,n,s,r){n.walk((n=>e(n)&&t[n.tag]?t[n.tag](n,s,r):n))}const s=(t,e,n=[])=>({tag:t,attrs:e,content:n,gen:!0}),r=t=>{const e=Object.keys(t).join(" "),n=Object.values(t).join(" ");return e===n?{_default:n}:t},o=t=>{if(!t.attrs)return`[${t.tag}]`;const e=r(t.attrs);return e._default?`[${t.tag}=${e._default}]`:t.toTagStart()},i=(t,e,n)=>{const s=t.substring(n||0).search(e);return s>=0?s+(n||0):s},a="\x3c!-- bbcode injected newlines --\x3e\n\n",c="\n\n\x3c!-- bbcode pre injected newlines --\x3e",l="\x3c!-- bbcode injected newlines --\x3e",u=new RegExp(`^${/(http|ftp|https|upload):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])/.source}|${/\!?\[.*\]\((http|ftp|https|upload):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])\)/.source}$`),d=/((\n|^)(?```+|~~~+)(?.*\n))|(?\[(?i?code|plain)(=.*)?\])|(?(?`{1,2})(.*)(?\k))/im,g=/^(\|[^\n]+\|\r?\n)((?:\| ?:?[-]+:? ?)+\|)(\n(?:\|[^\n]+\|\r?\n?)*)?$/m;function b(){let t=(new Date).getTime();return window.performance&&"function"==typeof window.performance.now&&(t+=performance.now()),"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,(function(e){const n=(t+16*Math.random())%16|0;return t=Math.floor(t/16),("x"===e?n:3&n|8).toString(16)}))}const p={left:t=>s("div",{class:"bb-left"},t.content),center:t=>s("div",{class:"bb-center"},t.content),right:t=>s("div",{class:"bb-right"},t.content)},h={a:t=>{const e=r(t.attrs)._default||"";return s("a",{id:`user-anchor-${e.trim()}`,name:`user-anchor-${e.trim()}`},t.content)},goto:t=>{const e=r(t.attrs)._default||"";s("a",{href:`#user-anchor-${e.trim()}`},t.content)}},f=["arial","book antiqua","courier new","georgia","tahoma","times new roman","trebuchet ms","verdana"],m={thin:"100",extralight:"200",light:"300",regular:"400",medium:"500",semibold:"600",bold:"700",extrabold:"800",black:"900"},v=["ital","opsz","slnt","wdth","wght"],y=/(?[a-zA-Z]*)?\s?(?[0-9]*)?\s?(?italic)?/;const w=s("div",{class:"bb-email-header"},""),x=s("div",{class:"bb-email-footer"},s("div",{class:"bb-email-button"},""));const $=["me","them","right","left"],k={textmessage:t=>{const e=r(t.attrs)._default||"Recipient",n=e&&""!==e.trim()?e:"Recipient";return s("div",{class:"bb-textmessage"},[s("div",{class:"bb-textmessage-name"},n),s("div",{class:"bb-textmessage-overflow"},[s("div",{class:"bb-textmessage-content"},t.content)])])},message:t=>{let e=r(t.attrs)._default.toLowerCase();$.includes(e)&&"right"!==e||(e="me"),"left"===e&&(e="them");return s("div",{class:"me"===e?"bb-message-me":"bb-message-them"},[s("div",{class:"bb-message-content"},t.content)])}},T="\n",A="\t",j="=",_='"',C=" ",L="[",S="]",N="/",O="\\",E=t=>"object"==typeof t&&!!t.tag,W=t=>"string"==typeof t,I=(t,e,n)=>Object.keys(t).reduce(e,n),U=t=>E(t)?t.content.reduce(((t,e)=>t+U(e)),0):W(t)?t.length:0,V=t=>t.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'").replace(/(javascript|data|vbscript):/gi,"$1%3A"),D=(t,e)=>{const n=typeof e,s={boolean:()=>e?`${t}`:"",number:()=>`${t}="${e}"`,string:()=>`${t}="${V(e)}"`,object:()=>`${t}="${V(JSON.stringify(e))}"`};return s[n]?s[n]():""},G=t=>null==t?"":I(t,((e,n)=>[...e,D(n,t[n])]),[""]).join(" "),z=(t,e)=>{const n=I(s=e,((t,e)=>s[e]===e?s[e]:null),null);var s;if(n){const s=D(t,n),r={...e};delete r[n];return`${s}${G(r)}`}return`${t}${G(e)}`};class q{attr(t,e){return void 0!==e&&(this.attrs[t]=e),this.attrs[t]}append(t){return((t,e)=>{t.content.push(e)})(this,t)}get length(){return U(this)}toTagStart({openTag:t=L,closeTag:e=S}={}){return`${t}${z(this.tag,this.attrs)}${e}`}toTagEnd({openTag:t=L,closeTag:e=S}={}){return`${t}${N}${this.tag}${e}`}toTagNode(){return new q(this.tag.toLowerCase(),this.attrs,this.content)}toString({openTag:t=L,closeTag:e=S}={}){const n=0===this.content.length,s=this.content.reduce(((n,s)=>n+s.toString({openTag:t,closeTag:e})),""),r=this.toTagStart({openTag:t,closeTag:e});return n?r:`${r}${s}${this.toTagEnd({openTag:t,closeTag:e})}`}constructor(t,e,n){this.tag=t,this.attrs=e,this.content=Array.isArray(n)?n:[n]}}q.create=(t,e={},n=[])=>new q(t,e,n),q.isOf=(t,e)=>t.tag===e;const M=Symbol("slide-title-open"),B=Symbol("slide-title-close"),R=Symbol("slide-close"),P=/(?\{slide=)|(?\})|(?\{\/slide\})/i;function F(t){switch(t){case M:return"{slide=";case B:return"}";case R:return"{/slide}";default:return t}}const J={accordion:t=>{const e=b(),n=function(t){t=[...t];const e=[];for(;t.length>0;){const n=t[0];if(E(n)){e.push(t.shift());continue}const s=i(n,P);if(-1===s){e.push(t.shift());continue}const r=n.match(P),o=n.slice(0,s),a=n.slice(s+r[0].length);o.length&&e.push(o),r.groups.slideTitleOpen&&e.push(M),r.groups.slideTitleClose&&e.push(B),r.groups.slideClose&&e.push(R),a.length?t[0]=a:t.shift()}return e}(t.content),a=function(t){const e=[];let n=null,s=null;for(const r of t)if(r===M&&null===s)n=q.create("slide"),n.content=[],n.customTitle=[],s=M;else{if(r===B&&s===M){s=B;continue}r===R&&n&&s===B?(e.push(n),n=null,s=null):n?s===M?n.customTitle.push(F(r)):n.content.push(F(r)):e.push(F(r))}return e}(n),c=a.filter((t=>E(t)&&"slide"===t.tag)).map((t=>(t.isValid=!0,t.groupId=e,t)));if(!c.length)return[o(t),...t.content,t.toTagEnd()];const l=r(t.attrs);if(l._default){const t=l._default.split("|").map((t=>t.trim()));t.includes("bright")&&(l.bright=!0),t.includes("bcenter")&&(l.bcenter=!0),t.includes("bleft")&&(l.bleft=!0),t.includes("fleft")&&(l.fleft=!0),t.includes("fright")&&(l.fright=!0),(t.some((t=>t.endsWith("px")))||t.some((t=>t.endsWith("%"))))&&(l.width=t.find((t=>t.endsWith("px")||t.endsWith("%"))))}let u=Object.keys(l).filter((t=>["bright","bcenter","bleft","fleft","fright"].includes(t))).join(" "),d="";return(l.width?.endsWith("px")||l.width?.endsWith("%"))&&(d=`width: ${l.width};`),s("div",{class:"bb-accordion "+u,"data-group-id":e,style:d},c)},slide:t=>{if(!t.isValid)return[o(t),...t.content,t.toTagEnd()];const e=r(t.attrs);let n=[e.title||e._default||"Slide"],i=!!e.open||!1,a=e.left?"left":e.right?"right":e.center?"center":"left";if(t.customTitle?.length){n=t.customTitle;const e=n.filter((t=>"string"==typeof t)).join("").toLowerCase().split("|").map((t=>t.trim()));e.includes("open")&&(i=!0),e.includes("right")&&(a="right"),e.includes("center")&&(a="center"),e.includes("left")&&(a="left"),n=n.map((t=>(W(t)&&(t=t.replace(/\|(open|right|center|left)/gi,"")),t)))}return[s("details",{class:"bb-slide",open:i},[s("summary",{class:"bb-slide-title",style:`text-align: ${a}; ${e.style||""}`},n),s("div",{class:"bb-slide-content"},t.content)])]}},Z=["init","click","change","input","dblclick","mouseenter","mouseleave","scroll"],H={...J,...p,...h,animation:(t,e)=>{e.data.previewing||e.data.commonGUID||(e.data.commonGUID="post-"+Math.random().toString(36).substring(2,7));const n=e.data.previewing?"preview":e.data.commonGUID,s=r(t.attrs)?._default||"",o=t.content.filter((t=>E(t)&&"keyframe"===t.tag)).map((t=>{t.isValid=!0;const e=r(t.attrs)._default||"";t.ident=e+(e.match(/^\d+$/)?"%":"");const n=t.content.filter(W).join("").replaceAll(/[\[\]\{\}]/g,"");return t.formatted=`${t.ident}{ ${n} }`,t})),i=`@keyframes ${n}${s} { ${o.map((t=>t.formatted)).join("\n")} }`;return e.data.styles.push(i),[]},bg:t=>{const e=r(t.attrs)._default;return s("div",{style:`background-color: ${e};`,class:"bb-background"},t.content)},block:t=>{const e="block",n=(r(t.attrs)._default||e).toLowerCase(),o=["block","dice","dice10","setting","warning","storyteller","announcement","important","question","encounter","information","character","treasure"].includes(n)?n:e;return s("table",{class:"bb-block","data-bb-block":o},[s("tbody",[s("tr",[s("td",{class:"bb-block-icon"}),s("td",{class:"bb-block-content"},t.content)])])])},blockquote:t=>{const e=r(t.attrs)._default||"";return s("div",{class:"bb-blockquote"},[s("div",{class:"bb-blockquote-left"}),s("div",{class:"bb-blockquote-content"},[t.content,s("div",{class:"bb-blockquote-speaker"},""!==e?`- ${e}`:"")]),s("div",{class:"bb-blockquote-right"})])},border:t=>{const e=r(t.attrs)._default;return s("div",{style:`border: ${e};`,class:"bb-border"},t.content)},br:()=>s("br",{},null),centerblock:t=>{const e=r(t.attrs)._default||"50";return s("div",{style:`margin: 0 auto; width: ${e}%`},t.content)},check:t=>{const e=r(t.attrs)._default||"dot";return s("div",{class:"bb-check","data-type":e},t.content)},class:(t,e)=>{const n=r(t.attrs),s=n.name||n._default;e.data.previewing||e.data.commonGUID||(e.data.commonGUID="post-"+Math.random().toString(36).substring(2,7));const o=e.data.previewing?"preview":e.data.commonGUID,i=s+"__"+o,a=t.content.filter(W).map((t=>t.replaceAll("{post_id}",o).replaceAll(/[\[\]\{\}]/g,"")));let c="";const l=[];return["hover","focus","active","focus-within","focus-visible"].includes(n.state?.toLowerCase())&&(c=":"+n.state.toLowerCase()),n.selector&&(c=n.selector.replace(/[,{}\\\n]/g,"")),n.minWidth?.match(/^[0-9]+[a-z]+$/)&&l.push(`(min-width: ${n.minWidth})`),n.maxWidth?.match(/^[0-9]+[a-z]+$/)&&l.push(`(max-width: ${n.maxWidth})`),a.unshift(`.${i}${c} {`),a.push("}"),l.length&&(a.unshift(`@media ${l.join(" and ")} {`),a.push("}")),e.data.styles.push(a.join("")),[]},code:t=>({isWhitespaceSensitive:!0,content:["```"+(r(t.attrs)._default||"bbcode")+"\n",t.content,"\n```\n"]}),color:t=>{const e=r(t.attrs)._default||"";return""===e.trim()?t.content:s("span",{style:`color: ${e}`},t.content)},comment:t=>s("span",{class:"hidden"},t.content),div:(t,e)=>{if(t.gen)return t;const n=r(t.attrs),o=n.style||n._default,i=n.class;if(!i?.trim())return s("div",{style:o},t.content);e.data.previewing||e.data.commonGUID||(e.data.commonGUID="post-"+Math.random().toString(36).substring(2,7));const a=e.data.previewing?"preview":e.data.commonGUID,c=i.split(" ").map((t=>t+"__"+a)).join(" ");return s("div",{class:c,style:o},t.content)},divide:t=>{const e=(r(t.attrs)._default||"").toLowerCase();return s("span",{class:"bb-divide","data-type":e},t.content)},fieldset:t=>{const e=r(t.attrs)._default||"";return s("fieldset",{class:"bb-fieldset"},[s("legend",{class:"bb-fieldset-legend"},e),s("div",{class:"bb-fieldset"},t.content)])},font:(t,e)=>{const n=r(t.attrs),o=n?._default||n.family||n.name;if(""===o.trim())return t.content;if(f.includes(o.trim().toLowerCase()))return s("span",{style:"font-family: "+o},t.content);const i=(t=>{let e={ital:0,wght:400};if(t?.style){const n=t.style.trim().toLowerCase(),s=y.exec(n).groups||{};s?.italic&&(e.ital=1);const r=s.weight;r&&r>=0&&r<=900?e.wght=r:Object.keys(m).includes(s.named_weight||"")&&(e.wght=m[s.named_weight]),e={...e,...Object.fromEntries(Object.entries(t).filter((([t])=>v.includes(t))))}}return e})(n),a=((t,e)=>(t=t.replaceAll(" ","+"),e=Object.keys(e).sort().reduce(((t,n)=>(t[n]=e[n],t)),{}),"https://fonts.googleapis.com/css2?family="+t+":"+Object.keys(e).join(",")+"@"+Object.values(e).join(",")))(o,i);e.data.fonts.add(a);const c=1===i.ital?"italic":"normal",l=Object.entries(i).filter((([t])=>"wght"!==t&&"ital"!==t));let u="";return l.length&&(u="font-variation-settings: "+l.map((([t,e])=>`'${t}' ${e}`)).join(", ")+";"),s("span",{style:`font-family: ${o}; font-weight: ${i.wght}; font-style: ${c}; ${u}`,"data-font":a},t.content)},h:t=>s("h1",{},t.content),h1:t=>s("h1",{},t.content),h2:t=>s("h2",{},t.content),h3:t=>s("h3",{},t.content),h4:t=>s("h4",{},t.content),h5:t=>s("h5",{},t.content),h6:t=>s("h6",{},t.content),heightrestrict:t=>{const e=function(t){const e=t&&""!==t.trim()?t.replace(/[^\d.]/g,""):0;return e&&e>=0&&e<=700?e:0===e?0:700}(r(t.attrs)._default).toString();return s("div","0"===e?{class:"bb-height-restrict"}:{class:"bb-height-restrict",style:`height: ${e}px;`},t.content)},highlight:t=>s("span",{class:"bb-highlight"},t.content),icode:t=>({isWhitespaceSensitive:!0,content:["`",t.content,"`"]}),imagefloat:t=>{const e=r(t.attrs)._default||"";return s("div",{class:`bb-float-${e}`},t.content)},inlinespoiler:t=>s("span",{class:"bb-inline-spoiler"},t.content),justify:t=>s("div",{class:"bb-justify"},t.content),keyframe:t=>t.isValid?[]:[o(t),...t.content,t.toTagEnd()],mail:t=>{const e=r(t.attrs);let n={mailOption:(e.type||"send").toLowerCase(),person:e.person||"Unknown",subject:e.subject||"Empty"};return s("div",{class:"bb-email","data-bb-email":n.mailOption},[w,(a=n.person,s("div",{class:"bb-email-address"},a)),(i=n.subject,s("div",{class:"bb-email-subject"},i)),(o=t.content,s("div",{class:"bb-email-content"},o)),x]);var o,i,a},newspaper:t=>s("div",{class:"bb-newspaper"},t.content),nobr:t=>({disableLineBreakConversion:!0,content:t.content}),note:t=>s("div",{class:"bb-note"},[s("div",{class:"bb-note-tape"},""),s("div",{class:"bb-note-content"},[t.content,s("div",{class:"bb-note-footer"},"")])]),ooc:t=>s("div",{class:"bb-ooc"},t.content),pindent:t=>s("span",{class:"bb-pindent"},t.content),plain:t=>t.content,print:t=>{const e="print",n=(r(t.attrs)._default||e).toLowerCase(),o=["print","line","graph","parchment"].includes(n)?n:e;return s("div",{class:o===e?"bb-print":`bb-print-${o}`},t.content)},progress:t=>{const e=r(t.attrs)._default;return s("div",{class:"bb-progress"},[s("div",{class:"bb-progress-text"},t.content),s("div",{class:"bb-progress-bar",style:`width: calc(${e}% - 6px)`},""),s("div",{class:"bb-progress-bar-other"},"")])},thinprogress:t=>{const e=r(t.attrs)._default;return s("div",{class:"bb-progress-thin"},[s("div",{class:"bb-progress-text"},t.content),s("div",{class:"bb-progress-bar",style:`width: calc(${e}% - 6px)`},""),s("div",{class:"bb-progress-bar-other"},"")])},savenl:t=>({isWhitespaceSensitive:!0,content:t.content}),sh:t=>s("h2",{},t.content),script:(t,e)=>{const n=r(t.attrs);e.data.previewing||e.data.commonGUID||(e.data.commonGUID="post-"+Math.random().toString(36).substring(2,7));const s=e.data.previewing?"preview":e.data.commonGUID,o=Z.includes(n.on?.toLowerCase()||"init")&&n.on?.toLowerCase()||"init",i={id:s,class:n.class||"",on:o,version:n.version||"",content:t.content.join("")};return e.data.bbscripts.push(i),[]},scroll:t=>{const e=function(t){const e=t&&""!==t.trim()?t.replace(/[^\d.]/g,""):0;return e&&e>=0&&e<=700?e:0===e?0:700}(r(t.attrs)._default);return s("div",{class:"bb-scroll",style:`height: ${e}px`},t.content)},side:t=>{const e=r(t.attrs)._default||"left";return s("div",{class:"bb-side","data-side":e},t.content)},size:t=>{const e=function(t){let e,n={valid:!0};const s=/(\d+\.?\d?)(px|rem)?/i.exec(t),r=36,o=8,i=3,a=.2,c=7,l=1;if(s&&(e=s[1])){switch(n.unit=(s[2]||"").toLowerCase(),n.unit){case"px":e>r?e=r:ei?e=i:ec?e=c:e{const e=r(t.attrs)._default;return s("details",{class:"bb-spoiler"},[s("summary",{},"Spoiler"+(e?`: ${e}`:"")),s("div",{class:"bb-spoiler-content"},t.content)])},sub:t=>s("sub",{},t.content),sup:t=>s("sup",{},t.content),tab:t=>{if(!t.isValid)return[o(t),...t.content,t.toTagEnd()];const e=r(t.attrs),n=e._default||e.name||"Tab",i=`tab-${n.replace(/\W/g,"_")}-${b()}`;return[s("input",{type:"radio",id:i,name:"tab-group-"+t.groupId,class:"bb-tab",checked:t.open}),s("label",{class:"bb-tab-label",for:i,style:e.style},n),s("div",{class:"bb-tab-content"},t.content)]},tabs:t=>{const e=t.content.filter((t=>E(t)&&"tab"===t.tag)),n=b();return e.forEach((t=>{t.isValid=!0,t.groupId=n})),e.length?(e[0].open=!0,s("div",{class:"bb-tabs"},e)):[o(t),...t.content,t.toTagEnd()]},...k,b:t=>s("span",{class:"bbcode-b"},t.content),i:t=>s("span",{class:"bbcode-i"},t.content),u:t=>s("span",{class:"bbcode-u"},t.content),s:t=>s("span",{class:"bbcode-s"},t.content)},K=Object.keys(H),Q=function t(e,s=n){const r=(t={})=>{r.options=Object.assign(r.options||{},t);const n=(t,n)=>s(e,t,n,r.options);return n.options=r.options,n};return r.extend=n=>t(n(e,r.options),s),r}(H),X="type",Y="value",tt="line",et=t=>t&&void 0!==t[Y]?t[Y]:"",nt=t=>et(t).charCodeAt(0)===N.charCodeAt(0);class st{isEmpty(){return isNaN(this[X])}isText(){return!(!(t=this)||void 0===t[X]||5!==t[X]&&6!==t[X]&&1!==t[X]);var t}isTag(){return!(!(t=this)||void 0===t[X])&&2===t[X];var t}isAttrName(){return!(!(t=this)||void 0===t[X])&&3===t[X];var t}isAttrValue(){return!(!(t=this)||void 0===t[X])&&4===t[X];var t}isStart(){return!nt(this)}isEnd(){return nt(this)}getName(){return(t=>{const e=et(t);return nt(t)?e.slice(1):e})(this)}getValue(){return et(this)}getLine(){return(t=this)&&t[tt]||0;var t}getColumn(){return(t=this)&&t.row||0;var t}toString(){return(t=>{let e=L;return e+=et(t),e+=S,e})(this)}constructor(t,e,n,s){this[X]=Number(t),this[Y]=String(e),this[tt]=Number(n),this.row=Number(s)}}function rt(t,e){const n={pos:0,len:t.length},s=()=>n.len>n.pos,r=(t=1,s)=>{n.pos+=t,e&&e.onSkip&&!s&&e.onSkip()},o=()=>t[n.pos];this.skip=r,this.hasNext=s,this.getCurr=o,this.getRest=()=>t.substring(n.pos),this.getNext=()=>{const e=n.pos+1;return e<=t.length-1?t[e]:null},this.getPrev=()=>{const e=n.pos-1;return void 0!==t[e]?t[e]:null},this.isLast=()=>n.pos===n.len,this.includes=e=>t.indexOf(e,n.pos)>=0,this.grabWhile=(e,i)=>{let a=0;if(s())for(a=n.pos;s()&&e(o());)r(1,i);return t.substring(a,n.pos)},this.grabN=(e=0)=>t.substring(n.pos,n.pos+e),this.substrUntilChar=e=>{const{pos:s}=n,r=t.indexOf(e,s);return r>=0?t.substring(s,r):""}}const ot=(t,e)=>new rt(t,e);function it(t=[]){const e=t;this.push=t=>e.push(t),this.toArray=()=>e,this.getLast=()=>Array.isArray(e)&&e.length>0&&void 0!==e[e.length-1]?e[e.length-1]:null,this.flushLast=()=>!!e.length&&e.pop()}const at=(t=[])=>new it(t);function ct(t,e={}){const n=0,s=1,r=2,o=0,i=1,a=2;let c=0,l=0,u=-1,d=n,g=o,b="";const p=new Array(Math.floor(t.length)),h=e.openTag||L,f=e.closeTag||S,m=!!e.enableEscapeTags,v=e.contextFreeTags||[],y=e.onToken||(()=>{}),w=[f,h,_,O,C,A,j,T,"!"],x=[h,C,A,T],$=[C,A],k=[j,C,A],E=t=>w.indexOf(t)>=0,W=t=>t===T,I=t=>$.indexOf(t)>=0,U=t=>-1===x.indexOf(t),V=t=>k.indexOf(t)>=0,D=t=>t===h||t===f||t===O,G=t=>t===O,z=()=>{l++},q=t=>((t,e)=>{for(;t.charAt(0)===e;)t=t.substring(1);for(;t.charAt(t.length-1)===e;)t=t.substring(0,t.length-1);return t})(t,_).replace(O+_,_),M=(t,e)=>{""!==b&&e&&(b=""),""===b&&v.includes(t)&&(b=t)},B=ot(t,{onSkip:z});function R(t,e){const n=((t,e,n=0,s=0)=>new st(t,e,n,s))(t,e,c,l);y(n),u+=1,p[u]=n}function P(t,e){if(g===i){const e=t=>!(t===j||I(t)),n=t.grabWhile(e),s=t.isLast(),r=t.getCurr()!==j;return t.skip(),s||r?R(4,q(n)):R(3,n),s?o:r?i:a}if(g===a){let n=!1;const s=s=>{const r=s===_,o=t.getPrev(),i=t.getNext(),a=o===O,c=i===j,l=I(s),u=I(i);return!(!n||!V(s))||!!(!r||a||(n=!n,n||c||u))&&(!!e||!1===l)},r=t.grabWhile(s);return t.skip(),R(4,q(r)),t.isLast()?o:i}const n=t.grabWhile((e=>!(e===j||I(e)||t.isLast())));if(R(2,n),M(n),t.skip(),e)return a;return t.includes(j)?i:a}function F(){const t=B.getCurr(),e=B.getNext();B.skip();const s=B.substrUntilChar(f),o=0===s.length||s.indexOf(h)>=0;if(E(e)||o||B.isLast())return R(1,t),n;const i=-1===s.indexOf(j),a=s[0]===N;if(i||a){const t=B.grabWhile((t=>t!==f));return B.skip(),R(2,t),M(t,a),n}return r}function J(){const t=B.grabWhile((t=>t!==f),!0),e=ot(t,{onSkip:z}),s=e.includes(C);for(g=o;e.hasNext();)g=P(e,!s);return B.skip(),n}function Z(){if(W(B.getCurr()))return R(6,B.getCurr()),B.skip(),l=0,c++,n;if(I(B.getCurr())){return R(5,B.grabWhile(I)),n}if(B.getCurr()===h){if(b){const t=h.length+1+b.length,e=`${h}${N}${b}`;if(B.grabN(t)===e)return s}else if(B.includes(f))return s;return R(1,B.getCurr()),B.skip(),n}if(m){if(G(B.getCurr())){const t=B.getCurr(),e=B.getNext();return B.skip(),D(e)?(B.skip(),R(1,e),n):(R(1,t),n)}const t=t=>U(t)&&!G(t);return R(1,B.grabWhile(t)),n}return R(1,B.grabWhile(U)),n}return{tokenize:function(){for(d=n;B.hasNext();)switch(d){case s:d=F();break;case r:d=J();break;default:d=Z()}return p.length=u+1,p},isTokenNested:function(e){const n=h+N+e.getValue();return t.indexOf(n)>-1}}}const lt=(t,e={})=>{const n=e,s=n.openTag||L,r=n.closeTag||S,o=(n.onlyAllowTags||[]).filter(Boolean).map((t=>t.toLowerCase()));let i=null;const a=at(),c=at(),l=at(),u=at(),d=new Set,g=t=>Boolean(d.has(t)),b=()=>{l.flushLast()&&u.flushLast()},p=()=>{const t=c.getLast();return t&&Array.isArray(t.content)?t.content:a.toArray()},h=(t,e=!0)=>{const n=p();Array.isArray(n)&&(n.push(t.toTagStart({openTag:s,closeTag:r})),t.content.length&&(t.content.forEach((t=>{n.push(t)})),e&&n.push(t.toTagEnd({openTag:s,closeTag:r}))))},f=t=>{const e=p();var n;Array.isArray(e)&&(E(t)?(n=t.tag,!o.length||o.indexOf(n.toLowerCase())>=0?e.push(t.toTagNode()):h(t)):e.push(t))},m=t=>{b();const e=q.create(t.getValue()),n=(t=>{const e=t.getValue();return!d.has(e)&&i.isTokenNested&&i.isTokenNested(t)?(d.add(e),!0):d.has(e)})(t);l.push(e),n?c.push(e):f(e)},v=t=>{t.isStart()&&m(t),t.isEnd()&&(t=>{b();const e=c.flushLast();if(e)f(e);else if("function"==typeof n.onError){const e=t.getValue(),s=t.getLine(),r=t.getColumn();n.onError({message:`Inconsistent tag '${e}' on line ${s} and column ${r}`,tagName:e,lineNumber:s,columnNumber:r})}})(t)};i=(e.createTokenizer?e.createTokenizer:ct)(t,{onToken:t=>{t.isTag()?v(t):(t=>{const e=l.getLast(),n=t.getValue(),s=g(t);if(e)if(t.isAttrName())u.push(n),e.attr(u.getLast(),"");else if(t.isAttrValue()){const t=u.getLast();t?(e.attr(t,n),u.flushLast()):e.attr(n,n)}else t.isText()?s?e.append(n):f(n):t.isTag()&&f(t.toString());else t.isText()?f(n):t.isTag()&&f(t.toString())})(t)},openTag:s,closeTag:r,onlyAllowTags:n.onlyAllowTags,contextFreeTags:n.contextFreeTags,enableEscapeTags:n.enableEscapeTags}),i.tokenize();const y=c.flushLast();return y&&g(y.tag)&&h(y,!1),a.toArray()},ut=t=>"object"==typeof t,dt=t=>"boolean"==typeof t;function gt(t,e){const n=t;if(Array.isArray(n))for(let t=0;t[].some.call(e,(e=>bt(t,e))))):Object.keys(t).every((n=>{const s=e[n],r=t[n];return ut(r)&&null!==r&&null!==s?bt(r,s):dt(r)?r!==(null===s):s===r})):t===e)}function pt(t,e){return Array.isArray(t)?gt(this,(n=>{for(let s=0;sbt(t,n)?e(n):n))}function ht(t){return gt(this,t)}const ft="/>",mt="",wt=(t,{stripTags:e=!1}={})=>[].concat(t).reduce(((t,n)=>t+((t,{stripTags:e=!1})=>{if(!t)return"";const n=typeof t;return"string"===n||"number"===n?t:"object"===n?!0===e?wt(t.content,{stripTags:e}):null===t.content?[vt,t.tag,G(t.attrs),ft].join(""):[vt,t.tag,G(t.attrs),yt,wt(t.content),mt,t.tag,yt].join(""):Array.isArray(t)?wt(t,{stripTags:e}):""})(n,{stripTags:e})),""),xt=wt,$t=t=>{const e=t;if(Array.isArray(e))for(let t=0;t1&&" "===e[0]){let t=e.length;return[String.fromCharCode(160).repeat(t)]}return e};function kt(t){return t.replaceAll(a,"").replaceAll(c,"").replaceAll("\n"+l,"").replaceAll(l+"\n","").replaceAll(l,"")}function Tt(t,e){const n=e.hoistMap;for(const[e,s]of Object.entries(n))t=t.replaceAll(e,s);return t}function At(t,e){if(0===e.styles.length)return t;return'"+t}function jt(t,e){if(0===e.bbscripts.length)return t;return e.bbscripts.map((t=>``)).join("")+t}const _t=t=>"string"==typeof t,Ct=(t,e=!1)=>{const n=t;if(Array.isArray(n)){n.some(_t)&&(n.unshift(a),n.push(a));for(let t=0;t{const i=b();return-1!==s?(n[i]=t.substring(e,s),t=t.substring(0,e)+i+t.substring(s)):(n[i]=t.substring(e),t=t.substring(0,e)+i+r),o&&(n[i].startsWith("\n")&&(n[i]=n[i].substring(1)),n[i].endsWith("\n")&&(n[i]=n[i].substring(0,n[i].length-1))),e+i.length+r.length};for(;-1!==(s=i(t,d,s));){const e=d.exec(t.substring(s));if(e.groups?.fence){const r=e.groups.fence,o=e.groups.fenceInfo;"\n"===t[s]&&(s+=1);const a=new RegExp("\n"+r+"(\n|$)"),c=i(t,a,s+r.length),l=b();n[l]=-1!==c?t.substring(s+r.length+o.length,c):t.substring(s+r.length+o.length);const u=`[saveNL]\n${r}${o}${l}\n${r}\n[/saveNL]`;t=t.substring(0,s)+u+(-1!==c?t.substring(c+1+r.length):""),s+=u.length}else if(e.groups?.bbcode){const n=e.groups.bbcode,o=`[/${e.groups.bbcodeTag.toLowerCase()}]`,i=t.toLowerCase().indexOf(o,s+1);s=r(s+n.length,i,o,!0)}else if(e.groups.backtick){const t=e.groups.backtick,n=e.groups.tickStart,o=e.groups.tickEnd;s=r(s+n.length,s+t.length-o.length,o)}}return e.hoistMap=n,[t,e]}function St(t,e){let n=0;for(;-1!==(n=i(t,g,n));){const e=g.exec(t.substring(n))[0],s=`[saveNL]\n${e}\n[/saveNL]`;t=t.substring(0,n)+s+t.substring(n+e.length),n+=s.length}return[t,e]}const Nt={onlyAllowTags:[...K],contextFreeTags:["plain","code","icode","class"],enableEscapeTags:!0,onError:t=>{Nt.previewing&&console.warn(t.message,t.lineNumber,t.columnNumber)}},Ot=Q();t.RpNBBCode=(t,e)=>{const n=[Ot];e.preserveWhitespace&&n.push((t=>$t(t))),n.push((t=>Ct(t)));const[s,r]=function(t){let e={};const n=[Lt,St];for(const s of n)[t,e]=s(t,e);return[t,e]}(t);return function(t){const e="function"==typeof t?[t]:t||[];let n={skipParse:!1};return{process(t,s){n=s||{};const r=n.parser||lt,o=n.render,i=n.data||null;if("function"!=typeof r)throw new Error('"parser" is not a function, please pass to "process(input, { parser })" right function');let a=n.skipParse?t||[]:r(t,n);const c=a;return a.messages=[],a.options=n,a.walk=ht,a.match=pt,e.forEach((t=>{a=t(a,{parse:r,render:o,iterate:gt,match:pt,data:i})||a})),{get html(){if("function"!=typeof o)throw new Error('"render" function not defined, please pass to "process(input, { render })"');return o(a,a.options)},tree:a,raw:c,messages:a.messages}}}}(n).process(s,{render:xt,...Nt,data:{...r,previewing:e.previewing,fonts:new Set,styles:[],bbscripts:[]}})},t.postprocess=function(t,e){let n=t;const s=[kt,At,jt,Tt];for(const t of s)n=t(n,e);return n}})); -//# sourceMappingURL=bbcode-parser.min.js.map + +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.bbcodeParser = {})); +})(this, (function (exports) { 'use strict'; + + /* eslint-disable indent */ const isTagNode$1 = (el)=>typeof el === 'object' && !!el.tag; + function process(tags, tree, core, options) { + tree.walk((node)=>isTagNode$1(node) && tags[node.tag] ? tags[node.tag](node, core, options) : node); + } + /** + * Creates preset for @bbob/core + * @param defTags {Object} + * @param processor {Function} a processor function of tree + * @returns {function(*=): function(*=, *=): void} + */ function createPreset(defTags, processor = process) { + const presetFactory = (opts = {})=>{ + presetFactory.options = Object.assign(presetFactory.options || {}, opts); + const presetExecutor = (tree, core)=>processor(defTags, tree, core, presetFactory.options); + presetExecutor.options = presetFactory.options; + return presetExecutor; + }; + presetFactory.extend = (callback)=>createPreset(callback(defTags, presetFactory.options), processor); + return presetFactory; + } + + const toNode = (tag, attrs, content) => ({ + tag, + attrs, + content, + }); + + /** + * Preprocess attributes of a node to either combine all values into a single default value + * or return a keyed attribute list + * @param {any} attrs object of bbcode node attrs + * @param {string[]} predefinedKeys array of predefined keys to be captured + * @returns processed attributes + */ + const preprocessAttr = (attrs) => { + const keys = Object.keys(attrs).join(" "); + const vals = Object.values(attrs).join(" "); + if (keys === vals) { + return { + _default: vals, + }; + } else { + return attrs; + } + }; + + /** + * Attempts to return tag into its original form with proper attributes + * @returns string of tag start + */ + const toOriginalStartTag = (node) => { + if (!node.attrs) { + return `[${node.tag}]`; + } + const attrs = preprocessAttr(node.attrs); + if (attrs._default) { + return `[${node.tag}=${attrs._default}]`; + } else { + return node.toTagStart(); + } + }; + + /** + * Given a string, find the first position of a regex match + * @param {string} string to test against + * @param {RegExp} regex to test with + * @param {number} startpos starting position. Defaults to 0 + * @returns index of the first match of the regex in the string + */ + const regexIndexOf = (string, regex, startpos) => { + const indexOf = string.substring(startpos || 0).search(regex); + return indexOf >= 0 ? indexOf + (startpos || 0) : indexOf; + }; + + const MD_NEWLINE_INJECT = "\n\n"; + const MD_NEWLINE_PRE_INJECT = "\n\n"; + const MD_NEWLINE_INJECT_COMMENT = ""; + + const URL_REGEX = + /(http|ftp|https|upload):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])/; + const MD_URL_REGEX = + /\!?\[.*\]\((http|ftp|https|upload):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])\)/; + const URL_REGEX_SINGLE_LINE = new RegExp(`^${URL_REGEX.source}|${MD_URL_REGEX.source}$`); + const ESCAPABLES_REGEX = + /((\n|^)(?```+|~~~+)(?.*\n))|(?\[(?i?code|plain)(=.*)?\])|(?(?`{1,2})(.*)(?\k))/im; + const MD_TABLE_REGEX = /^(\|[^\n]+\|\r?\n)((?:\| ?:?[-]+:? ?)+\|)(\n(?:\|[^\n]+\|\r?\n?)*)?$/m; + + /** + * Generates a random GUID. + * + * Mini Racer doesn't have the crypto module, so we can't use the built-in `crypto.randomUUID` function. + * @returns {string} a GUID + */ + function generateGUID() { + let d = new Date().getTime(); + if (window.performance && typeof window.performance.now === "function") { + d += performance.now(); //use high-precision timer if available + } + return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) { + // eslint-disable-next-line no-bitwise + const r = (d + Math.random() * 16) % 16 | 0; + d = Math.floor(d / 16); + // eslint-disable-next-line no-bitwise + return (c === "x" ? r : (r & 0x3) | 0x8).toString(16); + }); + } + + /** + * @file Adds [left], [center], and [right] to bbcode + * @example [center]content[/center] + */ + const alignment = { + left: (node) => toNode("div", { class: "bb-left" }, node.content), + center: (node) => toNode("div", { class: "bb-center" }, node.content), + right: (node) => toNode("div", { class: "bb-right" }, node.content), + }; + + /** + * @file Adds [id] and [goto] to bbcode + * @example [a=your_anchor_name]An anchor[/a] [goto=your_anchor_name]Jump to an anchor[/goto] + */ + const anchor = { + // name is not valid in HTML5; however, it correctly displays back while id does not + a: (node) => { + const attrs = preprocessAttr(node.attrs)._default || ""; + return toNode("a", { id: `user-anchor-${attrs.trim()}`, name: `user-anchor-${attrs.trim()}` }, node.content); + }, + goto: (node) => { + const attrs = preprocessAttr(node.attrs)._default || ""; + toNode("a", { href: `#user-anchor-${attrs.trim()}` }, node.content); + } + }; + + /** + * Add [bg] tag + * @example [bg=red]Hello[/bg] + */ + const bg = (node) => { + const color = preprocessAttr(node.attrs)._default; + return toNode( + "div", + { + style: `background-color: ${color};`, + class: "bb-background", + }, + node.content, + ); + }; + + /** + * Add [block] tag + * @example [block=treasure]content[/block] + */ + const block = (node) => { + const defaultOp = "block"; + const blockAttr = (preprocessAttr(node.attrs)._default || defaultOp).toLowerCase(); + + const OPTIONS = [ + "block", + "dice", + "dice10", + "setting", + "warning", + "storyteller", + "announcement", + "important", + "question", + "encounter", + "information", + "character", + "treasure", + ]; + + // Default to block option if user did not provide anything valid + const blockOption = OPTIONS.includes(blockAttr) ? blockAttr : defaultOp; + + return { + tag: "table", + attrs: { + class: "bb-block", + "data-bb-block": blockOption + }, + content: [ + { + tag: "tbody", + content: [ + { + tag: "tr", + content: [ + { + tag: "td", + attrs: { + class: "bb-block-icon" + } + }, + { + tag: "td", + attrs: { + class: "bb-block-content" + }, + content: node.content + } + ] + } + ] + } + ] + }; + }; + + /** + * @file Adds [blockquote] to bbcode + * @example [blockquote=author]content[/blockquote] + */ + const blockquote = (node) => { + const author = preprocessAttr(node.attrs)._default || ""; + + return { + tag: "div", + attrs: { + class: "bb-blockquote", + }, + content: [ + { + tag: "div", + attrs: { + class: "bb-blockquote-left", + }, + }, + { + tag: "div", + attrs: { + class: "bb-blockquote-content", + }, + content: [ + node.content, + { + tag: "div", + attrs: { + class: "bb-blockquote-speaker", + }, + content: `${author !== "" ? `- ${author}` : ""}`, + }, + ], + }, + { + tag: "div", + attrs: { + class: "bb-blockquote-right", + }, + }, + ], + }; + }; + + const border = (node) => { + const val = preprocessAttr(node.attrs)._default; + return toNode( + "div", + { + style: `border: ${val};`, + class: "bb-border", + }, + node.content + ); + }; + + const centerblock = (node) => { + const percentageInput = preprocessAttr(node.attrs)._default || "50"; + return toNode("div", { style: `margin: 0 auto; width: ${percentageInput}%` }, node.content); + }; + + const check = (node) => { + const attrs = preprocessAttr(node.attrs)._default || "dot"; + return toNode("div", { class: `bb-check`, "data-type": attrs }, node.content); + }; + + /** + * processes [code] tag and returns a fenced code block + */ + const code = (node) => { + const lang = preprocessAttr(node.attrs)._default || "bbcode"; + return { + isWhitespaceSensitive: true, + content: ["```" + lang + "\n", node.content, "\n```\n"], + }; + }; + + /** + * processes [icode] tag and returns inline code + */ + const icode = (node) => { + return { + isWhitespaceSensitive: true, + content: ["`", node.content, "`"], + }; + }; + + /** + * Special tag to save newlines in code blocks. Used for hoisting code blocks + */ + const savenl = (node) => { + return { + isWhitespaceSensitive: true, + content: node.content, + }; + }; + + const color = (node) => { + const inputColor = preprocessAttr(node.attrs)._default || ""; + if (inputColor.trim() === "") { + return node.content; + } + return toNode("span", { style: `color: ${inputColor}` }, node.content); + }; + + const comment = (node) => { + return toNode( + "span", {class: "hidden" }, node.content, + ); + }; + + const divide = (node) => { + const type = (preprocessAttr(node.attrs)._default || "").toLowerCase(); + return toNode( + "span", + { + class: "bb-divide", + "data-type": type, + }, + node.content + ); + }; + + /** + * @file Adds [fieldset] to bbcode + * @example [fieldset=title]content[/fieldset] + */ + const fieldset = (node) => { + const title = preprocessAttr(node.attrs)._default || ""; + return { + tag: "fieldset", + attrs: { + class: "bb-fieldset", + }, + content: [ + { + tag: "legend", + attrs: { + class: "bb-fieldset-legend" + }, + content: title + }, + { + tag: "div", + attrs: { + class: "bb-fieldset" + }, + content: node.content + } + ] + }; + }; + + const WEB_FONTS = [ + "arial", + "book antiqua", + "courier new", + "georgia", + "tahoma", + "times new roman", + "trebuchet ms", + "verdana", + ]; + const VALID_FONT_STYLES = { + thin: "100", + extralight: "200", + light: "300", + regular: "400", + medium: "500", + semibold: "600", + bold: "700", + extrabold: "800", + black: "900", + }; + // registered axis tags https://learn.microsoft.com/en-us/typography/opentype/spec/dvaraxisreg#registered-axis-tags + const REGISTERED_AXIS = ["ital", "opsz", "slnt", "wdth", "wght"]; + + const AXES_REGEX = /(?[a-zA-Z]*)?\s?(?[0-9]*)?\s?(?italic)?/; + + const axesParser = (attrs) => { + let axes = { + ital: 0, + wght: 400, + }; + + if (attrs?.style) { + // user just copy pasted the name of the style on the google font site, probably + const style = attrs.style.trim().toLowerCase(); + const matches = AXES_REGEX.exec(style).groups || {}; + if (matches?.italic) { + axes.ital = 1; + } + + const weight = matches.weight; + if (weight && weight >= 0 && weight <= 900) { + axes.wght = weight; + } else if (Object.keys(VALID_FONT_STYLES).includes(matches.named_weight || "")) { + axes.wght = VALID_FONT_STYLES[matches.named_weight]; + } + + axes = { + ...axes, + ...Object.fromEntries(Object.entries(attrs).filter(([key]) => REGISTERED_AXIS.includes(key))), + }; + } + return axes; + }; + + /** + * Create google font api url + * @param {string} family name of font + * @param {object} axes custom font axes + */ + const googleFontApiBuild = (family, axes) => { + family = family.replaceAll(" ", "+"); + // google fonts requires axes names to be in alphabetical order + axes = Object.keys(axes) + .sort() + .reduce((obj, key) => { + obj[key] = axes[key]; + return obj; + }, {}); + const axesList = Object.keys(axes).join(",") + "@" + Object.values(axes).join(","); + return "https://fonts.googleapis.com/css2?family=" + family + ":" + axesList; + }; + + const font = (node, options) => { + const attrs = preprocessAttr(node.attrs); + const fontFamily = attrs?._default || attrs.family || attrs.name; + if (fontFamily.trim() === "") { + return node.content; + } + if (WEB_FONTS.includes(fontFamily.trim().toLowerCase())) { + return toNode("span", { style: "font-family: " + fontFamily }, node.content); + } + + const axes = axesParser(attrs); + const url = googleFontApiBuild(fontFamily, axes); + options.data.fonts.add(url); + + const italic = axes.ital === 1 ? "italic" : "normal"; + + const custom = Object.entries(axes).filter(([key]) => key !== "wght" && key !== "ital"); + let fontVar = ""; + if (custom.length) { + fontVar = + "font-variation-settings: " + custom.map(([key, val]) => `'${key}' ${val}`).join(", ") + ";"; + } + + return toNode( + "span", + { + style: `font-family: ${fontFamily}; font-weight: ${axes.wght}; font-style: ${italic}; ${fontVar}`, + "data-font": url, + }, + node.content, + ); + }; + + /** + * @file Adds Header to bbcode + * @example [h]content[/h], [h2]content[/h2], [h3]content[/h3], + * [h4]content[/h4], [h5]content[/h5], [h6]content[/h6]. + */ + + const h = (node) => { + return toNode("h1", {}, node.content); + }; + + const h1 = (node) => { + return toNode("h1", {}, node.content); + }; + + const h2 = (node) => { + return toNode("h2", {}, node.content); + }; + + const sh = (node) => { + return toNode("h2", {}, node.content); + }; + + const h3 = (node) => { + return toNode("h3", {}, node.content); + }; + + const h4 = (node) => { + return toNode("h4", {}, node.content); + }; + + const h5 = (node) => { + return toNode("h5", {}, node.content); + }; + + const h6 = (node) => { + return toNode("h6", {}, node.content); + }; + + /** + * Parse the user provided height and return a valid height value + * @param {Number} heightValue obtains the input of the user entered height (default is 700) + * @returns A validated number less than 0. + */ + function parseHeight$1(heightValue) { + const maxHeight = 700; + const parsedHeight = + heightValue && heightValue.trim() !== "" ? heightValue.replace(/[^\d.]/g, "") : 0; + + if (parsedHeight && parsedHeight >= 0 && parsedHeight <= maxHeight) { + return parsedHeight; + } else { + // if the value = 0 then nothing will be returned + return parsedHeight === 0 ? 0 : maxHeight; + } + } + + /** + * @file Adds [heightrestrict] to bbcode + * @example [heightrestrict=50]content[/heightrestrict] + */ + const heightrestrict = (node) => { + const attrs = preprocessAttr(node.attrs)._default; + const heightInput = parseHeight$1(attrs).toString(); + // Return image's default size if heightrestrict did not involve a valid value + return heightInput === "0" + ? toNode("div", { class: "bb-height-restrict" }, node.content) + : toNode( + "div", + { class: "bb-height-restrict", style: `height: ${heightInput}px;` }, + node.content, + ); + }; + + /** + * @file Adds [highlight] to bbcode + * @example [highlight]content[/highlight] + */ + const highlight = (node) => { + return toNode("span", { class: "bb-highlight" }, node.content); + }; + + /** + * @file Adds [imagefloat] to bbcode + * @exmaple [imagefloat=left]content[/imagefloat] + */ + const imagefloat = (node) => { + const attrs = preprocessAttr(node.attrs)._default || ""; + return toNode("div", { class: `bb-float-${attrs}` }, node.content); + }; + + /** + * @file Adds [justify] to bbcode + * @example [justify]content[/justify] + */ + const justify = (node) => { + return toNode("div", { class: "bb-justify" }, node.content); + }; + + /** + * @file Adds [mail] to bbcode + * @param {string} [type="send"] Denotes type of mail either send or receive + * @param {string} [person="Unknown"] Denotes the person in the To/From field + * @param {string} [subject="Empty"] Denotes the subject line of the email + * @example [mail type="send" person="John Doe" subject="Hello World"]content[/mail] + */ + + const parseEmailContent = (content) => { + return toNode("div", { class: "bb-email-content" }, content); + }; + + const parseEmailSubject = (subject) => { + return toNode("div", { class: "bb-email-subject" }, subject); + }; + + const parseEmailPerson = (person) => { + return toNode("div", { class: "bb-email-address" }, person); + }; + + const emailHeader = toNode("div", { class: "bb-email-header" }, ""); + const emailFooter = toNode( + "div", + { class: "bb-email-footer" }, + toNode("div", { class: "bb-email-button" }, "") + ); + + const mail = (node) => { + const attributes = preprocessAttr(node.attrs); + let mailAttr = { + mailOption: (attributes.type || "send").toLowerCase(), + person: attributes.person || "Unknown", + subject: attributes.subject || "Empty", + }; + + return toNode( + "div", + { + class: "bb-email", + "data-bb-email": mailAttr.mailOption, + }, + [ + emailHeader, + parseEmailPerson(mailAttr.person), + parseEmailSubject(mailAttr.subject), + parseEmailContent(node.content), + emailFooter, + ] + ); + }; + + /** + * @file Adds [newspaper] to bbcode + * @example [newspaper]content[/newspaper] + */ + const newspaper = (node) => { + return toNode("div", { class: "bb-newspaper" }, node.content); + }; + + /** + * Creates a line break html
tag + */ + const br = () => { + return toNode("br", {}, null); + }; + + /** + * Disables line breaks for given content + * @example + * ``` + * [nobr]test + * test + * test + * [/nobr] + * + * test test test + * ``` + */ + const nobr = (node) => { + return { disableLineBreakConversion: true, content: node.content }; + }; + + /** + * @file Adds [note] to bbcode + * @example [note]content[/note] + */ + + const note = (node) => { + return toNode("div", { class: "bb-note" }, [ + toNode("div", { class: "bb-note-tape" }, ""), + toNode("div", { class: "bb-note-content" }, [ + node.content, + toNode("div", { class: "bb-note-footer" }, ""), + ]), + ]); + }; + + /** + * @file Adds [ooc] to bbcode + * @example [ooc]content[/ooc] + */ + const ooc = (node) => { + return toNode( + "div", + { + class: "bb-ooc", + }, + node.content, + ); + }; + + /** + * @file Adds [pindent] to bbcode + * @example [pindent]content[/pindent] + */ + const pindent = (node) => { + return toNode("span", { class: "bb-pindent" }, node.content); + }; + + /** + * [plain] bbcode tag that prevents parsing of inner tags + * @example + * ``` + * [plain]This is [b]bold[/b] and [i]italic[/i][/plain] + * ``` + * outputs to + * ``` + * This is [b]bold[/b] and [i]italic[/i] + * ``` + */ + const plain = (node) => { + return node.content; + }; + + /** + * Add [print] tag + * @example [print=lined]content[/print] + */ + const print = (node) => { + const defaultOp = "print"; + const printAttr = (preprocessAttr(node.attrs)._default || defaultOp).toLowerCase(); + + const OPTIONS = ["print", "line", "graph", "parchment"]; + + // Default to print if option is not valid + const printOption = OPTIONS.includes(printAttr) ? printAttr : defaultOp; + + return toNode( + "div", + { class: printOption === defaultOp ? `bb-print` : `bb-print-${printOption}` }, + node.content, + ); + }; + + /** + * @file Adds [progress] to bbcode + * @exmaple [progress=percentageInt]content[/progress] + */ + const progress = (node) => { + const percentageInt = preprocessAttr(node.attrs)._default; + return { + tag: "div", + attrs: { + class: "bb-progress", + }, + content: [ + { + tag: "div", + attrs: { + class: "bb-progress-text" + }, + content: node.content + }, + { + tag: "div", + attrs: { + class: "bb-progress-bar", + style: `width: calc(${percentageInt}% - 6px)` + } + }, + { + tag: "div", + attrs: { + class: "bb-progress-bar-other" + } + } + ], + }; + }; + + /** + * @file Adds [thinprogress] to bbcode + * @exmaple [thinprogress=percentageInt]content[/progthinprogressress] + */ + const thinprogress = (node) => { + const percentageInt = preprocessAttr(node.attrs)._default; + return { + tag: "div", + attrs: { + class: "bb-progress-thin", + }, + content: [ + { + tag: "div", + attrs: { + class: "bb-progress-text" + }, + content: node.content + }, + { + tag: "div", + attrs: { + class: "bb-progress-bar", + style: `width: calc(${percentageInt}% - 6px)` + } + }, + { + tag: "div", + attrs: { + class: "bb-progress-bar-other" + } + } + ], + }; + }; + + /** + * Parse the user provided height and return a valid height value + * @param {Number} heightValue obtains the input of the user entered height (default is 700) + * @returns A validated number less than 0. + */ + function parseHeight(heightValue) { + const maxHeight = 700; + const parsedHeight = + heightValue && heightValue.trim() !== "" ? heightValue.replace(/[^\d.]/g, "") : 0; + + if (parsedHeight && parsedHeight >= 0 && parsedHeight <= maxHeight) { + return parsedHeight; + } else { + // if the value = 0 then nothing will be returned + return parsedHeight === 0 ? 0 : maxHeight; + } + } + + /** + * @file Adds [scroll] to bbcode + * @example [scroll]content[/scroll] + */ + const scroll = (node) => { + const attrs = preprocessAttr(node.attrs)._default; + const heightInput = parseHeight(attrs); + return toNode("div", { class: "bb-scroll", style: `height: ${heightInput}px` }, node.content); + }; + + const side = (node) => { + const attrs = preprocessAttr(node.attrs)._default || "left"; + return toNode("div", { class: "bb-side", "data-side": attrs }, node.content); + }; + + /** + * Parses an inputted size value and returns the formatted valid font size + * @param {string} fontValue the input of the size + */ + function parseFontSize(fontValue) { + let value; + let fontSize = { valid: true }; + const parsedSize = /(\d+\.?\d?)(px|rem)?/i.exec(fontValue); + const sizeRanges = { + px_max: 36, + px_min: 8, + rem_max: 3, + rem_min: 0.2, + unitless_max: 7, + unitless_min: 1, + }; + + if (parsedSize && (value = parsedSize[1])) { + fontSize.unit = (parsedSize[2] || "").toLowerCase(); + switch (fontSize.unit) { + case "px": + if (value > sizeRanges.px_max) { + value = sizeRanges.px_max; + } else if (value < sizeRanges.px_min) { + value = sizeRanges.px_min; + } + break; + case "rem": + if (value > sizeRanges.rem_max) { + value = sizeRanges.rem_max; + } else if (value < sizeRanges.rem_min) { + value = sizeRanges.rem_min; + } + break; + default: + if ((fontSize.valid = fontValue.length === value.length)) { + if (value > sizeRanges.unitless_max) { + value = sizeRanges.unitless_max; + } else if (value < sizeRanges.unitless_min) { + value = sizeRanges.unitless_min; + } + } + break; + } + + fontSize.value = value; + } + return fontSize; + } + + + /** + * @file Adds [row][column] to bbcode + * @example Adds [row][column][/column][/row] + */ + const rowcolumn = { + row: (node) => toNode("div", { class: "bb-row" }, node.content), + column: (node) => { + const columnAttrs = preprocessAttr(node.attrs)._default || "8"; + const columnStyle = columnAttrs.startsWith("span") ? `column-width-${columnAttrs}` : `column-width-span${columnAttrs}`; + return toNode("div", { class: `bb-column`, "data-span": columnStyle }, node.content); + } + } + + const size = (node) => { + const input = preprocessAttr(node.attrs)._default; + const fontSize = parseFontSize(input); + if (!fontSize.valid) { + return node.content; + } + let outputAttr = {}; + if (fontSize.unit) { + outputAttr = { style: `font-size: ${fontSize.value}${fontSize.unit}` }; + } else { + outputAttr = { "data-size": fontSize.value }; + } + return toNode("span", outputAttr, node.content); + }; + + /** + * @file Adds subscript to BBCode + * @example [sub]content[/sub] + */ + + const sub = (node) => { + return toNode("sub", {}, node.content); + }; + + /** + * @file Adds superscript to bbcode + * @example [sup]content[/sup] + */ + + const sup = (node) => { + return toNode("sup", {}, node.content); + }; + + /** + * @file Adds [spoiler] and [inlinespoiler] to bbcode + * + * Defaults to "Spoiler" name if no title provided + * + * @example `[spoiler=Title]text[/spoiler]` + * @example `[inlinespoiler]hidden content[/inlinespoiler] + */ + + const spoiler = (node) => { + const providedTitle = preprocessAttr(node.attrs)._default; + const title = "Spoiler" + (providedTitle ? `: ${providedTitle}` : ""); + + /** + *
+ * Title + *
+ * lorem ipsum + *
+ *
+ */ + return { + tag: "details", + attrs: { + class: "bb-spoiler", + }, + content: [ + { + tag: "summary", + content: title, + }, + { + tag: "div", + attrs: { + class: "bb-spoiler-content", + }, + content: node.content, + }, + ], + }; + }; + + const inlinespoiler = (node) => { + return toNode("span", { class: "bb-inline-spoiler" }, node.content); + }; + + /** + * @file Adds textmessage to bbcode + * @exmaple [textmessage=Recipient][message=them]Hi [/message][message=me] Hey![/message][/textmessage] + */ + + const ACCEPTED_OPTIONS = ["me", "them", "right", "left"]; + const textmessage = { + textmessage: (node) => { + const attr = preprocessAttr(node.attrs)._default || "Recipient"; + const recipient = attr && attr.trim() !== "" ? attr : "Recipient"; + return { + tag: "div", + attrs: { + class: "bb-textmessage", + }, + content: [ + { + tag: "div", + attrs: { + class: "bb-textmessage-name", + }, + content: recipient, + }, + { + tag: "div", + attrs: { + class: "bb-textmessage-overflow", + }, + content: [ + { + tag: "div", + attrs: { + class: "bb-textmessage-content", + }, + content: node.content, + }, + ], + }, + ], + }; + }, + message: (node) => { + let option = preprocessAttr(node.attrs)._default.toLowerCase(); + if (!ACCEPTED_OPTIONS.includes(option) || option === "right") { + option = "me"; + } + if (option === "left") { + option = "them"; + } + + const senderAttrs = option === "me" ? "bb-message-me" : "bb-message-them"; + return { + tag: "div", + attrs: { + class: senderAttrs, + }, + content: [ + { + tag: "div", + attrs: { + class: "bb-message-content", + }, + content: node.content, + }, + ], + }; + }, + }; + + const N = '\n'; + const TAB = '\t'; + const EQ = '='; + const QUOTEMARK = '"'; + const SPACE = ' '; + const OPEN_BRAKET = '['; + const CLOSE_BRAKET = ']'; + const SLASH = '/'; + const BACKSLASH = '\\'; + + const isTagNode = (el)=>typeof el === 'object' && !!el.tag; + const isStringNode = (el)=>typeof el === 'string'; + const isEOL = (el)=>el === N; + const keysReduce = (obj, reduce, def)=>Object.keys(obj).reduce(reduce, def); + const getNodeLength = (node)=>{ + if (isTagNode(node)) { + return node.content.reduce((count, contentNode)=>count + getNodeLength(contentNode), 0); + } + if (isStringNode(node)) { + return node.length; + } + return 0; + }; + /** + * Appends value to Tag Node + * @param {TagNode} node + * @param value + */ const appendToNode = (node, value)=>{ + node.content.push(value); + }; + /** + * Replaces " to &qquot; + * @param {String} value + */ const escapeHTML = (value)=>value.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, ''')// eslint-disable-next-line no-script-url + .replace(/(javascript|data|vbscript):/gi, '$1%3A'); + /** + * Acept name and value and return valid html5 attribute string + * @param {String} name + * @param {String} value + * @return {string} + */ const attrValue = (name, value)=>{ + const type = typeof value; + const types = { + boolean: ()=>value ? `${name}` : '', + number: ()=>`${name}="${value}"`, + string: ()=>`${name}="${escapeHTML(value)}"`, + object: ()=>`${name}="${escapeHTML(JSON.stringify(value))}"` + }; + return types[type] ? types[type]() : ''; + }; + /** + * Transforms attrs to html params string + * @param values + */ const attrsToString = (values)=>{ + // To avoid some malformed attributes + if (values == null) { + return ''; + } + return keysReduce(values, (arr, key)=>[ + ...arr, + attrValue(key, values[key]) + ], [ + '' + ]).join(' '); + }; + /** + * Gets value from + * @example + * getUniqAttr({ 'foo': true, 'bar': bar' }) => 'bar' + * @param attrs + * @returns {string} + */ const getUniqAttr = (attrs)=>keysReduce(attrs, (res, key)=>attrs[key] === key ? attrs[key] : null, null); + + const getTagAttrs = (tag, params)=>{ + const uniqAattr = getUniqAttr(params); + if (uniqAattr) { + const tagAttr = attrValue(tag, uniqAattr); + const attrs = { + ...params + }; + delete attrs[uniqAattr]; + const attrsStr = attrsToString(attrs); + return `${tagAttr}${attrsStr}`; + } + return `${tag}${attrsToString(params)}`; + }; + class TagNode { + attr(name, value) { + if (typeof value !== 'undefined') { + this.attrs[name] = value; + } + return this.attrs[name]; + } + append(value) { + return appendToNode(this, value); + } + get length() { + return getNodeLength(this); + } + toTagStart({ openTag =OPEN_BRAKET , closeTag =CLOSE_BRAKET } = {}) { + const tagAttrs = getTagAttrs(this.tag, this.attrs); + return `${openTag}${tagAttrs}${closeTag}`; + } + toTagEnd({ openTag =OPEN_BRAKET , closeTag =CLOSE_BRAKET } = {}) { + return `${openTag}${SLASH}${this.tag}${closeTag}`; + } + toTagNode() { + return new TagNode(this.tag.toLowerCase(), this.attrs, this.content); + } + toString({ openTag =OPEN_BRAKET , closeTag =CLOSE_BRAKET } = {}) { + const isEmpty = this.content.length === 0; + const content = this.content.reduce((r, node)=>r + node.toString({ + openTag, + closeTag + }), ''); + const tagStart = this.toTagStart({ + openTag, + closeTag + }); + if (isEmpty) { + return tagStart; + } + return `${tagStart}${content}${this.toTagEnd({ + openTag, + closeTag + })}`; + } + constructor(tag, attrs, content){ + this.tag = tag; + this.attrs = attrs; + this.content = Array.isArray(content) ? content : [ + content + ]; + } + } + TagNode.create = (tag, attrs = {}, content = [])=>new TagNode(tag, attrs, content); + TagNode.isOf = (node, type)=>node.tag === type; + + /** + * @file Adds [tabs][tab] to bbcode + * @example [tabs][tab=name 1]content[/tab][tab=name 2]content[/tab][/tabs] + */ + const tabs = (node) => { + const tabsList = node.content.filter( + (contentNode) => isTagNode(contentNode) && contentNode.tag === "tab", + ); + const groupId = generateGUID(); + tabsList.forEach((tabNode) => { + tabNode.isValid = true; + tabNode.groupId = groupId; + }); + if (!tabsList.length) { + // no [tab] tags found + return [toOriginalStartTag(node), ...node.content, node.toTagEnd()]; + } + tabsList[0].open = true; + + return toNode( + "div", + { + class: "bb-tabs", + }, + tabsList, + ); + }; + + /** + * [tab=name]content[/tab] + * [tab name="name" style="style"]content[/tab] + */ + const tab = (node) => { + if (!node.isValid) { + // not inside a [tabs] tag + return [toOriginalStartTag(node), ...node.content, node.toTagEnd()]; + } + const attrs = preprocessAttr(node.attrs); + const name = attrs._default || attrs.name || "Tab"; + const tabId = `tab-${name.replace(/\W/g, "_")}-${generateGUID()}`; + return [ + toNode("input", { + type: "radio", + id: tabId, + name: "tab-group-" + node.groupId, + class: "bb-tab", + checked: node.open, + }), + toNode( + "label", + { + class: "bb-tab-label", + for: tabId, + style: attrs.style, + }, + name, + ), + toNode( + "div", + { + class: "bb-tab-content", + }, + node.content, + ), + ]; + }; + + const SLIDE_TITLE_OPEN = Symbol("slide-title-open"); + const SLIDE_TITLE_CLOSE = Symbol("slide-title-close"); + const SLIDE_CLOSE = Symbol("slide-close"); + const SLIDE_REGEX = + /(?\{slide=)|(?\})|(?\{\/slide\})/i; + + /** + * Adds the accordion tag + * [accordion]{slide=name}content{/slide}[/accordion] + * + * [accordion][slide=name]content[/slide][/accordion] + */ + const accordion = (node) => { + const groupId = generateGUID(); + + // add support for existing {slide} tags style, due to copious amounts of existing content + // also the only way to get true custom content inside a slide due to nesting limitations + const markedContent = generateSlideMarkersFromContent(node.content); + const generatedSlides = generateSlidesFromMarkers(markedContent); + + const filteredContent = generatedSlides + .filter((n) => isTagNode(n) && n.tag === "slide") + .map((content) => { + content.isValid = true; + content.groupId = groupId; + return content; + }); + if (!filteredContent.length) { + // no [slide] tags found + return [toOriginalStartTag(node), ...node.content, node.toTagEnd()]; + } + + const attrs = preprocessAttr(node.attrs); + + if (attrs._default) { + /** @type {string[]} */ + const customSettings = attrs._default.split("|").map((s) => s.trim()); + if (customSettings.includes("bright")) { + attrs.bright = true; + } + if (customSettings.includes("bcenter")) { + attrs.bcenter = true; + } + if (customSettings.includes("bleft")) { + attrs.bleft = true; + } + if (customSettings.includes("fleft")) { + attrs.fleft = true; + } + if (customSettings.includes("fright")) { + attrs.fright = true; + } + if ( + customSettings.some((s) => s.endsWith("px")) || + customSettings.some((s) => s.endsWith("%")) + ) { + attrs.width = customSettings.find((s) => s.endsWith("px") || s.endsWith("%")); + } + } + + let classes = Object.keys(attrs) + .filter((s) => ["bright", "bcenter", "bleft", "fleft", "fright"].includes(s)) + .join(" "); + let style = ""; + if (attrs.width?.endsWith("px") || attrs.width?.endsWith("%")) { + style = `width: ${attrs.width};`; + } + return toNode( + "div", + { class: "bb-accordion " + classes, "data-group-id": groupId, style }, + filteredContent, + ); + }; + + /** + * Locates and splits all {slide} tag components into their respective parts while preserving remaining content + * @param {(TagNode|string)[]} contentArr node content of the accordion tag + * + * @example + * ``` + * ["{slide=test}", "lorem ipsum", "{/slide}"] + * ``` + * becomes + * ``` + * [SLIDE_TITLE_OPEN, "test", SLIDE_TITLE_CLOSE, "lorem ipsum", SLIDE_CLOSE] + * ``` + */ + function generateSlideMarkersFromContent(contentArr) { + contentArr = [...contentArr]; // shallow clone. object nodes are not modified anyway + + const newArr = []; + while (contentArr.length > 0) { + const content = contentArr[0]; + if (isTagNode(content)) { + newArr.push(contentArr.shift()); + continue; + } + const foundIndex = regexIndexOf(content, SLIDE_REGEX); + if (foundIndex === -1) { + newArr.push(contentArr.shift()); + continue; + } + const match = content.match(SLIDE_REGEX); + const preContent = content.slice(0, foundIndex); + const postContent = content.slice(foundIndex + match[0].length); + if (preContent.length) { + newArr.push(preContent); + } + if (match.groups.slideTitleOpen) { + newArr.push(SLIDE_TITLE_OPEN); + } + if (match.groups.slideTitleClose) { + newArr.push(SLIDE_TITLE_CLOSE); + } + if (match.groups.slideClose) { + newArr.push(SLIDE_CLOSE); + } + if (postContent.length) { + contentArr[0] = postContent; + } else { + contentArr.shift(); + } + } + + return newArr; + } + + /** + * Generates slide nodes from markers + * @param {(string | typeof SLIDE_TITLE_OPEN | typeof SLIDE_TITLE_CLOSE | typeof SLIDE_CLOSE | TagNode)[]} markedContent + */ + function generateSlidesFromMarkers(markedContent) { + const nodes = []; + let currentSlide = null; + /** @type {typeof SLIDE_TITLE_OPEN | typeof SLIDE_TITLE_CLOSE | null} */ + let prevMarker = null; + for (const content of markedContent) { + if (content === SLIDE_TITLE_OPEN && prevMarker === null) { + currentSlide = TagNode.create("slide"); + currentSlide.content = []; + currentSlide.customTitle = []; + prevMarker = SLIDE_TITLE_OPEN; + } else if (content === SLIDE_TITLE_CLOSE && prevMarker === SLIDE_TITLE_OPEN) { + prevMarker = SLIDE_TITLE_CLOSE; + continue; + } else if (content === SLIDE_CLOSE && currentSlide && prevMarker === SLIDE_TITLE_CLOSE) { + nodes.push(currentSlide); + currentSlide = null; + prevMarker = null; + } else if (currentSlide) { + if (prevMarker === SLIDE_TITLE_OPEN) { + currentSlide.customTitle.push(markerToString(content)); + } else { + currentSlide.content.push(markerToString(content)); + } + } else { + // no slide open, just add content + nodes.push(markerToString(content)); + } + } + return nodes; + } + + /** + * Processes content into a string. Catches stray markers and converts them back into a string + * @param {string | typeof SLIDE_TITLE_OPEN | typeof SLIDE_TITLE_CLOSE | typeof SLIDE_CLOSE} marker + * @returns expected string + */ + function markerToString(marker) { + switch (marker) { + case SLIDE_TITLE_OPEN: + return "{slide="; + case SLIDE_TITLE_CLOSE: + return "}"; + case SLIDE_CLOSE: + return "{/slide}"; + default: + return marker; + } + } + + const slide = (node) => { + if (!node.isValid) { + // not inside an [accordion] tag + return [toOriginalStartTag(node), ...node.content, node.toTagEnd()]; + } + const attrs = preprocessAttr(node.attrs); + let title = [attrs.title || attrs._default || "Slide"]; + let isOpen = !!attrs.open || false; + let titleAlign = attrs.left ? "left" : attrs.right ? "right" : attrs.center ? "center" : "left"; + if (node.customTitle?.length) { + // slide was created from markers + title = node.customTitle; + // pull out old options from title if they exist + const possibleOptions = title + .filter((t) => typeof t === "string") + .join("") + .toLowerCase() + .split("|") + .map((s) => s.trim()); + if (possibleOptions.includes("open")) { + isOpen = true; + } + if (possibleOptions.includes("right")) { + titleAlign = "right"; + } + if (possibleOptions.includes("center")) { + titleAlign = "center"; + } + if (possibleOptions.includes("left")) { + titleAlign = "left"; + } + title = title.map((t) => { + if (isStringNode(t)) { + t = t.replace(/\|(open|right|center|left)/gi, ""); + } + return t; + }); + } + return [ + { + tag: "details", + attrs: { class: "bb-slide", open: isOpen }, + content: [ + { + tag: "summary", + attrs: { + class: "bb-slide-title", + style: `text-align: ${titleAlign}; ${attrs.style || ""}`, + }, + content: title, + }, + { + tag: "div", + attrs: { class: "bb-slide-content" }, + content: node.content, + }, + ], + }, + ]; + }; + + const accordionTags = { accordion, slide }; + + const tags = { + ...accordionTags, + ...alignment, + ...anchor, + bg, + block, + blockquote, + border, + br, + centerblock, + check, + code, + color, + comment, + divide, + fieldset, + font, + h, + h1, + h2, + h3, + h4, + h5, + h6, + heightrestrict, + highlight, + icode, + imagefloat, + inlinespoiler, + justify, + mail, + newspaper, + nobr, + note, + ooc, + pindent, + plain, + print, + progress, + ...rowcolumn, + thinprogress, + savenl, + sh, + scroll, + side, + size, + spoiler, + sub, + sup, + tab, + tabs, + ...textmessage, + }; + + const availableTags = Object.keys(tags); + const preventParsing = ["plain", "code", "icode"]; + + const preset = createPreset(tags); + + // type, value, line, row, + const TOKEN_TYPE_ID = 'type'; // 0; + const TOKEN_VALUE_ID = 'value'; // 1; + const TOKEN_COLUMN_ID = 'row'; // 2; + const TOKEN_LINE_ID = 'line'; // 3; + const TOKEN_TYPE_WORD = 1; // 'word'; + const TOKEN_TYPE_TAG = 2; // 'tag'; + const TOKEN_TYPE_ATTR_NAME = 3; // 'attr-name'; + const TOKEN_TYPE_ATTR_VALUE = 4; // 'attr-value'; + const TOKEN_TYPE_SPACE = 5; // 'space'; + const TOKEN_TYPE_NEW_LINE = 6; // 'new-line'; + /** + * @param {Token} token + * @returns {string} + */ const getTokenValue = (token)=>{ + if (token && typeof token[TOKEN_VALUE_ID] !== 'undefined') { + return token[TOKEN_VALUE_ID]; + } + return ''; + }; + /** + * @param {Token}token + * @returns {number} + */ const getTokenLine = (token)=>token && token[TOKEN_LINE_ID] || 0; + const getTokenColumn = (token)=>token && token[TOKEN_COLUMN_ID] || 0; + /** + * @param {Token} token + * @returns {boolean} + */ const isTextToken = (token)=>{ + if (token && typeof token[TOKEN_TYPE_ID] !== 'undefined') { + return token[TOKEN_TYPE_ID] === TOKEN_TYPE_SPACE || token[TOKEN_TYPE_ID] === TOKEN_TYPE_NEW_LINE || token[TOKEN_TYPE_ID] === TOKEN_TYPE_WORD; + } + return false; + }; + /** + * @param {Token} token + * @returns {boolean} + */ const isTagToken = (token)=>{ + if (token && typeof token[TOKEN_TYPE_ID] !== 'undefined') { + return token[TOKEN_TYPE_ID] === TOKEN_TYPE_TAG; + } + return false; + }; + const isTagEnd = (token)=>getTokenValue(token).charCodeAt(0) === SLASH.charCodeAt(0); + const isTagStart = (token)=>!isTagEnd(token); + const isAttrNameToken = (token)=>{ + if (token && typeof token[TOKEN_TYPE_ID] !== 'undefined') { + return token[TOKEN_TYPE_ID] === TOKEN_TYPE_ATTR_NAME; + } + return false; + }; + /** + * @param {Token} token + * @returns {boolean} + */ const isAttrValueToken = (token)=>{ + if (token && typeof token[TOKEN_TYPE_ID] !== 'undefined') { + return token[TOKEN_TYPE_ID] === TOKEN_TYPE_ATTR_VALUE; + } + return false; + }; + const getTagName = (token)=>{ + const value = getTokenValue(token); + return isTagEnd(token) ? value.slice(1) : value; + }; + const convertTagToText = (token)=>{ + let text = OPEN_BRAKET; + text += getTokenValue(token); + text += CLOSE_BRAKET; + return text; + }; + class Token { + isEmpty() { + // eslint-disable-next-line no-restricted-globals + return isNaN(this[TOKEN_TYPE_ID]); + } + isText() { + return isTextToken(this); + } + isTag() { + return isTagToken(this); + } + isAttrName() { + return isAttrNameToken(this); + } + isAttrValue() { + return isAttrValueToken(this); + } + isStart() { + return isTagStart(this); + } + isEnd() { + return isTagEnd(this); + } + getName() { + return getTagName(this); + } + getValue() { + return getTokenValue(this); + } + getLine() { + return getTokenLine(this); + } + getColumn() { + return getTokenColumn(this); + } + toString() { + return convertTagToText(this); + } + /** + * @param {String} type + * @param {String} value + * @param line + * @param row + */ constructor(type, value, line, row){ + this[TOKEN_TYPE_ID] = Number(type); + this[TOKEN_VALUE_ID] = String(value); + this[TOKEN_LINE_ID] = Number(line); + this[TOKEN_COLUMN_ID] = Number(row); + } + } + const TYPE_WORD = TOKEN_TYPE_WORD; + const TYPE_TAG = TOKEN_TYPE_TAG; + const TYPE_ATTR_NAME = TOKEN_TYPE_ATTR_NAME; + const TYPE_ATTR_VALUE = TOKEN_TYPE_ATTR_VALUE; + const TYPE_SPACE = TOKEN_TYPE_SPACE; + const TYPE_NEW_LINE = TOKEN_TYPE_NEW_LINE; + + function CharGrabber(source, options) { + const cursor = { + pos: 0, + len: source.length + }; + const substrUntilChar = (char)=>{ + const { pos } = cursor; + const idx = source.indexOf(char, pos); + return idx >= 0 ? source.substring(pos, idx) : ''; + }; + const includes = (val)=>source.indexOf(val, cursor.pos) >= 0; + const hasNext = ()=>cursor.len > cursor.pos; + const isLast = ()=>cursor.pos === cursor.len; + const skip = (num = 1, silent)=>{ + cursor.pos += num; + if (options && options.onSkip && !silent) { + options.onSkip(); + } + }; + const rest = ()=>source.substring(cursor.pos); + const grabN = (num = 0)=>source.substring(cursor.pos, cursor.pos + num); + const curr = ()=>source[cursor.pos]; + const prev = ()=>{ + const prevPos = cursor.pos - 1; + return typeof source[prevPos] !== 'undefined' ? source[prevPos] : null; + }; + const next = ()=>{ + const nextPos = cursor.pos + 1; + return nextPos <= source.length - 1 ? source[nextPos] : null; + }; + const grabWhile = (cond, silent)=>{ + let start = 0; + if (hasNext()) { + start = cursor.pos; + while(hasNext() && cond(curr())){ + skip(1, silent); + } + } + return source.substring(start, cursor.pos); + }; + /** + * @type {skip} + */ this.skip = skip; + /** + * @returns {Boolean} + */ this.hasNext = hasNext; + /** + * @returns {String} + */ this.getCurr = curr; + /** + * @returns {String} + */ this.getRest = rest; + /** + * @returns {String} + */ this.getNext = next; + /** + * @returns {String} + */ this.getPrev = prev; + /** + * @returns {Boolean} + */ this.isLast = isLast; + /** + * @returns {Boolean} + */ this.includes = includes; + /** + * @param {Function} cond + * @param {Boolean} silent + * @return {String} + */ this.grabWhile = grabWhile; + /** + * @param {Number} num + * @return {String} + */ this.grabN = grabN; + /** + * Grabs rest of string until it find a char + * @param {String} char + * @return {String} + */ this.substrUntilChar = substrUntilChar; + } + /** + * Creates a grabber wrapper for source string, that helps to iterate over string char by char + * @param {String} source + * @param {Object} options + * @param {Function} options.onSkip + * @return CharGrabber + */ const createCharGrabber = (source, options)=>new CharGrabber(source, options); + /** + * Trims string from start and end by char + * @example + * trimChar('*hello*', '*') ==> 'hello' + * @param {String} str + * @param {String} charToRemove + * @returns {String} + */ const trimChar = (str, charToRemove)=>{ + while(str.charAt(0) === charToRemove){ + // eslint-disable-next-line no-param-reassign + str = str.substring(1); + } + while(str.charAt(str.length - 1) === charToRemove){ + // eslint-disable-next-line no-param-reassign + str = str.substring(0, str.length - 1); + } + return str; + }; + /** + * Unquotes \" to " + * @param str + * @return {String} + */ const unquote = (str)=>str.replace(BACKSLASH + QUOTEMARK, QUOTEMARK); + function NodeList(values = []) { + const nodes = values; + const getLast = ()=>Array.isArray(nodes) && nodes.length > 0 && typeof nodes[nodes.length - 1] !== 'undefined' ? nodes[nodes.length - 1] : null; + const flushLast = ()=>nodes.length ? nodes.pop() : false; + const push = (value)=>nodes.push(value); + const toArray = ()=>nodes; + this.push = push; + this.toArray = toArray; + this.getLast = getLast; + this.flushLast = flushLast; + } + /** + * + * @param values + * @return {NodeList} + */ const createList = (values = [])=>new NodeList(values); + + // for cases + const EM = '!'; + /** + * Creates a Token entity class + * @param {Number} type + * @param {String} value + * @param {Number} r line number + * @param {Number} cl char number in line + */ const createToken = (type, value, r = 0, cl = 0)=>new Token(type, value, r, cl); + /** + * @typedef {Object} Lexer + * @property {Function} tokenize + * @property {Function} isTokenNested + */ /** + * @param {String} buffer + * @param {Object} options + * @param {Function} options.onToken + * @param {String} options.openTag + * @param {String} options.closeTag + * @param {Boolean} options.enableEscapeTags + * @return {Lexer} + */ function createLexer(buffer, options = {}) { + const STATE_WORD = 0; + const STATE_TAG = 1; + const STATE_TAG_ATTRS = 2; + const TAG_STATE_NAME = 0; + const TAG_STATE_ATTR = 1; + const TAG_STATE_VALUE = 2; + let row = 0; + let col = 0; + let tokenIndex = -1; + let stateMode = STATE_WORD; + let tagMode = TAG_STATE_NAME; + let contextFreeTag = ''; + const tokens = new Array(Math.floor(buffer.length)); + const openTag = options.openTag || OPEN_BRAKET; + const closeTag = options.closeTag || CLOSE_BRAKET; + const escapeTags = !!options.enableEscapeTags; + const contextFreeTags = options.contextFreeTags || []; + const onToken = options.onToken || (()=>{}); + const RESERVED_CHARS = [ + closeTag, + openTag, + QUOTEMARK, + BACKSLASH, + SPACE, + TAB, + EQ, + N, + EM + ]; + const NOT_CHAR_TOKENS = [ + openTag, + SPACE, + TAB, + N + ]; + const WHITESPACES = [ + SPACE, + TAB + ]; + const SPECIAL_CHARS = [ + EQ, + SPACE, + TAB + ]; + const isCharReserved = (char)=>RESERVED_CHARS.indexOf(char) >= 0; + const isNewLine = (char)=>char === N; + const isWhiteSpace = (char)=>WHITESPACES.indexOf(char) >= 0; + const isCharToken = (char)=>NOT_CHAR_TOKENS.indexOf(char) === -1; + const isSpecialChar = (char)=>SPECIAL_CHARS.indexOf(char) >= 0; + const isEscapableChar = (char)=>char === openTag || char === closeTag || char === BACKSLASH; + const isEscapeChar = (char)=>char === BACKSLASH; + const onSkip = ()=>{ + col++; + }; + const unq = (val)=>unquote(trimChar(val, QUOTEMARK)); + const checkContextFreeMode = (name, isClosingTag)=>{ + if (contextFreeTag !== '' && isClosingTag) { + contextFreeTag = ''; + } + if (contextFreeTag === '' && contextFreeTags.includes(name)) { + contextFreeTag = name; + } + }; + const chars = createCharGrabber(buffer, { + onSkip + }); + /** + * Emits newly created token to subscriber + * @param {Number} type + * @param {String} value + */ function emitToken(type, value) { + const token = createToken(type, value, row, col); + onToken(token); + tokenIndex += 1; + tokens[tokenIndex] = token; + } + function nextTagState(tagChars, isSingleValueTag) { + if (tagMode === TAG_STATE_ATTR) { + const validAttrName = (char)=>!(char === EQ || isWhiteSpace(char)); + const name = tagChars.grabWhile(validAttrName); + const isEnd = tagChars.isLast(); + const isValue = tagChars.getCurr() !== EQ; + tagChars.skip(); + if (isEnd || isValue) { + emitToken(TYPE_ATTR_VALUE, unq(name)); + } else { + emitToken(TYPE_ATTR_NAME, name); + } + if (isEnd) { + return TAG_STATE_NAME; + } + if (isValue) { + return TAG_STATE_ATTR; + } + return TAG_STATE_VALUE; + } + if (tagMode === TAG_STATE_VALUE) { + let stateSpecial = false; + const validAttrValue = (char)=>{ + // const isEQ = char === EQ; + const isQM = char === QUOTEMARK; + const prevChar = tagChars.getPrev(); + const nextChar = tagChars.getNext(); + const isPrevSLASH = prevChar === BACKSLASH; + const isNextEQ = nextChar === EQ; + const isWS = isWhiteSpace(char); + // const isPrevWS = isWhiteSpace(prevChar); + const isNextWS = isWhiteSpace(nextChar); + if (stateSpecial && isSpecialChar(char)) { + return true; + } + if (isQM && !isPrevSLASH) { + stateSpecial = !stateSpecial; + if (!stateSpecial && !(isNextEQ || isNextWS)) { + return false; + } + } + if (!isSingleValueTag) { + return isWS === false; + // return (isEQ || isWS) === false; + } + return true; + }; + const name1 = tagChars.grabWhile(validAttrValue); + tagChars.skip(); + emitToken(TYPE_ATTR_VALUE, unq(name1)); + if (tagChars.isLast()) { + return TAG_STATE_NAME; + } + return TAG_STATE_ATTR; + } + const validName = (char)=>!(char === EQ || isWhiteSpace(char) || tagChars.isLast()); + const name2 = tagChars.grabWhile(validName); + emitToken(TYPE_TAG, name2); + checkContextFreeMode(name2); + tagChars.skip(); + // in cases when we has [url=someval]GET[/url] and we dont need to parse all + if (isSingleValueTag) { + return TAG_STATE_VALUE; + } + const hasEQ = tagChars.includes(EQ); + return hasEQ ? TAG_STATE_ATTR : TAG_STATE_VALUE; + } + function stateTag() { + const currChar = chars.getCurr(); + const nextChar = chars.getNext(); + chars.skip(); + // detect case where we have '[My word [tag][/tag]' or we have '[My last line word' + const substr = chars.substrUntilChar(closeTag); + const hasInvalidChars = substr.length === 0 || substr.indexOf(openTag) >= 0; + if (isCharReserved(nextChar) || hasInvalidChars || chars.isLast()) { + emitToken(TYPE_WORD, currChar); + return STATE_WORD; + } + // [myTag ] + const isNoAttrsInTag = substr.indexOf(EQ) === -1; + // [/myTag] + const isClosingTag = substr[0] === SLASH; + if (isNoAttrsInTag || isClosingTag) { + const name = chars.grabWhile((char)=>char !== closeTag); + chars.skip(); // skip closeTag + emitToken(TYPE_TAG, name); + checkContextFreeMode(name, isClosingTag); + return STATE_WORD; + } + return STATE_TAG_ATTRS; + } + function stateAttrs() { + const silent = true; + const tagStr = chars.grabWhile((char)=>char !== closeTag, silent); + const tagGrabber = createCharGrabber(tagStr, { + onSkip + }); + const hasSpace = tagGrabber.includes(SPACE); + tagMode = TAG_STATE_NAME; + while(tagGrabber.hasNext()){ + tagMode = nextTagState(tagGrabber, !hasSpace); + } + chars.skip(); // skip closeTag + return STATE_WORD; + } + function stateWord() { + if (isNewLine(chars.getCurr())) { + emitToken(TYPE_NEW_LINE, chars.getCurr()); + chars.skip(); + col = 0; + row++; + return STATE_WORD; + } + if (isWhiteSpace(chars.getCurr())) { + const word = chars.grabWhile(isWhiteSpace); + emitToken(TYPE_SPACE, word); + return STATE_WORD; + } + if (chars.getCurr() === openTag) { + if (contextFreeTag) { + const fullTagLen = openTag.length + SLASH.length + contextFreeTag.length; + const fullTagName = `${openTag}${SLASH}${contextFreeTag}`; + const foundTag = chars.grabN(fullTagLen); + const isEndContextFreeMode = foundTag === fullTagName; + if (isEndContextFreeMode) { + return STATE_TAG; + } + } else if (chars.includes(closeTag)) { + return STATE_TAG; + } + emitToken(TYPE_WORD, chars.getCurr()); + chars.skip(); + return STATE_WORD; + } + if (escapeTags) { + if (isEscapeChar(chars.getCurr())) { + const currChar = chars.getCurr(); + const nextChar = chars.getNext(); + chars.skip(); // skip the \ without emitting anything + if (isEscapableChar(nextChar)) { + chars.skip(); // skip past the [, ] or \ as well + emitToken(TYPE_WORD, nextChar); + return STATE_WORD; + } + emitToken(TYPE_WORD, currChar); + return STATE_WORD; + } + const isChar = (char)=>isCharToken(char) && !isEscapeChar(char); + const word1 = chars.grabWhile(isChar); + emitToken(TYPE_WORD, word1); + return STATE_WORD; + } + const word2 = chars.grabWhile(isCharToken); + emitToken(TYPE_WORD, word2); + return STATE_WORD; + } + function tokenize() { + stateMode = STATE_WORD; + while(chars.hasNext()){ + switch(stateMode){ + case STATE_TAG: + stateMode = stateTag(); + break; + case STATE_TAG_ATTRS: + stateMode = stateAttrs(); + break; + case STATE_WORD: + default: + stateMode = stateWord(); + break; + } + } + tokens.length = tokenIndex + 1; + return tokens; + } + function isTokenNested(token) { + const value = openTag + SLASH + token.getValue(); + // potential bottleneck + return buffer.indexOf(value) > -1; + } + return { + tokenize, + isTokenNested + }; + } + + /** + * @public + * @param {string} input + * @param {Object} opts + * @param {Function} opts.createTokenizer + * @param {Array} opts.onlyAllowTags + * @param {Array} opts.contextFreeTags + * @param {Boolean} opts.enableEscapeTags + * @param {string} opts.openTag + * @param {string} opts.closeTag + * @return {Array} + */ const parse = (input, opts = {})=>{ + const options = opts; + const openTag = options.openTag || OPEN_BRAKET; + const closeTag = options.closeTag || CLOSE_BRAKET; + const onlyAllowTags = (options.onlyAllowTags || []).filter(Boolean).map((tag)=>tag.toLowerCase()); + let tokenizer = null; + /** + * Result AST of nodes + * @private + * @type {NodeList} + */ const nodes = createList(); + /** + * Temp buffer of nodes that's nested to another node + * @private + * @type {NodeList} + */ const nestedNodes = createList(); + /** + * Temp buffer of nodes [tag..]...[/tag] + * @private + * @type {NodeList} + */ const tagNodes = createList(); + /** + * Temp buffer of tag attributes + * @private + * @type {NodeList} + */ const tagNodesAttrName = createList(); + /** + * Cache for nested tags checks + * @type Set + */ const nestedTagsMap = new Set(); + /** + * @param {Token} token + * @returns {boolean} + */ const isTokenNested = (token)=>{ + const value = token.getValue(); + if (!nestedTagsMap.has(value) && tokenizer.isTokenNested && tokenizer.isTokenNested(token)) { + nestedTagsMap.add(value); + return true; + } + return nestedTagsMap.has(value); + }; + /** + * @private + * @param {string} tagName + * @returns {boolean} + */ const isTagNested = (tagName)=>Boolean(nestedTagsMap.has(tagName)); + /** + * @private + * @param {string} value + * @return {boolean} + */ const isAllowedTag = (value)=>{ + if (onlyAllowTags.length) { + return onlyAllowTags.indexOf(value.toLowerCase()) >= 0; + } + return true; + }; + /** + * Flushes temp tag nodes and its attributes buffers + * @private + * @return {Array} + */ const flushTagNodes = ()=>{ + if (tagNodes.flushLast()) { + tagNodesAttrName.flushLast(); + } + }; + /** + * @private + * @return {Array} + */ const getNodes = ()=>{ + const lastNestedNode = nestedNodes.getLast(); + if (lastNestedNode && Array.isArray(lastNestedNode.content)) { + return lastNestedNode.content; + } + return nodes.toArray(); + }; + /** + * @private + * @param {string|TagNode} node + * @param {boolean} isNested + */ const appendNodeAsString = (node, isNested = true)=>{ + const items = getNodes(); + if (Array.isArray(items)) { + items.push(node.toTagStart({ + openTag, + closeTag + })); + if (node.content.length) { + node.content.forEach((item)=>{ + items.push(item); + }); + if (isNested) { + items.push(node.toTagEnd({ + openTag, + closeTag + })); + } + } + } + }; + /** + * @private + * @param {string|TagNode} node + */ const appendNodes = (node)=>{ + const items = getNodes(); + if (Array.isArray(items)) { + if (isTagNode(node)) { + if (isAllowedTag(node.tag)) { + items.push(node.toTagNode()); + } else { + appendNodeAsString(node); + } + } else { + items.push(node); + } + } + }; + /** + * @private + * @param {Token} token + */ const handleTagStart = (token)=>{ + flushTagNodes(); + const tagNode = TagNode.create(token.getValue()); + const isNested = isTokenNested(token); + tagNodes.push(tagNode); + if (isNested) { + nestedNodes.push(tagNode); + } else { + appendNodes(tagNode); + } + }; + /** + * @private + * @param {Token} token + */ const handleTagEnd = (token)=>{ + flushTagNodes(); + const lastNestedNode = nestedNodes.flushLast(); + if (lastNestedNode) { + appendNodes(lastNestedNode); + } else if (typeof options.onError === 'function') { + const tag = token.getValue(); + const line = token.getLine(); + const column = token.getColumn(); + options.onError({ + message: `Inconsistent tag '${tag}' on line ${line} and column ${column}`, + tagName: tag, + lineNumber: line, + columnNumber: column + }); + } + }; + /** + * @private + * @param {Token} token + */ const handleTag = (token)=>{ + // [tag] + if (token.isStart()) { + handleTagStart(token); + } + // [/tag] + if (token.isEnd()) { + handleTagEnd(token); + } + }; + /** + * @private + * @param {Token} token + */ const handleNode = (token)=>{ + /** + * @type {TagNode} + */ const lastTagNode = tagNodes.getLast(); + const tokenValue = token.getValue(); + const isNested = isTagNested(token); + if (lastTagNode) { + if (token.isAttrName()) { + tagNodesAttrName.push(tokenValue); + lastTagNode.attr(tagNodesAttrName.getLast(), ''); + } else if (token.isAttrValue()) { + const attrName = tagNodesAttrName.getLast(); + if (attrName) { + lastTagNode.attr(attrName, tokenValue); + tagNodesAttrName.flushLast(); + } else { + lastTagNode.attr(tokenValue, tokenValue); + } + } else if (token.isText()) { + if (isNested) { + lastTagNode.append(tokenValue); + } else { + appendNodes(tokenValue); + } + } else if (token.isTag()) { + // if tag is not allowed, just past it as is + appendNodes(token.toString()); + } + } else if (token.isText()) { + appendNodes(tokenValue); + } else if (token.isTag()) { + // if tag is not allowed, just past it as is + appendNodes(token.toString()); + } + }; + /** + * @private + * @param {Token} token + */ const onToken = (token)=>{ + if (token.isTag()) { + handleTag(token); + } else { + handleNode(token); + } + }; + tokenizer = (opts.createTokenizer ? opts.createTokenizer : createLexer)(input, { + onToken, + openTag, + closeTag, + onlyAllowTags: options.onlyAllowTags, + contextFreeTags: options.contextFreeTags, + enableEscapeTags: options.enableEscapeTags + }); + // eslint-disable-next-line no-unused-vars + tokenizer.tokenize(); + // handles situations where we open tag, but forgot close them + // for ex [q]test[/q][u]some[/u][q]some [u]some[/u] // forgot to close [/q] + // so we need to flush nested content to nodes array + const lastNestedNode = nestedNodes.flushLast(); + if (lastNestedNode && isTagNested(lastNestedNode.tag)) { + appendNodeAsString(lastNestedNode, false); + } + return nodes.toArray(); + }; + + /* eslint-disable no-plusplus */ const isObj$2 = (value)=>typeof value === 'object'; + const isBool = (value)=>typeof value === 'boolean'; + function iterate(t, cb) { + const tree = t; + if (Array.isArray(tree)) { + for(let idx = 0; idx < tree.length; idx++){ + tree[idx] = iterate(cb(tree[idx]), cb); + } + } else if (tree && isObj$2(tree) && tree.content) { + iterate(tree.content, cb); + } + return tree; + } + function same(expected, actual) { + if (typeof expected !== typeof actual) { + return false; + } + if (!isObj$2(expected) || expected === null) { + return expected === actual; + } + if (Array.isArray(expected)) { + return expected.every((exp)=>[].some.call(actual, (act)=>same(exp, act))); + } + return Object.keys(expected).every((key)=>{ + const ao = actual[key]; + const eo = expected[key]; + if (isObj$2(eo) && eo !== null && ao !== null) { + return same(eo, ao); + } + if (isBool(eo)) { + return eo !== (ao === null); + } + return ao === eo; + }); + } + function match(expression, cb) { + return Array.isArray(expression) ? iterate(this, (node)=>{ + for(let idx = 0; idx < expression.length; idx++){ + if (same(expression[idx], node)) { + return cb(node); + } + } + return node; + }) : iterate(this, (node)=>same(expression, node) ? cb(node) : node); + } + + function walk$2(cb) { + return iterate(this, cb); + } + function bbob(plugs) { + const plugins = typeof plugs === 'function' ? [ + plugs + ] : plugs || []; + let options = { + skipParse: false + }; + return { + process (input, opts) { + options = opts || {}; + const parseFn = options.parser || parse; + const renderFn = options.render; + const data = options.data || null; + if (typeof parseFn !== 'function') { + throw new Error('"parser" is not a function, please pass to "process(input, { parser })" right function'); + } + let tree = options.skipParse ? input || [] : parseFn(input, options); + // raw tree before modification with plugins + const raw = tree; + tree.messages = []; + tree.options = options; + tree.walk = walk$2; + tree.match = match; + plugins.forEach((plugin)=>{ + tree = plugin(tree, { + parse: parseFn, + render: renderFn, + iterate, + match, + data + }) || tree; + }); + return { + get html () { + if (typeof renderFn !== 'function') { + throw new Error('"render" function not defined, please pass to "process(input, { render })"'); + } + return renderFn(tree, tree.options); + }, + tree, + raw, + messages: tree.messages + }; + } + }; + } + + const SELFCLOSE_END_TAG = '/>'; + const CLOSE_START_TAG = ''; + const renderNode = (node, { stripTags =false })=>{ + if (!node) return ''; + const type = typeof node; + if (type === 'string' || type === 'number') { + return node; + } + if (type === 'object') { + if (stripTags === true) { + // eslint-disable-next-line no-use-before-define + return renderNodes(node.content, { + stripTags + }); + } + if (node.content === null) { + return [ + START_TAG, + node.tag, + attrsToString(node.attrs), + SELFCLOSE_END_TAG + ].join(''); + } + // eslint-disable-next-line no-use-before-define + return [ + START_TAG, + node.tag, + attrsToString(node.attrs), + END_TAG, + renderNodes(node.content), + CLOSE_START_TAG, + node.tag, + END_TAG + ].join(''); + } + if (Array.isArray(node)) { + // eslint-disable-next-line no-use-before-define + return renderNodes(node, { + stripTags + }); + } + return ''; + }; + const renderNodes = (nodes, { stripTags =false } = {})=>[].concat(nodes).reduce((r, node)=>r + renderNode(node, { + stripTags + }), ''); + const render = renderNodes; + + /** + * Plugin that converts consecutive normal spaces (U+0020) to non-breaking spaces (U+00A0). + * To use, put as function similar to the presets. + * + * + * @example + * ```ts + * const output = bbob([preset(), , preserveWhitespace(), lineBreakPlugin()]).process(input, {render}).html + * ``` + */ + + /** + * Checks if input is an object + * @param value input + * @returns if value is an object + */ + const isObj$1 = (value) => typeof value === "object"; + + /** + * Walks the tree of nodes. Checks for node of consecutive spaces. If found replaces every space in + * node with a nonbreaking space. + * Preserves multiple spaces so html won't truncate them. + * + * Walks through entire tree. + * @param t tree of nodes to be processed + * @returns modified tree + */ + const walk$1 = (t) => { + const tree = t; + + if (Array.isArray(tree)) { + for (let idx = 0; idx < tree.length; idx++) { + const child = walk$1(tree[idx]); + if (Array.isArray(child)) { + tree.splice(idx, 1, ...child); + idx += child.length - 1; + } else { + tree[idx] = child; + } + } + } else if (tree && isObj$1(tree) && tree.content) { + walk$1(tree.content); + } + + //Bbob breaks up nodes by the presence of normal spaces. + //So a node with a normal space can only have normal spaces in that node. + if (isStringNode(tree)) { + if (tree.length > 1 && tree[0] === " ") { + let numSpaces = tree.length; + return [String.fromCharCode(160).repeat(numSpaces)]; + } + } + + return tree; + }; + + /** + * Converts consecutive normal spaces (U+0020) to nonbreaking spaces (U+00A0). + * Supply this as a plugin in the preset lists. + * + * @example converts consecutive normal spaces (U+0020) to nonbreaking spaces (U+00A0) + * ```ts + * const output = bbob([preset(), preserveWhitespace(), lineBreakPlugin()]).process(input, {render}).html + * ``` + * + * @returns plugin to be used in BBob process + */ + const preserveWhitespace = () => { + return (tree) => walk$1(tree); + }; + + /** + * Post Processing designed to fix issues with Markdown and BBCode that the parser can't fix. + * + * Separate from markdown-it post processing as it'll be able to manipulate the full string. + * @param {string} raw string from processing through both BBCode and Markdown + * @returns post processed string + */ + function removeNewlineInjects(raw) { + const processed = raw + .replaceAll(MD_NEWLINE_INJECT, "") + .replaceAll(MD_NEWLINE_PRE_INJECT, "") + .replaceAll(MD_NEWLINE_INJECT_COMMENT, ""); // Remove all instances of the injected newline + return processed; + } + + /** + * Injects hoisted code blocks back into the raw string + * @param {string} raw input to inject hoisted code blocks into + * @param {any} data contains hoist map + * @returns string with hoisted code blocks injected + */ + function renderHoistedCodeBlocks(raw, data) { + const hoistMap = data.hoistMap; + for (const [uuid, content] of Object.entries(hoistMap)) { + raw = raw.replaceAll(uuid, content); + } + return raw; + } + + /** + * Performs post processing on the raw string to address any necessary functionality that BBob/MD can't handle with a plugin (i.e. hoisting). + * @param {string} raw processed input from after bbob and md + * @param {any} data from bbob data + * @returns final processed string + */ + function postprocess(raw, data) { + let final = raw; + const postprocessors = [removeNewlineInjects, renderHoistedCodeBlocks]; + for (const postprocessor of postprocessors) { + final = postprocessor(final, data); + } + return final; + } + + /** + * Plugin that converts line breaks to `
` tags. + * To use, put as function similar to the presets. + * + * If a node is marked with `noLineBreakConversion`, then it'll skip the parsing the children + * + * @example + * ```ts + * const output = bbob([preset(), lineBreakPlugin()]).process(input, {render}).html + * ``` + */ + + const isObj = (value) => typeof value === "object"; + const isString = (value) => typeof value === "string"; + + /** + * Walks the tree of nodes. Will add `br` tag to all `\n` in format that can be used in any renderer. + * Preserves \n so that markdown-it doesn't try to treat everything like a block + * + * If a node has the property noLineBreakConversion is encountered, will skip parsing children. + * @param t tree of nodes to be processed + * @returns modified tree + */ + const walk = (t, disableLineBreakConversion = false) => { + const tree = t; + + if (Array.isArray(tree)) { + if (tree.some(isString)) { + // array contains strings. Might be md compatible + tree.unshift(MD_NEWLINE_INJECT); + tree.push(MD_NEWLINE_INJECT); + } + for (let idx = 0; idx < tree.length; idx++) { + const child = walk(tree[idx], disableLineBreakConversion); + if (Array.isArray(child)) { + tree.splice(idx, 1, ...child); + idx += child.length - 1; + } else { + tree[idx] = child; + } + } + } else if (tree && isObj(tree) && tree.content) { + if (tree.isWhitespaceSensitive) { + // applies only to [code] and [icode] + // stop walk. children won't be parsed to have
+ return tree.tag ? tree : tree.content; + } + if (tree.disableLineBreakConversion) { + disableLineBreakConversion = true; + } + walk(tree.content, disableLineBreakConversion); + return tree.tag ? tree : tree.content; + } else if (isString(tree) && URL_REGEX_SINGLE_LINE.test(tree.trim())) { + // if the entire string is a URL, then it should be prepared for onebox. + // BBob separates strings by newlines anyway, so we can already assume this is sitting on its own line + // MD_NEWLINE_INJECT is already replacing newline came before or the start of the array, + // so we only need to make sure \n\n is added after the URL + return [tree, MD_NEWLINE_PRE_INJECT]; + } + + if (isEOL(tree)) { + return disableLineBreakConversion + ? ["\n", MD_NEWLINE_INJECT] + : [{ tag: "br", content: null }, MD_NEWLINE_INJECT]; + } + + return tree; + }; + + /** + * Converts `\n` to `
` self closing tag. Supply this as the last plugin in the preset lists + * + * @example converts all line breaks to br + * ```ts + * const output = bbob([preset(), lineBreakPlugin()]).process(input, {render}).html + * ``` + * @example will not convert line breaks inside [nobr] + * ```ts + * const nobr = (node: TagNode) => {return { disableLineBreakConversion: true, content: node.content }}; \\ tag in preset + * ... + * const output = bbob([preset(), lineBreakPlugin()]).process(input, {render}).html + * ``` + * @returns plugin to be used in BBob process + */ + const lineBreakPlugin = () => { + return (tree) => walk(tree); + }; + + /** + * Find all code blocks and hoist them out of the content and into a map for later insertion + * @param {string} raw input to preprocess + * @returns processed string and hoist map + */ + function fenceCodeBlockPreprocess(content, data) { + /** @type {Object.} */ + const hoistMap = {}; + let index = 0; + + const addHoistAndReturnNewStartPoint = (cutOffStart, cutOffEnd, expected, trim = false) => { + const uuid = generateGUID(); + if (cutOffEnd !== -1) { + hoistMap[uuid] = content.substring(cutOffStart, cutOffEnd); + content = content.substring(0, cutOffStart) + uuid + content.substring(cutOffEnd); + } else { + hoistMap[uuid] = content.substring(cutOffStart); + content = content.substring(0, cutOffStart) + uuid + expected; + } + if (trim) { + if (hoistMap[uuid].startsWith("\n")) { + hoistMap[uuid] = hoistMap[uuid].substring(1); + } + if (hoistMap[uuid].endsWith("\n")) { + hoistMap[uuid] = hoistMap[uuid].substring(0, hoistMap[uuid].length - 1); + } + } + return cutOffStart + uuid.length + expected.length; + }; + + while ((index = regexIndexOf(content, ESCAPABLES_REGEX, index)) !== -1) { + const match = ESCAPABLES_REGEX.exec(content.substring(index)); + if (match.groups?.fence) { + const fence = match.groups.fence; + const fenceInfo = match.groups.fenceInfo; + if (content[index] === "\n") { + // Check if the fence is not at the start of the content + index += 1; + } + const closingFenceRegex = new RegExp("\n" + fence + "(\n|$)"); // Find the next fence. By commonmark spec, it should be the same fence length and type + const nextIndex = regexIndexOf(content, closingFenceRegex, index + fence.length); + + const uuid = generateGUID(); + if (nextIndex !== -1) { + hoistMap[uuid] = content.substring(index + fence.length + fenceInfo.length, nextIndex); + } else { + hoistMap[uuid] = content.substring(index + fence.length + fenceInfo.length); + } + // inject bbcode tag before and after the code block. This is to prevent BBob plugin from injecting newlines + const replacement = `[saveNL]\n${fence}${fenceInfo}${uuid}\n${fence}\n[/saveNL]`; + content = + content.substring(0, index) + + replacement + + (nextIndex !== -1 ? content.substring(nextIndex + 1 + fence.length) : ""); + index = index + replacement.length; + } else if (match.groups?.bbcode) { + const bbcode = match.groups.bbcode; + const bbcodeTag = match.groups.bbcodeTag.toLowerCase(); // coerce to lowercase for caseinsensitive matching + const closingTag = `[/${bbcodeTag}]`; + const nextIndex = content.toLowerCase().indexOf(closingTag, index + 1); + index = addHoistAndReturnNewStartPoint(index + bbcode.length, nextIndex, closingTag, true); + } else if (match.groups.backtick) { + const backtick = match.groups.backtick; // contains whole content + const tickStart = match.groups.tickStart; + const tickEnd = match.groups.tickEnd; + index = addHoistAndReturnNewStartPoint( + index + tickStart.length, + index + backtick.length - tickEnd.length, + tickEnd, + ); + } + } + + data.hoistMap = hoistMap; + return [content, data]; + } + + /** + * Find all markdown table blocks and mark them to ignore newlines + * @param {string} raw input to preprocess + * @returns processed string + */ + function mdTableBlockPreprocess(content, data) { + let index = 0; + while ((index = regexIndexOf(content, MD_TABLE_REGEX, index)) !== -1) { + const match = MD_TABLE_REGEX.exec(content.substring(index)); + const table = match[0]; + const replacement = `[saveNL]\n${table}\n[/saveNL]`; + content = content.substring(0, index) + replacement + content.substring(index + table.length); + index = index + replacement.length; + } + return [content, data]; + } + + /** + * Preprocesses input to be formatted for bbob to intake. Handles any necessary functionality that BBob can't handle with a plugin (i.e. hoisting). + * @param {string} raw input to preprocess + * @returns formatted input for bbob to intake + */ + function preprocessRaw(raw) { + let data = {}; + const preprocessors = [fenceCodeBlockPreprocess, mdTableBlockPreprocess]; + for (const preprocessor of preprocessors) { + [raw, data] = preprocessor(raw, data); + } + return [raw, data]; + } + + // TODO: Change error handling so active editing doesn't spam the console + const options = { + onlyAllowTags: [...availableTags], + contextFreeTags: preventParsing, // prevent parsing of children + enableEscapeTags: true, + onError: (err) => + // eslint-disable-next-line no-console + console.warn(err.message, err.lineNumber, err.columnNumber), + }; + + const RpNBBCode = (code, opts) => { + const plugins = [preset()]; + if (opts.preserveWhitespace) { + plugins.push(preserveWhitespace()); + } + plugins.push(lineBreakPlugin()); + const [preprocessed, preprocessedData] = preprocessRaw(code); + return bbob(plugins).process(preprocessed, { + render, + ...options, + data: { + ...preprocessedData, + fonts: new Set(), + }, + }); + }; + + exports.RpNBBCode = RpNBBCode; + exports.postprocess = postprocess; + +})); diff --git a/assets/stylesheets/common/rowcolumn.scss b/assets/stylesheets/common/rowcolumn.scss index e12f029..bd4f44f 100644 --- a/assets/stylesheets/common/rowcolumn.scss +++ b/assets/stylesheets/common/rowcolumn.scss @@ -1,32 +1,7 @@ .bb-row { width: 100%; clear: both; - overflow: hidden; margin: 0; - &[data-span="column-width-span1"] { - width: 10.5%; - } /*12.5*/ - &[data-span="column-width-span2"] { - width: 23%; - } /*25*/ - &[data-span="column-width-span3"] { - width: 35.5%; - } /*37.5*/ - &[data-span="column-width-span4"] { - width: 48%; - } /*50*/ - &[data-span="column-width-span5"] { - width: 60.5%; - } /*62.5*/ - &[data-span="column-width-span6"] { - width: 73%; - } /*75*/ - &[data-span="column-width-span7"] { - width: 85.5%; - } /*87.5*/ - &[data-span="column-width-span8"] { - width: 98%; - } /*100*/ } .bb-row .bb-column { @@ -35,21 +10,53 @@ float: left; } +.bb-column { + margin: 0 0.5%; + padding: 0 0.5%; + float: left; + &[data-span="column-width-span1"] { + width: 13.5%; + } + &[data-span="column-width-span2"] { + width: 23%; + } + &[data-span="column-width-span3"] { + width: 41%; + } + &[data-span="column-width-span4"] { + width: 48%; + } + &[data-span="column-width-span5"] { + width: 66%; + } + &[data-span="column-width-span6"] { + width: 79%; + } + &[data-span="column-width-span7"] { + width: 91%; + } + &[data-span="column-width-span8"] { + width: 100%; + } + } + @media screen and (max-width: 900px) { - .bb-row [data-span="column-width-span1"], - .bb-row [data-span="column-width-span2"], - .bb-row [data-span="column-width-span3"], - .bb-row [data-span="column-width-span4"], - .bb-row [data-span="column-width-span5"], - .bb-row [data-span="column-width-span6"], - .bb-row [data-span="column-width-span7"], - .bb-row [data-span="column-width-span8"] { - width: 100%; - } /*12.5*/ + .bb-row { + &[data-span="column-width-span1"], + &[data-span="column-width-span2"], + &[data-span="column-width-span3"], + &[data-span="column-width-span4"], + &[data-span="column-width-span5"], + &[data-span="column-width-span6"], + &[data-span="column-width-span7"], + &[data-span="column-width-span8"] { + width: 100%; + } - .bb-row .bb-column { - margin: 0; - padding: 0; - float: none; + .bb-column { + margin: 0; + padding: 0; + float: none; + } } } From f099c3b7ad97ab8599324aac89f83f379bb47142 Mon Sep 17 00:00:00 2001 From: blythechan Date: Wed, 26 Jun 2024 18:42:36 -0400 Subject: [PATCH 08/10] Prettier extension didn't pick up on tabbed css --- assets/stylesheets/common/rowcolumn.scss | 54 ++++++++++++------------ 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/assets/stylesheets/common/rowcolumn.scss b/assets/stylesheets/common/rowcolumn.scss index bd4f44f..1177c9e 100644 --- a/assets/stylesheets/common/rowcolumn.scss +++ b/assets/stylesheets/common/rowcolumn.scss @@ -11,34 +11,34 @@ } .bb-column { - margin: 0 0.5%; - padding: 0 0.5%; - float: left; - &[data-span="column-width-span1"] { - width: 13.5%; - } - &[data-span="column-width-span2"] { - width: 23%; - } - &[data-span="column-width-span3"] { - width: 41%; - } - &[data-span="column-width-span4"] { - width: 48%; - } - &[data-span="column-width-span5"] { - width: 66%; - } - &[data-span="column-width-span6"] { - width: 79%; - } - &[data-span="column-width-span7"] { - width: 91%; - } - &[data-span="column-width-span8"] { - width: 100%; - } + margin: 0 0.5%; + padding: 0 0.5%; + float: left; + &[data-span="column-width-span1"] { + width: 13.5%; + } + &[data-span="column-width-span2"] { + width: 23%; + } + &[data-span="column-width-span3"] { + width: 41%; + } + &[data-span="column-width-span4"] { + width: 48%; } + &[data-span="column-width-span5"] { + width: 66%; + } + &[data-span="column-width-span6"] { + width: 79%; + } + &[data-span="column-width-span7"] { + width: 91%; + } + &[data-span="column-width-span8"] { + width: 100%; + } +} @media screen and (max-width: 900px) { .bb-row { From 480532f4249d95ccc740bc1e5ad38cf958cbd952 Mon Sep 17 00:00:00 2001 From: blythechan Date: Wed, 26 Jun 2024 19:35:36 -0400 Subject: [PATCH 09/10] Synching back up with main to retrieve minified bbcode-parser --- assets/bundled/bbcode-parser.min.js | 2823 +-------------------------- 1 file changed, 3 insertions(+), 2820 deletions(-) diff --git a/assets/bundled/bbcode-parser.min.js b/assets/bundled/bbcode-parser.min.js index f5c3d7e..5016309 100644 --- a/assets/bundled/bbcode-parser.min.js +++ b/assets/bundled/bbcode-parser.min.js @@ -1,2820 +1,3 @@ - -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : - typeof define === 'function' && define.amd ? define(['exports'], factory) : - (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.bbcodeParser = {})); -})(this, (function (exports) { 'use strict'; - - /* eslint-disable indent */ const isTagNode$1 = (el)=>typeof el === 'object' && !!el.tag; - function process(tags, tree, core, options) { - tree.walk((node)=>isTagNode$1(node) && tags[node.tag] ? tags[node.tag](node, core, options) : node); - } - /** - * Creates preset for @bbob/core - * @param defTags {Object} - * @param processor {Function} a processor function of tree - * @returns {function(*=): function(*=, *=): void} - */ function createPreset(defTags, processor = process) { - const presetFactory = (opts = {})=>{ - presetFactory.options = Object.assign(presetFactory.options || {}, opts); - const presetExecutor = (tree, core)=>processor(defTags, tree, core, presetFactory.options); - presetExecutor.options = presetFactory.options; - return presetExecutor; - }; - presetFactory.extend = (callback)=>createPreset(callback(defTags, presetFactory.options), processor); - return presetFactory; - } - - const toNode = (tag, attrs, content) => ({ - tag, - attrs, - content, - }); - - /** - * Preprocess attributes of a node to either combine all values into a single default value - * or return a keyed attribute list - * @param {any} attrs object of bbcode node attrs - * @param {string[]} predefinedKeys array of predefined keys to be captured - * @returns processed attributes - */ - const preprocessAttr = (attrs) => { - const keys = Object.keys(attrs).join(" "); - const vals = Object.values(attrs).join(" "); - if (keys === vals) { - return { - _default: vals, - }; - } else { - return attrs; - } - }; - - /** - * Attempts to return tag into its original form with proper attributes - * @returns string of tag start - */ - const toOriginalStartTag = (node) => { - if (!node.attrs) { - return `[${node.tag}]`; - } - const attrs = preprocessAttr(node.attrs); - if (attrs._default) { - return `[${node.tag}=${attrs._default}]`; - } else { - return node.toTagStart(); - } - }; - - /** - * Given a string, find the first position of a regex match - * @param {string} string to test against - * @param {RegExp} regex to test with - * @param {number} startpos starting position. Defaults to 0 - * @returns index of the first match of the regex in the string - */ - const regexIndexOf = (string, regex, startpos) => { - const indexOf = string.substring(startpos || 0).search(regex); - return indexOf >= 0 ? indexOf + (startpos || 0) : indexOf; - }; - - const MD_NEWLINE_INJECT = "\n\n"; - const MD_NEWLINE_PRE_INJECT = "\n\n"; - const MD_NEWLINE_INJECT_COMMENT = ""; - - const URL_REGEX = - /(http|ftp|https|upload):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])/; - const MD_URL_REGEX = - /\!?\[.*\]\((http|ftp|https|upload):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])\)/; - const URL_REGEX_SINGLE_LINE = new RegExp(`^${URL_REGEX.source}|${MD_URL_REGEX.source}$`); - const ESCAPABLES_REGEX = - /((\n|^)(?```+|~~~+)(?.*\n))|(?\[(?i?code|plain)(=.*)?\])|(?(?`{1,2})(.*)(?\k))/im; - const MD_TABLE_REGEX = /^(\|[^\n]+\|\r?\n)((?:\| ?:?[-]+:? ?)+\|)(\n(?:\|[^\n]+\|\r?\n?)*)?$/m; - - /** - * Generates a random GUID. - * - * Mini Racer doesn't have the crypto module, so we can't use the built-in `crypto.randomUUID` function. - * @returns {string} a GUID - */ - function generateGUID() { - let d = new Date().getTime(); - if (window.performance && typeof window.performance.now === "function") { - d += performance.now(); //use high-precision timer if available - } - return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) { - // eslint-disable-next-line no-bitwise - const r = (d + Math.random() * 16) % 16 | 0; - d = Math.floor(d / 16); - // eslint-disable-next-line no-bitwise - return (c === "x" ? r : (r & 0x3) | 0x8).toString(16); - }); - } - - /** - * @file Adds [left], [center], and [right] to bbcode - * @example [center]content[/center] - */ - const alignment = { - left: (node) => toNode("div", { class: "bb-left" }, node.content), - center: (node) => toNode("div", { class: "bb-center" }, node.content), - right: (node) => toNode("div", { class: "bb-right" }, node.content), - }; - - /** - * @file Adds [id] and [goto] to bbcode - * @example [a=your_anchor_name]An anchor[/a] [goto=your_anchor_name]Jump to an anchor[/goto] - */ - const anchor = { - // name is not valid in HTML5; however, it correctly displays back while id does not - a: (node) => { - const attrs = preprocessAttr(node.attrs)._default || ""; - return toNode("a", { id: `user-anchor-${attrs.trim()}`, name: `user-anchor-${attrs.trim()}` }, node.content); - }, - goto: (node) => { - const attrs = preprocessAttr(node.attrs)._default || ""; - toNode("a", { href: `#user-anchor-${attrs.trim()}` }, node.content); - } - }; - - /** - * Add [bg] tag - * @example [bg=red]Hello[/bg] - */ - const bg = (node) => { - const color = preprocessAttr(node.attrs)._default; - return toNode( - "div", - { - style: `background-color: ${color};`, - class: "bb-background", - }, - node.content, - ); - }; - - /** - * Add [block] tag - * @example [block=treasure]content[/block] - */ - const block = (node) => { - const defaultOp = "block"; - const blockAttr = (preprocessAttr(node.attrs)._default || defaultOp).toLowerCase(); - - const OPTIONS = [ - "block", - "dice", - "dice10", - "setting", - "warning", - "storyteller", - "announcement", - "important", - "question", - "encounter", - "information", - "character", - "treasure", - ]; - - // Default to block option if user did not provide anything valid - const blockOption = OPTIONS.includes(blockAttr) ? blockAttr : defaultOp; - - return { - tag: "table", - attrs: { - class: "bb-block", - "data-bb-block": blockOption - }, - content: [ - { - tag: "tbody", - content: [ - { - tag: "tr", - content: [ - { - tag: "td", - attrs: { - class: "bb-block-icon" - } - }, - { - tag: "td", - attrs: { - class: "bb-block-content" - }, - content: node.content - } - ] - } - ] - } - ] - }; - }; - - /** - * @file Adds [blockquote] to bbcode - * @example [blockquote=author]content[/blockquote] - */ - const blockquote = (node) => { - const author = preprocessAttr(node.attrs)._default || ""; - - return { - tag: "div", - attrs: { - class: "bb-blockquote", - }, - content: [ - { - tag: "div", - attrs: { - class: "bb-blockquote-left", - }, - }, - { - tag: "div", - attrs: { - class: "bb-blockquote-content", - }, - content: [ - node.content, - { - tag: "div", - attrs: { - class: "bb-blockquote-speaker", - }, - content: `${author !== "" ? `- ${author}` : ""}`, - }, - ], - }, - { - tag: "div", - attrs: { - class: "bb-blockquote-right", - }, - }, - ], - }; - }; - - const border = (node) => { - const val = preprocessAttr(node.attrs)._default; - return toNode( - "div", - { - style: `border: ${val};`, - class: "bb-border", - }, - node.content - ); - }; - - const centerblock = (node) => { - const percentageInput = preprocessAttr(node.attrs)._default || "50"; - return toNode("div", { style: `margin: 0 auto; width: ${percentageInput}%` }, node.content); - }; - - const check = (node) => { - const attrs = preprocessAttr(node.attrs)._default || "dot"; - return toNode("div", { class: `bb-check`, "data-type": attrs }, node.content); - }; - - /** - * processes [code] tag and returns a fenced code block - */ - const code = (node) => { - const lang = preprocessAttr(node.attrs)._default || "bbcode"; - return { - isWhitespaceSensitive: true, - content: ["```" + lang + "\n", node.content, "\n```\n"], - }; - }; - - /** - * processes [icode] tag and returns inline code - */ - const icode = (node) => { - return { - isWhitespaceSensitive: true, - content: ["`", node.content, "`"], - }; - }; - - /** - * Special tag to save newlines in code blocks. Used for hoisting code blocks - */ - const savenl = (node) => { - return { - isWhitespaceSensitive: true, - content: node.content, - }; - }; - - const color = (node) => { - const inputColor = preprocessAttr(node.attrs)._default || ""; - if (inputColor.trim() === "") { - return node.content; - } - return toNode("span", { style: `color: ${inputColor}` }, node.content); - }; - - const comment = (node) => { - return toNode( - "span", {class: "hidden" }, node.content, - ); - }; - - const divide = (node) => { - const type = (preprocessAttr(node.attrs)._default || "").toLowerCase(); - return toNode( - "span", - { - class: "bb-divide", - "data-type": type, - }, - node.content - ); - }; - - /** - * @file Adds [fieldset] to bbcode - * @example [fieldset=title]content[/fieldset] - */ - const fieldset = (node) => { - const title = preprocessAttr(node.attrs)._default || ""; - return { - tag: "fieldset", - attrs: { - class: "bb-fieldset", - }, - content: [ - { - tag: "legend", - attrs: { - class: "bb-fieldset-legend" - }, - content: title - }, - { - tag: "div", - attrs: { - class: "bb-fieldset" - }, - content: node.content - } - ] - }; - }; - - const WEB_FONTS = [ - "arial", - "book antiqua", - "courier new", - "georgia", - "tahoma", - "times new roman", - "trebuchet ms", - "verdana", - ]; - const VALID_FONT_STYLES = { - thin: "100", - extralight: "200", - light: "300", - regular: "400", - medium: "500", - semibold: "600", - bold: "700", - extrabold: "800", - black: "900", - }; - // registered axis tags https://learn.microsoft.com/en-us/typography/opentype/spec/dvaraxisreg#registered-axis-tags - const REGISTERED_AXIS = ["ital", "opsz", "slnt", "wdth", "wght"]; - - const AXES_REGEX = /(?[a-zA-Z]*)?\s?(?[0-9]*)?\s?(?italic)?/; - - const axesParser = (attrs) => { - let axes = { - ital: 0, - wght: 400, - }; - - if (attrs?.style) { - // user just copy pasted the name of the style on the google font site, probably - const style = attrs.style.trim().toLowerCase(); - const matches = AXES_REGEX.exec(style).groups || {}; - if (matches?.italic) { - axes.ital = 1; - } - - const weight = matches.weight; - if (weight && weight >= 0 && weight <= 900) { - axes.wght = weight; - } else if (Object.keys(VALID_FONT_STYLES).includes(matches.named_weight || "")) { - axes.wght = VALID_FONT_STYLES[matches.named_weight]; - } - - axes = { - ...axes, - ...Object.fromEntries(Object.entries(attrs).filter(([key]) => REGISTERED_AXIS.includes(key))), - }; - } - return axes; - }; - - /** - * Create google font api url - * @param {string} family name of font - * @param {object} axes custom font axes - */ - const googleFontApiBuild = (family, axes) => { - family = family.replaceAll(" ", "+"); - // google fonts requires axes names to be in alphabetical order - axes = Object.keys(axes) - .sort() - .reduce((obj, key) => { - obj[key] = axes[key]; - return obj; - }, {}); - const axesList = Object.keys(axes).join(",") + "@" + Object.values(axes).join(","); - return "https://fonts.googleapis.com/css2?family=" + family + ":" + axesList; - }; - - const font = (node, options) => { - const attrs = preprocessAttr(node.attrs); - const fontFamily = attrs?._default || attrs.family || attrs.name; - if (fontFamily.trim() === "") { - return node.content; - } - if (WEB_FONTS.includes(fontFamily.trim().toLowerCase())) { - return toNode("span", { style: "font-family: " + fontFamily }, node.content); - } - - const axes = axesParser(attrs); - const url = googleFontApiBuild(fontFamily, axes); - options.data.fonts.add(url); - - const italic = axes.ital === 1 ? "italic" : "normal"; - - const custom = Object.entries(axes).filter(([key]) => key !== "wght" && key !== "ital"); - let fontVar = ""; - if (custom.length) { - fontVar = - "font-variation-settings: " + custom.map(([key, val]) => `'${key}' ${val}`).join(", ") + ";"; - } - - return toNode( - "span", - { - style: `font-family: ${fontFamily}; font-weight: ${axes.wght}; font-style: ${italic}; ${fontVar}`, - "data-font": url, - }, - node.content, - ); - }; - - /** - * @file Adds Header to bbcode - * @example [h]content[/h], [h2]content[/h2], [h3]content[/h3], - * [h4]content[/h4], [h5]content[/h5], [h6]content[/h6]. - */ - - const h = (node) => { - return toNode("h1", {}, node.content); - }; - - const h1 = (node) => { - return toNode("h1", {}, node.content); - }; - - const h2 = (node) => { - return toNode("h2", {}, node.content); - }; - - const sh = (node) => { - return toNode("h2", {}, node.content); - }; - - const h3 = (node) => { - return toNode("h3", {}, node.content); - }; - - const h4 = (node) => { - return toNode("h4", {}, node.content); - }; - - const h5 = (node) => { - return toNode("h5", {}, node.content); - }; - - const h6 = (node) => { - return toNode("h6", {}, node.content); - }; - - /** - * Parse the user provided height and return a valid height value - * @param {Number} heightValue obtains the input of the user entered height (default is 700) - * @returns A validated number less than 0. - */ - function parseHeight$1(heightValue) { - const maxHeight = 700; - const parsedHeight = - heightValue && heightValue.trim() !== "" ? heightValue.replace(/[^\d.]/g, "") : 0; - - if (parsedHeight && parsedHeight >= 0 && parsedHeight <= maxHeight) { - return parsedHeight; - } else { - // if the value = 0 then nothing will be returned - return parsedHeight === 0 ? 0 : maxHeight; - } - } - - /** - * @file Adds [heightrestrict] to bbcode - * @example [heightrestrict=50]content[/heightrestrict] - */ - const heightrestrict = (node) => { - const attrs = preprocessAttr(node.attrs)._default; - const heightInput = parseHeight$1(attrs).toString(); - // Return image's default size if heightrestrict did not involve a valid value - return heightInput === "0" - ? toNode("div", { class: "bb-height-restrict" }, node.content) - : toNode( - "div", - { class: "bb-height-restrict", style: `height: ${heightInput}px;` }, - node.content, - ); - }; - - /** - * @file Adds [highlight] to bbcode - * @example [highlight]content[/highlight] - */ - const highlight = (node) => { - return toNode("span", { class: "bb-highlight" }, node.content); - }; - - /** - * @file Adds [imagefloat] to bbcode - * @exmaple [imagefloat=left]content[/imagefloat] - */ - const imagefloat = (node) => { - const attrs = preprocessAttr(node.attrs)._default || ""; - return toNode("div", { class: `bb-float-${attrs}` }, node.content); - }; - - /** - * @file Adds [justify] to bbcode - * @example [justify]content[/justify] - */ - const justify = (node) => { - return toNode("div", { class: "bb-justify" }, node.content); - }; - - /** - * @file Adds [mail] to bbcode - * @param {string} [type="send"] Denotes type of mail either send or receive - * @param {string} [person="Unknown"] Denotes the person in the To/From field - * @param {string} [subject="Empty"] Denotes the subject line of the email - * @example [mail type="send" person="John Doe" subject="Hello World"]content[/mail] - */ - - const parseEmailContent = (content) => { - return toNode("div", { class: "bb-email-content" }, content); - }; - - const parseEmailSubject = (subject) => { - return toNode("div", { class: "bb-email-subject" }, subject); - }; - - const parseEmailPerson = (person) => { - return toNode("div", { class: "bb-email-address" }, person); - }; - - const emailHeader = toNode("div", { class: "bb-email-header" }, ""); - const emailFooter = toNode( - "div", - { class: "bb-email-footer" }, - toNode("div", { class: "bb-email-button" }, "") - ); - - const mail = (node) => { - const attributes = preprocessAttr(node.attrs); - let mailAttr = { - mailOption: (attributes.type || "send").toLowerCase(), - person: attributes.person || "Unknown", - subject: attributes.subject || "Empty", - }; - - return toNode( - "div", - { - class: "bb-email", - "data-bb-email": mailAttr.mailOption, - }, - [ - emailHeader, - parseEmailPerson(mailAttr.person), - parseEmailSubject(mailAttr.subject), - parseEmailContent(node.content), - emailFooter, - ] - ); - }; - - /** - * @file Adds [newspaper] to bbcode - * @example [newspaper]content[/newspaper] - */ - const newspaper = (node) => { - return toNode("div", { class: "bb-newspaper" }, node.content); - }; - - /** - * Creates a line break html
tag - */ - const br = () => { - return toNode("br", {}, null); - }; - - /** - * Disables line breaks for given content - * @example - * ``` - * [nobr]test - * test - * test - * [/nobr] - * - * test test test - * ``` - */ - const nobr = (node) => { - return { disableLineBreakConversion: true, content: node.content }; - }; - - /** - * @file Adds [note] to bbcode - * @example [note]content[/note] - */ - - const note = (node) => { - return toNode("div", { class: "bb-note" }, [ - toNode("div", { class: "bb-note-tape" }, ""), - toNode("div", { class: "bb-note-content" }, [ - node.content, - toNode("div", { class: "bb-note-footer" }, ""), - ]), - ]); - }; - - /** - * @file Adds [ooc] to bbcode - * @example [ooc]content[/ooc] - */ - const ooc = (node) => { - return toNode( - "div", - { - class: "bb-ooc", - }, - node.content, - ); - }; - - /** - * @file Adds [pindent] to bbcode - * @example [pindent]content[/pindent] - */ - const pindent = (node) => { - return toNode("span", { class: "bb-pindent" }, node.content); - }; - - /** - * [plain] bbcode tag that prevents parsing of inner tags - * @example - * ``` - * [plain]This is [b]bold[/b] and [i]italic[/i][/plain] - * ``` - * outputs to - * ``` - * This is [b]bold[/b] and [i]italic[/i] - * ``` - */ - const plain = (node) => { - return node.content; - }; - - /** - * Add [print] tag - * @example [print=lined]content[/print] - */ - const print = (node) => { - const defaultOp = "print"; - const printAttr = (preprocessAttr(node.attrs)._default || defaultOp).toLowerCase(); - - const OPTIONS = ["print", "line", "graph", "parchment"]; - - // Default to print if option is not valid - const printOption = OPTIONS.includes(printAttr) ? printAttr : defaultOp; - - return toNode( - "div", - { class: printOption === defaultOp ? `bb-print` : `bb-print-${printOption}` }, - node.content, - ); - }; - - /** - * @file Adds [progress] to bbcode - * @exmaple [progress=percentageInt]content[/progress] - */ - const progress = (node) => { - const percentageInt = preprocessAttr(node.attrs)._default; - return { - tag: "div", - attrs: { - class: "bb-progress", - }, - content: [ - { - tag: "div", - attrs: { - class: "bb-progress-text" - }, - content: node.content - }, - { - tag: "div", - attrs: { - class: "bb-progress-bar", - style: `width: calc(${percentageInt}% - 6px)` - } - }, - { - tag: "div", - attrs: { - class: "bb-progress-bar-other" - } - } - ], - }; - }; - - /** - * @file Adds [thinprogress] to bbcode - * @exmaple [thinprogress=percentageInt]content[/progthinprogressress] - */ - const thinprogress = (node) => { - const percentageInt = preprocessAttr(node.attrs)._default; - return { - tag: "div", - attrs: { - class: "bb-progress-thin", - }, - content: [ - { - tag: "div", - attrs: { - class: "bb-progress-text" - }, - content: node.content - }, - { - tag: "div", - attrs: { - class: "bb-progress-bar", - style: `width: calc(${percentageInt}% - 6px)` - } - }, - { - tag: "div", - attrs: { - class: "bb-progress-bar-other" - } - } - ], - }; - }; - - /** - * Parse the user provided height and return a valid height value - * @param {Number} heightValue obtains the input of the user entered height (default is 700) - * @returns A validated number less than 0. - */ - function parseHeight(heightValue) { - const maxHeight = 700; - const parsedHeight = - heightValue && heightValue.trim() !== "" ? heightValue.replace(/[^\d.]/g, "") : 0; - - if (parsedHeight && parsedHeight >= 0 && parsedHeight <= maxHeight) { - return parsedHeight; - } else { - // if the value = 0 then nothing will be returned - return parsedHeight === 0 ? 0 : maxHeight; - } - } - - /** - * @file Adds [scroll] to bbcode - * @example [scroll]content[/scroll] - */ - const scroll = (node) => { - const attrs = preprocessAttr(node.attrs)._default; - const heightInput = parseHeight(attrs); - return toNode("div", { class: "bb-scroll", style: `height: ${heightInput}px` }, node.content); - }; - - const side = (node) => { - const attrs = preprocessAttr(node.attrs)._default || "left"; - return toNode("div", { class: "bb-side", "data-side": attrs }, node.content); - }; - - /** - * Parses an inputted size value and returns the formatted valid font size - * @param {string} fontValue the input of the size - */ - function parseFontSize(fontValue) { - let value; - let fontSize = { valid: true }; - const parsedSize = /(\d+\.?\d?)(px|rem)?/i.exec(fontValue); - const sizeRanges = { - px_max: 36, - px_min: 8, - rem_max: 3, - rem_min: 0.2, - unitless_max: 7, - unitless_min: 1, - }; - - if (parsedSize && (value = parsedSize[1])) { - fontSize.unit = (parsedSize[2] || "").toLowerCase(); - switch (fontSize.unit) { - case "px": - if (value > sizeRanges.px_max) { - value = sizeRanges.px_max; - } else if (value < sizeRanges.px_min) { - value = sizeRanges.px_min; - } - break; - case "rem": - if (value > sizeRanges.rem_max) { - value = sizeRanges.rem_max; - } else if (value < sizeRanges.rem_min) { - value = sizeRanges.rem_min; - } - break; - default: - if ((fontSize.valid = fontValue.length === value.length)) { - if (value > sizeRanges.unitless_max) { - value = sizeRanges.unitless_max; - } else if (value < sizeRanges.unitless_min) { - value = sizeRanges.unitless_min; - } - } - break; - } - - fontSize.value = value; - } - return fontSize; - } - - - /** - * @file Adds [row][column] to bbcode - * @example Adds [row][column][/column][/row] - */ - const rowcolumn = { - row: (node) => toNode("div", { class: "bb-row" }, node.content), - column: (node) => { - const columnAttrs = preprocessAttr(node.attrs)._default || "8"; - const columnStyle = columnAttrs.startsWith("span") ? `column-width-${columnAttrs}` : `column-width-span${columnAttrs}`; - return toNode("div", { class: `bb-column`, "data-span": columnStyle }, node.content); - } - } - - const size = (node) => { - const input = preprocessAttr(node.attrs)._default; - const fontSize = parseFontSize(input); - if (!fontSize.valid) { - return node.content; - } - let outputAttr = {}; - if (fontSize.unit) { - outputAttr = { style: `font-size: ${fontSize.value}${fontSize.unit}` }; - } else { - outputAttr = { "data-size": fontSize.value }; - } - return toNode("span", outputAttr, node.content); - }; - - /** - * @file Adds subscript to BBCode - * @example [sub]content[/sub] - */ - - const sub = (node) => { - return toNode("sub", {}, node.content); - }; - - /** - * @file Adds superscript to bbcode - * @example [sup]content[/sup] - */ - - const sup = (node) => { - return toNode("sup", {}, node.content); - }; - - /** - * @file Adds [spoiler] and [inlinespoiler] to bbcode - * - * Defaults to "Spoiler" name if no title provided - * - * @example `[spoiler=Title]text[/spoiler]` - * @example `[inlinespoiler]hidden content[/inlinespoiler] - */ - - const spoiler = (node) => { - const providedTitle = preprocessAttr(node.attrs)._default; - const title = "Spoiler" + (providedTitle ? `: ${providedTitle}` : ""); - - /** - *
- * Title - *
- * lorem ipsum - *
- *
- */ - return { - tag: "details", - attrs: { - class: "bb-spoiler", - }, - content: [ - { - tag: "summary", - content: title, - }, - { - tag: "div", - attrs: { - class: "bb-spoiler-content", - }, - content: node.content, - }, - ], - }; - }; - - const inlinespoiler = (node) => { - return toNode("span", { class: "bb-inline-spoiler" }, node.content); - }; - - /** - * @file Adds textmessage to bbcode - * @exmaple [textmessage=Recipient][message=them]Hi [/message][message=me] Hey![/message][/textmessage] - */ - - const ACCEPTED_OPTIONS = ["me", "them", "right", "left"]; - const textmessage = { - textmessage: (node) => { - const attr = preprocessAttr(node.attrs)._default || "Recipient"; - const recipient = attr && attr.trim() !== "" ? attr : "Recipient"; - return { - tag: "div", - attrs: { - class: "bb-textmessage", - }, - content: [ - { - tag: "div", - attrs: { - class: "bb-textmessage-name", - }, - content: recipient, - }, - { - tag: "div", - attrs: { - class: "bb-textmessage-overflow", - }, - content: [ - { - tag: "div", - attrs: { - class: "bb-textmessage-content", - }, - content: node.content, - }, - ], - }, - ], - }; - }, - message: (node) => { - let option = preprocessAttr(node.attrs)._default.toLowerCase(); - if (!ACCEPTED_OPTIONS.includes(option) || option === "right") { - option = "me"; - } - if (option === "left") { - option = "them"; - } - - const senderAttrs = option === "me" ? "bb-message-me" : "bb-message-them"; - return { - tag: "div", - attrs: { - class: senderAttrs, - }, - content: [ - { - tag: "div", - attrs: { - class: "bb-message-content", - }, - content: node.content, - }, - ], - }; - }, - }; - - const N = '\n'; - const TAB = '\t'; - const EQ = '='; - const QUOTEMARK = '"'; - const SPACE = ' '; - const OPEN_BRAKET = '['; - const CLOSE_BRAKET = ']'; - const SLASH = '/'; - const BACKSLASH = '\\'; - - const isTagNode = (el)=>typeof el === 'object' && !!el.tag; - const isStringNode = (el)=>typeof el === 'string'; - const isEOL = (el)=>el === N; - const keysReduce = (obj, reduce, def)=>Object.keys(obj).reduce(reduce, def); - const getNodeLength = (node)=>{ - if (isTagNode(node)) { - return node.content.reduce((count, contentNode)=>count + getNodeLength(contentNode), 0); - } - if (isStringNode(node)) { - return node.length; - } - return 0; - }; - /** - * Appends value to Tag Node - * @param {TagNode} node - * @param value - */ const appendToNode = (node, value)=>{ - node.content.push(value); - }; - /** - * Replaces " to &qquot; - * @param {String} value - */ const escapeHTML = (value)=>value.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, ''')// eslint-disable-next-line no-script-url - .replace(/(javascript|data|vbscript):/gi, '$1%3A'); - /** - * Acept name and value and return valid html5 attribute string - * @param {String} name - * @param {String} value - * @return {string} - */ const attrValue = (name, value)=>{ - const type = typeof value; - const types = { - boolean: ()=>value ? `${name}` : '', - number: ()=>`${name}="${value}"`, - string: ()=>`${name}="${escapeHTML(value)}"`, - object: ()=>`${name}="${escapeHTML(JSON.stringify(value))}"` - }; - return types[type] ? types[type]() : ''; - }; - /** - * Transforms attrs to html params string - * @param values - */ const attrsToString = (values)=>{ - // To avoid some malformed attributes - if (values == null) { - return ''; - } - return keysReduce(values, (arr, key)=>[ - ...arr, - attrValue(key, values[key]) - ], [ - '' - ]).join(' '); - }; - /** - * Gets value from - * @example - * getUniqAttr({ 'foo': true, 'bar': bar' }) => 'bar' - * @param attrs - * @returns {string} - */ const getUniqAttr = (attrs)=>keysReduce(attrs, (res, key)=>attrs[key] === key ? attrs[key] : null, null); - - const getTagAttrs = (tag, params)=>{ - const uniqAattr = getUniqAttr(params); - if (uniqAattr) { - const tagAttr = attrValue(tag, uniqAattr); - const attrs = { - ...params - }; - delete attrs[uniqAattr]; - const attrsStr = attrsToString(attrs); - return `${tagAttr}${attrsStr}`; - } - return `${tag}${attrsToString(params)}`; - }; - class TagNode { - attr(name, value) { - if (typeof value !== 'undefined') { - this.attrs[name] = value; - } - return this.attrs[name]; - } - append(value) { - return appendToNode(this, value); - } - get length() { - return getNodeLength(this); - } - toTagStart({ openTag =OPEN_BRAKET , closeTag =CLOSE_BRAKET } = {}) { - const tagAttrs = getTagAttrs(this.tag, this.attrs); - return `${openTag}${tagAttrs}${closeTag}`; - } - toTagEnd({ openTag =OPEN_BRAKET , closeTag =CLOSE_BRAKET } = {}) { - return `${openTag}${SLASH}${this.tag}${closeTag}`; - } - toTagNode() { - return new TagNode(this.tag.toLowerCase(), this.attrs, this.content); - } - toString({ openTag =OPEN_BRAKET , closeTag =CLOSE_BRAKET } = {}) { - const isEmpty = this.content.length === 0; - const content = this.content.reduce((r, node)=>r + node.toString({ - openTag, - closeTag - }), ''); - const tagStart = this.toTagStart({ - openTag, - closeTag - }); - if (isEmpty) { - return tagStart; - } - return `${tagStart}${content}${this.toTagEnd({ - openTag, - closeTag - })}`; - } - constructor(tag, attrs, content){ - this.tag = tag; - this.attrs = attrs; - this.content = Array.isArray(content) ? content : [ - content - ]; - } - } - TagNode.create = (tag, attrs = {}, content = [])=>new TagNode(tag, attrs, content); - TagNode.isOf = (node, type)=>node.tag === type; - - /** - * @file Adds [tabs][tab] to bbcode - * @example [tabs][tab=name 1]content[/tab][tab=name 2]content[/tab][/tabs] - */ - const tabs = (node) => { - const tabsList = node.content.filter( - (contentNode) => isTagNode(contentNode) && contentNode.tag === "tab", - ); - const groupId = generateGUID(); - tabsList.forEach((tabNode) => { - tabNode.isValid = true; - tabNode.groupId = groupId; - }); - if (!tabsList.length) { - // no [tab] tags found - return [toOriginalStartTag(node), ...node.content, node.toTagEnd()]; - } - tabsList[0].open = true; - - return toNode( - "div", - { - class: "bb-tabs", - }, - tabsList, - ); - }; - - /** - * [tab=name]content[/tab] - * [tab name="name" style="style"]content[/tab] - */ - const tab = (node) => { - if (!node.isValid) { - // not inside a [tabs] tag - return [toOriginalStartTag(node), ...node.content, node.toTagEnd()]; - } - const attrs = preprocessAttr(node.attrs); - const name = attrs._default || attrs.name || "Tab"; - const tabId = `tab-${name.replace(/\W/g, "_")}-${generateGUID()}`; - return [ - toNode("input", { - type: "radio", - id: tabId, - name: "tab-group-" + node.groupId, - class: "bb-tab", - checked: node.open, - }), - toNode( - "label", - { - class: "bb-tab-label", - for: tabId, - style: attrs.style, - }, - name, - ), - toNode( - "div", - { - class: "bb-tab-content", - }, - node.content, - ), - ]; - }; - - const SLIDE_TITLE_OPEN = Symbol("slide-title-open"); - const SLIDE_TITLE_CLOSE = Symbol("slide-title-close"); - const SLIDE_CLOSE = Symbol("slide-close"); - const SLIDE_REGEX = - /(?\{slide=)|(?\})|(?\{\/slide\})/i; - - /** - * Adds the accordion tag - * [accordion]{slide=name}content{/slide}[/accordion] - * - * [accordion][slide=name]content[/slide][/accordion] - */ - const accordion = (node) => { - const groupId = generateGUID(); - - // add support for existing {slide} tags style, due to copious amounts of existing content - // also the only way to get true custom content inside a slide due to nesting limitations - const markedContent = generateSlideMarkersFromContent(node.content); - const generatedSlides = generateSlidesFromMarkers(markedContent); - - const filteredContent = generatedSlides - .filter((n) => isTagNode(n) && n.tag === "slide") - .map((content) => { - content.isValid = true; - content.groupId = groupId; - return content; - }); - if (!filteredContent.length) { - // no [slide] tags found - return [toOriginalStartTag(node), ...node.content, node.toTagEnd()]; - } - - const attrs = preprocessAttr(node.attrs); - - if (attrs._default) { - /** @type {string[]} */ - const customSettings = attrs._default.split("|").map((s) => s.trim()); - if (customSettings.includes("bright")) { - attrs.bright = true; - } - if (customSettings.includes("bcenter")) { - attrs.bcenter = true; - } - if (customSettings.includes("bleft")) { - attrs.bleft = true; - } - if (customSettings.includes("fleft")) { - attrs.fleft = true; - } - if (customSettings.includes("fright")) { - attrs.fright = true; - } - if ( - customSettings.some((s) => s.endsWith("px")) || - customSettings.some((s) => s.endsWith("%")) - ) { - attrs.width = customSettings.find((s) => s.endsWith("px") || s.endsWith("%")); - } - } - - let classes = Object.keys(attrs) - .filter((s) => ["bright", "bcenter", "bleft", "fleft", "fright"].includes(s)) - .join(" "); - let style = ""; - if (attrs.width?.endsWith("px") || attrs.width?.endsWith("%")) { - style = `width: ${attrs.width};`; - } - return toNode( - "div", - { class: "bb-accordion " + classes, "data-group-id": groupId, style }, - filteredContent, - ); - }; - - /** - * Locates and splits all {slide} tag components into their respective parts while preserving remaining content - * @param {(TagNode|string)[]} contentArr node content of the accordion tag - * - * @example - * ``` - * ["{slide=test}", "lorem ipsum", "{/slide}"] - * ``` - * becomes - * ``` - * [SLIDE_TITLE_OPEN, "test", SLIDE_TITLE_CLOSE, "lorem ipsum", SLIDE_CLOSE] - * ``` - */ - function generateSlideMarkersFromContent(contentArr) { - contentArr = [...contentArr]; // shallow clone. object nodes are not modified anyway - - const newArr = []; - while (contentArr.length > 0) { - const content = contentArr[0]; - if (isTagNode(content)) { - newArr.push(contentArr.shift()); - continue; - } - const foundIndex = regexIndexOf(content, SLIDE_REGEX); - if (foundIndex === -1) { - newArr.push(contentArr.shift()); - continue; - } - const match = content.match(SLIDE_REGEX); - const preContent = content.slice(0, foundIndex); - const postContent = content.slice(foundIndex + match[0].length); - if (preContent.length) { - newArr.push(preContent); - } - if (match.groups.slideTitleOpen) { - newArr.push(SLIDE_TITLE_OPEN); - } - if (match.groups.slideTitleClose) { - newArr.push(SLIDE_TITLE_CLOSE); - } - if (match.groups.slideClose) { - newArr.push(SLIDE_CLOSE); - } - if (postContent.length) { - contentArr[0] = postContent; - } else { - contentArr.shift(); - } - } - - return newArr; - } - - /** - * Generates slide nodes from markers - * @param {(string | typeof SLIDE_TITLE_OPEN | typeof SLIDE_TITLE_CLOSE | typeof SLIDE_CLOSE | TagNode)[]} markedContent - */ - function generateSlidesFromMarkers(markedContent) { - const nodes = []; - let currentSlide = null; - /** @type {typeof SLIDE_TITLE_OPEN | typeof SLIDE_TITLE_CLOSE | null} */ - let prevMarker = null; - for (const content of markedContent) { - if (content === SLIDE_TITLE_OPEN && prevMarker === null) { - currentSlide = TagNode.create("slide"); - currentSlide.content = []; - currentSlide.customTitle = []; - prevMarker = SLIDE_TITLE_OPEN; - } else if (content === SLIDE_TITLE_CLOSE && prevMarker === SLIDE_TITLE_OPEN) { - prevMarker = SLIDE_TITLE_CLOSE; - continue; - } else if (content === SLIDE_CLOSE && currentSlide && prevMarker === SLIDE_TITLE_CLOSE) { - nodes.push(currentSlide); - currentSlide = null; - prevMarker = null; - } else if (currentSlide) { - if (prevMarker === SLIDE_TITLE_OPEN) { - currentSlide.customTitle.push(markerToString(content)); - } else { - currentSlide.content.push(markerToString(content)); - } - } else { - // no slide open, just add content - nodes.push(markerToString(content)); - } - } - return nodes; - } - - /** - * Processes content into a string. Catches stray markers and converts them back into a string - * @param {string | typeof SLIDE_TITLE_OPEN | typeof SLIDE_TITLE_CLOSE | typeof SLIDE_CLOSE} marker - * @returns expected string - */ - function markerToString(marker) { - switch (marker) { - case SLIDE_TITLE_OPEN: - return "{slide="; - case SLIDE_TITLE_CLOSE: - return "}"; - case SLIDE_CLOSE: - return "{/slide}"; - default: - return marker; - } - } - - const slide = (node) => { - if (!node.isValid) { - // not inside an [accordion] tag - return [toOriginalStartTag(node), ...node.content, node.toTagEnd()]; - } - const attrs = preprocessAttr(node.attrs); - let title = [attrs.title || attrs._default || "Slide"]; - let isOpen = !!attrs.open || false; - let titleAlign = attrs.left ? "left" : attrs.right ? "right" : attrs.center ? "center" : "left"; - if (node.customTitle?.length) { - // slide was created from markers - title = node.customTitle; - // pull out old options from title if they exist - const possibleOptions = title - .filter((t) => typeof t === "string") - .join("") - .toLowerCase() - .split("|") - .map((s) => s.trim()); - if (possibleOptions.includes("open")) { - isOpen = true; - } - if (possibleOptions.includes("right")) { - titleAlign = "right"; - } - if (possibleOptions.includes("center")) { - titleAlign = "center"; - } - if (possibleOptions.includes("left")) { - titleAlign = "left"; - } - title = title.map((t) => { - if (isStringNode(t)) { - t = t.replace(/\|(open|right|center|left)/gi, ""); - } - return t; - }); - } - return [ - { - tag: "details", - attrs: { class: "bb-slide", open: isOpen }, - content: [ - { - tag: "summary", - attrs: { - class: "bb-slide-title", - style: `text-align: ${titleAlign}; ${attrs.style || ""}`, - }, - content: title, - }, - { - tag: "div", - attrs: { class: "bb-slide-content" }, - content: node.content, - }, - ], - }, - ]; - }; - - const accordionTags = { accordion, slide }; - - const tags = { - ...accordionTags, - ...alignment, - ...anchor, - bg, - block, - blockquote, - border, - br, - centerblock, - check, - code, - color, - comment, - divide, - fieldset, - font, - h, - h1, - h2, - h3, - h4, - h5, - h6, - heightrestrict, - highlight, - icode, - imagefloat, - inlinespoiler, - justify, - mail, - newspaper, - nobr, - note, - ooc, - pindent, - plain, - print, - progress, - ...rowcolumn, - thinprogress, - savenl, - sh, - scroll, - side, - size, - spoiler, - sub, - sup, - tab, - tabs, - ...textmessage, - }; - - const availableTags = Object.keys(tags); - const preventParsing = ["plain", "code", "icode"]; - - const preset = createPreset(tags); - - // type, value, line, row, - const TOKEN_TYPE_ID = 'type'; // 0; - const TOKEN_VALUE_ID = 'value'; // 1; - const TOKEN_COLUMN_ID = 'row'; // 2; - const TOKEN_LINE_ID = 'line'; // 3; - const TOKEN_TYPE_WORD = 1; // 'word'; - const TOKEN_TYPE_TAG = 2; // 'tag'; - const TOKEN_TYPE_ATTR_NAME = 3; // 'attr-name'; - const TOKEN_TYPE_ATTR_VALUE = 4; // 'attr-value'; - const TOKEN_TYPE_SPACE = 5; // 'space'; - const TOKEN_TYPE_NEW_LINE = 6; // 'new-line'; - /** - * @param {Token} token - * @returns {string} - */ const getTokenValue = (token)=>{ - if (token && typeof token[TOKEN_VALUE_ID] !== 'undefined') { - return token[TOKEN_VALUE_ID]; - } - return ''; - }; - /** - * @param {Token}token - * @returns {number} - */ const getTokenLine = (token)=>token && token[TOKEN_LINE_ID] || 0; - const getTokenColumn = (token)=>token && token[TOKEN_COLUMN_ID] || 0; - /** - * @param {Token} token - * @returns {boolean} - */ const isTextToken = (token)=>{ - if (token && typeof token[TOKEN_TYPE_ID] !== 'undefined') { - return token[TOKEN_TYPE_ID] === TOKEN_TYPE_SPACE || token[TOKEN_TYPE_ID] === TOKEN_TYPE_NEW_LINE || token[TOKEN_TYPE_ID] === TOKEN_TYPE_WORD; - } - return false; - }; - /** - * @param {Token} token - * @returns {boolean} - */ const isTagToken = (token)=>{ - if (token && typeof token[TOKEN_TYPE_ID] !== 'undefined') { - return token[TOKEN_TYPE_ID] === TOKEN_TYPE_TAG; - } - return false; - }; - const isTagEnd = (token)=>getTokenValue(token).charCodeAt(0) === SLASH.charCodeAt(0); - const isTagStart = (token)=>!isTagEnd(token); - const isAttrNameToken = (token)=>{ - if (token && typeof token[TOKEN_TYPE_ID] !== 'undefined') { - return token[TOKEN_TYPE_ID] === TOKEN_TYPE_ATTR_NAME; - } - return false; - }; - /** - * @param {Token} token - * @returns {boolean} - */ const isAttrValueToken = (token)=>{ - if (token && typeof token[TOKEN_TYPE_ID] !== 'undefined') { - return token[TOKEN_TYPE_ID] === TOKEN_TYPE_ATTR_VALUE; - } - return false; - }; - const getTagName = (token)=>{ - const value = getTokenValue(token); - return isTagEnd(token) ? value.slice(1) : value; - }; - const convertTagToText = (token)=>{ - let text = OPEN_BRAKET; - text += getTokenValue(token); - text += CLOSE_BRAKET; - return text; - }; - class Token { - isEmpty() { - // eslint-disable-next-line no-restricted-globals - return isNaN(this[TOKEN_TYPE_ID]); - } - isText() { - return isTextToken(this); - } - isTag() { - return isTagToken(this); - } - isAttrName() { - return isAttrNameToken(this); - } - isAttrValue() { - return isAttrValueToken(this); - } - isStart() { - return isTagStart(this); - } - isEnd() { - return isTagEnd(this); - } - getName() { - return getTagName(this); - } - getValue() { - return getTokenValue(this); - } - getLine() { - return getTokenLine(this); - } - getColumn() { - return getTokenColumn(this); - } - toString() { - return convertTagToText(this); - } - /** - * @param {String} type - * @param {String} value - * @param line - * @param row - */ constructor(type, value, line, row){ - this[TOKEN_TYPE_ID] = Number(type); - this[TOKEN_VALUE_ID] = String(value); - this[TOKEN_LINE_ID] = Number(line); - this[TOKEN_COLUMN_ID] = Number(row); - } - } - const TYPE_WORD = TOKEN_TYPE_WORD; - const TYPE_TAG = TOKEN_TYPE_TAG; - const TYPE_ATTR_NAME = TOKEN_TYPE_ATTR_NAME; - const TYPE_ATTR_VALUE = TOKEN_TYPE_ATTR_VALUE; - const TYPE_SPACE = TOKEN_TYPE_SPACE; - const TYPE_NEW_LINE = TOKEN_TYPE_NEW_LINE; - - function CharGrabber(source, options) { - const cursor = { - pos: 0, - len: source.length - }; - const substrUntilChar = (char)=>{ - const { pos } = cursor; - const idx = source.indexOf(char, pos); - return idx >= 0 ? source.substring(pos, idx) : ''; - }; - const includes = (val)=>source.indexOf(val, cursor.pos) >= 0; - const hasNext = ()=>cursor.len > cursor.pos; - const isLast = ()=>cursor.pos === cursor.len; - const skip = (num = 1, silent)=>{ - cursor.pos += num; - if (options && options.onSkip && !silent) { - options.onSkip(); - } - }; - const rest = ()=>source.substring(cursor.pos); - const grabN = (num = 0)=>source.substring(cursor.pos, cursor.pos + num); - const curr = ()=>source[cursor.pos]; - const prev = ()=>{ - const prevPos = cursor.pos - 1; - return typeof source[prevPos] !== 'undefined' ? source[prevPos] : null; - }; - const next = ()=>{ - const nextPos = cursor.pos + 1; - return nextPos <= source.length - 1 ? source[nextPos] : null; - }; - const grabWhile = (cond, silent)=>{ - let start = 0; - if (hasNext()) { - start = cursor.pos; - while(hasNext() && cond(curr())){ - skip(1, silent); - } - } - return source.substring(start, cursor.pos); - }; - /** - * @type {skip} - */ this.skip = skip; - /** - * @returns {Boolean} - */ this.hasNext = hasNext; - /** - * @returns {String} - */ this.getCurr = curr; - /** - * @returns {String} - */ this.getRest = rest; - /** - * @returns {String} - */ this.getNext = next; - /** - * @returns {String} - */ this.getPrev = prev; - /** - * @returns {Boolean} - */ this.isLast = isLast; - /** - * @returns {Boolean} - */ this.includes = includes; - /** - * @param {Function} cond - * @param {Boolean} silent - * @return {String} - */ this.grabWhile = grabWhile; - /** - * @param {Number} num - * @return {String} - */ this.grabN = grabN; - /** - * Grabs rest of string until it find a char - * @param {String} char - * @return {String} - */ this.substrUntilChar = substrUntilChar; - } - /** - * Creates a grabber wrapper for source string, that helps to iterate over string char by char - * @param {String} source - * @param {Object} options - * @param {Function} options.onSkip - * @return CharGrabber - */ const createCharGrabber = (source, options)=>new CharGrabber(source, options); - /** - * Trims string from start and end by char - * @example - * trimChar('*hello*', '*') ==> 'hello' - * @param {String} str - * @param {String} charToRemove - * @returns {String} - */ const trimChar = (str, charToRemove)=>{ - while(str.charAt(0) === charToRemove){ - // eslint-disable-next-line no-param-reassign - str = str.substring(1); - } - while(str.charAt(str.length - 1) === charToRemove){ - // eslint-disable-next-line no-param-reassign - str = str.substring(0, str.length - 1); - } - return str; - }; - /** - * Unquotes \" to " - * @param str - * @return {String} - */ const unquote = (str)=>str.replace(BACKSLASH + QUOTEMARK, QUOTEMARK); - function NodeList(values = []) { - const nodes = values; - const getLast = ()=>Array.isArray(nodes) && nodes.length > 0 && typeof nodes[nodes.length - 1] !== 'undefined' ? nodes[nodes.length - 1] : null; - const flushLast = ()=>nodes.length ? nodes.pop() : false; - const push = (value)=>nodes.push(value); - const toArray = ()=>nodes; - this.push = push; - this.toArray = toArray; - this.getLast = getLast; - this.flushLast = flushLast; - } - /** - * - * @param values - * @return {NodeList} - */ const createList = (values = [])=>new NodeList(values); - - // for cases - const EM = '!'; - /** - * Creates a Token entity class - * @param {Number} type - * @param {String} value - * @param {Number} r line number - * @param {Number} cl char number in line - */ const createToken = (type, value, r = 0, cl = 0)=>new Token(type, value, r, cl); - /** - * @typedef {Object} Lexer - * @property {Function} tokenize - * @property {Function} isTokenNested - */ /** - * @param {String} buffer - * @param {Object} options - * @param {Function} options.onToken - * @param {String} options.openTag - * @param {String} options.closeTag - * @param {Boolean} options.enableEscapeTags - * @return {Lexer} - */ function createLexer(buffer, options = {}) { - const STATE_WORD = 0; - const STATE_TAG = 1; - const STATE_TAG_ATTRS = 2; - const TAG_STATE_NAME = 0; - const TAG_STATE_ATTR = 1; - const TAG_STATE_VALUE = 2; - let row = 0; - let col = 0; - let tokenIndex = -1; - let stateMode = STATE_WORD; - let tagMode = TAG_STATE_NAME; - let contextFreeTag = ''; - const tokens = new Array(Math.floor(buffer.length)); - const openTag = options.openTag || OPEN_BRAKET; - const closeTag = options.closeTag || CLOSE_BRAKET; - const escapeTags = !!options.enableEscapeTags; - const contextFreeTags = options.contextFreeTags || []; - const onToken = options.onToken || (()=>{}); - const RESERVED_CHARS = [ - closeTag, - openTag, - QUOTEMARK, - BACKSLASH, - SPACE, - TAB, - EQ, - N, - EM - ]; - const NOT_CHAR_TOKENS = [ - openTag, - SPACE, - TAB, - N - ]; - const WHITESPACES = [ - SPACE, - TAB - ]; - const SPECIAL_CHARS = [ - EQ, - SPACE, - TAB - ]; - const isCharReserved = (char)=>RESERVED_CHARS.indexOf(char) >= 0; - const isNewLine = (char)=>char === N; - const isWhiteSpace = (char)=>WHITESPACES.indexOf(char) >= 0; - const isCharToken = (char)=>NOT_CHAR_TOKENS.indexOf(char) === -1; - const isSpecialChar = (char)=>SPECIAL_CHARS.indexOf(char) >= 0; - const isEscapableChar = (char)=>char === openTag || char === closeTag || char === BACKSLASH; - const isEscapeChar = (char)=>char === BACKSLASH; - const onSkip = ()=>{ - col++; - }; - const unq = (val)=>unquote(trimChar(val, QUOTEMARK)); - const checkContextFreeMode = (name, isClosingTag)=>{ - if (contextFreeTag !== '' && isClosingTag) { - contextFreeTag = ''; - } - if (contextFreeTag === '' && contextFreeTags.includes(name)) { - contextFreeTag = name; - } - }; - const chars = createCharGrabber(buffer, { - onSkip - }); - /** - * Emits newly created token to subscriber - * @param {Number} type - * @param {String} value - */ function emitToken(type, value) { - const token = createToken(type, value, row, col); - onToken(token); - tokenIndex += 1; - tokens[tokenIndex] = token; - } - function nextTagState(tagChars, isSingleValueTag) { - if (tagMode === TAG_STATE_ATTR) { - const validAttrName = (char)=>!(char === EQ || isWhiteSpace(char)); - const name = tagChars.grabWhile(validAttrName); - const isEnd = tagChars.isLast(); - const isValue = tagChars.getCurr() !== EQ; - tagChars.skip(); - if (isEnd || isValue) { - emitToken(TYPE_ATTR_VALUE, unq(name)); - } else { - emitToken(TYPE_ATTR_NAME, name); - } - if (isEnd) { - return TAG_STATE_NAME; - } - if (isValue) { - return TAG_STATE_ATTR; - } - return TAG_STATE_VALUE; - } - if (tagMode === TAG_STATE_VALUE) { - let stateSpecial = false; - const validAttrValue = (char)=>{ - // const isEQ = char === EQ; - const isQM = char === QUOTEMARK; - const prevChar = tagChars.getPrev(); - const nextChar = tagChars.getNext(); - const isPrevSLASH = prevChar === BACKSLASH; - const isNextEQ = nextChar === EQ; - const isWS = isWhiteSpace(char); - // const isPrevWS = isWhiteSpace(prevChar); - const isNextWS = isWhiteSpace(nextChar); - if (stateSpecial && isSpecialChar(char)) { - return true; - } - if (isQM && !isPrevSLASH) { - stateSpecial = !stateSpecial; - if (!stateSpecial && !(isNextEQ || isNextWS)) { - return false; - } - } - if (!isSingleValueTag) { - return isWS === false; - // return (isEQ || isWS) === false; - } - return true; - }; - const name1 = tagChars.grabWhile(validAttrValue); - tagChars.skip(); - emitToken(TYPE_ATTR_VALUE, unq(name1)); - if (tagChars.isLast()) { - return TAG_STATE_NAME; - } - return TAG_STATE_ATTR; - } - const validName = (char)=>!(char === EQ || isWhiteSpace(char) || tagChars.isLast()); - const name2 = tagChars.grabWhile(validName); - emitToken(TYPE_TAG, name2); - checkContextFreeMode(name2); - tagChars.skip(); - // in cases when we has [url=someval]GET[/url] and we dont need to parse all - if (isSingleValueTag) { - return TAG_STATE_VALUE; - } - const hasEQ = tagChars.includes(EQ); - return hasEQ ? TAG_STATE_ATTR : TAG_STATE_VALUE; - } - function stateTag() { - const currChar = chars.getCurr(); - const nextChar = chars.getNext(); - chars.skip(); - // detect case where we have '[My word [tag][/tag]' or we have '[My last line word' - const substr = chars.substrUntilChar(closeTag); - const hasInvalidChars = substr.length === 0 || substr.indexOf(openTag) >= 0; - if (isCharReserved(nextChar) || hasInvalidChars || chars.isLast()) { - emitToken(TYPE_WORD, currChar); - return STATE_WORD; - } - // [myTag ] - const isNoAttrsInTag = substr.indexOf(EQ) === -1; - // [/myTag] - const isClosingTag = substr[0] === SLASH; - if (isNoAttrsInTag || isClosingTag) { - const name = chars.grabWhile((char)=>char !== closeTag); - chars.skip(); // skip closeTag - emitToken(TYPE_TAG, name); - checkContextFreeMode(name, isClosingTag); - return STATE_WORD; - } - return STATE_TAG_ATTRS; - } - function stateAttrs() { - const silent = true; - const tagStr = chars.grabWhile((char)=>char !== closeTag, silent); - const tagGrabber = createCharGrabber(tagStr, { - onSkip - }); - const hasSpace = tagGrabber.includes(SPACE); - tagMode = TAG_STATE_NAME; - while(tagGrabber.hasNext()){ - tagMode = nextTagState(tagGrabber, !hasSpace); - } - chars.skip(); // skip closeTag - return STATE_WORD; - } - function stateWord() { - if (isNewLine(chars.getCurr())) { - emitToken(TYPE_NEW_LINE, chars.getCurr()); - chars.skip(); - col = 0; - row++; - return STATE_WORD; - } - if (isWhiteSpace(chars.getCurr())) { - const word = chars.grabWhile(isWhiteSpace); - emitToken(TYPE_SPACE, word); - return STATE_WORD; - } - if (chars.getCurr() === openTag) { - if (contextFreeTag) { - const fullTagLen = openTag.length + SLASH.length + contextFreeTag.length; - const fullTagName = `${openTag}${SLASH}${contextFreeTag}`; - const foundTag = chars.grabN(fullTagLen); - const isEndContextFreeMode = foundTag === fullTagName; - if (isEndContextFreeMode) { - return STATE_TAG; - } - } else if (chars.includes(closeTag)) { - return STATE_TAG; - } - emitToken(TYPE_WORD, chars.getCurr()); - chars.skip(); - return STATE_WORD; - } - if (escapeTags) { - if (isEscapeChar(chars.getCurr())) { - const currChar = chars.getCurr(); - const nextChar = chars.getNext(); - chars.skip(); // skip the \ without emitting anything - if (isEscapableChar(nextChar)) { - chars.skip(); // skip past the [, ] or \ as well - emitToken(TYPE_WORD, nextChar); - return STATE_WORD; - } - emitToken(TYPE_WORD, currChar); - return STATE_WORD; - } - const isChar = (char)=>isCharToken(char) && !isEscapeChar(char); - const word1 = chars.grabWhile(isChar); - emitToken(TYPE_WORD, word1); - return STATE_WORD; - } - const word2 = chars.grabWhile(isCharToken); - emitToken(TYPE_WORD, word2); - return STATE_WORD; - } - function tokenize() { - stateMode = STATE_WORD; - while(chars.hasNext()){ - switch(stateMode){ - case STATE_TAG: - stateMode = stateTag(); - break; - case STATE_TAG_ATTRS: - stateMode = stateAttrs(); - break; - case STATE_WORD: - default: - stateMode = stateWord(); - break; - } - } - tokens.length = tokenIndex + 1; - return tokens; - } - function isTokenNested(token) { - const value = openTag + SLASH + token.getValue(); - // potential bottleneck - return buffer.indexOf(value) > -1; - } - return { - tokenize, - isTokenNested - }; - } - - /** - * @public - * @param {string} input - * @param {Object} opts - * @param {Function} opts.createTokenizer - * @param {Array} opts.onlyAllowTags - * @param {Array} opts.contextFreeTags - * @param {Boolean} opts.enableEscapeTags - * @param {string} opts.openTag - * @param {string} opts.closeTag - * @return {Array} - */ const parse = (input, opts = {})=>{ - const options = opts; - const openTag = options.openTag || OPEN_BRAKET; - const closeTag = options.closeTag || CLOSE_BRAKET; - const onlyAllowTags = (options.onlyAllowTags || []).filter(Boolean).map((tag)=>tag.toLowerCase()); - let tokenizer = null; - /** - * Result AST of nodes - * @private - * @type {NodeList} - */ const nodes = createList(); - /** - * Temp buffer of nodes that's nested to another node - * @private - * @type {NodeList} - */ const nestedNodes = createList(); - /** - * Temp buffer of nodes [tag..]...[/tag] - * @private - * @type {NodeList} - */ const tagNodes = createList(); - /** - * Temp buffer of tag attributes - * @private - * @type {NodeList} - */ const tagNodesAttrName = createList(); - /** - * Cache for nested tags checks - * @type Set - */ const nestedTagsMap = new Set(); - /** - * @param {Token} token - * @returns {boolean} - */ const isTokenNested = (token)=>{ - const value = token.getValue(); - if (!nestedTagsMap.has(value) && tokenizer.isTokenNested && tokenizer.isTokenNested(token)) { - nestedTagsMap.add(value); - return true; - } - return nestedTagsMap.has(value); - }; - /** - * @private - * @param {string} tagName - * @returns {boolean} - */ const isTagNested = (tagName)=>Boolean(nestedTagsMap.has(tagName)); - /** - * @private - * @param {string} value - * @return {boolean} - */ const isAllowedTag = (value)=>{ - if (onlyAllowTags.length) { - return onlyAllowTags.indexOf(value.toLowerCase()) >= 0; - } - return true; - }; - /** - * Flushes temp tag nodes and its attributes buffers - * @private - * @return {Array} - */ const flushTagNodes = ()=>{ - if (tagNodes.flushLast()) { - tagNodesAttrName.flushLast(); - } - }; - /** - * @private - * @return {Array} - */ const getNodes = ()=>{ - const lastNestedNode = nestedNodes.getLast(); - if (lastNestedNode && Array.isArray(lastNestedNode.content)) { - return lastNestedNode.content; - } - return nodes.toArray(); - }; - /** - * @private - * @param {string|TagNode} node - * @param {boolean} isNested - */ const appendNodeAsString = (node, isNested = true)=>{ - const items = getNodes(); - if (Array.isArray(items)) { - items.push(node.toTagStart({ - openTag, - closeTag - })); - if (node.content.length) { - node.content.forEach((item)=>{ - items.push(item); - }); - if (isNested) { - items.push(node.toTagEnd({ - openTag, - closeTag - })); - } - } - } - }; - /** - * @private - * @param {string|TagNode} node - */ const appendNodes = (node)=>{ - const items = getNodes(); - if (Array.isArray(items)) { - if (isTagNode(node)) { - if (isAllowedTag(node.tag)) { - items.push(node.toTagNode()); - } else { - appendNodeAsString(node); - } - } else { - items.push(node); - } - } - }; - /** - * @private - * @param {Token} token - */ const handleTagStart = (token)=>{ - flushTagNodes(); - const tagNode = TagNode.create(token.getValue()); - const isNested = isTokenNested(token); - tagNodes.push(tagNode); - if (isNested) { - nestedNodes.push(tagNode); - } else { - appendNodes(tagNode); - } - }; - /** - * @private - * @param {Token} token - */ const handleTagEnd = (token)=>{ - flushTagNodes(); - const lastNestedNode = nestedNodes.flushLast(); - if (lastNestedNode) { - appendNodes(lastNestedNode); - } else if (typeof options.onError === 'function') { - const tag = token.getValue(); - const line = token.getLine(); - const column = token.getColumn(); - options.onError({ - message: `Inconsistent tag '${tag}' on line ${line} and column ${column}`, - tagName: tag, - lineNumber: line, - columnNumber: column - }); - } - }; - /** - * @private - * @param {Token} token - */ const handleTag = (token)=>{ - // [tag] - if (token.isStart()) { - handleTagStart(token); - } - // [/tag] - if (token.isEnd()) { - handleTagEnd(token); - } - }; - /** - * @private - * @param {Token} token - */ const handleNode = (token)=>{ - /** - * @type {TagNode} - */ const lastTagNode = tagNodes.getLast(); - const tokenValue = token.getValue(); - const isNested = isTagNested(token); - if (lastTagNode) { - if (token.isAttrName()) { - tagNodesAttrName.push(tokenValue); - lastTagNode.attr(tagNodesAttrName.getLast(), ''); - } else if (token.isAttrValue()) { - const attrName = tagNodesAttrName.getLast(); - if (attrName) { - lastTagNode.attr(attrName, tokenValue); - tagNodesAttrName.flushLast(); - } else { - lastTagNode.attr(tokenValue, tokenValue); - } - } else if (token.isText()) { - if (isNested) { - lastTagNode.append(tokenValue); - } else { - appendNodes(tokenValue); - } - } else if (token.isTag()) { - // if tag is not allowed, just past it as is - appendNodes(token.toString()); - } - } else if (token.isText()) { - appendNodes(tokenValue); - } else if (token.isTag()) { - // if tag is not allowed, just past it as is - appendNodes(token.toString()); - } - }; - /** - * @private - * @param {Token} token - */ const onToken = (token)=>{ - if (token.isTag()) { - handleTag(token); - } else { - handleNode(token); - } - }; - tokenizer = (opts.createTokenizer ? opts.createTokenizer : createLexer)(input, { - onToken, - openTag, - closeTag, - onlyAllowTags: options.onlyAllowTags, - contextFreeTags: options.contextFreeTags, - enableEscapeTags: options.enableEscapeTags - }); - // eslint-disable-next-line no-unused-vars - tokenizer.tokenize(); - // handles situations where we open tag, but forgot close them - // for ex [q]test[/q][u]some[/u][q]some [u]some[/u] // forgot to close [/q] - // so we need to flush nested content to nodes array - const lastNestedNode = nestedNodes.flushLast(); - if (lastNestedNode && isTagNested(lastNestedNode.tag)) { - appendNodeAsString(lastNestedNode, false); - } - return nodes.toArray(); - }; - - /* eslint-disable no-plusplus */ const isObj$2 = (value)=>typeof value === 'object'; - const isBool = (value)=>typeof value === 'boolean'; - function iterate(t, cb) { - const tree = t; - if (Array.isArray(tree)) { - for(let idx = 0; idx < tree.length; idx++){ - tree[idx] = iterate(cb(tree[idx]), cb); - } - } else if (tree && isObj$2(tree) && tree.content) { - iterate(tree.content, cb); - } - return tree; - } - function same(expected, actual) { - if (typeof expected !== typeof actual) { - return false; - } - if (!isObj$2(expected) || expected === null) { - return expected === actual; - } - if (Array.isArray(expected)) { - return expected.every((exp)=>[].some.call(actual, (act)=>same(exp, act))); - } - return Object.keys(expected).every((key)=>{ - const ao = actual[key]; - const eo = expected[key]; - if (isObj$2(eo) && eo !== null && ao !== null) { - return same(eo, ao); - } - if (isBool(eo)) { - return eo !== (ao === null); - } - return ao === eo; - }); - } - function match(expression, cb) { - return Array.isArray(expression) ? iterate(this, (node)=>{ - for(let idx = 0; idx < expression.length; idx++){ - if (same(expression[idx], node)) { - return cb(node); - } - } - return node; - }) : iterate(this, (node)=>same(expression, node) ? cb(node) : node); - } - - function walk$2(cb) { - return iterate(this, cb); - } - function bbob(plugs) { - const plugins = typeof plugs === 'function' ? [ - plugs - ] : plugs || []; - let options = { - skipParse: false - }; - return { - process (input, opts) { - options = opts || {}; - const parseFn = options.parser || parse; - const renderFn = options.render; - const data = options.data || null; - if (typeof parseFn !== 'function') { - throw new Error('"parser" is not a function, please pass to "process(input, { parser })" right function'); - } - let tree = options.skipParse ? input || [] : parseFn(input, options); - // raw tree before modification with plugins - const raw = tree; - tree.messages = []; - tree.options = options; - tree.walk = walk$2; - tree.match = match; - plugins.forEach((plugin)=>{ - tree = plugin(tree, { - parse: parseFn, - render: renderFn, - iterate, - match, - data - }) || tree; - }); - return { - get html () { - if (typeof renderFn !== 'function') { - throw new Error('"render" function not defined, please pass to "process(input, { render })"'); - } - return renderFn(tree, tree.options); - }, - tree, - raw, - messages: tree.messages - }; - } - }; - } - - const SELFCLOSE_END_TAG = '/>'; - const CLOSE_START_TAG = ''; - const renderNode = (node, { stripTags =false })=>{ - if (!node) return ''; - const type = typeof node; - if (type === 'string' || type === 'number') { - return node; - } - if (type === 'object') { - if (stripTags === true) { - // eslint-disable-next-line no-use-before-define - return renderNodes(node.content, { - stripTags - }); - } - if (node.content === null) { - return [ - START_TAG, - node.tag, - attrsToString(node.attrs), - SELFCLOSE_END_TAG - ].join(''); - } - // eslint-disable-next-line no-use-before-define - return [ - START_TAG, - node.tag, - attrsToString(node.attrs), - END_TAG, - renderNodes(node.content), - CLOSE_START_TAG, - node.tag, - END_TAG - ].join(''); - } - if (Array.isArray(node)) { - // eslint-disable-next-line no-use-before-define - return renderNodes(node, { - stripTags - }); - } - return ''; - }; - const renderNodes = (nodes, { stripTags =false } = {})=>[].concat(nodes).reduce((r, node)=>r + renderNode(node, { - stripTags - }), ''); - const render = renderNodes; - - /** - * Plugin that converts consecutive normal spaces (U+0020) to non-breaking spaces (U+00A0). - * To use, put as function similar to the presets. - * - * - * @example - * ```ts - * const output = bbob([preset(), , preserveWhitespace(), lineBreakPlugin()]).process(input, {render}).html - * ``` - */ - - /** - * Checks if input is an object - * @param value input - * @returns if value is an object - */ - const isObj$1 = (value) => typeof value === "object"; - - /** - * Walks the tree of nodes. Checks for node of consecutive spaces. If found replaces every space in - * node with a nonbreaking space. - * Preserves multiple spaces so html won't truncate them. - * - * Walks through entire tree. - * @param t tree of nodes to be processed - * @returns modified tree - */ - const walk$1 = (t) => { - const tree = t; - - if (Array.isArray(tree)) { - for (let idx = 0; idx < tree.length; idx++) { - const child = walk$1(tree[idx]); - if (Array.isArray(child)) { - tree.splice(idx, 1, ...child); - idx += child.length - 1; - } else { - tree[idx] = child; - } - } - } else if (tree && isObj$1(tree) && tree.content) { - walk$1(tree.content); - } - - //Bbob breaks up nodes by the presence of normal spaces. - //So a node with a normal space can only have normal spaces in that node. - if (isStringNode(tree)) { - if (tree.length > 1 && tree[0] === " ") { - let numSpaces = tree.length; - return [String.fromCharCode(160).repeat(numSpaces)]; - } - } - - return tree; - }; - - /** - * Converts consecutive normal spaces (U+0020) to nonbreaking spaces (U+00A0). - * Supply this as a plugin in the preset lists. - * - * @example converts consecutive normal spaces (U+0020) to nonbreaking spaces (U+00A0) - * ```ts - * const output = bbob([preset(), preserveWhitespace(), lineBreakPlugin()]).process(input, {render}).html - * ``` - * - * @returns plugin to be used in BBob process - */ - const preserveWhitespace = () => { - return (tree) => walk$1(tree); - }; - - /** - * Post Processing designed to fix issues with Markdown and BBCode that the parser can't fix. - * - * Separate from markdown-it post processing as it'll be able to manipulate the full string. - * @param {string} raw string from processing through both BBCode and Markdown - * @returns post processed string - */ - function removeNewlineInjects(raw) { - const processed = raw - .replaceAll(MD_NEWLINE_INJECT, "") - .replaceAll(MD_NEWLINE_PRE_INJECT, "") - .replaceAll(MD_NEWLINE_INJECT_COMMENT, ""); // Remove all instances of the injected newline - return processed; - } - - /** - * Injects hoisted code blocks back into the raw string - * @param {string} raw input to inject hoisted code blocks into - * @param {any} data contains hoist map - * @returns string with hoisted code blocks injected - */ - function renderHoistedCodeBlocks(raw, data) { - const hoistMap = data.hoistMap; - for (const [uuid, content] of Object.entries(hoistMap)) { - raw = raw.replaceAll(uuid, content); - } - return raw; - } - - /** - * Performs post processing on the raw string to address any necessary functionality that BBob/MD can't handle with a plugin (i.e. hoisting). - * @param {string} raw processed input from after bbob and md - * @param {any} data from bbob data - * @returns final processed string - */ - function postprocess(raw, data) { - let final = raw; - const postprocessors = [removeNewlineInjects, renderHoistedCodeBlocks]; - for (const postprocessor of postprocessors) { - final = postprocessor(final, data); - } - return final; - } - - /** - * Plugin that converts line breaks to `
` tags. - * To use, put as function similar to the presets. - * - * If a node is marked with `noLineBreakConversion`, then it'll skip the parsing the children - * - * @example - * ```ts - * const output = bbob([preset(), lineBreakPlugin()]).process(input, {render}).html - * ``` - */ - - const isObj = (value) => typeof value === "object"; - const isString = (value) => typeof value === "string"; - - /** - * Walks the tree of nodes. Will add `br` tag to all `\n` in format that can be used in any renderer. - * Preserves \n so that markdown-it doesn't try to treat everything like a block - * - * If a node has the property noLineBreakConversion is encountered, will skip parsing children. - * @param t tree of nodes to be processed - * @returns modified tree - */ - const walk = (t, disableLineBreakConversion = false) => { - const tree = t; - - if (Array.isArray(tree)) { - if (tree.some(isString)) { - // array contains strings. Might be md compatible - tree.unshift(MD_NEWLINE_INJECT); - tree.push(MD_NEWLINE_INJECT); - } - for (let idx = 0; idx < tree.length; idx++) { - const child = walk(tree[idx], disableLineBreakConversion); - if (Array.isArray(child)) { - tree.splice(idx, 1, ...child); - idx += child.length - 1; - } else { - tree[idx] = child; - } - } - } else if (tree && isObj(tree) && tree.content) { - if (tree.isWhitespaceSensitive) { - // applies only to [code] and [icode] - // stop walk. children won't be parsed to have
- return tree.tag ? tree : tree.content; - } - if (tree.disableLineBreakConversion) { - disableLineBreakConversion = true; - } - walk(tree.content, disableLineBreakConversion); - return tree.tag ? tree : tree.content; - } else if (isString(tree) && URL_REGEX_SINGLE_LINE.test(tree.trim())) { - // if the entire string is a URL, then it should be prepared for onebox. - // BBob separates strings by newlines anyway, so we can already assume this is sitting on its own line - // MD_NEWLINE_INJECT is already replacing newline came before or the start of the array, - // so we only need to make sure \n\n is added after the URL - return [tree, MD_NEWLINE_PRE_INJECT]; - } - - if (isEOL(tree)) { - return disableLineBreakConversion - ? ["\n", MD_NEWLINE_INJECT] - : [{ tag: "br", content: null }, MD_NEWLINE_INJECT]; - } - - return tree; - }; - - /** - * Converts `\n` to `
` self closing tag. Supply this as the last plugin in the preset lists - * - * @example converts all line breaks to br - * ```ts - * const output = bbob([preset(), lineBreakPlugin()]).process(input, {render}).html - * ``` - * @example will not convert line breaks inside [nobr] - * ```ts - * const nobr = (node: TagNode) => {return { disableLineBreakConversion: true, content: node.content }}; \\ tag in preset - * ... - * const output = bbob([preset(), lineBreakPlugin()]).process(input, {render}).html - * ``` - * @returns plugin to be used in BBob process - */ - const lineBreakPlugin = () => { - return (tree) => walk(tree); - }; - - /** - * Find all code blocks and hoist them out of the content and into a map for later insertion - * @param {string} raw input to preprocess - * @returns processed string and hoist map - */ - function fenceCodeBlockPreprocess(content, data) { - /** @type {Object.} */ - const hoistMap = {}; - let index = 0; - - const addHoistAndReturnNewStartPoint = (cutOffStart, cutOffEnd, expected, trim = false) => { - const uuid = generateGUID(); - if (cutOffEnd !== -1) { - hoistMap[uuid] = content.substring(cutOffStart, cutOffEnd); - content = content.substring(0, cutOffStart) + uuid + content.substring(cutOffEnd); - } else { - hoistMap[uuid] = content.substring(cutOffStart); - content = content.substring(0, cutOffStart) + uuid + expected; - } - if (trim) { - if (hoistMap[uuid].startsWith("\n")) { - hoistMap[uuid] = hoistMap[uuid].substring(1); - } - if (hoistMap[uuid].endsWith("\n")) { - hoistMap[uuid] = hoistMap[uuid].substring(0, hoistMap[uuid].length - 1); - } - } - return cutOffStart + uuid.length + expected.length; - }; - - while ((index = regexIndexOf(content, ESCAPABLES_REGEX, index)) !== -1) { - const match = ESCAPABLES_REGEX.exec(content.substring(index)); - if (match.groups?.fence) { - const fence = match.groups.fence; - const fenceInfo = match.groups.fenceInfo; - if (content[index] === "\n") { - // Check if the fence is not at the start of the content - index += 1; - } - const closingFenceRegex = new RegExp("\n" + fence + "(\n|$)"); // Find the next fence. By commonmark spec, it should be the same fence length and type - const nextIndex = regexIndexOf(content, closingFenceRegex, index + fence.length); - - const uuid = generateGUID(); - if (nextIndex !== -1) { - hoistMap[uuid] = content.substring(index + fence.length + fenceInfo.length, nextIndex); - } else { - hoistMap[uuid] = content.substring(index + fence.length + fenceInfo.length); - } - // inject bbcode tag before and after the code block. This is to prevent BBob plugin from injecting newlines - const replacement = `[saveNL]\n${fence}${fenceInfo}${uuid}\n${fence}\n[/saveNL]`; - content = - content.substring(0, index) + - replacement + - (nextIndex !== -1 ? content.substring(nextIndex + 1 + fence.length) : ""); - index = index + replacement.length; - } else if (match.groups?.bbcode) { - const bbcode = match.groups.bbcode; - const bbcodeTag = match.groups.bbcodeTag.toLowerCase(); // coerce to lowercase for caseinsensitive matching - const closingTag = `[/${bbcodeTag}]`; - const nextIndex = content.toLowerCase().indexOf(closingTag, index + 1); - index = addHoistAndReturnNewStartPoint(index + bbcode.length, nextIndex, closingTag, true); - } else if (match.groups.backtick) { - const backtick = match.groups.backtick; // contains whole content - const tickStart = match.groups.tickStart; - const tickEnd = match.groups.tickEnd; - index = addHoistAndReturnNewStartPoint( - index + tickStart.length, - index + backtick.length - tickEnd.length, - tickEnd, - ); - } - } - - data.hoistMap = hoistMap; - return [content, data]; - } - - /** - * Find all markdown table blocks and mark them to ignore newlines - * @param {string} raw input to preprocess - * @returns processed string - */ - function mdTableBlockPreprocess(content, data) { - let index = 0; - while ((index = regexIndexOf(content, MD_TABLE_REGEX, index)) !== -1) { - const match = MD_TABLE_REGEX.exec(content.substring(index)); - const table = match[0]; - const replacement = `[saveNL]\n${table}\n[/saveNL]`; - content = content.substring(0, index) + replacement + content.substring(index + table.length); - index = index + replacement.length; - } - return [content, data]; - } - - /** - * Preprocesses input to be formatted for bbob to intake. Handles any necessary functionality that BBob can't handle with a plugin (i.e. hoisting). - * @param {string} raw input to preprocess - * @returns formatted input for bbob to intake - */ - function preprocessRaw(raw) { - let data = {}; - const preprocessors = [fenceCodeBlockPreprocess, mdTableBlockPreprocess]; - for (const preprocessor of preprocessors) { - [raw, data] = preprocessor(raw, data); - } - return [raw, data]; - } - - // TODO: Change error handling so active editing doesn't spam the console - const options = { - onlyAllowTags: [...availableTags], - contextFreeTags: preventParsing, // prevent parsing of children - enableEscapeTags: true, - onError: (err) => - // eslint-disable-next-line no-console - console.warn(err.message, err.lineNumber, err.columnNumber), - }; - - const RpNBBCode = (code, opts) => { - const plugins = [preset()]; - if (opts.preserveWhitespace) { - plugins.push(preserveWhitespace()); - } - plugins.push(lineBreakPlugin()); - const [preprocessed, preprocessedData] = preprocessRaw(code); - return bbob(plugins).process(preprocessed, { - render, - ...options, - data: { - ...preprocessedData, - fonts: new Set(), - }, - }); - }; - - exports.RpNBBCode = RpNBBCode; - exports.postprocess = postprocess; - -})); +/* Source code in bbcode-src */ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).bbcodeParser={})}(this,(function(t){"use strict";const e=t=>"object"==typeof t&&!!t.tag;function n(t,n,s,r){n.walk((n=>e(n)&&t[n.tag]?t[n.tag](n,s,r):n))}const s=(t,e,n=[])=>({tag:t,attrs:e,content:n,gen:!0}),r=t=>{const e=Object.keys(t).join(" "),n=Object.values(t).join(" ");return e===n?{_default:n}:t},o=t=>{if(!t.attrs)return`[${t.tag}]`;const e=r(t.attrs);return e._default?`[${t.tag}=${e._default}]`:t.toTagStart()},i=(t,e,n)=>{const s=t.substring(n||0).search(e);return s>=0?s+(n||0):s},a="\x3c!-- bbcode injected newlines --\x3e\n\n",c="\n\n\x3c!-- bbcode pre injected newlines --\x3e",l="\x3c!-- bbcode injected newlines --\x3e",u=new RegExp(`^${/(http|ftp|https|upload):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])/.source}|${/\!?\[.*\]\((http|ftp|https|upload):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])\)/.source}$`),d=/((\n|^)(?```+|~~~+)(?.*\n))|(?\[(?i?code|plain)(=.*)?\])|(?(?`{1,2})(.*)(?\k))/im,g=/^(\|[^\n]+\|\r?\n)((?:\| ?:?[-]+:? ?)+\|)(\n(?:\|[^\n]+\|\r?\n?)*)?$/m;function b(){let t=(new Date).getTime();return window.performance&&"function"==typeof window.performance.now&&(t+=performance.now()),"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,(function(e){const n=(t+16*Math.random())%16|0;return t=Math.floor(t/16),("x"===e?n:3&n|8).toString(16)}))}const p={left:t=>s("div",{class:"bb-left"},t.content),center:t=>s("div",{class:"bb-center"},t.content),right:t=>s("div",{class:"bb-right"},t.content)},h={a:t=>{const e=r(t.attrs)._default||"";return s("a",{id:`user-anchor-${e.trim()}`,name:`user-anchor-${e.trim()}`},t.content)},goto:t=>{const e=r(t.attrs)._default||"";s("a",{href:`#user-anchor-${e.trim()}`},t.content)}},f=["arial","book antiqua","courier new","georgia","tahoma","times new roman","trebuchet ms","verdana"],m={thin:"100",extralight:"200",light:"300",regular:"400",medium:"500",semibold:"600",bold:"700",extrabold:"800",black:"900"},v=["ital","opsz","slnt","wdth","wght"],y=/(?[a-zA-Z]*)?\s?(?[0-9]*)?\s?(?italic)?/;const w=s("div",{class:"bb-email-header"},""),x=s("div",{class:"bb-email-footer"},s("div",{class:"bb-email-button"},""));const $=["me","them","right","left"],k={textmessage:t=>{const e=r(t.attrs)._default||"Recipient",n=e&&""!==e.trim()?e:"Recipient";return s("div",{class:"bb-textmessage"},[s("div",{class:"bb-textmessage-name"},n),s("div",{class:"bb-textmessage-overflow"},[s("div",{class:"bb-textmessage-content"},t.content)])])},message:t=>{let e=r(t.attrs)._default.toLowerCase();$.includes(e)&&"right"!==e||(e="me"),"left"===e&&(e="them");return s("div",{class:"me"===e?"bb-message-me":"bb-message-them"},[s("div",{class:"bb-message-content"},t.content)])}},T="\n",A="\t",j="=",_='"',C=" ",L="[",S="]",N="/",O="\\",E=t=>"object"==typeof t&&!!t.tag,W=t=>"string"==typeof t,I=(t,e,n)=>Object.keys(t).reduce(e,n),U=t=>E(t)?t.content.reduce(((t,e)=>t+U(e)),0):W(t)?t.length:0,V=t=>t.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'").replace(/(javascript|data|vbscript):/gi,"$1%3A"),D=(t,e)=>{const n=typeof e,s={boolean:()=>e?`${t}`:"",number:()=>`${t}="${e}"`,string:()=>`${t}="${V(e)}"`,object:()=>`${t}="${V(JSON.stringify(e))}"`};return s[n]?s[n]():""},G=t=>null==t?"":I(t,((e,n)=>[...e,D(n,t[n])]),[""]).join(" "),z=(t,e)=>{const n=I(s=e,((t,e)=>s[e]===e?s[e]:null),null);var s;if(n){const s=D(t,n),r={...e};delete r[n];return`${s}${G(r)}`}return`${t}${G(e)}`};class q{attr(t,e){return void 0!==e&&(this.attrs[t]=e),this.attrs[t]}append(t){return((t,e)=>{t.content.push(e)})(this,t)}get length(){return U(this)}toTagStart({openTag:t=L,closeTag:e=S}={}){return`${t}${z(this.tag,this.attrs)}${e}`}toTagEnd({openTag:t=L,closeTag:e=S}={}){return`${t}${N}${this.tag}${e}`}toTagNode(){return new q(this.tag.toLowerCase(),this.attrs,this.content)}toString({openTag:t=L,closeTag:e=S}={}){const n=0===this.content.length,s=this.content.reduce(((n,s)=>n+s.toString({openTag:t,closeTag:e})),""),r=this.toTagStart({openTag:t,closeTag:e});return n?r:`${r}${s}${this.toTagEnd({openTag:t,closeTag:e})}`}constructor(t,e,n){this.tag=t,this.attrs=e,this.content=Array.isArray(n)?n:[n]}}q.create=(t,e={},n=[])=>new q(t,e,n),q.isOf=(t,e)=>t.tag===e;const M=Symbol("slide-title-open"),B=Symbol("slide-title-close"),R=Symbol("slide-close"),P=/(?\{slide=)|(?\})|(?\{\/slide\})/i;function F(t){switch(t){case M:return"{slide=";case B:return"}";case R:return"{/slide}";default:return t}}const J={accordion:t=>{const e=b(),n=function(t){t=[...t];const e=[];for(;t.length>0;){const n=t[0];if(E(n)){e.push(t.shift());continue}const s=i(n,P);if(-1===s){e.push(t.shift());continue}const r=n.match(P),o=n.slice(0,s),a=n.slice(s+r[0].length);o.length&&e.push(o),r.groups.slideTitleOpen&&e.push(M),r.groups.slideTitleClose&&e.push(B),r.groups.slideClose&&e.push(R),a.length?t[0]=a:t.shift()}return e}(t.content),a=function(t){const e=[];let n=null,s=null;for(const r of t)if(r===M&&null===s)n=q.create("slide"),n.content=[],n.customTitle=[],s=M;else{if(r===B&&s===M){s=B;continue}r===R&&n&&s===B?(e.push(n),n=null,s=null):n?s===M?n.customTitle.push(F(r)):n.content.push(F(r)):e.push(F(r))}return e}(n),c=a.filter((t=>E(t)&&"slide"===t.tag)).map((t=>(t.isValid=!0,t.groupId=e,t)));if(!c.length)return[o(t),...t.content,t.toTagEnd()];const l=r(t.attrs);if(l._default){const t=l._default.split("|").map((t=>t.trim()));t.includes("bright")&&(l.bright=!0),t.includes("bcenter")&&(l.bcenter=!0),t.includes("bleft")&&(l.bleft=!0),t.includes("fleft")&&(l.fleft=!0),t.includes("fright")&&(l.fright=!0),(t.some((t=>t.endsWith("px")))||t.some((t=>t.endsWith("%"))))&&(l.width=t.find((t=>t.endsWith("px")||t.endsWith("%"))))}let u=Object.keys(l).filter((t=>["bright","bcenter","bleft","fleft","fright"].includes(t))).join(" "),d="";return(l.width?.endsWith("px")||l.width?.endsWith("%"))&&(d=`width: ${l.width};`),s("div",{class:"bb-accordion "+u,"data-group-id":e,style:d},c)},slide:t=>{if(!t.isValid)return[o(t),...t.content,t.toTagEnd()];const e=r(t.attrs);let n=[e.title||e._default||"Slide"],i=!!e.open||!1,a=e.left?"left":e.right?"right":e.center?"center":"left";if(t.customTitle?.length){n=t.customTitle;const e=n.filter((t=>"string"==typeof t)).join("").toLowerCase().split("|").map((t=>t.trim()));e.includes("open")&&(i=!0),e.includes("right")&&(a="right"),e.includes("center")&&(a="center"),e.includes("left")&&(a="left"),n=n.map((t=>(W(t)&&(t=t.replace(/\|(open|right|center|left)/gi,"")),t)))}return[s("details",{class:"bb-slide",open:i},[s("summary",{class:"bb-slide-title",style:`text-align: ${a}; ${e.style||""}`},n),s("div",{class:"bb-slide-content"},t.content)])]}},Z=["init","click","change","input","dblclick","mouseenter","mouseleave","scroll"],H={...J,...p,...h,animation:(t,e)=>{e.data.previewing||e.data.commonGUID||(e.data.commonGUID="post-"+Math.random().toString(36).substring(2,7));const n=e.data.previewing?"preview":e.data.commonGUID,s=r(t.attrs)?._default||"",o=t.content.filter((t=>E(t)&&"keyframe"===t.tag)).map((t=>{t.isValid=!0;const e=r(t.attrs)._default||"";t.ident=e+(e.match(/^\d+$/)?"%":"");const n=t.content.filter(W).join("").replaceAll(/[\[\]\{\}]/g,"");return t.formatted=`${t.ident}{ ${n} }`,t})),i=`@keyframes ${n}${s} { ${o.map((t=>t.formatted)).join("\n")} }`;return e.data.styles.push(i),[]},bg:t=>{const e=r(t.attrs)._default;return s("div",{style:`background-color: ${e};`,class:"bb-background"},t.content)},block:t=>{const e="block",n=(r(t.attrs)._default||e).toLowerCase(),o=["block","dice","dice10","setting","warning","storyteller","announcement","important","question","encounter","information","character","treasure"].includes(n)?n:e;return s("table",{class:"bb-block","data-bb-block":o},[s("tbody",[s("tr",[s("td",{class:"bb-block-icon"}),s("td",{class:"bb-block-content"},t.content)])])])},blockquote:t=>{const e=r(t.attrs)._default||"";return s("div",{class:"bb-blockquote"},[s("div",{class:"bb-blockquote-left"}),s("div",{class:"bb-blockquote-content"},[t.content,s("div",{class:"bb-blockquote-speaker"},""!==e?`- ${e}`:"")]),s("div",{class:"bb-blockquote-right"})])},border:t=>{const e=r(t.attrs)._default;return s("div",{style:`border: ${e};`,class:"bb-border"},t.content)},br:()=>s("br",{},null),centerblock:t=>{const e=r(t.attrs)._default||"50";return s("div",{style:`margin: 0 auto; width: ${e}%`},t.content)},check:t=>{const e=r(t.attrs)._default||"dot";return s("div",{class:"bb-check","data-type":e},t.content)},class:(t,e)=>{const n=r(t.attrs),s=n.name||n._default;e.data.previewing||e.data.commonGUID||(e.data.commonGUID="post-"+Math.random().toString(36).substring(2,7));const o=e.data.previewing?"preview":e.data.commonGUID,i=s+"__"+o,a=t.content.filter(W).map((t=>t.replaceAll("{post_id}",o).replaceAll(/[\[\]\{\}]/g,"")));let c="";const l=[];return["hover","focus","active","focus-within","focus-visible"].includes(n.state?.toLowerCase())&&(c=":"+n.state.toLowerCase()),n.selector&&(c=n.selector.replace(/[,{}\\\n]/g,"")),n.minWidth?.match(/^[0-9]+[a-z]+$/)&&l.push(`(min-width: ${n.minWidth})`),n.maxWidth?.match(/^[0-9]+[a-z]+$/)&&l.push(`(max-width: ${n.maxWidth})`),a.unshift(`.${i}${c} {`),a.push("}"),l.length&&(a.unshift(`@media ${l.join(" and ")} {`),a.push("}")),e.data.styles.push(a.join("")),[]},code:t=>({isWhitespaceSensitive:!0,content:["```"+(r(t.attrs)._default||"bbcode")+"\n",t.content,"\n```\n"]}),color:t=>{const e=r(t.attrs)._default||"";return""===e.trim()?t.content:s("span",{style:`color: ${e}`},t.content)},comment:t=>s("span",{class:"hidden"},t.content),div:(t,e)=>{if(t.gen)return t;const n=r(t.attrs),o=n.style||n._default,i=n.class;if(!i?.trim())return s("div",{style:o},t.content);e.data.previewing||e.data.commonGUID||(e.data.commonGUID="post-"+Math.random().toString(36).substring(2,7));const a=e.data.previewing?"preview":e.data.commonGUID,c=i.split(" ").map((t=>t+"__"+a)).join(" ");return s("div",{class:c,style:o},t.content)},divide:t=>{const e=(r(t.attrs)._default||"").toLowerCase();return s("span",{class:"bb-divide","data-type":e},t.content)},fieldset:t=>{const e=r(t.attrs)._default||"";return s("fieldset",{class:"bb-fieldset"},[s("legend",{class:"bb-fieldset-legend"},e),s("div",{class:"bb-fieldset"},t.content)])},font:(t,e)=>{const n=r(t.attrs),o=n?._default||n.family||n.name;if(""===o.trim())return t.content;if(f.includes(o.trim().toLowerCase()))return s("span",{style:"font-family: "+o},t.content);const i=(t=>{let e={ital:0,wght:400};if(t?.style){const n=t.style.trim().toLowerCase(),s=y.exec(n).groups||{};s?.italic&&(e.ital=1);const r=s.weight;r&&r>=0&&r<=900?e.wght=r:Object.keys(m).includes(s.named_weight||"")&&(e.wght=m[s.named_weight]),e={...e,...Object.fromEntries(Object.entries(t).filter((([t])=>v.includes(t))))}}return e})(n),a=((t,e)=>(t=t.replaceAll(" ","+"),e=Object.keys(e).sort().reduce(((t,n)=>(t[n]=e[n],t)),{}),"https://fonts.googleapis.com/css2?family="+t+":"+Object.keys(e).join(",")+"@"+Object.values(e).join(",")))(o,i);e.data.fonts.add(a);const c=1===i.ital?"italic":"normal",l=Object.entries(i).filter((([t])=>"wght"!==t&&"ital"!==t));let u="";return l.length&&(u="font-variation-settings: "+l.map((([t,e])=>`'${t}' ${e}`)).join(", ")+";"),s("span",{style:`font-family: ${o}; font-weight: ${i.wght}; font-style: ${c}; ${u}`,"data-font":a},t.content)},h:t=>s("h1",{},t.content),h1:t=>s("h1",{},t.content),h2:t=>s("h2",{},t.content),h3:t=>s("h3",{},t.content),h4:t=>s("h4",{},t.content),h5:t=>s("h5",{},t.content),h6:t=>s("h6",{},t.content),heightrestrict:t=>{const e=function(t){const e=t&&""!==t.trim()?t.replace(/[^\d.]/g,""):0;return e&&e>=0&&e<=700?e:0===e?0:700}(r(t.attrs)._default).toString();return s("div","0"===e?{class:"bb-height-restrict"}:{class:"bb-height-restrict",style:`height: ${e}px;`},t.content)},highlight:t=>s("span",{class:"bb-highlight"},t.content),icode:t=>({isWhitespaceSensitive:!0,content:["`",t.content,"`"]}),imagefloat:t=>{const e=r(t.attrs)._default||"";return s("div",{class:`bb-float-${e}`},t.content)},inlinespoiler:t=>s("span",{class:"bb-inline-spoiler"},t.content),justify:t=>s("div",{class:"bb-justify"},t.content),keyframe:t=>t.isValid?[]:[o(t),...t.content,t.toTagEnd()],mail:t=>{const e=r(t.attrs);let n={mailOption:(e.type||"send").toLowerCase(),person:e.person||"Unknown",subject:e.subject||"Empty"};return s("div",{class:"bb-email","data-bb-email":n.mailOption},[w,(a=n.person,s("div",{class:"bb-email-address"},a)),(i=n.subject,s("div",{class:"bb-email-subject"},i)),(o=t.content,s("div",{class:"bb-email-content"},o)),x]);var o,i,a},newspaper:t=>s("div",{class:"bb-newspaper"},t.content),nobr:t=>({disableLineBreakConversion:!0,content:t.content}),note:t=>s("div",{class:"bb-note"},[s("div",{class:"bb-note-tape"},""),s("div",{class:"bb-note-content"},[t.content,s("div",{class:"bb-note-footer"},"")])]),ooc:t=>s("div",{class:"bb-ooc"},t.content),pindent:t=>s("span",{class:"bb-pindent"},t.content),plain:t=>t.content,print:t=>{const e="print",n=(r(t.attrs)._default||e).toLowerCase(),o=["print","line","graph","parchment"].includes(n)?n:e;return s("div",{class:o===e?"bb-print":`bb-print-${o}`},t.content)},progress:t=>{const e=r(t.attrs)._default;return s("div",{class:"bb-progress"},[s("div",{class:"bb-progress-text"},t.content),s("div",{class:"bb-progress-bar",style:`width: calc(${e}% - 6px)`},""),s("div",{class:"bb-progress-bar-other"},"")])},thinprogress:t=>{const e=r(t.attrs)._default;return s("div",{class:"bb-progress-thin"},[s("div",{class:"bb-progress-text"},t.content),s("div",{class:"bb-progress-bar",style:`width: calc(${e}% - 6px)`},""),s("div",{class:"bb-progress-bar-other"},"")])},savenl:t=>({isWhitespaceSensitive:!0,content:t.content}),sh:t=>s("h2",{},t.content),script:(t,e)=>{const n=r(t.attrs);e.data.previewing||e.data.commonGUID||(e.data.commonGUID="post-"+Math.random().toString(36).substring(2,7));const s=e.data.previewing?"preview":e.data.commonGUID,o=Z.includes(n.on?.toLowerCase()||"init")&&n.on?.toLowerCase()||"init",i={id:s,class:n.class||"",on:o,version:n.version||"",content:t.content.join("")};return e.data.bbscripts.push(i),[]},scroll:t=>{const e=function(t){const e=t&&""!==t.trim()?t.replace(/[^\d.]/g,""):0;return e&&e>=0&&e<=700?e:0===e?0:700}(r(t.attrs)._default);return s("div",{class:"bb-scroll",style:`height: ${e}px`},t.content)},side:t=>{const e=r(t.attrs)._default||"left";return s("div",{class:"bb-side","data-side":e},t.content)},size:t=>{const e=function(t){let e,n={valid:!0};const s=/(\d+\.?\d?)(px|rem)?/i.exec(t),r=36,o=8,i=3,a=.2,c=7,l=1;if(s&&(e=s[1])){switch(n.unit=(s[2]||"").toLowerCase(),n.unit){case"px":e>r?e=r:ei?e=i:ec?e=c:e{const e=r(t.attrs)._default;return s("details",{class:"bb-spoiler"},[s("summary",{},"Spoiler"+(e?`: ${e}`:"")),s("div",{class:"bb-spoiler-content"},t.content)])},sub:t=>s("sub",{},t.content),sup:t=>s("sup",{},t.content),tab:t=>{if(!t.isValid)return[o(t),...t.content,t.toTagEnd()];const e=r(t.attrs),n=e._default||e.name||"Tab",i=`tab-${n.replace(/\W/g,"_")}-${b()}`;return[s("input",{type:"radio",id:i,name:"tab-group-"+t.groupId,class:"bb-tab",checked:t.open}),s("label",{class:"bb-tab-label",for:i,style:e.style},n),s("div",{class:"bb-tab-content"},t.content)]},tabs:t=>{const e=t.content.filter((t=>E(t)&&"tab"===t.tag)),n=b();return e.forEach((t=>{t.isValid=!0,t.groupId=n})),e.length?(e[0].open=!0,s("div",{class:"bb-tabs"},e)):[o(t),...t.content,t.toTagEnd()]},...k,b:t=>s("span",{class:"bbcode-b"},t.content),i:t=>s("span",{class:"bbcode-i"},t.content),u:t=>s("span",{class:"bbcode-u"},t.content),s:t=>s("span",{class:"bbcode-s"},t.content)},K=Object.keys(H),Q=function t(e,s=n){const r=(t={})=>{r.options=Object.assign(r.options||{},t);const n=(t,n)=>s(e,t,n,r.options);return n.options=r.options,n};return r.extend=n=>t(n(e,r.options),s),r}(H),X="type",Y="value",tt="line",et=t=>t&&void 0!==t[Y]?t[Y]:"",nt=t=>et(t).charCodeAt(0)===N.charCodeAt(0);class st{isEmpty(){return isNaN(this[X])}isText(){return!(!(t=this)||void 0===t[X]||5!==t[X]&&6!==t[X]&&1!==t[X]);var t}isTag(){return!(!(t=this)||void 0===t[X])&&2===t[X];var t}isAttrName(){return!(!(t=this)||void 0===t[X])&&3===t[X];var t}isAttrValue(){return!(!(t=this)||void 0===t[X])&&4===t[X];var t}isStart(){return!nt(this)}isEnd(){return nt(this)}getName(){return(t=>{const e=et(t);return nt(t)?e.slice(1):e})(this)}getValue(){return et(this)}getLine(){return(t=this)&&t[tt]||0;var t}getColumn(){return(t=this)&&t.row||0;var t}toString(){return(t=>{let e=L;return e+=et(t),e+=S,e})(this)}constructor(t,e,n,s){this[X]=Number(t),this[Y]=String(e),this[tt]=Number(n),this.row=Number(s)}}function rt(t,e){const n={pos:0,len:t.length},s=()=>n.len>n.pos,r=(t=1,s)=>{n.pos+=t,e&&e.onSkip&&!s&&e.onSkip()},o=()=>t[n.pos];this.skip=r,this.hasNext=s,this.getCurr=o,this.getRest=()=>t.substring(n.pos),this.getNext=()=>{const e=n.pos+1;return e<=t.length-1?t[e]:null},this.getPrev=()=>{const e=n.pos-1;return void 0!==t[e]?t[e]:null},this.isLast=()=>n.pos===n.len,this.includes=e=>t.indexOf(e,n.pos)>=0,this.grabWhile=(e,i)=>{let a=0;if(s())for(a=n.pos;s()&&e(o());)r(1,i);return t.substring(a,n.pos)},this.grabN=(e=0)=>t.substring(n.pos,n.pos+e),this.substrUntilChar=e=>{const{pos:s}=n,r=t.indexOf(e,s);return r>=0?t.substring(s,r):""}}const ot=(t,e)=>new rt(t,e);function it(t=[]){const e=t;this.push=t=>e.push(t),this.toArray=()=>e,this.getLast=()=>Array.isArray(e)&&e.length>0&&void 0!==e[e.length-1]?e[e.length-1]:null,this.flushLast=()=>!!e.length&&e.pop()}const at=(t=[])=>new it(t);function ct(t,e={}){const n=0,s=1,r=2,o=0,i=1,a=2;let c=0,l=0,u=-1,d=n,g=o,b="";const p=new Array(Math.floor(t.length)),h=e.openTag||L,f=e.closeTag||S,m=!!e.enableEscapeTags,v=e.contextFreeTags||[],y=e.onToken||(()=>{}),w=[f,h,_,O,C,A,j,T,"!"],x=[h,C,A,T],$=[C,A],k=[j,C,A],E=t=>w.indexOf(t)>=0,W=t=>t===T,I=t=>$.indexOf(t)>=0,U=t=>-1===x.indexOf(t),V=t=>k.indexOf(t)>=0,D=t=>t===h||t===f||t===O,G=t=>t===O,z=()=>{l++},q=t=>((t,e)=>{for(;t.charAt(0)===e;)t=t.substring(1);for(;t.charAt(t.length-1)===e;)t=t.substring(0,t.length-1);return t})(t,_).replace(O+_,_),M=(t,e)=>{""!==b&&e&&(b=""),""===b&&v.includes(t)&&(b=t)},B=ot(t,{onSkip:z});function R(t,e){const n=((t,e,n=0,s=0)=>new st(t,e,n,s))(t,e,c,l);y(n),u+=1,p[u]=n}function P(t,e){if(g===i){const e=t=>!(t===j||I(t)),n=t.grabWhile(e),s=t.isLast(),r=t.getCurr()!==j;return t.skip(),s||r?R(4,q(n)):R(3,n),s?o:r?i:a}if(g===a){let n=!1;const s=s=>{const r=s===_,o=t.getPrev(),i=t.getNext(),a=o===O,c=i===j,l=I(s),u=I(i);return!(!n||!V(s))||!!(!r||a||(n=!n,n||c||u))&&(!!e||!1===l)},r=t.grabWhile(s);return t.skip(),R(4,q(r)),t.isLast()?o:i}const n=t.grabWhile((e=>!(e===j||I(e)||t.isLast())));if(R(2,n),M(n),t.skip(),e)return a;return t.includes(j)?i:a}function F(){const t=B.getCurr(),e=B.getNext();B.skip();const s=B.substrUntilChar(f),o=0===s.length||s.indexOf(h)>=0;if(E(e)||o||B.isLast())return R(1,t),n;const i=-1===s.indexOf(j),a=s[0]===N;if(i||a){const t=B.grabWhile((t=>t!==f));return B.skip(),R(2,t),M(t,a),n}return r}function J(){const t=B.grabWhile((t=>t!==f),!0),e=ot(t,{onSkip:z}),s=e.includes(C);for(g=o;e.hasNext();)g=P(e,!s);return B.skip(),n}function Z(){if(W(B.getCurr()))return R(6,B.getCurr()),B.skip(),l=0,c++,n;if(I(B.getCurr())){return R(5,B.grabWhile(I)),n}if(B.getCurr()===h){if(b){const t=h.length+1+b.length,e=`${h}${N}${b}`;if(B.grabN(t)===e)return s}else if(B.includes(f))return s;return R(1,B.getCurr()),B.skip(),n}if(m){if(G(B.getCurr())){const t=B.getCurr(),e=B.getNext();return B.skip(),D(e)?(B.skip(),R(1,e),n):(R(1,t),n)}const t=t=>U(t)&&!G(t);return R(1,B.grabWhile(t)),n}return R(1,B.grabWhile(U)),n}return{tokenize:function(){for(d=n;B.hasNext();)switch(d){case s:d=F();break;case r:d=J();break;default:d=Z()}return p.length=u+1,p},isTokenNested:function(e){const n=h+N+e.getValue();return t.indexOf(n)>-1}}}const lt=(t,e={})=>{const n=e,s=n.openTag||L,r=n.closeTag||S,o=(n.onlyAllowTags||[]).filter(Boolean).map((t=>t.toLowerCase()));let i=null;const a=at(),c=at(),l=at(),u=at(),d=new Set,g=t=>Boolean(d.has(t)),b=()=>{l.flushLast()&&u.flushLast()},p=()=>{const t=c.getLast();return t&&Array.isArray(t.content)?t.content:a.toArray()},h=(t,e=!0)=>{const n=p();Array.isArray(n)&&(n.push(t.toTagStart({openTag:s,closeTag:r})),t.content.length&&(t.content.forEach((t=>{n.push(t)})),e&&n.push(t.toTagEnd({openTag:s,closeTag:r}))))},f=t=>{const e=p();var n;Array.isArray(e)&&(E(t)?(n=t.tag,!o.length||o.indexOf(n.toLowerCase())>=0?e.push(t.toTagNode()):h(t)):e.push(t))},m=t=>{b();const e=q.create(t.getValue()),n=(t=>{const e=t.getValue();return!d.has(e)&&i.isTokenNested&&i.isTokenNested(t)?(d.add(e),!0):d.has(e)})(t);l.push(e),n?c.push(e):f(e)},v=t=>{t.isStart()&&m(t),t.isEnd()&&(t=>{b();const e=c.flushLast();if(e)f(e);else if("function"==typeof n.onError){const e=t.getValue(),s=t.getLine(),r=t.getColumn();n.onError({message:`Inconsistent tag '${e}' on line ${s} and column ${r}`,tagName:e,lineNumber:s,columnNumber:r})}})(t)};i=(e.createTokenizer?e.createTokenizer:ct)(t,{onToken:t=>{t.isTag()?v(t):(t=>{const e=l.getLast(),n=t.getValue(),s=g(t);if(e)if(t.isAttrName())u.push(n),e.attr(u.getLast(),"");else if(t.isAttrValue()){const t=u.getLast();t?(e.attr(t,n),u.flushLast()):e.attr(n,n)}else t.isText()?s?e.append(n):f(n):t.isTag()&&f(t.toString());else t.isText()?f(n):t.isTag()&&f(t.toString())})(t)},openTag:s,closeTag:r,onlyAllowTags:n.onlyAllowTags,contextFreeTags:n.contextFreeTags,enableEscapeTags:n.enableEscapeTags}),i.tokenize();const y=c.flushLast();return y&&g(y.tag)&&h(y,!1),a.toArray()},ut=t=>"object"==typeof t,dt=t=>"boolean"==typeof t;function gt(t,e){const n=t;if(Array.isArray(n))for(let t=0;t[].some.call(e,(e=>bt(t,e))))):Object.keys(t).every((n=>{const s=e[n],r=t[n];return ut(r)&&null!==r&&null!==s?bt(r,s):dt(r)?r!==(null===s):s===r})):t===e)}function pt(t,e){return Array.isArray(t)?gt(this,(n=>{for(let s=0;sbt(t,n)?e(n):n))}function ht(t){return gt(this,t)}const ft="/>",mt="",wt=(t,{stripTags:e=!1}={})=>[].concat(t).reduce(((t,n)=>t+((t,{stripTags:e=!1})=>{if(!t)return"";const n=typeof t;return"string"===n||"number"===n?t:"object"===n?!0===e?wt(t.content,{stripTags:e}):null===t.content?[vt,t.tag,G(t.attrs),ft].join(""):[vt,t.tag,G(t.attrs),yt,wt(t.content),mt,t.tag,yt].join(""):Array.isArray(t)?wt(t,{stripTags:e}):""})(n,{stripTags:e})),""),xt=wt,$t=t=>{const e=t;if(Array.isArray(e))for(let t=0;t1&&" "===e[0]){let t=e.length;return[String.fromCharCode(160).repeat(t)]}return e};function kt(t){return t.replaceAll(a,"").replaceAll(c,"").replaceAll("\n"+l,"").replaceAll(l+"\n","").replaceAll(l,"")}function Tt(t,e){const n=e.hoistMap;for(const[e,s]of Object.entries(n))t=t.replaceAll(e,s);return t}function At(t,e){if(0===e.styles.length)return t;return'"+t}function jt(t,e){if(0===e.bbscripts.length)return t;return e.bbscripts.map((t=>``)).join("")+t}const _t=t=>"string"==typeof t,Ct=(t,e=!1)=>{const n=t;if(Array.isArray(n)){n.some(_t)&&(n.unshift(a),n.push(a));for(let t=0;t{const i=b();return-1!==s?(n[i]=t.substring(e,s),t=t.substring(0,e)+i+t.substring(s)):(n[i]=t.substring(e),t=t.substring(0,e)+i+r),o&&(n[i].startsWith("\n")&&(n[i]=n[i].substring(1)),n[i].endsWith("\n")&&(n[i]=n[i].substring(0,n[i].length-1))),e+i.length+r.length};for(;-1!==(s=i(t,d,s));){const e=d.exec(t.substring(s));if(e.groups?.fence){const r=e.groups.fence,o=e.groups.fenceInfo;"\n"===t[s]&&(s+=1);const a=new RegExp("\n"+r+"(\n|$)"),c=i(t,a,s+r.length),l=b();n[l]=-1!==c?t.substring(s+r.length+o.length,c):t.substring(s+r.length+o.length);const u=`[saveNL]\n${r}${o}${l}\n${r}\n[/saveNL]`;t=t.substring(0,s)+u+(-1!==c?t.substring(c+1+r.length):""),s+=u.length}else if(e.groups?.bbcode){const n=e.groups.bbcode,o=`[/${e.groups.bbcodeTag.toLowerCase()}]`,i=t.toLowerCase().indexOf(o,s+1);s=r(s+n.length,i,o,!0)}else if(e.groups.backtick){const t=e.groups.backtick,n=e.groups.tickStart,o=e.groups.tickEnd;s=r(s+n.length,s+t.length-o.length,o)}}return e.hoistMap=n,[t,e]}function St(t,e){let n=0;for(;-1!==(n=i(t,g,n));){const e=g.exec(t.substring(n))[0],s=`[saveNL]\n${e}\n[/saveNL]`;t=t.substring(0,n)+s+t.substring(n+e.length),n+=s.length}return[t,e]}const Nt={onlyAllowTags:[...K],contextFreeTags:["plain","code","icode","class"],enableEscapeTags:!0,onError:t=>{Nt.previewing&&console.warn(t.message,t.lineNumber,t.columnNumber)}},Ot=Q();t.RpNBBCode=(t,e)=>{const n=[Ot];e.preserveWhitespace&&n.push((t=>$t(t))),n.push((t=>Ct(t)));const[s,r]=function(t){let e={};const n=[Lt,St];for(const s of n)[t,e]=s(t,e);return[t,e]}(t);return function(t){const e="function"==typeof t?[t]:t||[];let n={skipParse:!1};return{process(t,s){n=s||{};const r=n.parser||lt,o=n.render,i=n.data||null;if("function"!=typeof r)throw new Error('"parser" is not a function, please pass to "process(input, { parser })" right function');let a=n.skipParse?t||[]:r(t,n);const c=a;return a.messages=[],a.options=n,a.walk=ht,a.match=pt,e.forEach((t=>{a=t(a,{parse:r,render:o,iterate:gt,match:pt,data:i})||a})),{get html(){if("function"!=typeof o)throw new Error('"render" function not defined, please pass to "process(input, { render })"');return o(a,a.options)},tree:a,raw:c,messages:a.messages}}}}(n).process(s,{render:xt,...Nt,data:{...r,previewing:e.previewing,fonts:new Set,styles:[],bbscripts:[]}})},t.postprocess=function(t,e){let n=t;const s=[kt,At,jt,Tt];for(const t of s)n=t(n,e);return n}})); +//# sourceMappingURL=bbcode-parser.min.js.map \ No newline at end of file From 8b2603b3d8a9ae84e44cc1b5d8891ee742f761f5 Mon Sep 17 00:00:00 2001 From: blythechan Date: Wed, 26 Jun 2024 19:54:37 -0400 Subject: [PATCH 10/10] Minified parser. --- assets/bundled/bbcode-parser.min.js | 4 ++-- assets/bundled/bbcode-parser.min.js.map | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/assets/bundled/bbcode-parser.min.js b/assets/bundled/bbcode-parser.min.js index 5016309..dd8c859 100644 --- a/assets/bundled/bbcode-parser.min.js +++ b/assets/bundled/bbcode-parser.min.js @@ -1,3 +1,3 @@ /* Source code in bbcode-src */ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).bbcodeParser={})}(this,(function(t){"use strict";const e=t=>"object"==typeof t&&!!t.tag;function n(t,n,s,r){n.walk((n=>e(n)&&t[n.tag]?t[n.tag](n,s,r):n))}const s=(t,e,n=[])=>({tag:t,attrs:e,content:n,gen:!0}),r=t=>{const e=Object.keys(t).join(" "),n=Object.values(t).join(" ");return e===n?{_default:n}:t},o=t=>{if(!t.attrs)return`[${t.tag}]`;const e=r(t.attrs);return e._default?`[${t.tag}=${e._default}]`:t.toTagStart()},i=(t,e,n)=>{const s=t.substring(n||0).search(e);return s>=0?s+(n||0):s},a="\x3c!-- bbcode injected newlines --\x3e\n\n",c="\n\n\x3c!-- bbcode pre injected newlines --\x3e",l="\x3c!-- bbcode injected newlines --\x3e",u=new RegExp(`^${/(http|ftp|https|upload):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])/.source}|${/\!?\[.*\]\((http|ftp|https|upload):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])\)/.source}$`),d=/((\n|^)(?```+|~~~+)(?.*\n))|(?\[(?i?code|plain)(=.*)?\])|(?(?`{1,2})(.*)(?\k))/im,g=/^(\|[^\n]+\|\r?\n)((?:\| ?:?[-]+:? ?)+\|)(\n(?:\|[^\n]+\|\r?\n?)*)?$/m;function b(){let t=(new Date).getTime();return window.performance&&"function"==typeof window.performance.now&&(t+=performance.now()),"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,(function(e){const n=(t+16*Math.random())%16|0;return t=Math.floor(t/16),("x"===e?n:3&n|8).toString(16)}))}const p={left:t=>s("div",{class:"bb-left"},t.content),center:t=>s("div",{class:"bb-center"},t.content),right:t=>s("div",{class:"bb-right"},t.content)},h={a:t=>{const e=r(t.attrs)._default||"";return s("a",{id:`user-anchor-${e.trim()}`,name:`user-anchor-${e.trim()}`},t.content)},goto:t=>{const e=r(t.attrs)._default||"";s("a",{href:`#user-anchor-${e.trim()}`},t.content)}},f=["arial","book antiqua","courier new","georgia","tahoma","times new roman","trebuchet ms","verdana"],m={thin:"100",extralight:"200",light:"300",regular:"400",medium:"500",semibold:"600",bold:"700",extrabold:"800",black:"900"},v=["ital","opsz","slnt","wdth","wght"],y=/(?[a-zA-Z]*)?\s?(?[0-9]*)?\s?(?italic)?/;const w=s("div",{class:"bb-email-header"},""),x=s("div",{class:"bb-email-footer"},s("div",{class:"bb-email-button"},""));const $=["me","them","right","left"],k={textmessage:t=>{const e=r(t.attrs)._default||"Recipient",n=e&&""!==e.trim()?e:"Recipient";return s("div",{class:"bb-textmessage"},[s("div",{class:"bb-textmessage-name"},n),s("div",{class:"bb-textmessage-overflow"},[s("div",{class:"bb-textmessage-content"},t.content)])])},message:t=>{let e=r(t.attrs)._default.toLowerCase();$.includes(e)&&"right"!==e||(e="me"),"left"===e&&(e="them");return s("div",{class:"me"===e?"bb-message-me":"bb-message-them"},[s("div",{class:"bb-message-content"},t.content)])}},T="\n",A="\t",j="=",_='"',C=" ",L="[",S="]",N="/",O="\\",E=t=>"object"==typeof t&&!!t.tag,W=t=>"string"==typeof t,I=(t,e,n)=>Object.keys(t).reduce(e,n),U=t=>E(t)?t.content.reduce(((t,e)=>t+U(e)),0):W(t)?t.length:0,V=t=>t.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'").replace(/(javascript|data|vbscript):/gi,"$1%3A"),D=(t,e)=>{const n=typeof e,s={boolean:()=>e?`${t}`:"",number:()=>`${t}="${e}"`,string:()=>`${t}="${V(e)}"`,object:()=>`${t}="${V(JSON.stringify(e))}"`};return s[n]?s[n]():""},G=t=>null==t?"":I(t,((e,n)=>[...e,D(n,t[n])]),[""]).join(" "),z=(t,e)=>{const n=I(s=e,((t,e)=>s[e]===e?s[e]:null),null);var s;if(n){const s=D(t,n),r={...e};delete r[n];return`${s}${G(r)}`}return`${t}${G(e)}`};class q{attr(t,e){return void 0!==e&&(this.attrs[t]=e),this.attrs[t]}append(t){return((t,e)=>{t.content.push(e)})(this,t)}get length(){return U(this)}toTagStart({openTag:t=L,closeTag:e=S}={}){return`${t}${z(this.tag,this.attrs)}${e}`}toTagEnd({openTag:t=L,closeTag:e=S}={}){return`${t}${N}${this.tag}${e}`}toTagNode(){return new q(this.tag.toLowerCase(),this.attrs,this.content)}toString({openTag:t=L,closeTag:e=S}={}){const n=0===this.content.length,s=this.content.reduce(((n,s)=>n+s.toString({openTag:t,closeTag:e})),""),r=this.toTagStart({openTag:t,closeTag:e});return n?r:`${r}${s}${this.toTagEnd({openTag:t,closeTag:e})}`}constructor(t,e,n){this.tag=t,this.attrs=e,this.content=Array.isArray(n)?n:[n]}}q.create=(t,e={},n=[])=>new q(t,e,n),q.isOf=(t,e)=>t.tag===e;const M=Symbol("slide-title-open"),B=Symbol("slide-title-close"),R=Symbol("slide-close"),P=/(?\{slide=)|(?\})|(?\{\/slide\})/i;function F(t){switch(t){case M:return"{slide=";case B:return"}";case R:return"{/slide}";default:return t}}const J={accordion:t=>{const e=b(),n=function(t){t=[...t];const e=[];for(;t.length>0;){const n=t[0];if(E(n)){e.push(t.shift());continue}const s=i(n,P);if(-1===s){e.push(t.shift());continue}const r=n.match(P),o=n.slice(0,s),a=n.slice(s+r[0].length);o.length&&e.push(o),r.groups.slideTitleOpen&&e.push(M),r.groups.slideTitleClose&&e.push(B),r.groups.slideClose&&e.push(R),a.length?t[0]=a:t.shift()}return e}(t.content),a=function(t){const e=[];let n=null,s=null;for(const r of t)if(r===M&&null===s)n=q.create("slide"),n.content=[],n.customTitle=[],s=M;else{if(r===B&&s===M){s=B;continue}r===R&&n&&s===B?(e.push(n),n=null,s=null):n?s===M?n.customTitle.push(F(r)):n.content.push(F(r)):e.push(F(r))}return e}(n),c=a.filter((t=>E(t)&&"slide"===t.tag)).map((t=>(t.isValid=!0,t.groupId=e,t)));if(!c.length)return[o(t),...t.content,t.toTagEnd()];const l=r(t.attrs);if(l._default){const t=l._default.split("|").map((t=>t.trim()));t.includes("bright")&&(l.bright=!0),t.includes("bcenter")&&(l.bcenter=!0),t.includes("bleft")&&(l.bleft=!0),t.includes("fleft")&&(l.fleft=!0),t.includes("fright")&&(l.fright=!0),(t.some((t=>t.endsWith("px")))||t.some((t=>t.endsWith("%"))))&&(l.width=t.find((t=>t.endsWith("px")||t.endsWith("%"))))}let u=Object.keys(l).filter((t=>["bright","bcenter","bleft","fleft","fright"].includes(t))).join(" "),d="";return(l.width?.endsWith("px")||l.width?.endsWith("%"))&&(d=`width: ${l.width};`),s("div",{class:"bb-accordion "+u,"data-group-id":e,style:d},c)},slide:t=>{if(!t.isValid)return[o(t),...t.content,t.toTagEnd()];const e=r(t.attrs);let n=[e.title||e._default||"Slide"],i=!!e.open||!1,a=e.left?"left":e.right?"right":e.center?"center":"left";if(t.customTitle?.length){n=t.customTitle;const e=n.filter((t=>"string"==typeof t)).join("").toLowerCase().split("|").map((t=>t.trim()));e.includes("open")&&(i=!0),e.includes("right")&&(a="right"),e.includes("center")&&(a="center"),e.includes("left")&&(a="left"),n=n.map((t=>(W(t)&&(t=t.replace(/\|(open|right|center|left)/gi,"")),t)))}return[s("details",{class:"bb-slide",open:i},[s("summary",{class:"bb-slide-title",style:`text-align: ${a}; ${e.style||""}`},n),s("div",{class:"bb-slide-content"},t.content)])]}},Z=["init","click","change","input","dblclick","mouseenter","mouseleave","scroll"],H={...J,...p,...h,animation:(t,e)=>{e.data.previewing||e.data.commonGUID||(e.data.commonGUID="post-"+Math.random().toString(36).substring(2,7));const n=e.data.previewing?"preview":e.data.commonGUID,s=r(t.attrs)?._default||"",o=t.content.filter((t=>E(t)&&"keyframe"===t.tag)).map((t=>{t.isValid=!0;const e=r(t.attrs)._default||"";t.ident=e+(e.match(/^\d+$/)?"%":"");const n=t.content.filter(W).join("").replaceAll(/[\[\]\{\}]/g,"");return t.formatted=`${t.ident}{ ${n} }`,t})),i=`@keyframes ${n}${s} { ${o.map((t=>t.formatted)).join("\n")} }`;return e.data.styles.push(i),[]},bg:t=>{const e=r(t.attrs)._default;return s("div",{style:`background-color: ${e};`,class:"bb-background"},t.content)},block:t=>{const e="block",n=(r(t.attrs)._default||e).toLowerCase(),o=["block","dice","dice10","setting","warning","storyteller","announcement","important","question","encounter","information","character","treasure"].includes(n)?n:e;return s("table",{class:"bb-block","data-bb-block":o},[s("tbody",[s("tr",[s("td",{class:"bb-block-icon"}),s("td",{class:"bb-block-content"},t.content)])])])},blockquote:t=>{const e=r(t.attrs)._default||"";return s("div",{class:"bb-blockquote"},[s("div",{class:"bb-blockquote-left"}),s("div",{class:"bb-blockquote-content"},[t.content,s("div",{class:"bb-blockquote-speaker"},""!==e?`- ${e}`:"")]),s("div",{class:"bb-blockquote-right"})])},border:t=>{const e=r(t.attrs)._default;return s("div",{style:`border: ${e};`,class:"bb-border"},t.content)},br:()=>s("br",{},null),centerblock:t=>{const e=r(t.attrs)._default||"50";return s("div",{style:`margin: 0 auto; width: ${e}%`},t.content)},check:t=>{const e=r(t.attrs)._default||"dot";return s("div",{class:"bb-check","data-type":e},t.content)},class:(t,e)=>{const n=r(t.attrs),s=n.name||n._default;e.data.previewing||e.data.commonGUID||(e.data.commonGUID="post-"+Math.random().toString(36).substring(2,7));const o=e.data.previewing?"preview":e.data.commonGUID,i=s+"__"+o,a=t.content.filter(W).map((t=>t.replaceAll("{post_id}",o).replaceAll(/[\[\]\{\}]/g,"")));let c="";const l=[];return["hover","focus","active","focus-within","focus-visible"].includes(n.state?.toLowerCase())&&(c=":"+n.state.toLowerCase()),n.selector&&(c=n.selector.replace(/[,{}\\\n]/g,"")),n.minWidth?.match(/^[0-9]+[a-z]+$/)&&l.push(`(min-width: ${n.minWidth})`),n.maxWidth?.match(/^[0-9]+[a-z]+$/)&&l.push(`(max-width: ${n.maxWidth})`),a.unshift(`.${i}${c} {`),a.push("}"),l.length&&(a.unshift(`@media ${l.join(" and ")} {`),a.push("}")),e.data.styles.push(a.join("")),[]},code:t=>({isWhitespaceSensitive:!0,content:["```"+(r(t.attrs)._default||"bbcode")+"\n",t.content,"\n```\n"]}),color:t=>{const e=r(t.attrs)._default||"";return""===e.trim()?t.content:s("span",{style:`color: ${e}`},t.content)},comment:t=>s("span",{class:"hidden"},t.content),div:(t,e)=>{if(t.gen)return t;const n=r(t.attrs),o=n.style||n._default,i=n.class;if(!i?.trim())return s("div",{style:o},t.content);e.data.previewing||e.data.commonGUID||(e.data.commonGUID="post-"+Math.random().toString(36).substring(2,7));const a=e.data.previewing?"preview":e.data.commonGUID,c=i.split(" ").map((t=>t+"__"+a)).join(" ");return s("div",{class:c,style:o},t.content)},divide:t=>{const e=(r(t.attrs)._default||"").toLowerCase();return s("span",{class:"bb-divide","data-type":e},t.content)},fieldset:t=>{const e=r(t.attrs)._default||"";return s("fieldset",{class:"bb-fieldset"},[s("legend",{class:"bb-fieldset-legend"},e),s("div",{class:"bb-fieldset"},t.content)])},font:(t,e)=>{const n=r(t.attrs),o=n?._default||n.family||n.name;if(""===o.trim())return t.content;if(f.includes(o.trim().toLowerCase()))return s("span",{style:"font-family: "+o},t.content);const i=(t=>{let e={ital:0,wght:400};if(t?.style){const n=t.style.trim().toLowerCase(),s=y.exec(n).groups||{};s?.italic&&(e.ital=1);const r=s.weight;r&&r>=0&&r<=900?e.wght=r:Object.keys(m).includes(s.named_weight||"")&&(e.wght=m[s.named_weight]),e={...e,...Object.fromEntries(Object.entries(t).filter((([t])=>v.includes(t))))}}return e})(n),a=((t,e)=>(t=t.replaceAll(" ","+"),e=Object.keys(e).sort().reduce(((t,n)=>(t[n]=e[n],t)),{}),"https://fonts.googleapis.com/css2?family="+t+":"+Object.keys(e).join(",")+"@"+Object.values(e).join(",")))(o,i);e.data.fonts.add(a);const c=1===i.ital?"italic":"normal",l=Object.entries(i).filter((([t])=>"wght"!==t&&"ital"!==t));let u="";return l.length&&(u="font-variation-settings: "+l.map((([t,e])=>`'${t}' ${e}`)).join(", ")+";"),s("span",{style:`font-family: ${o}; font-weight: ${i.wght}; font-style: ${c}; ${u}`,"data-font":a},t.content)},h:t=>s("h1",{},t.content),h1:t=>s("h1",{},t.content),h2:t=>s("h2",{},t.content),h3:t=>s("h3",{},t.content),h4:t=>s("h4",{},t.content),h5:t=>s("h5",{},t.content),h6:t=>s("h6",{},t.content),heightrestrict:t=>{const e=function(t){const e=t&&""!==t.trim()?t.replace(/[^\d.]/g,""):0;return e&&e>=0&&e<=700?e:0===e?0:700}(r(t.attrs)._default).toString();return s("div","0"===e?{class:"bb-height-restrict"}:{class:"bb-height-restrict",style:`height: ${e}px;`},t.content)},highlight:t=>s("span",{class:"bb-highlight"},t.content),icode:t=>({isWhitespaceSensitive:!0,content:["`",t.content,"`"]}),imagefloat:t=>{const e=r(t.attrs)._default||"";return s("div",{class:`bb-float-${e}`},t.content)},inlinespoiler:t=>s("span",{class:"bb-inline-spoiler"},t.content),justify:t=>s("div",{class:"bb-justify"},t.content),keyframe:t=>t.isValid?[]:[o(t),...t.content,t.toTagEnd()],mail:t=>{const e=r(t.attrs);let n={mailOption:(e.type||"send").toLowerCase(),person:e.person||"Unknown",subject:e.subject||"Empty"};return s("div",{class:"bb-email","data-bb-email":n.mailOption},[w,(a=n.person,s("div",{class:"bb-email-address"},a)),(i=n.subject,s("div",{class:"bb-email-subject"},i)),(o=t.content,s("div",{class:"bb-email-content"},o)),x]);var o,i,a},newspaper:t=>s("div",{class:"bb-newspaper"},t.content),nobr:t=>({disableLineBreakConversion:!0,content:t.content}),note:t=>s("div",{class:"bb-note"},[s("div",{class:"bb-note-tape"},""),s("div",{class:"bb-note-content"},[t.content,s("div",{class:"bb-note-footer"},"")])]),ooc:t=>s("div",{class:"bb-ooc"},t.content),pindent:t=>s("span",{class:"bb-pindent"},t.content),plain:t=>t.content,print:t=>{const e="print",n=(r(t.attrs)._default||e).toLowerCase(),o=["print","line","graph","parchment"].includes(n)?n:e;return s("div",{class:o===e?"bb-print":`bb-print-${o}`},t.content)},progress:t=>{const e=r(t.attrs)._default;return s("div",{class:"bb-progress"},[s("div",{class:"bb-progress-text"},t.content),s("div",{class:"bb-progress-bar",style:`width: calc(${e}% - 6px)`},""),s("div",{class:"bb-progress-bar-other"},"")])},thinprogress:t=>{const e=r(t.attrs)._default;return s("div",{class:"bb-progress-thin"},[s("div",{class:"bb-progress-text"},t.content),s("div",{class:"bb-progress-bar",style:`width: calc(${e}% - 6px)`},""),s("div",{class:"bb-progress-bar-other"},"")])},savenl:t=>({isWhitespaceSensitive:!0,content:t.content}),sh:t=>s("h2",{},t.content),script:(t,e)=>{const n=r(t.attrs);e.data.previewing||e.data.commonGUID||(e.data.commonGUID="post-"+Math.random().toString(36).substring(2,7));const s=e.data.previewing?"preview":e.data.commonGUID,o=Z.includes(n.on?.toLowerCase()||"init")&&n.on?.toLowerCase()||"init",i={id:s,class:n.class||"",on:o,version:n.version||"",content:t.content.join("")};return e.data.bbscripts.push(i),[]},scroll:t=>{const e=function(t){const e=t&&""!==t.trim()?t.replace(/[^\d.]/g,""):0;return e&&e>=0&&e<=700?e:0===e?0:700}(r(t.attrs)._default);return s("div",{class:"bb-scroll",style:`height: ${e}px`},t.content)},side:t=>{const e=r(t.attrs)._default||"left";return s("div",{class:"bb-side","data-side":e},t.content)},size:t=>{const e=function(t){let e,n={valid:!0};const s=/(\d+\.?\d?)(px|rem)?/i.exec(t),r=36,o=8,i=3,a=.2,c=7,l=1;if(s&&(e=s[1])){switch(n.unit=(s[2]||"").toLowerCase(),n.unit){case"px":e>r?e=r:ei?e=i:ec?e=c:e{const e=r(t.attrs)._default;return s("details",{class:"bb-spoiler"},[s("summary",{},"Spoiler"+(e?`: ${e}`:"")),s("div",{class:"bb-spoiler-content"},t.content)])},sub:t=>s("sub",{},t.content),sup:t=>s("sup",{},t.content),tab:t=>{if(!t.isValid)return[o(t),...t.content,t.toTagEnd()];const e=r(t.attrs),n=e._default||e.name||"Tab",i=`tab-${n.replace(/\W/g,"_")}-${b()}`;return[s("input",{type:"radio",id:i,name:"tab-group-"+t.groupId,class:"bb-tab",checked:t.open}),s("label",{class:"bb-tab-label",for:i,style:e.style},n),s("div",{class:"bb-tab-content"},t.content)]},tabs:t=>{const e=t.content.filter((t=>E(t)&&"tab"===t.tag)),n=b();return e.forEach((t=>{t.isValid=!0,t.groupId=n})),e.length?(e[0].open=!0,s("div",{class:"bb-tabs"},e)):[o(t),...t.content,t.toTagEnd()]},...k,b:t=>s("span",{class:"bbcode-b"},t.content),i:t=>s("span",{class:"bbcode-i"},t.content),u:t=>s("span",{class:"bbcode-u"},t.content),s:t=>s("span",{class:"bbcode-s"},t.content)},K=Object.keys(H),Q=function t(e,s=n){const r=(t={})=>{r.options=Object.assign(r.options||{},t);const n=(t,n)=>s(e,t,n,r.options);return n.options=r.options,n};return r.extend=n=>t(n(e,r.options),s),r}(H),X="type",Y="value",tt="line",et=t=>t&&void 0!==t[Y]?t[Y]:"",nt=t=>et(t).charCodeAt(0)===N.charCodeAt(0);class st{isEmpty(){return isNaN(this[X])}isText(){return!(!(t=this)||void 0===t[X]||5!==t[X]&&6!==t[X]&&1!==t[X]);var t}isTag(){return!(!(t=this)||void 0===t[X])&&2===t[X];var t}isAttrName(){return!(!(t=this)||void 0===t[X])&&3===t[X];var t}isAttrValue(){return!(!(t=this)||void 0===t[X])&&4===t[X];var t}isStart(){return!nt(this)}isEnd(){return nt(this)}getName(){return(t=>{const e=et(t);return nt(t)?e.slice(1):e})(this)}getValue(){return et(this)}getLine(){return(t=this)&&t[tt]||0;var t}getColumn(){return(t=this)&&t.row||0;var t}toString(){return(t=>{let e=L;return e+=et(t),e+=S,e})(this)}constructor(t,e,n,s){this[X]=Number(t),this[Y]=String(e),this[tt]=Number(n),this.row=Number(s)}}function rt(t,e){const n={pos:0,len:t.length},s=()=>n.len>n.pos,r=(t=1,s)=>{n.pos+=t,e&&e.onSkip&&!s&&e.onSkip()},o=()=>t[n.pos];this.skip=r,this.hasNext=s,this.getCurr=o,this.getRest=()=>t.substring(n.pos),this.getNext=()=>{const e=n.pos+1;return e<=t.length-1?t[e]:null},this.getPrev=()=>{const e=n.pos-1;return void 0!==t[e]?t[e]:null},this.isLast=()=>n.pos===n.len,this.includes=e=>t.indexOf(e,n.pos)>=0,this.grabWhile=(e,i)=>{let a=0;if(s())for(a=n.pos;s()&&e(o());)r(1,i);return t.substring(a,n.pos)},this.grabN=(e=0)=>t.substring(n.pos,n.pos+e),this.substrUntilChar=e=>{const{pos:s}=n,r=t.indexOf(e,s);return r>=0?t.substring(s,r):""}}const ot=(t,e)=>new rt(t,e);function it(t=[]){const e=t;this.push=t=>e.push(t),this.toArray=()=>e,this.getLast=()=>Array.isArray(e)&&e.length>0&&void 0!==e[e.length-1]?e[e.length-1]:null,this.flushLast=()=>!!e.length&&e.pop()}const at=(t=[])=>new it(t);function ct(t,e={}){const n=0,s=1,r=2,o=0,i=1,a=2;let c=0,l=0,u=-1,d=n,g=o,b="";const p=new Array(Math.floor(t.length)),h=e.openTag||L,f=e.closeTag||S,m=!!e.enableEscapeTags,v=e.contextFreeTags||[],y=e.onToken||(()=>{}),w=[f,h,_,O,C,A,j,T,"!"],x=[h,C,A,T],$=[C,A],k=[j,C,A],E=t=>w.indexOf(t)>=0,W=t=>t===T,I=t=>$.indexOf(t)>=0,U=t=>-1===x.indexOf(t),V=t=>k.indexOf(t)>=0,D=t=>t===h||t===f||t===O,G=t=>t===O,z=()=>{l++},q=t=>((t,e)=>{for(;t.charAt(0)===e;)t=t.substring(1);for(;t.charAt(t.length-1)===e;)t=t.substring(0,t.length-1);return t})(t,_).replace(O+_,_),M=(t,e)=>{""!==b&&e&&(b=""),""===b&&v.includes(t)&&(b=t)},B=ot(t,{onSkip:z});function R(t,e){const n=((t,e,n=0,s=0)=>new st(t,e,n,s))(t,e,c,l);y(n),u+=1,p[u]=n}function P(t,e){if(g===i){const e=t=>!(t===j||I(t)),n=t.grabWhile(e),s=t.isLast(),r=t.getCurr()!==j;return t.skip(),s||r?R(4,q(n)):R(3,n),s?o:r?i:a}if(g===a){let n=!1;const s=s=>{const r=s===_,o=t.getPrev(),i=t.getNext(),a=o===O,c=i===j,l=I(s),u=I(i);return!(!n||!V(s))||!!(!r||a||(n=!n,n||c||u))&&(!!e||!1===l)},r=t.grabWhile(s);return t.skip(),R(4,q(r)),t.isLast()?o:i}const n=t.grabWhile((e=>!(e===j||I(e)||t.isLast())));if(R(2,n),M(n),t.skip(),e)return a;return t.includes(j)?i:a}function F(){const t=B.getCurr(),e=B.getNext();B.skip();const s=B.substrUntilChar(f),o=0===s.length||s.indexOf(h)>=0;if(E(e)||o||B.isLast())return R(1,t),n;const i=-1===s.indexOf(j),a=s[0]===N;if(i||a){const t=B.grabWhile((t=>t!==f));return B.skip(),R(2,t),M(t,a),n}return r}function J(){const t=B.grabWhile((t=>t!==f),!0),e=ot(t,{onSkip:z}),s=e.includes(C);for(g=o;e.hasNext();)g=P(e,!s);return B.skip(),n}function Z(){if(W(B.getCurr()))return R(6,B.getCurr()),B.skip(),l=0,c++,n;if(I(B.getCurr())){return R(5,B.grabWhile(I)),n}if(B.getCurr()===h){if(b){const t=h.length+1+b.length,e=`${h}${N}${b}`;if(B.grabN(t)===e)return s}else if(B.includes(f))return s;return R(1,B.getCurr()),B.skip(),n}if(m){if(G(B.getCurr())){const t=B.getCurr(),e=B.getNext();return B.skip(),D(e)?(B.skip(),R(1,e),n):(R(1,t),n)}const t=t=>U(t)&&!G(t);return R(1,B.grabWhile(t)),n}return R(1,B.grabWhile(U)),n}return{tokenize:function(){for(d=n;B.hasNext();)switch(d){case s:d=F();break;case r:d=J();break;default:d=Z()}return p.length=u+1,p},isTokenNested:function(e){const n=h+N+e.getValue();return t.indexOf(n)>-1}}}const lt=(t,e={})=>{const n=e,s=n.openTag||L,r=n.closeTag||S,o=(n.onlyAllowTags||[]).filter(Boolean).map((t=>t.toLowerCase()));let i=null;const a=at(),c=at(),l=at(),u=at(),d=new Set,g=t=>Boolean(d.has(t)),b=()=>{l.flushLast()&&u.flushLast()},p=()=>{const t=c.getLast();return t&&Array.isArray(t.content)?t.content:a.toArray()},h=(t,e=!0)=>{const n=p();Array.isArray(n)&&(n.push(t.toTagStart({openTag:s,closeTag:r})),t.content.length&&(t.content.forEach((t=>{n.push(t)})),e&&n.push(t.toTagEnd({openTag:s,closeTag:r}))))},f=t=>{const e=p();var n;Array.isArray(e)&&(E(t)?(n=t.tag,!o.length||o.indexOf(n.toLowerCase())>=0?e.push(t.toTagNode()):h(t)):e.push(t))},m=t=>{b();const e=q.create(t.getValue()),n=(t=>{const e=t.getValue();return!d.has(e)&&i.isTokenNested&&i.isTokenNested(t)?(d.add(e),!0):d.has(e)})(t);l.push(e),n?c.push(e):f(e)},v=t=>{t.isStart()&&m(t),t.isEnd()&&(t=>{b();const e=c.flushLast();if(e)f(e);else if("function"==typeof n.onError){const e=t.getValue(),s=t.getLine(),r=t.getColumn();n.onError({message:`Inconsistent tag '${e}' on line ${s} and column ${r}`,tagName:e,lineNumber:s,columnNumber:r})}})(t)};i=(e.createTokenizer?e.createTokenizer:ct)(t,{onToken:t=>{t.isTag()?v(t):(t=>{const e=l.getLast(),n=t.getValue(),s=g(t);if(e)if(t.isAttrName())u.push(n),e.attr(u.getLast(),"");else if(t.isAttrValue()){const t=u.getLast();t?(e.attr(t,n),u.flushLast()):e.attr(n,n)}else t.isText()?s?e.append(n):f(n):t.isTag()&&f(t.toString());else t.isText()?f(n):t.isTag()&&f(t.toString())})(t)},openTag:s,closeTag:r,onlyAllowTags:n.onlyAllowTags,contextFreeTags:n.contextFreeTags,enableEscapeTags:n.enableEscapeTags}),i.tokenize();const y=c.flushLast();return y&&g(y.tag)&&h(y,!1),a.toArray()},ut=t=>"object"==typeof t,dt=t=>"boolean"==typeof t;function gt(t,e){const n=t;if(Array.isArray(n))for(let t=0;t[].some.call(e,(e=>bt(t,e))))):Object.keys(t).every((n=>{const s=e[n],r=t[n];return ut(r)&&null!==r&&null!==s?bt(r,s):dt(r)?r!==(null===s):s===r})):t===e)}function pt(t,e){return Array.isArray(t)?gt(this,(n=>{for(let s=0;sbt(t,n)?e(n):n))}function ht(t){return gt(this,t)}const ft="/>",mt="",wt=(t,{stripTags:e=!1}={})=>[].concat(t).reduce(((t,n)=>t+((t,{stripTags:e=!1})=>{if(!t)return"";const n=typeof t;return"string"===n||"number"===n?t:"object"===n?!0===e?wt(t.content,{stripTags:e}):null===t.content?[vt,t.tag,G(t.attrs),ft].join(""):[vt,t.tag,G(t.attrs),yt,wt(t.content),mt,t.tag,yt].join(""):Array.isArray(t)?wt(t,{stripTags:e}):""})(n,{stripTags:e})),""),xt=wt,$t=t=>{const e=t;if(Array.isArray(e))for(let t=0;t1&&" "===e[0]){let t=e.length;return[String.fromCharCode(160).repeat(t)]}return e};function kt(t){return t.replaceAll(a,"").replaceAll(c,"").replaceAll("\n"+l,"").replaceAll(l+"\n","").replaceAll(l,"")}function Tt(t,e){const n=e.hoistMap;for(const[e,s]of Object.entries(n))t=t.replaceAll(e,s);return t}function At(t,e){if(0===e.styles.length)return t;return'"+t}function jt(t,e){if(0===e.bbscripts.length)return t;return e.bbscripts.map((t=>``)).join("")+t}const _t=t=>"string"==typeof t,Ct=(t,e=!1)=>{const n=t;if(Array.isArray(n)){n.some(_t)&&(n.unshift(a),n.push(a));for(let t=0;t{const i=b();return-1!==s?(n[i]=t.substring(e,s),t=t.substring(0,e)+i+t.substring(s)):(n[i]=t.substring(e),t=t.substring(0,e)+i+r),o&&(n[i].startsWith("\n")&&(n[i]=n[i].substring(1)),n[i].endsWith("\n")&&(n[i]=n[i].substring(0,n[i].length-1))),e+i.length+r.length};for(;-1!==(s=i(t,d,s));){const e=d.exec(t.substring(s));if(e.groups?.fence){const r=e.groups.fence,o=e.groups.fenceInfo;"\n"===t[s]&&(s+=1);const a=new RegExp("\n"+r+"(\n|$)"),c=i(t,a,s+r.length),l=b();n[l]=-1!==c?t.substring(s+r.length+o.length,c):t.substring(s+r.length+o.length);const u=`[saveNL]\n${r}${o}${l}\n${r}\n[/saveNL]`;t=t.substring(0,s)+u+(-1!==c?t.substring(c+1+r.length):""),s+=u.length}else if(e.groups?.bbcode){const n=e.groups.bbcode,o=`[/${e.groups.bbcodeTag.toLowerCase()}]`,i=t.toLowerCase().indexOf(o,s+1);s=r(s+n.length,i,o,!0)}else if(e.groups.backtick){const t=e.groups.backtick,n=e.groups.tickStart,o=e.groups.tickEnd;s=r(s+n.length,s+t.length-o.length,o)}}return e.hoistMap=n,[t,e]}function St(t,e){let n=0;for(;-1!==(n=i(t,g,n));){const e=g.exec(t.substring(n))[0],s=`[saveNL]\n${e}\n[/saveNL]`;t=t.substring(0,n)+s+t.substring(n+e.length),n+=s.length}return[t,e]}const Nt={onlyAllowTags:[...K],contextFreeTags:["plain","code","icode","class"],enableEscapeTags:!0,onError:t=>{Nt.previewing&&console.warn(t.message,t.lineNumber,t.columnNumber)}},Ot=Q();t.RpNBBCode=(t,e)=>{const n=[Ot];e.preserveWhitespace&&n.push((t=>$t(t))),n.push((t=>Ct(t)));const[s,r]=function(t){let e={};const n=[Lt,St];for(const s of n)[t,e]=s(t,e);return[t,e]}(t);return function(t){const e="function"==typeof t?[t]:t||[];let n={skipParse:!1};return{process(t,s){n=s||{};const r=n.parser||lt,o=n.render,i=n.data||null;if("function"!=typeof r)throw new Error('"parser" is not a function, please pass to "process(input, { parser })" right function');let a=n.skipParse?t||[]:r(t,n);const c=a;return a.messages=[],a.options=n,a.walk=ht,a.match=pt,e.forEach((t=>{a=t(a,{parse:r,render:o,iterate:gt,match:pt,data:i})||a})),{get html(){if("function"!=typeof o)throw new Error('"render" function not defined, please pass to "process(input, { render })"');return o(a,a.options)},tree:a,raw:c,messages:a.messages}}}}(n).process(s,{render:xt,...Nt,data:{...r,previewing:e.previewing,fonts:new Set,styles:[],bbscripts:[]}})},t.postprocess=function(t,e){let n=t;const s=[kt,At,jt,Tt];for(const t of s)n=t(n,e);return n}})); -//# sourceMappingURL=bbcode-parser.min.js.map \ No newline at end of file +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).bbcodeParser={})}(this,(function(t){"use strict";const e=t=>"object"==typeof t&&!!t.tag;function n(t,n,s,r){n.walk((n=>e(n)&&t[n.tag]?t[n.tag](n,s,r):n))}const s=(t,e,n=[])=>({tag:t,attrs:e,content:n,gen:!0}),r=t=>{const e=Object.keys(t).join(" "),n=Object.values(t).join(" ");return e===n?{_default:n}:t},o=t=>{if(!t.attrs)return`[${t.tag}]`;const e=r(t.attrs);return e._default?`[${t.tag}=${e._default}]`:t.toTagStart()},i=(t,e,n)=>{const s=t.substring(n||0).search(e);return s>=0?s+(n||0):s},a="\x3c!-- bbcode injected newlines --\x3e\n\n",c="\n\n\x3c!-- bbcode pre injected newlines --\x3e",l="\x3c!-- bbcode injected newlines --\x3e",u=new RegExp(`^${/(http|ftp|https|upload):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])/.source}|${/\!?\[.*\]\((http|ftp|https|upload):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])\)/.source}$`),d=/((\n|^)(?```+|~~~+)(?.*\n))|(?\[(?i?code|plain)(=.*)?\])|(?(?`{1,2})(.*)(?\k))/im,g=/^(\|[^\n]+\|\r?\n)((?:\| ?:?[-]+:? ?)+\|)(\n(?:\|[^\n]+\|\r?\n?)*)?$/m;function b(){let t=(new Date).getTime();return window.performance&&"function"==typeof window.performance.now&&(t+=performance.now()),"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,(function(e){const n=(t+16*Math.random())%16|0;return t=Math.floor(t/16),("x"===e?n:3&n|8).toString(16)}))}const p={left:t=>s("div",{class:"bb-left"},t.content),center:t=>s("div",{class:"bb-center"},t.content),right:t=>s("div",{class:"bb-right"},t.content)},h={a:t=>{const e=r(t.attrs)._default||"";return s("a",{id:`user-anchor-${e.trim()}`,name:`user-anchor-${e.trim()}`},t.content)},goto:t=>{const e=r(t.attrs)._default||"";s("a",{href:`#user-anchor-${e.trim()}`},t.content)}},f=["arial","book antiqua","courier new","georgia","tahoma","times new roman","trebuchet ms","verdana"],m={thin:"100",extralight:"200",light:"300",regular:"400",medium:"500",semibold:"600",bold:"700",extrabold:"800",black:"900"},v=["ital","opsz","slnt","wdth","wght"],y=/(?[a-zA-Z]*)?\s?(?[0-9]*)?\s?(?italic)?/;const w=s("div",{class:"bb-email-header"},""),x=s("div",{class:"bb-email-footer"},s("div",{class:"bb-email-button"},"")),$={row:t=>s("div",{class:"bb-row"},t.content),column:t=>{const e=r(t.attrs)._default||"8",n=e.startsWith("span")?`column-width-${e}`:`column-width-span${e}`;return s("div",{class:"bb-column","data-span":n},t.content)}};const k=["me","them","right","left"],T={textmessage:t=>{const e=r(t.attrs)._default||"Recipient",n=e&&""!==e.trim()?e:"Recipient";return s("div",{class:"bb-textmessage"},[s("div",{class:"bb-textmessage-name"},n),s("div",{class:"bb-textmessage-overflow"},[s("div",{class:"bb-textmessage-content"},t.content)])])},message:t=>{let e=r(t.attrs)._default.toLowerCase();k.includes(e)&&"right"!==e||(e="me"),"left"===e&&(e="them");return s("div",{class:"me"===e?"bb-message-me":"bb-message-them"},[s("div",{class:"bb-message-content"},t.content)])}},A="\n",j="\t",_="=",C='"',L=" ",S="[",N="]",O="/",E="\\",W=t=>"object"==typeof t&&!!t.tag,I=t=>"string"==typeof t,U=(t,e,n)=>Object.keys(t).reduce(e,n),V=t=>W(t)?t.content.reduce(((t,e)=>t+V(e)),0):I(t)?t.length:0,D=t=>t.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'").replace(/(javascript|data|vbscript):/gi,"$1%3A"),G=(t,e)=>{const n=typeof e,s={boolean:()=>e?`${t}`:"",number:()=>`${t}="${e}"`,string:()=>`${t}="${D(e)}"`,object:()=>`${t}="${D(JSON.stringify(e))}"`};return s[n]?s[n]():""},z=t=>null==t?"":U(t,((e,n)=>[...e,G(n,t[n])]),[""]).join(" "),q=(t,e)=>{const n=U(s=e,((t,e)=>s[e]===e?s[e]:null),null);var s;if(n){const s=G(t,n),r={...e};delete r[n];return`${s}${z(r)}`}return`${t}${z(e)}`};class M{attr(t,e){return void 0!==e&&(this.attrs[t]=e),this.attrs[t]}append(t){return((t,e)=>{t.content.push(e)})(this,t)}get length(){return V(this)}toTagStart({openTag:t=S,closeTag:e=N}={}){return`${t}${q(this.tag,this.attrs)}${e}`}toTagEnd({openTag:t=S,closeTag:e=N}={}){return`${t}${O}${this.tag}${e}`}toTagNode(){return new M(this.tag.toLowerCase(),this.attrs,this.content)}toString({openTag:t=S,closeTag:e=N}={}){const n=0===this.content.length,s=this.content.reduce(((n,s)=>n+s.toString({openTag:t,closeTag:e})),""),r=this.toTagStart({openTag:t,closeTag:e});return n?r:`${r}${s}${this.toTagEnd({openTag:t,closeTag:e})}`}constructor(t,e,n){this.tag=t,this.attrs=e,this.content=Array.isArray(n)?n:[n]}}M.create=(t,e={},n=[])=>new M(t,e,n),M.isOf=(t,e)=>t.tag===e;const B=Symbol("slide-title-open"),R=Symbol("slide-title-close"),P=Symbol("slide-close"),F=/(?\{slide=)|(?\})|(?\{\/slide\})/i;function J(t){switch(t){case B:return"{slide=";case R:return"}";case P:return"{/slide}";default:return t}}const Z={accordion:t=>{const e=b(),n=function(t){t=[...t];const e=[];for(;t.length>0;){const n=t[0];if(W(n)){e.push(t.shift());continue}const s=i(n,F);if(-1===s){e.push(t.shift());continue}const r=n.match(F),o=n.slice(0,s),a=n.slice(s+r[0].length);o.length&&e.push(o),r.groups.slideTitleOpen&&e.push(B),r.groups.slideTitleClose&&e.push(R),r.groups.slideClose&&e.push(P),a.length?t[0]=a:t.shift()}return e}(t.content),a=function(t){const e=[];let n=null,s=null;for(const r of t)if(r===B&&null===s)n=M.create("slide"),n.content=[],n.customTitle=[],s=B;else{if(r===R&&s===B){s=R;continue}r===P&&n&&s===R?(e.push(n),n=null,s=null):n?s===B?n.customTitle.push(J(r)):n.content.push(J(r)):e.push(J(r))}return e}(n),c=a.filter((t=>W(t)&&"slide"===t.tag)).map((t=>(t.isValid=!0,t.groupId=e,t)));if(!c.length)return[o(t),...t.content,t.toTagEnd()];const l=r(t.attrs);if(l._default){const t=l._default.split("|").map((t=>t.trim()));t.includes("bright")&&(l.bright=!0),t.includes("bcenter")&&(l.bcenter=!0),t.includes("bleft")&&(l.bleft=!0),t.includes("fleft")&&(l.fleft=!0),t.includes("fright")&&(l.fright=!0),(t.some((t=>t.endsWith("px")))||t.some((t=>t.endsWith("%"))))&&(l.width=t.find((t=>t.endsWith("px")||t.endsWith("%"))))}let u=Object.keys(l).filter((t=>["bright","bcenter","bleft","fleft","fright"].includes(t))).join(" "),d="";return(l.width?.endsWith("px")||l.width?.endsWith("%"))&&(d=`width: ${l.width};`),s("div",{class:"bb-accordion "+u,"data-group-id":e,style:d},c)},slide:t=>{if(!t.isValid)return[o(t),...t.content,t.toTagEnd()];const e=r(t.attrs);let n=[e.title||e._default||"Slide"],i=!!e.open||!1,a=e.left?"left":e.right?"right":e.center?"center":"left";if(t.customTitle?.length){n=t.customTitle;const e=n.filter((t=>"string"==typeof t)).join("").toLowerCase().split("|").map((t=>t.trim()));e.includes("open")&&(i=!0),e.includes("right")&&(a="right"),e.includes("center")&&(a="center"),e.includes("left")&&(a="left"),n=n.map((t=>(I(t)&&(t=t.replace(/\|(open|right|center|left)/gi,"")),t)))}return[s("details",{class:"bb-slide",open:i},[s("summary",{class:"bb-slide-title",style:`text-align: ${a}; ${e.style||""}`},n),s("div",{class:"bb-slide-content"},t.content)])]}},H=["init","click","change","input","dblclick","mouseenter","mouseleave","scroll"],K={...Z,...p,...h,animation:(t,e)=>{e.data.previewing||e.data.commonGUID||(e.data.commonGUID="post-"+Math.random().toString(36).substring(2,7));const n=e.data.previewing?"preview":e.data.commonGUID,s=r(t.attrs)?._default||"",o=t.content.filter((t=>W(t)&&"keyframe"===t.tag)).map((t=>{t.isValid=!0;const e=r(t.attrs)._default||"";t.ident=e+(e.match(/^\d+$/)?"%":"");const n=t.content.filter(I).join("").replaceAll(/[\[\]\{\}]/g,"");return t.formatted=`${t.ident}{ ${n} }`,t})),i=`@keyframes ${n}${s} { ${o.map((t=>t.formatted)).join("\n")} }`;return e.data.styles.push(i),[]},bg:t=>{const e=r(t.attrs)._default;return s("div",{style:`background-color: ${e};`,class:"bb-background"},t.content)},block:t=>{const e="block",n=(r(t.attrs)._default||e).toLowerCase(),o=["block","dice","dice10","setting","warning","storyteller","announcement","important","question","encounter","information","character","treasure"].includes(n)?n:e;return s("table",{class:"bb-block","data-bb-block":o},[s("tbody",[s("tr",[s("td",{class:"bb-block-icon"}),s("td",{class:"bb-block-content"},t.content)])])])},blockquote:t=>{const e=r(t.attrs)._default||"";return s("div",{class:"bb-blockquote"},[s("div",{class:"bb-blockquote-left"}),s("div",{class:"bb-blockquote-content"},[t.content,s("div",{class:"bb-blockquote-speaker"},""!==e?`- ${e}`:"")]),s("div",{class:"bb-blockquote-right"})])},border:t=>{const e=r(t.attrs)._default;return s("div",{style:`border: ${e};`,class:"bb-border"},t.content)},br:()=>s("br",{},null),centerblock:t=>{const e=r(t.attrs)._default||"50";return s("div",{style:`margin: 0 auto; width: ${e}%`},t.content)},check:t=>{const e=r(t.attrs)._default||"dot";return s("div",{class:"bb-check","data-type":e},t.content)},class:(t,e)=>{const n=r(t.attrs),s=n.name||n._default;e.data.previewing||e.data.commonGUID||(e.data.commonGUID="post-"+Math.random().toString(36).substring(2,7));const o=e.data.previewing?"preview":e.data.commonGUID,i=s+"__"+o,a=t.content.filter(I).map((t=>t.replaceAll("{post_id}",o).replaceAll(/[\[\]\{\}]/g,"")));let c="";const l=[];return["hover","focus","active","focus-within","focus-visible"].includes(n.state?.toLowerCase())&&(c=":"+n.state.toLowerCase()),n.selector&&(c=n.selector.replace(/[,{}\\\n]/g,"")),n.minWidth?.match(/^[0-9]+[a-z]+$/)&&l.push(`(min-width: ${n.minWidth})`),n.maxWidth?.match(/^[0-9]+[a-z]+$/)&&l.push(`(max-width: ${n.maxWidth})`),a.unshift(`.${i}${c} {`),a.push("}"),l.length&&(a.unshift(`@media ${l.join(" and ")} {`),a.push("}")),e.data.styles.push(a.join("")),[]},code:t=>({isWhitespaceSensitive:!0,content:["```"+(r(t.attrs)._default||"bbcode")+"\n",t.content,"\n```\n"]}),color:t=>{const e=r(t.attrs)._default||"";return""===e.trim()?t.content:s("span",{style:`color: ${e}`},t.content)},comment:t=>s("span",{class:"hidden"},t.content),div:(t,e)=>{if(t.gen)return t;const n=r(t.attrs),o=n.style||n._default,i=n.class;if(!i?.trim())return s("div",{style:o},t.content);e.data.previewing||e.data.commonGUID||(e.data.commonGUID="post-"+Math.random().toString(36).substring(2,7));const a=e.data.previewing?"preview":e.data.commonGUID,c=i.split(" ").map((t=>t+"__"+a)).join(" ");return s("div",{class:c,style:o},t.content)},divide:t=>{const e=(r(t.attrs)._default||"").toLowerCase();return s("span",{class:"bb-divide","data-type":e},t.content)},fieldset:t=>{const e=r(t.attrs)._default||"";return s("fieldset",{class:"bb-fieldset"},[s("legend",{class:"bb-fieldset-legend"},e),s("div",{class:"bb-fieldset"},t.content)])},font:(t,e)=>{const n=r(t.attrs),o=n?._default||n.family||n.name;if(""===o.trim())return t.content;if(f.includes(o.trim().toLowerCase()))return s("span",{style:"font-family: "+o},t.content);const i=(t=>{let e={ital:0,wght:400};if(t?.style){const n=t.style.trim().toLowerCase(),s=y.exec(n).groups||{};s?.italic&&(e.ital=1);const r=s.weight;r&&r>=0&&r<=900?e.wght=r:Object.keys(m).includes(s.named_weight||"")&&(e.wght=m[s.named_weight]),e={...e,...Object.fromEntries(Object.entries(t).filter((([t])=>v.includes(t))))}}return e})(n),a=((t,e)=>(t=t.replaceAll(" ","+"),e=Object.keys(e).sort().reduce(((t,n)=>(t[n]=e[n],t)),{}),"https://fonts.googleapis.com/css2?family="+t+":"+Object.keys(e).join(",")+"@"+Object.values(e).join(",")))(o,i);e.data.fonts.add(a);const c=1===i.ital?"italic":"normal",l=Object.entries(i).filter((([t])=>"wght"!==t&&"ital"!==t));let u="";return l.length&&(u="font-variation-settings: "+l.map((([t,e])=>`'${t}' ${e}`)).join(", ")+";"),s("span",{style:`font-family: ${o}; font-weight: ${i.wght}; font-style: ${c}; ${u}`,"data-font":a},t.content)},h:t=>s("h1",{},t.content),h1:t=>s("h1",{},t.content),h2:t=>s("h2",{},t.content),h3:t=>s("h3",{},t.content),h4:t=>s("h4",{},t.content),h5:t=>s("h5",{},t.content),h6:t=>s("h6",{},t.content),heightrestrict:t=>{const e=function(t){const e=t&&""!==t.trim()?t.replace(/[^\d.]/g,""):0;return e&&e>=0&&e<=700?e:0===e?0:700}(r(t.attrs)._default).toString();return s("div","0"===e?{class:"bb-height-restrict"}:{class:"bb-height-restrict",style:`height: ${e}px;`},t.content)},highlight:t=>s("span",{class:"bb-highlight"},t.content),icode:t=>({isWhitespaceSensitive:!0,content:["`",t.content,"`"]}),imagefloat:t=>{const e=r(t.attrs)._default||"";return s("div",{class:`bb-float-${e}`},t.content)},inlinespoiler:t=>s("span",{class:"bb-inline-spoiler"},t.content),justify:t=>s("div",{class:"bb-justify"},t.content),keyframe:t=>t.isValid?[]:[o(t),...t.content,t.toTagEnd()],mail:t=>{const e=r(t.attrs);let n={mailOption:(e.type||"send").toLowerCase(),person:e.person||"Unknown",subject:e.subject||"Empty"};return s("div",{class:"bb-email","data-bb-email":n.mailOption},[w,(a=n.person,s("div",{class:"bb-email-address"},a)),(i=n.subject,s("div",{class:"bb-email-subject"},i)),(o=t.content,s("div",{class:"bb-email-content"},o)),x]);var o,i,a},newspaper:t=>s("div",{class:"bb-newspaper"},t.content),nobr:t=>({disableLineBreakConversion:!0,content:t.content}),note:t=>s("div",{class:"bb-note"},[s("div",{class:"bb-note-tape"},""),s("div",{class:"bb-note-content"},[t.content,s("div",{class:"bb-note-footer"},"")])]),ooc:t=>s("div",{class:"bb-ooc"},t.content),pindent:t=>s("span",{class:"bb-pindent"},t.content),plain:t=>t.content,print:t=>{const e="print",n=(r(t.attrs)._default||e).toLowerCase(),o=["print","line","graph","parchment"].includes(n)?n:e;return s("div",{class:o===e?"bb-print":`bb-print-${o}`},t.content)},progress:t=>{const e=r(t.attrs)._default;return s("div",{class:"bb-progress"},[s("div",{class:"bb-progress-text"},t.content),s("div",{class:"bb-progress-bar",style:`width: calc(${e}% - 6px)`},""),s("div",{class:"bb-progress-bar-other"},"")])},...$,thinprogress:t=>{const e=r(t.attrs)._default;return s("div",{class:"bb-progress-thin"},[s("div",{class:"bb-progress-text"},t.content),s("div",{class:"bb-progress-bar",style:`width: calc(${e}% - 6px)`},""),s("div",{class:"bb-progress-bar-other"},"")])},savenl:t=>({isWhitespaceSensitive:!0,content:t.content}),sh:t=>s("h2",{},t.content),script:(t,e)=>{const n=r(t.attrs);e.data.previewing||e.data.commonGUID||(e.data.commonGUID="post-"+Math.random().toString(36).substring(2,7));const s=e.data.previewing?"preview":e.data.commonGUID,o=H.includes(n.on?.toLowerCase()||"init")&&n.on?.toLowerCase()||"init",i={id:s,class:n.class||"",on:o,version:n.version||"",content:t.content.join("")};return e.data.bbscripts.push(i),[]},scroll:t=>{const e=function(t){const e=t&&""!==t.trim()?t.replace(/[^\d.]/g,""):0;return e&&e>=0&&e<=700?e:0===e?0:700}(r(t.attrs)._default);return s("div",{class:"bb-scroll",style:`height: ${e}px`},t.content)},side:t=>{const e=r(t.attrs)._default||"left";return s("div",{class:"bb-side","data-side":e},t.content)},size:t=>{const e=function(t){let e,n={valid:!0};const s=/(\d+\.?\d?)(px|rem)?/i.exec(t),r=36,o=8,i=3,a=.2,c=7,l=1;if(s&&(e=s[1])){switch(n.unit=(s[2]||"").toLowerCase(),n.unit){case"px":e>r?e=r:ei?e=i:ec?e=c:e{const e=r(t.attrs)._default;return s("details",{class:"bb-spoiler"},[s("summary",{},"Spoiler"+(e?`: ${e}`:"")),s("div",{class:"bb-spoiler-content"},t.content)])},sub:t=>s("sub",{},t.content),sup:t=>s("sup",{},t.content),tab:t=>{if(!t.isValid)return[o(t),...t.content,t.toTagEnd()];const e=r(t.attrs),n=e._default||e.name||"Tab",i=`tab-${n.replace(/\W/g,"_")}-${b()}`;return[s("input",{type:"radio",id:i,name:"tab-group-"+t.groupId,class:"bb-tab",checked:t.open}),s("label",{class:"bb-tab-label",for:i,style:e.style},n),s("div",{class:"bb-tab-content"},t.content)]},tabs:t=>{const e=t.content.filter((t=>W(t)&&"tab"===t.tag)),n=b();return e.forEach((t=>{t.isValid=!0,t.groupId=n})),e.length?(e[0].open=!0,s("div",{class:"bb-tabs"},e)):[o(t),...t.content,t.toTagEnd()]},...T,b:t=>s("span",{class:"bbcode-b"},t.content),i:t=>s("span",{class:"bbcode-i"},t.content),u:t=>s("span",{class:"bbcode-u"},t.content),s:t=>s("span",{class:"bbcode-s"},t.content)},Q=Object.keys(K),X=function t(e,s=n){const r=(t={})=>{r.options=Object.assign(r.options||{},t);const n=(t,n)=>s(e,t,n,r.options);return n.options=r.options,n};return r.extend=n=>t(n(e,r.options),s),r}(K),Y="type",tt="value",et="line",nt=t=>t&&void 0!==t[tt]?t[tt]:"",st=t=>nt(t).charCodeAt(0)===O.charCodeAt(0);class rt{isEmpty(){return isNaN(this[Y])}isText(){return!(!(t=this)||void 0===t[Y]||5!==t[Y]&&6!==t[Y]&&1!==t[Y]);var t}isTag(){return!(!(t=this)||void 0===t[Y])&&2===t[Y];var t}isAttrName(){return!(!(t=this)||void 0===t[Y])&&3===t[Y];var t}isAttrValue(){return!(!(t=this)||void 0===t[Y])&&4===t[Y];var t}isStart(){return!st(this)}isEnd(){return st(this)}getName(){return(t=>{const e=nt(t);return st(t)?e.slice(1):e})(this)}getValue(){return nt(this)}getLine(){return(t=this)&&t[et]||0;var t}getColumn(){return(t=this)&&t.row||0;var t}toString(){return(t=>{let e=S;return e+=nt(t),e+=N,e})(this)}constructor(t,e,n,s){this[Y]=Number(t),this[tt]=String(e),this[et]=Number(n),this.row=Number(s)}}function ot(t,e){const n={pos:0,len:t.length},s=()=>n.len>n.pos,r=(t=1,s)=>{n.pos+=t,e&&e.onSkip&&!s&&e.onSkip()},o=()=>t[n.pos];this.skip=r,this.hasNext=s,this.getCurr=o,this.getRest=()=>t.substring(n.pos),this.getNext=()=>{const e=n.pos+1;return e<=t.length-1?t[e]:null},this.getPrev=()=>{const e=n.pos-1;return void 0!==t[e]?t[e]:null},this.isLast=()=>n.pos===n.len,this.includes=e=>t.indexOf(e,n.pos)>=0,this.grabWhile=(e,i)=>{let a=0;if(s())for(a=n.pos;s()&&e(o());)r(1,i);return t.substring(a,n.pos)},this.grabN=(e=0)=>t.substring(n.pos,n.pos+e),this.substrUntilChar=e=>{const{pos:s}=n,r=t.indexOf(e,s);return r>=0?t.substring(s,r):""}}const it=(t,e)=>new ot(t,e);function at(t=[]){const e=t;this.push=t=>e.push(t),this.toArray=()=>e,this.getLast=()=>Array.isArray(e)&&e.length>0&&void 0!==e[e.length-1]?e[e.length-1]:null,this.flushLast=()=>!!e.length&&e.pop()}const ct=(t=[])=>new at(t);function lt(t,e={}){const n=0,s=1,r=2,o=0,i=1,a=2;let c=0,l=0,u=-1,d=n,g=o,b="";const p=new Array(Math.floor(t.length)),h=e.openTag||S,f=e.closeTag||N,m=!!e.enableEscapeTags,v=e.contextFreeTags||[],y=e.onToken||(()=>{}),w=[f,h,C,E,L,j,_,A,"!"],x=[h,L,j,A],$=[L,j],k=[_,L,j],T=t=>w.indexOf(t)>=0,W=t=>t===A,I=t=>$.indexOf(t)>=0,U=t=>-1===x.indexOf(t),V=t=>k.indexOf(t)>=0,D=t=>t===h||t===f||t===E,G=t=>t===E,z=()=>{l++},q=t=>((t,e)=>{for(;t.charAt(0)===e;)t=t.substring(1);for(;t.charAt(t.length-1)===e;)t=t.substring(0,t.length-1);return t})(t,C).replace(E+C,C),M=(t,e)=>{""!==b&&e&&(b=""),""===b&&v.includes(t)&&(b=t)},B=it(t,{onSkip:z});function R(t,e){const n=((t,e,n=0,s=0)=>new rt(t,e,n,s))(t,e,c,l);y(n),u+=1,p[u]=n}function P(t,e){if(g===i){const e=t=>!(t===_||I(t)),n=t.grabWhile(e),s=t.isLast(),r=t.getCurr()!==_;return t.skip(),s||r?R(4,q(n)):R(3,n),s?o:r?i:a}if(g===a){let n=!1;const s=s=>{const r=s===C,o=t.getPrev(),i=t.getNext(),a=o===E,c=i===_,l=I(s),u=I(i);return!(!n||!V(s))||!!(!r||a||(n=!n,n||c||u))&&(!!e||!1===l)},r=t.grabWhile(s);return t.skip(),R(4,q(r)),t.isLast()?o:i}const n=t.grabWhile((e=>!(e===_||I(e)||t.isLast())));if(R(2,n),M(n),t.skip(),e)return a;return t.includes(_)?i:a}function F(){const t=B.getCurr(),e=B.getNext();B.skip();const s=B.substrUntilChar(f),o=0===s.length||s.indexOf(h)>=0;if(T(e)||o||B.isLast())return R(1,t),n;const i=-1===s.indexOf(_),a=s[0]===O;if(i||a){const t=B.grabWhile((t=>t!==f));return B.skip(),R(2,t),M(t,a),n}return r}function J(){const t=B.grabWhile((t=>t!==f),!0),e=it(t,{onSkip:z}),s=e.includes(L);for(g=o;e.hasNext();)g=P(e,!s);return B.skip(),n}function Z(){if(W(B.getCurr()))return R(6,B.getCurr()),B.skip(),l=0,c++,n;if(I(B.getCurr())){return R(5,B.grabWhile(I)),n}if(B.getCurr()===h){if(b){const t=h.length+1+b.length,e=`${h}${O}${b}`;if(B.grabN(t)===e)return s}else if(B.includes(f))return s;return R(1,B.getCurr()),B.skip(),n}if(m){if(G(B.getCurr())){const t=B.getCurr(),e=B.getNext();return B.skip(),D(e)?(B.skip(),R(1,e),n):(R(1,t),n)}const t=t=>U(t)&&!G(t);return R(1,B.grabWhile(t)),n}return R(1,B.grabWhile(U)),n}return{tokenize:function(){for(d=n;B.hasNext();)switch(d){case s:d=F();break;case r:d=J();break;default:d=Z()}return p.length=u+1,p},isTokenNested:function(e){const n=h+O+e.getValue();return t.indexOf(n)>-1}}}const ut=(t,e={})=>{const n=e,s=n.openTag||S,r=n.closeTag||N,o=(n.onlyAllowTags||[]).filter(Boolean).map((t=>t.toLowerCase()));let i=null;const a=ct(),c=ct(),l=ct(),u=ct(),d=new Set,g=t=>Boolean(d.has(t)),b=()=>{l.flushLast()&&u.flushLast()},p=()=>{const t=c.getLast();return t&&Array.isArray(t.content)?t.content:a.toArray()},h=(t,e=!0)=>{const n=p();Array.isArray(n)&&(n.push(t.toTagStart({openTag:s,closeTag:r})),t.content.length&&(t.content.forEach((t=>{n.push(t)})),e&&n.push(t.toTagEnd({openTag:s,closeTag:r}))))},f=t=>{const e=p();var n;Array.isArray(e)&&(W(t)?(n=t.tag,!o.length||o.indexOf(n.toLowerCase())>=0?e.push(t.toTagNode()):h(t)):e.push(t))},m=t=>{b();const e=M.create(t.getValue()),n=(t=>{const e=t.getValue();return!d.has(e)&&i.isTokenNested&&i.isTokenNested(t)?(d.add(e),!0):d.has(e)})(t);l.push(e),n?c.push(e):f(e)},v=t=>{t.isStart()&&m(t),t.isEnd()&&(t=>{b();const e=c.flushLast();if(e)f(e);else if("function"==typeof n.onError){const e=t.getValue(),s=t.getLine(),r=t.getColumn();n.onError({message:`Inconsistent tag '${e}' on line ${s} and column ${r}`,tagName:e,lineNumber:s,columnNumber:r})}})(t)};i=(e.createTokenizer?e.createTokenizer:lt)(t,{onToken:t=>{t.isTag()?v(t):(t=>{const e=l.getLast(),n=t.getValue(),s=g(t);if(e)if(t.isAttrName())u.push(n),e.attr(u.getLast(),"");else if(t.isAttrValue()){const t=u.getLast();t?(e.attr(t,n),u.flushLast()):e.attr(n,n)}else t.isText()?s?e.append(n):f(n):t.isTag()&&f(t.toString());else t.isText()?f(n):t.isTag()&&f(t.toString())})(t)},openTag:s,closeTag:r,onlyAllowTags:n.onlyAllowTags,contextFreeTags:n.contextFreeTags,enableEscapeTags:n.enableEscapeTags}),i.tokenize();const y=c.flushLast();return y&&g(y.tag)&&h(y,!1),a.toArray()},dt=t=>"object"==typeof t,gt=t=>"boolean"==typeof t;function bt(t,e){const n=t;if(Array.isArray(n))for(let t=0;t[].some.call(e,(e=>pt(t,e))))):Object.keys(t).every((n=>{const s=e[n],r=t[n];return dt(r)&&null!==r&&null!==s?pt(r,s):gt(r)?r!==(null===s):s===r})):t===e)}function ht(t,e){return Array.isArray(t)?bt(this,(n=>{for(let s=0;spt(t,n)?e(n):n))}function ft(t){return bt(this,t)}const mt="/>",vt="",xt=(t,{stripTags:e=!1}={})=>[].concat(t).reduce(((t,n)=>t+((t,{stripTags:e=!1})=>{if(!t)return"";const n=typeof t;return"string"===n||"number"===n?t:"object"===n?!0===e?xt(t.content,{stripTags:e}):null===t.content?[yt,t.tag,z(t.attrs),mt].join(""):[yt,t.tag,z(t.attrs),wt,xt(t.content),vt,t.tag,wt].join(""):Array.isArray(t)?xt(t,{stripTags:e}):""})(n,{stripTags:e})),""),$t=xt,kt=t=>{const e=t;if(Array.isArray(e))for(let t=0;t1&&" "===e[0]){let t=e.length;return[String.fromCharCode(160).repeat(t)]}return e};function Tt(t){return t.replaceAll(a,"").replaceAll(c,"").replaceAll("\n"+l,"").replaceAll(l+"\n","").replaceAll(l,"")}function At(t,e){const n=e.hoistMap;for(const[e,s]of Object.entries(n))t=t.replaceAll(e,s);return t}function jt(t,e){if(0===e.styles.length)return t;return'"+t}function _t(t,e){if(0===e.bbscripts.length)return t;return e.bbscripts.map((t=>``)).join("")+t}const Ct=t=>"string"==typeof t,Lt=(t,e=!1)=>{const n=t;if(Array.isArray(n)){n.some(Ct)&&(n.unshift(a),n.push(a));for(let t=0;t{const i=b();return-1!==s?(n[i]=t.substring(e,s),t=t.substring(0,e)+i+t.substring(s)):(n[i]=t.substring(e),t=t.substring(0,e)+i+r),o&&(n[i].startsWith("\n")&&(n[i]=n[i].substring(1)),n[i].endsWith("\n")&&(n[i]=n[i].substring(0,n[i].length-1))),e+i.length+r.length};for(;-1!==(s=i(t,d,s));){const e=d.exec(t.substring(s));if(e.groups?.fence){const r=e.groups.fence,o=e.groups.fenceInfo;"\n"===t[s]&&(s+=1);const a=new RegExp("\n"+r+"(\n|$)"),c=i(t,a,s+r.length),l=b();n[l]=-1!==c?t.substring(s+r.length+o.length,c):t.substring(s+r.length+o.length);const u=`[saveNL]\n${r}${o}${l}\n${r}\n[/saveNL]`;t=t.substring(0,s)+u+(-1!==c?t.substring(c+1+r.length):""),s+=u.length}else if(e.groups?.bbcode){const n=e.groups.bbcode,o=`[/${e.groups.bbcodeTag.toLowerCase()}]`,i=t.toLowerCase().indexOf(o,s+1);s=r(s+n.length,i,o,!0)}else if(e.groups.backtick){const t=e.groups.backtick,n=e.groups.tickStart,o=e.groups.tickEnd;s=r(s+n.length,s+t.length-o.length,o)}}return e.hoistMap=n,[t,e]}function Nt(t,e){let n=0;for(;-1!==(n=i(t,g,n));){const e=g.exec(t.substring(n))[0],s=`[saveNL]\n${e}\n[/saveNL]`;t=t.substring(0,n)+s+t.substring(n+e.length),n+=s.length}return[t,e]}const Ot={onlyAllowTags:[...Q],contextFreeTags:["plain","code","icode","class"],enableEscapeTags:!0,onError:t=>{Ot.previewing&&console.warn(t.message,t.lineNumber,t.columnNumber)}},Et=X();t.RpNBBCode=(t,e)=>{const n=[Et];e.preserveWhitespace&&n.push((t=>kt(t))),n.push((t=>Lt(t)));const[s,r]=function(t){let e={};const n=[St,Nt];for(const s of n)[t,e]=s(t,e);return[t,e]}(t);return function(t){const e="function"==typeof t?[t]:t||[];let n={skipParse:!1};return{process(t,s){n=s||{};const r=n.parser||ut,o=n.render,i=n.data||null;if("function"!=typeof r)throw new Error('"parser" is not a function, please pass to "process(input, { parser })" right function');let a=n.skipParse?t||[]:r(t,n);const c=a;return a.messages=[],a.options=n,a.walk=ft,a.match=ht,e.forEach((t=>{a=t(a,{parse:r,render:o,iterate:bt,match:ht,data:i})||a})),{get html(){if("function"!=typeof o)throw new Error('"render" function not defined, please pass to "process(input, { render })"');return o(a,a.options)},tree:a,raw:c,messages:a.messages}}}}(n).process(s,{render:$t,...Ot,data:{...r,previewing:e.previewing,fonts:new Set,styles:[],bbscripts:[]}})},t.postprocess=function(t,e){let n=t;const s=[Tt,jt,_t,At];for(const t of s)n=t(n,e);return n}})); +//# sourceMappingURL=bbcode-parser.min.js.map diff --git a/assets/bundled/bbcode-parser.min.js.map b/assets/bundled/bbcode-parser.min.js.map index d0310d3..d08a3e1 100644 --- a/assets/bundled/bbcode-parser.min.js.map +++ b/assets/bundled/bbcode-parser.min.js.map @@ -1 +1 @@ -{"version":3,"file":"bbcode-parser.min.js","sources":["../../node_modules/@bbob/preset/es/index.js","../../bbcode-src/utils/common.js","../../bbcode-src/tags/alignment.js","../../bbcode-src/tags/anchor.js","../../bbcode-src/tags/font.js","../../bbcode-src/tags/heightrestrict.js","../../bbcode-src/tags/mail.js","../../bbcode-src/tags/size.js","../../bbcode-src/tags/textmessage.js","../../node_modules/@bbob/plugin-helper/es/char.js","../../node_modules/@bbob/plugin-helper/es/helpers.js","../../node_modules/@bbob/plugin-helper/es/TagNode.js","../../bbcode-src/tags/tabs.js","../../bbcode-src/tags/accordion.js","../../bbcode-src/tags/script.js","../../bbcode-src/preset.js","../../bbcode-src/tags/animation.js","../../bbcode-src/tags/background.js","../../bbcode-src/tags/block.js","../../bbcode-src/tags/blockquote.js","../../bbcode-src/tags/border.js","../../bbcode-src/tags/lineBreak.js","../../bbcode-src/tags/centerblock.js","../../bbcode-src/tags/check.js","../../bbcode-src/tags/class.js","../../bbcode-src/tags/code.js","../../bbcode-src/tags/color.js","../../bbcode-src/tags/comment.js","../../bbcode-src/tags/div.js","../../bbcode-src/tags/divide.js","../../bbcode-src/tags/fieldset.js","../../bbcode-src/tags/header.js","../../bbcode-src/tags/highlight.js","../../bbcode-src/tags/imagefloat.js","../../bbcode-src/tags/spoiler.js","../../bbcode-src/tags/justify.js","../../bbcode-src/tags/newspaper.js","../../bbcode-src/tags/note.js","../../bbcode-src/tags/ooc.js","../../bbcode-src/tags/pindent.js","../../bbcode-src/tags/plain.js","../../bbcode-src/tags/print.js","../../bbcode-src/tags/progress.js","../../bbcode-src/tags/thinprogress.js","../../bbcode-src/tags/scroll.js","../../bbcode-src/tags/side.js","../../bbcode-src/tags/subscript.js","../../bbcode-src/tags/superscript.js","../../bbcode-src/tags/discourse-core-replacement.js","../../node_modules/@bbob/parser/es/Token.js","../../node_modules/@bbob/parser/es/utils.js","../../node_modules/@bbob/parser/es/lexer.js","../../node_modules/@bbob/parser/es/parse.js","../../node_modules/@bbob/core/es/utils.js","../../node_modules/@bbob/core/es/index.js","../../node_modules/@bbob/html/es/index.js","../../bbcode-src/plugins/preserveWhitespace.js","../../bbcode-src/utils/postprocess.js","../../bbcode-src/plugins/lineBreak.js","../../bbcode-src/utils/preprocess.js","../../bbcode-src/index.js"],"sourcesContent":["/* eslint-disable indent */ const isTagNode = (el)=>typeof el === 'object' && !!el.tag;\nfunction process(tags, tree, core, options) {\n tree.walk((node)=>isTagNode(node) && tags[node.tag] ? tags[node.tag](node, core, options) : node);\n}\n/**\n * Creates preset for @bbob/core\n * @param defTags {Object}\n * @param processor {Function} a processor function of tree\n * @returns {function(*=): function(*=, *=): void}\n */ function createPreset(defTags, processor = process) {\n const presetFactory = (opts = {})=>{\n presetFactory.options = Object.assign(presetFactory.options || {}, opts);\n const presetExecutor = (tree, core)=>processor(defTags, tree, core, presetFactory.options);\n presetExecutor.options = presetFactory.options;\n return presetExecutor;\n };\n presetFactory.extend = (callback)=>createPreset(callback(defTags, presetFactory.options), processor);\n return presetFactory;\n}\nexport { createPreset };\nexport default createPreset;\n","/**\n * Generate the node object.\n *\n * Contains additional logic to help break any unintended side effects of the top down parsing of bbob.\n * @param {string} tag name of the tag\n * @param {Object} attrs attributes of the tag\n * @param {any} content contents of the tag. `[]` will create an empty tag. `null` will create a self closing tag\n *\n * @example\n * ```\n * toNode(\"div\", { class: \"class\" }, \"content\")\n * ```\n * becomes\n * ```\n * {\n * tag: \"div\",\n * attrs: { class: \"class\" },\n * content: \"content\",\n * gen: true,\n * }\n */\nconst toNode = (tag, attrs, content = []) => ({\n tag,\n attrs,\n content,\n gen: true,\n});\n\n/**\n * Preprocess attributes of a node to either combine all values into a single default value\n * or return a keyed attribute list\n * @param {any} attrs object of bbcode node attrs\n * @param {string[]} predefinedKeys array of predefined keys to be captured\n * @returns processed attributes\n */\nconst preprocessAttr = (attrs) => {\n const keys = Object.keys(attrs).join(\" \");\n const vals = Object.values(attrs).join(\" \");\n if (keys === vals) {\n return {\n _default: vals,\n };\n } else {\n return attrs;\n }\n};\n\n/**\n * Attempts to return tag into its original form with proper attributes\n * @returns string of tag start\n */\nconst toOriginalStartTag = (node) => {\n if (!node.attrs) {\n return `[${node.tag}]`;\n }\n const attrs = preprocessAttr(node.attrs);\n if (attrs._default) {\n return `[${node.tag}=${attrs._default}]`;\n } else {\n return node.toTagStart();\n }\n};\n\n/**\n * Given a string, find the first position of a regex match\n * @param {string} string to test against\n * @param {RegExp} regex to test with\n * @param {number} startpos starting position. Defaults to 0\n * @returns index of the first match of the regex in the string\n */\nconst regexIndexOf = (string, regex, startpos) => {\n const indexOf = string.substring(startpos || 0).search(regex);\n return indexOf >= 0 ? indexOf + (startpos || 0) : indexOf;\n};\n\nconst MD_NEWLINE_INJECT = \"\\n\\n\";\nconst MD_NEWLINE_PRE_INJECT = \"\\n\\n\";\nconst MD_NEWLINE_INJECT_COMMENT = \"\";\n\nconst URL_REGEX =\n /(http|ftp|https|upload):\\/\\/([\\w_-]+(?:(?:\\.[\\w_-]+)+))([\\w.,@?^=%&:\\/~+#-]*[\\w@?^=%&\\/~+#-])/;\nconst MD_URL_REGEX =\n /\\!?\\[.*\\]\\((http|ftp|https|upload):\\/\\/([\\w_-]+(?:(?:\\.[\\w_-]+)+))([\\w.,@?^=%&:\\/~+#-]*[\\w@?^=%&\\/~+#-])\\)/;\nconst URL_REGEX_SINGLE_LINE = new RegExp(`^${URL_REGEX.source}|${MD_URL_REGEX.source}$`);\nconst ESCAPABLES_REGEX =\n /((\\n|^)(?```+|~~~+)(?.*\\n))|(?\\[(?i?code|plain)(=.*)?\\])|(?(?`{1,2})(.*)(?\\k))/im;\nconst MD_TABLE_REGEX = /^(\\|[^\\n]+\\|\\r?\\n)((?:\\| ?:?[-]+:? ?)+\\|)(\\n(?:\\|[^\\n]+\\|\\r?\\n?)*)?$/m;\n\n/**\n * Generates a random GUID.\n *\n * Mini Racer doesn't have the crypto module, so we can't use the built-in `crypto.randomUUID` function.\n * @returns {string} a GUID\n */\nfunction generateGUID() {\n let d = new Date().getTime();\n if (window.performance && typeof window.performance.now === \"function\") {\n d += performance.now(); //use high-precision timer if available\n }\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, function (c) {\n // eslint-disable-next-line no-bitwise\n const r = (d + Math.random() * 16) % 16 | 0;\n d = Math.floor(d / 16);\n // eslint-disable-next-line no-bitwise\n return (c === \"x\" ? r : (r & 0x3) | 0x8).toString(16);\n });\n}\n\nexport {\n toNode,\n toOriginalStartTag,\n generateGUID,\n preprocessAttr,\n regexIndexOf,\n MD_NEWLINE_INJECT,\n MD_NEWLINE_INJECT_COMMENT,\n MD_NEWLINE_PRE_INJECT,\n URL_REGEX,\n MD_URL_REGEX,\n MD_TABLE_REGEX,\n URL_REGEX_SINGLE_LINE,\n ESCAPABLES_REGEX,\n};\n","import { toNode } from \"../utils/common\";\n/**\n * @file Adds [left], [center], and [right] to bbcode\n * @example [center]content[/center]\n */\nexport const alignment = {\n left: (node) => toNode(\"div\", { class: \"bb-left\" }, node.content),\n center: (node) => toNode(\"div\", { class: \"bb-center\" }, node.content),\n right: (node) => toNode(\"div\", { class: \"bb-right\" }, node.content),\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n/**\n * @file Adds [id] and [goto] to bbcode\n * @example [a=your_anchor_name]An anchor[/a] [goto=your_anchor_name]Jump to an anchor[/goto]\n */\nexport const anchor = {\n // name is not valid in HTML5; however, it correctly displays back while id does not\n a: (node) => {\n const attrs = preprocessAttr(node.attrs)._default || \"\";\n return toNode(\"a\", { id: `user-anchor-${attrs.trim()}`, name: `user-anchor-${attrs.trim()}` }, node.content);\n },\n goto: (node) => {\n const attrs = preprocessAttr(node.attrs)._default || \"\";\n toNode(\"a\", { href: `#user-anchor-${attrs.trim()}` }, node.content);\n }\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\nconst WEB_FONTS = [\n \"arial\",\n \"book antiqua\",\n \"courier new\",\n \"georgia\",\n \"tahoma\",\n \"times new roman\",\n \"trebuchet ms\",\n \"verdana\",\n];\nconst VALID_FONT_STYLES = {\n thin: \"100\",\n extralight: \"200\",\n light: \"300\",\n regular: \"400\",\n medium: \"500\",\n semibold: \"600\",\n bold: \"700\",\n extrabold: \"800\",\n black: \"900\",\n};\n// registered axis tags https://learn.microsoft.com/en-us/typography/opentype/spec/dvaraxisreg#registered-axis-tags\nconst REGISTERED_AXIS = [\"ital\", \"opsz\", \"slnt\", \"wdth\", \"wght\"];\n\nconst AXES_REGEX = /(?[a-zA-Z]*)?\\s?(?[0-9]*)?\\s?(?italic)?/;\n\nconst axesParser = (attrs) => {\n let axes = {\n ital: 0,\n wght: 400,\n };\n\n if (attrs?.style) {\n // user just copy pasted the name of the style on the google font site, probably\n const style = attrs.style.trim().toLowerCase();\n const matches = AXES_REGEX.exec(style).groups || {};\n if (matches?.italic) {\n axes.ital = 1;\n }\n\n const weight = matches.weight;\n if (weight && weight >= 0 && weight <= 900) {\n axes.wght = weight;\n } else if (Object.keys(VALID_FONT_STYLES).includes(matches.named_weight || \"\")) {\n axes.wght = VALID_FONT_STYLES[matches.named_weight];\n }\n\n axes = {\n ...axes,\n ...Object.fromEntries(Object.entries(attrs).filter(([key]) => REGISTERED_AXIS.includes(key))),\n };\n }\n return axes;\n};\n\n/**\n * Create google font api url\n * @param {string} family name of font\n * @param {object} axes custom font axes\n */\nconst googleFontApiBuild = (family, axes) => {\n family = family.replaceAll(\" \", \"+\");\n // google fonts requires axes names to be in alphabetical order\n axes = Object.keys(axes)\n .sort()\n .reduce((obj, key) => {\n obj[key] = axes[key];\n return obj;\n }, {});\n const axesList = Object.keys(axes).join(\",\") + \"@\" + Object.values(axes).join(\",\");\n return \"https://fonts.googleapis.com/css2?family=\" + family + \":\" + axesList;\n};\n\nexport const font = (node, options) => {\n const attrs = preprocessAttr(node.attrs);\n const fontFamily = attrs?._default || attrs.family || attrs.name;\n if (fontFamily.trim() === \"\") {\n return node.content;\n }\n if (WEB_FONTS.includes(fontFamily.trim().toLowerCase())) {\n return toNode(\"span\", { style: \"font-family: \" + fontFamily }, node.content);\n }\n\n const axes = axesParser(attrs);\n const url = googleFontApiBuild(fontFamily, axes);\n options.data.fonts.add(url);\n\n const italic = axes.ital === 1 ? \"italic\" : \"normal\";\n\n const custom = Object.entries(axes).filter(([key]) => key !== \"wght\" && key !== \"ital\");\n let fontVar = \"\";\n if (custom.length) {\n fontVar =\n \"font-variation-settings: \" + custom.map(([key, val]) => `'${key}' ${val}`).join(\", \") + \";\";\n }\n\n return toNode(\n \"span\",\n {\n style: `font-family: ${fontFamily}; font-weight: ${axes.wght}; font-style: ${italic}; ${fontVar}`,\n \"data-font\": url,\n },\n node.content,\n );\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\n/**\n * Parse the user provided height and return a valid height value\n * @param {Number} heightValue obtains the input of the user entered height (default is 700)\n * @returns A validated number less than 0.\n */\nfunction parseHeight(heightValue) {\n const maxHeight = 700;\n const parsedHeight =\n heightValue && heightValue.trim() !== \"\" ? heightValue.replace(/[^\\d.]/g, \"\") : 0;\n\n if (parsedHeight && parsedHeight >= 0 && parsedHeight <= maxHeight) {\n return parsedHeight;\n } else {\n // if the value = 0 then nothing will be returned\n return parsedHeight === 0 ? 0 : maxHeight;\n }\n}\n\n/**\n * @file Adds [heightrestrict] to bbcode\n * @example [heightrestrict=50]content[/heightrestrict]\n */\nexport const heightrestrict = (node) => {\n const attrs = preprocessAttr(node.attrs)._default;\n const heightInput = parseHeight(attrs).toString();\n // Return image's default size if heightrestrict did not involve a valid value\n return heightInput === \"0\"\n ? toNode(\"div\", { class: \"bb-height-restrict\" }, node.content)\n : toNode(\n \"div\",\n { class: \"bb-height-restrict\", style: `height: ${heightInput}px;` },\n node.content,\n );\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n/**\n * @file Adds [mail] to bbcode\n * @param {string} [type=\"send\"] Denotes type of mail either send or receive\n * @param {string} [person=\"Unknown\"] Denotes the person in the To/From field\n * @param {string} [subject=\"Empty\"] Denotes the subject line of the email\n * @example [mail type=\"send\" person=\"John Doe\" subject=\"Hello World\"]content[/mail]\n */\n\nconst parseEmailContent = (content) => {\n return toNode(\"div\", { class: \"bb-email-content\" }, content);\n};\n\nconst parseEmailSubject = (subject) => {\n return toNode(\"div\", { class: \"bb-email-subject\" }, subject);\n};\n\nconst parseEmailPerson = (person) => {\n return toNode(\"div\", { class: \"bb-email-address\" }, person);\n};\n\nconst emailHeader = toNode(\"div\", { class: \"bb-email-header\" }, \"\");\nconst emailFooter = toNode(\n \"div\",\n { class: \"bb-email-footer\" },\n toNode(\"div\", { class: \"bb-email-button\" }, \"\")\n);\n\nexport const mail = (node) => {\n const attributes = preprocessAttr(node.attrs);\n let mailAttr = {\n mailOption: (attributes.type || \"send\").toLowerCase(),\n person: attributes.person || \"Unknown\",\n subject: attributes.subject || \"Empty\",\n };\n\n return toNode(\n \"div\",\n {\n class: \"bb-email\",\n \"data-bb-email\": mailAttr.mailOption,\n },\n [\n emailHeader,\n parseEmailPerson(mailAttr.person),\n parseEmailSubject(mailAttr.subject),\n parseEmailContent(node.content),\n emailFooter,\n ]\n );\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\n/**\n * Parses an inputted size value and returns the formatted valid font size\n * @param {string} fontValue the input of the size\n */\nfunction parseFontSize(fontValue) {\n let value;\n let fontSize = { valid: true };\n const parsedSize = /(\\d+\\.?\\d?)(px|rem)?/i.exec(fontValue);\n const sizeRanges = {\n px_max: 36,\n px_min: 8,\n rem_max: 3,\n rem_min: 0.2,\n unitless_max: 7,\n unitless_min: 1,\n };\n\n if (parsedSize && (value = parsedSize[1])) {\n fontSize.unit = (parsedSize[2] || \"\").toLowerCase();\n switch (fontSize.unit) {\n case \"px\":\n if (value > sizeRanges.px_max) {\n value = sizeRanges.px_max;\n } else if (value < sizeRanges.px_min) {\n value = sizeRanges.px_min;\n }\n break;\n case \"rem\":\n if (value > sizeRanges.rem_max) {\n value = sizeRanges.rem_max;\n } else if (value < sizeRanges.rem_min) {\n value = sizeRanges.rem_min;\n }\n break;\n default:\n if ((fontSize.valid = fontValue.length === value.length)) {\n if (value > sizeRanges.unitless_max) {\n value = sizeRanges.unitless_max;\n } else if (value < sizeRanges.unitless_min) {\n value = sizeRanges.unitless_min;\n }\n }\n break;\n }\n\n fontSize.value = value;\n }\n return fontSize;\n}\n\nexport const size = (node) => {\n const input = preprocessAttr(node.attrs)._default;\n const fontSize = parseFontSize(input);\n if (!fontSize.valid) {\n return node.content;\n }\n let outputAttr = {};\n if (fontSize.unit) {\n outputAttr = { style: `font-size: ${fontSize.value}${fontSize.unit}` };\n } else {\n outputAttr = { \"data-size\": fontSize.value };\n }\n return toNode(\"span\", outputAttr, node.content);\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\n/**\n * @file Adds textmessage to bbcode\n * @exmaple [textmessage=Recipient][message=them]Hi [/message][message=me] Hey![/message][/textmessage]\n */\n\nconst ACCEPTED_OPTIONS = [\"me\", \"them\", \"right\", \"left\"];\nexport const textmessage = {\n textmessage: (node) => {\n const attr = preprocessAttr(node.attrs)._default || \"Recipient\";\n const recipient = attr && attr.trim() !== \"\" ? attr : \"Recipient\";\n return toNode(\"div\", { class: \"bb-textmessage\" }, [\n toNode(\"div\", { class: \"bb-textmessage-name\" }, recipient),\n toNode(\"div\", { class: \"bb-textmessage-overflow\" }, [\n toNode(\"div\", { class: \"bb-textmessage-content\" }, node.content),\n ]),\n ]);\n },\n message: (node) => {\n let option = preprocessAttr(node.attrs)._default.toLowerCase();\n if (!ACCEPTED_OPTIONS.includes(option) || option === \"right\") {\n option = \"me\";\n }\n if (option === \"left\") {\n option = \"them\";\n }\n\n const senderAttrs = option === \"me\" ? \"bb-message-me\" : \"bb-message-them\";\n return toNode(\"div\", { class: senderAttrs }, [\n toNode(\"div\", { class: \"bb-message-content\" }, node.content),\n ]);\n },\n};\n","const N = '\\n';\nconst TAB = '\\t';\nconst F = '\\f';\nconst R = '\\r';\nconst EQ = '=';\nconst QUOTEMARK = '\"';\nconst SPACE = ' ';\nconst OPEN_BRAKET = '[';\nconst CLOSE_BRAKET = ']';\nconst SLASH = '/';\nconst BACKSLASH = '\\\\';\nexport { N, F, R, EQ, TAB, SPACE, SLASH, BACKSLASH, QUOTEMARK, OPEN_BRAKET, CLOSE_BRAKET };\n","import { N } from './char';\nconst isTagNode = (el)=>typeof el === 'object' && !!el.tag;\nconst isStringNode = (el)=>typeof el === 'string';\nconst isEOL = (el)=>el === N;\nconst keysReduce = (obj, reduce, def)=>Object.keys(obj).reduce(reduce, def);\nconst getNodeLength = (node)=>{\n if (isTagNode(node)) {\n return node.content.reduce((count, contentNode)=>count + getNodeLength(contentNode), 0);\n }\n if (isStringNode(node)) {\n return node.length;\n }\n return 0;\n};\n/**\n * Appends value to Tag Node\n * @param {TagNode} node\n * @param value\n */ const appendToNode = (node, value)=>{\n node.content.push(value);\n};\n/**\n * Replaces \" to &qquot;\n * @param {String} value\n */ const escapeHTML = (value)=>value.replace(/&/g, '&').replace(//g, '>').replace(/\"/g, '"').replace(/'/g, ''')// eslint-disable-next-line no-script-url\n .replace(/(javascript|data|vbscript):/gi, '$1%3A');\n/**\n * Acept name and value and return valid html5 attribute string\n * @param {String} name\n * @param {String} value\n * @return {string}\n */ const attrValue = (name, value)=>{\n const type = typeof value;\n const types = {\n boolean: ()=>value ? `${name}` : '',\n number: ()=>`${name}=\"${value}\"`,\n string: ()=>`${name}=\"${escapeHTML(value)}\"`,\n object: ()=>`${name}=\"${escapeHTML(JSON.stringify(value))}\"`\n };\n return types[type] ? types[type]() : '';\n};\n/**\n * Transforms attrs to html params string\n * @param values\n */ const attrsToString = (values)=>{\n // To avoid some malformed attributes\n if (values == null) {\n return '';\n }\n return keysReduce(values, (arr, key)=>[\n ...arr,\n attrValue(key, values[key])\n ], [\n ''\n ]).join(' ');\n};\n/**\n * Gets value from\n * @example\n * getUniqAttr({ 'foo': true, 'bar': bar' }) => 'bar'\n * @param attrs\n * @returns {string}\n */ const getUniqAttr = (attrs)=>keysReduce(attrs, (res, key)=>attrs[key] === key ? attrs[key] : null, null);\nexport { attrsToString, attrValue, appendToNode, escapeHTML, getNodeLength, getUniqAttr, isTagNode, isStringNode, isEOL };\n","import { OPEN_BRAKET, CLOSE_BRAKET, SLASH } from './char';\nimport { getNodeLength, appendToNode, attrsToString, attrValue, getUniqAttr } from './helpers';\nconst getTagAttrs = (tag, params)=>{\n const uniqAattr = getUniqAttr(params);\n if (uniqAattr) {\n const tagAttr = attrValue(tag, uniqAattr);\n const attrs = {\n ...params\n };\n delete attrs[uniqAattr];\n const attrsStr = attrsToString(attrs);\n return `${tagAttr}${attrsStr}`;\n }\n return `${tag}${attrsToString(params)}`;\n};\nclass TagNode {\n attr(name, value) {\n if (typeof value !== 'undefined') {\n this.attrs[name] = value;\n }\n return this.attrs[name];\n }\n append(value) {\n return appendToNode(this, value);\n }\n get length() {\n return getNodeLength(this);\n }\n toTagStart({ openTag =OPEN_BRAKET , closeTag =CLOSE_BRAKET } = {}) {\n const tagAttrs = getTagAttrs(this.tag, this.attrs);\n return `${openTag}${tagAttrs}${closeTag}`;\n }\n toTagEnd({ openTag =OPEN_BRAKET , closeTag =CLOSE_BRAKET } = {}) {\n return `${openTag}${SLASH}${this.tag}${closeTag}`;\n }\n toTagNode() {\n return new TagNode(this.tag.toLowerCase(), this.attrs, this.content);\n }\n toString({ openTag =OPEN_BRAKET , closeTag =CLOSE_BRAKET } = {}) {\n const isEmpty = this.content.length === 0;\n const content = this.content.reduce((r, node)=>r + node.toString({\n openTag,\n closeTag\n }), '');\n const tagStart = this.toTagStart({\n openTag,\n closeTag\n });\n if (isEmpty) {\n return tagStart;\n }\n return `${tagStart}${content}${this.toTagEnd({\n openTag,\n closeTag\n })}`;\n }\n constructor(tag, attrs, content){\n this.tag = tag;\n this.attrs = attrs;\n this.content = Array.isArray(content) ? content : [\n content\n ];\n }\n}\nTagNode.create = (tag, attrs = {}, content = [])=>new TagNode(tag, attrs, content);\nTagNode.isOf = (node, type)=>node.tag === type;\nexport { TagNode };\nexport default TagNode;\n","import { generateGUID, preprocessAttr, toNode, toOriginalStartTag } from \"../utils/common\";\nimport { isTagNode } from \"@bbob/plugin-helper\";\n\n/**\n * @file Adds [tabs][tab] to bbcode\n * @example [tabs][tab=name 1]content[/tab][tab=name 2]content[/tab][/tabs]\n */\nexport const tabs = (node) => {\n const tabsList = node.content.filter(\n (contentNode) => isTagNode(contentNode) && contentNode.tag === \"tab\",\n );\n const groupId = generateGUID();\n tabsList.forEach((tabNode) => {\n tabNode.isValid = true;\n tabNode.groupId = groupId;\n });\n if (!tabsList.length) {\n // no [tab] tags found\n return [toOriginalStartTag(node), ...node.content, node.toTagEnd()];\n }\n tabsList[0].open = true;\n\n return toNode(\n \"div\",\n {\n class: \"bb-tabs\",\n },\n tabsList,\n );\n};\n\n/**\n * [tab=name]content[/tab]\n * [tab name=\"name\" style=\"style\"]content[/tab]\n */\nexport const tab = (node) => {\n if (!node.isValid) {\n // not inside a [tabs] tag\n return [toOriginalStartTag(node), ...node.content, node.toTagEnd()];\n }\n const attrs = preprocessAttr(node.attrs);\n const name = attrs._default || attrs.name || \"Tab\";\n const tabId = `tab-${name.replace(/\\W/g, \"_\")}-${generateGUID()}`;\n return [\n toNode(\"input\", {\n type: \"radio\",\n id: tabId,\n name: \"tab-group-\" + node.groupId,\n class: \"bb-tab\",\n checked: node.open,\n }),\n toNode(\n \"label\",\n {\n class: \"bb-tab-label\",\n for: tabId,\n style: attrs.style,\n },\n name,\n ),\n toNode(\n \"div\",\n {\n class: \"bb-tab-content\",\n },\n node.content,\n ),\n ];\n};\n","import {\n generateGUID,\n preprocessAttr,\n regexIndexOf,\n toNode,\n toOriginalStartTag,\n} from \"../utils/common\";\nimport { TagNode, isStringNode, isTagNode } from \"@bbob/plugin-helper\";\n\nconst SLIDE_TITLE_OPEN = Symbol(\"slide-title-open\");\nconst SLIDE_TITLE_CLOSE = Symbol(\"slide-title-close\");\nconst SLIDE_CLOSE = Symbol(\"slide-close\");\nconst SLIDE_REGEX =\n /(?\\{slide=)|(?\\})|(?\\{\\/slide\\})/i;\n\n/**\n * Adds the accordion tag\n * [accordion]{slide=name}content{/slide}[/accordion]\n *\n * [accordion][slide=name]content[/slide][/accordion]\n */\nconst accordion = (node) => {\n const groupId = generateGUID();\n\n // add support for existing {slide} tags style, due to copious amounts of existing content\n // also the only way to get true custom content inside a slide due to nesting limitations\n const markedContent = generateSlideMarkersFromContent(node.content);\n const generatedSlides = generateSlidesFromMarkers(markedContent);\n\n const filteredContent = generatedSlides\n .filter((n) => isTagNode(n) && n.tag === \"slide\")\n .map((content) => {\n content.isValid = true;\n content.groupId = groupId;\n return content;\n });\n if (!filteredContent.length) {\n // no [slide] tags found\n return [toOriginalStartTag(node), ...node.content, node.toTagEnd()];\n }\n\n const attrs = preprocessAttr(node.attrs);\n\n if (attrs._default) {\n /** @type {string[]} */\n const customSettings = attrs._default.split(\"|\").map((s) => s.trim());\n if (customSettings.includes(\"bright\")) {\n attrs.bright = true;\n }\n if (customSettings.includes(\"bcenter\")) {\n attrs.bcenter = true;\n }\n if (customSettings.includes(\"bleft\")) {\n attrs.bleft = true;\n }\n if (customSettings.includes(\"fleft\")) {\n attrs.fleft = true;\n }\n if (customSettings.includes(\"fright\")) {\n attrs.fright = true;\n }\n if (\n customSettings.some((s) => s.endsWith(\"px\")) ||\n customSettings.some((s) => s.endsWith(\"%\"))\n ) {\n attrs.width = customSettings.find((s) => s.endsWith(\"px\") || s.endsWith(\"%\"));\n }\n }\n\n let classes = Object.keys(attrs)\n .filter((s) => [\"bright\", \"bcenter\", \"bleft\", \"fleft\", \"fright\"].includes(s))\n .join(\" \");\n let style = \"\";\n if (attrs.width?.endsWith(\"px\") || attrs.width?.endsWith(\"%\")) {\n style = `width: ${attrs.width};`;\n }\n return toNode(\n \"div\",\n { class: \"bb-accordion \" + classes, \"data-group-id\": groupId, style },\n filteredContent,\n );\n};\n\n/**\n * Locates and splits all {slide} tag components into their respective parts while preserving remaining content\n * @param {(TagNode|string)[]} contentArr node content of the accordion tag\n *\n * @example\n * ```\n * [\"{slide=test}\", \"lorem ipsum\", \"{/slide}\"]\n * ```\n * becomes\n * ```\n * [SLIDE_TITLE_OPEN, \"test\", SLIDE_TITLE_CLOSE, \"lorem ipsum\", SLIDE_CLOSE]\n * ```\n */\nfunction generateSlideMarkersFromContent(contentArr) {\n contentArr = [...contentArr]; // shallow clone. object nodes are not modified anyway\n\n const newArr = [];\n while (contentArr.length > 0) {\n const content = contentArr[0];\n if (isTagNode(content)) {\n newArr.push(contentArr.shift());\n continue;\n }\n const foundIndex = regexIndexOf(content, SLIDE_REGEX);\n if (foundIndex === -1) {\n newArr.push(contentArr.shift());\n continue;\n }\n const match = content.match(SLIDE_REGEX);\n const preContent = content.slice(0, foundIndex);\n const postContent = content.slice(foundIndex + match[0].length);\n if (preContent.length) {\n newArr.push(preContent);\n }\n if (match.groups.slideTitleOpen) {\n newArr.push(SLIDE_TITLE_OPEN);\n }\n if (match.groups.slideTitleClose) {\n newArr.push(SLIDE_TITLE_CLOSE);\n }\n if (match.groups.slideClose) {\n newArr.push(SLIDE_CLOSE);\n }\n if (postContent.length) {\n contentArr[0] = postContent;\n } else {\n contentArr.shift();\n }\n }\n\n return newArr;\n}\n\n/**\n * Generates slide nodes from markers\n * @param {(string | typeof SLIDE_TITLE_OPEN | typeof SLIDE_TITLE_CLOSE | typeof SLIDE_CLOSE | TagNode)[]} markedContent\n */\nfunction generateSlidesFromMarkers(markedContent) {\n const nodes = [];\n let currentSlide = null;\n /** @type {typeof SLIDE_TITLE_OPEN | typeof SLIDE_TITLE_CLOSE | null} */\n let prevMarker = null;\n for (const content of markedContent) {\n if (content === SLIDE_TITLE_OPEN && prevMarker === null) {\n currentSlide = TagNode.create(\"slide\");\n currentSlide.content = [];\n currentSlide.customTitle = [];\n prevMarker = SLIDE_TITLE_OPEN;\n } else if (content === SLIDE_TITLE_CLOSE && prevMarker === SLIDE_TITLE_OPEN) {\n prevMarker = SLIDE_TITLE_CLOSE;\n continue;\n } else if (content === SLIDE_CLOSE && currentSlide && prevMarker === SLIDE_TITLE_CLOSE) {\n nodes.push(currentSlide);\n currentSlide = null;\n prevMarker = null;\n } else if (currentSlide) {\n if (prevMarker === SLIDE_TITLE_OPEN) {\n currentSlide.customTitle.push(markerToString(content));\n } else {\n currentSlide.content.push(markerToString(content));\n }\n } else {\n // no slide open, just add content\n nodes.push(markerToString(content));\n }\n }\n return nodes;\n}\n\n/**\n * Processes content into a string. Catches stray markers and converts them back into a string\n * @param {string | typeof SLIDE_TITLE_OPEN | typeof SLIDE_TITLE_CLOSE | typeof SLIDE_CLOSE} marker\n * @returns expected string\n */\nfunction markerToString(marker) {\n switch (marker) {\n case SLIDE_TITLE_OPEN:\n return \"{slide=\";\n case SLIDE_TITLE_CLOSE:\n return \"}\";\n case SLIDE_CLOSE:\n return \"{/slide}\";\n default:\n return marker;\n }\n}\n\nconst slide = (node) => {\n if (!node.isValid) {\n // not inside an [accordion] tag\n return [toOriginalStartTag(node), ...node.content, node.toTagEnd()];\n }\n const attrs = preprocessAttr(node.attrs);\n let title = [attrs.title || attrs._default || \"Slide\"];\n let isOpen = !!attrs.open || false;\n let titleAlign = attrs.left ? \"left\" : attrs.right ? \"right\" : attrs.center ? \"center\" : \"left\";\n if (node.customTitle?.length) {\n // slide was created from markers\n title = node.customTitle;\n // pull out old options from title if they exist\n const possibleOptions = title\n .filter((t) => typeof t === \"string\")\n .join(\"\")\n .toLowerCase()\n .split(\"|\")\n .map((s) => s.trim());\n if (possibleOptions.includes(\"open\")) {\n isOpen = true;\n }\n if (possibleOptions.includes(\"right\")) {\n titleAlign = \"right\";\n }\n if (possibleOptions.includes(\"center\")) {\n titleAlign = \"center\";\n }\n if (possibleOptions.includes(\"left\")) {\n titleAlign = \"left\";\n }\n title = title.map((t) => {\n if (isStringNode(t)) {\n t = t.replace(/\\|(open|right|center|left)/gi, \"\");\n }\n return t;\n });\n }\n return [\n toNode(\"details\", { class: \"bb-slide\", open: isOpen }, [\n toNode(\n \"summary\",\n { class: \"bb-slide-title\", style: `text-align: ${titleAlign}; ${attrs.style || \"\"}` },\n title,\n ),\n toNode(\"div\", { class: \"bb-slide-content\" }, node.content),\n ]),\n ];\n};\n\nexport const accordionTags = { accordion, slide };\n","import { preprocessAttr } from \"../utils/common\";\n\nconst EVENTS = [\n \"init\",\n \"click\",\n \"change\",\n \"input\",\n \"dblclick\",\n \"mouseenter\",\n \"mouseleave\",\n \"scroll\",\n];\n\n/**\n * Script tag\n *\n * [script]content[/script]\n *\n * [script class=\"id\" on=\"event\" version=\"2\"]content[/script]\n */\nexport const script = (node, options) => {\n const attrs = preprocessAttr(node.attrs);\n\n if (!options.data.previewing && !options.data.commonGUID) {\n // create a common GUID for the post\n // only applicable for div, style, and script tags\n // this is to prevent the same class name from being used in different posts\n options.data.commonGUID = \"post-\" + Math.random().toString(36).substring(2, 7);\n }\n const classSuffix = options.data.previewing ? \"preview\" : options.data.commonGUID;\n\n const onEvent =\n (EVENTS.includes(attrs.on?.toLowerCase() || \"init\") && attrs.on?.toLowerCase()) || \"init\";\n\n const scriptSetup = {\n id: classSuffix,\n class: attrs.class || \"\",\n on: onEvent,\n version: attrs.version || \"\",\n content: node.content.join(\"\"),\n };\n options.data.bbscripts.push(scriptSetup);\n\n return [];\n};\n","import { createPreset } from \"@bbob/preset\";\nimport { alignment } from \"./tags/alignment\";\nimport { anchor } from \"./tags/anchor\";\nimport { bg } from \"./tags/background\";\nimport { block } from \"./tags/block\";\nimport { blockquote } from \"./tags/blockquote\";\nimport { border } from \"./tags/border\";\nimport { centerblock } from \"./tags/centerblock\";\nimport { check } from \"./tags/check\";\nimport { code, icode, savenl } from \"./tags/code\";\nimport { color } from \"./tags/color\";\nimport { comment } from \"./tags/comment\";\nimport { divide } from \"./tags/divide\";\nimport { fieldset } from \"./tags/fieldset\";\nimport { font } from \"./tags/font\";\nimport { h, h1, h2, h3, h4, h5, h6, sh } from \"./tags/header\";\nimport { heightrestrict } from \"./tags/heightrestrict\";\nimport { highlight } from \"./tags/highlight\";\nimport { imagefloat } from \"./tags/imagefloat\";\nimport { justify } from \"./tags/justify\";\nimport { mail } from \"./tags/mail\";\nimport { newspaper } from \"./tags/newspaper\";\nimport { br, nobr } from \"./tags/lineBreak\";\nimport { note } from \"./tags/note\";\nimport { ooc } from \"./tags/ooc\";\nimport { pindent } from \"./tags/pindent\";\nimport { plain } from \"./tags/plain\";\nimport { print } from \"./tags/print\";\nimport { progress } from \"./tags/progress\";\nimport { thinprogress } from \"./tags/thinprogress\";\nimport { scroll } from \"./tags/scroll\";\nimport { side } from \"./tags/side\";\nimport { size } from \"./tags/size\";\nimport { sub } from \"./tags/subscript\";\nimport { sup } from \"./tags/superscript\";\nimport { inlinespoiler, spoiler } from \"./tags/spoiler\";\nimport { textmessage } from \"./tags/textmessage\";\nimport { tab, tabs } from \"./tags/tabs\";\nimport { accordionTags } from \"./tags/accordion\";\nimport { div } from \"./tags/div\";\nimport { classStyle } from \"./tags/class\";\nimport { script } from \"./tags/script\";\nimport { animation, keyframe } from \"./tags/animation\";\nimport { bold, italic, strike, underline } from \"./tags/discourse-core-replacement\";\n\nconst tags = {\n ...accordionTags,\n ...alignment,\n ...anchor,\n animation,\n bg,\n block,\n blockquote,\n border,\n br,\n centerblock,\n check,\n class: classStyle,\n code,\n color,\n comment,\n div,\n divide,\n fieldset,\n font,\n h,\n h1,\n h2,\n h3,\n h4,\n h5,\n h6,\n heightrestrict,\n highlight,\n icode,\n imagefloat,\n inlinespoiler,\n justify,\n keyframe,\n mail,\n newspaper,\n nobr,\n note,\n ooc,\n pindent,\n plain,\n print,\n progress,\n thinprogress,\n savenl,\n sh,\n script,\n scroll,\n side,\n size,\n spoiler,\n sub,\n sup,\n tab,\n tabs,\n ...textmessage,\n\n // discourse core replacement tags\n b: bold,\n i: italic,\n u: underline,\n s: strike,\n};\n\nconst availableTags = Object.keys(tags);\nconst preventParsing = [\"plain\", \"code\", \"icode\", \"class\"];\n\nconst preset = createPreset(tags);\n\nexport { availableTags, tags, preset, preventParsing };\nexport default preset;\n","import { preprocessAttr, toOriginalStartTag } from \"../utils/common\";\nimport { isStringNode, isTagNode } from \"@bbob/plugin-helper\";\n\n/**\n * Renders css Keyframes\n *\n * [animation=name][keyframe=0]color: red[/keyframe][/animation]\n */\nexport const animation = (node, options) => {\n if (!options.data.previewing && !options.data.commonGUID) {\n // create a common GUID for the post\n // only applicable for div, style, and script tags\n // this is to prevent the same class name from being used in different posts\n options.data.commonGUID = \"post-\" + Math.random().toString(36).substring(2, 7);\n }\n const commonId = options.data.previewing ? \"preview\" : options.data.commonGUID;\n\n const name = preprocessAttr(node.attrs)?._default || \"\";\n const keyframes = node.content\n .filter((n) => isTagNode(n) && n.tag === \"keyframe\")\n .map((content) => {\n content.isValid = true;\n /** @type {string} */\n const ident = preprocessAttr(content.attrs)._default || \"\";\n content.ident = ident + (ident.match(/^\\d+$/) ? \"%\" : \"\");\n const cleanContent = content.content\n .filter(isStringNode)\n .join(\"\")\n .replaceAll(/[\\[\\]\\{\\}]/g, \"\");\n content.formatted = `${content.ident}{ ${cleanContent} }`;\n return content;\n });\n const keyframeContent = keyframes.map((n) => n.formatted).join(\"\\n\");\n const content = `@keyframes ${commonId}${name} { ${keyframeContent} }`;\n options.data.styles.push(content);\n return [];\n};\n\nexport const keyframe = (node) => {\n if (!node.isValid) {\n return [toOriginalStartTag(node), ...node.content, node.toTagEnd()];\n }\n return [];\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\n/**\n * Add [bg] tag\n * @example [bg=red]Hello[/bg]\n */\nexport const bg = (node) => {\n const color = preprocessAttr(node.attrs)._default;\n return toNode(\n \"div\",\n {\n style: `background-color: ${color};`,\n class: \"bb-background\",\n },\n node.content,\n );\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\n/**\n * Add [block] tag\n * @example [block=treasure]content[/block]\n */\nexport const block = (node) => {\n const defaultOp = \"block\";\n const blockAttr = (preprocessAttr(node.attrs)._default || defaultOp).toLowerCase();\n\n const OPTIONS = [\n \"block\",\n \"dice\",\n \"dice10\",\n \"setting\",\n \"warning\",\n \"storyteller\",\n \"announcement\",\n \"important\",\n \"question\",\n \"encounter\",\n \"information\",\n \"character\",\n \"treasure\",\n ];\n\n // Default to block option if user did not provide anything valid\n const blockOption = OPTIONS.includes(blockAttr) ? blockAttr : defaultOp;\n\n return toNode(\"table\", { class: \"bb-block\", \"data-bb-block\": blockOption }, [\n toNode(\"tbody\", [\n toNode(\"tr\", [\n toNode(\"td\", { class: \"bb-block-icon\" }),\n toNode(\"td\", { class: \"bb-block-content\" }, node.content),\n ]),\n ]),\n ]);\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\n/**\n * @file Adds [blockquote] to bbcode\n * @example [blockquote=author]content[/blockquote]\n */\nexport const blockquote = (node) => {\n const author = preprocessAttr(node.attrs)._default || \"\";\n\n return toNode(\"div\", { class: \"bb-blockquote\" }, [\n toNode(\"div\", { class: \"bb-blockquote-left\" }),\n toNode(\"div\", { class: \"bb-blockquote-content\" }, [\n node.content,\n toNode(\"div\", { class: \"bb-blockquote-speaker\" }, author !== \"\" ? `- ${author}` : \"\"),\n ]),\n toNode(\"div\", { class: \"bb-blockquote-right\" }),\n ]);\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\nexport const border = (node) => {\n const val = preprocessAttr(node.attrs)._default;\n return toNode(\n \"div\",\n {\n style: `border: ${val};`,\n class: \"bb-border\",\n },\n node.content\n );\n};\n","import { toNode } from \"../utils/common\";\n\n/**\n * Creates a line break html
tag\n */\nexport const br = () => {\n return toNode(\"br\", {}, null);\n};\n\n/**\n * Disables line breaks for given content\n * @example\n * ```\n * [nobr]test\n * test\n * test\n * [/nobr]\n *\n * test test test\n * ```\n */\nexport const nobr = (node) => {\n return { disableLineBreakConversion: true, content: node.content };\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\nexport const centerblock = (node) => {\n const percentageInput = preprocessAttr(node.attrs)._default || \"50\";\n return toNode(\"div\", { style: `margin: 0 auto; width: ${percentageInput}%` }, node.content);\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\nexport const check = (node) => {\n const attrs = preprocessAttr(node.attrs)._default || \"dot\";\n return toNode(\"div\", { class: `bb-check`, \"data-type\": attrs }, node.content);\n};\n","import { isStringNode } from \"@bbob/plugin-helper\";\nimport { preprocessAttr } from \"../utils/common\";\n\n/**\n * Class style tag\n *\n * [class=name]content[/class]\n * [class name=\"className\" state=\"psuedo-class\" minWidth=\"\" maxWidth=\"\"]content[/class]\n * [class name=\"className\" selector=\"\"]content[/class]\n */\nexport const classStyle = (node, options) => {\n const attrs = preprocessAttr(node.attrs);\n const nameAttr = attrs.name || attrs._default;\n\n if (!options.data.previewing && !options.data.commonGUID) {\n // create a common GUID for the post\n // only applicable for div, style, and script tags\n // this is to prevent the same class name from being used in different posts\n options.data.commonGUID = \"post-\" + Math.random().toString(36).substring(2, 7);\n }\n const classSuffix = options.data.previewing ? \"preview\" : options.data.commonGUID;\n const className = nameAttr + \"__\" + classSuffix;\n const content = node.content\n .filter(isStringNode)\n .map((s) => s.replaceAll(\"{post_id}\", classSuffix).replaceAll(/[\\[\\]\\{\\}]/g, \"\"));\n let selector = \"\";\n const mediaQuery = [];\n if (\n [\"hover\", \"focus\", \"active\", \"focus-within\", \"focus-visible\"].includes(\n attrs.state?.toLowerCase(),\n )\n ) {\n selector = \":\" + attrs.state.toLowerCase();\n }\n if (attrs.selector) {\n selector = attrs.selector.replace(/[,{}\\\\\\n]/g, \"\");\n }\n if (attrs.minWidth?.match(/^[0-9]+[a-z]+$/)) {\n // @media (min-width: )\n mediaQuery.push(`(min-width: ${attrs.minWidth})`);\n }\n if (attrs.maxWidth?.match(/^[0-9]+[a-z]+$/)) {\n // @media (max-width: )\n mediaQuery.push(`(max-width: ${attrs.maxWidth})`);\n }\n\n content.unshift(`.${className}${selector} {`);\n content.push(\"}\");\n if (mediaQuery.length) {\n content.unshift(`@media ${mediaQuery.join(\" and \")} {`);\n content.push(\"}\");\n }\n options.data.styles.push(content.join(\"\"));\n\n return [];\n};\n","import { preprocessAttr } from \"../utils/common\";\n\n/**\n * processes [code] tag and returns a fenced code block\n */\nexport const code = (node) => {\n const lang = preprocessAttr(node.attrs)._default || \"bbcode\";\n return {\n isWhitespaceSensitive: true,\n content: [\"```\" + lang + \"\\n\", node.content, \"\\n```\\n\"],\n };\n};\n\n/**\n * processes [icode] tag and returns inline code\n */\nexport const icode = (node) => {\n return {\n isWhitespaceSensitive: true,\n content: [\"`\", node.content, \"`\"],\n };\n};\n\n/**\n * Special tag to save newlines in code blocks. Used for hoisting code blocks\n */\nexport const savenl = (node) => {\n return {\n isWhitespaceSensitive: true,\n content: node.content,\n };\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\nexport const color = (node) => {\n const inputColor = preprocessAttr(node.attrs)._default || \"\";\n if (inputColor.trim() === \"\") {\n return node.content;\n }\n return toNode(\"span\", { style: `color: ${inputColor}` }, node.content);\n};\n","import { toNode } from \"../utils/common\";\n\n/**\n * @file Adds [comment] tag\n * @example [comment]Content[/comment]\n */\n\nconst comment = (node) => {\n return toNode(\"span\", { class: \"hidden\" }, node.content);\n};\n\nexport { comment };\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\n/**\n * Adds [div] tag\n * [div=css]Content[/div]\n * [div class=\"class\" style=\"css\"]Content[/div]\n */\nexport const div = (node, options) => {\n if (node.gen) {\n // node is actually a generated node \"div\" made by another tag\n // don't process it\n return node;\n }\n const attrs = preprocessAttr(node.attrs);\n const style = attrs.style || attrs._default;\n const classAttrs = attrs.class;\n if (!classAttrs?.trim()) {\n return toNode(\n \"div\",\n {\n style,\n },\n node.content,\n );\n }\n\n if (!options.data.previewing && !options.data.commonGUID) {\n // create a common GUID for the post\n // only applicable for div, style, and script tags\n // this is to prevent the same class name from being used in different posts\n options.data.commonGUID = \"post-\" + Math.random().toString(36).substring(2, 7);\n }\n const classSuffix = options.data.previewing ? \"preview\" : options.data.commonGUID;\n const classNames = classAttrs\n .split(\" \")\n .map((c) => c + \"__\" + classSuffix)\n .join(\" \");\n\n return toNode(\n \"div\",\n {\n class: classNames,\n style,\n },\n node.content,\n );\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\nexport const divide = (node) => {\n const type = (preprocessAttr(node.attrs)._default || \"\").toLowerCase();\n return toNode(\n \"span\",\n {\n class: \"bb-divide\",\n \"data-type\": type,\n },\n node.content\n );\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\n/**\n * @file Adds [fieldset] to bbcode\n * @example [fieldset=title]content[/fieldset]\n */\nexport const fieldset = (node) => {\n const title = preprocessAttr(node.attrs)._default || \"\";\n return toNode(\"fieldset\", { class: \"bb-fieldset\" }, [\n toNode(\"legend\", { class: \"bb-fieldset-legend\" }, title),\n toNode(\"div\", { class: \"bb-fieldset\" }, node.content),\n ]);\n};\n","import { toNode } from \"../utils/common\";\n\n/**\n * @file Adds Header to bbcode\n * @example [h]content[/h], [h2]content[/h2], [h3]content[/h3],\n * [h4]content[/h4], [h5]content[/h5], [h6]content[/h6].\n */\n\nconst h = (node) => {\n return toNode(\"h1\", {}, node.content);\n};\n\nconst h1 = (node) => {\n return toNode(\"h1\", {}, node.content);\n};\n\nconst h2 = (node) => {\n return toNode(\"h2\", {}, node.content);\n};\n\nconst sh = (node) => {\n return toNode(\"h2\", {}, node.content);\n};\n\nconst h3 = (node) => {\n return toNode(\"h3\", {}, node.content);\n};\n\nconst h4 = (node) => {\n return toNode(\"h4\", {}, node.content);\n};\n\nconst h5 = (node) => {\n return toNode(\"h5\", {}, node.content);\n};\n\nconst h6 = (node) => {\n return toNode(\"h6\", {}, node.content);\n};\n\nexport { h, sh, h1, h2, h3, h4, h5, h6 };\n","import { toNode } from \"../utils/common\";\n/**\n * @file Adds [highlight] to bbcode\n * @example [highlight]content[/highlight]\n */\nexport const highlight = (node) => {\n return toNode(\"span\", { class: \"bb-highlight\" }, node.content);\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n/**\n * @file Adds [imagefloat] to bbcode\n * @exmaple [imagefloat=left]content[/imagefloat]\n */\nexport const imagefloat = (node) => {\n const attrs = preprocessAttr(node.attrs)._default || \"\";\n return toNode(\"div\", { class: `bb-float-${attrs}` }, node.content);\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n/**\n * @file Adds [spoiler] and [inlinespoiler] to bbcode\n *\n * Defaults to \"Spoiler\" name if no title provided\n *\n * @example `[spoiler=Title]text[/spoiler]`\n * @example `[inlinespoiler]hidden content[/inlinespoiler]\n */\n\nexport const spoiler = (node) => {\n const providedTitle = preprocessAttr(node.attrs)._default;\n const title = \"Spoiler\" + (providedTitle ? `: ${providedTitle}` : \"\");\n\n /**\n *
\n * Title\n *
\n * lorem ipsum\n *
\n *
\n */\n return toNode(\"details\", { class: \"bb-spoiler\" }, [\n toNode(\"summary\", {}, title),\n toNode(\"div\", { class: \"bb-spoiler-content\" }, node.content),\n ]);\n};\n\nexport const inlinespoiler = (node) => {\n return toNode(\"span\", { class: \"bb-inline-spoiler\" }, node.content);\n};\n","import { toNode } from \"../utils/common\";\n\n/**\n * @file Adds [justify] to bbcode\n * @example [justify]content[/justify]\n */\nexport const justify = (node) => {\n return toNode(\"div\", { class: \"bb-justify\" }, node.content);\n};\n","import { toNode } from \"../utils/common\";\n/**\n * @file Adds [newspaper] to bbcode\n * @example [newspaper]content[/newspaper]\n */\nexport const newspaper = (node) => {\n return toNode(\"div\", { class: \"bb-newspaper\" }, node.content);\n};\n","import { toNode } from \"../utils/common\";\n/**\n * @file Adds [note] to bbcode\n * @example [note]content[/note]\n */\n\nexport const note = (node) => {\n return toNode(\"div\", { class: \"bb-note\" }, [\n toNode(\"div\", { class: \"bb-note-tape\" }, \"\"),\n toNode(\"div\", { class: \"bb-note-content\" }, [\n node.content,\n toNode(\"div\", { class: \"bb-note-footer\" }, \"\"),\n ]),\n ]);\n};\n","import { toNode } from \"../utils/common\";\n\n/**\n * @file Adds [ooc] to bbcode\n * @example [ooc]content[/ooc]\n */\nexport const ooc = (node) => {\n return toNode(\n \"div\",\n {\n class: \"bb-ooc\",\n },\n node.content,\n );\n};\n","import { toNode } from \"../utils/common\";\n/**\n * @file Adds [pindent] to bbcode\n * @example [pindent]content[/pindent]\n */\nexport const pindent = (node) => {\n return toNode(\"span\", { class: \"bb-pindent\" }, node.content);\n};\n","/**\n * [plain] bbcode tag that prevents parsing of inner tags\n * @example\n * ```\n * [plain]This is [b]bold[/b] and [i]italic[/i][/plain]\n * ```\n * outputs to\n * ```\n * This is [b]bold[/b] and [i]italic[/i]\n * ```\n */\nexport const plain = (node) => {\n return node.content;\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\n/**\n * Add [print] tag\n * @example [print=lined]content[/print]\n */\nexport const print = (node) => {\n const defaultOp = \"print\";\n const printAttr = (preprocessAttr(node.attrs)._default || defaultOp).toLowerCase();\n\n const OPTIONS = [\"print\", \"line\", \"graph\", \"parchment\"];\n\n // Default to print if option is not valid\n const printOption = OPTIONS.includes(printAttr) ? printAttr : defaultOp;\n\n return toNode(\n \"div\",\n { class: printOption === defaultOp ? `bb-print` : `bb-print-${printOption}` },\n node.content,\n );\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\n/**\n * @file Adds [progress] to bbcode\n * @exmaple [progress=percentageInt]content[/progress]\n */\nexport const progress = (node) => {\n const percentageInt = preprocessAttr(node.attrs)._default;\n return toNode(\"div\", { class: \"bb-progress\" }, [\n toNode(\"div\", { class: \"bb-progress-text\" }, node.content),\n toNode(\"div\", { class: \"bb-progress-bar\", style: `width: calc(${percentageInt}% - 6px)` }, \"\"),\n toNode(\"div\", { class: \"bb-progress-bar-other\" }, \"\"),\n ]);\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\n/**\n * @file Adds [thinprogress] to bbcode\n * @exmaple [thinprogress=percentageInt]content[/progthinprogressress]\n */\nexport const thinprogress = (node) => {\n const percentageInt = preprocessAttr(node.attrs)._default;\n return toNode(\"div\", { class: \"bb-progress-thin\" }, [\n toNode(\"div\", { class: \"bb-progress-text\" }, node.content),\n toNode(\"div\", { class: \"bb-progress-bar\", style: `width: calc(${percentageInt}% - 6px)` }, \"\"),\n toNode(\"div\", { class: \"bb-progress-bar-other\" }, \"\"),\n ]);\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\n/**\n * Parse the user provided height and return a valid height value\n * @param {Number} heightValue obtains the input of the user entered height (default is 700)\n * @returns A validated number less than 0.\n */\nfunction parseHeight(heightValue) {\n const maxHeight = 700;\n const parsedHeight =\n heightValue && heightValue.trim() !== \"\" ? heightValue.replace(/[^\\d.]/g, \"\") : 0;\n\n if (parsedHeight && parsedHeight >= 0 && parsedHeight <= maxHeight) {\n return parsedHeight;\n } else {\n // if the value = 0 then nothing will be returned\n return parsedHeight === 0 ? 0 : maxHeight;\n }\n}\n\n/**\n * @file Adds [scroll] to bbcode\n * @example [scroll]content[/scroll]\n */\nexport const scroll = (node) => {\n const attrs = preprocessAttr(node.attrs)._default;\n const heightInput = parseHeight(attrs);\n return toNode(\"div\", { class: \"bb-scroll\", style: `height: ${heightInput}px` }, node.content);\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\nexport const side = (node) => {\n const attrs = preprocessAttr(node.attrs)._default || \"left\";\n return toNode(\"div\", { class: \"bb-side\", \"data-side\": attrs }, node.content);\n};\n","import { toNode } from \"../utils/common\";\n\n/**\n * @file Adds subscript to BBCode\n * @example [sub]content[/sub]\n */\n\nconst sub = (node) => {\n return toNode(\"sub\", {}, node.content);\n};\n\nexport { sub };\n","import { toNode } from \"../utils/common\";\n\n/**\n * @file Adds superscript to bbcode\n * @example [sup]content[/sup]\n */\n\nconst sup = (node) => {\n return toNode(\"sup\", {}, node.content);\n};\n\nexport { sup };\n","/**\n * @file discourse-core-replacement.js\n * This is a dedicated file for replacing the standard Discourse BBCode tags in core.\n * In the markdown-it engine, discourse has added these bbcode tags in the inline parser.\n * However this means that if the parser detects a block level tag inside an inline tag,\n * it will not parse the inline tag.\n *\n * This file is meant to fix such scenarios by doing the parsing of bbcode tags for it.\n *\n * @example\n * [b][h]bold[/h][/b] // this should properly parse the bold tag inside the h tag\n *\n * https://github.com/discourse/discourse/blob/d7ece61252d7671a1f124483836279b99852c08c/app/assets/javascripts/discourse-markdown-it/src/features/bbcode-inline.js\n */\nimport { toNode } from \"../utils/common\";\n\nexport const bold = (node) => {\n return toNode(\"span\", { class: \"bbcode-b\" }, node.content);\n};\n\nexport const italic = (node) => {\n return toNode(\"span\", { class: \"bbcode-i\" }, node.content);\n};\n\nexport const underline = (node) => {\n return toNode(\"span\", { class: \"bbcode-u\" }, node.content);\n};\n\nexport const strike = (node) => {\n return toNode(\"span\", { class: \"bbcode-s\" }, node.content);\n};\n","import { OPEN_BRAKET, CLOSE_BRAKET, SLASH } from '@bbob/plugin-helper';\n// type, value, line, row,\nconst TOKEN_TYPE_ID = 'type'; // 0;\nconst TOKEN_VALUE_ID = 'value'; // 1;\nconst TOKEN_COLUMN_ID = 'row'; // 2;\nconst TOKEN_LINE_ID = 'line'; // 3;\nconst TOKEN_TYPE_WORD = 1; // 'word';\nconst TOKEN_TYPE_TAG = 2; // 'tag';\nconst TOKEN_TYPE_ATTR_NAME = 3; // 'attr-name';\nconst TOKEN_TYPE_ATTR_VALUE = 4; // 'attr-value';\nconst TOKEN_TYPE_SPACE = 5; // 'space';\nconst TOKEN_TYPE_NEW_LINE = 6; // 'new-line';\n/**\n * @param {Token} token\n * @returns {string}\n */ const getTokenValue = (token)=>{\n if (token && typeof token[TOKEN_VALUE_ID] !== 'undefined') {\n return token[TOKEN_VALUE_ID];\n }\n return '';\n};\n/**\n * @param {Token}token\n * @returns {number}\n */ const getTokenLine = (token)=>token && token[TOKEN_LINE_ID] || 0;\nconst getTokenColumn = (token)=>token && token[TOKEN_COLUMN_ID] || 0;\n/**\n * @param {Token} token\n * @returns {boolean}\n */ const isTextToken = (token)=>{\n if (token && typeof token[TOKEN_TYPE_ID] !== 'undefined') {\n return token[TOKEN_TYPE_ID] === TOKEN_TYPE_SPACE || token[TOKEN_TYPE_ID] === TOKEN_TYPE_NEW_LINE || token[TOKEN_TYPE_ID] === TOKEN_TYPE_WORD;\n }\n return false;\n};\n/**\n * @param {Token} token\n * @returns {boolean}\n */ const isTagToken = (token)=>{\n if (token && typeof token[TOKEN_TYPE_ID] !== 'undefined') {\n return token[TOKEN_TYPE_ID] === TOKEN_TYPE_TAG;\n }\n return false;\n};\nconst isTagEnd = (token)=>getTokenValue(token).charCodeAt(0) === SLASH.charCodeAt(0);\nconst isTagStart = (token)=>!isTagEnd(token);\nconst isAttrNameToken = (token)=>{\n if (token && typeof token[TOKEN_TYPE_ID] !== 'undefined') {\n return token[TOKEN_TYPE_ID] === TOKEN_TYPE_ATTR_NAME;\n }\n return false;\n};\n/**\n * @param {Token} token\n * @returns {boolean}\n */ const isAttrValueToken = (token)=>{\n if (token && typeof token[TOKEN_TYPE_ID] !== 'undefined') {\n return token[TOKEN_TYPE_ID] === TOKEN_TYPE_ATTR_VALUE;\n }\n return false;\n};\nconst getTagName = (token)=>{\n const value = getTokenValue(token);\n return isTagEnd(token) ? value.slice(1) : value;\n};\nconst convertTagToText = (token)=>{\n let text = OPEN_BRAKET;\n text += getTokenValue(token);\n text += CLOSE_BRAKET;\n return text;\n};\nclass Token {\n isEmpty() {\n // eslint-disable-next-line no-restricted-globals\n return isNaN(this[TOKEN_TYPE_ID]);\n }\n isText() {\n return isTextToken(this);\n }\n isTag() {\n return isTagToken(this);\n }\n isAttrName() {\n return isAttrNameToken(this);\n }\n isAttrValue() {\n return isAttrValueToken(this);\n }\n isStart() {\n return isTagStart(this);\n }\n isEnd() {\n return isTagEnd(this);\n }\n getName() {\n return getTagName(this);\n }\n getValue() {\n return getTokenValue(this);\n }\n getLine() {\n return getTokenLine(this);\n }\n getColumn() {\n return getTokenColumn(this);\n }\n toString() {\n return convertTagToText(this);\n }\n /**\n * @param {String} type\n * @param {String} value\n * @param line\n * @param row\n */ constructor(type, value, line, row){\n this[TOKEN_TYPE_ID] = Number(type);\n this[TOKEN_VALUE_ID] = String(value);\n this[TOKEN_LINE_ID] = Number(line);\n this[TOKEN_COLUMN_ID] = Number(row);\n }\n}\nexport const TYPE_ID = TOKEN_TYPE_ID;\nexport const VALUE_ID = TOKEN_VALUE_ID;\nexport const LINE_ID = TOKEN_LINE_ID;\nexport const COLUMN_ID = TOKEN_COLUMN_ID;\nexport const TYPE_WORD = TOKEN_TYPE_WORD;\nexport const TYPE_TAG = TOKEN_TYPE_TAG;\nexport const TYPE_ATTR_NAME = TOKEN_TYPE_ATTR_NAME;\nexport const TYPE_ATTR_VALUE = TOKEN_TYPE_ATTR_VALUE;\nexport const TYPE_SPACE = TOKEN_TYPE_SPACE;\nexport const TYPE_NEW_LINE = TOKEN_TYPE_NEW_LINE;\nexport { Token };\nexport default Token;\n","import { QUOTEMARK, BACKSLASH } from '@bbob/plugin-helper';\nfunction CharGrabber(source, options) {\n const cursor = {\n pos: 0,\n len: source.length\n };\n const substrUntilChar = (char)=>{\n const { pos } = cursor;\n const idx = source.indexOf(char, pos);\n return idx >= 0 ? source.substring(pos, idx) : '';\n };\n const includes = (val)=>source.indexOf(val, cursor.pos) >= 0;\n const hasNext = ()=>cursor.len > cursor.pos;\n const isLast = ()=>cursor.pos === cursor.len;\n const skip = (num = 1, silent)=>{\n cursor.pos += num;\n if (options && options.onSkip && !silent) {\n options.onSkip();\n }\n };\n const rest = ()=>source.substring(cursor.pos);\n const grabN = (num = 0)=>source.substring(cursor.pos, cursor.pos + num);\n const curr = ()=>source[cursor.pos];\n const prev = ()=>{\n const prevPos = cursor.pos - 1;\n return typeof source[prevPos] !== 'undefined' ? source[prevPos] : null;\n };\n const next = ()=>{\n const nextPos = cursor.pos + 1;\n return nextPos <= source.length - 1 ? source[nextPos] : null;\n };\n const grabWhile = (cond, silent)=>{\n let start = 0;\n if (hasNext()) {\n start = cursor.pos;\n while(hasNext() && cond(curr())){\n skip(1, silent);\n }\n }\n return source.substring(start, cursor.pos);\n };\n /**\n * @type {skip}\n */ this.skip = skip;\n /**\n * @returns {Boolean}\n */ this.hasNext = hasNext;\n /**\n * @returns {String}\n */ this.getCurr = curr;\n /**\n * @returns {String}\n */ this.getRest = rest;\n /**\n * @returns {String}\n */ this.getNext = next;\n /**\n * @returns {String}\n */ this.getPrev = prev;\n /**\n * @returns {Boolean}\n */ this.isLast = isLast;\n /**\n * @returns {Boolean}\n */ this.includes = includes;\n /**\n * @param {Function} cond\n * @param {Boolean} silent\n * @return {String}\n */ this.grabWhile = grabWhile;\n /**\n * @param {Number} num\n * @return {String}\n */ this.grabN = grabN;\n /**\n * Grabs rest of string until it find a char\n * @param {String} char\n * @return {String}\n */ this.substrUntilChar = substrUntilChar;\n}\n/**\n * Creates a grabber wrapper for source string, that helps to iterate over string char by char\n * @param {String} source\n * @param {Object} options\n * @param {Function} options.onSkip\n * @return CharGrabber\n */ export const createCharGrabber = (source, options)=>new CharGrabber(source, options);\n/**\n * Trims string from start and end by char\n * @example\n * trimChar('*hello*', '*') ==> 'hello'\n * @param {String} str\n * @param {String} charToRemove\n * @returns {String}\n */ export const trimChar = (str, charToRemove)=>{\n while(str.charAt(0) === charToRemove){\n // eslint-disable-next-line no-param-reassign\n str = str.substring(1);\n }\n while(str.charAt(str.length - 1) === charToRemove){\n // eslint-disable-next-line no-param-reassign\n str = str.substring(0, str.length - 1);\n }\n return str;\n};\n/**\n * Unquotes \\\" to \"\n * @param str\n * @return {String}\n */ export const unquote = (str)=>str.replace(BACKSLASH + QUOTEMARK, QUOTEMARK);\nfunction NodeList(values = []) {\n const nodes = values;\n const getLast = ()=>Array.isArray(nodes) && nodes.length > 0 && typeof nodes[nodes.length - 1] !== 'undefined' ? nodes[nodes.length - 1] : null;\n const flushLast = ()=>nodes.length ? nodes.pop() : false;\n const push = (value)=>nodes.push(value);\n const toArray = ()=>nodes;\n this.push = push;\n this.toArray = toArray;\n this.getLast = getLast;\n this.flushLast = flushLast;\n}\n/**\n *\n * @param values\n * @return {NodeList}\n */ export const createList = (values = [])=>new NodeList(values);\n","/* eslint-disable no-plusplus,no-param-reassign */ import { OPEN_BRAKET, CLOSE_BRAKET, QUOTEMARK, BACKSLASH, SLASH, SPACE, TAB, EQ, N } from '@bbob/plugin-helper';\nimport { Token, TYPE_ATTR_NAME, TYPE_ATTR_VALUE, TYPE_NEW_LINE, TYPE_SPACE, TYPE_TAG, TYPE_WORD } from './Token';\nimport { createCharGrabber, trimChar, unquote } from './utils';\n// for cases \nconst EM = '!';\n/**\n * Creates a Token entity class\n * @param {Number} type\n * @param {String} value\n * @param {Number} r line number\n * @param {Number} cl char number in line\n */ const createToken = (type, value, r = 0, cl = 0)=>new Token(type, value, r, cl);\n/**\n * @typedef {Object} Lexer\n * @property {Function} tokenize\n * @property {Function} isTokenNested\n */ /**\n * @param {String} buffer\n * @param {Object} options\n * @param {Function} options.onToken\n * @param {String} options.openTag\n * @param {String} options.closeTag\n * @param {Boolean} options.enableEscapeTags\n * @return {Lexer}\n */ function createLexer(buffer, options = {}) {\n const STATE_WORD = 0;\n const STATE_TAG = 1;\n const STATE_TAG_ATTRS = 2;\n const TAG_STATE_NAME = 0;\n const TAG_STATE_ATTR = 1;\n const TAG_STATE_VALUE = 2;\n let row = 0;\n let col = 0;\n let tokenIndex = -1;\n let stateMode = STATE_WORD;\n let tagMode = TAG_STATE_NAME;\n let contextFreeTag = '';\n const tokens = new Array(Math.floor(buffer.length));\n const openTag = options.openTag || OPEN_BRAKET;\n const closeTag = options.closeTag || CLOSE_BRAKET;\n const escapeTags = !!options.enableEscapeTags;\n const contextFreeTags = options.contextFreeTags || [];\n const onToken = options.onToken || (()=>{});\n const RESERVED_CHARS = [\n closeTag,\n openTag,\n QUOTEMARK,\n BACKSLASH,\n SPACE,\n TAB,\n EQ,\n N,\n EM\n ];\n const NOT_CHAR_TOKENS = [\n openTag,\n SPACE,\n TAB,\n N\n ];\n const WHITESPACES = [\n SPACE,\n TAB\n ];\n const SPECIAL_CHARS = [\n EQ,\n SPACE,\n TAB\n ];\n const isCharReserved = (char)=>RESERVED_CHARS.indexOf(char) >= 0;\n const isNewLine = (char)=>char === N;\n const isWhiteSpace = (char)=>WHITESPACES.indexOf(char) >= 0;\n const isCharToken = (char)=>NOT_CHAR_TOKENS.indexOf(char) === -1;\n const isSpecialChar = (char)=>SPECIAL_CHARS.indexOf(char) >= 0;\n const isEscapableChar = (char)=>char === openTag || char === closeTag || char === BACKSLASH;\n const isEscapeChar = (char)=>char === BACKSLASH;\n const onSkip = ()=>{\n col++;\n };\n const unq = (val)=>unquote(trimChar(val, QUOTEMARK));\n const checkContextFreeMode = (name, isClosingTag)=>{\n if (contextFreeTag !== '' && isClosingTag) {\n contextFreeTag = '';\n }\n if (contextFreeTag === '' && contextFreeTags.includes(name)) {\n contextFreeTag = name;\n }\n };\n const chars = createCharGrabber(buffer, {\n onSkip\n });\n /**\n * Emits newly created token to subscriber\n * @param {Number} type\n * @param {String} value\n */ function emitToken(type, value) {\n const token = createToken(type, value, row, col);\n onToken(token);\n tokenIndex += 1;\n tokens[tokenIndex] = token;\n }\n function nextTagState(tagChars, isSingleValueTag) {\n if (tagMode === TAG_STATE_ATTR) {\n const validAttrName = (char)=>!(char === EQ || isWhiteSpace(char));\n const name = tagChars.grabWhile(validAttrName);\n const isEnd = tagChars.isLast();\n const isValue = tagChars.getCurr() !== EQ;\n tagChars.skip();\n if (isEnd || isValue) {\n emitToken(TYPE_ATTR_VALUE, unq(name));\n } else {\n emitToken(TYPE_ATTR_NAME, name);\n }\n if (isEnd) {\n return TAG_STATE_NAME;\n }\n if (isValue) {\n return TAG_STATE_ATTR;\n }\n return TAG_STATE_VALUE;\n }\n if (tagMode === TAG_STATE_VALUE) {\n let stateSpecial = false;\n const validAttrValue = (char)=>{\n // const isEQ = char === EQ;\n const isQM = char === QUOTEMARK;\n const prevChar = tagChars.getPrev();\n const nextChar = tagChars.getNext();\n const isPrevSLASH = prevChar === BACKSLASH;\n const isNextEQ = nextChar === EQ;\n const isWS = isWhiteSpace(char);\n // const isPrevWS = isWhiteSpace(prevChar);\n const isNextWS = isWhiteSpace(nextChar);\n if (stateSpecial && isSpecialChar(char)) {\n return true;\n }\n if (isQM && !isPrevSLASH) {\n stateSpecial = !stateSpecial;\n if (!stateSpecial && !(isNextEQ || isNextWS)) {\n return false;\n }\n }\n if (!isSingleValueTag) {\n return isWS === false;\n // return (isEQ || isWS) === false;\n }\n return true;\n };\n const name1 = tagChars.grabWhile(validAttrValue);\n tagChars.skip();\n emitToken(TYPE_ATTR_VALUE, unq(name1));\n if (tagChars.isLast()) {\n return TAG_STATE_NAME;\n }\n return TAG_STATE_ATTR;\n }\n const validName = (char)=>!(char === EQ || isWhiteSpace(char) || tagChars.isLast());\n const name2 = tagChars.grabWhile(validName);\n emitToken(TYPE_TAG, name2);\n checkContextFreeMode(name2);\n tagChars.skip();\n // in cases when we has [url=someval]GET[/url] and we dont need to parse all\n if (isSingleValueTag) {\n return TAG_STATE_VALUE;\n }\n const hasEQ = tagChars.includes(EQ);\n return hasEQ ? TAG_STATE_ATTR : TAG_STATE_VALUE;\n }\n function stateTag() {\n const currChar = chars.getCurr();\n const nextChar = chars.getNext();\n chars.skip();\n // detect case where we have '[My word [tag][/tag]' or we have '[My last line word'\n const substr = chars.substrUntilChar(closeTag);\n const hasInvalidChars = substr.length === 0 || substr.indexOf(openTag) >= 0;\n if (isCharReserved(nextChar) || hasInvalidChars || chars.isLast()) {\n emitToken(TYPE_WORD, currChar);\n return STATE_WORD;\n }\n // [myTag ]\n const isNoAttrsInTag = substr.indexOf(EQ) === -1;\n // [/myTag]\n const isClosingTag = substr[0] === SLASH;\n if (isNoAttrsInTag || isClosingTag) {\n const name = chars.grabWhile((char)=>char !== closeTag);\n chars.skip(); // skip closeTag\n emitToken(TYPE_TAG, name);\n checkContextFreeMode(name, isClosingTag);\n return STATE_WORD;\n }\n return STATE_TAG_ATTRS;\n }\n function stateAttrs() {\n const silent = true;\n const tagStr = chars.grabWhile((char)=>char !== closeTag, silent);\n const tagGrabber = createCharGrabber(tagStr, {\n onSkip\n });\n const hasSpace = tagGrabber.includes(SPACE);\n tagMode = TAG_STATE_NAME;\n while(tagGrabber.hasNext()){\n tagMode = nextTagState(tagGrabber, !hasSpace);\n }\n chars.skip(); // skip closeTag\n return STATE_WORD;\n }\n function stateWord() {\n if (isNewLine(chars.getCurr())) {\n emitToken(TYPE_NEW_LINE, chars.getCurr());\n chars.skip();\n col = 0;\n row++;\n return STATE_WORD;\n }\n if (isWhiteSpace(chars.getCurr())) {\n const word = chars.grabWhile(isWhiteSpace);\n emitToken(TYPE_SPACE, word);\n return STATE_WORD;\n }\n if (chars.getCurr() === openTag) {\n if (contextFreeTag) {\n const fullTagLen = openTag.length + SLASH.length + contextFreeTag.length;\n const fullTagName = `${openTag}${SLASH}${contextFreeTag}`;\n const foundTag = chars.grabN(fullTagLen);\n const isEndContextFreeMode = foundTag === fullTagName;\n if (isEndContextFreeMode) {\n return STATE_TAG;\n }\n } else if (chars.includes(closeTag)) {\n return STATE_TAG;\n }\n emitToken(TYPE_WORD, chars.getCurr());\n chars.skip();\n return STATE_WORD;\n }\n if (escapeTags) {\n if (isEscapeChar(chars.getCurr())) {\n const currChar = chars.getCurr();\n const nextChar = chars.getNext();\n chars.skip(); // skip the \\ without emitting anything\n if (isEscapableChar(nextChar)) {\n chars.skip(); // skip past the [, ] or \\ as well\n emitToken(TYPE_WORD, nextChar);\n return STATE_WORD;\n }\n emitToken(TYPE_WORD, currChar);\n return STATE_WORD;\n }\n const isChar = (char)=>isCharToken(char) && !isEscapeChar(char);\n const word1 = chars.grabWhile(isChar);\n emitToken(TYPE_WORD, word1);\n return STATE_WORD;\n }\n const word2 = chars.grabWhile(isCharToken);\n emitToken(TYPE_WORD, word2);\n return STATE_WORD;\n }\n function tokenize() {\n stateMode = STATE_WORD;\n while(chars.hasNext()){\n switch(stateMode){\n case STATE_TAG:\n stateMode = stateTag();\n break;\n case STATE_TAG_ATTRS:\n stateMode = stateAttrs();\n break;\n case STATE_WORD:\n default:\n stateMode = stateWord();\n break;\n }\n }\n tokens.length = tokenIndex + 1;\n return tokens;\n }\n function isTokenNested(token) {\n const value = openTag + SLASH + token.getValue();\n // potential bottleneck\n return buffer.indexOf(value) > -1;\n }\n return {\n tokenize,\n isTokenNested\n };\n}\nexport const createTokenOfType = createToken;\nexport { createLexer };\n","import { TagNode, CLOSE_BRAKET, OPEN_BRAKET, isTagNode } from '@bbob/plugin-helper';\nimport { createLexer } from './lexer';\nimport { createList } from './utils';\n/**\n * @public\n * @param {string} input\n * @param {Object} opts\n * @param {Function} opts.createTokenizer\n * @param {Array} opts.onlyAllowTags\n * @param {Array} opts.contextFreeTags\n * @param {Boolean} opts.enableEscapeTags\n * @param {string} opts.openTag\n * @param {string} opts.closeTag\n * @return {Array}\n */ const parse = (input, opts = {})=>{\n const options = opts;\n const openTag = options.openTag || OPEN_BRAKET;\n const closeTag = options.closeTag || CLOSE_BRAKET;\n const onlyAllowTags = (options.onlyAllowTags || []).filter(Boolean).map((tag)=>tag.toLowerCase());\n let tokenizer = null;\n /**\n * Result AST of nodes\n * @private\n * @type {NodeList}\n */ const nodes = createList();\n /**\n * Temp buffer of nodes that's nested to another node\n * @private\n * @type {NodeList}\n */ const nestedNodes = createList();\n /**\n * Temp buffer of nodes [tag..]...[/tag]\n * @private\n * @type {NodeList}\n */ const tagNodes = createList();\n /**\n * Temp buffer of tag attributes\n * @private\n * @type {NodeList}\n */ const tagNodesAttrName = createList();\n /**\n * Cache for nested tags checks\n * @type Set\n */ const nestedTagsMap = new Set();\n /**\n * @param {Token} token\n * @returns {boolean}\n */ const isTokenNested = (token)=>{\n const value = token.getValue();\n if (!nestedTagsMap.has(value) && tokenizer.isTokenNested && tokenizer.isTokenNested(token)) {\n nestedTagsMap.add(value);\n return true;\n }\n return nestedTagsMap.has(value);\n };\n /**\n * @private\n * @param {string} tagName\n * @returns {boolean}\n */ const isTagNested = (tagName)=>Boolean(nestedTagsMap.has(tagName));\n /**\n * @private\n * @param {string} value\n * @return {boolean}\n */ const isAllowedTag = (value)=>{\n if (onlyAllowTags.length) {\n return onlyAllowTags.indexOf(value.toLowerCase()) >= 0;\n }\n return true;\n };\n /**\n * Flushes temp tag nodes and its attributes buffers\n * @private\n * @return {Array}\n */ const flushTagNodes = ()=>{\n if (tagNodes.flushLast()) {\n tagNodesAttrName.flushLast();\n }\n };\n /**\n * @private\n * @return {Array}\n */ const getNodes = ()=>{\n const lastNestedNode = nestedNodes.getLast();\n if (lastNestedNode && Array.isArray(lastNestedNode.content)) {\n return lastNestedNode.content;\n }\n return nodes.toArray();\n };\n /**\n * @private\n * @param {string|TagNode} node\n * @param {boolean} isNested\n */ const appendNodeAsString = (node, isNested = true)=>{\n const items = getNodes();\n if (Array.isArray(items)) {\n items.push(node.toTagStart({\n openTag,\n closeTag\n }));\n if (node.content.length) {\n node.content.forEach((item)=>{\n items.push(item);\n });\n if (isNested) {\n items.push(node.toTagEnd({\n openTag,\n closeTag\n }));\n }\n }\n }\n };\n /**\n * @private\n * @param {string|TagNode} node\n */ const appendNodes = (node)=>{\n const items = getNodes();\n if (Array.isArray(items)) {\n if (isTagNode(node)) {\n if (isAllowedTag(node.tag)) {\n items.push(node.toTagNode());\n } else {\n appendNodeAsString(node);\n }\n } else {\n items.push(node);\n }\n }\n };\n /**\n * @private\n * @param {Token} token\n */ const handleTagStart = (token)=>{\n flushTagNodes();\n const tagNode = TagNode.create(token.getValue());\n const isNested = isTokenNested(token);\n tagNodes.push(tagNode);\n if (isNested) {\n nestedNodes.push(tagNode);\n } else {\n appendNodes(tagNode, token);\n }\n };\n /**\n * @private\n * @param {Token} token\n */ const handleTagEnd = (token)=>{\n flushTagNodes();\n const lastNestedNode = nestedNodes.flushLast();\n if (lastNestedNode) {\n appendNodes(lastNestedNode, token);\n } else if (typeof options.onError === 'function') {\n const tag = token.getValue();\n const line = token.getLine();\n const column = token.getColumn();\n options.onError({\n message: `Inconsistent tag '${tag}' on line ${line} and column ${column}`,\n tagName: tag,\n lineNumber: line,\n columnNumber: column\n });\n }\n };\n /**\n * @private\n * @param {Token} token\n */ const handleTag = (token)=>{\n // [tag]\n if (token.isStart()) {\n handleTagStart(token);\n }\n // [/tag]\n if (token.isEnd()) {\n handleTagEnd(token);\n }\n };\n /**\n * @private\n * @param {Token} token\n */ const handleNode = (token)=>{\n /**\n * @type {TagNode}\n */ const lastTagNode = tagNodes.getLast();\n const tokenValue = token.getValue();\n const isNested = isTagNested(token);\n if (lastTagNode) {\n if (token.isAttrName()) {\n tagNodesAttrName.push(tokenValue);\n lastTagNode.attr(tagNodesAttrName.getLast(), '');\n } else if (token.isAttrValue()) {\n const attrName = tagNodesAttrName.getLast();\n if (attrName) {\n lastTagNode.attr(attrName, tokenValue);\n tagNodesAttrName.flushLast();\n } else {\n lastTagNode.attr(tokenValue, tokenValue);\n }\n } else if (token.isText()) {\n if (isNested) {\n lastTagNode.append(tokenValue);\n } else {\n appendNodes(tokenValue);\n }\n } else if (token.isTag()) {\n // if tag is not allowed, just past it as is\n appendNodes(token.toString());\n }\n } else if (token.isText()) {\n appendNodes(tokenValue);\n } else if (token.isTag()) {\n // if tag is not allowed, just past it as is\n appendNodes(token.toString());\n }\n };\n /**\n * @private\n * @param {Token} token\n */ const onToken = (token)=>{\n if (token.isTag()) {\n handleTag(token);\n } else {\n handleNode(token);\n }\n };\n tokenizer = (opts.createTokenizer ? opts.createTokenizer : createLexer)(input, {\n onToken,\n openTag,\n closeTag,\n onlyAllowTags: options.onlyAllowTags,\n contextFreeTags: options.contextFreeTags,\n enableEscapeTags: options.enableEscapeTags\n });\n // eslint-disable-next-line no-unused-vars\n const tokens = tokenizer.tokenize();\n // handles situations where we open tag, but forgot close them\n // for ex [q]test[/q][u]some[/u][q]some [u]some[/u] // forgot to close [/q]\n // so we need to flush nested content to nodes array\n const lastNestedNode = nestedNodes.flushLast();\n if (lastNestedNode && isTagNested(lastNestedNode.tag)) {\n appendNodeAsString(lastNestedNode, false);\n }\n return nodes.toArray();\n};\nexport { parse };\nexport default parse;\n","/* eslint-disable no-plusplus */ const isObj = (value)=>typeof value === 'object';\nconst isBool = (value)=>typeof value === 'boolean';\nexport function iterate(t, cb) {\n const tree = t;\n if (Array.isArray(tree)) {\n for(let idx = 0; idx < tree.length; idx++){\n tree[idx] = iterate(cb(tree[idx]), cb);\n }\n } else if (tree && isObj(tree) && tree.content) {\n iterate(tree.content, cb);\n }\n return tree;\n}\nexport function same(expected, actual) {\n if (typeof expected !== typeof actual) {\n return false;\n }\n if (!isObj(expected) || expected === null) {\n return expected === actual;\n }\n if (Array.isArray(expected)) {\n return expected.every((exp)=>[].some.call(actual, (act)=>same(exp, act)));\n }\n return Object.keys(expected).every((key)=>{\n const ao = actual[key];\n const eo = expected[key];\n if (isObj(eo) && eo !== null && ao !== null) {\n return same(eo, ao);\n }\n if (isBool(eo)) {\n return eo !== (ao === null);\n }\n return ao === eo;\n });\n}\nexport function match(expression, cb) {\n return Array.isArray(expression) ? iterate(this, (node)=>{\n for(let idx = 0; idx < expression.length; idx++){\n if (same(expression[idx], node)) {\n return cb(node);\n }\n }\n return node;\n }) : iterate(this, (node)=>same(expression, node) ? cb(node) : node);\n}\n","import { parse } from '@bbob/parser';\nimport { iterate, match } from './utils';\nfunction walk(cb) {\n return iterate(this, cb);\n}\nexport default function bbob(plugs) {\n const plugins = typeof plugs === 'function' ? [\n plugs\n ] : plugs || [];\n let options = {\n skipParse: false\n };\n return {\n process (input, opts) {\n options = opts || {};\n const parseFn = options.parser || parse;\n const renderFn = options.render;\n const data = options.data || null;\n if (typeof parseFn !== 'function') {\n throw new Error('\"parser\" is not a function, please pass to \"process(input, { parser })\" right function');\n }\n let tree = options.skipParse ? input || [] : parseFn(input, options);\n // raw tree before modification with plugins\n const raw = tree;\n tree.messages = [];\n tree.options = options;\n tree.walk = walk;\n tree.match = match;\n plugins.forEach((plugin)=>{\n tree = plugin(tree, {\n parse: parseFn,\n render: renderFn,\n iterate,\n match,\n data\n }) || tree;\n });\n return {\n get html () {\n if (typeof renderFn !== 'function') {\n throw new Error('\"render\" function not defined, please pass to \"process(input, { render })\"');\n }\n return renderFn(tree, tree.options);\n },\n tree,\n raw,\n messages: tree.messages\n };\n }\n };\n}\n","import core from '@bbob/core';\nimport { attrsToString } from '@bbob/plugin-helper';\nconst SELFCLOSE_END_TAG = '/>';\nconst CLOSE_START_TAG = '';\nconst renderNode = (node, { stripTags =false })=>{\n if (!node) return '';\n const type = typeof node;\n if (type === 'string' || type === 'number') {\n return node;\n }\n if (type === 'object') {\n if (stripTags === true) {\n // eslint-disable-next-line no-use-before-define\n return renderNodes(node.content, {\n stripTags\n });\n }\n if (node.content === null) {\n return [\n START_TAG,\n node.tag,\n attrsToString(node.attrs),\n SELFCLOSE_END_TAG\n ].join('');\n }\n // eslint-disable-next-line no-use-before-define\n return [\n START_TAG,\n node.tag,\n attrsToString(node.attrs),\n END_TAG,\n renderNodes(node.content),\n CLOSE_START_TAG,\n node.tag,\n END_TAG\n ].join('');\n }\n if (Array.isArray(node)) {\n // eslint-disable-next-line no-use-before-define\n return renderNodes(node, {\n stripTags\n });\n }\n return '';\n};\nconst renderNodes = (nodes, { stripTags =false } = {})=>[].concat(nodes).reduce((r, node)=>r + renderNode(node, {\n stripTags\n }), '');\nconst toHTML = (source, plugins, options)=>core(plugins).process(source, {\n ...options,\n render: renderNodes\n }).html;\nexport const render = renderNodes;\nexport default toHTML;\n","/**\n * Plugin that converts consecutive normal spaces (U+0020) to non-breaking spaces (U+00A0).\n * To use, put as function similar to the presets.\n *\n *\n * @example\n * ```ts\n * const output = bbob([preset(), , preserveWhitespace(), lineBreakPlugin()]).process(input, {render}).html\n * ```\n */\nimport { isStringNode } from \"@bbob/plugin-helper\";\n\n/**\n * Checks if input is an object\n * @param value input\n * @returns if value is an object\n */\nconst isObj = (value) => typeof value === \"object\";\n\n/**\n * Walks the tree of nodes. Checks for node of consecutive spaces. If found replaces every space in\n * node with a nonbreaking space.\n * Preserves multiple spaces so html won't truncate them.\n *\n * Walks through entire tree.\n * @param t tree of nodes to be processed\n * @returns modified tree\n */\nconst walk = (t) => {\n const tree = t;\n\n if (Array.isArray(tree)) {\n for (let idx = 0; idx < tree.length; idx++) {\n const child = walk(tree[idx]);\n if (Array.isArray(child)) {\n tree.splice(idx, 1, ...child);\n idx += child.length - 1;\n } else {\n tree[idx] = child;\n }\n }\n } else if (tree && isObj(tree) && tree.content) {\n walk(tree.content);\n }\n\n //Bbob breaks up nodes by the presence of normal spaces.\n //So a node with a normal space can only have normal spaces in that node.\n if (isStringNode(tree)) {\n if (tree.length > 1 && tree[0] === \" \") {\n let numSpaces = tree.length;\n return [String.fromCharCode(160).repeat(numSpaces)];\n }\n }\n\n return tree;\n};\n\n/**\n * Converts consecutive normal spaces (U+0020) to nonbreaking spaces (U+00A0).\n * Supply this as a plugin in the preset lists.\n *\n * @example converts consecutive normal spaces (U+0020) to nonbreaking spaces (U+00A0)\n * ```ts\n * const output = bbob([preset(), preserveWhitespace(), lineBreakPlugin()]).process(input, {render}).html\n * ```\n *\n * @returns plugin to be used in BBob process\n */\nexport const preserveWhitespace = () => {\n return (tree) => walk(tree);\n};\n","import { MD_NEWLINE_INJECT, MD_NEWLINE_INJECT_COMMENT, MD_NEWLINE_PRE_INJECT } from \"./common\";\n\n/**\n * Post Processing designed to fix issues with Markdown and BBCode that the parser can't fix.\n *\n * Separate from markdown-it post processing as it'll be able to manipulate the full string.\n * @param {string} raw string from processing through both BBCode and Markdown\n * @returns post processed string\n */\nfunction removeNewlineInjects(raw) {\n const processed = raw\n .replaceAll(MD_NEWLINE_INJECT, \"\")\n .replaceAll(MD_NEWLINE_PRE_INJECT, \"\")\n .replaceAll(\"\\n\" + MD_NEWLINE_INJECT_COMMENT, \"\")\n .replaceAll(MD_NEWLINE_INJECT_COMMENT + \"\\n\", \"\")\n .replaceAll(MD_NEWLINE_INJECT_COMMENT, \"\"); // Remove all instances of the injected newline\n return processed;\n}\n\n/**\n * Injects hoisted code blocks back into the raw string\n * @param {string} raw input to inject hoisted code blocks into\n * @param {any} data contains hoist map\n * @returns string with hoisted code blocks injected\n */\nfunction renderHoistedCodeBlocks(raw, data) {\n const hoistMap = data.hoistMap;\n for (const [uuid, content] of Object.entries(hoistMap)) {\n raw = raw.replaceAll(uuid, content);\n }\n return raw;\n}\n\n/**\n * Setups the class style tag template for the post\n * @param {string} raw\n * @param {{styles: string[]}} data - contains styles array\n * @returns string\n */\nfunction createClassStyleTagTemplate(raw, data) {\n if (data.styles.length === 0) {\n return raw;\n }\n const template = '\";\n return template + raw;\n}\n\n/**\n * Setups the script tag template for the post\n * @param {string} raw\n * @param {{\n * bbscripts: {\n * id: string,\n * class: string,\n * on: string,\n * version: string,\n * content: string\n * }[]}} data - contains scripts array\n * @returns string\n */\nfunction createScriptTagTemplate(raw, data) {\n if (data.bbscripts.length === 0) {\n return raw;\n }\n const templates = data.bbscripts.map(\n (s) =>\n ``,\n );\n return templates.join(\"\") + raw;\n}\n\n/**\n * Performs post processing on the raw string to address any necessary functionality that BBob/MD can't handle with a plugin (i.e. hoisting).\n * @param {string} raw processed input from after bbob and md\n * @param {any} data from bbob data\n * @returns final processed string\n */\nexport function postprocess(raw, data) {\n let final = raw;\n const postprocessors = [\n removeNewlineInjects,\n createClassStyleTagTemplate,\n createScriptTagTemplate,\n renderHoistedCodeBlocks,\n ];\n for (const postprocessor of postprocessors) {\n final = postprocessor(final, data);\n }\n return final;\n}\n","/**\n * Plugin that converts line breaks to `
` tags.\n * To use, put as function similar to the presets.\n *\n * If a node is marked with `noLineBreakConversion`, then it'll skip the parsing the children\n *\n * @example\n * ```ts\n * const output = bbob([preset(), lineBreakPlugin()]).process(input, {render}).html\n * ```\n */\nimport { isEOL } from \"@bbob/plugin-helper\";\nimport { MD_NEWLINE_INJECT, MD_NEWLINE_PRE_INJECT, URL_REGEX_SINGLE_LINE } from \"../utils/common\";\n\nconst isObj = (value) => typeof value === \"object\";\nconst isString = (value) => typeof value === \"string\";\n\n/**\n * Walks the tree of nodes. Will add `br` tag to all `\\n` in format that can be used in any renderer.\n * Preserves \\n so that markdown-it doesn't try to treat everything like a block\n *\n * If a node has the property noLineBreakConversion is encountered, will skip parsing children.\n * @param t tree of nodes to be processed\n * @returns modified tree\n */\nconst walk = (t, disableLineBreakConversion = false) => {\n const tree = t;\n\n if (Array.isArray(tree)) {\n if (tree.some(isString)) {\n // array contains strings. Might be md compatible\n tree.unshift(MD_NEWLINE_INJECT);\n tree.push(MD_NEWLINE_INJECT);\n }\n for (let idx = 0; idx < tree.length; idx++) {\n const child = walk(tree[idx], disableLineBreakConversion);\n if (Array.isArray(child)) {\n tree.splice(idx, 1, ...child);\n idx += child.length - 1;\n } else {\n tree[idx] = child;\n }\n }\n } else if (tree && isObj(tree) && tree.content) {\n if (tree.isWhitespaceSensitive) {\n // applies only to [code] and [icode]\n // stop walk. children won't be parsed to have
\n return tree.tag ? tree : tree.content;\n }\n if (tree.disableLineBreakConversion) {\n disableLineBreakConversion = true;\n }\n walk(tree.content, disableLineBreakConversion);\n return tree.tag ? tree : tree.content;\n } else if (isString(tree) && URL_REGEX_SINGLE_LINE.test(tree.trim())) {\n // if the entire string is a URL, then it should be prepared for onebox.\n // BBob separates strings by newlines anyway, so we can already assume this is sitting on its own line\n // MD_NEWLINE_INJECT is already replacing newline came before or the start of the array,\n // so we only need to make sure \\n\\n is added after the URL\n return [tree, MD_NEWLINE_PRE_INJECT];\n }\n\n if (isString(tree) && isEOL(tree)) {\n return disableLineBreakConversion\n ? [\"\\n\", MD_NEWLINE_INJECT]\n : [{ tag: \"br\", content: null }, MD_NEWLINE_INJECT];\n }\n\n return tree;\n};\n\n/**\n * Converts `\\n` to `
` self closing tag. Supply this as the last plugin in the preset lists\n *\n * @example converts all line breaks to br\n * ```ts\n * const output = bbob([preset(), lineBreakPlugin()]).process(input, {render}).html\n * ```\n * @example will not convert line breaks inside [nobr]\n * ```ts\n * const nobr = (node: TagNode) => {return { disableLineBreakConversion: true, content: node.content }}; \\\\ tag in preset\n * ...\n * const output = bbob([preset(), lineBreakPlugin()]).process(input, {render}).html\n * ```\n * @returns plugin to be used in BBob process\n */\nexport const lineBreakPlugin = () => {\n return (tree) => walk(tree);\n};\n","import { ESCAPABLES_REGEX, MD_TABLE_REGEX, generateGUID, regexIndexOf } from \"./common\";\n\n/**\n * Find all code blocks and hoist them out of the content and into a map for later insertion\n * @param {string} raw input to preprocess\n * @returns processed string and hoist map\n */\nfunction fenceCodeBlockPreprocess(content, data) {\n /** @type {Object.} */\n const hoistMap = {};\n let index = 0;\n\n const addHoistAndReturnNewStartPoint = (cutOffStart, cutOffEnd, expected, trim = false) => {\n const uuid = generateGUID();\n if (cutOffEnd !== -1) {\n hoistMap[uuid] = content.substring(cutOffStart, cutOffEnd);\n content = content.substring(0, cutOffStart) + uuid + content.substring(cutOffEnd);\n } else {\n hoistMap[uuid] = content.substring(cutOffStart);\n content = content.substring(0, cutOffStart) + uuid + expected;\n }\n if (trim) {\n if (hoistMap[uuid].startsWith(\"\\n\")) {\n hoistMap[uuid] = hoistMap[uuid].substring(1);\n }\n if (hoistMap[uuid].endsWith(\"\\n\")) {\n hoistMap[uuid] = hoistMap[uuid].substring(0, hoistMap[uuid].length - 1);\n }\n }\n return cutOffStart + uuid.length + expected.length;\n };\n\n while ((index = regexIndexOf(content, ESCAPABLES_REGEX, index)) !== -1) {\n const match = ESCAPABLES_REGEX.exec(content.substring(index));\n if (match.groups?.fence) {\n const fence = match.groups.fence;\n const fenceInfo = match.groups.fenceInfo;\n if (content[index] === \"\\n\") {\n // Check if the fence is not at the start of the content\n index += 1;\n }\n const closingFenceRegex = new RegExp(\"\\n\" + fence + \"(\\n|$)\"); // Find the next fence. By commonmark spec, it should be the same fence length and type\n const nextIndex = regexIndexOf(content, closingFenceRegex, index + fence.length);\n\n const uuid = generateGUID();\n if (nextIndex !== -1) {\n hoistMap[uuid] = content.substring(index + fence.length + fenceInfo.length, nextIndex);\n } else {\n hoistMap[uuid] = content.substring(index + fence.length + fenceInfo.length);\n }\n // inject bbcode tag before and after the code block. This is to prevent BBob plugin from injecting newlines\n const replacement = `[saveNL]\\n${fence}${fenceInfo}${uuid}\\n${fence}\\n[/saveNL]`;\n content =\n content.substring(0, index) +\n replacement +\n (nextIndex !== -1 ? content.substring(nextIndex + 1 + fence.length) : \"\");\n index = index + replacement.length;\n } else if (match.groups?.bbcode) {\n const bbcode = match.groups.bbcode;\n const bbcodeTag = match.groups.bbcodeTag.toLowerCase(); // coerce to lowercase for caseinsensitive matching\n const closingTag = `[/${bbcodeTag}]`;\n const nextIndex = content.toLowerCase().indexOf(closingTag, index + 1);\n index = addHoistAndReturnNewStartPoint(index + bbcode.length, nextIndex, closingTag, true);\n } else if (match.groups.backtick) {\n const backtick = match.groups.backtick; // contains whole content\n const tickStart = match.groups.tickStart;\n const tickEnd = match.groups.tickEnd;\n index = addHoistAndReturnNewStartPoint(\n index + tickStart.length,\n index + backtick.length - tickEnd.length,\n tickEnd,\n );\n }\n }\n\n data.hoistMap = hoistMap;\n return [content, data];\n}\n\n/**\n * Find all markdown table blocks and mark them to ignore newlines\n * @param {string} raw input to preprocess\n * @returns processed string\n */\nfunction mdTableBlockPreprocess(content, data) {\n let index = 0;\n while ((index = regexIndexOf(content, MD_TABLE_REGEX, index)) !== -1) {\n const match = MD_TABLE_REGEX.exec(content.substring(index));\n const table = match[0];\n const replacement = `[saveNL]\\n${table}\\n[/saveNL]`;\n content = content.substring(0, index) + replacement + content.substring(index + table.length);\n index = index + replacement.length;\n }\n return [content, data];\n}\n\n/**\n * Preprocesses input to be formatted for bbob to intake. Handles any necessary functionality that BBob can't handle with a plugin (i.e. hoisting).\n * @param {string} raw input to preprocess\n * @returns formatted input for bbob to intake\n */\nexport function preprocessRaw(raw) {\n let data = {};\n const preprocessors = [fenceCodeBlockPreprocess, mdTableBlockPreprocess];\n for (const preprocessor of preprocessors) {\n [raw, data] = preprocessor(raw, data);\n }\n return [raw, data];\n}\n","import { availableTags, preset, preventParsing } from \"./preset\";\nimport bbob from \"@bbob/core\";\nimport { render } from \"@bbob/html\";\nimport { preserveWhitespace } from \"./plugins/preserveWhitespace\";\nimport { postprocess } from \"./utils/postprocess\";\nimport { lineBreakPlugin } from \"./plugins/lineBreak\";\nimport { preprocessRaw } from \"./utils/preprocess\";\n\nconst options = {\n onlyAllowTags: [...availableTags],\n contextFreeTags: preventParsing, // prevent parsing of children\n enableEscapeTags: true,\n onError: (err) => {\n if (options.previewing) {\n // eslint-disable-next-line no-console\n console.warn(err.message, err.lineNumber, err.columnNumber);\n }\n },\n};\nconst presetTags = preset();\n\nexport const RpNBBCode = (code, opts) => {\n const plugins = [presetTags];\n if (opts.preserveWhitespace) {\n plugins.push(preserveWhitespace());\n }\n plugins.push(lineBreakPlugin());\n const [preprocessed, preprocessedData] = preprocessRaw(code);\n return bbob(plugins).process(preprocessed, {\n render,\n ...options,\n data: {\n ...preprocessedData,\n previewing: opts.previewing,\n fonts: new Set(),\n styles: [],\n bbscripts: [],\n },\n });\n};\n\nexport { postprocess };\n"],"names":["isTagNode","el","tag","process","tags","tree","core","options","walk","node","toNode","attrs","content","gen","preprocessAttr","keys","Object","join","vals","values","_default","toOriginalStartTag","toTagStart","regexIndexOf","string","regex","startpos","indexOf","substring","search","MD_NEWLINE_INJECT","MD_NEWLINE_PRE_INJECT","MD_NEWLINE_INJECT_COMMENT","URL_REGEX_SINGLE_LINE","RegExp","source","ESCAPABLES_REGEX","MD_TABLE_REGEX","generateGUID","d","Date","getTime","window","performance","now","replace","c","r","Math","random","floor","toString","alignment","left","class","center","right","anchor","a","id","trim","name","goto","href","WEB_FONTS","VALID_FONT_STYLES","thin","extralight","light","regular","medium","semibold","bold","extrabold","black","REGISTERED_AXIS","AXES_REGEX","emailHeader","emailFooter","ACCEPTED_OPTIONS","textmessage","attr","recipient","message","option","toLowerCase","includes","N","TAB","EQ","QUOTEMARK","SPACE","OPEN_BRAKET","CLOSE_BRAKET","SLASH","BACKSLASH","isStringNode","keysReduce","obj","reduce","def","getNodeLength","count","contentNode","length","escapeHTML","value","attrValue","type","types","boolean","number","object","JSON","stringify","attrsToString","arr","key","getTagAttrs","params","uniqAattr","res","tagAttr","TagNode","this","append","push","appendToNode","openTag","closeTag","toTagEnd","toTagNode","isEmpty","tagStart","constructor","Array","isArray","create","isOf","SLIDE_TITLE_OPEN","Symbol","SLIDE_TITLE_CLOSE","SLIDE_CLOSE","SLIDE_REGEX","markerToString","marker","accordionTags","accordion","groupId","markedContent","contentArr","newArr","shift","foundIndex","match","preContent","slice","postContent","groups","slideTitleOpen","slideTitleClose","slideClose","generateSlideMarkersFromContent","generatedSlides","nodes","currentSlide","prevMarker","customTitle","generateSlidesFromMarkers","filteredContent","filter","n","map","isValid","customSettings","split","s","bright","bcenter","bleft","fleft","fright","some","endsWith","width","find","classes","style","slide","title","isOpen","open","titleAlign","possibleOptions","t","EVENTS","animation","data","previewing","commonGUID","commonId","keyframes","ident","cleanContent","replaceAll","formatted","styles","bg","color","block","defaultOp","blockAttr","blockOption","blockquote","author","border","val","br","centerblock","percentageInput","check","nameAttr","classSuffix","className","selector","mediaQuery","state","minWidth","maxWidth","unshift","code","isWhitespaceSensitive","inputColor","comment","div","classAttrs","classNames","divide","fieldset","font","fontFamily","family","axes","ital","wght","matches","exec","italic","weight","named_weight","fromEntries","entries","axesParser","url","sort","googleFontApiBuild","fonts","add","custom","fontVar","h","h1","h2","h3","h4","h5","h6","heightrestrict","heightInput","heightValue","parsedHeight","parseHeight","highlight","icode","imagefloat","inlinespoiler","justify","keyframe","mail","attributes","mailAttr","mailOption","person","subject","newspaper","nobr","disableLineBreakConversion","note","ooc","pindent","plain","print","printAttr","printOption","progress","percentageInt","thinprogress","savenl","sh","script","onEvent","on","scriptSetup","version","bbscripts","scroll","side","size","fontSize","fontValue","valid","parsedSize","sizeRanges","unit","parseFontSize","outputAttr","spoiler","providedTitle","sub","sup","tab","tabId","checked","for","tabs","tabsList","forEach","tabNode","b","i","u","availableTags","preset","createPreset","defTags","processor","presetFactory","opts","assign","presetExecutor","extend","callback","TOKEN_TYPE_ID","TOKEN_VALUE_ID","TOKEN_LINE_ID","getTokenValue","token","isTagEnd","charCodeAt","Token","isNaN","isText","isTag","isAttrName","isAttrValue","isStart","isEnd","getName","getTagName","getValue","getLine","getColumn","text","convertTagToText","line","row","Number","String","CharGrabber","cursor","pos","len","hasNext","skip","num","silent","onSkip","curr","getCurr","getRest","getNext","nextPos","getPrev","prevPos","isLast","grabWhile","cond","start","grabN","substrUntilChar","char","idx","createCharGrabber","NodeList","toArray","getLast","flushLast","pop","createList","createLexer","buffer","STATE_WORD","STATE_TAG","STATE_TAG_ATTRS","TAG_STATE_NAME","TAG_STATE_ATTR","TAG_STATE_VALUE","col","tokenIndex","stateMode","tagMode","contextFreeTag","tokens","escapeTags","enableEscapeTags","contextFreeTags","onToken","RESERVED_CHARS","NOT_CHAR_TOKENS","WHITESPACES","SPECIAL_CHARS","isCharReserved","isNewLine","isWhiteSpace","isCharToken","isSpecialChar","isEscapableChar","isEscapeChar","unq","str","charToRemove","charAt","trimChar","checkContextFreeMode","isClosingTag","chars","emitToken","cl","createToken","nextTagState","tagChars","isSingleValueTag","validAttrName","isValue","stateSpecial","validAttrValue","isQM","prevChar","nextChar","isPrevSLASH","isNextEQ","isWS","isNextWS","name1","name2","stateTag","currChar","substr","hasInvalidChars","isNoAttrsInTag","stateAttrs","tagStr","tagGrabber","hasSpace","stateWord","fullTagLen","fullTagName","isChar","tokenize","isTokenNested","parse","input","onlyAllowTags","Boolean","tokenizer","nestedNodes","tagNodes","tagNodesAttrName","nestedTagsMap","Set","isTagNested","tagName","has","flushTagNodes","getNodes","lastNestedNode","appendNodeAsString","isNested","items","item","appendNodes","handleTagStart","tagNode","handleTag","onError","column","lineNumber","columnNumber","handleTagEnd","createTokenizer","lastTagNode","tokenValue","attrName","handleNode","isObj","isBool","iterate","cb","same","expected","actual","every","exp","call","act","ao","eo","expression","SELFCLOSE_END_TAG","CLOSE_START_TAG","START_TAG","END_TAG","renderNodes","stripTags","concat","renderNode","render","child","splice","numSpaces","fromCharCode","repeat","removeNewlineInjects","raw","renderHoistedCodeBlocks","hoistMap","uuid","createClassStyleTagTemplate","createScriptTagTemplate","isString","test","fenceCodeBlockPreprocess","index","addHoistAndReturnNewStartPoint","cutOffStart","cutOffEnd","startsWith","fence","fenceInfo","closingFenceRegex","nextIndex","replacement","bbcode","closingTag","bbcodeTag","backtick","tickStart","tickEnd","mdTableBlockPreprocess","table","err","console","warn","presetTags","plugins","preserveWhitespace","preprocessed","preprocessedData","preprocessors","preprocessor","preprocessRaw","plugs","skipParse","parseFn","parser","renderFn","Error","messages","plugin","html","bbob","final","postprocessors","postprocessor"],"mappings":"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","x_google_ignoreList":[0,9,10,11,49,50,51,52,53,54,55]} \ No newline at end of file +{"version":3,"file":"bbcode-parser.min.js","sources":["../../node_modules/@bbob/preset/es/index.js","../../bbcode-src/utils/common.js","../../bbcode-src/tags/alignment.js","../../bbcode-src/tags/anchor.js","../../bbcode-src/tags/font.js","../../bbcode-src/tags/heightrestrict.js","../../bbcode-src/tags/mail.js","../../bbcode-src/tags/rowcolumn.js","../../bbcode-src/tags/size.js","../../bbcode-src/tags/textmessage.js","../../node_modules/@bbob/plugin-helper/es/char.js","../../node_modules/@bbob/plugin-helper/es/helpers.js","../../node_modules/@bbob/plugin-helper/es/TagNode.js","../../bbcode-src/tags/tabs.js","../../bbcode-src/tags/accordion.js","../../bbcode-src/tags/script.js","../../bbcode-src/preset.js","../../bbcode-src/tags/animation.js","../../bbcode-src/tags/background.js","../../bbcode-src/tags/block.js","../../bbcode-src/tags/blockquote.js","../../bbcode-src/tags/border.js","../../bbcode-src/tags/lineBreak.js","../../bbcode-src/tags/centerblock.js","../../bbcode-src/tags/check.js","../../bbcode-src/tags/class.js","../../bbcode-src/tags/code.js","../../bbcode-src/tags/color.js","../../bbcode-src/tags/comment.js","../../bbcode-src/tags/div.js","../../bbcode-src/tags/divide.js","../../bbcode-src/tags/fieldset.js","../../bbcode-src/tags/header.js","../../bbcode-src/tags/highlight.js","../../bbcode-src/tags/imagefloat.js","../../bbcode-src/tags/spoiler.js","../../bbcode-src/tags/justify.js","../../bbcode-src/tags/newspaper.js","../../bbcode-src/tags/note.js","../../bbcode-src/tags/ooc.js","../../bbcode-src/tags/pindent.js","../../bbcode-src/tags/plain.js","../../bbcode-src/tags/print.js","../../bbcode-src/tags/progress.js","../../bbcode-src/tags/thinprogress.js","../../bbcode-src/tags/scroll.js","../../bbcode-src/tags/side.js","../../bbcode-src/tags/subscript.js","../../bbcode-src/tags/superscript.js","../../bbcode-src/tags/discourse-core-replacement.js","../../node_modules/@bbob/parser/es/Token.js","../../node_modules/@bbob/parser/es/utils.js","../../node_modules/@bbob/parser/es/lexer.js","../../node_modules/@bbob/parser/es/parse.js","../../node_modules/@bbob/core/es/utils.js","../../node_modules/@bbob/core/es/index.js","../../node_modules/@bbob/html/es/index.js","../../bbcode-src/plugins/preserveWhitespace.js","../../bbcode-src/utils/postprocess.js","../../bbcode-src/plugins/lineBreak.js","../../bbcode-src/utils/preprocess.js","../../bbcode-src/index.js"],"sourcesContent":["/* eslint-disable indent */ const isTagNode = (el)=>typeof el === 'object' && !!el.tag;\nfunction process(tags, tree, core, options) {\n tree.walk((node)=>isTagNode(node) && tags[node.tag] ? tags[node.tag](node, core, options) : node);\n}\n/**\n * Creates preset for @bbob/core\n * @param defTags {Object}\n * @param processor {Function} a processor function of tree\n * @returns {function(*=): function(*=, *=): void}\n */ function createPreset(defTags, processor = process) {\n const presetFactory = (opts = {})=>{\n presetFactory.options = Object.assign(presetFactory.options || {}, opts);\n const presetExecutor = (tree, core)=>processor(defTags, tree, core, presetFactory.options);\n presetExecutor.options = presetFactory.options;\n return presetExecutor;\n };\n presetFactory.extend = (callback)=>createPreset(callback(defTags, presetFactory.options), processor);\n return presetFactory;\n}\nexport { createPreset };\nexport default createPreset;\n","/**\n * Generate the node object.\n *\n * Contains additional logic to help break any unintended side effects of the top down parsing of bbob.\n * @param {string} tag name of the tag\n * @param {Object} attrs attributes of the tag\n * @param {any} content contents of the tag. `[]` will create an empty tag. `null` will create a self closing tag\n *\n * @example\n * ```\n * toNode(\"div\", { class: \"class\" }, \"content\")\n * ```\n * becomes\n * ```\n * {\n * tag: \"div\",\n * attrs: { class: \"class\" },\n * content: \"content\",\n * gen: true,\n * }\n */\nconst toNode = (tag, attrs, content = []) => ({\n tag,\n attrs,\n content,\n gen: true,\n});\n\n/**\n * Preprocess attributes of a node to either combine all values into a single default value\n * or return a keyed attribute list\n * @param {any} attrs object of bbcode node attrs\n * @param {string[]} predefinedKeys array of predefined keys to be captured\n * @returns processed attributes\n */\nconst preprocessAttr = (attrs) => {\n const keys = Object.keys(attrs).join(\" \");\n const vals = Object.values(attrs).join(\" \");\n if (keys === vals) {\n return {\n _default: vals,\n };\n } else {\n return attrs;\n }\n};\n\n/**\n * Attempts to return tag into its original form with proper attributes\n * @returns string of tag start\n */\nconst toOriginalStartTag = (node) => {\n if (!node.attrs) {\n return `[${node.tag}]`;\n }\n const attrs = preprocessAttr(node.attrs);\n if (attrs._default) {\n return `[${node.tag}=${attrs._default}]`;\n } else {\n return node.toTagStart();\n }\n};\n\n/**\n * Given a string, find the first position of a regex match\n * @param {string} string to test against\n * @param {RegExp} regex to test with\n * @param {number} startpos starting position. Defaults to 0\n * @returns index of the first match of the regex in the string\n */\nconst regexIndexOf = (string, regex, startpos) => {\n const indexOf = string.substring(startpos || 0).search(regex);\n return indexOf >= 0 ? indexOf + (startpos || 0) : indexOf;\n};\n\nconst MD_NEWLINE_INJECT = \"\\n\\n\";\nconst MD_NEWLINE_PRE_INJECT = \"\\n\\n\";\nconst MD_NEWLINE_INJECT_COMMENT = \"\";\n\nconst URL_REGEX =\n /(http|ftp|https|upload):\\/\\/([\\w_-]+(?:(?:\\.[\\w_-]+)+))([\\w.,@?^=%&:\\/~+#-]*[\\w@?^=%&\\/~+#-])/;\nconst MD_URL_REGEX =\n /\\!?\\[.*\\]\\((http|ftp|https|upload):\\/\\/([\\w_-]+(?:(?:\\.[\\w_-]+)+))([\\w.,@?^=%&:\\/~+#-]*[\\w@?^=%&\\/~+#-])\\)/;\nconst URL_REGEX_SINGLE_LINE = new RegExp(`^${URL_REGEX.source}|${MD_URL_REGEX.source}$`);\nconst ESCAPABLES_REGEX =\n /((\\n|^)(?```+|~~~+)(?.*\\n))|(?\\[(?i?code|plain)(=.*)?\\])|(?(?`{1,2})(.*)(?\\k))/im;\nconst MD_TABLE_REGEX = /^(\\|[^\\n]+\\|\\r?\\n)((?:\\| ?:?[-]+:? ?)+\\|)(\\n(?:\\|[^\\n]+\\|\\r?\\n?)*)?$/m;\n\n/**\n * Generates a random GUID.\n *\n * Mini Racer doesn't have the crypto module, so we can't use the built-in `crypto.randomUUID` function.\n * @returns {string} a GUID\n */\nfunction generateGUID() {\n let d = new Date().getTime();\n if (window.performance && typeof window.performance.now === \"function\") {\n d += performance.now(); //use high-precision timer if available\n }\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, function (c) {\n // eslint-disable-next-line no-bitwise\n const r = (d + Math.random() * 16) % 16 | 0;\n d = Math.floor(d / 16);\n // eslint-disable-next-line no-bitwise\n return (c === \"x\" ? r : (r & 0x3) | 0x8).toString(16);\n });\n}\n\nexport {\n toNode,\n toOriginalStartTag,\n generateGUID,\n preprocessAttr,\n regexIndexOf,\n MD_NEWLINE_INJECT,\n MD_NEWLINE_INJECT_COMMENT,\n MD_NEWLINE_PRE_INJECT,\n URL_REGEX,\n MD_URL_REGEX,\n MD_TABLE_REGEX,\n URL_REGEX_SINGLE_LINE,\n ESCAPABLES_REGEX,\n};\n","import { toNode } from \"../utils/common\";\n/**\n * @file Adds [left], [center], and [right] to bbcode\n * @example [center]content[/center]\n */\nexport const alignment = {\n left: (node) => toNode(\"div\", { class: \"bb-left\" }, node.content),\n center: (node) => toNode(\"div\", { class: \"bb-center\" }, node.content),\n right: (node) => toNode(\"div\", { class: \"bb-right\" }, node.content),\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n/**\n * @file Adds [id] and [goto] to bbcode\n * @example [a=your_anchor_name]An anchor[/a] [goto=your_anchor_name]Jump to an anchor[/goto]\n */\nexport const anchor = {\n // name is not valid in HTML5; however, it correctly displays back while id does not\n a: (node) => {\n const attrs = preprocessAttr(node.attrs)._default || \"\";\n return toNode(\"a\", { id: `user-anchor-${attrs.trim()}`, name: `user-anchor-${attrs.trim()}` }, node.content);\n },\n goto: (node) => {\n const attrs = preprocessAttr(node.attrs)._default || \"\";\n toNode(\"a\", { href: `#user-anchor-${attrs.trim()}` }, node.content);\n }\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\nconst WEB_FONTS = [\n \"arial\",\n \"book antiqua\",\n \"courier new\",\n \"georgia\",\n \"tahoma\",\n \"times new roman\",\n \"trebuchet ms\",\n \"verdana\",\n];\nconst VALID_FONT_STYLES = {\n thin: \"100\",\n extralight: \"200\",\n light: \"300\",\n regular: \"400\",\n medium: \"500\",\n semibold: \"600\",\n bold: \"700\",\n extrabold: \"800\",\n black: \"900\",\n};\n// registered axis tags https://learn.microsoft.com/en-us/typography/opentype/spec/dvaraxisreg#registered-axis-tags\nconst REGISTERED_AXIS = [\"ital\", \"opsz\", \"slnt\", \"wdth\", \"wght\"];\n\nconst AXES_REGEX = /(?[a-zA-Z]*)?\\s?(?[0-9]*)?\\s?(?italic)?/;\n\nconst axesParser = (attrs) => {\n let axes = {\n ital: 0,\n wght: 400,\n };\n\n if (attrs?.style) {\n // user just copy pasted the name of the style on the google font site, probably\n const style = attrs.style.trim().toLowerCase();\n const matches = AXES_REGEX.exec(style).groups || {};\n if (matches?.italic) {\n axes.ital = 1;\n }\n\n const weight = matches.weight;\n if (weight && weight >= 0 && weight <= 900) {\n axes.wght = weight;\n } else if (Object.keys(VALID_FONT_STYLES).includes(matches.named_weight || \"\")) {\n axes.wght = VALID_FONT_STYLES[matches.named_weight];\n }\n\n axes = {\n ...axes,\n ...Object.fromEntries(Object.entries(attrs).filter(([key]) => REGISTERED_AXIS.includes(key))),\n };\n }\n return axes;\n};\n\n/**\n * Create google font api url\n * @param {string} family name of font\n * @param {object} axes custom font axes\n */\nconst googleFontApiBuild = (family, axes) => {\n family = family.replaceAll(\" \", \"+\");\n // google fonts requires axes names to be in alphabetical order\n axes = Object.keys(axes)\n .sort()\n .reduce((obj, key) => {\n obj[key] = axes[key];\n return obj;\n }, {});\n const axesList = Object.keys(axes).join(\",\") + \"@\" + Object.values(axes).join(\",\");\n return \"https://fonts.googleapis.com/css2?family=\" + family + \":\" + axesList;\n};\n\nexport const font = (node, options) => {\n const attrs = preprocessAttr(node.attrs);\n const fontFamily = attrs?._default || attrs.family || attrs.name;\n if (fontFamily.trim() === \"\") {\n return node.content;\n }\n if (WEB_FONTS.includes(fontFamily.trim().toLowerCase())) {\n return toNode(\"span\", { style: \"font-family: \" + fontFamily }, node.content);\n }\n\n const axes = axesParser(attrs);\n const url = googleFontApiBuild(fontFamily, axes);\n options.data.fonts.add(url);\n\n const italic = axes.ital === 1 ? \"italic\" : \"normal\";\n\n const custom = Object.entries(axes).filter(([key]) => key !== \"wght\" && key !== \"ital\");\n let fontVar = \"\";\n if (custom.length) {\n fontVar =\n \"font-variation-settings: \" + custom.map(([key, val]) => `'${key}' ${val}`).join(\", \") + \";\";\n }\n\n return toNode(\n \"span\",\n {\n style: `font-family: ${fontFamily}; font-weight: ${axes.wght}; font-style: ${italic}; ${fontVar}`,\n \"data-font\": url,\n },\n node.content,\n );\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\n/**\n * Parse the user provided height and return a valid height value\n * @param {Number} heightValue obtains the input of the user entered height (default is 700)\n * @returns A validated number less than 0.\n */\nfunction parseHeight(heightValue) {\n const maxHeight = 700;\n const parsedHeight =\n heightValue && heightValue.trim() !== \"\" ? heightValue.replace(/[^\\d.]/g, \"\") : 0;\n\n if (parsedHeight && parsedHeight >= 0 && parsedHeight <= maxHeight) {\n return parsedHeight;\n } else {\n // if the value = 0 then nothing will be returned\n return parsedHeight === 0 ? 0 : maxHeight;\n }\n}\n\n/**\n * @file Adds [heightrestrict] to bbcode\n * @example [heightrestrict=50]content[/heightrestrict]\n */\nexport const heightrestrict = (node) => {\n const attrs = preprocessAttr(node.attrs)._default;\n const heightInput = parseHeight(attrs).toString();\n // Return image's default size if heightrestrict did not involve a valid value\n return heightInput === \"0\"\n ? toNode(\"div\", { class: \"bb-height-restrict\" }, node.content)\n : toNode(\n \"div\",\n { class: \"bb-height-restrict\", style: `height: ${heightInput}px;` },\n node.content,\n );\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n/**\n * @file Adds [mail] to bbcode\n * @param {string} [type=\"send\"] Denotes type of mail either send or receive\n * @param {string} [person=\"Unknown\"] Denotes the person in the To/From field\n * @param {string} [subject=\"Empty\"] Denotes the subject line of the email\n * @example [mail type=\"send\" person=\"John Doe\" subject=\"Hello World\"]content[/mail]\n */\n\nconst parseEmailContent = (content) => {\n return toNode(\"div\", { class: \"bb-email-content\" }, content);\n};\n\nconst parseEmailSubject = (subject) => {\n return toNode(\"div\", { class: \"bb-email-subject\" }, subject);\n};\n\nconst parseEmailPerson = (person) => {\n return toNode(\"div\", { class: \"bb-email-address\" }, person);\n};\n\nconst emailHeader = toNode(\"div\", { class: \"bb-email-header\" }, \"\");\nconst emailFooter = toNode(\n \"div\",\n { class: \"bb-email-footer\" },\n toNode(\"div\", { class: \"bb-email-button\" }, \"\")\n);\n\nexport const mail = (node) => {\n const attributes = preprocessAttr(node.attrs);\n let mailAttr = {\n mailOption: (attributes.type || \"send\").toLowerCase(),\n person: attributes.person || \"Unknown\",\n subject: attributes.subject || \"Empty\",\n };\n\n return toNode(\n \"div\",\n {\n class: \"bb-email\",\n \"data-bb-email\": mailAttr.mailOption,\n },\n [\n emailHeader,\n parseEmailPerson(mailAttr.person),\n parseEmailSubject(mailAttr.subject),\n parseEmailContent(node.content),\n emailFooter,\n ]\n );\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\n/**\n * @file Adds [row][column] to bbcode\n * @example Adds [row][column][/column][/row]\n */\nexport const rowcolumn = {\n row: (node) => toNode(\"div\", { class: \"bb-row\" }, node.content),\n column: (node) => {\n const columnAttrs = preprocessAttr(node.attrs)._default || \"8\";\n const columnStyle = columnAttrs.startsWith(\"span\")\n ? `column-width-${columnAttrs}`\n : `column-width-span${columnAttrs}`;\n return toNode(\"div\", { class: `bb-column`, \"data-span\": columnStyle }, node.content);\n },\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\n/**\n * Parses an inputted size value and returns the formatted valid font size\n * @param {string} fontValue the input of the size\n */\nfunction parseFontSize(fontValue) {\n let value;\n let fontSize = { valid: true };\n const parsedSize = /(\\d+\\.?\\d?)(px|rem)?/i.exec(fontValue);\n const sizeRanges = {\n px_max: 36,\n px_min: 8,\n rem_max: 3,\n rem_min: 0.2,\n unitless_max: 7,\n unitless_min: 1,\n };\n\n if (parsedSize && (value = parsedSize[1])) {\n fontSize.unit = (parsedSize[2] || \"\").toLowerCase();\n switch (fontSize.unit) {\n case \"px\":\n if (value > sizeRanges.px_max) {\n value = sizeRanges.px_max;\n } else if (value < sizeRanges.px_min) {\n value = sizeRanges.px_min;\n }\n break;\n case \"rem\":\n if (value > sizeRanges.rem_max) {\n value = sizeRanges.rem_max;\n } else if (value < sizeRanges.rem_min) {\n value = sizeRanges.rem_min;\n }\n break;\n default:\n if ((fontSize.valid = fontValue.length === value.length)) {\n if (value > sizeRanges.unitless_max) {\n value = sizeRanges.unitless_max;\n } else if (value < sizeRanges.unitless_min) {\n value = sizeRanges.unitless_min;\n }\n }\n break;\n }\n\n fontSize.value = value;\n }\n return fontSize;\n}\n\nexport const size = (node) => {\n const input = preprocessAttr(node.attrs)._default;\n const fontSize = parseFontSize(input);\n if (!fontSize.valid) {\n return node.content;\n }\n let outputAttr = {};\n if (fontSize.unit) {\n outputAttr = { style: `font-size: ${fontSize.value}${fontSize.unit}` };\n } else {\n outputAttr = { \"data-size\": fontSize.value };\n }\n return toNode(\"span\", outputAttr, node.content);\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\n/**\n * @file Adds textmessage to bbcode\n * @exmaple [textmessage=Recipient][message=them]Hi [/message][message=me] Hey![/message][/textmessage]\n */\n\nconst ACCEPTED_OPTIONS = [\"me\", \"them\", \"right\", \"left\"];\nexport const textmessage = {\n textmessage: (node) => {\n const attr = preprocessAttr(node.attrs)._default || \"Recipient\";\n const recipient = attr && attr.trim() !== \"\" ? attr : \"Recipient\";\n return toNode(\"div\", { class: \"bb-textmessage\" }, [\n toNode(\"div\", { class: \"bb-textmessage-name\" }, recipient),\n toNode(\"div\", { class: \"bb-textmessage-overflow\" }, [\n toNode(\"div\", { class: \"bb-textmessage-content\" }, node.content),\n ]),\n ]);\n },\n message: (node) => {\n let option = preprocessAttr(node.attrs)._default.toLowerCase();\n if (!ACCEPTED_OPTIONS.includes(option) || option === \"right\") {\n option = \"me\";\n }\n if (option === \"left\") {\n option = \"them\";\n }\n\n const senderAttrs = option === \"me\" ? \"bb-message-me\" : \"bb-message-them\";\n return toNode(\"div\", { class: senderAttrs }, [\n toNode(\"div\", { class: \"bb-message-content\" }, node.content),\n ]);\n },\n};\n","const N = '\\n';\nconst TAB = '\\t';\nconst F = '\\f';\nconst R = '\\r';\nconst EQ = '=';\nconst QUOTEMARK = '\"';\nconst SPACE = ' ';\nconst OPEN_BRAKET = '[';\nconst CLOSE_BRAKET = ']';\nconst SLASH = '/';\nconst BACKSLASH = '\\\\';\nexport { N, F, R, EQ, TAB, SPACE, SLASH, BACKSLASH, QUOTEMARK, OPEN_BRAKET, CLOSE_BRAKET };\n","import { N } from './char';\nconst isTagNode = (el)=>typeof el === 'object' && !!el.tag;\nconst isStringNode = (el)=>typeof el === 'string';\nconst isEOL = (el)=>el === N;\nconst keysReduce = (obj, reduce, def)=>Object.keys(obj).reduce(reduce, def);\nconst getNodeLength = (node)=>{\n if (isTagNode(node)) {\n return node.content.reduce((count, contentNode)=>count + getNodeLength(contentNode), 0);\n }\n if (isStringNode(node)) {\n return node.length;\n }\n return 0;\n};\n/**\n * Appends value to Tag Node\n * @param {TagNode} node\n * @param value\n */ const appendToNode = (node, value)=>{\n node.content.push(value);\n};\n/**\n * Replaces \" to &qquot;\n * @param {String} value\n */ const escapeHTML = (value)=>value.replace(/&/g, '&').replace(//g, '>').replace(/\"/g, '"').replace(/'/g, ''')// eslint-disable-next-line no-script-url\n .replace(/(javascript|data|vbscript):/gi, '$1%3A');\n/**\n * Acept name and value and return valid html5 attribute string\n * @param {String} name\n * @param {String} value\n * @return {string}\n */ const attrValue = (name, value)=>{\n const type = typeof value;\n const types = {\n boolean: ()=>value ? `${name}` : '',\n number: ()=>`${name}=\"${value}\"`,\n string: ()=>`${name}=\"${escapeHTML(value)}\"`,\n object: ()=>`${name}=\"${escapeHTML(JSON.stringify(value))}\"`\n };\n return types[type] ? types[type]() : '';\n};\n/**\n * Transforms attrs to html params string\n * @param values\n */ const attrsToString = (values)=>{\n // To avoid some malformed attributes\n if (values == null) {\n return '';\n }\n return keysReduce(values, (arr, key)=>[\n ...arr,\n attrValue(key, values[key])\n ], [\n ''\n ]).join(' ');\n};\n/**\n * Gets value from\n * @example\n * getUniqAttr({ 'foo': true, 'bar': bar' }) => 'bar'\n * @param attrs\n * @returns {string}\n */ const getUniqAttr = (attrs)=>keysReduce(attrs, (res, key)=>attrs[key] === key ? attrs[key] : null, null);\nexport { attrsToString, attrValue, appendToNode, escapeHTML, getNodeLength, getUniqAttr, isTagNode, isStringNode, isEOL };\n","import { OPEN_BRAKET, CLOSE_BRAKET, SLASH } from './char';\nimport { getNodeLength, appendToNode, attrsToString, attrValue, getUniqAttr } from './helpers';\nconst getTagAttrs = (tag, params)=>{\n const uniqAattr = getUniqAttr(params);\n if (uniqAattr) {\n const tagAttr = attrValue(tag, uniqAattr);\n const attrs = {\n ...params\n };\n delete attrs[uniqAattr];\n const attrsStr = attrsToString(attrs);\n return `${tagAttr}${attrsStr}`;\n }\n return `${tag}${attrsToString(params)}`;\n};\nclass TagNode {\n attr(name, value) {\n if (typeof value !== 'undefined') {\n this.attrs[name] = value;\n }\n return this.attrs[name];\n }\n append(value) {\n return appendToNode(this, value);\n }\n get length() {\n return getNodeLength(this);\n }\n toTagStart({ openTag =OPEN_BRAKET , closeTag =CLOSE_BRAKET } = {}) {\n const tagAttrs = getTagAttrs(this.tag, this.attrs);\n return `${openTag}${tagAttrs}${closeTag}`;\n }\n toTagEnd({ openTag =OPEN_BRAKET , closeTag =CLOSE_BRAKET } = {}) {\n return `${openTag}${SLASH}${this.tag}${closeTag}`;\n }\n toTagNode() {\n return new TagNode(this.tag.toLowerCase(), this.attrs, this.content);\n }\n toString({ openTag =OPEN_BRAKET , closeTag =CLOSE_BRAKET } = {}) {\n const isEmpty = this.content.length === 0;\n const content = this.content.reduce((r, node)=>r + node.toString({\n openTag,\n closeTag\n }), '');\n const tagStart = this.toTagStart({\n openTag,\n closeTag\n });\n if (isEmpty) {\n return tagStart;\n }\n return `${tagStart}${content}${this.toTagEnd({\n openTag,\n closeTag\n })}`;\n }\n constructor(tag, attrs, content){\n this.tag = tag;\n this.attrs = attrs;\n this.content = Array.isArray(content) ? content : [\n content\n ];\n }\n}\nTagNode.create = (tag, attrs = {}, content = [])=>new TagNode(tag, attrs, content);\nTagNode.isOf = (node, type)=>node.tag === type;\nexport { TagNode };\nexport default TagNode;\n","import { generateGUID, preprocessAttr, toNode, toOriginalStartTag } from \"../utils/common\";\nimport { isTagNode } from \"@bbob/plugin-helper\";\n\n/**\n * @file Adds [tabs][tab] to bbcode\n * @example [tabs][tab=name 1]content[/tab][tab=name 2]content[/tab][/tabs]\n */\nexport const tabs = (node) => {\n const tabsList = node.content.filter(\n (contentNode) => isTagNode(contentNode) && contentNode.tag === \"tab\",\n );\n const groupId = generateGUID();\n tabsList.forEach((tabNode) => {\n tabNode.isValid = true;\n tabNode.groupId = groupId;\n });\n if (!tabsList.length) {\n // no [tab] tags found\n return [toOriginalStartTag(node), ...node.content, node.toTagEnd()];\n }\n tabsList[0].open = true;\n\n return toNode(\n \"div\",\n {\n class: \"bb-tabs\",\n },\n tabsList,\n );\n};\n\n/**\n * [tab=name]content[/tab]\n * [tab name=\"name\" style=\"style\"]content[/tab]\n */\nexport const tab = (node) => {\n if (!node.isValid) {\n // not inside a [tabs] tag\n return [toOriginalStartTag(node), ...node.content, node.toTagEnd()];\n }\n const attrs = preprocessAttr(node.attrs);\n const name = attrs._default || attrs.name || \"Tab\";\n const tabId = `tab-${name.replace(/\\W/g, \"_\")}-${generateGUID()}`;\n return [\n toNode(\"input\", {\n type: \"radio\",\n id: tabId,\n name: \"tab-group-\" + node.groupId,\n class: \"bb-tab\",\n checked: node.open,\n }),\n toNode(\n \"label\",\n {\n class: \"bb-tab-label\",\n for: tabId,\n style: attrs.style,\n },\n name,\n ),\n toNode(\n \"div\",\n {\n class: \"bb-tab-content\",\n },\n node.content,\n ),\n ];\n};\n","import {\n generateGUID,\n preprocessAttr,\n regexIndexOf,\n toNode,\n toOriginalStartTag,\n} from \"../utils/common\";\nimport { TagNode, isStringNode, isTagNode } from \"@bbob/plugin-helper\";\n\nconst SLIDE_TITLE_OPEN = Symbol(\"slide-title-open\");\nconst SLIDE_TITLE_CLOSE = Symbol(\"slide-title-close\");\nconst SLIDE_CLOSE = Symbol(\"slide-close\");\nconst SLIDE_REGEX =\n /(?\\{slide=)|(?\\})|(?\\{\\/slide\\})/i;\n\n/**\n * Adds the accordion tag\n * [accordion]{slide=name}content{/slide}[/accordion]\n *\n * [accordion][slide=name]content[/slide][/accordion]\n */\nconst accordion = (node) => {\n const groupId = generateGUID();\n\n // add support for existing {slide} tags style, due to copious amounts of existing content\n // also the only way to get true custom content inside a slide due to nesting limitations\n const markedContent = generateSlideMarkersFromContent(node.content);\n const generatedSlides = generateSlidesFromMarkers(markedContent);\n\n const filteredContent = generatedSlides\n .filter((n) => isTagNode(n) && n.tag === \"slide\")\n .map((content) => {\n content.isValid = true;\n content.groupId = groupId;\n return content;\n });\n if (!filteredContent.length) {\n // no [slide] tags found\n return [toOriginalStartTag(node), ...node.content, node.toTagEnd()];\n }\n\n const attrs = preprocessAttr(node.attrs);\n\n if (attrs._default) {\n /** @type {string[]} */\n const customSettings = attrs._default.split(\"|\").map((s) => s.trim());\n if (customSettings.includes(\"bright\")) {\n attrs.bright = true;\n }\n if (customSettings.includes(\"bcenter\")) {\n attrs.bcenter = true;\n }\n if (customSettings.includes(\"bleft\")) {\n attrs.bleft = true;\n }\n if (customSettings.includes(\"fleft\")) {\n attrs.fleft = true;\n }\n if (customSettings.includes(\"fright\")) {\n attrs.fright = true;\n }\n if (\n customSettings.some((s) => s.endsWith(\"px\")) ||\n customSettings.some((s) => s.endsWith(\"%\"))\n ) {\n attrs.width = customSettings.find((s) => s.endsWith(\"px\") || s.endsWith(\"%\"));\n }\n }\n\n let classes = Object.keys(attrs)\n .filter((s) => [\"bright\", \"bcenter\", \"bleft\", \"fleft\", \"fright\"].includes(s))\n .join(\" \");\n let style = \"\";\n if (attrs.width?.endsWith(\"px\") || attrs.width?.endsWith(\"%\")) {\n style = `width: ${attrs.width};`;\n }\n return toNode(\n \"div\",\n { class: \"bb-accordion \" + classes, \"data-group-id\": groupId, style },\n filteredContent,\n );\n};\n\n/**\n * Locates and splits all {slide} tag components into their respective parts while preserving remaining content\n * @param {(TagNode|string)[]} contentArr node content of the accordion tag\n *\n * @example\n * ```\n * [\"{slide=test}\", \"lorem ipsum\", \"{/slide}\"]\n * ```\n * becomes\n * ```\n * [SLIDE_TITLE_OPEN, \"test\", SLIDE_TITLE_CLOSE, \"lorem ipsum\", SLIDE_CLOSE]\n * ```\n */\nfunction generateSlideMarkersFromContent(contentArr) {\n contentArr = [...contentArr]; // shallow clone. object nodes are not modified anyway\n\n const newArr = [];\n while (contentArr.length > 0) {\n const content = contentArr[0];\n if (isTagNode(content)) {\n newArr.push(contentArr.shift());\n continue;\n }\n const foundIndex = regexIndexOf(content, SLIDE_REGEX);\n if (foundIndex === -1) {\n newArr.push(contentArr.shift());\n continue;\n }\n const match = content.match(SLIDE_REGEX);\n const preContent = content.slice(0, foundIndex);\n const postContent = content.slice(foundIndex + match[0].length);\n if (preContent.length) {\n newArr.push(preContent);\n }\n if (match.groups.slideTitleOpen) {\n newArr.push(SLIDE_TITLE_OPEN);\n }\n if (match.groups.slideTitleClose) {\n newArr.push(SLIDE_TITLE_CLOSE);\n }\n if (match.groups.slideClose) {\n newArr.push(SLIDE_CLOSE);\n }\n if (postContent.length) {\n contentArr[0] = postContent;\n } else {\n contentArr.shift();\n }\n }\n\n return newArr;\n}\n\n/**\n * Generates slide nodes from markers\n * @param {(string | typeof SLIDE_TITLE_OPEN | typeof SLIDE_TITLE_CLOSE | typeof SLIDE_CLOSE | TagNode)[]} markedContent\n */\nfunction generateSlidesFromMarkers(markedContent) {\n const nodes = [];\n let currentSlide = null;\n /** @type {typeof SLIDE_TITLE_OPEN | typeof SLIDE_TITLE_CLOSE | null} */\n let prevMarker = null;\n for (const content of markedContent) {\n if (content === SLIDE_TITLE_OPEN && prevMarker === null) {\n currentSlide = TagNode.create(\"slide\");\n currentSlide.content = [];\n currentSlide.customTitle = [];\n prevMarker = SLIDE_TITLE_OPEN;\n } else if (content === SLIDE_TITLE_CLOSE && prevMarker === SLIDE_TITLE_OPEN) {\n prevMarker = SLIDE_TITLE_CLOSE;\n continue;\n } else if (content === SLIDE_CLOSE && currentSlide && prevMarker === SLIDE_TITLE_CLOSE) {\n nodes.push(currentSlide);\n currentSlide = null;\n prevMarker = null;\n } else if (currentSlide) {\n if (prevMarker === SLIDE_TITLE_OPEN) {\n currentSlide.customTitle.push(markerToString(content));\n } else {\n currentSlide.content.push(markerToString(content));\n }\n } else {\n // no slide open, just add content\n nodes.push(markerToString(content));\n }\n }\n return nodes;\n}\n\n/**\n * Processes content into a string. Catches stray markers and converts them back into a string\n * @param {string | typeof SLIDE_TITLE_OPEN | typeof SLIDE_TITLE_CLOSE | typeof SLIDE_CLOSE} marker\n * @returns expected string\n */\nfunction markerToString(marker) {\n switch (marker) {\n case SLIDE_TITLE_OPEN:\n return \"{slide=\";\n case SLIDE_TITLE_CLOSE:\n return \"}\";\n case SLIDE_CLOSE:\n return \"{/slide}\";\n default:\n return marker;\n }\n}\n\nconst slide = (node) => {\n if (!node.isValid) {\n // not inside an [accordion] tag\n return [toOriginalStartTag(node), ...node.content, node.toTagEnd()];\n }\n const attrs = preprocessAttr(node.attrs);\n let title = [attrs.title || attrs._default || \"Slide\"];\n let isOpen = !!attrs.open || false;\n let titleAlign = attrs.left ? \"left\" : attrs.right ? \"right\" : attrs.center ? \"center\" : \"left\";\n if (node.customTitle?.length) {\n // slide was created from markers\n title = node.customTitle;\n // pull out old options from title if they exist\n const possibleOptions = title\n .filter((t) => typeof t === \"string\")\n .join(\"\")\n .toLowerCase()\n .split(\"|\")\n .map((s) => s.trim());\n if (possibleOptions.includes(\"open\")) {\n isOpen = true;\n }\n if (possibleOptions.includes(\"right\")) {\n titleAlign = \"right\";\n }\n if (possibleOptions.includes(\"center\")) {\n titleAlign = \"center\";\n }\n if (possibleOptions.includes(\"left\")) {\n titleAlign = \"left\";\n }\n title = title.map((t) => {\n if (isStringNode(t)) {\n t = t.replace(/\\|(open|right|center|left)/gi, \"\");\n }\n return t;\n });\n }\n return [\n toNode(\"details\", { class: \"bb-slide\", open: isOpen }, [\n toNode(\n \"summary\",\n { class: \"bb-slide-title\", style: `text-align: ${titleAlign}; ${attrs.style || \"\"}` },\n title,\n ),\n toNode(\"div\", { class: \"bb-slide-content\" }, node.content),\n ]),\n ];\n};\n\nexport const accordionTags = { accordion, slide };\n","import { preprocessAttr } from \"../utils/common\";\n\nconst EVENTS = [\n \"init\",\n \"click\",\n \"change\",\n \"input\",\n \"dblclick\",\n \"mouseenter\",\n \"mouseleave\",\n \"scroll\",\n];\n\n/**\n * Script tag\n *\n * [script]content[/script]\n *\n * [script class=\"id\" on=\"event\" version=\"2\"]content[/script]\n */\nexport const script = (node, options) => {\n const attrs = preprocessAttr(node.attrs);\n\n if (!options.data.previewing && !options.data.commonGUID) {\n // create a common GUID for the post\n // only applicable for div, style, and script tags\n // this is to prevent the same class name from being used in different posts\n options.data.commonGUID = \"post-\" + Math.random().toString(36).substring(2, 7);\n }\n const classSuffix = options.data.previewing ? \"preview\" : options.data.commonGUID;\n\n const onEvent =\n (EVENTS.includes(attrs.on?.toLowerCase() || \"init\") && attrs.on?.toLowerCase()) || \"init\";\n\n const scriptSetup = {\n id: classSuffix,\n class: attrs.class || \"\",\n on: onEvent,\n version: attrs.version || \"\",\n content: node.content.join(\"\"),\n };\n options.data.bbscripts.push(scriptSetup);\n\n return [];\n};\n","import { createPreset } from \"@bbob/preset\";\nimport { alignment } from \"./tags/alignment\";\nimport { anchor } from \"./tags/anchor\";\nimport { bg } from \"./tags/background\";\nimport { block } from \"./tags/block\";\nimport { blockquote } from \"./tags/blockquote\";\nimport { border } from \"./tags/border\";\nimport { centerblock } from \"./tags/centerblock\";\nimport { check } from \"./tags/check\";\nimport { code, icode, savenl } from \"./tags/code\";\nimport { color } from \"./tags/color\";\nimport { comment } from \"./tags/comment\";\nimport { divide } from \"./tags/divide\";\nimport { fieldset } from \"./tags/fieldset\";\nimport { font } from \"./tags/font\";\nimport { h, h1, h2, h3, h4, h5, h6, sh } from \"./tags/header\";\nimport { heightrestrict } from \"./tags/heightrestrict\";\nimport { highlight } from \"./tags/highlight\";\nimport { imagefloat } from \"./tags/imagefloat\";\nimport { justify } from \"./tags/justify\";\nimport { mail } from \"./tags/mail\";\nimport { newspaper } from \"./tags/newspaper\";\nimport { br, nobr } from \"./tags/lineBreak\";\nimport { note } from \"./tags/note\";\nimport { ooc } from \"./tags/ooc\";\nimport { pindent } from \"./tags/pindent\";\nimport { plain } from \"./tags/plain\";\nimport { print } from \"./tags/print\";\nimport { progress } from \"./tags/progress\";\nimport { rowcolumn } from './tags/rowcolumn';\nimport { thinprogress } from \"./tags/thinprogress\";\nimport { scroll } from \"./tags/scroll\";\nimport { side } from \"./tags/side\";\nimport { size } from \"./tags/size\";\nimport { sub } from \"./tags/subscript\";\nimport { sup } from \"./tags/superscript\";\nimport { inlinespoiler, spoiler } from \"./tags/spoiler\";\nimport { textmessage } from \"./tags/textmessage\";\nimport { tab, tabs } from \"./tags/tabs\";\nimport { accordionTags } from \"./tags/accordion\";\nimport { div } from \"./tags/div\";\nimport { classStyle } from \"./tags/class\";\nimport { script } from \"./tags/script\";\nimport { animation, keyframe } from \"./tags/animation\";\nimport { bold, italic, strike, underline } from \"./tags/discourse-core-replacement\";\n\nconst tags = {\n ...accordionTags,\n ...alignment,\n ...anchor,\n animation,\n bg,\n block,\n blockquote,\n border,\n br,\n centerblock,\n check,\n class: classStyle,\n code,\n color,\n comment,\n div,\n divide,\n fieldset,\n font,\n h,\n h1,\n h2,\n h3,\n h4,\n h5,\n h6,\n heightrestrict,\n highlight,\n icode,\n imagefloat,\n inlinespoiler,\n justify,\n keyframe,\n mail,\n newspaper,\n nobr,\n note,\n ooc,\n pindent,\n plain,\n print,\n progress,\n ...rowcolumn,\n thinprogress,\n savenl,\n sh,\n script,\n scroll,\n side,\n size,\n spoiler,\n sub,\n sup,\n tab,\n tabs,\n ...textmessage,\n\n // discourse core replacement tags\n b: bold,\n i: italic,\n u: underline,\n s: strike,\n};\n\nconst availableTags = Object.keys(tags);\nconst preventParsing = [\"plain\", \"code\", \"icode\", \"class\"];\n\nconst preset = createPreset(tags);\n\nexport { availableTags, tags, preset, preventParsing };\nexport default preset;\n","import { preprocessAttr, toOriginalStartTag } from \"../utils/common\";\nimport { isStringNode, isTagNode } from \"@bbob/plugin-helper\";\n\n/**\n * Renders css Keyframes\n *\n * [animation=name][keyframe=0]color: red[/keyframe][/animation]\n */\nexport const animation = (node, options) => {\n if (!options.data.previewing && !options.data.commonGUID) {\n // create a common GUID for the post\n // only applicable for div, style, and script tags\n // this is to prevent the same class name from being used in different posts\n options.data.commonGUID = \"post-\" + Math.random().toString(36).substring(2, 7);\n }\n const commonId = options.data.previewing ? \"preview\" : options.data.commonGUID;\n\n const name = preprocessAttr(node.attrs)?._default || \"\";\n const keyframes = node.content\n .filter((n) => isTagNode(n) && n.tag === \"keyframe\")\n .map((content) => {\n content.isValid = true;\n /** @type {string} */\n const ident = preprocessAttr(content.attrs)._default || \"\";\n content.ident = ident + (ident.match(/^\\d+$/) ? \"%\" : \"\");\n const cleanContent = content.content\n .filter(isStringNode)\n .join(\"\")\n .replaceAll(/[\\[\\]\\{\\}]/g, \"\");\n content.formatted = `${content.ident}{ ${cleanContent} }`;\n return content;\n });\n const keyframeContent = keyframes.map((n) => n.formatted).join(\"\\n\");\n const content = `@keyframes ${commonId}${name} { ${keyframeContent} }`;\n options.data.styles.push(content);\n return [];\n};\n\nexport const keyframe = (node) => {\n if (!node.isValid) {\n return [toOriginalStartTag(node), ...node.content, node.toTagEnd()];\n }\n return [];\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\n/**\n * Add [bg] tag\n * @example [bg=red]Hello[/bg]\n */\nexport const bg = (node) => {\n const color = preprocessAttr(node.attrs)._default;\n return toNode(\n \"div\",\n {\n style: `background-color: ${color};`,\n class: \"bb-background\",\n },\n node.content,\n );\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\n/**\n * Add [block] tag\n * @example [block=treasure]content[/block]\n */\nexport const block = (node) => {\n const defaultOp = \"block\";\n const blockAttr = (preprocessAttr(node.attrs)._default || defaultOp).toLowerCase();\n\n const OPTIONS = [\n \"block\",\n \"dice\",\n \"dice10\",\n \"setting\",\n \"warning\",\n \"storyteller\",\n \"announcement\",\n \"important\",\n \"question\",\n \"encounter\",\n \"information\",\n \"character\",\n \"treasure\",\n ];\n\n // Default to block option if user did not provide anything valid\n const blockOption = OPTIONS.includes(blockAttr) ? blockAttr : defaultOp;\n\n return toNode(\"table\", { class: \"bb-block\", \"data-bb-block\": blockOption }, [\n toNode(\"tbody\", [\n toNode(\"tr\", [\n toNode(\"td\", { class: \"bb-block-icon\" }),\n toNode(\"td\", { class: \"bb-block-content\" }, node.content),\n ]),\n ]),\n ]);\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\n/**\n * @file Adds [blockquote] to bbcode\n * @example [blockquote=author]content[/blockquote]\n */\nexport const blockquote = (node) => {\n const author = preprocessAttr(node.attrs)._default || \"\";\n\n return toNode(\"div\", { class: \"bb-blockquote\" }, [\n toNode(\"div\", { class: \"bb-blockquote-left\" }),\n toNode(\"div\", { class: \"bb-blockquote-content\" }, [\n node.content,\n toNode(\"div\", { class: \"bb-blockquote-speaker\" }, author !== \"\" ? `- ${author}` : \"\"),\n ]),\n toNode(\"div\", { class: \"bb-blockquote-right\" }),\n ]);\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\nexport const border = (node) => {\n const val = preprocessAttr(node.attrs)._default;\n return toNode(\n \"div\",\n {\n style: `border: ${val};`,\n class: \"bb-border\",\n },\n node.content\n );\n};\n","import { toNode } from \"../utils/common\";\n\n/**\n * Creates a line break html
tag\n */\nexport const br = () => {\n return toNode(\"br\", {}, null);\n};\n\n/**\n * Disables line breaks for given content\n * @example\n * ```\n * [nobr]test\n * test\n * test\n * [/nobr]\n *\n * test test test\n * ```\n */\nexport const nobr = (node) => {\n return { disableLineBreakConversion: true, content: node.content };\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\nexport const centerblock = (node) => {\n const percentageInput = preprocessAttr(node.attrs)._default || \"50\";\n return toNode(\"div\", { style: `margin: 0 auto; width: ${percentageInput}%` }, node.content);\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\nexport const check = (node) => {\n const attrs = preprocessAttr(node.attrs)._default || \"dot\";\n return toNode(\"div\", { class: `bb-check`, \"data-type\": attrs }, node.content);\n};\n","import { isStringNode } from \"@bbob/plugin-helper\";\nimport { preprocessAttr } from \"../utils/common\";\n\n/**\n * Class style tag\n *\n * [class=name]content[/class]\n * [class name=\"className\" state=\"psuedo-class\" minWidth=\"\" maxWidth=\"\"]content[/class]\n * [class name=\"className\" selector=\"\"]content[/class]\n */\nexport const classStyle = (node, options) => {\n const attrs = preprocessAttr(node.attrs);\n const nameAttr = attrs.name || attrs._default;\n\n if (!options.data.previewing && !options.data.commonGUID) {\n // create a common GUID for the post\n // only applicable for div, style, and script tags\n // this is to prevent the same class name from being used in different posts\n options.data.commonGUID = \"post-\" + Math.random().toString(36).substring(2, 7);\n }\n const classSuffix = options.data.previewing ? \"preview\" : options.data.commonGUID;\n const className = nameAttr + \"__\" + classSuffix;\n const content = node.content\n .filter(isStringNode)\n .map((s) => s.replaceAll(\"{post_id}\", classSuffix).replaceAll(/[\\[\\]\\{\\}]/g, \"\"));\n let selector = \"\";\n const mediaQuery = [];\n if (\n [\"hover\", \"focus\", \"active\", \"focus-within\", \"focus-visible\"].includes(\n attrs.state?.toLowerCase(),\n )\n ) {\n selector = \":\" + attrs.state.toLowerCase();\n }\n if (attrs.selector) {\n selector = attrs.selector.replace(/[,{}\\\\\\n]/g, \"\");\n }\n if (attrs.minWidth?.match(/^[0-9]+[a-z]+$/)) {\n // @media (min-width: )\n mediaQuery.push(`(min-width: ${attrs.minWidth})`);\n }\n if (attrs.maxWidth?.match(/^[0-9]+[a-z]+$/)) {\n // @media (max-width: )\n mediaQuery.push(`(max-width: ${attrs.maxWidth})`);\n }\n\n content.unshift(`.${className}${selector} {`);\n content.push(\"}\");\n if (mediaQuery.length) {\n content.unshift(`@media ${mediaQuery.join(\" and \")} {`);\n content.push(\"}\");\n }\n options.data.styles.push(content.join(\"\"));\n\n return [];\n};\n","import { preprocessAttr } from \"../utils/common\";\n\n/**\n * processes [code] tag and returns a fenced code block\n */\nexport const code = (node) => {\n const lang = preprocessAttr(node.attrs)._default || \"bbcode\";\n return {\n isWhitespaceSensitive: true,\n content: [\"```\" + lang + \"\\n\", node.content, \"\\n```\\n\"],\n };\n};\n\n/**\n * processes [icode] tag and returns inline code\n */\nexport const icode = (node) => {\n return {\n isWhitespaceSensitive: true,\n content: [\"`\", node.content, \"`\"],\n };\n};\n\n/**\n * Special tag to save newlines in code blocks. Used for hoisting code blocks\n */\nexport const savenl = (node) => {\n return {\n isWhitespaceSensitive: true,\n content: node.content,\n };\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\nexport const color = (node) => {\n const inputColor = preprocessAttr(node.attrs)._default || \"\";\n if (inputColor.trim() === \"\") {\n return node.content;\n }\n return toNode(\"span\", { style: `color: ${inputColor}` }, node.content);\n};\n","import { toNode } from \"../utils/common\";\n\n/**\n * @file Adds [comment] tag\n * @example [comment]Content[/comment]\n */\n\nconst comment = (node) => {\n return toNode(\"span\", { class: \"hidden\" }, node.content);\n};\n\nexport { comment };\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\n/**\n * Adds [div] tag\n * [div=css]Content[/div]\n * [div class=\"class\" style=\"css\"]Content[/div]\n */\nexport const div = (node, options) => {\n if (node.gen) {\n // node is actually a generated node \"div\" made by another tag\n // don't process it\n return node;\n }\n const attrs = preprocessAttr(node.attrs);\n const style = attrs.style || attrs._default;\n const classAttrs = attrs.class;\n if (!classAttrs?.trim()) {\n return toNode(\n \"div\",\n {\n style,\n },\n node.content,\n );\n }\n\n if (!options.data.previewing && !options.data.commonGUID) {\n // create a common GUID for the post\n // only applicable for div, style, and script tags\n // this is to prevent the same class name from being used in different posts\n options.data.commonGUID = \"post-\" + Math.random().toString(36).substring(2, 7);\n }\n const classSuffix = options.data.previewing ? \"preview\" : options.data.commonGUID;\n const classNames = classAttrs\n .split(\" \")\n .map((c) => c + \"__\" + classSuffix)\n .join(\" \");\n\n return toNode(\n \"div\",\n {\n class: classNames,\n style,\n },\n node.content,\n );\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\nexport const divide = (node) => {\n const type = (preprocessAttr(node.attrs)._default || \"\").toLowerCase();\n return toNode(\n \"span\",\n {\n class: \"bb-divide\",\n \"data-type\": type,\n },\n node.content\n );\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\n/**\n * @file Adds [fieldset] to bbcode\n * @example [fieldset=title]content[/fieldset]\n */\nexport const fieldset = (node) => {\n const title = preprocessAttr(node.attrs)._default || \"\";\n return toNode(\"fieldset\", { class: \"bb-fieldset\" }, [\n toNode(\"legend\", { class: \"bb-fieldset-legend\" }, title),\n toNode(\"div\", { class: \"bb-fieldset\" }, node.content),\n ]);\n};\n","import { toNode } from \"../utils/common\";\n\n/**\n * @file Adds Header to bbcode\n * @example [h]content[/h], [h2]content[/h2], [h3]content[/h3],\n * [h4]content[/h4], [h5]content[/h5], [h6]content[/h6].\n */\n\nconst h = (node) => {\n return toNode(\"h1\", {}, node.content);\n};\n\nconst h1 = (node) => {\n return toNode(\"h1\", {}, node.content);\n};\n\nconst h2 = (node) => {\n return toNode(\"h2\", {}, node.content);\n};\n\nconst sh = (node) => {\n return toNode(\"h2\", {}, node.content);\n};\n\nconst h3 = (node) => {\n return toNode(\"h3\", {}, node.content);\n};\n\nconst h4 = (node) => {\n return toNode(\"h4\", {}, node.content);\n};\n\nconst h5 = (node) => {\n return toNode(\"h5\", {}, node.content);\n};\n\nconst h6 = (node) => {\n return toNode(\"h6\", {}, node.content);\n};\n\nexport { h, sh, h1, h2, h3, h4, h5, h6 };\n","import { toNode } from \"../utils/common\";\n/**\n * @file Adds [highlight] to bbcode\n * @example [highlight]content[/highlight]\n */\nexport const highlight = (node) => {\n return toNode(\"span\", { class: \"bb-highlight\" }, node.content);\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n/**\n * @file Adds [imagefloat] to bbcode\n * @exmaple [imagefloat=left]content[/imagefloat]\n */\nexport const imagefloat = (node) => {\n const attrs = preprocessAttr(node.attrs)._default || \"\";\n return toNode(\"div\", { class: `bb-float-${attrs}` }, node.content);\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n/**\n * @file Adds [spoiler] and [inlinespoiler] to bbcode\n *\n * Defaults to \"Spoiler\" name if no title provided\n *\n * @example `[spoiler=Title]text[/spoiler]`\n * @example `[inlinespoiler]hidden content[/inlinespoiler]\n */\n\nexport const spoiler = (node) => {\n const providedTitle = preprocessAttr(node.attrs)._default;\n const title = \"Spoiler\" + (providedTitle ? `: ${providedTitle}` : \"\");\n\n /**\n *
\n * Title\n *
\n * lorem ipsum\n *
\n *
\n */\n return toNode(\"details\", { class: \"bb-spoiler\" }, [\n toNode(\"summary\", {}, title),\n toNode(\"div\", { class: \"bb-spoiler-content\" }, node.content),\n ]);\n};\n\nexport const inlinespoiler = (node) => {\n return toNode(\"span\", { class: \"bb-inline-spoiler\" }, node.content);\n};\n","import { toNode } from \"../utils/common\";\n\n/**\n * @file Adds [justify] to bbcode\n * @example [justify]content[/justify]\n */\nexport const justify = (node) => {\n return toNode(\"div\", { class: \"bb-justify\" }, node.content);\n};\n","import { toNode } from \"../utils/common\";\n/**\n * @file Adds [newspaper] to bbcode\n * @example [newspaper]content[/newspaper]\n */\nexport const newspaper = (node) => {\n return toNode(\"div\", { class: \"bb-newspaper\" }, node.content);\n};\n","import { toNode } from \"../utils/common\";\n/**\n * @file Adds [note] to bbcode\n * @example [note]content[/note]\n */\n\nexport const note = (node) => {\n return toNode(\"div\", { class: \"bb-note\" }, [\n toNode(\"div\", { class: \"bb-note-tape\" }, \"\"),\n toNode(\"div\", { class: \"bb-note-content\" }, [\n node.content,\n toNode(\"div\", { class: \"bb-note-footer\" }, \"\"),\n ]),\n ]);\n};\n","import { toNode } from \"../utils/common\";\n\n/**\n * @file Adds [ooc] to bbcode\n * @example [ooc]content[/ooc]\n */\nexport const ooc = (node) => {\n return toNode(\n \"div\",\n {\n class: \"bb-ooc\",\n },\n node.content,\n );\n};\n","import { toNode } from \"../utils/common\";\n/**\n * @file Adds [pindent] to bbcode\n * @example [pindent]content[/pindent]\n */\nexport const pindent = (node) => {\n return toNode(\"span\", { class: \"bb-pindent\" }, node.content);\n};\n","/**\n * [plain] bbcode tag that prevents parsing of inner tags\n * @example\n * ```\n * [plain]This is [b]bold[/b] and [i]italic[/i][/plain]\n * ```\n * outputs to\n * ```\n * This is [b]bold[/b] and [i]italic[/i]\n * ```\n */\nexport const plain = (node) => {\n return node.content;\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\n/**\n * Add [print] tag\n * @example [print=lined]content[/print]\n */\nexport const print = (node) => {\n const defaultOp = \"print\";\n const printAttr = (preprocessAttr(node.attrs)._default || defaultOp).toLowerCase();\n\n const OPTIONS = [\"print\", \"line\", \"graph\", \"parchment\"];\n\n // Default to print if option is not valid\n const printOption = OPTIONS.includes(printAttr) ? printAttr : defaultOp;\n\n return toNode(\n \"div\",\n { class: printOption === defaultOp ? `bb-print` : `bb-print-${printOption}` },\n node.content,\n );\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\n/**\n * @file Adds [progress] to bbcode\n * @exmaple [progress=percentageInt]content[/progress]\n */\nexport const progress = (node) => {\n const percentageInt = preprocessAttr(node.attrs)._default;\n return toNode(\"div\", { class: \"bb-progress\" }, [\n toNode(\"div\", { class: \"bb-progress-text\" }, node.content),\n toNode(\"div\", { class: \"bb-progress-bar\", style: `width: calc(${percentageInt}% - 6px)` }, \"\"),\n toNode(\"div\", { class: \"bb-progress-bar-other\" }, \"\"),\n ]);\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\n/**\n * @file Adds [thinprogress] to bbcode\n * @exmaple [thinprogress=percentageInt]content[/progthinprogressress]\n */\nexport const thinprogress = (node) => {\n const percentageInt = preprocessAttr(node.attrs)._default;\n return toNode(\"div\", { class: \"bb-progress-thin\" }, [\n toNode(\"div\", { class: \"bb-progress-text\" }, node.content),\n toNode(\"div\", { class: \"bb-progress-bar\", style: `width: calc(${percentageInt}% - 6px)` }, \"\"),\n toNode(\"div\", { class: \"bb-progress-bar-other\" }, \"\"),\n ]);\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\n/**\n * Parse the user provided height and return a valid height value\n * @param {Number} heightValue obtains the input of the user entered height (default is 700)\n * @returns A validated number less than 0.\n */\nfunction parseHeight(heightValue) {\n const maxHeight = 700;\n const parsedHeight =\n heightValue && heightValue.trim() !== \"\" ? heightValue.replace(/[^\\d.]/g, \"\") : 0;\n\n if (parsedHeight && parsedHeight >= 0 && parsedHeight <= maxHeight) {\n return parsedHeight;\n } else {\n // if the value = 0 then nothing will be returned\n return parsedHeight === 0 ? 0 : maxHeight;\n }\n}\n\n/**\n * @file Adds [scroll] to bbcode\n * @example [scroll]content[/scroll]\n */\nexport const scroll = (node) => {\n const attrs = preprocessAttr(node.attrs)._default;\n const heightInput = parseHeight(attrs);\n return toNode(\"div\", { class: \"bb-scroll\", style: `height: ${heightInput}px` }, node.content);\n};\n","import { preprocessAttr, toNode } from \"../utils/common\";\n\nexport const side = (node) => {\n const attrs = preprocessAttr(node.attrs)._default || \"left\";\n return toNode(\"div\", { class: \"bb-side\", \"data-side\": attrs }, node.content);\n};\n","import { toNode } from \"../utils/common\";\n\n/**\n * @file Adds subscript to BBCode\n * @example [sub]content[/sub]\n */\n\nconst sub = (node) => {\n return toNode(\"sub\", {}, node.content);\n};\n\nexport { sub };\n","import { toNode } from \"../utils/common\";\n\n/**\n * @file Adds superscript to bbcode\n * @example [sup]content[/sup]\n */\n\nconst sup = (node) => {\n return toNode(\"sup\", {}, node.content);\n};\n\nexport { sup };\n","/**\n * @file discourse-core-replacement.js\n * This is a dedicated file for replacing the standard Discourse BBCode tags in core.\n * In the markdown-it engine, discourse has added these bbcode tags in the inline parser.\n * However this means that if the parser detects a block level tag inside an inline tag,\n * it will not parse the inline tag.\n *\n * This file is meant to fix such scenarios by doing the parsing of bbcode tags for it.\n *\n * @example\n * [b][h]bold[/h][/b] // this should properly parse the bold tag inside the h tag\n *\n * https://github.com/discourse/discourse/blob/d7ece61252d7671a1f124483836279b99852c08c/app/assets/javascripts/discourse-markdown-it/src/features/bbcode-inline.js\n */\nimport { toNode } from \"../utils/common\";\n\nexport const bold = (node) => {\n return toNode(\"span\", { class: \"bbcode-b\" }, node.content);\n};\n\nexport const italic = (node) => {\n return toNode(\"span\", { class: \"bbcode-i\" }, node.content);\n};\n\nexport const underline = (node) => {\n return toNode(\"span\", { class: \"bbcode-u\" }, node.content);\n};\n\nexport const strike = (node) => {\n return toNode(\"span\", { class: \"bbcode-s\" }, node.content);\n};\n","import { OPEN_BRAKET, CLOSE_BRAKET, SLASH } from '@bbob/plugin-helper';\n// type, value, line, row,\nconst TOKEN_TYPE_ID = 'type'; // 0;\nconst TOKEN_VALUE_ID = 'value'; // 1;\nconst TOKEN_COLUMN_ID = 'row'; // 2;\nconst TOKEN_LINE_ID = 'line'; // 3;\nconst TOKEN_TYPE_WORD = 1; // 'word';\nconst TOKEN_TYPE_TAG = 2; // 'tag';\nconst TOKEN_TYPE_ATTR_NAME = 3; // 'attr-name';\nconst TOKEN_TYPE_ATTR_VALUE = 4; // 'attr-value';\nconst TOKEN_TYPE_SPACE = 5; // 'space';\nconst TOKEN_TYPE_NEW_LINE = 6; // 'new-line';\n/**\n * @param {Token} token\n * @returns {string}\n */ const getTokenValue = (token)=>{\n if (token && typeof token[TOKEN_VALUE_ID] !== 'undefined') {\n return token[TOKEN_VALUE_ID];\n }\n return '';\n};\n/**\n * @param {Token}token\n * @returns {number}\n */ const getTokenLine = (token)=>token && token[TOKEN_LINE_ID] || 0;\nconst getTokenColumn = (token)=>token && token[TOKEN_COLUMN_ID] || 0;\n/**\n * @param {Token} token\n * @returns {boolean}\n */ const isTextToken = (token)=>{\n if (token && typeof token[TOKEN_TYPE_ID] !== 'undefined') {\n return token[TOKEN_TYPE_ID] === TOKEN_TYPE_SPACE || token[TOKEN_TYPE_ID] === TOKEN_TYPE_NEW_LINE || token[TOKEN_TYPE_ID] === TOKEN_TYPE_WORD;\n }\n return false;\n};\n/**\n * @param {Token} token\n * @returns {boolean}\n */ const isTagToken = (token)=>{\n if (token && typeof token[TOKEN_TYPE_ID] !== 'undefined') {\n return token[TOKEN_TYPE_ID] === TOKEN_TYPE_TAG;\n }\n return false;\n};\nconst isTagEnd = (token)=>getTokenValue(token).charCodeAt(0) === SLASH.charCodeAt(0);\nconst isTagStart = (token)=>!isTagEnd(token);\nconst isAttrNameToken = (token)=>{\n if (token && typeof token[TOKEN_TYPE_ID] !== 'undefined') {\n return token[TOKEN_TYPE_ID] === TOKEN_TYPE_ATTR_NAME;\n }\n return false;\n};\n/**\n * @param {Token} token\n * @returns {boolean}\n */ const isAttrValueToken = (token)=>{\n if (token && typeof token[TOKEN_TYPE_ID] !== 'undefined') {\n return token[TOKEN_TYPE_ID] === TOKEN_TYPE_ATTR_VALUE;\n }\n return false;\n};\nconst getTagName = (token)=>{\n const value = getTokenValue(token);\n return isTagEnd(token) ? value.slice(1) : value;\n};\nconst convertTagToText = (token)=>{\n let text = OPEN_BRAKET;\n text += getTokenValue(token);\n text += CLOSE_BRAKET;\n return text;\n};\nclass Token {\n isEmpty() {\n // eslint-disable-next-line no-restricted-globals\n return isNaN(this[TOKEN_TYPE_ID]);\n }\n isText() {\n return isTextToken(this);\n }\n isTag() {\n return isTagToken(this);\n }\n isAttrName() {\n return isAttrNameToken(this);\n }\n isAttrValue() {\n return isAttrValueToken(this);\n }\n isStart() {\n return isTagStart(this);\n }\n isEnd() {\n return isTagEnd(this);\n }\n getName() {\n return getTagName(this);\n }\n getValue() {\n return getTokenValue(this);\n }\n getLine() {\n return getTokenLine(this);\n }\n getColumn() {\n return getTokenColumn(this);\n }\n toString() {\n return convertTagToText(this);\n }\n /**\n * @param {String} type\n * @param {String} value\n * @param line\n * @param row\n */ constructor(type, value, line, row){\n this[TOKEN_TYPE_ID] = Number(type);\n this[TOKEN_VALUE_ID] = String(value);\n this[TOKEN_LINE_ID] = Number(line);\n this[TOKEN_COLUMN_ID] = Number(row);\n }\n}\nexport const TYPE_ID = TOKEN_TYPE_ID;\nexport const VALUE_ID = TOKEN_VALUE_ID;\nexport const LINE_ID = TOKEN_LINE_ID;\nexport const COLUMN_ID = TOKEN_COLUMN_ID;\nexport const TYPE_WORD = TOKEN_TYPE_WORD;\nexport const TYPE_TAG = TOKEN_TYPE_TAG;\nexport const TYPE_ATTR_NAME = TOKEN_TYPE_ATTR_NAME;\nexport const TYPE_ATTR_VALUE = TOKEN_TYPE_ATTR_VALUE;\nexport const TYPE_SPACE = TOKEN_TYPE_SPACE;\nexport const TYPE_NEW_LINE = TOKEN_TYPE_NEW_LINE;\nexport { Token };\nexport default Token;\n","import { QUOTEMARK, BACKSLASH } from '@bbob/plugin-helper';\nfunction CharGrabber(source, options) {\n const cursor = {\n pos: 0,\n len: source.length\n };\n const substrUntilChar = (char)=>{\n const { pos } = cursor;\n const idx = source.indexOf(char, pos);\n return idx >= 0 ? source.substring(pos, idx) : '';\n };\n const includes = (val)=>source.indexOf(val, cursor.pos) >= 0;\n const hasNext = ()=>cursor.len > cursor.pos;\n const isLast = ()=>cursor.pos === cursor.len;\n const skip = (num = 1, silent)=>{\n cursor.pos += num;\n if (options && options.onSkip && !silent) {\n options.onSkip();\n }\n };\n const rest = ()=>source.substring(cursor.pos);\n const grabN = (num = 0)=>source.substring(cursor.pos, cursor.pos + num);\n const curr = ()=>source[cursor.pos];\n const prev = ()=>{\n const prevPos = cursor.pos - 1;\n return typeof source[prevPos] !== 'undefined' ? source[prevPos] : null;\n };\n const next = ()=>{\n const nextPos = cursor.pos + 1;\n return nextPos <= source.length - 1 ? source[nextPos] : null;\n };\n const grabWhile = (cond, silent)=>{\n let start = 0;\n if (hasNext()) {\n start = cursor.pos;\n while(hasNext() && cond(curr())){\n skip(1, silent);\n }\n }\n return source.substring(start, cursor.pos);\n };\n /**\n * @type {skip}\n */ this.skip = skip;\n /**\n * @returns {Boolean}\n */ this.hasNext = hasNext;\n /**\n * @returns {String}\n */ this.getCurr = curr;\n /**\n * @returns {String}\n */ this.getRest = rest;\n /**\n * @returns {String}\n */ this.getNext = next;\n /**\n * @returns {String}\n */ this.getPrev = prev;\n /**\n * @returns {Boolean}\n */ this.isLast = isLast;\n /**\n * @returns {Boolean}\n */ this.includes = includes;\n /**\n * @param {Function} cond\n * @param {Boolean} silent\n * @return {String}\n */ this.grabWhile = grabWhile;\n /**\n * @param {Number} num\n * @return {String}\n */ this.grabN = grabN;\n /**\n * Grabs rest of string until it find a char\n * @param {String} char\n * @return {String}\n */ this.substrUntilChar = substrUntilChar;\n}\n/**\n * Creates a grabber wrapper for source string, that helps to iterate over string char by char\n * @param {String} source\n * @param {Object} options\n * @param {Function} options.onSkip\n * @return CharGrabber\n */ export const createCharGrabber = (source, options)=>new CharGrabber(source, options);\n/**\n * Trims string from start and end by char\n * @example\n * trimChar('*hello*', '*') ==> 'hello'\n * @param {String} str\n * @param {String} charToRemove\n * @returns {String}\n */ export const trimChar = (str, charToRemove)=>{\n while(str.charAt(0) === charToRemove){\n // eslint-disable-next-line no-param-reassign\n str = str.substring(1);\n }\n while(str.charAt(str.length - 1) === charToRemove){\n // eslint-disable-next-line no-param-reassign\n str = str.substring(0, str.length - 1);\n }\n return str;\n};\n/**\n * Unquotes \\\" to \"\n * @param str\n * @return {String}\n */ export const unquote = (str)=>str.replace(BACKSLASH + QUOTEMARK, QUOTEMARK);\nfunction NodeList(values = []) {\n const nodes = values;\n const getLast = ()=>Array.isArray(nodes) && nodes.length > 0 && typeof nodes[nodes.length - 1] !== 'undefined' ? nodes[nodes.length - 1] : null;\n const flushLast = ()=>nodes.length ? nodes.pop() : false;\n const push = (value)=>nodes.push(value);\n const toArray = ()=>nodes;\n this.push = push;\n this.toArray = toArray;\n this.getLast = getLast;\n this.flushLast = flushLast;\n}\n/**\n *\n * @param values\n * @return {NodeList}\n */ export const createList = (values = [])=>new NodeList(values);\n","/* eslint-disable no-plusplus,no-param-reassign */ import { OPEN_BRAKET, CLOSE_BRAKET, QUOTEMARK, BACKSLASH, SLASH, SPACE, TAB, EQ, N } from '@bbob/plugin-helper';\nimport { Token, TYPE_ATTR_NAME, TYPE_ATTR_VALUE, TYPE_NEW_LINE, TYPE_SPACE, TYPE_TAG, TYPE_WORD } from './Token';\nimport { createCharGrabber, trimChar, unquote } from './utils';\n// for cases \nconst EM = '!';\n/**\n * Creates a Token entity class\n * @param {Number} type\n * @param {String} value\n * @param {Number} r line number\n * @param {Number} cl char number in line\n */ const createToken = (type, value, r = 0, cl = 0)=>new Token(type, value, r, cl);\n/**\n * @typedef {Object} Lexer\n * @property {Function} tokenize\n * @property {Function} isTokenNested\n */ /**\n * @param {String} buffer\n * @param {Object} options\n * @param {Function} options.onToken\n * @param {String} options.openTag\n * @param {String} options.closeTag\n * @param {Boolean} options.enableEscapeTags\n * @return {Lexer}\n */ function createLexer(buffer, options = {}) {\n const STATE_WORD = 0;\n const STATE_TAG = 1;\n const STATE_TAG_ATTRS = 2;\n const TAG_STATE_NAME = 0;\n const TAG_STATE_ATTR = 1;\n const TAG_STATE_VALUE = 2;\n let row = 0;\n let col = 0;\n let tokenIndex = -1;\n let stateMode = STATE_WORD;\n let tagMode = TAG_STATE_NAME;\n let contextFreeTag = '';\n const tokens = new Array(Math.floor(buffer.length));\n const openTag = options.openTag || OPEN_BRAKET;\n const closeTag = options.closeTag || CLOSE_BRAKET;\n const escapeTags = !!options.enableEscapeTags;\n const contextFreeTags = options.contextFreeTags || [];\n const onToken = options.onToken || (()=>{});\n const RESERVED_CHARS = [\n closeTag,\n openTag,\n QUOTEMARK,\n BACKSLASH,\n SPACE,\n TAB,\n EQ,\n N,\n EM\n ];\n const NOT_CHAR_TOKENS = [\n openTag,\n SPACE,\n TAB,\n N\n ];\n const WHITESPACES = [\n SPACE,\n TAB\n ];\n const SPECIAL_CHARS = [\n EQ,\n SPACE,\n TAB\n ];\n const isCharReserved = (char)=>RESERVED_CHARS.indexOf(char) >= 0;\n const isNewLine = (char)=>char === N;\n const isWhiteSpace = (char)=>WHITESPACES.indexOf(char) >= 0;\n const isCharToken = (char)=>NOT_CHAR_TOKENS.indexOf(char) === -1;\n const isSpecialChar = (char)=>SPECIAL_CHARS.indexOf(char) >= 0;\n const isEscapableChar = (char)=>char === openTag || char === closeTag || char === BACKSLASH;\n const isEscapeChar = (char)=>char === BACKSLASH;\n const onSkip = ()=>{\n col++;\n };\n const unq = (val)=>unquote(trimChar(val, QUOTEMARK));\n const checkContextFreeMode = (name, isClosingTag)=>{\n if (contextFreeTag !== '' && isClosingTag) {\n contextFreeTag = '';\n }\n if (contextFreeTag === '' && contextFreeTags.includes(name)) {\n contextFreeTag = name;\n }\n };\n const chars = createCharGrabber(buffer, {\n onSkip\n });\n /**\n * Emits newly created token to subscriber\n * @param {Number} type\n * @param {String} value\n */ function emitToken(type, value) {\n const token = createToken(type, value, row, col);\n onToken(token);\n tokenIndex += 1;\n tokens[tokenIndex] = token;\n }\n function nextTagState(tagChars, isSingleValueTag) {\n if (tagMode === TAG_STATE_ATTR) {\n const validAttrName = (char)=>!(char === EQ || isWhiteSpace(char));\n const name = tagChars.grabWhile(validAttrName);\n const isEnd = tagChars.isLast();\n const isValue = tagChars.getCurr() !== EQ;\n tagChars.skip();\n if (isEnd || isValue) {\n emitToken(TYPE_ATTR_VALUE, unq(name));\n } else {\n emitToken(TYPE_ATTR_NAME, name);\n }\n if (isEnd) {\n return TAG_STATE_NAME;\n }\n if (isValue) {\n return TAG_STATE_ATTR;\n }\n return TAG_STATE_VALUE;\n }\n if (tagMode === TAG_STATE_VALUE) {\n let stateSpecial = false;\n const validAttrValue = (char)=>{\n // const isEQ = char === EQ;\n const isQM = char === QUOTEMARK;\n const prevChar = tagChars.getPrev();\n const nextChar = tagChars.getNext();\n const isPrevSLASH = prevChar === BACKSLASH;\n const isNextEQ = nextChar === EQ;\n const isWS = isWhiteSpace(char);\n // const isPrevWS = isWhiteSpace(prevChar);\n const isNextWS = isWhiteSpace(nextChar);\n if (stateSpecial && isSpecialChar(char)) {\n return true;\n }\n if (isQM && !isPrevSLASH) {\n stateSpecial = !stateSpecial;\n if (!stateSpecial && !(isNextEQ || isNextWS)) {\n return false;\n }\n }\n if (!isSingleValueTag) {\n return isWS === false;\n // return (isEQ || isWS) === false;\n }\n return true;\n };\n const name1 = tagChars.grabWhile(validAttrValue);\n tagChars.skip();\n emitToken(TYPE_ATTR_VALUE, unq(name1));\n if (tagChars.isLast()) {\n return TAG_STATE_NAME;\n }\n return TAG_STATE_ATTR;\n }\n const validName = (char)=>!(char === EQ || isWhiteSpace(char) || tagChars.isLast());\n const name2 = tagChars.grabWhile(validName);\n emitToken(TYPE_TAG, name2);\n checkContextFreeMode(name2);\n tagChars.skip();\n // in cases when we has [url=someval]GET[/url] and we dont need to parse all\n if (isSingleValueTag) {\n return TAG_STATE_VALUE;\n }\n const hasEQ = tagChars.includes(EQ);\n return hasEQ ? TAG_STATE_ATTR : TAG_STATE_VALUE;\n }\n function stateTag() {\n const currChar = chars.getCurr();\n const nextChar = chars.getNext();\n chars.skip();\n // detect case where we have '[My word [tag][/tag]' or we have '[My last line word'\n const substr = chars.substrUntilChar(closeTag);\n const hasInvalidChars = substr.length === 0 || substr.indexOf(openTag) >= 0;\n if (isCharReserved(nextChar) || hasInvalidChars || chars.isLast()) {\n emitToken(TYPE_WORD, currChar);\n return STATE_WORD;\n }\n // [myTag ]\n const isNoAttrsInTag = substr.indexOf(EQ) === -1;\n // [/myTag]\n const isClosingTag = substr[0] === SLASH;\n if (isNoAttrsInTag || isClosingTag) {\n const name = chars.grabWhile((char)=>char !== closeTag);\n chars.skip(); // skip closeTag\n emitToken(TYPE_TAG, name);\n checkContextFreeMode(name, isClosingTag);\n return STATE_WORD;\n }\n return STATE_TAG_ATTRS;\n }\n function stateAttrs() {\n const silent = true;\n const tagStr = chars.grabWhile((char)=>char !== closeTag, silent);\n const tagGrabber = createCharGrabber(tagStr, {\n onSkip\n });\n const hasSpace = tagGrabber.includes(SPACE);\n tagMode = TAG_STATE_NAME;\n while(tagGrabber.hasNext()){\n tagMode = nextTagState(tagGrabber, !hasSpace);\n }\n chars.skip(); // skip closeTag\n return STATE_WORD;\n }\n function stateWord() {\n if (isNewLine(chars.getCurr())) {\n emitToken(TYPE_NEW_LINE, chars.getCurr());\n chars.skip();\n col = 0;\n row++;\n return STATE_WORD;\n }\n if (isWhiteSpace(chars.getCurr())) {\n const word = chars.grabWhile(isWhiteSpace);\n emitToken(TYPE_SPACE, word);\n return STATE_WORD;\n }\n if (chars.getCurr() === openTag) {\n if (contextFreeTag) {\n const fullTagLen = openTag.length + SLASH.length + contextFreeTag.length;\n const fullTagName = `${openTag}${SLASH}${contextFreeTag}`;\n const foundTag = chars.grabN(fullTagLen);\n const isEndContextFreeMode = foundTag === fullTagName;\n if (isEndContextFreeMode) {\n return STATE_TAG;\n }\n } else if (chars.includes(closeTag)) {\n return STATE_TAG;\n }\n emitToken(TYPE_WORD, chars.getCurr());\n chars.skip();\n return STATE_WORD;\n }\n if (escapeTags) {\n if (isEscapeChar(chars.getCurr())) {\n const currChar = chars.getCurr();\n const nextChar = chars.getNext();\n chars.skip(); // skip the \\ without emitting anything\n if (isEscapableChar(nextChar)) {\n chars.skip(); // skip past the [, ] or \\ as well\n emitToken(TYPE_WORD, nextChar);\n return STATE_WORD;\n }\n emitToken(TYPE_WORD, currChar);\n return STATE_WORD;\n }\n const isChar = (char)=>isCharToken(char) && !isEscapeChar(char);\n const word1 = chars.grabWhile(isChar);\n emitToken(TYPE_WORD, word1);\n return STATE_WORD;\n }\n const word2 = chars.grabWhile(isCharToken);\n emitToken(TYPE_WORD, word2);\n return STATE_WORD;\n }\n function tokenize() {\n stateMode = STATE_WORD;\n while(chars.hasNext()){\n switch(stateMode){\n case STATE_TAG:\n stateMode = stateTag();\n break;\n case STATE_TAG_ATTRS:\n stateMode = stateAttrs();\n break;\n case STATE_WORD:\n default:\n stateMode = stateWord();\n break;\n }\n }\n tokens.length = tokenIndex + 1;\n return tokens;\n }\n function isTokenNested(token) {\n const value = openTag + SLASH + token.getValue();\n // potential bottleneck\n return buffer.indexOf(value) > -1;\n }\n return {\n tokenize,\n isTokenNested\n };\n}\nexport const createTokenOfType = createToken;\nexport { createLexer };\n","import { TagNode, CLOSE_BRAKET, OPEN_BRAKET, isTagNode } from '@bbob/plugin-helper';\nimport { createLexer } from './lexer';\nimport { createList } from './utils';\n/**\n * @public\n * @param {string} input\n * @param {Object} opts\n * @param {Function} opts.createTokenizer\n * @param {Array} opts.onlyAllowTags\n * @param {Array} opts.contextFreeTags\n * @param {Boolean} opts.enableEscapeTags\n * @param {string} opts.openTag\n * @param {string} opts.closeTag\n * @return {Array}\n */ const parse = (input, opts = {})=>{\n const options = opts;\n const openTag = options.openTag || OPEN_BRAKET;\n const closeTag = options.closeTag || CLOSE_BRAKET;\n const onlyAllowTags = (options.onlyAllowTags || []).filter(Boolean).map((tag)=>tag.toLowerCase());\n let tokenizer = null;\n /**\n * Result AST of nodes\n * @private\n * @type {NodeList}\n */ const nodes = createList();\n /**\n * Temp buffer of nodes that's nested to another node\n * @private\n * @type {NodeList}\n */ const nestedNodes = createList();\n /**\n * Temp buffer of nodes [tag..]...[/tag]\n * @private\n * @type {NodeList}\n */ const tagNodes = createList();\n /**\n * Temp buffer of tag attributes\n * @private\n * @type {NodeList}\n */ const tagNodesAttrName = createList();\n /**\n * Cache for nested tags checks\n * @type Set\n */ const nestedTagsMap = new Set();\n /**\n * @param {Token} token\n * @returns {boolean}\n */ const isTokenNested = (token)=>{\n const value = token.getValue();\n if (!nestedTagsMap.has(value) && tokenizer.isTokenNested && tokenizer.isTokenNested(token)) {\n nestedTagsMap.add(value);\n return true;\n }\n return nestedTagsMap.has(value);\n };\n /**\n * @private\n * @param {string} tagName\n * @returns {boolean}\n */ const isTagNested = (tagName)=>Boolean(nestedTagsMap.has(tagName));\n /**\n * @private\n * @param {string} value\n * @return {boolean}\n */ const isAllowedTag = (value)=>{\n if (onlyAllowTags.length) {\n return onlyAllowTags.indexOf(value.toLowerCase()) >= 0;\n }\n return true;\n };\n /**\n * Flushes temp tag nodes and its attributes buffers\n * @private\n * @return {Array}\n */ const flushTagNodes = ()=>{\n if (tagNodes.flushLast()) {\n tagNodesAttrName.flushLast();\n }\n };\n /**\n * @private\n * @return {Array}\n */ const getNodes = ()=>{\n const lastNestedNode = nestedNodes.getLast();\n if (lastNestedNode && Array.isArray(lastNestedNode.content)) {\n return lastNestedNode.content;\n }\n return nodes.toArray();\n };\n /**\n * @private\n * @param {string|TagNode} node\n * @param {boolean} isNested\n */ const appendNodeAsString = (node, isNested = true)=>{\n const items = getNodes();\n if (Array.isArray(items)) {\n items.push(node.toTagStart({\n openTag,\n closeTag\n }));\n if (node.content.length) {\n node.content.forEach((item)=>{\n items.push(item);\n });\n if (isNested) {\n items.push(node.toTagEnd({\n openTag,\n closeTag\n }));\n }\n }\n }\n };\n /**\n * @private\n * @param {string|TagNode} node\n */ const appendNodes = (node)=>{\n const items = getNodes();\n if (Array.isArray(items)) {\n if (isTagNode(node)) {\n if (isAllowedTag(node.tag)) {\n items.push(node.toTagNode());\n } else {\n appendNodeAsString(node);\n }\n } else {\n items.push(node);\n }\n }\n };\n /**\n * @private\n * @param {Token} token\n */ const handleTagStart = (token)=>{\n flushTagNodes();\n const tagNode = TagNode.create(token.getValue());\n const isNested = isTokenNested(token);\n tagNodes.push(tagNode);\n if (isNested) {\n nestedNodes.push(tagNode);\n } else {\n appendNodes(tagNode, token);\n }\n };\n /**\n * @private\n * @param {Token} token\n */ const handleTagEnd = (token)=>{\n flushTagNodes();\n const lastNestedNode = nestedNodes.flushLast();\n if (lastNestedNode) {\n appendNodes(lastNestedNode, token);\n } else if (typeof options.onError === 'function') {\n const tag = token.getValue();\n const line = token.getLine();\n const column = token.getColumn();\n options.onError({\n message: `Inconsistent tag '${tag}' on line ${line} and column ${column}`,\n tagName: tag,\n lineNumber: line,\n columnNumber: column\n });\n }\n };\n /**\n * @private\n * @param {Token} token\n */ const handleTag = (token)=>{\n // [tag]\n if (token.isStart()) {\n handleTagStart(token);\n }\n // [/tag]\n if (token.isEnd()) {\n handleTagEnd(token);\n }\n };\n /**\n * @private\n * @param {Token} token\n */ const handleNode = (token)=>{\n /**\n * @type {TagNode}\n */ const lastTagNode = tagNodes.getLast();\n const tokenValue = token.getValue();\n const isNested = isTagNested(token);\n if (lastTagNode) {\n if (token.isAttrName()) {\n tagNodesAttrName.push(tokenValue);\n lastTagNode.attr(tagNodesAttrName.getLast(), '');\n } else if (token.isAttrValue()) {\n const attrName = tagNodesAttrName.getLast();\n if (attrName) {\n lastTagNode.attr(attrName, tokenValue);\n tagNodesAttrName.flushLast();\n } else {\n lastTagNode.attr(tokenValue, tokenValue);\n }\n } else if (token.isText()) {\n if (isNested) {\n lastTagNode.append(tokenValue);\n } else {\n appendNodes(tokenValue);\n }\n } else if (token.isTag()) {\n // if tag is not allowed, just past it as is\n appendNodes(token.toString());\n }\n } else if (token.isText()) {\n appendNodes(tokenValue);\n } else if (token.isTag()) {\n // if tag is not allowed, just past it as is\n appendNodes(token.toString());\n }\n };\n /**\n * @private\n * @param {Token} token\n */ const onToken = (token)=>{\n if (token.isTag()) {\n handleTag(token);\n } else {\n handleNode(token);\n }\n };\n tokenizer = (opts.createTokenizer ? opts.createTokenizer : createLexer)(input, {\n onToken,\n openTag,\n closeTag,\n onlyAllowTags: options.onlyAllowTags,\n contextFreeTags: options.contextFreeTags,\n enableEscapeTags: options.enableEscapeTags\n });\n // eslint-disable-next-line no-unused-vars\n const tokens = tokenizer.tokenize();\n // handles situations where we open tag, but forgot close them\n // for ex [q]test[/q][u]some[/u][q]some [u]some[/u] // forgot to close [/q]\n // so we need to flush nested content to nodes array\n const lastNestedNode = nestedNodes.flushLast();\n if (lastNestedNode && isTagNested(lastNestedNode.tag)) {\n appendNodeAsString(lastNestedNode, false);\n }\n return nodes.toArray();\n};\nexport { parse };\nexport default parse;\n","/* eslint-disable no-plusplus */ const isObj = (value)=>typeof value === 'object';\nconst isBool = (value)=>typeof value === 'boolean';\nexport function iterate(t, cb) {\n const tree = t;\n if (Array.isArray(tree)) {\n for(let idx = 0; idx < tree.length; idx++){\n tree[idx] = iterate(cb(tree[idx]), cb);\n }\n } else if (tree && isObj(tree) && tree.content) {\n iterate(tree.content, cb);\n }\n return tree;\n}\nexport function same(expected, actual) {\n if (typeof expected !== typeof actual) {\n return false;\n }\n if (!isObj(expected) || expected === null) {\n return expected === actual;\n }\n if (Array.isArray(expected)) {\n return expected.every((exp)=>[].some.call(actual, (act)=>same(exp, act)));\n }\n return Object.keys(expected).every((key)=>{\n const ao = actual[key];\n const eo = expected[key];\n if (isObj(eo) && eo !== null && ao !== null) {\n return same(eo, ao);\n }\n if (isBool(eo)) {\n return eo !== (ao === null);\n }\n return ao === eo;\n });\n}\nexport function match(expression, cb) {\n return Array.isArray(expression) ? iterate(this, (node)=>{\n for(let idx = 0; idx < expression.length; idx++){\n if (same(expression[idx], node)) {\n return cb(node);\n }\n }\n return node;\n }) : iterate(this, (node)=>same(expression, node) ? cb(node) : node);\n}\n","import { parse } from '@bbob/parser';\nimport { iterate, match } from './utils';\nfunction walk(cb) {\n return iterate(this, cb);\n}\nexport default function bbob(plugs) {\n const plugins = typeof plugs === 'function' ? [\n plugs\n ] : plugs || [];\n let options = {\n skipParse: false\n };\n return {\n process (input, opts) {\n options = opts || {};\n const parseFn = options.parser || parse;\n const renderFn = options.render;\n const data = options.data || null;\n if (typeof parseFn !== 'function') {\n throw new Error('\"parser\" is not a function, please pass to \"process(input, { parser })\" right function');\n }\n let tree = options.skipParse ? input || [] : parseFn(input, options);\n // raw tree before modification with plugins\n const raw = tree;\n tree.messages = [];\n tree.options = options;\n tree.walk = walk;\n tree.match = match;\n plugins.forEach((plugin)=>{\n tree = plugin(tree, {\n parse: parseFn,\n render: renderFn,\n iterate,\n match,\n data\n }) || tree;\n });\n return {\n get html () {\n if (typeof renderFn !== 'function') {\n throw new Error('\"render\" function not defined, please pass to \"process(input, { render })\"');\n }\n return renderFn(tree, tree.options);\n },\n tree,\n raw,\n messages: tree.messages\n };\n }\n };\n}\n","import core from '@bbob/core';\nimport { attrsToString } from '@bbob/plugin-helper';\nconst SELFCLOSE_END_TAG = '/>';\nconst CLOSE_START_TAG = '';\nconst renderNode = (node, { stripTags =false })=>{\n if (!node) return '';\n const type = typeof node;\n if (type === 'string' || type === 'number') {\n return node;\n }\n if (type === 'object') {\n if (stripTags === true) {\n // eslint-disable-next-line no-use-before-define\n return renderNodes(node.content, {\n stripTags\n });\n }\n if (node.content === null) {\n return [\n START_TAG,\n node.tag,\n attrsToString(node.attrs),\n SELFCLOSE_END_TAG\n ].join('');\n }\n // eslint-disable-next-line no-use-before-define\n return [\n START_TAG,\n node.tag,\n attrsToString(node.attrs),\n END_TAG,\n renderNodes(node.content),\n CLOSE_START_TAG,\n node.tag,\n END_TAG\n ].join('');\n }\n if (Array.isArray(node)) {\n // eslint-disable-next-line no-use-before-define\n return renderNodes(node, {\n stripTags\n });\n }\n return '';\n};\nconst renderNodes = (nodes, { stripTags =false } = {})=>[].concat(nodes).reduce((r, node)=>r + renderNode(node, {\n stripTags\n }), '');\nconst toHTML = (source, plugins, options)=>core(plugins).process(source, {\n ...options,\n render: renderNodes\n }).html;\nexport const render = renderNodes;\nexport default toHTML;\n","/**\n * Plugin that converts consecutive normal spaces (U+0020) to non-breaking spaces (U+00A0).\n * To use, put as function similar to the presets.\n *\n *\n * @example\n * ```ts\n * const output = bbob([preset(), , preserveWhitespace(), lineBreakPlugin()]).process(input, {render}).html\n * ```\n */\nimport { isStringNode } from \"@bbob/plugin-helper\";\n\n/**\n * Checks if input is an object\n * @param value input\n * @returns if value is an object\n */\nconst isObj = (value) => typeof value === \"object\";\n\n/**\n * Walks the tree of nodes. Checks for node of consecutive spaces. If found replaces every space in\n * node with a nonbreaking space.\n * Preserves multiple spaces so html won't truncate them.\n *\n * Walks through entire tree.\n * @param t tree of nodes to be processed\n * @returns modified tree\n */\nconst walk = (t) => {\n const tree = t;\n\n if (Array.isArray(tree)) {\n for (let idx = 0; idx < tree.length; idx++) {\n const child = walk(tree[idx]);\n if (Array.isArray(child)) {\n tree.splice(idx, 1, ...child);\n idx += child.length - 1;\n } else {\n tree[idx] = child;\n }\n }\n } else if (tree && isObj(tree) && tree.content) {\n walk(tree.content);\n }\n\n //Bbob breaks up nodes by the presence of normal spaces.\n //So a node with a normal space can only have normal spaces in that node.\n if (isStringNode(tree)) {\n if (tree.length > 1 && tree[0] === \" \") {\n let numSpaces = tree.length;\n return [String.fromCharCode(160).repeat(numSpaces)];\n }\n }\n\n return tree;\n};\n\n/**\n * Converts consecutive normal spaces (U+0020) to nonbreaking spaces (U+00A0).\n * Supply this as a plugin in the preset lists.\n *\n * @example converts consecutive normal spaces (U+0020) to nonbreaking spaces (U+00A0)\n * ```ts\n * const output = bbob([preset(), preserveWhitespace(), lineBreakPlugin()]).process(input, {render}).html\n * ```\n *\n * @returns plugin to be used in BBob process\n */\nexport const preserveWhitespace = () => {\n return (tree) => walk(tree);\n};\n","import { MD_NEWLINE_INJECT, MD_NEWLINE_INJECT_COMMENT, MD_NEWLINE_PRE_INJECT } from \"./common\";\n\n/**\n * Post Processing designed to fix issues with Markdown and BBCode that the parser can't fix.\n *\n * Separate from markdown-it post processing as it'll be able to manipulate the full string.\n * @param {string} raw string from processing through both BBCode and Markdown\n * @returns post processed string\n */\nfunction removeNewlineInjects(raw) {\n const processed = raw\n .replaceAll(MD_NEWLINE_INJECT, \"\")\n .replaceAll(MD_NEWLINE_PRE_INJECT, \"\")\n .replaceAll(\"\\n\" + MD_NEWLINE_INJECT_COMMENT, \"\")\n .replaceAll(MD_NEWLINE_INJECT_COMMENT + \"\\n\", \"\")\n .replaceAll(MD_NEWLINE_INJECT_COMMENT, \"\"); // Remove all instances of the injected newline\n return processed;\n}\n\n/**\n * Injects hoisted code blocks back into the raw string\n * @param {string} raw input to inject hoisted code blocks into\n * @param {any} data contains hoist map\n * @returns string with hoisted code blocks injected\n */\nfunction renderHoistedCodeBlocks(raw, data) {\n const hoistMap = data.hoistMap;\n for (const [uuid, content] of Object.entries(hoistMap)) {\n raw = raw.replaceAll(uuid, content);\n }\n return raw;\n}\n\n/**\n * Setups the class style tag template for the post\n * @param {string} raw\n * @param {{styles: string[]}} data - contains styles array\n * @returns string\n */\nfunction createClassStyleTagTemplate(raw, data) {\n if (data.styles.length === 0) {\n return raw;\n }\n const template = '\";\n return template + raw;\n}\n\n/**\n * Setups the script tag template for the post\n * @param {string} raw\n * @param {{\n * bbscripts: {\n * id: string,\n * class: string,\n * on: string,\n * version: string,\n * content: string\n * }[]}} data - contains scripts array\n * @returns string\n */\nfunction createScriptTagTemplate(raw, data) {\n if (data.bbscripts.length === 0) {\n return raw;\n }\n const templates = data.bbscripts.map(\n (s) =>\n ``,\n );\n return templates.join(\"\") + raw;\n}\n\n/**\n * Performs post processing on the raw string to address any necessary functionality that BBob/MD can't handle with a plugin (i.e. hoisting).\n * @param {string} raw processed input from after bbob and md\n * @param {any} data from bbob data\n * @returns final processed string\n */\nexport function postprocess(raw, data) {\n let final = raw;\n const postprocessors = [\n removeNewlineInjects,\n createClassStyleTagTemplate,\n createScriptTagTemplate,\n renderHoistedCodeBlocks,\n ];\n for (const postprocessor of postprocessors) {\n final = postprocessor(final, data);\n }\n return final;\n}\n","/**\n * Plugin that converts line breaks to `
` tags.\n * To use, put as function similar to the presets.\n *\n * If a node is marked with `noLineBreakConversion`, then it'll skip the parsing the children\n *\n * @example\n * ```ts\n * const output = bbob([preset(), lineBreakPlugin()]).process(input, {render}).html\n * ```\n */\nimport { isEOL } from \"@bbob/plugin-helper\";\nimport { MD_NEWLINE_INJECT, MD_NEWLINE_PRE_INJECT, URL_REGEX_SINGLE_LINE } from \"../utils/common\";\n\nconst isObj = (value) => typeof value === \"object\";\nconst isString = (value) => typeof value === \"string\";\n\n/**\n * Walks the tree of nodes. Will add `br` tag to all `\\n` in format that can be used in any renderer.\n * Preserves \\n so that markdown-it doesn't try to treat everything like a block\n *\n * If a node has the property noLineBreakConversion is encountered, will skip parsing children.\n * @param t tree of nodes to be processed\n * @returns modified tree\n */\nconst walk = (t, disableLineBreakConversion = false) => {\n const tree = t;\n\n if (Array.isArray(tree)) {\n if (tree.some(isString)) {\n // array contains strings. Might be md compatible\n tree.unshift(MD_NEWLINE_INJECT);\n tree.push(MD_NEWLINE_INJECT);\n }\n for (let idx = 0; idx < tree.length; idx++) {\n const child = walk(tree[idx], disableLineBreakConversion);\n if (Array.isArray(child)) {\n tree.splice(idx, 1, ...child);\n idx += child.length - 1;\n } else {\n tree[idx] = child;\n }\n }\n } else if (tree && isObj(tree) && tree.content) {\n if (tree.isWhitespaceSensitive) {\n // applies only to [code] and [icode]\n // stop walk. children won't be parsed to have
\n return tree.tag ? tree : tree.content;\n }\n if (tree.disableLineBreakConversion) {\n disableLineBreakConversion = true;\n }\n walk(tree.content, disableLineBreakConversion);\n return tree.tag ? tree : tree.content;\n } else if (isString(tree) && URL_REGEX_SINGLE_LINE.test(tree.trim())) {\n // if the entire string is a URL, then it should be prepared for onebox.\n // BBob separates strings by newlines anyway, so we can already assume this is sitting on its own line\n // MD_NEWLINE_INJECT is already replacing newline came before or the start of the array,\n // so we only need to make sure \\n\\n is added after the URL\n return [tree, MD_NEWLINE_PRE_INJECT];\n }\n\n if (isString(tree) && isEOL(tree)) {\n return disableLineBreakConversion\n ? [\"\\n\", MD_NEWLINE_INJECT]\n : [{ tag: \"br\", content: null }, MD_NEWLINE_INJECT];\n }\n\n return tree;\n};\n\n/**\n * Converts `\\n` to `
` self closing tag. Supply this as the last plugin in the preset lists\n *\n * @example converts all line breaks to br\n * ```ts\n * const output = bbob([preset(), lineBreakPlugin()]).process(input, {render}).html\n * ```\n * @example will not convert line breaks inside [nobr]\n * ```ts\n * const nobr = (node: TagNode) => {return { disableLineBreakConversion: true, content: node.content }}; \\\\ tag in preset\n * ...\n * const output = bbob([preset(), lineBreakPlugin()]).process(input, {render}).html\n * ```\n * @returns plugin to be used in BBob process\n */\nexport const lineBreakPlugin = () => {\n return (tree) => walk(tree);\n};\n","import { ESCAPABLES_REGEX, MD_TABLE_REGEX, generateGUID, regexIndexOf } from \"./common\";\n\n/**\n * Find all code blocks and hoist them out of the content and into a map for later insertion\n * @param {string} raw input to preprocess\n * @returns processed string and hoist map\n */\nfunction fenceCodeBlockPreprocess(content, data) {\n /** @type {Object.} */\n const hoistMap = {};\n let index = 0;\n\n const addHoistAndReturnNewStartPoint = (cutOffStart, cutOffEnd, expected, trim = false) => {\n const uuid = generateGUID();\n if (cutOffEnd !== -1) {\n hoistMap[uuid] = content.substring(cutOffStart, cutOffEnd);\n content = content.substring(0, cutOffStart) + uuid + content.substring(cutOffEnd);\n } else {\n hoistMap[uuid] = content.substring(cutOffStart);\n content = content.substring(0, cutOffStart) + uuid + expected;\n }\n if (trim) {\n if (hoistMap[uuid].startsWith(\"\\n\")) {\n hoistMap[uuid] = hoistMap[uuid].substring(1);\n }\n if (hoistMap[uuid].endsWith(\"\\n\")) {\n hoistMap[uuid] = hoistMap[uuid].substring(0, hoistMap[uuid].length - 1);\n }\n }\n return cutOffStart + uuid.length + expected.length;\n };\n\n while ((index = regexIndexOf(content, ESCAPABLES_REGEX, index)) !== -1) {\n const match = ESCAPABLES_REGEX.exec(content.substring(index));\n if (match.groups?.fence) {\n const fence = match.groups.fence;\n const fenceInfo = match.groups.fenceInfo;\n if (content[index] === \"\\n\") {\n // Check if the fence is not at the start of the content\n index += 1;\n }\n const closingFenceRegex = new RegExp(\"\\n\" + fence + \"(\\n|$)\"); // Find the next fence. By commonmark spec, it should be the same fence length and type\n const nextIndex = regexIndexOf(content, closingFenceRegex, index + fence.length);\n\n const uuid = generateGUID();\n if (nextIndex !== -1) {\n hoistMap[uuid] = content.substring(index + fence.length + fenceInfo.length, nextIndex);\n } else {\n hoistMap[uuid] = content.substring(index + fence.length + fenceInfo.length);\n }\n // inject bbcode tag before and after the code block. This is to prevent BBob plugin from injecting newlines\n const replacement = `[saveNL]\\n${fence}${fenceInfo}${uuid}\\n${fence}\\n[/saveNL]`;\n content =\n content.substring(0, index) +\n replacement +\n (nextIndex !== -1 ? content.substring(nextIndex + 1 + fence.length) : \"\");\n index = index + replacement.length;\n } else if (match.groups?.bbcode) {\n const bbcode = match.groups.bbcode;\n const bbcodeTag = match.groups.bbcodeTag.toLowerCase(); // coerce to lowercase for caseinsensitive matching\n const closingTag = `[/${bbcodeTag}]`;\n const nextIndex = content.toLowerCase().indexOf(closingTag, index + 1);\n index = addHoistAndReturnNewStartPoint(index + bbcode.length, nextIndex, closingTag, true);\n } else if (match.groups.backtick) {\n const backtick = match.groups.backtick; // contains whole content\n const tickStart = match.groups.tickStart;\n const tickEnd = match.groups.tickEnd;\n index = addHoistAndReturnNewStartPoint(\n index + tickStart.length,\n index + backtick.length - tickEnd.length,\n tickEnd,\n );\n }\n }\n\n data.hoistMap = hoistMap;\n return [content, data];\n}\n\n/**\n * Find all markdown table blocks and mark them to ignore newlines\n * @param {string} raw input to preprocess\n * @returns processed string\n */\nfunction mdTableBlockPreprocess(content, data) {\n let index = 0;\n while ((index = regexIndexOf(content, MD_TABLE_REGEX, index)) !== -1) {\n const match = MD_TABLE_REGEX.exec(content.substring(index));\n const table = match[0];\n const replacement = `[saveNL]\\n${table}\\n[/saveNL]`;\n content = content.substring(0, index) + replacement + content.substring(index + table.length);\n index = index + replacement.length;\n }\n return [content, data];\n}\n\n/**\n * Preprocesses input to be formatted for bbob to intake. Handles any necessary functionality that BBob can't handle with a plugin (i.e. hoisting).\n * @param {string} raw input to preprocess\n * @returns formatted input for bbob to intake\n */\nexport function preprocessRaw(raw) {\n let data = {};\n const preprocessors = [fenceCodeBlockPreprocess, mdTableBlockPreprocess];\n for (const preprocessor of preprocessors) {\n [raw, data] = preprocessor(raw, data);\n }\n return [raw, data];\n}\n","import { availableTags, preset, preventParsing } from \"./preset\";\nimport bbob from \"@bbob/core\";\nimport { render } from \"@bbob/html\";\nimport { preserveWhitespace } from \"./plugins/preserveWhitespace\";\nimport { postprocess } from \"./utils/postprocess\";\nimport { lineBreakPlugin } from \"./plugins/lineBreak\";\nimport { preprocessRaw } from \"./utils/preprocess\";\n\nconst options = {\n onlyAllowTags: [...availableTags],\n contextFreeTags: preventParsing, // prevent parsing of children\n enableEscapeTags: true,\n onError: (err) => {\n if (options.previewing) {\n // eslint-disable-next-line no-console\n console.warn(err.message, err.lineNumber, err.columnNumber);\n }\n },\n};\nconst presetTags = preset();\n\nexport const RpNBBCode = (code, opts) => {\n const plugins = [presetTags];\n if (opts.preserveWhitespace) {\n plugins.push(preserveWhitespace());\n }\n plugins.push(lineBreakPlugin());\n const [preprocessed, preprocessedData] = preprocessRaw(code);\n return bbob(plugins).process(preprocessed, {\n render,\n ...options,\n data: {\n ...preprocessedData,\n previewing: opts.previewing,\n fonts: new Set(),\n styles: [],\n bbscripts: [],\n },\n });\n};\n\nexport { postprocess };\n"],"names":["isTagNode","el","tag","process","tags","tree","core","options","walk","node","toNode","attrs","content","gen","preprocessAttr","keys","Object","join","vals","values","_default","toOriginalStartTag","toTagStart","regexIndexOf","string","regex","startpos","indexOf","substring","search","MD_NEWLINE_INJECT","MD_NEWLINE_PRE_INJECT","MD_NEWLINE_INJECT_COMMENT","URL_REGEX_SINGLE_LINE","RegExp","source","ESCAPABLES_REGEX","MD_TABLE_REGEX","generateGUID","d","Date","getTime","window","performance","now","replace","c","r","Math","random","floor","toString","alignment","left","class","center","right","anchor","a","id","trim","name","goto","href","WEB_FONTS","VALID_FONT_STYLES","thin","extralight","light","regular","medium","semibold","bold","extrabold","black","REGISTERED_AXIS","AXES_REGEX","emailHeader","emailFooter","rowcolumn","row","column","columnAttrs","columnStyle","startsWith","ACCEPTED_OPTIONS","textmessage","attr","recipient","message","option","toLowerCase","includes","N","TAB","EQ","QUOTEMARK","SPACE","OPEN_BRAKET","CLOSE_BRAKET","SLASH","BACKSLASH","isStringNode","keysReduce","obj","reduce","def","getNodeLength","count","contentNode","length","escapeHTML","value","attrValue","type","types","boolean","number","object","JSON","stringify","attrsToString","arr","key","getTagAttrs","params","uniqAattr","res","tagAttr","TagNode","this","append","push","appendToNode","openTag","closeTag","toTagEnd","toTagNode","isEmpty","tagStart","constructor","Array","isArray","create","isOf","SLIDE_TITLE_OPEN","Symbol","SLIDE_TITLE_CLOSE","SLIDE_CLOSE","SLIDE_REGEX","markerToString","marker","accordionTags","accordion","groupId","markedContent","contentArr","newArr","shift","foundIndex","match","preContent","slice","postContent","groups","slideTitleOpen","slideTitleClose","slideClose","generateSlideMarkersFromContent","generatedSlides","nodes","currentSlide","prevMarker","customTitle","generateSlidesFromMarkers","filteredContent","filter","n","map","isValid","customSettings","split","s","bright","bcenter","bleft","fleft","fright","some","endsWith","width","find","classes","style","slide","title","isOpen","open","titleAlign","possibleOptions","t","EVENTS","animation","data","previewing","commonGUID","commonId","keyframes","ident","cleanContent","replaceAll","formatted","styles","bg","color","block","defaultOp","blockAttr","blockOption","blockquote","author","border","val","br","centerblock","percentageInput","check","nameAttr","classSuffix","className","selector","mediaQuery","state","minWidth","maxWidth","unshift","code","isWhitespaceSensitive","inputColor","comment","div","classAttrs","classNames","divide","fieldset","font","fontFamily","family","axes","ital","wght","matches","exec","italic","weight","named_weight","fromEntries","entries","axesParser","url","sort","googleFontApiBuild","fonts","add","custom","fontVar","h","h1","h2","h3","h4","h5","h6","heightrestrict","heightInput","heightValue","parsedHeight","parseHeight","highlight","icode","imagefloat","inlinespoiler","justify","keyframe","mail","attributes","mailAttr","mailOption","person","subject","newspaper","nobr","disableLineBreakConversion","note","ooc","pindent","plain","print","printAttr","printOption","progress","percentageInt","thinprogress","savenl","sh","script","onEvent","on","scriptSetup","version","bbscripts","scroll","side","size","fontSize","fontValue","valid","parsedSize","sizeRanges","unit","parseFontSize","outputAttr","spoiler","providedTitle","sub","sup","tab","tabId","checked","for","tabs","tabsList","forEach","tabNode","b","i","u","availableTags","preset","createPreset","defTags","processor","presetFactory","opts","assign","presetExecutor","extend","callback","TOKEN_TYPE_ID","TOKEN_VALUE_ID","TOKEN_LINE_ID","getTokenValue","token","isTagEnd","charCodeAt","Token","isNaN","isText","isTag","isAttrName","isAttrValue","isStart","isEnd","getName","getTagName","getValue","getLine","getColumn","text","convertTagToText","line","Number","String","CharGrabber","cursor","pos","len","hasNext","skip","num","silent","onSkip","curr","getCurr","getRest","getNext","nextPos","getPrev","prevPos","isLast","grabWhile","cond","start","grabN","substrUntilChar","char","idx","createCharGrabber","NodeList","toArray","getLast","flushLast","pop","createList","createLexer","buffer","STATE_WORD","STATE_TAG","STATE_TAG_ATTRS","TAG_STATE_NAME","TAG_STATE_ATTR","TAG_STATE_VALUE","col","tokenIndex","stateMode","tagMode","contextFreeTag","tokens","escapeTags","enableEscapeTags","contextFreeTags","onToken","RESERVED_CHARS","NOT_CHAR_TOKENS","WHITESPACES","SPECIAL_CHARS","isCharReserved","isNewLine","isWhiteSpace","isCharToken","isSpecialChar","isEscapableChar","isEscapeChar","unq","str","charToRemove","charAt","trimChar","checkContextFreeMode","isClosingTag","chars","emitToken","cl","createToken","nextTagState","tagChars","isSingleValueTag","validAttrName","isValue","stateSpecial","validAttrValue","isQM","prevChar","nextChar","isPrevSLASH","isNextEQ","isWS","isNextWS","name1","name2","stateTag","currChar","substr","hasInvalidChars","isNoAttrsInTag","stateAttrs","tagStr","tagGrabber","hasSpace","stateWord","fullTagLen","fullTagName","isChar","tokenize","isTokenNested","parse","input","onlyAllowTags","Boolean","tokenizer","nestedNodes","tagNodes","tagNodesAttrName","nestedTagsMap","Set","isTagNested","tagName","has","flushTagNodes","getNodes","lastNestedNode","appendNodeAsString","isNested","items","item","appendNodes","handleTagStart","tagNode","handleTag","onError","lineNumber","columnNumber","handleTagEnd","createTokenizer","lastTagNode","tokenValue","attrName","handleNode","isObj","isBool","iterate","cb","same","expected","actual","every","exp","call","act","ao","eo","expression","SELFCLOSE_END_TAG","CLOSE_START_TAG","START_TAG","END_TAG","renderNodes","stripTags","concat","renderNode","render","child","splice","numSpaces","fromCharCode","repeat","removeNewlineInjects","raw","renderHoistedCodeBlocks","hoistMap","uuid","createClassStyleTagTemplate","createScriptTagTemplate","isString","test","fenceCodeBlockPreprocess","index","addHoistAndReturnNewStartPoint","cutOffStart","cutOffEnd","fence","fenceInfo","closingFenceRegex","nextIndex","replacement","bbcode","closingTag","bbcodeTag","backtick","tickStart","tickEnd","mdTableBlockPreprocess","table","err","console","warn","presetTags","plugins","preserveWhitespace","preprocessed","preprocessedData","preprocessors","preprocessor","preprocessRaw","plugs","skipParse","parseFn","parser","renderFn","Error","messages","plugin","html","bbob","final","postprocessors","postprocessor"],"mappings":"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","x_google_ignoreList":[0,10,11,12,50,51,52,53,54,55,56]} \ No newline at end of file