# Tools

URL: https://docs.ablyft.com/developers/reference/javascript-api/tools/

> Helper functions available through ablyft.getTools().



The tools object is returned by `ablyft.getTools()` and `ablyft.get('tools')`. Inside experiment and variation JavaScript
it is available as `ablyftTools`.

```js
const tools = ablyft.getTools();
// In experiment / variation JavaScript:
ablyftTools.waitForElement('.hero', (el) => { /* ... */ });
```

| Group                                      | Functions                                                                                                     |
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------- |
| [Cookies](#cookies)                        | `setCookie`, `getCookie`, `removeCookie`                                                                      |
| [URLs](#urls)                              | `parseUrl`, `getRootDomain`, `getUrlParameter`, `buildRedirectUrl`, `redirect`, `getSetBucketingUrlExtension` |
| [Waiting & observing](#waiting--observing) | `poll`, `check`, `domLoaded`, `domChanged`, `waitForElement`, `elementIsInView`, `urlChanged`                 |
| [Data layer](#data-layer)                  | `checkForDataLayerEntry`, `watchDataLayerEntry`                                                               |
| [Loading resources](#loading-resources)    | `loadExternalCss`, `loadExternalJs`                                                                           |
| [Shopify](#shopify)                        | `shopifyPreviewTheme`, `shopifySwitchTemplate`                                                                |
| [Editor extensions](#editor-extensions)    | `applyEditorExtension`, `resetEditorExtension`                                                                |
| [Utilities](#utilities)                    | `hash`, `generateRandomId`, `pickProperties`, `getNestedValue`                                                |

## Cookies

### setCookie

`setCookie(name, value, days?, ignoreDomain?)`

Sets a cookie (`SameSite=Lax`, `Path=/`). `days` defaults to the project's maximum cookie lifetime. Unless `ignoreDomain` is `true`, the cookie is set on the root domain (or the custom cookie domain of the project).

```js
tools.setCookie('my_cookie', 'yes', 30); // 30 days, root domain
tools.setCookie('my_host_cookie', '1', 7, true); // current host only
```

### getCookie

`getCookie(name)` returns the value or `null`.

```js
const value = tools.getCookie('my_cookie');
```

### removeCookie

`removeCookie(name)` removes the cookie on the current host and on the root domain.

```js
tools.removeCookie('my_cookie');
```

## URLs

### parseUrl

`parseUrl(url?)` splits a URL (default: current page) into `protocol`, `host`, `hostname`, `port`, `pathname`, `search`, `hash`, `domain`, `tld` and `subdomain`.

```js
const url = tools.parseUrl('https://shop.example.co.uk/cart?x=1');
// url.subdomain === 'shop', url.domain === 'example', url.tld === 'co.uk'
```

### getRootDomain

`getRootDomain()` returns the cookie domain, e.g. `.example.com` (or the custom cookie domain of the project).

```js
const rootDomain = tools.getRootDomain();
```

### getUrlParameter

`getUrlParameter(name, url?)` returns the parameter value, an empty string if the parameter has no value, or `null` if it is missing.

```js
const source = tools.getUrlParameter('utm_source');
const other = tools.getUrlParameter('id', 'https://example.com/?id=42');
```

### buildRedirectUrl

`buildRedirectUrl(url, keepQueryParameters?)` returns the final redirect URL (default `keepQueryParameters = true`). With `true`, the current query parameters and hash are merged into the target (parameters of the target win). When the target is on another domain, the current variation assignment is appended as URL parameter.

```js
const target = tools.buildRedirectUrl('/landing-b/');
```

### redirect

`redirect(url, keepQueryParameters?, preventLoops?)`

Redirects the visitor (using `location.replace`) and hides the page meanwhile. Returns `false` if the redirect was skipped.

| Parameter             | Default | Description                                                                                                       |
| --------------------- | ------- | ----------------------------------------------------------------------------------------------------------------- |
| `keepQueryParameters` | `true`  | Merge current query parameters into the target                                                                    |
| `preventLoops`        | `5`     | Minimum seconds between two redirects in a session. An integer sets the seconds, `false` disables the protection. |

```js
tools.redirect('/landing-b/');
tools.redirect('https://example.com/new/', false); // do not keep query parameters
tools.redirect('/landing-b/', true, false); // no loop protection
```

### getSetBucketingUrlExtension

`getSetBucketingUrlExtension()` returns the visitor's variation assignment formatted for the bucketing URL parameter (e.g. `["12345678_98765432"]`), or `null` if there is none. Use it to hand over the assignment to another domain manually.

```js
const extension = tools.getSetBucketingUrlExtension();
```

## Waiting & observing

### poll

`poll(pollingFn, callbackFn, callbackFnOnFail?, timeoutMs?, timeoutAfterDomReadyMs?)`

Calls `pollingFn` every 10 ms until it returns exactly `true`, then calls `callbackFn`. If it does not succeed within `timeoutMs` (default `10000`) or `timeoutAfterDomReadyMs` (default `2000`) after the DOM is ready, `callbackFnOnFail` is called.

```js
tools.poll(
  () => window.myLibrary !== undefined,
  () => console.log('library ready'),
  () => console.log('library not found'),
);
```

### check

`check(checkFn, callbackFn, callbackFnOnFail?)` evaluates `checkFn` once. If it returns exactly `true`, `callbackFn` is called, otherwise `callbackFnOnFail`.

```js
tools.check(
  () => document.body.classList.contains('checkout'),
  () => console.log('on checkout'),
);
```

### domLoaded

`domLoaded(callbackFn)` calls the function once the DOM is interactive (immediately if it already is).

```js
tools.domLoaded(() => console.log('DOM ready'));
```

### domChanged

`domChanged(callbackFn)` calls the function on every DOM change (children, subtree, attributes). Changes made by ABlyft itself are ignored.

```js
tools.domChanged(() => console.log('The page changed'));
```

### waitForElement

`waitForElement(selector, fn, additionalData?)`

Calls `fn(element, additionalData)` for every element matching `selector` as soon as it exists in the DOM (once per element). `this` is the element as well.

```js
tools.waitForElement('.product-title', (el) => {
  el.textContent = 'New title';
});
```

### elementIsInView

`elementIsInView(selector, fn, persistentObserving?)`

Calls `fn(element)` when a matching element scrolls into the viewport. With `persistentObserving = true`, elements stay observed. Elements added later are picked up.

```js
tools.elementIsInView('.reviews', (el) => {
  console.log('reviews are visible', el);
});
```

### urlChanged

`urlChanged(mode, callbackFn)`

Calls the function when the URL changes. `mode` is `'polling'` (compares the URL every 10 ms) or `'history'` (hooks into `pushState`, `replaceState` and `popstate`).

```js
tools.urlChanged('polling', () => console.log('URL is now', location.href));
tools.urlChanged('history', () => console.log('history navigation'));
```

## Data layer

### checkForDataLayerEntry

`checkForDataLayerEntry(entrySpecs, callbackFn)`

Waits (up to one hour) until an entry in `window.dataLayer` matches all `[key, value]` pairs exactly, then calls `callbackFn`.

```js
tools.checkForDataLayerEntry(
  [['event', 'purchase'], ['currency', 'EUR']],
  () => console.log('purchase event found'),
);
```

### watchDataLayerEntry

`watchDataLayerEntry(eventName, callback)`

Calls `callback(entry)` for the latest existing data layer entry with `event === eventName` and for every future one. Returns a function that stops watching. The detection method (`intercept` or `polling`) is set in the project's snippet settings. Errors inside the callback are caught and logged.

```js
const stop = tools.watchDataLayerEntry('add_to_cart', (entry) => {
  console.log('Item added', entry);
});

// later, if no longer needed
stop();
```

## Loading resources

### loadExternalCss

`loadExternalCss(url)` appends a stylesheet link to `<head>`.

```js
tools.loadExternalCss('https://example.com/extra.css');
```

### loadExternalJs

`loadExternalJs(url, callbackFn)` appends a script to `<head>` and calls `callbackFn` when it has loaded.

```js
tools.loadExternalJs('https://example.com/lib.js', () => console.log('loaded'));
```

## Shopify

For use in Shopify theme and template variations, see [Shopify variations](https://docs.ablyft.com/guides/experiments/shopify-variations/).

### shopifyPreviewTheme

`shopifyPreviewTheme(themeId)` waits for the `Shopify` object and, if the current theme differs, redirects to the preview of the given theme.

```js
tools.shopifyPreviewTheme(123456789);
```

### shopifySwitchTemplate

`shopifySwitchTemplate(templateName)` redirects to `?view=<templateName>` if the page has no `view` parameter yet.

```js
tools.shopifySwitchTemplate('landing-b');
```

## Editor extensions

Used by variations created with the visual editor and its extensions. The extension must exist in the project.

### applyEditorExtension

`applyEditorExtension(variationId, selector, extensionId, extensionData, instance?)`

Renders an extension into the variation and returns the instance ID (generated if omitted). Unknown extensions are logged as error.

```js
const instance = tools.applyEditorExtension(12345678, 'body', '1', {
  headline: 'Inserted by an extension',
});
```

### resetEditorExtension

`resetEditorExtension(variationId, extensionId, instance)` removes the extension's CSS and runs its reset code.

```js
tools.resetEditorExtension(12345678, '1', instance);
```

## Utilities

### hash

`hash(string)` returns a short hexadecimal hash of a string.

```js
const id = tools.hash('user@example.com');
```

### generateRandomId

`generateRandomId()` returns a random 10-character alphanumeric ID.

```js
const id = tools.generateRandomId();
```

### pickProperties

`pickProperties(object, keys)` returns a new object with only the listed keys (that exist on the object).

```js
const small = tools.pickProperties({ a: 1, b: 2, c: 3 }, ['a', 'c']); // { a: 1, c: 3 }
```

### getNestedValue

`getNestedValue(object, path)` reads a nested value by dot path and returns `undefined` if any part is missing.

```js
const coupon = tools.getNestedValue({ ecommerce: { coupon: 'SUMMER' } }, 'ecommerce.coupon');
```

