background.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. // chrome.storage.sync.clear()
  2. // chrome.permissions.getAll((p) => chrome.permissions.remove({origins: p.origins}))
  3. var match = '\\.(?:markdown|mdown|mkdn|md|mkd|mdwn|mdtxt|mdtext|text)(?:#.*|\\?.*)?$'
  4. var defaults = {
  5. theme: 'github',
  6. compiler: 'marked',
  7. content: {
  8. emoji: false,
  9. scroll: true,
  10. toc: false,
  11. mathjax: false,
  12. },
  13. raw: false,
  14. header: true,
  15. match,
  16. origins: {
  17. 'file://': match
  18. }
  19. }
  20. Object.keys(md).forEach((compiler) => {
  21. defaults[compiler] = md[compiler].defaults
  22. })
  23. var state
  24. function set (options) {
  25. chrome.storage.sync.set(options)
  26. Object.assign(state, options)
  27. }
  28. chrome.storage.sync.get((res) => {
  29. var options = !Object.keys(res).length ? defaults : res
  30. // v2.2 -> v2.3
  31. if (!options.match || !options.origins) {
  32. options.match = match
  33. options.origins = {
  34. 'file://': match
  35. }
  36. }
  37. // v2.3 -> v2.4
  38. else if (!options.origins['file://']) {
  39. options.origins['file://'] = match
  40. }
  41. // v2.4 -> v2.5
  42. if (!options.compiler) {
  43. options.compiler = options.options
  44. }
  45. if (!options.content) {
  46. options.content = defaults.content
  47. }
  48. // v2.7 -> v2.8
  49. if (!options.marked) {
  50. options.compiler = 'marked'
  51. options.marked = md.marked.defaults
  52. }
  53. // v2.8 -> v2.9
  54. if (!options.remark) {
  55. options.remark = md.remark.defaults
  56. }
  57. // v2.9 -> v3.0
  58. if (options.content.emoji === undefined) {
  59. options.content.emoji = false
  60. }
  61. // v3.0 -> v3.1
  62. if (options.header === undefined) {
  63. options.header = true
  64. }
  65. // v3.1 -> v3.2
  66. if (options.remark && options.remark.yaml) {
  67. delete options.remark.yaml
  68. }
  69. if (options.content.mathjax === undefined) {
  70. options.content.mathjax = false
  71. }
  72. Object.keys(md).forEach((compiler) => {
  73. if (!options[compiler]) {
  74. options[compiler] = md[compiler].defaults
  75. }
  76. })
  77. chrome.storage.sync.set(options)
  78. state = JSON.parse(JSON.stringify(options))
  79. })
  80. function inject (id) {
  81. chrome.tabs.executeScript(id, {
  82. code: `
  83. document.querySelector('pre').style.visibility = 'hidden'
  84. var theme = '${state.theme}'
  85. var raw = ${state.raw}
  86. var content = ${JSON.stringify(state.content)}
  87. var compiler = '${state.compiler}'
  88. `,
  89. runAt: 'document_start'
  90. })
  91. chrome.tabs.insertCSS(id, {file: 'css/content.css', runAt: 'document_start'})
  92. chrome.tabs.insertCSS(id, {file: 'vendor/prism.css', runAt: 'document_start'})
  93. chrome.tabs.executeScript(id, {file: 'vendor/mithril.min.js', runAt: 'document_start'})
  94. chrome.tabs.executeScript(id, {file: 'vendor/prism.js', runAt: 'document_start'})
  95. if (state.content.emoji) {
  96. chrome.tabs.executeScript(id, {file: 'content/emoji.js', runAt: 'document_start'})
  97. }
  98. chrome.tabs.executeScript(id, {file: 'content/content.js', runAt: 'document_start'})
  99. }
  100. chrome.tabs.onUpdated.addListener((id, info, tab) => {
  101. if (info.status === 'loading') {
  102. chrome.tabs.executeScript(id, {
  103. code: `
  104. JSON.stringify({
  105. location: window.location,
  106. contentType: document.contentType,
  107. loaded: !!window.state
  108. })
  109. `,
  110. runAt: 'document_start'
  111. }, (res) => {
  112. if (chrome.runtime.lastError) {
  113. // origin not allowed
  114. return
  115. }
  116. try {
  117. var win = JSON.parse(res)
  118. }
  119. catch (err) {
  120. // JSON parse error
  121. return
  122. }
  123. if (win.loaded) {
  124. // anchor
  125. return
  126. }
  127. if (state.header && /text\/(?:x-)?markdown/i.test(win.contentType)) {
  128. inject(id)
  129. }
  130. else {
  131. var path =
  132. state.origins[win.location.origin] ||
  133. state.origins['*://' + win.location.host] ||
  134. state.origins['*://*']
  135. if (path && new RegExp(path).test(win.location.href)) {
  136. inject(id)
  137. }
  138. }
  139. })
  140. }
  141. })
  142. chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
  143. if (req.message === 'markdown') {
  144. var markdown = req.markdown
  145. if (state.content.mathjax) {
  146. var m = mathjax()
  147. markdown = m.tokenize(markdown)
  148. }
  149. var html = md[state.compiler].compile(markdown)
  150. if (state.content.mathjax) {
  151. html = m.detokenize(html)
  152. }
  153. sendResponse({message: 'html', html})
  154. }
  155. else if (req.message === 'settings') {
  156. sendResponse(Object.assign({}, state, {
  157. options: state[state.compiler],
  158. description: md[state.compiler].description,
  159. compilers: Object.keys(md)
  160. }))
  161. }
  162. else if (req.message === 'compiler.name') {
  163. set({compiler: req.compiler})
  164. sendResponse()
  165. notifyContent({message: 'reload'})
  166. }
  167. else if (req.message === 'compiler.options') {
  168. set({[req.compiler]: req.options})
  169. notifyContent({message: 'reload'})
  170. }
  171. else if (req.message === 'content') {
  172. set({content: req.content})
  173. notifyContent({message: 'reload'})
  174. }
  175. else if (req.message === 'defaults') {
  176. set(defaults)
  177. sendResponse()
  178. notifyContent({message: 'reload'})
  179. }
  180. else if (req.message === 'theme') {
  181. set({theme: req.theme})
  182. notifyContent({message: 'theme', theme: req.theme})
  183. }
  184. else if (req.message === 'raw') {
  185. set({raw: req.raw})
  186. notifyContent({message: 'raw', raw: req.raw})
  187. }
  188. else if (req.message === 'advanced') {
  189. chrome.management.getSelf((extension) => {
  190. chrome.tabs.create({url: extension.optionsUrl})
  191. })
  192. }
  193. else if (req.message === 'origins') {
  194. sendResponse({origins: state.origins, header: state.header})
  195. }
  196. else if (req.message === 'header') {
  197. set({header: req.header})
  198. }
  199. else if (req.message === 'add') {
  200. state.origins[req.origin] = match
  201. set({origins: state.origins})
  202. sendResponse()
  203. }
  204. else if (req.message === 'remove') {
  205. delete state.origins[req.origin]
  206. set({origins: state.origins})
  207. sendResponse()
  208. }
  209. else if (req.message === 'update') {
  210. state.origins[req.origin] = req.match
  211. set({origins: state.origins})
  212. }
  213. return true
  214. })
  215. function notifyContent (req, res) {
  216. chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
  217. chrome.tabs.sendMessage(tabs[0].id, req, res)
  218. })
  219. }