AutoTOC

Automatically creates a table of contents for all HTML-headers on a web page.

Tính đến 29-01-2020. Xem phiên bản mới nhất.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

Bạn sẽ cần cài đặt một tiện ích mở rộng như Tampermonkey hoặc Violentmonkey để cài đặt kịch bản này.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(Tôi đã có Trình quản lý tập lệnh người dùng, hãy cài đặt nó!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         AutoTOC
// @namespace    http://runeskaug.com/greasemonkey
// @description  Automatically creates a table of contents for all HTML-headers on a web page.
// @author       Rune Skaug ([email protected]), IzzySoft
// @license      CC BY (https://creativecommons.org/licenses/by/2.5/)
// @include      http://*
// @include      https://*
// @version      1.8
// @grant        GM_registerMenuCommand
// @grant        GM_addStyle
// @homepageURL  https://codeberg.org/izzy/userscripts
// ==/UserScript==

(function() {
  // text constants
  var fullTOCText = "Table of Contents";
  var hideBtnText = "\u00a0X\u00a0";
  var RXmatch = /^h[1-4]$/i; // regexp
  var XPmatch = "//h1|//h2|//h3|//h4"; // xpath
  //set the optional behaviour of the TOC box
  // if true, resets it to its initial state after you have selected a header - false does not reset it
  var resetSelect = true;
  // if true, shows a "Hide TOC"/close button on the left side of the bar
  var showHide = true;
  // if true, hides the TOC for all pages on current site
  var useCookie = true;
  // if true, adds menu item (Toggle TOC)
  var addMenuItem = true;

  function f() {
    //only on (X)HTML pages containing at least one heading - excludes XML files, text files, plugins and images (displayed using minimal HTML)
    if (document.getElementsByTagName("html").length && ( document.getElementsByTagName('h1').length || 
                                                          document.getElementsByTagName('h2').length || 
                                                          document.getElementsByTagName('h3').length || 
                                                          document.getElementsByTagName('h4').length ) 
                                                     && (!useCookie || (useCookie && getCookie('autotoc_hide')!='true'))) {
      var aHs = getHTMLHeadings();
      if (aHs.length>1) { // HTML document, more than one heading.
        var body = document.getElementsByTagName('body')[0];
        body.style.marginBottom = "24px !important";
        // Browser sniff++ - due to rendering bug(s) in FF1.0
        var toc = document.createElement(window.opera||showHide?'tocdiv':'div');
        toc.id = 'js-toc';
        if (showHide) {
          var hideDiv = document.createElement('div');
          hideDiv.setAttribute('class','hideBtn');
          var hideLink = document.createElement('a');
          hideLink.setAttribute("href","#");
          hideLink.addEventListener("click",function(){if(useCookie){document.getElementById('js-toc').style.display='none';document.cookie='autotoc_hide=true; path=/';return false;}else{document.getElementById('js-toc').style.display='none';}});
          hideLink.appendChild(document.createTextNode(hideBtnText));
          hideDiv.appendChild(hideLink);
          toc.appendChild(hideDiv);
        }
        tocSelect = document.createElement('select');
        tocSelect.addEventListener("change", function(){gotoAnchor(this)});
        tocSelect.id = 'toc-select';
        tocEmptyOption = document.createElement('option');
        tocEmptyOption.setAttribute('value','');
        tocEmptyOption.appendChild(document.createTextNode(fullTOCText));
        tocSelect.appendChild(tocEmptyOption);
        toc.appendChild(tocSelect);
        document.body.appendChild(toc);  
        for (var i=0,aH;aH=aHs[i];i++) {
          if (aH.offsetWidth) {
            op  = document.createElement("option");
            op.appendChild(document.createTextNode(gs(aH.tagName)+getInnerText(aH).substring(0,100)));
            var refID = aH.id ? aH.id : aH.tagName+'-'+(i*1+1);
            op.setAttribute("value", refID);
            document.getElementById("toc-select").appendChild(op);
            aH.id = refID;
          }
        }
        GM_addStyle('@media print { #js-toc {display: none; visibility: hidden; }}\n'+
                    '@media screen { #js-toc {position: fixed; left: 0; right: 0; top: auto; bottom: 0; width: 100%; display: block; border-top: 1px solid #777; background: #ddd; margin: 0; padding: 3px; z-index: 9999; }\n'+
                    '#js-toc select { font: 8pt verdana, sans-serif; margin: 0; margin-left:5px; background: #fff; color: #000; float: left; padding: 0; vertical-align: bottom;}\n'+
                    '#js-toc option { font: 8pt verdana, sans-serif; color: #000; }\n'+
                    '#js-toc .hideBtn { font: bold 8pt verdana, sans-serif !important; float: left; margin-left: 2px; margin-right: 2px; padding: 1px; border: 1px solid #999; background: #e7e7e7; }\n'+
                    '#js-toc .hideBtn a { color: #333; text-decoration: none; background: transparent;} #js-toc .hideBtn a:hover { color: #333; text-decoration: none; background: transparent;}\n'+
                    '#js-toc:not(:hover) { height: 2px !important; width: 5px !important; border-radius: 5px !important; background-color: #00f !important; }'
                   );
      }
    }
  };

  function autoTOC_toggleDisplay() {
    if (document.getElementById('js-toc')) {
      // toc-bar exists
      if (document.getElementById('js-toc').style.display == 'none') {
        document.getElementById('js-toc').style.display = 'block'; 
        if (useCookie) {document.cookie = 'autotoc_hide=; path=/';}
      }
      else {
        document.getElementById('js-toc').style.display = 'none'; 
        if (useCookie) {document.cookie = 'autotoc_hide=true; path=/';}
      };
    } else {
      // toc-bar not created yet, clear hide-cookie and run main script
      if (useCookie) {document.cookie = 'autotoc_hide=; path=/';}
      f();
    }
  }

  function flash(el,rep,delay) {
    for (var i=rep;i>0;i--) {
      window.setTimeout(function(){el.style.background="#ff7";},delay*i*2);
      window.setTimeout(function(){el.style.background=elbg;},delay*((i*2)+1));
    }
  }
  function gotoAnchor(selectEl) {
    if(selectEl) {
      el = document.getElementById(selectEl.value);
      elbg = el.style.background;
      location.href = '#' + selectEl.value;
      flash(el,5,100);
      if ( resetSelect ) { selectEl.selectedIndex = 0; }
    }
  }

  function getHTMLHeadings() {
    function acceptNode(node) {
      if (node.tagName.match(RXmatch)) { if (node.value+''!='') { return NodeFilter.FILTER_ACCEPT; } }
      return NodeFilter.FILTER_SKIP;
    }
    outArray = new Array();
    // XPath
    if (document.evaluate) {
      var nodes = document.evaluate(XPmatch, document, null, XPathResult.ANY_TYPE, null);
      var thisHeading = nodes.iterateNext();
      var j = 0;
      while (thisHeading) {
        if (thisHeading.textContent+''!='') {
          outArray[j++] = thisHeading;
        }
        thisHeading = nodes.iterateNext();
      }
    }
    // document.getElementsByTagName - slow! :)
    else {
      var els = document.getElementsByTagName("*");
      var j = 0;
      for (var i=0,el;el=els[i];i++) {
        if (el.tagName.match(RXmatch)) outArray[j++] = el;
      }
    }
    return outArray;
  }
  function gs(s){
    s = s.toLowerCase();
    var ret = "";
    for (var i=1; i<(s.substring(1)*1);i++) {
      ret = ret + "\u00a0 \u00a0 ";
    }
    return ret;
  }
  function getInnerText(el) {
    var s='';
    for (var i=0,node; node=el.childNodes[i]; i++) {
      if (node.nodeType == 1) s += getInnerText(node); 
      else if (node.nodeType == 3) s += node.nodeValue;
    }
    return s;
  }
  function getCookie(cname)
  {
    var namesep = cname + "=";
    var ca = document.cookie.split(';');
    for(var i=0, c; c=ca[i]; i++)
    {
        c = c.replace(/^\s*|\s*$/g,"");
        if (c.indexOf(namesep) == 0) {
            return c.substring(namesep.length,c.length);
        }
    }
    return null;
  }
  // main()
  if (!window.opera && addMenuItem) {
    GM_registerMenuCommand('AutoTOC: Toggle display', autoTOC_toggleDisplay);
  }
  f();
})();