From 0fc4b8429e9292ccba6bf2560decaa1efb8258b0 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Fri, 25 Feb 2022 13:57:50 +0100 Subject: [PATCH] Remove static files --- static/kx-claim.js | 220 ------------ static/kx-key.js | 85 ----- static/kx-styles.css | 254 -------------- static/scripts.js | 214 ------------ static/scripts.util.js | 276 --------------- static/styles.css | 741 ----------------------------------------- 6 files changed, 1790 deletions(-) delete mode 100644 static/kx-claim.js delete mode 100644 static/kx-key.js delete mode 100644 static/kx-styles.css delete mode 100644 static/scripts.js delete mode 100644 static/scripts.util.js delete mode 100644 static/styles.css diff --git a/static/kx-claim.js b/static/kx-claim.js deleted file mode 100644 index e08e941..0000000 --- a/static/kx-claim.js +++ /dev/null @@ -1,220 +0,0 @@ -/* -Copyright (C) 2021 Yarmo Mackenbach - -This program is free software: you can redistribute it and/or modify it under -the terms of the GNU Affero General Public License as published by the Free -Software Foundation, either version 3 of the License, or (at your option) -any later version. - -This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more -details. - -You should have received a copy of the GNU Affero General Public License along -with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - -If your software can interact with users remotely through a computer network, -you should also make sure that it provides a way for users to get its source. -For example, if your program is a web application, its interface could display -a "Source" link that leads users to an archive of the code. There are many -ways you could offer source, and different solutions will be better for different -programs; see section 13 for the specific requirements. - -You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. For -more information on this, and how to apply and follow the GNU AGPL, see . -*/ -class Claim extends HTMLElement { - // Specify the attributes to observe - static get observedAttributes() { - return ['data-claim']; - } - - constructor() { - // Call super - super(); - } - - attributeChangedCallback(name, oldValue, newValue) { - this.updateContent(newValue); - } - - async verify() { - const claim = new doip.Claim(JSON.parse(this.getAttribute('data-claim'))); - await claim.verify({ - proxy: { - policy: 'adaptive', - hostname: 'PLACEHOLDER__PROXY_HOSTNAME' - } - }); - this.setAttribute('data-claim', JSON.stringify(claim)); - } - - updateContent(value) { - const root = this; - const claim = new doip.Claim(JSON.parse(value)); - - switch (claim.matches[0].serviceprovider.name) { - case 'dns': - case 'xmpp': - case 'irc': - root.querySelector('.info .subtitle').innerText = claim.matches[0].serviceprovider.name.toUpperCase(); - break; - - default: - root.querySelector('.info .subtitle').innerText = claim.matches[0].serviceprovider.name; - break; - } - root.querySelector('.info .title').innerText = claim.matches[0].profile.display; - - try { - if (claim.status === 'verified') { - root.querySelector('.icons .verificationStatus').setAttribute('data-value', claim.verification.result ? 'success' : 'failed'); - } else { - root.querySelector('.icons .verificationStatus').setAttribute('data-value', 'running'); - } - } catch (error) { - root.querySelector('.icons .verificationStatus').setAttribute('data-value', 'failed'); - } - - const elContent = root.querySelector('.content'); - elContent.innerHTML = ``; - - // Handle failed ambiguous claim - if (claim.status === 'verified' && !claim.verification.result && claim.isAmbiguous()) { - root.querySelector('.info .subtitle').innerText = '---'; - - const subsection_alert = elContent.appendChild(document.createElement('div')); - subsection_alert.setAttribute('class', 'subsection'); - const subsection_alert_icon = subsection_alert.appendChild(document.createElement('img')); - subsection_alert_icon.setAttribute('src', '/static/img/alert-decagram.png'); - subsection_alert_icon.setAttribute('alt', ''); - subsection_alert_icon.setAttribute('aria-hidden', 'true'); - const subsection_alert_text = subsection_alert.appendChild(document.createElement('div')); - - const message = subsection_alert_text.appendChild(document.createElement('p')); - message.innerHTML = `None of the matched service providers could be verified. Keyoxide was not able to determine which was the correct service provider or why the verification process failed.`; - return; - } - - // Links to profile and proof - const subsection_links = elContent.appendChild(document.createElement('div')); - subsection_links.setAttribute('class', 'subsection'); - const subsection_links_icon = subsection_links.appendChild(document.createElement('img')); - subsection_links_icon.setAttribute('src', '/static/img/link.png'); - subsection_links_icon.setAttribute('alt', ''); - subsection_links_icon.setAttribute('aria-hidden', 'true'); - const subsection_links_text = subsection_links.appendChild(document.createElement('div')); - - const profile_link = subsection_links_text.appendChild(document.createElement('p')); - if (claim.matches[0].profile.uri) { - profile_link.innerHTML = `Profile link: ${claim.matches[0].profile.uri}`; - } else { - profile_link.innerHTML = `Profile link: not accessible from browser`; - } - - const proof_link = subsection_links_text.appendChild(document.createElement('p')); - if (claim.matches[0].proof.uri) { - proof_link.innerHTML = `Proof link: ${claim.matches[0].proof.uri}`; - } else { - proof_link.innerHTML = `Proof link: not accessible from browser`; - } - - // QR Code - if (claim.matches[0].profile.qr) { - elContent.appendChild(document.createElement('hr')); - - const subsection_qr = elContent.appendChild(document.createElement('div')); - subsection_qr.setAttribute('class', 'subsection'); - const subsection_qr_icon = subsection_qr.appendChild(document.createElement('img')); - subsection_qr_icon.setAttribute('src', '/static/img/qrcode.png'); - subsection_qr_icon.setAttribute('alt', ''); - subsection_qr_icon.setAttribute('aria-hidden', 'true'); - const subsection_qr_text = subsection_qr.appendChild(document.createElement('div')); - - const button_profileQR = subsection_qr_text.appendChild(document.createElement('button')); - button_profileQR.innerText = `Show profile QR`; - button_profileQR.setAttribute('onClick', `showQR('${claim.matches[0].profile.qr}', 'url')`); - button_profileQR.setAttribute('aria-label', `Show QR code linking to profile`); - } - - elContent.appendChild(document.createElement('hr')); - - // Claim verification status - const subsection_status = elContent.appendChild(document.createElement('div')); - subsection_status.setAttribute('class', 'subsection'); - const subsection_status_icon = subsection_status.appendChild(document.createElement('img')); - subsection_status_icon.setAttribute('src', '/static/img/decagram.png'); - subsection_status_icon.setAttribute('alt', ''); - subsection_status_icon.setAttribute('aria-hidden', 'true'); - const subsection_status_text = subsection_status.appendChild(document.createElement('div')); - - const verification = subsection_status_text.appendChild(document.createElement('p')); - if (claim.status === 'verified') { - verification.innerHTML = `Claim verification has completed.`; - subsection_status_icon.setAttribute('src', '/static/img/check-decagram.png'); - subsection_status_icon.setAttribute('alt', ''); - subsection_status_icon.setAttribute('aria-hidden', 'true'); - } else { - verification.innerHTML = `Claim verification is in progress…`; - return; - } - - elContent.appendChild(document.createElement('hr')); - - // Result of claim verification - const subsection_result = elContent.appendChild(document.createElement('div')); - subsection_result.setAttribute('class', 'subsection'); - const subsection_result_icon = subsection_result.appendChild(document.createElement('img')); - subsection_result_icon.setAttribute('src', '/static/img/shield-search.png'); - subsection_result_icon.setAttribute('alt', ''); - subsection_result_icon.setAttribute('aria-hidden', 'true'); - const subsection_result_text = subsection_result.appendChild(document.createElement('div')); - - const result = subsection_result_text.appendChild(document.createElement('p')); - result.innerHTML = `The claim ${claim.verification.result ? 'HAS BEEN' : 'COULD NOT BE'} verified by the proof.`; - - // Additional info - if (claim.verification.proof.viaProxy) { - elContent.appendChild(document.createElement('hr')); - - const subsection_info = elContent.appendChild(document.createElement('div')); - subsection_info.setAttribute('class', 'subsection'); - const subsection_info_icon = subsection_info.appendChild(document.createElement('img')); - subsection_info_icon.setAttribute('src', '/static/img/information.png'); - subsection_info_icon.setAttribute('alt', ''); - subsection_info_icon.setAttribute('aria-hidden', 'true'); - const subsection_info_text = subsection_info.appendChild(document.createElement('div')); - - const result_proxyUsed = subsection_info_text.appendChild(document.createElement('p')); - result_proxyUsed.innerHTML = `A proxy was used to fetch the proof: PLACEHOLDER__PROXY_HOSTNAME`; - } - - // TODO Display errors - // if (claim.verification.errors.length > 0) { - // console.log(claim.verification); - // elContent.appendChild(document.createElement('hr')); - - // const subsection_errors = elContent.appendChild(document.createElement('div')); - // subsection_errors.setAttribute('class', 'subsection'); - // const subsection_errors_icon = subsection_errors.appendChild(document.createElement('img')); - // subsection_errors_icon.setAttribute('src', '/static/img/alert-circle.png'); - // const subsection_errors_text = subsection_errors.appendChild(document.createElement('div')); - - // claim.verification.errors.forEach(message => { - // const error = subsection_errors_text.appendChild(document.createElement('p')); - - // if (message instanceof Error) { - // error.innerText = message.message; - // } else { - // error.innerText = message; - // } - // }); - // } - } -} - -customElements.define('kx-claim', Claim); diff --git a/static/kx-key.js b/static/kx-key.js deleted file mode 100644 index 6290dac..0000000 --- a/static/kx-key.js +++ /dev/null @@ -1,85 +0,0 @@ -/* -Copyright (C) 2021 Yarmo Mackenbach - -This program is free software: you can redistribute it and/or modify it under -the terms of the GNU Affero General Public License as published by the Free -Software Foundation, either version 3 of the License, or (at your option) -any later version. - -This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more -details. - -You should have received a copy of the GNU Affero General Public License along -with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - -If your software can interact with users remotely through a computer network, -you should also make sure that it provides a way for users to get its source. -For example, if your program is a web application, its interface could display -a "Source" link that leads users to an archive of the code. There are many -ways you could offer source, and different solutions will be better for different -programs; see section 13 for the specific requirements. - -You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. For -more information on this, and how to apply and follow the GNU AGPL, see . -*/ -class Key extends HTMLElement { - // Specify the attributes to observe - static get observedAttributes() { - return ['data-keydata']; - } - - constructor() { - // Call super - super(); - } - - attributeChangedCallback(name, oldValue, newValue) { - this.updateContent(newValue); - } - - updateContent(value) { - const root = this; - const data = JSON.parse(value); - - root.querySelector('.info .subtitle').innerText = data.key.fetchMethod; - root.querySelector('.info .title').innerText = data.fingerprint; - - const elContent = root.querySelector('.content'); - elContent.innerHTML = ``; - - // Link to key - const subsection_links = elContent.appendChild(document.createElement('div')); - subsection_links.setAttribute('class', 'subsection'); - const subsection_links_icon = subsection_links.appendChild(document.createElement('img')); - subsection_links_icon.setAttribute('src', '/static/img/link.png'); - subsection_links_icon.setAttribute('alt', ''); - subsection_links_icon.setAttribute('aria-hidden', 'true'); - const subsection_links_text = subsection_links.appendChild(document.createElement('div')); - - const profile_link = subsection_links_text.appendChild(document.createElement('p')); - profile_link.innerHTML = `Key link: ${data.key.uri}`; - - elContent.appendChild(document.createElement('hr')); - - // QR Code - const subsection_qr = elContent.appendChild(document.createElement('div')); - subsection_qr.setAttribute('class', 'subsection'); - const subsection_qr_icon = subsection_qr.appendChild(document.createElement('img')); - subsection_qr_icon.setAttribute('src', '/static/img/qrcode.png'); - subsection_qr_icon.setAttribute('alt', ''); - subsection_qr_icon.setAttribute('aria-hidden', 'true'); - const subsection_qr_text = subsection_qr.appendChild(document.createElement('div')); - - const button_fingerprintQR = subsection_qr_text.appendChild(document.createElement('button')); - button_fingerprintQR.innerText = `Show OpenPGP fingerprint QR`; - button_fingerprintQR.setAttribute('onClick', `showQR('${data.fingerprint}', 'fingerprint')`); - button_fingerprintQR.setAttribute('aria-label', `Show QR code for cryptographic fingerprint`); - } -} - -customElements.define('kx-key', Key); diff --git a/static/kx-styles.css b/static/kx-styles.css deleted file mode 100644 index 0210c52..0000000 --- a/static/kx-styles.css +++ /dev/null @@ -1,254 +0,0 @@ -/* -Copyright (C) 2021 Yarmo Mackenbach - -This program is free software: you can redistribute it and/or modify it under -the terms of the GNU Affero General Public License as published by the Free -Software Foundation, either version 3 of the License, or (at your option) -any later version. - -This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more -details. - -You should have received a copy of the GNU Affero General Public License along -with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - -If your software can interact with users remotely through a computer network, -you should also make sure that it provides a way for users to get its source. -For example, if your program is a web application, its interface could display -a "Source" link that leads users to an archive of the code. There are many -ways you could offer source, and different solutions will be better for different -programs; see section 13 for the specific requirements. - -You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. For -more information on this, and how to apply and follow the GNU AGPL, see . -*/ -* { - box-sizing: border-box; -} -details.kx-item { - width: 100%; - border-radius: 8px; -} -details.kx-item p { - margin: 0; - word-break: break-word; -} -details.kx-item a { - color: var(--blue-700); -} -details.kx-item hr { - border: none; - border-top: 2px solid var(--purple-100); -} -details.kx-item .content { - padding: 12px; - border: solid 3px var(--purple-100); - border-top: 0px; - border-radius: 0px 0px 8px 8px; -} -details.kx-item summary { - display: flex; - align-items: center; - padding: 8px 12px; - background-color: var(--purple-100); - border: solid 3px var(--purple-100); - border-radius: 8px; - list-style: none; - cursor: pointer; -} -details.kx-item summary::-webkit-details-marker { - display: none; -} -details.kx-item summary:hover, summary:focus { - border-color: var(--purple-400); -} -details[open] summary { - border-radius: 8px 8px 0px 0px; -} -details.kx-item summary .info { - flex: 1; -} -details.kx-item summary .info .title { - font-size: 1.1em; -} -details.kx-item summary .claim__description p { - font-size: 1.4rem; - line-height: 2rem; -} -details.kx-item summary .claim__links p, p.subtle-links { - display: flex; - align-items: center; - flex-wrap: wrap; - font-size: 1rem; - color: var(--grey-700); -} -details.kx-item summary .claim__links a, summary .claim__links span, p.subtle-links a { - font-size: 1rem; - margin: 0 10px 0 0; - color: var(--grey-700); -} -details.kx-item summary .subtitle { - color: var(--purple-700); -} -details.kx-item summary .verificationStatus { - position: relative; - display: flex; - align-items: center; - justify-content: center; - width: 48px; - height: 48px; - border-radius: 100%; - color: #fff; - font-size: 2rem; - user-select: none; -} -details.kx-item summary .verificationStatus::after { - position: absolute; - display: flex; - top: 0; - left: 0; - right: 0; - bottom: 0; - align-items: center; - justify-content: center; -} -details.kx-item summary .verificationStatus .inProgress { - opacity: 0; - transition: opacity 0.4s ease; - pointer-events: none; -} -details.kx-item summary .verificationStatus[data-value="success"] { - content: "v"; - background-color: var(--green-600); -} -details.kx-item summary .verificationStatus[data-value="success"]::after { - content: "✔"; -} -details.kx-item summary .verificationStatus[data-value="failed"] { - background-color: var(--red-400); -} -details.kx-item summary .verificationStatus[data-value="failed"]::after { - content: "✕"; -} -details.kx-item summary .verificationStatus[data-value="running"] .inProgress { - opacity: 1; -} - -details.kx-item .subsection { - display: flex; - align-items: center; - gap: 16px; -} -details.kx-item .subsection > img { - width: 24px; - height: 24px; - opacity: 0.4; -} - -details.kx-item .inProgress { - font-size: 10px; - margin: 50px auto; - text-indent: -9999em; - width: 48px; - height: 48px; - border-radius: 50%; - background: var(--purple-400); - background: -moz-linear-gradient(left, var(--purple-400) 10%, rgba(255, 255, 255, 0) 42%); - background: -webkit-linear-gradient(left, var(--purple-400) 10%, rgba(255, 255, 255, 0) 42%); - background: -o-linear-gradient(left, var(--purple-400) 10%, rgba(255, 255, 255, 0) 42%); - background: -ms-linear-gradient(left, var(--purple-400) 10%, rgba(255, 255, 255, 0) 42%); - background: linear-gradient(to right, var(--purple-400) 10%, rgba(255, 255, 255, 0) 42%); - position: relative; - -webkit-animation: load3 1.4s infinite linear; - animation: load3 1.4s infinite linear; - -webkit-transform: translateZ(0); - -ms-transform: translateZ(0); - transform: translateZ(0); -} -details.kx-item .inProgress:before { - width: 50%; - height: 50%; - background: var(--purple-400); - border-radius: 100% 0 0 0; - position: absolute; - top: 0; - left: 0; - content: ''; -} -details.kx-item .inProgress:after { - background: var(--purple-100); - width: 65%; - height: 65%; - border-radius: 50%; - content: ''; - margin: auto; - position: absolute; - top: 0; - left: 0; - bottom: 0; - right: 0; -} -details.kx-item button { - padding: 0.4rem 0.8rem; - margin-right: 8px; - text-decoration: none; - text-transform: uppercase; - background-color: #fff; - border: solid 2px var(--purple-400); - border-radius: 4px; - cursor: pointer; -} -details.kx-item button:hover { - background-color: var(--purple-500); - border-color: var(--purple-500); - color: #fff; -} - -@media screen and (max-width: 640px) { - details.kx-item summary .claim__description p { - font-size: 1.2rem; - } - details.kx-item summary .claim__links a, p.subtle-links a { - font-size: 0.9rem; - } -} -@media screen and (max-width: 480px) { - summary .claim__description p { - font-size: 1rem; - } - details.kx-item summary .verificationStatus { - width: 36px; - height: 36px; - font-size: 1.6rem; - } - details.kx-item .inProgress { - width: 36px; - height: 36px; - } -} - -@-webkit-keyframes load3 { - 0% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } - 100% { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); - } -} -@keyframes load3 { - 0% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } - 100% { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); - } -} diff --git a/static/scripts.js b/static/scripts.js deleted file mode 100644 index 9c5a418..0000000 --- a/static/scripts.js +++ /dev/null @@ -1,214 +0,0 @@ -/* -Copyright (C) 2021 Yarmo Mackenbach - -This program is free software: you can redistribute it and/or modify it under -the terms of the GNU Affero General Public License as published by the Free -Software Foundation, either version 3 of the License, or (at your option) -any later version. - -This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more -details. - -You should have received a copy of the GNU Affero General Public License along -with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - -If your software can interact with users remotely through a computer network, -you should also make sure that it provides a way for users to get its source. -For example, if your program is a web application, its interface could display -a "Source" link that leads users to an archive of the code. There are many -ways you could offer source, and different solutions will be better for different -programs; see section 13 for the specific requirements. - -You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. For -more information on this, and how to apply and follow the GNU AGPL, see . -*/ -// Verify all claims -const claims = document.querySelectorAll('kx-claim'); -claims.forEach(function(claim) { - if (claim.hasAttribute('data-skip') && claim.getAttribute('data-skip')) { - return; - } - claim.verify(); -}); - -// Register modals -document.querySelectorAll('dialog').forEach(function(d) { - dialogPolyfill.registerDialog(d); - d.addEventListener('click', function(ev) { - if (ev && ev.target != d) { - return; - } - d.close(); - }); -}); - -// Register form listeners -const elFormEncrypt = document.body.querySelector("#dialog--encryptMessage form"); -if (elFormEncrypt) { - elFormEncrypt.onsubmit = async function (evt) { - evt.preventDefault(); - - try { - // Fetch a key if needed - await fetchProfileKey(); - - // Encrypt the message - let config = openpgp.config; - config.show_comment = false; - config.show_version = false; - - encrypted = await openpgp.encrypt({ - message: openpgp.message.fromText(elFormEncrypt.querySelector('.input').value), - publicKeys: window.kx.key.object, - config: config - }); - elFormEncrypt.querySelector('.output').value = encrypted.data; - } catch (e) { - console.error(e); - elFormEncrypt.querySelector('.output').value = `Could not encrypt message!\n==========================\n${e.message ? e.message : e}`; - } - } -} - -const elFormVerify = document.body.querySelector("#dialog--verifySignature form"); -if (elFormVerify) { - elFormVerify.onsubmit = async function (evt) { - evt.preventDefault(); - - try { - // Fetch a key if needed - await fetchProfileKey(); - - // Try two different methods of signature reading - let signature = null, verified = null, readError = null; - try { - signature = await openpgp.message.readArmored(elFormVerify.querySelector('.input').value); - } catch(e) { - readError = e; - } - try { - signature = await openpgp.cleartext.readArmored(elFormVerify.querySelector('.input').value); - } catch(e) { - readError = e; - } - if (signature == null) { throw(readError) }; - - // Verify the signature - verified = await openpgp.verify({ - message: signature, - publicKeys: window.kx.key.object - }); - - if (verified.signatures[0].valid) { - elFormVerify.querySelector('.output').value = `The message was signed by the profile's key.`; - } else { - elFormVerify.querySelector('.output').value = `The message was NOT signed by the profile's key.`; - } - } catch (e) { - console.error(e); - elFormVerify.querySelector('.output').value = `Could not verify signature!\n===========================\n${e.message ? e.message : e}`; - } - } -} - -const elFormSearch = document.body.querySelector("#search"); -if (elFormSearch) { - elFormSearch.onsubmit = function (evt) { - evt.preventDefault(); - - const protocol = elFormSearch.querySelector("input[type='radio']:checked").value; - const identifier = elFormSearch.querySelector("input[type='search']").value; - - if (protocol == 'sig') { - window.location.href = `/${protocol}`; - } else { - window.location.href = `/${protocol}/${encodeURIComponent(identifier)}`; - } - } - - const elSearchRadio = elFormSearch.querySelectorAll("input[type='radio']"); - elSearchRadio.forEach(function (el) { - el.oninput = function (evt) { - evt.preventDefault(); - - if (evt.target.getAttribute('id') === 'protocol-sig') { - elFormSearch.querySelector("input[type='search']").setAttribute('disabled', true); - } else { - elFormSearch.querySelector("input[type='search']").removeAttribute('disabled'); - } - } - }); - elFormSearch.querySelector("input[type='radio']:checked").dispatchEvent(new Event('input')); -} - -// Functions -const fetchProfileKey = async function() { - if (window.kx.key.object && window.kx.key.object instanceof openpgp.key.Key) { - return; - } - - const rawKeyData = await fetch(window.kx.key.url) - let key, errorMsg - - try { - key = (await openpgp.key.read(new Uint8Array(await rawKeyData.clone().arrayBuffer()))).keys[0] - } catch(error) { - errorMsg = error.message - } - - if (!key) { - try { - key = (await openpgp.key.readArmored(await rawKeyData.clone().text())).keys[0] - } catch (error) { - errorMsg = error.message - } - } - - if (key) { - window.kx.key.object = key - return - } else { - throw new Error(`Public key could not be fetched (${errorMsg})`) - } -} - -// Enable QR modal -const showQR = function(input, type) { - const qrTarget = document.getElementById('qr'); - const qrContext = qrTarget.getContext('2d'); - const qrOpts = { - errorCorrectionLevel: 'L', - margin: 1, - width: 256, - height: 256 - }; - - if (input) { - if (type === 'url') { - input = decodeURIComponent(input); - } - if (type === 'fingerprint') { - input = `OPENPGP4FPR:${input.toUpperCase()}` - } - - QRCode.toCanvas(qrTarget, input, qrOpts, function(error) { - if (error) { - document.querySelector("#qr--altLink").innerText = ""; - document.querySelector("#qr--altLink").href = "#"; - qrContext.clearRect(0, 0, qrTarget.width, qrTarget.height); - console.error(error); - } else { - document.querySelector("#qr--altLink").innerText = input; - document.querySelector("#qr--altLink").href = input; - document.querySelector('#dialog--qr').showModal(); - } - }); - } else { - qrContext.clearRect(0, 0, qrTarget.width, qrTarget.height); - } -} diff --git a/static/scripts.util.js b/static/scripts.util.js deleted file mode 100644 index 8a1ed48..0000000 --- a/static/scripts.util.js +++ /dev/null @@ -1,276 +0,0 @@ -/* -Copyright (C) 2021 Yarmo Mackenbach - -This program is free software: you can redistribute it and/or modify it under -the terms of the GNU Affero General Public License as published by the Free -Software Foundation, either version 3 of the License, or (at your option) -any later version. - -This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more -details. - -You should have received a copy of the GNU Affero General Public License along -with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - -If your software can interact with users remotely through a computer network, -you should also make sure that it provides a way for users to get its source. -For example, if your program is a web application, its interface could display -a "Source" link that leads users to an archive of the code. There are many -ways you could offer source, and different solutions will be better for different -programs; see section 13 for the specific requirements. - -You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. For -more information on this, and how to apply and follow the GNU AGPL, see . -*/ -async function computeWKDLocalPart(message) { - const data = openpgp.util.str_to_Uint8Array(message.toLowerCase()); - const hash = await openpgp.crypto.hash.sha1(data); - return openpgp.util.encodeZBase32(hash); -} -async function generateProfileURL(data) { - let hostname = window.location.hostname; - - if (data.input == "") { - return "Waiting for input..."; - } - switch (data.source) { - case "wkd": - return `https://${hostname}/${data.input}`; - break; - case "hkp": - if (/.*@.*\..*/.test(data.input)) { - return `https://${hostname}/hkp/${data.input}`; - } else { - return `https://${hostname}/${data.input}`; - } - break; - case "keybase": - const re = /https\:\/\/keybase.io\/(.*)\/pgp_keys\.asc\?fingerprint\=(.*)/; - if (!re.test(data.input)) { - return "Incorrect Keybase public key URL."; - } - const match = data.input.match(re); - return `https://${hostname}/keybase/${match[1]}/${match[2]}`; - break; - } -} - -let elFormSignatureProfile = document.body.querySelector("#formGenerateSignatureProfile"), - elProfileUid = document.body.querySelector("#profileUid"), - elProfileMode = document.body.querySelector("#profileMode"), - elProfileServer = document.body.querySelector("#profileServer"), - elModeSelect = document.body.querySelector("#modeSelect"), - elUtilWKD = document.body.querySelector("#form-util-wkd"), - elUtilQRFP = document.body.querySelector("#form-util-qrfp"), - elUtilQR = document.body.querySelector("#form-util-qr"), - elUtilProfileURL = document.body.querySelector("#form-util-profile-url"); - -if (elModeSelect) { - elModeSelect.onchange = function (evt) { - let elAllModes = document.body.querySelectorAll('.modes'); - elAllModes.forEach(function(el) { - el.classList.remove('modes--visible'); - }); - document.body.querySelector(`.modes--${elModeSelect.value}`).classList.add('modes--visible'); - } - elModeSelect.dispatchEvent(new Event("change")); -} - -if (elProfileUid) { - let opts, profileUid = elProfileUid.innerHTML; - switch (elProfileMode.innerHTML) { - default: - case "sig": - elFormSignatureProfile.onsubmit = function (evt) { - evt.preventDefault(); - - opts = { - input: document.body.querySelector("#plaintext_input").value, - mode: elProfileMode.innerHTML - } - - displayProfile(opts) - } - break; - - case "auto": - if (/.*@.*/.test(profileUid)) { - // Match email for wkd - opts = { - input: profileUid, - mode: "wkd" - } - } else { - // Match fingerprint for hkp - opts = { - input: profileUid, - mode: "hkp" - } - } - break; - - case "hkp": - opts = { - input: profileUid, - server: elProfileServer.innerHTML, - mode: elProfileMode.innerHTML - } - break; - - case "wkd": - opts = { - input: profileUid, - mode: elProfileMode.innerHTML - } - break; - - case "keybase": - let match = profileUid.match(/(.*)\/(.*)/); - opts = { - username: match[1], - fingerprint: match[2], - mode: elProfileMode.innerHTML - } - break; - } - - if (elProfileMode.innerHTML !== 'sig') { - keyoxide.displayProfile(opts); - } -} - -if (elUtilWKD) { - elUtilWKD.onsubmit = function (evt) { - evt.preventDefault(); - } - - const elInput = document.body.querySelector("#input"); - const elOutput = document.body.querySelector("#output"); - const elOutputDirect = document.body.querySelector("#output_url_direct"); - const elOutputAdvanced = document.body.querySelector("#output_url_advanced"); - let match; - - elInput.addEventListener("input", async function(evt) { - if (evt.target.value) { - if (/(.*)@(.{1,}\..{1,})/.test(evt.target.value)) { - match = evt.target.value.match(/(.*)@(.*)/); - elOutput.innerText = await computeWKDLocalPart(match[1]); - elOutputDirect.innerText = `https://${match[2]}/.well-known/openpgpkey/hu/${elOutput.innerText}?l=${match[1]}`; - elOutputAdvanced.innerText = `https://openpgpkey.${match[2]}/.well-known/openpgpkey/${match[2]}/hu/${elOutput.innerText}?l=${match[1]}`; - } else { - elOutput.innerText = await computeWKDLocalPart(evt.target.value); - elOutputDirect.innerText = "Waiting for input"; - elOutputAdvanced.innerText = "Waiting for input"; - } - } else { - elOutput.innerText = "Waiting for input"; - elOutputDirect.innerText = "Waiting for input"; - elOutputAdvanced.innerText = "Waiting for input"; - } - }); - - elInput.dispatchEvent(new Event("input")); -} - -if (elUtilQRFP) { - elUtilQRFP.onsubmit = function (evt) { - evt.preventDefault(); - } - - const qrTarget = document.getElementById('qrcode'); - const qrContext = qrTarget.getContext('2d'); - const qrOpts = { - errorCorrectionLevel: 'H', - margin: 1, - width: 256, - height: 256 - }; - - const elInput = document.body.querySelector("#input"); - - elInput.addEventListener("input", async function(evt) { - if (evt.target.value) { - QRCode.toCanvas(qrTarget, evt.target.value, qrOpts, function (error) { - if (error) { - qrContext.clearRect(0, 0, qrTarget.width, qrTarget.height); - console.error(error); - } - }); - } else { - qrContext.clearRect(0, 0, qrTarget.width, qrTarget.height); - } - }); - - elInput.dispatchEvent(new Event("input")); -} - -if (elUtilQR) { - elUtilQR.onsubmit = function (evt) { - evt.preventDefault(); - } - - const qrTarget = document.getElementById('qrcode'); - const qrContext = qrTarget.getContext('2d'); - const qrOpts = { - errorCorrectionLevel: 'L', - margin: 1, - width: 256, - height: 256 - }; - - const elInput = document.body.querySelector("#input"); - - if (elInput.innerText) { - elInput.innerText = decodeURIComponent(elInput.innerText); - - QRCode.toCanvas(qrTarget, elInput.innerText, qrOpts, function (error) { - if (error) { - document.body.querySelector("#qrcode--altLink").href = "#"; - qrContext.clearRect(0, 0, qrTarget.width, qrTarget.height); - console.error(error); - } else { - document.body.querySelector("#qrcode--altLink").href = elInput.innerText; - } - }); - } else { - qrContext.clearRect(0, 0, qrTarget.width, qrTarget.height); - } -} - -if (elUtilProfileURL) { - elUtilProfileURL.onsubmit = function (evt) { - evt.preventDefault(); - } - - const elInput = document.body.querySelector("#input"), - elSource = document.body.querySelector("#source"), - elOutput = document.body.querySelector("#output"); - - let data = { - input: elInput.value, - source: elSource.value - }; - - elInput.addEventListener("input", async function(evt) { - data = { - input: elInput.value, - source: elSource.value - }; - elOutput.innerText = await generateProfileURL(data); - }); - - elSource.addEventListener("input", async function(evt) { - data = { - input: elInput.value, - source: elSource.value - }; - elOutput.innerText = await generateProfileURL(data); - }); - - elInput.dispatchEvent(new Event("input")); -} diff --git a/static/styles.css b/static/styles.css deleted file mode 100644 index 279c7e1..0000000 --- a/static/styles.css +++ /dev/null @@ -1,741 +0,0 @@ -/* -Copyright (C) 2021 Yarmo Mackenbach - -This program is free software: you can redistribute it and/or modify it under -the terms of the GNU Affero General Public License as published by the Free -Software Foundation, either version 3 of the License, or (at your option) -any later version. - -This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more -details. - -You should have received a copy of the GNU Affero General Public License along -with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - -If your software can interact with users remotely through a computer network, -you should also make sure that it provides a way for users to get its source. -For example, if your program is a web application, its interface could display -a "Source" link that leads users to an archive of the code. There are many -ways you could offer source, and different solutions will be better for different -programs; see section 13 for the specific requirements. - -You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. For -more information on this, and how to apply and follow the GNU AGPL, see . -*/ -:root { - --grey-500: hsl(0, 0%, 50%); - --grey-600: hsl(0, 0%, 40%); - --grey-700: hsl(0, 0%, 30%); - --grey-900: hsl(0, 0%, 10%); - --green-300: hsl(110, 45%, 70%); - --green-400: hsl(110, 45%, 60%); - --green-600: hsl(110, 45%, 40%); - --red-400: hsl(10, 60%, 60%); - --blue-500: hsl(201, 80%, 59%); - --blue-700: hsl(201, 90%, 30%); - --purple-50: hsl(250, 30%, 98%); - --purple-100: hsl(250, 48%, 95%); - --purple-200: hsl(250, 48%, 90%); - --purple-300: hsl(250, 48%, 85%); - --purple-400: hsl(250, 48%, 70%); - --purple-500: hsl(250, 48%, 65%); - --purple-600: hsl(250, 48%, 60%); - --purple-700: hsl(250, 48%, 55%); - --purple-900: hsl(250, 38%, 45%); - --yellow-100: hsl(56, 100%, 95%); - --yellow-200: hsl(56, 100%, 90%); - --yellow-500: hsl(56, 100%, 65%); -} - -* { - box-sizing: border-box; -} -:focus { - outline: none; - box-shadow: 0 0 0 3px lightskyblue; -} -input:focus, textarea:focus { - background: azure; -} -input[type="radio"]:focus + label { - box-shadow: 0 0 0 3px lightskyblue; - background: azure !important; - color: var(--grey-900) !important; -} -body { - display: flex; - flex-direction: column; - min-height: 100vh; - margin: 0; - padding: 1.6rem 0 0; - line-height: 1.4rem; - font-family: sans-serif; - color: var(--grey-900); -} - -/* HELPERS */ -.spacer { - flex: 1; -} -.no-margin { - margin: 0 !important; -} -.full-width { - display: block; - width: 100% !important; -} - -/* LAYOUT */ -header { - margin: 0 1.6rem 1.6rem; -} -header nav { - flex: 1; - display: flex; - flex-wrap: wrap; - align-items: center; - justify-content: center; - gap: 8px; -} -header nav a.logo { - width: 64px; - height: 64px; - font-size: 1.6rem; - text-transform: uppercase; - text-decoration: none; - color: var(--purple-700); -} -header nav a.logo img { - width: 100%; -} -nav a.text { - /* font-size: 0.9em; */ - margin: 0; - padding: 0.5em 1em; - text-transform: uppercase; - text-decoration: none; - color: var(--purple-700); - border-radius: 4px; -} -nav a.text:hover, nav a.text:active { - color: #fff; - background-color: var(--purple-500); -} -main { - flex: 1; - margin: 0 1.6rem; -} -footer { - margin: 4.8rem 0 0; - padding: 0 1.6rem 1.6rem; - background-color: var(--purple-900); - color: var(--purple-200); -} - -.container { - width: 100%; - max-width: 720px; - margin: 0 auto; -} -section.profile p, .demo p { - font-size: 1.2rem; -} -.demo { - margin: 4.8rem auto; -} - -.card { - margin: 0 0 1.6rem; - padding: 0 1.2rem; - background-color: #fff; - background-color: var(--purple-50); - border: 2px solid var(--purple-200); - border-radius: 4px; -} -.card.card--transparent { - padding-left: 0; - padding-right: 0; - background-color: transparent; - border: 0; -} -.card--profileHeader { - display: flex; - flex-direction: column; - flex-wrap: wrap; - align-items: center; - gap: 24px; -} -.card--profileHeader p, .card--profileHeader small { - margin: 0; -} -.card--small-profile { - display: flex; - flex-direction: column; - text-align: center; -} -.card--small-profile-dummy { - opacity: 0.5; - border: 0; -} -.card--small-profile .name { - font-size: 1.4em; -} -.card--small-profile p { - margin-top: 0; -} -.card--small-profile p span.fingerprint { - display: inline-block; - width: 100%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-size: 0.8rem; -} -#profileName { - font-size: 1.6rem; - color: var(--grey-700); -} -#profileURLFingerprint { - font-size: 1rem; - margin: 0 0 1.2rem; -} - -.hcards { - display: grid; - grid-gap: 1.2rem; - grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); - margin-bottom: 1.6rem; -} -.hcards .card { - margin: 0; -} -.hcards--col-1-2, .hcards--col-2-1 { - grid-template-columns: repeat(auto-fit, minmax(256px, 1fr)); -} -.hcards--col-1-2 .card, .hcards--col-2-1 .card { - grid-column: 1 / 2; -} -@media screen and (min-width: 1024px) { - .hcards--max-3 { - grid-template-columns: 1fr 1fr 1fr; - } - .hcards--col-1-2, .hcards--col-2-1 { - grid-template-columns: repeat(3, 1fr); - } -} -@media screen and (min-width: 720px) { - .hcards--col-2-1 .card:nth-of-type(1) { - grid-column: 1 / -2; - } - .hcards--col-2-1 .card:nth-of-type(2) { - grid-column: -2 / -1; - } - .hcards--col-1-2 .card:nth-of-type(1) { - grid-column: 1 / 2; - } - .hcards--col-1-2 .card:nth-of-type(2) { - grid-column: 2 / -1; - } -} - -.warning { - padding: calc(0.8rem - 2px) 0.8rem; - background-color: var(--yellow-200); - border: solid 2px var(--yellow-500); -} -.warning p:first-of-type { - margin-top: 0; -} -.warning p:last-of-type { - margin-bottom: 0; -} - -kx-claim { - display: block; - margin: 12px 0; -} - -#profileAvatar { - display: inline-block; - min-width: 96px; - max-width: 128px; - line-height: 0; - text-align: center; - border-radius: 50%; -} - -/* TYPOGRAPHY */ -h1 { - font-size: 1.6em; - margin: 3.2rem 0 1.6rem; - font-weight: normal; - color: var(--purple-700); - cursor: default; -} -h2 { - font-size: 1.4em; - margin: 3.2rem 0 1.6rem; - font-weight: normal; - color: var(--purple-700); - cursor: default; -} -h2 small { - margin-left: 0.8rem; - padding: 3px 6px; - background-color: var(--purple-600); - color: #fff; - border-radius: 4px; -} -h3 { - margin: 1.6rem 0; - font-size: 1.3em; - line-height: 1.6rem; - color: var(--grey-700); - font-weight: normal; - /* text-align: center; */ - cursor: default; -} -h3 small { - margin-left: 0.8rem; - padding: 3px 6px; - background-color: var(--purple-400); - color: #fff; - border-radius: 4px; -} -h4 { - margin: 1.6rem 0; - font-size: 1em; - line-height: 1.6rem; - color: var(--grey-600); - /* color: var(--purple-700); */ - font-weight: bold; - cursor: default; -} -h4 small { - margin-left: 0.8rem; - padding: 3px 6px; - background-color: var(--purple-400); - color: #fff; - border-radius: 4px; -} -p { - margin: 1.6rem 0; -} -p.warning { - padding: 8px; - background-color: #fffadc; - border: solid 1px #ffeea8; -} -a { - color: var(--blue-700); -} -ul { - padding-left: 1em; - list-style: '- '; -} -main h1:first-of-type { - margin-top: 1.6rem; -} -footer h1 { - margin-bottom: 0.8rem; - color: var(--purple-200); - font-size: 1.2rem; - font-weight: bold; -} -footer a { - display: inline-block; - color: var(--purple-100); - height: 32px; -} - -code { - padding: 2px 4px; - background-color: var(--purple-100); - border: 1px solid var(--purple-500); -} -pre { - padding: 8px 12px; - background-color: var(--purple-100); - border: 1px solid var(--purple-500); - overflow-x: auto; - line-height: 1.2rem; - font-size: 1rem; -} -pre code { - padding: 0; - background-color: 0px; - border: 0px; -} - -#qr { - display: block; - width: 100% !important; - max-width: 256px !important; - height: auto !important; - margin: 0 auto 16px; -} - -/* FORM ELEMENTS */ -.form-wrapper { - align-items: center; - padding-top: 1.4rem; - padding-bottom: 1.6rem; - margin-bottom: 48px; -} -.form-wrapper form { - display: flex; - flex-direction: column; - margin: 0; -} -.form-wrapper h2 { - margin-top: 0; -} -form input[type="text"], form input[type="search"] { - margin: 8px 0; - padding: 4px; - border: 1px solid #444; - font-size: 0.9rem; -} -form textarea { - width: 100%; - height: 128px; - margin: 8px 0; - resize: vertical; - font-size: 0.9rem; - border: 1px solid #444; -} -.button-wrapper { - display: flex; - flex-wrap: wrap; - gap: 8px; - margin: 8px 0; -} -.radio-wrapper { - display: flex; - flex-wrap: wrap; - margin: 8px 0; -} -.radio-wrapper input[type="radio"] { - position: absolute; - opacity: 0; - z-index: -1; -} -.radio-wrapper input[type="radio"] + label { - margin: 0; - padding: 2px 8px; - background-color: #fff; - border: solid var(--purple-400); - border-width: 2px 1px; - cursor: pointer; -} -.radio-wrapper input[type="radio"]:first-of-type + label { - border-radius: 4px 0 0 4px; - border-left-width: 2px; -} -.radio-wrapper input[type="radio"]:last-of-type + label { - border-radius: 0 4px 4px 0; - border-right-width: 2px; -} -.radio-wrapper input[type="radio"]:focus + label { - z-index: 1; -} -.radio-wrapper input[type="radio"] + label:hover { - background-color: var(--purple-100); - border-color: var(--purple-500); -} -.radio-wrapper input[type="radio"]:checked + label { - color: #fff; - background-color: var(--purple-600); - border-color: var(--purple-600); -} - -input[type="button"], input[type="submit"], button, a.button { - display: inline-block; - min-height: 36px; - margin: 8px 0; - padding: 4px 8px; - font-family: sans-serif; - font-size: 0.9rem; - text-decoration: none; - text-transform: uppercase; - color: #333; - background-color: #fff; - border: solid 2px var(--purple-400); - border-radius: 4px; - cursor: pointer; -} -input[type="button"]:focus, input[type="submit"]:focus, button:focus, a.button:focus { - background-color: azure; -} -input[type="button"]:hover, input[type="submit"]:hover, button:hover, a.button:hover { - background-color: var(--purple-500); - border-color: var(--purple-500); - color: #fff; -} -a.button i { - font-size: 1.4em; -} -a.button.button--liberapay { - padding: 8px 16px; - font-size: 0.95rem; - color: #333; - background-color: #ffee16; - border: 0; -} -a.button.button--liberapay:hover { - background-color: #fff463; -} - -/* DIALOGS */ -dialog { - width: 100% !important; - max-width: 800px !important; - padding: 0 !important; - word-wrap: anywhere; -} -dialog > div { - padding: 1em; -} -dialog form[method="Dialog"] { - margin: 1em 0 0 !important; -} -dialog form[method="Dialog"] input { - width: auto; -} -dialog p { - font-size: 1rem !important; - margin: 1rem 0; -} -dialog p:first-of-type { - margin-top: 0; -} - -/* KX-ITEM */ -.kx-item details { - width: 100%; - border-radius: 8px; -} -.kx-item details p { - margin: 0; - word-break: break-word; - font-size: 1rem; -} -.kx-item details a { - color: var(--blue-700); -} -.kx-item details hr { - border: none; - border-top: 2px solid var(--purple-100); -} -.kx-item details .content { - padding: 12px; - border: solid 3px var(--purple-100); - border-top: 0px; - border-radius: 0px 0px 8px 8px; -} -.kx-item details summary { - display: flex; - align-items: center; - padding: 8px 12px; - background-color: var(--purple-100); - border: solid 3px var(--purple-100); - border-radius: 8px; - list-style: none; - cursor: pointer; -} -.kx-item details summary::-webkit-details-marker { - display: none; -} -.kx-item details summary:hover, summary:focus { - border-color: var(--purple-400); -} -details[open] summary { - border-radius: 8px 8px 0px 0px; -} -.kx-item details summary .info { - flex: 1; -} -.kx-item details summary .info .title { - font-size: 1.1em; -} -.kx-item details summary .claim__description p { - font-size: 1.4rem; - line-height: 2rem; -} -.kx-item details summary .claim__links p, p.subtle-links { - display: flex; - align-items: center; - flex-wrap: wrap; - font-size: 1rem; - color: var(--grey-700); -} -.kx-item details summary .claim__links a, summary .claim__links span, p.subtle-links a { - font-size: 1rem; - margin: 0 10px 0 0; - color: var(--grey-700); -} -.kx-item details summary .subtitle { - color: var(--purple-700); -} -.kx-item details summary .verificationStatus { - position: relative; - display: flex; - align-items: center; - justify-content: center; - width: 48px; - height: 48px; - border-radius: 100%; - color: #fff; - font-size: 2rem; - user-select: none; -} -.kx-item details summary .verificationStatus::after { - position: absolute; - display: flex; - top: 0; - left: 0; - right: 0; - bottom: 0; - align-items: center; - justify-content: center; -} -.kx-item details summary .verificationStatus .inProgress { - opacity: 0; - transition: opacity 0.4s ease; - pointer-events: none; -} -.kx-item details summary .verificationStatus[data-value="success"] { - content: "v"; - background-color: var(--green-600); -} -.kx-item details summary .verificationStatus[data-value="success"]::after { - content: "✔"; -} -.kx-item details summary .verificationStatus[data-value="failed"] { - background-color: var(--red-400); -} -.kx-item details summary .verificationStatus[data-value="failed"]::after { - content: "✕"; -} -.kx-item details summary .verificationStatus[data-value="running"] .inProgress { - opacity: 1; -} - -.kx-item details .subsection { - display: flex; - align-items: center; - gap: 16px; -} -.kx-item details .subsection > img { - width: 24px; - height: 24px; - opacity: 0.4; -} - -.kx-item details .inProgress { - font-size: 10px; - margin: 50px auto; - text-indent: -9999em; - width: 48px; - height: 48px; - border-radius: 50%; - background: var(--purple-400); - background: -moz-linear-gradient(left, var(--purple-400) 10%, rgba(255, 255, 255, 0) 42%); - background: -webkit-linear-gradient(left, var(--purple-400) 10%, rgba(255, 255, 255, 0) 42%); - background: -o-linear-gradient(left, var(--purple-400) 10%, rgba(255, 255, 255, 0) 42%); - background: -ms-linear-gradient(left, var(--purple-400) 10%, rgba(255, 255, 255, 0) 42%); - background: linear-gradient(to right, var(--purple-400) 10%, rgba(255, 255, 255, 0) 42%); - position: relative; - -webkit-animation: load3 1.4s infinite linear; - animation: load3 1.4s infinite linear; - -webkit-transform: translateZ(0); - -ms-transform: translateZ(0); - transform: translateZ(0); -} -.kx-item details .inProgress:before { - width: 50%; - height: 50%; - background: var(--purple-400); - border-radius: 100% 0 0 0; - position: absolute; - top: 0; - left: 0; - content: ''; -} -.kx-item details .inProgress:after { - background: var(--purple-100); - width: 65%; - height: 65%; - border-radius: 50%; - content: ''; - margin: auto; - position: absolute; - top: 0; - left: 0; - bottom: 0; - right: 0; -} -.kx-item details button { - padding: 0.4rem 0.8rem; - margin-right: 8px; - text-decoration: none; - text-transform: uppercase; - background-color: #fff; - border: solid 2px var(--purple-400); - border-radius: 4px; - cursor: pointer; -} -.kx-item details button:hover { - background-color: var(--purple-500); - border-color: var(--purple-500); - color: #fff; -} - -@media screen and (max-width: 640px) { - .kx-item details summary .claim__description p { - font-size: 1.2rem; - } - .kx-item details summary .claim__links a, p.subtle-links a { - font-size: 0.9rem; - } -} -@media screen and (max-width: 480px) { - summary .claim__description p { - font-size: 1rem; - } - .kx-item details summary .verificationStatus { - width: 36px; - height: 36px; - font-size: 1.6rem; - } - .kx-item details .inProgress { - width: 36px; - height: 36px; - } -} - -@-webkit-keyframes load3 { - 0% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } - 100% { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); - } -} -@keyframes load3 { - 0% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } - 100% { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); - } -}