wif3 / main.js
FriedsU's picture
Upload 6 files
351f851 verified
// Initialize charts
let priceChart;
let candleChart;
document.addEventListener('DOMContentLoaded', function () {
// Create candlestick chart
const candleCtx = document.getElementById('candleChart');
if (!candleCtx) {
console.error("Canvas element with ID 'candleChart' not found!");
return;
}
const context = candleCtx.getContext('2d');
if (!context) {
console.error("Could not get 2D context from canvas element.");
return;
}
// Generates simple random walk candle data
function generateCandleData(count) {
const data = [];
let date = new Date();
date.setHours(date.getHours() - count, 0, 0, 0);
let open = 80000; // Arbitrary starting point
for (let i = 0; i < count; i++) {
const time = date.getTime();
// Simple random change for close price
const change = (Math.random() - 0.5) * 500;
const close = open + change;
// High and Low based on random volatility around open/close
const high = Math.max(open, close) + Math.random() * 250;
const low = Math.min(open, close) - Math.random() * 250;
data.push({ x: time, o: open, h: high, l: low, c: close });
date.setHours(date.getHours() + 1);
open = close;
}
return data;
}
const candleData = generateCandleData(60);
// --- Update UI with latest price and percentage change ---\n if (candleData.length > 1) { // Need at least 2 points for change\n const latestDataPoint = candleData[candleData.length - 1];\n const previousDataPoint = candleData[candleData.length - 2];\n const latestPrice = latestDataPoint.c; // Latest closing price\n const previousPrice = previousDataPoint.c; // Previous closing price\n\n const priceChange = latestPrice - previousPrice;\n const percentageChange = (priceChange / previousPrice) * 100;\n const isPriceUp = priceChange >= 0;\n\n // Format the price (example: $XX,XXX.XX)\n const formattedPrice = `$${latestPrice.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;\n // Format percentage (e.g., +1.23%, -0.50%)\n const formattedPercentage = `${percentageChange >= 0 ? '+' : ''}${percentageChange.toFixed(2)}%`;\n // Format rounded price for watchlist\n const formattedPriceRounded = `$${Math.round(latestPrice).toLocaleString('en-US')}`;\n\n // Get UI elements\n const headerPriceElement = document.getElementById('latest-price-header');\n const headerChangeElement = document.getElementById('latest-price-change-header');\n const watchlistPriceElement = document.getElementById('watchlist-btc-price');\n // TODO: Add ID and update watchlist percentage element too\n \n // Update header price\n if (headerPriceElement) {\n headerPriceElement.textContent = formattedPrice;\n headerPriceElement.classList.toggle('text-green-mono', isPriceUp);\n headerPriceElement.classList.toggle('text-red-mono', !isPriceUp);\n }\n\n // Update header percentage change\n if (headerChangeElement) {\n headerChangeElement.textContent = formattedPercentage;\n headerChangeElement.classList.toggle('text-green-mono', isPriceUp);\n headerChangeElement.classList.toggle('text-red-mono', !isPriceUp);\n }\n\n // Update watchlist BTC price\n if (watchlistPriceElement) {\n watchlistPriceElement.textContent = formattedPriceRounded;\n // Note: Watchlist percentage/color update requires separate logic or IDs\n }\n }\n // --- End Price Update ---\n
try {
candleChart = new Chart(context, {
type: 'candlestick',
data: {
datasets: [{
label: 'BTC/USDT',
data: candleData,
color: { up: '#10b981', down: '#ef4444', unchanged: '#94a3b8' },
borderColor: { up: '#10b981', down: '#ef4444', unchanged: '#94a3b8' },
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: { legend: { display: false } },
scales: {
x: {
type: 'time',
time: {
unit: 'hour', // Changed from day
displayFormats: { hour: 'HH:mm' } // Changed format
},
grid: { color: 'rgba(255, 255, 255, 0.05)' },
ticks: { color: '#94a3b8' }
},
y: {
position: 'right',
grid: {
color: 'rgba(255, 255, 255, 0.1)', // Slightly more visible grid
borderDash: [5, 5] // Added dashed lines
},
ticks: { color: '#94a3b8' }
}
}
}
});
// ResizeObserver to handle chart resizing when container changes
const chartContainer = document.querySelector('.main-chart-container');
if (chartContainer && candleChart) {
const resizeObserver = new ResizeObserver(entries => {
// Removed requestAnimationFrame for potentially faster reaction
if (!Array.isArray(entries) || !entries.length) {
return;
}
// Call resize directly on the chart instance
candleChart.resize();
});
resizeObserver.observe(chartContainer);
} else {
console.error("Could not find chart container or chart instance for ResizeObserver.");
}
} catch (error) {
console.error("Error creating chart:", error);
}
// Buy/Sell button functionality
const buyBtn = document.getElementById('buy-btn');
const sellBtn = document.getElementById('sell-btn');
const placeOrderBtn = document.getElementById('place-order-btn');
buyBtn.addEventListener('click', function () {
buyBtn.classList.add('bg-green-600', 'text-white');
buyBtn.classList.remove('bg-green-900', 'bg-opacity-30', 'text-green-400');
sellBtn.classList.add('bg-red-900', 'bg-opacity-30', 'text-red-400');
sellBtn.classList.remove('bg-red-600', 'text-white');
placeOrderBtn.classList.add('bg-green-600');
placeOrderBtn.classList.remove('bg-red-600');
placeOrderBtn.textContent = 'Buy BTC';
});
sellBtn.addEventListener('click', function () {
sellBtn.classList.add('bg-red-600', 'text-white');
sellBtn.classList.remove('bg-red-900', 'bg-opacity-30', 'text-red-400');
buyBtn.classList.add('bg-green-900', 'bg-opacity-30', 'text-green-400');
buyBtn.classList.remove('bg-green-600', 'text-white');
placeOrderBtn.classList.add('bg-red-600');
placeOrderBtn.classList.remove('bg-green-600');
placeOrderBtn.textContent = 'Sell BTC';
});
});
// Feed switching functionality
function switchFeed(feedType) {
// Preserve chart dimensions by forcing a layout calculation before DOM changes
if (candleChart) {
candleChart.resize();
}
// Switch feed tabs - use visibility instead of display to maintain layout
const feeds = ['posts-feed', 'chat-feed', 'video-feed'];
feeds.forEach(feed => {
const feedElement = document.getElementById(feed);
if (feed === feedType + '-feed') {
feedElement.style.visibility = 'visible';
feedElement.style.zIndex = '1';
} else {
feedElement.style.visibility = 'hidden';
feedElement.style.zIndex = '0';
}
});
// Update active button state
const buttons = document.querySelectorAll('.feed-switch-btn');
buttons.forEach(btn => {
btn.classList.remove('active');
if (btn.getAttribute('onclick') === `switchFeed('${feedType}')`) {
btn.classList.add('active');
}
});
// Force chart resize after tab switch to ensure proper rendering
if (candleChart) {
// Immediate resize
candleChart.resize();
// Another resize after a small delay to ensure everything has settled
setTimeout(() => {
if (candleChart) {
candleChart.resize();
}
}, 50);
}
}
// Placeholder function for indicator buttons
function updateChart(indicatorType) {
// In a real implementation, you would update the chart data or options here
// based on the selected indicator.
// For now, we can just highlight the button.
// Remove active class from all indicator buttons inside the dropdown
const indicatorButtons = document.querySelectorAll('#indicators-menu button'); // Updated selector
indicatorButtons.forEach(btn => btn.classList.remove('indicator-active'));
// Add active class to the clicked button
const clickedButton = document.getElementById(`${indicatorType}-btn`);
if (clickedButton) {
clickedButton.classList.add('indicator-active');
}
// Example: Add/remove datasets based on indicatorType (requires more complex logic)
// if (indicatorType === 'sma') { ... }
}
// Dropdown Toggle Function
function toggleDropdown(menuId) {
const menu = document.getElementById(menuId);
if (menu) {
menu.classList.toggle('hidden');
// Update aria-expanded attribute for accessibility
const button = document.querySelector(`[onclick*="toggleDropdown('${menuId}')"]`);
if (button) {
const isExpanded = !menu.classList.contains('hidden');
button.setAttribute('aria-expanded', isExpanded.toString());
}
}
}
// Close Dropdown Function
function closeDropdown(menuId) {
const menu = document.getElementById(menuId);
if (menu && !menu.classList.contains('hidden')) {
menu.classList.add('hidden');
const button = document.querySelector(`[onclick*="toggleDropdown('${menuId}')"]`);
if (button) {
button.setAttribute('aria-expanded', 'false');
}
}
}
// Close dropdown if clicked outside
document.addEventListener('click', function(event) {
const dropdownContainer = document.getElementById('indicators-dropdown'); // ID of the main dropdown container div
const menu = document.getElementById('indicators-menu');
// Check if the click is outside the dropdown container
if (dropdownContainer && menu && !dropdownContainer.contains(event.target)) {
closeDropdown('indicators-menu');
}
});
// --- Resizable Panel Logic ---
document.addEventListener('DOMContentLoaded', () => {
const handle = document.getElementById('bottom-panel');
const chartArea = document.getElementById('chart-area');
const tabContent = document.getElementById('tab-content');
const mainTerminal = document.getElementById('main-terminal'); // Parent container
if (!handle || !chartArea || !tabContent || !mainTerminal) {
console.error("Resizable panel elements not found.");
return;
}
let isResizing = false;
let startY = 0;
let startChartHeight = 0;
let startTabContentHeight = 0;
const startResize = (e) => {
isResizing = true;
startY = e.clientY;
startChartHeight = chartArea.offsetHeight;
startTabContentHeight = tabContent.offsetHeight;
// Prevent text selection during drag
document.body.style.userSelect = 'none';
document.body.style.cursor = 'row-resize'; // Indicate resizing globally
// Add listeners to the whole document for robust dragging
document.addEventListener('mousemove', doResize);
document.addEventListener('mouseup', stopResize);
// Also add mouseleave from body to stop if cursor leaves window
document.body.addEventListener('mouseleave', stopResize);
};
const doResize = (e) => {
if (!isResizing) return;
const deltaY = e.clientY - startY;
const totalHeight = mainTerminal.clientHeight; // Use parent height
const handleHeight = handle.offsetHeight;
// Calculate new heights, respecting minimums (e.g., 50px)
let newChartHeight = Math.max(50, startChartHeight + deltaY);
let newTabContentHeight = Math.max(50, startTabContentHeight - deltaY);
// Ensure total height doesn't exceed parent minus handle
const availableHeight = totalHeight - handleHeight;
if (newChartHeight + newTabContentHeight > availableHeight) {
// Prioritize adjusting the element being shrunk (tabContent)
newTabContentHeight = availableHeight - newChartHeight;
if (newTabContentHeight < 50) {
newTabContentHeight = 50;
newChartHeight = availableHeight - 50;
}
}
// Apply heights using flex-basis for better flexbox behavior
chartArea.style.flexBasis = `${newChartHeight}px`;
tabContent.style.flexBasis = `${newTabContentHeight}px`;
// Crucially, resize the chart canvas after changing container size
if (window.candleChart) {
window.candleChart.resize();
}
};
const stopResize = () => {
if (isResizing) {
isResizing = false;
// Remove global listeners
document.removeEventListener('mousemove', doResize);
document.removeEventListener('mouseup', stopResize);
document.body.removeEventListener('mouseleave', stopResize);
// Restore default body styles
document.body.style.userSelect = '';
document.body.style.cursor = '';
}
};
handle.addEventListener('mousedown', startResize);
});
//