UNPKG

@microsoft/windows-admin-center-sdk

Version:

Microsoft - Windows Admin Center Shell

581 lines 87.4 kB
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