'use strict';

exports.__esModule = true;

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

var _events = require('events');

var _ensureArray = require('./ensure-array');

var _ensureArray2 = _interopRequireDefault(_ensureArray);

var _browser = require('./browser');

var _dom = require('./dom');

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

var ie = (0, _browser.getIEVersion)();

var Clusterize = function (_EventEmitter) {
    _inherits(Clusterize, _EventEmitter);

    function Clusterize(options) {
        _classCallCheck(this, Clusterize);

        var _this = _possibleConstructorReturn(this, _EventEmitter.call(this));

        _this.options = {
            rowsInBlock: 50,
            blocksInCluster: 4,
            tag: null,
            emptyClass: '',
            emptyText: '',
            keepParity: true
        };
        _this.state = {
            lastClusterIndex: -1,
            itemHeight: 0,
            blockHeight: 0,
            clusterHeight: 0
        };
        _this.scrollElement = null;
        _this.contentElement = null;
        _this.rows = [];
        _this.cache = {};

        _this.scrollEventListener = function () {
            var debounce = null;

            return function () {
                var isMac = navigator.platform.toLowerCase().indexOf('mac') >= 0;
                if (isMac) {
                    if (_this.contentElement.style.pointerEvents !== 'none') {
                        _this.contentElement.style.pointerEvents = 'none';
                    }

                    if (debounce) {
                        clearTimeout(debounce);
                        debounce = null;
                    }

                    debounce = setTimeout(function () {
                        debounce = null;
                        _this.contentElement.style.pointerEvents = 'auto';
                    }, 50);
                }

                var clusterIndex = _this.getCurrentClusterIndex();
                if (_this.state.lastClusterIndex !== clusterIndex) {
                    _this.changeDOM();
                }
                _this.state.lastClusterIndex = clusterIndex;
            };
        }();

        _this.resizeEventListener = function () {
            var debounce = null;

            return function () {
                if (debounce) {
                    clearTimeout(debounce);
                    debounce = null;
                }
                debounce = setTimeout(function () {
                    var prevItemHeight = _this.state.itemHeight;
                    var current = _this.computeHeight();

                    if (current.itemHeight > 0 && prevItemHeight !== current.itemHeight) {
                        _this.state = _extends({}, _this.state, current);
                        _this.update(_this.rows);
                    }
                }, 100);
            };
        }();

        if (!(_this instanceof Clusterize)) {
            var _ret;

            return _ret = new Clusterize(options), _possibleConstructorReturn(_this, _ret);
        }

        _this.options = Object.keys(_this.options).reduce(function (acc, key) {
            if (options[key] !== undefined) {
                acc[key] = options[key];
            } else {
                acc[key] = _this.options[key];
            }
            return acc;
        }, {});

        _this.scrollElement = options.scrollElement;
        _this.contentElement = options.contentElement;

        // Keep focus on the scrolling content
        if (!_this.contentElement.hasAttribute('tabindex')) {
            _this.contentElement.setAttribute('tabindex', 0);
        }

        if (Array.isArray(options.rows)) {
            _this.rows = options.rows;
        } else {
            _this.rows = [];

            var nodes = _this.contentElement.children;
            var length = nodes.length;
            for (var i = 0; i < length; ++i) {
                var node = nodes[i];
                _this.rows.push(node.outerHTML || '');
            }
        }

        // Remember scroll position
        var scrollTop = _this.scrollElement.scrollTop;

        _this.changeDOM();

        // Restore scroll position
        _this.scrollElement.scrollTop = scrollTop;

        (0, _dom.addEventListener)(_this.scrollElement, 'scroll', _this.scrollEventListener);
        (0, _dom.addEventListener)(window, 'resize', _this.resizeEventListener);
        return _this;
    }

    Clusterize.prototype.destroy = function destroy(clean) {
        (0, _dom.removeEventListener)(this.scrollElement, 'scroll', this.scrollEventListener);
        (0, _dom.removeEventListener)(window, 'resize', this.resizeEventListener);

        var rows = clean ? this.generateEmptyRow() : this.rows();
        this.setContent(rows.join(''));
    };

    Clusterize.prototype.update = function update(rows) {
        this.rows = (0, _ensureArray2['default'])(rows);

        // Remember scroll position
        var scrollTop = this.scrollElement.scrollTop;

        if (this.rows.length * this.state.itemHeight < scrollTop) {
            this.scrollElement.scrollTop = 0;
            this.state.lastClusterIndex = 0;
        }
        this.changeDOM();

        // Restore scroll position
        this.scrollElement.scrollTop = scrollTop;
    };

    Clusterize.prototype.clear = function clear() {
        this.rows = [];
        this.update();
    };

    Clusterize.prototype.append = function append(rows) {
        rows = (0, _ensureArray2['default'])(rows);
        if (!rows.length) {
            return;
        }
        this.rows = this.rows.concat(rows);
        this.changeDOM();
    };

    Clusterize.prototype.prepend = function prepend(rows) {
        rows = (0, _ensureArray2['default'])(rows);
        if (!rows.length) {
            return;
        }
        this.rows = rows.concat(this.rows);
        this.changeDOM();
    };

    Clusterize.prototype.computeHeight = function computeHeight() {
        if (!this.rows.length) {
            return {
                clusterHeight: 0,
                blockHeight: this.state.blockHeight,
                itemHeight: this.state.itemHeight
            };
        } else {
            var nodes = this.contentElement.children;
            var node = nodes[Math.floor(nodes.length / 2)];

            var itemHeight = node.offsetHeight;

            if (this.options.tag === 'tr' && (0, _dom.getElementStyle)(this.contentElement, 'borderCollapse') !== 'collapse') {
                itemHeight += parseInt((0, _dom.getElementStyle)(this.contentElement, 'borderSpacing'), 10) || 0;
            }

            if (this.options.tag !== 'tr') {
                var marginTop = parseInt((0, _dom.getElementStyle)(node, 'marginTop'), 10) || 0;
                var marginBottom = parseInt((0, _dom.getElementStyle)(node, 'marginBottom'), 10) || 0;
                itemHeight += Math.max(marginTop, marginBottom);
            }

            return {
                blockHeight: this.state.itemHeight * this.options.rowsInBlock,
                clusterHeight: this.state.blockHeight * this.options.blocksInCluster,
                itemHeight: itemHeight
            };
        }
    };

    Clusterize.prototype.getCurrentClusterIndex = function getCurrentClusterIndex() {
        var _state = this.state,
            blockHeight = _state.blockHeight,
            clusterHeight = _state.clusterHeight;

        if (!blockHeight || !clusterHeight) {
            return 0;
        }
        return Math.floor(this.scrollElement.scrollTop / (clusterHeight - blockHeight)) || 0;
    };

    Clusterize.prototype.generateEmptyRow = function generateEmptyRow() {
        var _options = this.options,
            tag = _options.tag,
            emptyText = _options.emptyText,
            emptyClass = _options.emptyClass;


        if (!tag || !emptyText) {
            return [];
        }

        var emptyRow = document.createElement(tag);
        emptyRow.className = emptyClass;

        if (tag === 'tr') {
            var td = document.createElement('td');
            td.colSpan = 100;
            td.appendChild(document.createTextNode(emptyText));
            emptyRow.appendChild(td);
        } else {
            emptyRow.appendChild(document.createTextNode(emptyText));
        }

        return [emptyRow.outerHTML];
    };

    Clusterize.prototype.renderExtraTag = function renderExtraTag(className, height) {
        var tag = document.createElement(this.options.tag);
        var prefix = 'infinite-tree-';

        tag.className = [prefix + 'extra-row', prefix + className].join(' ');

        if (height) {
            tag.style.height = height + 'px';
        }

        return tag.outerHTML;
    };

    Clusterize.prototype.changeDOM = function changeDOM() {
        if (!this.state.clusterHeight && this.rows.length > 0) {
            if (ie && ie <= 9 && !this.options.tag) {
                this.options.tag = this.rows[0].match(/<([^>\s/]*)/)[1].toLowerCase();
            }

            if (this.contentElement.children.length <= 1) {
                this.cache.content = this.setContent(this.rows[0] + this.rows[0] + this.rows[0]);
            }

            if (!this.options.tag) {
                this.options.tag = this.contentElement.children[0].tagName.toLowerCase();
            }

            this.state = _extends({}, this.state, this.computeHeight());
        }

        var topOffset = 0;
        var bottomOffset = 0;
        var rows = [];

        if (this.rows.length < this.options.rowsInBlock) {
            rows = this.rows.length > 0 ? this.rows : this.generateEmptyRow();
        } else {
            var rowsInCluster = this.options.rowsInBlock * this.options.blocksInCluster;
            var clusterIndex = this.getCurrentClusterIndex();
            var visibleStart = Math.max((rowsInCluster - this.options.rowsInBlock) * clusterIndex, 0);
            var visibleEnd = visibleStart + rowsInCluster;

            topOffset = Math.max(visibleStart * this.state.itemHeight, 0);
            bottomOffset = Math.max((this.rows.length - visibleEnd) * this.state.itemHeight, 0);

            // Returns a shallow copy of the rows selected from `visibleStart` to `visibleEnd` (`visibleEnd` not included).
            rows = this.rows.slice(visibleStart, visibleEnd);
        }

        var content = rows.join('');
        var contentChanged = this.checkChanges('content', content);
        var topOffsetChanged = this.checkChanges('top', topOffset);
        var bottomOffsetChanged = this.checkChanges('bottom', bottomOffset);

        if (contentChanged || topOffsetChanged) {
            var layout = [];

            if (topOffset > 0) {
                if (this.options.keepParity) {
                    layout.push(this.renderExtraTag('keep-parity'));
                }
                layout.push(this.renderExtraTag('top-space', topOffset));
            }

            layout.push(content);

            if (bottomOffset > 0) {
                layout.push(this.renderExtraTag('bottom-space', bottomOffset));
            }

            this.emit('clusterWillChange');

            this.setContent(layout.join(''));

            this.emit('clusterDidChange');
        } else if (bottomOffsetChanged) {
            this.contentElement.lastChild.style.height = bottomOffset + 'px';
        }
    };

    Clusterize.prototype.setContent = function setContent(content) {
        // For IE 9 and older versions
        if (ie && ie <= 9 && this.options.tag === 'tr') {
            var div = document.createElement('div');
            div.innerHTML = '<table><tbody>' + content + '</tbody></table>';

            var lastChild = this.contentElement.lastChild;
            while (lastChild) {
                this.contentElement.removeChild(lastChild);
                lastChild = this.contentElement.lastChild;
            }

            var rowsNodes = this.getChildNodes(div.firstChild.firstChild);
            while (rowsNodes.length) {
                this.contentElement.appendChild(rowsNodes.shift());
            }
        } else {
            this.contentElement.innerHTML = content;
        }
    };

    Clusterize.prototype.getChildNodes = function getChildNodes(tag) {
        var childNodes = tag.children;
        var nodes = [];
        var length = childNodes.length;

        for (var i = 0; i < length; i++) {
            nodes.push(childNodes[i]);
        }

        return nodes;
    };

    Clusterize.prototype.checkChanges = function checkChanges(type, value) {
        var changed = value !== this.cache[type];
        this.cache[type] = value;
        return changed;
    };

    return Clusterize;
}(_events.EventEmitter);

exports['default'] = Clusterize;