madebydanny.uk written in html, css, and a lot of JavaScript I don't understand
madeydanny.uk
html
css
javascript
1(function () {
2 const clients = [
3 { name: "Bluesky", base: "https://bsky.app/profile/", icon: "https://imrs.madebydanny.uk?url=https://cloudflareisawesome.madebydanny.uk/madebydanny.uk/followonbsky/bsky.webp", cls: "bsky" },
4 { name: "Blacksky", base: "https://blacksky.community/profile/", icon: "https://imrs.madebydanny.uk?url=https://cloudflareisawesome.madebydanny.uk/madebydanny.uk/followonbsky/blacksky.webp", cls: "blacksky" },
5 { name: "Anisota", base: "https://anisota.net/profile/", icon: "https://imrs.madebydanny.uk?url=https://cloudflareisawesome.madebydanny.uk/madebydanny.uk/followonbsky/anissota.webp", cls: "anisota" },
6 { name: "Deer Social", base: "https://deer.social/profile/", icon: "https://imrs.madebydanny.uk?url=https://cloudflareisawesome.madebydanny.uk/madebydanny.uk/followonbsky/deer.webp", cls: "deer" },
7 { name: "Catsky", base: "https://catsky.social/profile/", icon: "https://imrs.madebydanny.uk?url=https://cloudflareisawesome.madebydanny.uk/madebydanny.uk/followonbsky/catsky.webp", cls: "catsky" },
8 { name: "Witchsky", base: "https://witchsky.app/profile/", icon: "https://cloudflareisawesome.madebydanny.uk/madebydanny.uk/followonbsky/bitchsky.svg", cls: "bitch" },
9 { name: "kibun", base: "https://www.kibun.social/users/", icon: "https://cloudflareisawesome.madebydanny.uk/madebydanny.uk/followonbsky/kibun.ico", cls: "bsky" },
10 { name: "plyr.fm", base: "https://plyr.fm/u/", icon: "https://cloudflareisawesome.madebydanny.uk/madebydanny.uk/followonbsky/plyr.ico", cls: "bsky" },
11 { name: "Nooki", base: "https://nooki.me/user/", icon: "https://imrs.madebydanny.uk?url=https://cloudflareisawesome.madebydanny.uk/madebydanny.uk/followonbsky/nooki.webp", cls: "nooki" },
12 { name: "Tangled", base: "https://tangled.org/", icon: "https://imrs.madebydanny.uk?url=https://cloudflareisawesome.madebydanny.uk/madebydanny.uk/followonbsky/tangled.webp", cls: "tangled" },
13 { name: "Stream Place", base: "https://stream.place/", icon: "https://cloudflareisawesome.madebydanny.uk/madebydanny.uk/followonbsky/splace.ico", cls: "splace" },
14 { name: "PDSls", base: "https://pdsls.dev/at://", icon: "https://imrs.madebydanny.uk?url=https://cloudflareisawesome.madebydanny.uk/madebydanny.uk/followonbsky/pdsls.webp", cls: "bsky" },
15 { name: "Red Dwarf", base: "https://reddwarf.app/profile/", icon: "https://cloudflareisawesome.madebydanny.uk/madebydanny.uk/followonbsky/redd.ico", cls: "redd" },
16 { name: "Deer (aylac fork)", base: "https://deer.aylac.top/profile/", icon: "https://imrs.madebydanny.uk?url=https://cloudflareisawesome.madebydanny.uk/madebydanny.uk/followonbsky/aylac.webp", cls: "deer" },
17 { name: "Semble", base: "https://semble.so/user/", icon: "https://cloudflareisawesome.madebydanny.uk/madebydanny.uk/followonbsky/semble.svg", cls: "semble" },
18 { name: "Sky Space", base: "https://skyspace.me/?", icon: "https://imrs.madebydanny.uk?url=https://cloudflareisawesome.madebydanny.uk/madebydanny.uk/followonbsky/skyspace.jpg", cls: "skyspace" },
19
20 ];
21
22 const avatarEl = document.getElementById('avatar');
23 const nameEl = document.getElementById('displayName');
24 const tagEl = document.getElementById('tagline');
25 const linksRoot = document.getElementById('links');
26 const moreClientsRoot = document.getElementById('more-clients');
27 const showMoreBtn = document.getElementById('show-more');
28 const tryInput = document.getElementById('try-handle');
29 const tryBtn = document.getElementById('try-button');
30 const customInput = document.getElementById('custom-client');
31 const customBtn = document.getElementById('custom-open');
32
33 function setText(el, txt){ el.textContent = txt ?? ''; }
34
35 function makeClientLink(c, handle){
36 const a = document.createElement('a');
37 a.className = 'link ' + (c.cls || '');
38 a.href = c.base + encodeURIComponent(handle);
39 a.target = '_blank';
40 a.rel = 'noopener';
41 a.setAttribute('aria-label', `Open ${c.name} for ${handle}`);
42
43 const img = document.createElement('img');
44 img.src = c.icon;
45 img.alt = c.name + ' icon';
46 img.loading = 'lazy';
47
48 const span = document.createElement('span');
49 span.className = 'label';
50 span.textContent = c.name;
51
52 a.append(img, span);
53 return a;
54 }
55
56 function openCustom(handle){
57 let url = (customInput.value || '').trim();
58 if(!url)return;
59 if(!/^https?:\/\//i.test(url))url='https://'+url;
60 url=url.replace(/\/+$/,'')+'/profile/'+encodeURIComponent(handle);
61 window.open(url,'_blank','noopener');
62 }
63
64 function tryProfile() {
65 let value = (tryInput.value || '').trim();
66 if (!value) return;
67
68 if (value.startsWith('@')) value = value.slice(1);
69
70 try {
71 const parsed = new URL(value);
72 const parts = parsed.pathname.split('/').filter(Boolean);
73 if (parts.length && (parts[0] === 'profile' || parts[0] === 'actor')) {
74 value = parts[1];
75 }
76 } catch (_) { }
77
78 const u = new URL(location.href);
79 u.searchParams.set('did', value);
80 location.href = u.toString();
81 }
82
83 async function loadProfile(){
84 const params=new URLSearchParams(location.search);
85 const did=params.get('did');
86 if(!did){
87 setText(nameEl,'Missing ?did=');
88 setText(tagEl,'Add a DID or handle to view a profile.');
89 tryBtn.onclick=tryProfile;
90 tryInput.onkeydown=e=>{if(e.key==='Enter')tryProfile()};
91 return;
92 }
93 try{
94 const res=await fetch(`https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile?actor=${encodeURIComponent(did)}`);
95 if(!res.ok)throw new Error('fetch failed');
96 const data=await res.json();
97 const handle=data.handle||did;
98 const avatar=data.avatar?`https://imrs.madebydanny.uk?url=${encodeURIComponent(data.avatar)}`:'https://imrs.madebydanny.uk/?url=https://public-cdn.madebydanny.uk/user-content/2025-10-11/1760220038748_cloudflare-brands-solid-full.svg';
99
100 avatarEl.src=avatar;
101 avatarEl.alt=`${data.displayName||handle} avatar`;
102 setText(nameEl,data.displayName||handle);
103 setText(tagEl,`@${handle} is on the AT Protocol – pick a client to follow them!`);
104
105 linksRoot.innerHTML='';
106 moreClientsRoot.innerHTML='';
107
108 const topClients = clients.slice(0, 6);
109 const moreClients = clients.slice(6);
110
111 topClients.forEach(c=>linksRoot.appendChild(makeClientLink(c,handle)));
112 moreClients.forEach(c=>moreClientsRoot.appendChild(makeClientLink(c,handle)));
113
114 if(moreClients.length > 0){
115 showMoreBtn.style.display = 'block';
116 showMoreBtn.onclick = function(){
117 moreClientsRoot.classList.toggle('show');
118 this.textContent = moreClientsRoot.classList.contains('show') ? 'Show Less' : 'Show More Clients';
119 };
120 }
121
122 customBtn.onclick=()=>openCustom(handle);
123 customInput.onkeydown=e=>{if(e.key==='Enter')openCustom(handle)};
124 tryBtn.onclick=tryProfile;
125 tryInput.onkeydown=e=>{if(e.key==='Enter')tryProfile()};
126 }catch(e){
127 nameEl.textContent='Error loading profile';
128 tagEl.textContent='Check your DID or try again.';
129 linksRoot.innerHTML='';
130 moreClientsRoot.innerHTML='';
131 showMoreBtn.style.display = 'none';
132 tryBtn.onclick=tryProfile;
133 tryInput.onkeydown=e=>{if(e.key==='Enter')tryProfile()};
134 }
135 }
136
137 document.addEventListener('DOMContentLoaded',loadProfile);
138})();