Skip to content

Instantly share code, notes, and snippets.

@darkowic
Last active November 13, 2022 21:06
Show Gist options
  • Save darkowic/f4454f06658ec2ef1e2dbe0d6e8648d2 to your computer and use it in GitHub Desktop.
Save darkowic/f4454f06658ec2ef1e2dbe0d6e8648d2 to your computer and use it in GitHub Desktop.
mBank: Saldo transakcji

mBank saldo

Wpływy - Wydatki = Saldo - skrypt dodaje pole "Saldo" w widoku histori transakcji dla serwisu transakcyjnego mbank.pl.

Przed:

mBank historia transakcji wpływy i wydatki

Po:

mBank historia transakcji wpływy i wydatki plus saldo

Instalacja

  1. Zainstaluj rozszerzenie przeglądarki https://www.tampermonkey.net/
  2. Kliknij przycisk "Raw" dla poniższego mbank-saldo.user.js skrytpu i zainstaluje skrypt podążając za instrukcjami.
// ==UserScript==
// @name mBank Saldo
// @namespace https://gist.github.com/darkowic/f4454f06658ec2ef1e2dbe0d6e8648d2
// @version 0.1
// @description Wpływy - Wydatki = Saldo - skrypt dodaje pole "Saldo" w widoku histori transakcji dla serwisu transakcyjnego mbank.pl.
// @author https://github.com/darkowic
// @match https://online.mbank.pl/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=mbank.pl
// @grant none
// @downloadURL https://gist.github.com/darkowic/f4454f06658ec2ef1e2dbe0d6e8648d2/raw/mbank-saldo.user.js
// ==/UserScript==
(async function () {
("use strict");
function getAmountNode(node) {
return node.querySelector('[data-component="Amount"]');
}
function getSummaryNodes() {
const operationsCountNode = document.querySelector(
'[testid="OperationsSummary:operationsCount"]'
);
return operationsCountNode.previousSibling;
}
function getAmount(node) {
return parseFloat(
getAmountNode(node).textContent.replaceAll(/\s/g, "").replaceAll(",", ".")
);
}
function setAmount(node, amount) {
const intPart = Math.trunc(amount);
const decimalPart = amount - intPart;
node.removeChild(node.firstChild);
node.prepend(
(intPart < 0 ? "-" : "") +
Math.abs(intPart)
// add non-breaking space between every 3 digits
.toString()
.split("")
.reduce((acc, next, index, string) => {
const indexFromEnd = string.length - index;
if (
indexFromEnd !== string.length &&
indexFromEnd &&
indexFromEnd % 3 === 0
) {
acc += "\xa0";
}
return (acc += next);
}, "")
);
const decimalPartNode = node.children[0];
decimalPartNode.removeChild(decimalPartNode.lastChild);
decimalPartNode.append(
Math.abs(decimalPart).toFixed(2).toString().slice(2)
);
}
const TOTAL_NODE_ID = "script-total-node";
function createTotalNode(node) {
const totalNode = node.cloneNode(true);
totalNode.setAttribute("id", TOTAL_NODE_ID);
totalNode.firstChild.textContent = "Saldo";
const amountNode = getAmountNode(totalNode);
setAmount(amountNode, 0);
return totalNode;
}
function getOrCreateTotalNode(parentNode) {
const totalNode = document.getElementById(TOTAL_NODE_ID);
if (totalNode) {
return totalNode;
}
const newTotalNode = createTotalNode(parentNode.children[0]);
parentNode.appendChild(newTotalNode);
return newTotalNode;
}
function updateTotal() {
const parent = getSummaryNodes();
const income = getAmount(parent.children[0]);
const spending = getAmount(parent.children[1]);
const totalNode = getOrCreateTotalNode(parent);
const total = parseFloat(income - Math.abs(spending)).toFixed(2);
setAmount(getAmountNode(totalNode), total);
}
async function waitForSummaryNode() {
return new Promise((resolve) => {
const observer = new MutationObserver((_, obs) => {
try {
const summaryNode = getSummaryNodes();
obs.disconnect();
resolve(summaryNode);
} catch (e) {
// waiting
}
});
observer.observe(document.getElementById("root"), {
childList: true,
subtree: true,
});
});
}
function watchChange(node, callback) {
const observer = new MutationObserver(() => {
callback();
});
observer.observe(node, {
characterData: true,
subtree: true,
});
return observer;
}
function doesPathMatch(paths) {
return paths.some((path) => window.location.pathname.startsWith(path));
}
function runUnderPaths(paths, callback) {
let isUnderCorrectPath = false;
let cleanupSave = null;
const observer = new MutationObserver(() => {
if (doesPathMatch(paths)) {
if (!isUnderCorrectPath) {
callback().then((cleanup) => {
if (isUnderCorrectPath) {
cleanupSave = cleanup;
} else {
cleanup();
}
});
}
isUnderCorrectPath = true;
} else {
isUnderCorrectPath = false;
if (cleanupSave) {
cleanupSave();
cleanupSave = null;
}
}
});
observer.observe(document.body, {
childList: true,
subtree: true,
});
return observer;
}
runUnderPaths(["/history"], async () => {
const summaryNode = await waitForSummaryNode();
updateTotal();
const observer1 = watchChange(summaryNode.children[0], updateTotal);
const observer2 = watchChange(summaryNode.children[1], updateTotal);
return () => {
observer1.disconnect();
observer2.disconnect();
};
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment