Skip to content

Commit

Permalink
Add typescript support and compile to es5
Browse files Browse the repository at this point in the history
  • Loading branch information
timcubb committed Mar 18, 2021
1 parent 08f4f2c commit 0a3c2a5
Show file tree
Hide file tree
Showing 6 changed files with 220 additions and 50 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,16 @@
# web-helper-utils
Package containing methods to help coding experiments using Evolv

This branch is compiant with Evolv integrations as the `"main"` in `package.json` points to the compiled es5 output `dist/index.js`, which also exports a function which binds the helper methods to `window.evolv.helpers`.

# Getting Started

* `npm i`
* `npm run build`

Code is written in TypeScript inside of `/src/`.

To compile to es5, run `npm run build` to update the es5 output to `/dist`.

# Contributing
Be sure to update the `/dist` output before pushing changes up to the repo by running `npm run build`.
81 changes: 33 additions & 48 deletions index.js → dist/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"use strict";
/**
* Copyright (c) 2020
*
Expand All @@ -8,50 +9,49 @@
*
* Created at: 2020-12-01
*/

/**
* Run callback when document is ready and interactive
* @param {(void) => any} callback - function to run when document is ready.
* @param {() => any} callback - function to run when document is ready.
*/
function docReady(callback) {
// see if DOM is already available
if (document.readyState === "complete" || document.readyState === "interactive") {
// call on next available tick
setTimeout(callback, 1);
} else {
}
else {
document.addEventListener("DOMContentLoaded", callback);
}
}

/**
* Run code when document is ready and complete
* @param {(void) => any} callback - function to run when document has completed loading.
* @param {() => any} callback - function to run when document has completed loading.
*/
function docComplete(callback) {
// see if DOM is already available
if (document.readyState === "complete") {
// call on next available tick
setTimeout(callback, 1);
} else {
}
else {
document.addEventListener('readystatechange', function (event) {
if (event.target.readyState === 'complete') {
if (this.readyState === 'complete') {
callback();
}
});
}
}

/**
* Emits an Evolv 'selector-timeout' event.
* @param {Object} messageObj - object containing 'message' to print in console warning.
*/
function emitSelectorTimeout(messageObj) {
if (messageObj && messageObj.message) {
console.warn(messageObj.message);
function emitSelectorTimeout(_a) {
var message = _a.message;
if (message) {
console.warn(message);
}
window.evolv.client.emit('selector-timeout');
}

/**
* Wait for one or more selectors to be present on the page before running a callback.
* @param {Array<string | Array<string> | (void) => boolean>} selectors - Array of selector(s), function(s) and/or arrays of selectors.
Expand All @@ -63,60 +63,40 @@ function emitSelectorTimeout(messageObj) {
* @param {string} variant - string identifying the variant currently running. Ex: 'v1-2-1'
*/
function waitForExist(selectors, callback, timeout, clearIntervalOnTimeout, resolveCb, rejectCb, variant) {
// EXAMPLE USAGE from within an Evolv variant:
//
// waitForExist(['#header11', '#footer11', () => ({ true }) ['#sidebarDark', '#sidebarLight']],
// function() { console.log('render'); },
// 6000,
// false,
// resolve,
// reject);
//
// return true;

if (variant === void 0) { variant = ''; }
// used to store selectors as they are found
var found = [];

// flatten nested selectors
selectors = selectors.map(function (selector) {
return Array.isArray(selector) ? selector.join(',') : selector;
});

selectors = selectors.map(function (selector) { return Array.isArray(selector) ? selector.join(',') : selector; });
var existInterval = setInterval(function () {
// check each selector; if found - move to found[] until selectors[] is empty
selectors.forEach(function (selector) {
var check = typeof selector === "function" ? selector() : document.querySelector(selector);
var check = typeof selector === "function"
? selector()
: document.querySelector(selector);
if (check) {
if (found.find(function (sel) { return sel === selector; })) {
found.push(selector);
found.push(selector.toString());
}
selectors = selectors.filter(function (sel) {
return sel !== selector;
});
selectors = selectors.filter(function (sel) { return sel !== selector; });
}
});

// all selectors have been found
if (selectors.length === 0) {
// always clear interval once all selectors are found
clearInterval(existInterval);

try {
callback();
} catch (err) {
}
catch (err) {
window.evolv.client.contaminate({
details: err.message,
reason: "Variant #" + variant + " wasn't applied"
});

throw err;
}

// only set interval to null if callback() runs without error
existInterval = null;
}
}, 100);

function checkExist() {
setTimeout(function () {
if (existInterval) {
Expand All @@ -128,17 +108,22 @@ function waitForExist(selectors, callback, timeout, clearIntervalOnTimeout, reso
}
}, timeout);
}

// wait until document is complete before starting timer to check
// for selector existence.
docComplete(checkExist);
// resolve immediately
resolveCb();
}

module.exports = {
waitForExist: waitForExist,
emitSelectorTimeout: emitSelectorTimeout,
docComplete: docComplete,
docReady: docReady
module.exports = function () {
if (!window.evolv) {
window.evolv = {};
}
if (!window.evolv.helpers) {
window.evolv.helpers = {
docReady: docReady,
docComplete: docComplete,
emitSelectorTimeout: emitSelectorTimeout,
waitForExist: waitForExist
};
}
};
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@
"name": "@evolv-ai/web-helper-utils",
"version": "0.0.1",
"description": "Functions to help make writing Evolv experiments easier",
"main": "index.js",
"main": "dist/index.js",
"scripts": {
"build": "tsc",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Teo Lisitza",
"license": "Apache-2.0"
"license": "Apache-2.0",
"devDependencies": {
"typescript": "^4.2.3"
}
}
152 changes: 152 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/**
* Copyright (c) 2020
*
* Methods for use in Evolv experiments
*
* @summary Methods for use in Evolv experiments
* @author Teo Lisitza <[email protected]>
*
* Created at: 2020-12-01
*/

type selector = string | (() => boolean);

declare global {
interface Window {
evolv: any;
}
}

/**
* Run callback when document is ready and interactive
* @param {() => any} callback - function to run when document is ready.
*/
function docReady(callback: () => void) {
// see if DOM is already available
if (document.readyState === "complete" || document.readyState === "interactive") {
// call on next available tick
setTimeout(callback, 1);
} else {
document.addEventListener("DOMContentLoaded", callback);
}
}

/**
* Run code when document is ready and complete
* @param {() => any} callback - function to run when document has completed loading.
*/
function docComplete(callback: () => void) {
// see if DOM is already available
if (document.readyState === "complete") {
// call on next available tick
setTimeout(callback, 1);
} else {
document.addEventListener('readystatechange', function (event) {
if (this.readyState === 'complete') {
callback();
}
});
}
}

/**
* Emits an Evolv 'selector-timeout' event.
* @param {Object} messageObj - object containing 'message' to print in console warning.
*/
function emitSelectorTimeout({ message }: { message: string }) {
if (message) {
console.warn(message);
}
window.evolv.client.emit('selector-timeout');
}

/**
* Wait for one or more selectors to be present on the page before running a callback.
* @param {Array<string | Array<string> | (void) => boolean>} selectors - Array of selector(s), function(s) and/or arrays of selectors.
* @param {function} callback - function containing code to run when all selectors are found.
* @param {number} timeout - how many miliseconds spend looking for selectors before giving up.
* @param {boolean} clearIntervalOnTimeout - if true, clear the polling interval when timeout is reached.
* @param {Function} resolveCb - variant's resolve() method
* @param {Function} rejectCb - variant's reject() method
* @param {string} variant - string identifying the variant currently running. Ex: 'v1-2-1'
*/
function waitForExist(
selectors: selector[],
callback: () => void,
timeout: number,
clearIntervalOnTimeout: boolean,
resolveCb: () => void,
rejectCb: (message: { message: string }) => void,
variant: string = '') {

// used to store selectors as they are found
const found: string[] = [];

// flatten nested selectors
selectors = selectors.map(selector => Array.isArray(selector) ? selector.join(',') : selector);

const existInterval = setInterval(() => {
// check each selector; if found - move to found[] until selectors[] is empty
selectors.forEach(selector => {
const check = typeof selector === "function"
? selector()
: document.querySelector(selector);

if (check) {
if (found.find(sel => sel === selector)) {
found.push(selector.toString());
}
selectors = selectors.filter(sel => sel !== selector);
}
});

// all selectors have been found
if (selectors.length === 0) {
// always clear interval once all selectors are found
clearInterval(existInterval);

try {
callback();
} catch (err) {
window.evolv.client.contaminate({
details: err.message,
reason: "Variant #" + variant + " wasn't applied"
});

throw err;
}
}
}, 100);

function checkExist() {
setTimeout(() => {
if (existInterval) {
if (clearIntervalOnTimeout) {
clearInterval(existInterval);
}
// if we timeout before finding all selectors, contaminate with a message containing the selectors that were not found and the variant
rejectCb({ message: "Selectors not found or other error thrown: " + selectors.join('|') + "; Variant: " + variant });
}
}, timeout);
}

// wait until document is complete before starting timer to check
// for selector existence.
docComplete(checkExist);
// resolve immediately
resolveCb();
}

export = () => {
if (!window.evolv) {
window.evolv = {};
}
if (!window.evolv.helpers) {
window.evolv.helpers = {
docReady,
docComplete,
emitSelectorTimeout,
waitForExist
};
}
};
14 changes: 14 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"compilerOptions": {
"lib": ["ES2020", "dom"],
"module": "commonjs",
"moduleResolution": "node",
"outDir": "./dist/",
"rootDir": "./src/",
"target": "es5"
},
"exclude": [
"dist",
"node_modules"
]
}

0 comments on commit 0a3c2a5

Please sign in to comment.