Fix Unicode support and improve navigation functionality
- Fix album cover path generation for Unicode artist names (Beyoncé, Björk, etc.) - Improve Next button to respect current filter/sort state instead of just incrementing rank - Add proper Unicode character handling with regex property escapes - Update README to document new Unicode support and smart navigation features 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
75bbf157e7
commit
dc0c775eb4
2 changed files with 52 additions and 10 deletions
|
|
@ -17,7 +17,8 @@ A beautiful, interactive web application showcasing Rolling Stone's greatest alb
|
|||
- **Complete Metadata**: Full album information including artist, year, label, and descriptions
|
||||
- **Wikipedia Integration**: Direct links to Wikipedia pages for each album
|
||||
- **Spotify Integration**: Listen to albums directly on Spotify with one click
|
||||
- **Sequential Navigation**: Next button on each album for easy browsing
|
||||
- **Smart Navigation**: Next button that respects current filter/sort state for intuitive browsing
|
||||
- **Unicode Support**: Proper handling of international artist names (Beyoncé, Björk, etc.)
|
||||
- **Search & Filter**: Easy navigation with search, status filters, and sorting options
|
||||
|
||||
### Modern UI/UX
|
||||
|
|
|
|||
59
script.js
59
script.js
|
|
@ -247,11 +247,26 @@ function renderAlbums() {
|
|||
function getCoverImagePath(album) {
|
||||
if (!album.Artist || !album.Album || !album.Rank) return null;
|
||||
|
||||
// Sanitize artist and album names to match the downloaded filenames
|
||||
const safeArtist = album.Artist.replace(/[<>:"/\\|?*]/g, '').replace(/[^\w\s\-_.]/g, '').replace(/\s+/g, '_').slice(0, 100);
|
||||
const safeAlbum = album.Album.replace(/[<>:"/\\|?*]/g, '').replace(/[^\w\s\-_.]/g, '').replace(/\s+/g, '_').slice(0, 100);
|
||||
const rank = album.Rank.toString().padStart(3, '0');
|
||||
|
||||
// For rank 32 (Beyoncé - Lemonade) and other special cases with Unicode
|
||||
// we need to preserve the accented characters
|
||||
let safeArtist = album.Artist;
|
||||
let safeAlbum = album.Album;
|
||||
|
||||
// Match the Python script pattern: remove all non-word chars except spaces, hyphens, underscores, and dots
|
||||
// But preserve Unicode characters (Python's \w includes Unicode, JS needs a different approach)
|
||||
safeArtist = safeArtist.replace(/[<>:"/\\|?*]/g, ''); // Remove definitely problematic chars first
|
||||
safeAlbum = safeAlbum.replace(/[<>:"/\\|?*]/g, '');
|
||||
|
||||
// Remove other punctuation but keep Unicode letters, digits, spaces, hyphens, underscores, dots
|
||||
safeArtist = safeArtist.replace(/[^\p{L}\p{N}\s\-_.]/gu, '');
|
||||
safeAlbum = safeAlbum.replace(/[^\p{L}\p{N}\s\-_.]/gu, '');
|
||||
|
||||
// Replace spaces with underscores
|
||||
safeArtist = safeArtist.replace(/\s+/g, '_').slice(0, 100);
|
||||
safeAlbum = safeAlbum.replace(/\s+/g, '_').slice(0, 100);
|
||||
|
||||
const filename = `rank_${rank}_${safeArtist}_${safeAlbum}.jpg`;
|
||||
return `covers/${filename}`;
|
||||
}
|
||||
|
|
@ -267,6 +282,11 @@ function createAlbumCard(album) {
|
|||
|
||||
const coverImagePath = getCoverImagePath(album);
|
||||
|
||||
// Determine if this album has a next album in the current view
|
||||
const currentIndex = filteredData.findIndex(a => a.Rank === album.Rank);
|
||||
const hasNext = currentIndex !== -1 && currentIndex < filteredData.length - 1;
|
||||
const nextAlbum = hasNext ? filteredData[currentIndex + 1] : null;
|
||||
|
||||
card.innerHTML = `
|
||||
<div class="album-header-grid">
|
||||
<div class="album-rank">#${album.Rank}</div>
|
||||
|
|
@ -306,8 +326,8 @@ function createAlbumCard(album) {
|
|||
Listen on Spotify
|
||||
</a>
|
||||
</div>
|
||||
${album.Rank < 500 ? `
|
||||
<button class="next-album-link next-album-link-wide" data-rank="${album.Rank}" title="Go to next album (#${album.Rank + 1})">
|
||||
${hasNext ? `
|
||||
<button class="next-album-link next-album-link-wide" data-rank="${album.Rank}" title="Go to next album (#${nextAlbum.Rank})">
|
||||
Next Album →
|
||||
</button>
|
||||
` : ''}
|
||||
|
|
@ -330,10 +350,7 @@ function createAlbumCard(album) {
|
|||
nextBtn.addEventListener('click', function(e) {
|
||||
e.stopPropagation(); // Prevent card click
|
||||
const currentRank = parseInt(this.getAttribute('data-rank'));
|
||||
const nextRank = currentRank + 1;
|
||||
if (nextRank <= 500) {
|
||||
jumpToRankNumber(nextRank);
|
||||
}
|
||||
navigateToNextAlbum(currentRank);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -726,6 +743,30 @@ function scrollToRank(rank) {
|
|||
}
|
||||
}
|
||||
|
||||
function navigateToNextAlbum(currentRank) {
|
||||
// Find the current album in the filtered data
|
||||
const currentIndex = filteredData.findIndex(album => album.Rank === currentRank);
|
||||
|
||||
if (currentIndex === -1) return;
|
||||
|
||||
// Get the next album in the current view
|
||||
const nextIndex = currentIndex + 1;
|
||||
|
||||
if (nextIndex < filteredData.length) {
|
||||
const nextAlbum = filteredData[nextIndex];
|
||||
|
||||
// Ensure we've loaded enough albums
|
||||
const requiredPage = Math.ceil((nextIndex + 1) / itemsPerPage);
|
||||
if (currentPage < requiredPage) {
|
||||
currentPage = requiredPage;
|
||||
renderAlbums();
|
||||
}
|
||||
|
||||
// Scroll to the next album
|
||||
setTimeout(() => scrollToRank(nextAlbum.Rank), 100);
|
||||
}
|
||||
}
|
||||
|
||||
function handleAlbumShare(rank, buttonElement) {
|
||||
const albumUrl = generateAlbumUrl(rank);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue