/*!
FullCalendar Resource Timeline Plugin v4.3.0
Docs & License: https://fullcalendar.io/scheduler
(c) 2019 Adam Shaw
*/

import { removeElement, Component, memoizeRendering, createElement, htmlToElement, htmlEscape, isArraysEqual, EmitterMixin, memoize, parseFieldSpecs, PositionCache, applyStyleProp, View, createPlugin } from '@fullcalendar/core';
import TimelinePlugin, { TimelineLane, HeaderBodyLayout, TimeAxis, ScrollJoiner, StickyScroller } from '@fullcalendar/timeline';
import ResourceCommonPlugin, { isGroupsEqual, buildResourceFields, buildResourceTextFunc, ResourceApi, ResourceSplitter, buildRowNodes } from '@fullcalendar/resource-common';

/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0

THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.

See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
/* global Reflect, Promise */

var extendStatics = function(d, b) {
    extendStatics = Object.setPrototypeOf ||
        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
    return extendStatics(d, b);
};

function __extends(d, b) {
    extendStatics(d, b);
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
}

var __assign = function() {
    __assign = Object.assign || function __assign(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};

var Row = /** @class */ (function (_super) {
    __extends(Row, _super);
    function Row(context, spreadsheetParent, spreadsheetNextSibling, timeAxisParent, timeAxisNextSibling) {
        var _this = _super.call(this, context) || this;
        _this.isSizeDirty = false;
        spreadsheetParent.insertBefore(_this.spreadsheetTr = document.createElement('tr'), spreadsheetNextSibling);
        timeAxisParent.insertBefore(_this.timeAxisTr = document.createElement('tr'), timeAxisNextSibling);
        return _this;
    }
    Row.prototype.destroy = function () {
        removeElement(this.spreadsheetTr);
        removeElement(this.timeAxisTr);
        _super.prototype.destroy.call(this);
    };
    Row.prototype.updateSize = function (isResize) {
        this.isSizeDirty = false;
    };
    return Row;
}(Component));

function updateExpanderIcon(el, isExpanded) {
    var classList = el.classList;
    if (isExpanded) {
        classList.remove('fc-icon-plus-square');
        classList.add('fc-icon-minus-square');
    }
    else {
        classList.remove('fc-icon-minus-square');
        classList.add('fc-icon-plus-square');
    }
}
function clearExpanderIcon(el) {
    var classList = el.classList;
    classList.remove('fc-icon-minus-square');
    classList.remove('fc-icon-plus-square');
}
function updateTrResourceId(tr, resourceId) {
    tr.setAttribute('data-resource-id', resourceId);
}

var GroupRow = /** @class */ (function (_super) {
    __extends(GroupRow, _super);
    function GroupRow() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this._renderCells = memoizeRendering(_this.renderCells, _this.unrenderCells);
        _this._updateExpanderIcon = memoizeRendering(_this.updateExpanderIcon, null, [_this._renderCells]);
        _this.onExpanderClick = function (ev) {
            var props = _this.props;
            _this.calendar.dispatch({
                type: 'SET_RESOURCE_ENTITY_EXPANDED',
                id: props.id,
                isExpanded: !props.isExpanded
            });
        };
        return _this;
    }
    GroupRow.prototype.render = function (props) {
        this._renderCells(props.group, props.spreadsheetColCnt);
        this._updateExpanderIcon(props.isExpanded);
        this.isSizeDirty = true;
    };
    GroupRow.prototype.destroy = function () {
        _super.prototype.destroy.call(this);
        this._renderCells.unrender(); // should unrender everything else
    };
    GroupRow.prototype.renderCells = function (group, spreadsheetColCnt) {
        var spreadsheetContentEl = this.renderSpreadsheetContent(group);
        this.spreadsheetTr.appendChild(createElement('td', {
            className: 'fc-divider',
            colSpan: spreadsheetColCnt // span across all columns
        }, this.spreadsheetHeightEl = createElement('div', null, spreadsheetContentEl)) // needed by setTrInnerHeight
        );
        this.expanderIconEl = spreadsheetContentEl.querySelector('.fc-icon');
        this.expanderIconEl.parentElement.addEventListener('click', this.onExpanderClick);
        // insert a single cell, with a single empty <div>.
        // there will be no content
        this.timeAxisTr.appendChild(createElement('td', { className: 'fc-divider' }, this.timeAxisHeightEl = document.createElement('div')));
    };
    GroupRow.prototype.unrenderCells = function () {
        this.spreadsheetTr.innerHTML = '';
        this.timeAxisTr.innerHTML = '';
    };
    /*
    Renders the content wrapper element that will be inserted into this row's TD cell.
    */
    GroupRow.prototype.renderSpreadsheetContent = function (group) {
        var text = this.renderCellText(group);
        var contentEl = htmlToElement('<div class="fc-cell-content">' +
            '<span class="fc-expander">' +
            '<span class="fc-icon"></span>' +
            '</span>' +
            '<span class="fc-cell-text">' +
            (text ? htmlEscape(text) : '&nbsp;') +
            '</span>' +
            '</div>');
        var filter = group.spec.render;
        if (typeof filter === 'function') {
            contentEl = filter(contentEl, group.value) || contentEl;
        }
        return contentEl;
    };
    GroupRow.prototype.renderCellText = function (group) {
        var text = group.value || ''; // might be null/undefined if an ad-hoc grouping
        var filter = group.spec.text;
        if (typeof filter === 'function') {
            text = filter(text) || text;
        }
        return text;
    };
    GroupRow.prototype.getHeightEls = function () {
        return [this.spreadsheetHeightEl, this.timeAxisHeightEl];
    };
    GroupRow.prototype.updateExpanderIcon = function (isExpanded) {
        updateExpanderIcon(this.expanderIconEl, isExpanded);
    };
    return GroupRow;
}(Row));
GroupRow.addEqualityFuncs({
    group: isGroupsEqual // HACK for ResourceTimelineView::renderRows
});

var SpreadsheetRow = /** @class */ (function (_super) {
    __extends(SpreadsheetRow, _super);
    function SpreadsheetRow(context, tr) {
        var _this = _super.call(this, context) || this;
        _this._renderRow = memoizeRendering(_this.renderRow, _this.unrenderRow);
        _this._updateTrResourceId = memoizeRendering(updateTrResourceId, null, [_this._renderRow]);
        _this._updateExpanderIcon = memoizeRendering(_this.updateExpanderIcon, null, [_this._renderRow]);
        _this.onExpanderClick = function (ev) {
            var props = _this.props;
            _this.calendar.dispatch({
                type: 'SET_RESOURCE_ENTITY_EXPANDED',
                id: props.id,
                isExpanded: !props.isExpanded
            });
        };
        _this.tr = tr;
        return _this;
    }
    SpreadsheetRow.prototype.render = function (props) {
        this._renderRow(props.resource, props.rowSpans, props.depth, props.colSpecs);
        this._updateTrResourceId(this.tr, props.resource.id); // TODO: only use public ID?
        this._updateExpanderIcon(props.hasChildren, props.isExpanded);
    };
    SpreadsheetRow.prototype.destroy = function () {
        _super.prototype.destroy.call(this);
        this._renderRow.unrender(); // should unrender everything else
    };
    SpreadsheetRow.prototype.renderRow = function (resource, rowSpans, depth, colSpecs) {
        var _a = this, tr = _a.tr, theme = _a.theme, calendar = _a.calendar, view = _a.view;
        var resourceFields = buildResourceFields(resource); // slightly inefficient. already done up the call stack
        var mainTd;
        for (var i = 0; i < colSpecs.length; i++) {
            var colSpec = colSpecs[i];
            var rowSpan = rowSpans[i];
            if (rowSpan === 0) { // not responsible for group-based rows. VRowGroup is
                continue;
            }
            else if (rowSpan == null) {
                rowSpan = 1;
            }
            var text = void 0;
            if (colSpec.field) {
                text = resourceFields[colSpec.field];
            }
            else {
                text = buildResourceTextFunc(colSpec.text, calendar)(resource);
            }
            var contentEl = htmlToElement('<div class="fc-cell-content">' +
                (colSpec.isMain ? renderIconHtml(depth) : '') +
                '<span class="fc-cell-text">' +
                (text ? htmlEscape(text) : '&nbsp;') +
                '</span>' +
                '</div>');
            if (typeof colSpec.render === 'function') { // a filter function for the element
                contentEl = colSpec.render(new ResourceApi(calendar, resource), contentEl) || contentEl;
            }
            if (rowSpan > 1) {
                contentEl.classList.add('fc-sticky');
            }
            var td = createElement('td', {
                className: theme.getClass('widgetContent'),
                rowspan: rowSpan
            }, contentEl);
            // the first cell of the row needs to have an inner div for setTrInnerHeight
            if (colSpec.isMain) {
                td.appendChild(this.heightEl = createElement('div', null, td.childNodes) // inner wrap
                );
                mainTd = td;
            }
            tr.appendChild(td);
        }
        this.expanderIconEl = tr.querySelector('.fc-expander-space .fc-icon');
        // wait until very end
        view.publiclyTrigger('resourceRender', [
            {
                resource: new ResourceApi(calendar, resource),
                el: mainTd,
                view: view
            }
        ]);
    };
    SpreadsheetRow.prototype.unrenderRow = function () {
        this.tr.innerHTML = '';
    };
    SpreadsheetRow.prototype.updateExpanderIcon = function (hasChildren, isExpanded) {
        var expanderIconEl = this.expanderIconEl;
        var expanderEl = expanderIconEl.parentElement;
        if (expanderIconEl &&
            expanderEl // why would this be null?? was the case in IE11
        ) {
            if (hasChildren) {
                expanderEl.addEventListener('click', this.onExpanderClick);
                expanderEl.classList.add('fc-expander');
                updateExpanderIcon(expanderIconEl, isExpanded);
            }
            else {
                expanderEl.removeEventListener('click', this.onExpanderClick);
                expanderEl.classList.remove('fc-expander');
                clearExpanderIcon(expanderIconEl);
            }
        }
    };
    return SpreadsheetRow;
}(Component));
/*
Renders the HTML responsible for the subrow expander area,
as well as the space before it (used to align expanders of similar depths)
*/
function renderIconHtml(depth) {
    var html = '';
    for (var i = 0; i < depth; i++) {
        html += '<span class="fc-icon"></span>';
    }
    html +=
        '<span class="fc-expander-space">' +
            '<span class="fc-icon"></span>' +
            '</span>';
    return html;
}

var ResourceRow = /** @class */ (function (_super) {
    __extends(ResourceRow, _super);
    function ResourceRow(context, a, b, c, d, timeAxis) {
        var _this = _super.call(this, context, a, b, c, d) || this;
        _this._updateTrResourceId = memoizeRendering(updateTrResourceId);
        _this.spreadsheetRow = new SpreadsheetRow(context, _this.spreadsheetTr);
        _this.timeAxisTr.appendChild(createElement('td', { className: _this.theme.getClass('widgetContent') }, _this.innerContainerEl = document.createElement('div')));
        _this.lane = new TimelineLane(context, _this.innerContainerEl, _this.innerContainerEl, timeAxis);
        return _this;
    }
    ResourceRow.prototype.destroy = function () {
        this.spreadsheetRow.destroy();
        this.lane.destroy();
        _super.prototype.destroy.call(this);
    };
    ResourceRow.prototype.render = function (props) {
        // spreadsheetRow handles calling updateTrResourceId for spreadsheetTr
        this.spreadsheetRow.receiveProps({
            colSpecs: props.colSpecs,
            id: props.id,
            rowSpans: props.rowSpans,
            depth: props.depth,
            isExpanded: props.isExpanded,
            hasChildren: props.hasChildren,
            resource: props.resource
        });
        this._updateTrResourceId(this.timeAxisTr, props.resource.id);
        this.lane.receiveProps({
            dateProfile: props.dateProfile,
            nextDayThreshold: props.nextDayThreshold,
            businessHours: props.businessHours,
            eventStore: props.eventStore,
            eventUiBases: props.eventUiBases,
            dateSelection: props.dateSelection,
            eventSelection: props.eventSelection,
            eventDrag: props.eventDrag,
            eventResize: props.eventResize
        });
        this.isSizeDirty = true;
    };
    ResourceRow.prototype.updateSize = function (isResize) {
        _super.prototype.updateSize.call(this, isResize);
        this.lane.updateSize(isResize);
    };
    ResourceRow.prototype.getHeightEls = function () {
        return [this.spreadsheetRow.heightEl, this.innerContainerEl];
    };
    return ResourceRow;
}(Row));
ResourceRow.addEqualityFuncs({
    rowSpans: isArraysEqual // HACK for isSizeDirty, ResourceTimelineView::renderRows
});

var COL_MIN_WIDTH = 30;
var SpreadsheetHeader = /** @class */ (function (_super) {
    __extends(SpreadsheetHeader, _super);
    function SpreadsheetHeader(context, parentEl) {
        var _this = _super.call(this, context) || this;
        _this.resizables = [];
        _this.colWidths = [];
        _this.emitter = new EmitterMixin();
        parentEl.appendChild(_this.tableEl = createElement('table', {
            className: _this.theme.getClass('tableGrid')
        }));
        return _this;
    }
    SpreadsheetHeader.prototype.destroy = function () {
        for (var _i = 0, _a = this.resizables; _i < _a.length; _i++) {
            var resizable = _a[_i];
            resizable.destroy();
        }
        removeElement(this.tableEl);
        _super.prototype.destroy.call(this);
    };
    SpreadsheetHeader.prototype.render = function (props) {
        var theme = this.theme;
        var colSpecs = props.colSpecs;
        var html = '<colgroup>' + props.colTags + '</colgroup>' +
            '<tbody>';
        if (props.superHeaderText) {
            html +=
                '<tr class="fc-super">' +
                    '<th class="' + theme.getClass('widgetHeader') + '" colspan="' + colSpecs.length + '">' +
                    '<div class="fc-cell-content">' +
                    '<span class="fc-cell-text">' +
                    htmlEscape(props.superHeaderText) +
                    '</span>' +
                    '</div>' +
                    '</th>' +
                    '</tr>';
        }
        html += '<tr>';
        for (var i = 0; i < colSpecs.length; i++) {
            var o = colSpecs[i];
            var isLast = i === (colSpecs.length - 1);
            html +=
                "<th class=\"" + theme.getClass('widgetHeader') + "\">" +
                    '<div>' +
                    '<div class="fc-cell-content">' +
                    (o.isMain ?
                        '<span class="fc-expander-space">' +
                            '<span class="fc-icon"></span>' +
                            '</span>' :
                        '') +
                    '<span class="fc-cell-text">' +
                    htmlEscape(o.labelText || '') + // what about normalizing this value ahead of time?
                    '</span>' +
                    '</div>' +
                    (!isLast ? '<div class="fc-col-resizer"></div>' : '') +
                    '</div>' +
                    '</th>';
        }
        html += '</tr>';
        html += '</tbody>';
        this.tableEl.innerHTML = html;
        this.thEls = Array.prototype.slice.call(this.tableEl.querySelectorAll('th'));
        this.colEls = Array.prototype.slice.call(this.tableEl.querySelectorAll('col'));
        this.resizerEls = Array.prototype.slice.call(this.tableEl.querySelectorAll('.fc-col-resizer'));
        this.initColResizing();
    };
    SpreadsheetHeader.prototype.initColResizing = function () {
        var _this = this;
        var ElementDraggingImpl = this.calendar.pluginSystem.hooks.elementDraggingImpl;
        if (ElementDraggingImpl) {
            this.resizables = this.resizerEls.map(function (handleEl, colIndex) {
                var dragging = new ElementDraggingImpl(handleEl);
                var startWidth;
                dragging.emitter.on('dragstart', function () {
                    startWidth = _this.colWidths[colIndex];
                    if (typeof startWidth !== 'number') {
                        startWidth = _this.thEls[colIndex].getBoundingClientRect().width;
                    }
                });
                dragging.emitter.on('dragmove', function (pev) {
                    _this.colWidths[colIndex] = Math.max(startWidth + pev.deltaX * (_this.isRtl ? -1 : 1), COL_MIN_WIDTH);
                    _this.emitter.trigger('colwidthchange', _this.colWidths);
                });
                dragging.setAutoScrollEnabled(false); // because gets weird with auto-scrolling time area
                return dragging;
            });
        }
    };
    return SpreadsheetHeader;
}(Component));

var Spreadsheet = /** @class */ (function (_super) {
    __extends(Spreadsheet, _super);
    function Spreadsheet(context, headParentEl, bodyParentEl) {
        var _this = _super.call(this, context) || this;
        _this._renderCells = memoizeRendering(_this.renderCells, _this.unrenderCells);
        _this.layout = new HeaderBodyLayout(headParentEl, bodyParentEl, 'clipped-scroll');
        var headerEnhancedScroller = _this.layout.headerScroller.enhancedScroll;
        var bodyEnhancedScroller = _this.layout.bodyScroller.enhancedScroll;
        _this.header = new SpreadsheetHeader(context, headerEnhancedScroller.canvas.contentEl);
        _this.header.emitter.on('colwidthchange', function (colWidths) {
            _this.applyColWidths(colWidths);
        });
        bodyEnhancedScroller.canvas.contentEl
            .appendChild(_this.bodyContainerEl = createElement('div', { className: 'fc-rows' }, '<table>' +
            '<colgroup />' +
            '<tbody />' +
            '</table>'));
        _this.bodyColGroup = _this.bodyContainerEl.querySelector('colgroup');
        _this.bodyTbody = _this.bodyContainerEl.querySelector('tbody');
        return _this;
    }
    Spreadsheet.prototype.destroy = function () {
        this.header.destroy();
        this.layout.destroy();
        this._renderCells.unrender();
        _super.prototype.destroy.call(this);
    };
    Spreadsheet.prototype.render = function (props) {
        this._renderCells(props.superHeaderText, props.colSpecs);
    };
    Spreadsheet.prototype.renderCells = function (superHeaderText, colSpecs) {
        var colTags = this.renderColTags(colSpecs);
        this.header.receiveProps({
            superHeaderText: superHeaderText,
            colSpecs: colSpecs,
            colTags: colTags
        });
        this.bodyColGroup.innerHTML = colTags;
        this.bodyColEls = Array.prototype.slice.call(this.bodyColGroup.querySelectorAll('col'));
        this.applyColWidths(colSpecs.map(function (colSpec) { return colSpec.width; }));
    };
    Spreadsheet.prototype.unrenderCells = function () {
        this.bodyColGroup.innerHTML = '';
    };
    Spreadsheet.prototype.renderColTags = function (colSpecs) {
        var html = '';
        for (var _i = 0, colSpecs_1 = colSpecs; _i < colSpecs_1.length; _i++) {
            var o = colSpecs_1[_i];
            if (o.isMain) {
                html += '<col class="fc-main-col"/>';
            }
            else {
                html += '<col/>';
            }
        }
        return html;
    };
    Spreadsheet.prototype.updateSize = function (isResize, totalHeight, isAuto) {
        this.layout.setHeight(totalHeight, isAuto);
    };
    Spreadsheet.prototype.applyColWidths = function (colWidths) {
        var _this = this;
        colWidths.forEach(function (colWidth, colIndex) {
            var headEl = _this.header.colEls[colIndex]; // bad to access child
            var bodyEl = _this.bodyColEls[colIndex];
            var styleVal;
            if (typeof colWidth === 'number') {
                styleVal = colWidth + 'px';
            }
            else if (typeof colWidth == null) {
                styleVal = '';
            }
            headEl.style.width = bodyEl.style.width = styleVal;
        });
    };
    return Spreadsheet;
}(Component));

var MIN_RESOURCE_AREA_WIDTH = 30; // definitely bigger than scrollbars
var ResourceTimelineView = /** @class */ (function (_super) {
    __extends(ResourceTimelineView, _super);
    function ResourceTimelineView(context, viewSpec, dateProfileGenerator, parentEl) {
        var _this = _super.call(this, context, viewSpec, dateProfileGenerator, parentEl) || this;
        _this.isStickyScrollDirty = false;
        _this.rowNodes = [];
        _this.rowComponents = [];
        _this.rowComponentsById = {};
        _this.resourceAreaWidthDraggings = [];
        _this.splitter = new ResourceSplitter(); // doesn't let it do businessHours tho
        _this.hasResourceBusinessHours = memoize(hasResourceBusinessHours);
        _this.buildRowNodes = memoize(buildRowNodes);
        _this.hasNesting = memoize(hasNesting);
        _this._updateHasNesting = memoizeRendering(_this.updateHasNesting);
        var allColSpecs = _this.opt('resourceColumns') || [];
        var labelText = _this.opt('resourceLabelText'); // TODO: view.override
        var defaultLabelText = 'Resources'; // TODO: view.defaults
        var superHeaderText = null;
        if (!allColSpecs.length) {
            allColSpecs.push({
                labelText: labelText || defaultLabelText,
                text: buildResourceTextFunc(_this.opt('resourceText'), _this.calendar)
            });
        }
        else {
            superHeaderText = labelText;
        }
        var plainColSpecs = [];
        var groupColSpecs = [];
        var groupSpecs = [];
        var isVGrouping = false;
        var isHGrouping = false;
        for (var _i = 0, allColSpecs_1 = allColSpecs; _i < allColSpecs_1.length; _i++) {
            var colSpec = allColSpecs_1[_i];
            if (colSpec.group) {
                groupColSpecs.push(colSpec);
            }
            else {
                plainColSpecs.push(colSpec);
            }
        }
        plainColSpecs[0].isMain = true;
        if (groupColSpecs.length) {
            groupSpecs = groupColSpecs;
            isVGrouping = true;
        }
        else {
            var hGroupField = _this.opt('resourceGroupField');
            if (hGroupField) {
                isHGrouping = true;
                groupSpecs.push({
                    field: hGroupField,
                    text: _this.opt('resourceGroupText'),
                    render: _this.opt('resourceGroupRender')
                });
            }
        }
        var allOrderSpecs = parseFieldSpecs(_this.opt('resourceOrder'));
        var plainOrderSpecs = [];
        for (var _a = 0, allOrderSpecs_1 = allOrderSpecs; _a < allOrderSpecs_1.length; _a++) {
            var orderSpec = allOrderSpecs_1[_a];
            var isGroup = false;
            for (var _b = 0, groupSpecs_1 = groupSpecs; _b < groupSpecs_1.length; _b++) {
                var groupSpec = groupSpecs_1[_b];
                if (groupSpec.field === orderSpec.field) {
                    groupSpec.order = orderSpec.order; // -1, 0, 1
                    isGroup = true;
                    break;
                }
            }
            if (!isGroup) {
                plainOrderSpecs.push(orderSpec);
            }
        }
        _this.superHeaderText = superHeaderText;
        _this.isVGrouping = isVGrouping;
        _this.isHGrouping = isHGrouping;
        _this.groupSpecs = groupSpecs;
        _this.colSpecs = groupColSpecs.concat(plainColSpecs);
        _this.orderSpecs = plainOrderSpecs;
        // START RENDERING...
        _this.el.classList.add('fc-timeline');
        if (_this.opt('eventOverlap') === false) {
            _this.el.classList.add('fc-no-overlap');
        }
        _this.el.innerHTML = _this.renderSkeletonHtml();
        _this.resourceAreaHeadEl = _this.el.querySelector('thead .fc-resource-area');
        _this.setResourceAreaWidth(_this.opt('resourceAreaWidth'));
        _this.initResourceAreaWidthDragging();
        _this.miscHeight = _this.el.getBoundingClientRect().height;
        _this.spreadsheet = new Spreadsheet(_this.context, _this.resourceAreaHeadEl, _this.el.querySelector('tbody .fc-resource-area'));
        _this.timeAxis = new TimeAxis(_this.context, _this.el.querySelector('thead .fc-time-area'), _this.el.querySelector('tbody .fc-time-area'));
        var timeAxisRowContainer = createElement('div', { className: 'fc-rows' }, '<table><tbody /></table>');
        _this.timeAxis.layout.bodyScroller.enhancedScroll.canvas.contentEl.appendChild(timeAxisRowContainer);
        _this.timeAxisTbody = timeAxisRowContainer.querySelector('tbody');
        _this.lane = new TimelineLane(_this.context, null, _this.timeAxis.layout.bodyScroller.enhancedScroll.canvas.bgEl, _this.timeAxis);
        _this.bodyScrollJoiner = new ScrollJoiner('vertical', [
            _this.spreadsheet.layout.bodyScroller,
            _this.timeAxis.layout.bodyScroller
        ]);
        // after scrolljoiner
        _this.spreadsheetBodyStickyScroller = new StickyScroller(_this.spreadsheet.layout.bodyScroller.enhancedScroll, _this.isRtl, true // isVertical
        );
        _this.spreadsheet.receiveProps({
            superHeaderText: _this.superHeaderText,
            colSpecs: _this.colSpecs
        });
        // Component...
        context.calendar.registerInteractiveComponent(_this, {
            el: _this.timeAxis.slats.el
        });
        return _this;
    }
    ResourceTimelineView.prototype.renderSkeletonHtml = function () {
        var theme = this.theme;
        return "<table class=\"" + theme.getClass('tableGrid') + "\"> <thead class=\"fc-head\"> <tr> <td class=\"fc-resource-area " + theme.getClass('widgetHeader') + "\"></td> <td class=\"fc-divider fc-col-resizer " + theme.getClass('widgetHeader') + "\"></td> <td class=\"fc-time-area " + theme.getClass('widgetHeader') + "\"></td> </tr> </thead> <tbody class=\"fc-body\"> <tr> <td class=\"fc-resource-area " + theme.getClass('widgetContent') + "\"></td> <td class=\"fc-divider fc-col-resizer " + theme.getClass('widgetHeader') + "\"></td> <td class=\"fc-time-area " + theme.getClass('widgetContent') + "\"></td> </tr> </tbody> </table>";
    };
    ResourceTimelineView.prototype.render = function (props) {
        _super.prototype.render.call(this, props);
        var splitProps = this.splitter.splitProps(props);
        var hasResourceBusinessHours = this.hasResourceBusinessHours(props.resourceStore);
        this.timeAxis.receiveProps({
            dateProfile: props.dateProfile
        });
        // for all-resource bg events / selections / business-hours
        this.lane.receiveProps(__assign({}, splitProps[''], { dateProfile: props.dateProfile, nextDayThreshold: this.nextDayThreshold, businessHours: hasResourceBusinessHours ? null : props.businessHours }));
        var newRowNodes = this.buildRowNodes(props.resourceStore, this.groupSpecs, this.orderSpecs, this.isVGrouping, props.resourceEntityExpansions, this.opt('resourcesInitiallyExpanded'));
        this._updateHasNesting(this.hasNesting(newRowNodes));
        this.diffRows(newRowNodes);
        this.renderRows(props.dateProfile, hasResourceBusinessHours ? props.businessHours : null, // CONFUSING, comment
        splitProps);
    };
    ResourceTimelineView.prototype.updateHasNesting = function (isNesting) {
        var classList = this.el.classList;
        if (isNesting) {
            classList.remove('fc-flat');
        }
        else {
            classList.add('fc-flat');
        }
    };
    ResourceTimelineView.prototype.diffRows = function (newNodes) {
        var oldNodes = this.rowNodes;
        var oldLen = oldNodes.length;
        var oldIndexHash = {}; // id -> index
        var oldI = 0;
        var newI = 0;
        for (oldI = 0; oldI < oldLen; oldI++) {
            oldIndexHash[oldNodes[oldI].id] = oldI;
        }
        // iterate new nodes
        for (oldI = 0, newI = 0; newI < newNodes.length; newI++) {
            var newNode = newNodes[newI];
            var oldIFound = oldIndexHash[newNode.id];
            if (oldIFound != null && oldIFound >= oldI) {
                this.removeRows(newI, oldIFound - oldI, oldNodes); // won't do anything if same index
                oldI = oldIFound + 1;
            }
            else {
                this.addRow(newI, newNode);
            }
        }
        // old rows that weren't found need to be removed
        this.removeRows(newI, oldLen - oldI, oldNodes); // won't do anything if same index
        this.rowNodes = newNodes;
    };
    /*
    rowComponents is the in-progress result
    */
    ResourceTimelineView.prototype.addRow = function (index, rowNode) {
        var _a = this, rowComponents = _a.rowComponents, rowComponentsById = _a.rowComponentsById;
        var nextComponent = rowComponents[index];
        var newComponent = this.buildChildComponent(rowNode, this.spreadsheet.bodyTbody, nextComponent ? nextComponent.spreadsheetTr : null, this.timeAxisTbody, nextComponent ? nextComponent.timeAxisTr : null);
        rowComponents.splice(index, 0, newComponent);
        rowComponentsById[rowNode.id] = newComponent;
    };
    ResourceTimelineView.prototype.removeRows = function (startIndex, len, oldRowNodes) {
        if (len) {
            var _a = this, rowComponents = _a.rowComponents, rowComponentsById = _a.rowComponentsById;
            for (var i = 0; i < len; i++) {
                var rowComponent = rowComponents[startIndex + i];
                rowComponent.destroy();
                delete rowComponentsById[oldRowNodes[i].id];
            }
            rowComponents.splice(startIndex, len);
        }
    };
    ResourceTimelineView.prototype.buildChildComponent = function (node, spreadsheetTbody, spreadsheetNext, timeAxisTbody, timeAxisNext) {
        if (node.group) {
            return new GroupRow(this.context, spreadsheetTbody, spreadsheetNext, timeAxisTbody, timeAxisNext);
        }
        else if (node.resource) {
            return new ResourceRow(this.context, spreadsheetTbody, spreadsheetNext, timeAxisTbody, timeAxisNext, this.timeAxis);
        }
    };
    ResourceTimelineView.prototype.renderRows = function (dateProfile, fallbackBusinessHours, splitProps) {
        var _a = this, rowNodes = _a.rowNodes, rowComponents = _a.rowComponents;
        for (var i = 0; i < rowNodes.length; i++) {
            var rowNode = rowNodes[i];
            var rowComponent = rowComponents[i];
            if (rowNode.group) {
                rowComponent.receiveProps({
                    spreadsheetColCnt: this.colSpecs.length,
                    id: rowNode.id,
                    isExpanded: rowNode.isExpanded,
                    group: rowNode.group
                });
            }
            else {
                var resource = rowNode.resource;
                rowComponent.receiveProps(__assign({}, splitProps[resource.id], { dateProfile: dateProfile, nextDayThreshold: this.nextDayThreshold, businessHours: resource.businessHours || fallbackBusinessHours, colSpecs: this.colSpecs, id: rowNode.id, rowSpans: rowNode.rowSpans, depth: rowNode.depth, isExpanded: rowNode.isExpanded, hasChildren: rowNode.hasChildren, resource: rowNode.resource }));
            }
        }
    };
    ResourceTimelineView.prototype.updateSize = function (isResize, viewHeight, isAuto) {
        // FYI: this ordering is really important
        var calendar = this.calendar;
        var isBaseSizing = isResize || calendar.isViewUpdated || calendar.isDatesUpdated || calendar.isEventsUpdated;
        if (isBaseSizing) {
            this.syncHeadHeights();
            this.timeAxis.updateSize(isResize, viewHeight - this.miscHeight, isAuto);
            this.spreadsheet.updateSize(isResize, viewHeight - this.miscHeight, isAuto);
        }
        var rowSizingCnt = this.updateRowSizes(isResize);
        this.lane.updateSize(isResize); // is efficient. uses flags
        if (isBaseSizing || rowSizingCnt) {
            this.bodyScrollJoiner.update();
            this.timeAxis.layout.scrollJoiner.update(); // hack
            this.rowPositions = new PositionCache(this.timeAxis.slats.el, this.rowComponents.map(function (rowComponent) {
                return rowComponent.timeAxisTr;
            }), false, // isHorizontal
            true // isVertical
            );
            this.rowPositions.build();
            this.isStickyScrollDirty = true;
        }
    };
    ResourceTimelineView.prototype.syncHeadHeights = function () {
        var spreadsheetHeadEl = this.spreadsheet.header.tableEl;
        var timeAxisHeadEl = this.timeAxis.header.tableEl;
        spreadsheetHeadEl.style.height = '';
        timeAxisHeadEl.style.height = '';
        var max = Math.max(spreadsheetHeadEl.getBoundingClientRect().height, timeAxisHeadEl.getBoundingClientRect().height);
        spreadsheetHeadEl.style.height =
            timeAxisHeadEl.style.height = max + 'px';
    };
    ResourceTimelineView.prototype.updateRowSizes = function (isResize) {
        var dirtyRowComponents = this.rowComponents;
        if (!isResize) {
            dirtyRowComponents = dirtyRowComponents.filter(function (rowComponent) {
                return rowComponent.isSizeDirty;
            });
        }
        var elArrays = dirtyRowComponents.map(function (rowComponent) {
            return rowComponent.getHeightEls();
        });
        // reset to natural heights
        for (var _i = 0, elArrays_1 = elArrays; _i < elArrays_1.length; _i++) {
            var elArray = elArrays_1[_i];
            for (var _a = 0, elArray_1 = elArray; _a < elArray_1.length; _a++) {
                var el = elArray_1[_a];
                el.style.height = '';
            }
        }
        // let rows update their contents' heights
        for (var _b = 0, dirtyRowComponents_1 = dirtyRowComponents; _b < dirtyRowComponents_1.length; _b++) {
            var rowComponent = dirtyRowComponents_1[_b];
            rowComponent.updateSize(isResize); // will reset isSizeDirty
        }
        var maxHeights = elArrays.map(function (elArray) {
            var maxHeight = null;
            for (var _i = 0, elArray_2 = elArray; _i < elArray_2.length; _i++) {
                var el = elArray_2[_i];
                var height = el.getBoundingClientRect().height;
                if (maxHeight === null || height > maxHeight) {
                    maxHeight = height;
                }
            }
            return maxHeight;
        });
        for (var i = 0; i < elArrays.length; i++) {
            for (var _c = 0, _d = elArrays[i]; _c < _d.length; _c++) {
                var el = _d[_c];
                el.style.height = maxHeights[i] + 'px';
            }
        }
        return dirtyRowComponents.length;
    };
    ResourceTimelineView.prototype.destroy = function () {
        for (var _i = 0, _a = this.rowComponents; _i < _a.length; _i++) {
            var rowComponent = _a[_i];
            rowComponent.destroy();
        }
        this.rowNodes = [];
        this.rowComponents = [];
        this.spreadsheet.destroy();
        this.timeAxis.destroy();
        for (var _b = 0, _c = this.resourceAreaWidthDraggings; _b < _c.length; _b++) {
            var resourceAreaWidthDragging = _c[_b];
            resourceAreaWidthDragging.destroy();
        }
        this.spreadsheetBodyStickyScroller.destroy();
        _super.prototype.destroy.call(this);
        this.calendar.unregisterInteractiveComponent(this);
    };
    // Now Indicator
    // ------------------------------------------------------------------------------------------
    ResourceTimelineView.prototype.getNowIndicatorUnit = function (dateProfile) {
        return this.timeAxis.getNowIndicatorUnit(dateProfile);
    };
    ResourceTimelineView.prototype.renderNowIndicator = function (date) {
        this.timeAxis.renderNowIndicator(date);
    };
    ResourceTimelineView.prototype.unrenderNowIndicator = function () {
        this.timeAxis.unrenderNowIndicator();
    };
    // Scrolling
    // ------------------------------------------------------------------------------------------------------------------
    // this is useful for scrolling prev/next dates while resource is scrolled down
    ResourceTimelineView.prototype.queryScroll = function () {
        var scroll = _super.prototype.queryScroll.call(this);
        if (this.props.resourceStore) {
            __assign(scroll, this.queryResourceScroll());
        }
        return scroll;
    };
    ResourceTimelineView.prototype.applyScroll = function (scroll, isResize) {
        _super.prototype.applyScroll.call(this, scroll, isResize);
        if (this.props.resourceStore) {
            this.applyResourceScroll(scroll);
        }
        // avoid updating stickyscroll too often
        if (isResize || this.isStickyScrollDirty) {
            this.isStickyScrollDirty = false;
            this.spreadsheetBodyStickyScroller.updateSize();
            this.timeAxis.updateStickyScrollers();
        }
    };
    ResourceTimelineView.prototype.computeDateScroll = function (duration) {
        return this.timeAxis.computeDateScroll(duration);
    };
    ResourceTimelineView.prototype.queryDateScroll = function () {
        return this.timeAxis.queryDateScroll();
    };
    ResourceTimelineView.prototype.applyDateScroll = function (scroll) {
        this.timeAxis.applyDateScroll(scroll);
    };
    ResourceTimelineView.prototype.queryResourceScroll = function () {
        var _a = this, rowComponents = _a.rowComponents, rowNodes = _a.rowNodes;
        var scroll = {};
        var scrollerTop = this.timeAxis.layout.bodyScroller.el.getBoundingClientRect().top; // fixed position
        for (var i = 0; i < rowComponents.length; i++) {
            var rowComponent = rowComponents[i];
            var rowNode = rowNodes[i];
            var el = rowComponent.timeAxisTr;
            var elBottom = el.getBoundingClientRect().bottom; // fixed position
            if (elBottom > scrollerTop) {
                scroll.rowId = rowNode.id;
                scroll.bottom = elBottom - scrollerTop;
                break;
            }
        }
        // TODO: what about left scroll state for spreadsheet area?
        return scroll;
    };
    ResourceTimelineView.prototype.applyResourceScroll = function (scroll) {
        var rowId = scroll.forcedRowId || scroll.rowId;
        if (rowId) {
            var rowComponent = this.rowComponentsById[rowId];
            if (rowComponent) {
                var el = rowComponent.timeAxisTr;
                if (el) {
                    var innerTop = this.timeAxis.layout.bodyScroller.enhancedScroll.canvas.el.getBoundingClientRect().top;
                    var rowRect = el.getBoundingClientRect();
                    var scrollTop = (scroll.forcedRowId ?
                        rowRect.top : // just use top edge
                        rowRect.bottom - scroll.bottom) - // pixels from bottom edge
                        innerTop;
                    this.timeAxis.layout.bodyScroller.enhancedScroll.setScrollTop(scrollTop);
                    this.spreadsheet.layout.bodyScroller.enhancedScroll.setScrollTop(scrollTop);
                }
            }
        }
    };
    // TODO: scrollToResource
    // Hit System
    // ------------------------------------------------------------------------------------------
    ResourceTimelineView.prototype.buildPositionCaches = function () {
        this.timeAxis.slats.updateSize();
        this.rowPositions.build();
    };
    ResourceTimelineView.prototype.queryHit = function (positionLeft, positionTop) {
        var rowPositions = this.rowPositions;
        var slats = this.timeAxis.slats;
        var rowIndex = rowPositions.topToIndex(positionTop);
        if (rowIndex != null) {
            var resource = this.rowNodes[rowIndex].resource;
            if (resource) { // not a group
                var slatHit = slats.positionToHit(positionLeft);
                if (slatHit) {
                    return {
                        component: this,
                        dateSpan: {
                            range: slatHit.dateSpan.range,
                            allDay: slatHit.dateSpan.allDay,
                            resourceId: resource.id
                        },
                        rect: {
                            left: slatHit.left,
                            right: slatHit.right,
                            top: rowPositions.tops[rowIndex],
                            bottom: rowPositions.bottoms[rowIndex]
                        },
                        dayEl: slatHit.dayEl,
                        layer: 0
                    };
                }
            }
        }
    };
    // Resource Area
    // ------------------------------------------------------------------------------------------------------------------
    ResourceTimelineView.prototype.setResourceAreaWidth = function (widthVal) {
        this.resourceAreaWidth = widthVal;
        applyStyleProp(this.resourceAreaHeadEl, 'width', widthVal || '');
    };
    ResourceTimelineView.prototype.initResourceAreaWidthDragging = function () {
        var _this = this;
        var resourceAreaDividerEls = Array.prototype.slice.call(this.el.querySelectorAll('.fc-col-resizer'));
        var ElementDraggingImpl = this.calendar.pluginSystem.hooks.elementDraggingImpl;
        if (ElementDraggingImpl) {
            this.resourceAreaWidthDraggings = resourceAreaDividerEls.map(function (el) {
                var dragging = new ElementDraggingImpl(el);
                var dragStartWidth;
                var viewWidth;
                dragging.emitter.on('dragstart', function () {
                    dragStartWidth = _this.resourceAreaWidth;
                    if (typeof dragStartWidth !== 'number') {
                        dragStartWidth = _this.resourceAreaHeadEl.getBoundingClientRect().width;
                    }
                    viewWidth = _this.el.getBoundingClientRect().width;
                });
                dragging.emitter.on('dragmove', function (pev) {
                    var newWidth = dragStartWidth + pev.deltaX * (_this.isRtl ? -1 : 1);
                    newWidth = Math.max(newWidth, MIN_RESOURCE_AREA_WIDTH);
                    newWidth = Math.min(newWidth, viewWidth - MIN_RESOURCE_AREA_WIDTH);
                    _this.setResourceAreaWidth(newWidth);
                });
                dragging.setAutoScrollEnabled(false); // because gets weird with auto-scrolling time area
                return dragging;
            });
        }
    };
    ResourceTimelineView.needsResourceData = true; // for ResourceViewProps
    return ResourceTimelineView;
}(View));
function hasResourceBusinessHours(resourceStore) {
    for (var resourceId in resourceStore) {
        var resource = resourceStore[resourceId];
        if (resource.businessHours) {
            return true;
        }
    }
    return false;
}
function hasNesting(nodes) {
    for (var _i = 0, nodes_1 = nodes; _i < nodes_1.length; _i++) {
        var node = nodes_1[_i];
        if (node.group) {
            return true;
        }
        else if (node.resource) {
            if (node.hasChildren) {
                return true;
            }
        }
    }
    return false;
}

var main = createPlugin({
    deps: [ResourceCommonPlugin, TimelinePlugin],
    defaultView: 'resourceTimelineDay',
    views: {
        resourceTimeline: {
            class: ResourceTimelineView,
            resourceAreaWidth: '30%',
            resourcesInitiallyExpanded: true,
            eventResizableFromStart: true // TODO: not DRY with this same setting in the main timeline config
        },
        resourceTimelineDay: {
            type: 'resourceTimeline',
            duration: { days: 1 }
        },
        resourceTimelineWeek: {
            type: 'resourceTimeline',
            duration: { weeks: 1 }
        },
        resourceTimelineMonth: {
            type: 'resourceTimeline',
            duration: { months: 1 }
        },
        resourceTimelineYear: {
            type: 'resourceTimeline',
            duration: { years: 1 }
        }
    }
});

export default main;
export { ResourceTimelineView };
