import ApexCharts from '../thirdparty/apexchartsWrapper.js'; import { PeityAPI } from './../thirdparty/peity.es6.js'; // Control Center Dashboard document.addEventListener('DOMContentLoaded', function () { 'use strict'; /***************************************************************/ /* Script for the area chart with live updates but no animation*/ /***************************************************************/ // Generate initial random data with natural patterns const generateData = (count) => { const data = []; let value = 50; // Start from middle for (let i = 0; i < count; i++) { // Create more natural movements with smaller changes const change = (Math.random() - 0.5) * 8; // Smaller random changes value = Math.min(Math.max(value + change, 10), 90); // Keep within bounds data.push(Math.round(value)); } return data; }; // Area Chart const areaChartOptions = { series: [{ name: 'Activity', data: generateData(180) }], chart: { type: 'area', height: 300, toolbar: { show: false }, zoom: { enabled: false }, animations: { enabled: false }, parentHeightOffset: 0, sparkline: { enabled: false } }, colors: [window.colorMap.primary[500].hex], dataLabels: { enabled: false }, tooltip: { enabled: true, followCursor: true }, stroke: { curve: 'straight', width: 1.5, colors: [window.colorMap.primary[500].hex] }, fill: { type: 'gradient', gradient: { shadeIntensity: 1, opacityFrom: 0.45, opacityTo: 0.05, stops: [0, 95, 100], colorStops: [ { offset: 0, color: window.colorMap.primary[500].hex, opacity: 0.4 }, { offset: 100, color: window.colorMap.primary[500].hex, opacity: 0.1 } ] } }, grid: { show: true, borderColor: window.colorMap.bootstrapVars.bodyColor.rgba(0.08), strokeDashArray: 5, position: 'back', padding: { left: -5, right: 0, top: -20, bottom: -5 }, xaxis: { lines: { show: false } }, yaxis: { lines: { show: true } } }, xaxis: { type: 'numeric', tickAmount: 10, labels: { style: { colors: window.colorMap.bootstrapVars.bodyColor.rgba(0.5), fontSize: '10px', }, show: true, offsetX: 10 }, axisBorder: { show: false }, axisTicks: { show: false } }, yaxis: { min: 0, max: 100, tickAmount: 5, show: true, labels: { style: { colors: window.colorMap.bootstrapVars.bodyColor.rgba(0.5), fontSize: '10px', }, formatter: function (value) { return value; }, show: true, offsetX: -15, align: 'left' } }, legend: { show: false } }; // Initialize the chart const areaChart = new ApexCharts( document.querySelector('#apex-chart-1'), areaChartOptions ); areaChart.render(); // Create and add the live update checkbox const controlDiv = document.createElement('div'); controlDiv.className = 'mb-0 position-relative'; controlDiv.innerHTML = `
`; document.querySelector('#apex-chart-1').parentNode.insertBefore(controlDiv, document.querySelector('#apex-chart-1')); // Live update functionality with smoother transitions let updateInterval; const checkbox = document.querySelector('#liveUpdateCheck'); checkbox.addEventListener('change', function () { if (this.checked) { // Start live updates updateInterval = setInterval(() => { const currentData = areaChart.w.config.series[0].data; const lastValue = currentData[currentData.length - 1]; const change = (Math.random() - 0.5) * 8; const newValue = Math.round(Math.min(Math.max(lastValue + change, 10), 90)); const newData = [...currentData.slice(1), newValue]; areaChart.updateSeries([{ data: newData }], true); }, 1000); } else { // Stop live updates clearInterval(updateInterval); } }); /***************************************************************/ /* Script that generates random images for sound clips in chat */ /***************************************************************/ // Create a hidden canvas for rendering const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); // Waveform parameters const height = 35; // Fixed height like WhatsApp const barWidth = 2; // Width of each bar const barGap = 1; // Gap between bars const barColor = '#aaaaaa'; // Gray like WhatsApp const maxBarHeight = height * 0.4; // Max height (40% of canvas height) const minWidth = 100; // Minimum canvas width const maxWidth = 300; // Maximum canvas width // Function to generate random waveform function generateRandomWaveform(samples) { const frequency = 0.05 + Math.random() * 0.1; // Random frequency for wave variation const amplitude = maxBarHeight * 0.7; // Base amplitude const noiseLevel = 0.3 + Math.random() * 0.4; // Random noise level const waveform = []; for (let i = 0; i < samples; i++) { // Combine sine wave with noise for audio-like effect const sineValue = Math.sin(i * frequency) * amplitude + Math.sin(i * frequency * 2) * (amplitude / 3); const noise = (Math.random() - 0.5) * amplitude * noiseLevel; const value = Math.abs(sineValue + noise); // Absolute value for bar height waveform.push(Math.min(value, maxBarHeight)); // Cap at max height } return waveform; } // Function to draw WhatsApp-style waveform function drawSoundwave(waveform, width) { ctx.clearRect(0, 0, width, height); ctx.fillStyle = barColor; // Draw vertical bars for (let i = 0; i < waveform.length; i++) { const x = i * (barWidth + barGap); const barHeight = waveform[i]; const y = height / 2 - barHeight / 2; // Center the bar vertically // Draw bar (rectangle) ctx.fillRect(x, y, barWidth, barHeight); } } // Function to render soundwave for a div function renderSoundwave(div) { // Generate random canvas width const width = Math.floor(minWidth + Math.random() * (maxWidth - minWidth + 1)); canvas.width = width; // Set canvas width canvas.height = height; // Calculate number of bars based on width const samples = Math.floor(width / (barWidth + barGap)); // Generate random waveform const waveform = generateRandomWaveform(samples); // Draw on canvas drawSoundwave(waveform, width); // Create image element const img = document.createElement('img'); img.src = canvas.toDataURL('image/png'); img.alt = 'WhatsApp-Style Soundwave'; // Clear div and append image div.innerHTML = ''; div.appendChild(img); } // Find all sound-image divs and render unique waveforms document.querySelectorAll('.sound-image').forEach(div => { renderSoundwave(div); }); /***************************************************************/ /* Script for the calendar */ /***************************************************************/ const eventData = [ { id: '1', title: 'Team Meeting', start: new Date(new Date().setHours(10, 0)), end: new Date(new Date().setHours(11, 30)), backgroundColor: 'var(--primary-500)', borderColor: 'var(--primary-600)', description: 'Weekly team status meeting', location: 'Conference Room A' }, { id: '2', title: 'Client Call', start: new Date(new Date().setDate(new Date().getDate() + 1)), allDay: true, backgroundColor: 'var(--success-500)', borderColor: 'var(--success-600)', description: 'Quarterly review with major client', location: 'Zoom Meeting' }, { id: '3', title: 'Product Launch', start: new Date(new Date().setDate(new Date().getDate() + 3)), end: new Date(new Date().setDate(new Date().getDate() + 3)).setHours(15, 0), backgroundColor: 'var(--danger-500)', borderColor: 'var(--danger-600)', description: 'New product launch event', location: 'Main Auditorium' }, { id: '4', title: 'Deadline: Q3 Report', start: new Date(new Date().setDate(new Date().getDate() + 5)), allDay: true, backgroundColor: 'var(--warning-500)', borderColor: 'var(--warning-600)', description: 'Submit quarterly financial reports', location: 'Finance Department' }, { id: '5', title: 'Training Session', start: new Date(new Date().setDate(new Date().getDate() - 2)), end: new Date(new Date().setDate(new Date().getDate() - 2)).setHours(16, 0), backgroundColor: 'var(--info-500)', borderColor: 'var(--info-600)', description: 'New software training for all employees', location: 'Training Room B' }, { id: '6', title: 'Board Meeting', start: new Date(new Date().setDate(new Date().getDate() + 7)), allDay: true, backgroundColor: 'var(--danger-500)', borderColor: 'var(--danger-700)', description: 'Annual board meeting with stakeholders', location: 'Executive Boardroom' }, { id: '7', title: 'Website Maintenance', start: new Date(new Date().setDate(new Date().getDate() - 1)).setHours(23, 0), end: new Date(new Date().setDate(new Date().getDate())).setHours(5, 0), backgroundColor: 'var(--info-500)', borderColor: 'var(--info-600)', description: 'Scheduled website maintenance window', location: 'IT Department' }, { id: '8', title: 'Team Building', start: new Date(new Date().setDate(new Date().getDate() + 12)), end: new Date(new Date().setDate(new Date().getDate() + 12)).setHours(16, 0), backgroundColor: 'var(--primary-300)', borderColor: 'var(--primary-400)', description: 'Annual team building activities', location: 'City Park' }, { id: '9', title: 'Client Dinner', start: new Date(new Date().setDate(new Date().getDate() + 4)).setHours(19, 0), end: new Date(new Date().setDate(new Date().getDate() + 4)).setHours(21, 0), backgroundColor: 'var(--success-300)', borderColor: 'var(--success-400)', description: 'Dinner with potential investors', location: 'Downtown Restaurant' }, { id: '10', title: 'Vacation', start: new Date(new Date().setDate(new Date().getDate() + 14)), end: new Date(new Date().setDate(new Date().getDate() + 21)), backgroundColor: 'var(--bs-teal)', borderColor: 'var(--bs-teal)', description: 'Annual vacation time', location: 'Beach Resort' }, { id: '11', title: 'Marketing Campaign', start: new Date(new Date().setDate(new Date().getDate() + 2)), end: new Date(new Date().setDate(new Date().getDate() + 2)).setHours(17, 0), backgroundColor: 'var(--bs-purple)', borderColor: 'var(--bs-purple)', description: 'Launch new product marketing campaign', location: 'Marketing Department', category: 'marketing' }, { id: '12', title: 'Sales Meeting', start: new Date(new Date().setDate(new Date().getDate() + 3)).setHours(9, 0), end: new Date(new Date().setDate(new Date().getDate() + 3)).setHours(10, 30), backgroundColor: 'var(--success-600)', borderColor: 'var(--success-700)', description: 'Monthly sales team catchup', location: 'Conference Room B', category: 'sales' }, { id: '13', title: 'Development Sprint Review', start: new Date(new Date().setDate(new Date().getDate() + 6)).setHours(14, 0), end: new Date(new Date().setDate(new Date().getDate() + 6)).setHours(16, 0), backgroundColor: 'var(--bs-indigo)', borderColor: 'var(--bs-indigo)', description: 'End of sprint review with stakeholders', location: 'Dev Team Area', category: 'development' }, { id: '14', title: 'Tech Conference', start: new Date(new Date().setDate(new Date().getDate() + 8)), end: new Date(new Date().setDate(new Date().getDate() + 10)), backgroundColor: 'var(--bs-indigo)', borderColor: 'var(--bs-indigo)', description: 'Annual tech conference for industry professionals', location: 'Convention Center', category: 'conference' }, { id: '15', title: 'Dentist Appointment', start: new Date(new Date().setDate(new Date().getDate() + 2)).setHours(14, 0), end: new Date(new Date().setDate(new Date().getDate() + 2)).setHours(15, 0), backgroundColor: 'var(--warning-500)', borderColor: 'var(--warning-600)', description: 'Routine dental checkup', location: 'Downtown Clinic', category: 'personal' }, { id: '16', title: 'Project Milestone: Beta Release', start: new Date(new Date().setDate(new Date().getDate() + 15)), allDay: true, backgroundColor: 'var(--success-500)', borderColor: 'var(--success-600)', description: 'Beta release of the new app', location: 'Development Team', category: 'development' }, { id: '17', title: 'Company Announcement', start: new Date(new Date().setDate(new Date().getDate() + 1)).setHours(11, 0), end: new Date(new Date().setDate(new Date().getDate() + 1)).setHours(11, 30), backgroundColor: 'var(--primary-500)', borderColor: 'var(--primary-600)', description: 'Announcement of new company policies', location: 'Main Hall', category: 'announcement' }, { id: '18', title: 'Weekly Team Sync', start: new Date(new Date().setDate(new Date().getDate() + 4)).setHours(9, 0), end: new Date(new Date().setDate(new Date().getDate() + 4)).setHours(9, 30), backgroundColor: 'var(--primary-400)', borderColor: 'var(--primary-500)', description: 'Recurring weekly sync for project updates', location: 'Meeting Room C', category: 'team', extendedProps: { recurrence: 'weekly' } } ]; // Initialize the calendar const calendarEl = document.getElementById('myCalendar'); const calendar = new FullCalendar.Calendar(calendarEl, { initialView: 'dayGridMonth', themeSystem: 'bootstrap', headerToolbar: { right: 'today prev,next', left: 'title' }, footerToolbar: { left: 'dayGridMonth,timeGridWeek,timeGridDay,listWeek' }, buttonText: { today: 'Today', month: 'Month', week: 'Week', day: 'Day', list: 'List' }, dayMaxEvents: 2, // Show only 2 events per day, then show "more" link height: '760px', // Set minimum height for desktop eventSources: [ { events: eventData } ] }); calendar.render(); /***************************************************************/ /* Script to display current day, month, year */ /***************************************************************/ const currentDate = new Date(); const day = currentDate.getDate(); const month = currentDate.getMonth() + 1; // Months are 0-based const year = currentDate.getFullYear(); //document.querySelector('.currentDate').innerHTML = `${day} ${month} ${year}`; /***************************************************************/ /* Subscription Chart */ /***************************************************************/ // Generate hourly data for subscriptions const generateHourlyData = () => { const today = [ { x: '3am', y: 0 }, { x: '4am', y: 0 }, { x: '5am', y: 5000 }, { x: '6am', y: 7500 }, { x: '7am', y: 12000 }, { x: '8am', y: 22500 }, { x: '9am', y: 27000 }, { x: '10am', y: 37500 }, { x: '11am', y: 35000 }, { x: '12m', y: 37500 }, { x: '1pm', y: 9500 }, { x: '2pm', y: 0 }, { x: '3pm', y: 0 }, { x: '4pm', y: 0 }, { x: '5pm', y: 0 } ]; const yesterday = [ { x: '3am', y: 0 }, { x: '4am', y: 0 }, { x: '5am', y: 0 }, { x: '6am', y: 5500 }, { x: '7am', y: 15000 }, { x: '8am', y: 20000 }, { x: '9am', y: 35000 }, { x: '10am', y: 30500 }, { x: '11am', y: 35000 }, { x: '12m', y: 27500 }, { x: '1pm', y: 42500 }, { x: '2pm', y: 32500 }, { x: '3pm', y: 37500 }, { x: '4pm', y: 27500 }, { x: '5pm', y: 17500 } ]; return { today: today, yesterday: yesterday }; }; const hourlyData = generateHourlyData(); const subscriptionChartOptions = { series: [{ name: 'Today', data: hourlyData.today }, { name: 'Yesterday', data: hourlyData.yesterday }], chart: { height: '100%', maxHeight: 275, width: '100%', type: 'bar', toolbar: { show: false }, sparkline: { enabled: false } }, plotOptions: { bar: { horizontal: false, columnWidth: '70%', endingShape: 'rounded', borderRadius: 0, } }, colors: [window.colorMap.success[500].hex, window.colorMap.bootstrapVars.bodyColor.rgba(0.7)], dataLabels: { enabled: false }, grid: { show: true, borderColor: window.colorMap.bootstrapVars.bodyColor.rgba(0.08), strokeDashArray: 5, position: 'back', padding: { left: -5, right: 0, top: -20, bottom: -5 }, xaxis: { lines: { show: true } }, yaxis: { lines: { show: false } } }, xaxis: { type: 'category', labels: { style: { colors: '#8e8da4', fontSize: '10px' }, show: true }, axisBorder: { show: false }, axisTicks: { show: false } }, yaxis: { min: 0, max: 50000, tickAmount: 4, labels: { style: { colors: '#8e8da4', fontSize: '10px' }, formatter: function (value) { return '$' + Math.floor(value / 1000) + 'k'; }, show: true, offsetX: -15, align: 'left' } }, tooltip: { enabled: true, followCursor: true, theme: 'dark', y: { formatter: function (value) { return '$' + value.toLocaleString(); } } }, legend: { show: false } }; // Initialize the subscriptions chart const subscriptionsChart = new ApexCharts( document.querySelector('#subscriptionsChart'), subscriptionChartOptions ); subscriptionsChart.render(); /***************************************************************/ /* Secession Chart */ /***************************************************************/ // Calculate total for percentages const populationData = [ { label: "Current Usage", value: 4119630000, color: window.colorMap.primary[500].hex }, { label: "Net Usage", value: 590950000, color: window.colorMap.info[500].hex }, { label: "Users Blocked", value: 1012960000, color: window.colorMap.warning[500].hex }, { label: "Custom Cases", value: 95100000, color: window.colorMap.danger[500].hex }, { label: "Test Logs", value: 727080000, color: window.colorMap.success[500].hex }, { label: "Uptime Records", value: 344120000, color: window.colorMap.bootstrapVars.bodyColor.rgba(0.7) } ]; const total = populationData.reduce((sum, item) => sum + item.value, 0); const secessionChartOptions = { series: populationData.map(item => item.value), chart: { type: 'donut', height: '100%', maxHeight: 275, width: '100%' }, colors: populationData.map(item => item.color), plotOptions: { pie: { donut: { size: '50%' } } }, labels: populationData.map(item => item.label), dataLabels: { enabled: false }, legend: { show: false }, tooltip: { enabled: false, y: { formatter: function (value) { // Calculate percentage const percentage = ((value / total) * 100).toFixed(1); // Format the population number with commas const population = value.toLocaleString(); return `${percentage}% (${population})`; } } }, stroke: { width: 0 } }; // Initialize the secession chart const secessionChart = new ApexCharts( document.querySelector('#secessionChart'), secessionChartOptions ); secessionChart.render(); /***************************************************************/ /* Global View */ /***************************************************************/ // GDP data per country (raw data with lowercase keys) const rawGdpData = { "af": 16.63, "al": 0, "dz": 158.97, "ao": 85.81, "ag": 1.1, "ar": 351.02, "am": 8.83, "au": 1219.72, "at": 366.26, "az": 52.17, "bs": 7.54, "bh": 21.73, "bd": 105.4, "bb": 3.96, "by": 52.89, "be": 461.33, "bz": 1.43, "bj": 6.49, "bt": 1.4, "bo": 19.18, "ba": 16.2, "bw": 12.5, "br": 2023.53, "bn": 11.96, "bg": 44.84, "bf": 8.67, "bi": 1.47, "kh": 11.36, "cm": 21.88, "ca": 1563.66, "cv": 1.57, "cf": 2.11, "td": 7.59, "cl": 199.18, "cn": 5745.13, "co": 283.11, "km": 0.56, "cd": 12.6, "cg": 11.88, "cr": 35.02, "ci": 22.38, "hr": 59.92, "cy": 22.75, "cz": 195.23, "dk": 304.56, "dj": 1.14, "dm": 0.38, "do": 50.87, "ec": 61.49, "eg": 216.83, "sv": 21.8, "gq": 14.55, "er": 2.25, "ee": 19.22, "et": 30.94, "fj": 3.15, "fi": 231.98, "fr": 2555.44, "ga": 12.56, "gm": 1.04, "ge": 11.23, "de": 3305.9, "gh": 18.06, "gr": 305.01, "gd": 0.65, "gt": 40.77, "gn": 4.34, "gw": 0.83, "gy": 2.2, "ht": 6.5, "hn": 15.34, "hk": 226.49, "hu": 132.28, "is": 0, "in": 1430.02, "id": 695.06, "ir": 337.9, "iq": 84.14, "ie": 204.14, "il": 201.25, "it": 2036.69, "jm": 13.74, "jp": 5390.9, "jo": 27.13, "kz": 129.76, "ke": 32.42, "ki": 0.15, "kw": 117.32, "kg": 4.44, "la": 6.34, "lv": 23.39, "lb": 39.15, "ls": 1.8, "lr": 0.98, "lt": 35.73, "lu": 52.43, "mk": 9.58, "mg": 8.33, "mw": 5.04, "my": 218.95, "mv": 1.43, "ml": 9.08, "mt": 7.8, "mr": 3.49, "mu": 9.43, "mx": 1004.04, "md": 5.36, "rw": 5.69, "ws": 0.55, "st": 0.19, "sa": 434.44, "sn": 12.66, "rs": 38.92, "sc": 0.92, "sl": 1.9, "sg": 217.38, "sk": 86.26, "si": 46.44, "sb": 0.67, "za": 354.41, "es": 1374.78, "lk": 48.24, "kn": 0.56, "lc": 1, "vc": 0.58, "sd": 65.93, "sr": 3.3, "sz": 3.17, "se": 444.59, "ch": 522.44, "sy": 59.63, "tw": 426.98, "tj": 5.58, "tz": 22.43, "th": 312.61, "tl": 0.62, "tg": 3.07, "to": 0.3, "tt": 21.2, "tn": 43.86, "tr": 729.05, "tm": 0, "ug": 17.12, "ua": 136.56, "ae": 239.65, "gb": 2258.57, "us": 4624.18, "uy": 40.71, "uz": 37.72, "vu": 0.72, "ve": 285.21, "vn": 101.99, "ye": 30.02, "zm": 15.69, "zw": 0 }; // Convert to uppercase keys for jsVectorMap const gdpData = {}; for (const countryCode in rawGdpData) { gdpData[countryCode.toUpperCase()] = rawGdpData[countryCode]; } console.log("US GDP value:", gdpData["US"]); // Debug log // Initialize the map const map = new jsVectorMap({ map: "world_merc", selector: "#globalView", zoomButtons: true, zoomOnScroll: true, regionsSelectable: true, regionsSelectableOne: true, // Allow only one region to be selected at a time markersSelectable: true, backgroundColor: "transparent", responsive: true, // Enable responsive behavior regionStyle: { initial: { fill: '#9c99a130', // Default color stroke: window.colorMap.bootstrapVars.bodyColor.rgba(0.7), // Border color strokeWidth: 0.5, // Border width strokeOpacity: 0.8 // Border opacity }, hover: { fill: window.colorMap.success[400].hex, stroke: window.colorMap.bootstrapVars.bodyColor.rgba(0.8), // Darker border on hover strokeWidth: 1, // Slightly thicker border on hover cursor: 'pointer' }, selected: { fill: window.colorMap.success[300].hex, stroke: window.colorMap.bootstrapVars.bodyColor.hex, // Darkest border when selected strokeWidth: 1.5 // Thickest border when selected } }, visualizeData: { scale: [window.colorMap.primary[100].hex, window.colorMap.primary[700].hex], // Light blue to dark blue scale values: gdpData }, onRegionTooltipShow: function (event, tooltip, code) { const value = gdpData[code] || 0; let countryName = code; if (window.jsVectorMap && window.jsVectorMap.maps && window.jsVectorMap.maps.world_merc && window.jsVectorMap.maps.world_merc.regions && window.jsVectorMap.maps.world_merc.regions[code]) { countryName = window.jsVectorMap.maps.world_merc.regions[code].name; } tooltip.text( `${countryName} - $${value.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}` ); }, onRegionClick: function (event, code) { const value = gdpData[code] || 0; updateCountryInfoPanel(code, value); } }); // Store map in window for global access window.map = map; // Helper to update info panel function updateCountryInfoPanel(code, value) { // Get country name from map data if available let countryName = code; if (window.jsVectorMap && window.jsVectorMap.maps && window.jsVectorMap.maps.world_merc && window.jsVectorMap.maps.world_merc.regions && window.jsVectorMap.maps.world_merc.regions[code]) { countryName = window.jsVectorMap.maps.world_merc.regions[code].name; } // Update flag const flagImg = document.querySelector('.js-jqvmap-flag'); if (flagImg) { flagImg.src = `https://lipis.github.io/flag-icons/flags/4x3/${code.toLowerCase()}.svg`; } // Update country info text const infoText = document.querySelector('.js-jqvmap-country'); if (infoText) { infoText.textContent = `${countryName} - $${value.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`; } } // Set initial info panel to US with the correct value const usCode = "US"; updateCountryInfoPanel(usCode, gdpData[usCode]); // Force map update to apply visualization and handle initial sizing setTimeout(() => { map.updateSize(); }, 100); // Add window resize handler for responsiveness window.addEventListener('resize', function () { map.updateSize(); }); //update map colors const updateMapColors = function () { console.log('Rebuilding map with new theme colors'); // Try to find the map instance const originalMap = window.map; // Get map element const mapElement = document.querySelector('#globalView'); if (!mapElement) { console.warn('Map element #globalView not found'); return; } try { // Save important state before rebuilding let currentSelectedRegion = null; let currentZoom = 1; let currentOffset = { x: 0, y: 0 }; // Try to extract current state from original map if (originalMap) { // Save selected region if (originalMap.regions) { Object.entries(originalMap.regions).forEach(([code, region]) => { if (region && region.element && region.element.isSelected) { currentSelectedRegion = code; } }); } // Save zoom level and position if available if (originalMap.baseScale) { currentZoom = originalMap.scale / originalMap.baseScale; } if (originalMap.transX !== undefined && originalMap.transY !== undefined) { currentOffset.x = originalMap.transX; currentOffset.y = originalMap.transY; } // Destroy original map // First clear all event listeners to prevent memory leaks mapElement.innerHTML = ''; } // Create fresh map with new colors window.map = new jsVectorMap({ map: "world_merc", selector: "#globalView", zoomButtons: true, zoomOnScroll: true, regionsSelectable: true, regionsSelectableOne: true, markersSelectable: true, backgroundColor: "transparent", responsive: true, regionStyle: { initial: { fill: '#9c99a130', // Default color stroke: window.colorMap.bootstrapVars.bodyColor.rgba(0.7), // Border color strokeWidth: 0.5, strokeOpacity: 0.8 }, hover: { fill: window.colorMap.success[400].hex, // Updated from current theme stroke: window.colorMap.bootstrapVars.bodyColor.rgba(0.8), strokeWidth: 1, cursor: 'pointer' }, selected: { fill: window.colorMap.success[300].hex, // Updated from current theme stroke: window.colorMap.bootstrapVars.bodyColor.hex, strokeWidth: 1.5 } }, visualizeData: { scale: [window.colorMap.primary[100].hex, window.colorMap.primary[700].hex], // Updated scale colors values: gdpData }, onRegionTooltipShow: function (event, tooltip, code) { const value = gdpData[code] || 0; let countryName = code; if (window.jsVectorMap?.maps?.world_merc?.regions?.[code]) { countryName = window.jsVectorMap.maps.world_merc.regions[code].name; } tooltip.text( `${countryName} - $${value.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}` ); }, onRegionClick: function (event, code) { const value = gdpData[code] || 0; updateCountryInfoPanel(code, value); } }); // Restore previous state if available setTimeout(() => { const newMap = window.map; // Restore selected region if (currentSelectedRegion && newMap.setSelected) { newMap.setSelected('regions', currentSelectedRegion); } // Restore zoom and position if possible if (currentZoom !== 1 && newMap.setScale) { newMap.setScale(newMap.baseScale * currentZoom, currentOffset.x, currentOffset.y); } console.log('Map rebuilt successfully with new theme colors'); }, 100); } catch (e) { console.error('Error rebuilding map:', e); } }; // Add map color updates window.updateMiscPluginColors = function () { updateMapColors(); }; /***************************************************************/ /* Script for PeityCharts */ /***************************************************************/ // Global default settings for charts const defaults = { // Default background color for donut and pie charts (replaces the gray) donutBackground: 'var(--bs-border-color)', // Light purple background instead of gray pieBackground: 'var(--bs-border-color)' // Same for pie charts }; // Helper to create peity charts (similar to jQuery plugin style) function createPeity(selector, type, customOptions = {}) { document.querySelectorAll(selector).forEach(element => { // Parse data-peity attribute for options let options = { ...customOptions }; const dataAttr = element.getAttribute('data-peity'); if (dataAttr) { try { options = { ...options, ...JSON.parse(dataAttr) }; } catch (e) { console.warn('Invalid data-peity format for', selector); } } // Apply defaults for donut and pie charts background colors if (type === 'donut' || type === 'pie') { // If fill is not defined or is an empty array, set default colors if (!options.fill || options.fill.length === 0) { options.fill = type === 'donut' ? ['var(--primary-500)', defaults.donutBackground] : ['var(--primary-500)', defaults.pieBackground]; } // If fill is defined but doesn't include a background color (for fractions like "1/4") else if (Array.isArray(options.fill) && options.fill.length === 1) { options.fill.push(type === 'donut' ? defaults.donutBackground : defaults.pieBackground); } // If data contains multiple values (e.g., "10,4,4,6"), don't add background } PeityAPI.create(element, type, options); }); } try { // Simple chart initializations with default settings createPeity('.peity-pie', 'pie'); createPeity('.peity-donut', 'donut'); createPeity('.peity-line', 'line'); createPeity('.peity-bar', 'bar'); // Updating chart with animation const updatingChart = document.querySelector('.updating-chart'); if (updatingChart) { const values = updatingChart.textContent.split(',').map(Number); const chart = PeityAPI.create(updatingChart, 'line', { width: 200, height: 40, stroke: 'var(--info-500)', fill: 'var(--info-200)', min: 0, max: 10 }); setInterval(function () { values.shift(); values.push(Math.floor(Math.random() * 10)); updatingChart.textContent = values.join(','); chart.draw(); }, 500); } // Bar charts with negative values (red/green coloring) document.querySelectorAll('.bar-negative').forEach(element => { const values = element.textContent.split(',').map(Number); PeityAPI.create(element, 'bar', { height: 40, width: 110, fill: values.map(value => value > 0 ? 'var(--success-500)' : 'var(--danger-500)') }); }); // Bar charts with color transitions document.querySelectorAll('.bar-transition').forEach(element => { const values = element.textContent.split(',').map(Number); PeityAPI.create(element, 'bar', { height: 40, width: 110, fill: values.map((_, i, all) => { const g = parseInt((i / all.length) * 255); return `rgb(255, ${g}, ${g})`; }) }); }); // Process all remaining elements with data-peity attribute document.querySelectorAll('[data-peity]').forEach(element => { // Skip elements already handled by specific selectors if (element.classList.contains('peity-pie') || element.classList.contains('peity-donut') || element.classList.contains('peity-line') || element.classList.contains('peity-bar') || element.classList.contains('updating-chart') || element.classList.contains('bar-negative') || element.classList.contains('bar-transition')) { return; } // Auto-detect chart type based on content const content = element.textContent.trim(); let type; if (content.includes('/')) { type = 'donut'; // Fraction data is best for donut } else if (content.includes(',')) { // If it has commas, it's likely a series const hasNegative = content.split(',').some(val => parseFloat(val) < 0); type = hasNegative ? 'bar' : 'line'; // Bars handle negative values better visually } else { type = 'pie'; // Default fallback } // Get options from data attribute let options = {}; const dataAttr = element.getAttribute('data-peity'); if (dataAttr) { try { options = JSON.parse(dataAttr); } catch (e) { console.warn('Invalid data-peity format for element', element); } } // Apply global background color for donut/pie charts if ((type === 'donut' || type === 'pie') && content.includes('/')) { // For fraction notation (e.g. "1/4"), make sure we have a background color if (!options.fill || !Array.isArray(options.fill) || options.fill.length < 2) { options.fill = options.fill || []; // If we have one color already, keep it and add the background if (options.fill.length === 1) { options.fill.push(type === 'donut' ? defaults.donutBackground : defaults.pieBackground); } else { // No colors defined yet, set defaults options.fill = [ type === 'donut' ? 'var(--primary-500)' : 'var(--success-500)', type === 'donut' ? defaults.donutBackground : defaults.pieBackground ]; } } } // Create chart PeityAPI.create(element, type, options); }); } catch (error) { console.error('Error initializing Peity charts:', error); } /***************************************************************/ /* Script for EasyPieCharts */ /***************************************************************/ // Create and initialize easy pie charts // function initEasyPieCharts() { // document.querySelectorAll('.js-easy-pie-chart').forEach(function(element) { // // Skip if chart is already initialized // if (element._easyPieChart) return; // // Get element properties using vanilla JS // const computedStyle = window.getComputedStyle(element); // const barcolor = element.dataset.barcolor || computedStyle.color || 'var(--primary-700)'; // // Check if window.colorMap exists, if not use a fallback // let trackcolor; // try { // if (window.colorMap && window.colorMap.bootstrapVars && window.colorMap.bootstrapVars.bodyColorRgb) { // trackcolor = window.colorMap.bootstrapVars.bodyColorRgb.rgba(0.07); // } else { // trackcolor = 'rgba(0,0,0,0.04)'; // } // } catch (e) { // trackcolor = 'rgba(0,0,0,0.04)'; // } // // Read dataset attributes with fallbacks // const size = parseInt(element.dataset.piesize) || 50; // const scalecolor = element.dataset.scalecolor || computedStyle.color || 'var(--primary-700)'; // const scalelength = parseInt(element.dataset.scalelength) || 0; // const linewidth = parseInt(element.dataset.linewidth) || parseInt(size / 8.5); // const linecap = element.dataset.linecap || 'butt'; // butt, round and square. // // Create EasyPieChart instance // const chart = new EasyPieChart(element, { // size: size, // barColor: barcolor, // trackColor: trackcolor, // scaleColor: scalecolor, // scaleLength: scalelength, // lineCap: linecap, // lineWidth: linewidth, // animate: { // duration: 1500, // enabled: true // }, // easing: 'easeOutQuad', // Use our built-in easing function // onStep: function(from, to, percent) { // // Find the percentage element and update its text // const percentElement = element.querySelector('.js-percent'); // if (percentElement) { // percentElement.textContent = Math.round(percent); // } // } // }); // // Store the original percent value for rebuilds // if (element.dataset.percent) { // element._originalPercent = parseFloat(element.dataset.percent); // } else if (element.getAttribute('data-percent')) { // element._originalPercent = parseFloat(element.getAttribute('data-percent')); // } // }); // } // // Destroy all existing charts // function destroyEasyPieCharts() { // document.querySelectorAll('.js-easy-pie-chart').forEach(function(element) { // if (element._easyPieChart) { // element._easyPieChart.destroy(); // } // }); // } // // Rebuild all charts with current theme colors // window.rebuildEasyPieCharts = function() { // destroyEasyPieCharts(); // initEasyPieCharts(); // }; // // Initialize easy pie charts // initEasyPieCharts(); });