From c5f9a6435792eba3a838882873f14425a4649cae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abigail=20Magalh=C3=A3es?= Date: Mon, 16 Aug 2021 14:04:52 -0300 Subject: [PATCH] parse URLs --- .gitignore | 3 +- script.js | 1 - src/jsx.ts | 10 +++++-- src/main.tsx | 78 ++++++++++++++++++++++++++++++++++++---------------- 4 files changed, 64 insertions(+), 28 deletions(-) delete mode 100644 script.js diff --git a/.gitignore b/.gitignore index 9875610..9f3f28a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .out/ .out-tsc node_modules -/out.html \ No newline at end of file +/out.html +/script.js \ No newline at end of file diff --git a/script.js b/script.js deleted file mode 100644 index 6d2de71..0000000 --- a/script.js +++ /dev/null @@ -1 +0,0 @@ -function e(e,t){return" ".repeat(null!=e?e:0)+t}var t={"&":"&","<":"<",">":">"};class s{constructor(e){this.attrs={},this.content=[],this.name=e}setAttribute(e,t){this.attrs[e]=t}appendChild(...e){this.content.push(...e)}toHtmlStr(t){const s=[],n=[`<${this.name}`];for(const e of Object.keys(this.attrs)){if(!this.attrs.hasOwnProperty(e))continue;const t=this.attrs[e];void 0!==t&&("boolean"==typeof t&&t?n.push(e):n.push(`${e}="${t}"`))}if(0===this.content.length)return e(t,n.join(" "))+" />";s.push(e(t,n.join(" "))+">");for(const e of this.content)try{s.push(e.toHtmlStr((null!=t?t:0)+2))}catch(t){s.push(e.toString())}return s.push(e(t,``)),s.join("\n")}}class n{constructor(e){this.value=e}toHtmlStr(t){return e(t,this.value)}}const r=(e,t)=>{if("string"==typeof t)e.appendChild(new n(t.toString()));else if(t instanceof Array)t.forEach((t=>r(e,t)));else{if(void 0===t)return;e.appendChild(t)}};class a{static createElement(e,t,...n){if("string"!=typeof e)return e(t);{const a=new s(e),l=t||{};for(let e in l)if(e&&l.hasOwnProperty(e)){let t=l[e];!0===t?a.setAttribute(e,e):!1!==t&&null!=t&&a.setAttribute(e,t.toString())}for(let e=0;ee.trim())).filter((e=>"."!==e&&""!==e)).map(((e,t)=>{try{return function(e){const t=(e=e.trimEnd()).slice(0,5);switch(e[6]){case"-":return{time:t,type:"status",content:e.slice(10)};case"<":{const s=e[7];if("@"!==s&&"+"!==s&&" "!==s)throw`invalid mode: ${s}`;const n=e.slice(8).split(">")[0];return{time:t,type:"message",user:{mode:s,name:n},content:e.slice(10+n.length)}}case" ":{const s=e.slice(8).split(" ")[1];return{time:t,type:"action",user:{name:s},content:e.slice(10+s.length)}}}throw`couldn't parse message ${e}`}(e)}catch(e){throw`${e} ${t}`}}))),o=({message:e,inclTs:s})=>{let n=e.content.replace(/[&<>]/g,(e=>t[e]||e));return"action"===e.type&&(n="* "+n),a.createElement("div",{class:`mg-m mg-${e.type}`},void 0===s||!0===s?a.createElement("span",{class:"mg-ts"}," ",e.time," "):void 0,a.createElement("span",{class:`mg-txt mg-${e.type}`}," ",n," "))};const i=c.map((function(e){var t;const s=e.user;return console.error(e),void 0!==s?a.createElement("div",{class:"mg-user"},a.createElement("div",{class:"mg-pfp"},a.createElement("img",{src:`https://offtopia.org/pages/people/${s.name.toLocaleLowerCase()}.jpg`,alt:`${s.name}'s profile picture`})),a.createElement("div",{class:"mg-contents"},a.createElement("span",{class:"mg-ts-u"},a.createElement("span",{class:"mg-ts-u-u"}," ",null===(t=e.user)||void 0===t?void 0:t.name," "),a.createElement("span",{class:"mg-ts-u-ts"}," ",e.messages[0].time," ")),a.createElement(o,{message:e.messages[0],inclTs:!1}),e.messages.slice(1).map((e=>a.createElement(o,{message:e}))))):e.messages.length<1?a.createElement("span",null):a.createElement("div",{class:"mg-no-user"},e.messages.map((e=>a.createElement(o,{message:e}))))})),m=a.createElement("html",null,a.createElement("body",null,i,a.createElement("svg",{id:"squircle-container",width:"0",height:"0"},a.createElement("clipPath",{id:"squircle",clipPathUnits:"objectBoundingBox"},a.createElement("path",{fill:"red",stroke:"none",d:"M 0,0.5 C 0,0 0,0 0.5,0 S 1,0 1,0.5 1,1 0.5,1 0,1 0,0.5"})))),a.createElement("style",null,l("style.css")));console.log(m.toHtmlStr()); diff --git a/src/jsx.ts b/src/jsx.ts index e6848fe..15f872a 100644 --- a/src/jsx.ts +++ b/src/jsx.ts @@ -1,5 +1,5 @@ -function ind(x: number | undefined | null, str: string): string { - return " ".repeat(x ?? 0) + str; +function ind(_: number | undefined | null, str: string): string { + return str; }; var tagsToReplace: Record = { @@ -60,7 +60,7 @@ class HtmlElem implements HtmlEnt { } } out.push(ind(indent, ``)); - return out.join("\n"); + return out.join(""); } } @@ -93,6 +93,10 @@ type JSXName = string | ((props: T) => HtmlEnt); type ElemProps = { [id: string]: string | boolean } export class JSX { + static createTextNode(t: string): HtmlEnt { + return new HtmlText(t); + } + static createElement(fn: (props: T) => HtmlEnt, props: T, ...content: Content[]): HtmlEnt; static createElement>(name: T, arg: T extends 'string' ? ElemProps : P, ...content: Content[]): HtmlEnt { if (typeof name !== 'string') { diff --git a/src/main.tsx b/src/main.tsx index 41b09a5..2b75670 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -4,6 +4,14 @@ const { readFileSync } = require("fs"); const contents = readFileSync(0); const lines: string[] = contents.toString().split('\n'); +type MessageFrag = { + type: 'url', + link: string +} | { + type: 'text', + content: string +}; + type IrcUser = { mode?: '+' | '@' | ' ', name: string @@ -12,28 +20,46 @@ type IrcUser = { type IrcMessage = { time: string, type: 'status', - content: string + content: MessageFrag[] } | { time: string, type: 'action', user: IrcUser, - content: string + content: MessageFrag[] } | { time: string, type: 'message', user: IrcUser, - content: string + content: MessageFrag[] }; +const URLScheme = /https?:\/\/[\w\.\/\?\=\-]+/; + +function parseMessage(m: string): MessageFrag[] { + m = m.trimEnd(); + const parts: MessageFrag[] = []; + let match: RegExpMatchArray | null; + while ((match = m.match(URLScheme)) != null) { + parts.push({ type: 'text', content: m.slice(0, match.index) }); + parts.push({ type: 'url', link: match[0] }); + m = m.slice((match.index ?? 0) + match[0].length); + } + if (m !== "") { + parts.push({ type: 'text', content: m }); + } + console.error(parts); + return parts; +} + function parse(s: string): IrcMessage { s = s.trimEnd(); const time = s.slice(0, 5); - switch(s[6]) { + switch (s[6]) { case '-': return { time, type: 'status', - content: s.slice(10) + content: parseMessage(s.slice(10)) }; case '<': { const mode = s[7]; @@ -43,7 +69,7 @@ function parse(s: string): IrcMessage { time, type: 'message', user: { mode, name }, - content: s.slice(10 + name.length) + content: parseMessage(s.slice(10 + name.length)) } } case ' ': { @@ -52,14 +78,14 @@ function parse(s: string): IrcMessage { time, type: 'action', user: { name }, - content: s.slice(10 + name.length) + content: parseMessage(s.slice(10 + name.length)) } } } throw `couldn't parse message ${s}`; }; -const messages = lines.map(l => l.trim()).filter(l => l !== '.' && l !== "").map((v, i) => { try { return parse(v) } catch(e) { throw `${e} ${i}` } }); +const messages = lines.map(l => l.trim()).filter(l => l !== '.' && l !== "").map((v, i) => { try { return parse(v) } catch (e) { throw `${e} ${i}` } }); type MsgGroup = { user?: IrcUser, @@ -126,37 +152,43 @@ function group(messages: IrcMessage[]): MsgGroup[] { const groups = group(messages); -const Message = ( {message, inclTs }: { message: IrcMessage, inclTs?: boolean }) => { - let txt = escapeHtmlShitty(message.content); - if (message.type === "action") { - txt = "* " + txt; +const render = (c: MessageFrag): JSX.Element => { + if (c.type === "text") { + return JSX.createTextNode(escapeHtmlShitty(c.content)); + } else if (c.type === "url") { + return {escapeHtmlShitty(c.link)}; + } else { + throw "impossible!" // thanks TypeScript } - return
+} + +const Message = ({ message, inclTs }: { message: IrcMessage, inclTs?: boolean }) => { + const prefix = message.type === "action" ? "* " : ""; + return
{ - (inclTs === undefined || inclTs === true) ? { message.time } : undefined + (inclTs === undefined || inclTs === true) ? {message.time} : undefined } - { txt } + {prefix} {message.content.map(c => render(c))}
} function renderMessageGroup(m: MsgGroup): HtmlEnt { const u = m.user; - console.error(m); if (u !== undefined) { return
- { + {`${u.name}'s
- { m.user?.name } - { m.messages[0].time } + {m.user?.name} + {m.messages[0].time} { - m.messages.slice(1).map(x => + m.messages.slice(1).map(x => ) } @@ -168,7 +200,7 @@ function renderMessageGroup(m: MsgGroup): HtmlEnt { } return
- { m.messages.map(x => ) } + {m.messages.map(x => )}
; } } @@ -177,7 +209,7 @@ const rendered = groups.map(renderMessageGroup); const elem = - { rendered } + {rendered} @@ -185,7 +217,7 @@ const elem = ;