background.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  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. // reload extension bug
  73. chrome.permissions.getAll((permissions) => {
  74. var origins = Object.keys(res.origins || {})
  75. chrome.permissions.remove({
  76. origins: permissions.origins
  77. .filter((origin) => origins.indexOf(origin.slice(0, -2)) === -1)
  78. })
  79. })
  80. Object.keys(md).forEach((compiler) => {
  81. if (!options[compiler]) {
  82. options[compiler] = md[compiler].defaults
  83. }
  84. })
  85. chrome.storage.sync.set(options)
  86. state = JSON.parse(JSON.stringify(options))
  87. })
  88. function inject (id) {
  89. chrome.tabs.executeScript(id, {
  90. code: `
  91. document.querySelector('pre').style.visibility = 'hidden'
  92. var theme = '${state.theme}'
  93. var raw = ${state.raw}
  94. var content = ${JSON.stringify(state.content)}
  95. var compiler = '${state.compiler}'
  96. `,
  97. runAt: 'document_start'
  98. })
  99. chrome.tabs.insertCSS(id, {file: 'css/content.css', runAt: 'document_start'})
  100. chrome.tabs.insertCSS(id, {file: 'vendor/prism.css', runAt: 'document_start'})
  101. chrome.tabs.executeScript(id, {file: 'vendor/mithril.min.js', runAt: 'document_start'})
  102. chrome.tabs.executeScript(id, {file: 'vendor/prism.js', runAt: 'document_start'})
  103. if (state.content.emoji) {
  104. chrome.tabs.executeScript(id, {file: 'content/emoji.js', runAt: 'document_start'})
  105. }
  106. chrome.tabs.executeScript(id, {file: 'content/content.js', runAt: 'document_start'})
  107. }
  108. chrome.tabs.onUpdated.addListener((id, info, tab) => {
  109. if (info.status === 'loading') {
  110. chrome.tabs.executeScript(id, {
  111. code: `
  112. JSON.stringify({
  113. location: window.location,
  114. contentType: document.contentType,
  115. loaded: !!window.state
  116. })
  117. `,
  118. runAt: 'document_start'
  119. }, (res) => {
  120. if (chrome.runtime.lastError) {
  121. // origin not allowed
  122. return
  123. }
  124. try {
  125. var win = JSON.parse(res)
  126. }
  127. catch (err) {
  128. // JSON parse error
  129. return
  130. }
  131. if (win.loaded) {
  132. // anchor
  133. return
  134. }
  135. if (state.header && /text\/(?:x-)?markdown/i.test(win.contentType)) {
  136. inject(id)
  137. }
  138. else {
  139. var path =
  140. state.origins[win.location.origin] ||
  141. state.origins['*://' + win.location.host] ||
  142. state.origins['*://*']
  143. if (path && new RegExp(path).test(win.location.href)) {
  144. inject(id)
  145. }
  146. }
  147. })
  148. }
  149. })
  150. chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
  151. if (req.message === 'markdown') {
  152. var markdown = req.markdown
  153. if (state.content.mathjax) {
  154. var m = mathjax()
  155. markdown = m.tokenize(markdown)
  156. }
  157. var html = md[state.compiler].compile(markdown)
  158. if (state.content.mathjax) {
  159. html = m.detokenize(html)
  160. }
  161. sendResponse({message: 'html', html})
  162. }
  163. else if (req.message === 'settings') {
  164. sendResponse(Object.assign({}, state, {
  165. options: state[state.compiler],
  166. description: md[state.compiler].description,
  167. compilers: Object.keys(md)
  168. }))
  169. }
  170. else if (req.message === 'compiler.name') {
  171. set({compiler: req.compiler})
  172. sendResponse()
  173. notifyContent({message: 'reload'})
  174. }
  175. else if (req.message === 'compiler.options') {
  176. set({[req.compiler]: req.options})
  177. notifyContent({message: 'reload'})
  178. }
  179. else if (req.message === 'content') {
  180. set({content: req.content})
  181. notifyContent({message: 'reload'})
  182. }
  183. else if (req.message === 'defaults') {
  184. var options = Object.assign({}, defaults)
  185. options.origins = state.origins
  186. set(options)
  187. sendResponse()
  188. notifyContent({message: 'reload'})
  189. }
  190. else if (req.message === 'theme') {
  191. set({theme: req.theme})
  192. notifyContent({message: 'theme', theme: req.theme})
  193. }
  194. else if (req.message === 'raw') {
  195. set({raw: req.raw})
  196. notifyContent({message: 'raw', raw: req.raw})
  197. }
  198. else if (req.message === 'advanced') {
  199. chrome.management.getSelf((extension) => {
  200. chrome.tabs.create({url: extension.optionsUrl})
  201. })
  202. }
  203. else if (req.message === 'origins') {
  204. sendResponse({origins: state.origins, header: state.header})
  205. }
  206. else if (req.message === 'header') {
  207. set({header: req.header})
  208. }
  209. else if (req.message === 'add') {
  210. state.origins[req.origin] = match
  211. set({origins: state.origins})
  212. sendResponse()
  213. }
  214. else if (req.message === 'remove') {
  215. delete state.origins[req.origin]
  216. set({origins: state.origins})
  217. sendResponse()
  218. }
  219. else if (req.message === 'update') {
  220. state.origins[req.origin] = req.match
  221. set({origins: state.origins})
  222. }
  223. return true
  224. })
  225. function notifyContent (req, res) {
  226. chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
  227. chrome.tabs.sendMessage(tabs[0].id, req, res)
  228. })
  229. }