Back to packages list

Vals using escape-html

Description from the NPM package:
Escape string for use in HTML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { pollRss } from "https://esm.town/v/zackoverflow/pollRss";
type UnPromise<T> = T extends Promise<infer P> ? P : T;
export const rssEntriesToHTML = async (
newRssEntries: UnPromise<ReturnType<typeof pollRss>>,
) => {
const esc = (await import("npm:escape-html")).default;
const mdStrings = [];
mdStrings.push(`<h1> RSS Update ${(new Date()).toDateString()} </h1>`);
for (const { title, url, newEntries } of newRssEntries) {
mdStrings.push(`<h2> <a href="${url}">${title}</a> </h3>`);
mdStrings.push(...newEntries.flatMap((entry) => [
`<h3> <a href="${
entry.links[0]?.href || "oops"
}">${entry.title.value}</a> </h3>`,
`<div>${entry.description.value.slice(0, 256)}...</div>`,
]));
}
return mdStrings.join("");
};

Guestbook

You can put an interactive guestbook on your website using vals!

This val is the backend of the guestbook that returns existing messages and handles new messages.

To generate a HTML snippet to post on your website, use @vtdocs.generateGuestbookSnippet.

Setup

Fork this val.

Paste the below snippet into your Val Town workspace – replacing alice and guestbook with your Val Town username and the name of @vtdocs.guestbook fork respectively.

@vtdocs.generateGuestbookSnippet('alice', 'guestbook')

Place the HTML block it returns anywhere on your website.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { set } from "https://esm.town/v/std/set?v=11";
let { guestbookMessages } = await import("https://esm.town/v/vtdocs/guestbookMessages");
export const guestbook = async (req: express.Request, res: express.Response) => {
const esc = (await import("npm:escape-html@1.0.3")).default;
if (guestbookMessages === undefined) {
guestbookMessages = [];
}
if (req.method === "GET") {
return res.json(guestbookMessages);
}
if (req.body.name === undefined || req.body.text === undefined) {
return res.status(400);
}
guestbookMessages.push({
name: esc(req.body.name),
text: esc(req.body.text),
});
await set(
"guestbookMessages",
guestbookMessages,
);
return res.status(200);
};

SVG Button Link for Markdown

Pioneered by @easrng, this val allows you to create an SVG image via the Express API, which we can embed in Val Town markdown, and wrap so it acts like a button.

Usage

[![Button Link](https://stevekrouse-button.express.val.run/Button%20Link)](https://example.com)

Button Link

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import { fetch } from "https://esm.town/v/std/fetch";
export async function button(req, res) {
async function inlineUrls(cssString) {
const urlRegex = /url\(['"]?(.*?)['"]?\)/g;
let result = cssString;
for (const urlMatch of cssString.matchAll(urlRegex)) {
const originalUrl = urlMatch[1];
const response = await fetch(originalUrl);
const data = new Uint8Array(await response.arrayBuffer());
result = result.replace(
urlMatch[0],
`url("data:${response.headers.get("content-type")};base64,${
btoa(data.reduce((a, b) => a + String.fromCharCode(b), ""))
}")`,
);
}
return result;
}
const esc = (await import("npm:escape-html@1.0.3")).default;
const { init } = await import("npm:server-text-width@1.0.2");
const { getTextWidth } = init(
(await import("https://easrng.github.io/IBMPlexSansMetrics.js")).default,
);
const text = decodeURIComponent(req.path.split("/")[1]);
let fontCss = "";
try {
const r = await fetch(
"https://fonts.googleapis.com/css2?family=" +
encodeURIComponent("IBM Plex Sans:wght@600") +
"&text=" +
encodeURIComponent(text),
{ headers: { "user-agent": req.get("user-agent") } },
);
if (!r.ok)
throw new Error();
fontCss = await inlineUrls(await r.text());
}
catch (e) {}
res.set("content-type", "image/svg+xml");
const width = Math.ceil(getTextWidth(text) + 16);
res.end(
`<svg viewBox="0 0 ${width} 28" width="${width}" height="28" xmlns="http://www.w3.org/2000/svg"><defs><style>${fontCss}</style></defs><foreignObject width="100%" height="100%"><span xmlns="http://www.w3.org/1999/xhtml" style="font-family: 'IBM Plex San
esc(text)
}</span></foreignObject></svg>`,
);
}
// Forked from @easrng.button

Usage

![Button](https://pomdtr-badge.express.val.run/Custom%20Badge)

Button

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import { fetch } from "https://esm.town/v/std/fetch";
export async function badge(req, res) {
async function inlineUrls(cssString) {
const urlRegex = /url\(['"]?(.*?)['"]?\)/g;
let result = cssString;
for (const urlMatch of cssString.matchAll(urlRegex)) {
const originalUrl = urlMatch[1];
const response = await fetch(originalUrl);
const data = new Uint8Array(await response.arrayBuffer());
result = result.replace(
urlMatch[0],
`url("data:${response.headers.get("content-type")};base64,${
btoa(data.reduce((a, b) => a + String.fromCharCode(b), ""))
}")`,
);
}
return result;
}
const esc = (await import("npm:escape-html@1.0.3")).default;
const { init } = await import("npm:server-text-width@1.0.2");
const { getTextWidth } = init(
(await import("https://easrng.github.io/IBMPlexSansMetrics.js")).default,
);
const text = decodeURIComponent(req.path.split("/")[1]);
let fontCss = "";
try {
const r = await fetch(
"https://fonts.googleapis.com/css2?family=" +
encodeURIComponent("IBM Plex Sans:wght@600") +
"&text=" +
encodeURIComponent(text),
{ headers: { "user-agent": req.get("user-agent") } },
);
if (!r.ok)
throw new Error();
fontCss = await inlineUrls(await r.text());
}
catch (e) {}
res.set("content-type", "image/svg+xml");
const width = Math.ceil(getTextWidth(text) + 16);
res.end(
`<svg viewBox="0 0 ${width} 28" width="${width}" height="28" xmlns="http://www.w3.org/2000/svg"><defs><style>${fontCss}</style></defs><foreignObject width="100%" height="100%"><span xmlns="http://www.w3.org/1999/xhtml" style="font-family: 'IBM Plex San
esc(text)
}</span></foreignObject></svg>`,
);
}
// Forked from @easrng.button
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import { upgradeExpress } from "https://esm.town/v/easrng/upgradeExpress";
async function inlineUrls(cssString) {
const urlRegex = /url\(['"]?(.*?)['"]?\)/g;
let result = cssString;
for (const urlMatch of cssString.matchAll(urlRegex)) {
const originalUrl = urlMatch[1];
const response = await fetch(originalUrl);
const data = new Uint8Array(await response.arrayBuffer());
result = result.replace(
urlMatch[0],
`url("data:${response.headers.get("content-type")};base64,${
btoa(data.reduce((a, b) => a + String.fromCharCode(b), ""))
}")`,
);
}
return result;
}
import IBMPlexSansMetrics from "https://easrng.github.io/IBMPlexSansMetrics.js";
import esc from "npm:escape-html@1.0.3";
import { init } from "npm:server-text-width@1.0.2";
export const button = upgradeExpress(async function button(req) {
const { getTextWidth } = init(
IBMPlexSansMetrics,
);
const text = decodeURIComponent(new URL(req.url).pathname.slice(1) || "Button");
let fontCss = "";
try {
const r = await fetch(
"https://fonts.googleapis.com/css2?family="
+ encodeURIComponent("IBM Plex Sans:wght@600")
+ "&text="
+ encodeURIComponent(text),
{ headers: { "user-agent": req.headers.get("user-agent") } },
);
if (!r.ok)
throw new Error();
fontCss = await inlineUrls(await r.text());
}
catch (e) {}
const width = Math.ceil(getTextWidth(text) + 16);
return new Response(
`<svg viewBox="0 0 ${width} 28" width="${width}" height="28" xmlns="http://www.w3.org/2000/svg"><defs><style>${fontCss}</style></defs><foreignObject width="100%" height="100%"><span xmlns="http://www.w3.org/1999/xhtml" style="font-family: 'IBM Plex San
esc(text)
}</span></foreignObject></svg>`,
{
headers: {
"content-type": "image/svg+xml",
},
},
);
});
1
Next