content.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. var $ = document.querySelector.bind(document)
  2. var state = {
  3. theme: window['theme'] || '',
  4. raw: window['raw'] || false,
  5. content: window['content'] || {},
  6. html: '',
  7. markdown: '',
  8. toc: ''
  9. }
  10. chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
  11. if (req.message === 'reload') {
  12. window.location.reload(true)
  13. }
  14. else if (req.message === 'theme') {
  15. state.theme = req.theme
  16. m.redraw()
  17. }
  18. else if (req.message === 'raw') {
  19. state.raw = req.raw
  20. m.redraw()
  21. }
  22. })
  23. var oncreate = {
  24. markdown: () => {
  25. if (state.content.scroll) {
  26. document.body.scrollTop = parseInt(localStorage.getItem('md-' + location.href))
  27. }
  28. },
  29. html: () => {
  30. if (state.content.scroll) {
  31. document.body.scrollTop = parseInt(localStorage.getItem('md-' + location.href))
  32. }
  33. if (state.content.toc && !state.toc) {
  34. state.toc = toc()
  35. m.redraw()
  36. }
  37. setTimeout(() => Prism.highlightAll(), 20)
  38. }
  39. }
  40. function mount () {
  41. $('pre').style.display = 'none'
  42. var md = $('pre').innerText
  43. m.mount($('body'), {
  44. oninit: () => {
  45. ;((done) => {
  46. if (document.charset === 'UTF-8') {
  47. done()
  48. return
  49. }
  50. m.request({method: 'GET', url: window.location.href,
  51. deserialize: (body) => {
  52. done(body)
  53. return body
  54. }
  55. })
  56. })((data) => {
  57. state.markdown = data || md
  58. chrome.runtime.sendMessage({
  59. message: 'markdown',
  60. markdown: state.markdown
  61. }, (res) => {
  62. state.html = res.marked
  63. m.redraw()
  64. })
  65. })
  66. },
  67. view: () => {
  68. var dom = []
  69. if (state.raw) {
  70. dom.push(m('pre#markdown', {oncreate: oncreate.markdown}, state.markdown))
  71. $('body').classList.remove('_toc-left', '_toc-right')
  72. }
  73. else {
  74. if (state.theme) {
  75. dom.push(m('link#theme [rel="stylesheet"] [type="text/css"]', {
  76. href: chrome.runtime.getURL('/themes/' + state.theme + '.css')
  77. }))
  78. }
  79. if (state.html) {
  80. dom.push(m('#html', {oncreate: oncreate.html,
  81. class: /github(-dark)?/.test(state.theme) ? 'markdown-body' : 'markdown-theme'},
  82. m.trust(state.html)
  83. ))
  84. if (state.content.toc && state.toc) {
  85. dom.push(m.trust(state.toc))
  86. $('body').classList.add('_toc-left')
  87. }
  88. }
  89. }
  90. return (dom.length ? dom : m('div'))
  91. }
  92. })
  93. }
  94. function scroll () {
  95. setTimeout(() => {
  96. var timeout = null
  97. window.addEventListener('scroll', () => {
  98. clearTimeout(timeout)
  99. timeout = setTimeout(() => {
  100. localStorage.setItem('md-' + location.href, document.body.scrollTop)
  101. }, 100)
  102. })
  103. document.body.scrollTop = parseInt(localStorage.getItem('md-' + location.href))
  104. }, 100)
  105. }
  106. if (document.readyState === 'complete') {
  107. mount()
  108. if (state.content.scroll) {
  109. scroll()
  110. }
  111. }
  112. else {
  113. window.addEventListener('DOMContentLoaded', mount)
  114. if (state.content.scroll) {
  115. window.addEventListener('load', scroll)
  116. }
  117. }
  118. function toc () {
  119. // extract all headers
  120. var headers = []
  121. function walk (nodes) {
  122. nodes.forEach((node) => {
  123. var sub = Array.from(node.childNodes)
  124. if (sub.length) {
  125. walk(sub)
  126. }
  127. if (/h[1-6]/i.test(node.tagName)) {
  128. headers.push({
  129. id: node.getAttribute('id'),
  130. level: parseInt(node.tagName.replace('H', '')),
  131. title: node.innerText
  132. })
  133. }
  134. })
  135. }
  136. walk(Array.from($('body').childNodes))
  137. // generate TOC
  138. var link = (header) =>
  139. '<a href="#' + header.id + '">' + header.title + '</a>'
  140. var html = '<div id="_toc"><div id="_ul">'
  141. headers.forEach((header, index) => {
  142. if (index) {
  143. var prev = headers[index - 1]
  144. }
  145. if (!index || prev.level === header.level) {
  146. html += link(header)
  147. }
  148. else if (prev.level > header.level) {
  149. html += '</div>' + link(header)
  150. }
  151. else if (prev.level < header.level) {
  152. html += '<div id="_ul">' + link(header)
  153. }
  154. })
  155. html += '</div></div>'
  156. return html
  157. }