| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 | (function($) {  var navbarHeight;  var initialised = false;  var navbarOffset;  function elOffset($el) {    return $el.offset().top - (navbarHeight + navbarOffset);  }  function scrollToHash(duringPageLoad) {    var elScrollToId = location.hash.replace(/^#/, '');    var $el;    function doScroll() {      var offsetTop = elOffset($el);      window.scrollTo(window.pageXOffset || window.scrollX, offsetTop);    }    if (elScrollToId) {      $el = $(document.getElementById(elScrollToId));      if (!$el.length) {        $el = $(document.getElementsByName(elScrollToId));      }      if ($el.length) {        if (duringPageLoad) {          $(window).one('scroll', function() {            setTimeout(doScroll, 100);          });        } else {          setTimeout(doScroll, 0);        }      }    }  }  function init(opts) {    if (initialised) {      return;    }    initialised = true;    navbarHeight = $('.navbar').height();    navbarOffset = opts.navbarOffset;    // some browsers move the offset after changing location.    // also catch external links coming in    $(window).on("hashchange", scrollToHash.bind(null, false));    $(scrollToHash.bind(null, true));  }  $.catchAnchorLinks = function(options) {    var opts = $.extend({}, jQuery.fn.toc.defaults, options);    init(opts);  };  $.fn.toc = function(options) {    var self = this;    var opts = $.extend({}, jQuery.fn.toc.defaults, options);    var container = $(opts.container);    var tocs = [];    var headings = $(opts.selectors, container);    var headingOffsets = [];    var activeClassName = 'active';    var ANCHOR_PREFIX = "__anchor";    var maxScrollTo;    var visibleHeight;    var headerHeight = 10; // so if the header is readable, its counted as shown    init();    var scrollTo = function(e) {      e.preventDefault();      var target = $(e.target);      if (target.prop('tagName').toLowerCase() !== "a") {        target = target.parent();      }      var elScrollToId = target.attr('href').replace(/^#/, '') + ANCHOR_PREFIX;      var $el = $(document.getElementById(elScrollToId));      var offsetTop = Math.min(maxScrollTo, elOffset($el));      $('body,html').animate({ scrollTop: offsetTop }, 400, 'swing', function() {        location.hash = '#' + elScrollToId;      });      $('a', self).removeClass(activeClassName);      target.addClass(activeClassName);    };    var calcHadingOffsets = function() {      maxScrollTo = $("body").height() - $(window).height();      visibleHeight = $(window).height() - navbarHeight;      headingOffsets = [];      headings.each(function(i, heading) {        var anchorSpan = $(heading).prev("span");        var top = 0;        if (anchorSpan.length) {          top = elOffset(anchorSpan);        }        headingOffsets.push(top > 0 ? top : 0);      });    }    //highlight on scroll    var timeout;    var highlightOnScroll = function(e) {      if (!tocs.length) {        return;      }      if (timeout) {        clearTimeout(timeout);      }      timeout = setTimeout(function() {        var top = $(window).scrollTop(),          highlighted;        for (var i = headingOffsets.length - 1; i >= 0; i--) {          var isActive = tocs[i].hasClass(activeClassName);          // at the end of the page, allow any shown header          if (isActive && headingOffsets[i] >= maxScrollTo && top >= maxScrollTo) {            return;          }          // if we have got to the first heading or the heading is the first one visible          if (i === 0 || (headingOffsets[i] + headerHeight >= top && (headingOffsets[i - 1] + headerHeight <= top))) {            // in the case that a heading takes up more than the visible height e.g. we are showing            // only the one above, highlight the one above            if (i > 0 && headingOffsets[i] - visibleHeight >= top) {              i--;            }            $('a', self).removeClass(activeClassName);            if (i >= 0) {              highlighted = tocs[i].addClass(activeClassName);              opts.onHighlight(highlighted);            }            break;          }        }      }, 50);    };    if (opts.highlightOnScroll) {      $(window).bind('scroll', highlightOnScroll);      $(window).bind('load resize', function() {        calcHadingOffsets();        highlightOnScroll();      });    }    return this.each(function() {      //build TOC      var el = $(this);      var ul = $('<div class="list-group">');      headings.each(function(i, heading) {        var $h = $(heading);        var anchor = $('<span/>').attr('id', opts.anchorName(i, heading, opts.prefix) + ANCHOR_PREFIX).insertBefore($h);        var span = $('<span/>')          .text(opts.headerText(i, heading, $h));        //build TOC item        var a = $('<a class="list-group-item"/>')          .append(span)          .attr('href', '#' + opts.anchorName(i, heading, opts.prefix))          .bind('click', function(e) {            scrollTo(e);            el.trigger('selected', $(this).attr('href'));          });        span.addClass(opts.itemClass(i, heading, $h, opts.prefix));        tocs.push(a);        ul.append(a);      });      el.html(ul);      calcHadingOffsets();    });};jQuery.fn.toc.defaults = {  container: 'body',  selectors: 'h1,h2,h3',  smoothScrolling: true,  prefix: 'toc',  onHighlight: function() {},  highlightOnScroll: true,  navbarOffset: 0,  anchorName: function(i, heading, prefix) {    return prefix+i;  },  headerText: function(i, heading, $heading) {    return $heading.text();  },  itemClass: function(i, heading, $heading, prefix) {    return prefix + '-' + $heading[0].tagName.toLowerCase();  }};})(jQuery);
 |