AutoTOC

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

当前为 2020-01-20 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name AutoTOC
  3. // @namespace http://runeskaug.com/greasemonkey
  4. // @description Automatically creates a table of contents for all HTML-headers on a web page.
  5. // @author Rune Skaug (greasemonkey@runeskaug.com)
  6. // @include http://*
  7. // @include https://*
  8. // @version 1.7.66
  9. // @grant GM_registerMenuCommand
  10. // @homepageURL https://codeberg.org/izzy/userscripts
  11. // @released 2005-07-06
  12. // @updated 2020-01-20
  13. // @compatible Greasemonkey, Opera 8/9
  14. //
  15. // Change history (main changes):
  16. //
  17. // 1.0 - Basic version
  18. // 1.1 - Adds Hide TOC button
  19. // 1.2 - Only show visible headings (Firefox)
  20. // - Work around Firefox rendering bugs
  21. // - Adds Menu item: AutoTOC: Toggle display (Firefox)
  22. // 1.3 - Sets a session cookie for TOC hiding (per domain)
  23. // 1.4 - Disables adding of menu item.
  24. // - Choose your own string pattern to match (RXmatch)
  25. // 1.5 - Minor adjustments for GM 0.6/FF1.5
  26. // - Moved closebutton to the left
  27. // - Flash on select
  28. // 1.6 - Xpath search replaces treewalker, FF1,1.5,Opera9
  29. // 1.7 - Minor fixes, screen-only stylesheet
  30. // 1.7.66 - integrated my previously separate UserStyle to hide the TOC unless hovered (Izzy)
  31. //
  32. // @ujs:category browser: enhancements
  33. // @ujs:published 2005-07-06 13:03
  34. // @ujs:modified 2005-07-06 17:31
  35. // @ujs:documentation http://userjs.org/scripts/browser/enhancements/auto-toc
  36. // @ujs:download http://userjs.org/scripts/download/browser/enhancements/auto-toc.user.js
  37. // @ujs:download.gm http://userjs.org/scripts/download/browser/enhancements/auto-toc.user.js
  38. // ==/UserScript==
  39.  
  40.  
  41. /*
  42. * Creative Commons Attribution License
  43. * http://creativecommons.org/licenses/by/2.5/
  44. */
  45.  
  46. (function() {
  47. // text constants
  48. var fullTOCText = "Table of Contents";
  49. var hideBtnText = "\u00a0X\u00a0";
  50. var RXmatch = /^h[1-4]$/i; // regexp
  51. var XPmatch = "//h1|//h2|//h3|//h4"; // xpath
  52. //set the optional behaviour of the TOC box
  53. // if true, resets it to its initial state after you have selected a header - false does not reset it
  54. var resetSelect = true;
  55. // if true, shows a "Hide TOC"/close button on the left side of the bar
  56. var showHide = true;
  57. // if true, hides the TOC for all pages on current site
  58. var useCookie = true;
  59. // if true, adds menu item (Toggle TOC)
  60. var addMenuItem = true;
  61.  
  62. function f() {
  63. //only on (X)HTML pages containing at least one heading - excludes XML files, text files, plugins and images (displayed using minimal HTML)
  64. if (document.getElementsByTagName("html").length && ( document.getElementsByTagName('h1').length ||
  65. document.getElementsByTagName('h2').length ||
  66. document.getElementsByTagName('h3').length ||
  67. document.getElementsByTagName('h4').length )
  68. && (!useCookie || (useCookie && getCookie('autotoc_hide')!='true'))) {
  69. var aHs = getHTMLHeadings();
  70. if (aHs.length>1) { // HTML document, more than one heading.
  71. var body = document.getElementsByTagName('body')[0];
  72. body.style.marginBottom = "24px !important";
  73. addCSS(
  74. '@media print { #js-toc {display: none; visibility: hidden; }}\n'+
  75. '@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'+
  76. '#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'+
  77. '#js-toc option { font: 8pt verdana, sans-serif; color: #000; }\n'+
  78. '#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'+
  79. '#js-toc .hideBtn a { color: #333; text-decoration: none; background: transparent;} #js-toc .hideBtn a:hover { color: #333; text-decoration: none; background: transparent;}\n'+
  80. '#js-toc:not(:hover) { height: 0px !important; width: 0px !important; -moz-border-radius: 5px !important; background-color: #00f !important; } }'
  81. );
  82. // Browser sniff++ - due to rendering bug(s) in FF1.0
  83. var toc = document.createElement(window.opera||showHide?'tocdiv':'div');
  84. toc.id = 'js-toc';
  85. if (showHide) {
  86. var hideDiv = document.createElement('div');
  87. hideDiv.setAttribute('class','hideBtn');
  88. var hideLink = document.createElement('a');
  89. hideLink.setAttribute("href","#");
  90. hideLink.setAttribute("onclick",useCookie?"document.getElementById('js-toc').style.display = 'none'; document.cookie = 'autotoc_hide=true; path=/'; return false;":"document.getElementById('js-toc').style.display = 'none';");
  91. hideLink.appendChild(document.createTextNode(hideBtnText));
  92. hideDiv.appendChild(hideLink);
  93. toc.appendChild(hideDiv);
  94. }
  95. tocSelect = document.createElement('select');
  96. tocSelect.setAttribute("onchange", "if(this.value){function flash(rep,delay) { for (var i=rep;i>0;i--) {window.setTimeout('el.style.background=\"#ff7\";',delay*i*2);window.setTimeout('el.style.background=elbg',delay*((i*2)+1));};}; elid = this.value; el=document.getElementById(elid); elbg=el.style.background; location.href='#'+elid; flash(5,100);"+(resetSelect?"this.selectedIndex=0;}":"}"));
  97. tocSelect.id = 'toc-select';
  98. tocEmptyOption = document.createElement('option');
  99. tocEmptyOption.setAttribute('value','');
  100. tocEmptyOption.appendChild(document.createTextNode(fullTOCText));
  101. tocSelect.appendChild(tocEmptyOption);
  102. toc.appendChild(tocSelect);
  103. document.body.appendChild(toc);
  104. for (var i=0,aH;aH=aHs[i];i++) {
  105. if (aH.offsetWidth) {
  106. op = document.createElement("option");
  107. op.appendChild(document.createTextNode(gs(aH.tagName)+getInnerText(aH).substring(0,100)));
  108. var refID = aH.id ? aH.id : aH.tagName+'-'+(i*1+1);
  109. op.setAttribute("value", refID);
  110. document.getElementById("toc-select").appendChild(op);
  111. aH.id = refID;
  112. }
  113. }
  114. }
  115. }
  116. };
  117.  
  118. function autoTOC_toggleDisplay() {
  119. if (document.getElementById('js-toc')) {
  120. // toc-bar exists
  121. if (document.getElementById('js-toc').style.display == 'none') {
  122. document.getElementById('js-toc').style.display = 'block';
  123. if (useCookie) {document.cookie = 'autotoc_hide=; path=/';}
  124. }
  125. else {
  126. document.getElementById('js-toc').style.display = 'none';
  127. if (useCookie) {document.cookie = 'autotoc_hide=true; path=/';}
  128. };
  129. } else {
  130. // toc-bar not created yet, clear hide-cookie and run main script
  131. if (useCookie) {document.cookie = 'autotoc_hide=; path=/';}
  132. f();
  133. }
  134. }
  135.  
  136. function getHTMLHeadings() {
  137. function acceptNode(node) {
  138. if (node.tagName.match(RXmatch)) { if (node.value+''!='') { return NodeFilter.FILTER_ACCEPT; } }
  139. return NodeFilter.FILTER_SKIP;
  140. }
  141. outArray = new Array();
  142. // XPath
  143. if (document.evaluate) {
  144. var nodes = document.evaluate(XPmatch, document, null, XPathResult.ANY_TYPE, null);
  145. var thisHeading = nodes.iterateNext();
  146. var j = 0;
  147. while (thisHeading) {
  148. if (thisHeading.textContent+''!='') {
  149. outArray[j++] = thisHeading;
  150. }
  151. thisHeading = nodes.iterateNext();
  152. }
  153. }
  154. // document.getElementsByTagName - slow! :)
  155. else {
  156. var els = document.getElementsByTagName("*");
  157. var j = 0;
  158. for (var i=0,el;el=els[i];i++) {
  159. if (el.tagName.match(RXmatch)) outArray[j++] = el;
  160. }
  161. }
  162. return outArray;
  163. }
  164. function addCSS(css) {
  165. var head, styleLink;
  166. head = document.getElementsByTagName('head')[0];
  167. if (!head) { return; }
  168. styleLink = document.createElement('link');
  169. styleLink.setAttribute('rel','stylesheet');
  170. styleLink.setAttribute('type','text/css');
  171. styleLink.setAttribute('href','data:text/css,'+escape(css));
  172. head.appendChild(styleLink);
  173. }
  174. function gs(s){
  175. s = s.toLowerCase();
  176. var ret = "";
  177. for (var i=1; i<(s.substring(1)*1);i++) {
  178. ret = ret + "\u00a0 \u00a0 ";
  179. }
  180. return ret;
  181. }
  182. function getInnerText(el) {
  183. var s='';
  184. for (var i=0,node; node=el.childNodes[i]; i++) {
  185. if (node.nodeType == 1) s += getInnerText(node);
  186. else if (node.nodeType == 3) s += node.nodeValue;
  187. }
  188. return s;
  189. }
  190. function getCookie(cname)
  191. {
  192. var namesep = cname + "=";
  193. var ca = document.cookie.split(';');
  194. for(var i=0, c; c=ca[i]; i++)
  195. {
  196. c = c.replace(/^\s*|\s*$/g,"");
  197. if (c.indexOf(namesep) == 0) {
  198. return c.substring(namesep.length,c.length);
  199. }
  200. }
  201. return null;
  202. }
  203. // main()
  204. if (!window.opera && addMenuItem) {
  205. GM_registerMenuCommand('AutoTOC: Toggle display', autoTOC_toggleDisplay);
  206. }
  207. f();
  208. })();