1
0
simov 7 жил өмнө
parent
commit
c0944e6c95

+ 0 - 325
background/background.js

@@ -1,325 +0,0 @@
-
-// chrome.storage.sync.clear()
-// chrome.permissions.getAll((p) => chrome.permissions.remove({origins: p.origins}))
-
-var match = '\\.(?:markdown|mdown|mkdn|md|mkd|mdwn|mdtxt|mdtext|text)(?:#.*|\\?.*)?$'
-
-var defaults = {
-  theme: 'github',
-  compiler: 'marked',
-  content: {
-    emoji: false,
-    scroll: true,
-    toc: false,
-    mathjax: false,
-  },
-  raw: false,
-  header: true,
-  csp: false,
-  match,
-  origins: {
-    'file://': match
-  },
-}
-Object.keys(md).forEach((compiler) => {
-  defaults[compiler] = md[compiler].defaults
-})
-
-var state
-
-var onwakeup = true
-
-function set (options) {
-  chrome.storage.sync.set(options)
-  Object.assign(state, options)
-}
-
-chrome.storage.sync.get((res) => {
-  var options = !Object.keys(res).length ? defaults : res
-
-  // v2.2 -> v2.3
-  if (!options.match || !options.origins) {
-    options.match = match
-    options.origins = {
-      'file://': match
-    }
-  }
-  // v2.3 -> v2.4
-  else if (!options.origins['file://']) {
-    options.origins['file://'] = match
-  }
-  // v2.4 -> v2.5
-  if (!options.compiler) {
-    options.compiler = options.options
-  }
-  if (!options.content) {
-    options.content = defaults.content
-  }
-  // v2.7 -> v2.8
-  if (!options.marked) {
-    options.compiler = 'marked'
-    options.marked = md.marked.defaults
-  }
-  // v2.8 -> v2.9
-  if (!options.remark) {
-    options.remark = md.remark.defaults
-  }
-  // v2.9 -> v3.0
-  if (options.content.emoji === undefined) {
-    options.content.emoji = false
-  }
-  // v3.0 -> v3.1
-  if (options.header === undefined) {
-    options.header = true
-  }
-  // v3.1 -> v3.2
-  if (options.remark && options.remark.yaml) {
-    delete options.remark.yaml
-  }
-  if (options.content.mathjax === undefined) {
-    options.content.mathjax = false
-  }
-  // v3.3 -> v3.4
-  if (options.csp === undefined) {
-    options.csp = false
-  }
-
-  // reload extension bug
-  chrome.permissions.getAll((permissions) => {
-    var origins = Object.keys(res.origins || {})
-    chrome.permissions.remove({
-      origins: permissions.origins
-        .filter((origin) => origins.indexOf(origin.slice(0, -2)) === -1)
-    })
-  })
-
-  Object.keys(md).forEach((compiler) => {
-    if (!options[compiler]) {
-      options[compiler] = md[compiler].defaults
-    }
-  })
-
-  chrome.storage.sync.set(options)
-  state = JSON.parse(JSON.stringify(options))
-})
-
-function inject (id) {
-  if (onwakeup && state.csp) {
-    onwakeup = false
-    chrome.tabs.reload(id)
-  }
-
-  chrome.tabs.executeScript(id, {
-    code: `
-      document.querySelector('pre').style.visibility = 'hidden'
-      var theme = '${state.theme}'
-      var raw = ${state.raw}
-      var content = ${JSON.stringify(state.content)}
-      var compiler = '${state.compiler}'
-    `,
-    runAt: 'document_start'
-  })
-
-  chrome.tabs.insertCSS(id, {file: 'css/content.css', runAt: 'document_start'})
-  chrome.tabs.insertCSS(id, {file: 'vendor/prism.css', runAt: 'document_start'})
-
-  chrome.tabs.executeScript(id, {file: 'vendor/mithril.min.js', runAt: 'document_start'})
-  chrome.tabs.executeScript(id, {file: 'vendor/prism.js', runAt: 'document_start'})
-  if (state.content.emoji) {
-    chrome.tabs.executeScript(id, {file: 'content/emoji.js', runAt: 'document_start'})
-  }
-  chrome.tabs.executeScript(id, {file: 'content/content.js', runAt: 'document_start'})
-}
-
-chrome.tabs.onUpdated.addListener((id, info, tab) => {
-  if (info.status === 'loading') {
-    chrome.tabs.executeScript(id, {
-      code: `
-        JSON.stringify({
-          location: window.location,
-          contentType: document.contentType,
-          loaded: !!window.state,
-        })
-      `,
-      runAt: 'document_start'
-    }, (res) => {
-      if (chrome.runtime.lastError) {
-        // origin not allowed
-        return
-      }
-
-      try {
-        var win = JSON.parse(res)
-      }
-      catch (err) {
-        // JSON parse error
-        return
-      }
-
-      if (win.loaded) {
-        // anchor
-        return
-      }
-
-      if (state.header && /text\/(?:x-)?markdown/i.test(win.contentType)) {
-        inject(id)
-      }
-      else {
-        var path =
-          state.origins[win.location.origin] ||
-          state.origins['*://' + win.location.host] ||
-          state.origins['*://*']
-
-        // ff: webRequest bug - does not match on `hostname:port`
-        if (!path && /Firefox/.test(navigator.userAgent)) {
-          var path =
-            state.origins[win.location.protocol + '//' + win.location.hostname] ||
-            state.origins['*://' + win.location.hostname] ||
-            state.origins['*://*']
-        }
-
-        if (path && new RegExp(path).test(win.location.href)) {
-          inject(id)
-        }
-      }
-    })
-  }
-})
-
-chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
-  // content
-  if (req.message === 'markdown') {
-    var markdown = req.markdown
-
-    if (state.content.mathjax) {
-      var m = mathjax()
-      markdown = m.tokenize(markdown)
-    }
-
-    var html = md[state.compiler].compile(markdown)
-
-    if (state.content.mathjax) {
-      html = m.detokenize(html)
-    }
-
-    sendResponse({message: 'html', html})
-  }
-
-  // popup
-  else if (req.message === 'popup') {
-    sendResponse(Object.assign({}, state, {
-      options: state[state.compiler],
-      description: md[state.compiler].description,
-      compilers: Object.keys(md)
-    }))
-  }
-  else if (req.message === 'popup.theme') {
-    set({theme: req.theme})
-    notifyContent({message: 'theme', theme: req.theme})
-    sendResponse()
-  }
-  else if (req.message === 'popup.raw') {
-    set({raw: req.raw})
-    notifyContent({message: 'raw', raw: req.raw})
-    sendResponse()
-  }
-  else if (req.message === 'popup.defaults') {
-    var options = Object.assign({}, defaults)
-    options.origins = state.origins
-    set(options)
-    notifyContent({message: 'reload'})
-    sendResponse()
-  }
-  else if (req.message === 'popup.compiler.name') {
-    set({compiler: req.compiler})
-    notifyContent({message: 'reload'})
-    sendResponse()
-  }
-  else if (req.message === 'popup.compiler.options') {
-    set({[req.compiler]: req.options})
-    notifyContent({message: 'reload'})
-    sendResponse()
-  }
-  else if (req.message === 'popup.content') {
-    set({content: req.content})
-    notifyContent({message: 'reload'})
-    sendResponse()
-  }
-  else if (req.message === 'popup.advanced') {
-    chrome.management.getSelf((extension) => {
-      chrome.tabs.create({url: extension.optionsUrl})
-    })
-    sendResponse()
-  }
-
-  // options
-  else if (req.message === 'options') {
-    sendResponse({
-      origins: state.origins,
-      header: state.header,
-      csp: state.csp,
-      exclude: state.exclude,
-    })
-  }
-  else if (req.message === 'options.header') {
-    set({header: req.header})
-    sendResponse()
-  }
-  else if (req.message === 'options.csp') {
-    chrome.webRequest.onHeadersReceived
-      [`${req.csp ? 'add' : 'remove'}Listener`](...onHeaderArgs)
-    set({csp: req.csp})
-    sendResponse()
-  }
-
-  // options origins
-  else if (req.message === 'origin.add') {
-    state.origins[req.origin] = match
-    set({origins: state.origins})
-    sendResponse()
-  }
-  else if (req.message === 'origin.remove') {
-    delete state.origins[req.origin]
-    set({origins: state.origins})
-    sendResponse()
-  }
-  else if (req.message === 'origin.update') {
-    state.origins[req.origin] = req.match
-    set({origins: state.origins})
-    sendResponse()
-  }
-
-  return true
-})
-
-function notifyContent (req, res) {
-  chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
-    chrome.tabs.sendMessage(tabs[0].id, req, res)
-  })
-}
-
-var onHeaderArgs = [
-  ({responseHeaders}) => ({
-    responseHeaders: responseHeaders
-      .filter(({name}) => name.toLowerCase() !== 'content-security-policy')
-      // ff: markdown `content-type` is not allowed
-      .map((header) => {
-        if (
-          /Firefox/.test(navigator.userAgent) &&
-          header.name.toLowerCase() === 'content-type' &&
-          /text\/(?:x-)?markdown/.test(header.value)
-        ) {
-          header.value = 'text/plain; charset=utf-8'
-        }
-        return header
-      })
-    }),
-    {
-      urls: ['<all_urls>'],
-      types: ['main_frame', 'sub_frame']
-    },
-    ['blocking', 'responseHeaders']
-]
-
-chrome.webRequest &&
-chrome.webRequest.onHeadersReceived.addListener(...onHeaderArgs)

+ 17 - 9
background/marked.js → background/compilers/marked.js

@@ -1,8 +1,8 @@
 
-var md = {}
+var md = {compilers: {}}
 
-md.marked = {
-  defaults: {
+md.compilers.marked = (() => {
+  var defaults = {
     breaks: false,
     gfm: true,
     pedantic: false,
@@ -11,8 +11,9 @@ md.marked = {
     smartypants: false,
     tables: true,
     langPrefix: 'language-' // prism
-  },
-  description: {
+  }
+
+  var description = {
     breaks: 'Enable GFM line breaks\n(requires the gfm option to be true)',
     gfm: 'Enable GFM\n(GitHub Flavored Markdown)',
     pedantic: 'Don\'t fix any of the original markdown\nbugs or poor behavior',
@@ -20,7 +21,14 @@ md.marked = {
     smartLists: 'Use smarter list behavior\nthan the original markdown',
     smartypants: 'Use "smart" typographic punctuation\nfor things like quotes and dashes',
     tables: 'Enable GFM tables\n(requires the gfm option to be true)'
-  },
-  compile: (markdown) =>
-    marked(markdown, state.marked)
-}
+  }
+
+  var ctor = ({storage: {state}}) => ({
+    defaults,
+    description,
+    compile: (markdown) =>
+      marked(markdown, state.marked)
+  })
+
+  return Object.assign(ctor, {defaults, description})
+})()

+ 23 - 15
background/remark.js → background/compilers/remark.js

@@ -1,6 +1,6 @@
 
-md.remark = {
-  defaults: {
+md.compilers.remark = (() => {
+  var defaults = {
     breaks: false,
     commonmark: false,
     footnotes: false,
@@ -8,22 +8,30 @@ md.remark = {
     pedantic: false,
     sanitize: false,
     // blocks (Array.<string>, default: list of block HTML elements)
-  },
-  description: {
+  }
+
+  var description = {
     breaks: 'Exposes newline characters inside paragraphs as breaks',
     commonmark: 'Toggle CommonMark mode',
     footnotes: 'Toggle reference footnotes and inline footnotes',
     gfm: 'Toggle GFM (GitHub Flavored Markdown)',
     pedantic: 'Don\'t fix any of the original markdown\nbugs or poor behavior',
     sanitize: 'Disable HTML tag rendering',
-  },
-  compile: (markdown) =>
-    remark.unified()
-      .use(remark.parse, state.remark)
-      .use(remark.stringify)
-      .use(remarkSlug)
-      .use(remarkFrontmatter, ['yaml', 'toml'])
-      .use(remarkHTML, state.remark) // sanitize
-      .processSync(markdown)
-      .contents
-}
+  }
+
+  var ctor = ({storage: {state}}) => ({
+    defaults,
+    description,
+    compile: (markdown) =>
+      remark.unified()
+        .use(remark.parse, state.remark)
+        .use(remark.stringify)
+        .use(remarkSlug)
+        .use(remarkFrontmatter, ['yaml', 'toml'])
+        .use(remarkHTML, state.remark) // sanitize
+        .processSync(markdown)
+        .contents
+  })
+
+  return Object.assign(ctor, {defaults, description})
+})()

+ 73 - 0
background/detect.js

@@ -0,0 +1,73 @@
+
+md.detect = ({storage: {state}, inject}) => {
+
+  var onwakeup = true
+
+  var code = `
+    JSON.stringify({
+      location: window.location,
+      contentType: document.contentType,
+      loaded: !!window.state,
+    })
+  `
+
+  var tab = (id) => {
+    chrome.tabs.executeScript(id, {code, runAt: 'document_start'}, (res) => {
+      if (chrome.runtime.lastError) {
+        // origin not allowed
+        return
+      }
+
+      try {
+        var win = JSON.parse(res)
+      }
+      catch (err) {
+        // JSON parse error
+        return
+      }
+
+      if (win.loaded) {
+        // anchor
+        return
+      }
+
+      if (state.header && /text\/(?:x-)?markdown/i.test(win.contentType)) {
+        allowed(id)
+      }
+      else {
+        var path =
+          state.origins[win.location.origin] ||
+          state.origins['*://' + win.location.host] ||
+          state.origins['*://*']
+
+        // ff: webRequest bug - does not match on `hostname:port`
+        if (!path && /Firefox/.test(navigator.userAgent)) {
+          var path =
+            state.origins[win.location.protocol + '//' + win.location.hostname] ||
+            state.origins['*://' + win.location.hostname] ||
+            state.origins['*://*']
+        }
+
+        if (path && new RegExp(path).test(win.location.href)) {
+          allowed(id)
+        }
+      }
+    })
+  }
+
+  var allowed = (id) => {
+    if (onwakeup && state.csp) {
+      onwakeup = false
+      chrome.tabs.reload(id)
+    }
+    else {
+      inject()
+    }
+  }
+
+  return (id, info, _tab) => {
+    if (info.status === 'loading') {
+      tab(id)
+    }
+  }
+}

+ 36 - 0
background/headers.js

@@ -0,0 +1,36 @@
+
+md.headers = ({storage: {state}}) => {
+
+  var callback = ({responseHeaders}) => ({
+    responseHeaders: responseHeaders
+      .filter(({name}) => !state.csp || !/content-security-policy/i.test(name))
+      // ff: markdown `content-type` is not allowed
+      .map((header) => {
+        if (
+          /Firefox/.test(navigator.userAgent) &&
+          header.name.toLowerCase() === 'content-type' &&
+          /text\/(?:x-)?markdown/.test(header.value)
+        ) {
+          header.value = 'text/plain; charset=utf-8'
+        }
+        return header
+      })
+    })
+
+  var filter = {
+    urls: ['<all_urls>'],
+    types: ['main_frame', 'sub_frame']
+  }
+
+  var options = ['blocking', 'responseHeaders']
+
+  var add = () => {
+    chrome.webRequest.onHeadersReceived.addListener(callback, filter, options)
+  }
+
+  var remove = () => {
+    chrome.webRequest.onHeadersReceived.removeListener(callback, filter, options)
+  }
+
+  return {add, remove}
+}

+ 22 - 0
background/index.js

@@ -0,0 +1,22 @@
+
+var storage = md.storage(md)
+
+var headers = md.headers({storage})
+var inject = md.inject({storage})
+var detect = md.detect({storage, inject})
+var mathjax = md.mathjax()
+
+var compilers = Object.keys(md.compilers)
+  .reduce((all, compiler) => (
+    all[compiler] = md.compilers[compiler]({storage}),
+    all
+  ), {})
+
+var messages = md.messages({storage, compilers, mathjax, headers})
+
+
+chrome.tabs.onUpdated.addListener(detect)
+
+chrome.runtime.onMessage.addListener(messages)
+
+chrome.webRequest && headers.add()

+ 24 - 0
background/inject.js

@@ -0,0 +1,24 @@
+
+md.inject = ({storage: {state}}) => (id) => {
+
+  chrome.tabs.executeScript(id, {
+    code: `
+      document.querySelector('pre').style.visibility = 'hidden'
+      var theme = '${state.theme}'
+      var raw = ${state.raw}
+      var content = ${JSON.stringify(state.content)}
+      var compiler = '${state.compiler}'
+    `,
+    runAt: 'document_start'
+  })
+
+  chrome.tabs.insertCSS(id, {file: 'css/content.css', runAt: 'document_start'})
+  chrome.tabs.insertCSS(id, {file: 'vendor/prism.css', runAt: 'document_start'})
+
+  chrome.tabs.executeScript(id, {file: 'vendor/mithril.min.js', runAt: 'document_start'})
+  chrome.tabs.executeScript(id, {file: 'vendor/prism.js', runAt: 'document_start'})
+  if (state.content.emoji) {
+    chrome.tabs.executeScript(id, {file: 'content/emoji.js', runAt: 'document_start'})
+  }
+  chrome.tabs.executeScript(id, {file: 'content/content.js', runAt: 'document_start'})
+}

+ 33 - 29
background/mathjax.js

@@ -1,32 +1,36 @@
 
-var mathjax = (
-  (
-    math = new RegExp([
-      /\$\$[^`][\s\S]+?\$\$/,
-      /\\\([^`][\s\S]+?\\\)/,
-      /\\\[[^`][\s\S]+?\\\]/,
-      /\\begin\{.*?\}[^`][\s\S]+?\\end\{.*?\}/,
-      /\$[^$`].+?\$/,
-    ].map((regex) => `(?:${regex.source})`).join('|'), 'gi'),
-    escape = (math) => math.replace(/[<>&]/gi, (symbol) =>
+md.mathjax = () => {
+
+  var delimiters = new RegExp([
+    /\$\$[^`][\s\S]+?\$\$/,
+    /\\\([^`][\s\S]+?\\\)/,
+    /\\\[[^`][\s\S]+?\\\]/,
+    /\\begin\{.*?\}[^`][\s\S]+?\\end\{.*?\}/,
+    /\$[^$`].+?\$/,
+  ]
+  .map((regex) => `(?:${regex.source})`).join('|'), 'gi')
+
+  var escape = (math) =>
+    math.replace(/[<>&]/gi, (symbol) =>
       symbol === '>' ? '&gt;' :
       symbol === '<' ? '&lt;' :
-      symbol === '&' ? '&amp;': null)
-  ) => () => (
-    (
-      map = {}
-    ) => ({
-      tokenize: (markdown) =>
-        markdown.replace(math, (str, offset) => (
-          map[offset] = str,
-          `?${offset}?`
-        )),
-      detokenize: (html) => (
-        Object.keys(map).forEach((offset) =>
-          html = html.replace(`?${offset}?`, () => escape(map[offset]))),
-        delete map,
-        html
-      )
-    })
-  )()
-)()
+      symbol === '&' ? '&amp;': null
+    )
+
+  var ctor = (map = {}) => ({
+    tokenize: (markdown) =>
+      markdown.replace(delimiters, (str, offset) => (
+        map[offset] = str,
+        `?${offset}?`
+      ))
+    ,
+    detokenize: (html) =>
+      Object.keys(map)
+        .reduce((html, offset) =>
+          html = html.replace(`?${offset}?`, () => escape(map[offset])),
+          html
+        )
+  })
+
+  return Object.assign(ctor, {delimiters, escape})
+}

+ 112 - 0
background/messages.js

@@ -0,0 +1,112 @@
+
+md.messages = ({storage: {defaults, state, set}, compilers, mathjax, headers}) => {
+
+  return (req, sender, sendResponse) => {
+
+    if (req.message === 'markdown') {
+      var markdown = req.markdown
+
+      if (state.content.mathjax) {
+        var jax = mathjax()
+        markdown = jax.tokenize(markdown)
+      }
+
+      var html = compilers[state.compiler].compile(markdown)
+
+      if (state.content.mathjax) {
+        html = jax.detokenize(html)
+      }
+
+      sendResponse({message: 'html', html})
+    }
+
+    // popup
+    else if (req.message === 'popup') {
+      sendResponse(Object.assign({}, state, {
+        options: state[state.compiler],
+        description: compilers[state.compiler].description,
+        compilers: Object.keys(compilers)
+      }))
+    }
+    else if (req.message === 'popup.theme') {
+      set({theme: req.theme})
+      notifyContent({message: 'theme', theme: req.theme})
+      sendResponse()
+    }
+    else if (req.message === 'popup.raw') {
+      set({raw: req.raw})
+      notifyContent({message: 'raw', raw: req.raw})
+      sendResponse()
+    }
+    else if (req.message === 'popup.defaults') {
+      var options = Object.assign({}, defaults)
+      options.origins = state.origins
+      set(options)
+      notifyContent({message: 'reload'})
+      sendResponse()
+    }
+    else if (req.message === 'popup.compiler.name') {
+      set({compiler: req.compiler})
+      notifyContent({message: 'reload'})
+      sendResponse()
+    }
+    else if (req.message === 'popup.compiler.options') {
+      set({[req.compiler]: req.options})
+      notifyContent({message: 'reload'})
+      sendResponse()
+    }
+    else if (req.message === 'popup.content') {
+      set({content: req.content})
+      notifyContent({message: 'reload'})
+      sendResponse()
+    }
+    else if (req.message === 'popup.advanced') {
+      chrome.management.getSelf((extension) => {
+        chrome.tabs.create({url: extension.optionsUrl})
+      })
+      sendResponse()
+    }
+
+    // options
+    else if (req.message === 'options') {
+      sendResponse({
+        origins: state.origins,
+        header: state.header,
+        csp: state.csp,
+        exclude: state.exclude,
+      })
+    }
+    else if (req.message === 'options.header') {
+      set({header: req.header})
+      sendResponse()
+    }
+    else if (req.message === 'options.csp') {
+      headers[req.csp ? 'add' : 'remove']()
+      set({csp: req.csp})
+      sendResponse()
+    }
+
+    // options origins
+    else if (req.message === 'origin.add') {
+      state.origins[req.origin] = defaults.match
+      set({origins: state.origins})
+      sendResponse()
+    }
+    else if (req.message === 'origin.remove') {
+      delete state.origins[req.origin]
+      set({origins: state.origins})
+      sendResponse()
+    }
+    else if (req.message === 'origin.update') {
+      state.origins[req.origin] = req.match
+      set({origins: state.origins})
+      sendResponse()
+    }
+  }
+
+  function notifyContent (req, res) {
+    chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
+      chrome.tabs.sendMessage(tabs[0].id, req, res)
+    })
+  }
+}

+ 107 - 0
background/storage.js

@@ -0,0 +1,107 @@
+
+// chrome.storage.sync.clear()
+// chrome.permissions.getAll((p) => chrome.permissions.remove({origins: p.origins}))
+
+md.storage = ({compilers}) => {
+  var match = '\\.(?:markdown|mdown|mkdn|md|mkd|mdwn|mdtxt|mdtext|text)(?:#.*|\\?.*)?$'
+
+  var defaults = {
+    theme: 'github',
+    compiler: 'marked',
+    content: {
+      emoji: false,
+      scroll: true,
+      toc: false,
+      mathjax: false,
+    },
+    raw: false,
+    header: true,
+    csp: false,
+    match,
+    origins: {
+      'file://': match
+    },
+  }
+
+  Object.keys(compilers).forEach((compiler) => {
+    defaults[compiler] = compilers[compiler].defaults
+  })
+
+  var state = {}
+
+  function set (options) {
+    chrome.storage.sync.set(options)
+    Object.assign(state, options)
+  }
+
+  chrome.storage.sync.get((res) => {
+    var options = !Object.keys(res).length ? defaults : res
+
+    // v2.2 -> v2.3
+    if (!options.match || !options.origins) {
+      options.match = match
+      options.origins = {
+        'file://': match
+      }
+    }
+    // v2.3 -> v2.4
+    else if (!options.origins['file://']) {
+      options.origins['file://'] = match
+    }
+    // v2.4 -> v2.5
+    if (!options.compiler) {
+      options.compiler = options.options
+    }
+    if (!options.content) {
+      options.content = defaults.content
+    }
+    // v2.7 -> v2.8
+    if (!options.marked) {
+      options.compiler = 'marked'
+      options.marked = compilers.marked.defaults
+    }
+    // v2.8 -> v2.9
+    if (!options.remark) {
+      options.remark = compilers.remark.defaults
+    }
+    // v2.9 -> v3.0
+    if (options.content.emoji === undefined) {
+      options.content.emoji = false
+    }
+    // v3.0 -> v3.1
+    if (options.header === undefined) {
+      options.header = true
+    }
+    // v3.1 -> v3.2
+    if (options.remark && options.remark.yaml) {
+      delete options.remark.yaml
+    }
+    if (options.content.mathjax === undefined) {
+      options.content.mathjax = false
+    }
+    // v3.3 -> v3.4
+    if (options.csp === undefined) {
+      options.csp = false
+    }
+
+    // reload extension bug
+    chrome.permissions.getAll((permissions) => {
+      var origins = Object.keys(res.origins || {})
+      chrome.permissions.remove({
+        origins: permissions.origins
+          .filter((origin) => origins.indexOf(origin.slice(0, -2)) === -1)
+      })
+    })
+
+    Object.keys(compilers).forEach((compiler) => {
+      if (!options[compiler]) {
+        options[compiler] = compilers[compiler].defaults
+      }
+    })
+
+    chrome.storage.sync.set(options)
+    Object.assign(state, JSON.parse(JSON.stringify(options)))
+  })
+
+  return {defaults, state, set}
+}

+ 11 - 3
manifest.json

@@ -22,10 +22,18 @@
       "/vendor/remark-html.min.js",
       "/vendor/remark-slug.min.js",
       "/vendor/remark-frontmatter.min.js",
+
+      "/background/compilers/marked.js",
+      "/background/compilers/remark.js",
+
+      "/background/storage.js",
+      "/background/headers.js",
+      "/background/detect.js",
+      "/background/inject.js",
+      "/background/messages.js",
       "/background/mathjax.js",
-      "/background/marked.js",
-      "/background/remark.js",
-      "/background/background.js"
+
+      "/background/index.js"
     ],
     "persistent": false
   },