index.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. var $ = document.querySelector.bind(document)
  2. var state = {
  3. theme,
  4. raw,
  5. content,
  6. compiler,
  7. html: '',
  8. markdown: '',
  9. toc: '',
  10. interval: null,
  11. ms: 1000,
  12. }
  13. chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
  14. if (req.message === 'reload') {
  15. location.reload(true)
  16. }
  17. else if (req.message === 'theme') {
  18. state.theme = req.theme
  19. m.redraw()
  20. }
  21. else if (req.message === 'raw') {
  22. state.raw = req.raw
  23. m.redraw()
  24. }
  25. else if (req.message === 'autoreload') {
  26. clearInterval(state.interval)
  27. }
  28. })
  29. var oncreate = {
  30. markdown: () => {
  31. scroll()
  32. },
  33. html: () => {
  34. scroll()
  35. if (state.content.toc && !state.toc) {
  36. state.toc = toc()
  37. m.redraw()
  38. }
  39. setTimeout(() => Prism.highlightAll(), 20)
  40. anchors()
  41. }
  42. }
  43. function mount () {
  44. $('pre').style.display = 'none'
  45. var md = $('pre').innerText
  46. m.mount($('body'), {
  47. oninit: () => {
  48. state.markdown = md
  49. chrome.runtime.sendMessage({
  50. message: 'markdown',
  51. compiler: state.compiler,
  52. markdown: state.markdown
  53. }, (res) => {
  54. state.html = state.content.emoji ? emojinator(res.html) : res.html
  55. m.redraw()
  56. })
  57. },
  58. view: () => {
  59. var dom = []
  60. if (state.raw) {
  61. dom.push(m('pre#_markdown', {oncreate: oncreate.markdown}, state.markdown))
  62. $('body').classList.remove('_toc-left', '_toc-right')
  63. }
  64. else {
  65. if (state.theme) {
  66. dom.push(m('link#_theme', {
  67. rel: 'stylesheet', type: 'text/css',
  68. href: state.theme.url,
  69. }))
  70. }
  71. if (state.html) {
  72. dom.push(m('#_html', {oncreate: oncreate.html,
  73. class: /github(-dark)?/.test(state.theme.name) ? 'markdown-body' : 'markdown-theme'},
  74. m.trust(state.html)
  75. ))
  76. if (state.content.toc && state.toc) {
  77. dom.push(m.trust(state.toc))
  78. $('body').classList.add('_toc-left')
  79. }
  80. if (state.content.mathjax) {
  81. dom.push(m('script', {type: 'text/x-mathjax-config'}, mathjax))
  82. dom.push(m('script', {
  83. src: 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/MathJax.js'
  84. }))
  85. }
  86. }
  87. }
  88. return (dom.length ? dom : m('div'))
  89. }
  90. })
  91. }
  92. function scroll () {
  93. function race (done) {
  94. var images = Array.from(document.querySelectorAll('img'))
  95. if (!images.length) {
  96. done()
  97. }
  98. var loaded = 0
  99. images.forEach((img) => {
  100. img.addEventListener('load', () => {
  101. if (++loaded === images.length) {
  102. done()
  103. }
  104. }, {once: true})
  105. })
  106. setTimeout(done, 100)
  107. }
  108. function init () {
  109. if (state.content.scroll) {
  110. var key = 'md-' + location.origin + location.pathname
  111. $('body').scrollTop = parseInt(localStorage.getItem(key))
  112. var timeout = null
  113. window.addEventListener('scroll', () => {
  114. clearTimeout(timeout)
  115. timeout = setTimeout(() => {
  116. localStorage.setItem(key, $('body').scrollTop)
  117. }, 100)
  118. })
  119. }
  120. else if (location.hash && $(location.hash)) {
  121. $('body').scrollTop = $(location.hash).offsetTop
  122. }
  123. }
  124. var loaded
  125. race(() => {
  126. if (!loaded) {
  127. init()
  128. loaded = true
  129. }
  130. })
  131. }
  132. function anchors () {
  133. Array.from($('#_html').childNodes)
  134. .filter((node) => /h[1-6]/i.test(node.tagName))
  135. .forEach((node) => {
  136. var a = document.createElement('a')
  137. a.className = 'anchor'
  138. a.name = node.id
  139. a.href = '#' + node.id
  140. a.innerHTML = '<span class="octicon octicon-link"></span>'
  141. node.prepend(a)
  142. })
  143. }
  144. var toc = (
  145. link = (header) => '<a href="#' + header.id + '">' + header.title + '</a>') =>
  146. Array.from($('#_html').childNodes)
  147. .filter((node) => /h[1-6]/i.test(node.tagName))
  148. .map((node) => ({
  149. id: node.getAttribute('id'),
  150. level: parseInt(node.tagName.replace('H', '')),
  151. title: node.innerText
  152. }))
  153. .reduce((html, header, index, headers) => {
  154. if (index) {
  155. var prev = headers[index - 1]
  156. }
  157. if (!index || prev.level === header.level) {
  158. html += link(header)
  159. }
  160. else if (prev.level > header.level) {
  161. while (prev.level-- > header.level) {
  162. html += '</div>'
  163. }
  164. html += link(header)
  165. }
  166. else if (prev.level < header.level) {
  167. while (prev.level++ < header.level) {
  168. html += '<div id="_ul">'
  169. }
  170. html += link(header)
  171. }
  172. return html
  173. }, '<div id="_toc"><div id="_ul">') + '</div></div>'
  174. if (document.readyState === 'complete') {
  175. mount()
  176. }
  177. else {
  178. window.addEventListener('DOMContentLoaded', mount)
  179. }
  180. if (state.content.autoreload) {
  181. ;(() => {
  182. var initial = ''
  183. var xhr = new XMLHttpRequest()
  184. xhr.onreadystatechange = () => {
  185. if (xhr.readyState === 4) {
  186. if (!initial) {
  187. initial = xhr.responseText
  188. }
  189. else if (initial !== xhr.responseText) {
  190. location.reload(true)
  191. }
  192. }
  193. }
  194. var get = () => {
  195. xhr.open('GET', location.href + '?preventCache=' + Date.now(), true)
  196. try {
  197. xhr.send()
  198. }
  199. catch (err) {
  200. console.error(err)
  201. clearInterval(state.interval)
  202. }
  203. }
  204. get()
  205. state.interval = setInterval(get, state.ms)
  206. })()
  207. }
  208. var mathjax = `
  209. // TeX-AMS_HTML
  210. MathJax.Hub.Config({
  211. jax: [
  212. 'input/TeX',
  213. 'output/HTML-CSS',
  214. 'output/PreviewHTML',
  215. ],
  216. extensions: [
  217. 'tex2jax.js',
  218. 'AssistiveMML.js',
  219. 'a11y/accessibility-menu.js',
  220. ],
  221. TeX: {
  222. extensions: [
  223. 'AMSmath.js',
  224. 'AMSsymbols.js',
  225. 'noErrors.js',
  226. 'noUndefined.js',
  227. ]
  228. },
  229. tex2jax: {
  230. inlineMath: [
  231. ['$', '$'],
  232. ['\\\\(', '\\\\)'],
  233. ],
  234. displayMath: [
  235. ['$$', '$$'],
  236. ['\\\\[', '\\\\]'],
  237. ],
  238. processEscapes: true
  239. },
  240. showMathMenu: false,
  241. showProcessingMessages: false,
  242. messageStyle: 'none',
  243. skipStartupTypeset: true, // disable initial rendering
  244. positionToHash: false
  245. })
  246. // set specific container to render, can be delayed too
  247. MathJax.Hub.Queue(
  248. ['Typeset', MathJax.Hub, '_html']
  249. )
  250. `