|
| 1 | +"use strict"; |
| 2 | +(() => { |
| 3 | + const urlInput = document.querySelector("#url"); |
| 4 | + const baseInput = document.querySelector("#base"); |
| 5 | + |
| 6 | + // Use an iframe to avoid <base> affecting the main page. This is especially bad in Edge where it |
| 7 | + // appears to break Edge's DevTools. |
| 8 | + const browserIframeDocument = document.querySelector("#browser-iframe").contentDocument; |
| 9 | + const browserAnchor = browserIframeDocument.createElement("a"); |
| 10 | + const browserBase = browserIframeDocument.createElement("base"); |
| 11 | + browserIframeDocument.head.appendChild(browserBase); |
| 12 | + browserIframeDocument.body.appendChild(browserAnchor); |
| 13 | + |
| 14 | + const components = [ |
| 15 | + "href", "protocol", "username", |
| 16 | + "password", "port", "hostname", |
| 17 | + "pathname", "search", "hash" |
| 18 | + ]; |
| 19 | + |
| 20 | + urlInput.addEventListener("input", update); |
| 21 | + baseInput.addEventListener("input", update); |
| 22 | + setFromFragment(); |
| 23 | + update(); |
| 24 | + |
| 25 | + function update() { |
| 26 | + const browserResult = getBrowserResult(); |
| 27 | + const jsdomResult = getJsdomResult(); |
| 28 | + const mismatchedComponents = getMismatchedComponents(browserResult, jsdomResult); |
| 29 | + |
| 30 | + setResult("browser", browserResult, mismatchedComponents); |
| 31 | + setResult("jsdom", jsdomResult, mismatchedComponents); |
| 32 | + updateFragmentForSharing(); |
| 33 | + } |
| 34 | + |
| 35 | + function setResult(kind, result, mismatchedComponents) { |
| 36 | + const output = document.querySelector(`#${kind}-output`); |
| 37 | + const error = document.querySelector(`#${kind}-error`); |
| 38 | + |
| 39 | + if (result instanceof Error) { |
| 40 | + output.hidden = true; |
| 41 | + error.hidden = false; |
| 42 | + error.textContent = result.toString(); |
| 43 | + } else { |
| 44 | + output.hidden = false; |
| 45 | + error.hidden = true; |
| 46 | + for (const component of components) { |
| 47 | + const componentEl = output.querySelector(`#${component}`).querySelector("td"); |
| 48 | + setComponentElValue(componentEl, result[component]); |
| 49 | + setComponentElMismatch(componentEl, mismatchedComponents.has(component)); |
| 50 | + } |
| 51 | + } |
| 52 | + } |
| 53 | + |
| 54 | + function setComponentElValue(componentEl, value) { |
| 55 | + const isEmptyString = value === ""; |
| 56 | + componentEl.textContent = isEmptyString ? "(empty string)" : value; |
| 57 | + componentEl.classList.toggle("empty-string", isEmptyString); |
| 58 | + } |
| 59 | + |
| 60 | + function setComponentElMismatch(componentEl, isMismatched) { |
| 61 | + componentEl.classList.toggle("pass", !isMismatched); |
| 62 | + componentEl.classList.toggle("fail", isMismatched); |
| 63 | + } |
| 64 | + |
| 65 | + function getMismatchedComponents(result1, result2) { |
| 66 | + const mismatched = new Set(); |
| 67 | + for (const component of components) { |
| 68 | + if (result1[component] !== result2[component]) { |
| 69 | + mismatched.add(component); |
| 70 | + } |
| 71 | + } |
| 72 | + return mismatched; |
| 73 | + } |
| 74 | + |
| 75 | + function getBrowserResult() { |
| 76 | + // First make sure the base is not invalid by testing it against an about:blank base. |
| 77 | + browserBase.href = "about:blank"; |
| 78 | + browserAnchor.href = baseInput.value; |
| 79 | + if (browserAnchor.protocol === ":") { |
| 80 | + return new Error("Browser could not parse the base URL"); |
| 81 | + } |
| 82 | + |
| 83 | + // Now actually parse the URL against the base. |
| 84 | + browserAnchor.href = urlInput.value; |
| 85 | + browserBase.href = baseInput.value; |
| 86 | + if (browserAnchor.protocol === ":") { |
| 87 | + return new Error("Browser could not parse the input"); |
| 88 | + } |
| 89 | + |
| 90 | + return browserAnchor; |
| 91 | + } |
| 92 | + |
| 93 | + function getJsdomResult() { |
| 94 | + try { |
| 95 | + return new whatwgURL.URL(urlInput.value, baseInput.value); |
| 96 | + } catch (e) { |
| 97 | + return e; |
| 98 | + } |
| 99 | + } |
| 100 | + |
| 101 | + function updateFragmentForSharing() { |
| 102 | + location.hash = `url=${btoa(urlInput.value)}&base=${btoa(baseInput.value)}`; |
| 103 | + } |
| 104 | + |
| 105 | + function setFromFragment() { |
| 106 | + const pieces = /#url=([^&]+)&base=(.*)/.exec(location.hash); |
| 107 | + if (!pieces) { |
| 108 | + return; |
| 109 | + } |
| 110 | + const [, urlEncoded, baseEncoded] = pieces; |
| 111 | + urlInput.value = atob(urlEncoded); |
| 112 | + baseInput.value = atob(baseEncoded); |
| 113 | + } |
| 114 | +})(); |
0 commit comments