PageSourceSearch

https://aibluefisher.github.io/resources/js/blog_pagination.js

js aibluefisher.github.io collected 2026-10-03 08:33:57 UTC 7,730 bytes, 190 lines download raw bytes

1// Blog pagination
2(function() {
3    // Blog posts data
4    const blogPosts = [
5        {
6            id: 1,
7            title: "和 3D Vision 相遇的八年",
8            date: "2026-03-15",
9            readTime: 6,
10            category: "PhD",
11            tags: ["PhD", "reconstruction", "3D"],
12            excerpt: "Thoughts on 3D computer vision, and the journey of my PhD career.",
13            image: "./resources/images/blog01_teaser.jpg",
14            url: "./blog-posts/my_phd_journey.html",
15            featured: true
16        },
17        {
18            id: 2,
19            title: "NeRF vs 3DGS: a unified view",
20            date: "2026-01-15",
21            readTime: 9,
22            category: "NeRF",
23            tags: ["NeRF", "math"],
24            excerpt: "Deriving the rendering equations, comparison of volumetric vs. splatting, and where we can expect convergence.",
25            image: "./resources/anti_aliasing_gaussian.gif",
26            // url: "./blog-posts/nerf-vs-3dgs.html"
27        },
28    ];
29
30    const POSTS_PER_PAGE = 3; // Show 3 posts per page
31    let currentPage = 1;
32    const totalPages = Math.ceil(blogPosts.length / POSTS_PER_PAGE);
33
34    // Function to format date
35    function formatDate(dateStr) {
36        const options = { year: 'numeric', month: 'short', day: 'numeric' };
37        return new Date(dateStr).toLocaleDateString(undefined, options);
38    }
39
40    // Function to render blog posts for current page
41    function renderBlogPosts() {
42        const container = document.getElementById('blogPostsContainer');
43        if (!container) return;
44
45        const start = (currentPage - 1) * POSTS_PER_PAGE;
46        const end = start + POSTS_PER_PAGE;
47        const currentPosts = blogPosts.slice(start, end);
48
49        container.innerHTML = currentPosts.map(post => `
50            <article class="blog-post-card">
51                <div class="blog-card-image">
52                    <img src="${post.image}" alt="${post.title}" onerror="this.src='./resources/profile.jpg'">
53                    ${post.featured ? '<span class="featured-badge"><i class="fas fa-star"></i> Featured</span>' : ''}
54                </div>
55                <div class="blog-card-content">
56                    <div class="blog-card-meta">
57                        <span class="blog-date"><i class="far fa-calendar-alt"></i> ${formatDate(post.date)}</span>
58                        <span class="blog-reading"><i class="far fa-clock"></i> ${post.readTime} min read</span>
59                        <span class="blog-category"><i class="fas fa-folder"></i> ${post.category}</span>
60                    </div>
61                    <h4 class="blog-card-title">${post.title}</h4>
62                    <p class="blog-card-excerpt">${post.excerpt}</p>
63                    <div class="blog-card-tags">
64                        ${post.tags.map(tag => `<span class="tag"><i class="fas fa-tag"></i> ${tag}</span>`).join('')}
65                    </div>
66                    <a href="${post.url}" class="blog-card-link">Read full article <i class="fas fa-arrow-right"></i></a>
67                </div>
68            </article>
69        `).join('');
70
71        // Update pagination buttons
72        updatePaginationButtons();
73    }
74
75    // Function to update pagination buttons
76    function updatePaginationButtons() {
77        const prevBtn = document.getElementById('prevPage');
78        const nextBtn = document.getElementById('nextPage');
79        const paginationNumbers = document.getElementById('paginationNumbers');
80
81        if (prevBtn) {
82            prevBtn.disabled = currentPage === 1;
83        }
84        if (nextBtn) {
85            nextBtn.disabled = currentPage === totalPages;
86        }
87
88        // Generate page numbers
89        if (paginationNumbers) {
90            let numbersHTML = '';
91            
92            // Always show first page
93            numbersHTML += `<button class="page-number ${currentPage === 1 ? 'active' : ''}" data-page="1">1</button>`;
94            
95            // Show ellipsis if needed
96            if (currentPage > 3) {
97                numbersHTML += `<span class="page-ellipsis">...</span>`;
98            }
99            
100            // Show pages around current page
101            for (let i = Math.max(2, currentPage - 1); i <= Math.min(totalPages - 1, currentPage + 1); i++) {
102                if (i > 1 && i < totalPages) {
103                    numbersHTML += `<button class="page-number ${currentPage === i ? 'active' : ''}" data-page="${i}">${i}</button>`;
104                }
105            }
106            
107            // Show ellipsis if needed
108            if (currentPage < totalPages - 2) {
109                numbersHTML += `<span class="page-ellipsis">...</span>`;
110            }
111            
112            // Always show last page if there is more than one page
113            if (totalPages > 1) {
114                numbersHTML += `<button class="page-number ${currentPage === totalPages ? 'active' : ''}" data-page="${totalPages}">${totalPages}</button>`;
115            }
116            
117            paginationNumbers.innerHTML = numbersHTML;
118
119            // Add click handlers to page numbers
120            document.querySelectorAll('.page-number').forEach(btn => {
121                btn.addEventListener('click', () => {
122                    const page = parseInt(btn.dataset.page);
123                    if (page !== currentPage) {
124                        currentPage = page;
125                        renderBlogPosts();
126                        // Scroll to blog section smoothly
127                        document.getElementById('blog').scrollIntoView({ behavior: 'smooth' });
128                    }
129                });
130            });
131        }
132    }
133
134    // Initialize pagination
135    function initPagination() {
136        const prevBtn = document.getElementById('prevPage');
137        const nextBtn = document.getElementById('nextPage');
138
139        if (prevBtn) {
140            prevBtn.addEventListener('click', () => {
141                if (currentPage > 1) {
142                    currentPage--;
143                    renderBlogPosts();
144                    document.getElementById('blog').scrollIntoView({ behavior: 'smooth' });
145                }
146            });
147        }
148
149        if (nextBtn) {
150            nextBtn.addEventListener('click', () => {
151                if (currentPage < totalPages) {
152                    currentPage++;
153                    renderBlogPosts();
154                    document.getElementById('blog').scrollIntoView({ behavior: 'smooth' });
155                }
156            });
157        }
158    }
159
160    // Load blog posts when section is in view (lazy loading)
161    function observeBlogSection() {
162        const blogSection = document.getElementById('blog');
163        if (!blogSection) return;
164
165        const observer = new IntersectionObserver((entries) => {
166            entries.forEach(entry => {
167                if (entry.isIntersecting) {
168                    renderBlogPosts();
169                    initPagination();
170                    observer.disconnect(); // Stop observing once loaded
171                }
172            });
173        }, { threshold: 0.1 });
174
175        observer.observe(blogSection);
176    }
177
178    // Initialize if blog section exists
179    if (document.getElementById('blog')) {
180        // Check if we should load immediately (if section is visible)
181        const blogSection = document.getElementById('blog');
182        const rect = blogSection.getBoundingClientRect();
183        if (rect.top < window.innerHeight) {
184            renderBlogPosts();
185            initPagination();
186        } else {
187            observeBlogSection();
188        }
189    }
190})();

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.