content.js 5.8 KB

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