319 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			319 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
| <!doctype html>
 | |
| 
 | |
| <html lang='en'>
 | |
| 
 | |
| <head>
 | |
|     <meta charset='utf-8'>
 | |
|     <meta content='width=device-width, initial-scale=1, minimum-scale=1' name='viewport'>
 | |
| 
 | |
|     <title>vance.land</title>
 | |
|     <meta content='vance.land' name='description'>
 | |
|     <meta content='vance' name='author'>
 | |
|     <base target='_blank'>
 | |
| 
 | |
|     <style>
 | |
|         @font-face {
 | |
|             font-family: 'VT323';
 | |
|             src: url('VT323.ttf');
 | |
|         }
 | |
| 
 | |
|         * {
 | |
|             color: #20C20E;
 | |
|             font-family: 'VT323', monospace;
 | |
|             font-size: 16pt;
 | |
|             cursor: none;
 | |
|         }
 | |
| 
 | |
|         html {
 | |
|             width: 100%;
 | |
|             height: 100%;
 | |
|         }
 | |
| 
 | |
|         body {
 | |
|             background: #000000;
 | |
|             overflow: hidden;
 | |
|         }
 | |
| 
 | |
|         a {
 | |
|             text-decoration: none;
 | |
|             white-space: nowrap;
 | |
|             overflow: hidden;
 | |
|         }
 | |
| 
 | |
|         p {
 | |
|             padding: 0;
 | |
|             margin: 0;
 | |
|             font-size: 1rem;
 | |
|             white-space: normal;
 | |
|             width: 420px;
 | |
|         }
 | |
| 
 | |
|         span {
 | |
|             font-size: 1rem;
 | |
|         }
 | |
| 
 | |
|         #banana {
 | |
|             position: absolute;
 | |
|             z-index: 2;
 | |
|             pointer-events: none;
 | |
|         }
 | |
| 
 | |
|         #bonzi {
 | |
|             position: absolute;
 | |
|             transition: transform 0.8s;
 | |
|             transform-style: preserve-3d;
 | |
|             z-index: 1;
 | |
|             pointer-events: none;
 | |
|         }
 | |
| 
 | |
|         #live {
 | |
|             position: fixed;
 | |
|             top: 50%;
 | |
|             left: 50%;
 | |
|             transform: translate(-50%, -50%);
 | |
|             z-index: 0;
 | |
|         }
 | |
|     </style>
 | |
| </head>
 | |
| 
 | |
| <body>
 | |
| <img alt='banana' id='banana' src='banana.png'/>
 | |
| <img alt='bonzi' id='bonzi' src='bonzi.png'/>
 | |
| <video autoplay controls height='360' id='live' playsinline width='640'>
 | |
| </video>
 | |
| <script src="https://cdn.jsdelivr.net/npm/hls.js/dist/hls.min.js"></script>
 | |
| <script>
 | |
|     const email = (function () {
 | |
|         var p = Array.prototype.slice.call(arguments),
 | |
|             V = p.shift();
 | |
|         return p.reverse().map(function (o, i) {
 | |
|             return String.fromCharCode(o - V - 36 - i)
 | |
|         }).join('')
 | |
|     })(59, 164, 200, 197, 207, 193, 213) + (40559).toString(36).toLowerCase() + (function () {
 | |
|         var Q = Array.prototype.slice.call(arguments),
 | |
|             O = Q.shift();
 | |
|         return Q.reverse().map(function (e, u) {
 | |
|             return String.fromCharCode(e - O - 57 - u)
 | |
|         }).join('')
 | |
|     })(20, 125, 179, 176) + (766).toString(36).toLowerCase() + (function () {
 | |
|         var E = Array.prototype.slice.call(arguments),
 | |
|             f = E.shift();
 | |
|         return E.reverse().map(function (s, C) {
 | |
|             return String.fromCharCode(s - f - 33 - C)
 | |
|         }).join('')
 | |
|     })(24, 158, 167);
 | |
| 
 | |
|     const socials = {
 | |
|         'matrix': {
 | |
|             'text': '💊 @vance:vance.land',
 | |
|             'link': 'https://matrix.to/#/@vance:vance.land'
 | |
|         },
 | |
|         'feed': {
 | |
|             'text': `🥫 @${email}`,
 | |
|             'link': 'https://feed.vance.land/@vance'
 | |
|         },
 | |
|         'live': {
 | |
|             'text': '🎥 live.vance.land',
 | |
|             'link': 'https://live.vance.land/vance/'
 | |
|         },
 | |
|         'email': {
 | |
|             'text': `📧 ${email}`,
 | |
|             'link': `mailto:${email}`
 | |
|         },
 | |
|         'urbit': {
 | |
|             'text': '🌐 ~sarsup-figput',
 | |
|             'link': 'https://sarsup-figput.vance.land/blog'
 | |
|         },
 | |
|         'telegram': {
 | |
|             'text': '💬 @vanceland',
 | |
|             'link': 'https://t.me/vanceland'
 | |
|         },
 | |
|         'code': {
 | |
|             'text': '🐱💻 code.vance.land',
 | |
|             'link': 'https://code.vance.land/vance'
 | |
|         },
 | |
|         'music': {
 | |
|             'text': '🎶 @SMOOTHAPPLIANCE',
 | |
|             'link': 'https://soundcloud.com/smoothappliance'
 | |
|         },
 | |
|         'haram': {
 | |
|             'text': '🕋',
 | |
|             'link': 'https://dreamsinco.de'
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     // bonzi
 | |
|     const banana = document.getElementById('banana');
 | |
|     const bonzi = document.getElementById('bonzi');
 | |
| 
 | |
|     let mouseTop = 0;
 | |
|     let mouseLeft = 0;
 | |
|     let bonziTop = 0;
 | |
|     let bonziLeft = 0;
 | |
|     const speed = 0.02;
 | |
| 
 | |
|     (function moveBonzi() {
 | |
|         bonzi.style.transform = bonziLeft > mouseLeft ? 'translate(5%, -35%) scaleX(-1)' : 'translate(-80%, -35%)';
 | |
|         bonziTop = bonziTop + ((mouseTop - bonziTop) * speed);
 | |
|         bonziLeft = bonziLeft + ((mouseLeft - bonziLeft) * speed);
 | |
|         bonzi.style.top = `${bonziTop}px`;
 | |
|         bonzi.style.left = `${bonziLeft}px`;
 | |
|         requestAnimationFrame(moveBonzi);
 | |
|     })();
 | |
| 
 | |
|     ['mousemove', 'touchstart', 'touchmove'].forEach(type => {
 | |
|         window.addEventListener(type, event => {
 | |
|             mouseTop = event.clientY ?? event.touches[0]?.clientY;
 | |
|             mouseLeft = event.clientX ?? event.touches[0]?.clientX;
 | |
|             banana.style.top = `${mouseTop}px`;
 | |
|             banana.style.left = `${mouseLeft}px`;
 | |
|         });
 | |
|     });
 | |
| 
 | |
|     //live
 | |
|     const live = document.getElementById('live');
 | |
|     const liveSrc = new URL('index.m3u8', socials.live.link).toString();
 | |
| 
 | |
|     const vw = Math.max(document.documentElement.clientWidth ?? 0, window.innerWidth ?? 0);
 | |
|     const vh = Math.max(document.documentElement.clientHeight ?? 0, window.innerHeight ?? 0);
 | |
| 
 | |
|     (function loadStream() {
 | |
|         // remove stream if embedded
 | |
|         if (window.self !== window.top) {
 | |
|             live.remove();
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         // resize video if view width is smaller than frame
 | |
|         if (live.width > vw) {
 | |
|             live.width = vw;
 | |
|             live.height = vw * (9 / 16);
 | |
|         }
 | |
| 
 | |
|         if (Hls.isSupported()) {
 | |
|             const hls = new Hls({
 | |
|                 maxLiveSyncPlaybackRate: 1.5,
 | |
|             });
 | |
| 
 | |
|             hls.on(Hls.Events.ERROR, (event, data) => {
 | |
|                 if (data.fatal) {
 | |
|                     if(data?.response.code === 404)
 | |
|                         live.style.display = 'none';
 | |
|                     hls.destroy();
 | |
|                 }
 | |
|                 setTimeout(loadStream, 5000);
 | |
|             });
 | |
|             hls.on(Hls.Events.MEDIA_ATTACHED, () => {
 | |
|                 hls.loadSource(liveSrc);
 | |
|             });
 | |
|             hls.on(Hls.Events.MANIFEST_PARSED, () => {
 | |
|                 live.style.removeProperty('display');
 | |
|                 live.play();
 | |
|             });
 | |
| 
 | |
|             hls.attachMedia(live);
 | |
|         } else if (live.canPlayType('application/vnd.apple.mpegurl')) {
 | |
|             fetch(liveSrc)
 | |
|                 .then(() => {
 | |
|                     live.src = liveSrc;
 | |
|                     live.play();
 | |
|                 });
 | |
|         }
 | |
|     })();
 | |
| 
 | |
|     // animated anchors
 | |
|     const minSpeed = 10;
 | |
|     const maxSpeed = 20;
 | |
|     let anchors = 0;
 | |
|     const maxAnchors = Math.floor(vw / 30);
 | |
| 
 | |
|     function animateAnchor(anchor) {
 | |
|         let flag = parseInt(anchor.style.left, 10);
 | |
| 
 | |
|         setInterval(() => {
 | |
|             anchor.style.left = `${--flag}px`;
 | |
| 
 | |
|             if (anchor.offsetWidth <= -flag) {
 | |
|                 flag = vw + Math.floor(anchor.offsetWidth / 2);
 | |
|                 let posy = Math.floor(Math.random() * vh) - Math.floor(anchor.offsetHeight / 2);
 | |
|                 anchor.style.top = `${posy}px`;
 | |
|             }
 | |
|         }, Math.floor(Math.random() * (maxSpeed - minSpeed) + minSpeed));
 | |
|     }
 | |
| 
 | |
|     function createAnchor(link, content) {
 | |
|         if (anchors >= maxAnchors) {
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         const anchor = document.createElement('a');
 | |
|         anchor.href = link;
 | |
|         anchor.target = '_blank';
 | |
|         anchor.innerHTML = content;
 | |
| 
 | |
|         const posTop = Math.floor(Math.random() * vh) - Math.floor(anchor.offsetHeight / 2);
 | |
|         const posLeft = Math.floor(Math.random() * vw) - Math.floor(anchor.offsetWidth / 2);
 | |
|         let zIndex = Math.floor(anchors - maxAnchors / 2);
 | |
|         zIndex = (zIndex >= 0) ? zIndex + 1 : zIndex;
 | |
| 
 | |
|         anchor.style.position = 'absolute';
 | |
|         anchor.style.top = `${posTop}px`;
 | |
|         anchor.style.left = `${posLeft}px`;
 | |
|         anchor.style.zIndex = `${zIndex}`;
 | |
| 
 | |
|         document.body.prepend(anchor);
 | |
|         ++anchors;
 | |
|         animateAnchor(anchor);
 | |
|     }
 | |
| 
 | |
|     // Always have socials
 | |
|     while (anchors < maxAnchors / 4) {
 | |
|         for (const key in socials) {
 | |
|             createAnchor(socials[key].link, socials[key].text);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // feed
 | |
|     fetch(`${socials.feed.link}.rss`)
 | |
|         .then(response => response.text())
 | |
|         .then(str => new DOMParser().parseFromString(str, 'text/xml'))
 | |
|         .then(data => {
 | |
|             const items = data.querySelectorAll('item');
 | |
|             items.forEach(element => {
 | |
|                 const text = document.createElement('p');
 | |
|                 text.innerHTML = (element.getElementsByTagName('content:encoded')[0] ?? element.getElementsByTagName('title')[0]).textContent;
 | |
|                 createAnchor(element.querySelector('link').innerHTML, text.outerHTML);
 | |
|             });
 | |
|         });
 | |
| 
 | |
|     // code + helpers
 | |
|     function absoluteURL(data, query, url) {
 | |
|         data.querySelectorAll(query).forEach(element => {
 | |
|             element.setAttribute('href', new URL(element.getAttribute('href'), url));
 | |
|             element.removeAttribute('rel');
 | |
|         });
 | |
|         return data;
 | |
|     }
 | |
| 
 | |
|     function htmlDecode(input) {
 | |
|         let doc = new DOMParser().parseFromString(input, 'text/html');
 | |
|         doc = new DOMParser().parseFromString(doc.documentElement.textContent, 'text/html');
 | |
|         return doc;
 | |
|     }
 | |
| 
 | |
|     fetch(`${socials.code.link}.atom`)
 | |
|         .then(response => response.text())
 | |
|         .then(str => new DOMParser().parseFromString(str, 'text/xml'))
 | |
|         .then(data => {
 | |
|             data = absoluteURL(data, 'link', socials.code.link);
 | |
|             const items = data.querySelectorAll('entry');
 | |
|             items.forEach(element => {
 | |
|                 const title = absoluteURL(htmlDecode(element.querySelector('title').innerHTML), 'a', socials.code.link);
 | |
|                 createAnchor(element.querySelector('link').getAttribute('href'), title.body.innerHTML);
 | |
|             });
 | |
|         });
 | |
| </script>
 | |
| </body>
 | |
| 
 | |
| </html>
 |