/**
 * CSSCore.js
 *
 * @form https://github.com/facebook/fbjs/blob/master/packages/fbjs/src/core/CSSCore.js
 * @Date: 2018/6/4 下午3:27
 */

import invariant from 'invariant';

function matchesSelector_SLOW(element, selector) {
    var root = element;
    while (root.parentNode) {
        root = root.parentNode;
    }

    var all = root.querySelectorAll(selector);
    return Array.prototype.indexOf.call(all, element) !== -1;
}

var CSSCore = {
    /**
     * Adds the class passed in to the element if it doesn't already have it.
     *
     * @param {DOMElement} element the element to set the class on
     * @param {string} className the CSS className
     * @return {DOMElement} the element passed in
     */
    addClass: function addClass(element, className) {
        invariant(!/\s/.test(className), 'CSSCore.addClass takes only a single class name. "%s" contains ' + 'multiple classes.', className);

        if (className) {
            if (element.classList) {
                element.classList.add(className);
            } else if (!CSSCore.hasClass(element, className)) {
                element.className = element.className + ' ' + className;
            }
        }
        return element;
    },

    /**
     * Removes the class passed in from the element
     *
     * @param {DOMElement} element the element to set the class on
     * @param {string} className the CSS className
     * @return {DOMElement} the element passed in
     */
    removeClass: function removeClass(element, className) {
        invariant(!/\s/.test(className), 'CSSCore.removeClass takes only a single class name. "%s" contains ' + 'multiple classes.', className);

        if (className) {
            if (element.classList) {
                element.classList.remove(className);
            } else if (CSSCore.hasClass(element, className)) {
                element.className = element.className.replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)', 'g'), '$1').replace(/\s+/g, ' ') // multiple spaces to one
                .replace(/^\s*|\s*$/g, ''); // trim the ends
            }
        }
        return element;
    },

    /**
     * Helper to add or remove a class from an element based on a condition.
     *
     * @param {DOMElement} element the element to set the class on
     * @param {string} className the CSS className
     * @param {*} bool condition to whether to add or remove the class
     * @return {DOMElement} the element passed in
     */
    conditionClass: function conditionClass(element, className, bool) {
        return (bool ? CSSCore.addClass : CSSCore.removeClass)(element, className);
    },

    /**
     * Tests whether the element has the class specified.
     *
     * @param {DOMNode|DOMWindow} element the element to check the class on
     * @param {string} className the CSS className
     * @return {boolean} true if the element has the class, false if not
     */
    hasClass: function hasClass(element, className) {
        invariant(!/\s/.test(className), 'CSS.hasClass takes only a single class name.');
        if (element.classList) {
            return !!className && element.classList.contains(className);
        }
        return (' ' + element.className + ' ').indexOf(' ' + className + ' ') > -1;
    },

    /**
     * Tests whether the element matches the selector specified
     *
     * @param {DOMNode|DOMWindow} element the element that we are querying
     * @param {string} selector the CSS selector
     * @return {boolean} true if the element matches the selector, false if not
     */
    matchesSelector: function matchesSelector(element, selector) {
        var matchesImpl = element.matches || element.webkitMatchesSelector || element.mozMatchesSelector || element.msMatchesSelector || function (s) {
            return matchesSelector_SLOW(element, s);
        };
        return matchesImpl.call(element, selector);
    }
};

export default CSSCore;