瀏覽代碼

Add exclude domain option

simov 8 年之前
父節點
當前提交
fe6f155e91
共有 4 個文件被更改,包括 332 次插入173 次删除
  1. 79 27
      background/background.js
  2. 154 79
      content/options.js
  3. 14 14
      content/popup.js
  4. 85 53
      css/options.css

+ 79 - 27
background/background.js

@@ -18,7 +18,13 @@ var defaults = {
   match,
   origins: {
     'file://': match
-  }
+  },
+  exclude: [
+    'bitbucket.org',
+    'getcomposer.org',
+    'github.com',
+    'gitlab.com',
+  ]
 }
 Object.keys(md).forEach((compiler) => {
   defaults[compiler] = md[compiler].defaults
@@ -76,6 +82,10 @@ chrome.storage.sync.get((res) => {
   if (options.content.mathjax === undefined) {
     options.content.mathjax = false
   }
+  // v3.2 -> v3.3
+  if (options.exclude === undefined) {
+    options.exclude = defaults.exclude
+  }
 
   // reload extension bug
   chrome.permissions.getAll((permissions) => {
@@ -126,7 +136,7 @@ chrome.tabs.onUpdated.addListener((id, info, tab) => {
         JSON.stringify({
           location: window.location,
           contentType: document.contentType,
-          loaded: !!window.state
+          loaded: !!window.state,
         })
       `,
       runAt: 'document_start'
@@ -149,6 +159,12 @@ chrome.tabs.onUpdated.addListener((id, info, tab) => {
         return
       }
 
+      if (state.origins['*://*'] &&
+        state.exclude.some((domain) => domain === win.location.host)) {
+        // excluded domain
+        return
+      }
+
       if (state.header && /text\/(?:x-)?markdown/i.test(win.contentType)) {
         inject(id)
       }
@@ -167,6 +183,7 @@ chrome.tabs.onUpdated.addListener((id, info, tab) => {
 })
 
 chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
+  // content
   if (req.message === 'markdown') {
     var markdown = req.markdown
 
@@ -183,66 +200,101 @@ chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
 
     sendResponse({message: 'html', html})
   }
-  else if (req.message === 'settings') {
+
+  // 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 === 'compiler.name') {
-    set({compiler: req.compiler})
+  else if (req.message === 'popup.theme') {
+    set({theme: req.theme})
+    notifyContent({message: 'theme', theme: req.theme})
     sendResponse()
-    notifyContent({message: 'reload'})
-  }
-  else if (req.message === 'compiler.options') {
-    set({[req.compiler]: req.options})
-    notifyContent({message: 'reload'})
   }
-  else if (req.message === 'content') {
-    set({content: req.content})
-    notifyContent({message: 'reload'})
+  else if (req.message === 'popup.raw') {
+    set({raw: req.raw})
+    notifyContent({message: 'raw', raw: req.raw})
+    sendResponse()
   }
-  else if (req.message === 'defaults') {
+  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 === 'theme') {
-    set({theme: req.theme})
-    notifyContent({message: 'theme', theme: req.theme})
+  else if (req.message === 'popup.compiler.options') {
+    set({[req.compiler]: req.options})
+    notifyContent({message: 'reload'})
+    sendResponse()
   }
-  else if (req.message === 'raw') {
-    set({raw: req.raw})
-    notifyContent({message: 'raw', raw: req.raw})
+  else if (req.message === 'popup.content') {
+    set({content: req.content})
+    notifyContent({message: 'reload'})
+    sendResponse()
   }
-  else if (req.message === 'advanced') {
+  else if (req.message === 'popup.advanced') {
     chrome.management.getSelf((extension) => {
       chrome.tabs.create({url: extension.optionsUrl})
     })
+    sendResponse()
   }
-  else if (req.message === 'origins') {
-    sendResponse({origins: state.origins, header: state.header})
+
+  // options
+  else if (req.message === 'options') {
+    sendResponse({
+      origins: state.origins,
+      header: state.header,
+      exclude: state.exclude,
+    })
   }
-  else if (req.message === 'header') {
+  else if (req.message === 'options.header') {
     set({header: req.header})
+    sendResponse()
   }
-  else if (req.message === 'add') {
+
+  // options origins
+  else if (req.message === 'origin.add') {
     state.origins[req.origin] = match
     set({origins: state.origins})
     sendResponse()
   }
-  else if (req.message === 'remove') {
+  else if (req.message === 'origin.remove') {
     delete state.origins[req.origin]
     set({origins: state.origins})
     sendResponse()
   }
-  else if (req.message === 'update') {
+  else if (req.message === 'origin.update') {
     state.origins[req.origin] = req.match
     set({origins: state.origins})
+    sendResponse()
+  }
+
+  // options domains
+  else if (req.message === 'domain.add') {
+    state.exclude.push(req.domain)
+    set({exclude: state.exclude})
+    sendResponse()
   }
+  else if (req.message === 'domain.remove') {
+    state.exclude.splice(req.index, 1)
+    set({exclude: state.exclude})
+    sendResponse()
+  }
+  else if (req.message === 'domain.defaults') {
+    state.exclude = defaults.exclude.slice()
+    set({exclude: state.exclude})
+    sendResponse()
+  }
+
   return true
 })
 

+ 154 - 79
content/options.js

@@ -1,87 +1,119 @@
 
-var state = {
-  protocol: 'https',
-  protocols: ['https', 'http', '*'],
-  origin: '',
+var defaults = {
+  // storage
   origins: {},
   header: false,
+  exclude: [],
+  // static
+  protocols: ['https', 'http', '*'],
+  // UI
+  protocol: 'https',
+  origin: '',
+  domain: '',
   timeout: null,
-  file: true
+  file: true,
 }
+var state = Object.assign({}, defaults)
 
 var events = {
   file: () => {
     chrome.tabs.create({url: 'chrome://extensions/?id=' + chrome.runtime.id})
   },
 
-  protocol: (e) => {
-    state.protocol = state.protocols[e.target.selectedIndex]
-  },
-
-  origin: (e) => {
-    state.origin = e.target.value
-  },
-
   header: (e) => {
     state.header = !state.header
-    chrome.runtime.sendMessage({message: 'header', header: state.header})
+    chrome.runtime.sendMessage({
+      message: 'options.header',
+      header: state.header,
+    })
   },
 
-  add: () => {
-    var host = state.origin
-      .replace(/^(file|http(s)?):\/\//, '')
-      .replace(/\/.*$/, '')
+  origin: {
+    protocol: (e) => {
+      state.protocol = state.protocols[e.target.selectedIndex]
+    },
 
-    if (!host) {
-      return
-    }
+    name: (e) => {
+      state.origin = e.target.value
+    },
 
-    var origin = state.protocol + '://' + host
-    chrome.permissions.request({origins: [origin + '/*']}, (granted) => {
-      if (granted) {
-        chrome.runtime.sendMessage({message: 'add', origin}, (res) => {
-          state.origin = ''
-          get()
-        })
+    add: () => {
+      var domain = state.origin.replace(/.*:\/\/([^/]+).*/i, '$1')
+      if (!domain) {
+        return
       }
-    })
-  },
+      var origin = state.protocol + '://' + domain
+      chrome.permissions.request({origins: [origin + '/*']}, (granted) => {
+        if (granted) {
+          chrome.runtime.sendMessage({message: 'origin.add', origin}, init)
+        }
+      })
+    },
+
+    all: () => {
+      var origin = '*://*'
+      chrome.permissions.request({origins: [origin + '/*']}, (granted) => {
+        if (granted) {
+          chrome.runtime.sendMessage({message: 'origin.add', origin}, init)
+        }
+      })
+    },
 
-  all: () => {
-    var origin = '*://*'
-    chrome.permissions.request({origins: [origin + '/*']}, (granted) => {
-      if (granted) {
-        chrome.runtime.sendMessage({message: 'add', origin}, (res) => {
-          state.origin = ''
-          get()
+    remove: (origin) => () => {
+      chrome.permissions.remove({origins: [origin + '/*']}, (removed) => {
+        if (removed) {
+          chrome.runtime.sendMessage({message: 'origin.remove', origin}, init)
+        }
+      })
+    },
+
+    update: (origin) => (e) => {
+      state.origins[origin] = e.target.value
+      clearTimeout(state.timeout)
+      state.timeout = setTimeout(() => {
+        chrome.runtime.sendMessage({
+          message: 'origin.update', origin, match: e.target.value
         })
-      }
-    })
+      }, 750)
+    },
+
+    refresh: (origin) => () => {
+      chrome.permissions.request({origins: [origin + '/*']})
+    },
   },
 
-  remove: (origin) => () => {
-    chrome.permissions.remove({origins: [origin + '/*']}, (removed) => {
-      if (removed) {
-        chrome.runtime.sendMessage({message: 'remove', origin}, (res) => {
-          get()
-        })
+  domain: {
+    name: (e) => {
+      state.domain = e.target.value
+    },
+
+    add: () => {
+      var domain = state.domain.replace(/.*:\/\/([^/]+).*/i, '$1')
+      if (!domain) {
+        return
       }
-    })
-  },
+      if (state.exclude.includes(domain)) {
+        return
+      }
+      chrome.runtime.sendMessage({
+        message: 'domain.add',
+        domain,
+      }, init)
+    },
 
-  update: (origin) => (e) => {
-    state.origins[origin] = e.target.value
-    clearTimeout(state.timeout)
-    state.timeout = setTimeout(() => {
+    remove: (domain) => () => {
       chrome.runtime.sendMessage({
-        message: 'update', origin, match: e.target.value
-      }, (res) => {})
-    }, 750)
-  },
+        message: 'domain.remove',
+        index: state.exclude.indexOf(domain),
+      }, init)
+    },
 
-  refresh: (origin) => () => {
-    chrome.permissions.request({origins: [origin + '/*']}, (granted) => {})
-  }
+    defaults: () => {
+      chrome.runtime.sendMessage({
+        message: 'domain.defaults',
+      }, init)
+    },
+  },
 }
 
 chrome.extension.isAllowedFileSchemeAccess((isAllowedAccess) => {
@@ -89,15 +121,14 @@ chrome.extension.isAllowedFileSchemeAccess((isAllowedAccess) => {
   m.redraw()
 })
 
-var get = () => {
-  chrome.runtime.sendMessage({message: 'origins'}, (res) => {
-    state.origins = res.origins
-    state.header = res.header
+var init = () => {
+  chrome.runtime.sendMessage({message: 'options'}, (res) => {
+    state = Object.assign({}, defaults, {file: state.file}, res)
     m.redraw()
   })
 }
 
-get()
+init()
 
 var oncreate = {
   ripple: (vnode) => {
@@ -119,7 +150,7 @@ m.mount(document.querySelector('main'), {
 
       // file access is disabled
       (!state.file || null) &&
-      m('.bs-callout m-file-access',
+      m('.bs-callout m-file',
         m('h4.mdc-typography--headline', 'Access to file:// URLs is Disabled'),
         m('img.mdc-elevation--z2', {src: '/images/file-urls.png'}),
         m('button.mdc-button mdc-button--raised m-button', {
@@ -130,11 +161,13 @@ m.mount(document.querySelector('main'), {
         )
       ),
 
-      // add new origin
-      m('.bs-callout m-add-new-origin',
-        m('h4.mdc-typography--headline', 'Add New Origin'),
+      // allowed origins
+      m('.bs-callout m-origins',
+        m('h4.mdc-typography--headline', 'Allowed Origins'),
+
+        // add origin
         m('select.mdc-elevation--z2 m-select', {
-          onchange: events.protocol
+          onchange: events.origin.protocol
           },
           state.protocols.map((protocol) =>
           m('option', {
@@ -147,27 +180,24 @@ m.mount(document.querySelector('main'), {
           m('input.mdc-textfield__input', {
             type: 'text',
             value: state.origin,
-            onchange: events.origin,
+            onchange: events.origin.name,
             placeholder: 'raw.githubusercontent.com'
           })
         ),
         m('button.mdc-button mdc-button--raised m-button', {
           oncreate: oncreate.ripple,
-          onclick: events.add
+          onclick: events.origin.add
           },
           'Add'
         ),
         m('button.mdc-button mdc-button--raised m-button', {
           oncreate: oncreate.ripple,
-          onclick: events.all
+          onclick: events.origin.all
           },
           'Allow All'
-        )
-      ),
+        ),
 
-      // allowed origins
-      m('.bs-callout m-allowed-origins',
-        m('h4.mdc-typography--headline', 'Allowed Origins'),
+        // header detection
         m('label.mdc-switch m-switch', {
           onupdate: onupdate.switch,
           title: 'Toggle header detection'
@@ -197,7 +227,7 @@ m.mount(document.querySelector('main'), {
                 },
                 m('input.mdc-textfield__input', {
                   type: 'text',
-                  onkeyup: events.update(origin),
+                  onkeyup: events.origin.update(origin),
                   value: state.origins[origin],
                 })
               ),
@@ -205,14 +235,14 @@ m.mount(document.querySelector('main'), {
               m('span',
                 m('button.mdc-button', {
                   oncreate: oncreate.ripple,
-                  onclick: events.refresh(origin),
+                  onclick: events.origin.refresh(origin),
                   title: 'Refresh'
                   },
                   m('i.material-icons icon-refresh')
                 ),
                 m('button.mdc-button', {
                   oncreate: oncreate.ripple,
-                  onclick: events.remove(origin),
+                  onclick: events.origin.remove(origin),
                   title: 'Remove'
                   },
                   m('i.material-icons icon-remove')
@@ -221,6 +251,51 @@ m.mount(document.querySelector('main'), {
             )
           )
         )
+      ),
+
+      // excluded domains
+      (state.origins['*://*'] || null) &&
+      m('.bs-callout m-exclude',
+        m('h4.mdc-typography--headline', 'Excluded Domains'),
+
+        m('.mdc-textfield m-textfield',
+          m('input.mdc-textfield__input', {
+            type: 'text',
+            value: state.domain,
+            onchange: events.domain.name,
+            placeholder: 'github.com'
+          })
+        ),
+        m('button.mdc-button mdc-button--raised m-button', {
+          oncreate: oncreate.ripple,
+          onclick: events.domain.add
+          },
+          'Add'
+        ),
+        m('button.mdc-button mdc-button--raised m-button', {
+          oncreate: oncreate.ripple,
+          onclick: events.domain.defaults
+          },
+          'Defaults'
+        ),
+
+        m('ul.mdc-elevation--z2 m-list',
+          state.exclude.sort().map((domain) =>
+            m('li',
+              m('span', domain),
+              (origin !== 'file://' || null) &&
+              m('span',
+                m('button.mdc-button', {
+                  oncreate: oncreate.ripple,
+                  onclick: events.domain.remove(domain),
+                  title: 'Remove'
+                  },
+                  m('i.material-icons icon-remove')
+                )
+              )
+            )
+          )
+        ),
       )
     )
 })

+ 14 - 14
content/popup.js

@@ -31,18 +31,18 @@ var events = {
     name: (e) => {
       state.compiler = state.compilers[e.target.selectedIndex]
       chrome.runtime.sendMessage({
-        message: 'compiler.name',
-        compiler: state.compiler
+        message: 'popup.compiler.name',
+        compiler: state.compiler,
       }, () => {
-        chrome.runtime.sendMessage({message: 'settings'}, init)
+        chrome.runtime.sendMessage({message: 'popup'}, init)
       })
     },
     options: (e) => {
       state.options[e.target.name] = !state.options[e.target.name]
       chrome.runtime.sendMessage({
-        message: 'compiler.options',
+        message: 'popup.compiler.options',
         compiler: state.compiler,
-        options: state.options
+        options: state.options,
       })
     }
   },
@@ -50,15 +50,15 @@ var events = {
   content: (e) => {
     state.content[e.target.name] = !state.content[e.target.name]
     chrome.runtime.sendMessage({
-      message: 'content',
-      content: state.content
+      message: 'popup.content',
+      content: state.content,
     })
   },
 
   theme: (e) => {
     state.theme = state.themes[e.target.selectedIndex]
     chrome.runtime.sendMessage({
-      message: 'theme',
+      message: 'popup.theme',
       theme: state.theme
     })
   },
@@ -66,23 +66,23 @@ var events = {
   raw: () => {
     state.raw = !state.raw
     chrome.runtime.sendMessage({
-      message: 'raw',
+      message: 'popup.raw',
       raw: state.raw
     })
   },
 
   defaults: () => {
     chrome.runtime.sendMessage({
-      message: 'defaults'
+      message: 'popup.defaults'
     }, () => {
-      chrome.runtime.sendMessage({message: 'settings'}, init)
+      chrome.runtime.sendMessage({message: 'popup'}, init)
       localStorage.removeItem('tab')
       state._tabs.activeTabIndex = 0
     })
   },
 
   advanced: () => {
-    chrome.runtime.sendMessage({message: 'advanced'})
+    chrome.runtime.sendMessage({message: 'popup.advanced'})
   }
 }
 
@@ -104,7 +104,7 @@ var init = (res) => {
   m.redraw()
 }
 
-chrome.runtime.sendMessage({message: 'settings'}, init)
+chrome.runtime.sendMessage({message: 'popup'}, init)
 
 var oncreate = {
   ripple: (vnode) => {
@@ -114,7 +114,7 @@ var oncreate = {
     state._tabs = mdc.tabs.MDCTabBar.attachTo(vnode.dom)
     setTimeout(() => {
       state._tabs.activeTabIndex = state.tabs.indexOf(state.tab)
-    }, 100)
+    }, 250)
   }
 }
 

+ 85 - 53
css/options.css

@@ -56,6 +56,42 @@ footer a:nth-of-type(2):before {
 }
 
 
+/*custom scrollbars in WebKit*/
+::-webkit-scrollbar {
+  width: 13px;
+  height: 13px;
+}
+::-webkit-scrollbar-track-piece {
+  background: #ececec;
+  -webkit-border-radius: 2px;
+}
+::-webkit-scrollbar-thumb {
+  height: 50px;
+  background: #afbdc3;
+  -webkit-border-radius: 8px;
+  outline: 2px solid #333;
+  outline-offset: -2px;
+  border: 2px solid #ececec;
+}
+::-webkit-scrollbar-thumb:hover {
+  height: 50px;
+  background-color: #607d8b;
+  -webkit-border-radius: 8px;
+}
+
+
+/*bootstrap callout*/
+.bs-callout {
+  border: 1px solid #eee;
+  border-left-width: 5px;
+  border-left-color: #607d8b;
+  border-radius: 3px;
+  background: #fcfcfc;
+  padding: 20px;
+  margin: 0 0 30px 0;
+}
+
+
 /*button*/
 .m-button {
   background-color: #ececec !important;
@@ -110,7 +146,9 @@ footer a:nth-of-type(2):before {
   padding: 9px 12px;
 }
 
-#options .m-textfield {
+
+/*textfield*/
+.m-textfield {
   font-size: 1rem;
   line-height: 1.5rem;
   height: auto;
@@ -118,18 +156,10 @@ footer a:nth-of-type(2):before {
 }
 
 
-/*add new origin*/
-.m-add-new-origin .m-textfield {
-  width: 400px;
-  margin: 0 10px !important;
-}
-.m-add-new-origin button:nth-of-type(2) { float: right; }
-.m-add-new-origin .m-select { width: 110px; }
-
-
-/*allowed-origins*/
+/*list*/
 .m-list {
   list-style: none;
+  clear: both;
   background: #fff;
   padding: 0;
   margin: 0;
@@ -143,18 +173,10 @@ footer a:nth-of-type(2):before {
   border-bottom: 1px solid #ececec;
   padding: 10px 20px;
 }
-.m-list li:last-child {
-  border: 0;
-}
-.m-list li:hover {
-  background: #eee;
-}
+.m-list li:last-child { border: 0; }
+.m-list li:hover { background: #eee; }
 .m-list li:after { content: ''; display: block; clear: both; }
 .m-list li > * { display: inline-block; }
-.m-list li > *:nth-child(1) { width: 5%; }
-.m-list li > *:nth-child(2) { width: 30%; }
-.m-list li > *:nth-child(3) { width: 55%; }
-.m-list li > *:nth-child(4) { width: 10%; text-align: right; }
 .m-list li span {
   font-size: 1rem;
   line-height: 36px;
@@ -172,47 +194,57 @@ footer a:nth-of-type(2):before {
 .m-list button:hover { background: #cacaca; }
 
 
-/*bootstrap callout*/
-.bs-callout {
-  border: 1px solid #eee;
-  border-left-width: 5px;
-  border-left-color: #607d8b;
-  border-radius: 3px;
-  background: #fcfcfc;
-  padding: 20px;
-  margin: 0 0 30px 0;
-}
-.bs-callout h4 {
+/*file access*/
+.m-file h4 {
   margin: 0 0 16px 0;
 }
-.bs-callout img {
+.m-file img {
   display: block;
   margin-bottom: 20px;
 }
-.bs-callout .m-switch {
-  margin: 0 0 20px 0;
+
+
+/*allowed origins*/
+.m-origins h4 {
+  line-height: 36px;
+  float: left;
+  padding: 0 100px 0 0;
+  margin: 0 !important;
+}
+.m-origins > .m-textfield:first-of-type {
+  width: 400px;
+  margin: 0 10px !important;
+}
+.m-origins button:nth-of-type(2) { float: right; }
+.m-origins .m-select { width: 110px; }
+.m-origins .m-switch {
+  margin: 20px 0 20px 0;
+}
+.m-origins .m-list {
+  clear: both;
 }
+.m-origins .m-list li > *:nth-child(1) { width: 7%; }
+.m-origins .m-list li > *:nth-child(2) { width: 30%; }
+.m-origins .m-list li > *:nth-child(3) { width: 54%; }
+.m-origins .m-list li > *:nth-child(4) { width: 9%; text-align: right; }
 
 
-/*custom scrollbars in WebKit*/
-::-webkit-scrollbar {
-  width: 13px;
-  height: 13px;
+/*excluded domains*/
+.m-exclude h4 {
+  line-height: 36px;
+  float: left;
+  margin: 0 180px 20px 0 !important;
 }
-::-webkit-scrollbar-track-piece {
-  background: #ececec;
-  -webkit-border-radius: 2px;
+.m-exclude .m-textfield {
+  float: left;
+  width: 400px;
+  margin: 0 10px 0 0 !important;
 }
-::-webkit-scrollbar-thumb {
-  height: 50px;
-  background: #afbdc3;
-  -webkit-border-radius: 8px;
-  outline: 2px solid #333;
-  outline-offset: -2px;
-  border: 2px solid #ececec;
+.m-exclude > .m-button {
+  float: left;
 }
-::-webkit-scrollbar-thumb:hover {
-  height: 50px;
-  background-color: #607d8b;
-  -webkit-border-radius: 8px;
+.m-exclude button:nth-of-type(2) {
+  float: right;
 }
+.m-exclude .m-list li > *:nth-child(1) { width: 95% !important; }
+.m-exclude .m-list li > *:nth-child(2) { width: 5%; text-align: right; }