diff --git a/img/qr.jpg b/img/qr.jpg new file mode 100644 index 0000000..a8f6e34 Binary files /dev/null and b/img/qr.jpg differ diff --git a/index.php b/index.php index 9fa90c4..27dc946 100644 --- a/index.php +++ b/index.php @@ -27,6 +27,7 @@ $display_servers_guru = isset($config['servers_guru']) && $config['servers_guru' $attribution = isset($config['attribution']) ? $config['attribution'] : ''; $preferred_currencies = isset($config['preferred_currencies']) ? $config['preferred_currencies'] : []; $github_url = isset($config['github_url']) ? $config['github_url'] : 'https://github.com/rottenwheel/moner.ooo/'; +$footer_html = isset($config['footer_html']) ? $config['footer_html'] : ''; // Extract the keys $currencies = array_map('strtoupper', array_keys($api_cg)); diff --git a/package-lock.json b/package-lock.json index e4bb07c..70ae82f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -124,30 +124,10 @@ "url": "https://opencollective.com/popperjs" } }, - "node_modules/@types/eslint": { - "version": "8.56.10", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz", - "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "license": "MIT", - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", "license": "MIT" }, "node_modules/@types/json-schema": { @@ -379,10 +359,10 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-import-assertions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", - "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", "license": "MIT", "peerDependencies": { "acorn": "^8" @@ -833,9 +813,9 @@ "license": "MIT" }, "node_modules/enhanced-resolve": { - "version": "5.16.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.1.tgz", - "integrity": "sha512-4U5pNsuDl0EhuZpq46M5xPslstkviJuhrdobaRDBk2Jy2KO37FDAJl4lb2KlNabxT0m4MTK2UHNrsAcphE8nyw==", + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", @@ -1587,9 +1567,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", "license": "ISC" }, "node_modules/pkg-dir": { @@ -1605,9 +1585,9 @@ } }, "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", "dev": true, "funding": [ { @@ -1626,8 +1606,8 @@ "license": "MIT", "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.2.0" + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -1965,9 +1945,9 @@ } }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -2248,21 +2228,20 @@ } }, "node_modules/webpack": { - "version": "5.91.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.91.0.tgz", - "integrity": "sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw==", + "version": "5.95.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.95.0.tgz", + "integrity": "sha512-2t3XstrKULz41MNMBF+cJ97TyHdyQ8HCt//pqErqDvNjU9YQBnZxIHa11VXsi7F3mb5/aO2tuDxdeTPdU7xu9Q==", "license": "MIT", "dependencies": { - "@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.5", "@webassemblyjs/ast": "^1.12.1", "@webassemblyjs/wasm-edit": "^1.12.1", "@webassemblyjs/wasm-parser": "^1.12.1", "acorn": "^8.7.1", - "acorn-import-assertions": "^1.9.0", + "acorn-import-attributes": "^1.9.5", "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.16.0", + "enhanced-resolve": "^5.17.1", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", diff --git a/src/js/main.js b/src/js/main.js index 7390237..9f99bd2 100644 --- a/src/js/main.js +++ b/src/js/main.js @@ -1,163 +1,182 @@ import 'bootstrap/dist/css/bootstrap.min.css'; -import 'bootstrap/dist/js/bootstrap.bundle.min'; - import '../css/custom.css'; -import Tooltip from "bootstrap/js/dist/tooltip"; -var tooltipTriggerList = [].slice.call( - document.querySelectorAll('[data-toggle="tooltip"]') -); -var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) { - return new Tooltip(tooltipTriggerEl, { placement: "top" }); -}); +import Tooltip from 'bootstrap/js/dist/tooltip'; + +const tooltipTriggerList = Array.from(document.querySelectorAll('[data-toggle="tooltip"]')); +const tooltipList = tooltipTriggerList.map(tooltipTriggerEl => new Tooltip(tooltipTriggerEl, { placement: 'top' })); console.log(tooltipList); let lastModifiedField = 'xmr'; +const exchangeRates = {}; -var exchangeRates = {}; +const runConvert = () => + lastModifiedField === 'xmr' ? xmrConvert() : fiatConvert(); -document.addEventListener('DOMContentLoaded', function () { - const copyXMRBtn = document.getElementById('copyXMRBtn'); - const copyFiatBtn = document.getElementById('copyFiatBtn'); - const xmrInput = document.getElementById('xmrInput'); - const fiatInput = document.getElementById('fiatInput'); - const selectBox = document.getElementById('selectBox'); - const convertXMRToFiatBtn = document.getElementById('convertXMRToFiat'); - const convertFiatToXMRBtn = document.getElementById('convertFiatToXMR'); - const fiatButtons = document.querySelectorAll('.fiat-btn'); +let updateInterval +const startFetching = () => updateInterval = setInterval(fetchUpdatedExchangeRates, 5000); +const stopFetching = () => { + clearInterval(updateInterval) + updateInterval = null; +}; - // Add event listeners for the currency buttons - fiatButtons.forEach(button => { - button.addEventListener('click', (e) => { - e.preventDefault(); - selectBox.value = button.textContent; - if (lastModifiedField === 'xmr') { - xmrConvert(); - } else { - fiatConvert(); - } - history.pushState(null, '', `?in=${button.textContent}`); - }); - }); +const inactivityTimeout = 30 * 1000; // 30 seconds +let lastActivity = Date.now() - // Add event listeners for the copy buttons - copyXMRBtn.addEventListener('click', copyToClipBoardXMR); - copyFiatBtn.addEventListener('click', copyToClipBoardFiat); - - // Add event listeners for the XMR input field - xmrInput.addEventListener('change', () => xmrConvert(xmrInput.value)); - xmrInput.addEventListener('keyup', () => { - xmrInput.value = xmrInput.value.replace(/[^\.^,\d]/g, ''); - xmrInput.value = xmrInput.value.replace(/\,/, '.'); - if (xmrInput.value.split('.').length > 2) { - xmrInput.value = xmrInput.value.slice(0, -1); - } - xmrConvert(xmrInput.value); - }); - xmrInput.addEventListener('input', () => { - lastModifiedField = 'xmr'; - }); - - // Add event listeners for the fiat input field - fiatInput.addEventListener('change', () => fiatConvert(fiatInput.value)); - fiatInput.addEventListener('keyup', () => { - fiatInput.value = fiatInput.value.replace(/[^\.^,\d]/g, ''); - fiatInput.value = fiatInput.value.replace(/\,/, '.'); - if (fiatInput.value.split('.').length > 2) { - fiatInput.value = fiatInput.value.slice(0, -1); - } - fiatConvert(fiatInput.value); - }); - fiatInput.addEventListener('input', () => { - lastModifiedField = 'fiat'; - }); - - // Add event listener for the select box to change the conversion - selectBox.addEventListener('change', () => { - if (lastModifiedField === 'xmr') { - xmrConvert(selectBox.value) +const resetActivity = () => lastActivity = Date.now() +const checkInactivity = () => { + if (Date.now() - lastActivity > inactivityTimeout) { + console.log('Inactivity detected, stopping exchange rate updates'); + stopFetching(); } else { - fiatConvert(selectBox.value) + requestAnimationFrame(checkInactivity); } - }); +} - // Hide the conversion buttons if JavaScript is enabled - convertXMRToFiatBtn.style.display = 'none'; - convertFiatToXMRBtn.style.display = 'none'; +document.addEventListener('focus', () => { + const focused = document.hasFocus(); + console.log(`Page is ${focused ? 'visible' : 'hidden'}`); - // Fetch updated exchange rates immediately, then every 5 seconds - fetchUpdatedExchangeRates(); - setInterval(fetchUpdatedExchangeRates, 5000); + if (focused && !updateInterval) { + console.log('Restarting exchange rate updates'); + startFetching(); + + resetActivity(); + requestAnimationFrame(checkInactivity); + } else { + stopFetching(); + } +}); +window.addEventListener('mousemove', resetActivity); +window.addEventListener('keydown', resetActivity); +window.addEventListener('touchstart', resetActivity); + +requestAnimationFrame(checkInactivity); + +document.addEventListener('DOMContentLoaded', () => { + const copyXMRBtn = document.getElementById('copyXMRBtn'); + const copyFiatBtn = document.getElementById('copyFiatBtn'); + const xmrInput = document.getElementById('xmrInput'); + const fiatInput = document.getElementById('fiatInput'); + const selectBox = document.getElementById('selectBox'); + const convertXMRToFiatBtn = document.getElementById('convertXMRToFiat'); + const convertFiatToXMRBtn = document.getElementById('convertFiatToXMR'); + const fiatButtons = document.querySelectorAll('.fiat-btn'); + + // Add event listeners for the currency buttons + fiatButtons.forEach(button => { + button.addEventListener('click', (e) => { + e.preventDefault(); + selectBox.value = button.textContent; + runConvert(); + history.pushState(null, '', `?in=${button.textContent}`); + }); + }); + + // Add event listeners for the copy buttons + copyXMRBtn.addEventListener('click', copyToClipboardXMR); + copyFiatBtn.addEventListener('click', copyToClipboardFiat); + + // Add event listeners for the XMR input field + xmrInput.addEventListener('change', xmrConvert); + xmrInput.addEventListener('keyup', () => { + xmrInput.value = xmrInput.value.replace(/[^\.^,\d]/g, '').replace(/\,/, '.'); + if (xmrInput.value.split('.').length > 2) { + xmrInput.value = xmrInput.value.slice(0, -1); + } + xmrConvert(); + }); + xmrInput.addEventListener('input', () => lastModifiedField = 'xmr'); + + // Add event listeners for the fiat input field + fiatInput.addEventListener('change', fiatConvert); + fiatInput.addEventListener('keyup', () => { + fiatInput.value = fiatInput.value.replace(/[^\.^,\d]/g, '').replace(/\,/, '.'); + if (fiatInput.value.split('.').length > 2) { + fiatInput.value = fiatInput.value.slice(0, -1); + } + fiatConvert(); + }); + fiatInput.addEventListener('input', () => lastModifiedField = 'fiat'); + + // Add event listener for the select box to change the conversion + selectBox.addEventListener('change', runConvert); + + // Hide the conversion buttons if JavaScript is enabled + convertXMRToFiatBtn.style.display = 'none'; + convertFiatToXMRBtn.style.display = 'none'; + + // Fetch updated exchange rates immediately, then every 5 seconds + fetchUpdatedExchangeRates(true) + startFetching(); }); -function fetchUpdatedExchangeRates() { - fetch('/coingecko.php') +function fetchUpdatedExchangeRates(showAlert = false) { + fetch('/coingecko.php') .then(response => response.json()) .then(data => { - // Update the exchangeRates object with the new values - for (const [currency, value] of Object.entries(data)) { - exchangeRates[currency.toUpperCase()] = value.lastValue; - } - - updateTimeElement(data.time); - - // Re-execute the appropriate conversion function - if (lastModifiedField === 'xmr') { - xmrConvert(); - } else { - fiatConvert(); - } + // Update the exchangeRates object with the new values + for (const [currency, value] of Object.entries(data)) { + exchangeRates[currency.toUpperCase()] = value.lastValue; + } + + updateTimeElement(data.time); + + // Re-execute the appropriate conversion function + runConvert(); }) - .catch(error => console.error('Error fetching exchange rates:', error)); + .catch(e => { + const msg = `Error fetching exchange rates: ${e}`; + showAlert ? alert(msg) : console.error(msg); + }); } function updateTimeElement(unixTimestamp) { - const date = new Date(unixTimestamp * 1000); - const hours = String(date.getHours()).padStart(2, '0'); - const minutes = String(date.getMinutes()).padStart(2, '0'); - const seconds = String(date.getSeconds()).padStart(2, '0'); - const formattedTime = `${hours}:${minutes}:${seconds}`; - - const u = document.querySelector('u'); - u.textContent = formattedTime; - u.parentElement.innerHTML = u.parentElement.innerHTML.replace('Europe/Berlin', Intl.DateTimeFormat().resolvedOptions().timeZone); + const date = new Date(unixTimestamp * 1000); + const hours = String(date.getHours()).padStart(2, '0'); + const minutes = String(date.getMinutes()).padStart(2, '0'); + const seconds = String(date.getSeconds()).padStart(2, '0'); + const formattedTime = `${hours}:${minutes}:${seconds}`; + + const u = document.querySelector('u'); + u.textContent = formattedTime; + u.parentElement.innerHTML = u.parentElement.innerHTML.replace('Europe/Berlin', Intl.DateTimeFormat().resolvedOptions().timeZone); } -function copyToClipBoardXMR() { - var content = document.getElementById('xmrInput'); - content.select(); - document.execCommand('copy'); +function copyToClipboardXMR() { + const content = document.getElementById('xmrInput'); + content.select(); + + // Using deprecated execCommand for compatibility with older browsers + document.execCommand('copy'); } -function copyToClipBoardFiat() { - var content = document.getElementById('fiatInput'); - content.select(); - document.execCommand('copy'); +function copyToClipboardFiat() { + const content = document.getElementById('fiatInput'); + content.select(); + + // Using deprecated execCommand for compatibility with older browsers + document.execCommand('copy'); } -function fiatConvert(value) { - let fiatAmount = document.getElementById("fiatInput").value; - let xmrValue = document.getElementById("xmrInput"); - let selectBox = document.getElementById("selectBox").value; - - if (exchangeRates[selectBox]) { - let value = fiatAmount / exchangeRates[selectBox]; - xmrValue.value = value.toFixed(12); - } +function fiatConvert() { + const fiatAmount = document.getElementById('fiatInput').value; + const xmrValue = document.getElementById('xmrInput'); + const selectBox = document.getElementById('selectBox').value; + + if (exchangeRates[selectBox]) { + const value = fiatAmount / exchangeRates[selectBox]; + xmrValue.value = value.toFixed(12); + } } -function xmrConvert(value) { - let xmrAmount = document.getElementById("xmrInput").value; - let fiatValue = document.getElementById("fiatInput"); - let selectBox = document.getElementById("selectBox").value; - - if (exchangeRates[selectBox]) { - let value = xmrAmount * exchangeRates[selectBox]; - fiatValue.value = value.toFixed(selectBox == 'BTC' || selectBox == 'LTC' || selectBox == 'ETH' || selectBox == 'XAG' || selectBox == 'XAU' ? 8 : 2); - } -} - -window.copyToClipBoardXMR = copyToClipBoardXMR; -window.copyToClipBoardFiat = copyToClipBoardFiat; -window.fiatConvert = fiatConvert; -window.xmrConvert = xmrConvert; \ No newline at end of file +function xmrConvert() { + const xmrAmount = document.getElementById('xmrInput').value; + const fiatValue = document.getElementById('fiatInput'); + const selectBox = document.getElementById('selectBox').value; + + if (exchangeRates[selectBox]) { + const value = xmrAmount * exchangeRates[selectBox]; + fiatValue.value = value.toFixed(['BTC', 'LTC', 'ETH', 'XAG', 'XAU'].includes(selectBox) ? 8 : 2); + } +} \ No newline at end of file diff --git a/templates/index.php b/templates/index.php index 997cc41..c7c8575 100644 --- a/templates/index.php +++ b/templates/index.php @@ -138,6 +138,7 @@ <small class="cursor-default text-white" lang="<?php echo $lang_meta; ?>"> <?php echo $footer_links . $getmonero . $countrymonero; ?> + <?php echo $footer_html; ?> </small> </div> diff --git a/webpack.config.js b/webpack.config.js index d1a8d91..ea9d9cd 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -29,7 +29,9 @@ module.exports = { }), new PurgeCSSPlugin({ paths: glob.sync([ - path.join(__dirname, 'index.php') + path.join(__dirname, 'index.php'), + path.join(__dirname, 'src/js/*.js'), + path.join(__dirname, 'templates/*.php'), ]), safelist: ['tooltip', 'fade', 'show', 'bs-tooltip-top', 'tooltip-inner', 'tooltip-arrow', 'btn-equals', 'btn-arrow', 'alert', 'alert-warning'] })