Automatically keeps track of your vault transactions when vault sharing (GM_setValue storage)
// ==UserScript==
// @name Vault Sharing - Mobile Enhanced
// @namespace vault.sharing.mobile
// @version 3.1
// @description Automatically keeps track of your vault transactions when vault sharing (GM_setValue storage)
// @author ANITABURN
// @match https://www.torn.com/properties.php*
// @match https://www.torn.com/properties.php
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_deleteValue
// @run-at document-end
// @license MIT
// ==/UserScript==
(function () {
'use strict';
const isMobile = /Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|Mobile/i.test(navigator.userAgent) ||
window.innerWidth <= 768 ||
('ontouchstart' in window) ||
navigator.maxTouchPoints > 0;
let localStorageKey = "vault_sharing_mobile:settings";
let playerName = "";
window.addEventListener("hashchange", (event) => {
if (event.newURL.indexOf("vault") > -1) {
setTimeout(startVaultSharing, 1000);
}
}, false);
if (window.location.href.indexOf("vault") > -1) {
setTimeout(startVaultSharing, 1000);
}
function startVaultSharing() {
console.log('Starting vault sharing on mobile:', isMobile);
try {
let wsData = document.querySelector("#websocketConnectionData");
if (wsData) {
playerName = JSON.parse(wsData.innerText).playername;
} else {
let userLinks = document.querySelectorAll('a[href*="/profiles.php?XID="]');
if (userLinks.length > 0) {
playerName = userLinks[0].textContent.trim();
}
}
} catch (e) {
console.log('Could not get player name:', e);
playerName = "You";
}
console.log('Player name:', playerName);
let { startTime, ownStartingBalance, spouseStartingBalance, totalVaultBalance, playerDisplayName, spouseDisplayName, useTornPDA } = JSON.parse(localStorage.getItem(localStorageKey)) || {
startTime: new Date().toISOString().slice(0, 16),
ownStartingBalance: 0,
spouseStartingBalance: 0,
totalVaultBalance: 0,
playerDisplayName: playerName || "You",
spouseDisplayName: "Spouse",
useTornPDA: false
};
let isDarkMode = true;
function parseTransaction(transactionItem) {
try {
let dateEl = transactionItem.querySelector("span.transaction-date") || transactionItem.querySelector('[class*="date"]');
let timeEl = transactionItem.querySelector("span.transaction-time") || transactionItem.querySelector('[class*="time"]');
let userEl = transactionItem.querySelector(".user.name") || transactionItem.querySelector('a[href*="XID="]');
let typeEl = transactionItem.querySelector(".type") || transactionItem.querySelector('[class*="type"]');
let amountEl = transactionItem.querySelector(".amount") || transactionItem.querySelector('[class*="amount"]');
let balanceEl = transactionItem.querySelector(".balance") || transactionItem.querySelector('[class*="balance"]');
if (!dateEl || !timeEl || !userEl || !typeEl || !amountEl) {
return null;
}
let dateStr = dateEl.innerText.trim();
let timeStr = timeEl.innerText.trim();
let dateParts = dateStr.split("/");
if (dateParts.length !== 3) {
return null;
}
let [day, month, year] = dateParts;
let fullYear = year.length === 2 ? '20' + year : year;
let datetime = new Date(`${fullYear}-${month.padStart(2, '0')}-${day.padStart(2, '0')}T${timeStr}Z`);
if (isNaN(datetime.getTime())) {
return null;
}
let userName = userEl.title ? userEl.title.split(" ")[0] : userEl.textContent.trim();
let type = typeEl.innerText.replace(/[^A-z]/g, "");
let amount = parseInt(amountEl.innerText.replace(/[^0-9]/g, ""));
let balance = balanceEl ? parseInt(balanceEl.innerText.replace(/[^0-9]/g, "")) : 0;
return {
datetime: datetime,
name: userName,
type: type,
amount: amount,
originalBalance: balance
};
} catch (e) {
console.log('Error parsing transaction:', e, transactionItem);
return null;
}
}
function readTransactionData() {
let transactionData = {};
let selectors = [
".vault-trans-wrap ul li[transaction_id]",
".vault-trans-wrap li",
'[class*="vault"] li',
'[class*="transaction"]'
];
let transactionListItems = [];
for (let selector of selectors) {
transactionListItems = document.querySelectorAll(selector);
if (transactionListItems.length > 0) break;
}
console.log(`Found ${transactionListItems.length} transaction items on page`);
for (let item of transactionListItems) {
let parsedTransaction = parseTransaction(item);
if (parsedTransaction) {
let transactionId = item.getAttribute("transaction_id") ||
item.getAttribute("data-id") ||
`${parsedTransaction.datetime.getTime()}-${parsedTransaction.name}-${parsedTransaction.amount}-${parsedTransaction.type}`;
transactionData[transactionId] = parsedTransaction;
}
}
return transactionData;
}
function syncAndGetAllTransactions() {
let storedData = GM_getValue('transactions', {});
let allTransactions = storedData;
const currentTransactions = readTransactionData();
const startTimeAsDate = new Date(startTime + ':00Z');
let newTransactionsFound = false;
let skippedOld = 0;
let skippedDuplicate = 0;
for (let [id, transaction] of Object.entries(currentTransactions)) {
if (transaction.datetime <= startTimeAsDate) {
skippedOld++;
continue;
}
if (allTransactions[id]) {
skippedDuplicate++;
continue;
}
allTransactions[id] = {
...transaction,
datetime: transaction.datetime.toISOString()
};
newTransactionsFound = true;
}
if (newTransactionsFound) {
GM_setValue('transactions', allTransactions);
let newCount = Object.keys(currentTransactions).length - skippedOld - skippedDuplicate;
console.log(`Synced ${newCount} new transaction(s). Total cached: ${Object.keys(allTransactions).length}. Skipped: ${skippedOld} old, ${skippedDuplicate} duplicates`);
}
for (let id in allTransactions) {
if (typeof allTransactions[id].datetime === 'string') {
allTransactions[id].datetime = new Date(allTransactions[id].datetime);
}
}
return allTransactions;
}
function formatBalance(balance) {
return (balance < 0 ? "-" : "") + "$" + Math.abs(balance).toLocaleString();
}
function showBalances(ownBalance, spouseBalance) {
let ownEl = document.getElementById("vault-sharing-own-balance");
let spouseEl = document.getElementById("vault-sharing-spouse-balance");
let totalEl = document.getElementById("vault-sharing-total-balance");
if (ownEl) ownEl.innerText = formatBalance(ownBalance);
if (spouseEl) spouseEl.innerText = formatBalance(spouseBalance);
if (totalEl) totalEl.innerText = formatBalance(ownBalance + spouseBalance);
}
function calculateBalances() {
try {
let transactionData = syncAndGetAllTransactions();
let ownBalance = ownStartingBalance;
let spouseBalance = spouseStartingBalance;
let allTransactions = Object.entries(transactionData)
.filter(e => e[1] && e[1].datetime)
.sort((a, b) => a[1].datetime - b[1].datetime);
let skipFirst = useTornPDA && allTransactions.length > 0;
for (let i = 0; i < allTransactions.length; i++) {
let [id, transaction] = allTransactions[i];
if (skipFirst && i === 0) {
console.log('Skipping first transaction (TornPDA mode):', transaction.name, transaction.type, transaction.amount);
continue;
}
let amount = parseInt(transaction.type === "Deposit" ? transaction.amount : -transaction.amount);
if (transaction.name === playerName || transaction.name.toLowerCase().includes(playerName.toLowerCase())) {
ownBalance += amount;
} else {
spouseBalance += amount;
}
}
return { ownBalance, spouseBalance };
} catch (error) {
console.error('Error in calculateBalances:', error);
return { ownBalance: 0, spouseBalance: 0 };
}
}
function calculateAndShowBalances() {
let result = calculateBalances();
showBalances(result.ownBalance, result.spouseBalance);
}
function saveSettings() {
let ownBalanceInput = document.getElementById("vault-sharing-own-start-balance");
let spouseBalanceInput = document.getElementById("vault-sharing-spouse-start-balance");
let totalVaultBalanceInput = document.getElementById("vault-sharing-total-vault-balance");
let startTimeInput = document.getElementById("vault-sharing-start-time");
let playerNameInput = document.getElementById("vault-sharing-player-name");
let spouseNameInput = document.getElementById("vault-sharing-spouse-name");
let useTornPDAInput = document.getElementById("vault-sharing-use-tornpda");
if (!ownBalanceInput || !spouseBalanceInput || !startTimeInput) return;
let ownBalanceSetting = Number(ownBalanceInput.value.replace(/[^0-9]/g, ""));
let spouseBalanceSetting = Number(spouseBalanceInput.value.replace(/[^0-9]/g, ""));
let totalVaultBalanceSetting = totalVaultBalanceInput ? Number(totalVaultBalanceInput.value.replace(/[^0-9]/g, "")) : 0;
let startTimeSetting = startTimeInput.value;
let playerNameSetting = playerNameInput ? playerNameInput.value.trim() : playerDisplayName;
let spouseNameSetting = spouseNameInput ? spouseNameInput.value.trim() : spouseDisplayName;
let useTornPDASetting = useTornPDAInput ? useTornPDAInput.checked : false;
let startTimeChanged = startTime !== startTimeSetting;
let balancesChanged = ownStartingBalance !== ownBalanceSetting || spouseStartingBalance !== spouseBalanceSetting || totalVaultBalance !== totalVaultBalanceSetting;
let tornPDAChanged = useTornPDA !== useTornPDASetting;
if (startTimeChanged || balancesChanged || tornPDAChanged) {
console.log('Settings changed - clearing transaction cache');
GM_deleteValue('transactions');
}
localStorage.setItem(localStorageKey, JSON.stringify({
startTime: startTimeSetting,
ownStartingBalance: ownBalanceSetting,
spouseStartingBalance: spouseBalanceSetting,
totalVaultBalance: totalVaultBalanceSetting,
playerDisplayName: playerNameSetting,
spouseDisplayName: spouseNameSetting,
useTornPDA: useTornPDASetting
}));
startTime = startTimeSetting;
ownStartingBalance = ownBalanceSetting;
spouseStartingBalance = spouseBalanceSetting;
totalVaultBalance = totalVaultBalanceSetting;
playerDisplayName = playerNameSetting;
spouseDisplayName = spouseNameSetting;
useTornPDA = useTornPDASetting;
toggleEditMode(false);
showStatus('Settings saved!', false);
}
function handleSave() {
saveSettings();
calculateAndShowBalances();
}
function handleSetCurrent() {
let result = calculateBalances();
let ownBalanceInput = document.getElementById("vault-sharing-own-start-balance");
let spouseBalanceInput = document.getElementById("vault-sharing-spouse-start-balance");
let startTimeInput = document.getElementById("vault-sharing-start-time");
if (ownBalanceInput) ownBalanceInput.value = result.ownBalance.toLocaleString();
if (spouseBalanceInput) spouseBalanceInput.value = result.spouseBalance.toLocaleString();
if (startTimeInput) startTimeInput.value = new Date().toISOString().slice(0, 16);
showStatus('Updated to current values!', false);
}
function toggleEditMode(forceState = null) {
let editSection = document.getElementById("vault-sharing-edit-section");
let editButton = document.getElementById("vault-sharing-edit-btn");
let playerNameEl = document.getElementById("vault-sharing-player-display-name");
let spouseNameEl = document.getElementById("vault-sharing-spouse-display-name");
let currentlyEditing = editSection && editSection.style.display !== 'none';
let shouldEdit = forceState !== null ? forceState : !currentlyEditing;
if (editSection) {
editSection.style.display = shouldEdit ? 'block' : 'none';
}
if (editButton) {
editButton.textContent = shouldEdit ? 'Cancel' : 'Edit';
editButton.style.background = shouldEdit ? '#68acff' : '#68acff';
}
if (playerNameEl) playerNameEl.textContent = playerDisplayName;
if (spouseNameEl) spouseNameEl.textContent = spouseDisplayName;
}
function showStatus(message, isError = false) {
let statusEl = document.getElementById("vault-sharing-status");
if (statusEl) {
statusEl.textContent = message;
statusEl.style.display = 'block';
statusEl.style.color = isError ? '#68acff' : '#e168ff';
statusEl.style.background = isError ? 'rgba(255,155,200,0.1)' : 'rgba(155,183,255,0.1)';
statusEl.style.padding = '8px';
statusEl.style.borderRadius = '4px';
statusEl.style.marginTop = '10px';
setTimeout(() => {
statusEl.style.display = 'none';
}, isMobile ? 5000 : 3000);
}
}
function showCachePopup() {
try {
console.log('Opening cache popup...');
let existing = document.getElementById("vault-sharing-cache-popup");
if (existing) {
existing.remove();
document.getElementById("vault-cache-overlay")?.remove();
return;
}
let allTransactions = syncAndGetAllTransactions();
console.log('All transactions:', allTransactions);
let transactionArray = Object.entries(allTransactions)
.filter(([id, t]) => t && t.datetime)
.map(([id, t]) => ({ id, ...t }))
.sort((a, b) => {
if (!a.datetime || !b.datetime) return 0;
return b.datetime - a.datetime;
});
console.log('Transaction array:', transactionArray);
let popup = document.createElement('div');
popup.id = 'vault-sharing-cache-popup';
popup.style.cssText = `
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: #3a3a3a;
border: 2px solid #68ebff;
border-radius: 8px;
padding: 20px;
max-width: ${isMobile ? '90%' : '600px'};
max-height: ${isMobile ? '80%' : '70%'};
overflow-y: auto;
z-index: 10000;
box-shadow: 0 4px 20px rgba(0,0,0,0.5);
`;
let header = `
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; border-bottom: 1px solid #555; padding-bottom: 10px;">
<h3 style="margin: 0; color: #68ebff; font-size: ${isMobile ? '16px' : '14px'};">Transaction Cache (${transactionArray.length})</h3>
<button id="vault-cache-close" style="background: #e168ff; color: white; border: none; padding: 6px 12px; border-radius: 3px; cursor: pointer; font-size: ${isMobile ? '12px' : '11px'};">Close</button>
</div>
`;
let transactionHTML = '';
if (transactionArray.length === 0) {
transactionHTML = '<div style="color: #aaa; text-align: center; padding: 20px;">No transactions in cache</div>';
} else {
transactionHTML = '<div style="font-size: ' + (isMobile ? '12px' : '11px') + ';">';
for (let t of transactionArray) {
try {
let typeColor = t.type === 'Deposit' ? '#68ebff' : '#e168ff';
let dateStr = 'Unknown Date';
if (t.datetime && t.datetime.toISOString) {
let d = new Date(t.datetime);
let day = String(d.getUTCDate()).padStart(2, '0');
let month = String(d.getUTCMonth() + 1).padStart(2, '0');
let year = String(d.getUTCFullYear()).slice(-2);
let hours = String(d.getUTCHours()).padStart(2, '0');
let minutes = String(d.getUTCMinutes()).padStart(2, '0');
let seconds = String(d.getUTCSeconds()).padStart(2, '0');
dateStr = `${day}/${month}/${year} ${hours}:${minutes}:${seconds}`;
}
let amountStr = '$' + (t.amount ? t.amount.toLocaleString() : '0');
transactionHTML += `
<div style="background: #4a4a4a; padding: ${isMobile ? '10px' : '8px'}; margin-bottom: 8px; border-radius: 4px; border-left: 3px solid ${typeColor};">
<div style="display: flex; justify-content: space-between; margin-bottom: 4px;">
<span style="color: #ddd; font-weight: bold;">${t.name || 'Unknown'}</span>
<span style="color: ${typeColor}; font-weight: bold;">${t.type || 'Unknown'}</span>
</div>
<div style="display: flex; justify-content: space-between;">
<span style="color: #aaa; font-size: ${isMobile ? '11px' : '10px'};">${dateStr}</span>
<span style="color: #ddd;">${amountStr}</span>
</div>
</div>
`;
} catch (err) {
console.error('Error rendering transaction:', t, err);
}
}
transactionHTML += '</div>';
}
let overlay = document.createElement('div');
overlay.id = 'vault-cache-overlay';
overlay.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.7);
z-index: 9999;
`;
popup.innerHTML = header + transactionHTML;
document.body.appendChild(overlay);
document.body.appendChild(popup);
document.getElementById('vault-cache-close').addEventListener('click', () => {
popup.remove();
overlay.remove();
});
overlay.addEventListener('click', () => {
popup.remove();
overlay.remove();
});
} catch (error) {
console.error('Error showing cache popup:', error);
console.error('Error stack:', error.stack);
alert('Error showing cache popup: ' + error.message + '\nCheck console for details.');
}
}
function formatMoneyInput(event) {
let value = event.target.value.replace(/[^\d]/g, '');
if (value) {
event.target.value = parseInt(value).toLocaleString();
}
}
function addUI() {
let existing = document.getElementById("vault-sharing-container");
if (existing) existing.remove();
let mobileStyles = isMobile ? `
padding: 10px;
margin: 5px;
font-size: 14px;
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
`: `
padding: 8px;
margin: 3px 0;
`;
let html = `
<div id="vault-sharing-container" style="
background: #3a3a3a;
border: 1px solid #4a4a4a;
border-radius: 6px;
${mobileStyles}
position: relative;
color: #ddd;
">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
<h4 style="margin: 0; color: #ddd; font-size: ${isMobile ? '16px' : '14px'};">Vault Sharing</h4>
<div style="display: flex; gap: 4px;">
<button id="vault-sharing-cache-btn" style="
background: #e168ff;
color: white;
border: none;
padding: ${isMobile ? '6px 10px' : '4px 8px'};
border-radius: 3px;
cursor: pointer;
font-size: ${isMobile ? '12px' : '11px'};
min-height: ${isMobile ? '32px' : '24px'};
">Cache</button>
<button id="vault-sharing-edit-btn" style="
background: #68acff;
color: white;
border: none;
padding: ${isMobile ? '6px 10px' : '4px 8px'};
border-radius: 3px;
cursor: pointer;
font-size: ${isMobile ? '12px' : '11px'};
min-height: ${isMobile ? '32px' : '24px'};
">Edit</button>
</div>
</div>
<div style="display: flex; gap: 8px; margin-bottom: 8px;">
<div style="flex: 1; background: #4a4a4a; padding: ${isMobile ? '10px' : '8px'}; border-radius: 4px; text-align: center; border-left: 3px solid #e168ff;">
<div style="font-size: ${isMobile ? '11px' : '10px'}; color: #aaa; margin-bottom: 4px; font-weight: bold;">
<span id="vault-sharing-player-display-name">${playerDisplayName}</span>
</div>
<div id="vault-sharing-own-balance" style="font-size: ${isMobile ? '14px' : '12px'}; color: #e168ff; font-weight: bold;">Calculating...</div>
</div>
<div style="flex: 1; background: #4a4a4a; padding: ${isMobile ? '10px' : '8px'}; border-radius: 4px; text-align: center; border-left: 3px solid #68acff;">
<div style="font-size: ${isMobile ? '11px' : '10px'}; color: #aaa; margin-bottom: 4px; font-weight: bold;">
<span id="vault-sharing-spouse-display-name">${spouseDisplayName}</span>
</div>
<div id="vault-sharing-spouse-balance" style="font-size: ${isMobile ? '14px' : '12px'}; color: #68acff; font-weight: bold;">Calculating...</div>
</div>
</div>
<div style="text-align: center; padding: ${isMobile ? '10px' : '8px'}; background: #4a4a4a; border-radius: 4px; border-left: 3px solid #68ebff;">
<div style="font-size: ${isMobile ? '11px' : '10px'}; color: #aaa; margin-bottom: 4px; font-weight: bold;">Total Balance</div>
<div id="vault-sharing-total-balance" style="font-size: ${isMobile ? '14px' : '12px'}; color: #68ebff; font-weight: bold;">$0</div>
</div>
<div id="vault-sharing-edit-section" style="display: none; margin-top: 10px; padding: 10px; background: #4a4a4a; border-radius: 4px; border: 1px solid #555;">
<div style="display: ${isMobile ? 'block' : 'flex'}; gap: 8px; margin-bottom: 10px;">
<div style="flex: 1; margin-bottom: ${isMobile ? '8px' : '0'};">
<label style="display: block; margin-bottom: 2px; font-size: ${isMobile ? '12px' : '11px'}; color: #ddd;">Your Name:</label>
<input id="vault-sharing-player-name" type="text" value="${playerDisplayName}" style="
width: 100%;
padding: ${isMobile ? '6px' : '4px'};
border: 1px solid #666;
border-radius: 3px;
font-size: ${isMobile ? '14px' : '12px'};
box-sizing: border-box;
background: #333;
color: #ddd;
">
</div>
<div style="flex: 1;">
<label style="display: block; margin-bottom: 2px; font-size: ${isMobile ? '12px' : '11px'}; color: #ddd;">Spouse Name:</label>
<input id="vault-sharing-spouse-name" type="text" value="${spouseDisplayName}" style="
width: 100%;
padding: ${isMobile ? '6px' : '4px'};
border: 1px solid #666;
border-radius: 3px;
font-size: ${isMobile ? '14px' : '12px'};
box-sizing: border-box;
background: #333;
color: #ddd;
">
</div>
</div>
<div style="margin-bottom: 10px;">
<label style="display: block; margin-bottom: 2px; font-size: ${isMobile ? '12px' : '11px'}; color: #ddd;">Total Vault Balance (at start time):</label>
<input id="vault-sharing-total-vault-balance" type="text" value="${(totalVaultBalance || 0).toLocaleString()}" style="
width: 100%;
padding: ${isMobile ? '6px' : '4px'};
border: 1px solid #666;
border-radius: 3px;
font-size: ${isMobile ? '14px' : '12px'};
box-sizing: border-box;
background: #333;
color: #ddd;
">
</div>
<div style="display: ${isMobile ? 'block' : 'flex'}; gap: 8px; margin-bottom: 10px;">
<div style="flex: 1; margin-bottom: ${isMobile ? '8px' : '0'};">
<label style="display: block; margin-bottom: 2px; font-size: ${isMobile ? '12px' : '11px'}; color: #ddd;">Your Starting Balance:</label>
<input id="vault-sharing-own-start-balance" type="text" value="${ownStartingBalance.toLocaleString()}" style="
width: 100%;
padding: ${isMobile ? '6px' : '4px'};
border: 1px solid #666;
border-radius: 3px;
font-size: ${isMobile ? '14px' : '12px'};
box-sizing: border-box;
background: #333;
color: #ddd;
">
</div>
<div style="flex: 1;">
<label style="display: block; margin-bottom: 2px; font-size: ${isMobile ? '12px' : '11px'}; color: #ddd;">Spouse Starting Balance:</label>
<input id="vault-sharing-spouse-start-balance" type="text" value="${spouseStartingBalance.toLocaleString()}" style="
width: 100%;
padding: ${isMobile ? '6px' : '4px'};
border: 1px solid #666;
border-radius: 3px;
font-size: ${isMobile ? '14px' : '12px'};
box-sizing: border-box;
background: #333;
color: #ddd;
">
</div>
</div>
<div style="margin-bottom: 10px;">
<label style="display: block; margin-bottom: 2px; font-size: ${isMobile ? '12px' : '11px'}; color: #ddd;">Start Date & Time:</label>
<input id="vault-sharing-start-time" type="datetime-local" value="${startTime}" style="
width: 100%;
padding: ${isMobile ? '6px' : '4px'};
border: 1px solid #666;
border-radius: 3px;
font-size: ${isMobile ? '14px' : '12px'};
box-sizing: border-box;
background: #333;
color: #ddd;
">
</div>
<div style="margin-bottom: 10px; display: flex; align-items: center; gap: 8px;">
<input id="vault-sharing-use-tornpda" type="checkbox" ${useTornPDA ? 'checked' : ''} style="
width: 16px;
height: 16px;
cursor: pointer;
flex-shrink: 0;
opacity: 1;
position: relative;
z-index: 1;
-webkit-appearance: checkbox;
-moz-appearance: checkbox;
appearance: checkbox;
">
<label for="vault-sharing-use-tornpda" style="font-size: ${isMobile ? '12px' : '11px'}; color: #ddd; cursor: pointer; margin: 0; line-height: 1.4;">
Balances from TornPDA with backdated start date (skip first transaction)
</label>
</div>
<div style="display: ${isMobile ? 'block' : 'flex'}; gap: 6px;">
<button id="vault-sharing-save" style="
background: #e168ff;
color: white;
border: none;
padding: ${isMobile ? '8px 12px' : '6px 10px'};
border-radius: 3px;
cursor: pointer;
font-size: ${isMobile ? '12px' : '11px'};
flex: 1;
margin-bottom: ${isMobile ? '6px' : '0'};
min-height: ${isMobile ? '36px' : '28px'};
">Save</button>
<button id="vault-sharing-update" style="
background: #68acff;
color: white;
border: none;
padding: ${isMobile ? '8px 12px' : '6px 10px'};
border-radius: 3px;
cursor: pointer;
font-size: ${isMobile ? '12px' : '11px'};
flex: 1;
min-height: ${isMobile ? '36px' : '28px'};
">Set to Current</button>
</div>
<div id="vault-sharing-status" style="display: none; margin-top: 8px; font-size: ${isMobile ? '12px' : '11px'};"></div>
</div>
</div>
`;
let insertTarget = document.querySelector(".vault-trans-wrap") ||
document.querySelector('[class*="vault-trans"]') ||
document.querySelector('[class*="property-option"]') ||
document.querySelector('#properties-page-wrap');
if (insertTarget) {
insertTarget.insertAdjacentHTML("beforebegin", html);
document.getElementById("vault-sharing-save")?.addEventListener("click", handleSave);
document.getElementById("vault-sharing-update")?.addEventListener("click", handleSetCurrent);
document.getElementById("vault-sharing-cache-btn")?.addEventListener("click", showCachePopup);
document.getElementById("vault-sharing-edit-btn")?.addEventListener("click", () => toggleEditMode());
['vault-sharing-own-start-balance', 'vault-sharing-spouse-start-balance', 'vault-sharing-total-vault-balance'].forEach(id => {
let input = document.getElementById(id);
if (input) {
input.addEventListener('input', formatMoneyInput);
if (isMobile) {
input.addEventListener('focus', () => {
document.querySelector('meta[name="viewport"]')?.setAttribute('content', 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no');
});
input.addEventListener('blur', () => {
document.querySelector('meta[name="viewport"]')?.setAttribute('content', 'width=device-width, initial-scale=1.0');
});
}
}
});
toggleEditMode(false);
console.log('UI added successfully');
return true;
} else {
console.log('Could not find insertion target');
return false;
}
}
function setupObservers() {
let mutationConfig = { attributes: false, childList: true, subtree: true };
let isCalculating = false;
let calculationTimeout = null;
let transactionCallback = (mutationList, observer) => {
if (isCalculating) return;
clearTimeout(calculationTimeout);
calculationTimeout = setTimeout(() => {
isCalculating = true;
calculateAndShowBalances();
setTimeout(() => { isCalculating = false; }, 500);
}, 300);
};
let initialCallback = (mutationList, observer) => {
for (let mutation of mutationList) {
if (mutation.type === "childList") {
for (let node of mutation.addedNodes) {
if (node.nodeType === Node.ELEMENT_NODE) {
if (node.classList?.contains("property-option") ||
node.querySelector?.('.vault-trans-wrap') ||
node.classList?.contains("vault-trans-wrap")) {
if (addUI()) {
calculateAndShowBalances();
let transactionList = document.querySelector(".vault-trans-wrap ul") ||
document.querySelector('[class*="vault-trans"] ul');
if (transactionList) {
let transactionObserver = new MutationObserver(transactionCallback);
transactionObserver.observe(transactionList, mutationConfig);
}
}
}
}
}
}
}
};
let pageWrap = document.getElementById("properties-page-wrap");
if (pageWrap) {
let pageObserver = new MutationObserver(initialCallback);
pageObserver.observe(pageWrap, mutationConfig);
}
let vaultTransWrap = document.querySelector(".vault-trans-wrap");
if (vaultTransWrap) {
if (addUI()) {
calculateAndShowBalances();
let transactionList = vaultTransWrap.querySelector("ul");
if (transactionList) {
let transactionObserver = new MutationObserver(transactionCallback);
transactionObserver.observe(transactionList, mutationConfig);
}
}
}
}
setupObservers();
}
if (isMobile) {
let style = document.createElement('style');
style.textContent = `
#vault-sharing-container input {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
#vault-sharing-container button {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
#vault-sharing-container button:active {
transform: scale(0.98);
}
#vault-sharing-container input:focus {
border-color: #e94560 !important;
}
`;
document.head.appendChild(style);
}
})();