index.js 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  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. interval: null,
  12. ms: 1000,
  13. _themes: {
  14. 'github': 'auto',
  15. 'github-dark': 'dark',
  16. 'almond': 'light',
  17. 'air': 'auto',
  18. 'awsm': 'light',
  19. 'axist': 'light',
  20. 'bamboo': 'auto',
  21. 'bullframe': 'light',
  22. 'holiday': 'auto',
  23. 'kacit': 'light',
  24. 'latex': 'light',
  25. 'marx': 'auto',
  26. 'mini': 'light',
  27. 'modest': 'auto',
  28. 'new': 'auto',
  29. 'no-class': 'auto',
  30. 'pico': 'auto',
  31. 'retro': 'dark',
  32. 'sakura': 'light',
  33. 'sakura-vader': 'dark',
  34. 'semantic': 'auto',
  35. 'simple': 'auto',
  36. 'splendor': 'auto',
  37. 'style-sans': 'light',
  38. 'style-serif': 'light',
  39. 'stylize': 'auto',
  40. 'superstylin': 'auto',
  41. 'tacit': 'light',
  42. 'vanilla': 'auto',
  43. 'water': 'light',
  44. 'water-dark': 'dark',
  45. 'writ': 'auto',
  46. }
  47. }
  48. chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
  49. if (req.message === 'reload') {
  50. location.reload(true)
  51. }
  52. else if (req.message === 'theme') {
  53. state.theme = req.theme
  54. m.redraw()
  55. }
  56. else if (req.message === 'themes') {
  57. state.themes = req.themes
  58. m.redraw()
  59. }
  60. else if (req.message === 'raw') {
  61. state.raw = req.raw
  62. m.redraw()
  63. }
  64. else if (req.message === 'autoreload') {
  65. clearInterval(state.interval)
  66. }
  67. })
  68. var oncreate = {
  69. markdown: () => {
  70. scroll.body()
  71. },
  72. html: () => {
  73. scroll.body()
  74. if (state.content.toc && !state.toc) {
  75. state.toc = toc()
  76. m.redraw()
  77. }
  78. if (state.content.syntax) {
  79. setTimeout(() => Prism.highlightAll(), 20)
  80. }
  81. anchors()
  82. },
  83. toc: () => {
  84. scroll.toc()
  85. }
  86. }
  87. function mount () {
  88. $('pre').style.display = 'none'
  89. var md = $('pre').innerText
  90. m.mount($('body'), {
  91. oninit: () => {
  92. state.markdown = md
  93. chrome.runtime.sendMessage({
  94. message: 'markdown',
  95. compiler: state.compiler,
  96. markdown: state.markdown
  97. }, (res) => {
  98. state.html = state.content.emoji ? emojinator(res.html) : res.html
  99. m.redraw()
  100. })
  101. },
  102. view: () => {
  103. var dom = []
  104. if (state.raw) {
  105. dom.push(m('pre#_markdown', {oncreate: oncreate.markdown}, state.markdown))
  106. $('body').classList.remove('_toc-left', '_toc-right')
  107. }
  108. else {
  109. if (state.theme) {
  110. var included = Array.from($('body').classList).filter((name) => /^_theme/.test(name))
  111. if (included.length) {
  112. $('body').classList.remove(included)
  113. }
  114. dom.push(m('link#_theme', {
  115. rel: 'stylesheet', type: 'text/css',
  116. href: chrome.runtime.getURL(`/themes/${state.theme}.css`),
  117. }))
  118. $('body').classList.add(`_theme-${state.theme}`)
  119. if (state.content.syntax) {
  120. var prism =
  121. state._themes[state.theme] === 'dark' ||
  122. (state._themes[state.theme] === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches)
  123. ? 'prism-okaidia' : 'prism'
  124. dom.push(m('link#_prism', {
  125. rel: 'stylesheet', type: 'text/css',
  126. href: chrome.runtime.getURL(`/vendor/${prism}.min.css`),
  127. }))
  128. }
  129. if (state.content.mermaid) {
  130. mmd.refresh()
  131. }
  132. }
  133. if (state.html) {
  134. dom.push(m('#_html', {oncreate: oncreate.html,
  135. class: (/github(-dark)?/.test(state.theme) ? 'markdown-body' : 'markdown-theme') +
  136. (state.themes.width !== 'auto' ? ` _width-${state.themes.width}` : '')
  137. },
  138. m.trust(state.html)
  139. ))
  140. if (state.content.toc && state.toc) {
  141. dom.push(m('#_toc.tex2jax-ignore', {oncreate: oncreate.toc},
  142. m.trust(state.toc)
  143. ))
  144. $('body').classList.add('_toc-left')
  145. }
  146. if (state.content.mathjax) {
  147. dom.push(m('script', {
  148. src: chrome.runtime.getURL('/content/mathjax.js')
  149. }))
  150. dom.push(m('script', {
  151. src: chrome.runtime.getURL('/vendor/mathjax/tex-mml-chtml.js')
  152. }))
  153. }
  154. }
  155. }
  156. return (dom.length ? dom : m('div'))
  157. }
  158. })
  159. }
  160. var scroll = (() => {
  161. function race (done) {
  162. Promise.race([
  163. Promise.all([
  164. new Promise((resolve) => {
  165. var diagrams = Array.from(document.querySelectorAll('code.language-mmd, code.language-mermaid'))
  166. if (!state.content.mermaid || !diagrams.length) {
  167. resolve()
  168. }
  169. else {
  170. var timeout = setInterval(() => {
  171. var svg = Array.from(document.querySelectorAll('code.language-mmd svg, code.language-mermaid svg'))
  172. if (diagrams.length === svg.length) {
  173. clearInterval(timeout)
  174. resolve()
  175. }
  176. }, 50)
  177. }
  178. }),
  179. new Promise((resolve) => {
  180. var images = Array.from(document.querySelectorAll('img'))
  181. if (!images.length) {
  182. resolve()
  183. }
  184. else {
  185. var loaded = 0
  186. images.forEach((img) => {
  187. img.addEventListener('load', () => {
  188. if (++loaded === images.length) {
  189. resolve()
  190. }
  191. }, {once: true})
  192. })
  193. }
  194. }),
  195. ]),
  196. new Promise((resolve) => setTimeout(resolve, 500))
  197. ])
  198. .then(done)
  199. }
  200. function debounce (container, done) {
  201. var listener = /html|body/i.test(container.nodeName) ? window : container
  202. var timeout = null
  203. listener.addEventListener('scroll', () => {
  204. clearTimeout(timeout)
  205. timeout = setTimeout(done, 100)
  206. })
  207. }
  208. function listen (container, prefix) {
  209. var key = prefix + location.origin + location.pathname
  210. try {
  211. container.scrollTop = parseInt(localStorage.getItem(key))
  212. debounce(container, () => {
  213. localStorage.setItem(key, container.scrollTop)
  214. })
  215. }
  216. catch (err) {
  217. chrome.storage.local.get(key, (res) => {
  218. container.scrollTop = parseInt(res[key])
  219. })
  220. debounce(container, () => {
  221. chrome.storage.local.set({[key]: container.scrollTop})
  222. })
  223. }
  224. }
  225. return {
  226. body: () => {
  227. var loaded
  228. race(() => {
  229. if (!loaded) {
  230. loaded = true
  231. var container = ((html = $('html')) => (
  232. html.scrollTop = 1,
  233. html.scrollTop ? (html.scrollTop = 0, html) : $('body')
  234. ))()
  235. if (state.content.scroll) {
  236. listen(container, 'md-')
  237. }
  238. else if (location.hash && $(location.hash)) {
  239. container.scrollTop = $(location.hash).offsetTop
  240. }
  241. }
  242. })
  243. },
  244. toc: () => {
  245. listen($('#_toc'), 'md-toc-')
  246. }
  247. }
  248. })()
  249. function anchors () {
  250. Array.from($('#_html').childNodes)
  251. .filter((node) => /h[1-6]/i.test(node.tagName))
  252. .forEach((node) => {
  253. var a = document.createElement('a')
  254. a.className = 'anchor'
  255. a.name = node.id
  256. a.href = '#' + node.id
  257. a.innerHTML = '<span class="octicon octicon-link"></span>'
  258. node.prepend(a)
  259. })
  260. }
  261. var toc = (
  262. link = (header) => '<a href="#' + header.id + '">' + header.title + '</a>') =>
  263. Array.from($('#_html').childNodes)
  264. .filter((node) => /h[1-6]/i.test(node.tagName))
  265. .map((node) => ({
  266. id: node.getAttribute('id'),
  267. level: parseInt(node.tagName.replace('H', '')),
  268. title: node.innerText.replace(/</g, '&lt;').replace(/>/g, '&gt;')
  269. }))
  270. .reduce((html, header) => {
  271. html += '<div class="_ul">'.repeat(header.level)
  272. html += link(header)
  273. html += '</div>'.repeat(header.level)
  274. return html
  275. }, '')
  276. if (document.readyState === 'complete') {
  277. mount()
  278. }
  279. else {
  280. var timeout = setInterval(() => {
  281. if (document.readyState === 'complete') {
  282. clearInterval(timeout)
  283. mount()
  284. }
  285. }, 0)
  286. }
  287. if (state.content.autoreload) {
  288. ;(() => {
  289. var initial = ''
  290. var response = (body) => {
  291. if (!initial) {
  292. initial = body
  293. }
  294. else if (initial !== body) {
  295. location.reload(true)
  296. }
  297. }
  298. var xhr = new XMLHttpRequest()
  299. xhr.onreadystatechange = () => {
  300. if (xhr.readyState === 4) {
  301. response(xhr.responseText)
  302. }
  303. }
  304. var get = () => {
  305. if (location.protocol === 'file:') {
  306. chrome.runtime.sendMessage({
  307. message: 'autoreload',
  308. location: location.href
  309. }, (res) => {
  310. if (res.err) {
  311. console.error(res.err)
  312. clearInterval(state.interval)
  313. }
  314. else {
  315. response(res.body)
  316. }
  317. })
  318. }
  319. else {
  320. xhr.open('GET', location.href + '?preventCache=' + Date.now(), true)
  321. try {
  322. xhr.send()
  323. }
  324. catch (err) {
  325. console.error(err)
  326. clearInterval(state.interval)
  327. }
  328. }
  329. }
  330. get()
  331. state.interval = setInterval(get, state.ms)
  332. })()
  333. }