451-tools
Version:
Censorship resilient and distributed publishing: informed by the needs of publishers and their audiences, More Mirrors implements a set of offline fallback strategies for censorship resilient websites.
206 lines (171 loc) • 13.6 kB
JavaScript
(() => {
const template = document.createElement('template');
template.innerHTML = `
<div class="button">
<div class="status status--unknown">
<span class="visually-hidden"></span>
<svg class="icon" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><path d="M52.137 92.76a8.95 8.95 0 0 1-2.437.36c-.035 0-.069-.003-.104-.003-6.909-.059-13.116-8.343-16.638-20.505 4.853-1.173 10.096-1.81 15.498-1.897.326-1.481.761-2.92 1.298-4.31l-.083-.002c-6.186 0-12.206.718-17.777 2.072-1.129-5.031-1.819-10.575-1.956-16.378h31.368a29.802 29.802 0 0 1 12.359-4.638c-.154-6.133-.868-11.924-2.053-17.181 4.778-1.503 9.139-3.496 12.868-5.935a42.913 42.913 0 0 1 8.394 23.477H83.92a29.905 29.905 0 0 1 13.086 6.389c.125-1.401.2-2.817.2-4.251 0-26.158-21.281-47.44-47.44-47.44l-.062.002-.033-.002c-.035 0-.07.005-.105.005-26.066.109-47.24 21.344-47.24 47.436S23.5 97.286 49.567 97.394c.035 0 .07.005.105.005l.033-.002.062.002c1.884 0 3.738-.123 5.565-.337a30.125 30.125 0 0 1-3.195-4.302zM29.938 47.82c.137-5.802.828-11.346 1.956-16.377 5.571 1.354 11.592 2.072 17.777 2.072 6.189 0 12.208-.718 17.777-2.072 1.129 5.031 1.819 10.574 1.956 16.377H29.938zm51.813-26.813c-3.266 2.083-7.047 3.811-11.198 5.135-2.152-7.456-5.298-13.584-9.11-17.728a43.232 43.232 0 0 1 20.308 12.593zM49.597 6.8c.035 0 .069-.003.104-.003 6.928.023 13.155 8.32 16.685 20.511-5.216 1.261-10.876 1.929-16.714 1.929-5.835 0-11.495-.67-16.713-1.932C36.481 15.143 42.687 6.86 49.597 6.8zm-11.76 1.686c-3.784 4.143-6.906 10.242-9.046 17.656-4.099-1.307-7.838-3.01-11.076-5.058A43.233 43.233 0 0 1 37.837 8.486zM14.992 24.428c3.7 2.4 8.016 4.365 12.739 5.85-1.209 5.362-1.935 11.274-2.07 17.542H6.657a42.915 42.915 0 0 1 8.335-23.392zm0 51.062a42.915 42.915 0 0 1-8.335-23.393h19.004c.135 6.268.861 12.181 2.07 17.542-4.723 1.486-9.039 3.451-12.739 5.851zm2.723 3.343c3.238-2.048 6.977-3.75 11.076-5.057 2.14 7.414 5.263 13.513 9.046 17.656a43.235 43.235 0 0 1-20.122-12.599z"/><path d="M77.738 56.003c-11.686 0-21.16 9.474-21.16 21.16s9.474 21.16 21.16 21.16 21.16-9.474 21.16-21.16c0-11.687-9.474-21.16-21.16-21.16zM63.573 72.461a2.591 2.591 0 0 1 3.637-.38l4.796 3.891 10.419-13.605a2.585 2.585 0 1 1 4.105 3.143L74.493 81.226a2.585 2.585 0 0 1-3.68.437l-6.859-5.564a2.586 2.586 0 0 1-.381-3.638zm25.428 5.827L76.965 94.004a2.585 2.585 0 0 1-3.68.437l-6.859-5.564a2.586 2.586 0 1 1 3.257-4.017l4.796 3.891 10.419-13.605a2.584 2.584 0 1 1 4.103 3.142z"/></svg>
</div>
<div class="status status--up">
<span class="visually-hidden"></span>
<svg class="icon" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><path d="M52.137 92.76a8.95 8.95 0 0 1-2.437.36c-.035 0-.069-.003-.104-.003-6.909-.059-13.116-8.343-16.638-20.505 4.853-1.173 10.096-1.81 15.498-1.897.326-1.481.761-2.92 1.298-4.31l-.083-.002c-6.186 0-12.206.718-17.777 2.072-1.129-5.031-1.819-10.575-1.956-16.378h31.368a29.802 29.802 0 0 1 12.359-4.638c-.154-6.133-.868-11.924-2.053-17.181 4.778-1.503 9.139-3.496 12.868-5.935a42.913 42.913 0 0 1 8.394 23.477H83.92a29.905 29.905 0 0 1 13.086 6.389c.125-1.401.2-2.817.2-4.251 0-26.158-21.281-47.44-47.44-47.44l-.062.002-.033-.002c-.035 0-.07.005-.105.005-26.066.109-47.24 21.344-47.24 47.436S23.5 97.286 49.567 97.394c.035 0 .07.005.105.005l.033-.002.062.002c1.884 0 3.738-.123 5.565-.337a30.125 30.125 0 0 1-3.195-4.302zM29.938 47.82c.137-5.802.828-11.346 1.956-16.377 5.571 1.354 11.592 2.072 17.777 2.072 6.189 0 12.208-.718 17.777-2.072 1.129 5.031 1.819 10.574 1.956 16.377H29.938zm51.813-26.813c-3.266 2.083-7.047 3.811-11.198 5.135-2.152-7.456-5.298-13.584-9.11-17.728a43.232 43.232 0 0 1 20.308 12.593zM49.597 6.8c.035 0 .069-.003.104-.003 6.928.023 13.155 8.32 16.685 20.511-5.216 1.261-10.876 1.929-16.714 1.929-5.835 0-11.495-.67-16.713-1.932C36.481 15.143 42.687 6.86 49.597 6.8zm-11.76 1.686c-3.784 4.143-6.906 10.242-9.046 17.656-4.099-1.307-7.838-3.01-11.076-5.058A43.233 43.233 0 0 1 37.837 8.486zM14.992 24.428c3.7 2.4 8.016 4.365 12.739 5.85-1.209 5.362-1.935 11.274-2.07 17.542H6.657a42.915 42.915 0 0 1 8.335-23.392zm0 51.062a42.915 42.915 0 0 1-8.335-23.393h19.004c.135 6.268.861 12.181 2.07 17.542-4.723 1.486-9.039 3.451-12.739 5.851zm2.723 3.343c3.238-2.048 6.977-3.75 11.076-5.057 2.14 7.414 5.263 13.513 9.046 17.656a43.235 43.235 0 0 1-20.122-12.599z"/><path d="M77.738 56.003c-11.686 0-21.16 9.474-21.16 21.16s9.474 21.16 21.16 21.16 21.16-9.474 21.16-21.16c0-11.687-9.474-21.16-21.16-21.16zM63.573 72.461a2.591 2.591 0 0 1 3.637-.38l4.796 3.891 10.419-13.605a2.585 2.585 0 1 1 4.105 3.143L74.493 81.226a2.585 2.585 0 0 1-3.68.437l-6.859-5.564a2.586 2.586 0 0 1-.381-3.638zm25.428 5.827L76.965 94.004a2.585 2.585 0 0 1-3.68.437l-6.859-5.564a2.586 2.586 0 1 1 3.257-4.017l4.796 3.891 10.419-13.605a2.584 2.584 0 1 1 4.103 3.142z"/></svg>
</div>
<div class="status status--low">
<span class="visually-hidden"></span>
<svg class="icon" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><path d="M52.137 92.76a8.95 8.95 0 0 1-2.437.36c-.035 0-.069-.003-.104-.003-6.909-.059-13.116-8.343-16.638-20.505 4.853-1.173 10.096-1.81 15.498-1.897.326-1.481.761-2.92 1.298-4.31l-.083-.002c-6.186 0-12.206.718-17.777 2.072-1.129-5.031-1.819-10.575-1.956-16.378h31.368a29.802 29.802 0 0 1 12.359-4.638c-.154-6.133-.868-11.924-2.053-17.181 4.778-1.503 9.139-3.496 12.868-5.935a42.913 42.913 0 0 1 8.394 23.477H83.92a29.905 29.905 0 0 1 13.086 6.389c.125-1.401.2-2.817.2-4.251 0-26.158-21.281-47.44-47.44-47.44l-.062.002-.033-.002c-.035 0-.07.005-.105.005-26.066.109-47.24 21.344-47.24 47.436S23.5 97.286 49.567 97.394c.035 0 .07.005.105.005l.033-.002.062.002c1.884 0 3.738-.123 5.565-.337a30.125 30.125 0 0 1-3.195-4.302zM29.938 47.82c.137-5.802.828-11.346 1.956-16.377 5.571 1.354 11.592 2.072 17.777 2.072 6.189 0 12.208-.718 17.777-2.072 1.129 5.031 1.819 10.574 1.956 16.377H29.938zm51.813-26.813c-3.266 2.083-7.047 3.811-11.198 5.135-2.152-7.456-5.298-13.584-9.11-17.728a43.232 43.232 0 0 1 20.308 12.593zM49.597 6.8c.035 0 .069-.003.104-.003 6.928.023 13.155 8.32 16.685 20.511-5.216 1.261-10.876 1.929-16.714 1.929-5.835 0-11.495-.67-16.713-1.932C36.481 15.143 42.687 6.86 49.597 6.8zm-11.76 1.686c-3.784 4.143-6.906 10.242-9.046 17.656-4.099-1.307-7.838-3.01-11.076-5.058A43.233 43.233 0 0 1 37.837 8.486zM14.992 24.428c3.7 2.4 8.016 4.365 12.739 5.85-1.209 5.362-1.935 11.274-2.07 17.542H6.657a42.915 42.915 0 0 1 8.335-23.392zm0 51.062a42.915 42.915 0 0 1-8.335-23.393h19.004c.135 6.268.861 12.181 2.07 17.542-4.723 1.486-9.039 3.451-12.739 5.851zm2.723 3.343c3.238-2.048 6.977-3.75 11.076-5.057 2.14 7.414 5.263 13.513 9.046 17.656a43.235 43.235 0 0 1-20.122-12.599z"/><path d="M77.738 56.003c-11.686 0-21.16 9.474-21.16 21.16s9.474 21.16 21.16 21.16 21.16-9.474 21.16-21.16c0-11.687-9.474-21.16-21.16-21.16zm8.644 26.452a2.37 2.37 0 1 1-3.352 3.352l-5.292-5.292-5.292 5.292c-.463.463-1.069.694-1.676.694s-1.213-.231-1.676-.694a2.37 2.37 0 0 1 0-3.352l5.292-5.292-5.292-5.292a2.37 2.37 0 1 1 3.352-3.352l5.292 5.292 5.292-5.292a2.37 2.37 0 1 1 3.352 3.352l-5.292 5.292 5.292 5.292z"/></svg>
</div>
<div class="status status--down">
<span class="visually-hidden"></span>
<svg class="icon" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><path d="M52.137 92.76a8.95 8.95 0 0 1-2.437.36c-.035 0-.069-.003-.104-.003-6.909-.059-13.116-8.343-16.638-20.505 4.853-1.173 10.096-1.81 15.498-1.897.326-1.481.761-2.92 1.298-4.31l-.083-.002c-6.186 0-12.206.718-17.777 2.072-1.129-5.031-1.819-10.575-1.956-16.378h31.368a29.802 29.802 0 0 1 12.359-4.638c-.154-6.133-.868-11.924-2.053-17.181 4.778-1.503 9.139-3.496 12.868-5.935a42.913 42.913 0 0 1 8.394 23.477H83.92a29.905 29.905 0 0 1 13.086 6.389c.125-1.401.2-2.817.2-4.251 0-26.158-21.281-47.44-47.44-47.44l-.062.002-.033-.002c-.035 0-.07.005-.105.005-26.066.109-47.24 21.344-47.24 47.436S23.5 97.286 49.567 97.394c.035 0 .07.005.105.005l.033-.002.062.002c1.884 0 3.738-.123 5.565-.337a30.125 30.125 0 0 1-3.195-4.302zM29.938 47.82c.137-5.802.828-11.346 1.956-16.377 5.571 1.354 11.592 2.072 17.777 2.072 6.189 0 12.208-.718 17.777-2.072 1.129 5.031 1.819 10.574 1.956 16.377H29.938zm51.813-26.813c-3.266 2.083-7.047 3.811-11.198 5.135-2.152-7.456-5.298-13.584-9.11-17.728a43.232 43.232 0 0 1 20.308 12.593zM49.597 6.8c.035 0 .069-.003.104-.003 6.928.023 13.155 8.32 16.685 20.511-5.216 1.261-10.876 1.929-16.714 1.929-5.835 0-11.495-.67-16.713-1.932C36.481 15.143 42.687 6.86 49.597 6.8zm-11.76 1.686c-3.784 4.143-6.906 10.242-9.046 17.656-4.099-1.307-7.838-3.01-11.076-5.058A43.233 43.233 0 0 1 37.837 8.486zM14.992 24.428c3.7 2.4 8.016 4.365 12.739 5.85-1.209 5.362-1.935 11.274-2.07 17.542H6.657a42.915 42.915 0 0 1 8.335-23.392zm0 51.062a42.915 42.915 0 0 1-8.335-23.393h19.004c.135 6.268.861 12.181 2.07 17.542-4.723 1.486-9.039 3.451-12.739 5.851zm2.723 3.343c3.238-2.048 6.977-3.75 11.076-5.057 2.14 7.414 5.263 13.513 9.046 17.656a43.235 43.235 0 0 1-20.122-12.599z"/><path d="M77.738 56.003c-11.686 0-21.16 9.474-21.16 21.16s9.474 21.16 21.16 21.16 21.16-9.474 21.16-21.16c0-11.687-9.474-21.16-21.16-21.16zm8.644 26.452a2.37 2.37 0 1 1-3.352 3.352l-5.292-5.292-5.292 5.292c-.463.463-1.069.694-1.676.694s-1.213-.231-1.676-.694a2.37 2.37 0 0 1 0-3.352l5.292-5.292-5.292-5.292a2.37 2.37 0 1 1 3.352-3.352l5.292 5.292 5.292-5.292a2.37 2.37 0 1 1 3.352 3.352l-5.292 5.292 5.292 5.292z"/></svg>
</div>
</div>
<style>
*,
*::before,
*::after {
box-sizing: border-box;
}
.button {
appearance: none;
border-radius: 0.25rem;
border: none;
bottom: 1.5rem;
fill: black;
height: 3rem;
left: 1.5rem;
padding: 0.5rem;
position: fixed;
transition: background-color 0.15s ease-in-out, transform 0.15s ease-in-out;
width: 3rem;
z-index: 1000;
}
.button.has-link:hover {
transform: scale(1.15);
}
.button.up {
background-color: #00E676;
}
.button.has-link.up:hover {
background-color: #00C853;
}
.button.low {
background-color: #F5B82F;
}
.button.has-link.low:hover {
background-color: #E0A735;
}
.button.down {
background-color: #FF1744;
}
.button.has-link.down:hover {
background-color: #D50000;
}
.button a {
height: 100%;
left: 0;
padding: 0.5rem;
position: absolute;
top: 0;
width: 100%;
}
.status {
display: none;
}
.unknown .status--unknown {
display: block;
}
.up .status--up {
display: block;
}
.low .status--low {
display: block;
}
.down .status--down {
display: block;
}
.icon {
height: auto;
width: 100%;
}
.visually-hidden {
border: 0;
clip: rect(0 0 0 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
}
</style>
`;
class TrafficLight extends HTMLElement {
constructor () {
super();
const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.appendChild(template.content.cloneNode(true));
this.button = shadowRoot.querySelector('.button');
navigator.serviceWorker.addEventListener('message', (event) => {
const payload = JSON.parse(event.data);
if (payload.type === 'mirror-status-changed') {
this.setNetworkStatus();
}
});
}
connectedCallback () {
this.setNetworkStatus();
this.button.querySelector('.status--unknown span').textContent = this.unknownText;
this.button.querySelector('.status--up span').textContent = this.upText;
this.button.querySelector('.status--low span').textContent = this.lowText;
this.button.querySelector('.status--down span').textContent = this.downText;
if (this.href) {
const a = document.createElement('a');
a.href = this.href;
a.innerHTML = this.button.innerHTML;
if (this.target) a.target = this.target;
if (this.rel) a.rel = this.rel;
this.button.classList.add('has-link');
this.button.innerHTML = a.outerHTML;
}
}
async setNetworkStatus () {
const status = await fetch('/451-tools/mirror-status/')
.then(response => {
if (!response.ok) {
throw new Error('Unable to call mirror status endpoint');
}
return response.json();
})
.then(data => data.status) // unknown, up. down or low
.catch(() => 'unknown');
this.button.classList.remove('unknown', 'up', 'down', 'low');
this.button.classList.add(status);
this.button.setAttribute(
'part',
`button ${status}`
);
}
get unknownText () {
return this.getAttribute('unknown-text') || 'Unknown';
}
get upText () {
return this.getAttribute('up-text') || 'Online';
}
get lowText () {
return this.getAttribute('low-text') || 'Low';
}
get downText () {
return this.getAttribute('down-text') || 'Offline';
}
get href () {
return this.getAttribute('href');
}
get target () {
return this.getAttribute('target');
}
get rel () {
return this.getAttribute('rel');
}
}
customElements.define('traffic-light', TrafficLight);
})();