UNPKG

@rcb-plugins/html-renderer

Version:

A simple plugin for rendering html messages in React ChatBotify.

12 lines (9 loc) 13.6 kB
"use strict";const S=require("react"),E=require("react-chatbotify"),B=require("react/jsx-runtime"),d=1,Y=2,q=4,b=8,C=16,O=32,w=64,H={a:{content:d|b,self:!1,type:d|b|O|w},address:{invalid:["h1","h2","h3","h4","h5","h6","address","article","aside","section","div","header","footer"],self:!1},audio:{children:["track","source"]},br:{type:d|b,void:!0},body:{content:d|Y|q|b|C|O|w},button:{content:b,type:d|b|O|w},caption:{content:d,parent:["table"]},col:{parent:["colgroup"],void:!0},colgroup:{children:["col"],parent:["table"]},details:{children:["summary"],type:d|O|w},dd:{content:d,parent:["dl"]},dl:{children:["dt","dd"],type:d},dt:{content:d,invalid:["footer","header"],parent:["dl"]},figcaption:{content:d,parent:["figure"]},footer:{invalid:["footer","header"]},header:{invalid:["footer","header"]},hr:{type:d,void:!0},img:{void:!0},li:{content:d,parent:["ul","ol","menu"]},main:{self:!1},ol:{children:["li"],type:d},picture:{children:["source","img"],type:d|b|C},rb:{parent:["ruby","rtc"]},rp:{parent:["ruby","rtc"]},rt:{content:b,parent:["ruby","rtc"]},rtc:{content:b,parent:["ruby"]},ruby:{children:["rb","rp","rt","rtc"]},source:{parent:["audio","video","picture"],void:!0},summary:{content:b,parent:["details"]},table:{children:["caption","colgroup","thead","tbody","tfoot","tr"],type:d},tbody:{parent:["table"],children:["tr"]},td:{content:d,parent:["tr"]},tfoot:{parent:["table"],children:["tr"]},th:{content:d,parent:["tr"]},thead:{parent:["table"],children:["tr"]},tr:{parent:["table","tbody","thead","tfoot"],children:["th","td"]},track:{parent:["audio","video"],void:!0},ul:{children:["li"],type:d},video:{children:["track","source"]},wbr:{type:d|b,void:!0}};function _(a){return e=>{H[e]={...a,...H[e]}}}["address","main","div","figure","p","pre"].forEach(_({content:d,type:d|w}));["abbr","b","bdi","bdo","cite","code","data","dfn","em","i","kbd","mark","q","ruby","samp","strong","sub","sup","time","u","var"].forEach(_({content:b,type:d|b|w}));["p","pre"].forEach(_({content:b,type:d|w}));["s","small","span","del","ins"].forEach(_({content:b,type:d|b}));["article","aside","footer","header","nav","section","blockquote"].forEach(_({content:d,type:d|Y|w}));["h1","h2","h3","h4","h5","h6"].forEach(_({content:b,type:d|q|w}));["audio","canvas","iframe","img","video"].forEach(_({type:d|b|C|w}));const D=Object.freeze(H),J=["applet","base","body","command","embed","frame","frameset","head","html","link","meta","noscript","object","script","style","title"],X=Object.keys(D).filter(a=>a!=="canvas"&&a!=="iframe"),h=1,Q=2,L=3,x=4,G=5,W=Object.freeze({alt:h,cite:h,class:h,colspan:L,controls:x,datetime:h,default:x,disabled:x,dir:h,height:h,href:h,id:h,kind:h,label:h,lang:h,loading:h,loop:x,media:h,muted:x,poster:h,rel:h,role:h,rowspan:L,scope:h,sizes:h,span:L,start:L,style:G,src:h,srclang:h,srcset:h,tabindex:h,target:h,title:h,type:h,width:h}),Z=Object.freeze({class:"className",colspan:"colSpan",datetime:"dateTime",rowspan:"rowSpan",srclang:"srcLang",srcset:"srcSet",tabindex:"tabIndex"});function P(){return P=Object.assign||function(a){for(var e=1;e<arguments.length;e++){var t=arguments[e];for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(a[r]=t[r])}return a},P.apply(this,arguments)}function U({attributes:a={},className:e,children:t=null,selfClose:r=!1,tagName:n}){const s=n;return r?S.createElement(s,P({className:e},a)):S.createElement(s,P({className:e},a),t)}class ee{attribute(e,t){return t}node(e,t){return t}}function te(a){return a&&a.__esModule&&Object.prototype.hasOwnProperty.call(a,"default")?a.default:a}/*! * escape-html * Copyright(c) 2012-2013 TJ Holowaychuk * Copyright(c) 2015 Andreas Lubbe * Copyright(c) 2015 Tiancheng "Timothy" Gu * MIT Licensed */var M,j;function re(){if(j)return M;j=1;var a=/["'&<>]/;M=e;function e(t){var r=""+t,n=a.exec(r);if(!n)return r;var s,c="",u=0,o=0;for(u=n.index;u<r.length;u++){switch(r.charCodeAt(u)){case 34:s="&quot;";break;case 38:s="&amp;";break;case 39:s="&#39;";break;case 60:s="&lt;";break;case 62:s="&gt;";break;default:continue}o!==u&&(c+=r.substring(o,u)),o=u+1,c+=s}return o!==u?c+r.substring(o,u):c}return M}var ne=re();const se=te(ne);function A(a,e,t){return e in a?Object.defineProperty(a,e,{value:t,enumerable:!0,configurable:!0,writable:!0}):a[e]=t,a}const ae=/(url|image|image-set)\(/i;class oe extends ee{attribute(e,t){return e==="style"&&Object.keys(t).forEach(r=>{String(t[r]).match(ae)&&delete t[r]}),t}}const V=1,ie=3,ce=/^<(!doctype|(html|head|body)(\s|>))/i,le=/^(aria-|data-|\w+:)/iu,ue=/{{{(\w+)\/?}}}/;function de(){if(!(typeof window>"u"||typeof document>"u"))return document.implementation.createHTMLDocument("Interweave")}class z{constructor(e,t={},r=[],n=[]){var s;if(A(this,"allowed",void 0),A(this,"banned",void 0),A(this,"blocked",void 0),A(this,"container",void 0),A(this,"content",[]),A(this,"props",void 0),A(this,"matchers",void 0),A(this,"filters",void 0),A(this,"keyIndex",void 0),process.env.NODE_ENV!=="production"&&e&&typeof e!="string")throw new TypeError("Interweave parser requires a valid string.");this.props=t,this.matchers=r,this.filters=[...n,new oe],this.keyIndex=-1,this.container=this.createContainer(e||""),this.allowed=new Set((s=t.allowList)!==null&&s!==void 0?s:X),this.banned=new Set(J),this.blocked=new Set(t.blockList)}applyAttributeFilters(e,t){return this.filters.reduce((r,n)=>r!==null&&typeof n.attribute=="function"?n.attribute(e,r):r,t)}applyNodeFilters(e,t){return this.filters.reduce((r,n)=>r!==null&&typeof n.node=="function"?n.node(e,r):r,t)}applyMatchers(e,t){const r={},{props:n}=this;let s=e,c=0,u=null;return this.matchers.forEach(o=>{const m=o.asTag().toLowerCase(),f=this.getTagConfig(m);if(n[o.inverseName]||!this.isTagAllowed(m)||!this.canRenderChild(t,f))return;let i="";for(;s&&(u=o.match(s));){const{index:l,length:p,match:g,valid:T,void:v,...y}=u,R=o.propName+String(c);l>0&&(i+=s.slice(0,l)),T?(i+=v?`{{{${R}/}}}`:`{{{${R}}}}${g}{{{/${R}}}}`,this.keyIndex+=1,c+=1,r[R]={children:g,matcher:o,props:{...n,...y,key:this.keyIndex}}):i+=g,o.greedy?(s=i+s.slice(l+p),i=""):s=s.slice(l+(p||g.length))}o.greedy||(s=i+s)}),c===0?e:this.replaceTokens(s,r)}canRenderChild(e,t){return!e.tagName||!t.tagName||e.void?!1:e.children.length>0?e.children.includes(t.tagName):e.invalid.length>0&&e.invalid.includes(t.tagName)?!1:t.parent.length>0?t.parent.includes(e.tagName):!e.self&&e.tagName===t.tagName?!1:!!(e&&e.content&t.type)}convertLineBreaks(e){const{noHtml:t,disableLineBreaks:r}=this.props;if(t||r||e.match(/<((?:\/[ a-z]+)|(?:[ a-z]+\/))>/gi))return e;let n=e.replace(/\r\n/g,` `);return n=n.replace(/\n{3,}/g,` `),n=n.replace(/\n/g,"<br/>"),n}createContainer(e){var t;const n=(typeof global<"u"&&global.INTERWEAVE_SSR_POLYFILL||de)();if(!n)return;const s=(t=this.props.containerTagName)!==null&&t!==void 0?t:"body",c=s==="body"||s==="fragment"?n.body:n.createElement(s);if(e.match(ce)){if(process.env.NODE_ENV!=="production")throw new Error("HTML documents as Interweave content are not supported.")}else c.innerHTML=this.convertLineBreaks(this.props.escapeHtml?se(e):e);return c}extractAttributes(e){const{allowAttributes:t}=this.props,r={};let n=0;return e.nodeType!==V||!e.attributes||([...e.attributes].forEach(s=>{const{name:c,value:u}=s,o=c.toLowerCase(),m=W[o]||W[c];if(!this.isSafe(e)||!o.match(le)&&(!t&&(!m||m===Q)||o.startsWith("on")||u.replace(/(\s|\0|&#x0([9AD]);)/,"").match(/(javascript|vbscript|livescript|xss):/i)))return;let f=o==="style"?this.extractStyleAttribute(e):u;m===x?f=!0:m===L?f=Number.parseFloat(String(f)):m!==G&&(f=String(f)),r[Z[o]||o]=this.applyAttributeFilters(o,f),n+=1}),n===0)?null:r}extractStyleAttribute(e){const t={};return Array.from(e.style).forEach(r=>{const n=e.style[r];(typeof n=="string"||typeof n=="number")&&(t[r.replace(/-([a-z])/g,(s,c)=>String(c).toUpperCase())]=n)}),t}getTagConfig(e){const t={children:[],content:0,invalid:[],parent:[],self:!0,tagName:"",type:0,void:!1};return D[e]?{...t,...D[e],tagName:e}:t}isSafe(e){if(typeof HTMLAnchorElement<"u"&&e instanceof HTMLAnchorElement){const t=e.getAttribute("href");if(t!=null&&t.startsWith("#"))return!0;const r=e.protocol.toLowerCase();return r===":"||r==="http:"||r==="https:"||r==="mailto:"||r==="tel:"}return!0}isTagAllowed(e){return this.banned.has(e)||this.blocked.has(e)?!1:this.props.allowElements||this.allowed.has(e)}parse(){return this.container?this.parseNode(this.container,this.getTagConfig(this.container.nodeName.toLowerCase())):[]}parseNode(e,t){const{noHtml:r,noHtmlExceptMatchers:n,allowElements:s,transform:c,transformOnlyAllowList:u}=this.props;let o=[],m="";return[...e.childNodes].forEach(f=>{if(f.nodeType===V){const l=f.nodeName.toLowerCase(),p=this.getTagConfig(l);m&&(o.push(m),m="");const g=this.applyNodeFilters(l,f);if(!g)return;let T;if(c&&!(u&&!this.isTagAllowed(l))){this.keyIndex+=1;const v=this.keyIndex;T=this.parseNode(g,p);const y=c(g,T,p);if(y===null)return;if(typeof y<"u"){o.push(S.cloneElement(y,{key:v}));return}this.keyIndex=v-1}if(this.banned.has(l))return;if(!(r||n&&l!=="br")&&this.isTagAllowed(l)&&(s||this.canRenderChild(t,p))){var i;this.keyIndex+=1;const v=this.extractAttributes(g),y={tagName:l};v&&(y.attributes=v),p.void&&(y.selfClose=p.void),o.push(S.createElement(U,{...y,key:this.keyIndex},(i=T)!==null&&i!==void 0?i:this.parseNode(g,p)))}else o=[...o,...this.parseNode(g,p.tagName?p:t)]}else if(f.nodeType===ie){const l=r&&!n?f.textContent:this.applyMatchers(f.textContent||"",t);Array.isArray(l)?o=[...o,...l]:m+=l}}),m&&o.push(m),o}replaceTokens(e,t){if(!e.includes("{{{"))return e;const r=[];let n=e,s=null;for(;s=n.match(ue);){const[c,u]=s,o=s.index,m=c.includes("/");if(process.env.NODE_ENV!=="production"&&!t[u])throw new Error(`Token "${u}" found but no matching element to replace with.`);o>0&&(r.push(n.slice(0,o)),n=n.slice(o));const{children:f,matcher:i,props:l}=t[u];let p;if(m)p=c.length,r.push(i.createElement(f,l));else{const g=n.match(new RegExp(`{{{/${u}}}}`));if(process.env.NODE_ENV!=="production"&&!g)throw new Error(`Closing token missing for interpolated element "${u}".`);p=g.index+g[0].length,r.push(i.createElement(this.replaceTokens(n.slice(c.length,g.index),t),l))}n=n.slice(p)}return n.length>0&&r.push(n),r.length===0?"":r.length===1&&typeof r[0]=="string"?r[0]:r}}function pe(a){var e;const{attributes:t,className:r,containerTagName:n,content:s,emptyContent:c,parsedContent:u,tagName:o,noWrap:m}=a,f=(e=n??o)!==null&&e!==void 0?e:"span",i=f==="fragment"?!0:m;let l;if(u)l=u;else{const p=new z(s??"",a).parse();p.length>0&&(l=p)}return l||(l=c),i?S.createElement(S.Fragment,null,l):S.createElement(U,{attributes:t,className:r,tagName:f},l)}function fe(a){const{attributes:e,className:t,content:r="",disableFilters:n=!1,disableMatchers:s=!1,emptyContent:c=null,filters:u=[],matchers:o=[],onAfterParse:m=null,onBeforeParse:f=null,tagName:i="span",noWrap:l=!1,...p}=a,g=s?[]:o,T=n?[]:u,v=f?[f]:[],y=m?[m]:[];g.forEach(N=>{N.onBeforeParse&&v.push(N.onBeforeParse.bind(N)),N.onAfterParse&&y.push(N.onAfterParse.bind(N))});const R=v.reduce((N,k)=>{const I=k(N,a);if(process.env.NODE_ENV!=="production"&&typeof I!="string")throw new TypeError("Interweave `onBeforeParse` must return a valid HTML string.");return I},r??""),K=new z(R,p,g,T),F=y.reduce((N,k)=>{const I=k(N,a);if(process.env.NODE_ENV!=="production"&&!Array.isArray(I))throw new TypeError("Interweave `onAfterParse` must return an array of strings and React elements.");return I},K.parse());return S.createElement(pe,{attributes:e,className:t,containerTagName:a.containerTagName,emptyContent:c,noWrap:l,parsedContent:F.length===0?void 0:F,tagName:i})}const he=({children:a})=>{const e=typeof a=="string"?a:"";return B.jsx("div",{style:{whiteSpace:"normal"},children:B.jsx(fe,{content:e})})},me={autoConfig:!0},$=(a,e,t)=>{var n;if(!a.detail.currPath)return!1;const r=e[a.detail.currPath];return r?((n=r.renderHtml)==null?void 0:n.map(s=>s.toUpperCase()).includes(t))??!1:!1},ge=a=>{const e=[];let t="",r=!1;for(let n=0;n<a.length;n++){const s=a[n];s==="<"?r?(e.push(t),t=s):(r=!0,t=s):s===">"?(t+=s,e.push(t),t="",r=!1):r?t+=s:e.push(s)}return t!==""&&e.push(t),e},be=a=>typeof window.DOMParser<"u"?new DOMParser().parseFromString(a,"text/html").body.textContent||"":a.replace(/<\/?[^>]+(>|$)/g,""),Ee=a=>{const{getFlow:e}=E.useFlow(),{messages:t,replaceMessages:r}=E.useMessages(),{settings:n}=E.useSettings(),{hasChatHistoryLoaded:s}=E.useChatHistory(),c={...a,...me},u=c.htmlComponent?c.htmlComponent:he;S.useEffect(()=>{var i,l;if(s){const p=[...t];for(let g=0;g<p.length&&g<(((i=n.chatHistory)==null?void 0:i.maxEntries)??30);g++){const T=p[g];(l=T.tags)!=null&&l.includes("rcb-html-renderer-plugin:parsed")&&(T.contentWrapper=u)}r(p)}},[s]);const o=async i=>{var p;const l=(p=i.data.message)==null?void 0:p.sender.toUpperCase();typeof i.data.message.content=="string"&&$(i,e(),l)&&(i.type==="rcb-start-simulate-stream-message"&&(i.data.simulateStreamChunker=ge),i.data.message.contentWrapper=u,i.data.message.tags||(i.data.message.tags=[]),i.data.message.tags.push("rcb-html-renderer-plugin:parsed"))},m=async i=>{$(i,e(),"BOT")&&(i.data.textToRead=be(i.data.textToRead))};E.useOnRcbEvent(E.RcbEvent.PRE_INJECT_MESSAGE,o),E.useOnRcbEvent(E.RcbEvent.CHUNK_STREAM_MESSAGE,o),E.useOnRcbEvent(E.RcbEvent.START_STREAM_MESSAGE,o),E.useOnRcbEvent(E.RcbEvent.START_SIMULATE_STREAM_MESSAGE,o),E.useOnRcbEvent(E.RcbEvent.START_SPEAK_AUDIO,m);const f={name:"@rcb-plugins/html-renderer"};return c!=null&&c.autoConfig&&(f.settings={event:{rcbPreInjectMessage:!0,rcbChunkStreamMessage:!0,rcbStartSimulateStreamMessage:!0,rcbStartStreamMessage:!0,rcbStartSpeakAudio:!0}}),f},ye=a=>()=>Ee(a);module.exports=ye;