[
  {
    "path": "README.md",
    "content": "# open-in\nSend URLs from one browser to another one\n\n## browser-specific projects\n\n* [open-in-chromium](https://github.com/andy-portmen/open-in-chromium)\n* [open-in-edge](https://github.com/andy-portmen/open-in-edge)\n* [open-in-ie](https://github.com/andy-portmen/open-in-ie)\n* [open-in-opera](https://github.com/andy-portmen/open-in-opera)\n* [open-in-firefox](https://github.com/andy-portmen/open-in-firefox)\n\n## general-purpose connector\n\n* https://github.com/andy-portmen/native-client\n"
  },
  {
    "path": "brave/config.js",
    "content": "'use strict';\n\nvar app = {\n  id: 'com.add0n.node',\n  tag: 'brave',\n  multiple: true\n};\n\napp.locale = {\n  name: 'brave',\n  current: 'Open Link in Brave Browser',\n  all: 'Open all Tabs in Brave Browser',\n  call: 'Open all Tabs in Brave Browser (Current window)',\n  example: 'example D:\\\\Brave\\\\Application\\\\Brave.exe'\n};\n\napp.runtime = {\n  mac: {\n    args: ['-a', 'Brave Browser']\n  },\n  linux: {\n    name: 'brave-browser'\n  },\n  windows: {\n    name: 'cmd',\n    args: ['/s/c', 'start', 'brave \"%url;\"'],\n    prgfiles: '%ProgramFiles(x86)%\\\\BraveSoftware\\\\Brave-Browser\\\\Application\\\\brave.exe'\n  }\n};\n"
  },
  {
    "path": "brave/manifest.json",
    "content": "{\r\n  \"name\": \"Open in Brave Browser\",\r\n  \"description\": \"Open current page, link, or all tabs in the Brave browser\",\r\n  \"version\": \"0.1.3\",\r\n  \"manifest_version\": 2,\r\n  \"permissions\": [\r\n    \"storage\",\r\n    \"tabs\",\r\n    \"contextMenus\",\r\n    \"nativeMessaging\"\r\n  ],\r\n  \"optional_permissions\": [\r\n    \"downloads\"\r\n  ],\r\n  \"background\": {\r\n    \"persistent\": false,\r\n    \"scripts\": [\r\n      \"config.js\",\r\n      \"common.js\"\r\n    ]\r\n  },\r\n  \"storage\": {\r\n    \"managed_schema\": \"schema.json\"\r\n  },\r\n  \"homepage_url\": \"https://add0n.com/open-in.html?from=brave\",\r\n  \"icons\": {\r\n    \"16\": \"data/icons/16.png\",\r\n    \"32\": \"data/icons/32.png\",\r\n    \"48\": \"data/icons/48.png\",\r\n    \"64\": \"data/icons/64.png\",\r\n    \"128\": \"data/icons/128.png\",\r\n    \"256\": \"data/icons/256.png\",\r\n    \"512\": \"data/icons/512.png\"\r\n  },\r\n  \"browser_action\": {},\r\n  \"content_scripts\": [{\r\n    \"matches\": [\"<all_urls>\"],\r\n    \"js\": [\"data/inject.js\"],\r\n    \"run_at\": \"document_start\",\r\n    \"all_frames\": true,\r\n    \"match_about_blank\": true\r\n  }],\r\n  \"options_ui\": {\r\n    \"page\": \"data/options/index.html\",\r\n    \"chrome_style\": true,\r\n    \"open_in_tab\": true\r\n  }\r\n}\r\n"
  },
  {
    "path": "chrome/README",
    "content": "moved to https://github.com/andy-portmen/open-in-chrome/\n"
  },
  {
    "path": "chromium/README",
    "content": "moved to https://github.com/andy-portmen/open-in-chromium/\n"
  },
  {
    "path": "common/common.js",
    "content": "/* globals app */\n'use strict';\n\nconst os = {\n  mac: navigator.userAgent.indexOf('Mac') !== -1,\n  linux: navigator.userAgent.indexOf('Linux') !== -1\n};\nconst isFirefox = navigator.userAgent.indexOf('Firefox') !== -1;\n\nfunction error(response) {\n  window.alert(`Something went wrong!\n\n-----\nCode: ${response.code}\nOutput: ${response.stdout}\nError: ${response.stderr}`);\n}\n\nfunction response(res, success = () => {}) {\n  // windows batch file returns 1\n  if (res && (res.code !== 0 && (res.code !== 1 || res.stderr !== ''))) {\n    error(res);\n  }\n  else if (!res) {\n    chrome.tabs.query({\n      url: chrome.runtime.getURL('data/helper/index.html')\n    }, tabs => {\n      if (tabs && tabs.length) {\n        chrome.tabs.update(tabs[0].id, {\n          active: true\n        }, () => {\n          chrome.windows.update(tabs[0].windowId, {\n            focused: true\n          });\n        });\n      }\n      else {\n        chrome.tabs.create({\n          url: 'data/helper/index.html'\n        });\n      }\n    });\n  }\n  else {\n    success();\n  }\n}\n\nfunction exec(command, args, callback, properties = {}) {\n  if (command) {\n    chrome.runtime.sendNativeMessage(app.id, {\n      cmd: 'exec',\n      command,\n      arguments: args,\n      properties\n    }, res => (callback || response)(res));\n  }\n  else {\n    window.alert(`Please set the \"${app.locale.name}\" executable path in the options page`);\n    chrome.runtime.openOptionsPage();\n  }\n}\n\nfunction find(callback) {\n  chrome.runtime.sendNativeMessage(app.id, {\n    cmd: 'env'\n  }, res => {\n    if (res && res.env && res.env.ProgramFiles) {\n      chrome.storage.local.set({\n        path: app.runtime.windows.prgfiles\n          .replace('%LOCALAPPDATA%', res.env.LOCALAPPDATA)\n          .replace('%ProgramFiles(x86)%', res.env['ProgramFiles(x86)'])\n          .replace('%ProgramFiles%', res.env.ProgramFiles)\n      }, callback);\n    }\n    else {\n      response(res);\n    }\n  });\n}\n\n\nconst open = (urls, closeIDs = []) => {\n  chrome.storage.local.get({\n    path: null,\n    closeme: false\n  }, prefs => {\n    const close = () => {\n      if (prefs.closeme && closeIDs.length) {\n        chrome.tabs.remove(closeIDs);\n      }\n    };\n    if (os.mac) {\n      if (prefs.path) {\n        const length = app.runtime.mac.args.length;\n        app.runtime.mac.args[length - 1] = prefs.path;\n      }\n      exec('open', [...app.runtime.mac.args, ...urls], r => response(r, close));\n    }\n    else if (os.linux) {\n      exec(prefs.path || app.runtime.linux.name, urls, r => response(r, close));\n    }\n    else {\n      if (prefs.path) {\n        exec(prefs.path, [...(app.runtime.windows.args2 || []), ...urls], r => response(r, close));\n      }\n      else {\n        const args = app.runtime.windows.args\n          .map(a => a.replace('%url;', urls.join(' ')))\n          // Firefox is not detaching the process on Windows\n          .map(s => s.replace('start', isFirefox ? 'start /WAIT' : 'start'));\n        exec(app.runtime.windows.name, args, res => {\n          // use old method\n          if (res && res.code !== 0) {\n            find(() => open(urls, closeIDs));\n          }\n          else {\n            response(res, close);\n          }\n        }, {windowsVerbatimArguments: true});\n      }\n    }\n  });\n};\n\nfunction delayOpen(tabs) {\n  chrome.storage.local.get({\n    multiple: app.multiple\n  }, prefs => {\n    if (prefs.multiple) {\n      return open(tabs.map(t => t.url), tabs.map(t => t.id));\n    }\n    const tab = tabs.shift();\n    if (tab) {\n      open([tab.url], [tab.id]);\n      window.setTimeout(delayOpen, 1000, tabs);\n    }\n  });\n}\n\nchrome.browserAction.onClicked.addListener(() => {\n  chrome.tabs.query({\n    active: true,\n    currentWindow: true\n  }, tabs => open(tabs.map(t => t.url), tabs.map(t => t.id)));\n});\n// context menu\n{\n  const callback = () => {\n    chrome.contextMenus.create({\n      id: 'open-current',\n      title: app.locale.current,\n      contexts: ['link'],\n      documentUrlPatterns: ['*://*/*']\n    });\n    chrome.contextMenus.create({\n      id: 'open-all',\n      title: app.locale.all,\n      contexts: ['browser_action']\n    });\n    chrome.contextMenus.create({\n      id: 'open-call',\n      title: app.locale.call,\n      contexts: ['browser_action']\n    });\n  };\n  chrome.runtime.onInstalled.addListener(callback);\n  chrome.runtime.onStartup.addListener(callback);\n}\n\nchrome.contextMenus.onClicked.addListener(info => {\n  if (info.menuItemId === 'open-current') {\n    open([info.linkUrl || info.pageUrl], []);\n  }\n  else if (info.menuItemId === 'open-all') {\n    chrome.tabs.query({\n      url: ['*://*/*']\n    }, delayOpen);\n  }\n  else if (info.menuItemId === 'open-call') {\n    chrome.tabs.query({\n      url: ['*://*/*'],\n      currentWindow: true\n    }, delayOpen);\n  }\n});\nchrome.runtime.onMessage.addListener((request, sender) => {\n  if (request.cmd === 'open-in') {\n    open([request.url], [sender.tab.id]);\n  }\n});\n\n// FAQs & Feedback/* FAQs & Feedback */\n{\n  const {management, runtime: {onInstalled, setUninstallURL, getManifest}, storage, tabs} = chrome;\n  if (navigator.webdriver !== true) {\n    const page = getManifest().homepage_url;\n    const {name, version} = getManifest();\n    onInstalled.addListener(({reason, previousVersion}) => {\n      management.getSelf(({installType}) => installType === 'normal' && storage.local.get({\n        'faqs': true,\n        'last-update': 0\n      }, prefs => {\n        if (reason === 'install' || (prefs.faqs && reason === 'update')) {\n          const doUpdate = (Date.now() - prefs['last-update']) / 1000 / 60 / 60 / 24 > 45;\n          if (doUpdate && previousVersion !== version) {\n            tabs.create({\n              url: page + '&version=' + version + (previousVersion ? '&p=' + previousVersion : '') + '&type=' + reason,\n              active: reason === 'install'\n            });\n            storage.local.set({'last-update': Date.now()});\n          }\n        }\n      }));\n    });\n    setUninstallURL(page + '&rd=feedback&name=' + encodeURIComponent(name) + '&version=' + version);\n  }\n}\n"
  },
  {
    "path": "common/data/inject.js",
    "content": "'use strict';\n\nconst config = {\n  button: 0,\n  altKey: true,\n  ctrlKey: false,\n  shiftKey: true,\n  metaKey: false,\n  enabled: false,\n  hosts: [],\n  urls: [],\n  reverse: false,\n  topRedict: false\n};\n\nconst validate = (a, callback, isTop = false) => {\n  if (config.hosts.length) {\n    const host = a.hostname;\n    if (host) {\n      if (config.hosts.some(h => h.endsWith(host) || host.endsWith(h))) {\n        return config.reverse ? '' : callback(a.href);\n      }\n    }\n  }\n  else {\n    const href = a.href;\n    if (href) {\n      if (config.urls.some(h => href.startsWith(h))) {\n        return config.reverse ? '' : callback(a.href);\n      }\n    }\n  }\n  // reverse mode\n  if (config.reverse && a.href && (a.href.indexOf('#') === -1 || isTop)) {\n    if (a.href.startsWith('http') || a.href.startsWith('file')) {\n      return callback(a.href);\n    }\n  }\n};\nchrome.storage.local.get(config, prefs => {\n  Object.assign(config, prefs);\n  // managed\n  chrome.storage.managed.get({\n    hosts: [],\n    urls: [],\n    reverse: false\n  }, prefs => {\n    if (!chrome.runtime.lastError) {\n      config.hosts.push(...prefs.hosts);\n      config.urls.push(...prefs.urls);\n      config.reverse = config.reverse || prefs.reverse;\n    }\n    // top level redirect\n    if (window.top === window && config.topRedict) {\n      validate(location, url => {\n        if (history.length) {\n          history.back();\n        }\n        else {\n          window.stop();\n        }\n        chrome.runtime.sendMessage({\n          cmd: 'open-in',\n          url\n        });\n      }, true);\n    }\n    // Gmail attachments\n    // https://github.com/andy-portmen/open-in/issues/42\n    if (window.top === window && location.hostname === 'mail.google.com') {\n      validate(location, () => {\n        const script = document.createElement('script');\n        script.textContent = `{\n          const script = document.currentScript;\n          const hps = Object.getOwnPropertyDescriptor(HTMLIFrameElement.prototype, 'src');\n          Object.defineProperty(HTMLIFrameElement.prototype, 'src', {\n            set(v) {\n              if (v && v.indexOf('&view=att&') !== -1) {\n                script.dispatchEvent(new CustomEvent('open-request', {\n                  detail: v\n                }));\n              }\n              else {\n                hps.set.call(this, v);\n              }\n            }\n          });\n        }`;\n        script.addEventListener('open-request', e => {\n          e.stopPropagation();\n          chrome.runtime.sendMessage({\n            cmd: 'open-in',\n            url: e.detail\n          });\n        });\n        document.documentElement.appendChild(script);\n        script.remove();\n      }, true);\n    }\n  });\n});\n\nchrome.storage.onChanged.addListener(e => {\n  Object.keys(e).forEach(n => {\n    config[n] = e[n].newValue;\n  });\n});\n\ndocument.addEventListener('click', e => {\n  const redirect = url => {\n    e.preventDefault();\n    e.stopPropagation();\n    e.stopImmediatePropagation();\n    chrome.runtime.sendMessage({\n      cmd: 'open-in',\n      url\n    });\n    return false;\n  };\n  // hostname on left-click\n  if (e.button === 0 && !e.altKey && !e.ctrlKey && !e.metaKey && !e.shiftKey) {\n    if (config.hosts.length || config.urls.length || config.reverse) {\n      let a = e.target.closest('a');\n      if (a) {\n        if (a.href.startsWith('https://www.google') && a.href.indexOf('&url=') !== -1) {\n          const link = decodeURIComponent(a.href.split('&url=')[1].split('&')[0]);\n          a = new URL(link);\n        }\n        validate(a, redirect);\n      }\n    }\n  }\n  // click + modifier\n  if (\n    config.enabled &&\n    e.button === config.button &&\n    e.altKey === config.altKey &&\n    e.ctrlKey === config.ctrlKey &&\n    e.metaKey === config.metaKey &&\n    e.shiftKey === config.shiftKey\n  ) {\n    const a = e.target.closest('a');\n    if (a && a.href) {\n      return redirect(a.href);\n    }\n  }\n}, true);\n"
  },
  {
    "path": "common/data/options/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>Open In Options</title>\n  <meta charset=\"UTF-8\">\n  <style>\n    @supports (-moz-appearance:none) {\n      body {\n        font-size: 13px;\n        font-family: Arial, \"Helvetica Neue\", Helvetica, sans-serif;\n        margin: 10px;\n        min-width: unset;\n        color: #424242;\n        background-color: #fff;\n      }\n      textarea {\n        padding: 5px;\n      }\n      button,\n      input[type=submit],\n      input[type=button] {\n        height: 24px;\n        color: #444;\n        background-image: linear-gradient(rgb(237, 237, 237), rgb(237, 237, 237) 38%, rgb(222, 222, 222));\n        box-shadow: rgba(0, 0, 0, 0.08) 0 1px 0, rgba(255, 255, 255, 0.75) 0 1px 2px inset;\n        text-shadow: rgb(240, 240, 240) 0 1px 0;\n        border: solid 1px rgba(0, 0, 0, 0.25);\n      }\n      input[type=button]:disabled {\n        opacity: 0.5;\n      }\n      h2 {\n        font-size: 110%;\n        font-weight: normal;\n      }\n    }\n\n    table.tbl {\n      width: 100%;\n    }\n    .tbl td:first-child {\n      width: 1px;\n      white-space: nowrap;\n    }\n    .tbl td:last-child {\n      text-align: right;\n    }\n    .admin {\n      background-color: #ffffed;\n      border: solid 1px #e8ec3a;\n      padding: 10px;\n      margin: 15px 0;\n    }\n    code {\n      background-color: rgba(0, 0, 0, 0.05);\n      padding: 0 3px;\n    }\n    code:empty::after {\n      content: 'not defined';\n      font-family: italic;\n    }\n    hr {\n      border: none;\n      border-top: dashed 1px #ccc;\n    }\n    h2 {\n      background-color: #e6e6e6;\n      padding: 5px;\n    }\n    ol {\n      padding-left: 1.8em;\n    }\n    li {\n      margin-bottom: 5px;\n    }\n    .grid {\n      display: grid;\n      grid-template-columns: 1fr min-content;\n      grid-gap: 5px;\n    }\n  </style>\n</head>\n\n<body>\n  <h2>Open With Keyboard-Mouse-Click Combinations</h2>\n  <table class=\"tbl\">\n    <tr>\n      <td><label for=\"enabled\">Enabled</label></td>\n      <td><input id=\"enabled\" type=\"checkbox\"></td>\n    </tr>\n    <tr>\n      <td>Mouse click<sup>4</sup></td>\n      <td>\n        <label>Ctrl <input id=\"ctrlKey\" type=\"checkbox\"></label>&nbsp;+&nbsp;\n        <label>Alt <input id=\"altKey\" type=\"checkbox\"></label>&nbsp;+&nbsp;\n        <label>Shift <input id=\"shiftKey\" type=\"checkbox\"></label>&nbsp;+&nbsp;\n        <label>Meta <input id=\"metaKey\" type=\"checkbox\"></label>&nbsp;+&nbsp;\n        <select id=\"button\">\n          <option value=0>Left Click</option>\n          <option value=1 disabled=\"true\">Middle Click</option>\n          <option value=2 disabled=\"true\">Right Click</option>\n        </select>\n      </td>\n    </tr>\n  </table>\n  <h2>Open with Left-Click</h2>\n  <table class=\"tbl\">\n    <tr>\n      <td colspan=\"2\" style=\"text-align: left; white-space: normal;\">Comma-separated list of domains to open with the external browser when the link is opened with left-click<sup>1</sup></td>\n    </tr>\n    <tr>\n      <td colspan=\"2\"><textarea id=\"hosts\" style=\"width: 100%\" rows=\"3\" placeholder=\"e.g.: www.google.com, bing.com\"></textarea></td>\n    </tr>\n    <tr>\n      <td colspan=\"2\" style=\"text-align: left; white-space: normal;\">Comma-separated list of URLs to open with the external browser when the link is opened with left-click<sup>1</sup></td>\n    </tr>\n    <tr>\n      <td colspan=\"2\"><textarea id=\"urls\" style=\"width: 100%\" rows=\"3\" placeholder=\"e.g.: https://www.google.com/, https://bing.com/\"></textarea></td>\n    </tr>\n  </table>\n  <div class=\"grid\">\n    <label for=\"reverse\">Reverse Mode<sup>5</sup></label>\n    <input type=\"checkbox\" id=\"reverse\">\n    <label for=\"topRedict\">Consider even top-level navigation<sup>6</sup></label>\n    <input type=\"checkbox\" id=\"topRedict\">\n  </div>\n  <h2>Misc</h2>\n  <div class=\"grid\">\n    <label for=\"closeme\">Close the source tab when link is pushed</label>\n    <input type=\"checkbox\" id=\"closeme\">\n    <label for=\"multiple\">Open multiple links at once<sup>3</sup></label>\n    <input type=\"checkbox\" id=\"multiple\">\n    <label for=\"faqs\">Open FAQs page on updates</label>\n    <input type=\"checkbox\" id=\"faqs\">\n  </div>\n  <div>\n    <span style=\"margin-top: 10px; margin-bottom: 5px; display: block\">Path to the executable<sup>2</sup>:</span>\n    <div style=\"display: flex;\">\n      <input type=\"text\" id=\"path\" style=\"flex: 1;\">\n    </div>\n  </div>\n\n  <div class=\"admin\">This extension supports managed storage. Some of the preferences can be pre-configured by <a href=\"https://add0n.com/open-in.html#faq17\">the domain administrator</a></div>\n\n  <div id=\"explore\" data-inc=50></div>\n  <p>\n    <button id=\"reset\">Reset Settings</button>\n    <button id=\"support\">Support Development</button> - <button id=\"save\">Save Options</button>\n    <span id=\"status\"></span>\n  </p>\n\n  <hr style=\"margin: 10px 0\">\n  <ol>\n    <li>It is possible to use \"managed storage\" to set the default host and URL list. This is useful for administrators to force opening a set of URLs or hostnames in another browser. Instruction on how to setup the managed storage can be found in the FAQs page.</li>\n    <li>On Windows, if the path to the executable is not provided (is empty) <code id=\"l2\"></code> is used. On Linux, if the path is not set, it is assumed <code id=\"l3\"></code> binary can be found in the global PATH environment. On Mac OS, <code>open -a</code> command is used (with <code id=\"l4\"></code> as the application or whatever is in the input).</li>\n    <li>By activating this option all the requested URLs are sent to the external executable at once. Only activate this option if the external executable is capable of handling this type of requests.</li>\n    <li>Not all key combinations are allowed in all operating systems.</li>\n    <li>When enabled, all left-click links except the ones that match with at least one condition will be sent to the external executable.</li>\n    <li>If checked, the extension validates top-level navigation with the list of left-click hostnames and URLs. If matched, the URL is sent to the external executable, and the navigation is blocked.</li>\n  </ol style=\"padding-left: 1.8em;\">\n\n  <script src=\"../../config.js\"></script>\n  <script src=\"index.js\"></script>\n  <script async src=\"matched.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "common/data/options/index.js",
    "content": "/* globals app */\n'use strict';\n\ndocument.title = 'Open In ' + app.locale.name + ' :: Options';\ndocument.getElementById('path').placeholder = app.locale.example;\ndocument.getElementById('l2').textContent = app.runtime.windows.prgfiles;\ndocument.getElementById('l3').textContent = app.runtime.linux.name;\ndocument.getElementById('l4').textContent = app.runtime.mac.args[1];\n\nfunction restore() {\n  // Use default value color = 'red' and likesColor = true.\n  chrome.storage.local.get({\n    path: '',\n    enabled: false,\n    altKey: true,\n    shiftKey: true,\n    ctrlKey: false,\n    metaKey: false,\n    button: 0,\n    faqs: true,\n    closeme: false,\n    multiple: app.multiple,\n    hosts: [],\n    urls: [],\n    reverse: false,\n    topRedict: false\n  }, prefs => {\n    document.getElementById('path').value = prefs.path;\n    document.getElementById('enabled').checked = prefs.enabled;\n    document.getElementById('altKey').checked = prefs.altKey;\n    document.getElementById('shiftKey').checked = prefs.shiftKey;\n    document.getElementById('ctrlKey').checked = prefs.ctrlKey;\n    document.getElementById('metaKey').checked = prefs.metaKey;\n    document.getElementById('button').selectedIndex = prefs.button;\n    document.getElementById('faqs').checked = prefs.faqs;\n    document.getElementById('closeme').checked = prefs.closeme;\n    document.getElementById('multiple').checked = prefs.multiple;\n    document.getElementById('hosts').value = prefs.hosts.join(', ');\n    document.getElementById('urls').value = prefs.urls.join(', ');\n    document.getElementById('reverse').checked = prefs.reverse;\n    document.getElementById('topRedict').checked = prefs.topRedict;\n  });\n}\n\nfunction save() {\n  const path = document.getElementById('path').value;\n  const enabled = document.getElementById('enabled').checked;\n  const altKey = document.getElementById('altKey').checked;\n  const shiftKey = document.getElementById('shiftKey').checked;\n  const ctrlKey = document.getElementById('ctrlKey').checked;\n  const metaKey = document.getElementById('metaKey').checked;\n  const button = document.getElementById('button').selectedIndex;\n  const faqs = document.getElementById('faqs').checked;\n  const closeme = document.getElementById('closeme').checked;\n  const multiple = document.getElementById('multiple').checked;\n  const hosts = document.getElementById('hosts').value;\n  const urls = document.getElementById('urls').value;\n  const reverse = document.getElementById('reverse').checked;\n  const topRedict = document.getElementById('topRedict').checked;\n\n  chrome.storage.local.set({\n    path,\n    enabled,\n    altKey,\n    shiftKey,\n    ctrlKey,\n    metaKey,\n    button,\n    faqs,\n    closeme,\n    multiple,\n    hosts: hosts.split(/\\s*,\\s*/).map(s => s.replace('http://', '')\n      .replace('https://', '')\n      .split('/')[0].trim())\n      .filter((h, i, l) => h && l.indexOf(h) === i),\n    urls: urls.split(/\\s*,\\s*/).filter(s => s.startsWith('http') || s.startsWith('file'))\n      .filter((h, i, l) => h && l.indexOf(h) === i),\n    reverse,\n    topRedict\n  }, () => {\n    restore();\n    const status = document.getElementById('status');\n    status.textContent = 'Options saved.';\n    setTimeout(() => status.textContent = '', 750);\n  });\n}\n\ndocument.addEventListener('DOMContentLoaded', restore);\ndocument.getElementById('save').addEventListener('click', save);\ndocument.getElementById('support').addEventListener('click', () => chrome.tabs.create({\n  url: chrome.runtime.getManifest().homepage_url + '&rd=donate'\n}));\n\ndocument.getElementById('save').addEventListener('click', save);\ndocument.getElementById('reset').addEventListener('click', e => {\n  if (e.detail === 1) {\n    const status = document.getElementById('status');\n    window.setTimeout(() => status.textContent = '', 750);\n    status.textContent = 'Double-click to reset!';\n  }\n  else {\n    localStorage.clear();\n    chrome.storage.local.clear(() => {\n      chrome.runtime.reload();\n      window.close();\n    });\n  }\n});\n"
  },
  {
    "path": "common/schema.json",
    "content": "{\n  \"type\": \"object\",\n  \"properties\": {\n    \"hosts\": {\n      \"title\": \"List of hosts\",\n      \"description\": \"List of domains to open with the external browser when the link is opened with left-click\",\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"string\"\n      }\n    },\n    \"urls\": {\n      \"title\": \"List of URLs\",\n      \"description\": \"List of URLs to open with the external browser when the link is opened with left-click\",\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"string\"\n      }\n    },\n    \"faqs\": {\n      \"title\": \"Open FAQs page on updates\",\n      \"type\": \"boolean\"\n    },\n    \"reverse\": {\n      \"title\": \"Enable reversed left-click mode\",\n      \"type\": \"boolean\"\n    }\n  }\n}\n"
  },
  {
    "path": "edge/README",
    "content": "moved to https://github.com/Rynu/open-in-edge/tree/master\n"
  },
  {
    "path": "palemoon/config.js",
    "content": "'use strict';\n\nvar app = {\n  id: 'com.add0n.node',\n  tag: 'palemoon',\n  multiple: true\n};\n\napp.locale = {\n  name: 'Pale Moon',\n  current: 'Open Link in Pale Moon Browser',\n  all: 'Open all Tabs in Pale Moon Browser',\n  call: 'Open all Tabs in Pale Moon Browser (Current window)',\n  example: 'example D:\\\\Pale Moon\\\\browser.exe'\n};\n\napp.runtime = {\n  mac: {\n    args: ['-a', 'Palemoon']\n  },\n  linux: {\n    name: 'palemoon'\n  },\n  windows: {\n    name: 'cmd',\n    args: ['/s/c', 'start', 'palemoon \"%url;\"'],\n    prgfiles: '%ProgramFiles(x86)%\\\\Pale Moon\\\\palemoon.exe'\n  }\n};\n"
  },
  {
    "path": "palemoon/manifest.json",
    "content": "{\r\n  \"name\": \"Open in Pale Moon\",\r\n  \"description\": \"Open current page, link, or all tabs in the Pale Moon browser.\",\r\n  \"version\": \"0.1.1\",\r\n  \"manifest_version\": 2,\r\n  \"permissions\": [\r\n    \"storage\",\r\n    \"tabs\",\r\n    \"contextMenus\",\r\n    \"nativeMessaging\"\r\n  ],\r\n  \"optional_permissions\": [\r\n    \"downloads\"\r\n  ],\r\n  \"background\": {\r\n    \"persistent\": false,\r\n    \"scripts\": [\r\n      \"config.js\",\r\n      \"common.js\"\r\n    ]\r\n  },\r\n  \"storage\": {\r\n    \"managed_schema\": \"schema.json\"\r\n  },\r\n  \"homepage_url\": \"https://add0n.com/open-in.html?from=palemoon\",\r\n  \"icons\": {\r\n    \"16\": \"data/icons/16.png\",\r\n    \"32\": \"data/icons/32.png\",\r\n    \"48\": \"data/icons/48.png\",\r\n    \"64\": \"data/icons/64.png\",\r\n    \"128\": \"data/icons/128.png\",\r\n    \"256\": \"data/icons/256.png\",\r\n    \"512\": \"data/icons/512.png\"\r\n  },\r\n  \"browser_action\": {},\r\n  \"content_scripts\": [{\r\n    \"matches\": [\"<all_urls>\"],\r\n    \"js\": [\"data/inject.js\"],\r\n    \"run_at\": \"document_start\",\r\n    \"all_frames\": true,\r\n    \"match_about_blank\": true\r\n  }],\r\n  \"options_ui\": {\r\n    \"page\": \"data/options/index.html\",\r\n    \"chrome_style\": true,\r\n    \"open_in_tab\": true\r\n  }\r\n}\r\n"
  },
  {
    "path": "vivaldi/config.js",
    "content": "'use strict';\n\nvar app = {\n  id: 'com.add0n.node',\n  tag: 'vivaldi',\n  multiple: true\n};\n\napp.locale = {\n  name: 'Vivaldi',\n  current: 'Open Link in Vivaldi Browser',\n  all: 'Open all Tabs in Vivaldi Browser',\n  call: 'Open all Tabs in Vivaldi Browser (Current window)',\n  example: 'example D:\\\\Vivaldi\\\\vivaldi.exe'\n};\n\napp.runtime = {\n  mac: {\n    args: ['-a', 'vivaldi']\n  },\n  linux: {\n    name: 'vivaldi'\n  },\n  windows: {\n    name: 'cmd',\n    args: ['/s/c', 'start', 'vivaldi \"%url;\"'],\n    prgfiles: '%LOCALAPPDATA%\\\\Vivaldi\\\\Application\\\\vivaldi.exe'\n  }\n};\n"
  },
  {
    "path": "vivaldi/manifest.json",
    "content": "{\r\n  \"name\": \"Open in Vivaldi\",\r\n  \"description\": \"Open current page, link, or all tabs in the Vivaldi browser.\",\r\n  \"version\": \"0.2.0\",\r\n  \"manifest_version\": 2,\r\n  \"permissions\": [\r\n    \"storage\",\r\n    \"tabs\",\r\n    \"contextMenus\",\r\n    \"nativeMessaging\"\r\n  ],\r\n  \"optional_permissions\": [\r\n    \"downloads\"\r\n  ],\r\n  \"background\": {\r\n    \"persistent\": false,\r\n    \"scripts\": [\r\n      \"config.js\",\r\n      \"common.js\"\r\n    ]\r\n  },\r\n  \"storage\": {\r\n    \"managed_schema\": \"schema.json\"\r\n  },\r\n  \"homepage_url\": \"https://add0n.com/open-in.html?from=vivaldi\",\r\n  \"icons\": {\r\n    \"16\": \"data/icons/16.png\",\r\n    \"32\": \"data/icons/32.png\",\r\n    \"48\": \"data/icons/48.png\",\r\n    \"64\": \"data/icons/64.png\",\r\n    \"128\": \"data/icons/128.png\",\r\n    \"256\": \"data/icons/256.png\",\r\n    \"512\": \"data/icons/512.png\"\r\n  },\r\n  \"browser_action\": {},\r\n  \"content_scripts\": [{\r\n    \"matches\": [\"<all_urls>\"],\r\n    \"js\": [\"data/inject.js\"],\r\n    \"run_at\": \"document_start\",\r\n    \"all_frames\": true,\r\n    \"match_about_blank\": true\r\n  }],\r\n  \"options_ui\": {\r\n    \"page\": \"data/options/index.html\",\r\n    \"chrome_style\": true,\r\n    \"open_in_tab\": true\r\n  }\r\n}\r\n"
  },
  {
    "path": "waterfox/config.js",
    "content": "'use strict';\n\nvar app = {\n  id: 'com.add0n.node',\n  tag: 'waterfox',\n  multiple: true\n};\n\napp.locale = {\n  name: 'Waterfox',\n  current: 'Open Link in Waterfox Browser',\n  all: 'Open all Tabs in Waterfox Browser',\n  call: 'Open all Tabs in Waterfox Browser (Current window)',\n  example: 'example D:\\\\Waterfox\\\\browser.exe'\n};\n\napp.runtime = {\n  mac: {\n    args: ['-a', 'Waterfox']\n  },\n  linux: {\n    name: 'waterfox'\n  },\n  windows: {\n    name: 'cmd',\n    args: ['/s/c', 'start', 'waterfox \"%url;\"'],\n    prgfiles: '%ProgramFiles(x86)%\\\\Waterfox\\\\waterfox.exe'\n  }\n};\n"
  },
  {
    "path": "waterfox/manifest.json",
    "content": "{\r\n  \"name\": \"Open in Waterfox\",\r\n  \"description\": \"Open current page, link, or all tabs in the Waterfox browser.\",\r\n  \"version\": \"0.1.1\",\r\n  \"manifest_version\": 2,\r\n  \"permissions\": [\r\n    \"storage\",\r\n    \"tabs\",\r\n    \"contextMenus\",\r\n    \"nativeMessaging\"\r\n  ],\r\n  \"optional_permissions\": [\r\n    \"downloads\"\r\n  ],\r\n  \"background\": {\r\n    \"persistent\": false,\r\n    \"scripts\": [\r\n      \"config.js\",\r\n      \"common.js\"\r\n    ]\r\n  },\r\n  \"storage\": {\r\n    \"managed_schema\": \"schema.json\"\r\n  },\r\n  \"homepage_url\": \"https://add0n.com/open-in.html?from=waterfox\",\r\n  \"icons\": {\r\n    \"16\": \"data/icons/16.png\",\r\n    \"32\": \"data/icons/32.png\",\r\n    \"48\": \"data/icons/48.png\",\r\n    \"64\": \"data/icons/64.png\",\r\n    \"128\": \"data/icons/128.png\",\r\n    \"256\": \"data/icons/256.png\",\r\n    \"512\": \"data/icons/512.png\"\r\n  },\r\n  \"browser_action\": {},\r\n  \"content_scripts\": [{\r\n    \"matches\": [\"<all_urls>\"],\r\n    \"js\": [\"data/inject.js\"],\r\n    \"run_at\": \"document_start\",\r\n    \"all_frames\": true,\r\n    \"match_about_blank\": true\r\n  }],\r\n  \"options_ui\": {\r\n    \"page\": \"data/options/index.html\",\r\n    \"chrome_style\": true,\r\n    \"open_in_tab\": true\r\n  }\r\n}\r\n"
  },
  {
    "path": "yandex/config.js",
    "content": "'use strict';\n\nvar app = {\n  id: 'com.add0n.node',\n  tag: 'yandex',\n  multiple: true\n};\n\napp.locale = {\n  name: 'Yandex',\n  current: 'Open Link in Yandex Browser',\n  all: 'Open all Tabs in Yandex Browser',\n  call: 'Open all Tabs in Yandex Browser (Current window)',\n  example: 'example D:\\\\Yandex\\\\browser.exe'\n};\n\napp.runtime = {\n  mac: {\n    args: ['-a', 'yandex']\n  },\n  linux: {\n    name: 'yandex'\n  },\n  windows: {\n    name: 'cmd',\n    args: ['/s/c', 'start', 'yandex \"%url;\"'],\n    prgfiles: '%LOCALAPPDATA%\\\\Yandex\\\\YandexBrowser\\\\Application\\\\browser.exe'\n  }\n};\n"
  },
  {
    "path": "yandex/manifest.json",
    "content": "{\r\n  \"name\": \"Open in Yandex browser\",\r\n  \"description\": \"Open current page, link, or all tabs in the Yandex browser.\",\r\n  \"version\": \"0.1.6\",\r\n  \"manifest_version\": 2,\r\n  \"permissions\": [\r\n    \"storage\",\r\n    \"tabs\",\r\n    \"contextMenus\",\r\n    \"nativeMessaging\"\r\n  ],\r\n  \"optional_permissions\": [\r\n    \"downloads\"\r\n  ],\r\n  \"background\": {\r\n    \"persistent\": false,\r\n    \"scripts\": [\r\n      \"config.js\",\r\n      \"common.js\"\r\n    ]\r\n  },\r\n  \"storage\": {\r\n    \"managed_schema\": \"schema.json\"\r\n  },\r\n  \"homepage_url\": \"https://add0n.com/open-in.html?from=yandex\",\r\n  \"icons\": {\r\n    \"16\": \"data/icons/16.png\",\r\n    \"32\": \"data/icons/32.png\",\r\n    \"48\": \"data/icons/48.png\",\r\n    \"64\": \"data/icons/64.png\",\r\n    \"128\": \"data/icons/128.png\",\r\n    \"256\": \"data/icons/256.png\",\r\n    \"512\": \"data/icons/512.png\"\r\n  },\r\n  \"browser_action\": {},\r\n  \"content_scripts\": [{\r\n    \"matches\": [\"<all_urls>\"],\r\n    \"js\": [\"data/inject.js\"],\r\n    \"run_at\": \"document_start\",\r\n    \"all_frames\": true,\r\n    \"match_about_blank\": true\r\n  }],\r\n  \"options_ui\": {\r\n    \"page\": \"data/options/index.html\",\r\n    \"chrome_style\": true,\r\n    \"open_in_tab\": true\r\n  }\r\n}\r\n"
  }
]