text:
'use strict'
/* eslint-env browser, webextensions */

import browser from 'webextension-polyfill'

/**
 * Renders a translated string with html anchors.
 * @param {String} message The message to translate.
 * @param {Array} links The array of hrefs.
 * @param {Object} attributes HTML attributes to put in the anchor.
 * @return {html} An HTML node with the translated string with anchors.
 */
export const renderTranslatedLinks = (message, links, attributes) => {
  const regexLink = /<\d+>(.+?)<\/\d+>/mg
  const regexIndex = /<(\d+)>/mg
  const str = browser.i18n.getMessage(message)
  let output = str

  let matchLink = regexLink.exec(str)
  while (matchLink !== null) {
    let matchIndex = regexIndex.exec(matchLink[0])
    while (matchIndex !== null) {
      output = output.replace(matchLink[0], `<a href="${links[parseInt(matchIndex[1])]}" ${attributes}>${matchLink[1]}</a>`)
      matchIndex = regexIndex.exec(str)
    }
    matchLink = regexLink.exec(str)
  }

  const template = document.createElement('template')
  template.innerHTML = output

  return template.content
}

/**
 * Renders a translated string with html spans.
 * @param {String} message - The message to translate.
 * @param {Array} values -  The array of dynamic values ($1, $2 etc) in the translation.
 * @param {Object} attributes - HTML attributes to put in the span around each of them.
 * @return {html} An HTML node with the translated string with spans.
 */
export const renderTranslatedSpans = (message, values, attributes) => {
  const regexSpan = /<\d+>(.+?)<\/\d+>/mg
  const regexIndex = /<(\d+)>/mg
  const str = browser.i18n.getMessage(message, values)
  let output = str

  let matchSpan = regexSpan.exec(str)
  while (matchSpan !== null) {
    let matchIndex = regexIndex.exec(matchSpan[0])
    while (matchIndex !== null) {
      output = output.replace(matchSpan[0], `<span ${attributes}>${matchSpan[1]}</span>`)
      matchIndex = regexIndex.exec(str)
    }
    matchSpan = regexSpan.exec(str)
  }

  const template = document.createElement('template')
  template.innerHTML = output

  return template.content
}


text:
@import url('~tachyons/css/tachyons.css');
@import url('~ipfs-css/ipfs.css');
@import url('../pages/components/switch-toggle.css');

header {
  border-color: #69c4cd;
  text-transform: uppercase;
}
#header-logo {
  height: 50px;
  width: 117.5px;
}
form * {
  font-family: Inter,system-ui,sans-serif;
  font-size: 0.875rem;
}
fieldset {
  border: none;
  display: flex;
  flex-direction: column;
}
fieldset > div {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding-bottom: 1.5em;
}
fieldset > div + div {
  border-top: 1px solid #ccc;
}
label {
  flex: 1;
}
select:hover,
button:hover,
label:hover {
  cursor: pointer;
}
label > dl {
  margin-right: 2em;
}
label > dl > dt {
  font-weight: 600;
}
label > dl > dd {
  margin: .5em 0 .5em 0;
}
label a[href^="http"]:after {
  content: url('data:image/svg+xml; utf8, <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="%2334373f" viewBox="20 12 60 70"><path d="M75.4 49.9c-.9 0-1.6.7-1.6 1.6v21.8c0 1.5-.6 2.9-1.7 4s-2.5 1.7-4 1.7h-40c-1.5 0-2.9-.6-4-1.7s-1.7-2.5-1.7-4v-40c0-1.5.6-2.9 1.7-4s2.5-1.7 4-1.7h21.8c.9 0 1.6-.7 1.6-1.6s-.7-1.6-1.6-1.6H28.1c-2.4 0-4.6.9-6.3 2.6-1.7 1.7-2.6 3.9-2.6 6.3v40c0 2.4.9 4.6 2.6 6.3 1.7 1.7 3.9 2.6 6.3 2.6h40c2.4 0 4.6-.9 6.3-2.6 1.7-1.7 2.6-3.9 2.6-6.3V51.5c0-.9-.7-1.6-1.6-1.6z"/><path d="M83.8 19.3V19v-.2-.2c0-.1-.1-.1-.1-.2 0 0 0-.1-.1-.1-.1-.1-.1-.2-.2-.2l-.2-.2s-.1 0-.1-.1l-.1-.1c-.1 0-.1 0-.2-.1H60.3c-.9 0-1.6.7-1.6 1.6s.7 1.6 1.6 1.6h17.9l-25.8 26c-.6.6-.6 1.7 0 2.3.3.3.7.5 1.1.5s.8-.2 1.1-.5l25.8-25.8v17.9c0 .9.7 1.6 1.6 1.6s1.6-.7 1.6-1.6l.2-21.9z"/></svg>');
  padding-left: 0.25em;
}
input, textarea, select {
  flex: 1;
  padding: .75em;
  font-family: Consolas, monaco, monospace;
}
select {
	appearance: none;
}

fieldset > div > div,
#resetAllOptions {
  flex: 1;
}
input[type=text],
input[type=url],
input[type=number],
select,
textarea
 {
  transition: all 0.30s ease-in-out;
  border-radius: 0.25rem;
  border: 1px solid #b7bbc8;
}

input:invalid {
  border: 1px solid #ea5037;
  background-color: #f36149;
}
.mdc-switch {
  display: block;
}

/* https://brave.com/brave-branding-assets/ */
.brave {
  background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg version='1.1' viewBox='0 0 218 256' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3Cdefs%3E%3ClinearGradient id='c' y1='50.706%25' y2='50.706%25'%3E%3Cstop stop-color='%23f50' offset='0'/%3E%3Cstop stop-color='%23f50' offset='.40988'/%3E%3Cstop stop-color='%23FF2000' offset='.58198'/%3E%3Cstop stop-color='%23FF2000' offset='1'/%3E%3C/linearGradient%3E%3ClinearGradient id='b' x1='2.1484%25' y1='50.706%25' y2='50.706%25'%3E%3Cstop stop-color='%23FF452A' offset='0'/%3E%3Cstop stop-color='%23FF2000' offset='1'/%3E%3C/linearGradient%3E%3Cpath id='a' d='m170.27 25.336-22.304-25.336h-39.168-39.168l-22.304 25.336s-19.584-5.4468-28.832 3.8128c0 0 26.112-2.3601 35.088 12.255 0 0 24.208 4.6298 27.472 4.6298s10.336-2.7234 16.864-4.9021c6.528-2.1787 10.88-2.1945 10.88-2.1945s4.352 0.015796 10.88 2.1945 13.6 4.9021 16.864 4.9021 27.472-4.6298 27.472-4.6298c8.976-14.615 35.088-12.255 35.088-12.255-9.248-9.2596-28.832-3.8128-28.832-3.8128'/%3E%3C/defs%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg transform='translate(-70 -350)'%3E%3Cg transform='translate(50 350)'%3E%3Cg transform='translate(20.114)'%3E%3Cpath d='m209.98 61.285 5.984-14.706s-7.616-8.1702-16.864-17.43c-9.248-9.2596-28.832-3.8128-28.832-3.8128l-22.304-25.336h-39.168-39.168l-22.304 25.336s-19.584-5.4468-28.832 3.8128c-9.248 9.2596-16.864 17.43-16.864 17.43l5.984 14.706-7.616 21.787s22.399 84.946 25.024 95.319c5.168 20.426 8.704 28.323 23.392 38.672s41.344 28.323 45.696 31.047 9.792 7.3625 14.688 7.3625 10.336-4.639 14.688-7.3625 31.008-20.698 45.696-31.047 18.224-18.247 23.392-38.672c2.6243-10.373 25.024-95.319 25.024-95.319l-7.616-21.787z' fill='url(%23c)'/%3E%3Cpath d='m164.02 41.404s28.688 34.723 28.688 42.145-3.6089 9.3799-7.2374 13.238c-3.6285 3.858-19.476 20.707-21.511 22.871-2.0351 2.164-6.2718 5.4446-3.7797 11.35s6.1684 13.419 2.0803 21.04c-4.0887 7.6212-11.093 12.708-15.581 11.867-4.488-0.84153-15.028-6.3564-18.904-8.8756s-16.161-12.663-16.161-16.545c0-3.8809 12.699-10.849 15.044-12.433 2.3468-1.5828 13.047-7.711 13.267-10.116 0.21869-2.4059 0.13546-3.1112-3.0225-9.0548-3.1579-5.9441-8.8449-13.876-7.8983-19.153 0.94656-5.2769 10.118-8.021 16.664-10.496 6.5443-2.4745 19.146-7.1484 20.72-7.875 1.5749-0.72715 1.168-1.4194-3.6013-1.8721-4.7682-0.45263-18.301-2.2512-24.402-0.54849-6.101 1.7027-16.524 4.2932-17.368 5.6669s-1.589 1.42-0.72189 6.1587c0.86659 4.7387 5.3307 27.477 5.7637 31.516 0.43357 4.0388 1.2811 6.7088-3.0682 7.7045-4.3498 0.99568-11.672 2.7245-14.188 2.7245s-9.8382-1.7288-14.188-2.7245c-4.3498-0.99568-3.5023-3.6657-3.0687-7.7045 0.43357-4.0388 4.8971-26.777 5.7642-31.516 0.86659-4.7387 0.12186-4.785-0.72189-6.1587-0.84429-1.3737-11.268-3.9642-17.369-5.6669-6.101-1.7027-19.633 0.095864-24.402 0.54849s-5.1756 1.1449-3.6013 1.8721c1.5743 0.7266 14.176 5.4005 20.72 7.875 6.5449 2.475 15.717 5.2191 16.664 10.496 0.94656 5.2774-4.741 13.209-7.8983 19.153-3.1579 5.9436-3.2417 6.6489-3.0225 9.0548 0.21869 2.4053 10.92 8.5335 13.266 10.116 2.3463 1.5834 15.044 8.552 15.044 12.433 0 3.8814-12.284 14.026-16.16 16.545s-14.416 8.034-18.904 8.8756c-4.488 0.84099-11.492-4.2458-15.581-11.867-4.0882-7.6212-0.41181-15.135 2.0797-21.04 2.4921-5.9049-1.7441-9.1855-3.7797-11.35-2.0346-2.1635-17.882-19.013-21.511-22.871-3.6285-3.858-7.2368-5.8166-7.2368-13.238s28.688-42.145 28.688-42.145 24.208 4.6298 27.472 4.6298 10.336-2.7234 16.864-4.9021c6.528-2.1787 10.88-2.1945 10.88-2.1945s4.352 0.015796 10.88 2.1945 13.6 4.9021 16.864 4.9021 27.472-4.6298 27.472-4.6298zm-21.507 132.82c1.7756 1.1133 0.69251 3.212-0.9248 4.3569-1.6179 1.1449-23.357 18-25.466 19.862-2.1102 1.8634-5.2104 4.9403-7.3184 4.9403s-5.2088-3.0769-7.3184-4.9403c-2.1102-1.8628-23.848-18.717-25.466-19.862-1.6179-1.1449-2.7004-3.2436-0.9248-4.3569 1.7762-1.1128 7.332-3.9217 14.999-7.8941 7.6655-3.9718 17.22-7.3488 18.711-7.3488s11.045 3.377 18.711 7.3488c7.666 3.9724 13.222 6.7813 14.998 7.8941z' fill='%23fff'/%3E%3Cmask fill='white'%3E%3Cuse xlink:href='%23a'/%3E%3C/mask%3E%3Cuse fill='url(%23b)' xlink:href='%23a'/%3E%3C/g%3E%3C/g%3E%3C/g%3E%3C/g%3E%3C/svg%3E%0A");
  background-size: 1.5em;
  background-position: left 0.5em top 50%;
  background-repeat: no-repeat;
  padding-left: 2.5em;
}
input.brave {
  background-color: #f7f8fa;
}

repo_name: ipfs/ipfs-companion

repo_language: JavaScript

mime_type: text/x-diff


text:
'use strict'
/* eslint-env browser, webextensions */

import html from 'choo/html/index.js'
import globalToggleForm from './forms/global-toggle-form.js'
import ipfsNodeForm from './forms/ipfs-node-form.js'
import fileImportForm from './forms/file-import-form.js'
import dnslinkForm from './forms/dnslink-form.js'
import gatewaysForm from './forms/gateways-form.js'
import apiForm from './forms/api-form.js'
import experimentsForm from './forms/experiments-form.js'
import telemetryForm from './forms/telemetry-form.js'
import resetForm from './forms/reset-form.js'

// Render the options page:
// Passed current app `state` from the store and `emit`, a function to create
// events, allowing views to signal back to the store that something happened.
export default function optionsPage (state, emit) {
  const onOptionChange = (key, modifyValue) => (e) => {
    e.preventDefault()

    const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value

    if (!e.target.reportValidity()) {
      return console.warn(`[ipfs-companion] Invalid value for ${key}: ${value}`)
    }

    emit('optionChange', { key, value: modifyValue ? modifyValue(value) : value })
    if (modifyValue) {
      emit('render')
    }
  }

  const onOptionsReset = (e) => {
    e.preventDefault()
    emit('optionsReset')
  }

  if (!state.options.active) {
    // we don't want to confuse users by showing "active" checkboxes
    // when global toggle is in "suspended" state
    return html`

  ${globalToggleForm({
    active: state.options.active,
    onOptionChange
  })}

    `
  }
  return html`

  ${globalToggleForm({
    active: state.options.active,
    onOptionChange
  })}
  ${ipfsNodeForm({
    ipfsNodeType: state.options.ipfsNodeType,
    ipfsNodeConfig: state.options.ipfsNodeConfig,
    withNodeFromBrave: state.withNodeFromBrave,
    onOptionChange
  })}
  ${state.options.ipfsNodeType.startsWith('external')
  ? apiForm({
    ipfsNodeType: state.options.ipfsNodeType,
    ipfsApiUrl: state.options.ipfsApiUrl,
    ipfsApiPollMs: state.options.ipfsApiPollMs,
    automaticMode: state.options.automaticMode,
    onOptionChange
  })
  : null}
  ${gatewaysForm({
    ipfsNodeType: state.options.ipfsNodeType,
    customGatewayUrl: state.options.customGatewayUrl,
    useCustomGateway: state.options.useCustomGateway,
    useSubdomains: state.options.useSubdomains,
    publicGatewayUrl: state.options.publicGatewayUrl,
    publicSubdomainGatewayUrl: state.options.publicSubdomainGatewayUrl,
    disabledOn: state.options.disabledOn,
    enabledOn: state.options.enabledOn,
    onOptionChange
  })}
  ${fileImportForm({
    importDir: state.options.importDir,
    openViaWebUI: state.options.openViaWebUI,
    preloadAtPublicGateway: state.options.preloadAtPublicGateway,
    onOptionChange
  })}
  ${dnslinkForm({
    dnslinkPolicy: state.options.dnslinkPolicy,
    dnslinkDataPreload: state.options.dnslinkDataPreload,
    dnslinkRedirect: state.options.dnslinkRedirect,
    onOptionChange
  })}
  ${experimentsForm({
    useLatestWebUI: state.options.useLatestWebUI,
    displayNotifications: state.options.displayNotifications,
    displayReleaseNotes: state.options.displayReleaseNotes,
    catchUnhandledProtocols: state.options.catchUnhandledProtocols,
    linkify: state.options.linkify,
    recoverFailedHttpRequests: state.options.recoverFailedHttpRequests,
    detectIpfsPathHeader: state.options.detectIpfsPathHeader,
    logNamespaces: state.options.logNamespaces,
    onOptionChange
  })}
  ${telemetryForm({
    telemetryGroupMinimal: state.options.telemetryGroupMinimal,
    telemetryGroupMarketing: state.options.telemetryGroupMarketing,
    telemetryGroupPerformance: state.options.telemetryGroupPerformance,
    telemetryGroupTracking: state.options.telemetryGroupTracking,
    onOptionChange
  })}
  ${resetForm({
    onOptionsReset
  })}

  `
}


text:
'use strict'
/* eslint-env browser, webextensions */

import './options.css'

import { i18n } from 'webextension-polyfill'
import choo from 'choo'
import optionsPage from './page.js'
import optionsStore from './store.js'

const app = choo()

// Use the store to setup state defaults and event listeners for mutations
app.use(optionsStore)

// Register our single route
app.route('*', optionsPage)

// Start the application and render it to the given querySelector
app.mount('#root')

// Set page title and header translation
document.getElementById('header-text').innerText = i18n.getMessage('option_page_header')
document.title = i18n.getMessage('option_page_title')


text:
'use strict'
/* eslint-env browser, webextensions */

import browser from 'webextension-polyfill'
import { optionDefaults } from '../lib/options.js'
import createRuntimeChecks from '../lib/runtime-checks.js'

// The store contains and mutates the state for the app
export default function optionStore (state, emitter) {
  state.options = optionDefaults

  const updateStateOptions = async () => {
    const runtime = await createRuntimeChecks(browser)
    state.withNodeFromBrave = runtime.brave && await runtime.brave.getIPFSEnabled()
    /**
     * FIXME: Why are we setting `state.options` when state is supposed to extend options?
     */
    state.options = await getOptions()
    emitter.emit('render')
  }

  emitter.on('DOMContentLoaded', async () => {
    browser.runtime.sendMessage({ telemetry: { trackView: 'options' } })
    updateStateOptions()
    browser.storage.onChanged.addListener(updateStateOptions)
  })

  emitter.on('optionChange', ({ key, value }) => (
    browser.storage.local.set({ [key]: value })
  ))

  emitter.on('optionsReset', () => (
    browser.storage.local.set(optionDefaults)
  ))
}

async function getOptions () {
  const storedOpts = await browser.storage.local.get()
  return Object.keys(optionDefaults).reduce((opts, key) => {
    opts[key] = storedOpts[key] == null ? optionDefaults[key] : storedOpts[key]
    return opts
  }, {})
}


text:
'use strict'
/* eslint-env browser, webextensions */

import browser from 'webextension-polyfill'
import html from 'choo/html/index.js'
import switchToggle from '../../pages/components/switch-toggle.js'

export default function experimentsForm ({
  useLatestWebUI,
  displayNotifications,
  displayReleaseNotes,
  catchUnhandledProtocols,
  linkify,
  recoverFailedHttpRequests,
  detectIpfsPathHeader,
  logNamespaces,
  onOptionChange
}) {
  const onDisplayNotificationsChange = onOptionChange('displayNotifications')
  const onDisplayReleaseNotesChange = onOptionChange('displayReleaseNotes')
  const onUseLatestWebUIChange = onOptionChange('useLatestWebUI')
  const onCatchUnhandledProtocolsChange = onOptionChange('catchUnhandledProtocols')
  const onLinkifyChange = onOptionChange('linkify')
  const onrecoverFailedHttpRequestsChange = onOptionChange('recoverFailedHttpRequests')
  const onDetectIpfsPathHeaderChange = onOptionChange('detectIpfsPathHeader')

  return html`
    <form>
      <fieldset class="mb3 pa1 pa4-ns pa3 bg-snow-muted charcoal">

${browser.i18n.getMessage('option_header_experiments')}

${browser.i18n.getMessage('option_experiments_warning')}

${browser.i18n.getMessage('option_useLatestWebUI_title')}

${browser.i18n.getMessage('option_useLatestWebUI_description')}

${switchToggle({ id: 'useLatestWebUI', checked: useLatestWebUI, onchange: onUseLatestWebUIChange })}

${browser.i18n.getMessage('option_displayNotifications_title')}

${browser.i18n.getMessage('option_displayNotifications_description')}

${switchToggle({ id: 'displayNotifications', checked: displayNotifications, onchange: onDisplayNotificationsChange })}

${browser.i18n.getMessage('option_displayReleaseNotes_title')}

${browser.i18n.getMessage('option_displayReleaseNotes_description')}

${switchToggle({ id: 'displayReleaseNotes', checked: displayReleaseNotes, onchange: onDisplayReleaseNotesChange })}

${browser.i18n.getMessage('option_catchUnhandledProtocols_title')}

${browser.i18n.getMessage('option_catchUnhandledProtocols_description')}

${switchToggle({ id: 'catchUnhandledProtocols', checked: catchUnhandledProtocols, onchange: onCatchUnhandledProtocolsChange })}

${browser.i18n.getMessage('option_recoverFailedHttpRequests_title')}

${browser.i18n.getMessage('option_recoverFailedHttpRequests_description')}

${switchToggle({ id: 'recoverFailedHttpRequests', checked: recoverFailedHttpRequests, onchange: onrecoverFailedHttpRequestsChange })}

${browser.i18n.getMessage('option_linkify_title')}

${browser.i18n.getMessage('option_linkify_description')}

${switchToggle({ id: 'linkify', checked: linkify, onchange: onLinkifyChange })}

${browser.i18n.getMessage('option_detectIpfsPathHeader_title')}

${browser.i18n.getMessage('option_detectIpfsPathHeader_description')}

                  ${browser.i18n.getMessage('option_legend_readMore')}

${switchToggle({ id: 'detectIpfsPathHeader', checked: detectIpfsPathHeader, onchange: onDetectIpfsPathHeaderChange })}

${browser.i18n.getMessage('option_logNamespaces_title')}

${browser.i18n.getMessage('option_logNamespaces_description')}

          <input
            class="bg-white navy self-center-ns"
            id="logNamespaces"
            type="text"
            required
            onchange=${onOptionChange('logNamespaces')}
            value=${logNamespaces} />

      </fieldset>
    </form>
  `
}


text:
'use strict'
/* eslint-env browser, webextensions */

import html from 'choo/html/index.js'
import browser from 'webextension-polyfill'
import switchToggle from '../../pages/components/switch-toggle.js'

export default function telemetryForm ({
  onOptionChange,
  ...stateOptions
}) {
  // @ts-expect-error - TS doesn't like the `html` template tag
  return html`
    <form>
      <fieldset class="mb3 pa1 pa4-ns pa3 bg-snow-muted charcoal">

${browser.i18n.getMessage('option_header_telemetry')}

${browser.i18n.getMessage('option_telemetry_disclaimer')}

              ${browser.i18n.getMessage('option_legend_readMore')}

${browser.i18n.getMessage('option_telemetryGroupMinimal_title')}

${browser.i18n.getMessage('option_telemetryGroupMinimal_description')}

${switchToggle({
            id: 'telemetryGroupMinimal',
            checked: stateOptions.telemetryGroupMinimal,
            onchange: onOptionChange('telemetryGroupMinimal')
          })}

      </fieldset>
    </form>
  `
}


text:
'use strict'
/* eslint-env browser, webextensions */

import browser from 'webextension-polyfill'
import html from 'choo/html/index.js'
import switchToggle from '../../pages/components/switch-toggle.js'
import { guiURLString, hostTextToArray, hostArrayToText } from '../../lib/options.js'
import { braveNodeType } from '../../lib/ipfs-client/brave.js'

// Warn about mixed content issues when changing the gateway
// to something other than HTTP or localhost
// https://github.com/ipfs-shipyard/ipfs-companion/issues/648
const secureContextUrl = /^https:\/\/|^http:\/\/localhost|^http:\/\/127.0.0.1|^http:\/\/\[::1\]/

export default function gatewaysForm ({
  ipfsNodeType,
  customGatewayUrl,
  useCustomGateway,
  useSubdomains,
  disabledOn,
  enabledOn,
  publicGatewayUrl,
  publicSubdomainGatewayUrl,
  onOptionChange
}) {
  const onCustomGatewayUrlChange = onOptionChange('customGatewayUrl', (url) => guiURLString(url, { useLocalhostName: useSubdomains }))
  const onUseCustomGatewayChange = onOptionChange('useCustomGateway')
  const onUseSubdomainProxyChange = onOptionChange('useSubdomains')
  const onPublicGatewayUrlChange = onOptionChange('publicGatewayUrl', guiURLString)
  const onPublicSubdomainGatewayUrlChange = onOptionChange('publicSubdomainGatewayUrl', guiURLString)
  const onDisabledOnChange = onOptionChange('disabledOn', hostTextToArray)
  const onEnabledOnChange = onOptionChange('enabledOn', hostTextToArray)
  const mixedContentWarning = !secureContextUrl.test(customGatewayUrl)
  const supportRedirectToCustomGateway = ipfsNodeType !== 'embedded'
  const allowChangeOfCustomGateway = ipfsNodeType === 'external'
  const braveClass = ipfsNodeType === braveNodeType ? 'brave' : ''

  return html`
    <form>
      <fieldset class="mb3 pa1 pa4-ns pa3 bg-snow-muted charcoal">

${browser.i18n.getMessage('option_header_gateways')}

${browser.i18n.getMessage('option_publicGatewayUrl_title')}

${browser.i18n.getMessage('option_publicGatewayUrl_description')}

            <input
              class="bg-white navy self-center-ns"
              id="publicGatewayUrl"
              type="url"
              inputmode="url"
              required
              pattern="^https?://[^/]+/?$"
              spellcheck="false"
              title="${browser.i18n.getMessage('option_hint_url')}"
              onchange=${onPublicGatewayUrlChange}
              value=${publicGatewayUrl} />

${browser.i18n.getMessage('option_publicSubdomainGatewayUrl_title')}

                  ${browser.i18n.getMessage('option_publicSubdomainGatewayUrl_description')}

                    ${browser.i18n.getMessage('option_legend_readMore')}

            <input
              class="bg-white navy self-center-ns"
              id="publicSubdomainGatewayUrl"
              type="url"
              inputmode="url"
              required
              pattern="^https?://[^/]+/?$"
              spellcheck="false"
              title="${browser.i18n.getMessage('option_hint_url')}"
              onchange=${onPublicSubdomainGatewayUrlChange}
              value=${publicSubdomainGatewayUrl} />

          ${supportRedirectToCustomGateway
            ? html`

${browser.i18n.getMessage('option_customGatewayUrl_title')}

${browser.i18n.getMessage('option_customGatewayUrl_description')}
                    ${mixedContentWarning ? html`<p class="red i">${browser.i18n.getMessage('option_customGatewayUrl_warning')}</p>` : null}

              <input
                class="bg-white navy self-center-ns ${braveClass}"
                id="customGatewayUrl"
                type="url"
                inputmode="url"
                required
                pattern="^https?://[^/]+/?$"
                spellcheck="false"
                title="${browser.i18n.getMessage(allowChangeOfCustomGateway ? 'option_hint_url' : 'option_hint_readonly')}"
                onchange=${onCustomGatewayUrlChange}
                ${allowChangeOfCustomGateway ? '' : 'disabled'}
                value=${customGatewayUrl} />

`
            : null}
          ${supportRedirectToCustomGateway
          ? html`

${browser.i18n.getMessage('option_useCustomGateway_title')}

${browser.i18n.getMessage('option_useCustomGateway_description')}

${switchToggle({ id: 'useCustomGateway', checked: useCustomGateway, onchange: onUseCustomGatewayChange })}

`
          : null}
          ${supportRedirectToCustomGateway
          ? html`

${browser.i18n.getMessage('option_useSubdomains_title')}

                    ${browser.i18n.getMessage('option_useSubdomains_description')}

                      ${browser.i18n.getMessage('option_legend_readMore')}

${switchToggle({ id: 'useSubdomains', checked: useSubdomains, onchange: onUseSubdomainProxyChange })}

`
            : null}
          ${supportRedirectToCustomGateway
          ? html`

${browser.i18n.getMessage('option_disabledOn_title')}

${browser.i18n.getMessage('option_disabledOn_description')}

              <textarea
                class="bg-white navy self-center-ns"
                id="disabledOn"
                spellcheck="false"
                onchange=${onDisabledOnChange}
                rows="${Math.min(disabledOn.length + 1, 10)}"
                >${hostArrayToText(disabledOn)}</textarea>

${browser.i18n.getMessage('option_enabledOn_title')}

${browser.i18n.getMessage('option_enabledOn_description')}

              <textarea
                class="bg-white navy self-center-ns"
                id="enabledOn"
                spellcheck="false"
                onchange=${onEnabledOnChange}
                rows="${Math.min(enabledOn.length + 1, 10)}"
                >${hostArrayToText(enabledOn)}</textarea>

`
            : null}

      </fieldset>
    </form>
  `
}


text:
'use strict'
/* eslint-env browser, webextensions */

import browser from 'webextension-polyfill'
import html from 'choo/html/index.js'
import { braveNodeType } from '../../lib/ipfs-client/brave.js'

export default function ipfsNodeForm ({ ipfsNodeType, ipfsNodeConfig, onOptionChange, withNodeFromBrave }) {
  const onIpfsNodeTypeChange = onOptionChange('ipfsNodeType')
  const onIpfsNodeConfigChange = onOptionChange('ipfsNodeConfig')
  const braveClass = ipfsNodeType === braveNodeType ? 'brave' : ''
  return html`
    <form>
      <fieldset class="mb3 pa1 pa4-ns pa3 bg-snow-muted charcoal">

${browser.i18n.getMessage('option_header_nodeType')}

${browser.i18n.getMessage('option_ipfsNodeType_title')}

${browser.i18n.getMessage('option_ipfsNodeType_external_description')}

                ${withNodeFromBrave ? html`<p>${browser.i18n.getMessage('option_ipfsNodeType_brave_description')}</p>` : null}

${browser.i18n.getMessage('option_ipfsNodeType_embedded_description')}

                  ${browser.i18n.getMessage('option_legend_readMore')}

          <select id="ipfsNodeType" name='ipfsNodeType' class="self-center-ns bg-white navy ${braveClass}" onchange=${onIpfsNodeTypeChange}>
            <option
              value='external'
              selected=${ipfsNodeType === 'external'}>
              ${browser.i18n.getMessage('option_ipfsNodeType_external')}
            </option>
            ${withNodeFromBrave
            ? html`<option
                  value='external:brave'
                  selected=${ipfsNodeType === 'external:brave'}>
                  ${browser.i18n.getMessage('option_ipfsNodeType_brave')}
                </option>`
              : null}
            <option
              value='embedded'
              selected=${ipfsNodeType === 'embedded'}>
              ${browser.i18n.getMessage('option_ipfsNodeType_embedded')} (${browser.i18n.getMessage('option_experimental')})
            </option>
          </select>

        ${ipfsNodeType.startsWith('embedded')
        ? html`

${browser.i18n.getMessage('option_ipfsNodeConfig_title')}

${browser.i18n.getMessage('option_ipfsNodeConfig_description')}

            <textarea
              class="bg-white navy self-center-ns"
              spellcheck="false"
              id="ipfsNodeConfig"
              rows="${Math.min((ipfsNodeConfig.match(/\n/g) || []).length + 1, 30)}"
              onchange=${onIpfsNodeConfigChange}>${ipfsNodeConfig}</textarea>

`
          : null}
      </fieldset>
    </form>
  `
}


text:
'use strict'
/* eslint-env browser, webextensions */

import browser from 'webextension-polyfill'
import html from 'choo/html/index.js'

export default function resetForm ({
  onOptionsReset
}) {
  return html`
    <form>
      <fieldset class="mb3 pa1 pa4-ns pa3 bg-snow-muted charcoal">

${browser.i18n.getMessage('option_header_reset')}

${browser.i18n.getMessage('option_resetAllOptions_title')}

${browser.i18n.getMessage('option_resetAllOptions_description')}

<button id="resetAllOptions" class="Button transition-all sans-serif v-mid fw5 nowrap lh-copy bn br1 pa2 pointer focus-outline white bg-red white" onclick=${onOptionsReset}>${browser.i18n.getMessage('option_resetAllOptions_title')}</button>

      </fieldset>
    </form>
  `
}


text:
'use strict'
/* eslint-env browser, webextensions */

import browser from 'webextension-polyfill'
import html from 'choo/html/index.js'
import switchToggle from '../../pages/components/switch-toggle.js'

export default function fileImportForm ({
  importDir, openViaWebUI, preloadAtPublicGateway, onOptionChange
}) {
  const onImportDirChange = onOptionChange('importDir')
  const onOpenViaWebUIChange = onOptionChange('openViaWebUI')
  const onPreloadAtPublicGatewayChange = onOptionChange('preloadAtPublicGateway')
  return html`
    <form>
      <fieldset class="mb3 pa1 pa4-ns pa3 bg-snow-muted charcoal">

${browser.i18n.getMessage('option_header_fileImport')}

${browser.i18n.getMessage('option_importDir_title')}

                ${browser.i18n.getMessage('option_importDir_description')}

                  ${browser.i18n.getMessage('option_legend_readMore')}

          <input
            class="bg-white navy self-center-ns"
            id="importDir"
            type="text"
            pattern="^\/(.*)"
            required
            onchange=${onImportDirChange}
            value=${importDir} />

${browser.i18n.getMessage('option_openViaWebUI_title')}

${browser.i18n.getMessage('option_openViaWebUI_description')}

${switchToggle({ id: 'openViaWebUI', checked: openViaWebUI, onchange: onOpenViaWebUIChange })}

${browser.i18n.getMessage('option_preloadAtPublicGateway_title')}

${browser.i18n.getMessage('option_preloadAtPublicGateway_description')}

${switchToggle({ id: 'preloadAtPublicGateway', checked: preloadAtPublicGateway, onchange: onPreloadAtPublicGatewayChange })}

      </fieldset>
    </form>
  `
}


text:
'use strict'
/* eslint-env browser, webextensions */

import browser from 'webextension-polyfill'
import html from 'choo/html/index.js'
import switchToggle from '../../pages/components/switch-toggle.js'

export default function globalToggleForm ({ active, onOptionChange }) {
  const toggle = onOptionChange('active')
  return html`
    <form class="db b mb3 bg-aqua-muted charcoal">
      <label for="active" class="dib pa3 flex items-center pointer ${!active ? 'charcoal bg-gray-muted br2' : ''}">
        ${switchToggle({ id: 'active', checked: active, onchange: toggle, style: 'mr3' })}
        ${browser.i18n.getMessage('panel_headerActiveToggleTitle')}
      </label>
    </form>
  `
}


text:
'use strict'
/* eslint-env browser, webextensions */

import browser from 'webextension-polyfill'
import html from 'choo/html/index.js'
import switchToggle from '../../pages/components/switch-toggle.js'

export default function dnslinkForm ({
  dnslinkPolicy,
  dnslinkDataPreload,
  dnslinkRedirect,
  onOptionChange
}) {
  const onDnslinkPolicyChange = onOptionChange('dnslinkPolicy')
  const onDnslinkRedirectChange = onOptionChange('dnslinkRedirect')
  const onDnslinkDataPreloadChange = onOptionChange('dnslinkDataPreload')

  return html`
    <form>
      <fieldset class="mb3 pa1 pa4-ns pa3 bg-snow-muted charcoal">

${browser.i18n.getMessage('option_header_dnslink')}

${browser.i18n.getMessage('option_dnslinkPolicy_title')}

                ${browser.i18n.getMessage('option_dnslinkPolicy_description')}

                  ${browser.i18n.getMessage('option_legend_readMore')}

          <select id="dnslinkPolicy" name='dnslinkPolicy' class="self-center-ns bg-white navy" onchange=${onDnslinkPolicyChange}>
            <option
              value='false'
              selected=${String(dnslinkPolicy) === 'false'}>
              ${browser.i18n.getMessage('option_dnslinkPolicy_disabled')}
            </option>
            <option
              value='best-effort'
              selected=${dnslinkPolicy === 'best-effort'}>
              ${browser.i18n.getMessage('option_dnslinkPolicy_bestEffort')}
            </option>
            <option
              value='enabled'
              selected=${dnslinkPolicy === 'enabled'}>
              ${browser.i18n.getMessage('option_dnslinkPolicy_enabled')}
            </option>
          </select>

${browser.i18n.getMessage('option_dnslinkDataPreload_title')}

${browser.i18n.getMessage('option_dnslinkDataPreload_description')}

${switchToggle({ id: 'dnslinkDataPreload', checked: dnslinkDataPreload, disabled: dnslinkRedirect, onchange: onDnslinkDataPreloadChange })}

${browser.i18n.getMessage('option_dnslinkRedirect_title')}

                ${browser.i18n.getMessage('option_dnslinkRedirect_description')}
                ${dnslinkRedirect ? html`<p class="red i">${browser.i18n.getMessage('option_dnslinkRedirect_warning')}</p>` : null}

                  ${browser.i18n.getMessage('option_legend_readMore')}

${switchToggle({ id: 'dnslinkRedirect', checked: dnslinkRedirect, onchange: onDnslinkRedirectChange })}

      </fieldset>
    </form>
  `
}


text:
'use strict'
/* eslint-env browser, webextensions */

import browser from 'webextension-polyfill'
import html from 'choo/html/index.js'
import { guiURLString } from '../../lib/options.js'
import { braveNodeType } from '../../lib/ipfs-client/brave.js'
import switchToggle from '../../pages/components/switch-toggle.js'

export default function apiForm ({ ipfsNodeType, ipfsApiUrl, ipfsApiPollMs, automaticMode, onOptionChange }) {
  const onIpfsApiUrlChange = onOptionChange('ipfsApiUrl', (url) => guiURLString(url, { useLocalhostName: false }))
  const onIpfsApiPollMsChange = onOptionChange('ipfsApiPollMs')
  const onAutomaticModeChange = onOptionChange('automaticMode')
  const apiAddresEditable = ipfsNodeType === 'external'
  const braveClass = ipfsNodeType === braveNodeType ? 'brave' : ''

  return html`
    <form>
      <fieldset class="mb3 pa1 pa4-ns pa3 bg-snow-muted charcoal">

${browser.i18n.getMessage('option_header_api')}

${browser.i18n.getMessage('option_ipfsApiUrl_title')}

${browser.i18n.getMessage('option_ipfsApiUrl_description')}

          <input
            class="bg-white navy self-center-ns ${braveClass}"
            id="ipfsApiUrl"
            type="url"
            inputmode="url"
            required
            pattern="^https?://[^/]+/?$"
            spellcheck="false"
            title="${browser.i18n.getMessage(apiAddresEditable ? 'option_hint_url' : 'option_hint_readonly')}"
            onchange=${onIpfsApiUrlChange}
            ${apiAddresEditable ? '' : 'disabled'}
            value=${ipfsApiUrl} />

${browser.i18n.getMessage('option_ipfsApiPollMs_title')}

${browser.i18n.getMessage('option_ipfsApiPollMs_description')}

          <input
            class="bg-white navy self-center-ns"
            id="ipfsApiPollMs"
            type="number"
            inputmode="numeric"
            min="1000"
            max="60000"
            step="1000"
            required
            onchange=${onIpfsApiPollMsChange}
            value=${ipfsApiPollMs} />

${browser.i18n.getMessage('option_automaticMode_title')}

${browser.i18n.getMessage('option_automaticMode_description')}

${switchToggle({ id: 'automaticMode', checked: automaticMode, onchange: onAutomaticModeChange })}

      </fieldset>
    </form>
  `
}


text:
'use strict'
/* eslint-env browser, webextensions */

import browser from 'webextension-polyfill'
import PQueue from 'p-queue'

/*
 * This content script is responsible for performing the logic of replacing
 * plain text with IPFS addresses with clickable links.
 * Loosely based on https://github.com/mdn/webextensions-examples/blob/master/emoji-substitution/substitute.js
 * Note that this is a quick&dirty PoC and may slow down browsing experience.
 * Test page: http://bit.ly/2yuknPI
 * TODO: measure & improve performance
 */

;(function (alreadyLoaded) {
  if (alreadyLoaded) {
    return
  }

  // Limit contentType to "text/plain" or "text/html"
  if (document.contentType !== undefined && document.contentType !== 'text/plain' && document.contentType !== 'text/html') {
    return
  }

  // linkify lock
  window.ipfsCompanionLinkifiedDOM = true
  window.ipfsCompanionLinkifyValidationCache = new Map()

  const urlRE = /(?:\s+|^)(\/ip(?:f|n)s\/|dweb:\/ip(?:f|n)s\/|ipns:\/\/|ipfs:\/\/)([^\s+"<>:]+)/g

  // tags we will scan looking for un-hyperlinked IPFS addresses
  const allowedParents = [
    'abbr', 'acronym', 'address', 'applet', 'b', 'bdo', 'big', 'blockquote', 'body',
    'caption', 'center', 'cite', 'code', 'dd', 'del', 'div', 'dfn', 'dt', 'em',
    'fieldset', 'font', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'i', 'iframe',
    'ins', 'kdb', 'li', 'object', 'pre', 'p', 'q', 'samp', 'small', 'span', 'strike',
    's', 'strong', 'sub', 'sup', 'td', 'th', 'tt', 'u', 'var'
  ]

  const textNodeXpath = './/text()[' +
    "(contains(., '/ipfs/') or contains(., '/ipns/') or contains(., 'ipns:/') or contains(., 'ipfs:/')) and " +
    'not(ancestor::a) and not(ancestor::script) and not(ancestor::style) and ' +
    '(parent::' + allowedParents.join(' or parent::') + ') ' +
    ']'

  function init () {
    // Linkify jobs are executed one by one
    // (fixes race-conditions in huge DOMs, does not lock UI)
    const linkifyJobs = new PQueue({ concurrency: 1 })
    // console.log('[ipfs-companion] running Linkify experiment')
    linkifyContainer(document.body, linkifyJobs)
      .then(() => {
        // console.log('[ipfs-companion] registering MutationObserver for Linkify experiment')
        new MutationObserver(function (mutations) {
          mutations.forEach(async (mutation) => linkifyMutation(mutation, linkifyJobs))
        }).observe(document.body, {
          characterData: true,
          childList: true,
          subtree: true
        })
      })
  }

  async function linkifyMutation (mutation, linkifyJobs) {
    linkifyJobs = linkifyJobs || new PQueue({ concurrency: 1 })
    if (mutation.type === 'childList') {
      for (const addedNode of mutation.addedNodes) {
        if (addedNode.nodeType === Node.TEXT_NODE) {
          linkifyJobs.add(async () => linkifyTextNode(addedNode))
        } else {
          linkifyJobs.add(async () => linkifyContainer(addedNode))
        }
      }
    }
    if (mutation.type === 'characterData') {
      linkifyJobs.add(async () => linkifyTextNode(mutation.target))
    }
    await linkifyJobs.onIdle()
  }

  async function linkifyContainer (container, linkifyJobs) {
    if (!container || !container.nodeType || container.isContentEditable) {
      return
    }
    if (container.className && container.className.match && container.className.match(/\blinkifiedIpfsAddress\b/)) {
      // prevent infinite recursion
      return
    }
    const xpathResult = document.evaluate(textNodeXpath, container, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null)
    let i = 0
    let node = null

    linkifyJobs = linkifyJobs || new PQueue({ concurrency: 1 })
    while ((node = xpathResult.snapshotItem(i++))) {
      const parent = node.parentNode
      // Skip if no longer in visible DOM
      if (!parent || !container.contains(node)) continue
      const currentNode = node
      linkifyJobs.add(async () => linkifyTextNode(currentNode))
    }
    await linkifyJobs.onIdle()
  }

  function textToIpfsResource (match) {
    let root = match[1]
    let path = match[2]

    // skip trailing dots and commas
    path = path.replace(/[.,]*$/, '')

    // convert various protocol variants to regular URL at the public gateway
    if (root === 'ipfs://') {
      root = '/ipfs/'
    } else if (root === 'ipns://') {
      root = '/ipns/'
    } else if (root === 'dweb:/ipfs/') {
      root = '/ipfs/'
    } else if (root === 'dweb:/ipns/') {
      root = '/ipns/'
    }
    return validIpfsResource(root + path)
  }

  async function validIpfsResource (path) {
    // validation is expensive, caching result improved performance
    // on page that have multiple copies of the same path
    if (window.ipfsCompanionLinkifyValidationCache.has(path)) {
      return window.ipfsCompanionLinkifyValidationCache.get(path)
    }
    try {
      // Callback wrapped in promise -- Chrome compatibility
      const checkResult = await browser.runtime.sendMessage({ pubGwUrlForIpfsOrIpnsPath: path })
      window.ipfsCompanionLinkifyValidationCache.set(path, checkResult.pubGwUrlForIpfsOrIpnsPath)
    } catch (error) {
      window.ipfsCompanionLinkifyValidationCache.set(path, null)
      console.error('pubGwUrlForIpfsOrIpnsPath.error for ' + path, error)
    }
    return window.ipfsCompanionLinkifyValidationCache.get(path)
  }

  function isParentTreeSafe (node) {
    let parent = node.parentNode
    // Skip if no longer in visible DOM
    if (!parent) return false
    // Walk back over parent tree and check each of them
    while (parent) {
      // Skip forms, textareas
      if (parent.isContentEditable) return false
      // Skip already linkified nodes
      if (parent.className && parent.className.match(/\blinkifiedIpfsAddress\b/)) return false
      // Skip styled <pre> -- often highlighted by scripts
      if (parent.tagName === 'PRE' && parent.className) return false
      // Skip if no longer in visible DOM
      if (!(parent instanceof HTMLDocument) && !parent.parentNode) return false
      parent = parent.parentNode
    }
    return true
  }

  async function linkifyTextNode (node) {
    // Skip if node belongs to a parent from unsafe tree
    if (!isParentTreeSafe(node)) return

    let link
    let match
    const txt = node.textContent
    let span = null
    let point = 0

    while ((match = urlRE.exec(txt))) {
      link = await textToIpfsResource(match)
      if (link) {
        const textChunk = document.createTextNode(match[0])
        if (span == null) {
          // Create a span to hold the new text with links in it.
          span = document.createElement('span')
          span.className = 'linkifiedIpfsAddress'
        }
        // put in text up to the link
        span.appendChild(document.createTextNode(txt.substring(point, match.index)))
        // create a link and put it in the span
        const a = document.createElement('a')
        a.className = 'linkifiedIpfsAddress'
        a.setAttribute('href', link)
        a.appendChild(textChunk)
        span.appendChild(a)
        // track insertion point
        const replaceLength = match[0].length
        point = match.index + replaceLength
      }
    }
    if (span && node.parentNode) {
      try {
        // take the text after the last link
        span.appendChild(document.createTextNode(txt.substring(point, txt.length)))
        // replace the original text with the new span
        node.parentNode.replaceChild(span, node)
      } catch (e) {
        console.error(e)
      }
    }
  }

  init()
}(window.ipfsCompanionLinkifiedDOM))


text:
diff --git a/node_modules/multiaddr/src/convert.js b/node_modules/multiaddr/src/convert.js
index c315201..80170c7 100644
--- a/node_modules/multiaddr/src/convert.js
+++ b/node_modules/multiaddr/src/convert.js
@@ -51,6 +51,7 @@ Convert.toString = function convertToString (proto, buf) {
     case 55: // dns6
     case 56: // dnsaddr
     case 400: // unix
+    case 466: // certhash
     case 777: // memory
       return bytes2str(buf)

@@ -84,6 +85,7 @@ Convert.toBytes = function convertToBytes (/** @type {string | number } */ proto
     case 55: // dns6
     case 56: // dnsaddr
     case 400: // unix
+    case 466: // certhash
     case 777: // memory
       return str2bytes(str)

diff --git a/node_modules/multiaddr/src/protocols-table.js b/node_modules/multiaddr/src/protocols-table.js
index 3431af5..8939fb1 100644
--- a/node_modules/multiaddr/src/protocols-table.js
+++ b/node_modules/multiaddr/src/protocols-table.js
@@ -60,6 +60,9 @@ Protocols.table = [
   [445, 296, 'onion3'],
   [446, V, 'garlic64'],
   [460, 0, 'quic'],
+  [461, 0, 'quic-v1'],
+  [465, 0, 'webtransport'],
+  [466, V, 'certhash'],
   [477, 0, 'ws'],
   [478, 0, 'wss'],
   [479, 0, 'p2p-websocket-star'],


text:
diff --git a/node_modules/@multiformats/multiaddr/dist/src/protocols-table.js b/node_modules/@multiformats/multiaddr/dist/src/protocols-table.js
index 1ee89f9..ed10f8e 100644
--- a/node_modules/@multiformats/multiaddr/dist/src/protocols-table.js
+++ b/node_modules/@multiformats/multiaddr/dist/src/protocols-table.js
@@ -32,6 +32,7 @@ export const table = [
     [445, 296, 'onion3'],
     [446, V, 'garlic64'],
     [460, 0, 'quic'],
+    [461, 0, 'quic-v1'],
     [465, 0, 'webtransport'],
     [466, V, 'certhash'],
     [477, 0, 'ws'],
diff --git a/node_modules/@multiformats/multiaddr/src/protocols-table.ts b/node_modules/@multiformats/multiaddr/src/protocols-table.ts
index 8d864cd..2e352ea 100644
--- a/node_modules/@multiformats/multiaddr/src/protocols-table.ts
+++ b/node_modules/@multiformats/multiaddr/src/protocols-table.ts
@@ -41,6 +41,7 @@ export const table: Array<[number, number, string, boolean?, boolean?]> = [
   [445, 296, 'onion3'],
   [446, V, 'garlic64'],
   [460, 0, 'quic'],
+  [461, 0, 'quic-v1'],
   [465, 0, 'webtransport'],
   [466, V, 'certhash'],
   [477, 0, 'ws'],


text:
# ![Aimbot V2](https://user-images.githubusercontent.com/76539058/211103495-6f208311-c282-43d7-982e-d3ead86bef26.png) [![Visitors](https://visitor-badge.glitch.me/badge?page_id=Exunys.Aimbot-V2)](https://github.com/Exunys/Aimbot-V2)
## ![Fast](https://img.shields.io/badge/Fast-blue) ![Secure](https://img.shields.io/badge/Secure-darkgreen) ![Customizable](https://img.shields.io/badge/Customizable-purple) ![Organized](https://img.shields.io/badge/Organized-red) ![Optimized](https://img.shields.io/badge/Optimized-darkblue) ![Data Storing](https://img.shields.io/badge/Data-Storing-green) ![Undetectable](https://img.shields.io/badge/Undetectable-violet) ![Simple](https://img.shields.io/badge/Simple-yellow) ![Support](https://img.shields.io/badge/Multiplatform-Support-darkred) ![Reliable](https://img.shields.io/badge/Reliable-lightblue) ![Trusted](https://img.shields.io/badge/Trusted-lightgreen)
##
| [Update Log](https://github.com/Exunys/Aimbot-V2#update-log-ddmmyyyy0) | [Notices / Dependencies](https://github.com/Exunys/Aimbot-V2#notices) | [Configuration](https://github.com/Exunys/Aimbot-V2#configuration) | [Fixes / Troubleshooting](https://github.com/Exunys/Aimbot-V2#fixes) | [Hotkey](https://github.com/Exunys/Aimbot-V2#hotkey) | [Functions](https://github.com/Exunys/Aimbot-V2#functions) | [Previews](https://github.com/Exunys/Aimbot-V2#previews) | [Script](https://github.com/Exunys/Aimbot-V2#script)
| :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: |
##
![Aimbot V2](https://user-images.githubusercontent.com/76539058/197401886-b988d375-a258-4555-a014-476295b67752.png)
## Information

### ℹThis repository is discontinued. Use the new script [AirHub](https://github.com/Exunys/AirHub).

This script is a universal aim-locking script. Works on all games which use the default character. This specific version has lots of crucial improvements such as:
- More Functions / Features & Settings
- Security / Decreased Detection Risk
- Optimized & Organized Code
- Speed / Faster Execution & Functionality
- Configurable Settings
- Storing
- Execution / Precise & Efficient
- Third Person Support
### License
This project is completely free and open sourced. But, that does not mean you own rights to it. Read this [document](https://github.com/Exunys/Aimbot-V2/blob/main/LICENSE) for more information.
You can re-use / stitch this script or any system of this project into any of your repositories, as long as you credit the developer [Exunys](https://github.com/Exunys).

## Update Log *(DD/MM/YYYY)*
- [23/10/2022] - **Added Third Person Support** (Read [*Script Examples*](https://github.com/Exunys/Aimbot-V2#script-examples))
- [08/01/2023] - **Bug fixes, optimizations & new script - [Aimbot V2 GUI](https://github.com/Exunys/Aimbot-V2/blob/main/Resources/Scripts/Aimbot%20V2%20GUI.lua)** (Read [*Aimbot V2 GUI*](https://github.com/Exunys/Aimbot-V2#aimbot-v2-gui))
## Notices
* The script will not function properly unless your exploit fully supports / includes these following functions & libraries:
  - `isfolder()`, `makefolder()` & `delfolder()`
  - `isfile()`, `writefile()` & `delfile()`
  - `getgenv()`
  - `syn.queue_on_teleport()` / `queue_on_teleport()`
  - `Drawing`
* This script will store your changed settings every 10 seconds passed. You can also disable this feature:
```lua
getgenv().Aimbot.Settings.SaveSettings = false
```
* This script is indeed universal, but it might not run on games with specific character constructions (custom characters).
* A recommended exploit to run this script on is [Synapse X](https://x.synapse.to).
## Environment
The script's environment is stored as:
```lua
getgenv().Aimbot
```
More on how to configure the aimbot below this part.
## Configuration
This script includes settings which can be easily configured to your preference.
### Preview Of The Settings
```lua
getgenv().Aimbot.Settings = {
    SendNotifications = true,
    SaveSettings = true, -- Re-execute upon changing
    ReloadOnTeleport = true,
    Enabled = true,
    TeamCheck = false,
    AliveCheck = true,
    WallCheck = false, -- Laggy
    Sensitivity = 0, -- Animation length (in seconds) before fully locking onto target
    ThirdPerson = false, -- Uses mousemoverel instead of CFrame to support locking in third person (could be choppy)
    ThirdPersonSensitivity = 3, -- Boundary: 0.1 - 5
    TriggerKey = "MouseButton2",
    Toggle = false,
    LockPart = "Head" -- Body part to lock on (Character part's name)
}

getgenv().Aimbot.FOVSettings = {
    Enabled = true,
    Visible = true,
    Amount = 90,
    Color = "255, 255, 255",
    LockedColor = "255, 70, 70",
    Transparency = 0.5,
    Sides = 60,
    Thickness = 1,
    Filled = false
}
```
**Graphic View (Horizontal Tree Diagram)**

![image](https://user-images.githubusercontent.com/76539058/167846294-9a9f023a-14a8-42f2-b693-8557bc6a8ba5.png)

⚠WARNING⚠ - ***The WallCheck function is very laggy, it is recommended you have it set as*** **false** ***at all times!***
* You can also find the JSON format (which is the way they get stored) [here](https://github.com/Exunys/Aimbot-V2/tree/main/Resources/Settings).
* The Lua format of the settings / the factory reset script can be found [here](https://github.com/Exunys/Aimbot-V2/blob/main/Resources/Scripts/Manual%20Factory%20Reset%20Settings.lua).

By reading the visual representation of the configuration part of the environment table, it should be pretty easy to configure the script afterwards. Here are some examples:

### Script Examples

The following script will disable the aimbot temporarily:
```lua
getgenv().Aimbot.Settings.Enabled = false
```
You can also change the color of the FOV Circle:
```lua
getgenv().Aimbot.FOVSettings.Color = "50, 255, 70" -- The colors must be fed as strings in RGB format. [(R)ed (0 - 255); (G)reen (0 - 255); (B)lue (0 - 255)]
```
**The script only accepts RGB configurations in strings as colors, if you input anything else, the script will break and not execute. Read below on how to fix this.**

You can also disable FOV Checking incase you don't find it useful / don't need it:
```lua
getgenv().Aimbot.FOVSettings.Enabled = false
```
If you want the script to work in third person, you can enable the *ThirdPerson* setting:
```lua
getgenv().Aimbot.Settings.ThirdPerson = true
```
If you want to edit the sensitivity for the third person mode:
```lua
-- The locking animation becomes faster as you increase the value (meaning 5 = fastest). The script becomes choppy if you increase the value.
getgenv().Aimbot.Settings.ThirdPersonSensitivity = 3 -- Boundary: 0.1 - 5
```

The options are endless, you can configure the script in any way you desire.

Read about the input types and more information about the drawing library in use for the FOV Circle (to learn how to configure it) [here](https://x.synapse.to/docs/reference/drawing_lib.html).

### Disable Storage System
If you want to disable the script saving the settings & delete the saved settings, you can do that automatically by executing the code below:
```lua
getgenv().Aimbot.Settings.SaveSettings = false
loadstring(game:HttpGet("https://raw.githubusercontent.com/Exunys/Aimbot-V2/main/Resources/Scripts/Delete%20Settings%20-%20Fix%20Script.lua"))()
```

## Fixes
If the script is not running upon execution, try a few of the solutions below:
### Solutions
* There is possibly a configuration that is unacceptable at most cases. Execute [this script](https://github.com/Exunys/Aimbot-V2/blob/main/Resources/Scripts/Delete%20Settings%20-%20Fix%20Script.lua) and restart your game for changes to take effect.
* Open your exploit's root folder, find a folder named `workspace` and look for a folder named `Exunys Developer`, inside there should be another folder named `Aimbot`. Once you find it, delete this folder and restart your game.

If none of these solutions work, check if your exploit is supported (read the **Notices** part). If the script still doesn't work, contact Exunys & report the problem you are experiencing in detail.

Check if your exploit is supported [here](https://github.com/Exunys/Aimbot-V2/blob/main/Resources/Scripts/Support%20Checker.lua).
## Hotkey
If you want to change the key you want to press to trigger the Aimbot, configure the ***TriggerKey*** setting.
- *Examples* :
```lua
getgenv().Aimbot.Settings.TriggerKey = "E" -- E is the key's name (This is equivalent to Enum.KeyCode.E, except, the script only handles strings)
```
```lua
getgenv().Aimbot.Settings.TriggerKey = "MouseButton1" -- MouseButton1 is the key's name [LMB] (This is equivalent to Enum.UserInputType.MouseButton1, except, the script only handles strings)
```
This script will work if the input type selected is either a *KeyCode* or *UserInputType* so all you have to input is the last part / the key's name.
- Read: [KeyCode](https://developer.roblox.com/en-us/api-reference/enum/KeyCode), [UserInputType](https://developer.roblox.com/en-us/api-reference/enum/UserInputType)
## Functions
This script includes built-in functions to control the Aimbot.
The functions can be accessed by indexing **Functions** in the Environment. Example:
```lua
getgenv().Aimbot.Functions
```
### Their purposes
* `Functions:Exit()`
  - Exits (unexecutes) the script and leaves no traces back.
* `Functions:Restart()`
  - Restarts the script, good for incase the script starts lagging.
* `Functions:ResetSettings()`
  - Factory resets the settings and wipes the previous ones that were saved to the workspace.

- Exit
```lua
getgenv().Aimbot.Functions:Exit()
```
- Restart
```lua
getgenv().Aimbot.Functions:Restart()
```
- Reset Settings
```lua
getgenv().Aimbot.Functions:ResetSettings()
```
## Previews

https://user-images.githubusercontent.com/76539058/148576112-928455f3-b071-4650-b2a6-5fb45cbfcf04.mp4

Settings used in the video above:
```lua
getgenv().Aimbot.Settings.Sensitivity = 0.5
getgenv().Aimbot.FOVSettings.Amount = 300
getgenv().Aimbot.FOVSettings.Sides = 18
getgenv().Aimbot.FOVSettings.Transparency = 1
getgenv().Aimbot.FOVSettings.Thickness = 2
getgenv().Aimbot.FOVSettings.Color = "25, 70, 200"
```

https://user-images.githubusercontent.com/76539058/148576459-11e0d92b-6658-4dc5-9841-2c18f61adde9.mp4

The video above presents the `Environment.Functions:ResetSettings()` function. Read the **Functions** part for more.
```lua
getgenv().Aimbot.Functions:ResetSettings()
```

https://user-images.githubusercontent.com/76539058/148576715-c90af8c4-a530-45b3-8b76-a014797b3eb5.mp4

The video above presents the `Environment.Functions:Exit()` function. Read the **Functions** part for more.
```lua
getgenv().Aimbot.Functions:Exit()
```

https://user-images.githubusercontent.com/76539058/197402881-bb298b50-6e90-4b57-ab70-258f3f2b4521.mp4

The video above shows how the script functions when the `Environment.Settings.ThirdPerson` value is set to `true`.
```lua
getgenv().Aimbot.Settings.ThirdPerson = true
```

https://user-images.githubusercontent.com/76539058/211179224-df514393-1d7f-46fc-ad3d-eab1035b2ac3.mp4

The video above shows the [Aimbot V2 GUI](https://github.com/Exunys/Aimbot-V2/blob/main/Resources/Scripts/Aimbot%20V2%20GUI.lua).

## Raw Source
- This source does not include any Exunys Developer systems like storing the settings, sending notifications, using colors as strings, reloading the script on teleport etc.
- This source is more optimized and relatively smaller to allow the user to modify the code and freely put it into their own script. But of course, do not forget that this script has a [license](https://github.com/Exunys/Aimbot-V2/blob/main/LICENSE).
- The raw source can be found [here](https://github.com/Exunys/Aimbot-V2/blob/main/Resources/Scripts/Raw%20Main.lua).
## Aimbot V2 GUI
This script can be found [here](https://github.com/Exunys/Aimbot-V2/blob/main/Resources/Scripts/Aimbot%20V2%20GUI.lua). The script is powered by [Pepsi's UI Library](https://v3rmillion.net/showthread.php?tid=1139856) and it uses the [Raw Aimbot V2 Source](https://raw.githubusercontent.com/Exunys/Aimbot-V2/main/Resources/Scripts/Raw%20Main.lua).

This script is the same as the raw source but implemented into a UI Library which can be used as an independent script rather then an embeddable script for other developers.
## Script
### GUI
Load the script by copying it from [here](https://github.com/Exunys/Aimbot-V2/blob/main/Resources/Scripts/Aimbot%20V2%20GUI.lua) or by executing the code below.
```lua
loadstring(game:HttpGet("https://raw.githubusercontent.com/Exunys/Aimbot-V2/main/Resources/Scripts/Aimbot%20V2%20GUI.lua"))()
```
### Default (With Exunys Developer Systems)
Load the script by copying it from [here](https://github.com/Exunys/Aimbot-V2/blob/main/Resources/Scripts/Main.lua) or by executing the code below.
```lua
loadstring(game:HttpGet("https://raw.githubusercontent.com/Exunys/Aimbot-V2/main/Resources/Scripts/Main.lua"))()
```
### Raw (Without Exunys Developer Systems)
Load the script by copying it from [here](https://github.com/Exunys/Aimbot-V2/blob/main/Resources/Scripts/Raw%20Main.lua) or by executing the code below.

```lua
loadstring(game:HttpGet("https://raw.githubusercontent.com/Exunys/Aimbot-V2/main/Resources/Scripts/Raw%20Main.lua"))()
```
## Developer Contact Information
 ![Untitled](https://user-images.githubusercontent.com/76539058/211099673-3d1d2025-1739-483f-b752-627097b49f44.png)

![Untitled](https://user-images.githubusercontent.com/76539058/211104101-d850c778-6273-49ee-aa48-70846ddd2d45.png) ![Untitled](https://user-images.githubusercontent.com/76539058/211103792-1546762b-03ef-453c-9f7f-78839cf3bc7a.png) [![E-Mail](https://raw.githubusercontent.com/gauravghongde/social-icons/master/PNG/White/Gmail_white.png)](mailto:exunys@gang.email) ![Untitled](https://user-images.githubusercontent.com/76539058/211103824-21b70a3c-1d65-4920-bf69-0ddb61ca59c3.png) [![Discord](https://raw.githubusercontent.com/gauravghongde/social-icons/master/PNG/White/Discord_white.png)](https://discord.com/users/611111398818316309)

 ![Untitled](https://user-images.githubusercontent.com/76539058/211099673-3d1d2025-1739-483f-b752-627097b49f44.png)

repo_language: Lua

file_name: Delete Settings - Fix Script.lua

mime_type: text/plain


text:
--// Preventing Multiple Processes

pcall(function()
	getgenv().Aimbot.Functions:Exit()
end)

--// Environment

getgenv().Aimbot = {}
local Environment = getgenv().Aimbot

--// Services

local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")
local HttpService = game:GetService("HttpService")
local TweenService = game:GetService("TweenService")
local StarterGui = game:GetService("StarterGui")
local Players = game:GetService("Players")
local Camera = game:GetService("Workspace").CurrentCamera

--// Variables

local LocalPlayer = Players.LocalPlayer
local Title = "Exunys Developer"
local FileNames = {"Aimbot", "Configuration.json", "Drawing.json"}
local Typing, Running, Animation, RequiredDistance, ServiceConnections = false, false, nil, 2000, {}

--// Support Functions

local mousemoverel = mousemoverel or (Input and Input.MouseMove)
local queueonteleport = queue_on_teleport or syn.queue_on_teleport

--// Script Settings

Environment.Settings = {
	SendNotifications = true,
	SaveSettings = true, -- Re-execute upon changing
	ReloadOnTeleport = true,
	Enabled = true,
	TeamCheck = false,
	AliveCheck = true,
	WallCheck = false, -- Laggy
	Sensitivity = 0, -- Animation length (in seconds) before fully locking onto target
	ThirdPerson = false, -- Uses mousemoverel instead of CFrame to support locking in third person (could be choppy)
	ThirdPersonSensitivity = 3, -- Boundary: 0.1 - 5
	TriggerKey = "MouseButton2",
	Toggle = false,
	LockPart = "Head" -- Body part to lock on
}

Environment.FOVSettings = {
	Enabled = true,
	Visible = true,
	Amount = 90,
	Color = "255, 255, 255",
	LockedColor = "255, 70, 70",
	Transparency = 0.5,
	Sides = 60,
	Thickness = 1,
	Filled = false
}

Environment.FOVCircle = Drawing.new("Circle")
Environment.Locked = nil

--// Core Functions

local function Encode(Table)
	if Table and type(Table) == "table" then
		local EncodedTable = HttpService:JSONEncode(Table)

		return EncodedTable
	end
end

local function Decode(String)
	if String and type(String) == "string" then
		local DecodedTable = HttpService:JSONDecode(String)

		return DecodedTable
	end
end

local function GetColor(Color)
	local R = tonumber(string.match(Color, "([%d]+)[%s]*,[%s]*[%d]+[%s]*,[%s]*[%d]+"))
	local G = tonumber(string.match(Color, "[%d]+[%s]*,[%s]*([%d]+)[%s]*,[%s]*[%d]+"))
	local B = tonumber(string.match(Color, "[%d]+[%s]*,[%s]*[%d]+[%s]*,[%s]*([%d]+)"))

	return Color3.fromRGB(R, G, B)
end

local function SendNotification(TitleArg, DescriptionArg, DurationArg)
	if Environment.Settings.SendNotifications then
		StarterGui:SetCore("SendNotification", {
			Title = TitleArg,
			Text = DescriptionArg,
			Duration = DurationArg
		})
	end
end

--// Functions

local function SaveSettings()
	if Environment.Settings.SaveSettings then
		if isfile(Title.."/"..FileNames[1].."/"..FileNames[2]) then
			writefile(Title.."/"..FileNames[1].."/"..FileNames[2], Encode(Environment.Settings))
		end

		if isfile(Title.."/"..FileNames[1].."/"..FileNames[3]) then
			writefile(Title.."/"..FileNames[1].."/"..FileNames[3], Encode(Environment.FOVSettings))
		end
	end
end

local function GetClosestPlayer()
	if not Environment.Locked then
		if Environment.FOVSettings.Enabled then
			RequiredDistance = Environment.FOVSettings.Amount
		else
			RequiredDistance = 2000
		end

		for _, v in next, Players:GetPlayers() do
			if v ~= LocalPlayer then
				if v.Character and v.Character:FindFirstChild(Environment.Settings.LockPart) and v.Character:FindFirstChildOfClass("Humanoid") then
					if Environment.Settings.TeamCheck and v.Team == LocalPlayer.Team then continue end
					if Environment.Settings.AliveCheck and v.Character:FindFirstChildOfClass("Humanoid").Health <= 0 then continue end
					if Environment.Settings.WallCheck and #(Camera:GetPartsObscuringTarget({v.Character[Environment.Settings.LockPart].Position}, v.Character:GetDescendants())) > 0 then continue end

					local Vector, OnScreen = Camera:WorldToViewportPoint(v.Character[Environment.Settings.LockPart].Position)
					local Distance = (Vector2.new(UserInputService:GetMouseLocation().X, UserInputService:GetMouseLocation().Y) - Vector2.new(Vector.X, Vector.Y)).Magnitude

					if Distance < RequiredDistance and OnScreen then
						RequiredDistance = Distance
						Environment.Locked = v
					end
				end
			end
		end
	elseif (Vector2.new(UserInputService:GetMouseLocation().X, UserInputService:GetMouseLocation().Y) - Vector2.new(Camera:WorldToViewportPoint(Environment.Locked.Character[Environment.Settings.LockPart].Position).X, Camera:WorldToViewportPoint(Environment.Locked.Character[Environment.Settings.LockPart].Position).Y)).Magnitude > RequiredDistance then
		Environment.Locked = nil
		Animation:Cancel()
		Environment.FOVCircle.Color = GetColor(Environment.FOVSettings.Color)
	end
end

--// Typing Check

ServiceConnections.TypingStartedConnection = UserInputService.TextBoxFocused:Connect(function()
	Typing = true
end)

ServiceConnections.TypingEndedConnection = UserInputService.TextBoxFocusReleased:Connect(function()
	Typing = false
end)

--// Create, Save & Load Settings

if Environment.Settings.SaveSettings then
	if not isfolder(Title) then
		makefolder(Title)
	end

	if not isfolder(Title.."/"..FileNames[1]) then
		makefolder(Title.."/"..FileNames[1])
	end

	if not isfile(Title.."/"..FileNames[1].."/"..FileNames[2]) then
		writefile(Title.."/"..FileNames[1].."/"..FileNames[2], Encode(Environment.Settings))
	else
		Environment.Settings = Decode(readfile(Title.."/"..FileNames[1].."/"..FileNames[2]))
	end

	if not isfile(Title.."/"..FileNames[1].."/"..FileNames[3]) then
		writefile(Title.."/"..FileNames[1].."/"..FileNames[3], Encode(Environment.FOVSettings))
	else
		Environment.Visuals = Decode(readfile(Title.."/"..FileNames[1].."/"..FileNames[3]))
	end

	coroutine.wrap(function()
		while wait(10) and Environment.Settings.SaveSettings do
			SaveSettings()
		end
	end)()
else
	if isfolder(Title) then
		delfolder(Title)
	end
end

local function Load()
	ServiceConnections.RenderSteppedConnection = RunService.RenderStepped:Connect(function()
		if Environment.FOVSettings.Enabled and Environment.Settings.Enabled then
			Environment.FOVCircle.Radius = Environment.FOVSettings.Amount
			Environment.FOVCircle.Thickness = Environment.FOVSettings.Thickness
			Environment.FOVCircle.Filled = Environment.FOVSettings.Filled
			Environment.FOVCircle.NumSides = Environment.FOVSettings.Sides
			Environment.FOVCircle.Color = GetColor(Environment.FOVSettings.Color)
			Environment.FOVCircle.Transparency = Environment.FOVSettings.Transparency
			Environment.FOVCircle.Visible = Environment.FOVSettings.Visible
			Environment.FOVCircle.Position = Vector2.new(UserInputService:GetMouseLocation().X, UserInputService:GetMouseLocation().Y)
		else
			Environment.FOVCircle.Visible = false
		end

		if Running and Environment.Settings.Enabled then
			GetClosestPlayer()

			if Environment.Settings.ThirdPerson then
				Environment.Settings.ThirdPersonSensitivity = math.clamp(Environment.Settings.ThirdPersonSensitivity, 0.1, 5)

				local Vector = Camera:WorldToViewportPoint(Environment.Locked.Character[Environment.Settings.LockPart].Position)
				mousemoverel((Vector.X - UserInputService:GetMouseLocation().X) * Environment.Settings.ThirdPersonSensitivity, (Vector.Y - UserInputService:GetMouseLocation().Y) * Environment.Settings.ThirdPersonSensitivity)
			else
				if Environment.Settings.Sensitivity > 0 then
					Animation = TweenService:Create(Camera, TweenInfo.new(Environment.Settings.Sensitivity, Enum.EasingStyle.Sine, Enum.EasingDirection.Out), {CFrame = CFrame.new(Camera.CFrame.Position, Environment.Locked.Character[Environment.Settings.LockPart].Position)})
					Animation:Play()
				else
					Camera.CFrame = CFrame.new(Camera.CFrame.Position, Environment.Locked.Character[Environment.Settings.LockPart].Position)
				end
			end

			Environment.FOVCircle.Color = GetColor(Environment.FOVSettings.LockedColor)
		end
	end)

	ServiceConnections.InputBeganConnection = UserInputService.InputBegan:Connect(function(Input)
		if not Typing then
			pcall(function()
				if Input.KeyCode == Enum.KeyCode[Environment.Settings.TriggerKey] then
					if Environment.Settings.Toggle then
						Running = not Running

						if not Running then
							Environment.Locked = nil
							Animation:Cancel()
							Environment.FOVCircle.Color = GetColor(Environment.FOVSettings.Color)
						end
					else
						Running = true
					end
				end
			end)

			pcall(function()
				if Input.UserInputType == Enum.UserInputType[Environment.Settings.TriggerKey] then
					if Environment.Settings.Toggle then
						Running = not Running

						if not Running then
							Environment.Locked = nil
							Animation:Cancel()
							Environment.FOVCircle.Color = GetColor(Environment.FOVSettings.Color)
						end
					else
						Running = true
					end
				end
			end)
		end
	end)

	ServiceConnections.InputEndedConnection = UserInputService.InputEnded:Connect(function(Input)
		if not Typing then
			pcall(function()
				if Input.KeyCode == Enum.KeyCode[Environment.Settings.TriggerKey] then
					if not Environment.Settings.Toggle then
						Running = false
						Environment.Locked = nil
						Animation:Cancel()
						Environment.FOVCircle.Color = GetColor(Environment.FOVSettings.Color)
					end
				end
			end)

			pcall(function()
				if Input.UserInputType == Enum.UserInputType[Environment.Settings.TriggerKey] then
					if not Environment.Settings.Toggle then
						Running = false
						Environment.Locked = nil
						Animation:Cancel()
						Environment.FOVCircle.Color = GetColor(Environment.FOVSettings.Color)
					end
				end
			end)
		end
	end)
end

--// Functions

Environment.Functions = {}

function Environment.Functions:Exit()
	SaveSettings()

	for _, v in next, ServiceConnections do
		v:Disconnect()
	end

	if Environment.FOVCircle.Remove then Environment.FOVCircle:Remove() end

	getgenv().Aimbot.Functions = nil
	getgenv().Aimbot = nil
end

function Environment.Functions:Restart()
	SaveSettings()

	for _, v in next, ServiceConnections do
		v:Disconnect()
	end

	Load()
end

function Environment.Functions:ResetSettings()
	Environment.Settings = {
		SendNotifications = true,
		SaveSettings = true, -- Re-execute upon changing
		ReloadOnTeleport = true,
		Enabled = true,
		TeamCheck = false,
		AliveCheck = true,
		WallCheck = false,
		Sensitivity = 0, -- Animation length (in seconds) before fully locking onto target
		ThirdPerson = false,
		ThirdPersonSensitivity = 3,
		TriggerKey = "MouseButton2",
		Toggle = false,
		LockPart = "Head" -- Body part to lock on
	}

	Environment.FOVSettings = {
		Enabled = true,
		Visible = true,
		Amount = 90,
		Color = "255, 255, 255",
		LockedColor = "255, 70, 70",
		Transparency = 0.5,
		Sides = 60,
		Thickness = 1,
		Filled = false
	}
end

--// Support Check

if not Drawing or not getgenv then
	SendNotification(Title, "Your exploit does not support this script", 3); return
end

--// Reload On Teleport

if Environment.Settings.ReloadOnTeleport then
	if queueonteleport then
		queueonteleport(game:HttpGet("https://raw.githubusercontent.com/Exunys/Aimbot-V2/main/Resources/Scripts/Main.lua"))
	else
		SendNotification(Title, "Your exploit does not support \"syn.queue_on_teleport()\"")
	end
end

--// Load

Load(); SendNotification(Title, "Aimbot script successfully loaded! Check the GitHub page on how to configure the script.", 5)


text:
--// Cache

local select = select
local pcall, getgenv, next, Vector2, mathclamp, type, mousemoverel = select(1, pcall, getgenv, next, Vector2.new, math.clamp, type, mousemoverel or (Input and Input.MouseMove))

--// Preventing Multiple Processes

pcall(function()
	getgenv().Aimbot.Functions:Exit()
end)

--// Environment

getgenv().Aimbot = {}
local Environment = getgenv().Aimbot

--// Services

local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")
local TweenService = game:GetService("TweenService")
local Players = game:GetService("Players")
local Camera = workspace.CurrentCamera
local LocalPlayer = Players.LocalPlayer

--// Variables

local RequiredDistance, Typing, Running, Animation, ServiceConnections = 2000, false, false, nil, {}

--// Script Settings

Environment.Settings = {
	Enabled = true,
	TeamCheck = false,
	AliveCheck = true,
	WallCheck = false, -- Laggy
	Sensitivity = 0, -- Animation length (in seconds) before fully locking onto target
	ThirdPerson = false, -- Uses mousemoverel instead of CFrame to support locking in third person (could be choppy)
	ThirdPersonSensitivity = 3, -- Boundary: 0.1 - 5
	TriggerKey = "MouseButton2",
	Toggle = false,
	LockPart = "Head" -- Body part to lock on
}

Environment.FOVSettings = {
	Enabled = true,
	Visible = true,
	Amount = 90,
	Color = Color3.fromRGB(255, 255, 255),
	LockedColor = Color3.fromRGB(255, 70, 70),
	Transparency = 0.5,
	Sides = 60,
	Thickness = 1,
	Filled = false
}

Environment.FOVCircle = Drawing.new("Circle")

--// Functions

local function CancelLock()
	Environment.Locked = nil
	if Animation then Animation:Cancel() end
	Environment.FOVCircle.Color = Environment.FOVSettings.Color
end

local function GetClosestPlayer()
	if not Environment.Locked then
		RequiredDistance = (Environment.FOVSettings.Enabled and Environment.FOVSettings.Amount or 2000)

		for _, v in next, Players:GetPlayers() do
			if v ~= LocalPlayer then
				if v.Character and v.Character:FindFirstChild(Environment.Settings.LockPart) and v.Character:FindFirstChildOfClass("Humanoid") then
					if Environment.Settings.TeamCheck and v.Team == LocalPlayer.Team then continue end
					if Environment.Settings.AliveCheck and v.Character:FindFirstChildOfClass("Humanoid").Health <= 0 then continue end
					if Environment.Settings.WallCheck and #(Camera:GetPartsObscuringTarget({v.Character[Environment.Settings.LockPart].Position}, v.Character:GetDescendants())) > 0 then continue end

					local Vector, OnScreen = Camera:WorldToViewportPoint(v.Character[Environment.Settings.LockPart].Position)
					local Distance = (Vector2(UserInputService:GetMouseLocation().X, UserInputService:GetMouseLocation().Y) - Vector2(Vector.X, Vector.Y)).Magnitude

					if Distance < RequiredDistance and OnScreen then
						RequiredDistance = Distance
						Environment.Locked = v
					end
				end
			end
		end
	elseif (Vector2(UserInputService:GetMouseLocation().X, UserInputService:GetMouseLocation().Y) - Vector2(Camera:WorldToViewportPoint(Environment.Locked.Character[Environment.Settings.LockPart].Position).X, Camera:WorldToViewportPoint(Environment.Locked.Character[Environment.Settings.LockPart].Position).Y)).Magnitude > RequiredDistance then
		CancelLock()
	end
end

--// Typing Check

ServiceConnections.TypingStartedConnection = UserInputService.TextBoxFocused:Connect(function()
	Typing = true
end)

ServiceConnections.TypingEndedConnection = UserInputService.TextBoxFocusReleased:Connect(function()
	Typing = false
end)

--// Main

local function Load()
	ServiceConnections.RenderSteppedConnection = RunService.RenderStepped:Connect(function()
		if Environment.FOVSettings.Enabled and Environment.Settings.Enabled then
			Environment.FOVCircle.Radius = Environment.FOVSettings.Amount
			Environment.FOVCircle.Thickness = Environment.FOVSettings.Thickness
			Environment.FOVCircle.Filled = Environment.FOVSettings.Filled
			Environment.FOVCircle.NumSides = Environment.FOVSettings.Sides
			Environment.FOVCircle.Color = Environment.FOVSettings.Color
			Environment.FOVCircle.Transparency = Environment.FOVSettings.Transparency
			Environment.FOVCircle.Visible = Environment.FOVSettings.Visible
			Environment.FOVCircle.Position = Vector2(UserInputService:GetMouseLocation().X, UserInputService:GetMouseLocation().Y)
		else
			Environment.FOVCircle.Visible = false
		end

		if Running and Environment.Settings.Enabled then
			GetClosestPlayer()

			if Environment.Locked then
				if Environment.Settings.ThirdPerson then
					Environment.Settings.ThirdPersonSensitivity = mathclamp(Environment.Settings.ThirdPersonSensitivity, 0.1, 5)

					local Vector = Camera:WorldToViewportPoint(Environment.Locked.Character[Environment.Settings.LockPart].Position)
					mousemoverel((Vector.X - UserInputService:GetMouseLocation().X) * Environment.Settings.ThirdPersonSensitivity, (Vector.Y - UserInputService:GetMouseLocation().Y) * Environment.Settings.ThirdPersonSensitivity)
				else
					if Environment.Settings.Sensitivity > 0 then
						Animation = TweenService:Create(Camera, TweenInfo.new(Environment.Settings.Sensitivity, Enum.EasingStyle.Sine, Enum.EasingDirection.Out), {CFrame = CFrame.new(Camera.CFrame.Position, Environment.Locked.Character[Environment.Settings.LockPart].Position)})
						Animation:Play()
					else
						Camera.CFrame = CFrame.new(Camera.CFrame.Position, Environment.Locked.Character[Environment.Settings.LockPart].Position)
					end
				end

			Environment.FOVCircle.Color = Environment.FOVSettings.LockedColor

			end
		end
	end)

	ServiceConnections.InputBeganConnection = UserInputService.InputBegan:Connect(function(Input)
		if not Typing then
			pcall(function()
				if Input.KeyCode == Enum.KeyCode[Environment.Settings.TriggerKey] then
					if Environment.Settings.Toggle then
						Running = not Running

						if not Running then
							CancelLock()
						end
					else
						Running = true
					end
				end
			end)

			pcall(function()
				if Input.UserInputType == Enum.UserInputType[Environment.Settings.TriggerKey] then
					if Environment.Settings.Toggle then
						Running = not Running

						if not Running then
							CancelLock()
						end
					else
						Running = true
					end
				end
			end)
		end
	end)

	ServiceConnections.InputEndedConnection = UserInputService.InputEnded:Connect(function(Input)
		if not Typing then
			if not Environment.Settings.Toggle then
				pcall(function()
					if Input.KeyCode == Enum.KeyCode[Environment.Settings.TriggerKey] then
						Running = false; CancelLock()
					end
				end)

				pcall(function()
					if Input.UserInputType == Enum.UserInputType[Environment.Settings.TriggerKey] then
						Running = false; CancelLock()
					end
				end)
			end
		end
	end)
end

--// Functions

Environment.Functions = {}

function Environment.Functions:Exit()
	for _, v in next, ServiceConnections do
		v:Disconnect()
	end

	if Environment.FOVCircle.Remove then Environment.FOVCircle:Remove() end

	getgenv().Aimbot.Functions = nil
	getgenv().Aimbot = nil

	Load = nil; GetClosestPlayer = nil; CancelLock = nil
end

function Environment.Functions:Restart()
	for _, v in next, ServiceConnections do
		v:Disconnect()
	end

	Load()
end

function Environment.Functions:ResetSettings()
	Environment.Settings = {
		Enabled = true,
		TeamCheck = false,
		AliveCheck = true,
		WallCheck = false,
		Sensitivity = 0, -- Animation length (in seconds) before fully locking onto target
		ThirdPerson = false, -- Uses mousemoverel instead of CFrame to support locking in third person (could be choppy)
		ThirdPersonSensitivity = 3, -- Boundary: 0.1 - 5
		TriggerKey = "MouseButton2",
		Toggle = false,
		LockPart = "Head" -- Body part to lock on
	}

	Environment.FOVSettings = {
		Enabled = true,
		Visible = true,
		Amount = 90,
		Color = Color3.fromRGB(255, 255, 255),
		LockedColor = Color3.fromRGB(255, 70, 70),
		Transparency = 0.5,
		Sides = 60,
		Thickness = 1,
		Filled = false
	}
end

--// Load

Load()


text:
--// Cache

local loadstring, game, getgenv, setclipboard = loadstring, game, getgenv, setclipboard

--// Loaded check

if getgenv().Aimbot then return end

--// Load Aimbot V2 (Raw)

loadstring(game:HttpGet("https://raw.githubusercontent.com/Exunys/Aimbot-V2/main/Resources/Scripts/Raw%20Main.lua"))()

--// Variables

local Aimbot = getgenv().Aimbot
local Settings, FOVSettings, Functions = Aimbot.Settings, Aimbot.FOVSettings, Aimbot.Functions

local Library = loadstring(game:GetObjects("rbxassetid://7657867786")[1].Source)() -- Pepsi's UI Library

local Parts = {"Head", "HumanoidRootPart", "Torso", "Left Arm", "Right Arm", "Left Leg", "Right Leg", "LeftHand", "RightHand", "LeftLowerArm", "RightLowerArm", "LeftUpperArm", "RightUpperArm", "LeftFoot", "LeftLowerLeg", "UpperTorso", "LeftUpperLeg", "RightFoot", "RightLowerLeg", "LowerTorso", "RightUpperLeg"}

--// Frame

Library.UnloadCallback = Functions.Exit

local MainFrame = Library:CreateWindow({
	Name = "Aimbot V2",
	Themeable = {
		Image = "7059346386",
		Info = "Made by Exunys\nPowered by Pepsi's UI Library",
		Credit = false
	},
	Background = "",
	Theme = [[{"__Designer.Colors.section":"ADC7FF","__Designer.Colors.topGradient":"1B242F","__Designer.Settings.ShowHideKey":"Enum.KeyCode.RightShift","__Designer.Colors.otherElementText":"54637D","__Designer.Colors.hoveredOptionBottom":"38667D","__Designer.Background.ImageAssetID":"","__Designer.Colors.unhoveredOptionTop":"407495","__Designer.Colors.innerBorder":"2C4168","__Designer.Colors.unselectedOption":"4E6EA0","__Designer.Background.UseBackgroundImage":true,"__Designer.Files.WorkspaceFile":"Aimbot V2","__Designer.Colors.main":"23A0FF","__Designer.Colors.outerBorder":"162943","__Designer.Background.ImageColor":"FFFFFF","__Designer.Colors.tabText":"C9DFF1","__Designer.Colors.elementBorder":"111D26","__Designer.Colors.sectionBackground":"0E141C","__Designer.Colors.selectedOption":"558AC2","__Designer.Colors.background":"11182A","__Designer.Colors.bottomGradient":"202B42","__Designer.Background.ImageTransparency":95,"__Designer.Colors.hoveredOptionTop":"4885A0","__Designer.Colors.elementText":"7692B8","__Designer.Colors.unhoveredOptionBottom":"5471C4"}]]
})

--// Tabs

local SettingsTab = MainFrame:CreateTab({
	Name = "Settings"
})

local FOVSettingsTab = MainFrame:CreateTab({
	Name = "FOV Settings"
})

local FunctionsTab = MainFrame:CreateTab({
	Name = "Functions"
})

--// Settings - Sections

local Values = SettingsTab:CreateSection({
	Name = "Values"
})

local Checks = SettingsTab:CreateSection({
	Name = "Checks"
})

local ThirdPerson = SettingsTab:CreateSection({
	Name = "Third Person"
})

--// FOV Settings - Sections

local FOV_Values = FOVSettingsTab:CreateSection({
	Name = "Values"
})

local FOV_Appearance = FOVSettingsTab:CreateSection({
	Name = "Appearance"
})

--// Functions - Sections

local FunctionsSection = FunctionsTab:CreateSection({
	Name = "Functions"
})

--// Settings / Values

Values:AddToggle({
	Name = "Enabled",
	Value = Settings.Enabled,
	Callback = function(New, Old)
		Settings.Enabled = New
	end
}).Default = Settings.Enabled

Values:AddToggle({
	Name = "Toggle",
	Value = Settings.Toggle,
	Callback = function(New, Old)
		Settings.Toggle = New
	end
}).Default = Settings.Toggle

Settings.LockPart = Parts[1]; Values:AddDropdown({
	Name = "Lock Part",
	Value = Parts[1],
	Callback = function(New, Old)
		Settings.LockPart = New
	end,
	List = Parts,
	Nothing = "Head"
}).Default = Parts[1]

Values:AddTextbox({ -- Using a Textbox instead of a Keybind because the UI Library doesn't support Mouse inputs like Left Click / Right Click...
	Name = "Hotkey",
	Value = Settings.TriggerKey,
	Callback = function(New, Old)
		Settings.TriggerKey = New
	end
}).Default = Settings.TriggerKey

--[[
Values:AddKeybind({
	Name = "Hotkey",
	Value = Settings.TriggerKey,
	Callback = function(New, Old)
		Settings.TriggerKey = stringmatch(tostring(New), "Enum%.[UserInputType]*[KeyCode]*%.(.+)")
	end,
}).Default = Settings.TriggerKey
]]

Values:AddSlider({
	Name = "Sensitivity",
	Value = Settings.Sensitivity,
	Callback = function(New, Old)
		Settings.Sensitivity = New
	end,
	Min = 0,
	Max = 1,
	Decimals = 2
}).Default = Settings.Sensitivity

--// Settings / Checks

Checks:AddToggle({
	Name = "Team Check",
	Value = Settings.TeamCheck,
	Callback = function(New, Old)
		Settings.TeamCheck = New
	end
}).Default = Settings.TeamCheck

Checks:AddToggle({
	Name = "Wall Check",
	Value = Settings.WallCheck,
	Callback = function(New, Old)
		Settings.WallCheck = New
	end
}).Default = Settings.WallCheck

Checks:AddToggle({
	Name = "Alive Check",
	Value = Settings.AliveCheck,
	Callback = function(New, Old)
		Settings.AliveCheck = New
	end
}).Default = Settings.AliveCheck

--// Settings / ThirdPerson

ThirdPerson:AddToggle({
	Name = "Enable Third Person",
	Value = Settings.ThirdPerson,
	Callback = function(New, Old)
		Settings.ThirdPerson = New
	end
}).Default = Settings.ThirdPerson

ThirdPerson:AddSlider({
	Name = "Sensitivity",
	Value = Settings.ThirdPersonSensitivity,
	Callback = function(New, Old)
		Settings.ThirdPersonSensitivity = New
	end,
	Min = 0.1,
	Max = 5,
	Decimals = 1
}).Default = Settings.ThirdPersonSensitivity

--// FOV Settings / Values

FOV_Values:AddToggle({
	Name = "Enabled",
	Value = FOVSettings.Enabled,
	Callback = function(New, Old)
		FOVSettings.Enabled = New
	end
}).Default = FOVSettings.Enabled

FOV_Values:AddToggle({
	Name = "Visible",
	Value = FOVSettings.Visible,
	Callback = function(New, Old)
		FOVSettings.Visible = New
	end
}).Default = FOVSettings.Visible

FOV_Values:AddSlider({
	Name = "Amount",
	Value = FOVSettings.Amount,
	Callback = function(New, Old)
		FOVSettings.Amount = New
	end,
	Min = 10,
	Max = 300
}).Default = FOVSettings.Amount

--// FOV Settings / Appearance

FOV_Appearance:AddToggle({
	Name = "Filled",
	Value = FOVSettings.Filled,
	Callback = function(New, Old)
		FOVSettings.Filled = New
	end
}).Default = FOVSettings.Filled

FOV_Appearance:AddSlider({
	Name = "Transparency",
	Value = FOVSettings.Transparency,
	Callback = function(New, Old)
		FOVSettings.Transparency = New
	end,
	Min = 0,
	Max = 1,
	Decimal = 1
}).Default = FOVSettings.Transparency

FOV_Appearance:AddSlider({
	Name = "Sides",
	Value = FOVSettings.Sides,
	Callback = function(New, Old)
		FOVSettings.Sides = New
	end,
	Min = 3,
	Max = 60
}).Default = FOVSettings.Sides

FOV_Appearance:AddSlider({
	Name = "Thickness",
	Value = FOVSettings.Thickness,
	Callback = function(New, Old)
		FOVSettings.Thickness = New
	end,
	Min = 1,
	Max = 50
}).Default = FOVSettings.Thickness

FOV_Appearance:AddColorpicker({
	Name = "Color",
	Value = FOVSettings.Color,
	Callback = function(New, Old)
		FOVSettings.Color = New
	end
}).Default = FOVSettings.Color

FOV_Appearance:AddColorpicker({
	Name = "Locked Color",
	Value = FOVSettings.LockedColor,
	Callback = function(New, Old)
		FOVSettings.LockedColor = New
	end
}).Default = FOVSettings.LockedColor

--// Functions / Functions

FunctionsSection:AddButton({
	Name = "Reset Settings",
	Callback = function()
		Functions.ResetSettings()
		Library.ResetAll()
	end
})

FunctionsSection:AddButton({
	Name = "Restart",
	Callback = Functions.Restart
})

FunctionsSection:AddButton({
	Name = "Exit",
	Callback = function()
		Functions:Exit()
		Library.Unload()
	end
})

FunctionsSection:AddButton({
	Name = "Copy Script Page",
	Callback = function()
		setclipboard("https://github.com/Exunys/Aimbot-V2")
	end
})


text:
getgenv().Aimbot.Settings = {
	SendNotifications = true,
	SaveSettings = true, -- Re-execute upon changing
	ReloadOnTeleport = true,
	Enabled = true,
	TeamCheck = false,
	AliveCheck = true,
	WallCheck = false, -- Laggy
	Sensitivity = 0, -- Animation length (in seconds) before fully locking onto target
	ThirdPerson = false, -- Uses mousemoverel instead of CFrame to support locking in third person (could be choppy)
	ThirdPersonSensitivity = 3, -- Boundary: 0.1 - 5
	TriggerKey = "MouseButton2",
	Toggle = false,
	LockPart = "Head" -- Body part to lock on
}

getgenv().Aimbot.FOVSettings = {
	Enabled = true,
	Visible = true,
	Amount = 90,
	Color = "255, 255, 255",
	LockedColor = "255, 70, 70",
	Transparency = 0.5,
	Sides = 60,
	Thickness = 1,
	Filled = false
}


text:
--// Variables

local Title = "Exunys Developer"
local StarterGui = game:GetService("StarterGui")

--// Functions

local function SendNotification(TitleArg, DescriptionArg, DurationArg)
    StarterGui:SetCore("SendNotification", {
        Title = TitleArg,
        Text = DescriptionArg,
        Duration = DurationArg
    })
end

--// Main

if not writefile then SendNotification(Title, "Your exploit does not support writefile()", 3) return end
if not isfile then SendNotification(Title, "Your exploit does not support isfile()", 3) return end
if not delfile then SendNotification(Title, "Your exploit does not support delfile()", 3) return end
if not makefolder then SendNotification(Title, "Your exploit does not support makefolder()", 3) return end
if not isfolder then SendNotification(Title, "Your exploit does not support isfolder()", 3) return end
if not delfolder then SendNotification(Title, "Your exploit does not support delfolder()", 3) return end
if not syn then SendNotification(Title, "Your exploit does not support the library syn", 3) return end
if not syn.queue_on_teleport then SendNotification(Title, "Your exploit does not support syn.queue_on_teleport()", 3) return end
if not Drawing then SendNotification(Title, "Your exploit does not support the library Drawing", 3) return end
if not getgenv then SendNotification(Title, "Your exploit does not support getgenv()", 3) return end

SendNotification(Title, "Your exploit supports this script.", 3)


text:
pcall(function()
	if isfolder("Exunys Developer/Aimbot") then
		delfolder("Exunys Developer/Aimbot")
	end
end)


text:
#### 0.7.2 - June 9, 2018
* multi-target net45, netstandard2.0
* support SourceLink
* FSharp.Core 4.5

#### 0.7.1 - December 29, 2017
* multi-target release: net45, net47, netstandard2.0, netcoreapp2.0

#### 0.7.0 - May 16, 2017
* initial release as FsRegEx
* breaking change, derived from FSharpVerbalExpressons, which is now deprecated

repo_name: jackfoxy/FsRegEx

repo_language: F#


text:
source https://nuget.org/api/v2

nuget FSharp.Core

nuget Expecto
nuget Expecto.BenchmarkDotNet
nuget Expecto.FsCheck
nuget Expecto.VisualStudio.TestAdapter version_in_path: true

clitool dotnet-fake

group Formatting
source https://nuget.org/api/v2
source https://ci.appveyor.com/nuget/fsharp-formatting

nuget FSharp.Formatting prerelease
nuget FSharp.Formatting.CommandTool prerelease

group FakeBuild
source https://api.nuget.org/v3/index.json

storage: none

nuget Fake.Core.Target
nuget Fake.IO.FileSystem
nuget Fake.DotNet.Cli
nuget Fake.Tools.Git
nuget Fake.DotNet.MSBuild
nuget Fake.Core.ReleaseNotes
nuget Fake.DotNet.AssemblyInfoFile
nuget Fake.DotNet.Paket
nuget Fake.DotNet.Testing.Expecto
nuget Fake.DotNet.FSFormatting


text:
init:
  - git config --global core.autocrlf input
build_script:
  - cmd: build.cmd All
test: off
version: 0.0.1.{build}
artifacts:
  - path: bin
    name: bin


text:
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27428.2037
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".paket", ".paket", "{63297B98-5CED-492C-A5B7-A5B4F73CF142}"
	ProjectSection(SolutionItems) = preProject
		paket.dependencies = paket.dependencies
		paket.lock = paket.lock
	EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{A6A6AF7D-D6E3-442D-9B1E-58CC91879BE1}"
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FsRegEx", "src\FsRegEx\FsRegEx.fsproj", "{A44BCE88-C621-4102-BD37-95C4843D50CE}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "project", "project", "{BF60BC93-E09B-4E5F-9D85-95A519479D54}"
	ProjectSection(SolutionItems) = preProject
		.github\ISSUE_TEMPLATE.md = .github\ISSUE_TEMPLATE.md
		LICENSE.txt = LICENSE.txt
		src\FsRegEx\paket.template = src\FsRegEx\paket.template
		README.md = README.md
		RELEASE_NOTES.md = RELEASE_NOTES.md
	EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{83F16175-43B1-4C90-A1EE-8E351C33435D}"
	ProjectSection(SolutionItems) = preProject
		docsrc\tools\templates\template.cshtml = docsrc\tools\templates\template.cshtml
	EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "content", "content", "{8E6D5255-776D-4B61-85F9-73C37AA1FB9A}"
	ProjectSection(SolutionItems) = preProject
		docsrc\content\capture.fsx = docsrc\content\capture.fsx
		docsrc\content\emailregex.fsx = docsrc\content\emailregex.fsx
		docsrc\content\group.fsx = docsrc\content\group.fsx
		docsrc\content\index.fsx = docsrc\content\index.fsx
		docsrc\content\matching.fsx = docsrc\content\matching.fsx
		docsrc\content\replacesplit.fsx = docsrc\content\replacesplit.fsx
		docsrc\content\tutorial.fsx = docsrc\content\tutorial.fsx
	EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{ED8079DD-2B06-4030-9F0F-DC548F98E1C4}"
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FsRegEx.Tests", "tests\FsRegEx.Tests\FsRegEx.Tests.fsproj", "{9D0671EB-3ADB-4866-997D-BA677D4347AA}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "git", "git", "{078A9C52-DDC1-46F4-9235-9E6C89C87AFD}"
	ProjectSection(SolutionItems) = preProject
		.gitattributes = .gitattributes
		.gitignore = .gitignore
	EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".build", ".build", "{7C6D08E7-3EAC-4335-8F4B-252C193C27C9}"
	ProjectSection(SolutionItems) = preProject
		.travis.yml = .travis.yml
		appveyor.yml = appveyor.yml
		build.cmd = build.cmd
		build.fsx = build.fsx
		build.proj = build.proj
		build.sh = build.sh
	EndProjectSection
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Email.Tests", "tests\Email.Tests\Email.Tests.fsproj", "{0D1BE493-6880-4EE4-96F7-F9D8665310F0}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{72A309C4-6DDB-478E-A7BA-78613372772E}"
EndProject
Global
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
		Debug|Any CPU = Debug|Any CPU
		Release|Any CPU = Release|Any CPU
	EndGlobalSection
	GlobalSection(ProjectConfigurationPlatforms) = postSolution
		{A44BCE88-C621-4102-BD37-95C4843D50CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{A44BCE88-C621-4102-BD37-95C4843D50CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{A44BCE88-C621-4102-BD37-95C4843D50CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{A44BCE88-C621-4102-BD37-95C4843D50CE}.Release|Any CPU.Build.0 = Release|Any CPU
		{9D0671EB-3ADB-4866-997D-BA677D4347AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{9D0671EB-3ADB-4866-997D-BA677D4347AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{9D0671EB-3ADB-4866-997D-BA677D4347AA}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{9D0671EB-3ADB-4866-997D-BA677D4347AA}.Release|Any CPU.Build.0 = Release|Any CPU
		{0D1BE493-6880-4EE4-96F7-F9D8665310F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{0D1BE493-6880-4EE4-96F7-F9D8665310F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{0D1BE493-6880-4EE4-96F7-F9D8665310F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{0D1BE493-6880-4EE4-96F7-F9D8665310F0}.Release|Any CPU.Build.0 = Release|Any CPU
	EndGlobalSection
	GlobalSection(SolutionProperties) = preSolution
		HideSolutionNode = FALSE
	EndGlobalSection
	GlobalSection(NestedProjects) = preSolution
		{83F16175-43B1-4C90-A1EE-8E351C33435D} = {A6A6AF7D-D6E3-442D-9B1E-58CC91879BE1}
		{8E6D5255-776D-4B61-85F9-73C37AA1FB9A} = {A6A6AF7D-D6E3-442D-9B1E-58CC91879BE1}
		{9D0671EB-3ADB-4866-997D-BA677D4347AA} = {ED8079DD-2B06-4030-9F0F-DC548F98E1C4}
		{0D1BE493-6880-4EE4-96F7-F9D8665310F0} = {ED8079DD-2B06-4030-9F0F-DC548F98E1C4}
	EndGlobalSection
	GlobalSection(ExtensibilityGlobals) = postSolution
		SolutionGuid = {66CFF8F9-0961-4245-9C27-746AEF03CC95}
	EndGlobalSection
EndGlobal


text:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <!-- Enable the restore command to run before builds -->
    <RestorePackages Condition=" '$(RestorePackages)' == '' ">true</RestorePackages>
    <!-- Download Paket.exe if it does not already exist -->
    <DownloadPaket Condition=" '$(DownloadPaket)' == '' ">true</DownloadPaket>
    <PaketToolsPath>$(MSBuildThisFileDirectory)</PaketToolsPath>
    <PaketRootPath>$(MSBuildThisFileDirectory)</PaketRootPath>
  </PropertyGroup>
  <PropertyGroup>
    <!-- Paket command -->
    <PaketExePath Condition=" '$(PaketExePath)' == '' ">$(PaketToolsPath)paket.exe</PaketExePath>
    <PaketBootStrapperExePath Condition=" '$(PaketBootStrapperExePath)' == '' ">$(PaketToolsPath)paket.bootstrapper.exe</PaketBootStrapperExePath>
    <PaketCommand Condition=" '$(OS)' == 'Windows_NT'">"$(PaketExePath)"</PaketCommand>
    <PaketCommand Condition=" '$(OS)' != 'Windows_NT' ">mono --runtime=v4.0.30319 "$(PaketExePath)"</PaketCommand>
    <PaketBootStrapperCommand Condition=" '$(OS)' == 'Windows_NT'">"$(PaketBootStrapperExePath)"</PaketBootStrapperCommand>
    <PaketBootStrapperCommand Condition=" '$(OS)' != 'Windows_NT' ">mono --runtime=v4.0.30319 $(PaketBootStrapperExePath)</PaketBootStrapperCommand>
    <!-- Commands -->
    <RestoreCommand>$(PaketCommand) restore</RestoreCommand>
    <DownloadPaketCommand>$(PaketBootStrapperCommand)</DownloadPaketCommand>
    <!-- We need to ensure packages are restored prior to assembly resolve -->
    <BuildDependsOn Condition="$(RestorePackages) == 'true'">RestorePackages; $(BuildDependsOn);</BuildDependsOn>
  </PropertyGroup>
  <Target Name="CheckPrerequisites">
    <!-- Raise an error if we're unable to locate paket.exe -->
    <Error Condition="'$(DownloadPaket)' != 'true' AND !Exists('$(PaketExePath)')" Text="Unable to locate '$(PaketExePath)'" />
    <MsBuild Targets="DownloadPaket" Projects="$(MSBuildThisFileFullPath)" Properties="Configuration=NOT_IMPORTANT;DownloadPaket=$(DownloadPaket)" />
  </Target>
  <Target Name="DownloadPaket">
    <Exec Command="$(DownloadPaketCommand)" Condition=" '$(DownloadPaket)' == 'true' AND !Exists('$(PaketExePath)')" />
  </Target>
  <Target Name="RestorePackages" DependsOnTargets="CheckPrerequisites">
    <Exec Command="$(RestoreCommand)" WorkingDirectory="$(PaketRootPath)" />
  </Target>
</Project>


text:
<?xml version="1.0" encoding="utf-8"?>
<Project>
  <Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>
  <Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
  <PropertyGroup>
    <RepoRootDir>$([System.IO.Path]::GetFullPath("$(MSBuildThisFileDirectory)"))</RepoRootDir>
    <BuildDependsOn>
    </BuildDependsOn>
    <CoreBuildDependsOn>
    </CoreBuildDependsOn>
  </PropertyGroup>
  <Target Name="Build">
    <Exec IgnoreStandardErrorWarningFormat="true" Command="dotnet fake build" WorkingDirectory="$(RepoRootDir)" />
  </Target>
  <Target Name="Pack">
  </Target>
  <Target Name="Test">
  </Target>
  <Target Name="VSTest" DependsOnTargets="Test" />
  <Import Project=".paket\Paket.Restore.targets" />
</Project>


text:
#!/usr/bin/env bash

set -eu

cd "$(dirname "$0")"

PAKET_BOOTSTRAPPER_EXE=/paket.bootstrapper.exe
PAKET_EXE=/paket.exe
FAKE_EXE=packages/build/FAKE/tools/FAKE.exe

FSIARGS=""
FSIARGS2=""
OS=${OS:-"unknown"}
if [ "$OS" != "Windows_NT" ]
then
  # Can't use FSIARGS="--fsiargs -d:MONO" in zsh, so split it up
  # (Can't use arrays since dash can't handle them)
  FSIARGS="--fsiargs"
  FSIARGS2="-d:MONO"
fi

run() {
  if [ "$OS" != "Windows_NT" ]
  then
    mono "$@"
  else
    "$@"
  fi
}

yesno() {
  # NOTE: Defaults to NO
  read -p "$1 [y/N] " ynresult
  case "$ynresult" in
    [yY]*) true ;;
    *) false ;;
  esac
}

set +e
run $PAKET_BOOTSTRAPPER_EXE
bootstrapper_exitcode=$?
set -e

if [ "$OS" != "Windows_NT" ] &&
       [ $bootstrapper_exitcode -ne 0 ] &&
       [ $(certmgr -list -c Trust | grep X.509 | wc -l) -le 1 ] &&
       [ $(certmgr -list -c -m Trust | grep X.509 | wc -l) -le 1 ]
then
  echo "Your Mono installation has no trusted SSL root certificates set up."
  echo "This may result in the Paket bootstrapper failing to download Paket"
  echo "because Github's SSL certificate can't be verified. One way to fix"
  echo "this issue would be to download the list of SSL root certificates"
  echo "from the Mozilla project by running the following command:"
  echo ""
  echo "    mozroots --import --sync"
  echo ""
  echo "This will import over 100 SSL root certificates into your Mono"
  echo "certificate repository."
  echo ""
  if yesno "Run 'mozroots --import --sync' now?"
  then
    mozroots --import --sync
  else
    echo "Attempting to continue without running mozroots. This might fail."
  fi
  # Re-run bootstrapper whether or not the user ran mozroots, because maybe
  # they fixed the problem in a separate terminal window.
  run $PAKET_BOOTSTRAPPER_EXE
fi

run $PAKET_EXE restore

[ ! -e build.fsx ] && run $PAKET_EXE update
[ ! -e build.fsx ] && run $FAKE_EXE init.fsx
run $FAKE_EXE "$@" $FSIARGS $FSIARGS2 build.fsx