lettus_typeset.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. /*
  2. @licstart The following is the entire license notice for the
  3. JavaScript code in this page.
  4. Copyright (C) 2015-2018 Emilia Blåsten emily@countermail.com
  5. This program is free software: you can redistribute it and/or modify
  6. it under the terms of the GNU Affero General Public License as
  7. published by the Free Software Foundation, either version 3 of the
  8. License, or (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU Affero General Public License for more details.
  13. You should have received a copy of the GNU Affero General Public License
  14. along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. @licend The above is the entire license notice for the JavaScript code
  16. in this page.
  17. Contributions are welcome: https://notabug.org/emily/Lettus-CSS-MathJax
  18. */
  19. // Use strict JavaScript mode
  20. "use strict";
  21. function loadMathJax(MJengine) {
  22. var engine = document.createElement("script");
  23. engine.type = "text/javascript";
  24. if(MJengine == "CDN") {
  25. engine.setAttribute("src", "https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-MML-AM_CHTML");
  26. } else {
  27. // if using this one, configure lettus.css to the use appropriate url for the web fonts
  28. engine.setAttribute("src", "/MathJax/MathJax.js?config=TeX-MML-AM_CHTML");
  29. }
  30. // The MathJax.Hub.Queue will run after MathJax loaded everything
  31. engine.innerHTML = "MathJax.Hub.Queue(function() {" +
  32. "var theBody = document.getElementsByTagName('body')[0];" +
  33. "theBody.removeChild(document.getElementById('MJloading'));" +
  34. "setStaticFavicon();" +
  35. "document.getElementById('focusHere').scrollIntoView();" +
  36. "});";
  37. document.getElementsByTagName("head")[0].appendChild(engine);
  38. };
  39. loadMathJax("CDN"); // If use other than "CDN" then configure the web font URL in lettus.css
  40. setAnimFavicon();
  41. document.addEventListener("DOMContentLoaded", addBodyElements);
  42. document.addEventListener("DOMContentLoaded", checkMissingIDs);
  43. document.addEventListener("DOMContentLoaded", countersToNbr);
  44. document.addEventListener("DOMContentLoaded", setNumbersAndLinks);
  45. document.addEventListener("DOMContentLoaded", addAllLinksToFooter);
  46. document.addEventListener("DOMContentLoaded", numberAllReferences);
  47. document.addEventListener("DOMContentLoaded", displayCitationRefNbrs);
  48. /*
  49. Functions to set favicons
  50. */
  51. function setAnimFavicon() {
  52. // stash old favicon
  53. var links = document.getElementsByTagName("link");
  54. var link, i;
  55. for(i=0; i < links.length; i++) {
  56. if(links[i].getAttribute("rel") && links[i].rel == "shortcut icon") {
  57. links[i].rel = "old shortcut icon";
  58. links[i].id = "old-favicon-link"
  59. link = document.createElement("link");
  60. link = links[i].cloneNode(true);
  61. links[i].parentNode.removeChild(links[i]);
  62. document.getElementsByTagName("head")[0].appendChild(link);
  63. break;
  64. }
  65. }
  66. // set animated favicon
  67. link = document.createElement("link");
  68. link.type = "image/gif";
  69. link.rel = "shortcut icon";
  70. link.href = "favicon-animated.gif";
  71. link.id = "favicon-link";
  72. document.getElementsByTagName("head")[0].appendChild(link);
  73. };
  74. function setStaticFavicon() {
  75. var ico, stashed, newIco;
  76. // remove the animated favicon
  77. ico = document.getElementById("favicon-link");
  78. ico.parentNode.removeChild(ico);
  79. // assumes the original favicon is in element with id "old-favicon-link"
  80. stashed = document.getElementById("old-favicon-link");
  81. newIco = document.createElement("link");
  82. if(stashed) {
  83. // restore the old one
  84. newIco = stashed.cloneNode(true);
  85. stashed.parentNode.removeChild(stashed);
  86. } else {
  87. // create an empty transparent one using ImageMagick and base64:
  88. // "convert -size 16x16 xc:black -transparent black try.gif"
  89. // "base64 try.gif" and then you see the binary encoding in the terminal.
  90. newIco.href = "data:image/gif;base64," +
  91. "R0lGODlhEAAQAPAAAAAAAAAAACH5BAEAAAAALA" +
  92. "AAAAAQABAAAAIOhI+py+0Po5y02ouzPgUAOw==";
  93. newIco.type = "image/gif";
  94. }
  95. newIco.rel = "shortcut icon";
  96. newIco.id = "favicon-link";
  97. document.getElementsByTagName("head")[0].appendChild(newIco);
  98. };
  99. /*
  100. Function to set the background "MathJax typesetting..." text right after
  101. the DOM has been loaded.
  102. */
  103. function addBodyElements() {
  104. var theBody = document.getElementsByTagName("body")[0];
  105. var bg, anim; //background + animation
  106. var theFoot; //Footer
  107. // MathJax loading background
  108. bg = document.createElement("div");
  109. bg.id = "MJloading";
  110. bg.innerHTML = "MathJax<br>typesetting...";
  111. // an animated child node
  112. anim = document.createElement("span");
  113. anim.id = "anim1";
  114. anim.innerHTML = "+";
  115. bg.appendChild(anim);
  116. theBody.insertBefore(bg, theBody.firstChild);
  117. // Footer containig reference list
  118. if(!document.getElementById("bibliography")) {
  119. theFoot = document.createElement("footer");
  120. theFoot.id = "bibliography";
  121. theFoot.innerHTML = "<h2>References</h2>";
  122. theBody.appendChild(theFoot);
  123. } else
  124. console.log("The bibliography already exists, typed in manually?");
  125. };
  126. /*
  127. Functions to be run when the DOM tree has been loaded. Sets the "Theorem
  128. X. " text in front of theorem divs, etc. Also prepends "Lemma Y" to the
  129. text in links pointing to theorems, lemmas, etc. Also checks for missing
  130. link targets.
  131. */
  132. function checkMissingIDs() {
  133. var i, iMax;
  134. var links, linksTo;
  135. links = document.getElementsByTagName("a");
  136. iMax = links.length;
  137. for(i=0; i < iMax; i++) {
  138. linksTo = links[i].getAttribute("href");
  139. if(linksTo.substring(0,1) == "#") {
  140. if(document.getElementById(linksTo.substring(1,linksTo.length)) === null) {
  141. alert("Link " + linksTo + " missing target.");
  142. }
  143. }
  144. }
  145. };
  146. function countersToNbr() {
  147. var cntr1 = 0;
  148. var cntr2 = 0;
  149. var cntr3 = 0;
  150. var elems = document.querySelectorAll("[data-counters]");
  151. var i, iMax = elems.length, nbr = "";
  152. for(i = 0; i < iMax; i++) {
  153. switch(elems[i].getAttribute("data-counters")) {
  154. case "1":
  155. nbr = "" + (++cntr1);
  156. cntr2 = 0;
  157. cntr3 = 0;
  158. break;
  159. case "2":
  160. nbr = "" + cntr1 + "." + (++cntr2);
  161. cntr3 = 0;
  162. break;
  163. case "3":
  164. nbr = "" + cntr1 + "." + cntr2 + "." + (++cntr3);
  165. break;
  166. default:
  167. console.log('Attribute "data-counters" must be either "1", "2" or "3".');
  168. }
  169. elems[i].setAttribute("data-nbr", nbr);
  170. }
  171. };
  172. function setNumbersAndLinks() {
  173. var beforeElements = [
  174. ["theorem", "Theorem"],
  175. ["lemma", "Lemma"],
  176. ["corollary", "Corollary"],
  177. ["proposition", "Proposition"],
  178. ["conjecture", "Conjecture"],
  179. ["definition", "Definition"],
  180. ["remark", "Remark"],
  181. ["proof", "Proof"],
  182. ["motivation", "Motivation"]
  183. ];
  184. var name, fullName, getsNbr, titleSpan;
  185. var divs = document.getElementsByTagName("div");
  186. var i, j;
  187. // Get the data structure
  188. for(i=0; i < divs.length; i++) {
  189. if(!divs[i].className) continue;
  190. for(j=0; j < beforeElements.length; j++) {
  191. if(divs[i].className.indexOf(beforeElements[j][0]) == -1)
  192. continue;
  193. // Now we know that it has one of the classes of interest
  194. // Place the number in its name only from the data-nbr attribute
  195. getsNbr = divs[i].getAttribute("data-nbr") != null;
  196. name = beforeElements[j][1] + ((getsNbr) ? " " + divs[i].getAttribute("data-nbr") : ""),
  197. fullName = document.createElement("span")
  198. fullName.className = beforeElements[j][0];
  199. // Check if the div has a title (e.g. theorem name)
  200. if(divs[i].title) {
  201. titleSpan = document.createElement("span");
  202. titleSpan.innerHTML = "(" + divs[i].title + ")";
  203. titleSpan.className = "theoremName";
  204. fullName.innerHTML = name + " ";
  205. fullName.appendChild(titleSpan);
  206. fullName.innerHTML += ". ";
  207. } else {
  208. fullName.innerHTML = name + ". ";
  209. }
  210. // Link the name to the div if it has an ID
  211. if(divs[i].id) {
  212. fullName.innerHTML = "<a href='\#" + divs[i].id +
  213. "'class='addTxtForbidden'>" + fullName.innerHTML + "</a>";
  214. }
  215. // Show the name in front of the result div
  216. if(divs[i].firstChild)
  217. divs[i].insertBefore(fullName, divs[i].firstChild);
  218. else
  219. divs[i].appendChild(fullName);
  220. // If already done for this div, then go to next div
  221. break;
  222. }
  223. }
  224. // Add result numbers to links refering to them from same file
  225. displayInArticleReferenceNbrs();
  226. };
  227. function listOutsideWebsitesLinkedTo() {
  228. var sourcesLinkedTo = [];
  229. var links, linksTo, i;
  230. links = document.getElementsByTagName("a");
  231. for(i=0; i < links.length; i++) {
  232. linksTo = links[i].getAttribute("href");
  233. // remove anything after a '#'
  234. if(linksTo.indexOf("#") > -1)
  235. linksTo = linksTo.slice(0,linksTo.indexOf("#"));
  236. // Add to list if not yet added and points outside
  237. if( linksTo.length > 0 &&
  238. sourcesLinkedTo.indexOf(linksTo) == -1 &&
  239. linksTo.substring(0,1) != "#") {
  240. sourcesLinkedTo.push(linksTo);
  241. }
  242. }
  243. return sourcesLinkedTo.sort();
  244. };
  245. function addAllLinksToFooter() {
  246. // set reference list to footer
  247. var theFoot;
  248. var onlineRefsHeader, onlineList;
  249. var targets = listOutsideWebsitesLinkedTo();
  250. var ref, i, item;
  251. theFoot = document.getElementById("bibliography");
  252. onlineRefsHeader = document.createElement("h3");
  253. onlineRefsHeader.innerHTML = "Links";
  254. theFoot.appendChild(onlineRefsHeader);
  255. onlineList = document.createElement("ul");
  256. onlineList.setAttribute("class", "referenceList");
  257. for(i = 0; i < targets.length; i++) {
  258. ref = document.createElement("a");
  259. ref.setAttribute("href", targets[i]);
  260. ref.classList.add("addTxtForbidden");
  261. ref.innerHTML = targets[i];
  262. item = document.createElement("li");
  263. item.setAttribute("class", "reference");
  264. item.appendChild(ref);
  265. onlineList.appendChild(item);
  266. }
  267. theFoot.appendChild(onlineList);
  268. };
  269. function numberAllReferences() {
  270. var refs = document.getElementsByClassName("reference");
  271. var i, refNbr;
  272. for(i = 0; i < refs.length; i++) {
  273. refNbr = document.createElement("span");
  274. refNbr.setAttribute("class", "ref-nbr");
  275. refNbr.innerHTML = "[" + (i+1) + "] ";
  276. refs[i].insertBefore(refNbr, refs[i].firstChild);
  277. refs[i].setAttribute("data-refnumber", i+1);
  278. }
  279. };
  280. function displayInArticleReferenceNbrs() {
  281. var links = document.getElementsByTagName("a");
  282. var i, iMax;
  283. var linkTo, elem, nBr;
  284. iMax = links.length;
  285. // Loop through all links and check if the ID they point to exists
  286. for(i = 0; i < iMax; i++) {
  287. nBr = null;
  288. // just using link[i].herf will give "file://.........#thm1"
  289. linkTo = links[i].getAttribute("href");
  290. if(linkTo.substring(0,1) != "#" || links[i].classList.contains("addTxtForbidden"))
  291. continue;
  292. // Now links[i] points to this document and accepts displaying a number
  293. elem = document.getElementById(linkTo.substring(1,linkTo.length));
  294. if(elem !== null)
  295. nBr = elem.getAttribute("data-nbr");
  296. if(nBr !== null)
  297. links[i].innerHTML += nBr ? " " + nBr : "";
  298. }
  299. };
  300. function displayCitationRefNbrs() {
  301. var links = document.getElementsByTagName("a");
  302. var refs = document.getElementsByClassName("reference");
  303. var i, iMax = links.length;
  304. var j, jMax = refs.length;
  305. var linkTo, refNbr;
  306. var refAnchor;
  307. for(i = 0; i < iMax; i++) {
  308. if(links[i].classList.contains("addTxtForbidden"))
  309. continue;
  310. linkTo = links[i].getAttribute("href");
  311. for(j = 0; j < jMax; j++) {
  312. refAnchor = refs[j].getElementsByTagName("a")[0];
  313. if( // 1st case: cites this document using #ID
  314. (linkTo.substring(0,1) == "#" && linkTo == "#" + refs[j].id) ||
  315. // 2nd case: cites outside URL without #ID in href
  316. (typeof refAnchor != "undefined" &&
  317. linkTo.indexOf("#") == -1 &&
  318. refAnchor.getAttribute("href") == linkTo) ) {
  319. refNbr = document.createElement("span");
  320. refNbr.setAttribute("class", "ref-nbr");
  321. refNbr.innerHTML = " [" + refs[j].getAttribute("data-refnumber");
  322. // If linking to item in reference list, then put innerHTML inside [ ]
  323. if(links[i].innerHTML && linkTo.substring(0,1)=="#") {
  324. refNbr.innerHTML += ", " + links[i].innerHTML;
  325. links[i].innerHTML = "";
  326. }
  327. refNbr.innerHTML += "]";
  328. links[i].appendChild(refNbr);
  329. break;
  330. }
  331. if( //3rd case: cites outside URL with #ID in href
  332. typeof refAnchor != "undefined" &&
  333. linkTo.indexOf("#") > -1 &&
  334. refAnchor.getAttribute("href") == linkTo.slice(0,linkTo.indexOf("#")) ) {
  335. refNbr = document.createElement("span");
  336. refNbr.setAttribute("class", "ref-nbr");
  337. refNbr.innerHTML = " [" + refs[j].getAttribute("data-refnumber") + ", "
  338. + linkTo.slice(linkTo.indexOf("#"),linkTo.length) + "]";
  339. links[i].appendChild(refNbr);
  340. break;
  341. }
  342. }
  343. }
  344. };