@digital-nature-ltd/carbon-lite
Version:
A simple tool to reduce the carbon usage of your website by blanking out the screen after a period of inactivity
287 lines (276 loc) • 10.3 kB
JavaScript
import { CarbonLiteMessage } from './elements/CarbonLiteMessage.mjs';
import { CarbonLiteElement } from './elements/CarbonLiteElement.mjs';
class CarbonLite {
constructor() {
this.initialised = false;
this.ignoreInteractions = false;
// objects
this.carbonLite = new CarbonLiteElement();
this.carbonLiteMessage = new CarbonLiteMessage();
this.carbonLiteTimer = undefined;
this.carbonLiteMessageTimer = undefined;
// configurable
this.config = {
message: 'CarbonLite is reducing the carbon usage of this site, one (dark) pixel at a time',
timeout: 60000,
backgroundColour: '#000',
messageTimeout: 3000,
messageColour: '#254137',
messageColourHover: '#7DF799',
messageBorderColour: '#7DF799',
messageDropShadowColour: '#254137',
debug: false
};
}
configure(configuration) {
this.config = { ...this.config, ...configuration };
}
debug(message) {
if (this.config.debug) {
console.log(`CarbonLite: ${message}`);
}
}
init(configuration = null) {
this.debug('initialising');
if (this.initialised) {
return;
}
if (configuration) {
this.configure(configuration);
}
if (this.config.message) {
this.carbonLite.configure(this.config.message);
}
// add styles
const style = document.createElement('style');
style.innerHTML = this.generateStyles();
document.head.appendChild(style);
this.addEventListeners();
this.restartTimer();
this.initialised = true;
}
generateStyles() {
return `
carbon-lite {
position: fixed;
bottom: 0;
right: 0;
top: 0;
left: 0;
color: ${this.config.messageColour};
z-index: 2147483646;
background: ${this.config.backgroundColour};
display: flex;
justify-content: center;
align-items: center;
div {
max-width: 80%;
line-height: 1.5rem;
}
}
carbon-lite-message {
position: fixed;
bottom: 100px;
right: 100px;
color: ${this.config.messageColour};
text-align: center;
z-index: 2147483647;
opacity: 0.8;
transition: opacity ${this.config.messageTimeout}ms ease-in;
a {
color: ${this.config.messageColour};
background: ${this.config.backgroundColour};
padding: 20px;
display: block;
text-decoration: none;
border-radius: 20px;
border: 3px solid ${this.config.backgroundColour};
&:hover {
color: ${this.config.messageColourHover};
text-decoration: underline;
border-color: ${this.config.messageBorderColour};
filter: drop-shadow(0 0 0.75rem ${this.config.messageDropShadowColour});
}
svg {
height: 100px;
display: block;
margin: 0 auto 20px auto;
}
}
&.fading {
opacity: 0;
a {
border-color: ${this.config.messageBorderColour};
filter: drop-shadow(0 0 0.75rem ${this.config.messageDropShadowColour});
}
}
&:hover {
transition: opacity 0ms linear;
}
}
`;
}
getIframes() {
let iframes = document.getElementsByTagName(`iframe`);
let iframesArray = [];
for (let i = 0; i < iframes.length; i++) {
iframesArray.push(iframes[i]);
}
return iframesArray;
}
addGlobalEventListener(eventType) {
this.debug(`Adding listeners for event type ${eventType}`);
let CarbonLite = this;
let iframes = this.getIframes();
iframes.forEach(iframe => {
iframe.addEventListener(eventType, () => {
CarbonLite.userInteracted();
});
});
document.addEventListener(eventType, () => {
CarbonLite.userInteracted();
});
}
addEventListeners() {
this.debug('adding event listeners');
this.addGlobalEventListener('mousemove');
this.addGlobalEventListener('click');
this.addGlobalEventListener('scroll');
this.addGlobalEventListener('keypress');
this.addGlobalEventListener('resize');
this.addVideoEventListeners();
let CarbonLite = this;
this.debug('adding message event listeners');
CarbonLite.carbonLiteMessage.addEventListener(`mouseenter`, (event) => {
CarbonLite.carbonLiteMessage.classList.remove('fading');
if (CarbonLite.carbonLiteMessageTimer) {
this.debug('clearing message fade out timer due to mouseenter');
clearTimeout(CarbonLite.carbonLiteMessageTimer);
}
});
CarbonLite.carbonLiteMessage.addEventListener(`mouseleave`, (event) => {
CarbonLite.fadeOutMessage();
});
document.addEventListener('carbon-lite-suspend', function (event) {
CarbonLite.suspend();
});
document.addEventListener('carbon-lite-resume', function (event) {
CarbonLite.resume();
});
document.addEventListener('carbon-lite-open', (event) => {
if (event.detail?.interactionDelay) {
this.debug(`ignoring interactions for ${event.detail.interactionDelay} ms`);
CarbonLite.ignoreInteractions = true;
let originalMessage = this.config.message;
if (event.detail.tempMessage) {
this.debug(`Adding a temporary message ${event.detail.tempMessage}`);
this.carbonLite.configure(event.detail.tempMessage);
}
setTimeout(() => {
this.debug('allowing interactions');
CarbonLite.ignoreInteractions = false;
if (originalMessage) {
this.carbonLite.configure(originalMessage);
}
}, event.detail.interactionDelay);
}
CarbonLite.open();
});
}
addVideoEventListeners() {
this.debug('adding video event listeners');
let CarbonLite = this;
let videos = document.getElementsByTagName(`video`);
for (let i = 0; i < videos.length; i++) {
let videoEl = videos[i];
videoEl.addEventListener(`playing`, () => {
CarbonLite.debug('video playing');
CarbonLite.suspend();
});
videoEl.addEventListener(`ended`, () => {
CarbonLite.debug('video ended');
CarbonLite.resume();
});
videoEl.addEventListener(`pause`, () => {
CarbonLite.debug('video paused');
CarbonLite.resume();
});
}
}
restartTimer() {
clearTimeout(this.carbonLiteTimer);
this.carbonLiteTimer = setTimeout(() => { this.open(); }, this.config.timeout);
}
userInteracted() {
if (this.ignoreInteractions) {
this.debug('ignoring interactions');
return;
}
if (this.backgroundIsVisible()) {
this.debug('user interacted - hiding');
this.hideBackground();
}
else {
this.debug('user interacted - restarting timer');
this.restartTimer();
}
}
suspend() {
this.debug('suspending timer');
if (this.backgroundIsVisible()) {
document.body.removeChild(this.carbonLite);
}
if (this.messageIsVisible()) {
document.body.removeChild(this.carbonLiteMessage);
}
clearTimeout(this.carbonLiteTimer);
clearTimeout(this.carbonLiteMessageTimer);
}
backgroundIsVisible() {
return this.carbonLite.parentNode === document.body;
}
messageIsVisible() {
return this.carbonLiteMessage.parentNode === document.body;
}
resume() {
this.debug('resuming timer');
this.restartTimer();
}
hideBackground() {
if (!this.carbonLite || !this.carbonLite.parentNode) {
return;
}
document.body.removeChild(this.carbonLite);
this.fadeOutMessage();
}
hideMessage() {
let CarbonLite = this;
if (!CarbonLite.carbonLiteMessage || !CarbonLite.carbonLiteMessage.parentNode) {
return;
}
this.debug('hiding message');
document.body.removeChild(CarbonLite.carbonLiteMessage);
this.debug('clearing message fade out timer due to message being hidden');
clearTimeout(CarbonLite.carbonLiteMessageTimer);
CarbonLite.carbonLiteMessage.classList.remove('fading');
this.debug('restarting timer after message has been hidden');
CarbonLite.restartTimer();
}
fadeOutMessage() {
this.debug('setting timer to fade out message');
this.carbonLiteMessage.classList.add('fading');
this.carbonLiteMessageTimer = setTimeout(() => { this.hideMessage(); }, this.config.messageTimeout);
}
open() {
this.debug('opening');
let CarbonLite = this;
if (CarbonLite.carbonLiteMessageTimer) {
this.debug('clearing message fade out timer');
clearTimeout(CarbonLite.carbonLiteMessageTimer);
}
document.body.appendChild(CarbonLite.carbonLite);
document.body.appendChild(CarbonLite.carbonLiteMessage);
}
}
export { CarbonLite as default };
//# sourceMappingURL=CarbonLite.mjs.map