|
1 | 1 | "use strict";
|
2 | 2 | (() => {
|
3 |
| - const urlInput = document.querySelector("#url"); |
4 |
| - const baseInput = document.querySelector("#base"); |
5 |
| - |
6 |
| - const te = new TextEncoder(); |
7 |
| - const td = new TextDecoder(); |
8 |
| - |
9 |
| - // Use an iframe to avoid <base> affecting the main page. This is especially bad in Edge where it |
10 |
| - // appears to break Edge's DevTools. |
11 |
| - const browserIframeDocument = document.querySelector("#browser-iframe").contentDocument; |
12 |
| - const browserAnchor = browserIframeDocument.createElement("a"); |
13 |
| - const browserBase = browserIframeDocument.createElement("base"); |
14 |
| - browserIframeDocument.head.appendChild(browserBase); |
15 |
| - browserIframeDocument.body.appendChild(browserAnchor); |
| 3 | + const messageInput = document.querySelector("#message"); |
16 | 4 |
|
17 | 5 | const components = [
|
18 |
| - "href", "protocol", "username", |
19 |
| - "password", "port", "hostname", |
20 |
| - "pathname", "search", "hash" |
| 6 | + "encoded", |
| 7 | + "decoded" |
21 | 8 | ];
|
22 | 9 |
|
23 |
| - urlInput.addEventListener("input", update); |
24 |
| - baseInput.addEventListener("input", update); |
25 |
| - window.addEventListener("hashchange", setFromFragment); |
26 |
| - setFromFragment(); |
| 10 | + messageInput.addEventListener("input", update); |
| 11 | + update(); |
27 | 12 |
|
28 | 13 | function update() {
|
29 | 14 | const browserResult = getBrowserResult();
|
|
32 | 17 |
|
33 | 18 | setResult("browser", browserResult, mismatchedComponents);
|
34 | 19 | setResult("jsdom", jsdomResult, mismatchedComponents);
|
35 |
| - updateFragmentForSharing(); |
36 | 20 | }
|
37 | 21 |
|
38 | 22 | function setResult(kind, result, mismatchedComponents) {
|
|
72 | 56 | function getMismatchedComponents(result1, result2) {
|
73 | 57 | const mismatched = new Set();
|
74 | 58 | for (const component of components) {
|
75 |
| - if (result1[component] !== result2[component]) { |
76 |
| - mismatched.add(component); |
| 59 | + if (typeof result1[component] === "string") { |
| 60 | + if (result1[component] !== result2[component]) { |
| 61 | + mismatched.add(component); |
| 62 | + } |
| 63 | + } |
| 64 | + if (result1[component] instanceof Uint8Array) { |
| 65 | + if (equalBuffer(result1[component], result2[component]) === false) { |
| 66 | + mismatched.add(component); |
| 67 | + } |
77 | 68 | }
|
78 | 69 | }
|
79 | 70 | return mismatched;
|
80 | 71 | }
|
81 | 72 |
|
82 | 73 | function getBrowserResult() {
|
83 |
| - // First make sure the base is not invalid by testing it against an about:blank base. |
84 |
| - browserBase.href = "about:blank"; |
85 |
| - browserAnchor.href = baseInput.value; |
86 |
| - if (browserAnchor.protocol === ":") { |
87 |
| - return new Error("Browser could not parse the base URL"); |
88 |
| - } |
89 |
| - |
90 |
| - // Now actually parse the URL against the base. |
91 |
| - browserAnchor.href = urlInput.value; |
92 |
| - browserBase.href = baseInput.value; |
93 |
| - if (browserAnchor.protocol === ":") { |
94 |
| - return new Error("Browser could not parse the input"); |
| 74 | + try { |
| 75 | + const encoder = new TextEncoder(); |
| 76 | + const decoder = new TextDecoder(); |
| 77 | + return { |
| 78 | + encoded: encoder.encode(messageInput.value), |
| 79 | + decoded: decoder.decode(encoder.encode(messageInput.value)) |
| 80 | + }; |
| 81 | + } catch (e) { |
| 82 | + return e; |
95 | 83 | }
|
96 |
| - |
97 |
| - return browserAnchor; |
98 | 84 | }
|
99 | 85 |
|
100 | 86 | function getJsdomResult() {
|
101 | 87 | try {
|
102 |
| - return new whatwgEncoding.TextEncoder(); |
| 88 | + const encoder = new whatwgEncoding.TextEncoder(); |
| 89 | + const decoder = new whatwgEncoding.TextDecoder(); |
| 90 | + return { |
| 91 | + encoded: encoder.encode(messageInput.value), |
| 92 | + decoded: decoder.decode(encoder.encode(messageInput.value)) |
| 93 | + }; |
103 | 94 | } catch (e) {
|
104 | 95 | return e;
|
105 | 96 | }
|
106 | 97 | }
|
107 | 98 |
|
108 |
| - function updateFragmentForSharing() { |
109 |
| - location.hash = `url=${encodeToBase64(urlInput.value)}&base=${encodeToBase64(baseInput.value)}`; |
110 |
| - } |
111 |
| - |
112 |
| - function setFromFragment() { |
113 |
| - const pieces = /#url=([^&]+)&base=(.*)/.exec(location.hash); |
114 |
| - if (!pieces) { |
115 |
| - return; |
| 99 | + function equalBuffer(buf1, buf2) { |
| 100 | + if (buf1.byteLength !== buf2.byteLength) { |
| 101 | + return false; |
116 | 102 | }
|
117 |
| - const [, urlEncoded, baseEncoded] = pieces; |
118 |
| - try { |
119 |
| - urlInput.value = decodeFromBase64(urlEncoded); |
120 |
| - } catch (e) { |
121 |
| - // eslint-disable-next-line no-console |
122 |
| - console.warn("url hash parameter was not deserializable."); |
123 |
| - } |
124 |
| - |
125 |
| - try { |
126 |
| - baseInput.value = decodeFromBase64(baseEncoded); |
127 |
| - } catch (e) { |
128 |
| - // eslint-disable-next-line no-console |
129 |
| - console.warn("base hash parameter was not deserializable."); |
| 103 | + for (let i = 0; i !== buf1.byteLength; i++) { |
| 104 | + if (buf1[i] !== buf2[i]) { |
| 105 | + return false; |
| 106 | + } |
130 | 107 | }
|
131 |
| - |
132 |
| - update(); |
| 108 | + return true; |
133 | 109 | }
|
134 | 110 |
|
135 |
| - // btoa / atob don't work on Unicode. |
136 |
| - // This version is a superset of btoa / atob, so it maintains compatibility with older versions of |
137 |
| - // the live viewer which used btoa / atob directly. |
138 |
| - function encodeToBase64(originalString) { |
139 |
| - const bytes = te.encode(originalString); |
140 |
| - const byteString = Array.from(bytes, byte => String.fromCharCode(byte)).join(""); |
141 |
| - const encoded = btoa(byteString); |
142 |
| - return encoded; |
143 |
| - } |
144 |
| - |
145 |
| - function decodeFromBase64(encoded) { |
146 |
| - const byteString = atob(encoded); |
147 |
| - const bytes = Uint8Array.from(byteString, char => char.charCodeAt(0)); |
148 |
| - const originalString = td.decode(bytes); |
149 |
| - return originalString; |
150 |
| - } |
151 | 111 | })();
|
0 commit comments