TOC TouchButton

Add Touchfriendly TOC and next and previous link variant

  1. // ==UserScript==
  2. // https://gf.qytechs.cn/scripts/380720-toc-touchbutton
  3. // @name TOC TouchButton
  4. // @namespace Touchfriendly TOC Variant
  5. // @match https://*.wordpress.com/*
  6. // @version 0.8
  7. // @description Add Touchfriendly TOC and next and previous link variant
  8. // @grant none
  9. // ==/UserScript==
  10.  
  11. //Known Bug: look for a way to detect readerview/android page reload
  12. // onreload with active reader view(firefox) -> loads original version with longText link
  13.  
  14.  
  15. const SELECTORTOC = 'p > a';
  16. const MEDIAQUERYMAXWIDTH = '480px';
  17.  
  18. const DEFAULTSHORTTOC = "ToC";
  19. const DEFAULTSHORTPREV = "-1←";
  20. const DEFAULTSHORTNEXT = "→+1";
  21. /**
  22. * Setting to Pair ["Longtext", "ShortText"]
  23. * ShortText will be shown as button
  24. * @param {String} LongText longer String which should be replaced
  25. * @param {Array} ShortText replacement for longText
  26. */
  27. const LINKVARIANTS = [
  28. {
  29. "shortVersion": DEFAULTSHORTTOC,
  30. "longVariants": ["Table of Contents", "Index", "ToC"]
  31. },
  32. {
  33. "shortVersion": DEFAULTSHORTPREV,
  34. "longVariants": ["Previous Chapter", "Prev", "<- Previous Chapter"]
  35. },
  36. {
  37. "shortVersion": DEFAULTSHORTNEXT,
  38. "longVariants": ["Next Chapter", "Next", "Next Chapter -&gt;"]
  39. }];
  40. //"<- Previous Chapter" or "&lt;- Previous Chapter" possible
  41. //"Next Chapter ->" same as before
  42.  
  43. // ↥ front UI ↥ for adjustment for different sites copy script and adjust SELECTORTOC and LINKVARIANTS and metas(@match, @namespace)
  44. // ↧ script ↧
  45. var nodesArray = [];
  46.  
  47. function readCssStyle(element, attribute) {
  48. let style = window.getComputedStyle ? getComputedStyle(element, null) : element.currentStyle;
  49. return style[attribute]
  50. }
  51.  
  52. /**
  53. * toggle Hidden attributes depending on css value (set by mediaquery)
  54. *
  55. * without active javascript attribute toggling -> the longT Version would get read by Firefox Reader View and maybe other readability plugins
  56. */
  57. function checkHidden() {
  58. let shortT = document.querySelectorAll('.shortTOC');
  59. let longT = document.querySelectorAll('.longTOC');
  60.  
  61. //console.log("checkHidden","checkHidden shortT length: "+shortT.length +" # "+ readStyle(shortT[0],"display") );
  62. //http://john.foliot.ca/aria-hidden/
  63. for (var i = 0; i < shortT.length; i++) {
  64. //console.log("checkHidden","checkHidden shortT setAttribute aria-hidden + " + i);
  65. if (readCssStyle(shortT[i], "display") === "none") {
  66. //console.log("checkHidden","checkHidden shortT setAttribute aria-hidden display none");
  67. shortT[i].setAttribute('aria-hidden', 'true');
  68. shortT[i].setAttribute('hidden', "true");
  69. shortT[i].setAttribute('role', 'presentation');
  70. }
  71. else if (readCssStyle(shortT[i], "display") == "inline-block") {
  72. shortT[i].setAttribute('aria-hidden', 'false')
  73. shortT[i].removeAttribute('role');
  74. shortT[i].removeAttribute('hidden');
  75. }
  76.  
  77. }
  78. for (let i = 0; i < longT.length; i++) {
  79. if (readCssStyle(longT[i], "display") == "none") {
  80. longT[i].setAttribute('aria-hidden', 'true');
  81. longT[i].setAttribute('hidden', "true");
  82. longT[i].setAttribute('role', 'presentation');
  83. }
  84. else if (readCssStyle(longT[i], "display") == "inline-block") {
  85. longT[i].setAttribute('aria-hidden', 'false')
  86. longT[i].removeAttribute('role');
  87. longT[i].removeAttribute('hidden');
  88. }
  89. }
  90. }
  91.  
  92.  
  93. //https://stackoverflow.com/questions/23683439/gm-addstyle-equivalent-in-tampermonkey
  94. function addGlobalStyle(css) {
  95. let head, style;
  96. head = document.getElementsByTagName('head')[0];
  97. if (!head) { return; }
  98. style = document.createElement('style');
  99. style.type = 'text/css';
  100. style.innerHTML = css;
  101. head.appendChild(style);
  102. }
  103.  
  104. //https://stackoverflow.com/questions/1686571/greasemonkey-how-to-apply-a-css-rule-only-for-media-print
  105. //https://stackoverflow.com/questions/8624210/getting-jquery-and-gm-addstyle-to-work-in-a-chrome-userscript-based-off-of-a-wor
  106. //multiline for better readability add at end of line -> \
  107. //https://stackoverflow.com/questions/23608346/how-to-style-a-div-like-the-button-element
  108. addGlobalStyle('\
  109. .shortTOC{ display:none;visiblity:hidden;}\
  110. .longTOC{ display:inline-block;visiblity:visible;} \
  111. @media (max-width: '+ MEDIAQUERYMAXWIDTH + ') { \
  112. .shortTOC { display: inline-block;visiblity:visible;\
  113. width: 23%;\
  114. text-align: center;\
  115. line-height:3.5em;\
  116. font-size:6vw;\
  117. -ms-touch-action: manipulation;\
  118. touch-action: manipulation;\
  119. cursor: pointer;\
  120. -webkit-user-select: none;\
  121. -moz-user-select: none;\
  122. -ms-user-select: none;\
  123. user-select: none;\
  124. background-image: none;\
  125. border: 1px solid transparent;\
  126. border-radius: 4px;\
  127. border-color: #ccc;\
  128. }\
  129. .longTOC { display:none;visiblity:hidden;}\
  130. }\
  131. ');
  132.  
  133. /**
  134. * results has only Nodes which match filterString
  135. * @param {Array} Nodes
  136. * @param {String} filterString
  137. */
  138. function getNodes(Nodes, filterString) {
  139. //filter both HTML entity character and pure text
  140. //innerHTML:characters as is (example %nbsp; kept as space) ; textContent: example &nbsp; converted to space character
  141. let TOCFilter = Nodes.filter(e => (e.innerHTML == filterString || e.textContent == filterString));
  142. /*
  143. let TOCNodes = TOCFilter.map((e, i) => {
  144. //console.log("elementcontent: " +e + " at index: "+i +" nodeValueOuterhtml: " + e.outerHTML + " nodeValueinnerhtml: " + e.innerHTML );
  145. return e
  146. });
  147. */
  148. return TOCFilter
  149. }
  150. /**
  151. * add custom classes to link text
  152. * @param {array} Nodes linkarray
  153. * @param {string} shortText
  154. */
  155. function setClasses(Nodes, shortText) {
  156. Nodes.forEach(element => {
  157. let TOCButton = document.createElement("a")
  158. TOCButton.href = element;
  159. TOCButton.setAttribute("class", "shortTOC")
  160. TOCButton.textContent = shortText
  161. element.setAttribute("class", "longTOC")
  162. element.parentNode.insertBefore(TOCButton, element.nextSibling);
  163. });
  164. }
  165. /**
  166. * Compare long link text and replace with shortText version
  167. * @param {Array} nodes link array
  168. * @param {String} longText longer String which should be replaced and is used as filter for the linkarray
  169. * @param {String} shortText replacement for longText
  170. */
  171. function addTouchfriendlyVariant(nodes, longText, shortText) {
  172. let TOCNodes = getNodes(nodes, longText)
  173. setClasses(TOCNodes, shortText);
  174. //console.log('Toc element: ' + TOCNodes);
  175. }
  176.  
  177.  
  178. function main() {
  179. let getSelectedLinks = function () {
  180. let links = Array.from(
  181. document.querySelectorAll(SELECTORTOC)
  182. );
  183.  
  184. return links;
  185. };
  186. const links = getSelectedLinks();
  187.  
  188. /*
  189. addTouchfriendlyVariant(links, "Table of Contents", "ToC");
  190. addTouchfriendlyVariant(links, "Previous Chapter", "-1←");
  191. addTouchfriendlyVariant(links, "Next Chapter", '→+1');
  192. */
  193.  
  194. LINKVARIANTS.forEach(key => {
  195. key.longVariants.forEach(variant => {
  196. //console.log("LINKVARIANTS: " + key.shortVersion + " # " + variant);
  197. addTouchfriendlyVariant(links, variant, key.shortVersion);
  198. });
  199. });
  200.  
  201.  
  202. }
  203. main()
  204.  
  205. //https://www.sitepoint.com/jquery-document-ready-plain-javascript/
  206. var callback = function () {
  207. // Handler when the DOM is fully loaded
  208. checkHidden();
  209. };
  210.  
  211. //https://stackoverflow.com/questions/2720658/how-to-detect-when-a-tab-is-focused-or-not-in-chrome-with-javascript
  212. var focusCheckCallback = function () {
  213. //console.log("onvisibilityState reaction: " + document.visibilityState);
  214. if (document.visibilityState == "visible") //hidden -> readerview rebuild?
  215. checkHidden();
  216. }
  217.  
  218. if (
  219. document.readyState === "complete" ||
  220. (document.readyState !== "loading" && !document.documentElement.doScroll)
  221. ) {
  222. callback();
  223. } else {
  224. document.addEventListener("DOMContentLoaded", callback);
  225. }
  226.  
  227. window.addEventListener("resize", function () {
  228. checkHidden();
  229. });
  230. window.addEventListener("visibilitychange", focusCheckCallback);
  231. //window.addEventListener("blur",focusCheckCallback);

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址