content.js 6.1 KB

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