index.js 6.9 KB

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