/**
 * batch class operation
 * @ignore
 * @author yiminghe@gmail.com
 */
KISSY.add('dom/base/class', function (S, Dom) {

    var slice = [].slice,
        NodeType = Dom.NodeType,
        RE_SPLIT = /[\.\s]\s*\.?/;

    function strToArray(str) {
        str = S.trim(str || '');
        var arr = str.split(RE_SPLIT),
            newArr = [], v,
            l = arr.length,
            i = 0;
        for (; i < l; i++) {
            if (v = arr[i]) {
                newArr.push(v);
            }
        }
        return newArr;
    }

    function batchClassList(method) {
        return function (elem, classNames) {
            var i, l,
                className,
                classList = elem.classList,
                extraArgs = slice.call(arguments, 2);
            for (i = 0, l = classNames.length; i < l; i++) {
                if (className = classNames[i]) {
                    classList[method].apply(classList, [className].concat(extraArgs));
                }
            }
        }
    }

    function batchEls(method) {
        return function (selector, className) {
            var classNames = strToArray(className),
                extraArgs = slice.call(arguments, 2);
            Dom.query(selector).each(function (elem) {
                if (elem.nodeType == NodeType.ELEMENT_NODE) {
                    Dom[method].apply(Dom, [elem, classNames].concat(extraArgs));
                }
            });
        }
    }

    S.mix(Dom,
        /**
         * @override KISSY.DOM
         * @class
         * @singleton
         */
        {
            _hasClass: function (elem, classNames) {
                var i, l, className, classList = elem.classList;
                if (classList.length) {
                    for (i = 0, l = classNames.length; i < l; i++) {
                        className = classNames[i];
                        if (className && !classList.contains(className)) {
                            return false;
                        }
                    }
                    return true;
                }
                return false;
            },

            _addClass: batchClassList('add'),

            _removeClass: batchClassList('remove'),

            _toggleClass: batchClassList('toggle'),

            /**
             * Determine whether any of the matched elements are assigned the given classes.
             * @param {HTMLElement|String|HTMLElement[]} selector matched elements
             * @method
             * @param {String} className One or more class names to search for.
             * multiple class names is separated by space
             * @return {Boolean}
             */
            hasClass: function (selector, className) {
                var elem = Dom.get(selector);
                return elem && elem.nodeType == NodeType.ELEMENT_NODE && Dom._hasClass(elem, strToArray(className));
            },

            /**
             * Replace a class with another class for matched elements.
             * If no oldClassName is present, the newClassName is simply added.
             * @param {HTMLElement|String|HTMLElement[]} selector matched elements
             * @method
             * @param {String} oldClassName One or more class names to be removed from the class attribute of each matched element.
             * multiple class names is separated by space
             * @param {String} newClassName One or more class names to be added to the class attribute of each matched element.
             * multiple class names is separated by space
             */
            replaceClass: function (selector, oldClassName, newClassName) {
                Dom.removeClass(selector, oldClassName);
                Dom.addClass(selector, newClassName);
            },

            /**
             * Adds the specified class(es) to each of the set of matched elements.
             * @method
             * @param {HTMLElement|String|HTMLElement[]} selector matched elements
             * @param {String} className One or more class names to be added to the class attribute of each matched element.
             * multiple class names is separated by space
             */
            addClass: batchEls('_addClass'),

            /**
             * Remove a single class, multiple classes, or all classes from each element in the set of matched elements.
             * @param {HTMLElement|String|HTMLElement[]} selector matched elements
             * @method
             * @param {String} className One or more class names to be removed from the class attribute of each matched element.
             * multiple class names is separated by space
             */
            removeClass: batchEls('_removeClass'),

            /**
             * Add or remove one or more classes from each element in the set of
             * matched elements, depending on either the class's presence or the
             * value of the switch argument.
             * @param {HTMLElement|String|HTMLElement[]} selector matched elements
             * @param {String} className One or more class names to be added to the class attribute of each matched element.
             * multiple class names is separated by space
             * @method
             */
            toggleClass: batchEls('_toggleClass')
            // @param [state] {Boolean} optional boolean to indicate whether class
            // should be added or removed regardless of current state.
            // latest firefox/ie10 does not support
        });

    return Dom;

}, {
    requires: ['./api']
});

/*
 http://jsperf.com/kissy-classlist-vs-classname 17157:14741
 http://jsperf.com/kissy-1-3-vs-jquery-on-dom-class 15721:15223

 NOTES:
 - hasClass/addClass/removeClass 的逻辑和 jQuery 保持一致
 - toggleClass 不支持 value 为 undefined 的情形(jQuery 支持)
 */