index.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. var $ = document.querySelector.bind(document)
  2. var state = {
  3. theme: args.theme,
  4. raw: args.raw,
  5. themes: args.themes,
  6. content: args.content,
  7. compiler: args.compiler,
  8. html: '',
  9. markdown: '',
  10. toc: '',
  11. reload: {
  12. interval: null,
  13. ms: 1000,
  14. md: false,
  15. },
  16. _themes: {
  17. 'github': 'auto',
  18. 'github-dark': 'dark',
  19. 'almond': 'light',
  20. // 'air': 'light',
  21. 'awsm': 'light',
  22. 'axist': 'light',
  23. 'bamboo': 'auto',
  24. 'bullframe': 'light',
  25. 'holiday': 'auto',
  26. 'kacit': 'light',
  27. 'latex': 'light',
  28. 'marx': 'light',
  29. 'mini': 'light',
  30. 'modest': 'light',
  31. 'new': 'auto',
  32. 'no-class': 'auto',
  33. 'pico': 'auto',
  34. 'retro': 'dark',
  35. 'sakura': 'light',
  36. 'sakura-vader': 'dark',
  37. 'semantic': 'light',
  38. 'simple': 'auto',
  39. // 'splendor': 'light',
  40. 'style-sans': 'light',
  41. 'style-serif': 'light',
  42. 'stylize': 'light',
  43. 'superstylin': 'auto',
  44. 'tacit': 'light',
  45. 'vanilla': 'auto',
  46. 'water': 'light',
  47. 'water-dark': 'dark',
  48. 'writ': 'light',
  49. }
  50. }
  51. chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
  52. if (req.message === 'reload') {
  53. location.reload(true)
  54. }
  55. else if (req.message === 'theme') {
  56. state.theme = req.theme
  57. m.redraw()
  58. }
  59. else if (req.message === 'themes') {
  60. state.themes = req.themes
  61. m.redraw()
  62. }
  63. else if (req.message === 'raw') {
  64. state.raw = req.raw
  65. m.redraw()
  66. }
  67. else if (req.message === 'autoreload') {
  68. clearInterval(state.reload.interval)
  69. }
  70. })
  71. var oncreate = {
  72. markdown: () => {
  73. setTimeout(() => scroll(), 0)
  74. },
  75. html: () => {
  76. update()
  77. }
  78. }
  79. var onupdate = {
  80. html: () => {
  81. if (state.reload.md) {
  82. state.reload.md = false
  83. update()
  84. }
  85. },
  86. theme: () => {
  87. if (state.content.mermaid) {
  88. setTimeout(() => mmd.render(), 0)
  89. }
  90. }
  91. }
  92. var update = () => {
  93. scroll()
  94. if (state.content.syntax) {
  95. setTimeout(() => Prism.highlightAll(), 20)
  96. }
  97. if (state.content.mermaid) {
  98. setTimeout(() => mmd.render(), 40)
  99. }
  100. if (state.content.mathjax) {
  101. setTimeout(() => mj.render(), 60)
  102. }
  103. }
  104. var render = (md) => {
  105. state.markdown = md
  106. chrome.runtime.sendMessage({
  107. message: 'markdown',
  108. compiler: state.compiler,
  109. markdown: state.markdown
  110. }, (res) => {
  111. state.html = res.html
  112. if (state.content.emoji) {
  113. state.html = emojinator(state.html)
  114. }
  115. if (state.content.mermaid) {
  116. state.html = state.html.replace(
  117. /<code class="language-(?:mermaid|mmd)">/gi,
  118. '<code class="mermaid">'
  119. )
  120. }
  121. if (state.content.toc) {
  122. state.toc = toc.render(state.html)
  123. }
  124. state.html = anchors(state.html)
  125. m.redraw()
  126. })
  127. }
  128. function mount () {
  129. $('pre').style.display = 'none'
  130. var md = $('pre').innerText
  131. m.mount($('body'), {
  132. oninit: () => {
  133. render(md)
  134. },
  135. view: () => {
  136. var dom = []
  137. if (state.raw) {
  138. dom.push(m('pre#_markdown', {oncreate: oncreate.markdown}, state.markdown))
  139. $('body').classList.remove('_toc-left', '_toc-right')
  140. }
  141. else if (state.html) {
  142. var loaded = Array.from($('body').classList).filter((name) => /^_theme/.test(name))[0]
  143. $('body').classList.remove(loaded)
  144. dom.push(m('link#_theme', {
  145. onupdate: onupdate.theme,
  146. rel: 'stylesheet', type: 'text/css',
  147. href: chrome.runtime.getURL(`/themes/${state.theme}.css`),
  148. }))
  149. $('body').classList.add(`_theme-${state.theme}`)
  150. if (state.content.syntax) {
  151. var prism =
  152. state._themes[state.theme] === 'dark' ||
  153. (state._themes[state.theme] === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches)
  154. ? 'prism-okaidia' : 'prism'
  155. dom.push(m('link#_prism', {
  156. rel: 'stylesheet', type: 'text/css',
  157. href: chrome.runtime.getURL(`/vendor/${prism}.min.css`),
  158. }))
  159. }
  160. dom.push(m('#_html', {oncreate: oncreate.html, onupdate: onupdate.html,
  161. class: (/github(-dark)?/.test(state.theme) ? 'markdown-body' : 'markdown-theme') +
  162. (state.themes.width !== 'auto' ? ` _width-${state.themes.width}` : '')
  163. },
  164. m.trust(state.html)
  165. ))
  166. if (state.content.toc) {
  167. dom.push(m('#_toc.tex2jax-ignore', m.trust(state.toc)))
  168. $('body').classList.add('_toc-left')
  169. }
  170. }
  171. return dom
  172. }
  173. })
  174. }
  175. var anchors = (html) =>
  176. html.replace(/(<h[1-6] id="(.*?)">)/g, (header, _, id) =>
  177. header +
  178. '<a class="anchor" name="' + id + '" href="#' + id + '">' +
  179. '<span class="octicon octicon-link"></span></a>'
  180. )
  181. var toc = (() => {
  182. var walk = (regex, string, group, result = [], match = regex.exec(string)) =>
  183. !match ? result : walk(regex, string, group, result.concat(!group ? match[1] :
  184. group.reduce((all, name, index) => (all[name] = match[index + 1], all), {})))
  185. return {
  186. render: (html) =>
  187. walk(
  188. /<h([1-6]) id="(.*?)">(.*?)<\/h[1-6]>/gs,
  189. html,
  190. ['level', 'id', 'title']
  191. )
  192. .reduce((toc, {id, title, level}) => toc +=
  193. '<div class="_ul">'.repeat(level) +
  194. '<a href="#' + id + '">' + title + '</a>' +
  195. '</div>'.repeat(level)
  196. , '')
  197. }
  198. })()
  199. if (document.readyState === 'complete') {
  200. mount()
  201. }
  202. else {
  203. var timeout = setInterval(() => {
  204. if (document.readyState === 'complete') {
  205. clearInterval(timeout)
  206. mount()
  207. }
  208. }, 0)
  209. }