// ==UserScript==
// @name Web3 OKX Demo Trade Addon
// @namespace http://tampermonkey.net/
// @version 1.1
// @description Add Demo Trading tab and functional demo trade panel
// @author mamiis
// @match https://web3.okx.com/*
// @grant none
// @license MIT
// ==/UserScript==
(function() {
'use strict';
let demoMode = false;
let demoBalance = 1000;
let demoPanelVisible = false;
let currentTokenPrice = 0.001;
let currentSolPrice = 100;
let currentTokenSymbol = 'TOKEN';
let currentTokenImage = '';
let totalPnL = 0;
let totalPnLPercent = 0;
let tokenPnL = 0;
let tokenPnLPercent = 0;
let currentSettings = {
buySlippage: '15%',
sellSlippage: '20%',
buyAmounts: ['0.03', '0.05', '0.07'],
sellPercents: ['50%', '75%', '100%'],
serviceFee: '0.68%',
marketFee: '0.0004-0.0009 SOL'
};
// Customize amounts state
let customizeMode = false;
let tempBuyAmounts = [...currentSettings.buyAmounts];
let tempSellPercents = [...currentSettings.sellPercents];
let tempServiceFee = currentSettings.serviceFee;
// Sayfa değişikliklerini izlemek için observer
let pageObserver = null;
// Geçmiş işlemleri takip etmek için yeni değişkenler
// localStorage'dan portföyü yükle
let demoPortfolio = JSON.parse(localStorage.getItem('demoPortfolio')) || {
'SOL': { amount: 10, avgPrice: 100 },
'TOKEN': { amount: 0, avgPrice: 0 }
};
let totalRealizedPnL = parseFloat(localStorage.getItem('totalRealizedPnL')) || 0;
let totalTradeVolume = parseFloat(localStorage.getItem('totalTradeVolume')) || 0;
let tradeHistory = JSON.parse(localStorage.getItem('tradeHistory')) || [];
function initDemoTrading() {
console.log('Initializing Demo Trading...');
addDemoTab();
addDemoButton();
setupDemoMode();
loadSettingsFromRealPanel();
startPriceUpdates();
startSolPriceUpdates();
setupPageObserver();
}
function setupPageObserver() {
// Eğer observer zaten varsa, önce onu durdur
if (pageObserver) {
pageObserver.disconnect();
}
// Sayfa değişikliklerini izle
pageObserver = new MutationObserver(function(mutations) {
let shouldUpdate = false;
mutations.forEach(function(mutation) {
// Yeni node'lar eklendiğinde kontrol et
if (mutation.addedNodes && mutation.addedNodes.length > 0) {
for (let node of mutation.addedNodes) {
if (node.nodeType === 1) { // Element node
// Tab container değişti mi?
if (node.querySelector && (
node.querySelector('.dex-tabs-pane-list-container') ||
node.querySelector('.Q68nJL__dex') ||
node.querySelector('.vP8ohB__dex')
)) {
shouldUpdate = true;
break;
}
}
}
}
// Attribute değişiklikleri (active tab değişimi)
if (mutation.type === 'attributes' &&
(mutation.attributeName === 'class' || mutation.attributeName === 'data-pane-id')) {
shouldUpdate = true;
}
});
if (shouldUpdate) {
console.log('Page changed, updating demo components...');
setTimeout(updateDemoComponents, 500);
}
});
// Tüm sayfayı izle
pageObserver.observe(document.body, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['class', 'data-pane-id']
});
}
function updateDemoComponents() {
// Demo tab'ı kontrol et ve ekle
if (!document.querySelector('[data-pane-id="demo_trading"]')) {
addDemoTab();
}
// Demo butonunu kontrol et ve ekle
if (!document.querySelector('.demo-trade-button')) {
addDemoButton();
}
// Demo panel açıksa güncelle
if (demoPanelVisible) {
updateDemoPanelValues();
}
// Aktif tab'ı güncelle
updateActiveTab();
}
function loadSettingsFromRealPanel() {
const realPanel = document.querySelector('.cIknRK__dex:not(.demo-trade-panel)');
if (realPanel) {
// Buy slippage
const buySlippage = realPanel.querySelector('.Nox6EF__dex .font-500');
if (buySlippage) {
currentSettings.buySlippage = buySlippage.textContent || '15%';
}
// Sell slippage
const sellSlippageElements = realPanel.querySelectorAll('.Nox6EF__dex .font-500');
if (sellSlippageElements.length > 1) {
currentSettings.sellSlippage = sellSlippageElements[1].textContent || '20%';
}
// Buy amounts
const buyAmounts = realPanel.querySelectorAll('.H5f_ax__dex.Cg7h1c__dex .l5GPKh__dex');
if (buyAmounts.length >= 3) {
currentSettings.buyAmounts = [
buyAmounts[0].textContent || '0.03',
buyAmounts[1].textContent || '0.05',
buyAmounts[2].textContent || '0.07'
];
}
// Sell percents
const sellPercents = realPanel.querySelectorAll('.H5f_ax__dex.ODJ17x__dex .l5GPKh__dex');
if (sellPercents.length >= 3) {
currentSettings.sellPercents = [
sellPercents[0].textContent || '50%',
sellPercents[1].textContent || '75%',
sellPercents[2].textContent || '100%'
];
}
}
}
function getCurrentPrices() {
try {
const previousTokenSymbol = currentTokenSymbol;
let priceFound = false;
console.log('--- COIN PRICE SEARCH STARTED ---');
// 1. TOKEN PRICE BULMA - GÜNCELLENMİŞ VERSİYON
const priceDivs = document.querySelectorAll('.glKoFv__dex');
let priceValue = null;
// Tüm glKoFv__dex div'lerini kontrol et
for (let div of priceDivs) {
const labelDiv = div.querySelector('.KMsU5K__dex');
if (labelDiv && labelDiv.textContent === 'Price') {
const priceValueDiv = div.querySelector('.IMDFTB__dex');
if (priceValueDiv) {
priceValue = priceValueDiv.textContent.trim();
console.log('✅ PRICE TEXT FOUND:', priceValue);
break;
}
}
}
if (priceValue) {
let cleanedPrice = priceValue.replace('$', '').trim();
// Özel subscript karakterlerini sıfırlara çevir
const subscriptMap = {
'₀': 0, '₁': 1, '₂': 2, '₃': 3, '₄': 4,
'₅': 5, '₆': 6, '₇': 7, '₈': 8, '₉': 9
};
// "0.0₄17856" -> "0.000017856" formatını handle et
if (cleanedPrice.startsWith('0.0')) {
let finalPrice = '0.0';
let foundSubscript = false;
// String'i karakter karakter işle
for (let i = 2; i < cleanedPrice.length; i++) {
const char = cleanedPrice[i];
if (subscriptMap.hasOwnProperty(char) && !foundSubscript) {
// Subscript karakteri bulundu, bu kadar sıfır ekle
const zeroCount = subscriptMap[char];
finalPrice += '0'.repeat(zeroCount);
foundSubscript = true;
} else {
// Normal karakter, direkt ekle
finalPrice += char;
}
}
cleanedPrice = finalPrice;
console.log('🔄 FORMATTED PRICE:', cleanedPrice);
}
// Parse etmeyi dene
const parsedPrice = parseFloat(cleanedPrice);
if (!isNaN(parsedPrice) && parsedPrice > 0) {
currentTokenPrice = parsedPrice;
priceFound = true;
console.log('✅ PRICE SUCCESSFULLY PARSED:', {
original: priceValue,
formatted: cleanedPrice,
parsed: currentTokenPrice
});
} else {
console.log('❌ Price parsing failed:', cleanedPrice);
}
}
if (!priceFound) {
console.log('❌ No valid token price found');
// Hemen hata gösterme, belki sayfa henüz yüklenmedi
// Sadece debug için log bırak
currentTokenPrice = 0.001; // Geçici olarak default değer
} else {
console.log('✅ FINAL TOKEN PRICE:', currentTokenPrice);
}
console.log('--- COIN PRICE SEARCH ENDED ---');
// Token sembolü güncelleme
const tokenSymbolElement = document.querySelector('.B73M_W__dex');
if (tokenSymbolElement) {
const symbol = tokenSymbolElement.textContent.trim();
if (symbol && symbol !== 'SOL' && symbol !== 'USDC' && symbol !== 'USDT') {
/*if (previousTokenSymbol !== symbol) {
showDemoNotification(`Switched to ${symbol}, resetting token portfolio`, 'info');
demoPortfolio.TOKEN.amount = 0;
demoPortfolio.TOKEN.avgPrice = 0;
totalRealizedPnL = 0;
tradeHistory = [];
}*/
currentTokenSymbol = symbol;
const tokenImage = document.querySelector('.vP8ohB__dex img');
currentTokenImage = (tokenImage && tokenImage.src) ? tokenImage.src : 'https://web3.okx.com/cdn/web3/currency/token/large/501-FM6ZsWmVFA41D72NNNTk35ZrUSg2wkCerSNzg7Wjpump-108/type=default_90_0?v=1759520281357';
}
}
// PnL hesapla
calculatePnL();
} catch (e) {
console.error('❌ Price update error:', e);
currentTokenPrice = 0.001; // Hata durumunda geçici default
}
}
// SOL fiyatını API'den çek
function fetchSolPrice() {
fetch('https://api.coingecko.com/api/v3/simple/price?ids=solana&vs_currencies=usd')
.then(response => response.json())
.then(data => {
if (data.solana && data.solana.usd) {
currentSolPrice = data.solana.usd;
console.log('✅ SOL PRICE FROM API:', currentSolPrice);
// Panel açıksa değerleri güncelle
if (demoPanelVisible) {
updateDemoPanelValues();
}
}
})
.catch(error => {
console.log('❌ Coingecko API error, using default SOL price');
currentSolPrice = 100; // Fallback price
});
}
// SOL fiyatını periyodik olarak güncelle
function startSolPriceUpdates() {
// İlk güncellemeyi hemen yap
fetchSolPrice();
// Sonra her 15 saniyede bir güncelle
setInterval(fetchSolPrice, 15000);
}
function calculatePnL() {
console.log('🔄 Calculating PnL with current data:', {
tokenAmount: demoPortfolio.TOKEN.amount,
avgPrice: demoPortfolio.TOKEN.avgPrice,
currentPrice: currentTokenPrice,
realizedPnL: totalRealizedPnL
});
// Sıfırlama
tokenPnL = 0;
tokenPnLPercent = 0;
totalPnL = 0;
totalPnLPercent = 0;
// Aktif Token PnL - DOĞRU HESAPLAMA
if (demoPortfolio.TOKEN.amount > 0 && demoPortfolio.TOKEN.avgPrice > 0) {
const currentTokenValue = demoPortfolio.TOKEN.amount * currentTokenPrice;
const tokenCostBasis = demoPortfolio.TOKEN.amount * demoPortfolio.TOKEN.avgPrice;
tokenPnL = currentTokenValue - tokenCostBasis;
tokenPnLPercent = tokenCostBasis > 0 ? (tokenPnL / tokenCostBasis) * 100 : 0;
console.log('📊 Token PnL calculated:', {
currentValue: currentTokenValue,
costBasis: tokenCostBasis,
pnl: tokenPnL,
percent: tokenPnLPercent
});
}
// Total PnL = Aktif Token PnL + Realize Edilen PnL
totalPnL = tokenPnL + totalRealizedPnL;
// Total PnL Percent - BAŞLANGIÇ BAKİYESİNE GÖRE HESAPLA
const initialBalance = 1000; // Başlangıç bakiyesi
totalPnLPercent = initialBalance > 0 ? (totalPnL / initialBalance) * 100 : 0;
// NaN ve hata kontrolleri
if (isNaN(totalPnL)) totalPnL = 0;
if (isNaN(totalPnLPercent)) totalPnLPercent = 0;
if (isNaN(tokenPnL)) tokenPnL = 0;
if (isNaN(tokenPnLPercent)) tokenPnLPercent = 0;
console.log('✅ Final PnL values:', {
tokenPnL: tokenPnL,
tokenPnLPercent: tokenPnLPercent.toFixed(2) + '%',
totalPnL: totalPnL,
totalPnLPercent: totalPnLPercent.toFixed(2) + '%',
totalRealizedPnL: totalRealizedPnL
});
savePortfolioToStorage();
}
function savePortfolioToStorage() {
localStorage.setItem('demoPortfolio', JSON.stringify(demoPortfolio));
localStorage.setItem('totalRealizedPnL', totalRealizedPnL.toString());
localStorage.setItem('totalTradeVolume', totalTradeVolume.toString());
localStorage.setItem('tradeHistory', JSON.stringify(tradeHistory));
console.log('Portfolio saved to localStorage');
}
function startPriceUpdates() {
// İlk fiyat güncellemesini hemen yap
getCurrentPrices();
fetchSolPrice();
// Sonra periyodik olarak devam et - 0.5 SANİYE
setInterval(() => {
if (demoMode) {
getCurrentPrices();
if (demoPanelVisible) {
updateDemoPanelValues();
}
}
}, 500); // 0.5 saniye
}
function addDemoTab() {
const tabList = document.querySelector('.dex-tabs-pane-list-container');
if (!tabList) {
setTimeout(addDemoTab, 1000);
return;
}
if (document.querySelector('[data-pane-id="demo_trading"]')) {
return;
}
const demoTabHTML = `
<div class="dex-tabs-pane dex-tabs-pane-lg dex-tabs-pane-blue dex-tabs-pane-underline AV2oC5__dex" data-pane-id="demo_trading" id=":r2if:-demo_trading" role="tab" aria-selected="false" tabindex="-1">
<h2 class="font-inherit">Demo Trading</h2>
</div>
`;
const lastTab = tabList.querySelector('.dex-tabs-pane:last-child');
if (lastTab) {
lastTab.insertAdjacentHTML('beforebegin', demoTabHTML);
addTabClickHandler();
}
}
function addDemoButton() {
const buttonContainer = document.querySelector('.Q68nJL__dex');
if (!buttonContainer) {
setTimeout(addDemoButton, 1000);
return;
}
if (document.querySelector('.demo-trade-button')) {
return;
}
const demoButtonHTML = `
<div data-testid="okd-popup" class="dex dex-popup-var dex-popup dex-coach-popover dex-coachmark-var demo-trade-button">
<button type="button" class="dex-plain-button peM48g__dex MovYo___dex q7TVYE__dex demo-mode-toggle">
<span class="zGJMLt__dex MovYo___dex">
<i class="icon iconfont dex-okx-defi-dex-quick-filled uZT0ej__dex" role="img" aria-hidden="true"></i>
<span>Demo Trade</span>
</span>
</button>
</div>
`;
const instantTradeBtn = buttonContainer.querySelector('[data-testid="okd-popup"]:last-child');
if (instantTradeBtn) {
instantTradeBtn.insertAdjacentHTML('beforebegin', demoButtonHTML);
addButtonClickHandler();
}
}
function addTabClickHandler() {
const demoTab = document.querySelector('[data-pane-id="demo_trading"]');
if (demoTab) {
demoTab.addEventListener('click', function() {
switchToDemoMode();
updateActiveTab();
showDemoPanel();
});
}
}
function addButtonClickHandler() {
const demoButton = document.querySelector('.demo-mode-toggle');
if (demoButton) {
demoButton.addEventListener('click', function(e) {
e.stopPropagation();
demoMode = !demoMode;
updateDemoButton();
if (demoMode) {
//showDemoNotification('Demo mode activated! Starting balance: $' + demoBalance, 'success');
showDemoPanel();
} else {
//showDemoNotification('Demo mode deactivated', 'info');
hideDemoPanel();
}
});
}
}
function switchToDemoMode() {
demoMode = true;
updateDemoButton();
showDemoPanel();
}
function updateDemoButton() {
const demoButton = document.querySelector('.demo-mode-toggle');
if (demoButton) {
if (demoMode) {
demoButton.style.backgroundColor = '';
demoButton.querySelector('span span').textContent = 'Demo Active';
} else {
demoButton.style.backgroundColor = '';
demoButton.querySelector('span span').textContent = 'Demo Trade';
}
}
}
function updateActiveTab() {
const tabs = document.querySelectorAll('.dex-tabs-pane');
tabs.forEach(tab => {
tab.classList.remove('dex-tabs-pane-underline-active', 'p0ZfTr__dex');
});
const activeTab = document.querySelector('[data-pane-id="demo_trading"]');
if (activeTab) {
activeTab.classList.add('dex-tabs-pane-underline-active', 'p0ZfTr__dex');
}
}
function showDemoPanel() {
if (demoPanelVisible) return;
loadSettingsFromRealPanel();
getCurrentPrices();
const demoPanelHTML = `
<div class="cIknRK__dex gRJpse__dex demo-trade-panel" role="button" tabindex="-1" style="left: 400px; top: 200px; opacity: 1; transition: all 0.2s; cursor: default; z-index: 1007;">
<div class="vM9Rz8__dex F_UE_X__dex" role="button" tabindex="0" aria-disabled="false" aria-roledescription="draggable">
<div class="bSH5VS__dex" aria-label="drag-to-move-dialog"><i class="icon iconfont cenOGi__dex dex-okx-defi-dex-drag" role="img" aria-hidden="true"></i></div>
<div>
<div class="NZD_dv__dex">
<div class="dex dex-select-var dex-select select-text Hp9ZjI__dex demo-default-select">
<div class="dex-select-value-box display-area pm_IiY__dex">
<div class="LFH5He__dex">
<div class="D3fk1I__dex">Default</div>
<i class="icon iconfont dex-okds-chevron-down icon-sign select-up dex-select-reference-icon dex-select-reference-icon-md" role="img" aria-label="" aria-hidden="true"></i>
</div>
</div>
<div data-testid="okd-popup" class="dex dex-popup-var dex-popup select-popup-reference"></div>
</div>
</div>
</div>
<div class="aynava__dex">
<!-- RESET BUTON - ICON DÜZELTİLDİ -->
<div data-testid="okd-popup" class="dex dex-popup-var dex-popup dex-tooltip dex-tooltip-var dex-tooltip-neutral">
<button type="button" class="dex-plain-button hs2qqD__dex demo-reset-balance">
<i class="icon iconfont McyHpj__dex dex-okx-defi-dex-reset" role="img" aria-label="Reset Demo"></i>
</button>
</div>
<!-- CUSTOMIZE BUTON - SORUN ÇÖZÜLDÜ -->
<div data-testid="okd-popup" class="dex dex-popup-var dex-popup">
<button type="button" class="dex-plain-button hs2qqD__dex demo-customize-btn ${customizeMode ? 'QzYi7N__dex' : ''}">
<i class="icon iconfont McyHpj__dex ${customizeMode ? 'dex-okx-defi-dex-check' : 'dex-okx-defi-marketplace-edit'}" role="img" aria-label="Customize amounts"></i>
</button>
</div>
<!-- SETTINGS BUTON -->
<div data-testid="okd-popup" class="dex dex-popup-var dex-popup dex-tooltip dex-tooltip-var dex-tooltip-neutral">
<button type="button" class="dex-plain-button hs2qqD__dex demo-settings-btn">
<i class="icon iconfont McyHpj__dex dex-okx-defi-dex-keyboard" role="img" aria-label="Click to activate hotkeys"></i>
</button>
</div>
<i class="icon iconfont vuymPd__dex dex-okx-defi-marketplace-close dex-a11y-button demo-panel-close" role="button" aria-label="Close" tabindex="0"></i>
</div>
</div>
<div class="JlS9Ws__dex">
<div>
<div class="flex justify-between items-center uyKNVr__dex">
<div class="dex dex-select-var dex-select select-text lnOLCt__dex instant-trade-token-selector-buy demo-buy-select">
<div class="dex-select-value-box display-area NC4WG2__dex">
<div class="e4mTHT__dex flex items-center font-12">
<span class="ioQevx__dex">Buy with</span>
<span class="font-500 UE7t1T__dex">SOL</span>
<i class="icon iconfont dex-okx-defi-dex-market-sort-down RQGYJ6__dex" role="img" aria-hidden="true"></i>
</div>
</div>
<div data-testid="okd-popup" class="dex dex-popup-var dex-popup select-popup-reference"></div>
</div>
<div class="flex items-center">
<span class="VHD_xh__dex">${demoPortfolio.SOL.amount.toFixed(4)}</span>
<picture class="dex dex-picture dex-picture-font"><source srcset="https://web3.okx.com/cdn/web3/currency/token/501-11111111111111111111111111111111-1.png/type=default_350_0?v=1734571825920&x-oss-process=image/format,webp/ignore-error,1"><img width="16" height="16" class="Lrw8Qc__dex" alt="" src="https://web3.okx.com/cdn/web3/currency/token/501-11111111111111111111111111111111-1.png/type=default_350_0?v=1734571825920" style="width: 16px; height: 16px;"></picture>
</div>
</div>
<div class="flex justify-between lFouoK__dex">
<div class="k_jil0__dex xr2g7U__dex">
<div class="ThQSDh__dex options-wrapper">
${customizeMode ? `
<!-- CUSTOMIZE MODE: INPUT ALANLARI -->
<div class="dex dex-input-var dex-input dex-input-md __XCfV__dex mW93WY__dex">
<div class="dex-input-box auto-size E_NN8J__dex utlHnK__dex" role="none">
<input autocomplete="off" readonly="" type="hidden" style="display: none;">
<input inputmode="decimal" enable_thousands="true" min="0" max="9007199254740991" autocomplete="off" step="1" id="demo-buy-input-1" class="dex-input-input PfVQrS__dex demo-buy-input" autocapitalize="off" autocorrect="off" type="text" value="${tempBuyAmounts[0]}" name="demo_buy_input_1">
<div class="dex-input-suffix"></div>
</div>
</div>
<div class="dex dex-input-var dex-input dex-input-md __XCfV__dex mW93WY__dex">
<div class="dex-input-box auto-size E_NN8J__dex utlHnK__dex" role="none">
<input autocomplete="off" readonly="" type="hidden" style="display: none;">
<input inputmode="decimal" enable_thousands="true" min="0" max="9007199254740991" autocomplete="off" step="1" id="demo-buy-input-2" class="dex-input-input PfVQrS__dex demo-buy-input" autocapitalize="off" autocorrect="off" type="text" value="${tempBuyAmounts[1]}" name="demo_buy_input_2">
<div class="dex-input-suffix"></div>
</div>
</div>
<div class="dex dex-input-var dex-input dex-input-md __XCfV__dex mW93WY__dex">
<div class="dex-input-box auto-size E_NN8J__dex utlHnK__dex" role="none">
<input autocomplete="off" readonly="" type="hidden" style="display: none;">
<input inputmode="decimal" enable_thousands="true" min="0" max="9007199254740991" autocomplete="off" step="1" id="demo-buy-input-3" class="dex-input-input PfVQrS__dex demo-buy-input" autocapitalize="off" autocorrect="off" type="text" value="${tempBuyAmounts[2]}" name="demo_buy_input_3">
<div class="dex-input-suffix"></div>
</div>
</div>
` : `
<!-- NORMAL MODE: BUTONLAR -->
<div class="flex items-center kw6r9M__dex">
<div class="H5f_ax__dex Cg7h1c__dex VmOKPA__dex demo-buy-quick" data-amount="${parseFloat(currentSettings.buyAmounts[0]) || 0.03}" style="cursor: pointer;">
<div class="l5GPKh__dex ellipsis">${currentSettings.buyAmounts[0] || '0.03'}</div>
</div>
</div>
<div class="flex items-center kw6r9M__dex">
<div class="H5f_ax__dex Cg7h1c__dex VmOKPA__dex demo-buy-quick" data-amount="${parseFloat(currentSettings.buyAmounts[1]) || 0.05}" style="cursor: pointer;">
<div class="l5GPKh__dex ellipsis">${currentSettings.buyAmounts[1] || '0.05'}</div>
</div>
</div>
<div class="flex items-center kw6r9M__dex">
<div class="H5f_ax__dex Cg7h1c__dex VmOKPA__dex demo-buy-quick" data-amount="${parseFloat(currentSettings.buyAmounts[2]) || 0.07}" style="cursor: pointer;">
<div class="l5GPKh__dex ellipsis">${currentSettings.buyAmounts[2] || '0.07'}</div>
</div>
</div>
`}
</div>
</div>
</div>
<div class="flex justify-start Nox6EF__dex">
<div class="flex items-center Fb9n18__dex" role="presentation">
<i class="icon iconfont dex-okx-defi-dex-slippage GuPJu7__dex" role="img" aria-hidden="true"></i>
<span class="font-12 ml-4 font-500">${currentSettings.buySlippage}</span>
<span class="ROEGPH__dex"></span>
<i class="icon iconfont dex-okx-defi-dex-fee GuPJu7__dex" role="img" aria-hidden="true"></i>
<span class="font-12 ml-4 font-500">Market</span>
<span class="ROEGPH__dex"></span>
<div data-testid="okd-popup" class="dex dex-popup-var dex-popup dex-tooltip dex-tooltip-var dex-tooltip-neutral "><div class="flex items-center"><i class="icon iconfont dex-okx-defi-web3-shield Xm6VIO__dex" role="img" aria-label="config-item"></i><span class="Or4Gzq__dex">Auto</span></div></div>
<i class="icon iconfont dex-okx-defi-marketplace-chevron-right C0NIv2__dex demo-buy-settings" role="img" aria-hidden="true" style="cursor: pointer;"></i>
</div>
</div>
</div>
<div class="QwqNAT__dex">
<div class="flex justify-between items-center uyKNVr__dex">
<div class="dex dex-select-var dex-select select-text lnOLCt__dex instant-trade-token-selector-sell demo-sell-select">
<div class="dex-select-value-box display-area NC4WG2__dex">
<div class="e4mTHT__dex flex items-center font-12">
<span class="ioQevx__dex">Sell for</span>
<span class="font-500 UE7t1T__dex">${currentTokenSymbol}</span>
<i class="icon iconfont dex-okx-defi-dex-market-sort-down RQGYJ6__dex" role="img" aria-hidden="true"></i>
</div>
</div>
<div data-testid="okd-popup" class="dex dex-popup-var dex-popup select-popup-reference"></div>
</div>
<div class="flex items-center">
<span class="VHD_xh__dex">${demoPortfolio.TOKEN.amount.toFixed(4)}</span>
<picture class="dex dex-picture dex-picture-font"><source srcset="${currentTokenImage || 'https://web3.okx.com/cdn/web3/currency/token/large/501-FM6ZsWmVFA41D72NNNTk35ZrUSg2wkCerSNzg7Wjpump-108/type=default_90_0?v=1759520281357'}&x-oss-process=image/format,webp/ignore-error,1"><img width="16" height="16" class="Lrw8Qc__dex" alt="" src="${currentTokenImage || 'https://web3.okx.com/cdn/web3/currency/token/large/501-FM6ZsWmVFA41D72NNNTk35ZrUSg2wkCerSNzg7Wjpump-108/type=default_90_0?v=1759520281357'}" style="width: 16px; height: 16px;"></picture>
</div>
</div>
<div class="flex justify-between lFouoK__dex">
<div class="k_jil0__dex xr2g7U__dex">
<div class="ThQSDh__dex options-wrapper">
${customizeMode ? `
<!-- CUSTOMIZE MODE: INPUT ALANLARI -->
<div class="dex dex-input-var dex-input dex-input-md __XCfV__dex mW93WY__dex">
<div class="dex-input-box auto-size E_NN8J__dex utlHnK__dex" role="none">
<input autocomplete="off" readonly="" type="hidden" style="display: none;">
<input inputmode="decimal" enable_thousands="true" min="0" max="100" autocomplete="off" step="1" id="demo-sell-input-1" class="dex-input-input PfVQrS__dex demo-sell-input" autocapitalize="off" autocorrect="off" type="text" value="${tempSellPercents[0]}" name="demo_sell_input_1">
<div class="dex-input-suffix"></div>
</div>
</div>
<div class="dex dex-input-var dex-input dex-input-md __XCfV__dex mW93WY__dex">
<div class="dex-input-box auto-size E_NN8J__dex utlHnK__dex" role="none">
<input autocomplete="off" readonly="" type="hidden" style="display: none;">
<input inputmode="decimal" enable_thousands="true" min="0" max="100" autocomplete="off" step="1" id="demo-sell-input-2" class="dex-input-input PfVQrS__dex demo-sell-input" autocapitalize="off" autocorrect="off" type="text" value="${tempSellPercents[1]}" name="demo_sell_input_2">
<div class="dex-input-suffix"></div>
</div>
</div>
<div class="dex dex-input-var dex-input dex-input-md __XCfV__dex mW93WY__dex">
<div class="dex-input-box auto-size E_NN8J__dex utlHnK__dex" role="none">
<input autocomplete="off" readonly="" type="hidden" style="display: none;">
<input inputmode="decimal" enable_thousands="true" min="0" max="100" autocomplete="off" step="1" id="demo-sell-input-3" class="dex-input-input PfVQrS__dex demo-sell-input" autocapitalize="off" autocorrect="off" type="text" value="${tempSellPercents[2]}" name="demo_sell_input_3">
<div class="dex-input-suffix"></div>
</div>
</div>
` : `
<!-- NORMAL MODE: BUTONLAR -->
<div class="flex items-center kw6r9M__dex">
<div class="H5f_ax__dex ODJ17x__dex VmOKPA__dex demo-sell-quick" data-percent="50" style="cursor: pointer;">
<div class="l5GPKh__dex ellipsis">${currentSettings.sellPercents[0] || '50%'}</div>
</div>
</div>
<div class="flex items-center kw6r9M__dex">
<div class="H5f_ax__dex ODJ17x__dex VmOKPA__dex demo-sell-quick" data-percent="75" style="cursor: pointer;">
<div class="l5GPKh__dex ellipsis">${currentSettings.sellPercents[1] || '75%'}</div>
</div>
</div>
<div class="flex items-center kw6r9M__dex">
<div class="H5f_ax__dex ODJ17x__dex VmOKPA__dex demo-sell-quick" data-percent="100" style="cursor: pointer;">
<div class="l5GPKh__dex ellipsis">${currentSettings.sellPercents[2] || '100%'}</div>
</div>
</div>
`}
</div>
</div>
</div>
<div class="flex justify-start Nox6EF__dex">
<div class="flex items-center Fb9n18__dex" role="presentation">
<i class="icon iconfont dex-okx-defi-dex-slippage GuPJu7__dex" role="img" aria-hidden="true"></i>
<span class="font-12 ml-4 font-500">${currentSettings.sellSlippage}</span>
<span class="ROEGPH__dex"></span>
<i class="icon iconfont dex-okx-defi-dex-fee GuPJu7__dex" role="img" aria-hidden="true"></i>
<span class="font-12 ml-4 font-500">Market</span>
<span class="ROEGPH__dex"></span>
<div data-testid="okd-popup" class="dex dex-popup-var dex-popup dex-tooltip dex-tooltip-var dex-tooltip-neutral "><div class="flex items-center"><i class="icon iconfont dex-okx-defi-web3-shield Xm6VIO__dex" role="img" aria-label="config-item"></i><span class="Or4Gzq__dex">Auto</span></div></div>
<i class="icon iconfont dex-okx-defi-marketplace-chevron-right C0NIv2__dex demo-sell-settings" role="img" aria-hidden="true" style="cursor: pointer;"></i>
</div>
</div>
</div>
<div class="MEKLl5__dex LAaFhW__dex">
<div data-testid="okd-popup" class="dex dex-popup-var dex-popup dex-tooltip dex-tooltip-var dex-tooltip-neutral cursor-pointer flex items-center underline-dash vgre7c__dex"><span class="Nx6sMk__dex">Service fee</span></div>
${customizeMode ? `
<div class="dex dex-input-var dex-input dex-input-sm" style="width: 80px; display: inline-block;">
<div class="dex-input-box auto-size" role="none">
<input autocomplete="off" readonly="" type="hidden" style="display: none;">
<input inputmode="decimal" enable_thousands="true" min="0" max="100" autocomplete="off" step="0.01" id="demo-service-fee-input" class="dex-input-input demo-service-fee-input" autocapitalize="off" autocorrect="off" type="text" value="${tempServiceFee}" name="demo_service_fee_input">
<div class="dex-input-suffix"></div>
</div>
</div>
` : `<span>${currentSettings.serviceFee}</span>`}
<span class="JBwvD1__dex">DEMO</span>
<div class="demo-pnl-display" style="margin-left: auto; display: flex; flex-direction: column; align-items: flex-end; font-size: 11px; font-weight: 500; gap: 2px;">
<div style="color: ${tokenPnL >= 0 ? '#00a86b' : '#ff4444'}">
Token PnL: ${tokenPnL >= 0 ? '+' : ''}$${tokenPnL.toFixed(2)} (${tokenPnL >= 0 ? '+' : ''}${tokenPnLPercent.toFixed(2)}%)
</div>
<div style="color: ${totalPnL >= 0 ? '#00a86b' : '#ff4444'}">
Total PnL: ${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)} (${totalPnL >= 0 ? '+' : ''}${totalPnLPercent.toFixed(2)}%)
</div>
</div>
</div>
</div>
</div>
`;
hideDemoPanel();
document.body.insertAdjacentHTML('beforeend', demoPanelHTML);
setTimeout(() => {
try {
setupDemoPanelEvents();
setupAdvancedDragAndDrop();
setupDropdowns();
setupDropdownEvents();
updateDemoPanelValues();
} catch (error) {
console.error('Error setting up demo panel:', error);
}
}, 200);
demoPanelVisible = true;
}
function hideDemoPanel() {
const demoPanel = document.querySelector('.demo-trade-panel');
if (demoPanel) {
demoPanel.remove();
}
demoPanelVisible = false;
}
function setupAdvancedDragAndDrop() {
const demoPanel = document.querySelector('.demo-trade-panel');
if (!demoPanel) return;
let isDragging = false;
let startX, startY, initialX, initialY;
const dragHandle = demoPanel.querySelector('.vM9Rz8__dex');
const dragStart = (e) => {
if (e.target.closest('.vM9Rz8__dex')) {
isDragging = true;
startX = e.clientX || e.touches[0].clientX;
startY = e.clientY || e.touches[0].clientY;
const rect = demoPanel.getBoundingClientRect();
initialX = rect.left;
initialY = rect.top;
demoPanel.style.cursor = 'grabbing';
demoPanel.style.transition = 'none';
demoPanel.style.opacity = '0.8';
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
}
};
const drag = (e) => {
if (!isDragging) return;
const currentX = e.clientX || e.touches[0].clientX;
const currentY = e.clientY || e.touches[0].clientY;
const deltaX = currentX - startX;
const deltaY = currentY - startY;
demoPanel.style.left = (initialX + deltaX) + 'px';
demoPanel.style.top = (initialY + deltaY) + 'px';
e.preventDefault();
e.stopPropagation();
};
const dragEnd = () => {
if (!isDragging) return;
isDragging = false;
demoPanel.style.cursor = 'default';
demoPanel.style.transition = 'all 0.2s';
demoPanel.style.opacity = '1';
};
dragHandle.addEventListener('mousedown', dragStart, true);
dragHandle.addEventListener('touchstart', dragStart, { passive: false });
document.addEventListener('mousemove', drag, true);
document.addEventListener('touchmove', drag, { passive: false });
document.addEventListener('mouseup', dragEnd, true);
document.addEventListener('touchend', dragEnd, true);
document.addEventListener('mouseleave', dragEnd, true);
}
function setupDropdowns() {
// Default select dropdown
const defaultSelect = document.querySelector('.demo-default-select');
if (defaultSelect) {
const popupReference = defaultSelect.querySelector('.select-popup-reference');
if (popupReference) {
popupReference.innerHTML = `
<div class="dex dex-popup-var dex-popup-layer dex-popup-layer-visible" style="z-index: 10000; visibility: hidden; position: absolute; left: 0px; top: 0px; margin: 0px; transform: translate(0px, 40px);" data-popper-placement="bottom-start">
<div class="dex-popup-layer-content" style="width: 140px;">
<div class="dex-select-var dex-select-option dex-select-option-pc yip61W__dex align-left drop-mode option-md">
<div class="dex-select-option-box">
<div class="pc-option-scroll" style="max-height: 200px;">
<div class="dex-select-item-container dex-select-item-container-real">
<div class="dex-select-item dex-select-item-active dex-dropdown-option demo-default-option" role="option" aria-selected="true" data-value="Default">
<div class="flex justify-between items-center Be9HsZ__dex">
<div class="vZhXdm__dex">Default</div>
<i class="icon iconfont dex-okx-defi-dex-check DaCpQM__dex qriW7s__dex" role="img" aria-hidden="true"></i>
</div>
</div>
<div class="dex-select-item dex-dropdown-option demo-default-option" role="option" aria-selected="false" data-value="Meme">
<div class="flex justify-between items-center Be9HsZ__dex">
<div class="vZhXdm__dex">Meme</div>
<i class="icon iconfont dex-okx-defi-dex-check DaCpQM__dex" role="img" aria-hidden="true"></i>
</div>
</div>
<div class="dex-select-item dex-dropdown-option demo-default-option" role="option" aria-selected="false" data-value="AMV">
<div class="flex justify-between items-center Be9HsZ__dex">
<div class="vZhXdm__dex">AMV/20%/FA</div>
<i class="icon iconfont dex-okx-defi-dex-check DaCpQM__dex" role="img" aria-hidden="true"></i>
</div>
</div>
<div class="dex-select-item dex-dropdown-option demo-default-option" role="option" aria-selected="false" data-value="MEV1">
<div class="flex justify-between items-center Be9HsZ__dex">
<div class="vZhXdm__dex">MEV/30%/FA</div>
<i class="icon iconfont dex-okx-defi-dex-check DaCpQM__dex" role="img" aria-hidden="true"></i>
</div>
</div>
<div class="dex-select-item dex-dropdown-option demo-default-option" role="option" aria-selected="false" data-value="MEV2">
<div class="flex justify-between items-center Be9HsZ__dex">
<div class="vZhXdm__dex">MEV/30%/TURB</div>
<i class="icon iconfont dex-okx-defi-dex-check DaCpQM__dex" role="img" aria-hidden="true"></i>
</div>
</div>
</div>
</div>
<div class="pc-option-footer">
<div class="ViayfB__dex">
<button type="button" class="dex-plain-button ViayfB__dex demo-edit-settings">
<i class="icon iconfont dex-okx-defi-dex-web3-settings cQG_ch__dex" role="img" aria-hidden="true"></i>
<span>Edit</span>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
`;
}
}
// Buy token select dropdown
const buySelect = document.querySelector('.demo-buy-select');
if (buySelect) {
const popupReference = buySelect.querySelector('.select-popup-reference');
if (popupReference) {
const solBalance = demoPortfolio.SOL.amount.toFixed(4);
const solValue = (demoPortfolio.SOL.amount * currentSolPrice).toFixed(2);
popupReference.innerHTML = `
<div class="dex dex-popup-var dex-popup-layer dex-popup-layer-visible" style="z-index: 10001; visibility: hidden; position: absolute; left: 0px; top: 0px; margin: 0px; transform: translate(0px, 40px);" data-popper-placement="bottom-start">
<div class="dex-popup-layer-content" style="width: 308px;">
<div class="dex-select-var dex-select-option dex-select-option-pc WFwMwa__dex instant-trade-token-selector-option-cont-buy align-left drop-mode option-md">
<div class="dex-select-option-box">
<div class="pc-option-scroll" style="max-height: 208px;">
<div class="dex-select-item-container dex-select-item-container-real">
<div class="dex-select-item dex-select-item-active dex-dropdown-option demo-buy-option" role="option" aria-selected="true" data-value="SOL">
<div class="flex justify-between items-center G9BMtd__dex">
<div class="flex items-center">
<picture class="dex dex-picture dex-picture-font">
<source srcset="https://web3.okx.com/cdn/web3/currency/token/501-11111111111111111111111111111111-1.png/type=default_350_0?v=1734571825920&x-oss-process=image/format,webp/ignore-error,1">
<img width="28" height="28" class="jxzKU9__dex" alt="" src="https://web3.okx.com/cdn/web3/currency/token/501-11111111111111111111111111111111-1.png/type=default_350_0?v=1734571825920" style="width: 28px; height: 28px;">
</picture>
<div class="flex flex-col">
<span class="tiQWEG__dex">SOL</span>
<span class="inAvcR__dex">Solana</span>
</div>
</div>
<div class="flex flex-col tsKlgJ__dex">
<span class="tiQWEG__dex">${solBalance}</span>
<span class="inAvcR__dex">$${solValue}</span>
</div>
</div>
</div>
<div class="dex-select-item dex-dropdown-option demo-buy-option" role="option" aria-selected="false" data-value="USDC">
<div class="flex justify-between items-center G9BMtd__dex">
<div class="flex items-center">
<picture class="dex dex-picture dex-picture-font">
<source srcset="https://web3.okx.com/cdn/web3/currency/token/large/637-0xbae207659db88bea0cbead6da0ed00aac12edcdda169e591cd41c94180b46f3b-107/type=default_90_0?v=1756203256814&x-oss-process=image/format,webp/ignore-error,1">
<img width="28" height="28" class="jxzKU9__dex" alt="" src="https://web3.okx.com/cdn/web3/currency/token/large/637-0xbae207659db88bea0cbead6da0ed00aac12edcdda169e591cd41c94180b46f3b-107/type=default_90_0?v=1756203256814" style="width: 28px; height: 28px;">
</picture>
<div class="flex flex-col">
<span class="tiQWEG__dex">USDC</span>
<span class="inAvcR__dex">USD Coin</span>
</div>
</div>
<div class="flex flex-col tsKlgJ__dex">
<span class="tiQWEG__dex">0</span>
<span class="inAvcR__dex">$0.00</span>
</div>
</div>
</div>
</div>
</div>
<div class="pc-option-footer">
<div class="CnFTM4__dex">
<span>If you wish to trade other tokens, switch to <button type="button" class="dex-plain-button k3rUid__dex demo-swap-btn">Swap</button></span>
</div>
</div>
</div>
</div>
</div>
</div>
`;
}
}
// Sell token select dropdown
const sellSelect = document.querySelector('.demo-sell-select');
if (sellSelect) {
const popupReference = sellSelect.querySelector('.select-popup-reference');
if (popupReference) {
const tokenBalance = demoPortfolio.TOKEN.amount.toFixed(4);
const tokenValue = (demoPortfolio.TOKEN.amount * currentTokenPrice).toFixed(2);
const solBalance = demoPortfolio.SOL.amount.toFixed(4);
const solValue = (demoPortfolio.SOL.amount * currentSolPrice).toFixed(2);
popupReference.innerHTML = `
<div class="dex dex-popup-var dex-popup-layer dex-popup-layer-visible" style="z-index: 10002; visibility: hidden; position: absolute; left: 0px; top: 0px; margin: 0px; transform: translate(0px, 40px);" data-popper-placement="bottom-start">
<div class="dex-popup-layer-content" style="width: 308px;">
<div class="dex-select-var dex-select-option dex-select-option-pc WFwMwa__dex instant-trade-token-selector-option-cont-sell align-left drop-mode option-md">
<div class="dex-select-option-box">
<div class="pc-option-scroll" style="max-height: 208px;">
<div class="dex-select-item-container dex-select-item-container-real">
<div class="dex-select-item dex-select-item-active dex-dropdown-option demo-sell-option" role="option" aria-selected="true" data-value="${currentTokenSymbol}">
<div class="flex justify-between items-center G9BMtd__dex">
<div class="flex items-center">
<picture class="dex dex-picture dex-picture-font">
<source srcset="${currentTokenImage || 'https://web3.okx.com/cdn/web3/currency/token/large/501-FM6ZsWmVFA41D72NNNTk35ZrUSg2wkCerSNzg7Wjpump-108/type=default_90_0?v=1759520281357'}&x-oss-process=image/format,webp/ignore-error,1">
<img width="28" height="28" class="jxzKU9__dex" alt="" src="${currentTokenImage || 'https://web3.okx.com/cdn/web3/currency/token/large/501-FM6ZsWmVFA41D72NNNTk35ZrUSg2wkCerSNzg7Wjpump-108/type=default_90_0?v=1759520281357'}" style="width: 28px; height: 28px;">
</picture>
<div class="flex flex-col">
<span class="tiQWEG__dex">${currentTokenSymbol}</span>
<span class="inAvcR__dex">Current Token</span>
</div>
</div>
<div class="flex flex-col tsKlgJ__dex">
<span class="tiQWEG__dex">${tokenBalance}</span>
<span class="inAvcR__dex">$${tokenValue}</span>
</div>
</div>
</div>
<div class="dex-select-item dex-dropdown-option demo-sell-option" role="option" aria-selected="false" data-value="SOL">
<div class="flex justify-between items-center G9BMtd__dex">
<div class="flex items-center">
<picture class="dex dex-picture dex-picture-font">
<source srcset="https://web3.okx.com/cdn/web3/currency/token/501-11111111111111111111111111111111-1.png/type=default_350_0?v=1734571825920&x-oss-process=image/format,webp/ignore-error,1">
<img width="28" height="28" class="jxzKU9__dex" alt="" src="https://web3.okx.com/cdn/web3/currency/token/501-11111111111111111111111111111111-1.png/type=default_350_0?v=1734571825920" style="width: 28px; height: 28px;">
</picture>
<div class="flex flex-col">
<span class="tiQWEG__dex">SOL</span>
<span class="inAvcR__dex">Solana</span>
</div>
</div>
<div class="flex flex-col tsKlgJ__dex">
<span class="tiQWEG__dex">${solBalance}</span>
<span class="inAvcR__dex">$${solValue}</span>
</div>
</div>
</div>
</div>
</div>
<div class="pc-option-footer">
<div class="CnFTM4__dex">
<span>If you wish to trade other tokens, switch to <button type="button" class="dex-plain-button k3rUid__dex demo-swap-btn">Swap</button></span>
</div>
</div>
</div>
</div>
</div>
</div>
`;
}
}
}
function setupDropdownEvents() {
console.log('Setting up dropdown events...');
// Default select
const defaultSelect = document.querySelector('.demo-default-select .dex-select-value-box');
if (defaultSelect) {
defaultSelect.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
const popup = this.closest('.dex-select').querySelector('.dex-popup-layer');
if (popup) {
const isVisible = popup.style.visibility === 'visible';
// Tüm popup'ları kapat
document.querySelectorAll('.dex-popup-layer').forEach(p => {
p.style.visibility = 'hidden';
p.style.zIndex = '-1';
});
// Bu popup'ı aç
if (!isVisible) {
popup.style.visibility = 'visible';
popup.style.zIndex = '10008';
}
}
return false;
}, true);
}
// Buy select
const buySelect = document.querySelector('.demo-buy-select .dex-select-value-box');
if (buySelect) {
buySelect.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
const popup = this.closest('.dex-select').querySelector('.dex-popup-layer');
if (popup) {
const isVisible = popup.style.visibility === 'visible';
document.querySelectorAll('.dex-popup-layer').forEach(p => {
p.style.visibility = 'hidden';
p.style.zIndex = '-1';
});
if (!isVisible) {
popup.style.visibility = 'visible';
popup.style.zIndex = '10009';
}
}
return false;
}, true);
}
// Sell select
const sellSelect = document.querySelector('.demo-sell-select .dex-select-value-box');
if (sellSelect) {
sellSelect.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
const popup = this.closest('.dex-select').querySelector('.dex-popup-layer');
if (popup) {
const isVisible = popup.style.visibility === 'visible';
document.querySelectorAll('.dex-popup-layer').forEach(p => {
p.style.visibility = 'hidden';
p.style.zIndex = '-1';
});
if (!isVisible) {
popup.style.visibility = 'visible';
popup.style.zIndex = '10009';
}
}
return false;
}, true);
}
// Dropdown options
setTimeout(() => {
document.querySelectorAll('.demo-default-option').forEach(option => {
option.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
const value = this.getAttribute('data-value');
const display = document.querySelector('.demo-default-select .D3fk1I__dex');
if (display) display.textContent = value;
document.querySelectorAll('.dex-popup-layer').forEach(p => {
p.style.visibility = 'hidden';
p.style.zIndex = '-1';
});
showDemoNotification(`Mode changed to: ${value}`, 'info');
return false;
}, true);
});
document.querySelectorAll('.demo-buy-option').forEach(option => {
option.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
const value = this.getAttribute('data-value');
const display = document.querySelector('.demo-buy-select .UE7t1T__dex');
if (display) display.textContent = value;
document.querySelectorAll('.dex-popup-layer').forEach(p => {
p.style.visibility = 'hidden';
p.style.zIndex = '-1';
});
showDemoNotification(`Buy with: ${value}`, 'info');
return false;
}, true);
});
document.querySelectorAll('.demo-sell-option').forEach(option => {
option.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
const value = this.getAttribute('data-value');
const display = document.querySelector('.demo-sell-select .UE7t1T__dex');
if (display) display.textContent = value;
document.querySelectorAll('.dex-popup-layer').forEach(p => {
p.style.visibility = 'hidden';
p.style.zIndex = '-1';
});
showDemoNotification(`Sell for: ${value}`, 'info');
return false;
}, true);
});
// SOL action butonları için event listener ekle
document.querySelectorAll('.demo-sol-action').forEach(button => {
button.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
const action = this.getAttribute('data-action');
handleSolAction(action);
// Popup'ı kapat
document.querySelectorAll('.dex-popup-layer').forEach(p => {
p.style.visibility = 'hidden';
p.style.zIndex = '-1';
});
return false;
}, true);
});
// Custom SOL input için event
const customSolInput = document.getElementById('demo-sol-custom');
if (customSolInput) {
customSolInput.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
e.preventDefault();
e.stopPropagation();
handleSolAction('custom');
document.querySelectorAll('.dex-popup-layer').forEach(p => {
p.style.visibility = 'hidden';
p.style.zIndex = '-1';
});
return false;
}
}, true);
// Input'a tıklama olayını da engelle
customSolInput.addEventListener('click', function(e) {
e.stopPropagation();
}, true);
}
}, 100);
// Document click to close dropdowns - daha spesifik hale getir
document.addEventListener('click', function(e) {
// Sadece demo dropdown'ları değilse kapat
if (!e.target.closest('.demo-default-select') &&
!e.target.closest('.demo-buy-select') &&
!e.target.closest('.demo-sell-select') &&
!e.target.closest('.dex-popup-layer')) {
document.querySelectorAll('.dex-popup-layer').forEach(popup => {
popup.style.visibility = 'hidden';
popup.style.zIndex = '-1';
});
}
}, true);
}
function setupDemoPanelEvents() {
console.log('Setting up demo panel events...');
// Kapat butonu
const closeBtn = document.querySelector('.demo-panel-close');
if (closeBtn) {
closeBtn.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
hideDemoPanel();
return false;
}, true);
}
// Reset butonu - ICON DÜZELTİLDİ
const resetBtn = document.querySelector('.demo-reset-balance');
if (resetBtn) {
resetBtn.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
resetDemoAccount();
return false;
}, true);
}
const customizeBtn = document.querySelector('.demo-customize-btn');
if (customizeBtn) {
console.log('Customize button found, setting up click event...');
// Tüm event listener'ları temizle
const newCustomizeBtn = customizeBtn.cloneNode(true);
customizeBtn.parentNode.replaceChild(newCustomizeBtn, customizeBtn);
// Yeni butona direkt onclick event ekle
newCustomizeBtn.onclick = function(e) {
console.log('✅ CUSTOMIZE BUTTON CLICKED! Mode:', customizeMode);
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
// Customize modunu değiştir
toggleCustomizeMode();
return false;
};
// Tooltip özelliklerini kaldır
if (newCustomizeBtn.parentNode.classList.contains('dex-tooltip')) {
newCustomizeBtn.parentNode.classList.remove('dex-tooltip', 'dex-tooltip-neutral', 'dex-tooltip-var');
}
}
// Settings butonu
const settingsBtn = document.querySelector('.demo-settings-btn');
if (settingsBtn) {
settingsBtn.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
showDemoNotification('Demo settings - Real-time prices active', 'info');
return false;
}, true);
}
// Buy settings butonu
const buySettings = document.querySelector('.demo-buy-settings');
if (buySettings) {
buySettings.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
showDemoNotification(`Buy settings: ${currentSettings.buySlippage} slippage`, 'info');
return false;
}, true);
}
// Sell settings butonu
const sellSettings = document.querySelector('.demo-sell-settings');
if (sellSettings) {
sellSettings.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
showDemoNotification(`Sell settings: ${currentSettings.sellSlippage} slippage`, 'info');
return false;
}, true);
}
// Hızlı alım butonları (sadece customize mode kapalıyken)
if (!customizeMode) {
const buyButtons = document.querySelectorAll('.demo-buy-quick');
buyButtons.forEach(btn => {
btn.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
const amount = parseFloat(this.getAttribute('data-amount')) || 0.03;
executeDemoBuy(amount);
return false;
}, true);
});
}
// Hızlı satış butonları (sadece customize mode kapalıyken)
if (!customizeMode) {
const sellButtons = document.querySelectorAll('.demo-sell-quick');
sellButtons.forEach(btn => {
btn.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
const percent = parseInt(this.getAttribute('data-percent')) || 50;
executeDemoSell(percent);
return false;
}, true);
});
}
// Input event'leri (sadece customize mode açıkken)
if (customizeMode) {
const buyInputs = document.querySelectorAll('.demo-buy-input');
buyInputs.forEach((input, index) => {
// Input'u clone ederek temizle
const newInput = input.cloneNode(true);
input.parentNode.replaceChild(newInput, input);
newInput.addEventListener('input', function(e) {
tempBuyAmounts[index] = this.value;
console.log(`Buy amount ${index + 1} updated: ${this.value}`);
});
newInput.addEventListener('blur', function(e) {
tempBuyAmounts[index] = this.value;
console.log(`Buy amount ${index + 1} saved: ${this.value}`);
});
newInput.addEventListener('change', function(e) {
tempBuyAmounts[index] = this.value;
});
});
const sellInputs = document.querySelectorAll('.demo-sell-input');
sellInputs.forEach((input, index) => {
// Input'u clone ederek temizle
const newInput = input.cloneNode(true);
input.parentNode.replaceChild(newInput, input);
newInput.addEventListener('input', function(e) {
tempSellPercents[index] = this.value;
console.log(`Sell percent ${index + 1} updated: ${this.value}`);
});
newInput.addEventListener('blur', function(e) {
tempSellPercents[index] = this.value;
console.log(`Sell percent ${index + 1} saved: ${this.value}`);
});
newInput.addEventListener('change', function(e) {
tempSellPercents[index] = this.value;
});
});
// Service fee input event'i
const serviceFeeInput = document.querySelector('.demo-service-fee-input');
if (serviceFeeInput) {
const newServiceFeeInput = serviceFeeInput.cloneNode(true);
serviceFeeInput.parentNode.replaceChild(newServiceFeeInput, serviceFeeInput);
newServiceFeeInput.addEventListener('input', function(e) {
tempServiceFee = this.value;
console.log(`Service fee updated: ${this.value}`);
});
newServiceFeeInput.addEventListener('blur', function(e) {
tempServiceFee = this.value;
console.log(`Service fee saved: ${this.value}`);
});
newServiceFeeInput.addEventListener('change', function(e) {
tempServiceFee = this.value;
});
}
}
}
function toggleCustomizeMode() {
console.log('Toggling customize mode. Current mode:', customizeMode);
customizeMode = !customizeMode;
if (customizeMode) {
// Geçici değerleri mevcut ayarlarla doldur
tempBuyAmounts = [...currentSettings.buyAmounts];
tempSellPercents = [...currentSettings.sellPercents];
tempServiceFee = currentSettings.serviceFee;
showDemoNotification('Customize amounts mode activated - Edit buy amounts, sell percentages and service fee', 'info');
} else {
// Değişiklikleri kaydet
currentSettings.buyAmounts = [...tempBuyAmounts];
currentSettings.sellPercents = [...tempSellPercents];
currentSettings.serviceFee = tempServiceFee;
showDemoNotification('Custom amounts and service fee saved successfully!', 'success');
}
// PANELİ YENİDEN OLUŞTUR - BU KISIM ÇOK ÖNEMLİ
if (demoPanelVisible) {
// Mevcut panel pozisyonunu kaydet
const currentPanel = document.querySelector('.demo-trade-panel');
let panelStyle = {};
if (currentPanel) {
panelStyle = {
left: currentPanel.style.left,
top: currentPanel.style.top
};
}
// Panel'i yeniden oluştur
showDemoPanel();
// Pozisyonu geri yükle
if (panelStyle.left && panelStyle.top) {
const newPanel = document.querySelector('.demo-trade-panel');
if (newPanel) {
newPanel.style.left = panelStyle.left;
newPanel.style.top = panelStyle.top;
}
}
console.log('Panel rebuilt with customize mode:', customizeMode);
}
}
function resetDemoAccount() {
if (confirm('Are you sure you want to reset your demo account? All portfolio data will be lost.')) {
demoBalance = 1000;
demoPortfolio = {
'SOL': { amount: 10, avgPrice: 100 },
'TOKEN': { amount: 0, avgPrice: 0 }
};
totalPnL = 0;
totalPnLPercent = 0;
tokenPnL = 0;
tokenPnLPercent = 0;
totalRealizedPnL = 0;
totalTradeVolume = 0;
tradeHistory = [];
// localStorage'ı da temizle
savePortfolioToStorage();
updateDemoPanelValues();
showDemoNotification('Demo account reset! Balance: $1000, SOL: 10', 'success');
}
}
// Rastgele SOL fee hesaplama fonksiyonu
function calculateRandomSolFee() {
const min = 0.0004;
const max = 0.0009;
return (Math.random() * (max - min) + min).toFixed(6);
}
// Service fee hesaplama fonksiyonu
function calculateServiceFee(amount, feePercentage) {
const percentage = parseFloat(feePercentage.replace('%', '')) / 100;
return amount * percentage;
}
function executeDemoBuy(amount) {
// Fiyat kontrolü - sadece 0 veya NaN ise engelle
if (!currentTokenPrice || currentTokenPrice <= 0 || isNaN(currentTokenPrice)) {
showDemoNotification('Cannot execute buy - token price not available! Please wait for price data to load.', 'error');
return;
}
if (!demoMode) {
showDemoNotification('Please activate demo mode first!', 'error');
return;
}
const solCost = amount;
if (solCost > demoPortfolio.SOL.amount) {
showDemoNotification(`Insufficient SOL! Need ${solCost.toFixed(4)} SOL but only have ${demoPortfolio.SOL.amount.toFixed(4)} SOL`, 'error');
return;
}
console.log('Executing buy:', {
solCost: solCost,
currentSolPrice: currentSolPrice,
currentTokenPrice: currentTokenPrice
});
// Service fee ve market fee hesapla
const serviceFeeAmount = calculateServiceFee(solCost, currentSettings.serviceFee);
const marketFeeAmount = parseFloat(calculateRandomSolFee());
const totalFees = serviceFeeAmount + marketFeeAmount;
// Toplam SOL maliyeti (işlem + fee'ler)
const totalSolCost = solCost + totalFees;
if (totalSolCost > demoPortfolio.SOL.amount) {
showDemoNotification(`Insufficient SOL for fees! Need ${totalSolCost.toFixed(6)} SOL but only have ${demoPortfolio.SOL.amount.toFixed(4)} SOL`, 'error');
return;
}
// Doğru token miktarı hesaplama: SOL miktarı * (SOL fiyatı / Token fiyatı)
const tokenAmount = (solCost * currentSolPrice) / currentTokenPrice;
const usdValue = solCost * currentSolPrice;
console.log('Buy calculation:', {
tokenAmount: tokenAmount,
usdValue: usdValue,
serviceFee: serviceFeeAmount,
marketFee: marketFeeAmount,
totalFees: totalFees
});
// Geçmiş işleme ekle
tradeHistory.push({
type: 'BUY',
tokenAmount: tokenAmount,
solAmount: solCost,
tokenPrice: currentTokenPrice,
solPrice: currentSolPrice,
usdValue: usdValue,
serviceFee: serviceFeeAmount,
marketFee: marketFeeAmount,
totalFees: totalFees,
timestamp: new Date()
});
totalTradeVolume += usdValue;
// ÖNCE: Mevcut portföy değerlerini kaydet
const previousTokenAmount = demoPortfolio.TOKEN.amount;
const previousAvgPrice = demoPortfolio.TOKEN.avgPrice;
const previousTotalValue = previousTokenAmount * previousAvgPrice;
// Yeni token ekle
const newTokenValue = tokenAmount * currentTokenPrice;
const totalTokenAmount = previousTokenAmount + tokenAmount;
// SONRA: Weighted average ile YENİ ORTALAMA FİYAT hesapla
if (totalTokenAmount > 0) {
demoPortfolio.TOKEN.avgPrice = (previousTotalValue + newTokenValue) / totalTokenAmount;
}
// Portföyü güncelle (fee'leri de çıkar)
demoPortfolio.SOL.amount -= totalSolCost;
demoPortfolio.TOKEN.amount = totalTokenAmount;
console.log('After buy portfolio update:', {
newAvgPrice: demoPortfolio.TOKEN.avgPrice,
newTokenAmount: demoPortfolio.TOKEN.amount,
remainingSOL: demoPortfolio.SOL.amount,
feesPaid: totalFees
});
calculatePnL();
updateDemoPanelValues();
showDemoNotification(`Bought ${tokenAmount.toFixed(2)} ${currentTokenSymbol} for ${solCost.toFixed(4)} SOL | Fees: ${serviceFeeAmount.toFixed(6)} SOL (${currentSettings.serviceFee}) + ${marketFeeAmount.toFixed(6)} SOL (Market)`, 'success');
}
function executeDemoSell(percent) {
if (!demoMode) {
showDemoNotification('Please activate demo mode first!', 'error');
return;
}
if (demoPortfolio.TOKEN.amount <= 0) {
showDemoNotification(`No ${currentTokenSymbol} to sell! Buy some tokens first.`, 'error');
return;
}
const tokenAmount = demoPortfolio.TOKEN.amount * (percent / 100);
// Doğru SOL kazancı hesaplama: Token miktarı * (Token fiyatı / SOL fiyatı)
const solRevenue = (tokenAmount * currentTokenPrice) / currentSolPrice;
const usdValue = tokenAmount * currentTokenPrice;
// Service fee ve market fee hesapla
const serviceFeeAmount = calculateServiceFee(solRevenue, currentSettings.serviceFee);
const marketFeeAmount = parseFloat(calculateRandomSolFee());
const totalFees = serviceFeeAmount + marketFeeAmount;
// Net SOL kazancı (işlem - fee'ler)
const netSolRevenue = solRevenue - totalFees;
if (netSolRevenue < 0) {
showDemoNotification(`Fees exceed revenue! Cannot complete transaction.`, 'error');
return;
}
// Realize edilen PnL'yi hesapla - MEVCUT ORTALAMA FİYAT kullan
const costBasis = tokenAmount * demoPortfolio.TOKEN.avgPrice;
const realizedPnL = usdValue - costBasis;
console.log('Sell calculation:', {
tokenAmount: tokenAmount,
avgPrice: demoPortfolio.TOKEN.avgPrice,
costBasis: costBasis,
usdValue: usdValue,
realizedPnL: realizedPnL,
serviceFee: serviceFeeAmount,
marketFee: marketFeeAmount,
totalFees: totalFees,
netRevenue: netSolRevenue
});
// Geçmiş işleme ekle
tradeHistory.push({
type: 'SELL',
tokenAmount: tokenAmount,
solAmount: solRevenue,
netSolAmount: netSolRevenue,
tokenPrice: currentTokenPrice,
solPrice: currentSolPrice,
usdValue: usdValue,
realizedPnL: realizedPnL,
serviceFee: serviceFeeAmount,
marketFee: marketFeeAmount,
totalFees: totalFees,
timestamp: new Date()
});
totalTradeVolume += usdValue;
totalRealizedPnL += realizedPnL;
// Satış işlemi (net kazancı ekle)
demoPortfolio.SOL.amount += netSolRevenue;
demoPortfolio.TOKEN.amount -= tokenAmount;
// Eğer tüm tokenlar satıldıysa average price'ı sıfırla
if (demoPortfolio.TOKEN.amount === 0) {
demoPortfolio.TOKEN.avgPrice = 0;
}
calculatePnL();
updateDemoPanelValues();
const pnlPercent = costBasis > 0 ? (realizedPnL / costBasis) * 100 : 0;
const pnlText = realizedPnL >= 0 ?
`Profit: +$${realizedPnL.toFixed(2)} (${pnlPercent.toFixed(2)}%)` :
`Loss: $${Math.abs(realizedPnL).toFixed(2)} (${Math.abs(pnlPercent).toFixed(2)}%)`;
showDemoNotification(`Sold ${tokenAmount.toFixed(2)} ${currentTokenSymbol} for ${netSolRevenue.toFixed(4)} SOL (after fees) | ${pnlText} | Fees: ${serviceFeeAmount.toFixed(6)} SOL (${currentSettings.serviceFee}) + ${marketFeeAmount.toFixed(6)} SOL (Market)`, 'success');
}
function updateDemoPanelValues() {
const demoPanel = document.querySelector('.demo-trade-panel');
if (!demoPanel) return;
// Balance hesapla
const totalBalance = (demoPortfolio.SOL.amount * currentSolPrice) + (demoPortfolio.TOKEN.amount * currentTokenPrice);
demoBalance = totalBalance;
// Token görüntülerini güncelle
const buyTokenDisplay = demoPanel.querySelector('.instant-trade-token-selector-buy + .flex.items-center .VHD_xh__dex');
const sellTokenDisplay = demoPanel.querySelector('.instant-trade-token-selector-sell + .flex.items-center .VHD_xh__dex');
const sellTokenSymbol = demoPanel.querySelector('.instant-trade-token-selector-sell .UE7t1T__dex');
const sellTokenImage = demoPanel.querySelector('.instant-trade-token-selector-sell + .flex.items-center img');
const pnlDisplay = demoPanel.querySelector('.demo-pnl-display');
if (buyTokenDisplay) buyTokenDisplay.textContent = demoPortfolio.SOL.amount.toFixed(4);
if (sellTokenDisplay) sellTokenDisplay.textContent = demoPortfolio.TOKEN.amount.toFixed(4);
if (sellTokenSymbol) sellTokenSymbol.textContent = currentTokenSymbol;
if (sellTokenImage) {
sellTokenImage.src = currentTokenImage || 'https://web3.okx.com/cdn/web3/currency/token/large/501-FM6ZsWmVFA41D72NNNTk35ZrUSg2wkCerSNzg7Wjpump-108/type=default_90_0?v=1759520281357';
sellTokenImage.srcset = `${currentTokenImage || 'https://web3.okx.com/cdn/web3/currency/token/large/501-FM6ZsWmVFA41D72NNNTk35ZrUSg2wkCerSNzg7Wjpump-108/type=default_90_0?v=1759520281357'}&x-oss-process=image/format,webp/ignore-error,1`;
}
// PnL display kısmını güncelle
if (pnlDisplay) {
pnlDisplay.innerHTML = `
<div style="color: ${tokenPnL >= 0 ? '#00a86b' : '#ff4444'}">
Token PnL: ${tokenPnL >= 0 ? '+' : ''}$${tokenPnL.toFixed(2)} (${tokenPnL >= 0 ? '+' : ''}${tokenPnLPercent.toFixed(2)}%)
</div>
<div style="color: ${totalPnL >= 0 ? '#00a86b' : '#ff4444'}">
Total PnL: ${totalPnL >= 0 ? '+' : ''}$${totalPnL.toFixed(2)} (${totalPnL >= 0 ? '+' : ''}${totalPnLPercent.toFixed(2)}%)
</div>
`;
}
// Dropdown içeriklerini güncelle
setupDropdowns();
}
function showDemoNotification(message, type = 'info') {
// Mevcut bildirim container'ını bul veya oluştur
let notificationContainer = document.querySelector('.dex-notification-container');
if (!notificationContainer) {
notificationContainer = document.createElement('div');
notificationContainer.className = 'dex-notification-container';
notificationContainer.style.cssText = `
position: fixed;
top: 80px;
left: 50%;
transform: translateX(-50%);
z-index: 10000;
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
max-width: 400px;
width: 100%;
`;
document.body.appendChild(notificationContainer);
// CSS animasyonlarını ekle
addNotificationStyles();
}
// Maksimum bildirim sayısı kontrolü (2'den fazla ise en eski bildirimleri temizle)
const currentNotifications = notificationContainer.querySelectorAll('.dex-notification-box');
if (currentNotifications.length >= 2) {
// 2'den fazla varsa, fazla olanları hızlıca temizle
const notificationsToRemove = Array.from(currentNotifications).slice(0, currentNotifications.length - 1);
notificationsToRemove.forEach((notification, index) => {
// Stagger effect - her birini biraz gecikmeli kaldır
setTimeout(() => {
notification.style.animation = 'quickFadeOut 0.3s ease-out forwards';
setTimeout(() => {
if (notification.parentNode) {
notification.parentNode.removeChild(notification);
}
}, 300);
}, index * 80); // 80ms gecikme ile sırayla
});
}
// Bildirim ID'si oluştur
const notificationId = 'demo-notification-' + Date.now();
// Tip'e göre stil ve icon belirle
let iconClass, boxClass, iconLabel;
switch(type) {
case 'error':
iconClass = 'dex-okds-fail-circle-fill';
boxClass = 'error';
iconLabel = 'Error';
break;
case 'success':
iconClass = 'dex-okds-success-circle-fill';
boxClass = 'success';
iconLabel = 'Success';
break;
case 'warning':
iconClass = 'dex-okds-warning-circle-fill';
boxClass = 'warning';
iconLabel = 'Warning';
break;
case 'info':
default:
iconClass = 'dex-okds-info-circle-fill';
boxClass = 'info';
iconLabel = 'Info';
break;
}
// Bildirim HTML'i oluştur
const notificationHTML = `
<div class="dex-notification-var dex-notification-box ${boxClass} auto-width"
id="${notificationId}"
role="alert"
aria-live="polite"
style="animation: lightSlideIn 0.4s ease-out;">
<span class="dex-notification-icon-circle-container">
<i class="icon iconfont dex-notification-icon-new ${iconClass}"
role="img"
aria-label="${iconLabel}"></i>
</span>
<div class="dex-notification-content">
<div class="dex-notification-title-box">
<span class="dex-notification-title">${message}</span>
</div>
</div>
</div>
`;
// Bildirimi container'a EKLE (üste değil, alta ekleyeceğiz)
notificationContainer.insertAdjacentHTML('beforeend', notificationHTML);
// 4 saniye sonra bildirimi kaldır
setTimeout(() => {
const notification = document.getElementById(notificationId);
if (notification) {
notification.style.animation = 'lightSlideOut 0.35s ease-in forwards';
setTimeout(() => {
if (notification.parentNode) {
notification.parentNode.removeChild(notification);
}
}, 350);
}
}, 4000);
}
// CSS animasyonlarını ekleyen fonksiyon
function addNotificationStyles() {
if (document.querySelector('#notification-styles')) return;
const style = document.createElement('style');
style.id = 'notification-styles';
style.textContent = `
@keyframes lightSlideIn {
0% {
transform: translateY(-15px);
opacity: 0;
scale: 0.98;
}
70% {
transform: translateY(-5px);
opacity: 0.8;
scale: 0.99;
}
100% {
transform: translateY(0);
opacity: 1;
scale: 1;
}
}
@keyframes lightSlideOut {
0% {
transform: translateY(0);
opacity: 1;
scale: 1;
}
100% {
transform: translateY(-15px);
opacity: 0;
scale: 0.98;
}
}
@keyframes quickFadeOut {
0% {
transform: translateY(0);
opacity: 1;
}
100% {
transform: translateY(-10px);
opacity: 0;
}
}
.dex-notification-box {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
will-change: transform, opacity;
}
.dex-notification-container {
transition: all 0.3s ease-in-out;
}
/* Daha hafif geçişler */
.dex-notification-box.shifting {
transition: transform 0.25s ease-out;
}
`;
document.head.appendChild(style);
}
function setupDemoMode() {
console.log('Demo trading mode initialized');
}
// Sayfa yüklendiğinde başlat
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initDemoTrading);
} else {
initDemoTrading();
}
// URL değişikliklerini dinle (SPA'lar için)
let lastUrl = location.href;
new MutationObserver(() => {
const url = location.href;
if (url !== lastUrl) {
lastUrl = url;
console.log('URL changed, reinitializing demo trading...');
setTimeout(initDemoTrading, 1000);
}
}).observe(document, { subtree: true, childList: true });
})();