Skip to content

Commit 72cb991

Browse files
committed
Update index.html
1 parent 6aed8dc commit 72cb991

File tree

1 file changed

+146
-11
lines changed

1 file changed

+146
-11
lines changed

ng_sandbox/index.html

Lines changed: 146 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,21 +39,156 @@ <h1 id="title">Loading...</h1>
3939
</div>
4040

4141
<script>
42+
fetch('https:/<!DOCTYPE html>
43+
<html lang="en">
44+
<head>
45+
<meta charset="UTF-8" />
46+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
47+
<title>Smart Collection Browser</title>
48+
<style>
49+
body {
50+
margin: 0;
51+
min-height: 100vh;
52+
display: flex;
53+
justify-content: center;
54+
align-items: center;
55+
font-family: Arial, Helvetica, sans-serif;
56+
background-color: #f5f5f5;
57+
}
58+
59+
.layout {
60+
display: flex;
61+
gap: 24px;
62+
align-items: center;
63+
}
64+
65+
.main {
66+
text-align: center;
67+
transition: opacity 0.4s ease, transform 0.4s ease;
68+
}
69+
70+
.main.fade {
71+
opacity: 0;
72+
transform: scale(0.95);
73+
}
74+
75+
h1 {
76+
margin-bottom: 20px;
77+
}
78+
79+
.main img {
80+
max-width: 60vw;
81+
max-height: 70vh;
82+
object-fit: contain;
83+
border-radius: 8px;
84+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
85+
}
86+
87+
.similar {
88+
display: flex;
89+
flex-direction: column;
90+
gap: 12px;
91+
}
92+
93+
.similar img {
94+
width: 120px;
95+
height: 120px;
96+
object-fit: cover;
97+
cursor: pointer;
98+
border-radius: 6px;
99+
box-shadow: 0 2px 6px rgba(0,0,0,0.2);
100+
transition: transform 0.2s ease;
101+
}
102+
103+
.similar img:hover {
104+
transform: scale(1.05);
105+
}
106+
</style>
107+
</head>
108+
<body>
109+
<div class="layout">
110+
<div class="main" id="main">
111+
<h1 id="title">Loading...</h1>
112+
<img id="image" />
113+
</div>
114+
<div class="similar" id="similar"></div>
115+
</div>
116+
117+
<script>
118+
let collection = [];
119+
let currentIndex = 0;
120+
121+
// --- Simple ML: TF-IDF cosine similarity on titles ---
122+
function tokenize(text) {
123+
return text.toLowerCase().split(/\W+/).filter(Boolean);
124+
}
125+
126+
function buildTf(tokens) {
127+
const tf = {};
128+
tokens.forEach(t => tf[t] = (tf[t] || 0) + 1);
129+
return tf;
130+
}
131+
132+
function cosine(a, b) {
133+
let dot = 0, magA = 0, magB = 0;
134+
for (const k in a) {
135+
if (b[k]) dot += a[k] * b[k];
136+
magA += a[k] * a[k];
137+
}
138+
for (const k in b) magB += b[k] * b[k];
139+
return dot / (Math.sqrt(magA) * Math.sqrt(magB) || 1);
140+
}
141+
142+
function findSimilar(index, count = 4) {
143+
const baseTf = collection[index].tf;
144+
return collection
145+
.map((item, i) => ({
146+
index: i,
147+
score: i === index ? -1 : cosine(baseTf, item.tf)
148+
}))
149+
.sort((a, b) => b.score - a.score)
150+
.slice(0, count);
151+
}
152+
153+
function render(index) {
154+
const main = document.getElementById('main');
155+
main.classList.add('fade');
156+
157+
setTimeout(() => {
158+
currentIndex = index;
159+
const item = collection[index];
160+
document.getElementById('title').textContent = item.Title;
161+
document.getElementById('image').src = item['Image URL'];
162+
163+
const similarDiv = document.getElementById('similar');
164+
similarDiv.innerHTML = '';
165+
166+
findSimilar(index).forEach(sim => {
167+
const img = document.createElement('img');
168+
img.src = collection[sim.index]['Image URL'];
169+
img.title = collection[sim.index].Title;
170+
img.onclick = () => render(sim.index);
171+
similarDiv.appendChild(img);
172+
});
173+
174+
main.classList.remove('fade');
175+
}, 300);
176+
}
177+
42178
fetch('https://johnstack.github.io/JavaScript-Sandpit/ng_sandbox/collection.json')
43-
.then(response => response.json())
179+
.then(r => r.json())
44180
.then(data => {
45-
if (!Array.isArray(data) || data.length === 0) {
46-
throw new Error('collection.json is empty or not an array');
47-
}
48-
49-
const randomEntry = data[Math.floor(Math.random() * data.length)];
181+
collection = data.map(item => ({
182+
...item,
183+
tf: buildTf(tokenize(item.Title || ''))
184+
}));
50185

51-
document.getElementById('title').textContent = randomEntry['Title'] || 'Untitled';
52-
document.getElementById('image').src = randomEntry['Image URL'] || '';
186+
const start = Math.floor(Math.random() * collection.length);
187+
render(start);
53188
})
54-
.catch(error => {
55-
document.getElementById('title').textContent = 'Error loading collection';
56-
console.error(error);
189+
.catch(err => {
190+
document.getElementById('title').textContent = 'Failed to load collection';
191+
console.error(err);
57192
});
58193
</script>
59194
</body>

0 commit comments

Comments
 (0)