UNPKG

@true-directive/grid

Version:

Angular Data Grid from Yopsilon.

999 lines 129 kB
import * as tslib_1 from "tslib"; /** * Copyright (c) 2018-2019 Aleksey Melnikov, True Directive Company. * @link https://truedirective.com/ * @license MIT */ /* View only. Missed features: - Checkboxes. - Editing. - Selection. */ import { Component, Input, Output, ViewChild, ViewChildren, ContentChildren, ElementRef, QueryList, ChangeDetectorRef, KeyValueDiffers, EventEmitter, Inject } from '@angular/core'; import { Subject, Observable } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { RenderMode, DetectionMode, ColumnType } from '@true-directive/base'; import { CellPosition, GridSettings, RowLayout, SortInfo, SortType, MenuAction, LazyLoadingMode } from '@true-directive/base'; import { PagePipe, RowCalculator } from '@true-directive/base'; import { ScrollerComponent } from './scroller.component'; import { BaseComponent } from './base.component'; import { RowDirective } from './row.directive'; import { GridStateService } from './grid-state.service'; import { InternationalizationService } from './internationalization/internationalization.service'; import { FilterTextComponent } from './filters/datatypes/filter-text.component'; import { FilterDateComponent } from './filters/datatypes/filter-date.component'; import { FilterNumberComponent } from './filters/datatypes/filter-number.component'; import { FilterBooleanComponent } from './filters/datatypes/filter-boolean.component'; import { FilterPopupComponent } from './filters/filter-popup.component'; import { MenuStarterComponent } from './controls/menu-starter.component'; let GridViewComponent = class GridViewComponent extends BaseComponent { constructor(state, intl, elementRef, changeDetector, keyValueDiffers) { super(); this.state = state; this.intl = intl; this.elementRef = elementRef; this.changeDetector = changeDetector; this.keyValueDiffers = keyValueDiffers; this.destroy$ = new Subject(); this._data = null; /** * The maximum grid height, unless explicitly specified. */ this.maxHeight = null; /** * Event that will be triggered when data retrieval options are changed (filters, * sortings) * @param 'dataQuery' [description] * @return [description] */ this.dataQuery = new EventEmitter(); this.queryChanged = new EventEmitter(); this.startProcess = new EventEmitter(); this.endProcess = new EventEmitter(); this.headerContextMenu = new EventEmitter(); this.customCellEvent = new EventEmitter(); this.menuAction = new EventEmitter(); this.uiAction = null; this._initialized = false; // Grid initialized this._viewInitialized = false; // View initialized this._lastUpdateTime = 0; // Last page refresh time // Position and time of last scroll this._lastScroll = { pos: -1, renderedPos: -1, time: 0 }; this.dataUpdating = false; // Updating data in progress // Page processing required this.need_recalc_page = true; this._prevOffset = -1; this._prevLimit = -1; /** * Current appearance css-class */ this._currentAppearance = ''; /** * List of rows to be rendered. */ this._currentRendered = []; this.viewPortLeft = -1; this.viewPortWidth = -1; this.RC = new RowCalculator(this.state); this._inProcess = false; // Locale was changed this.intl.onLocaleChanged.pipe(takeUntil(this.destroy$)).subscribe(l => { if (this._viewInitialized) { this.detectChanges('locale'); } }); // Запрос данных у слушателя события this.state.events.onDataQuery.pipe(takeUntil(this.destroy$)).subscribe(q => { this.dataUpdating = true; this.dataQuery.emit(q); }); // Данные получены - отображаем this.state.events.onDataFetch.pipe(takeUntil(this.destroy$)).subscribe(q => { if (this._viewInitialized) { if (q.resetData) { this.scrollToTop(); } this.renderData(); } }); // Изменены фильтры или сортировка или набор колонок или группировка this.state.events.onQueryChanged.pipe(takeUntil(this.destroy$)).subscribe(q => { if (this._initialized) { this.updateData(); } /* Мы будем запрашивать здесь запрос. Потому что может быть ленивая загрузка хочет его подправить. Нет, его нельзя запросить, потому что у него уже идентификатор, с которым мы запросили и он для нас важен */ this.queryChanged.emit(this.state.getQuery()); }); // Следует отобразить окно фильтра для заданной колонки this.state.events.onFilterShow.pipe(takeUntil(this.destroy$)).subscribe(e => this.showFilter(e)); // Изменение списка суммирований колонки this.state.events.onSummariesChanged.pipe(takeUntil(this.destroy$)).subscribe(v => { this.state.updateSummaries(); this.updateView(); }); this.state.events.onHeaderContextMenu.pipe(takeUntil(this.destroy$)).subscribe(e => { // Это нужно вынести куда-нибудь подальше if (this.state.settings.enableHeaderContextMenu) { const actions = this.state.settings.headerContextMenuActions; const sorted = this.state.dataSource.sortedByField(e.column.fieldName); if (actions.length > 0) { actions.forEach(a => { if (a === MenuAction.SORT_ASC) { a.disabled = sorted && sorted.sortType === SortType.ASC; } if (a === MenuAction.SORT_DESC) { a.disabled = sorted && sorted.sortType === SortType.DESC; } }); this.menuStarter.start(e.event, this.state.settings.headerContextMenuActions, e.column); this.detectChanges(); e.event.preventDefault(); } } this.headerContextMenu.emit(e); }); // Custom cell event this.state.events.onCustomCellEvent.pipe(takeUntil(this.destroy$)).subscribe(e => { this.customCellEvent.emit(e); }); } /** * Grid data source. You can specify an observable object or array. */ set data(ds) { // Unsubscribe from previous source if (this._data !== null && ds === this._data) { return; } if (this._data !== null) { this._dataSubscription.unsubscribe(); this._data = null; } if (ds instanceof Observable) { // Subscribe for new data this._data = ds; this._dataSubscription = this._data .pipe(takeUntil(this.destroy$)) .subscribe(data => { this.state.model = data; this.updateData(); }); } else { // Just array if (this.state.model !== ds) { this.state.model = ds; if (this._initialized) { this.updateData(); } } } } /** * Data source * @return Array of the rows or observable object */ get data() { return this._data !== null ? this._data : this.state.model; } /** * List of the rows to display * @return Array of the rows */ get rows() { return this.state.model; } /** * Resulting list of rows after applying filters and sorting * @return Array of the rows */ get resultRows() { return this.state.dataSource.resultRows; } /** * Current locale name */ get locale() { return this.intl.currentLocaleName; } /** * List of the columns */ set columns(v) { this.state.columns = v; } get columns() { return this.state.columns; } /** * Grid's settings */ set settings(v) { this.state.settings = v; } get settings() { return this.state.settings; } /** * Text search data for all columns */ get searchString() { return this.state.dataSource.searchString; } set searchString(v) { if (this.state.dataSource.searchString !== v) { this.state.dataSource.searchString = v; // A pause is necessary so that when printing quickly, do not perform // too frequent updates. setTimeout(() => { if (this.state.dataSource.searchString === v) { this.updateData(); } }, this.state.settings.searchDelay); } } get displayedRows() { if (this.state.settings.customTemplate) { return this.displayedRows_template; } return this.displayedRowsCenter; } get selection() { return this.state.selection; } _headerParts() { return [this.gridHeader]; } get headerParts() { return this._headerParts(); } _dataParts() { return [this.displayedRowsCenter]; } get dataParts() { return this._dataParts(); } immediateFilter(filter) { this.state.dataSource.searchString = filter; this.updateData(); } /** * List of the rows to render * @return Array of the rows */ get visibleRows() { if (!this.state.dataSource.resultRows) { return []; } if (this._prevOffset !== this.state.pageInfo.offset || this._prevLimit !== this.state.pageInfo.limit || this.need_recalc_page) { // Page refresh this.state.lazyLoader.query(this.state.pageInfo.offset); const toRender = new PagePipe().transform(this.state.dataSource.resultRows, this.state.pageInfo); this._prevOffset = this.state.pageInfo.offset; this._prevLimit = this.state.pageInfo.limit; this._currentRendered = toRender; this.need_recalc_page = false; // Important! this.state.displayedStartIndex = this.state.pageInfo.offset; // Если мы.. Доскроллили до предела... // Надо бы запросить еще данные. } return this._currentRendered; } // Keeping the height of all rows saveRowHeights() { this.displayedRowsCenter.forEach(el => { let hh; if (this.state.IE) { hh = el.elementRef.nativeElement.getBoundingClientRect().height; } else { hh = el.elementRef.nativeElement.clientHeight; } this.RC.setRowHeight(this.state.dataSource.resultRowCount, el.axI + this.state.pageInfo.offset, hh); }); } /** * Page refresh * @param forceChanges Force update even if there was no scrolling * @param overwork The number of rows that will be rendered outside the viewport */ updatePage(log = '', forceChanges = false, overwork = null) { // Это надо перекинуть в состояние if (this.viewPortLeft !== this.scroller.scrollLeft || this.viewPortWidth !== this.scroller.viewPortWidth) { this.viewPortLeft = this.scroller.scrollLeft; this.viewPortWidth = this.scroller.viewPortWidth; forceChanges = true; } if (!forceChanges) { this.saveRowHeights(); } var rc = this.state.dataSource.resultRowCount; if (this.state.st.lazyLoading === LazyLoadingMode.FRAGMENTARY) { rc = this.state.dataSource.totalRowCount; } const pageChanged = this.RC.updateRenderInfo(rc, this.scroller.scrollTop, this.scroller.viewPortHeight, overwork); if (forceChanges || pageChanged) { this.detectChanges('page'); } } /** * Force view update */ updateView() { // First we need to update all view params this.detectChanges('view'); this.state.updateLayouts(); // Then the current page. Because its calculation depends on these parameters this.updatePage('updateView', true); } cellPosition(row, fieldName) { const ri = this.resultRows.indexOf(row); return new CellPosition(row, ri, fieldName); } startSelect(cp, add = false) { this.state.ui.startSelect(cp, add); } proceedToSelect(cp) { this.state.ui.proceedToSelect(cp); } /** * Hide all buttons in column headers */ hideHeaderBtns() { this.headerParts.forEach(p => { p.hideHeaderBtns(); }); } /** * Asynchronous data update. The Observable returns, by subscribing to which * you can find out when this update ended. * Use this method with lazy loading settings. * @param data The data (array of objects). * @return Observable object whose event will occur immediately after processing. */ updateDataAsync(data) { this.data = data; this.startProcess.emit('DataUpdate'); this.elementRef.nativeElement.classList.add('processing'); const subj = this.state.updateDataAsync(); subj.subscribe(e => { this.endProcess.emit('DataUpdate'); }); return subj; } /** * Updating data with external or internal change. */ updateData(async = true) { if (!this._initialized) { async = false; } // The changes are serious. Row heights update. this.RC.clear(); // Through a timeout, so that when the filter is applied, the button does // not have time to turn off before it is shown with an accent color // setTimeout(() => this.hideHeaderBtns()); if (async) { this.startProcess.emit('DataUpdate'); this.elementRef.nativeElement.classList.add('processing'); setTimeout(() => { this.state.updateData(); this.endProcess.emit('DataUpdate'); }, 1); } else { this.state.updateData(async); } } updateSummaries() { this.state.updateSummaries(); this.updateView(); } /** * Received data externally (from the parent component) * @param query The request in response to which the data received. * @param data Data that received in response to a given request. */ fetchData(query, data, totalRowCount = null) { this.state.fetchData(query, data, totalRowCount); } renderData() { setTimeout(() => this.hideHeaderBtns()); this.dataUpdating = false; this.elementRef.nativeElement.classList.remove('processing'); this.need_recalc_page = true; // Sent for further processing (aggregation, grouping) // Refreshing page with forced detectChanges this.updatePage('renderData', true); // After adding data, the width of the client part of the grid may change // due to the appearance of a scrollbar. Therefore, check the width of the grid. if (this.state.settings.columnAutoWidth) { this.checkSize(); } // The height of the data changed, but the scroll position does not. // Therefore, scroll the related parts. this.scroller.scrollParts(); } updatePageByScroll() { this.updatePage('scroll', false); this._lastUpdateTime = Date.now(); } /** * Scrolling data * @param e Scroll event */ gridScroll(e) { let scrollPos = e.target.scrollTop; let scrollPosH = e.target.scrollLeft; const dscrollH = scrollPosH - this.viewPortLeft; const dscroll = scrollPos - this._lastScroll.renderedPos; this._lastScroll.pos = scrollPos; this.RC.currentScrollPos = scrollPos; let needUpdate = false; if (Math.abs(dscroll) > this.RC.currentRH * 2) { // We have a stock of two lines. If the scroll is larger, then update. needUpdate = true; } if (Math.abs(dscrollH) > 1 && this.state.settings.renderMode === RenderMode.VISIBLE) { // When scrolling horizontally only if RenderMode.VISIBLE needUpdate = true; } if (!needUpdate) { // заменим на явное задание margin-top у return; } if (this._inProcess && this.state.iOS) { return; } if (scrollPosH < 0) { return; } if (scrollPos < 0 || scrollPos > (this.scroller.scrollHeight - this.scroller.viewPortHeight)) { return; } this._inProcess = true; const dt = Date.now(); this._lastScroll.renderedPos = scrollPos; this._lastScroll.time = dt; const ms = dt - this._lastUpdateTime; let dms = this.state.IE ? 40 : 10; if (ms < dms && !this.state.safari) { // Delaying const delay = dms * 4; setTimeout(() => { if (this._lastScroll.time === dt) { this.updatePageByScroll(); } }, delay); this._inProcess = false; return; } this.updatePageByScroll(); this._inProcess = false; } /** * Triggered by automatic scrolling during area selection * @param dx How many pixels are scrolled horizontally? */ gridAutoScrollX(dx) { this.gridHeader.autoScrollX(dx); } /** * We need call this method for the changes to take effect. * If dataAffected=true then re-filter and re-sort data. * @param log Reason for detecting changes * @param dataAffected Is the data changed */ detectChanges(log = '', dataAffected = false) { if (dataAffected) { this.updateData(); return; } if (this.state.settings.changeDetectionMode === DetectionMode.MANUAL) { // Otherwise, it will check the changes. And we won’t pull the detector again.. this.changeDetector.detectChanges(); } } displayedRowsByXY(x, y, place = null) { return this.displayedRows; } rowLayouts(rows) { const result = []; const firstRow = rows.first; if (!firstRow) { return result; } let ri = this.state.pageInfo.offset; rows.forEach(el => { const rl = new RowLayout(); rl.index = ri; rl.rowComponent = el; rl.clientRect = el.elementRef.nativeElement.getBoundingClientRect(); result.push(rl); ri++; }); return result; } rowByXY(rows, x, y) { return RowLayout.rowByXY(this.rowLayouts(rows), x, y); } cellByXY(x, y, place = null) { const rows = this.displayedRowsByXY(x, y, place); const r = this.rowByXY(rows, x, y); if (r && r.rowComponent) { return this.state.ui.cellPosition(r.rowComponent.row, r.index, r.rowComponent.cellByXY(x, y)); } return null; } // Перерисовка выделенного refreshSelection(scrollTo) { this.dataParts.forEach(p => p && p.forEach(el => el.setSelection())); if (scrollTo) { this.scrollTo(scrollTo); } } /** * Прокрутить скроллбоксы так, чтобы была видна ячейка, на которой находится * виртуальный фокус */ scrollToFocused() { if (this.state.selection.focusedCell) { this.scrollTo(this.state.selection.focusedCell); } } /** * Прокрутка к указанной ячейке. * Обычно вызываем после обработки нажатия клавиши, чтобы была видна строка * с фокусом. * Также вызывается после клика мышью по ячейке.. Если ячейка видна на экране * только частично - немного скроллим. * @param cellPos Позиция ячейки, к которой прокручиваем грид */ scrollTo(cellPos) { if (!cellPos) { return; } // Будем считать, что нам нужна только ячейка с фокусом // Сохраним высоты строк.. Т.к. они могут быть еще не сохранены, // если не случилось прокрутки this.saveRowHeights(); let ri = cellPos.rowIndex; let fieldName = cellPos.fieldName; // сначала по вертикали let scrollTop = this.scroller.scrollTop; let scrollLeft = this.scroller.scrollLeft; let scrollAreaHeight = this.scroller.viewPortHeight; let scrollAreaWidth = this.scroller.viewPortWidth; let rowTop = this.RC.getRowTop(ri); let rowBottom = rowTop + this.RC.getRowHeight(ri); if (rowTop < scrollTop) { this.scroller.scrollTo(-1, rowTop); } else { if (rowBottom > (scrollTop + scrollAreaHeight)) { this.scroller.scrollTo(-1, rowBottom - scrollAreaHeight); } } // Теперь по горизонтали // Возьмем строку this.displayedRowsCenter.some(row => { if (row.row === cellPos.row) { const hPos = row.cellHorizontalPos(fieldName); if (hPos && hPos.l < scrollLeft) { this.scroller.scrollTo(hPos.l); } if (hPos && hPos.r > scrollLeft + scrollAreaWidth) { this.scroller.scrollTo(hPos.r - scrollAreaWidth); } return true; } return false; }); } scrollToTop() { this.scroller.scrollTo(-1, 0); } toggleClass(v, c) { if (v) { this.elementRef.nativeElement.classList.add(c); } else { this.elementRef.nativeElement.classList.remove(c); } } /** * Проверить, нужно ли отобразить левую или правую область для зафиксированных * колонок * @param xx [description] * @param scrollRect [description] * @param target [description] * @return [description] */ checkParts(xx, scrollRect, target) { // Нечего проверять в этой реализации } /** * Выделение заданной строки * @param r Заданная строка * @return Найдена ли заданная строка в списке отображаемых строк */ locateRow(r) { return this.state.locateRow(r); } /** * Выделение строки по заданному значению ключевого поля * @param keyValue Значение ключевого поля * @param keyField Ключевое поле. Если не задано, то поле берется из settings * @return Найдена ли строка с заданным ключом */ locateByKey(keyValue, keyField = '') { this.state.locateByKey(keyValue, keyField); } /** * Очистка выделения */ clearSelection() { this.state.clearSelection(); } /** * Трек отображаемых записей для более быстрого рендера ангуляром * @param index Индекс строки * @param data Данные строки */ trackRow(index, data) { return data; } /** * При изменении размеров окна вызывается этот метод. * Если размер компонента изменяется какими-то действиями пользователя помимо * изменения размеров окна браузера (вьюпорта), то необходимо вызывать этот * метод. * Этот метод также следует вызывать при drop колонки, т.к. размер центральной * области меняется, если колонка переброшена в левую или правую фиксированную * область. * @param update_page=false [description] * @return [description] */ checkSize(update_page = false) { // Указываем ширину клиентской области. // Если она изменена, то в сеттере будет вызван updateLayouts if (this.state.checkClientWidth(this.scroller.viewPortWidth) || this.state.checkClientHeight(this.scroller.viewPortHeight)) { this.need_recalc_page = true; if (this._initialized) { this.updatePage('checkSize', true); } return true; } if (update_page) { this.updatePage('checkSize2', true); } return false; } // Изменение размера окна windowResize(e) { this.checkSize(true); } /** * Показать кнопку заголовка для колонки по заданному полю * @param fieldName Заданное поле */ showHeaderBtn(fieldName) { this.headerParts.forEach(p => { p.showHeaderBtn(fieldName); }); } /** * Data sorting * @param sortings List of sortings */ sort(sortings, update = true) { this.state.sort(sortings, update); } clearSorting(update = true) { this.state.sort([], update); } /** * Data filtering */ filter(filters, update = true) { this.state.filter(filters, update); } filterToString(filter) { return filter.toString(this.intl, this.state.dataSource.valueFormatter); } filterClosed(result) { if (!result) { this.hideHeaderBtns(); } } setFilter(f) { this.state.setFilter(f); } resetFilter(f) { this.state.resetFilter(f); } getFilterComponentType(filter) { let filterType = FilterTextComponent; if (filter.type === ColumnType.NUMBER) { filterType = FilterNumberComponent; } if (filter.type === ColumnType.DATETIME) { filterType = FilterDateComponent; } if (filter.type === ColumnType.BOOLEAN) { filterType = FilterBooleanComponent; } const col = this.state.columnByFieldName(filter.fieldName); if (!col) { return null; } if (col.filterComponentType !== null) { filterType = col.filterComponentType; } return filterType; } showFilter(e) { if (this.menuStarter) { this.menuStarter.finish(); } let l = e.target.tagName === 'SPAN' ? e.target.parentElement : e.target; if (this.filterPopup.visible) { if (this.filterPopup.filter.fieldName === e.filter.fieldName) { this.filterPopup.closePopup(); return; } else { this.filterPopup.closePopup(); } } this.hideHeaderBtns(); this.showHeaderBtn(e.filter.fieldName); const filterType = this.getFilterComponentType(e.filter); if (filterType !== null) { setTimeout(() => { this.filterPopup.showByTarget(l, e.filter, filterType, this.state.model); }, 10); } } focus() { this.scroller.focus(); } get appearanceClass() { return this.state.sta.class; } /** * Установка класса внешнего вида * @param appearanceClass Класс, который будет применен к компоненту */ setAppearance() { const appearanceClass = this.appearanceClass; if (appearanceClass === this._currentAppearance) { // Без изменений return; } if (this._currentAppearance !== '') { // Убираем добавленный this.elementRef.nativeElement.classList.remove(this._currentAppearance); } this.elementRef.nativeElement.classList.add(appearanceClass); this._currentAppearance = appearanceClass; } menuItemClick(e) { const col = e.target; const action = e.action; if (action === MenuAction.SORT_ASC) { this.sort([new SortInfo(col.fieldName, SortType.ASC)]); return; } if (action === MenuAction.SORT_DESC) { this.sort([new SortInfo(col.fieldName, SortType.DESC)]); return; } if (action === MenuAction.HIDE) { this.state.hideColumn(col); return; } this.menuAction.emit(e); } ngOnInit() { // Отключаем детектор по настройке. // Без отключения всё работает, но много чего мелькает не вовремя.. if (this.state.settings.changeDetectionMode === DetectionMode.MANUAL) { this.changeDetector.detach(); } // Grid appearance this.setAppearance(); if (this.state.iOS || this.state.android) { this.elementRef.nativeElement.classList.add('true-fix-touch'); } if (this.state.IE) { this.elementRef.nativeElement.classList.add('true-fix-ie'); } this._settingsDiffer = this.keyValueDiffers.find(this.state.settings).create(); this._appearanceDiffer = this.keyValueDiffers.find(this.state.settings.appearance).create(); this._settingsDiffer.diff(this.state.settings); this._appearanceDiffer.diff(this.state.settings.appearance); this.updateData(); this._initialized = true; } ngAfterContentInit() { // } ngAfterViewInit() { // Сохраняем ширину this.state.checkClientWidth(this.scroller.viewPortWidth); // На этот момент может быть неизвестна высота грида. Которая нам очень нужна! // Обычно это случается при сложной разметке, когда размер задан в процентах от // родительского элемента. let vh = this.scroller.viewPortHeight; if (vh <= this.state.settings.rowHeight) { // Это условие показывает, что что-то не так. // Мы немного отложим первый рендер данных. // Мы увидим белый экран перед тем как увидим данные. setTimeout(() => this.renderData()); } else { // Будем оптимистами. Возможно, нас спасёт ngDoCheck this.renderData(); } // Запоминаем текущую высоту вьюпорта this.state.checkClientHeight(vh); // Добавляем пассивные слушатели тач-событий this.addTouchListeners(this.gridData.nativeElement); // Следим за размером окна, чтобы дорендерить невидимые ранее данные this.addWindowResizeListener(); this._viewInitialized = true; } doCheckParts() { // Обновляем изменение // Фильтры не виновaты, что у нас отключен детектор изменений if (this.filterPopup && this.filterPopup.visible) { // Поэтому даём знать об изменениях this.filterPopup.changes(); } } ngDoCheck() { this.doCheckParts(); // Сверяем настройки const sChanges = this._settingsDiffer.diff(this.state.settings); const aChanges = this._appearanceDiffer.diff(this.state.settings.appearance); if (sChanges || aChanges) { if (this._viewInitialized) { this.setAppearance(); if (!this.checkSize()) { this.updateView(); } return; } } // Сверяем высоту грида. Это немного затормаживает нас. // Но дает гарантию, что данные будут вовремя отрисованы if (this.state.checkClientHeight(this.scroller.viewPortHeight) && this._viewInitialized) { this.updatePage(); } } ngOnDestroy() { // Отписаться от событий this.destroy$.next(true); this.destroy$.unsubscribe(); super.ngOnDestroy(); } }; tslib_1.__decorate([ Input('data'), tslib_1.__metadata("design:type", Object), tslib_1.__metadata("design:paramtypes", [Object]) ], GridViewComponent.prototype, "data", null); tslib_1.__decorate([ Input('columns'), tslib_1.__metadata("design:type", Array), tslib_1.__metadata("design:paramtypes", [Array]) ], GridViewComponent.prototype, "columns", null); tslib_1.__decorate([ Input('settings'), tslib_1.__metadata("design:type", GridSettings), tslib_1.__metadata("design:paramtypes", [GridSettings]) ], GridViewComponent.prototype, "settings", null); tslib_1.__decorate([ Input('maxHeight'), tslib_1.__metadata("design:type", Number) ], GridViewComponent.prototype, "maxHeight", void 0); tslib_1.__decorate([ Input('searchString'), tslib_1.__metadata("design:type", String), tslib_1.__metadata("design:paramtypes", [String]) ], GridViewComponent.prototype, "searchString", null); tslib_1.__decorate([ Output('dataQuery'), tslib_1.__metadata("design:type", EventEmitter) ], GridViewComponent.prototype, "dataQuery", void 0); tslib_1.__decorate([ Output('queryChanged'), tslib_1.__metadata("design:type", EventEmitter) ], GridViewComponent.prototype, "queryChanged", void 0); tslib_1.__decorate([ Output(), tslib_1.__metadata("design:type", EventEmitter) ], GridViewComponent.prototype, "startProcess", void 0); tslib_1.__decorate([ Output(), tslib_1.__metadata("design:type", EventEmitter) ], GridViewComponent.prototype, "endProcess", void 0); tslib_1.__decorate([ Output(), tslib_1.__metadata("design:type", EventEmitter) ], GridViewComponent.prototype, "headerContextMenu", void 0); tslib_1.__decorate([ Output(), tslib_1.__metadata("design:type", EventEmitter) ], GridViewComponent.prototype, "customCellEvent", void 0); tslib_1.__decorate([ Output(), tslib_1.__metadata("design:type", EventEmitter) ], GridViewComponent.prototype, "menuAction", void 0); tslib_1.__decorate([ ViewChild('menuStarter', { static: true }), tslib_1.__metadata("design:type", MenuStarterComponent) ], GridViewComponent.prototype, "menuStarter", void 0); tslib_1.__decorate([ ViewChild('filterPopup', { static: false }), tslib_1.__metadata("design:type", FilterPopupComponent) ], GridViewComponent.prototype, "filterPopup", void 0); tslib_1.__decorate([ ViewChild('scroller', { static: true }), tslib_1.__metadata("design:type", ScrollerComponent) ], GridViewComponent.prototype, "scroller", void 0); tslib_1.__decorate([ ViewChild('grid', { static: true }), tslib_1.__metadata("design:type", Object) ], GridViewComponent.prototype, "grid", void 0); tslib_1.__decorate([ ViewChild('gridHeader', { static: true }), tslib_1.__metadata("design:type", Object) ], GridViewComponent.prototype, "gridHeader", void 0); tslib_1.__decorate([ ViewChild('gridData', { static: true }), tslib_1.__metadata("design:type", Object) ], GridViewComponent.prototype, "gridData", void 0); tslib_1.__decorate([ ViewChild('dragItem', { static: true }), tslib_1.__metadata("design:type", Object) ], GridViewComponent.prototype, "dragItem", void 0); tslib_1.__decorate([ ContentChildren(RowDirective), tslib_1.__metadata("design:type", QueryList) ], GridViewComponent.prototype, "displayedRows_template", void 0); tslib_1.__decorate([ ViewChildren('displayedRows', { read: RowDirective }), tslib_1.__metadata("design:type", QueryList) ], GridViewComponent.prototype, "displayedRowsCenter", void 0); tslib_1.__decorate([ ViewChild('customTemplate', { static: true }), tslib_1.__metadata("design:type", Object) ], GridViewComponent.prototype, "customTemplate", void 0); GridViewComponent = tslib_1.__decorate([ Component({ selector: 'true-grid-view', template: "<true-menu-starter #menuStarter (itemClick)=\"menuItemClick($event)\"></true-menu-starter>\r\n<!-- Filter popup -->\r\n<true-filter-popup #filterPopup *ngIf=\"state.settings.allowFilter\"\r\n (setFilter)=\"setFilter($event)\"\r\n (resetFilter)=\"resetFilter($event)\"\r\n (closed)=\"filterClosed($event)\">\r\n</true-filter-popup>\r\n<!-- Scroll manager -->\r\n<true-scroller #scroller\r\n tabindex=\"0\"\r\n (autoscrollx)=\"gridAutoScrollX($event)\"\r\n (scroll)=\"gridScroll($event)\">\r\n <!-- \u0417\u0430\u0433\u043E\u043B\u043E\u0432\u043A\u0438 -->\r\n <true-grid-header #gridHeader true-header\r\n [ngClass]=\"state.settings.appearance.headerAreaClass\"\r\n [layout]=\"state.layout\"\r\n [scroller]=\"scroller\">\r\n </true-grid-header>\r\n <div true-data [ngClass]=\"settings.appearance.getDataClass()\">\r\n <table #gridData class=\"true-grid-data\"\r\n [class.true-grid-data_fixed-height]=\"state.settings.fixedRowHeight\"\r\n [style.width]=\"state.layout.dataWidth\">\r\n <colgroup>\r\n <!-- Level columns -->\r\n <col *ngFor=\"let c of state.layout.levelColumns\" [style.width]=\"state.st.levelWidth\" />\r\n <!-- Data columns -->\r\n <col *ngFor=\"let c of state.layout.columns\" [style.width]=\"c.displayedWidthU\" />\r\n </colgroup>\r\n\r\n <tbody *ngIf=\"!state.st.customTemplate\">\r\n <tr *ngFor=\"let r of RC.ghostRows('start'); trackBy: RC.trackGhostRowStart\" [style.height.px]=\"r.H\" style=\"border:0\">\r\n <td [style.height.px]=\"r.H\" style=\"padding:0; border-right: 0;\">&nbsp;</td>\r\n </tr>\r\n\r\n <tr *ngFor=\"let r of visibleRows; let i=index; trackBy: trackRow;\"\r\n true-row\r\n [row]=\"r\"\r\n [true-locale]=\"locale\"\r\n [true-layout]=\"state.layout\"\r\n [true-state]=\"state\"\r\n [true-i]=\"i\"\r\n [style.height.px]=\"state.settings.rowHeight\"\r\n #displayedRows>\r\n </tr>\r\n <tr *ngFor=\"let r of RC.ghostRows('end')\" [style.height.px]=\"r.H\">\r\n <td [style.height.px]=\"r.H\" style=\"padding:0; border-right: 0;\">&nbsp;</td>\r\n </tr>\r\n <tr *ngIf=\"visibleRows.length === 0\"><td style=\"border: 0;\"></td></tr>\r\n </tbody>\r\n <!-- User's template -->\r\n <ng-content *ngIf=\"state.st.customTemplate\" select=\"[true-body]\"></ng-content>\r\n </table>\r\n </div>\r\n <!-- Footer -->\r\n <true-grid-footer #gridFooter true-footer [layout]=\"state.layout\"></true-grid-footer>\r\n</true-scroller>\r\n", providers: [{ provide: 'gridState', useClass: GridStateService }], styles: [":host{display:block;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.true-grid{box-sizing:border-box;height:100%;width:100%}.true-grid-data{box-sizing:border-box;table-layout:fixed;border-spacing:0;border-collapse:collapse;outline:0}.true-grid-data td{overflow-x:hidden;overflow-y:hidden}.true-grid-drop-marker{box-sizing:border-box;visibility:hidden;position:fixed;width:3px;border:1px solid #757779;background-color:rgba(255,255,255,.05);pointer-events:none;z-index:7}.true-grid-data_fixed-height td:not(.true-cell-indent){white-space:nowrap}.true-grid-data_fixed-height td:not(.true-cell-indent):not(.true-cell-checkbox){text-overflow:ellipsis}.true-cell-checkbox.true-check-by-click{cursor:pointer}.true-grid-drag-item{visibility:hidden;position:fixed;overflow-x:hidden;text-overflow:ellipsis;pointer-events:none;z-index:8}::ng-deep .true-grid-btn{cursor:pointer;align-self:stretch;visibility:hidden;display:-webkit-box;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;flex-direction:column;-webkit-box-pack:center;justify-content:center;flex-shrink:0}::ng-deep .true-grid-btn span{display:inline-block}::ng-deep .true-align-right{text-align:right}::ng-deep .true-align-center{text-align:center}::ng-deep .true-align-left{text-align:left!important}"] }), tslib_1.__param(0, Inject('gridState')), tslib_1.__metadata("design:paramtypes", [GridStateService, InternationalizationService, ElementRef, ChangeDetectorRef, KeyValueDiffers]) ], GridViewComponent); export { GridViewComponent }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ3JpZC12aWV3LmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiJuZzovL0B0cnVlLWRpcmVjdGl2ZS9ncmlkLyIsInNvdXJjZXMiOlsic3JjL2dyaWQtdmlldy5jb21wb25lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7O0VBSUU7QUFDRjs7Ozs7R0FLRztBQUNILE9BQU8sRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxTQUFTLEVBQUUsWUFBWSxFQUFFLGVBQWUsRUFDbEUsVUFBVSxFQUFFLFNBQVMsRUFBRSxpQkFBaUIsRUFDeEMsZUFBZSxFQUFFLFlBQVksRUFBRSxNQUFNLEVBQ3RDLE1BQU0sZUFBZSxDQUFDO0FBRTlCLE9BQU8sRUFBRSxPQUFPLEVBQUUsVUFBVSxFQUFnQixNQUFNLE1BQU0sQ0FBQztBQUN6RCxPQUFPLEVBQUUsU0FBUyxFQUFRLE1BQU0sZ0JBQWdCLENBQUM7QUFFakQsT0FBTyxFQUFZLFVBQVUsRUFBRSxhQUFhLEVBQUUsVUFBVSxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDdkYsT0FBTyxFQUFVLFlBQVksRUFBRSxZQUFZLEVBQUUsU0FBUyxFQUM3QyxRQUFRLEVBQUUsUUFBUSxFQUFxQixVQUFVLEVBQUUsZUFBZSxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDMUcsT0FBTyxFQUFFLFFBQVEsRUFBRSxhQUFhLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUUvRCxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUN6RCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFFakQsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBRS9DLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQ3hELE9BQU8sRUFBRSwyQkFBMkIsRUFBRSxNQUFNLHFEQUFxRCxDQUFDO0FBRWxHLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxNQUFNLDJDQUEyQyxDQUFDO0FBQ2hGLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxNQUFNLDJDQUEyQyxDQUFDO0FBQ2hGLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxNQUFNLDZDQUE2QyxDQUFDO0FBQ3BGLE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxNQUFNLDhDQUE4QyxDQUFDO0FBRXRGLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLGtDQUFrQyxDQUFDO0FBRXhFLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLG1DQUFtQyxDQUFDO0FBUXpFLElBQWEsaUJBQWlCLEdBQTlCLE1BQWEsaUJBQWtCLFNBQVEsYUFBYTtJQWsrQmxELFlBQzhCLEtBQXVCLEVBQ3pDLElBQWlDLEVBQ2pDLFVBQXNCLEVBQ3RCLGNBQWlDLEVBQ2pDLGVBQWdDO1FBRTFDLEtBQUssRUFBRSxDQUFDO1FBTm9CLFVBQUssR0FBTCxLQUFLLENBQWtCO1FBQ3pDLFNBQUksR0FBSixJQUFJLENBQTZCO1FBQ2pDLGVBQVUsR0FBVixVQUFVLENBQVk7UUFDdEIsbUJBQWMsR0FBZCxjQUFjLENBQW1CO1FBQ2pDLG9CQUFlLEdBQWYsZUFBZSxDQUFpQjtRQXIrQmxDLGFBQVEsR0FBcUIsSUFBSSxPQUFPLEVBQVcsQ0FBQztRQUV0RCxVQUFLLEdBQXNCLElBQUksQ0FBQztRQTZGeEM7O1dBRUc7UUFFSSxjQUFTLEdBQVcsSUFBSSxDQUFDO1FBdUJoQzs7Ozs7V0FLRztRQUVILGNBQVMsR0FBNEIsSUFBSSxZQUFZLEVBQWEsQ0FBQztRQUduRSxpQkFBWSxHQUE0QixJQUFJLFlBQVksRUFBYSxDQUFDO1FBR3RFLGlCQUFZLEdBQXlCLElBQUksWUFBWSxFQUFVLENBQUM7UUFHaEUsZUFBVSxHQUF5QixJQUFJLFlBQVksRUFBVSxDQUFDO1FBRzlELHNCQUFpQixHQUFzQixJQUFJLFlBQVksRUFBTyxDQUFDO1FBRy9ELG9CQUFlLEdBQXNCLElBQUksWUFBWSxFQUFPLENBQUM7UUFHN0QsZUFBVSxHQUFzQixJQUFJLFlBQVksRUFBTyxDQUFDO1FBbUI5QyxhQUFRLEdBQWEsSUFBSSxDQUFDO1FBQzFCLGlCQUFZLEdBQVksS0FBSyxDQUFDLENBQUMsbUJBQW1CO1FBQ3BELHFCQUFnQixHQUFZLEtBQUssQ0FBQyxDQUFDLG1CQUFtQjtRQUN0RCxvQkFBZSxHQUFXLENBQUMsQ0FBQyxDQUFDLHlCQUF5QjtRQUU5RCxtQ0FBbUM7UUFDaEIsZ0JBQVcsR0FBRyxFQUFFLEdBQUcsRUFBRSxDQUFDLENBQUMsRUFBRSxXQUFXLEVBQUUsQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLENBQUMsRUFBRSxDQUFDO1FBRWhFLGlCQUFZLEdBQUcsS0FBSyxDQUFDLENBQUMsNEJBQTRCO1FBU3pELDJCQUEyQjtRQUNqQixxQkFBZ0IsR0FBRyxJQUFJLENBQUM7UUFFMUIsZ0JBQVcsR0FBRyxDQUFDLENBQUMsQ0FBQztRQUNqQixlQUFVLEdBQUcsQ0FBQyxDQUFDLENBQUM7UUFFeEI7O1dBRUc7UUFDSyx1QkFBa0IsR0FBRyxFQUFFLENBQUM7UUFFaEM7O1dBRUc7UUFDSyxxQkFBZ0IsR0FBVSxFQUFFLENBQUM7UUFROUIsaUJBQVksR0FBRyxDQUFDLENBQUMsQ0FBQztRQUNsQixrQkFBYSxHQUFHLENBQUMsQ0FBQyxDQUFDO1FBRW5CLE9BQUUsR0FBa0IsSUFBSSxhQUFhLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBOE5qRCxlQUFVLEdBQUcsS0FBSyxDQUFDO1FBMmpCekIscUJBQXFCO1FBQ3JCLElBQUksQ0FBQyxJQUFJLENBQUMsZUFBZSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxFQUFFO1lBQ25FLElBQUksSUFBSSxDQUFDLGdCQUFnQixFQUFFO2dCQUN6QixJQUFJLENBQUMsYUFBYSxDQUFDLFFBQVEsQ0FBQyxDQUFDO2FBQzlCO1FBQ0wsQ0FBQyxDQUFDLENBQUM7UUFFSCxvQ0FBb0M7UUFDcEMsSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxFQUFFO1lBQ3pFLElBQUksQ0FBQyxZQUFZLEdBQUcsSUFBSSxDQUFDO1lBQ3pCLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ3pCLENBQUMsQ0FBQyxDQUFDO1FBRUgsK0JBQStCO1FBQy9CLElBQUksQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsRUFBRTtZQUN6RSxJQUFJLElBQUksQ0FBQyxnQkFBZ0IsRUFBRTtnQkFDekIsSUFBSSxDQUFDLENBQUMsU0FBUyxFQUFFO29CQUNmLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQztpQkFDcEI7Z0JBQ0QsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO2FBQ25CO1FBQ0gsQ0FBQyxDQUFDLENBQUM7UUFFSCxvRUFBb0U7UUFDcEUsSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsY0FBYyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxFQUFFO1lBQzVFLElBQUksSUFBSSxDQUFDLFlBQVksRUFBRTtnQkFDckIsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO2FBQ25CO1lBQ0Q7Ozs7aUNBSXFCO1lBQ3JCLElBQUksQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBQztRQUNoRCxDQUFDLENBQUMsQ0FBQztRQUVILHVEQUF1RDtRQUN2RCxJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFFakcsd0NBQXdDO1FBQ3hDLElBQUksQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLGtCQUFrQixDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxFQUFFO1lBQ2hGLElBQUksQ0FBQyxLQUFLLENBQUMsZUFBZSxFQUFFLENBQUM7WUFDN0IsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO1FBQ3BCLENBQUMsQ0FBQyxDQUFDO1FBRUgsSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsbUJBQW1CLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLEVBQUU7WUFDakYseUNBQXlDO1lBQ3pDLElBQUksSUFBSSxDQUFDLEtBQUssQ0FBQyxRQUFRLENBQUMsdUJBQXVCLEVBQUU7Z0JBQy9DLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsUUFBUSxDQUFDLHdCQUF3QixDQUFDO2dCQUM3RCxNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLFVBQVUsQ0FBQyxhQUFhLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsQ0FBQztnQkFDdkUsSUFBSSxPQUFPLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRTtvQkFDdEIsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsRUFBRTt3QkFDbEIsSUFBSSxDQUFDLEtBQUssVUFBVSxDQUFDLFFBQVEsRUFBRTs0QkFDN0IsQ0FBQyxDQUFDLFFBQVEsR0FBRyxNQUFNLElBQUksTUFBTSxDQUFDLFFBQVEsS0FBSyxRQUFRLENBQUMsR0FBRyxDQUFDO3lCQUN6RDt3QkFDRCxJQUFJLENBQUMsS0FBSyxVQUFVLENBQUMsU0FBUyxFQUFFOzRCQUM5QixDQUFDLENBQUMsUUFBUSxHQUFHLE1BQU0sSUFBSSxNQUFNLENBQUMsUUFBUSxLQUFLLFFBQVEsQ0FBQyxJQUFJLENBQUM7eUJBQzFEO29CQUNILENBQUMsQ0FBQyxDQUFDO29CQUNILElBQUksQ0FBQyxXQUFXLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxRQUFRLENBQUMsd0JBQXdCLEVBQUUsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDO29CQUN4RixJQUFJLENBQUMsYUFBYSxFQUFFLENBQUM7b0JBQ3JCLENBQUMsQ0FBQyxLQUFLLENBQUMsY0FBYyxFQUFFLENBQUM7aUJBQzFCO2FBQ0Y7WUFDRCxJQUFJLENBQUMsaUJBQWlCLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ2pDLENBQUMsQ0FBQyxDQUFDO1FBRUgsb0JBQW9CO1FBQ3BCLElBQUksQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLGlCQUFpQixDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxFQUFFO1lBQy9FLElBQUksQ0FBQyxlQUFlLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQy9CLENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQTNpQ0Q7O09BRUc7SUFFSCxJQUFXLElBQUksQ0FBQyxFQUE2QjtRQUMzQyxtQ0FBbUM7UUFDbkMsSUFBSSxJQUFJLENBQUMsS0FBSyxLQUFLLElBQUksSUFBSSxFQUFFLEtBQUssSUFBSSxDQUFDLEtBQUssRUFBRTtZQUM1QyxPQUFPO1NBQ1I7UUFFRCxJQUFJLElBQUksQ0FBQyxLQUFLLEtBQUssSUFBSSxFQUFFO1lBQ3ZCLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxXQUFXLEVBQUUsQ0FBQztZQUNyQyxJQUFJLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQztTQUNuQjtRQUVELElBQUksRUFBRSxZQUFZLFVBQVUsRUFBRTtZQUM1Qix5QkFBeUI7WUFDekIsSUFBSSxDQUFDLEtBQUssR0FBRyxFQUFFLENBQUM7WUFDaEIsSUFBSSxDQUFDLGlCQUFpQixHQUFHLElBQUksQ0FBQyxLQUFLO2lCQUNoQyxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQztpQkFDOUIsU0FBUyxDQUFDLElBQUksQ0FBQyxFQUFFO2dCQUNsQixJQUFJLENBQUMsS0FBSyxDQUFDLEtBQUssR0FBRyxJQUFJLENBQUM7Z0JBQ3hCLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQztZQUNwQixDQUFDLENBQUMsQ0FBQztTQUNKO2FBQU07WUFDTCxhQUFhO1lBQ2IsSUFBSSxJQUFJLENBQUMsS0FBSyxDQUFDLEtBQUssS0FBSyxFQUFFLEVBQUU7Z0JBQzNCLElBQUksQ0FBQyxLQUFLLENBQUMsS0FBSyxHQUFHLEVBQUUsQ0FBQztnQkFDdEIsSUFBSSxJQUFJLENBQUMsWUFBWSxFQUFFO29CQUNyQixJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7aUJBQ25CO2FBQ0Y7U0FDRjtJQUNILENBQUM7SUFFRDs7O09BR0c7SUFDSCxJQUFXLElBQUk7UUFDYixPQUFPLElBQUksQ0FBQyxLQUFLLEtBQUssSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQztJQUM3RCxDQUFDO0lBRUQ7OztPQUdHO0lBQ0gsSUFBVyxJQUFJO1FBQ2IsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQztJQUMxQixDQUFDO0lBRUQ7OztPQUdHO0lBQ0gsSUFBVyxVQUFVO1FBQ25CLE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxVQUFVLENBQUMsVUFBVSxDQUFDO0lBQzFDLENBQUM7SUFFRDs7T0FFRztJQUNILElBQVcsTUFBTTtRQUNmLE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQztJQUNyQyxDQUFDO0lBRUQ7O09BRUc7SUFFSCxJQUFXLE9BQU8sQ0FBQyxDQUFXO1FBQzVCLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxHQUFHLENBQUMsQ0FBQztJQUN6QixDQUFDO0lBRUQsSUFBVyxPQUFPO1FBQ2hCLE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUM7SUFDNUIsQ0FBQztJQUVEOztPQUVHO0lBRUgsSUFBVyxRQUFRLENBQUMsQ0FBZTtRQUNqQyxJQUFJLENBQUMsS0FBSyxDQUFDLFFBQVEsR0FBRyxDQUFDLENBQUM7SUFDMUIsQ0FBQztJQUVELElBQVcsUUFBUTtRQUNqQixPQUFPLElBQUksQ0FBQyxLQUFLLENBQUMsUUFBUSxDQUFDO0lBQzdCLENBQUM7SUFRRDs7T0FFRztJQUVILElBQUksWUFBWTtRQUNkLE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxVQUFVLENBQUMsWUFBWSxDQUFDO0lBQzVDLENBQUM7SUFFRCxJQUFJLFlBQVksQ0FBQyxDQUFTO1FBQ3hCLElBQUksSUFBSSxDQUFDLEtBQUssQ0