From bdef4b446463d71fe453e80656067b69dcb23795 Mon Sep 17 00:00:00 2001 From: Max <9123069+Mondrethos@users.noreply.github.com> Date: Tue, 22 Sep 2026 16:59:38 -0500 Subject: [PATCH] FIX: Integrate BBCode through the native Markdown pipeline --- INTEGRATION_NOTES.md | 101 ++++ README.md | 40 +- assets/bundled/bbcode-parser.min.js | 2 +- assets/bundled/bbcode-parser.min.js.map | 2 +- .../discourse/api-initializers/fontawesome.js | 2 +- .../discourse/api-initializers/google-font.js | 8 +- .../discourse/api-initializers/highlight.js | 4 + .../initializers/bbcode-parser-init.js | 15 - .../lib/discourse-markdown/bbcode-plugin.js | 180 +++---- bbcode-src/index.js | 38 +- bbcode-src/utils/common.js | 19 +- bbcode-src/utils/postprocess.js | 13 +- package.json | 1 + plugin.rb | 11 +- pnpm-lock.yaml | 3 + spec/lib/pretty_text_spec.rb | 457 ++++++++++++++++++ test/javascripts/lib/bbcode-cooked-test.js | 68 +++ 17 files changed, 813 insertions(+), 151 deletions(-) create mode 100644 INTEGRATION_NOTES.md delete mode 100644 assets/javascripts/discourse/initializers/bbcode-parser-init.js create mode 100644 spec/lib/pretty_text_spec.rb create mode 100644 test/javascripts/lib/bbcode-cooked-test.js diff --git a/INTEGRATION_NOTES.md b/INTEGRATION_NOTES.md new file mode 100644 index 0000000..10660f9 --- /dev/null +++ b/INTEGRATION_NOTES.md @@ -0,0 +1,101 @@ +# Discourse integration notes + +## Rendering model + +BBCode is registered through Discourse's Markdown plugin pipeline, using the +same bundled BBob parser for server cooking and browser previews. A markdown-it +core rule runs before normalization and detects whether the source needs custom +BBCode processing. The `bbcode_enabled` setting and Markdown feature overrides +control whether that rule is registered, including restricted cooks such as chat. + +Posts without custom BBCode retain native Markdown paragraphs, headings, lists, +tables, whitespace, and code blocks. Discourse's `[b]`, `[i]`, `[u]`, and `[s]` +tags also stay native on that path. BBCode shown only inside native Markdown +code examples does not enable layout processing. + +For a post containing custom BBCode, BBob processes the source before Markdown. +That whole post retains the plugin's legacy whitespace conventions: paragraph +wrappers are suppressed and indented layout markup is not treated as a native +indented code block. These differences apply to surrounding Markdown too; this +implementation does not isolate processing to individual BBCode spans. Core +inline tags nested in those posts use the BBob preset handlers. + +A renderer extension restores content hoisted by the parser, including code +examples and custom styles. Restored code text is escaped, so HTML inside +`[code]` or `[plain]` appears as source. Allowed HTML outside code examples still +uses Discourse's normal rendering and sanitization. The integration does not +replace Discourse's engine initialization or bypass its sanitizer. + +## Imported source + +- Use native Markdown lists and tables. This change adds no XenForo `[list]`, + `[*]`, `[table]`, `[tr]`, `[td]`, `[th]`, or `[tf]` handlers. Convert unsupported + imported syntax while preserving its contents; inspect complex tables individually. +- Markdown headings remain active inside layout tags such as `[div]` and + `[nobr]`. Escape a line's leading hash as `\# label` when it is literal text. + Rebaking cannot determine the author's original intent. +- This change adds no automatic repair of missing or misnested closing tags. + Malformed source can lose content during parsing; correct it before relying + on a rebake to reproduce the original layout. + +Use the composer's Markdown mode for BBCode. This change does not add a rich-text +BBCode editor. External fonts, images, and optional Font Awesome kits still need +available resources and their normal configuration. + +## Existing safeguards and behavior + +The current CSS containment rules, cross-process parser reset, spoiler fix, and +rehosting of external image URLs used in BBCode CSS are retained. Initializers +for fonts, icons, and BBCode highlighting respect the plugin's enabled setting. + +Containment and Discourse's sanitizer serve different purposes; this integration +does not establish a sandbox for arbitrary user HTML, CSS, or scripts. BBScript +is optional and remains disabled by default. Expanding that interpreter or +introducing unrestricted HTML requires a separate security review. + +## Deployment and rebaking + +1. After changing `bbcode-src`, rebuild and include the generated bundle and + source map: + + ```sh + pnpm install --frozen-lockfile + pnpm build + ``` + +2. Deploy the full plugin. Restart Discourse web processes and Sidekiq so both + use the new renderer. Restart the frontend development build when adding or + removing plugin modules. +3. Correct unsupported or malformed imported source, then compare representative + posts in the composer preview and after saving. +4. Rebuild saved posts on the destination server from its Discourse environment: + + ```sh + RAILS_ENV=production bundle exec rake posts:rebake + ``` + + For imported posts, follow the + [importer's verified rebake instructions](https://github.com/RpNation/discourse_xf_importer#readme), + using a fresh run ID after renderer changes. A previously completed checkpoint + does not establish that posts have been rebuilt with the new renderer. +5. Check failed post IDs and Sidekiq retry/dead jobs, allow background processing + to finish, and inspect representative layouts. Command completion does not + prove that every imported design renders faithfully. + +Run full imports and site-wide rebakes on the migration servers, not on the +local development computer. + +## Focused validation + +Inside a Discourse checkout with this plugin installed and assets built: + +```sh +LOAD_PLUGINS=1 bin/rspec plugins/bbcode/spec/lib/pretty_text_spec.rb +bin/qunit --target bbcode --filter BBCode +``` + +Check native Markdown and code examples, mixed BBCode layouts, enabled/disabled +settings, sanitization, and preview/save parity. Run lint for changed files and +review desktop and mobile layouts. Synthetic tests and browser emulation do not +establish that all historical designs or Safari/iOS render correctly; retain a +visual review of representative imported posts after source conversion. diff --git a/README.md b/README.md index 7db9050..d6d5f5c 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,18 @@ RpNation's Official BBCode Implementation for Discourse -See: [https://www.rpnation.com] +See [RpNation](https://www.rpnation.com). -The goal of this repo and plugin is to provide users with the BBCode suite that they have grown accustomed to when it comes to using our site before our migration to Discourse and make sure that old posts rebake correctly. Above in the chart is marked our status on each BBCode which will hopefully co-exist in tandom even with the markdown/htlm versions provided in the box experience by the Discourse Software. +This plugin adds RpNation's custom BBCode to Discourse while using native +Discourse formatting where it already exists. Use Markdown for lists and tables, +and Discourse's own inline bold, italic, underline, and strikethrough tags. +Custom layout tags remain available for designs that need them. + +Imported XenForo posts may need source conversion before rebaking: this plugin +does not add XenForo list/table syntax or automatically repair malformed tags. +Markdown headings also work inside layout tags; write `\# label` when the hash +should be literal. See the [integration notes](INTEGRATION_NOTES.md) for migration +and rendering boundaries. ## Features/Planned @@ -52,7 +61,8 @@ The goal of this repo and plugin is to provide users with the BBCode suite that - [x] Sides - [x] Tabs - [x] Accordions -- [x] ~~Tables~~ now using markdown tables +- [x] Native Discourse Markdown tables +- [x] Native Discourse numbered and bulleted lists - [x] Center Block - [x] Background - [x] Border @@ -105,6 +115,24 @@ For more, see the [Discourse Docker Guide](https://meta.discourse.org/docs?topic ## Architecture -The architecture of this project is for all BBCode Parser related code to be contained in `/bbcode-src`, which would then be minified into a module and added to the appropriate location in `/assets/javascripts` to be used by the discourse plugin proper. This is to work around the weird way discourse requires libraries to be loaded in. There will be a Rollup config and github action setup to automate minifying and moving the module. - -Honestly, if anyone has a better solution, please send help. +Parser and tag implementations live in `bbcode-src`. Run +`pnpm install --frozen-lockfile` and `pnpm build` to regenerate the checked-in +parser bundle and source map after source changes. Install the full plugin in +Discourse's `plugins/bbcode` directory. + +The same parser bundle serves server cooking and browser previews. A registered +Markdown plugin uses a markdown-it core rule to detect custom BBCode and process +it through BBob. Ordinary Markdown, core inline formatting, and Markdown code +examples containing BBCode keep native behavior when no custom BBCode occurs +outside those examples. + +Processing remains per post: a post containing custom BBCode uses the plugin's +legacy whitespace and paragraph behavior throughout the post, including its +surrounding Markdown. This is not isolation of each BBCode span. Rendered output +still passes through Discourse's sanitizer. + +Deploying a parser change does not refresh saved cooked HTML. Restart Discourse +web processes and Sidekiq, then follow the +[deployment and rebake instructions](INTEGRATION_NOTES.md#deployment-and-rebaking) +on the destination server. Existing CSS containment, parser resets, and CSS +image rehosting remain in place. BBScript remains optional and disabled by default. diff --git a/assets/bundled/bbcode-parser.min.js b/assets/bundled/bbcode-parser.min.js index 6b3dff1..797ecdd 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="\n",n="\t",s="=",r='"',o=" ",a="[",i="]",c="/",l="\\";function u(t){return"object"==typeof t&&null!==t&&"tag"in t}function d(t){return"string"==typeof t}function g(t){return t===e}function h(t,e,n){return Object.keys(t).reduce(((n,s)=>e(n,s,t)),n)}function p(t){return u(t)&&Array.isArray(t.content)?t.content.reduce(((t,e)=>t+p(e)),0):d(t)?String(t).length:0}function f(t){return t.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'").replace(/(javascript|data|vbscript):/gi,"$1%3A")}function b(t,e){switch(typeof e){case"boolean":return e?`${t}`:"";case"number":return`${t}="${e}"`;case"string":return`${t}="${f(e)}"`;case"object":return`${t}="${f(JSON.stringify(e))}"`;default:return""}}function m(t){return null==t?"":h(t,((t,e,n)=>[...t,b(e,n[e])]),[""]).join(" ")}const w=(t,e)=>{const n=h(e||{},((t,e,n)=>n[e]===e?n[e]:null),null);if(n){const s=b(t,n),r={...e};delete r[String(n)];return`${s}${m(r)}`}return`${t}${m(e)}`};class y{attr(t,e){return void 0!==e&&(this.attrs[t]=e),this.attrs[t]}append(t){return function(t,e){Array.isArray(t.content)&&t.content.push(e)}(this,t)}setStart(t){this.start=t}setEnd(t){this.end=t}get length(){return p(this)}toTagStart({openTag:t=a,closeTag:e=i}={}){return`${t}${w(String(this.tag),this.attrs)}${e}`}toTagEnd({openTag:t=a,closeTag:e=i}={}){return`${t}${c}${this.tag}${e}`}toTagNode(){const t=new y(String(this.tag).toLowerCase(),this.attrs,this.content);return this.start&&t.setStart(this.start),this.end&&t.setEnd(this.end),t}toString({openTag:t=a,closeTag:e=i}={}){const n=this.content?((t,e,n)=>{const s=t=>u(t)?t.toString({openTag:e,closeTag:n}):String(t);return Array.isArray(t)?t.reduce(((t,e)=>null!==e?t+s(e):t),""):t?s(t):null})(this.content,t,e):"",s=this.toTagStart({openTag:t,closeTag:e});return null===this.content||Array.isArray(this.content)&&0===this.content.length?s:`${s}${n}${this.toTagEnd({openTag:t,closeTag:e})}`}static create(t,e={},n=null,s){const r=new y(t,e,n);return s&&r.setStart(s),r}static isOf(t,e){return t.tag===e}constructor(t,e,n){this.tag=t,this.attrs=e,this.content=n}}const v="t",x=t=>t&&void 0!==t.v?t.v:"",$=t=>x(t).charCodeAt(0)===c.charCodeAt(0);class k{get type(){return this[v]}isEmpty(){return 0===this[v]||isNaN(this[v])}isText(){return!(!(t=this)||void 0===t[v]||5!==t[v]&&6!==t[v]&&1!==t[v]);var t}isTag(){return!(!(t=this)||void 0===t[v])&&2===t[v];var t}isAttrName(){return!(!(t=this)||void 0===t[v])&&3===t[v];var t}isAttrValue(){return!(!(t=this)||void 0===t[v])&&4===t[v];var t}isStart(){return!$(this)}isEnd(){return $(this)}getName(){return(t=>{const e=x(t);return $(t)?e.slice(1):e})(this)}getValue(){return x(this)}getLine(){return(t=this)&&t.l||0;var t}getColumn(){return(t=this)&&t.r||0;var t}getStart(){return(t=this)&&t.s||0;var t}getEnd(){return(t=this)&&t.e||0;var t}toString({openTag:t=a,closeTag:e=i}={}){return((t,e=a,n=i)=>{let s=e;return s+=x(t),s+=n,s})(this,t,e)}constructor(t,e,n=0,s=0,r=0,o=0){this.l=n,this.r=s,this[v]=t||0,this.v=String(e),this.s=r,this.e=o}}class T{skip(t=1,e){this.c.pos+=t,this.o&&this.o.onSkip&&!e&&this.o.onSkip()}hasNext(){return this.c.len>this.c.pos}getCurr(){return void 0===this.s[this.c.pos]?"":this.s[this.c.pos]}getPos(){return this.c.pos}getLength(){return this.c.len}getRest(){return this.s.substring(this.c.pos)}getNext(){const t=this.c.pos+1;return t<=this.s.length-1?this.s[t]:null}getPrev(){const t=this.c.pos-1;return void 0===this.s[t]?null:this.s[t]}isLast(){return this.c.pos===this.c.len}includes(t){return this.s.indexOf(t,this.c.pos)>=0}grabWhile(t,e){let n=0;if(this.hasNext())for(n=this.c.pos;this.hasNext()&&t(this.getCurr());)this.skip(1,e);return this.s.substring(n,this.c.pos)}grabN(t=0){return this.s.substring(this.c.pos,this.c.pos+t)}substrUntilChar(t){const{pos:e}=this.c,n=this.s.indexOf(t,e);return n>=0?this.s.substring(e,n):""}constructor(t,e={}){this.s=t,this.c={pos:0,len:t.length},this.o=e}}const A=(t,e)=>new T(t,e);const C=[o,n],S=[s,o,n],_=t=>C.indexOf(t)>=0,j=t=>t===l,L=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,r).replace(l+r,r);function O(t,u={}){let d=0,g=0,h=0,p=-1,f=0,b=0,m="";const w=new Array(Math.floor(t.length)),y=u.openTag||a,v=u.closeTag||i,x=!!u.enableEscapeTags,$=(u.contextFreeTags||[]).filter(Boolean).map((t=>t.toLowerCase())),T=u.caseFreeTags||!1,C=new Map,O=u.onToken||(()=>{}),E=[v,y,r,l,o,n,s,e,"!"],N=[y,o,n,e],W=t=>-1===N.indexOf(t),I=()=>{h++},V=(t,e)=>{""!==m&&e&&(m=""),""===m&&$.includes(t.toLowerCase())&&(m=t)},U=A(t,{onSkip:I});function q(t,e,n,s){const r=function(t,e,n=0,s=0,r=0,o=0){return new k(t,e,n,s,r,o)}(t,e,d,g,n,s);O(r),g=h,p+=1,w[p]=r}function D(t,e,n){if(1===b){const e=t=>!(t===s||_(t)),n=t.grabWhile(e),r=t.isLast(),o=t.getCurr()!==s;return t.skip(),r||o?q(4,L(n)):q(3,n),r?0:o?1:2}if(2===b){let n=!1;const o=o=>{const a=o===r,i=t.getPrev(),c=t.getNext(),u=i===l,d=c===s,g=_(o),h=c&&_(c);return!(!n||!(t=>S.indexOf(t)>=0)(o))||!!(!a||u||(n=!n,n||d||h))&&(!!e||!g)},a=t.grabWhile(o);return t.skip(),q(4,L(a)),t.getPrev()===r&&g++,t.isLast()?0:1}const o=n+t.getPos()-1,a=t.grabWhile((e=>!(e===s||_(e)||t.isLast())));if(q(2,a,o,n+t.getLength()+1),V(a),t.skip(),g++,e)return 2;return t.includes(s)?1:2}function G(){const t=U.getCurr(),e=U.getNext();U.skip();const n=U.substrUntilChar(v),r=0===n.length||n.indexOf(y)>=0;if(e&&(o=e,E.indexOf(o)>=0)||r||U.isLast())return q(1,t),0;var o;const a=-1===n.indexOf(s),i=n[0]===c;if(a||i){const t=U.getPos()-1,e=U.grabWhile((t=>t!==v)),n=t+e.length+2;return U.skip(),q(2,e,t,n),V(e,i),0}return 2}function z(){const t=U.getPos(),e=U.grabWhile((t=>t!==v),!0),n=A(e,{onSkip:I}),s=n.includes(o);for(b=0;n.hasNext();)b=D(n,!s,t);return U.skip(),0}function P(){if(U.getCurr()===e)return q(6,U.getCurr()),U.skip(),h=0,g=0,d++,0;if(_(U.getCurr())){return q(5,U.grabWhile(_)),0}if(U.getCurr()===y){if(m){const t=y.length+1+m.length,e=`${y}${c}${m}`;if(U.grabN(t)===e)return 1}else if(U.includes(v))return 1;return q(1,U.getCurr()),U.skip(),g++,0}if(x){if(j(U.getCurr())){const t=U.getCurr(),e=U.getNext();return U.skip(),e&&(t=>t===y||t===v||t===l)(e)?(U.skip(),q(1,e),0):(q(1,t),0)}const t=t=>W(t)&&!j(t);return q(1,U.grabWhile(t)),0}return q(1,U.grabWhile(W)),0}return{tokenize:function(){for(f=0;U.hasNext();)switch(f){case 1:f=G();break;case 2:f=z();break;default:f=P()}return w.length=p+1,w},isTokenNested:function(e){const n=y+c+e;if(C.has(n))return!!C.get(n);{const e=T?t.toLowerCase().indexOf(n.toLowerCase())>-1:t.indexOf(n)>-1;return C.set(n,e),e}}}}class E{last(){return Array.isArray(this.n)&&this.n.length>0&&void 0!==this.n[this.n.length-1]?this.n[this.n.length-1]:null}flush(){return!!this.n.length&&this.n.pop()}push(t){this.n.push(t)}toArray(){return this.n}constructor(){this.n=[]}}const N=()=>new E;function W(t,e={}){const n=e,s=n.openTag||a,r=n.closeTag||i,o=(n.onlyAllowTags||[]).filter(Boolean).map((t=>t.toLowerCase())),c=n.caseFreeTags||!1;let l=null;const d=N(),g=N(),h=N(),p=N(),f=new Set;function b(t){return Boolean(f.has(c?t.toLowerCase():t))}function m(){h.flush()&&p.flush()}function w(){const t=g.last();return t&&u(t)?t.content:d.toArray()}function v(t,e,n=!0){Array.isArray(t)&&void 0!==e&&(t.push(e.toTagStart({openTag:s,closeTag:r})),Array.isArray(e.content)&&e.content.length&&(e.content.forEach((e=>{t.push(e)})),n&&t.push(e.toTagEnd({openTag:s,closeTag:r}))))}function x(t,e){var n;Array.isArray(t)&&void 0!==e&&(u(e)?(n=e.tag,!o.length||o.indexOf(n.toLowerCase())>=0?t.push(e.toTagNode()):v(t,e)):t.push(e))}function $(t){m();const e=y.create(t.getValue(),{},[],{from:t.getStart(),to:t.getEnd()}),n=function(t){const e=t.getValue(),n=c?e.toLowerCase():e,{isTokenNested:s}=l||{};return!f.has(n)&&s&&s(n)?(f.add(n),!0):f.has(n)}(t);if(h.push(e),n)g.push(e);else{x(w(),e)}}function k(t){t.isStart()&&$(t),t.isEnd()&&function(t){const e=t.getValue().slice(1),o=g.flush();if(m(),o){const e=w();u(o)&&o.setEnd({from:t.getStart(),to:t.getEnd()}),x(e,o)}else if(b(e)){if("function"==typeof n.onError){const e=t.getValue(),s=t.getLine(),r=t.getColumn();n.onError({tagName:e,lineNumber:s,columnNumber:r})}}else x(w(),t.toString({openTag:s,closeTag:r}))}(t)}const T=e.createTokenizer?e.createTokenizer:O;l=T(t,{onToken:function(t){t.isTag()?k(t):function(t){const e=h.last(),n=t.getValue(),o=b(t.toString()),a=w();if(null!==e)if(t.isAttrName()){p.push(n);const t=p.last();t&&e.attr(t,"")}else if(t.isAttrValue()){const t=p.last();t?(e.attr(t,n),p.flush()):e.attr(n,n)}else t.isText()?o?e.append(n):x(a,n):t.isTag()&&x(a,t.toString({openTag:s,closeTag:r}));else t.isText()?x(a,n):t.isTag()&&x(a,t.toString({openTag:s,closeTag:r}))}(t)},openTag:s,closeTag:r,onlyAllowTags:n.onlyAllowTags,contextFreeTags:n.contextFreeTags,caseFreeTags:n.caseFreeTags,enableEscapeTags:n.enableEscapeTags}),l.tokenize();const A=g.flush();return null!==A&&A&&u(A)&&b(A.tag)&&v(w(),A,!1),d.toArray()}const I=t=>"object"==typeof t&&null!==t;function V(t,e){const n=t;if(Array.isArray(n))for(let t=0;t[].some.call(e,(e=>U(t,e))))):!(!I(t)||!I(e))&&Object.keys(t).every((n=>{const s=e[n],r=t[n];return I(r)&&I(s)?U(r,s):"boolean"==typeof r?r!==(null===s):s===r})):t===e)}function q(t,e){const n=t;return n.messages=[...n.messages||[]],n.options={...e,...n.options},n.walk=function(t){return V(this,t)},n.match=function(t,e){return function(t,e,n){return Array.isArray(e)?V(t,(t=>{for(let s=0;sU(e,t)?n(t):t))}(this,t,e)},n}function D(t,e){const{stripTags:n=!1}=e||{};if(null==t)return"";if("string"==typeof t||"number"==typeof t)return String(t);if(Array.isArray(t))return G(t,e);if(u(t)){if(n)return G(t.content,e);const s=m(t.attrs);return null===t.content?"<"+t.tag+s+"/>":"<"+t.tag+s+">"+G(t.content,e)+""}return""}function G(t,e){return t&&Array.isArray(t)?t.reduce(((t,n)=>t+D(n,e)),""):t?D(t,e):""}const z=(t,e,n=[])=>({tag:t,attrs:e,content:n,gen:!0}),P=(t,e)=>{const n=Object.keys(t.attrs).join(" "),s=Object.values(t.attrs).join(" ");if(n!==s)return t.attrs;if(!e||!t.start)return{_default:s};const r=e.substring(t.start.from,t.start.to);if(!r.includes("="))return t.attrs;const o=r.split("=");if(2!==o.length)return t.attrs;let a=o[1].slice(0,-1).trim();return a.startsWith('"')&&a.endsWith('"')&&(a=a.slice(1,-1)),{_default:a}},M=(t,e)=>{if(t.start)return e.substring(t.start.from,t.start.to);if(!t.attrs)return`[${t.tag}]`;const n=P(t,e);return n._default?`[${t.tag}=${n._default}]`:t.toTagStart()},F=(t,e)=>t.end?e.substring(t.end.from,t.end.to):t.toTagEnd(),B=(t,e)=>t.end?[M(t,e),...t.content,F(t,e)]:M(t,e),R=(t,e,n)=>{const s=t.substring(n||0).search(e);return s>=0?s+(n||0):s},J="\x3c!-- bbcode injected newlines --\x3e\n\n",Z="\n\n\x3c!-- bbcode pre injected newlines --\x3e",H="\x3c!-- bbcode injected newlines --\x3e",K=new RegExp(`^${/(http|ftp|https|upload):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])/.source}|${/\!?\[.*\]\((http|ftp|https|upload):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])\)/.source}$`),Q=/((\n|^)(?```+|~~~+)(?.*\n))|(?\[(?i?code|plain)(=.*)?\])|(?(?`{1,2})(.*)(?\k))/im,X=/^(\|[^\n]+\|\r?\n)((?:\| ?:?[-]+:? ?)+\|)(\n(?:\|[^\n]+\|\r?\n?)*)?$/m;function Y(){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 tt=t=>"object"==typeof t,et=t=>"string"==typeof t,nt=(t,e=!1)=>{const n=t;if(Array.isArray(n)){st(n),n.some(et)&&(n.unshift(J),n.push(J));for(let t=0;t{let e=t.findLastIndex((t=>et(t)&&!g(t)))+1;for(let n=e-1;n>=0;n--)et(t[n])&&!g(t[n])||(g(t[n])||tt(t[n]))&&(n!==e-1&&t.splice(n+1,e-n-1,t.slice(n+1,e).join("")),e=n);0!==e&&t.splice(0,e,t.slice(0,e).join(""))},rt=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},ot=/\n{2,}/gm,at=t=>t.walk((t=>(u(t)&&t.attrs&&Object.keys(t.attrs).forEach((e=>{"string"==typeof t.attrs[e]&&(t.attrs[e]=t.attrs[e].replace(ot,"\n"))})),t)));function it(t,e,n,s){return e.walk((e=>{if(u(e)){const r=e.tag,o=t[r];if("function"==typeof o)return o(e,n,s)}return e}))}const ct=Symbol("slide-title-open"),lt=Symbol("slide-title-close"),ut=Symbol("slide-close"),dt=/(?\{slide=)|(?\})|(?\{\/slide\})/i;function gt(t){switch(t){case ct:return"{slide=";case lt:return"}";case ut:return"{/slide}";default:return t}}const ht={accordion:(t,e)=>{const n=Y(),s=function(t){t=[...t];const e=[];for(;t.length>0;){const n=t[0];if(u(n)){e.push(t.shift());continue}const s=R(n,dt);if(-1===s){e.push(t.shift());continue}const r=n.match(dt),o=n.slice(0,s),a=n.slice(s+r[0].length);o.length&&e.push(o),r.groups.slideTitleOpen&&e.push(ct),r.groups.slideTitleClose&&e.push(lt),r.groups.slideClose&&e.push(ut),a.length?t[0]=a:t.shift()}return e}(t.content),r=function(t){const e=[];let n=null,s=null;for(const r of t)if(r===ct&&null===s)n=y.create("slide"),n.content=[],n.customTitle=[],s=ct;else{if(r===lt&&s===ct){s=lt;continue}r===ut&&n&&s===lt?(e.push(n),n=null,s=null):n?s===ct?n.customTitle.push(gt(r)):n.content.push(gt(r)):e.push(gt(r))}return e}(s),o=r.filter((t=>u(t)&&"slide"===t.tag)).map((t=>(t.isValid=!0,t.groupId=n,t)));if(!o.length)return[M(t,e.data.raw),...t.content,F(t,e.data.raw)];const a=P(t,e.data.raw);if(a._default){const t=a._default.split("|").map((t=>t.trim())),e=t.filter((t=>["bright","bcenter","bleft","fleft","fright"].includes(t))).pop();e&&(a.align??=e),(t.some((t=>t.endsWith("px")))||t.some((t=>t.endsWith("%"))))&&(a.width??=t.find((t=>t.endsWith("px")||t.endsWith("%"))))}let i=a.align?.toLowerCase()||"",c="";return(a.width?.endsWith("px")||a.width?.endsWith("%"))&&(c=`width: ${a.width};`),z("div",{class:"bb-accordion "+i,"data-group-id":n,style:c},o)},slide:(t,e)=>{if(!t.isValid)return[M(t,e.data.raw),...t.content,F(t,e.data.raw)];const n=P(t,e.data.raw);let s=[n.title||n._default||"Slide"],r=!!n.open||!1,o=n.left?"left":n.right?"right":n.center?"center":"left";if(t.customTitle?.length){s=t.customTitle;const e=s.filter((t=>"string"==typeof t)).join("").toLowerCase().split("|").map((t=>t.trim()));e.includes("open")&&(r=!0),e.includes("right")&&(o="right"),e.includes("center")&&(o="center"),e.includes("left")&&(o="left"),s=s.map((t=>(d(t)&&(t=t.replace(/\|(open|right|center|left)/gi,"")),t)))}return[z("details",{class:"bb-slide",open:r},[z("summary",{class:"bb-slide-title",style:`text-align: ${o}; ${n.style||""}`},s),z("div",{class:"bb-slide-content"},t.content)])]}},pt={left:t=>z("div",{class:"bb-left"},t.content),center:t=>z("div",{class:"bb-center"},t.content),right:t=>z("div",{class:"bb-right"},t.content)},ft={a:(t,e)=>{const n=P(t,e.data.raw)._default||"";return z("a",{id:`user-anchor-${n.trim()}`,name:`user-anchor-${n.trim()}`},t.content)},goto:(t,e)=>{const n=P(t,e.data.raw)._default||"";return z("a",{href:`#user-anchor-${n.trim()}`},t.content)}},bt=["arial","book antiqua","courier new","georgia","tahoma","times new roman","trebuchet ms","verdana"],mt={thin:"100",extralight:"200",light:"300",regular:"400",medium:"500",semibold:"600",bold:"700",extrabold:"800",black:"900"},wt=["ital","opsz","slnt","wdth","wght"],yt=/(?[a-zA-Z]*)?\s?(?[0-9]*)?\s?(?italic)?/;const vt=z("div",{class:"bb-email-header"},""),xt=z("div",{class:"bb-email-footer"},z("div",{class:"bb-email-button"},"")),$t={row:t=>z("div",{class:"bb-row"},t.content),column:(t,e)=>{const n=P(t,e.data.raw)._default||"8",s=n.startsWith("span")?`column-width-${n}`:`column-width-span${n}`;return z("div",{class:"bb-column","data-span":s},t.content)}},kt=["init","click","change","input","dblclick","mouseenter","mouseleave","scroll"];const Tt=["me","them","right","left"],At={textmessage:(t,e)=>{const n=t.content.filter((t=>u(t)&&"message"===t.tag));if(n.forEach((t=>{t.isValid=!0})),!n.length)return B(t,e.data.raw);const s=P(t,e.data.raw)._default||"Recipient",r=""!==s.trim()?s:"Recipient";return z("div",{class:"bb-textmessage"},[z("div",{class:"bb-textmessage-name"},r),z("div",{class:"bb-textmessage-overflow"},[z("div",{class:"bb-textmessage-content"},t.content)])])},message:(t,e)=>{if(!t.isValid)return B(t,e.data.raw);let n=P(t,e?.data?.raw)?._default?.toLowerCase();Tt.includes(n)&&"right"!==n||(n="me"),"left"===n&&(n="them");return z("div",{class:"me"===n?"bb-message-me":"bb-message-them"},[z("div",{class:"bb-message-content"},t.content)])}},Ct={...ht,...pt,...ft,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=P(t,e.data.raw)?._default||"",r=t.content.filter((t=>u(t)&&"keyframe"===t.tag)).map((t=>{t.isValid=!0;const n=P(t,e.data.raw)._default||"";t.ident=n+(n.match(/^\d+$/)?"%":"");const s=t.content.filter(d).join("").replaceAll(/[\[\]\{\}]/g,"");return t.formatted=`${t.ident}{ ${s} }`,t})),o=`@keyframes ${n}${s} { ${r.map((t=>t.formatted)).join("\n")} }`;return e.data.styles.push(o),[]},bg:(t,e)=>{const n=P(t,e.data.raw)._default;return z("div",{style:`background-color: ${n};`,class:"bb-background"},t.content)},block:(t,e)=>{const n="block",s=(P(t,e.data.raw)._default||n).toLowerCase(),r=["block","dice","dice10","setting","warning","storyteller","announcement","important","question","encounter","information","character","treasure"].includes(s)?s:n;return z("table",{class:"bb-block","data-bb-block":r},[z("tbody",[z("tr",[z("td",{class:"bb-block-icon"}),z("td",{class:"bb-block-content"},t.content)])])])},blockquote:(t,e)=>{const n=P(t,e.data.raw)._default||"";return z("div",{class:"bb-blockquote"},[z("div",{class:"bb-blockquote-left"}),z("div",{class:"bb-blockquote-content"},[t.content,z("div",{class:"bb-blockquote-speaker"},""!==n?`- ${n}`:"")]),z("div",{class:"bb-blockquote-right"})])},border:(t,e)=>{const n=P(t,e.data.raw)._default;return z("div",{style:`border: ${n};`,class:"bb-border"},t.content)},br:()=>z("br",{},null),centerblock:(t,e)=>{const n=P(t,e.data.raw)._default||"50";return z("div",{style:`margin: 0 auto; width: ${n}%`},t.content)},check:(t,e)=>{const n=P(t,e.data.raw)._default||"dot";return z("div",{class:"bb-check","data-type":n},t.content)},class:(t,e)=>{const n=P(t),s=n.name||n._default;e.data.previewing||e.data.commonGUID||(e.data.commonGUID="post-"+Math.random().toString(36).substring(2,7));const r=e.data.previewing?"preview":e.data.commonGUID,o=s+"__"+r,a=t.content.filter(d).map((t=>t.replaceAll("{post_id}",r).replaceAll(/[\[\]\{\}]/g,"")));let i="";const c=[];return["hover","focus","active","focus-within","focus-visible"].includes(n.state?.toLowerCase())&&(i=":"+n.state.toLowerCase()),n.selector&&(i=n.selector.replace(/[,{}\\\n]/g,"")),n.minWidth?.match(/^[0-9]+[a-z]+$/)&&c.push(`(min-width: ${n.minWidth})`),n.maxWidth?.match(/^[0-9]+[a-z]+$/)&&c.push(`(max-width: ${n.maxWidth})`),a.unshift(`.${o}${i} {`),a.push("}"),c.length&&(a.unshift(`@media ${c.join(" and ")} {`),a.push("}")),e.data.styles.push(a.join("")),[]},code:t=>({isWhitespaceSensitive:!0,content:["```"+(P(t)._default||"bbcode")+"\n",t.content,"\n```\n"]}),color:t=>{const e=P(t)._default||"";return""===e.trim()?t.content:z("span",{style:`color: ${e}`},t.content)},comment:t=>z("span",{class:"hidden"},t.content),div:(t,e)=>{if(t.gen)return t;const n=P(t,e.data.raw),s=n.style||n._default,r=n.class;if(!r?.trim())return z("div",{style:s},t.content);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,a=r.split(" ").map((t=>t+"__"+o)).join(" ");return z("div",{class:a,style:s},t.content)},divide:t=>{const e=(P(t)._default||"").toLowerCase();return z("span",{class:"bb-divide","data-type":e},t.content)},fieldset:(t,e)=>{const n=P(t,e.data.raw)._default||"";return z("fieldset",{class:"bb-fieldset"},[z("legend",{class:"bb-fieldset-legend"},n),z("div",{class:"bb-fieldset"},t.content)])},fa:t=>{const e=t.attrs;let n=e.style||"";return n+=e["primary-color"]?`--fa-primary-color: ${e["primary-color"]};`:"",n+=e["secondary-color"]?`--fa-secondary-color: ${e["secondary-color"]};`:"",n+=e["primary-opacity"]?`--fa-primary-opacity: ${e["primary-opacity"]};`:"",n+=e["secondary-opacity"]?`--fa-secondary-opacity: ${e["secondary-opacity"]};`:"",n+=e["rotate-angle"]?`--fa-rotate-angle: ${e["rotate-angle"]};`:"",z("i",{"data-bbcode-fa":null},[z("i",{class:(t.content||[]).join(""),style:n,"data-fa-transform":e["fa-transform"]||""},[])])},font:(t,e)=>{const n=P(t,e.data.raw),s=n?._default||n?.family||n?.name;if(!s||""===s.trim())return t.content;if(bt.includes(s.trim().toLowerCase()))return z("span",{style:`font-family: '${s}'`},t.content);const r=(t=>{let e={ital:0,wght:400};if(t?.style){const n=t.style.trim().toLowerCase(),s=yt.exec(n).groups||{};s?.italic&&(e.ital=1);const r=s.weight;r&&r>=0&&r<=900?e.wght=r:Object.keys(mt).includes(s.named_weight||"")&&(e.wght=mt[s.named_weight]),e={...e,...Object.fromEntries(Object.entries(t).filter((([t])=>wt.includes(t))))}}return e})(n),o=((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(",")))(s,r);e.data.fonts.add(o);const a=1===r.ital?"italic":"normal",i=Object.entries(r).filter((([t])=>"wght"!==t&&"ital"!==t));let c="";return i.length&&(c="font-variation-settings: "+i.map((([t,e])=>`'${t}' ${e}`)).join(", ")+";"),z("span",{style:`font-family: '${s}'; font-weight: ${r.wght}; font-style: ${a}; ${c}`,"data-font":o},t.content)},h:t=>z("h1",{},t.content),h1:t=>z("h1",{},t.content),h2:t=>z("h2",{},t.content),h3:t=>z("h3",{},t.content),h4:t=>z("h4",{},t.content),h5:t=>z("h5",{},t.content),h6:t=>z("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}(P(t)._default).toString();return z("div","0"===e?{class:"bb-height-restrict"}:{class:"bb-height-restrict",style:`height: ${e}px;`},t.content)},highlight:t=>z("span",{class:"bb-highlight"},t.content),icode:t=>({isWhitespaceSensitive:!0,content:["`",t.content,"`"]}),imagefloat:t=>{const e=P(t)._default||"";return z("div",{class:`bb-float-${e}`},t.content)},inlinespoiler:t=>z("span",{class:"bb-inline-spoiler"},t.content),justify:t=>z("div",{class:"bb-justify"},t.content),keyframe:(t,e)=>t.isValid?[]:[M(t,e.data.raw),...t.content,F(t,e.data.raw)],mail:t=>{const e=t.attrs;let n={mailOption:(e.type||"send").toLowerCase(),person:e.person||"Unknown",subject:e.subject||"Empty"};return z("div",{class:"bb-email","data-bb-email":n.mailOption},[vt,(o=n.person,z("div",{class:"bb-email-address"},o)),(r=n.subject,z("div",{class:"bb-email-subject"},r)),(s=t.content,z("div",{class:"bb-email-content"},s)),xt]);var s,r,o},newspaper:t=>z("div",{class:"bb-newspaper"},t.content),nobr:t=>({disableLineBreakConversion:!0,content:t.content}),note:t=>z("div",{class:"bb-note"},[z("div",{class:"bb-note-tape"},""),z("div",{class:"bb-note-content"},[t.content,z("div",{class:"bb-note-footer"},"")])]),ooc:t=>z("div",{class:"bb-ooc"},t.content),pindent:t=>z("span",{class:"bb-pindent"},t.content),plain:t=>t.content,print:t=>{const e="print",n=(P(t)._default||e).toLowerCase(),s=["print","line","graph","parchment"].includes(n)?n:e;return z("div",{class:s===e?"bb-print":`bb-print-${s}`},t.content)},progress:t=>{const e=P(t)._default;return z("div",{class:"bb-progress"},[z("div",{class:"bb-progress-text"},t.content),z("div",{class:"bb-progress-bar",style:`width: calc(${e}% - 6px)`},""),z("div",{class:"bb-progress-bar-other"},"")])},quote:(t,e)=>{const n=P(t,e.data.raw);return"\n"===t.content[0]&&t.content.shift(),[`\n[${t.tag}="${n._default}"]\n\n`,...t.content,"\n\n[/quote]\n"]},...$t,thinprogress:(t,e)=>{const n=P(t,e.data.raw)._default;return z("div",{class:"bb-progress-thin"},[z("div",{class:"bb-progress-text"},t.content),z("div",{class:"bb-progress-bar",style:`width: calc(${n}% - 6px)`},""),z("div",{class:"bb-progress-bar-other"},"")])},savenl:t=>({isWhitespaceSensitive:!0,content:t.content}),sh:t=>z("h2",{},t.content),script:(t,e)=>{const n=P(t,e.data.raw);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,r=kt.includes(n.on?.toLowerCase()||"init")&&n.on?.toLowerCase()||"init",o={id:s,class:n.class||"",on:r,version:n.version||"",content:t.content.join("")};return e.data.bbscripts.push(o),[]},scroll:(t,e)=>{const n=function(t){const e=t&&""!==t.trim()?t.replace(/[^\d.]/g,""):0;return e&&e>=0&&e<=700?e:0===e?0:700}(P(t,e.data.raw)._default);return z("div",{class:"bb-scroll",style:`height: ${n}px`},t.content)},side:t=>{const e=P(t)._default||"left";return z("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,a=3,i=.2,c=7,l=1;if(s&&(e=s[1])){switch(n.unit=(s[2]||"").toLowerCase(),n.unit){case"px":e>r?e=r:ea?e=a:ec?e=c:e{const n=P(t,e.data.raw)._default;return z("details",{class:"bb-spoiler"},[z("summary",{},"Spoiler"+(n?`: ${n}`:"")),z("div",{class:"bb-spoiler-content"},t.content)])},sub:t=>z("sub",{},t.content),sup:t=>z("sup",{},t.content),tab:(t,e)=>{if(!t.isValid)return B(t,e.data.raw);const n=P(t,e.data.raw),s=n?._default||n?.name||"Tab",r=`tab-${s.replace(/\W/g,"_")}-${Y()}`;return[z("input",{type:"radio",id:r,name:"tab-group-"+t.groupId,class:"bb-tab",checked:t.open}),z("label",{class:"bb-tab-label",for:r,style:n.style},s),z("div",{class:"bb-tab-content"},t.content)]},tabs:(t,e)=>{const n=t.content.filter((t=>u(t)&&"tab"===t.tag)),s=Y();return n.forEach((t=>{t.isValid=!0,t.groupId=s})),n.length?(n[0].open=!0,z("div",{class:"bb-tabs"},n)):B(t,e.data.raw)},...At,b:t=>z("span",{class:"bbcode-b"},t.content),i:t=>t.gen?t:z("span",{class:"bbcode-i"},t.content),u:t=>z("span",{class:"bbcode-u"},t.content),s:t=>z("span",{class:"bbcode-s"},t.content)},St=Object.keys(Ct),_t=function t(e,n=it){const s=t=>{function r(t,r){return n(e,t,r,s.options||{})}return s.options=Object.assign(s.options||{},t),r.options=s.options,r};return s.extend=function(r){return t(r(e,s.options),n)},s}(Ct);function jt(t){return t.replaceAll(J,"").replaceAll(Z,"").replaceAll("\n"+H,"").replaceAll(H+"\n","").replaceAll(H,"")}function Lt(t){return t.replaceAll("\n
    ","").replaceAll("\n
      ","").replaceAll("\n
      ","")}function Ot(t,e){const n=e.hoistMap;for(const[e,s]of Object.entries(n))t=t.replaceAll(e,s);return t}function Et(t,e){if(0===e.styles.length)return t;return'"+t}function Nt(t,e){if(0===e.bbscripts.length)return t;return e.bbscripts.map((t=>``)).join("")+t}function Wt(t,e){const n={};let s=0;const r=(e,s,r,o=!1)=>{const a=Y();return-1!==s?(n[a]=t.substring(e,s),t=t.substring(0,e)+a+t.substring(s)):(n[a]=t.substring(e),t=t.substring(0,e)+a+r),o&&(n[a].startsWith("\n")&&(n[a]=n[a].substring(1)),n[a].endsWith("\n")&&(n[a]=n[a].substring(0,n[a].length-1))),e+a.length+r.length};for(;-1!==(s=R(t,Q,s));){const e=Q.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|$)"),i=R(t,a,s+r.length),c=Y();n[c]=-1!==i?t.substring(s+r.length+o.length,i):t.substring(s+r.length+o.length);const l=`[saveNL]\n${r}${o}${c}\n${r}\n[/saveNL]`;t=t.substring(0,s)+l+(-1!==i?t.substring(i+1+r.length):""),s+=l.length}else if(e.groups?.bbcode){const n=e.groups.bbcode,o=`[/${e.groups.bbcodeTag.toLowerCase()}]`,a=t.toLowerCase().indexOf(o,s+1);s=r(s+n.length,a,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 It(t,e){let n=0;for(;-1!==(n=R(t,X,n));){const e=X.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 Vt={onlyAllowTags:[...St],caseFreeTags:!0,contextFreeTags:["plain","code","icode","class","fa"],enableEscapeTags:!0,onError:t=>{Vt.previewing&&console.warn(t.message,t.lineNumber,t.columnNumber)}},Ut=_t();t.RpNBBCode=(t,e)=>{const n=[Ut];e.preserveWhitespace&&n.push((t=>rt(t))),n.push((t=>nt(t)),at);const[s,r]=function(t){let e={};const n=[Wt,It];for(const s of n)[t,e]=s(t,e);return[t,e]}(t);return function(t){const e="function"==typeof t?[t]:t||[],n=()=>"";return{process(t,s){const r=s||{skipParse:!1,parser:W,render:n,data:null},o=r.parser||W,a=r.render,i=r.data||null;if("function"!=typeof o)throw new Error("C1");const c=r.skipParse&&Array.isArray(t)?t:o(t,r);let l=r.skipParse&&Array.isArray(t)?q(t||[],r):q(c,r);for(let t=0;te(n,s,t),n)}function h(t){return u(t)&&Array.isArray(t.content)?t.content.reduce((t,e)=>t+h(e),0):d(t)?String(t).length:0}const f=/[\u0000-\u001F\u007F]/g;function b(t){return t.replace(f,"").replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'").replace(/(javascript|data|vbscript|file):/gi,"$1%3A")}function m(t,e){switch(typeof e){case"boolean":return e?`${t}`:"";case"number":return`${t}="${e}"`;case"string":return`${t}="${b(e)}"`;case"object":return`${t}="${b(JSON.stringify(e))}"`;default:return""}}function w(t){return null==t?"":p(t,(t,e,n)=>[...t,m(e,n[e])],[""]).join(" ")}const y=(t,e)=>{const n=p(e||{},(t,e,n)=>n[e]===e?n[e]:null,null);if(n){const s=m(t,n),r={...e};delete r[String(n)];return`${s}${w(r)}`}return`${t}${w(e)}`},v=(t,e,n)=>u(t)?t.toString({openTag:e,closeTag:n}):String(t);class ${get length(){return h(this)}attr(t,e){return void 0!==e&&(this.attrs[t]=e),this.attrs[t]}append(t){return function(t,e){Array.isArray(t.content)&&t.content.push(e)}(this,t)}setStart(t){this.start=t}setEnd(t){this.end=t}toTagStart({openTag:t=a,closeTag:e=i}={}){return`${t}${y(String(this.tag),this.attrs)}${e}`}toTagEnd({openTag:t=a,closeTag:e=i}={}){return`${t}${c}${this.tag}${e}`}toTagNode(){return new $(this.tag,this.attrs,this.content,this.start,this.end)}toString({openTag:t=a,closeTag:e=i}={}){const n=this.content?((t,e,n)=>Array.isArray(t)?t.reduce((t,s)=>null!==s?t+v(s,e,n):t,""):t?v(t,e,n):null)(this.content,t,e):"",s=this.toTagStart({openTag:t,closeTag:e});return null===this.content||Array.isArray(this.content)&&0===this.content.length?s:`${s}${n}${this.toTagEnd({openTag:t,closeTag:e})}`}toJSON(){return{tag:this.tag,attrs:this.attrs,content:this.content,start:this.start,end:this.end}}static create(t,e={},n=null,s){return new $(t,e,n,s)}static isOf(t,e){return t.tag===e}constructor(t,e,n,s,r){this.tag=t,this.attrs=e,this.content=n,this.start=s,this.end=r}}const x="t",k=t=>t&&void 0!==t.v?t.v:"",T=t=>k(t).charCodeAt(0)===c.charCodeAt(0);class A{get type(){return this[x]}isEmpty(){return 0===this[x]||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!T(this)}isEnd(){return T(this)}getName(){return(t=>{const e=k(t);return T(t)?e.slice(1):e})(this)}getValue(){return k(this)}getLine(){return(t=this)&&t.l||0;var t}getColumn(){return(t=this)&&t.r||0;var t}getStart(){return(t=this)&&t.s||0;var t}getEnd(){return(t=this)&&t.e||0;var t}toString({openTag:t=a,closeTag:e=i}={}){return((t,e=a,n=i)=>{let s=e;return s+=k(t),s+=n,s})(this,t,e)}constructor(t,e,n=0,s=0,r=0,o=0){this.l=n,this.r=s,this[x]=t||0,this.v=String(e),this.s=r,this.e=o}}class S{skip(t=1,e){this.pos+=t,!e&&this.onSkip&&this.onSkip()}hasNext(){return this.len>this.pos}getCurr(){return this.pos=0?this.s[t]:null}isLast(){return this.pos===this.len}includes(t){return this.s.indexOf(t,this.pos)>=0}grabWhile(t,e){let n=0;if(this.hasNext()){n=this.pos;const s=e?null:this.onSkip;for(;this.pos=0?this.s.substring(e,n):""}constructor(t,e={}){this.s=t,this.pos=0,this.len=t.length,this.onSkip=e.onSkip||null}}const C=(t,e)=>new S(t,e);const j=t=>t===o||t===n,_=t=>t===l,L=t=>((t,e)=>{let n=0,s=t.length;for(;nn&&t[s-1]===e;)s--;return 0===n&&s===t.length?t:t.substring(n,s)})(t,r).replace(l+r,r);function E(t,u={}){let d=0,g=0,p=0,h=-1,f=0,b=0,m="";const w=new Array(Math.floor(t.length)),y=u.openTag||a,v=u.closeTag||i,$=!!u.enableEscapeTags,x=(u.contextFreeTags||[]).filter(Boolean).map(t=>t.toLowerCase()),k=u.caseFreeTags||!1,T=new Map,S=u.onToken||(()=>{}),E=new Set([v,y,r,l,o,n,s,e,"!"]),O=t=>t!==y&&t!==o&&t!==n&&t!==e,N=()=>{p++},W=(t,e)=>{""!==m&&e&&(m="");const n=t.toLowerCase();""===m&&P(t)&&x.includes(n)&&(m=n)},I=t=>`${y}${c}${t}${v}`,U=C(t,{onSkip:N});function V(t,e,n,s){const r=function(t,e,n=0,s=0,r=0,o=0){return new A(t,e,n,s,r,o)}(t,e,d,g,n,s);S(r),g=p,h+=1,w[h]=r}function q(t,e,a){if(1===b){const e=t=>!(t===s||j(t)),n=t.grabWhile(e),r=t.isLast(),o=t.getCurr()!==s;return t.skip(),r||o?V(4,L(n)):V(3,n),r?0:o?1:2}if(2===b){let a=!1;const i=i=>{const c=i===r,u=t.getPrev(),d=t.getNext(),g=u===l,p=d===s,h=j(i),f=!!d&&j(d);return!(!a||!(t=>t===s||t===o||t===n)(i))||!!(!c||g||(a=!a,a||p||f))&&(!!e||!h)},c=t.grabWhile(i);return t.skip(),V(4,L(c)),t.getPrev()===r&&g++,t.isLast()?0:1}const i=a+t.getPos()-1,c=t.grabWhile(e=>!(e===s||j(e)||t.isLast()));if(V(2,c,i,a+t.getLength()+1),W(c),t.skip(),g++,e)return 2;return t.includes(s)?1:2}function D(){const t=U.getCurr(),e=U.getNext(),n=Boolean(e&&(r=e,E.has(r)));var r;U.skip();const a=U.substrUntilChar(v),i=0===a.length||a.indexOf(y)>=0,l=U.isLast(),d=a.indexOf(o)>=0&&!1===u.whitespaceInTags;if(n||i||l||d)return V(1,t),0;const g=-1===a.indexOf(s),p=a[0]===c;if(g||p){const t=U.getPos()-1,e=U.grabWhile(t=>t!==v),n=t+e.length+2;return U.skip(),V(2,e,t,n),W(e,p),0}return 2}function z(){const t=U.getPos(),e=U.grabWhile(t=>t!==v,!0),n=C(e,{onSkip:N}),r=e.split(s)[0],a=r[0]===c,i=-1===r.indexOf(o),l=!a&&i;for(b=0;n.hasNext();)b=q(n,l,t);return U.skip(),0}function G(){if(U.getCurr()===e)return V(6,U.getCurr()),U.skip(),p=0,g=0,d++,0;if(j(U.getCurr())){return V(5,U.grabWhile(j)),0}if(U.getCurr()===y){if(m){const t=I(m);if(U.grabN(t.length).toLowerCase()===t.toLowerCase())return 1}else if(U.includes(v))return 1;return V(1,U.getCurr()),U.skip(),g++,0}if($){if(_(U.getCurr())){const t=U.getCurr(),e=U.getNext();return U.skip(),e&&(t=>t===y||t===v||t===l)(e)?(U.skip(),V(1,e),0):(V(1,t),0)}const t=t=>O(t)&&!_(t);return V(1,U.grabWhile(t)),0}return V(1,U.grabWhile(O)),0}let F=null;function P(e){const n=I(e);if(T.has(n))return!!T.get(n);let s;return k?(F||(F=t.toLowerCase()),s=F.indexOf(n.toLowerCase())>-1):s=t.indexOf(n)>-1,T.set(n,s),s}return{tokenize:function(){for(f=0;U.hasNext();)switch(f){case 1:f=D();break;case 2:f=z();break;default:f=G()}return w.length=h+1,w},isTokenNested:P}}class O{last(){const t=this.n.length;if(t>0)return this.n[t-1]}has(){return this.n.length>0}flush(){return this.n.length?this.n.pop():void 0}push(t){this.n.push(t)}ref(){return this.n}constructor(){this.n=[]}}const N=()=>new O;function W(t,e={}){const n=e,s=n.openTag||a,r=n.closeTag||i,o=(n.onlyAllowTags||[]).filter(Boolean).map(t=>t.toLowerCase()),c=n.caseFreeTags||!1;let l=null;const d=N(),g=N();let p=null,h=null;const f=new Set;function b(t){return c?t.toLowerCase():t}function m(t){return Boolean(f.has(b(t)))}function w(){p&&(p=null,h=null)}function y(){const t=g.last();return t&&u(t)?t.content:d.ref()}function v(t,e,n=!0){Array.isArray(t)&&void 0!==e&&(t.push(e.toTagStart({openTag:s,closeTag:r})),Array.isArray(e.content)&&e.content.length&&(e.content.forEach(e=>{t.push(e)}),n&&t.push(e.toTagEnd({openTag:s,closeTag:r}))))}function x(t){const e=y();var n;Array.isArray(e)&&void 0!==t&&(u(t)?(n=t.tag,!o.length||o.indexOf(n.toLowerCase())>=0?e.push(t.toTagNode()):v(e,t)):e.push(t))}function k(t){w();const e=$.create(t.getValue(),{},[],{from:t.getStart(),to:t.getEnd()}),n=function(t){const e=b(t.getValue()),{isTokenNested:n}=l||{};return!f.has(e)&&"function"==typeof n&&n(e)?(f.add(e),!0):f.has(e)}(t);p=e,n?g.push(e):x(e)}const T=e.createTokenizer?e.createTokenizer:E;l=T(t,{onToken:function(t){t.isTag()?(t.isStart()&&k(t),t.isEnd()&&function(t){const e=t.getValue().slice(1),o=g.flush();if(w(),o)u(o)&&o.setEnd({from:t.getStart(),to:t.getEnd()}),x(o);else if(m(e)){if("function"==typeof n.onError){const e=t.getValue(),s=t.getLine(),r=t.getColumn();n.onError({tagName:e,lineNumber:s,columnNumber:r})}}else x(t.toString({openTag:s,closeTag:r}))}(t)):function(t){const e=t.getValue(),n=m(t.toString());if(p)switch(t.type){case 3:h=e,e&&p.attr(e,"");break;case 4:h?(p.attr(h,e),h=null):p.attr(e,e);break;case 5:case 6:case 1:n?p.append(e):x(e);break;case 2:x(t.toString({openTag:s,closeTag:r}))}else t.isText()?x(e):t.isTag()&&x(t.toString({openTag:s,closeTag:r}))}(t)},openTag:s,closeTag:r,onlyAllowTags:n.onlyAllowTags,contextFreeTags:n.contextFreeTags,caseFreeTags:n.caseFreeTags,enableEscapeTags:n.enableEscapeTags,whitespaceInTags:n.whitespaceInTags}),l.tokenize();do{const t=g.flush();u(t)&&m(t.tag)?v(y(),t,!1):void 0!==t&&x(t)}while(g.has());return d.ref()}const I=t=>"object"==typeof t&&null!==t;function U(t,e){const n=t;if(Array.isArray(n))for(let t=0;t[].some.call(e,e=>V(t,e))):!(!I(t)||!I(e))&&Object.keys(t).every(n=>{const s=e[n],r=t[n];return I(r)&&I(s)?V(r,s):"boolean"==typeof r?r!==(null===s):s===r}):t===e)}let q="check https://github.com/jilizart/bbob/blob/master/ERRORS.md",D=`"C1" ${q}`,z=`"C2" ${q}`;function G(t,e){const n=t;return n.messages=[...n.messages||[]],n.options={...e,...n.options},n.walk=function(t){return U(this,t)},n.match=function(t,e){return function(t,e,n){return Array.isArray(e)?U(t,t=>{for(let s=0;sV(e,t)?n(t):t)}(this,t,e)},n}function F(t,e){const{stripTags:n=!1}=e||{};if(null==t)return"";if("string"==typeof t||"number"==typeof t)return String(t);if(Array.isArray(t))return P(t,e);if(u(t)){if(n)return P(t.content,e);const s=w(t.attrs);return null===t.content?"<"+t.tag+s+"/>":"<"+t.tag+s+">"+P(t.content,e)+""}return""}function P(t,e){return t&&Array.isArray(t)?t.reduce((t,n)=>t+F(n,e),""):t?F(t,e):""}const B=(t,e,n=[])=>({tag:t,attrs:e,content:n,gen:!0}),M=(t,e)=>{const n=Object.keys(t.attrs).join(" "),s=Object.values(t.attrs).join(" ");if(n!==s)return t.attrs;if(!e||!t.start)return{_default:s};const r=e.substring(t.start.from,t.start.to);if(!r.includes("="))return t.attrs;const o=r.split("=");if(2!==o.length)return t.attrs;let a=o[1].slice(0,-1).trim();return a.startsWith('"')&&a.endsWith('"')&&(a=a.slice(1,-1)),{_default:a}},R=(t,e)=>{if(t.start)return e.substring(t.start.from,t.start.to);if(!t.attrs)return`[${t.tag}]`;const n=M(t,e);return n._default?`[${t.tag}=${n._default}]`:t.toTagStart()},J=(t,e)=>t.end?e.substring(t.end.from,t.end.to):t.toTagEnd(),Z=(t,e)=>t.end?[R(t,e),...t.content,J(t,e)]:R(t,e),H=(t,e,n)=>{const s=t.substring(n||0).search(e);return s>=0?s+(n||0):s},K="\x3c!-- bbcode injected newlines --\x3e\n\n",Q="\n\n\x3c!-- bbcode pre injected newlines --\x3e",X="\x3c!-- bbcode injected newlines --\x3e",Y=new RegExp(`^${/(http|ftp|https|upload):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])/.source}|${/\!?\[.*\]\((http|ftp|https|upload):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])\)/.source}$`),tt=/((\n|^)(?```+|~~~+)(?.*\n))|(?\[(?i?code|plain)(=[^\]\r\n]*)?\])|(?(?`+)(?!`)([\s\S]*?)(?(?)(?!`))/im,et=/^(\|[^\n]+\|\r?\n)((?:\| ?:?[-]+:? ?)+\|)(\n(?:\|[^\n]+\|\r?\n?)*)?$/m;function nt(){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 st=t=>"object"==typeof t,rt=t=>"string"==typeof t,ot=(t,e=!1)=>{const n=t;if(Array.isArray(n)){at(n),n.some(rt)&&(n.unshift(K),n.push(K));for(let t=0;t{let e=t.findLastIndex(t=>rt(t)&&!g(t))+1;for(let n=e-1;n>=0;n--)rt(t[n])&&!g(t[n])||(g(t[n])||st(t[n]))&&(n!==e-1&&t.splice(n+1,e-n-1,t.slice(n+1,e).join("")),e=n);0!==e&&t.splice(0,e,t.slice(0,e).join(""))},it=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},ct=/\n{2,}/gm,lt=t=>t.walk(t=>(u(t)&&t.attrs&&Object.keys(t.attrs).forEach(e=>{"string"==typeof t.attrs[e]&&(t.attrs[e]=t.attrs[e].replace(ct,"\n"))}),t));function ut(t,e,n,s){return e.walk(e=>{if(u(e)){const r=e.tag,o=t[r];if("function"==typeof o)return o(e,n,s)}return e})}const dt=Symbol("slide-title-open"),gt=Symbol("slide-title-close"),pt=Symbol("slide-close"),ht=/(?\{slide=)|(?\})|(?\{\/slide\})/i;function ft(t){switch(t){case dt:return"{slide=";case gt:return"}";case pt:return"{/slide}";default:return t}}const bt={accordion:(t,e)=>{const n=nt(),s=function(t){t=[...t];const e=[];for(;t.length>0;){const n=t[0];if(u(n)){e.push(t.shift());continue}const s=H(n,ht);if(-1===s){e.push(t.shift());continue}const r=n.match(ht),o=n.slice(0,s),a=n.slice(s+r[0].length);o.length&&e.push(o),r.groups.slideTitleOpen&&e.push(dt),r.groups.slideTitleClose&&e.push(gt),r.groups.slideClose&&e.push(pt),a.length?t[0]=a:t.shift()}return e}(t.content),r=function(t){const e=[];let n=null,s=null;for(const r of t)if(r===dt&&null===s)n=$.create("slide"),n.content=[],n.customTitle=[],s=dt;else{if(r===gt&&s===dt){s=gt;continue}r===pt&&n&&s===gt?(e.push(n),n=null,s=null):n?s===dt?n.customTitle.push(ft(r)):n.content.push(ft(r)):e.push(ft(r))}return e}(s),o=r.filter(t=>u(t)&&"slide"===t.tag).map(t=>(t.isValid=!0,t.groupId=n,t));if(!o.length)return[R(t,e.data.raw),...t.content,J(t,e.data.raw)];const a=M(t,e.data.raw);if(a._default){const t=a._default.split("|").map(t=>t.trim()),e=t.filter(t=>["bright","bcenter","bleft","fleft","fright"].includes(t)).pop();e&&(a.align??=e),(t.some(t=>t.endsWith("px"))||t.some(t=>t.endsWith("%")))&&(a.width??=t.find(t=>t.endsWith("px")||t.endsWith("%")))}let i=a.align?.toLowerCase()||"",c="";return(a.width?.endsWith("px")||a.width?.endsWith("%"))&&(c=`width: ${a.width};`),B("div",{class:"bb-accordion "+i,"data-group-id":n,style:c},o)},slide:(t,e)=>{if(!t.isValid)return[R(t,e.data.raw),...t.content,J(t,e.data.raw)];const n=M(t,e.data.raw);let s=[n.title||n._default||"Slide"],r=!!n.open||!1,o=n.left?"left":n.right?"right":n.center?"center":"left";if(t.customTitle?.length){s=t.customTitle;const e=s.filter(t=>"string"==typeof t).join("").toLowerCase().split("|").map(t=>t.trim());e.includes("open")&&(r=!0),e.includes("right")&&(o="right"),e.includes("center")&&(o="center"),e.includes("left")&&(o="left"),s=s.map(t=>(d(t)&&(t=t.replace(/\|(open|right|center|left)/gi,"")),t))}return[B("details",{class:"bb-slide",open:r},[B("summary",{class:"bb-slide-title",style:`text-align: ${o}; ${n.style||""}`},s),B("div",{class:"bb-slide-content"},t.content)])]}},mt={left:t=>B("div",{class:"bb-left"},t.content),center:t=>B("div",{class:"bb-center"},t.content),right:t=>B("div",{class:"bb-right"},t.content)},wt={a:(t,e)=>{const n=M(t,e.data.raw)._default||"";return B("a",{id:`user-anchor-${n.trim()}`,name:`user-anchor-${n.trim()}`},t.content)},goto:(t,e)=>{const n=M(t,e.data.raw)._default||"";return B("a",{href:`#user-anchor-${n.trim()}`},t.content)}},yt=["arial","book antiqua","courier new","georgia","tahoma","times new roman","trebuchet ms","verdana"],vt={thin:"100",extralight:"200",light:"300",regular:"400",medium:"500",semibold:"600",bold:"700",extrabold:"800",black:"900"},$t=["ital","opsz","slnt","wdth","wght"],xt=/(?[a-zA-Z]*)?\s?(?[0-9]*)?\s?(?italic)?/;const kt=B("div",{class:"bb-email-header"},""),Tt=B("div",{class:"bb-email-footer"},B("div",{class:"bb-email-button"},"")),At={row:t=>B("div",{class:"bb-row"},t.content),column:(t,e)=>{const n=M(t,e.data.raw)._default||"8",s=n.startsWith("span")?`column-width-${n}`:`column-width-span${n}`;return B("div",{class:"bb-column","data-span":s},t.content)}},St=["init","click","change","input","dblclick","mouseenter","mouseleave","scroll"];const Ct=["me","them","right","left"],jt={textmessage:(t,e)=>{const n=t.content.filter(t=>u(t)&&"message"===t.tag);if(n.forEach(t=>{t.isValid=!0}),!n.length)return Z(t,e.data.raw);const s=M(t,e.data.raw)._default||"Recipient",r=""!==s.trim()?s:"Recipient";return B("div",{class:"bb-textmessage"},[B("div",{class:"bb-textmessage-name"},r),B("div",{class:"bb-textmessage-overflow"},[B("div",{class:"bb-textmessage-content"},t.content)])])},message:(t,e)=>{if(!t.isValid)return Z(t,e.data.raw);let n=M(t,e?.data?.raw)?._default?.toLowerCase();Ct.includes(n)&&"right"!==n||(n="me"),"left"===n&&(n="them");return B("div",{class:"me"===n?"bb-message-me":"bb-message-them"},[B("div",{class:"bb-message-content"},t.content)])}},_t={...bt,...mt,...wt,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=M(t,e.data.raw)?._default||"",r=t.content.filter(t=>u(t)&&"keyframe"===t.tag).map(t=>{t.isValid=!0;const n=M(t,e.data.raw)._default||"";t.ident=n+(n.match(/^\d+$/)?"%":"");const s=t.content.filter(d).join("").replaceAll(/[\[\]\{\}]/g,"");return t.formatted=`${t.ident}{ ${s} }`,t}),o=`@keyframes ${n}${s} { ${r.map(t=>t.formatted).join("\n")} }`;return e.data.styles.push(o),[]},bg:(t,e)=>{const n=M(t,e.data.raw)._default;return B("div",{style:`background-color: ${n};`,class:"bb-background"},t.content)},block:(t,e)=>{const n="block",s=(M(t,e.data.raw)._default||n).toLowerCase(),r=["block","dice","dice10","setting","warning","storyteller","announcement","important","question","encounter","information","character","treasure"].includes(s)?s:n;return B("table",{class:"bb-block","data-bb-block":r},[B("tbody",[B("tr",[B("td",{class:"bb-block-icon"}),B("td",{class:"bb-block-content"},t.content)])])])},blockquote:(t,e)=>{const n=M(t,e.data.raw)._default||"";return B("div",{class:"bb-blockquote"},[B("div",{class:"bb-blockquote-left"}),B("div",{class:"bb-blockquote-content"},[t.content,B("div",{class:"bb-blockquote-speaker"},""!==n?`- ${n}`:"")]),B("div",{class:"bb-blockquote-right"})])},border:(t,e)=>{const n=M(t,e.data.raw)._default;return B("div",{style:`border: ${n};`,class:"bb-border"},t.content)},br:()=>B("br",{},null),centerblock:(t,e)=>{const n=M(t,e.data.raw)._default||"50";return B("div",{style:`margin: 0 auto; width: ${n}%`},t.content)},check:(t,e)=>{const n=M(t,e.data.raw)._default||"dot";return B("div",{class:"bb-check","data-type":n},t.content)},class:(t,e)=>{const n=M(t),s=n.name||n._default;e.data.previewing||e.data.commonGUID||(e.data.commonGUID="post-"+Math.random().toString(36).substring(2,7));const r=e.data.previewing?"preview":e.data.commonGUID,o=s+"__"+r,a=t.content.filter(d).map(t=>t.replaceAll("{post_id}",r).replaceAll(/[\[\]\{\}]/g,""));let i="";const c=[];return["hover","focus","active","focus-within","focus-visible"].includes(n.state?.toLowerCase())&&(i=":"+n.state.toLowerCase()),n.selector&&(i=n.selector.replace(/[,{}\\\n]/g,"")),n.minWidth?.match(/^[0-9]+[a-z]+$/)&&c.push(`(min-width: ${n.minWidth})`),n.maxWidth?.match(/^[0-9]+[a-z]+$/)&&c.push(`(max-width: ${n.maxWidth})`),a.unshift(`.${o}${i} {`),a.push("}"),c.length&&(a.unshift(`@media ${c.join(" and ")} {`),a.push("}")),e.data.styles.push(a.join("")),[]},code:t=>({isWhitespaceSensitive:!0,content:["```"+(M(t)._default||"bbcode")+"\n",t.content,"\n```\n"]}),color:t=>{const e=M(t)._default||"";return""===e.trim()?t.content:B("span",{style:`color: ${e}`},t.content)},comment:t=>B("span",{class:"hidden"},t.content),div:(t,e)=>{if(t.gen)return t;const n=M(t,e.data.raw),s=n.style||n._default,r=n.class;if(!r?.trim())return B("div",{style:s},t.content);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,a=r.split(" ").map(t=>t+"__"+o).join(" ");return B("div",{class:a,style:s},t.content)},divide:t=>{const e=(M(t)._default||"").toLowerCase();return B("span",{class:"bb-divide","data-type":e},t.content)},fieldset:(t,e)=>{const n=M(t,e.data.raw)._default||"";return B("fieldset",{class:"bb-fieldset"},[B("legend",{class:"bb-fieldset-legend"},n),B("div",{class:"bb-fieldset"},t.content)])},fa:t=>{const e=t.attrs;let n=e.style||"";return n+=e["primary-color"]?`--fa-primary-color: ${e["primary-color"]};`:"",n+=e["secondary-color"]?`--fa-secondary-color: ${e["secondary-color"]};`:"",n+=e["primary-opacity"]?`--fa-primary-opacity: ${e["primary-opacity"]};`:"",n+=e["secondary-opacity"]?`--fa-secondary-opacity: ${e["secondary-opacity"]};`:"",n+=e["rotate-angle"]?`--fa-rotate-angle: ${e["rotate-angle"]};`:"",B("i",{"data-bbcode-fa":null},[B("i",{class:(t.content||[]).join(""),style:n,"data-fa-transform":e["fa-transform"]||""},[])])},font:(t,e)=>{const n=M(t,e.data.raw),s=n?._default||n?.family||n?.name;if(!s||""===s.trim())return t.content;if(yt.includes(s.trim().toLowerCase()))return B("span",{style:`font-family: '${s}'`},t.content);const r=(t=>{let e={ital:0,wght:400};if(t?.style){const n=t.style.trim().toLowerCase(),s=xt.exec(n).groups||{};s?.italic&&(e.ital=1);const r=s.weight;r&&r>=0&&r<=900?e.wght=r:Object.keys(vt).includes(s.named_weight||"")&&(e.wght=vt[s.named_weight]),e={...e,...Object.fromEntries(Object.entries(t).filter(([t])=>$t.includes(t)))}}return e})(n),o=((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(",")))(s,r);e.data.fonts.add(o);const a=1===r.ital?"italic":"normal",i=Object.entries(r).filter(([t])=>"wght"!==t&&"ital"!==t);let c="";return i.length&&(c="font-variation-settings: "+i.map(([t,e])=>`'${t}' ${e}`).join(", ")+";"),B("span",{style:`font-family: '${s}'; font-weight: ${r.wght}; font-style: ${a}; ${c}`,"data-font":o},t.content)},h:t=>B("h1",{},t.content),h1:t=>B("h1",{},t.content),h2:t=>B("h2",{},t.content),h3:t=>B("h3",{},t.content),h4:t=>B("h4",{},t.content),h5:t=>B("h5",{},t.content),h6:t=>B("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}(M(t)._default).toString();return B("div","0"===e?{class:"bb-height-restrict"}:{class:"bb-height-restrict",style:`height: ${e}px;`},t.content)},highlight:t=>B("span",{class:"bb-highlight"},t.content),icode:t=>({isWhitespaceSensitive:!0,content:["`",t.content,"`"]}),imagefloat:t=>{const e=M(t)._default||"";return B("div",{class:`bb-float-${e}`},t.content)},inlinespoiler:t=>B("span",{class:"bb-inline-spoiler"},t.content),justify:t=>B("div",{class:"bb-justify"},t.content),keyframe:(t,e)=>t.isValid?[]:[R(t,e.data.raw),...t.content,J(t,e.data.raw)],mail:t=>{const e=t.attrs;let n={mailOption:(e.type||"send").toLowerCase(),person:e.person||"Unknown",subject:e.subject||"Empty"};return B("div",{class:"bb-email","data-bb-email":n.mailOption},[kt,(o=n.person,B("div",{class:"bb-email-address"},o)),(r=n.subject,B("div",{class:"bb-email-subject"},r)),(s=t.content,B("div",{class:"bb-email-content"},s)),Tt]);var s,r,o},newspaper:t=>B("div",{class:"bb-newspaper"},t.content),nobr:t=>({disableLineBreakConversion:!0,content:t.content}),note:t=>B("div",{class:"bb-note"},[B("div",{class:"bb-note-tape"},""),B("div",{class:"bb-note-content"},[t.content,B("div",{class:"bb-note-footer"},"")])]),ooc:t=>B("div",{class:"bb-ooc"},t.content),pindent:t=>B("span",{class:"bb-pindent"},t.content),plain:t=>t.content,print:t=>{const e="print",n=(M(t)._default||e).toLowerCase(),s=["print","line","graph","parchment"].includes(n)?n:e;return B("div",{class:s===e?"bb-print":`bb-print-${s}`},t.content)},progress:t=>{const e=M(t)._default;return B("div",{class:"bb-progress"},[B("div",{class:"bb-progress-text"},t.content),B("div",{class:"bb-progress-bar",style:`width: calc(${e}% - 6px)`},""),B("div",{class:"bb-progress-bar-other"},"")])},quote:(t,e)=>{const n=M(t,e.data.raw);return"\n"===t.content[0]&&t.content.shift(),[`\n[${t.tag}="${n._default}"]\n\n`,...t.content,"\n\n[/quote]\n"]},...At,thinprogress:(t,e)=>{const n=M(t,e.data.raw)._default;return B("div",{class:"bb-progress-thin"},[B("div",{class:"bb-progress-text"},t.content),B("div",{class:"bb-progress-bar",style:`width: calc(${n}% - 6px)`},""),B("div",{class:"bb-progress-bar-other"},"")])},savenl:t=>({isWhitespaceSensitive:!0,content:t.content}),sh:t=>B("h2",{},t.content),script:(t,e)=>{const n=M(t,e.data.raw);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,r=St.includes(n.on?.toLowerCase()||"init")&&n.on?.toLowerCase()||"init",o={id:s,class:n.class||"",on:r,version:n.version||"",content:t.content.join("")};return e.data.bbscripts.push(o),[]},scroll:(t,e)=>{const n=function(t){const e=t&&""!==t.trim()?t.replace(/[^\d.]/g,""):0;return e&&e>=0&&e<=700?e:0===e?0:700}(M(t,e.data.raw)._default);return B("div",{class:"bb-scroll",style:`height: ${n}px`},t.content)},side:t=>{const e=M(t)._default||"left";return B("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,a=3,i=.2,c=7,l=1;if(s&&(e=s[1])){switch(n.unit=(s[2]||"").toLowerCase(),n.unit){case"px":e>r?e=r:ea?e=a:ec?e=c:e{const n=M(t,e.data.raw)._default;return B("details",{class:"bb-spoiler"},[B("summary",{},"Spoiler"+(n?`: ${n}`:"")),B("div",{class:"bb-spoiler-content"},t.content)])},sub:t=>B("sub",{},t.content),sup:t=>B("sup",{},t.content),tab:(t,e)=>{if(!t.isValid)return Z(t,e.data.raw);const n=M(t,e.data.raw),s=n?._default||n?.name||"Tab",r=`tab-${s.replace(/\W/g,"_")}-${nt()}`;return[B("input",{type:"radio",id:r,name:"tab-group-"+t.groupId,class:"bb-tab",checked:t.open}),B("label",{class:"bb-tab-label",for:r,style:n.style},s),B("div",{class:"bb-tab-content"},t.content)]},tabs:(t,e)=>{const n=t.content.filter(t=>u(t)&&"tab"===t.tag),s=nt();return n.forEach(t=>{t.isValid=!0,t.groupId=s}),n.length?(n[0].open=!0,B("div",{class:"bb-tabs"},n)):Z(t,e.data.raw)},...jt,b:t=>B("span",{class:"bbcode-b"},t.content),i:t=>t.gen?t:B("span",{class:"bbcode-i"},t.content),u:t=>B("span",{class:"bbcode-u"},t.content),s:t=>B("span",{class:"bbcode-s"},t.content)},Lt=Object.keys(_t),Et=function t(e,n=ut){const s=t=>{function r(t,r){return n(e,t,r,s.options||{})}return s.options=Object.assign(s.options||{},t),r.options=s.options,r};return s.extend=function(r){return t(r(e,s.options),n)},s}(_t);function Ot(t){return t.replaceAll(K,"").replaceAll(Q,"").replaceAll("\n"+X,"").replaceAll(X+"\n","").replaceAll(X,"")}function Nt(t){return t.replaceAll("
\n
    ","").replaceAll("\n
      ","").replaceAll("\n
      ","")}function Wt(t,e){const n=e.hoistMap;for(const[e,s]of Object.entries(n)){const n=s.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">");t=t.replaceAll(e,n)}return t}function It(t,e){if(0===e.styles.length)return t;return'"+t}function Ut(t,e){if(0===e.bbscripts.length)return t;return e.bbscripts.map(t=>``).join("")+t}function Vt(t,e){const n={};let s=0;const r=(e,s,r,o=!1)=>{const a=nt();return-1!==s?(n[a]=t.substring(e,s),t=t.substring(0,e)+a+t.substring(s)):(n[a]=t.substring(e),t=t.substring(0,e)+a+r),o&&(n[a].startsWith("\n")&&(n[a]=n[a].substring(1)),n[a].endsWith("\n")&&(n[a]=n[a].substring(0,n[a].length-1))),e+a.length+r.length};for(;-1!==(s=H(t,tt,s));){const e=tt.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|$)"),i=H(t,a,s+r.length),c=nt();n[c]=-1!==i?t.substring(s+r.length+o.length,i):t.substring(s+r.length+o.length);const l=`[saveNL]\n${r}${o}${c}\n${r}\n[/saveNL]`;t=t.substring(0,s)+l+(-1!==i?t.substring(i+1+r.length):""),s+=l.length}else if(e.groups?.bbcode){const n=e.groups.bbcode,o=`[/${e.groups.bbcodeTag.toLowerCase()}]`,a=t.toLowerCase().indexOf(o,s+1);s=r(s+n.length,a,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 qt(t,e){let n=0;for(;-1!==(n=H(t,et,n));){const e=et.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]}function Dt(t){let e={};const n=[Vt,qt];for(const s of n)[t,e]=s(t,e);return[t,e]}const zt={onlyAllowTags:[...Lt],caseFreeTags:!0,contextFreeTags:["plain","code","icode","class","fa"],enableEscapeTags:!0,onError:t=>{zt.previewing&&console.warn(t.message,t.lineNumber,t.columnNumber)}},Gt=Et();function Ft(t){return t.walk(t=>(t.tag&&(t.tag=t.tag.toLowerCase()),t))}const Pt=new Set(["b","i","u","s"]),Bt=new RegExp(`\\[(?:${Lt.filter(t=>"savenl"!==t&&!Pt.has(t)).join("|")})(?=[\\s=\\]])`,"i");t.RpNBBCode=(t,e)=>{const n=[Ft,Gt];e.preserveWhitespace&&n.push(t=>it(t)),n.push(t=>ot(t),lt);const[s,r]=Dt(t);return function(t){const e="function"==typeof t?[t]:t||[],n=()=>"";return{process(t,s){const r=s||{skipParse:!1,parser:W,render:n,data:null},o=r.parser||W,a=r.render,i=r.data||null;if("function"!=typeof o)throw new Error(D);const c=r.skipParse&&Array.isArray(t)?t:o(t,r);let l=r.skipParse&&Array.isArray(t)?G(t||[],r):G(c,r);for(let t=0;treduce(acc, key, obj), def);\n}\nfunction getNodeLength(node) {\n if (isTagNode(node) && Array.isArray(node.content)) {\n return node.content.reduce((count, contentNode)=>{\n return count + getNodeLength(contentNode);\n }, 0);\n }\n if (isStringNode(node)) {\n return String(node).length;\n }\n return 0;\n}\nfunction appendToNode(node, value) {\n if (Array.isArray(node.content)) {\n node.content.push(value);\n }\n}\n/**\n * Replaces \" to &qquot;\n * @param {string} value\n */ function escapeAttrValue(value) {\n return 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/**\n * @deprecated use escapeAttrValue\n */ const escapeHTML = escapeAttrValue;\n/**\n * Accept name and value and return valid html5 attribute string\n */ function attrValue(name, value) {\n // in case of performance\n switch(typeof value){\n case 'boolean':\n return value ? `${name}` : '';\n case 'number':\n return `${name}=\"${value}\"`;\n case 'string':\n return `${name}=\"${escapeAttrValue(value)}\"`;\n case 'object':\n return `${name}=\"${escapeAttrValue(JSON.stringify(value))}\"`;\n default:\n return '';\n }\n}\n/**\n * Transforms attrs to html params string\n * @example\n * attrsToString({ 'foo': true, 'bar': bar' }) => 'foo=\"true\" bar=\"bar\"'\n */ function attrsToString(values) {\n // To avoid some malformed attributes\n if (values == null) {\n return '';\n }\n return keysReduce(values, (arr, key, obj)=>[\n ...arr,\n attrValue(key, obj[key])\n ], [\n ''\n ]).join(' ');\n}\n/**\n * Gets value from\n * @example\n * getUniqAttr({ 'foo': true, 'bar': bar' }) => 'bar'\n */ function getUniqAttr(attrs) {\n return keysReduce(attrs || {}, (res, key, obj)=>obj[key] === key ? obj[key] : null, null);\n}\nexport { attrsToString, attrValue, appendToNode, escapeHTML, escapeAttrValue, getNodeLength, getUniqAttr, isTagNode, isStringNode, isEOL };\n","import { OPEN_BRAKET, CLOSE_BRAKET, SLASH } from './char';\nimport { getUniqAttr, getNodeLength, appendToNode, attrsToString, attrValue, isTagNode } from './helpers';\nconst getTagAttrs = (tag, params)=>{\n const uniqAttr = getUniqAttr(params);\n if (uniqAttr) {\n const tagAttr = attrValue(tag, uniqAttr);\n const attrs = {\n ...params\n };\n delete attrs[String(uniqAttr)];\n const attrsStr = attrsToString(attrs);\n return `${tagAttr}${attrsStr}`;\n }\n return `${tag}${attrsToString(params)}`;\n};\nconst renderContent = (content, openTag, closeTag)=>{\n const toString = (node)=>{\n if (isTagNode(node)) {\n return node.toString({\n openTag,\n closeTag\n });\n }\n return String(node);\n };\n if (Array.isArray(content)) {\n return content.reduce((r, node)=>{\n if (node !== null) {\n return r + toString(node);\n }\n return r;\n }, '');\n }\n if (content) {\n return toString(content);\n }\n return null;\n};\nexport class 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 setStart(value) {\n this.start = value;\n }\n setEnd(value) {\n this.end = value;\n }\n get length() {\n return getNodeLength(this);\n }\n toTagStart({ openTag = OPEN_BRAKET, closeTag = CLOSE_BRAKET } = {}) {\n const tagAttrs = getTagAttrs(String(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 const newNode = new TagNode(String(this.tag).toLowerCase(), this.attrs, this.content);\n if (this.start) {\n newNode.setStart(this.start);\n }\n if (this.end) {\n newNode.setEnd(this.end);\n }\n return newNode;\n }\n toString({ openTag = OPEN_BRAKET, closeTag = CLOSE_BRAKET } = {}) {\n const content = this.content ? renderContent(this.content, openTag, closeTag) : '';\n const tagStart = this.toTagStart({\n openTag,\n closeTag\n });\n if (this.content === null || Array.isArray(this.content) && this.content.length === 0) {\n return tagStart;\n }\n return `${tagStart}${content}${this.toTagEnd({\n openTag,\n closeTag\n })}`;\n }\n static create(tag, attrs = {}, content = null, start) {\n const node = new TagNode(tag, attrs, content);\n if (start) {\n node.setStart(start);\n }\n return node;\n }\n static isOf(node, type) {\n return node.tag === type;\n }\n constructor(tag, attrs, content){\n this.tag = tag;\n this.attrs = attrs;\n this.content = content;\n }\n}\n","import { OPEN_BRAKET, CLOSE_BRAKET, SLASH } from '@bbob/plugin-helper';\n// type, value, line, row, start pos, end pos\nconst TOKEN_TYPE_ID = 't'; // 0;\nconst TOKEN_VALUE_ID = 'v'; // 1;\nconst TOKEN_COLUMN_ID = 'r'; // 2;\nconst TOKEN_LINE_ID = 'l'; // 3;\nconst TOKEN_START_POS_ID = 's'; // 4;\nconst TOKEN_END_POS_ID = 'e'; // 5;\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';\nconst getTokenValue = (token)=>{\n if (token && typeof token[TOKEN_VALUE_ID] !== 'undefined') {\n return token[TOKEN_VALUE_ID];\n }\n return '';\n};\nconst getTokenLine = (token)=>token && token[TOKEN_LINE_ID] || 0;\nconst getTokenColumn = (token)=>token && token[TOKEN_COLUMN_ID] || 0;\nconst getStartPosition = (token)=>token && token[TOKEN_START_POS_ID] || 0;\nconst getEndPosition = (token)=>token && token[TOKEN_END_POS_ID] || 0;\nconst 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};\nconst 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};\nconst 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 tokenToText = (token, openTag = OPEN_BRAKET, closeTag = CLOSE_BRAKET)=>{\n let text = openTag;\n text += getTokenValue(token);\n text += closeTag;\n return text;\n};\n/**\n * @export\n * @class Token\n */ class Token {\n get type() {\n return this[TOKEN_TYPE_ID];\n }\n isEmpty() {\n return this[TOKEN_TYPE_ID] === 0 || 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 getStart() {\n return getStartPosition(this);\n }\n getEnd() {\n return getEndPosition(this);\n }\n toString({ openTag = OPEN_BRAKET, closeTag = CLOSE_BRAKET } = {}) {\n return tokenToText(this, openTag, closeTag);\n }\n constructor(type, value, row = 0, col = 0, start = 0, end = 0){\n this[TOKEN_LINE_ID] = row;\n this[TOKEN_COLUMN_ID] = col;\n this[TOKEN_TYPE_ID] = type || 0;\n this[TOKEN_VALUE_ID] = String(value);\n this[TOKEN_START_POS_ID] = start;\n this[TOKEN_END_POS_ID] = end;\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 START_POS_ID = TOKEN_START_POS_ID;\nexport const END_POS_ID = TOKEN_END_POS_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';\nexport class CharGrabber {\n skip(num = 1, silent) {\n this.c.pos += num;\n if (this.o && this.o.onSkip && !silent) {\n this.o.onSkip();\n }\n }\n hasNext() {\n return this.c.len > this.c.pos;\n }\n getCurr() {\n if (typeof this.s[this.c.pos] === 'undefined') {\n return '';\n }\n return this.s[this.c.pos];\n }\n getPos() {\n return this.c.pos;\n }\n getLength() {\n return this.c.len;\n }\n getRest() {\n return this.s.substring(this.c.pos);\n }\n getNext() {\n const nextPos = this.c.pos + 1;\n return nextPos <= this.s.length - 1 ? this.s[nextPos] : null;\n }\n getPrev() {\n const prevPos = this.c.pos - 1;\n if (typeof this.s[prevPos] === 'undefined') {\n return null;\n }\n return this.s[prevPos];\n }\n isLast() {\n return this.c.pos === this.c.len;\n }\n includes(val) {\n return this.s.indexOf(val, this.c.pos) >= 0;\n }\n grabWhile(condition, silent) {\n let start = 0;\n if (this.hasNext()) {\n start = this.c.pos;\n while(this.hasNext() && condition(this.getCurr())){\n this.skip(1, silent);\n }\n }\n return this.s.substring(start, this.c.pos);\n }\n grabN(num = 0) {\n return this.s.substring(this.c.pos, this.c.pos + num);\n }\n /**\n * Grabs rest of string until it find a char\n */ substrUntilChar(char) {\n const { pos } = this.c;\n const idx = this.s.indexOf(char, pos);\n return idx >= 0 ? this.s.substring(pos, idx) : '';\n }\n constructor(source, options = {}){\n this.s = source;\n this.c = {\n pos: 0,\n len: source.length\n };\n this.o = options;\n }\n}\n/**\n * Creates a grabber wrapper for source string, that helps to iterate over string char by char\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 */ 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 */ export const unquote = (str)=>str.replace(BACKSLASH + QUOTEMARK, QUOTEMARK);\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 = '!';\nexport function createTokenOfType(type, value, r = 0, cl = 0, p = 0, e = 0) {\n return new Token(type, value, r, cl, p, e);\n}\nconst STATE_WORD = 0;\nconst STATE_TAG = 1;\nconst STATE_TAG_ATTRS = 2;\nconst TAG_STATE_NAME = 0;\nconst TAG_STATE_ATTR = 1;\nconst TAG_STATE_VALUE = 2;\nconst WHITESPACES = [\n SPACE,\n TAB\n];\nconst SPECIAL_CHARS = [\n EQ,\n SPACE,\n TAB\n];\nconst END_POS_OFFSET = 2; // length + start position offset\nconst isWhiteSpace = (char)=>WHITESPACES.indexOf(char) >= 0;\nconst isEscapeChar = (char)=>char === BACKSLASH;\nconst isSpecialChar = (char)=>SPECIAL_CHARS.indexOf(char) >= 0;\nconst isNewLine = (char)=>char === N;\nconst unq = (val)=>unquote(trimChar(val, QUOTEMARK));\nexport function createLexer(buffer, options = {}) {\n let row = 0;\n let prevCol = 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 || []).filter(Boolean).map((tag)=>tag.toLowerCase());\n const caseFreeTags = options.caseFreeTags || false;\n const nestedMap = new Map();\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 isCharReserved = (char)=>RESERVED_CHARS.indexOf(char) >= 0;\n const isCharToken = (char)=>NOT_CHAR_TOKENS.indexOf(char) === -1;\n const isEscapableChar = (char)=>char === openTag || char === closeTag || char === BACKSLASH;\n const onSkip = ()=>{\n col++;\n };\n const checkContextFreeMode = (name, isClosingTag)=>{\n if (contextFreeTag !== '' && isClosingTag) {\n contextFreeTag = '';\n }\n if (contextFreeTag === '' && contextFreeTags.includes(name.toLowerCase())) {\n contextFreeTag = name;\n }\n };\n const chars = createCharGrabber(buffer, {\n onSkip\n });\n /**\n * Emits newly created token to subscriber\n */ function emitToken(type, value, startPos, endPos) {\n const token = createTokenOfType(type, value, row, prevCol, startPos, endPos);\n onToken(token);\n prevCol = col;\n tokenIndex += 1;\n tokens[tokenIndex] = token;\n }\n function nextTagState(tagChars, isSingleValueTag, masterStartPos) {\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 = nextChar && 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;\n // return (isEQ || isWS) === false;\n }\n return true;\n };\n const name = tagChars.grabWhile(validAttrValue);\n tagChars.skip();\n emitToken(TYPE_ATTR_VALUE, unq(name));\n if (tagChars.getPrev() === QUOTEMARK) {\n prevCol++;\n }\n if (tagChars.isLast()) {\n return TAG_STATE_NAME;\n }\n return TAG_STATE_ATTR;\n }\n const start = masterStartPos + tagChars.getPos() - 1;\n const validName = (char)=>!(char === EQ || isWhiteSpace(char) || tagChars.isLast());\n const name = tagChars.grabWhile(validName);\n emitToken(TYPE_TAG, name, start, masterStartPos + tagChars.getLength() + 1);\n checkContextFreeMode(name);\n tagChars.skip();\n prevCol++;\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 (nextChar && 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 startPos = chars.getPos() - 1;\n const name = chars.grabWhile((char)=>char !== closeTag);\n const endPos = startPos + name.length + END_POS_OFFSET;\n chars.skip(); // skip closeTag\n emitToken(TYPE_TAG, name, startPos, endPos);\n checkContextFreeMode(name, isClosingTag);\n return STATE_WORD;\n }\n return STATE_TAG_ATTRS;\n }\n function stateAttrs() {\n const startPos = chars.getPos();\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, startPos);\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 prevCol = 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 prevCol++;\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 (nextChar && 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 word = chars.grabWhile(isChar);\n emitToken(TYPE_WORD, word);\n return STATE_WORD;\n }\n const word = chars.grabWhile(isCharToken);\n emitToken(TYPE_WORD, word);\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(tokenValue) {\n const value = openTag + SLASH + tokenValue;\n if (nestedMap.has(value)) {\n return !!nestedMap.get(value);\n } else {\n const status = caseFreeTags ? buffer.toLowerCase().indexOf(value.toLowerCase()) > -1 : buffer.indexOf(value) > -1;\n nestedMap.set(value, status);\n return status;\n }\n }\n return {\n tokenize,\n isTokenNested\n };\n}\n","import { CLOSE_BRAKET, OPEN_BRAKET, TagNode, isTagNode } from \"@bbob/plugin-helper\";\nimport { createLexer } from \"./lexer\";\nclass NodeList {\n last() {\n if (Array.isArray(this.n) && this.n.length > 0 && typeof this.n[this.n.length - 1] !== \"undefined\") {\n return this.n[this.n.length - 1];\n }\n return null;\n }\n flush() {\n return this.n.length ? this.n.pop() : false;\n }\n push(value) {\n this.n.push(value);\n }\n toArray() {\n return this.n;\n }\n constructor(){\n this.n = [];\n }\n}\nconst createList = ()=>new NodeList();\nfunction 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 const caseFreeTags = options.caseFreeTags || false;\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 */ 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 */ const nestedTagsMap = new Set();\n function isTokenNested(token) {\n const tokenValue = token.getValue();\n const value = caseFreeTags ? tokenValue.toLowerCase() : tokenValue;\n const { isTokenNested } = tokenizer || {};\n if (!nestedTagsMap.has(value) && isTokenNested && isTokenNested(value)) {\n nestedTagsMap.add(value);\n return true;\n }\n return nestedTagsMap.has(value);\n }\n /**\n * @private\n */ function isTagNested(tagName) {\n return Boolean(nestedTagsMap.has(caseFreeTags ? tagName.toLowerCase() : tagName));\n }\n /**\n * @private\n */ function 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 */ function flushTagNodes() {\n if (tagNodes.flush()) {\n tagNodesAttrName.flush();\n }\n }\n /**\n * @private\n */ function getNodes() {\n const lastNestedNode = nestedNodes.last();\n if (lastNestedNode && isTagNode(lastNestedNode)) {\n return lastNestedNode.content;\n }\n return nodes.toArray();\n }\n /**\n * @private\n */ function appendNodeAsString(nodes, node, isNested = true) {\n if (Array.isArray(nodes) && typeof node !== \"undefined\") {\n nodes.push(node.toTagStart({\n openTag,\n closeTag\n }));\n if (Array.isArray(node.content) && node.content.length) {\n node.content.forEach((item)=>{\n nodes.push(item);\n });\n if (isNested) {\n nodes.push(node.toTagEnd({\n openTag,\n closeTag\n }));\n }\n }\n }\n }\n /**\n * @private\n */ function appendNodes(nodes, node) {\n if (Array.isArray(nodes) && typeof node !== \"undefined\") {\n if (isTagNode(node)) {\n if (isAllowedTag(node.tag)) {\n nodes.push(node.toTagNode());\n } else {\n appendNodeAsString(nodes, node);\n }\n } else {\n nodes.push(node);\n }\n }\n }\n /**\n * @private\n * @param {Token} token\n */ function handleTagStart(token) {\n flushTagNodes();\n const tagNode = TagNode.create(token.getValue(), {}, [], {\n from: token.getStart(),\n to: token.getEnd()\n });\n const isNested = isTokenNested(token);\n tagNodes.push(tagNode);\n if (isNested) {\n nestedNodes.push(tagNode);\n } else {\n const nodes = getNodes();\n appendNodes(nodes, tagNode);\n }\n }\n /**\n * @private\n * @param {Token} token\n */ function handleTagEnd(token) {\n const tagName = token.getValue().slice(1);\n const lastNestedNode = nestedNodes.flush();\n flushTagNodes();\n if (lastNestedNode) {\n const nodes = getNodes();\n if (isTagNode(lastNestedNode)) {\n lastNestedNode.setEnd({\n from: token.getStart(),\n to: token.getEnd()\n });\n }\n appendNodes(nodes, lastNestedNode);\n } else if (!isTagNested(tagName)) {\n const nodes = getNodes();\n appendNodes(nodes, token.toString({\n openTag,\n closeTag\n }));\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 tagName: tag,\n lineNumber: line,\n columnNumber: column\n });\n }\n }\n /**\n * @private\n * @param {Token} token\n */ function 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 */ function handleNode(token) {\n /**\n * @type {TagNode}\n */ const activeTagNode = tagNodes.last();\n const tokenValue = token.getValue();\n const isNested = isTagNested(token.toString());\n const nodes = getNodes();\n if (activeTagNode !== null) {\n if (token.isAttrName()) {\n tagNodesAttrName.push(tokenValue);\n const attrName = tagNodesAttrName.last();\n if (attrName) {\n activeTagNode.attr(attrName, \"\");\n }\n } else if (token.isAttrValue()) {\n const attrName = tagNodesAttrName.last();\n if (attrName) {\n activeTagNode.attr(attrName, tokenValue);\n tagNodesAttrName.flush();\n } else {\n activeTagNode.attr(tokenValue, tokenValue);\n }\n } else if (token.isText()) {\n if (isNested) {\n activeTagNode.append(tokenValue);\n } else {\n appendNodes(nodes, tokenValue);\n }\n } else if (token.isTag()) {\n // if tag is not allowed, just pass it as is\n appendNodes(nodes, token.toString({\n openTag,\n closeTag\n }));\n }\n } else if (token.isText()) {\n appendNodes(nodes, tokenValue);\n } else if (token.isTag()) {\n // if tag is not allowed, just pass it as is\n appendNodes(nodes, token.toString({\n openTag,\n closeTag\n }));\n }\n }\n /**\n * @private\n * @param {Token} token\n */ function onToken(token) {\n if (token.isTag()) {\n handleTag(token);\n } else {\n handleNode(token);\n }\n }\n const lexer = opts.createTokenizer ? opts.createTokenizer : createLexer;\n tokenizer = lexer(input, {\n onToken,\n openTag,\n closeTag,\n onlyAllowTags: options.onlyAllowTags,\n contextFreeTags: options.contextFreeTags,\n caseFreeTags: options.caseFreeTags,\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.flush();\n if (lastNestedNode !== null && lastNestedNode && isTagNode(lastNestedNode) && isTagNested(lastNestedNode.tag)) {\n appendNodeAsString(getNodes(), lastNestedNode, false);\n }\n return nodes.toArray();\n}\nexport { parse };\nexport default parse;\n","/* eslint-disable no-plusplus */ const isObj = (value)=>typeof value === 'object' && value !== null;\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 (isObj(tree) && 'content' in tree) {\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 if (isObj(expected) && isObj(actual)) {\n return Object.keys(expected).every((key)=>{\n const ao = actual[key];\n const eo = expected[key];\n if (isObj(eo) && isObj(ao)) {\n return same(eo, ao);\n }\n if (isBool(eo)) {\n return eo !== (ao === null);\n }\n return ao === eo;\n });\n }\n return false;\n}\nexport function match(t, expression, cb) {\n if (Array.isArray(expression)) {\n return iterate(t, (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 });\n }\n return iterate(t, (node)=>same(expression, node) ? cb(node) : node);\n}\n","import { parse } from '@bbob/parser';\nimport { iterate, match } from './utils';\nimport { C1, C2 } from './errors';\nexport function createTree(tree, options) {\n const extendedTree = tree;\n extendedTree.messages = [\n ...extendedTree.messages || []\n ];\n extendedTree.options = {\n ...options,\n ...extendedTree.options\n };\n extendedTree.walk = function walkNodes(cb) {\n return iterate(this, cb);\n };\n extendedTree.match = function matchNodes(expr, cb) {\n return match(this, expr, cb);\n };\n return extendedTree;\n}\nexport default function bbob(plugs) {\n const plugins = typeof plugs === 'function' ? [\n plugs\n ] : plugs || [];\n const mockRender = ()=>\"\";\n return {\n process (input, opts) {\n const options = opts || {\n skipParse: false,\n parser: parse,\n render: mockRender,\n data: null\n };\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(C1);\n }\n // raw tree before modification with plugins\n const raw = options.skipParse && Array.isArray(input) ? input : parseFn(input, options);\n let tree = options.skipParse && Array.isArray(input) ? createTree(input || [], options) : createTree(raw, options);\n for(let idx = 0; idx < plugins.length; idx++){\n const plugin = plugins[idx];\n if (typeof plugin === 'function' && renderFn) {\n const newTree = plugin(tree, {\n parse: parseFn,\n render: renderFn,\n iterate,\n data\n });\n tree = createTree(newTree || tree, options);\n }\n }\n return {\n get html () {\n if (typeof renderFn !== 'function') {\n throw new Error(C2);\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, isTagNode } from '@bbob/plugin-helper';\nconst SELFCLOSE_END_TAG = '/>';\nconst CLOSE_START_TAG = '';\nfunction renderNode(node, options) {\n const { stripTags = false } = options || {};\n if (typeof node === 'undefined' || node === null) {\n return '';\n }\n if (typeof node === 'string' || typeof node === 'number') {\n return String(node);\n }\n if (Array.isArray(node)) {\n return render(node, options);\n }\n if (isTagNode(node)) {\n if (stripTags) {\n return render(node.content, options);\n }\n const attrs = attrsToString(node.attrs);\n if (node.content === null) {\n return START_TAG + node.tag + attrs + SELFCLOSE_END_TAG;\n }\n return START_TAG + node.tag + attrs + END_TAG + render(node.content, options) + CLOSE_START_TAG + node.tag + END_TAG;\n }\n return '';\n}\nexport function render(nodes, options) {\n if (nodes && Array.isArray(nodes)) {\n return nodes.reduce((r, node)=>r + renderNode(node, options), '');\n }\n if (nodes) {\n return renderNode(nodes, options);\n }\n return '';\n}\nexport function html(source, plugins, options) {\n return core(plugins).process(source, {\n ...options,\n render: render\n }).html;\n}\nexport default html;\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 return the default single attribute\n * or return a keyed attribute list\n * @param {import('@bbob/types').TagNode} node bbcode node to process\n * @param {string} [raw] raw string. Only include if the single attribute is allowed to have spaces\n * @returns processed attributes\n */\nconst preprocessAttr = (node, raw) => {\n const keys = Object.keys(node.attrs).join(\" \");\n const vals = Object.values(node.attrs).join(\" \");\n if (keys !== vals) {\n // [tag key=val]\n return node.attrs;\n }\n if (!raw || !node.start) {\n return {\n _default: vals,\n };\n }\n // [tag=attr]\n // node.start.from = 0\n // node.start.to = 10\n const nodeRaw = raw.substring(node.start.from, node.start.to);\n if (!nodeRaw.includes(\"=\")) {\n // [tag] or [tag attr]\n return node.attrs;\n }\n const openTagParts = nodeRaw.split(\"=\");\n if (openTagParts.length !== 2) {\n return node.attrs;\n }\n let val = openTagParts[1].slice(0, -1).trim(); // `attr` or `\"attr\"`\n if (val.startsWith('\"') && val.endsWith('\"')) {\n val = val.slice(1, -1);\n }\n return {\n _default: val,\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, raw) => {\n if (node.start) {\n return raw.substring(node.start.from, node.start.to);\n }\n if (!node.attrs) {\n return `[${node.tag}]`;\n }\n const attrs = preprocessAttr(node, raw);\n if (attrs._default) {\n return `[${node.tag}=${attrs._default}]`;\n } else {\n return node.toTagStart();\n }\n};\n\n/**\n * Attempts to return tag into its original form\n * @returns string of tag end\n */\nconst toOriginalEndTag = (node, raw) => {\n if (node.end) {\n return raw.substring(node.end.from, node.end.to);\n }\n return node.toTagEnd();\n};\n\n/**\n * Attempts to convert a node to a Markdown URL-embed-safe raw tag.\n *\n * Depending on the input node, the result is either:\n * - A single raw tag string if no end node is present\n * - Or an array of such strings if an end node is present\n * @returns {(string|string[])} - A Markdown URL-embed-safe raw tag or an array of tags.\n */\nconst toRawTag = (node, raw) => {\n if (node.end) {\n return [\n toOriginalStartTag(node, raw),\n ...node.content,\n toOriginalEndTag(node, raw),\n ];\n }\n return toOriginalStartTag(node, raw);\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\nconst MD_BROKEN_ORDERED_LIST = \"
\\n
    \";\nconst MD_BROKEN_UNORDERED_LIST = \"\\n
      \";\nconst MD_BROKEN_BLOCKQUOTE = \"\\n
      \";\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 toOriginalEndTag,\n toRawTag,\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 MD_BROKEN_ORDERED_LIST,\n MD_BROKEN_UNORDERED_LIST,\n MD_BROKEN_BLOCKQUOTE,\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 reduceWordsToLines(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 * Reduces the list into lines, so that we can process them by line.\n * Performs in place.\n * @param {(string|Object)[]} words\n */\nconst reduceWordsToLines = (words) => {\n let rightIdx = words.findLastIndex((w) => isString(w) && !isEOL(w)) + 1;\n\n for (let i = rightIdx - 1; i >= 0; i--) {\n if (isString(words[i]) && !isEOL(words[i])) {\n continue;\n }\n if (isEOL(words[i])) {\n if (i !== rightIdx - 1) {\n words.splice(i + 1, rightIdx - i - 1, words.slice(i + 1, rightIdx).join(\"\"));\n }\n rightIdx = i;\n continue;\n }\n if (isObj(words[i])) {\n if (i !== rightIdx - 1) {\n words.splice(i + 1, rightIdx - i - 1, words.slice(i + 1, rightIdx).join(\"\"));\n }\n rightIdx = i;\n }\n }\n\n if (0 !== rightIdx) {\n words.splice(0, rightIdx, words.slice(0, rightIdx).join(\"\"));\n }\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","/**\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 { isTagNode } from \"@bbob/plugin-helper\";\n\nconst CONSECUTIVE_NEWLINE_REGEX = /\\n{2,}/gm;\n\n/**\n * Removes empty lines from a string\n * @param {string} text\n */\nconst removeEmptyLines = (text) => {\n return text.replace(CONSECUTIVE_NEWLINE_REGEX, \"\\n\");\n};\n\n/**\n * Removes empty lines from attributes\n * @type {import('@bbob/types').BBobPluginFunction}\n */\nexport const removeEmptyLinePlugin = (tree) => {\n return tree.walk((node) => {\n if (isTagNode(node) && node.attrs) {\n Object.keys(node.attrs).forEach((key) => {\n if (typeof node.attrs[key] === \"string\") {\n node.attrs[key] = removeEmptyLines(node.attrs[key]);\n }\n });\n }\n return node;\n });\n};\n","import { isTagNode } from \"@bbob/plugin-helper\";\nexport function process(tags, tree, core, options) {\n return tree.walk((node)=>{\n if (isTagNode(node)) {\n const tag = node.tag;\n const tagCallback = tags[tag];\n if (typeof tagCallback === \"function\") {\n return tagCallback(node, core, options);\n }\n }\n return node;\n });\n}\n/**\n * Create a preset plugin for @bbob/core\n */ function createPreset(defTags, processor = process) {\n const presetFactory = (opts)=>{\n presetFactory.options = Object.assign(presetFactory.options || {}, opts);\n function presetExecutor(tree, core) {\n return processor(defTags, tree, core, presetFactory.options || {});\n }\n presetExecutor.options = presetFactory.options;\n return presetExecutor;\n };\n presetFactory.extend = function presetExtend(callback) {\n const newTags = callback(defTags, presetFactory.options);\n return createPreset(newTags, processor);\n };\n return presetFactory;\n}\nexport { createPreset };\nexport default createPreset;\n","import { isStringNode, isTagNode, TagNode } from \"@bbob/plugin-helper\";\nimport {\n generateGUID,\n preprocessAttr,\n regexIndexOf,\n toNode,\n toOriginalEndTag,\n toOriginalStartTag,\n} from \"../utils/common\";\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, options) => {\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 [\n toOriginalStartTag(node, options.data.raw),\n ...node.content,\n toOriginalEndTag(node, options.data.raw),\n ];\n }\n const attrs = preprocessAttr(node, options.data.raw);\n\n if (attrs._default) {\n /** @type {string[]} */\n const customSettings = attrs._default.split(\"|\").map((s) => s.trim());\n const lastValidAlignment = customSettings\n .filter((s) => [\"bright\", \"bcenter\", \"bleft\", \"fleft\", \"fright\"].includes(s))\n .pop();\n if (lastValidAlignment) {\n attrs.align ??= lastValidAlignment;\n }\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 = attrs.align?.toLowerCase() || \"\";\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, options) => {\n if (!node.isValid) {\n // not inside an [accordion] tag\n return [\n toOriginalStartTag(node, options.data.raw),\n ...node.content,\n toOriginalEndTag(node, options.data.raw),\n ];\n }\n const attrs = preprocessAttr(node, options.data.raw);\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 { 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 [a] 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, options) => {\n const attrs = preprocessAttr(node, options.data.raw)._default || \"\";\n return toNode(\n \"a\",\n { id: `user-anchor-${attrs.trim()}`, name: `user-anchor-${attrs.trim()}` },\n node.content,\n );\n },\n goto: (node, options) => {\n const attrs = preprocessAttr(node, options.data.raw)._default || \"\";\n return 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, options.data.raw);\n const fontFamily = attrs?._default || attrs?.family || attrs?.name;\n if (!fontFamily || 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)._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 { 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 = 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, options) => {\n const columnAttrs = preprocessAttr(node, options.data.raw)._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 } 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, options.data.raw);\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 { 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)._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 { isTagNode } from \"@bbob/plugin-helper\";\nimport { preprocessAttr, toNode, toRawTag } from \"../utils/common\";\n\n/**\n * @file Adds textmessage to bbcode\n * @example [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, options) => {\n const messageList = node.content.filter(\n (contentNode) => isTagNode(contentNode) && contentNode.tag === \"message\"\n );\n messageList.forEach((messageNode) => {\n messageNode.isValid = true;\n });\n\n if (!messageList.length) {\n return toRawTag(node, options.data.raw);\n }\n\n const attr = preprocessAttr(node, options.data.raw)._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, options) => {\n if (!node.isValid) {\n return toRawTag(node, options.data.raw);\n }\n\n let option = preprocessAttr(node, options?.data?.raw)?._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","import { createPreset } from \"@bbob/preset\";\nimport { accordionTags } from \"./tags/accordion\";\nimport { alignment } from \"./tags/alignment\";\nimport { anchor } from \"./tags/anchor\";\nimport { animation, keyframe } from \"./tags/animation\";\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 { classStyle } from \"./tags/class\";\nimport { code, icode, savenl } from \"./tags/code\";\nimport { color } from \"./tags/color\";\nimport { comment } from \"./tags/comment\";\nimport { bold, italic, strike, underline } from \"./tags/discourse-core-replacement\";\nimport { div } from \"./tags/div\";\nimport { divide } from \"./tags/divide\";\nimport { fieldset } from \"./tags/fieldset\";\nimport { font } from \"./tags/font\";\nimport { fa } from \"./tags/fontawesome\";\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 { br, nobr } from \"./tags/lineBreak\";\nimport { mail } from \"./tags/mail\";\nimport { newspaper } from \"./tags/newspaper\";\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 { quote } from \"./tags/quote\";\nimport { rowcolumn } from \"./tags/rowcolumn\";\nimport { script } from \"./tags/script\";\nimport { scroll } from \"./tags/scroll\";\nimport { side } from \"./tags/side\";\nimport { size } from \"./tags/size\";\nimport { inlinespoiler, spoiler } from \"./tags/spoiler\";\nimport { sub } from \"./tags/subscript\";\nimport { sup } from \"./tags/superscript\";\nimport { tab, tabs } from \"./tags/tabs\";\nimport { textmessage } from \"./tags/textmessage\";\nimport { thinprogress } from \"./tags/thinprogress\";\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 fa,\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 quote,\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\", \"fa\"];\n\nconst preset = createPreset(tags);\n\nexport { availableTags, tags, preset, preventParsing };\nexport default preset;\n","import { isStringNode, isTagNode } from \"@bbob/plugin-helper\";\nimport { preprocessAttr, toOriginalEndTag, toOriginalStartTag } from \"../utils/common\";\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, options.data.raw)?._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, options.data.raw)._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, options) => {\n if (!node.isValid) {\n return [\n toOriginalStartTag(node, options.data.raw),\n ...node.content,\n toOriginalEndTag(node, options.data.raw),\n ];\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, options) => {\n const color = preprocessAttr(node, options.data.raw)._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, options) => {\n const defaultOp = \"block\";\n const blockAttr = (preprocessAttr(node, options.data.raw)._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, options) => {\n const author = preprocessAttr(node, options.data.raw)._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, options) => {\n const val = preprocessAttr(node, options.data.raw)._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, options) => {\n const percentageInput = preprocessAttr(node, options.data.raw)._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, options) => {\n const attrs = preprocessAttr(node, options.data.raw)._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);\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)._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)._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, options.data.raw);\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)._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, options) => {\n const title = preprocessAttr(node, options.data.raw)._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 * Adds [fa] tag\n * [fa]fa-icon[/fa]\n * [fa style=\"\" fa-transform=\"\"]fa-solid fa-icon[/fa]\n * [fa primary-color=\"\" secondary-color=\"\" primary-opacity=\"\" secondary-opacity=\"\" rotate-angle=\"\"]fa-duotone fa-icon[/fa]\n */\nexport const fa = (node) => {\n const attrs = node.attrs;\n let style = attrs.style || \"\";\n style += attrs[\"primary-color\"] ? `--fa-primary-color: ${attrs[\"primary-color\"]};` : \"\";\n style += attrs[\"secondary-color\"] ? `--fa-secondary-color: ${attrs[\"secondary-color\"]};` : \"\";\n style += attrs[\"primary-opacity\"] ? `--fa-primary-opacity: ${attrs[\"primary-opacity\"]};` : \"\";\n style += attrs[\"secondary-opacity\"]\n ? `--fa-secondary-opacity: ${attrs[\"secondary-opacity\"]};`\n : \"\";\n style += attrs[\"rotate-angle\"] ? `--fa-rotate-angle: ${attrs[\"rotate-angle\"]};` : \"\";\n\n return toNode(\n \"i\",\n {\n \"data-bbcode-fa\": null,\n },\n [\n toNode(\n \"i\",\n {\n class: (node.content || []).join(\"\"),\n style,\n \"data-fa-transform\": attrs[\"fa-transform\"] || \"\",\n },\n [],\n ),\n ],\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)._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, options) => {\n const providedTitle = preprocessAttr(node, options.data.raw)._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)._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)._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 } from \"../utils/common\";\n\n/**\n * rebuild the [quote] tag so that markdown-it engine can parse it for itself\n */\nexport const quote = (node, options) => {\n const attrs = preprocessAttr(node, options.data.raw);\n if (node.content[0] === \"\\n\") {\n node.content.shift();\n }\n return [`\\n[${node.tag}=\"${attrs._default}\"]\\n\\n`, ...node.content, \"\\n\\n[/quote]\\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, options) => {\n const percentageInt = preprocessAttr(node, options.data.raw)._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, options) => {\n const attrs = preprocessAttr(node, options.data.raw)._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)._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","import { isTagNode } from \"@bbob/plugin-helper\";\nimport {\n generateGUID,\n preprocessAttr,\n toNode,\n toRawTag,\n} from \"../utils/common\";\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, options) => {\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\n if (!tabsList.length) {\n return toRawTag(node, options.data.raw);\n }\n\n tabsList[0].open = true;\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, options) => {\n if (!node.isValid) {\n return toRawTag(node, options.data.raw);\n }\n\n const attrs = preprocessAttr(node, options.data.raw);\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","/**\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 if (node.gen) {\n // node is actually a generated node \"i\" made by another tag\n // don't process it\n return node;\n }\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 {\n MD_BROKEN_BLOCKQUOTE,\n MD_BROKEN_ORDERED_LIST,\n MD_BROKEN_UNORDERED_LIST,\n MD_NEWLINE_INJECT,\n MD_NEWLINE_INJECT_COMMENT,\n MD_NEWLINE_PRE_INJECT,\n} 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\nfunction cleanMultilineMDBlocks(raw) {\n const processed = raw\n .replaceAll(MD_BROKEN_ORDERED_LIST, \"\")\n .replaceAll(MD_BROKEN_UNORDERED_LIST, \"\")\n .replaceAll(MD_BROKEN_BLOCKQUOTE, \"\");\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 cleanMultilineMDBlocks,\n renderHoistedCodeBlocks,\n ];\n for (const postprocessor of postprocessors) {\n final = postprocessor(final, data);\n }\n return final;\n}\n","import { ESCAPABLES_REGEX, generateGUID, MD_TABLE_REGEX, 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 bbob from \"@bbob/core\";\nimport { render } from \"@bbob/html\";\nimport { lineBreakPlugin } from \"./plugins/lineBreak\";\nimport { preserveWhitespace } from \"./plugins/preserveWhitespace\";\nimport { removeEmptyLinePlugin } from \"./plugins/removeEmptyLinesInAttr\";\nimport { availableTags, preset, preventParsing } from \"./preset\";\nimport { postprocess } from \"./utils/postprocess\";\nimport { preprocessRaw } from \"./utils/preprocess\";\n\nconst options = {\n onlyAllowTags: [...availableTags],\n caseFreeTags: true,\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(), removeEmptyLinePlugin);\n const [preprocessed, preprocessedData] = preprocessRaw(code);\n return bbob(plugins).process(preprocessed, {\n render,\n ...options,\n data: {\n ...preprocessedData,\n raw: preprocessed,\n previewing: opts.previewing,\n fonts: new Set(),\n styles: [],\n bbscripts: [],\n },\n });\n};\n\nexport { postprocess };\n","let C1 = 'C1';\nlet C2 = 'C2';\nif (process.env.NODE_ENV !== 'production') {\n C1 = '\"parser\" is not a function, please pass to \"process(input, { parser })\" right function';\n C2 = '\"render\" function not defined, please pass to \"process(input, { render })\"';\n}\nexport { C1, C2 };\n"],"names":["N","TAB","EQ","QUOTEMARK","SPACE","OPEN_BRAKET","CLOSE_BRAKET","SLASH","BACKSLASH","isTagNode","el","isStringNode","isEOL","keysReduce","obj","reduce","def","Object","keys","acc","key","getNodeLength","node","Array","isArray","content","count","contentNode","String","length","escapeAttrValue","value","replace","attrValue","name","JSON","stringify","attrsToString","values","arr","join","getTagAttrs","tag","params","uniqAttr","res","tagAttr","attrs","TagNode","attr","this","append","push","appendToNode","setStart","start","setEnd","end","toTagStart","openTag","closeTag","toTagEnd","toTagNode","newNode","toLowerCase","toString","r","renderContent","tagStart","create","isOf","type","constructor","TOKEN_TYPE_ID","getTokenValue","token","isTagEnd","charCodeAt","Token","isEmpty","isNaN","isText","isTag","isAttrName","isAttrValue","isStart","isEnd","getName","slice","getTagName","getValue","getLine","getColumn","getStart","getEnd","text","tokenToText","row","col","CharGrabber","skip","num","silent","c","pos","o","onSkip","hasNext","len","getCurr","s","getPos","getLength","getRest","substring","getNext","nextPos","getPrev","prevPos","isLast","includes","val","indexOf","grabWhile","condition","grabN","substrUntilChar","char","idx","source","options","createCharGrabber","WHITESPACES","SPECIAL_CHARS","isWhiteSpace","isEscapeChar","unq","str","charToRemove","charAt","trimChar","createLexer","buffer","prevCol","tokenIndex","stateMode","tagMode","contextFreeTag","tokens","Math","floor","escapeTags","enableEscapeTags","contextFreeTags","filter","Boolean","map","caseFreeTags","nestedMap","Map","onToken","RESERVED_CHARS","NOT_CHAR_TOKENS","isCharToken","checkContextFreeMode","isClosingTag","chars","emitToken","startPos","endPos","cl","p","e","createTokenOfType","nextTagState","tagChars","isSingleValueTag","masterStartPos","validAttrName","isValue","stateSpecial","validAttrValue","isQM","prevChar","nextChar","isPrevSLASH","isNextEQ","isWS","isNextWS","isSpecialChar","stateTag","currChar","substr","hasInvalidChars","isNoAttrsInTag","stateAttrs","tagStr","tagGrabber","hasSpace","stateWord","fullTagLen","fullTagName","isEscapableChar","isChar","tokenize","isTokenNested","tokenValue","has","get","status","set","NodeList","last","n","flush","pop","toArray","createList","parse","input","opts","onlyAllowTags","tokenizer","nodes","nestedNodes","tagNodes","tagNodesAttrName","nestedTagsMap","Set","isTagNested","tagName","flushTagNodes","getNodes","lastNestedNode","appendNodeAsString","isNested","forEach","item","appendNodes","handleTagStart","tagNode","from","to","add","handleTag","onError","line","column","lineNumber","columnNumber","handleTagEnd","lexer","createTokenizer","activeTagNode","attrName","handleNode","isObj","iterate","t","cb","tree","same","expected","actual","every","exp","some","call","act","ao","eo","createTree","extendedTree","messages","walk","match","expr","expression","renderNode","stripTags","render","toNode","gen","preprocessAttr","raw","vals","_default","nodeRaw","openTagParts","split","trim","startsWith","endsWith","toOriginalStartTag","toOriginalEndTag","toRawTag","regexIndexOf","string","regex","startpos","search","MD_NEWLINE_INJECT","MD_NEWLINE_PRE_INJECT","MD_NEWLINE_INJECT_COMMENT","URL_REGEX_SINGLE_LINE","RegExp","ESCAPABLES_REGEX","MD_TABLE_REGEX","generateGUID","d","Date","getTime","window","performance","now","random","isString","disableLineBreakConversion","reduceWordsToLines","unshift","child","splice","isWhitespaceSensitive","test","words","rightIdx","findLastIndex","w","i","numSpaces","fromCharCode","repeat","CONSECUTIVE_NEWLINE_REGEX","removeEmptyLinePlugin","process","tags","core","tagCallback","SLIDE_TITLE_OPEN","Symbol","SLIDE_TITLE_CLOSE","SLIDE_CLOSE","SLIDE_REGEX","markerToString","marker","accordionTags","accordion","groupId","markedContent","contentArr","newArr","shift","foundIndex","preContent","postContent","groups","slideTitleOpen","slideTitleClose","slideClose","generateSlideMarkersFromContent","generatedSlides","currentSlide","prevMarker","customTitle","generateSlidesFromMarkers","filteredContent","isValid","data","customSettings","lastValidAlignment","align","width","find","classes","style","class","slide","title","isOpen","open","titleAlign","left","right","center","possibleOptions","alignment","anchor","a","id","goto","href","WEB_FONTS","VALID_FONT_STYLES","thin","extralight","light","regular","medium","semibold","bold","extrabold","black","REGISTERED_AXIS","AXES_REGEX","emailHeader","emailFooter","rowcolumn","columnAttrs","columnStyle","EVENTS","ACCEPTED_OPTIONS","textmessage","messageList","messageNode","recipient","message","option","animation","previewing","commonGUID","commonId","keyframes","ident","cleanContent","replaceAll","formatted","styles","bg","color","block","defaultOp","blockAttr","blockOption","blockquote","author","border","br","centerblock","percentageInput","check","nameAttr","classSuffix","className","selector","mediaQuery","state","minWidth","maxWidth","code","inputColor","comment","div","classAttrs","classNames","divide","fieldset","fa","font","fontFamily","family","axes","ital","wght","matches","exec","italic","weight","named_weight","fromEntries","entries","axesParser","url","sort","googleFontApiBuild","fonts","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","note","ooc","pindent","plain","print","printAttr","printOption","progress","percentageInt","quote","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","tabNode","b","u","availableTags","preset","createPreset","defTags","processor","presetFactory","presetExecutor","assign","extend","callback","removeNewlineInjects","cleanMultilineMDBlocks","renderHoistedCodeBlocks","hoistMap","uuid","createClassStyleTagTemplate","createScriptTagTemplate","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","mockRender","skipParse","parser","parseFn","renderFn","Error","plugin","newTree","html","bbob","final","postprocessors","postprocessor"],"mappings":";oPAAA,MAAMA,EAAI,KACJC,EAAM,KAGNC,EAAK,IACLC,EAAY,IACZC,EAAQ,IACRC,EAAc,IACdC,EAAe,IACfC,EAAQ,IACRC,EAAY,KCTlB,SAASC,EAAUC,GACf,MAAqB,iBAAPA,GAA0B,OAAPA,GAAe,QAASA,CAC7D,CACA,SAASC,EAAaD,GAClB,MAAqB,iBAAPA,CAClB,CAEA,SAASE,EAAMF,GACX,OAAOA,IAAOV,CAClB,CACA,SAASa,EAAWC,EAAKC,EAAQC,GAE7B,OADaC,OAAOC,KAAKJ,GACbC,QAAO,CAACI,EAAKC,IAAML,EAAOI,EAAKC,EAAKN,IAAME,EAC1D,CACA,SAASK,EAAcC,GACnB,OAAIb,EAAUa,IAASC,MAAMC,QAAQF,EAAKG,SAC/BH,EAAKG,QAAQV,QAAO,CAACW,EAAOC,IACxBD,EAAQL,EAAcM,IAC9B,GAEHhB,EAAaW,GACNM,OAAON,GAAMO,OAEjB,CACX,CASI,SAASC,EAAgBC,GACzB,OAAOA,EAAMC,QAAQ,KAAM,SAASA,QAAQ,KAAM,QAAQA,QAAQ,KAAM,QAAQA,QAAQ,KAAM,UAAUA,QAAQ,KAAM,UACrHA,QAAQ,gCAAiC,QAC9C,CAMI,SAASC,EAAUC,EAAMH,GAEzB,cAAcA,GACV,IAAK,UACD,OAAOA,EAAQ,GAAGG,IAAS,GAC/B,IAAK,SACD,MAAO,GAAGA,MAASH,KACvB,IAAK,SACD,MAAO,GAAGG,MAASJ,EAAgBC,MACvC,IAAK,SACD,MAAO,GAAGG,MAASJ,EAAgBK,KAAKC,UAAUL,OACtD,QACI,MAAO,GAEnB,CAKI,SAASM,EAAcC,GAEvB,OAAc,MAAVA,EACO,GAEJzB,EAAWyB,GAAQ,CAACC,EAAKnB,EAAKN,IAAM,IAChCyB,EACHN,EAAUb,EAAKN,EAAIM,MACpB,CACH,KACDoB,KAAK,IACZ,CCvEA,MAAMC,EAAc,CAACC,EAAKC,KACtB,MAAMC,ED4EC/B,EC5EsB8B,GD4EF,CAAA,GAAI,CAACE,EAAKzB,EAAKN,IAAMA,EAAIM,KAASA,EAAMN,EAAIM,GAAO,MAAM,MC3EpF,GAAIwB,EAAU,CACV,MAAME,EAAUb,EAAUS,EAAKE,GACzBG,EAAQ,IACPJ,UAEAI,EAAMnB,OAAOgB,IAEpB,MAAO,GAAGE,IADOT,EAAcU,IAEvC,CACI,MAAO,GAAGL,IAAML,EAAcM,IAAS,EAyBpC,MAAMK,EACT,IAAAC,CAAKf,EAAMH,GAIP,YAHqB,IAAVA,IACPmB,KAAKH,MAAMb,GAAQH,GAEhBmB,KAAKH,MAAMb,EAC1B,CACI,MAAAiB,CAAOpB,GACH,ODpBR,SAAsBT,EAAMS,GACpBR,MAAMC,QAAQF,EAAKG,UACnBH,EAAKG,QAAQ2B,KAAKrB,EAE1B,CCgBesB,CAAaH,KAAMnB,EAClC,CACI,QAAAuB,CAASvB,GACLmB,KAAKK,MAAQxB,CACrB,CACI,MAAAyB,CAAOzB,GACHmB,KAAKO,IAAM1B,CACnB,CACI,UAAIF,GACA,OAAOR,EAAc6B,KAC7B,CACI,UAAAQ,EAAWC,QAAEA,EAAUtD,EAAWuD,SAAEA,EAAWtD,GAAiB,IAE5D,MAAO,GAAGqD,IADOlB,EAAYb,OAAOsB,KAAKR,KAAMQ,KAAKH,SACrBa,GACvC,CACI,QAAAC,EAASF,QAAEA,EAAUtD,EAAWuD,SAAEA,EAAWtD,GAAiB,IAC1D,MAAO,GAAGqD,IAAUpD,IAAQ2C,KAAKR,MAAMkB,GAC/C,CACI,SAAAE,GACI,MAAMC,EAAU,IAAIf,EAAQpB,OAAOsB,KAAKR,KAAKsB,cAAed,KAAKH,MAAOG,KAAKzB,SAO7E,OANIyB,KAAKK,OACLQ,EAAQT,SAASJ,KAAKK,OAEtBL,KAAKO,KACLM,EAAQP,OAAON,KAAKO,KAEjBM,CACf,CACI,QAAAE,EAASN,QAAEA,EAAUtD,EAAWuD,SAAEA,EAAWtD,GAAiB,IAC1D,MAAMmB,EAAUyB,KAAKzB,QA5DP,EAACA,EAASkC,EAASC,KACrC,MAAMK,EAAY3C,GACVb,EAAUa,GACHA,EAAK2C,SAAS,CACjBN,UACAC,aAGDhC,OAAON,GAElB,OAAIC,MAAMC,QAAQC,GACPA,EAAQV,QAAO,CAACmD,EAAG5C,IACT,OAATA,EACO4C,EAAID,EAAS3C,GAEjB4C,GACR,IAEHzC,EACOwC,EAASxC,GAEb,IAAI,EAuCwB0C,CAAcjB,KAAKzB,QAASkC,EAASC,GAAY,GAC1EQ,EAAWlB,KAAKQ,WAAW,CAC7BC,UACAC,aAEJ,OAAqB,OAAjBV,KAAKzB,SAAoBF,MAAMC,QAAQ0B,KAAKzB,UAAoC,IAAxByB,KAAKzB,QAAQI,OAC9DuC,EAEJ,GAAGA,IAAW3C,IAAUyB,KAAKW,SAAS,CACzCF,UACAC,cAEZ,CACI,aAAOS,CAAO3B,EAAKK,EAAQ,CAAE,EAAEtB,EAAU,KAAM8B,GAC3C,MAAMjC,EAAO,IAAI0B,EAAQN,EAAKK,EAAOtB,GAIrC,OAHI8B,GACAjC,EAAKgC,SAASC,GAEXjC,CACf,CACI,WAAOgD,CAAKhD,EAAMiD,GACd,OAAOjD,EAAKoB,MAAQ6B,CAC5B,CACI,WAAAC,CAAY9B,EAAKK,EAAOtB,GACpByB,KAAKR,IAAMA,EACXQ,KAAKH,MAAQA,EACbG,KAAKzB,QAAUA,CACvB,ECpGA,MAAMgD,EAAgB,IAYhBC,EAAiBC,GACfA,QAA0C,IAA1BA,EAAoB,EAC7BA,EAAoB,EAExB,GAkBLC,EAAYD,GAAQD,EAAcC,GAAOE,WAAW,KAAOtE,EAAMsE,WAAW,GA2B9E,MAAMC,EACN,QAAIP,GACA,OAAOrB,KAAKuB,EACpB,CACI,OAAAM,GACI,OAA+B,IAAxB7B,KAAKuB,IAAwBO,MAAM9B,KAAKuB,GACvD,CACI,MAAAQ,GACI,UA/CaN,EA+CMzB,YA9CsB,IAAzByB,EAAMF,IAbL,IAcVE,EAAMF,IAbO,IAagCE,EAAMF,IAlB1C,IAkBoFE,EAAMF,IAF9F,IAACE,CAgDrB,CACI,KAAAO,GACI,UA5CYP,EA4CMzB,YA3CuB,IAAzByB,EAAMF,KAtBP,IAuBRE,EAAMF,GAFF,IAACE,CA6CpB,CACI,UAAAQ,GACI,UAvCiBR,EAuCMzB,YAtCkB,IAAzByB,EAAMF,KA7BD,IA8BdE,EAAMF,GAFG,IAACE,CAwCzB,CACI,WAAAS,GACI,UApCkBT,EAoCMzB,YAnCiB,IAAzByB,EAAMF,KAlCA,IAmCfE,EAAMF,GAFI,IAACE,CAqC1B,CACI,OAAAU,GACI,OA9CqBT,EA8CH1B,KAC1B,CACI,KAAAoC,GACI,OAAOV,EAAS1B,KACxB,CACI,OAAAqC,GACI,MAvCW,CAACZ,IAChB,MAAM5C,EAAQ2C,EAAcC,GAC5B,OAAOC,EAASD,GAAS5C,EAAMyD,MAAM,GAAKzD,CAAK,EAqCpC0D,CAAWvC,KAC1B,CACI,QAAAwC,GACI,OAAOhB,EAAcxB,KAC7B,CACI,OAAAyC,GACI,OA3EchB,EA2EMzB,OA3EWyB,EAAmB,GAAK,EAA1C,IAACA,CA4EtB,CACI,SAAAiB,GACI,OA7EgBjB,EA6EMzB,OA7EWyB,EAAqB,GAAK,EAA5C,IAACA,CA8ExB,CACI,QAAAkB,GACI,OA/EkBlB,EA+EMzB,OA/EWyB,EAAwB,GAAK,EAA/C,IAACA,CAgF1B,CACI,MAAAmB,GACI,OAjFgBnB,EAiFMzB,OAjFWyB,EAAsB,GAAK,EAA7C,IAACA,CAkFxB,CACI,QAAAV,EAASN,QAAEA,EAAUtD,EAAWuD,SAAEA,EAAWtD,GAAiB,IAC1D,MArDY,EAACqE,EAAOhB,EAAUtD,EAAauD,EAAWtD,KAC1D,IAAIyF,EAAOpC,EAGX,OAFAoC,GAAQrB,EAAcC,GACtBoB,GAAQnC,EACDmC,CAAI,EAiDAC,CAAY9C,KAAMS,EAASC,EAC1C,CACI,WAAAY,CAAYD,EAAMxC,EAAOkE,EAAM,EAAGC,EAAM,EAAG3C,EAAQ,EAAGE,EAAM,GACxDP,KAAkB,EAAI+C,EACtB/C,KAAoB,EAAIgD,EACxBhD,KAAKuB,GAAiBF,GAAQ,EAC9BrB,KAAmB,EAAItB,OAAOG,GAC9BmB,KAAuB,EAAIK,EAC3BL,KAAqB,EAAIO,CACjC,ECnHO,MAAM0C,EACT,IAAAC,CAAKC,EAAM,EAAGC,GACVpD,KAAKqD,EAAEC,KAAOH,EACVnD,KAAKuD,GAAKvD,KAAKuD,EAAEC,SAAWJ,GAC5BpD,KAAKuD,EAAEC,QAEnB,CACI,OAAAC,GACI,OAAOzD,KAAKqD,EAAEK,IAAM1D,KAAKqD,EAAEC,GACnC,CACI,OAAAK,GACI,YAAkC,IAAvB3D,KAAK4D,EAAE5D,KAAKqD,EAAEC,KACd,GAEJtD,KAAK4D,EAAE5D,KAAKqD,EAAEC,IAC7B,CACI,MAAAO,GACI,OAAO7D,KAAKqD,EAAEC,GACtB,CACI,SAAAQ,GACI,OAAO9D,KAAKqD,EAAEK,GACtB,CACI,OAAAK,GACI,OAAO/D,KAAK4D,EAAEI,UAAUhE,KAAKqD,EAAEC,IACvC,CACI,OAAAW,GACI,MAAMC,EAAUlE,KAAKqD,EAAEC,IAAM,EAC7B,OAAOY,GAAWlE,KAAK4D,EAAEjF,OAAS,EAAIqB,KAAK4D,EAAEM,GAAW,IAChE,CACI,OAAAC,GACI,MAAMC,EAAUpE,KAAKqD,EAAEC,IAAM,EAC7B,YAA+B,IAApBtD,KAAK4D,EAAEQ,GACP,KAEJpE,KAAK4D,EAAEQ,EACtB,CACI,MAAAC,GACI,OAAOrE,KAAKqD,EAAEC,MAAQtD,KAAKqD,EAAEK,GACrC,CACI,QAAAY,CAASC,GACL,OAAOvE,KAAK4D,EAAEY,QAAQD,EAAKvE,KAAKqD,EAAEC,MAAQ,CAClD,CACI,SAAAmB,CAAUC,EAAWtB,GACjB,IAAI/C,EAAQ,EACZ,GAAIL,KAAKyD,UAEL,IADApD,EAAQL,KAAKqD,EAAEC,IACTtD,KAAKyD,WAAaiB,EAAU1E,KAAK2D,YACnC3D,KAAKkD,KAAK,EAAGE,GAGrB,OAAOpD,KAAK4D,EAAEI,UAAU3D,EAAOL,KAAKqD,EAAEC,IAC9C,CACI,KAAAqB,CAAMxB,EAAM,GACR,OAAOnD,KAAK4D,EAAEI,UAAUhE,KAAKqD,EAAEC,IAAKtD,KAAKqD,EAAEC,IAAMH,EACzD,CAGM,eAAAyB,CAAgBC,GACd,MAAMvB,IAAEA,GAAQtD,KAAKqD,EACfyB,EAAM9E,KAAK4D,EAAEY,QAAQK,EAAMvB,GACjC,OAAOwB,GAAO,EAAI9E,KAAK4D,EAAEI,UAAUV,EAAKwB,GAAO,EACvD,CACI,WAAAxD,CAAYyD,EAAQC,EAAU,IAC1BhF,KAAK4D,EAAImB,EACT/E,KAAKqD,EAAI,CACLC,IAAK,EACLI,IAAKqB,EAAOpG,QAEhBqB,KAAKuD,EAAIyB,CACjB,EAIW,MAAMC,EAAoB,CAACF,EAAQC,IAAU,IAAI/B,EAAY8B,EAAQC,GClEhF,MAMME,EAAc,CAChBhI,EACAH,GAEEoI,EAAgB,CAClBnI,EACAE,EACAH,GAGEqI,EAAgBP,GAAOK,EAAYV,QAAQK,IAAS,EACpDQ,EAAgBR,GAAOA,IAASvH,EAGhCgI,EAAOf,GDmDe,EAACgB,EAAKC,KAC9B,KAAMD,EAAIE,OAAO,KAAOD,GAEpBD,EAAMA,EAAIvB,UAAU,GAExB,KAAMuB,EAAIE,OAAOF,EAAI5G,OAAS,KAAO6G,GAEjCD,EAAMA,EAAIvB,UAAU,EAAGuB,EAAI5G,OAAS,GAExC,OAAO4G,CAAG,EC5DaG,CAASnB,EAAKtH,GDgEH6B,QAAQxB,EAAYL,EAAWA,GC/D9D,SAAS0I,EAAYC,EAAQZ,EAAU,IAC1C,IAAIjC,EAAM,EACN8C,EAAU,EACV7C,EAAM,EACN8C,GAAe,EACfC,EA1BW,EA2BXC,EAxBe,EAyBfC,EAAiB,GACrB,MAAMC,EAAS,IAAI7H,MAAM8H,KAAKC,MAAMR,EAAOjH,SACrC8B,EAAUuE,EAAQvE,SAAWtD,EAC7BuD,EAAWsE,EAAQtE,UAAYtD,EAC/BiJ,IAAerB,EAAQsB,iBACvBC,GAAmBvB,EAAQuB,iBAAmB,IAAIC,OAAOC,SAASC,KAAKlH,GAAMA,EAAIsB,gBACjF6F,EAAe3B,EAAQ2B,eAAgB,EACvCC,EAAY,IAAIC,IAChBC,EAAU9B,EAAQ8B,SAAO,MAAW,GACpCC,EAAiB,CACnBrG,EACAD,EACAxD,EACAK,EACAJ,EACAH,EACAC,EACAF,EAjDG,KAoDDkK,EAAkB,CACpBvG,EACAvD,EACAH,EACAD,GAGEmK,EAAepC,IAA2C,IAApCmC,EAAgBxC,QAAQK,GAE9CrB,EAAS,KACXR,GAAK,EAEHkE,EAAuB,CAAClI,EAAMmI,KACT,KAAnBlB,GAAyBkB,IACzBlB,EAAiB,IAEE,KAAnBA,GAAyBM,EAAgBjC,SAAStF,EAAK8B,iBACvDmF,EAAiBjH,EAC7B,EAEUoI,EAAQnC,EAAkBW,EAAQ,CACpCpC,WAIF,SAAS6D,EAAUhG,EAAMxC,EAAOyI,EAAUC,GACxC,MAAM9F,EA7EP,SAA2BJ,EAAMxC,EAAOmC,EAAI,EAAGwG,EAAK,EAAGC,EAAI,EAAGC,EAAI,GACrE,OAAO,IAAI9F,EAAMP,EAAMxC,EAAOmC,EAAGwG,EAAIC,EAAGC,EAC5C,CA2EsBC,CAAkBtG,EAAMxC,EAAOkE,EAAK8C,EAASyB,EAAUC,GACrET,EAAQrF,GACRoE,EAAU7C,EACV8C,GAAc,EACdI,EAAOJ,GAAcrE,CAC7B,CACI,SAASmG,EAAaC,EAAUC,EAAkBC,GAC9C,GA7Ee,IA6EX/B,EAA4B,CAC5B,MAAMgC,EAAiBnD,KAASA,IAAS7H,GAAMoI,EAAaP,IACtD7F,EAAO6I,EAASpD,UAAUuD,GAC1B5F,EAAQyF,EAASxD,SACjB4D,EAAUJ,EAASlE,YAAc3G,EAOvC,OANA6K,EAAS3E,OACLd,GAAS6F,EACTZ,EFrFc,EEqFa/B,EAAItG,IAE/BqI,EFxFa,EEwFarI,GAE1BoD,EAzFO,EA4FP6F,EA3FO,EACC,CA8FxB,CACQ,GA/FgB,IA+FZjC,EAA6B,CAC7B,IAAIkC,GAAe,EACnB,MAAMC,EAAkBtD,IAEpB,MAAMuD,EAAOvD,IAAS5H,EAChBoL,EAAWR,EAAS1D,UACpBmE,EAAWT,EAAS5D,UACpBsE,EAAcF,IAAa/K,EAC3BkL,EAAWF,IAAatL,EACxByL,EAAOrD,EAAaP,GAEpB6D,EAAWJ,GAAYlD,EAAakD,GAC1C,SAAIJ,IA9FE,CAACrD,GAAOM,EAAcX,QAAQK,IAAS,EA8FzB8D,CAAc9D,SAG9BuD,GAASG,IACTL,GAAgBA,EACXA,GAAkBM,GAAYE,QAIlCZ,IACOW,EAGD,EAETzJ,EAAO6I,EAASpD,UAAU0D,GAMhC,OALAN,EAAS3E,OACTmE,EF9HkB,EE8HS/B,EAAItG,IAC3B6I,EAAS1D,YAAclH,GACvB4I,IAEAgC,EAASxD,SAlIF,EACA,CAqIvB,CACQ,MAAMhE,EAAQ0H,EAAiBF,EAAShE,SAAW,EAE7C7E,EAAO6I,EAASpD,WADHI,KAASA,IAAS7H,GAAMoI,EAAaP,IAASgD,EAASxD,YAO1E,GALAgD,EF5Ie,EE4IKrI,EAAMqB,EAAO0H,EAAiBF,EAAS/D,YAAc,GACzEoD,EAAqBlI,GACrB6I,EAAS3E,OACT2C,IAEIiC,EACA,OA9IY,EAiJhB,OADcD,EAASvD,SAAStH,GAjJjB,EACC,CAkJxB,CACI,SAAS4L,IACL,MAAMC,EAAWzB,EAAMzD,UACjB2E,EAAWlB,EAAMnD,UACvBmD,EAAMlE,OAEN,MAAM4F,EAAS1B,EAAMxC,gBAAgBlE,GAC/BqI,EAAoC,IAAlBD,EAAOnK,QAAgBmK,EAAOtE,QAAQ/D,IAAY,EAC1E,GAAI6H,IAzGgBzD,EAyGWyD,EAzGJvB,EAAevC,QAAQK,IAAS,IAyGfkE,GAAmB3B,EAAM/C,SAEjE,OADAgD,EFhKY,EEgKSwB,GAhKd,EAsDQ,IAAChE,EA8GpB,MAAMmE,GAA0C,IAAzBF,EAAOtE,QAAQxH,GAEhCmK,EAAe2B,EAAO,KAAOzL,EACnC,GAAI2L,GAAkB7B,EAAc,CAChC,MAAMG,EAAWF,EAAMvD,SAAW,EAC5B7E,EAAOoI,EAAM3C,WAAWI,GAAOA,IAASnE,IACxC6G,EAASD,EAAWtI,EAAKL,OA3JpB,EA+JX,OAHAyI,EAAMlE,OACNmE,EF3KW,EE2KSrI,EAAMsI,EAAUC,GACpCL,EAAqBlI,EAAMmI,GA7KpB,CA+KnB,CACQ,OA9KgB,CA+KxB,CACI,SAAS8B,IACL,MAAM3B,EAAWF,EAAMvD,SAEjBqF,EAAS9B,EAAM3C,WAAWI,GAAOA,IAASnE,IADjC,GAETyI,EAAalE,EAAkBiE,EAAQ,CACzC1F,WAEE4F,EAAWD,EAAW7E,SAASpH,GAErC,IADA8I,EAvLe,EAwLTmD,EAAW1F,WACbuC,EAAU4B,EAAauB,GAAaC,EAAU9B,GAGlD,OADAF,EAAMlE,OA9LK,CAgMnB,CACI,SAASmG,IACL,GAAcjC,EAAMzD,YA/KO7G,EAqLvB,OALAuK,EF9LgB,EE8LSD,EAAMzD,WAC/ByD,EAAMlE,OACNF,EAAM,EACN6C,EAAU,EACV9C,IAvMO,EA0MX,GAAIqC,EAAagC,EAAMzD,WAAY,CAG/B,OADA0D,EFxMa,EEuMAD,EAAM3C,UAAUW,IA3MtB,CA8MnB,CACQ,GAAIgC,EAAMzD,YAAclD,EAAS,CAC7B,GAAIwF,EAAgB,CAChB,MAAMqD,EAAa7I,EAAQ9B,OAAStB,EAAe4I,EAAetH,OAC5D4K,EAAc,GAAG9I,IAAUpD,IAAQ4I,IAGzC,GAFiBmB,EAAMzC,MAAM2E,KACaC,EAEtC,OArNF,CAuNL,MAAM,GAAInC,EAAM9C,SAAS5D,GACtB,OAxNE,EA6NN,OAHA2G,EF3NY,EE2NSD,EAAMzD,WAC3ByD,EAAMlE,OACN2C,IA7NO,CA+NnB,CACQ,GAAIQ,EAAY,CACZ,GAAIhB,EAAa+B,EAAMzD,WAAY,CAC/B,MAAMkF,EAAWzB,EAAMzD,UACjB2E,EAAWlB,EAAMnD,UAEvB,OADAmD,EAAMlE,OACFoF,GA7KQ,CAACzD,GAAOA,IAASpE,GAAWoE,IAASnE,GAAYmE,IAASvH,EA6KtDkM,CAAgBlB,IAC5BlB,EAAMlE,OACNmE,EFvOI,EEuOiBiB,GAvOtB,IA0OHjB,EF1OQ,EE0OawB,GA1OlB,EA4OnB,CACY,MAAMY,EAAU5E,GAAOoC,EAAYpC,KAAUQ,EAAaR,GAG1D,OADAwC,EF/OY,EE8OCD,EAAM3C,UAAUgF,IA9OtB,CAiPnB,CAGQ,OADApC,EFnPgB,EEkPHD,EAAM3C,UAAUwC,IAlPlB,CAqPnB,CA8BI,MAAO,CACHyC,SA9BJ,WAEI,IADA3D,EAvPW,EAwPLqB,EAAM3D,WACR,OAAOsC,GACH,KAzPE,EA0PEA,EAAY6C,IACZ,MACJ,KA3PQ,EA4PJ7C,EAAYkD,IACZ,MAEJ,QACIlD,EAAYsD,IAKxB,OADAnD,EAAOvH,OAASmH,EAAa,EACtBI,CACf,EAaQyD,cAZJ,SAAuBC,GACnB,MAAM/K,EAAQ4B,EAAUpD,EAAQuM,EAChC,GAAIhD,EAAUiD,IAAIhL,GACd,QAAS+H,EAAUkD,IAAIjL,GACpB,CACH,MAAMkL,EAASpD,EAAef,EAAO9E,cAAc0D,QAAQ3F,EAAMiC,gBAAiB,EAAK8E,EAAOpB,QAAQ3F,IAAW,EAEjH,OADA+H,EAAUoD,IAAInL,EAAOkL,GACdA,CACnB,CACA,EAKA,CC7RA,MAAME,EACF,IAAAC,GACI,OAAI7L,MAAMC,QAAQ0B,KAAKmK,IAAMnK,KAAKmK,EAAExL,OAAS,QAA0C,IAA9BqB,KAAKmK,EAAEnK,KAAKmK,EAAExL,OAAS,GACrEqB,KAAKmK,EAAEnK,KAAKmK,EAAExL,OAAS,GAE3B,IACf,CACI,KAAAyL,GACI,QAAOpK,KAAKmK,EAAExL,QAASqB,KAAKmK,EAAEE,KACtC,CACI,IAAAnK,CAAKrB,GACDmB,KAAKmK,EAAEjK,KAAKrB,EACpB,CACI,OAAAyL,GACI,OAAOtK,KAAKmK,CACpB,CACI,WAAA7I,GACItB,KAAKmK,EAAI,EACjB,EAEA,MAAMI,EAAa,IAAI,IAAIN,EAC3B,SAASO,EAAMC,EAAOC,EAAO,IACzB,MAAM1F,EAAU0F,EACVjK,EAAUuE,EAAQvE,SAAWtD,EAC7BuD,EAAWsE,EAAQtE,UAAYtD,EAC/BuN,GAAiB3F,EAAQ2F,eAAiB,IAAInE,OAAOC,SAASC,KAAKlH,GAAMA,EAAIsB,gBAC7E6F,EAAe3B,EAAQ2B,eAAgB,EAC7C,IAAIiE,EAAY,KAKd,MAAMC,EAAQN,IAIRO,EAAcP,IAKdQ,EAAWR,IAKXS,EAAmBT,IAGnBU,EAAgB,IAAIC,IAa1B,SAASC,EAAYC,GACnB,OAAO3E,QAAQwE,EAAcpB,IAAIlD,EAAeyE,EAAQtK,cAAgBsK,GAChF,CAYM,SAASC,IACHN,EAASX,SACTY,EAAiBZ,OAE7B,CAGM,SAASkB,IACP,MAAMC,EAAiBT,EAAYZ,OACnC,OAAIqB,GAAkBhO,EAAUgO,GACrBA,EAAehN,QAEnBsM,EAAMP,SACrB,CAGM,SAASkB,EAAmBX,EAAOzM,EAAMqN,GAAW,GAC9CpN,MAAMC,QAAQuM,SAA0B,IAATzM,IAC/ByM,EAAM3K,KAAK9B,EAAKoC,WAAW,CACvBC,UACAC,cAEArC,MAAMC,QAAQF,EAAKG,UAAYH,EAAKG,QAAQI,SAC5CP,EAAKG,QAAQmN,SAASC,IAClBd,EAAM3K,KAAKyL,EAAK,IAEhBF,GACAZ,EAAM3K,KAAK9B,EAAKuC,SAAS,CACrBF,UACAC,eAKxB,CAGM,SAASkL,EAAYf,EAAOzM,GA9C5B,IAAsBS,EA+ChBR,MAAMC,QAAQuM,SAA0B,IAATzM,IAC3Bb,EAAUa,IAhDES,EAiDKT,EAAKoB,KAhD1BmL,EAAchM,QACPgM,EAAcnG,QAAQ3F,EAAMiC,gBAAkB,EAgD7C+J,EAAM3K,KAAK9B,EAAKwC,aAEhB4K,EAAmBX,EAAOzM,IAG9ByM,EAAM3K,KAAK9B,GAG3B,CAIM,SAASyN,EAAepK,GACtB4J,IACA,MAAMS,EAAUhM,EAAQqB,OAAOM,EAAMe,WAAY,CAAE,EAAE,GAAI,CACrDuJ,KAAMtK,EAAMkB,WACZqJ,GAAIvK,EAAMmB,WAER6I,EArFV,SAAuBhK,GACnB,MAAMmI,EAAanI,EAAMe,WACnB3D,EAAQ8H,EAAeiD,EAAW9I,cAAgB8I,GAClDD,cAAEA,GAAkBiB,GAAa,CAAE,EACzC,OAAKK,EAAcpB,IAAIhL,IAAU8K,GAAiBA,EAAc9K,IAC5DoM,EAAcgB,IAAIpN,IACX,GAEJoM,EAAcpB,IAAIhL,EACjC,CA4EyB8K,CAAclI,GAE/B,GADAsJ,EAAS7K,KAAK4L,GACVL,EACAX,EAAY5K,KAAK4L,OACd,CAEHF,EADcN,IACKQ,EAC/B,CACA,CAqCM,SAASI,EAAUzK,GAEbA,EAAMU,WACN0J,EAAepK,GAGfA,EAAMW,SAvCZ,SAAsBX,GACpB,MAAM2J,EAAU3J,EAAMe,WAAWF,MAAM,GACjCiJ,EAAiBT,EAAYV,QAEnC,GADAiB,IACIE,EAAgB,CAChB,MAAMV,EAAQS,IACV/N,EAAUgO,IACVA,EAAejL,OAAO,CAClByL,KAAMtK,EAAMkB,WACZqJ,GAAIvK,EAAMmB,WAGlBgJ,EAAYf,EAAOU,EAC/B,MAAe,GAAKJ,EAAYC,IAMjB,GAA+B,mBAApBpG,EAAQmH,QAAwB,CAC9C,MAAM3M,EAAMiC,EAAMe,WACZ4J,EAAO3K,EAAMgB,UACb4J,EAAS5K,EAAMiB,YACrBsC,EAAQmH,QAAQ,CACZf,QAAS5L,EACT8M,WAAYF,EACZG,aAAcF,GAE9B,OAbYT,EADcN,IACK7J,EAAMV,SAAS,CAC9BN,UACAC,aAYhB,CAWY8L,CAAa/K,EAEzB,CA2DI,MAAMgL,EAAQ/B,EAAKgC,gBAAkBhC,EAAKgC,gBAAkB/G,EAC5DiF,EAAY6B,EAAMhC,EAAO,CACrB3D,QATF,SAAiBrF,GACXA,EAAMO,QACNkK,EAAUzK,GAlDhB,SAAoBA,GAGlB,MAAMkL,EAAgB5B,EAASb,OACzBN,EAAanI,EAAMe,WACnBiJ,EAAWN,EAAY1J,EAAMV,YAC7B8J,EAAQS,IACd,GAAsB,OAAlBqB,EACA,GAAIlL,EAAMQ,aAAc,CACpB+I,EAAiB9K,KAAK0J,GACtB,MAAMgD,EAAW5B,EAAiBd,OAC9B0C,GACAD,EAAc5M,KAAK6M,EAAU,GAEjD,MAAmB,GAAInL,EAAMS,cAAe,CAC5B,MAAM0K,EAAW5B,EAAiBd,OAC9B0C,GACAD,EAAc5M,KAAK6M,EAAUhD,GAC7BoB,EAAiBZ,SAEjBuC,EAAc5M,KAAK6J,EAAYA,EAEnD,MAAuBnI,EAAMM,SACT0J,EACAkB,EAAc1M,OAAO2J,GAErBgC,EAAYf,EAAOjB,GAEhBnI,EAAMO,SAEb4J,EAAYf,EAAOpJ,EAAMV,SAAS,CAC9BN,UACAC,mBAGDe,EAAMM,SACb6J,EAAYf,EAAOjB,GACZnI,EAAMO,SAEb4J,EAAYf,EAAOpJ,EAAMV,SAAS,CAC9BN,UACAC,aAGhB,CAQYmM,CAAWpL,EAEvB,EAIQhB,UACAC,WACAiK,cAAe3F,EAAQ2F,cACvBpE,gBAAiBvB,EAAQuB,gBACzBI,aAAc3B,EAAQ2B,aACtBL,iBAAkBtB,EAAQsB,mBAGfsE,EAAUlB,WAIzB,MAAM6B,EAAiBT,EAAYV,QAInC,OAHuB,OAAnBmB,GAA2BA,GAAkBhO,EAAUgO,IAAmBJ,EAAYI,EAAe/L,MACrGgM,EAAmBF,IAAYC,GAAgB,GAE5CV,EAAMP,SACjB,CC9QiC,MAAMwC,EAASjO,GAAyB,iBAAVA,GAAgC,OAAVA,EAE9E,SAASkO,EAAQC,EAAGC,GACvB,MAAMC,EAAOF,EACb,GAAI3O,MAAMC,QAAQ4O,GACd,IAAI,IAAIpI,EAAM,EAAGA,EAAMoI,EAAKvO,OAAQmG,IAChCoI,EAAKpI,GAAOiI,EAAQE,EAAGC,EAAKpI,IAAOmI,QAEhCH,EAAMI,IAAS,YAAaA,GACnCH,EAAQG,EAAK3O,QAAS0O,GAE1B,OAAOC,CACX,CACO,SAASC,EAAKC,EAAUC,GAC3B,cAAWD,UAAoBC,IAG1BP,EAAMM,IAA0B,OAAbA,EAGpB/O,MAAMC,QAAQ8O,GACPA,EAASE,OAAOC,GAAM,GAAGC,KAAKC,KAAKJ,GAASK,GAAMP,EAAKI,EAAKG,UAEnEZ,EAAMM,KAAaN,EAAMO,KAClBtP,OAAOC,KAAKoP,GAAUE,OAAOpP,IAChC,MAAMyP,EAAKN,EAAOnP,GACZ0P,EAAKR,EAASlP,GACpB,OAAI4O,EAAMc,IAAOd,EAAMa,GACZR,EAAKS,EAAID,GA3BS,kBA6BlBC,EACAA,KAAe,OAAPD,GAEZA,IAAOC,CAAE,IAfbR,IAAaC,EAmB5B,CClCO,SAASQ,EAAWX,EAAMlI,GAC7B,MAAM8I,EAAeZ,EAcrB,OAbAY,EAAaC,SAAW,IACjBD,EAAaC,UAAY,IAEhCD,EAAa9I,QAAU,IAChBA,KACA8I,EAAa9I,SAEpB8I,EAAaE,KAAO,SAAmBf,GACnC,OAAOF,EAAQ/M,KAAMiN,EACxB,EACDa,EAAaG,MAAQ,SAAoBC,EAAMjB,GAC3C,ODsBD,SAAeD,EAAGmB,EAAYlB,GACjC,OAAI5O,MAAMC,QAAQ6P,GACPpB,EAAQC,GAAI5O,IACf,IAAI,IAAI0G,EAAM,EAAGA,EAAMqJ,EAAWxP,OAAQmG,IACtC,GAAIqI,EAAKgB,EAAWrJ,GAAM1G,GACtB,OAAO6O,EAAG7O,GAGlB,OAAOA,CAAI,IAGZ2O,EAAQC,GAAI5O,GAAO+O,EAAKgB,EAAY/P,GAAQ6O,EAAG7O,GAAQA,GAClE,CClCe6P,CAAMjO,KAAMkO,EAAMjB,EAC5B,EACMa,CACX,CCbA,SAASM,EAAWhQ,EAAM4G,GACtB,MAAMqJ,UAAEA,GAAY,GAAUrJ,GAAW,CAAE,EAC3C,GAAI,MAAO5G,EACP,MAAO,GAEX,GAAoB,iBAATA,GAAqC,iBAATA,EACnC,OAAOM,OAAON,GAElB,GAAIC,MAAMC,QAAQF,GACd,OAAOkQ,EAAOlQ,EAAM4G,GAExB,GAAIzH,EAAUa,GAAO,CACjB,GAAIiQ,EACA,OAAOC,EAAOlQ,EAAKG,QAASyG,GAEhC,MAAMnF,EAAQV,EAAcf,EAAKyB,OACjC,OAAqB,OAAjBzB,EAAKG,QAlBC,IAmBaH,EAAKoB,IAAMK,EArBhB,KAER,IAqBSzB,EAAKoB,IAAMK,EApBtB,IAoBwCyO,EAAOlQ,EAAKG,QAASyG,GAtBrD,KAsBkF5G,EAAKoB,IApB/F,GAqBhB,CACI,MAAO,EACX,CACO,SAAS8O,EAAOzD,EAAO7F,GAC1B,OAAI6F,GAASxM,MAAMC,QAAQuM,GAChBA,EAAMhN,QAAO,CAACmD,EAAG5C,IAAO4C,EAAIoN,EAAWhQ,EAAM4G,IAAU,IAE9D6F,EACOuD,EAAWvD,EAAO7F,GAEtB,EACX,CChBA,MAAMuJ,EAAS,CAAC/O,EAAKK,EAAOtB,EAAU,MAAQ,CAC5CiB,MACAK,QACAtB,UACAiQ,KAAK,IAUDC,EAAiB,CAACrQ,EAAMsQ,KAC5B,MAAM1Q,EAAOD,OAAOC,KAAKI,EAAKyB,OAAOP,KAAK,KACpCqP,EAAO5Q,OAAOqB,OAAOhB,EAAKyB,OAAOP,KAAK,KAC5C,GAAItB,IAAS2Q,EAEX,OAAOvQ,EAAKyB,MAEd,IAAK6O,IAAQtQ,EAAKiC,MAChB,MAAO,CACLuO,SAAUD,GAMd,MAAME,EAAUH,EAAI1K,UAAU5F,EAAKiC,MAAM0L,KAAM3N,EAAKiC,MAAM2L,IAC1D,IAAK6C,EAAQvK,SAAS,KAEpB,OAAOlG,EAAKyB,MAEd,MAAMiP,EAAeD,EAAQE,MAAM,KACnC,GAA4B,IAAxBD,EAAanQ,OACf,OAAOP,EAAKyB,MAEd,IAAI0E,EAAMuK,EAAa,GAAGxM,MAAM,GAAG,GAAI0M,OAIvC,OAHIzK,EAAI0K,WAAW,MAAQ1K,EAAI2K,SAAS,OACtC3K,EAAMA,EAAIjC,MAAM,GAAG,IAEd,CACLsM,SAAUrK,EACX,EAOG4K,EAAqB,CAAC/Q,EAAMsQ,KAChC,GAAItQ,EAAKiC,MACP,OAAOqO,EAAI1K,UAAU5F,EAAKiC,MAAM0L,KAAM3N,EAAKiC,MAAM2L,IAEnD,IAAK5N,EAAKyB,MACR,MAAO,IAAIzB,EAAKoB,OAElB,MAAMK,EAAQ4O,EAAerQ,EAAMsQ,GACnC,OAAI7O,EAAM+O,SACD,IAAIxQ,EAAKoB,OAAOK,EAAM+O,YAEtBxQ,EAAKoC,YAChB,EAOM4O,EAAmB,CAAChR,EAAMsQ,IAC1BtQ,EAAKmC,IACAmO,EAAI1K,UAAU5F,EAAKmC,IAAIwL,KAAM3N,EAAKmC,IAAIyL,IAExC5N,EAAKuC,WAWR0O,EAAW,CAACjR,EAAMsQ,IAClBtQ,EAAKmC,IACE,CACL4O,EAAmB/Q,EAAMsQ,MACtBtQ,EAAKG,QACR6Q,EAAiBhR,EAAMsQ,IAGpBS,EAAmB/Q,EAAMsQ,GAU9BY,EAAe,CAACC,EAAQC,EAAOC,KACnC,MAAMjL,EAAU+K,EAAOvL,UAAUyL,GAAY,GAAGC,OAAOF,GACvD,OAAOhL,GAAW,EAAIA,GAAWiL,GAAY,GAAKjL,CAAO,EAGrDmL,EAAoB,8CACpBC,EAAwB,kDACxBC,EAA4B,0CAM5BC,EAAwB,IAAIC,OAAO,IAHvC,gGAGqDhL,UADrD,6GAC4EA,WACxEiL,EACJ,iKACIC,EAAiB,wEAYvB,SAASC,IACP,IAAIC,GAAI,IAAIC,MAAOC,UAInB,OAHIC,OAAOC,aAAiD,mBAA3BD,OAAOC,YAAYC,MAClDL,GAAKI,YAAYC,OAEZ,uCAAuC1R,QAAQ,SAAS,SAAUuE,GAEvE,MAAMrC,GAAKmP,EAAoB,GAAhBhK,KAAKsK,UAAiB,GAAK,EAG1C,OAFAN,EAAIhK,KAAKC,MAAM+J,EAAI,KAEL,MAAN9M,EAAYrC,EAAS,EAAJA,EAAW,GAAKD,SAAS,GACtD,GACA,CCtJA,MAAM+L,GAASjO,GAA2B,iBAAVA,EAC1B6R,GAAY7R,GAA2B,iBAAVA,EAU7BmP,GAAO,CAAChB,EAAG2D,GAA6B,KAC5C,MAAMzD,EAAOF,EAEb,GAAI3O,MAAMC,QAAQ4O,GAAO,CACvB0D,GAAmB1D,GACfA,EAAKM,KAAKkD,MAEZxD,EAAK2D,QAAQlB,GACbzC,EAAKhN,KAAKyP,IAEZ,IAAK,IAAI7K,EAAM,EAAGA,EAAMoI,EAAKvO,OAAQmG,IAAO,CAC1C,MAAMgM,EAAQ9C,GAAKd,EAAKpI,GAAM6L,GAC1BtS,MAAMC,QAAQwS,IAChB5D,EAAK6D,OAAOjM,EAAK,KAAMgM,GACvBhM,GAAOgM,EAAMnS,OAAS,GAEtBuO,EAAKpI,GAAOgM,CAEpB,CACA,KAAS,IAAI5D,GAAQJ,GAAMI,IAASA,EAAK3O,QACrC,OAAI2O,EAAK8D,wBAKL9D,EAAKyD,6BACPA,GAA6B,GAE/B3C,GAAKd,EAAK3O,QAASoS,IALVzD,EAAK1N,IAAM0N,EAAOA,EAAK3O,QAO3B,GAAImS,GAASxD,IAAS4C,EAAsBmB,KAAK/D,EAAK8B,QAK3D,MAAO,CAAC9B,EAAM0C,EAClB,CAEE,OAAIc,GAASxD,IAASxP,EAAMwP,GACnByD,EACH,CAAC,KAAMhB,GACP,CAAC,CAAEnQ,IAAK,KAAMjB,QAAS,MAAQoR,GAG9BzC,CAAI,EAQP0D,GAAsBM,IAC1B,IAAIC,EAAWD,EAAME,eAAeC,GAAMX,GAASW,KAAO3T,EAAM2T,KAAM,EAEtE,IAAK,IAAIC,EAAIH,EAAW,EAAGG,GAAK,EAAGA,IAC7BZ,GAASQ,EAAMI,MAAQ5T,EAAMwT,EAAMI,MAGnC5T,EAAMwT,EAAMI,KAOZxE,GAAMoE,EAAMI,OANVA,IAAMH,EAAW,GACnBD,EAAMH,OAAOO,EAAI,EAAGH,EAAWG,EAAI,EAAGJ,EAAM5O,MAAMgP,EAAI,EAAGH,GAAU7R,KAAK,KAE1E6R,EAAWG,GAWX,IAAMH,GACRD,EAAMH,OAAO,EAAGI,EAAUD,EAAM5O,MAAM,EAAG6O,GAAU7R,KAAK,IAC5D,ECzEM0O,GAAQhB,IACZ,MAAME,EAAOF,EAEb,GAAI3O,MAAMC,QAAQ4O,GAChB,IAAK,IAAIpI,EAAM,EAAGA,EAAMoI,EAAKvO,OAAQmG,IAAO,CAC1C,MAAMgM,EAAQ9C,GAAKd,EAAKpI,IACpBzG,MAAMC,QAAQwS,IAChB5D,EAAK6D,OAAOjM,EAAK,KAAMgM,GACvBhM,GAAOgM,EAAMnS,OAAS,GAEtBuO,EAAKpI,GAAOgM,CAEpB,MACa5D,GAxB6B,iBAwBfA,GAASA,EAAK3O,SACrCyP,GAAKd,EAAK3O,SAKZ,GAAId,EAAayP,IACXA,EAAKvO,OAAS,GAAiB,MAAZuO,EAAK,GAAY,CACtC,IAAIqE,EAAYrE,EAAKvO,OACrB,MAAO,CAACD,OAAO8S,aAAa,KAAKC,OAAOF,GAC9C,CAGE,OAAOrE,CAAI,ECpDPwE,GAA4B,WAcrBC,GAAyBzE,GAC7BA,EAAKc,MAAM5P,IACZb,EAAUa,IAASA,EAAKyB,OAC1B9B,OAAOC,KAAKI,EAAKyB,OAAO6L,SAASxN,IACA,iBAApBE,EAAKyB,MAAM3B,KACpBE,EAAKyB,MAAM3B,GAAwBE,EAAKyB,MAAM3B,GAZ1CY,QAAQ4S,GAA2B,MAajD,IAGWtT,KCxBJ,SAASwT,GAAQC,EAAM3E,EAAM4E,EAAM9M,GACtC,OAAOkI,EAAKc,MAAM5P,IACd,GAAIb,EAAUa,GAAO,CACjB,MAAMoB,EAAMpB,EAAKoB,IACXuS,EAAcF,EAAKrS,GACzB,GAA2B,mBAAhBuS,EACP,OAAOA,EAAY3T,EAAM0T,EAAM9M,EAE/C,CACQ,OAAO5G,CAAI,GAEnB,CCFA,MAAM4T,GAAmBC,OAAO,oBAC1BC,GAAoBD,OAAO,qBAC3BE,GAAcF,OAAO,eACrBG,GACJ,iFA6JF,SAASC,GAAeC,GACtB,OAAQA,GACN,KAAKN,GACH,MAAO,UACT,KAAKE,GACH,MAAO,IACT,KAAKC,GACH,MAAO,WACT,QACE,OAAOG,EAEb,CAEA,MAsDaC,GAAgB,CAAEC,UAxNb,CAACpU,EAAM4G,KACvB,MAAMyN,EAAUvC,IAIVwC,EA+DR,SAAyCC,GACvCA,EAAa,IAAIA,GAEjB,MAAMC,EAAS,GACf,KAAOD,EAAWhU,OAAS,GAAG,CAC5B,MAAMJ,EAAUoU,EAAW,GAC3B,GAAIpV,EAAUgB,GAAU,CACtBqU,EAAO1S,KAAKyS,EAAWE,SACvB,QACN,CACI,MAAMC,EAAaxD,EAAa/Q,EAAS6T,IACzC,IAAmB,IAAfU,EAAmB,CACrBF,EAAO1S,KAAKyS,EAAWE,SACvB,QACN,CACI,MAAM5E,EAAQ1P,EAAQ0P,MAAMmE,IACtBW,EAAaxU,EAAQ+D,MAAM,EAAGwQ,GAC9BE,EAAczU,EAAQ+D,MAAMwQ,EAAa7E,EAAM,GAAGtP,QACpDoU,EAAWpU,QACbiU,EAAO1S,KAAK6S,GAEV9E,EAAMgF,OAAOC,gBACfN,EAAO1S,KAAK8R,IAEV/D,EAAMgF,OAAOE,iBACfP,EAAO1S,KAAKgS,IAEVjE,EAAMgF,OAAOG,YACfR,EAAO1S,KAAKiS,IAEVa,EAAYrU,OACdgU,EAAW,GAAKK,EAEhBL,EAAWE,OAEjB,CAEE,OAAOD,CACT,CArGwBS,CAAgCjV,EAAKG,SACrD+U,EA0GR,SAAmCZ,GACjC,MAAM7H,EAAQ,GACd,IAAI0I,EAAe,KAEfC,EAAa,KACjB,IAAK,MAAMjV,KAAWmU,EACpB,GAAInU,IAAYyT,IAAmC,OAAfwB,EAClCD,EAAezT,EAAQqB,OAAO,SAC9BoS,EAAahV,QAAU,GACvBgV,EAAaE,YAAc,GAC3BD,EAAaxB,OACR,IAAIzT,IAAY2T,IAAqBsB,IAAexB,GAAkB,CAC3EwB,EAAatB,GACb,QACD,CAAU3T,IAAY4T,IAAeoB,GAAgBC,IAAetB,IACnErH,EAAM3K,KAAKqT,GACXA,EAAe,KACfC,EAAa,MACJD,EACLC,IAAexB,GACjBuB,EAAaE,YAAYvT,KAAKmS,GAAe9T,IAE7CgV,EAAahV,QAAQ2B,KAAKmS,GAAe9T,IAI3CsM,EAAM3K,KAAKmS,GAAe9T,GAChC,CAEE,OAAOsM,CACT,CAxI0B6I,CAA0BhB,GAE5CiB,EAAkBL,EACrB9M,QAAQ2D,GAAM5M,EAAU4M,IAAgB,UAAVA,EAAE3K,MAChCkH,KAAKnI,IACJA,EAAQqV,SAAU,EAClBrV,EAAQkU,QAAUA,EACXlU,KAEX,IAAKoV,EAAgBhV,OAEnB,MAAO,CACLwQ,EAAmB/Q,EAAM4G,EAAQ6O,KAAKnF,QACnCtQ,EAAKG,QACR6Q,EAAiBhR,EAAM4G,EAAQ6O,KAAKnF,MAGxC,MAAM7O,EAAQ4O,EAAerQ,EAAM4G,EAAQ6O,KAAKnF,KAEhD,GAAI7O,EAAM+O,SAAU,CAElB,MAAMkF,EAAiBjU,EAAM+O,SAASG,MAAM,KAAKrI,KAAK9C,GAAMA,EAAEoL,SACxD+E,EAAqBD,EACxBtN,QAAQ5C,GAAM,CAAC,SAAU,UAAW,QAAS,QAAS,UAAUU,SAASV,KACzEyG,MACC0J,IACFlU,EAAMmU,QAAUD,IAIhBD,EAAetG,MAAM5J,GAAMA,EAAEsL,SAAS,SACtC4E,EAAetG,MAAM5J,GAAMA,EAAEsL,SAAS,UAEtCrP,EAAMoU,QAAUH,EAAeI,MAAMtQ,GAAMA,EAAEsL,SAAS,OAAStL,EAAEsL,SAAS,OAEhF,CAEE,IAAIiF,EAAUtU,EAAMmU,OAAOlT,eAAiB,GACxCsT,EAAQ,GAIZ,OAHIvU,EAAMoU,OAAO/E,SAAS,OAASrP,EAAMoU,OAAO/E,SAAS,QACvDkF,EAAQ,UAAUvU,EAAMoU,UAEnB1F,EACL,MACA,CAAE8F,MAAO,gBAAkBF,EAAS,gBAAiB1B,EAAS2B,SAC9DT,EACD,EAoKuCW,MAtD5B,CAAClW,EAAM4G,KACnB,IAAK5G,EAAKwV,QAER,MAAO,CACLzE,EAAmB/Q,EAAM4G,EAAQ6O,KAAKnF,QACnCtQ,EAAKG,QACR6Q,EAAiBhR,EAAM4G,EAAQ6O,KAAKnF,MAGxC,MAAM7O,EAAQ4O,EAAerQ,EAAM4G,EAAQ6O,KAAKnF,KAChD,IAAI6F,EAAQ,CAAC1U,EAAM0U,OAAS1U,EAAM+O,UAAY,SAC1C4F,IAAW3U,EAAM4U,OAAQ,EACzBC,EAAa7U,EAAM8U,KAAO,OAAS9U,EAAM+U,MAAQ,QAAU/U,EAAMgV,OAAS,SAAW,OACzF,GAAIzW,EAAKqV,aAAa9U,OAAQ,CAE5B4V,EAAQnW,EAAKqV,YAEb,MAAMqB,EAAkBP,EACrB/N,QAAQwG,GAAmB,iBAANA,IACrB1N,KAAK,IACLwB,cACAiO,MAAM,KACNrI,KAAK9C,GAAMA,EAAEoL,SACZ8F,EAAgBxQ,SAAS,UAC3BkQ,GAAS,GAEPM,EAAgBxQ,SAAS,WAC3BoQ,EAAa,SAEXI,EAAgBxQ,SAAS,YAC3BoQ,EAAa,UAEXI,EAAgBxQ,SAAS,UAC3BoQ,EAAa,QAEfH,EAAQA,EAAM7N,KAAKsG,IACbvP,EAAauP,KACfA,EAAIA,EAAElO,QAAQ,+BAAgC,KAEzCkO,IAEb,CACE,MAAO,CACLuB,EAAO,UAAW,CAAE8F,MAAO,WAAYI,KAAMD,GAAU,CACrDjG,EACE,UACA,CAAE8F,MAAO,iBAAkBD,MAAO,eAAeM,MAAe7U,EAAMuU,OAAS,MAC/EG,GAEFhG,EAAO,MAAO,CAAE8F,MAAO,oBAAsBjW,EAAKG,WAErD,GCtOUwW,GAAY,CACvBJ,KAAOvW,GAASmQ,EAAO,MAAO,CAAE8F,MAAO,WAAajW,EAAKG,SACzDsW,OAASzW,GAASmQ,EAAO,MAAO,CAAE8F,MAAO,aAAejW,EAAKG,SAC7DqW,MAAQxW,GAASmQ,EAAO,MAAO,CAAE8F,MAAO,YAAcjW,EAAKG,UCHhDyW,GAAS,CAEpBC,EAAG,CAAC7W,EAAM4G,KACR,MAAMnF,EAAQ4O,EAAerQ,EAAM4G,EAAQ6O,KAAKnF,KAAKE,UAAY,GACjE,OAAOL,EACL,IACA,CAAE2G,GAAI,eAAerV,EAAMmP,SAAUhQ,KAAM,eAAea,EAAMmP,UAChE5Q,EAAKG,QACN,EAEH4W,KAAM,CAAC/W,EAAM4G,KACX,MAAMnF,EAAQ4O,EAAerQ,EAAM4G,EAAQ6O,KAAKnF,KAAKE,UAAY,GACjE,OAAOL,EAAO,IAAK,CAAE6G,KAAM,gBAAgBvV,EAAMmP,UAAY5Q,EAAKG,QAAQ,GCfxE8W,GAAY,CAChB,QACA,eACA,cACA,UACA,SACA,kBACA,eACA,WAEIC,GAAoB,CACxBC,KAAM,MACNC,WAAY,MACZC,MAAO,MACPC,QAAS,MACTC,OAAQ,MACRC,SAAU,MACVC,KAAM,MACNC,UAAW,MACXC,MAAO,OAGHC,GAAkB,CAAC,OAAQ,OAAQ,OAAQ,OAAQ,QAEnDC,GAAa,wECFZ,MCHDC,GAAc3H,EAAO,MAAO,CAAE8F,MAAO,mBAAqB,IAC1D8B,GAAc5H,EAClB,MACA,CAAE8F,MAAO,mBACT9F,EAAO,MAAO,CAAE8F,MAAO,mBAAqB,KCnBjC+B,GAAY,CACvBrT,IAAM3E,GAASmQ,EAAO,MAAO,CAAE8F,MAAO,UAAYjW,EAAKG,SACvD8N,OAAQ,CAACjO,EAAM4G,KACb,MAAMqR,EAAc5H,EAAerQ,EAAM4G,EAAQ6O,KAAKnF,KAAKE,UAAY,IACjE0H,EAAcD,EAAYpH,WAAW,QACvC,gBAAgBoH,IAChB,oBAAoBA,IACxB,OAAO9H,EAAO,MAAO,CAAE8F,MAAO,YAAa,YAAaiC,GAAelY,EAAKG,QAAQ,GCXlFgY,GAAS,CACb,OACA,QACA,SACA,QACA,WACA,aACA,aACA,UC0CK,MC5CDC,GAAmB,CAAC,KAAM,OAAQ,QAAS,QACpCC,GAAc,CACzBA,YAAa,CAACrY,EAAM4G,KAClB,MAAM0R,EAActY,EAAKG,QAAQiI,QAC9B/H,GAAgBlB,EAAUkB,IAAoC,YAApBA,EAAYe,MAMzD,GAJAkX,EAAYhL,SAASiL,IACnBA,EAAY/C,SAAU,CAAI,KAGvB8C,EAAY/X,OACf,OAAO0Q,EAASjR,EAAM4G,EAAQ6O,KAAKnF,KAGrC,MAAM3O,EAAO0O,EAAerQ,EAAM4G,EAAQ6O,KAAKnF,KAAKE,UAAY,YAC1DgI,EAAoC,KAAhB7W,EAAKiP,OAAgBjP,EAAO,YACtD,OAAOwO,EAAO,MAAO,CAAE8F,MAAO,kBAAoB,CAChD9F,EAAO,MAAO,CAAE8F,MAAO,uBAAyBuC,GAChDrI,EAAO,MAAO,CAAE8F,MAAO,2BAA6B,CAClD9F,EAAO,MAAO,CAAE8F,MAAO,0BAA4BjW,EAAKG,YAE1D,EAEJsY,QAAS,CAACzY,EAAM4G,KACd,IAAK5G,EAAKwV,QACR,OAAOvE,EAASjR,EAAM4G,EAAQ6O,KAAKnF,KAGrC,IAAIoI,EAASrI,EAAerQ,EAAM4G,GAAS6O,MAAMnF,MAAME,UAAU9N,cAC5D0V,GAAiBlS,SAASwS,IAAsB,UAAXA,IACxCA,EAAS,MAEI,SAAXA,IACFA,EAAS,QAIX,OAAOvI,EAAO,MAAO,CAAE8F,MADQ,OAAXyC,EAAkB,gBAAkB,mBACX,CAC3CvI,EAAO,MAAO,CAAE8F,MAAO,sBAAwBjW,EAAKG,UACpD,GCCAsT,GAAO,IACRU,MACAwC,MACAC,GACH+B,UC5CuB,CAAC3Y,EAAM4G,KACzBA,EAAQ6O,KAAKmD,YAAehS,EAAQ6O,KAAKoD,aAI5CjS,EAAQ6O,KAAKoD,WAAa,QAAU9Q,KAAKsK,SAAS1P,SAAS,IAAIiD,UAAU,EAAG,IAE9E,MAAMkT,EAAWlS,EAAQ6O,KAAKmD,WAAa,UAAYhS,EAAQ6O,KAAKoD,WAE9DjY,EAAOyP,EAAerQ,EAAM4G,EAAQ6O,KAAKnF,MAAME,UAAY,GAC3DuI,EAAY/Y,EAAKG,QACpBiI,QAAQ2D,GAAM5M,EAAU4M,IAAgB,aAAVA,EAAE3K,MAChCkH,KAAKnI,IACJA,EAAQqV,SAAU,EAElB,MAAMwD,EAAQ3I,EAAelQ,EAASyG,EAAQ6O,KAAKnF,KAAKE,UAAY,GACpErQ,EAAQ6Y,MAAQA,GAASA,EAAMnJ,MAAM,SAAW,IAAM,IACtD,MAAMoJ,EAAe9Y,EAAQA,QAC1BiI,OAAO/I,GACP6B,KAAK,IACLgY,WAAW,cAAe,IAE7B,OADA/Y,EAAQgZ,UAAY,GAAGhZ,EAAQ6Y,UAAUC,MAClC9Y,CAAO,IAGZA,EAAU,cAAc2Y,IAAWlY,OADjBmY,EAAUzQ,KAAKyD,GAAMA,EAAEoN,YAAWjY,KAAK,UAG/D,OADA0F,EAAQ6O,KAAK2D,OAAOtX,KAAK3B,GAClB,EAAE,EDkBTkZ,GE/CgB,CAACrZ,EAAM4G,KACvB,MAAM0S,EAAQjJ,EAAerQ,EAAM4G,EAAQ6O,KAAKnF,KAAKE,SACrD,OAAOL,EACL,MACA,CACE6F,MAAO,qBAAqBsD,KAC5BrD,MAAO,iBAETjW,EAAKG,QACN,EFuCDoZ,MGhDmB,CAACvZ,EAAM4G,KAC1B,MAAM4S,EAAY,QACZC,GAAapJ,EAAerQ,EAAM4G,EAAQ6O,KAAKnF,KAAKE,UAAYgJ,GAAW9W,cAmB3EgX,EAjBU,CACd,QACA,OACA,SACA,UACA,UACA,cACA,eACA,YACA,WACA,YACA,cACA,YACA,YAI0BxT,SAASuT,GAAaA,EAAYD,EAE9D,OAAOrJ,EAAO,QAAS,CAAE8F,MAAO,WAAY,gBAAiByD,GAAe,CAC1EvJ,EAAO,QAAS,CACdA,EAAO,KAAM,CACXA,EAAO,KAAM,CAAE8F,MAAO,kBACtB9F,EAAO,KAAM,CAAE8F,MAAO,oBAAsBjW,EAAKG,cAGrD,EHmBFwZ,WIjDwB,CAAC3Z,EAAM4G,KAC/B,MAAMgT,EAASvJ,EAAerQ,EAAM4G,EAAQ6O,KAAKnF,KAAKE,UAAY,GAElE,OAAOL,EAAO,MAAO,CAAE8F,MAAO,iBAAmB,CAC/C9F,EAAO,MAAO,CAAE8F,MAAO,uBACvB9F,EAAO,MAAO,CAAE8F,MAAO,yBAA2B,CAChDjW,EAAKG,QACLgQ,EAAO,MAAO,CAAE8F,MAAO,yBAAsC,KAAX2D,EAAgB,KAAKA,IAAW,MAEpFzJ,EAAO,MAAO,CAAE8F,MAAO,yBACvB,EJwCF4D,OKtDoB,CAAC7Z,EAAM4G,KAC3B,MAAMT,EAAMkK,EAAerQ,EAAM4G,EAAQ6O,KAAKnF,KAAKE,SACnD,OAAOL,EACL,MACA,CACE6F,MAAO,WAAW7P,KAClB8P,MAAO,aAETjW,EAAKG,QACN,EL8CD2Z,GMpDgB,IACT3J,EAAO,KAAM,CAAE,EAAE,MNoDxB4J,YOxDyB,CAAC/Z,EAAM4G,KAChC,MAAMoT,EAAkB3J,EAAerQ,EAAM4G,EAAQ6O,KAAKnF,KAAKE,UAAY,KAC3E,OAAOL,EAAO,MAAO,CAAE6F,MAAO,0BAA0BgE,MAAsBha,EAAKG,QAAQ,EPuD3F8Z,MQzDmB,CAACja,EAAM4G,KAC1B,MAAMnF,EAAQ4O,EAAerQ,EAAM4G,EAAQ6O,KAAKnF,KAAKE,UAAY,MACjE,OAAOL,EAAO,MAAO,CAAE8F,MAAO,WAAY,YAAaxU,GAASzB,EAAKG,QAAQ,ERwD7E8V,MSlDwB,CAACjW,EAAM4G,KAC/B,MAAMnF,EAAQ4O,EAAerQ,GACvBka,EAAWzY,EAAMb,MAAQa,EAAM+O,SAEhC5J,EAAQ6O,KAAKmD,YAAehS,EAAQ6O,KAAKoD,aAI5CjS,EAAQ6O,KAAKoD,WAAa,QAAU9Q,KAAKsK,SAAS1P,SAAS,IAAIiD,UAAU,EAAG,IAE9E,MAAMuU,EAAcvT,EAAQ6O,KAAKmD,WAAa,UAAYhS,EAAQ6O,KAAKoD,WACjEuB,EAAYF,EAAW,KAAOC,EAC9Bha,EAAUH,EAAKG,QAClBiI,OAAO/I,GACPiJ,KAAK9C,GAAMA,EAAE0T,WAAW,YAAaiB,GAAajB,WAAW,cAAe,MAC/E,IAAImB,EAAW,GACf,MAAMC,EAAa,GA4BnB,MA1BE,CAAC,QAAS,QAAS,SAAU,eAAgB,iBAAiBpU,SAC5DzE,EAAM8Y,OAAO7X,iBAGf2X,EAAW,IAAM5Y,EAAM8Y,MAAM7X,eAE3BjB,EAAM4Y,WACRA,EAAW5Y,EAAM4Y,SAAS3Z,QAAQ,aAAc,KAE9Ce,EAAM+Y,UAAU3K,MAAM,mBAExByK,EAAWxY,KAAK,eAAeL,EAAM+Y,aAEnC/Y,EAAMgZ,UAAU5K,MAAM,mBAExByK,EAAWxY,KAAK,eAAeL,EAAMgZ,aAGvCta,EAAQsS,QAAQ,IAAI2H,IAAYC,OAChCla,EAAQ2B,KAAK,KACTwY,EAAW/Z,SACbJ,EAAQsS,QAAQ,UAAU6H,EAAWpZ,KAAK,cAC1Cf,EAAQ2B,KAAK,MAEf8E,EAAQ6O,KAAK2D,OAAOtX,KAAK3B,EAAQe,KAAK,KAE/B,EAAE,ETOTwZ,KUxDmB1a,IAEZ,CACL4S,uBAAuB,EACvBzS,QAAS,CAAC,OAHCkQ,EAAerQ,GAAMwQ,UAAY,UAGnB,KAAMxQ,EAAKG,QAAS,aVqD/CmZ,MW5DoBtZ,IACpB,MAAM2a,EAAatK,EAAerQ,GAAMwQ,UAAY,GACpD,MAA0B,KAAtBmK,EAAW/J,OACN5Q,EAAKG,QAEPgQ,EAAO,OAAQ,CAAE6F,MAAO,UAAU2E,KAAgB3a,EAAKG,QAAQ,EXwDtEya,QYxDe5a,GACRmQ,EAAO,OAAQ,CAAE8F,MAAO,UAAYjW,EAAKG,SZwDhD0a,IazDiB,CAAC7a,EAAM4G,KACxB,GAAI5G,EAAKoQ,IAGP,OAAOpQ,EAET,MAAMyB,EAAQ4O,EAAerQ,EAAM4G,EAAQ6O,KAAKnF,KAC1C0F,EAAQvU,EAAMuU,OAASvU,EAAM+O,SAC7BsK,EAAarZ,EAAMwU,MACzB,IAAK6E,GAAYlK,OACf,OAAOT,EACL,MACA,CACE6F,SAEFhW,EAAKG,SAIJyG,EAAQ6O,KAAKmD,YAAehS,EAAQ6O,KAAKoD,aAI5CjS,EAAQ6O,KAAKoD,WAAa,QAAU9Q,KAAKsK,SAAS1P,SAAS,IAAIiD,UAAU,EAAG,IAE9E,MAAMuU,EAAcvT,EAAQ6O,KAAKmD,WAAa,UAAYhS,EAAQ6O,KAAKoD,WACjEkC,EAAaD,EAChBnK,MAAM,KACNrI,KAAKrD,GAAMA,EAAI,KAAOkV,IACtBjZ,KAAK,KAER,OAAOiP,EACL,MACA,CACE8F,MAAO8E,EACP/E,SAEFhW,EAAKG,QACN,EboBD6a,Oc/DqBhb,IACrB,MAAMiD,GAAQoN,EAAerQ,GAAMwQ,UAAY,IAAI9N,cACnD,OAAOyN,EACL,OACA,CACE8F,MAAO,YACP,YAAahT,GAEfjD,EAAKG,QACN,EduDD8a,Se5DsB,CAACjb,EAAM4G,KAC7B,MAAMuP,EAAQ9F,EAAerQ,EAAM4G,EAAQ6O,KAAKnF,KAAKE,UAAY,GACjE,OAAOL,EAAO,WAAY,CAAE8F,MAAO,eAAiB,CAClD9F,EAAO,SAAU,CAAE8F,MAAO,sBAAwBE,GAClDhG,EAAO,MAAO,CAAE8F,MAAO,eAAiBjW,EAAKG,UAC7C,EfwDF+a,GgB3DiBlb,IACjB,MAAMyB,EAAQzB,EAAKyB,MACnB,IAAIuU,EAAQvU,EAAMuU,OAAS,GAS3B,OARAA,GAASvU,EAAM,iBAAmB,uBAAuBA,EAAM,oBAAsB,GACrFuU,GAASvU,EAAM,mBAAqB,yBAAyBA,EAAM,sBAAwB,GAC3FuU,GAASvU,EAAM,mBAAqB,yBAAyBA,EAAM,sBAAwB,GAC3FuU,GAASvU,EAAM,qBACX,2BAA2BA,EAAM,wBACjC,GACJuU,GAASvU,EAAM,gBAAkB,sBAAsBA,EAAM,mBAAqB,GAE3E0O,EACL,IACA,CACE,iBAAkB,MAEpB,CACEA,EACE,IACA,CACE8F,OAAQjW,EAAKG,SAAW,IAAIe,KAAK,IACjC8U,QACA,oBAAqBvU,EAAM,iBAAmB,IAEhD,KAGL,EhBiCD0Z,KPOkB,CAACnb,EAAM4G,KACzB,MAAMnF,EAAQ4O,EAAerQ,EAAM4G,EAAQ6O,KAAKnF,KAC1C8K,EAAa3Z,GAAO+O,UAAY/O,GAAO4Z,QAAU5Z,GAAOb,KAC9D,IAAKwa,GAAoC,KAAtBA,EAAWxK,OAC5B,OAAO5Q,EAAKG,QAEd,GAAI8W,GAAU/Q,SAASkV,EAAWxK,OAAOlO,eACvC,OAAOyN,EAAO,OAAQ,CAAE6F,MAAO,iBAAiBoF,MAAiBpb,EAAKG,SAGxE,MAAMmb,EAzDW,CAAC7Z,IAClB,IAAI6Z,EAAO,CACTC,KAAM,EACNC,KAAM,KAGR,GAAI/Z,GAAOuU,MAAO,CAEhB,MAAMA,EAAQvU,EAAMuU,MAAMpF,OAAOlO,cAC3B+Y,EAAU5D,GAAW6D,KAAK1F,GAAOnB,QAAU,CAAE,EAC/C4G,GAASE,SACXL,EAAKC,KAAO,GAGd,MAAMK,EAASH,EAAQG,OACnBA,GAAUA,GAAU,GAAKA,GAAU,IACrCN,EAAKE,KAAOI,EACHjc,OAAOC,KAAKsX,IAAmBhR,SAASuV,EAAQI,cAAgB,MACzEP,EAAKE,KAAOtE,GAAkBuE,EAAQI,eAGxCP,EAAO,IACFA,KACA3b,OAAOmc,YAAYnc,OAAOoc,QAAQta,GAAO2G,QAAO,EAAEtI,KAAS8X,GAAgB1R,SAASpG,MAE7F,CACE,OAAOwb,CAAI,EA+BEU,CAAWva,GAClBwa,EAxBmB,EAACZ,EAAQC,KAClCD,EAASA,EAAOnC,WAAW,IAAK,KAEhCoC,EAAO3b,OAAOC,KAAK0b,GAChBY,OACAzc,QAAO,CAACD,EAAKM,KACZN,EAAIM,GAAOwb,EAAKxb,GACTN,IACN,IAEE,4CAA8C6b,EAAS,IAD7C1b,OAAOC,KAAK0b,GAAMpa,KAAK,KAAO,IAAMvB,OAAOqB,OAAOsa,GAAMpa,KAAK,MAelEib,CAAmBf,EAAYE,GAC3C1U,EAAQ6O,KAAK2G,MAAMvO,IAAIoO,GAEvB,MAAMN,EAAuB,IAAdL,EAAKC,KAAa,SAAW,SAEtCc,EAAS1c,OAAOoc,QAAQT,GAAMlT,QAAO,EAAEtI,KAAiB,SAARA,GAA0B,SAARA,IACxE,IAAIwc,EAAU,GAMd,OALID,EAAO9b,SACT+b,EACE,4BAA8BD,EAAO/T,KAAI,EAAExI,EAAKqG,KAAS,IAAIrG,MAAQqG,MAAOjF,KAAK,MAAQ,KAGtFiP,EACL,OACA,CACE6F,MAAO,iBAAiBoF,oBAA6BE,EAAKE,qBAAqBG,MAAWW,IAC1F,YAAaL,GAEfjc,EAAKG,QACN,EOpCDoc,EiB7DSvc,GACFmQ,EAAO,KAAM,GAAInQ,EAAKG,SjB6D7Bqc,GiB1DUxc,GACHmQ,EAAO,KAAM,GAAInQ,EAAKG,SjB0D7Bsc,GiBvDUzc,GACHmQ,EAAO,KAAM,GAAInQ,EAAKG,SjBuD7Buc,GiBhDU1c,GACHmQ,EAAO,KAAM,GAAInQ,EAAKG,SjBgD7Bwc,GiB7CU3c,GACHmQ,EAAO,KAAM,GAAInQ,EAAKG,SjB6C7Byc,GiB1CU5c,GACHmQ,EAAO,KAAM,GAAInQ,EAAKG,SjB0C7B0c,GiBvCU7c,GACHmQ,EAAO,KAAM,GAAInQ,EAAKG,SjBuC7B2c,eNpD6B9c,IAC7B,MACM+c,EAnBR,SAAqBC,GACnB,MACMC,EACJD,GAAsC,KAAvBA,EAAYpM,OAAgBoM,EAAYtc,QAAQ,UAAW,IAAM,EAElF,OAAIuc,GAAgBA,GAAgB,GAAKA,GAJvB,IAKTA,EAGiB,IAAjBA,EAAqB,EARZ,GAUpB,CAQsBC,CADN7M,EAAerQ,GAAMwQ,UACI7N,WAEvC,OACIwN,EAAO,MADY,MAAhB4M,EACW,CAAE9G,MAAO,sBAGrB,CAAEA,MAAO,qBAAsBD,MAAO,WAAW+G,QAHJ/c,EAAKG,QAKnD,EM2CLgd,UkBxEwBnd,GACjBmQ,EAAO,OAAQ,CAAE8F,MAAO,gBAAkBjW,EAAKG,SlBwEtDid,MU9DoBpd,IACb,CACL4S,uBAAuB,EACvBzS,QAAS,CAAC,IAAKH,EAAKG,QAAS,OV4D/Bkd,WmB1EyBrd,IACzB,MAAMyB,EAAQ4O,EAAerQ,GAAMwQ,UAAY,GAC/C,OAAOL,EAAO,MAAO,CAAE8F,MAAO,YAAYxU,KAAWzB,EAAKG,QAAQ,EnByElEmd,coBpD4Btd,GACrBmQ,EAAO,OAAQ,CAAE8F,MAAO,qBAAuBjW,EAAKG,SpBoD3Dod,QqB3EsBvd,GACfmQ,EAAO,MAAO,CAAE8F,MAAO,cAAgBjW,EAAKG,SrB2EnDqd,SC5CsB,CAACxd,EAAM4G,IACxB5G,EAAKwV,QAOH,GANE,CACLzE,EAAmB/Q,EAAM4G,EAAQ6O,KAAKnF,QACnCtQ,EAAKG,QACR6Q,EAAiBhR,EAAM4G,EAAQ6O,KAAKnF,MDwCxCmN,KLvDmBzd,IACnB,MAAM0d,EAAa1d,EAAKyB,MACxB,IAAIkc,EAAW,CACbC,YAAaF,EAAWza,MAAQ,QAAQP,cACxCmb,OAAQH,EAAWG,QAAU,UAC7BC,QAASJ,EAAWI,SAAW,SAGjC,OAAO3N,EACL,MACA,CACE8F,MAAO,WACP,gBAAiB0H,EAASC,YAE5B,CACE9F,IA1BoB+F,EA2BHF,EAASE,OA1BvB1N,EAAO,MAAO,CAAE8F,MAAO,oBAAsB4H,KAL3BC,EAgCHH,EAASG,QA/BxB3N,EAAO,MAAO,CAAE8F,MAAO,oBAAsB6H,KAL3B3d,EAqCHH,EAAKG,QApCpBgQ,EAAO,MAAO,CAAE8F,MAAO,oBAAsB9V,IAqChD4X,KAtCoB,IAAC5X,EAIA2d,EAIDD,CAgCvB,EKmCDE,UsB/EwB/d,GACjBmQ,EAAO,MAAO,CAAE8F,MAAO,gBAAkBjW,EAAKG,StB+ErD6d,KMhEmBhe,IACZ,CAAEuS,4BAA4B,EAAMpS,QAASH,EAAKG,UNgEzD8d,KuBhFmBje,GACZmQ,EAAO,MAAO,CAAE8F,MAAO,WAAa,CACzC9F,EAAO,MAAO,CAAE8F,MAAO,gBAAkB,IACzC9F,EAAO,MAAO,CAAE8F,MAAO,mBAAqB,CAC1CjW,EAAKG,QACLgQ,EAAO,MAAO,CAAE8F,MAAO,kBAAoB,QvB4E/CiI,IwBjFkBle,GACXmQ,EACL,MACA,CACE8F,MAAO,UAETjW,EAAKG,SxB4EPge,QyBnFsBne,GACfmQ,EAAO,OAAQ,CAAE8F,MAAO,cAAgBjW,EAAKG,SzBmFpDie,M0B9EoBpe,GACbA,EAAKG,Q1B8EZke,M2BpFoBre,IACpB,MAAMwZ,EAAY,QACZ8E,GAAajO,EAAerQ,GAAMwQ,UAAYgJ,GAAW9W,cAKzD6b,EAHU,CAAC,QAAS,OAAQ,QAAS,aAGfrY,SAASoY,GAAaA,EAAY9E,EAE9D,OAAOrJ,EACL,MACA,CAAE8F,MAAOsI,IAAgB/E,EAAY,WAAa,YAAY+E,KAC9Dve,EAAKG,QACN,E3BwEDqe,S4BrFuBxe,IACvB,MAAMye,EAAgBpO,EAAerQ,GAAMwQ,SAC3C,OAAOL,EAAO,MAAO,CAAE8F,MAAO,eAAiB,CAC7C9F,EAAO,MAAO,CAAE8F,MAAO,oBAAsBjW,EAAKG,SAClDgQ,EAAO,MAAO,CAAE8F,MAAO,kBAAmBD,MAAO,eAAeyI,aAA2B,IAC3FtO,EAAO,MAAO,CAAE8F,MAAO,yBAA2B,KAClD,E5BgFFyI,M6BvFmB,CAAC1e,EAAM4G,KAC1B,MAAMnF,EAAQ4O,EAAerQ,EAAM4G,EAAQ6O,KAAKnF,KAIhD,MAHwB,OAApBtQ,EAAKG,QAAQ,IACfH,EAAKG,QAAQsU,QAER,CAAC,MAAMzU,EAAKoB,QAAQK,EAAM+O,oBAAqBxQ,EAAKG,QAAS,iBAAiB,K7BmFlF6X,GACH2G,a8BxF0B,CAAC3e,EAAM4G,KACjC,MAAM6X,EAAgBpO,EAAerQ,EAAM4G,EAAQ6O,KAAKnF,KAAKE,SAC7D,OAAOL,EAAO,MAAO,CAAE8F,MAAO,oBAAsB,CAClD9F,EAAO,MAAO,CAAE8F,MAAO,oBAAsBjW,EAAKG,SAClDgQ,EAAO,MAAO,CAAE8F,MAAO,kBAAmBD,MAAO,eAAeyI,aAA2B,IAC3FtO,EAAO,MAAO,CAAE8F,MAAO,yBAA2B,KAClD,E9BmFF2I,OUrEqB5e,IACd,CACL4S,uBAAuB,EACvBzS,QAASH,EAAKG,UVmEhB0e,GiB5EU7e,GACHmQ,EAAO,KAAM,GAAInQ,EAAKG,SjB4E7B2e,OH7EoB,CAAC9e,EAAM4G,KAC3B,MAAMnF,EAAQ4O,EAAerQ,EAAM4G,EAAQ6O,KAAKnF,KAE3C1J,EAAQ6O,KAAKmD,YAAehS,EAAQ6O,KAAKoD,aAI5CjS,EAAQ6O,KAAKoD,WAAa,QAAU9Q,KAAKsK,SAAS1P,SAAS,IAAIiD,UAAU,EAAG,IAE9E,MAAMuU,EAAcvT,EAAQ6O,KAAKmD,WAAa,UAAYhS,EAAQ6O,KAAKoD,WAEjEkG,EACH5G,GAAOjS,SAASzE,EAAMud,IAAItc,eAAiB,SAAWjB,EAAMud,IAAItc,eAAkB,OAE/Euc,EAAc,CAClBnI,GAAIqD,EACJlE,MAAOxU,EAAMwU,OAAS,GACtB+I,GAAID,EACJG,QAASzd,EAAMyd,SAAW,GAC1B/e,QAASH,EAAKG,QAAQe,KAAK,KAI7B,OAFA0F,EAAQ6O,KAAK0J,UAAUrd,KAAKmd,GAErB,EAAE,EGuDTG,O+B1EoB,CAACpf,EAAM4G,KAC3B,MACMmW,EAnBR,SAAqBC,GACnB,MACMC,EACJD,GAAsC,KAAvBA,EAAYpM,OAAgBoM,EAAYtc,QAAQ,UAAW,IAAM,EAElF,OAAIuc,GAAgBA,GAAgB,GAAKA,GAJvB,IAKTA,EAGiB,IAAjBA,EAAqB,EARZ,GAUpB,CAQsBC,CADN7M,EAAerQ,EAAM4G,EAAQ6O,KAAKnF,KAAKE,UAErD,OAAOL,EAAO,MAAO,CAAE8F,MAAO,YAAaD,MAAO,WAAW+G,OAAmB/c,EAAKG,QAAQ,E/BwE7Fkf,KgCjGmBrf,IACnB,MAAMyB,EAAQ4O,EAAerQ,GAAMwQ,UAAY,OAC/C,OAAOL,EAAO,MAAO,CAAE8F,MAAO,UAAW,YAAaxU,GAASzB,EAAKG,QAAQ,EhCgG5Emf,KFhDmBtf,IACnB,MACMuf,EAhDR,SAAuBC,GACrB,IAAI/e,EACA8e,EAAW,CAAEE,OAAO,GACxB,MAAMC,EAAa,wBAAwBhE,KAAK8D,GAC1CG,EACI,GADJA,EAEI,EAFJA,EAGK,EAHLA,EAIK,GAJLA,EAKU,EALVA,EAMU,EAGhB,GAAID,IAAejf,EAAQif,EAAW,IAAK,CAEzC,OADAH,EAASK,MAAQF,EAAW,IAAM,IAAIhd,cAC9B6c,EAASK,MACf,IAAK,KACCnf,EAAQkf,EACVlf,EAAQkf,EACClf,EAAQkf,IACjBlf,EAAQkf,GAEV,MACF,IAAK,MACClf,EAAQkf,EACVlf,EAAQkf,EACClf,EAAQkf,IACjBlf,EAAQkf,GAEV,MACF,SACOJ,EAASE,MAAQD,EAAUjf,SAAWE,EAAMF,UAC3CE,EAAQkf,EACVlf,EAAQkf,EACClf,EAAQkf,IACjBlf,EAAQkf,IAMhBJ,EAAS9e,MAAQA,CACrB,CACE,OAAO8e,CACT,CAImBM,CADHxP,EAAerQ,GAAMwQ,UAEnC,IAAK+O,EAASE,MACZ,OAAOzf,EAAKG,QAEd,IAAI2f,EAAa,CAAE,EAMnB,OAJEA,EADEP,EAASK,KACE,CAAE5J,MAAO,cAAcuJ,EAAS9e,QAAQ8e,EAASK,QAEjD,CAAE,YAAaL,EAAS9e,OAEhC0P,EAAO,OAAQ2P,EAAY9f,EAAKG,QAAQ,EEqC/C4f,QoB3FqB,CAAC/f,EAAM4G,KAC5B,MAAMoZ,EAAgB3P,EAAerQ,EAAM4G,EAAQ6O,KAAKnF,KAAKE,SAW7D,OAAOL,EAAO,UAAW,CAAE8F,MAAO,cAAgB,CAChD9F,EAAO,UAAW,CAAE,EAXR,WAAa6P,EAAgB,KAAKA,IAAkB,KAYhE7P,EAAO,MAAO,CAAE8F,MAAO,sBAAwBjW,EAAKG,UACpD,EpB6EF8f,IiC/FWjgB,GACJmQ,EAAO,MAAO,GAAInQ,EAAKG,SjC+F9B+f,IkChGWlgB,GACJmQ,EAAO,MAAO,GAAInQ,EAAKG,SlCgG9BggB,ImChEiB,CAACngB,EAAM4G,KACxB,IAAK5G,EAAKwV,QACR,OAAOvE,EAASjR,EAAM4G,EAAQ6O,KAAKnF,KAGrC,MAAM7O,EAAQ4O,EAAerQ,EAAM4G,EAAQ6O,KAAKnF,KAC1C1P,EAAOa,GAAO+O,UAAY/O,GAAOb,MAAQ,MACzCwf,EAAQ,OAAOxf,EAAKF,QAAQ,MAAO,QAAQoR,MACjD,MAAO,CACL3B,EAAO,QAAS,CACdlN,KAAM,QACN6T,GAAIsJ,EACJxf,KAAM,aAAeZ,EAAKqU,QAC1B4B,MAAO,SACPoK,QAASrgB,EAAKqW,OAEhBlG,EACE,QACA,CACE8F,MAAO,eACPqK,IAAKF,EACLpK,MAAOvU,EAAMuU,OAEfpV,GAEFuP,EACE,MACA,CACE8F,MAAO,kBAETjW,EAAKG,SAER,EnCiCDogB,KmC7FkB,CAACvgB,EAAM4G,KACzB,MAAM4Z,EAAWxgB,EAAKG,QAAQiI,QAC3B/H,GAAgBlB,EAAUkB,IAAoC,QAApBA,EAAYe,MAEnDiT,EAAUvC,IAMhB,OALA0O,EAASlT,SAASmT,IAChBA,EAAQjL,SAAU,EAClBiL,EAAQpM,QAAUA,CAAO,IAGtBmM,EAASjgB,QAIdigB,EAAS,GAAGnK,MAAO,EACZlG,EACL,MACA,CACE8F,MAAO,WAETuK,IATOvP,EAASjR,EAAM4G,EAAQ6O,KAAKnF,IAUpC,KnCyEE+H,GAGHqI,EoC7FmB1gB,GACZmQ,EAAO,OAAQ,CAAE8F,MAAO,YAAcjW,EAAKG,SpC6FlD+S,EoC1FqBlT,GACjBA,EAAKoQ,IAGApQ,EAEFmQ,EAAO,OAAQ,CAAE8F,MAAO,YAAcjW,EAAKG,SpCqFlDwgB,EoClFwB3gB,GACjBmQ,EAAO,OAAQ,CAAE8F,MAAO,YAAcjW,EAAKG,SpCkFlDqF,EoC/EqBxF,GACdmQ,EAAO,OAAQ,CAAE8F,MAAO,YAAcjW,EAAKG,UpCiF9CygB,GAAgBjhB,OAAOC,KAAK6T,IAG5BoN,GXvGF,SAASC,EAAaC,EAASC,EAAYxN,IAC3C,MAAMyN,EAAiB3U,IAEnB,SAAS4U,EAAepS,EAAM4E,GAC1B,OAAOsN,EAAUD,EAASjS,EAAM4E,EAAMuN,EAAcra,SAAW,GAC3E,CAEQ,OALAqa,EAAcra,QAAUjH,OAAOwhB,OAAOF,EAAcra,SAAW,CAAE,EAAE0F,GAInE4U,EAAeta,QAAUqa,EAAcra,QAChCsa,CAAc,EAMzB,OAJAD,EAAcG,OAAS,SAAsBC,GAEzC,OAAOP,EADSO,EAASN,EAASE,EAAcra,SACnBoa,EAChC,EACMC,CACX,CWyFeH,CAAarN,IqCtG5B,SAAS6N,GAAqBhR,GAO5B,OANkBA,EACf4I,WAAW3H,EAAmB,IAC9B2H,WAAW1H,EAAuB,IAClC0H,WAAW,KAAOzH,EAA2B,IAC7CyH,WAAWzH,EAA4B,KAAM,IAC7CyH,WAAWzH,EAA2B,GAE3C,CAEA,SAAS8P,GAAuBjR,GAK9B,OAJkBA,EACf4I,WpDkH0B,kBoDlHS,IACnCA,WpDkH4B,kBoDlHS,IACrCA,WpDkHwB,8BoDlHS,GAEtC,CAQA,SAASsI,GAAwBlR,EAAKmF,GACpC,MAAMgM,EAAWhM,EAAKgM,SACtB,IAAK,MAAOC,EAAMvhB,KAAYR,OAAOoc,QAAQ0F,GAC3CnR,EAAMA,EAAI4I,WAAWwI,EAAMvhB,GAE7B,OAAOmQ,CACT,CAQA,SAASqR,GAA4BrR,EAAKmF,GACxC,GAA2B,IAAvBA,EAAK2D,OAAO7Y,OACd,OAAO+P,EAGT,MADiB,sCAAwCmF,EAAK2D,OAAOlY,KAAK,MAAQ,cAChEoP,CACpB,CAeA,SAASsR,GAAwBtR,EAAKmF,GACpC,GAA8B,IAA1BA,EAAK0J,UAAU5e,OACjB,OAAO+P,EAMT,OAJkBmF,EAAK0J,UAAU7W,KAC9B9C,GACC,yDAAyDA,EAAEsR,4BAA4BtR,EAAEyQ,4BAA4BzQ,EAAEwZ,0BAA0BxZ,EAAE0Z,YAAY1Z,EAAErF,uBAEpJe,KAAK,IAAMoP,CAC9B,CC7EA,SAASuR,GAAyB1hB,EAASsV,GAEzC,MAAMgM,EAAW,CAAE,EACnB,IAAIK,EAAQ,EAEZ,MAAMC,EAAiC,CAACC,EAAaC,EAAWjT,EAAU4B,GAAO,KAC/E,MAAM8Q,EAAO5P,IAgBb,OAfkB,IAAdmQ,GACFR,EAASC,GAAQvhB,EAAQyF,UAAUoc,EAAaC,GAChD9hB,EAAUA,EAAQyF,UAAU,EAAGoc,GAAeN,EAAOvhB,EAAQyF,UAAUqc,KAEvER,EAASC,GAAQvhB,EAAQyF,UAAUoc,GACnC7hB,EAAUA,EAAQyF,UAAU,EAAGoc,GAAeN,EAAO1S,GAEnD4B,IACE6Q,EAASC,GAAM7Q,WAAW,QAC5B4Q,EAASC,GAAQD,EAASC,GAAM9b,UAAU,IAExC6b,EAASC,GAAM5Q,SAAS,QAC1B2Q,EAASC,GAAQD,EAASC,GAAM9b,UAAU,EAAG6b,EAASC,GAAMnhB,OAAS,KAGlEyhB,EAAcN,EAAKnhB,OAASyO,EAASzO,MAAM,EAGpD,WAAQuhB,EAAQ5Q,EAAa/Q,EAASyR,EAAkBkQ,KAAgB,CACtE,MAAMjS,EAAQ+B,EAAiB8J,KAAKvb,EAAQyF,UAAUkc,IACtD,GAAIjS,EAAMgF,QAAQqN,MAAO,CACvB,MAAMA,EAAQrS,EAAMgF,OAAOqN,MACrBC,EAAYtS,EAAMgF,OAAOsN,UACR,OAAnBhiB,EAAQ2hB,KAEVA,GAAS,GAEX,MAAMM,EAAoB,IAAIzQ,OAAO,KAAOuQ,EAAQ,UAC9CG,EAAYnR,EAAa/Q,EAASiiB,EAAmBN,EAAQI,EAAM3hB,QAEnEmhB,EAAO5P,IAEX2P,EAASC,IADO,IAAdW,EACeliB,EAAQyF,UAAUkc,EAAQI,EAAM3hB,OAAS4hB,EAAU5hB,OAAQ8hB,GAE3DliB,EAAQyF,UAAUkc,EAAQI,EAAM3hB,OAAS4hB,EAAU5hB,QAGtE,MAAM+hB,EAAc,aAAaJ,IAAQC,IAAYT,MAASQ,eAC9D/hB,EACEA,EAAQyF,UAAU,EAAGkc,GACrBQ,IACe,IAAdD,EAAmBliB,EAAQyF,UAAUyc,EAAY,EAAIH,EAAM3hB,QAAU,IACxEuhB,GAAgBQ,EAAY/hB,MAClC,MAAW,GAAIsP,EAAMgF,QAAQ0N,OAAQ,CAC/B,MAAMA,EAAS1S,EAAMgF,OAAO0N,OAEtBC,EAAa,KADD3S,EAAMgF,OAAO4N,UAAU/f,iBAEnC2f,EAAYliB,EAAQuC,cAAc0D,QAAQoc,EAAYV,EAAQ,GACpEA,EAAQC,EAA+BD,EAAQS,EAAOhiB,OAAQ8hB,EAAWG,GAAY,EAC3F,MAAW,GAAI3S,EAAMgF,OAAO6N,SAAU,CAChC,MAAMA,EAAW7S,EAAMgF,OAAO6N,SACxBC,EAAY9S,EAAMgF,OAAO8N,UACzBC,EAAU/S,EAAMgF,OAAO+N,QAC7Bd,EAAQC,EACND,EAAQa,EAAUpiB,OAClBuhB,EAAQY,EAASniB,OAASqiB,EAAQriB,OAClCqiB,EAER,CACA,CAGE,OADAnN,EAAKgM,SAAWA,EACT,CAACthB,EAASsV,EACnB,CAOA,SAASoN,GAAuB1iB,EAASsV,GACvC,IAAIqM,EAAQ,EACZ,WAAQA,EAAQ5Q,EAAa/Q,EAAS0R,EAAgBiQ,KAAgB,CACpE,MACMgB,EADQjR,EAAe6J,KAAKvb,EAAQyF,UAAUkc,IAChC,GACdQ,EAAc,aAAaQ,eACjC3iB,EAAUA,EAAQyF,UAAU,EAAGkc,GAASQ,EAAcniB,EAAQyF,UAAUkc,EAAQgB,EAAMviB,QACtFuhB,GAAgBQ,EAAY/hB,MAChC,CACE,MAAO,CAACJ,EAASsV,EACnB,CCrFA,MAAM7O,GAAU,CACd2F,cAAe,IAAIqU,IACnBrY,cAAc,EACdJ,gBvCwGqB,CAAC,QAAS,OAAQ,QAAS,QAAS,MuCvGzDD,kBAAkB,EAClB6F,QAAUgV,IACJnc,GAAQgS,YAEVoK,QAAQC,KAAKF,EAAItK,QAASsK,EAAI7U,WAAY6U,EAAI5U,aACpD,GAGM+U,GAAarC,iBAEM,CAACnG,EAAMpO,KAC9B,MAAM6W,EAAU,CAACD,IACb5W,EAAK8W,oBACPD,EAAQrhB,MpD2CFgN,GAASc,GAAKd,KoDzCtBqU,EAAQrhB,MrD4FAgN,GAASc,GAAKd,IqD5FUyE,IAChC,MAAO8P,EAAcC,GDwEhB,SAAuBhT,GAC5B,IAAImF,EAAO,CAAE,EACb,MAAM8N,EAAgB,CAAC1B,GAA0BgB,IACjD,IAAK,MAAMW,KAAgBD,GACxBjT,EAAKmF,GAAQ+N,EAAalT,EAAKmF,GAElC,MAAO,CAACnF,EAAKmF,EACf,CC/E2CgO,CAAc/I,GACvD,OxDVa,SAAcgJ,GACzB,MAAMP,EAA2B,mBAAVO,EAAuB,CAC1CA,GACAA,GAAS,GACPC,EAAa,IAAI,GACvB,MAAO,CACH,OAAAnQ,CAASnH,EAAOC,GACZ,MAAM1F,EAAU0F,GAAQ,CACpBsX,WAAW,EACXC,OAAQzX,EACR8D,OAAQyT,EACRlO,KAAM,MAEJqO,EAAUld,EAAQid,QAAUzX,EAC5B2X,EAAWnd,EAAQsJ,OACnBuF,EAAO7O,EAAQ6O,MAAQ,KAC7B,GAAuB,mBAAZqO,EACP,MAAM,IAAIE,MyDrCjB,MzDwCG,MAAM1T,EAAM1J,EAAQgd,WAAa3jB,MAAMC,QAAQmM,GAASA,EAAQyX,EAAQzX,EAAOzF,GAC/E,IAAIkI,EAAOlI,EAAQgd,WAAa3jB,MAAMC,QAAQmM,GAASoD,EAAWpD,GAAS,GAAIzF,GAAW6I,EAAWa,EAAK1J,GAC1G,IAAI,IAAIF,EAAM,EAAGA,EAAMyc,EAAQ5iB,OAAQmG,IAAM,CACzC,MAAMud,EAASd,EAAQzc,GACvB,GAAsB,mBAAXud,GAAyBF,EAAU,CAC1C,MAAMG,EAAUD,EAAOnV,EAAM,CACzB1C,MAAO0X,EACP5T,OAAQ6T,EACRpV,UACA8G,SAEJ3G,EAAOW,EAAWyU,GAAWpV,EAAMlI,EACvD,CACA,CACY,MAAO,CACH,QAAIud,GACA,GAAwB,mBAAbJ,EACP,MAAM,IAAIC,MyDxDzB,MzD0DW,OAAOD,EAASjV,EAAMA,EAAKlI,QAC9B,EACDkI,OACAwB,MACAX,SAAUb,EAAKa,SAE/B,EAEA,CwDrCSyU,CAAKjB,GAAS3P,QAAQ6P,EAAc,CACzCnT,YACGtJ,GACH6O,KAAM,IACD6N,EACHhT,IAAK+S,EACLzK,WAAYtM,EAAKsM,WACjBwD,MAAO,IAAItP,IACXsM,OAAQ,GACR+F,UAAW,KAEb,gBFmDG,SAAqB7O,EAAKmF,GAC/B,IAAI4O,EAAQ/T,EACZ,MAAMgU,EAAiB,CACrBhD,GACAK,GACAC,GACAL,GACAC,IAEF,IAAK,MAAM+C,KAAiBD,EAC1BD,EAAQE,EAAcF,EAAO5O,GAE/B,OAAO4O,CACT","x_google_ignoreList":[0,1,2,3,4,5,6,7,8,9,14,65]} \ No newline at end of file +{"version":3,"file":"bbcode-parser.min.js","sources":["../../node_modules/.pnpm/@bbob+plugin-helper@4.4.1/node_modules/@bbob/plugin-helper/es/char.mjs","../../node_modules/.pnpm/@bbob+plugin-helper@4.4.1/node_modules/@bbob/plugin-helper/es/helpers.mjs","../../node_modules/.pnpm/@bbob+plugin-helper@4.4.1/node_modules/@bbob/plugin-helper/es/TagNode.mjs","../../node_modules/.pnpm/@bbob+parser@4.4.1/node_modules/@bbob/parser/es/Token.mjs","../../node_modules/.pnpm/@bbob+parser@4.4.1/node_modules/@bbob/parser/es/utils.mjs","../../node_modules/.pnpm/@bbob+parser@4.4.1/node_modules/@bbob/parser/es/lexer.mjs","../../node_modules/.pnpm/@bbob+parser@4.4.1/node_modules/@bbob/parser/es/NodeList.mjs","../../node_modules/.pnpm/@bbob+parser@4.4.1/node_modules/@bbob/parser/es/parse.mjs","../../node_modules/.pnpm/@bbob+core@4.4.1/node_modules/@bbob/core/es/utils.mjs","../../node_modules/.pnpm/@bbob+core@4.4.1/node_modules/@bbob/core/es/errors.mjs","../../node_modules/.pnpm/@bbob+core@4.4.1/node_modules/@bbob/core/es/index.mjs","../../node_modules/.pnpm/@bbob+html@4.4.1/node_modules/@bbob/html/es/index.mjs","../../bbcode-src/utils/common.js","../../bbcode-src/plugins/lineBreak.js","../../bbcode-src/plugins/preserveWhitespace.js","../../bbcode-src/plugins/removeEmptyLinesInAttr.js","../../node_modules/.pnpm/@bbob+preset@4.4.1/node_modules/@bbob/preset/es/preset.mjs","../../bbcode-src/tags/accordion.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/script.js","../../bbcode-src/tags/size.js","../../bbcode-src/tags/textmessage.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/fontawesome.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/quote.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/tabs.js","../../bbcode-src/tags/discourse-core-replacement.js","../../bbcode-src/utils/postprocess.js","../../bbcode-src/utils/preprocess.js","../../bbcode-src/index.js"],"sourcesContent":["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.mjs';\nfunction isTagNode(el) {\n return typeof el === 'object' && el !== null && 'tag' in el;\n}\nfunction isStringNode(el) {\n return typeof el === 'string';\n}\n// check string is end of line\nfunction isEOL(el) {\n return el === N;\n}\nfunction keysReduce(obj, reduce, def) {\n const keys = Object.keys(obj);\n return keys.reduce((acc, key)=>reduce(acc, key, obj), def);\n}\nfunction getNodeLength(node) {\n if (isTagNode(node) && Array.isArray(node.content)) {\n return node.content.reduce((count, contentNode)=>{\n return count + getNodeLength(contentNode);\n }, 0);\n }\n if (isStringNode(node)) {\n return String(node).length;\n }\n return 0;\n}\nfunction appendToNode(node, value) {\n if (Array.isArray(node.content)) {\n node.content.push(value);\n }\n}\n/**\n * C0 control characters (plus DEL). Browsers remove ASCII tab/newline/CR and\n * ignore other control chars when resolving a URL scheme, so a control char\n * inside the scheme keyword (e.g. \"java\\tscript:\") would otherwise slip past\n * the scheme guard below while still executing. Strip them so the scheme can't\n * be split apart. Harmless for non-URL attributes, where such chars aren't valid.\n */ const CONTROL_CHARS_RE = /[\\u0000-\\u001F\\u007F]/g;\n/**\n * Escapes an attribute value for safe HTML output and neutralizes\n * dangerous URL schemes.\n * @param {string} value\n */ function escapeAttrValue(value) {\n return value.replace(CONTROL_CHARS_RE, '').replace(/&/g, '&').replace(//g, '>').replace(/\"/g, '"').replace(/'/g, ''')// eslint-disable-next-line no-script-url\n .replace(/(javascript|data|vbscript|file):/gi, '$1%3A');\n}\n/**\n * @deprecated use escapeAttrValue\n */ const escapeHTML = escapeAttrValue;\n/**\n * Accept name and value and return valid html5 attribute string\n */ function attrValue(name, value) {\n // in case of performance\n switch(typeof value){\n case 'boolean':\n return value ? `${name}` : '';\n case 'number':\n return `${name}=\"${value}\"`;\n case 'string':\n return `${name}=\"${escapeAttrValue(value)}\"`;\n case 'object':\n return `${name}=\"${escapeAttrValue(JSON.stringify(value))}\"`;\n default:\n return '';\n }\n}\n/**\n * Transforms attrs to html params string\n * @example\n * attrsToString({ 'foo': true, 'bar': bar' }) => 'foo=\"true\" bar=\"bar\"'\n */ function attrsToString(values) {\n // To avoid some malformed attributes\n if (values == null) {\n return '';\n }\n return keysReduce(values, (arr, key, obj)=>[\n ...arr,\n attrValue(key, obj[key])\n ], [\n ''\n ]).join(' ');\n}\n/**\n * Gets value from\n * @example\n * getUniqAttr({ 'foo': true, 'bar': bar' }) => 'bar'\n */ function getUniqAttr(attrs) {\n return keysReduce(attrs || {}, (res, key, obj)=>obj[key] === key ? obj[key] : null, null);\n}\nexport { attrsToString, attrValue, appendToNode, escapeHTML, escapeAttrValue, getNodeLength, getUniqAttr, isTagNode, isStringNode, isEOL };\n","import { CLOSE_BRAKET, OPEN_BRAKET, SLASH } from './char.mjs';\nimport { appendToNode, attrsToString, attrValue, getNodeLength, getUniqAttr, isTagNode } from './helpers.mjs';\nconst getTagAttrs = (tag, params)=>{\n const uniqAttr = getUniqAttr(params);\n if (uniqAttr) {\n const tagAttr = attrValue(tag, uniqAttr);\n const attrs = {\n ...params\n };\n delete attrs[String(uniqAttr)];\n const attrsStr = attrsToString(attrs);\n return `${tagAttr}${attrsStr}`;\n }\n return `${tag}${attrsToString(params)}`;\n};\nconst toString = (node, openTag, closeTag)=>{\n if (isTagNode(node)) {\n return node.toString({\n openTag,\n closeTag\n });\n }\n return String(node);\n};\nconst nodeTreeToString = (content, openTag, closeTag)=>{\n if (Array.isArray(content)) {\n return content.reduce((r, node)=>{\n if (node !== null) {\n return r + toString(node, openTag, closeTag);\n }\n return r;\n }, '');\n }\n if (content) {\n return toString(content, openTag, closeTag);\n }\n return null;\n};\nexport class TagNode {\n get length() {\n return getNodeLength(this);\n }\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 setStart(value) {\n this.start = value;\n }\n setEnd(value) {\n this.end = value;\n }\n toTagStart({ openTag = OPEN_BRAKET, closeTag = CLOSE_BRAKET } = {}) {\n const tagAttrs = getTagAttrs(String(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, this.attrs, this.content, this.start, this.end);\n }\n toString({ openTag = OPEN_BRAKET, closeTag = CLOSE_BRAKET } = {}) {\n const content = this.content ? nodeTreeToString(this.content, openTag, closeTag) : '';\n const tagStart = this.toTagStart({\n openTag,\n closeTag\n });\n if (this.content === null || Array.isArray(this.content) && this.content.length === 0) {\n return tagStart;\n }\n return `${tagStart}${content}${this.toTagEnd({\n openTag,\n closeTag\n })}`;\n }\n toJSON() {\n return {\n tag: this.tag,\n attrs: this.attrs,\n content: this.content,\n start: this.start,\n end: this.end\n };\n }\n static create(tag, attrs = {}, content = null, start) {\n return new TagNode(tag, attrs, content, start);\n }\n static isOf(node, type) {\n return node.tag === type;\n }\n constructor(tag, attrs, content, start, end){\n this.tag = tag;\n this.attrs = attrs;\n this.content = content;\n this.start = start;\n this.end = end;\n }\n}\n","import { OPEN_BRAKET, CLOSE_BRAKET, SLASH } from '@bbob/plugin-helper';\n// type, value, line, row, start pos, end pos\nexport const TYPE_ID = 't'; // 0;\nexport const VALUE_ID = 'v'; // 1;\nexport const LINE_ID = 'l'; // 3;\nexport const COLUMN_ID = 'r'; // 2;\nexport const START_POS_ID = 's'; // 4;\nexport const END_POS_ID = 'e'; // 5;\nexport const TYPE_WORD = 1; // 'word';\nexport const TYPE_TAG = 2; // 'tag';\nexport const TYPE_ATTR_NAME = 3; // 'attr-name';\nexport const TYPE_ATTR_VALUE = 4; // 'attr-value';\nexport const TYPE_SPACE = 5; // 'space';\nexport const TYPE_NEW_LINE = 6; // 'new-line';\nconst getTokenValue = (token)=>{\n if (token && typeof token[VALUE_ID] !== 'undefined') {\n return token[VALUE_ID];\n }\n return '';\n};\nconst getTokenLine = (token)=>token && token[LINE_ID] || 0;\nconst getTokenColumn = (token)=>token && token[COLUMN_ID] || 0;\nconst getStartPosition = (token)=>token && token[START_POS_ID] || 0;\nconst getEndPosition = (token)=>token && token[END_POS_ID] || 0;\nconst isTextToken = (token)=>{\n if (token && typeof token[TYPE_ID] !== 'undefined') {\n return token[TYPE_ID] === TYPE_SPACE || token[TYPE_ID] === TYPE_NEW_LINE || token[TYPE_ID] === TYPE_WORD;\n }\n return false;\n};\nconst isTagToken = (token)=>{\n if (token && typeof token[TYPE_ID] !== 'undefined') {\n return token[TYPE_ID] === 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[TYPE_ID] !== 'undefined') {\n return token[TYPE_ID] === TYPE_ATTR_NAME;\n }\n return false;\n};\nconst isAttrValueToken = (token)=>{\n if (token && typeof token[TYPE_ID] !== 'undefined') {\n return token[TYPE_ID] === 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 tokenToText = (token, openTag = OPEN_BRAKET, closeTag = CLOSE_BRAKET)=>{\n let text = openTag;\n text += getTokenValue(token);\n text += closeTag;\n return text;\n};\n/**\n * @export\n * @class Token\n */ class Token {\n get type() {\n return this[TYPE_ID];\n }\n isEmpty() {\n return this[TYPE_ID] === 0 || isNaN(this[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 getStart() {\n return getStartPosition(this);\n }\n getEnd() {\n return getEndPosition(this);\n }\n toString({ openTag = OPEN_BRAKET, closeTag = CLOSE_BRAKET } = {}) {\n return tokenToText(this, openTag, closeTag);\n }\n constructor(type, value, row = 0, col = 0, start = 0, end = 0){\n this[LINE_ID] = row;\n this[COLUMN_ID] = col;\n this[TYPE_ID] = type || 0;\n this[VALUE_ID] = String(value);\n this[START_POS_ID] = start;\n this[END_POS_ID] = end;\n }\n}\nexport { Token };\nexport default Token;\n","import { QUOTEMARK, BACKSLASH } from '@bbob/plugin-helper';\nexport class CharGrabber {\n skip(num = 1, silent) {\n this.pos += num;\n if (!silent && this.onSkip) {\n this.onSkip();\n }\n }\n hasNext() {\n return this.len > this.pos;\n }\n getCurr() {\n return this.pos < this.len ? this.s[this.pos] : '';\n }\n getPos() {\n return this.pos;\n }\n getLength() {\n return this.len;\n }\n getRest() {\n return this.s.substring(this.pos);\n }\n getNext() {\n const nextPos = this.pos + 1;\n return nextPos < this.len ? this.s[nextPos] : null;\n }\n getPrev() {\n const prevPos = this.pos - 1;\n return prevPos >= 0 ? this.s[prevPos] : null;\n }\n isLast() {\n return this.pos === this.len;\n }\n includes(val) {\n return this.s.indexOf(val, this.pos) >= 0;\n }\n grabWhile(condition, silent) {\n let start = 0;\n if (this.hasNext()) {\n start = this.pos;\n const onSkip = silent ? null : this.onSkip;\n while(this.pos < this.len && condition(this.s[this.pos])){\n this.pos++;\n if (onSkip) onSkip();\n }\n }\n return this.s.substring(start, this.pos);\n }\n grabN(num = 0) {\n return this.s.substring(this.pos, this.pos + num);\n }\n /**\n * Grabs rest of string until it find a char\n */ substrUntilChar(char) {\n const pos = this.pos;\n const idx = this.s.indexOf(char, pos);\n return idx >= 0 ? this.s.substring(pos, idx) : '';\n }\n constructor(source, options = {}){\n this.s = source;\n this.pos = 0;\n this.len = source.length;\n this.onSkip = options.onSkip || null;\n }\n}\n/**\n * Creates a grabber wrapper for source string, that helps to iterate over string char by char\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 */ export const trimChar = (str, charToRemove)=>{\n let start = 0;\n let end = str.length;\n while(start < end && str[start] === charToRemove)start++;\n while(end > start && str[end - 1] === charToRemove)end--;\n return start === 0 && end === str.length ? str : str.substring(start, end);\n};\n/**\n * Unquotes \\\" to \"\n */ export const unquote = (str)=>str.replace(BACKSLASH + QUOTEMARK, QUOTEMARK);\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.mjs';\nimport { createCharGrabber, trimChar, unquote } from './utils.mjs';\n// for cases \nconst EM = '!';\nexport function createTokenOfType(type, value, r = 0, cl = 0, p = 0, e = 0) {\n return new Token(type, value, r, cl, p, e);\n}\nconst STATE_WORD = 0;\nconst STATE_TAG = 1;\nconst STATE_TAG_ATTRS = 2;\nconst TAG_STATE_NAME = 0;\nconst TAG_STATE_ATTR = 1;\nconst TAG_STATE_VALUE = 2;\nconst END_POS_OFFSET = 2; // length + start position offset\nconst isWhiteSpace = (char)=>char === SPACE || char === TAB;\nconst isEscapeChar = (char)=>char === BACKSLASH;\nconst isSpecialChar = (char)=>char === EQ || char === SPACE || char === TAB;\nconst isNewLine = (char)=>char === N;\nconst unq = (val)=>unquote(trimChar(val, QUOTEMARK));\nexport function createLexer(buffer, options = {}) {\n let row = 0;\n let prevCol = 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 || []).filter(Boolean).map((tag)=>tag.toLowerCase());\n const caseFreeTags = options.caseFreeTags || false;\n const nestedMap = new Map();\n const onToken = options.onToken || (()=>{});\n const RESERVED_CHARS_SET = new Set([\n closeTag,\n openTag,\n QUOTEMARK,\n BACKSLASH,\n SPACE,\n TAB,\n EQ,\n N,\n EM\n ]);\n const isCharReserved = (char)=>RESERVED_CHARS_SET.has(char);\n const isCharToken = (char)=>char !== openTag && char !== SPACE && char !== TAB && char !== N;\n const isEscapableChar = (char)=>char === openTag || char === closeTag || char === BACKSLASH;\n const onSkip = ()=>{\n col++;\n };\n const setupContextFreeTag = (name, isClosingTag)=>{\n if (contextFreeTag !== '' && isClosingTag) {\n contextFreeTag = '';\n }\n const tagName = name.toLowerCase();\n if (contextFreeTag === '' && isTokenNested(name) && contextFreeTags.includes(tagName)) {\n contextFreeTag = tagName;\n }\n };\n const toEndTag = (tagName)=>`${openTag}${SLASH}${tagName}${closeTag}`;\n const chars = createCharGrabber(buffer, {\n onSkip\n });\n /**\n * Emits newly created token to subscriber\n *\n * @param {number} type - 1 - word, 2 - tag, 3 - attr-name, 4 - attr-value, 5 - space, 6 - new-line\n * @param {string} value - token value\n * @param {number} startPos - start position\n * @param {number} endPos - end position\n */ function emitToken(type, value, startPos, endPos) {\n const token = createTokenOfType(type, value, row, prevCol, startPos, endPos);\n onToken(token);\n prevCol = col;\n tokenIndex += 1;\n tokens[tokenIndex] = token;\n }\n function nextTagState(tagChars, isSingleValueTag, masterStartPos) {\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 = !!nextChar && 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;\n // return (isEQ || isWS) === false;\n }\n return true;\n };\n const name = tagChars.grabWhile(validAttrValue);\n tagChars.skip();\n emitToken(TYPE_ATTR_VALUE, unq(name));\n if (tagChars.getPrev() === QUOTEMARK) {\n prevCol++;\n }\n if (tagChars.isLast()) {\n return TAG_STATE_NAME;\n }\n return TAG_STATE_ATTR;\n }\n const start = masterStartPos + tagChars.getPos() - 1;\n const validName = (char)=>!(char === EQ || isWhiteSpace(char) || tagChars.isLast());\n const name = tagChars.grabWhile(validName);\n emitToken(TYPE_TAG, name, start, masterStartPos + tagChars.getLength() + 1);\n setupContextFreeTag(name);\n tagChars.skip();\n prevCol++;\n // in cases when we have [url=someval]GET[/url] and we don't 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 const isNextCharReserved = Boolean(nextChar && isCharReserved(nextChar));\n chars.skip(); // skip openTag\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 const isLastChar = chars.isLast();\n const hasSpace = substr.indexOf(SPACE) >= 0;\n const isSpaceRestricted = hasSpace && options.whitespaceInTags === false;\n if (isNextCharReserved || hasInvalidChars || isLastChar || isSpaceRestricted) {\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 // [url] or [/url]\n if (isNoAttrsInTag || isClosingTag) {\n const startPos = chars.getPos() - 1;\n const name = chars.grabWhile((char)=>char !== closeTag);\n const endPos = startPos + name.length + END_POS_OFFSET;\n chars.skip(); // skip closeTag\n emitToken(TYPE_TAG, name, startPos, endPos);\n setupContextFreeTag(name, isClosingTag);\n return STATE_WORD;\n }\n return STATE_TAG_ATTRS;\n }\n function stateAttrs() {\n const startPos = chars.getPos();\n const silent = true;\n const tagStr = chars.grabWhile((char)=>char !== closeTag, silent);\n const tagGrabber = createCharGrabber(tagStr, {\n onSkip\n });\n const eqParts = tagStr.split(EQ);\n const tagName = eqParts[0];\n const isEndTag = tagName[0] === SLASH;\n const isSingleAttrTag = tagName.indexOf(SPACE) === -1;\n const isSingleValueTag = !isEndTag && isSingleAttrTag;\n tagMode = TAG_STATE_NAME;\n while(tagGrabber.hasNext()){\n tagMode = nextTagState(tagGrabber, isSingleValueTag, startPos);\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 prevCol = 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 fullTagName = toEndTag(contextFreeTag);\n const foundTag = chars.grabN(fullTagName.length);\n const isContextFreeEnded = foundTag.toLowerCase() === fullTagName.toLowerCase();\n if (isContextFreeEnded) {\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 prevCol++;\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 (nextChar && 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 word = chars.grabWhile(isChar);\n emitToken(TYPE_WORD, word);\n return STATE_WORD;\n }\n const word = chars.grabWhile(isCharToken);\n emitToken(TYPE_WORD, word);\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 let lowercaseBuffer = null;\n function isTokenNested(tokenValue) {\n const value = toEndTag(tokenValue);\n if (nestedMap.has(value)) {\n return !!nestedMap.get(value);\n }\n let status;\n if (caseFreeTags) {\n if (!lowercaseBuffer) lowercaseBuffer = buffer.toLowerCase();\n status = lowercaseBuffer.indexOf(value.toLowerCase()) > -1;\n } else {\n status = buffer.indexOf(value) > -1;\n }\n nestedMap.set(value, status);\n return status;\n }\n return {\n tokenize,\n isTokenNested\n };\n}\n","class NodeList {\n last() {\n const len = this.n.length;\n if (len > 0) {\n return this.n[len - 1];\n }\n return undefined;\n }\n has() {\n return this.n.length > 0;\n }\n flush() {\n return this.n.length ? this.n.pop() : undefined;\n }\n push(value) {\n this.n.push(value);\n }\n ref() {\n return this.n;\n }\n constructor(){\n this.n = [];\n }\n}\nexport { NodeList };\nexport default NodeList;\n","import { CLOSE_BRAKET, isTagNode, OPEN_BRAKET, TagNode } from \"@bbob/plugin-helper\";\nimport { createLexer } from \"./lexer.mjs\";\nimport { TYPE_ATTR_NAME, TYPE_ATTR_VALUE, TYPE_NEW_LINE, TYPE_SPACE, TYPE_TAG, TYPE_WORD } from \"./Token.mjs\";\nimport { NodeList } from \"./NodeList.mjs\";\nconst createList = ()=>new NodeList();\nfunction 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 const caseFreeTags = options.caseFreeTags || false;\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 */ const nestedNodes = createList();\n /**\n * Temp buffer of nodes [tag..]...[/tag]\n * @private\n * @type {NodeList}\n */ let activeTagNode = null;\n /**\n * Temp buffer of tag attributes\n * @private\n * @type {NodeList}\n */ let activeTagNodesAttrName = null;\n /**\n * Cache for nested tags checks\n */ const nestedTagsMap = new Set();\n function getValue(tokenValue) {\n return caseFreeTags ? tokenValue.toLowerCase() : tokenValue;\n }\n function isTokenNested(token) {\n const tokenValue = token.getValue();\n const value = getValue(tokenValue);\n const { isTokenNested } = tokenizer || {};\n if (!nestedTagsMap.has(value) && typeof isTokenNested === \"function\") {\n if (isTokenNested(value)) {\n nestedTagsMap.add(value);\n return true;\n }\n }\n return nestedTagsMap.has(value);\n }\n /**\n * @private\n */ function isTagNested(tagName) {\n return Boolean(nestedTagsMap.has(getValue(tagName)));\n }\n /**\n * @private\n */ function isTagAllowed(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 */ function activeTagNodeFlush() {\n if (activeTagNode) {\n activeTagNode = null;\n activeTagNodesAttrName = null;\n }\n }\n /**\n * @private\n */ function getNodesContent() {\n const lastNestedNode = nestedNodes.last();\n if (lastNestedNode && isTagNode(lastNestedNode)) {\n return lastNestedNode.content;\n }\n return nodes.ref();\n }\n /**\n * @private\n */ function nodesAppendAsString(nodes, node, isNested = true) {\n if (Array.isArray(nodes) && typeof node !== \"undefined\") {\n nodes.push(node.toTagStart({\n openTag,\n closeTag\n }));\n if (Array.isArray(node.content) && node.content.length) {\n node.content.forEach((item)=>{\n nodes.push(item);\n });\n if (isNested) {\n nodes.push(node.toTagEnd({\n openTag,\n closeTag\n }));\n }\n }\n }\n }\n /**\n * @private\n */ function nodesAppend(node) {\n const nodes = getNodesContent();\n if (Array.isArray(nodes) && typeof node !== \"undefined\") {\n if (isTagNode(node)) {\n if (isTagAllowed(node.tag)) {\n nodes.push(node.toTagNode());\n } else {\n nodesAppendAsString(nodes, node);\n }\n } else {\n nodes.push(node);\n }\n }\n }\n /**\n * @private\n * @param {Token} token\n */ function tagHandleStart(token) {\n activeTagNodeFlush();\n const tagNode = TagNode.create(token.getValue(), {}, [], {\n from: token.getStart(),\n to: token.getEnd()\n });\n const isNested = isTokenNested(token);\n activeTagNode = tagNode;\n if (isNested) {\n nestedNodes.push(tagNode);\n } else {\n nodesAppend(tagNode);\n }\n }\n /**\n * @private\n * @param {Token} token\n */ function tagHandleEnd(token) {\n const tagName = token.getValue().slice(1);\n const lastNestedNode = nestedNodes.flush();\n activeTagNodeFlush();\n if (lastNestedNode) {\n if (isTagNode(lastNestedNode)) {\n lastNestedNode.setEnd({\n from: token.getStart(),\n to: token.getEnd()\n });\n }\n nodesAppend(lastNestedNode);\n } else if (!isTagNested(tagName)) {\n nodesAppend(token.toString({\n openTag,\n closeTag\n }));\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 tagName: tag,\n lineNumber: line,\n columnNumber: column\n });\n }\n }\n /**\n * @private\n * @param {Token} token\n */ function nodeHandle(token) {\n const tokenValue = token.getValue();\n const isNested = isTagNested(token.toString());\n if (activeTagNode) {\n switch(token.type){\n case TYPE_ATTR_NAME:\n activeTagNodesAttrName = tokenValue;\n if (tokenValue) {\n activeTagNode.attr(tokenValue, \"\");\n }\n break;\n case TYPE_ATTR_VALUE:\n if (activeTagNodesAttrName) {\n activeTagNode.attr(activeTagNodesAttrName, tokenValue);\n activeTagNodesAttrName = null;\n } else {\n activeTagNode.attr(tokenValue, tokenValue);\n }\n break;\n case TYPE_SPACE:\n case TYPE_NEW_LINE:\n case TYPE_WORD:\n if (isNested) {\n activeTagNode.append(tokenValue);\n } else {\n nodesAppend(tokenValue);\n }\n break;\n case TYPE_TAG:\n // if tag is not allowed, just pass it as is\n nodesAppend(token.toString({\n openTag,\n closeTag\n }));\n break;\n }\n } else if (token.isText()) {\n nodesAppend(tokenValue);\n } else if (token.isTag()) {\n // if tag is not allowed, just pass it as is\n nodesAppend(token.toString({\n openTag,\n closeTag\n }));\n }\n }\n /**\n * @private\n * @param {Token} token\n */ function onToken(token) {\n if (token.isTag()) {\n // [tag]\n if (token.isStart()) {\n tagHandleStart(token);\n }\n // [/tag]\n if (token.isEnd()) {\n tagHandleEnd(token);\n }\n } else {\n nodeHandle(token);\n }\n }\n const lexer = opts.createTokenizer ? opts.createTokenizer : createLexer;\n tokenizer = lexer(input, {\n onToken,\n openTag,\n closeTag,\n onlyAllowTags: options.onlyAllowTags,\n contextFreeTags: options.contextFreeTags,\n caseFreeTags: options.caseFreeTags,\n enableEscapeTags: options.enableEscapeTags,\n whitespaceInTags: options.whitespaceInTags\n });\n // eslint-disable-next-line no-unused-vars\n const tokens = tokenizer.tokenize();\n // handles situations where we opened tag, but forget to 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 do {\n const node = nestedNodes.flush();\n if (isTagNode(node) && isTagNested(node.tag)) {\n nodesAppendAsString(getNodesContent(), node, false);\n } else if (typeof node !== 'undefined') {\n nodesAppend(node);\n }\n }while (nestedNodes.has())\n return nodes.ref();\n}\nexport { parse };\nexport default parse;\n","/* eslint-disable no-plusplus */ const isObj = (value)=>typeof value === 'object' && value !== null;\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 (isObj(tree) && 'content' in tree) {\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 if (isObj(expected) && isObj(actual)) {\n return Object.keys(expected).every((key)=>{\n const ao = actual[key];\n const eo = expected[key];\n if (isObj(eo) && isObj(ao)) {\n return same(eo, ao);\n }\n if (isBool(eo)) {\n return eo !== (ao === null);\n }\n return ao === eo;\n });\n }\n return false;\n}\nexport function match(t, expression, cb) {\n if (Array.isArray(expression)) {\n return iterate(t, (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 });\n }\n return iterate(t, (node)=>same(expression, node) ? cb(node) : node);\n}\n","let errors = 'check https://github.com/jilizart/bbob/blob/master/ERRORS.md';\nlet C1 = `\"C1\" ${errors}`;\nlet C2 = `\"C2\" ${errors}`;\nexport { C1, C2 };\n","import { parse } from '@bbob/parser';\nimport { iterate, match } from './utils.mjs';\nimport { C1, C2 } from './errors.mjs';\nexport function createTree(tree, options) {\n const extendedTree = tree;\n extendedTree.messages = [\n ...extendedTree.messages || []\n ];\n extendedTree.options = {\n ...options,\n ...extendedTree.options\n };\n extendedTree.walk = function walkNodes(cb) {\n return iterate(this, cb);\n };\n extendedTree.match = function matchNodes(expr, cb) {\n return match(this, expr, cb);\n };\n return extendedTree;\n}\nexport default function bbob(plugs) {\n const plugins = typeof plugs === 'function' ? [\n plugs\n ] : plugs || [];\n const mockRender = ()=>\"\";\n return {\n process (input, opts) {\n const options = opts || {\n skipParse: false,\n parser: parse,\n render: mockRender,\n data: null\n };\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(C1);\n }\n // raw tree before modification with plugins\n const raw = options.skipParse && Array.isArray(input) ? input : parseFn(input, options);\n let tree = options.skipParse && Array.isArray(input) ? createTree(input || [], options) : createTree(raw, options);\n for(let idx = 0; idx < plugins.length; idx++){\n const plugin = plugins[idx];\n if (typeof plugin === 'function' && renderFn) {\n const newTree = plugin(tree, {\n parse: parseFn,\n render: renderFn,\n iterate,\n data\n });\n tree = createTree(newTree || tree, options);\n }\n }\n return {\n get html () {\n if (typeof renderFn !== 'function') {\n throw new Error(C2);\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, isTagNode } from '@bbob/plugin-helper';\nconst SELFCLOSE_END_TAG = '/>';\nconst CLOSE_START_TAG = '';\nfunction renderNode(node, options) {\n const { stripTags = false } = options || {};\n if (typeof node === 'undefined' || node === null) {\n return '';\n }\n if (typeof node === 'string' || typeof node === 'number') {\n return String(node);\n }\n if (Array.isArray(node)) {\n return render(node, options);\n }\n if (isTagNode(node)) {\n if (stripTags) {\n return render(node.content, options);\n }\n const attrs = attrsToString(node.attrs);\n if (node.content === null) {\n return START_TAG + node.tag + attrs + SELFCLOSE_END_TAG;\n }\n return START_TAG + node.tag + attrs + END_TAG + render(node.content, options) + CLOSE_START_TAG + node.tag + END_TAG;\n }\n return '';\n}\nexport function render(nodes, options) {\n if (nodes && Array.isArray(nodes)) {\n return nodes.reduce((r, node)=>r + renderNode(node, options), '');\n }\n if (nodes) {\n return renderNode(nodes, options);\n }\n return '';\n}\nexport function html(source, plugins, options) {\n return core(plugins).process(source, {\n ...options,\n render: render\n }).html;\n}\nexport default html;\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 return the default single attribute\n * or return a keyed attribute list\n * @param {import('@bbob/types').TagNode} node bbcode node to process\n * @param {string} [raw] raw string. Only include if the single attribute is allowed to have spaces\n * @returns processed attributes\n */\nconst preprocessAttr = (node, raw) => {\n const keys = Object.keys(node.attrs).join(\" \");\n const vals = Object.values(node.attrs).join(\" \");\n if (keys !== vals) {\n // [tag key=val]\n return node.attrs;\n }\n if (!raw || !node.start) {\n return {\n _default: vals,\n };\n }\n // [tag=attr]\n // node.start.from = 0\n // node.start.to = 10\n const nodeRaw = raw.substring(node.start.from, node.start.to);\n if (!nodeRaw.includes(\"=\")) {\n // [tag] or [tag attr]\n return node.attrs;\n }\n const openTagParts = nodeRaw.split(\"=\");\n if (openTagParts.length !== 2) {\n return node.attrs;\n }\n let val = openTagParts[1].slice(0, -1).trim(); // `attr` or `\"attr\"`\n if (val.startsWith('\"') && val.endsWith('\"')) {\n val = val.slice(1, -1);\n }\n return {\n _default: val,\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, raw) => {\n if (node.start) {\n return raw.substring(node.start.from, node.start.to);\n }\n if (!node.attrs) {\n return `[${node.tag}]`;\n }\n const attrs = preprocessAttr(node, raw);\n if (attrs._default) {\n return `[${node.tag}=${attrs._default}]`;\n } else {\n return node.toTagStart();\n }\n};\n\n/**\n * Attempts to return tag into its original form\n * @returns string of tag end\n */\nconst toOriginalEndTag = (node, raw) => {\n if (node.end) {\n return raw.substring(node.end.from, node.end.to);\n }\n return node.toTagEnd();\n};\n\n/**\n * Attempts to convert a node to a Markdown URL-embed-safe raw tag.\n *\n * Depending on the input node, the result is either:\n * - A single raw tag string if no end node is present\n * - Or an array of such strings if an end node is present\n * @returns {(string|string[])} - A Markdown URL-embed-safe raw tag or an array of tags.\n */\nconst toRawTag = (node, raw) => {\n if (node.end) {\n return [\n toOriginalStartTag(node, raw),\n ...node.content,\n toOriginalEndTag(node, raw),\n ];\n }\n return toOriginalStartTag(node, raw);\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(\n `^${URL_REGEX.source}|${MD_URL_REGEX.source}$`\n);\n// Both ends of an inline-code delimiter must be whole backtick runs. In\n// particular, never retry shorter openers from inside an unmatched long run.\nconst ESCAPABLES_REGEX =\n /((\\n|^)(?```+|~~~+)(?.*\\n))|(?\\[(?i?code|plain)(=[^\\]\\r\\n]*)?\\])|(?(?`+)(?!`)([\\s\\S]*?)(?(?)(?!`))/im;\nconst MD_TABLE_REGEX =\n /^(\\|[^\\n]+\\|\\r?\\n)((?:\\| ?:?[-]+:? ?)+\\|)(\\n(?:\\|[^\\n]+\\|\\r?\\n?)*)?$/m;\n\nconst MD_BROKEN_ORDERED_LIST = \"
\\n
    \";\nconst MD_BROKEN_UNORDERED_LIST = \"\\n
      \";\nconst MD_BROKEN_BLOCKQUOTE = \"\\n
      \";\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 toOriginalEndTag,\n toRawTag,\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 MD_BROKEN_ORDERED_LIST,\n MD_BROKEN_UNORDERED_LIST,\n MD_BROKEN_BLOCKQUOTE,\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 reduceWordsToLines(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 * Reduces the list into lines, so that we can process them by line.\n * Performs in place.\n * @param {(string|Object)[]} words\n */\nconst reduceWordsToLines = (words) => {\n let rightIdx = words.findLastIndex((w) => isString(w) && !isEOL(w)) + 1;\n\n for (let i = rightIdx - 1; i >= 0; i--) {\n if (isString(words[i]) && !isEOL(words[i])) {\n continue;\n }\n if (isEOL(words[i])) {\n if (i !== rightIdx - 1) {\n words.splice(i + 1, rightIdx - i - 1, words.slice(i + 1, rightIdx).join(\"\"));\n }\n rightIdx = i;\n continue;\n }\n if (isObj(words[i])) {\n if (i !== rightIdx - 1) {\n words.splice(i + 1, rightIdx - i - 1, words.slice(i + 1, rightIdx).join(\"\"));\n }\n rightIdx = i;\n }\n }\n\n if (0 !== rightIdx) {\n words.splice(0, rightIdx, words.slice(0, rightIdx).join(\"\"));\n }\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","/**\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 { isTagNode } from \"@bbob/plugin-helper\";\n\nconst CONSECUTIVE_NEWLINE_REGEX = /\\n{2,}/gm;\n\n/**\n * Removes empty lines from a string\n * @param {string} text\n */\nconst removeEmptyLines = (text) => {\n return text.replace(CONSECUTIVE_NEWLINE_REGEX, \"\\n\");\n};\n\n/**\n * Removes empty lines from attributes\n * @type {import('@bbob/types').BBobPluginFunction}\n */\nexport const removeEmptyLinePlugin = (tree) => {\n return tree.walk((node) => {\n if (isTagNode(node) && node.attrs) {\n Object.keys(node.attrs).forEach((key) => {\n if (typeof node.attrs[key] === \"string\") {\n node.attrs[key] = removeEmptyLines(node.attrs[key]);\n }\n });\n }\n return node;\n });\n};\n","import { isTagNode } from \"@bbob/plugin-helper\";\nexport function process(tags, tree, core, options) {\n return tree.walk((node)=>{\n if (isTagNode(node)) {\n const tag = node.tag;\n const tagCallback = tags[tag];\n if (typeof tagCallback === \"function\") {\n return tagCallback(node, core, options);\n }\n }\n return node;\n });\n}\n/**\n * Create a preset plugin for @bbob/core\n */ function createPreset(defTags, processor = process) {\n const presetFactory = (opts)=>{\n presetFactory.options = Object.assign(presetFactory.options || {}, opts);\n function presetExecutor(tree, core) {\n return processor(defTags, tree, core, presetFactory.options || {});\n }\n presetExecutor.options = presetFactory.options;\n return presetExecutor;\n };\n presetFactory.extend = function presetExtend(callback) {\n const newTags = callback(defTags, presetFactory.options);\n return createPreset(newTags, processor);\n };\n return presetFactory;\n}\nexport { createPreset };\nexport default createPreset;\n","import { isStringNode, isTagNode, TagNode } from \"@bbob/plugin-helper\";\nimport {\n generateGUID,\n preprocessAttr,\n regexIndexOf,\n toNode,\n toOriginalEndTag,\n toOriginalStartTag,\n} from \"../utils/common\";\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, options) => {\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 [\n toOriginalStartTag(node, options.data.raw),\n ...node.content,\n toOriginalEndTag(node, options.data.raw),\n ];\n }\n const attrs = preprocessAttr(node, options.data.raw);\n\n if (attrs._default) {\n /** @type {string[]} */\n const customSettings = attrs._default.split(\"|\").map((s) => s.trim());\n const lastValidAlignment = customSettings\n .filter((s) => [\"bright\", \"bcenter\", \"bleft\", \"fleft\", \"fright\"].includes(s))\n .pop();\n if (lastValidAlignment) {\n attrs.align ??= lastValidAlignment;\n }\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 = attrs.align?.toLowerCase() || \"\";\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, options) => {\n if (!node.isValid) {\n // not inside an [accordion] tag\n return [\n toOriginalStartTag(node, options.data.raw),\n ...node.content,\n toOriginalEndTag(node, options.data.raw),\n ];\n }\n const attrs = preprocessAttr(node, options.data.raw);\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 { 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 [a] 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, options) => {\n const attrs = preprocessAttr(node, options.data.raw)._default || \"\";\n return toNode(\n \"a\",\n { id: `user-anchor-${attrs.trim()}`, name: `user-anchor-${attrs.trim()}` },\n node.content,\n );\n },\n goto: (node, options) => {\n const attrs = preprocessAttr(node, options.data.raw)._default || \"\";\n return 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, options.data.raw);\n const fontFamily = attrs?._default || attrs?.family || attrs?.name;\n if (!fontFamily || 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)._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 { 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 = 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, options) => {\n const columnAttrs = preprocessAttr(node, options.data.raw)._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 } 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, options.data.raw);\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 { 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)._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 { isTagNode } from \"@bbob/plugin-helper\";\nimport { preprocessAttr, toNode, toRawTag } from \"../utils/common\";\n\n/**\n * @file Adds textmessage to bbcode\n * @example [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, options) => {\n const messageList = node.content.filter(\n (contentNode) => isTagNode(contentNode) && contentNode.tag === \"message\"\n );\n messageList.forEach((messageNode) => {\n messageNode.isValid = true;\n });\n\n if (!messageList.length) {\n return toRawTag(node, options.data.raw);\n }\n\n const attr = preprocessAttr(node, options.data.raw)._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, options) => {\n if (!node.isValid) {\n return toRawTag(node, options.data.raw);\n }\n\n let option = preprocessAttr(node, options?.data?.raw)?._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","import { createPreset } from \"@bbob/preset\";\nimport { accordionTags } from \"./tags/accordion\";\nimport { alignment } from \"./tags/alignment\";\nimport { anchor } from \"./tags/anchor\";\nimport { animation, keyframe } from \"./tags/animation\";\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 { classStyle } from \"./tags/class\";\nimport { code, icode, savenl } from \"./tags/code\";\nimport { color } from \"./tags/color\";\nimport { comment } from \"./tags/comment\";\nimport { bold, italic, strike, underline } from \"./tags/discourse-core-replacement\";\nimport { div } from \"./tags/div\";\nimport { divide } from \"./tags/divide\";\nimport { fieldset } from \"./tags/fieldset\";\nimport { font } from \"./tags/font\";\nimport { fa } from \"./tags/fontawesome\";\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 { br, nobr } from \"./tags/lineBreak\";\nimport { mail } from \"./tags/mail\";\nimport { newspaper } from \"./tags/newspaper\";\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 { quote } from \"./tags/quote\";\nimport { rowcolumn } from \"./tags/rowcolumn\";\nimport { script } from \"./tags/script\";\nimport { scroll } from \"./tags/scroll\";\nimport { side } from \"./tags/side\";\nimport { size } from \"./tags/size\";\nimport { inlinespoiler, spoiler } from \"./tags/spoiler\";\nimport { sub } from \"./tags/subscript\";\nimport { sup } from \"./tags/superscript\";\nimport { tab, tabs } from \"./tags/tabs\";\nimport { textmessage } from \"./tags/textmessage\";\nimport { thinprogress } from \"./tags/thinprogress\";\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 fa,\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 quote,\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\", \"fa\"];\n\nconst preset = createPreset(tags);\n\nexport { availableTags, tags, preset, preventParsing };\nexport default preset;\n","import { isStringNode, isTagNode } from \"@bbob/plugin-helper\";\nimport { preprocessAttr, toOriginalEndTag, toOriginalStartTag } from \"../utils/common\";\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, options.data.raw)?._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, options.data.raw)._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, options) => {\n if (!node.isValid) {\n return [\n toOriginalStartTag(node, options.data.raw),\n ...node.content,\n toOriginalEndTag(node, options.data.raw),\n ];\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, options) => {\n const color = preprocessAttr(node, options.data.raw)._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, options) => {\n const defaultOp = \"block\";\n const blockAttr = (preprocessAttr(node, options.data.raw)._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, options) => {\n const author = preprocessAttr(node, options.data.raw)._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, options) => {\n const val = preprocessAttr(node, options.data.raw)._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, options) => {\n const percentageInput = preprocessAttr(node, options.data.raw)._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, options) => {\n const attrs = preprocessAttr(node, options.data.raw)._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);\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)._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)._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, options.data.raw);\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)._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, options) => {\n const title = preprocessAttr(node, options.data.raw)._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 * Adds [fa] tag\n * [fa]fa-icon[/fa]\n * [fa style=\"\" fa-transform=\"\"]fa-solid fa-icon[/fa]\n * [fa primary-color=\"\" secondary-color=\"\" primary-opacity=\"\" secondary-opacity=\"\" rotate-angle=\"\"]fa-duotone fa-icon[/fa]\n */\nexport const fa = (node) => {\n const attrs = node.attrs;\n let style = attrs.style || \"\";\n style += attrs[\"primary-color\"] ? `--fa-primary-color: ${attrs[\"primary-color\"]};` : \"\";\n style += attrs[\"secondary-color\"] ? `--fa-secondary-color: ${attrs[\"secondary-color\"]};` : \"\";\n style += attrs[\"primary-opacity\"] ? `--fa-primary-opacity: ${attrs[\"primary-opacity\"]};` : \"\";\n style += attrs[\"secondary-opacity\"]\n ? `--fa-secondary-opacity: ${attrs[\"secondary-opacity\"]};`\n : \"\";\n style += attrs[\"rotate-angle\"] ? `--fa-rotate-angle: ${attrs[\"rotate-angle\"]};` : \"\";\n\n return toNode(\n \"i\",\n {\n \"data-bbcode-fa\": null,\n },\n [\n toNode(\n \"i\",\n {\n class: (node.content || []).join(\"\"),\n style,\n \"data-fa-transform\": attrs[\"fa-transform\"] || \"\",\n },\n [],\n ),\n ],\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)._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, options) => {\n const providedTitle = preprocessAttr(node, options.data.raw)._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)._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)._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 } from \"../utils/common\";\n\n/**\n * rebuild the [quote] tag so that markdown-it engine can parse it for itself\n */\nexport const quote = (node, options) => {\n const attrs = preprocessAttr(node, options.data.raw);\n if (node.content[0] === \"\\n\") {\n node.content.shift();\n }\n return [`\\n[${node.tag}=\"${attrs._default}\"]\\n\\n`, ...node.content, \"\\n\\n[/quote]\\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, options) => {\n const percentageInt = preprocessAttr(node, options.data.raw)._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, options) => {\n const attrs = preprocessAttr(node, options.data.raw)._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)._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","import { isTagNode } from \"@bbob/plugin-helper\";\nimport { generateGUID, preprocessAttr, toNode, toRawTag } from \"../utils/common\";\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, options) => {\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\n if (!tabsList.length) {\n return toRawTag(node, options.data.raw);\n }\n\n tabsList[0].open = true;\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, options) => {\n if (!node.isValid) {\n return toRawTag(node, options.data.raw);\n }\n\n const attrs = preprocessAttr(node, options.data.raw);\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","/**\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 if (node.gen) {\n // node is actually a generated node \"i\" made by another tag\n // don't process it\n return node;\n }\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 {\n MD_BROKEN_BLOCKQUOTE,\n MD_BROKEN_ORDERED_LIST,\n MD_BROKEN_UNORDERED_LIST,\n MD_NEWLINE_INJECT,\n MD_NEWLINE_INJECT_COMMENT,\n MD_NEWLINE_PRE_INJECT,\n} 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\nfunction cleanMultilineMDBlocks(raw) {\n const processed = raw\n .replaceAll(MD_BROKEN_ORDERED_LIST, \"\")\n .replaceAll(MD_BROKEN_UNORDERED_LIST, \"\")\n .replaceAll(MD_BROKEN_BLOCKQUOTE, \"\");\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 // These examples bypass Markdown's text renderer while hoisted. Escape\n // them when restoring them, so HTML examples stay text inside code/plain.\n const escaped = content\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\");\n raw = raw.replaceAll(uuid, escaped);\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 '\";\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 cleanMultilineMDBlocks,\n renderHoistedCodeBlocks,\n ];\n for (const postprocessor of postprocessors) {\n final = postprocessor(final, data);\n }\n return final;\n}\n","import { ESCAPABLES_REGEX, generateGUID, MD_TABLE_REGEX, 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 bbob from \"@bbob/core\";\nimport { render } from \"@bbob/html\";\nimport { lineBreakPlugin } from \"./plugins/lineBreak\";\nimport { preserveWhitespace } from \"./plugins/preserveWhitespace\";\nimport { removeEmptyLinePlugin } from \"./plugins/removeEmptyLinesInAttr\";\nimport { availableTags, preset, preventParsing } from \"./preset\";\nimport { postprocess } from \"./utils/postprocess\";\nimport { preprocessRaw } from \"./utils/preprocess\";\n\nconst options = {\n onlyAllowTags: [...availableTags],\n caseFreeTags: true,\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\n// BBob matches mixed-case tags but retains their spelling in the parsed tree.\n// Preset handlers, including the internal saveNL tag, are keyed by lowercase names.\nfunction normalizeTagNames(tree) {\n return tree.walk((node) => {\n if (node.tag) {\n node.tag = node.tag.toLowerCase();\n }\n return node;\n });\n}\n// Core handles its inline formatting unless a custom layout needs the BBCode tree.\nconst nativeInlineTags = new Set([\"b\", \"i\", \"u\", \"s\"]);\nconst customTagPattern = new RegExp(\n `\\\\[(?:${availableTags.filter((tag) => tag !== \"savenl\" && !nativeInlineTags.has(tag)).join(\"|\")})(?=[\\\\s=\\\\]])`,\n \"i\"\n);\n\nexport function containsBBCode(code) {\n // Hoist native fenced/inline code before deciding whether this post needs\n // legacy whitespace rules. A BBCode example is still ordinary Markdown.\n // Indented code can only start at the document start or after a blank line;\n // indentation continuing an ordinary paragraph is not a Markdown code block.\n // This is detection only: indenting nested tags in legacy layouts must not\n // turn those layouts into code when actual BBCode exists outside examples.\n const withoutIndentedCode = code\n .replace(/\\r\\n?/g, \"\\n\")\n .replace(\n /(^|\\n[ \\t]*\\n)(?:(?: {4}| {0,3}\\t)[^\\n]*(?:\\n|$)|[ \\t]*\\n)+/g,\n \"$1\"\n );\n const [preprocessed] = preprocessRaw(withoutIndentedCode);\n return customTagPattern.test(preprocessed);\n}\n\nexport const RpNBBCode = (code, opts) => {\n const plugins = [normalizeTagNames, presetTags];\n if (opts.preserveWhitespace) {\n plugins.push(preserveWhitespace());\n }\n plugins.push(lineBreakPlugin(), removeEmptyLinePlugin);\n const [preprocessed, preprocessedData] = preprocessRaw(code);\n return bbob(plugins).process(preprocessed, {\n render,\n ...options,\n data: {\n ...preprocessedData,\n raw: preprocessed,\n previewing: opts.previewing,\n fonts: new Set(),\n styles: [],\n bbscripts: [],\n },\n });\n};\n\nexport { availableTags, postprocess };\n"],"names":["N","TAB","EQ","QUOTEMARK","SPACE","OPEN_BRAKET","CLOSE_BRAKET","SLASH","BACKSLASH","isTagNode","el","isStringNode","isEOL","keysReduce","obj","reduce","def","Object","keys","acc","key","getNodeLength","node","Array","isArray","content","count","contentNode","String","length","CONTROL_CHARS_RE","escapeAttrValue","value","replace","attrValue","name","JSON","stringify","attrsToString","values","arr","join","getTagAttrs","tag","params","uniqAttr","res","tagAttr","attrs","toString","openTag","closeTag","TagNode","this","attr","append","push","appendToNode","setStart","start","setEnd","end","toTagStart","toTagEnd","toTagNode","r","nodeTreeToString","tagStart","toJSON","create","isOf","type","constructor","TYPE_ID","getTokenValue","token","isTagEnd","charCodeAt","Token","isEmpty","isNaN","isText","isTag","isAttrName","isAttrValue","isStart","isEnd","getName","slice","getTagName","getValue","getLine","getColumn","getStart","getEnd","text","tokenToText","row","col","CharGrabber","skip","num","silent","pos","onSkip","hasNext","len","getCurr","s","getPos","getLength","getRest","substring","getNext","nextPos","getPrev","prevPos","isLast","includes","val","indexOf","grabWhile","condition","grabN","substrUntilChar","char","idx","source","options","createCharGrabber","isWhiteSpace","isEscapeChar","unq","str","charToRemove","trimChar","createLexer","buffer","prevCol","tokenIndex","stateMode","tagMode","contextFreeTag","tokens","Math","floor","escapeTags","enableEscapeTags","contextFreeTags","filter","Boolean","map","toLowerCase","caseFreeTags","nestedMap","Map","onToken","RESERVED_CHARS_SET","Set","isCharToken","setupContextFreeTag","isClosingTag","tagName","isTokenNested","toEndTag","chars","emitToken","startPos","endPos","cl","p","e","createTokenOfType","nextTagState","tagChars","isSingleValueTag","masterStartPos","validAttrName","isValue","stateSpecial","validAttrValue","isQM","prevChar","nextChar","isPrevSLASH","isNextEQ","isWS","isNextWS","isSpecialChar","stateTag","currChar","isNextCharReserved","has","substr","hasInvalidChars","isLastChar","isSpaceRestricted","whitespaceInTags","isNoAttrsInTag","stateAttrs","tagStr","tagGrabber","split","isEndTag","isSingleAttrTag","stateWord","fullTagName","isEscapableChar","isChar","lowercaseBuffer","tokenValue","get","status","set","tokenize","NodeList","last","n","flush","pop","undefined","ref","createList","parse","input","opts","onlyAllowTags","tokenizer","nodes","nestedNodes","activeTagNode","activeTagNodesAttrName","nestedTagsMap","isTagNested","activeTagNodeFlush","getNodesContent","lastNestedNode","nodesAppendAsString","isNested","forEach","item","nodesAppend","tagHandleStart","tagNode","from","to","add","lexer","createTokenizer","onError","line","column","lineNumber","columnNumber","tagHandleEnd","nodeHandle","isObj","iterate","t","cb","tree","same","expected","actual","every","exp","some","call","act","ao","eo","errors","C1","C2","createTree","extendedTree","messages","walk","match","expr","expression","renderNode","stripTags","render","toNode","gen","preprocessAttr","raw","vals","_default","nodeRaw","openTagParts","trim","startsWith","endsWith","toOriginalStartTag","toOriginalEndTag","toRawTag","regexIndexOf","string","regex","startpos","search","MD_NEWLINE_INJECT","MD_NEWLINE_PRE_INJECT","MD_NEWLINE_INJECT_COMMENT","URL_REGEX_SINGLE_LINE","RegExp","ESCAPABLES_REGEX","MD_TABLE_REGEX","generateGUID","d","Date","getTime","window","performance","now","c","random","isString","disableLineBreakConversion","reduceWordsToLines","unshift","child","splice","isWhitespaceSensitive","test","words","rightIdx","findLastIndex","w","i","numSpaces","fromCharCode","repeat","CONSECUTIVE_NEWLINE_REGEX","removeEmptyLinePlugin","process","tags","core","tagCallback","SLIDE_TITLE_OPEN","Symbol","SLIDE_TITLE_CLOSE","SLIDE_CLOSE","SLIDE_REGEX","markerToString","marker","accordionTags","accordion","groupId","markedContent","contentArr","newArr","shift","foundIndex","preContent","postContent","groups","slideTitleOpen","slideTitleClose","slideClose","generateSlideMarkersFromContent","generatedSlides","currentSlide","prevMarker","customTitle","generateSlidesFromMarkers","filteredContent","isValid","data","customSettings","lastValidAlignment","align","width","find","classes","style","class","slide","title","isOpen","open","titleAlign","left","right","center","possibleOptions","alignment","anchor","a","id","goto","href","WEB_FONTS","VALID_FONT_STYLES","thin","extralight","light","regular","medium","semibold","bold","extrabold","black","REGISTERED_AXIS","AXES_REGEX","emailHeader","emailFooter","rowcolumn","columnAttrs","columnStyle","EVENTS","ACCEPTED_OPTIONS","textmessage","messageList","messageNode","recipient","message","option","animation","previewing","commonGUID","commonId","keyframes","ident","cleanContent","replaceAll","formatted","styles","bg","color","block","defaultOp","blockAttr","blockOption","blockquote","author","border","br","centerblock","percentageInput","check","nameAttr","classSuffix","className","selector","mediaQuery","state","minWidth","maxWidth","code","inputColor","comment","div","classAttrs","classNames","divide","fieldset","fa","font","fontFamily","family","axes","ital","wght","matches","exec","italic","weight","named_weight","fromEntries","entries","axesParser","url","sort","googleFontApiBuild","fonts","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","note","ooc","pindent","plain","print","printAttr","printOption","progress","percentageInt","quote","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","tabNode","b","u","availableTags","preset","createPreset","defTags","processor","presetFactory","presetExecutor","assign","extend","callback","removeNewlineInjects","cleanMultilineMDBlocks","renderHoistedCodeBlocks","hoistMap","uuid","escaped","createClassStyleTagTemplate","createScriptTagTemplate","fenceCodeBlockPreprocess","index","addHoistAndReturnNewStartPoint","cutOffStart","cutOffEnd","fence","fenceInfo","closingFenceRegex","nextIndex","replacement","bbcode","closingTag","bbcodeTag","backtick","tickStart","tickEnd","mdTableBlockPreprocess","table","preprocessRaw","preprocessors","preprocessor","err","console","warn","presetTags","normalizeTagNames","nativeInlineTags","customTagPattern","plugins","preserveWhitespace","preprocessed","preprocessedData","plugs","mockRender","skipParse","parser","parseFn","renderFn","Error","plugin","newTree","html","bbob","withoutIndentedCode","final","postprocessors","postprocessor"],"mappings":"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","x_google_ignoreList":[0,1,2,3,4,5,6,7,8,9,10,11,16]} \ No newline at end of file diff --git a/assets/javascripts/discourse/api-initializers/fontawesome.js b/assets/javascripts/discourse/api-initializers/fontawesome.js index 386b59d..e1295b8 100644 --- a/assets/javascripts/discourse/api-initializers/fontawesome.js +++ b/assets/javascripts/discourse/api-initializers/fontawesome.js @@ -5,7 +5,7 @@ const postsMissingFontAwesome = []; export default apiInitializer((api) => { const siteSettings = api.container.lookup("service:site-settings"); - if (!siteSettings.fontawesome_kit_url) { + if (!siteSettings.bbcode_enabled || !siteSettings.fontawesome_kit_url) { return; } window.FontAwesomeConfig = { diff --git a/assets/javascripts/discourse/api-initializers/google-font.js b/assets/javascripts/discourse/api-initializers/google-font.js index 5b56e78..5e8930b 100644 --- a/assets/javascripts/discourse/api-initializers/google-font.js +++ b/assets/javascripts/discourse/api-initializers/google-font.js @@ -10,7 +10,7 @@ import { apiInitializer } from "discourse/lib/api"; function addGoogleFont(post) { // Cleans up post if we're editing const priorLinks = post.querySelectorAll("link[data-rendered-gfont]"); - priorLinks.forEach((oldLink) => post.removeChild(oldLink)); + priorLinks.forEach((oldLink) => oldLink.remove()); const elements = post.querySelectorAll("[data-font]"); if (!elements.length) { @@ -22,7 +22,7 @@ function addGoogleFont(post) { const data = e.getAttribute("data-font"); if ( !gFonts.includes(data) && - data.startsWith("https://fonts.googleapis.com") + data.startsWith("https://fonts.googleapis.com/css2?") ) { frag.appendChild(linkBuilder(data)); gFonts.push(data); @@ -46,6 +46,10 @@ function linkBuilder(data) { } export default apiInitializer((api) => { + if (!api.container.lookup("service:site-settings").bbcode_enabled) { + return; + } + api.decorateCookedElement((elem) => addGoogleFont(elem), { id: "add google font", }); diff --git a/assets/javascripts/discourse/api-initializers/highlight.js b/assets/javascripts/discourse/api-initializers/highlight.js index fc78843..9fca867 100644 --- a/assets/javascripts/discourse/api-initializers/highlight.js +++ b/assets/javascripts/discourse/api-initializers/highlight.js @@ -340,6 +340,10 @@ function markdownHighlight(hljs) { } export default apiInitializer((api) => { + if (!api.container.lookup("service:site-settings").bbcode_enabled) { + return; + } + api.registerHighlightJSLanguage("bbcode", bbcodeHighlight); api.registerHighlightJSLanguage("markdown-bbcode", markdownHighlight); }); diff --git a/assets/javascripts/discourse/initializers/bbcode-parser-init.js b/assets/javascripts/discourse/initializers/bbcode-parser-init.js deleted file mode 100644 index f9a67ff..0000000 --- a/assets/javascripts/discourse/initializers/bbcode-parser-init.js +++ /dev/null @@ -1,15 +0,0 @@ -import loadscript from "discourse/lib/load-script"; - -export default { - name: "bbcode-parser-init", - initialize() { - if (window.bbcodeParser) { - return; - } else { - loadscript("/assets/bundled/bbcode-parser.min.js").then(() => { - // eslint-disable-next-line no-console - console.info("BBCode parser loaded for preview."); - }); - } - }, -}; diff --git a/assets/javascripts/lib/discourse-markdown/bbcode-plugin.js b/assets/javascripts/lib/discourse-markdown/bbcode-plugin.js index 7a3438e..8b2bd2c 100644 --- a/assets/javascripts/lib/discourse-markdown/bbcode-plugin.js +++ b/assets/javascripts/lib/discourse-markdown/bbcode-plugin.js @@ -1,119 +1,73 @@ -/** - * Processes inputted BBCode string using custom configured 3rd party library (see /bbcode-src) - * @param {string} raw content to preprocess into HTML - * @returns processed HTML string to pass into markdown-it - */ -function preprocessor(raw, opts, previewing = false) { - // eslint-disable-next-line no-undef - if (!bbcodeParser) { - // parser doesn't exist. Something horrible has happened and somehow the parser wasn't imported/initialized - // give up and send it straight back. - // eslint-disable-next-line no-console - console.warn( - "Attempted to get the bbcode parser: does not exist. Defaulting to standard markdown-it.", - "\ncalled on: \n", - raw - ); - return [raw, {}]; - } - const parser = globalThis.bbcodeParser.RpNBBCode; - opts.previewing = previewing; - - const processed = parser(raw, opts); - return [processed.html, processed.tree.options.data]; -} - -/** - * Processes the output of both the markdown-it and the bbcode parser, concatenating additional content if necessary - * @param {string} raw processed string - * @param {boolean} previewing flag - * @param {any} data from preprocessor - * @returns processed string - */ -function postprocessor(raw, previewing = false, data = {}) { - // eslint-disable-next-line no-undef - if (!bbcodeParser) { - // parser doesn't exist. Something horrible has happened and somehow the parser wasn't imported/initialized - // give up and send it straight back. - // eslint-disable-next-line no-console - console.warn( - "Attempted to get the bbcode parser: does not exist. Defaulting to standard markdown-it.", - "\ncalled on: \n", - raw - ); - return raw; - } - // preview auto clear doesn't check against the live dom, so if a onebox is at the end of the post, - // it won't be cleared and could cause a fatal error - const append = previewing ? '
      ' : ""; - return globalThis.bbcodeParser.postprocess(raw, data) + append; -} - +// Keep the BBCode parser inside Discourse's registered Markdown pipeline. +// Its output still passes through the normal sanitizer on both server and client. export function setup(helper) { - if (!helper.markdownIt) { - return; - } + helper.registerOptions((options, siteSettings) => { + options.features["bbcode-plugin"] = !!siteSettings.bbcode_enabled; + options.bbcodePreserveWhitespace = + siteSettings.preserve_whitespace && + !siteSettings.discourse_normalize_whitespace; + }); - helper.registerOptions((opts, siteSettings) => { - // Key must match this module's basename — that is the id the markdown - // pipeline gates registerPlugin and allowList on. - opts.features["bbcode-plugin"] = siteSettings.bbcode_enabled; - if (opts.engine || !siteSettings.bbcode_enabled) { - return; + helper.registerPlugin((md) => { + const parser = globalThis.bbcodeParser; + if (!parser) { + throw new Error("The registered BBCode parser asset is missing"); } - //Add check site settings for options to send to RpNBBCode - let preprocessor_options = { - preserveWhitespace: - siteSettings.preserve_whitespace && - !siteSettings.discourse_normalize_whitespace, - }; - Object.defineProperty(opts, "engine", { - configurable: true, - set(engine) { - const md = engine.render; - engine.set({ breaks: false }); // disable breaks. Let BBob handle line breaks. + const parsedTokens = new WeakMap(); - engine.render = function (raw) { - if (engine.options?.discourse?.featuresOverride !== undefined) { - // if featuresOverride is set, we're in a chat message and should not preprocess - return md.apply(this, [raw]); - } - const [preprocessed, data] = preprocessor( - raw, - preprocessor_options, - engine.options?.discourse?.previewing - ); - const processed = md.apply(this, [preprocessed]); - const postprocessed = postprocessor( - processed, - engine.options?.discourse?.previewing, - data - ); - return postprocessed; - }; - Object.defineProperty(opts, "engine", { - configurable: true, - enumerable: true, - writable: true, - value: engine, - }); - }, + md.core.ruler.before("normalize", "rpn-bbcode", (state) => { + // Plain Markdown keeps native paragraphs, whitespace and code blocks. + // Restricted cooks (including chat) do not enable this feature. + state.env.rpnBBCode = false; + if (!parser.containsBBCode(state.src)) { + return; + } + + const processed = parser.RpNBBCode(state.src, { + preserveWhitespace: md.options.discourse.bbcodePreserveWhitespace, + previewing: md.options.discourse.previewing, + }); + state.src = processed.html; + state.env.rpnBBCode = true; + parsedTokens.set(state.tokens, processed.tree.options.data); }); - }); - helper.registerPlugin((md) => { - // disable paragraph rendering - md.renderer.rules.paragraph_open = function () { - return ""; - }; - md.renderer.rules.paragraph_close = function () { - return ""; - }; + // Legacy layouts indent nested tags for readability. After conversion the + // generated HTML must not become an indented Markdown code block. Capture + // the native rule through Ruler's public API while registering the plugin, + // then keep it enabled for every ordinary Markdown cook. + const blockRules = md.block.ruler.getRules(""); + md.block.ruler.disable("code", true); + const withoutCode = new Set(md.block.ruler.getRules("")); + const nativeCode = blockRules.find((rule) => !withoutCode.has(rule)); + if (nativeCode) { + md.block.ruler.at("code", (state, ...args) => { + return !state.env.rpnBBCode && nativeCode(state, ...args); + }); + md.block.ruler.enable("code"); + } + + for (const name of ["paragraph_open", "paragraph_close", "softbreak"]) { + const renderRule = md.renderer.rules[name]; + md.renderer.rules[name] = (tokens, index, options, env, renderer) => { + if (env.rpnBBCode) { + return name === "softbreak" ? "\n" : ""; + } + return renderRule + ? renderRule(tokens, index, options, env, renderer) + : renderer.renderToken(tokens, index, options); + }; + } - // this rule is where an indent (space/indent) is converted to a code block - // rarely used in the wild, but it's a common source of confusion - md.disable("code"); + // A renderer extension preserves the parser's hoisted code, styles and + // scripts without intercepting Discourse's engine initialization. + const render = md.renderer.render; + md.renderer.render = function (tokens, options, env) { + const html = render.call(this, tokens, options, env); + const data = parsedTokens.get(tokens); + return data ? parser.postprocess(html, data) : html; + }; }); helper.allowList([ @@ -141,6 +95,7 @@ export function setup(helper) { "div.bb-email-subject", "div.bb-float-left", "div.bb-float-right", + "div.bb-fieldset", "div.bb-height-restrict", "div.bb-img", "div.bb-justify", @@ -157,6 +112,7 @@ export function setup(helper) { "div.bb-progress", "div.bb-progress-bar", "div.bb-progress-other", + "div.bb-progress-bar-other", "div.bb-progress-text", "div.bb-progress-thin", "div.bb-ooc", @@ -207,6 +163,14 @@ export function setup(helper) { return true; } + if (tag === "div" && name === "data-span") { + return /^column-width-span\d+$/.test(value); + } + + if (tag === "span" && name === "data-font") { + return value.startsWith("https://fonts.googleapis.com/css2?"); + } + // custom attr allowlist for tabs if (tag === "input" && name === "type" && value === "radio") { return true; diff --git a/bbcode-src/index.js b/bbcode-src/index.js index 69a47b0..49671dc 100644 --- a/bbcode-src/index.js +++ b/bbcode-src/index.js @@ -21,8 +21,42 @@ const options = { }; const presetTags = preset(); +// BBob matches mixed-case tags but retains their spelling in the parsed tree. +// Preset handlers, including the internal saveNL tag, are keyed by lowercase names. +function normalizeTagNames(tree) { + return tree.walk((node) => { + if (node.tag) { + node.tag = node.tag.toLowerCase(); + } + return node; + }); +} +// Core handles its inline formatting unless a custom layout needs the BBCode tree. +const nativeInlineTags = new Set(["b", "i", "u", "s"]); +const customTagPattern = new RegExp( + `\\[(?:${availableTags.filter((tag) => tag !== "savenl" && !nativeInlineTags.has(tag)).join("|")})(?=[\\s=\\]])`, + "i" +); + +export function containsBBCode(code) { + // Hoist native fenced/inline code before deciding whether this post needs + // legacy whitespace rules. A BBCode example is still ordinary Markdown. + // Indented code can only start at the document start or after a blank line; + // indentation continuing an ordinary paragraph is not a Markdown code block. + // This is detection only: indenting nested tags in legacy layouts must not + // turn those layouts into code when actual BBCode exists outside examples. + const withoutIndentedCode = code + .replace(/\r\n?/g, "\n") + .replace( + /(^|\n[ \t]*\n)(?:(?: {4}| {0,3}\t)[^\n]*(?:\n|$)|[ \t]*\n)+/g, + "$1" + ); + const [preprocessed] = preprocessRaw(withoutIndentedCode); + return customTagPattern.test(preprocessed); +} + export const RpNBBCode = (code, opts) => { - const plugins = [presetTags]; + const plugins = [normalizeTagNames, presetTags]; if (opts.preserveWhitespace) { plugins.push(preserveWhitespace()); } @@ -42,4 +76,4 @@ export const RpNBBCode = (code, opts) => { }); }; -export { postprocess }; +export { availableTags, postprocess }; diff --git a/bbcode-src/utils/common.js b/bbcode-src/utils/common.js index 8ca8adc..60ab4cb 100644 --- a/bbcode-src/utils/common.js +++ b/bbcode-src/utils/common.js @@ -106,7 +106,11 @@ const toOriginalEndTag = (node, raw) => { */ const toRawTag = (node, raw) => { if (node.end) { - return [toOriginalStartTag(node, raw), ...node.content, toOriginalEndTag(node, raw)]; + return [ + toOriginalStartTag(node, raw), + ...node.content, + toOriginalEndTag(node, raw), + ]; } return toOriginalStartTag(node, raw); }; @@ -131,10 +135,15 @@ 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 URL_REGEX_SINGLE_LINE = new RegExp( + `^${URL_REGEX.source}|${MD_URL_REGEX.source}$` +); +// Both ends of an inline-code delimiter must be whole backtick runs. In +// particular, never retry shorter openers from inside an unmatched long run. const ESCAPABLES_REGEX = - /((\n|^)(?```+|~~~+)(?.*\n))|(?\[(?i?code|plain)(=.*)?\])|(?(?`{1,2})(.*)(?\k))/im; -const MD_TABLE_REGEX = /^(\|[^\n]+\|\r?\n)((?:\| ?:?[-]+:? ?)+\|)(\n(?:\|[^\n]+\|\r?\n?)*)?$/m; + /((\n|^)(?```+|~~~+)(?.*\n))|(?\[(?i?code|plain)(=[^\]\r\n]*)?\])|(?(?`+)(?!`)([\s\S]*?)(?(?)(?!`))/im; +const MD_TABLE_REGEX = + /^(\|[^\n]+\|\r?\n)((?:\| ?:?[-]+:? ?)+\|)(\n(?:\|[^\n]+\|\r?\n?)*)?$/m; const MD_BROKEN_ORDERED_LIST = "
\n
    "; const MD_BROKEN_UNORDERED_LIST = "\n
      "; @@ -153,7 +162,7 @@ function generateGUID() { } 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; + 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); diff --git a/bbcode-src/utils/postprocess.js b/bbcode-src/utils/postprocess.js index 6270578..6b229f7 100644 --- a/bbcode-src/utils/postprocess.js +++ b/bbcode-src/utils/postprocess.js @@ -41,7 +41,13 @@ function cleanMultilineMDBlocks(raw) { function renderHoistedCodeBlocks(raw, data) { const hoistMap = data.hoistMap; for (const [uuid, content] of Object.entries(hoistMap)) { - raw = raw.replaceAll(uuid, content); + // These examples bypass Markdown's text renderer while hoisted. Escape + // them when restoring them, so HTML examples stay text inside code/plain. + const escaped = content + .replaceAll("&", "&") + .replaceAll("<", "<") + .replaceAll(">", ">"); + raw = raw.replaceAll(uuid, escaped); } return raw; } @@ -56,7 +62,10 @@ function createClassStyleTagTemplate(raw, data) { if (data.styles.length === 0) { return raw; } - const template = '"; + const template = + '"; return template + raw; } diff --git a/package.json b/package.json index b339791..48e8c76 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "dependencies": { "@bbob/core": "^4.2.0", "@bbob/html": "^4.2.0", + "@bbob/plugin-helper": "^4.4.1", "@bbob/preset": "^4.2.0", "@bbob/preset-html5": "^4.2.0" }, diff --git a/plugin.rb b/plugin.rb index bbf9f4e..1ace188 100644 --- a/plugin.rb +++ b/plugin.rb @@ -49,15 +49,10 @@ module ::BbCode # when false, it does nothing, which allows for persistence of non-default whitespace. class ::TextCleaner # rubocop:disable Discourse/Plugins/NoMonkeyPatching module Optional_normalize_whitespace - def title_options - options = super - options[:normalize_whitespace_opt] = SiteSetting.discourse_normalize_whitespace - options - end def normalize_whitespaces(text) - options = title_options - text = super(text) if (options[:normalize_whitespace_opt]) - text + return text if SiteSetting.bbcode_enabled && !SiteSetting.discourse_normalize_whitespace + + super end end singleton_class.prepend Optional_normalize_whitespace diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a1b1bb3..7b2b1c5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,9 @@ importers: '@bbob/html': specifier: ^4.2.0 version: 4.4.1 + '@bbob/plugin-helper': + specifier: ^4.4.1 + version: 4.4.1 '@bbob/preset': specifier: ^4.2.0 version: 4.4.1 diff --git a/spec/lib/pretty_text_spec.rb b/spec/lib/pretty_text_spec.rb new file mode 100644 index 0000000..40e881d --- /dev/null +++ b/spec/lib/pretty_text_spec.rb @@ -0,0 +1,457 @@ +# frozen_string_literal: true + +RSpec.describe PrettyText do + before { SiteSetting.bbcode_enabled = true } + + def bbcode_fragment(raw, **options) + Nokogiri::HTML5.fragment(PrettyText.cook(raw, options)) + end + + it "cooks nested XenForo spoilers as independent, initially closed details" do + fragment = + bbcode_fragment( + "[spoiler=Outer]Before [spoiler=Inner][b]Hidden text[/b][/spoiler] After[/spoiler]", + ) + + outer = fragment.at_css("details.bb-spoiler") + inner = outer.at_css(".bb-spoiler-content details.bb-spoiler") + + expect(fragment.css("details.bb-spoiler").size).to eq(2) + expect(outer.at_css("summary").text).to eq("Spoiler: Outer") + expect(inner.at_css("summary").text).to eq("Spoiler: Inner") + expect(inner.at_css(".bb-spoiler-content .bbcode-b").text).to eq("Hidden text") + expect(outer.key?("open")).to eq(false) + expect(inner.key?("open")).to eq(false) + expect(outer.at_css(".bb-spoiler-content").text).to include("Before", "After") + end + + it "accepts mixed-case legacy spoiler tags" do + fragment = bbcode_fragment("[SPOILER=Example]Hidden [I]text[/I][/SPOILER]") + + expect(fragment.at_css("details.bb-spoiler > summary").text).to eq("Spoiler: Example") + expect(fragment.at_css(".bb-spoiler-content .bbcode-i").text).to eq("text") + end + + it "keeps formatted children inside an inline spoiler" do + fragment = bbcode_fragment("Before [inlinespoiler][b]Hidden[/b][/inlinespoiler] after") + + expect(fragment.at_css("span.bb-inline-spoiler .bbcode-b").text).to eq("Hidden") + expect(fragment.text).to include("Before", "after") + end + + it "preserves literal guide examples in BBCode and Markdown code blocks" do + example = "[spoiler=Example][b]Do not render this example[/b][/spoiler]" + fragment = bbcode_fragment("[code]#{example}[/code]\n\n```bbcode\n#{example}\n```") + + expect(fragment.css("pre code").size).to eq(2) + expect(fragment.css("pre code").map(&:text).map(&:strip)).to eq([example, example]) + expect(fragment.css("details, .bbcode-b")).to be_empty + end + + it "preserves inline code and plain-tag examples used by the guide" do + fragment = + bbcode_fragment( + "`[spoiler=Example]Hidden[/spoiler]` [plain][b]literal[/b][/plain] [b]rendered[/b]", + ) + + expect(fragment.at_css("code").text).to eq("[spoiler=Example]Hidden[/spoiler]") + expect(fragment.text).to include("[b]literal[/b]") + expect(fragment.css(".bbcode-b").map(&:text)).to eq(["rendered"]) + expect(fragment.css("details")).to be_empty + end + + it "does not reinterpret part of an unmatched backtick run as a shorter delimiter" do + fragment = bbcode_fragment("[b]Outside[/b] prefix ````example``` suffix") + + expect(fragment.css("code")).to be_empty + expect(fragment.text).to include("prefix ````example``` suffix") + expect(fragment.at_css(".bbcode-b").text).to eq("Outside") + end + + it "keeps exact-length backtick delimiters and multiline inline examples" do + fragment = + bbcode_fragment("[b]Outside[/b] prefix ``one ` two`` suffix `alpha\n[b]literal[/b]\nbeta`") + + expect(fragment.css("code").size).to eq(2) + expect(fragment.css("code").first.text).to eq("one ` two") + expect(fragment.css("code").last.text).to include("alpha", "[b]literal[/b]", "beta") + expect(fragment.css(".bbcode-b").map(&:text)).to eq(["Outside"]) + end + + it "shows HTML examples as text inside BBCode and Markdown code blocks" do + example = '
      A & B
      ' + fragment = + bbcode_fragment("[code=html]#{example}[/code]\n\n```html\n#{example}\n```\n\n`#{example}`") + + expect(fragment.css("code").map(&:text).map(&:strip)).to eq([example, example, example]) + expect(fragment.css("code div, .example")).to be_empty + end + + it "retains Markdown paragraphs when BBCode appears only inside a fenced example" do + example = "[spoiler=Example]Hidden text[/spoiler]" + fragment = + bbcode_fragment("First **paragraph**.\n\n```bbcode\n#{example}\n```\n\nSecond paragraph.") + + expect(fragment.css("p").map(&:text)).to eq(["First paragraph.", "Second paragraph."]) + expect(fragment.at_css("strong").text).to eq("paragraph") + expect(fragment.at_css("pre code").text.strip).to eq(example) + expect(fragment.css("details")).to be_empty + end + + it "retains Markdown paragraphs when BBCode appears only inside inline code" do + example = "[div]A layout example[/div]" + fragment = bbcode_fragment("First `#{example}` paragraph.\n\nSecond **paragraph**.") + + expect(fragment.css("p").map(&:text)).to eq( + ["First #{example} paragraph.", "Second paragraph."], + ) + expect(fragment.at_css("code").text).to eq(example) + expect(fragment.at_css("strong").text).to eq("paragraph") + expect(fragment.css("div")).to be_empty + end + + it "retains native indented code containing BBCode examples" do + fragment = bbcode_fragment(<<~MARKDOWN) + First **paragraph**. + + [b]Literal bold example[/b] + [spoiler=Example]Hidden text[/spoiler] + + Second paragraph. + MARKDOWN + + expect(fragment.css("p").map(&:text)).to eq(["First paragraph.", "Second paragraph."]) + expect(fragment.at_css("strong").text).to eq("paragraph") + expect(fragment.at_css("pre code").text.strip).to eq( + "[b]Literal bold example[/b]\n[spoiler=Example]Hidden text[/spoiler]", + ) + expect(fragment.css(".bbcode-b, details")).to be_empty + end + + it "retains native tab-indented code containing a BBCode example" do + example = "[spoiler=Example]Hidden text[/spoiler]" + fragment = bbcode_fragment("First paragraph.\n\n\t#{example}\n\nSecond paragraph.") + + expect(fragment.css("p").map(&:text)).to eq(["First paragraph.", "Second paragraph."]) + expect(fragment.at_css("pre code").text.strip).to eq(example) + expect(fragment.css("details")).to be_empty + end + + it "preserves historical curly-brace accordion slides and their nested content" do + fragment = bbcode_fragment(<<~BBCODE) + [accordion] + {slide=First|open}[spoiler=Secret][b]First body[/b][/spoiler]{/slide} + {slide=Second}Second body{/slide} + [/accordion] + BBCODE + slides = fragment.css(".bb-accordion > details.bb-slide") + + expect(slides.size).to eq(2) + expect(slides.map { |slide| slide.at_css("summary").text }).to eq(%w[First Second]) + expect(slides.first.key?("open")).to eq(true) + expect(slides.last.key?("open")).to eq(false) + expect(slides.first.at_css(".bb-slide-content details.bb-spoiler .bbcode-b").text).to eq( + "First body", + ) + expect(slides.last.at_css(".bb-slide-content").text).to eq("Second body") + end + + it "gives separate tab sets independent controls and matching labels" do + fragment = + bbcode_fragment( + "[tabs][tab=First]One[/tab][tab=Second]Two[/tab][/tabs]\n" \ + "[tabs][tab=First]Three[/tab][tab=Second]Four[/tab][/tabs]", + ) + groups = fragment.css(".bb-tabs") + inputs = fragment.css("input.bb-tab") + + expect(groups.size).to eq(2) + expect(inputs.size).to eq(4) + expect(inputs.map { |input| input["id"] }.uniq.size).to eq(4) + expect(inputs.map { |input| input["name"] }.uniq.size).to eq(2) + expect(fragment.css("label.bb-tab-label").map { |label| label["for"] }).to eq( + inputs.map { |input| input["id"] }, + ) + groups.each do |group| + expect(group.css("input[checked]").size).to eq(1) + expect(group.css(".bb-tab-content").size).to eq(2) + end + end + + it "preserves nested column layout and fieldset content through sanitization" do + fragment = + bbcode_fragment( + "[row][column=4][fieldset=Character][b]Name[/b][/fieldset][/column]" \ + "[column=8][spoiler=History]Story[/spoiler][/column][/row]", + ) + columns = fragment.css(".bb-row > .bb-column") + + expect(columns.size).to eq(2) + expect(columns.map { |column| column["data-span"] }).to eq( + %w[column-width-span4 column-width-span8], + ) + expect(columns.first.at_css("fieldset.bb-fieldset legend").text).to eq("Character") + expect(columns.first.at_css("fieldset.bb-fieldset .bbcode-b").text).to eq("Name") + expect(columns.last.at_css("details.bb-spoiler .bb-spoiler-content").text).to eq("Story") + end + + it "retains native paragraphs, emphasis, lists and indented code for Markdown posts" do + fragment = bbcode_fragment(<<~MARKDOWN) + First paragraph with **bold**. + + Second paragraph. + + - One + - Two + + standalone code + MARKDOWN + + expect(fragment.css("p").map(&:text)).to include( + "First paragraph with bold.", + "Second paragraph.", + ) + expect(fragment.at_css("strong").text).to eq("bold") + expect(fragment.css("ul > li").size).to eq(2) + + code = bbcode_fragment("Paragraph.\n\n standalone code") + expect(code.at_css("pre code").text.strip).to eq("standalone code") + end + + it "retains native Markdown tables" do + fragment = bbcode_fragment("| Name | Role |\n| --- | --- |\n| Alice | Writer |") + + expect(fragment.css("table th").map(&:text)).to eq(%w[Name Role]) + expect(fragment.css("table td").map(&:text)).to eq(%w[Alice Writer]) + end + + it "uses native inline formatting without changing Markdown paragraphs" do + fragment = + bbcode_fragment("First [b]bold[/b] [i]italic[/i].\n\nSecond [u]underlined[/u] [s]struck[/s].") + + expect(fragment.css("p").map(&:text)).to eq(["First bold italic.", "Second underlined struck."]) + expect(fragment.at_css("span.bbcode-b").text).to eq("bold") + expect(fragment.at_css("span.bbcode-i").text).to eq("italic") + expect(fragment.at_css("span.bbcode-u").text).to eq("underlined") + expect(fragment.at_css("span.bbcode-s").text).to eq("struck") + end + + it "retains native Markdown lists and tables containing core inline BBCode" do + fragment = bbcode_fragment(<<~MARKDOWN) + - [b]First[/b] + - [i]Second[/i] + + | Name | Role | + | --- | --- | + | [u]Alice[/u] | [s]Writer[/s] | + MARKDOWN + + expect(fragment.css("ul > li").map(&:text)).to eq(%w[First Second]) + expect(fragment.at_css("li .bbcode-b").text).to eq("First") + expect(fragment.css("table th").map(&:text)).to eq(%w[Name Role]) + expect(fragment.at_css("table td .bbcode-u").text).to eq("Alice") + end + + it "retains native indented code alongside core inline BBCode" do + fragment = bbcode_fragment("[b]Formatted paragraph[/b]\n\n [i]Literal example[/i]") + + expect(fragment.at_css("p .bbcode-b").text).to eq("Formatted paragraph") + expect(fragment.at_css("pre code").text.strip).to eq("[i]Literal example[/i]") + expect(fragment.css(".bbcode-i")).to be_empty + end + + it "keeps native Markdown headings inside authored div and nobr wrappers" do + fragment = + bbcode_fragment("[div]\n\n# First heading\n\n[/div]\n[nobr]\n\n## Second heading\n\n[/nobr]") + + expect(fragment.at_css("div h1").text).to eq("First heading") + expect(fragment.at_css("h2").text).to eq("Second heading") + end + + it "lets authors escape literal hash labels without disabling headings" do + fragment = bbcode_fragment("[div]\n\n\\# character sheet\n\n# Heading\n\n[/div]") + + expect(fragment.at_css("div").text).to include("# character sheet") + expect(fragment.css("h1").map(&:text)).to eq(["Heading"]) + end + + it "renders allowed raw HTML and leaves comments hidden beside custom BBCode" do + fragment = bbcode_fragment(<<~MARKUP) + [center]Custom content[/center] + +
      Open detailsVisible body
      + + + After the details. + MARKUP + + expect(fragment.at_css("details > summary").text).to eq("Open details") + expect(fragment.at_css("details").text).to include("Visible body") + expect(fragment.text).not_to include("
      ", "Hidden source note") + expect(fragment.at_css(".bb-center").text).to eq("Custom content") + end + + it "shows HTML and comments literally in plain and code examples" do + example = "
      ExampleBody
      " + fragment = bbcode_fragment("[plain]#{example}[/plain]\n[code=html]#{example}[/code]") + + expect(fragment.css("details, summary")).to be_empty + expect(fragment.text).to include(example) + expect(fragment.at_css("pre code").text.strip).to eq(example) + end + + it "leaves unsupported XenForo list and table tags literal" do + example = "[list][*]First[/list]\n[table][tr][td]Cell[/td][/tr][/table]" + fragment = bbcode_fragment(example) + + expect(fragment.text).to include( + "[list][*]First[/list]", + "[table][tr][td]Cell[/td][/tr][/table]", + ) + expect(fragment.css("ul, ol, table")).to be_empty + end + + it "renders indented legacy div layouts inside nobr without exposing generated HTML" do + fragment = bbcode_fragment(<<~BBCODE) + [div class=outer][nobr] + [div class=card] + [div class=inside][b]Character name[/b][/div] + [/div] + [/nobr][/div] + BBCODE + + expect(fragment.at_css("div div div .bbcode-b").text).to eq("Character name") + expect(fragment.css("pre, code")).to be_empty + expect(fragment.text).not_to include("", "[div", "[/div]") + end + + it "leaves native cooking in control when the plugin is disabled" do + SiteSetting.bbcode_enabled = false + fragment = bbcode_fragment("First **paragraph**.\n\n[center]Legacy text[/center]") + + expect(fragment.at_css("strong").text).to eq("paragraph") + expect(fragment.css("p").size).to eq(2) + expect(fragment.text).to include("[center]Legacy text[/center]") + expect(fragment.css(".bb-center")).to be_empty + end + + it "honors an explicit feature override used by chat cooking" do + fragment = + bbcode_fragment( + "**Chat text**\n\n[center]Legacy text[/center]", + features_override: %w[bold italics code paragraph], + ) + + expect(fragment.text).to include("[center]Legacy text[/center]") + expect(fragment.css(".bb-center")).to be_empty + end + + it "keeps cooked custom content inside Discourse's sanitizer" do + fragment = + bbcode_fragment( + '[spoiler=Example]' \ + "[b]Safe text[/b][/spoiler]", + ) + + expect(fragment.at_css("details.bb-spoiler .bbcode-b").text).to eq("Safe text") + expect(fragment.css("script, [onerror]")).to be_empty + end + + it "uses one class namespace across separate custom blocks in a post" do + fragment = bbcode_fragment(<<~BBCODE) + [class=card]color: red;[/class] + + A **Markdown** paragraph between the declaration and its elements. + + [div class=card]First card[/div] + + Another paragraph. + + [div class=card]Second card[/div] + BBCODE + cards = fragment.css("div[class^='card__post-']") + styles = fragment.css('template[data-bbcode-plus="class"]') + + expect(cards.map(&:text)).to eq(["First card", "Second card"]) + expect(cards.map { |card| card["class"] }.uniq.size).to eq(1) + expect(styles.size).to eq(1) + expect(styles.first.text).to include(".#{cards.first["class"]}", "color: red;") + expect(fragment.at_css("strong").text).to eq("Markdown") + end + + it "keeps class styles local to each cook" do + first = bbcode_fragment("[class=card]color: red;[/class]\n[div class=card]First[/div]") + second = bbcode_fragment("[class=card]color: blue;[/class]\n[div class=card]Second[/div]") + first_card = first.at_css("div[class^='card__post-']") + second_card = second.at_css("div[class^='card__post-']") + first_styles = first.at_css('template[data-bbcode-plus="class"]').text + second_styles = second.at_css('template[data-bbcode-plus="class"]').text + + expect(first_card["class"]).not_to eq(second_card["class"]) + expect(first_styles).to include(".#{first_card["class"]}", "color: red;") + expect(second_styles).to include(".#{second_card["class"]}", "color: blue;") + expect(second_styles).not_to include(first_card["class"]) + expect(bbcode_fragment("Ordinary Markdown").css("template")).to be_empty + end + + it "preserves image references in both class declarations and inline styles" do + remote_url = "https://example.test/card.png" + upload_url = "upload://example-background.png" + fragment = bbcode_fragment(<<~BBCODE) + [class=card]background-image: url(#{remote_url});[/class] + + [div class=card style="background-image: url(#{upload_url})"]Card[/div] + BBCODE + + expect(fragment.at_css('template[data-bbcode-plus="class"]').text).to include(remote_url) + expect(fragment.at_css("div[class^='card__post-']")["style"]).to include(upload_url) + candidates = HotlinkedMedia.extract_candidates(fragment) + expect(candidates.map { |candidate| HotlinkedMedia.download_src_for(candidate) }).to eq( + [remote_url], + ) + end + + it "retains the font and layout attributes consumed by client decorators" do + fragment = bbcode_fragment(<<~BBCODE) + [font=Roboto]Custom font[/font] + + [fieldset=Details]Contents[/fieldset] + + [progress=50]Half complete[/progress] + BBCODE + + expect(fragment.at_css("span[data-font]")["data-font"]).to start_with( + "https://fonts.googleapis.com/css2?", + ) + expect(fragment.at_css("fieldset.bb-fieldset > div.bb-fieldset").text).to eq("Contents") + expect(fragment.at_css(".bb-progress > .bb-progress-bar-other")).to be_present + end + + it "applies feature settings independently on consecutive cooks" do + raw = "[center]Custom content[/center]\n\nA **Markdown** paragraph." + enabled = bbcode_fragment(raw) + SiteSetting.bbcode_enabled = false + disabled = bbcode_fragment(raw) + SiteSetting.bbcode_enabled = true + reenabled = bbcode_fragment(raw) + + expect(enabled.at_css(".bb-center").text).to eq("Custom content") + expect(disabled.css(".bb-center")).to be_empty + expect(disabled.text).to include("[center]Custom content[/center]") + expect(disabled.at_css("strong").text).to eq("Markdown") + expect(reenabled.at_css(".bb-center").text).to eq("Custom content") + end + + it "preserves custom cooking after the PrettyText engine is reset" do + raw = "[spoiler=Example][b]Hidden text[/b][/spoiler]" + first = bbcode_fragment(raw) + PrettyText.reset_context + second = bbcode_fragment(raw) + + expect(second.to_html).to eq(first.to_html) + expect(second.at_css("details.bb-spoiler .bbcode-b").text).to eq("Hidden text") + expect(bbcode_fragment("First paragraph.\n\nSecond paragraph.").css("p").map(&:text)).to eq( + ["First paragraph.", "Second paragraph."], + ) + end +end diff --git a/test/javascripts/lib/bbcode-cooked-test.js b/test/javascripts/lib/bbcode-cooked-test.js new file mode 100644 index 0000000..ef17a39 --- /dev/null +++ b/test/javascripts/lib/bbcode-cooked-test.js @@ -0,0 +1,68 @@ +import { setupTest } from "ember-qunit"; +import { module, test } from "qunit"; +import { cook } from "discourse/lib/text"; + +async function cookFragment(raw, options = {}) { + const html = (await cook(raw, { previewing: true, ...options })).toString(); + return new DOMParser().parseFromString(html, "text/html").body; +} + +module("lib:bbcode-cooked", (hooks) => { + setupTest(hooks); + + hooks.beforeEach(function () { + this.siteSettings = this.owner.lookup("service:site-settings"); + this.siteSettings.bbcode_enabled = true; + }); + + test("previews mixed-case nested tags through the sanitized pipeline", async (assert) => { + const fragment = await cookFragment( + '[SPOILER=Example][I]Hidden[/I][/SPOILER]' + ); + + assert.strictEqual( + fragment.querySelector("details.bb-spoiler > summary").textContent, + "Spoiler: Example" + ); + assert.strictEqual( + fragment.querySelector(".bbcode-i").textContent, + "Hidden" + ); + assert.false(fragment.querySelector("details").open); + assert.strictEqual(fragment.querySelector("[onerror]"), null); + }); + + test("keeps native paragraphs and literal code examples in preview", async (assert) => { + const example = "[spoiler=Example]Literal[/spoiler]"; + const fragment = await cookFragment( + `First **paragraph**.\n\n\`\`\`bbcode\n${example}\n\`\`\`\n\nSecond paragraph.` + ); + + assert.deepEqual( + [...fragment.querySelectorAll("p")].map((p) => p.textContent), + ["First paragraph.", "Second paragraph."] + ); + assert.strictEqual( + fragment.querySelector("pre code").textContent.trim(), + example + ); + assert.strictEqual(fragment.querySelector("details"), null); + }); + + test("respects the enabled setting across preview cooks", async function (assert) { + const raw = "[spoiler=Example]Hidden[/spoiler]"; + assert.true( + !!(await cookFragment(raw)).querySelector("details.bb-spoiler") + ); + + this.siteSettings.bbcode_enabled = false; + const disabled = await cookFragment(raw); + assert.strictEqual(disabled.querySelector("details.bb-spoiler"), null); + assert.true(disabled.textContent.includes(raw)); + + this.siteSettings.bbcode_enabled = true; + assert.true( + !!(await cookFragment(raw)).querySelector("details.bb-spoiler") + ); + }); +});