structured-field-values
Version:
Implementation of Structured Field Values from IETF httpbis RFC8941
257 lines (224 loc) • 6.6 kB
HTML
<meta charset=utf-8>
<meta name=viewport content="width=device-width,initial-scale=1">
<title>Structured Field Values DEMO</title>
<style>
:root {
--width: 56vw;
--font-family: "Google Sans", Segoe UI, system-ui, -apple-system, sans-serif;
--font-color: #333;
--font-family-mono: Menlo, Consolas, Liberation, Mono, Courier, monospace;
}
@media screen and (max-device-width: 960px) {
:root {
--width: 96vw;
}
}
body {
font-family: var(--font-family);
width: var(--width);
color: var(--font-color);
margin: 0 auto;
padding-top: 10px;
}
h1, h2 {
margin: 0 0 10px 0;
font-weight: 400;
}
h1 {
font-size: 1.2em;
}
h2 {
font-size: 1em;
}
main {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
align-content: center;
height: 80vh;
}
footer {
width: 100%;
display: flex;
justify-content: flex-end;
font-style: italic;
}
textarea {
font-family: var(--font-family-mono);
width: 100%;
box-sizing: border-box;
}
button {
height: 100%;
padding: auto;
background-color: #eee;
border: solid 1px #ccc;
border-radius: 2px;
margin: 0 4px;
}
.encoded {
height: 15vh;
}
.decoded {
height: 30vh;
}
.active {
background-color: #a2c7fd;
}
.center {
display: flex;
justify-content: center;
box-sizing: border-box;
}
.samples {
font-family: var(--font-family-mono);
font-size: 0.5em;
margin: 0;
padding: 0 0 0 1em;
}
.samples li {
margin: 4px;
}
.samples a {
padding: 1px;
display: inline-block;
border-bottom: solid 2px #2981c1;
background-color: #eef4ff;
text-decoration: none;
}
.worn {
width: 100%;
padding: 0;
margin: 0;
font-size: 0.6em;
font-style: italic;
}
.error {
margin: 0;
padding: 0;
width: 100%;
height: 2em;
font-family: var(--font-family-mono);
font-size: 0.5em;
color: red;
}
</style>
<h1>Structured Field Values DEMO</h1>
<h2><a href=https://www.rfc-editor.org/rfc/rfc8941.html>RFC 8941: Structured Field Values for HTTP</a></h2>
<main>
<label for=encoded>encoded</label>
<textarea id=encoded class=encoded></textarea>
<p class="decode error"></p>
<div class=center>
<button class="decode Item">decode item ⬇️ </button>
<button class="decode List">decode list ⬇️ </button>
<button class="decode Dict">decode dict ⬇️ </button>
</div>
<div class=center>
<button class="encode Item">encode item ⬆️ </button>
<button class="encode List">encode list ⬆️ </button>
<button class="encode Dict">encode dict ⬆️ </button>
</div>
<p class="encode error"></p>
<label for=decoded>decoded</label>
<textarea id=decoded class=decoded></textarea>
<p class="worn">decode a token(Symbol) is not supported</p>
</main>
<hr>
<h2>sample</h2>
<ul class=samples>
<li>item: <a class="sample Item" href="">?1</a></li>
<li>item: <a class="sample Item" href="">a</a></li>
<li>item: <a class="sample Item" href="">"a"</a></li>
<li>item: <a class="sample Item" href="">100</a></li>
<li>item: <a class="sample Item" href="">@1659578233</a></li>
<li>list: <a class="sample List" href="">1,2,3,4</a></li>
<li>list: <a class="sample List" href="">"a";x, "b";y=?0, 10, (1 2)</a></li>
<li>dict: <a class="sample Dict" href="">a=10, b=20, c=30</a>
<li>dict: <a class="sample Dict" href="">a="x", b=10, c=?0, d=(1 2)</a></li>
<li>Accept-CH(<a href=https://www.rfc-editor.org/rfc/rfc8942.html>RFC 8942: HTTP Client Hints</a>): <a class="sample List" href="">Sec-CH-Example, Sec-CH-Example-2</a></li>
<li>Cache-Status(<a href=https://tools.ietf.org/html/draft-ietf-httpbis-cache-header-07>id</a>): <a class="sample List" href="">OriginCache; hit; ttl=1100; collapsed, "CDN Company Here"; hit; ttl=545</a></li>
<li>Proxy-Status(<a href=https://tools.ietf.org/html/draft-ietf-httpbis-proxy-status-02>id</a>): <a class="sample List" href="">ExampleProxy; error="http_protocol_error"; details="Malformed response header - space before colon"</a></li>
</ul>
<hr>
<footer><small>Author <a href=https://jxck.io>Jxck</a><small></footer>
<script type=module>
const $ = document.querySelector.bind(document)
const $$ = document.querySelectorAll.bind(document)
EventTarget.prototype.on = EventTarget.prototype.addEventListener
import * as sfv from "./index.js"
const $encoded = $("textarea.encoded")
const $decoded = $("textarea.decoded")
const $encodeError = $(".encode.error")
const $decodeError = $(".decode.error")
function clear() {
$decodeError.textContent = ""
$encodeError.textContent = ""
$$("button").forEach(($button) => {
$button.classList.remove("active")
})
}
document.on("DOMContentLoaded", async (e) => {
$$(".decode").forEach(($decode) => $decode.on("click", (e) => {
try {
clear()
const encoded = $encoded.value
console.log(encoded)
location.hash = btoa(encoded)
const method = e.target.classList.item(1)
const decoded = sfv[`decode${method}`](encoded)
console.log(decoded)
$decoded.value = JSON.stringify(decoded, function(key, value) {
if (typeof value === "symbol") {
return value.toString()
}
return value
}, " ")
e.target.classList.add("active")
} catch(error) {
console.error(error)
$decodeError.textContent = error
}
}))
$$(".encode").forEach(($encode) => $encode.on("click", (e) => {
try {
clear()
const decoded = JSON.parse($decoded.value, function(k, v) {
if (k === "value" && typeof v === "string") {
const token = v.match(/^Symbol\((.*)\)$/)
if (token) {
v = Symbol.for(token[1])
}
}
return v
})
console.log(decoded)
const method = e.target.classList.item(1)
const encoded = sfv[`encode${method}`](decoded)
console.log(encoded)
$encoded.value = encoded
e.target.classList.add("active")
} catch(error) {
console.error(error)
$encodeError.textContent = error
}
}))
$$(".sample").forEach(($sample) => {
$sample.on("click", (e) => {
clear()
e.preventDefault()
const sample = e.target.textContent
$encoded.value = sample
$decoded.value = ""
})
})
if (location.hash === "") {
$encoded.value = $$(".sample.List")[1].textContent
$(".decode.List").click()
} else {
$encoded.value = atob(location.hash.substr(1))
}
})
</script>