(function(window) {
var SLIDE_DEFAULT_OPTS = {
    duration: 350,
    easing: 'ease-in-out'
};

window.slideutils = {
    up: function slideUp(node, opts, callback) {
        opts = Object.assign({}, SLIDE_DEFAULT_OPTS, opts || {});
        var wrap = null;

        if (node._slideActive) {
            if (node._slideTimeout) {
                clearTimeout(node._slideTimeout);
                delete node._slideTimeout;
            }
            wrap = node.parentNode;
        } else {
            var height = node.offsetHeight;
            wrap = ce('div', {});
            setStyle(wrap, {
                height: height,
                transition: 'height '+(opts.duration/1000)+'s '+opts.easing,
                overflow: 'hidden'
            });

            insertAfter(wrap, node);
            wrap.appendChild(node);
        }

        node._slideActive = 1;

        setTimeout(function() {
            setStyle(wrap, 'height', '0px');

            node._slideTimeout = setTimeout(function() {
                hide(node);
                insertAfter(node, wrap);
                re(wrap);

                delete node._slideActive;
                delete node._slideTimeout;

                callback && callback();
            }, opts.duration);
        });
    },

    down: function slideDown(node, opts, callback) {
        opts = Object.assign({}, SLIDE_DEFAULT_OPTS, opts || {});
        var wrap = null;

        if (node._slideActive) {
            if (node._slideTimeout) {
                clearTimeout(node._slideTimeout);
                delete node._slideTimeout;
            }
            wrap = node.parentNode;
        } else {
            wrap = ce('div', {});
            setStyle(wrap, {
                height: 0,
                transition: 'height '+(opts.duration/1000)+'s '+opts.easing,
                overflow: 'hidden'
            });

            insertAfter(wrap, node);
            wrap.appendChild(node);
            show(node);
        };

        node._slideActive = 2;

        setTimeout(function() {
            setStyle(wrap, 'height', node.offsetHeight);
            node._slideTimeout = setTimeout(function() {
                insertAfter(node, wrap);
                re(wrap);

                delete node._slideActive;
                delete node._slideTimeout;

                callback && callback();
            }, opts.duration);
        })
    },

    toggle: function slideToggle(node, opts, callback) {
        opts = opts || {};
        var isVisible = getStyle(node, 'display') !== 'none';
        if (isVisible && node._slideActive !== 1) {
            this.up(node, opts, callback);
            return false;
        } else {
            this.down(node, opts, callback);
            return true;
        }
    },

    custom: function(node, targetHeight, opts, callback) {
        opts = Object.assign({}, SLIDE_DEFAULT_OPTS, opts || {})

        if (node._slideActive) {
            if (node._slideTimeout) {
                clearTimeout(node._slideTimeout)
                delete node._slideTimeout
            }
        }

        setStyle(node, {
            transition: 'height '+(opts.duration/1000)+'s '+opts.easing
        });

        node._slideActive = 2;
        setTimeout(function() {
            setStyle(node, 'height', targetHeight);
            node._slideTimeout = setTimeout(function() {
                delete node._slideActive;
                delete node._slideTimeout;
                callback && callback();
            }, opts.duration)
        });
    }
};
})(window);