nw-skeleton

Source: app-wrapper/js/helper/htmlHelper.js

/**
 * @fileOverview HtmlHelper class file
 * @author Dino Ivankov <dinoivankov@gmail.com>
 * @version 1.3.1
 */

const _ = require('lodash');
const AppBaseClass = require('../lib/appBase').AppBaseClass;

var _appWrapper;
// var appState;
/**
 * HtmlHelper class - manages HTML element operations
 *
 * @class
 * @extends {appWrapper.AppBaseClass}
 * @memberof appWrapper.helpers
 */

class HtmlHelper extends AppBaseClass {

    /**
     * Creates HtmlHelper instance
     *
     * @constructor
     * @return {HtmlHelper}              Instance of HtmlHelper class
     */
    constructor(){
        super();

        _appWrapper = window.getAppWrapper();
        // appState = _appWrapper.getAppState();

        this.intervals = {
            scrollTo: {}
        };
    }

    /**
     * Initializes htmlHelper and extends HTMLElement prototype with its methods
     *
     * @return {HtmlHelper} Instance of HtmlHelper class
     */
    async initialize () {
        await super.initialize();
        this.extendElementProto();
        return this;
    }

    /**
     * Extends HTMLElement prototype with methods from htmlHelper
     *
     * @return {undefined}
     */
    extendElementProto () {
        var self = this;

        var customMethods = _.without((Object.getOwnPropertyNames(HtmlHelper.prototype).filter(function (p) {
            return typeof HtmlHelper.prototype[p] == 'function';
        })), 'constructor', 'initialize', 'finalize', 'extendElementProto');

        Element.prototype.getCustomMethods = function() {
            return customMethods;
        };

        for (let i=0; i<customMethods.length; i++){
            Element.prototype[customMethods[i]] = function() {
                if (arguments && arguments.length){
                    if (typeof arguments[0] == Element){
                        // if first argument is Element, use 'this' instead
                        return self[customMethods[i]].apply(self, arguments);
                    } else {
                        // call method with unchanged arguments
                        return self[customMethods[i]].apply(self, _.union([this], arguments));
                    }
                } else {
                    // call method with 'this' as only argument
                    return self[customMethods[i]](this);
                }
            };
        }
    }

    /**
     * Sets element CSS styles
     *
     * @param {HTMLElement} element Html element for style manipulation
     * @param {array}       styles  An array of objects representing style properties to set
     * @param {Boolean}     merge   Flag to indicate merging existing styles with new ones instead of rewriting entire style attribute
     * @return {undefined}
     */
    setElementStyles (element, styles, merge){
        if (this instanceof Element){
            element = this;
        }
        if (element && element.setAttribute && _.isFunction(element.setAttribute) && styles && _.isObject(styles)){
            var newStyles;
            if (merge){
                newStyles = _.assignIn(this.getElementStyles(element), styles);
            } else {
                newStyles = styles;
            }
            var styleString = '';
            var stylesData = [];
            var propertyNames = _.keys(newStyles);
            for (let i=0; i<propertyNames.length; i++){
                stylesData.push(propertyNames[i] + ': ' + newStyles[propertyNames[i]]);
            }
            styleString = stylesData.join('; ');
            element.setAttribute('style', styleString);
        }
    }

    /**
     * Returns current element style values from style attribute
     *
     * @param  {HTMLElement}    element Element to get styles for
     * @return {Object}                 An array of objects representing style properties
     */
    getElementStyles (element){
        var styles = {};
        if (element && element.getAttribute && _.isFunction(element.getAttribute)){
            var elementStyleString = element.getAttribute('style');
            if (elementStyleString){
                var elementStyles = elementStyleString.split(';');
                for (let i=0; i<elementStyles.length; i++){
                    var styleData = elementStyles[i].split(':');
                    if (styleData && styleData.length == 2){
                        styles[styleData[0].trim()] = styleData[1].trim();
                    }
                }
            }
        }
        return styles;
    }

    /**
     * Removes style properties from element style attribute
     *
     * @param  {HTMLElement}    element Element to remove styles from
     * @param  {string[]} propertyNames An array of style property names to remove
     * @return {undefined}
     */
    removeElementStyles (element, propertyNames){
        if (propertyNames && propertyNames.length && element && element.getAttribute && _.isFunction(element.getAttribute)){
            if (_.isString(propertyNames)){
                propertyNames = [propertyNames];
            }
            var elementStyles = this.getElementStyles(element);
            for(let i=0; i<propertyNames.length; i++){
                if (elementStyles && elementStyles[propertyNames[i]]){
                    delete elementStyles[propertyNames[i]];
                }
            }
            this.setElementStyles(element, elementStyles);
        }
    }

    /**
     * Sets element width and height to current values, forcing its size to be fixed
     *
     * @param {HTMLElement}    element  Element to set fixed size on
     * @param {Number} elementHeight    Optional height value to force element size
     * @param {Number} elementWidth     Optional width value to force element size
     * @return {undefined}
     */
    setFixedSize (element, elementHeight, elementWidth) {
        if (element && element.offsetHeight){
            if (_.isUndefined(elementHeight)){
                elementHeight = parseInt(element.offsetHeight, 10);
            }
            if (_.isUndefined(elementWidth)){
                elementWidth = parseInt(element.offsetWidth, 10);
            }

            var elementStyles = this.getElementStyles(element);

            elementStyles.height = elementHeight + 'px';
            elementStyles.width = elementWidth + 'px';
            elementStyles.overflow = 'hidden';

            this.setElementStyles(element, elementStyles);
        }
    }

    /**
     * Removes fixed size set by this.setFixedSize method
     *
     * @param {HTMLElement}    element  Element to unset fixed size on
     * @return {undefined}
     */
    unsetFixedSize (element) {
        var propertiesToRemove = ['width', 'height', 'overflow'];
        this.removeElementStyles(element, propertiesToRemove);
    }

    /**
     * Forces element width and height to values from the arguments, if values are omitted, current element size is used
     *
     * @param {HTMLElement}    element  Element to set fixed size on
     * @param {Number} elementHeight    Optional height value to force element size
     * @param {Number} elementWidth     Optional width value to force element size
     * @return {undefined}
     */
    forceSize (element, elementHeight, elementWidth) {
        if (element && element.offsetHeight){
            if (_.isUndefined(elementHeight)){
                elementHeight = parseInt(element.offsetHeight, 10);
            } else {
                element.setAttribute('data-previous-height', parseInt(element.offsetHeight, 10));
            }

            if (_.isUndefined(elementWidth)){
                elementWidth = parseInt(element.offsetWidth, 10);
            } else {
                element.setAttribute('data-previous-width', parseInt(element.offsetWidth, 10));
            }

            let newStyles = {
                height: elementHeight + 'px',
                width: elementWidth + 'px'
            };

            this.setElementStyles(element, newStyles, true);
            element.setAttribute('data-forced-size', '1');
        }
    }

    /**
     * Removes forced size values from element style
     *
     * @param {HTMLElement}    element  Element to unset fixed size on
     * @return {undefined}
     */
    unforceSize (element) {
        let previousWidth = element.getAttribute('data-previous-width');
        let previousHeight = element.getAttribute('data-previous-height');
        this.removeElementStyles(element, ['width', 'height']);

        let newStyles = {};

        if (previousWidth){
            element.removeAttribute('data-previous-width');
            newStyles.width = previousWidth;
        }

        if (previousHeight){
            element.removeAttribute('data-previous-height');
            newStyles.width = previousHeight;
        }

        if (Object.keys(newStyles).length){
            this.setElementStyles(element, newStyles, true);
        }
        element.removeAttribute('data-forced-size');

    }

    /**
     * Gets element "real" dimensions, taking paddings, borders etc. into consideration
     *
     * @param {HTMLElement} element     Element to get dimensions (or parent in which selector argument will be applied)
     * @param  {string}     selector    Optional selector to filter children in 'element' argument
     * @return {Object}                 Object with width and height properties
     */
    getRealDimensions (element, selector){
        let dimensions = {
            width: 0,
            height: 0
        };

        if (this instanceof Element){
            element = this;
        }

        if (element){
            var dimsElement = element;
            if (selector && dimsElement.querySelector(selector)){
                dimsElement = dimsElement.querySelector(selector);
            }

            var dimsElementStyles = dimsElement.getComputedStyles();
            dimensions.height = parseFloat(dimsElementStyles.height, 10);
            dimensions.width = parseFloat(dimsElementStyles.width, 10);

            let widthOffset = 0;
            let heightOffset = 0;

            let widthOffsetProperties = ['padding-left', 'padding-right', 'border-left-width', 'border-right-width'];
            let heightOffsetProperties = ['padding-top', 'padding-bottom', 'border-top-width', 'border-bottom-width'];

            for (let i=0; i<widthOffsetProperties.length; i++){
                if (dimsElementStyles[widthOffsetProperties[i]] && parseFloat(dimsElementStyles[widthOffsetProperties[i]], 10)){
                    widthOffset -= parseFloat(dimsElementStyles[widthOffsetProperties[i]], 10);
                }
            }

            for (let i=0; i<heightOffsetProperties.length; i++){
                if (dimsElementStyles[heightOffsetProperties[i]] && parseFloat(dimsElementStyles[heightOffsetProperties[i]], 10)){
                    heightOffset -= parseFloat(dimsElementStyles[heightOffsetProperties[i]], 10);
                }
            }

            dimensions.width += widthOffset;
            dimensions.height += heightOffset;
        }

        return dimensions;
    }

    /**
     * Gets element "real" dimensions, by cloning given element
     *
     * @param {HTMLElement} element     Element to get dimensions (or parent in which selector argument will be applied)
     * @param  {string}     selector    Optional selector to filter children in 'element' argument
     * @return {Object}                 Object with width and height properties
     */
    getCloneRealDimensions (element, selector){
        let dimensions = {
            width: 0,
            height: 0
        };

        if (element && element.cloneNode && _.isFunction(element.cloneNode)){
            let clonedEl = element.cloneNode(true);

            let clonedElStyles = this.getElementStyles(element);
            // clonedElStyles['margin-top'] = '-10000px';

            this.setElementStyles(clonedEl, clonedElStyles);
            this.removeElementStyles(clonedEl, ['width', 'height']);

            if (!selector && element && element.parentNode){
                element.parentNode.appendChild(clonedEl);
            } else {
                document.body.appendChild(clonedEl);
            }

            let originalCloned = clonedEl;

            if (selector && clonedEl.querySelector(selector)){
                clonedEl = clonedEl.querySelector(selector);
            }

            clonedEl.removeClass('transition-wh');

            this.removeElementStyles(clonedEl, ['width', 'height']);

            dimensions.height = parseInt(clonedEl.offsetHeight, 10);
            dimensions.width = parseInt(clonedEl.offsetWidth, 10);

            originalCloned.parentNode.removeChild(originalCloned);
            originalCloned = null;
        }

        return dimensions;
    }

    /**
     * Sets or returns elements unique identifier
     *
     * @param {HTMLElement} element Element to set identifier on
     * @param  {Boolean}    setAttr Flag to indicate whether to set 'data-identifier' attribute on element
     * @return {string}             Element unique identifier
     */
    getUniqueElementIdentifier (element, setAttr){
        var identifier = '';

        if (element.getAttribute('data-identifier')){
            identifier = element.getAttribute('data-identifier');
        } else if (element.getAttribute('id')){
            identifier = element.getAttribute('id');
        } else {
            identifier = element.tagName + '_' + element.className + '_' + element.offsetTop + '_' + element.offsetHeight;
        }

        if (identifier && setAttr){
            element.setAttribute('data-identifier', identifier);
        }

        return identifier;
    }

    /**
     * Adds css class to element
     *
     * @param {HTMLElement}         element     Element to add class to
     * @param {(string|string[])}   className   Class name to add
     * @return {HTMLElement}                    Same element for chaining
     */
    addClass (element, className){
        if (this instanceof Element){
            element = this;
        }
        if (_.isArray(className)){
            for (let i=0; i<className.length;i++){
                this.addClass(element, className[i]);
            }
        } else {
            if (!this.hasClass(element, className)){
                let classes = element.getAttribute('class');
                if (classes && classes.split && _.isFunction(classes.split)){
                    classes = classes.split(' ');
                    classes.push(className);
                    element.setAttribute('class', classes.join(' '));
                } else {
                    element.setAttribute('class', className);
                }
            }
        }
        return element;
    }

    /**
     * Removes css class from element
     *
     * @param {HTMLElement}         element     Element to remove class from
     * @param {(string|string[])}   className   Class name to remove
     * @return {HTMLElement}                    Same element for chaining
     */
    removeClass (element, className){
        if (this instanceof Element){
            element = this;
        }
        if (_.isArray(className)){
            for (let i=0; i<className.length;i++){
                this.removeClass(element, className[i]);
            }
        } else {
            if (this.hasClass(element, className)){
                let classes = element.getAttribute('class');
                if (classes && classes.split && _.isFunction(classes.split)){
                    classes = classes.split(' ');
                    classes = _.without(classes, className);
                    element.setAttribute('class', classes.join(' '));
                }
            }
        }
        return element;
    }

    /**
     * Checks whether given element has certain className
     *
     * @param {HTMLElement} element Element to check
     * @param {string} className    Class name to check
     * @return {Boolean}            True if element has class, false otherwise
     */
    hasClass (element, className){
        if (this instanceof Element){
            element = this;
        }
        let hasClass = false;
        let classes = element.getAttribute('class');
        if (classes && classes.split && _.isFunction(classes.split)){
            classes = classes.split(' ');
            hasClass = _.includes(classes, className);
        }
        return hasClass;
    }

    /**
     * Toggles css class on element
     *
     * @param {HTMLElement} element Element to toggle class on
     * @param {string} className    Class name to toggle
     * @return {HTMLElement}        Same element for chaining
     */
    toggleClass (element, className){
        let hasClass = this.hasClass(element, className);
        if (hasClass){
            return this.removeClass(element, className);
        } else {
            return this.addClass(element, className);
        }
    }

    /**
     * Shows hidden element
     *
     * @param {HTMLElement} element Element to show
     * @return {undefined}
     */
    show (element) {
        if (element){
            this.removeElementStyles(element, 'display');
        }
    }

    /**
     * Hides the element
     *
     * @param {HTMLElement} element Element to hide
     * @return {undefined}
     */
    hide (element) {
        if (element){
            this.setElementStyles(element, {display: 'none'}, true);
        }
    }

    /**
     * Shows or hides an element
     *
     * @param {HTMLElement} element Element to show or hide
     * @return {undefined}
     */
    toggle (element) {
        if (element){
            if (!element.clientHeight && !element.clientWidth){
                var defaultDisplay = this.getElementDefaultDisplay(element);
                this.setElementStyles(element, {display: defaultDisplay}, true);
            } else {
                this.hide(element);
            }
        }
    }

    /**
     * Hides hidden element by setting 'visibility' css property
     *
     * @param {HTMLElement} element Element to hide
     * @return {undefined}
     */
    makeInvisible (element) {
        if (this instanceof Element){
            element = this;
        }
        if (element){
            this.setElementStyles(element, {visibility: 'hidden'}, true);
        }
    }

    /**
     * Shows hidden element by setting 'visibility' css property
     *
     * @param {HTMLElement} element Element to show
     * @return {undefined}
     */
    makeVisible (element) {
        if (this instanceof Element){
            element = this;
        }
        if (element){
            this.setElementStyles(element, {visibility: 'visible'}, true);
        }
    }

    /**
     * Toggles 'visibility' css property, showing or hiding the element
     *
     * @param {HTMLElement} element Element to toggle visibility on
     * @return {undefined}
     */
    toggleVisibility (element) {
        if (this instanceof Element){
            element = this;
        }
        if (element){
            let styles = element.getComputedStyles();
            if (styles && styles.visibility){
                if (styles.visibility == 'hidden'){
                    _appWrapper.getHelper('html').makeVisible(element);
                } else {
                    _appWrapper.getHelper('html').makeInvisible(element);
                }
            }
        }
    }

    /**
     * Gets default 'display' css property value for given element
     *
     * @param {HTMLElement} element Element to get value for
     * @return {string}             Default 'display' value for element
     */
    getElementDefaultDisplay(element) {
        var tag = element.tagName;
        var cStyle;
        var newElement = document.createElement(tag);
        var gcs = 'getComputedStyle' in window;

        document.body.appendChild(newElement);
        cStyle = (gcs ? window.getComputedStyle(newElement, '') : newElement.currentStyle).display;
        document.body.removeChild(newElement);

        return cStyle;
    }

    /**
     * Scrolls element to given value
     *
     * @param {HTMLElement} element Element to scroll
     * @param  {Number} to          Value to scroll to
     * @param  {Number} duration    Duration for animated scrolls
     * @return {undefined}
     */
    scrollElementTo (element, to, duration) {
        var identifier = this.getUniqueElementIdentifier(element, true);
        var frameDuration = parseInt(1000/60, 10);
        var maxScrollHeight = element.scrollHeight - element.clientHeight;

        if (duration <= 0) {
            if (to > maxScrollHeight){
                to = maxScrollHeight;
            }
            element.scrollTop = to;
            return;
        }

        if (element.scrollHeight <= element.clientHeight){
            return;
        }

        var finalValue = to;
        var difference = finalValue - element.scrollTop;
        if (difference > 0 && finalValue >= maxScrollHeight){
            finalValue = maxScrollHeight;
            difference = finalValue - element.scrollTop;
        }

        var frameCount = parseInt(duration/frameDuration, 10);
        var stepIncrease = parseInt(difference / frameCount, 10);
        if (!stepIncrease){
            stepIncrease = difference > 0 ? 1 : -1;
        }

        clearInterval(this.intervals.scrollTo[identifier]);
        this.intervals.scrollTo[identifier] = setInterval(() => {
            this.scrollElementStep(element, stepIncrease, finalValue);
        }, frameDuration);
    }

    /**
     * Helper method for animated element scrolling, used by this.scrollElementTo
     *
     * @param {HTMLElement} element    Element to scroll
     * @param {Number} stepIncrease    Value for each scrolling step
     * @param {Number} finalValue      Final element scroll value
     * @return {undefined}
     */
    scrollElementStep (element, stepIncrease, finalValue){
        var currentValue = element.scrollTop;
        var nextValue = currentValue + stepIncrease;
        var maxValue = element.scrollHeight;
        var minValue = 0;
        var identifier = this.getUniqueElementIdentifier(element, true);

        if (stepIncrease >= 0){
            if (currentValue >= maxValue){
                nextValue = maxValue;
            }
        } else {
            if (currentValue <= minValue){
                nextValue = minValue;
            }
        }

        element.scrollTop = nextValue;

        if (stepIncrease >= 0 && nextValue >= finalValue){
            clearInterval(this.intervals.scrollTo[identifier]);
        } else if (stepIncrease < 0 && nextValue <= finalValue){
            clearInterval(this.intervals.scrollTo[identifier]);
        }
    }

    /**
     * Scrolls parent until its child element is visible
     *
     * @param {HTMLElement} element    Element whose parent will be scrolled
     * @param {Number}      duration   Duration for animated scrolls
     * @return {undefined}
     */
    scrollParentToElement (element, duration) {
        let parentElement = element.parentNode;
        let currentParentScrollTop = parentElement.scrollTop;
        element.scrollIntoView();
        let newParentScrollTop = parentElement.scrollTop;
        parentElement.scrollTop = currentParentScrollTop;
        return this.scrollElementTo(parentElement, newParentScrollTop, duration);
    }

    /**
     * Returns first element in parent tree that has given class name
     *
     * @param {HTMLElement} element     Element whose parents will be searched
     * @param  {string} targetClass     Class name for searching
     * @return {HTMLElement}            Parent element that matches given criteria
     */
    getParentByClass(element, targetClass){
        if (this instanceof Element){
            element = this;
        }
        let parent;
        if (element && element.parentNode){
            parent = element;
            while (!parent.hasClass(targetClass)) {
                parent = parent.parentNode;
            }
        }
        return parent;
    }

    /**
     * Selects all text in element
     *
     * @param {HTMLElement} element     Element to select
     * @return {undefined}
     */
    selectAll (element){
        var selection = window.getSelection();
        selection.removeAllRanges();
        var range = document.createRange();
        range.selectNode(element);
        selection.addRange(range);
    }

    /**
     * Returns all computed styles for given element
     *
     * @param {HTMLElement} element     Element to get styles for
     * @return {Object}                 An array of objects representing style properties
     */
    getComputedStyles (element) {
        var style;
        var returns = {};
        if (element && element.tagName){
            style = window.getComputedStyle(element, null);
            for(let i=0; i<style.length; i++){
                var prop = style[i];
                var val = style.getPropertyValue(prop);
                returns[prop] = val;
            }
        }
        return returns;
    }

    /**
     * Returns single computed style property for given element
     *
     * @param  {HTMLElement} element     Element to get style for
     * @param  {string}      propName    Style property to get
     * @return {mixed}                 Style property value
     */
    getComputedStyle (element, propName) {
        let style;
        let val = '';
        if (propName && element && element.tagName){
            style = window.getComputedStyle(element, null);
            for(let i=0; i<style.length; i++){
                if (style[i] == propName){
                    val = style.getPropertyValue(style[i]);
                    break;
                }
            }
        }
        return val;
    }

    /**
     * Gets absolute position for element
     *
     * @param  {HTMLElement} element     Element to get position for
     * @return {Object}                  Object with 'offsetTop' and 'offsetLeft' numeric values
     */
    getAbsolutePosition (element){
        var offsetLeft = element.offsetLeft;
        var offsetTop = element.offsetTop;
        var parent = element.parentNode;

        if (parent && parent.tagName && parent.tagName.toLowerCase() !== 'body'){
            var parentOffset = this.getAbsolutePosition(parent);
            offsetLeft += parentOffset.offsetLeft;
            offsetTop += parentOffset.offsetTop;
        }

        return {
            offsetLeft: offsetLeft,
            offsetTop: offsetTop
        };
    }

    /**
     * Returns first parent element that has children matching selector
     *
     * @param  {HTMLElement} element    Element to search
     * @param  {String}      selector   Selector to search
     * @return {(Boolean|HTMLElement)}  Parent element or false if none found
     */
    parentQuerySelector (element, selector){
        let parent = element;
        let parentFound = false;
        do {
            if (parent && parent.parentNode){
                parent = parent.parentNode;
                if (parent && parent.parentNode && parent.parentNode.querySelectorAll && _.isFunction(parent.parentNode.querySelectorAll)){
                    let parentChildren = parent.parentNode.querySelectorAll(selector);
                    parentFound = _.includes(parentChildren, parent);
                } else {
                    parentFound = true;
                    parent = false;
                }
            } else {
                parentFound = true;
            }
        } while (!parentFound);
        return parent;
    }

    /**
     * Checks whether text can be pasted into given element
     *
     * @param  {HTMLElement} element    Element to check
     * @return {Boolean}                True if text can be pasted, false otherwise
     */
    canPasteText (element) {
        return (element && !_.isUndefined(element.selectionStart) && !_.isUndefined(element.selectionEnd) && !_.isUndefined(element.value));
    }

    /**
     * Checks whether text in given element can be selected
     *
     * @param  {HTMLElement} element    Element to check
     * @return {Boolean}                True if text can be selected, false otherwise
     */
    isTextSelectable (element) {
        return (element && !_.isUndefined(element.selectionStart) && !_.isNull(element.selectionStart) && !_.isUndefined(element.selectionEnd) && !_.isNull(element.selectionEnd) && !_.isUndefined(element.value));
    }

    /**
     * Triggers custom event on the element
     *
     * @param  {HTMLElement}    element      Element to trigger event on
     * @param  {string}         eventName    Name of the event
     * @param  {Object}         eventOptions Object with event options
     * @return {undefined}
     */
    triggerCustomEvent (element, eventName, eventOptions){
        if (this instanceof Element){
            element = this;
        }
        if (!eventOptions){
            eventOptions = {};
        }
        let options = _.extend({
            bubbles: true,
            cancelable: true,
            target: element,
            type: 'input',
            isTrusted: true,
        }, eventOptions);

        var event = new CustomEvent(eventName, options);
        element.dispatchEvent(event);
    }

    /**
     * Gets input value for input elements
     *
     * @param  {HTMLElement}    element  Element to get value from
     * @return {string}                  Input element value
     */
    getInputValue (element){
        if (this instanceof Element){
            element = this;
        }
        let value;
        if (_.includes(['checkbox'], element.getAttribute('type'))){
            value = element.checked;
        } else {
            value = element.value;
        }
        return value;
    }

    /**
     * Sets input element value
     *
     * @param {HTMLElement}    element  Element to set value for
     * @param {mixed} value             Value to set
     * @return {undefined}
     */
    setInputValue (element, value){
        if (this instanceof Element){
            element = this;
        }
        if (_.includes(['checkbox'], element.getAttribute('type'))){
            if (value) {
                element.checked = true;
            } else {
                element.checked = false;
            }
        } else {
            element.value = value;
        }
        return value;
    }

    /**
     * Gets transition duration in milliseconds for given element
     *
     * @param {HTMLElement}    element  Element for which should transition-duration be returned
     * @return {Number}                 transition-duration in milliseconds
     */
    getTransitionDuration(element){
        let duration = 0;

        if (this instanceof Element){
            element = this;
        }

        let styles = element.getComputedStyles();
        if (styles && styles['transition-duration']){
            let newDuration = parseFloat(styles['transition-duration']);
            if (newDuration && !isNaN(newDuration)){
                duration = newDuration * 1000;
            }
        }
        return duration;
    }
}

exports.HtmlHelper = HtmlHelper;