File size: 3,911 Bytes
9f7c465
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
const queryArg = "section";

function syncHFSpacesURLHash() {
  // Handle explicit section requests (don't update hash automatically on load)
  const hasExplicitRequest = handleExplicitSectionRequest();
  
  // Set up hash change monitoring
  updateHashBasedOnHashChange();
  
  // Always set up scroll monitoring to update hash during scrolling
  setupScrollMonitoring();
  
  // If no explicit request, we don't update the hash on initial load
  // The hash will only start updating when the user scrolls
}

function handleExplicitSectionRequest() {
  // Check for section parameter in URL
  const urlParams = new URLSearchParams(window.location.search);
  const sectionId = urlParams.get(queryArg);
  
  // If we have an explicit section request
  if (sectionId) {
    const targetElement = document.getElementById(sectionId);
    if (targetElement) {
      // Slight delay to ensure the browser doesn't try to do its own scrolling first
      setTimeout(() => {
        targetElement.scrollIntoView();
        history.replaceState(null, null, `#${sectionId}`);
      }, 100);
    }
    return true;
  }
  
  // No explicit section parameter found
  return false;
}

function setupScrollMonitoring() {
  // Variables to manage throttling
  let isScrolling = false;
  let lastKnownScrollPosition = 0;
  let initialScroll = true;
  
  // Add the scroll event listener
  window.addEventListener('scroll', function() {
    lastKnownScrollPosition = window.scrollY;
    
    if (!isScrolling) {
      window.requestAnimationFrame(function() {
        // Skip the first scroll event which might be browser's automatic scroll
        // to a hash on page load
        if (initialScroll) {
          initialScroll = false;
        } else {
          updateHashBasedOnScroll(lastKnownScrollPosition);
        }
        isScrolling = false;
      });
    }
    
    isScrolling = true;
  });
}

// Function to update the URL hash based on scroll position
function updateHashBasedOnScroll(scrollPosition) {
  const closestHeading = findClosestHeading(scrollPosition);
  
  // Update the URL hash if we found a closest element
  if (closestHeading && closestHeading.id) {
    // Only update if the hash is different to avoid unnecessary operations
    if (window.location.hash !== `#${closestHeading.id}`) {
      silentlyUpdateHash(closestHeading.id);
      postMessageToHFSpaces(closestHeading.id);
    }
  }
}

// Find the closest heading to the current scroll position
function findClosestHeading(scrollPosition) {
  // Get only heading elements with IDs that we want to track
  const headingsWithIds = Array.from(document.querySelectorAll('h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]'));

  // Skip if there are no headings with IDs
  if (headingsWithIds.length === 0) return null;

  // Find the element closest to the middle of the viewport
  let closestHeading = null;
  let closestDistance = Infinity;
  const viewportMiddle = scrollPosition + window.innerHeight / 2;
  
  // Iterate through all headings to find the closest one
  headingsWithIds.forEach(heading => {
    const headingTop = heading.getBoundingClientRect().top + scrollPosition;
    const distance = Math.abs(headingTop - viewportMiddle);
    
    if (distance < closestDistance) {
      closestDistance = distance;
      closestHeading = heading;
    }
  });
  
  return closestHeading;
}

// Update hash without triggering scroll or other side effects
function silentlyUpdateHash(id) {
  history.replaceState(null, null, `#${id}`);
}

function updateHashBasedOnHashChange() {
  window.addEventListener('hashchange', () => {
    const elementId = window.location.hash.slice(1);
    postMessageToHFSpaces(elementId);
  });
}

function postMessageToHFSpaces(elementId) {
  const parentOrigin = "https://huggingface.co";
  window.parent.postMessage({ queryString: `${queryArg}=${elementId}` }, parentOrigin);
}

export { syncHFSpacesURLHash };