1+ ( function ( ) {
2+ "use strict" ;
3+
4+ const sectionLabels = {
5+ title : "Start" ,
6+ introduction : "Intro" ,
7+ gender : "Gender" ,
8+ delegations : "Medals" ,
9+ "body-types" : "Bodies" ,
10+ geopolitics : "Politics" ,
11+ fair : "Fairness" ,
12+ conclusion : "Finish"
13+ } ;
14+
15+ const state = {
16+ sections : [ ] ,
17+ currentSectionIndex : 0 ,
18+ currentSlideIndex : 0
19+ } ;
20+
21+ function initRaceNavigation ( ) {
22+ const oldNav = document . getElementById ( "race-nav" ) ;
23+ if ( oldNav ) oldNav . remove ( ) ;
24+
25+ state . sections = Array . from ( document . querySelectorAll ( "#website > .section" ) )
26+ . filter ( section => ! section . classList . contains ( "fp-auto-height" ) )
27+ . map ( ( section , index , array ) => {
28+ const anchor = section . dataset . anchor || `section-${ index } ` ;
29+ const slides = Array . from ( section . querySelectorAll ( ".slide" ) ) ;
30+
31+ return {
32+ anchor,
33+ label : sectionLabels [ anchor ] || anchor ,
34+ hasSlides : slides . length > 1 ,
35+ slideCount : slides . length ,
36+ isStart : index === 0 ,
37+ isFinish : index === array . length - 1
38+ } ;
39+ } ) ;
40+
41+ const nav = document . createElement ( "div" ) ;
42+ nav . id = "race-nav" ;
43+
44+ nav . innerHTML = `
45+ <div class="race-track-shell">
46+ <div class="race-start">START</div>
47+
48+ <div class="race-track">
49+ <div class="race-track-line"></div>
50+ <div class="race-track-progress"></div>
51+ <div class="race-checkpoints"></div>
52+
53+ <div class="race-runner" aria-hidden="true">
54+ ${ runnerSvg ( ) }
55+ </div>
56+ </div>
57+
58+ <div class="race-finish-wrap" aria-hidden="true">
59+ <div class="race-finish-label">FINISH</div>
60+ </div>
61+ </div>
62+ ` ;
63+
64+ document . body . appendChild ( nav ) ;
65+
66+ buildCheckpoints ( ) ;
67+ updateRaceNavigation ( 0 , 0 , "down" ) ;
68+ }
69+
70+ function buildCheckpoints ( ) {
71+ const container = document . querySelector ( ".race-checkpoints" ) ;
72+ if ( ! container ) return ;
73+
74+ container . innerHTML = "" ;
75+
76+ state . sections . forEach ( ( section , index ) => {
77+ const checkpoint = document . createElement ( "button" ) ;
78+
79+ checkpoint . className = "race-checkpoint" ;
80+ checkpoint . type = "button" ;
81+ checkpoint . dataset . index = index ;
82+ checkpoint . style . top = `${ getProgressPercent ( index ) } %` ;
83+
84+ checkpoint . innerHTML = `
85+ <span class="race-dot"></span>
86+ ${ section . hasSlides ? `<span class="race-side-dot"></span>` : "" }
87+ <span class="race-label">${ section . label } </span>
88+ ` ;
89+
90+ checkpoint . addEventListener ( "click" , ( ) => {
91+ if ( window . fullpage_api ) {
92+ window . fullpage_api . moveTo ( index + 1 ) ;
93+ }
94+ } ) ;
95+
96+ container . appendChild ( checkpoint ) ;
97+ } ) ;
98+ }
99+
100+ function updateRaceNavigation ( sectionIndex = 0 , slideIndex = 0 , direction = "down" ) {
101+ const nav = document . getElementById ( "race-nav" ) ;
102+ if ( ! nav || ! state . sections . length ) return ;
103+
104+ const safeSectionIndex = clamp ( sectionIndex , 0 , state . sections . length - 1 ) ;
105+ const currentSection = state . sections [ safeSectionIndex ] ;
106+
107+ state . currentSectionIndex = safeSectionIndex ;
108+ state . currentSlideIndex = slideIndex ;
109+
110+ const progress = getProgressPercent ( safeSectionIndex ) ;
111+
112+ nav . style . setProperty ( "--race-progress" , `${ progress } %` ) ;
113+
114+ const runner = nav . querySelector ( ".race-runner" ) ;
115+ if ( runner ) {
116+ runner . style . top = `${ progress } %` ;
117+
118+ const shouldMoveRight =
119+ currentSection &&
120+ currentSection . hasSlides &&
121+ slideIndex > 0 ;
122+
123+ // left of vertical line by default, move right when on horizontal slide
124+ runner . style . setProperty (
125+ "--runner-x" ,
126+ shouldMoveRight ? "60px" : "-20px"
127+ ) ;
128+ }
129+
130+ nav . classList . toggle ( "race-is-start" , safeSectionIndex === 0 ) ;
131+ nav . classList . toggle ( "race-is-finish" , Boolean ( currentSection ?. isFinish ) ) ;
132+
133+ nav . classList . toggle ( "race-going-up" , direction === "up" ) ;
134+ nav . classList . toggle ( "race-going-down" , direction === "down" ) ;
135+ nav . classList . toggle ( "race-going-right" , direction === "right" ) ;
136+ nav . classList . toggle ( "race-going-left" , direction === "left" ) ;
137+
138+ nav . classList . add ( "race-is-moving" ) ;
139+
140+ window . clearTimeout ( nav . _raceTimer ) ;
141+ nav . _raceTimer = window . setTimeout ( ( ) => {
142+ nav . classList . remove ( "race-is-moving" ) ;
143+ } , 700 ) ;
144+
145+ updateCheckpoints ( safeSectionIndex , slideIndex ) ;
146+ }
147+
148+ function updateCheckpoints ( activeSectionIndex , activeSlideIndex ) {
149+ document . querySelectorAll ( ".race-checkpoint" ) . forEach ( ( checkpoint , index ) => {
150+ const section = state . sections [ index ] ;
151+
152+ checkpoint . classList . toggle ( "is-active" , index === activeSectionIndex ) ;
153+ checkpoint . classList . toggle ( "is-passed" , index < activeSectionIndex ) ;
154+
155+ const sideDot = checkpoint . querySelector ( ".race-side-dot" ) ;
156+
157+ if ( sideDot && section ) {
158+ const sidePassed =
159+ index < activeSectionIndex ||
160+ ( index === activeSectionIndex && activeSlideIndex > 0 ) ;
161+
162+ const sideActive =
163+ index === activeSectionIndex && activeSlideIndex > 0 ;
164+
165+ sideDot . classList . toggle ( "is-passed-side" , sidePassed ) ;
166+ sideDot . classList . toggle ( "is-active-side" , sideActive ) ;
167+ }
168+ } ) ;
169+ }
170+
171+ function getProgressPercent ( index ) {
172+ if ( state . sections . length <= 1 ) return 0 ;
173+ return ( index / ( state . sections . length - 1 ) ) * 100 ;
174+ }
175+
176+ function clamp ( value , min , max ) {
177+ return Math . min ( Math . max ( value , min ) , max ) ;
178+ }
179+
180+ function runnerSvg ( ) {
181+ return `
182+ <svg viewBox="0 0 80 80" class="runner-svg">
183+ <circle class="runner-head" cx="42" cy="14" r="7"></circle>
184+ <line class="runner-body" x1="42" y1="22" x2="37" y2="42"></line>
185+
186+ <line class="runner-arm runner-arm-front" x1="39" y1="28" x2="20" y2="36"></line>
187+ <line class="runner-arm runner-arm-back" x1="39" y1="28" x2="58" y2="34"></line>
188+
189+ <line class="runner-leg runner-leg-front" x1="37" y1="42" x2="20" y2="63"></line>
190+ <line class="runner-leg runner-leg-back" x1="37" y1="42" x2="59" y2="61"></line>
191+ </svg>
192+ ` ;
193+ }
194+
195+ window . RaceNavigation = {
196+ init : initRaceNavigation ,
197+ update : updateRaceNavigation
198+ } ;
199+
200+ if ( document . readyState === "loading" ) {
201+ document . addEventListener ( "DOMContentLoaded" , initRaceNavigation ) ;
202+ } else {
203+ initRaceNavigation ( ) ;
204+ }
205+ } ) ( ) ;
0 commit comments