diff --git a/web_src/js/utils/dom.test.ts b/web_src/js/utils/dom.test.ts
index 3edbe94ce4a..a30b29bab2c 100644
--- a/web_src/js/utils/dom.test.ts
+++ b/web_src/js/utils/dom.test.ts
@@ -9,6 +9,8 @@ import {
test('createElementFromHTML', () => {
expect(createElementFromHTML('foobar').outerHTML).toEqual('foobar');
expect(createElementFromHTML('
| foo |
').outerHTML).toEqual('| foo |
');
+ expect(createElementFromHTML('| foo |
').outerHTML).toEqual('| foo |
');
+ expect(createElementFromHTML('').outerHTML).toEqual('');
});
test('createElementFromAttrs', () => {
diff --git a/web_src/js/utils/dom.ts b/web_src/js/utils/dom.ts
index d6823fc8950..b18f33f33dc 100644
--- a/web_src/js/utils/dom.ts
+++ b/web_src/js/utils/dom.ts
@@ -267,9 +267,14 @@ export function isElemVisible(el: HTMLElement): boolean {
export function createElementFromHTML(htmlString: string): T {
htmlString = htmlString.trim();
+ const isLetter = (code: number) => (code >= 65 && code <= 90) || (code >= 97 && code <= 122);
+ const startsWithTag = (s: string, tag: string) => {
+ return s.startsWith('<') &&
+ s.substring(1, 1 + tag.length).toLowerCase() === tag.toLowerCase() &&
+ !isLetter(s[1 + tag.length].charCodeAt(0));
+ };
// There is no way to create some elements without a proper parent, jQuery's approach: https://github.com/jquery/jquery/blob/main/src/manipulation/wrapMap.js
- // eslint-disable-next-line github/unescaped-html-literal
- if (htmlString.startsWith('('tr')!;
diff --git a/web_src/js/utils/url.ts b/web_src/js/utils/url.ts
index 84328faf249..06e1aa29511 100644
--- a/web_src/js/utils/url.ts
+++ b/web_src/js/utils/url.ts
@@ -1,3 +1,5 @@
+import {html, htmlRaw} from './html.ts';
+
export function urlQueryEscape(s: string) {
// See "TestQueryEscape" in backend
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent#encoding_for_rfc3986
@@ -35,9 +37,9 @@ const urlLinkifyPattern = /(<([-\w]+)[^>]*>)|(<\/([-\w]+)[^>]*>)|(https?:\/\/[^\
const trailingPunctPattern = /[.,;:!?]+$/;
// Convert URLs to clickable links in HTML, preserving existing HTML tags
-export function linkifyURLs(html: string): string {
+export function linkifyURLs(htmlString: string): string {
let inAnchor = false;
- return html.replace(urlLinkifyPattern, (match, _openTagFull, openTag, _closeTagFull, closeTag, url) => {
+ return htmlString.replace(urlLinkifyPattern, (match, _openTagFull, openTag, _closeTagFull, closeTag, url) => {
// skip URLs inside existing tags
if (openTag === 'a') {
inAnchor = true;
@@ -54,6 +56,6 @@ export function linkifyURLs(html: string): string {
const cleanUrl = trailingPunct ? url.slice(0, -trailingPunct[0].length) : url;
const trailing = trailingPunct ? trailingPunct[0] : '';
// safe because regexp only matches valid URLs (no quotes or angle brackets)
- return `${cleanUrl}${trailing}`; // eslint-disable-line github/unescaped-html-literal
+ return html`${htmlRaw(cleanUrl)}${htmlRaw(trailing)}`;
});
}