Monero/Aeon Webminer add-on module for After Dark https://after-dark.habd.as/module/toxic-swamp/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

411 lines
16 KiB

{{ partial "modules/toxic-swamp/inline.css.html" }}
{{ $toggle := partial "components/button" (dict "isghost" "true" "type" "success" "name" "toggle") }}
{{ $controls := partial "components/buttongroup" (dict "class" "btn-controls" "body" $toggle) }}
{{ $hashrate := partial "components/textinput" (dict "attrs" (dict "type" "hidden" "name" "hashrate" "value" "0")) }}
{{ $hashes := partial "components/textinput" (dict "attrs" (dict "type" "hidden" "name" "hashes" "value" "0")) }}
<form hidden name="webminer" class="grid -center js-toolbar">
{{ partial "components/helpblock" (dict "class" "status" "body" (`<small id="js-status"></small>` | safeHTML))}}
{{ partial "components/helpblock" (dict "class" "interstitial" "body" (`<small id="js-interstitial" hidden>Click to activate…</small>` | safeHTML))}}
{{ partial "components/helpblock" (dict "class" "ticker" "body" (`<small id="js-ticker"></small>` | safeHTML))}}
<input name="throttle" type="range" min="10" max="100" step="5" value="30">
&nbsp;{{ $controls }}
{{ $hashrate }}
{{ $hashes }}
</form>
<noscript>
<style>form[name="webminer"] { display: none !important; }</style>
</noscript>
<script>
(function (window, document, undefiend) {
'use strict';
if (window.frameElement) return;
const nodeList = document.head.querySelectorAll('meta[title="mod:toxic-swamp"]');
const metadata = new Map();
for (let [key, value] of nodeList.entries()) {
let kvp = value.content.split(':'); metadata.set(kvp[0],kvp[1]);
}
const active = ['enabled', 'debugging'].includes(metadata.get('status'));
if (!(active && metadata.has('settings'))) return;
const proxies = JSON.parse(atob(metadata.get('settings')));
const proxy = proxies[Object.keys(proxies)[0]];
const debug = args =>
(metadata.get('status') === 'debugging') && console.log(args);
class SessionManager {
static get shouldMine () {
return !(sessionStorage.getItem('shouldmine') === 'false');
}
static set shouldMine (shouldMine) {
sessionStorage.setItem('shouldmine', shouldMine);
}
static get totalHashes () {
return Number(sessionStorage.getItem('totalhashes'));
}
static set totalHashes (total) {
sessionStorage.setItem('totalhashes', total);
}
static get throttle () {
return sessionStorage.getItem('throttle');
}
static set throttle (throttle) {
sessionStorage.setItem('throttle', throttle);
}
static get hashrate () {
return sessionStorage.getItem('hashrate');
}
static set hashrate (hashrate) {
sessionStorage.setItem('hashrate', hashrate);
}
}
class WebMiner {
static initialize () {
window.server = window._server;
window.throttleMiner = state.throttle || window._throttleMiner;
}
static start () { window.startMining(proxy.pool, proxy.address); }
static stop () { window.stopMining(); }
static get throttle () { return window.throttleMiner; }
static set throttle (throttle) { window.throttleMiner = throttle; }
static get hashTotal () { return window.totalhashes; }
static set hashTotal (total) { window.totalhashes = total; }
static get sendStack () { return window.sendStack; }
static get receiveStack () { return window.receiveStack; }
}
const form = document.forms.webminer;
const state = {
isMining: false,
shouldMine: SessionManager.shouldMine,
throttle: SessionManager.throttle,
hashrate: SessionManager.hashrate,
totalHashes: SessionManager.totalHashes,
shouldDisclose: window.matchMedia('(min-width: 840px)').matches
};
if (state.throttle) form.throttle.value = 100 - state.throttle;
if (state.hashrate) form.hashrate.value = state.hashrate;
if (state.totalHashes) form.hashes.value = state.totalHashes;
fetchInject([
{{ "/modules/toxic-swamp/webminer.min.js" | relURL }}
]).then(() => {
const status = form.querySelector('#js-status');
const interstitial = form.querySelector('#js-interstitial');
const ticker = form.querySelector('#js-ticker');
const displaySetting = state.shouldDisclose ? 'full' : 'compact';
const helpText = {
minerActivated: 'Miner activated…',
minerDeactivated: 'Miner deactivated…',
deviceOnline: 'Device online…',
deviceOffline: 'Device offline…',
devicePowered: 'Device powered…',
powerUnplugged: 'Power unplugged…',
miningPaused: 'Mining paused…',
miningResumed: 'Mining resumed…',
networkDisconnected: 'Network disconnected…',
networkRestored: 'Network restored…',
clickToActivate: 'Click to activate…',
clickToOverride: 'Click to override…',
savingBattery: 'Saving battery…',
phraseHashPower: 'hash power…',
wordHashes: 'hashes',
unitsPerSecond: 'H/s'
};
const statusText = {
waitingForNetwork: 'waiting for network',
waitingForServer: 'waiting for server',
newJob: 'new job',
solvedJob: 'solved job',
poolAcceptedHash: 'pool accepted hash!',
error: 'error'
}
const errorText = {
displayModeInvalid: `Display mode invalid! Expected one of 'full', 'compact', 'minimal' or 'hidden'.`
}
let statusIntervalId;
const startMining = () => {
clearInterval(statusIntervalId);
WebMiner.start();
const { receiveStack, sendStack } = WebMiner;
statusIntervalId = setInterval(function () {
while (sendStack.length) updateStatus(sendStack.pop());
while (receiveStack.length) updateStatus(receiveStack.pop());
}, 2000);
};
const stopMining = () => {
WebMiner.stop();
clearInterval(statusIntervalId);
}
const updateStatus = data => {
status.textContent = `[${new Date().toLocaleString()}] `;
if (data.identifier === 'job') {
form.toggle.classList.add('-mining');
status.textContent += `${statusText.newJob}: ${data.job_id}`;
} else if (data.identifier === 'solved') {
status.textContent += `${statusText.solvedJob}: ${data.job_id}`;
} else if (data.identifier === 'hashsolved') {
status.textContent += statusText.poolAcceptedHash;
} else if (data.identifier === 'error') {
form.toggle.classList.remove('-mining');
status.textContent += `${statusText.error}: ${data.param}`;
} else status.textContent += data;
debug(status.textContent);
};
const showInterstitial = (message, delay = 0) => {
setTimeout(function () {
interstitial.textContent = message;
ticker.hidden = true;
interstitial.hidden = false;
setTimeout(function () {
ticker.hidden = false;
interstitial.hidden = true;
}, 2000);
}, delay);
};
class Actuator {
static activate (shouldPersist = false) {
state.isMining = true;
startMining();
shouldPersist && (() => {
SessionManager.shouldMine = true;
showInterstitial(helpText.minerActivated);
})();
}
static deactivate (shouldPersist = false) {
state.isMining = false;
stopMining();
shouldPersist && (() => {
SessionManager.shouldMine = false;
showInterstitial(helpText.minerDeactivated);
})();
}
static get status () {
return state.isMining ? 'active' : 'inactive';
}
}
class Toolbar {
static initialize () {
WebMiner.initialize();
setInterval(function () {
const hashTotal = WebMiner.hashTotal;
state.hashrate = Math.floor(hashTotal / 2 + state.hashrate / 2);
state.totalHashes = state.totalHashes + hashTotal;
WebMiner.hashTotal = 0;
}, 1000);
const updateTicker = () => {
Toolbar.updateTickerTotal();
requestAnimationFrame(updateTicker);
};
requestAnimationFrame(updateTicker);
}
static togglePower (wasUserInitiated = false) {
const isMinerActive = Actuator.status === 'active';
const isDeviceOnline = navigator.onLine;
form.toggle.classList.toggle('-active');
isMinerActive
? Actuator.deactivate(wasUserInitiated) && Toolbar.toggleStatusbar()
: Actuator.activate(wasUserInitiated);
isDeviceOnline
? updateStatus(statusText.waitingForServer)
: updateStatus(statusText.waitingForNetwork);
}
static setThrottle (throttle) {
WebMiner.throttle = 100 - throttle;
SessionManager.throttle = 100 - throttle;
showInterstitial(`${throttle}% ${helpText.phraseHashPower}`);
}
static setDisplayMode (displayMode = 'full') {
const isValidMode = [
'full', 'compact', 'hidden', 'minimal'
].includes(displayMode);
if (!isValidMode) throw new Error(errorText.displayModeInvalid);
Toolbar.displayMode = displayMode;
Toolbar.updateDisplayMode();
}
static getDisplayMode () {
return Toolbar.displayMode;
}
static updateTickerTotal () {
const total = state.totalHashes + WebMiner.hashTotal;
const hashrate = state.hashrate || 0;
ticker.textContent = `${total} ${helpText.wordHashes} (${hashrate} ${helpText.unitsPerSecond})`;
}
static updateDisplayMode () {
const displayMode = Toolbar.displayMode;
const shouldMine = SessionManager.shouldMine;
const visibleItems = new Set([
form, ticker, status, form.toggle, form.throttle
]);
let hiddenItems;
switch (displayMode) {
case 'full':
hiddenItems = shouldMine ? [] : [].concat(ticker);
break;
case 'compact':
hiddenItems = [].concat([form, ticker, status]);
break;
case 'minimal':
hiddenItems = [].concat([form, ticker, status, form.throttle]);
break;
case 'hidden':
hiddenItems = [].concat(
[form, ticker, status, form.toggle, form.throttle]
);
break;
}
hiddenItems.forEach(hiddenItem => {
hiddenItem.hidden = true;
visibleItems.delete(hiddenItem);
});
visibleItems.forEach(visibleItem => {
visibleItem.hidden = false;
});
}
static toggleStatusbar () {
const isMinerActive = Actuator.status === 'active';
isMinerActive
? form.classList.toggle('-disclosed')
: form.classList.remove('-disclosed');
}
static registerListeners () {
form.addEventListener(
'submit', evt => evt.preventDefault()
);
form.throttle.addEventListener(
'change', evt => Toolbar.setThrottle(evt.target.value)
);
form.toggle.addEventListener(
'keyup', evt => evt.keyCode === 13 && Toolbar.togglePower(true)
);
form.toggle.addEventListener(
'click', evt => evt.detail && Toolbar.togglePower(true)
);
form.toggle.addEventListener(
'mouseenter', evt => Toolbar.toggleStatusbar()
);
form.toggle.addEventListener(
'mouseleave', evt => Toolbar.toggleStatusbar()
);
form.toggle.addEventListener(
'focus', evt => Toolbar.toggleStatusbar()
);
form.toggle.addEventListener(
'blur', evt => Toolbar.toggleStatusbar()
);
}
}
Toolbar.initialize();
Toolbar.setDisplayMode(displaySetting);
Toolbar.registerListeners();
const handleChargingChange = evt => {
const shouldMine = SessionManager.shouldMine;
if (!shouldMine) return;
const isMinerActive = Actuator.status === 'active';
const startedCharging = evt.target.charging;
const isDeviceOnline = navigator.onLine;
if (startedCharging) {
const isDeviceOnline = navigator.onLine;
showInterstitial(helpText.devicePowered);
!isMinerActive && Toolbar.togglePower()
if (isDeviceOnline) {
showInterstitial(helpText.miningResumed, 3000);
} else {
updateStatus('waiting for network');
showInterstitial(helpText.networkDisconnected, 3000);
}
} else {
showInterstitial(helpText.powerUnplugged);
isMinerActive && Toolbar.togglePower();
isDeviceOnline
? showInterstitial(helpText.savingBattery, 3000)
: showInterstitial(helpText.networkDisconnected, 3000);
}
};
const handleOnlineChange = evt => {
const shouldMine = SessionManager.shouldMine;
if (!shouldMine) return;
const isMinerActive = Actuator.status === 'active';
const wentOnline = evt.type === 'online';
if (wentOnline) {
showInterstitial(helpText.networkRestored);
if (isMinerActive) {
updateStatus(statusText.waitingForServer);
showInterstitial(helpText.miningResumed, 3000);
} else {
showInterstitial(helpText.clickToActivate, 3000);
}
} else {
showInterstitial(helpText.networkDisconnected);
if (isMinerActive) {
Toolbar.togglePower();
updateStatus(statusText.waitingForNetwork);
showInterstitial(helpText.miningPaused, 3000);
} else {
showInterstitial(helpText.miningPaused, 3000);
}
}
};
const handleVisibilityChange = evt => {
const shouldMine = SessionManager.shouldMine;
if (!shouldMine) return;
const isMinerActive = Actuator.status === 'active';
const wasDocumentHidden = document['hidden'];
if (wasDocumentHidden) {
isMinerActive && Toolbar.togglePower();
} else {
navigator.getBattery().then(battery => {
const isDeviceCharging = battery.charging;
if (isDeviceCharging) {
!isMinerActive && Toolbar.togglePower();
} else {
showInterstitial(helpText.savingBattery);
showInterstitial(helpText.clickToOverride, 3000);
}
});
}
};
const handlePageHide = evt => {
SessionManager.totalHashes = state.totalHashes + WebMiner.hashTotal;
SessionManager.hashrate = state.hashrate;
};
const handlePageShow = evt => Toolbar.updateTickerTotal();
window.addEventListener('online', handleOnlineChange);
window.addEventListener('offline', handleOnlineChange);
window.addEventListener('pageshow', handlePageShow);
window.addEventListener('pagehide', handlePageHide);
document.addEventListener('visibilitychange', handleVisibilityChange);
navigator.getBattery().then(battery => {
battery.onchargingchange = handleChargingChange;
const shouldMine = SessionManager.shouldMine;
if (!shouldMine) return;
const isDeviceOnline = navigator.onLine;
const isDeviceCharging = battery.charging;
if (isDeviceCharging) {
if (isDeviceOnline) return Toolbar.togglePower();
showInterstitial(helpText.networkDisconnected);
showInterstitial(helpText.miningPaused, 3000);
} else {
showInterstitial(helpText.powerUnplugged);
showInterstitial(helpText.miningPaused, 3000);
}
}); // zip it up and zip it out
});
})(window, document);
</script>