@@ -156,6 +156,108 @@ function init() {
156156 sidebarViewModel . selectedPageName = window . docObject . name ;
157157 }
158158
159+ // Set up the client-side TOC
160+ var tocContainer = document . querySelector ( "#toc-sidebar nav" ) ;
161+ var oldToc = document . querySelector ( "bit-toc" ) ;
162+ if ( oldToc ) {
163+ tocContainer . removeChild ( oldToc ) ;
164+ }
165+ var newToc = document . createElement ( "bit-toc" ) ;
166+ newToc . depth = parseInt ( window . docObject . outline , 10 ) || 1 ;
167+ newToc . headingsContainerSelector = "body" ;
168+ newToc . scrollSelector = "#toc-sidebar" ;
169+ newToc . highlight = function ( ) {
170+ var articleRect = this . article . getBoundingClientRect ( ) ;
171+ var buttons = this . buttons ;
172+ var positions = this . titles . map ( function ( header , i ) {
173+ return {
174+ header : header ,
175+ rect : header . getBoundingClientRect ( ) ,
176+ button : buttons [ i ]
177+ } ;
178+ } ) ;
179+ positions . push ( { rect : { top : articleRect . top + this . article . scrollHeight - this . article . scrollTop } } ) ;
180+ positions . slice ( 0 , positions . length - 1 ) . forEach ( function ( position , index ) {
181+ position . button . classList . remove ( 'completed' , 'active' ) ;
182+ var curRect = position . rect ;
183+ var curDistance = curRect . top ; // was - articleRect.top
184+ var nextRect = positions [ index + 1 ] . rect ;
185+ var nextDistance = nextRect . top ; // was - articleRect.top
186+ if ( nextDistance >= 0 && nextDistance <= articleRect . height && curDistance >= 0 && curDistance <= articleRect . height ) {
187+ var lastPosition = positions [ index - 1 ] ;
188+ if ( lastPosition ) {
189+ lastPosition . button . classList . add ( 'completed' ) ;
190+ }
191+ position . button . classList . add ( 'active' ) ;
192+ } else if ( nextDistance < articleRect . height / 2 ) {
193+ position . button . classList . add ( 'completed' ) ;
194+ } else if ( nextDistance >= articleRect . height / 2 && curDistance < articleRect . height / 2 ) {
195+ var lastPosition = positions [ index - 1 ] ;
196+ if ( lastPosition ) {
197+ lastPosition . button . classList . add ( 'completed' ) ;
198+ }
199+ position . button . classList . add ( 'active' ) ;
200+ }
201+ } ) ;
202+
203+ // Get the last element in the nav that’s highlighted
204+ var activeOrCompleted = this . querySelectorAll ( ".active,.completed" ) ;
205+ var lastActiveOrCompleted = activeOrCompleted [ activeOrCompleted . length - 1 ] ;
206+ if ( lastActiveOrCompleted ) {
207+ lastActiveOrCompleted = lastActiveOrCompleted . querySelector ( 'a' ) ;
208+
209+ // Check to see if it’s in viewport
210+ var lastActiveOrCompletedRect = lastActiveOrCompleted . getBoundingClientRect ( ) ;
211+ var sidebarElement = this . outlineScrollElement ;
212+ var topInset = sidebarElement . getBoundingClientRect ( ) . top ; // Main nav height
213+ var viewportHeight = window . innerHeight ;
214+ var lastActiveOrCompletedRectIsInViewport = (
215+ lastActiveOrCompletedRect . bottom <= viewportHeight &&
216+ lastActiveOrCompletedRect . left >= 0 &&
217+ lastActiveOrCompletedRect . left <= window . innerWidth &&
218+ lastActiveOrCompletedRect . top >= topInset &&
219+ lastActiveOrCompletedRect . top <= viewportHeight
220+ ) ;
221+ if ( lastActiveOrCompletedRectIsInViewport === false ) {
222+ // Scroll the sidebar so the highlighted element is in the viewport
223+ var visibleSidebarHeight = sidebarElement . offsetHeight ; // Not the entire height, just what’s visible in the viewport
224+ var amountScrolledDownSidebar = sidebarElement . scrollTop ;
225+ var additionalScrollAmount = lastActiveOrCompletedRect . top - viewportHeight ;
226+ var amountToScroll = topInset + ( visibleSidebarHeight / 2 ) + additionalScrollAmount + amountScrolledDownSidebar ;
227+ sidebarElement . scrollTop = amountToScroll ;
228+ }
229+ }
230+ } ;
231+ newToc . setupHighlighting = function ( ) {
232+
233+ // Add a class to li elements with a ul element inside them
234+ tocContainer . querySelectorAll ( "li > ul" ) . forEach ( function ( childUl ) {
235+ childUl . parentElement . classList . add ( "nested" ) ;
236+ } ) ;
237+
238+ // Highlighting
239+ this . article = document . querySelector ( this . containerSelector ) ;
240+ var highlight = debounce ( this . highlight . bind ( this ) , 1 ) ;
241+ window . addEventListener ( "scroll" , highlight ) ;
242+ this . teardowns . push ( function ( ) {
243+ window . removeEventListener ( "scroll" , highlight ) ;
244+ } . bind ( this ) ) ;
245+ this . highlight ( ) ;
246+ } ;
247+ tocContainer . appendChild ( newToc ) ;
248+
249+ // Show the “On this page” title
250+ var onThisPage = document . querySelector ( "#toc-sidebar h1" ) ;
251+ onThisPage . classList . remove ( "hide" ) ;
252+
253+ // After the TOC loads, determine whether the “On this page” title should show
254+ setTimeout ( function ( ) {
255+ if ( tocContainer . contains ( newToc ) === false ) {
256+ // Hide the “On this page” title
257+ onThisPage . classList . add ( "hide" ) ;
258+ }
259+ } ) ;
260+
159261 hasShownSearch = true ;
160262}
161263
@@ -277,8 +379,9 @@ function navigate(href, updateLocation) {
277379 window . location . reload ( ) ;
278380 }
279381
280- // Scroll to the top of the page
382+ // Scroll to the top of the page & TOC sidebar
281383 setPageScrollTop ( 0 ) ;
384+ $ ( '#toc-sidebar' ) . scrollTop ( 0 ) ;
282385
283386 var $article = $content . find ( "article" ) ;
284387 var currentPage = $content . filter ( "#everything" ) . attr ( "data-current-page" ) ;
0 commit comments