File size: 3,911 Bytes
8f8f7dd
 
f9b076a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8f8f7dd
 
f9b076a
8f8f7dd
f9b076a
 
 
 
 
 
8f8f7dd
f9b076a
 
 
 
 
 
 
 
 
8f8f7dd
f9b076a
 
 
 
 
 
 
8f8f7dd
f9b076a
 
 
 
8f8f7dd
f9b076a
 
8f8f7dd
 
 
 
f9b076a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8f8f7dd
f9b076a
 
 
 
8f8f7dd
f9b076a
 
 
 
8f8f7dd
f9b076a
 
 
8f8f7dd
f9b076a
 
 
 
 
 
 
 
8f8f7dd
 
 
 
f9b076a
 
8f8f7dd
 
 
f9b076a
8f8f7dd
 
 
 
f9b076a
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 };