1document.addEventListener('DOMContentLoaded', function () { 2 console.log('Contact details loaded...'); 3 4 const linkIcon = '<i class="fa-solid fa-arrow-up-right-from-square"></i>'; 5 const copyIcon = '<i class="fa-solid fa-copy"></i>'; 6 7 const contactDetails = { 8 Email: { address: "[email protected]", icon: '<i class="fa-solid fa-envelope"></i>', hoverIcon: copyIcon }, 9 deSypher: { link: "https://desypher.net/", icon: '<i class="fa-solid fa-globe"></i>', hoverIcon: linkIcon }, 10 Github: { link: "https://github.com/tukyo", icon: '<i class="fa-brands fa-github"></i>', hoverIcon: linkIcon }, 11 TwitterX: { link: "https://twitter.com/tukyowave", icon: '<i class="fa-brands fa-twitter"></i>', hoverIcon: linkIcon }, 12 Telegram: { link: "https://t.me/tukyohub", icon: '<i class="fa-brands fa-telegram"></i>', hoverIcon: linkIcon }, 13 Instagram: { link: "https://instagram.com/tukyowave", icon: '<i class="fa-brands fa-instagram"></i>', hoverIcon: linkIcon }, 14 Youtube: { link: "https://www.youtube.com/tukyo", icon: '<i class="fa-brands fa-youtube"></i>', hoverIcon: linkIcon }, 15 Linkedin: { link: "https://linkedin.com/in/tukyo/", icon: '<i class="fa-brands fa-linkedin"></i>', hoverIcon: linkIcon }, 16 // Medium: { link: "https://tukyo.medium.com/", icon: '<i class="fa-brands fa-medium"></i>', hoverIcon: linkIcon }, 17 }; 18 19 const contactColors = [ 20 { light: "rgba(255,123,123,1)", dark: "rgba(103,25,19,1)" }, 21 { light: "rgba(255,214,123,1)", dark: "rgba(103,78,26,1)" }, 22 { light: "rgba(123,255,171,1)", dark: "rgba(17,123,23,1)" }, 23 { light: "rgba(123,171,255,1)", dark: "rgba(24,40,120,1)" }, 24 { light: "rgba(255,123,255,1)", dark: "rgba(146,29,145,1)" } 25 ]; 26 27 const contactContent = document.querySelector('.contact-content'); 28 29 const hoverRadius = 100; 30 const movementIntensity = 10; 31 const easingSpeed = 0.1; 32 33 if (contactContent) { 34 const bubbles = []; 35 36 Object.keys(contactDetails).forEach(key => { 37 const contactItem = document.createElement('div'); 38 contactItem.className = 'contact-item'; 39 40 const chosen = contactColors[Math.floor(Math.random() * contactColors.length)]; 41 contactItem.style.background = `radial-gradient(circle at 30% 30%, ${chosen.light}, ${chosen.dark})`; 42 43 const linkElement = document.createElement('div'); 44 linkElement.innerHTML = contactDetails[key].icon; 45 linkElement.className = 'contact-link'; 46 47 contactItem.appendChild(linkElement); 48 49 if (key === 'Email') { 50 contactItem.title = "Click to copy email"; 51 contactItem.addEventListener('click', () => { 52 navigator.clipboard.writeText(contactDetails[key].address).then(() => { 53 alert('Email copied to clipboard!'); 54 }); 55 }); 56 } else { 57 contactItem.title = `Visit ${key}`; 58 contactItem.addEventListener('click', () => { 59 window.open(contactDetails[key].link, '_blank'); 60 }); 61 } 62 63 contactItem.addEventListener('mouseenter', () => { 64 linkElement.innerHTML = contactDetails[key].hoverIcon; 65 }); 66 67 contactItem.addEventListener('mouseleave', () => { 68 linkElement.innerHTML = contactDetails[key].icon; 69 }); 70 71 contactItem.dataset.targetX = 0; 72 contactItem.dataset.targetY = 0; 73 bubbles.push(contactItem); 74 contactContent.appendChild(contactItem); 75 }); 76 77 78 document.addEventListener('mousemove', (e) => { 79 const mouseX = e.clientX; 80 const mouseY = e.clientY; 81 82 bubbles.forEach(bubble => { 83 const rect = bubble.getBoundingClientRect(); 84 const bubbleX = rect.left + rect.width / 2; 85 const bubbleY = rect.top + rect.height / 2; 86 87 const distX = mouseX - bubbleX; 88 const distY = mouseY - bubbleY; 89 const distance = Math.sqrt(distX * distX + distY * distY); 90 91 if (distance < hoverRadius) { 92 const movementFactor = (hoverRadius - distance) / hoverRadius; 93 bubble.dataset.targetX = movementIntensity * movementFactor * (distX / distance); 94 bubble.dataset.targetY = movementIntensity * movementFactor * (distY / distance); 95 } else { 96 bubble.dataset.targetX = 0; 97 bubble.dataset.targetY = 0; 98 } 99 }); 100 }); 101 setInterval(() => {
102 bubbles.forEach(bubble => { 103 const currentTransform = getComputedStyle(bubble).transform; 104 const matrix = new DOMMatrix(currentTransform === 'none' ? '' : currentTransform); 105 const currentX = matrix.m41; 106 const currentY = matrix.m42; 107 108 const targetX = parseFloat(bubble.dataset.targetX); 109 const targetY = parseFloat(bubble.dataset.targetY); 110 111 const easedX = currentX + (targetX - currentX) * easingSpeed; 112 const easedY = currentY + (targetY - currentY) * easingSpeed; 113 114 bubble.style.transform = `translate(${easedX}px, ${easedY}px)`; 115 }); 116 }, 16); // ~60 FPS 117 } 118});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.