@microsoft/windows-admin-center-sdk
Version:
Microsoft - Windows Admin Center Shell
581 lines • 87.4 kB
JavaScript
import { __decorate, __metadata } from "tslib";
import { Injectable } from '@angular/core';
import { Yield } from '@microsoft/windows-admin-center-sdk/core/base/decorators/yield.decorator';
import { KeyCode } from '@microsoft/windows-admin-center-sdk/core/data/accessibility-manager';
import { Dom } from '@microsoft/windows-admin-center-sdk/core/dom/dom';
import * as i0 from "@angular/core";
/**
* Service for maintaining dropdown state.
* Only one dropdown can be opened at a time.
* This does not support nested dropdowns.
*/
export class DropdownService {
constructor() {
this.onDocumentClickBinding = this.onDocumentClick.bind(this);
this.onDocumentKeydownBinding = this.onDocumentKeydown.bind(this);
this.onWindowScrollBinding = this.onWindowScroll.bind(this);
this.onWindowBlurBinding = this.onWindowBlur.bind(this);
/**
* This saves the records for the last opened dropdowns
*/
this.dropdownList = [];
// It defines the height for the dropdown with the specific window height.
// This will be enabled only if <sme-dropdown [calculateHeight]="true">
this.heightCalculationObject = {
small: {
// when the window height is smaller than 300px
// The smallest margin top and bottom
max: 300,
margin: 5,
wholeHeight: true
},
medium: {
// when then window height is within [301,500]
// The smallest margin top and bottom
min: 300,
max: 500,
margin: 10
},
large: {
// when then window height is greater than 500px
// The smallest margin top and bottom
min: 500,
margin: 20
}
};
}
/**
* opens a dropdown and sets it to active
* @param dropdown The dropdown to open
* @param calculateHeight Whether to do height calculation
* @param event The event
*/
open(dropdown, calculateHeight, displayToSide) {
return new Promise((resolve) => {
let isCurrentDropdownChildFromActiveDropdown = false;
if (!this.activeDropdown) {
// if there is not an active dropdown, add handler for auto closing the dropdown
window.document.addEventListener('click', this.onDocumentClickBinding);
window.document.addEventListener('keydown', this.onDocumentKeydownBinding);
window.addEventListener('scroll', this.onWindowScrollBinding, true);
window.addEventListener('blur', this.onWindowBlurBinding);
}
else {
// if the passed in dropdown is not the active dropdown
// if current dropdown is the child of the active dropdown
// we should keep both dropdown opening
if (this.activeDropdown !== dropdown) {
const dropdownNode = dropdown.contentElement.nativeElement;
const activeDropdownChildren = this.activeDropdown.contentElement.nativeElement.querySelectorAll('.sme-layout-dropdown');
if (activeDropdownChildren.length > 0) {
for (let i = 0; i < activeDropdownChildren.length; i++) {
if (activeDropdownChildren[i].isSameNode(dropdownNode)) {
isCurrentDropdownChildFromActiveDropdown = true;
}
}
}
if (isCurrentDropdownChildFromActiveDropdown) {
this.activeDropdown.isOpen = true;
}
else {
this.activeDropdown.isOpen = false;
}
}
else {
// otherwise close the active dropdown
this.activeDropdown.isOpen = false;
}
}
// if not from the same parent, close the previous active element's directs
// then open current active dropdown
// if need to close, then need to close first and then open
this.closeNestedOnOpen(dropdown, this.activeDropdown, isCurrentDropdownChildFromActiveDropdown).then(() => {
// set the new dropdown as the open dropdown
this.activeDropdown = dropdown;
this.activeDropdown.isOpen = true;
// push new expanded dropdown component
if (this.activeDropdown && !this.dropdownList.includes(this.activeDropdown)) {
this.dropdownList.push(this.activeDropdown);
}
this.updatePosition(dropdown, calculateHeight, displayToSide);
resolve();
});
});
}
/**
* updates the position of a dropdown so that it remains on screen
* @param dropdown The dropdown to update
* @param calculateHeight Defines whether to calculate height of the dropdown based on window size.
*/
updatePosition(dropdown, calculateHeight, displayToSide) {
const contentElement = dropdown.contentElement.nativeElement;
// reset dropdown overflow
dropdown.overflowX = false;
dropdown.overflowY = false;
// Reset the content height and width then the content.scrollHeight and scrollWidth can get the actual content dimensions
contentElement.style.height = '';
contentElement.style.width = '';
const toggleRect = dropdown.toggleElement.nativeElement.getBoundingClientRect();
// we don't allow the dropdown to exceed the height or width of the window, so account for that as well
const rectWidth = contentElement.scrollWidth;
const rectHeight = contentElement.scrollHeight;
// Set the left position of the dropdown
this.dropdownSetPositionLeft(toggleRect, dropdown, rectWidth, displayToSide);
// Set the right position of the dropdown
this.dropdownSetPositionTop(toggleRect, dropdown, rectHeight, calculateHeight, displayToSide);
if (!dropdown.dropdownApplyFormFieldOffsetWidth) {
// min-content would be a good choice but won't be supported by ie or edge
dropdown.contentElement.nativeElement.style.maxWidth = 'inherit';
}
}
/**
* Marks a dropdown as closed, if the dropdown is the active dropdown,
* the active dropdown is reset and event handlers are removed.
* @param dropdown The dropdown to close
*/
close(dropdown, event) {
// mark the dropdown as closed
dropdown.isOpen = false;
// return if the passed in dropdown is not the active dropdown
if (this.activeDropdown !== dropdown) {
return;
}
// reset active dropdown to null
this.activeDropdown = null;
if (event) {
this.closeNested(dropdown, event);
}
// stop listening to document events if no active dropdown
if (!this.activeDropdown) {
window.document.removeEventListener('click', this.onDocumentClickBinding);
window.document.removeEventListener('keydown', this.onDocumentKeydownBinding);
window.removeEventListener('scroll', this.onWindowScrollBinding, true);
window.removeEventListener('blur', this.onWindowBlurBinding);
}
}
/**
* Handles document clicks while a dropdown is open
*/
onDocumentClick(event) {
// stop if there is no dropdown
if (!this.activeDropdown) {
return;
}
// stop if the clicked element is also the toggle element for the active dropdown
if (this.activeDropdown.toggleElement && this.activeDropdown.toggleElement.nativeElement === event.target) {
return;
}
// check if the click occured an element that is inside the dropdown content
if (this.activeDropdown.contentElement &&
this.activeDropdown.contentElement.nativeElement.contains(event.target)) {
// stop if the element is an element that handles input, return
if (/input|textarea/i.test(event.target.tagName)) {
return;
}
// stop if the target element has the 'sme-dropdown-interaction-enabled' class on one of its ancestors up to the content element
let element = event.target;
while (!this.activeDropdown.contentElement.nativeElement.isSameNode(element)) {
if (element.classList.contains('sme-dropdown-interaction-enabled')) {
return;
}
element = element.parentElement;
}
}
// otherwise, close the dropdown
this.close(this.activeDropdown, event);
}
/**
* Handles document keydowns while a dropdown is open
*/
onDocumentKeydown(event) {
// if the esc key is pressed, close the popup and focus on the toggle element
if (event.keyCode === KeyCode.Escape) {
if (this.activeDropdown && this.activeDropdown.toggleElement) {
const docElement = this.activeDropdown.toggleElement.nativeElement;
const nextElement = Dom.getFirstFocusableDescendent(docElement) || Dom.getNextFocusableElement(docElement);
if (nextElement) {
nextElement.focus();
}
}
this.close(this.activeDropdown);
return;
}
}
/**
* Handles document scrolling while a dropdown is open
*/
onWindowScroll(event) {
// stop if there is no dropdown
if (!this.activeDropdown) {
return;
}
// check if the scroll occured on an element that is inside the dropdown content
if (this.activeDropdown.contentElement &&
this.activeDropdown.contentElement.nativeElement.contains(event.target)) {
return;
}
// otherwise, close dropdown(s)
if (this.dropdownList && this.dropdownList.length) {
this.closeNestedAllExpandedDropdowns(this.dropdownList);
// close a dropdown that appears inside a context menu
if (this.activeDropdown.isContextMenu) {
this.close(this.activeDropdown);
}
}
else {
this.close(this.activeDropdown);
}
}
/**
* Handles window blurs while a dropdown is open
*/
onWindowBlur(event) {
// close the dropdown if we leave the window (this also works to prevent to close dropdowns in iframes)
this.close(this.activeDropdown, event);
}
/**
* Handles the position calculation for the left of the dropdown container
* Decides whether to place the dropdown left, right, or on top of the toggle button
*/
dropdownSetPositionLeft(toggleRect, dropdown, rectWidth, displayToSide) {
// modify the left position of the rect to account for any old offset
let rectLeft = toggleRect.left;
// get the viewport width
const windowWidth = window.innerWidth;
// If displayToSide attribute is set, display dropdown to side of toggleRect, else place it above or below
if (!!displayToSide) {
const spaceLeft = toggleRect.left;
const spaceRight = windowWidth - toggleRect.right;
// Always try to put dropdown to the direction that has more space. First try to put on the right, if not then put left
if (spaceRight >= DropdownService.minSpaceForDropdown || rectWidth < spaceRight || spaceRight > spaceLeft) {
if (rectWidth > spaceRight) {
rectWidth = spaceRight;
dropdown.overflowX = true;
}
// show right of control
rectLeft = toggleRect.right;
}
else if (rectWidth < spaceLeft) {
// show left of control
rectLeft = toggleRect.left - rectWidth;
}
else {
// if rect is bigger than space to the left and toggle rectangle combined
if (rectWidth > spaceLeft + toggleRect.width) {
rectWidth = spaceLeft + toggleRect.width;
dropdown.overflowX = true;
}
// show on top of control
rectLeft = toggleRect.right - rectWidth;
}
}
else {
// Cap width and height to the window dimensions and set appropriate overflow
if (rectWidth > windowWidth) {
rectWidth = windowWidth;
dropdown.overflowX = true;
}
if (rectWidth < toggleRect.width) {
rectWidth = toggleRect.width;
}
if (rectLeft + rectWidth > windowWidth) {
rectLeft = windowWidth - rectWidth;
}
}
rectLeft = Math.max(0, rectLeft);
dropdown.left = rectLeft;
dropdown.contentElement.nativeElement.style.left = rectLeft + 'px';
if (!dropdown.autoWidth) {
dropdown.contentElement.nativeElement.style.width = rectWidth + 'px';
}
}
/**
* Handles the position calculation for the top of the dropdown container
* Decides whether to place the dropdown above or below the toggle button
*/
dropdownSetPositionTop(toggleRect, dropdown, rectHeight, calculateHeight, displayToSide) {
// need to remember the original value for small height window rendering
const originalRectHeight = rectHeight;
// modify the top position of the rect to account for any old offset
let rectTop = toggleRect.bottom;
// get the viewport height
const windowHeight = window.innerHeight;
const spaceAbove = toggleRect.top;
const spaceBelow = windowHeight - toggleRect.bottom;
// Always try to put dropdown to the direction that has more space.
let shouldDisplayingAbove = false;
// First try to put on the bottom, if not then put on above
if (spaceBelow >= DropdownService.minSpaceForDropdown || rectHeight < spaceBelow || spaceBelow > spaceAbove) {
if (rectHeight > spaceBelow) {
rectHeight = spaceBelow;
dropdown.overflowY = true;
}
// show below the control.
rectTop = displayToSide === true ? toggleRect.top : toggleRect.bottom;
}
else {
// if rect is bigger than space above
if (rectHeight > spaceAbove) {
rectHeight = spaceAbove;
dropdown.overflowY = true;
}
// show above the control.
rectTop = displayToSide === true ? toggleRect.bottom - rectHeight : toggleRect.top - rectHeight;
shouldDisplayingAbove = true;
}
rectTop = Math.max(0, rectTop);
dropdown.top = rectTop;
if (calculateHeight) {
const heightTopObject = this.dropdownCalculateHeightTop(rectHeight, rectTop, shouldDisplayingAbove, spaceAbove, spaceBelow, originalRectHeight);
dropdown.contentElement.nativeElement.style.height = heightTopObject.height + 'px';
dropdown.contentElement.nativeElement.style.top = heightTopObject.top + 'px';
}
else {
dropdown.contentElement.nativeElement.style.height = rectHeight + 'px';
dropdown.contentElement.nativeElement.style.top = rectTop + 'px';
}
}
/**
* It handles the height calculation for the dropdown container
* It calculates based on the heightCalculationObject
* First try to put the dropdown container to decide whether a scroll bar is needed
* Then apply the height from calculation
*/
dropdownCalculateHeightTop(rectHeight, rectTop, shouldDisplayingAbove, toggleTop, toggleBottom, originalRectHeight) {
const windowHeight = window.innerHeight;
let actualTop = rectTop;
let actualHeight = rectHeight;
const toggleHeight = shouldDisplayingAbove ? toggleTop : toggleBottom;
const windowHeightMargin = this.getDropdownMargin(windowHeight);
const shouldUpdateHeight = this.shouldUpdateHeight(rectHeight, toggleHeight, windowHeightMargin);
if (shouldUpdateHeight) {
// handles special cases where dropdown body is too big, use full height mode
if (originalRectHeight >= toggleHeight * DropdownService.shouldUseFullHeightDropdownRatio) {
actualTop = windowHeightMargin;
// if original body exceeded the whole window height, use window height
// otherwise use the original height
if (originalRectHeight >= windowHeight - 2 * windowHeightMargin) {
actualHeight = windowHeight - 2 * windowHeightMargin;
}
else {
actualHeight = originalRectHeight;
}
}
else {
if (windowHeight <= this.heightCalculationObject.small.max) {
if (originalRectHeight > windowHeight - 2 * windowHeightMargin) {
// whole height
actualHeight = windowHeight - 2 * windowHeightMargin;
actualTop = windowHeightMargin;
}
else if (rectHeight > toggleHeight - windowHeightMargin) {
// display from top, will cover part of the input box
actualHeight = rectHeight - windowHeightMargin;
actualTop = windowHeightMargin;
}
}
else {
// can be only displayed as below or above in bigger screen
actualHeight = rectHeight - windowHeightMargin;
}
}
if (shouldDisplayingAbove) {
if (toggleTop > actualHeight) {
actualTop = toggleTop - actualHeight;
}
}
}
return {
top: actualTop,
height: actualHeight
};
}
/**
* Calculates whether should update the height pixels for the dropdown
*/
shouldUpdateHeight(rectHeight, toggleHeight, windowHeightMargin) {
let res = false;
if (toggleHeight - rectHeight < windowHeightMargin) {
res = true;
}
return res;
}
/**
* Get dropdown margin base on the calculation object
*/
getDropdownMargin(windowHeight) {
let windowHeightMargin = 0;
if (windowHeight <= this.heightCalculationObject.small.max) {
windowHeightMargin = this.heightCalculationObject.small.margin;
}
else if (windowHeight >= this.heightCalculationObject.medium.min && windowHeight <= this.heightCalculationObject.medium.max) {
windowHeightMargin = this.heightCalculationObject.medium.margin;
}
else if (windowHeight >= this.heightCalculationObject.large.min) {
windowHeightMargin = this.heightCalculationObject.large.margin;
}
return windowHeightMargin;
}
/**
* Deal with nested close for dropdown
* @param dropdown the active dropdown
* @param event contains the current event target
*/
closeNested(dropdown, event) {
if (dropdown && event) {
let eventTarget = event.target;
// assign active dropdown
if (this.dropdownList && this.dropdownList.length) {
if (eventTarget) {
if (eventTarget.classList) {
if (!eventTarget.classList.contains('.sme-layout-dropdown')) {
eventTarget = Dom.getAncestor(eventTarget, element => element.classList.contains('sme-layout-dropdown'));
}
}
else {
// this handles when the event target is Window object that does't contains event.target
eventTarget = null;
}
}
else if (eventTarget.hasOwnProperty('querySelector') && eventTarget.querySelector('sme-dropdown')) {
eventTarget = eventTarget.querySelector('sme-dropdown');
}
let activeDropdown = null;
if (eventTarget) {
for (let index = 0; index < this.dropdownList.length; index++) {
const d = this.dropdownList[index];
if (d.contentElement.nativeElement === eventTarget) {
activeDropdown = d;
break;
}
}
}
this.activeDropdown = activeDropdown;
}
// close drop-downs
if (eventTarget) {
const dropdownsInEventRegion = eventTarget.querySelectorAll('sme-dropdown .sme-dropdown-toggle-container');
if (dropdownsInEventRegion && dropdownsInEventRegion.length) {
// close all children dropdowns
// we can only refer to UI level from querySelectorAll, not the component.
dropdownsInEventRegion.forEach((d) => {
if (d) {
d.dispatchEvent(new Event('closeEvent'));
}
});
}
else {
// close all expanded dropdowns
this.closeNestedAllExpandedDropdowns(this.dropdownList);
this.activeDropdown = null;
}
}
else {
// close all expanded dropdowns
// close child first
this.closeNestedAllExpandedDropdowns(this.dropdownList);
this.activeDropdown = null;
}
}
else {
return;
}
}
/**
* It handles closing all the expanded dropdowns
*/
closeNestedAllExpandedDropdowns(dropdownList) {
if (dropdownList && dropdownList.length) {
for (let i = dropdownList.length - 1; i >= 0; i--) {
const element = dropdownList[i];
if (element && element.contentElement.nativeElement) {
const node = element.contentElement.nativeElement;
// only close the ones displaying
if (!node.classList.contains('sme-layout-hidden')) {
this.closeNestedHelper(element);
}
}
}
}
}
closeNestedHelper(dropdown) {
if (dropdown) {
dropdown.isOpen = false;
}
}
/**
* It handles when opening a dropdown when other dropdown(s) is/are open
*/
closeNestedOnOpen(currentDropdown, previousDropdown, isCurrentDropdownChildFromActiveDropdown) {
return new Promise((resolve) => {
if (isCurrentDropdownChildFromActiveDropdown || !this.dropdownList || !this.dropdownList.length || !previousDropdown) {
resolve();
}
else {
const isFromSameParent = this.isFromSameParent(currentDropdown, previousDropdown);
const currentDropdownNode = currentDropdown.contentElement.nativeElement;
const previousDropdownNode = previousDropdown.contentElement.nativeElement;
const d = Dom;
// find if there are intersection on parent node. And close till that level.
// if no intersection, close the previous active dropdown one by one.
const previousActiveDropdowns = this.dropdownList;
if (isFromSameParent) {
const shouldCloseDropdowns = previousActiveDropdowns.filter((dropdown) => {
const allChildren = d.getAllElements(dropdown.contentElement.nativeElement, element => element.classList.contains('sme-layout-dropdown'));
if (allChildren.includes(previousDropdownNode) && allChildren.includes(currentDropdownNode)) {
return false;
}
return true;
});
this.closeNestedAllExpandedDropdowns(shouldCloseDropdowns);
}
else {
this.closeNestedAllExpandedDropdowns(this.dropdownList);
}
}
resolve();
});
}
/**
* It handles whether two dropdowns are from the same parent
*/
isFromSameParent(currentDropdown, PreviousDropdown) {
const currentDropdownNode = currentDropdown.contentElement.nativeElement;
const previousDropdownNode = PreviousDropdown.contentElement.nativeElement;
const d = Dom;
const currentDropdownNodeParents = d.getAllAncestors(currentDropdownNode, element => element.classList.contains('sme-layout-dropdown'));
const previousDropdownNodeParents = d.getAllAncestors(previousDropdownNode, element => element.classList.contains('sme-layout-dropdown'));
let res = false;
for (let i = 0; i < currentDropdownNodeParents.length; i++) {
const iElement = currentDropdownNodeParents[i];
for (let j = 0; j < previousDropdownNodeParents.length; j++) {
const jElement = previousDropdownNodeParents[j];
if (iElement.isSameNode(jElement)) {
res = true;
}
}
}
return res;
}
}
// Minimum space above or below the dropdown to determine positioning.
DropdownService.minSpaceForDropdown = 300;
// If the original height for the whole dropdown is 2 times the available space
// should display as full height
DropdownService.shouldUseFullHeightDropdownRatio = 2;
/** @nocollapse */ DropdownService.ɵfac = function DropdownService_Factory(t) { return new (t || DropdownService)(); };
/** @nocollapse */ DropdownService.ɵprov = /** @pureOrBreakMyCode */ i0.ɵɵdefineInjectable({ token: DropdownService, factory: DropdownService.ɵfac });
__decorate([
Yield()
/**
* It handles closing the dropdown from the component
*/
,
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", void 0)
], DropdownService.prototype, "closeNestedHelper", null);
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(DropdownService, [{
type: Injectable
}], null, { closeNestedHelper: [] }); })();
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZHJvcGRvd24uc2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uLy4uL2FuZ3VsYXIvc3JjL2NvbnRyb2xzL2Ryb3Bkb3duL2Ryb3Bkb3duLnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDM0MsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLDBFQUEwRSxDQUFDO0FBQ2pHLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxxRUFBcUUsQ0FBQztBQUM5RixPQUFPLEVBQUUsR0FBRyxFQUFFLE1BQU0sa0RBQWtELENBQUM7O0FBT3ZFOzs7O0dBSUc7QUFDSCxNQUFNLE9BQU8sZUFBZTtJQU41QjtRQWFZLDJCQUFzQixHQUFHLElBQUksQ0FBQyxlQUFlLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3pELDZCQUF3QixHQUFHLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDN0QsMEJBQXFCLEdBQUcsSUFBSSxDQUFDLGNBQWMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDdkQsd0JBQW1CLEdBQUcsSUFBSSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7UUFFM0Q7O1dBRUc7UUFDSyxpQkFBWSxHQUFvQixFQUFFLENBQUM7UUFDM0MsMEVBQTBFO1FBQzFFLHVFQUF1RTtRQUMvRCw0QkFBdUIsR0FBb0M7WUFDL0QsS0FBSyxFQUFFO2dCQUNILCtDQUErQztnQkFDL0MscUNBQXFDO2dCQUNyQyxHQUFHLEVBQUUsR0FBRztnQkFDUixNQUFNLEVBQUUsQ0FBQztnQkFDVCxXQUFXLEVBQUUsSUFBSTthQUNwQjtZQUNELE1BQU0sRUFBRTtnQkFDSiw4Q0FBOEM7Z0JBQzlDLHFDQUFxQztnQkFDckMsR0FBRyxFQUFFLEdBQUc7Z0JBQ1IsR0FBRyxFQUFFLEdBQUc7Z0JBQ1IsTUFBTSxFQUFFLEVBQUU7YUFDYjtZQUNELEtBQUssRUFBRTtnQkFDSCxnREFBZ0Q7Z0JBQ2hELHFDQUFxQztnQkFDckMsR0FBRyxFQUFFLEdBQUc7Z0JBQ1IsTUFBTSxFQUFFLEVBQUU7YUFDYjtTQUNKLENBQUM7S0Fra0JMO0lBaGtCRzs7Ozs7T0FLRztJQUNJLElBQUksQ0FBQyxRQUFrQixFQUFFLGVBQXlCLEVBQUUsYUFBdUI7UUFDOUUsT0FBTyxJQUFJLE9BQU8sQ0FBQyxDQUFDLE9BQU8sRUFBRSxFQUFFO1lBQzNCLElBQUksd0NBQXdDLEdBQUcsS0FBSyxDQUFDO1lBQ3JELElBQUksQ0FBQyxJQUFJLENBQUMsY0FBYyxFQUFFO2dCQUN0QixnRkFBZ0Y7Z0JBQ2hGLE1BQU0sQ0FBQyxRQUFRLENBQUMsZ0JBQWdCLENBQUMsT0FBTyxFQUFFLElBQUksQ0FBQyxzQkFBc0IsQ0FBQyxDQUFDO2dCQUN2RSxNQUFNLENBQUMsUUFBUSxDQUFDLGdCQUFnQixDQUFDLFNBQVMsRUFBRSxJQUFJLENBQUMsd0JBQXdCLENBQUMsQ0FBQztnQkFDM0UsTUFBTSxDQUFDLGdCQUFnQixDQUFDLFFBQVEsRUFBRSxJQUFJLENBQUMscUJBQXFCLEVBQUUsSUFBSSxDQUFDLENBQUM7Z0JBQ3BFLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLG1CQUFtQixDQUFDLENBQUM7YUFDN0Q7aUJBQU07Z0JBQ0gsdURBQXVEO2dCQUN2RCwwREFBMEQ7Z0JBQzFELHVDQUF1QztnQkFDdkMsSUFBSSxJQUFJLENBQUMsY0FBYyxLQUFLLFFBQVEsRUFBRTtvQkFDbEMsTUFBTSxZQUFZLEdBQUcsUUFBUSxDQUFDLGNBQWMsQ0FBQyxhQUFhLENBQUM7b0JBQzNELE1BQU0sc0JBQXNCLEdBQ3hCLElBQUksQ0FBQyxjQUFjLENBQUMsY0FBYyxDQUFDLGFBQWEsQ0FBQyxnQkFBZ0IsQ0FBQyxzQkFBc0IsQ0FBQyxDQUFDO29CQUM5RixJQUFJLHNCQUFzQixDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUU7d0JBQ25DLEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxzQkFBc0IsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLEVBQUU7NEJBQ3BELElBQUksc0JBQXNCLENBQUMsQ0FBQyxDQUFDLENBQUMsVUFBVSxDQUFDLFlBQVksQ0FBQyxFQUFFO2dDQUNwRCx3Q0FBd0MsR0FBRyxJQUFJLENBQUM7NkJBQ25EO3lCQUNKO3FCQUNKO29CQUNELElBQUksd0NBQXdDLEVBQUU7d0JBQzFDLElBQUksQ0FBQyxjQUFjLENBQUMsTUFBTSxHQUFHLElBQUksQ0FBQztxQkFDckM7eUJBQU07d0JBQ0gsSUFBSSxDQUFDLGNBQWMsQ0FBQyxNQUFNLEdBQUcsS0FBSyxDQUFDO3FCQUN0QztpQkFFSjtxQkFBTTtvQkFDSCxzQ0FBc0M7b0JBQ3RDLElBQUksQ0FBQyxjQUFjLENBQUMsTUFBTSxHQUFHLEtBQUssQ0FBQztpQkFDdEM7YUFDSjtZQUNELDJFQUEyRTtZQUMzRSxvQ0FBb0M7WUFDcEMsMkRBQTJEO1lBQzNELElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxRQUFRLEVBQUUsSUFBSSxDQUFDLGNBQWMsRUFBRSx3Q0FBd0MsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUU7Z0JBQ3RHLDRDQUE0QztnQkFDNUMsSUFBSSxDQUFDLGNBQWMsR0FBRyxRQUFRLENBQUM7Z0JBQy9CLElBQUksQ0FBQyxjQUFjLENBQUMsTUFBTSxHQUFHLElBQUksQ0FBQztnQkFFbEMsdUNBQXVDO2dCQUN2QyxJQUFJLElBQUksQ0FBQyxjQUFjLElBQUksQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsY0FBYyxDQUFDLEVBQUU7b0JBQ3pFLElBQUksQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxjQUFjLENBQUMsQ0FBQztpQkFDL0M7Z0JBRUQsSUFBSSxDQUFDLGNBQWMsQ0FBQyxRQUFRLEVBQUUsZUFBZSxFQUFFLGFBQWEsQ0FBQyxDQUFDO2dCQUU5RCxPQUFPLEVBQUUsQ0FBQztZQUNkLENBQUMsQ0FBQyxDQUFDO1FBQ1AsQ0FBQyxDQUFDLENBQUM7SUFDUCxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNJLGNBQWMsQ0FBQyxRQUFrQixFQUFFLGVBQXlCLEVBQUUsYUFBdUI7UUFDeEYsTUFBTSxjQUFjLEdBQWlCLFFBQVEsQ0FBQyxjQUFjLENBQUMsYUFBYyxDQUFDO1FBRTVFLDBCQUEwQjtRQUMxQixRQUFRLENBQUMsU0FBUyxHQUFHLEtBQUssQ0FBQztRQUMzQixRQUFRLENBQUMsU0FBUyxHQUFHLEtBQUssQ0FBQztRQUUzQix5SEFBeUg7UUFDekgsY0FBYyxDQUFDLEtBQUssQ0FBQyxNQUFNLEdBQUcsRUFBRSxDQUFDO1FBQ2pDLGNBQWMsQ0FBQyxLQUFLLENBQUMsS0FBSyxHQUFHLEVBQUUsQ0FBQztRQUVoQyxNQUFNLFVBQVUsR0FBaUIsUUFBUSxDQUFDLGFBQWEsQ0FBQyxhQUFjLENBQUMscUJBQXFCLEVBQUUsQ0FBQztRQUUvRix1R0FBdUc7UUFDdkcsTUFBTSxTQUFTLEdBQUcsY0FBYyxDQUFDLFdBQVcsQ0FBQztRQUM3QyxNQUFNLFVBQVUsR0FBRyxjQUFjLENBQUMsWUFBWSxDQUFDO1FBRS9DLHdDQUF3QztRQUN4QyxJQUFJLENBQUMsdUJBQXVCLENBQUMsVUFBVSxFQUFFLFFBQVEsRUFBRSxTQUFTLEVBQUUsYUFBYSxDQUFDLENBQUM7UUFFN0UseUNBQXlDO1FBQ3pDLElBQUksQ0FBQyxzQkFBc0IsQ0FBQyxVQUFVLEVBQUUsUUFBUSxFQUFFLFVBQVUsRUFBRSxlQUFlLEVBQUUsYUFBYSxDQUFDLENBQUM7UUFFOUYsSUFBSSxDQUFDLFFBQVEsQ0FBQyxpQ0FBaUMsRUFBRTtZQUM3QywwRUFBMEU7WUFDNUQsUUFBUSxDQUFDLGNBQWMsQ0FBQyxhQUFjLENBQUMsS0FBSyxDQUFDLFFBQVEsR0FBRyxTQUFTLENBQUM7U0FDbkY7SUFDTCxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNJLEtBQUssQ0FBQyxRQUFrQixFQUFFLEtBQWM7UUFDM0MsOEJBQThCO1FBQzlCLFFBQVEsQ0FBQyxNQUFNLEdBQUcsS0FBSyxDQUFDO1FBQ3hCLDhEQUE4RDtRQUM5RCxJQUFJLElBQUksQ0FBQyxjQUFjLEtBQUssUUFBUSxFQUFFO1lBQ2xDLE9BQU87U0FDVjtRQUVELGdDQUFnQztRQUNoQyxJQUFJLENBQUMsY0FBYyxHQUFHLElBQUksQ0FBQztRQUUzQixJQUFJLEtBQUssRUFBRTtZQUNQLElBQUksQ0FBQyxXQUFXLENBQUMsUUFBUSxFQUFFLEtBQUssQ0FBQyxDQUFDO1NBQ3JDO1FBRUQsMERBQTBEO1FBQzFELElBQUksQ0FBQyxJQUFJLENBQUMsY0FBYyxFQUFFO1lBQ3RCLE1BQU0sQ0FBQyxRQUFRLENBQUMsbUJBQW1CLENBQUMsT0FBTyxFQUFFLElBQUksQ0FBQyxzQkFBc0IsQ0FBQyxDQUFDO1lBQzFFLE1BQU0sQ0FBQyxRQUFRLENBQUMsbUJBQW1CLENBQUMsU0FBUyxFQUFFLElBQUksQ0FBQyx3QkFBd0IsQ0FBQyxDQUFDO1lBQzlFLE1BQU0sQ0FBQyxtQkFBbUIsQ0FBQyxRQUFRLEVBQUUsSUFBSSxDQUFDLHFCQUFxQixFQUFFLElBQUksQ0FBQyxDQUFDO1lBQ3ZFLE1BQU0sQ0FBQyxtQkFBbUIsQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLG1CQUFtQixDQUFDLENBQUM7U0FDaEU7SUFDTCxDQUFDO0lBRUQ7O09BRUc7SUFDSyxlQUFlLENBQUMsS0FBaUI7UUFDckMsK0JBQStCO1FBQy9CLElBQUksQ0FBQyxJQUFJLENBQUMsY0FBYyxFQUFFO1lBQ3RCLE9BQU87U0FDVjtRQUVELGlGQUFpRjtRQUNqRixJQUFJLElBQUksQ0FBQyxjQUFjLENBQUMsYUFBYSxJQUFJLElBQUksQ0FBQyxjQUFjLENBQUMsYUFBYSxDQUFDLGFBQWEsS0FBSyxLQUFLLENBQUMsTUFBTSxFQUFFO1lBQ3ZHLE9BQU87U0FDVjtRQUVELDRFQUE0RTtRQUM1RSxJQUFJLElBQUksQ0FBQyxjQUFjLENBQUMsY0FBYztZQUNsQyxJQUFJLENBQUMsY0FBYyxDQUFDLGNBQWMsQ0FBQyxhQUFhLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsRUFBRTtZQUV6RSwrREFBK0Q7WUFDL0QsSUFBSSxpQkFBaUIsQ0FBQyxJQUFJLENBQU8sS0FBSyxDQUFDLE1BQU8sQ0FBQyxPQUFPLENBQUMsRUFBRTtnQkFDckQsT0FBTzthQUNWO1lBRUQsZ0lBQWdJO1lBQ2hJLElBQUksT0FBTyxHQUFnQixLQUFLLENBQUMsTUFBTSxDQUFDO1lBQ3hDLE9BQU8sQ0FBQyxJQUFJLENBQUMsY0FBYyxDQUFDLGNBQWMsQ0FBQyxhQUFhLENBQUMsVUFBVSxDQUFDLE9BQU8sQ0FBQyxFQUFFO2dCQUMxRSxJQUFJLE9BQU8sQ0FBQyxTQUFTLENBQUMsUUFBUSxDQUFDLGtDQUFrQyxDQUFDLEVBQUU7b0JBQ2hFLE9BQU87aUJBQ1Y7Z0JBQ0QsT0FBTyxHQUFHLE9BQU8sQ0FBQyxhQUFhLENBQUM7YUFDbkM7U0FDSjtRQUVELGdDQUFnQztRQUNoQyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxjQUFjLEVBQUUsS0FBSyxDQUFDLENBQUM7SUFDM0MsQ0FBQztJQUVEOztPQUVHO0lBQ0ssaUJBQWlCLENBQUMsS0FBb0I7UUFDMUMsNkVBQTZFO1FBQzdFLElBQUksS0FBSyxDQUFDLE9BQU8sS0FBSyxPQUFPLENBQUMsTUFBTSxFQUFFO1lBQ2xDLElBQUksSUFBSSxDQUFDLGNBQWMsSUFBSSxJQUFJLENBQUMsY0FBYyxDQUFDLGFBQWEsRUFBRTtnQkFDMUQsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLGNBQWMsQ0FBQyxhQUFhLENBQUMsYUFBYSxDQUFDO2dCQUNuRSxNQUFNLFdBQVcsR0FBRyxHQUFHLENBQUMsMkJBQTJCLENBQUMsVUFBVSxDQUFDLElBQUksR0FBRyxDQUFDLHVCQUF1QixDQUFDLFVBQVUsQ0FBQyxDQUFDO2dCQUUzRyxJQUFJLFdBQVcsRUFBRTtvQkFDYixXQUFXLENBQUMsS0FBSyxFQUFFLENBQUM7aUJBQ3ZCO2FBQ0o7WUFFRCxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxjQUFjLENBQUMsQ0FBQztZQUNoQyxPQUFPO1NBQ1Y7SUFDTCxDQUFDO0lBRUQ7O09BRUc7SUFDSyxjQUFjLENBQUMsS0FBWTtRQUMvQiwrQkFBK0I7UUFDL0IsSUFBSSxDQUFDLElBQUksQ0FBQyxjQUFjLEVBQUU7WUFDdEIsT0FBTztTQUNWO1FBQ0QsZ0ZBQWdGO1FBQ2hGLElBQUksSUFBSSxDQUFDLGNBQWMsQ0FBQyxjQUFjO1lBQ2xDLElBQUksQ0FBQyxjQUFjLENBQUMsY0FBYyxDQUFDLGFBQWEsQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxFQUFFO1lBQ3pFLE9BQU87U0FDVjtRQUNELCtCQUErQjtRQUMvQixJQUFJLElBQUksQ0FBQyxZQUFZLElBQUksSUFBSSxDQUFDLFlBQVksQ0FBQyxNQUFNLEVBQUU7WUFDL0MsSUFBSSxDQUFDLCtCQUErQixDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsQ0FBQztZQUV4RCxzREFBc0Q7WUFDdEQsSUFBSSxJQUFJLENBQUMsY0FBYyxDQUFDLGFBQWEsRUFBRTtnQkFDbkMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsY0FBYyxDQUFDLENBQUM7YUFDbkM7U0FDSjthQUFNO1lBQ0gsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsY0FBYyxDQUFDLENBQUM7U0FDbkM7SUFDTCxDQUFDO0lBRUQ7O09BRUc7SUFDSyxZQUFZLENBQUMsS0FBaUI7UUFDbEMsdUdBQXVHO1FBQ3ZHLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLGNBQWMsRUFBRSxLQUFLLENBQUMsQ0FBQztJQUMzQyxDQUFDO0lBRUQ7OztNQUdFO0lBQ00sdUJBQXVCLENBQzNCLFVBQWdDLEVBQ2hDLFFBQWtCLEVBQ2xCLFNBQWlCLEVBQ2pCLGFBQXVCO1FBRXZCLHFFQUFxRTtRQUNyRSxJQUFJLFFBQVEsR0FBRyxVQUFVLENBQUMsSUFBSSxDQUFDO1FBRS9CLHlCQUF5QjtRQUN6QixNQUFNLFdBQVcsR0FBRyxNQUFNLENBQUMsVUFBVSxDQUFDO1FBRXRDLDBHQUEwRztRQUMxRyxJQUFJLENBQUMsQ0FBQyxhQUFhLEVBQUU7WUFDakIsTUFBTSxTQUFTLEdBQUcsVUFBVSxDQUFDLElBQUksQ0FBQztZQUNsQyxNQUFNLFVBQVUsR0FBRyxXQUFXLEdBQUcsVUFBVSxDQUFDLEtBQUssQ0FBQztZQUVsRCx1SEFBdUg7WUFDdkgsSUFBSSxVQUFVLElBQUksZUFBZSxDQUFDLG1CQUFtQixJQUFJLFNBQVMsR0FBRyxVQUFVLElBQUksVUFBVSxHQUFHLFNBQVMsRUFBRTtnQkFDdkcsSUFBSSxTQUFTLEdBQUcsVUFBVSxFQUFFO29CQUN4QixTQUFTLEdBQUcsVUFBVSxDQUFDO29CQUN2QixRQUFRLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQztpQkFDN0I7Z0JBQ0Qsd0JBQXdCO2dCQUN4QixRQUFRLEdBQUcsVUFBVSxDQUFDLEtBQUssQ0FBQzthQUMvQjtpQkFBTSxJQUFJLFNBQVMsR0FBRyxTQUFTLEVBQUU7Z0JBQzlCLHVCQUF1QjtnQkFDdkIsUUFBUSxHQUFHLFVBQVUsQ0FBQyxJQUFJLEdBQUcsU0FBUyxDQUFDO2FBQzFDO2lCQUFNO2dCQUNILHlFQUF5RTtnQkFDekUsSUFBSSxTQUFTLEdBQUcsU0FBUyxHQUFHLFVBQVUsQ0FBQyxLQUFLLEVBQUU7b0JBQzFDLFNBQVMsR0FBRyxTQUFTLEdBQUcsVUFBVSxDQUFDLEtBQUssQ0FBQztvQkFDekMsUUFBUSxDQUFDLFNBQVMsR0FBRyxJQUFJLENBQUM7aUJBQzdCO2dCQUNELHlCQUF5QjtnQkFDekIsUUFBUSxHQUFHLFVBQVUsQ0FBQyxLQUFLLEdBQUcsU0FBUyxDQUFDO2FBQzNDO1NBQ0o7YUFBTTtZQUNILDZFQUE2RTtZQUM3RSxJQUFJLFNBQVMsR0FBRyxXQUFXLEVBQUU7Z0JBQ3pCLFNBQVMsR0FBRyxXQUFXLENBQUM7Z0JBQ3hCLFFBQVEsQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDO2FBQzdCO1lBQ0QsSUFBSSxTQUFTLEdBQUcsVUFBVSxDQUFDLEtBQUssRUFBRTtnQkFDOUIsU0FBUyxHQUFHLFVBQVUsQ0FBQyxLQUFLLENBQUM7YUFDaEM7WUFFRCxJQUFJLFFBQVEsR0FBRyxTQUFTLEdBQUcsV0FBVyxFQUFFO2dCQUNwQyxRQUFRLEdBQUcsV0FBVyxHQUFHLFNBQVMsQ0FBQzthQUN0QztTQUNKO1FBRUQsUUFBUSxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLFFBQVEsQ0FBQyxDQUFDO1FBQ2pDLFFBQVEsQ0FBQyxJQUFJLEdBQUcsUUFBUSxDQUFDO1FBRVgsUUFBUSxDQUFDLGNBQWMsQ0FBQyxhQUFjLENBQUMsS0FBSyxDQUFDLElBQUksR0FBRyxRQUFRLEdBQUcsSUFBSSxDQUFDO1FBQ2xGLElBQUksQ0FBQyxRQUFRLENBQUMsU0FBUyxFQUFFO1lBQ1AsUUFBUSxDQUFDLGNBQWMsQ0FBQyxhQUFjLENBQUMsS0FBSyxDQUFDLEtBQUssR0FBRyxTQUFTLEdBQUcsSUFBSSxDQUFDO1NBQ3ZGO0lBQ0wsQ0FBQztJQUVEOzs7T0FHRztJQUNLLHNCQUFzQixDQUMxQixVQUFnQyxFQUNoQyxRQUFrQixFQUNsQixVQUFrQixFQUNsQixlQUF5QixFQUN6QixhQUF1QjtRQUV2Qix3RUFBd0U7UUFDeEUsTUFBTSxrQkFBa0IsR0FBRyxVQUFVLENBQUM7UUFFdEMsb0VBQW9FO1FBQ3BFLElBQUksT0FBTyxHQUFHLFVBQVUsQ0FBQyxNQUFNLENBQUM7UUFFaEMsMEJBQTBCO1FBQzFCLE1BQU0sWUFBWSxHQUFHLE1BQU0sQ0FBQyxXQUFXLENBQUM7UUFFeEMsTUFBTSxVQUFVLEdBQUcsVUFBVSxDQUFDLEdBQUcsQ0FBQztRQUNsQyxNQUFNLFVBQVUsR0FBRyxZQUFZLEdBQUcsVUFBVSxDQUFDLE1BQU0sQ0FBQztRQUVwRCxtRUFBbUU7UUFDbkUsSUFBSSxxQkFBcUIsR0FBRyxLQUFLLENBQUM7UUFDbEMsMkRBQTJEO1FBQzNELElBQUksVUFBVSxJQUFJLGVBQWUsQ0FBQyxtQkFBbUIsSUFBSSxVQUFVLEdBQUcsVUFBVSxJQUFJLFVBQVUsR0FBRyxVQUFVLEVBQUU7WUFDekcsSUFBSSxVQUFVLEdBQUcsVUFBVSxFQUFFO2dCQUN6QixVQUFVLEdBQUcsVUFBVSxDQUFDO2dCQUN4QixRQUFRLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQzthQUM3QjtZQUNELDBCQUEwQjtZQUMxQixPQUFPLEdBQUcsYUFBYSxLQUFLLElBQUksQ0FBQyxDQUFDLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsVUFBVSxDQUFDLE1BQU0sQ0FBQztTQUN6RTthQUFNO1lBQ0gscUNBQXFDO1lBQ3JDLElBQUksVUFBVSxHQUFHLFVBQVUsRUFBRTtnQkFDekIsVUFBVSxHQUFHLFVBQVUsQ0FBQztnQkFDeEIsUUFBUSxDQUFDLFNBQVMsR0FBRyxJQUFJLENBQUM7YUFDN0I7WUFDRCwwQkFBMEI7WUFDMUIsT0FBTyxHQUFHLGFBQWEsS0FBSyxJQUFJLENBQUMsQ0FBQyxDQUFDLFVBQVUsQ0FBQyxNQUFNLEdBQUcsVUFBVSxDQUFDLENBQUMsQ0FBQyxVQUFVLENBQUMsR0FBRyxHQUFHLFVBQVUsQ0FBQztZQUNoRyxxQkFBcUIsR0FBRyxJQUFJLENBQUM7U0FDaEM7UUFFRCxPQUFPLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFDL0IsUUFBUSxDQUFDLEdBQUcsR0FBRyxPQUFPLENBQUM7UUFFdkIsSUFBSSxlQUFlLEVBQUU7WUFDakIsTUFBTSxlQUFlLEdBQ2pCLElBQUksQ0FBQywwQkFBMEIsQ0FDM0IsVUFBVSxFQUNWLE9BQU8sRUFDUCxxQkFBcUIsRUFDckIsVUFBVSxFQUNWLFVBQVUsRUFDVixrQkFBa0IsQ0FDckIsQ0FBQztZQUNRLFFBQVEsQ0FBQyxjQUFjLENBQUMsYUFBYyxDQUFDLEtBQUssQ0FBQyxNQUFNLEdBQUcsZUFBZSxDQUFDLE1BQU0sR0FBRyxJQUFJLENBQUM7WUFDcEYsUUFBUSxDQUFDLGNBQWMsQ0FBQyxhQUFjLENBQUMsS0FBSyxDQUFDLEdBQUcsR0FBRyxlQUFlLENBQUMsR0FBRyxHQUFHLElBQUksQ0FBQztTQUMvRjthQUFNO1lBQ1csUUFBUSxDQUFDLGNBQWMsQ0FBQyxhQUFjLENBQUMsS0FBSyxDQUFDLE1BQU0sR0FBRyxVQUFVLEdBQUcsSUFBSSxDQUFDO1lBQ3hFLFFBQVEsQ0FBQyxjQUFjLENBQUMsYUFBYyxDQUFDLEtBQUssQ0FBQyxHQUFHLEdBQUcsT0FBTyxHQUFHLElBQUksQ0FBQztTQUNuRjtJQUNMLENBQUM7SUFFRDs7Ozs7T0FLRztJQUNLLDBCQUEwQixDQUFDLFVBQWtCLEVBQUUsT0FBZSxFQUFFLHFCQUE4QixFQUFFLFNBQWlCLEVBQ3JILFlBQW9CLEVBQUUsa0JBQTBCO1FBQ2hELE1BQU0sWUFBWSxHQUFHLE1BQU0sQ0FBQyxXQUFXLENBQUM7UUFDeEMsSUFBSSxTQUFTLEdBQUcsT0FBTyxDQUFDO1FBQ3hCLElBQUksWUFBWSxHQUFHLFVBQVUsQ0FBQztRQUM5QixNQUFNLFlBQVksR0FBRyxxQkFBcUIsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxZQUFZLENBQUM7UUFDdEUsTUFBTSxrQkFBa0IsR0FBRyxJQUFJLENBQUMsaUJBQWlCLENBQUMsWUFBWSxDQUFDLENBQUM7UUFDaEUsTUFBTSxrQkFBa0IsR0FBRyxJQUFJLENBQUMsa0JBQWtCLENBQUMsVUFBVSxFQUFFLFlBQVksRUFBRSxrQkFBa0IsQ0FBQyxDQUFDO1FBRWpHLElBQUksa0JBQWtCLEVBQUU7WUFDcEIsNkVBQTZFO1lBQzdFLElBQUksa0JBQWtCLElBQUksWUFBWSxHQUFHLGVBQWUsQ0FBQyxnQ0FBZ0MsRUFBRTtnQkFDdkYsU0FBUyxHQUFHLGtCQUFrQixDQUFDO2dCQUMvQix1RUFBdUU7Z0JBQ3ZFLG9DQUFvQztnQkFDcEMsSUFBSSxrQkFBa0IsSUFBSSxZQUFZLEdBQUcsQ0FBQyxHQUFHLGtCQUFrQixFQUFFO29CQUM3RCxZQUFZLEdBQUcsWUFBWSxHQUFHLENBQUMsR0FBRyxrQkFBa0IsQ0FBQztpQkFDeEQ7cUJBQU07b0JBQ0gsWUFBWSxHQUFHLGtCQUFrQixDQUFDO2lCQUNyQzthQUNKO2lCQUFNO2dCQUNILElBQUksWUFBWSxJQUFJLElBQUksQ0FBQyx1QkFBdUIsQ0FBQyxLQUFLLENBQUMsR0FBRyxFQUFFO29CQUN4RCxJQUFJLGtCQUFrQixHQUFHLFlBQVksR0FBRyxDQUFDLEdBQUcsa0JBQWtCLEVBQUU7d0JBQzVELGVBQWU7d0JBQ2YsWUFBWSxHQUFHLFlBQVksR0FBRyxDQUFDLEdBQUcsa0JBQWtCLENBQUM7d0JBQ3JELFNBQVMsR0FBRyxrQkFBa0IsQ0FBQztxQkFDbEM7eUJBQU0sSUFBSSxVQUFVLEdBQUcsWUFBWSxHQUFHLGtCQUFrQixFQUFFO3dCQUN2RCxxREFBcUQ7d0JBQ3JELFlBQVksR0FBRyxVQUFVLEdBQUcsa0JBQWtCLENBQUM7d0JBQy9DLFNBQVMsR0FBRyxrQkFBa0IsQ0FBQztxQkFDbEM7aUJBQ0o7cUJBQU07b0JBQ0gsMkRBQTJEO29CQUMzRCxZQUFZLEdBQUcsVUFBVSxHQUFHLGtCQUFrQixDQUFDO2lCQUNsRDthQUNKO1lBRUQsSUFBSSxxQkFBcUIsRUFBRTtnQkFDdkIsSUFBSSxTQUFTLEdBQUcsWUFBWSxFQUFFO29CQUMxQixTQUFTLEdBQUcsU0FBUyxHQUFHLFlBQVksQ0FBQztpQkFDeEM7YUFDSjtTQUNKO1FBRUQsT0FBTztZQUNILEdBQUcsRUFBRSxTQUFTO1lBQ2QsTUFBTSxFQUFFLFlBQVk7U0FDdkIsQ0FBQztJQUNOLENBQUM7SUFFRDs7T0FFRztJQUNLLGtCQUFrQixDQUFDLFVBQWtCLEVBQUUsWUFBb0IsRUFBRSxrQkFBMEI7UUFDM0YsSUFBSSxHQUFHLEdBQUcsS0FBSyxDQUFDO1FBQ2hCLElBQUksWUFBWSxHQUFHLFVBQVUsR0FBRyxrQkFBa0IsRUFBRTtZQUNoRCxHQUFHLEdBQUcsSUFBSSxDQUFDO1NBQ2Q7UUFDRCxPQUFPLEdBQUcsQ0FBQztJQUNmLENBQUM7SUFFRDs7T0FFRztJQUNLLGlCQUFpQixDQUFDLFlBQW9CO1FBQzFDLElBQUksa0JBQWtCLEdBQUcsQ0FBQyxDQUFDO1FBQzNCLElBQUksWUFBWSxJQUFJLElBQUksQ0FBQyx1QkFBdUIsQ0FBQyxLQUFLLENBQUMsR0FBRyxFQUFFO1lBQ3hELGtCQUFrQixHQUFHLElBQUksQ0FBQyx1QkFBdUIsQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDO1NBQ2xFO2FBQU0sSUFBSSxZQUFZLElBQUksSUFBSSxDQUFDLHVCQUF1QixDQUFDLE1BQU0sQ0FBQyxHQUFHLElBQUksWUFBWSxJQUFJLElBQUksQ0FBQyx1QkFBdUIsQ0FBQyxNQUFNLENBQUMsR0FBRyxFQUFFO1lBQzNILGtCQUFrQixHQUFHLElBQUksQ0FBQyx1QkFBdUIsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDO1NBQ25FO2FBQU0sSUFBSSxZQUFZLElBQUksSUFBSSxDQUFDLHVCQUF1QixDQUFDLEtBQUssQ0FBQyxHQUFHLEVBQUU7WUFDL0Qsa0JBQWtCLEdBQUcsSUFBSSxDQUFDLHVCQUF1QixDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUM7U0FDbEU7UUFDRCxPQUFPLGtCQUFrQixDQUFDO0lBQzlCLENBQUM7SUFFRDs7OztPQUlHO0lBQ0ksV0FBVyxDQUFDLFFBQWtCLEVBQUUsS0FBYTtRQUNoRCxJQUFJLFFBQVEsSUFBSSxLQUFLLEVBQUU7WUFDbkIsSUFBSSxXQUFXLEdBQWdCLEtBQUssQ0FBQyxNQUFNLENBQUM7WUFDNUMseUJBQXlCO1lBQ3pCLElBQUksSUFBSSxDQUFDLFlBQVksSUFBSSxJQUFJLENBQUMsWUFBWSxDQUFDLE1BQU0sRUFBRTtnQkFDL0MsSUFBSSxXQUFXLEVBQUU7b0JBQ2IsSUFBSSxXQUFXLENBQUMsU0FBUyxFQUFFO3dCQUN2QixJQUFJLENBQUMsV0FBVyxDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUMsc0JBQXNCLENBQUMsRUFBRTs0QkFDekQsV0FBVyxHQUFHLEdBQUcsQ0FBQyxXQUFXLENBQUMsV0FBVyxFQUFFLE9BQU8sQ0FBQyxFQUFFLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUMscUJBQXFCLENBQUMsQ0FBQyxDQUFDO3lCQUM1RztxQkFDSjt5QkFBTTt3QkFDSCx3RkFBd0Y7d0JBQ3hGLFdBQVcsR0FBRyxJQUFJLENBQUM7cUJBQ3RCO2lCQUVKO3FCQUFNLElBQUksV0FBVyxDQUFDLGNBQWMsQ0FBQyxlQUFlLENBQUMsSUFBSSxXQUFXLENBQUMsYUFBYSxDQUFDLGNBQWMsQ0FBQyxFQUFFO29CQUNqRyxXQUFXLEdBQUcsV0FBVyxDQUFDLGFBQWEsQ0FBQyxjQUFjLENBQUMsQ0FBQztpQkFDM0Q7Z0JBRUQsSUFBSSxjQUFjLEdBQUcsSUFBSSxDQUFDO2dCQUMxQixJQUFJLFdBQVcsRUFBRTtvQkFDYixLQUFLLElBQUksS0FBSyxHQUFHLENBQUMsRUFBRSxLQUFLLEdBQUcsSUFBSSxDQUFDLFlBQVksQ0FBQyxNQUFNLEVBQUUsS0FBSyxFQUFFLEVBQUU7d0JBQzNELE1BQU0sQ0FBQyxHQUFHLElBQUksQ0FBQyxZQUFZLENBQUMsS0FBSyxDQUFDLENBQUM7d0JBQ25DLElBQUksQ0FBQyxDQUFDLGNBQWMsQ0FBQyxhQUFhLEtBQUssV0FBVyxFQUFFOzRCQUNoRCxjQUFjLEdBQUcsQ0FBQyxDQUFDOzRCQUNuQixNQUFNO3lCQUNUO3FCQUNKO2lCQUNKO2dCQUNELElBQUksQ0FBQyxjQUFjLEdBQUcsY0FBYyxDQUFDO2FBQ3hDO1lBQ0QsbUJBQW1CO1lBQ25CLElBQUksV0FBVyxFQUFFO2dCQUNiLE1BQU0sc0JBQXNCLEdBQUcsV0FBVyxDQUFDLGdCQUFnQixDQUFDLDZDQUE2QyxDQUFDLENBQUM7Z0JBQzNHLElBQUksc0JBQXNCLElBQUksc0JBQXNCLENBQUMsTUFBTSxFQUFFO29CQUN6RCwrQkFBK0I7b0JBQy9CLDBFQUEwRTtvQkFDMUUsc0JBQXNCLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUU7d0JBQ2pDLElBQUksQ0FBQyxFQUFFOzRCQUNILENBQUMsQ0FBQyxhQUFhLENBQUMsSUFBSSxLQUFLLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQzt5QkFDNUM7b0JBQ0wsQ0FBQyxDQUFDLENBQUM7aUJBQ047cUJBQU07b0JBQ0gsK0JBQStCO29CQUMvQixJQUFJLENBQUMsK0JBQStCLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxDQUFDO29CQUN4RCxJQUFJLENBQUMsY0FBYyxHQUFHLElBQUksQ0FBQztpQkFDOUI7YUFDSjtpQkFBTTtnQkFDSCwrQkFBK0I7Z0JBQy9CLG9CQUFvQjtnQkFDcEIsSUFBSSxDQUFDLCtCQUErQixDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsQ0FBQztnQkFDeEQsSUFBSSxDQUFDLGNBQWMsR0FBRyxJQUFJLENBQUM7YUFDOUI7U0FDSjthQUFNO1lBQ0gsT0FBTztTQUNWO0lBQ0wsQ0FBQztJQUVEOztPQUVHO0lBQ0ssK0JBQStCLENBQUMsWUFBNkI7UUFDakUsSUFBSSxZQUFZLElBQUksWUFBWSxDQUFDLE1BQU0sRUFBRTtZQUNyQyxLQUFLLElBQUksQ0FBQyxHQUFHLFlBQVksQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUU7Z0JBQy9DLE1BQU0sT0FBTyxHQUFHLFlBQVksQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDaEMsSUFBSSxPQUFPLElBQUksT0FBTyxDQUFDLGNBQWMsQ0FBQyxhQUFhLEVBQUU7b0JBQ2pELE1BQU0sSUFBSSxHQUFHLE9BQU8sQ0FBQyxjQUFjLENBQUMsYUFBYSxDQUFDO29CQUNsRCxpQ0FBaUM7b0JBQ2pDLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxtQkFBbUIsQ0FBQyxFQUFFO3dCQUMvQyxJQUFJLENBQUMsaUJBQWlCLENBQUMsT0FBTyxDQUFDLENBQUM7cUJBQ25DO2lCQUNKO2FBQ0o7U0FDSjtJQUNMLENBQUM7SUFNTyxpQkFBaUIsQ0FBQyxRQUFrQjtRQUN4QyxJQUFJLFFBQVEsRUFBRTtZQUNWLFFBQVEsQ0FBQyxNQUFNLEdBQUcsS0FBSyxDQUFDO1NBQzNCO0lBQ0wsQ0FBQztJQUVEOztPQUVHO0lBQ0ssaUJBQWlCLENBQUMsZUFBeUIsRUFBRSxnQkFBMEIsRUFBRSx3Q0FBaUQ7UUFFOUgsT0FBTyxJQUFJLE9BQU8sQ0FBQyxDQUFDLE9BQU8sRUFBRSxFQUFFO1lBQzNCLElBQUksd0NBQXdDLElBQUksQ0FBQyxJQUFJLENBQUMsWUFBWSxJQUFJLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxNQUFNLElBQUksQ0FBQyxnQkFBZ0IsRUFBRTtnQkFDbEgsT0FBTyxFQUFFLENBQUM7YUFDYjtpQkFBTTtnQkFDSCxNQUFNLGdCQUFnQixHQUFHLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxlQUFlLEVBQUUsZ0JBQWdCLENBQUMsQ0FBQztnQkFDbEYsTUFBTSxtQkFBbUIsR0FBRyxlQUFlLENBQUMsY0FBYyxDQUFDLGFBQWEsQ0FBQztnQkFDekUsTUFBTSxvQkFBb0IsR0FBRyxnQkFBZ0IsQ0FBQyxjQUFjLENBQUMsYUFBYSxDQUFDO2dCQUMzRSxNQUFNLENBQUMsR0FBRyxHQUFHLENBQUM7Z0JBQ2QsNEVBQTRFO2dCQUM1RSxxRUFBcUU7Z0JBQ3JFLE1BQU0sdUJBQXVCLEdBQUcsSUFBSSxDQUFDLFlBQVksQ0FBQztnQkFDbEQsSUFBSSxnQkFBZ0IsRUFBRTtvQkFDbEIsTUFBTSxvQkFBb0IsR0FBRyx1QkFBdUIsQ0FBQyxNQUFNLENBQUMsQ0FBQyxRQUFRLEVBQUUsRUFBRTt3QkFDckUsTUFBTSxXQUFXLEdBQUcsQ0FBQyxDQUFDLGNBQWMsQ0FBQyxRQUFRLENBQUMsY0FBYyxDQUFDLGFBQWEsRUFDdEUsT0FBTyxDQUFDLEVBQUUsQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxxQkFBcUIsQ0FBQyxDQUFDLENBQUM7d0JBQ2xFLElBQUksV0FBVyxDQUFDLFFBQVEsQ0FBQyxvQkFBb0IsQ0FBQyxJQUFJLFdBQVcsQ0FBQyxRQUFRLENBQUMsbUJBQW1CLENBQUMsRUFBRTs0QkFDekYsT0FBTyxLQUFLLENBQUM7eUJBQ2hCO3dCQUNELE9BQU8sSUFBSSxDQUFDO29CQUNoQixDQUFDLENBQUMsQ0FBQztvQkFDSCxJQUFJLENBQUMsK0JBQStCLENBQUMsb0JBQW9CLENBQUMsQ0FBQztpQkFDOUQ7cUJBQU07b0JBQ0gsSUFBSSxDQUFDLCtCQUErQixDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsQ0FBQztpQkFDM0Q7YUFDSjtZQUVELE9BQU8sRUFBRSxDQUFDO1FBQ2QsQ0FBQyxDQUFDLENBQUM7SUFDUCxDQUFDO0lBRUQ7O09BRUc7SUFDSyxnQkFBZ0IsQ0FBQyxlQUF5QixFQUFFLGdCQUEwQjtRQUMxRSxNQUFNLG1CQUFtQixHQUFHLGVBQWUsQ0FBQyxjQUFjLENBQUMsYUFBYSxDQUFDO1FBQ3pFLE1BQU0sb0JBQW9CLEdBQUcsZ0JBQWdCLENBQUMsY0FBYyxDQUFDLGFBQWEsQ0FBQztRQUMzRSxNQUFNLENBQUMsR0FBRyxHQUFHLENBQUM7UUFDZCxNQUFNLDBCQUEwQixHQUFHLENBQUMsQ0FBQyxlQUFlLENBQUMsbUJBQW1CLEVBQ3BFLE9BQU8sQ0FBQyxFQUFFLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUMscUJBQXFCLENBQUMsQ0FBQyxDQUFDO1FBQ2xFLE1BQU0sMkJBQTJCLEdBQUcsQ0FBQyxDQUFDLGVBQWUsQ0FBQyxvQkFBb0IsRUFDdEUsT0FBTyxDQUFDLEVBQUUsQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxxQkFBcUIsQ0FBQyxDQUFDLENBQUM7UUFDbEUsSUFBSSxHQUFHLEdBQUcsS0FBSyxDQUFDO1FBQ2hCLEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRywwQkFBMEIsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLEVBQUU7WUFDeEQsTUFBTSxRQUFRLEdBQUcsMEJBQTBCLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDL0MsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLDJCQUEyQixDQUFDLE1BQU0sRUFBRSxDQUFDLEVBQUUsRUFBRTtnQkFDekQsTUFBTSxRQUFRLEdBQUcsMkJBQTJCLENBQUMsQ0FBQyxDQUFDLENBQUM7Z0JBQ2hELElBQUksUUFBUSxDQUFDLFVBQVUsQ0FBQyxRQUFRLENBQUMsRUFBRTtvQkFDL0IsR0FBRyxHQUFHLElBQUksQ0FBQztpQkFDZDthQUNKO1NBQ0o7UUFDRCxPQUFPLEdBQUcsQ0FBQztJQUNmLENBQUM7O0FBdm1CRCxzRUFBc0U7QUFDdkQsbUNBQW1CLEdBQUcsR0FBRyxDQUFDO0FBQ3pDLCtFQUErRTtBQUMvRSxnQ0FBZ0M7QUFDakIsZ0RBQWdDLEdBQUcsQ0FBQyxDQUFDO2lHQUwzQyxlQUFlO29HQUFmLGVBQWUsV0FBZixlQUFlO0FBcWlCeEI7SUFBQyxLQUFLLEVBQUU7SUFDUjs7T0FFRzs7Ozs7d0RBS0Y7dUZBN2lCUSxlQUFlO2NBTjNCLFVBQVU7Z0JBK2lCQyxpQkFBaUIiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBJbmplY3RhYmxlIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XHJcbmltcG9ydCB7IFlpZWxkIH0gZnJvbSAnQG1pY3Jvc29mdC93aW5kb3dzLWFkbWluLWNlbnRlci1zZGsvY29yZS9iYXNlL2RlY29yYXRvcnMveWllbGQuZGVjb3JhdG9yJztcclxuaW1wb3J0IHsgS2V5Q29kZSB9IGZyb20gJ0BtaWNyb3NvZnQvd2luZG93cy1hZG1pbi1jZW50ZXItc2RrL2NvcmUvZGF0YS9hY2Nlc3NpYmlsaXR5LW1hbmFnZXInO1xyXG5pbXBvcnQgeyBEb20gfSBmcm9tICdAbWljcm9zb2Z0L3dpbmRvd3MtYWRtaW4tY2VudGVyLXNkay9jb3JlL2RvbS9kb20nO1xyXG5pbXBvcnQgeyBEcm9wZG93biB9IGZyb20gJy4vbW9kZWxzJztcclxuaW1wb3J0IHsgRHJvcGRvd25IZWlnaHRDYWxjdWxhdGlvblJlc3VsdCB9IGZyb20gJy4vbW9kZWxzL2Ryb3Bkb3duLWhlaWdodC1jYWxjdWxhdGlvbi1yZXN1bHQnO1xyXG5pbXBvcnQgeyBEcm9wZG93bkhlaWdodENhbGN1bGF0aW9uU2NoZW1hIH0gZnJvbSAnLi9tb2RlbHMvZHJvcGRvd24taGVpZ2h0LXNjaGVtYSc7XHJcblxyXG5leHBvcnQgdHlwZSBFdmVudHMgPSBLZXlib2FyZEV2ZW50IHwgTW91c2VFdmVudCB8IEZvY3VzRXZlbnQ7XHJcbkBJbmplY3RhYmxlKClcclxuLyoqXHJcbiAqIFNlcnZpY2UgZm9yIG1haW50YWluaW5nIGRyb3Bkb3duIHN0YXRlLlxyXG4gKiBPbmx5IG9uZSBkcm9wZG93biBjYW4gYmUgb3BlbmVkIGF0IGEgdGltZS5cclxuICogVGhpcyBkb2VzIG5vdCBzdXBwb3J0IG5lc3RlZCBkcm9wZG93bnMuXHJcbiAqL1xyXG5leHBvcnQgY2xhc3MgRHJvcGRvd25TZXJ2aWNlIHtcclxuICAgIC8vIE1pbmltdW0gc3BhY2UgYWJvdmUgb3IgYmVsb3cgdGhlIGRyb3Bkb3duIHRvIGRldGVybWluZSBwb3NpdGlvbmluZy5cclxuICAgIHByaXZhdGUgc3RhdGljIG1pblNwYWNlRm9yRHJvcGRvd24gPSAzMDA7XHJcbiAgICAvLyBJZiB0aGUgb3JpZ2luYWwgaGVpZ2h0IGZvciB0aGUgd2hvbGUgZHJvcGRvd24gaXMgMiB0aW1lcyB0aGUgYXZhaWxhYmxlIHNwYWNlXHJcbiAgICAvLyBzaG91bGQgZGlzcGxheSBhcyBmdWxsIGhlaWdodFxyXG4gICAgcHJpdmF0ZSBzdGF0aWMgc2hvdWxkVXNlRnVsbEhlaWdodERyb3Bkb3duUmF0aW8gPSAyO1xyXG5cclxuICAgIHByaXZhdGUgb25Eb2N1bWVudENsaWNrQmluZGluZyA9IHRoaXMub25Eb2N1bWVudENsaWNrLmJpbmQodGhpcyk7XHJcbiAgICBwcml2YXRlIG9uRG9jdW