# @botpoison/browser

## Install

### Script tag

Without a bundler, load version 1.1.0 from a CDN. Load it `async`, so it never holds up the page.

HTML

```html
<script
  src="https://unpkg.com/@botpoison/browser@1.1.0/dist/index.js"
  integrity="sha384-OV9jtDYCk1ORwcQiG2ooB9pcBIpHSRTng9CbA1vObEm7oaL+nRiFC45XXdQNg22i"
  crossorigin="anonymous"
  async
></script>
```

The tag names one version, and the `integrity` hash makes the browser refuse any other file. A new release, or a compromised CDN, cannot change the script on your page: it changes when you change the tag. To serve it from your own domain instead, download that file and point `src` at your copy, keeping the same hash.

### npm

Install from npm when you use a bundler. Your lockfile pins the version, so it changes only when you upgrade.

npm install @botpoison/browser

## Requirements

The library runs the check, a small proof of work, with `BigInt` and Web Crypto, so the page must be served over HTTPS, or from localhost while you develop. It has no dependencies, sets no cookies and stores nothing in the browser.

## Automatic mode and events

Once the library is loaded, it protects every form with a `data-botpoison-public-key` attribute, including forms added to the page later. When the visitor presses Send, it holds the submission, runs the check, adds the solution in a hidden `_botpoison` field, and sends the form.

HTML

```html
<!-- 1. Import the @botpoison/browser script, pinned to one version -->
<script
  src="https://unpkg.com/@botpoison/browser@1.1.0/dist/index.js"
  integrity="sha384-OV9jtDYCk1ORwcQiG2ooB9pcBIpHSRTng9CbA1vObEm7oaL+nRiFC45XXdQNg22i"
  crossorigin="anonymous"
  async
></script>

<!-- 2. Add your public key to the data-botpoison-public-key attribute -->
<form
  action="https://example.demo"
  data-botpoison-public-key="pk_xxxxxxxx"
>
  <input type="text" name="message" />
  <button type="submit">Send</button>
</form>
```

The form fires an event at each step. Listen for them to disable the submit button or show a loading state.

- `botpoison-challenge-start`
  The visitor pressed Send and solving began.
- `botpoison-challenge-success`
  The check is done. The form is sent right after.
- `botpoison-challenge-error`
  The check could not be completed. The form is not sent.

HTML

```html
<form
  id="form"
  action="https://example.demo"
  data-botpoison-public-key="pk_xxxxxxxx"
>
  <input type="text" name="message" />
  <button id="button" type="submit">Send</button>
</form>
<script>
  var formElement = document.getElementById("form");
  var buttonElement = document.getElementById("button");
  formElement.addEventListener("botpoison-challenge-start", function () {
    buttonElement.setAttribute("disabled", "disabled");
  });
  formElement.addEventListener("botpoison-challenge-success", function () {
    buttonElement.removeAttribute("disabled");
  });
  formElement.addEventListener("botpoison-challenge-error", function () {
    buttonElement.removeAttribute("disabled");
  });
</script>
```

If the script cannot reach Botpoison, the form fires `botpoison-challenge-error` and does not send. Use it to tell the visitor what happened. See [If Botpoison is unreachable](https://botpoison.com/documentation/challenge/rest-api/#unreachable) for the server side.

The library sends the form itself, without a second submit event, so a script that sends the form some other way never sees it. For a form you send with JavaScript, leave the attribute off and call `challenge()` instead.

## new Botpoison({ publicKey })

Creates an instance for a form you send with JavaScript, with the public key of your project.

JavaScript

```javascript
import Botpoison from "@botpoison/browser";

const botpoison = new Botpoison({
  publicKey: "pk_xxxxxxxx"
});
```

## challenge({ onProgress })

Runs the check and resolves to `{ solution }`. Send the solution with the form as `_botpoison`. It rejects when the check cannot be completed, such as when the network is down or Botpoison is out of reach: catch it and tell the visitor, rather than sending the form without a solution.

JavaScript

```javascript
import Botpoison from "@botpoison/browser";

const botpoison = new Botpoison({
  publicKey: "pk_xxxxxxxx"
});

const { solution } = await botpoison.challenge();
```

### Progress

`onProgress` is called with 0 when solving starts, with a growing fraction in steps while it runs, and with 1 when it is done. The values in between depend on how much work the check takes, so compare with a threshold or show a percentage rather than wait for an exact value such as 0.5.

JavaScript

```javascript
import Botpoison from "@botpoison/browser";

const botpoison = new Botpoison({
  publicKey: "pk_xxxxxxxx"
});

const button = document.querySelector("button[type=submit]");
const { solution } = await botpoison.challenge({
  // From 0 to 1, in steps while the check runs
  onProgress: (progress) => {
    button.textContent = `Checking ${Math.round(progress * 100)}%`;
  },
});
button.textContent = "Send";
```

## Server rendering

Importing the library sets up automatic mode once per page. Where there is no document, such as the server render in Next.js or Nuxt, the import does nothing, so it needs no setup of its own. `Botpoison.init()` runs the same step for integrations that call it, and a second call does nothing.
