Source: helper/prototype.element.js

/*******************************************************************************
 * ClassName Function
 ******************************************************************************/

/**
 * @namespace Element
 */

/**
 * If an element has a class name
 * @param   {string}  name class to check for
 * @returns {boolean} true if Element has class name
 */
Element.prototype.hasClassName = function (name) {
    return this.className.indexOf(name) >= 0;
};

/**
 * Add a class name to an Element
 * @param   {string}  name class to add
 * @returns {Element} the originating element
 */
Element.prototype.addClassName = function (name) {
    if (!this.hasClassName(name)) {
        this.className += (this.className.length > 0 ? ' ' : '') + name;
    }
    return this;
};

/**
 * Remove a class name from an element
 * @param   {string}  name class to remove
 * @returns {Element} the originating element
 */
Element.prototype.removeClassName = function (name) {
    if (this.hasClassName(name)) {
        this.className = this.className.replace(new RegExp(' ?' + name, 'i'), '');
    }
    return this;
};

/**
 * Shortcut to add or remove the class name 'disabled'
 * @param   {boolean} enabled true to enable and remove the 'disabled' class name
 * @returns {Element} the originating element
 */
Element.prototype.setEnabled = function (enabled) {
    return this.addRemoveClass('disabled', !enabled);
};

/**
 * Check if the Element is enabled. Looks for not having the 'disabled' class name
 * @returns {boolean} true if enabled
 */
Element.prototype.isEnabled = function () {
    return !this.hasClassName('disabled');
};

/**
 * Shortcut to add or remove the class name 'active'
 * @param   {boolean} active true to set and remove the 'active' class name
 * @returns {Element} the originating element
 */
Element.prototype.setIsCurrent = function (active) {
    return this.addRemoveClass('active', active);
};

/**
 * Check if the Element is active. Looks for the Element to have the 'active' class name
 * @returns {boolean} true if it is the currently active
 */
Element.prototype.isCurrent = function () {
    return this.hasClassName('active');
};

/**
 * Add or remove a class name to an Element
 * @param   {string}  className name of the class
 * @param   {boolean} add       true if it should be added, false otherwise
 * @returns {Element} the originating element
 */
Element.prototype.addRemoveClass = function (className, add) {
    if (add) {
        return this.addClassName(className);
    } else {
        return this.removeClassName(className);
    }
};

/**
 * Calculate the absolute offset to the top.
 * does this recursively up the DOM
 * 
 * @param   {object} containerElement the Element to check its absolut top for
 * @returns {number} absolute top in px
 */
Element.prototype.absoluteOffsetTop = function (containerElement) {

    var el = this;
    var top = el.offsetTop;

    while (el.offsetParent && el != containerElement && (containerElement == null || (containerElement != null && el != containerElement.offsetParent))) {
        el = el.offsetParent;
        var tr = el.stylePropertyValue("transform");
        if (tr) {

            // Take the transform scale into account.
            // Extracted from: https://css-tricks.com/get-value-of-css-rotation-through-javascript/
            var values = tr.split('(')[1].split(')')[0].split(',');
            var a = values[0],
                b = values[1]/*,
                c = values[2],
                d = values[3]*/;
            var scale = Math.sqrt(a * a + b * b);
            top *= scale;
        }

        top += el.offsetTop;
    }

    return top;
};

/**
 * Element Style object of an element
 * @returns {object} element style object
 */
Element.prototype.elementStyle = function () {
    var rtval = null;
    try {
        // FireFox has a bug on getComputedStyle for textNodes. This solves it.
        rtval = (this.currentStyle || (window.getComputedStyle && window.getComputedStyle(this, null)) || this.currentStyle || this.style);
    } catch (e) {}
    return rtval;
};

/**
 * Returns the calculated value of a property of the elements CSS
 * 
 * Extracted from: https://css-tricks.com/get-value-of-css-rotation-through-javascript/
 * @param {string} name property name to get the value from
 */
Element.prototype.stylePropertyValue = function (name) {
    var st = this.elementStyle();
    var tr = st.getPropertyValue("-webkit-" + name) ||
        st.getPropertyValue("-moz-" + name) ||
        st.getPropertyValue("-ms-" + name) ||
        st.getPropertyValue("-o-" + name) ||
        st.getPropertyValue(name) ||
        "none";
    return tr && tr != "none" ? tr : null;
};

if (!document.getElementsByClassName) {
    /**
     * @function
     * @param String classname to search for
     * @param HTMLElement node to search in
     * @returns Array list
     */
    document.getElementsByClassName = function (className) {
        return this.querySelectorAll("." + className.replace(" ", "."));
    };
    Element.prototype.getElementsByClassName = document.getElementsByClassName;
}