1(function($) { 2 var navbarHeight; 3 var initialised = false; 4 var navbarOffset; 5 6 function elOffset($el) { 7 return $el.offset().top - (navbarHeight + navbarOffset); 8 } 9 10 function scrollToHash(duringPageLoad) { 11 var elScrollToId = location.hash.replace(/^#/, ''); 12 var $el; 13 14 function doScroll() { 15 var offsetTop = elOffset($el); 16 window.scrollTo(window.pageXOffset || window.scrollX, offsetTop); 17 } 18 19 if (elScrollToId) { 20 $el = $(document.getElementById(elScrollToId)); 21 22 if (!$el.length) { 23 $el = $(document.getElementsByName(elScrollToId)); 24 } 25 26 if ($el.length) { 27 if (duringPageLoad) { 28 $(window).one('scroll', function() { 29 setTimeout(doScroll, 100); 30 }); 31 } else { 32 setTimeout(doScroll, 0); 33 } 34 } 35 } 36 } 37 38 function init(opts) { 39 if (initialised) { 40 return; 41 } 42 initialised = true; 43 navbarHeight = $('.navbar').height(); 44 navbarOffset = opts.navbarOffset; 45 46 // some browsers move the offset after changing location. 47 // also catch external links coming in 48 $(window).on("hashchange", scrollToHash.bind(null, false)); 49 $(scrollToHash.bind(null, true)); 50 } 51 52 $.catchAnchorLinks = function(options) { 53 var opts = $.extend({}, jQuery.fn.toc.defaults, options); 54 init(opts); 55 }; 56 57 $.fn.toc = function(options) { 58 var self = this; 59 var opts = $.extend({}, jQuery.fn.toc.defaults, options); 60 61 var container = $(opts.container); 62 var tocs = []; 63 var headings = $(opts.selectors, container); 64 var headingOffsets = []; 65 var activeClassName = 'active'; 66 var ANCHOR_PREFIX = "__anchor"; 67 var maxScrollTo; 68 var visibleHeight; 69 var headerHeight = 10; // so if the header is readable, its counted as shown 70 init(); 71 72 var scrollTo = function(e) { 73 e.preventDefault(); 74 var target = $(e.target); 75 if (target.prop('tagName').toLowerCase() !== "a") { 76 target = target.parent(); 77 } 78 var elScrollToId = target.attr('href').replace(/^#/, '') + ANCHOR_PREFIX; 79 var $el = $(document.getElementById(elScrollToId)); 80 81 var offsetTop = Math.min(maxScrollTo, elOffset($el)); 82 83 $('body,html').animate({ scrollTop: offsetTop }, 400, 'swing', function() { 84 location.hash = '#' + elScrollToId; 85 }); 86 87 $('a', self).removeClass(activeClassName); 88 target.addClass(activeClassName); 89 }; 90 91 var calcHadingOffsets = function() { 92 maxScrollTo = $("body").height() - $(window).height(); 93 visibleHeight = $(window).height() - navbarHeight; 94 headingOffsets = []; 95 headings.each(function(i, heading) { 96 var anchorSpan = $(heading).prev("span"); 97 var top = 0; 98 if (anchorSpan.length) { 99 top = elOffset(anchorSpan); 100 } 101 headingOffsets.push(top > 0 ? top : 0); 102 }); 103 } 104 105 //highlight on scroll 106 var timeout; 107 var highlightOnScroll = function(e) { 108 if (!tocs.length) { 109 return; 110 } 111 if (timeout) { 112 clearTimeout(timeout); 113 } 114 timeout = setTimeout(function() { 115 var top = $(window).scrollTop(), 116 highlighted; 117 for (var i = headingOffsets.length - 1; i >= 0; i--) { 118 var isActive = tocs[i].hasClass(activeClassName); 119 // at the end of the page, allow any shown header 120 if (isActive && headingOffsets[i] >= maxScrollTo && top >= maxScrollTo) { 121 return; 122 } 123 // if we have got to the first heading or the heading is the first one visible 124 if (i === 0 || (headingOffsets[i] + headerHeight >= top && (headingOffsets[i - 1] + headerHeight <= top))) { 125 // in the case that a heading takes up more than the visible height e.g. we are showing 126 // only the one above, highlight the one above 127 if (i > 0 && headingOffsets[i] - visibleHeight >= top) { 128 i--; 129 } 130 $('a', self).removeClass(activeClassName); 131 if (i >= 0) { 132 highlighted = tocs[i].addClass(activeClassName); 133 opts.onHighlight(highlighted); 134 } 135 break; 136 } 137 } 138 }, 50); 139 }; 140 if (opts.highlightOnScroll) { 141 $(window).bind('scroll', highlightOnScroll); 142 $(window).bind('load resize', function() { 143 calcHadingOffsets(); 144 highlightOnScroll(); 145 }); 146 } 147 148 return this.each(function() { 149 //build TOC 150 var el = $(this); 151 var ul = $('<div class="list-group">'); 152 153 headings.each(function(i, heading) { 154 var $h = $(heading); 155 156 var anchor = $('<span/>').attr('id', opts.anchorName(i, heading, opts.prefix) + ANCHOR_PREFIX).insertBefore($h); 157 158 var span = $('<span/>')
159 .text(opts.headerText(i, heading, $h)); 160 161 //build TOC item 162 var a = $('<a class="list-group-item"/>') 163 .append(span) 164 .attr('href', '#' + opts.anchorName(i, heading, opts.prefix)) 165 .bind('click', function(e) { 166 scrollTo(e); 167 el.trigger('selected', $(this).attr('href')); 168 }); 169 170 span.addClass(opts.itemClass(i, heading, $h, opts.prefix)); 171 172 tocs.push(a); 173 174 ul.append(a); 175 }); 176 el.html(ul); 177 178 calcHadingOffsets(); 179 }); 180}; 181 182 183jQuery.fn.toc.defaults = { 184 container: 'body', 185 selectors: 'h1,h2,h3', 186 smoothScrolling: true, 187 prefix: 'toc', 188 onHighlight: function() {}, 189 highlightOnScroll: true, 190 navbarOffset: 0, 191 anchorName: function(i, heading, prefix) { 192 return prefix+i; 193 }, 194 headerText: function(i, heading, $heading) { 195 return $heading.text(); 196 }, 197 itemClass: function(i, heading, $heading, prefix) { 198 return prefix + '-' + $heading[0].tagName.toLowerCase(); 199 } 200 201}; 202 203})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.