$(document).ready(function () { const defaultSettings = { settings: { exchanges: 'binance', marketplaces: 'opensea', priceAs: 'fiat', currency: 'USD', displayETHTotal: false, isMasked: false, darkMode: true, countPudgyGroup: true, // is Pudgy values count in total? showPudgyGroup: true, // show or hide Pudgy area expPudgyGroup: true, // expand or collapse Pudgy area countAbsGroup: true, // is Abs values count in total? showAbsGroup: true, // show or hide Abs area expAbsGroup: true, // expand or collapse Abs area autoRefresh: false, // Refresh wallet when holding $Huddle }, address: { eth: [], sol: [], abs: [], }, token: { pengu: { total: 0 } }, collections: { pudgyrods: { contract_address: '0x062e691c2054de82f28008a8ccc6d7a1c8ce060d', slug: 'pudgyrods', qty: 0 }, lilpudgys: { contract_address: '0x524cab2ec69124574082676e6f654a18df49a048', slug: 'lilpudgys', qty: 0 }, pudgypenguins: { contract_address: '0xbd3531da5cf5857e7cfaa92426877b022e612cf8', slug: 'pudgypenguins', qty: 0 } }, abs: { } }; let userData = JSON.parse(localStorage.getItem('userData')); if (!userData) { userData = defaultSettings; localStorage.setItem('userData', JSON.stringify(userData)); let $popoverElement = $('#firstWelcomeLink'); $popoverElement.popover(); $popoverElement.popover('show'); setTimeout(function () { $popoverElement.popover('hide'); }, 5500); }else{ if (!userData.hasOwnProperty('abs')) { userData.abs = {}; localStorage.setItem('userData', JSON.stringify(userData)); } } function updateData(populate = true) { const $dataElements = $('[data-sync-key]'); userData = JSON.parse(localStorage.getItem('userData')); if (populate == true) { populateABSData(userData.abs); } $dataElements.addClass('skeleton-bg'); if (userData.settings.isMasked == false){ $('#pengu_amount').text(userData.token.pengu.total.toLocaleString('en-US')); $('#qty_pudgypenguins').text(userData.collections.pudgypenguins.qty); $('#qty_lilpudgys').text(userData.collections.lilpudgys.qty); $('#qty_pudgyrods').text(userData.collections.pudgyrods.qty); } $('#pengu_amount').data('original', userData.token.pengu.total.toLocaleString('en-US')); $('#qty_pudgypenguins').data('original', userData.collections.pudgypenguins.qty); $('#qty_lilpudgys').data('original', userData.collections.lilpudgys.qty); $('#qty_pudgyrods').data('original', userData.collections.pudgyrods.qty); var wait = 0; if (populate == true) { wait = 1000; } setTimeout(function (){ $.ajax({ url: '/ajax/hb', method: 'POST', data: { huddle_xFkiLamdyJgamk:'fb683d0ee1eb9e48ca04e1bda17edbea', data: userData, }, success: function (data) { var response = jQuery.parseJSON(data); if (userData.settings.isMasked == false){ $('#total_eth_amount').text(response?.data?.Z9W2M5X3); $('#total_fiat').text($('#total_fiat').data('prefix') + response?.data?.Y4C6L7J9); }else{ $('#total_fiat').data('original', response?.data?.Y4C6L7J9); $('#total_eth_amount').data('original', response?.data?.Z9W2M5X3); } $('#floor_pudgypenguins').text(response?.data?.J9B4M7K2); $('#floor_lilpudgys').text(response?.data?.O3F7M9X4); $('#floor_pudgyrods').text(response?.data?.T8B4J9P3); if (userData.settings.priceAs == 'fiat') { if (userData.settings.isMasked == false){ $('#total_pudgypenguins').text(response?.data?.M8D9C4N7); $('#total_lilpudgys').text(response?.data?.R1M3Y7L6); $('#total_pudgyrods').text(response?.data?.W3Y9X5M1); $('#total_pengu_fiat').text(response?.data?.E4L7C2Z6); } $('#total_pudgypenguins').data('original', response?.data?.M8D9C4N7); $('#total_lilpudgys').data('original', response?.data?.R1M3Y7L6); $('#total_pudgyrods').data('original', response?.data?.W3Y9X5M1); $('#total_pengu_fiat').data('original', response?.data?.E4L7C2Z6); }else{ if (userData.settings.isMasked == false){ $('#total_pudgypenguins').text(response?.data?.N2P6L1J3); $('#total_lilpudgys').text(response?.data?.S6X5C2N9); $('#total_pudgyrods').text(response?.data?.X2P4B8T6); $('#total_pengu_fiat').text(response?.data?.A5N3Y6V4); } $('#total_pudgypenguins').data('original', response?.data?.N2P6L1J3); $('#total_lilpudgys').data('original', response?.data?.S6X5C2N9); $('#total_pudgyrods').data('original', response?.data?.X2P4B8T6); $('#total_pengu_fiat').data('original', response?.data?.A5N3Y6V4); } $('#pengu_price_fiat').text(response?.data?.B9D6W3P8); $('#huddle_price').text(response?.data?.G3W5P8D2); $('#eth_price').text(response?.data?.H7C2X9M4); $('#sol_price').text(response?.data?.BJK79HBM); const abstokens = response?.data?.abstokens || {}; const absnfts = response?.data?.absnfts || {}; $.each(abstokens, function(key, info) { var k = String(key).toLowerCase(); // adjust if IDs aren't lowercase var total = Number(info?.total) || 0; var totalETH = Number(info?.totalETH) || 0; var price = Number(info?.price) || 0; // Update total_{key}_fiat var $totalEl = $(`#total_${k}_fiat`); if ($totalEl.length) { if (userData.settings.priceAs == 'fiat') { var formattedTotal = total.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 }) $totalEl.attr('data-original', formattedTotal); if (userData.settings.isMasked == false){ $totalEl.text(formattedTotal); } }else{ var formattedTotalETH = totalETH.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 }) $totalEl.attr('data-original', formattedTotalETH); if (userData.settings.isMasked == false){ $totalEl.text(formattedTotalETH); } } } // Update {key}_price_fiat var $priceEl = $(`#${k}_price_fiat`); if ($priceEl.length) { $priceEl.text(price.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 7 })); } }); $.each(absnfts, function(key, info) { var k = String(key).toLowerCase(); // adjust if IDs aren't lowercase var total = Number(info?.total) || 0; var totalETH = Number(info?.totalETH) || 0; var price = Number(info?.price) || 0; // Update total_{key}_fiat var $totalEl = $(`#total_${k}`); if ($totalEl.length) { if (userData.settings.priceAs == 'fiat') { if (userData.settings.isMasked == false){ $totalEl.text(total.toLocaleString()); } $totalEl.attr('data-original', total.toLocaleString()); }else{ if (userData.settings.isMasked == false){ $totalEl.text(totalETH.toLocaleString()); } $totalEl.attr('data-original', totalETH.toLocaleString()); } } // Update {key}_price_fiat var $priceEl = $(`#floor_${k}`); if ($priceEl.length) { $priceEl.text(price.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 7 })); } }); setTimeout(() => { $dataElements.removeClass('skeleton-bg'); }, 500); }, error: function () { $dataElements.removeClass('skeleton-bg'); } }); }, wait); } let pollingInterval; const pollingTime = 4500; const pollingUrl = '/ajax/update'; function resetCountdownBar() { const $bar = $('#countdown-bar'); $bar.removeClass('animate'); void $bar[0].offsetWidth; $bar.addClass('animate'); } function startPolling(runPop = true) { resetCountdownBar(); updateData(runPop); if (pollingInterval) return; pollingInterval = setInterval(() => { resetCountdownBar(); updateData(false); }, pollingTime); } function stopPolling() { clearInterval(pollingInterval); pollingInterval = null; $('#countdown-bar').removeClass('animate'); } function updateCurrencyLabel() { const priceAs = userData.settings.priceAs; const currency = userData.settings.currency; $('.main_currency_label').text(currency); if (priceAs === 'fiat') { $('.currency_label').text(currency); $('.pengu_total_currency_label').text(currency); } else { $('.currency_label').text('ETH'); $('.pengu_total_currency_label').text('SOL'); } } function updateSetting(key, value) { userData.settings[key] = value; localStorage.setItem('userData', JSON.stringify(userData)); if (key !== 'isMasked') { updateData(false); } } function getSelectedSettings() { return userData.settings; } function debounce(func, wait) { let timeout; return function (...args) { const later = () => { clearTimeout(timeout); func.apply(this, args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } function updatePillStyle($pill, selected) { const brandColor = $pill.data('brand-color'); const brandDarkColor = $pill.data('brand-dark-color'); const $img = $pill.find('img'); const isDarkMode = $('body').hasClass('dark-mode'); if ($img.length) { let src = $img.attr('src'); let srcset = $img.attr('srcset'); if (selected) { src = src.replace(/\/([^\/]+?)(?:_on)?(@\dx\.png)/, '/$1_on$2'); srcset = srcset.replace(/\/([^\/]+?)(?:_on)?(@\dx\.png)/g, '/$1_on$2'); } else { src = src.replace(/_on(?=@\dx\.png)/, ''); srcset = srcset.replace(/_on(?=@\dx\.png)/g, ''); } $img.attr('src', src); $img.attr('srcset', srcset); } const backgroundColor = selected ? (isDarkMode ? brandDarkColor : brandColor) : (isDarkMode ? '#aaa' : '#fff'); const borderStyle = selected ? (isDarkMode ? `1px solid ${brandDarkColor}` : `1px solid ${brandColor}`) : '0px'; $pill.css({ backgroundColor: backgroundColor, border: borderStyle }); } function restoreSelections() { const settings = userData.settings; applyDarkMode(settings.darkMode); $('.group-content').each(function () { const group = $(this).data('group'); const key = `exp${capitalize(group)}Group`; const $icon = $(`.hm-group-title.${group} .collapse-toggle i`); if (userData.settings[key] === false) { $(this).hide(); $icon.removeClass('uil-angle-down').addClass('uil-angle-up'); } }); ['exchanges', 'marketplaces', 'priceAs'].forEach(key => { const attr = key === 'priceAs' ? 'price-as' : key; $(`[data-${attr}]`).each(function () { const $pill = $(this); if ($pill.data(attr) === settings[key]) { $pill.addClass('selected'); updatePillStyle($pill, true); } }); }); $('#fm_currency').val(settings.currency); $('input[name="displayETHTotal"]').prop('checked', settings.displayETHTotal); $('input[name="autoRefresh"]').prop('checked', settings.autoRefresh); $('#total_eth').toggle(settings.displayETHTotal); applyMasking(settings.isMasked); updateCurrencyLabel(); } function applyDarkMode(darkMode) { if (darkMode) { document.body.classList.add('dark-mode'); $('#toggleDarkMode').prop('checked', true); } else { document.body.classList.remove('dark-mode'); $('#toggleDarkMode').prop('checked', false); } } function applyMasking(isMasked) { if (isMasked) { $('#showhideeye').removeClass('uil-eye').addClass('uil-eye-slash'); $('[data-maskable]').each(function () { const $this = $(this); const prefix = $this.data('prefix') || ''; const len = $this.data('len') || 6; const masked = '*'.repeat(len); $this.text(prefix + masked); }); } else { $('#showhideeye').removeClass('uil-eye-slash').addClass('uil-eye'); $('[data-maskable]').each(function () { const $this = $(this); const prefix = $this.data('prefix') || ''; const original = $this.data('original'); $this.text(prefix + original); }); } } function masking() { const isEyeVisible = $('#showhideeye').hasClass('uil-eye'); const newState = isEyeVisible; $('#showhideeye').toggleClass('uil-eye uil-eye-slash'); $('[data-maskable]').each(function () { const $this = $(this); const prefix = $this.data('prefix') || ''; const original = $this.data('original'); const len = $this.data('len') || 6; if (isEyeVisible) { const masked = '*'.repeat(len); $this.text(prefix + masked); } else { $this.text(prefix + original); } }); updateSetting('isMasked', newState); } $('#showhideeye').on('click', masking); $('.option-pill').on('click', function () { const $pill = $(this); const $row = $pill.closest('.option-row'); $row.find('.option-pill').each(function () { $(this).removeClass('selected'); updatePillStyle($(this), false); }); $pill.addClass('selected'); updatePillStyle($pill, true); ['exchanges', 'marketplaces', 'price-as'].forEach(attr => { const value = $pill.data(attr); if (value !== undefined) { const key = attr === 'price-as' ? 'priceAs' : attr; updateSetting(key, value); } }); updateCurrencyLabel(); }); $('#fm_currency').on('change', function () { updateSetting('currency', $(this).val()); updateCurrencyLabel(); }); $('input[name="displayETHTotal"]').on('change', function () { const isChecked = $(this).is(':checked'); updateSetting('displayETHTotal', isChecked); $('#total_eth').toggle(isChecked); }); $('input[name="autoRefresh"]').on('change', function () { var $checkbox = $(this); var isChecked = $checkbox.is(':checked'); if (isChecked && hasHuddle() === false) { var msg = 'Please trade some $Huddle to enable this feature'; $('#refreshMsg').html(msg).fadeIn(); $checkbox.prop('checked', false); setTimeout(function () { $('#refreshMsg').fadeOut(); }, 4000); return; } updateSetting('autoRefresh', isChecked); }); $('#offcanvas-settings').on('shown.bs.offcanvas', function () { stopPolling(); }); $('#offcanvas-settings').on('hidden.bs.offcanvas', function () { startPolling(false); }); $('#modal-wallet').on('shown.bs.modal', function () { stopPolling(); }); $('#modal-wallet').on('hidden.bs.modal', function () { startPolling(false); }); restoreSelections(); var baseTime = 900; var additionalTime = 0; var waitTime = baseTime; var targetTokenAddress = "0x143bd0eac0a811aac43e8aae5d28b624b6b63489"; if (userData && userData.abs) { var absWallets = userData.abs; var walletAddresses = Object.keys(absWallets); var userSettings = userData.settings; var hasTargetToken = walletAddresses.some(address => { var walletData = absWallets[address]; var tokens = walletData.token || []; return tokens.some(token => token.c === targetTokenAddress); }); if (hasTargetToken && userSettings.autoRefresh == true) { var delayPerWallet = 400; walletAddresses.forEach(function(address, index) { setTimeout(function () { var entry = $('.wallet-entry[data-wallet-address="' + address + '"]'); var syncIcon = entry.find('.uil-sync'); if (syncIcon.length) { syncIcon.trigger('click'); } }, index * delayPerWallet); }); additionalTime = delayPerWallet * walletAddresses.length; waitTime = baseTime + additionalTime; setTimeout(function(){ startPolling(); }, 1200); }else{ startPolling(); } } applyDarkMode(userData.settings.darkMode); $('#captureBtn').on('click', function () { const captureArea = document.querySelector('#captureArea'); html2canvas(captureArea).then(canvas => { $('#hm-teaser').css('max-height','50px'); $('#hm-teaser button').hide(); canvas.toBlob(blob => { const imgFile = new File([blob], 'my-huddle-value.png', { type: 'image/png' }); if (/iPhone|iPad|iPod/i.test(navigator.userAgent)) { if (navigator.canShare && navigator.canShare({ files: [imgFile] })) { navigator.share({ files: [imgFile], title: 'My Huddle Value' }).then(() => { $('#statusMessage').text('Image shared successfully!'); }).catch(err => { console.error('Sharing failed:', err); $('#statusMessage').text('Failed to share the image.'); }); } else { $('#statusMessage').text('Sharing not supported on this device.'); } } else { if (navigator.clipboard && navigator.clipboard.write) { const clipboardItem = new ClipboardItem({ 'image/png': blob }); navigator.clipboard.write([clipboardItem]).then(() => { $('#statusMessage').text('Image copied to clipboard!'); }).catch(err => { console.error('Failed to copy:', err); $('#statusMessage').text('Failed to copy image.'); }); } else { console.error('Clipboard API not supported.'); $('#statusMessage').text('Clipboard API not supported.'); } } }, 'image/png'); }).catch(err => { console.error('Error capturing the area:', err); $('#statusMessage').text('Error capturing the area.'); }); }); const $input = $('#fm_wallet_address'); const $button = $('#address_form button'); const $result = $('#wallet_result'); $input.on('input', debounce(function () { const address = $input.val().trim(); $button.prop('disabled', !(address.length >= 32 && address.length <= 44)); if (address.length < 32) return; let walletType = null; if (/^0x[a-fA-F0-9]{40}$/.test(address)) { walletType = 'eth'; } else if (/^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(address)) { walletType = 'sol'; } else { return; } $input.addClass('input-loading'); if (userData.address[walletType]?.some(w => w.wallet === address)) { $input.removeClass('input-loading'); return; } $.ajax({ url: '/ajax/wallet', method: 'POST', data: { huddle_xFkiLamdyJgamk:'fb683d0ee1eb9e48ca04e1bda17edbea', address: address, walletType: walletType }, success: function (data) { $input.removeClass('input-loading'); var response = jQuery.parseJSON(data); if (!userData.address[response.return.type].some(w => w.wallet === response.return.wallet)) { const walletType = response.return.type; const wallet = response.return.wallet; const balances = response.return.balances; const balanceObj = {}; let iconsHTML = ""; let absTokens = []; let absNFTs = []; let abstokenCount = 0; let absnftCount = 0; balances.forEach(bal => { const quantity = parseFloat(bal.quantity || 0); if (quantity === 0) return; if (bal.type === 'token') { userData.token.pengu.total += quantity; balanceObj[bal.name] = quantity; const penguFormatted = formatPengu(quantity); iconsHTML += createWalletIcon( "/assets/img/0xbd3531da5cf5857e7cfaa92426877b022e612cf8.avif", penguFormatted + ' $PENGU', "50%" ); } else if (bal.type === 'nft') { if (userData.collections[bal.name]) { userData.collections[bal.name].qty += quantity; balanceObj[bal.name] = quantity; const logoSlug = userData.collections[bal.name].contract_address; iconsHTML += createWalletIcon( `/assets/img/${logoSlug}.avif`, quantity, "5px" ); } } else if (bal.type === 'abstoken') { absTokens.push({ c: bal.address, n: bal.name, b: quantity }); abstokenCount++; } else if (bal.type === 'absnft') { absNFTs.push({ c: bal.address, n: bal.name, b: quantity }); absnftCount++; } }); // Store Abstract tokens & NFTs in abs if (walletType === 'abs' || absTokens.length > 0 || absNFTs.length > 0) { userData.abs[wallet] = { token: absTokens, nft: absNFTs }; // Append icons for Abstract (if any holdings) if (absTokens.length > 0) { iconsHTML += createWalletIcon( "/assets/img/vendor/abstract_logo_sq.png", `${absTokens.length} tokens`, "50%" ); } if (absNFTs.length > 0) { iconsHTML += createWalletIcon( "/assets/img/vendor/abstract_logo_sq.png", `${absNFTs.length} collections`, "5px" ); } } userData.address[walletType].push({ wallet: wallet, balance: balanceObj }); localStorage.setItem('userData', JSON.stringify(userData)); let walletLogo = "https://cdn.huddle.is/assets/img/eth-logo.png"; if (walletType === "sol") walletLogo = "https://cdn.huddle.is/assets/img/sol-logo.png"; else if (walletType === "abs") walletLogo = "https://cdn.huddle.is/assets/img/abs_logo.png"; const html = createWalletHTML(wallet, walletLogo, iconsHTML, walletType); $('#linked_wallets').append(html); $('#linked_wallet_area').show(); $('#fm_wallet_address').val(''); setTimeout(() => { if (typeof bootstrap !== 'undefined') { var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')); tooltipTriggerList.map(function (tooltipTriggerEl) { return new bootstrap.Tooltip(tooltipTriggerEl); }); } }, 100); } updateData(); }, error: function () { $input.removeClass('input-loading'); } }); })); function formatPengu(pengu) { const num = parseFloat(pengu); if (num >= 1e6) return (num / 1e6).toFixed(1) + 'M'; if (num >= 1e3) return (num / 1e3).toFixed(1) + 'K'; return num.toString(); } function createWalletIcon(imgSrc, tooltip, radius) { return `