2016-08-14 23:14:37 +02:00
|
|
|
/* eslint-disable */
|
2017-08-16 10:47:22 +02:00
|
|
|
|
2016-03-16 21:24:11 +01:00
|
|
|
/**
|
2016-08-14 23:14:37 +02:00
|
|
|
* Capitalises the first letter of each word in a string
|
2016-03-16 21:24:11 +01:00
|
|
|
* @param {String} str String to capitalise
|
|
|
|
* @return {String} Capitalised string
|
|
|
|
*/
|
2016-04-04 00:07:10 +02:00
|
|
|
export const capitalise = function(str) {
|
2017-08-16 10:47:22 +02:00
|
|
|
return str.replace(/\w\S*/g, txt => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase());
|
2016-03-16 21:24:11 +01:00
|
|
|
};
|
|
|
|
|
2017-06-02 22:34:07 +02:00
|
|
|
/**
|
|
|
|
* Generates a string of random chars
|
|
|
|
* @param {Number} length Length of the string to generate
|
|
|
|
* @return {String} String of random chars
|
|
|
|
*/
|
2017-08-16 10:47:22 +02:00
|
|
|
export const generateChars = function(length) {
|
|
|
|
let chars = '';
|
2017-06-02 22:34:07 +02:00
|
|
|
|
2017-08-16 10:47:22 +02:00
|
|
|
for (let i = 0; i < length; i++) {
|
|
|
|
const randomChar = getRandomNumber(0, 36);
|
2017-06-02 22:34:07 +02:00
|
|
|
chars += randomChar.toString(36);
|
|
|
|
}
|
|
|
|
|
|
|
|
return chars;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generates a unique id based on an element
|
|
|
|
* @param {HTMLElement} element Element to generate the id from
|
|
|
|
* @param {String} Prefix for the Id
|
|
|
|
* @return {String} Unique Id
|
|
|
|
*/
|
2017-08-16 10:47:22 +02:00
|
|
|
export const generateId = function(element, prefix) {
|
|
|
|
let id = element.id || (element.name && (`${element.name}-${generateChars(2)}`)) || generateChars(4);
|
2017-06-02 22:34:07 +02:00
|
|
|
id = id.replace(/(:|\.|\[|\]|,)/g, '');
|
|
|
|
id = prefix + id;
|
|
|
|
|
|
|
|
return id;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2017-02-17 10:23:52 +01:00
|
|
|
/**
|
|
|
|
* Tests the type of an object
|
|
|
|
* @param {String} type Type to test object against
|
|
|
|
* @param {Object} obj Object to be tested
|
|
|
|
* @return {Boolean}
|
|
|
|
*/
|
|
|
|
export const getType = function(obj) {
|
|
|
|
return Object.prototype.toString.call(obj).slice(8, -1);
|
|
|
|
};
|
|
|
|
|
2016-03-16 21:24:11 +01:00
|
|
|
/**
|
|
|
|
* Tests the type of an object
|
|
|
|
* @param {String} type Type to test object against
|
|
|
|
* @param {Object} obj Object to be tested
|
|
|
|
* @return {Boolean}
|
|
|
|
*/
|
2016-04-04 00:07:10 +02:00
|
|
|
export const isType = function(type, obj) {
|
2017-08-16 10:47:22 +02:00
|
|
|
const clas = getType(obj);
|
2016-09-05 23:04:15 +02:00
|
|
|
return obj !== undefined && obj !== null && clas === type;
|
2016-03-16 21:24:11 +01:00
|
|
|
};
|
|
|
|
|
2016-06-30 14:57:56 +02:00
|
|
|
/**
|
|
|
|
* Tests to see if a passed object is a node
|
|
|
|
* @param {Object} obj Object to be tested
|
|
|
|
* @return {Boolean}
|
|
|
|
*/
|
2017-08-16 10:47:22 +02:00
|
|
|
export const isNode = o => (
|
|
|
|
typeof Node === 'object' ? o instanceof Node :
|
|
|
|
o && typeof o === 'object' && typeof o.nodeType === 'number' && typeof o.nodeName === 'string'
|
2017-02-17 10:23:52 +01:00
|
|
|
);
|
2016-06-30 14:57:56 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Tests to see if a passed object is an element
|
|
|
|
* @param {Object} obj Object to be tested
|
|
|
|
* @return {Boolean}
|
|
|
|
*/
|
2017-08-16 10:47:22 +02:00
|
|
|
export const isElement = o => (
|
|
|
|
typeof HTMLElement === 'object' ? o instanceof HTMLElement : // DOM2
|
|
|
|
o && typeof o === 'object' && o !== null && o.nodeType === 1 && typeof o.nodeName === 'string'
|
2017-02-17 10:23:52 +01:00
|
|
|
);
|
2016-06-30 14:57:56 +02:00
|
|
|
|
2016-04-04 22:44:32 +02:00
|
|
|
/**
|
|
|
|
* Merges unspecified amount of objects into new object
|
|
|
|
* @private
|
|
|
|
* @return {Object} Merged object of arguments
|
|
|
|
*/
|
|
|
|
export const extend = function() {
|
2017-08-16 10:47:22 +02:00
|
|
|
const extended = {};
|
|
|
|
const length = arguments.length;
|
2016-09-05 23:04:15 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Merge one object into another
|
|
|
|
* @param {Object} obj Object to merge into extended object
|
|
|
|
*/
|
2017-08-16 10:47:22 +02:00
|
|
|
const merge = function(obj) {
|
|
|
|
for (const prop in obj) {
|
2016-09-05 23:04:15 +02:00
|
|
|
if (Object.prototype.hasOwnProperty.call(obj, prop)) {
|
|
|
|
// If deep merge and property is an object, merge properties
|
2017-08-16 10:47:22 +02:00
|
|
|
if (isType('Object', obj[prop])) {
|
2016-09-05 23:04:15 +02:00
|
|
|
extended[prop] = extend(true, extended[prop], obj[prop]);
|
|
|
|
} else {
|
|
|
|
extended[prop] = obj[prop];
|
2016-04-04 22:44:32 +02:00
|
|
|
}
|
2017-08-16 10:47:22 +02:00
|
|
|
}
|
2016-09-05 23:04:15 +02:00
|
|
|
}
|
|
|
|
};
|
2016-04-04 22:44:32 +02:00
|
|
|
|
2016-09-05 23:04:15 +02:00
|
|
|
// Loop through each passed argument
|
|
|
|
for (let i = 0; i < length; i++) {
|
|
|
|
// store argument at position i
|
2017-08-16 10:47:22 +02:00
|
|
|
const obj = arguments[i];
|
2016-04-04 22:44:32 +02:00
|
|
|
|
2016-09-15 22:04:15 +02:00
|
|
|
// If we are in fact dealing with an object, merge it.
|
2016-09-05 23:04:15 +02:00
|
|
|
if (isType('Object', obj)) {
|
|
|
|
merge(obj);
|
2016-04-04 22:44:32 +02:00
|
|
|
}
|
2016-09-05 23:04:15 +02:00
|
|
|
}
|
2016-04-04 22:44:32 +02:00
|
|
|
|
2016-09-05 23:04:15 +02:00
|
|
|
return extended;
|
2016-04-04 22:44:32 +02:00
|
|
|
};
|
2016-03-16 21:24:11 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* CSS transition end event listener
|
|
|
|
* @return
|
|
|
|
*/
|
2016-09-05 23:04:15 +02:00
|
|
|
export const whichTransitionEvent = function() {
|
2017-08-16 10:47:22 +02:00
|
|
|
let t,
|
|
|
|
el = document.createElement('fakeelement');
|
|
|
|
|
|
|
|
const transitions = {
|
|
|
|
transition: 'transitionend',
|
|
|
|
OTransition: 'oTransitionEnd',
|
|
|
|
MozTransition: 'transitionend',
|
|
|
|
WebkitTransition: 'webkitTransitionEnd',
|
|
|
|
};
|
2016-03-16 21:24:11 +01:00
|
|
|
|
2016-09-05 23:04:15 +02:00
|
|
|
for (t in transitions) {
|
|
|
|
if (el.style[t] !== undefined) {
|
|
|
|
return transitions[t];
|
2016-03-16 21:24:11 +01:00
|
|
|
}
|
2016-09-05 23:04:15 +02:00
|
|
|
}
|
2016-08-14 23:14:37 +02:00
|
|
|
};
|
2016-03-16 21:24:11 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* CSS animation end event listener
|
|
|
|
* @return
|
|
|
|
*/
|
2016-04-04 00:07:10 +02:00
|
|
|
export const whichAnimationEvent = function() {
|
2017-08-16 10:47:22 +02:00
|
|
|
let t,
|
|
|
|
el = document.createElement('fakeelement');
|
|
|
|
|
|
|
|
const animations = {
|
|
|
|
animation: 'animationend',
|
|
|
|
OAnimation: 'oAnimationEnd',
|
|
|
|
MozAnimation: 'animationend',
|
|
|
|
WebkitAnimation: 'webkitAnimationEnd',
|
2016-09-05 23:04:15 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
for (t in animations) {
|
|
|
|
if (el.style[t] !== undefined) {
|
|
|
|
return animations[t];
|
2016-03-16 21:24:11 +01:00
|
|
|
}
|
2016-09-05 23:04:15 +02:00
|
|
|
}
|
2016-03-16 21:24:11 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2016-08-14 23:14:37 +02:00
|
|
|
* Get the ancestors of each element in the current set of matched elements,
|
2016-03-16 21:24:11 +01:00
|
|
|
* up to but not including the element matched by the selector
|
|
|
|
* @param {NodeElement} elem Element to begin search from
|
|
|
|
* @param {NodeElement} parent Parent to find
|
|
|
|
* @param {String} selector Class to find
|
|
|
|
* @return {Array} Array of parent elements
|
|
|
|
*/
|
2016-04-04 00:07:10 +02:00
|
|
|
export const getParentsUntil = function(elem, parent, selector) {
|
2017-08-16 10:47:22 +02:00
|
|
|
const parents = [];
|
2016-09-05 23:04:15 +02:00
|
|
|
// Get matches
|
|
|
|
for (; elem && elem !== document; elem = elem.parentNode) {
|
|
|
|
// Check if parent has been reached
|
|
|
|
if (parent) {
|
2017-08-16 10:47:22 +02:00
|
|
|
const parentType = parent.charAt(0);
|
2016-03-16 21:24:11 +01:00
|
|
|
|
2016-09-05 23:04:15 +02:00
|
|
|
// If parent is a class
|
|
|
|
if (parentType === '.') {
|
|
|
|
if (elem.classList.contains(parent.substr(1))) {
|
|
|
|
break;
|
2016-03-16 21:24:11 +01:00
|
|
|
}
|
2016-09-05 23:04:15 +02:00
|
|
|
}
|
2016-03-16 21:24:11 +01:00
|
|
|
|
2016-09-05 23:04:15 +02:00
|
|
|
// If parent is an ID
|
|
|
|
if (parentType === '#') {
|
|
|
|
if (elem.id === parent.substr(1)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If parent is a data attribute
|
|
|
|
if (parentType === '[') {
|
|
|
|
if (elem.hasAttribute(parent.substr(1, parent.length - 1))) {
|
|
|
|
break;
|
2016-03-16 21:24:11 +01:00
|
|
|
}
|
2016-09-05 23:04:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// If parent is a tag
|
|
|
|
if (elem.tagName.toLowerCase() === parent) {
|
|
|
|
break;
|
|
|
|
}
|
2016-03-16 21:24:11 +01:00
|
|
|
}
|
2016-09-05 23:04:15 +02:00
|
|
|
if (selector) {
|
2017-08-16 10:47:22 +02:00
|
|
|
const selectorType = selector.charAt(0);
|
2016-09-05 23:04:15 +02:00
|
|
|
|
|
|
|
// If selector is a class
|
|
|
|
if (selectorType === '.') {
|
|
|
|
if (elem.classList.contains(selector.substr(1))) {
|
|
|
|
parents.push(elem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If selector is an ID
|
|
|
|
if (selectorType === '#') {
|
|
|
|
if (elem.id === selector.substr(1)) {
|
|
|
|
parents.push(elem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If selector is a data attribute
|
|
|
|
if (selectorType === '[') {
|
|
|
|
if (elem.hasAttribute(selector.substr(1, selector.length - 1))) {
|
|
|
|
parents.push(elem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If selector is a tag
|
|
|
|
if (elem.tagName.toLowerCase() === selector) {
|
|
|
|
parents.push(elem);
|
|
|
|
}
|
2016-03-16 21:24:11 +01:00
|
|
|
} else {
|
2016-09-05 23:04:15 +02:00
|
|
|
parents.push(elem);
|
2016-03-16 21:24:11 +01:00
|
|
|
}
|
2016-09-05 23:04:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Return parents if any exist
|
|
|
|
if (parents.length === 0) {
|
|
|
|
return null;
|
|
|
|
}
|
2017-08-16 10:47:22 +02:00
|
|
|
return parents;
|
2016-03-16 21:24:11 +01:00
|
|
|
};
|
|
|
|
|
2016-09-05 23:04:15 +02:00
|
|
|
export const wrap = function(element, wrapper) {
|
|
|
|
wrapper = wrapper || document.createElement('div');
|
|
|
|
if (element.nextSibling) {
|
|
|
|
element.parentNode.insertBefore(wrapper, element.nextSibling);
|
|
|
|
} else {
|
|
|
|
element.parentNode.appendChild(wrapper);
|
|
|
|
}
|
|
|
|
return wrapper.appendChild(element);
|
2016-03-16 21:24:11 +01:00
|
|
|
};
|
|
|
|
|
2016-09-05 23:04:15 +02:00
|
|
|
export const getSiblings = function(elem) {
|
2017-08-16 10:47:22 +02:00
|
|
|
const siblings = [];
|
|
|
|
let sibling = elem.parentNode.firstChild;
|
2016-09-05 23:04:15 +02:00
|
|
|
for (; sibling; sibling = sibling.nextSibling) {
|
|
|
|
if (sibling.nodeType === 1 && sibling !== elem) {
|
|
|
|
siblings.push(sibling);
|
2016-03-16 21:24:11 +01:00
|
|
|
}
|
2016-09-05 23:04:15 +02:00
|
|
|
}
|
|
|
|
return siblings;
|
2016-03-16 21:24:11 +01:00
|
|
|
};
|
|
|
|
|
2016-08-14 23:14:37 +02:00
|
|
|
/**
|
2016-03-16 21:24:11 +01:00
|
|
|
* Find ancestor in DOM tree
|
2016-08-14 23:14:37 +02:00
|
|
|
* @param {NodeElement} el Element to start search from
|
2016-03-16 21:24:11 +01:00
|
|
|
* @param {[type]} cls Class of parent
|
|
|
|
* @return {NodeElement} Found parent element
|
|
|
|
*/
|
2016-04-04 00:07:10 +02:00
|
|
|
export const findAncestor = function(el, cls) {
|
2016-09-05 23:04:15 +02:00
|
|
|
while ((el = el.parentElement) && !el.classList.contains(cls));
|
|
|
|
return el;
|
2016-03-16 21:24:11 +01:00
|
|
|
};
|
|
|
|
|
2017-03-12 14:17:46 +01:00
|
|
|
/**
|
|
|
|
* Find ancestor in DOM tree by attribute name
|
|
|
|
* @param {NodeElement} el Element to start search from
|
|
|
|
* @param {string} attr Attribute name of parent
|
|
|
|
* @return {?NodeElement} Found parent element or null
|
|
|
|
*/
|
2017-03-12 14:21:00 +01:00
|
|
|
export const findAncestorByAttrName = function(el, attr) {
|
2017-03-12 14:17:46 +01:00
|
|
|
let target = el;
|
|
|
|
|
|
|
|
while (target) {
|
|
|
|
if (target.hasAttribute(attr)) {
|
|
|
|
return target;
|
|
|
|
}
|
|
|
|
|
|
|
|
target = target.parentElement;
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
};
|
|
|
|
|
2016-03-16 21:24:11 +01:00
|
|
|
/**
|
|
|
|
* Debounce an event handler.
|
|
|
|
* @param {Function} func Function to run after wait
|
|
|
|
* @param {Number} wait The delay before the function is executed
|
2016-08-14 23:14:37 +02:00
|
|
|
* @param {Boolean} immediate If passed, trigger the function on the leading edge, instead of the trailing.
|
2016-03-16 21:24:11 +01:00
|
|
|
* @return {Function} A function will be called after it stops being called for a given delay
|
|
|
|
*/
|
2016-04-04 00:07:10 +02:00
|
|
|
export const debounce = function(func, wait, immediate) {
|
2017-08-16 10:47:22 +02:00
|
|
|
let timeout;
|
2016-09-05 23:04:15 +02:00
|
|
|
return function() {
|
2017-08-16 10:47:22 +02:00
|
|
|
let context = this,
|
|
|
|
args = arguments;
|
|
|
|
const later = function() {
|
2016-09-05 23:04:15 +02:00
|
|
|
timeout = null;
|
|
|
|
if (!immediate) func.apply(context, args);
|
2016-03-16 21:24:11 +01:00
|
|
|
};
|
2017-08-16 10:47:22 +02:00
|
|
|
const callNow = immediate && !timeout;
|
2016-09-05 23:04:15 +02:00
|
|
|
clearTimeout(timeout);
|
|
|
|
timeout = setTimeout(later, wait);
|
|
|
|
if (callNow) func.apply(context, args);
|
|
|
|
};
|
2016-03-16 21:24:11 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get an element's distance from the top of the page
|
|
|
|
* @private
|
|
|
|
* @param {NodeElement} el Element to test for
|
|
|
|
* @return {Number} Elements Distance from top of page
|
|
|
|
*/
|
2016-04-04 00:07:10 +02:00
|
|
|
export const getElemDistance = function(el) {
|
2017-08-16 10:47:22 +02:00
|
|
|
let location = 0;
|
2016-09-05 23:04:15 +02:00
|
|
|
if (el.offsetParent) {
|
|
|
|
do {
|
|
|
|
location += el.offsetTop;
|
|
|
|
el = el.offsetParent;
|
|
|
|
} while (el);
|
|
|
|
}
|
|
|
|
return location >= 0 ? location : 0;
|
2016-03-16 21:24:11 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determine element height multiplied by any offsets
|
|
|
|
* @private
|
2016-05-02 14:22:53 +02:00
|
|
|
* @param {HTMLElement} el Element to test for
|
2016-03-16 21:24:11 +01:00
|
|
|
* @return {Number} Height of element
|
|
|
|
*/
|
2016-04-04 00:07:10 +02:00
|
|
|
export const getElementOffset = function(el, offset) {
|
2017-08-16 10:47:22 +02:00
|
|
|
let elOffset = offset;
|
2016-09-05 23:04:15 +02:00
|
|
|
if (elOffset > 1) elOffset = 1;
|
|
|
|
if (elOffset > 0) elOffset = 0;
|
2016-03-16 21:24:11 +01:00
|
|
|
|
2016-09-05 23:04:15 +02:00
|
|
|
return Math.max(el.offsetHeight * elOffset);
|
2016-03-16 21:24:11 +01:00
|
|
|
};
|
|
|
|
|
2016-05-02 14:22:53 +02:00
|
|
|
/**
|
2016-08-14 23:14:37 +02:00
|
|
|
* Get the next or previous element from a given start point
|
2016-05-02 14:22:53 +02:00
|
|
|
* @param {HTMLElement} startEl Element to start position from
|
2016-08-14 23:14:37 +02:00
|
|
|
* @param {String} className The class we will look through
|
2016-05-02 14:22:53 +02:00
|
|
|
* @param {Number} direction Positive next element, negative previous element
|
|
|
|
* @return {[HTMLElement} Found element
|
|
|
|
*/
|
|
|
|
export const getAdjacentEl = (startEl, className, direction = 1) => {
|
2016-09-05 23:04:15 +02:00
|
|
|
if (!startEl || !className) return;
|
2016-05-02 14:22:53 +02:00
|
|
|
|
2016-09-05 23:04:15 +02:00
|
|
|
const parent = startEl.parentNode.parentNode;
|
|
|
|
const children = Array.from(parent.querySelectorAll(className));
|
2016-05-02 14:22:53 +02:00
|
|
|
|
2016-09-05 23:04:15 +02:00
|
|
|
const startPos = children.indexOf(startEl);
|
|
|
|
const operatorDirection = direction > 0 ? 1 : -1;
|
2016-08-14 23:14:37 +02:00
|
|
|
|
2016-09-05 23:04:15 +02:00
|
|
|
return children[startPos + operatorDirection];
|
2016-05-02 14:22:53 +02:00
|
|
|
};
|
|
|
|
|
2016-03-16 21:24:11 +01:00
|
|
|
/**
|
|
|
|
* Get scroll position based on top/bottom position
|
|
|
|
* @private
|
|
|
|
* @return {String} Position of scroll
|
|
|
|
*/
|
2016-04-04 00:07:10 +02:00
|
|
|
export const getScrollPosition = function(position) {
|
2016-09-05 23:04:15 +02:00
|
|
|
if (position === 'bottom') {
|
|
|
|
// Scroll position from the bottom of the viewport
|
|
|
|
return Math.max((window.scrollY || window.pageYOffset) + (window.innerHeight || document.documentElement.clientHeight));
|
|
|
|
}
|
2017-08-16 10:47:22 +02:00
|
|
|
// Scroll position from the top of the viewport
|
|
|
|
return (window.scrollY || window.pageYOffset);
|
2016-03-16 21:24:11 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determine whether an element is within the viewport
|
2016-05-02 22:39:33 +02:00
|
|
|
* @param {HTMLElement} el Element to test
|
2016-03-16 21:24:11 +01:00
|
|
|
* @return {String} Position of scroll
|
|
|
|
* @return {Boolean}
|
|
|
|
*/
|
2016-04-04 00:07:10 +02:00
|
|
|
export const isInView = function(el, position, offset) {
|
2016-09-05 23:04:15 +02:00
|
|
|
// If the user has scrolled further than the distance from the element to the top of its parent
|
2017-08-16 10:47:22 +02:00
|
|
|
return this.getScrollPosition(position) > (this.getElemDistance(el) + this.getElementOffset(el, offset));
|
2016-03-16 21:24:11 +01:00
|
|
|
};
|
|
|
|
|
2016-05-02 22:39:33 +02:00
|
|
|
/**
|
2016-08-14 23:14:37 +02:00
|
|
|
* Determine whether an element is within
|
2016-05-02 22:39:33 +02:00
|
|
|
* @param {HTMLElement} el Element to test
|
|
|
|
* @param {HTMLElement} parent Scrolling parent
|
2016-08-14 23:14:37 +02:00
|
|
|
* @param {Number} direction Whether element is visible from above or below
|
2016-05-02 22:39:33 +02:00
|
|
|
* @return {Boolean}
|
|
|
|
*/
|
|
|
|
export const isScrolledIntoView = (el, parent, direction = 1) => {
|
2016-09-05 23:04:15 +02:00
|
|
|
if (!el) return;
|
2016-05-02 22:39:33 +02:00
|
|
|
|
2016-09-05 23:04:15 +02:00
|
|
|
let isVisible;
|
2016-05-02 22:39:33 +02:00
|
|
|
|
2016-09-05 23:04:15 +02:00
|
|
|
if (direction > 0) {
|
|
|
|
// In view from bottom
|
|
|
|
isVisible = (parent.scrollTop + parent.offsetHeight) >= (el.offsetTop + el.offsetHeight);
|
|
|
|
} else {
|
|
|
|
// In view from top
|
|
|
|
isVisible = el.offsetTop >= parent.scrollTop;
|
|
|
|
}
|
2016-08-14 23:14:37 +02:00
|
|
|
|
2016-09-05 23:04:15 +02:00
|
|
|
return isVisible;
|
2016-08-14 23:14:37 +02:00
|
|
|
};
|
2016-05-02 16:29:05 +02:00
|
|
|
|
2016-03-16 21:24:11 +01:00
|
|
|
/**
|
|
|
|
* Remove html tags from a string
|
|
|
|
* @param {String} Initial string/html
|
|
|
|
* @return {String} Sanitised string
|
|
|
|
*/
|
2016-04-04 00:07:10 +02:00
|
|
|
export const stripHTML = function(html) {
|
2017-08-16 10:47:22 +02:00
|
|
|
const el = document.createElement('DIV');
|
2016-09-05 23:04:15 +02:00
|
|
|
el.innerHTML = html;
|
2017-08-16 10:47:22 +02:00
|
|
|
return el.textContent || el.innerText || '';
|
2016-03-16 21:24:11 +01:00
|
|
|
};
|
|
|
|
|
2016-08-14 23:14:37 +02:00
|
|
|
/**
|
2016-03-16 21:24:11 +01:00
|
|
|
* Adds animation to an element and removes it upon animation completion
|
|
|
|
* @param {Element} el Element to add animation to
|
|
|
|
* @param {String} animation Animation class to add to element
|
|
|
|
* @return
|
|
|
|
*/
|
2016-04-04 00:07:10 +02:00
|
|
|
export const addAnimation = (el, animation) => {
|
2017-08-16 10:47:22 +02:00
|
|
|
const animationEvent = whichAnimationEvent();
|
2016-03-16 21:24:11 +01:00
|
|
|
|
2017-08-16 10:47:22 +02:00
|
|
|
const removeAnimation = () => {
|
2016-09-05 23:04:15 +02:00
|
|
|
el.classList.remove(animation);
|
|
|
|
el.removeEventListener(animationEvent, removeAnimation, false);
|
|
|
|
};
|
2016-03-16 21:24:11 +01:00
|
|
|
|
2016-09-05 23:04:15 +02:00
|
|
|
el.classList.add(animation);
|
|
|
|
el.addEventListener(animationEvent, removeAnimation, false);
|
2016-03-16 21:24:11 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a random number between a range
|
|
|
|
* @param {Number} min Minimum range
|
|
|
|
* @param {Number} max Maximum range
|
|
|
|
* @return {Number} Random number
|
|
|
|
*/
|
2016-04-04 00:07:10 +02:00
|
|
|
export const getRandomNumber = function(min, max) {
|
2016-09-05 23:04:15 +02:00
|
|
|
return Math.floor(Math.random() * (max - min) + min);
|
2016-08-14 23:14:37 +02:00
|
|
|
};
|
2016-04-04 00:07:10 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Turn a string into a node
|
|
|
|
* @param {String} String to convert
|
2016-05-02 14:22:53 +02:00
|
|
|
* @return {HTMLElement} Converted node element
|
2016-04-04 00:07:10 +02:00
|
|
|
*/
|
|
|
|
export const strToEl = (function() {
|
2017-08-16 10:47:22 +02:00
|
|
|
const tmpEl = document.createElement('div');
|
2016-09-05 23:04:15 +02:00
|
|
|
return function(str) {
|
2017-08-16 10:47:22 +02:00
|
|
|
const cleanedInput = str.trim();
|
2017-06-27 13:42:01 +02:00
|
|
|
let r;
|
|
|
|
tmpEl.innerHTML = cleanedInput;
|
2016-09-05 23:04:15 +02:00
|
|
|
r = tmpEl.children[0];
|
|
|
|
|
|
|
|
while (tmpEl.firstChild) {
|
|
|
|
tmpEl.removeChild(tmpEl.firstChild);
|
|
|
|
}
|
2016-04-04 00:07:10 +02:00
|
|
|
|
2016-09-05 23:04:15 +02:00
|
|
|
return r;
|
|
|
|
};
|
2016-04-07 20:44:16 +02:00
|
|
|
}());
|
|
|
|
|
2016-08-14 23:14:37 +02:00
|
|
|
/**
|
2016-04-16 18:06:27 +02:00
|
|
|
* Sets the width of a passed input based on its value
|
2016-04-07 20:44:16 +02:00
|
|
|
* @return {Number} Width of input
|
|
|
|
*/
|
2016-05-16 15:46:04 +02:00
|
|
|
export const getWidthOfInput = (input) => {
|
2016-09-05 23:04:15 +02:00
|
|
|
const value = input.value || input.placeholder;
|
|
|
|
let width = input.offsetWidth;
|
|
|
|
|
|
|
|
if (value) {
|
2017-08-16 10:47:22 +02:00
|
|
|
const testEl = strToEl(`<span>${value}</span>`);
|
2016-09-05 23:04:15 +02:00
|
|
|
testEl.style.position = 'absolute';
|
|
|
|
testEl.style.padding = '0';
|
|
|
|
testEl.style.top = '-9999px';
|
|
|
|
testEl.style.left = '-9999px';
|
|
|
|
testEl.style.width = 'auto';
|
|
|
|
testEl.style.whiteSpace = 'pre';
|
|
|
|
|
2017-08-02 15:05:26 +02:00
|
|
|
if (document.body.contains(input) && window.getComputedStyle) {
|
|
|
|
const inputStyle = window.getComputedStyle(input);
|
|
|
|
|
|
|
|
if (inputStyle) {
|
|
|
|
testEl.style.fontSize = inputStyle.fontSize;
|
|
|
|
testEl.style.fontFamily = inputStyle.fontFamily;
|
|
|
|
testEl.style.fontWeight = inputStyle.fontWeight;
|
|
|
|
testEl.style.fontStyle = inputStyle.fontStyle;
|
|
|
|
testEl.style.letterSpacing = inputStyle.letterSpacing;
|
|
|
|
testEl.style.textTransform = inputStyle.textTransform;
|
|
|
|
testEl.style.padding = inputStyle.padding;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-05 23:04:15 +02:00
|
|
|
document.body.appendChild(testEl);
|
|
|
|
|
|
|
|
if (value && testEl.offsetWidth !== input.offsetWidth) {
|
|
|
|
width = testEl.offsetWidth + 4;
|
2016-04-16 18:06:27 +02:00
|
|
|
}
|
|
|
|
|
2016-09-05 23:04:15 +02:00
|
|
|
document.body.removeChild(testEl);
|
|
|
|
}
|
|
|
|
|
|
|
|
return `${width}px`;
|
2016-06-29 15:47:58 +02:00
|
|
|
};
|
|
|
|
|
2017-01-01 16:32:09 +01:00
|
|
|
/**
|
|
|
|
* Sorting function for current and previous string
|
|
|
|
* @param {String} a Current value
|
|
|
|
* @param {String} b Next value
|
|
|
|
* @return {Number} -1 for after previous,
|
|
|
|
* 1 for before,
|
|
|
|
* 0 for same location
|
|
|
|
*/
|
2016-06-29 15:47:58 +02:00
|
|
|
export const sortByAlpha = (a, b) => {
|
2016-09-05 23:04:15 +02:00
|
|
|
const labelA = (a.label || a.value).toLowerCase();
|
|
|
|
const labelB = (b.label || b.value).toLowerCase();
|
2016-06-29 15:47:58 +02:00
|
|
|
|
2016-09-05 23:04:15 +02:00
|
|
|
if (labelA < labelB) return -1;
|
|
|
|
if (labelA > labelB) return 1;
|
|
|
|
return 0;
|
2016-08-03 15:23:23 +02:00
|
|
|
};
|
|
|
|
|
2017-01-01 16:32:09 +01:00
|
|
|
/**
|
|
|
|
* Sort by numeric score
|
|
|
|
* @param {Object} a Current value
|
|
|
|
* @param {Object} b Next value
|
|
|
|
* @return {Number} -1 for after previous,
|
|
|
|
* 1 for before,
|
|
|
|
* 0 for same location
|
|
|
|
*/
|
2017-08-16 10:47:22 +02:00
|
|
|
export const sortByScore = (a, b) => a.score - b.score;
|
2017-01-01 16:32:09 +01:00
|
|
|
|
|
|
|
/**
|
2017-10-18 10:08:27 +02:00
|
|
|
* Dispatch native event
|
2017-01-01 16:32:09 +01:00
|
|
|
* @param {NodeElement} element Element to trigger event on
|
|
|
|
* @param {String} type Type of event to trigger
|
|
|
|
* @param {Object} customArgs Data to pass with event
|
|
|
|
* @return {Object} Triggered event
|
|
|
|
*/
|
2017-10-18 10:08:27 +02:00
|
|
|
export const dispatchEvent = (element, type, customArgs = null) => {
|
2017-12-20 13:38:16 +01:00
|
|
|
const event = new CustomEvent(type, {
|
2017-01-01 16:32:09 +01:00
|
|
|
detail: customArgs,
|
|
|
|
bubbles: true,
|
2017-08-16 10:47:22 +02:00
|
|
|
cancelable: true,
|
2017-01-01 16:32:09 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
return element.dispatchEvent(event);
|
|
|
|
};
|
2017-08-10 12:57:17 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Tests value against a regular expression
|
|
|
|
* @param {string} value Value to test
|
|
|
|
* @return {Boolean} Whether test passed/failed
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
export const regexFilter = (value, regex) => {
|
|
|
|
if (!value || !regex) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const expression = new RegExp(regex.source, 'i');
|
|
|
|
return expression.test(value);
|
2017-08-16 10:47:22 +02:00
|
|
|
};
|
2017-12-10 19:00:57 +01:00
|
|
|
|
|
|
|
export const getWindowHeight = () => {
|
|
|
|
const body = document.body;
|
|
|
|
const html = document.documentElement;
|
|
|
|
return Math.max(
|
|
|
|
body.scrollHeight,
|
|
|
|
body.offsetHeight,
|
|
|
|
html.clientHeight,
|
|
|
|
html.scrollHeight,
|
|
|
|
html.offsetHeight,
|
|
|
|
);
|
2017-12-18 13:06:38 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
export const reduceToValues = (items, key = 'value') => {
|
|
|
|
const values = items.reduce((prev, current) => {
|
|
|
|
prev.push(current[key]);
|
|
|
|
return prev;
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
return values;
|
2018-03-12 21:42:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fetch properties from object
|
|
|
|
* @param {Object} object Related object
|
|
|
|
* @param {String} properties Properties from object
|
|
|
|
*/
|
|
|
|
export const fetchFromObject = function (object, properties){
|
|
|
|
const index = properties.indexOf('.');
|
|
|
|
|
|
|
|
if(index > -1){
|
|
|
|
return fetchFromObject(object[properties.substring(0, index)], properties.substr(index + 1));
|
|
|
|
}
|
|
|
|
|
|
|
|
return object[properties];
|
|
|
|
};
|