PageSourceSearch

https://heardle80s.com/inc/settings_LT.js?v=10

js heardle80s.com collected 2026-09-25 15:18:25 UTC 35,313 bytes, 965 lines download raw bytes

1// Heardle Settings - Settings Popup
2// This file handles the settings popup with theme toggle and reset stats
3
4(function() {
5    // Get current theme from localStorage (default is dark)
6    function getCurrentTheme() {
7        return localStorage.getItem('heardleTheme') || 'dark';
8    }
9    
10    // Apply theme to the page
11    function applyTheme(theme) {
12        const body = document.body;
13        
14        if (theme === 'light') {
15            body.classList.remove('dark-theme');
16            body.classList.add('light-theme');
17        } else {
18            body.classList.remove('light-theme');
19            body.classList.add('dark-theme');
20        }
21        
22        localStorage.setItem('heardleTheme', theme);
23        
24        // Force a repaint to ensure theme changes are applied
25        body.style.display = 'none';
26        body.offsetHeight; // Trigger reflow
27        body.style.display = '';
28    }
29    
30    // Create the settings modal HTML
31    function createSettingsHTML() {
32        const currentTheme = getCurrentTheme();
33        const isLight = currentTheme === 'light';
34        
35        return `
36            <div id="settings-modal" class="settings-modal-overlay">
37                <div class="settings-modal-content">
38                    <div class="settings-header">
39                        <h2 class="settings-title">SETTINGS</h2>
40                        <button id="settings-close" class="settings-close-btn">
41                            <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
42                                <line x1="18" y1="6" x2="6" y2="18"></line>
43                                <line x1="6" y1="6" x2="18" y2="18"></line>
44                            </svg>
45                        </button>
46                    </div>
47                    
48                    <!-- Theme Toggle Section -->
49                    <div class="settings-section">
50                        <div class="settings-item">
51                            <div class="settings-label-group">
52                                <div class="settings-label">🌙 Theme</div>
53                                <div class="settings-description">Switch between dark mode and light</div>
54                            </div>
55                            <label class="theme-toggle">
56                                <input type="checkbox" id="theme-checkbox" ${isLight ? 'checked' : ''}>
57                                <span class="theme-toggle-slider"></span>
58                                <span class="theme-toggle-labels">
59<span class="theme-label-dark">🌙</span>                                    
60<span class="theme-label-light">☀️</span>
61                                    
62                                </span>
63                            </label>
64                        </div>
65                    </div>
66                    
67<!-- Sticky Icons Section -->
68                    <div class="settings-section">
69                        <div class="settings-item">
70                            <div class="settings-label-group">
71                                <div class="settings-label">📊 Sticky Stats</div>
72                                <div class="settings-description">Show stats icon on the right side</div>
73                            </div>
74                            <label class="theme-toggle">
75                                <input type="checkbox" id="sticky-stats-checkbox" ${localStorage.getItem('stickyStats') === 'true' ? 'checked' : ''}>
76                                <span class="theme-toggle-slider"></span>
77                            </label>
78                        </div>
79                    </div>
80                    
81                    <div class="settings-section">
82                        <div class="settings-item">
83                            <div class="settings-label-group">
84                                <div class="settings-label">📅 Sticky Calendar</div>
85                                <div class="settings-description">Show calendar icon on the right side</div>
86                            </div>
87                            <label class="theme-toggle">
88                                <input type="checkbox" id="sticky-calendar-checkbox" ${localStorage.getItem('stickyCalendar') === 'true' ? 'checked' : ''}>
89                                <span class="theme-toggle-slider"></span>
90                            </label>
91                        </div>
92                    </div>
93
94                    <!-- Download Stats Section -->
95                    <div class="settings-section">
96                        <div class="settings-item settings-item-column">
97                            <div class="settings-label-group">
98                                <div class="settings-label">⬇️ Download Stats</div>
99                                <div class="settings-description">Save your stats as a file to transfer to another device</div>
100                            </div>
101                            <button id="download-stats-btn" class="settings-download-btn">
102                                Download My Stats
103                            </button>
104                        </div>
105                    </div>
106
107                    <!-- Upload Stats Section -->
108                    <div class="settings-section">
109                        <div class="settings-item settings-item-column">
110                            <div class="settings-label-group">
111                                <div class="settings-label">⬆️ Upload Stats</div>
112                                <div class="settings-description">Restore stats from a previously downloaded file</div>
113                            </div>
114                            <label id="upload-stats-label" class="settings-upload-btn">
115                                Upload Stats File
116                                <input type="file" id="upload-stats-input" accept=".json" style="display:none;">
117                            </label>
118                        </div>
119                    </div>
120
121                    <!-- Reset Stats Section -->
122                    <div class="settings-section">
123                        <div class="settings-item settings-item-column">
124                            <div class="settings-label-group">
125                                <div class="settings-label">🔄 Reset Stats</div>
126                                <div class="settings-description">Clear all your game statistics and start fresh</div>
127                            </div>
128                            <button id="reset-stats-btn" class="settings-reset-btn">
129                                Reset All Stats
130                            </button>
131                        </div>
132                    </div>
133                    
134                    <div class="settings-footer">
135                        <div class="settings-version">&#169;HeardleDecades.com</div>
136                    </div>
137                </div>
138            </div>
139            
140            <!-- Confirmation Modal (hidden by default) -->
141            <div id="confirm-modal" class="confirm-modal-overlay" style="display: none;">
142                <div class="confirm-modal-content">
143                    <h3 class="confirm-title">⚠️ Confirm Reset</h3>
144                    <p class="confirm-message">Are you sure you want to reset all your stats? This will permanently delete:</p>
145                    <ul class="confirm-list">
146                        <li>All your game history</li>
147                        <li>Your win streak</li>
148                        <li>Your guess distribution</li>
149                    </ul>
150                    <p class="confirm-warning">This cannot be undone!</p>
151                    <div class="confirm-buttons">
152                        <button id="confirm-cancel" class="confirm-btn confirm-btn-cancel">Cancel</button>
153                        <button id="confirm-reset" class="confirm-btn confirm-btn-danger">Yes, Reset Everything</button>
154                    </div>
155                </div>
156            </div>
157        `;
158    }
159    
160    // Create the settings CSS
161    function createSettingsStyles() {
162        const styles = document.createElement('style');
163        styles.id = 'settings-styles';
164        styles.textContent = `
165            .settings-modal-overlay {
166                position: fixed;
167                top: 0;
168                left: 0;
169                width: 100%;
170                height: 100%;
171                background: rgba(0, 0, 0, 0.8);
172                display: flex;
173                justify-content: center;
174                align-items: flex-start;
175                padding-top: 80px;
176                z-index: 9999;
177                overflow-y: auto;
178            }
179            
180            .settings-modal-content {
181                background: #1a1a2e;
182                border: 1px solid #4a4a6a;
183                border-radius: 8px;
184                padding: 20px;
185                max-width: 450px;
186                width: 90%;
187                color: #ffffff;
188                position: relative;
189            }
190            
191            .settings-header {
192                display: flex;
193                justify-content: space-between;
194                align-items: center;
195                margin-bottom: 20px;
196                border-bottom: 1px solid #4a4a6a;
197                padding-bottom: 12px;
198            }
199            
200            .settings-title {
201                font-size: 14px;
202                font-weight: 600;
203                letter-spacing: 2px;
204                color: #9b59b6;
205                margin: 0;
206            }
207            
208            .settings-close-btn {
209                background: none;
210                border: none;
211                color: #9b59b6;
212                cursor: pointer;
213                padding: 4px;
214                display: flex;
215                align-items: center;
216                justify-content: center;
217                transition: transform 0.2s;
218            }
219            
220            .settings-close-btn:hover {
221                transform: rotate(90deg);
222            }
223            
224            .settings-section {
225                margin-bottom: 20px;
226                padding: 16px;
227                background: rgba(74, 74, 106, 0.2);
228                border-radius: 8px;
229                border: 1px solid #4a4a6a;
230            }
231            
232            .settings-item {
233                display: flex;
234                justify-content: space-between;
235                align-items: center;
236                gap: 16px;
237            }
238            
239            .settings-item-column {
240                flex-direction: column;
241                align-items: stretch;
242            }
243            
244            .settings-label-group {
245                flex: 1;
246            }
247            
248            .settings-label {
249                font-size: 16px;
250                font-weight: 600;
251                margin-bottom: 4px;
252                color: #ffffff;
253            }
254            
255            .settings-description {
256                font-size: 12px;
257                color: #b0b0c0;
258                line-height: 1.4;
259            }
260            
261            /* Theme Toggle Switch */
262            .theme-toggle {
263                position: relative;
264                display: inline-block;
265                width: 60px;
266                height: 30px;
267                flex-shrink: 0;
268            }
269            
270            .theme-toggle input {
271                opacity: 0;
272                width: 0;
273                height: 0;
274            }
275            
276            .theme-toggle-slider {
277                position: absolute;
278                cursor: pointer;
279                top: 0;
280                left: 0;
281                right: 0;
282                bottom: 0;
283                background: linear-gradient(135deg, #2c3e50, #34495e);
284                transition: 0.4s;
285                border-radius: 30px;
286            }
287            
288            .theme-toggle-slider:before {
289                position: absolute;
290                content: "";
291                height: 22px;
292                width: 22px;
293                left: 4px;
294                bottom: 4px;
295                background-color: white;
296                transition: 0.4s;
297                border-radius: 50%;
298            }
299            
300            .theme-toggle input:checked + .theme-toggle-slider {
301                background: linear-gradient(135deg, #ffd700, #ff8c00);
302            }
303            
304            .theme-toggle input:checked + .theme-toggle-slider:before {
305                transform: translateX(30px);
306            }
307            
308            .theme-toggle-labels {
309                position: absolute;
310                top: 50%;
311                width: 100%;
312                transform: translateY(-50%);
313                display: flex;
314                justify-content: space-between;
315                padding: 0 6px;
316                pointer-events: none;
317                font-size: 14px;
318            }
319            
320            .theme-label-light {
321                opacity: 1;
322                transition: opacity 0.3s;
323            }
324            
325            .theme-label-dark {
326                opacity: 0.3;
327                transition: opacity 0.3s;
328            }
329            
330            .theme-toggle input:checked ~ .theme-toggle-labels .theme-label-light {
331                opacity: 0.3;
332            }
333            
334            .theme-toggle input:checked ~ .theme-toggle-labels .theme-label-dark {
335                opacity: 1;
336            }
337            
338            /* Download Button */
339            .settings-download-btn {
340                background: linear-gradient(135deg, #00bcd4, #0097a7);
341                border: none;
342                color: white;
343                padding: 12px 24px;
344                border-radius: 8px;
345                cursor: pointer;
346                font-size: 14px;
347                font-weight: 600;
348                transition: transform 0.2s, box-shadow 0.2s;
349                margin-top: 12px;
350                width: 100%;
351            }
352
353            .settings-download-btn:hover {
354                transform: scale(1.02);
355                box-shadow: 0 4px 15px rgba(0, 188, 212, 0.4);
356            }
357
358            /* Upload Button */
359            .settings-upload-btn {
360                background: linear-gradient(135deg, #9b59b6, #7d3c98);
361                border: none;
362                color: white;
363                padding: 12px 24px;
364                border-radius: 8px;
365                cursor: pointer;
366                font-size: 14px;
367                font-weight: 600;
368                transition: transform 0.2s, box-shadow 0.2s;
369                margin-top: 12px;
370                width: 100%;
371                display: block;
372                text-align: center;
373                box-sizing: border-box;
374            }
375
376            .settings-upload-btn:hover {
377                transform: scale(1.02);
378                box-shadow: 0 4px 15px rgba(155, 89, 182, 0.4);
379            }
380
381            /* Reset Button */
382            .settings-reset-btn {
383                background: linear-gradient(135deg, #e74c3c, #c0392b);
384                border: none;
385                color: white;
386                padding: 12px 24px;
387                border-radius: 8px;
388                cursor: pointer;
389                font-size: 14px;
390                font-weight: 600;
391                transition: transform 0.2s, box-shadow 0.2s;
392                margin-top: 12px;
393                width: 100%;
394            }
395            
396            .settings-reset-btn:hover {
397                transform: scale(1.02);
398                box-shadow: 0 4px 15px rgba(231, 76, 60, 0.4);
399            }
400            
401            .settings-footer {
402                text-align: center;
403                padding-top: 16px;
404                margin-top: 16px;
405                border-top: 1px solid #4a4a6a;
406            }
407            
408            .settings-version {
409                font-size: 11px;
410                color: #7a7a8a;
411            }
412            
413            /* Confirmation Modal */
414            .confirm-modal-overlay {
415                position: fixed;
416                top: 0;
417                left: 0;
418                width: 100%;
419                height: 100%;
420                background: rgba(0, 0, 0, 0.9);
421                display: flex;
422                justify-content: center;
423                align-items: center;
424                z-index: 10000;
425            }
426            
427            .confirm-modal-content {
428                background: #1a1a2e;
429                border: 2px solid #e74c3c;
430                border-radius: 8px;
431                padding: 24px;
432                max-width: 400px;
433                width: 90%;
434                color: #ffffff;
435            }
436            
437            .confirm-title {
438                font-size: 18px;
439                font-weight: 700;
440                margin: 0 0 16px 0;
441                color: #e74c3c;
442            }
443            
444            .confirm-message {
445                font-size: 14px;
446                margin-bottom: 12px;
447                color: #ffffff;
448            }
449            
450            .confirm-list {
451                list-style: none;
452                padding: 0;
453                margin: 12px 0;
454            }
455            
456            .confirm-list li {
457                padding: 6px 0;
458                padding-left: 24px;
459                position: relative;
460                font-size: 13px;
461                color: #b0b0c0;
462            }
463            
464            .confirm-list li:before {
465                content: "•";
466                position: absolute;
467                left: 8px;
468                color: #e74c3c;
469                font-weight: bold;
470            }
471            
472            .confirm-warning {
473                font-size: 13px;
474                font-weight: 600;
475                color: #e74c3c;
476                margin: 16px 0;
477            }
478            
479            .confirm-buttons {
480                display: flex;
481                gap: 12px;
482                margin-top: 20px;
483            }
484            
485            .confirm-btn {
486                flex: 1;
487                padding: 12px;
488                border: none;
489                border-radius: 8px;
490                font-size: 14px;
491                font-weight: 600;
492                cursor: pointer;
493                transition: transform 0.2s, box-shadow 0.2s;
494            }
495            
496            .confirm-btn-cancel {
497                background: #4a4a6a;
498                color: white;
499            }
500            
501            .confirm-btn-cancel:hover {
502                background: #5a5a7a;
503                transform: scale(1.02);
504            }
505            
506            .confirm-btn-danger {
507                background: linear-gradient(135deg, #e74c3c, #c0392b);
508                color: white;
509            }
510            
511            .confirm-btn-danger:hover {
512                transform: scale(1.02);
513                box-shadow: 0 4px 15px rgba(231, 76, 60, 0.4);
514            }
515            
516            /* Success message */
517            .settings-success-message {
518                position: fixed;
519                bottom: 20px;
520                left: 50%;
521                transform: translateX(-50%);
522                background: #2d5a2d;
523                color: white;
524                padding: 12px 24px;
525                border-radius: 8px;
526                font-size: 14px;
527                font-weight: 600;
528                z-index: 10001;
529                animation: fadeInOut 2s ease-in-out;
530            }
531            
532            @keyframes fadeInOut {
533                0% { opacity: 0; transform: translateX(-50%) translateY(20px); }
534                15% { opacity: 1; transform: translateX(-50%) translateY(0); }
535                85% { opacity: 1; transform: translateX(-50%) translateY(0); }
536                100% { opacity: 0; transform: translateX(-50%) translateY(-20px); }
537            }
538            
539            @media (max-width: 400px) {
540                .settings-modal-content {
541                    padding: 16px;
542                }
543                
544                .settings-item {
545                    flex-direction: column;
546                    align-items: flex-start;
547                }
548            }
549
550/* Sticky Icons Container */
551            .sticky-icons-container {
552                position: fixed;
553                right: 20px;
554                top: 15px;
555                display: flex;
556                flex-direction: column;
557                gap: 12px;
558                z-index: 1000;
559            }
560            
561            .sticky-icon {
562                width: 50px;
563                height: 50px;
564                border-radius: 50%;
565                background: linear-gradient(135deg, #9b59b6, #8e44ad);
566                border: 2px solid #4a4a6a;
567                color: white;
568                font-size: 24px;
569                cursor: pointer;
570                display: flex;
571                align-items: center;
572                justify-content: center;
573                transition: all 0.3s ease;
574                box-shadow: 0 4px 15px rgba(155, 89, 182, 0.3);
575            }
576            
577            .sticky-icon:hover {
578                transform: scale(1.1);
579                box-shadow: 0 6px 20px rgba(155, 89, 182, 0.5);
580                background: linear-gradient(135deg, #a569c0, #9b59b6);
581            }
582            
583            .sticky-icon:active {
584                transform: scale(0.95);
585            }
586            
587            @media (max-width: 768px) {
588                .sticky-icons-container {
589                    right: 10px;
590                    top: 15px;
591                }
592                
593                .sticky-icon {
594                    width: 45px;
595                    height: 45px;
596                    font-size: 22px;
597                }
598            }
599
600/* Light Theme Styles */
601            body.light-theme {
602                background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
603                color: #1a1a1a;
604            }
605            
606            body.light-theme .settings-modal-content {
607                background: #ffffff;
608                border: 1px solid #d0d0d0;
609                color: #1a1a1a;
610            }
611            
612            body.light-theme .settings-title {
613                color: #7b3ff2;
614            }
615            
616            body.light-theme .settings-close-btn {
617                color: #7b3ff2;
618            }
619            
620            body.light-theme .settings-section {
621                border-bottom: 1px solid #e0e0e0;
622            }
623            
624            body.light-theme .settings-label {
625                color: #1a1a1a;
626            }
627            
628            body.light-theme .settings-description {
629                color: #666666;
630            }
631            
632            body.light-theme .settings-reset-btn {
633                background: #7b3ff2;
634                color: white;
635            }
636            
637            body.light-theme .settings-reset-btn:hover {
638                background: #6a2dd4;
639            }
640            
641            body.light-theme .settings-version {
642                color: #666666;
643            }
644            
645            body.light-theme .confirm-modal-content {
646                background: #ffffff;
647                border: 1px solid #d0d0d0;
648                color: #1a1a1a;
649            }
650            
651            body.light-theme .confirm-title {
652                color: #1a1a1a;
653            }
654            
655            body.light-theme .confirm-message,
656            body.light-theme .confirm-list,
657            body.light-theme .confirm-warning {
658                color: #333333;
659            }
660
661`;
662        return styles;
663    }
664    
665    // Show success message
666    function showSuccessMessage(text) {
667        const existing = document.querySelector('.settings-success-message');
668        if (existing) existing.remove();
669        
670        const message = document.createElement('div');
671        message.className = 'settings-success-message';
672        message.textContent = text;
673        document.body.appendChild(message);
674        
675        setTimeout(() => {
676            message.remove();
677        }, 2000);
678    }
679    
680    // Handle theme toggle
681    function toggleTheme() {
682        const checkbox = document.getElementById('theme-checkbox');
683        const newTheme = checkbox.checked ? 'light' : 'dark';
684        applyTheme(newTheme);
685        // Shorter message for mobile screens
686        const message = newTheme === 'dark' ? '🌙 Dark mode' : '☀️ Light mode';
687        showSuccessMessage(message);
688    }
689
690// Handle sticky stats toggle
691    function toggleStickyStats() {
692        const checkbox = document.getElementById('sticky-stats-checkbox');
693        const isEnabled = checkbox.checked;
694        localStorage.setItem('stickyStats', isEnabled.toString());
695        
696        if (isEnabled) {
697            showStickyIcons();
698            showSuccessMessage('📊 Stats enabled');
699        } else {
700            hideStickyIcons();
701            showSuccessMessage('📊 Stats disabled');
702        }
703    }
704    
705    // Handle sticky calendar toggle
706    function toggleStickyCalendar() {
707        const checkbox = document.getElementById('sticky-calendar-checkbox');
708        const isEnabled = checkbox.checked;
709        localStorage.setItem('stickyCalendar', isEnabled.toString());
710        
711        if (isEnabled) {
712            showStickyIcons();
713            showSuccessMessage('📅 Calendar enabled');
714        } else {
715            hideStickyIcons();
716            showSuccessMessage('📅 Calendar disabled');
717        }
718    }
719    
720    // Show sticky icons based on settings
721    function showStickyIcons() {
722        // Remove existing icons first
723        const existing = document.getElementById('sticky-icons-container');
724        if (existing) existing.remove();
725        
726        const showStats = localStorage.getItem('stickyStats') === 'true';
727        const showCalendar = localStorage.getItem('stickyCalendar') === 'true';
728        
729        // Only create container if at least one is enabled
730        if (!showStats && !showCalendar) return;
731        
732        const container = document.createElement('div');
733        container.id = 'sticky-icons-container';
734        container.className = 'sticky-icons-container';
735        
736        if (showStats) {
737            const statsIcon = document.createElement('button');
738            statsIcon.className = 'sticky-icon';
739            statsIcon.innerHTML = '📊';
740            statsIcon.title = 'View Stats';
741            statsIcon.onclick = () => {
742                if (window.HeardleStats && window.HeardleStats.open) {
743                    window.HeardleStats.open();
744                }
745            };
746            container.appendChild(statsIcon);
747        }
748        
749        if (showCalendar) {
750            const calendarIcon = document.createElement('button');
751            calendarIcon.className = 'sticky-icon';
752            calendarIcon.innerHTML = '📅';
753            calendarIcon.title = 'View Calendar';
754            calendarIcon.onclick = () => {
755                if (window.HeardleCalendar && window.HeardleCalendar.open) {
756                    window.HeardleCalendar.open();
757                }
758            };
759            container.appendChild(calendarIcon);
760        }
761        
762        document.body.appendChild(container);
763    }
764    
765    // Hide sticky icons
766    function hideStickyIcons() {
767        const container = document.getElementById('sticky-icons-container');
768        const showStats = localStorage.getItem('stickyStats') === 'true';
769        const showCalendar = localStorage.getItem('stickyCalendar') === 'true';
770        
771        // If both are disabled, remove container
772        if (!showStats && !showCalendar) {
773            if (container) container.remove();
774        } else {
775            // Otherwise, refresh to show only enabled ones
776            showStickyIcons();
777        }
778    }
779    
780    // Show confirmation modal
781    function showConfirmModal() {
782        const confirmModal = document.getElementById('confirm-modal');
783        if (confirmModal) {
784            confirmModal.style.display = 'flex';
785        }
786    }
787    
788    // Hide confirmation modal
789    function hideConfirmModal() {
790        const confirmModal = document.getElementById('confirm-modal');
791        if (confirmModal) {
792            confirmModal.style.display = 'none';
793        }
794    }
795    
796    // Download stats as a JSON file
797    function downloadStats() {
798        const stats = localStorage.getItem('userStats');
799        if (!stats || JSON.parse(stats).length === 0) {
800            showSuccessMessage('⚠️ No stats to download!');
801            return;
802        }
803        const blob = new Blob([stats], { type: 'application/json' });
804        const url = URL.createObjectURL(blob);
805        const a = document.createElement('a');
806        a.href = url;
807        a.download = 'heardle-stats.json';
808        a.click();
809        URL.revokeObjectURL(url);
810        showSuccessMessage('✅ Stats downloaded!');
811    }
812
813    // Upload stats from a JSON file
814    function uploadStats(file) {
815        if (!file) return;
816        const reader = new FileReader();
817        reader.onload = function(e) {
818            try {
819                const data = JSON.parse(e.target.result);
820                if (!Array.isArray(data)) throw new Error('Invalid format');
821                localStorage.setItem('userStats', JSON.stringify(data));
822                showSuccessMessage('✅ Stats restored!');
823                setTimeout(() => {
824                    closeSettings();
825                    location.reload();
826                }, 1500);
827            } catch (err) {
828                showSuccessMessage('❌ Invalid stats file!');
829            }
830        };
831        reader.readAsText(file);
832    }
833
834    // Reset all stats
835    function resetAllStats() {
836        // Clear the userStats from localStorage
837        localStorage.removeItem('userStats');
838        
839        // Reinitialize with empty array
840        localStorage.setItem('userStats', JSON.stringify([]));
841        
842        // Hide confirmation modal
843        hideConfirmModal();
844        
845        // Show success message (shorter for mobile)
846        showSuccessMessage('✅ Stats reset!');
847        
848        // Close settings modal
849        setTimeout(() => {
850            closeSettings();
851            // Reload the page to refresh everything
852            location.reload();
853        }, 1500);
854    }
855    
856    // Open the settings modal
857    function openSettings() {
858        // Add styles if not already added
859        if (!document.getElementById('settings-styles')) {
860            document.head.appendChild(createSettingsStyles());
861        }
862        
863        // Remove any existing modal
864        const existingModal = document.getElementById('settings-modal');
865        if (existingModal) {
866            existingModal.remove();
867        }
868        
869        const existingConfirm = document.getElementById('confirm-modal');
870        if (existingConfirm) {
871            existingConfirm.remove();
872        }
873        
874        // Create and add the modal
875        const modalWrapper = document.createElement('div');
876        modalWrapper.innerHTML = createSettingsHTML();
877        
878        // Add both modals to the body
879        while (modalWrapper.firstChild) {
880            document.body.appendChild(modalWrapper.firstChild);
881        }
882        
883        // Add event listeners for settings modal
884        document.getElementById('settings-close').addEventListener('click', closeSettings);
885        document.getElementById('theme-checkbox').addEventListener('change', toggleTheme);
886        document.getElementById('sticky-stats-checkbox').addEventListener('change', toggleStickyStats);
887        document.getElementById('sticky-calendar-checkbox').addEventListener('change', toggleStickyCalendar);
888        document.getElementById('download-stats-btn').addEventListener('click', downloadStats);
889        document.getElementById('upload-stats-input').addEventListener('change', (e) => uploadStats(e.target.files[0]));
890        document.getElementById('reset-stats-btn').addEventListener('click', showConfirmModal);
891        
892        // Add event listeners for confirmation modal
893        document.getElementById('confirm-cancel').addEventListener('click', hideConfirmModal);
894        document.getElementById('confirm-reset').addEventListener('click', resetAllStats);
895        
896        // Close when clicking outside
897        document.getElementById('settings-modal').addEventListener('click', (e) => {
898            if (e.target.id === 'settings-modal') {
899                closeSettings();
900            }
901        });
902        
903        // Apply current theme on page load
904        applyTheme(getCurrentTheme());
905    }
906    
907    // Close the settings modal
908    function closeSettings() {
909        const modal = document.getElementById('settings-modal');
910        if (modal) {
911            modal.remove();
912        }
913        const confirm = document.getElementById('confirm-modal');
914        if (confirm) {
915            confirm.remove();
916        }
917    }
918    
919    // Apply saved theme when page loads (runs immediately)
920    (function() {
921        const savedTheme = getCurrentTheme();
922        applyTheme(savedTheme);
923    })();
924    
925    // Also apply on DOMContentLoaded as backup
926    if (document.readyState === 'loading') {
927        document.addEventListener('DOMContentLoaded', () => {
928            applyTheme(getCurrentTheme());
929        });
930    } else {
931        // DOM already loaded
932        applyTheme(getCurrentTheme());
933    }
934    
935    // Expose functions globally
936   window.HeardleSettings = {
937        open: openSettings,
938        close: closeSettings,
939        showStickyIcons: showStickyIcons,
940        hideStickyIcons: hideStickyIcons
941    };
942    
943    // Add styles immediately when page loads
944    if (!document.getElementById('settings-styles')) {
945        document.head.appendChild(createSettingsStyles());
946    }
947    
948    // Initialize sticky icons when page loads
949    function initializeStickyIcons() {
950        // Ensure styles are loaded first
951        if (!document.getElementById('settings-styles')) {
952            document.head.appendChild(createSettingsStyles());
953        }
954        // Small delay to ensure CSS is parsed
955        setTimeout(() => {
956            showStickyIcons();
957        }, 150);
958    }
959    
960    if (document.readyState === 'loading') {
961        document.addEventListener('DOMContentLoaded', initializeStickyIcons);
962    } else {
963        initializeStickyIcons();
964    }
965})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.