+117
static/emoji-resolver.js
+117
static/emoji-resolver.js
···
1
+
// Emoji Resolver Module - Handles mapping emoji names to correct filenames
2
+
(function() {
3
+
'use strict';
4
+
5
+
// Cache for emoji name -> filename mapping
6
+
let emojiMap = null;
7
+
let loadPromise = null;
8
+
9
+
// Load emoji mapping from API
10
+
async function loadEmojiMap() {
11
+
if (emojiMap) return emojiMap;
12
+
if (loadPromise) return loadPromise;
13
+
14
+
loadPromise = fetch('/api/custom-emojis')
15
+
.then(response => response.json())
16
+
.then(data => {
17
+
if (!Array.isArray(data)) {
18
+
console.error('Invalid emoji data received');
19
+
return new Map();
20
+
}
21
+
emojiMap = new Map(data.map(emoji => [emoji.name, emoji.filename]));
22
+
return emojiMap;
23
+
})
24
+
.catch(err => {
25
+
console.error('Failed to load emoji map:', err);
26
+
emojiMap = new Map();
27
+
return emojiMap;
28
+
});
29
+
30
+
return loadPromise;
31
+
}
32
+
33
+
// Get the correct emoji filename for a given name
34
+
function getEmojiFilename(emojiName) {
35
+
if (!emojiMap) return null;
36
+
return emojiMap.get(emojiName);
37
+
}
38
+
39
+
// Update a single emoji image element
40
+
function updateEmojiImage(img) {
41
+
const emojiName = img.getAttribute('data-emoji-name');
42
+
if (!emojiName) return;
43
+
44
+
const filename = getEmojiFilename(emojiName);
45
+
if (filename) {
46
+
// Found the correct filename, update src
47
+
img.src = `/emojis/${filename}`;
48
+
// Remove placeholder class if present
49
+
img.classList.remove('emoji-placeholder');
50
+
// Remove the error handler since we have the correct path
51
+
img.onerror = null;
52
+
} else {
53
+
// Emoji not found in map, try common extensions as fallback
54
+
// This handles newly added emojis that aren't in the cached map yet
55
+
img.src = `/emojis/${emojiName}.png`;
56
+
img.onerror = function() {
57
+
this.onerror = null;
58
+
this.src = `/emojis/${emojiName}.gif`;
59
+
};
60
+
img.classList.remove('emoji-placeholder');
61
+
}
62
+
}
63
+
64
+
// Update all emoji images on the page
65
+
function updateAllEmojiImages() {
66
+
const images = document.querySelectorAll('img[data-emoji-name]');
67
+
images.forEach(updateEmojiImage);
68
+
}
69
+
70
+
// Initialize on DOM ready
71
+
async function initialize() {
72
+
// Load the emoji map
73
+
await loadEmojiMap();
74
+
// Update all existing emoji images
75
+
updateAllEmojiImages();
76
+
77
+
// Set up a MutationObserver to handle dynamically added content
78
+
const observer = new MutationObserver((mutations) => {
79
+
mutations.forEach((mutation) => {
80
+
mutation.addedNodes.forEach((node) => {
81
+
if (node.nodeType === Node.ELEMENT_NODE) {
82
+
// Check if the added node is an emoji image
83
+
if (node.tagName === 'IMG' && node.getAttribute('data-emoji-name')) {
84
+
updateEmojiImage(node);
85
+
}
86
+
// Also check descendants
87
+
const images = node.querySelectorAll?.('img[data-emoji-name]');
88
+
images?.forEach(updateEmojiImage);
89
+
}
90
+
});
91
+
});
92
+
});
93
+
94
+
// Start observing the document body for changes
95
+
observer.observe(document.body, {
96
+
childList: true,
97
+
subtree: true
98
+
});
99
+
}
100
+
101
+
// Export to global scope
102
+
window.EmojiResolver = {
103
+
loadEmojiMap,
104
+
getEmojiFilename,
105
+
updateEmojiImage,
106
+
updateAllEmojiImages,
107
+
initialize
108
+
};
109
+
110
+
// Auto-initialize when DOM is ready
111
+
if (document.readyState === 'loading') {
112
+
document.addEventListener('DOMContentLoaded', initialize);
113
+
} else {
114
+
// DOM is already ready
115
+
initialize();
116
+
}
117
+
})();
+3
templates/base.html
+3
templates/base.html
···
31
31
<!-- Markdown link renderer for statuses -->
32
32
<script src="/static/markdown.js"></script>
33
33
34
+
<!-- Emoji Resolver for correct file extensions -->
35
+
<script src="/static/emoji-resolver.js"></script>
36
+
34
37
<!-- Apply User Settings -->
35
38
<script>
36
39
// Apply saved settings immediately to prevent flash
+4
-3
templates/feed.html
+4
-3
templates/feed.html
···
129
129
<span class="status-emoji">
130
130
{% if status.status.starts_with("custom:") %}
131
131
{% let emoji_name = status.status.strip_prefix("custom:").unwrap() %}
132
-
<img src="/emojis/{{emoji_name}}.png" alt="{{emoji_name}}" title="{{emoji_name}}" class="custom-emoji-display"
133
-
onerror="this.onerror=null; this.src='/emojis/{{emoji_name}}.gif';">
132
+
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
133
+
alt="{{emoji_name}}" title="{{emoji_name}}" class="custom-emoji-display emoji-placeholder"
134
+
data-emoji-name="{{emoji_name}}">
134
135
{% else %}
135
136
<span title="{{status.status}}">{{status.status}}</span>
136
137
{% endif %}
···
989
990
let emojiHtml = '';
990
991
if (status.status.startsWith('custom:')) {
991
992
const emojiName = status.status.substring(7);
992
-
emojiHtml = `<img src="/emojis/${emojiName}.png" alt="${emojiName}" title="${emojiName}" class="custom-emoji-display" onerror="this.onerror=null; this.src='/emojis/${emojiName}.gif';">`;
993
+
emojiHtml = `<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" alt="${emojiName}" title="${emojiName}" class="custom-emoji-display emoji-placeholder" data-emoji-name="${emojiName}">`;
993
994
} else {
994
995
emojiHtml = `<span title="${status.status}">${status.status}</span>`;
995
996
}
+19
-7
templates/status.html
+19
-7
templates/status.html
···
103
103
<span class="status-emoji">
104
104
{% if current.status.starts_with("custom:") %}
105
105
{% let emoji_name = current.status.strip_prefix("custom:").unwrap() %}
106
-
<img src="/emojis/{{emoji_name}}.png" alt="{{emoji_name}}" title="{{emoji_name}}" class="custom-emoji-display"
107
-
onerror="this.onerror=null; this.src='/emojis/{{emoji_name}}.gif';">
106
+
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
107
+
alt="{{emoji_name}}" title="{{emoji_name}}" class="custom-emoji-display emoji-placeholder"
108
+
data-emoji-name="{{emoji_name}}">
108
109
{% else %}
109
110
<span title="{{ current.status }}">{{ current.status }}</span>
110
111
{% endif %}
···
140
141
{% if let Some(current) = current_status.as_ref() %}
141
142
{% if current.status.starts_with("custom:") %}
142
143
{% let emoji_name = current.status.strip_prefix("custom:").unwrap() %}
143
-
<img src="/emojis/{{emoji_name}}.png" alt="{{emoji_name}}" title="{{emoji_name}}"
144
-
onerror="this.onerror=null; this.src='/emojis/{{emoji_name}}.gif';">
144
+
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
145
+
alt="{{emoji_name}}" title="{{emoji_name}}" class="emoji-placeholder"
146
+
data-emoji-name="{{emoji_name}}">
145
147
{% else %}
146
148
<span title="{{ current.status }}">{{ current.status }}</span>
147
149
{% endif %}
···
412
414
<span class="history-emoji">
413
415
{% if status.status.starts_with("custom:") %}
414
416
{% let emoji_name = status.status.strip_prefix("custom:").unwrap() %}
415
-
<img src="/emojis/{{emoji_name}}.png" alt="{{emoji_name}}" title="{{emoji_name}}" class="custom-emoji-display"
416
-
onerror="this.onerror=null; this.src='/emojis/{{emoji_name}}.gif';">
417
+
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
418
+
alt="{{emoji_name}}" title="{{emoji_name}}" class="custom-emoji-display emoji-placeholder"
419
+
data-emoji-name="{{emoji_name}}">
417
420
{% else %}
418
421
<span title="{{ status.status }}">{{ status.status }}</span>
419
422
{% endif %}
···
2243
2246
button.dataset.name = emoji.name;
2244
2247
2245
2248
const img = document.createElement('img');
2246
-
img.src = `/emojis/${emoji.filename}`;
2249
+
// Use placeholder initially to avoid 404s
2250
+
img.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
2251
+
img.dataset.emojiName = emoji.name;
2252
+
img.dataset.emojiFilename = emoji.filename;
2247
2253
img.alt = emoji.name;
2248
2254
img.title = emoji.name;
2249
2255
button.appendChild(img);
2256
+
2257
+
// Load the actual image after a brief delay to let resolver initialize
2258
+
requestAnimationFrame(() => {
2259
+
img.src = `/emojis/${emoji.filename}`;
2260
+
});
2261
+
2250
2262
return button;
2251
2263
};
2252
2264