Skip to content

Commit 2a1faeb

Browse files
committed
Live viewer: add escape support
Closes #136.
1 parent 2141112 commit 2a1faeb

File tree

3 files changed

+57
-1
lines changed

3 files changed

+57
-1
lines changed

live-viewer/index.html

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,19 @@ <h1>Live URL Viewer</h1>
2222
<label for="url">URL:</label>
2323
<input id="url" value="https://example.com/" autofocus>
2424

25+
<label for="url-escaped">URL with escapes:</label>
26+
<input id="url-escaped" value="https://example.com/">
27+
2528
<label for="base">Base URL:</label>
2629
<input id="base" value="about:blank">
2730
</form>
2831

32+
<details>
33+
<summary>How do I use "URL with escapes?"</summary>
34+
35+
<p>Most of the time, you don't need to worry about this. But if you want to test characters outside of the U+0021 (!) through U+007E (~) range, which can be harder to input or to visually distinguish, you can use this field. Any strings of the form <samp>\u{<var>number</var>}</samp> will be decoded to their corresponding Unicode code point. So for example, you could test how null characters are processed by the URL parser by using the input <samp>test:\u{0}</samp>.
36+
</details>
37+
2938
<h2>Browser's URL components</h2>
3039

3140
<table class="output" id="browser-output">

live-viewer/live-viewer.mjs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import whatwgURL from "./whatwg-url.mjs";
22

33
const urlInput = document.querySelector("#url");
4+
const urlEscapedInput = document.querySelector("#url-escaped");
45
const baseInput = document.querySelector("#base");
56

67
const te = new TextEncoder();
@@ -19,8 +20,13 @@ const components = [
1920
"origin"
2021
];
2122

23+
urlInput.addEventListener("input", updateEscaped);
24+
urlEscapedInput.addEventListener("input", updateUnescaped);
25+
2226
urlInput.addEventListener("input", update);
27+
urlEscapedInput.addEventListener("input", update);
2328
baseInput.addEventListener("input", update);
29+
2430
window.addEventListener("hashchange", setFromFragment);
2531
setFromFragment();
2632
update();
@@ -54,6 +60,14 @@ function setResult(kind, result, mismatchedComponents) {
5460
}
5561
}
5662

63+
function updateEscaped() {
64+
urlEscapedInput.value = escape(urlInput.value);
65+
}
66+
67+
function updateUnescaped() {
68+
urlInput.value = unescape(urlEscapedInput.value);
69+
}
70+
5771
function setComponentElValue(componentEl, value) {
5872
const isEmptyString = value === "";
5973

@@ -120,6 +134,7 @@ function setFromFragment() {
120134
console.warn("base hash parameter was not deserializable.");
121135
}
122136

137+
updateEscaped();
123138
update();
124139
}
125140

@@ -139,3 +154,27 @@ function decodeFromBase64(encoded) {
139154
const originalString = td.decode(bytes);
140155
return originalString;
141156
}
157+
158+
function escape(rawString) {
159+
return rawString
160+
.replaceAll(
161+
"\\u",
162+
"\\\\u"
163+
)
164+
.replaceAll(
165+
/[^\u{0021}-\u{007E}]/ug,
166+
c => `\\u{${c.codePointAt(0).toString(16).padStart(4, "0")}}`
167+
);
168+
}
169+
170+
function unescape(escapedString) {
171+
return escapedString
172+
.replaceAll(
173+
/(?<!\\)\\u\{([0-9a-fA-F]+)\}/ug,
174+
(_, c) => String.fromCodePoint(Number.parseInt(c, 16))
175+
)
176+
.replaceAll(
177+
"\\\\u",
178+
"\\u"
179+
);
180+
}

live-viewer/style.css

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
* {
2-
font-family: Georgia;
32
box-sizing: border-box;
43
}
54

65
body {
6+
font-family: Georgia;
77
max-width: 70em;
88
margin: 12px;
99
}
@@ -88,3 +88,11 @@ form input {
8888
.output td.empty-string:not(.fail) {
8989
color: #CCC;
9090
}
91+
92+
summary {
93+
cursor: pointer;
94+
}
95+
96+
samp {
97+
font-family: monospace;
98+
}

0 commit comments

Comments
 (0)