AD

Example post

<p>Progress</p>
<div id="progress-container">
  <div id="progress-bar" style="width: 0%;"></div>
  <span id="progressPercentage">0%</span>
</div>

<div id="sort-controls">
  <label for="sort-select">Sort by:</label>
  <select id="sort-select" onchange="changeSortOrder(this.value)">
    <option value="newest">Newest First</option>
    <option value="oldest">Oldest First</option>
  </select>
</div>

<div id="lit005-posts"></div>

<style>
  #progress-container {
    width: 100%;
    height: 30px;
    background-color: #f3f3f3;
    margin-bottom: 20px;
    border-radius: 5px;
    overflow: hidden;
    position: relative;
  }
  
  #progress-bar {
    height: 100%;
    background-color: #28a745;
    text-align: center;
    line-height: 30px;
    color: white;
    transition: width 0.5s ease-in-out;
  }
  
  #progressPercentage {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    font-weight: bold;
    color: #333;
    z-index: 2;
  }

  #sort-controls {
    margin: 20px 0;
    text-align: right;
  }
  
  #sort-select {
    padding: 8px 12px;
    border-radius: 5px;
    border: 1px solid #ddd;
    background-color: #f8f8f8;
    cursor: pointer;
    font-size: 14px;
    transition: all 0.2s ease;
  }
  
  #sort-select:hover {
    border-color: #bbb;
    background-color: #f0f0f0;
  }
  
  #sort-select:focus {
    outline: none;
    border-color: #007bff;
    box-shadow: 0 0 0 2px rgba(0,123,255,0.25);
  }

  #lit005-posts {
    font-family: Arial, sans-serif;
    line-height: 1.6;
    margin: 20px;
  }

  #lit005-posts h3 {
    color: #005c99;
    border-bottom: 2px solid #005c99;
    padding-bottom: 5px;
    margin-top: 30px;
    display: flex;
    justify-content: space-between;
    align-items: center;
  }

  #lit005-posts ul {
    list-style: none;
    padding-left: 0;
    margin-left: 20px;
  }

  #lit005-posts li {
    margin-bottom: 12px;
    position: relative;
  }

  /* No visual indicator for completed posts as per request */

  #lit005-posts a {
    text-decoration: none;
    color: #333;
    font-size: 14px;
    font-weight: bold;
    display: inline-block;
    margin-bottom: 5px;
    padding: 10px 20px;
    background-color: #007bff;
    color: white;
    border-radius: 5px;
    text-align: center;
    width: 80%;
    transition: background-color 0.3s;
  }

  #lit005-posts a:hover {
    background-color: #005c99;
  }

  /* New and improved pagination styles */
  .pagination {
    display: flex;
    justify-content: center;
    align-items: center;
    flex-wrap: wrap;
    margin: 30px 0 20px;
    gap: 8px;
  }

  .pagination-item {
    margin: 0 2px;
    padding: 8px 16px;
    background-color: #f0f0f0;
    color: #333;
    text-decoration: none;
    border-radius: 5px;
    min-width: 40px;
    text-align: center;
    font-weight: 500;
    cursor: pointer;
    transition: all 0.2s ease;
    border: 1px solid #ddd;
    user-select: none;
  }

  .pagination-item:hover:not(.active):not(.disabled) {
    background-color: #e0e0e0;
    border-color: #bbb;
  }

  .pagination-item.active {
    background-color: #007bff;
    color: white;
    border-color: #0062cc;
    font-weight: bold;
    box-shadow: 0 2px 5px rgba(0,0,0,0.1);
    cursor: default;
  }
  
  .pagination-item.disabled {
    opacity: 0.6;
    cursor: not-allowed;
    background-color: #f8f8f8;
  }
  
  .pagination-ellipsis {
    margin: 0 2px;
    padding: 8px 12px;
    color: #666;
  }
  
  .pagination-nav {
    display: flex;
    align-items: center;
    padding: 8px 12px;
    border-radius: 5px;
    background-color: #f0f0f0;
    border: 1px solid #ddd;
    cursor: pointer;
    transition: all 0.2s ease;
  }
  
  .pagination-nav:hover:not(.disabled) {
    background-color: #e0e0e0;
    border-color: #bbb;
  }
  
  .pagination-nav.disabled {
    opacity: 0.6;
    cursor: not-allowed;
  }
  
  .no-posts {
    padding: 20px;
    text-align: center;
    font-size: 14px;
    color: #666;
    background-color: #f8f8f8;
    border-radius: 5px;
    margin-top: 20px;
  }
  
  /* Loading indicator */
  .page-loading {
    opacity: 0.7;
    pointer-events: none;
    transition: opacity 0.3s;
  }
  
  /* Add media query for mobile responsiveness */
  @media (max-width: 576px) {
    .pagination {
      gap: 4px;
    }
    
    .pagination-item {
      padding: 6px 10px;
      min-width: 32px;
      font-size: 14px;
    }
    
    .pagination-nav {
      padding: 6px 8px;
    }
    
    .pagination-ellipsis {
      padding: 6px 8px;
    }
  }
</style>

<script>
  const blogURL = "https://jupebcourseindex.blogspot.com";
  const label = "lit005"; // Changed label from lit004 to lit005
  const maxPostsPerLabel = 10; // Maximum number of posts to show per page
  let currentPage = 1;
  let totalPosts = 0;
  let totalPages = 0;
  let completedPosts = 0;
  let activeTimers = {}; // To track active timers for opened posts
  let allPosts = []; // Store all posts for sorting
  let currentSortOrder = "newest"; // Default sort order
  let isPageLoading = false; // Track if a page transition is in progress
  
  // Initialize when the page loads
  document.addEventListener('DOMContentLoaded', function() {
    // Restore current page from localStorage if available
    const savedPage = localStorage.getItem('lit005-current-page');
    if (savedPage) {
      currentPage = parseInt(savedPage, 10);
    }
    
    // Restore sort preference if available
    const savedSort = localStorage.getItem('lit005-sort-order');
    if (savedSort) {
      currentSortOrder = savedSort;
      document.getElementById('sort-select').value = currentSortOrder;
    }
    
    fetchPostsByLabel(); 
  });
  
  // Run on load without waiting for DOMContentLoaded (for some environments)
  if (document.readyState === 'complete' || document.readyState === 'interactive') {
    setTimeout(() => {
      // Restore current page from localStorage if available
      const savedPage = localStorage.getItem('lit005-current-page');
      if (savedPage) {
        currentPage = parseInt(savedPage, 10);
      }
      
      // Restore sort preference if available
      const savedSort = localStorage.getItem('lit005-sort-order');
      if (savedSort) {
        currentSortOrder = savedSort;
        const sortSelect = document.getElementById('sort-select');
        if (sortSelect) sortSelect.value = currentSortOrder;
      }
      
      fetchPostsByLabel();
    }, 1);
  }
  
  function fetchPostsByLabel() {
    setPageLoading(true);
    const labelURL = `${blogURL}/feeds/posts/summary/-/${encodeURIComponent(label)}?alt=json&max-results=100`;

    fetch(labelURL)
      .then(response => response.json())
      .then(data => {
        const posts = data.feed.entry || [];
        allPosts = posts.map(post => {
          return {
            title: post.title.$t,
            link: post.link.find(l => l.rel === "alternate").href,
            published: new Date(post.published.$t),
            updated: new Date(post.updated.$t)
          };
        });
        
        totalPosts = allPosts.length;
        
        // Sort posts based on current preference
        sortPosts();
        
        // Save total posts count to localStorage
        localStorage.setItem('lit002-total-posts', totalPosts);
        
        displayCurrentPage();
        setPageLoading(false);
      })
      .catch(error => {
        console.error('Error fetching posts:', error);
        document.getElementById("lit002-posts").innerHTML = 
          '<p class="no-posts">Error loading posts. Please try again later.</p>';
        setPageLoading(false);
      });
  }
  
  function setPageLoading(isLoading) {
    isPageLoading = isLoading;
    const postsContainer = document.getElementById("lit002-posts");
    if (isLoading) {
      postsContainer.classList.add("page-loading");
    } else {
      postsContainer.classList.remove("page-loading");
    }
  }
  
  function sortPosts() {
    if (currentSortOrder === "newest") {
      allPosts.sort((a, b) => b.published - a.published);
    } else {
      allPosts.sort((a, b) => a.published - b.published);
    }
  }
  
  function changeSortOrder(sortOrder) {
    if (isPageLoading) return;
    
    setPageLoading(true);
    currentSortOrder = sortOrder;
    localStorage.setItem('lit005-sort-order', sortOrder);
    
    // Re-sort and display
    sortPosts();
    displayCurrentPage();
    
    setTimeout(() => {
      setPageLoading(false);
    }, 300);
  }
  
  function displayCurrentPage() {
    totalPages = Math.ceil(totalPosts / maxPostsPerLabel);
    
    // Validate current page
    if (currentPage > totalPages) {
      currentPage = 1;
    }
    
    localStorage.setItem('lit002-total-pages', totalPages);

    if (totalPosts === 0) {
      document.getElementById("lit002-posts").innerHTML = 
        '<p class="no-posts">No posts available under lit005.</p>';
      return;
    }

    // Save the current page to localStorage
    localStorage.setItem('lit005-current-page', currentPage);

    const startIndex = (currentPage - 1) * maxPostsPerLabel;
    const endIndex = Math.min(startIndex + maxPostsPerLabel, totalPosts);
    let html = `<h3>Literature 005  <span class="post-count">(${totalPosts} posts)</span></h3><ul>`;

    for (let i = startIndex; i < endIndex; i++) {
      const post = allPosts[i];
      const title = post.title;
      const link = post.link;
      const postId = `post-${i}`;
      
      // Save post info to localStorage
      localStorage.setItem(`lit005-post-${i}-title`, title);
      localStorage.setItem(`lit005-post-${i}-link`, link);
      
      // No visual indication needed for completed posts
      html += `<li data-post-id="${postId}"><a href="${link}" target="_blank" class="post-link" data-post-id="${postId}" data-index="${i}">${title}</a></li>`;
    }

    html += `</ul>`;

    // Add pagination if there are multiple pages
    if (totalPages > 1) {
      html += generatePagination();
    }

    document.getElementById("lit005-posts").innerHTML = html;

    // Setup event listeners
    setupPostListeners();
    
    // Update progress bar after posts are loaded
    updateProgressBarFromStorage();
  }

  function generatePagination() {
    let paginationHTML = '<div class="pagination" role="navigation" aria-label="Pagination Navigation">';

    // Previous Page Button
    if (currentPage > 1) {
      paginationHTML += `<span class="pagination-nav" onclick="return changePage(${currentPage - 1})" aria-label="Go to previous page">&laquo; Prev</span>`;
    } else {
      paginationHTML += `<span class="pagination-nav disabled" aria-disabled="true">&laquo; Prev</span>`;
    }

    // Page Number Links
    // Improved algorithm for page number display
    const maxVisiblePages = 5; // Maximum page numbers to show
    
    // Calculate range of pages to display
    let startPage, endPage;
    
    if (totalPages <= maxVisiblePages) {
      // Show all pages if total pages is less than max visible
      startPage = 1;
      endPage = totalPages;
    } else {
      // Calculate start and end pages for large page counts
      const halfVisible = Math.floor(maxVisiblePages / 2);
      
      if (currentPage <= halfVisible + 1) {
        // Near the start
        startPage = 1;
        endPage = maxVisiblePages;
      } else if (currentPage >= totalPages - halfVisible) {
        // Near the end
        startPage = totalPages - maxVisiblePages + 1;
        endPage = totalPages;
      } else {
        // In the middle
        startPage = currentPage - halfVisible;
        endPage = currentPage + halfVisible;
      }
    }
    
    // First page link (if not already in range)
    if (startPage > 1) {
      paginationHTML += `<span class="pagination-item" onclick="return changePage(1)" aria-label="Go to page 1">1</span>`;
      if (startPage > 2) {
        paginationHTML += `<span class="pagination-ellipsis" aria-hidden="true">...</span>`;
      }
    }
    
    // Page numbers
    for (let i = startPage; i <= endPage; i++) {
      if (i === currentPage) {
        paginationHTML += `<span class="pagination-item active" aria-current="page" aria-label="Current page, page ${i}">${i}</span>`;
      } else {
        paginationHTML += `<span class="pagination-item" onclick="return changePage(${i})" aria-label="Go to page ${i}">${i}</span>`;
      }
    }
    
    // Last page link (if not already in range)
    if (endPage < totalPages) {
      if (endPage < totalPages - 1) {
        paginationHTML += `<span class="pagination-ellipsis" aria-hidden="true">...</span>`;
      }
      paginationHTML += `<span class="pagination-item" onclick="return changePage(${totalPages})" aria-label="Go to page ${totalPages}">${totalPages}</span>`;
    }

    // Next Page Button
    if (currentPage < totalPages) {
      paginationHTML += `<span class="pagination-nav" onclick="return changePage(${currentPage + 1})" aria-label="Go to next page">Next &raquo;</span>`;
    } else {
      paginationHTML += `<span class="pagination-nav disabled" aria-disabled="true">Next &raquo;</span>`;
    }

    paginationHTML += '</div>';
    return paginationHTML;
  }

  function changePage(page) {
    if (isPageLoading) return false;
    
    if (page >= 1 && page <= totalPages && page !== currentPage) {
      setPageLoading(true);
      currentPage = page;
      
      // Small delay to show loading state
      setTimeout(() => {
        displayCurrentPage();
        setPageLoading(false);
        
        // Scroll to top of posts container for better UX
        const postsContainer = document.getElementById("lit005-posts");
        if (postsContainer) {
          postsContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
        }
      }, 300);
    }
    return false; // Prevent default anchor behavior
  }

  function setupPostListeners() {
    // Add click handlers to post links
    const postLinks = document.querySelectorAll('.post-link');
    postLinks.forEach(link => {
      const postId = link.dataset.postId;
      const postIndex = link.dataset.index;
      
      link.addEventListener("click", function(e) {
        // Start timer when user clicks
        startPostTimer(postId, postIndex);
      });
    });
  }

  function startPostTimer(postId, postIndex) {
    // Cancel any existing timer for this post
    if (activeTimers[postId]) {
      clearTimeout(activeTimers[postId]);
    }
    
    // Start a new 15-second timer
    activeTimers[postId] = setTimeout(() => {
      markPostAsCompleted(postId, postIndex);
    }, 15000); // 15 seconds
  }

  function markPostAsCompleted(postId, postIndex) {
    // Mark post as completed in local storage using consistent naming
    localStorage.setItem(`lit005-completed-post-${postIndex}`, 'true');
    
    // No need to update link appearance as we only track via progress bar
    
    // Clear the timer reference
    delete activeTimers[postId];
    
    // Update progress bar
    updateProgressBarFromStorage();
  }

  function updateProgressBarFromStorage() {
    const totalPostsCount = parseInt(localStorage.getItem('lit002-total-posts') || '0', 10);
    let completedCount = 0;
    
    // Count completed posts from localStorage using consistent ID format
    for (let i = 0; i < totalPostsCount; i++) {
      if (localStorage.getItem(`lit002-completed-post-${i}`) === 'true') {
        completedCount++;
      }
    }
    
    // Update progress counter in localStorage
    localStorage.setItem('lit005-completed-count', completedCount);
    
    if (totalPostsCount > 0) {
      const progress = (completedCount / totalPostsCount) * 100;
      localStorage.setItem('lit005-progress-percentage', progress.toFixed(1));
      
      const progressBar = document.getElementById("progress-bar");
      const progressText = document.getElementById("progressPercentage");
      
      if (progressBar && progressText) {
        progressBar.style.width = `${progress}%`;
        progressText.textContent = `${Math.round(progress)}%`;
      }
    }
  }
</script>