/* eslint-disable */ /** * Capitalises the first letter of each word in a string * @param {String} str String to capitalise * @return {String} Capitalised string */ export const capitalise = function(str) { return str.replace(/\w\S*/g, function(txt) { return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); }); }; /** * 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 isType = function(type, obj) { var clas = Object.prototype.toString.call(obj).slice(8, -1); return obj !== undefined && obj !== null && clas === type; }; /** * Tests to see if a passed object is a node * @param {Object} obj Object to be tested * @return {Boolean} */ export const isNode = (o) => { return ( typeof Node === "object" ? o instanceof Node : o && typeof o === "object" && typeof o.nodeType === "number" && typeof o.nodeName === "string" ); }; /** * Tests to see if a passed object is an element * @param {Object} obj Object to be tested * @return {Boolean} */ export const isElement = (o) => { return ( typeof HTMLElement === "object" ? o instanceof HTMLElement : //DOM2 o && typeof o === "object" && o !== null && o.nodeType === 1 && typeof o.nodeName === "string" ); }; /** * Merges unspecified amount of objects into new object * @private * @return {Object} Merged object of arguments */ export const extend = function() { let extended = {}; let deep = false; let length = arguments.length; /** * Merge one object into another * @param {Object} obj Object to merge into extended object */ let merge = function(obj) { for (let prop in obj) { if (Object.prototype.hasOwnProperty.call(obj, prop)) { // If deep merge and property is an object, merge properties if (deep && isType('Object', obj[prop])) { extended[prop] = extend(true, extended[prop], obj[prop]); } else { extended[prop] = obj[prop]; } } } }; // Loop through each passed argument for (let i = 0; i < length; i++) { // store argument at position i let obj = arguments[i]; // If we are in fact dealing with an object, merge it. Otherwise throw error if (isType('Object', obj)) { merge(obj); } else { console.error('Custom options must be an object'); } } return extended; }; /** * CSS transition end event listener * @return */ export const whichTransitionEvent = function() { var t, el = document.createElement("fakeelement"); var transitions = { "transition": "transitionend", "OTransition": "oTransitionEnd", "MozTransition": "transitionend", "WebkitTransition": "webkitTransitionEnd" } for (t in transitions) { if (el.style[t] !== undefined) { return transitions[t]; } } }; /** * CSS animation end event listener * @return */ export const whichAnimationEvent = function() { var t, el = document.createElement('fakeelement'); var animations = { 'animation': 'animationend', 'OAnimation': 'oAnimationEnd', 'MozAnimation': 'animationend', 'WebkitAnimation': 'webkitAnimationEnd' }; for (t in animations) { if (el.style[t] !== undefined) { return animations[t]; } } }; /** * Get the ancestors of each element in the current set of matched elements, * 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 */ export const getParentsUntil = function(elem, parent, selector) { var parents = []; // Get matches for (; elem && elem !== document; elem = elem.parentNode) { // Check if parent has been reached if (parent) { var parentType = parent.charAt(0); // If parent is a class if (parentType === '.') { if (elem.classList.contains(parent.substr(1))) { break; } } // 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; } } // If parent is a tag if (elem.tagName.toLowerCase() === parent) { break; } } if (selector) { var selectorType = selector.charAt(0); // 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); } } else { parents.push(elem); } } // Return parents if any exist if (parents.length === 0) { return null; } else { return parents; } }; 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); }; export const getSiblings = function(elem) { var siblings = []; var sibling = elem.parentNode.firstChild; for (; sibling; sibling = sibling.nextSibling) { if (sibling.nodeType === 1 && sibling !== elem) { siblings.push(sibling); } } return siblings; }; /** * Find ancestor in DOM tree * @param {NodeElement} el Element to start search from * @param {[type]} cls Class of parent * @return {NodeElement} Found parent element */ export const findAncestor = function(el, cls) { while ((el = el.parentElement) && !el.classList.contains(cls)); return el; }; /** * Debounce an event handler. * @param {Function} func Function to run after wait * @param {Number} wait The delay before the function is executed * @param {Boolean} immediate If passed, trigger the function on the leading edge, instead of the trailing. * @return {Function} A function will be called after it stops being called for a given delay */ export const debounce = function(func, wait, immediate) { var timeout; return function() { var context = this, args = arguments; var later = function() { timeout = null; if (!immediate) func.apply(context, args); }; var callNow = immediate && !timeout; clearTimeout(timeout); timeout = setTimeout(later, wait); if (callNow) func.apply(context, args); }; }; /** * 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 */ export const getElemDistance = function(el) { var location = 0; if (el.offsetParent) { do { location += el.offsetTop; el = el.offsetParent; } while (el); } return location >= 0 ? location : 0; }; /** * Determine element height multiplied by any offsets * @private * @param {HTMLElement} el Element to test for * @return {Number} Height of element */ export const getElementOffset = function(el, offset) { var elOffset = offset; if (elOffset > 1) elOffset = 1; if (elOffset > 0) elOffset = 0; return Math.max(el.offsetHeight * elOffset); }; /** * Get the next or previous element from a given start point * @param {HTMLElement} startEl Element to start position from * @param {String} className The class we will look through * @param {Number} direction Positive next element, negative previous element * @return {[HTMLElement} Found element */ export const getAdjacentEl = (startEl, className, direction = 1) => { if (!startEl || !className) return; const parent = startEl.parentNode.parentNode; const children = Array.from(parent.querySelectorAll(className)); const startPos = children.indexOf(startEl); const operatorDirection = direction > 0 ? 1 : -1; return children[startPos + operatorDirection]; }; /** * Get scroll position based on top/bottom position * @private * @return {String} Position of scroll */ export const getScrollPosition = function(position) { if (position === 'bottom') { // Scroll position from the bottom of the viewport return Math.max((window.scrollY || window.pageYOffset) + (window.innerHeight || document.documentElement.clientHeight)); } else { // Scroll position from the top of the viewport return (window.scrollY || window.pageYOffset); } }; /** * Determine whether an element is within the viewport * @param {HTMLElement} el Element to test * @return {String} Position of scroll * @return {Boolean} */ export const isInView = function(el, position, offset) { // If the user has scrolled further than the distance from the element to the top of its parent return this.getScrollPosition(position) > (this.getElemDistance(el) + this.getElementOffset(el, offset)) ? true : false; }; /** * Determine whether an element is within * @param {HTMLElement} el Element to test * @param {HTMLElement} parent Scrolling parent * @param {Number} direction Whether element is visible from above or below * @return {Boolean} */ export const isScrolledIntoView = (el, parent, direction = 1) => { if (!el) return; let isVisible; 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; } return isVisible; }; /** * Remove html tags from a string * @param {String} Initial string/html * @return {String} Sanitised string */ export const stripHTML = function(html) { let el = document.createElement("DIV"); el.innerHTML = html; return el.textContent || el.innerText || ""; }; /** * 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 */ export const addAnimation = (el, animation) => { let animationEvent = whichAnimationEvent(); let removeAnimation = () => { el.classList.remove(animation); el.removeEventListener(animationEvent, removeAnimation, false); }; el.classList.add(animation); el.addEventListener(animationEvent, removeAnimation, false); }; /** * Get a random number between a range * @param {Number} min Minimum range * @param {Number} max Maximum range * @return {Number} Random number */ export const getRandomNumber = function(min, max) { return Math.floor(Math.random() * (max - min) + min); }; /** * Turn a string into a node * @param {String} String to convert * @return {HTMLElement} Converted node element */ export const strToEl = (function() { var tmpEl = document.createElement('div'); return function(str) { var r; tmpEl.innerHTML = str; r = tmpEl.children[0]; while (tmpEl.firstChild) { tmpEl.removeChild(tmpEl.firstChild); } return r; }; }()); /** * Sets the width of a passed input based on its value * @return {Number} Width of input */ export const getWidthOfInput = (input) => { const value = input.value || input.placeholder; let width = input.offsetWidth; if (value) { const testEl = strToEl(`${ value }`); testEl.style.position = 'absolute'; testEl.style.padding = '0'; testEl.style.top = '-9999px'; testEl.style.left = '-9999px'; testEl.style.width = 'auto'; testEl.style.whiteSpace = 'pre'; document.body.appendChild(testEl); if (value && testEl.offsetWidth !== input.offsetWidth) { width = testEl.offsetWidth + 4; } document.body.removeChild(testEl); } return `${width}px`; }; export const sortByAlpha = (a, b) => { const labelA = (a.label || a.value).toLowerCase(); const labelB = (b.label || b.value).toLowerCase(); if (labelA < labelB) return -1; if (labelA > labelB) return 1; return 0; }; export const sortByScore = (a, b) => { return a.score - b.score; };