From fd5ab6bc0d4d0da84b3aa4bfacf93e14612138cf Mon Sep 17 00:00:00 2001 From: "Janus C. H. Knudsen" Date: Tue, 3 Feb 2026 00:02:25 +0100 Subject: [PATCH] Some ignored filles was missing --- .gitignore | 6 +- wwwroot/js/calendar-min.js | 26 + wwwroot/js/calendar-v2.js | 1631 +++++ wwwroot/js/calendar.js | 1664 +++++ wwwroot/js/components/NavigationButtons.d.ts | 63 + wwwroot/js/components/NavigationButtons.js | 131 + .../js/components/NavigationButtons.js.map | 1 + wwwroot/js/components/ViewSelector.d.ts | 70 + wwwroot/js/components/ViewSelector.js | 130 + wwwroot/js/components/ViewSelector.js.map | 1 + wwwroot/js/components/WorkweekPresets.d.ts | 47 + wwwroot/js/components/WorkweekPresets.js | 95 + wwwroot/js/components/WorkweekPresets.js.map | 1 + wwwroot/js/configuration/CalendarConfig.d.ts | 44 + wwwroot/js/configuration/CalendarConfig.js | 90 + .../js/configuration/CalendarConfig.js.map | 1 + wwwroot/js/configuration/ConfigManager.d.ts | 11 + wwwroot/js/configuration/ConfigManager.js | 43 + wwwroot/js/configuration/ConfigManager.js.map | 1 + .../js/configuration/DateViewSettings.d.ts | 10 + wwwroot/js/configuration/DateViewSettings.js | 2 + .../js/configuration/DateViewSettings.js.map | 1 + wwwroot/js/configuration/GridSettings.d.ts | 22 + wwwroot/js/configuration/GridSettings.js | 11 + wwwroot/js/configuration/GridSettings.js.map | 1 + wwwroot/js/configuration/ICalendarConfig.d.ts | 21 + wwwroot/js/configuration/ICalendarConfig.js | 2 + .../js/configuration/ICalendarConfig.js.map | 1 + .../js/configuration/TimeFormatConfig.d.ts | 10 + wwwroot/js/configuration/TimeFormatConfig.js | 2 + .../js/configuration/TimeFormatConfig.js.map | 1 + .../js/configuration/WorkWeekSettings.d.ts | 9 + wwwroot/js/configuration/WorkWeekSettings.js | 2 + .../js/configuration/WorkWeekSettings.js.map | 1 + wwwroot/js/configurations/CalendarConfig.d.ts | 46 + wwwroot/js/configurations/CalendarConfig.js | 85 + .../js/configurations/CalendarConfig.js.map | 1 + wwwroot/js/configurations/ConfigManager.d.ts | 28 + wwwroot/js/configurations/ConfigManager.js | 80 + .../js/configurations/ConfigManager.js.map | 1 + .../js/configurations/DateViewSettings.d.ts | 10 + wwwroot/js/configurations/DateViewSettings.js | 2 + .../js/configurations/DateViewSettings.js.map | 1 + wwwroot/js/configurations/GridSettings.d.ts | 22 + wwwroot/js/configurations/GridSettings.js | 11 + wwwroot/js/configurations/GridSettings.js.map | 1 + .../js/configurations/ICalendarConfig.d.ts | 21 + wwwroot/js/configurations/ICalendarConfig.js | 2 + .../js/configurations/ICalendarConfig.js.map | 1 + .../js/configurations/TimeFormatConfig.d.ts | 10 + wwwroot/js/configurations/TimeFormatConfig.js | 2 + .../js/configurations/TimeFormatConfig.js.map | 1 + .../js/configurations/WorkWeekSettings.d.ts | 9 + wwwroot/js/configurations/WorkWeekSettings.js | 2 + .../js/configurations/WorkWeekSettings.js.map | 1 + wwwroot/js/constants/CoreEvents.d.ts | 37 + wwwroot/js/constants/CoreEvents.js | 48 + wwwroot/js/constants/CoreEvents.js.map | 1 + wwwroot/js/core/CalendarConfig.d.ts | 225 + wwwroot/js/core/CalendarConfig.js | 421 ++ wwwroot/js/core/CalendarConfig.js.map | 1 + wwwroot/js/core/EventBus.d.ts | 60 + wwwroot/js/core/EventBus.js | 158 + wwwroot/js/core/EventBus.js.map | 1 + .../js/datasources/DateColumnDataSource.d.ts | 55 + .../js/datasources/DateColumnDataSource.js | 94 + .../datasources/DateColumnDataSource.js.map | 1 + wwwroot/js/demo.js | 6489 +++++++++++++++++ wwwroot/js/edge-scroll.js | 104 + wwwroot/js/elements/SwpEventElement.d.ts | 98 + wwwroot/js/elements/SwpEventElement.js | 303 + wwwroot/js/elements/SwpEventElement.js.map | 1 + wwwroot/js/factories/CalendarTypeFactory.d.ts | 55 + wwwroot/js/factories/CalendarTypeFactory.js | 84 + .../js/factories/CalendarTypeFactory.js.map | 1 + wwwroot/js/factories/ManagerFactory.d.ts | 18 + wwwroot/js/factories/ManagerFactory.js | 60 + wwwroot/js/factories/ManagerFactory.js.map | 1 + .../all-day/AllDayCollapseService.d.ts | 45 + .../features/all-day/AllDayCollapseService.js | 168 + .../all-day/AllDayCollapseService.js.map | 1 + .../features/all-day/AllDayCoordinator.d.ts | 45 + .../js/features/all-day/AllDayCoordinator.js | 168 + .../features/all-day/AllDayCoordinator.js.map | 1 + .../js/features/all-day/AllDayDomReader.d.ts | 74 + .../js/features/all-day/AllDayDomReader.js | 175 + .../features/all-day/AllDayDomReader.js.map | 1 + .../features/all-day/AllDayDragService.d.ts | 50 + .../js/features/all-day/AllDayDragService.js | 183 + .../features/all-day/AllDayDragService.js.map | 1 + .../features/all-day/AllDayHeightService.d.ts | 26 + .../features/all-day/AllDayHeightService.js | 85 + .../all-day/AllDayHeightService.js.map | 1 + wwwroot/js/features/all-day/index.d.ts | 9 + wwwroot/js/features/all-day/index.js | 10 + wwwroot/js/features/all-day/index.js.map | 1 + .../all-day/utils/AllDayDomReader.d.ts | 74 + .../features/all-day/utils/AllDayDomReader.js | 175 + .../all-day/utils/AllDayDomReader.js.map | 1 + wwwroot/js/index.d.ts | 1 + wwwroot/js/index.js | 171 + wwwroot/js/index.js.map | 1 + wwwroot/js/interfaces/IManager.d.ts | 48 + wwwroot/js/interfaces/IManager.js | 2 + wwwroot/js/interfaces/IManager.js.map | 1 + wwwroot/js/managers/AllDayManager.d.ts | 91 + wwwroot/js/managers/AllDayManager.js | 528 ++ wwwroot/js/managers/AllDayManager.js.map | 1 + wwwroot/js/managers/CalendarManager.d.ts | 45 + wwwroot/js/managers/CalendarManager.js | 145 + wwwroot/js/managers/CalendarManager.js.map | 1 + wwwroot/js/managers/DragDropManager.d.ts | 222 + wwwroot/js/managers/DragDropManager.js | 626 ++ wwwroot/js/managers/DragDropManager.js.map | 1 + wwwroot/js/managers/DragHoverManager.d.ts | 31 + wwwroot/js/managers/DragHoverManager.js | 101 + wwwroot/js/managers/DragHoverManager.js.map | 1 + wwwroot/js/managers/EdgeScrollManager.d.ts | 30 + wwwroot/js/managers/EdgeScrollManager.js | 191 + wwwroot/js/managers/EdgeScrollManager.js.map | 1 + wwwroot/js/managers/EventFilterManager.d.ts | 32 + wwwroot/js/managers/EventFilterManager.js | 192 + wwwroot/js/managers/EventFilterManager.js.map | 1 + .../js/managers/EventLayoutCoordinator.d.ts | 78 + wwwroot/js/managers/EventLayoutCoordinator.js | 201 + .../js/managers/EventLayoutCoordinator.js.map | 1 + wwwroot/js/managers/EventManager.d.ts | 69 + wwwroot/js/managers/EventManager.js | 164 + wwwroot/js/managers/EventManager.js.map | 1 + wwwroot/js/managers/EventStackManager.d.ts | 91 + wwwroot/js/managers/EventStackManager.js | 217 + wwwroot/js/managers/EventStackManager.js.map | 1 + wwwroot/js/managers/GridManager.d.ts | 30 + wwwroot/js/managers/GridManager.js | 77 + wwwroot/js/managers/GridManager.js.map | 1 + wwwroot/js/managers/HeaderManager.d.ts | 32 + wwwroot/js/managers/HeaderManager.js | 103 + wwwroot/js/managers/HeaderManager.js.map | 1 + .../js/managers/NavigationButtonsManager.d.ts | 40 + .../js/managers/NavigationButtonsManager.js | 63 + .../managers/NavigationButtonsManager.js.map | 1 + wwwroot/js/managers/NavigationManager.d.ts | 32 + wwwroot/js/managers/NavigationManager.js | 188 + wwwroot/js/managers/NavigationManager.js.map | 1 + wwwroot/js/managers/ResizeHandleManager.d.ts | 42 + wwwroot/js/managers/ResizeHandleManager.js | 194 + .../js/managers/ResizeHandleManager.js.map | 1 + wwwroot/js/managers/ScrollManager.d.ts | 64 + wwwroot/js/managers/ScrollManager.js | 217 + wwwroot/js/managers/ScrollManager.js.map | 1 + .../managers/SimpleEventOverlapManager.d.ts | 80 + .../js/managers/SimpleEventOverlapManager.js | 399 + .../managers/SimpleEventOverlapManager.js.map | 1 + wwwroot/js/managers/ViewManager.d.ts | 23 + wwwroot/js/managers/ViewManager.js | 106 + wwwroot/js/managers/ViewManager.js.map | 1 + wwwroot/js/managers/ViewSelectorManager.d.ts | 70 + wwwroot/js/managers/ViewSelectorManager.js | 130 + .../js/managers/ViewSelectorManager.js.map | 1 + wwwroot/js/managers/WorkHoursManager.d.ts | 71 + wwwroot/js/managers/WorkHoursManager.js | 108 + wwwroot/js/managers/WorkHoursManager.js.map | 1 + .../js/managers/WorkweekPresetsManager.d.ts | 47 + wwwroot/js/managers/WorkweekPresetsManager.js | 95 + .../js/managers/WorkweekPresetsManager.js.map | 1 + wwwroot/js/renderers/AllDayEventRenderer.d.ts | 32 + wwwroot/js/renderers/AllDayEventRenderer.js | 97 + .../js/renderers/AllDayEventRenderer.js.map | 1 + wwwroot/js/renderers/ColumnRenderer.d.ts | 26 + wwwroot/js/renderers/ColumnRenderer.js | 44 + wwwroot/js/renderers/ColumnRenderer.js.map | 1 + wwwroot/js/renderers/DateHeaderRenderer.d.ts | 21 + wwwroot/js/renderers/DateHeaderRenderer.js | 35 + .../js/renderers/DateHeaderRenderer.js.map | 1 + wwwroot/js/renderers/EventRenderer.d.ts | 96 + wwwroot/js/renderers/EventRenderer.js | 296 + wwwroot/js/renderers/EventRenderer.js.map | 1 + .../js/renderers/EventRendererManager.d.ts | 55 + wwwroot/js/renderers/EventRendererManager.js | 264 + .../js/renderers/EventRendererManager.js.map | 1 + wwwroot/js/renderers/GridRenderer.d.ts | 180 + wwwroot/js/renderers/GridRenderer.js | 289 + wwwroot/js/renderers/GridRenderer.js.map | 1 + wwwroot/js/renderers/GridStyleManager.d.ts | 24 + wwwroot/js/renderers/GridStyleManager.js | 76 + wwwroot/js/renderers/GridStyleManager.js.map | 1 + wwwroot/js/renderers/HeaderRenderer.d.ts | 29 + wwwroot/js/renderers/HeaderRenderer.js | 56 + wwwroot/js/renderers/HeaderRenderer.js.map | 1 + wwwroot/js/renderers/NavigationRenderer.d.ts | 22 + wwwroot/js/renderers/NavigationRenderer.js | 68 + .../js/renderers/NavigationRenderer.js.map | 1 + wwwroot/js/renderers/WeekInfoRenderer.d.ts | 26 + wwwroot/js/renderers/WeekInfoRenderer.js | 75 + wwwroot/js/renderers/WeekInfoRenderer.js.map | 1 + .../js/repositories/ApiEventRepository.d.ts | 39 + wwwroot/js/repositories/ApiEventRepository.js | 115 + .../js/repositories/ApiEventRepository.js.map | 1 + wwwroot/js/repositories/IEventRepository.d.ts | 51 + wwwroot/js/repositories/IEventRepository.js | 2 + .../js/repositories/IEventRepository.js.map | 1 + .../IndexedDBEventRepository.d.ts | 47 + .../repositories/IndexedDBEventRepository.js | 127 + .../IndexedDBEventRepository.js.map | 1 + .../js/repositories/MockEventRepository.d.ts | 33 + .../js/repositories/MockEventRepository.js | 62 + .../repositories/MockEventRepository.js.map | 1 + wwwroot/js/storage/IndexedDBService.d.ts | 97 + wwwroot/js/storage/IndexedDBService.js | 340 + wwwroot/js/storage/IndexedDBService.js.map | 1 + wwwroot/js/storage/OperationQueue.d.ts | 55 + wwwroot/js/storage/OperationQueue.js | 96 + wwwroot/js/storage/OperationQueue.js.map | 1 + wwwroot/js/strategies/MonthViewStrategy.d.ts | 25 + wwwroot/js/strategies/MonthViewStrategy.js | 124 + .../js/strategies/MonthViewStrategy.js.map | 1 + wwwroot/js/strategies/ViewStrategy.d.ts | 58 + wwwroot/js/strategies/ViewStrategy.js | 6 + wwwroot/js/strategies/ViewStrategy.js.map | 1 + wwwroot/js/strategies/WeekViewStrategy.d.ts | 22 + wwwroot/js/strategies/WeekViewStrategy.js | 57 + wwwroot/js/strategies/WeekViewStrategy.js.map | 1 + wwwroot/js/types/CalendarTypes.d.ts | 56 + wwwroot/js/types/CalendarTypes.js | 3 + wwwroot/js/types/CalendarTypes.js.map | 1 + wwwroot/js/types/ColumnDataSource.d.ts | 17 + wwwroot/js/types/ColumnDataSource.js | 2 + wwwroot/js/types/ColumnDataSource.js.map | 1 + wwwroot/js/types/DragDropTypes.d.ts | 41 + wwwroot/js/types/DragDropTypes.js | 5 + wwwroot/js/types/DragDropTypes.js.map | 1 + wwwroot/js/types/EventPayloadMap.d.ts | 133 + wwwroot/js/types/EventPayloadMap.js | 6 + wwwroot/js/types/EventPayloadMap.js.map | 1 + wwwroot/js/types/EventTypes.d.ts | 81 + wwwroot/js/types/EventTypes.js | 5 + wwwroot/js/types/EventTypes.js.map | 1 + wwwroot/js/types/ManagerTypes.d.ts | 59 + wwwroot/js/types/ManagerTypes.js | 2 + wwwroot/js/types/ManagerTypes.js.map | 1 + wwwroot/js/utils/AllDayLayoutEngine.d.ts | 42 + wwwroot/js/utils/AllDayLayoutEngine.js | 108 + wwwroot/js/utils/AllDayLayoutEngine.js.map | 1 + wwwroot/js/utils/ColumnDetectionUtils.d.ts | 30 + wwwroot/js/utils/ColumnDetectionUtils.js | 87 + wwwroot/js/utils/ColumnDetectionUtils.js.map | 1 + wwwroot/js/utils/DateCalculator.d.ts | 149 + wwwroot/js/utils/DateCalculator.js | 260 + wwwroot/js/utils/DateCalculator.js.map | 1 + wwwroot/js/utils/DateService.d.ts | 254 + wwwroot/js/utils/DateService.js | 418 ++ wwwroot/js/utils/DateService.js.map | 1 + wwwroot/js/utils/OverlapDetector.d.ts | 33 + wwwroot/js/utils/OverlapDetector.js | 52 + wwwroot/js/utils/OverlapDetector.js.map | 1 + wwwroot/js/utils/PositionUtils.d.ts | 101 + wwwroot/js/utils/PositionUtils.js | 209 + wwwroot/js/utils/PositionUtils.js.map | 1 + wwwroot/js/utils/TimeFormatter.d.ts | 45 + wwwroot/js/utils/TimeFormatter.js | 92 + wwwroot/js/utils/TimeFormatter.js.map | 1 + wwwroot/js/utils/URLManager.d.ts | 29 + wwwroot/js/utils/URLManager.js | 76 + wwwroot/js/utils/URLManager.js.map | 1 + wwwroot/js/v2-demo.js | 6463 ++++++++++++++++ wwwroot/js/workers/SyncManager.d.ts | 78 + wwwroot/js/workers/SyncManager.js | 229 + wwwroot/js/workers/SyncManager.js.map | 1 + 268 files changed, 31970 insertions(+), 4 deletions(-) create mode 100644 wwwroot/js/calendar-min.js create mode 100644 wwwroot/js/calendar-v2.js create mode 100644 wwwroot/js/calendar.js create mode 100644 wwwroot/js/components/NavigationButtons.d.ts create mode 100644 wwwroot/js/components/NavigationButtons.js create mode 100644 wwwroot/js/components/NavigationButtons.js.map create mode 100644 wwwroot/js/components/ViewSelector.d.ts create mode 100644 wwwroot/js/components/ViewSelector.js create mode 100644 wwwroot/js/components/ViewSelector.js.map create mode 100644 wwwroot/js/components/WorkweekPresets.d.ts create mode 100644 wwwroot/js/components/WorkweekPresets.js create mode 100644 wwwroot/js/components/WorkweekPresets.js.map create mode 100644 wwwroot/js/configuration/CalendarConfig.d.ts create mode 100644 wwwroot/js/configuration/CalendarConfig.js create mode 100644 wwwroot/js/configuration/CalendarConfig.js.map create mode 100644 wwwroot/js/configuration/ConfigManager.d.ts create mode 100644 wwwroot/js/configuration/ConfigManager.js create mode 100644 wwwroot/js/configuration/ConfigManager.js.map create mode 100644 wwwroot/js/configuration/DateViewSettings.d.ts create mode 100644 wwwroot/js/configuration/DateViewSettings.js create mode 100644 wwwroot/js/configuration/DateViewSettings.js.map create mode 100644 wwwroot/js/configuration/GridSettings.d.ts create mode 100644 wwwroot/js/configuration/GridSettings.js create mode 100644 wwwroot/js/configuration/GridSettings.js.map create mode 100644 wwwroot/js/configuration/ICalendarConfig.d.ts create mode 100644 wwwroot/js/configuration/ICalendarConfig.js create mode 100644 wwwroot/js/configuration/ICalendarConfig.js.map create mode 100644 wwwroot/js/configuration/TimeFormatConfig.d.ts create mode 100644 wwwroot/js/configuration/TimeFormatConfig.js create mode 100644 wwwroot/js/configuration/TimeFormatConfig.js.map create mode 100644 wwwroot/js/configuration/WorkWeekSettings.d.ts create mode 100644 wwwroot/js/configuration/WorkWeekSettings.js create mode 100644 wwwroot/js/configuration/WorkWeekSettings.js.map create mode 100644 wwwroot/js/configurations/CalendarConfig.d.ts create mode 100644 wwwroot/js/configurations/CalendarConfig.js create mode 100644 wwwroot/js/configurations/CalendarConfig.js.map create mode 100644 wwwroot/js/configurations/ConfigManager.d.ts create mode 100644 wwwroot/js/configurations/ConfigManager.js create mode 100644 wwwroot/js/configurations/ConfigManager.js.map create mode 100644 wwwroot/js/configurations/DateViewSettings.d.ts create mode 100644 wwwroot/js/configurations/DateViewSettings.js create mode 100644 wwwroot/js/configurations/DateViewSettings.js.map create mode 100644 wwwroot/js/configurations/GridSettings.d.ts create mode 100644 wwwroot/js/configurations/GridSettings.js create mode 100644 wwwroot/js/configurations/GridSettings.js.map create mode 100644 wwwroot/js/configurations/ICalendarConfig.d.ts create mode 100644 wwwroot/js/configurations/ICalendarConfig.js create mode 100644 wwwroot/js/configurations/ICalendarConfig.js.map create mode 100644 wwwroot/js/configurations/TimeFormatConfig.d.ts create mode 100644 wwwroot/js/configurations/TimeFormatConfig.js create mode 100644 wwwroot/js/configurations/TimeFormatConfig.js.map create mode 100644 wwwroot/js/configurations/WorkWeekSettings.d.ts create mode 100644 wwwroot/js/configurations/WorkWeekSettings.js create mode 100644 wwwroot/js/configurations/WorkWeekSettings.js.map create mode 100644 wwwroot/js/constants/CoreEvents.d.ts create mode 100644 wwwroot/js/constants/CoreEvents.js create mode 100644 wwwroot/js/constants/CoreEvents.js.map create mode 100644 wwwroot/js/core/CalendarConfig.d.ts create mode 100644 wwwroot/js/core/CalendarConfig.js create mode 100644 wwwroot/js/core/CalendarConfig.js.map create mode 100644 wwwroot/js/core/EventBus.d.ts create mode 100644 wwwroot/js/core/EventBus.js create mode 100644 wwwroot/js/core/EventBus.js.map create mode 100644 wwwroot/js/datasources/DateColumnDataSource.d.ts create mode 100644 wwwroot/js/datasources/DateColumnDataSource.js create mode 100644 wwwroot/js/datasources/DateColumnDataSource.js.map create mode 100644 wwwroot/js/demo.js create mode 100644 wwwroot/js/edge-scroll.js create mode 100644 wwwroot/js/elements/SwpEventElement.d.ts create mode 100644 wwwroot/js/elements/SwpEventElement.js create mode 100644 wwwroot/js/elements/SwpEventElement.js.map create mode 100644 wwwroot/js/factories/CalendarTypeFactory.d.ts create mode 100644 wwwroot/js/factories/CalendarTypeFactory.js create mode 100644 wwwroot/js/factories/CalendarTypeFactory.js.map create mode 100644 wwwroot/js/factories/ManagerFactory.d.ts create mode 100644 wwwroot/js/factories/ManagerFactory.js create mode 100644 wwwroot/js/factories/ManagerFactory.js.map create mode 100644 wwwroot/js/features/all-day/AllDayCollapseService.d.ts create mode 100644 wwwroot/js/features/all-day/AllDayCollapseService.js create mode 100644 wwwroot/js/features/all-day/AllDayCollapseService.js.map create mode 100644 wwwroot/js/features/all-day/AllDayCoordinator.d.ts create mode 100644 wwwroot/js/features/all-day/AllDayCoordinator.js create mode 100644 wwwroot/js/features/all-day/AllDayCoordinator.js.map create mode 100644 wwwroot/js/features/all-day/AllDayDomReader.d.ts create mode 100644 wwwroot/js/features/all-day/AllDayDomReader.js create mode 100644 wwwroot/js/features/all-day/AllDayDomReader.js.map create mode 100644 wwwroot/js/features/all-day/AllDayDragService.d.ts create mode 100644 wwwroot/js/features/all-day/AllDayDragService.js create mode 100644 wwwroot/js/features/all-day/AllDayDragService.js.map create mode 100644 wwwroot/js/features/all-day/AllDayHeightService.d.ts create mode 100644 wwwroot/js/features/all-day/AllDayHeightService.js create mode 100644 wwwroot/js/features/all-day/AllDayHeightService.js.map create mode 100644 wwwroot/js/features/all-day/index.d.ts create mode 100644 wwwroot/js/features/all-day/index.js create mode 100644 wwwroot/js/features/all-day/index.js.map create mode 100644 wwwroot/js/features/all-day/utils/AllDayDomReader.d.ts create mode 100644 wwwroot/js/features/all-day/utils/AllDayDomReader.js create mode 100644 wwwroot/js/features/all-day/utils/AllDayDomReader.js.map create mode 100644 wwwroot/js/index.d.ts create mode 100644 wwwroot/js/index.js create mode 100644 wwwroot/js/index.js.map create mode 100644 wwwroot/js/interfaces/IManager.d.ts create mode 100644 wwwroot/js/interfaces/IManager.js create mode 100644 wwwroot/js/interfaces/IManager.js.map create mode 100644 wwwroot/js/managers/AllDayManager.d.ts create mode 100644 wwwroot/js/managers/AllDayManager.js create mode 100644 wwwroot/js/managers/AllDayManager.js.map create mode 100644 wwwroot/js/managers/CalendarManager.d.ts create mode 100644 wwwroot/js/managers/CalendarManager.js create mode 100644 wwwroot/js/managers/CalendarManager.js.map create mode 100644 wwwroot/js/managers/DragDropManager.d.ts create mode 100644 wwwroot/js/managers/DragDropManager.js create mode 100644 wwwroot/js/managers/DragDropManager.js.map create mode 100644 wwwroot/js/managers/DragHoverManager.d.ts create mode 100644 wwwroot/js/managers/DragHoverManager.js create mode 100644 wwwroot/js/managers/DragHoverManager.js.map create mode 100644 wwwroot/js/managers/EdgeScrollManager.d.ts create mode 100644 wwwroot/js/managers/EdgeScrollManager.js create mode 100644 wwwroot/js/managers/EdgeScrollManager.js.map create mode 100644 wwwroot/js/managers/EventFilterManager.d.ts create mode 100644 wwwroot/js/managers/EventFilterManager.js create mode 100644 wwwroot/js/managers/EventFilterManager.js.map create mode 100644 wwwroot/js/managers/EventLayoutCoordinator.d.ts create mode 100644 wwwroot/js/managers/EventLayoutCoordinator.js create mode 100644 wwwroot/js/managers/EventLayoutCoordinator.js.map create mode 100644 wwwroot/js/managers/EventManager.d.ts create mode 100644 wwwroot/js/managers/EventManager.js create mode 100644 wwwroot/js/managers/EventManager.js.map create mode 100644 wwwroot/js/managers/EventStackManager.d.ts create mode 100644 wwwroot/js/managers/EventStackManager.js create mode 100644 wwwroot/js/managers/EventStackManager.js.map create mode 100644 wwwroot/js/managers/GridManager.d.ts create mode 100644 wwwroot/js/managers/GridManager.js create mode 100644 wwwroot/js/managers/GridManager.js.map create mode 100644 wwwroot/js/managers/HeaderManager.d.ts create mode 100644 wwwroot/js/managers/HeaderManager.js create mode 100644 wwwroot/js/managers/HeaderManager.js.map create mode 100644 wwwroot/js/managers/NavigationButtonsManager.d.ts create mode 100644 wwwroot/js/managers/NavigationButtonsManager.js create mode 100644 wwwroot/js/managers/NavigationButtonsManager.js.map create mode 100644 wwwroot/js/managers/NavigationManager.d.ts create mode 100644 wwwroot/js/managers/NavigationManager.js create mode 100644 wwwroot/js/managers/NavigationManager.js.map create mode 100644 wwwroot/js/managers/ResizeHandleManager.d.ts create mode 100644 wwwroot/js/managers/ResizeHandleManager.js create mode 100644 wwwroot/js/managers/ResizeHandleManager.js.map create mode 100644 wwwroot/js/managers/ScrollManager.d.ts create mode 100644 wwwroot/js/managers/ScrollManager.js create mode 100644 wwwroot/js/managers/ScrollManager.js.map create mode 100644 wwwroot/js/managers/SimpleEventOverlapManager.d.ts create mode 100644 wwwroot/js/managers/SimpleEventOverlapManager.js create mode 100644 wwwroot/js/managers/SimpleEventOverlapManager.js.map create mode 100644 wwwroot/js/managers/ViewManager.d.ts create mode 100644 wwwroot/js/managers/ViewManager.js create mode 100644 wwwroot/js/managers/ViewManager.js.map create mode 100644 wwwroot/js/managers/ViewSelectorManager.d.ts create mode 100644 wwwroot/js/managers/ViewSelectorManager.js create mode 100644 wwwroot/js/managers/ViewSelectorManager.js.map create mode 100644 wwwroot/js/managers/WorkHoursManager.d.ts create mode 100644 wwwroot/js/managers/WorkHoursManager.js create mode 100644 wwwroot/js/managers/WorkHoursManager.js.map create mode 100644 wwwroot/js/managers/WorkweekPresetsManager.d.ts create mode 100644 wwwroot/js/managers/WorkweekPresetsManager.js create mode 100644 wwwroot/js/managers/WorkweekPresetsManager.js.map create mode 100644 wwwroot/js/renderers/AllDayEventRenderer.d.ts create mode 100644 wwwroot/js/renderers/AllDayEventRenderer.js create mode 100644 wwwroot/js/renderers/AllDayEventRenderer.js.map create mode 100644 wwwroot/js/renderers/ColumnRenderer.d.ts create mode 100644 wwwroot/js/renderers/ColumnRenderer.js create mode 100644 wwwroot/js/renderers/ColumnRenderer.js.map create mode 100644 wwwroot/js/renderers/DateHeaderRenderer.d.ts create mode 100644 wwwroot/js/renderers/DateHeaderRenderer.js create mode 100644 wwwroot/js/renderers/DateHeaderRenderer.js.map create mode 100644 wwwroot/js/renderers/EventRenderer.d.ts create mode 100644 wwwroot/js/renderers/EventRenderer.js create mode 100644 wwwroot/js/renderers/EventRenderer.js.map create mode 100644 wwwroot/js/renderers/EventRendererManager.d.ts create mode 100644 wwwroot/js/renderers/EventRendererManager.js create mode 100644 wwwroot/js/renderers/EventRendererManager.js.map create mode 100644 wwwroot/js/renderers/GridRenderer.d.ts create mode 100644 wwwroot/js/renderers/GridRenderer.js create mode 100644 wwwroot/js/renderers/GridRenderer.js.map create mode 100644 wwwroot/js/renderers/GridStyleManager.d.ts create mode 100644 wwwroot/js/renderers/GridStyleManager.js create mode 100644 wwwroot/js/renderers/GridStyleManager.js.map create mode 100644 wwwroot/js/renderers/HeaderRenderer.d.ts create mode 100644 wwwroot/js/renderers/HeaderRenderer.js create mode 100644 wwwroot/js/renderers/HeaderRenderer.js.map create mode 100644 wwwroot/js/renderers/NavigationRenderer.d.ts create mode 100644 wwwroot/js/renderers/NavigationRenderer.js create mode 100644 wwwroot/js/renderers/NavigationRenderer.js.map create mode 100644 wwwroot/js/renderers/WeekInfoRenderer.d.ts create mode 100644 wwwroot/js/renderers/WeekInfoRenderer.js create mode 100644 wwwroot/js/renderers/WeekInfoRenderer.js.map create mode 100644 wwwroot/js/repositories/ApiEventRepository.d.ts create mode 100644 wwwroot/js/repositories/ApiEventRepository.js create mode 100644 wwwroot/js/repositories/ApiEventRepository.js.map create mode 100644 wwwroot/js/repositories/IEventRepository.d.ts create mode 100644 wwwroot/js/repositories/IEventRepository.js create mode 100644 wwwroot/js/repositories/IEventRepository.js.map create mode 100644 wwwroot/js/repositories/IndexedDBEventRepository.d.ts create mode 100644 wwwroot/js/repositories/IndexedDBEventRepository.js create mode 100644 wwwroot/js/repositories/IndexedDBEventRepository.js.map create mode 100644 wwwroot/js/repositories/MockEventRepository.d.ts create mode 100644 wwwroot/js/repositories/MockEventRepository.js create mode 100644 wwwroot/js/repositories/MockEventRepository.js.map create mode 100644 wwwroot/js/storage/IndexedDBService.d.ts create mode 100644 wwwroot/js/storage/IndexedDBService.js create mode 100644 wwwroot/js/storage/IndexedDBService.js.map create mode 100644 wwwroot/js/storage/OperationQueue.d.ts create mode 100644 wwwroot/js/storage/OperationQueue.js create mode 100644 wwwroot/js/storage/OperationQueue.js.map create mode 100644 wwwroot/js/strategies/MonthViewStrategy.d.ts create mode 100644 wwwroot/js/strategies/MonthViewStrategy.js create mode 100644 wwwroot/js/strategies/MonthViewStrategy.js.map create mode 100644 wwwroot/js/strategies/ViewStrategy.d.ts create mode 100644 wwwroot/js/strategies/ViewStrategy.js create mode 100644 wwwroot/js/strategies/ViewStrategy.js.map create mode 100644 wwwroot/js/strategies/WeekViewStrategy.d.ts create mode 100644 wwwroot/js/strategies/WeekViewStrategy.js create mode 100644 wwwroot/js/strategies/WeekViewStrategy.js.map create mode 100644 wwwroot/js/types/CalendarTypes.d.ts create mode 100644 wwwroot/js/types/CalendarTypes.js create mode 100644 wwwroot/js/types/CalendarTypes.js.map create mode 100644 wwwroot/js/types/ColumnDataSource.d.ts create mode 100644 wwwroot/js/types/ColumnDataSource.js create mode 100644 wwwroot/js/types/ColumnDataSource.js.map create mode 100644 wwwroot/js/types/DragDropTypes.d.ts create mode 100644 wwwroot/js/types/DragDropTypes.js create mode 100644 wwwroot/js/types/DragDropTypes.js.map create mode 100644 wwwroot/js/types/EventPayloadMap.d.ts create mode 100644 wwwroot/js/types/EventPayloadMap.js create mode 100644 wwwroot/js/types/EventPayloadMap.js.map create mode 100644 wwwroot/js/types/EventTypes.d.ts create mode 100644 wwwroot/js/types/EventTypes.js create mode 100644 wwwroot/js/types/EventTypes.js.map create mode 100644 wwwroot/js/types/ManagerTypes.d.ts create mode 100644 wwwroot/js/types/ManagerTypes.js create mode 100644 wwwroot/js/types/ManagerTypes.js.map create mode 100644 wwwroot/js/utils/AllDayLayoutEngine.d.ts create mode 100644 wwwroot/js/utils/AllDayLayoutEngine.js create mode 100644 wwwroot/js/utils/AllDayLayoutEngine.js.map create mode 100644 wwwroot/js/utils/ColumnDetectionUtils.d.ts create mode 100644 wwwroot/js/utils/ColumnDetectionUtils.js create mode 100644 wwwroot/js/utils/ColumnDetectionUtils.js.map create mode 100644 wwwroot/js/utils/DateCalculator.d.ts create mode 100644 wwwroot/js/utils/DateCalculator.js create mode 100644 wwwroot/js/utils/DateCalculator.js.map create mode 100644 wwwroot/js/utils/DateService.d.ts create mode 100644 wwwroot/js/utils/DateService.js create mode 100644 wwwroot/js/utils/DateService.js.map create mode 100644 wwwroot/js/utils/OverlapDetector.d.ts create mode 100644 wwwroot/js/utils/OverlapDetector.js create mode 100644 wwwroot/js/utils/OverlapDetector.js.map create mode 100644 wwwroot/js/utils/PositionUtils.d.ts create mode 100644 wwwroot/js/utils/PositionUtils.js create mode 100644 wwwroot/js/utils/PositionUtils.js.map create mode 100644 wwwroot/js/utils/TimeFormatter.d.ts create mode 100644 wwwroot/js/utils/TimeFormatter.js create mode 100644 wwwroot/js/utils/TimeFormatter.js.map create mode 100644 wwwroot/js/utils/URLManager.d.ts create mode 100644 wwwroot/js/utils/URLManager.js create mode 100644 wwwroot/js/utils/URLManager.js.map create mode 100644 wwwroot/js/v2-demo.js create mode 100644 wwwroot/js/workers/SyncManager.d.ts create mode 100644 wwwroot/js/workers/SyncManager.js create mode 100644 wwwroot/js/workers/SyncManager.js.map diff --git a/.gitignore b/.gitignore index de20219..9bbe200 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ # Build outputs bin/ obj/ -wwwroot/js/ # Node modules node_modules/ @@ -30,6 +29,5 @@ Thumbs.db *.suo *.userosscache *.sln.docstates -js/ - -packages/calendar/dist/ + +packages/calendar/dist/ diff --git a/wwwroot/js/calendar-min.js b/wwwroot/js/calendar-min.js new file mode 100644 index 0000000..322988f --- /dev/null +++ b/wwwroot/js/calendar-min.js @@ -0,0 +1,26 @@ +var Lt=Object.create;var vt=Object.defineProperty;var $t=Object.getOwnPropertyDescriptor;var Ht=Object.getOwnPropertyNames;var Bt=Object.getPrototypeOf,Wt=Object.prototype.hasOwnProperty;var se=(o,e)=>()=>(e||o((e={exports:{}}).exports,e),e.exports);var Pt=(o,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of Ht(e))!Wt.call(o,r)&&r!==t&&vt(o,r,{get:()=>e[r],enumerable:!(n=$t(e,r))||n.enumerable});return o};var ie=(o,e,t)=>(t=o!=null?Lt(Bt(o)):{},Pt(e||!o||!o.__esModule?vt(t,"default",{value:o,enumerable:!0}):t,o));var Et=se((rt,st)=>{(function(o,e){typeof rt=="object"&&typeof st<"u"?st.exports=e():typeof define=="function"&&define.amd?define(e):(o=typeof globalThis<"u"?globalThis:o||self).dayjs=e()})(rt,function(){"use strict";var o=1e3,e=6e4,t=36e5,n="millisecond",r="second",s="minute",i="hour",a="day",c="week",d="month",g="quarter",w="year",E="date",m="Invalid Date",u=/^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/,D=/\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,C={name:"en",weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),ordinal:function(S){var f=["th","st","nd","rd"],h=S%100;return"["+S+(f[(h-20)%10]||f[h]||f[0])+"]"}},k=function(S,f,h){var y=String(S);return!y||y.length>=f?S:""+Array(f+1-y.length).join(h)+S},l={s:k,z:function(S){var f=-S.utcOffset(),h=Math.abs(f),y=Math.floor(h/60),p=h%60;return(f<=0?"+":"-")+k(y,2,"0")+":"+k(p,2,"0")},m:function S(f,h){if(f.date()1)return S(M[0])}else{var R=f.name;W[R]=f,p=R}return!y&&p&&(O=p),p||!y&&O},x=function(S,f){if(N(S))return S.clone();var h=typeof f=="object"?f:{};return h.date=S,h.args=arguments,new Y(h)},A=l;A.l=$,A.i=N,A.w=function(S,f){return x(S,{locale:f.$L,utc:f.$u,x:f.$x,$offset:f.$offset})};var Y=function(){function S(h){this.$L=$(h.locale,null,!0),this.parse(h),this.$x=this.$x||h.x||{},this[P]=!0}var f=S.prototype;return f.parse=function(h){this.$d=function(y){var p=y.date,T=y.utc;if(p===null)return new Date(NaN);if(A.u(p))return new Date;if(p instanceof Date)return new Date(p);if(typeof p=="string"&&!/Z$/i.test(p)){var M=p.match(u);if(M){var R=M[2]-1||0,L=(M[7]||"0").substring(0,3);return T?new Date(Date.UTC(M[1],R,M[3]||1,M[4]||0,M[5]||0,M[6]||0,L)):new Date(M[1],R,M[3]||1,M[4]||0,M[5]||0,M[6]||0,L)}}return new Date(p)}(h),this.init()},f.init=function(){var h=this.$d;this.$y=h.getFullYear(),this.$M=h.getMonth(),this.$D=h.getDate(),this.$W=h.getDay(),this.$H=h.getHours(),this.$m=h.getMinutes(),this.$s=h.getSeconds(),this.$ms=h.getMilliseconds()},f.$utils=function(){return A},f.isValid=function(){return this.$d.toString()!==m},f.isSame=function(h,y){var p=x(h);return this.startOf(y)<=p&&p<=this.endOf(y)},f.isAfter=function(h,y){return x(h){(function(o,e){typeof it=="object"&&typeof at<"u"?at.exports=e():typeof define=="function"&&define.amd?define(e):(o=typeof globalThis<"u"?globalThis:o||self).dayjs_plugin_utc=e()})(it,function(){"use strict";var o="minute",e=/[+-]\d\d(?::?\d\d)?/g,t=/([+-]|\d\d)/g;return function(n,r,s){var i=r.prototype;s.utc=function(m){var u={date:m,utc:!0,args:arguments};return new r(u)},i.utc=function(m){var u=s(this.toDate(),{locale:this.$L,utc:!0});return m?u.add(this.utcOffset(),o):u},i.local=function(){return s(this.toDate(),{locale:this.$L,utc:!1})};var a=i.parse;i.parse=function(m){m.utc&&(this.$u=!0),this.$utils().u(m.$offset)||(this.$offset=m.$offset),a.call(this,m)};var c=i.init;i.init=function(){if(this.$u){var m=this.$d;this.$y=m.getUTCFullYear(),this.$M=m.getUTCMonth(),this.$D=m.getUTCDate(),this.$W=m.getUTCDay(),this.$H=m.getUTCHours(),this.$m=m.getUTCMinutes(),this.$s=m.getUTCSeconds(),this.$ms=m.getUTCMilliseconds()}else c.call(this)};var d=i.utcOffset;i.utcOffset=function(m,u){var D=this.$utils().u;if(D(m))return this.$u?0:D(this.$offset)?d.call(this):this.$offset;if(typeof m=="string"&&(m=function(O){O===void 0&&(O="");var W=O.match(e);if(!W)return null;var P=(""+W[0]).match(t)||["-",0,0],N=P[0],$=60*+P[1]+ +P[2];return $===0?0:N==="+"?$:-$}(m),m===null))return this;var C=Math.abs(m)<=16?60*m:m;if(C===0)return this.utc(u);var k=this.clone();if(u)return k.$offset=C,k.$u=!1,k;var l=this.$u?this.toDate().getTimezoneOffset():-1*this.utcOffset();return(k=this.local().add(C+l,o)).$offset=C,k.$x.$localOffset=l,k};var g=i.format;i.format=function(m){var u=m||(this.$u?"YYYY-MM-DDTHH:mm:ss[Z]":"");return g.call(this,u)},i.valueOf=function(){var m=this.$utils().u(this.$offset)?0:this.$offset+(this.$x.$localOffset||this.$d.getTimezoneOffset());return this.$d.valueOf()-6e4*m},i.isUTC=function(){return!!this.$u},i.toISOString=function(){return this.toDate().toISOString()},i.toString=function(){return this.toDate().toUTCString()};var w=i.toDate;i.toDate=function(m){return m==="s"&&this.$offset?s(this.format("YYYY-MM-DD HH:mm:ss:SSS")).toDate():w.call(this)};var E=i.diff;i.diff=function(m,u,D){if(m&&this.$u===m.$u)return E.call(this,m,u,D);var C=this.local(),k=s(m).local();return E.call(C,k,u,D)}}})});var St=se((ot,lt)=>{(function(o,e){typeof ot=="object"&&typeof lt<"u"?lt.exports=e():typeof define=="function"&&define.amd?define(e):(o=typeof globalThis<"u"?globalThis:o||self).dayjs_plugin_timezone=e()})(ot,function(){"use strict";var o={year:0,month:1,day:2,hour:3,minute:4,second:5},e={};return function(t,n,r){var s,i=function(g,w,E){E===void 0&&(E={});var m=new Date(g),u=function(D,C){C===void 0&&(C={});var k=C.timeZoneName||"short",l=D+"|"+k,O=e[l];return O||(O=new Intl.DateTimeFormat("en-US",{hour12:!1,timeZone:D,year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",timeZoneName:k}),e[l]=O),O}(w,E);return u.formatToParts(m)},a=function(g,w){for(var E=i(g,w),m=[],u=0;u=0&&(m[l]=parseInt(k,10))}var O=m[3],W=O===24?0:O,P=m[0]+"-"+m[1]+"-"+m[2]+" "+W+":"+m[4]+":"+m[5]+":000",N=+g;return(r.utc(P).valueOf()-(N-=N%1e3))/6e4},c=n.prototype;c.tz=function(g,w){g===void 0&&(g=s);var E,m=this.utcOffset(),u=this.toDate(),D=u.toLocaleString("en-US",{timeZone:g}),C=Math.round((u-new Date(D))/1e3/60),k=15*-Math.round(u.getTimezoneOffset()/15)-C;if(!Number(k))E=this.utcOffset(0,w);else if(E=r(D,{locale:this.$L}).$set("millisecond",this.$ms).utcOffset(k,!0),w){var l=E.utcOffset();E=E.add(m-l,"minute")}return E.$x.$timezone=g,E},c.offsetName=function(g){var w=this.$x.$timezone||r.tz.guess(),E=i(this.valueOf(),w,{timeZoneName:g}).find(function(m){return m.type.toLowerCase()==="timezonename"});return E&&E.value};var d=c.startOf;c.startOf=function(g,w){if(!this.$x||!this.$x.$timezone)return d.call(this,g,w);var E=r(this.format("YYYY-MM-DD HH:mm:ss:SSS"),{locale:this.$L});return d.call(E,g,w).tz(this.$x.$timezone,!0)},r.tz=function(g,w,E){var m=E&&w,u=E||w||s,D=a(+r(),u);if(typeof g!="string")return r(g).tz(u);var C=function(W,P,N){var $=W-60*P*1e3,x=a($,N);if(P===x)return[$,P];var A=a($-=60*(x-P)*1e3,N);return x===A?[$,x]:[W-60*Math.min(x,A)*1e3,Math.max(x,A)]}(r.utc(g,m).valueOf(),D,u),k=C[0],l=C[1],O=r(k).utcOffset(l);return O.$x.$timezone=u,O},r.tz.guess=function(){return Intl.DateTimeFormat().resolvedOptions().timeZone},r.tz.setDefault=function(g){s=g}}})});var wt=se((ct,dt)=>{(function(o,e){typeof ct=="object"&&typeof dt<"u"?dt.exports=e():typeof define=="function"&&define.amd?define(e):(o=typeof globalThis<"u"?globalThis:o||self).dayjs_plugin_isoWeek=e()})(ct,function(){"use strict";var o="day";return function(e,t,n){var r=function(a){return a.add(4-a.isoWeekday(),o)},s=t.prototype;s.isoWeekYear=function(){return r(this).year()},s.isoWeek=function(a){if(!this.$utils().u(a))return this.add(7*(a-this.isoWeek()),o);var c,d,g,w,E=r(this),m=(c=this.isoWeekYear(),d=this.$u,g=(d?n.utc:n)().year(c).startOf("year"),w=4-g.isoWeekday(),g.isoWeekday()>4&&(w+=7),g.add(w,o));return E.diff(m,"week")+1},s.isoWeekday=function(a){return this.$utils().u(a)?this.day()||7:this.day(this.day()%7?a:a-7)};var i=s.startOf;s.startOf=function(a,c){var d=this.$utils(),g=!!d.u(c)||c;return d.p(a)==="isoweek"?g?this.date(this.date()-(this.isoWeekday()-1)).startOf("day"):this.date(this.date()-1-(this.isoWeekday()-1)+7).endOf("day"):i.bind(this)(a,c)}}})});var Ct=se((ut,ht)=>{(function(o,e){typeof ut=="object"&&typeof ht<"u"?ht.exports=e():typeof define=="function"&&define.amd?define(e):(o=typeof globalThis<"u"?globalThis:o||self).dayjs_plugin_customParseFormat=e()})(ut,function(){"use strict";var o={LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},e=/(\[[^[]*\])|([-_:/.,()\s]+)|(A|a|Q|YYYY|YY?|ww?|MM?M?M?|Do|DD?|hh?|HH?|mm?|ss?|S{1,3}|z|ZZ?)/g,t=/\d/,n=/\d\d/,r=/\d\d?/,s=/\d*[^-_:/,()\s\d]+/,i={},a=function(u){return(u=+u)+(u>68?1900:2e3)},c=function(u){return function(D){this[u]=+D}},d=[/[+-]\d\d:?(\d\d)?|Z/,function(u){(this.zone||(this.zone={})).offset=function(D){if(!D||D==="Z")return 0;var C=D.match(/([+-]|\d\d)/g),k=60*C[1]+(+C[2]||0);return k===0?0:C[0]==="+"?-k:k}(u)}],g=function(u){var D=i[u];return D&&(D.indexOf?D:D.s.concat(D.f))},w=function(u,D){var C,k=i.meridiem;if(k){for(var l=1;l<=24;l+=1)if(u.indexOf(k(l,0,D))>-1){C=l>12;break}}else C=u===(D?"pm":"PM");return C},E={A:[s,function(u){this.afternoon=w(u,!1)}],a:[s,function(u){this.afternoon=w(u,!0)}],Q:[t,function(u){this.month=3*(u-1)+1}],S:[t,function(u){this.milliseconds=100*+u}],SS:[n,function(u){this.milliseconds=10*+u}],SSS:[/\d{3}/,function(u){this.milliseconds=+u}],s:[r,c("seconds")],ss:[r,c("seconds")],m:[r,c("minutes")],mm:[r,c("minutes")],H:[r,c("hours")],h:[r,c("hours")],HH:[r,c("hours")],hh:[r,c("hours")],D:[r,c("day")],DD:[n,c("day")],Do:[s,function(u){var D=i.ordinal,C=u.match(/\d+/);if(this.day=C[0],D)for(var k=1;k<=31;k+=1)D(k).replace(/\[|\]/g,"")===u&&(this.day=k)}],w:[r,c("week")],ww:[n,c("week")],M:[r,c("month")],MM:[n,c("month")],MMM:[s,function(u){var D=g("months"),C=(g("monthsShort")||D.map(function(k){return k.slice(0,3)})).indexOf(u)+1;if(C<1)throw new Error;this.month=C%12||C}],MMMM:[s,function(u){var D=g("months").indexOf(u)+1;if(D<1)throw new Error;this.month=D%12||D}],Y:[/[+-]?\d+/,c("year")],YY:[n,function(u){this.year=a(u)}],YYYY:[/\d{4}/,c("year")],Z:d,ZZ:d};function m(u){var D,C;D=u,C=i&&i.formats;for(var k=(u=D.replace(/(\[[^\]]+])|(LTS?|l{1,4}|L{1,4})/g,function(x,A,Y){var z=Y&&Y.toUpperCase();return A||C[Y]||o[Y]||C[z].replace(/(\[[^\]]+])|(MMMM|MM|DD|dddd)/g,function(S,f,h){return f||h.slice(1)})})).match(e),l=k.length,O=0;O-1)return new Date((p==="X"?1e3:1)*y);var R=m(p)(y),L=R.year,H=R.month,F=R.day,q=R.hours,ee=R.minutes,Z=R.seconds,re=R.milliseconds,j=R.zone,_=R.week,G=new Date,X=F||(L||H?1:G.getDate()),te=L||G.getFullYear(),ve=0;L&&!H||(ve=H>0?H-1:G.getMonth());var ye,Qe=q||0,Ze=ee||0,Xe=Z||0,Ke=re||0;return j?new Date(Date.UTC(te,ve,X,Qe,Ze,Xe,Ke+60*j.offset*1e3)):T?new Date(Date.UTC(te,ve,X,Qe,Ze,Xe,Ke)):(ye=new Date(te,ve,X,Qe,Ze,Xe,Ke),_&&(ye=M(ye).week(_).toDate()),ye)}catch{return new Date("")}}(W,$,P,C),this.init(),z&&z!==!0&&(this.$L=this.locale(z).$L),Y&&W!=this.format($)&&(this.$d=new Date("")),i={}}else if($ instanceof Array)for(var S=$.length,f=1;f<=S;f+=1){N[1]=$[f-1];var h=C.apply(this,N);if(h.isValid()){this.$d=h.$d,this.$L=h.$L,this.init();break}f===S&&(this.$d=new Date(""))}else l.call(this,O)}}})});var Tt=se((gt,mt)=>{(function(o,e){typeof gt=="object"&&typeof mt<"u"?mt.exports=e():typeof define=="function"&&define.amd?define(e):(o=typeof globalThis<"u"?globalThis:o||self).dayjs_plugin_isSameOrAfter=e()})(gt,function(){"use strict";return function(o,e){e.prototype.isSameOrAfter=function(t,n){return this.isSame(t,n)||this.isAfter(t,n)}}})});var kt=se((ft,pt)=>{(function(o,e){typeof ft=="object"&&typeof pt<"u"?pt.exports=e():typeof define=="function"&&define.amd?define(e):(o=typeof globalThis<"u"?globalThis:o||self).dayjs_plugin_isSameOrBefore=e()})(ft,function(){"use strict";return function(o,e){e.prototype.isSameOrBefore=function(t,n){return this.isSame(t,n)||this.isBefore(t,n)}}})});var Nt=0;function ae(o){let e=++Nt;return{symbol:Symbol(o?`Token(${o})`:`Token#${e}`),description:o,toString(){return o?`Token<${o}>`:`Token<#${e}>`}}}var de=class extends Error{constructor(e){super(e),this.name="ContainerError"}},ue=class extends de{constructor(e,t=[]){let n=t.length>0?` + Dependency path: ${t.join(" -> ")}`:"";super(`Token "${e}" is not bound or registered in the container.${n}`),this.name="BindingNotFoundError"}},he=class extends de{constructor(e){super(`Circular dependency detected: ${e.join(" -> ")}`),this.name="CircularDependencyError"}};var yt=new WeakMap;function Ft(o){let e=yt.get(o);if(e)return e;let t=o.toString(),n=t.match(/constructor\s*\(([^)]*)\)/)||t.match(/^[^(]*\(([^)]*)\)/);if(!n||!n[1])return[];let r=n[1].split(",").map(s=>s.trim()).filter(s=>s.length>0).map(s=>{let i=s.split(/[:=]/)[0].trim();return i=i.replace(/^((public|private|protected|readonly)\s+)+/,""),i.includes("{")||i.includes("[")?null:i}).filter(s=>s!==null);return yt.set(o,r),r}function _t(o,e,t){if(!t.map)throw new Error("AutoWire map strategy requires options.map to be defined");let n=Ft(o),r=[];for(let s of n){let i=t.map[s];if(i===void 0){if(t.strict)throw new Error(`Cannot resolve parameter "${s}" on ${o.name}. Not found in autowire map. Add it to the map: .autoWire({ map: { ${s}: ... } })`);r.push(void 0);continue}typeof i=="function"?r.push(i(e)):r.push(e.resolve(i))}return r}function Yt(o,e,t){if(!t.mapResolvers||t.mapResolvers.length===0)return[];let n=[];for(let r=0;r0?Yt(o,e,n):n.map&&Object.keys(n.map).length>0?_t(o,e,n):[]}var le=class{constructor(e,t){this.registrations=t,this.configs=[],this.defaultLifetime="singleton",this.pending=e}as(e){if(e&&typeof e=="object"&&"symbol"in e){let t={token:e,type:this.pending.type,value:this.pending.value,factory:this.pending.factory,constructor:this.pending.constructor,lifetime:this.defaultLifetime};return this.configs.push(t),this.registrations.push(t),this}else{let t={token:null,type:this.pending.type,value:this.pending.value,factory:this.pending.factory,constructor:this.pending.constructor,lifetime:this.defaultLifetime,interfaceType:e};return this.configs.push(t),this.registrations.push(t),this}}asDefaultInterface(e){return this.as("TInterface",e),this.asDefault()}asKeyedInterface(e,t){return this.as("TInterface",t),this.keyed(e)}asImplementedInterfaces(e){if(e.length===0)return this;if(this.configs.length>0){for(let n of this.configs)n.lifetime="singleton",n.additionalTokens=n.additionalTokens||[],n.additionalTokens.push(...e);return this}let t={token:e[0],type:this.pending.type,value:this.pending.value,factory:this.pending.factory,constructor:this.pending.constructor,lifetime:"singleton"};this.configs.push(t),this.registrations.push(t);for(let n=1;ni.resolve(n),{lifetime:t.lifetime}),r.add(s)}build(){let e=this.baseContainer.createChild();this.resolveInterfaceTokens(e);let t=new Set,n=new Map,r=new Map,s=new Map,i=this.identifyNonDefaultTokens();for(let a of this.registrations){if(this.shouldSkipRegistration(a,i,t))continue;let c=this.createBindingToken(a,n,r,s);this.applyRegistration(e,{...a,token:c}),t.add(a.token),this.registerAdditionalInterfaces(e,a,c,t)}return e.__namedRegistrations=n,e.__keyedRegistrations=r,e.__multiRegistrations=s,e}analyzeConstructor(e){let t=e.toString();return{hasDependencies:/constructor\s*\([^)]+\)/.test(t)}}createOptimizedFactory(e,t,n){if(t.lifetime==="singleton"){let r=new t.constructor;e.bindValue(t.token,r)}else if(t.lifetime==="transient"){let r=t.constructor,s=()=>new r;e.fastTransientCache.set(t.token,s),e.bindFactory(t.token,s,n)}else{let r=()=>new t.constructor;e.bindFactory(t.token,r,n)}}createAutoWireFactory(e,t,n){let r=s=>{let i=Je(t.constructor,s,t.autowireOptions);return new t.constructor(...i)};e.bindFactory(t.token,r,n)}createParameterFactory(e,t,n){let r=()=>{let s=Object.values(t.parameterValues);return new t.constructor(...s)};e.bindFactory(t.token,r,n)}applyTypeRegistration(e,t,n){let{hasDependencies:r}=this.analyzeConstructor(t.constructor);if(!r&&!t.autowireOptions&&!t.parameterValues){this.createOptimizedFactory(e,t,n);return}if(t.autowireOptions){this.createAutoWireFactory(e,t,n);return}if(t.parameterValues){this.createParameterFactory(e,t,n);return}if(r){let i=t.constructor.name||"UnnamedClass";throw new Error(`Service "${i}" has constructor dependencies but no autowiring configuration. + +Solutions: + 1. \u2B50 Use the NovaDI transformer (recommended): + - Add "@novadi/core/unplugin" to your build config + - Transformer automatically generates .autoWire() for all dependencies + + 2. Add manual autowiring: + .autoWire({ map: { /* param: resolver */ } }) + + 3. Use a factory function: + .register((c) => new ${i}(...)) + +See docs: https://github.com/janus007/NovaDI#autowire`)}let s=()=>new t.constructor;e.bindFactory(t.token,s,n)}applyRegistration(e,t){let n={lifetime:t.lifetime};switch(t.type){case"instance":e.bindValue(t.token,t.value);break;case"factory":e.bindFactory(t.token,t.factory,n);break;case"type":this.applyTypeRegistration(e,t,n);break}}};function Vt(o){return o&&typeof o.dispose=="function"}var et=class{constructor(){this.resolvingStack=new Set,this.perRequestCache=new Map}isResolving(e){return this.resolvingStack.has(e)}enterResolve(e){this.resolvingStack.add(e)}exitResolve(e){this.resolvingStack.delete(e),this.path=void 0}getPath(){return this.path||(this.path=Array.from(this.resolvingStack).map(e=>e.toString())),[...this.path]}cachePerRequest(e,t){this.perRequestCache.set(e,t)}getPerRequest(e){return this.perRequestCache.get(e)}hasPerRequest(e){return this.perRequestCache.has(e)}reset(){this.resolvingStack.clear(),this.perRequestCache.clear(),this.path=void 0}},tt=class{constructor(){this.pool=[],this.maxSize=10}acquire(){let e=this.pool.pop();return e?(e.reset(),e):new et}release(e){this.pool.lengthnew t)}resolve(e){let t=this.tryGetFromCaches(e);if(t!==void 0)return t;if(this.currentContext)return this.resolveWithContext(e,this.currentContext);let n=o.contextPool.acquire();this.currentContext=n;try{return this.resolveWithContext(e,n)}finally{this.currentContext=void 0,o.contextPool.release(n)}}resolveSingletonUnsafe(e){return this.ultraFastSingletonCache.get(e)??this.singletonCache.get(e)}resolveTransientSimple(e){let t=this.fastTransientCache.get(e);return t?t():this.resolve(e)}resolveBatch(e){let t=!!this.currentContext,n=this.currentContext||o.contextPool.acquire();t||(this.currentContext=n);try{return e.map(s=>{let i=this.tryGetFromCaches(s);return i!==void 0?i:this.resolveWithContext(s,n)})}finally{t||(this.currentContext=void 0,o.contextPool.release(n))}}async resolveAsync(e){if(this.currentContext)return this.resolveAsyncWithContext(e,this.currentContext);let t=o.contextPool.acquire();this.currentContext=t;try{return await this.resolveAsyncWithContext(e,t)}finally{this.currentContext=void 0,o.contextPool.release(t)}}tryGetFromCaches(e){let t=this.ultraFastSingletonCache.get(e);if(t!==void 0)return t;if(this.singletonCache.has(e)){let r=this.singletonCache.get(e);return this.ultraFastSingletonCache.set(e,r),r}let n=this.fastTransientCache.get(e);if(n)return n()}cacheInstance(e,t,n,r){n==="singleton"?(this.singletonCache.set(e,t),this.singletonOrder.push(e),this.ultraFastSingletonCache.set(e,t)):n==="per-request"&&r&&r.cachePerRequest(e,t)}validateAndGetBinding(e,t){if(t.isResolving(e))throw new he([...t.getPath(),e.toString()]);let n=this.getBinding(e);if(!n)throw new ue(e.toString(),t.getPath());return n}instantiateBindingSync(e,t,n){switch(e.type){case"value":return e.value;case"factory":let r=e.factory(this);if(r instanceof Promise)throw new Error(`Async factory detected for ${t.toString()}. Use resolveAsync() instead.`);return r;case"class":let i=(e.dependencies||[]).map(a=>this.resolveWithContext(a,n));return new e.constructor(...i);case"inline-class":return new e.constructor;default:throw new Error(`Unknown binding type: ${e.type}`)}}async instantiateBindingAsync(e,t){switch(e.type){case"value":return e.value;case"factory":return await Promise.resolve(e.factory(this));case"class":let n=e.dependencies||[],r=await Promise.all(n.map(s=>this.resolveAsyncWithContext(s,t)));return new e.constructor(...r);case"inline-class":return new e.constructor;default:throw new Error(`Unknown binding type: ${e.type}`)}}createChild(){return new o(this)}async dispose(){let e=[];for(let t=this.singletonOrder.length-1;t>=0;t--){let n=this.singletonOrder[t],r=this.singletonCache.get(n);if(r&&Vt(r))try{await r.dispose()}catch(s){e.push(s)}}this.singletonCache.clear(),this.singletonOrder.length=0}builder(){return new ge(this)}resolveNamed(e){let t=this.__namedRegistrations;if(!t)throw new Error(`Named service "${e}" not found. No named registrations exist.`);let n=t.get(e);if(!n)throw new Error(`Named service "${e}" not found`);return this.resolve(n.token)}resolveKeyed(e){let t=this.__keyedRegistrations;if(!t)throw new Error("Keyed service not found. No keyed registrations exist.");let n=t.get(e);if(!n){let r=typeof e=="symbol"?e.toString():`"${e}"`;throw new Error(`Keyed service ${r} not found`)}return this.resolve(n.token)}resolveAll(e){let t=this.__multiRegistrations;if(!t)return[];let n=t.get(e);return!n||n.length===0?[]:n.map(r=>this.resolve(r))}getRegistry(){let e=[];return this.bindings.forEach((t,n)=>{e.push({token:n.description||n.symbol.toString(),type:t.type,lifetime:t.lifetime,dependencies:t.dependencies?.map(r=>r.description||r.symbol.toString())})}),e}interfaceToken(e){let t=e||`Interface_${Math.random().toString(36).substr(2,9)}`;if(this.interfaceRegistry.has(t))return this.interfaceRegistry.get(t);if(this.parent)return this.parent.interfaceToken(t);let n=ae(t);return this.interfaceRegistry.set(t,n),n}resolveType(e){let t=e||"",n=this.interfaceTokenCache.get(t);return n||(n=this.interfaceToken(e),this.interfaceTokenCache.set(t,n)),this.resolve(n)}resolveTypeKeyed(e,t){return this.resolveKeyed(e)}resolveTypeAll(e){let t=this.interfaceToken(e);return this.resolveAll(t)}resolveWithContext(e,t){let n=this.validateAndGetBinding(e,t);if(n.lifetime==="per-request"&&t.hasPerRequest(e))return t.getPerRequest(e);if(n.lifetime==="singleton"&&this.singletonCache.has(e))return this.singletonCache.get(e);t.enterResolve(e);try{let r=this.instantiateBindingSync(n,e,t);return this.cacheInstance(e,r,n.lifetime,t),r}finally{t.exitResolve(e)}}async resolveAsyncWithContext(e,t){let n=this.validateAndGetBinding(e,t);if(n.lifetime==="per-request"&&t.hasPerRequest(e))return t.getPerRequest(e);if(n.lifetime==="singleton"&&this.singletonCache.has(e))return this.singletonCache.get(e);t.enterResolve(e);try{let r=await this.instantiateBindingAsync(n,t);return this.cacheInstance(e,r,n.lifetime,t),r}finally{t.exitResolve(e)}}getBinding(e){return this.bindingCache||this.buildBindingCache(),this.bindingCache.get(e)}buildBindingCache(){this.bindingCache=new Map;let e=this;for(;e;)e.bindings.forEach((t,n)=>{this.bindingCache.has(n)||this.bindingCache.set(n,t)}),e=e.parent}invalidateBindingCache(){this.bindingCache=void 0,this.ultraFastSingletonCache.clear()}};ce.contextPool=new tt;var nt=class{constructor(){this.eventLog=[],this.debug=!1,this.listeners=new Set,this.logConfig={calendar:!0,grid:!0,event:!0,scroll:!0,navigation:!0,view:!0,default:!0}}on(e,t,n){return document.addEventListener(e,t,n),this.listeners.add({eventType:e,handler:t,options:n}),()=>this.off(e,t)}once(e,t){return this.on(e,t,{once:!0})}off(e,t){document.removeEventListener(e,t);for(let n of this.listeners)if(n.eventType===e&&n.handler===t){this.listeners.delete(n);break}}emit(e,t={}){if(!e)return!1;let n=new CustomEvent(e,{detail:t??{},bubbles:!0,cancelable:!0});return this.debug&&this.logEventWithGrouping(e,t),this.eventLog.push({type:e,detail:t??{},timestamp:Date.now()}),!document.dispatchEvent(n)}logEventWithGrouping(e,t){let n=this.extractCategory(e);if(!this.logConfig[n])return;let{emoji:r,color:s}=this.getCategoryStyle(n)}extractCategory(e){if(!e)return"unknown";if(e.includes(":"))return e.split(":")[0];let t=e.toLowerCase();return t.includes("grid")||t.includes("rendered")?"grid":t.includes("event")||t.includes("sync")?"event":t.includes("scroll")?"scroll":t.includes("nav")||t.includes("date")?"navigation":t.includes("view")?"view":"default"}getCategoryStyle(e){let t={calendar:{emoji:"\u{1F5D3}\uFE0F",color:"#2196F3"},grid:{emoji:"\u{1F4CA}",color:"#4CAF50"},event:{emoji:"\u{1F4C5}",color:"#FF9800"},scroll:{emoji:"\u{1F4DC}",color:"#9C27B0"},navigation:{emoji:"\u{1F9ED}",color:"#F44336"},view:{emoji:"\u{1F441}\uFE0F",color:"#00BCD4"},default:{emoji:"\u{1F4E2}",color:"#607D8B"}};return t[e]||t.default}setLogConfig(e){this.logConfig={...this.logConfig,...e}}getLogConfig(){return{...this.logConfig}}getEventLog(e){return e?this.eventLog.filter(t=>t.type===e):this.eventLog}setDebug(e){this.debug=e}},b=new nt;var ne={EVENT_HEIGHT:22,EVENT_GAP:2,CONTAINER_PADDING:4,MAX_COLLAPSED_ROWS:4,get SINGLE_ROW_HEIGHT(){return this.EVENT_HEIGHT+this.EVENT_GAP}},me={standard:{id:"standard",workDays:[1,2,3,4,5],totalDays:5,firstWorkDay:1},compressed:{id:"compressed",workDays:[1,2,3,4],totalDays:4,firstWorkDay:1},midweek:{id:"midweek",workDays:[3,4,5],totalDays:3,firstWorkDay:3},weekend:{id:"weekend",workDays:[6,7],totalDays:2,firstWorkDay:6},fullweek:{id:"fullweek",workDays:[1,2,3,4,5,6,7],totalDays:7,firstWorkDay:1}},K=class o{constructor(e,t,n,r,s,i,a=new Date){this.apiEndpoint="/api",this.config=e,this.gridSettings=t,this.dateViewSettings=n,this.timeFormatConfig=r,this.currentWorkWeek=s,this.currentView=i,this.selectedDate=a,o._instance=this}static getInstance(){if(!o._instance)throw new Error("Configuration has not been initialized. Call ConfigManager.load() first.");return o._instance}setSelectedDate(e){this.selectedDate=e}getWorkWeekSettings(){return me[this.currentWorkWeek]||me.standard}};K._instance=null;var I=ie(Et(),1),Mt=ie(Dt(),1),At=ie(St(),1),Rt=ie(wt(),1),bt=ie(Ct(),1),xt=ie(Tt(),1),It=ie(kt(),1);I.default.extend(Mt.default);I.default.extend(At.default);I.default.extend(Rt.default);I.default.extend(bt.default);I.default.extend(xt.default);I.default.extend(It.default);var U=class{constructor(e){this.timezone=e.timeFormatConfig.timezone}toUTC(e){return I.default.tz(e,this.timezone).utc().toISOString()}fromUTC(e){return I.default.utc(e).tz(this.timezone).toDate()}formatTime(e,t=!1){let n=t?"HH:mm:ss":"HH:mm";return(0,I.default)(e).format(n)}formatTimeRange(e,t){return`${this.formatTime(e)} - ${this.formatTime(t)}`}formatTechnicalDateTime(e){return(0,I.default)(e).format("YYYY-MM-DD HH:mm:ss")}formatDate(e){return(0,I.default)(e).format("YYYY-MM-DD")}formatMonthYear(e,t="en-US"){return e.toLocaleDateString(t,{month:"long",year:"numeric"})}formatISODate(e){return this.formatDate(e)}formatTime12(e){return(0,I.default)(e).format("h:mm A")}getDayName(e,t="short",n="da-DK"){return new Intl.DateTimeFormat(n,{weekday:t}).format(e)}formatDateRange(e,t,n={}){let{locale:r="en-US",month:s="short",day:i="numeric"}=n,a=e.getFullYear(),c=t.getFullYear(),d=new Intl.DateTimeFormat(r,{month:s,day:i,year:a!==c?"numeric":void 0});return typeof d.formatRange=="function"?d.formatRange(e,t):`${d.format(e)} - ${d.format(t)}`}timeToMinutes(e){let t=e.split(":").map(Number),n=t[0]||0,r=t[1]||0;return n*60+r}minutesToTime(e){let t=Math.floor(e/60),n=e%60;return(0,I.default)().hour(t).minute(n).format("HH:mm")}formatTimeFromMinutes(e){return this.minutesToTime(e)}getMinutesSinceMidnight(e){let t=(0,I.default)(e);return t.hour()*60+t.minute()}getDurationMinutes(e,t){let n=(0,I.default)(e);return(0,I.default)(t).diff(n,"minute")}getWeekBounds(e){let t=(0,I.default)(e);return{start:t.startOf("week").add(1,"day").toDate(),end:t.endOf("week").add(1,"day").toDate()}}addWeeks(e,t){return(0,I.default)(e).add(t,"week").toDate()}addMonths(e,t){return(0,I.default)(e).add(t,"month").toDate()}getWeekNumber(e){return(0,I.default)(e).isoWeek()}getFullWeekDates(e){let t=[];for(let n=0;n<7;n++)t.push(this.addDays(e,n));return t}getWorkWeekDates(e,t){let n=[],r=this.getWeekBounds(e),s=this.startOfDay(r.start);return t.forEach(i=>{let a=new Date(s),c=i===7?6:i-1;a.setDate(s.getDate()+c),n.push(a)}),n}createDateAtTime(e,t){let n=Math.floor(t/60),r=t%60;return(0,I.default)(e).startOf("day").hour(n).minute(r).toDate()}snapToInterval(e,t){let n=this.getMinutesSinceMidnight(e),r=Math.round(n/t)*t;return this.createDateAtTime(e,r)}isSameDay(e,t){return(0,I.default)(e).isSame(t,"day")}startOfDay(e){return(0,I.default)(e).startOf("day").toDate()}endOfDay(e){return(0,I.default)(e).endOf("day").toDate()}addDays(e,t){return(0,I.default)(e).add(t,"day").toDate()}addMinutes(e,t){return(0,I.default)(e).add(t,"minute").toDate()}parseISO(e){return(0,I.default)(e).toDate()}isValid(e){return(0,I.default)(e).isValid()}differenceInCalendarDays(e,t){let n=(0,I.default)(e).startOf("day"),r=(0,I.default)(t).startOf("day");return n.diff(r,"day")}isValidRange(e,t){return!this.isValid(e)||!this.isValid(t)?!1:e.getTime()<=t.getTime()}isWithinBounds(e){if(!this.isValid(e))return!1;let t=e.getFullYear();return t>=1900&&t<=2100}validateDate(e,t={}){if(!this.isValid(e))return{valid:!1,error:"Invalid date"};if(!this.isWithinBounds(e))return{valid:!1,error:"Date out of bounds (1900-2100)"};let n=new Date;return t.requireFuture&&e<=n?{valid:!1,error:"Date must be in the future"}:t.requirePast&&e>=n?{valid:!1,error:"Date must be in the past"}:t.minDate&&et.maxDate?{valid:!1,error:`Date must be before ${this.formatDate(t.maxDate)}`}:{valid:!0}}};var V=class o{static getDateService(){if(!o.dateService){if(!o.settings)throw new Error("TimeFormatter must be configured before use. Call TimeFormatter.configure() first.");let e={timeFormatConfig:{timezone:o.settings.timezone}};o.dateService=new U(e)}return o.dateService}static configure(e){o.settings=e,o.dateService=null}static convertToLocalTime(e){if(typeof e=="string")return o.getDateService().fromUTC(e);let t=e.toISOString();return o.getDateService().fromUTC(t)}static format24Hour(e){if(!o.settings)throw new Error("TimeFormatter must be configured before use. Call TimeFormatter.configure() first.");let t=o.convertToLocalTime(e);return o.getDateService().formatTime(t,o.settings.showSeconds)}static formatTime(e){return o.format24Hour(e)}static formatTimeRange(e,t){let n=o.convertToLocalTime(e),r=o.convertToLocalTime(t);return o.getDateService().formatTimeRange(n,r)}};V.settings=null;V.dateService=null;var v={INITIALIZED:"core:initialized",READY:"core:ready",DESTROYED:"core:destroyed",VIEW_CHANGED:"view:changed",VIEW_RENDERED:"view:rendered",WORKWEEK_CHANGED:"workweek:changed",NAV_BUTTON_CLICKED:"nav:button-clicked",DATE_CHANGED:"nav:date-changed",NAVIGATION_COMPLETED:"nav:navigation-completed",PERIOD_INFO_UPDATE:"nav:period-info-update",NAVIGATE_TO_EVENT:"nav:navigate-to-event",DATA_LOADING:"data:loading",DATA_LOADED:"data:loaded",DATA_ERROR:"data:error",EVENTS_FILTERED:"data:events-filtered",REMOTE_UPDATE_RECEIVED:"data:remote-update",GRID_RENDERED:"grid:rendered",GRID_CLICKED:"grid:clicked",CELL_SELECTED:"grid:cell-selected",EVENT_CREATED:"event:created",EVENT_UPDATED:"event:updated",EVENT_DELETED:"event:deleted",EVENT_SELECTED:"event:selected",ERROR:"system:error",REFRESH_REQUESTED:"system:refresh",OFFLINE_MODE_CHANGED:"system:offline-mode-changed",SYNC_STARTED:"sync:started",SYNC_COMPLETED:"sync:completed",SYNC_FAILED:"sync:failed",SYNC_RETRY:"sync:retry",FILTER_CHANGED:"filter:changed",EVENTS_RENDERED:"events:rendered"};var fe=class{constructor(e,t){this.eventBus=e,this.config=t,this.setupEventListeners(),this.syncGridCSSVariables(),this.syncWorkweekCSSVariables()}setupEventListeners(){this.eventBus.on(v.WORKWEEK_CHANGED,e=>{let{settings:t}=e.detail;this.syncWorkweekCSSVariables(t)})}syncGridCSSVariables(){let e=this.config.gridSettings;document.documentElement.style.setProperty("--hour-height",`${e.hourHeight}px`),document.documentElement.style.setProperty("--day-start-hour",e.dayStartHour.toString()),document.documentElement.style.setProperty("--day-end-hour",e.dayEndHour.toString()),document.documentElement.style.setProperty("--work-start-hour",e.workStartHour.toString()),document.documentElement.style.setProperty("--work-end-hour",e.workEndHour.toString())}syncWorkweekCSSVariables(e){let t=e||this.config.getWorkWeekSettings();document.documentElement.style.setProperty("--grid-columns",t.totalDays.toString())}static async load(){let e=await fetch("/wwwroot/data/calendar-config.json");if(!e.ok)throw new Error(`Failed to load config: ${e.statusText}`);let t=await e.json(),n={scrollbarWidth:t.scrollbar.width,scrollbarColor:t.scrollbar.color,scrollbarTrackColor:t.scrollbar.trackColor,scrollbarHoverColor:t.scrollbar.hoverColor,scrollbarBorderRadius:t.scrollbar.borderRadius,allowDrag:t.interaction.allowDrag,allowResize:t.interaction.allowResize,allowCreate:t.interaction.allowCreate,apiEndpoint:t.api.endpoint,dateFormat:t.api.dateFormat,timeFormat:t.api.timeFormat,enableSearch:t.features.enableSearch,enableTouch:t.features.enableTouch,defaultEventDuration:t.eventDefaults.defaultEventDuration,minEventDuration:t.gridSettings.snapInterval,maxEventDuration:t.eventDefaults.maxEventDuration},r=new K(n,t.gridSettings,t.dateViewSettings,t.timeFormatConfig,t.currentWorkWeek,t.currentView||"week");return V.configure(r.timeFormatConfig),r}};var Ee=class{constructor(e){this.eventBus=e}parseEventIdFromURL(){try{let t=new URLSearchParams(window.location.search).get("eventId");return t&&t.trim()!==""?t.trim():null}catch(e){return console.warn("URLManager: Failed to parse URL parameters:",e),null}}getAllQueryParams(){try{let e=new URLSearchParams(window.location.search),t={};for(let[n,r]of e.entries())t[n]=r;return t}catch(e){return console.warn("URLManager: Failed to parse URL parameters:",e),{}}}updateURL(e){try{let t=new URL(window.location.href);Object.entries(e).forEach(([n,r])=>{r===null?t.searchParams.delete(n):t.searchParams.set(n,r)}),window.history.replaceState({},"",t.toString())}catch(t){console.warn("URLManager: Failed to update URL:",t)}}hasQueryParams(){return window.location.search.length>0}};var De=class{constructor(e,t,n,r){this.eventBus=e,this.dateService=t,this.config=n,this.repository=r}async loadData(){try{await this.repository.loadEvents()}catch(e){throw console.error("Failed to load event data:",e),e}}async getEvents(e=!1){let t=await this.repository.loadEvents();return e?[...t]:t}async getEventById(e){return(await this.repository.loadEvents()).find(n=>n.id===e)}async getEventForNavigation(e){let t=await this.getEventById(e);if(!t)return null;let n=this.dateService.validateDate(t.start);return n.valid?this.dateService.isValidRange(t.start,t.end)?{event:t,eventDate:t.start}:(console.warn(`EventManager: Invalid date range for event ${e}: start must be before end`),null):(console.warn(`EventManager: Invalid event start date for event ${e}:`,n.error),null)}async navigateToEvent(e){let t=await this.getEventForNavigation(e);if(!t)return console.warn(`EventManager: Event with ID ${e} not found`),!1;let{event:n,eventDate:r}=t;return this.eventBus.emit(v.NAVIGATE_TO_EVENT,{eventId:e,event:n,eventDate:r,eventStartTime:n.start}),!0}async getEventsForPeriod(e,t){return(await this.repository.loadEvents()).filter(r=>r.start<=t&&r.end>=e)}async addEvent(e){let t=await this.repository.createEvent(e,"local");return this.eventBus.emit(v.EVENT_CREATED,{event:t}),t}async updateEvent(e,t){try{let n=await this.repository.updateEvent(e,t,"local");return this.eventBus.emit(v.EVENT_UPDATED,{event:n}),n}catch(n){return console.error(`Failed to update event ${e}:`,n),null}}async deleteEvent(e){try{return await this.repository.deleteEvent(e,"local"),this.eventBus.emit(v.EVENT_DELETED,{eventId:e}),!0}catch(t){return console.error(`Failed to delete event ${e}:`,t),!1}}async handleRemoteUpdate(e){try{await this.repository.updateEvent(e.id,e,"remote"),this.eventBus.emit(v.REMOTE_UPDATE_RECEIVED,{event:e}),this.eventBus.emit(v.EVENT_UPDATED,{event:e})}catch(t){console.error(`Failed to handle remote update for event ${e.id}:`,t)}}};var B=class{static updateColumnBoundsCache(){this.columnBoundsCache=[];let e=document.querySelectorAll("swp-day-column"),t=1;e.forEach(n=>{let r=n.getBoundingClientRect(),s=n.dataset.date;s&&this.columnBoundsCache.push({boundingClientRect:r,element:n,date:s,left:r.left,right:r.right,index:t++})}),this.columnBoundsCache.sort((n,r)=>n.left-r.left)}static getColumnBounds(e){this.columnBoundsCache.length===0&&this.updateColumnBoundsCache();let t=this.columnBoundsCache.find(n=>e.x>=n.left&&e.x<=n.right);return t||null}static getColumnBoundsByDate(e){this.columnBoundsCache.length===0&&this.updateColumnBoundsCache();let t=e.toISOString().split("T")[0];return this.columnBoundsCache.find(r=>r.date===t)||null}static getColumns(){return[...this.columnBoundsCache]}static getHeaderColumns(){let e=[],t=document.querySelectorAll("swp-calendar-header swp-day-header"),n=1;return t.forEach(r=>{let s=r.getBoundingClientRect(),i=r.dataset.date;i&&e.push({boundingClientRect:s,element:r,date:i,left:s.left,right:s.right,index:n++})}),e.sort((r,s)=>r.left-s.left),e}};B.columnBoundsCache=[];var Se=class{constructor(e,t,n,r){this.dragMouseLeaveHeaderListener=null,this.eventBus=e,this.eventManager=t,this.strategy=n,this.dateService=r,this.setupEventListeners()}async renderEvents(e){this.strategy.clearEvents(e.container);let t=await this.eventManager.getEventsForPeriod(e.startDate,e.endDate);if(t.length===0)return;let n=t.filter(r=>!r.allDay);console.log("\u{1F3AF} EventRenderingService: Event filtering",{totalEvents:t.length,timedEvents:n.length,allDayEvents:t.length-n.length}),n.length>0&&this.strategy.renderEvents(n,e.container),this.eventBus.emit(v.EVENTS_RENDERED,{events:t,container:e.container})}setupEventListeners(){this.eventBus.on(v.GRID_RENDERED,e=>{this.handleGridRendered(e)}),this.eventBus.on(v.VIEW_CHANGED,e=>{this.handleViewChanged(e)}),this.setupDragEventListeners()}handleGridRendered(e){let{container:t,startDate:n,endDate:r}=e.detail;!t||!n||!r||this.renderEvents({container:t,startDate:n,endDate:r})}handleViewChanged(e){this.clearEvents()}setupDragEventListeners(){this.setupDragStartListener(),this.setupDragMoveListener(),this.setupDragEndListener(),this.setupDragColumnChangeListener(),this.setupDragMouseLeaveHeaderListener(),this.setupDragMouseEnterColumnListener(),this.setupResizeEndListener(),this.setupNavigationCompletedListener()}setupDragStartListener(){this.eventBus.on("drag:start",e=>{let t=e.detail;t.originalElement.hasAttribute("data-allday")||t.originalElement&&this.strategy.handleDragStart&&t.columnBounds&&this.strategy.handleDragStart(t)})}setupDragMoveListener(){this.eventBus.on("drag:move",e=>{let t=e.detail;t.draggedClone.hasAttribute("data-allday")||this.strategy.handleDragMove&&this.strategy.handleDragMove(t)})}setupDragEndListener(){this.eventBus.on("drag:end",async e=>{let{originalElement:t,draggedClone:n,originalSourceColumn:r,finalPosition:s,target:i}=e.detail,a=s.column,c=s.snappedY,d=n;i==="swp-day-column"&&a&&(t&&n&&this.strategy.handleDragEnd&&this.strategy.handleDragEnd(t,n,a,c),await this.eventManager.updateEvent(d.eventId,{start:d.start,end:d.end,allDay:!1}),await this.reRenderAffectedColumns(r,a))})}setupDragColumnChangeListener(){this.eventBus.on("drag:column-change",e=>{let t=e.detail;t.draggedClone&&t.draggedClone.hasAttribute("data-allday")||this.strategy.handleColumnChange&&this.strategy.handleColumnChange(t)})}setupDragMouseLeaveHeaderListener(){this.dragMouseLeaveHeaderListener=e=>{let{targetDate:t,mousePosition:n,originalElement:r,draggedClone:s}=e.detail;s&&(s.style.display=""),console.log("\u{1F6AA} EventRendererManager: Received drag:mouseleave-header",{targetDate:t,originalElement:r,cloneElement:s})},this.eventBus.on("drag:mouseleave-header",this.dragMouseLeaveHeaderListener)}setupDragMouseEnterColumnListener(){this.eventBus.on("drag:mouseenter-column",e=>{let t=e.detail;t.draggedClone.hasAttribute("data-allday")&&(console.log("\u{1F3AF} EventRendererManager: Received drag:mouseenter-column",{targetColumn:t.targetColumn,snappedY:t.snappedY,calendarEvent:t.calendarEvent}),this.strategy.handleConvertAllDayToTimed&&this.strategy.handleConvertAllDayToTimed(t))})}setupResizeEndListener(){this.eventBus.on("resize:end",async e=>{let{eventId:t,element:n}=e.detail,r=n,s=r.start,i=r.end;await this.eventManager.updateEvent(t,{start:s,end:i}),console.log("\u{1F4DD} EventRendererManager: Updated event after resize",{eventId:t,newStart:s,newEnd:i});let a=B.getColumnBoundsByDate(s);a&&await this.renderSingleColumn(a)})}setupNavigationCompletedListener(){this.eventBus.on(v.NAVIGATION_COMPLETED,()=>{this.strategy.handleNavigationCompleted&&this.strategy.handleNavigationCompleted()})}async reRenderAffectedColumns(e,t){e&&await this.renderSingleColumn(e),t&&t.date!==e?.date&&await this.renderSingleColumn(t)}clearColumnEvents(e){let t=e.querySelectorAll("swp-event"),n=e.querySelectorAll("swp-event-group");t.forEach(r=>r.remove()),n.forEach(r=>r.remove())}async renderSingleColumn(e){let t=this.dateService.parseISO(`${e.date}T00:00:00`),n=this.dateService.parseISO(`${e.date}T23:59:59.999`),s=(await this.eventManager.getEventsForPeriod(t,n)).filter(a=>!a.allDay),i=e.element.querySelector("swp-events-layer");if(!i){console.warn("EventRendererManager: Events layer not found in column");return}this.clearColumnEvents(i),this.strategy.renderSingleColumnEvents&&this.strategy.renderSingleColumnEvents(e,s),console.log("\u{1F504} EventRendererManager: Re-rendered single column",{columnDate:e.date,eventsCount:s.length})}clearEvents(e){this.strategy.clearEvents(e)}refresh(e){this.clearEvents(e)}};var we=class{constructor(e,t){this.container=null,this.currentDate=new Date,this.currentView="week",this.gridRenderer=e,this.dateService=t,this.init()}init(){this.findElements(),this.subscribeToEvents()}getISOWeekStart(e){let t=this.dateService.getWeekBounds(e);return this.dateService.startOfDay(t.start)}getWeekEnd(e){let t=this.dateService.getWeekBounds(e);return this.dateService.endOfDay(t.end)}findElements(){this.container=document.querySelector("swp-calendar-container")}subscribeToEvents(){b.on(v.VIEW_CHANGED,e=>{let t=e.detail;this.currentView=t.currentView,this.render()}),b.on(v.REFRESH_REQUESTED,e=>{this.render()}),b.on(v.WORKWEEK_CHANGED,()=>{this.render()})}async render(){if(!this.container)return;this.gridRenderer.renderGrid(this.container,this.currentDate);let e=this.getPeriodRange(),t=this.getLayoutConfig();b.emit(v.GRID_RENDERED,{container:this.container,currentDate:this.currentDate,startDate:e.startDate,endDate:e.endDate,layoutConfig:t,columnCount:t.columnCount})}getCurrentPeriodLabel(){switch(this.currentView){case"week":case"day":let e=this.getISOWeekStart(this.currentDate),t=this.getWeekEnd(this.currentDate);return this.dateService.formatDateRange(e,t);case"month":return this.dateService.formatMonthYear(this.currentDate);default:let n=this.getISOWeekStart(this.currentDate),r=this.getWeekEnd(this.currentDate);return this.dateService.formatDateRange(n,r)}}navigateNext(){let e;switch(this.currentView){case"week":e=this.dateService.addWeeks(this.currentDate,1);break;case"month":e=this.dateService.addMonths(this.currentDate,1);break;case"day":e=this.dateService.addDays(this.currentDate,1);break;default:e=this.dateService.addWeeks(this.currentDate,1)}this.currentDate=e,b.emit(v.NAVIGATION_COMPLETED,{direction:"next",newDate:e,periodLabel:this.getCurrentPeriodLabel()}),this.render()}navigatePrevious(){let e;switch(this.currentView){case"week":e=this.dateService.addWeeks(this.currentDate,-1);break;case"month":e=this.dateService.addMonths(this.currentDate,-1);break;case"day":e=this.dateService.addDays(this.currentDate,-1);break;default:e=this.dateService.addWeeks(this.currentDate,-1)}this.currentDate=e,b.emit(v.NAVIGATION_COMPLETED,{direction:"previous",newDate:e,periodLabel:this.getCurrentPeriodLabel()}),this.render()}getDisplayDates(){switch(this.currentView){case"week":let e=this.getISOWeekStart(this.currentDate);return this.dateService.getFullWeekDates(e);case"month":return this.getMonthDates(this.currentDate);case"day":return[this.currentDate];default:let t=this.getISOWeekStart(this.currentDate);return this.dateService.getFullWeekDates(t)}}getPeriodRange(){switch(this.currentView){case"week":let e=this.getISOWeekStart(this.currentDate),t=this.getWeekEnd(this.currentDate);return{startDate:e,endDate:t};case"month":return{startDate:this.getMonthStart(this.currentDate),endDate:this.getMonthEnd(this.currentDate)};case"day":return{startDate:this.currentDate,endDate:this.currentDate};default:let n=this.getISOWeekStart(this.currentDate),r=this.getWeekEnd(this.currentDate);return{startDate:n,endDate:r}}}getLayoutConfig(){switch(this.currentView){case"week":return{columnCount:7,type:"week"};case"month":return{columnCount:7,type:"month"};case"day":return{columnCount:1,type:"day"};default:return{columnCount:7,type:"week"}}}getMonthStart(e){let t=e.getFullYear(),n=e.getMonth();return this.dateService.startOfDay(new Date(t,n,1))}getMonthEnd(e){let t=this.dateService.addMonths(e,1),n=this.getMonthStart(t);return this.dateService.endOfDay(this.dateService.addDays(n,-1))}getMonthDates(e){let t=[],n=this.getMonthStart(e),r=this.getMonthEnd(e),s=Math.ceil((r.getTime()-n.getTime())/(1e3*60*60*24))+1;for(let i=0;i{this.syncTimeAxisPosition(),this.setupScrolling()}),b.on("header:height-changed",()=>{this.updateScrollableHeight()}),b.on("header:ready",()=>{this.calendarHeader=document.querySelector("swp-calendar-header"),this.scrollableContent&&this.calendarHeader&&(this.setupHorizontalScrollSynchronization(),this.syncCalendarHeaderPosition()),this.updateScrollableHeight()}),window.addEventListener("resize",()=>{this.updateScrollableHeight()}),b.on("scroll:to-event-time",e=>{let t=e,{eventStartTime:n}=t.detail;n&&this.scrollToEventTime(n)})}setupScrolling(){this.findElements(),this.scrollableContent&&this.calendarContainer&&(this.setupResizeObserver(),this.updateScrollableHeight(),this.setupScrollSynchronization()),this.scrollableContent&&this.calendarHeader&&this.setupHorizontalScrollSynchronization()}findElements(){this.scrollableContent=document.querySelector("swp-scrollable-content"),this.calendarContainer=document.querySelector("swp-calendar-container"),this.timeAxis=document.querySelector("swp-time-axis"),this.calendarHeader=document.querySelector("swp-calendar-header")}scrollTo(e){this.scrollableContent&&(this.scrollableContent.scrollTop=e)}scrollToHour(e){let t=`${e.toString().padStart(2,"0")}:00`,n=this.positionUtils.timeToPixels(t);this.scrollTo(n)}scrollToEventTime(e){try{let t=new Date(e),n=t.getHours(),r=t.getMinutes(),s=n+r/60;this.scrollToHour(s)}catch(t){console.warn("ScrollManager: Failed to scroll to event time:",t)}}setupResizeObserver(){this.calendarContainer&&(this.resizeObserver&&this.resizeObserver.disconnect(),this.resizeObserver=new ResizeObserver(e=>{for(let t of e)this.updateScrollableHeight()}),this.resizeObserver.observe(this.calendarContainer))}updateScrollableHeight(){if(!this.scrollableContent||!this.calendarContainer)return;let e=this.calendarContainer.getBoundingClientRect(),t=document.querySelector("swp-calendar-nav"),n=t?t.getBoundingClientRect().height:0,r=document.querySelector("swp-calendar-header"),s=r?r.getBoundingClientRect().height:80,i=e.height-s,a=e.width-60;i>0&&(this.scrollableContent.style.height=`${i}px`),a>0&&(this.scrollableContent.style.width=`${a}px`)}setupScrollSynchronization(){if(!this.scrollableContent||!this.timeAxis)return;let e=null;this.scrollableContent.addEventListener("scroll",()=>{e&&cancelAnimationFrame(e),e=requestAnimationFrame(()=>{this.syncTimeAxisPosition()})})}syncTimeAxisPosition(){if(!this.scrollableContent||!this.timeAxis)return;let e=this.scrollableContent.scrollTop,t=this.timeAxis.querySelector("swp-time-axis-content");t&&(t.style.transform=`translateY(-${e}px)`,e%100)}setupHorizontalScrollSynchronization(){!this.scrollableContent||!this.calendarHeader||this.scrollableContent.addEventListener("scroll",()=>{this.syncCalendarHeaderPosition()})}syncCalendarHeaderPosition(){if(!this.scrollableContent||!this.calendarHeader)return;let e=this.scrollableContent.scrollLeft;this.calendarHeader.style.transform=`translateX(-${e}px)`,e%100}};var Te=class{constructor(e,t,n,r,s){this.animationQueue=0,this.eventBus=e,this.dateService=r,this.weekInfoRenderer=s,this.gridRenderer=n,this.currentWeek=this.getISOWeekStart(new Date),this.targetWeek=new Date(this.currentWeek),this.init()}init(){this.setupEventListeners()}getISOWeekStart(e){let t=this.dateService.getWeekBounds(e);return this.dateService.startOfDay(t.start)}setupEventListeners(){this.eventBus.on(v.INITIALIZED,()=>{this.updateWeekInfo()}),this.eventBus.on(v.FILTER_CHANGED,e=>{let t=e.detail;this.weekInfoRenderer.applyFilterToPreRenderedGrids(t)}),this.eventBus.on(v.NAV_BUTTON_CLICKED,e=>{let{action:t}=e.detail;switch(t){case"prev":this.navigateToPreviousWeek();break;case"next":this.navigateToNextWeek();break;case"today":this.navigateToToday();break}}),this.eventBus.on(v.DATE_CHANGED,e=>{let n=e.detail.currentDate;if(!n){console.warn("NavigationManager: No date provided in DATE_CHANGED event");return}let r=new Date(n),s=this.dateService.validateDate(r);if(!s.valid){console.warn("NavigationManager: Invalid date received:",s.error);return}this.navigateToDate(r)}),this.eventBus.on(v.NAVIGATE_TO_EVENT,e=>{let t=e,{eventDate:n,eventStartTime:r}=t.detail;if(!n||!r){console.warn("NavigationManager: Invalid event navigation data");return}this.navigateToEventDate(n,r)})}navigateToEventDate(e,t){let n=this.getISOWeekStart(e);this.targetWeek=new Date(n);let r=this.currentWeek.getTime(),s=n.getTime(),i=()=>{this.eventBus.emit("scroll:to-event-time",{eventStartTime:t})};rs?(this.animationQueue++,this.animateTransition("prev",n),this.eventBus.once(v.NAVIGATION_COMPLETED,i)):i()}navigateToPreviousWeek(){this.targetWeek=this.dateService.addWeeks(this.targetWeek,-1);let e=new Date(this.targetWeek);this.animationQueue++,this.animateTransition("prev",e)}navigateToNextWeek(){this.targetWeek=this.dateService.addWeeks(this.targetWeek,1);let e=new Date(this.targetWeek);this.animationQueue++,this.animateTransition("next",e)}navigateToToday(){let e=new Date,t=this.getISOWeekStart(e);this.targetWeek=new Date(t);let n=this.currentWeek.getTime(),r=t.getTime();nr&&(this.animationQueue++,this.animateTransition("prev",t))}navigateToDate(e){let t=this.getISOWeekStart(e);this.targetWeek=new Date(t);let n=this.currentWeek.getTime(),r=t.getTime();nr&&(this.animationQueue++,this.animateTransition("prev",t))}animateTransition(e,t){let n=document.querySelector("swp-calendar-container"),r=document.querySelector("swp-calendar-container swp-grid-container:not([data-prerendered])");if(!n||!r)return;document.documentElement.style.setProperty("--all-day-row-height","0px");let i;console.group("\u{1F527} NavigationManager.refactored"),console.log("Calling GridRenderer instead of NavigationRenderer"),console.log("Target week:",t),i=this.gridRenderer.createNavigationGrid(n,t),console.groupEnd(),i.style.transform="",r.style.transform="";let a=r.animate([{transform:"translateX(0)",opacity:"1"},{transform:e==="next"?"translateX(-100%)":"translateX(100%)",opacity:"0.5"}],{duration:400,easing:"ease-in-out",fill:"forwards"});i.animate([{transform:e==="next"?"translateX(100%)":"translateX(-100%)"},{transform:"translateX(0)"}],{duration:400,easing:"ease-in-out",fill:"forwards"}).addEventListener("finish",()=>{let d=n.querySelectorAll("swp-grid-container");for(let g=0;g{let n=r=>{r.preventDefault();let s=t.getAttribute("data-action");s&&this.isValidAction(s)&&this.handleNavigation(s)};t.addEventListener("click",n),this.buttonListeners.set(t,n)})}handleNavigation(e){this.eventBus.emit(v.NAV_BUTTON_CLICKED,{action:e})}isValidAction(e){return["prev","next","today"].includes(e)}};var Me=class{constructor(e,t){this.buttonListeners=new Map,this.eventBus=e,this.config=t,this.setupButtonListeners(),this.setupEventListeners()}setupButtonListeners(){document.querySelectorAll("swp-view-button[data-view]").forEach(t=>{let n=r=>{r.preventDefault();let s=t.getAttribute("data-view");s&&this.isValidView(s)&&this.changeView(s)};t.addEventListener("click",n),this.buttonListeners.set(t,n)}),this.updateButtonStates()}setupEventListeners(){this.eventBus.on(v.INITIALIZED,()=>{this.initializeView()}),this.eventBus.on(v.DATE_CHANGED,()=>{this.refreshCurrentView()})}changeView(e){if(e===this.config.currentView)return;let t=this.config.currentView;this.config.currentView=e,this.updateButtonStates(),this.eventBus.emit(v.VIEW_CHANGED,{previousView:t,currentView:e})}updateButtonStates(){document.querySelectorAll("swp-view-button[data-view]").forEach(t=>{t.getAttribute("data-view")===this.config.currentView?t.setAttribute("data-active","true"):t.removeAttribute("data-active")})}initializeView(){this.updateButtonStates(),this.emitViewRendered()}emitViewRendered(){this.eventBus.emit(v.VIEW_RENDERED,{view:this.config.currentView})}refreshCurrentView(){this.emitViewRendered()}isValidView(e){return["day","week","month"].includes(e)}};var Ae=class{constructor(e,t,n,r,s,i){this.currentView="week",this.currentDate=new Date,this.isInitialized=!1,this.eventBus=e,this.eventManager=t,this.gridManager=n,this.eventRenderer=r,this.scrollManager=s,this.config=i,this.setupEventListeners()}async initialize(){if(!this.isInitialized)try{await this.eventManager.loadData(),await this.gridManager.render(),this.scrollManager.initialize(),this.setView(this.currentView),this.setCurrentDate(this.currentDate),this.isInitialized=!0,this.eventBus.emit(v.INITIALIZED,{currentDate:this.currentDate,currentView:this.currentView})}catch(e){throw e}}setView(e){if(this.currentView===e)return;let t=this.currentView;this.currentView=e,this.eventBus.emit(v.VIEW_CHANGED,{previousView:t,currentView:e,date:this.currentDate})}setCurrentDate(e){let t=this.currentDate;this.currentDate=new Date(e),this.eventBus.emit(v.DATE_CHANGED,{previousDate:t,currentDate:this.currentDate,view:this.currentView})}setupEventListeners(){this.eventBus.on(v.WORKWEEK_CHANGED,e=>{let t=e;this.handleWorkweekChange()})}calculateCurrentPeriod(){let e=new Date(this.currentDate);switch(this.currentView){case"day":let t=new Date(e);t.setHours(0,0,0,0);let n=new Date(e);return n.setHours(23,59,59,999),{start:t.toISOString(),end:n.toISOString()};case"week":let r=new Date(e),s=r.getDay(),i=s===0?6:s-1;r.setDate(r.getDate()-i),r.setHours(0,0,0,0);let a=new Date(r);return a.setDate(a.getDate()+6),a.setHours(23,59,59,999),{start:r.toISOString(),end:a.toISOString()};case"month":let c=new Date(e.getFullYear(),e.getMonth(),1),d=new Date(e.getFullYear(),e.getMonth()+1,0,23,59,59,999);return{start:c.toISOString(),end:d.toISOString()};default:let g=new Date(e);g.setDate(g.getDate()-3),g.setHours(0,0,0,0);let w=new Date(e);return w.setDate(w.getDate()+3),w.setHours(23,59,59,999),{start:g.toISOString(),end:w.toISOString()}}}handleWorkweekChange(){this.eventBus.emit("workweek:header-update",{currentDate:this.currentDate,currentView:this.currentView})}};var Re=class extends HTMLElement{constructor(){super(),this.config=K.getInstance(),this.dateService=new U(this.config)}get eventId(){return this.dataset.eventId||""}set eventId(e){this.dataset.eventId=e}get start(){return new Date(this.dataset.start||"")}set start(e){this.dataset.start=this.dateService.toUTC(e)}get end(){return new Date(this.dataset.end||"")}set end(e){this.dataset.end=this.dateService.toUTC(e)}get title(){return this.dataset.title||""}set title(e){this.dataset.title=e}get description(){return this.dataset.description||""}set description(e){this.dataset.description=e}get type(){return this.dataset.type||"work"}set type(e){this.dataset.type=e}},Q=class extends Re{static get observedAttributes(){return["data-start","data-end","data-title","data-description","data-type"]}connectedCallback(){this.hasChildNodes()||this.render()}attributeChangedCallback(e,t,n){t!==n&&this.isConnected&&this.updateDisplay()}updatePosition(e,t){this.style.top=`${t+1}px`;let{startMinutes:n,endMinutes:r}=this.calculateTimesFromPosition(t),s=this.dateService.createDateAtTime(e,n),i=this.dateService.createDateAtTime(e,r);if(r>=1440){let a=Math.floor(r/1440);i=this.dateService.addDays(i,a)}this.start=s,this.end=i}updateHeight(e){this.style.height=`${e}px`;let t=this.config.gridSettings,{hourHeight:n,snapInterval:r}=t,s=this.start,i=e/n*60,a=Math.round(i/r)*r,c=this.dateService.addMinutes(s,a);this.end=c}createClone(){let e=this.cloneNode(!0);e.dataset.eventId=`clone-${this.eventId}`,e.style.pointerEvents="none";let t=this.querySelector("swp-event-time");if(t){let n=t.getAttribute("data-duration");n&&(e.dataset.originalDuration=n)}return e.style.height=this.style.height||`${this.getBoundingClientRect().height}px`,e}render(){let e=this.start,t=this.end,n=V.formatTimeRange(e,t),r=(t.getTime()-e.getTime())/(1e3*60);this.innerHTML=` + ${n} + ${this.title} + ${this.description?`${this.description}`:""} + `}updateDisplay(){let e=this.querySelector("swp-event-time"),t=this.querySelector("swp-event-title"),n=this.querySelector("swp-event-description");if(e&&this.dataset.start&&this.dataset.end){let r=new Date(this.dataset.start),s=new Date(this.dataset.end),i=V.formatTimeRange(r,s);e.textContent=i;let a=(s.getTime()-r.getTime())/(1e3*60);e.setAttribute("data-duration",a.toString())}if(t&&this.dataset.title&&(t.textContent=this.dataset.title),this.dataset.description){if(n)n.textContent=this.dataset.description;else if(this.description){let r=document.createElement("swp-event-description");r.textContent=this.description,this.appendChild(r)}}else n&&n.remove()}calculateTimesFromPosition(e){let t=this.config.gridSettings,{hourHeight:n,dayStartHour:r,snapInterval:s}=t,i=parseInt(this.dataset.originalDuration||this.dataset.duration||"60"),a=e/n*60,c=r*60+a,d=Math.round(c/s)*s,g=d+i;return{startMinutes:d,endMinutes:g}}static fromCalendarEvent(e){let t=document.createElement("swp-event"),n=K.getInstance(),r=new U(n);return t.dataset.eventId=e.id,t.dataset.title=e.title,t.dataset.description=e.description||"",t.dataset.start=r.toUTC(e.start),t.dataset.end=r.toUTC(e.end),t.dataset.type=e.type,t.dataset.duration=e.metadata?.duration?.toString()||"60",t}static extractCalendarEventFromElement(e){return{id:e.dataset.eventId||"",title:e.dataset.title||"",description:e.dataset.description||void 0,start:new Date(e.dataset.start||""),end:new Date(e.dataset.end||""),type:e.dataset.type||"work",allDay:!1,syncStatus:"synced",metadata:{duration:e.dataset.duration}}}},oe=class extends Re{connectedCallback(){this.textContent||(this.textContent=this.dataset.title||"Untitled")}createClone(){let e=this.cloneNode(!0);return e.dataset.eventId=`clone-${this.eventId}`,e.style.pointerEvents="none",e.style.opacity="1",e}applyGridPositioning(e,t,n){let r=`${e} / ${t} / ${e+1} / ${n+1}`;this.style.gridArea=r}static fromCalendarEvent(e){let t=document.createElement("swp-allday-event"),n=K.getInstance(),r=new U(n);return t.dataset.eventId=e.id,t.dataset.title=e.title,t.dataset.start=r.toUTC(e.start),t.dataset.end=r.toUTC(e.end),t.dataset.type=e.type,t.dataset.allday="true",t.textContent=e.title,t}};customElements.define("swp-event",Q);customElements.define("swp-allday-event",oe);var be=class{constructor(e,t){this.mouseDownPosition={x:0,y:0},this.currentMousePosition={x:0,y:0},this.mouseOffset={x:0,y:0},this.currentColumn=null,this.previousColumn=null,this.originalSourceColumn=null,this.isDragStarted=!1,this.dragThreshold=5,this.scrollableContent=null,this.scrollDeltaY=0,this.lastScrollTop=0,this.isScrollCompensating=!1,this.dragAnimationId=null,this.targetY=0,this.currentY=0,this.targetColumn=null,this.eventBus=e,this.positionUtils=t,this.init()}init(){document.body.addEventListener("mousemove",this.handleMouseMove.bind(this)),document.body.addEventListener("mousedown",this.handleMouseDown.bind(this)),document.body.addEventListener("mouseup",this.handleMouseUp.bind(this));let e=document.querySelector("swp-calendar-container");e&&(e.addEventListener("mouseleave",()=>{this.originalElement&&this.isDragStarted&&this.cancelDrag()}),e.addEventListener("mouseenter",t=>{let n=t.target;n.closest("swp-calendar-header")?this.handleHeaderMouseEnter(t):n.closest("swp-day-column")&&this.handleColumnMouseEnter(t)},!0),e.addEventListener("mouseleave",t=>{t.target.closest("swp-calendar-header")&&this.handleHeaderMouseLeave(t)},!0)),B.updateColumnBoundsCache(),window.addEventListener("resize",()=>{B.updateColumnBoundsCache()}),this.eventBus.on("navigation:completed",()=>{B.updateColumnBoundsCache()}),this.eventBus.on(v.GRID_RENDERED,t=>{this.handleGridRendered(t)}),this.eventBus.on("edgescroll:started",()=>{this.isScrollCompensating=!0,this.scrollableContent&&(this.lastScrollTop=this.scrollableContent.scrollTop)}),this.eventBus.on("edgescroll:stopped",()=>{this.isScrollCompensating=!1}),this.eventBus.on("drag:mouseenter-header",()=>{this.scrollDeltaY=0,this.lastScrollTop=0}),this.eventBus.on("drag:mouseenter-column",()=>{this.scrollDeltaY=0,this.lastScrollTop=0})}handleGridRendered(e){this.scrollableContent=document.querySelector("swp-scrollable-content"),this.scrollableContent.addEventListener("scroll",this.handleScroll.bind(this),{passive:!0})}handleMouseDown(e){this.cleanupDragState(),B.updateColumnBoundsCache();let t=e.target;if(t.closest("swp-resize-handle"))return;let n=t;for(;n&&n.tagName!=="SWP-GRID-CONTAINER"&&!(n.tagName==="SWP-EVENT"||n.tagName==="SWP-ALLDAY-EVENT");)if(n=n.parentElement,!n)return;if(n){this.originalElement=n;let r=n.getBoundingClientRect();this.mouseOffset={x:e.clientX-r.left,y:e.clientY-r.top},this.mouseDownPosition={x:e.clientX,y:e.clientY}}}handleMouseMove(e){if(e.buttons===1){if(this.currentMousePosition={x:e.clientX,y:e.clientY},!this.isDragStarted&&this.originalElement&&!this.initializeDrag(this.currentMousePosition))return;this.isDragStarted&&this.originalElement&&this.draggedClone&&(this.continueDrag(this.currentMousePosition),this.detectColumnChange(this.currentMousePosition))}}initializeDrag(e){let t=Math.abs(e.x-this.mouseDownPosition.x),n=Math.abs(e.y-this.mouseDownPosition.y);if(Math.sqrt(t*t+n*n)0&&e.forEach(t=>t.remove())}cancelDrag(){if(!this.originalElement||!this.draggedClone)return;let e=this.draggedClone.getBoundingClientRect(),t=this.originalElement.getBoundingClientRect(),n=t.left-e.left,r=t.top-e.top;this.draggedClone.style.transition="transform 300ms ease-out",this.draggedClone.style.transform=`translate(${n}px, ${r}px)`,setTimeout(()=>{this.cleanupAllClones(),this.originalElement&&(this.originalElement.style.opacity="",this.originalElement.style.cursor=""),this.eventBus.emit("drag:cancelled",{originalElement:this.originalElement,reason:"mouse-left-grid"}),this.cleanupDragState(),this.stopDragAnimation()},300)}calculateSnapPosition(e,t){let n=e-this.mouseOffset.y,r=this.positionUtils.getPositionFromCoordinate(n,t);return Math.max(0,r)}animateDrag(){if(!this.isDragStarted||!this.draggedClone||!this.targetColumn){this.dragAnimationId=null;return}let e=this.targetY-this.currentY,t=e*.3;if(Math.abs(e)>.5){this.currentY+=t;let n={originalElement:this.originalElement,draggedClone:this.draggedClone,mousePosition:this.currentMousePosition,snappedY:this.currentY,columnBounds:this.targetColumn,mouseOffset:this.mouseOffset};this.eventBus.emit("drag:move",n),this.dragAnimationId=requestAnimationFrame(()=>this.animateDrag())}else{this.currentY=this.targetY;let n={originalElement:this.originalElement,draggedClone:this.draggedClone,mousePosition:this.currentMousePosition,snappedY:this.currentY,columnBounds:this.targetColumn,mouseOffset:this.mouseOffset};this.eventBus.emit("drag:move",n),this.dragAnimationId=null}}handleScroll(){if(!this.isDragStarted||!this.draggedClone||!this.scrollableContent||!this.isScrollCompensating)return;let e=this.scrollableContent.scrollTop,t=e-this.lastScrollTop;this.scrollDeltaY+=t,this.lastScrollTop=e,this.continueDrag(this.currentMousePosition)}stopDragAnimation(){this.dragAnimationId!==null&&(cancelAnimationFrame(this.dragAnimationId),this.dragAnimationId=null)}cleanupDragState(){this.previousColumn=null,this.originalElement=null,this.draggedClone=null,this.currentColumn=null,this.originalSourceColumn=null,this.isDragStarted=!1,this.scrollDeltaY=0,this.lastScrollTop=0}detectDropTarget(e){let t=this.draggedClone;for(;t&&t!==document.body;){if(t.tagName==="SWP-ALLDAY-CONTAINER")return"swp-day-header";if(t.tagName==="SWP-DAY-COLUMN")return"swp-day-column";t=t.parentElement}return null}handleHeaderMouseEnter(e){if(!this.isDragStarted||!this.draggedClone)return;let t={x:e.clientX,y:e.clientY},n=B.getColumnBounds(t);if(n){let r=Q.extractCalendarEventFromElement(this.draggedClone),s={targetColumn:n,mousePosition:t,originalElement:this.originalElement,draggedClone:this.draggedClone,calendarEvent:r,replaceClone:i=>{this.draggedClone=i,this.dragAnimationId}};this.eventBus.emit("drag:mouseenter-header",s)}}handleColumnMouseEnter(e){if(!this.isDragStarted||!this.draggedClone||!this.draggedClone.hasAttribute("data-allday"))return;let t={x:e.clientX,y:e.clientY},n=B.getColumnBounds(t);if(!n)return;let r=this.calculateSnapPosition(t.y,n),s=Q.extractCalendarEventFromElement(this.draggedClone),i={targetColumn:n,mousePosition:t,snappedY:r,originalElement:this.originalElement,draggedClone:this.draggedClone,calendarEvent:s,replaceClone:a=>{this.draggedClone=a,this.dragAnimationId,this.stopDragAnimation()}};this.eventBus.emit("drag:mouseenter-column",i)}handleHeaderMouseLeave(e){if(!this.isDragStarted||!this.draggedClone||!this.draggedClone.hasAttribute("data-allday"))return;let t={x:e.clientX,y:e.clientY},n=B.getColumnBounds(t);if(!n)return;let r={targetDate:n.date,mousePosition:t,originalElement:this.originalElement,draggedClone:this.draggedClone};this.eventBus.emit("drag:mouseleave-header",r)}};var xe=class{constructor(e){this.weekDates=e,this.tracks=[]}calculateLayout(e){let t=[];this.tracks=[new Array(this.weekDates.length).fill(!1)];let n=e.filter(r=>this.isEventVisible(r));for(let r of n){let s=this.getEventStartDay(r),i=this.getEventEndDay(r);if(s>0&&i>0){let a=this.findAvailableTrack(s-1,i-1);for(let d=s-1;d<=i-1;d++)this.tracks[a][d]=!0;let c={calenderEvent:r,gridArea:`${a+1} / ${s} / ${a+2} / ${i+1}`,startColumn:s,endColumn:i,row:a+1,columnSpan:i-s+1};t.push(c)}}return t}findAvailableTrack(e,t){for(let n=0;n=0?s+1:0}getEventEndDay(e){let t=this.formatDate(e.end),n=this.weekDates[this.weekDates.length-1],r=t>n?n:t,s=this.weekDates.indexOf(r);return s>=0?s+1:0}isEventVisible(e){if(this.weekDates.length===0)return!1;let t=this.formatDate(e.start),n=this.formatDate(e.end),r=this.weekDates[0],s=this.weekDates[this.weekDates.length-1];return!(ns)}formatDate(e){let t=e.getFullYear(),n=String(e.getMonth()+1).padStart(2,"0"),r=String(e.getDate()).padStart(2,"0");return`${t}-${n}-${r}`}};var Ie=class{constructor(e,t,n){this.layoutEngine=null,this.currentAllDayEvents=[],this.currentWeekDates=[],this.isExpanded=!1,this.actualRowCount=0,this.eventManager=e,this.allDayEventRenderer=t,this.dateService=n,document.documentElement.style.setProperty("--single-row-height",`${ne.EVENT_HEIGHT}px`),this.setupEventListeners()}setupEventListeners(){b.on("drag:mouseenter-header",e=>{let t=e.detail;t.draggedClone.hasAttribute("data-allday")||(console.log("\u{1F504} AllDayManager: Received drag:mouseenter-header",{targetDate:t.targetColumn,originalElementId:t.originalElement?.dataset?.eventId,originalElementTag:t.originalElement?.tagName}),this.handleConvertToAllDay(t))}),b.on("drag:mouseleave-header",e=>{let{originalElement:t,cloneElement:n}=e.detail;console.log("\u{1F6AA} AllDayManager: Received drag:mouseleave-header",{originalElementId:t?.dataset?.eventId})}),b.on("drag:start",e=>{let t=e.detail;t.draggedClone?.hasAttribute("data-allday")&&this.allDayEventRenderer.handleDragStart(t)}),b.on("drag:column-change",e=>{let t=e.detail;t.draggedClone?.hasAttribute("data-allday")&&this.handleColumnChange(t)}),b.on("drag:end",e=>{let t=e.detail;if(console.log("\u{1F3AF} AllDayManager: drag:end received",{target:t.target,originalElementTag:t.originalElement?.tagName,hasAllDayAttribute:t.originalElement?.hasAttribute("data-allday"),eventId:t.originalElement?.dataset.eventId}),t.target==="swp-day-header"&&t.originalElement?.hasAttribute("data-allday")){console.log("\u2705 AllDayManager: Handling all-day \u2192 all-day drop"),this.handleDragEnd(t);return}if(t.target==="swp-day-header"&&!t.originalElement?.hasAttribute("data-allday")){console.log("\u{1F504} AllDayManager: Timed \u2192 all-day conversion on drop"),this.handleTimedToAllDayDrop(t);return}if(t.target==="swp-day-column"&&t.originalElement?.hasAttribute("data-allday")){let n=t.originalElement.dataset.eventId;console.log("\u{1F504} AllDayManager: All-day \u2192 timed conversion",{eventId:n}),this.fadeOutAndRemove(t.originalElement);let r=this.currentAllDayEvents.filter(i=>i.id!==n),s=this.calculateAllDayEventsLayout(r,this.currentWeekDates);this.allDayEventRenderer.renderAllDayEventsForPeriod(s),this.checkAndAnimateAllDayHeight()}}),b.on("drag:cancelled",e=>{let{draggedElement:t,reason:n}=e.detail;console.log("\u{1F6AB} AllDayManager: Drag cancelled",{eventId:t?.dataset?.eventId,reason:n})}),b.on("header:ready",async e=>{let t=e.detail,n=new Date(t.headerElements.at(0).date),r=new Date(t.headerElements.at(-1).date),i=(await this.eventManager.getEventsForPeriod(n,r)).filter(c=>c.allDay),a=this.calculateAllDayEventsLayout(i,t.headerElements);this.allDayEventRenderer.renderAllDayEventsForPeriod(a),this.checkAndAnimateAllDayHeight()}),b.on(v.VIEW_CHANGED,e=>{this.allDayEventRenderer.handleViewChanged(e)})}getAllDayContainer(){return document.querySelector("swp-calendar-header swp-allday-container")}getCalendarHeader(){return document.querySelector("swp-calendar-header")}getHeaderSpacer(){return document.querySelector("swp-header-spacer")}getMaxRowFromDOM(){let e=this.getAllDayContainer();if(!e)return 0;let t=0;return e.querySelectorAll("swp-allday-event:not(.max-event-indicator):not([data-removing])").forEach(r=>{let i=parseInt(r.style.gridRow)||1;t=Math.max(t,i)}),t}getGridAreaFromDOM(e){let t=this.getAllDayContainer();return t&&t.querySelector(`[data-event-id="${e}"]`)?.style.gridArea||null}countEventsInColumnFromDOM(e){let t=this.getAllDayContainer();if(!t)return 0;let n=0;return t.querySelectorAll("swp-allday-event:not(.max-event-indicator)").forEach(s=>{let c=s.style.gridColumn.match(/(\d+)\s*\/\s*(\d+)/);if(c){let d=parseInt(c[1]),g=parseInt(c[2])-1;d<=e&&g>=e&&n++}}),n}calculateAllDayHeight(e){let t=document.documentElement,n=e*ne.SINGLE_ROW_HEIGHT,r=t.style.getPropertyValue("--all-day-row-height")||"0px",s=parseInt(r)||0,i=n-s;return{targetHeight:n,currentHeight:s,heightDifference:i}}checkAndAnimateAllDayHeight(){let e=this.getMaxRowFromDOM();console.log("\u{1F4CA} AllDayManager: Height calculation",{maxRows:e,isExpanded:this.isExpanded}),this.actualRowCount=e;let t=e;e>ne.MAX_COLLAPSED_ROWS?(this.updateChevronButton(!0),this.isExpanded?this.clearOverflowIndicators():(t=ne.MAX_COLLAPSED_ROWS,this.updateOverflowIndicators())):(this.updateChevronButton(!1),this.clearOverflowIndicators()),console.log("\u{1F3AC} AllDayManager: Will animate to",{displayRows:t,maxRows:e,willAnimate:t!==this.actualRowCount}),console.log(`\u{1F3AF} AllDayManager: Animating to ${t} rows`),this.animateToRows(t)}animateToRows(e){let{targetHeight:t,currentHeight:n,heightDifference:r}=this.calculateAllDayHeight(e);if(t===n)return;console.log(`\u{1F3AC} All-day height animation: ${n}px \u2192 ${t}px (${Math.ceil(n/ne.SINGLE_ROW_HEIGHT)} \u2192 ${e} rows)`);let s=this.getCalendarHeader(),i=this.getHeaderSpacer(),a=this.getAllDayContainer();if(!s||!a)return;let c=parseFloat(getComputedStyle(s).height),d=c+r,g=[s.animate([{height:`${c}px`},{height:`${d}px`}],{duration:150,easing:"ease-out",fill:"forwards"})];if(i){let E=document.documentElement.style.getPropertyValue("--header-height"),m=parseInt(E),u=m+n,D=m+t;g.push(i.animate([{height:`${u}px`},{height:`${D}px`}],{duration:150,easing:"ease-out"}))}Promise.all(g.map(w=>w.finished)).then(()=>{document.documentElement.style.setProperty("--all-day-row-height",`${t}px`),b.emit("header:height-changed")})}calculateAllDayEventsLayout(e,t){return this.currentAllDayEvents=e,this.currentWeekDates=t,new xe(t.map(r=>r.date)).calculateLayout(e)}handleConvertToAllDay(e){let t=this.getAllDayContainer();if(!t)return;let n=oe.fromCalendarEvent(e.calendarEvent);n.style.gridRow="1",n.style.gridColumn=e.targetColumn.index.toString(),e.draggedClone.remove(),e.replaceClone(n),t.appendChild(n),B.updateColumnBoundsCache(),this.checkAndAnimateAllDayHeight()}handleColumnChange(e){if(!this.getAllDayContainer())return;let n=B.getColumnBounds(e.mousePosition);if(n==null||!e.draggedClone)return;let r=window.getComputedStyle(e.draggedClone),s=parseInt(r.gridColumnStart)||n.index,a=(parseInt(r.gridColumnEnd)||n.index+1)-s,c=n.index,d=c+a;e.draggedClone.style.gridColumn=`${c} / ${d}`}fadeOutAndRemove(e){console.log("\u{1F5D1}\uFE0F AllDayManager: About to remove all-day event",{eventId:e.dataset.eventId,element:e.tagName}),e.setAttribute("data-removing","true"),e.style.transition="opacity 0.3s ease-out",e.style.opacity="0",setTimeout(()=>{e.remove(),console.log("\u2705 AllDayManager: All-day event removed from DOM")},300)}async handleTimedToAllDayDrop(e){if(!e.draggedClone||!e.finalPosition.column)return;let t=e.draggedClone,n=t.eventId.replace("clone-",""),r=e.finalPosition.column.date;console.log("\u{1F504} AllDayManager: Converting timed event to all-day",{eventId:n,targetDate:r});let s=new Date(r);s.setHours(t.start.getHours(),t.start.getMinutes(),0,0);let i=new Date(r);i.setHours(t.end.getHours(),t.end.getMinutes(),0,0),await this.eventManager.updateEvent(n,{start:s,end:i,allDay:!0}),this.fadeOutAndRemove(e.originalElement);let a={id:n,title:t.title,start:s,end:i,type:t.type,allDay:!0,syncStatus:"synced"},c=[...this.currentAllDayEvents,a],d=this.calculateAllDayEventsLayout(c,this.currentWeekDates);this.allDayEventRenderer.renderAllDayEventsForPeriod(d),this.checkAndAnimateAllDayHeight()}async handleDragEnd(e){if(!e.draggedClone||!e.finalPosition.column)return;let t=e.draggedClone,n=t.eventId.replace("clone-",""),r=e.finalPosition.column.date,s=this.dateService.differenceInCalendarDays(t.end,t.start),i=new Date(r);i.setHours(t.start.getHours(),t.start.getMinutes(),0,0);let a=new Date(r);a.setDate(a.getDate()+s),a.setHours(t.end.getHours(),t.end.getMinutes(),0,0),await this.eventManager.updateEvent(n,{start:i,end:a,allDay:!0}),this.fadeOutAndRemove(e.originalElement);let c=this.currentAllDayEvents.map(g=>g.id===n?{...g,start:i,end:a}:g),d=this.calculateAllDayEventsLayout(c,this.currentWeekDates);this.allDayEventRenderer.renderAllDayEventsForPeriod(d),this.checkAndAnimateAllDayHeight()}updateChevronButton(e){let t=this.getHeaderSpacer();if(!t)return;let n=t.querySelector(".allday-chevron");e&&!n?(n=document.createElement("button"),n.className="allday-chevron collapsed",n.innerHTML=` + + + + `,n.onclick=()=>this.toggleExpanded(),t.appendChild(n)):!e&&n?n.remove():n&&(n.classList.toggle("collapsed",!this.isExpanded),n.classList.toggle("expanded",this.isExpanded))}toggleExpanded(){this.isExpanded=!this.isExpanded,this.checkAndAnimateAllDayHeight(),document.querySelectorAll("swp-allday-container swp-allday-event.max-event-overflow-hide, swp-allday-container swp-allday-event.max-event-overflow-show").forEach(t=>{this.isExpanded?(t.classList.remove("max-event-overflow-hide"),t.classList.add("max-event-overflow-show")):(t.classList.remove("max-event-overflow-show"),t.classList.add("max-event-overflow-hide"))})}countEventsInColumn(e){return this.countEventsInColumnFromDOM(e.index)}updateOverflowIndicators(){let e=this.getAllDayContainer();if(!e)return;B.getColumns().forEach(n=>{let s=this.countEventsInColumn(n)-ne.MAX_COLLAPSED_ROWS;if(s>0){let i=e.querySelector(`.max-event-indicator[data-column="${n.index}"]`);if(i)i.innerHTML=`+${s+1} more`;else{let a=document.createElement("swp-allday-event");a.className="max-event-indicator",a.setAttribute("data-column",n.index.toString()),a.style.gridRow=ne.MAX_COLLAPSED_ROWS.toString(),a.style.gridColumn=n.index.toString(),a.innerHTML=`+${s+1} more`,a.onclick=c=>{c.stopPropagation(),this.toggleExpanded()},e.appendChild(a)}}})}clearOverflowIndicators(){let e=this.getAllDayContainer();e&&e.querySelectorAll(".max-event-indicator").forEach(t=>{t.remove()})}};var Oe=class{constructor(e,t){this.config=e,this.positionUtils=t,this.isResizing=!1,this.targetEl=null,this.startY=0,this.startDurationMin=0,this.animationId=null,this.currentHeight=0,this.targetHeight=0,this.pointerCaptured=!1,this.ANIMATION_SPEED=.35,this.Z_INDEX_RESIZING="1000",this.EVENT_REFRESH_THRESHOLD=.5,this.onMouseOver=r=>{let i=r.target.closest("swp-event");if(i&&!this.isResizing&&!i.querySelector(":scope > swp-resize-handle")){let a=this.createResizeHandle();i.appendChild(a)}},this.onPointerDown=r=>{let s=r.target.closest("swp-resize-handle");if(!s)return;let i=s.parentElement;this.startResizing(i,r)},this.onPointerMove=r=>{!this.isResizing||!this.targetEl||this.updateResizeHeight(r.clientY)},this.animate=()=>{if(!this.isResizing||!this.targetEl){this.animationId=null;return}let r=this.targetHeight-this.currentHeight;Math.abs(r)>this.EVENT_REFRESH_THRESHOLD?(this.currentHeight+=r*this.ANIMATION_SPEED,this.targetEl.updateHeight?.(this.currentHeight),this.animationId=requestAnimationFrame(this.animate)):this.finalizeAnimation()},this.onPointerUp=r=>{!this.isResizing||!this.targetEl||(this.cleanupAnimation(),this.snapToGrid(),this.emitResizeEndEvent(),this.cleanupResizing(r))};let n=this.config.gridSettings;this.snapMin=n.snapInterval,this.minDurationMin=this.snapMin}initialize(){this.attachGlobalListeners()}destroy(){this.removeEventListeners()}removeEventListeners(){let e=document.querySelector("swp-calendar-container");e&&e.removeEventListener("mouseover",this.onMouseOver,!0),document.removeEventListener("pointerdown",this.onPointerDown,!0),document.removeEventListener("pointermove",this.onPointerMove,!0),document.removeEventListener("pointerup",this.onPointerUp,!0)}createResizeHandle(){let e=document.createElement("swp-resize-handle");return e.setAttribute("aria-label","Resize event"),e.setAttribute("role","separator"),e}attachGlobalListeners(){let e=document.querySelector("swp-calendar-container");e&&e.addEventListener("mouseover",this.onMouseOver,!0),document.addEventListener("pointerdown",this.onPointerDown,!0),document.addEventListener("pointermove",this.onPointerMove,!0),document.addEventListener("pointerup",this.onPointerUp,!0)}startResizing(e,t){this.targetEl=e,this.isResizing=!0,this.startY=t.clientY;let n=e.offsetHeight;this.startDurationMin=Math.max(this.minDurationMin,Math.round(this.positionUtils.pixelsToMinutes(n))),this.setZIndexForResizing(e),this.capturePointer(t),document.documentElement.classList.add("swp--resizing"),t.preventDefault()}setZIndexForResizing(e){let t=e.closest("swp-event-group")??e;this.prevZ=t.style.zIndex,t.style.zIndex=this.Z_INDEX_RESIZING}capturePointer(e){try{e.target.setPointerCapture?.(e.pointerId),this.pointerCaptured=!0}catch(t){console.warn("Pointer capture failed:",t)}}updateResizeHeight(e){let t=e-this.startY,r=this.positionUtils.minutesToPixels(this.startDurationMin)+t,s=this.positionUtils.minutesToPixels(this.minDurationMin);this.targetHeight=Math.max(s,r),this.animationId==null&&(this.currentHeight=this.targetEl?.offsetHeight,this.animate())}finalizeAnimation(){this.targetEl&&(this.currentHeight=this.targetHeight,this.targetEl.updateHeight?.(this.currentHeight),this.animationId=null)}cleanupAnimation(){this.animationId!=null&&(cancelAnimationFrame(this.animationId),this.animationId=null)}snapToGrid(){if(!this.targetEl)return;let e=this.targetEl.offsetHeight,t=this.positionUtils.minutesToPixels(this.snapMin),n=Math.round(e/t)*t,r=this.positionUtils.minutesToPixels(this.minDurationMin),s=Math.max(r,n)-3;this.targetEl.updateHeight?.(s)}emitResizeEndEvent(){if(!this.targetEl)return;let t={eventId:this.targetEl.dataset.eventId||"",element:this.targetEl,finalHeight:this.targetEl.offsetHeight};b.emit("resize:end",t)}cleanupResizing(e){this.restoreZIndex(),this.releasePointer(e),this.isResizing=!1,this.targetEl=null,document.documentElement.classList.remove("swp--resizing")}restoreZIndex(){if(!this.targetEl||this.prevZ===void 0)return;let e=this.targetEl.closest("swp-event-group")??this.targetEl;e.style.zIndex=this.prevZ,this.prevZ=void 0}releasePointer(e){if(this.pointerCaptured)try{e.target.releasePointerCapture?.(e.pointerId),this.pointerCaptured=!1}catch(t){console.warn("Pointer release failed:",t)}}};var Le=class{constructor(e){this.eventBus=e,this.scrollableContent=null,this.timeGrid=null,this.draggedClone=null,this.scrollRAF=null,this.mouseY=0,this.isDragging=!1,this.isScrolling=!1,this.lastTs=0,this.rect=null,this.initialScrollTop=0,this.scrollListener=null,this.OUTER_ZONE=100,this.INNER_ZONE=50,this.SLOW_SPEED_PXS=140,this.FAST_SPEED_PXS=640,this.init()}init(){setTimeout(()=>{this.scrollableContent=document.querySelector("swp-scrollable-content"),this.timeGrid=document.querySelector("swp-time-grid"),this.scrollableContent&&(this.scrollableContent.style.scrollBehavior="auto",this.scrollListener=this.handleScroll.bind(this),this.scrollableContent.addEventListener("scroll",this.scrollListener,{passive:!0}))},100),document.body.addEventListener("mousemove",e=>{this.isDragging&&(this.mouseY=e.clientY)}),this.subscribeToEvents()}subscribeToEvents(){this.eventBus.on("drag:start",e=>{let t=e.detail;this.draggedClone=t.draggedClone,this.startDrag()}),this.eventBus.on("drag:end",()=>this.stopDrag()),this.eventBus.on("drag:cancelled",()=>this.stopDrag()),this.eventBus.on("drag:mouseenter-header",()=>{console.log("\u{1F504} EdgeScrollManager: Event converting to all-day - stopping scroll"),this.stopDrag()}),this.eventBus.on("drag:mouseenter-column",()=>{this.startDrag()})}startDrag(){console.log("\u{1F3AC} EdgeScrollManager: Starting drag"),this.isDragging=!0,this.isScrolling=!1,this.lastTs=performance.now(),this.scrollableContent&&(this.initialScrollTop=this.scrollableContent.scrollTop),this.scrollRAF===null&&(this.scrollRAF=requestAnimationFrame(e=>this.scrollTick(e)))}stopDrag(){this.isDragging=!1,this.isScrolling&&(this.isScrolling=!1,console.log("\u{1F6D1} EdgeScrollManager: Edge-scroll stopped (drag ended)"),this.eventBus.emit("edgescroll:stopped",{})),this.scrollRAF!==null&&(cancelAnimationFrame(this.scrollRAF),this.scrollRAF=null),this.rect=null,this.lastTs=0,this.initialScrollTop=0}handleScroll(){if(!this.isDragging||!this.scrollableContent)return;let e=this.scrollableContent.scrollTop,t=Math.abs(e-this.initialScrollTop);t>1&&!this.isScrolling&&(this.isScrolling=!0,console.log("\u{1F4BE} EdgeScrollManager: Edge-scroll started (actual scroll detected)",{initialScrollTop:this.initialScrollTop,currentScrollTop:e,scrollDelta:t}),this.eventBus.emit("edgescroll:started",{}))}scrollTick(e){let t=this.lastTs?(e-this.lastTs)/1e3:0;if(this.lastTs=e,!this.scrollableContent){this.stopDrag();return}this.rect||(this.rect=this.scrollableContent.getBoundingClientRect());let n=0;if(this.isDragging){let r=this.mouseY-this.rect.top,s=this.rect.bottom-this.mouseY;r=g&&n>0;w||E?(this.isScrolling&&(this.isScrolling=!1,this.initialScrollTop=this.scrollableContent.scrollTop,console.log("\u{1F6D1} EdgeScrollManager: Edge-scroll stopped (reached boundary)"),this.eventBus.emit("edgescroll:stopped",{})),this.isDragging&&(this.scrollRAF=requestAnimationFrame(m=>this.scrollTick(m)))):(this.scrollableContent.scrollTop+=n*t,this.rect=null,this.scrollRAF=requestAnimationFrame(m=>this.scrollTick(m)))}else this.isScrolling&&(this.isScrolling=!1,this.initialScrollTop=this.scrollableContent.scrollTop,console.log("\u{1F6D1} EdgeScrollManager: Edge-scroll stopped (mouse left edge)"),this.eventBus.emit("edgescroll:stopped",{})),this.isDragging?this.scrollRAF=requestAnimationFrame(r=>this.scrollTick(r)):this.stopDrag()}};var $e=class{constructor(e,t){this.headerRenderer=e,this.config=t,this.handleDragMouseEnterHeader=this.handleDragMouseEnterHeader.bind(this),this.handleDragMouseLeaveHeader=this.handleDragMouseLeaveHeader.bind(this),this.setupNavigationListener()}setupHeaderDragListeners(){console.log("\u{1F3AF} HeaderManager: Setting up drag event listeners"),b.on("drag:mouseenter-header",this.handleDragMouseEnterHeader),b.on("drag:mouseleave-header",this.handleDragMouseLeaveHeader),console.log("\u2705 HeaderManager: Drag event listeners attached")}handleDragMouseEnterHeader(e){let{targetColumn:t,mousePosition:n,originalElement:r,draggedClone:s}=e.detail;console.log("\u{1F3AF} HeaderManager: Received drag:mouseenter-header",{targetDate:t,originalElement:!!r,cloneElement:!!s})}handleDragMouseLeaveHeader(e){let{targetDate:t,mousePosition:n,originalElement:r,draggedClone:s}=e.detail;console.log("\u{1F6AA} HeaderManager: Received drag:mouseleave-header",{targetDate:t,originalElement:!!r,cloneElement:!!s})}setupNavigationListener(){b.on(v.NAVIGATION_COMPLETED,e=>{let{currentDate:t}=e.detail;this.updateHeader(t)}),b.on(v.DATE_CHANGED,e=>{let{currentDate:t}=e.detail;this.updateHeader(t)}),b.on("workweek:header-update",e=>{let{currentDate:t}=e.detail;this.updateHeader(t)})}updateHeader(e){console.log("\u{1F3AF} HeaderManager.updateHeader called",{currentDate:e,rendererType:this.headerRenderer.constructor.name});let t=document.querySelector("swp-calendar-header");if(!t){console.warn("\u274C HeaderManager: No calendar header found!");return}t.innerHTML="";let n={currentWeek:e,config:this.config};this.headerRenderer.render(t,n),this.setupHeaderDragListeners();let r={headerElements:B.getHeaderColumns()};b.emit("header:ready",r)}};var He=class{constructor(e,t){this.buttonListeners=new Map,this.eventBus=e,this.config=t,this.setupButtonListeners()}setupButtonListeners(){document.querySelectorAll("swp-preset-button[data-workweek]").forEach(t=>{let n=r=>{r.preventDefault();let s=t.getAttribute("data-workweek");s&&this.changePreset(s)};t.addEventListener("click",n),this.buttonListeners.set(t,n)}),this.updateButtonStates()}changePreset(e){if(!me[e]){console.warn(`Invalid preset ID "${e}"`);return}if(e===this.config.currentWorkWeek)return;let t=this.config.currentWorkWeek;this.config.currentWorkWeek=e;let n=me[e];this.updateButtonStates(),this.eventBus.emit(v.WORKWEEK_CHANGED,{workWeekId:e,previousWorkWeekId:t,settings:n})}updateButtonStates(){document.querySelectorAll("swp-preset-button[data-workweek]").forEach(t=>{t.getAttribute("data-workweek")===this.config.currentWorkWeek?t.setAttribute("data-active","true"):t.removeAttribute("data-active")})}};var Be=class{constructor(e,t){this.indexedDB=e,this.queue=t}async loadEvents(){return this.indexedDB.isInitialized()||(await this.indexedDB.initialize(),await this.indexedDB.seedIfEmpty()),await this.indexedDB.getAllEvents()}async createEvent(e,t="local"){let n=this.generateEventId(),s={...e,id:n,syncStatus:t==="local"?"pending":"synced"};return await this.indexedDB.saveEvent(s),t==="local"&&await this.queue.enqueue({type:"create",eventId:n,data:s,timestamp:Date.now(),retryCount:0}),s}async updateEvent(e,t,n="local"){let r=await this.indexedDB.getEvent(e);if(!r)throw new Error(`Event with ID ${e} not found`);let i={...r,...t,id:e,syncStatus:n==="local"?"pending":"synced"};return await this.indexedDB.saveEvent(i),n==="local"&&await this.queue.enqueue({type:"update",eventId:e,data:t,timestamp:Date.now(),retryCount:0}),i}async deleteEvent(e,t="local"){if(!await this.indexedDB.getEvent(e))throw new Error(`Event with ID ${e} not found`);t==="local"&&await this.queue.enqueue({type:"delete",eventId:e,data:{},timestamp:Date.now(),retryCount:0}),await this.indexedDB.deleteEvent(e)}generateEventId(){let e=Date.now(),t=Math.random().toString(36).substring(2,9);return`${e}-${t}`}};var We=class{constructor(e){this.apiEndpoint=e.apiEndpoint}async sendCreate(e){throw new Error("ApiEventRepository.sendCreate not implemented yet")}async sendUpdate(e,t){throw new Error("ApiEventRepository.sendUpdate not implemented yet")}async sendDelete(e){throw new Error("ApiEventRepository.sendDelete not implemented yet")}async fetchAll(){throw new Error("ApiEventRepository.fetchAll not implemented yet")}async initializeSignalR(){throw new Error("SignalR not implemented yet")}};var J=class o{constructor(){this.db=null,this.initialized=!1}async initialize(){return new Promise((e,t)=>{let n=indexedDB.open(o.DB_NAME,o.DB_VERSION);n.onerror=()=>{t(new Error(`Failed to open IndexedDB: ${n.error}`))},n.onsuccess=()=>{this.db=n.result,this.initialized=!0,e()},n.onupgradeneeded=r=>{let s=r.target.result;if(!s.objectStoreNames.contains(o.EVENTS_STORE)){let i=s.createObjectStore(o.EVENTS_STORE,{keyPath:"id"});i.createIndex("start","start",{unique:!1}),i.createIndex("end","end",{unique:!1}),i.createIndex("syncStatus","syncStatus",{unique:!1})}s.objectStoreNames.contains(o.QUEUE_STORE)||s.createObjectStore(o.QUEUE_STORE,{keyPath:"id"}).createIndex("timestamp","timestamp",{unique:!1}),s.objectStoreNames.contains(o.SYNC_STATE_STORE)||s.createObjectStore(o.SYNC_STATE_STORE,{keyPath:"key"})}})}isInitialized(){return this.initialized}ensureDB(){if(!this.db)throw new Error("IndexedDB not initialized. Call initialize() first.");return this.db}async getEvent(e){let t=this.ensureDB();return new Promise((n,r)=>{let a=t.transaction([o.EVENTS_STORE],"readonly").objectStore(o.EVENTS_STORE).get(e);a.onsuccess=()=>{let c=a.result;n(c?this.deserializeEvent(c):null)},a.onerror=()=>{r(new Error(`Failed to get event ${e}: ${a.error}`))}})}async getAllEvents(){let e=this.ensureDB();return new Promise((t,n)=>{let i=e.transaction([o.EVENTS_STORE],"readonly").objectStore(o.EVENTS_STORE).getAll();i.onsuccess=()=>{let a=i.result;t(a.map(c=>this.deserializeEvent(c)))},i.onerror=()=>{n(new Error(`Failed to get all events: ${i.error}`))}})}async saveEvent(e){let t=this.ensureDB(),n=this.serializeEvent(e);return new Promise((r,s)=>{let c=t.transaction([o.EVENTS_STORE],"readwrite").objectStore(o.EVENTS_STORE).put(n);c.onsuccess=()=>{r()},c.onerror=()=>{s(new Error(`Failed to save event ${e.id}: ${c.error}`))}})}async deleteEvent(e){let t=this.ensureDB();return new Promise((n,r)=>{let a=t.transaction([o.EVENTS_STORE],"readwrite").objectStore(o.EVENTS_STORE).delete(e);a.onsuccess=()=>{n()},a.onerror=()=>{r(new Error(`Failed to delete event ${e}: ${a.error}`))}})}async addToQueue(e){let t=this.ensureDB(),n={...e,id:`${e.type}-${e.eventId}-${Date.now()}`};return new Promise((r,s)=>{let c=t.transaction([o.QUEUE_STORE],"readwrite").objectStore(o.QUEUE_STORE).put(n);c.onsuccess=()=>{r()},c.onerror=()=>{s(new Error(`Failed to add to queue: ${c.error}`))}})}async getQueue(){let e=this.ensureDB();return new Promise((t,n)=>{let a=e.transaction([o.QUEUE_STORE],"readonly").objectStore(o.QUEUE_STORE).index("timestamp").getAll();a.onsuccess=()=>{t(a.result)},a.onerror=()=>{n(new Error(`Failed to get queue: ${a.error}`))}})}async removeFromQueue(e){let t=this.ensureDB();return new Promise((n,r)=>{let a=t.transaction([o.QUEUE_STORE],"readwrite").objectStore(o.QUEUE_STORE).delete(e);a.onsuccess=()=>{n()},a.onerror=()=>{r(new Error(`Failed to remove from queue: ${a.error}`))}})}async clearQueue(){let e=this.ensureDB();return new Promise((t,n)=>{let i=e.transaction([o.QUEUE_STORE],"readwrite").objectStore(o.QUEUE_STORE).clear();i.onsuccess=()=>{t()},i.onerror=()=>{n(new Error(`Failed to clear queue: ${i.error}`))}})}async setSyncState(e,t){let n=this.ensureDB();return new Promise((r,s)=>{let c=n.transaction([o.SYNC_STATE_STORE],"readwrite").objectStore(o.SYNC_STATE_STORE).put({key:e,value:t});c.onsuccess=()=>{r()},c.onerror=()=>{s(new Error(`Failed to set sync state ${e}: ${c.error}`))}})}async getSyncState(e){let t=this.ensureDB();return new Promise((n,r)=>{let a=t.transaction([o.SYNC_STATE_STORE],"readonly").objectStore(o.SYNC_STATE_STORE).get(e);a.onsuccess=()=>{let c=a.result;n(c?c.value:null)},a.onerror=()=>{r(new Error(`Failed to get sync state ${e}: ${a.error}`))}})}serializeEvent(e){return{...e,start:e.start instanceof Date?e.start.toISOString():e.start,end:e.end instanceof Date?e.end.toISOString():e.end}}deserializeEvent(e){return{...e,start:typeof e.start=="string"?new Date(e.start):e.start,end:typeof e.end=="string"?new Date(e.end):e.end}}close(){this.db&&(this.db.close(),this.db=null)}static async deleteDatabase(){return new Promise((e,t)=>{let n=indexedDB.deleteDatabase(o.DB_NAME);n.onsuccess=()=>{e()},n.onerror=()=>{t(new Error(`Failed to delete database: ${n.error}`))}})}async seedIfEmpty(e="data/mock-events.json"){try{let t=await this.getAllEvents();if(t.length>0){console.log(`IndexedDB already has ${t.length} events - skipping seed`);return}if(console.log("IndexedDB is empty - seeding with mock data"),!navigator.onLine){console.warn("Offline and IndexedDB empty - starting with no events");return}let n=await fetch(e);if(!n.ok)throw new Error(`Failed to fetch mock events: ${n.statusText}`);let r=await n.json();for(let s of r){let i={...s,start:new Date(s.start),end:new Date(s.end),allDay:s.allDay||!1,syncStatus:"synced"};await this.saveEvent(i)}console.log(`Seeded IndexedDB with ${r.length} mock events`)}catch(t){console.error("Failed to seed IndexedDB:",t)}}};J.DB_NAME="CalendarDB";J.DB_VERSION=1;J.EVENTS_STORE="events";J.QUEUE_STORE="operationQueue";J.SYNC_STATE_STORE="syncState";var Pe=class{constructor(e){this.indexedDB=e}async enqueue(e){await this.indexedDB.addToQueue(e)}async peek(){let e=await this.indexedDB.getQueue();return e.length>0?e[0]:null}async getAll(){return await this.indexedDB.getQueue()}async remove(e){await this.indexedDB.removeFromQueue(e)}async dequeue(){let e=await this.peek();return e&&await this.remove(e.id),e}async clear(){await this.indexedDB.clearQueue()}async size(){return(await this.getAll()).length}async isEmpty(){return await this.size()===0}async getOperationsForEvent(e){return(await this.getAll()).filter(n=>n.eventId===e)}async removeOperationsForEvent(e){let t=await this.getOperationsForEvent(e);for(let n of t)await this.remove(n.id)}async incrementRetryCount(e){let n=(await this.getAll()).find(r=>r.id===e);n&&(n.retryCount++,await this.remove(e),await this.enqueue(n))}};var Ne=class{constructor(e,t,n,r){this.isOnline=navigator.onLine,this.isSyncing=!1,this.syncInterval=5e3,this.maxRetries=5,this.intervalId=null,this.eventBus=e,this.queue=t,this.indexedDB=n,this.apiRepository=r,this.setupNetworkListeners(),this.startSync(),console.log("SyncManager initialized and started")}setupNetworkListeners(){window.addEventListener("online",()=>{this.isOnline=!0,this.eventBus.emit(v.OFFLINE_MODE_CHANGED,{isOnline:!0}),console.log("SyncManager: Network online - starting sync"),this.startSync()}),window.addEventListener("offline",()=>{this.isOnline=!1,this.eventBus.emit(v.OFFLINE_MODE_CHANGED,{isOnline:!1}),console.log("SyncManager: Network offline - pausing sync"),this.stopSync()})}startSync(){this.intervalId||(console.log("SyncManager: Starting background sync"),this.processQueue(),this.intervalId=window.setInterval(()=>{this.processQueue()},this.syncInterval))}stopSync(){this.intervalId&&(window.clearInterval(this.intervalId),this.intervalId=null,console.log("SyncManager: Stopped background sync"))}async processQueue(){if(this.isOnline&&!this.isSyncing&&!await this.queue.isEmpty()){this.isSyncing=!0;try{let e=await this.queue.getAll();this.eventBus.emit(v.SYNC_STARTED,{operationCount:e.length});for(let t of e)await this.processOperation(t);this.eventBus.emit(v.SYNC_COMPLETED,{operationCount:e.length})}catch(e){console.error("SyncManager: Queue processing error:",e),this.eventBus.emit(v.SYNC_FAILED,{error:e instanceof Error?e.message:"Unknown error"})}finally{this.isSyncing=!1}}}async processOperation(e){if(e.retryCount>=this.maxRetries){console.error(`SyncManager: Max retries exceeded for operation ${e.id}`,e),await this.queue.remove(e.id),await this.markEventAsError(e.eventId);return}try{switch(e.type){case"create":await this.apiRepository.sendCreate(e.data);break;case"update":await this.apiRepository.sendUpdate(e.eventId,e.data);break;case"delete":await this.apiRepository.sendDelete(e.eventId);break;default:console.error(`SyncManager: Unknown operation type ${e.type}`),await this.queue.remove(e.id);return}await this.queue.remove(e.id),await this.markEventAsSynced(e.eventId),console.log(`SyncManager: Successfully synced operation ${e.id}`)}catch(t){console.error(`SyncManager: Failed to sync operation ${e.id}:`,t),await this.queue.incrementRetryCount(e.id);let n=this.calculateBackoff(e.retryCount+1);this.eventBus.emit(v.SYNC_RETRY,{operationId:e.id,retryCount:e.retryCount+1,nextRetryIn:n})}}async markEventAsSynced(e){try{let t=await this.indexedDB.getEvent(e);t&&(t.syncStatus="synced",await this.indexedDB.saveEvent(t))}catch(t){console.error(`SyncManager: Failed to mark event ${e} as synced:`,t)}}async markEventAsError(e){try{let t=await this.indexedDB.getEvent(e);t&&(t.syncStatus="error",await this.indexedDB.saveEvent(t))}catch(t){console.error(`SyncManager: Failed to mark event ${e} as error:`,t)}}calculateBackoff(e){let n=Math.pow(2,e)*1e3;return Math.min(n,6e4)}async triggerManualSync(){console.log("SyncManager: Manual sync triggered"),await this.processQueue()}getSyncStatus(){return{isOnline:this.isOnline,isSyncing:this.isSyncing,isRunning:this.intervalId!==null}}destroy(){this.stopSync()}};var Fe=class{render(e,t){let{currentWeek:n,config:r}=t,s=document.createElement("swp-allday-container");e.appendChild(s);let i=r.timeFormatConfig.timezone,a=r.timeFormatConfig.locale;this.dateService=new U(r);let c=r.getWorkWeekSettings(),d=this.dateService.getWorkWeekDates(n,c.workDays),g=r.dateViewSettings.weekDays;d.slice(0,g).forEach((E,m)=>{let u=document.createElement("swp-day-header");this.dateService.isSameDay(E,new Date)&&(u.dataset.today="true");let D=this.dateService.getDayName(E,"long",a).toUpperCase();u.innerHTML=` + ${D} + ${E.getDate()} + `,u.dataset.date=this.dateService.formatISODate(E),e.appendChild(u)})}};var _e=class{constructor(e,t){this.dateService=e,this.workHoursManager=t}render(e,t){let{currentWeek:n,config:r}=t,s=r.getWorkWeekSettings(),i=this.dateService.getWorkWeekDates(n,s.workDays),a=r.dateViewSettings;i.slice(0,a.weekDays).forEach(d=>{let g=document.createElement("swp-day-column");g.dataset.date=this.dateService.formatISODate(d),this.applyWorkHoursToColumn(g,d);let w=document.createElement("swp-events-layer");g.appendChild(w),e.appendChild(g)})}applyWorkHoursToColumn(e,t){let n=this.workHoursManager.getWorkHoursForDate(t);if(n==="off")e.dataset.workHours="off";else{let r=this.workHoursManager.calculateNonWorkHoursStyle(n);r&&(e.style.setProperty("--before-work-height",`${r.beforeWorkHeight}px`),e.style.setProperty("--after-work-top",`${r.afterWorkTop}px`))}}};var Ye=class{constructor(e,t,n,r,s){this.draggedClone=null,this.originalEvent=null,this.dateService=e,this.stackManager=t,this.layoutCoordinator=n,this.config=r,this.positionUtils=s}applyDragStyling(e){e.classList.add("dragging"),e.style.removeProperty("margin-left")}handleDragStart(e){if(this.originalEvent=e.originalElement,this.draggedClone=e.draggedClone,this.draggedClone&&e.columnBounds){this.applyDragStyling(this.draggedClone);let t=e.columnBounds.element.querySelector("swp-events-layer");if(t){t.appendChild(this.draggedClone);let n=this.originalEvent.getBoundingClientRect(),r=e.columnBounds.boundingClientRect,s=n.top-r.top;this.draggedClone.style.top=`${s}px`}}this.originalEvent.style.opacity="0.3",this.originalEvent.style.userSelect="none"}handleDragMove(e){let t=e.draggedClone,n=this.dateService.parseISO(e.columnBounds.date);t.updatePosition(n,e.snappedY)}handleColumnChange(e){let t=e.newColumn.element.querySelector("swp-events-layer");if(t&&e.draggedClone.parentElement!==t){t.appendChild(e.draggedClone);let n=parseFloat(e.draggedClone.style.top)||0,r=e.draggedClone,s=this.dateService.parseISO(e.newColumn.date);r.updatePosition(s,n)}}handleConvertAllDayToTimed(e){console.log("\u{1F3AF} DateEventRenderer: Converting all-day to timed event",{eventId:e.calendarEvent.id,targetColumn:e.targetColumn.date,snappedY:e.snappedY});let t=Q.fromCalendarEvent(e.calendarEvent),n=this.calculateEventPosition(e.calendarEvent);t.style.height=`${n.height-3}px`,t.style.left="2px",t.style.right="2px",t.style.width="auto",t.style.pointerEvents="none",this.applyDragStyling(t);let r=e.targetColumn.element.querySelector("swp-events-layer");e.draggedClone.remove(),e.replaceClone(t),r.appendChild(t)}handleDragEnd(e,t,n,r){if(!t||!e){console.warn("Missing draggedClone or originalElement");return}e.tagName==="SWP-EVENT"&&this.fadeOutAndRemove(e);let s=t.dataset.eventId;s&&s.startsWith("clone-")&&(t.dataset.eventId=s.replace("clone-","")),t.classList.remove("dragging"),t.style.pointerEvents="",this.draggedClone=null,this.originalEvent=null;let i=document.querySelector(`swp-event[data-event-id="clone-${s}"]`);i&&i.remove()}handleNavigationCompleted(){}fadeOutAndRemove(e){e.style.transition="opacity 0.3s ease-out",e.style.opacity="0",setTimeout(()=>{e.remove()},300)}renderEvents(e,t){let n=e.filter(s=>!s.allDay);this.getColumns(t).forEach(s=>{let i=this.getEventsForColumn(s,n),a=s.querySelector("swp-events-layer");a&&this.renderColumnEvents(i,a)})}renderSingleColumnEvents(e,t){let n=this.getEventsForColumn(e.element,t),r=e.element.querySelector("swp-events-layer");r&&this.renderColumnEvents(n,r)}renderColumnEvents(e,t){if(e.length===0)return;let n=this.layoutCoordinator.calculateColumnLayout(e);n.gridGroups.forEach(r=>{this.renderGridGroup(r,t)}),n.stackedEvents.forEach(r=>{let s=this.renderEvent(r.event);this.stackManager.applyStackLinkToElement(s,r.stackLink),this.stackManager.applyVisualStyling(s,r.stackLink.stackLevel),t.appendChild(s)})}renderGridGroup(e,t){let n=document.createElement("swp-event-group"),r=e.columns.length;n.classList.add(`cols-${r}`),n.classList.add(`stack-level-${e.stackLevel}`),n.style.top=`${e.position.top}px`;let s={stackLevel:e.stackLevel};this.stackManager.applyStackLinkToElement(n,s),this.stackManager.applyVisualStyling(n,e.stackLevel);let i=e.events[0];e.columns.forEach(a=>{let c=this.renderGridColumn(a,i.start);n.appendChild(c)}),t.appendChild(n)}renderGridColumn(e,t){let n=document.createElement("div");return n.style.position="relative",e.forEach(r=>{let s=this.renderEventInGrid(r,t);n.appendChild(s)}),n}renderEventInGrid(e,t){let n=Q.fromCalendarEvent(e),r=this.calculateEventPosition(e),i=(e.start.getTime()-t.getTime())/(1e3*60),a=this.config.gridSettings,c=i>0?i/60*a.hourHeight:0;return n.style.position="absolute",n.style.top=`${c}px`,n.style.height=`${r.height-3}px`,n.style.left="0",n.style.right="0",n}renderEvent(e){let t=Q.fromCalendarEvent(e),n=this.calculateEventPosition(e);return t.style.position="absolute",t.style.top=`${n.top+1}px`,t.style.height=`${n.height-3}px`,t.style.left="2px",t.style.right="2px",t}calculateEventPosition(e){return this.positionUtils.calculateEventPosition(e.start,e.end)}clearEvents(e){let t="swp-event",n="swp-event-group",r=e?e.querySelectorAll(t):document.querySelectorAll(t),s=e?e.querySelectorAll(n):document.querySelectorAll(n);r.forEach(i=>i.remove()),s.forEach(i=>i.remove())}getColumns(e){let t=e.querySelectorAll("swp-day-column");return Array.from(t)}getEventsForColumn(e,t){let n=e.dataset.date;if(!n)return[];let r=this.dateService.parseISO(`${n}T00:00:00`),s=this.dateService.parseISO(`${n}T23:59:59.999`);return t.filter(a=>a.startr)}};var Ve=class{constructor(){this.container=null,this.originalEvent=null,this.draggedClone=null,this.getContainer()}getContainer(){let e=document.querySelector("swp-calendar-header");return e&&(this.container=e.querySelector("swp-allday-container"),this.container||(this.container=document.createElement("swp-allday-container"),e.appendChild(this.container))),this.container}getAllDayContainer(){return document.querySelector("swp-calendar-header swp-allday-container")}handleDragStart(e){if(this.originalEvent=e.originalElement,this.draggedClone=e.draggedClone,this.draggedClone){let t=this.getAllDayContainer();if(!t)return;this.draggedClone.style.gridColumn=this.originalEvent.style.gridColumn,this.draggedClone.style.gridRow=this.originalEvent.style.gridRow,console.log("handleDragStart:this.draggedClone",this.draggedClone),t.appendChild(this.draggedClone),this.draggedClone.classList.add("dragging"),this.draggedClone.style.zIndex="1000",this.draggedClone.style.cursor="grabbing",this.originalEvent.style.opacity="0.3",this.originalEvent.style.userSelect="none"}}renderAllDayEventWithLayout(e,t){let n=this.getContainer();if(!n)return null;let r=oe.fromCalendarEvent(e);r.applyGridPositioning(t.row,t.startColumn,t.endColumn),r.classList.add("highlight"),n.appendChild(r)}removeAllDayEvent(e){let t=this.getContainer();if(!t)return;let n=t.querySelector(`swp-allday-event[data-event-id="${e}"]`);n&&n.remove()}clearCache(){this.container=null}renderAllDayEventsForPeriod(e){this.clearAllDayEvents(),e.forEach(t=>{this.renderAllDayEventWithLayout(t.calenderEvent,t)})}clearAllDayEvents(){let e=document.querySelector("swp-allday-container");e&&e.querySelectorAll("swp-allday-event:not(.max-event-indicator)").forEach(t=>t.remove())}handleViewChanged(e){this.clearAllDayEvents()}};var ze=class{constructor(e,t,n){this.cachedGridContainer=null,this.cachedTimeAxis=null,this.dateService=t,this.columnRenderer=e,this.config=n}renderGrid(e,t,n="week"){!e||!t||(this.cachedGridContainer=e,e.children.length===0?this.createCompleteGridStructure(e,t,n):this.updateGridContent(e,t,n))}createCompleteGridStructure(e,t,n){let r=document.createDocumentFragment(),s=document.createElement("swp-header-spacer");r.appendChild(s);let i=this.createOptimizedTimeAxis();this.cachedTimeAxis=i,r.appendChild(i);let a=this.createOptimizedGridContainer(t,n);this.cachedGridContainer=a,r.appendChild(a),e.appendChild(r)}createOptimizedTimeAxis(){let e=document.createElement("swp-time-axis"),t=document.createElement("swp-time-axis-content"),n=this.config.gridSettings,r=n.dayStartHour,s=n.dayEndHour,i=document.createDocumentFragment();for(let a=r;a{let t=e,{weekNumber:n,dateRange:r}=t.detail;this.updateWeekInfoInDOM(n,r)})}updateWeekInfoInDOM(e,t){let n=document.querySelector("swp-week-number"),r=document.querySelector("swp-date-range");n&&(n.textContent=`Week ${e}`),r&&(r.textContent=t)}applyFilterToPreRenderedGrids(e){document.querySelectorAll("swp-grid-container").forEach(n=>{n.querySelectorAll("swp-events-layer").forEach(s=>{e.active?(s.setAttribute("data-filter-active","true"),s.querySelectorAll("swp-event").forEach(a=>{let c=a.getAttribute("data-event-id");c&&e.matchingIds.includes(c)?a.setAttribute("data-matches","true"):a.removeAttribute("data-matches")})):(s.removeAttribute("data-filter-active"),s.querySelectorAll("swp-event").forEach(a=>{a.removeAttribute("data-matches")}))})})}};var Ge=class{constructor(e,t){this.dateService=e,this.config=t}minutesToPixels(e){let n=this.config.gridSettings.hourHeight;return e/60*n}pixelsToMinutes(e){let n=this.config.gridSettings.hourHeight;return e/n*60}timeToPixels(e){let t=this.dateService.timeToMinutes(e),r=this.config.gridSettings.dayStartHour*60,s=t-r;return this.minutesToPixels(s)}dateToPixels(e){let t=this.dateService.getMinutesSinceMidnight(e),r=this.config.gridSettings.dayStartHour*60,s=t-r;return this.minutesToPixels(s)}pixelsToTime(e){let t=this.pixelsToMinutes(e),s=this.config.gridSettings.dayStartHour*60+t;return this.dateService.minutesToTime(s)}calculateEventPosition(e,t){let n,r;typeof e=="string"?n=this.timeToPixels(e):n=this.dateToPixels(e),typeof t=="string"?r=this.timeToPixels(t):r=this.dateToPixels(t);let s=Math.max(r-n,this.getMinimumEventHeight()),i=this.pixelsToMinutes(s);return{top:n,height:s,duration:i}}snapToGrid(e){let n=this.config.gridSettings.snapInterval,r=this.minutesToPixels(n);return Math.round(e/r)*r}snapTimeToInterval(e){let t=this.dateService.timeToMinutes(e),r=this.config.gridSettings.snapInterval,s=Math.round(t/r)*r;return this.dateService.minutesToTime(s)}calculateColumnPosition(e,t,n){let r=n/t,s=e*r,i=2,a=r-i;return{left:s+i/2,width:Math.max(a,50)}}eventsOverlap(e,t,n,r){let s=this.calculateEventPosition(e,t),i=this.calculateEventPosition(n,r),a=s.top+s.height,c=i.top+i.height;return!(a<=i.top||c<=s.top)}getPositionFromCoordinate(e,t){let n=e-t.boundingClientRect.top;return this.snapToGrid(n)}isWithinWorkHours(e){let[t]=e.split(":").map(Number),n=this.config.gridSettings;return t>=n.workStartHour&&t=n.dayStartHour&&t{let r=this.dateService.formatISODate(n),s=this.getWorkHoursForDate(n);t.set(r,s)}),t}calculateNonWorkHoursStyle(e){if(e==="off")return null;let t=this.config.gridSettings,n=t.dayStartHour,r=t.hourHeight,s=(e.start-n)*r,i=(e.end-n)*r;return{beforeWorkHeight:Math.max(0,s),afterWorkTop:Math.max(0,i)}}calculateWorkHoursStyle(e){if(e==="off")return null;let t=`${e.start.toString().padStart(2,"0")}:00`,n=`${e.end.toString().padStart(2,"0")}:00`,r=this.positionUtils.calculateEventPosition(t,n);return{top:r.top,height:r.height}}async loadWorkSchedule(e){this.workSchedule=e}getWorkSchedule(){return this.workSchedule}getDayName(e){return["sunday","monday","tuesday","wednesday","thursday","friday","saturday"][e.getDay()]}};var pe=class o{constructor(e){this.config=e}groupEventsByStartTime(e){if(e.length===0)return[];let n=this.config.gridSettings.gridStartThresholdMinutes,r=[...e].sort((i,a)=>i.start.getTime()-a.start.getTime()),s=[];for(let i of r){let a=s.find(c=>c.events.some(d=>{if(Math.abs(i.start.getTime()-d.start.getTime())/6e4<=n)return!0;let w=(d.end.getTime()-i.start.getTime())/(1e3*60);if(w>0&&w<=n)return!0;let E=(i.end.getTime()-d.start.getTime())/(1e3*60);return E>0&&E<=n}));a?a.events.push(i):s.push({events:[i],containerType:"NONE",startTime:i.start})}return s}decideContainerType(e){return e.events.length===1?"NONE":"GRID"}doEventsOverlap(e,t){return e.startt.start}createOptimizedStackLinks(e){let t=new Map;if(e.length===0)return t;let n=[...e].sort((r,s)=>r.start.getTime()-s.start.getTime());for(let r of n){let s=n.filter(a=>a!==r&&this.doEventsOverlap(r,a)),i=0;for(let a of s){let c=t.get(a.id);c&&(i=Math.max(i,c.stackLevel+1))}t.set(r.id,{stackLevel:i})}for(let r of n){let s=t.get(r.id),i=n.filter(d=>d!==r&&this.doEventsOverlap(r,d)),a=i.filter(d=>{let g=t.get(d.id);return g&&g.stackLevel===s.stackLevel-1});a.length>0&&(s.prev=a[0].id);let c=i.filter(d=>{let g=t.get(d.id);return g&&g.stackLevel===s.stackLevel+1});c.length>0&&(s.next=c[0].id)}return t}calculateMarginLeft(e){return e*o.STACK_OFFSET_PX}calculateZIndex(e){return 100+e}serializeStackLink(e){return JSON.stringify(e)}deserializeStackLink(e){try{return JSON.parse(e)}catch{return null}}applyStackLinkToElement(e,t){e.dataset.stackLink=this.serializeStackLink(t)}getStackLinkFromElement(e){let t=e.dataset.stackLink;return t?this.deserializeStackLink(t):null}applyVisualStyling(e,t){e.style.marginLeft=`${this.calculateMarginLeft(t)}px`,e.style.zIndex=`${this.calculateZIndex(t)}`}clearStackLinkFromElement(e){delete e.dataset.stackLink}clearVisualStyling(e){e.style.marginLeft="",e.style.zIndex=""}};pe.STACK_OFFSET_PX=15;var je=class{constructor(e,t,n){this.stackManager=e,this.config=t,this.positionUtils=n}calculateColumnLayout(e){if(e.length===0)return{gridGroups:[],stackedEvents:[]};let t=[],n=[],r=[],s=[...e].sort((i,a)=>i.start.getTime()-a.start.getTime());for(;s.length>0;){let i=s[0],c=this.config.gridSettings.gridStartThresholdMinutes,d=this.expandGridCandidates(i,s,c),g={events:d,containerType:"NONE",startTime:i.start};if(this.stackManager.decideContainerType(g)==="GRID"&&d.length>1){let E=this.calculateGridGroupStackLevelFromRendered(d,r),m=[...d].sort((C,k)=>C.start.getTime()-k.start.getTime())[0],u=this.positionUtils.calculateEventPosition(m.start,m.end),D=this.allocateColumns(d);t.push({events:d,stackLevel:E,position:{top:u.top+1},columns:D}),d.forEach(C=>r.push({event:C,level:E})),s=s.filter(C=>!d.includes(C))}else{let E=this.calculateStackLevelFromRendered(i,r),m=this.positionUtils.calculateEventPosition(i.start,i.end);n.push({event:i,stackLink:{stackLevel:E},position:{top:m.top+1,height:m.height-3}}),r.push({event:i,level:E}),s=s.slice(1)}}return{gridGroups:t,stackedEvents:n}}calculateGridGroupStackLevelFromRendered(e,t){let n=-1;for(let r of e)for(let s of t)this.stackManager.doEventsOverlap(r,s.event)&&(n=Math.max(n,s.level));return n+1}calculateStackLevelFromRendered(e,t){let n=-1;for(let r of t)this.stackManager.doEventsOverlap(e,r.event)&&(n=Math.max(n,r.level));return n+1}detectConflict(e,t,n){if(Math.abs(e.start.getTime()-t.start.getTime())/6e4<=n&&this.stackManager.doEventsOverlap(e,t))return!0;let s=(t.end.getTime()-e.start.getTime())/(1e3*60);if(s>0&&s<=n)return!0;let i=(e.end.getTime()-t.start.getTime())/(1e3*60);return i>0&&i<=n}expandGridCandidates(e,t,n){let r=[e],s=!0;for(;s;){s=!1;for(let i=1;ithis.stackManager.doEventsOverlap(n,a))){s.push(n),r=!0;break}r||t.push([n])}return t}};async function zt(o,e){try{let t=e.parseEventIdFromURL();t&&(console.log(`Deep linking to event ID: ${t}`),setTimeout(async()=>{await o.navigateToEvent(t)||console.warn(`Deep linking failed: Event with ID ${t} not found`)},500))}catch(t){console.warn("Deep linking failed:",t)}}async function Ot(){try{let o=await fe.load(),t=new ce().builder();b.setDebug(!0),t.registerInstance(b).as("IEventBus"),t.registerInstance(o).as("Configuration"),t.registerType(J).as("IndexedDBService"),t.registerType(Pe).as("OperationQueue").autoWire({mapResolvers:[l=>l.resolveType("IndexedDBService")]}),t.registerType(We).as("ApiEventRepository").autoWire({mapResolvers:[l=>l.resolveType("Configuration")]}),t.registerType(Be).as("IEventRepository").autoWire({mapResolvers:[l=>l.resolveType("IndexedDBService"),l=>l.resolveType("OperationQueue")]}),t.registerType(Ne).as("SyncManager").autoWire({mapResolvers:[l=>l.resolveType("IEventBus"),l=>l.resolveType("OperationQueue"),l=>l.resolveType("IndexedDBService"),l=>l.resolveType("ApiEventRepository")]}),t.registerType(Fe).as("IHeaderRenderer"),t.registerType(_e).as("IColumnRenderer").autoWire({mapResolvers:[l=>l.resolveType("DateService"),l=>l.resolveType("WorkHoursManager")]}),t.registerType(Ye).as("IEventRenderer").autoWire({mapResolvers:[l=>l.resolveType("DateService"),l=>l.resolveType("EventStackManager"),l=>l.resolveType("EventLayoutCoordinator"),l=>l.resolveType("Configuration"),l=>l.resolveType("PositionUtils")]}),t.registerType(U).as("DateService").autoWire({mapResolvers:[l=>l.resolveType("Configuration")]}),t.registerType(pe).as("EventStackManager").autoWire({mapResolvers:[l=>l.resolveType("Configuration")]}),t.registerType(je).as("EventLayoutCoordinator").autoWire({mapResolvers:[l=>l.resolveType("EventStackManager"),l=>l.resolveType("Configuration"),l=>l.resolveType("PositionUtils")]}),t.registerType(Ue).as("WorkHoursManager").autoWire({mapResolvers:[l=>l.resolveType("DateService"),l=>l.resolveType("Configuration"),l=>l.resolveType("PositionUtils")]}),t.registerType(Ee).as("URLManager").autoWire({mapResolvers:[l=>l.resolveType("IEventBus")]}),t.registerType(V).as("TimeFormatter"),t.registerType(Ge).as("PositionUtils").autoWire({mapResolvers:[l=>l.resolveType("DateService"),l=>l.resolveType("Configuration")]}),t.registerType(qe).as("WeekInfoRenderer").autoWire({mapResolvers:[l=>l.resolveType("IEventBus"),l=>l.resolveType("EventRenderingService")]}),t.registerType(Ve).as("AllDayEventRenderer"),t.registerType(Se).as("EventRenderingService").autoWire({mapResolvers:[l=>l.resolveType("IEventBus"),l=>l.resolveType("EventManager"),l=>l.resolveType("IEventRenderer"),l=>l.resolveType("DateService")]}),t.registerType(ze).as("GridRenderer").autoWire({mapResolvers:[l=>l.resolveType("IColumnRenderer"),l=>l.resolveType("DateService"),l=>l.resolveType("Configuration")]}),t.registerType(we).as("GridManager").autoWire({mapResolvers:[l=>l.resolveType("GridRenderer"),l=>l.resolveType("DateService")]}),t.registerType(Ce).as("ScrollManager").autoWire({mapResolvers:[l=>l.resolveType("PositionUtils")]}),t.registerType(Te).as("NavigationManager").autoWire({mapResolvers:[l=>l.resolveType("IEventBus"),l=>l.resolveType("EventRenderingService"),l=>l.resolveType("GridRenderer"),l=>l.resolveType("DateService"),l=>l.resolveType("WeekInfoRenderer")]}),t.registerType(ke).as("NavigationButtons").autoWire({mapResolvers:[l=>l.resolveType("IEventBus")]}),t.registerType(Me).as("ViewSelector").autoWire({mapResolvers:[l=>l.resolveType("IEventBus"),l=>l.resolveType("Configuration")]}),t.registerType(be).as("DragDropManager").autoWire({mapResolvers:[l=>l.resolveType("IEventBus"),l=>l.resolveType("PositionUtils")]}),t.registerType(Ie).as("AllDayManager").autoWire({mapResolvers:[l=>l.resolveType("EventManager"),l=>l.resolveType("AllDayEventRenderer"),l=>l.resolveType("DateService")]}),t.registerType(Oe).as("ResizeHandleManager").autoWire({mapResolvers:[l=>l.resolveType("Configuration"),l=>l.resolveType("PositionUtils")]}),t.registerType(Le).as("EdgeScrollManager").autoWire({mapResolvers:[l=>l.resolveType("IEventBus")]}),t.registerType($e).as("HeaderManager").autoWire({mapResolvers:[l=>l.resolveType("IHeaderRenderer"),l=>l.resolveType("Configuration")]}),t.registerType(Ae).as("CalendarManager").autoWire({mapResolvers:[l=>l.resolveType("IEventBus"),l=>l.resolveType("EventManager"),l=>l.resolveType("GridManager"),l=>l.resolveType("EventRenderingService"),l=>l.resolveType("ScrollManager"),l=>l.resolveType("Configuration")]}),t.registerType(He).as("WorkweekPresets").autoWire({mapResolvers:[l=>l.resolveType("IEventBus"),l=>l.resolveType("Configuration")]}),t.registerType(fe).as("ConfigManager").autoWire({mapResolvers:[l=>l.resolveType("IEventBus"),l=>l.resolveType("Configuration")]}),t.registerType(De).as("EventManager").autoWire({mapResolvers:[l=>l.resolveType("IEventBus"),l=>l.resolveType("DateService"),l=>l.resolveType("Configuration"),l=>l.resolveType("IEventRepository")]});let n=t.build(),r=n.resolveType("IEventBus"),s=n.resolveType("CalendarManager"),i=n.resolveType("EventManager"),a=n.resolveType("ResizeHandleManager"),c=n.resolveType("HeaderManager"),d=n.resolveType("DragDropManager"),g=n.resolveType("ViewSelector"),w=n.resolveType("NavigationManager"),E=n.resolveType("NavigationButtons"),m=n.resolveType("EdgeScrollManager"),u=n.resolveType("AllDayManager"),D=n.resolveType("URLManager"),C=n.resolveType("WorkweekPresets"),k=n.resolveType("ConfigManager");await s.initialize?.(),await a.initialize?.(),await zt(i,D),window.calendarDebug={eventBus:b,app:n,calendarManager:s,eventManager:i,workweekPresetsManager:C}}catch(o){throw o}}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>{Ot().catch(o=>{console.error("Calendar initialization failed:",o)})}):Ot().catch(o=>{console.error("Calendar initialization failed:",o)}); diff --git a/wwwroot/js/calendar-v2.js b/wwwroot/js/calendar-v2.js new file mode 100644 index 0000000..7287e63 --- /dev/null +++ b/wwwroot/js/calendar-v2.js @@ -0,0 +1,1631 @@ +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); +var __commonJS = (cb, mod) => function __require() { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); + +// node_modules/dayjs/dayjs.min.js +var require_dayjs_min = __commonJS({ + "node_modules/dayjs/dayjs.min.js"(exports, module) { + !function(t, e) { + "object" == typeof exports && "undefined" != typeof module ? module.exports = e() : "function" == typeof define && define.amd ? define(e) : (t = "undefined" != typeof globalThis ? globalThis : t || self).dayjs = e(); + }(exports, function() { + "use strict"; + var t = 1e3, e = 6e4, n = 36e5, r = "millisecond", i = "second", s = "minute", u = "hour", a = "day", o = "week", c = "month", f = "quarter", h = "year", d = "date", l = "Invalid Date", $ = /^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/, y = /\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g, M = { name: "en", weekdays: "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"), months: "January_February_March_April_May_June_July_August_September_October_November_December".split("_"), ordinal: function(t2) { + var e2 = ["th", "st", "nd", "rd"], n2 = t2 % 100; + return "[" + t2 + (e2[(n2 - 20) % 10] || e2[n2] || e2[0]) + "]"; + } }, m = /* @__PURE__ */ __name(function(t2, e2, n2) { + var r2 = String(t2); + return !r2 || r2.length >= e2 ? t2 : "" + Array(e2 + 1 - r2.length).join(n2) + t2; + }, "m"), v = { s: m, z: function(t2) { + var e2 = -t2.utcOffset(), n2 = Math.abs(e2), r2 = Math.floor(n2 / 60), i2 = n2 % 60; + return (e2 <= 0 ? "+" : "-") + m(r2, 2, "0") + ":" + m(i2, 2, "0"); + }, m: /* @__PURE__ */ __name(function t2(e2, n2) { + if (e2.date() < n2.date()) + return -t2(n2, e2); + var r2 = 12 * (n2.year() - e2.year()) + (n2.month() - e2.month()), i2 = e2.clone().add(r2, c), s2 = n2 - i2 < 0, u2 = e2.clone().add(r2 + (s2 ? -1 : 1), c); + return +(-(r2 + (n2 - i2) / (s2 ? i2 - u2 : u2 - i2)) || 0); + }, "t"), a: function(t2) { + return t2 < 0 ? Math.ceil(t2) || 0 : Math.floor(t2); + }, p: function(t2) { + return { M: c, y: h, w: o, d: a, D: d, h: u, m: s, s: i, ms: r, Q: f }[t2] || String(t2 || "").toLowerCase().replace(/s$/, ""); + }, u: function(t2) { + return void 0 === t2; + } }, g = "en", D = {}; + D[g] = M; + var p = "$isDayjsObject", S = /* @__PURE__ */ __name(function(t2) { + return t2 instanceof _ || !(!t2 || !t2[p]); + }, "S"), w = /* @__PURE__ */ __name(function t2(e2, n2, r2) { + var i2; + if (!e2) + return g; + if ("string" == typeof e2) { + var s2 = e2.toLowerCase(); + D[s2] && (i2 = s2), n2 && (D[s2] = n2, i2 = s2); + var u2 = e2.split("-"); + if (!i2 && u2.length > 1) + return t2(u2[0]); + } else { + var a2 = e2.name; + D[a2] = e2, i2 = a2; + } + return !r2 && i2 && (g = i2), i2 || !r2 && g; + }, "t"), O = /* @__PURE__ */ __name(function(t2, e2) { + if (S(t2)) + return t2.clone(); + var n2 = "object" == typeof e2 ? e2 : {}; + return n2.date = t2, n2.args = arguments, new _(n2); + }, "O"), b = v; + b.l = w, b.i = S, b.w = function(t2, e2) { + return O(t2, { locale: e2.$L, utc: e2.$u, x: e2.$x, $offset: e2.$offset }); + }; + var _ = function() { + function M2(t2) { + this.$L = w(t2.locale, null, true), this.parse(t2), this.$x = this.$x || t2.x || {}, this[p] = true; + } + __name(M2, "M"); + var m2 = M2.prototype; + return m2.parse = function(t2) { + this.$d = function(t3) { + var e2 = t3.date, n2 = t3.utc; + if (null === e2) + return /* @__PURE__ */ new Date(NaN); + if (b.u(e2)) + return /* @__PURE__ */ new Date(); + if (e2 instanceof Date) + return new Date(e2); + if ("string" == typeof e2 && !/Z$/i.test(e2)) { + var r2 = e2.match($); + if (r2) { + var i2 = r2[2] - 1 || 0, s2 = (r2[7] || "0").substring(0, 3); + return n2 ? new Date(Date.UTC(r2[1], i2, r2[3] || 1, r2[4] || 0, r2[5] || 0, r2[6] || 0, s2)) : new Date(r2[1], i2, r2[3] || 1, r2[4] || 0, r2[5] || 0, r2[6] || 0, s2); + } + } + return new Date(e2); + }(t2), this.init(); + }, m2.init = function() { + var t2 = this.$d; + this.$y = t2.getFullYear(), this.$M = t2.getMonth(), this.$D = t2.getDate(), this.$W = t2.getDay(), this.$H = t2.getHours(), this.$m = t2.getMinutes(), this.$s = t2.getSeconds(), this.$ms = t2.getMilliseconds(); + }, m2.$utils = function() { + return b; + }, m2.isValid = function() { + return !(this.$d.toString() === l); + }, m2.isSame = function(t2, e2) { + var n2 = O(t2); + return this.startOf(e2) <= n2 && n2 <= this.endOf(e2); + }, m2.isAfter = function(t2, e2) { + return O(t2) < this.startOf(e2); + }, m2.isBefore = function(t2, e2) { + return this.endOf(e2) < O(t2); + }, m2.$g = function(t2, e2, n2) { + return b.u(t2) ? this[e2] : this.set(n2, t2); + }, m2.unix = function() { + return Math.floor(this.valueOf() / 1e3); + }, m2.valueOf = function() { + return this.$d.getTime(); + }, m2.startOf = function(t2, e2) { + var n2 = this, r2 = !!b.u(e2) || e2, f2 = b.p(t2), l2 = /* @__PURE__ */ __name(function(t3, e3) { + var i2 = b.w(n2.$u ? Date.UTC(n2.$y, e3, t3) : new Date(n2.$y, e3, t3), n2); + return r2 ? i2 : i2.endOf(a); + }, "l"), $2 = /* @__PURE__ */ __name(function(t3, e3) { + return b.w(n2.toDate()[t3].apply(n2.toDate("s"), (r2 ? [0, 0, 0, 0] : [23, 59, 59, 999]).slice(e3)), n2); + }, "$"), y2 = this.$W, M3 = this.$M, m3 = this.$D, v2 = "set" + (this.$u ? "UTC" : ""); + switch (f2) { + case h: + return r2 ? l2(1, 0) : l2(31, 11); + case c: + return r2 ? l2(1, M3) : l2(0, M3 + 1); + case o: + var g2 = this.$locale().weekStart || 0, D2 = (y2 < g2 ? y2 + 7 : y2) - g2; + return l2(r2 ? m3 - D2 : m3 + (6 - D2), M3); + case a: + case d: + return $2(v2 + "Hours", 0); + case u: + return $2(v2 + "Minutes", 1); + case s: + return $2(v2 + "Seconds", 2); + case i: + return $2(v2 + "Milliseconds", 3); + default: + return this.clone(); + } + }, m2.endOf = function(t2) { + return this.startOf(t2, false); + }, m2.$set = function(t2, e2) { + var n2, o2 = b.p(t2), f2 = "set" + (this.$u ? "UTC" : ""), l2 = (n2 = {}, n2[a] = f2 + "Date", n2[d] = f2 + "Date", n2[c] = f2 + "Month", n2[h] = f2 + "FullYear", n2[u] = f2 + "Hours", n2[s] = f2 + "Minutes", n2[i] = f2 + "Seconds", n2[r] = f2 + "Milliseconds", n2)[o2], $2 = o2 === a ? this.$D + (e2 - this.$W) : e2; + if (o2 === c || o2 === h) { + var y2 = this.clone().set(d, 1); + y2.$d[l2]($2), y2.init(), this.$d = y2.set(d, Math.min(this.$D, y2.daysInMonth())).$d; + } else + l2 && this.$d[l2]($2); + return this.init(), this; + }, m2.set = function(t2, e2) { + return this.clone().$set(t2, e2); + }, m2.get = function(t2) { + return this[b.p(t2)](); + }, m2.add = function(r2, f2) { + var d2, l2 = this; + r2 = Number(r2); + var $2 = b.p(f2), y2 = /* @__PURE__ */ __name(function(t2) { + var e2 = O(l2); + return b.w(e2.date(e2.date() + Math.round(t2 * r2)), l2); + }, "y"); + if ($2 === c) + return this.set(c, this.$M + r2); + if ($2 === h) + return this.set(h, this.$y + r2); + if ($2 === a) + return y2(1); + if ($2 === o) + return y2(7); + var M3 = (d2 = {}, d2[s] = e, d2[u] = n, d2[i] = t, d2)[$2] || 1, m3 = this.$d.getTime() + r2 * M3; + return b.w(m3, this); + }, m2.subtract = function(t2, e2) { + return this.add(-1 * t2, e2); + }, m2.format = function(t2) { + var e2 = this, n2 = this.$locale(); + if (!this.isValid()) + return n2.invalidDate || l; + var r2 = t2 || "YYYY-MM-DDTHH:mm:ssZ", i2 = b.z(this), s2 = this.$H, u2 = this.$m, a2 = this.$M, o2 = n2.weekdays, c2 = n2.months, f2 = n2.meridiem, h2 = /* @__PURE__ */ __name(function(t3, n3, i3, s3) { + return t3 && (t3[n3] || t3(e2, r2)) || i3[n3].slice(0, s3); + }, "h"), d2 = /* @__PURE__ */ __name(function(t3) { + return b.s(s2 % 12 || 12, t3, "0"); + }, "d"), $2 = f2 || function(t3, e3, n3) { + var r3 = t3 < 12 ? "AM" : "PM"; + return n3 ? r3.toLowerCase() : r3; + }; + return r2.replace(y, function(t3, r3) { + return r3 || function(t4) { + switch (t4) { + case "YY": + return String(e2.$y).slice(-2); + case "YYYY": + return b.s(e2.$y, 4, "0"); + case "M": + return a2 + 1; + case "MM": + return b.s(a2 + 1, 2, "0"); + case "MMM": + return h2(n2.monthsShort, a2, c2, 3); + case "MMMM": + return h2(c2, a2); + case "D": + return e2.$D; + case "DD": + return b.s(e2.$D, 2, "0"); + case "d": + return String(e2.$W); + case "dd": + return h2(n2.weekdaysMin, e2.$W, o2, 2); + case "ddd": + return h2(n2.weekdaysShort, e2.$W, o2, 3); + case "dddd": + return o2[e2.$W]; + case "H": + return String(s2); + case "HH": + return b.s(s2, 2, "0"); + case "h": + return d2(1); + case "hh": + return d2(2); + case "a": + return $2(s2, u2, true); + case "A": + return $2(s2, u2, false); + case "m": + return String(u2); + case "mm": + return b.s(u2, 2, "0"); + case "s": + return String(e2.$s); + case "ss": + return b.s(e2.$s, 2, "0"); + case "SSS": + return b.s(e2.$ms, 3, "0"); + case "Z": + return i2; + } + return null; + }(t3) || i2.replace(":", ""); + }); + }, m2.utcOffset = function() { + return 15 * -Math.round(this.$d.getTimezoneOffset() / 15); + }, m2.diff = function(r2, d2, l2) { + var $2, y2 = this, M3 = b.p(d2), m3 = O(r2), v2 = (m3.utcOffset() - this.utcOffset()) * e, g2 = this - m3, D2 = /* @__PURE__ */ __name(function() { + return b.m(y2, m3); + }, "D"); + switch (M3) { + case h: + $2 = D2() / 12; + break; + case c: + $2 = D2(); + break; + case f: + $2 = D2() / 3; + break; + case o: + $2 = (g2 - v2) / 6048e5; + break; + case a: + $2 = (g2 - v2) / 864e5; + break; + case u: + $2 = g2 / n; + break; + case s: + $2 = g2 / e; + break; + case i: + $2 = g2 / t; + break; + default: + $2 = g2; + } + return l2 ? $2 : b.a($2); + }, m2.daysInMonth = function() { + return this.endOf(c).$D; + }, m2.$locale = function() { + return D[this.$L]; + }, m2.locale = function(t2, e2) { + if (!t2) + return this.$L; + var n2 = this.clone(), r2 = w(t2, e2, true); + return r2 && (n2.$L = r2), n2; + }, m2.clone = function() { + return b.w(this.$d, this); + }, m2.toDate = function() { + return new Date(this.valueOf()); + }, m2.toJSON = function() { + return this.isValid() ? this.toISOString() : null; + }, m2.toISOString = function() { + return this.$d.toISOString(); + }, m2.toString = function() { + return this.$d.toUTCString(); + }, M2; + }(), k = _.prototype; + return O.prototype = k, [["$ms", r], ["$s", i], ["$m", s], ["$H", u], ["$W", a], ["$M", c], ["$y", h], ["$D", d]].forEach(function(t2) { + k[t2[1]] = function(e2) { + return this.$g(e2, t2[0], t2[1]); + }; + }), O.extend = function(t2, e2) { + return t2.$i || (t2(e2, _, O), t2.$i = true), O; + }, O.locale = w, O.isDayjs = S, O.unix = function(t2) { + return O(1e3 * t2); + }, O.en = D[g], O.Ls = D, O.p = {}, O; + }); + } +}); + +// node_modules/dayjs/plugin/utc.js +var require_utc = __commonJS({ + "node_modules/dayjs/plugin/utc.js"(exports, module) { + !function(t, i) { + "object" == typeof exports && "undefined" != typeof module ? module.exports = i() : "function" == typeof define && define.amd ? define(i) : (t = "undefined" != typeof globalThis ? globalThis : t || self).dayjs_plugin_utc = i(); + }(exports, function() { + "use strict"; + var t = "minute", i = /[+-]\d\d(?::?\d\d)?/g, e = /([+-]|\d\d)/g; + return function(s, f, n) { + var u = f.prototype; + n.utc = function(t2) { + var i2 = { date: t2, utc: true, args: arguments }; + return new f(i2); + }, u.utc = function(i2) { + var e2 = n(this.toDate(), { locale: this.$L, utc: true }); + return i2 ? e2.add(this.utcOffset(), t) : e2; + }, u.local = function() { + return n(this.toDate(), { locale: this.$L, utc: false }); + }; + var r = u.parse; + u.parse = function(t2) { + t2.utc && (this.$u = true), this.$utils().u(t2.$offset) || (this.$offset = t2.$offset), r.call(this, t2); + }; + var o = u.init; + u.init = function() { + if (this.$u) { + var t2 = this.$d; + this.$y = t2.getUTCFullYear(), this.$M = t2.getUTCMonth(), this.$D = t2.getUTCDate(), this.$W = t2.getUTCDay(), this.$H = t2.getUTCHours(), this.$m = t2.getUTCMinutes(), this.$s = t2.getUTCSeconds(), this.$ms = t2.getUTCMilliseconds(); + } else + o.call(this); + }; + var a = u.utcOffset; + u.utcOffset = function(s2, f2) { + var n2 = this.$utils().u; + if (n2(s2)) + return this.$u ? 0 : n2(this.$offset) ? a.call(this) : this.$offset; + if ("string" == typeof s2 && (s2 = function(t2) { + void 0 === t2 && (t2 = ""); + var s3 = t2.match(i); + if (!s3) + return null; + var f3 = ("" + s3[0]).match(e) || ["-", 0, 0], n3 = f3[0], u3 = 60 * +f3[1] + +f3[2]; + return 0 === u3 ? 0 : "+" === n3 ? u3 : -u3; + }(s2), null === s2)) + return this; + var u2 = Math.abs(s2) <= 16 ? 60 * s2 : s2; + if (0 === u2) + return this.utc(f2); + var r2 = this.clone(); + if (f2) + return r2.$offset = u2, r2.$u = false, r2; + var o2 = this.$u ? this.toDate().getTimezoneOffset() : -1 * this.utcOffset(); + return (r2 = this.local().add(u2 + o2, t)).$offset = u2, r2.$x.$localOffset = o2, r2; + }; + var h = u.format; + u.format = function(t2) { + var i2 = t2 || (this.$u ? "YYYY-MM-DDTHH:mm:ss[Z]" : ""); + return h.call(this, i2); + }, u.valueOf = function() { + var t2 = this.$utils().u(this.$offset) ? 0 : this.$offset + (this.$x.$localOffset || this.$d.getTimezoneOffset()); + return this.$d.valueOf() - 6e4 * t2; + }, u.isUTC = function() { + return !!this.$u; + }, u.toISOString = function() { + return this.toDate().toISOString(); + }, u.toString = function() { + return this.toDate().toUTCString(); + }; + var l = u.toDate; + u.toDate = function(t2) { + return "s" === t2 && this.$offset ? n(this.format("YYYY-MM-DD HH:mm:ss:SSS")).toDate() : l.call(this); + }; + var c = u.diff; + u.diff = function(t2, i2, e2) { + if (t2 && this.$u === t2.$u) + return c.call(this, t2, i2, e2); + var s2 = this.local(), f2 = n(t2).local(); + return c.call(s2, f2, i2, e2); + }; + }; + }); + } +}); + +// node_modules/dayjs/plugin/timezone.js +var require_timezone = __commonJS({ + "node_modules/dayjs/plugin/timezone.js"(exports, module) { + !function(t, e) { + "object" == typeof exports && "undefined" != typeof module ? module.exports = e() : "function" == typeof define && define.amd ? define(e) : (t = "undefined" != typeof globalThis ? globalThis : t || self).dayjs_plugin_timezone = e(); + }(exports, function() { + "use strict"; + var t = { year: 0, month: 1, day: 2, hour: 3, minute: 4, second: 5 }, e = {}; + return function(n, i, o) { + var r, a = /* @__PURE__ */ __name(function(t2, n2, i2) { + void 0 === i2 && (i2 = {}); + var o2 = new Date(t2), r2 = function(t3, n3) { + void 0 === n3 && (n3 = {}); + var i3 = n3.timeZoneName || "short", o3 = t3 + "|" + i3, r3 = e[o3]; + return r3 || (r3 = new Intl.DateTimeFormat("en-US", { hour12: false, timeZone: t3, year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit", timeZoneName: i3 }), e[o3] = r3), r3; + }(n2, i2); + return r2.formatToParts(o2); + }, "a"), u = /* @__PURE__ */ __name(function(e2, n2) { + for (var i2 = a(e2, n2), r2 = [], u2 = 0; u2 < i2.length; u2 += 1) { + var f2 = i2[u2], s2 = f2.type, m = f2.value, c = t[s2]; + c >= 0 && (r2[c] = parseInt(m, 10)); + } + var d = r2[3], l = 24 === d ? 0 : d, h = r2[0] + "-" + r2[1] + "-" + r2[2] + " " + l + ":" + r2[4] + ":" + r2[5] + ":000", v = +e2; + return (o.utc(h).valueOf() - (v -= v % 1e3)) / 6e4; + }, "u"), f = i.prototype; + f.tz = function(t2, e2) { + void 0 === t2 && (t2 = r); + var n2, i2 = this.utcOffset(), a2 = this.toDate(), u2 = a2.toLocaleString("en-US", { timeZone: t2 }), f2 = Math.round((a2 - new Date(u2)) / 1e3 / 60), s2 = 15 * -Math.round(a2.getTimezoneOffset() / 15) - f2; + if (!Number(s2)) + n2 = this.utcOffset(0, e2); + else if (n2 = o(u2, { locale: this.$L }).$set("millisecond", this.$ms).utcOffset(s2, true), e2) { + var m = n2.utcOffset(); + n2 = n2.add(i2 - m, "minute"); + } + return n2.$x.$timezone = t2, n2; + }, f.offsetName = function(t2) { + var e2 = this.$x.$timezone || o.tz.guess(), n2 = a(this.valueOf(), e2, { timeZoneName: t2 }).find(function(t3) { + return "timezonename" === t3.type.toLowerCase(); + }); + return n2 && n2.value; + }; + var s = f.startOf; + f.startOf = function(t2, e2) { + if (!this.$x || !this.$x.$timezone) + return s.call(this, t2, e2); + var n2 = o(this.format("YYYY-MM-DD HH:mm:ss:SSS"), { locale: this.$L }); + return s.call(n2, t2, e2).tz(this.$x.$timezone, true); + }, o.tz = function(t2, e2, n2) { + var i2 = n2 && e2, a2 = n2 || e2 || r, f2 = u(+o(), a2); + if ("string" != typeof t2) + return o(t2).tz(a2); + var s2 = function(t3, e3, n3) { + var i3 = t3 - 60 * e3 * 1e3, o2 = u(i3, n3); + if (e3 === o2) + return [i3, e3]; + var r2 = u(i3 -= 60 * (o2 - e3) * 1e3, n3); + return o2 === r2 ? [i3, o2] : [t3 - 60 * Math.min(o2, r2) * 1e3, Math.max(o2, r2)]; + }(o.utc(t2, i2).valueOf(), f2, a2), m = s2[0], c = s2[1], d = o(m).utcOffset(c); + return d.$x.$timezone = a2, d; + }, o.tz.guess = function() { + return Intl.DateTimeFormat().resolvedOptions().timeZone; + }, o.tz.setDefault = function(t2) { + r = t2; + }; + }; + }); + } +}); + +// node_modules/dayjs/plugin/isoWeek.js +var require_isoWeek = __commonJS({ + "node_modules/dayjs/plugin/isoWeek.js"(exports, module) { + !function(e, t) { + "object" == typeof exports && "undefined" != typeof module ? module.exports = t() : "function" == typeof define && define.amd ? define(t) : (e = "undefined" != typeof globalThis ? globalThis : e || self).dayjs_plugin_isoWeek = t(); + }(exports, function() { + "use strict"; + var e = "day"; + return function(t, i, s) { + var a = /* @__PURE__ */ __name(function(t2) { + return t2.add(4 - t2.isoWeekday(), e); + }, "a"), d = i.prototype; + d.isoWeekYear = function() { + return a(this).year(); + }, d.isoWeek = function(t2) { + if (!this.$utils().u(t2)) + return this.add(7 * (t2 - this.isoWeek()), e); + var i2, d2, n2, o, r = a(this), u = (i2 = this.isoWeekYear(), d2 = this.$u, n2 = (d2 ? s.utc : s)().year(i2).startOf("year"), o = 4 - n2.isoWeekday(), n2.isoWeekday() > 4 && (o += 7), n2.add(o, e)); + return r.diff(u, "week") + 1; + }, d.isoWeekday = function(e2) { + return this.$utils().u(e2) ? this.day() || 7 : this.day(this.day() % 7 ? e2 : e2 - 7); + }; + var n = d.startOf; + d.startOf = function(e2, t2) { + var i2 = this.$utils(), s2 = !!i2.u(t2) || t2; + return "isoweek" === i2.p(e2) ? s2 ? this.date(this.date() - (this.isoWeekday() - 1)).startOf("day") : this.date(this.date() - 1 - (this.isoWeekday() - 1) + 7).endOf("day") : n.bind(this)(e2, t2); + }; + }; + }); + } +}); + +// src/v2/core/RenderBuilder.ts +function buildPipeline(renderers) { + return { + async run(context) { + for (const renderer of renderers) { + await renderer.render(context); + } + } + }; +} +__name(buildPipeline, "buildPipeline"); + +// src/v2/core/FilterTemplate.ts +var _FilterTemplate = class _FilterTemplate { + constructor(dateService, entityResolver) { + this.dateService = dateService; + this.entityResolver = entityResolver; + this.fields = []; + } + /** + * Tilføj felt til template + * @param idProperty - Property-navn (bruges på både event og column.dataset) + * @param derivedFrom - Hvis feltet udledes fra anden property (f.eks. date fra start) + */ + addField(idProperty, derivedFrom) { + this.fields.push({ idProperty, derivedFrom }); + return this; + } + /** + * Parse dot-notation string into components + * @example 'resource.teamId' → { entityType: 'resource', property: 'teamId', foreignKey: 'resourceId' } + */ + parseDotNotation(idProperty) { + if (!idProperty.includes(".")) + return null; + const [entityType, property] = idProperty.split("."); + return { + entityType, + property, + foreignKey: entityType + "Id" + // Convention: resource → resourceId + }; + } + /** + * Get dataset key for column lookup + * For dot-notation 'resource.teamId', we look for 'teamId' in dataset + */ + getDatasetKey(idProperty) { + const dotNotation = this.parseDotNotation(idProperty); + if (dotNotation) { + return dotNotation.property; + } + return idProperty; + } + /** + * Byg nøgle fra kolonne + * Læser værdier fra column.dataset[idProperty] + * For dot-notation, uses the property part (resource.teamId → teamId) + */ + buildKeyFromColumn(column) { + return this.fields.map((f) => { + const key = this.getDatasetKey(f.idProperty); + return column.dataset[key] || ""; + }).join(":"); + } + /** + * Byg nøgle fra event + * Læser værdier fra event[idProperty] eller udleder fra derivedFrom + * For dot-notation, resolves via EntityResolver + */ + buildKeyFromEvent(event) { + const eventRecord = event; + return this.fields.map((f) => { + const dotNotation = this.parseDotNotation(f.idProperty); + if (dotNotation) { + return this.resolveDotNotation(eventRecord, dotNotation); + } + if (f.derivedFrom) { + const sourceValue = eventRecord[f.derivedFrom]; + if (sourceValue instanceof Date) { + return this.dateService.getDateKey(sourceValue); + } + return String(sourceValue || ""); + } + return String(eventRecord[f.idProperty] || ""); + }).join(":"); + } + /** + * Resolve dot-notation reference via EntityResolver + */ + resolveDotNotation(eventRecord, dotNotation) { + if (!this.entityResolver) { + console.warn(`FilterTemplate: EntityResolver required for dot-notation '${dotNotation.entityType}.${dotNotation.property}'`); + return ""; + } + const foreignId = eventRecord[dotNotation.foreignKey]; + if (!foreignId) + return ""; + const entity = this.entityResolver.resolve(dotNotation.entityType, String(foreignId)); + if (!entity) + return ""; + return String(entity[dotNotation.property] || ""); + } + /** + * Match event mod kolonne + */ + matches(event, column) { + return this.buildKeyFromEvent(event) === this.buildKeyFromColumn(column); + } +}; +__name(_FilterTemplate, "FilterTemplate"); +var FilterTemplate = _FilterTemplate; + +// src/v2/core/CalendarOrchestrator.ts +var _CalendarOrchestrator = class _CalendarOrchestrator { + constructor(allRenderers, eventRenderer, scheduleRenderer, headerDrawerRenderer, dateService, entityServices) { + this.allRenderers = allRenderers; + this.eventRenderer = eventRenderer; + this.scheduleRenderer = scheduleRenderer; + this.headerDrawerRenderer = headerDrawerRenderer; + this.dateService = dateService; + this.entityServices = entityServices; + } + async render(viewConfig, container) { + const headerContainer = container.querySelector("swp-calendar-header"); + const columnContainer = container.querySelector("swp-day-columns"); + if (!headerContainer || !columnContainer) { + throw new Error("Missing swp-calendar-header or swp-day-columns"); + } + const filter = {}; + for (const grouping of viewConfig.groupings) { + filter[grouping.type] = grouping.values; + } + const filterTemplate = new FilterTemplate(this.dateService); + for (const grouping of viewConfig.groupings) { + if (grouping.idProperty) { + filterTemplate.addField(grouping.idProperty, grouping.derivedFrom); + } + } + const { parentChildMap, childType } = await this.resolveBelongsTo(viewConfig.groupings, filter); + const context = { headerContainer, columnContainer, filter, groupings: viewConfig.groupings, parentChildMap, childType }; + headerContainer.innerHTML = ""; + columnContainer.innerHTML = ""; + const levels = viewConfig.groupings.map((g) => g.type).join(" "); + headerContainer.dataset.levels = levels; + const activeRenderers = this.selectRenderers(viewConfig); + const pipeline = buildPipeline(activeRenderers); + await pipeline.run(context); + await this.scheduleRenderer.render(container, filter); + await this.eventRenderer.render(container, filter, filterTemplate); + await this.headerDrawerRenderer.render(container, filter, filterTemplate); + } + selectRenderers(viewConfig) { + const types = viewConfig.groupings.map((g) => g.type); + return types.map((type) => this.allRenderers.find((r) => r.type === type)).filter((r) => r !== void 0); + } + /** + * Resolve belongsTo relations to build parent-child map + * e.g., belongsTo: 'team.resourceIds' → { team1: ['EMP001', 'EMP002'], team2: [...] } + * Also returns the childType (the grouping type that has belongsTo) + */ + async resolveBelongsTo(groupings, filter) { + const childGrouping = groupings.find((g) => g.belongsTo); + if (!childGrouping?.belongsTo) + return {}; + const [entityType, property] = childGrouping.belongsTo.split("."); + if (!entityType || !property) + return {}; + const parentIds = filter[entityType] || []; + if (parentIds.length === 0) + return {}; + const service = this.entityServices.find( + (s) => s.entityType.toLowerCase() === entityType + ); + if (!service) + return {}; + const allEntities = await service.getAll(); + const entities = allEntities.filter( + (e) => parentIds.includes(e.id) + ); + const map = {}; + for (const entity of entities) { + const entityRecord = entity; + const children = entityRecord[property] || []; + map[entityRecord.id] = children; + } + return { parentChildMap: map, childType: childGrouping.type }; + } +}; +__name(_CalendarOrchestrator, "CalendarOrchestrator"); +var CalendarOrchestrator = _CalendarOrchestrator; + +// src/v2/core/NavigationAnimator.ts +var _NavigationAnimator = class _NavigationAnimator { + constructor(headerTrack, contentTrack) { + this.headerTrack = headerTrack; + this.contentTrack = contentTrack; + } + async slide(direction, renderFn) { + const out = direction === "left" ? "-100%" : "100%"; + const into = direction === "left" ? "100%" : "-100%"; + await this.animateOut(out); + await renderFn(); + await this.animateIn(into); + } + async animateOut(translate) { + await Promise.all([ + this.headerTrack.animate( + [{ transform: "translateX(0)" }, { transform: `translateX(${translate})` }], + { duration: 200, easing: "ease-in" } + ).finished, + this.contentTrack.animate( + [{ transform: "translateX(0)" }, { transform: `translateX(${translate})` }], + { duration: 200, easing: "ease-in" } + ).finished + ]); + } + async animateIn(translate) { + await Promise.all([ + this.headerTrack.animate( + [{ transform: `translateX(${translate})` }, { transform: "translateX(0)" }], + { duration: 200, easing: "ease-out" } + ).finished, + this.contentTrack.animate( + [{ transform: `translateX(${translate})` }, { transform: "translateX(0)" }], + { duration: 200, easing: "ease-out" } + ).finished + ]); + } +}; +__name(_NavigationAnimator, "NavigationAnimator"); +var NavigationAnimator = _NavigationAnimator; + +// src/v2/features/date/DateRenderer.ts +var _DateRenderer = class _DateRenderer { + constructor(dateService) { + this.dateService = dateService; + this.type = "date"; + } + render(context) { + const dates = context.filter["date"] || []; + const resourceIds = context.filter["resource"] || []; + const dateGrouping = context.groupings?.find((g) => g.type === "date"); + const hideHeader = dateGrouping?.hideHeader === true; + const iterations = resourceIds.length || 1; + let columnCount = 0; + for (let r = 0; r < iterations; r++) { + const resourceId = resourceIds[r]; + for (const dateStr of dates) { + const date = this.dateService.parseISO(dateStr); + const segments = { date: dateStr }; + if (resourceId) + segments.resource = resourceId; + const columnKey = this.dateService.buildColumnKey(segments); + const header = document.createElement("swp-day-header"); + header.dataset.date = dateStr; + header.dataset.columnKey = columnKey; + if (resourceId) { + header.dataset.resourceId = resourceId; + } + if (hideHeader) { + header.dataset.hidden = "true"; + } + header.innerHTML = ` + ${this.dateService.getDayName(date, "short")} + ${date.getDate()} + `; + context.headerContainer.appendChild(header); + const column = document.createElement("swp-day-column"); + column.dataset.date = dateStr; + column.dataset.columnKey = columnKey; + if (resourceId) { + column.dataset.resourceId = resourceId; + } + column.innerHTML = ""; + context.columnContainer.appendChild(column); + columnCount++; + } + } + const container = context.columnContainer.closest("swp-calendar-container"); + if (container) { + container.style.setProperty("--grid-columns", String(columnCount)); + } + } +}; +__name(_DateRenderer, "DateRenderer"); +var DateRenderer = _DateRenderer; + +// src/v2/core/DateService.ts +var import_dayjs = __toESM(require_dayjs_min(), 1); +var import_utc = __toESM(require_utc(), 1); +var import_timezone = __toESM(require_timezone(), 1); +var import_isoWeek = __toESM(require_isoWeek(), 1); +import_dayjs.default.extend(import_utc.default); +import_dayjs.default.extend(import_timezone.default); +import_dayjs.default.extend(import_isoWeek.default); +var _DateService = class _DateService { + constructor(config, baseDate) { + this.config = config; + this.timezone = config.timezone; + this.baseDate = baseDate ? (0, import_dayjs.default)(baseDate) : (0, import_dayjs.default)(); + } + /** + * Set a fixed base date (useful for demos with static mock data) + */ + setBaseDate(date) { + this.baseDate = (0, import_dayjs.default)(date); + } + /** + * Get the current base date (either fixed or today) + */ + getBaseDate() { + return this.baseDate.toDate(); + } + parseISO(isoString) { + return (0, import_dayjs.default)(isoString).toDate(); + } + getDayName(date, format = "short") { + return new Intl.DateTimeFormat(this.config.locale, { weekday: format }).format(date); + } + getWeekDates(offset = 0, days = 7) { + const monday = this.baseDate.startOf("week").add(1, "day").add(offset, "week"); + return Array.from( + { length: days }, + (_, i) => monday.add(i, "day").format("YYYY-MM-DD") + ); + } + /** + * Get dates for specific weekdays within a week + * @param offset - Week offset from base date (0 = current week) + * @param workDays - Array of ISO weekday numbers (1=Monday, 7=Sunday) + * @returns Array of date strings in YYYY-MM-DD format + */ + getWorkWeekDates(offset, workDays) { + const monday = this.baseDate.startOf("week").add(1, "day").add(offset, "week"); + return workDays.map((isoDay) => { + const daysFromMonday = isoDay === 7 ? 6 : isoDay - 1; + return monday.add(daysFromMonday, "day").format("YYYY-MM-DD"); + }); + } + // ============================================ + // FORMATTING + // ============================================ + formatTime(date, showSeconds = false) { + const pattern = showSeconds ? "HH:mm:ss" : "HH:mm"; + return (0, import_dayjs.default)(date).format(pattern); + } + formatTimeRange(start, end) { + return `${this.formatTime(start)} - ${this.formatTime(end)}`; + } + formatDate(date) { + return (0, import_dayjs.default)(date).format("YYYY-MM-DD"); + } + getDateKey(date) { + return this.formatDate(date); + } + // ============================================ + // COLUMN KEY + // ============================================ + /** + * Build a uniform columnKey from grouping segments + * Handles any combination of date, resource, team, etc. + * + * @example + * buildColumnKey({ date: '2025-12-09' }) → "2025-12-09" + * buildColumnKey({ date: '2025-12-09', resource: 'EMP001' }) → "2025-12-09:EMP001" + */ + buildColumnKey(segments) { + const date = segments.date; + const others = Object.entries(segments).filter(([k]) => k !== "date").sort(([a], [b]) => a.localeCompare(b)).map(([, v]) => v); + return date ? [date, ...others].join(":") : others.join(":"); + } + /** + * Parse a columnKey back into segments + * Assumes format: "date:resource:..." or just "date" + */ + parseColumnKey(columnKey) { + const parts = columnKey.split(":"); + return { + date: parts[0], + resource: parts[1] + }; + } + /** + * Extract dateKey from columnKey (first segment) + */ + getDateFromColumnKey(columnKey) { + return columnKey.split(":")[0]; + } + // ============================================ + // TIME CALCULATIONS + // ============================================ + timeToMinutes(timeString) { + const parts = timeString.split(":").map(Number); + const hours = parts[0] || 0; + const minutes = parts[1] || 0; + return hours * 60 + minutes; + } + minutesToTime(totalMinutes) { + const hours = Math.floor(totalMinutes / 60); + const minutes = totalMinutes % 60; + return (0, import_dayjs.default)().hour(hours).minute(minutes).format("HH:mm"); + } + getMinutesSinceMidnight(date) { + const d = (0, import_dayjs.default)(date); + return d.hour() * 60 + d.minute(); + } + // ============================================ + // UTC CONVERSIONS + // ============================================ + toUTC(localDate) { + return import_dayjs.default.tz(localDate, this.timezone).utc().toISOString(); + } + fromUTC(utcString) { + return import_dayjs.default.utc(utcString).tz(this.timezone).toDate(); + } + // ============================================ + // DATE CREATION + // ============================================ + createDateAtTime(baseDate, timeString) { + const totalMinutes = this.timeToMinutes(timeString); + const hours = Math.floor(totalMinutes / 60); + const minutes = totalMinutes % 60; + return (0, import_dayjs.default)(baseDate).startOf("day").hour(hours).minute(minutes).toDate(); + } + getISOWeekDay(date) { + return (0, import_dayjs.default)(date).isoWeekday(); + } +}; +__name(_DateService, "DateService"); +var DateService = _DateService; + +// src/v2/utils/PositionUtils.ts +function calculateEventPosition(start, end, config) { + const startMinutes = start.getHours() * 60 + start.getMinutes(); + const endMinutes = end.getHours() * 60 + end.getMinutes(); + const dayStartMinutes = config.dayStartHour * 60; + const minuteHeight = config.hourHeight / 60; + const top = (startMinutes - dayStartMinutes) * minuteHeight; + const height = (endMinutes - startMinutes) * minuteHeight; + return { top, height }; +} +__name(calculateEventPosition, "calculateEventPosition"); +function minutesToPixels(minutes, config) { + return minutes / 60 * config.hourHeight; +} +__name(minutesToPixels, "minutesToPixels"); +function pixelsToMinutes(pixels, config) { + return pixels / config.hourHeight * 60; +} +__name(pixelsToMinutes, "pixelsToMinutes"); +function snapToGrid(pixels, config) { + const snapPixels = minutesToPixels(config.snapInterval, config); + return Math.round(pixels / snapPixels) * snapPixels; +} +__name(snapToGrid, "snapToGrid"); + +// src/v2/constants/CoreEvents.ts +var CoreEvents = { + // Lifecycle events + INITIALIZED: "core:initialized", + READY: "core:ready", + DESTROYED: "core:destroyed", + // View events + VIEW_CHANGED: "view:changed", + VIEW_RENDERED: "view:rendered", + // Navigation events + DATE_CHANGED: "nav:date-changed", + NAVIGATION_COMPLETED: "nav:navigation-completed", + // Data events + DATA_LOADING: "data:loading", + DATA_LOADED: "data:loaded", + DATA_ERROR: "data:error", + // Grid events + GRID_RENDERED: "grid:rendered", + GRID_CLICKED: "grid:clicked", + // Event management + EVENT_CREATED: "event:created", + EVENT_UPDATED: "event:updated", + EVENT_DELETED: "event:deleted", + EVENT_SELECTED: "event:selected", + // Event drag-drop + EVENT_DRAG_START: "event:drag-start", + EVENT_DRAG_MOVE: "event:drag-move", + EVENT_DRAG_END: "event:drag-end", + EVENT_DRAG_CANCEL: "event:drag-cancel", + EVENT_DRAG_COLUMN_CHANGE: "event:drag-column-change", + // Header drag (timed → header conversion) + EVENT_DRAG_ENTER_HEADER: "event:drag-enter-header", + EVENT_DRAG_MOVE_HEADER: "event:drag-move-header", + EVENT_DRAG_LEAVE_HEADER: "event:drag-leave-header", + // Event resize + EVENT_RESIZE_START: "event:resize-start", + EVENT_RESIZE_END: "event:resize-end", + // Edge scroll + EDGE_SCROLL_TICK: "edge-scroll:tick", + EDGE_SCROLL_STARTED: "edge-scroll:started", + EDGE_SCROLL_STOPPED: "edge-scroll:stopped", + // System events + ERROR: "system:error", + // Sync events + SYNC_STARTED: "sync:started", + SYNC_COMPLETED: "sync:completed", + SYNC_FAILED: "sync:failed", + // Entity events - for audit and sync + ENTITY_SAVED: "entity:saved", + ENTITY_DELETED: "entity:deleted", + // Audit events + AUDIT_LOGGED: "audit:logged", + // Rendering events + EVENTS_RENDERED: "events:rendered" +}; + +// src/v2/features/event/EventLayoutEngine.ts +function eventsOverlap(a, b) { + return a.start < b.end && a.end > b.start; +} +__name(eventsOverlap, "eventsOverlap"); +function eventsWithinThreshold(a, b, thresholdMinutes) { + const thresholdMs = thresholdMinutes * 60 * 1e3; + const startToStartDiff = Math.abs(a.start.getTime() - b.start.getTime()); + if (startToStartDiff <= thresholdMs) + return true; + const bStartsBeforeAEnds = a.end.getTime() - b.start.getTime(); + if (bStartsBeforeAEnds > 0 && bStartsBeforeAEnds <= thresholdMs) + return true; + const aStartsBeforeBEnds = b.end.getTime() - a.start.getTime(); + if (aStartsBeforeBEnds > 0 && aStartsBeforeBEnds <= thresholdMs) + return true; + return false; +} +__name(eventsWithinThreshold, "eventsWithinThreshold"); +function findOverlapGroups(events) { + if (events.length === 0) + return []; + const sorted = [...events].sort((a, b) => a.start.getTime() - b.start.getTime()); + const used = /* @__PURE__ */ new Set(); + const groups = []; + for (const event of sorted) { + if (used.has(event.id)) + continue; + const group = [event]; + used.add(event.id); + let expanded = true; + while (expanded) { + expanded = false; + for (const candidate of sorted) { + if (used.has(candidate.id)) + continue; + const connects = group.some((member) => eventsOverlap(member, candidate)); + if (connects) { + group.push(candidate); + used.add(candidate.id); + expanded = true; + } + } + } + groups.push(group); + } + return groups; +} +__name(findOverlapGroups, "findOverlapGroups"); +function findGridCandidates(events, thresholdMinutes) { + if (events.length === 0) + return []; + const sorted = [...events].sort((a, b) => a.start.getTime() - b.start.getTime()); + const used = /* @__PURE__ */ new Set(); + const groups = []; + for (const event of sorted) { + if (used.has(event.id)) + continue; + const group = [event]; + used.add(event.id); + let expanded = true; + while (expanded) { + expanded = false; + for (const candidate of sorted) { + if (used.has(candidate.id)) + continue; + const connects = group.some( + (member) => eventsWithinThreshold(member, candidate, thresholdMinutes) + ); + if (connects) { + group.push(candidate); + used.add(candidate.id); + expanded = true; + } + } + } + groups.push(group); + } + return groups; +} +__name(findGridCandidates, "findGridCandidates"); +function calculateStackLevels(events) { + const levels = /* @__PURE__ */ new Map(); + const sorted = [...events].sort((a, b) => a.start.getTime() - b.start.getTime()); + for (const event of sorted) { + let maxOverlappingLevel = -1; + for (const [id, level] of levels) { + const other = events.find((e) => e.id === id); + if (other && eventsOverlap(event, other)) { + maxOverlappingLevel = Math.max(maxOverlappingLevel, level); + } + } + levels.set(event.id, maxOverlappingLevel + 1); + } + return levels; +} +__name(calculateStackLevels, "calculateStackLevels"); +function allocateColumns(events) { + const sorted = [...events].sort((a, b) => a.start.getTime() - b.start.getTime()); + const columns = []; + for (const event of sorted) { + let placed = false; + for (const column of columns) { + const canFit = !column.some((e) => eventsOverlap(event, e)); + if (canFit) { + column.push(event); + placed = true; + break; + } + } + if (!placed) { + columns.push([event]); + } + } + return columns; +} +__name(allocateColumns, "allocateColumns"); +function calculateColumnLayout(events, config) { + const thresholdMinutes = config.gridStartThresholdMinutes ?? 10; + const result = { + grids: [], + stacked: [] + }; + if (events.length === 0) + return result; + const overlapGroups = findOverlapGroups(events); + for (const overlapGroup of overlapGroups) { + if (overlapGroup.length === 1) { + result.stacked.push({ + event: overlapGroup[0], + stackLevel: 0 + }); + continue; + } + const gridSubgroups = findGridCandidates(overlapGroup, thresholdMinutes); + const largestGridCandidate = gridSubgroups.reduce((max, g) => g.length > max.length ? g : max, gridSubgroups[0]); + if (largestGridCandidate.length === overlapGroup.length) { + const columns = allocateColumns(overlapGroup); + const earliest = overlapGroup.reduce((min, e) => e.start < min.start ? e : min, overlapGroup[0]); + const position = calculateEventPosition(earliest.start, earliest.end, config); + result.grids.push({ + events: overlapGroup, + columns, + stackLevel: 0, + position: { top: position.top } + }); + } else { + const levels = calculateStackLevels(overlapGroup); + for (const event of overlapGroup) { + result.stacked.push({ + event, + stackLevel: levels.get(event.id) ?? 0 + }); + } + } + } + return result; +} +__name(calculateColumnLayout, "calculateColumnLayout"); + +// src/v2/features/event/EventRenderer.ts +var _EventRenderer = class _EventRenderer { + constructor(eventService, dateService, gridConfig, eventBus) { + this.eventService = eventService; + this.dateService = dateService; + this.gridConfig = gridConfig; + this.eventBus = eventBus; + this.container = null; + this.setupListeners(); + } + /** + * Setup listeners for drag-drop and update events + */ + setupListeners() { + this.eventBus.on(CoreEvents.EVENT_DRAG_COLUMN_CHANGE, (e) => { + const payload = e.detail; + this.handleColumnChange(payload); + }); + this.eventBus.on(CoreEvents.EVENT_DRAG_MOVE, (e) => { + const payload = e.detail; + this.updateDragTimestamp(payload); + }); + this.eventBus.on(CoreEvents.EVENT_UPDATED, (e) => { + const payload = e.detail; + this.handleEventUpdated(payload); + }); + this.eventBus.on(CoreEvents.EVENT_DRAG_END, (e) => { + const payload = e.detail; + this.handleDragEnd(payload); + }); + this.eventBus.on(CoreEvents.EVENT_DRAG_LEAVE_HEADER, (e) => { + const payload = e.detail; + this.handleDragLeaveHeader(payload); + }); + } + /** + * Handle EVENT_DRAG_END - remove element if dropped in header + */ + handleDragEnd(payload) { + if (payload.target === "header") { + const element = this.container?.querySelector(`swp-content-viewport swp-event[data-event-id="${payload.swpEvent.eventId}"]`); + element?.remove(); + } + } + /** + * Handle header item leaving header - create swp-event in grid + */ + handleDragLeaveHeader(payload) { + if (payload.source !== "header") + return; + if (!payload.targetColumn || !payload.start || !payload.end) + return; + if (payload.element) { + payload.element.classList.add("drag-ghost"); + payload.element.style.opacity = "0.3"; + payload.element.style.pointerEvents = "none"; + } + const event = { + id: payload.eventId, + title: payload.title || "", + description: "", + start: payload.start, + end: payload.end, + type: "customer", + allDay: false, + syncStatus: "pending" + }; + const element = this.createEventElement(event); + let eventsLayer = payload.targetColumn.querySelector("swp-events-layer"); + if (!eventsLayer) { + eventsLayer = document.createElement("swp-events-layer"); + payload.targetColumn.appendChild(eventsLayer); + } + eventsLayer.appendChild(element); + element.classList.add("dragging"); + } + /** + * Handle EVENT_UPDATED - re-render affected columns + */ + async handleEventUpdated(payload) { + if (payload.sourceColumnKey !== payload.targetColumnKey) { + await this.rerenderColumn(payload.sourceColumnKey); + } + await this.rerenderColumn(payload.targetColumnKey); + } + /** + * Re-render a single column with fresh data from IndexedDB + */ + async rerenderColumn(columnKey) { + const column = this.findColumn(columnKey); + if (!column) + return; + const date = column.dataset.date; + const resourceId = column.dataset.resourceId; + if (!date) + return; + const startDate = new Date(date); + const endDate = new Date(date); + endDate.setHours(23, 59, 59, 999); + const events = resourceId ? await this.eventService.getByResourceAndDateRange(resourceId, startDate, endDate) : await this.eventService.getByDateRange(startDate, endDate); + const timedEvents = events.filter( + (event) => !event.allDay && this.dateService.getDateKey(event.start) === date + ); + let eventsLayer = column.querySelector("swp-events-layer"); + if (!eventsLayer) { + eventsLayer = document.createElement("swp-events-layer"); + column.appendChild(eventsLayer); + } + eventsLayer.innerHTML = ""; + const layout = calculateColumnLayout(timedEvents, this.gridConfig); + layout.grids.forEach((grid) => { + const groupEl = this.renderGridGroup(grid); + eventsLayer.appendChild(groupEl); + }); + layout.stacked.forEach((item) => { + const eventEl = this.renderStackedEvent(item.event, item.stackLevel); + eventsLayer.appendChild(eventEl); + }); + } + /** + * Find a column element by columnKey + */ + findColumn(columnKey) { + if (!this.container) + return null; + return this.container.querySelector(`swp-day-column[data-column-key="${columnKey}"]`); + } + /** + * Handle event moving to a new column during drag + */ + handleColumnChange(payload) { + const eventsLayer = payload.newColumn.querySelector("swp-events-layer"); + if (!eventsLayer) + return; + eventsLayer.appendChild(payload.element); + payload.element.style.top = `${payload.currentY}px`; + } + /** + * Update timestamp display during drag (snapped to grid) + */ + updateDragTimestamp(payload) { + const timeEl = payload.element.querySelector("swp-event-time"); + if (!timeEl) + return; + const snappedY = snapToGrid(payload.currentY, this.gridConfig); + const minutesFromGridStart = pixelsToMinutes(snappedY, this.gridConfig); + const startMinutes = this.gridConfig.dayStartHour * 60 + minutesFromGridStart; + const height = parseFloat(payload.element.style.height) || this.gridConfig.hourHeight; + const durationMinutes = pixelsToMinutes(height, this.gridConfig); + const start = this.minutesToDate(startMinutes); + const end = this.minutesToDate(startMinutes + durationMinutes); + timeEl.textContent = this.dateService.formatTimeRange(start, end); + } + /** + * Convert minutes since midnight to a Date object (today) + */ + minutesToDate(minutes) { + const date = /* @__PURE__ */ new Date(); + date.setHours(Math.floor(minutes / 60) % 24, minutes % 60, 0, 0); + return date; + } + /** + * Render events for visible dates into day columns + * @param container - Calendar container element + * @param filter - Filter with 'date' and optionally 'resource' arrays + * @param filterTemplate - Template for matching events to columns + */ + async render(container, filter, filterTemplate) { + this.container = container; + const visibleDates = filter["date"] || []; + if (visibleDates.length === 0) + return; + const startDate = new Date(visibleDates[0]); + const endDate = new Date(visibleDates[visibleDates.length - 1]); + endDate.setHours(23, 59, 59, 999); + const events = await this.eventService.getByDateRange(startDate, endDate); + const dayColumns = container.querySelector("swp-day-columns"); + if (!dayColumns) + return; + const columns = dayColumns.querySelectorAll("swp-day-column"); + columns.forEach((column) => { + const columnEl = column; + const columnEvents = events.filter((event) => filterTemplate.matches(event, columnEl)); + let eventsLayer = column.querySelector("swp-events-layer"); + if (!eventsLayer) { + eventsLayer = document.createElement("swp-events-layer"); + column.appendChild(eventsLayer); + } + eventsLayer.innerHTML = ""; + const timedEvents = columnEvents.filter((event) => !event.allDay); + const layout = calculateColumnLayout(timedEvents, this.gridConfig); + layout.grids.forEach((grid) => { + const groupEl = this.renderGridGroup(grid); + eventsLayer.appendChild(groupEl); + }); + layout.stacked.forEach((item) => { + const eventEl = this.renderStackedEvent(item.event, item.stackLevel); + eventsLayer.appendChild(eventEl); + }); + }); + } + /** + * Create a single event element + * + * CLEAN approach: + * - Only data-id for lookup + * - Visible content in innerHTML only + */ + createEventElement(event) { + const element = document.createElement("swp-event"); + element.dataset.eventId = event.id; + if (event.resourceId) { + element.dataset.resourceId = event.resourceId; + } + const position = calculateEventPosition(event.start, event.end, this.gridConfig); + element.style.top = `${position.top}px`; + element.style.height = `${position.height}px`; + const colorClass = this.getColorClass(event); + if (colorClass) { + element.classList.add(colorClass); + } + element.innerHTML = ` + ${this.dateService.formatTimeRange(event.start, event.end)} + ${this.escapeHtml(event.title)} + ${event.description ? `${this.escapeHtml(event.description)}` : ""} + `; + return element; + } + /** + * Get color class based on metadata.color or event type + */ + getColorClass(event) { + if (event.metadata?.color) { + return `is-${event.metadata.color}`; + } + const typeColors = { + "customer": "is-blue", + "vacation": "is-green", + "break": "is-amber", + "meeting": "is-purple", + "blocked": "is-red" + }; + return typeColors[event.type] || "is-blue"; + } + /** + * Escape HTML to prevent XSS + */ + escapeHtml(text) { + const div = document.createElement("div"); + div.textContent = text; + return div.innerHTML; + } + /** + * Render a GRID group with side-by-side columns + * Used when multiple events start at the same time + */ + renderGridGroup(layout) { + const group = document.createElement("swp-event-group"); + group.classList.add(`cols-${layout.columns.length}`); + group.style.top = `${layout.position.top}px`; + if (layout.stackLevel > 0) { + group.style.marginLeft = `${layout.stackLevel * 15}px`; + group.style.zIndex = `${100 + layout.stackLevel}`; + } + let maxBottom = 0; + for (const event of layout.events) { + const pos = calculateEventPosition(event.start, event.end, this.gridConfig); + const eventBottom = pos.top + pos.height; + if (eventBottom > maxBottom) + maxBottom = eventBottom; + } + const groupHeight = maxBottom - layout.position.top; + group.style.height = `${groupHeight}px`; + layout.columns.forEach((columnEvents) => { + const wrapper = document.createElement("div"); + wrapper.style.position = "relative"; + columnEvents.forEach((event) => { + const eventEl = this.createEventElement(event); + const pos = calculateEventPosition(event.start, event.end, this.gridConfig); + eventEl.style.top = `${pos.top - layout.position.top}px`; + eventEl.style.position = "absolute"; + eventEl.style.left = "0"; + eventEl.style.right = "0"; + wrapper.appendChild(eventEl); + }); + group.appendChild(wrapper); + }); + return group; + } + /** + * Render a STACKED event with margin-left offset + * Used for overlapping events that don't start at the same time + */ + renderStackedEvent(event, stackLevel) { + const element = this.createEventElement(event); + element.dataset.stackLink = JSON.stringify({ stackLevel }); + if (stackLevel > 0) { + element.style.marginLeft = `${stackLevel * 15}px`; + element.style.zIndex = `${100 + stackLevel}`; + } + return element; + } +}; +__name(_EventRenderer, "EventRenderer"); +var EventRenderer = _EventRenderer; + +// src/v2/core/BaseGroupingRenderer.ts +var _BaseGroupingRenderer = class _BaseGroupingRenderer { + /** + * Main render method - handles common logic + */ + async render(context) { + const allowedIds = context.filter[this.type] || []; + if (allowedIds.length === 0) + return; + const entities = await this.getEntities(allowedIds); + const dateCount = context.filter["date"]?.length || 1; + const childIds = context.childType ? context.filter[context.childType] || [] : []; + for (const entity of entities) { + const entityChildIds = context.parentChildMap?.[entity.id] || []; + const childCount = entityChildIds.filter((id) => childIds.includes(id)).length; + const colspan = childCount * dateCount; + const header = document.createElement(this.config.elementTag); + header.dataset[this.config.idAttribute] = entity.id; + header.style.setProperty(this.config.colspanVar, String(colspan)); + this.renderHeader(entity, header, context); + context.headerContainer.appendChild(header); + } + } + /** + * Override this method for custom header rendering + * Default: just sets textContent to display name + */ + renderHeader(entity, header, _context) { + header.textContent = this.getDisplayName(entity); + } + /** + * Helper to render a single entity header. + * Can be used by subclasses that override render() but want consistent header creation. + */ + createHeader(entity, context) { + const header = document.createElement(this.config.elementTag); + header.dataset[this.config.idAttribute] = entity.id; + this.renderHeader(entity, header, context); + return header; + } +}; +__name(_BaseGroupingRenderer, "BaseGroupingRenderer"); +var BaseGroupingRenderer = _BaseGroupingRenderer; + +// src/v2/features/resource/ResourceRenderer.ts +var _ResourceRenderer = class _ResourceRenderer extends BaseGroupingRenderer { + constructor(resourceService) { + super(); + this.resourceService = resourceService; + this.type = "resource"; + this.config = { + elementTag: "swp-resource-header", + idAttribute: "resourceId", + colspanVar: "--resource-cols" + }; + } + getEntities(ids) { + return this.resourceService.getByIds(ids); + } + getDisplayName(entity) { + return entity.displayName; + } + /** + * Override render to handle: + * 1. Special ordering when parentChildMap exists (resources grouped by parent) + * 2. Different colspan calculation (just dateCount, not childCount * dateCount) + */ + async render(context) { + const resourceIds = context.filter["resource"] || []; + const dateCount = context.filter["date"]?.length || 1; + let orderedResourceIds; + if (context.parentChildMap) { + orderedResourceIds = []; + for (const childIds of Object.values(context.parentChildMap)) { + for (const childId of childIds) { + if (resourceIds.includes(childId)) { + orderedResourceIds.push(childId); + } + } + } + } else { + orderedResourceIds = resourceIds; + } + const resources = await this.getEntities(orderedResourceIds); + const resourceMap = new Map(resources.map((r) => [r.id, r])); + for (const resourceId of orderedResourceIds) { + const resource = resourceMap.get(resourceId); + if (!resource) + continue; + const header = this.createHeader(resource, context); + header.style.gridColumn = `span ${dateCount}`; + context.headerContainer.appendChild(header); + } + } +}; +__name(_ResourceRenderer, "ResourceRenderer"); +var ResourceRenderer = _ResourceRenderer; + +// src/v2/features/team/TeamRenderer.ts +var _TeamRenderer = class _TeamRenderer extends BaseGroupingRenderer { + constructor(teamService) { + super(); + this.teamService = teamService; + this.type = "team"; + this.config = { + elementTag: "swp-team-header", + idAttribute: "teamId", + colspanVar: "--team-cols" + }; + } + getEntities(ids) { + return this.teamService.getByIds(ids); + } + getDisplayName(entity) { + return entity.name; + } +}; +__name(_TeamRenderer, "TeamRenderer"); +var TeamRenderer = _TeamRenderer; + +// src/v2/features/timeaxis/TimeAxisRenderer.ts +var _TimeAxisRenderer = class _TimeAxisRenderer { + render(container, startHour = 6, endHour = 20) { + container.innerHTML = ""; + for (let hour = startHour; hour <= endHour; hour++) { + const marker = document.createElement("swp-hour-marker"); + marker.textContent = `${hour.toString().padStart(2, "0")}:00`; + container.appendChild(marker); + } + } +}; +__name(_TimeAxisRenderer, "TimeAxisRenderer"); +var TimeAxisRenderer = _TimeAxisRenderer; +export { + CalendarOrchestrator, + DateRenderer, + DateService, + EventRenderer, + NavigationAnimator, + ResourceRenderer, + TeamRenderer, + TimeAxisRenderer, + buildPipeline +}; +//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vbm9kZV9tb2R1bGVzL2RheWpzL2RheWpzLm1pbi5qcyIsICIuLi8uLi9ub2RlX21vZHVsZXMvZGF5anMvcGx1Z2luL3V0Yy5qcyIsICIuLi8uLi9ub2RlX21vZHVsZXMvZGF5anMvcGx1Z2luL3RpbWV6b25lLmpzIiwgIi4uLy4uL25vZGVfbW9kdWxlcy9kYXlqcy9wbHVnaW4vaXNvV2Vlay5qcyIsICIuLi8uLi9zcmMvdjIvY29yZS9SZW5kZXJCdWlsZGVyLnRzIiwgIi4uLy4uL3NyYy92Mi9jb3JlL0ZpbHRlclRlbXBsYXRlLnRzIiwgIi4uLy4uL3NyYy92Mi9jb3JlL0NhbGVuZGFyT3JjaGVzdHJhdG9yLnRzIiwgIi4uLy4uL3NyYy92Mi9jb3JlL05hdmlnYXRpb25BbmltYXRvci50cyIsICIuLi8uLi9zcmMvdjIvZmVhdHVyZXMvZGF0ZS9EYXRlUmVuZGVyZXIudHMiLCAiLi4vLi4vc3JjL3YyL2NvcmUvRGF0ZVNlcnZpY2UudHMiLCAiLi4vLi4vc3JjL3YyL3V0aWxzL1Bvc2l0aW9uVXRpbHMudHMiLCAiLi4vLi4vc3JjL3YyL2NvbnN0YW50cy9Db3JlRXZlbnRzLnRzIiwgIi4uLy4uL3NyYy92Mi9mZWF0dXJlcy9ldmVudC9FdmVudExheW91dEVuZ2luZS50cyIsICIuLi8uLi9zcmMvdjIvZmVhdHVyZXMvZXZlbnQvRXZlbnRSZW5kZXJlci50cyIsICIuLi8uLi9zcmMvdjIvY29yZS9CYXNlR3JvdXBpbmdSZW5kZXJlci50cyIsICIuLi8uLi9zcmMvdjIvZmVhdHVyZXMvcmVzb3VyY2UvUmVzb3VyY2VSZW5kZXJlci50cyIsICIuLi8uLi9zcmMvdjIvZmVhdHVyZXMvdGVhbS9UZWFtUmVuZGVyZXIudHMiLCAiLi4vLi4vc3JjL3YyL2ZlYXR1cmVzL3RpbWVheGlzL1RpbWVBeGlzUmVuZGVyZXIudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbIiFmdW5jdGlvbih0LGUpe1wib2JqZWN0XCI9PXR5cGVvZiBleHBvcnRzJiZcInVuZGVmaW5lZFwiIT10eXBlb2YgbW9kdWxlP21vZHVsZS5leHBvcnRzPWUoKTpcImZ1bmN0aW9uXCI9PXR5cGVvZiBkZWZpbmUmJmRlZmluZS5hbWQ/ZGVmaW5lKGUpOih0PVwidW5kZWZpbmVkXCIhPXR5cGVvZiBnbG9iYWxUaGlzP2dsb2JhbFRoaXM6dHx8c2VsZikuZGF5anM9ZSgpfSh0aGlzLChmdW5jdGlvbigpe1widXNlIHN0cmljdFwiO3ZhciB0PTFlMyxlPTZlNCxuPTM2ZTUscj1cIm1pbGxpc2Vjb25kXCIsaT1cInNlY29uZFwiLHM9XCJtaW51dGVcIix1PVwiaG91clwiLGE9XCJkYXlcIixvPVwid2Vla1wiLGM9XCJtb250aFwiLGY9XCJxdWFydGVyXCIsaD1cInllYXJcIixkPVwiZGF0ZVwiLGw9XCJJbnZhbGlkIERhdGVcIiwkPS9eKFxcZHs0fSlbLS9dPyhcXGR7MSwyfSk/Wy0vXT8oXFxkezAsMn0pW1R0XFxzXSooXFxkezEsMn0pPzo/KFxcZHsxLDJ9KT86PyhcXGR7MSwyfSk/Wy46XT8oXFxkKyk/JC8seT0vXFxbKFteXFxdXSspXXxZezEsNH18TXsxLDR9fER7MSwyfXxkezEsNH18SHsxLDJ9fGh7MSwyfXxhfEF8bXsxLDJ9fHN7MSwyfXxaezEsMn18U1NTL2csTT17bmFtZTpcImVuXCIsd2Vla2RheXM6XCJTdW5kYXlfTW9uZGF5X1R1ZXNkYXlfV2VkbmVzZGF5X1RodXJzZGF5X0ZyaWRheV9TYXR1cmRheVwiLnNwbGl0KFwiX1wiKSxtb250aHM6XCJKYW51YXJ5X0ZlYnJ1YXJ5X01hcmNoX0FwcmlsX01heV9KdW5lX0p1bHlfQXVndXN0X1NlcHRlbWJlcl9PY3RvYmVyX05vdmVtYmVyX0RlY2VtYmVyXCIuc3BsaXQoXCJfXCIpLG9yZGluYWw6ZnVuY3Rpb24odCl7dmFyIGU9W1widGhcIixcInN0XCIsXCJuZFwiLFwicmRcIl0sbj10JTEwMDtyZXR1cm5cIltcIit0KyhlWyhuLTIwKSUxMF18fGVbbl18fGVbMF0pK1wiXVwifX0sbT1mdW5jdGlvbih0LGUsbil7dmFyIHI9U3RyaW5nKHQpO3JldHVybiFyfHxyLmxlbmd0aD49ZT90OlwiXCIrQXJyYXkoZSsxLXIubGVuZ3RoKS5qb2luKG4pK3R9LHY9e3M6bSx6OmZ1bmN0aW9uKHQpe3ZhciBlPS10LnV0Y09mZnNldCgpLG49TWF0aC5hYnMoZSkscj1NYXRoLmZsb29yKG4vNjApLGk9biU2MDtyZXR1cm4oZTw9MD9cIitcIjpcIi1cIikrbShyLDIsXCIwXCIpK1wiOlwiK20oaSwyLFwiMFwiKX0sbTpmdW5jdGlvbiB0KGUsbil7aWYoZS5kYXRlKCk8bi5kYXRlKCkpcmV0dXJuLXQobixlKTt2YXIgcj0xMioobi55ZWFyKCktZS55ZWFyKCkpKyhuLm1vbnRoKCktZS5tb250aCgpKSxpPWUuY2xvbmUoKS5hZGQocixjKSxzPW4taTwwLHU9ZS5jbG9uZSgpLmFkZChyKyhzPy0xOjEpLGMpO3JldHVybisoLShyKyhuLWkpLyhzP2ktdTp1LWkpKXx8MCl9LGE6ZnVuY3Rpb24odCl7cmV0dXJuIHQ8MD9NYXRoLmNlaWwodCl8fDA6TWF0aC5mbG9vcih0KX0scDpmdW5jdGlvbih0KXtyZXR1cm57TTpjLHk6aCx3Om8sZDphLEQ6ZCxoOnUsbTpzLHM6aSxtczpyLFE6Zn1bdF18fFN0cmluZyh0fHxcIlwiKS50b0xvd2VyQ2FzZSgpLnJlcGxhY2UoL3MkLyxcIlwiKX0sdTpmdW5jdGlvbih0KXtyZXR1cm4gdm9pZCAwPT09dH19LGc9XCJlblwiLEQ9e307RFtnXT1NO3ZhciBwPVwiJGlzRGF5anNPYmplY3RcIixTPWZ1bmN0aW9uKHQpe3JldHVybiB0IGluc3RhbmNlb2YgX3x8ISghdHx8IXRbcF0pfSx3PWZ1bmN0aW9uIHQoZSxuLHIpe3ZhciBpO2lmKCFlKXJldHVybiBnO2lmKFwic3RyaW5nXCI9PXR5cGVvZiBlKXt2YXIgcz1lLnRvTG93ZXJDYXNlKCk7RFtzXSYmKGk9cyksbiYmKERbc109bixpPXMpO3ZhciB1PWUuc3BsaXQoXCItXCIpO2lmKCFpJiZ1Lmxlbmd0aD4xKXJldHVybiB0KHVbMF0pfWVsc2V7dmFyIGE9ZS5uYW1lO0RbYV09ZSxpPWF9cmV0dXJuIXImJmkmJihnPWkpLGl8fCFyJiZnfSxPPWZ1bmN0aW9uKHQsZSl7aWYoUyh0KSlyZXR1cm4gdC5jbG9uZSgpO3ZhciBuPVwib2JqZWN0XCI9PXR5cGVvZiBlP2U6e307cmV0dXJuIG4uZGF0ZT10LG4uYXJncz1hcmd1bWVudHMsbmV3IF8obil9LGI9djtiLmw9dyxiLmk9UyxiLnc9ZnVuY3Rpb24odCxlKXtyZXR1cm4gTyh0LHtsb2NhbGU6ZS4kTCx1dGM6ZS4kdSx4OmUuJHgsJG9mZnNldDplLiRvZmZzZXR9KX07dmFyIF89ZnVuY3Rpb24oKXtmdW5jdGlvbiBNKHQpe3RoaXMuJEw9dyh0LmxvY2FsZSxudWxsLCEwKSx0aGlzLnBhcnNlKHQpLHRoaXMuJHg9dGhpcy4keHx8dC54fHx7fSx0aGlzW3BdPSEwfXZhciBtPU0ucHJvdG90eXBlO3JldHVybiBtLnBhcnNlPWZ1bmN0aW9uKHQpe3RoaXMuJGQ9ZnVuY3Rpb24odCl7dmFyIGU9dC5kYXRlLG49dC51dGM7aWYobnVsbD09PWUpcmV0dXJuIG5ldyBEYXRlKE5hTik7aWYoYi51KGUpKXJldHVybiBuZXcgRGF0ZTtpZihlIGluc3RhbmNlb2YgRGF0ZSlyZXR1cm4gbmV3IERhdGUoZSk7aWYoXCJzdHJpbmdcIj09dHlwZW9mIGUmJiEvWiQvaS50ZXN0KGUpKXt2YXIgcj1lLm1hdGNoKCQpO2lmKHIpe3ZhciBpPXJbMl0tMXx8MCxzPShyWzddfHxcIjBcIikuc3Vic3RyaW5nKDAsMyk7cmV0dXJuIG4/bmV3IERhdGUoRGF0ZS5VVEMoclsxXSxpLHJbM118fDEscls0XXx8MCxyWzVdfHwwLHJbNl18fDAscykpOm5ldyBEYXRlKHJbMV0saSxyWzNdfHwxLHJbNF18fDAscls1XXx8MCxyWzZdfHwwLHMpfX1yZXR1cm4gbmV3IERhdGUoZSl9KHQpLHRoaXMuaW5pdCgpfSxtLmluaXQ9ZnVuY3Rpb24oKXt2YXIgdD10aGlzLiRkO3RoaXMuJHk9dC5nZXRGdWxsWWVhcigpLHRoaXMuJE09dC5nZXRNb250aCgpLHRoaXMuJEQ9dC5nZXREYXRlKCksdGhpcy4kVz10LmdldERheSgpLHRoaXMuJEg9dC5nZXRIb3VycygpLHRoaXMuJG09dC5nZXRNaW51dGVzKCksdGhpcy4kcz10LmdldFNlY29uZHMoKSx0aGlzLiRtcz10LmdldE1pbGxpc2Vjb25kcygpfSxtLiR1dGlscz1mdW5jdGlvbigpe3JldHVybiBifSxtLmlzVmFsaWQ9ZnVuY3Rpb24oKXtyZXR1cm4hKHRoaXMuJGQudG9TdHJpbmcoKT09PWwpfSxtLmlzU2FtZT1mdW5jdGlvbih0LGUpe3ZhciBuPU8odCk7cmV0dXJuIHRoaXMuc3RhcnRPZihlKTw9biYmbjw9dGhpcy5lbmRPZihlKX0sbS5pc0FmdGVyPWZ1bmN0aW9uKHQsZSl7cmV0dXJuIE8odCk8dGhpcy5zdGFydE9mKGUpfSxtLmlzQmVmb3JlPWZ1bmN0aW9uKHQsZSl7cmV0dXJuIHRoaXMuZW5kT2YoZSk8Tyh0KX0sbS4kZz1mdW5jdGlvbih0LGUsbil7cmV0dXJuIGIudSh0KT90aGlzW2VdOnRoaXMuc2V0KG4sdCl9LG0udW5peD1mdW5jdGlvbigpe3JldHVybiBNYXRoLmZsb29yKHRoaXMudmFsdWVPZigpLzFlMyl9LG0udmFsdWVPZj1mdW5jdGlvbigpe3JldHVybiB0aGlzLiRkLmdldFRpbWUoKX0sbS5zdGFydE9mPWZ1bmN0aW9uKHQsZSl7dmFyIG49dGhpcyxyPSEhYi51KGUpfHxlLGY9Yi5wKHQpLGw9ZnVuY3Rpb24odCxlKXt2YXIgaT1iLncobi4kdT9EYXRlLlVUQyhuLiR5LGUsdCk6bmV3IERhdGUobi4keSxlLHQpLG4pO3JldHVybiByP2k6aS5lbmRPZihhKX0sJD1mdW5jdGlvbih0LGUpe3JldHVybiBiLncobi50b0RhdGUoKVt0XS5hcHBseShuLnRvRGF0ZShcInNcIiksKHI/WzAsMCwwLDBdOlsyMyw1OSw1OSw5OTldKS5zbGljZShlKSksbil9LHk9dGhpcy4kVyxNPXRoaXMuJE0sbT10aGlzLiRELHY9XCJzZXRcIisodGhpcy4kdT9cIlVUQ1wiOlwiXCIpO3N3aXRjaChmKXtjYXNlIGg6cmV0dXJuIHI/bCgxLDApOmwoMzEsMTEpO2Nhc2UgYzpyZXR1cm4gcj9sKDEsTSk6bCgwLE0rMSk7Y2FzZSBvOnZhciBnPXRoaXMuJGxvY2FsZSgpLndlZWtTdGFydHx8MCxEPSh5PGc/eSs3OnkpLWc7cmV0dXJuIGwocj9tLUQ6bSsoNi1EKSxNKTtjYXNlIGE6Y2FzZSBkOnJldHVybiAkKHYrXCJIb3Vyc1wiLDApO2Nhc2UgdTpyZXR1cm4gJCh2K1wiTWludXRlc1wiLDEpO2Nhc2UgczpyZXR1cm4gJCh2K1wiU2Vjb25kc1wiLDIpO2Nhc2UgaTpyZXR1cm4gJCh2K1wiTWlsbGlzZWNvbmRzXCIsMyk7ZGVmYXVsdDpyZXR1cm4gdGhpcy5jbG9uZSgpfX0sbS5lbmRPZj1mdW5jdGlvbih0KXtyZXR1cm4gdGhpcy5zdGFydE9mKHQsITEpfSxtLiRzZXQ9ZnVuY3Rpb24odCxlKXt2YXIgbixvPWIucCh0KSxmPVwic2V0XCIrKHRoaXMuJHU/XCJVVENcIjpcIlwiKSxsPShuPXt9LG5bYV09ZitcIkRhdGVcIixuW2RdPWYrXCJEYXRlXCIsbltjXT1mK1wiTW9udGhcIixuW2hdPWYrXCJGdWxsWWVhclwiLG5bdV09ZitcIkhvdXJzXCIsbltzXT1mK1wiTWludXRlc1wiLG5baV09ZitcIlNlY29uZHNcIixuW3JdPWYrXCJNaWxsaXNlY29uZHNcIixuKVtvXSwkPW89PT1hP3RoaXMuJEQrKGUtdGhpcy4kVyk6ZTtpZihvPT09Y3x8bz09PWgpe3ZhciB5PXRoaXMuY2xvbmUoKS5zZXQoZCwxKTt5LiRkW2xdKCQpLHkuaW5pdCgpLHRoaXMuJGQ9eS5zZXQoZCxNYXRoLm1pbih0aGlzLiRELHkuZGF5c0luTW9udGgoKSkpLiRkfWVsc2UgbCYmdGhpcy4kZFtsXSgkKTtyZXR1cm4gdGhpcy5pbml0KCksdGhpc30sbS5zZXQ9ZnVuY3Rpb24odCxlKXtyZXR1cm4gdGhpcy5jbG9uZSgpLiRzZXQodCxlKX0sbS5nZXQ9ZnVuY3Rpb24odCl7cmV0dXJuIHRoaXNbYi5wKHQpXSgpfSxtLmFkZD1mdW5jdGlvbihyLGYpe3ZhciBkLGw9dGhpcztyPU51bWJlcihyKTt2YXIgJD1iLnAoZikseT1mdW5jdGlvbih0KXt2YXIgZT1PKGwpO3JldHVybiBiLncoZS5kYXRlKGUuZGF0ZSgpK01hdGgucm91bmQodCpyKSksbCl9O2lmKCQ9PT1jKXJldHVybiB0aGlzLnNldChjLHRoaXMuJE0rcik7aWYoJD09PWgpcmV0dXJuIHRoaXMuc2V0KGgsdGhpcy4keStyKTtpZigkPT09YSlyZXR1cm4geSgxKTtpZigkPT09bylyZXR1cm4geSg3KTt2YXIgTT0oZD17fSxkW3NdPWUsZFt1XT1uLGRbaV09dCxkKVskXXx8MSxtPXRoaXMuJGQuZ2V0VGltZSgpK3IqTTtyZXR1cm4gYi53KG0sdGhpcyl9LG0uc3VidHJhY3Q9ZnVuY3Rpb24odCxlKXtyZXR1cm4gdGhpcy5hZGQoLTEqdCxlKX0sbS5mb3JtYXQ9ZnVuY3Rpb24odCl7dmFyIGU9dGhpcyxuPXRoaXMuJGxvY2FsZSgpO2lmKCF0aGlzLmlzVmFsaWQoKSlyZXR1cm4gbi5pbnZhbGlkRGF0ZXx8bDt2YXIgcj10fHxcIllZWVktTU0tRERUSEg6bW06c3NaXCIsaT1iLnoodGhpcykscz10aGlzLiRILHU9dGhpcy4kbSxhPXRoaXMuJE0sbz1uLndlZWtkYXlzLGM9bi5tb250aHMsZj1uLm1lcmlkaWVtLGg9ZnVuY3Rpb24odCxuLGkscyl7cmV0dXJuIHQmJih0W25dfHx0KGUscikpfHxpW25dLnNsaWNlKDAscyl9LGQ9ZnVuY3Rpb24odCl7cmV0dXJuIGIucyhzJTEyfHwxMix0LFwiMFwiKX0sJD1mfHxmdW5jdGlvbih0LGUsbil7dmFyIHI9dDwxMj9cIkFNXCI6XCJQTVwiO3JldHVybiBuP3IudG9Mb3dlckNhc2UoKTpyfTtyZXR1cm4gci5yZXBsYWNlKHksKGZ1bmN0aW9uKHQscil7cmV0dXJuIHJ8fGZ1bmN0aW9uKHQpe3N3aXRjaCh0KXtjYXNlXCJZWVwiOnJldHVybiBTdHJpbmcoZS4keSkuc2xpY2UoLTIpO2Nhc2VcIllZWVlcIjpyZXR1cm4gYi5zKGUuJHksNCxcIjBcIik7Y2FzZVwiTVwiOnJldHVybiBhKzE7Y2FzZVwiTU1cIjpyZXR1cm4gYi5zKGErMSwyLFwiMFwiKTtjYXNlXCJNTU1cIjpyZXR1cm4gaChuLm1vbnRoc1Nob3J0LGEsYywzKTtjYXNlXCJNTU1NXCI6cmV0dXJuIGgoYyxhKTtjYXNlXCJEXCI6cmV0dXJuIGUuJEQ7Y2FzZVwiRERcIjpyZXR1cm4gYi5zKGUuJEQsMixcIjBcIik7Y2FzZVwiZFwiOnJldHVybiBTdHJpbmcoZS4kVyk7Y2FzZVwiZGRcIjpyZXR1cm4gaChuLndlZWtkYXlzTWluLGUuJFcsbywyKTtjYXNlXCJkZGRcIjpyZXR1cm4gaChuLndlZWtkYXlzU2hvcnQsZS4kVyxvLDMpO2Nhc2VcImRkZGRcIjpyZXR1cm4gb1tlLiRXXTtjYXNlXCJIXCI6cmV0dXJuIFN0cmluZyhzKTtjYXNlXCJISFwiOnJldHVybiBiLnMocywyLFwiMFwiKTtjYXNlXCJoXCI6cmV0dXJuIGQoMSk7Y2FzZVwiaGhcIjpyZXR1cm4gZCgyKTtjYXNlXCJhXCI6cmV0dXJuICQocyx1LCEwKTtjYXNlXCJBXCI6cmV0dXJuICQocyx1LCExKTtjYXNlXCJtXCI6cmV0dXJuIFN0cmluZyh1KTtjYXNlXCJtbVwiOnJldHVybiBiLnModSwyLFwiMFwiKTtjYXNlXCJzXCI6cmV0dXJuIFN0cmluZyhlLiRzKTtjYXNlXCJzc1wiOnJldHVybiBiLnMoZS4kcywyLFwiMFwiKTtjYXNlXCJTU1NcIjpyZXR1cm4gYi5zKGUuJG1zLDMsXCIwXCIpO2Nhc2VcIlpcIjpyZXR1cm4gaX1yZXR1cm4gbnVsbH0odCl8fGkucmVwbGFjZShcIjpcIixcIlwiKX0pKX0sbS51dGNPZmZzZXQ9ZnVuY3Rpb24oKXtyZXR1cm4gMTUqLU1hdGgucm91bmQodGhpcy4kZC5nZXRUaW1lem9uZU9mZnNldCgpLzE1KX0sbS5kaWZmPWZ1bmN0aW9uKHIsZCxsKXt2YXIgJCx5PXRoaXMsTT1iLnAoZCksbT1PKHIpLHY9KG0udXRjT2Zmc2V0KCktdGhpcy51dGNPZmZzZXQoKSkqZSxnPXRoaXMtbSxEPWZ1bmN0aW9uKCl7cmV0dXJuIGIubSh5LG0pfTtzd2l0Y2goTSl7Y2FzZSBoOiQ9RCgpLzEyO2JyZWFrO2Nhc2UgYzokPUQoKTticmVhaztjYXNlIGY6JD1EKCkvMzticmVhaztjYXNlIG86JD0oZy12KS82MDQ4ZTU7YnJlYWs7Y2FzZSBhOiQ9KGctdikvODY0ZTU7YnJlYWs7Y2FzZSB1OiQ9Zy9uO2JyZWFrO2Nhc2UgczokPWcvZTticmVhaztjYXNlIGk6JD1nL3Q7YnJlYWs7ZGVmYXVsdDokPWd9cmV0dXJuIGw/JDpiLmEoJCl9LG0uZGF5c0luTW9udGg9ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5lbmRPZihjKS4kRH0sbS4kbG9jYWxlPWZ1bmN0aW9uKCl7cmV0dXJuIERbdGhpcy4kTF19LG0ubG9jYWxlPWZ1bmN0aW9uKHQsZSl7aWYoIXQpcmV0dXJuIHRoaXMuJEw7dmFyIG49dGhpcy5jbG9uZSgpLHI9dyh0LGUsITApO3JldHVybiByJiYobi4kTD1yKSxufSxtLmNsb25lPWZ1bmN0aW9uKCl7cmV0dXJuIGIudyh0aGlzLiRkLHRoaXMpfSxtLnRvRGF0ZT1mdW5jdGlvbigpe3JldHVybiBuZXcgRGF0ZSh0aGlzLnZhbHVlT2YoKSl9LG0udG9KU09OPWZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuaXNWYWxpZCgpP3RoaXMudG9JU09TdHJpbmcoKTpudWxsfSxtLnRvSVNPU3RyaW5nPWZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuJGQudG9JU09TdHJpbmcoKX0sbS50b1N0cmluZz1mdW5jdGlvbigpe3JldHVybiB0aGlzLiRkLnRvVVRDU3RyaW5nKCl9LE19KCksaz1fLnByb3RvdHlwZTtyZXR1cm4gTy5wcm90b3R5cGU9ayxbW1wiJG1zXCIscl0sW1wiJHNcIixpXSxbXCIkbVwiLHNdLFtcIiRIXCIsdV0sW1wiJFdcIixhXSxbXCIkTVwiLGNdLFtcIiR5XCIsaF0sW1wiJERcIixkXV0uZm9yRWFjaCgoZnVuY3Rpb24odCl7a1t0WzFdXT1mdW5jdGlvbihlKXtyZXR1cm4gdGhpcy4kZyhlLHRbMF0sdFsxXSl9fSkpLE8uZXh0ZW5kPWZ1bmN0aW9uKHQsZSl7cmV0dXJuIHQuJGl8fCh0KGUsXyxPKSx0LiRpPSEwKSxPfSxPLmxvY2FsZT13LE8uaXNEYXlqcz1TLE8udW5peD1mdW5jdGlvbih0KXtyZXR1cm4gTygxZTMqdCl9LE8uZW49RFtnXSxPLkxzPUQsTy5wPXt9LE99KSk7IiwgIiFmdW5jdGlvbih0LGkpe1wib2JqZWN0XCI9PXR5cGVvZiBleHBvcnRzJiZcInVuZGVmaW5lZFwiIT10eXBlb2YgbW9kdWxlP21vZHVsZS5leHBvcnRzPWkoKTpcImZ1bmN0aW9uXCI9PXR5cGVvZiBkZWZpbmUmJmRlZmluZS5hbWQ/ZGVmaW5lKGkpOih0PVwidW5kZWZpbmVkXCIhPXR5cGVvZiBnbG9iYWxUaGlzP2dsb2JhbFRoaXM6dHx8c2VsZikuZGF5anNfcGx1Z2luX3V0Yz1pKCl9KHRoaXMsKGZ1bmN0aW9uKCl7XCJ1c2Ugc3RyaWN0XCI7dmFyIHQ9XCJtaW51dGVcIixpPS9bKy1dXFxkXFxkKD86Oj9cXGRcXGQpPy9nLGU9LyhbKy1dfFxcZFxcZCkvZztyZXR1cm4gZnVuY3Rpb24ocyxmLG4pe3ZhciB1PWYucHJvdG90eXBlO24udXRjPWZ1bmN0aW9uKHQpe3ZhciBpPXtkYXRlOnQsdXRjOiEwLGFyZ3M6YXJndW1lbnRzfTtyZXR1cm4gbmV3IGYoaSl9LHUudXRjPWZ1bmN0aW9uKGkpe3ZhciBlPW4odGhpcy50b0RhdGUoKSx7bG9jYWxlOnRoaXMuJEwsdXRjOiEwfSk7cmV0dXJuIGk/ZS5hZGQodGhpcy51dGNPZmZzZXQoKSx0KTplfSx1LmxvY2FsPWZ1bmN0aW9uKCl7cmV0dXJuIG4odGhpcy50b0RhdGUoKSx7bG9jYWxlOnRoaXMuJEwsdXRjOiExfSl9O3ZhciByPXUucGFyc2U7dS5wYXJzZT1mdW5jdGlvbih0KXt0LnV0YyYmKHRoaXMuJHU9ITApLHRoaXMuJHV0aWxzKCkudSh0LiRvZmZzZXQpfHwodGhpcy4kb2Zmc2V0PXQuJG9mZnNldCksci5jYWxsKHRoaXMsdCl9O3ZhciBvPXUuaW5pdDt1LmluaXQ9ZnVuY3Rpb24oKXtpZih0aGlzLiR1KXt2YXIgdD10aGlzLiRkO3RoaXMuJHk9dC5nZXRVVENGdWxsWWVhcigpLHRoaXMuJE09dC5nZXRVVENNb250aCgpLHRoaXMuJEQ9dC5nZXRVVENEYXRlKCksdGhpcy4kVz10LmdldFVUQ0RheSgpLHRoaXMuJEg9dC5nZXRVVENIb3VycygpLHRoaXMuJG09dC5nZXRVVENNaW51dGVzKCksdGhpcy4kcz10LmdldFVUQ1NlY29uZHMoKSx0aGlzLiRtcz10LmdldFVUQ01pbGxpc2Vjb25kcygpfWVsc2Ugby5jYWxsKHRoaXMpfTt2YXIgYT11LnV0Y09mZnNldDt1LnV0Y09mZnNldD1mdW5jdGlvbihzLGYpe3ZhciBuPXRoaXMuJHV0aWxzKCkudTtpZihuKHMpKXJldHVybiB0aGlzLiR1PzA6bih0aGlzLiRvZmZzZXQpP2EuY2FsbCh0aGlzKTp0aGlzLiRvZmZzZXQ7aWYoXCJzdHJpbmdcIj09dHlwZW9mIHMmJihzPWZ1bmN0aW9uKHQpe3ZvaWQgMD09PXQmJih0PVwiXCIpO3ZhciBzPXQubWF0Y2goaSk7aWYoIXMpcmV0dXJuIG51bGw7dmFyIGY9KFwiXCIrc1swXSkubWF0Y2goZSl8fFtcIi1cIiwwLDBdLG49ZlswXSx1PTYwKitmWzFdKyArZlsyXTtyZXR1cm4gMD09PXU/MDpcIitcIj09PW4/dTotdX0ocyksbnVsbD09PXMpKXJldHVybiB0aGlzO3ZhciB1PU1hdGguYWJzKHMpPD0xNj82MCpzOnM7aWYoMD09PXUpcmV0dXJuIHRoaXMudXRjKGYpO3ZhciByPXRoaXMuY2xvbmUoKTtpZihmKXJldHVybiByLiRvZmZzZXQ9dSxyLiR1PSExLHI7dmFyIG89dGhpcy4kdT90aGlzLnRvRGF0ZSgpLmdldFRpbWV6b25lT2Zmc2V0KCk6LTEqdGhpcy51dGNPZmZzZXQoKTtyZXR1cm4ocj10aGlzLmxvY2FsKCkuYWRkKHUrbyx0KSkuJG9mZnNldD11LHIuJHguJGxvY2FsT2Zmc2V0PW8scn07dmFyIGg9dS5mb3JtYXQ7dS5mb3JtYXQ9ZnVuY3Rpb24odCl7dmFyIGk9dHx8KHRoaXMuJHU/XCJZWVlZLU1NLUREVEhIOm1tOnNzW1pdXCI6XCJcIik7cmV0dXJuIGguY2FsbCh0aGlzLGkpfSx1LnZhbHVlT2Y9ZnVuY3Rpb24oKXt2YXIgdD10aGlzLiR1dGlscygpLnUodGhpcy4kb2Zmc2V0KT8wOnRoaXMuJG9mZnNldCsodGhpcy4keC4kbG9jYWxPZmZzZXR8fHRoaXMuJGQuZ2V0VGltZXpvbmVPZmZzZXQoKSk7cmV0dXJuIHRoaXMuJGQudmFsdWVPZigpLTZlNCp0fSx1LmlzVVRDPWZ1bmN0aW9uKCl7cmV0dXJuISF0aGlzLiR1fSx1LnRvSVNPU3RyaW5nPWZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMudG9EYXRlKCkudG9JU09TdHJpbmcoKX0sdS50b1N0cmluZz1mdW5jdGlvbigpe3JldHVybiB0aGlzLnRvRGF0ZSgpLnRvVVRDU3RyaW5nKCl9O3ZhciBsPXUudG9EYXRlO3UudG9EYXRlPWZ1bmN0aW9uKHQpe3JldHVyblwic1wiPT09dCYmdGhpcy4kb2Zmc2V0P24odGhpcy5mb3JtYXQoXCJZWVlZLU1NLUREIEhIOm1tOnNzOlNTU1wiKSkudG9EYXRlKCk6bC5jYWxsKHRoaXMpfTt2YXIgYz11LmRpZmY7dS5kaWZmPWZ1bmN0aW9uKHQsaSxlKXtpZih0JiZ0aGlzLiR1PT09dC4kdSlyZXR1cm4gYy5jYWxsKHRoaXMsdCxpLGUpO3ZhciBzPXRoaXMubG9jYWwoKSxmPW4odCkubG9jYWwoKTtyZXR1cm4gYy5jYWxsKHMsZixpLGUpfX19KSk7IiwgIiFmdW5jdGlvbih0LGUpe1wib2JqZWN0XCI9PXR5cGVvZiBleHBvcnRzJiZcInVuZGVmaW5lZFwiIT10eXBlb2YgbW9kdWxlP21vZHVsZS5leHBvcnRzPWUoKTpcImZ1bmN0aW9uXCI9PXR5cGVvZiBkZWZpbmUmJmRlZmluZS5hbWQ/ZGVmaW5lKGUpOih0PVwidW5kZWZpbmVkXCIhPXR5cGVvZiBnbG9iYWxUaGlzP2dsb2JhbFRoaXM6dHx8c2VsZikuZGF5anNfcGx1Z2luX3RpbWV6b25lPWUoKX0odGhpcywoZnVuY3Rpb24oKXtcInVzZSBzdHJpY3RcIjt2YXIgdD17eWVhcjowLG1vbnRoOjEsZGF5OjIsaG91cjozLG1pbnV0ZTo0LHNlY29uZDo1fSxlPXt9O3JldHVybiBmdW5jdGlvbihuLGksbyl7dmFyIHIsYT1mdW5jdGlvbih0LG4saSl7dm9pZCAwPT09aSYmKGk9e30pO3ZhciBvPW5ldyBEYXRlKHQpLHI9ZnVuY3Rpb24odCxuKXt2b2lkIDA9PT1uJiYobj17fSk7dmFyIGk9bi50aW1lWm9uZU5hbWV8fFwic2hvcnRcIixvPXQrXCJ8XCIraSxyPWVbb107cmV0dXJuIHJ8fChyPW5ldyBJbnRsLkRhdGVUaW1lRm9ybWF0KFwiZW4tVVNcIix7aG91cjEyOiExLHRpbWVab25lOnQseWVhcjpcIm51bWVyaWNcIixtb250aDpcIjItZGlnaXRcIixkYXk6XCIyLWRpZ2l0XCIsaG91cjpcIjItZGlnaXRcIixtaW51dGU6XCIyLWRpZ2l0XCIsc2Vjb25kOlwiMi1kaWdpdFwiLHRpbWVab25lTmFtZTppfSksZVtvXT1yKSxyfShuLGkpO3JldHVybiByLmZvcm1hdFRvUGFydHMobyl9LHU9ZnVuY3Rpb24oZSxuKXtmb3IodmFyIGk9YShlLG4pLHI9W10sdT0wO3U8aS5sZW5ndGg7dSs9MSl7dmFyIGY9aVt1XSxzPWYudHlwZSxtPWYudmFsdWUsYz10W3NdO2M+PTAmJihyW2NdPXBhcnNlSW50KG0sMTApKX12YXIgZD1yWzNdLGw9MjQ9PT1kPzA6ZCxoPXJbMF0rXCItXCIrclsxXStcIi1cIityWzJdK1wiIFwiK2wrXCI6XCIrcls0XStcIjpcIityWzVdK1wiOjAwMFwiLHY9K2U7cmV0dXJuKG8udXRjKGgpLnZhbHVlT2YoKS0odi09diUxZTMpKS82ZTR9LGY9aS5wcm90b3R5cGU7Zi50ej1mdW5jdGlvbih0LGUpe3ZvaWQgMD09PXQmJih0PXIpO3ZhciBuLGk9dGhpcy51dGNPZmZzZXQoKSxhPXRoaXMudG9EYXRlKCksdT1hLnRvTG9jYWxlU3RyaW5nKFwiZW4tVVNcIix7dGltZVpvbmU6dH0pLGY9TWF0aC5yb3VuZCgoYS1uZXcgRGF0ZSh1KSkvMWUzLzYwKSxzPTE1Ki1NYXRoLnJvdW5kKGEuZ2V0VGltZXpvbmVPZmZzZXQoKS8xNSktZjtpZighTnVtYmVyKHMpKW49dGhpcy51dGNPZmZzZXQoMCxlKTtlbHNlIGlmKG49byh1LHtsb2NhbGU6dGhpcy4kTH0pLiRzZXQoXCJtaWxsaXNlY29uZFwiLHRoaXMuJG1zKS51dGNPZmZzZXQocywhMCksZSl7dmFyIG09bi51dGNPZmZzZXQoKTtuPW4uYWRkKGktbSxcIm1pbnV0ZVwiKX1yZXR1cm4gbi4keC4kdGltZXpvbmU9dCxufSxmLm9mZnNldE5hbWU9ZnVuY3Rpb24odCl7dmFyIGU9dGhpcy4keC4kdGltZXpvbmV8fG8udHouZ3Vlc3MoKSxuPWEodGhpcy52YWx1ZU9mKCksZSx7dGltZVpvbmVOYW1lOnR9KS5maW5kKChmdW5jdGlvbih0KXtyZXR1cm5cInRpbWV6b25lbmFtZVwiPT09dC50eXBlLnRvTG93ZXJDYXNlKCl9KSk7cmV0dXJuIG4mJm4udmFsdWV9O3ZhciBzPWYuc3RhcnRPZjtmLnN0YXJ0T2Y9ZnVuY3Rpb24odCxlKXtpZighdGhpcy4keHx8IXRoaXMuJHguJHRpbWV6b25lKXJldHVybiBzLmNhbGwodGhpcyx0LGUpO3ZhciBuPW8odGhpcy5mb3JtYXQoXCJZWVlZLU1NLUREIEhIOm1tOnNzOlNTU1wiKSx7bG9jYWxlOnRoaXMuJEx9KTtyZXR1cm4gcy5jYWxsKG4sdCxlKS50eih0aGlzLiR4LiR0aW1lem9uZSwhMCl9LG8udHo9ZnVuY3Rpb24odCxlLG4pe3ZhciBpPW4mJmUsYT1ufHxlfHxyLGY9dSgrbygpLGEpO2lmKFwic3RyaW5nXCIhPXR5cGVvZiB0KXJldHVybiBvKHQpLnR6KGEpO3ZhciBzPWZ1bmN0aW9uKHQsZSxuKXt2YXIgaT10LTYwKmUqMWUzLG89dShpLG4pO2lmKGU9PT1vKXJldHVybltpLGVdO3ZhciByPXUoaS09NjAqKG8tZSkqMWUzLG4pO3JldHVybiBvPT09cj9baSxvXTpbdC02MCpNYXRoLm1pbihvLHIpKjFlMyxNYXRoLm1heChvLHIpXX0oby51dGModCxpKS52YWx1ZU9mKCksZixhKSxtPXNbMF0sYz1zWzFdLGQ9byhtKS51dGNPZmZzZXQoYyk7cmV0dXJuIGQuJHguJHRpbWV6b25lPWEsZH0sby50ei5ndWVzcz1mdW5jdGlvbigpe3JldHVybiBJbnRsLkRhdGVUaW1lRm9ybWF0KCkucmVzb2x2ZWRPcHRpb25zKCkudGltZVpvbmV9LG8udHouc2V0RGVmYXVsdD1mdW5jdGlvbih0KXtyPXR9fX0pKTsiLCAiIWZ1bmN0aW9uKGUsdCl7XCJvYmplY3RcIj09dHlwZW9mIGV4cG9ydHMmJlwidW5kZWZpbmVkXCIhPXR5cGVvZiBtb2R1bGU/bW9kdWxlLmV4cG9ydHM9dCgpOlwiZnVuY3Rpb25cIj09dHlwZW9mIGRlZmluZSYmZGVmaW5lLmFtZD9kZWZpbmUodCk6KGU9XCJ1bmRlZmluZWRcIiE9dHlwZW9mIGdsb2JhbFRoaXM/Z2xvYmFsVGhpczplfHxzZWxmKS5kYXlqc19wbHVnaW5faXNvV2Vlaz10KCl9KHRoaXMsKGZ1bmN0aW9uKCl7XCJ1c2Ugc3RyaWN0XCI7dmFyIGU9XCJkYXlcIjtyZXR1cm4gZnVuY3Rpb24odCxpLHMpe3ZhciBhPWZ1bmN0aW9uKHQpe3JldHVybiB0LmFkZCg0LXQuaXNvV2Vla2RheSgpLGUpfSxkPWkucHJvdG90eXBlO2QuaXNvV2Vla1llYXI9ZnVuY3Rpb24oKXtyZXR1cm4gYSh0aGlzKS55ZWFyKCl9LGQuaXNvV2Vlaz1mdW5jdGlvbih0KXtpZighdGhpcy4kdXRpbHMoKS51KHQpKXJldHVybiB0aGlzLmFkZCg3Kih0LXRoaXMuaXNvV2VlaygpKSxlKTt2YXIgaSxkLG4sbyxyPWEodGhpcyksdT0oaT10aGlzLmlzb1dlZWtZZWFyKCksZD10aGlzLiR1LG49KGQ/cy51dGM6cykoKS55ZWFyKGkpLnN0YXJ0T2YoXCJ5ZWFyXCIpLG89NC1uLmlzb1dlZWtkYXkoKSxuLmlzb1dlZWtkYXkoKT40JiYobys9Nyksbi5hZGQobyxlKSk7cmV0dXJuIHIuZGlmZih1LFwid2Vla1wiKSsxfSxkLmlzb1dlZWtkYXk9ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMuJHV0aWxzKCkudShlKT90aGlzLmRheSgpfHw3OnRoaXMuZGF5KHRoaXMuZGF5KCklNz9lOmUtNyl9O3ZhciBuPWQuc3RhcnRPZjtkLnN0YXJ0T2Y9ZnVuY3Rpb24oZSx0KXt2YXIgaT10aGlzLiR1dGlscygpLHM9ISFpLnUodCl8fHQ7cmV0dXJuXCJpc293ZWVrXCI9PT1pLnAoZSk/cz90aGlzLmRhdGUodGhpcy5kYXRlKCktKHRoaXMuaXNvV2Vla2RheSgpLTEpKS5zdGFydE9mKFwiZGF5XCIpOnRoaXMuZGF0ZSh0aGlzLmRhdGUoKS0xLSh0aGlzLmlzb1dlZWtkYXkoKS0xKSs3KS5lbmRPZihcImRheVwiKTpuLmJpbmQodGhpcykoZSx0KX19fSkpOyIsICJpbXBvcnQgeyBJUmVuZGVyZXIsIElSZW5kZXJDb250ZXh0IH0gZnJvbSAnLi9JR3JvdXBpbmdSZW5kZXJlcic7XG5cbmV4cG9ydCBpbnRlcmZhY2UgUGlwZWxpbmUge1xuICBydW4oY29udGV4dDogSVJlbmRlckNvbnRleHQpOiBQcm9taXNlPHZvaWQ+O1xufVxuXG5leHBvcnQgZnVuY3Rpb24gYnVpbGRQaXBlbGluZShyZW5kZXJlcnM6IElSZW5kZXJlcltdKTogUGlwZWxpbmUge1xuICByZXR1cm4ge1xuICAgIGFzeW5jIHJ1bihjb250ZXh0OiBJUmVuZGVyQ29udGV4dCkge1xuICAgICAgZm9yIChjb25zdCByZW5kZXJlciBvZiByZW5kZXJlcnMpIHtcbiAgICAgICAgYXdhaXQgcmVuZGVyZXIucmVuZGVyKGNvbnRleHQpO1xuICAgICAgfVxuICAgIH1cbiAgfTtcbn1cbiIsICJpbXBvcnQgeyBJQ2FsZW5kYXJFdmVudCB9IGZyb20gJy4uL3R5cGVzL0NhbGVuZGFyVHlwZXMnO1xyXG5pbXBvcnQgeyBEYXRlU2VydmljZSB9IGZyb20gJy4vRGF0ZVNlcnZpY2UnO1xyXG5pbXBvcnQgeyBJRW50aXR5UmVzb2x2ZXIgfSBmcm9tICcuL0lFbnRpdHlSZXNvbHZlcic7XHJcblxyXG4vKipcclxuICogRmllbGQgZGVmaW5pdGlvbiBmb3IgRmlsdGVyVGVtcGxhdGVcclxuICovXHJcbmludGVyZmFjZSBJRmlsdGVyRmllbGQge1xyXG4gIGlkUHJvcGVydHk6IHN0cmluZztcclxuICBkZXJpdmVkRnJvbT86IHN0cmluZztcclxufVxyXG5cclxuLyoqXHJcbiAqIFBhcnNlZCBkb3Qtbm90YXRpb24gcmVmZXJlbmNlXHJcbiAqL1xyXG5pbnRlcmZhY2UgSURvdE5vdGF0aW9uIHtcclxuICBlbnRpdHlUeXBlOiBzdHJpbmc7ICAgLy8gZS5nLiwgJ3Jlc291cmNlJ1xyXG4gIHByb3BlcnR5OiBzdHJpbmc7ICAgICAvLyBlLmcuLCAndGVhbUlkJ1xyXG4gIGZvcmVpZ25LZXk6IHN0cmluZzsgICAvLyBlLmcuLCAncmVzb3VyY2VJZCdcclxufVxyXG5cclxuLyoqXHJcbiAqIEZpbHRlclRlbXBsYXRlIC0gQnlnZ2VyIG5cdTAwRjhnbGVyIHRpbCBldmVudC1rb2xvbm5lIG1hdGNoaW5nXHJcbiAqXHJcbiAqIFZpZXdDb25maWcgZGVmaW5lcmVyIGh2aWxrZSBmZWx0ZXIgKGlkUHJvcGVydGllcykgZGVyIGluZGdcdTAwRTVyIGkga29sb25uZW5zIG5cdTAwRjhnbGUuXHJcbiAqIFNhbW1lIHRlbXBsYXRlIGJydWdlcyB0aWwgYXQgYnlnZ2Ugblx1MDBGOGdsZSBmb3IgYlx1MDBFNWRlIGtvbG9ubmUgb2cgZXZlbnQuXHJcbiAqXHJcbiAqIFN1cHBvcnRzIGRvdC1ub3RhdGlvbiBmb3IgaGllcmFyY2hpY2FsIHJlbGF0aW9uczpcclxuICogLSAncmVzb3VyY2UudGVhbUlkJyBcdTIxOTIgbG9va3MgdXAgZXZlbnQucmVzb3VyY2VJZCBcdTIxOTIgcmVzb3VyY2UgZW50aXR5IFx1MjE5MiB0ZWFtSWRcclxuICpcclxuICogUHJpbmNpcDogS29sb25uZW5zIG5cdTAwRjhnbGUtdGVtcGxhdGUgYmVzdGVtbWVyIGh2YWQgZGVyIG1hdGNoZXMgcFx1MDBFNS5cclxuICpcclxuICogQHNlZSBkb2NzL2ZpbHRlci10ZW1wbGF0ZS5tZFxyXG4gKi9cclxuZXhwb3J0IGNsYXNzIEZpbHRlclRlbXBsYXRlIHtcclxuICBwcml2YXRlIGZpZWxkczogSUZpbHRlckZpZWxkW10gPSBbXTtcclxuXHJcbiAgY29uc3RydWN0b3IoXHJcbiAgICBwcml2YXRlIGRhdGVTZXJ2aWNlOiBEYXRlU2VydmljZSxcclxuICAgIHByaXZhdGUgZW50aXR5UmVzb2x2ZXI/OiBJRW50aXR5UmVzb2x2ZXJcclxuICApIHt9XHJcblxyXG4gIC8qKlxyXG4gICAqIFRpbGZcdTAwRjhqIGZlbHQgdGlsIHRlbXBsYXRlXHJcbiAgICogQHBhcmFtIGlkUHJvcGVydHkgLSBQcm9wZXJ0eS1uYXZuIChicnVnZXMgcFx1MDBFNSBiXHUwMEU1ZGUgZXZlbnQgb2cgY29sdW1uLmRhdGFzZXQpXHJcbiAgICogQHBhcmFtIGRlcml2ZWRGcm9tIC0gSHZpcyBmZWx0ZXQgdWRsZWRlcyBmcmEgYW5kZW4gcHJvcGVydHkgKGYuZWtzLiBkYXRlIGZyYSBzdGFydClcclxuICAgKi9cclxuICBhZGRGaWVsZChpZFByb3BlcnR5OiBzdHJpbmcsIGRlcml2ZWRGcm9tPzogc3RyaW5nKTogdGhpcyB7XHJcbiAgICB0aGlzLmZpZWxkcy5wdXNoKHsgaWRQcm9wZXJ0eSwgZGVyaXZlZEZyb20gfSk7XHJcbiAgICByZXR1cm4gdGhpcztcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFBhcnNlIGRvdC1ub3RhdGlvbiBzdHJpbmcgaW50byBjb21wb25lbnRzXHJcbiAgICogQGV4YW1wbGUgJ3Jlc291cmNlLnRlYW1JZCcgXHUyMTkyIHsgZW50aXR5VHlwZTogJ3Jlc291cmNlJywgcHJvcGVydHk6ICd0ZWFtSWQnLCBmb3JlaWduS2V5OiAncmVzb3VyY2VJZCcgfVxyXG4gICAqL1xyXG4gIHByaXZhdGUgcGFyc2VEb3ROb3RhdGlvbihpZFByb3BlcnR5OiBzdHJpbmcpOiBJRG90Tm90YXRpb24gfCBudWxsIHtcclxuICAgIGlmICghaWRQcm9wZXJ0eS5pbmNsdWRlcygnLicpKSByZXR1cm4gbnVsbDtcclxuICAgIGNvbnN0IFtlbnRpdHlUeXBlLCBwcm9wZXJ0eV0gPSBpZFByb3BlcnR5LnNwbGl0KCcuJyk7XHJcbiAgICByZXR1cm4ge1xyXG4gICAgICBlbnRpdHlUeXBlLFxyXG4gICAgICBwcm9wZXJ0eSxcclxuICAgICAgZm9yZWlnbktleTogZW50aXR5VHlwZSArICdJZCcgLy8gQ29udmVudGlvbjogcmVzb3VyY2UgXHUyMTkyIHJlc291cmNlSWRcclxuICAgIH07XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBHZXQgZGF0YXNldCBrZXkgZm9yIGNvbHVtbiBsb29rdXBcclxuICAgKiBGb3IgZG90LW5vdGF0aW9uICdyZXNvdXJjZS50ZWFtSWQnLCB3ZSBsb29rIGZvciAndGVhbUlkJyBpbiBkYXRhc2V0XHJcbiAgICovXHJcbiAgcHJpdmF0ZSBnZXREYXRhc2V0S2V5KGlkUHJvcGVydHk6IHN0cmluZyk6IHN0cmluZyB7XHJcbiAgICBjb25zdCBkb3ROb3RhdGlvbiA9IHRoaXMucGFyc2VEb3ROb3RhdGlvbihpZFByb3BlcnR5KTtcclxuICAgIGlmIChkb3ROb3RhdGlvbikge1xyXG4gICAgICByZXR1cm4gZG90Tm90YXRpb24ucHJvcGVydHk7IC8vICd0ZWFtSWQnXHJcbiAgICB9XHJcbiAgICByZXR1cm4gaWRQcm9wZXJ0eTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIEJ5ZyBuXHUwMEY4Z2xlIGZyYSBrb2xvbm5lXHJcbiAgICogTFx1MDBFNnNlciB2XHUwMEU2cmRpZXIgZnJhIGNvbHVtbi5kYXRhc2V0W2lkUHJvcGVydHldXHJcbiAgICogRm9yIGRvdC1ub3RhdGlvbiwgdXNlcyB0aGUgcHJvcGVydHkgcGFydCAocmVzb3VyY2UudGVhbUlkIFx1MjE5MiB0ZWFtSWQpXHJcbiAgICovXHJcbiAgYnVpbGRLZXlGcm9tQ29sdW1uKGNvbHVtbjogSFRNTEVsZW1lbnQpOiBzdHJpbmcge1xyXG4gICAgcmV0dXJuIHRoaXMuZmllbGRzXHJcbiAgICAgIC5tYXAoZiA9PiB7XHJcbiAgICAgICAgY29uc3Qga2V5ID0gdGhpcy5nZXREYXRhc2V0S2V5KGYuaWRQcm9wZXJ0eSk7XHJcbiAgICAgICAgcmV0dXJuIGNvbHVtbi5kYXRhc2V0W2tleV0gfHwgJyc7XHJcbiAgICAgIH0pXHJcbiAgICAgIC5qb2luKCc6Jyk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBCeWcgblx1MDBGOGdsZSBmcmEgZXZlbnRcclxuICAgKiBMXHUwMEU2c2VyIHZcdTAwRTZyZGllciBmcmEgZXZlbnRbaWRQcm9wZXJ0eV0gZWxsZXIgdWRsZWRlciBmcmEgZGVyaXZlZEZyb21cclxuICAgKiBGb3IgZG90LW5vdGF0aW9uLCByZXNvbHZlcyB2aWEgRW50aXR5UmVzb2x2ZXJcclxuICAgKi9cclxuICBidWlsZEtleUZyb21FdmVudChldmVudDogSUNhbGVuZGFyRXZlbnQpOiBzdHJpbmcge1xyXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1leHBsaWNpdC1hbnlcclxuICAgIGNvbnN0IGV2ZW50UmVjb3JkID0gZXZlbnQgYXMgYW55O1xyXG4gICAgcmV0dXJuIHRoaXMuZmllbGRzXHJcbiAgICAgIC5tYXAoZiA9PiB7XHJcbiAgICAgICAgLy8gQ2hlY2sgZm9yIGRvdC1ub3RhdGlvbiAoZS5nLiwgJ3Jlc291cmNlLnRlYW1JZCcpXHJcbiAgICAgICAgY29uc3QgZG90Tm90YXRpb24gPSB0aGlzLnBhcnNlRG90Tm90YXRpb24oZi5pZFByb3BlcnR5KTtcclxuICAgICAgICBpZiAoZG90Tm90YXRpb24pIHtcclxuICAgICAgICAgIHJldHVybiB0aGlzLnJlc29sdmVEb3ROb3RhdGlvbihldmVudFJlY29yZCwgZG90Tm90YXRpb24pO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgaWYgKGYuZGVyaXZlZEZyb20pIHtcclxuICAgICAgICAgIC8vIFVkbGVkIHZcdTAwRTZyZGkgKGYuZWtzLiBkYXRlIGZyYSBzdGFydClcclxuICAgICAgICAgIGNvbnN0IHNvdXJjZVZhbHVlID0gZXZlbnRSZWNvcmRbZi5kZXJpdmVkRnJvbV07XHJcbiAgICAgICAgICBpZiAoc291cmNlVmFsdWUgaW5zdGFuY2VvZiBEYXRlKSB7XHJcbiAgICAgICAgICAgIHJldHVybiB0aGlzLmRhdGVTZXJ2aWNlLmdldERhdGVLZXkoc291cmNlVmFsdWUpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgICAgcmV0dXJuIFN0cmluZyhzb3VyY2VWYWx1ZSB8fCAnJyk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHJldHVybiBTdHJpbmcoZXZlbnRSZWNvcmRbZi5pZFByb3BlcnR5XSB8fCAnJyk7XHJcbiAgICAgIH0pXHJcbiAgICAgIC5qb2luKCc6Jyk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBSZXNvbHZlIGRvdC1ub3RhdGlvbiByZWZlcmVuY2UgdmlhIEVudGl0eVJlc29sdmVyXHJcbiAgICovXHJcbiAgcHJpdmF0ZSByZXNvbHZlRG90Tm90YXRpb24oZXZlbnRSZWNvcmQ6IFJlY29yZDxzdHJpbmcsIHVua25vd24+LCBkb3ROb3RhdGlvbjogSURvdE5vdGF0aW9uKTogc3RyaW5nIHtcclxuICAgIGlmICghdGhpcy5lbnRpdHlSZXNvbHZlcikge1xyXG4gICAgICBjb25zb2xlLndhcm4oYEZpbHRlclRlbXBsYXRlOiBFbnRpdHlSZXNvbHZlciByZXF1aXJlZCBmb3IgZG90LW5vdGF0aW9uICcke2RvdE5vdGF0aW9uLmVudGl0eVR5cGV9LiR7ZG90Tm90YXRpb24ucHJvcGVydHl9J2ApO1xyXG4gICAgICByZXR1cm4gJyc7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gR2V0IGZvcmVpZ24ga2V5IHZhbHVlIGZyb20gZXZlbnQgKGUuZy4sIHJlc291cmNlSWQpXHJcbiAgICBjb25zdCBmb3JlaWduSWQgPSBldmVudFJlY29yZFtkb3ROb3RhdGlvbi5mb3JlaWduS2V5XTtcclxuICAgIGlmICghZm9yZWlnbklkKSByZXR1cm4gJyc7XHJcblxyXG4gICAgLy8gUmVzb2x2ZSBlbnRpdHlcclxuICAgIGNvbnN0IGVudGl0eSA9IHRoaXMuZW50aXR5UmVzb2x2ZXIucmVzb2x2ZShkb3ROb3RhdGlvbi5lbnRpdHlUeXBlLCBTdHJpbmcoZm9yZWlnbklkKSk7XHJcbiAgICBpZiAoIWVudGl0eSkgcmV0dXJuICcnO1xyXG5cclxuICAgIC8vIFJldHVybiBwcm9wZXJ0eSB2YWx1ZSBmcm9tIGVudGl0eVxyXG4gICAgcmV0dXJuIFN0cmluZyhlbnRpdHlbZG90Tm90YXRpb24ucHJvcGVydHldIHx8ICcnKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIE1hdGNoIGV2ZW50IG1vZCBrb2xvbm5lXHJcbiAgICovXHJcbiAgbWF0Y2hlcyhldmVudDogSUNhbGVuZGFyRXZlbnQsIGNvbHVtbjogSFRNTEVsZW1lbnQpOiBib29sZWFuIHtcclxuICAgIHJldHVybiB0aGlzLmJ1aWxkS2V5RnJvbUV2ZW50KGV2ZW50KSA9PT0gdGhpcy5idWlsZEtleUZyb21Db2x1bW4oY29sdW1uKTtcclxuICB9XHJcbn1cclxuIiwgImltcG9ydCB7IElSZW5kZXJlciwgSVJlbmRlckNvbnRleHQgfSBmcm9tICcuL0lHcm91cGluZ1JlbmRlcmVyJztcclxuaW1wb3J0IHsgYnVpbGRQaXBlbGluZSB9IGZyb20gJy4vUmVuZGVyQnVpbGRlcic7XHJcbmltcG9ydCB7IEV2ZW50UmVuZGVyZXIgfSBmcm9tICcuLi9mZWF0dXJlcy9ldmVudC9FdmVudFJlbmRlcmVyJztcclxuaW1wb3J0IHsgU2NoZWR1bGVSZW5kZXJlciB9IGZyb20gJy4uL2ZlYXR1cmVzL3NjaGVkdWxlL1NjaGVkdWxlUmVuZGVyZXInO1xyXG5pbXBvcnQgeyBIZWFkZXJEcmF3ZXJSZW5kZXJlciB9IGZyb20gJy4uL2ZlYXR1cmVzL2hlYWRlcmRyYXdlci9IZWFkZXJEcmF3ZXJSZW5kZXJlcic7XHJcbmltcG9ydCB7IFZpZXdDb25maWcsIEdyb3VwaW5nQ29uZmlnIH0gZnJvbSAnLi9WaWV3Q29uZmlnJztcclxuaW1wb3J0IHsgRmlsdGVyVGVtcGxhdGUgfSBmcm9tICcuL0ZpbHRlclRlbXBsYXRlJztcclxuaW1wb3J0IHsgRGF0ZVNlcnZpY2UgfSBmcm9tICcuL0RhdGVTZXJ2aWNlJztcclxuaW1wb3J0IHsgSUVudGl0eVNlcnZpY2UgfSBmcm9tICcuLi9zdG9yYWdlL0lFbnRpdHlTZXJ2aWNlJztcclxuaW1wb3J0IHsgSVN5bmMgfSBmcm9tICcuLi90eXBlcy9DYWxlbmRhclR5cGVzJztcclxuXHJcbmV4cG9ydCBjbGFzcyBDYWxlbmRhck9yY2hlc3RyYXRvciB7XHJcbiAgY29uc3RydWN0b3IoXHJcbiAgICBwcml2YXRlIGFsbFJlbmRlcmVyczogSVJlbmRlcmVyW10sXHJcbiAgICBwcml2YXRlIGV2ZW50UmVuZGVyZXI6IEV2ZW50UmVuZGVyZXIsXHJcbiAgICBwcml2YXRlIHNjaGVkdWxlUmVuZGVyZXI6IFNjaGVkdWxlUmVuZGVyZXIsXHJcbiAgICBwcml2YXRlIGhlYWRlckRyYXdlclJlbmRlcmVyOiBIZWFkZXJEcmF3ZXJSZW5kZXJlcixcclxuICAgIHByaXZhdGUgZGF0ZVNlcnZpY2U6IERhdGVTZXJ2aWNlLFxyXG4gICAgcHJpdmF0ZSBlbnRpdHlTZXJ2aWNlczogSUVudGl0eVNlcnZpY2U8SVN5bmM+W11cclxuICApIHt9XHJcblxyXG4gIGFzeW5jIHJlbmRlcih2aWV3Q29uZmlnOiBWaWV3Q29uZmlnLCBjb250YWluZXI6IEhUTUxFbGVtZW50KTogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICBjb25zdCBoZWFkZXJDb250YWluZXIgPSBjb250YWluZXIucXVlcnlTZWxlY3Rvcignc3dwLWNhbGVuZGFyLWhlYWRlcicpIGFzIEhUTUxFbGVtZW50O1xyXG4gICAgY29uc3QgY29sdW1uQ29udGFpbmVyID0gY29udGFpbmVyLnF1ZXJ5U2VsZWN0b3IoJ3N3cC1kYXktY29sdW1ucycpIGFzIEhUTUxFbGVtZW50O1xyXG4gICAgaWYgKCFoZWFkZXJDb250YWluZXIgfHwgIWNvbHVtbkNvbnRhaW5lcikge1xyXG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ01pc3Npbmcgc3dwLWNhbGVuZGFyLWhlYWRlciBvciBzd3AtZGF5LWNvbHVtbnMnKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBCeWcgZmlsdGVyIGZyYSB2aWV3Q29uZmlnXHJcbiAgICBjb25zdCBmaWx0ZXI6IFJlY29yZDxzdHJpbmcsIHN0cmluZ1tdPiA9IHt9O1xyXG4gICAgZm9yIChjb25zdCBncm91cGluZyBvZiB2aWV3Q29uZmlnLmdyb3VwaW5ncykge1xyXG4gICAgICBmaWx0ZXJbZ3JvdXBpbmcudHlwZV0gPSBncm91cGluZy52YWx1ZXM7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQnlnIEZpbHRlclRlbXBsYXRlIGZyYSB2aWV3Q29uZmlnIGdyb3VwaW5ncyAoa3VuIGRlIG1lZCBpZFByb3BlcnR5KVxyXG4gICAgY29uc3QgZmlsdGVyVGVtcGxhdGUgPSBuZXcgRmlsdGVyVGVtcGxhdGUodGhpcy5kYXRlU2VydmljZSk7XHJcbiAgICBmb3IgKGNvbnN0IGdyb3VwaW5nIG9mIHZpZXdDb25maWcuZ3JvdXBpbmdzKSB7XHJcbiAgICAgIGlmIChncm91cGluZy5pZFByb3BlcnR5KSB7XHJcbiAgICAgICAgZmlsdGVyVGVtcGxhdGUuYWRkRmllbGQoZ3JvdXBpbmcuaWRQcm9wZXJ0eSwgZ3JvdXBpbmcuZGVyaXZlZEZyb20pO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUmVzb2x2ZSBiZWxvbmdzVG8gcmVsYXRpb25zIChlLmcuLCB0ZWFtLnJlc291cmNlSWRzKVxyXG4gICAgY29uc3QgeyBwYXJlbnRDaGlsZE1hcCwgY2hpbGRUeXBlIH0gPSBhd2FpdCB0aGlzLnJlc29sdmVCZWxvbmdzVG8odmlld0NvbmZpZy5ncm91cGluZ3MsIGZpbHRlcik7XHJcblxyXG4gICAgY29uc3QgY29udGV4dDogSVJlbmRlckNvbnRleHQgPSB7IGhlYWRlckNvbnRhaW5lciwgY29sdW1uQ29udGFpbmVyLCBmaWx0ZXIsIGdyb3VwaW5nczogdmlld0NvbmZpZy5ncm91cGluZ3MsIHBhcmVudENoaWxkTWFwLCBjaGlsZFR5cGUgfTtcclxuXHJcbiAgICAvLyBDbGVhclxyXG4gICAgaGVhZGVyQ29udGFpbmVyLmlubmVySFRNTCA9ICcnO1xyXG4gICAgY29sdW1uQ29udGFpbmVyLmlubmVySFRNTCA9ICcnO1xyXG5cclxuICAgIC8vIFNcdTAwRTZ0IGRhdGEtbGV2ZWxzIGF0dHJpYnV0IGZvciBDU1MgZ3JpZC1yb3cgc3R5bGluZ1xyXG4gICAgY29uc3QgbGV2ZWxzID0gdmlld0NvbmZpZy5ncm91cGluZ3MubWFwKGcgPT4gZy50eXBlKS5qb2luKCcgJyk7XHJcbiAgICBoZWFkZXJDb250YWluZXIuZGF0YXNldC5sZXZlbHMgPSBsZXZlbHM7XHJcblxyXG4gICAgLy8gVlx1MDBFNmxnIHJlbmRlcmVycyBiYXNlcmV0IHBcdTAwRTUgZ3JvdXBpbmdzIHR5cGVzXHJcbiAgICBjb25zdCBhY3RpdmVSZW5kZXJlcnMgPSB0aGlzLnNlbGVjdFJlbmRlcmVycyh2aWV3Q29uZmlnKTtcclxuXHJcbiAgICAvLyBCeWcgb2cga1x1MDBGOHIgcGlwZWxpbmVcclxuICAgIGNvbnN0IHBpcGVsaW5lID0gYnVpbGRQaXBlbGluZShhY3RpdmVSZW5kZXJlcnMpO1xyXG4gICAgYXdhaXQgcGlwZWxpbmUucnVuKGNvbnRleHQpO1xyXG5cclxuICAgIC8vIFJlbmRlciBzY2hlZHVsZSB1bmF2YWlsYWJsZSB6b25lcyAoZlx1MDBGOHIgZXZlbnRzKVxyXG4gICAgYXdhaXQgdGhpcy5zY2hlZHVsZVJlbmRlcmVyLnJlbmRlcihjb250YWluZXIsIGZpbHRlcik7XHJcblxyXG4gICAgLy8gUmVuZGVyIHRpbWVkIGV2ZW50cyBpbiBncmlkIChtZWQgZmlsdGVyVGVtcGxhdGUgdGlsIG1hdGNoaW5nKVxyXG4gICAgYXdhaXQgdGhpcy5ldmVudFJlbmRlcmVyLnJlbmRlcihjb250YWluZXIsIGZpbHRlciwgZmlsdGVyVGVtcGxhdGUpO1xyXG5cclxuICAgIC8vIFJlbmRlciBhbGxEYXkgZXZlbnRzIGluIGhlYWRlciBkcmF3ZXIgKG1lZCBmaWx0ZXJUZW1wbGF0ZSB0aWwgbWF0Y2hpbmcpXHJcbiAgICBhd2FpdCB0aGlzLmhlYWRlckRyYXdlclJlbmRlcmVyLnJlbmRlcihjb250YWluZXIsIGZpbHRlciwgZmlsdGVyVGVtcGxhdGUpO1xyXG4gIH1cclxuXHJcbiAgcHJpdmF0ZSBzZWxlY3RSZW5kZXJlcnModmlld0NvbmZpZzogVmlld0NvbmZpZyk6IElSZW5kZXJlcltdIHtcclxuICAgIGNvbnN0IHR5cGVzID0gdmlld0NvbmZpZy5ncm91cGluZ3MubWFwKGcgPT4gZy50eXBlKTtcclxuICAgIC8vIFNvcnRcdTAwRTlyIHJlbmRlcmVycyBpIHNhbW1lIHJcdTAwRTZra2VmXHUwMEY4bGdlIHNvbSB2aWV3Q29uZmlnLmdyb3VwaW5nc1xyXG4gICAgcmV0dXJuIHR5cGVzXHJcbiAgICAgIC5tYXAodHlwZSA9PiB0aGlzLmFsbFJlbmRlcmVycy5maW5kKHIgPT4gci50eXBlID09PSB0eXBlKSlcclxuICAgICAgLmZpbHRlcigocik6IHIgaXMgSVJlbmRlcmVyID0+IHIgIT09IHVuZGVmaW5lZCk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBSZXNvbHZlIGJlbG9uZ3NUbyByZWxhdGlvbnMgdG8gYnVpbGQgcGFyZW50LWNoaWxkIG1hcFxyXG4gICAqIGUuZy4sIGJlbG9uZ3NUbzogJ3RlYW0ucmVzb3VyY2VJZHMnIFx1MjE5MiB7IHRlYW0xOiBbJ0VNUDAwMScsICdFTVAwMDInXSwgdGVhbTI6IFsuLi5dIH1cclxuICAgKiBBbHNvIHJldHVybnMgdGhlIGNoaWxkVHlwZSAodGhlIGdyb3VwaW5nIHR5cGUgdGhhdCBoYXMgYmVsb25nc1RvKVxyXG4gICAqL1xyXG4gIHByaXZhdGUgYXN5bmMgcmVzb2x2ZUJlbG9uZ3NUbyhcclxuICAgIGdyb3VwaW5nczogR3JvdXBpbmdDb25maWdbXSxcclxuICAgIGZpbHRlcjogUmVjb3JkPHN0cmluZywgc3RyaW5nW10+XHJcbiAgKTogUHJvbWlzZTx7IHBhcmVudENoaWxkTWFwPzogUmVjb3JkPHN0cmluZywgc3RyaW5nW10+OyBjaGlsZFR5cGU/OiBzdHJpbmcgfT4ge1xyXG4gICAgLy8gRmluZCBncm91cGluZyB3aXRoIGJlbG9uZ3NUb1xyXG4gICAgY29uc3QgY2hpbGRHcm91cGluZyA9IGdyb3VwaW5ncy5maW5kKGcgPT4gZy5iZWxvbmdzVG8pO1xyXG4gICAgaWYgKCFjaGlsZEdyb3VwaW5nPy5iZWxvbmdzVG8pIHJldHVybiB7fTtcclxuXHJcbiAgICAvLyBQYXJzZSBiZWxvbmdzVG86ICd0ZWFtLnJlc291cmNlSWRzJ1xyXG4gICAgY29uc3QgW2VudGl0eVR5cGUsIHByb3BlcnR5XSA9IGNoaWxkR3JvdXBpbmcuYmVsb25nc1RvLnNwbGl0KCcuJyk7XHJcbiAgICBpZiAoIWVudGl0eVR5cGUgfHwgIXByb3BlcnR5KSByZXR1cm4ge307XHJcblxyXG4gICAgLy8gR2V0IHBhcmVudCBJRHMgZnJvbSBmaWx0ZXJcclxuICAgIGNvbnN0IHBhcmVudElkcyA9IGZpbHRlcltlbnRpdHlUeXBlXSB8fCBbXTtcclxuICAgIGlmIChwYXJlbnRJZHMubGVuZ3RoID09PSAwKSByZXR1cm4ge307XHJcblxyXG4gICAgLy8gRmluZCBzZXJ2aWNlIGR5bmFtaXNrIGJhc2VyZXQgcFx1MDBFNSBlbnRpdHlUeXBlIChpbmdlbiBoYXJkY29kZWQgdHlwZSBjaGVjaylcclxuICAgIGNvbnN0IHNlcnZpY2UgPSB0aGlzLmVudGl0eVNlcnZpY2VzLmZpbmQocyA9PlxyXG4gICAgICBzLmVudGl0eVR5cGUudG9Mb3dlckNhc2UoKSA9PT0gZW50aXR5VHlwZVxyXG4gICAgKTtcclxuICAgIGlmICghc2VydmljZSkgcmV0dXJuIHt9O1xyXG5cclxuICAgIC8vIEhlbnQgYWxsZSBlbnRpdGllcyBvZyBmaWx0cmVyIHBcdTAwRTUgcGFyZW50SWRzXHJcbiAgICBjb25zdCBhbGxFbnRpdGllcyA9IGF3YWl0IHNlcnZpY2UuZ2V0QWxsKCk7XHJcbiAgICBjb25zdCBlbnRpdGllcyA9IGFsbEVudGl0aWVzLmZpbHRlcihlID0+XHJcbiAgICAgIHBhcmVudElkcy5pbmNsdWRlcygoZSBhcyB1bmtub3duIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+KS5pZCBhcyBzdHJpbmcpXHJcbiAgICApO1xyXG5cclxuICAgIC8vIEJ5ZyBwYXJlbnQtY2hpbGQgbWFwXHJcbiAgICBjb25zdCBtYXA6IFJlY29yZDxzdHJpbmcsIHN0cmluZ1tdPiA9IHt9O1xyXG4gICAgZm9yIChjb25zdCBlbnRpdHkgb2YgZW50aXRpZXMpIHtcclxuICAgICAgY29uc3QgZW50aXR5UmVjb3JkID0gZW50aXR5IGFzIHVua25vd24gYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj47XHJcbiAgICAgIGNvbnN0IGNoaWxkcmVuID0gKGVudGl0eVJlY29yZFtwcm9wZXJ0eV0gYXMgc3RyaW5nW10pIHx8IFtdO1xyXG4gICAgICBtYXBbZW50aXR5UmVjb3JkLmlkIGFzIHN0cmluZ10gPSBjaGlsZHJlbjtcclxuICAgIH1cclxuXHJcbiAgICByZXR1cm4geyBwYXJlbnRDaGlsZE1hcDogbWFwLCBjaGlsZFR5cGU6IGNoaWxkR3JvdXBpbmcudHlwZSB9O1xyXG4gIH1cclxufVxyXG4iLCAiZXhwb3J0IGNsYXNzIE5hdmlnYXRpb25BbmltYXRvciB7XHJcbiAgY29uc3RydWN0b3IoXHJcbiAgICBwcml2YXRlIGhlYWRlclRyYWNrOiBIVE1MRWxlbWVudCxcclxuICAgIHByaXZhdGUgY29udGVudFRyYWNrOiBIVE1MRWxlbWVudFxyXG4gICkge31cclxuXHJcbiAgYXN5bmMgc2xpZGUoZGlyZWN0aW9uOiAnbGVmdCcgfCAncmlnaHQnLCByZW5kZXJGbjogKCkgPT4gUHJvbWlzZTx2b2lkPik6IFByb21pc2U8dm9pZD4ge1xyXG4gICAgY29uc3Qgb3V0ID0gZGlyZWN0aW9uID09PSAnbGVmdCcgPyAnLTEwMCUnIDogJzEwMCUnO1xyXG4gICAgY29uc3QgaW50byA9IGRpcmVjdGlvbiA9PT0gJ2xlZnQnID8gJzEwMCUnIDogJy0xMDAlJztcclxuXHJcbiAgICBhd2FpdCB0aGlzLmFuaW1hdGVPdXQob3V0KTtcclxuICAgIGF3YWl0IHJlbmRlckZuKCk7XHJcbiAgICBhd2FpdCB0aGlzLmFuaW1hdGVJbihpbnRvKTtcclxuICB9XHJcblxyXG4gIHByaXZhdGUgYXN5bmMgYW5pbWF0ZU91dCh0cmFuc2xhdGU6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xyXG4gICAgYXdhaXQgUHJvbWlzZS5hbGwoW1xyXG4gICAgICB0aGlzLmhlYWRlclRyYWNrLmFuaW1hdGUoXHJcbiAgICAgICAgW3sgdHJhbnNmb3JtOiAndHJhbnNsYXRlWCgwKScgfSwgeyB0cmFuc2Zvcm06IGB0cmFuc2xhdGVYKCR7dHJhbnNsYXRlfSlgIH1dLFxyXG4gICAgICAgIHsgZHVyYXRpb246IDIwMCwgZWFzaW5nOiAnZWFzZS1pbicgfVxyXG4gICAgICApLmZpbmlzaGVkLFxyXG4gICAgICB0aGlzLmNvbnRlbnRUcmFjay5hbmltYXRlKFxyXG4gICAgICAgIFt7IHRyYW5zZm9ybTogJ3RyYW5zbGF0ZVgoMCknIH0sIHsgdHJhbnNmb3JtOiBgdHJhbnNsYXRlWCgke3RyYW5zbGF0ZX0pYCB9XSxcclxuICAgICAgICB7IGR1cmF0aW9uOiAyMDAsIGVhc2luZzogJ2Vhc2UtaW4nIH1cclxuICAgICAgKS5maW5pc2hlZFxyXG4gICAgXSk7XHJcbiAgfVxyXG5cclxuICBwcml2YXRlIGFzeW5jIGFuaW1hdGVJbih0cmFuc2xhdGU6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xyXG4gICAgYXdhaXQgUHJvbWlzZS5hbGwoW1xyXG4gICAgICB0aGlzLmhlYWRlclRyYWNrLmFuaW1hdGUoXHJcbiAgICAgICAgW3sgdHJhbnNmb3JtOiBgdHJhbnNsYXRlWCgke3RyYW5zbGF0ZX0pYCB9LCB7IHRyYW5zZm9ybTogJ3RyYW5zbGF0ZVgoMCknIH1dLFxyXG4gICAgICAgIHsgZHVyYXRpb246IDIwMCwgZWFzaW5nOiAnZWFzZS1vdXQnIH1cclxuICAgICAgKS5maW5pc2hlZCxcclxuICAgICAgdGhpcy5jb250ZW50VHJhY2suYW5pbWF0ZShcclxuICAgICAgICBbeyB0cmFuc2Zvcm06IGB0cmFuc2xhdGVYKCR7dHJhbnNsYXRlfSlgIH0sIHsgdHJhbnNmb3JtOiAndHJhbnNsYXRlWCgwKScgfV0sXHJcbiAgICAgICAgeyBkdXJhdGlvbjogMjAwLCBlYXNpbmc6ICdlYXNlLW91dCcgfVxyXG4gICAgICApLmZpbmlzaGVkXHJcbiAgICBdKTtcclxuICB9XHJcbn1cclxuIiwgImltcG9ydCB7IElSZW5kZXJlciwgSVJlbmRlckNvbnRleHQgfSBmcm9tICcuLi8uLi9jb3JlL0lHcm91cGluZ1JlbmRlcmVyJztcclxuaW1wb3J0IHsgRGF0ZVNlcnZpY2UgfSBmcm9tICcuLi8uLi9jb3JlL0RhdGVTZXJ2aWNlJztcclxuXHJcbmV4cG9ydCBjbGFzcyBEYXRlUmVuZGVyZXIgaW1wbGVtZW50cyBJUmVuZGVyZXIge1xyXG4gIHJlYWRvbmx5IHR5cGUgPSAnZGF0ZSc7XHJcblxyXG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgZGF0ZVNlcnZpY2U6IERhdGVTZXJ2aWNlKSB7fVxyXG5cclxuICByZW5kZXIoY29udGV4dDogSVJlbmRlckNvbnRleHQpOiB2b2lkIHtcclxuICAgIGNvbnN0IGRhdGVzID0gY29udGV4dC5maWx0ZXJbJ2RhdGUnXSB8fCBbXTtcclxuICAgIGNvbnN0IHJlc291cmNlSWRzID0gY29udGV4dC5maWx0ZXJbJ3Jlc291cmNlJ10gfHwgW107XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgZGF0ZSBoZWFkZXJzIHNob3VsZCBiZSBoaWRkZW4gKGUuZy4sIGluIGRheSB2aWV3KVxyXG4gICAgY29uc3QgZGF0ZUdyb3VwaW5nID0gY29udGV4dC5ncm91cGluZ3M/LmZpbmQoZyA9PiBnLnR5cGUgPT09ICdkYXRlJyk7XHJcbiAgICBjb25zdCBoaWRlSGVhZGVyID0gZGF0ZUdyb3VwaW5nPy5oaWRlSGVhZGVyID09PSB0cnVlO1xyXG5cclxuICAgIC8vIFJlbmRlciBkYXRlcyBmb3IgSFZFUiByZXNvdXJjZSAoZWxsZXIgMSBnYW5nIGh2aXMgaW5nZW4gcmVzb3VyY2VzKVxyXG4gICAgY29uc3QgaXRlcmF0aW9ucyA9IHJlc291cmNlSWRzLmxlbmd0aCB8fCAxO1xyXG4gICAgbGV0IGNvbHVtbkNvdW50ID0gMDtcclxuXHJcbiAgICBmb3IgKGxldCByID0gMDsgciA8IGl0ZXJhdGlvbnM7IHIrKykge1xyXG4gICAgICBjb25zdCByZXNvdXJjZUlkID0gcmVzb3VyY2VJZHNbcl07IC8vIHVuZGVmaW5lZCBodmlzIGluZ2VuIHJlc291cmNlc1xyXG5cclxuICAgICAgZm9yIChjb25zdCBkYXRlU3RyIG9mIGRhdGVzKSB7XHJcbiAgICAgICAgY29uc3QgZGF0ZSA9IHRoaXMuZGF0ZVNlcnZpY2UucGFyc2VJU08oZGF0ZVN0cik7XHJcblxyXG4gICAgICAgIC8vIEJ1aWxkIGNvbHVtbktleSBmb3IgdW5pZm9ybSBpZGVudGlmaWNhdGlvblxyXG4gICAgICAgIGNvbnN0IHNlZ21lbnRzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0geyBkYXRlOiBkYXRlU3RyIH07XHJcbiAgICAgICAgaWYgKHJlc291cmNlSWQpIHNlZ21lbnRzLnJlc291cmNlID0gcmVzb3VyY2VJZDtcclxuICAgICAgICBjb25zdCBjb2x1bW5LZXkgPSB0aGlzLmRhdGVTZXJ2aWNlLmJ1aWxkQ29sdW1uS2V5KHNlZ21lbnRzKTtcclxuXHJcbiAgICAgICAgLy8gSGVhZGVyXHJcbiAgICAgICAgY29uc3QgaGVhZGVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3dwLWRheS1oZWFkZXInKTtcclxuICAgICAgICBoZWFkZXIuZGF0YXNldC5kYXRlID0gZGF0ZVN0cjtcclxuICAgICAgICBoZWFkZXIuZGF0YXNldC5jb2x1bW5LZXkgPSBjb2x1bW5LZXk7XHJcbiAgICAgICAgaWYgKHJlc291cmNlSWQpIHtcclxuICAgICAgICAgIGhlYWRlci5kYXRhc2V0LnJlc291cmNlSWQgPSByZXNvdXJjZUlkO1xyXG4gICAgICAgIH1cclxuICAgICAgICBpZiAoaGlkZUhlYWRlcikge1xyXG4gICAgICAgICAgaGVhZGVyLmRhdGFzZXQuaGlkZGVuID0gJ3RydWUnO1xyXG4gICAgICAgIH1cclxuICAgICAgICBoZWFkZXIuaW5uZXJIVE1MID0gYFxyXG4gICAgICAgICAgPHN3cC1kYXktbmFtZT4ke3RoaXMuZGF0ZVNlcnZpY2UuZ2V0RGF5TmFtZShkYXRlLCAnc2hvcnQnKX08L3N3cC1kYXktbmFtZT5cclxuICAgICAgICAgIDxzd3AtZGF5LWRhdGU+JHtkYXRlLmdldERhdGUoKX08L3N3cC1kYXktZGF0ZT5cclxuICAgICAgICBgO1xyXG4gICAgICAgIGNvbnRleHQuaGVhZGVyQ29udGFpbmVyLmFwcGVuZENoaWxkKGhlYWRlcik7XHJcblxyXG4gICAgICAgIC8vIENvbHVtblxyXG4gICAgICAgIGNvbnN0IGNvbHVtbiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3N3cC1kYXktY29sdW1uJyk7XHJcbiAgICAgICAgY29sdW1uLmRhdGFzZXQuZGF0ZSA9IGRhdGVTdHI7XHJcbiAgICAgICAgY29sdW1uLmRhdGFzZXQuY29sdW1uS2V5ID0gY29sdW1uS2V5O1xyXG4gICAgICAgIGlmIChyZXNvdXJjZUlkKSB7XHJcbiAgICAgICAgICBjb2x1bW4uZGF0YXNldC5yZXNvdXJjZUlkID0gcmVzb3VyY2VJZDtcclxuICAgICAgICB9XHJcbiAgICAgICAgY29sdW1uLmlubmVySFRNTCA9ICc8c3dwLWV2ZW50cy1sYXllcj48L3N3cC1ldmVudHMtbGF5ZXI+JztcclxuICAgICAgICBjb250ZXh0LmNvbHVtbkNvbnRhaW5lci5hcHBlbmRDaGlsZChjb2x1bW4pO1xyXG5cclxuICAgICAgICBjb2x1bW5Db3VudCsrO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gU2V0IGdyaWQgY29sdW1ucyBvbiBjb250YWluZXJcclxuICAgIGNvbnN0IGNvbnRhaW5lciA9IGNvbnRleHQuY29sdW1uQ29udGFpbmVyLmNsb3Nlc3QoJ3N3cC1jYWxlbmRhci1jb250YWluZXInKTtcclxuICAgIGlmIChjb250YWluZXIpIHtcclxuICAgICAgKGNvbnRhaW5lciBhcyBIVE1MRWxlbWVudCkuc3R5bGUuc2V0UHJvcGVydHkoJy0tZ3JpZC1jb2x1bW5zJywgU3RyaW5nKGNvbHVtbkNvdW50KSk7XHJcbiAgICB9XHJcbiAgfVxyXG59XHJcbiIsICJpbXBvcnQgZGF5anMgZnJvbSAnZGF5anMnO1xyXG5pbXBvcnQgdXRjIGZyb20gJ2RheWpzL3BsdWdpbi91dGMnO1xyXG5pbXBvcnQgdGltZXpvbmUgZnJvbSAnZGF5anMvcGx1Z2luL3RpbWV6b25lJztcclxuaW1wb3J0IGlzb1dlZWsgZnJvbSAnZGF5anMvcGx1Z2luL2lzb1dlZWsnO1xyXG5pbXBvcnQgeyBJVGltZUZvcm1hdENvbmZpZyB9IGZyb20gJy4vSVRpbWVGb3JtYXRDb25maWcnO1xyXG5cclxuLy8gRW5hYmxlIGRheWpzIHBsdWdpbnNcclxuZGF5anMuZXh0ZW5kKHV0Yyk7XHJcbmRheWpzLmV4dGVuZCh0aW1lem9uZSk7XHJcbmRheWpzLmV4dGVuZChpc29XZWVrKTtcclxuXHJcbmV4cG9ydCBjbGFzcyBEYXRlU2VydmljZSB7XHJcbiAgcHJpdmF0ZSB0aW1lem9uZTogc3RyaW5nO1xyXG4gIHByaXZhdGUgYmFzZURhdGU6IGRheWpzLkRheWpzO1xyXG5cclxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIGNvbmZpZzogSVRpbWVGb3JtYXRDb25maWcsIGJhc2VEYXRlPzogRGF0ZSkge1xyXG4gICAgdGhpcy50aW1lem9uZSA9IGNvbmZpZy50aW1lem9uZTtcclxuICAgIC8vIEFsbG93IHNldHRpbmcgYSBmaXhlZCBiYXNlIGRhdGUgZm9yIGRlbW8vdGVzdGluZyBwdXJwb3Nlc1xyXG4gICAgdGhpcy5iYXNlRGF0ZSA9IGJhc2VEYXRlID8gZGF5anMoYmFzZURhdGUpIDogZGF5anMoKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFNldCBhIGZpeGVkIGJhc2UgZGF0ZSAodXNlZnVsIGZvciBkZW1vcyB3aXRoIHN0YXRpYyBtb2NrIGRhdGEpXHJcbiAgICovXHJcbiAgc2V0QmFzZURhdGUoZGF0ZTogRGF0ZSk6IHZvaWQge1xyXG4gICAgdGhpcy5iYXNlRGF0ZSA9IGRheWpzKGRhdGUpO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogR2V0IHRoZSBjdXJyZW50IGJhc2UgZGF0ZSAoZWl0aGVyIGZpeGVkIG9yIHRvZGF5KVxyXG4gICAqL1xyXG4gIGdldEJhc2VEYXRlKCk6IERhdGUge1xyXG4gICAgcmV0dXJuIHRoaXMuYmFzZURhdGUudG9EYXRlKCk7XHJcbiAgfVxyXG5cclxuICBwYXJzZUlTTyhpc29TdHJpbmc6IHN0cmluZyk6IERhdGUge1xyXG4gICAgcmV0dXJuIGRheWpzKGlzb1N0cmluZykudG9EYXRlKCk7XHJcbiAgfVxyXG5cclxuICBnZXREYXlOYW1lKGRhdGU6IERhdGUsIGZvcm1hdDogJ3Nob3J0JyB8ICdsb25nJyA9ICdzaG9ydCcpOiBzdHJpbmcge1xyXG4gICAgcmV0dXJuIG5ldyBJbnRsLkRhdGVUaW1lRm9ybWF0KHRoaXMuY29uZmlnLmxvY2FsZSwgeyB3ZWVrZGF5OiBmb3JtYXQgfSkuZm9ybWF0KGRhdGUpO1xyXG4gIH1cclxuXHJcbiAgZ2V0V2Vla0RhdGVzKG9mZnNldCA9IDAsIGRheXMgPSA3KTogc3RyaW5nW10ge1xyXG4gICAgY29uc3QgbW9uZGF5ID0gdGhpcy5iYXNlRGF0ZS5zdGFydE9mKCd3ZWVrJykuYWRkKDEsICdkYXknKS5hZGQob2Zmc2V0LCAnd2VlaycpO1xyXG4gICAgcmV0dXJuIEFycmF5LmZyb20oeyBsZW5ndGg6IGRheXMgfSwgKF8sIGkpID0+XHJcbiAgICAgIG1vbmRheS5hZGQoaSwgJ2RheScpLmZvcm1hdCgnWVlZWS1NTS1ERCcpXHJcbiAgICApO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogR2V0IGRhdGVzIGZvciBzcGVjaWZpYyB3ZWVrZGF5cyB3aXRoaW4gYSB3ZWVrXHJcbiAgICogQHBhcmFtIG9mZnNldCAtIFdlZWsgb2Zmc2V0IGZyb20gYmFzZSBkYXRlICgwID0gY3VycmVudCB3ZWVrKVxyXG4gICAqIEBwYXJhbSB3b3JrRGF5cyAtIEFycmF5IG9mIElTTyB3ZWVrZGF5IG51bWJlcnMgKDE9TW9uZGF5LCA3PVN1bmRheSlcclxuICAgKiBAcmV0dXJucyBBcnJheSBvZiBkYXRlIHN0cmluZ3MgaW4gWVlZWS1NTS1ERCBmb3JtYXRcclxuICAgKi9cclxuICBnZXRXb3JrV2Vla0RhdGVzKG9mZnNldDogbnVtYmVyLCB3b3JrRGF5czogbnVtYmVyW10pOiBzdHJpbmdbXSB7XHJcbiAgICBjb25zdCBtb25kYXkgPSB0aGlzLmJhc2VEYXRlLnN0YXJ0T2YoJ3dlZWsnKS5hZGQoMSwgJ2RheScpLmFkZChvZmZzZXQsICd3ZWVrJyk7XHJcbiAgICByZXR1cm4gd29ya0RheXMubWFwKGlzb0RheSA9PiB7XHJcbiAgICAgIC8vIElTTzogMT1Nb25kYXksIDc9U3VuZGF5IFx1MjE5MiBkYXlzIGZyb20gTW9uZGF5OiAwLTZcclxuICAgICAgY29uc3QgZGF5c0Zyb21Nb25kYXkgPSBpc29EYXkgPT09IDcgPyA2IDogaXNvRGF5IC0gMTtcclxuICAgICAgcmV0dXJuIG1vbmRheS5hZGQoZGF5c0Zyb21Nb25kYXksICdkYXknKS5mb3JtYXQoJ1lZWVktTU0tREQnKTtcclxuICAgIH0pO1xyXG4gIH1cclxuXHJcbiAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cclxuICAvLyBGT1JNQVRUSU5HXHJcbiAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cclxuXHJcbiAgZm9ybWF0VGltZShkYXRlOiBEYXRlLCBzaG93U2Vjb25kcyA9IGZhbHNlKTogc3RyaW5nIHtcclxuICAgIGNvbnN0IHBhdHRlcm4gPSBzaG93U2Vjb25kcyA/ICdISDptbTpzcycgOiAnSEg6bW0nO1xyXG4gICAgcmV0dXJuIGRheWpzKGRhdGUpLmZvcm1hdChwYXR0ZXJuKTtcclxuICB9XHJcblxyXG4gIGZvcm1hdFRpbWVSYW5nZShzdGFydDogRGF0ZSwgZW5kOiBEYXRlKTogc3RyaW5nIHtcclxuICAgIHJldHVybiBgJHt0aGlzLmZvcm1hdFRpbWUoc3RhcnQpfSAtICR7dGhpcy5mb3JtYXRUaW1lKGVuZCl9YDtcclxuICB9XHJcblxyXG4gIGZvcm1hdERhdGUoZGF0ZTogRGF0ZSk6IHN0cmluZyB7XHJcbiAgICByZXR1cm4gZGF5anMoZGF0ZSkuZm9ybWF0KCdZWVlZLU1NLUREJyk7XHJcbiAgfVxyXG5cclxuICBnZXREYXRlS2V5KGRhdGU6IERhdGUpOiBzdHJpbmcge1xyXG4gICAgcmV0dXJuIHRoaXMuZm9ybWF0RGF0ZShkYXRlKTtcclxuICB9XHJcblxyXG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XHJcbiAgLy8gQ09MVU1OIEtFWVxyXG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XHJcblxyXG4gIC8qKlxyXG4gICAqIEJ1aWxkIGEgdW5pZm9ybSBjb2x1bW5LZXkgZnJvbSBncm91cGluZyBzZWdtZW50c1xyXG4gICAqIEhhbmRsZXMgYW55IGNvbWJpbmF0aW9uIG9mIGRhdGUsIHJlc291cmNlLCB0ZWFtLCBldGMuXHJcbiAgICpcclxuICAgKiBAZXhhbXBsZVxyXG4gICAqIGJ1aWxkQ29sdW1uS2V5KHsgZGF0ZTogJzIwMjUtMTItMDknIH0pIFx1MjE5MiBcIjIwMjUtMTItMDlcIlxyXG4gICAqIGJ1aWxkQ29sdW1uS2V5KHsgZGF0ZTogJzIwMjUtMTItMDknLCByZXNvdXJjZTogJ0VNUDAwMScgfSkgXHUyMTkyIFwiMjAyNS0xMi0wOTpFTVAwMDFcIlxyXG4gICAqL1xyXG4gIGJ1aWxkQ29sdW1uS2V5KHNlZ21lbnRzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+KTogc3RyaW5nIHtcclxuICAgIC8vIEFsd2F5cyBwdXQgZGF0ZSBmaXJzdCBpZiBwcmVzZW50LCB0aGVuIG90aGVyIHNlZ21lbnRzIGFscGhhYmV0aWNhbGx5XHJcbiAgICBjb25zdCBkYXRlID0gc2VnbWVudHMuZGF0ZTtcclxuICAgIGNvbnN0IG90aGVycyA9IE9iamVjdC5lbnRyaWVzKHNlZ21lbnRzKVxyXG4gICAgICAuZmlsdGVyKChba10pID0+IGsgIT09ICdkYXRlJylcclxuICAgICAgLnNvcnQoKFthXSwgW2JdKSA9PiBhLmxvY2FsZUNvbXBhcmUoYikpXHJcbiAgICAgIC5tYXAoKFssIHZdKSA9PiB2KTtcclxuXHJcbiAgICByZXR1cm4gZGF0ZSA/IFtkYXRlLCAuLi5vdGhlcnNdLmpvaW4oJzonKSA6IG90aGVycy5qb2luKCc6Jyk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBQYXJzZSBhIGNvbHVtbktleSBiYWNrIGludG8gc2VnbWVudHNcclxuICAgKiBBc3N1bWVzIGZvcm1hdDogXCJkYXRlOnJlc291cmNlOi4uLlwiIG9yIGp1c3QgXCJkYXRlXCJcclxuICAgKi9cclxuICBwYXJzZUNvbHVtbktleShjb2x1bW5LZXk6IHN0cmluZyk6IHsgZGF0ZTogc3RyaW5nOyByZXNvdXJjZT86IHN0cmluZyB9IHtcclxuICAgIGNvbnN0IHBhcnRzID0gY29sdW1uS2V5LnNwbGl0KCc6Jyk7XHJcbiAgICByZXR1cm4ge1xyXG4gICAgICBkYXRlOiBwYXJ0c1swXSxcclxuICAgICAgcmVzb3VyY2U6IHBhcnRzWzFdXHJcbiAgICB9O1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogRXh0cmFjdCBkYXRlS2V5IGZyb20gY29sdW1uS2V5IChmaXJzdCBzZWdtZW50KVxyXG4gICAqL1xyXG4gIGdldERhdGVGcm9tQ29sdW1uS2V5KGNvbHVtbktleTogc3RyaW5nKTogc3RyaW5nIHtcclxuICAgIHJldHVybiBjb2x1bW5LZXkuc3BsaXQoJzonKVswXTtcclxuICB9XHJcblxyXG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XHJcbiAgLy8gVElNRSBDQUxDVUxBVElPTlNcclxuICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxyXG5cclxuICB0aW1lVG9NaW51dGVzKHRpbWVTdHJpbmc6IHN0cmluZyk6IG51bWJlciB7XHJcbiAgICBjb25zdCBwYXJ0cyA9IHRpbWVTdHJpbmcuc3BsaXQoJzonKS5tYXAoTnVtYmVyKTtcclxuICAgIGNvbnN0IGhvdXJzID0gcGFydHNbMF0gfHwgMDtcclxuICAgIGNvbnN0IG1pbnV0ZXMgPSBwYXJ0c1sxXSB8fCAwO1xyXG4gICAgcmV0dXJuIGhvdXJzICogNjAgKyBtaW51dGVzO1xyXG4gIH1cclxuXHJcbiAgbWludXRlc1RvVGltZSh0b3RhbE1pbnV0ZXM6IG51bWJlcik6IHN0cmluZyB7XHJcbiAgICBjb25zdCBob3VycyA9IE1hdGguZmxvb3IodG90YWxNaW51dGVzIC8gNjApO1xyXG4gICAgY29uc3QgbWludXRlcyA9IHRvdGFsTWludXRlcyAlIDYwO1xyXG4gICAgcmV0dXJuIGRheWpzKCkuaG91cihob3VycykubWludXRlKG1pbnV0ZXMpLmZvcm1hdCgnSEg6bW0nKTtcclxuICB9XHJcblxyXG4gIGdldE1pbnV0ZXNTaW5jZU1pZG5pZ2h0KGRhdGU6IERhdGUpOiBudW1iZXIge1xyXG4gICAgY29uc3QgZCA9IGRheWpzKGRhdGUpO1xyXG4gICAgcmV0dXJuIGQuaG91cigpICogNjAgKyBkLm1pbnV0ZSgpO1xyXG4gIH1cclxuXHJcbiAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cclxuICAvLyBVVEMgQ09OVkVSU0lPTlNcclxuICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxyXG5cclxuICB0b1VUQyhsb2NhbERhdGU6IERhdGUpOiBzdHJpbmcge1xyXG4gICAgcmV0dXJuIGRheWpzLnR6KGxvY2FsRGF0ZSwgdGhpcy50aW1lem9uZSkudXRjKCkudG9JU09TdHJpbmcoKTtcclxuICB9XHJcblxyXG4gIGZyb21VVEModXRjU3RyaW5nOiBzdHJpbmcpOiBEYXRlIHtcclxuICAgIHJldHVybiBkYXlqcy51dGModXRjU3RyaW5nKS50eih0aGlzLnRpbWV6b25lKS50b0RhdGUoKTtcclxuICB9XHJcblxyXG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XHJcbiAgLy8gREFURSBDUkVBVElPTlxyXG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XHJcblxyXG4gIGNyZWF0ZURhdGVBdFRpbWUoYmFzZURhdGU6IERhdGUgfCBzdHJpbmcsIHRpbWVTdHJpbmc6IHN0cmluZyk6IERhdGUge1xyXG4gICAgY29uc3QgdG90YWxNaW51dGVzID0gdGhpcy50aW1lVG9NaW51dGVzKHRpbWVTdHJpbmcpO1xyXG4gICAgY29uc3QgaG91cnMgPSBNYXRoLmZsb29yKHRvdGFsTWludXRlcyAvIDYwKTtcclxuICAgIGNvbnN0IG1pbnV0ZXMgPSB0b3RhbE1pbnV0ZXMgJSA2MDtcclxuICAgIHJldHVybiBkYXlqcyhiYXNlRGF0ZSkuc3RhcnRPZignZGF5JykuaG91cihob3VycykubWludXRlKG1pbnV0ZXMpLnRvRGF0ZSgpO1xyXG4gIH1cclxuXHJcbiAgZ2V0SVNPV2Vla0RheShkYXRlOiBEYXRlIHwgc3RyaW5nKTogbnVtYmVyIHtcclxuICAgIHJldHVybiBkYXlqcyhkYXRlKS5pc29XZWVrZGF5KCk7ICAvLyAxPU1vbmRheSwgNz1TdW5kYXlcclxuICB9XHJcbn1cclxuIiwgIi8qKlxuICogUG9zaXRpb25VdGlscyAtIFBpeGVsL3Bvc2l0aW9uIGNhbGN1bGF0aW9ucyBmb3IgY2FsZW5kYXIgZ3JpZFxuICpcbiAqIFJFU1BPTlNJQklMSVRZOiBDb252ZXJ0IGJldHdlZW4gdGltZSBhbmQgcGl4ZWwgcG9zaXRpb25zXG4gKiBOT1RFOiBEYXRlIGZvcm1hdHRpbmcgYmVsb25ncyBpbiBEYXRlU2VydmljZSwgbm90IGhlcmVcbiAqL1xuXG5pbXBvcnQgeyBJR3JpZENvbmZpZyB9IGZyb20gJy4uL2NvcmUvSUdyaWRDb25maWcnO1xuXG5leHBvcnQgaW50ZXJmYWNlIEV2ZW50UG9zaXRpb24ge1xuICB0b3A6IG51bWJlcjsgICAgLy8gcGl4ZWxzIGZyb20gZGF5IHN0YXJ0XG4gIGhlaWdodDogbnVtYmVyOyAvLyBwaXhlbHNcbn1cblxuLyoqXG4gKiBDYWxjdWxhdGUgcGl4ZWwgcG9zaXRpb24gZm9yIGFuIGV2ZW50IGJhc2VkIG9uIGl0cyB0aW1lc1xuICovXG5leHBvcnQgZnVuY3Rpb24gY2FsY3VsYXRlRXZlbnRQb3NpdGlvbihcbiAgc3RhcnQ6IERhdGUsXG4gIGVuZDogRGF0ZSxcbiAgY29uZmlnOiBJR3JpZENvbmZpZ1xuKTogRXZlbnRQb3NpdGlvbiB7XG4gIGNvbnN0IHN0YXJ0TWludXRlcyA9IHN0YXJ0LmdldEhvdXJzKCkgKiA2MCArIHN0YXJ0LmdldE1pbnV0ZXMoKTtcbiAgY29uc3QgZW5kTWludXRlcyA9IGVuZC5nZXRIb3VycygpICogNjAgKyBlbmQuZ2V0TWludXRlcygpO1xuXG4gIGNvbnN0IGRheVN0YXJ0TWludXRlcyA9IGNvbmZpZy5kYXlTdGFydEhvdXIgKiA2MDtcbiAgY29uc3QgbWludXRlSGVpZ2h0ID0gY29uZmlnLmhvdXJIZWlnaHQgLyA2MDtcblxuICBjb25zdCB0b3AgPSAoc3RhcnRNaW51dGVzIC0gZGF5U3RhcnRNaW51dGVzKSAqIG1pbnV0ZUhlaWdodDtcbiAgY29uc3QgaGVpZ2h0ID0gKGVuZE1pbnV0ZXMgLSBzdGFydE1pbnV0ZXMpICogbWludXRlSGVpZ2h0O1xuXG4gIHJldHVybiB7IHRvcCwgaGVpZ2h0IH07XG59XG5cbi8qKlxuICogQ29udmVydCBtaW51dGVzIHRvIHBpeGVsc1xuICovXG5leHBvcnQgZnVuY3Rpb24gbWludXRlc1RvUGl4ZWxzKG1pbnV0ZXM6IG51bWJlciwgY29uZmlnOiBJR3JpZENvbmZpZyk6IG51bWJlciB7XG4gIHJldHVybiAobWludXRlcyAvIDYwKSAqIGNvbmZpZy5ob3VySGVpZ2h0O1xufVxuXG4vKipcbiAqIENvbnZlcnQgcGl4ZWxzIHRvIG1pbnV0ZXNcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHBpeGVsc1RvTWludXRlcyhwaXhlbHM6IG51bWJlciwgY29uZmlnOiBJR3JpZENvbmZpZyk6IG51bWJlciB7XG4gIHJldHVybiAocGl4ZWxzIC8gY29uZmlnLmhvdXJIZWlnaHQpICogNjA7XG59XG5cbi8qKlxuICogU25hcCBwaXhlbCBwb3NpdGlvbiB0byBncmlkIGludGVydmFsXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzbmFwVG9HcmlkKHBpeGVsczogbnVtYmVyLCBjb25maWc6IElHcmlkQ29uZmlnKTogbnVtYmVyIHtcbiAgY29uc3Qgc25hcFBpeGVscyA9IG1pbnV0ZXNUb1BpeGVscyhjb25maWcuc25hcEludGVydmFsLCBjb25maWcpO1xuICByZXR1cm4gTWF0aC5yb3VuZChwaXhlbHMgLyBzbmFwUGl4ZWxzKSAqIHNuYXBQaXhlbHM7XG59XG4iLCAiLyoqXG4gKiBDb3JlRXZlbnRzIC0gQ29uc29saWRhdGVkIGVzc2VudGlhbCBldmVudHMgZm9yIHRoZSBjYWxlbmRhciBWMlxuICovXG5leHBvcnQgY29uc3QgQ29yZUV2ZW50cyA9IHtcbiAgLy8gTGlmZWN5Y2xlIGV2ZW50c1xuICBJTklUSUFMSVpFRDogJ2NvcmU6aW5pdGlhbGl6ZWQnLFxuICBSRUFEWTogJ2NvcmU6cmVhZHknLFxuICBERVNUUk9ZRUQ6ICdjb3JlOmRlc3Ryb3llZCcsXG5cbiAgLy8gVmlldyBldmVudHNcbiAgVklFV19DSEFOR0VEOiAndmlldzpjaGFuZ2VkJyxcbiAgVklFV19SRU5ERVJFRDogJ3ZpZXc6cmVuZGVyZWQnLFxuXG4gIC8vIE5hdmlnYXRpb24gZXZlbnRzXG4gIERBVEVfQ0hBTkdFRDogJ25hdjpkYXRlLWNoYW5nZWQnLFxuICBOQVZJR0FUSU9OX0NPTVBMRVRFRDogJ25hdjpuYXZpZ2F0aW9uLWNvbXBsZXRlZCcsXG5cbiAgLy8gRGF0YSBldmVudHNcbiAgREFUQV9MT0FESU5HOiAnZGF0YTpsb2FkaW5nJyxcbiAgREFUQV9MT0FERUQ6ICdkYXRhOmxvYWRlZCcsXG4gIERBVEFfRVJST1I6ICdkYXRhOmVycm9yJyxcblxuICAvLyBHcmlkIGV2ZW50c1xuICBHUklEX1JFTkRFUkVEOiAnZ3JpZDpyZW5kZXJlZCcsXG4gIEdSSURfQ0xJQ0tFRDogJ2dyaWQ6Y2xpY2tlZCcsXG5cbiAgLy8gRXZlbnQgbWFuYWdlbWVudFxuICBFVkVOVF9DUkVBVEVEOiAnZXZlbnQ6Y3JlYXRlZCcsXG4gIEVWRU5UX1VQREFURUQ6ICdldmVudDp1cGRhdGVkJyxcbiAgRVZFTlRfREVMRVRFRDogJ2V2ZW50OmRlbGV0ZWQnLFxuICBFVkVOVF9TRUxFQ1RFRDogJ2V2ZW50OnNlbGVjdGVkJyxcblxuICAvLyBFdmVudCBkcmFnLWRyb3BcbiAgRVZFTlRfRFJBR19TVEFSVDogJ2V2ZW50OmRyYWctc3RhcnQnLFxuICBFVkVOVF9EUkFHX01PVkU6ICdldmVudDpkcmFnLW1vdmUnLFxuICBFVkVOVF9EUkFHX0VORDogJ2V2ZW50OmRyYWctZW5kJyxcbiAgRVZFTlRfRFJBR19DQU5DRUw6ICdldmVudDpkcmFnLWNhbmNlbCcsXG4gIEVWRU5UX0RSQUdfQ09MVU1OX0NIQU5HRTogJ2V2ZW50OmRyYWctY29sdW1uLWNoYW5nZScsXG5cbiAgLy8gSGVhZGVyIGRyYWcgKHRpbWVkIFx1MjE5MiBoZWFkZXIgY29udmVyc2lvbilcbiAgRVZFTlRfRFJBR19FTlRFUl9IRUFERVI6ICdldmVudDpkcmFnLWVudGVyLWhlYWRlcicsXG4gIEVWRU5UX0RSQUdfTU9WRV9IRUFERVI6ICdldmVudDpkcmFnLW1vdmUtaGVhZGVyJyxcbiAgRVZFTlRfRFJBR19MRUFWRV9IRUFERVI6ICdldmVudDpkcmFnLWxlYXZlLWhlYWRlcicsXG5cbiAgLy8gRXZlbnQgcmVzaXplXG4gIEVWRU5UX1JFU0laRV9TVEFSVDogJ2V2ZW50OnJlc2l6ZS1zdGFydCcsXG4gIEVWRU5UX1JFU0laRV9FTkQ6ICdldmVudDpyZXNpemUtZW5kJyxcblxuICAvLyBFZGdlIHNjcm9sbFxuICBFREdFX1NDUk9MTF9USUNLOiAnZWRnZS1zY3JvbGw6dGljaycsXG4gIEVER0VfU0NST0xMX1NUQVJURUQ6ICdlZGdlLXNjcm9sbDpzdGFydGVkJyxcbiAgRURHRV9TQ1JPTExfU1RPUFBFRDogJ2VkZ2Utc2Nyb2xsOnN0b3BwZWQnLFxuXG4gIC8vIFN5c3RlbSBldmVudHNcbiAgRVJST1I6ICdzeXN0ZW06ZXJyb3InLFxuXG4gIC8vIFN5bmMgZXZlbnRzXG4gIFNZTkNfU1RBUlRFRDogJ3N5bmM6c3RhcnRlZCcsXG4gIFNZTkNfQ09NUExFVEVEOiAnc3luYzpjb21wbGV0ZWQnLFxuICBTWU5DX0ZBSUxFRDogJ3N5bmM6ZmFpbGVkJyxcblxuICAvLyBFbnRpdHkgZXZlbnRzIC0gZm9yIGF1ZGl0IGFuZCBzeW5jXG4gIEVOVElUWV9TQVZFRDogJ2VudGl0eTpzYXZlZCcsXG4gIEVOVElUWV9ERUxFVEVEOiAnZW50aXR5OmRlbGV0ZWQnLFxuXG4gIC8vIEF1ZGl0IGV2ZW50c1xuICBBVURJVF9MT0dHRUQ6ICdhdWRpdDpsb2dnZWQnLFxuXG4gIC8vIFJlbmRlcmluZyBldmVudHNcbiAgRVZFTlRTX1JFTkRFUkVEOiAnZXZlbnRzOnJlbmRlcmVkJ1xufSBhcyBjb25zdDtcbiIsICIvKipcclxuICogRXZlbnRMYXlvdXRFbmdpbmUgLSBTaW1wbGlmaWVkIHN0YWNraW5nL2dyb3VwaW5nIGFsZ29yaXRobSBmb3IgVjJcclxuICpcclxuICogU3VwcG9ydHMgdHdvIGxheW91dCBtb2RlczpcclxuICogLSBHUklEOiBFdmVudHMgc3RhcnRpbmcgYXQgc2FtZSB0aW1lIHJlbmRlcmVkIHNpZGUtYnktc2lkZVxyXG4gKiAtIFNUQUNLSU5HOiBPdmVybGFwcGluZyBldmVudHMgd2l0aCBtYXJnaW4tbGVmdCBvZmZzZXQgKDE1cHggcGVyIGxldmVsKVxyXG4gKlxyXG4gKiBTaW1wbGlmaWVkIGZyb20gVjE6IE5vIHByZXYvbmV4dCBjaGFpbnMsIHNpbmdsZS1wYXNzIGdyZWVkeSBhbGdvcml0aG1cclxuICovXHJcblxyXG5pbXBvcnQgeyBJQ2FsZW5kYXJFdmVudCB9IGZyb20gJy4uLy4uL3R5cGVzL0NhbGVuZGFyVHlwZXMnO1xyXG5pbXBvcnQgeyBJR3JpZENvbmZpZyB9IGZyb20gJy4uLy4uL2NvcmUvSUdyaWRDb25maWcnO1xyXG5pbXBvcnQgeyBjYWxjdWxhdGVFdmVudFBvc2l0aW9uIH0gZnJvbSAnLi4vLi4vdXRpbHMvUG9zaXRpb25VdGlscyc7XHJcbmltcG9ydCB7IElDb2x1bW5MYXlvdXQsIElHcmlkR3JvdXBMYXlvdXQsIElTdGFja2VkRXZlbnRMYXlvdXQgfSBmcm9tICcuL0V2ZW50TGF5b3V0VHlwZXMnO1xyXG5cclxuLyoqXHJcbiAqIENoZWNrIGlmIHR3byBldmVudHMgb3ZlcmxhcCAoc3RyaWN0IC0gdG91Y2hpbmcgYXQgYm91bmRhcnkgPSBOT1Qgb3ZlcmxhcHBpbmcpXHJcbiAqIFRoaXMgbWF0Y2hlcyBTY2VuYXJpbyA4OiBlbmQ9PT1zdGFydCBpcyBOT1Qgb3ZlcmxhcFxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGV2ZW50c092ZXJsYXAoYTogSUNhbGVuZGFyRXZlbnQsIGI6IElDYWxlbmRhckV2ZW50KTogYm9vbGVhbiB7XHJcbiAgcmV0dXJuIGEuc3RhcnQgPCBiLmVuZCAmJiBhLmVuZCA+IGIuc3RhcnQ7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDaGVjayBpZiB0d28gZXZlbnRzIGFyZSB3aXRoaW4gdGhyZXNob2xkIGZvciBncmlkIGdyb3VwaW5nLlxyXG4gKiBUaGlzIGluY2x1ZGVzOlxyXG4gKiAxLiBTdGFydC10by1zdGFydDogRXZlbnRzIHN0YXJ0IHdpdGhpbiB0aHJlc2hvbGQgb2YgZWFjaCBvdGhlclxyXG4gKiAyLiBFbmQtdG8tc3RhcnQ6IE9uZSBldmVudCBzdGFydHMgd2l0aGluIHRocmVzaG9sZCBiZWZvcmUgYW5vdGhlciBlbmRzXHJcbiAqL1xyXG5mdW5jdGlvbiBldmVudHNXaXRoaW5UaHJlc2hvbGQoYTogSUNhbGVuZGFyRXZlbnQsIGI6IElDYWxlbmRhckV2ZW50LCB0aHJlc2hvbGRNaW51dGVzOiBudW1iZXIpOiBib29sZWFuIHtcclxuICBjb25zdCB0aHJlc2hvbGRNcyA9IHRocmVzaG9sZE1pbnV0ZXMgKiA2MCAqIDEwMDA7XHJcblxyXG4gIC8vIFN0YXJ0LXRvLXN0YXJ0OiBib3RoIGV2ZW50cyBzdGFydCB3aXRoaW4gdGhyZXNob2xkXHJcbiAgY29uc3Qgc3RhcnRUb1N0YXJ0RGlmZiA9IE1hdGguYWJzKGEuc3RhcnQuZ2V0VGltZSgpIC0gYi5zdGFydC5nZXRUaW1lKCkpO1xyXG4gIGlmIChzdGFydFRvU3RhcnREaWZmIDw9IHRocmVzaG9sZE1zKSByZXR1cm4gdHJ1ZTtcclxuXHJcbiAgLy8gRW5kLXRvLXN0YXJ0OiBvbmUgZXZlbnQgc3RhcnRzIHdpdGhpbiB0aHJlc2hvbGQgYmVmb3JlIHRoZSBvdGhlciBlbmRzXHJcbiAgLy8gQiBzdGFydHMgd2l0aGluIHRocmVzaG9sZCBiZWZvcmUgQSBlbmRzXHJcbiAgY29uc3QgYlN0YXJ0c0JlZm9yZUFFbmRzID0gYS5lbmQuZ2V0VGltZSgpIC0gYi5zdGFydC5nZXRUaW1lKCk7XHJcbiAgaWYgKGJTdGFydHNCZWZvcmVBRW5kcyA+IDAgJiYgYlN0YXJ0c0JlZm9yZUFFbmRzIDw9IHRocmVzaG9sZE1zKSByZXR1cm4gdHJ1ZTtcclxuXHJcbiAgLy8gQSBzdGFydHMgd2l0aGluIHRocmVzaG9sZCBiZWZvcmUgQiBlbmRzXHJcbiAgY29uc3QgYVN0YXJ0c0JlZm9yZUJFbmRzID0gYi5lbmQuZ2V0VGltZSgpIC0gYS5zdGFydC5nZXRUaW1lKCk7XHJcbiAgaWYgKGFTdGFydHNCZWZvcmVCRW5kcyA+IDAgJiYgYVN0YXJ0c0JlZm9yZUJFbmRzIDw9IHRocmVzaG9sZE1zKSByZXR1cm4gdHJ1ZTtcclxuXHJcbiAgcmV0dXJuIGZhbHNlO1xyXG59XHJcblxyXG4vKipcclxuICogQ2hlY2sgaWYgYWxsIGV2ZW50cyBpbiBhIGdyb3VwIHN0YXJ0IHdpdGhpbiB0aHJlc2hvbGQgb2YgZWFjaCBvdGhlclxyXG4gKi9cclxuZnVuY3Rpb24gYWxsU3RhcnRXaXRoaW5UaHJlc2hvbGQoZXZlbnRzOiBJQ2FsZW5kYXJFdmVudFtdLCB0aHJlc2hvbGRNaW51dGVzOiBudW1iZXIpOiBib29sZWFuIHtcclxuICBpZiAoZXZlbnRzLmxlbmd0aCA8PSAxKSByZXR1cm4gdHJ1ZTtcclxuXHJcbiAgLy8gRmluZCBlYXJsaWVzdCBhbmQgbGF0ZXN0IHN0YXJ0IHRpbWVzXHJcbiAgbGV0IGVhcmxpZXN0ID0gZXZlbnRzWzBdLnN0YXJ0LmdldFRpbWUoKTtcclxuICBsZXQgbGF0ZXN0ID0gZXZlbnRzWzBdLnN0YXJ0LmdldFRpbWUoKTtcclxuXHJcbiAgZm9yIChjb25zdCBldmVudCBvZiBldmVudHMpIHtcclxuICAgIGNvbnN0IHRpbWUgPSBldmVudC5zdGFydC5nZXRUaW1lKCk7XHJcbiAgICBpZiAodGltZSA8IGVhcmxpZXN0KSBlYXJsaWVzdCA9IHRpbWU7XHJcbiAgICBpZiAodGltZSA+IGxhdGVzdCkgbGF0ZXN0ID0gdGltZTtcclxuICB9XHJcblxyXG4gIGNvbnN0IGRpZmZNaW51dGVzID0gKGxhdGVzdCAtIGVhcmxpZXN0KSAvICgxMDAwICogNjApO1xyXG4gIHJldHVybiBkaWZmTWludXRlcyA8PSB0aHJlc2hvbGRNaW51dGVzO1xyXG59XHJcblxyXG4vKipcclxuICogRmluZCBncm91cHMgb2Ygb3ZlcmxhcHBpbmcgZXZlbnRzIChjb25uZWN0ZWQgYnkgb3ZlcmxhcCBjaGFpbilcclxuICogRXZlbnRzIGFyZSBncm91cGVkIGlmIHRoZXkgb3ZlcmxhcCB3aXRoIGFueSBldmVudCBpbiB0aGUgZ3JvdXBcclxuICovXHJcbmZ1bmN0aW9uIGZpbmRPdmVybGFwR3JvdXBzKGV2ZW50czogSUNhbGVuZGFyRXZlbnRbXSk6IElDYWxlbmRhckV2ZW50W11bXSB7XHJcbiAgaWYgKGV2ZW50cy5sZW5ndGggPT09IDApIHJldHVybiBbXTtcclxuXHJcbiAgY29uc3Qgc29ydGVkID0gWy4uLmV2ZW50c10uc29ydCgoYSwgYikgPT4gYS5zdGFydC5nZXRUaW1lKCkgLSBiLnN0YXJ0LmdldFRpbWUoKSk7XHJcbiAgY29uc3QgdXNlZCA9IG5ldyBTZXQ8c3RyaW5nPigpO1xyXG4gIGNvbnN0IGdyb3VwczogSUNhbGVuZGFyRXZlbnRbXVtdID0gW107XHJcblxyXG4gIGZvciAoY29uc3QgZXZlbnQgb2Ygc29ydGVkKSB7XHJcbiAgICBpZiAodXNlZC5oYXMoZXZlbnQuaWQpKSBjb250aW51ZTtcclxuXHJcbiAgICAvLyBTdGFydCBhIG5ldyBncm91cCB3aXRoIHRoaXMgZXZlbnRcclxuICAgIGNvbnN0IGdyb3VwOiBJQ2FsZW5kYXJFdmVudFtdID0gW2V2ZW50XTtcclxuICAgIHVzZWQuYWRkKGV2ZW50LmlkKTtcclxuXHJcbiAgICAvLyBFeHBhbmQgZ3JvdXAgYnkgZmluZGluZyBhbGwgY29ubmVjdGVkIGV2ZW50cyAodmlhIG92ZXJsYXApXHJcbiAgICBsZXQgZXhwYW5kZWQgPSB0cnVlO1xyXG4gICAgd2hpbGUgKGV4cGFuZGVkKSB7XHJcbiAgICAgIGV4cGFuZGVkID0gZmFsc2U7XHJcbiAgICAgIGZvciAoY29uc3QgY2FuZGlkYXRlIG9mIHNvcnRlZCkge1xyXG4gICAgICAgIGlmICh1c2VkLmhhcyhjYW5kaWRhdGUuaWQpKSBjb250aW51ZTtcclxuXHJcbiAgICAgICAgLy8gQ2hlY2sgaWYgY2FuZGlkYXRlIG92ZXJsYXBzIHdpdGggYW55IGV2ZW50IGluIGdyb3VwXHJcbiAgICAgICAgY29uc3QgY29ubmVjdHMgPSBncm91cC5zb21lKG1lbWJlciA9PiBldmVudHNPdmVybGFwKG1lbWJlciwgY2FuZGlkYXRlKSk7XHJcblxyXG4gICAgICAgIGlmIChjb25uZWN0cykge1xyXG4gICAgICAgICAgZ3JvdXAucHVzaChjYW5kaWRhdGUpO1xyXG4gICAgICAgICAgdXNlZC5hZGQoY2FuZGlkYXRlLmlkKTtcclxuICAgICAgICAgIGV4cGFuZGVkID0gdHJ1ZTtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICBncm91cHMucHVzaChncm91cCk7XHJcbiAgfVxyXG5cclxuICByZXR1cm4gZ3JvdXBzO1xyXG59XHJcblxyXG4vKipcclxuICogRmluZCBncmlkIGNhbmRpZGF0ZXMgd2l0aGluIGEgZ3JvdXAgLSBldmVudHMgY29ubmVjdGVkIHZpYSB0aHJlc2hvbGQgY2hhaW5cclxuICogVXNlcyBWMSBsb2dpYzogZXZlbnRzIGFyZSBjb25uZWN0ZWQgaWYgd2l0aGluIHRocmVzaG9sZCAobm8gb3ZlcmxhcCByZXF1aXJlbWVudClcclxuICovXHJcbmZ1bmN0aW9uIGZpbmRHcmlkQ2FuZGlkYXRlcyhcclxuICBldmVudHM6IElDYWxlbmRhckV2ZW50W10sXHJcbiAgdGhyZXNob2xkTWludXRlczogbnVtYmVyXHJcbik6IElDYWxlbmRhckV2ZW50W11bXSB7XHJcbiAgaWYgKGV2ZW50cy5sZW5ndGggPT09IDApIHJldHVybiBbXTtcclxuXHJcbiAgY29uc3Qgc29ydGVkID0gWy4uLmV2ZW50c10uc29ydCgoYSwgYikgPT4gYS5zdGFydC5nZXRUaW1lKCkgLSBiLnN0YXJ0LmdldFRpbWUoKSk7XHJcbiAgY29uc3QgdXNlZCA9IG5ldyBTZXQ8c3RyaW5nPigpO1xyXG4gIGNvbnN0IGdyb3VwczogSUNhbGVuZGFyRXZlbnRbXVtdID0gW107XHJcblxyXG4gIGZvciAoY29uc3QgZXZlbnQgb2Ygc29ydGVkKSB7XHJcbiAgICBpZiAodXNlZC5oYXMoZXZlbnQuaWQpKSBjb250aW51ZTtcclxuXHJcbiAgICBjb25zdCBncm91cDogSUNhbGVuZGFyRXZlbnRbXSA9IFtldmVudF07XHJcbiAgICB1c2VkLmFkZChldmVudC5pZCk7XHJcblxyXG4gICAgLy8gRXhwYW5kIGJ5IHRocmVzaG9sZCBjaGFpbiAoVjEgbG9naWM6IG5vIG92ZXJsYXAgcmVxdWlyZW1lbnQsIGp1c3QgdGhyZXNob2xkKVxyXG4gICAgbGV0IGV4cGFuZGVkID0gdHJ1ZTtcclxuICAgIHdoaWxlIChleHBhbmRlZCkge1xyXG4gICAgICBleHBhbmRlZCA9IGZhbHNlO1xyXG4gICAgICBmb3IgKGNvbnN0IGNhbmRpZGF0ZSBvZiBzb3J0ZWQpIHtcclxuICAgICAgICBpZiAodXNlZC5oYXMoY2FuZGlkYXRlLmlkKSkgY29udGludWU7XHJcblxyXG4gICAgICAgIGNvbnN0IGNvbm5lY3RzID0gZ3JvdXAuc29tZShtZW1iZXIgPT5cclxuICAgICAgICAgIGV2ZW50c1dpdGhpblRocmVzaG9sZChtZW1iZXIsIGNhbmRpZGF0ZSwgdGhyZXNob2xkTWludXRlcylcclxuICAgICAgICApO1xyXG5cclxuICAgICAgICBpZiAoY29ubmVjdHMpIHtcclxuICAgICAgICAgIGdyb3VwLnB1c2goY2FuZGlkYXRlKTtcclxuICAgICAgICAgIHVzZWQuYWRkKGNhbmRpZGF0ZS5pZCk7XHJcbiAgICAgICAgICBleHBhbmRlZCA9IHRydWU7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgZ3JvdXBzLnB1c2goZ3JvdXApO1xyXG4gIH1cclxuXHJcbiAgcmV0dXJuIGdyb3VwcztcclxufVxyXG5cclxuLyoqXHJcbiAqIENhbGN1bGF0ZSBzdGFjayBsZXZlbHMgZm9yIG92ZXJsYXBwaW5nIGV2ZW50cyB1c2luZyBncmVlZHkgYWxnb3JpdGhtXHJcbiAqIEZvciBlYWNoIGV2ZW50OiBsZXZlbCA9IG1heChvdmVybGFwcGluZyBhbHJlYWR5LXByb2Nlc3NlZCBldmVudHMpICsgMVxyXG4gKi9cclxuZnVuY3Rpb24gY2FsY3VsYXRlU3RhY2tMZXZlbHMoZXZlbnRzOiBJQ2FsZW5kYXJFdmVudFtdKTogTWFwPHN0cmluZywgbnVtYmVyPiB7XHJcbiAgY29uc3QgbGV2ZWxzID0gbmV3IE1hcDxzdHJpbmcsIG51bWJlcj4oKTtcclxuICBjb25zdCBzb3J0ZWQgPSBbLi4uZXZlbnRzXS5zb3J0KChhLCBiKSA9PiBhLnN0YXJ0LmdldFRpbWUoKSAtIGIuc3RhcnQuZ2V0VGltZSgpKTtcclxuXHJcbiAgZm9yIChjb25zdCBldmVudCBvZiBzb3J0ZWQpIHtcclxuICAgIGxldCBtYXhPdmVybGFwcGluZ0xldmVsID0gLTE7XHJcblxyXG4gICAgLy8gRmluZCBtYXggbGV2ZWwgYW1vbmcgb3ZlcmxhcHBpbmcgZXZlbnRzIGFscmVhZHkgcHJvY2Vzc2VkXHJcbiAgICBmb3IgKGNvbnN0IFtpZCwgbGV2ZWxdIG9mIGxldmVscykge1xyXG4gICAgICBjb25zdCBvdGhlciA9IGV2ZW50cy5maW5kKGUgPT4gZS5pZCA9PT0gaWQpO1xyXG4gICAgICBpZiAob3RoZXIgJiYgZXZlbnRzT3ZlcmxhcChldmVudCwgb3RoZXIpKSB7XHJcbiAgICAgICAgbWF4T3ZlcmxhcHBpbmdMZXZlbCA9IE1hdGgubWF4KG1heE92ZXJsYXBwaW5nTGV2ZWwsIGxldmVsKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIGxldmVscy5zZXQoZXZlbnQuaWQsIG1heE92ZXJsYXBwaW5nTGV2ZWwgKyAxKTtcclxuICB9XHJcblxyXG4gIHJldHVybiBsZXZlbHM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBbGxvY2F0ZSBldmVudHMgdG8gY29sdW1ucyBmb3IgR1JJRCBsYXlvdXQgdXNpbmcgZ3JlZWR5IGFsZ29yaXRobVxyXG4gKiBOb24tb3ZlcmxhcHBpbmcgZXZlbnRzIGNhbiBzaGFyZSBhIGNvbHVtbiB0byBtaW5pbWl6ZSB0b3RhbCBjb2x1bW5zXHJcbiAqL1xyXG5mdW5jdGlvbiBhbGxvY2F0ZUNvbHVtbnMoZXZlbnRzOiBJQ2FsZW5kYXJFdmVudFtdKTogSUNhbGVuZGFyRXZlbnRbXVtdIHtcclxuICBjb25zdCBzb3J0ZWQgPSBbLi4uZXZlbnRzXS5zb3J0KChhLCBiKSA9PiBhLnN0YXJ0LmdldFRpbWUoKSAtIGIuc3RhcnQuZ2V0VGltZSgpKTtcclxuICBjb25zdCBjb2x1bW5zOiBJQ2FsZW5kYXJFdmVudFtdW10gPSBbXTtcclxuXHJcbiAgZm9yIChjb25zdCBldmVudCBvZiBzb3J0ZWQpIHtcclxuICAgIC8vIEZpbmQgZmlyc3QgY29sdW1uIHdoZXJlIGV2ZW50IGRvZXNuJ3Qgb3ZlcmxhcCB3aXRoIGV4aXN0aW5nIGV2ZW50c1xyXG4gICAgbGV0IHBsYWNlZCA9IGZhbHNlO1xyXG4gICAgZm9yIChjb25zdCBjb2x1bW4gb2YgY29sdW1ucykge1xyXG4gICAgICBjb25zdCBjYW5GaXQgPSAhY29sdW1uLnNvbWUoZSA9PiBldmVudHNPdmVybGFwKGV2ZW50LCBlKSk7XHJcbiAgICAgIGlmIChjYW5GaXQpIHtcclxuICAgICAgICBjb2x1bW4ucHVzaChldmVudCk7XHJcbiAgICAgICAgcGxhY2VkID0gdHJ1ZTtcclxuICAgICAgICBicmVhaztcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIE5vIHN1aXRhYmxlIGNvbHVtbiBmb3VuZCwgY3JlYXRlIG5ldyBvbmVcclxuICAgIGlmICghcGxhY2VkKSB7XHJcbiAgICAgIGNvbHVtbnMucHVzaChbZXZlbnRdKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIHJldHVybiBjb2x1bW5zO1xyXG59XHJcblxyXG4vKipcclxuICogTWFpbiBlbnRyeSBwb2ludDogQ2FsY3VsYXRlIGNvbXBsZXRlIGxheW91dCBmb3IgYSBjb2x1bW4ncyBldmVudHNcclxuICpcclxuICogQWxnb3JpdGhtOlxyXG4gKiAxLiBGaW5kIG92ZXJsYXAgZ3JvdXBzIChldmVudHMgY29ubmVjdGVkIGJ5IG92ZXJsYXAgY2hhaW4pXHJcbiAqIDIuIEZvciBlYWNoIG92ZXJsYXAgZ3JvdXAsIGZpbmQgZ3JpZCBjYW5kaWRhdGVzIChldmVudHMgd2l0aGluIHRocmVzaG9sZCBjaGFpbilcclxuICogMy4gSWYgYWxsIGV2ZW50cyBpbiBvdmVybGFwIGdyb3VwIGZvcm0gYSBzaW5nbGUgZ3JpZCBjYW5kaWRhdGUgXHUyMTkyIEdSSUQgbW9kZVxyXG4gKiA0LiBPdGhlcndpc2UgXHUyMTkyIFNUQUNLSU5HIG1vZGUgd2l0aCBjYWxjdWxhdGVkIGxldmVsc1xyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGNhbGN1bGF0ZUNvbHVtbkxheW91dChcclxuICBldmVudHM6IElDYWxlbmRhckV2ZW50W10sXHJcbiAgY29uZmlnOiBJR3JpZENvbmZpZ1xyXG4pOiBJQ29sdW1uTGF5b3V0IHtcclxuICBjb25zdCB0aHJlc2hvbGRNaW51dGVzID0gY29uZmlnLmdyaWRTdGFydFRocmVzaG9sZE1pbnV0ZXMgPz8gMTA7XHJcblxyXG4gIGNvbnN0IHJlc3VsdDogSUNvbHVtbkxheW91dCA9IHtcclxuICAgIGdyaWRzOiBbXSxcclxuICAgIHN0YWNrZWQ6IFtdXHJcbiAgfTtcclxuXHJcbiAgaWYgKGV2ZW50cy5sZW5ndGggPT09IDApIHJldHVybiByZXN1bHQ7XHJcblxyXG4gIC8vIEZpbmQgYWxsIG92ZXJsYXBwaW5nIGV2ZW50IGdyb3Vwc1xyXG4gIGNvbnN0IG92ZXJsYXBHcm91cHMgPSBmaW5kT3ZlcmxhcEdyb3VwcyhldmVudHMpO1xyXG5cclxuICBmb3IgKGNvbnN0IG92ZXJsYXBHcm91cCBvZiBvdmVybGFwR3JvdXBzKSB7XHJcbiAgICBpZiAob3ZlcmxhcEdyb3VwLmxlbmd0aCA9PT0gMSkge1xyXG4gICAgICAvLyBTaW5nbGUgZXZlbnQgLSBubyBncm91cGluZyBuZWVkZWRcclxuICAgICAgcmVzdWx0LnN0YWNrZWQucHVzaCh7XHJcbiAgICAgICAgZXZlbnQ6IG92ZXJsYXBHcm91cFswXSxcclxuICAgICAgICBzdGFja0xldmVsOiAwXHJcbiAgICAgIH0pO1xyXG4gICAgICBjb250aW51ZTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBXaXRoaW4gdGhpcyBvdmVybGFwIGdyb3VwLCBmaW5kIGdyaWQgY2FuZGlkYXRlcyAodGhyZXNob2xkLWNvbm5lY3RlZCBzdWJncm91cHMpXHJcbiAgICBjb25zdCBncmlkU3ViZ3JvdXBzID0gZmluZEdyaWRDYW5kaWRhdGVzKG92ZXJsYXBHcm91cCwgdGhyZXNob2xkTWludXRlcyk7XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgdGhlIEVOVElSRSBvdmVybGFwIGdyb3VwIGZvcm1zIGEgc2luZ2xlIGdyaWQgY2FuZGlkYXRlXHJcbiAgICAvLyBUaGlzIGhhcHBlbnMgd2hlbiBhbGwgZXZlbnRzIGFyZSBjb25uZWN0ZWQgdmlhIHRocmVzaG9sZCBjaGFpblxyXG4gICAgY29uc3QgbGFyZ2VzdEdyaWRDYW5kaWRhdGUgPSBncmlkU3ViZ3JvdXBzLnJlZHVjZSgobWF4LCBnKSA9PlxyXG4gICAgICBnLmxlbmd0aCA+IG1heC5sZW5ndGggPyBnIDogbWF4LCBncmlkU3ViZ3JvdXBzWzBdKTtcclxuXHJcbiAgICBpZiAobGFyZ2VzdEdyaWRDYW5kaWRhdGUubGVuZ3RoID09PSBvdmVybGFwR3JvdXAubGVuZ3RoKSB7XHJcbiAgICAgIC8vIEFsbCBldmVudHMgaW4gb3ZlcmxhcCBncm91cCBhcmUgY29ubmVjdGVkIHZpYSB0aHJlc2hvbGQgY2hhaW4gXHUyMTkyIEdSSUQgbW9kZVxyXG4gICAgICBjb25zdCBjb2x1bW5zID0gYWxsb2NhdGVDb2x1bW5zKG92ZXJsYXBHcm91cCk7XHJcbiAgICAgIGNvbnN0IGVhcmxpZXN0ID0gb3ZlcmxhcEdyb3VwLnJlZHVjZSgobWluLCBlKSA9PlxyXG4gICAgICAgIGUuc3RhcnQgPCBtaW4uc3RhcnQgPyBlIDogbWluLCBvdmVybGFwR3JvdXBbMF0pO1xyXG4gICAgICBjb25zdCBwb3NpdGlvbiA9IGNhbGN1bGF0ZUV2ZW50UG9zaXRpb24oZWFybGllc3Quc3RhcnQsIGVhcmxpZXN0LmVuZCwgY29uZmlnKTtcclxuXHJcbiAgICAgIHJlc3VsdC5ncmlkcy5wdXNoKHtcclxuICAgICAgICBldmVudHM6IG92ZXJsYXBHcm91cCxcclxuICAgICAgICBjb2x1bW5zLFxyXG4gICAgICAgIHN0YWNrTGV2ZWw6IDAsXHJcbiAgICAgICAgcG9zaXRpb246IHsgdG9wOiBwb3NpdGlvbi50b3AgfVxyXG4gICAgICB9KTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIC8vIE5vdCBhbGwgZXZlbnRzIGNvbm5lY3RlZCB2aWEgdGhyZXNob2xkIFx1MjE5MiBTVEFDS0lORyBtb2RlXHJcbiAgICAgIGNvbnN0IGxldmVscyA9IGNhbGN1bGF0ZVN0YWNrTGV2ZWxzKG92ZXJsYXBHcm91cCk7XHJcbiAgICAgIGZvciAoY29uc3QgZXZlbnQgb2Ygb3ZlcmxhcEdyb3VwKSB7XHJcbiAgICAgICAgcmVzdWx0LnN0YWNrZWQucHVzaCh7XHJcbiAgICAgICAgICBldmVudCxcclxuICAgICAgICAgIHN0YWNrTGV2ZWw6IGxldmVscy5nZXQoZXZlbnQuaWQpID8/IDBcclxuICAgICAgICB9KTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgcmV0dXJuIHJlc3VsdDtcclxufVxyXG4iLCAiaW1wb3J0IHsgSUNhbGVuZGFyRXZlbnQsIElFdmVudEJ1cywgSUV2ZW50VXBkYXRlZFBheWxvYWQgfSBmcm9tICcuLi8uLi90eXBlcy9DYWxlbmRhclR5cGVzJztcclxuaW1wb3J0IHsgRXZlbnRTZXJ2aWNlIH0gZnJvbSAnLi4vLi4vc3RvcmFnZS9ldmVudHMvRXZlbnRTZXJ2aWNlJztcclxuaW1wb3J0IHsgRGF0ZVNlcnZpY2UgfSBmcm9tICcuLi8uLi9jb3JlL0RhdGVTZXJ2aWNlJztcclxuaW1wb3J0IHsgSUdyaWRDb25maWcgfSBmcm9tICcuLi8uLi9jb3JlL0lHcmlkQ29uZmlnJztcclxuaW1wb3J0IHsgY2FsY3VsYXRlRXZlbnRQb3NpdGlvbiwgc25hcFRvR3JpZCwgcGl4ZWxzVG9NaW51dGVzIH0gZnJvbSAnLi4vLi4vdXRpbHMvUG9zaXRpb25VdGlscyc7XHJcbmltcG9ydCB7IENvcmVFdmVudHMgfSBmcm9tICcuLi8uLi9jb25zdGFudHMvQ29yZUV2ZW50cyc7XHJcbmltcG9ydCB7IElEcmFnQ29sdW1uQ2hhbmdlUGF5bG9hZCwgSURyYWdNb3ZlUGF5bG9hZCwgSURyYWdFbmRQYXlsb2FkLCBJRHJhZ0xlYXZlSGVhZGVyUGF5bG9hZCB9IGZyb20gJy4uLy4uL3R5cGVzL0RyYWdUeXBlcyc7XHJcbmltcG9ydCB7IGNhbGN1bGF0ZUNvbHVtbkxheW91dCB9IGZyb20gJy4vRXZlbnRMYXlvdXRFbmdpbmUnO1xyXG5pbXBvcnQgeyBJR3JpZEdyb3VwTGF5b3V0IH0gZnJvbSAnLi9FdmVudExheW91dFR5cGVzJztcclxuaW1wb3J0IHsgRmlsdGVyVGVtcGxhdGUgfSBmcm9tICcuLi8uLi9jb3JlL0ZpbHRlclRlbXBsYXRlJztcclxuXHJcbi8qKlxyXG4gKiBFdmVudFJlbmRlcmVyIC0gUmVuZGVycyBjYWxlbmRhciBldmVudHMgdG8gdGhlIERPTVxyXG4gKlxyXG4gKiBDTEVBTiBhcHByb2FjaDpcclxuICogLSBPbmx5IGRhdGEtaWQgYXR0cmlidXRlIG9uIGV2ZW50IGVsZW1lbnRcclxuICogLSBpbm5lckhUTUwgY29udGFpbnMgb25seSB2aXNpYmxlIGNvbnRlbnRcclxuICogLSBFdmVudCBkYXRhIHJldHJpZXZlZCB2aWEgRXZlbnRTZXJ2aWNlIHdoZW4gbmVlZGVkXHJcbiAqL1xyXG5leHBvcnQgY2xhc3MgRXZlbnRSZW5kZXJlciB7XHJcbiAgcHJpdmF0ZSBjb250YWluZXI6IEhUTUxFbGVtZW50IHwgbnVsbCA9IG51bGw7XHJcblxyXG4gIGNvbnN0cnVjdG9yKFxyXG4gICAgcHJpdmF0ZSBldmVudFNlcnZpY2U6IEV2ZW50U2VydmljZSxcclxuICAgIHByaXZhdGUgZGF0ZVNlcnZpY2U6IERhdGVTZXJ2aWNlLFxyXG4gICAgcHJpdmF0ZSBncmlkQ29uZmlnOiBJR3JpZENvbmZpZyxcclxuICAgIHByaXZhdGUgZXZlbnRCdXM6IElFdmVudEJ1c1xyXG4gICkge1xyXG4gICAgdGhpcy5zZXR1cExpc3RlbmVycygpO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogU2V0dXAgbGlzdGVuZXJzIGZvciBkcmFnLWRyb3AgYW5kIHVwZGF0ZSBldmVudHNcclxuICAgKi9cclxuICBwcml2YXRlIHNldHVwTGlzdGVuZXJzKCk6IHZvaWQge1xyXG4gICAgdGhpcy5ldmVudEJ1cy5vbihDb3JlRXZlbnRzLkVWRU5UX0RSQUdfQ09MVU1OX0NIQU5HRSwgKGUpID0+IHtcclxuICAgICAgY29uc3QgcGF5bG9hZCA9IChlIGFzIEN1c3RvbUV2ZW50PElEcmFnQ29sdW1uQ2hhbmdlUGF5bG9hZD4pLmRldGFpbDtcclxuICAgICAgdGhpcy5oYW5kbGVDb2x1bW5DaGFuZ2UocGF5bG9hZCk7XHJcbiAgICB9KTtcclxuXHJcbiAgICB0aGlzLmV2ZW50QnVzLm9uKENvcmVFdmVudHMuRVZFTlRfRFJBR19NT1ZFLCAoZSkgPT4ge1xyXG4gICAgICBjb25zdCBwYXlsb2FkID0gKGUgYXMgQ3VzdG9tRXZlbnQ8SURyYWdNb3ZlUGF5bG9hZD4pLmRldGFpbDtcclxuICAgICAgdGhpcy51cGRhdGVEcmFnVGltZXN0YW1wKHBheWxvYWQpO1xyXG4gICAgfSk7XHJcblxyXG4gICAgdGhpcy5ldmVudEJ1cy5vbihDb3JlRXZlbnRzLkVWRU5UX1VQREFURUQsIChlKSA9PiB7XHJcbiAgICAgIGNvbnN0IHBheWxvYWQgPSAoZSBhcyBDdXN0b21FdmVudDxJRXZlbnRVcGRhdGVkUGF5bG9hZD4pLmRldGFpbDtcclxuICAgICAgdGhpcy5oYW5kbGVFdmVudFVwZGF0ZWQocGF5bG9hZCk7XHJcbiAgICB9KTtcclxuXHJcbiAgICB0aGlzLmV2ZW50QnVzLm9uKENvcmVFdmVudHMuRVZFTlRfRFJBR19FTkQsIChlKSA9PiB7XHJcbiAgICAgIGNvbnN0IHBheWxvYWQgPSAoZSBhcyBDdXN0b21FdmVudDxJRHJhZ0VuZFBheWxvYWQ+KS5kZXRhaWw7XHJcbiAgICAgIHRoaXMuaGFuZGxlRHJhZ0VuZChwYXlsb2FkKTtcclxuICAgIH0pO1xyXG5cclxuICAgIHRoaXMuZXZlbnRCdXMub24oQ29yZUV2ZW50cy5FVkVOVF9EUkFHX0xFQVZFX0hFQURFUiwgKGUpID0+IHtcclxuICAgICAgY29uc3QgcGF5bG9hZCA9IChlIGFzIEN1c3RvbUV2ZW50PElEcmFnTGVhdmVIZWFkZXJQYXlsb2FkPikuZGV0YWlsO1xyXG4gICAgICB0aGlzLmhhbmRsZURyYWdMZWF2ZUhlYWRlcihwYXlsb2FkKTtcclxuICAgIH0pO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogSGFuZGxlIEVWRU5UX0RSQUdfRU5EIC0gcmVtb3ZlIGVsZW1lbnQgaWYgZHJvcHBlZCBpbiBoZWFkZXJcclxuICAgKi9cclxuICBwcml2YXRlIGhhbmRsZURyYWdFbmQocGF5bG9hZDogSURyYWdFbmRQYXlsb2FkKTogdm9pZCB7XHJcbiAgICBpZiAocGF5bG9hZC50YXJnZXQgPT09ICdoZWFkZXInKSB7XHJcbiAgICAgIC8vIEV2ZW50IHdhcyBkcm9wcGVkIGluIGhlYWRlciBkcmF3ZXIgLSByZW1vdmUgZnJvbSBncmlkXHJcbiAgICAgIGNvbnN0IGVsZW1lbnQgPSB0aGlzLmNvbnRhaW5lcj8ucXVlcnlTZWxlY3Rvcihgc3dwLWNvbnRlbnQtdmlld3BvcnQgc3dwLWV2ZW50W2RhdGEtZXZlbnQtaWQ9XCIke3BheWxvYWQuc3dwRXZlbnQuZXZlbnRJZH1cIl1gKTtcclxuICAgICAgZWxlbWVudD8ucmVtb3ZlKCk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBIYW5kbGUgaGVhZGVyIGl0ZW0gbGVhdmluZyBoZWFkZXIgLSBjcmVhdGUgc3dwLWV2ZW50IGluIGdyaWRcclxuICAgKi9cclxuICBwcml2YXRlIGhhbmRsZURyYWdMZWF2ZUhlYWRlcihwYXlsb2FkOiBJRHJhZ0xlYXZlSGVhZGVyUGF5bG9hZCk6IHZvaWQge1xyXG4gICAgLy8gT25seSBoYW5kbGUgd2hlbiBzb3VyY2UgaXMgaGVhZGVyIChoZWFkZXIgaXRlbSBkcmFnZ2VkIHRvIGdyaWQpXHJcbiAgICBpZiAocGF5bG9hZC5zb3VyY2UgIT09ICdoZWFkZXInKSByZXR1cm47XHJcbiAgICBpZiAoIXBheWxvYWQudGFyZ2V0Q29sdW1uIHx8ICFwYXlsb2FkLnN0YXJ0IHx8ICFwYXlsb2FkLmVuZCkgcmV0dXJuO1xyXG5cclxuICAgIC8vIFR1cm4gaGVhZGVyIGl0ZW0gaW50byBnaG9zdCAoc3RheXMgdmlzaWJsZSBidXQgZmFkZWQpXHJcbiAgICBpZiAocGF5bG9hZC5lbGVtZW50KSB7XHJcbiAgICAgIHBheWxvYWQuZWxlbWVudC5jbGFzc0xpc3QuYWRkKCdkcmFnLWdob3N0Jyk7XHJcbiAgICAgIHBheWxvYWQuZWxlbWVudC5zdHlsZS5vcGFjaXR5ID0gJzAuMyc7XHJcbiAgICAgIHBheWxvYWQuZWxlbWVudC5zdHlsZS5wb2ludGVyRXZlbnRzID0gJ25vbmUnO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIENyZWF0ZSBldmVudCBvYmplY3QgZnJvbSBoZWFkZXIgaXRlbSBkYXRhXHJcbiAgICBjb25zdCBldmVudDogSUNhbGVuZGFyRXZlbnQgPSB7XHJcbiAgICAgIGlkOiBwYXlsb2FkLmV2ZW50SWQsXHJcbiAgICAgIHRpdGxlOiBwYXlsb2FkLnRpdGxlIHx8ICcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJycsXHJcbiAgICAgIHN0YXJ0OiBwYXlsb2FkLnN0YXJ0LFxyXG4gICAgICBlbmQ6IHBheWxvYWQuZW5kLFxyXG4gICAgICB0eXBlOiAnY3VzdG9tZXInLFxyXG4gICAgICBhbGxEYXk6IGZhbHNlLFxyXG4gICAgICBzeW5jU3RhdHVzOiAncGVuZGluZydcclxuICAgIH07XHJcblxyXG4gICAgLy8gQ3JlYXRlIHN3cC1ldmVudCBlbGVtZW50IHVzaW5nIGV4aXN0aW5nIG1ldGhvZFxyXG4gICAgY29uc3QgZWxlbWVudCA9IHRoaXMuY3JlYXRlRXZlbnRFbGVtZW50KGV2ZW50KTtcclxuXHJcbiAgICAvLyBBZGQgdG8gdGFyZ2V0IGNvbHVtblxyXG4gICAgbGV0IGV2ZW50c0xheWVyID0gcGF5bG9hZC50YXJnZXRDb2x1bW4ucXVlcnlTZWxlY3Rvcignc3dwLWV2ZW50cy1sYXllcicpO1xyXG4gICAgaWYgKCFldmVudHNMYXllcikge1xyXG4gICAgICBldmVudHNMYXllciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3N3cC1ldmVudHMtbGF5ZXInKTtcclxuICAgICAgcGF5bG9hZC50YXJnZXRDb2x1bW4uYXBwZW5kQ2hpbGQoZXZlbnRzTGF5ZXIpO1xyXG4gICAgfVxyXG4gICAgZXZlbnRzTGF5ZXIuYXBwZW5kQ2hpbGQoZWxlbWVudCk7XHJcblxyXG4gICAgLy8gTWFyayBhcyBkcmFnZ2luZyBzbyBEcmFnRHJvcE1hbmFnZXIgY2FuIGNvbnRpbnVlIHdpdGggaXRcclxuICAgIGVsZW1lbnQuY2xhc3NMaXN0LmFkZCgnZHJhZ2dpbmcnKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIEhhbmRsZSBFVkVOVF9VUERBVEVEIC0gcmUtcmVuZGVyIGFmZmVjdGVkIGNvbHVtbnNcclxuICAgKi9cclxuICBwcml2YXRlIGFzeW5jIGhhbmRsZUV2ZW50VXBkYXRlZChwYXlsb2FkOiBJRXZlbnRVcGRhdGVkUGF5bG9hZCk6IFByb21pc2U8dm9pZD4ge1xyXG4gICAgLy8gUmUtcmVuZGVyIHNvdXJjZSBjb2x1bW4gKGlmIGRpZmZlcmVudCBmcm9tIHRhcmdldClcclxuICAgIGlmIChwYXlsb2FkLnNvdXJjZUNvbHVtbktleSAhPT0gcGF5bG9hZC50YXJnZXRDb2x1bW5LZXkpIHtcclxuICAgICAgYXdhaXQgdGhpcy5yZXJlbmRlckNvbHVtbihwYXlsb2FkLnNvdXJjZUNvbHVtbktleSk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUmUtcmVuZGVyIHRhcmdldCBjb2x1bW5cclxuICAgIGF3YWl0IHRoaXMucmVyZW5kZXJDb2x1bW4ocGF5bG9hZC50YXJnZXRDb2x1bW5LZXkpO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogUmUtcmVuZGVyIGEgc2luZ2xlIGNvbHVtbiB3aXRoIGZyZXNoIGRhdGEgZnJvbSBJbmRleGVkREJcclxuICAgKi9cclxuICBwcml2YXRlIGFzeW5jIHJlcmVuZGVyQ29sdW1uKGNvbHVtbktleTogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICBjb25zdCBjb2x1bW4gPSB0aGlzLmZpbmRDb2x1bW4oY29sdW1uS2V5KTtcclxuICAgIGlmICghY29sdW1uKSByZXR1cm47XHJcblxyXG4gICAgLy8gUmVhZCBkYXRlIGFuZCByZXNvdXJjZUlkIGRpcmVjdGx5IGZyb20gY29sdW1uIGF0dHJpYnV0ZXMgKGNvbHVtbktleSBpcyBvcGFxdWUpXHJcbiAgICBjb25zdCBkYXRlID0gY29sdW1uLmRhdGFzZXQuZGF0ZTtcclxuICAgIGNvbnN0IHJlc291cmNlSWQgPSBjb2x1bW4uZGF0YXNldC5yZXNvdXJjZUlkO1xyXG5cclxuICAgIGlmICghZGF0ZSkgcmV0dXJuO1xyXG5cclxuICAgIC8vIEdldCBkYXRlIHJhbmdlIGZvciB0aGlzIGRheVxyXG4gICAgY29uc3Qgc3RhcnREYXRlID0gbmV3IERhdGUoZGF0ZSk7XHJcbiAgICBjb25zdCBlbmREYXRlID0gbmV3IERhdGUoZGF0ZSk7XHJcbiAgICBlbmREYXRlLnNldEhvdXJzKDIzLCA1OSwgNTksIDk5OSk7XHJcblxyXG4gICAgLy8gRmV0Y2ggZXZlbnRzIGZyb20gSW5kZXhlZERCXHJcbiAgICBjb25zdCBldmVudHMgPSByZXNvdXJjZUlkXHJcbiAgICAgID8gYXdhaXQgdGhpcy5ldmVudFNlcnZpY2UuZ2V0QnlSZXNvdXJjZUFuZERhdGVSYW5nZShyZXNvdXJjZUlkLCBzdGFydERhdGUsIGVuZERhdGUpXHJcbiAgICAgIDogYXdhaXQgdGhpcy5ldmVudFNlcnZpY2UuZ2V0QnlEYXRlUmFuZ2Uoc3RhcnREYXRlLCBlbmREYXRlKTtcclxuXHJcbiAgICAvLyBGaWx0ZXIgdG8gdGltZWQgZXZlbnRzIGFuZCBtYXRjaCBkYXRlIGV4YWN0bHlcclxuICAgIGNvbnN0IHRpbWVkRXZlbnRzID0gZXZlbnRzLmZpbHRlcihldmVudCA9PlxyXG4gICAgICAhZXZlbnQuYWxsRGF5ICYmIHRoaXMuZGF0ZVNlcnZpY2UuZ2V0RGF0ZUtleShldmVudC5zdGFydCkgPT09IGRhdGVcclxuICAgICk7XHJcblxyXG4gICAgLy8gR2V0IG9yIGNyZWF0ZSBldmVudHMgbGF5ZXJcclxuICAgIGxldCBldmVudHNMYXllciA9IGNvbHVtbi5xdWVyeVNlbGVjdG9yKCdzd3AtZXZlbnRzLWxheWVyJyk7XHJcbiAgICBpZiAoIWV2ZW50c0xheWVyKSB7XHJcbiAgICAgIGV2ZW50c0xheWVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3dwLWV2ZW50cy1sYXllcicpO1xyXG4gICAgICBjb2x1bW4uYXBwZW5kQ2hpbGQoZXZlbnRzTGF5ZXIpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIENsZWFyIGV4aXN0aW5nIGV2ZW50c1xyXG4gICAgZXZlbnRzTGF5ZXIuaW5uZXJIVE1MID0gJyc7XHJcblxyXG4gICAgLy8gQ2FsY3VsYXRlIGxheW91dCB3aXRoIHN0YWNraW5nL2dyb3VwaW5nXHJcbiAgICBjb25zdCBsYXlvdXQgPSBjYWxjdWxhdGVDb2x1bW5MYXlvdXQodGltZWRFdmVudHMsIHRoaXMuZ3JpZENvbmZpZyk7XHJcblxyXG4gICAgLy8gUmVuZGVyIEdSSUQgZ3JvdXBzXHJcbiAgICBsYXlvdXQuZ3JpZHMuZm9yRWFjaChncmlkID0+IHtcclxuICAgICAgY29uc3QgZ3JvdXBFbCA9IHRoaXMucmVuZGVyR3JpZEdyb3VwKGdyaWQpO1xyXG4gICAgICBldmVudHNMYXllciEuYXBwZW5kQ2hpbGQoZ3JvdXBFbCk7XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBSZW5kZXIgU1RBQ0tFRCBldmVudHNcclxuICAgIGxheW91dC5zdGFja2VkLmZvckVhY2goaXRlbSA9PiB7XHJcbiAgICAgIGNvbnN0IGV2ZW50RWwgPSB0aGlzLnJlbmRlclN0YWNrZWRFdmVudChpdGVtLmV2ZW50LCBpdGVtLnN0YWNrTGV2ZWwpO1xyXG4gICAgICBldmVudHNMYXllciEuYXBwZW5kQ2hpbGQoZXZlbnRFbCk7XHJcbiAgICB9KTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIEZpbmQgYSBjb2x1bW4gZWxlbWVudCBieSBjb2x1bW5LZXlcclxuICAgKi9cclxuICBwcml2YXRlIGZpbmRDb2x1bW4oY29sdW1uS2V5OiBzdHJpbmcpOiBIVE1MRWxlbWVudCB8IG51bGwge1xyXG4gICAgaWYgKCF0aGlzLmNvbnRhaW5lcikgcmV0dXJuIG51bGw7XHJcbiAgICByZXR1cm4gdGhpcy5jb250YWluZXIucXVlcnlTZWxlY3Rvcihgc3dwLWRheS1jb2x1bW5bZGF0YS1jb2x1bW4ta2V5PVwiJHtjb2x1bW5LZXl9XCJdYCkgYXMgSFRNTEVsZW1lbnQ7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBIYW5kbGUgZXZlbnQgbW92aW5nIHRvIGEgbmV3IGNvbHVtbiBkdXJpbmcgZHJhZ1xyXG4gICAqL1xyXG4gIHByaXZhdGUgaGFuZGxlQ29sdW1uQ2hhbmdlKHBheWxvYWQ6IElEcmFnQ29sdW1uQ2hhbmdlUGF5bG9hZCk6IHZvaWQge1xyXG4gICAgY29uc3QgZXZlbnRzTGF5ZXIgPSBwYXlsb2FkLm5ld0NvbHVtbi5xdWVyeVNlbGVjdG9yKCdzd3AtZXZlbnRzLWxheWVyJyk7XHJcbiAgICBpZiAoIWV2ZW50c0xheWVyKSByZXR1cm47XHJcblxyXG4gICAgLy8gTW92ZSBlbGVtZW50IHRvIG5ldyBjb2x1bW5cclxuICAgIGV2ZW50c0xheWVyLmFwcGVuZENoaWxkKHBheWxvYWQuZWxlbWVudCk7XHJcblxyXG4gICAgLy8gUHJlc2VydmUgWSBwb3NpdGlvblxyXG4gICAgcGF5bG9hZC5lbGVtZW50LnN0eWxlLnRvcCA9IGAke3BheWxvYWQuY3VycmVudFl9cHhgO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogVXBkYXRlIHRpbWVzdGFtcCBkaXNwbGF5IGR1cmluZyBkcmFnIChzbmFwcGVkIHRvIGdyaWQpXHJcbiAgICovXHJcbiAgcHJpdmF0ZSB1cGRhdGVEcmFnVGltZXN0YW1wKHBheWxvYWQ6IElEcmFnTW92ZVBheWxvYWQpOiB2b2lkIHtcclxuICAgIGNvbnN0IHRpbWVFbCA9IHBheWxvYWQuZWxlbWVudC5xdWVyeVNlbGVjdG9yKCdzd3AtZXZlbnQtdGltZScpO1xyXG4gICAgaWYgKCF0aW1lRWwpIHJldHVybjtcclxuXHJcbiAgICAvLyBTbmFwIHBvc2l0aW9uIHRvIGdyaWQgaW50ZXJ2YWxcclxuICAgIGNvbnN0IHNuYXBwZWRZID0gc25hcFRvR3JpZChwYXlsb2FkLmN1cnJlbnRZLCB0aGlzLmdyaWRDb25maWcpO1xyXG5cclxuICAgIC8vIENhbGN1bGF0ZSBuZXcgc3RhcnQgdGltZVxyXG4gICAgY29uc3QgbWludXRlc0Zyb21HcmlkU3RhcnQgPSBwaXhlbHNUb01pbnV0ZXMoc25hcHBlZFksIHRoaXMuZ3JpZENvbmZpZyk7XHJcbiAgICBjb25zdCBzdGFydE1pbnV0ZXMgPSAodGhpcy5ncmlkQ29uZmlnLmRheVN0YXJ0SG91ciAqIDYwKSArIG1pbnV0ZXNGcm9tR3JpZFN0YXJ0O1xyXG5cclxuICAgIC8vIEtlZXAgb3JpZ2luYWwgZHVyYXRpb24gKGZyb20gZWxlbWVudCBoZWlnaHQpXHJcbiAgICBjb25zdCBoZWlnaHQgPSBwYXJzZUZsb2F0KHBheWxvYWQuZWxlbWVudC5zdHlsZS5oZWlnaHQpIHx8IHRoaXMuZ3JpZENvbmZpZy5ob3VySGVpZ2h0O1xyXG4gICAgY29uc3QgZHVyYXRpb25NaW51dGVzID0gcGl4ZWxzVG9NaW51dGVzKGhlaWdodCwgdGhpcy5ncmlkQ29uZmlnKTtcclxuXHJcbiAgICAvLyBDcmVhdGUgRGF0ZSBvYmplY3RzIGZvciBjb25zaXN0ZW50IGZvcm1hdHRpbmcgdmlhIERhdGVTZXJ2aWNlXHJcbiAgICBjb25zdCBzdGFydCA9IHRoaXMubWludXRlc1RvRGF0ZShzdGFydE1pbnV0ZXMpO1xyXG4gICAgY29uc3QgZW5kID0gdGhpcy5taW51dGVzVG9EYXRlKHN0YXJ0TWludXRlcyArIGR1cmF0aW9uTWludXRlcyk7XHJcblxyXG4gICAgdGltZUVsLnRleHRDb250ZW50ID0gdGhpcy5kYXRlU2VydmljZS5mb3JtYXRUaW1lUmFuZ2Uoc3RhcnQsIGVuZCk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBDb252ZXJ0IG1pbnV0ZXMgc2luY2UgbWlkbmlnaHQgdG8gYSBEYXRlIG9iamVjdCAodG9kYXkpXHJcbiAgICovXHJcbiAgcHJpdmF0ZSBtaW51dGVzVG9EYXRlKG1pbnV0ZXM6IG51bWJlcik6IERhdGUge1xyXG4gICAgY29uc3QgZGF0ZSA9IG5ldyBEYXRlKCk7XHJcbiAgICBkYXRlLnNldEhvdXJzKE1hdGguZmxvb3IobWludXRlcyAvIDYwKSAlIDI0LCBtaW51dGVzICUgNjAsIDAsIDApO1xyXG4gICAgcmV0dXJuIGRhdGU7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBSZW5kZXIgZXZlbnRzIGZvciB2aXNpYmxlIGRhdGVzIGludG8gZGF5IGNvbHVtbnNcclxuICAgKiBAcGFyYW0gY29udGFpbmVyIC0gQ2FsZW5kYXIgY29udGFpbmVyIGVsZW1lbnRcclxuICAgKiBAcGFyYW0gZmlsdGVyIC0gRmlsdGVyIHdpdGggJ2RhdGUnIGFuZCBvcHRpb25hbGx5ICdyZXNvdXJjZScgYXJyYXlzXHJcbiAgICogQHBhcmFtIGZpbHRlclRlbXBsYXRlIC0gVGVtcGxhdGUgZm9yIG1hdGNoaW5nIGV2ZW50cyB0byBjb2x1bW5zXHJcbiAgICovXHJcbiAgYXN5bmMgcmVuZGVyKGNvbnRhaW5lcjogSFRNTEVsZW1lbnQsIGZpbHRlcjogUmVjb3JkPHN0cmluZywgc3RyaW5nW10+LCBmaWx0ZXJUZW1wbGF0ZTogRmlsdGVyVGVtcGxhdGUpOiBQcm9taXNlPHZvaWQ+IHtcclxuICAgIC8vIFN0b3JlIGNvbnRhaW5lciByZWZlcmVuY2UgZm9yIGxhdGVyIHJlLXJlbmRlcnNcclxuICAgIHRoaXMuY29udGFpbmVyID0gY29udGFpbmVyO1xyXG5cclxuICAgIGNvbnN0IHZpc2libGVEYXRlcyA9IGZpbHRlclsnZGF0ZSddIHx8IFtdO1xyXG5cclxuICAgIGlmICh2aXNpYmxlRGF0ZXMubGVuZ3RoID09PSAwKSByZXR1cm47XHJcblxyXG4gICAgLy8gR2V0IGRhdGUgcmFuZ2UgZm9yIHF1ZXJ5XHJcbiAgICBjb25zdCBzdGFydERhdGUgPSBuZXcgRGF0ZSh2aXNpYmxlRGF0ZXNbMF0pO1xyXG4gICAgY29uc3QgZW5kRGF0ZSA9IG5ldyBEYXRlKHZpc2libGVEYXRlc1t2aXNpYmxlRGF0ZXMubGVuZ3RoIC0gMV0pO1xyXG4gICAgZW5kRGF0ZS5zZXRIb3VycygyMywgNTksIDU5LCA5OTkpO1xyXG5cclxuICAgIC8vIEZldGNoIGV2ZW50cyBmcm9tIEluZGV4ZWREQlxyXG4gICAgY29uc3QgZXZlbnRzID0gYXdhaXQgdGhpcy5ldmVudFNlcnZpY2UuZ2V0QnlEYXRlUmFuZ2Uoc3RhcnREYXRlLCBlbmREYXRlKTtcclxuXHJcbiAgICAvLyBGaW5kIGRheSBjb2x1bW5zXHJcbiAgICBjb25zdCBkYXlDb2x1bW5zID0gY29udGFpbmVyLnF1ZXJ5U2VsZWN0b3IoJ3N3cC1kYXktY29sdW1ucycpO1xyXG4gICAgaWYgKCFkYXlDb2x1bW5zKSByZXR1cm47XHJcblxyXG4gICAgY29uc3QgY29sdW1ucyA9IGRheUNvbHVtbnMucXVlcnlTZWxlY3RvckFsbCgnc3dwLWRheS1jb2x1bW4nKTtcclxuXHJcbiAgICAvLyBSZW5kZXIgZXZlbnRzIGludG8gZWFjaCBjb2x1bW4gYmFzZWQgb24gRmlsdGVyVGVtcGxhdGUgbWF0Y2hpbmdcclxuICAgIGNvbHVtbnMuZm9yRWFjaChjb2x1bW4gPT4ge1xyXG4gICAgICBjb25zdCBjb2x1bW5FbCA9IGNvbHVtbiBhcyBIVE1MRWxlbWVudDtcclxuXHJcbiAgICAgIC8vIFVzZSBGaWx0ZXJUZW1wbGF0ZSBmb3IgbWF0Y2hpbmcgLSBvbmx5IGZpZWxkcyBpbiB0ZW1wbGF0ZSBhcmUgY2hlY2tlZFxyXG4gICAgICBjb25zdCBjb2x1bW5FdmVudHMgPSBldmVudHMuZmlsdGVyKGV2ZW50ID0+IGZpbHRlclRlbXBsYXRlLm1hdGNoZXMoZXZlbnQsIGNvbHVtbkVsKSk7XHJcblxyXG4gICAgICAvLyBHZXQgb3IgY3JlYXRlIGV2ZW50cyBsYXllclxyXG4gICAgICBsZXQgZXZlbnRzTGF5ZXIgPSBjb2x1bW4ucXVlcnlTZWxlY3Rvcignc3dwLWV2ZW50cy1sYXllcicpO1xyXG4gICAgICBpZiAoIWV2ZW50c0xheWVyKSB7XHJcbiAgICAgICAgZXZlbnRzTGF5ZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzd3AtZXZlbnRzLWxheWVyJyk7XHJcbiAgICAgICAgY29sdW1uLmFwcGVuZENoaWxkKGV2ZW50c0xheWVyKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gQ2xlYXIgZXhpc3RpbmcgZXZlbnRzXHJcbiAgICAgIGV2ZW50c0xheWVyLmlubmVySFRNTCA9ICcnO1xyXG5cclxuICAgICAgLy8gRmlsdGVyIHRvIHRpbWVkIGV2ZW50cyBvbmx5XHJcbiAgICAgIGNvbnN0IHRpbWVkRXZlbnRzID0gY29sdW1uRXZlbnRzLmZpbHRlcihldmVudCA9PiAhZXZlbnQuYWxsRGF5KTtcclxuXHJcbiAgICAgIC8vIENhbGN1bGF0ZSBsYXlvdXQgd2l0aCBzdGFja2luZy9ncm91cGluZ1xyXG4gICAgICBjb25zdCBsYXlvdXQgPSBjYWxjdWxhdGVDb2x1bW5MYXlvdXQodGltZWRFdmVudHMsIHRoaXMuZ3JpZENvbmZpZyk7XHJcblxyXG4gICAgICAvLyBSZW5kZXIgR1JJRCBncm91cHMgKHNpbXVsdGFuZW91cyBldmVudHMgc2lkZS1ieS1zaWRlKVxyXG4gICAgICBsYXlvdXQuZ3JpZHMuZm9yRWFjaChncmlkID0+IHtcclxuICAgICAgICBjb25zdCBncm91cEVsID0gdGhpcy5yZW5kZXJHcmlkR3JvdXAoZ3JpZCk7XHJcbiAgICAgICAgZXZlbnRzTGF5ZXIhLmFwcGVuZENoaWxkKGdyb3VwRWwpO1xyXG4gICAgICB9KTtcclxuXHJcbiAgICAgIC8vIFJlbmRlciBTVEFDS0VEIGV2ZW50cyAob3ZlcmxhcHBpbmcgd2l0aCBtYXJnaW4gb2Zmc2V0KVxyXG4gICAgICBsYXlvdXQuc3RhY2tlZC5mb3JFYWNoKGl0ZW0gPT4ge1xyXG4gICAgICAgIGNvbnN0IGV2ZW50RWwgPSB0aGlzLnJlbmRlclN0YWNrZWRFdmVudChpdGVtLmV2ZW50LCBpdGVtLnN0YWNrTGV2ZWwpO1xyXG4gICAgICAgIGV2ZW50c0xheWVyIS5hcHBlbmRDaGlsZChldmVudEVsKTtcclxuICAgICAgfSk7XHJcbiAgICB9KTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIENyZWF0ZSBhIHNpbmdsZSBldmVudCBlbGVtZW50XHJcbiAgICpcclxuICAgKiBDTEVBTiBhcHByb2FjaDpcclxuICAgKiAtIE9ubHkgZGF0YS1pZCBmb3IgbG9va3VwXHJcbiAgICogLSBWaXNpYmxlIGNvbnRlbnQgaW4gaW5uZXJIVE1MIG9ubHlcclxuICAgKi9cclxuICBwcml2YXRlIGNyZWF0ZUV2ZW50RWxlbWVudChldmVudDogSUNhbGVuZGFyRXZlbnQpOiBIVE1MRWxlbWVudCB7XHJcbiAgICBjb25zdCBlbGVtZW50ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3dwLWV2ZW50Jyk7XHJcblxyXG4gICAgLy8gRGF0YSBhdHRyaWJ1dGVzIGZvciBTd3BFdmVudCBjb21wYXRpYmlsaXR5XHJcbiAgICBlbGVtZW50LmRhdGFzZXQuZXZlbnRJZCA9IGV2ZW50LmlkO1xyXG4gICAgaWYgKGV2ZW50LnJlc291cmNlSWQpIHtcclxuICAgICAgZWxlbWVudC5kYXRhc2V0LnJlc291cmNlSWQgPSBldmVudC5yZXNvdXJjZUlkO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIENhbGN1bGF0ZSBwb3NpdGlvblxyXG4gICAgY29uc3QgcG9zaXRpb24gPSBjYWxjdWxhdGVFdmVudFBvc2l0aW9uKGV2ZW50LnN0YXJ0LCBldmVudC5lbmQsIHRoaXMuZ3JpZENvbmZpZyk7XHJcbiAgICBlbGVtZW50LnN0eWxlLnRvcCA9IGAke3Bvc2l0aW9uLnRvcH1weGA7XHJcbiAgICBlbGVtZW50LnN0eWxlLmhlaWdodCA9IGAke3Bvc2l0aW9uLmhlaWdodH1weGA7XHJcblxyXG4gICAgLy8gQ29sb3IgY2xhc3MgYmFzZWQgb24gZXZlbnQgdHlwZVxyXG4gICAgY29uc3QgY29sb3JDbGFzcyA9IHRoaXMuZ2V0Q29sb3JDbGFzcyhldmVudCk7XHJcbiAgICBpZiAoY29sb3JDbGFzcykge1xyXG4gICAgICBlbGVtZW50LmNsYXNzTGlzdC5hZGQoY29sb3JDbGFzcyk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gVmlzaWJsZSBjb250ZW50IG9ubHlcclxuICAgIGVsZW1lbnQuaW5uZXJIVE1MID0gYFxyXG4gICAgICA8c3dwLWV2ZW50LXRpbWU+JHt0aGlzLmRhdGVTZXJ2aWNlLmZvcm1hdFRpbWVSYW5nZShldmVudC5zdGFydCwgZXZlbnQuZW5kKX08L3N3cC1ldmVudC10aW1lPlxyXG4gICAgICA8c3dwLWV2ZW50LXRpdGxlPiR7dGhpcy5lc2NhcGVIdG1sKGV2ZW50LnRpdGxlKX08L3N3cC1ldmVudC10aXRsZT5cclxuICAgICAgJHtldmVudC5kZXNjcmlwdGlvbiA/IGA8c3dwLWV2ZW50LWRlc2NyaXB0aW9uPiR7dGhpcy5lc2NhcGVIdG1sKGV2ZW50LmRlc2NyaXB0aW9uKX08L3N3cC1ldmVudC1kZXNjcmlwdGlvbj5gIDogJyd9XHJcbiAgICBgO1xyXG5cclxuICAgIHJldHVybiBlbGVtZW50O1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogR2V0IGNvbG9yIGNsYXNzIGJhc2VkIG9uIG1ldGFkYXRhLmNvbG9yIG9yIGV2ZW50IHR5cGVcclxuICAgKi9cclxuICBwcml2YXRlIGdldENvbG9yQ2xhc3MoZXZlbnQ6IElDYWxlbmRhckV2ZW50KTogc3RyaW5nIHtcclxuICAgIC8vIENoZWNrIG1ldGFkYXRhLmNvbG9yIGZpcnN0XHJcbiAgICBpZiAoZXZlbnQubWV0YWRhdGE/LmNvbG9yKSB7XHJcbiAgICAgIHJldHVybiBgaXMtJHtldmVudC5tZXRhZGF0YS5jb2xvcn1gO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIEZhbGxiYWNrIHRvIHR5cGUtYmFzZWQgY29sb3JcclxuICAgIGNvbnN0IHR5cGVDb2xvcnM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSB7XHJcbiAgICAgICdjdXN0b21lcic6ICdpcy1ibHVlJyxcclxuICAgICAgJ3ZhY2F0aW9uJzogJ2lzLWdyZWVuJyxcclxuICAgICAgJ2JyZWFrJzogJ2lzLWFtYmVyJyxcclxuICAgICAgJ21lZXRpbmcnOiAnaXMtcHVycGxlJyxcclxuICAgICAgJ2Jsb2NrZWQnOiAnaXMtcmVkJ1xyXG4gICAgfTtcclxuICAgIHJldHVybiB0eXBlQ29sb3JzW2V2ZW50LnR5cGVdIHx8ICdpcy1ibHVlJztcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIEVzY2FwZSBIVE1MIHRvIHByZXZlbnQgWFNTXHJcbiAgICovXHJcbiAgcHJpdmF0ZSBlc2NhcGVIdG1sKHRleHQ6IHN0cmluZyk6IHN0cmluZyB7XHJcbiAgICBjb25zdCBkaXYgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcclxuICAgIGRpdi50ZXh0Q29udGVudCA9IHRleHQ7XHJcbiAgICByZXR1cm4gZGl2LmlubmVySFRNTDtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFJlbmRlciBhIEdSSUQgZ3JvdXAgd2l0aCBzaWRlLWJ5LXNpZGUgY29sdW1uc1xyXG4gICAqIFVzZWQgd2hlbiBtdWx0aXBsZSBldmVudHMgc3RhcnQgYXQgdGhlIHNhbWUgdGltZVxyXG4gICAqL1xyXG4gIHByaXZhdGUgcmVuZGVyR3JpZEdyb3VwKGxheW91dDogSUdyaWRHcm91cExheW91dCk6IEhUTUxFbGVtZW50IHtcclxuICAgIGNvbnN0IGdyb3VwID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3dwLWV2ZW50LWdyb3VwJyk7XHJcbiAgICBncm91cC5jbGFzc0xpc3QuYWRkKGBjb2xzLSR7bGF5b3V0LmNvbHVtbnMubGVuZ3RofWApO1xyXG4gICAgZ3JvdXAuc3R5bGUudG9wID0gYCR7bGF5b3V0LnBvc2l0aW9uLnRvcH1weGA7XHJcblxyXG4gICAgLy8gU3RhY2sgbGV2ZWwgc3R5bGluZyBmb3IgZW50aXJlIGdyb3VwIChpZiBuZXN0ZWQgaW4gYW5vdGhlciBldmVudClcclxuICAgIGlmIChsYXlvdXQuc3RhY2tMZXZlbCA+IDApIHtcclxuICAgICAgZ3JvdXAuc3R5bGUubWFyZ2luTGVmdCA9IGAke2xheW91dC5zdGFja0xldmVsICogMTV9cHhgO1xyXG4gICAgICBncm91cC5zdHlsZS56SW5kZXggPSBgJHsxMDAgKyBsYXlvdXQuc3RhY2tMZXZlbH1gO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIENhbGN1bGF0ZSB0aGUgaGVpZ2h0IG5lZWRlZCBmb3IgdGhlIGdyb3VwICh0YWxsZXN0IGV2ZW50KVxyXG4gICAgbGV0IG1heEJvdHRvbSA9IDA7XHJcbiAgICBmb3IgKGNvbnN0IGV2ZW50IG9mIGxheW91dC5ldmVudHMpIHtcclxuICAgICAgY29uc3QgcG9zID0gY2FsY3VsYXRlRXZlbnRQb3NpdGlvbihldmVudC5zdGFydCwgZXZlbnQuZW5kLCB0aGlzLmdyaWRDb25maWcpO1xyXG4gICAgICBjb25zdCBldmVudEJvdHRvbSA9IHBvcy50b3AgKyBwb3MuaGVpZ2h0O1xyXG4gICAgICBpZiAoZXZlbnRCb3R0b20gPiBtYXhCb3R0b20pIG1heEJvdHRvbSA9IGV2ZW50Qm90dG9tO1xyXG4gICAgfVxyXG4gICAgY29uc3QgZ3JvdXBIZWlnaHQgPSBtYXhCb3R0b20gLSBsYXlvdXQucG9zaXRpb24udG9wO1xyXG4gICAgZ3JvdXAuc3R5bGUuaGVpZ2h0ID0gYCR7Z3JvdXBIZWlnaHR9cHhgO1xyXG5cclxuICAgIC8vIENyZWF0ZSB3cmFwcGVyIGRpdiBmb3IgZWFjaCBjb2x1bW5cclxuICAgIGxheW91dC5jb2x1bW5zLmZvckVhY2goY29sdW1uRXZlbnRzID0+IHtcclxuICAgICAgY29uc3Qgd3JhcHBlciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpO1xyXG4gICAgICB3cmFwcGVyLnN0eWxlLnBvc2l0aW9uID0gJ3JlbGF0aXZlJztcclxuXHJcbiAgICAgIGNvbHVtbkV2ZW50cy5mb3JFYWNoKGV2ZW50ID0+IHtcclxuICAgICAgICBjb25zdCBldmVudEVsID0gdGhpcy5jcmVhdGVFdmVudEVsZW1lbnQoZXZlbnQpO1xyXG4gICAgICAgIC8vIFBvc2l0aW9uIHJlbGF0aXZlIHRvIGdyb3VwIHRvcFxyXG4gICAgICAgIGNvbnN0IHBvcyA9IGNhbGN1bGF0ZUV2ZW50UG9zaXRpb24oZXZlbnQuc3RhcnQsIGV2ZW50LmVuZCwgdGhpcy5ncmlkQ29uZmlnKTtcclxuICAgICAgICBldmVudEVsLnN0eWxlLnRvcCA9IGAke3Bvcy50b3AgLSBsYXlvdXQucG9zaXRpb24udG9wfXB4YDtcclxuICAgICAgICBldmVudEVsLnN0eWxlLnBvc2l0aW9uID0gJ2Fic29sdXRlJztcclxuICAgICAgICBldmVudEVsLnN0eWxlLmxlZnQgPSAnMCc7XHJcbiAgICAgICAgZXZlbnRFbC5zdHlsZS5yaWdodCA9ICcwJztcclxuICAgICAgICB3cmFwcGVyLmFwcGVuZENoaWxkKGV2ZW50RWwpO1xyXG4gICAgICB9KTtcclxuXHJcbiAgICAgIGdyb3VwLmFwcGVuZENoaWxkKHdyYXBwZXIpO1xyXG4gICAgfSk7XHJcblxyXG4gICAgcmV0dXJuIGdyb3VwO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogUmVuZGVyIGEgU1RBQ0tFRCBldmVudCB3aXRoIG1hcmdpbi1sZWZ0IG9mZnNldFxyXG4gICAqIFVzZWQgZm9yIG92ZXJsYXBwaW5nIGV2ZW50cyB0aGF0IGRvbid0IHN0YXJ0IGF0IHRoZSBzYW1lIHRpbWVcclxuICAgKi9cclxuICBwcml2YXRlIHJlbmRlclN0YWNrZWRFdmVudChldmVudDogSUNhbGVuZGFyRXZlbnQsIHN0YWNrTGV2ZWw6IG51bWJlcik6IEhUTUxFbGVtZW50IHtcclxuICAgIGNvbnN0IGVsZW1lbnQgPSB0aGlzLmNyZWF0ZUV2ZW50RWxlbWVudChldmVudCk7XHJcblxyXG4gICAgLy8gQWRkIHN0YWNrIG1ldGFkYXRhIGZvciBkcmFnLWRyb3AgYW5kIG90aGVyIGZlYXR1cmVzXHJcbiAgICBlbGVtZW50LmRhdGFzZXQuc3RhY2tMaW5rID0gSlNPTi5zdHJpbmdpZnkoeyBzdGFja0xldmVsIH0pO1xyXG5cclxuICAgIC8vIFZpc3VhbCBzdHlsaW5nIGJhc2VkIG9uIHN0YWNrIGxldmVsXHJcbiAgICBpZiAoc3RhY2tMZXZlbCA+IDApIHtcclxuICAgICAgZWxlbWVudC5zdHlsZS5tYXJnaW5MZWZ0ID0gYCR7c3RhY2tMZXZlbCAqIDE1fXB4YDtcclxuICAgICAgZWxlbWVudC5zdHlsZS56SW5kZXggPSBgJHsxMDAgKyBzdGFja0xldmVsfWA7XHJcbiAgICB9XHJcblxyXG4gICAgcmV0dXJuIGVsZW1lbnQ7XHJcbiAgfVxyXG59XHJcbiIsICJpbXBvcnQgeyBJUmVuZGVyZXIsIElSZW5kZXJDb250ZXh0IH0gZnJvbSAnLi9JR3JvdXBpbmdSZW5kZXJlcic7XHJcblxyXG4vKipcclxuICogRW50aXR5IG11c3QgaGF2ZSBpZFxyXG4gKi9cclxuZXhwb3J0IGludGVyZmFjZSBJR3JvdXBpbmdFbnRpdHkge1xyXG4gIGlkOiBzdHJpbmc7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDb25maWd1cmF0aW9uIGZvciBhIGdyb3VwaW5nIHJlbmRlcmVyXHJcbiAqL1xyXG5leHBvcnQgaW50ZXJmYWNlIElHcm91cGluZ1JlbmRlcmVyQ29uZmlnIHtcclxuICBlbGVtZW50VGFnOiBzdHJpbmc7ICAgICAgLy8gZS5nLiwgJ3N3cC10ZWFtLWhlYWRlcidcclxuICBpZEF0dHJpYnV0ZTogc3RyaW5nOyAgICAgLy8gZS5nLiwgJ3RlYW1JZCcgLT4gZGF0YS10ZWFtLWlkXHJcbiAgY29sc3BhblZhcjogc3RyaW5nOyAgICAgIC8vIGUuZy4sICctLXRlYW0tY29scydcclxufVxyXG5cclxuLyoqXHJcbiAqIEFic3RyYWN0IGJhc2UgY2xhc3MgZm9yIGdyb3VwaW5nIHJlbmRlcmVyc1xyXG4gKlxyXG4gKiBIYW5kbGVzOlxyXG4gKiAtIEZldGNoaW5nIGVudGl0aWVzIGJ5IElEc1xyXG4gKiAtIENhbGN1bGF0aW5nIGNvbHNwYW4gZnJvbSBwYXJlbnRDaGlsZE1hcFxyXG4gKiAtIENyZWF0aW5nIGhlYWRlciBlbGVtZW50c1xyXG4gKiAtIEFwcGVuZGluZyB0byBjb250YWluZXJcclxuICpcclxuICogU3ViY2xhc3NlcyBvdmVycmlkZTpcclxuICogLSByZW5kZXJIZWFkZXIoKSBmb3IgY3VzdG9tIGNvbnRlbnRcclxuICogLSBnZXREaXNwbGF5TmFtZSgpIGZvciBlbnRpdHkgZGlzcGxheSB0ZXh0XHJcbiAqL1xyXG5leHBvcnQgYWJzdHJhY3QgY2xhc3MgQmFzZUdyb3VwaW5nUmVuZGVyZXI8VCBleHRlbmRzIElHcm91cGluZ0VudGl0eT4gaW1wbGVtZW50cyBJUmVuZGVyZXIge1xyXG4gIGFic3RyYWN0IHJlYWRvbmx5IHR5cGU6IHN0cmluZztcclxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgcmVhZG9ubHkgY29uZmlnOiBJR3JvdXBpbmdSZW5kZXJlckNvbmZpZztcclxuXHJcbiAgLyoqXHJcbiAgICogRmV0Y2ggZW50aXRpZXMgZnJvbSBzZXJ2aWNlXHJcbiAgICovXHJcbiAgcHJvdGVjdGVkIGFic3RyYWN0IGdldEVudGl0aWVzKGlkczogc3RyaW5nW10pOiBQcm9taXNlPFRbXT47XHJcblxyXG4gIC8qKlxyXG4gICAqIEdldCBkaXNwbGF5IG5hbWUgZm9yIGVudGl0eVxyXG4gICAqL1xyXG4gIHByb3RlY3RlZCBhYnN0cmFjdCBnZXREaXNwbGF5TmFtZShlbnRpdHk6IFQpOiBzdHJpbmc7XHJcblxyXG4gIC8qKlxyXG4gICAqIE1haW4gcmVuZGVyIG1ldGhvZCAtIGhhbmRsZXMgY29tbW9uIGxvZ2ljXHJcbiAgICovXHJcbiAgYXN5bmMgcmVuZGVyKGNvbnRleHQ6IElSZW5kZXJDb250ZXh0KTogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICBjb25zdCBhbGxvd2VkSWRzID0gY29udGV4dC5maWx0ZXJbdGhpcy50eXBlXSB8fCBbXTtcclxuICAgIGlmIChhbGxvd2VkSWRzLmxlbmd0aCA9PT0gMCkgcmV0dXJuO1xyXG5cclxuICAgIGNvbnN0IGVudGl0aWVzID0gYXdhaXQgdGhpcy5nZXRFbnRpdGllcyhhbGxvd2VkSWRzKTtcclxuICAgIGNvbnN0IGRhdGVDb3VudCA9IGNvbnRleHQuZmlsdGVyWydkYXRlJ10/Lmxlbmd0aCB8fCAxO1xyXG4gICAgY29uc3QgY2hpbGRJZHMgPSBjb250ZXh0LmNoaWxkVHlwZSA/IGNvbnRleHQuZmlsdGVyW2NvbnRleHQuY2hpbGRUeXBlXSB8fCBbXSA6IFtdO1xyXG5cclxuICAgIGZvciAoY29uc3QgZW50aXR5IG9mIGVudGl0aWVzKSB7XHJcbiAgICAgIGNvbnN0IGVudGl0eUNoaWxkSWRzID0gY29udGV4dC5wYXJlbnRDaGlsZE1hcD8uW2VudGl0eS5pZF0gfHwgW107XHJcbiAgICAgIGNvbnN0IGNoaWxkQ291bnQgPSBlbnRpdHlDaGlsZElkcy5maWx0ZXIoaWQgPT4gY2hpbGRJZHMuaW5jbHVkZXMoaWQpKS5sZW5ndGg7XHJcbiAgICAgIGNvbnN0IGNvbHNwYW4gPSBjaGlsZENvdW50ICogZGF0ZUNvdW50O1xyXG5cclxuICAgICAgY29uc3QgaGVhZGVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCh0aGlzLmNvbmZpZy5lbGVtZW50VGFnKTtcclxuICAgICAgaGVhZGVyLmRhdGFzZXRbdGhpcy5jb25maWcuaWRBdHRyaWJ1dGVdID0gZW50aXR5LmlkO1xyXG4gICAgICBoZWFkZXIuc3R5bGUuc2V0UHJvcGVydHkodGhpcy5jb25maWcuY29sc3BhblZhciwgU3RyaW5nKGNvbHNwYW4pKTtcclxuXHJcbiAgICAgIC8vIEFsbG93IHN1YmNsYXNzIHRvIGN1c3RvbWl6ZSBoZWFkZXIgY29udGVudFxyXG4gICAgICB0aGlzLnJlbmRlckhlYWRlcihlbnRpdHksIGhlYWRlciwgY29udGV4dCk7XHJcblxyXG4gICAgICBjb250ZXh0LmhlYWRlckNvbnRhaW5lci5hcHBlbmRDaGlsZChoZWFkZXIpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogT3ZlcnJpZGUgdGhpcyBtZXRob2QgZm9yIGN1c3RvbSBoZWFkZXIgcmVuZGVyaW5nXHJcbiAgICogRGVmYXVsdDoganVzdCBzZXRzIHRleHRDb250ZW50IHRvIGRpc3BsYXkgbmFtZVxyXG4gICAqL1xyXG4gIHByb3RlY3RlZCByZW5kZXJIZWFkZXIoZW50aXR5OiBULCBoZWFkZXI6IEhUTUxFbGVtZW50LCBfY29udGV4dDogSVJlbmRlckNvbnRleHQpOiB2b2lkIHtcclxuICAgIGhlYWRlci50ZXh0Q29udGVudCA9IHRoaXMuZ2V0RGlzcGxheU5hbWUoZW50aXR5KTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIEhlbHBlciB0byByZW5kZXIgYSBzaW5nbGUgZW50aXR5IGhlYWRlci5cclxuICAgKiBDYW4gYmUgdXNlZCBieSBzdWJjbGFzc2VzIHRoYXQgb3ZlcnJpZGUgcmVuZGVyKCkgYnV0IHdhbnQgY29uc2lzdGVudCBoZWFkZXIgY3JlYXRpb24uXHJcbiAgICovXHJcbiAgcHJvdGVjdGVkIGNyZWF0ZUhlYWRlcihlbnRpdHk6IFQsIGNvbnRleHQ6IElSZW5kZXJDb250ZXh0KTogSFRNTEVsZW1lbnQge1xyXG4gICAgY29uc3QgaGVhZGVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCh0aGlzLmNvbmZpZy5lbGVtZW50VGFnKTtcclxuICAgIGhlYWRlci5kYXRhc2V0W3RoaXMuY29uZmlnLmlkQXR0cmlidXRlXSA9IGVudGl0eS5pZDtcclxuICAgIHRoaXMucmVuZGVySGVhZGVyKGVudGl0eSwgaGVhZGVyLCBjb250ZXh0KTtcclxuICAgIHJldHVybiBoZWFkZXI7XHJcbiAgfVxyXG59XHJcbiIsICJpbXBvcnQgeyBJUmVuZGVyQ29udGV4dCB9IGZyb20gJy4uLy4uL2NvcmUvSUdyb3VwaW5nUmVuZGVyZXInO1xyXG5pbXBvcnQgeyBCYXNlR3JvdXBpbmdSZW5kZXJlciwgSUdyb3VwaW5nUmVuZGVyZXJDb25maWcgfSBmcm9tICcuLi8uLi9jb3JlL0Jhc2VHcm91cGluZ1JlbmRlcmVyJztcclxuaW1wb3J0IHsgUmVzb3VyY2VTZXJ2aWNlIH0gZnJvbSAnLi4vLi4vc3RvcmFnZS9yZXNvdXJjZXMvUmVzb3VyY2VTZXJ2aWNlJztcclxuaW1wb3J0IHsgSVJlc291cmNlIH0gZnJvbSAnLi4vLi4vdHlwZXMvQ2FsZW5kYXJUeXBlcyc7XHJcblxyXG5leHBvcnQgY2xhc3MgUmVzb3VyY2VSZW5kZXJlciBleHRlbmRzIEJhc2VHcm91cGluZ1JlbmRlcmVyPElSZXNvdXJjZT4ge1xyXG4gIHJlYWRvbmx5IHR5cGUgPSAncmVzb3VyY2UnO1xyXG5cclxuICBwcm90ZWN0ZWQgcmVhZG9ubHkgY29uZmlnOiBJR3JvdXBpbmdSZW5kZXJlckNvbmZpZyA9IHtcclxuICAgIGVsZW1lbnRUYWc6ICdzd3AtcmVzb3VyY2UtaGVhZGVyJyxcclxuICAgIGlkQXR0cmlidXRlOiAncmVzb3VyY2VJZCcsXHJcbiAgICBjb2xzcGFuVmFyOiAnLS1yZXNvdXJjZS1jb2xzJ1xyXG4gIH07XHJcblxyXG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgcmVzb3VyY2VTZXJ2aWNlOiBSZXNvdXJjZVNlcnZpY2UpIHtcclxuICAgIHN1cGVyKCk7XHJcbiAgfVxyXG5cclxuICBwcm90ZWN0ZWQgZ2V0RW50aXRpZXMoaWRzOiBzdHJpbmdbXSk6IFByb21pc2U8SVJlc291cmNlW10+IHtcclxuICAgIHJldHVybiB0aGlzLnJlc291cmNlU2VydmljZS5nZXRCeUlkcyhpZHMpO1xyXG4gIH1cclxuXHJcbiAgcHJvdGVjdGVkIGdldERpc3BsYXlOYW1lKGVudGl0eTogSVJlc291cmNlKTogc3RyaW5nIHtcclxuICAgIHJldHVybiBlbnRpdHkuZGlzcGxheU5hbWU7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBPdmVycmlkZSByZW5kZXIgdG8gaGFuZGxlOlxyXG4gICAqIDEuIFNwZWNpYWwgb3JkZXJpbmcgd2hlbiBwYXJlbnRDaGlsZE1hcCBleGlzdHMgKHJlc291cmNlcyBncm91cGVkIGJ5IHBhcmVudClcclxuICAgKiAyLiBEaWZmZXJlbnQgY29sc3BhbiBjYWxjdWxhdGlvbiAoanVzdCBkYXRlQ291bnQsIG5vdCBjaGlsZENvdW50ICogZGF0ZUNvdW50KVxyXG4gICAqL1xyXG4gIGFzeW5jIHJlbmRlcihjb250ZXh0OiBJUmVuZGVyQ29udGV4dCk6IFByb21pc2U8dm9pZD4ge1xyXG4gICAgY29uc3QgcmVzb3VyY2VJZHMgPSBjb250ZXh0LmZpbHRlclsncmVzb3VyY2UnXSB8fCBbXTtcclxuICAgIGNvbnN0IGRhdGVDb3VudCA9IGNvbnRleHQuZmlsdGVyWydkYXRlJ10/Lmxlbmd0aCB8fCAxO1xyXG5cclxuICAgIC8vIERldGVybWluZSByZW5kZXIgb3JkZXIgYmFzZWQgb24gcGFyZW50Q2hpbGRNYXBcclxuICAgIC8vIElmIHBhcmVudENoaWxkTWFwIGV4aXN0cywgcmVuZGVyIHJlc291cmNlcyBncm91cGVkIGJ5IHBhcmVudCAoZS5nLiwgdGVhbSlcclxuICAgIC8vIE90aGVyd2lzZSwgcmVuZGVyIGluIGZpbHRlciBvcmRlclxyXG4gICAgbGV0IG9yZGVyZWRSZXNvdXJjZUlkczogc3RyaW5nW107XHJcblxyXG4gICAgaWYgKGNvbnRleHQucGFyZW50Q2hpbGRNYXApIHtcclxuICAgICAgLy8gUmVuZGVyIHJlc291cmNlcyBpbiBwYXJlbnQtY2hpbGQgb3JkZXJcclxuICAgICAgb3JkZXJlZFJlc291cmNlSWRzID0gW107XHJcbiAgICAgIGZvciAoY29uc3QgY2hpbGRJZHMgb2YgT2JqZWN0LnZhbHVlcyhjb250ZXh0LnBhcmVudENoaWxkTWFwKSkge1xyXG4gICAgICAgIGZvciAoY29uc3QgY2hpbGRJZCBvZiBjaGlsZElkcykge1xyXG4gICAgICAgICAgaWYgKHJlc291cmNlSWRzLmluY2x1ZGVzKGNoaWxkSWQpKSB7XHJcbiAgICAgICAgICAgIG9yZGVyZWRSZXNvdXJjZUlkcy5wdXNoKGNoaWxkSWQpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfSBlbHNlIHtcclxuICAgICAgb3JkZXJlZFJlc291cmNlSWRzID0gcmVzb3VyY2VJZHM7XHJcbiAgICB9XHJcblxyXG4gICAgY29uc3QgcmVzb3VyY2VzID0gYXdhaXQgdGhpcy5nZXRFbnRpdGllcyhvcmRlcmVkUmVzb3VyY2VJZHMpO1xyXG5cclxuICAgIC8vIENyZWF0ZSBhIG1hcCBmb3IgcXVpY2sgbG9va3VwIHRvIHByZXNlcnZlIG9yZGVyXHJcbiAgICBjb25zdCByZXNvdXJjZU1hcCA9IG5ldyBNYXAocmVzb3VyY2VzLm1hcChyID0+IFtyLmlkLCByXSkpO1xyXG5cclxuICAgIGZvciAoY29uc3QgcmVzb3VyY2VJZCBvZiBvcmRlcmVkUmVzb3VyY2VJZHMpIHtcclxuICAgICAgY29uc3QgcmVzb3VyY2UgPSByZXNvdXJjZU1hcC5nZXQocmVzb3VyY2VJZCk7XHJcbiAgICAgIGlmICghcmVzb3VyY2UpIGNvbnRpbnVlO1xyXG5cclxuICAgICAgY29uc3QgaGVhZGVyID0gdGhpcy5jcmVhdGVIZWFkZXIocmVzb3VyY2UsIGNvbnRleHQpO1xyXG4gICAgICBoZWFkZXIuc3R5bGUuZ3JpZENvbHVtbiA9IGBzcGFuICR7ZGF0ZUNvdW50fWA7XHJcbiAgICAgIGNvbnRleHQuaGVhZGVyQ29udGFpbmVyLmFwcGVuZENoaWxkKGhlYWRlcik7XHJcbiAgICB9XHJcbiAgfVxyXG59XHJcbiIsICJpbXBvcnQgeyBCYXNlR3JvdXBpbmdSZW5kZXJlciwgSUdyb3VwaW5nUmVuZGVyZXJDb25maWcgfSBmcm9tICcuLi8uLi9jb3JlL0Jhc2VHcm91cGluZ1JlbmRlcmVyJztcclxuaW1wb3J0IHsgVGVhbVNlcnZpY2UgfSBmcm9tICcuLi8uLi9zdG9yYWdlL3RlYW1zL1RlYW1TZXJ2aWNlJztcclxuaW1wb3J0IHsgSVRlYW0gfSBmcm9tICcuLi8uLi90eXBlcy9DYWxlbmRhclR5cGVzJztcclxuXHJcbmV4cG9ydCBjbGFzcyBUZWFtUmVuZGVyZXIgZXh0ZW5kcyBCYXNlR3JvdXBpbmdSZW5kZXJlcjxJVGVhbT4ge1xyXG4gIHJlYWRvbmx5IHR5cGUgPSAndGVhbSc7XHJcblxyXG4gIHByb3RlY3RlZCByZWFkb25seSBjb25maWc6IElHcm91cGluZ1JlbmRlcmVyQ29uZmlnID0ge1xyXG4gICAgZWxlbWVudFRhZzogJ3N3cC10ZWFtLWhlYWRlcicsXHJcbiAgICBpZEF0dHJpYnV0ZTogJ3RlYW1JZCcsXHJcbiAgICBjb2xzcGFuVmFyOiAnLS10ZWFtLWNvbHMnXHJcbiAgfTtcclxuXHJcbiAgY29uc3RydWN0b3IocHJpdmF0ZSB0ZWFtU2VydmljZTogVGVhbVNlcnZpY2UpIHtcclxuICAgIHN1cGVyKCk7XHJcbiAgfVxyXG5cclxuICBwcm90ZWN0ZWQgZ2V0RW50aXRpZXMoaWRzOiBzdHJpbmdbXSk6IFByb21pc2U8SVRlYW1bXT4ge1xyXG4gICAgcmV0dXJuIHRoaXMudGVhbVNlcnZpY2UuZ2V0QnlJZHMoaWRzKTtcclxuICB9XHJcblxyXG4gIHByb3RlY3RlZCBnZXREaXNwbGF5TmFtZShlbnRpdHk6IElUZWFtKTogc3RyaW5nIHtcclxuICAgIHJldHVybiBlbnRpdHkubmFtZTtcclxuICB9XHJcbn1cclxuIiwgImV4cG9ydCBjbGFzcyBUaW1lQXhpc1JlbmRlcmVyIHtcclxuICByZW5kZXIoY29udGFpbmVyOiBIVE1MRWxlbWVudCwgc3RhcnRIb3VyID0gNiwgZW5kSG91ciA9IDIwKTogdm9pZCB7XHJcbiAgICBjb250YWluZXIuaW5uZXJIVE1MID0gJyc7XHJcbiAgICBmb3IgKGxldCBob3VyID0gc3RhcnRIb3VyOyBob3VyIDw9IGVuZEhvdXI7IGhvdXIrKykge1xyXG4gICAgICBjb25zdCBtYXJrZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzd3AtaG91ci1tYXJrZXInKTtcclxuICAgICAgbWFya2VyLnRleHRDb250ZW50ID0gYCR7aG91ci50b1N0cmluZygpLnBhZFN0YXJ0KDIsICcwJyl9OjAwYDtcclxuICAgICAgY29udGFpbmVyLmFwcGVuZENoaWxkKG1hcmtlcik7XHJcbiAgICB9XHJcbiAgfVxyXG59XHJcbiJdLAogICJtYXBwaW5ncyI6ICI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQTtBQUFBO0FBQUEsS0FBQyxTQUFTLEdBQUUsR0FBRTtBQUFDLGtCQUFVLE9BQU8sV0FBUyxlQUFhLE9BQU8sU0FBTyxPQUFPLFVBQVEsRUFBRSxJQUFFLGNBQVksT0FBTyxVQUFRLE9BQU8sTUFBSSxPQUFPLENBQUMsS0FBRyxJQUFFLGVBQWEsT0FBTyxhQUFXLGFB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RSxHQUFFLEdBQUU7QUFBQyxZQUFJLElBQUUsRUFBRTtBQUFVLFVBQUUsTUFBSSxTQUFTcUIsSUFBRTtBQUFDLGNBQUlDLEtBQUUsRUFBQyxNQUFLRCxJQUFFLEtBQUksTUFBRyxNQUFLLFVBQVM7QUFBRSxpQkFBTyxJQUFJLEVBQUVDLEVBQUM7QUFBQSxRQUFDLEdBQUUsRUFBRSxNQUFJLFNBQVNBLElBQUU7QUFBQyxjQUFJQyxLQUFFLEVBQUUsS0FBSyxPQUFPLEdBQUUsRUFBQyxRQUFPLEtBQUssSUFBRyxLQUFJLEtBQUUsQ0FBQztBQUFFLGlCQUFPRCxLQUFFQyxHQUFFLElBQUksS0FBSyxVQUFVLEdBQUUsQ0FBQyxJQUFFQTtBQUFBLFFBQUMsR0FBRSxFQUFFLFFBQU0sV0FBVTtBQUFDLGlCQUFPLEVBQUUsS0FBSyxPQUFPLEdBQUUsRUFBQyxRQUFPLEtBQUssSUFBRyxLQUFJLE1BQUUsQ0FBQztBQUFBLFFBQUM7QUFBRSxZQUFJLElBQUUsRUFBRTtBQUFNLFVBQUUsUUFBTSxTQUFTRixJQUFFO0FBQUMsVUFBQUEsR0FBRSxRQUFNLEtBQUssS0FBRyxPQUFJLEtBQUssT0FBTyxFQUFFLEVBQUVBLEdBQUUsT0FBTyxNQUFJLEtBQUssVUFBUUEsR0FBRSxVQUFTLEVBQUUsS0FBSyxNQUFLQSxFQUFDO0FBQUEsUUFBQztBQUFFLFlBQUksSUFBRSxFQUFFO0FBQUssVUFBRSxPQUFLLFdBQVU7QUFBQyxjQUFHLEtBQUssSUFBRztBQUFDLGdCQUFJQSxLQUFFLEtBQUs7QUFBRyxpQkFBSyxLQUFHQSxHQUFFLGVBQWUsR0FBRSxLQUFLLEtBQUdBLEdBQUUsWUFBWSxHQUFFLEtBQUssS0FBR0EsR0FBRSxXQUFXLEdBQUUsS0FBSyxLQUFHQSxHQUFFLFVBQVUsR0FBRSxLQUFLLEtBQUdBLEdBQUUsWUFBWSxHQUFFLEtBQUssS0FBR0EsR0FBRSxjQUFjLEdBQUUsS0FBSyxLQUFHQSxHQUFFLGNBQWMsR0FBRSxLQUFLLE1BQUlBLEdBQUUsbUJBQW1CO0FBQUEsVUFBQztBQUFNLGNBQUUsS0FBSyxJQUFJO0FB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QVcsR0FBRSxDQUFDO0FBQUEsUUFBQyxHQUE1QyxNQUE4QyxJQUFFLEVBQUU7QUFBVSxVQUFFLGNBQVksV0FBVTtBQUFDLGlCQUFPLEVBQUUsSUFBSSxFQUFFLEtBQUs7QUFBQSxRQUFDLEdBQUUsRUFBRSxVQUFRLFNBQVNBLElBQUU7QUFBQyxjQUFHLENBQUMsS0FBSyxPQUFPLEVBQUUsRUFBRUEsRUFBQztBQUFFLG1CQUFPLEtBQUssSUFBSSxLQUFHQSxLQUFFLEtBQUssUUFBUSxJQUFHLENBQUM7QUFBRSxjQUFJQyxJQUFFQyxJQUFFQyxJQUFFLEdBQUUsSUFBRSxFQUFFLElBQUksR0FBRSxLQUFHRixLQUFFLEtBQUssWUFBWSxHQUFFQyxLQUFFLEtBQUssSUFBR0MsTUFBR0QsS0FBRSxFQUFFLE1BQUksR0FBRyxFQUFFLEtBQUtELEVBQUMsRUFBRSxRQUFRLE1BQU0sR0FBRSxJQUFFLElBQUVFLEdBQUUsV0FBVyxHQUFFQSxHQUFFLFdBQVcsSUFBRSxNQUFJLEtBQUcsSUFBR0EsR0FBRSxJQUFJLEdBQUUsQ0FBQztBQUFHLGlCQUFPLEVBQUUsS0FBSyxHQUFFLE1BQU0sSUFBRTtBQUFBLFFBQUMsR0FBRSxFQUFFLGFBQVcsU0FBU0MsSUFBRTtBQUFDLGlCQUFPLEtBQUssT0FBTyxFQUFFLEVBQUVBLEVBQUMsSUFBRSxLQUFLLElBQUksS0FBRyxJQUFFLEtBQUssSUFBSSxLQUFLLElBQUksSUFBRSxJQUFFQSxLQUFFQSxLQUFFLENBQUM7QUFBQSxRQUFDO0FBQUUsWUFBSSxJQUFFLEVBQUU7QUFBUSxVQUFFLFVBQVEsU0FBU0EsSUFBRUosSUFBRTtBQUFDLGNBQUlDLEtBQUUsS0FBSyxPQUFPLEdBQUVJLEtBQUUsQ0FBQyxDQUFDSixHQUFFLEVBQUVELEVBQUMsS0FBR0E7QUFBRSxpQkFBTSxjQUFZQyxHQUFFLEVBQUVHLEVBQUMsSUFBRUMsS0FBRSxLQUFLLEtBQUssS0FBSyxLQUFLLEtBQUcsS0FBSyxXQUFXLElBQUUsRUFBRSxFQUFFLFFBQVEsS0FBSyxJQUFFLEtBQUssS0FBSyxLQUFLLEtB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R0FBRyxVQUFVLElBQVU7QUFDaEUsY0FBVSxZQUFZO0FBQ3RCLGFBQVMsT0FBTyxXQUFXLFFBQVEsU0FBUyxRQUFRO0FBQ2xELFlBQU0sU0FBUyxTQUFTLGNBQWMsaUJBQWlCO0FBQ3ZELGFBQU8sY0FBYyxHQUFHLEtBQUssU0FBUyxFQUFFLFNBQVMsR0FBRyxHQUFHLENBQUM7QUFDeEQsZ0JBQVUsWUFBWSxNQUFNO0FBQUEsSUFDOUI7QUFBQSxFQUNGO0FBQ0Y7QUFUOEI7QUFBdkIsSUFBTSxtQkFBTjsiLAogICJuYW1lcyI6IFsidCIsICJlIiwgIm4iLCAiciIsICJpIiwgInMiLCAidSIsICJhIiwgIk0iLCAibSIsICJmIiwgImwiLCAiJCIsICJ5IiwgInYiLCAiZyIsICJEIiwgIm8iLCAiZCIsICJjIiwgImgiLCAidCIsICJpIiwgImUiLCAicyIsICJmIiwgIm4iLCAidSIsICJyIiwgIm8iLCAidCIsICJuIiwgImkiLCAibyIsICJyIiwgImUiLCAidSIsICJmIiwgInMiLCAiYSIsICJ0IiwgImkiLCAiZCIsICJuIiwgImUiLCAicyIsICJkYXlqcyIsICJ1dGMiLCAidGltZXpvbmUiLCAiaXNvV2VlayJdCn0K diff --git a/wwwroot/js/calendar.js b/wwwroot/js/calendar.js new file mode 100644 index 0000000..4ebf767 --- /dev/null +++ b/wwwroot/js/calendar.js @@ -0,0 +1,1664 @@ +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); +var __commonJS = (cb, mod) => function __require() { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); + +// node_modules/dayjs/dayjs.min.js +var require_dayjs_min = __commonJS({ + "node_modules/dayjs/dayjs.min.js"(exports, module) { + !function(t, e) { + "object" == typeof exports && "undefined" != typeof module ? module.exports = e() : "function" == typeof define && define.amd ? define(e) : (t = "undefined" != typeof globalThis ? globalThis : t || self).dayjs = e(); + }(exports, function() { + "use strict"; + var t = 1e3, e = 6e4, n = 36e5, r = "millisecond", i = "second", s = "minute", u = "hour", a = "day", o = "week", c = "month", f = "quarter", h = "year", d = "date", l = "Invalid Date", $ = /^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/, y = /\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g, M = { name: "en", weekdays: "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"), months: "January_February_March_April_May_June_July_August_September_October_November_December".split("_"), ordinal: function(t2) { + var e2 = ["th", "st", "nd", "rd"], n2 = t2 % 100; + return "[" + t2 + (e2[(n2 - 20) % 10] || e2[n2] || e2[0]) + "]"; + } }, m = /* @__PURE__ */ __name(function(t2, e2, n2) { + var r2 = String(t2); + return !r2 || r2.length >= e2 ? t2 : "" + Array(e2 + 1 - r2.length).join(n2) + t2; + }, "m"), v = { s: m, z: function(t2) { + var e2 = -t2.utcOffset(), n2 = Math.abs(e2), r2 = Math.floor(n2 / 60), i2 = n2 % 60; + return (e2 <= 0 ? "+" : "-") + m(r2, 2, "0") + ":" + m(i2, 2, "0"); + }, m: /* @__PURE__ */ __name(function t2(e2, n2) { + if (e2.date() < n2.date()) + return -t2(n2, e2); + var r2 = 12 * (n2.year() - e2.year()) + (n2.month() - e2.month()), i2 = e2.clone().add(r2, c), s2 = n2 - i2 < 0, u2 = e2.clone().add(r2 + (s2 ? -1 : 1), c); + return +(-(r2 + (n2 - i2) / (s2 ? i2 - u2 : u2 - i2)) || 0); + }, "t"), a: function(t2) { + return t2 < 0 ? Math.ceil(t2) || 0 : Math.floor(t2); + }, p: function(t2) { + return { M: c, y: h, w: o, d: a, D: d, h: u, m: s, s: i, ms: r, Q: f }[t2] || String(t2 || "").toLowerCase().replace(/s$/, ""); + }, u: function(t2) { + return void 0 === t2; + } }, g = "en", D = {}; + D[g] = M; + var p = "$isDayjsObject", S = /* @__PURE__ */ __name(function(t2) { + return t2 instanceof _ || !(!t2 || !t2[p]); + }, "S"), w = /* @__PURE__ */ __name(function t2(e2, n2, r2) { + var i2; + if (!e2) + return g; + if ("string" == typeof e2) { + var s2 = e2.toLowerCase(); + D[s2] && (i2 = s2), n2 && (D[s2] = n2, i2 = s2); + var u2 = e2.split("-"); + if (!i2 && u2.length > 1) + return t2(u2[0]); + } else { + var a2 = e2.name; + D[a2] = e2, i2 = a2; + } + return !r2 && i2 && (g = i2), i2 || !r2 && g; + }, "t"), O = /* @__PURE__ */ __name(function(t2, e2) { + if (S(t2)) + return t2.clone(); + var n2 = "object" == typeof e2 ? e2 : {}; + return n2.date = t2, n2.args = arguments, new _(n2); + }, "O"), b = v; + b.l = w, b.i = S, b.w = function(t2, e2) { + return O(t2, { locale: e2.$L, utc: e2.$u, x: e2.$x, $offset: e2.$offset }); + }; + var _ = function() { + function M2(t2) { + this.$L = w(t2.locale, null, true), this.parse(t2), this.$x = this.$x || t2.x || {}, this[p] = true; + } + __name(M2, "M"); + var m2 = M2.prototype; + return m2.parse = function(t2) { + this.$d = function(t3) { + var e2 = t3.date, n2 = t3.utc; + if (null === e2) + return /* @__PURE__ */ new Date(NaN); + if (b.u(e2)) + return /* @__PURE__ */ new Date(); + if (e2 instanceof Date) + return new Date(e2); + if ("string" == typeof e2 && !/Z$/i.test(e2)) { + var r2 = e2.match($); + if (r2) { + var i2 = r2[2] - 1 || 0, s2 = (r2[7] || "0").substring(0, 3); + return n2 ? new Date(Date.UTC(r2[1], i2, r2[3] || 1, r2[4] || 0, r2[5] || 0, r2[6] || 0, s2)) : new Date(r2[1], i2, r2[3] || 1, r2[4] || 0, r2[5] || 0, r2[6] || 0, s2); + } + } + return new Date(e2); + }(t2), this.init(); + }, m2.init = function() { + var t2 = this.$d; + this.$y = t2.getFullYear(), this.$M = t2.getMonth(), this.$D = t2.getDate(), this.$W = t2.getDay(), this.$H = t2.getHours(), this.$m = t2.getMinutes(), this.$s = t2.getSeconds(), this.$ms = t2.getMilliseconds(); + }, m2.$utils = function() { + return b; + }, m2.isValid = function() { + return !(this.$d.toString() === l); + }, m2.isSame = function(t2, e2) { + var n2 = O(t2); + return this.startOf(e2) <= n2 && n2 <= this.endOf(e2); + }, m2.isAfter = function(t2, e2) { + return O(t2) < this.startOf(e2); + }, m2.isBefore = function(t2, e2) { + return this.endOf(e2) < O(t2); + }, m2.$g = function(t2, e2, n2) { + return b.u(t2) ? this[e2] : this.set(n2, t2); + }, m2.unix = function() { + return Math.floor(this.valueOf() / 1e3); + }, m2.valueOf = function() { + return this.$d.getTime(); + }, m2.startOf = function(t2, e2) { + var n2 = this, r2 = !!b.u(e2) || e2, f2 = b.p(t2), l2 = /* @__PURE__ */ __name(function(t3, e3) { + var i2 = b.w(n2.$u ? Date.UTC(n2.$y, e3, t3) : new Date(n2.$y, e3, t3), n2); + return r2 ? i2 : i2.endOf(a); + }, "l"), $2 = /* @__PURE__ */ __name(function(t3, e3) { + return b.w(n2.toDate()[t3].apply(n2.toDate("s"), (r2 ? [0, 0, 0, 0] : [23, 59, 59, 999]).slice(e3)), n2); + }, "$"), y2 = this.$W, M3 = this.$M, m3 = this.$D, v2 = "set" + (this.$u ? "UTC" : ""); + switch (f2) { + case h: + return r2 ? l2(1, 0) : l2(31, 11); + case c: + return r2 ? l2(1, M3) : l2(0, M3 + 1); + case o: + var g2 = this.$locale().weekStart || 0, D2 = (y2 < g2 ? y2 + 7 : y2) - g2; + return l2(r2 ? m3 - D2 : m3 + (6 - D2), M3); + case a: + case d: + return $2(v2 + "Hours", 0); + case u: + return $2(v2 + "Minutes", 1); + case s: + return $2(v2 + "Seconds", 2); + case i: + return $2(v2 + "Milliseconds", 3); + default: + return this.clone(); + } + }, m2.endOf = function(t2) { + return this.startOf(t2, false); + }, m2.$set = function(t2, e2) { + var n2, o2 = b.p(t2), f2 = "set" + (this.$u ? "UTC" : ""), l2 = (n2 = {}, n2[a] = f2 + "Date", n2[d] = f2 + "Date", n2[c] = f2 + "Month", n2[h] = f2 + "FullYear", n2[u] = f2 + "Hours", n2[s] = f2 + "Minutes", n2[i] = f2 + "Seconds", n2[r] = f2 + "Milliseconds", n2)[o2], $2 = o2 === a ? this.$D + (e2 - this.$W) : e2; + if (o2 === c || o2 === h) { + var y2 = this.clone().set(d, 1); + y2.$d[l2]($2), y2.init(), this.$d = y2.set(d, Math.min(this.$D, y2.daysInMonth())).$d; + } else + l2 && this.$d[l2]($2); + return this.init(), this; + }, m2.set = function(t2, e2) { + return this.clone().$set(t2, e2); + }, m2.get = function(t2) { + return this[b.p(t2)](); + }, m2.add = function(r2, f2) { + var d2, l2 = this; + r2 = Number(r2); + var $2 = b.p(f2), y2 = /* @__PURE__ */ __name(function(t2) { + var e2 = O(l2); + return b.w(e2.date(e2.date() + Math.round(t2 * r2)), l2); + }, "y"); + if ($2 === c) + return this.set(c, this.$M + r2); + if ($2 === h) + return this.set(h, this.$y + r2); + if ($2 === a) + return y2(1); + if ($2 === o) + return y2(7); + var M3 = (d2 = {}, d2[s] = e, d2[u] = n, d2[i] = t, d2)[$2] || 1, m3 = this.$d.getTime() + r2 * M3; + return b.w(m3, this); + }, m2.subtract = function(t2, e2) { + return this.add(-1 * t2, e2); + }, m2.format = function(t2) { + var e2 = this, n2 = this.$locale(); + if (!this.isValid()) + return n2.invalidDate || l; + var r2 = t2 || "YYYY-MM-DDTHH:mm:ssZ", i2 = b.z(this), s2 = this.$H, u2 = this.$m, a2 = this.$M, o2 = n2.weekdays, c2 = n2.months, f2 = n2.meridiem, h2 = /* @__PURE__ */ __name(function(t3, n3, i3, s3) { + return t3 && (t3[n3] || t3(e2, r2)) || i3[n3].slice(0, s3); + }, "h"), d2 = /* @__PURE__ */ __name(function(t3) { + return b.s(s2 % 12 || 12, t3, "0"); + }, "d"), $2 = f2 || function(t3, e3, n3) { + var r3 = t3 < 12 ? "AM" : "PM"; + return n3 ? r3.toLowerCase() : r3; + }; + return r2.replace(y, function(t3, r3) { + return r3 || function(t4) { + switch (t4) { + case "YY": + return String(e2.$y).slice(-2); + case "YYYY": + return b.s(e2.$y, 4, "0"); + case "M": + return a2 + 1; + case "MM": + return b.s(a2 + 1, 2, "0"); + case "MMM": + return h2(n2.monthsShort, a2, c2, 3); + case "MMMM": + return h2(c2, a2); + case "D": + return e2.$D; + case "DD": + return b.s(e2.$D, 2, "0"); + case "d": + return String(e2.$W); + case "dd": + return h2(n2.weekdaysMin, e2.$W, o2, 2); + case "ddd": + return h2(n2.weekdaysShort, e2.$W, o2, 3); + case "dddd": + return o2[e2.$W]; + case "H": + return String(s2); + case "HH": + return b.s(s2, 2, "0"); + case "h": + return d2(1); + case "hh": + return d2(2); + case "a": + return $2(s2, u2, true); + case "A": + return $2(s2, u2, false); + case "m": + return String(u2); + case "mm": + return b.s(u2, 2, "0"); + case "s": + return String(e2.$s); + case "ss": + return b.s(e2.$s, 2, "0"); + case "SSS": + return b.s(e2.$ms, 3, "0"); + case "Z": + return i2; + } + return null; + }(t3) || i2.replace(":", ""); + }); + }, m2.utcOffset = function() { + return 15 * -Math.round(this.$d.getTimezoneOffset() / 15); + }, m2.diff = function(r2, d2, l2) { + var $2, y2 = this, M3 = b.p(d2), m3 = O(r2), v2 = (m3.utcOffset() - this.utcOffset()) * e, g2 = this - m3, D2 = /* @__PURE__ */ __name(function() { + return b.m(y2, m3); + }, "D"); + switch (M3) { + case h: + $2 = D2() / 12; + break; + case c: + $2 = D2(); + break; + case f: + $2 = D2() / 3; + break; + case o: + $2 = (g2 - v2) / 6048e5; + break; + case a: + $2 = (g2 - v2) / 864e5; + break; + case u: + $2 = g2 / n; + break; + case s: + $2 = g2 / e; + break; + case i: + $2 = g2 / t; + break; + default: + $2 = g2; + } + return l2 ? $2 : b.a($2); + }, m2.daysInMonth = function() { + return this.endOf(c).$D; + }, m2.$locale = function() { + return D[this.$L]; + }, m2.locale = function(t2, e2) { + if (!t2) + return this.$L; + var n2 = this.clone(), r2 = w(t2, e2, true); + return r2 && (n2.$L = r2), n2; + }, m2.clone = function() { + return b.w(this.$d, this); + }, m2.toDate = function() { + return new Date(this.valueOf()); + }, m2.toJSON = function() { + return this.isValid() ? this.toISOString() : null; + }, m2.toISOString = function() { + return this.$d.toISOString(); + }, m2.toString = function() { + return this.$d.toUTCString(); + }, M2; + }(), k = _.prototype; + return O.prototype = k, [["$ms", r], ["$s", i], ["$m", s], ["$H", u], ["$W", a], ["$M", c], ["$y", h], ["$D", d]].forEach(function(t2) { + k[t2[1]] = function(e2) { + return this.$g(e2, t2[0], t2[1]); + }; + }), O.extend = function(t2, e2) { + return t2.$i || (t2(e2, _, O), t2.$i = true), O; + }, O.locale = w, O.isDayjs = S, O.unix = function(t2) { + return O(1e3 * t2); + }, O.en = D[g], O.Ls = D, O.p = {}, O; + }); + } +}); + +// node_modules/dayjs/plugin/utc.js +var require_utc = __commonJS({ + "node_modules/dayjs/plugin/utc.js"(exports, module) { + !function(t, i) { + "object" == typeof exports && "undefined" != typeof module ? module.exports = i() : "function" == typeof define && define.amd ? define(i) : (t = "undefined" != typeof globalThis ? globalThis : t || self).dayjs_plugin_utc = i(); + }(exports, function() { + "use strict"; + var t = "minute", i = /[+-]\d\d(?::?\d\d)?/g, e = /([+-]|\d\d)/g; + return function(s, f, n) { + var u = f.prototype; + n.utc = function(t2) { + var i2 = { date: t2, utc: true, args: arguments }; + return new f(i2); + }, u.utc = function(i2) { + var e2 = n(this.toDate(), { locale: this.$L, utc: true }); + return i2 ? e2.add(this.utcOffset(), t) : e2; + }, u.local = function() { + return n(this.toDate(), { locale: this.$L, utc: false }); + }; + var r = u.parse; + u.parse = function(t2) { + t2.utc && (this.$u = true), this.$utils().u(t2.$offset) || (this.$offset = t2.$offset), r.call(this, t2); + }; + var o = u.init; + u.init = function() { + if (this.$u) { + var t2 = this.$d; + this.$y = t2.getUTCFullYear(), this.$M = t2.getUTCMonth(), this.$D = t2.getUTCDate(), this.$W = t2.getUTCDay(), this.$H = t2.getUTCHours(), this.$m = t2.getUTCMinutes(), this.$s = t2.getUTCSeconds(), this.$ms = t2.getUTCMilliseconds(); + } else + o.call(this); + }; + var a = u.utcOffset; + u.utcOffset = function(s2, f2) { + var n2 = this.$utils().u; + if (n2(s2)) + return this.$u ? 0 : n2(this.$offset) ? a.call(this) : this.$offset; + if ("string" == typeof s2 && (s2 = function(t2) { + void 0 === t2 && (t2 = ""); + var s3 = t2.match(i); + if (!s3) + return null; + var f3 = ("" + s3[0]).match(e) || ["-", 0, 0], n3 = f3[0], u3 = 60 * +f3[1] + +f3[2]; + return 0 === u3 ? 0 : "+" === n3 ? u3 : -u3; + }(s2), null === s2)) + return this; + var u2 = Math.abs(s2) <= 16 ? 60 * s2 : s2; + if (0 === u2) + return this.utc(f2); + var r2 = this.clone(); + if (f2) + return r2.$offset = u2, r2.$u = false, r2; + var o2 = this.$u ? this.toDate().getTimezoneOffset() : -1 * this.utcOffset(); + return (r2 = this.local().add(u2 + o2, t)).$offset = u2, r2.$x.$localOffset = o2, r2; + }; + var h = u.format; + u.format = function(t2) { + var i2 = t2 || (this.$u ? "YYYY-MM-DDTHH:mm:ss[Z]" : ""); + return h.call(this, i2); + }, u.valueOf = function() { + var t2 = this.$utils().u(this.$offset) ? 0 : this.$offset + (this.$x.$localOffset || this.$d.getTimezoneOffset()); + return this.$d.valueOf() - 6e4 * t2; + }, u.isUTC = function() { + return !!this.$u; + }, u.toISOString = function() { + return this.toDate().toISOString(); + }, u.toString = function() { + return this.toDate().toUTCString(); + }; + var l = u.toDate; + u.toDate = function(t2) { + return "s" === t2 && this.$offset ? n(this.format("YYYY-MM-DD HH:mm:ss:SSS")).toDate() : l.call(this); + }; + var c = u.diff; + u.diff = function(t2, i2, e2) { + if (t2 && this.$u === t2.$u) + return c.call(this, t2, i2, e2); + var s2 = this.local(), f2 = n(t2).local(); + return c.call(s2, f2, i2, e2); + }; + }; + }); + } +}); + +// node_modules/dayjs/plugin/timezone.js +var require_timezone = __commonJS({ + "node_modules/dayjs/plugin/timezone.js"(exports, module) { + !function(t, e) { + "object" == typeof exports && "undefined" != typeof module ? module.exports = e() : "function" == typeof define && define.amd ? define(e) : (t = "undefined" != typeof globalThis ? globalThis : t || self).dayjs_plugin_timezone = e(); + }(exports, function() { + "use strict"; + var t = { year: 0, month: 1, day: 2, hour: 3, minute: 4, second: 5 }, e = {}; + return function(n, i, o) { + var r, a = /* @__PURE__ */ __name(function(t2, n2, i2) { + void 0 === i2 && (i2 = {}); + var o2 = new Date(t2), r2 = function(t3, n3) { + void 0 === n3 && (n3 = {}); + var i3 = n3.timeZoneName || "short", o3 = t3 + "|" + i3, r3 = e[o3]; + return r3 || (r3 = new Intl.DateTimeFormat("en-US", { hour12: false, timeZone: t3, year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit", timeZoneName: i3 }), e[o3] = r3), r3; + }(n2, i2); + return r2.formatToParts(o2); + }, "a"), u = /* @__PURE__ */ __name(function(e2, n2) { + for (var i2 = a(e2, n2), r2 = [], u2 = 0; u2 < i2.length; u2 += 1) { + var f2 = i2[u2], s2 = f2.type, m = f2.value, c = t[s2]; + c >= 0 && (r2[c] = parseInt(m, 10)); + } + var d = r2[3], l = 24 === d ? 0 : d, h = r2[0] + "-" + r2[1] + "-" + r2[2] + " " + l + ":" + r2[4] + ":" + r2[5] + ":000", v = +e2; + return (o.utc(h).valueOf() - (v -= v % 1e3)) / 6e4; + }, "u"), f = i.prototype; + f.tz = function(t2, e2) { + void 0 === t2 && (t2 = r); + var n2, i2 = this.utcOffset(), a2 = this.toDate(), u2 = a2.toLocaleString("en-US", { timeZone: t2 }), f2 = Math.round((a2 - new Date(u2)) / 1e3 / 60), s2 = 15 * -Math.round(a2.getTimezoneOffset() / 15) - f2; + if (!Number(s2)) + n2 = this.utcOffset(0, e2); + else if (n2 = o(u2, { locale: this.$L }).$set("millisecond", this.$ms).utcOffset(s2, true), e2) { + var m = n2.utcOffset(); + n2 = n2.add(i2 - m, "minute"); + } + return n2.$x.$timezone = t2, n2; + }, f.offsetName = function(t2) { + var e2 = this.$x.$timezone || o.tz.guess(), n2 = a(this.valueOf(), e2, { timeZoneName: t2 }).find(function(t3) { + return "timezonename" === t3.type.toLowerCase(); + }); + return n2 && n2.value; + }; + var s = f.startOf; + f.startOf = function(t2, e2) { + if (!this.$x || !this.$x.$timezone) + return s.call(this, t2, e2); + var n2 = o(this.format("YYYY-MM-DD HH:mm:ss:SSS"), { locale: this.$L }); + return s.call(n2, t2, e2).tz(this.$x.$timezone, true); + }, o.tz = function(t2, e2, n2) { + var i2 = n2 && e2, a2 = n2 || e2 || r, f2 = u(+o(), a2); + if ("string" != typeof t2) + return o(t2).tz(a2); + var s2 = function(t3, e3, n3) { + var i3 = t3 - 60 * e3 * 1e3, o2 = u(i3, n3); + if (e3 === o2) + return [i3, e3]; + var r2 = u(i3 -= 60 * (o2 - e3) * 1e3, n3); + return o2 === r2 ? [i3, o2] : [t3 - 60 * Math.min(o2, r2) * 1e3, Math.max(o2, r2)]; + }(o.utc(t2, i2).valueOf(), f2, a2), m = s2[0], c = s2[1], d = o(m).utcOffset(c); + return d.$x.$timezone = a2, d; + }, o.tz.guess = function() { + return Intl.DateTimeFormat().resolvedOptions().timeZone; + }, o.tz.setDefault = function(t2) { + r = t2; + }; + }; + }); + } +}); + +// node_modules/dayjs/plugin/isoWeek.js +var require_isoWeek = __commonJS({ + "node_modules/dayjs/plugin/isoWeek.js"(exports, module) { + !function(e, t) { + "object" == typeof exports && "undefined" != typeof module ? module.exports = t() : "function" == typeof define && define.amd ? define(t) : (e = "undefined" != typeof globalThis ? globalThis : e || self).dayjs_plugin_isoWeek = t(); + }(exports, function() { + "use strict"; + var e = "day"; + return function(t, i, s) { + var a = /* @__PURE__ */ __name(function(t2) { + return t2.add(4 - t2.isoWeekday(), e); + }, "a"), d = i.prototype; + d.isoWeekYear = function() { + return a(this).year(); + }, d.isoWeek = function(t2) { + if (!this.$utils().u(t2)) + return this.add(7 * (t2 - this.isoWeek()), e); + var i2, d2, n2, o, r = a(this), u = (i2 = this.isoWeekYear(), d2 = this.$u, n2 = (d2 ? s.utc : s)().year(i2).startOf("year"), o = 4 - n2.isoWeekday(), n2.isoWeekday() > 4 && (o += 7), n2.add(o, e)); + return r.diff(u, "week") + 1; + }, d.isoWeekday = function(e2) { + return this.$utils().u(e2) ? this.day() || 7 : this.day(this.day() % 7 ? e2 : e2 - 7); + }; + var n = d.startOf; + d.startOf = function(e2, t2) { + var i2 = this.$utils(), s2 = !!i2.u(t2) || t2; + return "isoweek" === i2.p(e2) ? s2 ? this.date(this.date() - (this.isoWeekday() - 1)).startOf("day") : this.date(this.date() - 1 - (this.isoWeekday() - 1) + 7).endOf("day") : n.bind(this)(e2, t2); + }; + }; + }); + } +}); + +// src/core/RenderBuilder.ts +function buildPipeline(renderers) { + return { + async run(context) { + for (const renderer of renderers) { + await renderer.render(context); + } + } + }; +} +__name(buildPipeline, "buildPipeline"); + +// src/core/FilterTemplate.ts +var _FilterTemplate = class _FilterTemplate { + constructor(dateService, entityResolver) { + this.dateService = dateService; + this.entityResolver = entityResolver; + this.fields = []; + } + /** + * Tilføj felt til template + * @param idProperty - Property-navn (bruges på både event og column.dataset) + * @param derivedFrom - Hvis feltet udledes fra anden property (f.eks. date fra start) + */ + addField(idProperty, derivedFrom) { + this.fields.push({ idProperty, derivedFrom }); + return this; + } + /** + * Parse dot-notation string into components + * @example 'resource.teamId' → { entityType: 'resource', property: 'teamId', foreignKey: 'resourceId' } + */ + parseDotNotation(idProperty) { + if (!idProperty.includes(".")) + return null; + const [entityType, property] = idProperty.split("."); + return { + entityType, + property, + foreignKey: entityType + "Id" + // Convention: resource → resourceId + }; + } + /** + * Get dataset key for column lookup + * For dot-notation 'resource.teamId', we look for 'teamId' in dataset + */ + getDatasetKey(idProperty) { + const dotNotation = this.parseDotNotation(idProperty); + if (dotNotation) { + return dotNotation.property; + } + return idProperty; + } + /** + * Byg nøgle fra kolonne + * Læser værdier fra column.dataset[idProperty] + * For dot-notation, uses the property part (resource.teamId → teamId) + */ + buildKeyFromColumn(column) { + return this.fields.map((f) => { + const key = this.getDatasetKey(f.idProperty); + return column.dataset[key] || ""; + }).join(":"); + } + /** + * Byg nøgle fra event + * Læser værdier fra event[idProperty] eller udleder fra derivedFrom + * For dot-notation, resolves via EntityResolver + */ + buildKeyFromEvent(event) { + const eventRecord = event; + return this.fields.map((f) => { + const dotNotation = this.parseDotNotation(f.idProperty); + if (dotNotation) { + return this.resolveDotNotation(eventRecord, dotNotation); + } + if (f.derivedFrom) { + const sourceValue = eventRecord[f.derivedFrom]; + if (sourceValue instanceof Date) { + return this.dateService.getDateKey(sourceValue); + } + return String(sourceValue || ""); + } + return String(eventRecord[f.idProperty] || ""); + }).join(":"); + } + /** + * Resolve dot-notation reference via EntityResolver + */ + resolveDotNotation(eventRecord, dotNotation) { + if (!this.entityResolver) { + console.warn(`FilterTemplate: EntityResolver required for dot-notation '${dotNotation.entityType}.${dotNotation.property}'`); + return ""; + } + const foreignId = eventRecord[dotNotation.foreignKey]; + if (!foreignId) + return ""; + const entity = this.entityResolver.resolve(dotNotation.entityType, String(foreignId)); + if (!entity) + return ""; + return String(entity[dotNotation.property] || ""); + } + /** + * Match event mod kolonne + */ + matches(event, column) { + return this.buildKeyFromEvent(event) === this.buildKeyFromColumn(column); + } +}; +__name(_FilterTemplate, "FilterTemplate"); +var FilterTemplate = _FilterTemplate; + +// src/core/CalendarOrchestrator.ts +var _CalendarOrchestrator = class _CalendarOrchestrator { + constructor(allRenderers, eventRenderer, scheduleRenderer, headerDrawerRenderer, dateService, entityServices) { + this.allRenderers = allRenderers; + this.eventRenderer = eventRenderer; + this.scheduleRenderer = scheduleRenderer; + this.headerDrawerRenderer = headerDrawerRenderer; + this.dateService = dateService; + this.entityServices = entityServices; + } + async render(viewConfig, container) { + const headerContainer = container.querySelector("swp-calendar-header"); + const columnContainer = container.querySelector("swp-day-columns"); + if (!headerContainer || !columnContainer) { + throw new Error("Missing swp-calendar-header or swp-day-columns"); + } + const filter = {}; + for (const grouping of viewConfig.groupings) { + filter[grouping.type] = grouping.values; + } + const filterTemplate = new FilterTemplate(this.dateService); + for (const grouping of viewConfig.groupings) { + if (grouping.idProperty) { + filterTemplate.addField(grouping.idProperty, grouping.derivedFrom); + } + } + const { parentChildMap, childType } = await this.resolveBelongsTo(viewConfig.groupings, filter); + const context = { headerContainer, columnContainer, filter, groupings: viewConfig.groupings, parentChildMap, childType }; + headerContainer.innerHTML = ""; + columnContainer.innerHTML = ""; + const levels = viewConfig.groupings.map((g) => g.type).join(" "); + headerContainer.dataset.levels = levels; + const activeRenderers = this.selectRenderers(viewConfig); + const pipeline = buildPipeline(activeRenderers); + await pipeline.run(context); + await this.scheduleRenderer.render(container, filter); + await this.eventRenderer.render(container, filter, filterTemplate); + await this.headerDrawerRenderer.render(container, filter, filterTemplate); + } + selectRenderers(viewConfig) { + const types = viewConfig.groupings.map((g) => g.type); + return types.map((type) => this.allRenderers.find((r) => r.type === type)).filter((r) => r !== void 0); + } + /** + * Resolve belongsTo relations to build parent-child map + * e.g., belongsTo: 'team.resourceIds' → { team1: ['EMP001', 'EMP002'], team2: [...] } + * Also returns the childType (the grouping type that has belongsTo) + */ + async resolveBelongsTo(groupings, filter) { + const childGrouping = groupings.find((g) => g.belongsTo); + if (!childGrouping?.belongsTo) + return {}; + const [entityType, property] = childGrouping.belongsTo.split("."); + if (!entityType || !property) + return {}; + const parentIds = filter[entityType] || []; + if (parentIds.length === 0) + return {}; + const service = this.entityServices.find( + (s) => s.entityType.toLowerCase() === entityType + ); + if (!service) + return {}; + const allEntities = await service.getAll(); + const entities = allEntities.filter( + (e) => parentIds.includes(e.id) + ); + const map = {}; + for (const entity of entities) { + const entityRecord = entity; + const children = entityRecord[property] || []; + map[entityRecord.id] = children; + } + return { parentChildMap: map, childType: childGrouping.type }; + } +}; +__name(_CalendarOrchestrator, "CalendarOrchestrator"); +var CalendarOrchestrator = _CalendarOrchestrator; + +// src/core/NavigationAnimator.ts +var _NavigationAnimator = class _NavigationAnimator { + constructor(headerTrack, contentTrack, headerDrawer) { + this.headerTrack = headerTrack; + this.contentTrack = contentTrack; + this.headerDrawer = headerDrawer; + } + async slide(direction, renderFn) { + const out = direction === "left" ? "-100%" : "100%"; + const into = direction === "left" ? "100%" : "-100%"; + await this.animateOut(out); + await renderFn(); + await this.animateIn(into); + } + async animateOut(translate) { + const animations = [ + this.headerTrack.animate( + [{ transform: "translateX(0)" }, { transform: `translateX(${translate})` }], + { duration: 200, easing: "ease-in" } + ).finished, + this.contentTrack.animate( + [{ transform: "translateX(0)" }, { transform: `translateX(${translate})` }], + { duration: 200, easing: "ease-in" } + ).finished + ]; + if (this.headerDrawer) { + animations.push( + this.headerDrawer.animate( + [{ transform: "translateX(0)" }, { transform: `translateX(${translate})` }], + { duration: 200, easing: "ease-in" } + ).finished + ); + } + await Promise.all(animations); + } + async animateIn(translate) { + const animations = [ + this.headerTrack.animate( + [{ transform: `translateX(${translate})` }, { transform: "translateX(0)" }], + { duration: 200, easing: "ease-out" } + ).finished, + this.contentTrack.animate( + [{ transform: `translateX(${translate})` }, { transform: "translateX(0)" }], + { duration: 200, easing: "ease-out" } + ).finished + ]; + if (this.headerDrawer) { + animations.push( + this.headerDrawer.animate( + [{ transform: `translateX(${translate})` }, { transform: "translateX(0)" }], + { duration: 200, easing: "ease-out" } + ).finished + ); + } + await Promise.all(animations); + } +}; +__name(_NavigationAnimator, "NavigationAnimator"); +var NavigationAnimator = _NavigationAnimator; + +// src/features/date/DateRenderer.ts +var _DateRenderer = class _DateRenderer { + constructor(dateService) { + this.dateService = dateService; + this.type = "date"; + } + render(context) { + const dates = context.filter["date"] || []; + const resourceIds = context.filter["resource"] || []; + const dateGrouping = context.groupings?.find((g) => g.type === "date"); + const hideHeader = dateGrouping?.hideHeader === true; + const iterations = resourceIds.length || 1; + let columnCount = 0; + for (let r = 0; r < iterations; r++) { + const resourceId = resourceIds[r]; + for (const dateStr of dates) { + const date = this.dateService.parseISO(dateStr); + const segments = { date: dateStr }; + if (resourceId) + segments.resource = resourceId; + const columnKey = this.dateService.buildColumnKey(segments); + const header = document.createElement("swp-day-header"); + header.dataset.date = dateStr; + header.dataset.columnKey = columnKey; + if (resourceId) { + header.dataset.resourceId = resourceId; + } + if (hideHeader) { + header.dataset.hidden = "true"; + } + header.innerHTML = ` + ${this.dateService.getDayName(date, "short")} + ${date.getDate()} + `; + context.headerContainer.appendChild(header); + const column = document.createElement("swp-day-column"); + column.dataset.date = dateStr; + column.dataset.columnKey = columnKey; + if (resourceId) { + column.dataset.resourceId = resourceId; + } + column.innerHTML = ""; + context.columnContainer.appendChild(column); + columnCount++; + } + } + const container = context.columnContainer.closest("swp-calendar-container"); + if (container) { + container.style.setProperty("--grid-columns", String(columnCount)); + } + } +}; +__name(_DateRenderer, "DateRenderer"); +var DateRenderer = _DateRenderer; + +// src/core/DateService.ts +var import_dayjs = __toESM(require_dayjs_min(), 1); +var import_utc = __toESM(require_utc(), 1); +var import_timezone = __toESM(require_timezone(), 1); +var import_isoWeek = __toESM(require_isoWeek(), 1); +import_dayjs.default.extend(import_utc.default); +import_dayjs.default.extend(import_timezone.default); +import_dayjs.default.extend(import_isoWeek.default); +var _DateService = class _DateService { + constructor(config, baseDate) { + this.config = config; + this.timezone = config.timezone; + this.baseDate = baseDate ? (0, import_dayjs.default)(baseDate) : (0, import_dayjs.default)(); + } + /** + * Set a fixed base date (useful for demos with static mock data) + */ + setBaseDate(date) { + this.baseDate = (0, import_dayjs.default)(date); + } + /** + * Get the current base date (either fixed or today) + */ + getBaseDate() { + return this.baseDate.toDate(); + } + parseISO(isoString) { + return (0, import_dayjs.default)(isoString).toDate(); + } + getDayName(date, format = "short") { + return new Intl.DateTimeFormat(this.config.locale, { weekday: format }).format(date); + } + /** + * Get dates starting from a day offset + * @param dayOffset - Day offset from base date + * @param count - Number of consecutive days to return + * @returns Array of date strings in YYYY-MM-DD format + */ + getDatesFromOffset(dayOffset, count) { + const startDate = this.baseDate.add(dayOffset, "day"); + return Array.from( + { length: count }, + (_, i) => startDate.add(i, "day").format("YYYY-MM-DD") + ); + } + /** + * Get specific weekdays from the week containing the offset date + * @param dayOffset - Day offset from base date + * @param workDays - Array of ISO weekday numbers (1=Monday, 7=Sunday) + * @returns Array of date strings in YYYY-MM-DD format + */ + getWorkDaysFromOffset(dayOffset, workDays) { + const targetDate = this.baseDate.add(dayOffset, "day"); + const monday = targetDate.startOf("week").add(1, "day"); + return workDays.map((isoDay) => { + const daysFromMonday = isoDay === 7 ? 6 : isoDay - 1; + return monday.add(daysFromMonday, "day").format("YYYY-MM-DD"); + }); + } + // Legacy methods for backwards compatibility + getWeekDates(weekOffset = 0, days = 7) { + return this.getDatesFromOffset(weekOffset * 7, days); + } + getWorkWeekDates(weekOffset, workDays) { + return this.getWorkDaysFromOffset(weekOffset * 7, workDays); + } + // ============================================ + // FORMATTING + // ============================================ + formatTime(date, showSeconds = false) { + const pattern = showSeconds ? "HH:mm:ss" : "HH:mm"; + return (0, import_dayjs.default)(date).format(pattern); + } + formatTimeRange(start, end) { + return `${this.formatTime(start)} - ${this.formatTime(end)}`; + } + formatDate(date) { + return (0, import_dayjs.default)(date).format("YYYY-MM-DD"); + } + getDateKey(date) { + return this.formatDate(date); + } + // ============================================ + // COLUMN KEY + // ============================================ + /** + * Build a uniform columnKey from grouping segments + * Handles any combination of date, resource, team, etc. + * + * @example + * buildColumnKey({ date: '2025-12-09' }) → "2025-12-09" + * buildColumnKey({ date: '2025-12-09', resource: 'EMP001' }) → "2025-12-09:EMP001" + */ + buildColumnKey(segments) { + const date = segments.date; + const others = Object.entries(segments).filter(([k]) => k !== "date").sort(([a], [b]) => a.localeCompare(b)).map(([, v]) => v); + return date ? [date, ...others].join(":") : others.join(":"); + } + /** + * Parse a columnKey back into segments + * Assumes format: "date:resource:..." or just "date" + */ + parseColumnKey(columnKey) { + const parts = columnKey.split(":"); + return { + date: parts[0], + resource: parts[1] + }; + } + /** + * Extract dateKey from columnKey (first segment) + */ + getDateFromColumnKey(columnKey) { + return columnKey.split(":")[0]; + } + // ============================================ + // TIME CALCULATIONS + // ============================================ + timeToMinutes(timeString) { + const parts = timeString.split(":").map(Number); + const hours = parts[0] || 0; + const minutes = parts[1] || 0; + return hours * 60 + minutes; + } + minutesToTime(totalMinutes) { + const hours = Math.floor(totalMinutes / 60); + const minutes = totalMinutes % 60; + return (0, import_dayjs.default)().hour(hours).minute(minutes).format("HH:mm"); + } + getMinutesSinceMidnight(date) { + const d = (0, import_dayjs.default)(date); + return d.hour() * 60 + d.minute(); + } + // ============================================ + // UTC CONVERSIONS + // ============================================ + toUTC(localDate) { + return import_dayjs.default.tz(localDate, this.timezone).utc().toISOString(); + } + fromUTC(utcString) { + return import_dayjs.default.utc(utcString).tz(this.timezone).toDate(); + } + // ============================================ + // DATE CREATION + // ============================================ + createDateAtTime(baseDate, timeString) { + const totalMinutes = this.timeToMinutes(timeString); + const hours = Math.floor(totalMinutes / 60); + const minutes = totalMinutes % 60; + return (0, import_dayjs.default)(baseDate).startOf("day").hour(hours).minute(minutes).toDate(); + } + getISOWeekDay(date) { + return (0, import_dayjs.default)(date).isoWeekday(); + } +}; +__name(_DateService, "DateService"); +var DateService = _DateService; + +// src/utils/PositionUtils.ts +function calculateEventPosition(start, end, config) { + const startMinutes = start.getHours() * 60 + start.getMinutes(); + const endMinutes = end.getHours() * 60 + end.getMinutes(); + const dayStartMinutes = config.dayStartHour * 60; + const minuteHeight = config.hourHeight / 60; + const top = (startMinutes - dayStartMinutes) * minuteHeight; + const height = (endMinutes - startMinutes) * minuteHeight; + return { top, height }; +} +__name(calculateEventPosition, "calculateEventPosition"); +function minutesToPixels(minutes, config) { + return minutes / 60 * config.hourHeight; +} +__name(minutesToPixels, "minutesToPixels"); +function pixelsToMinutes(pixels, config) { + return pixels / config.hourHeight * 60; +} +__name(pixelsToMinutes, "pixelsToMinutes"); +function snapToGrid(pixels, config) { + const snapPixels = minutesToPixels(config.snapInterval, config); + return Math.round(pixels / snapPixels) * snapPixels; +} +__name(snapToGrid, "snapToGrid"); + +// src/constants/CoreEvents.ts +var CoreEvents = { + // Lifecycle events + INITIALIZED: "core:initialized", + READY: "core:ready", + DESTROYED: "core:destroyed", + // View events + VIEW_CHANGED: "view:changed", + VIEW_RENDERED: "view:rendered", + // Navigation events + DATE_CHANGED: "nav:date-changed", + NAVIGATION_COMPLETED: "nav:navigation-completed", + // Data events + DATA_LOADING: "data:loading", + DATA_LOADED: "data:loaded", + DATA_ERROR: "data:error", + // Grid events + GRID_RENDERED: "grid:rendered", + GRID_CLICKED: "grid:clicked", + // Event management + EVENT_CREATED: "event:created", + EVENT_UPDATED: "event:updated", + EVENT_DELETED: "event:deleted", + EVENT_SELECTED: "event:selected", + // Event drag-drop + EVENT_DRAG_START: "event:drag-start", + EVENT_DRAG_MOVE: "event:drag-move", + EVENT_DRAG_END: "event:drag-end", + EVENT_DRAG_CANCEL: "event:drag-cancel", + EVENT_DRAG_COLUMN_CHANGE: "event:drag-column-change", + // Header drag (timed → header conversion) + EVENT_DRAG_ENTER_HEADER: "event:drag-enter-header", + EVENT_DRAG_MOVE_HEADER: "event:drag-move-header", + EVENT_DRAG_LEAVE_HEADER: "event:drag-leave-header", + // Event resize + EVENT_RESIZE_START: "event:resize-start", + EVENT_RESIZE_END: "event:resize-end", + // Edge scroll + EDGE_SCROLL_TICK: "edge-scroll:tick", + EDGE_SCROLL_STARTED: "edge-scroll:started", + EDGE_SCROLL_STOPPED: "edge-scroll:stopped", + // System events + ERROR: "system:error", + // Sync events + SYNC_STARTED: "sync:started", + SYNC_COMPLETED: "sync:completed", + SYNC_FAILED: "sync:failed", + // Entity events - for audit and sync + ENTITY_SAVED: "entity:saved", + ENTITY_DELETED: "entity:deleted", + // Audit events + AUDIT_LOGGED: "audit:logged", + // Rendering events + EVENTS_RENDERED: "events:rendered" +}; + +// src/features/event/EventLayoutEngine.ts +function eventsOverlap(a, b) { + return a.start < b.end && a.end > b.start; +} +__name(eventsOverlap, "eventsOverlap"); +function eventsWithinThreshold(a, b, thresholdMinutes) { + const thresholdMs = thresholdMinutes * 60 * 1e3; + const startToStartDiff = Math.abs(a.start.getTime() - b.start.getTime()); + if (startToStartDiff <= thresholdMs) + return true; + const bStartsBeforeAEnds = a.end.getTime() - b.start.getTime(); + if (bStartsBeforeAEnds > 0 && bStartsBeforeAEnds <= thresholdMs) + return true; + const aStartsBeforeBEnds = b.end.getTime() - a.start.getTime(); + if (aStartsBeforeBEnds > 0 && aStartsBeforeBEnds <= thresholdMs) + return true; + return false; +} +__name(eventsWithinThreshold, "eventsWithinThreshold"); +function findOverlapGroups(events) { + if (events.length === 0) + return []; + const sorted = [...events].sort((a, b) => a.start.getTime() - b.start.getTime()); + const used = /* @__PURE__ */ new Set(); + const groups = []; + for (const event of sorted) { + if (used.has(event.id)) + continue; + const group = [event]; + used.add(event.id); + let expanded = true; + while (expanded) { + expanded = false; + for (const candidate of sorted) { + if (used.has(candidate.id)) + continue; + const connects = group.some((member) => eventsOverlap(member, candidate)); + if (connects) { + group.push(candidate); + used.add(candidate.id); + expanded = true; + } + } + } + groups.push(group); + } + return groups; +} +__name(findOverlapGroups, "findOverlapGroups"); +function findGridCandidates(events, thresholdMinutes) { + if (events.length === 0) + return []; + const sorted = [...events].sort((a, b) => a.start.getTime() - b.start.getTime()); + const used = /* @__PURE__ */ new Set(); + const groups = []; + for (const event of sorted) { + if (used.has(event.id)) + continue; + const group = [event]; + used.add(event.id); + let expanded = true; + while (expanded) { + expanded = false; + for (const candidate of sorted) { + if (used.has(candidate.id)) + continue; + const connects = group.some( + (member) => eventsWithinThreshold(member, candidate, thresholdMinutes) + ); + if (connects) { + group.push(candidate); + used.add(candidate.id); + expanded = true; + } + } + } + groups.push(group); + } + return groups; +} +__name(findGridCandidates, "findGridCandidates"); +function calculateStackLevels(events) { + const levels = /* @__PURE__ */ new Map(); + const sorted = [...events].sort((a, b) => a.start.getTime() - b.start.getTime()); + for (const event of sorted) { + let maxOverlappingLevel = -1; + for (const [id, level] of levels) { + const other = events.find((e) => e.id === id); + if (other && eventsOverlap(event, other)) { + maxOverlappingLevel = Math.max(maxOverlappingLevel, level); + } + } + levels.set(event.id, maxOverlappingLevel + 1); + } + return levels; +} +__name(calculateStackLevels, "calculateStackLevels"); +function allocateColumns(events) { + const sorted = [...events].sort((a, b) => a.start.getTime() - b.start.getTime()); + const columns = []; + for (const event of sorted) { + let placed = false; + for (const column of columns) { + const canFit = !column.some((e) => eventsOverlap(event, e)); + if (canFit) { + column.push(event); + placed = true; + break; + } + } + if (!placed) { + columns.push([event]); + } + } + return columns; +} +__name(allocateColumns, "allocateColumns"); +function calculateColumnLayout(events, config) { + const thresholdMinutes = config.gridStartThresholdMinutes ?? 10; + const result = { + grids: [], + stacked: [] + }; + if (events.length === 0) + return result; + const overlapGroups = findOverlapGroups(events); + for (const overlapGroup of overlapGroups) { + if (overlapGroup.length === 1) { + result.stacked.push({ + event: overlapGroup[0], + stackLevel: 0 + }); + continue; + } + const gridSubgroups = findGridCandidates(overlapGroup, thresholdMinutes); + const largestGridCandidate = gridSubgroups.reduce((max, g) => g.length > max.length ? g : max, gridSubgroups[0]); + if (largestGridCandidate.length === overlapGroup.length) { + const columns = allocateColumns(overlapGroup); + const earliest = overlapGroup.reduce((min, e) => e.start < min.start ? e : min, overlapGroup[0]); + const position = calculateEventPosition(earliest.start, earliest.end, config); + result.grids.push({ + events: overlapGroup, + columns, + stackLevel: 0, + position: { top: position.top } + }); + } else { + const levels = calculateStackLevels(overlapGroup); + for (const event of overlapGroup) { + result.stacked.push({ + event, + stackLevel: levels.get(event.id) ?? 0 + }); + } + } + } + return result; +} +__name(calculateColumnLayout, "calculateColumnLayout"); + +// src/features/event/EventRenderer.ts +var _EventRenderer = class _EventRenderer { + constructor(eventService, dateService, gridConfig, eventBus) { + this.eventService = eventService; + this.dateService = dateService; + this.gridConfig = gridConfig; + this.eventBus = eventBus; + this.container = null; + this.setupListeners(); + } + /** + * Setup listeners for drag-drop and update events + */ + setupListeners() { + this.eventBus.on(CoreEvents.EVENT_DRAG_COLUMN_CHANGE, (e) => { + const payload = e.detail; + this.handleColumnChange(payload); + }); + this.eventBus.on(CoreEvents.EVENT_DRAG_MOVE, (e) => { + const payload = e.detail; + this.updateDragTimestamp(payload); + }); + this.eventBus.on(CoreEvents.EVENT_UPDATED, (e) => { + const payload = e.detail; + this.handleEventUpdated(payload); + }); + this.eventBus.on(CoreEvents.EVENT_DRAG_END, (e) => { + const payload = e.detail; + this.handleDragEnd(payload); + }); + this.eventBus.on(CoreEvents.EVENT_DRAG_LEAVE_HEADER, (e) => { + const payload = e.detail; + this.handleDragLeaveHeader(payload); + }); + } + /** + * Handle EVENT_DRAG_END - remove element if dropped in header + */ + handleDragEnd(payload) { + if (payload.target === "header") { + const element = this.container?.querySelector(`swp-content-viewport swp-event[data-event-id="${payload.swpEvent.eventId}"]`); + element?.remove(); + } + } + /** + * Handle header item leaving header - create swp-event in grid + */ + handleDragLeaveHeader(payload) { + if (payload.source !== "header") + return; + if (!payload.targetColumn || !payload.start || !payload.end) + return; + if (payload.element) { + payload.element.classList.add("drag-ghost"); + payload.element.style.opacity = "0.3"; + payload.element.style.pointerEvents = "none"; + } + const event = { + id: payload.eventId, + title: payload.title || "", + description: "", + start: payload.start, + end: payload.end, + type: "customer", + allDay: false, + syncStatus: "pending" + }; + const element = this.createEventElement(event); + let eventsLayer = payload.targetColumn.querySelector("swp-events-layer"); + if (!eventsLayer) { + eventsLayer = document.createElement("swp-events-layer"); + payload.targetColumn.appendChild(eventsLayer); + } + eventsLayer.appendChild(element); + element.classList.add("dragging"); + } + /** + * Handle EVENT_UPDATED - re-render affected columns + */ + async handleEventUpdated(payload) { + if (payload.sourceColumnKey !== payload.targetColumnKey) { + await this.rerenderColumn(payload.sourceColumnKey); + } + await this.rerenderColumn(payload.targetColumnKey); + } + /** + * Re-render a single column with fresh data from IndexedDB + */ + async rerenderColumn(columnKey) { + const column = this.findColumn(columnKey); + if (!column) + return; + const date = column.dataset.date; + const resourceId = column.dataset.resourceId; + if (!date) + return; + const startDate = new Date(date); + const endDate = new Date(date); + endDate.setHours(23, 59, 59, 999); + const events = resourceId ? await this.eventService.getByResourceAndDateRange(resourceId, startDate, endDate) : await this.eventService.getByDateRange(startDate, endDate); + const timedEvents = events.filter( + (event) => !event.allDay && this.dateService.getDateKey(event.start) === date + ); + let eventsLayer = column.querySelector("swp-events-layer"); + if (!eventsLayer) { + eventsLayer = document.createElement("swp-events-layer"); + column.appendChild(eventsLayer); + } + eventsLayer.innerHTML = ""; + const layout = calculateColumnLayout(timedEvents, this.gridConfig); + layout.grids.forEach((grid) => { + const groupEl = this.renderGridGroup(grid); + eventsLayer.appendChild(groupEl); + }); + layout.stacked.forEach((item) => { + const eventEl = this.renderStackedEvent(item.event, item.stackLevel); + eventsLayer.appendChild(eventEl); + }); + } + /** + * Find a column element by columnKey + */ + findColumn(columnKey) { + if (!this.container) + return null; + return this.container.querySelector(`swp-day-column[data-column-key="${columnKey}"]`); + } + /** + * Handle event moving to a new column during drag + */ + handleColumnChange(payload) { + const eventsLayer = payload.newColumn.querySelector("swp-events-layer"); + if (!eventsLayer) + return; + eventsLayer.appendChild(payload.element); + payload.element.style.top = `${payload.currentY}px`; + } + /** + * Update timestamp display during drag (snapped to grid) + */ + updateDragTimestamp(payload) { + const timeEl = payload.element.querySelector("swp-event-time"); + if (!timeEl) + return; + const snappedY = snapToGrid(payload.currentY, this.gridConfig); + const minutesFromGridStart = pixelsToMinutes(snappedY, this.gridConfig); + const startMinutes = this.gridConfig.dayStartHour * 60 + minutesFromGridStart; + const height = parseFloat(payload.element.style.height) || this.gridConfig.hourHeight; + const durationMinutes = pixelsToMinutes(height, this.gridConfig); + const start = this.minutesToDate(startMinutes); + const end = this.minutesToDate(startMinutes + durationMinutes); + timeEl.textContent = this.dateService.formatTimeRange(start, end); + } + /** + * Convert minutes since midnight to a Date object (today) + */ + minutesToDate(minutes) { + const date = /* @__PURE__ */ new Date(); + date.setHours(Math.floor(minutes / 60) % 24, minutes % 60, 0, 0); + return date; + } + /** + * Render events for visible dates into day columns + * @param container - Calendar container element + * @param filter - Filter with 'date' and optionally 'resource' arrays + * @param filterTemplate - Template for matching events to columns + */ + async render(container, filter, filterTemplate) { + this.container = container; + const visibleDates = filter["date"] || []; + if (visibleDates.length === 0) + return; + const startDate = new Date(visibleDates[0]); + const endDate = new Date(visibleDates[visibleDates.length - 1]); + endDate.setHours(23, 59, 59, 999); + const events = await this.eventService.getByDateRange(startDate, endDate); + const dayColumns = container.querySelector("swp-day-columns"); + if (!dayColumns) + return; + const columns = dayColumns.querySelectorAll("swp-day-column"); + columns.forEach((column) => { + const columnEl = column; + const columnEvents = events.filter((event) => filterTemplate.matches(event, columnEl)); + let eventsLayer = column.querySelector("swp-events-layer"); + if (!eventsLayer) { + eventsLayer = document.createElement("swp-events-layer"); + column.appendChild(eventsLayer); + } + eventsLayer.innerHTML = ""; + const timedEvents = columnEvents.filter((event) => !event.allDay); + const layout = calculateColumnLayout(timedEvents, this.gridConfig); + layout.grids.forEach((grid) => { + const groupEl = this.renderGridGroup(grid); + eventsLayer.appendChild(groupEl); + }); + layout.stacked.forEach((item) => { + const eventEl = this.renderStackedEvent(item.event, item.stackLevel); + eventsLayer.appendChild(eventEl); + }); + }); + } + /** + * Create a single event element + * + * CLEAN approach: + * - Only data-id for lookup + * - Visible content in innerHTML only + */ + createEventElement(event) { + const element = document.createElement("swp-event"); + element.dataset.eventId = event.id; + if (event.resourceId) { + element.dataset.resourceId = event.resourceId; + } + const position = calculateEventPosition(event.start, event.end, this.gridConfig); + element.style.top = `${position.top}px`; + element.style.height = `${position.height}px`; + const colorClass = this.getColorClass(event); + if (colorClass) { + element.classList.add(colorClass); + } + element.innerHTML = ` + ${this.dateService.formatTimeRange(event.start, event.end)} + ${this.escapeHtml(event.title)} + ${event.description ? `${this.escapeHtml(event.description)}` : ""} + `; + return element; + } + /** + * Get color class based on metadata.color or event type + */ + getColorClass(event) { + if (event.metadata?.color) { + return `is-${event.metadata.color}`; + } + const typeColors = { + "customer": "is-blue", + "vacation": "is-green", + "break": "is-amber", + "meeting": "is-purple", + "blocked": "is-red" + }; + return typeColors[event.type] || "is-blue"; + } + /** + * Escape HTML to prevent XSS + */ + escapeHtml(text) { + const div = document.createElement("div"); + div.textContent = text; + return div.innerHTML; + } + /** + * Render a GRID group with side-by-side columns + * Used when multiple events start at the same time + */ + renderGridGroup(layout) { + const group = document.createElement("swp-event-group"); + group.classList.add(`cols-${layout.columns.length}`); + group.style.top = `${layout.position.top}px`; + if (layout.stackLevel > 0) { + group.style.marginLeft = `${layout.stackLevel * 15}px`; + group.style.zIndex = `${100 + layout.stackLevel}`; + } + let maxBottom = 0; + for (const event of layout.events) { + const pos = calculateEventPosition(event.start, event.end, this.gridConfig); + const eventBottom = pos.top + pos.height; + if (eventBottom > maxBottom) + maxBottom = eventBottom; + } + const groupHeight = maxBottom - layout.position.top; + group.style.height = `${groupHeight}px`; + layout.columns.forEach((columnEvents) => { + const wrapper = document.createElement("div"); + wrapper.style.position = "relative"; + columnEvents.forEach((event) => { + const eventEl = this.createEventElement(event); + const pos = calculateEventPosition(event.start, event.end, this.gridConfig); + eventEl.style.top = `${pos.top - layout.position.top}px`; + eventEl.style.position = "absolute"; + eventEl.style.left = "0"; + eventEl.style.right = "0"; + wrapper.appendChild(eventEl); + }); + group.appendChild(wrapper); + }); + return group; + } + /** + * Render a STACKED event with margin-left offset + * Used for overlapping events that don't start at the same time + */ + renderStackedEvent(event, stackLevel) { + const element = this.createEventElement(event); + element.dataset.stackLink = JSON.stringify({ stackLevel }); + if (stackLevel > 0) { + element.style.marginLeft = `${stackLevel * 15}px`; + element.style.zIndex = `${100 + stackLevel}`; + } + return element; + } +}; +__name(_EventRenderer, "EventRenderer"); +var EventRenderer = _EventRenderer; + +// src/core/BaseGroupingRenderer.ts +var _BaseGroupingRenderer = class _BaseGroupingRenderer { + /** + * Main render method - handles common logic + */ + async render(context) { + const allowedIds = context.filter[this.type] || []; + if (allowedIds.length === 0) + return; + const entities = await this.getEntities(allowedIds); + const dateCount = context.filter["date"]?.length || 1; + const childIds = context.childType ? context.filter[context.childType] || [] : []; + for (const entity of entities) { + const entityChildIds = context.parentChildMap?.[entity.id] || []; + const childCount = entityChildIds.filter((id) => childIds.includes(id)).length; + const colspan = childCount * dateCount; + const header = document.createElement(this.config.elementTag); + header.dataset[this.config.idAttribute] = entity.id; + header.style.setProperty(this.config.colspanVar, String(colspan)); + this.renderHeader(entity, header, context); + context.headerContainer.appendChild(header); + } + } + /** + * Override this method for custom header rendering + * Default: just sets textContent to display name + */ + renderHeader(entity, header, _context) { + header.textContent = this.getDisplayName(entity); + } + /** + * Helper to render a single entity header. + * Can be used by subclasses that override render() but want consistent header creation. + */ + createHeader(entity, context) { + const header = document.createElement(this.config.elementTag); + header.dataset[this.config.idAttribute] = entity.id; + this.renderHeader(entity, header, context); + return header; + } +}; +__name(_BaseGroupingRenderer, "BaseGroupingRenderer"); +var BaseGroupingRenderer = _BaseGroupingRenderer; + +// src/features/resource/ResourceRenderer.ts +var _ResourceRenderer = class _ResourceRenderer extends BaseGroupingRenderer { + constructor(resourceService) { + super(); + this.resourceService = resourceService; + this.type = "resource"; + this.config = { + elementTag: "swp-resource-header", + idAttribute: "resourceId", + colspanVar: "--resource-cols" + }; + } + getEntities(ids) { + return this.resourceService.getByIds(ids); + } + getDisplayName(entity) { + return entity.displayName; + } + /** + * Override render to handle: + * 1. Special ordering when parentChildMap exists (resources grouped by parent) + * 2. Different colspan calculation (just dateCount, not childCount * dateCount) + */ + async render(context) { + const resourceIds = context.filter["resource"] || []; + const dateCount = context.filter["date"]?.length || 1; + let orderedResourceIds; + if (context.parentChildMap) { + orderedResourceIds = []; + for (const childIds of Object.values(context.parentChildMap)) { + for (const childId of childIds) { + if (resourceIds.includes(childId)) { + orderedResourceIds.push(childId); + } + } + } + } else { + orderedResourceIds = resourceIds; + } + const resources = await this.getEntities(orderedResourceIds); + const resourceMap = new Map(resources.map((r) => [r.id, r])); + for (const resourceId of orderedResourceIds) { + const resource = resourceMap.get(resourceId); + if (!resource) + continue; + const header = this.createHeader(resource, context); + header.style.gridColumn = `span ${dateCount}`; + context.headerContainer.appendChild(header); + } + } +}; +__name(_ResourceRenderer, "ResourceRenderer"); +var ResourceRenderer = _ResourceRenderer; + +// src/features/team/TeamRenderer.ts +var _TeamRenderer = class _TeamRenderer extends BaseGroupingRenderer { + constructor(teamService) { + super(); + this.teamService = teamService; + this.type = "team"; + this.config = { + elementTag: "swp-team-header", + idAttribute: "teamId", + colspanVar: "--team-cols" + }; + } + getEntities(ids) { + return this.teamService.getByIds(ids); + } + getDisplayName(entity) { + return entity.name; + } +}; +__name(_TeamRenderer, "TeamRenderer"); +var TeamRenderer = _TeamRenderer; + +// src/features/timeaxis/TimeAxisRenderer.ts +var _TimeAxisRenderer = class _TimeAxisRenderer { + render(container, startHour = 6, endHour = 20) { + container.innerHTML = ""; + for (let hour = startHour; hour <= endHour; hour++) { + const marker = document.createElement("swp-hour-marker"); + marker.textContent = `${hour.toString().padStart(2, "0")}:00`; + container.appendChild(marker); + } + } +}; +__name(_TimeAxisRenderer, "TimeAxisRenderer"); +var TimeAxisRenderer = _TimeAxisRenderer; +export { + CalendarOrchestrator, + DateRenderer, + DateService, + EventRenderer, + NavigationAnimator, + ResourceRenderer, + TeamRenderer, + TimeAxisRenderer, + buildPipeline +}; +//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vbm9kZV9tb2R1bGVzL2RheWpzL2RheWpzLm1pbi5qcyIsICIuLi8uLi9ub2RlX21vZHVsZXMvZGF5anMvcGx1Z2luL3V0Yy5qcyIsICIuLi8uLi9ub2RlX21vZHVsZXMvZGF5anMvcGx1Z2luL3RpbWV6b25lLmpzIiwgIi4uLy4uL25vZGVfbW9kdWxlcy9kYXlqcy9wbHVnaW4vaXNvV2Vlay5qcyIsICIuLi8uLi9zcmMvY29yZS9SZW5kZXJCdWlsZGVyLnRzIiwgIi4uLy4uL3NyYy9jb3JlL0ZpbHRlclRlbXBsYXRlLnRzIiwgIi4uLy4uL3NyYy9jb3JlL0NhbGVuZGFyT3JjaGVzdHJhdG9yLnRzIiwgIi4uLy4uL3NyYy9jb3JlL05hdmlnYXRpb25BbmltYXRvci50cyIsICIuLi8uLi9zcmMvZmVhdHVyZXMvZGF0ZS9EYXRlUmVuZGVyZXIudHMiLCAiLi4vLi4vc3JjL2NvcmUvRGF0ZVNlcnZpY2UudHMiLCAiLi4vLi4vc3JjL3V0aWxzL1Bvc2l0aW9uVXRpbHMudHMiLCAiLi4vLi4vc3JjL2NvbnN0YW50cy9Db3JlRXZlbnRzLnRzIiwgIi4uLy4uL3NyYy9mZWF0dXJlcy9ldmVudC9FdmVudExheW91dEVuZ2luZS50cyIsICIuLi8uLi9zcmMvZmVhdHVyZXMvZXZlbnQvRXZlbnRSZW5kZXJlci50cyIsICIuLi8uLi9zcmMvY29yZS9CYXNlR3JvdXBpbmdSZW5kZXJlci50cyIsICIuLi8uLi9zcmMvZmVhdHVyZXMvcmVzb3VyY2UvUmVzb3VyY2VSZW5kZXJlci50cyIsICIuLi8uLi9zcmMvZmVhdHVyZXMvdGVhbS9UZWFtUmVuZGVyZXIudHMiLCAiLi4vLi4vc3JjL2ZlYXR1cmVzL3RpbWVheGlzL1RpbWVBeGlzUmVuZGVyZXIudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbIiFmdW5jdGlvbih0LGUpe1wib2JqZWN0XCI9PXR5cGVvZiBleHBvcnRzJiZcInVuZGVmaW5lZFwiIT10eXBlb2YgbW9kdWxlP21vZHVsZS5leHBvcnRzPWUoKTpcImZ1bmN0aW9uXCI9PXR5cGVvZiBkZWZpbmUmJmRlZmluZS5hbWQ/ZGVmaW5lKGUpOih0PVwidW5kZWZpbmVkXCIhPXR5cGVvZiBnbG9iYWxUaGlzP2dsb2JhbFRoaXM6dHx8c2VsZikuZGF5anM9ZSgpfSh0aGlzLChmdW5jdGlvbigpe1widXNlIHN0cmljdFwiO3ZhciB0PTFlMyxlPTZlNCxuPTM2ZTUscj1cIm1pbGxpc2Vjb25kXCIsaT1cInNlY29uZFwiLHM9XCJtaW51dGVcIix1PVwiaG91clwiLGE9XCJkYXlcIixvPVwid2Vla1wiLGM9XCJtb250aFwiLGY9XCJxdWFydGVyXCIsaD1cInllYXJcIixkPVwiZGF0ZVwiLGw9XCJJbnZhbGlkIERhdGVcIiwkPS9eKFxcZHs0fSlbLS9dPyhcXGR7MSwyfSk/Wy0vXT8oXFxkezAsMn0pW1R0XFxzXSooXFxkezEsMn0pPzo/KFxcZHsxLDJ9KT86PyhcXGR7MSwyfSk/Wy46XT8oXFxkKyk/JC8seT0vXFxbKFteXFxdXSspXXxZezEsNH18TXsxLDR9fER7MSwyfXxkezEsNH18SHsxLDJ9fGh7MSwyfXxhfEF8bXsxLDJ9fHN7MSwyfXxaezEsMn18U1NTL2csTT17bmFtZTpcImVuXCIsd2Vla2RheXM6XCJTdW5kYXlfTW9uZGF5X1R1ZXNkYXlfV2VkbmVzZGF5X1RodXJzZGF5X0ZyaWRheV9TYXR1cmRheVwiLnNwbGl0KFwiX1wiKSxtb250aHM6XCJKYW51YXJ5X0ZlYnJ1YXJ5X01hcmNoX0FwcmlsX01heV9KdW5lX0p1bHlfQXVndXN0X1NlcHRlbWJlcl9PY3RvYmVyX05vdmVtYmVyX0RlY2VtYmVyXCIuc3BsaXQoXCJfXCIpLG9yZGluYWw6ZnVuY3Rpb24odCl7dmFyIGU9W1widGhcIixcInN0XCIsXCJuZFwiLFwicmRcIl0sbj10JTEwMDtyZXR1cm5cIltcIit0KyhlWyhuLTIwKSUxMF18fGVbbl18fGVbMF0pK1wiXVwifX0sbT1mdW5jdGlvbih0LGUsbil7dmFyIHI9U3RyaW5nKHQpO3JldHVybiFyfHxyLmxlbmd0aD49ZT90OlwiXCIrQXJyYXkoZSsxLXIubGVuZ3RoKS5qb2luKG4pK3R9LHY9e3M6bSx6OmZ1bmN0aW9uKHQpe3ZhciBlPS10LnV0Y09mZnNldCgpLG49TWF0aC5hYnMoZSkscj1NYXRoLmZsb29yKG4vNjApLGk9biU2MDtyZXR1cm4oZTw9MD9cIitcIjpcIi1cIikrbShyLDIsXCIwXCIpK1wiOlwiK20oaSwyLFwiMFwiKX0sbTpmdW5jdGlvbiB0KGUsbil7aWYoZS5kYXRlKCk8bi5kYXRlKCkpcmV0dXJuLXQobixlKTt2YXIgcj0xMioobi55ZWFyKCktZS55ZWFyKCkpKyhuLm1vbnRoKCktZS5tb250aCgpKSxpPWUuY2xvbmUoKS5hZGQocixjKSxzPW4taTwwLHU9ZS5jbG9uZSgpLmFkZChyKyhzPy0xOjEpLGMpO3JldHVybisoLShyKyhuLWkpLyhzP2ktdTp1LWkpKXx8MCl9LGE6ZnVuY3Rpb24odCl7cmV0dXJuIHQ8MD9NYXRoLmNlaWwodCl8fDA6TWF0aC5mbG9vcih0KX0scDpmdW5jdGlvbih0KXtyZXR1cm57TTpjLHk6aCx3Om8sZDphLEQ6ZCxoOnUsbTpzLHM6aSxtczpyLFE6Zn1bdF18fFN0cmluZyh0fHxcIlwiKS50b0xvd2VyQ2FzZSgpLnJlcGxhY2UoL3MkLyxcIlwiKX0sdTpmdW5jdGlvbih0KXtyZXR1cm4gdm9pZCAwPT09dH19LGc9XCJlblwiLEQ9e307RFtnXT1NO3ZhciBwPVwiJGlzRGF5anNPYmplY3RcIixTPWZ1bmN0aW9uKHQpe3JldHVybiB0IGluc3RhbmNlb2YgX3x8ISghdHx8IXRbcF0pfSx3PWZ1bmN0aW9uIHQoZSxuLHIpe3ZhciBpO2lmKCFlKXJldHVybiBnO2lmKFwic3RyaW5nXCI9PXR5cGVvZiBlKXt2YXIgcz1lLnRvTG93ZXJDYXNlKCk7RFtzXSYmKGk9cyksbiYmKERbc109bixpPXMpO3ZhciB1PWUuc3BsaXQoXCItXCIpO2lmKCFpJiZ1Lmxlbmd0aD4xKXJldHVybiB0KHVbMF0pfWVsc2V7dmFyIGE9ZS5uYW1lO0RbYV09ZSxpPWF9cmV0dXJuIXImJmkmJihnPWkpLGl8fCFyJiZnfSxPPWZ1bmN0aW9uKHQsZSl7aWYoUyh0KSlyZXR1cm4gdC5jbG9uZSgpO3ZhciBuPVwib2JqZWN0XCI9PXR5cGVvZiBlP2U6e307cmV0dXJuIG4uZGF0ZT10LG4uYXJncz1hcmd1bWVudHMsbmV3IF8obil9LGI9djtiLmw9dyxiLmk9UyxiLnc9ZnVuY3Rpb24odCxlKXtyZXR1cm4gTyh0LHtsb2NhbGU6ZS4kTCx1dGM6ZS4kdSx4OmUuJHgsJG9mZnNldDplLiRvZmZzZXR9KX07dmFyIF89ZnVuY3Rpb24oKXtmdW5jdGlvbiBNKHQpe3RoaXMuJEw9dyh0LmxvY2FsZSxudWxsLCEwKSx0aGlzLnBhcnNlKHQpLHRoaXMuJHg9dGhpcy4keHx8dC54fHx7fSx0aGlzW3BdPSEwfXZhciBtPU0ucHJvdG90eXBlO3JldHVybiBtLnBhcnNlPWZ1bmN0aW9uKHQpe3RoaXMuJGQ9ZnVuY3Rpb24odCl7dmFyIGU9dC5kYXRlLG49dC51dGM7aWYobnVsbD09PWUpcmV0dXJuIG5ldyBEYXRlKE5hTik7aWYoYi51KGUpKXJldHVybiBuZXcgRGF0ZTtpZihlIGluc3RhbmNlb2YgRGF0ZSlyZXR1cm4gbmV3IERhdGUoZSk7aWYoXCJzdHJpbmdcIj09dHlwZW9mIGUmJiEvWiQvaS50ZXN0KGUpKXt2YXIgcj1lLm1hdGNoKCQpO2lmKHIpe3ZhciBpPXJbMl0tMXx8MCxzPShyWzddfHxcIjBcIikuc3Vic3RyaW5nKDAsMyk7cmV0dXJuIG4/bmV3IERhdGUoRGF0ZS5VVEMoclsxXSxpLHJbM118fDEscls0XXx8MCxyWzVdfHwwLHJbNl18fDAscykpOm5ldyBEYXRlKHJbMV0saSxyWzNdfHwxLHJbNF18fDAscls1XXx8MCxyWzZdfHwwLHMpfX1yZXR1cm4gbmV3IERhdGUoZSl9KHQpLHRoaXMuaW5pdCgpfSxtLmluaXQ9ZnVuY3Rpb24oKXt2YXIgdD10aGlzLiRkO3RoaXMuJHk9dC5nZXRGdWxsWWVhcigpLHRoaXMuJE09dC5nZXRNb250aCgpLHRoaXMuJEQ9dC5nZXREYXRlKCksdGhpcy4kVz10LmdldERheSgpLHRoaXMuJEg9dC5nZXRIb3VycygpLHRoaXMuJG09dC5nZXRNaW51dGVzKCksdGhpcy4kcz10LmdldFNlY29uZHMoKSx0aGlzLiRtcz10LmdldE1pbGxpc2Vjb25kcygpfSxtLiR1dGlscz1mdW5jdGlvbigpe3JldHVybiBifSxtLmlzVmFsaWQ9ZnVuY3Rpb24oKXtyZXR1cm4hKHRoaXMuJGQudG9TdHJpbmcoKT09PWwpfSxtLmlzU2FtZT1mdW5jdGlvbih0LGUpe3ZhciBuPU8odCk7cmV0dXJuIHRoaXMuc3RhcnRPZihlKTw9biYmbjw9dGhpcy5lbmRPZihlKX0sbS5pc0FmdGVyPWZ1bmN0aW9uKHQsZSl7cmV0dXJuIE8odCk8dGhpcy5zdGFydE9mKGUpfSxtLmlzQmVmb3JlPWZ1bmN0aW9uKHQsZSl7cmV0dXJuIHRoaXMuZW5kT2YoZSk8Tyh0KX0sbS4kZz1mdW5jdGlvbih0LGUsbil7cmV0dXJuIGIudSh0KT90aGlzW2VdOnRoaXMuc2V0KG4sdCl9LG0udW5peD1mdW5jdGlvbigpe3JldHVybiBNYXRoLmZsb29yKHRoaXMudmFsdWVPZigpLzFlMyl9LG0udmFsdWVPZj1mdW5jdGlvbigpe3JldHVybiB0aGlzLiRkLmdldFRpbWUoKX0sbS5zdGFydE9mPWZ1bmN0aW9uKHQsZSl7dmFyIG49dGhpcyxyPSEhYi51KGUpfHxlLGY9Yi5wKHQpLGw9ZnVuY3Rpb24odCxlKXt2YXIgaT1iLncobi4kdT9EYXRlLlVUQyhuLiR5LGUsdCk6bmV3IERhdGUobi4keSxlLHQpLG4pO3JldHVybiByP2k6aS5lbmRPZihhKX0sJD1mdW5jdGlvbih0LGUpe3JldHVybiBiLncobi50b0RhdGUoKVt0XS5hcHBseShuLnRvRGF0ZShcInNcIiksKHI/WzAsMCwwLDBdOlsyMyw1OSw1OSw5OTldKS5zbGljZShlKSksbil9LHk9dGhpcy4kVyxNPXRoaXMuJE0sbT10aGlzLiRELHY9XCJzZXRcIisodGhpcy4kdT9cIlVUQ1wiOlwiXCIpO3N3aXRjaChmKXtjYXNlIGg6cmV0dXJuIHI/bCgxLDApOmwoMzEsMTEpO2Nhc2UgYzpyZXR1cm4gcj9sKDEsTSk6bCgwLE0rMSk7Y2FzZSBvOnZhciBnPXRoaXMuJGxvY2FsZSgpLndlZWtTdGFydHx8MCxEPSh5PGc/eSs3OnkpLWc7cmV0dXJuIGwocj9tLUQ6bSsoNi1EKSxNKTtjYXNlIGE6Y2FzZSBkOnJldHVybiAkKHYrXCJIb3Vyc1wiLDApO2Nhc2UgdTpyZXR1cm4gJCh2K1wiTWludXRlc1wiLDEpO2Nhc2UgczpyZXR1cm4gJCh2K1wiU2Vjb25kc1wiLDIpO2Nhc2UgaTpyZXR1cm4gJCh2K1wiTWlsbGlzZWNvbmRzXCIsMyk7ZGVmYXVsdDpyZXR1cm4gdGhpcy5jbG9uZSgpfX0sbS5lbmRPZj1mdW5jdGlvbih0KXtyZXR1cm4gdGhpcy5zdGFydE9mKHQsITEpfSxtLiRzZXQ9ZnVuY3Rpb24odCxlKXt2YXIgbixvPWIucCh0KSxmPVwic2V0XCIrKHRoaXMuJHU/XCJVVENcIjpcIlwiKSxsPShuPXt9LG5bYV09ZitcIkRhdGVcIixuW2RdPWYrXCJEYXRlXCIsbltjXT1mK1wiTW9udGhcIixuW2hdPWYrXCJGdWxsWWVhclwiLG5bdV09ZitcIkhvdXJzXCIsbltzXT1mK1wiTWludXRlc1wiLG5baV09ZitcIlNlY29uZHNcIixuW3JdPWYrXCJNaWxsaXNlY29uZHNcIixuKVtvXSwkPW89PT1hP3RoaXMuJEQrKGUtdGhpcy4kVyk6ZTtpZihvPT09Y3x8bz09PWgpe3ZhciB5PXRoaXMuY2xvbmUoKS5zZXQoZCwxKTt5LiRkW2xdKCQpLHkuaW5pdCgpLHRoaXMuJGQ9eS5zZXQoZCxNYXRoLm1pbih0aGlzLiRELHkuZGF5c0luTW9udGgoKSkpLiRkfWVsc2UgbCYmdGhpcy4kZFtsXSgkKTtyZXR1cm4gdGhpcy5pbml0KCksdGhpc30sbS5zZXQ9ZnVuY3Rpb24odCxlKXtyZXR1cm4gdGhpcy5jbG9uZSgpLiRzZXQodCxlKX0sbS5nZXQ9ZnVuY3Rpb24odCl7cmV0dXJuIHRoaXNbYi5wKHQpXSgpfSxtLmFkZD1mdW5jdGlvbihyLGYpe3ZhciBkLGw9dGhpcztyPU51bWJlcihyKTt2YXIgJD1iLnAoZikseT1mdW5jdGlvbih0KXt2YXIgZT1PKGwpO3JldHVybiBiLncoZS5kYXRlKGUuZGF0ZSgpK01hdGgucm91bmQodCpyKSksbCl9O2lmKCQ9PT1jKXJldHVybiB0aGlzLnNldChjLHRoaXMuJE0rcik7aWYoJD09PWgpcmV0dXJuIHRoaXMuc2V0KGgsdGhpcy4keStyKTtpZigkPT09YSlyZXR1cm4geSgxKTtpZigkPT09bylyZXR1cm4geSg3KTt2YXIgTT0oZD17fSxkW3NdPWUsZFt1XT1uLGRbaV09dCxkKVskXXx8MSxtPXRoaXMuJGQuZ2V0VGltZSgpK3IqTTtyZXR1cm4gYi53KG0sdGhpcyl9LG0uc3VidHJhY3Q9ZnVuY3Rpb24odCxlKXtyZXR1cm4gdGhpcy5hZGQoLTEqdCxlKX0sbS5mb3JtYXQ9ZnVuY3Rpb24odCl7dmFyIGU9dGhpcyxuPXRoaXMuJGxvY2FsZSgpO2lmKCF0aGlzLmlzVmFsaWQoKSlyZXR1cm4gbi5pbnZhbGlkRGF0ZXx8bDt2YXIgcj10fHxcIllZWVktTU0tRERUSEg6bW06c3NaXCIsaT1iLnoodGhpcykscz10aGlzLiRILHU9dGhpcy4kbSxhPXRoaXMuJE0sbz1uLndlZWtkYXlzLGM9bi5tb250aHMsZj1uLm1lcmlkaWVtLGg9ZnVuY3Rpb24odCxuLGkscyl7cmV0dXJuIHQmJih0W25dfHx0KGUscikpfHxpW25dLnNsaWNlKDAscyl9LGQ9ZnVuY3Rpb24odCl7cmV0dXJuIGIucyhzJTEyfHwxMix0LFwiMFwiKX0sJD1mfHxmdW5jdGlvbih0LGUsbil7dmFyIHI9dDwxMj9cIkFNXCI6XCJQTVwiO3JldHVybiBuP3IudG9Mb3dlckNhc2UoKTpyfTtyZXR1cm4gci5yZXBsYWNlKHksKGZ1bmN0aW9uKHQscil7cmV0dXJuIHJ8fGZ1bmN0aW9uKHQpe3N3aXRjaCh0KXtjYXNlXCJZWVwiOnJldHVybiBTdHJpbmcoZS4keSkuc2xpY2UoLTIpO2Nhc2VcIllZWVlcIjpyZXR1cm4gYi5zKGUuJHksNCxcIjBcIik7Y2FzZVwiTVwiOnJldHVybiBhKzE7Y2FzZVwiTU1cIjpyZXR1cm4gYi5zKGErMSwyLFwiMFwiKTtjYXNlXCJNTU1cIjpyZXR1cm4gaChuLm1vbnRoc1Nob3J0LGEsYywzKTtjYXNlXCJNTU1NXCI6cmV0dXJuIGgoYyxhKTtjYXNlXCJEXCI6cmV0dXJuIGUuJEQ7Y2FzZVwiRERcIjpyZXR1cm4gYi5zKGUuJEQsMixcIjBcIik7Y2FzZVwiZFwiOnJldHVybiBTdHJpbmcoZS4kVyk7Y2FzZVwiZGRcIjpyZXR1cm4gaChuLndlZWtkYXlzTWluLGUuJFcsbywyKTtjYXNlXCJkZGRcIjpyZXR1cm4gaChuLndlZWtkYXlzU2hvcnQsZS4kVyxvLDMpO2Nhc2VcImRkZGRcIjpyZXR1cm4gb1tlLiRXXTtjYXNlXCJIXCI6cmV0dXJuIFN0cmluZyhzKTtjYXNlXCJISFwiOnJldHVybiBiLnMocywyLFwiMFwiKTtjYXNlXCJoXCI6cmV0dXJuIGQoMSk7Y2FzZVwiaGhcIjpyZXR1cm4gZCgyKTtjYXNlXCJhXCI6cmV0dXJuICQocyx1LCEwKTtjYXNlXCJBXCI6cmV0dXJuICQocyx1LCExKTtjYXNlXCJtXCI6cmV0dXJuIFN0cmluZyh1KTtjYXNlXCJtbVwiOnJldHVybiBiLnModSwyLFwiMFwiKTtjYXNlXCJzXCI6cmV0dXJuIFN0cmluZyhlLiRzKTtjYXNlXCJzc1wiOnJldHVybiBiLnMoZS4kcywyLFwiMFwiKTtjYXNlXCJTU1NcIjpyZXR1cm4gYi5zKGUuJG1zLDMsXCIwXCIpO2Nhc2VcIlpcIjpyZXR1cm4gaX1yZXR1cm4gbnVsbH0odCl8fGkucmVwbGFjZShcIjpcIixcIlwiKX0pKX0sbS51dGNPZmZzZXQ9ZnVuY3Rpb24oKXtyZXR1cm4gMTUqLU1hdGgucm91bmQodGhpcy4kZC5nZXRUaW1lem9uZU9mZnNldCgpLzE1KX0sbS5kaWZmPWZ1bmN0aW9uKHIsZCxsKXt2YXIgJCx5PXRoaXMsTT1iLnAoZCksbT1PKHIpLHY9KG0udXRjT2Zmc2V0KCktdGhpcy51dGNPZmZzZXQoKSkqZSxnPXRoaXMtbSxEPWZ1bmN0aW9uKCl7cmV0dXJuIGIubSh5LG0pfTtzd2l0Y2goTSl7Y2FzZSBoOiQ9RCgpLzEyO2JyZWFrO2Nhc2UgYzokPUQoKTticmVhaztjYXNlIGY6JD1EKCkvMzticmVhaztjYXNlIG86JD0oZy12KS82MDQ4ZTU7YnJlYWs7Y2FzZSBhOiQ9KGctdikvODY0ZTU7YnJlYWs7Y2FzZSB1OiQ9Zy9uO2JyZWFrO2Nhc2UgczokPWcvZTticmVhaztjYXNlIGk6JD1nL3Q7YnJlYWs7ZGVmYXVsdDokPWd9cmV0dXJuIGw/JDpiLmEoJCl9LG0uZGF5c0luTW9udGg9ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5lbmRPZihjKS4kRH0sbS4kbG9jYWxlPWZ1bmN0aW9uKCl7cmV0dXJuIERbdGhpcy4kTF19LG0ubG9jYWxlPWZ1bmN0aW9uKHQsZSl7aWYoIXQpcmV0dXJuIHRoaXMuJEw7dmFyIG49dGhpcy5jbG9uZSgpLHI9dyh0LGUsITApO3JldHVybiByJiYobi4kTD1yKSxufSxtLmNsb25lPWZ1bmN0aW9uKCl7cmV0dXJuIGIudyh0aGlzLiRkLHRoaXMpfSxtLnRvRGF0ZT1mdW5jdGlvbigpe3JldHVybiBuZXcgRGF0ZSh0aGlzLnZhbHVlT2YoKSl9LG0udG9KU09OPWZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuaXNWYWxpZCgpP3RoaXMudG9JU09TdHJpbmcoKTpudWxsfSxtLnRvSVNPU3RyaW5nPWZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuJGQudG9JU09TdHJpbmcoKX0sbS50b1N0cmluZz1mdW5jdGlvbigpe3JldHVybiB0aGlzLiRkLnRvVVRDU3RyaW5nKCl9LE19KCksaz1fLnByb3RvdHlwZTtyZXR1cm4gTy5wcm90b3R5cGU9ayxbW1wiJG1zXCIscl0sW1wiJHNcIixpXSxbXCIkbVwiLHNdLFtcIiRIXCIsdV0sW1wiJFdcIixhXSxbXCIkTVwiLGNdLFtcIiR5XCIsaF0sW1wiJERcIixkXV0uZm9yRWFjaCgoZnVuY3Rpb24odCl7a1t0WzFdXT1mdW5jdGlvbihlKXtyZXR1cm4gdGhpcy4kZyhlLHRbMF0sdFsxXSl9fSkpLE8uZXh0ZW5kPWZ1bmN0aW9uKHQsZSl7cmV0dXJuIHQuJGl8fCh0KGUsXyxPKSx0LiRpPSEwKSxPfSxPLmxvY2FsZT13LE8uaXNEYXlqcz1TLE8udW5peD1mdW5jdGlvbih0KXtyZXR1cm4gTygxZTMqdCl9LE8uZW49RFtnXSxPLkxzPUQsTy5wPXt9LE99KSk7IiwgIiFmdW5jdGlvbih0LGkpe1wib2JqZWN0XCI9PXR5cGVvZiBleHBvcnRzJiZcInVuZGVmaW5lZFwiIT10eXBlb2YgbW9kdWxlP21vZHVsZS5leHBvcnRzPWkoKTpcImZ1bmN0aW9uXCI9PXR5cGVvZiBkZWZpbmUmJmRlZmluZS5hbWQ/ZGVmaW5lKGkpOih0PVwidW5kZWZpbmVkXCIhPXR5cGVvZiBnbG9iYWxUaGlzP2dsb2JhbFRoaXM6dHx8c2VsZikuZGF5anNfcGx1Z2luX3V0Yz1pKCl9KHRoaXMsKGZ1bmN0aW9uKCl7XCJ1c2Ugc3RyaWN0XCI7dmFyIHQ9XCJtaW51dGVcIixpPS9bKy1dXFxkXFxkKD86Oj9cXGRcXGQpPy9nLGU9LyhbKy1dfFxcZFxcZCkvZztyZXR1cm4gZnVuY3Rpb24ocyxmLG4pe3ZhciB1PWYucHJvdG90eXBlO24udXRjPWZ1bmN0aW9uKHQpe3ZhciBpPXtkYXRlOnQsdXRjOiEwLGFyZ3M6YXJndW1lbnRzfTtyZXR1cm4gbmV3IGYoaSl9LHUudXRjPWZ1bmN0aW9uKGkpe3ZhciBlPW4odGhpcy50b0RhdGUoKSx7bG9jYWxlOnRoaXMuJEwsdXRjOiEwfSk7cmV0dXJuIGk/ZS5hZGQodGhpcy51dGNPZmZzZXQoKSx0KTplfSx1LmxvY2FsPWZ1bmN0aW9uKCl7cmV0dXJuIG4odGhpcy50b0RhdGUoKSx7bG9jYWxlOnRoaXMuJEwsdXRjOiExfSl9O3ZhciByPXUucGFyc2U7dS5wYXJzZT1mdW5jdGlvbih0KXt0LnV0YyYmKHRoaXMuJHU9ITApLHRoaXMuJHV0aWxzKCkudSh0LiRvZmZzZXQpfHwodGhpcy4kb2Zmc2V0PXQuJG9mZnNldCksci5jYWxsKHRoaXMsdCl9O3ZhciBvPXUuaW5pdDt1LmluaXQ9ZnVuY3Rpb24oKXtpZih0aGlzLiR1KXt2YXIgdD10aGlzLiRkO3RoaXMuJHk9dC5nZXRVVENGdWxsWWVhcigpLHRoaXMuJE09dC5nZXRVVENNb250aCgpLHRoaXMuJEQ9dC5nZXRVVENEYXRlKCksdGhpcy4kVz10LmdldFVUQ0RheSgpLHRoaXMuJEg9dC5nZXRVVENIb3VycygpLHRoaXMuJG09dC5nZXRVVENNaW51dGVzKCksdGhpcy4kcz10LmdldFVUQ1NlY29uZHMoKSx0aGlzLiRtcz10LmdldFVUQ01pbGxpc2Vjb25kcygpfWVsc2Ugby5jYWxsKHRoaXMpfTt2YXIgYT11LnV0Y09mZnNldDt1LnV0Y09mZnNldD1mdW5jdGlvbihzLGYpe3ZhciBuPXRoaXMuJHV0aWxzKCkudTtpZihuKHMpKXJldHVybiB0aGlzLiR1PzA6bih0aGlzLiRvZmZzZXQpP2EuY2FsbCh0aGlzKTp0aGlzLiRvZmZzZXQ7aWYoXCJzdHJpbmdcIj09dHlwZW9mIHMmJihzPWZ1bmN0aW9uKHQpe3ZvaWQgMD09PXQmJih0PVwiXCIpO3ZhciBzPXQubWF0Y2goaSk7aWYoIXMpcmV0dXJuIG51bGw7dmFyIGY9KFwiXCIrc1swXSkubWF0Y2goZSl8fFtcIi1cIiwwLDBdLG49ZlswXSx1PTYwKitmWzFdKyArZlsyXTtyZXR1cm4gMD09PXU/MDpcIitcIj09PW4/dTotdX0ocyksbnVsbD09PXMpKXJldHVybiB0aGlzO3ZhciB1PU1hdGguYWJzKHMpPD0xNj82MCpzOnM7aWYoMD09PXUpcmV0dXJuIHRoaXMudXRjKGYpO3ZhciByPXRoaXMuY2xvbmUoKTtpZihmKXJldHVybiByLiRvZmZzZXQ9dSxyLiR1PSExLHI7dmFyIG89dGhpcy4kdT90aGlzLnRvRGF0ZSgpLmdldFRpbWV6b25lT2Zmc2V0KCk6LTEqdGhpcy51dGNPZmZzZXQoKTtyZXR1cm4ocj10aGlzLmxvY2FsKCkuYWRkKHUrbyx0KSkuJG9mZnNldD11LHIuJHguJGxvY2FsT2Zmc2V0PW8scn07dmFyIGg9dS5mb3JtYXQ7dS5mb3JtYXQ9ZnVuY3Rpb24odCl7dmFyIGk9dHx8KHRoaXMuJHU/XCJZWVlZLU1NLUREVEhIOm1tOnNzW1pdXCI6XCJcIik7cmV0dXJuIGguY2FsbCh0aGlzLGkpfSx1LnZhbHVlT2Y9ZnVuY3Rpb24oKXt2YXIgdD10aGlzLiR1dGlscygpLnUodGhpcy4kb2Zmc2V0KT8wOnRoaXMuJG9mZnNldCsodGhpcy4keC4kbG9jYWxPZmZzZXR8fHRoaXMuJGQuZ2V0VGltZXpvbmVPZmZzZXQoKSk7cmV0dXJuIHRoaXMuJGQudmFsdWVPZigpLTZlNCp0fSx1LmlzVVRDPWZ1bmN0aW9uKCl7cmV0dXJuISF0aGlzLiR1fSx1LnRvSVNPU3RyaW5nPWZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMudG9EYXRlKCkudG9JU09TdHJpbmcoKX0sdS50b1N0cmluZz1mdW5jdGlvbigpe3JldHVybiB0aGlzLnRvRGF0ZSgpLnRvVVRDU3RyaW5nKCl9O3ZhciBsPXUudG9EYXRlO3UudG9EYXRlPWZ1bmN0aW9uKHQpe3JldHVyblwic1wiPT09dCYmdGhpcy4kb2Zmc2V0P24odGhpcy5mb3JtYXQoXCJZWVlZLU1NLUREIEhIOm1tOnNzOlNTU1wiKSkudG9EYXRlKCk6bC5jYWxsKHRoaXMpfTt2YXIgYz11LmRpZmY7dS5kaWZmPWZ1bmN0aW9uKHQsaSxlKXtpZih0JiZ0aGlzLiR1PT09dC4kdSlyZXR1cm4gYy5jYWxsKHRoaXMsdCxpLGUpO3ZhciBzPXRoaXMubG9jYWwoKSxmPW4odCkubG9jYWwoKTtyZXR1cm4gYy5jYWxsKHMsZixpLGUpfX19KSk7IiwgIiFmdW5jdGlvbih0LGUpe1wib2JqZWN0XCI9PXR5cGVvZiBleHBvcnRzJiZcInVuZGVmaW5lZFwiIT10eXBlb2YgbW9kdWxlP21vZHVsZS5leHBvcnRzPWUoKTpcImZ1bmN0aW9uXCI9PXR5cGVvZiBkZWZpbmUmJmRlZmluZS5hbWQ/ZGVmaW5lKGUpOih0PVwidW5kZWZpbmVkXCIhPXR5cGVvZiBnbG9iYWxUaGlzP2dsb2JhbFRoaXM6dHx8c2VsZikuZGF5anNfcGx1Z2luX3RpbWV6b25lPWUoKX0odGhpcywoZnVuY3Rpb24oKXtcInVzZSBzdHJpY3RcIjt2YXIgdD17eWVhcjowLG1vbnRoOjEsZGF5OjIsaG91cjozLG1pbnV0ZTo0LHNlY29uZDo1fSxlPXt9O3JldHVybiBmdW5jdGlvbihuLGksbyl7dmFyIHIsYT1mdW5jdGlvbih0LG4saSl7dm9pZCAwPT09aSYmKGk9e30pO3ZhciBvPW5ldyBEYXRlKHQpLHI9ZnVuY3Rpb24odCxuKXt2b2lkIDA9PT1uJiYobj17fSk7dmFyIGk9bi50aW1lWm9uZU5hbWV8fFwic2hvcnRcIixvPXQrXCJ8XCIraSxyPWVbb107cmV0dXJuIHJ8fChyPW5ldyBJbnRsLkRhdGVUaW1lRm9ybWF0KFwiZW4tVVNcIix7aG91cjEyOiExLHRpbWVab25lOnQseWVhcjpcIm51bWVyaWNcIixtb250aDpcIjItZGlnaXRcIixkYXk6XCIyLWRpZ2l0XCIsaG91cjpcIjItZGlnaXRcIixtaW51dGU6XCIyLWRpZ2l0XCIsc2Vjb25kOlwiMi1kaWdpdFwiLHRpbWVab25lTmFtZTppfSksZVtvXT1yKSxyfShuLGkpO3JldHVybiByLmZvcm1hdFRvUGFydHMobyl9LHU9ZnVuY3Rpb24oZSxuKXtmb3IodmFyIGk9YShlLG4pLHI9W10sdT0wO3U8aS5sZW5ndGg7dSs9MSl7dmFyIGY9aVt1XSxzPWYudHlwZSxtPWYudmFsdWUsYz10W3NdO2M+PTAmJihyW2NdPXBhcnNlSW50KG0sMTApKX12YXIgZD1yWzNdLGw9MjQ9PT1kPzA6ZCxoPXJbMF0rXCItXCIrclsxXStcIi1cIityWzJdK1wiIFwiK2wrXCI6XCIrcls0XStcIjpcIityWzVdK1wiOjAwMFwiLHY9K2U7cmV0dXJuKG8udXRjKGgpLnZhbHVlT2YoKS0odi09diUxZTMpKS82ZTR9LGY9aS5wcm90b3R5cGU7Zi50ej1mdW5jdGlvbih0LGUpe3ZvaWQgMD09PXQmJih0PXIpO3ZhciBuLGk9dGhpcy51dGNPZmZzZXQoKSxhPXRoaXMudG9EYXRlKCksdT1hLnRvTG9jYWxlU3RyaW5nKFwiZW4tVVNcIix7dGltZVpvbmU6dH0pLGY9TWF0aC5yb3VuZCgoYS1uZXcgRGF0ZSh1KSkvMWUzLzYwKSxzPTE1Ki1NYXRoLnJvdW5kKGEuZ2V0VGltZXpvbmVPZmZzZXQoKS8xNSktZjtpZighTnVtYmVyKHMpKW49dGhpcy51dGNPZmZzZXQoMCxlKTtlbHNlIGlmKG49byh1LHtsb2NhbGU6dGhpcy4kTH0pLiRzZXQoXCJtaWxsaXNlY29uZFwiLHRoaXMuJG1zKS51dGNPZmZzZXQocywhMCksZSl7dmFyIG09bi51dGNPZmZzZXQoKTtuPW4uYWRkKGktbSxcIm1pbnV0ZVwiKX1yZXR1cm4gbi4keC4kdGltZXpvbmU9dCxufSxmLm9mZnNldE5hbWU9ZnVuY3Rpb24odCl7dmFyIGU9dGhpcy4keC4kdGltZXpvbmV8fG8udHouZ3Vlc3MoKSxuPWEodGhpcy52YWx1ZU9mKCksZSx7dGltZVpvbmVOYW1lOnR9KS5maW5kKChmdW5jdGlvbih0KXtyZXR1cm5cInRpbWV6b25lbmFtZVwiPT09dC50eXBlLnRvTG93ZXJDYXNlKCl9KSk7cmV0dXJuIG4mJm4udmFsdWV9O3ZhciBzPWYuc3RhcnRPZjtmLnN0YXJ0T2Y9ZnVuY3Rpb24odCxlKXtpZighdGhpcy4keHx8IXRoaXMuJHguJHRpbWV6b25lKXJldHVybiBzLmNhbGwodGhpcyx0LGUpO3ZhciBuPW8odGhpcy5mb3JtYXQoXCJZWVlZLU1NLUREIEhIOm1tOnNzOlNTU1wiKSx7bG9jYWxlOnRoaXMuJEx9KTtyZXR1cm4gcy5jYWxsKG4sdCxlKS50eih0aGlzLiR4LiR0aW1lem9uZSwhMCl9LG8udHo9ZnVuY3Rpb24odCxlLG4pe3ZhciBpPW4mJmUsYT1ufHxlfHxyLGY9dSgrbygpLGEpO2lmKFwic3RyaW5nXCIhPXR5cGVvZiB0KXJldHVybiBvKHQpLnR6KGEpO3ZhciBzPWZ1bmN0aW9uKHQsZSxuKXt2YXIgaT10LTYwKmUqMWUzLG89dShpLG4pO2lmKGU9PT1vKXJldHVybltpLGVdO3ZhciByPXUoaS09NjAqKG8tZSkqMWUzLG4pO3JldHVybiBvPT09cj9baSxvXTpbdC02MCpNYXRoLm1pbihvLHIpKjFlMyxNYXRoLm1heChvLHIpXX0oby51dGModCxpKS52YWx1ZU9mKCksZixhKSxtPXNbMF0sYz1zWzFdLGQ9byhtKS51dGNPZmZzZXQoYyk7cmV0dXJuIGQuJHguJHRpbWV6b25lPWEsZH0sby50ei5ndWVzcz1mdW5jdGlvbigpe3JldHVybiBJbnRsLkRhdGVUaW1lRm9ybWF0KCkucmVzb2x2ZWRPcHRpb25zKCkudGltZVpvbmV9LG8udHouc2V0RGVmYXVsdD1mdW5jdGlvbih0KXtyPXR9fX0pKTsiLCAiIWZ1bmN0aW9uKGUsdCl7XCJvYmplY3RcIj09dHlwZW9mIGV4cG9ydHMmJlwidW5kZWZpbmVkXCIhPXR5cGVvZiBtb2R1bGU/bW9kdWxlLmV4cG9ydHM9dCgpOlwiZnVuY3Rpb25cIj09dHlwZW9mIGRlZmluZSYmZGVmaW5lLmFtZD9kZWZpbmUodCk6KGU9XCJ1bmRlZmluZWRcIiE9dHlwZW9mIGdsb2JhbFRoaXM/Z2xvYmFsVGhpczplfHxzZWxmKS5kYXlqc19wbHVnaW5faXNvV2Vlaz10KCl9KHRoaXMsKGZ1bmN0aW9uKCl7XCJ1c2Ugc3RyaWN0XCI7dmFyIGU9XCJkYXlcIjtyZXR1cm4gZnVuY3Rpb24odCxpLHMpe3ZhciBhPWZ1bmN0aW9uKHQpe3JldHVybiB0LmFkZCg0LXQuaXNvV2Vla2RheSgpLGUpfSxkPWkucHJvdG90eXBlO2QuaXNvV2Vla1llYXI9ZnVuY3Rpb24oKXtyZXR1cm4gYSh0aGlzKS55ZWFyKCl9LGQuaXNvV2Vlaz1mdW5jdGlvbih0KXtpZighdGhpcy4kdXRpbHMoKS51KHQpKXJldHVybiB0aGlzLmFkZCg3Kih0LXRoaXMuaXNvV2VlaygpKSxlKTt2YXIgaSxkLG4sbyxyPWEodGhpcyksdT0oaT10aGlzLmlzb1dlZWtZZWFyKCksZD10aGlzLiR1LG49KGQ/cy51dGM6cykoKS55ZWFyKGkpLnN0YXJ0T2YoXCJ5ZWFyXCIpLG89NC1uLmlzb1dlZWtkYXkoKSxuLmlzb1dlZWtkYXkoKT40JiYobys9Nyksbi5hZGQobyxlKSk7cmV0dXJuIHIuZGlmZih1LFwid2Vla1wiKSsxfSxkLmlzb1dlZWtkYXk9ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMuJHV0aWxzKCkudShlKT90aGlzLmRheSgpfHw3OnRoaXMuZGF5KHRoaXMuZGF5KCklNz9lOmUtNyl9O3ZhciBuPWQuc3RhcnRPZjtkLnN0YXJ0T2Y9ZnVuY3Rpb24oZSx0KXt2YXIgaT10aGlzLiR1dGlscygpLHM9ISFpLnUodCl8fHQ7cmV0dXJuXCJpc293ZWVrXCI9PT1pLnAoZSk/cz90aGlzLmRhdGUodGhpcy5kYXRlKCktKHRoaXMuaXNvV2Vla2RheSgpLTEpKS5zdGFydE9mKFwiZGF5XCIpOnRoaXMuZGF0ZSh0aGlzLmRhdGUoKS0xLSh0aGlzLmlzb1dlZWtkYXkoKS0xKSs3KS5lbmRPZihcImRheVwiKTpuLmJpbmQodGhpcykoZSx0KX19fSkpOyIsICJpbXBvcnQgeyBJUmVuZGVyZXIsIElSZW5kZXJDb250ZXh0IH0gZnJvbSAnLi9JR3JvdXBpbmdSZW5kZXJlcic7XG5cbmV4cG9ydCBpbnRlcmZhY2UgUGlwZWxpbmUge1xuICBydW4oY29udGV4dDogSVJlbmRlckNvbnRleHQpOiBQcm9taXNlPHZvaWQ+O1xufVxuXG5leHBvcnQgZnVuY3Rpb24gYnVpbGRQaXBlbGluZShyZW5kZXJlcnM6IElSZW5kZXJlcltdKTogUGlwZWxpbmUge1xuICByZXR1cm4ge1xuICAgIGFzeW5jIHJ1bihjb250ZXh0OiBJUmVuZGVyQ29udGV4dCkge1xuICAgICAgZm9yIChjb25zdCByZW5kZXJlciBvZiByZW5kZXJlcnMpIHtcbiAgICAgICAgYXdhaXQgcmVuZGVyZXIucmVuZGVyKGNvbnRleHQpO1xuICAgICAgfVxuICAgIH1cbiAgfTtcbn1cbiIsICJpbXBvcnQgeyBJQ2FsZW5kYXJFdmVudCB9IGZyb20gJy4uL3R5cGVzL0NhbGVuZGFyVHlwZXMnO1xyXG5pbXBvcnQgeyBEYXRlU2VydmljZSB9IGZyb20gJy4vRGF0ZVNlcnZpY2UnO1xyXG5pbXBvcnQgeyBJRW50aXR5UmVzb2x2ZXIgfSBmcm9tICcuL0lFbnRpdHlSZXNvbHZlcic7XHJcblxyXG4vKipcclxuICogRmllbGQgZGVmaW5pdGlvbiBmb3IgRmlsdGVyVGVtcGxhdGVcclxuICovXHJcbmludGVyZmFjZSBJRmlsdGVyRmllbGQge1xyXG4gIGlkUHJvcGVydHk6IHN0cmluZztcclxuICBkZXJpdmVkRnJvbT86IHN0cmluZztcclxufVxyXG5cclxuLyoqXHJcbiAqIFBhcnNlZCBkb3Qtbm90YXRpb24gcmVmZXJlbmNlXHJcbiAqL1xyXG5pbnRlcmZhY2UgSURvdE5vdGF0aW9uIHtcclxuICBlbnRpdHlUeXBlOiBzdHJpbmc7ICAgLy8gZS5nLiwgJ3Jlc291cmNlJ1xyXG4gIHByb3BlcnR5OiBzdHJpbmc7ICAgICAvLyBlLmcuLCAndGVhbUlkJ1xyXG4gIGZvcmVpZ25LZXk6IHN0cmluZzsgICAvLyBlLmcuLCAncmVzb3VyY2VJZCdcclxufVxyXG5cclxuLyoqXHJcbiAqIEZpbHRlclRlbXBsYXRlIC0gQnlnZ2VyIG5cdTAwRjhnbGVyIHRpbCBldmVudC1rb2xvbm5lIG1hdGNoaW5nXHJcbiAqXHJcbiAqIFZpZXdDb25maWcgZGVmaW5lcmVyIGh2aWxrZSBmZWx0ZXIgKGlkUHJvcGVydGllcykgZGVyIGluZGdcdTAwRTVyIGkga29sb25uZW5zIG5cdTAwRjhnbGUuXHJcbiAqIFNhbW1lIHRlbXBsYXRlIGJydWdlcyB0aWwgYXQgYnlnZ2Ugblx1MDBGOGdsZSBmb3IgYlx1MDBFNWRlIGtvbG9ubmUgb2cgZXZlbnQuXHJcbiAqXHJcbiAqIFN1cHBvcnRzIGRvdC1ub3RhdGlvbiBmb3IgaGllcmFyY2hpY2FsIHJlbGF0aW9uczpcclxuICogLSAncmVzb3VyY2UudGVhbUlkJyBcdTIxOTIgbG9va3MgdXAgZXZlbnQucmVzb3VyY2VJZCBcdTIxOTIgcmVzb3VyY2UgZW50aXR5IFx1MjE5MiB0ZWFtSWRcclxuICpcclxuICogUHJpbmNpcDogS29sb25uZW5zIG5cdTAwRjhnbGUtdGVtcGxhdGUgYmVzdGVtbWVyIGh2YWQgZGVyIG1hdGNoZXMgcFx1MDBFNS5cclxuICpcclxuICogQHNlZSBkb2NzL2ZpbHRlci10ZW1wbGF0ZS5tZFxyXG4gKi9cclxuZXhwb3J0IGNsYXNzIEZpbHRlclRlbXBsYXRlIHtcclxuICBwcml2YXRlIGZpZWxkczogSUZpbHRlckZpZWxkW10gPSBbXTtcclxuXHJcbiAgY29uc3RydWN0b3IoXHJcbiAgICBwcml2YXRlIGRhdGVTZXJ2aWNlOiBEYXRlU2VydmljZSxcclxuICAgIHByaXZhdGUgZW50aXR5UmVzb2x2ZXI/OiBJRW50aXR5UmVzb2x2ZXJcclxuICApIHt9XHJcblxyXG4gIC8qKlxyXG4gICAqIFRpbGZcdTAwRjhqIGZlbHQgdGlsIHRlbXBsYXRlXHJcbiAgICogQHBhcmFtIGlkUHJvcGVydHkgLSBQcm9wZXJ0eS1uYXZuIChicnVnZXMgcFx1MDBFNSBiXHUwMEU1ZGUgZXZlbnQgb2cgY29sdW1uLmRhdGFzZXQpXHJcbiAgICogQHBhcmFtIGRlcml2ZWRGcm9tIC0gSHZpcyBmZWx0ZXQgdWRsZWRlcyBmcmEgYW5kZW4gcHJvcGVydHkgKGYuZWtzLiBkYXRlIGZyYSBzdGFydClcclxuICAgKi9cclxuICBhZGRGaWVsZChpZFByb3BlcnR5OiBzdHJpbmcsIGRlcml2ZWRGcm9tPzogc3RyaW5nKTogdGhpcyB7XHJcbiAgICB0aGlzLmZpZWxkcy5wdXNoKHsgaWRQcm9wZXJ0eSwgZGVyaXZlZEZyb20gfSk7XHJcbiAgICByZXR1cm4gdGhpcztcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFBhcnNlIGRvdC1ub3RhdGlvbiBzdHJpbmcgaW50byBjb21wb25lbnRzXHJcbiAgICogQGV4YW1wbGUgJ3Jlc291cmNlLnRlYW1JZCcgXHUyMTkyIHsgZW50aXR5VHlwZTogJ3Jlc291cmNlJywgcHJvcGVydHk6ICd0ZWFtSWQnLCBmb3JlaWduS2V5OiAncmVzb3VyY2VJZCcgfVxyXG4gICAqL1xyXG4gIHByaXZhdGUgcGFyc2VEb3ROb3RhdGlvbihpZFByb3BlcnR5OiBzdHJpbmcpOiBJRG90Tm90YXRpb24gfCBudWxsIHtcclxuICAgIGlmICghaWRQcm9wZXJ0eS5pbmNsdWRlcygnLicpKSByZXR1cm4gbnVsbDtcclxuICAgIGNvbnN0IFtlbnRpdHlUeXBlLCBwcm9wZXJ0eV0gPSBpZFByb3BlcnR5LnNwbGl0KCcuJyk7XHJcbiAgICByZXR1cm4ge1xyXG4gICAgICBlbnRpdHlUeXBlLFxyXG4gICAgICBwcm9wZXJ0eSxcclxuICAgICAgZm9yZWlnbktleTogZW50aXR5VHlwZSArICdJZCcgLy8gQ29udmVudGlvbjogcmVzb3VyY2UgXHUyMTkyIHJlc291cmNlSWRcclxuICAgIH07XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBHZXQgZGF0YXNldCBrZXkgZm9yIGNvbHVtbiBsb29rdXBcclxuICAgKiBGb3IgZG90LW5vdGF0aW9uICdyZXNvdXJjZS50ZWFtSWQnLCB3ZSBsb29rIGZvciAndGVhbUlkJyBpbiBkYXRhc2V0XHJcbiAgICovXHJcbiAgcHJpdmF0ZSBnZXREYXRhc2V0S2V5KGlkUHJvcGVydHk6IHN0cmluZyk6IHN0cmluZyB7XHJcbiAgICBjb25zdCBkb3ROb3RhdGlvbiA9IHRoaXMucGFyc2VEb3ROb3RhdGlvbihpZFByb3BlcnR5KTtcclxuICAgIGlmIChkb3ROb3RhdGlvbikge1xyXG4gICAgICByZXR1cm4gZG90Tm90YXRpb24ucHJvcGVydHk7IC8vICd0ZWFtSWQnXHJcbiAgICB9XHJcbiAgICByZXR1cm4gaWRQcm9wZXJ0eTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIEJ5ZyBuXHUwMEY4Z2xlIGZyYSBrb2xvbm5lXHJcbiAgICogTFx1MDBFNnNlciB2XHUwMEU2cmRpZXIgZnJhIGNvbHVtbi5kYXRhc2V0W2lkUHJvcGVydHldXHJcbiAgICogRm9yIGRvdC1ub3RhdGlvbiwgdXNlcyB0aGUgcHJvcGVydHkgcGFydCAocmVzb3VyY2UudGVhbUlkIFx1MjE5MiB0ZWFtSWQpXHJcbiAgICovXHJcbiAgYnVpbGRLZXlGcm9tQ29sdW1uKGNvbHVtbjogSFRNTEVsZW1lbnQpOiBzdHJpbmcge1xyXG4gICAgcmV0dXJuIHRoaXMuZmllbGRzXHJcbiAgICAgIC5tYXAoZiA9PiB7XHJcbiAgICAgICAgY29uc3Qga2V5ID0gdGhpcy5nZXREYXRhc2V0S2V5KGYuaWRQcm9wZXJ0eSk7XHJcbiAgICAgICAgcmV0dXJuIGNvbHVtbi5kYXRhc2V0W2tleV0gfHwgJyc7XHJcbiAgICAgIH0pXHJcbiAgICAgIC5qb2luKCc6Jyk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBCeWcgblx1MDBGOGdsZSBmcmEgZXZlbnRcclxuICAgKiBMXHUwMEU2c2VyIHZcdTAwRTZyZGllciBmcmEgZXZlbnRbaWRQcm9wZXJ0eV0gZWxsZXIgdWRsZWRlciBmcmEgZGVyaXZlZEZyb21cclxuICAgKiBGb3IgZG90LW5vdGF0aW9uLCByZXNvbHZlcyB2aWEgRW50aXR5UmVzb2x2ZXJcclxuICAgKi9cclxuICBidWlsZEtleUZyb21FdmVudChldmVudDogSUNhbGVuZGFyRXZlbnQpOiBzdHJpbmcge1xyXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1leHBsaWNpdC1hbnlcclxuICAgIGNvbnN0IGV2ZW50UmVjb3JkID0gZXZlbnQgYXMgYW55O1xyXG4gICAgcmV0dXJuIHRoaXMuZmllbGRzXHJcbiAgICAgIC5tYXAoZiA9PiB7XHJcbiAgICAgICAgLy8gQ2hlY2sgZm9yIGRvdC1ub3RhdGlvbiAoZS5nLiwgJ3Jlc291cmNlLnRlYW1JZCcpXHJcbiAgICAgICAgY29uc3QgZG90Tm90YXRpb24gPSB0aGlzLnBhcnNlRG90Tm90YXRpb24oZi5pZFByb3BlcnR5KTtcclxuICAgICAgICBpZiAoZG90Tm90YXRpb24pIHtcclxuICAgICAgICAgIHJldHVybiB0aGlzLnJlc29sdmVEb3ROb3RhdGlvbihldmVudFJlY29yZCwgZG90Tm90YXRpb24pO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgaWYgKGYuZGVyaXZlZEZyb20pIHtcclxuICAgICAgICAgIC8vIFVkbGVkIHZcdTAwRTZyZGkgKGYuZWtzLiBkYXRlIGZyYSBzdGFydClcclxuICAgICAgICAgIGNvbnN0IHNvdXJjZVZhbHVlID0gZXZlbnRSZWNvcmRbZi5kZXJpdmVkRnJvbV07XHJcbiAgICAgICAgICBpZiAoc291cmNlVmFsdWUgaW5zdGFuY2VvZiBEYXRlKSB7XHJcbiAgICAgICAgICAgIHJldHVybiB0aGlzLmRhdGVTZXJ2aWNlLmdldERhdGVLZXkoc291cmNlVmFsdWUpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgICAgcmV0dXJuIFN0cmluZyhzb3VyY2VWYWx1ZSB8fCAnJyk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHJldHVybiBTdHJpbmcoZXZlbnRSZWNvcmRbZi5pZFByb3BlcnR5XSB8fCAnJyk7XHJcbiAgICAgIH0pXHJcbiAgICAgIC5qb2luKCc6Jyk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBSZXNvbHZlIGRvdC1ub3RhdGlvbiByZWZlcmVuY2UgdmlhIEVudGl0eVJlc29sdmVyXHJcbiAgICovXHJcbiAgcHJpdmF0ZSByZXNvbHZlRG90Tm90YXRpb24oZXZlbnRSZWNvcmQ6IFJlY29yZDxzdHJpbmcsIHVua25vd24+LCBkb3ROb3RhdGlvbjogSURvdE5vdGF0aW9uKTogc3RyaW5nIHtcclxuICAgIGlmICghdGhpcy5lbnRpdHlSZXNvbHZlcikge1xyXG4gICAgICBjb25zb2xlLndhcm4oYEZpbHRlclRlbXBsYXRlOiBFbnRpdHlSZXNvbHZlciByZXF1aXJlZCBmb3IgZG90LW5vdGF0aW9uICcke2RvdE5vdGF0aW9uLmVudGl0eVR5cGV9LiR7ZG90Tm90YXRpb24ucHJvcGVydHl9J2ApO1xyXG4gICAgICByZXR1cm4gJyc7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gR2V0IGZvcmVpZ24ga2V5IHZhbHVlIGZyb20gZXZlbnQgKGUuZy4sIHJlc291cmNlSWQpXHJcbiAgICBjb25zdCBmb3JlaWduSWQgPSBldmVudFJlY29yZFtkb3ROb3RhdGlvbi5mb3JlaWduS2V5XTtcclxuICAgIGlmICghZm9yZWlnbklkKSByZXR1cm4gJyc7XHJcblxyXG4gICAgLy8gUmVzb2x2ZSBlbnRpdHlcclxuICAgIGNvbnN0IGVudGl0eSA9IHRoaXMuZW50aXR5UmVzb2x2ZXIucmVzb2x2ZShkb3ROb3RhdGlvbi5lbnRpdHlUeXBlLCBTdHJpbmcoZm9yZWlnbklkKSk7XHJcbiAgICBpZiAoIWVudGl0eSkgcmV0dXJuICcnO1xyXG5cclxuICAgIC8vIFJldHVybiBwcm9wZXJ0eSB2YWx1ZSBmcm9tIGVudGl0eVxyXG4gICAgcmV0dXJuIFN0cmluZyhlbnRpdHlbZG90Tm90YXRpb24ucHJvcGVydHldIHx8ICcnKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIE1hdGNoIGV2ZW50IG1vZCBrb2xvbm5lXHJcbiAgICovXHJcbiAgbWF0Y2hlcyhldmVudDogSUNhbGVuZGFyRXZlbnQsIGNvbHVtbjogSFRNTEVsZW1lbnQpOiBib29sZWFuIHtcclxuICAgIHJldHVybiB0aGlzLmJ1aWxkS2V5RnJvbUV2ZW50KGV2ZW50KSA9PT0gdGhpcy5idWlsZEtleUZyb21Db2x1bW4oY29sdW1uKTtcclxuICB9XHJcbn1cclxuIiwgImltcG9ydCB7IElSZW5kZXJlciwgSVJlbmRlckNvbnRleHQgfSBmcm9tICcuL0lHcm91cGluZ1JlbmRlcmVyJztcclxuaW1wb3J0IHsgYnVpbGRQaXBlbGluZSB9IGZyb20gJy4vUmVuZGVyQnVpbGRlcic7XHJcbmltcG9ydCB7IEV2ZW50UmVuZGVyZXIgfSBmcm9tICcuLi9mZWF0dXJlcy9ldmVudC9FdmVudFJlbmRlcmVyJztcclxuaW1wb3J0IHsgU2NoZWR1bGVSZW5kZXJlciB9IGZyb20gJy4uL2ZlYXR1cmVzL3NjaGVkdWxlL1NjaGVkdWxlUmVuZGVyZXInO1xyXG5pbXBvcnQgeyBIZWFkZXJEcmF3ZXJSZW5kZXJlciB9IGZyb20gJy4uL2ZlYXR1cmVzL2hlYWRlcmRyYXdlci9IZWFkZXJEcmF3ZXJSZW5kZXJlcic7XHJcbmltcG9ydCB7IFZpZXdDb25maWcsIEdyb3VwaW5nQ29uZmlnIH0gZnJvbSAnLi9WaWV3Q29uZmlnJztcclxuaW1wb3J0IHsgRmlsdGVyVGVtcGxhdGUgfSBmcm9tICcuL0ZpbHRlclRlbXBsYXRlJztcclxuaW1wb3J0IHsgRGF0ZVNlcnZpY2UgfSBmcm9tICcuL0RhdGVTZXJ2aWNlJztcclxuaW1wb3J0IHsgSUVudGl0eVNlcnZpY2UgfSBmcm9tICcuLi9zdG9yYWdlL0lFbnRpdHlTZXJ2aWNlJztcclxuaW1wb3J0IHsgSVN5bmMgfSBmcm9tICcuLi90eXBlcy9DYWxlbmRhclR5cGVzJztcclxuXHJcbmV4cG9ydCBjbGFzcyBDYWxlbmRhck9yY2hlc3RyYXRvciB7XHJcbiAgY29uc3RydWN0b3IoXHJcbiAgICBwcml2YXRlIGFsbFJlbmRlcmVyczogSVJlbmRlcmVyW10sXHJcbiAgICBwcml2YXRlIGV2ZW50UmVuZGVyZXI6IEV2ZW50UmVuZGVyZXIsXHJcbiAgICBwcml2YXRlIHNjaGVkdWxlUmVuZGVyZXI6IFNjaGVkdWxlUmVuZGVyZXIsXHJcbiAgICBwcml2YXRlIGhlYWRlckRyYXdlclJlbmRlcmVyOiBIZWFkZXJEcmF3ZXJSZW5kZXJlcixcclxuICAgIHByaXZhdGUgZGF0ZVNlcnZpY2U6IERhdGVTZXJ2aWNlLFxyXG4gICAgcHJpdmF0ZSBlbnRpdHlTZXJ2aWNlczogSUVudGl0eVNlcnZpY2U8SVN5bmM+W11cclxuICApIHt9XHJcblxyXG4gIGFzeW5jIHJlbmRlcih2aWV3Q29uZmlnOiBWaWV3Q29uZmlnLCBjb250YWluZXI6IEhUTUxFbGVtZW50KTogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICBjb25zdCBoZWFkZXJDb250YWluZXIgPSBjb250YWluZXIucXVlcnlTZWxlY3Rvcignc3dwLWNhbGVuZGFyLWhlYWRlcicpIGFzIEhUTUxFbGVtZW50O1xyXG4gICAgY29uc3QgY29sdW1uQ29udGFpbmVyID0gY29udGFpbmVyLnF1ZXJ5U2VsZWN0b3IoJ3N3cC1kYXktY29sdW1ucycpIGFzIEhUTUxFbGVtZW50O1xyXG4gICAgaWYgKCFoZWFkZXJDb250YWluZXIgfHwgIWNvbHVtbkNvbnRhaW5lcikge1xyXG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ01pc3Npbmcgc3dwLWNhbGVuZGFyLWhlYWRlciBvciBzd3AtZGF5LWNvbHVtbnMnKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBCeWcgZmlsdGVyIGZyYSB2aWV3Q29uZmlnXHJcbiAgICBjb25zdCBmaWx0ZXI6IFJlY29yZDxzdHJpbmcsIHN0cmluZ1tdPiA9IHt9O1xyXG4gICAgZm9yIChjb25zdCBncm91cGluZyBvZiB2aWV3Q29uZmlnLmdyb3VwaW5ncykge1xyXG4gICAgICBmaWx0ZXJbZ3JvdXBpbmcudHlwZV0gPSBncm91cGluZy52YWx1ZXM7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQnlnIEZpbHRlclRlbXBsYXRlIGZyYSB2aWV3Q29uZmlnIGdyb3VwaW5ncyAoa3VuIGRlIG1lZCBpZFByb3BlcnR5KVxyXG4gICAgY29uc3QgZmlsdGVyVGVtcGxhdGUgPSBuZXcgRmlsdGVyVGVtcGxhdGUodGhpcy5kYXRlU2VydmljZSk7XHJcbiAgICBmb3IgKGNvbnN0IGdyb3VwaW5nIG9mIHZpZXdDb25maWcuZ3JvdXBpbmdzKSB7XHJcbiAgICAgIGlmIChncm91cGluZy5pZFByb3BlcnR5KSB7XHJcbiAgICAgICAgZmlsdGVyVGVtcGxhdGUuYWRkRmllbGQoZ3JvdXBpbmcuaWRQcm9wZXJ0eSwgZ3JvdXBpbmcuZGVyaXZlZEZyb20pO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUmVzb2x2ZSBiZWxvbmdzVG8gcmVsYXRpb25zIChlLmcuLCB0ZWFtLnJlc291cmNlSWRzKVxyXG4gICAgY29uc3QgeyBwYXJlbnRDaGlsZE1hcCwgY2hpbGRUeXBlIH0gPSBhd2FpdCB0aGlzLnJlc29sdmVCZWxvbmdzVG8odmlld0NvbmZpZy5ncm91cGluZ3MsIGZpbHRlcik7XHJcblxyXG4gICAgY29uc3QgY29udGV4dDogSVJlbmRlckNvbnRleHQgPSB7IGhlYWRlckNvbnRhaW5lciwgY29sdW1uQ29udGFpbmVyLCBmaWx0ZXIsIGdyb3VwaW5nczogdmlld0NvbmZpZy5ncm91cGluZ3MsIHBhcmVudENoaWxkTWFwLCBjaGlsZFR5cGUgfTtcclxuXHJcbiAgICAvLyBDbGVhclxyXG4gICAgaGVhZGVyQ29udGFpbmVyLmlubmVySFRNTCA9ICcnO1xyXG4gICAgY29sdW1uQ29udGFpbmVyLmlubmVySFRNTCA9ICcnO1xyXG5cclxuICAgIC8vIFNcdTAwRTZ0IGRhdGEtbGV2ZWxzIGF0dHJpYnV0IGZvciBDU1MgZ3JpZC1yb3cgc3R5bGluZ1xyXG4gICAgY29uc3QgbGV2ZWxzID0gdmlld0NvbmZpZy5ncm91cGluZ3MubWFwKGcgPT4gZy50eXBlKS5qb2luKCcgJyk7XHJcbiAgICBoZWFkZXJDb250YWluZXIuZGF0YXNldC5sZXZlbHMgPSBsZXZlbHM7XHJcblxyXG4gICAgLy8gVlx1MDBFNmxnIHJlbmRlcmVycyBiYXNlcmV0IHBcdTAwRTUgZ3JvdXBpbmdzIHR5cGVzXHJcbiAgICBjb25zdCBhY3RpdmVSZW5kZXJlcnMgPSB0aGlzLnNlbGVjdFJlbmRlcmVycyh2aWV3Q29uZmlnKTtcclxuXHJcbiAgICAvLyBCeWcgb2cga1x1MDBGOHIgcGlwZWxpbmVcclxuICAgIGNvbnN0IHBpcGVsaW5lID0gYnVpbGRQaXBlbGluZShhY3RpdmVSZW5kZXJlcnMpO1xyXG4gICAgYXdhaXQgcGlwZWxpbmUucnVuKGNvbnRleHQpO1xyXG5cclxuICAgIC8vIFJlbmRlciBzY2hlZHVsZSB1bmF2YWlsYWJsZSB6b25lcyAoZlx1MDBGOHIgZXZlbnRzKVxyXG4gICAgYXdhaXQgdGhpcy5zY2hlZHVsZVJlbmRlcmVyLnJlbmRlcihjb250YWluZXIsIGZpbHRlcik7XHJcblxyXG4gICAgLy8gUmVuZGVyIHRpbWVkIGV2ZW50cyBpbiBncmlkIChtZWQgZmlsdGVyVGVtcGxhdGUgdGlsIG1hdGNoaW5nKVxyXG4gICAgYXdhaXQgdGhpcy5ldmVudFJlbmRlcmVyLnJlbmRlcihjb250YWluZXIsIGZpbHRlciwgZmlsdGVyVGVtcGxhdGUpO1xyXG5cclxuICAgIC8vIFJlbmRlciBhbGxEYXkgZXZlbnRzIGluIGhlYWRlciBkcmF3ZXIgKG1lZCBmaWx0ZXJUZW1wbGF0ZSB0aWwgbWF0Y2hpbmcpXHJcbiAgICBhd2FpdCB0aGlzLmhlYWRlckRyYXdlclJlbmRlcmVyLnJlbmRlcihjb250YWluZXIsIGZpbHRlciwgZmlsdGVyVGVtcGxhdGUpO1xyXG4gIH1cclxuXHJcbiAgcHJpdmF0ZSBzZWxlY3RSZW5kZXJlcnModmlld0NvbmZpZzogVmlld0NvbmZpZyk6IElSZW5kZXJlcltdIHtcclxuICAgIGNvbnN0IHR5cGVzID0gdmlld0NvbmZpZy5ncm91cGluZ3MubWFwKGcgPT4gZy50eXBlKTtcclxuICAgIC8vIFNvcnRcdTAwRTlyIHJlbmRlcmVycyBpIHNhbW1lIHJcdTAwRTZra2VmXHUwMEY4bGdlIHNvbSB2aWV3Q29uZmlnLmdyb3VwaW5nc1xyXG4gICAgcmV0dXJuIHR5cGVzXHJcbiAgICAgIC5tYXAodHlwZSA9PiB0aGlzLmFsbFJlbmRlcmVycy5maW5kKHIgPT4gci50eXBlID09PSB0eXBlKSlcclxuICAgICAgLmZpbHRlcigocik6IHIgaXMgSVJlbmRlcmVyID0+IHIgIT09IHVuZGVmaW5lZCk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBSZXNvbHZlIGJlbG9uZ3NUbyByZWxhdGlvbnMgdG8gYnVpbGQgcGFyZW50LWNoaWxkIG1hcFxyXG4gICAqIGUuZy4sIGJlbG9uZ3NUbzogJ3RlYW0ucmVzb3VyY2VJZHMnIFx1MjE5MiB7IHRlYW0xOiBbJ0VNUDAwMScsICdFTVAwMDInXSwgdGVhbTI6IFsuLi5dIH1cclxuICAgKiBBbHNvIHJldHVybnMgdGhlIGNoaWxkVHlwZSAodGhlIGdyb3VwaW5nIHR5cGUgdGhhdCBoYXMgYmVsb25nc1RvKVxyXG4gICAqL1xyXG4gIHByaXZhdGUgYXN5bmMgcmVzb2x2ZUJlbG9uZ3NUbyhcclxuICAgIGdyb3VwaW5nczogR3JvdXBpbmdDb25maWdbXSxcclxuICAgIGZpbHRlcjogUmVjb3JkPHN0cmluZywgc3RyaW5nW10+XHJcbiAgKTogUHJvbWlzZTx7IHBhcmVudENoaWxkTWFwPzogUmVjb3JkPHN0cmluZywgc3RyaW5nW10+OyBjaGlsZFR5cGU/OiBzdHJpbmcgfT4ge1xyXG4gICAgLy8gRmluZCBncm91cGluZyB3aXRoIGJlbG9uZ3NUb1xyXG4gICAgY29uc3QgY2hpbGRHcm91cGluZyA9IGdyb3VwaW5ncy5maW5kKGcgPT4gZy5iZWxvbmdzVG8pO1xyXG4gICAgaWYgKCFjaGlsZEdyb3VwaW5nPy5iZWxvbmdzVG8pIHJldHVybiB7fTtcclxuXHJcbiAgICAvLyBQYXJzZSBiZWxvbmdzVG86ICd0ZWFtLnJlc291cmNlSWRzJ1xyXG4gICAgY29uc3QgW2VudGl0eVR5cGUsIHByb3BlcnR5XSA9IGNoaWxkR3JvdXBpbmcuYmVsb25nc1RvLnNwbGl0KCcuJyk7XHJcbiAgICBpZiAoIWVudGl0eVR5cGUgfHwgIXByb3BlcnR5KSByZXR1cm4ge307XHJcblxyXG4gICAgLy8gR2V0IHBhcmVudCBJRHMgZnJvbSBmaWx0ZXJcclxuICAgIGNvbnN0IHBhcmVudElkcyA9IGZpbHRlcltlbnRpdHlUeXBlXSB8fCBbXTtcclxuICAgIGlmIChwYXJlbnRJZHMubGVuZ3RoID09PSAwKSByZXR1cm4ge307XHJcblxyXG4gICAgLy8gRmluZCBzZXJ2aWNlIGR5bmFtaXNrIGJhc2VyZXQgcFx1MDBFNSBlbnRpdHlUeXBlIChpbmdlbiBoYXJkY29kZWQgdHlwZSBjaGVjaylcclxuICAgIGNvbnN0IHNlcnZpY2UgPSB0aGlzLmVudGl0eVNlcnZpY2VzLmZpbmQocyA9PlxyXG4gICAgICBzLmVudGl0eVR5cGUudG9Mb3dlckNhc2UoKSA9PT0gZW50aXR5VHlwZVxyXG4gICAgKTtcclxuICAgIGlmICghc2VydmljZSkgcmV0dXJuIHt9O1xyXG5cclxuICAgIC8vIEhlbnQgYWxsZSBlbnRpdGllcyBvZyBmaWx0cmVyIHBcdTAwRTUgcGFyZW50SWRzXHJcbiAgICBjb25zdCBhbGxFbnRpdGllcyA9IGF3YWl0IHNlcnZpY2UuZ2V0QWxsKCk7XHJcbiAgICBjb25zdCBlbnRpdGllcyA9IGFsbEVudGl0aWVzLmZpbHRlcihlID0+XHJcbiAgICAgIHBhcmVudElkcy5pbmNsdWRlcygoZSBhcyB1bmtub3duIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+KS5pZCBhcyBzdHJpbmcpXHJcbiAgICApO1xyXG5cclxuICAgIC8vIEJ5ZyBwYXJlbnQtY2hpbGQgbWFwXHJcbiAgICBjb25zdCBtYXA6IFJlY29yZDxzdHJpbmcsIHN0cmluZ1tdPiA9IHt9O1xyXG4gICAgZm9yIChjb25zdCBlbnRpdHkgb2YgZW50aXRpZXMpIHtcclxuICAgICAgY29uc3QgZW50aXR5UmVjb3JkID0gZW50aXR5IGFzIHVua25vd24gYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj47XHJcbiAgICAgIGNvbnN0IGNoaWxkcmVuID0gKGVudGl0eVJlY29yZFtwcm9wZXJ0eV0gYXMgc3RyaW5nW10pIHx8IFtdO1xyXG4gICAgICBtYXBbZW50aXR5UmVjb3JkLmlkIGFzIHN0cmluZ10gPSBjaGlsZHJlbjtcclxuICAgIH1cclxuXHJcbiAgICByZXR1cm4geyBwYXJlbnRDaGlsZE1hcDogbWFwLCBjaGlsZFR5cGU6IGNoaWxkR3JvdXBpbmcudHlwZSB9O1xyXG4gIH1cclxufVxyXG4iLCAiZXhwb3J0IGNsYXNzIE5hdmlnYXRpb25BbmltYXRvciB7XHJcbiAgY29uc3RydWN0b3IoXHJcbiAgICBwcml2YXRlIGhlYWRlclRyYWNrOiBIVE1MRWxlbWVudCxcclxuICAgIHByaXZhdGUgY29udGVudFRyYWNrOiBIVE1MRWxlbWVudCxcclxuICAgIHByaXZhdGUgaGVhZGVyRHJhd2VyOiBIVE1MRWxlbWVudCB8IG51bGxcclxuICApIHt9XHJcblxyXG4gIGFzeW5jIHNsaWRlKGRpcmVjdGlvbjogJ2xlZnQnIHwgJ3JpZ2h0JywgcmVuZGVyRm46ICgpID0+IFByb21pc2U8dm9pZD4pOiBQcm9taXNlPHZvaWQ+IHtcclxuICAgIGNvbnN0IG91dCA9IGRpcmVjdGlvbiA9PT0gJ2xlZnQnID8gJy0xMDAlJyA6ICcxMDAlJztcclxuICAgIGNvbnN0IGludG8gPSBkaXJlY3Rpb24gPT09ICdsZWZ0JyA/ICcxMDAlJyA6ICctMTAwJSc7XHJcblxyXG4gICAgYXdhaXQgdGhpcy5hbmltYXRlT3V0KG91dCk7XHJcbiAgICBhd2FpdCByZW5kZXJGbigpO1xyXG4gICAgYXdhaXQgdGhpcy5hbmltYXRlSW4oaW50byk7XHJcbiAgfVxyXG5cclxuICBwcml2YXRlIGFzeW5jIGFuaW1hdGVPdXQodHJhbnNsYXRlOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcclxuICAgIGNvbnN0IGFuaW1hdGlvbnMgPSBbXHJcbiAgICAgIHRoaXMuaGVhZGVyVHJhY2suYW5pbWF0ZShcclxuICAgICAgICBbeyB0cmFuc2Zvcm06ICd0cmFuc2xhdGVYKDApJyB9LCB7IHRyYW5zZm9ybTogYHRyYW5zbGF0ZVgoJHt0cmFuc2xhdGV9KWAgfV0sXHJcbiAgICAgICAgeyBkdXJhdGlvbjogMjAwLCBlYXNpbmc6ICdlYXNlLWluJyB9XHJcbiAgICAgICkuZmluaXNoZWQsXHJcbiAgICAgIHRoaXMuY29udGVudFRyYWNrLmFuaW1hdGUoXHJcbiAgICAgICAgW3sgdHJhbnNmb3JtOiAndHJhbnNsYXRlWCgwKScgfSwgeyB0cmFuc2Zvcm06IGB0cmFuc2xhdGVYKCR7dHJhbnNsYXRlfSlgIH1dLFxyXG4gICAgICAgIHsgZHVyYXRpb246IDIwMCwgZWFzaW5nOiAnZWFzZS1pbicgfVxyXG4gICAgICApLmZpbmlzaGVkXHJcbiAgICBdO1xyXG5cclxuICAgIGlmICh0aGlzLmhlYWRlckRyYXdlcikge1xyXG4gICAgICBhbmltYXRpb25zLnB1c2goXHJcbiAgICAgICAgdGhpcy5oZWFkZXJEcmF3ZXIuYW5pbWF0ZShcclxuICAgICAgICAgIFt7IHRyYW5zZm9ybTogJ3RyYW5zbGF0ZVgoMCknIH0sIHsgdHJhbnNmb3JtOiBgdHJhbnNsYXRlWCgke3RyYW5zbGF0ZX0pYCB9XSxcclxuICAgICAgICAgIHsgZHVyYXRpb246IDIwMCwgZWFzaW5nOiAnZWFzZS1pbicgfVxyXG4gICAgICAgICkuZmluaXNoZWRcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICBhd2FpdCBQcm9taXNlLmFsbChhbmltYXRpb25zKTtcclxuICB9XHJcblxyXG4gIHByaXZhdGUgYXN5bmMgYW5pbWF0ZUluKHRyYW5zbGF0ZTogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICBjb25zdCBhbmltYXRpb25zID0gW1xyXG4gICAgICB0aGlzLmhlYWRlclRyYWNrLmFuaW1hdGUoXHJcbiAgICAgICAgW3sgdHJhbnNmb3JtOiBgdHJhbnNsYXRlWCgke3RyYW5zbGF0ZX0pYCB9LCB7IHRyYW5zZm9ybTogJ3RyYW5zbGF0ZVgoMCknIH1dLFxyXG4gICAgICAgIHsgZHVyYXRpb246IDIwMCwgZWFzaW5nOiAnZWFzZS1vdXQnIH1cclxuICAgICAgKS5maW5pc2hlZCxcclxuICAgICAgdGhpcy5jb250ZW50VHJhY2suYW5pbWF0ZShcclxuICAgICAgICBbeyB0cmFuc2Zvcm06IGB0cmFuc2xhdGVYKCR7dHJhbnNsYXRlfSlgIH0sIHsgdHJhbnNmb3JtOiAndHJhbnNsYXRlWCgwKScgfV0sXHJcbiAgICAgICAgeyBkdXJhdGlvbjogMjAwLCBlYXNpbmc6ICdlYXNlLW91dCcgfVxyXG4gICAgICApLmZpbmlzaGVkXHJcbiAgICBdO1xyXG5cclxuICAgIGlmICh0aGlzLmhlYWRlckRyYXdlcikge1xyXG4gICAgICBhbmltYXRpb25zLnB1c2goXHJcbiAgICAgICAgdGhpcy5oZWFkZXJEcmF3ZXIuYW5pbWF0ZShcclxuICAgICAgICAgIFt7IHRyYW5zZm9ybTogYHRyYW5zbGF0ZVgoJHt0cmFuc2xhdGV9KWAgfSwgeyB0cmFuc2Zvcm06ICd0cmFuc2xhdGVYKDApJyB9XSxcclxuICAgICAgICAgIHsgZHVyYXRpb246IDIwMCwgZWFzaW5nOiAnZWFzZS1vdXQnIH1cclxuICAgICAgICApLmZpbmlzaGVkXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgYXdhaXQgUHJvbWlzZS5hbGwoYW5pbWF0aW9ucyk7XHJcbiAgfVxyXG59XHJcbiIsICJpbXBvcnQgeyBJUmVuZGVyZXIsIElSZW5kZXJDb250ZXh0IH0gZnJvbSAnLi4vLi4vY29yZS9JR3JvdXBpbmdSZW5kZXJlcic7XHJcbmltcG9ydCB7IERhdGVTZXJ2aWNlIH0gZnJvbSAnLi4vLi4vY29yZS9EYXRlU2VydmljZSc7XHJcblxyXG5leHBvcnQgY2xhc3MgRGF0ZVJlbmRlcmVyIGltcGxlbWVudHMgSVJlbmRlcmVyIHtcclxuICByZWFkb25seSB0eXBlID0gJ2RhdGUnO1xyXG5cclxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIGRhdGVTZXJ2aWNlOiBEYXRlU2VydmljZSkge31cclxuXHJcbiAgcmVuZGVyKGNvbnRleHQ6IElSZW5kZXJDb250ZXh0KTogdm9pZCB7XHJcbiAgICBjb25zdCBkYXRlcyA9IGNvbnRleHQuZmlsdGVyWydkYXRlJ10gfHwgW107XHJcbiAgICBjb25zdCByZXNvdXJjZUlkcyA9IGNvbnRleHQuZmlsdGVyWydyZXNvdXJjZSddIHx8IFtdO1xyXG5cclxuICAgIC8vIENoZWNrIGlmIGRhdGUgaGVhZGVycyBzaG91bGQgYmUgaGlkZGVuIChlLmcuLCBpbiBkYXkgdmlldylcclxuICAgIGNvbnN0IGRhdGVHcm91cGluZyA9IGNvbnRleHQuZ3JvdXBpbmdzPy5maW5kKGcgPT4gZy50eXBlID09PSAnZGF0ZScpO1xyXG4gICAgY29uc3QgaGlkZUhlYWRlciA9IGRhdGVHcm91cGluZz8uaGlkZUhlYWRlciA9PT0gdHJ1ZTtcclxuXHJcbiAgICAvLyBSZW5kZXIgZGF0ZXMgZm9yIEhWRVIgcmVzb3VyY2UgKGVsbGVyIDEgZ2FuZyBodmlzIGluZ2VuIHJlc291cmNlcylcclxuICAgIGNvbnN0IGl0ZXJhdGlvbnMgPSByZXNvdXJjZUlkcy5sZW5ndGggfHwgMTtcclxuICAgIGxldCBjb2x1bW5Db3VudCA9IDA7XHJcblxyXG4gICAgZm9yIChsZXQgciA9IDA7IHIgPCBpdGVyYXRpb25zOyByKyspIHtcclxuICAgICAgY29uc3QgcmVzb3VyY2VJZCA9IHJlc291cmNlSWRzW3JdOyAvLyB1bmRlZmluZWQgaHZpcyBpbmdlbiByZXNvdXJjZXNcclxuXHJcbiAgICAgIGZvciAoY29uc3QgZGF0ZVN0ciBvZiBkYXRlcykge1xyXG4gICAgICAgIGNvbnN0IGRhdGUgPSB0aGlzLmRhdGVTZXJ2aWNlLnBhcnNlSVNPKGRhdGVTdHIpO1xyXG5cclxuICAgICAgICAvLyBCdWlsZCBjb2x1bW5LZXkgZm9yIHVuaWZvcm0gaWRlbnRpZmljYXRpb25cclxuICAgICAgICBjb25zdCBzZWdtZW50czogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHsgZGF0ZTogZGF0ZVN0ciB9O1xyXG4gICAgICAgIGlmIChyZXNvdXJjZUlkKSBzZWdtZW50cy5yZXNvdXJjZSA9IHJlc291cmNlSWQ7XHJcbiAgICAgICAgY29uc3QgY29sdW1uS2V5ID0gdGhpcy5kYXRlU2VydmljZS5idWlsZENvbHVtbktleShzZWdtZW50cyk7XHJcblxyXG4gICAgICAgIC8vIEhlYWRlclxyXG4gICAgICAgIGNvbnN0IGhlYWRlciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3N3cC1kYXktaGVhZGVyJyk7XHJcbiAgICAgICAgaGVhZGVyLmRhdGFzZXQuZGF0ZSA9IGRhdGVTdHI7XHJcbiAgICAgICAgaGVhZGVyLmRhdGFzZXQuY29sdW1uS2V5ID0gY29sdW1uS2V5O1xyXG4gICAgICAgIGlmIChyZXNvdXJjZUlkKSB7XHJcbiAgICAgICAgICBoZWFkZXIuZGF0YXNldC5yZXNvdXJjZUlkID0gcmVzb3VyY2VJZDtcclxuICAgICAgICB9XHJcbiAgICAgICAgaWYgKGhpZGVIZWFkZXIpIHtcclxuICAgICAgICAgIGhlYWRlci5kYXRhc2V0LmhpZGRlbiA9ICd0cnVlJztcclxuICAgICAgICB9XHJcbiAgICAgICAgaGVhZGVyLmlubmVySFRNTCA9IGBcclxuICAgICAgICAgIDxzd3AtZGF5LW5hbWU+JHt0aGlzLmRhdGVTZXJ2aWNlLmdldERheU5hbWUoZGF0ZSwgJ3Nob3J0Jyl9PC9zd3AtZGF5LW5hbWU+XHJcbiAgICAgICAgICA8c3dwLWRheS1kYXRlPiR7ZGF0ZS5nZXREYXRlKCl9PC9zd3AtZGF5LWRhdGU+XHJcbiAgICAgICAgYDtcclxuICAgICAgICBjb250ZXh0LmhlYWRlckNvbnRhaW5lci5hcHBlbmRDaGlsZChoZWFkZXIpO1xyXG5cclxuICAgICAgICAvLyBDb2x1bW5cclxuICAgICAgICBjb25zdCBjb2x1bW4gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzd3AtZGF5LWNvbHVtbicpO1xyXG4gICAgICAgIGNvbHVtbi5kYXRhc2V0LmRhdGUgPSBkYXRlU3RyO1xyXG4gICAgICAgIGNvbHVtbi5kYXRhc2V0LmNvbHVtbktleSA9IGNvbHVtbktleTtcclxuICAgICAgICBpZiAocmVzb3VyY2VJZCkge1xyXG4gICAgICAgICAgY29sdW1uLmRhdGFzZXQucmVzb3VyY2VJZCA9IHJlc291cmNlSWQ7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGNvbHVtbi5pbm5lckhUTUwgPSAnPHN3cC1ldmVudHMtbGF5ZXI+PC9zd3AtZXZlbnRzLWxheWVyPic7XHJcbiAgICAgICAgY29udGV4dC5jb2x1bW5Db250YWluZXIuYXBwZW5kQ2hpbGQoY29sdW1uKTtcclxuXHJcbiAgICAgICAgY29sdW1uQ291bnQrKztcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIFNldCBncmlkIGNvbHVtbnMgb24gY29udGFpbmVyXHJcbiAgICBjb25zdCBjb250YWluZXIgPSBjb250ZXh0LmNvbHVtbkNvbnRhaW5lci5jbG9zZXN0KCdzd3AtY2FsZW5kYXItY29udGFpbmVyJyk7XHJcbiAgICBpZiAoY29udGFpbmVyKSB7XHJcbiAgICAgIChjb250YWluZXIgYXMgSFRNTEVsZW1lbnQpLnN0eWxlLnNldFByb3BlcnR5KCctLWdyaWQtY29sdW1ucycsIFN0cmluZyhjb2x1bW5Db3VudCkpO1xyXG4gICAgfVxyXG4gIH1cclxufVxyXG4iLCAiaW1wb3J0IGRheWpzIGZyb20gJ2RheWpzJztcclxuaW1wb3J0IHV0YyBmcm9tICdkYXlqcy9wbHVnaW4vdXRjJztcclxuaW1wb3J0IHRpbWV6b25lIGZyb20gJ2RheWpzL3BsdWdpbi90aW1lem9uZSc7XHJcbmltcG9ydCBpc29XZWVrIGZyb20gJ2RheWpzL3BsdWdpbi9pc29XZWVrJztcclxuaW1wb3J0IHsgSVRpbWVGb3JtYXRDb25maWcgfSBmcm9tICcuL0lUaW1lRm9ybWF0Q29uZmlnJztcclxuXHJcbi8vIEVuYWJsZSBkYXlqcyBwbHVnaW5zXHJcbmRheWpzLmV4dGVuZCh1dGMpO1xyXG5kYXlqcy5leHRlbmQodGltZXpvbmUpO1xyXG5kYXlqcy5leHRlbmQoaXNvV2Vlayk7XHJcblxyXG5leHBvcnQgY2xhc3MgRGF0ZVNlcnZpY2Uge1xyXG4gIHByaXZhdGUgdGltZXpvbmU6IHN0cmluZztcclxuICBwcml2YXRlIGJhc2VEYXRlOiBkYXlqcy5EYXlqcztcclxuXHJcbiAgY29uc3RydWN0b3IocHJpdmF0ZSBjb25maWc6IElUaW1lRm9ybWF0Q29uZmlnLCBiYXNlRGF0ZT86IERhdGUpIHtcclxuICAgIHRoaXMudGltZXpvbmUgPSBjb25maWcudGltZXpvbmU7XHJcbiAgICAvLyBBbGxvdyBzZXR0aW5nIGEgZml4ZWQgYmFzZSBkYXRlIGZvciBkZW1vL3Rlc3RpbmcgcHVycG9zZXNcclxuICAgIHRoaXMuYmFzZURhdGUgPSBiYXNlRGF0ZSA/IGRheWpzKGJhc2VEYXRlKSA6IGRheWpzKCk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBTZXQgYSBmaXhlZCBiYXNlIGRhdGUgKHVzZWZ1bCBmb3IgZGVtb3Mgd2l0aCBzdGF0aWMgbW9jayBkYXRhKVxyXG4gICAqL1xyXG4gIHNldEJhc2VEYXRlKGRhdGU6IERhdGUpOiB2b2lkIHtcclxuICAgIHRoaXMuYmFzZURhdGUgPSBkYXlqcyhkYXRlKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIEdldCB0aGUgY3VycmVudCBiYXNlIGRhdGUgKGVpdGhlciBmaXhlZCBvciB0b2RheSlcclxuICAgKi9cclxuICBnZXRCYXNlRGF0ZSgpOiBEYXRlIHtcclxuICAgIHJldHVybiB0aGlzLmJhc2VEYXRlLnRvRGF0ZSgpO1xyXG4gIH1cclxuXHJcbiAgcGFyc2VJU08oaXNvU3RyaW5nOiBzdHJpbmcpOiBEYXRlIHtcclxuICAgIHJldHVybiBkYXlqcyhpc29TdHJpbmcpLnRvRGF0ZSgpO1xyXG4gIH1cclxuXHJcbiAgZ2V0RGF5TmFtZShkYXRlOiBEYXRlLCBmb3JtYXQ6ICdzaG9ydCcgfCAnbG9uZycgPSAnc2hvcnQnKTogc3RyaW5nIHtcclxuICAgIHJldHVybiBuZXcgSW50bC5EYXRlVGltZUZvcm1hdCh0aGlzLmNvbmZpZy5sb2NhbGUsIHsgd2Vla2RheTogZm9ybWF0IH0pLmZvcm1hdChkYXRlKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIEdldCBkYXRlcyBzdGFydGluZyBmcm9tIGEgZGF5IG9mZnNldFxyXG4gICAqIEBwYXJhbSBkYXlPZmZzZXQgLSBEYXkgb2Zmc2V0IGZyb20gYmFzZSBkYXRlXHJcbiAgICogQHBhcmFtIGNvdW50IC0gTnVtYmVyIG9mIGNvbnNlY3V0aXZlIGRheXMgdG8gcmV0dXJuXHJcbiAgICogQHJldHVybnMgQXJyYXkgb2YgZGF0ZSBzdHJpbmdzIGluIFlZWVktTU0tREQgZm9ybWF0XHJcbiAgICovXHJcbiAgZ2V0RGF0ZXNGcm9tT2Zmc2V0KGRheU9mZnNldDogbnVtYmVyLCBjb3VudDogbnVtYmVyKTogc3RyaW5nW10ge1xyXG4gICAgY29uc3Qgc3RhcnREYXRlID0gdGhpcy5iYXNlRGF0ZS5hZGQoZGF5T2Zmc2V0LCAnZGF5Jyk7XHJcbiAgICByZXR1cm4gQXJyYXkuZnJvbSh7IGxlbmd0aDogY291bnQgfSwgKF8sIGkpID0+XHJcbiAgICAgIHN0YXJ0RGF0ZS5hZGQoaSwgJ2RheScpLmZvcm1hdCgnWVlZWS1NTS1ERCcpXHJcbiAgICApO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogR2V0IHNwZWNpZmljIHdlZWtkYXlzIGZyb20gdGhlIHdlZWsgY29udGFpbmluZyB0aGUgb2Zmc2V0IGRhdGVcclxuICAgKiBAcGFyYW0gZGF5T2Zmc2V0IC0gRGF5IG9mZnNldCBmcm9tIGJhc2UgZGF0ZVxyXG4gICAqIEBwYXJhbSB3b3JrRGF5cyAtIEFycmF5IG9mIElTTyB3ZWVrZGF5IG51bWJlcnMgKDE9TW9uZGF5LCA3PVN1bmRheSlcclxuICAgKiBAcmV0dXJucyBBcnJheSBvZiBkYXRlIHN0cmluZ3MgaW4gWVlZWS1NTS1ERCBmb3JtYXRcclxuICAgKi9cclxuICBnZXRXb3JrRGF5c0Zyb21PZmZzZXQoZGF5T2Zmc2V0OiBudW1iZXIsIHdvcmtEYXlzOiBudW1iZXJbXSk6IHN0cmluZ1tdIHtcclxuICAgIC8vIEdldCB0aGUgZGF0ZSBhdCBvZmZzZXQsIHRoZW4gZmluZCBpdHMgd2VlaydzIE1vbmRheVxyXG4gICAgY29uc3QgdGFyZ2V0RGF0ZSA9IHRoaXMuYmFzZURhdGUuYWRkKGRheU9mZnNldCwgJ2RheScpO1xyXG4gICAgY29uc3QgbW9uZGF5ID0gdGFyZ2V0RGF0ZS5zdGFydE9mKCd3ZWVrJykuYWRkKDEsICdkYXknKTtcclxuXHJcbiAgICByZXR1cm4gd29ya0RheXMubWFwKGlzb0RheSA9PiB7XHJcbiAgICAgIC8vIElTTzogMT1Nb25kYXksIDc9U3VuZGF5IFx1MjE5MiBkYXlzIGZyb20gTW9uZGF5OiAwLTZcclxuICAgICAgY29uc3QgZGF5c0Zyb21Nb25kYXkgPSBpc29EYXkgPT09IDcgPyA2IDogaXNvRGF5IC0gMTtcclxuICAgICAgcmV0dXJuIG1vbmRheS5hZGQoZGF5c0Zyb21Nb25kYXksICdkYXknKS5mb3JtYXQoJ1lZWVktTU0tREQnKTtcclxuICAgIH0pO1xyXG4gIH1cclxuXHJcbiAgLy8gTGVnYWN5IG1ldGhvZHMgZm9yIGJhY2t3YXJkcyBjb21wYXRpYmlsaXR5XHJcbiAgZ2V0V2Vla0RhdGVzKHdlZWtPZmZzZXQgPSAwLCBkYXlzID0gNyk6IHN0cmluZ1tdIHtcclxuICAgIHJldHVybiB0aGlzLmdldERhdGVzRnJvbU9mZnNldCh3ZWVrT2Zmc2V0ICogNywgZGF5cyk7XHJcbiAgfVxyXG5cclxuICBnZXRXb3JrV2Vla0RhdGVzKHdlZWtPZmZzZXQ6IG51bWJlciwgd29ya0RheXM6IG51bWJlcltdKTogc3RyaW5nW10ge1xyXG4gICAgcmV0dXJuIHRoaXMuZ2V0V29ya0RheXNGcm9tT2Zmc2V0KHdlZWtPZmZzZXQgKiA3LCB3b3JrRGF5cyk7XHJcbiAgfVxyXG5cclxuICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxyXG4gIC8vIEZPUk1BVFRJTkdcclxuICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxyXG5cclxuICBmb3JtYXRUaW1lKGRhdGU6IERhdGUsIHNob3dTZWNvbmRzID0gZmFsc2UpOiBzdHJpbmcge1xyXG4gICAgY29uc3QgcGF0dGVybiA9IHNob3dTZWNvbmRzID8gJ0hIOm1tOnNzJyA6ICdISDptbSc7XHJcbiAgICByZXR1cm4gZGF5anMoZGF0ZSkuZm9ybWF0KHBhdHRlcm4pO1xyXG4gIH1cclxuXHJcbiAgZm9ybWF0VGltZVJhbmdlKHN0YXJ0OiBEYXRlLCBlbmQ6IERhdGUpOiBzdHJpbmcge1xyXG4gICAgcmV0dXJuIGAke3RoaXMuZm9ybWF0VGltZShzdGFydCl9IC0gJHt0aGlzLmZvcm1hdFRpbWUoZW5kKX1gO1xyXG4gIH1cclxuXHJcbiAgZm9ybWF0RGF0ZShkYXRlOiBEYXRlKTogc3RyaW5nIHtcclxuICAgIHJldHVybiBkYXlqcyhkYXRlKS5mb3JtYXQoJ1lZWVktTU0tREQnKTtcclxuICB9XHJcblxyXG4gIGdldERhdGVLZXkoZGF0ZTogRGF0ZSk6IHN0cmluZyB7XHJcbiAgICByZXR1cm4gdGhpcy5mb3JtYXREYXRlKGRhdGUpO1xyXG4gIH1cclxuXHJcbiAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cclxuICAvLyBDT0xVTU4gS0VZXHJcbiAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cclxuXHJcbiAgLyoqXHJcbiAgICogQnVpbGQgYSB1bmlmb3JtIGNvbHVtbktleSBmcm9tIGdyb3VwaW5nIHNlZ21lbnRzXHJcbiAgICogSGFuZGxlcyBhbnkgY29tYmluYXRpb24gb2YgZGF0ZSwgcmVzb3VyY2UsIHRlYW0sIGV0Yy5cclxuICAgKlxyXG4gICAqIEBleGFtcGxlXHJcbiAgICogYnVpbGRDb2x1bW5LZXkoeyBkYXRlOiAnMjAyNS0xMi0wOScgfSkgXHUyMTkyIFwiMjAyNS0xMi0wOVwiXHJcbiAgICogYnVpbGRDb2x1bW5LZXkoeyBkYXRlOiAnMjAyNS0xMi0wOScsIHJlc291cmNlOiAnRU1QMDAxJyB9KSBcdTIxOTIgXCIyMDI1LTEyLTA5OkVNUDAwMVwiXHJcbiAgICovXHJcbiAgYnVpbGRDb2x1bW5LZXkoc2VnbWVudHM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4pOiBzdHJpbmcge1xyXG4gICAgLy8gQWx3YXlzIHB1dCBkYXRlIGZpcnN0IGlmIHByZXNlbnQsIHRoZW4gb3RoZXIgc2VnbWVudHMgYWxwaGFiZXRpY2FsbHlcclxuICAgIGNvbnN0IGRhdGUgPSBzZWdtZW50cy5kYXRlO1xyXG4gICAgY29uc3Qgb3RoZXJzID0gT2JqZWN0LmVudHJpZXMoc2VnbWVudHMpXHJcbiAgICAgIC5maWx0ZXIoKFtrXSkgPT4gayAhPT0gJ2RhdGUnKVxyXG4gICAgICAuc29ydCgoW2FdLCBbYl0pID0+IGEubG9jYWxlQ29tcGFyZShiKSlcclxuICAgICAgLm1hcCgoWywgdl0pID0+IHYpO1xyXG5cclxuICAgIHJldHVybiBkYXRlID8gW2RhdGUsIC4uLm90aGVyc10uam9pbignOicpIDogb3RoZXJzLmpvaW4oJzonKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFBhcnNlIGEgY29sdW1uS2V5IGJhY2sgaW50byBzZWdtZW50c1xyXG4gICAqIEFzc3VtZXMgZm9ybWF0OiBcImRhdGU6cmVzb3VyY2U6Li4uXCIgb3IganVzdCBcImRhdGVcIlxyXG4gICAqL1xyXG4gIHBhcnNlQ29sdW1uS2V5KGNvbHVtbktleTogc3RyaW5nKTogeyBkYXRlOiBzdHJpbmc7IHJlc291cmNlPzogc3RyaW5nIH0ge1xyXG4gICAgY29uc3QgcGFydHMgPSBjb2x1bW5LZXkuc3BsaXQoJzonKTtcclxuICAgIHJldHVybiB7XHJcbiAgICAgIGRhdGU6IHBhcnRzWzBdLFxyXG4gICAgICByZXNvdXJjZTogcGFydHNbMV1cclxuICAgIH07XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBFeHRyYWN0IGRhdGVLZXkgZnJvbSBjb2x1bW5LZXkgKGZpcnN0IHNlZ21lbnQpXHJcbiAgICovXHJcbiAgZ2V0RGF0ZUZyb21Db2x1bW5LZXkoY29sdW1uS2V5OiBzdHJpbmcpOiBzdHJpbmcge1xyXG4gICAgcmV0dXJuIGNvbHVtbktleS5zcGxpdCgnOicpWzBdO1xyXG4gIH1cclxuXHJcbiAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cclxuICAvLyBUSU1FIENBTENVTEFUSU9OU1xyXG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XHJcblxyXG4gIHRpbWVUb01pbnV0ZXModGltZVN0cmluZzogc3RyaW5nKTogbnVtYmVyIHtcclxuICAgIGNvbnN0IHBhcnRzID0gdGltZVN0cmluZy5zcGxpdCgnOicpLm1hcChOdW1iZXIpO1xyXG4gICAgY29uc3QgaG91cnMgPSBwYXJ0c1swXSB8fCAwO1xyXG4gICAgY29uc3QgbWludXRlcyA9IHBhcnRzWzFdIHx8IDA7XHJcbiAgICByZXR1cm4gaG91cnMgKiA2MCArIG1pbnV0ZXM7XHJcbiAgfVxyXG5cclxuICBtaW51dGVzVG9UaW1lKHRvdGFsTWludXRlczogbnVtYmVyKTogc3RyaW5nIHtcclxuICAgIGNvbnN0IGhvdXJzID0gTWF0aC5mbG9vcih0b3RhbE1pbnV0ZXMgLyA2MCk7XHJcbiAgICBjb25zdCBtaW51dGVzID0gdG90YWxNaW51dGVzICUgNjA7XHJcbiAgICByZXR1cm4gZGF5anMoKS5ob3VyKGhvdXJzKS5taW51dGUobWludXRlcykuZm9ybWF0KCdISDptbScpO1xyXG4gIH1cclxuXHJcbiAgZ2V0TWludXRlc1NpbmNlTWlkbmlnaHQoZGF0ZTogRGF0ZSk6IG51bWJlciB7XHJcbiAgICBjb25zdCBkID0gZGF5anMoZGF0ZSk7XHJcbiAgICByZXR1cm4gZC5ob3VyKCkgKiA2MCArIGQubWludXRlKCk7XHJcbiAgfVxyXG5cclxuICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxyXG4gIC8vIFVUQyBDT05WRVJTSU9OU1xyXG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XHJcblxyXG4gIHRvVVRDKGxvY2FsRGF0ZTogRGF0ZSk6IHN0cmluZyB7XHJcbiAgICByZXR1cm4gZGF5anMudHoobG9jYWxEYXRlLCB0aGlzLnRpbWV6b25lKS51dGMoKS50b0lTT1N0cmluZygpO1xyXG4gIH1cclxuXHJcbiAgZnJvbVVUQyh1dGNTdHJpbmc6IHN0cmluZyk6IERhdGUge1xyXG4gICAgcmV0dXJuIGRheWpzLnV0Yyh1dGNTdHJpbmcpLnR6KHRoaXMudGltZXpvbmUpLnRvRGF0ZSgpO1xyXG4gIH1cclxuXHJcbiAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cclxuICAvLyBEQVRFIENSRUFUSU9OXHJcbiAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cclxuXHJcbiAgY3JlYXRlRGF0ZUF0VGltZShiYXNlRGF0ZTogRGF0ZSB8IHN0cmluZywgdGltZVN0cmluZzogc3RyaW5nKTogRGF0ZSB7XHJcbiAgICBjb25zdCB0b3RhbE1pbnV0ZXMgPSB0aGlzLnRpbWVUb01pbnV0ZXModGltZVN0cmluZyk7XHJcbiAgICBjb25zdCBob3VycyA9IE1hdGguZmxvb3IodG90YWxNaW51dGVzIC8gNjApO1xyXG4gICAgY29uc3QgbWludXRlcyA9IHRvdGFsTWludXRlcyAlIDYwO1xyXG4gICAgcmV0dXJuIGRheWpzKGJhc2VEYXRlKS5zdGFydE9mKCdkYXknKS5ob3VyKGhvdXJzKS5taW51dGUobWludXRlcykudG9EYXRlKCk7XHJcbiAgfVxyXG5cclxuICBnZXRJU09XZWVrRGF5KGRhdGU6IERhdGUgfCBzdHJpbmcpOiBudW1iZXIge1xyXG4gICAgcmV0dXJuIGRheWpzKGRhdGUpLmlzb1dlZWtkYXkoKTsgIC8vIDE9TW9uZGF5LCA3PVN1bmRheVxyXG4gIH1cclxufVxyXG4iLCAiLyoqXG4gKiBQb3NpdGlvblV0aWxzIC0gUGl4ZWwvcG9zaXRpb24gY2FsY3VsYXRpb25zIGZvciBjYWxlbmRhciBncmlkXG4gKlxuICogUkVTUE9OU0lCSUxJVFk6IENvbnZlcnQgYmV0d2VlbiB0aW1lIGFuZCBwaXhlbCBwb3NpdGlvbnNcbiAqIE5PVEU6IERhdGUgZm9ybWF0dGluZyBiZWxvbmdzIGluIERhdGVTZXJ2aWNlLCBub3QgaGVyZVxuICovXG5cbmltcG9ydCB7IElHcmlkQ29uZmlnIH0gZnJvbSAnLi4vY29yZS9JR3JpZENvbmZpZyc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgRXZlbnRQb3NpdGlvbiB7XG4gIHRvcDogbnVtYmVyOyAgICAvLyBwaXhlbHMgZnJvbSBkYXkgc3RhcnRcbiAgaGVpZ2h0OiBudW1iZXI7IC8vIHBpeGVsc1xufVxuXG4vKipcbiAqIENhbGN1bGF0ZSBwaXhlbCBwb3NpdGlvbiBmb3IgYW4gZXZlbnQgYmFzZWQgb24gaXRzIHRpbWVzXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjYWxjdWxhdGVFdmVudFBvc2l0aW9uKFxuICBzdGFydDogRGF0ZSxcbiAgZW5kOiBEYXRlLFxuICBjb25maWc6IElHcmlkQ29uZmlnXG4pOiBFdmVudFBvc2l0aW9uIHtcbiAgY29uc3Qgc3RhcnRNaW51dGVzID0gc3RhcnQuZ2V0SG91cnMoKSAqIDYwICsgc3RhcnQuZ2V0TWludXRlcygpO1xuICBjb25zdCBlbmRNaW51dGVzID0gZW5kLmdldEhvdXJzKCkgKiA2MCArIGVuZC5nZXRNaW51dGVzKCk7XG5cbiAgY29uc3QgZGF5U3RhcnRNaW51dGVzID0gY29uZmlnLmRheVN0YXJ0SG91ciAqIDYwO1xuICBjb25zdCBtaW51dGVIZWlnaHQgPSBjb25maWcuaG91ckhlaWdodCAvIDYwO1xuXG4gIGNvbnN0IHRvcCA9IChzdGFydE1pbnV0ZXMgLSBkYXlTdGFydE1pbnV0ZXMpICogbWludXRlSGVpZ2h0O1xuICBjb25zdCBoZWlnaHQgPSAoZW5kTWludXRlcyAtIHN0YXJ0TWludXRlcykgKiBtaW51dGVIZWlnaHQ7XG5cbiAgcmV0dXJuIHsgdG9wLCBoZWlnaHQgfTtcbn1cblxuLyoqXG4gKiBDb252ZXJ0IG1pbnV0ZXMgdG8gcGl4ZWxzXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBtaW51dGVzVG9QaXhlbHMobWludXRlczogbnVtYmVyLCBjb25maWc6IElHcmlkQ29uZmlnKTogbnVtYmVyIHtcbiAgcmV0dXJuIChtaW51dGVzIC8gNjApICogY29uZmlnLmhvdXJIZWlnaHQ7XG59XG5cbi8qKlxuICogQ29udmVydCBwaXhlbHMgdG8gbWludXRlc1xuICovXG5leHBvcnQgZnVuY3Rpb24gcGl4ZWxzVG9NaW51dGVzKHBpeGVsczogbnVtYmVyLCBjb25maWc6IElHcmlkQ29uZmlnKTogbnVtYmVyIHtcbiAgcmV0dXJuIChwaXhlbHMgLyBjb25maWcuaG91ckhlaWdodCkgKiA2MDtcbn1cblxuLyoqXG4gKiBTbmFwIHBpeGVsIHBvc2l0aW9uIHRvIGdyaWQgaW50ZXJ2YWxcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNuYXBUb0dyaWQocGl4ZWxzOiBudW1iZXIsIGNvbmZpZzogSUdyaWRDb25maWcpOiBudW1iZXIge1xuICBjb25zdCBzbmFwUGl4ZWxzID0gbWludXRlc1RvUGl4ZWxzKGNvbmZpZy5zbmFwSW50ZXJ2YWwsIGNvbmZpZyk7XG4gIHJldHVybiBNYXRoLnJvdW5kKHBpeGVscyAvIHNuYXBQaXhlbHMpICogc25hcFBpeGVscztcbn1cbiIsICIvKipcbiAqIENvcmVFdmVudHMgLSBDb25zb2xpZGF0ZWQgZXNzZW50aWFsIGV2ZW50cyBmb3IgdGhlIGNhbGVuZGFyXG4gKi9cbmV4cG9ydCBjb25zdCBDb3JlRXZlbnRzID0ge1xuICAvLyBMaWZlY3ljbGUgZXZlbnRzXG4gIElOSVRJQUxJWkVEOiAnY29yZTppbml0aWFsaXplZCcsXG4gIFJFQURZOiAnY29yZTpyZWFkeScsXG4gIERFU1RST1lFRDogJ2NvcmU6ZGVzdHJveWVkJyxcblxuICAvLyBWaWV3IGV2ZW50c1xuICBWSUVXX0NIQU5HRUQ6ICd2aWV3OmNoYW5nZWQnLFxuICBWSUVXX1JFTkRFUkVEOiAndmlldzpyZW5kZXJlZCcsXG5cbiAgLy8gTmF2aWdhdGlvbiBldmVudHNcbiAgREFURV9DSEFOR0VEOiAnbmF2OmRhdGUtY2hhbmdlZCcsXG4gIE5BVklHQVRJT05fQ09NUExFVEVEOiAnbmF2Om5hdmlnYXRpb24tY29tcGxldGVkJyxcblxuICAvLyBEYXRhIGV2ZW50c1xuICBEQVRBX0xPQURJTkc6ICdkYXRhOmxvYWRpbmcnLFxuICBEQVRBX0xPQURFRDogJ2RhdGE6bG9hZGVkJyxcbiAgREFUQV9FUlJPUjogJ2RhdGE6ZXJyb3InLFxuXG4gIC8vIEdyaWQgZXZlbnRzXG4gIEdSSURfUkVOREVSRUQ6ICdncmlkOnJlbmRlcmVkJyxcbiAgR1JJRF9DTElDS0VEOiAnZ3JpZDpjbGlja2VkJyxcblxuICAvLyBFdmVudCBtYW5hZ2VtZW50XG4gIEVWRU5UX0NSRUFURUQ6ICdldmVudDpjcmVhdGVkJyxcbiAgRVZFTlRfVVBEQVRFRDogJ2V2ZW50OnVwZGF0ZWQnLFxuICBFVkVOVF9ERUxFVEVEOiAnZXZlbnQ6ZGVsZXRlZCcsXG4gIEVWRU5UX1NFTEVDVEVEOiAnZXZlbnQ6c2VsZWN0ZWQnLFxuXG4gIC8vIEV2ZW50IGRyYWctZHJvcFxuICBFVkVOVF9EUkFHX1NUQVJUOiAnZXZlbnQ6ZHJhZy1zdGFydCcsXG4gIEVWRU5UX0RSQUdfTU9WRTogJ2V2ZW50OmRyYWctbW92ZScsXG4gIEVWRU5UX0RSQUdfRU5EOiAnZXZlbnQ6ZHJhZy1lbmQnLFxuICBFVkVOVF9EUkFHX0NBTkNFTDogJ2V2ZW50OmRyYWctY2FuY2VsJyxcbiAgRVZFTlRfRFJBR19DT0xVTU5fQ0hBTkdFOiAnZXZlbnQ6ZHJhZy1jb2x1bW4tY2hhbmdlJyxcblxuICAvLyBIZWFkZXIgZHJhZyAodGltZWQgXHUyMTkyIGhlYWRlciBjb252ZXJzaW9uKVxuICBFVkVOVF9EUkFHX0VOVEVSX0hFQURFUjogJ2V2ZW50OmRyYWctZW50ZXItaGVhZGVyJyxcbiAgRVZFTlRfRFJBR19NT1ZFX0hFQURFUjogJ2V2ZW50OmRyYWctbW92ZS1oZWFkZXInLFxuICBFVkVOVF9EUkFHX0xFQVZFX0hFQURFUjogJ2V2ZW50OmRyYWctbGVhdmUtaGVhZGVyJyxcblxuICAvLyBFdmVudCByZXNpemVcbiAgRVZFTlRfUkVTSVpFX1NUQVJUOiAnZXZlbnQ6cmVzaXplLXN0YXJ0JyxcbiAgRVZFTlRfUkVTSVpFX0VORDogJ2V2ZW50OnJlc2l6ZS1lbmQnLFxuXG4gIC8vIEVkZ2Ugc2Nyb2xsXG4gIEVER0VfU0NST0xMX1RJQ0s6ICdlZGdlLXNjcm9sbDp0aWNrJyxcbiAgRURHRV9TQ1JPTExfU1RBUlRFRDogJ2VkZ2Utc2Nyb2xsOnN0YXJ0ZWQnLFxuICBFREdFX1NDUk9MTF9TVE9QUEVEOiAnZWRnZS1zY3JvbGw6c3RvcHBlZCcsXG5cbiAgLy8gU3lzdGVtIGV2ZW50c1xuICBFUlJPUjogJ3N5c3RlbTplcnJvcicsXG5cbiAgLy8gU3luYyBldmVudHNcbiAgU1lOQ19TVEFSVEVEOiAnc3luYzpzdGFydGVkJyxcbiAgU1lOQ19DT01QTEVURUQ6ICdzeW5jOmNvbXBsZXRlZCcsXG4gIFNZTkNfRkFJTEVEOiAnc3luYzpmYWlsZWQnLFxuXG4gIC8vIEVudGl0eSBldmVudHMgLSBmb3IgYXVkaXQgYW5kIHN5bmNcbiAgRU5USVRZX1NBVkVEOiAnZW50aXR5OnNhdmVkJyxcbiAgRU5USVRZX0RFTEVURUQ6ICdlbnRpdHk6ZGVsZXRlZCcsXG5cbiAgLy8gQXVkaXQgZXZlbnRzXG4gIEFVRElUX0xPR0dFRDogJ2F1ZGl0OmxvZ2dlZCcsXG5cbiAgLy8gUmVuZGVyaW5nIGV2ZW50c1xuICBFVkVOVFNfUkVOREVSRUQ6ICdldmVudHM6cmVuZGVyZWQnXG59IGFzIGNvbnN0O1xuIiwgIi8qKlxyXG4gKiBFdmVudExheW91dEVuZ2luZSAtIFNpbXBsaWZpZWQgc3RhY2tpbmcvZ3JvdXBpbmcgYWxnb3JpdGhtXHJcbiAqXHJcbiAqIFN1cHBvcnRzIHR3byBsYXlvdXQgbW9kZXM6XHJcbiAqIC0gR1JJRDogRXZlbnRzIHN0YXJ0aW5nIGF0IHNhbWUgdGltZSByZW5kZXJlZCBzaWRlLWJ5LXNpZGVcclxuICogLSBTVEFDS0lORzogT3ZlcmxhcHBpbmcgZXZlbnRzIHdpdGggbWFyZ2luLWxlZnQgb2Zmc2V0ICgxNXB4IHBlciBsZXZlbClcclxuICpcclxuICogTm8gcHJldi9uZXh0IGNoYWlucywgc2luZ2xlLXBhc3MgZ3JlZWR5IGFsZ29yaXRobVxyXG4gKi9cclxuXHJcbmltcG9ydCB7IElDYWxlbmRhckV2ZW50IH0gZnJvbSAnLi4vLi4vdHlwZXMvQ2FsZW5kYXJUeXBlcyc7XHJcbmltcG9ydCB7IElHcmlkQ29uZmlnIH0gZnJvbSAnLi4vLi4vY29yZS9JR3JpZENvbmZpZyc7XHJcbmltcG9ydCB7IGNhbGN1bGF0ZUV2ZW50UG9zaXRpb24gfSBmcm9tICcuLi8uLi91dGlscy9Qb3NpdGlvblV0aWxzJztcclxuaW1wb3J0IHsgSUNvbHVtbkxheW91dCwgSUdyaWRHcm91cExheW91dCwgSVN0YWNrZWRFdmVudExheW91dCB9IGZyb20gJy4vRXZlbnRMYXlvdXRUeXBlcyc7XHJcblxyXG4vKipcclxuICogQ2hlY2sgaWYgdHdvIGV2ZW50cyBvdmVybGFwIChzdHJpY3QgLSB0b3VjaGluZyBhdCBib3VuZGFyeSA9IE5PVCBvdmVybGFwcGluZylcclxuICogVGhpcyBtYXRjaGVzIFNjZW5hcmlvIDg6IGVuZD09PXN0YXJ0IGlzIE5PVCBvdmVybGFwXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZXZlbnRzT3ZlcmxhcChhOiBJQ2FsZW5kYXJFdmVudCwgYjogSUNhbGVuZGFyRXZlbnQpOiBib29sZWFuIHtcclxuICByZXR1cm4gYS5zdGFydCA8IGIuZW5kICYmIGEuZW5kID4gYi5zdGFydDtcclxufVxyXG5cclxuLyoqXHJcbiAqIENoZWNrIGlmIHR3byBldmVudHMgYXJlIHdpdGhpbiB0aHJlc2hvbGQgZm9yIGdyaWQgZ3JvdXBpbmcuXHJcbiAqIFRoaXMgaW5jbHVkZXM6XHJcbiAqIDEuIFN0YXJ0LXRvLXN0YXJ0OiBFdmVudHMgc3RhcnQgd2l0aGluIHRocmVzaG9sZCBvZiBlYWNoIG90aGVyXHJcbiAqIDIuIEVuZC10by1zdGFydDogT25lIGV2ZW50IHN0YXJ0cyB3aXRoaW4gdGhyZXNob2xkIGJlZm9yZSBhbm90aGVyIGVuZHNcclxuICovXHJcbmZ1bmN0aW9uIGV2ZW50c1dpdGhpblRocmVzaG9sZChhOiBJQ2FsZW5kYXJFdmVudCwgYjogSUNhbGVuZGFyRXZlbnQsIHRocmVzaG9sZE1pbnV0ZXM6IG51bWJlcik6IGJvb2xlYW4ge1xyXG4gIGNvbnN0IHRocmVzaG9sZE1zID0gdGhyZXNob2xkTWludXRlcyAqIDYwICogMTAwMDtcclxuXHJcbiAgLy8gU3RhcnQtdG8tc3RhcnQ6IGJvdGggZXZlbnRzIHN0YXJ0IHdpdGhpbiB0aHJlc2hvbGRcclxuICBjb25zdCBzdGFydFRvU3RhcnREaWZmID0gTWF0aC5hYnMoYS5zdGFydC5nZXRUaW1lKCkgLSBiLnN0YXJ0LmdldFRpbWUoKSk7XHJcbiAgaWYgKHN0YXJ0VG9TdGFydERpZmYgPD0gdGhyZXNob2xkTXMpIHJldHVybiB0cnVlO1xyXG5cclxuICAvLyBFbmQtdG8tc3RhcnQ6IG9uZSBldmVudCBzdGFydHMgd2l0aGluIHRocmVzaG9sZCBiZWZvcmUgdGhlIG90aGVyIGVuZHNcclxuICAvLyBCIHN0YXJ0cyB3aXRoaW4gdGhyZXNob2xkIGJlZm9yZSBBIGVuZHNcclxuICBjb25zdCBiU3RhcnRzQmVmb3JlQUVuZHMgPSBhLmVuZC5nZXRUaW1lKCkgLSBiLnN0YXJ0LmdldFRpbWUoKTtcclxuICBpZiAoYlN0YXJ0c0JlZm9yZUFFbmRzID4gMCAmJiBiU3RhcnRzQmVmb3JlQUVuZHMgPD0gdGhyZXNob2xkTXMpIHJldHVybiB0cnVlO1xyXG5cclxuICAvLyBBIHN0YXJ0cyB3aXRoaW4gdGhyZXNob2xkIGJlZm9yZSBCIGVuZHNcclxuICBjb25zdCBhU3RhcnRzQmVmb3JlQkVuZHMgPSBiLmVuZC5nZXRUaW1lKCkgLSBhLnN0YXJ0LmdldFRpbWUoKTtcclxuICBpZiAoYVN0YXJ0c0JlZm9yZUJFbmRzID4gMCAmJiBhU3RhcnRzQmVmb3JlQkVuZHMgPD0gdGhyZXNob2xkTXMpIHJldHVybiB0cnVlO1xyXG5cclxuICByZXR1cm4gZmFsc2U7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDaGVjayBpZiBhbGwgZXZlbnRzIGluIGEgZ3JvdXAgc3RhcnQgd2l0aGluIHRocmVzaG9sZCBvZiBlYWNoIG90aGVyXHJcbiAqL1xyXG5mdW5jdGlvbiBhbGxTdGFydFdpdGhpblRocmVzaG9sZChldmVudHM6IElDYWxlbmRhckV2ZW50W10sIHRocmVzaG9sZE1pbnV0ZXM6IG51bWJlcik6IGJvb2xlYW4ge1xyXG4gIGlmIChldmVudHMubGVuZ3RoIDw9IDEpIHJldHVybiB0cnVlO1xyXG5cclxuICAvLyBGaW5kIGVhcmxpZXN0IGFuZCBsYXRlc3Qgc3RhcnQgdGltZXNcclxuICBsZXQgZWFybGllc3QgPSBldmVudHNbMF0uc3RhcnQuZ2V0VGltZSgpO1xyXG4gIGxldCBsYXRlc3QgPSBldmVudHNbMF0uc3RhcnQuZ2V0VGltZSgpO1xyXG5cclxuICBmb3IgKGNvbnN0IGV2ZW50IG9mIGV2ZW50cykge1xyXG4gICAgY29uc3QgdGltZSA9IGV2ZW50LnN0YXJ0LmdldFRpbWUoKTtcclxuICAgIGlmICh0aW1lIDwgZWFybGllc3QpIGVhcmxpZXN0ID0gdGltZTtcclxuICAgIGlmICh0aW1lID4gbGF0ZXN0KSBsYXRlc3QgPSB0aW1lO1xyXG4gIH1cclxuXHJcbiAgY29uc3QgZGlmZk1pbnV0ZXMgPSAobGF0ZXN0IC0gZWFybGllc3QpIC8gKDEwMDAgKiA2MCk7XHJcbiAgcmV0dXJuIGRpZmZNaW51dGVzIDw9IHRocmVzaG9sZE1pbnV0ZXM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBGaW5kIGdyb3VwcyBvZiBvdmVybGFwcGluZyBldmVudHMgKGNvbm5lY3RlZCBieSBvdmVybGFwIGNoYWluKVxyXG4gKiBFdmVudHMgYXJlIGdyb3VwZWQgaWYgdGhleSBvdmVybGFwIHdpdGggYW55IGV2ZW50IGluIHRoZSBncm91cFxyXG4gKi9cclxuZnVuY3Rpb24gZmluZE92ZXJsYXBHcm91cHMoZXZlbnRzOiBJQ2FsZW5kYXJFdmVudFtdKTogSUNhbGVuZGFyRXZlbnRbXVtdIHtcclxuICBpZiAoZXZlbnRzLmxlbmd0aCA9PT0gMCkgcmV0dXJuIFtdO1xyXG5cclxuICBjb25zdCBzb3J0ZWQgPSBbLi4uZXZlbnRzXS5zb3J0KChhLCBiKSA9PiBhLnN0YXJ0LmdldFRpbWUoKSAtIGIuc3RhcnQuZ2V0VGltZSgpKTtcclxuICBjb25zdCB1c2VkID0gbmV3IFNldDxzdHJpbmc+KCk7XHJcbiAgY29uc3QgZ3JvdXBzOiBJQ2FsZW5kYXJFdmVudFtdW10gPSBbXTtcclxuXHJcbiAgZm9yIChjb25zdCBldmVudCBvZiBzb3J0ZWQpIHtcclxuICAgIGlmICh1c2VkLmhhcyhldmVudC5pZCkpIGNvbnRpbnVlO1xyXG5cclxuICAgIC8vIFN0YXJ0IGEgbmV3IGdyb3VwIHdpdGggdGhpcyBldmVudFxyXG4gICAgY29uc3QgZ3JvdXA6IElDYWxlbmRhckV2ZW50W10gPSBbZXZlbnRdO1xyXG4gICAgdXNlZC5hZGQoZXZlbnQuaWQpO1xyXG5cclxuICAgIC8vIEV4cGFuZCBncm91cCBieSBmaW5kaW5nIGFsbCBjb25uZWN0ZWQgZXZlbnRzICh2aWEgb3ZlcmxhcClcclxuICAgIGxldCBleHBhbmRlZCA9IHRydWU7XHJcbiAgICB3aGlsZSAoZXhwYW5kZWQpIHtcclxuICAgICAgZXhwYW5kZWQgPSBmYWxzZTtcclxuICAgICAgZm9yIChjb25zdCBjYW5kaWRhdGUgb2Ygc29ydGVkKSB7XHJcbiAgICAgICAgaWYgKHVzZWQuaGFzKGNhbmRpZGF0ZS5pZCkpIGNvbnRpbnVlO1xyXG5cclxuICAgICAgICAvLyBDaGVjayBpZiBjYW5kaWRhdGUgb3ZlcmxhcHMgd2l0aCBhbnkgZXZlbnQgaW4gZ3JvdXBcclxuICAgICAgICBjb25zdCBjb25uZWN0cyA9IGdyb3VwLnNvbWUobWVtYmVyID0+IGV2ZW50c092ZXJsYXAobWVtYmVyLCBjYW5kaWRhdGUpKTtcclxuXHJcbiAgICAgICAgaWYgKGNvbm5lY3RzKSB7XHJcbiAgICAgICAgICBncm91cC5wdXNoKGNhbmRpZGF0ZSk7XHJcbiAgICAgICAgICB1c2VkLmFkZChjYW5kaWRhdGUuaWQpO1xyXG4gICAgICAgICAgZXhwYW5kZWQgPSB0cnVlO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIGdyb3Vwcy5wdXNoKGdyb3VwKTtcclxuICB9XHJcblxyXG4gIHJldHVybiBncm91cHM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBGaW5kIGdyaWQgY2FuZGlkYXRlcyB3aXRoaW4gYSBncm91cCAtIGV2ZW50cyBjb25uZWN0ZWQgdmlhIHRocmVzaG9sZCBjaGFpblxyXG4gKiBVc2VzIFYxIGxvZ2ljOiBldmVudHMgYXJlIGNvbm5lY3RlZCBpZiB3aXRoaW4gdGhyZXNob2xkIChubyBvdmVybGFwIHJlcXVpcmVtZW50KVxyXG4gKi9cclxuZnVuY3Rpb24gZmluZEdyaWRDYW5kaWRhdGVzKFxyXG4gIGV2ZW50czogSUNhbGVuZGFyRXZlbnRbXSxcclxuICB0aHJlc2hvbGRNaW51dGVzOiBudW1iZXJcclxuKTogSUNhbGVuZGFyRXZlbnRbXVtdIHtcclxuICBpZiAoZXZlbnRzLmxlbmd0aCA9PT0gMCkgcmV0dXJuIFtdO1xyXG5cclxuICBjb25zdCBzb3J0ZWQgPSBbLi4uZXZlbnRzXS5zb3J0KChhLCBiKSA9PiBhLnN0YXJ0LmdldFRpbWUoKSAtIGIuc3RhcnQuZ2V0VGltZSgpKTtcclxuICBjb25zdCB1c2VkID0gbmV3IFNldDxzdHJpbmc+KCk7XHJcbiAgY29uc3QgZ3JvdXBzOiBJQ2FsZW5kYXJFdmVudFtdW10gPSBbXTtcclxuXHJcbiAgZm9yIChjb25zdCBldmVudCBvZiBzb3J0ZWQpIHtcclxuICAgIGlmICh1c2VkLmhhcyhldmVudC5pZCkpIGNvbnRpbnVlO1xyXG5cclxuICAgIGNvbnN0IGdyb3VwOiBJQ2FsZW5kYXJFdmVudFtdID0gW2V2ZW50XTtcclxuICAgIHVzZWQuYWRkKGV2ZW50LmlkKTtcclxuXHJcbiAgICAvLyBFeHBhbmQgYnkgdGhyZXNob2xkIGNoYWluIChWMSBsb2dpYzogbm8gb3ZlcmxhcCByZXF1aXJlbWVudCwganVzdCB0aHJlc2hvbGQpXHJcbiAgICBsZXQgZXhwYW5kZWQgPSB0cnVlO1xyXG4gICAgd2hpbGUgKGV4cGFuZGVkKSB7XHJcbiAgICAgIGV4cGFuZGVkID0gZmFsc2U7XHJcbiAgICAgIGZvciAoY29uc3QgY2FuZGlkYXRlIG9mIHNvcnRlZCkge1xyXG4gICAgICAgIGlmICh1c2VkLmhhcyhjYW5kaWRhdGUuaWQpKSBjb250aW51ZTtcclxuXHJcbiAgICAgICAgY29uc3QgY29ubmVjdHMgPSBncm91cC5zb21lKG1lbWJlciA9PlxyXG4gICAgICAgICAgZXZlbnRzV2l0aGluVGhyZXNob2xkKG1lbWJlciwgY2FuZGlkYXRlLCB0aHJlc2hvbGRNaW51dGVzKVxyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIGlmIChjb25uZWN0cykge1xyXG4gICAgICAgICAgZ3JvdXAucHVzaChjYW5kaWRhdGUpO1xyXG4gICAgICAgICAgdXNlZC5hZGQoY2FuZGlkYXRlLmlkKTtcclxuICAgICAgICAgIGV4cGFuZGVkID0gdHJ1ZTtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICBncm91cHMucHVzaChncm91cCk7XHJcbiAgfVxyXG5cclxuICByZXR1cm4gZ3JvdXBzO1xyXG59XHJcblxyXG4vKipcclxuICogQ2FsY3VsYXRlIHN0YWNrIGxldmVscyBmb3Igb3ZlcmxhcHBpbmcgZXZlbnRzIHVzaW5nIGdyZWVkeSBhbGdvcml0aG1cclxuICogRm9yIGVhY2ggZXZlbnQ6IGxldmVsID0gbWF4KG92ZXJsYXBwaW5nIGFscmVhZHktcHJvY2Vzc2VkIGV2ZW50cykgKyAxXHJcbiAqL1xyXG5mdW5jdGlvbiBjYWxjdWxhdGVTdGFja0xldmVscyhldmVudHM6IElDYWxlbmRhckV2ZW50W10pOiBNYXA8c3RyaW5nLCBudW1iZXI+IHtcclxuICBjb25zdCBsZXZlbHMgPSBuZXcgTWFwPHN0cmluZywgbnVtYmVyPigpO1xyXG4gIGNvbnN0IHNvcnRlZCA9IFsuLi5ldmVudHNdLnNvcnQoKGEsIGIpID0+IGEuc3RhcnQuZ2V0VGltZSgpIC0gYi5zdGFydC5nZXRUaW1lKCkpO1xyXG5cclxuICBmb3IgKGNvbnN0IGV2ZW50IG9mIHNvcnRlZCkge1xyXG4gICAgbGV0IG1heE92ZXJsYXBwaW5nTGV2ZWwgPSAtMTtcclxuXHJcbiAgICAvLyBGaW5kIG1heCBsZXZlbCBhbW9uZyBvdmVybGFwcGluZyBldmVudHMgYWxyZWFkeSBwcm9jZXNzZWRcclxuICAgIGZvciAoY29uc3QgW2lkLCBsZXZlbF0gb2YgbGV2ZWxzKSB7XHJcbiAgICAgIGNvbnN0IG90aGVyID0gZXZlbnRzLmZpbmQoZSA9PiBlLmlkID09PSBpZCk7XHJcbiAgICAgIGlmIChvdGhlciAmJiBldmVudHNPdmVybGFwKGV2ZW50LCBvdGhlcikpIHtcclxuICAgICAgICBtYXhPdmVybGFwcGluZ0xldmVsID0gTWF0aC5tYXgobWF4T3ZlcmxhcHBpbmdMZXZlbCwgbGV2ZWwpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgbGV2ZWxzLnNldChldmVudC5pZCwgbWF4T3ZlcmxhcHBpbmdMZXZlbCArIDEpO1xyXG4gIH1cclxuXHJcbiAgcmV0dXJuIGxldmVscztcclxufVxyXG5cclxuLyoqXHJcbiAqIEFsbG9jYXRlIGV2ZW50cyB0byBjb2x1bW5zIGZvciBHUklEIGxheW91dCB1c2luZyBncmVlZHkgYWxnb3JpdGhtXHJcbiAqIE5vbi1vdmVybGFwcGluZyBldmVudHMgY2FuIHNoYXJlIGEgY29sdW1uIHRvIG1pbmltaXplIHRvdGFsIGNvbHVtbnNcclxuICovXHJcbmZ1bmN0aW9uIGFsbG9jYXRlQ29sdW1ucyhldmVudHM6IElDYWxlbmRhckV2ZW50W10pOiBJQ2FsZW5kYXJFdmVudFtdW10ge1xyXG4gIGNvbnN0IHNvcnRlZCA9IFsuLi5ldmVudHNdLnNvcnQoKGEsIGIpID0+IGEuc3RhcnQuZ2V0VGltZSgpIC0gYi5zdGFydC5nZXRUaW1lKCkpO1xyXG4gIGNvbnN0IGNvbHVtbnM6IElDYWxlbmRhckV2ZW50W11bXSA9IFtdO1xyXG5cclxuICBmb3IgKGNvbnN0IGV2ZW50IG9mIHNvcnRlZCkge1xyXG4gICAgLy8gRmluZCBmaXJzdCBjb2x1bW4gd2hlcmUgZXZlbnQgZG9lc24ndCBvdmVybGFwIHdpdGggZXhpc3RpbmcgZXZlbnRzXHJcbiAgICBsZXQgcGxhY2VkID0gZmFsc2U7XHJcbiAgICBmb3IgKGNvbnN0IGNvbHVtbiBvZiBjb2x1bW5zKSB7XHJcbiAgICAgIGNvbnN0IGNhbkZpdCA9ICFjb2x1bW4uc29tZShlID0+IGV2ZW50c092ZXJsYXAoZXZlbnQsIGUpKTtcclxuICAgICAgaWYgKGNhbkZpdCkge1xyXG4gICAgICAgIGNvbHVtbi5wdXNoKGV2ZW50KTtcclxuICAgICAgICBwbGFjZWQgPSB0cnVlO1xyXG4gICAgICAgIGJyZWFrO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gTm8gc3VpdGFibGUgY29sdW1uIGZvdW5kLCBjcmVhdGUgbmV3IG9uZVxyXG4gICAgaWYgKCFwbGFjZWQpIHtcclxuICAgICAgY29sdW1ucy5wdXNoKFtldmVudF0pO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgcmV0dXJuIGNvbHVtbnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBNYWluIGVudHJ5IHBvaW50OiBDYWxjdWxhdGUgY29tcGxldGUgbGF5b3V0IGZvciBhIGNvbHVtbidzIGV2ZW50c1xyXG4gKlxyXG4gKiBBbGdvcml0aG06XHJcbiAqIDEuIEZpbmQgb3ZlcmxhcCBncm91cHMgKGV2ZW50cyBjb25uZWN0ZWQgYnkgb3ZlcmxhcCBjaGFpbilcclxuICogMi4gRm9yIGVhY2ggb3ZlcmxhcCBncm91cCwgZmluZCBncmlkIGNhbmRpZGF0ZXMgKGV2ZW50cyB3aXRoaW4gdGhyZXNob2xkIGNoYWluKVxyXG4gKiAzLiBJZiBhbGwgZXZlbnRzIGluIG92ZXJsYXAgZ3JvdXAgZm9ybSBhIHNpbmdsZSBncmlkIGNhbmRpZGF0ZSBcdTIxOTIgR1JJRCBtb2RlXHJcbiAqIDQuIE90aGVyd2lzZSBcdTIxOTIgU1RBQ0tJTkcgbW9kZSB3aXRoIGNhbGN1bGF0ZWQgbGV2ZWxzXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gY2FsY3VsYXRlQ29sdW1uTGF5b3V0KFxyXG4gIGV2ZW50czogSUNhbGVuZGFyRXZlbnRbXSxcclxuICBjb25maWc6IElHcmlkQ29uZmlnXHJcbik6IElDb2x1bW5MYXlvdXQge1xyXG4gIGNvbnN0IHRocmVzaG9sZE1pbnV0ZXMgPSBjb25maWcuZ3JpZFN0YXJ0VGhyZXNob2xkTWludXRlcyA/PyAxMDtcclxuXHJcbiAgY29uc3QgcmVzdWx0OiBJQ29sdW1uTGF5b3V0ID0ge1xyXG4gICAgZ3JpZHM6IFtdLFxyXG4gICAgc3RhY2tlZDogW11cclxuICB9O1xyXG5cclxuICBpZiAoZXZlbnRzLmxlbmd0aCA9PT0gMCkgcmV0dXJuIHJlc3VsdDtcclxuXHJcbiAgLy8gRmluZCBhbGwgb3ZlcmxhcHBpbmcgZXZlbnQgZ3JvdXBzXHJcbiAgY29uc3Qgb3ZlcmxhcEdyb3VwcyA9IGZpbmRPdmVybGFwR3JvdXBzKGV2ZW50cyk7XHJcblxyXG4gIGZvciAoY29uc3Qgb3ZlcmxhcEdyb3VwIG9mIG92ZXJsYXBHcm91cHMpIHtcclxuICAgIGlmIChvdmVybGFwR3JvdXAubGVuZ3RoID09PSAxKSB7XHJcbiAgICAgIC8vIFNpbmdsZSBldmVudCAtIG5vIGdyb3VwaW5nIG5lZWRlZFxyXG4gICAgICByZXN1bHQuc3RhY2tlZC5wdXNoKHtcclxuICAgICAgICBldmVudDogb3ZlcmxhcEdyb3VwWzBdLFxyXG4gICAgICAgIHN0YWNrTGV2ZWw6IDBcclxuICAgICAgfSk7XHJcbiAgICAgIGNvbnRpbnVlO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFdpdGhpbiB0aGlzIG92ZXJsYXAgZ3JvdXAsIGZpbmQgZ3JpZCBjYW5kaWRhdGVzICh0aHJlc2hvbGQtY29ubmVjdGVkIHN1Ymdyb3VwcylcclxuICAgIGNvbnN0IGdyaWRTdWJncm91cHMgPSBmaW5kR3JpZENhbmRpZGF0ZXMob3ZlcmxhcEdyb3VwLCB0aHJlc2hvbGRNaW51dGVzKTtcclxuXHJcbiAgICAvLyBDaGVjayBpZiB0aGUgRU5USVJFIG92ZXJsYXAgZ3JvdXAgZm9ybXMgYSBzaW5nbGUgZ3JpZCBjYW5kaWRhdGVcclxuICAgIC8vIFRoaXMgaGFwcGVucyB3aGVuIGFsbCBldmVudHMgYXJlIGNvbm5lY3RlZCB2aWEgdGhyZXNob2xkIGNoYWluXHJcbiAgICBjb25zdCBsYXJnZXN0R3JpZENhbmRpZGF0ZSA9IGdyaWRTdWJncm91cHMucmVkdWNlKChtYXgsIGcpID0+XHJcbiAgICAgIGcubGVuZ3RoID4gbWF4Lmxlbmd0aCA/IGcgOiBtYXgsIGdyaWRTdWJncm91cHNbMF0pO1xyXG5cclxuICAgIGlmIChsYXJnZXN0R3JpZENhbmRpZGF0ZS5sZW5ndGggPT09IG92ZXJsYXBHcm91cC5sZW5ndGgpIHtcclxuICAgICAgLy8gQWxsIGV2ZW50cyBpbiBvdmVybGFwIGdyb3VwIGFyZSBjb25uZWN0ZWQgdmlhIHRocmVzaG9sZCBjaGFpbiBcdTIxOTIgR1JJRCBtb2RlXHJcbiAgICAgIGNvbnN0IGNvbHVtbnMgPSBhbGxvY2F0ZUNvbHVtbnMob3ZlcmxhcEdyb3VwKTtcclxuICAgICAgY29uc3QgZWFybGllc3QgPSBvdmVybGFwR3JvdXAucmVkdWNlKChtaW4sIGUpID0+XHJcbiAgICAgICAgZS5zdGFydCA8IG1pbi5zdGFydCA/IGUgOiBtaW4sIG92ZXJsYXBHcm91cFswXSk7XHJcbiAgICAgIGNvbnN0IHBvc2l0aW9uID0gY2FsY3VsYXRlRXZlbnRQb3NpdGlvbihlYXJsaWVzdC5zdGFydCwgZWFybGllc3QuZW5kLCBjb25maWcpO1xyXG5cclxuICAgICAgcmVzdWx0LmdyaWRzLnB1c2goe1xyXG4gICAgICAgIGV2ZW50czogb3ZlcmxhcEdyb3VwLFxyXG4gICAgICAgIGNvbHVtbnMsXHJcbiAgICAgICAgc3RhY2tMZXZlbDogMCxcclxuICAgICAgICBwb3NpdGlvbjogeyB0b3A6IHBvc2l0aW9uLnRvcCB9XHJcbiAgICAgIH0pO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gTm90IGFsbCBldmVudHMgY29ubmVjdGVkIHZpYSB0aHJlc2hvbGQgXHUyMTkyIFNUQUNLSU5HIG1vZGVcclxuICAgICAgY29uc3QgbGV2ZWxzID0gY2FsY3VsYXRlU3RhY2tMZXZlbHMob3ZlcmxhcEdyb3VwKTtcclxuICAgICAgZm9yIChjb25zdCBldmVudCBvZiBvdmVybGFwR3JvdXApIHtcclxuICAgICAgICByZXN1bHQuc3RhY2tlZC5wdXNoKHtcclxuICAgICAgICAgIGV2ZW50LFxyXG4gICAgICAgICAgc3RhY2tMZXZlbDogbGV2ZWxzLmdldChldmVudC5pZCkgPz8gMFxyXG4gICAgICAgIH0pO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICByZXR1cm4gcmVzdWx0O1xyXG59XHJcbiIsICJpbXBvcnQgeyBJQ2FsZW5kYXJFdmVudCwgSUV2ZW50QnVzLCBJRXZlbnRVcGRhdGVkUGF5bG9hZCB9IGZyb20gJy4uLy4uL3R5cGVzL0NhbGVuZGFyVHlwZXMnO1xyXG5pbXBvcnQgeyBFdmVudFNlcnZpY2UgfSBmcm9tICcuLi8uLi9zdG9yYWdlL2V2ZW50cy9FdmVudFNlcnZpY2UnO1xyXG5pbXBvcnQgeyBEYXRlU2VydmljZSB9IGZyb20gJy4uLy4uL2NvcmUvRGF0ZVNlcnZpY2UnO1xyXG5pbXBvcnQgeyBJR3JpZENvbmZpZyB9IGZyb20gJy4uLy4uL2NvcmUvSUdyaWRDb25maWcnO1xyXG5pbXBvcnQgeyBjYWxjdWxhdGVFdmVudFBvc2l0aW9uLCBzbmFwVG9HcmlkLCBwaXhlbHNUb01pbnV0ZXMgfSBmcm9tICcuLi8uLi91dGlscy9Qb3NpdGlvblV0aWxzJztcclxuaW1wb3J0IHsgQ29yZUV2ZW50cyB9IGZyb20gJy4uLy4uL2NvbnN0YW50cy9Db3JlRXZlbnRzJztcclxuaW1wb3J0IHsgSURyYWdDb2x1bW5DaGFuZ2VQYXlsb2FkLCBJRHJhZ01vdmVQYXlsb2FkLCBJRHJhZ0VuZFBheWxvYWQsIElEcmFnTGVhdmVIZWFkZXJQYXlsb2FkIH0gZnJvbSAnLi4vLi4vdHlwZXMvRHJhZ1R5cGVzJztcclxuaW1wb3J0IHsgY2FsY3VsYXRlQ29sdW1uTGF5b3V0IH0gZnJvbSAnLi9FdmVudExheW91dEVuZ2luZSc7XHJcbmltcG9ydCB7IElHcmlkR3JvdXBMYXlvdXQgfSBmcm9tICcuL0V2ZW50TGF5b3V0VHlwZXMnO1xyXG5pbXBvcnQgeyBGaWx0ZXJUZW1wbGF0ZSB9IGZyb20gJy4uLy4uL2NvcmUvRmlsdGVyVGVtcGxhdGUnO1xyXG5cclxuLyoqXHJcbiAqIEV2ZW50UmVuZGVyZXIgLSBSZW5kZXJzIGNhbGVuZGFyIGV2ZW50cyB0byB0aGUgRE9NXHJcbiAqXHJcbiAqIENMRUFOIGFwcHJvYWNoOlxyXG4gKiAtIE9ubHkgZGF0YS1pZCBhdHRyaWJ1dGUgb24gZXZlbnQgZWxlbWVudFxyXG4gKiAtIGlubmVySFRNTCBjb250YWlucyBvbmx5IHZpc2libGUgY29udGVudFxyXG4gKiAtIEV2ZW50IGRhdGEgcmV0cmlldmVkIHZpYSBFdmVudFNlcnZpY2Ugd2hlbiBuZWVkZWRcclxuICovXHJcbmV4cG9ydCBjbGFzcyBFdmVudFJlbmRlcmVyIHtcclxuICBwcml2YXRlIGNvbnRhaW5lcjogSFRNTEVsZW1lbnQgfCBudWxsID0gbnVsbDtcclxuXHJcbiAgY29uc3RydWN0b3IoXHJcbiAgICBwcml2YXRlIGV2ZW50U2VydmljZTogRXZlbnRTZXJ2aWNlLFxyXG4gICAgcHJpdmF0ZSBkYXRlU2VydmljZTogRGF0ZVNlcnZpY2UsXHJcbiAgICBwcml2YXRlIGdyaWRDb25maWc6IElHcmlkQ29uZmlnLFxyXG4gICAgcHJpdmF0ZSBldmVudEJ1czogSUV2ZW50QnVzXHJcbiAgKSB7XHJcbiAgICB0aGlzLnNldHVwTGlzdGVuZXJzKCk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBTZXR1cCBsaXN0ZW5lcnMgZm9yIGRyYWctZHJvcCBhbmQgdXBkYXRlIGV2ZW50c1xyXG4gICAqL1xyXG4gIHByaXZhdGUgc2V0dXBMaXN0ZW5lcnMoKTogdm9pZCB7XHJcbiAgICB0aGlzLmV2ZW50QnVzLm9uKENvcmVFdmVudHMuRVZFTlRfRFJBR19DT0xVTU5fQ0hBTkdFLCAoZSkgPT4ge1xyXG4gICAgICBjb25zdCBwYXlsb2FkID0gKGUgYXMgQ3VzdG9tRXZlbnQ8SURyYWdDb2x1bW5DaGFuZ2VQYXlsb2FkPikuZGV0YWlsO1xyXG4gICAgICB0aGlzLmhhbmRsZUNvbHVtbkNoYW5nZShwYXlsb2FkKTtcclxuICAgIH0pO1xyXG5cclxuICAgIHRoaXMuZXZlbnRCdXMub24oQ29yZUV2ZW50cy5FVkVOVF9EUkFHX01PVkUsIChlKSA9PiB7XHJcbiAgICAgIGNvbnN0IHBheWxvYWQgPSAoZSBhcyBDdXN0b21FdmVudDxJRHJhZ01vdmVQYXlsb2FkPikuZGV0YWlsO1xyXG4gICAgICB0aGlzLnVwZGF0ZURyYWdUaW1lc3RhbXAocGF5bG9hZCk7XHJcbiAgICB9KTtcclxuXHJcbiAgICB0aGlzLmV2ZW50QnVzLm9uKENvcmVFdmVudHMuRVZFTlRfVVBEQVRFRCwgKGUpID0+IHtcclxuICAgICAgY29uc3QgcGF5bG9hZCA9IChlIGFzIEN1c3RvbUV2ZW50PElFdmVudFVwZGF0ZWRQYXlsb2FkPikuZGV0YWlsO1xyXG4gICAgICB0aGlzLmhhbmRsZUV2ZW50VXBkYXRlZChwYXlsb2FkKTtcclxuICAgIH0pO1xyXG5cclxuICAgIHRoaXMuZXZlbnRCdXMub24oQ29yZUV2ZW50cy5FVkVOVF9EUkFHX0VORCwgKGUpID0+IHtcclxuICAgICAgY29uc3QgcGF5bG9hZCA9IChlIGFzIEN1c3RvbUV2ZW50PElEcmFnRW5kUGF5bG9hZD4pLmRldGFpbDtcclxuICAgICAgdGhpcy5oYW5kbGVEcmFnRW5kKHBheWxvYWQpO1xyXG4gICAgfSk7XHJcblxyXG4gICAgdGhpcy5ldmVudEJ1cy5vbihDb3JlRXZlbnRzLkVWRU5UX0RSQUdfTEVBVkVfSEVBREVSLCAoZSkgPT4ge1xyXG4gICAgICBjb25zdCBwYXlsb2FkID0gKGUgYXMgQ3VzdG9tRXZlbnQ8SURyYWdMZWF2ZUhlYWRlclBheWxvYWQ+KS5kZXRhaWw7XHJcbiAgICAgIHRoaXMuaGFuZGxlRHJhZ0xlYXZlSGVhZGVyKHBheWxvYWQpO1xyXG4gICAgfSk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBIYW5kbGUgRVZFTlRfRFJBR19FTkQgLSByZW1vdmUgZWxlbWVudCBpZiBkcm9wcGVkIGluIGhlYWRlclxyXG4gICAqL1xyXG4gIHByaXZhdGUgaGFuZGxlRHJhZ0VuZChwYXlsb2FkOiBJRHJhZ0VuZFBheWxvYWQpOiB2b2lkIHtcclxuICAgIGlmIChwYXlsb2FkLnRhcmdldCA9PT0gJ2hlYWRlcicpIHtcclxuICAgICAgLy8gRXZlbnQgd2FzIGRyb3BwZWQgaW4gaGVhZGVyIGRyYXdlciAtIHJlbW92ZSBmcm9tIGdyaWRcclxuICAgICAgY29uc3QgZWxlbWVudCA9IHRoaXMuY29udGFpbmVyPy5xdWVyeVNlbGVjdG9yKGBzd3AtY29udGVudC12aWV3cG9ydCBzd3AtZXZlbnRbZGF0YS1ldmVudC1pZD1cIiR7cGF5bG9hZC5zd3BFdmVudC5ldmVudElkfVwiXWApO1xyXG4gICAgICBlbGVtZW50Py5yZW1vdmUoKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIEhhbmRsZSBoZWFkZXIgaXRlbSBsZWF2aW5nIGhlYWRlciAtIGNyZWF0ZSBzd3AtZXZlbnQgaW4gZ3JpZFxyXG4gICAqL1xyXG4gIHByaXZhdGUgaGFuZGxlRHJhZ0xlYXZlSGVhZGVyKHBheWxvYWQ6IElEcmFnTGVhdmVIZWFkZXJQYXlsb2FkKTogdm9pZCB7XHJcbiAgICAvLyBPbmx5IGhhbmRsZSB3aGVuIHNvdXJjZSBpcyBoZWFkZXIgKGhlYWRlciBpdGVtIGRyYWdnZWQgdG8gZ3JpZClcclxuICAgIGlmIChwYXlsb2FkLnNvdXJjZSAhPT0gJ2hlYWRlcicpIHJldHVybjtcclxuICAgIGlmICghcGF5bG9hZC50YXJnZXRDb2x1bW4gfHwgIXBheWxvYWQuc3RhcnQgfHwgIXBheWxvYWQuZW5kKSByZXR1cm47XHJcblxyXG4gICAgLy8gVHVybiBoZWFkZXIgaXRlbSBpbnRvIGdob3N0IChzdGF5cyB2aXNpYmxlIGJ1dCBmYWRlZClcclxuICAgIGlmIChwYXlsb2FkLmVsZW1lbnQpIHtcclxuICAgICAgcGF5bG9hZC5lbGVtZW50LmNsYXNzTGlzdC5hZGQoJ2RyYWctZ2hvc3QnKTtcclxuICAgICAgcGF5bG9hZC5lbGVtZW50LnN0eWxlLm9wYWNpdHkgPSAnMC4zJztcclxuICAgICAgcGF5bG9hZC5lbGVtZW50LnN0eWxlLnBvaW50ZXJFdmVudHMgPSAnbm9uZSc7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ3JlYXRlIGV2ZW50IG9iamVjdCBmcm9tIGhlYWRlciBpdGVtIGRhdGFcclxuICAgIGNvbnN0IGV2ZW50OiBJQ2FsZW5kYXJFdmVudCA9IHtcclxuICAgICAgaWQ6IHBheWxvYWQuZXZlbnRJZCxcclxuICAgICAgdGl0bGU6IHBheWxvYWQudGl0bGUgfHwgJycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnJyxcclxuICAgICAgc3RhcnQ6IHBheWxvYWQuc3RhcnQsXHJcbiAgICAgIGVuZDogcGF5bG9hZC5lbmQsXHJcbiAgICAgIHR5cGU6ICdjdXN0b21lcicsXHJcbiAgICAgIGFsbERheTogZmFsc2UsXHJcbiAgICAgIHN5bmNTdGF0dXM6ICdwZW5kaW5nJ1xyXG4gICAgfTtcclxuXHJcbiAgICAvLyBDcmVhdGUgc3dwLWV2ZW50IGVsZW1lbnQgdXNpbmcgZXhpc3RpbmcgbWV0aG9kXHJcbiAgICBjb25zdCBlbGVtZW50ID0gdGhpcy5jcmVhdGVFdmVudEVsZW1lbnQoZXZlbnQpO1xyXG5cclxuICAgIC8vIEFkZCB0byB0YXJnZXQgY29sdW1uXHJcbiAgICBsZXQgZXZlbnRzTGF5ZXIgPSBwYXlsb2FkLnRhcmdldENvbHVtbi5xdWVyeVNlbGVjdG9yKCdzd3AtZXZlbnRzLWxheWVyJyk7XHJcbiAgICBpZiAoIWV2ZW50c0xheWVyKSB7XHJcbiAgICAgIGV2ZW50c0xheWVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3dwLWV2ZW50cy1sYXllcicpO1xyXG4gICAgICBwYXlsb2FkLnRhcmdldENvbHVtbi5hcHBlbmRDaGlsZChldmVudHNMYXllcik7XHJcbiAgICB9XHJcbiAgICBldmVudHNMYXllci5hcHBlbmRDaGlsZChlbGVtZW50KTtcclxuXHJcbiAgICAvLyBNYXJrIGFzIGRyYWdnaW5nIHNvIERyYWdEcm9wTWFuYWdlciBjYW4gY29udGludWUgd2l0aCBpdFxyXG4gICAgZWxlbWVudC5jbGFzc0xpc3QuYWRkKCdkcmFnZ2luZycpO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogSGFuZGxlIEVWRU5UX1VQREFURUQgLSByZS1yZW5kZXIgYWZmZWN0ZWQgY29sdW1uc1xyXG4gICAqL1xyXG4gIHByaXZhdGUgYXN5bmMgaGFuZGxlRXZlbnRVcGRhdGVkKHBheWxvYWQ6IElFdmVudFVwZGF0ZWRQYXlsb2FkKTogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICAvLyBSZS1yZW5kZXIgc291cmNlIGNvbHVtbiAoaWYgZGlmZmVyZW50IGZyb20gdGFyZ2V0KVxyXG4gICAgaWYgKHBheWxvYWQuc291cmNlQ29sdW1uS2V5ICE9PSBwYXlsb2FkLnRhcmdldENvbHVtbktleSkge1xyXG4gICAgICBhd2FpdCB0aGlzLnJlcmVuZGVyQ29sdW1uKHBheWxvYWQuc291cmNlQ29sdW1uS2V5KTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBSZS1yZW5kZXIgdGFyZ2V0IGNvbHVtblxyXG4gICAgYXdhaXQgdGhpcy5yZXJlbmRlckNvbHVtbihwYXlsb2FkLnRhcmdldENvbHVtbktleSk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBSZS1yZW5kZXIgYSBzaW5nbGUgY29sdW1uIHdpdGggZnJlc2ggZGF0YSBmcm9tIEluZGV4ZWREQlxyXG4gICAqL1xyXG4gIHByaXZhdGUgYXN5bmMgcmVyZW5kZXJDb2x1bW4oY29sdW1uS2V5OiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcclxuICAgIGNvbnN0IGNvbHVtbiA9IHRoaXMuZmluZENvbHVtbihjb2x1bW5LZXkpO1xyXG4gICAgaWYgKCFjb2x1bW4pIHJldHVybjtcclxuXHJcbiAgICAvLyBSZWFkIGRhdGUgYW5kIHJlc291cmNlSWQgZGlyZWN0bHkgZnJvbSBjb2x1bW4gYXR0cmlidXRlcyAoY29sdW1uS2V5IGlzIG9wYXF1ZSlcclxuICAgIGNvbnN0IGRhdGUgPSBjb2x1bW4uZGF0YXNldC5kYXRlO1xyXG4gICAgY29uc3QgcmVzb3VyY2VJZCA9IGNvbHVtbi5kYXRhc2V0LnJlc291cmNlSWQ7XHJcblxyXG4gICAgaWYgKCFkYXRlKSByZXR1cm47XHJcblxyXG4gICAgLy8gR2V0IGRhdGUgcmFuZ2UgZm9yIHRoaXMgZGF5XHJcbiAgICBjb25zdCBzdGFydERhdGUgPSBuZXcgRGF0ZShkYXRlKTtcclxuICAgIGNvbnN0IGVuZERhdGUgPSBuZXcgRGF0ZShkYXRlKTtcclxuICAgIGVuZERhdGUuc2V0SG91cnMoMjMsIDU5LCA1OSwgOTk5KTtcclxuXHJcbiAgICAvLyBGZXRjaCBldmVudHMgZnJvbSBJbmRleGVkREJcclxuICAgIGNvbnN0IGV2ZW50cyA9IHJlc291cmNlSWRcclxuICAgICAgPyBhd2FpdCB0aGlzLmV2ZW50U2VydmljZS5nZXRCeVJlc291cmNlQW5kRGF0ZVJhbmdlKHJlc291cmNlSWQsIHN0YXJ0RGF0ZSwgZW5kRGF0ZSlcclxuICAgICAgOiBhd2FpdCB0aGlzLmV2ZW50U2VydmljZS5nZXRCeURhdGVSYW5nZShzdGFydERhdGUsIGVuZERhdGUpO1xyXG5cclxuICAgIC8vIEZpbHRlciB0byB0aW1lZCBldmVudHMgYW5kIG1hdGNoIGRhdGUgZXhhY3RseVxyXG4gICAgY29uc3QgdGltZWRFdmVudHMgPSBldmVudHMuZmlsdGVyKGV2ZW50ID0+XHJcbiAgICAgICFldmVudC5hbGxEYXkgJiYgdGhpcy5kYXRlU2VydmljZS5nZXREYXRlS2V5KGV2ZW50LnN0YXJ0KSA9PT0gZGF0ZVxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBHZXQgb3IgY3JlYXRlIGV2ZW50cyBsYXllclxyXG4gICAgbGV0IGV2ZW50c0xheWVyID0gY29sdW1uLnF1ZXJ5U2VsZWN0b3IoJ3N3cC1ldmVudHMtbGF5ZXInKTtcclxuICAgIGlmICghZXZlbnRzTGF5ZXIpIHtcclxuICAgICAgZXZlbnRzTGF5ZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzd3AtZXZlbnRzLWxheWVyJyk7XHJcbiAgICAgIGNvbHVtbi5hcHBlbmRDaGlsZChldmVudHNMYXllcik7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2xlYXIgZXhpc3RpbmcgZXZlbnRzXHJcbiAgICBldmVudHNMYXllci5pbm5lckhUTUwgPSAnJztcclxuXHJcbiAgICAvLyBDYWxjdWxhdGUgbGF5b3V0IHdpdGggc3RhY2tpbmcvZ3JvdXBpbmdcclxuICAgIGNvbnN0IGxheW91dCA9IGNhbGN1bGF0ZUNvbHVtbkxheW91dCh0aW1lZEV2ZW50cywgdGhpcy5ncmlkQ29uZmlnKTtcclxuXHJcbiAgICAvLyBSZW5kZXIgR1JJRCBncm91cHNcclxuICAgIGxheW91dC5ncmlkcy5mb3JFYWNoKGdyaWQgPT4ge1xyXG4gICAgICBjb25zdCBncm91cEVsID0gdGhpcy5yZW5kZXJHcmlkR3JvdXAoZ3JpZCk7XHJcbiAgICAgIGV2ZW50c0xheWVyIS5hcHBlbmRDaGlsZChncm91cEVsKTtcclxuICAgIH0pO1xyXG5cclxuICAgIC8vIFJlbmRlciBTVEFDS0VEIGV2ZW50c1xyXG4gICAgbGF5b3V0LnN0YWNrZWQuZm9yRWFjaChpdGVtID0+IHtcclxuICAgICAgY29uc3QgZXZlbnRFbCA9IHRoaXMucmVuZGVyU3RhY2tlZEV2ZW50KGl0ZW0uZXZlbnQsIGl0ZW0uc3RhY2tMZXZlbCk7XHJcbiAgICAgIGV2ZW50c0xheWVyIS5hcHBlbmRDaGlsZChldmVudEVsKTtcclxuICAgIH0pO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogRmluZCBhIGNvbHVtbiBlbGVtZW50IGJ5IGNvbHVtbktleVxyXG4gICAqL1xyXG4gIHByaXZhdGUgZmluZENvbHVtbihjb2x1bW5LZXk6IHN0cmluZyk6IEhUTUxFbGVtZW50IHwgbnVsbCB7XHJcbiAgICBpZiAoIXRoaXMuY29udGFpbmVyKSByZXR1cm4gbnVsbDtcclxuICAgIHJldHVybiB0aGlzLmNvbnRhaW5lci5xdWVyeVNlbGVjdG9yKGBzd3AtZGF5LWNvbHVtbltkYXRhLWNvbHVtbi1rZXk9XCIke2NvbHVtbktleX1cIl1gKSBhcyBIVE1MRWxlbWVudDtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIEhhbmRsZSBldmVudCBtb3ZpbmcgdG8gYSBuZXcgY29sdW1uIGR1cmluZyBkcmFnXHJcbiAgICovXHJcbiAgcHJpdmF0ZSBoYW5kbGVDb2x1bW5DaGFuZ2UocGF5bG9hZDogSURyYWdDb2x1bW5DaGFuZ2VQYXlsb2FkKTogdm9pZCB7XHJcbiAgICBjb25zdCBldmVudHNMYXllciA9IHBheWxvYWQubmV3Q29sdW1uLnF1ZXJ5U2VsZWN0b3IoJ3N3cC1ldmVudHMtbGF5ZXInKTtcclxuICAgIGlmICghZXZlbnRzTGF5ZXIpIHJldHVybjtcclxuXHJcbiAgICAvLyBNb3ZlIGVsZW1lbnQgdG8gbmV3IGNvbHVtblxyXG4gICAgZXZlbnRzTGF5ZXIuYXBwZW5kQ2hpbGQocGF5bG9hZC5lbGVtZW50KTtcclxuXHJcbiAgICAvLyBQcmVzZXJ2ZSBZIHBvc2l0aW9uXHJcbiAgICBwYXlsb2FkLmVsZW1lbnQuc3R5bGUudG9wID0gYCR7cGF5bG9hZC5jdXJyZW50WX1weGA7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBVcGRhdGUgdGltZXN0YW1wIGRpc3BsYXkgZHVyaW5nIGRyYWcgKHNuYXBwZWQgdG8gZ3JpZClcclxuICAgKi9cclxuICBwcml2YXRlIHVwZGF0ZURyYWdUaW1lc3RhbXAocGF5bG9hZDogSURyYWdNb3ZlUGF5bG9hZCk6IHZvaWQge1xyXG4gICAgY29uc3QgdGltZUVsID0gcGF5bG9hZC5lbGVtZW50LnF1ZXJ5U2VsZWN0b3IoJ3N3cC1ldmVudC10aW1lJyk7XHJcbiAgICBpZiAoIXRpbWVFbCkgcmV0dXJuO1xyXG5cclxuICAgIC8vIFNuYXAgcG9zaXRpb24gdG8gZ3JpZCBpbnRlcnZhbFxyXG4gICAgY29uc3Qgc25hcHBlZFkgPSBzbmFwVG9HcmlkKHBheWxvYWQuY3VycmVudFksIHRoaXMuZ3JpZENvbmZpZyk7XHJcblxyXG4gICAgLy8gQ2FsY3VsYXRlIG5ldyBzdGFydCB0aW1lXHJcbiAgICBjb25zdCBtaW51dGVzRnJvbUdyaWRTdGFydCA9IHBpeGVsc1RvTWludXRlcyhzbmFwcGVkWSwgdGhpcy5ncmlkQ29uZmlnKTtcclxuICAgIGNvbnN0IHN0YXJ0TWludXRlcyA9ICh0aGlzLmdyaWRDb25maWcuZGF5U3RhcnRIb3VyICogNjApICsgbWludXRlc0Zyb21HcmlkU3RhcnQ7XHJcblxyXG4gICAgLy8gS2VlcCBvcmlnaW5hbCBkdXJhdGlvbiAoZnJvbSBlbGVtZW50IGhlaWdodClcclxuICAgIGNvbnN0IGhlaWdodCA9IHBhcnNlRmxvYXQocGF5bG9hZC5lbGVtZW50LnN0eWxlLmhlaWdodCkgfHwgdGhpcy5ncmlkQ29uZmlnLmhvdXJIZWlnaHQ7XHJcbiAgICBjb25zdCBkdXJhdGlvbk1pbnV0ZXMgPSBwaXhlbHNUb01pbnV0ZXMoaGVpZ2h0LCB0aGlzLmdyaWRDb25maWcpO1xyXG5cclxuICAgIC8vIENyZWF0ZSBEYXRlIG9iamVjdHMgZm9yIGNvbnNpc3RlbnQgZm9ybWF0dGluZyB2aWEgRGF0ZVNlcnZpY2VcclxuICAgIGNvbnN0IHN0YXJ0ID0gdGhpcy5taW51dGVzVG9EYXRlKHN0YXJ0TWludXRlcyk7XHJcbiAgICBjb25zdCBlbmQgPSB0aGlzLm1pbnV0ZXNUb0RhdGUoc3RhcnRNaW51dGVzICsgZHVyYXRpb25NaW51dGVzKTtcclxuXHJcbiAgICB0aW1lRWwudGV4dENvbnRlbnQgPSB0aGlzLmRhdGVTZXJ2aWNlLmZvcm1hdFRpbWVSYW5nZShzdGFydCwgZW5kKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIENvbnZlcnQgbWludXRlcyBzaW5jZSBtaWRuaWdodCB0byBhIERhdGUgb2JqZWN0ICh0b2RheSlcclxuICAgKi9cclxuICBwcml2YXRlIG1pbnV0ZXNUb0RhdGUobWludXRlczogbnVtYmVyKTogRGF0ZSB7XHJcbiAgICBjb25zdCBkYXRlID0gbmV3IERhdGUoKTtcclxuICAgIGRhdGUuc2V0SG91cnMoTWF0aC5mbG9vcihtaW51dGVzIC8gNjApICUgMjQsIG1pbnV0ZXMgJSA2MCwgMCwgMCk7XHJcbiAgICByZXR1cm4gZGF0ZTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFJlbmRlciBldmVudHMgZm9yIHZpc2libGUgZGF0ZXMgaW50byBkYXkgY29sdW1uc1xyXG4gICAqIEBwYXJhbSBjb250YWluZXIgLSBDYWxlbmRhciBjb250YWluZXIgZWxlbWVudFxyXG4gICAqIEBwYXJhbSBmaWx0ZXIgLSBGaWx0ZXIgd2l0aCAnZGF0ZScgYW5kIG9wdGlvbmFsbHkgJ3Jlc291cmNlJyBhcnJheXNcclxuICAgKiBAcGFyYW0gZmlsdGVyVGVtcGxhdGUgLSBUZW1wbGF0ZSBmb3IgbWF0Y2hpbmcgZXZlbnRzIHRvIGNvbHVtbnNcclxuICAgKi9cclxuICBhc3luYyByZW5kZXIoY29udGFpbmVyOiBIVE1MRWxlbWVudCwgZmlsdGVyOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmdbXT4sIGZpbHRlclRlbXBsYXRlOiBGaWx0ZXJUZW1wbGF0ZSk6IFByb21pc2U8dm9pZD4ge1xyXG4gICAgLy8gU3RvcmUgY29udGFpbmVyIHJlZmVyZW5jZSBmb3IgbGF0ZXIgcmUtcmVuZGVyc1xyXG4gICAgdGhpcy5jb250YWluZXIgPSBjb250YWluZXI7XHJcblxyXG4gICAgY29uc3QgdmlzaWJsZURhdGVzID0gZmlsdGVyWydkYXRlJ10gfHwgW107XHJcblxyXG4gICAgaWYgKHZpc2libGVEYXRlcy5sZW5ndGggPT09IDApIHJldHVybjtcclxuXHJcbiAgICAvLyBHZXQgZGF0ZSByYW5nZSBmb3IgcXVlcnlcclxuICAgIGNvbnN0IHN0YXJ0RGF0ZSA9IG5ldyBEYXRlKHZpc2libGVEYXRlc1swXSk7XHJcbiAgICBjb25zdCBlbmREYXRlID0gbmV3IERhdGUodmlzaWJsZURhdGVzW3Zpc2libGVEYXRlcy5sZW5ndGggLSAxXSk7XHJcbiAgICBlbmREYXRlLnNldEhvdXJzKDIzLCA1OSwgNTksIDk5OSk7XHJcblxyXG4gICAgLy8gRmV0Y2ggZXZlbnRzIGZyb20gSW5kZXhlZERCXHJcbiAgICBjb25zdCBldmVudHMgPSBhd2FpdCB0aGlzLmV2ZW50U2VydmljZS5nZXRCeURhdGVSYW5nZShzdGFydERhdGUsIGVuZERhdGUpO1xyXG5cclxuICAgIC8vIEZpbmQgZGF5IGNvbHVtbnNcclxuICAgIGNvbnN0IGRheUNvbHVtbnMgPSBjb250YWluZXIucXVlcnlTZWxlY3Rvcignc3dwLWRheS1jb2x1bW5zJyk7XHJcbiAgICBpZiAoIWRheUNvbHVtbnMpIHJldHVybjtcclxuXHJcbiAgICBjb25zdCBjb2x1bW5zID0gZGF5Q29sdW1ucy5xdWVyeVNlbGVjdG9yQWxsKCdzd3AtZGF5LWNvbHVtbicpO1xyXG5cclxuICAgIC8vIFJlbmRlciBldmVudHMgaW50byBlYWNoIGNvbHVtbiBiYXNlZCBvbiBGaWx0ZXJUZW1wbGF0ZSBtYXRjaGluZ1xyXG4gICAgY29sdW1ucy5mb3JFYWNoKGNvbHVtbiA9PiB7XHJcbiAgICAgIGNvbnN0IGNvbHVtbkVsID0gY29sdW1uIGFzIEhUTUxFbGVtZW50O1xyXG5cclxuICAgICAgLy8gVXNlIEZpbHRlclRlbXBsYXRlIGZvciBtYXRjaGluZyAtIG9ubHkgZmllbGRzIGluIHRlbXBsYXRlIGFyZSBjaGVja2VkXHJcbiAgICAgIGNvbnN0IGNvbHVtbkV2ZW50cyA9IGV2ZW50cy5maWx0ZXIoZXZlbnQgPT4gZmlsdGVyVGVtcGxhdGUubWF0Y2hlcyhldmVudCwgY29sdW1uRWwpKTtcclxuXHJcbiAgICAgIC8vIEdldCBvciBjcmVhdGUgZXZlbnRzIGxheWVyXHJcbiAgICAgIGxldCBldmVudHNMYXllciA9IGNvbHVtbi5xdWVyeVNlbGVjdG9yKCdzd3AtZXZlbnRzLWxheWVyJyk7XHJcbiAgICAgIGlmICghZXZlbnRzTGF5ZXIpIHtcclxuICAgICAgICBldmVudHNMYXllciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3N3cC1ldmVudHMtbGF5ZXInKTtcclxuICAgICAgICBjb2x1bW4uYXBwZW5kQ2hpbGQoZXZlbnRzTGF5ZXIpO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBDbGVhciBleGlzdGluZyBldmVudHNcclxuICAgICAgZXZlbnRzTGF5ZXIuaW5uZXJIVE1MID0gJyc7XHJcblxyXG4gICAgICAvLyBGaWx0ZXIgdG8gdGltZWQgZXZlbnRzIG9ubHlcclxuICAgICAgY29uc3QgdGltZWRFdmVudHMgPSBjb2x1bW5FdmVudHMuZmlsdGVyKGV2ZW50ID0+ICFldmVudC5hbGxEYXkpO1xyXG5cclxuICAgICAgLy8gQ2FsY3VsYXRlIGxheW91dCB3aXRoIHN0YWNraW5nL2dyb3VwaW5nXHJcbiAgICAgIGNvbnN0IGxheW91dCA9IGNhbGN1bGF0ZUNvbHVtbkxheW91dCh0aW1lZEV2ZW50cywgdGhpcy5ncmlkQ29uZmlnKTtcclxuXHJcbiAgICAgIC8vIFJlbmRlciBHUklEIGdyb3VwcyAoc2ltdWx0YW5lb3VzIGV2ZW50cyBzaWRlLWJ5LXNpZGUpXHJcbiAgICAgIGxheW91dC5ncmlkcy5mb3JFYWNoKGdyaWQgPT4ge1xyXG4gICAgICAgIGNvbnN0IGdyb3VwRWwgPSB0aGlzLnJlbmRlckdyaWRHcm91cChncmlkKTtcclxuICAgICAgICBldmVudHNMYXllciEuYXBwZW5kQ2hpbGQoZ3JvdXBFbCk7XHJcbiAgICAgIH0pO1xyXG5cclxuICAgICAgLy8gUmVuZGVyIFNUQUNLRUQgZXZlbnRzIChvdmVybGFwcGluZyB3aXRoIG1hcmdpbiBvZmZzZXQpXHJcbiAgICAgIGxheW91dC5zdGFja2VkLmZvckVhY2goaXRlbSA9PiB7XHJcbiAgICAgICAgY29uc3QgZXZlbnRFbCA9IHRoaXMucmVuZGVyU3RhY2tlZEV2ZW50KGl0ZW0uZXZlbnQsIGl0ZW0uc3RhY2tMZXZlbCk7XHJcbiAgICAgICAgZXZlbnRzTGF5ZXIhLmFwcGVuZENoaWxkKGV2ZW50RWwpO1xyXG4gICAgICB9KTtcclxuICAgIH0pO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogQ3JlYXRlIGEgc2luZ2xlIGV2ZW50IGVsZW1lbnRcclxuICAgKlxyXG4gICAqIENMRUFOIGFwcHJvYWNoOlxyXG4gICAqIC0gT25seSBkYXRhLWlkIGZvciBsb29rdXBcclxuICAgKiAtIFZpc2libGUgY29udGVudCBpbiBpbm5lckhUTUwgb25seVxyXG4gICAqL1xyXG4gIHByaXZhdGUgY3JlYXRlRXZlbnRFbGVtZW50KGV2ZW50OiBJQ2FsZW5kYXJFdmVudCk6IEhUTUxFbGVtZW50IHtcclxuICAgIGNvbnN0IGVsZW1lbnQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzd3AtZXZlbnQnKTtcclxuXHJcbiAgICAvLyBEYXRhIGF0dHJpYnV0ZXMgZm9yIFN3cEV2ZW50IGNvbXBhdGliaWxpdHlcclxuICAgIGVsZW1lbnQuZGF0YXNldC5ldmVudElkID0gZXZlbnQuaWQ7XHJcbiAgICBpZiAoZXZlbnQucmVzb3VyY2VJZCkge1xyXG4gICAgICBlbGVtZW50LmRhdGFzZXQucmVzb3VyY2VJZCA9IGV2ZW50LnJlc291cmNlSWQ7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2FsY3VsYXRlIHBvc2l0aW9uXHJcbiAgICBjb25zdCBwb3NpdGlvbiA9IGNhbGN1bGF0ZUV2ZW50UG9zaXRpb24oZXZlbnQuc3RhcnQsIGV2ZW50LmVuZCwgdGhpcy5ncmlkQ29uZmlnKTtcclxuICAgIGVsZW1lbnQuc3R5bGUudG9wID0gYCR7cG9zaXRpb24udG9wfXB4YDtcclxuICAgIGVsZW1lbnQuc3R5bGUuaGVpZ2h0ID0gYCR7cG9zaXRpb24uaGVpZ2h0fXB4YDtcclxuXHJcbiAgICAvLyBDb2xvciBjbGFzcyBiYXNlZCBvbiBldmVudCB0eXBlXHJcbiAgICBjb25zdCBjb2xvckNsYXNzID0gdGhpcy5nZXRDb2xvckNsYXNzKGV2ZW50KTtcclxuICAgIGlmIChjb2xvckNsYXNzKSB7XHJcbiAgICAgIGVsZW1lbnQuY2xhc3NMaXN0LmFkZChjb2xvckNsYXNzKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBWaXNpYmxlIGNvbnRlbnQgb25seVxyXG4gICAgZWxlbWVudC5pbm5lckhUTUwgPSBgXHJcbiAgICAgIDxzd3AtZXZlbnQtdGltZT4ke3RoaXMuZGF0ZVNlcnZpY2UuZm9ybWF0VGltZVJhbmdlKGV2ZW50LnN0YXJ0LCBldmVudC5lbmQpfTwvc3dwLWV2ZW50LXRpbWU+XHJcbiAgICAgIDxzd3AtZXZlbnQtdGl0bGU+JHt0aGlzLmVzY2FwZUh0bWwoZXZlbnQudGl0bGUpfTwvc3dwLWV2ZW50LXRpdGxlPlxyXG4gICAgICAke2V2ZW50LmRlc2NyaXB0aW9uID8gYDxzd3AtZXZlbnQtZGVzY3JpcHRpb24+JHt0aGlzLmVzY2FwZUh0bWwoZXZlbnQuZGVzY3JpcHRpb24pfTwvc3dwLWV2ZW50LWRlc2NyaXB0aW9uPmAgOiAnJ31cclxuICAgIGA7XHJcblxyXG4gICAgcmV0dXJuIGVsZW1lbnQ7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBHZXQgY29sb3IgY2xhc3MgYmFzZWQgb24gbWV0YWRhdGEuY29sb3Igb3IgZXZlbnQgdHlwZVxyXG4gICAqL1xyXG4gIHByaXZhdGUgZ2V0Q29sb3JDbGFzcyhldmVudDogSUNhbGVuZGFyRXZlbnQpOiBzdHJpbmcge1xyXG4gICAgLy8gQ2hlY2sgbWV0YWRhdGEuY29sb3IgZmlyc3RcclxuICAgIGlmIChldmVudC5tZXRhZGF0YT8uY29sb3IpIHtcclxuICAgICAgcmV0dXJuIGBpcy0ke2V2ZW50Lm1ldGFkYXRhLmNvbG9yfWA7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRmFsbGJhY2sgdG8gdHlwZS1iYXNlZCBjb2xvclxyXG4gICAgY29uc3QgdHlwZUNvbG9yczogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHtcclxuICAgICAgJ2N1c3RvbWVyJzogJ2lzLWJsdWUnLFxyXG4gICAgICAndmFjYXRpb24nOiAnaXMtZ3JlZW4nLFxyXG4gICAgICAnYnJlYWsnOiAnaXMtYW1iZXInLFxyXG4gICAgICAnbWVldGluZyc6ICdpcy1wdXJwbGUnLFxyXG4gICAgICAnYmxvY2tlZCc6ICdpcy1yZWQnXHJcbiAgICB9O1xyXG4gICAgcmV0dXJuIHR5cGVDb2xvcnNbZXZlbnQudHlwZV0gfHwgJ2lzLWJsdWUnO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogRXNjYXBlIEhUTUwgdG8gcHJldmVudCBYU1NcclxuICAgKi9cclxuICBwcml2YXRlIGVzY2FwZUh0bWwodGV4dDogc3RyaW5nKTogc3RyaW5nIHtcclxuICAgIGNvbnN0IGRpdiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpO1xyXG4gICAgZGl2LnRleHRDb250ZW50ID0gdGV4dDtcclxuICAgIHJldHVybiBkaXYuaW5uZXJIVE1MO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogUmVuZGVyIGEgR1JJRCBncm91cCB3aXRoIHNpZGUtYnktc2lkZSBjb2x1bW5zXHJcbiAgICogVXNlZCB3aGVuIG11bHRpcGxlIGV2ZW50cyBzdGFydCBhdCB0aGUgc2FtZSB0aW1lXHJcbiAgICovXHJcbiAgcHJpdmF0ZSByZW5kZXJHcmlkR3JvdXAobGF5b3V0OiBJR3JpZEdyb3VwTGF5b3V0KTogSFRNTEVsZW1lbnQge1xyXG4gICAgY29uc3QgZ3JvdXAgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzd3AtZXZlbnQtZ3JvdXAnKTtcclxuICAgIGdyb3VwLmNsYXNzTGlzdC5hZGQoYGNvbHMtJHtsYXlvdXQuY29sdW1ucy5sZW5ndGh9YCk7XHJcbiAgICBncm91cC5zdHlsZS50b3AgPSBgJHtsYXlvdXQucG9zaXRpb24udG9wfXB4YDtcclxuXHJcbiAgICAvLyBTdGFjayBsZXZlbCBzdHlsaW5nIGZvciBlbnRpcmUgZ3JvdXAgKGlmIG5lc3RlZCBpbiBhbm90aGVyIGV2ZW50KVxyXG4gICAgaWYgKGxheW91dC5zdGFja0xldmVsID4gMCkge1xyXG4gICAgICBncm91cC5zdHlsZS5tYXJnaW5MZWZ0ID0gYCR7bGF5b3V0LnN0YWNrTGV2ZWwgKiAxNX1weGA7XHJcbiAgICAgIGdyb3VwLnN0eWxlLnpJbmRleCA9IGAkezEwMCArIGxheW91dC5zdGFja0xldmVsfWA7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2FsY3VsYXRlIHRoZSBoZWlnaHQgbmVlZGVkIGZvciB0aGUgZ3JvdXAgKHRhbGxlc3QgZXZlbnQpXHJcbiAgICBsZXQgbWF4Qm90dG9tID0gMDtcclxuICAgIGZvciAoY29uc3QgZXZlbnQgb2YgbGF5b3V0LmV2ZW50cykge1xyXG4gICAgICBjb25zdCBwb3MgPSBjYWxjdWxhdGVFdmVudFBvc2l0aW9uKGV2ZW50LnN0YXJ0LCBldmVudC5lbmQsIHRoaXMuZ3JpZENvbmZpZyk7XHJcbiAgICAgIGNvbnN0IGV2ZW50Qm90dG9tID0gcG9zLnRvcCArIHBvcy5oZWlnaHQ7XHJcbiAgICAgIGlmIChldmVudEJvdHRvbSA+IG1heEJvdHRvbSkgbWF4Qm90dG9tID0gZXZlbnRCb3R0b207XHJcbiAgICB9XHJcbiAgICBjb25zdCBncm91cEhlaWdodCA9IG1heEJvdHRvbSAtIGxheW91dC5wb3NpdGlvbi50b3A7XHJcbiAgICBncm91cC5zdHlsZS5oZWlnaHQgPSBgJHtncm91cEhlaWdodH1weGA7XHJcblxyXG4gICAgLy8gQ3JlYXRlIHdyYXBwZXIgZGl2IGZvciBlYWNoIGNvbHVtblxyXG4gICAgbGF5b3V0LmNvbHVtbnMuZm9yRWFjaChjb2x1bW5FdmVudHMgPT4ge1xyXG4gICAgICBjb25zdCB3cmFwcGVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XHJcbiAgICAgIHdyYXBwZXIuc3R5bGUucG9zaXRpb24gPSAncmVsYXRpdmUnO1xyXG5cclxuICAgICAgY29sdW1uRXZlbnRzLmZvckVhY2goZXZlbnQgPT4ge1xyXG4gICAgICAgIGNvbnN0IGV2ZW50RWwgPSB0aGlzLmNyZWF0ZUV2ZW50RWxlbWVudChldmVudCk7XHJcbiAgICAgICAgLy8gUG9zaXRpb24gcmVsYXRpdmUgdG8gZ3JvdXAgdG9wXHJcbiAgICAgICAgY29uc3QgcG9zID0gY2FsY3VsYXRlRXZlbnRQb3NpdGlvbihldmVudC5zdGFydCwgZXZlbnQuZW5kLCB0aGlzLmdyaWRDb25maWcpO1xyXG4gICAgICAgIGV2ZW50RWwuc3R5bGUudG9wID0gYCR7cG9zLnRvcCAtIGxheW91dC5wb3NpdGlvbi50b3B9cHhgO1xyXG4gICAgICAgIGV2ZW50RWwuc3R5bGUucG9zaXRpb24gPSAnYWJzb2x1dGUnO1xyXG4gICAgICAgIGV2ZW50RWwuc3R5bGUubGVmdCA9ICcwJztcclxuICAgICAgICBldmVudEVsLnN0eWxlLnJpZ2h0ID0gJzAnO1xyXG4gICAgICAgIHdyYXBwZXIuYXBwZW5kQ2hpbGQoZXZlbnRFbCk7XHJcbiAgICAgIH0pO1xyXG5cclxuICAgICAgZ3JvdXAuYXBwZW5kQ2hpbGQod3JhcHBlcik7XHJcbiAgICB9KTtcclxuXHJcbiAgICByZXR1cm4gZ3JvdXA7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBSZW5kZXIgYSBTVEFDS0VEIGV2ZW50IHdpdGggbWFyZ2luLWxlZnQgb2Zmc2V0XHJcbiAgICogVXNlZCBmb3Igb3ZlcmxhcHBpbmcgZXZlbnRzIHRoYXQgZG9uJ3Qgc3RhcnQgYXQgdGhlIHNhbWUgdGltZVxyXG4gICAqL1xyXG4gIHByaXZhdGUgcmVuZGVyU3RhY2tlZEV2ZW50KGV2ZW50OiBJQ2FsZW5kYXJFdmVudCwgc3RhY2tMZXZlbDogbnVtYmVyKTogSFRNTEVsZW1lbnQge1xyXG4gICAgY29uc3QgZWxlbWVudCA9IHRoaXMuY3JlYXRlRXZlbnRFbGVtZW50KGV2ZW50KTtcclxuXHJcbiAgICAvLyBBZGQgc3RhY2sgbWV0YWRhdGEgZm9yIGRyYWctZHJvcCBhbmQgb3RoZXIgZmVhdHVyZXNcclxuICAgIGVsZW1lbnQuZGF0YXNldC5zdGFja0xpbmsgPSBKU09OLnN0cmluZ2lmeSh7IHN0YWNrTGV2ZWwgfSk7XHJcblxyXG4gICAgLy8gVmlzdWFsIHN0eWxpbmcgYmFzZWQgb24gc3RhY2sgbGV2ZWxcclxuICAgIGlmIChzdGFja0xldmVsID4gMCkge1xyXG4gICAgICBlbGVtZW50LnN0eWxlLm1hcmdpbkxlZnQgPSBgJHtzdGFja0xldmVsICogMTV9cHhgO1xyXG4gICAgICBlbGVtZW50LnN0eWxlLnpJbmRleCA9IGAkezEwMCArIHN0YWNrTGV2ZWx9YDtcclxuICAgIH1cclxuXHJcbiAgICByZXR1cm4gZWxlbWVudDtcclxuICB9XHJcbn1cclxuIiwgImltcG9ydCB7IElSZW5kZXJlciwgSVJlbmRlckNvbnRleHQgfSBmcm9tICcuL0lHcm91cGluZ1JlbmRlcmVyJztcclxuXHJcbi8qKlxyXG4gKiBFbnRpdHkgbXVzdCBoYXZlIGlkXHJcbiAqL1xyXG5leHBvcnQgaW50ZXJmYWNlIElHcm91cGluZ0VudGl0eSB7XHJcbiAgaWQ6IHN0cmluZztcclxufVxyXG5cclxuLyoqXHJcbiAqIENvbmZpZ3VyYXRpb24gZm9yIGEgZ3JvdXBpbmcgcmVuZGVyZXJcclxuICovXHJcbmV4cG9ydCBpbnRlcmZhY2UgSUdyb3VwaW5nUmVuZGVyZXJDb25maWcge1xyXG4gIGVsZW1lbnRUYWc6IHN0cmluZzsgICAgICAvLyBlLmcuLCAnc3dwLXRlYW0taGVhZGVyJ1xyXG4gIGlkQXR0cmlidXRlOiBzdHJpbmc7ICAgICAvLyBlLmcuLCAndGVhbUlkJyAtPiBkYXRhLXRlYW0taWRcclxuICBjb2xzcGFuVmFyOiBzdHJpbmc7ICAgICAgLy8gZS5nLiwgJy0tdGVhbS1jb2xzJ1xyXG59XHJcblxyXG4vKipcclxuICogQWJzdHJhY3QgYmFzZSBjbGFzcyBmb3IgZ3JvdXBpbmcgcmVuZGVyZXJzXHJcbiAqXHJcbiAqIEhhbmRsZXM6XHJcbiAqIC0gRmV0Y2hpbmcgZW50aXRpZXMgYnkgSURzXHJcbiAqIC0gQ2FsY3VsYXRpbmcgY29sc3BhbiBmcm9tIHBhcmVudENoaWxkTWFwXHJcbiAqIC0gQ3JlYXRpbmcgaGVhZGVyIGVsZW1lbnRzXHJcbiAqIC0gQXBwZW5kaW5nIHRvIGNvbnRhaW5lclxyXG4gKlxyXG4gKiBTdWJjbGFzc2VzIG92ZXJyaWRlOlxyXG4gKiAtIHJlbmRlckhlYWRlcigpIGZvciBjdXN0b20gY29udGVudFxyXG4gKiAtIGdldERpc3BsYXlOYW1lKCkgZm9yIGVudGl0eSBkaXNwbGF5IHRleHRcclxuICovXHJcbmV4cG9ydCBhYnN0cmFjdCBjbGFzcyBCYXNlR3JvdXBpbmdSZW5kZXJlcjxUIGV4dGVuZHMgSUdyb3VwaW5nRW50aXR5PiBpbXBsZW1lbnRzIElSZW5kZXJlciB7XHJcbiAgYWJzdHJhY3QgcmVhZG9ubHkgdHlwZTogc3RyaW5nO1xyXG4gIHByb3RlY3RlZCBhYnN0cmFjdCByZWFkb25seSBjb25maWc6IElHcm91cGluZ1JlbmRlcmVyQ29uZmlnO1xyXG5cclxuICAvKipcclxuICAgKiBGZXRjaCBlbnRpdGllcyBmcm9tIHNlcnZpY2VcclxuICAgKi9cclxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgZ2V0RW50aXRpZXMoaWRzOiBzdHJpbmdbXSk6IFByb21pc2U8VFtdPjtcclxuXHJcbiAgLyoqXHJcbiAgICogR2V0IGRpc3BsYXkgbmFtZSBmb3IgZW50aXR5XHJcbiAgICovXHJcbiAgcHJvdGVjdGVkIGFic3RyYWN0IGdldERpc3BsYXlOYW1lKGVudGl0eTogVCk6IHN0cmluZztcclxuXHJcbiAgLyoqXHJcbiAgICogTWFpbiByZW5kZXIgbWV0aG9kIC0gaGFuZGxlcyBjb21tb24gbG9naWNcclxuICAgKi9cclxuICBhc3luYyByZW5kZXIoY29udGV4dDogSVJlbmRlckNvbnRleHQpOiBQcm9taXNlPHZvaWQ+IHtcclxuICAgIGNvbnN0IGFsbG93ZWRJZHMgPSBjb250ZXh0LmZpbHRlclt0aGlzLnR5cGVdIHx8IFtdO1xyXG4gICAgaWYgKGFsbG93ZWRJZHMubGVuZ3RoID09PSAwKSByZXR1cm47XHJcblxyXG4gICAgY29uc3QgZW50aXRpZXMgPSBhd2FpdCB0aGlzLmdldEVudGl0aWVzKGFsbG93ZWRJZHMpO1xyXG4gICAgY29uc3QgZGF0ZUNvdW50ID0gY29udGV4dC5maWx0ZXJbJ2RhdGUnXT8ubGVuZ3RoIHx8IDE7XHJcbiAgICBjb25zdCBjaGlsZElkcyA9IGNvbnRleHQuY2hpbGRUeXBlID8gY29udGV4dC5maWx0ZXJbY29udGV4dC5jaGlsZFR5cGVdIHx8IFtdIDogW107XHJcblxyXG4gICAgZm9yIChjb25zdCBlbnRpdHkgb2YgZW50aXRpZXMpIHtcclxuICAgICAgY29uc3QgZW50aXR5Q2hpbGRJZHMgPSBjb250ZXh0LnBhcmVudENoaWxkTWFwPy5bZW50aXR5LmlkXSB8fCBbXTtcclxuICAgICAgY29uc3QgY2hpbGRDb3VudCA9IGVudGl0eUNoaWxkSWRzLmZpbHRlcihpZCA9PiBjaGlsZElkcy5pbmNsdWRlcyhpZCkpLmxlbmd0aDtcclxuICAgICAgY29uc3QgY29sc3BhbiA9IGNoaWxkQ291bnQgKiBkYXRlQ291bnQ7XHJcblxyXG4gICAgICBjb25zdCBoZWFkZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KHRoaXMuY29uZmlnLmVsZW1lbnRUYWcpO1xyXG4gICAgICBoZWFkZXIuZGF0YXNldFt0aGlzLmNvbmZpZy5pZEF0dHJpYnV0ZV0gPSBlbnRpdHkuaWQ7XHJcbiAgICAgIGhlYWRlci5zdHlsZS5zZXRQcm9wZXJ0eSh0aGlzLmNvbmZpZy5jb2xzcGFuVmFyLCBTdHJpbmcoY29sc3BhbikpO1xyXG5cclxuICAgICAgLy8gQWxsb3cgc3ViY2xhc3MgdG8gY3VzdG9taXplIGhlYWRlciBjb250ZW50XHJcbiAgICAgIHRoaXMucmVuZGVySGVhZGVyKGVudGl0eSwgaGVhZGVyLCBjb250ZXh0KTtcclxuXHJcbiAgICAgIGNvbnRleHQuaGVhZGVyQ29udGFpbmVyLmFwcGVuZENoaWxkKGhlYWRlcik7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBPdmVycmlkZSB0aGlzIG1ldGhvZCBmb3IgY3VzdG9tIGhlYWRlciByZW5kZXJpbmdcclxuICAgKiBEZWZhdWx0OiBqdXN0IHNldHMgdGV4dENvbnRlbnQgdG8gZGlzcGxheSBuYW1lXHJcbiAgICovXHJcbiAgcHJvdGVjdGVkIHJlbmRlckhlYWRlcihlbnRpdHk6IFQsIGhlYWRlcjogSFRNTEVsZW1lbnQsIF9jb250ZXh0OiBJUmVuZGVyQ29udGV4dCk6IHZvaWQge1xyXG4gICAgaGVhZGVyLnRleHRDb250ZW50ID0gdGhpcy5nZXREaXNwbGF5TmFtZShlbnRpdHkpO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogSGVscGVyIHRvIHJlbmRlciBhIHNpbmdsZSBlbnRpdHkgaGVhZGVyLlxyXG4gICAqIENhbiBiZSB1c2VkIGJ5IHN1YmNsYXNzZXMgdGhhdCBvdmVycmlkZSByZW5kZXIoKSBidXQgd2FudCBjb25zaXN0ZW50IGhlYWRlciBjcmVhdGlvbi5cclxuICAgKi9cclxuICBwcm90ZWN0ZWQgY3JlYXRlSGVhZGVyKGVudGl0eTogVCwgY29udGV4dDogSVJlbmRlckNvbnRleHQpOiBIVE1MRWxlbWVudCB7XHJcbiAgICBjb25zdCBoZWFkZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KHRoaXMuY29uZmlnLmVsZW1lbnRUYWcpO1xyXG4gICAgaGVhZGVyLmRhdGFzZXRbdGhpcy5jb25maWcuaWRBdHRyaWJ1dGVdID0gZW50aXR5LmlkO1xyXG4gICAgdGhpcy5yZW5kZXJIZWFkZXIoZW50aXR5LCBoZWFkZXIsIGNvbnRleHQpO1xyXG4gICAgcmV0dXJuIGhlYWRlcjtcclxuICB9XHJcbn1cclxuIiwgImltcG9ydCB7IElSZW5kZXJDb250ZXh0IH0gZnJvbSAnLi4vLi4vY29yZS9JR3JvdXBpbmdSZW5kZXJlcic7XHJcbmltcG9ydCB7IEJhc2VHcm91cGluZ1JlbmRlcmVyLCBJR3JvdXBpbmdSZW5kZXJlckNvbmZpZyB9IGZyb20gJy4uLy4uL2NvcmUvQmFzZUdyb3VwaW5nUmVuZGVyZXInO1xyXG5pbXBvcnQgeyBSZXNvdXJjZVNlcnZpY2UgfSBmcm9tICcuLi8uLi9zdG9yYWdlL3Jlc291cmNlcy9SZXNvdXJjZVNlcnZpY2UnO1xyXG5pbXBvcnQgeyBJUmVzb3VyY2UgfSBmcm9tICcuLi8uLi90eXBlcy9DYWxlbmRhclR5cGVzJztcclxuXHJcbmV4cG9ydCBjbGFzcyBSZXNvdXJjZVJlbmRlcmVyIGV4dGVuZHMgQmFzZUdyb3VwaW5nUmVuZGVyZXI8SVJlc291cmNlPiB7XHJcbiAgcmVhZG9ubHkgdHlwZSA9ICdyZXNvdXJjZSc7XHJcblxyXG4gIHByb3RlY3RlZCByZWFkb25seSBjb25maWc6IElHcm91cGluZ1JlbmRlcmVyQ29uZmlnID0ge1xyXG4gICAgZWxlbWVudFRhZzogJ3N3cC1yZXNvdXJjZS1oZWFkZXInLFxyXG4gICAgaWRBdHRyaWJ1dGU6ICdyZXNvdXJjZUlkJyxcclxuICAgIGNvbHNwYW5WYXI6ICctLXJlc291cmNlLWNvbHMnXHJcbiAgfTtcclxuXHJcbiAgY29uc3RydWN0b3IocHJpdmF0ZSByZXNvdXJjZVNlcnZpY2U6IFJlc291cmNlU2VydmljZSkge1xyXG4gICAgc3VwZXIoKTtcclxuICB9XHJcblxyXG4gIHByb3RlY3RlZCBnZXRFbnRpdGllcyhpZHM6IHN0cmluZ1tdKTogUHJvbWlzZTxJUmVzb3VyY2VbXT4ge1xyXG4gICAgcmV0dXJuIHRoaXMucmVzb3VyY2VTZXJ2aWNlLmdldEJ5SWRzKGlkcyk7XHJcbiAgfVxyXG5cclxuICBwcm90ZWN0ZWQgZ2V0RGlzcGxheU5hbWUoZW50aXR5OiBJUmVzb3VyY2UpOiBzdHJpbmcge1xyXG4gICAgcmV0dXJuIGVudGl0eS5kaXNwbGF5TmFtZTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIE92ZXJyaWRlIHJlbmRlciB0byBoYW5kbGU6XHJcbiAgICogMS4gU3BlY2lhbCBvcmRlcmluZyB3aGVuIHBhcmVudENoaWxkTWFwIGV4aXN0cyAocmVzb3VyY2VzIGdyb3VwZWQgYnkgcGFyZW50KVxyXG4gICAqIDIuIERpZmZlcmVudCBjb2xzcGFuIGNhbGN1bGF0aW9uIChqdXN0IGRhdGVDb3VudCwgbm90IGNoaWxkQ291bnQgKiBkYXRlQ291bnQpXHJcbiAgICovXHJcbiAgYXN5bmMgcmVuZGVyKGNvbnRleHQ6IElSZW5kZXJDb250ZXh0KTogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICBjb25zdCByZXNvdXJjZUlkcyA9IGNvbnRleHQuZmlsdGVyWydyZXNvdXJjZSddIHx8IFtdO1xyXG4gICAgY29uc3QgZGF0ZUNvdW50ID0gY29udGV4dC5maWx0ZXJbJ2RhdGUnXT8ubGVuZ3RoIHx8IDE7XHJcblxyXG4gICAgLy8gRGV0ZXJtaW5lIHJlbmRlciBvcmRlciBiYXNlZCBvbiBwYXJlbnRDaGlsZE1hcFxyXG4gICAgLy8gSWYgcGFyZW50Q2hpbGRNYXAgZXhpc3RzLCByZW5kZXIgcmVzb3VyY2VzIGdyb3VwZWQgYnkgcGFyZW50IChlLmcuLCB0ZWFtKVxyXG4gICAgLy8gT3RoZXJ3aXNlLCByZW5kZXIgaW4gZmlsdGVyIG9yZGVyXHJcbiAgICBsZXQgb3JkZXJlZFJlc291cmNlSWRzOiBzdHJpbmdbXTtcclxuXHJcbiAgICBpZiAoY29udGV4dC5wYXJlbnRDaGlsZE1hcCkge1xyXG4gICAgICAvLyBSZW5kZXIgcmVzb3VyY2VzIGluIHBhcmVudC1jaGlsZCBvcmRlclxyXG4gICAgICBvcmRlcmVkUmVzb3VyY2VJZHMgPSBbXTtcclxuICAgICAgZm9yIChjb25zdCBjaGlsZElkcyBvZiBPYmplY3QudmFsdWVzKGNvbnRleHQucGFyZW50Q2hpbGRNYXApKSB7XHJcbiAgICAgICAgZm9yIChjb25zdCBjaGlsZElkIG9mIGNoaWxkSWRzKSB7XHJcbiAgICAgICAgICBpZiAocmVzb3VyY2VJZHMuaW5jbHVkZXMoY2hpbGRJZCkpIHtcclxuICAgICAgICAgICAgb3JkZXJlZFJlc291cmNlSWRzLnB1c2goY2hpbGRJZCk7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICBvcmRlcmVkUmVzb3VyY2VJZHMgPSByZXNvdXJjZUlkcztcclxuICAgIH1cclxuXHJcbiAgICBjb25zdCByZXNvdXJjZXMgPSBhd2FpdCB0aGlzLmdldEVudGl0aWVzKG9yZGVyZWRSZXNvdXJjZUlkcyk7XHJcblxyXG4gICAgLy8gQ3JlYXRlIGEgbWFwIGZvciBxdWljayBsb29rdXAgdG8gcHJlc2VydmUgb3JkZXJcclxuICAgIGNvbnN0IHJlc291cmNlTWFwID0gbmV3IE1hcChyZXNvdXJjZXMubWFwKHIgPT4gW3IuaWQsIHJdKSk7XHJcblxyXG4gICAgZm9yIChjb25zdCByZXNvdXJjZUlkIG9mIG9yZGVyZWRSZXNvdXJjZUlkcykge1xyXG4gICAgICBjb25zdCByZXNvdXJjZSA9IHJlc291cmNlTWFwLmdldChyZXNvdXJjZUlkKTtcclxuICAgICAgaWYgKCFyZXNvdXJjZSkgY29udGludWU7XHJcblxyXG4gICAgICBjb25zdCBoZWFkZXIgPSB0aGlzLmNyZWF0ZUhlYWRlcihyZXNvdXJjZSwgY29udGV4dCk7XHJcbiAgICAgIGhlYWRlci5zdHlsZS5ncmlkQ29sdW1uID0gYHNwYW4gJHtkYXRlQ291bnR9YDtcclxuICAgICAgY29udGV4dC5oZWFkZXJDb250YWluZXIuYXBwZW5kQ2hpbGQoaGVhZGVyKTtcclxuICAgIH1cclxuICB9XHJcbn1cclxuIiwgImltcG9ydCB7IEJhc2VHcm91cGluZ1JlbmRlcmVyLCBJR3JvdXBpbmdSZW5kZXJlckNvbmZpZyB9IGZyb20gJy4uLy4uL2NvcmUvQmFzZUdyb3VwaW5nUmVuZGVyZXInO1xyXG5pbXBvcnQgeyBUZWFtU2VydmljZSB9IGZyb20gJy4uLy4uL3N0b3JhZ2UvdGVhbXMvVGVhbVNlcnZpY2UnO1xyXG5pbXBvcnQgeyBJVGVhbSB9IGZyb20gJy4uLy4uL3R5cGVzL0NhbGVuZGFyVHlwZXMnO1xyXG5cclxuZXhwb3J0IGNsYXNzIFRlYW1SZW5kZXJlciBleHRlbmRzIEJhc2VHcm91cGluZ1JlbmRlcmVyPElUZWFtPiB7XHJcbiAgcmVhZG9ubHkgdHlwZSA9ICd0ZWFtJztcclxuXHJcbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGNvbmZpZzogSUdyb3VwaW5nUmVuZGVyZXJDb25maWcgPSB7XHJcbiAgICBlbGVtZW50VGFnOiAnc3dwLXRlYW0taGVhZGVyJyxcclxuICAgIGlkQXR0cmlidXRlOiAndGVhbUlkJyxcclxuICAgIGNvbHNwYW5WYXI6ICctLXRlYW0tY29scydcclxuICB9O1xyXG5cclxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHRlYW1TZXJ2aWNlOiBUZWFtU2VydmljZSkge1xyXG4gICAgc3VwZXIoKTtcclxuICB9XHJcblxyXG4gIHByb3RlY3RlZCBnZXRFbnRpdGllcyhpZHM6IHN0cmluZ1tdKTogUHJvbWlzZTxJVGVhbVtdPiB7XHJcbiAgICByZXR1cm4gdGhpcy50ZWFtU2VydmljZS5nZXRCeUlkcyhpZHMpO1xyXG4gIH1cclxuXHJcbiAgcHJvdGVjdGVkIGdldERpc3BsYXlOYW1lKGVudGl0eTogSVRlYW0pOiBzdHJpbmcge1xyXG4gICAgcmV0dXJuIGVudGl0eS5uYW1lO1xyXG4gIH1cclxufVxyXG4iLCAiZXhwb3J0IGNsYXNzIFRpbWVBeGlzUmVuZGVyZXIge1xyXG4gIHJlbmRlcihjb250YWluZXI6IEhUTUxFbGVtZW50LCBzdGFydEhvdXIgPSA2LCBlbmRIb3VyID0gMjApOiB2b2lkIHtcclxuICAgIGNvbnRhaW5lci5pbm5lckhUTUwgPSAnJztcclxuICAgIGZvciAobGV0IGhvdXIgPSBzdGFydEhvdXI7IGhvdXIgPD0gZW5kSG91cjsgaG91cisrKSB7XHJcbiAgICAgIGNvbnN0IG1hcmtlciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3N3cC1ob3VyLW1hcmtlcicpO1xyXG4gICAgICBtYXJrZXIudGV4dENvbnRlbnQgPSBgJHtob3VyLnRvU3RyaW5nKCkucGFkU3RhcnQoMiwgJzAnKX06MDBgO1xyXG4gICAgICBjb250YWluZXIuYXBwZW5kQ2hpbGQobWFya2VyKTtcclxuICAgIH1cclxuICB9XHJcbn1cclxuIl0sCiAgIm1hcHBpbmdzIjogIjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBO0FBQUE7QUFBQSxLQUFDLFNBQVMsR0FBRSxHQUFFO0FBQUMsa0JBQVUsT0FBTyxXQUFTLGVBQWEsT0FBTyxTQUFPLE9BQU8sVUFBUSxFQUFFLElBQUUsY0FBWSxPQUFPLFVBQVEsT0FBTyxNQUFJLE9BQU8sQ0FBQyxLQUFHLElBQUUsZUFBYSxPQUFPLGFBQVcsYUFBVyxLQUFHLE1BQU0sUUFBTSxFQUFFO0FBQUEsSUFBQyxFQUFFLFNBQU0sV0FBVTtBQUFDO0FBQWEsVUFBSSxJQUFFLEtBQUksSUFBRSxLQUFJLElBQUUsTUFBSyxJQUFFLGVBQWMsSUFBRSxVQUFTLElBQUUsVUFBUyxJQUFFLFFBQU8sSUFBRSxPQUFNLElBQUUsUUFBTyxJQUFFLFNBQVEsSUFBRSxXQUFVLElBQUUsUUFBTyxJQUFFLFFBQU8sSUFBRSxnQkFBZSxJQUFFLDhGQUE2RixJQUFFLHVGQUFzRixJQUFFLEVBQUMsTUFBSyxNQUFLLFVBQVMsMkRBQTJELE1BQU0sR0FBRyxHQUFFLFFBQU8sd0ZBQXdGLE1BQU0sR0FBRyxHQUFFLFNBQVEsU0FBU0EsSUFBRTtBQUFDLFlBQUlDLEtBQUUsQ0FBQyxNQUFLLE1BQUssTUFBSyxJQUFJLEdBQUVDLEtBQUVGLEtBQUU7QUFBSSxlQUFNLE1BQUlBLE1BQUdDLElBQUdDLEtBQUUsTUFBSSxFQUFFLEtBQUdELEdBQUVDLEVBQUMsS0FBR0QsR0FBRSxDQUFDLEtBQUc7QUFBQSxNQUFHLEVBQUMsR0FBRSxJQUFFLGdDQUFTRCxJQUFF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QUFPLE9BQU87QUFBQSxFQUNoQjtBQUNGO0FBcEI4RDtBQUF2RCxJQUFNLGVBQU47OztBQ0pBLElBQU0sb0JBQU4sTUFBTSxrQkFBaUI7QUFBQSxFQUM1QixPQUFPLFdBQXdCLFlBQVksR0FBRyxVQUFVLElBQVU7QUFDaEUsY0FBVSxZQUFZO0FBQ3RCLGFBQVMsT0FBTyxXQUFXLFFBQVEsU0FBUyxRQUFRO0FBQ2xELFlBQU0sU0FBUyxTQUFTLGNBQWMsaUJBQWlCO0FBQ3ZELGFBQU8sY0FBYyxHQUFHLEtBQUssU0FBUyxFQUFFLFNBQVMsR0FBRyxHQUFHLENBQUM7QUFDeEQsZ0JBQVUsWUFBWSxNQUFNO0FBQUEsSUFDOUI7QUFBQSxFQUNGO0FBQ0Y7QUFUOEI7QUFBdkIsSUFBTSxtQkFBTjsiLAogICJuYW1lcyI6IFsidCIsICJlIiwgIm4iLCAiciIsICJpIiwgInMiLCAidSIsICJhIiwgIk0iLCAibSIsICJmIiwgImwiLCAiJCIsICJ5IiwgInYiLCAiZyIsICJEIiwgIm8iLCAiZCIsICJjIiwgImgiLCAidCIsICJpIiwgImUiLCAicyIsICJmIiwgIm4iLCAidSIsICJyIiwgIm8iLCAidCIsICJuIiwgImkiLCAibyIsICJyIiwgImUiLCAidSIsICJmIiwgInMiLCAiYSIsICJ0IiwgImkiLCAiZCIsICJuIiwgImUiLCAicyIsICJkYXlqcyIsICJ1dGMiLCAidGltZXpvbmUiLCAiaXNvV2VlayJdCn0K diff --git a/wwwroot/js/components/NavigationButtons.d.ts b/wwwroot/js/components/NavigationButtons.d.ts new file mode 100644 index 0000000..75f5002 --- /dev/null +++ b/wwwroot/js/components/NavigationButtons.d.ts @@ -0,0 +1,63 @@ +import { IEventBus } from '../types/CalendarTypes'; +import { DateService } from '../utils/DateService'; +import { Configuration } from '../configurations/CalendarConfig'; +/** + * NavigationButtons - Manages navigation button UI and navigation logic + * + * RESPONSIBILITY: + * =============== + * This manager owns all logic related to the UI element + * and performs the actual navigation calculations. + * + * RESPONSIBILITIES: + * - Handles button clicks on swp-nav-button elements + * - Validates navigation actions (prev, next, today) + * - Calculates next/previous dates based on current view + * - Emits NAVIGATION_COMPLETED events with new date + * - Manages button UI listeners + * + * EVENT FLOW: + * =========== + * User clicks button → calculateNewDate() → emit NAVIGATION_COMPLETED → GridManager re-renders + */ +export declare class NavigationButtons { + private eventBus; + private buttonListeners; + private dateService; + private config; + private currentDate; + private currentView; + constructor(eventBus: IEventBus, dateService: DateService, config: Configuration); + /** + * Subscribe to events + */ + private subscribeToEvents; + /** + * Setup click listeners on all navigation buttons + */ + private setupButtonListeners; + /** + * Handle navigation action + */ + private handleNavigation; + /** + * Navigate in specified direction + */ + private navigate; + /** + * Navigate to next period + */ + private navigateNext; + /** + * Navigate to previous period + */ + private navigatePrevious; + /** + * Navigate to today + */ + private navigateToday; + /** + * Validate if string is a valid navigation action + */ + private isValidAction; +} diff --git a/wwwroot/js/components/NavigationButtons.js b/wwwroot/js/components/NavigationButtons.js new file mode 100644 index 0000000..1f53d45 --- /dev/null +++ b/wwwroot/js/components/NavigationButtons.js @@ -0,0 +1,131 @@ +import { CoreEvents } from '../constants/CoreEvents'; +/** + * NavigationButtons - Manages navigation button UI and navigation logic + * + * RESPONSIBILITY: + * =============== + * This manager owns all logic related to the UI element + * and performs the actual navigation calculations. + * + * RESPONSIBILITIES: + * - Handles button clicks on swp-nav-button elements + * - Validates navigation actions (prev, next, today) + * - Calculates next/previous dates based on current view + * - Emits NAVIGATION_COMPLETED events with new date + * - Manages button UI listeners + * + * EVENT FLOW: + * =========== + * User clicks button → calculateNewDate() → emit NAVIGATION_COMPLETED → GridManager re-renders + */ +export class NavigationButtons { + constructor(eventBus, dateService, config) { + this.buttonListeners = new Map(); + this.currentDate = new Date(); + this.currentView = 'week'; + this.eventBus = eventBus; + this.dateService = dateService; + this.config = config; + this.setupButtonListeners(); + this.subscribeToEvents(); + } + /** + * Subscribe to events + */ + subscribeToEvents() { + // Listen for view changes + this.eventBus.on(CoreEvents.VIEW_CHANGED, (e) => { + const detail = e.detail; + this.currentView = detail.currentView; + }); + } + /** + * Setup click listeners on all navigation buttons + */ + setupButtonListeners() { + const buttons = document.querySelectorAll('swp-nav-button[data-action]'); + buttons.forEach(button => { + const clickHandler = (event) => { + event.preventDefault(); + const action = button.getAttribute('data-action'); + if (action && this.isValidAction(action)) { + this.handleNavigation(action); + } + }; + button.addEventListener('click', clickHandler); + this.buttonListeners.set(button, clickHandler); + }); + } + /** + * Handle navigation action + */ + handleNavigation(action) { + switch (action) { + case 'prev': + this.navigatePrevious(); + break; + case 'next': + this.navigateNext(); + break; + case 'today': + this.navigateToday(); + break; + } + } + /** + * Navigate in specified direction + */ + navigate(direction) { + const offset = direction === 'next' ? 1 : -1; + let newDate; + switch (this.currentView) { + case 'week': + newDate = this.dateService.addWeeks(this.currentDate, offset); + break; + case 'month': + newDate = this.dateService.addMonths(this.currentDate, offset); + break; + case 'day': + newDate = this.dateService.addDays(this.currentDate, offset); + break; + default: + newDate = this.dateService.addWeeks(this.currentDate, offset); + } + this.currentDate = newDate; + const payload = { + direction: direction, + newDate: newDate + }; + this.eventBus.emit(CoreEvents.NAV_BUTTON_CLICKED, payload); + } + /** + * Navigate to next period + */ + navigateNext() { + this.navigate('next'); + } + /** + * Navigate to previous period + */ + navigatePrevious() { + this.navigate('previous'); + } + /** + * Navigate to today + */ + navigateToday() { + this.currentDate = new Date(); + const payload = { + direction: 'today', + newDate: this.currentDate + }; + this.eventBus.emit(CoreEvents.NAV_BUTTON_CLICKED, payload); + } + /** + * Validate if string is a valid navigation action + */ + isValidAction(action) { + return ['prev', 'next', 'today'].includes(action); + } +} +//# sourceMappingURL=NavigationButtons.js.map \ No newline at end of file diff --git a/wwwroot/js/components/NavigationButtons.js.map b/wwwroot/js/components/NavigationButtons.js.map new file mode 100644 index 0000000..85a7e6d --- /dev/null +++ b/wwwroot/js/components/NavigationButtons.js.map @@ -0,0 +1 @@ +{"version":3,"file":"NavigationButtons.js","sourceRoot":"","sources":["../../../src/components/NavigationButtons.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/components/ViewSelector.d.ts b/wwwroot/js/components/ViewSelector.d.ts new file mode 100644 index 0000000..04b1853 --- /dev/null +++ b/wwwroot/js/components/ViewSelector.d.ts @@ -0,0 +1,70 @@ +import { IEventBus } from '../types/CalendarTypes'; +import { Configuration } from '../configurations/CalendarConfig'; +/** + * ViewSelectorManager - Manages view selector UI and state + * + * RESPONSIBILITY: + * =============== + * This manager owns all logic related to the UI element. + * It follows the principle that each functional UI element has its own manager. + * + * RESPONSIBILITIES: + * - Handles button clicks on swp-view-button elements + * - Manages current view state (day/week/month) + * - Validates view values + * - Emits VIEW_CHANGED and VIEW_RENDERED events + * - Updates button UI states (data-active attributes) + * + * EVENT FLOW: + * =========== + * User clicks button → changeView() → validate → update state → emit event → update UI + * + * IMPLEMENTATION STATUS: + * ====================== + * - Week view: FULLY IMPLEMENTED + * - Day view: NOT IMPLEMENTED (button exists but no rendering) + * - Month view: NOT IMPLEMENTED (button exists but no rendering) + * + * SUBSCRIBERS: + * ============ + * - GridRenderer: Uses view parameter (currently only supports 'week') + * - Future: DayRenderer, MonthRenderer when implemented + */ +export declare class ViewSelector { + private eventBus; + private config; + private buttonListeners; + constructor(eventBus: IEventBus, config: Configuration); + /** + * Setup click listeners on all view selector buttons + */ + private setupButtonListeners; + /** + * Setup event bus listeners + */ + private setupEventListeners; + /** + * Change the active view + */ + private changeView; + /** + * Update button states (data-active attributes) + */ + private updateButtonStates; + /** + * Initialize view on INITIALIZED event + */ + private initializeView; + /** + * Emit VIEW_RENDERED event + */ + private emitViewRendered; + /** + * Refresh current view on DATE_CHANGED event + */ + private refreshCurrentView; + /** + * Validate if string is a valid CalendarView type + */ + private isValidView; +} diff --git a/wwwroot/js/components/ViewSelector.js b/wwwroot/js/components/ViewSelector.js new file mode 100644 index 0000000..a54939c --- /dev/null +++ b/wwwroot/js/components/ViewSelector.js @@ -0,0 +1,130 @@ +import { CoreEvents } from '../constants/CoreEvents'; +/** + * ViewSelectorManager - Manages view selector UI and state + * + * RESPONSIBILITY: + * =============== + * This manager owns all logic related to the UI element. + * It follows the principle that each functional UI element has its own manager. + * + * RESPONSIBILITIES: + * - Handles button clicks on swp-view-button elements + * - Manages current view state (day/week/month) + * - Validates view values + * - Emits VIEW_CHANGED and VIEW_RENDERED events + * - Updates button UI states (data-active attributes) + * + * EVENT FLOW: + * =========== + * User clicks button → changeView() → validate → update state → emit event → update UI + * + * IMPLEMENTATION STATUS: + * ====================== + * - Week view: FULLY IMPLEMENTED + * - Day view: NOT IMPLEMENTED (button exists but no rendering) + * - Month view: NOT IMPLEMENTED (button exists but no rendering) + * + * SUBSCRIBERS: + * ============ + * - GridRenderer: Uses view parameter (currently only supports 'week') + * - Future: DayRenderer, MonthRenderer when implemented + */ +export class ViewSelector { + constructor(eventBus, config) { + this.buttonListeners = new Map(); + this.eventBus = eventBus; + this.config = config; + this.setupButtonListeners(); + this.setupEventListeners(); + } + /** + * Setup click listeners on all view selector buttons + */ + setupButtonListeners() { + const buttons = document.querySelectorAll('swp-view-button[data-view]'); + buttons.forEach(button => { + const clickHandler = (event) => { + event.preventDefault(); + const view = button.getAttribute('data-view'); + if (view && this.isValidView(view)) { + this.changeView(view); + } + }; + button.addEventListener('click', clickHandler); + this.buttonListeners.set(button, clickHandler); + }); + // Initialize button states + this.updateButtonStates(); + } + /** + * Setup event bus listeners + */ + setupEventListeners() { + this.eventBus.on(CoreEvents.INITIALIZED, () => { + this.initializeView(); + }); + this.eventBus.on(CoreEvents.DATE_CHANGED, () => { + this.refreshCurrentView(); + }); + } + /** + * Change the active view + */ + changeView(newView) { + if (newView === this.config.currentView) { + return; // No change + } + const previousView = this.config.currentView; + this.config.currentView = newView; + // Update button UI states + this.updateButtonStates(); + // Emit event for subscribers + this.eventBus.emit(CoreEvents.VIEW_CHANGED, { + previousView, + currentView: newView + }); + } + /** + * Update button states (data-active attributes) + */ + updateButtonStates() { + const buttons = document.querySelectorAll('swp-view-button[data-view]'); + buttons.forEach(button => { + const buttonView = button.getAttribute('data-view'); + if (buttonView === this.config.currentView) { + button.setAttribute('data-active', 'true'); + } + else { + button.removeAttribute('data-active'); + } + }); + } + /** + * Initialize view on INITIALIZED event + */ + initializeView() { + this.updateButtonStates(); + this.emitViewRendered(); + } + /** + * Emit VIEW_RENDERED event + */ + emitViewRendered() { + this.eventBus.emit(CoreEvents.VIEW_RENDERED, { + view: this.config.currentView + }); + } + /** + * Refresh current view on DATE_CHANGED event + */ + refreshCurrentView() { + this.emitViewRendered(); + } + /** + * Validate if string is a valid CalendarView type + */ + isValidView(view) { + return ['day', 'week', 'month'].includes(view); + } +} +//# sourceMappingURL=ViewSelector.js.map \ No newline at end of file diff --git a/wwwroot/js/components/ViewSelector.js.map b/wwwroot/js/components/ViewSelector.js.map new file mode 100644 index 0000000..291c1a2 --- /dev/null +++ b/wwwroot/js/components/ViewSelector.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ViewSelector.js","sourceRoot":"","sources":["../../../src/components/ViewSelector.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/components/WorkweekPresets.d.ts b/wwwroot/js/components/WorkweekPresets.d.ts new file mode 100644 index 0000000..7b96030 --- /dev/null +++ b/wwwroot/js/components/WorkweekPresets.d.ts @@ -0,0 +1,47 @@ +import { IEventBus } from '../types/CalendarTypes'; +import { Configuration } from '../configurations/CalendarConfig'; +/** + * WorkweekPresetsManager - Manages workweek preset UI and state + * + * RESPONSIBILITY: + * =============== + * This manager owns all logic related to the UI element. + * It follows the principle that each functional UI element has its own manager. + * + * RESPONSIBILITIES: + * - Owns WORK_WEEK_PRESETS data + * - Handles button clicks on swp-preset-button elements + * - Manages current workweek preset state + * - Validates preset IDs + * - Emits WORKWEEK_CHANGED events + * - Updates button UI states (data-active attributes) + * + * EVENT FLOW: + * =========== + * User clicks button → changePreset() → validate → update state → emit event → update UI + * + * SUBSCRIBERS: + * ============ + * - ConfigManager: Updates CSS variables (--grid-columns) + * - GridManager: Re-renders grid with new column count + * - CalendarManager: Relays to header update (via workweek:header-update) + * - HeaderManager: Updates date headers + */ +export declare class WorkweekPresets { + private eventBus; + private config; + private buttonListeners; + constructor(eventBus: IEventBus, config: Configuration); + /** + * Setup click listeners on all workweek preset buttons + */ + private setupButtonListeners; + /** + * Change the active workweek preset + */ + private changePreset; + /** + * Update button states (data-active attributes) + */ + private updateButtonStates; +} diff --git a/wwwroot/js/components/WorkweekPresets.js b/wwwroot/js/components/WorkweekPresets.js new file mode 100644 index 0000000..e7e953f --- /dev/null +++ b/wwwroot/js/components/WorkweekPresets.js @@ -0,0 +1,95 @@ +import { CoreEvents } from '../constants/CoreEvents'; +import { WORK_WEEK_PRESETS } from '../configurations/CalendarConfig'; +/** + * WorkweekPresetsManager - Manages workweek preset UI and state + * + * RESPONSIBILITY: + * =============== + * This manager owns all logic related to the UI element. + * It follows the principle that each functional UI element has its own manager. + * + * RESPONSIBILITIES: + * - Owns WORK_WEEK_PRESETS data + * - Handles button clicks on swp-preset-button elements + * - Manages current workweek preset state + * - Validates preset IDs + * - Emits WORKWEEK_CHANGED events + * - Updates button UI states (data-active attributes) + * + * EVENT FLOW: + * =========== + * User clicks button → changePreset() → validate → update state → emit event → update UI + * + * SUBSCRIBERS: + * ============ + * - ConfigManager: Updates CSS variables (--grid-columns) + * - GridManager: Re-renders grid with new column count + * - CalendarManager: Relays to header update (via workweek:header-update) + * - HeaderManager: Updates date headers + */ +export class WorkweekPresets { + constructor(eventBus, config) { + this.buttonListeners = new Map(); + this.eventBus = eventBus; + this.config = config; + this.setupButtonListeners(); + } + /** + * Setup click listeners on all workweek preset buttons + */ + setupButtonListeners() { + const buttons = document.querySelectorAll('swp-preset-button[data-workweek]'); + buttons.forEach(button => { + const clickHandler = (event) => { + event.preventDefault(); + const presetId = button.getAttribute('data-workweek'); + if (presetId) { + this.changePreset(presetId); + } + }; + button.addEventListener('click', clickHandler); + this.buttonListeners.set(button, clickHandler); + }); + // Initialize button states + this.updateButtonStates(); + } + /** + * Change the active workweek preset + */ + changePreset(presetId) { + if (!WORK_WEEK_PRESETS[presetId]) { + console.warn(`Invalid preset ID "${presetId}"`); + return; + } + if (presetId === this.config.currentWorkWeek) { + return; // No change + } + const previousPresetId = this.config.currentWorkWeek; + this.config.currentWorkWeek = presetId; + const settings = WORK_WEEK_PRESETS[presetId]; + // Update button UI states + this.updateButtonStates(); + // Emit event for subscribers + this.eventBus.emit(CoreEvents.WORKWEEK_CHANGED, { + workWeekId: presetId, + previousWorkWeekId: previousPresetId, + settings: settings + }); + } + /** + * Update button states (data-active attributes) + */ + updateButtonStates() { + const buttons = document.querySelectorAll('swp-preset-button[data-workweek]'); + buttons.forEach(button => { + const buttonPresetId = button.getAttribute('data-workweek'); + if (buttonPresetId === this.config.currentWorkWeek) { + button.setAttribute('data-active', 'true'); + } + else { + button.removeAttribute('data-active'); + } + }); + } +} +//# sourceMappingURL=WorkweekPresets.js.map \ No newline at end of file diff --git a/wwwroot/js/components/WorkweekPresets.js.map b/wwwroot/js/components/WorkweekPresets.js.map new file mode 100644 index 0000000..10f34a5 --- /dev/null +++ b/wwwroot/js/components/WorkweekPresets.js.map @@ -0,0 +1 @@ +{"version":3,"file":"WorkweekPresets.js","sourceRoot":"","sources":["../../../src/components/WorkweekPresets.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAErD,OAAO,EAAE,iBAAiB,EAAiB,MAAM,kCAAkC,CAAC;AAEpF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,OAAO,eAAe;IAK1B,YAAY,QAAmB,EAAE,MAAqB;QAF9C,oBAAe,GAAgC,IAAI,GAAG,EAAE,CAAC;QAG/D,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QAErB,IAAI,CAAC,oBAAoB,EAAE,CAAC;IAC9B,CAAC;IAED;;OAEG;IACK,oBAAoB;QAC1B,MAAM,OAAO,GAAG,QAAQ,CAAC,gBAAgB,CAAC,kCAAkC,CAAC,CAAC;QAE9E,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;YACvB,MAAM,YAAY,GAAG,CAAC,KAAY,EAAE,EAAE;gBACpC,KAAK,CAAC,cAAc,EAAE,CAAC;gBACvB,MAAM,QAAQ,GAAG,MAAM,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC;gBACtD,IAAI,QAAQ,EAAE,CAAC;oBACb,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;gBAC9B,CAAC;YACH,CAAC,CAAC;YAEF,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;YAC/C,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QACjD,CAAC,CAAC,CAAC;QAEH,2BAA2B;QAC3B,IAAI,CAAC,kBAAkB,EAAE,CAAC;IAC5B,CAAC;IAED;;OAEG;IACK,YAAY,CAAC,QAAgB;QACnC,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,EAAE,CAAC;YACjC,OAAO,CAAC,IAAI,CAAC,sBAAsB,QAAQ,GAAG,CAAC,CAAC;YAChD,OAAO;QACT,CAAC;QAED,IAAI,QAAQ,KAAK,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC;YAC7C,OAAO,CAAC,YAAY;QACtB,CAAC;QAED,MAAM,gBAAgB,GAAG,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC;QACrD,IAAI,CAAC,MAAM,CAAC,eAAe,GAAG,QAAQ,CAAC;QAEvC,MAAM,QAAQ,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QAE7C,0BAA0B;QAC1B,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAE1B,6BAA6B;QAC7B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,gBAAgB,EAAE;YAC9C,UAAU,EAAE,QAAQ;YACpB,kBAAkB,EAAE,gBAAgB;YACpC,QAAQ,EAAE,QAAQ;SACnB,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACK,kBAAkB;QACxB,MAAM,OAAO,GAAG,QAAQ,CAAC,gBAAgB,CAAC,kCAAkC,CAAC,CAAC;QAE9E,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;YACvB,MAAM,cAAc,GAAG,MAAM,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC;YAE5D,IAAI,cAAc,KAAK,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC;gBACnD,MAAM,CAAC,YAAY,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;YAC7C,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC;YACxC,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;CAEF"} \ No newline at end of file diff --git a/wwwroot/js/configuration/CalendarConfig.d.ts b/wwwroot/js/configuration/CalendarConfig.d.ts new file mode 100644 index 0000000..92b98dc --- /dev/null +++ b/wwwroot/js/configuration/CalendarConfig.d.ts @@ -0,0 +1,44 @@ +import { ICalendarConfig } from './ICalendarConfig'; +import { IGridSettings } from './GridSettings'; +import { IDateViewSettings } from './DateViewSettings'; +import { ITimeFormatConfig } from './TimeFormatConfig'; +import { IWorkWeekSettings } from './WorkWeekSettings'; +/** + * All-day event layout constants + */ +export declare const ALL_DAY_CONSTANTS: { + readonly EVENT_HEIGHT: 22; + readonly EVENT_GAP: 2; + readonly CONTAINER_PADDING: 4; + readonly MAX_COLLAPSED_ROWS: 4; + readonly SINGLE_ROW_HEIGHT: number; +}; +/** + * Work week presets + */ +export declare const WORK_WEEK_PRESETS: { + [key: string]: IWorkWeekSettings; +}; +/** + * Configuration - DTO container for all configuration + * Pure data object loaded from JSON via ConfigManager + */ +export declare class Configuration { + private static _instance; + config: ICalendarConfig; + gridSettings: IGridSettings; + dateViewSettings: IDateViewSettings; + timeFormatConfig: ITimeFormatConfig; + currentWorkWeek: string; + selectedDate: Date; + constructor(config: ICalendarConfig, gridSettings: IGridSettings, dateViewSettings: IDateViewSettings, timeFormatConfig: ITimeFormatConfig, currentWorkWeek: string, selectedDate?: Date); + /** + * Get the current Configuration instance + * Used by web components that can't use dependency injection + */ + static getInstance(): Configuration; + getWorkWeekSettings(): IWorkWeekSettings; + setWorkWeek(workWeekId: string): void; + setSelectedDate(date: Date): void; +} +export { Configuration as CalendarConfig }; diff --git a/wwwroot/js/configuration/CalendarConfig.js b/wwwroot/js/configuration/CalendarConfig.js new file mode 100644 index 0000000..8c769a3 --- /dev/null +++ b/wwwroot/js/configuration/CalendarConfig.js @@ -0,0 +1,90 @@ +/** + * All-day event layout constants + */ +export const ALL_DAY_CONSTANTS = { + EVENT_HEIGHT: 22, + EVENT_GAP: 2, + CONTAINER_PADDING: 4, + MAX_COLLAPSED_ROWS: 4, + get SINGLE_ROW_HEIGHT() { + return this.EVENT_HEIGHT + this.EVENT_GAP; // 28px + } +}; +/** + * Work week presets + */ +export const WORK_WEEK_PRESETS = { + 'standard': { + id: 'standard', + workDays: [1, 2, 3, 4, 5], + totalDays: 5, + firstWorkDay: 1 + }, + 'compressed': { + id: 'compressed', + workDays: [1, 2, 3, 4], + totalDays: 4, + firstWorkDay: 1 + }, + 'midweek': { + id: 'midweek', + workDays: [3, 4, 5], + totalDays: 3, + firstWorkDay: 3 + }, + 'weekend': { + id: 'weekend', + workDays: [6, 7], + totalDays: 2, + firstWorkDay: 6 + }, + 'fullweek': { + id: 'fullweek', + workDays: [1, 2, 3, 4, 5, 6, 7], + totalDays: 7, + firstWorkDay: 1 + } +}; +/** + * Configuration - DTO container for all configuration + * Pure data object loaded from JSON via ConfigManager + */ +export class Configuration { + constructor(config, gridSettings, dateViewSettings, timeFormatConfig, currentWorkWeek, selectedDate = new Date()) { + this.config = config; + this.gridSettings = gridSettings; + this.dateViewSettings = dateViewSettings; + this.timeFormatConfig = timeFormatConfig; + this.currentWorkWeek = currentWorkWeek; + this.selectedDate = selectedDate; + // Store as singleton instance for web components + Configuration._instance = this; + } + /** + * Get the current Configuration instance + * Used by web components that can't use dependency injection + */ + static getInstance() { + if (!Configuration._instance) { + throw new Error('Configuration has not been initialized. Call ConfigManager.load() first.'); + } + return Configuration._instance; + } + // Helper methods + getWorkWeekSettings() { + return WORK_WEEK_PRESETS[this.currentWorkWeek] || WORK_WEEK_PRESETS['standard']; + } + setWorkWeek(workWeekId) { + if (WORK_WEEK_PRESETS[workWeekId]) { + this.currentWorkWeek = workWeekId; + this.dateViewSettings.weekDays = WORK_WEEK_PRESETS[workWeekId].totalDays; + } + } + setSelectedDate(date) { + this.selectedDate = date; + } +} +Configuration._instance = null; +// Backward compatibility alias +export { Configuration as CalendarConfig }; +//# sourceMappingURL=CalendarConfig.js.map \ No newline at end of file diff --git a/wwwroot/js/configuration/CalendarConfig.js.map b/wwwroot/js/configuration/CalendarConfig.js.map new file mode 100644 index 0000000..c9c14a2 --- /dev/null +++ b/wwwroot/js/configuration/CalendarConfig.js.map @@ -0,0 +1 @@ +{"version":3,"file":"CalendarConfig.js","sourceRoot":"","sources":["../../../src/configuration/CalendarConfig.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/configuration/ConfigManager.d.ts b/wwwroot/js/configuration/ConfigManager.d.ts new file mode 100644 index 0000000..efc52f3 --- /dev/null +++ b/wwwroot/js/configuration/ConfigManager.d.ts @@ -0,0 +1,11 @@ +import { Configuration } from './CalendarConfig'; +/** + * ConfigManager - Static configuration loader + * Loads JSON and creates Configuration instance + */ +export declare class ConfigManager { + /** + * Load configuration from JSON and create Configuration instance + */ + static load(): Promise; +} diff --git a/wwwroot/js/configuration/ConfigManager.js b/wwwroot/js/configuration/ConfigManager.js new file mode 100644 index 0000000..7f90a7d --- /dev/null +++ b/wwwroot/js/configuration/ConfigManager.js @@ -0,0 +1,43 @@ +import { Configuration } from './CalendarConfig'; +import { TimeFormatter } from '../utils/TimeFormatter'; +/** + * ConfigManager - Static configuration loader + * Loads JSON and creates Configuration instance + */ +export class ConfigManager { + /** + * Load configuration from JSON and create Configuration instance + */ + static async load() { + const response = await fetch('/wwwroot/data/calendar-config.json'); + if (!response.ok) { + throw new Error(`Failed to load config: ${response.statusText}`); + } + const data = await response.json(); + // Build main config + const mainConfig = { + scrollbarWidth: data.scrollbar.width, + scrollbarColor: data.scrollbar.color, + scrollbarTrackColor: data.scrollbar.trackColor, + scrollbarHoverColor: data.scrollbar.hoverColor, + scrollbarBorderRadius: data.scrollbar.borderRadius, + allowDrag: data.interaction.allowDrag, + allowResize: data.interaction.allowResize, + allowCreate: data.interaction.allowCreate, + apiEndpoint: data.api.endpoint, + dateFormat: data.api.dateFormat, + timeFormat: data.api.timeFormat, + enableSearch: data.features.enableSearch, + enableTouch: data.features.enableTouch, + defaultEventDuration: data.eventDefaults.defaultEventDuration, + minEventDuration: data.gridSettings.snapInterval, + maxEventDuration: data.eventDefaults.maxEventDuration + }; + // Create Configuration instance + const config = new Configuration(mainConfig, data.gridSettings, data.dateViewSettings, data.timeFormatConfig, data.currentWorkWeek); + // Configure TimeFormatter + TimeFormatter.configure(config.timeFormatConfig); + return config; + } +} +//# sourceMappingURL=ConfigManager.js.map \ No newline at end of file diff --git a/wwwroot/js/configuration/ConfigManager.js.map b/wwwroot/js/configuration/ConfigManager.js.map new file mode 100644 index 0000000..0bc0010 --- /dev/null +++ b/wwwroot/js/configuration/ConfigManager.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ConfigManager.js","sourceRoot":"","sources":["../../../src/configuration/ConfigManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjD,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAEvD;;;GAGG;AACH,MAAM,OAAO,aAAa;IACxB;;OAEG;IACH,MAAM,CAAC,KAAK,CAAC,IAAI;QACf,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,oCAAoC,CAAC,CAAC;QACnE,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,0BAA0B,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;QACnE,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QAEnC,oBAAoB;QACpB,MAAM,UAAU,GAAoB;YAClC,cAAc,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK;YACpC,cAAc,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK;YACpC,mBAAmB,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU;YAC9C,mBAAmB,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU;YAC9C,qBAAqB,EAAE,IAAI,CAAC,SAAS,CAAC,YAAY;YAClD,SAAS,EAAE,IAAI,CAAC,WAAW,CAAC,SAAS;YACrC,WAAW,EAAE,IAAI,CAAC,WAAW,CAAC,WAAW;YACzC,WAAW,EAAE,IAAI,CAAC,WAAW,CAAC,WAAW;YACzC,WAAW,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ;YAC9B,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,UAAU;YAC/B,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,UAAU;YAC/B,YAAY,EAAE,IAAI,CAAC,QAAQ,CAAC,YAAY;YACxC,WAAW,EAAE,IAAI,CAAC,QAAQ,CAAC,WAAW;YACtC,oBAAoB,EAAE,IAAI,CAAC,aAAa,CAAC,oBAAoB;YAC7D,gBAAgB,EAAE,IAAI,CAAC,YAAY,CAAC,YAAY;YAChD,gBAAgB,EAAE,IAAI,CAAC,aAAa,CAAC,gBAAgB;SACtD,CAAC;QAEF,gCAAgC;QAChC,MAAM,MAAM,GAAG,IAAI,aAAa,CAC9B,UAAU,EACV,IAAI,CAAC,YAAY,EACjB,IAAI,CAAC,gBAAgB,EACrB,IAAI,CAAC,gBAAgB,EACrB,IAAI,CAAC,eAAe,CACrB,CAAC;QAEF,0BAA0B;QAC1B,aAAa,CAAC,SAAS,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;QAEjD,OAAO,MAAM,CAAC;IAChB,CAAC;CACF"} \ No newline at end of file diff --git a/wwwroot/js/configuration/DateViewSettings.d.ts b/wwwroot/js/configuration/DateViewSettings.d.ts new file mode 100644 index 0000000..5459f66 --- /dev/null +++ b/wwwroot/js/configuration/DateViewSettings.d.ts @@ -0,0 +1,10 @@ +import { ViewPeriod } from '../types/CalendarTypes'; +/** + * View settings for date-based calendar mode + */ +export interface IDateViewSettings { + period: ViewPeriod; + weekDays: number; + firstDayOfWeek: number; + showAllDay: boolean; +} diff --git a/wwwroot/js/configuration/DateViewSettings.js b/wwwroot/js/configuration/DateViewSettings.js new file mode 100644 index 0000000..cb2b894 --- /dev/null +++ b/wwwroot/js/configuration/DateViewSettings.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=DateViewSettings.js.map \ No newline at end of file diff --git a/wwwroot/js/configuration/DateViewSettings.js.map b/wwwroot/js/configuration/DateViewSettings.js.map new file mode 100644 index 0000000..cf1d286 --- /dev/null +++ b/wwwroot/js/configuration/DateViewSettings.js.map @@ -0,0 +1 @@ +{"version":3,"file":"DateViewSettings.js","sourceRoot":"","sources":["../../../src/configuration/DateViewSettings.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/wwwroot/js/configuration/GridSettings.d.ts b/wwwroot/js/configuration/GridSettings.d.ts new file mode 100644 index 0000000..0db6981 --- /dev/null +++ b/wwwroot/js/configuration/GridSettings.d.ts @@ -0,0 +1,22 @@ +/** + * Grid display settings interface + */ +export interface IGridSettings { + dayStartHour: number; + dayEndHour: number; + workStartHour: number; + workEndHour: number; + hourHeight: number; + snapInterval: number; + fitToWidth: boolean; + scrollToHour: number | null; + gridStartThresholdMinutes: number; + showCurrentTime: boolean; + showWorkHours: boolean; +} +/** + * Grid settings utility functions + */ +export declare namespace GridSettingsUtils { + function isValidSnapInterval(interval: number): boolean; +} diff --git a/wwwroot/js/configuration/GridSettings.js b/wwwroot/js/configuration/GridSettings.js new file mode 100644 index 0000000..c7e399e --- /dev/null +++ b/wwwroot/js/configuration/GridSettings.js @@ -0,0 +1,11 @@ +/** + * Grid settings utility functions + */ +export var GridSettingsUtils; +(function (GridSettingsUtils) { + function isValidSnapInterval(interval) { + return [5, 10, 15, 30, 60].includes(interval); + } + GridSettingsUtils.isValidSnapInterval = isValidSnapInterval; +})(GridSettingsUtils || (GridSettingsUtils = {})); +//# sourceMappingURL=GridSettings.js.map \ No newline at end of file diff --git a/wwwroot/js/configuration/GridSettings.js.map b/wwwroot/js/configuration/GridSettings.js.map new file mode 100644 index 0000000..f8a3c86 --- /dev/null +++ b/wwwroot/js/configuration/GridSettings.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GridSettings.js","sourceRoot":"","sources":["../../../src/configuration/GridSettings.ts"],"names":[],"mappings":"AAiBA;;GAEG;AACH,MAAM,KAAW,iBAAiB,CAIjC;AAJD,WAAiB,iBAAiB;IAChC,SAAgB,mBAAmB,CAAC,QAAgB;QAClD,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAChD,CAAC;IAFe,qCAAmB,sBAElC,CAAA;AACH,CAAC,EAJgB,iBAAiB,KAAjB,iBAAiB,QAIjC"} \ No newline at end of file diff --git a/wwwroot/js/configuration/ICalendarConfig.d.ts b/wwwroot/js/configuration/ICalendarConfig.d.ts new file mode 100644 index 0000000..4c66c10 --- /dev/null +++ b/wwwroot/js/configuration/ICalendarConfig.d.ts @@ -0,0 +1,21 @@ +/** + * Main calendar configuration interface + */ +export interface ICalendarConfig { + scrollbarWidth: number; + scrollbarColor: string; + scrollbarTrackColor: string; + scrollbarHoverColor: string; + scrollbarBorderRadius: number; + allowDrag: boolean; + allowResize: boolean; + allowCreate: boolean; + apiEndpoint: string; + dateFormat: string; + timeFormat: string; + enableSearch: boolean; + enableTouch: boolean; + defaultEventDuration: number; + minEventDuration: number; + maxEventDuration: number; +} diff --git a/wwwroot/js/configuration/ICalendarConfig.js b/wwwroot/js/configuration/ICalendarConfig.js new file mode 100644 index 0000000..769ac97 --- /dev/null +++ b/wwwroot/js/configuration/ICalendarConfig.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=ICalendarConfig.js.map \ No newline at end of file diff --git a/wwwroot/js/configuration/ICalendarConfig.js.map b/wwwroot/js/configuration/ICalendarConfig.js.map new file mode 100644 index 0000000..46eb186 --- /dev/null +++ b/wwwroot/js/configuration/ICalendarConfig.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ICalendarConfig.js","sourceRoot":"","sources":["../../../src/configuration/ICalendarConfig.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/wwwroot/js/configuration/TimeFormatConfig.d.ts b/wwwroot/js/configuration/TimeFormatConfig.d.ts new file mode 100644 index 0000000..d1f26c1 --- /dev/null +++ b/wwwroot/js/configuration/TimeFormatConfig.d.ts @@ -0,0 +1,10 @@ +/** + * Time format configuration settings + */ +export interface ITimeFormatConfig { + timezone: string; + use24HourFormat: boolean; + locale: string; + dateFormat: 'locale' | 'technical'; + showSeconds: boolean; +} diff --git a/wwwroot/js/configuration/TimeFormatConfig.js b/wwwroot/js/configuration/TimeFormatConfig.js new file mode 100644 index 0000000..31213da --- /dev/null +++ b/wwwroot/js/configuration/TimeFormatConfig.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=TimeFormatConfig.js.map \ No newline at end of file diff --git a/wwwroot/js/configuration/TimeFormatConfig.js.map b/wwwroot/js/configuration/TimeFormatConfig.js.map new file mode 100644 index 0000000..8306905 --- /dev/null +++ b/wwwroot/js/configuration/TimeFormatConfig.js.map @@ -0,0 +1 @@ +{"version":3,"file":"TimeFormatConfig.js","sourceRoot":"","sources":["../../../src/configuration/TimeFormatConfig.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/wwwroot/js/configuration/WorkWeekSettings.d.ts b/wwwroot/js/configuration/WorkWeekSettings.d.ts new file mode 100644 index 0000000..b971f20 --- /dev/null +++ b/wwwroot/js/configuration/WorkWeekSettings.d.ts @@ -0,0 +1,9 @@ +/** + * Work week configuration settings + */ +export interface IWorkWeekSettings { + id: string; + workDays: number[]; + totalDays: number; + firstWorkDay: number; +} diff --git a/wwwroot/js/configuration/WorkWeekSettings.js b/wwwroot/js/configuration/WorkWeekSettings.js new file mode 100644 index 0000000..1b2eefc --- /dev/null +++ b/wwwroot/js/configuration/WorkWeekSettings.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=WorkWeekSettings.js.map \ No newline at end of file diff --git a/wwwroot/js/configuration/WorkWeekSettings.js.map b/wwwroot/js/configuration/WorkWeekSettings.js.map new file mode 100644 index 0000000..52447fd --- /dev/null +++ b/wwwroot/js/configuration/WorkWeekSettings.js.map @@ -0,0 +1 @@ +{"version":3,"file":"WorkWeekSettings.js","sourceRoot":"","sources":["../../../src/configuration/WorkWeekSettings.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/wwwroot/js/configurations/CalendarConfig.d.ts b/wwwroot/js/configurations/CalendarConfig.d.ts new file mode 100644 index 0000000..d883b53 --- /dev/null +++ b/wwwroot/js/configurations/CalendarConfig.d.ts @@ -0,0 +1,46 @@ +import { ICalendarConfig } from './ICalendarConfig'; +import { IGridSettings } from './GridSettings'; +import { IDateViewSettings } from './DateViewSettings'; +import { ITimeFormatConfig } from './TimeFormatConfig'; +import { IWorkWeekSettings } from './WorkWeekSettings'; +import { CalendarView } from '../types/CalendarTypes'; +/** + * All-day event layout constants + */ +export declare const ALL_DAY_CONSTANTS: { + readonly EVENT_HEIGHT: 22; + readonly EVENT_GAP: 2; + readonly CONTAINER_PADDING: 4; + readonly MAX_COLLAPSED_ROWS: 4; + readonly SINGLE_ROW_HEIGHT: number; +}; +/** + * Work week presets - Configuration data + */ +export declare const WORK_WEEK_PRESETS: { + [key: string]: IWorkWeekSettings; +}; +/** + * Configuration - DTO container for all configuration + * Pure data object loaded from JSON via ConfigManager + */ +export declare class Configuration { + private static _instance; + config: ICalendarConfig; + gridSettings: IGridSettings; + dateViewSettings: IDateViewSettings; + timeFormatConfig: ITimeFormatConfig; + currentWorkWeek: string; + currentView: CalendarView; + selectedDate: Date; + apiEndpoint: string; + constructor(config: ICalendarConfig, gridSettings: IGridSettings, dateViewSettings: IDateViewSettings, timeFormatConfig: ITimeFormatConfig, currentWorkWeek: string, currentView: CalendarView, selectedDate?: Date); + /** + * Get the current Configuration instance + * Used by web components that can't use dependency injection + */ + static getInstance(): Configuration; + setSelectedDate(date: Date): void; + getWorkWeekSettings(): IWorkWeekSettings; +} +export { Configuration as CalendarConfig }; diff --git a/wwwroot/js/configurations/CalendarConfig.js b/wwwroot/js/configurations/CalendarConfig.js new file mode 100644 index 0000000..89d9237 --- /dev/null +++ b/wwwroot/js/configurations/CalendarConfig.js @@ -0,0 +1,85 @@ +/** + * All-day event layout constants + */ +export const ALL_DAY_CONSTANTS = { + EVENT_HEIGHT: 22, + EVENT_GAP: 2, + CONTAINER_PADDING: 4, + MAX_COLLAPSED_ROWS: 4, + get SINGLE_ROW_HEIGHT() { + return this.EVENT_HEIGHT + this.EVENT_GAP; // 28px + } +}; +/** + * Work week presets - Configuration data + */ +export const WORK_WEEK_PRESETS = { + 'standard': { + id: 'standard', + workDays: [1, 2, 3, 4, 5], + totalDays: 5, + firstWorkDay: 1 + }, + 'compressed': { + id: 'compressed', + workDays: [1, 2, 3, 4], + totalDays: 4, + firstWorkDay: 1 + }, + 'midweek': { + id: 'midweek', + workDays: [3, 4, 5], + totalDays: 3, + firstWorkDay: 3 + }, + 'weekend': { + id: 'weekend', + workDays: [6, 7], + totalDays: 2, + firstWorkDay: 6 + }, + 'fullweek': { + id: 'fullweek', + workDays: [1, 2, 3, 4, 5, 6, 7], + totalDays: 7, + firstWorkDay: 1 + } +}; +/** + * Configuration - DTO container for all configuration + * Pure data object loaded from JSON via ConfigManager + */ +export class Configuration { + constructor(config, gridSettings, dateViewSettings, timeFormatConfig, currentWorkWeek, currentView, selectedDate = new Date()) { + this.apiEndpoint = '/api'; + this.config = config; + this.gridSettings = gridSettings; + this.dateViewSettings = dateViewSettings; + this.timeFormatConfig = timeFormatConfig; + this.currentWorkWeek = currentWorkWeek; + this.currentView = currentView; + this.selectedDate = selectedDate; + // Store as singleton instance for web components + Configuration._instance = this; + } + /** + * Get the current Configuration instance + * Used by web components that can't use dependency injection + */ + static getInstance() { + if (!Configuration._instance) { + throw new Error('Configuration has not been initialized. Call ConfigManager.load() first.'); + } + return Configuration._instance; + } + setSelectedDate(date) { + this.selectedDate = date; + } + getWorkWeekSettings() { + return WORK_WEEK_PRESETS[this.currentWorkWeek] || WORK_WEEK_PRESETS['standard']; + } +} +Configuration._instance = null; +// Backward compatibility alias +export { Configuration as CalendarConfig }; +//# sourceMappingURL=CalendarConfig.js.map \ No newline at end of file diff --git a/wwwroot/js/configurations/CalendarConfig.js.map b/wwwroot/js/configurations/CalendarConfig.js.map new file mode 100644 index 0000000..e563232 --- /dev/null +++ b/wwwroot/js/configurations/CalendarConfig.js.map @@ -0,0 +1 @@ +{"version":3,"file":"CalendarConfig.js","sourceRoot":"","sources":["../../../src/configurations/CalendarConfig.ts"],"names":[],"mappings":"AAOA;;GAEG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,YAAY,EAAE,EAAE;IAChB,SAAS,EAAE,CAAC;IACZ,iBAAiB,EAAE,CAAC;IACpB,kBAAkB,EAAE,CAAC;IACrB,IAAI,iBAAiB;QACnB,OAAO,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO;IACpD,CAAC;CACO,CAAC;AAEX;;GAEG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAyC;IACrE,UAAU,EAAE;QACV,EAAE,EAAE,UAAU;QACd,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QACzB,SAAS,EAAE,CAAC;QACZ,YAAY,EAAE,CAAC;KAChB;IACD,YAAY,EAAE;QACZ,EAAE,EAAE,YAAY;QAChB,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QACtB,SAAS,EAAE,CAAC;QACZ,YAAY,EAAE,CAAC;KAChB;IACD,SAAS,EAAE;QACT,EAAE,EAAE,SAAS;QACb,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QACnB,SAAS,EAAE,CAAC;QACZ,YAAY,EAAE,CAAC;KAChB;IACD,SAAS,EAAE;QACT,EAAE,EAAE,SAAS;QACb,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;QAChB,SAAS,EAAE,CAAC;QACZ,YAAY,EAAE,CAAC;KAChB;IACD,UAAU,EAAE;QACV,EAAE,EAAE,UAAU;QACd,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QAC/B,SAAS,EAAE,CAAC;QACZ,YAAY,EAAE,CAAC;KAChB;CACF,CAAC;AAEF;;;GAGG;AACH,MAAM,OAAO,aAAa;IAYxB,YACE,MAAuB,EACvB,YAA2B,EAC3B,gBAAmC,EACnC,gBAAmC,EACnC,eAAuB,EACvB,WAAyB,EACzB,eAAqB,IAAI,IAAI,EAAE;QAT1B,gBAAW,GAAW,MAAM,CAAC;QAWlC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,CAAC,gBAAgB,GAAG,gBAAgB,CAAC;QACzC,IAAI,CAAC,gBAAgB,GAAG,gBAAgB,CAAC;QACzC,IAAI,CAAC,eAAe,GAAG,eAAe,CAAC;QACvC,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QAEjC,iDAAiD;QACjD,aAAa,CAAC,SAAS,GAAG,IAAI,CAAC;IACjC,CAAC;IAED;;;OAGG;IACI,MAAM,CAAC,WAAW;QACvB,IAAI,CAAC,aAAa,CAAC,SAAS,EAAE,CAAC;YAC7B,MAAM,IAAI,KAAK,CAAC,0EAA0E,CAAC,CAAC;QAC9F,CAAC;QACD,OAAO,aAAa,CAAC,SAAS,CAAC;IACjC,CAAC;IAED,eAAe,CAAC,IAAU;QACxB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;IAC3B,CAAC;IAED,mBAAmB;QACjB,OAAO,iBAAiB,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,iBAAiB,CAAC,UAAU,CAAC,CAAC;IAClF,CAAC;;AAjDc,uBAAS,GAAyB,IAAI,AAA7B,CAA8B;AAoDxD,+BAA+B;AAC/B,OAAO,EAAE,aAAa,IAAI,cAAc,EAAE,CAAC"} \ No newline at end of file diff --git a/wwwroot/js/configurations/ConfigManager.d.ts b/wwwroot/js/configurations/ConfigManager.d.ts new file mode 100644 index 0000000..1123edd --- /dev/null +++ b/wwwroot/js/configurations/ConfigManager.d.ts @@ -0,0 +1,28 @@ +import { Configuration } from './CalendarConfig'; +import { IEventBus } from '../types/CalendarTypes'; +/** + * ConfigManager - Configuration loader and CSS property manager + * Loads JSON and creates Configuration instance + * Listens to events and manages CSS custom properties for dynamic styling + */ +export declare class ConfigManager { + private eventBus; + private config; + constructor(eventBus: IEventBus, config: Configuration); + /** + * Setup event listeners for dynamic CSS updates + */ + private setupEventListeners; + /** + * Sync grid-related CSS variables from configuration + */ + private syncGridCSSVariables; + /** + * Sync workweek-related CSS variables + */ + private syncWorkweekCSSVariables; + /** + * Load configuration from JSON and create Configuration instance + */ + static load(): Promise; +} diff --git a/wwwroot/js/configurations/ConfigManager.js b/wwwroot/js/configurations/ConfigManager.js new file mode 100644 index 0000000..1c75db8 --- /dev/null +++ b/wwwroot/js/configurations/ConfigManager.js @@ -0,0 +1,80 @@ +import { Configuration } from './CalendarConfig'; +import { TimeFormatter } from '../utils/TimeFormatter'; +import { CoreEvents } from '../constants/CoreEvents'; +/** + * ConfigManager - Configuration loader and CSS property manager + * Loads JSON and creates Configuration instance + * Listens to events and manages CSS custom properties for dynamic styling + */ +export class ConfigManager { + constructor(eventBus, config) { + this.eventBus = eventBus; + this.config = config; + this.setupEventListeners(); + this.syncGridCSSVariables(); + this.syncWorkweekCSSVariables(); + } + /** + * Setup event listeners for dynamic CSS updates + */ + setupEventListeners() { + // Listen to workweek changes and update CSS accordingly + this.eventBus.on(CoreEvents.WORKWEEK_CHANGED, (event) => { + const { settings } = event.detail; + this.syncWorkweekCSSVariables(settings); + }); + } + /** + * Sync grid-related CSS variables from configuration + */ + syncGridCSSVariables() { + const gridSettings = this.config.gridSettings; + document.documentElement.style.setProperty('--hour-height', `${gridSettings.hourHeight}px`); + document.documentElement.style.setProperty('--day-start-hour', gridSettings.dayStartHour.toString()); + document.documentElement.style.setProperty('--day-end-hour', gridSettings.dayEndHour.toString()); + document.documentElement.style.setProperty('--work-start-hour', gridSettings.workStartHour.toString()); + document.documentElement.style.setProperty('--work-end-hour', gridSettings.workEndHour.toString()); + } + /** + * Sync workweek-related CSS variables + */ + syncWorkweekCSSVariables(workWeekSettings) { + const settings = workWeekSettings || this.config.getWorkWeekSettings(); + document.documentElement.style.setProperty('--grid-columns', settings.totalDays.toString()); + } + /** + * Load configuration from JSON and create Configuration instance + */ + static async load() { + const response = await fetch('/wwwroot/data/calendar-config.json'); + if (!response.ok) { + throw new Error(`Failed to load config: ${response.statusText}`); + } + const data = await response.json(); + // Build main config + const mainConfig = { + scrollbarWidth: data.scrollbar.width, + scrollbarColor: data.scrollbar.color, + scrollbarTrackColor: data.scrollbar.trackColor, + scrollbarHoverColor: data.scrollbar.hoverColor, + scrollbarBorderRadius: data.scrollbar.borderRadius, + allowDrag: data.interaction.allowDrag, + allowResize: data.interaction.allowResize, + allowCreate: data.interaction.allowCreate, + apiEndpoint: data.api.endpoint, + dateFormat: data.api.dateFormat, + timeFormat: data.api.timeFormat, + enableSearch: data.features.enableSearch, + enableTouch: data.features.enableTouch, + defaultEventDuration: data.eventDefaults.defaultEventDuration, + minEventDuration: data.gridSettings.snapInterval, + maxEventDuration: data.eventDefaults.maxEventDuration + }; + // Create Configuration instance + const config = new Configuration(mainConfig, data.gridSettings, data.dateViewSettings, data.timeFormatConfig, data.currentWorkWeek, data.currentView || 'week'); + // Configure TimeFormatter + TimeFormatter.configure(config.timeFormatConfig); + return config; + } +} +//# sourceMappingURL=ConfigManager.js.map \ No newline at end of file diff --git a/wwwroot/js/configurations/ConfigManager.js.map b/wwwroot/js/configurations/ConfigManager.js.map new file mode 100644 index 0000000..71e69a0 --- /dev/null +++ b/wwwroot/js/configurations/ConfigManager.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ConfigManager.js","sourceRoot":"","sources":["../../../src/configurations/ConfigManager.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/configurations/DateViewSettings.d.ts b/wwwroot/js/configurations/DateViewSettings.d.ts new file mode 100644 index 0000000..5459f66 --- /dev/null +++ b/wwwroot/js/configurations/DateViewSettings.d.ts @@ -0,0 +1,10 @@ +import { ViewPeriod } from '../types/CalendarTypes'; +/** + * View settings for date-based calendar mode + */ +export interface IDateViewSettings { + period: ViewPeriod; + weekDays: number; + firstDayOfWeek: number; + showAllDay: boolean; +} diff --git a/wwwroot/js/configurations/DateViewSettings.js b/wwwroot/js/configurations/DateViewSettings.js new file mode 100644 index 0000000..cb2b894 --- /dev/null +++ b/wwwroot/js/configurations/DateViewSettings.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=DateViewSettings.js.map \ No newline at end of file diff --git a/wwwroot/js/configurations/DateViewSettings.js.map b/wwwroot/js/configurations/DateViewSettings.js.map new file mode 100644 index 0000000..385c982 --- /dev/null +++ b/wwwroot/js/configurations/DateViewSettings.js.map @@ -0,0 +1 @@ +{"version":3,"file":"DateViewSettings.js","sourceRoot":"","sources":["../../../src/configurations/DateViewSettings.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/wwwroot/js/configurations/GridSettings.d.ts b/wwwroot/js/configurations/GridSettings.d.ts new file mode 100644 index 0000000..0db6981 --- /dev/null +++ b/wwwroot/js/configurations/GridSettings.d.ts @@ -0,0 +1,22 @@ +/** + * Grid display settings interface + */ +export interface IGridSettings { + dayStartHour: number; + dayEndHour: number; + workStartHour: number; + workEndHour: number; + hourHeight: number; + snapInterval: number; + fitToWidth: boolean; + scrollToHour: number | null; + gridStartThresholdMinutes: number; + showCurrentTime: boolean; + showWorkHours: boolean; +} +/** + * Grid settings utility functions + */ +export declare namespace GridSettingsUtils { + function isValidSnapInterval(interval: number): boolean; +} diff --git a/wwwroot/js/configurations/GridSettings.js b/wwwroot/js/configurations/GridSettings.js new file mode 100644 index 0000000..c7e399e --- /dev/null +++ b/wwwroot/js/configurations/GridSettings.js @@ -0,0 +1,11 @@ +/** + * Grid settings utility functions + */ +export var GridSettingsUtils; +(function (GridSettingsUtils) { + function isValidSnapInterval(interval) { + return [5, 10, 15, 30, 60].includes(interval); + } + GridSettingsUtils.isValidSnapInterval = isValidSnapInterval; +})(GridSettingsUtils || (GridSettingsUtils = {})); +//# sourceMappingURL=GridSettings.js.map \ No newline at end of file diff --git a/wwwroot/js/configurations/GridSettings.js.map b/wwwroot/js/configurations/GridSettings.js.map new file mode 100644 index 0000000..cdfbb83 --- /dev/null +++ b/wwwroot/js/configurations/GridSettings.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GridSettings.js","sourceRoot":"","sources":["../../../src/configurations/GridSettings.ts"],"names":[],"mappings":"AAiBA;;GAEG;AACH,MAAM,KAAW,iBAAiB,CAIjC;AAJD,WAAiB,iBAAiB;IAChC,SAAgB,mBAAmB,CAAC,QAAgB;QAClD,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAChD,CAAC;IAFe,qCAAmB,sBAElC,CAAA;AACH,CAAC,EAJgB,iBAAiB,KAAjB,iBAAiB,QAIjC"} \ No newline at end of file diff --git a/wwwroot/js/configurations/ICalendarConfig.d.ts b/wwwroot/js/configurations/ICalendarConfig.d.ts new file mode 100644 index 0000000..4c66c10 --- /dev/null +++ b/wwwroot/js/configurations/ICalendarConfig.d.ts @@ -0,0 +1,21 @@ +/** + * Main calendar configuration interface + */ +export interface ICalendarConfig { + scrollbarWidth: number; + scrollbarColor: string; + scrollbarTrackColor: string; + scrollbarHoverColor: string; + scrollbarBorderRadius: number; + allowDrag: boolean; + allowResize: boolean; + allowCreate: boolean; + apiEndpoint: string; + dateFormat: string; + timeFormat: string; + enableSearch: boolean; + enableTouch: boolean; + defaultEventDuration: number; + minEventDuration: number; + maxEventDuration: number; +} diff --git a/wwwroot/js/configurations/ICalendarConfig.js b/wwwroot/js/configurations/ICalendarConfig.js new file mode 100644 index 0000000..769ac97 --- /dev/null +++ b/wwwroot/js/configurations/ICalendarConfig.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=ICalendarConfig.js.map \ No newline at end of file diff --git a/wwwroot/js/configurations/ICalendarConfig.js.map b/wwwroot/js/configurations/ICalendarConfig.js.map new file mode 100644 index 0000000..f3e954b --- /dev/null +++ b/wwwroot/js/configurations/ICalendarConfig.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ICalendarConfig.js","sourceRoot":"","sources":["../../../src/configurations/ICalendarConfig.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/wwwroot/js/configurations/TimeFormatConfig.d.ts b/wwwroot/js/configurations/TimeFormatConfig.d.ts new file mode 100644 index 0000000..d1f26c1 --- /dev/null +++ b/wwwroot/js/configurations/TimeFormatConfig.d.ts @@ -0,0 +1,10 @@ +/** + * Time format configuration settings + */ +export interface ITimeFormatConfig { + timezone: string; + use24HourFormat: boolean; + locale: string; + dateFormat: 'locale' | 'technical'; + showSeconds: boolean; +} diff --git a/wwwroot/js/configurations/TimeFormatConfig.js b/wwwroot/js/configurations/TimeFormatConfig.js new file mode 100644 index 0000000..31213da --- /dev/null +++ b/wwwroot/js/configurations/TimeFormatConfig.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=TimeFormatConfig.js.map \ No newline at end of file diff --git a/wwwroot/js/configurations/TimeFormatConfig.js.map b/wwwroot/js/configurations/TimeFormatConfig.js.map new file mode 100644 index 0000000..c94321c --- /dev/null +++ b/wwwroot/js/configurations/TimeFormatConfig.js.map @@ -0,0 +1 @@ +{"version":3,"file":"TimeFormatConfig.js","sourceRoot":"","sources":["../../../src/configurations/TimeFormatConfig.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/wwwroot/js/configurations/WorkWeekSettings.d.ts b/wwwroot/js/configurations/WorkWeekSettings.d.ts new file mode 100644 index 0000000..b971f20 --- /dev/null +++ b/wwwroot/js/configurations/WorkWeekSettings.d.ts @@ -0,0 +1,9 @@ +/** + * Work week configuration settings + */ +export interface IWorkWeekSettings { + id: string; + workDays: number[]; + totalDays: number; + firstWorkDay: number; +} diff --git a/wwwroot/js/configurations/WorkWeekSettings.js b/wwwroot/js/configurations/WorkWeekSettings.js new file mode 100644 index 0000000..1b2eefc --- /dev/null +++ b/wwwroot/js/configurations/WorkWeekSettings.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=WorkWeekSettings.js.map \ No newline at end of file diff --git a/wwwroot/js/configurations/WorkWeekSettings.js.map b/wwwroot/js/configurations/WorkWeekSettings.js.map new file mode 100644 index 0000000..9fe597d --- /dev/null +++ b/wwwroot/js/configurations/WorkWeekSettings.js.map @@ -0,0 +1 @@ +{"version":3,"file":"WorkWeekSettings.js","sourceRoot":"","sources":["../../../src/configurations/WorkWeekSettings.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/wwwroot/js/constants/CoreEvents.d.ts b/wwwroot/js/constants/CoreEvents.d.ts new file mode 100644 index 0000000..7e98d27 --- /dev/null +++ b/wwwroot/js/constants/CoreEvents.d.ts @@ -0,0 +1,37 @@ +/** + * CoreEvents - Consolidated essential events for the calendar + * Reduces complexity from 102+ events to ~20 core events + */ +export declare const CoreEvents: { + readonly INITIALIZED: "core:initialized"; + readonly READY: "core:ready"; + readonly DESTROYED: "core:destroyed"; + readonly VIEW_CHANGED: "view:changed"; + readonly VIEW_RENDERED: "view:rendered"; + readonly WORKWEEK_CHANGED: "workweek:changed"; + readonly NAV_BUTTON_CLICKED: "nav:button-clicked"; + readonly DATE_CHANGED: "nav:date-changed"; + readonly NAVIGATION_COMPLETED: "nav:navigation-completed"; + readonly NAVIGATE_TO_EVENT: "nav:navigate-to-event"; + readonly DATA_LOADING: "data:loading"; + readonly DATA_LOADED: "data:loaded"; + readonly DATA_ERROR: "data:error"; + readonly EVENTS_FILTERED: "data:events-filtered"; + readonly REMOTE_UPDATE_RECEIVED: "data:remote-update"; + readonly GRID_RENDERED: "grid:rendered"; + readonly GRID_CLICKED: "grid:clicked"; + readonly CELL_SELECTED: "grid:cell-selected"; + readonly EVENT_CREATED: "event:created"; + readonly EVENT_UPDATED: "event:updated"; + readonly EVENT_DELETED: "event:deleted"; + readonly EVENT_SELECTED: "event:selected"; + readonly ERROR: "system:error"; + readonly REFRESH_REQUESTED: "system:refresh"; + readonly OFFLINE_MODE_CHANGED: "system:offline-mode-changed"; + readonly SYNC_STARTED: "sync:started"; + readonly SYNC_COMPLETED: "sync:completed"; + readonly SYNC_FAILED: "sync:failed"; + readonly SYNC_RETRY: "sync:retry"; + readonly FILTER_CHANGED: "filter:changed"; + readonly EVENTS_RENDERED: "events:rendered"; +}; diff --git a/wwwroot/js/constants/CoreEvents.js b/wwwroot/js/constants/CoreEvents.js new file mode 100644 index 0000000..f72a48a --- /dev/null +++ b/wwwroot/js/constants/CoreEvents.js @@ -0,0 +1,48 @@ +/** + * CoreEvents - Consolidated essential events for the calendar + * Reduces complexity from 102+ events to ~20 core events + */ +export const CoreEvents = { + // Lifecycle events (3) + INITIALIZED: 'core:initialized', + READY: 'core:ready', + DESTROYED: 'core:destroyed', + // View events (3) + VIEW_CHANGED: 'view:changed', + VIEW_RENDERED: 'view:rendered', + WORKWEEK_CHANGED: 'workweek:changed', + // Navigation events (4) + NAV_BUTTON_CLICKED: 'nav:button-clicked', + DATE_CHANGED: 'nav:date-changed', + NAVIGATION_COMPLETED: 'nav:navigation-completed', + NAVIGATE_TO_EVENT: 'nav:navigate-to-event', + // Data events (5) + DATA_LOADING: 'data:loading', + DATA_LOADED: 'data:loaded', + DATA_ERROR: 'data:error', + EVENTS_FILTERED: 'data:events-filtered', + REMOTE_UPDATE_RECEIVED: 'data:remote-update', + // Grid events (3) + GRID_RENDERED: 'grid:rendered', + GRID_CLICKED: 'grid:clicked', + CELL_SELECTED: 'grid:cell-selected', + // Event management (4) + EVENT_CREATED: 'event:created', + EVENT_UPDATED: 'event:updated', + EVENT_DELETED: 'event:deleted', + EVENT_SELECTED: 'event:selected', + // System events (3) + ERROR: 'system:error', + REFRESH_REQUESTED: 'system:refresh', + OFFLINE_MODE_CHANGED: 'system:offline-mode-changed', + // Sync events (4) + SYNC_STARTED: 'sync:started', + SYNC_COMPLETED: 'sync:completed', + SYNC_FAILED: 'sync:failed', + SYNC_RETRY: 'sync:retry', + // Filter events (1) + FILTER_CHANGED: 'filter:changed', + // Rendering events (1) + EVENTS_RENDERED: 'events:rendered' +}; +//# sourceMappingURL=CoreEvents.js.map \ No newline at end of file diff --git a/wwwroot/js/constants/CoreEvents.js.map b/wwwroot/js/constants/CoreEvents.js.map new file mode 100644 index 0000000..d32cee3 --- /dev/null +++ b/wwwroot/js/constants/CoreEvents.js.map @@ -0,0 +1 @@ +{"version":3,"file":"CoreEvents.js","sourceRoot":"","sources":["../../../src/constants/CoreEvents.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,uBAAuB;IACvB,WAAW,EAAE,kBAAkB;IAC/B,KAAK,EAAE,YAAY;IACnB,SAAS,EAAE,gBAAgB;IAE3B,kBAAkB;IAClB,YAAY,EAAE,cAAc;IAC5B,aAAa,EAAE,eAAe;IAC9B,gBAAgB,EAAE,kBAAkB;IAEpC,wBAAwB;IACxB,kBAAkB,EAAE,oBAAoB;IACxC,YAAY,EAAE,kBAAkB;IAChC,oBAAoB,EAAE,0BAA0B;IAChD,iBAAiB,EAAE,uBAAuB;IAE1C,kBAAkB;IAClB,YAAY,EAAE,cAAc;IAC5B,WAAW,EAAE,aAAa;IAC1B,UAAU,EAAE,YAAY;IACxB,eAAe,EAAE,sBAAsB;IACvC,sBAAsB,EAAE,oBAAoB;IAE5C,kBAAkB;IAClB,aAAa,EAAE,eAAe;IAC9B,YAAY,EAAE,cAAc;IAC5B,aAAa,EAAE,oBAAoB;IAEnC,uBAAuB;IACvB,aAAa,EAAE,eAAe;IAC9B,aAAa,EAAE,eAAe;IAC9B,aAAa,EAAE,eAAe;IAC9B,cAAc,EAAE,gBAAgB;IAEhC,oBAAoB;IACpB,KAAK,EAAE,cAAc;IACrB,iBAAiB,EAAE,gBAAgB;IACnC,oBAAoB,EAAE,6BAA6B;IAEnD,kBAAkB;IAClB,YAAY,EAAE,cAAc;IAC5B,cAAc,EAAE,gBAAgB;IAChC,WAAW,EAAE,aAAa;IAC1B,UAAU,EAAE,YAAY;IAExB,oBAAoB;IACpB,cAAc,EAAE,gBAAgB;IAEhC,uBAAuB;IACvB,eAAe,EAAE,iBAAiB;CAC1B,CAAC"} \ No newline at end of file diff --git a/wwwroot/js/core/CalendarConfig.d.ts b/wwwroot/js/core/CalendarConfig.d.ts new file mode 100644 index 0000000..c19e1f3 --- /dev/null +++ b/wwwroot/js/core/CalendarConfig.d.ts @@ -0,0 +1,225 @@ +import { CalendarConfig as ICalendarConfig, ViewPeriod, CalendarMode } from '../types/CalendarTypes'; +/** + * All-day event layout constants + */ +export declare const ALL_DAY_CONSTANTS: { + readonly EVENT_HEIGHT: 22; + readonly EVENT_GAP: 2; + readonly CONTAINER_PADDING: 4; + readonly MAX_COLLAPSED_ROWS: 4; + readonly SINGLE_ROW_HEIGHT: number; +}; +/** + * Layout and timing settings for the calendar grid + */ +interface GridSettings { + dayStartHour: number; + dayEndHour: number; + workStartHour: number; + workEndHour: number; + hourHeight: number; + snapInterval: number; + fitToWidth: boolean; + scrollToHour: number | null; + gridStartThresholdMinutes: number; + showCurrentTime: boolean; + showWorkHours: boolean; +} +/** + * View settings for date-based calendar mode + */ +interface DateViewSettings { + period: ViewPeriod; + weekDays: number; + firstDayOfWeek: number; + showAllDay: boolean; +} +/** + * Work week configuration settings + */ +interface WorkWeekSettings { + id: string; + workDays: number[]; + totalDays: number; + firstWorkDay: number; +} +/** + * View settings for resource-based calendar mode + */ +interface ResourceViewSettings { + maxResources: number; + showAvatars: boolean; + avatarSize: number; + resourceNameFormat: 'full' | 'short'; + showResourceDetails: boolean; + showAllDay: boolean; +} +/** + * Time format configuration settings + */ +interface TimeFormatConfig { + timezone: string; + use24HourFormat: boolean; + locale: string; + dateFormat: 'locale' | 'technical'; + showSeconds: boolean; +} +/** + * Calendar configuration management + */ +export declare class CalendarConfig { + private config; + private calendarMode; + private selectedDate; + private gridSettings; + private dateViewSettings; + private resourceViewSettings; + private currentWorkWeek; + private timeFormatConfig; + constructor(); + /** + * Load calendar type and date from URL parameters + */ + private loadCalendarType; + /** + * Load configuration from DOM data attributes + */ + private loadFromDOM; + /** + * Get a config value + */ + get(key: K): ICalendarConfig[K]; + /** + * Set a config value + */ + set(key: K, value: ICalendarConfig[K]): void; + /** + * Update multiple config values + */ + update(updates: Partial): void; + /** + * Get all config + */ + getAll(): ICalendarConfig; + /** + * Calculate derived values + */ + get minuteHeight(): number; + get totalHours(): number; + get totalMinutes(): number; + get slotsPerHour(): number; + get totalSlots(): number; + get slotHeight(): number; + /** + * Validate snap interval + */ + isValidSnapInterval(interval: number): boolean; + /** + * Get grid display settings + */ + getGridSettings(): GridSettings; + /** + * Update grid display settings + */ + updateGridSettings(updates: Partial): void; + /** + * Get date view settings + */ + getDateViewSettings(): DateViewSettings; + /** + * Update date view settings + */ + updateDateViewSettings(updates: Partial): void; + /** + * Get resource view settings + */ + getResourceViewSettings(): ResourceViewSettings; + /** + * Update resource view settings + */ + updateResourceViewSettings(updates: Partial): void; + /** + * Check if current mode is resource-based + */ + isResourceMode(): boolean; + /** + * Check if current mode is date-based + */ + isDateMode(): boolean; + /** + * Get calendar mode + */ + getCalendarMode(): CalendarMode; + /** + * Set calendar mode + */ + setCalendarMode(mode: CalendarMode): void; + /** + * Get selected date + */ + getSelectedDate(): Date | null; + /** + * Set selected date + * Note: Does not emit events - caller is responsible for event emission + */ + setSelectedDate(date: Date): void; + /** + * Get work week presets + */ + private getWorkWeekPresets; + /** + * Get current work week settings + */ + getWorkWeekSettings(): WorkWeekSettings; + /** + * Set work week preset + * Note: Does not emit events - caller is responsible for event emission + */ + setWorkWeek(workWeekId: string): void; + /** + * Get current work week ID + */ + getCurrentWorkWeek(): string; + /** + * Get time format settings + */ + getTimeFormatSettings(): TimeFormatConfig; + /** + * Update time format settings + */ + updateTimeFormatSettings(updates: Partial): void; + /** + * Set timezone (convenience method) + */ + setTimezone(timezone: string): void; + /** + * Set 12/24 hour format (convenience method) + */ + set24HourFormat(use24Hour: boolean): void; + /** + * Get configured timezone + */ + getTimezone(): string; + /** + * Get configured locale + */ + getLocale(): string; + /** + * Check if using 24-hour format + */ + is24HourFormat(): boolean; + /** + * Set date format (convenience method) + */ + setDateFormat(format: 'locale' | 'technical'): void; + /** + * Set whether to show seconds (convenience method) + */ + setShowSeconds(show: boolean): void; + /** + * Get current date format + */ + getDateFormat(): 'locale' | 'technical'; +} +export declare const calendarConfig: CalendarConfig; +export {}; diff --git a/wwwroot/js/core/CalendarConfig.js b/wwwroot/js/core/CalendarConfig.js new file mode 100644 index 0000000..010ca10 --- /dev/null +++ b/wwwroot/js/core/CalendarConfig.js @@ -0,0 +1,421 @@ +// Calendar configuration management +import { eventBus } from './EventBus'; +import { CoreEvents } from '../constants/CoreEvents'; +import { TimeFormatter } from '../utils/TimeFormatter'; +/** + * All-day event layout constants + */ +export const ALL_DAY_CONSTANTS = { + EVENT_HEIGHT: 22, // Height of single all-day event + EVENT_GAP: 2, // Gap between stacked events + CONTAINER_PADDING: 4, // Container padding (top + bottom) + get SINGLE_ROW_HEIGHT() { + return this.EVENT_HEIGHT + this.EVENT_GAP + this.CONTAINER_PADDING; // 28px + } +}; +/** + * Calendar configuration management + */ +export class CalendarConfig { + constructor() { + this.calendarMode = 'date'; + this.selectedDate = null; + this.currentWorkWeek = 'standard'; + this.config = { + // Scrollbar styling + scrollbarWidth: 16, // Width of scrollbar in pixels + scrollbarColor: '#666', // Scrollbar thumb color + scrollbarTrackColor: '#f0f0f0', // Scrollbar track color + scrollbarHoverColor: '#b53f7aff', // Scrollbar thumb hover color + scrollbarBorderRadius: 6, // Border radius for scrollbar thumb + // Interaction settings + allowDrag: true, + allowResize: true, + allowCreate: true, + // API settings + apiEndpoint: '/api/events', + dateFormat: 'YYYY-MM-DD', + timeFormat: 'HH:mm', + // Feature flags + enableSearch: true, + enableTouch: true, + // Event defaults + defaultEventDuration: 60, // Minutes + minEventDuration: 15, // Will be same as snapInterval + maxEventDuration: 480 // 8 hours + }; + // Grid display settings + this.gridSettings = { + hourHeight: 60, + dayStartHour: 0, + dayEndHour: 24, + workStartHour: 8, + workEndHour: 17, + snapInterval: 15, + showCurrentTime: true, + showWorkHours: true, + fitToWidth: false, + scrollToHour: 8 + }; + // Date view settings + this.dateViewSettings = { + period: 'week', + weekDays: 7, + firstDayOfWeek: 1, + showAllDay: true + }; + // Resource view settings + this.resourceViewSettings = { + maxResources: 10, + showAvatars: true, + avatarSize: 32, + resourceNameFormat: 'full', + showResourceDetails: true, + showAllDay: true + }; + // Time format settings - default to Denmark + this.timeFormatConfig = { + timezone: 'Europe/Copenhagen', + use24HourFormat: true, + locale: 'da-DK' + }; + // Set computed values + this.config.minEventDuration = this.gridSettings.snapInterval; + // Initialize TimeFormatter with default settings + TimeFormatter.configure(this.timeFormatConfig); + // Load calendar type from URL parameter + this.loadCalendarType(); + // Load from data attributes + this.loadFromDOM(); + } + /** + * Load calendar type and date from URL parameters + */ + loadCalendarType() { + const urlParams = new URLSearchParams(window.location.search); + const typeParam = urlParams.get('type'); + const dateParam = urlParams.get('date'); + // Set calendar mode + if (typeParam === 'resource' || typeParam === 'date') { + this.calendarMode = typeParam; + } + else { + this.calendarMode = 'date'; // Default + } + // Set selected date + if (dateParam) { + const parsedDate = new Date(dateParam); + if (!isNaN(parsedDate.getTime())) { + this.selectedDate = parsedDate; + } + else { + this.selectedDate = new Date(); + } + } + else { + this.selectedDate = new Date(); // Default to today + } + } + /** + * Load configuration from DOM data attributes + */ + loadFromDOM() { + const calendar = document.querySelector('swp-calendar'); + if (!calendar) + return; + // Read data attributes + const attrs = calendar.dataset; + // Update date view settings + if (attrs.view) + this.dateViewSettings.period = attrs.view; + if (attrs.weekDays) + this.dateViewSettings.weekDays = parseInt(attrs.weekDays); + // Update grid settings + if (attrs.snapInterval) + this.gridSettings.snapInterval = parseInt(attrs.snapInterval); + if (attrs.dayStartHour) + this.gridSettings.dayStartHour = parseInt(attrs.dayStartHour); + if (attrs.dayEndHour) + this.gridSettings.dayEndHour = parseInt(attrs.dayEndHour); + if (attrs.hourHeight) + this.gridSettings.hourHeight = parseInt(attrs.hourHeight); + if (attrs.fitToWidth !== undefined) + this.gridSettings.fitToWidth = attrs.fitToWidth === 'true'; + // Update computed values + this.config.minEventDuration = this.gridSettings.snapInterval; + } + /** + * Get a config value + */ + get(key) { + return this.config[key]; + } + /** + * Set a config value + */ + set(key, value) { + const oldValue = this.config[key]; + this.config[key] = value; + // Update computed values handled in specific update methods + // Emit config update event + eventBus.emit(CoreEvents.REFRESH_REQUESTED, { + key, + value, + oldValue + }); + } + /** + * Update multiple config values + */ + update(updates) { + Object.entries(updates).forEach(([key, value]) => { + this.set(key, value); + }); + } + /** + * Get all config + */ + getAll() { + return { ...this.config }; + } + /** + * Calculate derived values + */ + get minuteHeight() { + return this.gridSettings.hourHeight / 60; + } + get totalHours() { + return this.gridSettings.dayEndHour - this.gridSettings.dayStartHour; + } + get totalMinutes() { + return this.totalHours * 60; + } + get slotsPerHour() { + return 60 / this.gridSettings.snapInterval; + } + get totalSlots() { + return this.totalHours * this.slotsPerHour; + } + get slotHeight() { + return this.gridSettings.hourHeight / this.slotsPerHour; + } + /** + * Validate snap interval + */ + isValidSnapInterval(interval) { + return [5, 10, 15, 30, 60].includes(interval); + } + /** + * Get grid display settings + */ + getGridSettings() { + return { ...this.gridSettings }; + } + /** + * Update grid display settings + */ + updateGridSettings(updates) { + this.gridSettings = { ...this.gridSettings, ...updates }; + // Update computed values + if (updates.snapInterval) { + this.config.minEventDuration = updates.snapInterval; + } + // Grid settings changes trigger general refresh - avoid specific event + eventBus.emit(CoreEvents.REFRESH_REQUESTED, { + key: 'gridSettings', + value: this.gridSettings + }); + } + /** + * Get date view settings + */ + getDateViewSettings() { + return { ...this.dateViewSettings }; + } + /** + * Update date view settings + */ + updateDateViewSettings(updates) { + this.dateViewSettings = { ...this.dateViewSettings, ...updates }; + // Date view settings changes trigger general refresh - avoid specific event + eventBus.emit(CoreEvents.REFRESH_REQUESTED, { + key: 'dateViewSettings', + value: this.dateViewSettings + }); + } + /** + * Get resource view settings + */ + getResourceViewSettings() { + return { ...this.resourceViewSettings }; + } + /** + * Update resource view settings + */ + updateResourceViewSettings(updates) { + this.resourceViewSettings = { ...this.resourceViewSettings, ...updates }; + // Resource view settings changes trigger general refresh - avoid specific event + eventBus.emit(CoreEvents.REFRESH_REQUESTED, { + key: 'resourceViewSettings', + value: this.resourceViewSettings + }); + } + /** + * Check if current mode is resource-based + */ + isResourceMode() { + return this.calendarMode === 'resource'; + } + /** + * Check if current mode is date-based + */ + isDateMode() { + return this.calendarMode === 'date'; + } + /** + * Get calendar mode + */ + getCalendarMode() { + return this.calendarMode; + } + /** + * Set calendar mode + */ + setCalendarMode(mode) { + const oldMode = this.calendarMode; + this.calendarMode = mode; + // Emit calendar mode change event + eventBus.emit(CoreEvents.VIEW_CHANGED, { + oldType: oldMode, + newType: mode + }); + } + /** + * Get selected date + */ + getSelectedDate() { + return this.selectedDate; + } + /** + * Set selected date + */ + setSelectedDate(date) { + this.selectedDate = date; + // Emit date change event + eventBus.emit(CoreEvents.DATE_CHANGED, { + date: date + }); + } + /** + * Get work week presets + */ + getWorkWeekPresets() { + return { + 'standard': { + id: 'standard', + workDays: [1, 2, 3, 4, 5], // Monday-Friday (ISO) + totalDays: 5, + firstWorkDay: 1 + }, + 'compressed': { + id: 'compressed', + workDays: [1, 2, 3, 4], // Monday-Thursday (ISO) + totalDays: 4, + firstWorkDay: 1 + }, + 'midweek': { + id: 'midweek', + workDays: [3, 4, 5], // Wednesday-Friday (ISO) + totalDays: 3, + firstWorkDay: 3 + }, + 'weekend': { + id: 'weekend', + workDays: [6, 7], // Saturday-Sunday (ISO) + totalDays: 2, + firstWorkDay: 6 + }, + 'fullweek': { + id: 'fullweek', + workDays: [1, 2, 3, 4, 5, 6, 7], // Monday-Sunday (ISO) + totalDays: 7, + firstWorkDay: 1 + } + }; + } + /** + * Get current work week settings + */ + getWorkWeekSettings() { + const presets = this.getWorkWeekPresets(); + return presets[this.currentWorkWeek] || presets['standard']; + } + /** + * Set work week preset + */ + setWorkWeek(workWeekId) { + const presets = this.getWorkWeekPresets(); + if (presets[workWeekId]) { + this.currentWorkWeek = workWeekId; + // Update dateViewSettings to match work week + this.dateViewSettings.weekDays = presets[workWeekId].totalDays; + // Emit work week change event + eventBus.emit(CoreEvents.WORKWEEK_CHANGED, { + workWeekId: workWeekId, + settings: presets[workWeekId] + }); + } + } + /** + * Get current work week ID + */ + getCurrentWorkWeek() { + return this.currentWorkWeek; + } + /** + * Get time format settings + */ + getTimeFormatSettings() { + return { ...this.timeFormatConfig }; + } + /** + * Update time format settings + */ + updateTimeFormatSettings(updates) { + this.timeFormatConfig = { ...this.timeFormatConfig, ...updates }; + // Update TimeFormatter with new settings + TimeFormatter.configure(this.timeFormatConfig); + // Emit time format change event + eventBus.emit(CoreEvents.REFRESH_REQUESTED, { + key: 'timeFormatSettings', + value: this.timeFormatConfig + }); + } + /** + * Set timezone (convenience method) + */ + setTimezone(timezone) { + this.updateTimeFormatSettings({ timezone }); + } + /** + * Set 12/24 hour format (convenience method) + */ + set24HourFormat(use24Hour) { + this.updateTimeFormatSettings({ use24HourFormat: use24Hour }); + } + /** + * Get configured timezone + */ + getTimezone() { + return this.timeFormatConfig.timezone; + } + /** + * Check if using 24-hour format + */ + is24HourFormat() { + return this.timeFormatConfig.use24HourFormat; + } +} +// Create singleton instance +export const calendarConfig = new CalendarConfig(); +//# sourceMappingURL=CalendarConfig.js.map \ No newline at end of file diff --git a/wwwroot/js/core/CalendarConfig.js.map b/wwwroot/js/core/CalendarConfig.js.map new file mode 100644 index 0000000..d0e1710 --- /dev/null +++ b/wwwroot/js/core/CalendarConfig.js.map @@ -0,0 +1 @@ +{"version":3,"file":"CalendarConfig.js","sourceRoot":"","sources":["../../../src/core/CalendarConfig.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/core/EventBus.d.ts b/wwwroot/js/core/EventBus.d.ts new file mode 100644 index 0000000..93273d5 --- /dev/null +++ b/wwwroot/js/core/EventBus.d.ts @@ -0,0 +1,60 @@ +import { IEventLogEntry, IEventBus } from '../types/CalendarTypes'; +/** + * Central event dispatcher for calendar using DOM CustomEvents + * Provides logging and debugging capabilities + */ +export declare class EventBus implements IEventBus { + private eventLog; + private debug; + private listeners; + private logConfig; + /** + * Subscribe to an event via DOM addEventListener + */ + on(eventType: string, handler: EventListener, options?: AddEventListenerOptions): () => void; + /** + * Subscribe to an event once + */ + once(eventType: string, handler: EventListener): () => void; + /** + * Unsubscribe from an event + */ + off(eventType: string, handler: EventListener): void; + /** + * Emit an event via DOM CustomEvent + */ + emit(eventType: string, detail?: unknown): boolean; + /** + * Log event with console grouping + */ + private logEventWithGrouping; + /** + * Extract category from event type + */ + private extractCategory; + /** + * Get styling for different categories + */ + private getCategoryStyle; + /** + * Configure logging for specific categories + */ + setLogConfig(config: { + [key: string]: boolean; + }): void; + /** + * Get current log configuration + */ + getLogConfig(): { + [key: string]: boolean; + }; + /** + * Get event history + */ + getEventLog(eventType?: string): IEventLogEntry[]; + /** + * Enable/disable debug mode + */ + setDebug(enabled: boolean): void; +} +export declare const eventBus: EventBus; diff --git a/wwwroot/js/core/EventBus.js b/wwwroot/js/core/EventBus.js new file mode 100644 index 0000000..07b721e --- /dev/null +++ b/wwwroot/js/core/EventBus.js @@ -0,0 +1,158 @@ +/** + * Central event dispatcher for calendar using DOM CustomEvents + * Provides logging and debugging capabilities + */ +export class EventBus { + constructor() { + this.eventLog = []; + this.debug = false; + this.listeners = new Set(); + // Log configuration for different categories + this.logConfig = { + calendar: true, + grid: true, + event: true, + scroll: true, + navigation: true, + view: true, + default: true + }; + } + /** + * Subscribe to an event via DOM addEventListener + */ + on(eventType, handler, options) { + document.addEventListener(eventType, handler, options); + // Track for cleanup + this.listeners.add({ eventType, handler, options }); + // Return unsubscribe function + return () => this.off(eventType, handler); + } + /** + * Subscribe to an event once + */ + once(eventType, handler) { + return this.on(eventType, handler, { once: true }); + } + /** + * Unsubscribe from an event + */ + off(eventType, handler) { + document.removeEventListener(eventType, handler); + // Remove from tracking + for (const listener of this.listeners) { + if (listener.eventType === eventType && listener.handler === handler) { + this.listeners.delete(listener); + break; + } + } + } + /** + * Emit an event via DOM CustomEvent + */ + emit(eventType, detail = {}) { + // Validate eventType + if (!eventType) { + return false; + } + const event = new CustomEvent(eventType, { + detail: detail ?? {}, + bubbles: true, + cancelable: true + }); + // Log event with grouping + if (this.debug) { + this.logEventWithGrouping(eventType, detail); + } + this.eventLog.push({ + type: eventType, + detail: detail ?? {}, + timestamp: Date.now() + }); + // Emit on document (only DOM events now) + return !document.dispatchEvent(event); + } + /** + * Log event with console grouping + */ + logEventWithGrouping(eventType, detail) { + // Extract category from event type (e.g., 'calendar:datechanged' → 'calendar') + const category = this.extractCategory(eventType); + // Only log if category is enabled + if (!this.logConfig[category]) { + return; + } + // Get category emoji and color + const { emoji, color } = this.getCategoryStyle(category); + // Use collapsed group to reduce visual noise + } + /** + * Extract category from event type + */ + extractCategory(eventType) { + if (!eventType) { + return 'unknown'; + } + if (eventType.includes(':')) { + return eventType.split(':')[0]; + } + // Fallback: try to detect category from event name patterns + const lowerType = eventType.toLowerCase(); + if (lowerType.includes('grid') || lowerType.includes('rendered')) + return 'grid'; + if (lowerType.includes('event') || lowerType.includes('sync')) + return 'event'; + if (lowerType.includes('scroll')) + return 'scroll'; + if (lowerType.includes('nav') || lowerType.includes('date')) + return 'navigation'; + if (lowerType.includes('view')) + return 'view'; + return 'default'; + } + /** + * Get styling for different categories + */ + getCategoryStyle(category) { + const styles = { + calendar: { emoji: '🗓️', color: '#2196F3' }, + grid: { emoji: '📊', color: '#4CAF50' }, + event: { emoji: '📅', color: '#FF9800' }, + scroll: { emoji: '📜', color: '#9C27B0' }, + navigation: { emoji: '🧭', color: '#F44336' }, + view: { emoji: '👁️', color: '#00BCD4' }, + default: { emoji: '📢', color: '#607D8B' } + }; + return styles[category] || styles.default; + } + /** + * Configure logging for specific categories + */ + setLogConfig(config) { + this.logConfig = { ...this.logConfig, ...config }; + } + /** + * Get current log configuration + */ + getLogConfig() { + return { ...this.logConfig }; + } + /** + * Get event history + */ + getEventLog(eventType) { + if (eventType) { + return this.eventLog.filter(e => e.type === eventType); + } + return this.eventLog; + } + /** + * Enable/disable debug mode + */ + setDebug(enabled) { + this.debug = enabled; + } +} +// Create singleton instance +export const eventBus = new EventBus(); +//# sourceMappingURL=EventBus.js.map \ No newline at end of file diff --git a/wwwroot/js/core/EventBus.js.map b/wwwroot/js/core/EventBus.js.map new file mode 100644 index 0000000..36bb9bc --- /dev/null +++ b/wwwroot/js/core/EventBus.js.map @@ -0,0 +1 @@ +{"version":3,"file":"EventBus.js","sourceRoot":"","sources":["../../../src/core/EventBus.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/datasources/DateColumnDataSource.d.ts b/wwwroot/js/datasources/DateColumnDataSource.d.ts new file mode 100644 index 0000000..3807ff2 --- /dev/null +++ b/wwwroot/js/datasources/DateColumnDataSource.d.ts @@ -0,0 +1,55 @@ +import { IColumnDataSource } from '../types/ColumnDataSource'; +import { DateService } from '../utils/DateService'; +import { Configuration } from '../configurations/CalendarConfig'; +import { CalendarView } from '../types/CalendarTypes'; +/** + * DateColumnDataSource - Provides date-based columns + * + * Calculates which dates to display based on: + * - Current date + * - Current view (day/week/month) + * - Workweek settings + */ +export declare class DateColumnDataSource implements IColumnDataSource { + private dateService; + private config; + private currentDate; + private currentView; + constructor(dateService: DateService, config: Configuration, currentDate: Date, currentView: CalendarView); + /** + * Get columns (dates) to display + */ + getColumns(): Date[]; + /** + * Get type of datasource + */ + getType(): 'date' | 'resource'; + /** + * Update current date + */ + setCurrentDate(date: Date): void; + /** + * Update current view + */ + setCurrentView(view: CalendarView): void; + /** + * Get dates for week view based on workweek settings + */ + private getWeekDates; + /** + * Get all dates in current month + */ + private getMonthDates; + /** + * Get ISO week start (Monday) + */ + private getISOWeekStart; + /** + * Get month start + */ + private getMonthStart; + /** + * Get month end + */ + private getMonthEnd; +} diff --git a/wwwroot/js/datasources/DateColumnDataSource.js b/wwwroot/js/datasources/DateColumnDataSource.js new file mode 100644 index 0000000..eefe010 --- /dev/null +++ b/wwwroot/js/datasources/DateColumnDataSource.js @@ -0,0 +1,94 @@ +/** + * DateColumnDataSource - Provides date-based columns + * + * Calculates which dates to display based on: + * - Current date + * - Current view (day/week/month) + * - Workweek settings + */ +export class DateColumnDataSource { + constructor(dateService, config, currentDate, currentView) { + this.dateService = dateService; + this.config = config; + this.currentDate = currentDate; + this.currentView = currentView; + } + /** + * Get columns (dates) to display + */ + getColumns() { + switch (this.currentView) { + case 'week': + return this.getWeekDates(); + case 'month': + return this.getMonthDates(); + case 'day': + return [this.currentDate]; + default: + return this.getWeekDates(); + } + } + /** + * Get type of datasource + */ + getType() { + return 'date'; + } + /** + * Update current date + */ + setCurrentDate(date) { + this.currentDate = date; + } + /** + * Update current view + */ + setCurrentView(view) { + this.currentView = view; + } + /** + * Get dates for week view based on workweek settings + */ + getWeekDates() { + const weekStart = this.getISOWeekStart(this.currentDate); + const workWeekSettings = this.config.getWorkWeekSettings(); + return this.dateService.getWorkWeekDates(weekStart, workWeekSettings.workDays); + } + /** + * Get all dates in current month + */ + getMonthDates() { + const dates = []; + const monthStart = this.getMonthStart(this.currentDate); + const monthEnd = this.getMonthEnd(this.currentDate); + const totalDays = Math.ceil((monthEnd.getTime() - monthStart.getTime()) / (1000 * 60 * 60 * 24)) + 1; + for (let i = 0; i < totalDays; i++) { + dates.push(this.dateService.addDays(monthStart, i)); + } + return dates; + } + /** + * Get ISO week start (Monday) + */ + getISOWeekStart(date) { + const weekBounds = this.dateService.getWeekBounds(date); + return this.dateService.startOfDay(weekBounds.start); + } + /** + * Get month start + */ + getMonthStart(date) { + const year = date.getFullYear(); + const month = date.getMonth(); + return this.dateService.startOfDay(new Date(year, month, 1)); + } + /** + * Get month end + */ + getMonthEnd(date) { + const nextMonth = this.dateService.addMonths(date, 1); + const firstOfNextMonth = this.getMonthStart(nextMonth); + return this.dateService.endOfDay(this.dateService.addDays(firstOfNextMonth, -1)); + } +} +//# sourceMappingURL=DateColumnDataSource.js.map \ No newline at end of file diff --git a/wwwroot/js/datasources/DateColumnDataSource.js.map b/wwwroot/js/datasources/DateColumnDataSource.js.map new file mode 100644 index 0000000..b40c024 --- /dev/null +++ b/wwwroot/js/datasources/DateColumnDataSource.js.map @@ -0,0 +1 @@ +{"version":3,"file":"DateColumnDataSource.js","sourceRoot":"","sources":["../../../src/datasources/DateColumnDataSource.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/demo.js b/wwwroot/js/demo.js new file mode 100644 index 0000000..2ab50eb --- /dev/null +++ b/wwwroot/js/demo.js @@ -0,0 +1,6489 @@ +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); +var __commonJS = (cb, mod) => function __require() { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); + +// node_modules/dayjs/dayjs.min.js +var require_dayjs_min = __commonJS({ + "node_modules/dayjs/dayjs.min.js"(exports, module) { + !function(t, e) { + "object" == typeof exports && "undefined" != typeof module ? module.exports = e() : "function" == typeof define && define.amd ? define(e) : (t = "undefined" != typeof globalThis ? globalThis : t || self).dayjs = e(); + }(exports, function() { + "use strict"; + var t = 1e3, e = 6e4, n = 36e5, r = "millisecond", i = "second", s = "minute", u = "hour", a = "day", o = "week", c = "month", f = "quarter", h = "year", d = "date", l = "Invalid Date", $ = /^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/, y = /\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g, M = { name: "en", weekdays: "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"), months: "January_February_March_April_May_June_July_August_September_October_November_December".split("_"), ordinal: function(t2) { + var e2 = ["th", "st", "nd", "rd"], n2 = t2 % 100; + return "[" + t2 + (e2[(n2 - 20) % 10] || e2[n2] || e2[0]) + "]"; + } }, m = /* @__PURE__ */ __name(function(t2, e2, n2) { + var r2 = String(t2); + return !r2 || r2.length >= e2 ? t2 : "" + Array(e2 + 1 - r2.length).join(n2) + t2; + }, "m"), v = { s: m, z: function(t2) { + var e2 = -t2.utcOffset(), n2 = Math.abs(e2), r2 = Math.floor(n2 / 60), i2 = n2 % 60; + return (e2 <= 0 ? "+" : "-") + m(r2, 2, "0") + ":" + m(i2, 2, "0"); + }, m: /* @__PURE__ */ __name(function t2(e2, n2) { + if (e2.date() < n2.date()) + return -t2(n2, e2); + var r2 = 12 * (n2.year() - e2.year()) + (n2.month() - e2.month()), i2 = e2.clone().add(r2, c), s2 = n2 - i2 < 0, u2 = e2.clone().add(r2 + (s2 ? -1 : 1), c); + return +(-(r2 + (n2 - i2) / (s2 ? i2 - u2 : u2 - i2)) || 0); + }, "t"), a: function(t2) { + return t2 < 0 ? Math.ceil(t2) || 0 : Math.floor(t2); + }, p: function(t2) { + return { M: c, y: h, w: o, d: a, D: d, h: u, m: s, s: i, ms: r, Q: f }[t2] || String(t2 || "").toLowerCase().replace(/s$/, ""); + }, u: function(t2) { + return void 0 === t2; + } }, g = "en", D = {}; + D[g] = M; + var p = "$isDayjsObject", S = /* @__PURE__ */ __name(function(t2) { + return t2 instanceof _ || !(!t2 || !t2[p]); + }, "S"), w = /* @__PURE__ */ __name(function t2(e2, n2, r2) { + var i2; + if (!e2) + return g; + if ("string" == typeof e2) { + var s2 = e2.toLowerCase(); + D[s2] && (i2 = s2), n2 && (D[s2] = n2, i2 = s2); + var u2 = e2.split("-"); + if (!i2 && u2.length > 1) + return t2(u2[0]); + } else { + var a2 = e2.name; + D[a2] = e2, i2 = a2; + } + return !r2 && i2 && (g = i2), i2 || !r2 && g; + }, "t"), O = /* @__PURE__ */ __name(function(t2, e2) { + if (S(t2)) + return t2.clone(); + var n2 = "object" == typeof e2 ? e2 : {}; + return n2.date = t2, n2.args = arguments, new _(n2); + }, "O"), b = v; + b.l = w, b.i = S, b.w = function(t2, e2) { + return O(t2, { locale: e2.$L, utc: e2.$u, x: e2.$x, $offset: e2.$offset }); + }; + var _ = function() { + function M2(t2) { + this.$L = w(t2.locale, null, true), this.parse(t2), this.$x = this.$x || t2.x || {}, this[p] = true; + } + __name(M2, "M"); + var m2 = M2.prototype; + return m2.parse = function(t2) { + this.$d = function(t3) { + var e2 = t3.date, n2 = t3.utc; + if (null === e2) + return /* @__PURE__ */ new Date(NaN); + if (b.u(e2)) + return /* @__PURE__ */ new Date(); + if (e2 instanceof Date) + return new Date(e2); + if ("string" == typeof e2 && !/Z$/i.test(e2)) { + var r2 = e2.match($); + if (r2) { + var i2 = r2[2] - 1 || 0, s2 = (r2[7] || "0").substring(0, 3); + return n2 ? new Date(Date.UTC(r2[1], i2, r2[3] || 1, r2[4] || 0, r2[5] || 0, r2[6] || 0, s2)) : new Date(r2[1], i2, r2[3] || 1, r2[4] || 0, r2[5] || 0, r2[6] || 0, s2); + } + } + return new Date(e2); + }(t2), this.init(); + }, m2.init = function() { + var t2 = this.$d; + this.$y = t2.getFullYear(), this.$M = t2.getMonth(), this.$D = t2.getDate(), this.$W = t2.getDay(), this.$H = t2.getHours(), this.$m = t2.getMinutes(), this.$s = t2.getSeconds(), this.$ms = t2.getMilliseconds(); + }, m2.$utils = function() { + return b; + }, m2.isValid = function() { + return !(this.$d.toString() === l); + }, m2.isSame = function(t2, e2) { + var n2 = O(t2); + return this.startOf(e2) <= n2 && n2 <= this.endOf(e2); + }, m2.isAfter = function(t2, e2) { + return O(t2) < this.startOf(e2); + }, m2.isBefore = function(t2, e2) { + return this.endOf(e2) < O(t2); + }, m2.$g = function(t2, e2, n2) { + return b.u(t2) ? this[e2] : this.set(n2, t2); + }, m2.unix = function() { + return Math.floor(this.valueOf() / 1e3); + }, m2.valueOf = function() { + return this.$d.getTime(); + }, m2.startOf = function(t2, e2) { + var n2 = this, r2 = !!b.u(e2) || e2, f2 = b.p(t2), l2 = /* @__PURE__ */ __name(function(t3, e3) { + var i2 = b.w(n2.$u ? Date.UTC(n2.$y, e3, t3) : new Date(n2.$y, e3, t3), n2); + return r2 ? i2 : i2.endOf(a); + }, "l"), $2 = /* @__PURE__ */ __name(function(t3, e3) { + return b.w(n2.toDate()[t3].apply(n2.toDate("s"), (r2 ? [0, 0, 0, 0] : [23, 59, 59, 999]).slice(e3)), n2); + }, "$"), y2 = this.$W, M3 = this.$M, m3 = this.$D, v2 = "set" + (this.$u ? "UTC" : ""); + switch (f2) { + case h: + return r2 ? l2(1, 0) : l2(31, 11); + case c: + return r2 ? l2(1, M3) : l2(0, M3 + 1); + case o: + var g2 = this.$locale().weekStart || 0, D2 = (y2 < g2 ? y2 + 7 : y2) - g2; + return l2(r2 ? m3 - D2 : m3 + (6 - D2), M3); + case a: + case d: + return $2(v2 + "Hours", 0); + case u: + return $2(v2 + "Minutes", 1); + case s: + return $2(v2 + "Seconds", 2); + case i: + return $2(v2 + "Milliseconds", 3); + default: + return this.clone(); + } + }, m2.endOf = function(t2) { + return this.startOf(t2, false); + }, m2.$set = function(t2, e2) { + var n2, o2 = b.p(t2), f2 = "set" + (this.$u ? "UTC" : ""), l2 = (n2 = {}, n2[a] = f2 + "Date", n2[d] = f2 + "Date", n2[c] = f2 + "Month", n2[h] = f2 + "FullYear", n2[u] = f2 + "Hours", n2[s] = f2 + "Minutes", n2[i] = f2 + "Seconds", n2[r] = f2 + "Milliseconds", n2)[o2], $2 = o2 === a ? this.$D + (e2 - this.$W) : e2; + if (o2 === c || o2 === h) { + var y2 = this.clone().set(d, 1); + y2.$d[l2]($2), y2.init(), this.$d = y2.set(d, Math.min(this.$D, y2.daysInMonth())).$d; + } else + l2 && this.$d[l2]($2); + return this.init(), this; + }, m2.set = function(t2, e2) { + return this.clone().$set(t2, e2); + }, m2.get = function(t2) { + return this[b.p(t2)](); + }, m2.add = function(r2, f2) { + var d2, l2 = this; + r2 = Number(r2); + var $2 = b.p(f2), y2 = /* @__PURE__ */ __name(function(t2) { + var e2 = O(l2); + return b.w(e2.date(e2.date() + Math.round(t2 * r2)), l2); + }, "y"); + if ($2 === c) + return this.set(c, this.$M + r2); + if ($2 === h) + return this.set(h, this.$y + r2); + if ($2 === a) + return y2(1); + if ($2 === o) + return y2(7); + var M3 = (d2 = {}, d2[s] = e, d2[u] = n, d2[i] = t, d2)[$2] || 1, m3 = this.$d.getTime() + r2 * M3; + return b.w(m3, this); + }, m2.subtract = function(t2, e2) { + return this.add(-1 * t2, e2); + }, m2.format = function(t2) { + var e2 = this, n2 = this.$locale(); + if (!this.isValid()) + return n2.invalidDate || l; + var r2 = t2 || "YYYY-MM-DDTHH:mm:ssZ", i2 = b.z(this), s2 = this.$H, u2 = this.$m, a2 = this.$M, o2 = n2.weekdays, c2 = n2.months, f2 = n2.meridiem, h2 = /* @__PURE__ */ __name(function(t3, n3, i3, s3) { + return t3 && (t3[n3] || t3(e2, r2)) || i3[n3].slice(0, s3); + }, "h"), d2 = /* @__PURE__ */ __name(function(t3) { + return b.s(s2 % 12 || 12, t3, "0"); + }, "d"), $2 = f2 || function(t3, e3, n3) { + var r3 = t3 < 12 ? "AM" : "PM"; + return n3 ? r3.toLowerCase() : r3; + }; + return r2.replace(y, function(t3, r3) { + return r3 || function(t4) { + switch (t4) { + case "YY": + return String(e2.$y).slice(-2); + case "YYYY": + return b.s(e2.$y, 4, "0"); + case "M": + return a2 + 1; + case "MM": + return b.s(a2 + 1, 2, "0"); + case "MMM": + return h2(n2.monthsShort, a2, c2, 3); + case "MMMM": + return h2(c2, a2); + case "D": + return e2.$D; + case "DD": + return b.s(e2.$D, 2, "0"); + case "d": + return String(e2.$W); + case "dd": + return h2(n2.weekdaysMin, e2.$W, o2, 2); + case "ddd": + return h2(n2.weekdaysShort, e2.$W, o2, 3); + case "dddd": + return o2[e2.$W]; + case "H": + return String(s2); + case "HH": + return b.s(s2, 2, "0"); + case "h": + return d2(1); + case "hh": + return d2(2); + case "a": + return $2(s2, u2, true); + case "A": + return $2(s2, u2, false); + case "m": + return String(u2); + case "mm": + return b.s(u2, 2, "0"); + case "s": + return String(e2.$s); + case "ss": + return b.s(e2.$s, 2, "0"); + case "SSS": + return b.s(e2.$ms, 3, "0"); + case "Z": + return i2; + } + return null; + }(t3) || i2.replace(":", ""); + }); + }, m2.utcOffset = function() { + return 15 * -Math.round(this.$d.getTimezoneOffset() / 15); + }, m2.diff = function(r2, d2, l2) { + var $2, y2 = this, M3 = b.p(d2), m3 = O(r2), v2 = (m3.utcOffset() - this.utcOffset()) * e, g2 = this - m3, D2 = /* @__PURE__ */ __name(function() { + return b.m(y2, m3); + }, "D"); + switch (M3) { + case h: + $2 = D2() / 12; + break; + case c: + $2 = D2(); + break; + case f: + $2 = D2() / 3; + break; + case o: + $2 = (g2 - v2) / 6048e5; + break; + case a: + $2 = (g2 - v2) / 864e5; + break; + case u: + $2 = g2 / n; + break; + case s: + $2 = g2 / e; + break; + case i: + $2 = g2 / t; + break; + default: + $2 = g2; + } + return l2 ? $2 : b.a($2); + }, m2.daysInMonth = function() { + return this.endOf(c).$D; + }, m2.$locale = function() { + return D[this.$L]; + }, m2.locale = function(t2, e2) { + if (!t2) + return this.$L; + var n2 = this.clone(), r2 = w(t2, e2, true); + return r2 && (n2.$L = r2), n2; + }, m2.clone = function() { + return b.w(this.$d, this); + }, m2.toDate = function() { + return new Date(this.valueOf()); + }, m2.toJSON = function() { + return this.isValid() ? this.toISOString() : null; + }, m2.toISOString = function() { + return this.$d.toISOString(); + }, m2.toString = function() { + return this.$d.toUTCString(); + }, M2; + }(), k = _.prototype; + return O.prototype = k, [["$ms", r], ["$s", i], ["$m", s], ["$H", u], ["$W", a], ["$M", c], ["$y", h], ["$D", d]].forEach(function(t2) { + k[t2[1]] = function(e2) { + return this.$g(e2, t2[0], t2[1]); + }; + }), O.extend = function(t2, e2) { + return t2.$i || (t2(e2, _, O), t2.$i = true), O; + }, O.locale = w, O.isDayjs = S, O.unix = function(t2) { + return O(1e3 * t2); + }, O.en = D[g], O.Ls = D, O.p = {}, O; + }); + } +}); + +// node_modules/dayjs/plugin/utc.js +var require_utc = __commonJS({ + "node_modules/dayjs/plugin/utc.js"(exports, module) { + !function(t, i) { + "object" == typeof exports && "undefined" != typeof module ? module.exports = i() : "function" == typeof define && define.amd ? define(i) : (t = "undefined" != typeof globalThis ? globalThis : t || self).dayjs_plugin_utc = i(); + }(exports, function() { + "use strict"; + var t = "minute", i = /[+-]\d\d(?::?\d\d)?/g, e = /([+-]|\d\d)/g; + return function(s, f, n) { + var u = f.prototype; + n.utc = function(t2) { + var i2 = { date: t2, utc: true, args: arguments }; + return new f(i2); + }, u.utc = function(i2) { + var e2 = n(this.toDate(), { locale: this.$L, utc: true }); + return i2 ? e2.add(this.utcOffset(), t) : e2; + }, u.local = function() { + return n(this.toDate(), { locale: this.$L, utc: false }); + }; + var r = u.parse; + u.parse = function(t2) { + t2.utc && (this.$u = true), this.$utils().u(t2.$offset) || (this.$offset = t2.$offset), r.call(this, t2); + }; + var o = u.init; + u.init = function() { + if (this.$u) { + var t2 = this.$d; + this.$y = t2.getUTCFullYear(), this.$M = t2.getUTCMonth(), this.$D = t2.getUTCDate(), this.$W = t2.getUTCDay(), this.$H = t2.getUTCHours(), this.$m = t2.getUTCMinutes(), this.$s = t2.getUTCSeconds(), this.$ms = t2.getUTCMilliseconds(); + } else + o.call(this); + }; + var a = u.utcOffset; + u.utcOffset = function(s2, f2) { + var n2 = this.$utils().u; + if (n2(s2)) + return this.$u ? 0 : n2(this.$offset) ? a.call(this) : this.$offset; + if ("string" == typeof s2 && (s2 = function(t2) { + void 0 === t2 && (t2 = ""); + var s3 = t2.match(i); + if (!s3) + return null; + var f3 = ("" + s3[0]).match(e) || ["-", 0, 0], n3 = f3[0], u3 = 60 * +f3[1] + +f3[2]; + return 0 === u3 ? 0 : "+" === n3 ? u3 : -u3; + }(s2), null === s2)) + return this; + var u2 = Math.abs(s2) <= 16 ? 60 * s2 : s2; + if (0 === u2) + return this.utc(f2); + var r2 = this.clone(); + if (f2) + return r2.$offset = u2, r2.$u = false, r2; + var o2 = this.$u ? this.toDate().getTimezoneOffset() : -1 * this.utcOffset(); + return (r2 = this.local().add(u2 + o2, t)).$offset = u2, r2.$x.$localOffset = o2, r2; + }; + var h = u.format; + u.format = function(t2) { + var i2 = t2 || (this.$u ? "YYYY-MM-DDTHH:mm:ss[Z]" : ""); + return h.call(this, i2); + }, u.valueOf = function() { + var t2 = this.$utils().u(this.$offset) ? 0 : this.$offset + (this.$x.$localOffset || this.$d.getTimezoneOffset()); + return this.$d.valueOf() - 6e4 * t2; + }, u.isUTC = function() { + return !!this.$u; + }, u.toISOString = function() { + return this.toDate().toISOString(); + }, u.toString = function() { + return this.toDate().toUTCString(); + }; + var l = u.toDate; + u.toDate = function(t2) { + return "s" === t2 && this.$offset ? n(this.format("YYYY-MM-DD HH:mm:ss:SSS")).toDate() : l.call(this); + }; + var c = u.diff; + u.diff = function(t2, i2, e2) { + if (t2 && this.$u === t2.$u) + return c.call(this, t2, i2, e2); + var s2 = this.local(), f2 = n(t2).local(); + return c.call(s2, f2, i2, e2); + }; + }; + }); + } +}); + +// node_modules/dayjs/plugin/timezone.js +var require_timezone = __commonJS({ + "node_modules/dayjs/plugin/timezone.js"(exports, module) { + !function(t, e) { + "object" == typeof exports && "undefined" != typeof module ? module.exports = e() : "function" == typeof define && define.amd ? define(e) : (t = "undefined" != typeof globalThis ? globalThis : t || self).dayjs_plugin_timezone = e(); + }(exports, function() { + "use strict"; + var t = { year: 0, month: 1, day: 2, hour: 3, minute: 4, second: 5 }, e = {}; + return function(n, i, o) { + var r, a = /* @__PURE__ */ __name(function(t2, n2, i2) { + void 0 === i2 && (i2 = {}); + var o2 = new Date(t2), r2 = function(t3, n3) { + void 0 === n3 && (n3 = {}); + var i3 = n3.timeZoneName || "short", o3 = t3 + "|" + i3, r3 = e[o3]; + return r3 || (r3 = new Intl.DateTimeFormat("en-US", { hour12: false, timeZone: t3, year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit", timeZoneName: i3 }), e[o3] = r3), r3; + }(n2, i2); + return r2.formatToParts(o2); + }, "a"), u = /* @__PURE__ */ __name(function(e2, n2) { + for (var i2 = a(e2, n2), r2 = [], u2 = 0; u2 < i2.length; u2 += 1) { + var f2 = i2[u2], s2 = f2.type, m = f2.value, c = t[s2]; + c >= 0 && (r2[c] = parseInt(m, 10)); + } + var d = r2[3], l = 24 === d ? 0 : d, h = r2[0] + "-" + r2[1] + "-" + r2[2] + " " + l + ":" + r2[4] + ":" + r2[5] + ":000", v = +e2; + return (o.utc(h).valueOf() - (v -= v % 1e3)) / 6e4; + }, "u"), f = i.prototype; + f.tz = function(t2, e2) { + void 0 === t2 && (t2 = r); + var n2, i2 = this.utcOffset(), a2 = this.toDate(), u2 = a2.toLocaleString("en-US", { timeZone: t2 }), f2 = Math.round((a2 - new Date(u2)) / 1e3 / 60), s2 = 15 * -Math.round(a2.getTimezoneOffset() / 15) - f2; + if (!Number(s2)) + n2 = this.utcOffset(0, e2); + else if (n2 = o(u2, { locale: this.$L }).$set("millisecond", this.$ms).utcOffset(s2, true), e2) { + var m = n2.utcOffset(); + n2 = n2.add(i2 - m, "minute"); + } + return n2.$x.$timezone = t2, n2; + }, f.offsetName = function(t2) { + var e2 = this.$x.$timezone || o.tz.guess(), n2 = a(this.valueOf(), e2, { timeZoneName: t2 }).find(function(t3) { + return "timezonename" === t3.type.toLowerCase(); + }); + return n2 && n2.value; + }; + var s = f.startOf; + f.startOf = function(t2, e2) { + if (!this.$x || !this.$x.$timezone) + return s.call(this, t2, e2); + var n2 = o(this.format("YYYY-MM-DD HH:mm:ss:SSS"), { locale: this.$L }); + return s.call(n2, t2, e2).tz(this.$x.$timezone, true); + }, o.tz = function(t2, e2, n2) { + var i2 = n2 && e2, a2 = n2 || e2 || r, f2 = u(+o(), a2); + if ("string" != typeof t2) + return o(t2).tz(a2); + var s2 = function(t3, e3, n3) { + var i3 = t3 - 60 * e3 * 1e3, o2 = u(i3, n3); + if (e3 === o2) + return [i3, e3]; + var r2 = u(i3 -= 60 * (o2 - e3) * 1e3, n3); + return o2 === r2 ? [i3, o2] : [t3 - 60 * Math.min(o2, r2) * 1e3, Math.max(o2, r2)]; + }(o.utc(t2, i2).valueOf(), f2, a2), m = s2[0], c = s2[1], d = o(m).utcOffset(c); + return d.$x.$timezone = a2, d; + }, o.tz.guess = function() { + return Intl.DateTimeFormat().resolvedOptions().timeZone; + }, o.tz.setDefault = function(t2) { + r = t2; + }; + }; + }); + } +}); + +// node_modules/dayjs/plugin/isoWeek.js +var require_isoWeek = __commonJS({ + "node_modules/dayjs/plugin/isoWeek.js"(exports, module) { + !function(e, t) { + "object" == typeof exports && "undefined" != typeof module ? module.exports = t() : "function" == typeof define && define.amd ? define(t) : (e = "undefined" != typeof globalThis ? globalThis : e || self).dayjs_plugin_isoWeek = t(); + }(exports, function() { + "use strict"; + var e = "day"; + return function(t, i, s) { + var a = /* @__PURE__ */ __name(function(t2) { + return t2.add(4 - t2.isoWeekday(), e); + }, "a"), d = i.prototype; + d.isoWeekYear = function() { + return a(this).year(); + }, d.isoWeek = function(t2) { + if (!this.$utils().u(t2)) + return this.add(7 * (t2 - this.isoWeek()), e); + var i2, d2, n2, o, r = a(this), u = (i2 = this.isoWeekYear(), d2 = this.$u, n2 = (d2 ? s.utc : s)().year(i2).startOf("year"), o = 4 - n2.isoWeekday(), n2.isoWeekday() > 4 && (o += 7), n2.add(o, e)); + return r.diff(u, "week") + 1; + }, d.isoWeekday = function(e2) { + return this.$utils().u(e2) ? this.day() || 7 : this.day(this.day() % 7 ? e2 : e2 - 7); + }; + var n = d.startOf; + d.startOf = function(e2, t2) { + var i2 = this.$utils(), s2 = !!i2.u(t2) || t2; + return "isoweek" === i2.p(e2) ? s2 ? this.date(this.date() - (this.isoWeekday() - 1)).startOf("day") : this.date(this.date() - 1 - (this.isoWeekday() - 1) + 7).endOf("day") : n.bind(this)(e2, t2); + }; + }; + }); + } +}); + +// node_modules/@novadi/core/dist/token.js +var tokenCounter = 0; +function Token(description) { + const id = ++tokenCounter; + const sym = Symbol(description ? `Token(${description})` : `Token#${id}`); + const token2 = { + symbol: sym, + description, + toString() { + return description ? `Token<${description}>` : `Token<#${id}>`; + } + }; + return token2; +} +__name(Token, "Token"); + +// node_modules/@novadi/core/dist/errors.js +var _ContainerError = class _ContainerError extends Error { + constructor(message) { + super(message); + this.name = "ContainerError"; + } +}; +__name(_ContainerError, "ContainerError"); +var ContainerError = _ContainerError; +var _BindingNotFoundError = class _BindingNotFoundError extends ContainerError { + constructor(tokenDescription, path = []) { + const pathStr = path.length > 0 ? ` + Dependency path: ${path.join(" -> ")}` : ""; + super(`Token "${tokenDescription}" is not bound or registered in the container.${pathStr}`); + this.name = "BindingNotFoundError"; + } +}; +__name(_BindingNotFoundError, "BindingNotFoundError"); +var BindingNotFoundError = _BindingNotFoundError; +var _CircularDependencyError = class _CircularDependencyError extends ContainerError { + constructor(path) { + super(`Circular dependency detected: ${path.join(" -> ")}`); + this.name = "CircularDependencyError"; + } +}; +__name(_CircularDependencyError, "CircularDependencyError"); +var CircularDependencyError = _CircularDependencyError; + +// node_modules/@novadi/core/dist/autowire.js +var paramNameCache = /* @__PURE__ */ new WeakMap(); +function extractParameterNames(constructor) { + const cached = paramNameCache.get(constructor); + if (cached) { + return cached; + } + const fnStr = constructor.toString(); + const match = fnStr.match(/constructor\s*\(([^)]*)\)/) || fnStr.match(/^[^(]*\(([^)]*)\)/); + if (!match || !match[1]) { + return []; + } + const params = match[1].split(",").map((param) => param.trim()).filter((param) => param.length > 0).map((param) => { + let name = param.split(/[:=]/)[0].trim(); + name = name.replace(/^((public|private|protected|readonly)\s+)+/, ""); + if (name.includes("{") || name.includes("[")) { + return null; + } + return name; + }).filter((name) => name !== null); + paramNameCache.set(constructor, params); + return params; +} +__name(extractParameterNames, "extractParameterNames"); +function resolveByMap(constructor, container2, options) { + if (!options.map) { + throw new Error("AutoWire map strategy requires options.map to be defined"); + } + const paramNames = extractParameterNames(constructor); + const resolvedDeps = []; + for (const paramName of paramNames) { + const resolver = options.map[paramName]; + if (resolver === void 0) { + if (options.strict) { + throw new Error(`Cannot resolve parameter "${paramName}" on ${constructor.name}. Not found in autowire map. Add it to the map: .autoWire({ map: { ${paramName}: ... } })`); + } else { + resolvedDeps.push(void 0); + } + continue; + } + if (typeof resolver === "function") { + resolvedDeps.push(resolver(container2)); + } else { + resolvedDeps.push(container2.resolve(resolver)); + } + } + return resolvedDeps; +} +__name(resolveByMap, "resolveByMap"); +function resolveByMapResolvers(_constructor, container2, options) { + if (!options.mapResolvers || options.mapResolvers.length === 0) { + return []; + } + const resolvedDeps = []; + for (let i = 0; i < options.mapResolvers.length; i++) { + const resolver = options.mapResolvers[i]; + if (resolver === void 0) { + resolvedDeps.push(void 0); + } else if (typeof resolver === "function") { + resolvedDeps.push(resolver(container2)); + } else { + resolvedDeps.push(container2.resolve(resolver)); + } + } + return resolvedDeps; +} +__name(resolveByMapResolvers, "resolveByMapResolvers"); +function autowire(constructor, container2, options) { + const opts = { + by: "paramName", + strict: false, + ...options + }; + if (opts.mapResolvers && opts.mapResolvers.length > 0) { + return resolveByMapResolvers(constructor, container2, opts); + } + if (opts.map && Object.keys(opts.map).length > 0) { + return resolveByMap(constructor, container2, opts); + } + return []; +} +__name(autowire, "autowire"); + +// node_modules/@novadi/core/dist/builder.js +var _RegistrationBuilder = class _RegistrationBuilder { + constructor(pending, registrations) { + this.registrations = registrations; + this.configs = []; + this.defaultLifetime = "singleton"; + this.pending = pending; + } + /** + * Bind this registration to a token or interface type + * + * @overload + * @param {Token} token - Explicit token for binding + * + * @overload + * @param {string} typeName - Interface type name (auto-generated by transformer) + */ + as(tokenOrTypeName) { + if (tokenOrTypeName && typeof tokenOrTypeName === "object" && "symbol" in tokenOrTypeName) { + const config = { + token: tokenOrTypeName, + type: this.pending.type, + value: this.pending.value, + factory: this.pending.factory, + constructor: this.pending.constructor, + lifetime: this.defaultLifetime + }; + this.configs.push(config); + this.registrations.push(config); + return this; + } else { + const config = { + token: null, + // Will be set during build() + type: this.pending.type, + value: this.pending.value, + factory: this.pending.factory, + constructor: this.pending.constructor, + lifetime: this.defaultLifetime, + interfaceType: tokenOrTypeName + }; + this.configs.push(config); + this.registrations.push(config); + return this; + } + } + /** + * Register as default implementation for an interface + * Combines as() + asDefault() + */ + asDefaultInterface(typeName) { + this.as("TInterface", typeName); + return this.asDefault(); + } + /** + * Register as a keyed interface implementation + * Combines as() + keyed() + */ + asKeyedInterface(key, typeName) { + this.as("TInterface", typeName); + return this.keyed(key); + } + /** + * Register as multiple implemented interfaces + */ + asImplementedInterfaces(tokens) { + if (tokens.length === 0) { + return this; + } + if (this.configs.length > 0) { + for (const config of this.configs) { + config.lifetime = "singleton"; + config.additionalTokens = config.additionalTokens || []; + config.additionalTokens.push(...tokens); + } + return this; + } + const firstConfig = { + token: tokens[0], + type: this.pending.type, + value: this.pending.value, + factory: this.pending.factory, + constructor: this.pending.constructor, + lifetime: "singleton" + }; + this.configs.push(firstConfig); + this.registrations.push(firstConfig); + for (let i = 1; i < tokens.length; i++) { + firstConfig.additionalTokens = firstConfig.additionalTokens || []; + firstConfig.additionalTokens.push(tokens[i]); + } + return this; + } + /** + * Set singleton lifetime (one instance for entire container) + */ + singleInstance() { + for (const config of this.configs) { + config.lifetime = "singleton"; + } + return this; + } + /** + * Set per-request lifetime (one instance per resolve call tree) + */ + instancePerRequest() { + for (const config of this.configs) { + config.lifetime = "per-request"; + } + return this; + } + /** + * Set transient lifetime (new instance every time) + * Alias for default behavior + */ + instancePerDependency() { + for (const config of this.configs) { + config.lifetime = "transient"; + } + return this; + } + /** + * Name this registration for named resolution + */ + named(name) { + for (const config of this.configs) { + config.name = name; + } + return this; + } + /** + * Key this registration for keyed resolution + */ + keyed(key) { + for (const config of this.configs) { + config.key = key; + } + return this; + } + /** + * Mark this as default registration + * Default registrations don't override existing ones + */ + asDefault() { + for (const config of this.configs) { + config.isDefault = true; + } + return this; + } + /** + * Only register if token not already registered + */ + ifNotRegistered() { + for (const config of this.configs) { + config.ifNotRegistered = true; + } + return this; + } + /** + * Specify parameter values for constructor (primitives and constants) + * Use this for non-DI parameters like strings, numbers, config values + */ + withParameters(parameters) { + for (const config of this.configs) { + config.parameterValues = parameters; + } + return this; + } + /** + * Enable automatic dependency injection (autowiring) + * Supports three strategies: paramName (default), map, and class + * + * @example + * ```ts + * // Strategy 1: paramName (default, requires non-minified code in dev) + * builder.registerType(EventBus).as().autoWire() + * + * // Strategy 2: map (minify-safe, explicit) + * builder.registerType(EventBus).as().autoWire({ + * map: { + * logger: (c) => c.resolveType() + * } + * }) + * + * // Strategy 3: class (requires build-time codegen) + * builder.registerType(EventBus).as().autoWire({ by: 'class' }) + * ``` + */ + autoWire(options) { + for (const config of this.configs) { + config.autowireOptions = options || { by: "paramName", strict: false }; + } + return this; + } +}; +__name(_RegistrationBuilder, "RegistrationBuilder"); +var RegistrationBuilder = _RegistrationBuilder; +var _Builder = class _Builder { + constructor(baseContainer) { + this.baseContainer = baseContainer; + this.registrations = []; + } + /** + * Register a class constructor + */ + registerType(constructor) { + const pending = { + type: "type", + value: null, + constructor + }; + return new RegistrationBuilder(pending, this.registrations); + } + /** + * Register a pre-created instance + */ + registerInstance(instance) { + const pending = { + type: "instance", + value: instance, + constructor: void 0 + }; + return new RegistrationBuilder(pending, this.registrations); + } + /** + * Register a factory function + */ + register(factory) { + const pending = { + type: "factory", + value: null, + factory, + constructor: void 0 + }; + return new RegistrationBuilder(pending, this.registrations); + } + /** + * Register a module (function that adds multiple registrations) + */ + module(moduleFunc) { + moduleFunc(this); + return this; + } + /** + * Resolve interface type names to tokens + * @internal + */ + resolveInterfaceTokens(container2) { + for (const config of this.registrations) { + if (config.interfaceType !== void 0 && !config.token) { + config.token = container2.interfaceToken(config.interfaceType); + } + } + } + /** + * Identify tokens that have non-default registrations + * @internal + */ + identifyNonDefaultTokens() { + const tokensWithNonDefaults = /* @__PURE__ */ new Set(); + for (const config of this.registrations) { + if (!config.isDefault && !config.name && config.key === void 0) { + tokensWithNonDefaults.add(config.token); + } + } + return tokensWithNonDefaults; + } + /** + * Check if registration should be skipped + * @internal + */ + shouldSkipRegistration(config, tokensWithNonDefaults, registeredTokens) { + if (config.isDefault && !config.name && config.key === void 0 && tokensWithNonDefaults.has(config.token)) { + return true; + } + if (config.ifNotRegistered && registeredTokens.has(config.token)) { + return true; + } + if (config.isDefault && registeredTokens.has(config.token)) { + return true; + } + return false; + } + /** + * Create binding token for registration (named, keyed, or multi) + * @internal + */ + createBindingToken(config, namedRegistrations, keyedRegistrations, multiRegistrations) { + if (config.name) { + const bindingToken = Token(`__named_${config.name}`); + namedRegistrations.set(config.name, { ...config, token: bindingToken }); + return bindingToken; + } else if (config.key !== void 0) { + const keyStr = typeof config.key === "symbol" ? config.key.toString() : config.key; + const bindingToken = Token(`__keyed_${keyStr}`); + keyedRegistrations.set(config.key, { ...config, token: bindingToken }); + return bindingToken; + } else { + if (multiRegistrations.has(config.token)) { + const bindingToken = Token(`__multi_${config.token.toString()}_${multiRegistrations.get(config.token).length}`); + multiRegistrations.get(config.token).push(bindingToken); + return bindingToken; + } else { + multiRegistrations.set(config.token, [config.token]); + return config.token; + } + } + } + /** + * Register additional interfaces for a config + * @internal + */ + registerAdditionalInterfaces(container2, config, bindingToken, registeredTokens) { + if (config.additionalTokens) { + for (const additionalToken of config.additionalTokens) { + container2.bindFactory(additionalToken, (c) => c.resolve(bindingToken), { lifetime: config.lifetime }); + registeredTokens.add(additionalToken); + } + } + } + /** + * Build the container with all registered bindings + */ + build() { + const container2 = this.baseContainer.createChild(); + this.resolveInterfaceTokens(container2); + const registeredTokens = /* @__PURE__ */ new Set(); + const namedRegistrations = /* @__PURE__ */ new Map(); + const keyedRegistrations = /* @__PURE__ */ new Map(); + const multiRegistrations = /* @__PURE__ */ new Map(); + const tokensWithNonDefaults = this.identifyNonDefaultTokens(); + for (const config of this.registrations) { + if (this.shouldSkipRegistration(config, tokensWithNonDefaults, registeredTokens)) { + continue; + } + const bindingToken = this.createBindingToken(config, namedRegistrations, keyedRegistrations, multiRegistrations); + this.applyRegistration(container2, { ...config, token: bindingToken }); + registeredTokens.add(config.token); + this.registerAdditionalInterfaces(container2, config, bindingToken, registeredTokens); + } + ; + container2.__namedRegistrations = namedRegistrations; + container2.__keyedRegistrations = keyedRegistrations; + container2.__multiRegistrations = multiRegistrations; + return container2; + } + /** + * Analyze constructor to detect dependencies + * @internal + */ + analyzeConstructor(constructor) { + const constructorStr = constructor.toString(); + const hasDependencies = /constructor\s*\([^)]+\)/.test(constructorStr); + return { hasDependencies }; + } + /** + * Create optimized factory for zero-dependency constructors + * @internal + */ + createOptimizedFactory(container2, config, options) { + if (config.lifetime === "singleton") { + const instance = new config.constructor(); + container2.bindValue(config.token, instance); + } else if (config.lifetime === "transient") { + const ctor = config.constructor; + const fastFactory = /* @__PURE__ */ __name(() => new ctor(), "fastFactory"); + container2.fastTransientCache.set(config.token, fastFactory); + container2.bindFactory(config.token, fastFactory, options); + } else { + const factory = /* @__PURE__ */ __name(() => new config.constructor(), "factory"); + container2.bindFactory(config.token, factory, options); + } + } + /** + * Create autowire factory + * @internal + */ + createAutoWireFactory(container2, config, options) { + const factory = /* @__PURE__ */ __name((c) => { + const resolvedDeps = autowire(config.constructor, c, config.autowireOptions); + return new config.constructor(...resolvedDeps); + }, "factory"); + container2.bindFactory(config.token, factory, options); + } + /** + * Create withParameters factory + * @internal + */ + createParameterFactory(container2, config, options) { + const factory = /* @__PURE__ */ __name(() => { + const values = Object.values(config.parameterValues); + return new config.constructor(...values); + }, "factory"); + container2.bindFactory(config.token, factory, options); + } + /** + * Apply type registration (class constructor) + * @internal + */ + applyTypeRegistration(container2, config, options) { + const { hasDependencies } = this.analyzeConstructor(config.constructor); + if (!hasDependencies && !config.autowireOptions && !config.parameterValues) { + this.createOptimizedFactory(container2, config, options); + return; + } + if (config.autowireOptions) { + this.createAutoWireFactory(container2, config, options); + return; + } + if (config.parameterValues) { + this.createParameterFactory(container2, config, options); + return; + } + if (hasDependencies) { + const className = config.constructor.name || "UnnamedClass"; + throw new Error(`Service "${className}" has constructor dependencies but no autowiring configuration. + +Solutions: + 1. \u2B50 Use the NovaDI transformer (recommended): + - Add "@novadi/core/unplugin" to your build config + - Transformer automatically generates .autoWire() for all dependencies + + 2. Add manual autowiring: + .autoWire({ map: { /* param: resolver */ } }) + + 3. Use a factory function: + .register((c) => new ${className}(...)) + +See docs: https://github.com/janus007/NovaDI#autowire`); + } + const factory = /* @__PURE__ */ __name(() => new config.constructor(), "factory"); + container2.bindFactory(config.token, factory, options); + } + applyRegistration(container2, config) { + const options = { lifetime: config.lifetime }; + switch (config.type) { + case "instance": + container2.bindValue(config.token, config.value); + break; + case "factory": + container2.bindFactory(config.token, config.factory, options); + break; + case "type": + this.applyTypeRegistration(container2, config, options); + break; + } + } +}; +__name(_Builder, "Builder"); +var Builder = _Builder; + +// node_modules/@novadi/core/dist/container.js +function isDisposable(obj) { + return obj && typeof obj.dispose === "function"; +} +__name(isDisposable, "isDisposable"); +var _ResolutionContext = class _ResolutionContext { + constructor() { + this.resolvingStack = /* @__PURE__ */ new Set(); + this.perRequestCache = /* @__PURE__ */ new Map(); + } + isResolving(token2) { + return this.resolvingStack.has(token2); + } + enterResolve(token2) { + this.resolvingStack.add(token2); + } + exitResolve(token2) { + this.resolvingStack.delete(token2); + this.path = void 0; + } + getPath() { + if (!this.path) { + this.path = Array.from(this.resolvingStack).map((t) => t.toString()); + } + return [...this.path]; + } + cachePerRequest(token2, instance) { + this.perRequestCache.set(token2, instance); + } + getPerRequest(token2) { + return this.perRequestCache.get(token2); + } + hasPerRequest(token2) { + return this.perRequestCache.has(token2); + } + /** + * Reset context for reuse in object pool + * Performance: Reusing contexts avoids heap allocations + */ + reset() { + this.resolvingStack.clear(); + this.perRequestCache.clear(); + this.path = void 0; + } +}; +__name(_ResolutionContext, "ResolutionContext"); +var ResolutionContext = _ResolutionContext; +var _ResolutionContextPool = class _ResolutionContextPool { + constructor() { + this.pool = []; + this.maxSize = 10; + } + acquire() { + const context = this.pool.pop(); + if (context) { + context.reset(); + return context; + } + return new ResolutionContext(); + } + release(context) { + if (this.pool.length < this.maxSize) { + this.pool.push(context); + } + } +}; +__name(_ResolutionContextPool, "ResolutionContextPool"); +var ResolutionContextPool = _ResolutionContextPool; +var _Container = class _Container { + constructor(parent) { + this.bindings = /* @__PURE__ */ new Map(); + this.singletonCache = /* @__PURE__ */ new Map(); + this.singletonOrder = []; + this.interfaceRegistry = /* @__PURE__ */ new Map(); + this.interfaceTokenCache = /* @__PURE__ */ new Map(); + this.fastTransientCache = /* @__PURE__ */ new Map(); + this.ultraFastSingletonCache = /* @__PURE__ */ new Map(); + this.parent = parent; + } + /** + * Bind a pre-created value to a token + */ + bindValue(token2, value) { + this.bindings.set(token2, { + type: "value", + lifetime: "singleton", + value, + constructor: void 0 + }); + this.invalidateBindingCache(); + } + /** + * Bind a factory function to a token + */ + bindFactory(token2, factory, options) { + this.bindings.set(token2, { + type: "factory", + lifetime: options?.lifetime || "transient", + factory, + dependencies: options?.dependencies, + constructor: void 0 + }); + this.invalidateBindingCache(); + } + /** + * Bind a class constructor to a token + */ + bindClass(token2, constructor, options) { + const binding = { + type: "class", + lifetime: options?.lifetime || "transient", + constructor, + dependencies: options?.dependencies + }; + this.bindings.set(token2, binding); + this.invalidateBindingCache(); + if (binding.lifetime === "transient" && (!binding.dependencies || binding.dependencies.length === 0)) { + this.fastTransientCache.set(token2, () => new constructor()); + } + } + /** + * Resolve a dependency synchronously + * Performance optimized with multiple fast paths + */ + resolve(token2) { + const cached = this.tryGetFromCaches(token2); + if (cached !== void 0) { + return cached; + } + if (this.currentContext) { + return this.resolveWithContext(token2, this.currentContext); + } + const context = _Container.contextPool.acquire(); + this.currentContext = context; + try { + return this.resolveWithContext(token2, context); + } finally { + this.currentContext = void 0; + _Container.contextPool.release(context); + } + } + /** + * SPECIALIZED: Ultra-fast singleton resolve (no safety checks) + * Use ONLY when you're 100% sure the token is a registered singleton + * @internal For performance-critical paths only + */ + resolveSingletonUnsafe(token2) { + return this.ultraFastSingletonCache.get(token2) ?? this.singletonCache.get(token2); + } + /** + * SPECIALIZED: Fast transient resolve for zero-dependency classes + * Skips all context creation and circular dependency checks + * @internal For performance-critical paths only + */ + resolveTransientSimple(token2) { + const factory = this.fastTransientCache.get(token2); + if (factory) { + return factory(); + } + return this.resolve(token2); + } + /** + * SPECIALIZED: Batch resolve multiple dependencies at once + * More efficient than multiple individual resolves + */ + resolveBatch(tokens) { + const wasResolving = !!this.currentContext; + const context = this.currentContext || _Container.contextPool.acquire(); + if (!wasResolving) { + this.currentContext = context; + } + try { + const results = tokens.map((token2) => { + const cached = this.tryGetFromCaches(token2); + if (cached !== void 0) + return cached; + return this.resolveWithContext(token2, context); + }); + return results; + } finally { + if (!wasResolving) { + this.currentContext = void 0; + _Container.contextPool.release(context); + } + } + } + /** + * Resolve a dependency asynchronously (supports async factories) + */ + async resolveAsync(token2) { + if (this.currentContext) { + return this.resolveAsyncWithContext(token2, this.currentContext); + } + const context = _Container.contextPool.acquire(); + this.currentContext = context; + try { + return await this.resolveAsyncWithContext(token2, context); + } finally { + this.currentContext = void 0; + _Container.contextPool.release(context); + } + } + /** + * Try to get instance from all cache levels + * Returns undefined if not cached + * @internal + */ + tryGetFromCaches(token2) { + const ultraFast = this.ultraFastSingletonCache.get(token2); + if (ultraFast !== void 0) { + return ultraFast; + } + if (this.singletonCache.has(token2)) { + const cached = this.singletonCache.get(token2); + this.ultraFastSingletonCache.set(token2, cached); + return cached; + } + const fastFactory = this.fastTransientCache.get(token2); + if (fastFactory) { + return fastFactory(); + } + return void 0; + } + /** + * Cache instance based on lifetime strategy + * @internal + */ + cacheInstance(token2, instance, lifetime, context) { + if (lifetime === "singleton") { + this.singletonCache.set(token2, instance); + this.singletonOrder.push(token2); + this.ultraFastSingletonCache.set(token2, instance); + } else if (lifetime === "per-request" && context) { + context.cachePerRequest(token2, instance); + } + } + /** + * Validate and get binding with circular dependency check + * Returns binding or throws error + * @internal + */ + validateAndGetBinding(token2, context) { + if (context.isResolving(token2)) { + throw new CircularDependencyError([...context.getPath(), token2.toString()]); + } + const binding = this.getBinding(token2); + if (!binding) { + throw new BindingNotFoundError(token2.toString(), context.getPath()); + } + return binding; + } + /** + * Instantiate from binding synchronously + * @internal + */ + instantiateBindingSync(binding, token2, context) { + switch (binding.type) { + case "value": + return binding.value; + case "factory": + const result = binding.factory(this); + if (result instanceof Promise) { + throw new Error(`Async factory detected for ${token2.toString()}. Use resolveAsync() instead.`); + } + return result; + case "class": + const deps = binding.dependencies || []; + const resolvedDeps = deps.map((dep) => this.resolveWithContext(dep, context)); + return new binding.constructor(...resolvedDeps); + case "inline-class": + return new binding.constructor(); + default: + throw new Error(`Unknown binding type: ${binding.type}`); + } + } + /** + * Instantiate from binding asynchronously + * @internal + */ + async instantiateBindingAsync(binding, context) { + switch (binding.type) { + case "value": + return binding.value; + case "factory": + return await Promise.resolve(binding.factory(this)); + case "class": + const deps = binding.dependencies || []; + const resolvedDeps = await Promise.all(deps.map((dep) => this.resolveAsyncWithContext(dep, context))); + return new binding.constructor(...resolvedDeps); + case "inline-class": + return new binding.constructor(); + default: + throw new Error(`Unknown binding type: ${binding.type}`); + } + } + /** + * Create a child container that inherits bindings from this container + */ + createChild() { + return new _Container(this); + } + /** + * Dispose all singleton instances in reverse registration order + */ + async dispose() { + const errors = []; + for (let i = this.singletonOrder.length - 1; i >= 0; i--) { + const token2 = this.singletonOrder[i]; + const instance = this.singletonCache.get(token2); + if (instance && isDisposable(instance)) { + try { + await instance.dispose(); + } catch (error) { + errors.push(error); + } + } + } + this.singletonCache.clear(); + this.singletonOrder.length = 0; + } + /** + * Create a fluent builder for registering dependencies + */ + builder() { + return new Builder(this); + } + /** + * Resolve a named service + */ + resolveNamed(name) { + const namedRegistrations = this.__namedRegistrations; + if (!namedRegistrations) { + throw new Error(`Named service "${name}" not found. No named registrations exist.`); + } + const config = namedRegistrations.get(name); + if (!config) { + throw new Error(`Named service "${name}" not found`); + } + return this.resolve(config.token); + } + /** + * Resolve a keyed service + */ + resolveKeyed(key) { + const keyedRegistrations = this.__keyedRegistrations; + if (!keyedRegistrations) { + throw new Error(`Keyed service not found. No keyed registrations exist.`); + } + const config = keyedRegistrations.get(key); + if (!config) { + const keyStr = typeof key === "symbol" ? key.toString() : `"${key}"`; + throw new Error(`Keyed service ${keyStr} not found`); + } + return this.resolve(config.token); + } + /** + * Resolve all registrations for a token + */ + resolveAll(token2) { + const multiRegistrations = this.__multiRegistrations; + if (!multiRegistrations) { + return []; + } + const tokens = multiRegistrations.get(token2); + if (!tokens || tokens.length === 0) { + return []; + } + return tokens.map((t) => this.resolve(t)); + } + /** + * Get registry information for debugging/visualization + * Returns array of binding information + */ + getRegistry() { + const registry = []; + this.bindings.forEach((binding, token2) => { + registry.push({ + token: token2.description || token2.symbol.toString(), + type: binding.type, + lifetime: binding.lifetime, + dependencies: binding.dependencies?.map((d) => d.description || d.symbol.toString()) + }); + }); + return registry; + } + /** + * Get or create a token for an interface type + * Uses a type name hash as key for the interface registry + */ + interfaceToken(typeName) { + const key = typeName || `Interface_${Math.random().toString(36).substr(2, 9)}`; + if (this.interfaceRegistry.has(key)) { + return this.interfaceRegistry.get(key); + } + if (this.parent) { + const parentToken = this.parent.interfaceToken(key); + return parentToken; + } + const token2 = Token(key); + this.interfaceRegistry.set(key, token2); + return token2; + } + /** + * Resolve a dependency by interface type without explicit token + */ + resolveType(typeName) { + const key = typeName || ""; + let token2 = this.interfaceTokenCache.get(key); + if (!token2) { + token2 = this.interfaceToken(typeName); + this.interfaceTokenCache.set(key, token2); + } + return this.resolve(token2); + } + /** + * Resolve a keyed interface + */ + resolveTypeKeyed(key, _typeName) { + return this.resolveKeyed(key); + } + /** + * Resolve all registrations for an interface type + */ + resolveTypeAll(typeName) { + const token2 = this.interfaceToken(typeName); + return this.resolveAll(token2); + } + /** + * Internal: Resolve with context for circular dependency detection + */ + resolveWithContext(token2, context) { + const binding = this.validateAndGetBinding(token2, context); + if (binding.lifetime === "per-request" && context.hasPerRequest(token2)) { + return context.getPerRequest(token2); + } + if (binding.lifetime === "singleton" && this.singletonCache.has(token2)) { + return this.singletonCache.get(token2); + } + context.enterResolve(token2); + try { + const instance = this.instantiateBindingSync(binding, token2, context); + this.cacheInstance(token2, instance, binding.lifetime, context); + return instance; + } finally { + context.exitResolve(token2); + } + } + /** + * Internal: Async resolve with context + */ + async resolveAsyncWithContext(token2, context) { + const binding = this.validateAndGetBinding(token2, context); + if (binding.lifetime === "per-request" && context.hasPerRequest(token2)) { + return context.getPerRequest(token2); + } + if (binding.lifetime === "singleton" && this.singletonCache.has(token2)) { + return this.singletonCache.get(token2); + } + context.enterResolve(token2); + try { + const instance = await this.instantiateBindingAsync(binding, context); + this.cacheInstance(token2, instance, binding.lifetime, context); + return instance; + } finally { + context.exitResolve(token2); + } + } + /** + * Get binding from this container or parent chain + * Performance optimized: Uses flat cache to avoid recursive parent lookups + */ + getBinding(token2) { + if (!this.bindingCache) { + this.buildBindingCache(); + } + return this.bindingCache.get(token2); + } + /** + * Build flat cache of all bindings including parent chain + * This converts O(n) parent chain traversal to O(1) lookup + */ + buildBindingCache() { + this.bindingCache = /* @__PURE__ */ new Map(); + let current = this; + while (current) { + current.bindings.forEach((binding, token2) => { + if (!this.bindingCache.has(token2)) { + this.bindingCache.set(token2, binding); + } + }); + current = current.parent; + } + } + /** + * Invalidate binding cache when new bindings are added + * Called by bindValue, bindFactory, bindClass + */ + invalidateBindingCache() { + this.bindingCache = void 0; + this.ultraFastSingletonCache.clear(); + } +}; +__name(_Container, "Container"); +var Container = _Container; +Container.contextPool = new ResolutionContextPool(); + +// src/features/date/DateRenderer.ts +var _DateRenderer = class _DateRenderer { + constructor(dateService) { + this.dateService = dateService; + this.type = "date"; + } + render(context) { + const dates = context.filter["date"] || []; + const resourceIds = context.filter["resource"] || []; + const dateGrouping = context.groupings?.find((g) => g.type === "date"); + const hideHeader = dateGrouping?.hideHeader === true; + const iterations = resourceIds.length || 1; + let columnCount = 0; + for (let r = 0; r < iterations; r++) { + const resourceId = resourceIds[r]; + for (const dateStr of dates) { + const date = this.dateService.parseISO(dateStr); + const segments = { date: dateStr }; + if (resourceId) + segments.resource = resourceId; + const columnKey = this.dateService.buildColumnKey(segments); + const header = document.createElement("swp-day-header"); + header.dataset.date = dateStr; + header.dataset.columnKey = columnKey; + if (resourceId) { + header.dataset.resourceId = resourceId; + } + if (hideHeader) { + header.dataset.hidden = "true"; + } + header.innerHTML = ` + ${this.dateService.getDayName(date, "short")} + ${date.getDate()} + `; + context.headerContainer.appendChild(header); + const column = document.createElement("swp-day-column"); + column.dataset.date = dateStr; + column.dataset.columnKey = columnKey; + if (resourceId) { + column.dataset.resourceId = resourceId; + } + column.innerHTML = ""; + context.columnContainer.appendChild(column); + columnCount++; + } + } + const container2 = context.columnContainer.closest("swp-calendar-container"); + if (container2) { + container2.style.setProperty("--grid-columns", String(columnCount)); + } + } +}; +__name(_DateRenderer, "DateRenderer"); +var DateRenderer = _DateRenderer; + +// src/core/DateService.ts +var import_dayjs = __toESM(require_dayjs_min(), 1); +var import_utc = __toESM(require_utc(), 1); +var import_timezone = __toESM(require_timezone(), 1); +var import_isoWeek = __toESM(require_isoWeek(), 1); +import_dayjs.default.extend(import_utc.default); +import_dayjs.default.extend(import_timezone.default); +import_dayjs.default.extend(import_isoWeek.default); +var _DateService = class _DateService { + constructor(config, baseDate) { + this.config = config; + this.timezone = config.timezone; + this.baseDate = baseDate ? (0, import_dayjs.default)(baseDate) : (0, import_dayjs.default)(); + } + /** + * Set a fixed base date (useful for demos with static mock data) + */ + setBaseDate(date) { + this.baseDate = (0, import_dayjs.default)(date); + } + /** + * Get the current base date (either fixed or today) + */ + getBaseDate() { + return this.baseDate.toDate(); + } + parseISO(isoString) { + return (0, import_dayjs.default)(isoString).toDate(); + } + getDayName(date, format = "short") { + return new Intl.DateTimeFormat(this.config.locale, { weekday: format }).format(date); + } + /** + * Get dates starting from a day offset + * @param dayOffset - Day offset from base date + * @param count - Number of consecutive days to return + * @returns Array of date strings in YYYY-MM-DD format + */ + getDatesFromOffset(dayOffset, count) { + const startDate = this.baseDate.add(dayOffset, "day"); + return Array.from({ length: count }, (_, i) => startDate.add(i, "day").format("YYYY-MM-DD")); + } + /** + * Get specific weekdays from the week containing the offset date + * @param dayOffset - Day offset from base date + * @param workDays - Array of ISO weekday numbers (1=Monday, 7=Sunday) + * @returns Array of date strings in YYYY-MM-DD format + */ + getWorkDaysFromOffset(dayOffset, workDays) { + const targetDate = this.baseDate.add(dayOffset, "day"); + const monday = targetDate.startOf("week").add(1, "day"); + return workDays.map((isoDay) => { + const daysFromMonday = isoDay === 7 ? 6 : isoDay - 1; + return monday.add(daysFromMonday, "day").format("YYYY-MM-DD"); + }); + } + // Legacy methods for backwards compatibility + getWeekDates(weekOffset = 0, days = 7) { + return this.getDatesFromOffset(weekOffset * 7, days); + } + getWorkWeekDates(weekOffset, workDays) { + return this.getWorkDaysFromOffset(weekOffset * 7, workDays); + } + // ============================================ + // FORMATTING + // ============================================ + formatTime(date, showSeconds = false) { + const pattern = showSeconds ? "HH:mm:ss" : "HH:mm"; + return (0, import_dayjs.default)(date).format(pattern); + } + formatTimeRange(start, end) { + return `${this.formatTime(start)} - ${this.formatTime(end)}`; + } + formatDate(date) { + return (0, import_dayjs.default)(date).format("YYYY-MM-DD"); + } + getDateKey(date) { + return this.formatDate(date); + } + // ============================================ + // COLUMN KEY + // ============================================ + /** + * Build a uniform columnKey from grouping segments + * Handles any combination of date, resource, team, etc. + * + * @example + * buildColumnKey({ date: '2025-12-09' }) → "2025-12-09" + * buildColumnKey({ date: '2025-12-09', resource: 'EMP001' }) → "2025-12-09:EMP001" + */ + buildColumnKey(segments) { + const date = segments.date; + const others = Object.entries(segments).filter(([k]) => k !== "date").sort(([a], [b]) => a.localeCompare(b)).map(([, v]) => v); + return date ? [date, ...others].join(":") : others.join(":"); + } + /** + * Parse a columnKey back into segments + * Assumes format: "date:resource:..." or just "date" + */ + parseColumnKey(columnKey) { + const parts = columnKey.split(":"); + return { + date: parts[0], + resource: parts[1] + }; + } + /** + * Extract dateKey from columnKey (first segment) + */ + getDateFromColumnKey(columnKey) { + return columnKey.split(":")[0]; + } + // ============================================ + // TIME CALCULATIONS + // ============================================ + timeToMinutes(timeString) { + const parts = timeString.split(":").map(Number); + const hours = parts[0] || 0; + const minutes = parts[1] || 0; + return hours * 60 + minutes; + } + minutesToTime(totalMinutes) { + const hours = Math.floor(totalMinutes / 60); + const minutes = totalMinutes % 60; + return (0, import_dayjs.default)().hour(hours).minute(minutes).format("HH:mm"); + } + getMinutesSinceMidnight(date) { + const d = (0, import_dayjs.default)(date); + return d.hour() * 60 + d.minute(); + } + // ============================================ + // UTC CONVERSIONS + // ============================================ + toUTC(localDate) { + return import_dayjs.default.tz(localDate, this.timezone).utc().toISOString(); + } + fromUTC(utcString) { + return import_dayjs.default.utc(utcString).tz(this.timezone).toDate(); + } + // ============================================ + // DATE CREATION + // ============================================ + createDateAtTime(baseDate, timeString) { + const totalMinutes = this.timeToMinutes(timeString); + const hours = Math.floor(totalMinutes / 60); + const minutes = totalMinutes % 60; + return (0, import_dayjs.default)(baseDate).startOf("day").hour(hours).minute(minutes).toDate(); + } + getISOWeekDay(date) { + return (0, import_dayjs.default)(date).isoWeekday(); + } +}; +__name(_DateService, "DateService"); +var DateService = _DateService; + +// src/core/BaseGroupingRenderer.ts +var _BaseGroupingRenderer = class _BaseGroupingRenderer { + /** + * Main render method - handles common logic + */ + async render(context) { + const allowedIds = context.filter[this.type] || []; + if (allowedIds.length === 0) + return; + const entities = await this.getEntities(allowedIds); + const dateCount = context.filter["date"]?.length || 1; + const childIds = context.childType ? context.filter[context.childType] || [] : []; + for (const entity of entities) { + const entityChildIds = context.parentChildMap?.[entity.id] || []; + const childCount = entityChildIds.filter((id) => childIds.includes(id)).length; + const colspan = childCount * dateCount; + const header = document.createElement(this.config.elementTag); + header.dataset[this.config.idAttribute] = entity.id; + header.style.setProperty(this.config.colspanVar, String(colspan)); + this.renderHeader(entity, header, context); + context.headerContainer.appendChild(header); + } + } + /** + * Override this method for custom header rendering + * Default: just sets textContent to display name + */ + renderHeader(entity, header, _context) { + header.textContent = this.getDisplayName(entity); + } + /** + * Helper to render a single entity header. + * Can be used by subclasses that override render() but want consistent header creation. + */ + createHeader(entity, context) { + const header = document.createElement(this.config.elementTag); + header.dataset[this.config.idAttribute] = entity.id; + this.renderHeader(entity, header, context); + return header; + } +}; +__name(_BaseGroupingRenderer, "BaseGroupingRenderer"); +var BaseGroupingRenderer = _BaseGroupingRenderer; + +// src/features/resource/ResourceRenderer.ts +var _ResourceRenderer = class _ResourceRenderer extends BaseGroupingRenderer { + constructor(resourceService) { + super(); + this.resourceService = resourceService; + this.type = "resource"; + this.config = { + elementTag: "swp-resource-header", + idAttribute: "resourceId", + colspanVar: "--resource-cols" + }; + } + getEntities(ids) { + return this.resourceService.getByIds(ids); + } + getDisplayName(entity) { + return entity.displayName; + } + /** + * Override render to handle: + * 1. Special ordering when parentChildMap exists (resources grouped by parent) + * 2. Different colspan calculation (just dateCount, not childCount * dateCount) + */ + async render(context) { + const resourceIds = context.filter["resource"] || []; + const dateCount = context.filter["date"]?.length || 1; + let orderedResourceIds; + if (context.parentChildMap) { + orderedResourceIds = []; + for (const childIds of Object.values(context.parentChildMap)) { + for (const childId of childIds) { + if (resourceIds.includes(childId)) { + orderedResourceIds.push(childId); + } + } + } + } else { + orderedResourceIds = resourceIds; + } + const resources = await this.getEntities(orderedResourceIds); + const resourceMap = new Map(resources.map((r) => [r.id, r])); + for (const resourceId of orderedResourceIds) { + const resource = resourceMap.get(resourceId); + if (!resource) + continue; + const header = this.createHeader(resource, context); + header.style.gridColumn = `span ${dateCount}`; + context.headerContainer.appendChild(header); + } + } +}; +__name(_ResourceRenderer, "ResourceRenderer"); +var ResourceRenderer = _ResourceRenderer; + +// src/features/team/TeamRenderer.ts +var _TeamRenderer = class _TeamRenderer extends BaseGroupingRenderer { + constructor(teamService) { + super(); + this.teamService = teamService; + this.type = "team"; + this.config = { + elementTag: "swp-team-header", + idAttribute: "teamId", + colspanVar: "--team-cols" + }; + } + getEntities(ids) { + return this.teamService.getByIds(ids); + } + getDisplayName(entity) { + return entity.name; + } +}; +__name(_TeamRenderer, "TeamRenderer"); +var TeamRenderer = _TeamRenderer; + +// src/features/department/DepartmentRenderer.ts +var _DepartmentRenderer = class _DepartmentRenderer extends BaseGroupingRenderer { + constructor(departmentService) { + super(); + this.departmentService = departmentService; + this.type = "department"; + this.config = { + elementTag: "swp-department-header", + idAttribute: "departmentId", + colspanVar: "--department-cols" + }; + } + getEntities(ids) { + return this.departmentService.getByIds(ids); + } + getDisplayName(entity) { + return entity.name; + } +}; +__name(_DepartmentRenderer, "DepartmentRenderer"); +var DepartmentRenderer = _DepartmentRenderer; + +// src/core/RenderBuilder.ts +function buildPipeline(renderers) { + return { + async run(context) { + for (const renderer of renderers) { + await renderer.render(context); + } + } + }; +} +__name(buildPipeline, "buildPipeline"); + +// src/core/FilterTemplate.ts +var _FilterTemplate = class _FilterTemplate { + constructor(dateService, entityResolver) { + this.dateService = dateService; + this.entityResolver = entityResolver; + this.fields = []; + } + /** + * Tilføj felt til template + * @param idProperty - Property-navn (bruges på både event og column.dataset) + * @param derivedFrom - Hvis feltet udledes fra anden property (f.eks. date fra start) + */ + addField(idProperty, derivedFrom) { + this.fields.push({ idProperty, derivedFrom }); + return this; + } + /** + * Parse dot-notation string into components + * @example 'resource.teamId' → { entityType: 'resource', property: 'teamId', foreignKey: 'resourceId' } + */ + parseDotNotation(idProperty) { + if (!idProperty.includes(".")) + return null; + const [entityType, property] = idProperty.split("."); + return { + entityType, + property, + foreignKey: entityType + "Id" + // Convention: resource → resourceId + }; + } + /** + * Get dataset key for column lookup + * For dot-notation 'resource.teamId', we look for 'teamId' in dataset + */ + getDatasetKey(idProperty) { + const dotNotation = this.parseDotNotation(idProperty); + if (dotNotation) { + return dotNotation.property; + } + return idProperty; + } + /** + * Byg nøgle fra kolonne + * Læser værdier fra column.dataset[idProperty] + * For dot-notation, uses the property part (resource.teamId → teamId) + */ + buildKeyFromColumn(column) { + return this.fields.map((f) => { + const key = this.getDatasetKey(f.idProperty); + return column.dataset[key] || ""; + }).join(":"); + } + /** + * Byg nøgle fra event + * Læser værdier fra event[idProperty] eller udleder fra derivedFrom + * For dot-notation, resolves via EntityResolver + */ + buildKeyFromEvent(event) { + const eventRecord = event; + return this.fields.map((f) => { + const dotNotation = this.parseDotNotation(f.idProperty); + if (dotNotation) { + return this.resolveDotNotation(eventRecord, dotNotation); + } + if (f.derivedFrom) { + const sourceValue = eventRecord[f.derivedFrom]; + if (sourceValue instanceof Date) { + return this.dateService.getDateKey(sourceValue); + } + return String(sourceValue || ""); + } + return String(eventRecord[f.idProperty] || ""); + }).join(":"); + } + /** + * Resolve dot-notation reference via EntityResolver + */ + resolveDotNotation(eventRecord, dotNotation) { + if (!this.entityResolver) { + console.warn(`FilterTemplate: EntityResolver required for dot-notation '${dotNotation.entityType}.${dotNotation.property}'`); + return ""; + } + const foreignId = eventRecord[dotNotation.foreignKey]; + if (!foreignId) + return ""; + const entity = this.entityResolver.resolve(dotNotation.entityType, String(foreignId)); + if (!entity) + return ""; + return String(entity[dotNotation.property] || ""); + } + /** + * Match event mod kolonne + */ + matches(event, column) { + return this.buildKeyFromEvent(event) === this.buildKeyFromColumn(column); + } +}; +__name(_FilterTemplate, "FilterTemplate"); +var FilterTemplate = _FilterTemplate; + +// src/core/CalendarOrchestrator.ts +var _CalendarOrchestrator = class _CalendarOrchestrator { + constructor(allRenderers, eventRenderer, scheduleRenderer, headerDrawerRenderer, dateService, entityServices) { + this.allRenderers = allRenderers; + this.eventRenderer = eventRenderer; + this.scheduleRenderer = scheduleRenderer; + this.headerDrawerRenderer = headerDrawerRenderer; + this.dateService = dateService; + this.entityServices = entityServices; + } + async render(viewConfig, container2) { + const headerContainer = container2.querySelector("swp-calendar-header"); + const columnContainer = container2.querySelector("swp-day-columns"); + if (!headerContainer || !columnContainer) { + throw new Error("Missing swp-calendar-header or swp-day-columns"); + } + const filter = {}; + for (const grouping of viewConfig.groupings) { + filter[grouping.type] = grouping.values; + } + const filterTemplate = new FilterTemplate(this.dateService); + for (const grouping of viewConfig.groupings) { + if (grouping.idProperty) { + filterTemplate.addField(grouping.idProperty, grouping.derivedFrom); + } + } + const { parentChildMap, childType } = await this.resolveBelongsTo(viewConfig.groupings, filter); + const context = { headerContainer, columnContainer, filter, groupings: viewConfig.groupings, parentChildMap, childType }; + headerContainer.innerHTML = ""; + columnContainer.innerHTML = ""; + const levels = viewConfig.groupings.map((g) => g.type).join(" "); + headerContainer.dataset.levels = levels; + const activeRenderers = this.selectRenderers(viewConfig); + const pipeline = buildPipeline(activeRenderers); + await pipeline.run(context); + await this.scheduleRenderer.render(container2, filter); + await this.eventRenderer.render(container2, filter, filterTemplate); + await this.headerDrawerRenderer.render(container2, filter, filterTemplate); + } + selectRenderers(viewConfig) { + const types = viewConfig.groupings.map((g) => g.type); + return types.map((type) => this.allRenderers.find((r) => r.type === type)).filter((r) => r !== void 0); + } + /** + * Resolve belongsTo relations to build parent-child map + * e.g., belongsTo: 'team.resourceIds' → { team1: ['EMP001', 'EMP002'], team2: [...] } + * Also returns the childType (the grouping type that has belongsTo) + */ + async resolveBelongsTo(groupings, filter) { + const childGrouping = groupings.find((g) => g.belongsTo); + if (!childGrouping?.belongsTo) + return {}; + const [entityType, property] = childGrouping.belongsTo.split("."); + if (!entityType || !property) + return {}; + const parentIds = filter[entityType] || []; + if (parentIds.length === 0) + return {}; + const service = this.entityServices.find((s) => s.entityType.toLowerCase() === entityType); + if (!service) + return {}; + const allEntities = await service.getAll(); + const entities = allEntities.filter((e) => parentIds.includes(e.id)); + const map = {}; + for (const entity of entities) { + const entityRecord = entity; + const children = entityRecord[property] || []; + map[entityRecord.id] = children; + } + return { parentChildMap: map, childType: childGrouping.type }; + } +}; +__name(_CalendarOrchestrator, "CalendarOrchestrator"); +var CalendarOrchestrator = _CalendarOrchestrator; + +// src/core/NavigationAnimator.ts +var _NavigationAnimator = class _NavigationAnimator { + constructor(headerTrack, contentTrack, headerDrawer) { + this.headerTrack = headerTrack; + this.contentTrack = contentTrack; + this.headerDrawer = headerDrawer; + } + async slide(direction, renderFn) { + const out = direction === "left" ? "-100%" : "100%"; + const into = direction === "left" ? "100%" : "-100%"; + await this.animateOut(out); + await renderFn(); + await this.animateIn(into); + } + async animateOut(translate) { + const animations = [ + this.headerTrack.animate([{ transform: "translateX(0)" }, { transform: `translateX(${translate})` }], { duration: 200, easing: "ease-in" }).finished, + this.contentTrack.animate([{ transform: "translateX(0)" }, { transform: `translateX(${translate})` }], { duration: 200, easing: "ease-in" }).finished + ]; + if (this.headerDrawer) { + animations.push(this.headerDrawer.animate([{ transform: "translateX(0)" }, { transform: `translateX(${translate})` }], { duration: 200, easing: "ease-in" }).finished); + } + await Promise.all(animations); + } + async animateIn(translate) { + const animations = [ + this.headerTrack.animate([{ transform: `translateX(${translate})` }, { transform: "translateX(0)" }], { duration: 200, easing: "ease-out" }).finished, + this.contentTrack.animate([{ transform: `translateX(${translate})` }, { transform: "translateX(0)" }], { duration: 200, easing: "ease-out" }).finished + ]; + if (this.headerDrawer) { + animations.push(this.headerDrawer.animate([{ transform: `translateX(${translate})` }, { transform: "translateX(0)" }], { duration: 200, easing: "ease-out" }).finished); + } + await Promise.all(animations); + } +}; +__name(_NavigationAnimator, "NavigationAnimator"); +var NavigationAnimator = _NavigationAnimator; + +// src/core/CalendarEvents.ts +var CalendarEvents = { + // Command events (host → calendar) + CMD_NAVIGATE_PREV: "calendar:cmd:navigate:prev", + CMD_NAVIGATE_NEXT: "calendar:cmd:navigate:next", + CMD_DRAWER_TOGGLE: "calendar:cmd:drawer:toggle", + CMD_RENDER: "calendar:cmd:render", + CMD_WORKWEEK_CHANGE: "calendar:cmd:workweek:change", + CMD_VIEW_UPDATE: "calendar:cmd:view:update" +}; + +// src/core/CalendarApp.ts +var _CalendarApp = class _CalendarApp { + constructor(orchestrator, timeAxisRenderer, dateService, scrollManager, headerDrawerManager, dragDropManager, edgeScrollManager, resizeManager, headerDrawerRenderer, eventPersistenceManager, settingsService, viewConfigService, eventBus) { + this.orchestrator = orchestrator; + this.timeAxisRenderer = timeAxisRenderer; + this.dateService = dateService; + this.scrollManager = scrollManager; + this.headerDrawerManager = headerDrawerManager; + this.dragDropManager = dragDropManager; + this.edgeScrollManager = edgeScrollManager; + this.resizeManager = resizeManager; + this.headerDrawerRenderer = headerDrawerRenderer; + this.eventPersistenceManager = eventPersistenceManager; + this.settingsService = settingsService; + this.viewConfigService = viewConfigService; + this.eventBus = eventBus; + this.dayOffset = 0; + this.currentViewId = "simple"; + this.workweekPreset = null; + this.groupingOverrides = /* @__PURE__ */ new Map(); + } + async init(container2) { + this.container = container2; + const gridSettings = await this.settingsService.getGridSettings(); + if (!gridSettings) { + throw new Error("GridSettings not found"); + } + this.workweekPreset = await this.settingsService.getDefaultWorkweekPreset(); + this.animator = new NavigationAnimator(container2.querySelector("swp-header-track"), container2.querySelector("swp-content-track"), container2.querySelector("swp-header-drawer")); + this.timeAxisRenderer.render(container2.querySelector("#time-axis"), gridSettings.dayStartHour, gridSettings.dayEndHour); + this.scrollManager.init(container2); + this.headerDrawerManager.init(container2); + this.dragDropManager.init(container2); + this.resizeManager.init(container2); + const scrollableContent = container2.querySelector("swp-scrollable-content"); + this.edgeScrollManager.init(scrollableContent); + this.setupEventListeners(); + this.emitStatus("ready"); + } + setupEventListeners() { + this.eventBus.on(CalendarEvents.CMD_NAVIGATE_PREV, () => { + this.handleNavigatePrev(); + }); + this.eventBus.on(CalendarEvents.CMD_NAVIGATE_NEXT, () => { + this.handleNavigateNext(); + }); + this.eventBus.on(CalendarEvents.CMD_DRAWER_TOGGLE, () => { + this.headerDrawerManager.toggle(); + }); + this.eventBus.on(CalendarEvents.CMD_RENDER, (e) => { + const { viewId } = e.detail; + this.handleRenderCommand(viewId); + }); + this.eventBus.on(CalendarEvents.CMD_WORKWEEK_CHANGE, (e) => { + const { presetId } = e.detail; + this.handleWorkweekChange(presetId); + }); + this.eventBus.on(CalendarEvents.CMD_VIEW_UPDATE, (e) => { + const { type, values } = e.detail; + this.handleViewUpdate(type, values); + }); + } + async handleRenderCommand(viewId) { + this.currentViewId = viewId; + await this.render(); + this.emitStatus("rendered", { viewId }); + } + async handleNavigatePrev() { + const step = this.workweekPreset?.periodDays ?? 7; + this.dayOffset -= step; + await this.animator.slide("right", () => this.render()); + this.emitStatus("rendered", { viewId: this.currentViewId }); + } + async handleNavigateNext() { + const step = this.workweekPreset?.periodDays ?? 7; + this.dayOffset += step; + await this.animator.slide("left", () => this.render()); + this.emitStatus("rendered", { viewId: this.currentViewId }); + } + async handleWorkweekChange(presetId) { + const preset = await this.settingsService.getWorkweekPreset(presetId); + if (preset) { + this.workweekPreset = preset; + await this.render(); + this.emitStatus("rendered", { viewId: this.currentViewId }); + } + } + async handleViewUpdate(type, values) { + this.groupingOverrides.set(type, values); + await this.render(); + this.emitStatus("rendered", { viewId: this.currentViewId }); + } + async render() { + const storedConfig = await this.viewConfigService.getById(this.currentViewId); + if (!storedConfig) { + this.emitStatus("error", { message: `ViewConfig not found: ${this.currentViewId}` }); + return; + } + const workDays = this.workweekPreset?.workDays || [1, 2, 3, 4, 5]; + const periodDays = this.workweekPreset?.periodDays ?? 7; + const dates = periodDays === 1 ? this.dateService.getDatesFromOffset(this.dayOffset, workDays.length) : this.dateService.getWorkDaysFromOffset(this.dayOffset, workDays); + const viewConfig = { + ...storedConfig, + groupings: storedConfig.groupings.map((g) => { + if (g.type === "date") { + return { ...g, values: dates }; + } + const override = this.groupingOverrides.get(g.type); + if (override) { + return { ...g, values: override }; + } + return g; + }) + }; + await this.orchestrator.render(viewConfig, this.container); + } + emitStatus(status, detail) { + this.container.dispatchEvent(new CustomEvent(`calendar:status:${status}`, { + detail, + bubbles: true + })); + } +}; +__name(_CalendarApp, "CalendarApp"); +var CalendarApp = _CalendarApp; + +// src/features/timeaxis/TimeAxisRenderer.ts +var _TimeAxisRenderer = class _TimeAxisRenderer { + render(container2, startHour = 6, endHour = 20) { + container2.innerHTML = ""; + for (let hour = startHour; hour <= endHour; hour++) { + const marker = document.createElement("swp-hour-marker"); + marker.textContent = `${hour.toString().padStart(2, "0")}:00`; + container2.appendChild(marker); + } + } +}; +__name(_TimeAxisRenderer, "TimeAxisRenderer"); +var TimeAxisRenderer = _TimeAxisRenderer; + +// src/core/ScrollManager.ts +var _ScrollManager = class _ScrollManager { + init(container2) { + this.scrollableContent = container2.querySelector("swp-scrollable-content"); + this.timeAxisContent = container2.querySelector("swp-time-axis-content"); + this.calendarHeader = container2.querySelector("swp-calendar-header"); + this.headerDrawer = container2.querySelector("swp-header-drawer"); + this.headerViewport = container2.querySelector("swp-header-viewport"); + this.headerSpacer = container2.querySelector("swp-header-spacer"); + this.scrollableContent.addEventListener("scroll", () => this.onScroll()); + this.resizeObserver = new ResizeObserver(() => this.syncHeaderSpacerHeight()); + this.resizeObserver.observe(this.headerViewport); + this.syncHeaderSpacerHeight(); + } + syncHeaderSpacerHeight() { + const computedHeight = getComputedStyle(this.headerViewport).height; + this.headerSpacer.style.height = computedHeight; + } + onScroll() { + const { scrollTop, scrollLeft } = this.scrollableContent; + this.timeAxisContent.style.transform = `translateY(-${scrollTop}px)`; + this.calendarHeader.style.transform = `translateX(-${scrollLeft}px)`; + this.headerDrawer.style.transform = `translateX(-${scrollLeft}px)`; + } +}; +__name(_ScrollManager, "ScrollManager"); +var ScrollManager = _ScrollManager; + +// src/core/HeaderDrawerManager.ts +var _HeaderDrawerManager = class _HeaderDrawerManager { + constructor() { + this.expanded = false; + this.currentRows = 0; + this.rowHeight = 25; + this.duration = 200; + } + init(container2) { + this.drawer = container2.querySelector("swp-header-drawer"); + if (!this.drawer) + console.error("HeaderDrawerManager: swp-header-drawer not found"); + } + toggle() { + this.expanded ? this.collapse() : this.expand(); + } + /** + * Expand drawer to single row (legacy support) + */ + expand() { + this.expandToRows(1); + } + /** + * Expand drawer to fit specified number of rows + */ + expandToRows(rowCount) { + const targetHeight = rowCount * this.rowHeight; + const currentHeight = this.expanded ? this.currentRows * this.rowHeight : 0; + if (this.expanded && this.currentRows === rowCount) + return; + this.currentRows = rowCount; + this.expanded = true; + this.animate(currentHeight, targetHeight); + } + collapse() { + if (!this.expanded) + return; + const currentHeight = this.currentRows * this.rowHeight; + this.expanded = false; + this.currentRows = 0; + this.animate(currentHeight, 0); + } + animate(from, to) { + const keyframes = [ + { height: `${from}px` }, + { height: `${to}px` } + ]; + const options = { + duration: this.duration, + easing: "ease", + fill: "forwards" + }; + this.drawer.animate(keyframes, options); + } + isExpanded() { + return this.expanded; + } + getRowCount() { + return this.currentRows; + } +}; +__name(_HeaderDrawerManager, "HeaderDrawerManager"); +var HeaderDrawerManager = _HeaderDrawerManager; + +// src/demo/MockStores.ts +var _MockTeamStore = class _MockTeamStore { + constructor() { + this.type = "team"; + this.teams = [ + { id: "alpha", name: "Team Alpha" }, + { id: "beta", name: "Team Beta" } + ]; + } + getByIds(ids) { + return this.teams.filter((t) => ids.includes(t.id)); + } +}; +__name(_MockTeamStore, "MockTeamStore"); +var MockTeamStore = _MockTeamStore; +var _MockResourceStore = class _MockResourceStore { + constructor() { + this.type = "resource"; + this.resources = [ + { id: "alice", name: "Alice", teamId: "alpha" }, + { id: "bob", name: "Bob", teamId: "alpha" }, + { id: "carol", name: "Carol", teamId: "beta" }, + { id: "dave", name: "Dave", teamId: "beta" } + ]; + } + getByIds(ids) { + return this.resources.filter((r) => ids.includes(r.id)); + } +}; +__name(_MockResourceStore, "MockResourceStore"); +var MockResourceStore = _MockResourceStore; + +// src/demo/DemoApp.ts +var _DemoApp = class _DemoApp { + constructor(indexedDBContext, dataSeeder, auditService, calendarApp, dateService, resourceService, eventBus) { + this.indexedDBContext = indexedDBContext; + this.dataSeeder = dataSeeder; + this.auditService = auditService; + this.calendarApp = calendarApp; + this.dateService = dateService; + this.resourceService = resourceService; + this.eventBus = eventBus; + this.currentView = "simple"; + } + async init() { + this.dateService.setBaseDate(/* @__PURE__ */ new Date("2025-12-08")); + await this.indexedDBContext.initialize(); + console.log("[DemoApp] IndexedDB initialized"); + await this.dataSeeder.seedIfEmpty(); + console.log("[DemoApp] Data seeding complete"); + this.container = document.querySelector("swp-calendar-container"); + await this.calendarApp.init(this.container); + console.log("[DemoApp] CalendarApp initialized"); + this.setupNavigation(); + this.setupDrawerToggle(); + this.setupViewSwitching(); + this.setupWorkweekSelector(); + await this.setupResourceSelector(); + this.setupStatusListeners(); + this.eventBus.emit(CalendarEvents.CMD_RENDER, { viewId: this.currentView }); + } + setupNavigation() { + document.getElementById("btn-prev").onclick = () => { + this.eventBus.emit(CalendarEvents.CMD_NAVIGATE_PREV); + }; + document.getElementById("btn-next").onclick = () => { + this.eventBus.emit(CalendarEvents.CMD_NAVIGATE_NEXT); + }; + } + setupViewSwitching() { + const chips = document.querySelectorAll(".view-chip"); + chips.forEach((chip) => { + chip.addEventListener("click", () => { + chips.forEach((c) => c.classList.remove("active")); + chip.classList.add("active"); + const view = chip.dataset.view; + if (view) { + this.currentView = view; + this.updateSelectorVisibility(); + this.eventBus.emit(CalendarEvents.CMD_RENDER, { viewId: view }); + } + }); + }); + } + updateSelectorVisibility() { + const selector = document.querySelector("swp-resource-selector"); + const showSelector = this.currentView === "picker" || this.currentView === "day"; + selector?.classList.toggle("hidden", !showSelector); + } + setupDrawerToggle() { + document.getElementById("btn-drawer").onclick = () => { + this.eventBus.emit(CalendarEvents.CMD_DRAWER_TOGGLE); + }; + } + setupWorkweekSelector() { + const workweekSelect = document.getElementById("workweek-select"); + workweekSelect?.addEventListener("change", () => { + const presetId = workweekSelect.value; + this.eventBus.emit(CalendarEvents.CMD_WORKWEEK_CHANGE, { presetId }); + }); + } + async setupResourceSelector() { + const resources = await this.resourceService.getAll(); + const container2 = document.querySelector(".resource-checkboxes"); + if (!container2) + return; + container2.innerHTML = ""; + resources.forEach((r) => { + const label = document.createElement("label"); + label.innerHTML = ` + + ${r.displayName} + `; + container2.appendChild(label); + }); + container2.addEventListener("change", () => { + const checked = container2.querySelectorAll("input:checked"); + const values = Array.from(checked).map((cb) => cb.value); + this.eventBus.emit(CalendarEvents.CMD_VIEW_UPDATE, { type: "resource", values }); + }); + } + setupStatusListeners() { + this.container.addEventListener("calendar:status:ready", () => { + console.log("[DemoApp] Calendar ready"); + }); + this.container.addEventListener("calendar:status:rendered", (e) => { + console.log("[DemoApp] Calendar rendered:", e.detail.viewId); + }); + this.container.addEventListener("calendar:status:error", (e) => { + console.error("[DemoApp] Calendar error:", e.detail.message); + }); + } +}; +__name(_DemoApp, "DemoApp"); +var DemoApp = _DemoApp; + +// src/core/EventBus.ts +var _EventBus = class _EventBus { + constructor() { + this.eventLog = []; + this.debug = false; + this.listeners = /* @__PURE__ */ new Set(); + this.logConfig = { + calendar: true, + grid: true, + event: true, + scroll: true, + navigation: true, + view: true, + default: true + }; + } + /** + * Subscribe to an event via DOM addEventListener + */ + on(eventType, handler, options) { + document.addEventListener(eventType, handler, options); + this.listeners.add({ eventType, handler, options }); + return () => this.off(eventType, handler); + } + /** + * Subscribe to an event once + */ + once(eventType, handler) { + return this.on(eventType, handler, { once: true }); + } + /** + * Unsubscribe from an event + */ + off(eventType, handler) { + document.removeEventListener(eventType, handler); + for (const listener of this.listeners) { + if (listener.eventType === eventType && listener.handler === handler) { + this.listeners.delete(listener); + break; + } + } + } + /** + * Emit an event via DOM CustomEvent + */ + emit(eventType, detail = {}) { + if (!eventType) { + return false; + } + const event = new CustomEvent(eventType, { + detail: detail ?? {}, + bubbles: true, + cancelable: true + }); + if (this.debug) { + this.logEventWithGrouping(eventType, detail); + } + this.eventLog.push({ + type: eventType, + detail: detail ?? {}, + timestamp: Date.now() + }); + return !document.dispatchEvent(event); + } + /** + * Log event with console grouping + */ + logEventWithGrouping(eventType, _detail) { + const category = this.extractCategory(eventType); + if (!this.logConfig[category]) { + return; + } + this.getCategoryStyle(category); + } + /** + * Extract category from event type + */ + extractCategory(eventType) { + if (!eventType) { + return "unknown"; + } + if (eventType.includes(":")) { + return eventType.split(":")[0]; + } + const lowerType = eventType.toLowerCase(); + if (lowerType.includes("grid") || lowerType.includes("rendered")) + return "grid"; + if (lowerType.includes("event") || lowerType.includes("sync")) + return "event"; + if (lowerType.includes("scroll")) + return "scroll"; + if (lowerType.includes("nav") || lowerType.includes("date")) + return "navigation"; + if (lowerType.includes("view")) + return "view"; + return "default"; + } + /** + * Get styling for different categories + */ + getCategoryStyle(category) { + const styles = { + calendar: { emoji: "\u{1F4C5}", color: "#2196F3" }, + grid: { emoji: "\u{1F4CA}", color: "#4CAF50" }, + event: { emoji: "\u{1F4CC}", color: "#FF9800" }, + scroll: { emoji: "\u{1F4DC}", color: "#9C27B0" }, + navigation: { emoji: "\u{1F9ED}", color: "#F44336" }, + view: { emoji: "\u{1F441}", color: "#00BCD4" }, + default: { emoji: "\u{1F4E2}", color: "#607D8B" } + }; + return styles[category] || styles.default; + } + /** + * Configure logging for specific categories + */ + setLogConfig(config) { + this.logConfig = { ...this.logConfig, ...config }; + } + /** + * Get current log configuration + */ + getLogConfig() { + return { ...this.logConfig }; + } + /** + * Get event history + */ + getEventLog(eventType) { + if (eventType) { + return this.eventLog.filter((e) => e.type === eventType); + } + return this.eventLog; + } + /** + * Enable/disable debug mode + */ + setDebug(enabled) { + this.debug = enabled; + } +}; +__name(_EventBus, "EventBus"); +var EventBus = _EventBus; + +// src/storage/IndexedDBContext.ts +var _IndexedDBContext = class _IndexedDBContext { + constructor(stores) { + this.db = null; + this.initialized = false; + this.stores = stores; + } + /** + * Initialize and open the database + */ + async initialize() { + return new Promise((resolve, reject) => { + const request = indexedDB.open(_IndexedDBContext.DB_NAME, _IndexedDBContext.DB_VERSION); + request.onerror = () => { + reject(new Error(`Failed to open IndexedDB: ${request.error}`)); + }; + request.onsuccess = () => { + this.db = request.result; + this.initialized = true; + resolve(); + }; + request.onupgradeneeded = (event) => { + const db = event.target.result; + this.stores.forEach((store) => { + if (!db.objectStoreNames.contains(store.storeName)) { + store.create(db); + } + }); + }; + }); + } + /** + * Check if database is initialized + */ + isInitialized() { + return this.initialized; + } + /** + * Get IDBDatabase instance + */ + getDatabase() { + if (!this.db) { + throw new Error("IndexedDB not initialized. Call initialize() first."); + } + return this.db; + } + /** + * Close database connection + */ + close() { + if (this.db) { + this.db.close(); + this.db = null; + this.initialized = false; + } + } + /** + * Delete entire database (for testing/reset) + */ + static async deleteDatabase() { + return new Promise((resolve, reject) => { + const request = indexedDB.deleteDatabase(_IndexedDBContext.DB_NAME); + request.onsuccess = () => resolve(); + request.onerror = () => reject(new Error(`Failed to delete database: ${request.error}`)); + }); + } +}; +__name(_IndexedDBContext, "IndexedDBContext"); +var IndexedDBContext = _IndexedDBContext; +IndexedDBContext.DB_NAME = "CalendarDB"; +IndexedDBContext.DB_VERSION = 4; + +// src/storage/events/EventStore.ts +var _EventStore = class _EventStore { + constructor() { + this.storeName = _EventStore.STORE_NAME; + } + /** + * Create the events ObjectStore with indexes + */ + create(db) { + const store = db.createObjectStore(_EventStore.STORE_NAME, { keyPath: "id" }); + store.createIndex("start", "start", { unique: false }); + store.createIndex("end", "end", { unique: false }); + store.createIndex("syncStatus", "syncStatus", { unique: false }); + store.createIndex("resourceId", "resourceId", { unique: false }); + store.createIndex("customerId", "customerId", { unique: false }); + store.createIndex("bookingId", "bookingId", { unique: false }); + store.createIndex("startEnd", ["start", "end"], { unique: false }); + } +}; +__name(_EventStore, "EventStore"); +var EventStore = _EventStore; +EventStore.STORE_NAME = "events"; + +// src/storage/events/EventSerialization.ts +var _EventSerialization = class _EventSerialization { + /** + * Serialize event for IndexedDB storage + */ + static serialize(event) { + return { + ...event, + start: event.start instanceof Date ? event.start.toISOString() : event.start, + end: event.end instanceof Date ? event.end.toISOString() : event.end + }; + } + /** + * Deserialize event from IndexedDB storage + */ + static deserialize(data) { + return { + ...data, + start: typeof data.start === "string" ? new Date(data.start) : data.start, + end: typeof data.end === "string" ? new Date(data.end) : data.end + }; + } +}; +__name(_EventSerialization, "EventSerialization"); +var EventSerialization = _EventSerialization; + +// src/storage/SyncPlugin.ts +var _SyncPlugin = class _SyncPlugin { + constructor(service) { + this.service = service; + } + /** + * Mark entity as successfully synced + */ + async markAsSynced(id) { + const entity = await this.service.get(id); + if (entity) { + entity.syncStatus = "synced"; + await this.service.save(entity); + } + } + /** + * Mark entity as sync error + */ + async markAsError(id) { + const entity = await this.service.get(id); + if (entity) { + entity.syncStatus = "error"; + await this.service.save(entity); + } + } + /** + * Get current sync status for an entity + */ + async getSyncStatus(id) { + const entity = await this.service.get(id); + return entity ? entity.syncStatus : null; + } + /** + * Get entities by sync status using IndexedDB index + */ + async getBySyncStatus(syncStatus) { + return new Promise((resolve, reject) => { + const transaction = this.service.db.transaction([this.service.storeName], "readonly"); + const store = transaction.objectStore(this.service.storeName); + const index = store.index("syncStatus"); + const request = index.getAll(syncStatus); + request.onsuccess = () => { + const data = request.result; + const entities = data.map((item) => this.service.deserialize(item)); + resolve(entities); + }; + request.onerror = () => { + reject(new Error(`Failed to get by sync status ${syncStatus}: ${request.error}`)); + }; + }); + } +}; +__name(_SyncPlugin, "SyncPlugin"); +var SyncPlugin = _SyncPlugin; + +// src/constants/CoreEvents.ts +var CoreEvents = { + // Lifecycle events + INITIALIZED: "core:initialized", + READY: "core:ready", + DESTROYED: "core:destroyed", + // View events + VIEW_CHANGED: "view:changed", + VIEW_RENDERED: "view:rendered", + // Navigation events + DATE_CHANGED: "nav:date-changed", + NAVIGATION_COMPLETED: "nav:navigation-completed", + // Data events + DATA_LOADING: "data:loading", + DATA_LOADED: "data:loaded", + DATA_ERROR: "data:error", + // Grid events + GRID_RENDERED: "grid:rendered", + GRID_CLICKED: "grid:clicked", + // Event management + EVENT_CREATED: "event:created", + EVENT_UPDATED: "event:updated", + EVENT_DELETED: "event:deleted", + EVENT_SELECTED: "event:selected", + // Event drag-drop + EVENT_DRAG_START: "event:drag-start", + EVENT_DRAG_MOVE: "event:drag-move", + EVENT_DRAG_END: "event:drag-end", + EVENT_DRAG_CANCEL: "event:drag-cancel", + EVENT_DRAG_COLUMN_CHANGE: "event:drag-column-change", + // Header drag (timed → header conversion) + EVENT_DRAG_ENTER_HEADER: "event:drag-enter-header", + EVENT_DRAG_MOVE_HEADER: "event:drag-move-header", + EVENT_DRAG_LEAVE_HEADER: "event:drag-leave-header", + // Event resize + EVENT_RESIZE_START: "event:resize-start", + EVENT_RESIZE_END: "event:resize-end", + // Edge scroll + EDGE_SCROLL_TICK: "edge-scroll:tick", + EDGE_SCROLL_STARTED: "edge-scroll:started", + EDGE_SCROLL_STOPPED: "edge-scroll:stopped", + // System events + ERROR: "system:error", + // Sync events + SYNC_STARTED: "sync:started", + SYNC_COMPLETED: "sync:completed", + SYNC_FAILED: "sync:failed", + // Entity events - for audit and sync + ENTITY_SAVED: "entity:saved", + ENTITY_DELETED: "entity:deleted", + // Audit events + AUDIT_LOGGED: "audit:logged", + // Rendering events + EVENTS_RENDERED: "events:rendered" +}; + +// node_modules/json-diff-ts/dist/index.js +function arrayDifference(first, second) { + const secondSet = new Set(second); + return first.filter((item) => !secondSet.has(item)); +} +__name(arrayDifference, "arrayDifference"); +function arrayIntersection(first, second) { + const secondSet = new Set(second); + return first.filter((item) => secondSet.has(item)); +} +__name(arrayIntersection, "arrayIntersection"); +function keyBy(arr, getKey2) { + const result = {}; + for (const item of arr) { + result[String(getKey2(item))] = item; + } + return result; +} +__name(keyBy, "keyBy"); +function diff(oldObj, newObj, options = {}) { + let { embeddedObjKeys } = options; + const { keysToSkip, treatTypeChangeAsReplace } = options; + if (embeddedObjKeys instanceof Map) { + embeddedObjKeys = new Map( + Array.from(embeddedObjKeys.entries()).map(([key, value]) => [ + key instanceof RegExp ? key : key.replace(/^\./, ""), + value + ]) + ); + } else if (embeddedObjKeys) { + embeddedObjKeys = Object.fromEntries( + Object.entries(embeddedObjKeys).map(([key, value]) => [key.replace(/^\./, ""), value]) + ); + } + return compare(oldObj, newObj, [], [], { + embeddedObjKeys, + keysToSkip: keysToSkip ?? [], + treatTypeChangeAsReplace: treatTypeChangeAsReplace ?? true + }); +} +__name(diff, "diff"); +var getTypeOfObj = /* @__PURE__ */ __name((obj) => { + if (typeof obj === "undefined") { + return "undefined"; + } + if (obj === null) { + return null; + } + return Object.prototype.toString.call(obj).match(/^\[object\s(.*)\]$/)[1]; +}, "getTypeOfObj"); +var getKey = /* @__PURE__ */ __name((path) => { + const left = path[path.length - 1]; + return left != null ? left : "$root"; +}, "getKey"); +var compare = /* @__PURE__ */ __name((oldObj, newObj, path, keyPath, options) => { + let changes = []; + const currentPath = keyPath.join("."); + if (options.keysToSkip?.some((skipPath) => { + if (currentPath === skipPath) { + return true; + } + if (skipPath.includes(".") && skipPath.startsWith(currentPath + ".")) { + return false; + } + if (skipPath.includes(".")) { + const skipParts = skipPath.split("."); + const currentParts = currentPath.split("."); + if (currentParts.length >= skipParts.length) { + for (let i = 0; i < skipParts.length; i++) { + if (skipParts[i] !== currentParts[i]) { + return false; + } + } + return true; + } + } + return false; + })) { + return changes; + } + const typeOfOldObj = getTypeOfObj(oldObj); + const typeOfNewObj = getTypeOfObj(newObj); + if (options.treatTypeChangeAsReplace && typeOfOldObj !== typeOfNewObj) { + if (typeOfOldObj !== "undefined") { + changes.push({ type: "REMOVE", key: getKey(path), value: oldObj }); + } + if (typeOfNewObj !== "undefined") { + changes.push({ type: "ADD", key: getKey(path), value: newObj }); + } + return changes; + } + if (typeOfNewObj === "undefined" && typeOfOldObj !== "undefined") { + changes.push({ type: "REMOVE", key: getKey(path), value: oldObj }); + return changes; + } + if (typeOfNewObj === "Object" && typeOfOldObj === "Array") { + changes.push({ type: "UPDATE", key: getKey(path), value: newObj, oldValue: oldObj }); + return changes; + } + if (typeOfNewObj === null) { + if (typeOfOldObj !== null) { + changes.push({ type: "UPDATE", key: getKey(path), value: newObj, oldValue: oldObj }); + } + return changes; + } + switch (typeOfOldObj) { + case "Date": + if (typeOfNewObj === "Date") { + changes = changes.concat( + comparePrimitives(oldObj.getTime(), newObj.getTime(), path).map((x) => ({ + ...x, + value: new Date(x.value), + oldValue: new Date(x.oldValue) + })) + ); + } else { + changes = changes.concat(comparePrimitives(oldObj, newObj, path)); + } + break; + case "Object": { + const diffs = compareObject(oldObj, newObj, path, keyPath, false, options); + if (diffs.length) { + if (path.length) { + changes.push({ + type: "UPDATE", + key: getKey(path), + changes: diffs + }); + } else { + changes = changes.concat(diffs); + } + } + break; + } + case "Array": + changes = changes.concat(compareArray(oldObj, newObj, path, keyPath, options)); + break; + case "Function": + break; + default: + changes = changes.concat(comparePrimitives(oldObj, newObj, path)); + } + return changes; +}, "compare"); +var compareObject = /* @__PURE__ */ __name((oldObj, newObj, path, keyPath, skipPath = false, options = {}) => { + let k; + let newKeyPath; + let newPath; + if (skipPath == null) { + skipPath = false; + } + let changes = []; + const oldObjKeys = Object.keys(oldObj); + const newObjKeys = Object.keys(newObj); + const intersectionKeys = arrayIntersection(oldObjKeys, newObjKeys); + for (k of intersectionKeys) { + newPath = path.concat([k]); + newKeyPath = skipPath ? keyPath : keyPath.concat([k]); + const diffs = compare(oldObj[k], newObj[k], newPath, newKeyPath, options); + if (diffs.length) { + changes = changes.concat(diffs); + } + } + const addedKeys = arrayDifference(newObjKeys, oldObjKeys); + for (k of addedKeys) { + newPath = path.concat([k]); + newKeyPath = skipPath ? keyPath : keyPath.concat([k]); + const currentPath = newKeyPath.join("."); + if (options.keysToSkip?.some((skipPath2) => currentPath === skipPath2 || currentPath.startsWith(skipPath2 + "."))) { + continue; + } + changes.push({ + type: "ADD", + key: getKey(newPath), + value: newObj[k] + }); + } + const deletedKeys = arrayDifference(oldObjKeys, newObjKeys); + for (k of deletedKeys) { + newPath = path.concat([k]); + newKeyPath = skipPath ? keyPath : keyPath.concat([k]); + const currentPath = newKeyPath.join("."); + if (options.keysToSkip?.some((skipPath2) => currentPath === skipPath2 || currentPath.startsWith(skipPath2 + "."))) { + continue; + } + changes.push({ + type: "REMOVE", + key: getKey(newPath), + value: oldObj[k] + }); + } + return changes; +}, "compareObject"); +var compareArray = /* @__PURE__ */ __name((oldObj, newObj, path, keyPath, options) => { + if (getTypeOfObj(newObj) !== "Array") { + return [{ type: "UPDATE", key: getKey(path), value: newObj, oldValue: oldObj }]; + } + const left = getObjectKey(options.embeddedObjKeys, keyPath); + const uniqKey = left != null ? left : "$index"; + const indexedOldObj = convertArrayToObj(oldObj, uniqKey); + const indexedNewObj = convertArrayToObj(newObj, uniqKey); + const diffs = compareObject(indexedOldObj, indexedNewObj, path, keyPath, true, options); + if (diffs.length) { + return [ + { + type: "UPDATE", + key: getKey(path), + embeddedKey: typeof uniqKey === "function" && uniqKey.length === 2 ? uniqKey(newObj[0], true) : uniqKey, + changes: diffs + } + ]; + } else { + return []; + } +}, "compareArray"); +var getObjectKey = /* @__PURE__ */ __name((embeddedObjKeys, keyPath) => { + if (embeddedObjKeys != null) { + const path = keyPath.join("."); + if (embeddedObjKeys instanceof Map) { + for (const [key2, value] of embeddedObjKeys.entries()) { + if (key2 instanceof RegExp) { + if (path.match(key2)) { + return value; + } + } else if (path === key2) { + return value; + } + } + } + const key = embeddedObjKeys[path]; + if (key != null) { + return key; + } + } + return void 0; +}, "getObjectKey"); +var convertArrayToObj = /* @__PURE__ */ __name((arr, uniqKey) => { + let obj = {}; + if (uniqKey === "$value") { + arr.forEach((value) => { + obj[value] = value; + }); + } else if (uniqKey !== "$index") { + const keyFunction = typeof uniqKey === "string" ? (item) => item[uniqKey] : uniqKey; + obj = keyBy(arr, keyFunction); + } else { + for (let i = 0; i < arr.length; i++) { + const value = arr[i]; + obj[i] = value; + } + } + return obj; +}, "convertArrayToObj"); +var comparePrimitives = /* @__PURE__ */ __name((oldObj, newObj, path) => { + const changes = []; + if (oldObj !== newObj) { + changes.push({ + type: "UPDATE", + key: getKey(path), + value: newObj, + oldValue: oldObj + }); + } + return changes; +}, "comparePrimitives"); + +// src/storage/BaseEntityService.ts +var _BaseEntityService = class _BaseEntityService { + constructor(context, eventBus) { + this.context = context; + this.eventBus = eventBus; + this.syncPlugin = new SyncPlugin(this); + } + get db() { + return this.context.getDatabase(); + } + /** + * Serialize entity before storing in IndexedDB + */ + serialize(entity) { + return entity; + } + /** + * Deserialize data from IndexedDB back to entity + */ + deserialize(data) { + return data; + } + /** + * Get a single entity by ID + */ + async get(id) { + return new Promise((resolve, reject) => { + const transaction = this.db.transaction([this.storeName], "readonly"); + const store = transaction.objectStore(this.storeName); + const request = store.get(id); + request.onsuccess = () => { + const data = request.result; + resolve(data ? this.deserialize(data) : null); + }; + request.onerror = () => { + reject(new Error(`Failed to get ${this.entityType} ${id}: ${request.error}`)); + }; + }); + } + /** + * Get all entities + */ + async getAll() { + return new Promise((resolve, reject) => { + const transaction = this.db.transaction([this.storeName], "readonly"); + const store = transaction.objectStore(this.storeName); + const request = store.getAll(); + request.onsuccess = () => { + const data = request.result; + const entities = data.map((item) => this.deserialize(item)); + resolve(entities); + }; + request.onerror = () => { + reject(new Error(`Failed to get all ${this.entityType}s: ${request.error}`)); + }; + }); + } + /** + * Save an entity (create or update) + * Emits ENTITY_SAVED event with operation type and changes (diff for updates) + * @param entity - Entity to save + * @param silent - If true, skip event emission (used for seeding) + */ + async save(entity, silent = false) { + const entityId = entity.id; + const existingEntity = await this.get(entityId); + const isCreate = existingEntity === null; + let changes; + if (isCreate) { + changes = entity; + } else { + const existingSerialized = this.serialize(existingEntity); + const newSerialized = this.serialize(entity); + changes = diff(existingSerialized, newSerialized); + } + const serialized = this.serialize(entity); + return new Promise((resolve, reject) => { + const transaction = this.db.transaction([this.storeName], "readwrite"); + const store = transaction.objectStore(this.storeName); + const request = store.put(serialized); + request.onsuccess = () => { + if (!silent) { + const payload = { + entityType: this.entityType, + entityId, + operation: isCreate ? "create" : "update", + changes, + timestamp: Date.now() + }; + this.eventBus.emit(CoreEvents.ENTITY_SAVED, payload); + } + resolve(); + }; + request.onerror = () => { + reject(new Error(`Failed to save ${this.entityType} ${entityId}: ${request.error}`)); + }; + }); + } + /** + * Delete an entity + * Emits ENTITY_DELETED event + */ + async delete(id) { + return new Promise((resolve, reject) => { + const transaction = this.db.transaction([this.storeName], "readwrite"); + const store = transaction.objectStore(this.storeName); + const request = store.delete(id); + request.onsuccess = () => { + const payload = { + entityType: this.entityType, + entityId: id, + operation: "delete", + timestamp: Date.now() + }; + this.eventBus.emit(CoreEvents.ENTITY_DELETED, payload); + resolve(); + }; + request.onerror = () => { + reject(new Error(`Failed to delete ${this.entityType} ${id}: ${request.error}`)); + }; + }); + } + // Sync methods - delegate to SyncPlugin + async markAsSynced(id) { + return this.syncPlugin.markAsSynced(id); + } + async markAsError(id) { + return this.syncPlugin.markAsError(id); + } + async getSyncStatus(id) { + return this.syncPlugin.getSyncStatus(id); + } + async getBySyncStatus(syncStatus) { + return this.syncPlugin.getBySyncStatus(syncStatus); + } +}; +__name(_BaseEntityService, "BaseEntityService"); +var BaseEntityService = _BaseEntityService; + +// src/storage/events/EventService.ts +var _EventService = class _EventService extends BaseEntityService { + constructor(context, eventBus) { + super(context, eventBus); + this.storeName = EventStore.STORE_NAME; + this.entityType = "Event"; + } + serialize(event) { + return EventSerialization.serialize(event); + } + deserialize(data) { + return EventSerialization.deserialize(data); + } + /** + * Get events within a date range + */ + async getByDateRange(start, end) { + return new Promise((resolve, reject) => { + const transaction = this.db.transaction([this.storeName], "readonly"); + const store = transaction.objectStore(this.storeName); + const index = store.index("start"); + const range = IDBKeyRange.lowerBound(start.toISOString()); + const request = index.getAll(range); + request.onsuccess = () => { + const data = request.result; + const events = data.map((item) => this.deserialize(item)).filter((event) => event.start <= end); + resolve(events); + }; + request.onerror = () => { + reject(new Error(`Failed to get events by date range: ${request.error}`)); + }; + }); + } + /** + * Get events for a specific resource + */ + async getByResource(resourceId) { + return new Promise((resolve, reject) => { + const transaction = this.db.transaction([this.storeName], "readonly"); + const store = transaction.objectStore(this.storeName); + const index = store.index("resourceId"); + const request = index.getAll(resourceId); + request.onsuccess = () => { + const data = request.result; + const events = data.map((item) => this.deserialize(item)); + resolve(events); + }; + request.onerror = () => { + reject(new Error(`Failed to get events for resource ${resourceId}: ${request.error}`)); + }; + }); + } + /** + * Get events for a resource within a date range + */ + async getByResourceAndDateRange(resourceId, start, end) { + const resourceEvents = await this.getByResource(resourceId); + return resourceEvents.filter((event) => event.start >= start && event.start <= end); + } +}; +__name(_EventService, "EventService"); +var EventService = _EventService; + +// src/storage/resources/ResourceStore.ts +var _ResourceStore = class _ResourceStore { + constructor() { + this.storeName = _ResourceStore.STORE_NAME; + } + create(db) { + const store = db.createObjectStore(_ResourceStore.STORE_NAME, { keyPath: "id" }); + store.createIndex("type", "type", { unique: false }); + store.createIndex("syncStatus", "syncStatus", { unique: false }); + store.createIndex("isActive", "isActive", { unique: false }); + } +}; +__name(_ResourceStore, "ResourceStore"); +var ResourceStore = _ResourceStore; +ResourceStore.STORE_NAME = "resources"; + +// src/storage/resources/ResourceService.ts +var _ResourceService = class _ResourceService extends BaseEntityService { + constructor(context, eventBus) { + super(context, eventBus); + this.storeName = ResourceStore.STORE_NAME; + this.entityType = "Resource"; + } + /** + * Get all active resources + */ + async getActive() { + const all = await this.getAll(); + return all.filter((r) => r.isActive !== false); + } + /** + * Get resources by IDs + */ + async getByIds(ids) { + if (ids.length === 0) + return []; + const results = await Promise.all(ids.map((id) => this.get(id))); + return results.filter((r) => r !== null); + } + /** + * Get resources by type + */ + async getByType(type) { + return new Promise((resolve, reject) => { + const transaction = this.db.transaction([this.storeName], "readonly"); + const store = transaction.objectStore(this.storeName); + const index = store.index("type"); + const request = index.getAll(type); + request.onsuccess = () => { + const data = request.result; + resolve(data); + }; + request.onerror = () => { + reject(new Error(`Failed to get resources by type ${type}: ${request.error}`)); + }; + }); + } +}; +__name(_ResourceService, "ResourceService"); +var ResourceService = _ResourceService; + +// src/storage/bookings/BookingStore.ts +var _BookingStore = class _BookingStore { + constructor() { + this.storeName = _BookingStore.STORE_NAME; + } + create(db) { + const store = db.createObjectStore(_BookingStore.STORE_NAME, { keyPath: "id" }); + store.createIndex("customerId", "customerId", { unique: false }); + store.createIndex("status", "status", { unique: false }); + store.createIndex("syncStatus", "syncStatus", { unique: false }); + store.createIndex("createdAt", "createdAt", { unique: false }); + } +}; +__name(_BookingStore, "BookingStore"); +var BookingStore = _BookingStore; +BookingStore.STORE_NAME = "bookings"; + +// src/storage/bookings/BookingService.ts +var _BookingService = class _BookingService extends BaseEntityService { + constructor(context, eventBus) { + super(context, eventBus); + this.storeName = BookingStore.STORE_NAME; + this.entityType = "Booking"; + } + serialize(booking) { + return { + ...booking, + createdAt: booking.createdAt.toISOString() + }; + } + deserialize(data) { + const raw = data; + return { + ...raw, + createdAt: new Date(raw.createdAt) + }; + } + /** + * Get bookings for a customer + */ + async getByCustomer(customerId) { + return new Promise((resolve, reject) => { + const transaction = this.db.transaction([this.storeName], "readonly"); + const store = transaction.objectStore(this.storeName); + const index = store.index("customerId"); + const request = index.getAll(customerId); + request.onsuccess = () => { + const data = request.result; + const bookings = data.map((item) => this.deserialize(item)); + resolve(bookings); + }; + request.onerror = () => { + reject(new Error(`Failed to get bookings for customer ${customerId}: ${request.error}`)); + }; + }); + } + /** + * Get bookings by status + */ + async getByStatus(status) { + return new Promise((resolve, reject) => { + const transaction = this.db.transaction([this.storeName], "readonly"); + const store = transaction.objectStore(this.storeName); + const index = store.index("status"); + const request = index.getAll(status); + request.onsuccess = () => { + const data = request.result; + const bookings = data.map((item) => this.deserialize(item)); + resolve(bookings); + }; + request.onerror = () => { + reject(new Error(`Failed to get bookings with status ${status}: ${request.error}`)); + }; + }); + } +}; +__name(_BookingService, "BookingService"); +var BookingService = _BookingService; + +// src/storage/customers/CustomerStore.ts +var _CustomerStore = class _CustomerStore { + constructor() { + this.storeName = _CustomerStore.STORE_NAME; + } + create(db) { + const store = db.createObjectStore(_CustomerStore.STORE_NAME, { keyPath: "id" }); + store.createIndex("name", "name", { unique: false }); + store.createIndex("phone", "phone", { unique: false }); + store.createIndex("syncStatus", "syncStatus", { unique: false }); + } +}; +__name(_CustomerStore, "CustomerStore"); +var CustomerStore = _CustomerStore; +CustomerStore.STORE_NAME = "customers"; + +// src/storage/customers/CustomerService.ts +var _CustomerService = class _CustomerService extends BaseEntityService { + constructor(context, eventBus) { + super(context, eventBus); + this.storeName = CustomerStore.STORE_NAME; + this.entityType = "Customer"; + } + /** + * Search customers by name (case-insensitive contains) + */ + async searchByName(query) { + const all = await this.getAll(); + const lowerQuery = query.toLowerCase(); + return all.filter((c) => c.name.toLowerCase().includes(lowerQuery)); + } + /** + * Find customer by phone + */ + async getByPhone(phone) { + return new Promise((resolve, reject) => { + const transaction = this.db.transaction([this.storeName], "readonly"); + const store = transaction.objectStore(this.storeName); + const index = store.index("phone"); + const request = index.get(phone); + request.onsuccess = () => { + const data = request.result; + resolve(data ? data : null); + }; + request.onerror = () => { + reject(new Error(`Failed to find customer by phone ${phone}: ${request.error}`)); + }; + }); + } +}; +__name(_CustomerService, "CustomerService"); +var CustomerService = _CustomerService; + +// src/storage/teams/TeamStore.ts +var _TeamStore = class _TeamStore { + constructor() { + this.storeName = _TeamStore.STORE_NAME; + } + create(db) { + db.createObjectStore(_TeamStore.STORE_NAME, { keyPath: "id" }); + } +}; +__name(_TeamStore, "TeamStore"); +var TeamStore = _TeamStore; +TeamStore.STORE_NAME = "teams"; + +// src/storage/teams/TeamService.ts +var _TeamService = class _TeamService extends BaseEntityService { + constructor(context, eventBus) { + super(context, eventBus); + this.storeName = TeamStore.STORE_NAME; + this.entityType = "Team"; + } + /** + * Get teams by IDs + */ + async getByIds(ids) { + if (ids.length === 0) + return []; + const results = await Promise.all(ids.map((id) => this.get(id))); + return results.filter((t) => t !== null); + } + /** + * Build reverse lookup: resourceId → teamId + */ + async buildResourceToTeamMap() { + const teams = await this.getAll(); + const map = {}; + for (const team of teams) { + for (const resourceId of team.resourceIds) { + map[resourceId] = team.id; + } + } + return map; + } +}; +__name(_TeamService, "TeamService"); +var TeamService = _TeamService; + +// src/storage/departments/DepartmentStore.ts +var _DepartmentStore = class _DepartmentStore { + constructor() { + this.storeName = _DepartmentStore.STORE_NAME; + } + create(db) { + db.createObjectStore(_DepartmentStore.STORE_NAME, { keyPath: "id" }); + } +}; +__name(_DepartmentStore, "DepartmentStore"); +var DepartmentStore = _DepartmentStore; +DepartmentStore.STORE_NAME = "departments"; + +// src/storage/departments/DepartmentService.ts +var _DepartmentService = class _DepartmentService extends BaseEntityService { + constructor(context, eventBus) { + super(context, eventBus); + this.storeName = DepartmentStore.STORE_NAME; + this.entityType = "Department"; + } + /** + * Get departments by IDs + */ + async getByIds(ids) { + if (ids.length === 0) + return []; + const results = await Promise.all(ids.map((id) => this.get(id))); + return results.filter((d) => d !== null); + } +}; +__name(_DepartmentService, "DepartmentService"); +var DepartmentService = _DepartmentService; + +// src/storage/settings/SettingsStore.ts +var _SettingsStore = class _SettingsStore { + constructor() { + this.storeName = _SettingsStore.STORE_NAME; + } + create(db) { + db.createObjectStore(_SettingsStore.STORE_NAME, { keyPath: "id" }); + } +}; +__name(_SettingsStore, "SettingsStore"); +var SettingsStore = _SettingsStore; +SettingsStore.STORE_NAME = "settings"; + +// src/types/SettingsTypes.ts +var SettingsIds = { + WORKWEEK: "workweek", + GRID: "grid", + TIME_FORMAT: "timeFormat", + VIEWS: "views" +}; + +// src/storage/settings/SettingsService.ts +var _SettingsService = class _SettingsService extends BaseEntityService { + constructor(context, eventBus) { + super(context, eventBus); + this.storeName = SettingsStore.STORE_NAME; + this.entityType = "Settings"; + } + /** + * Get workweek settings + */ + async getWorkweekSettings() { + return this.get(SettingsIds.WORKWEEK); + } + /** + * Get grid settings + */ + async getGridSettings() { + return this.get(SettingsIds.GRID); + } + /** + * Get time format settings + */ + async getTimeFormatSettings() { + return this.get(SettingsIds.TIME_FORMAT); + } + /** + * Get view settings + */ + async getViewSettings() { + return this.get(SettingsIds.VIEWS); + } + /** + * Get workweek preset by ID + */ + async getWorkweekPreset(presetId) { + const settings = await this.getWorkweekSettings(); + if (!settings) + return null; + return settings.presets[presetId] || null; + } + /** + * Get the default workweek preset + */ + async getDefaultWorkweekPreset() { + const settings = await this.getWorkweekSettings(); + if (!settings) + return null; + return settings.presets[settings.defaultPreset] || null; + } + /** + * Get all available workweek presets + */ + async getWorkweekPresets() { + const settings = await this.getWorkweekSettings(); + if (!settings) + return []; + return Object.values(settings.presets); + } +}; +__name(_SettingsService, "SettingsService"); +var SettingsService = _SettingsService; + +// src/storage/viewconfigs/ViewConfigStore.ts +var _ViewConfigStore = class _ViewConfigStore { + constructor() { + this.storeName = _ViewConfigStore.STORE_NAME; + } + create(db) { + db.createObjectStore(_ViewConfigStore.STORE_NAME, { keyPath: "id" }); + } +}; +__name(_ViewConfigStore, "ViewConfigStore"); +var ViewConfigStore = _ViewConfigStore; +ViewConfigStore.STORE_NAME = "viewconfigs"; + +// src/storage/viewconfigs/ViewConfigService.ts +var _ViewConfigService = class _ViewConfigService extends BaseEntityService { + constructor(context, eventBus) { + super(context, eventBus); + this.storeName = ViewConfigStore.STORE_NAME; + this.entityType = "ViewConfig"; + } + async getById(id) { + return this.get(id); + } +}; +__name(_ViewConfigService, "ViewConfigService"); +var ViewConfigService = _ViewConfigService; + +// src/storage/audit/AuditStore.ts +var _AuditStore = class _AuditStore { + constructor() { + this.storeName = "audit"; + } + create(db) { + const store = db.createObjectStore(this.storeName, { keyPath: "id" }); + store.createIndex("syncStatus", "syncStatus", { unique: false }); + store.createIndex("synced", "synced", { unique: false }); + store.createIndex("entityId", "entityId", { unique: false }); + store.createIndex("timestamp", "timestamp", { unique: false }); + } +}; +__name(_AuditStore, "AuditStore"); +var AuditStore = _AuditStore; + +// src/storage/audit/AuditService.ts +var _AuditService = class _AuditService extends BaseEntityService { + constructor(context, eventBus) { + super(context, eventBus); + this.storeName = "audit"; + this.entityType = "Audit"; + this.setupEventListeners(); + } + /** + * Setup listeners for ENTITY_SAVED and ENTITY_DELETED events + */ + setupEventListeners() { + this.eventBus.on(CoreEvents.ENTITY_SAVED, (event) => { + const detail = event.detail; + this.handleEntitySaved(detail); + }); + this.eventBus.on(CoreEvents.ENTITY_DELETED, (event) => { + const detail = event.detail; + this.handleEntityDeleted(detail); + }); + } + /** + * Handle ENTITY_SAVED event - create audit entry + */ + async handleEntitySaved(payload) { + if (payload.entityType === "Audit") + return; + const auditEntry = { + id: crypto.randomUUID(), + entityType: payload.entityType, + entityId: payload.entityId, + operation: payload.operation, + userId: _AuditService.DEFAULT_USER_ID, + timestamp: payload.timestamp, + changes: payload.changes, + synced: false, + syncStatus: "pending" + }; + await this.save(auditEntry); + } + /** + * Handle ENTITY_DELETED event - create audit entry + */ + async handleEntityDeleted(payload) { + if (payload.entityType === "Audit") + return; + const auditEntry = { + id: crypto.randomUUID(), + entityType: payload.entityType, + entityId: payload.entityId, + operation: "delete", + userId: _AuditService.DEFAULT_USER_ID, + timestamp: payload.timestamp, + changes: { id: payload.entityId }, + // For delete, just store the ID + synced: false, + syncStatus: "pending" + }; + await this.save(auditEntry); + } + /** + * Override save to NOT trigger ENTITY_SAVED event + * Instead, emits AUDIT_LOGGED for SyncManager to listen + * + * This prevents infinite loops: + * - BaseEntityService.save() emits ENTITY_SAVED + * - AuditService listens to ENTITY_SAVED and creates audit + * - If AuditService.save() also emitted ENTITY_SAVED, it would loop + */ + async save(entity) { + const serialized = this.serialize(entity); + return new Promise((resolve, reject) => { + const transaction = this.db.transaction([this.storeName], "readwrite"); + const store = transaction.objectStore(this.storeName); + const request = store.put(serialized); + request.onsuccess = () => { + const payload = { + auditId: entity.id, + entityType: entity.entityType, + entityId: entity.entityId, + operation: entity.operation, + timestamp: entity.timestamp + }; + this.eventBus.emit(CoreEvents.AUDIT_LOGGED, payload); + resolve(); + }; + request.onerror = () => { + reject(new Error(`Failed to save audit entry ${entity.id}: ${request.error}`)); + }; + }); + } + /** + * Override delete to NOT trigger ENTITY_DELETED event + * Audit entries should never be deleted (compliance requirement) + */ + async delete(_id) { + throw new Error("Audit entries cannot be deleted (compliance requirement)"); + } + /** + * Get pending audit entries (for sync) + */ + async getPendingAudits() { + return this.getBySyncStatus("pending"); + } + /** + * Get audit entries for a specific entity + */ + async getByEntityId(entityId) { + return new Promise((resolve, reject) => { + const transaction = this.db.transaction([this.storeName], "readonly"); + const store = transaction.objectStore(this.storeName); + const index = store.index("entityId"); + const request = index.getAll(entityId); + request.onsuccess = () => { + const entries = request.result; + resolve(entries); + }; + request.onerror = () => { + reject(new Error(`Failed to get audit entries for entity ${entityId}: ${request.error}`)); + }; + }); + } +}; +__name(_AuditService, "AuditService"); +var AuditService = _AuditService; +AuditService.DEFAULT_USER_ID = "00000000-0000-0000-0000-000000000001"; + +// src/repositories/MockEventRepository.ts +var _MockEventRepository = class _MockEventRepository { + constructor() { + this.entityType = "Event"; + this.dataUrl = "data/mock-events.json"; + } + /** + * Fetch all events from mock JSON file + */ + async fetchAll() { + try { + const response = await fetch(this.dataUrl); + if (!response.ok) { + throw new Error(`Failed to load mock events: ${response.status} ${response.statusText}`); + } + const rawData = await response.json(); + return this.processCalendarData(rawData); + } catch (error) { + console.error("Failed to load event data:", error); + throw error; + } + } + async sendCreate(_event) { + throw new Error("MockEventRepository does not support sendCreate. Mock data is read-only."); + } + async sendUpdate(_id, _updates) { + throw new Error("MockEventRepository does not support sendUpdate. Mock data is read-only."); + } + async sendDelete(_id) { + throw new Error("MockEventRepository does not support sendDelete. Mock data is read-only."); + } + processCalendarData(data) { + return data.map((event) => { + if (event.type === "customer") { + if (!event.bookingId) + console.warn(`Customer event ${event.id} missing bookingId`); + if (!event.resourceId) + console.warn(`Customer event ${event.id} missing resourceId`); + if (!event.customerId) + console.warn(`Customer event ${event.id} missing customerId`); + } + return { + id: event.id, + title: event.title, + description: event.description, + start: new Date(event.start), + end: new Date(event.end), + type: event.type, + allDay: event.allDay || false, + bookingId: event.bookingId, + resourceId: event.resourceId, + customerId: event.customerId, + recurringId: event.recurringId, + metadata: event.metadata, + syncStatus: "synced" + }; + }); + } +}; +__name(_MockEventRepository, "MockEventRepository"); +var MockEventRepository = _MockEventRepository; + +// src/repositories/MockResourceRepository.ts +var _MockResourceRepository = class _MockResourceRepository { + constructor() { + this.entityType = "Resource"; + this.dataUrl = "data/mock-resources.json"; + } + async fetchAll() { + try { + const response = await fetch(this.dataUrl); + if (!response.ok) { + throw new Error(`Failed to load mock resources: ${response.status} ${response.statusText}`); + } + const rawData = await response.json(); + return this.processResourceData(rawData); + } catch (error) { + console.error("Failed to load resource data:", error); + throw error; + } + } + async sendCreate(_resource) { + throw new Error("MockResourceRepository does not support sendCreate. Mock data is read-only."); + } + async sendUpdate(_id, _updates) { + throw new Error("MockResourceRepository does not support sendUpdate. Mock data is read-only."); + } + async sendDelete(_id) { + throw new Error("MockResourceRepository does not support sendDelete. Mock data is read-only."); + } + processResourceData(data) { + return data.map((resource) => ({ + id: resource.id, + name: resource.name, + displayName: resource.displayName, + type: resource.type, + avatarUrl: resource.avatarUrl, + color: resource.color, + isActive: resource.isActive, + defaultSchedule: resource.defaultSchedule, + metadata: resource.metadata, + syncStatus: "synced" + })); + } +}; +__name(_MockResourceRepository, "MockResourceRepository"); +var MockResourceRepository = _MockResourceRepository; + +// src/repositories/MockBookingRepository.ts +var _MockBookingRepository = class _MockBookingRepository { + constructor() { + this.entityType = "Booking"; + this.dataUrl = "data/mock-bookings.json"; + } + async fetchAll() { + try { + const response = await fetch(this.dataUrl); + if (!response.ok) { + throw new Error(`Failed to load mock bookings: ${response.status} ${response.statusText}`); + } + const rawData = await response.json(); + return this.processBookingData(rawData); + } catch (error) { + console.error("Failed to load booking data:", error); + throw error; + } + } + async sendCreate(_booking) { + throw new Error("MockBookingRepository does not support sendCreate. Mock data is read-only."); + } + async sendUpdate(_id, _updates) { + throw new Error("MockBookingRepository does not support sendUpdate. Mock data is read-only."); + } + async sendDelete(_id) { + throw new Error("MockBookingRepository does not support sendDelete. Mock data is read-only."); + } + processBookingData(data) { + return data.map((booking) => ({ + id: booking.id, + customerId: booking.customerId, + status: booking.status, + createdAt: new Date(booking.createdAt), + services: booking.services, + totalPrice: booking.totalPrice, + tags: booking.tags, + notes: booking.notes, + syncStatus: "synced" + })); + } +}; +__name(_MockBookingRepository, "MockBookingRepository"); +var MockBookingRepository = _MockBookingRepository; + +// src/repositories/MockCustomerRepository.ts +var _MockCustomerRepository = class _MockCustomerRepository { + constructor() { + this.entityType = "Customer"; + this.dataUrl = "data/mock-customers.json"; + } + async fetchAll() { + try { + const response = await fetch(this.dataUrl); + if (!response.ok) { + throw new Error(`Failed to load mock customers: ${response.status} ${response.statusText}`); + } + const rawData = await response.json(); + return this.processCustomerData(rawData); + } catch (error) { + console.error("Failed to load customer data:", error); + throw error; + } + } + async sendCreate(_customer) { + throw new Error("MockCustomerRepository does not support sendCreate. Mock data is read-only."); + } + async sendUpdate(_id, _updates) { + throw new Error("MockCustomerRepository does not support sendUpdate. Mock data is read-only."); + } + async sendDelete(_id) { + throw new Error("MockCustomerRepository does not support sendDelete. Mock data is read-only."); + } + processCustomerData(data) { + return data.map((customer) => ({ + id: customer.id, + name: customer.name, + phone: customer.phone, + email: customer.email, + metadata: customer.metadata, + syncStatus: "synced" + })); + } +}; +__name(_MockCustomerRepository, "MockCustomerRepository"); +var MockCustomerRepository = _MockCustomerRepository; + +// src/repositories/MockAuditRepository.ts +var _MockAuditRepository = class _MockAuditRepository { + constructor() { + this.entityType = "Audit"; + } + async sendCreate(entity) { + await new Promise((resolve) => setTimeout(resolve, 100)); + console.log("MockAuditRepository: Audit entry synced to backend:", { + id: entity.id, + entityType: entity.entityType, + entityId: entity.entityId, + operation: entity.operation, + timestamp: new Date(entity.timestamp).toISOString() + }); + return entity; + } + async sendUpdate(_id, _entity) { + throw new Error("Audit entries cannot be updated"); + } + async sendDelete(_id) { + throw new Error("Audit entries cannot be deleted"); + } + async fetchAll() { + return []; + } + async fetchById(_id) { + return null; + } +}; +__name(_MockAuditRepository, "MockAuditRepository"); +var MockAuditRepository = _MockAuditRepository; + +// src/repositories/MockTeamRepository.ts +var _MockTeamRepository = class _MockTeamRepository { + constructor() { + this.entityType = "Team"; + this.dataUrl = "data/mock-teams.json"; + } + async fetchAll() { + try { + const response = await fetch(this.dataUrl); + if (!response.ok) { + throw new Error(`Failed to load mock teams: ${response.status} ${response.statusText}`); + } + const rawData = await response.json(); + return this.processTeamData(rawData); + } catch (error) { + console.error("Failed to load team data:", error); + throw error; + } + } + async sendCreate(_team) { + throw new Error("MockTeamRepository does not support sendCreate. Mock data is read-only."); + } + async sendUpdate(_id, _updates) { + throw new Error("MockTeamRepository does not support sendUpdate. Mock data is read-only."); + } + async sendDelete(_id) { + throw new Error("MockTeamRepository does not support sendDelete. Mock data is read-only."); + } + processTeamData(data) { + return data.map((team) => ({ + id: team.id, + name: team.name, + resourceIds: team.resourceIds, + syncStatus: "synced" + })); + } +}; +__name(_MockTeamRepository, "MockTeamRepository"); +var MockTeamRepository = _MockTeamRepository; + +// src/repositories/MockDepartmentRepository.ts +var _MockDepartmentRepository = class _MockDepartmentRepository { + constructor() { + this.entityType = "Department"; + this.dataUrl = "data/mock-departments.json"; + } + async fetchAll() { + try { + const response = await fetch(this.dataUrl); + if (!response.ok) { + throw new Error(`Failed to load mock departments: ${response.status} ${response.statusText}`); + } + const rawData = await response.json(); + return this.processDepartmentData(rawData); + } catch (error) { + console.error("Failed to load department data:", error); + throw error; + } + } + async sendCreate(_department) { + throw new Error("MockDepartmentRepository does not support sendCreate. Mock data is read-only."); + } + async sendUpdate(_id, _updates) { + throw new Error("MockDepartmentRepository does not support sendUpdate. Mock data is read-only."); + } + async sendDelete(_id) { + throw new Error("MockDepartmentRepository does not support sendDelete. Mock data is read-only."); + } + processDepartmentData(data) { + return data.map((dept) => ({ + id: dept.id, + name: dept.name, + resourceIds: dept.resourceIds, + syncStatus: "synced" + })); + } +}; +__name(_MockDepartmentRepository, "MockDepartmentRepository"); +var MockDepartmentRepository = _MockDepartmentRepository; + +// src/repositories/MockSettingsRepository.ts +var _MockSettingsRepository = class _MockSettingsRepository { + constructor() { + this.entityType = "Settings"; + this.dataUrl = "data/tenant-settings.json"; + } + async fetchAll() { + try { + const response = await fetch(this.dataUrl); + if (!response.ok) { + throw new Error(`Failed to load tenant settings: ${response.status} ${response.statusText}`); + } + const settings = await response.json(); + return settings.map((s) => ({ + ...s, + syncStatus: s.syncStatus || "synced" + })); + } catch (error) { + console.error("Failed to load tenant settings:", error); + throw error; + } + } + async sendCreate(_settings) { + throw new Error("MockSettingsRepository does not support sendCreate. Mock data is read-only."); + } + async sendUpdate(_id, _updates) { + throw new Error("MockSettingsRepository does not support sendUpdate. Mock data is read-only."); + } + async sendDelete(_id) { + throw new Error("MockSettingsRepository does not support sendDelete. Mock data is read-only."); + } +}; +__name(_MockSettingsRepository, "MockSettingsRepository"); +var MockSettingsRepository = _MockSettingsRepository; + +// src/repositories/MockViewConfigRepository.ts +var _MockViewConfigRepository = class _MockViewConfigRepository { + constructor() { + this.entityType = "ViewConfig"; + this.dataUrl = "data/viewconfigs.json"; + } + async fetchAll() { + try { + const response = await fetch(this.dataUrl); + if (!response.ok) { + throw new Error(`Failed to load viewconfigs: ${response.status} ${response.statusText}`); + } + const rawData = await response.json(); + const configs = rawData.map((config) => ({ + ...config, + syncStatus: config.syncStatus || "synced" + })); + return configs; + } catch (error) { + console.error("Failed to load viewconfigs:", error); + throw error; + } + } + async sendCreate(_config) { + throw new Error("MockViewConfigRepository does not support sendCreate. Mock data is read-only."); + } + async sendUpdate(_id, _updates) { + throw new Error("MockViewConfigRepository does not support sendUpdate. Mock data is read-only."); + } + async sendDelete(_id) { + throw new Error("MockViewConfigRepository does not support sendDelete. Mock data is read-only."); + } +}; +__name(_MockViewConfigRepository, "MockViewConfigRepository"); +var MockViewConfigRepository = _MockViewConfigRepository; + +// src/workers/DataSeeder.ts +var _DataSeeder = class _DataSeeder { + constructor(services, repositories) { + this.services = services; + this.repositories = repositories; + } + /** + * Seed all entity stores if they are empty + */ + async seedIfEmpty() { + console.log("[DataSeeder] Checking if database needs seeding..."); + try { + for (const service of this.services) { + const repository = this.repositories.find((repo) => repo.entityType === service.entityType); + if (!repository) { + console.warn(`[DataSeeder] No repository found for entity type: ${service.entityType}, skipping`); + continue; + } + await this.seedEntity(service.entityType, service, repository); + } + console.log("[DataSeeder] Seeding complete"); + } catch (error) { + console.error("[DataSeeder] Seeding failed:", error); + throw error; + } + } + async seedEntity(entityType, service, repository) { + const existing = await service.getAll(); + if (existing.length > 0) { + console.log(`[DataSeeder] ${entityType} store already has ${existing.length} items, skipping seed`); + return; + } + console.log(`[DataSeeder] ${entityType} store is empty, fetching from repository...`); + const data = await repository.fetchAll(); + console.log(`[DataSeeder] Fetched ${data.length} ${entityType} items, saving to IndexedDB...`); + for (const entity of data) { + await service.save(entity, true); + } + console.log(`[DataSeeder] ${entityType} seeding complete (${data.length} items saved)`); + } +}; +__name(_DataSeeder, "DataSeeder"); +var DataSeeder = _DataSeeder; + +// src/utils/PositionUtils.ts +function calculateEventPosition(start, end, config) { + const startMinutes = start.getHours() * 60 + start.getMinutes(); + const endMinutes = end.getHours() * 60 + end.getMinutes(); + const dayStartMinutes = config.dayStartHour * 60; + const minuteHeight = config.hourHeight / 60; + const top = (startMinutes - dayStartMinutes) * minuteHeight; + const height = (endMinutes - startMinutes) * minuteHeight; + return { top, height }; +} +__name(calculateEventPosition, "calculateEventPosition"); +function minutesToPixels(minutes, config) { + return minutes / 60 * config.hourHeight; +} +__name(minutesToPixels, "minutesToPixels"); +function pixelsToMinutes(pixels, config) { + return pixels / config.hourHeight * 60; +} +__name(pixelsToMinutes, "pixelsToMinutes"); +function snapToGrid(pixels, config) { + const snapPixels = minutesToPixels(config.snapInterval, config); + return Math.round(pixels / snapPixels) * snapPixels; +} +__name(snapToGrid, "snapToGrid"); + +// src/features/event/EventLayoutEngine.ts +function eventsOverlap(a, b) { + return a.start < b.end && a.end > b.start; +} +__name(eventsOverlap, "eventsOverlap"); +function eventsWithinThreshold(a, b, thresholdMinutes) { + const thresholdMs = thresholdMinutes * 60 * 1e3; + const startToStartDiff = Math.abs(a.start.getTime() - b.start.getTime()); + if (startToStartDiff <= thresholdMs) + return true; + const bStartsBeforeAEnds = a.end.getTime() - b.start.getTime(); + if (bStartsBeforeAEnds > 0 && bStartsBeforeAEnds <= thresholdMs) + return true; + const aStartsBeforeBEnds = b.end.getTime() - a.start.getTime(); + if (aStartsBeforeBEnds > 0 && aStartsBeforeBEnds <= thresholdMs) + return true; + return false; +} +__name(eventsWithinThreshold, "eventsWithinThreshold"); +function findOverlapGroups(events) { + if (events.length === 0) + return []; + const sorted = [...events].sort((a, b) => a.start.getTime() - b.start.getTime()); + const used = /* @__PURE__ */ new Set(); + const groups = []; + for (const event of sorted) { + if (used.has(event.id)) + continue; + const group = [event]; + used.add(event.id); + let expanded = true; + while (expanded) { + expanded = false; + for (const candidate of sorted) { + if (used.has(candidate.id)) + continue; + const connects = group.some((member) => eventsOverlap(member, candidate)); + if (connects) { + group.push(candidate); + used.add(candidate.id); + expanded = true; + } + } + } + groups.push(group); + } + return groups; +} +__name(findOverlapGroups, "findOverlapGroups"); +function findGridCandidates(events, thresholdMinutes) { + if (events.length === 0) + return []; + const sorted = [...events].sort((a, b) => a.start.getTime() - b.start.getTime()); + const used = /* @__PURE__ */ new Set(); + const groups = []; + for (const event of sorted) { + if (used.has(event.id)) + continue; + const group = [event]; + used.add(event.id); + let expanded = true; + while (expanded) { + expanded = false; + for (const candidate of sorted) { + if (used.has(candidate.id)) + continue; + const connects = group.some((member) => eventsWithinThreshold(member, candidate, thresholdMinutes)); + if (connects) { + group.push(candidate); + used.add(candidate.id); + expanded = true; + } + } + } + groups.push(group); + } + return groups; +} +__name(findGridCandidates, "findGridCandidates"); +function calculateStackLevels(events) { + const levels = /* @__PURE__ */ new Map(); + const sorted = [...events].sort((a, b) => a.start.getTime() - b.start.getTime()); + for (const event of sorted) { + let maxOverlappingLevel = -1; + for (const [id, level] of levels) { + const other = events.find((e) => e.id === id); + if (other && eventsOverlap(event, other)) { + maxOverlappingLevel = Math.max(maxOverlappingLevel, level); + } + } + levels.set(event.id, maxOverlappingLevel + 1); + } + return levels; +} +__name(calculateStackLevels, "calculateStackLevels"); +function allocateColumns(events) { + const sorted = [...events].sort((a, b) => a.start.getTime() - b.start.getTime()); + const columns = []; + for (const event of sorted) { + let placed = false; + for (const column of columns) { + const canFit = !column.some((e) => eventsOverlap(event, e)); + if (canFit) { + column.push(event); + placed = true; + break; + } + } + if (!placed) { + columns.push([event]); + } + } + return columns; +} +__name(allocateColumns, "allocateColumns"); +function calculateColumnLayout(events, config) { + const thresholdMinutes = config.gridStartThresholdMinutes ?? 10; + const result = { + grids: [], + stacked: [] + }; + if (events.length === 0) + return result; + const overlapGroups = findOverlapGroups(events); + for (const overlapGroup of overlapGroups) { + if (overlapGroup.length === 1) { + result.stacked.push({ + event: overlapGroup[0], + stackLevel: 0 + }); + continue; + } + const gridSubgroups = findGridCandidates(overlapGroup, thresholdMinutes); + const largestGridCandidate = gridSubgroups.reduce((max, g) => g.length > max.length ? g : max, gridSubgroups[0]); + if (largestGridCandidate.length === overlapGroup.length) { + const columns = allocateColumns(overlapGroup); + const earliest = overlapGroup.reduce((min, e) => e.start < min.start ? e : min, overlapGroup[0]); + const position = calculateEventPosition(earliest.start, earliest.end, config); + result.grids.push({ + events: overlapGroup, + columns, + stackLevel: 0, + position: { top: position.top } + }); + } else { + const levels = calculateStackLevels(overlapGroup); + for (const event of overlapGroup) { + result.stacked.push({ + event, + stackLevel: levels.get(event.id) ?? 0 + }); + } + } + } + return result; +} +__name(calculateColumnLayout, "calculateColumnLayout"); + +// src/features/event/EventRenderer.ts +var _EventRenderer = class _EventRenderer { + constructor(eventService, dateService, gridConfig, eventBus) { + this.eventService = eventService; + this.dateService = dateService; + this.gridConfig = gridConfig; + this.eventBus = eventBus; + this.container = null; + this.setupListeners(); + } + /** + * Setup listeners for drag-drop and update events + */ + setupListeners() { + this.eventBus.on(CoreEvents.EVENT_DRAG_COLUMN_CHANGE, (e) => { + const payload = e.detail; + this.handleColumnChange(payload); + }); + this.eventBus.on(CoreEvents.EVENT_DRAG_MOVE, (e) => { + const payload = e.detail; + this.updateDragTimestamp(payload); + }); + this.eventBus.on(CoreEvents.EVENT_UPDATED, (e) => { + const payload = e.detail; + this.handleEventUpdated(payload); + }); + this.eventBus.on(CoreEvents.EVENT_DRAG_END, (e) => { + const payload = e.detail; + this.handleDragEnd(payload); + }); + this.eventBus.on(CoreEvents.EVENT_DRAG_LEAVE_HEADER, (e) => { + const payload = e.detail; + this.handleDragLeaveHeader(payload); + }); + } + /** + * Handle EVENT_DRAG_END - remove element if dropped in header + */ + handleDragEnd(payload) { + if (payload.target === "header") { + const element = this.container?.querySelector(`swp-content-viewport swp-event[data-event-id="${payload.swpEvent.eventId}"]`); + element?.remove(); + } + } + /** + * Handle header item leaving header - create swp-event in grid + */ + handleDragLeaveHeader(payload) { + if (payload.source !== "header") + return; + if (!payload.targetColumn || !payload.start || !payload.end) + return; + if (payload.element) { + payload.element.classList.add("drag-ghost"); + payload.element.style.opacity = "0.3"; + payload.element.style.pointerEvents = "none"; + } + const event = { + id: payload.eventId, + title: payload.title || "", + description: "", + start: payload.start, + end: payload.end, + type: "customer", + allDay: false, + syncStatus: "pending" + }; + const element = this.createEventElement(event); + let eventsLayer = payload.targetColumn.querySelector("swp-events-layer"); + if (!eventsLayer) { + eventsLayer = document.createElement("swp-events-layer"); + payload.targetColumn.appendChild(eventsLayer); + } + eventsLayer.appendChild(element); + element.classList.add("dragging"); + } + /** + * Handle EVENT_UPDATED - re-render affected columns + */ + async handleEventUpdated(payload) { + if (payload.sourceColumnKey !== payload.targetColumnKey) { + await this.rerenderColumn(payload.sourceColumnKey); + } + await this.rerenderColumn(payload.targetColumnKey); + } + /** + * Re-render a single column with fresh data from IndexedDB + */ + async rerenderColumn(columnKey) { + const column = this.findColumn(columnKey); + if (!column) + return; + const date = column.dataset.date; + const resourceId = column.dataset.resourceId; + if (!date) + return; + const startDate = new Date(date); + const endDate = new Date(date); + endDate.setHours(23, 59, 59, 999); + const events = resourceId ? await this.eventService.getByResourceAndDateRange(resourceId, startDate, endDate) : await this.eventService.getByDateRange(startDate, endDate); + const timedEvents = events.filter((event) => !event.allDay && this.dateService.getDateKey(event.start) === date); + let eventsLayer = column.querySelector("swp-events-layer"); + if (!eventsLayer) { + eventsLayer = document.createElement("swp-events-layer"); + column.appendChild(eventsLayer); + } + eventsLayer.innerHTML = ""; + const layout = calculateColumnLayout(timedEvents, this.gridConfig); + layout.grids.forEach((grid) => { + const groupEl = this.renderGridGroup(grid); + eventsLayer.appendChild(groupEl); + }); + layout.stacked.forEach((item) => { + const eventEl = this.renderStackedEvent(item.event, item.stackLevel); + eventsLayer.appendChild(eventEl); + }); + } + /** + * Find a column element by columnKey + */ + findColumn(columnKey) { + if (!this.container) + return null; + return this.container.querySelector(`swp-day-column[data-column-key="${columnKey}"]`); + } + /** + * Handle event moving to a new column during drag + */ + handleColumnChange(payload) { + const eventsLayer = payload.newColumn.querySelector("swp-events-layer"); + if (!eventsLayer) + return; + eventsLayer.appendChild(payload.element); + payload.element.style.top = `${payload.currentY}px`; + } + /** + * Update timestamp display during drag (snapped to grid) + */ + updateDragTimestamp(payload) { + const timeEl = payload.element.querySelector("swp-event-time"); + if (!timeEl) + return; + const snappedY = snapToGrid(payload.currentY, this.gridConfig); + const minutesFromGridStart = pixelsToMinutes(snappedY, this.gridConfig); + const startMinutes = this.gridConfig.dayStartHour * 60 + minutesFromGridStart; + const height = parseFloat(payload.element.style.height) || this.gridConfig.hourHeight; + const durationMinutes = pixelsToMinutes(height, this.gridConfig); + const start = this.minutesToDate(startMinutes); + const end = this.minutesToDate(startMinutes + durationMinutes); + timeEl.textContent = this.dateService.formatTimeRange(start, end); + } + /** + * Convert minutes since midnight to a Date object (today) + */ + minutesToDate(minutes) { + const date = /* @__PURE__ */ new Date(); + date.setHours(Math.floor(minutes / 60) % 24, minutes % 60, 0, 0); + return date; + } + /** + * Render events for visible dates into day columns + * @param container - Calendar container element + * @param filter - Filter with 'date' and optionally 'resource' arrays + * @param filterTemplate - Template for matching events to columns + */ + async render(container2, filter, filterTemplate) { + this.container = container2; + const visibleDates = filter["date"] || []; + if (visibleDates.length === 0) + return; + const startDate = new Date(visibleDates[0]); + const endDate = new Date(visibleDates[visibleDates.length - 1]); + endDate.setHours(23, 59, 59, 999); + const events = await this.eventService.getByDateRange(startDate, endDate); + const dayColumns = container2.querySelector("swp-day-columns"); + if (!dayColumns) + return; + const columns = dayColumns.querySelectorAll("swp-day-column"); + columns.forEach((column) => { + const columnEl = column; + const columnEvents = events.filter((event) => filterTemplate.matches(event, columnEl)); + let eventsLayer = column.querySelector("swp-events-layer"); + if (!eventsLayer) { + eventsLayer = document.createElement("swp-events-layer"); + column.appendChild(eventsLayer); + } + eventsLayer.innerHTML = ""; + const timedEvents = columnEvents.filter((event) => !event.allDay); + const layout = calculateColumnLayout(timedEvents, this.gridConfig); + layout.grids.forEach((grid) => { + const groupEl = this.renderGridGroup(grid); + eventsLayer.appendChild(groupEl); + }); + layout.stacked.forEach((item) => { + const eventEl = this.renderStackedEvent(item.event, item.stackLevel); + eventsLayer.appendChild(eventEl); + }); + }); + } + /** + * Create a single event element + * + * CLEAN approach: + * - Only data-id for lookup + * - Visible content in innerHTML only + */ + createEventElement(event) { + const element = document.createElement("swp-event"); + element.dataset.eventId = event.id; + if (event.resourceId) { + element.dataset.resourceId = event.resourceId; + } + const position = calculateEventPosition(event.start, event.end, this.gridConfig); + element.style.top = `${position.top}px`; + element.style.height = `${position.height}px`; + const colorClass = this.getColorClass(event); + if (colorClass) { + element.classList.add(colorClass); + } + element.innerHTML = ` + ${this.dateService.formatTimeRange(event.start, event.end)} + ${this.escapeHtml(event.title)} + ${event.description ? `${this.escapeHtml(event.description)}` : ""} + `; + return element; + } + /** + * Get color class based on metadata.color or event type + */ + getColorClass(event) { + if (event.metadata?.color) { + return `is-${event.metadata.color}`; + } + const typeColors = { + "customer": "is-blue", + "vacation": "is-green", + "break": "is-amber", + "meeting": "is-purple", + "blocked": "is-red" + }; + return typeColors[event.type] || "is-blue"; + } + /** + * Escape HTML to prevent XSS + */ + escapeHtml(text) { + const div = document.createElement("div"); + div.textContent = text; + return div.innerHTML; + } + /** + * Render a GRID group with side-by-side columns + * Used when multiple events start at the same time + */ + renderGridGroup(layout) { + const group = document.createElement("swp-event-group"); + group.classList.add(`cols-${layout.columns.length}`); + group.style.top = `${layout.position.top}px`; + if (layout.stackLevel > 0) { + group.style.marginLeft = `${layout.stackLevel * 15}px`; + group.style.zIndex = `${100 + layout.stackLevel}`; + } + let maxBottom = 0; + for (const event of layout.events) { + const pos = calculateEventPosition(event.start, event.end, this.gridConfig); + const eventBottom = pos.top + pos.height; + if (eventBottom > maxBottom) + maxBottom = eventBottom; + } + const groupHeight = maxBottom - layout.position.top; + group.style.height = `${groupHeight}px`; + layout.columns.forEach((columnEvents) => { + const wrapper = document.createElement("div"); + wrapper.style.position = "relative"; + columnEvents.forEach((event) => { + const eventEl = this.createEventElement(event); + const pos = calculateEventPosition(event.start, event.end, this.gridConfig); + eventEl.style.top = `${pos.top - layout.position.top}px`; + eventEl.style.position = "absolute"; + eventEl.style.left = "0"; + eventEl.style.right = "0"; + wrapper.appendChild(eventEl); + }); + group.appendChild(wrapper); + }); + return group; + } + /** + * Render a STACKED event with margin-left offset + * Used for overlapping events that don't start at the same time + */ + renderStackedEvent(event, stackLevel) { + const element = this.createEventElement(event); + element.dataset.stackLink = JSON.stringify({ stackLevel }); + if (stackLevel > 0) { + element.style.marginLeft = `${stackLevel * 15}px`; + element.style.zIndex = `${100 + stackLevel}`; + } + return element; + } +}; +__name(_EventRenderer, "EventRenderer"); +var EventRenderer = _EventRenderer; + +// src/features/schedule/ScheduleRenderer.ts +var _ScheduleRenderer = class _ScheduleRenderer { + constructor(scheduleService, dateService, gridConfig) { + this.scheduleService = scheduleService; + this.dateService = dateService; + this.gridConfig = gridConfig; + } + /** + * Render unavailable zones for visible columns + * @param container - Calendar container element + * @param filter - Filter with 'date' and 'resource' arrays + */ + async render(container2, filter) { + const dates = filter["date"] || []; + const resourceIds = filter["resource"] || []; + if (dates.length === 0) + return; + const dayColumns = container2.querySelector("swp-day-columns"); + if (!dayColumns) + return; + const columns = dayColumns.querySelectorAll("swp-day-column"); + for (const column of columns) { + const date = column.dataset.date; + const resourceId = column.dataset.resourceId; + if (!date || !resourceId) + continue; + let unavailableLayer = column.querySelector("swp-unavailable-layer"); + if (!unavailableLayer) { + unavailableLayer = document.createElement("swp-unavailable-layer"); + column.insertBefore(unavailableLayer, column.firstChild); + } + unavailableLayer.innerHTML = ""; + const schedule = await this.scheduleService.getScheduleForDate(resourceId, date); + this.renderUnavailableZones(unavailableLayer, schedule); + } + } + /** + * Render unavailable time zones based on schedule + */ + renderUnavailableZones(layer, schedule) { + const dayStartMinutes = this.gridConfig.dayStartHour * 60; + const dayEndMinutes = this.gridConfig.dayEndHour * 60; + const minuteHeight = this.gridConfig.hourHeight / 60; + if (schedule === null) { + const zone = this.createUnavailableZone(0, (dayEndMinutes - dayStartMinutes) * minuteHeight); + layer.appendChild(zone); + return; + } + const workStartMinutes = this.dateService.timeToMinutes(schedule.start); + const workEndMinutes = this.dateService.timeToMinutes(schedule.end); + if (workStartMinutes > dayStartMinutes) { + const top = 0; + const height = (workStartMinutes - dayStartMinutes) * minuteHeight; + const zone = this.createUnavailableZone(top, height); + layer.appendChild(zone); + } + if (workEndMinutes < dayEndMinutes) { + const top = (workEndMinutes - dayStartMinutes) * minuteHeight; + const height = (dayEndMinutes - workEndMinutes) * minuteHeight; + const zone = this.createUnavailableZone(top, height); + layer.appendChild(zone); + } + } + /** + * Create an unavailable zone element + */ + createUnavailableZone(top, height) { + const zone = document.createElement("swp-unavailable-zone"); + zone.style.top = `${top}px`; + zone.style.height = `${height}px`; + return zone; + } +}; +__name(_ScheduleRenderer, "ScheduleRenderer"); +var ScheduleRenderer = _ScheduleRenderer; + +// src/features/headerdrawer/HeaderDrawerRenderer.ts +var _HeaderDrawerRenderer = class _HeaderDrawerRenderer { + constructor(eventBus, gridConfig, headerDrawerManager, eventService, dateService) { + this.eventBus = eventBus; + this.gridConfig = gridConfig; + this.headerDrawerManager = headerDrawerManager; + this.eventService = eventService; + this.dateService = dateService; + this.currentItem = null; + this.container = null; + this.sourceElement = null; + this.wasExpandedBeforeDrag = false; + this.filterTemplate = null; + this.setupListeners(); + } + /** + * Render allDay events into the header drawer with row stacking + * @param filterTemplate - Template for matching events to columns + */ + async render(container2, filter, filterTemplate) { + this.filterTemplate = filterTemplate; + const drawer = container2.querySelector("swp-header-drawer"); + if (!drawer) + return; + const visibleDates = filter["date"] || []; + if (visibleDates.length === 0) + return; + const visibleColumnKeys = this.getVisibleColumnKeysFromDOM(); + if (visibleColumnKeys.length === 0) + return; + const startDate = new Date(visibleDates[0]); + const endDate = new Date(visibleDates[visibleDates.length - 1]); + endDate.setHours(23, 59, 59, 999); + const events = await this.eventService.getByDateRange(startDate, endDate); + const allDayEvents = events.filter((event) => event.allDay !== false); + drawer.innerHTML = ""; + if (allDayEvents.length === 0) + return; + const layouts = this.calculateLayout(allDayEvents, visibleColumnKeys); + const rowCount = Math.max(1, ...layouts.map((l) => l.row)); + layouts.forEach((layout) => { + const item = this.createHeaderItem(layout); + drawer.appendChild(item); + }); + this.headerDrawerManager.expandToRows(rowCount); + } + /** + * Create a header item element from layout + */ + createHeaderItem(layout) { + const { event, columnKey, row, colStart, colEnd } = layout; + const item = document.createElement("swp-header-item"); + item.dataset.eventId = event.id; + item.dataset.itemType = "event"; + item.dataset.start = event.start.toISOString(); + item.dataset.end = event.end.toISOString(); + item.dataset.columnKey = columnKey; + item.textContent = event.title; + const colorClass = this.getColorClass(event); + if (colorClass) + item.classList.add(colorClass); + item.style.gridArea = `${row} / ${colStart} / ${row + 1} / ${colEnd}`; + return item; + } + /** + * Calculate layout for all events with row stacking + * Uses track-based algorithm to find available rows for overlapping events + */ + calculateLayout(events, visibleColumnKeys) { + const tracks = [new Array(visibleColumnKeys.length).fill(false)]; + const layouts = []; + for (const event of events) { + const columnKey = this.buildColumnKeyFromEvent(event); + const startCol = visibleColumnKeys.indexOf(columnKey); + const endColumnKey = this.buildColumnKeyFromEvent(event, event.end); + const endCol = visibleColumnKeys.indexOf(endColumnKey); + if (startCol === -1 && endCol === -1) + continue; + const colStart = Math.max(0, startCol); + const colEnd = (endCol !== -1 ? endCol : visibleColumnKeys.length - 1) + 1; + const row = this.findAvailableRow(tracks, colStart, colEnd); + for (let c = colStart; c < colEnd; c++) { + tracks[row][c] = true; + } + layouts.push({ event, columnKey, row: row + 1, colStart: colStart + 1, colEnd: colEnd + 1 }); + } + return layouts; + } + /** + * Build columnKey from event using FilterTemplate + * Uses the same template that columns use for matching + */ + buildColumnKeyFromEvent(event, date) { + if (!this.filterTemplate) { + const dateStr = this.dateService.getDateKey(date || event.start); + return dateStr; + } + if (date && date.getTime() !== event.start.getTime()) { + const tempEvent = { ...event, start: date }; + return this.filterTemplate.buildKeyFromEvent(tempEvent); + } + return this.filterTemplate.buildKeyFromEvent(event); + } + /** + * Find available row for event spanning columns [colStart, colEnd) + */ + findAvailableRow(tracks, colStart, colEnd) { + for (let row = 0; row < tracks.length; row++) { + let available = true; + for (let c = colStart; c < colEnd; c++) { + if (tracks[row][c]) { + available = false; + break; + } + } + if (available) + return row; + } + tracks.push(new Array(tracks[0].length).fill(false)); + return tracks.length - 1; + } + /** + * Get color class based on event metadata or type + */ + getColorClass(event) { + if (event.metadata?.color) { + return `is-${event.metadata.color}`; + } + const typeColors = { + "customer": "is-blue", + "vacation": "is-green", + "break": "is-amber", + "meeting": "is-purple", + "blocked": "is-red" + }; + return typeColors[event.type] || "is-blue"; + } + /** + * Setup event listeners for drag events + */ + setupListeners() { + this.eventBus.on(CoreEvents.EVENT_DRAG_ENTER_HEADER, (e) => { + const payload = e.detail; + this.handleDragEnter(payload); + }); + this.eventBus.on(CoreEvents.EVENT_DRAG_MOVE_HEADER, (e) => { + const payload = e.detail; + this.handleDragMove(payload); + }); + this.eventBus.on(CoreEvents.EVENT_DRAG_LEAVE_HEADER, (e) => { + const payload = e.detail; + this.handleDragLeave(payload); + }); + this.eventBus.on(CoreEvents.EVENT_DRAG_END, (e) => { + const payload = e.detail; + this.handleDragEnd(payload); + }); + this.eventBus.on(CoreEvents.EVENT_DRAG_CANCEL, () => { + this.cleanup(); + }); + } + /** + * Handle drag entering header zone - create preview item + */ + handleDragEnter(payload) { + this.container = document.querySelector("swp-header-drawer"); + if (!this.container) + return; + this.wasExpandedBeforeDrag = this.headerDrawerManager.isExpanded(); + if (!this.wasExpandedBeforeDrag) { + this.headerDrawerManager.expandToRows(1); + } + this.sourceElement = payload.element; + const item = document.createElement("swp-header-item"); + item.dataset.eventId = payload.eventId; + item.dataset.itemType = payload.itemType; + item.dataset.duration = String(payload.duration); + item.dataset.columnKey = payload.sourceColumnKey; + item.textContent = payload.title; + if (payload.colorClass) { + item.classList.add(payload.colorClass); + } + item.classList.add("dragging"); + const col = payload.sourceColumnIndex + 1; + const endCol = col + payload.duration; + item.style.gridArea = `1 / ${col} / 2 / ${endCol}`; + this.container.appendChild(item); + this.currentItem = item; + payload.element.style.visibility = "hidden"; + } + /** + * Handle drag moving within header - update column position + */ + handleDragMove(payload) { + if (!this.currentItem) + return; + const col = payload.columnIndex + 1; + const duration = parseInt(this.currentItem.dataset.duration || "1", 10); + const endCol = col + duration; + this.currentItem.style.gridArea = `1 / ${col} / 2 / ${endCol}`; + this.currentItem.dataset.columnKey = payload.columnKey; + } + /** + * Handle drag leaving header - cleanup for grid→header drag only + */ + handleDragLeave(payload) { + if (payload.source === "grid") { + this.cleanup(); + } + } + /** + * Handle drag end - finalize based on drop target + */ + handleDragEnd(payload) { + if (payload.target === "header") { + if (this.currentItem) { + this.currentItem.classList.remove("dragging"); + this.recalculateDrawerLayout(); + this.currentItem = null; + this.sourceElement = null; + } + } else { + const ghost = document.querySelector(`swp-header-item.drag-ghost[data-event-id="${payload.swpEvent.eventId}"]`); + ghost?.remove(); + this.recalculateDrawerLayout(); + } + } + /** + * Recalculate layout for all items currently in the drawer + * Called after drop to reposition items and adjust height + */ + recalculateDrawerLayout() { + const drawer = document.querySelector("swp-header-drawer"); + if (!drawer) + return; + const items = Array.from(drawer.querySelectorAll("swp-header-item")); + if (items.length === 0) + return; + const visibleColumnKeys = this.getVisibleColumnKeysFromDOM(); + if (visibleColumnKeys.length === 0) + return; + const itemData = items.map((item) => ({ + element: item, + columnKey: item.dataset.columnKey || "", + duration: parseInt(item.dataset.duration || "1", 10) + })); + const tracks = [new Array(visibleColumnKeys.length).fill(false)]; + for (const item of itemData) { + const startCol = visibleColumnKeys.indexOf(item.columnKey); + if (startCol === -1) + continue; + const colStart = startCol; + const colEnd = Math.min(startCol + item.duration, visibleColumnKeys.length); + const row = this.findAvailableRow(tracks, colStart, colEnd); + for (let c = colStart; c < colEnd; c++) { + tracks[row][c] = true; + } + item.element.style.gridArea = `${row + 1} / ${colStart + 1} / ${row + 2} / ${colEnd + 1}`; + } + const rowCount = tracks.length; + this.headerDrawerManager.expandToRows(rowCount); + } + /** + * Get visible column keys from DOM (preserves order for multi-resource views) + * Uses filterTemplate.buildKeyFromColumn() for consistent key format with events + */ + getVisibleColumnKeysFromDOM() { + if (!this.filterTemplate) + return []; + const columns = document.querySelectorAll("swp-day-column"); + const columnKeys = []; + columns.forEach((col) => { + const columnKey = this.filterTemplate.buildKeyFromColumn(col); + if (columnKey) + columnKeys.push(columnKey); + }); + return columnKeys; + } + /** + * Cleanup preview item and restore source visibility + */ + cleanup() { + this.currentItem?.remove(); + this.currentItem = null; + if (this.sourceElement) { + this.sourceElement.style.visibility = ""; + this.sourceElement = null; + } + if (!this.wasExpandedBeforeDrag) { + this.headerDrawerManager.collapse(); + } + } +}; +__name(_HeaderDrawerRenderer, "HeaderDrawerRenderer"); +var HeaderDrawerRenderer = _HeaderDrawerRenderer; + +// src/storage/schedules/ScheduleOverrideStore.ts +var _ScheduleOverrideStore = class _ScheduleOverrideStore { + constructor() { + this.storeName = _ScheduleOverrideStore.STORE_NAME; + } + create(db) { + const store = db.createObjectStore(_ScheduleOverrideStore.STORE_NAME, { keyPath: "id" }); + store.createIndex("resourceId", "resourceId", { unique: false }); + store.createIndex("date", "date", { unique: false }); + store.createIndex("resourceId_date", ["resourceId", "date"], { unique: true }); + store.createIndex("syncStatus", "syncStatus", { unique: false }); + } +}; +__name(_ScheduleOverrideStore, "ScheduleOverrideStore"); +var ScheduleOverrideStore = _ScheduleOverrideStore; +ScheduleOverrideStore.STORE_NAME = "scheduleOverrides"; + +// src/storage/schedules/ScheduleOverrideService.ts +var _ScheduleOverrideService = class _ScheduleOverrideService { + constructor(context) { + this.context = context; + } + get db() { + return this.context.getDatabase(); + } + /** + * Get override for a specific resource and date + */ + async getOverride(resourceId, date) { + return new Promise((resolve, reject) => { + const transaction = this.db.transaction([ScheduleOverrideStore.STORE_NAME], "readonly"); + const store = transaction.objectStore(ScheduleOverrideStore.STORE_NAME); + const index = store.index("resourceId_date"); + const request = index.get([resourceId, date]); + request.onsuccess = () => { + resolve(request.result || null); + }; + request.onerror = () => { + reject(new Error(`Failed to get override for ${resourceId} on ${date}: ${request.error}`)); + }; + }); + } + /** + * Get all overrides for a resource + */ + async getByResource(resourceId) { + return new Promise((resolve, reject) => { + const transaction = this.db.transaction([ScheduleOverrideStore.STORE_NAME], "readonly"); + const store = transaction.objectStore(ScheduleOverrideStore.STORE_NAME); + const index = store.index("resourceId"); + const request = index.getAll(resourceId); + request.onsuccess = () => { + resolve(request.result || []); + }; + request.onerror = () => { + reject(new Error(`Failed to get overrides for ${resourceId}: ${request.error}`)); + }; + }); + } + /** + * Get overrides for a date range + */ + async getByDateRange(resourceId, startDate, endDate) { + const all = await this.getByResource(resourceId); + return all.filter((o) => o.date >= startDate && o.date <= endDate); + } + /** + * Save an override + */ + async save(override) { + return new Promise((resolve, reject) => { + const transaction = this.db.transaction([ScheduleOverrideStore.STORE_NAME], "readwrite"); + const store = transaction.objectStore(ScheduleOverrideStore.STORE_NAME); + const request = store.put(override); + request.onsuccess = () => resolve(); + request.onerror = () => { + reject(new Error(`Failed to save override ${override.id}: ${request.error}`)); + }; + }); + } + /** + * Delete an override + */ + async delete(id) { + return new Promise((resolve, reject) => { + const transaction = this.db.transaction([ScheduleOverrideStore.STORE_NAME], "readwrite"); + const store = transaction.objectStore(ScheduleOverrideStore.STORE_NAME); + const request = store.delete(id); + request.onsuccess = () => resolve(); + request.onerror = () => { + reject(new Error(`Failed to delete override ${id}: ${request.error}`)); + }; + }); + } +}; +__name(_ScheduleOverrideService, "ScheduleOverrideService"); +var ScheduleOverrideService = _ScheduleOverrideService; + +// src/storage/schedules/ResourceScheduleService.ts +var _ResourceScheduleService = class _ResourceScheduleService { + constructor(resourceService, overrideService, dateService) { + this.resourceService = resourceService; + this.overrideService = overrideService; + this.dateService = dateService; + } + /** + * Get effective schedule for a resource on a specific date + * + * @param resourceId - Resource ID + * @param date - Date string "YYYY-MM-DD" + * @returns ITimeSlot or null (fri/closed) + */ + async getScheduleForDate(resourceId, date) { + const override = await this.overrideService.getOverride(resourceId, date); + if (override) { + return override.schedule; + } + const resource = await this.resourceService.get(resourceId); + if (!resource || !resource.defaultSchedule) { + return null; + } + const weekDay = this.dateService.getISOWeekDay(date); + return resource.defaultSchedule[weekDay] || null; + } + /** + * Get schedules for multiple dates + * + * @param resourceId - Resource ID + * @param dates - Array of date strings "YYYY-MM-DD" + * @returns Map of date -> ITimeSlot | null + */ + async getSchedulesForDates(resourceId, dates) { + const result = /* @__PURE__ */ new Map(); + const resource = await this.resourceService.get(resourceId); + const overrides = dates.length > 0 ? await this.overrideService.getByDateRange(resourceId, dates[0], dates[dates.length - 1]) : []; + const overrideMap = new Map(overrides.map((o) => [o.date, o.schedule])); + for (const date of dates) { + if (overrideMap.has(date)) { + result.set(date, overrideMap.get(date)); + continue; + } + if (resource?.defaultSchedule) { + const weekDay = this.dateService.getISOWeekDay(date); + result.set(date, resource.defaultSchedule[weekDay] || null); + } else { + result.set(date, null); + } + } + return result; + } +}; +__name(_ResourceScheduleService, "ResourceScheduleService"); +var ResourceScheduleService = _ResourceScheduleService; + +// src/types/SwpEvent.ts +var _SwpEvent = class _SwpEvent { + constructor(element, columnKey, start, end) { + this.element = element; + this.columnKey = columnKey; + this._start = start; + this._end = end; + } + /** Event ID from element.dataset.eventId */ + get eventId() { + return this.element.dataset.eventId || ""; + } + get start() { + return this._start; + } + get end() { + return this._end; + } + /** Duration in minutes */ + get durationMinutes() { + return (this._end.getTime() - this._start.getTime()) / (1e3 * 60); + } + /** Duration in milliseconds */ + get durationMs() { + return this._end.getTime() - this._start.getTime(); + } + /** + * Factory: Create SwpEvent from element + columnKey + * Reads top/height from element.style to calculate start/end + * @param columnKey - Opaque column identifier (do NOT parse - use only for matching) + * @param date - Date string (YYYY-MM-DD) for time calculations + */ + static fromElement(element, columnKey, date, gridConfig) { + const topPixels = parseFloat(element.style.top) || 0; + const heightPixels = parseFloat(element.style.height) || 0; + const startMinutesFromGrid = topPixels / gridConfig.hourHeight * 60; + const totalMinutes = gridConfig.dayStartHour * 60 + startMinutesFromGrid; + const start = new Date(date); + start.setHours(Math.floor(totalMinutes / 60), totalMinutes % 60, 0, 0); + const durationMinutes = heightPixels / gridConfig.hourHeight * 60; + const end = new Date(start.getTime() + durationMinutes * 60 * 1e3); + return new _SwpEvent(element, columnKey, start, end); + } +}; +__name(_SwpEvent, "SwpEvent"); +var SwpEvent = _SwpEvent; + +// src/managers/DragDropManager.ts +var _DragDropManager = class _DragDropManager { + constructor(eventBus, gridConfig) { + this.eventBus = eventBus; + this.gridConfig = gridConfig; + this.dragState = null; + this.mouseDownPosition = null; + this.pendingElement = null; + this.pendingMouseOffset = null; + this.container = null; + this.inHeader = false; + this.DRAG_THRESHOLD = 5; + this.INTERPOLATION_FACTOR = 0.3; + this.handlePointerDown = (e) => { + const target = e.target; + if (target.closest("swp-resize-handle")) + return; + const eventElement = target.closest("swp-event"); + const headerItem = target.closest("swp-header-item"); + const draggable = eventElement || headerItem; + if (!draggable) + return; + this.mouseDownPosition = { x: e.clientX, y: e.clientY }; + this.pendingElement = draggable; + const rect = draggable.getBoundingClientRect(); + this.pendingMouseOffset = { + x: e.clientX - rect.left, + y: e.clientY - rect.top + }; + draggable.setPointerCapture(e.pointerId); + }; + this.handlePointerMove = (e) => { + if (!this.mouseDownPosition || !this.pendingElement) { + if (this.dragState) { + this.updateDragTarget(e); + } + return; + } + const deltaX = Math.abs(e.clientX - this.mouseDownPosition.x); + const deltaY = Math.abs(e.clientY - this.mouseDownPosition.y); + const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); + if (distance < this.DRAG_THRESHOLD) + return; + this.initializeDrag(this.pendingElement, this.pendingMouseOffset, e); + this.mouseDownPosition = null; + this.pendingElement = null; + this.pendingMouseOffset = null; + }; + this.handlePointerUp = (_e) => { + this.mouseDownPosition = null; + this.pendingElement = null; + this.pendingMouseOffset = null; + if (!this.dragState) + return; + cancelAnimationFrame(this.dragState.animationId); + if (this.dragState.dragSource === "header") { + this.handleHeaderItemDragEnd(); + } else { + this.handleGridEventDragEnd(); + } + this.dragState.element.classList.remove("dragging"); + this.dragState = null; + this.inHeader = false; + }; + this.animateDrag = () => { + if (!this.dragState) + return; + const diff2 = this.dragState.targetY - this.dragState.currentY; + if (Math.abs(diff2) <= 0.5) { + this.dragState.animationId = 0; + return; + } + this.dragState.currentY += diff2 * this.INTERPOLATION_FACTOR; + this.dragState.element.style.top = `${this.dragState.currentY}px`; + if (this.dragState.columnElement) { + const payload = { + eventId: this.dragState.eventId, + element: this.dragState.element, + currentY: this.dragState.currentY, + columnElement: this.dragState.columnElement + }; + this.eventBus.emit(CoreEvents.EVENT_DRAG_MOVE, payload); + } + this.dragState.animationId = requestAnimationFrame(this.animateDrag); + }; + this.setupScrollListener(); + } + setupScrollListener() { + this.eventBus.on(CoreEvents.EDGE_SCROLL_TICK, (e) => { + if (!this.dragState) + return; + const { scrollDelta } = e.detail; + this.dragState.targetY += scrollDelta; + this.dragState.currentY += scrollDelta; + this.dragState.element.style.top = `${this.dragState.currentY}px`; + }); + } + /** + * Initialize drag-drop on a container element + */ + init(container2) { + this.container = container2; + container2.addEventListener("pointerdown", this.handlePointerDown); + document.addEventListener("pointermove", this.handlePointerMove); + document.addEventListener("pointerup", this.handlePointerUp); + } + /** + * Handle drag end for header items + */ + handleHeaderItemDragEnd() { + if (!this.dragState) + return; + if (!this.inHeader && this.dragState.currentColumn) { + const gridEvent = this.dragState.currentColumn.querySelector(`swp-event[data-event-id="${this.dragState.eventId}"]`); + if (gridEvent) { + const columnKey = this.dragState.currentColumn.dataset.columnKey || ""; + const date = this.dragState.currentColumn.dataset.date || ""; + const swpEvent = SwpEvent.fromElement(gridEvent, columnKey, date, this.gridConfig); + const payload = { + swpEvent, + sourceColumnKey: this.dragState.sourceColumnKey, + target: "grid" + }; + this.eventBus.emit(CoreEvents.EVENT_DRAG_END, payload); + } + } + } + /** + * Handle drag end for grid events + */ + handleGridEventDragEnd() { + if (!this.dragState || !this.dragState.columnElement) + return; + const snappedY = snapToGrid(this.dragState.currentY, this.gridConfig); + this.dragState.element.style.top = `${snappedY}px`; + this.dragState.ghostElement?.remove(); + const columnKey = this.dragState.columnElement.dataset.columnKey || ""; + const date = this.dragState.columnElement.dataset.date || ""; + const swpEvent = SwpEvent.fromElement(this.dragState.element, columnKey, date, this.gridConfig); + const payload = { + swpEvent, + sourceColumnKey: this.dragState.sourceColumnKey, + target: this.inHeader ? "header" : "grid" + }; + this.eventBus.emit(CoreEvents.EVENT_DRAG_END, payload); + } + initializeDrag(element, mouseOffset, e) { + const eventId = element.dataset.eventId || ""; + const isHeaderItem = element.tagName.toLowerCase() === "swp-header-item"; + const columnElement = element.closest("swp-day-column"); + if (!isHeaderItem && !columnElement) + return; + if (isHeaderItem) { + this.initializeHeaderItemDrag(element, mouseOffset, eventId); + } else { + this.initializeGridEventDrag(element, mouseOffset, e, columnElement, eventId); + } + } + /** + * Initialize drag for a header item (allDay event) + */ + initializeHeaderItemDrag(element, mouseOffset, eventId) { + element.classList.add("dragging"); + this.dragState = { + eventId, + element, + ghostElement: null, + // No ghost for header items + startY: 0, + mouseOffset, + columnElement: null, + currentColumn: null, + targetY: 0, + currentY: 0, + animationId: 0, + sourceColumnKey: "", + // Will be set from header item data + dragSource: "header" + }; + this.inHeader = true; + } + /** + * Initialize drag for a grid event + */ + initializeGridEventDrag(element, mouseOffset, e, columnElement, eventId) { + const elementRect = element.getBoundingClientRect(); + const columnRect = columnElement.getBoundingClientRect(); + const startY = elementRect.top - columnRect.top; + const group = element.closest("swp-event-group"); + if (group) { + const eventsLayer = columnElement.querySelector("swp-events-layer"); + if (eventsLayer) { + eventsLayer.appendChild(element); + } + } + element.style.position = "absolute"; + element.style.top = `${startY}px`; + element.style.left = "2px"; + element.style.right = "2px"; + element.style.marginLeft = "0"; + const ghostElement = element.cloneNode(true); + ghostElement.classList.add("drag-ghost"); + ghostElement.style.opacity = "0.3"; + ghostElement.style.pointerEvents = "none"; + element.parentNode?.insertBefore(ghostElement, element); + element.classList.add("dragging"); + const targetY = e.clientY - columnRect.top - mouseOffset.y; + this.dragState = { + eventId, + element, + ghostElement, + startY, + mouseOffset, + columnElement, + currentColumn: columnElement, + targetY: Math.max(0, targetY), + currentY: startY, + animationId: 0, + sourceColumnKey: columnElement.dataset.columnKey || "", + dragSource: "grid" + }; + const payload = { + eventId, + element, + ghostElement, + startY, + mouseOffset, + columnElement + }; + this.eventBus.emit(CoreEvents.EVENT_DRAG_START, payload); + this.animateDrag(); + } + updateDragTarget(e) { + if (!this.dragState) + return; + this.checkHeaderZone(e); + if (this.inHeader) + return; + const columnAtPoint = this.getColumnAtPoint(e.clientX); + if (this.dragState.dragSource === "header" && columnAtPoint && !this.dragState.currentColumn) { + this.dragState.currentColumn = columnAtPoint; + this.dragState.columnElement = columnAtPoint; + } + if (columnAtPoint && columnAtPoint !== this.dragState.currentColumn && this.dragState.currentColumn) { + const payload = { + eventId: this.dragState.eventId, + element: this.dragState.element, + previousColumn: this.dragState.currentColumn, + newColumn: columnAtPoint, + currentY: this.dragState.currentY + }; + this.eventBus.emit(CoreEvents.EVENT_DRAG_COLUMN_CHANGE, payload); + this.dragState.currentColumn = columnAtPoint; + this.dragState.columnElement = columnAtPoint; + } + if (!this.dragState.columnElement) + return; + const columnRect = this.dragState.columnElement.getBoundingClientRect(); + const targetY = e.clientY - columnRect.top - this.dragState.mouseOffset.y; + this.dragState.targetY = Math.max(0, targetY); + if (!this.dragState.animationId) { + this.animateDrag(); + } + } + /** + * Check if pointer is in header zone and emit appropriate events + */ + checkHeaderZone(e) { + if (!this.dragState) + return; + const headerViewport = document.querySelector("swp-header-viewport"); + if (!headerViewport) + return; + const rect = headerViewport.getBoundingClientRect(); + const isInHeader = e.clientY < rect.bottom; + if (isInHeader && !this.inHeader) { + this.inHeader = true; + if (this.dragState.dragSource === "grid" && this.dragState.columnElement) { + const payload = { + eventId: this.dragState.eventId, + element: this.dragState.element, + sourceColumnIndex: this.getColumnIndex(this.dragState.columnElement), + sourceColumnKey: this.dragState.columnElement.dataset.columnKey || "", + title: this.dragState.element.querySelector("swp-event-title")?.textContent || "", + colorClass: [...this.dragState.element.classList].find((c) => c.startsWith("is-")), + itemType: "event", + duration: 1 + }; + this.eventBus.emit(CoreEvents.EVENT_DRAG_ENTER_HEADER, payload); + } + } else if (!isInHeader && this.inHeader) { + this.inHeader = false; + const targetColumn = this.getColumnAtPoint(e.clientX); + if (this.dragState.dragSource === "header") { + const payload = { + eventId: this.dragState.eventId, + source: "header", + element: this.dragState.element, + targetColumn: targetColumn || void 0, + start: this.dragState.element.dataset.start ? new Date(this.dragState.element.dataset.start) : void 0, + end: this.dragState.element.dataset.end ? new Date(this.dragState.element.dataset.end) : void 0, + title: this.dragState.element.textContent || "", + colorClass: [...this.dragState.element.classList].find((c) => c.startsWith("is-")) + }; + this.eventBus.emit(CoreEvents.EVENT_DRAG_LEAVE_HEADER, payload); + if (targetColumn) { + const newElement = targetColumn.querySelector(`swp-event[data-event-id="${this.dragState.eventId}"]`); + if (newElement) { + this.dragState.element = newElement; + this.dragState.columnElement = targetColumn; + this.dragState.currentColumn = targetColumn; + this.animateDrag(); + } + } + } else { + const payload = { + eventId: this.dragState.eventId, + source: "grid" + }; + this.eventBus.emit(CoreEvents.EVENT_DRAG_LEAVE_HEADER, payload); + } + } else if (isInHeader) { + const column = this.getColumnAtX(e.clientX); + if (column) { + const payload = { + eventId: this.dragState.eventId, + columnIndex: this.getColumnIndex(column), + columnKey: column.dataset.columnKey || "" + }; + this.eventBus.emit(CoreEvents.EVENT_DRAG_MOVE_HEADER, payload); + } + } + } + /** + * Get column index (0-based) for a column element + */ + getColumnIndex(column) { + if (!this.container || !column) + return 0; + const columns = Array.from(this.container.querySelectorAll("swp-day-column")); + return columns.indexOf(column); + } + /** + * Get column at X coordinate (alias for getColumnAtPoint) + */ + getColumnAtX(clientX) { + return this.getColumnAtPoint(clientX); + } + /** + * Find column element at given X coordinate + */ + getColumnAtPoint(clientX) { + if (!this.container) + return null; + const columns = this.container.querySelectorAll("swp-day-column"); + for (const col of columns) { + const rect = col.getBoundingClientRect(); + if (clientX >= rect.left && clientX <= rect.right) { + return col; + } + } + return null; + } + /** + * Cancel drag and animate back to start position + */ + cancelDrag() { + if (!this.dragState) + return; + cancelAnimationFrame(this.dragState.animationId); + const { element, ghostElement, startY, eventId } = this.dragState; + element.style.transition = "top 200ms ease-out"; + element.style.top = `${startY}px`; + setTimeout(() => { + ghostElement?.remove(); + element.style.transition = ""; + element.classList.remove("dragging"); + }, 200); + const payload = { + eventId, + element, + startY + }; + this.eventBus.emit(CoreEvents.EVENT_DRAG_CANCEL, payload); + this.dragState = null; + this.inHeader = false; + } +}; +__name(_DragDropManager, "DragDropManager"); +var DragDropManager = _DragDropManager; + +// src/managers/EdgeScrollManager.ts +var _EdgeScrollManager = class _EdgeScrollManager { + constructor(eventBus) { + this.eventBus = eventBus; + this.scrollableContent = null; + this.timeGrid = null; + this.draggedElement = null; + this.scrollRAF = null; + this.mouseY = 0; + this.isDragging = false; + this.isScrolling = false; + this.lastTs = 0; + this.rect = null; + this.initialScrollTop = 0; + this.OUTER_ZONE = 100; + this.INNER_ZONE = 50; + this.SLOW_SPEED = 140; + this.FAST_SPEED = 640; + this.trackMouse = (e) => { + if (this.isDragging) { + this.mouseY = e.clientY; + } + }; + this.scrollTick = (ts) => { + if (!this.isDragging || !this.scrollableContent) + return; + const dt = this.lastTs ? (ts - this.lastTs) / 1e3 : 0; + this.lastTs = ts; + this.rect ?? (this.rect = this.scrollableContent.getBoundingClientRect()); + const velocity = this.calculateVelocity(); + if (velocity !== 0 && !this.isAtBoundary(velocity)) { + const scrollDelta = velocity * dt; + this.scrollableContent.scrollTop += scrollDelta; + this.rect = null; + this.eventBus.emit(CoreEvents.EDGE_SCROLL_TICK, { scrollDelta }); + this.setScrollingState(true); + } else { + this.setScrollingState(false); + } + this.scrollRAF = requestAnimationFrame(this.scrollTick); + }; + this.subscribeToEvents(); + document.addEventListener("pointermove", this.trackMouse); + } + init(scrollableContent) { + this.scrollableContent = scrollableContent; + this.timeGrid = scrollableContent.querySelector("swp-time-grid"); + this.scrollableContent.style.scrollBehavior = "auto"; + } + subscribeToEvents() { + this.eventBus.on(CoreEvents.EVENT_DRAG_START, (event) => { + const payload = event.detail; + this.draggedElement = payload.element; + this.startDrag(); + }); + this.eventBus.on(CoreEvents.EVENT_DRAG_END, () => this.stopDrag()); + this.eventBus.on(CoreEvents.EVENT_DRAG_CANCEL, () => this.stopDrag()); + } + startDrag() { + this.isDragging = true; + this.isScrolling = false; + this.lastTs = 0; + this.initialScrollTop = this.scrollableContent?.scrollTop ?? 0; + if (this.scrollRAF === null) { + this.scrollRAF = requestAnimationFrame(this.scrollTick); + } + } + stopDrag() { + this.isDragging = false; + this.setScrollingState(false); + if (this.scrollRAF !== null) { + cancelAnimationFrame(this.scrollRAF); + this.scrollRAF = null; + } + this.rect = null; + this.lastTs = 0; + this.initialScrollTop = 0; + } + calculateVelocity() { + if (!this.rect) + return 0; + const distTop = this.mouseY - this.rect.top; + const distBot = this.rect.bottom - this.mouseY; + if (distTop < this.INNER_ZONE) + return -this.FAST_SPEED; + if (distTop < this.OUTER_ZONE) + return -this.SLOW_SPEED; + if (distBot < this.INNER_ZONE) + return this.FAST_SPEED; + if (distBot < this.OUTER_ZONE) + return this.SLOW_SPEED; + return 0; + } + isAtBoundary(velocity) { + if (!this.scrollableContent || !this.timeGrid || !this.draggedElement) + return false; + const atTop = this.scrollableContent.scrollTop <= 0 && velocity < 0; + const atBottom = velocity > 0 && this.draggedElement.getBoundingClientRect().bottom >= this.timeGrid.getBoundingClientRect().bottom; + return atTop || atBottom; + } + setScrollingState(scrolling) { + if (this.isScrolling === scrolling) + return; + this.isScrolling = scrolling; + if (scrolling) { + this.eventBus.emit(CoreEvents.EDGE_SCROLL_STARTED, {}); + } else { + this.initialScrollTop = this.scrollableContent?.scrollTop ?? 0; + this.eventBus.emit(CoreEvents.EDGE_SCROLL_STOPPED, {}); + } + } +}; +__name(_EdgeScrollManager, "EdgeScrollManager"); +var EdgeScrollManager = _EdgeScrollManager; + +// src/managers/ResizeManager.ts +var _ResizeManager = class _ResizeManager { + constructor(eventBus, gridConfig, dateService) { + this.eventBus = eventBus; + this.gridConfig = gridConfig; + this.dateService = dateService; + this.container = null; + this.resizeState = null; + this.Z_INDEX_RESIZING = "1000"; + this.ANIMATION_SPEED = 0.35; + this.MIN_HEIGHT_MINUTES = 15; + this.handleMouseOver = (e) => { + const target = e.target; + const eventElement = target.closest("swp-event"); + if (!eventElement || this.resizeState) + return; + if (!eventElement.querySelector(":scope > swp-resize-handle")) { + const handle = this.createResizeHandle(); + eventElement.appendChild(handle); + } + }; + this.handlePointerDown = (e) => { + const handle = e.target.closest("swp-resize-handle"); + if (!handle) + return; + const element = handle.parentElement; + if (!element) + return; + const eventId = element.dataset.eventId || ""; + const startHeight = element.offsetHeight; + const startDurationMinutes = pixelsToMinutes(startHeight, this.gridConfig); + const container2 = element.closest("swp-event-group") ?? element; + const prevZIndex = container2.style.zIndex; + this.resizeState = { + eventId, + element, + handleElement: handle, + startY: e.clientY, + startHeight, + startDurationMinutes, + pointerId: e.pointerId, + prevZIndex, + // Animation state + currentHeight: startHeight, + targetHeight: startHeight, + animationId: null + }; + container2.style.zIndex = this.Z_INDEX_RESIZING; + try { + handle.setPointerCapture(e.pointerId); + } catch (err) { + console.warn("Pointer capture failed:", err); + } + document.documentElement.classList.add("swp--resizing"); + this.eventBus.emit(CoreEvents.EVENT_RESIZE_START, { + eventId, + element, + startHeight + }); + e.preventDefault(); + }; + this.handlePointerMove = (e) => { + if (!this.resizeState) + return; + const deltaY = e.clientY - this.resizeState.startY; + const minHeight = this.MIN_HEIGHT_MINUTES / 60 * this.gridConfig.hourHeight; + const newHeight = Math.max(minHeight, this.resizeState.startHeight + deltaY); + this.resizeState.targetHeight = newHeight; + if (this.resizeState.animationId === null) { + this.animateHeight(); + } + }; + this.animateHeight = () => { + if (!this.resizeState) + return; + const diff2 = this.resizeState.targetHeight - this.resizeState.currentHeight; + if (Math.abs(diff2) < 0.5) { + this.resizeState.animationId = null; + return; + } + this.resizeState.currentHeight += diff2 * this.ANIMATION_SPEED; + this.resizeState.element.style.height = `${this.resizeState.currentHeight}px`; + this.updateTimestampDisplay(); + this.resizeState.animationId = requestAnimationFrame(this.animateHeight); + }; + this.handlePointerUp = (e) => { + if (!this.resizeState) + return; + if (this.resizeState.animationId !== null) { + cancelAnimationFrame(this.resizeState.animationId); + } + try { + this.resizeState.handleElement.releasePointerCapture(e.pointerId); + } catch (err) { + console.warn("Pointer release failed:", err); + } + this.snapToGridFinal(); + this.updateTimestampDisplay(); + const container2 = this.resizeState.element.closest("swp-event-group") ?? this.resizeState.element; + container2.style.zIndex = this.resizeState.prevZIndex; + document.documentElement.classList.remove("swp--resizing"); + const column = this.resizeState.element.closest("swp-day-column"); + const columnKey = column?.dataset.columnKey || ""; + const date = column?.dataset.date || ""; + const swpEvent = SwpEvent.fromElement(this.resizeState.element, columnKey, date, this.gridConfig); + this.eventBus.emit(CoreEvents.EVENT_RESIZE_END, { + swpEvent + }); + this.resizeState = null; + }; + } + /** + * Initialize resize functionality on container + */ + init(container2) { + this.container = container2; + container2.addEventListener("mouseover", this.handleMouseOver, true); + document.addEventListener("pointerdown", this.handlePointerDown, true); + document.addEventListener("pointermove", this.handlePointerMove, true); + document.addEventListener("pointerup", this.handlePointerUp, true); + } + /** + * Create resize handle element + */ + createResizeHandle() { + const handle = document.createElement("swp-resize-handle"); + handle.setAttribute("aria-label", "Resize event"); + handle.setAttribute("role", "separator"); + return handle; + } + /** + * Update timestamp display with snapped end time + */ + updateTimestampDisplay() { + if (!this.resizeState) + return; + const timeEl = this.resizeState.element.querySelector("swp-event-time"); + if (!timeEl) + return; + const top = parseFloat(this.resizeState.element.style.top) || 0; + const startMinutesFromGrid = pixelsToMinutes(top, this.gridConfig); + const startMinutes = this.gridConfig.dayStartHour * 60 + startMinutesFromGrid; + const snappedHeight = snapToGrid(this.resizeState.currentHeight, this.gridConfig); + const durationMinutes = pixelsToMinutes(snappedHeight, this.gridConfig); + const endMinutes = startMinutes + durationMinutes; + const start = this.minutesToDate(startMinutes); + const end = this.minutesToDate(endMinutes); + timeEl.textContent = this.dateService.formatTimeRange(start, end); + } + /** + * Convert minutes since midnight to Date + */ + minutesToDate(minutes) { + const date = /* @__PURE__ */ new Date(); + date.setHours(Math.floor(minutes / 60) % 24, minutes % 60, 0, 0); + return date; + } + /** + * Snap final height to grid interval + */ + snapToGridFinal() { + if (!this.resizeState) + return; + const currentHeight = this.resizeState.element.offsetHeight; + const snappedHeight = snapToGrid(currentHeight, this.gridConfig); + const minHeight = minutesToPixels(this.MIN_HEIGHT_MINUTES, this.gridConfig); + const finalHeight = Math.max(minHeight, snappedHeight); + this.resizeState.element.style.height = `${finalHeight}px`; + this.resizeState.currentHeight = finalHeight; + } +}; +__name(_ResizeManager, "ResizeManager"); +var ResizeManager = _ResizeManager; + +// src/managers/EventPersistenceManager.ts +var _EventPersistenceManager = class _EventPersistenceManager { + constructor(eventService, eventBus, dateService) { + this.eventService = eventService; + this.eventBus = eventBus; + this.dateService = dateService; + this.handleDragEnd = async (e) => { + const payload = e.detail; + const { swpEvent } = payload; + const event = await this.eventService.get(swpEvent.eventId); + if (!event) { + console.warn(`EventPersistenceManager: Event ${swpEvent.eventId} not found`); + return; + } + const { resource } = this.dateService.parseColumnKey(swpEvent.columnKey); + const updatedEvent = { + ...event, + start: swpEvent.start, + end: swpEvent.end, + resourceId: resource ?? event.resourceId, + allDay: payload.target === "header", + syncStatus: "pending" + }; + await this.eventService.save(updatedEvent); + const updatePayload = { + eventId: updatedEvent.id, + sourceColumnKey: payload.sourceColumnKey, + targetColumnKey: swpEvent.columnKey + }; + this.eventBus.emit(CoreEvents.EVENT_UPDATED, updatePayload); + }; + this.handleResizeEnd = async (e) => { + const payload = e.detail; + const { swpEvent } = payload; + const event = await this.eventService.get(swpEvent.eventId); + if (!event) { + console.warn(`EventPersistenceManager: Event ${swpEvent.eventId} not found`); + return; + } + const updatedEvent = { + ...event, + end: swpEvent.end, + syncStatus: "pending" + }; + await this.eventService.save(updatedEvent); + const updatePayload = { + eventId: updatedEvent.id, + sourceColumnKey: swpEvent.columnKey, + targetColumnKey: swpEvent.columnKey + }; + this.eventBus.emit(CoreEvents.EVENT_UPDATED, updatePayload); + }; + this.setupListeners(); + } + setupListeners() { + this.eventBus.on(CoreEvents.EVENT_DRAG_END, this.handleDragEnd); + this.eventBus.on(CoreEvents.EVENT_RESIZE_END, this.handleResizeEnd); + } +}; +__name(_EventPersistenceManager, "EventPersistenceManager"); +var EventPersistenceManager = _EventPersistenceManager; + +// src/CompositionRoot.ts +var defaultTimeFormatConfig = { + timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, + use24HourFormat: true, + locale: "da-DK", + dateFormat: "locale", + showSeconds: false +}; +var defaultGridConfig = { + hourHeight: 64, + dayStartHour: 6, + dayEndHour: 18, + snapInterval: 15, + gridStartThresholdMinutes: 30 +}; +function createContainer() { + const container2 = new Container(); + const builder = container2.builder(); + builder.registerInstance(defaultTimeFormatConfig).as("ITimeFormatConfig"); + builder.registerInstance(defaultGridConfig).as("IGridConfig"); + builder.registerType(EventBus).as("EventBus"); + builder.registerType(EventBus).as("IEventBus"); + builder.registerType(DateService).as("DateService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("ITimeFormatConfig"), + void 0 + ] + }); + builder.registerType(IndexedDBContext).as("IndexedDBContext").autoWire({ + mapResolvers: [ + (c) => c.resolveTypeAll("IStore") + ] + }); + builder.registerType(EventStore).as("IStore"); + builder.registerType(ResourceStore).as("IStore"); + builder.registerType(BookingStore).as("IStore"); + builder.registerType(CustomerStore).as("IStore"); + builder.registerType(TeamStore).as("IStore"); + builder.registerType(DepartmentStore).as("IStore"); + builder.registerType(ScheduleOverrideStore).as("IStore"); + builder.registerType(AuditStore).as("IStore"); + builder.registerType(SettingsStore).as("IStore"); + builder.registerType(ViewConfigStore).as("IStore"); + builder.registerType(EventService).as("IEntityService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(EventService).as("IEntityService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(EventService).as("EventService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(ResourceService).as("IEntityService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(ResourceService).as("IEntityService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(ResourceService).as("ResourceService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(BookingService).as("IEntityService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(BookingService).as("IEntityService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(BookingService).as("BookingService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(CustomerService).as("IEntityService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(CustomerService).as("IEntityService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(CustomerService).as("CustomerService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(TeamService).as("IEntityService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(TeamService).as("IEntityService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(TeamService).as("TeamService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(DepartmentService).as("IEntityService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(DepartmentService).as("IEntityService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(DepartmentService).as("DepartmentService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(SettingsService).as("IEntityService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(SettingsService).as("IEntityService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(SettingsService).as("SettingsService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(ViewConfigService).as("IEntityService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(ViewConfigService).as("IEntityService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(ViewConfigService).as("ViewConfigService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(MockEventRepository).as("IApiRepository"); + builder.registerType(MockEventRepository).as("IApiRepository"); + builder.registerType(MockResourceRepository).as("IApiRepository"); + builder.registerType(MockResourceRepository).as("IApiRepository"); + builder.registerType(MockBookingRepository).as("IApiRepository"); + builder.registerType(MockBookingRepository).as("IApiRepository"); + builder.registerType(MockCustomerRepository).as("IApiRepository"); + builder.registerType(MockCustomerRepository).as("IApiRepository"); + builder.registerType(MockAuditRepository).as("IApiRepository"); + builder.registerType(MockAuditRepository).as("IApiRepository"); + builder.registerType(MockTeamRepository).as("IApiRepository"); + builder.registerType(MockTeamRepository).as("IApiRepository"); + builder.registerType(MockDepartmentRepository).as("IApiRepository"); + builder.registerType(MockDepartmentRepository).as("IApiRepository"); + builder.registerType(MockSettingsRepository).as("IApiRepository"); + builder.registerType(MockSettingsRepository).as("IApiRepository"); + builder.registerType(MockViewConfigRepository).as("IApiRepository"); + builder.registerType(MockViewConfigRepository).as("IApiRepository"); + builder.registerType(AuditService).as("AuditService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(DataSeeder).as("DataSeeder").autoWire({ + mapResolvers: [ + (c) => c.resolveTypeAll("IEntityService"), + (c) => c.resolveTypeAll("IApiRepository") + ] + }); + builder.registerType(ScheduleOverrideService).as("ScheduleOverrideService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext") + ] + }); + builder.registerType(ResourceScheduleService).as("ResourceScheduleService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("ResourceService"), + (c) => c.resolveType("ScheduleOverrideService"), + (c) => c.resolveType("DateService") + ] + }); + builder.registerType(EventRenderer).as("EventRenderer").autoWire({ + mapResolvers: [ + (c) => c.resolveType("EventService"), + (c) => c.resolveType("DateService"), + (c) => c.resolveType("IGridConfig"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(ScheduleRenderer).as("ScheduleRenderer").autoWire({ + mapResolvers: [ + (c) => c.resolveType("ResourceScheduleService"), + (c) => c.resolveType("DateService"), + (c) => c.resolveType("IGridConfig") + ] + }); + builder.registerType(HeaderDrawerRenderer).as("HeaderDrawerRenderer").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IEventBus"), + (c) => c.resolveType("IGridConfig"), + (c) => c.resolveType("HeaderDrawerManager"), + (c) => c.resolveType("EventService"), + (c) => c.resolveType("DateService") + ] + }); + builder.registerType(DateRenderer).as("IRenderer").autoWire({ + mapResolvers: [ + (c) => c.resolveType("DateService") + ] + }); + builder.registerType(ResourceRenderer).as("IRenderer").autoWire({ + mapResolvers: [ + (c) => c.resolveType("ResourceService") + ] + }); + builder.registerType(TeamRenderer).as("IRenderer").autoWire({ + mapResolvers: [ + (c) => c.resolveType("TeamService") + ] + }); + builder.registerType(DepartmentRenderer).as("IRenderer").autoWire({ + mapResolvers: [ + (c) => c.resolveType("DepartmentService") + ] + }); + builder.registerType(MockTeamStore).as("IGroupingStore"); + builder.registerType(MockResourceStore).as("IGroupingStore"); + builder.registerType(CalendarOrchestrator).as("CalendarOrchestrator").autoWire({ + mapResolvers: [ + (c) => c.resolveTypeAll("IRenderer"), + (c) => c.resolveType("EventRenderer"), + (c) => c.resolveType("ScheduleRenderer"), + (c) => c.resolveType("HeaderDrawerRenderer"), + (c) => c.resolveType("DateService"), + (c) => c.resolveTypeAll("IEntityService") + ] + }); + builder.registerType(TimeAxisRenderer).as("TimeAxisRenderer"); + builder.registerType(ScrollManager).as("ScrollManager"); + builder.registerType(HeaderDrawerManager).as("HeaderDrawerManager"); + builder.registerType(DragDropManager).as("DragDropManager").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IEventBus"), + (c) => c.resolveType("IGridConfig") + ] + }); + builder.registerType(EdgeScrollManager).as("EdgeScrollManager").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(ResizeManager).as("ResizeManager").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IEventBus"), + (c) => c.resolveType("IGridConfig"), + (c) => c.resolveType("DateService") + ] + }); + builder.registerType(EventPersistenceManager).as("EventPersistenceManager").autoWire({ + mapResolvers: [ + (c) => c.resolveType("EventService"), + (c) => c.resolveType("IEventBus"), + (c) => c.resolveType("DateService") + ] + }); + builder.registerType(CalendarApp).as("CalendarApp").autoWire({ + mapResolvers: [ + (c) => c.resolveType("CalendarOrchestrator"), + (c) => c.resolveType("TimeAxisRenderer"), + (c) => c.resolveType("DateService"), + (c) => c.resolveType("ScrollManager"), + (c) => c.resolveType("HeaderDrawerManager"), + (c) => c.resolveType("DragDropManager"), + (c) => c.resolveType("EdgeScrollManager"), + (c) => c.resolveType("ResizeManager"), + (c) => c.resolveType("HeaderDrawerRenderer"), + (c) => c.resolveType("EventPersistenceManager"), + (c) => c.resolveType("SettingsService"), + (c) => c.resolveType("ViewConfigService"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(DemoApp).as("DemoApp").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("DataSeeder"), + (c) => c.resolveType("AuditService"), + (c) => c.resolveType("CalendarApp"), + (c) => c.resolveType("DateService"), + (c) => c.resolveType("ResourceService"), + (c) => c.resolveType("IEventBus") + ] + }); + return builder.build(); +} +__name(createContainer, "createContainer"); + +// src/demo/index.ts +var container = createContainer(); +container.resolveType("DemoApp").init().catch(console.error); +//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vbm9kZV9tb2R1bGVzL2RheWpzL2RheWpzLm1pbi5qcyIsICIuLi8uLi9ub2RlX21vZHVsZXMvZGF5anMvcGx1Z2luL3V0Yy5qcyIsICIuLi8uLi9ub2RlX21vZHVsZXMvZGF5anMvcGx1Z2luL3RpbWV6b25lLmpzIiwgIi4uLy4uL25vZGVfbW9kdWxlcy9kYXlqcy9wbHVnaW4vaXNvV2Vlay5qcyIsICIuLi8uLi9ub2RlX21vZHVsZXMvQG5vdmFkaS9jb3JlL2Rpc3QvdG9rZW4uanMiLCAiLi4vLi4vbm9kZV9tb2R1bGVzL0Bub3ZhZGkvY29yZS9kaXN0L2Vycm9ycy5qcyIsICIuLi8uLi9ub2RlX21vZHVsZXMvQG5vdmFkaS9jb3JlL2Rpc3QvYXV0b3dpcmUuanMiLCAiLi4vLi4vbm9kZV9tb2R1bGVzL0Bub3ZhZGkvY29yZS9kaXN0L2J1aWxkZXIuanMiLCAiLi4vLi4vbm9kZV9tb2R1bGVzL0Bub3ZhZGkvY29yZS9kaXN0L2NvbnRhaW5lci5qcyIsICIuLi8uLi9zcmMvZmVhdHVyZXMvZGF0ZS9EYXRlUmVuZGVyZXIudHMiLCAiLi4vLi4vc3JjL2NvcmUvRGF0ZVNlcnZpY2UudHMiLCAiLi4vLi4vc3JjL2NvcmUvQmFzZUdyb3VwaW5nUmVuZGVyZXIudHMiLCAiLi4vLi4vc3JjL2ZlYXR1cmVzL3Jlc291cmNlL1Jlc291cmNlUmVuZGVyZXIudHMiLCAiLi4vLi4vc3JjL2ZlYXR1cmVzL3RlYW0vVGVhbVJlbmRlcmVyLnRzIiwgIi4uLy4uL3NyYy9mZWF0dXJlcy9kZXBhcnRtZW50L0RlcGFydG1lbnRSZW5kZXJlci50cyIsICIuLi8uLi9zcmMvY29yZS9SZW5kZXJCdWlsZGVyLnRzIiwgIi4uLy4uL3NyYy9jb3JlL0ZpbHRlclRlbXBsYXRlLnRzIiwgIi4uLy4uL3NyYy9jb3JlL0NhbGVuZGFyT3JjaGVzdHJhdG9yLnRzIiwgIi4uLy4uL3NyYy9jb3JlL05hdmlnYXRpb25BbmltYXRvci50cyIsICIuLi8uLi9zcmMvY29yZS9DYWxlbmRhckV2ZW50cy50cyIsICIuLi8uLi9zcmMvY29yZS9DYWxlbmRhckFwcC50cyIsICIuLi8uLi9zcmMvZmVhdHVyZXMvdGltZWF4aXMvVGltZUF4aXNSZW5kZXJlci50cyIsICIuLi8uLi9zcmMvY29yZS9TY3JvbGxNYW5hZ2VyLnRzIiwgIi4uLy4uL3NyYy9jb3JlL0hlYWRlckRyYXdlck1hbmFnZXIudHMiLCAiLi4vLi4vc3JjL2RlbW8vTW9ja1N0b3Jlcy50cyIsICIuLi8uLi9zcmMvZGVtby9EZW1vQXBwLnRzIiwgIi4uLy4uL3NyYy9jb3JlL0V2ZW50QnVzLnRzIiwgIi4uLy4uL3NyYy9zdG9yYWdlL0luZGV4ZWREQkNvbnRleHQudHMiLCAiLi4vLi4vc3JjL3N0b3JhZ2UvZXZlbnRzL0V2ZW50U3RvcmUudHMiLCAiLi4vLi4vc3JjL3N0b3JhZ2UvZXZlbnRzL0V2ZW50U2VyaWFsaXphdGlvbi50cyIsICIuLi8uLi9zcmMvc3RvcmFnZS9TeW5jUGx1Z2luLnRzIiwgIi4uLy4uL3NyYy9jb25zdGFudHMvQ29yZUV2ZW50cy50cyIsICIuLi8uLi9ub2RlX21vZHVsZXMvanNvbi1kaWZmLXRzL3NyYy9oZWxwZXJzLnRzIiwgIi4uLy4uL25vZGVfbW9kdWxlcy9qc29uLWRpZmYtdHMvc3JjL2pzb25EaWZmLnRzIiwgIi4uLy4uL25vZGVfbW9kdWxlcy9qc29uLWRpZmYtdHMvc3JjL2pzb25Db21wYXJlLnRzIiwgIi4uLy4uL3NyYy9zdG9yYWdlL0Jhc2VFbnRpdHlTZXJ2aWNlLnRzIiwgIi4uLy4uL3NyYy9zdG9yYWdlL2V2ZW50cy9FdmVudFNlcnZpY2UudHMiLCAiLi4vLi4vc3JjL3N0b3JhZ2UvcmVzb3VyY2VzL1Jlc291cmNlU3RvcmUudHMiLCAiLi4vLi4vc3JjL3N0b3JhZ2UvcmVzb3VyY2VzL1Jlc291cmNlU2VydmljZS50cyIsICIuLi8uLi9zcmMvc3RvcmFnZS9ib29raW5ncy9Cb29raW5nU3RvcmUudHMiLCAiLi4vLi4vc3JjL3N0b3JhZ2UvYm9va2luZ3MvQm9va2luZ1NlcnZpY2UudHMiLCAiLi4vLi4vc3JjL3N0b3JhZ2UvY3VzdG9tZXJzL0N1c3RvbWVyU3RvcmUudHMiLCAiLi4vLi4vc3JjL3N0b3JhZ2UvY3VzdG9tZXJzL0N1c3RvbWVyU2VydmljZS50cyIsICIuLi8uLi9zcmMvc3RvcmFnZS90ZWFtcy9UZWFtU3RvcmUudHMiLCAiLi4vLi4vc3JjL3N0b3JhZ2UvdGVhbXMvVGVhbVNlcnZpY2UudHMiLCAiLi4vLi4vc3JjL3N0b3JhZ2UvZGVwYXJ0bWVudHMvRGVwYXJ0bWVudFN0b3JlLnRzIiwgIi4uLy4uL3NyYy9zdG9yYWdlL2RlcGFydG1lbnRzL0RlcGFydG1lbnRTZXJ2aWNlLnRzIiwgIi4uLy4uL3NyYy9zdG9yYWdlL3NldHRpbmdzL1NldHRpbmdzU3RvcmUudHMiLCAiLi4vLi4vc3JjL3R5cGVzL1NldHRpbmdzVHlwZXMudHMiLCAiLi4vLi4vc3JjL3N0b3JhZ2Uvc2V0dGluZ3MvU2V0dGluZ3NTZXJ2aWNlLnRzIiwgIi4uLy4uL3NyYy9zdG9yYWdlL3ZpZXdjb25maWdzL1ZpZXdDb25maWdTdG9yZS50cyIsICIuLi8uLi9zcmMvc3RvcmFnZS92aWV3Y29uZmlncy9WaWV3Q29uZmlnU2VydmljZS50cyIsICIuLi8uLi9zcmMvc3RvcmFnZS9hdWRpdC9BdWRpdFN0b3JlLnRzIiwgIi4uLy4uL3NyYy9zdG9yYWdlL2F1ZGl0L0F1ZGl0U2VydmljZS50cyIsICIuLi8uLi9zcmMvcmVwb3NpdG9yaWVzL01vY2tFdmVudFJlcG9zaXRvcnkudHMiLCAiLi4vLi4vc3JjL3JlcG9zaXRvcmllcy9Nb2NrUmVzb3VyY2VSZXBvc2l0b3J5LnRzIiwgIi4uLy4uL3NyYy9yZXBvc2l0b3JpZXMvTW9ja0Jvb2tpbmdSZXBvc2l0b3J5LnRzIiwgIi4uLy4uL3NyYy9yZXBvc2l0b3JpZXMvTW9ja0N1c3RvbWVyUmVwb3NpdG9yeS50cyIsICIuLi8uLi9zcmMvcmVwb3NpdG9yaWVzL01vY2tBdWRpdFJlcG9zaXRvcnkudHMiLCAiLi4vLi4vc3JjL3JlcG9zaXRvcmllcy9Nb2NrVGVhbVJlcG9zaXRvcnkudHMiLCAiLi4vLi4vc3JjL3JlcG9zaXRvcmllcy9Nb2NrRGVwYXJ0bWVudFJlcG9zaXRvcnkudHMiLCAiLi4vLi4vc3JjL3JlcG9zaXRvcmllcy9Nb2NrU2V0dGluZ3NSZXBvc2l0b3J5LnRzIiwgIi4uLy4uL3NyYy9yZXBvc2l0b3JpZXMvTW9ja1ZpZXdDb25maWdSZXBvc2l0b3J5LnRzIiwgIi4uLy4uL3NyYy93b3JrZXJzL0RhdGFTZWVkZXIudHMiLCAiLi4vLi4vc3JjL3V0aWxzL1Bvc2l0aW9uVXRpbHMudHMiLCAiLi4vLi4vc3JjL2ZlYXR1cmVzL2V2ZW50L0V2ZW50TGF5b3V0RW5naW5lLnRzIiwgIi4uLy4uL3NyYy9mZWF0dXJlcy9ldmVudC9FdmVudFJlbmRlcmVyLnRzIiwgIi4uLy4uL3NyYy9mZWF0dXJlcy9zY2hlZHVsZS9TY2hlZHVsZVJlbmRlcmVyLnRzIiwgIi4uLy4uL3NyYy9mZWF0dXJlcy9oZWFkZXJkcmF3ZXIvSGVhZGVyRHJhd2VyUmVuZGVyZXIudHMiLCAiLi4vLi4vc3JjL3N0b3JhZ2Uvc2NoZWR1bGVzL1NjaGVkdWxlT3ZlcnJpZGVTdG9yZS50cyIsICIuLi8uLi9zcmMvc3RvcmFnZS9zY2hlZHVsZXMvU2NoZWR1bGVPdmVycmlkZVNlcnZpY2UudHMiLCAiLi4vLi4vc3JjL3N0b3JhZ2Uvc2NoZWR1bGVzL1Jlc291cmNlU2NoZWR1bGVTZXJ2aWNlLnRzIiwgIi4uLy4uL3NyYy90eXBlcy9Td3BFdmVudC50cyIsICIuLi8uLi9zcmMvbWFuYWdlcnMvRHJhZ0Ryb3BNYW5hZ2VyLnRzIiwgIi4uLy4uL3NyYy9tYW5hZ2Vycy9FZGdlU2Nyb2xsTWFuYWdlci50cyIsICIuLi8uLi9zcmMvbWFuYWdlcnMvUmVzaXplTWFuYWdlci50cyIsICIuLi8uLi9zcmMvbWFuYWdlcnMvRXZlbnRQZXJzaXN0ZW5jZU1hbmFnZXIudHMiLCAiLi4vLi4vc3JjL0NvbXBvc2l0aW9uUm9vdC50cyIsICIuLi8uLi9zcmMvZGVtby9pbmRleC50cyJdLAogICJzb3VyY2VzQ29udGVudCI6IFsiIWZ1bmN0aW9uKHQsZSl7XCJvYmplY3RcIj09dHlwZW9mIGV4cG9ydHMmJlwidW5kZWZpbmVkXCIhPXR5cGVvZiBtb2R1bGU/bW9kdWxlLmV4cG9ydHM9ZSgpOlwiZnVuY3Rpb25cIj09dHlwZW9mIGRlZmluZSYmZGVmaW5lLmFtZD9kZWZpbmUoZSk6KHQ9XCJ1bmRlZmluZWRcIiE9dHlwZW9mIGdsb2JhbFRoaXM/Z2xvYmFsVGhpczp0fHxzZWxmKS5kYXlqcz1lKCl9KHRoaXMsKGZ1bmN0aW9uKCl7XCJ1c2Ugc3RyaWN0XCI7dmFyIHQ9MWUzLGU9NmU0LG49MzZlNSxyPVwibWlsbGlzZWNvbmRcIixpPVwic2Vjb25kXCIscz1cIm1pbnV0ZVwiLHU9XCJob3VyXCIsYT1cImRheVwiLG89XCJ3ZWVrXCIsYz1cIm1vbnRoXCIsZj1cInF1YXJ0ZXJcIixoPVwieWVhclwiLGQ9XCJkYXRlXCIsbD1cIkludmFsaWQgRGF0ZVwiLCQ9L14oXFxkezR9KVstL10/KFxcZHsxLDJ9KT9bLS9dPyhcXGR7MCwyfSlbVHRcXHNdKihcXGR7MSwyfSk/Oj8oXFxkezEsMn0pPzo/KFxcZHsxLDJ9KT9bLjpdPyhcXGQrKT8kLyx5PS9cXFsoW15cXF1dKyldfFl7MSw0fXxNezEsNH18RHsxLDJ9fGR7MSw0fXxIezEsMn18aHsxLDJ9fGF8QXxtezEsMn18c3sxLDJ9fFp7MSwyfXxTU1MvZyxNPXtuYW1lOlwiZW5cIix3ZWVrZGF5czpcIlN1bmRheV9Nb25kYXlfVHVlc2RheV9XZWRuZXNkYXlfVGh1cnNkYXlfRnJpZGF5X1NhdHVyZGF5XCIuc3BsaXQoXCJfXCIpLG1vbnRoczpcIkphbnVhcnlfRmVicnVhcnlfTWFyY2hfQXByaWxfTWF5X0p1bmVfSnVseV9BdWd1c3RfU2VwdGVtYmVyX09jdG9iZXJfTm92ZW1iZXJfRGVjZW1iZXJcIi5zcGxpdChcIl9cIiksb3JkaW5hbDpmdW5jdGlvbih0KXt2YXIgZT1bXCJ0aFwiLFwic3RcIixcIm5kXCIsXCJyZFwiXSxuPXQlMTAwO3JldHVyblwiW1wiK3QrKGVbKG4tMjApJTEwXXx8ZVtuXXx8ZVswXSkrXCJdXCJ9fSxtPWZ1bmN0aW9uKHQsZSxuKXt2YXIgcj1TdHJpbmcodCk7cmV0dXJuIXJ8fHIubGVuZ3RoPj1lP3Q6XCJcIitBcnJheShlKzEtci5sZW5ndGgpLmpvaW4obikrdH0sdj17czptLHo6ZnVuY3Rpb24odCl7dmFyIGU9LXQudXRjT2Zmc2V0KCksbj1NYXRoLmFicyhlKSxyPU1hdGguZmxvb3Iobi82MCksaT1uJTYwO3JldHVybihlPD0wP1wiK1wiOlwiLVwiKSttKHIsMixcIjBcIikrXCI6XCIrbShpLDIsXCIwXCIpfSxtOmZ1bmN0aW9uIHQoZSxuKXtpZihlLmRhdGUoKTxuLmRhdGUoKSlyZXR1cm4tdChuLGUpO3ZhciByPTEyKihuLnllYXIoKS1lLnllYXIoKSkrKG4ubW9udGgoKS1lLm1vbnRoKCkpLGk9ZS5jbG9uZSgpLmFkZChyLGMpLHM9bi1pPDAsdT1lLmNsb25lKCkuYWRkKHIrKHM/LTE6MSksYyk7cmV0dXJuKygtKHIrKG4taSkvKHM/aS11OnUtaSkpfHwwKX0sYTpmdW5jdGlvbih0KXtyZXR1cm4gdDwwP01hdGguY2VpbCh0KXx8MDpNYXRoLmZsb29yKHQpfSxwOmZ1bmN0aW9uKHQpe3JldHVybntNOmMseTpoLHc6byxkOmEsRDpkLGg6dSxtOnMsczppLG1zOnIsUTpmfVt0XXx8U3RyaW5nKHR8fFwiXCIpLnRvTG93ZXJDYXNlKCkucmVwbGFjZSgvcyQvLFwiXCIpfSx1OmZ1bmN0aW9uKHQpe3JldHVybiB2b2lkIDA9PT10fX0sZz1cImVuXCIsRD17fTtEW2ddPU07dmFyIHA9XCIkaXNEYXlqc09iamVjdFwiLFM9ZnVuY3Rpb24odCl7cmV0dXJuIHQgaW5zdGFuY2VvZiBffHwhKCF0fHwhdFtwXSl9LHc9ZnVuY3Rpb24gdChlLG4scil7dmFyIGk7aWYoIWUpcmV0dXJuIGc7aWYoXCJzdHJpbmdcIj09dHlwZW9mIGUpe3ZhciBzPWUudG9Mb3dlckNhc2UoKTtEW3NdJiYoaT1zKSxuJiYoRFtzXT1uLGk9cyk7dmFyIHU9ZS5zcGxpdChcIi1cIik7aWYoIWkmJnUubGVuZ3RoPjEpcmV0dXJuIHQodVswXSl9ZWxzZXt2YXIgYT1lLm5hbWU7RFthXT1lLGk9YX1yZXR1cm4hciYmaSYmKGc9aSksaXx8IXImJmd9LE89ZnVuY3Rpb24odCxlKXtpZihTKHQpKXJldHVybiB0LmNsb25lKCk7dmFyIG49XCJvYmplY3RcIj09dHlwZW9mIGU/ZTp7fTtyZXR1cm4gbi5kYXRlPXQsbi5hcmdzPWFyZ3VtZW50cyxuZXcgXyhuKX0sYj12O2IubD13LGIuaT1TLGIudz1mdW5jdGlvbih0LGUpe3JldHVybiBPKHQse2xvY2FsZTplLiRMLHV0YzplLiR1LHg6ZS4keCwkb2Zmc2V0OmUuJG9mZnNldH0pfTt2YXIgXz1mdW5jdGlvbigpe2Z1bmN0aW9uIE0odCl7dGhpcy4kTD13KHQubG9jYWxlLG51bGwsITApLHRoaXMucGFyc2UodCksdGhpcy4keD10aGlzLiR4fHx0Lnh8fHt9LHRoaXNbcF09ITB9dmFyIG09TS5wcm90b3R5cGU7cmV0dXJuIG0ucGFyc2U9ZnVuY3Rpb24odCl7dGhpcy4kZD1mdW5jdGlvbih0KXt2YXIgZT10LmRhdGUsbj10LnV0YztpZihudWxsPT09ZSlyZXR1cm4gbmV3IERhdGUoTmFOKTtpZihiLnUoZSkpcmV0dXJuIG5ldyBEYXRlO2lmKGUgaW5zdGFuY2VvZiBEYXRlKXJldHVybiBuZXcgRGF0ZShlKTtpZihcInN0cmluZ1wiPT10eXBlb2YgZSYmIS9aJC9pLnRlc3QoZSkpe3ZhciByPWUubWF0Y2goJCk7aWYocil7dmFyIGk9clsyXS0xfHwwLHM9KHJbN118fFwiMFwiKS5zdWJzdHJpbmcoMCwzKTtyZXR1cm4gbj9uZXcgRGF0ZShEYXRlLlVUQyhyWzFdLGksclszXXx8MSxyWzRdfHwwLHJbNV18fDAscls2XXx8MCxzKSk6bmV3IERhdGUoclsxXSxpLHJbM118fDEscls0XXx8MCxyWzVdfHwwLHJbNl18fDAscyl9fXJldHVybiBuZXcgRGF0ZShlKX0odCksdGhpcy5pbml0KCl9LG0uaW5pdD1mdW5jdGlvbigpe3ZhciB0PXRoaXMuJGQ7dGhpcy4keT10LmdldEZ1bGxZZWFyKCksdGhpcy4kTT10LmdldE1vbnRoKCksdGhpcy4kRD10LmdldERhdGUoKSx0aGlzLiRXPXQuZ2V0RGF5KCksdGhpcy4kSD10LmdldEhvdXJzKCksdGhpcy4kbT10LmdldE1pbnV0ZXMoKSx0aGlzLiRzPXQuZ2V0U2Vjb25kcygpLHRoaXMuJG1zPXQuZ2V0TWlsbGlzZWNvbmRzKCl9LG0uJHV0aWxzPWZ1bmN0aW9uKCl7cmV0dXJuIGJ9LG0uaXNWYWxpZD1mdW5jdGlvbigpe3JldHVybiEodGhpcy4kZC50b1N0cmluZygpPT09bCl9LG0uaXNTYW1lPWZ1bmN0aW9uKHQsZSl7dmFyIG49Tyh0KTtyZXR1cm4gdGhpcy5zdGFydE9mKGUpPD1uJiZuPD10aGlzLmVuZE9mKGUpfSxtLmlzQWZ0ZXI9ZnVuY3Rpb24odCxlKXtyZXR1cm4gTyh0KTx0aGlzLnN0YXJ0T2YoZSl9LG0uaXNCZWZvcmU9ZnVuY3Rpb24odCxlKXtyZXR1cm4gdGhpcy5lbmRPZihlKTxPKHQpfSxtLiRnPWZ1bmN0aW9uKHQsZSxuKXtyZXR1cm4gYi51KHQpP3RoaXNbZV06dGhpcy5zZXQobix0KX0sbS51bml4PWZ1bmN0aW9uKCl7cmV0dXJuIE1hdGguZmxvb3IodGhpcy52YWx1ZU9mKCkvMWUzKX0sbS52YWx1ZU9mPWZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuJGQuZ2V0VGltZSgpfSxtLnN0YXJ0T2Y9ZnVuY3Rpb24odCxlKXt2YXIgbj10aGlzLHI9ISFiLnUoZSl8fGUsZj1iLnAodCksbD1mdW5jdGlvbih0LGUpe3ZhciBpPWIudyhuLiR1P0RhdGUuVVRDKG4uJHksZSx0KTpuZXcgRGF0ZShuLiR5LGUsdCksbik7cmV0dXJuIHI/aTppLmVuZE9mKGEpfSwkPWZ1bmN0aW9uKHQsZSl7cmV0dXJuIGIudyhuLnRvRGF0ZSgpW3RdLmFwcGx5KG4udG9EYXRlKFwic1wiKSwocj9bMCwwLDAsMF06WzIzLDU5LDU5LDk5OV0pLnNsaWNlKGUpKSxuKX0seT10aGlzLiRXLE09dGhpcy4kTSxtPXRoaXMuJEQsdj1cInNldFwiKyh0aGlzLiR1P1wiVVRDXCI6XCJcIik7c3dpdGNoKGYpe2Nhc2UgaDpyZXR1cm4gcj9sKDEsMCk6bCgzMSwxMSk7Y2FzZSBjOnJldHVybiByP2woMSxNKTpsKDAsTSsxKTtjYXNlIG86dmFyIGc9dGhpcy4kbG9jYWxlKCkud2Vla1N0YXJ0fHwwLEQ9KHk8Zz95Kzc6eSktZztyZXR1cm4gbChyP20tRDptKyg2LUQpLE0pO2Nhc2UgYTpjYXNlIGQ6cmV0dXJuICQoditcIkhvdXJzXCIsMCk7Y2FzZSB1OnJldHVybiAkKHYrXCJNaW51dGVzXCIsMSk7Y2FzZSBzOnJldHVybiAkKHYrXCJTZWNvbmRzXCIsMik7Y2FzZSBpOnJldHVybiAkKHYrXCJNaWxsaXNlY29uZHNcIiwzKTtkZWZhdWx0OnJldHVybiB0aGlzLmNsb25lKCl9fSxtLmVuZE9mPWZ1bmN0aW9uKHQpe3JldHVybiB0aGlzLnN0YXJ0T2YodCwhMSl9LG0uJHNldD1mdW5jdGlvbih0LGUpe3ZhciBuLG89Yi5wKHQpLGY9XCJzZXRcIisodGhpcy4kdT9cIlVUQ1wiOlwiXCIpLGw9KG49e30sblthXT1mK1wiRGF0ZVwiLG5bZF09ZitcIkRhdGVcIixuW2NdPWYrXCJNb250aFwiLG5baF09ZitcIkZ1bGxZZWFyXCIsblt1XT1mK1wiSG91cnNcIixuW3NdPWYrXCJNaW51dGVzXCIsbltpXT1mK1wiU2Vjb25kc1wiLG5bcl09ZitcIk1pbGxpc2Vjb25kc1wiLG4pW29dLCQ9bz09PWE/dGhpcy4kRCsoZS10aGlzLiRXKTplO2lmKG89PT1jfHxvPT09aCl7dmFyIHk9dGhpcy5jbG9uZSgpLnNldChkLDEpO3kuJGRbbF0oJCkseS5pbml0KCksdGhpcy4kZD15LnNldChkLE1hdGgubWluKHRoaXMuJEQseS5kYXlzSW5Nb250aCgpKSkuJGR9ZWxzZSBsJiZ0aGlzLiRkW2xdKCQpO3JldHVybiB0aGlzLmluaXQoKSx0aGlzfSxtLnNldD1mdW5jdGlvbih0LGUpe3JldHVybiB0aGlzLmNsb25lKCkuJHNldCh0LGUpfSxtLmdldD1mdW5jdGlvbih0KXtyZXR1cm4gdGhpc1tiLnAodCldKCl9LG0uYWRkPWZ1bmN0aW9uKHIsZil7dmFyIGQsbD10aGlzO3I9TnVtYmVyKHIpO3ZhciAkPWIucChmKSx5PWZ1bmN0aW9uKHQpe3ZhciBlPU8obCk7cmV0dXJuIGIudyhlLmRhdGUoZS5kYXRlKCkrTWF0aC5yb3VuZCh0KnIpKSxsKX07aWYoJD09PWMpcmV0dXJuIHRoaXMuc2V0KGMsdGhpcy4kTStyKTtpZigkPT09aClyZXR1cm4gdGhpcy5zZXQoaCx0aGlzLiR5K3IpO2lmKCQ9PT1hKXJldHVybiB5KDEpO2lmKCQ9PT1vKXJldHVybiB5KDcpO3ZhciBNPShkPXt9LGRbc109ZSxkW3VdPW4sZFtpXT10LGQpWyRdfHwxLG09dGhpcy4kZC5nZXRUaW1lKCkrcipNO3JldHVybiBiLncobSx0aGlzKX0sbS5zdWJ0cmFjdD1mdW5jdGlvbih0LGUpe3JldHVybiB0aGlzLmFkZCgtMSp0LGUpfSxtLmZvcm1hdD1mdW5jdGlvbih0KXt2YXIgZT10aGlzLG49dGhpcy4kbG9jYWxlKCk7aWYoIXRoaXMuaXNWYWxpZCgpKXJldHVybiBuLmludmFsaWREYXRlfHxsO3ZhciByPXR8fFwiWVlZWS1NTS1ERFRISDptbTpzc1pcIixpPWIueih0aGlzKSxzPXRoaXMuJEgsdT10aGlzLiRtLGE9dGhpcy4kTSxvPW4ud2Vla2RheXMsYz1uLm1vbnRocyxmPW4ubWVyaWRpZW0saD1mdW5jdGlvbih0LG4saSxzKXtyZXR1cm4gdCYmKHRbbl18fHQoZSxyKSl8fGlbbl0uc2xpY2UoMCxzKX0sZD1mdW5jdGlvbih0KXtyZXR1cm4gYi5zKHMlMTJ8fDEyLHQsXCIwXCIpfSwkPWZ8fGZ1bmN0aW9uKHQsZSxuKXt2YXIgcj10PDEyP1wiQU1cIjpcIlBNXCI7cmV0dXJuIG4/ci50b0xvd2VyQ2FzZSgpOnJ9O3JldHVybiByLnJlcGxhY2UoeSwoZnVuY3Rpb24odCxyKXtyZXR1cm4gcnx8ZnVuY3Rpb24odCl7c3dpdGNoKHQpe2Nhc2VcIllZXCI6cmV0dXJuIFN0cmluZyhlLiR5KS5zbGljZSgtMik7Y2FzZVwiWVlZWVwiOnJldHVybiBiLnMoZS4keSw0LFwiMFwiKTtjYXNlXCJNXCI6cmV0dXJuIGErMTtjYXNlXCJNTVwiOnJldHVybiBiLnMoYSsxLDIsXCIwXCIpO2Nhc2VcIk1NTVwiOnJldHVybiBoKG4ubW9udGhzU2hvcnQsYSxjLDMpO2Nhc2VcIk1NTU1cIjpyZXR1cm4gaChjLGEpO2Nhc2VcIkRcIjpyZXR1cm4gZS4kRDtjYXNlXCJERFwiOnJldHVybiBiLnMoZS4kRCwyLFwiMFwiKTtjYXNlXCJkXCI6cmV0dXJuIFN0cmluZyhlLiRXKTtjYXNlXCJkZFwiOnJldHVybiBoKG4ud2Vla2RheXNNaW4sZS4kVyxvLDIpO2Nhc2VcImRkZFwiOnJldHVybiBoKG4ud2Vla2RheXNTaG9ydCxlLiRXLG8sMyk7Y2FzZVwiZGRkZFwiOnJldHVybiBvW2UuJFddO2Nhc2VcIkhcIjpyZXR1cm4gU3RyaW5nKHMpO2Nhc2VcIkhIXCI6cmV0dXJuIGIucyhzLDIsXCIwXCIpO2Nhc2VcImhcIjpyZXR1cm4gZCgxKTtjYXNlXCJoaFwiOnJldHVybiBkKDIpO2Nhc2VcImFcIjpyZXR1cm4gJChzLHUsITApO2Nhc2VcIkFcIjpyZXR1cm4gJChzLHUsITEpO2Nhc2VcIm1cIjpyZXR1cm4gU3RyaW5nKHUpO2Nhc2VcIm1tXCI6cmV0dXJuIGIucyh1LDIsXCIwXCIpO2Nhc2VcInNcIjpyZXR1cm4gU3RyaW5nKGUuJHMpO2Nhc2VcInNzXCI6cmV0dXJuIGIucyhlLiRzLDIsXCIwXCIpO2Nhc2VcIlNTU1wiOnJldHVybiBiLnMoZS4kbXMsMyxcIjBcIik7Y2FzZVwiWlwiOnJldHVybiBpfXJldHVybiBudWxsfSh0KXx8aS5yZXBsYWNlKFwiOlwiLFwiXCIpfSkpfSxtLnV0Y09mZnNldD1mdW5jdGlvbigpe3JldHVybiAxNSotTWF0aC5yb3VuZCh0aGlzLiRkLmdldFRpbWV6b25lT2Zmc2V0KCkvMTUpfSxtLmRpZmY9ZnVuY3Rpb24ocixkLGwpe3ZhciAkLHk9dGhpcyxNPWIucChkKSxtPU8ociksdj0obS51dGNPZmZzZXQoKS10aGlzLnV0Y09mZnNldCgpKSplLGc9dGhpcy1tLEQ9ZnVuY3Rpb24oKXtyZXR1cm4gYi5tKHksbSl9O3N3aXRjaChNKXtjYXNlIGg6JD1EKCkvMTI7YnJlYWs7Y2FzZSBjOiQ9RCgpO2JyZWFrO2Nhc2UgZjokPUQoKS8zO2JyZWFrO2Nhc2UgbzokPShnLXYpLzYwNDhlNTticmVhaztjYXNlIGE6JD0oZy12KS84NjRlNTticmVhaztjYXNlIHU6JD1nL247YnJlYWs7Y2FzZSBzOiQ9Zy9lO2JyZWFrO2Nhc2UgaTokPWcvdDticmVhaztkZWZhdWx0OiQ9Z31yZXR1cm4gbD8kOmIuYSgkKX0sbS5kYXlzSW5Nb250aD1mdW5jdGlvbigpe3JldHVybiB0aGlzLmVuZE9mKGMpLiREfSxtLiRsb2NhbGU9ZnVuY3Rpb24oKXtyZXR1cm4gRFt0aGlzLiRMXX0sbS5sb2NhbGU9ZnVuY3Rpb24odCxlKXtpZighdClyZXR1cm4gdGhpcy4kTDt2YXIgbj10aGlzLmNsb25lKCkscj13KHQsZSwhMCk7cmV0dXJuIHImJihuLiRMPXIpLG59LG0uY2xvbmU9ZnVuY3Rpb24oKXtyZXR1cm4gYi53KHRoaXMuJGQsdGhpcyl9LG0udG9EYXRlPWZ1bmN0aW9uKCl7cmV0dXJuIG5ldyBEYXRlKHRoaXMudmFsdWVPZigpKX0sbS50b0pTT049ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5pc1ZhbGlkKCk/dGhpcy50b0lTT1N0cmluZygpOm51bGx9LG0udG9JU09TdHJpbmc9ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy4kZC50b0lTT1N0cmluZygpfSxtLnRvU3RyaW5nPWZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuJGQudG9VVENTdHJpbmcoKX0sTX0oKSxrPV8ucHJvdG90eXBlO3JldHVybiBPLnByb3RvdHlwZT1rLFtbXCIkbXNcIixyXSxbXCIkc1wiLGldLFtcIiRtXCIsc10sW1wiJEhcIix1XSxbXCIkV1wiLGFdLFtcIiRNXCIsY10sW1wiJHlcIixoXSxbXCIkRFwiLGRdXS5mb3JFYWNoKChmdW5jdGlvbih0KXtrW3RbMV1dPWZ1bmN0aW9uKGUpe3JldHVybiB0aGlzLiRnKGUsdFswXSx0WzFdKX19KSksTy5leHRlbmQ9ZnVuY3Rpb24odCxlKXtyZXR1cm4gdC4kaXx8KHQoZSxfLE8pLHQuJGk9ITApLE99LE8ubG9jYWxlPXcsTy5pc0RheWpzPVMsTy51bml4PWZ1bmN0aW9uKHQpe3JldHVybiBPKDFlMyp0KX0sTy5lbj1EW2ddLE8uTHM9RCxPLnA9e30sT30pKTsiLCAiIWZ1bmN0aW9uKHQsaSl7XCJvYmplY3RcIj09dHlwZW9mIGV4cG9ydHMmJlwidW5kZWZpbmVkXCIhPXR5cGVvZiBtb2R1bGU/bW9kdWxlLmV4cG9ydHM9aSgpOlwiZnVuY3Rpb25cIj09dHlwZW9mIGRlZmluZSYmZGVmaW5lLmFtZD9kZWZpbmUoaSk6KHQ9XCJ1bmRlZmluZWRcIiE9dHlwZW9mIGdsb2JhbFRoaXM/Z2xvYmFsVGhpczp0fHxzZWxmKS5kYXlqc19wbHVnaW5fdXRjPWkoKX0odGhpcywoZnVuY3Rpb24oKXtcInVzZSBzdHJpY3RcIjt2YXIgdD1cIm1pbnV0ZVwiLGk9L1srLV1cXGRcXGQoPzo6P1xcZFxcZCk/L2csZT0vKFsrLV18XFxkXFxkKS9nO3JldHVybiBmdW5jdGlvbihzLGYsbil7dmFyIHU9Zi5wcm90b3R5cGU7bi51dGM9ZnVuY3Rpb24odCl7dmFyIGk9e2RhdGU6dCx1dGM6ITAsYXJnczphcmd1bWVudHN9O3JldHVybiBuZXcgZihpKX0sdS51dGM9ZnVuY3Rpb24oaSl7dmFyIGU9bih0aGlzLnRvRGF0ZSgpLHtsb2NhbGU6dGhpcy4kTCx1dGM6ITB9KTtyZXR1cm4gaT9lLmFkZCh0aGlzLnV0Y09mZnNldCgpLHQpOmV9LHUubG9jYWw9ZnVuY3Rpb24oKXtyZXR1cm4gbih0aGlzLnRvRGF0ZSgpLHtsb2NhbGU6dGhpcy4kTCx1dGM6ITF9KX07dmFyIHI9dS5wYXJzZTt1LnBhcnNlPWZ1bmN0aW9uKHQpe3QudXRjJiYodGhpcy4kdT0hMCksdGhpcy4kdXRpbHMoKS51KHQuJG9mZnNldCl8fCh0aGlzLiRvZmZzZXQ9dC4kb2Zmc2V0KSxyLmNhbGwodGhpcyx0KX07dmFyIG89dS5pbml0O3UuaW5pdD1mdW5jdGlvbigpe2lmKHRoaXMuJHUpe3ZhciB0PXRoaXMuJGQ7dGhpcy4keT10LmdldFVUQ0Z1bGxZZWFyKCksdGhpcy4kTT10LmdldFVUQ01vbnRoKCksdGhpcy4kRD10LmdldFVUQ0RhdGUoKSx0aGlzLiRXPXQuZ2V0VVRDRGF5KCksdGhpcy4kSD10LmdldFVUQ0hvdXJzKCksdGhpcy4kbT10LmdldFVUQ01pbnV0ZXMoKSx0aGlzLiRzPXQuZ2V0VVRDU2Vjb25kcygpLHRoaXMuJG1zPXQuZ2V0VVRDTWlsbGlzZWNvbmRzKCl9ZWxzZSBvLmNhbGwodGhpcyl9O3ZhciBhPXUudXRjT2Zmc2V0O3UudXRjT2Zmc2V0PWZ1bmN0aW9uKHMsZil7dmFyIG49dGhpcy4kdXRpbHMoKS51O2lmKG4ocykpcmV0dXJuIHRoaXMuJHU/MDpuKHRoaXMuJG9mZnNldCk/YS5jYWxsKHRoaXMpOnRoaXMuJG9mZnNldDtpZihcInN0cmluZ1wiPT10eXBlb2YgcyYmKHM9ZnVuY3Rpb24odCl7dm9pZCAwPT09dCYmKHQ9XCJcIik7dmFyIHM9dC5tYXRjaChpKTtpZighcylyZXR1cm4gbnVsbDt2YXIgZj0oXCJcIitzWzBdKS5tYXRjaChlKXx8W1wiLVwiLDAsMF0sbj1mWzBdLHU9NjAqK2ZbMV0rICtmWzJdO3JldHVybiAwPT09dT8wOlwiK1wiPT09bj91Oi11fShzKSxudWxsPT09cykpcmV0dXJuIHRoaXM7dmFyIHU9TWF0aC5hYnMocyk8PTE2PzYwKnM6cztpZigwPT09dSlyZXR1cm4gdGhpcy51dGMoZik7dmFyIHI9dGhpcy5jbG9uZSgpO2lmKGYpcmV0dXJuIHIuJG9mZnNldD11LHIuJHU9ITEscjt2YXIgbz10aGlzLiR1P3RoaXMudG9EYXRlKCkuZ2V0VGltZXpvbmVPZmZzZXQoKTotMSp0aGlzLnV0Y09mZnNldCgpO3JldHVybihyPXRoaXMubG9jYWwoKS5hZGQodStvLHQpKS4kb2Zmc2V0PXUsci4keC4kbG9jYWxPZmZzZXQ9byxyfTt2YXIgaD11LmZvcm1hdDt1LmZvcm1hdD1mdW5jdGlvbih0KXt2YXIgaT10fHwodGhpcy4kdT9cIllZWVktTU0tRERUSEg6bW06c3NbWl1cIjpcIlwiKTtyZXR1cm4gaC5jYWxsKHRoaXMsaSl9LHUudmFsdWVPZj1mdW5jdGlvbigpe3ZhciB0PXRoaXMuJHV0aWxzKCkudSh0aGlzLiRvZmZzZXQpPzA6dGhpcy4kb2Zmc2V0Kyh0aGlzLiR4LiRsb2NhbE9mZnNldHx8dGhpcy4kZC5nZXRUaW1lem9uZU9mZnNldCgpKTtyZXR1cm4gdGhpcy4kZC52YWx1ZU9mKCktNmU0KnR9LHUuaXNVVEM9ZnVuY3Rpb24oKXtyZXR1cm4hIXRoaXMuJHV9LHUudG9JU09TdHJpbmc9ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy50b0RhdGUoKS50b0lTT1N0cmluZygpfSx1LnRvU3RyaW5nPWZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMudG9EYXRlKCkudG9VVENTdHJpbmcoKX07dmFyIGw9dS50b0RhdGU7dS50b0RhdGU9ZnVuY3Rpb24odCl7cmV0dXJuXCJzXCI9PT10JiZ0aGlzLiRvZmZzZXQ/bih0aGlzLmZvcm1hdChcIllZWVktTU0tREQgSEg6bW06c3M6U1NTXCIpKS50b0RhdGUoKTpsLmNhbGwodGhpcyl9O3ZhciBjPXUuZGlmZjt1LmRpZmY9ZnVuY3Rpb24odCxpLGUpe2lmKHQmJnRoaXMuJHU9PT10LiR1KXJldHVybiBjLmNhbGwodGhpcyx0LGksZSk7dmFyIHM9dGhpcy5sb2NhbCgpLGY9bih0KS5sb2NhbCgpO3JldHVybiBjLmNhbGwocyxmLGksZSl9fX0pKTsiLCAiIWZ1bmN0aW9uKHQsZSl7XCJvYmplY3RcIj09dHlwZW9mIGV4cG9ydHMmJlwidW5kZWZpbmVkXCIhPXR5cGVvZiBtb2R1bGU/bW9kdWxlLmV4cG9ydHM9ZSgpOlwiZnVuY3Rpb25cIj09dHlwZW9mIGRlZmluZSYmZGVmaW5lLmFtZD9kZWZpbmUoZSk6KHQ9XCJ1bmRlZmluZWRcIiE9dHlwZW9mIGdsb2JhbFRoaXM/Z2xvYmFsVGhpczp0fHxzZWxmKS5kYXlqc19wbHVnaW5fdGltZXpvbmU9ZSgpfSh0aGlzLChmdW5jdGlvbigpe1widXNlIHN0cmljdFwiO3ZhciB0PXt5ZWFyOjAsbW9udGg6MSxkYXk6Mixob3VyOjMsbWludXRlOjQsc2Vjb25kOjV9LGU9e307cmV0dXJuIGZ1bmN0aW9uKG4saSxvKXt2YXIgcixhPWZ1bmN0aW9uKHQsbixpKXt2b2lkIDA9PT1pJiYoaT17fSk7dmFyIG89bmV3IERhdGUodCkscj1mdW5jdGlvbih0LG4pe3ZvaWQgMD09PW4mJihuPXt9KTt2YXIgaT1uLnRpbWVab25lTmFtZXx8XCJzaG9ydFwiLG89dCtcInxcIitpLHI9ZVtvXTtyZXR1cm4gcnx8KHI9bmV3IEludGwuRGF0ZVRpbWVGb3JtYXQoXCJlbi1VU1wiLHtob3VyMTI6ITEsdGltZVpvbmU6dCx5ZWFyOlwibnVtZXJpY1wiLG1vbnRoOlwiMi1kaWdpdFwiLGRheTpcIjItZGlnaXRcIixob3VyOlwiMi1kaWdpdFwiLG1pbnV0ZTpcIjItZGlnaXRcIixzZWNvbmQ6XCIyLWRpZ2l0XCIsdGltZVpvbmVOYW1lOml9KSxlW29dPXIpLHJ9KG4saSk7cmV0dXJuIHIuZm9ybWF0VG9QYXJ0cyhvKX0sdT1mdW5jdGlvbihlLG4pe2Zvcih2YXIgaT1hKGUsbikscj1bXSx1PTA7dTxpLmxlbmd0aDt1Kz0xKXt2YXIgZj1pW3VdLHM9Zi50eXBlLG09Zi52YWx1ZSxjPXRbc107Yz49MCYmKHJbY109cGFyc2VJbnQobSwxMCkpfXZhciBkPXJbM10sbD0yND09PWQ/MDpkLGg9clswXStcIi1cIityWzFdK1wiLVwiK3JbMl0rXCIgXCIrbCtcIjpcIityWzRdK1wiOlwiK3JbNV0rXCI6MDAwXCIsdj0rZTtyZXR1cm4oby51dGMoaCkudmFsdWVPZigpLSh2LT12JTFlMykpLzZlNH0sZj1pLnByb3RvdHlwZTtmLnR6PWZ1bmN0aW9uKHQsZSl7dm9pZCAwPT09dCYmKHQ9cik7dmFyIG4saT10aGlzLnV0Y09mZnNldCgpLGE9dGhpcy50b0RhdGUoKSx1PWEudG9Mb2NhbGVTdHJpbmcoXCJlbi1VU1wiLHt0aW1lWm9uZTp0fSksZj1NYXRoLnJvdW5kKChhLW5ldyBEYXRlKHUpKS8xZTMvNjApLHM9MTUqLU1hdGgucm91bmQoYS5nZXRUaW1lem9uZU9mZnNldCgpLzE1KS1mO2lmKCFOdW1iZXIocykpbj10aGlzLnV0Y09mZnNldCgwLGUpO2Vsc2UgaWYobj1vKHUse2xvY2FsZTp0aGlzLiRMfSkuJHNldChcIm1pbGxpc2Vjb25kXCIsdGhpcy4kbXMpLnV0Y09mZnNldChzLCEwKSxlKXt2YXIgbT1uLnV0Y09mZnNldCgpO249bi5hZGQoaS1tLFwibWludXRlXCIpfXJldHVybiBuLiR4LiR0aW1lem9uZT10LG59LGYub2Zmc2V0TmFtZT1mdW5jdGlvbih0KXt2YXIgZT10aGlzLiR4LiR0aW1lem9uZXx8by50ei5ndWVzcygpLG49YSh0aGlzLnZhbHVlT2YoKSxlLHt0aW1lWm9uZU5hbWU6dH0pLmZpbmQoKGZ1bmN0aW9uKHQpe3JldHVyblwidGltZXpvbmVuYW1lXCI9PT10LnR5cGUudG9Mb3dlckNhc2UoKX0pKTtyZXR1cm4gbiYmbi52YWx1ZX07dmFyIHM9Zi5zdGFydE9mO2Yuc3RhcnRPZj1mdW5jdGlvbih0LGUpe2lmKCF0aGlzLiR4fHwhdGhpcy4keC4kdGltZXpvbmUpcmV0dXJuIHMuY2FsbCh0aGlzLHQsZSk7dmFyIG49byh0aGlzLmZvcm1hdChcIllZWVktTU0tREQgSEg6bW06c3M6U1NTXCIpLHtsb2NhbGU6dGhpcy4kTH0pO3JldHVybiBzLmNhbGwobix0LGUpLnR6KHRoaXMuJHguJHRpbWV6b25lLCEwKX0sby50ej1mdW5jdGlvbih0LGUsbil7dmFyIGk9biYmZSxhPW58fGV8fHIsZj11KCtvKCksYSk7aWYoXCJzdHJpbmdcIiE9dHlwZW9mIHQpcmV0dXJuIG8odCkudHooYSk7dmFyIHM9ZnVuY3Rpb24odCxlLG4pe3ZhciBpPXQtNjAqZSoxZTMsbz11KGksbik7aWYoZT09PW8pcmV0dXJuW2ksZV07dmFyIHI9dShpLT02MCooby1lKSoxZTMsbik7cmV0dXJuIG89PT1yP1tpLG9dOlt0LTYwKk1hdGgubWluKG8scikqMWUzLE1hdGgubWF4KG8scildfShvLnV0Yyh0LGkpLnZhbHVlT2YoKSxmLGEpLG09c1swXSxjPXNbMV0sZD1vKG0pLnV0Y09mZnNldChjKTtyZXR1cm4gZC4keC4kdGltZXpvbmU9YSxkfSxvLnR6Lmd1ZXNzPWZ1bmN0aW9uKCl7cmV0dXJuIEludGwuRGF0ZVRpbWVGb3JtYXQoKS5yZXNvbHZlZE9wdGlvbnMoKS50aW1lWm9uZX0sby50ei5zZXREZWZhdWx0PWZ1bmN0aW9uKHQpe3I9dH19fSkpOyIsICIhZnVuY3Rpb24oZSx0KXtcIm9iamVjdFwiPT10eXBlb2YgZXhwb3J0cyYmXCJ1bmRlZmluZWRcIiE9dHlwZW9mIG1vZHVsZT9tb2R1bGUuZXhwb3J0cz10KCk6XCJmdW5jdGlvblwiPT10eXBlb2YgZGVmaW5lJiZkZWZpbmUuYW1kP2RlZmluZSh0KTooZT1cInVuZGVmaW5lZFwiIT10eXBlb2YgZ2xvYmFsVGhpcz9nbG9iYWxUaGlzOmV8fHNlbGYpLmRheWpzX3BsdWdpbl9pc29XZWVrPXQoKX0odGhpcywoZnVuY3Rpb24oKXtcInVzZSBzdHJpY3RcIjt2YXIgZT1cImRheVwiO3JldHVybiBmdW5jdGlvbih0LGkscyl7dmFyIGE9ZnVuY3Rpb24odCl7cmV0dXJuIHQuYWRkKDQtdC5pc29XZWVrZGF5KCksZSl9LGQ9aS5wcm90b3R5cGU7ZC5pc29XZWVrWWVhcj1mdW5jdGlvbigpe3JldHVybiBhKHRoaXMpLnllYXIoKX0sZC5pc29XZWVrPWZ1bmN0aW9uKHQpe2lmKCF0aGlzLiR1dGlscygpLnUodCkpcmV0dXJuIHRoaXMuYWRkKDcqKHQtdGhpcy5pc29XZWVrKCkpLGUpO3ZhciBpLGQsbixvLHI9YSh0aGlzKSx1PShpPXRoaXMuaXNvV2Vla1llYXIoKSxkPXRoaXMuJHUsbj0oZD9zLnV0YzpzKSgpLnllYXIoaSkuc3RhcnRPZihcInllYXJcIiksbz00LW4uaXNvV2Vla2RheSgpLG4uaXNvV2Vla2RheSgpPjQmJihvKz03KSxuLmFkZChvLGUpKTtyZXR1cm4gci5kaWZmKHUsXCJ3ZWVrXCIpKzF9LGQuaXNvV2Vla2RheT1mdW5jdGlvbihlKXtyZXR1cm4gdGhpcy4kdXRpbHMoKS51KGUpP3RoaXMuZGF5KCl8fDc6dGhpcy5kYXkodGhpcy5kYXkoKSU3P2U6ZS03KX07dmFyIG49ZC5zdGFydE9mO2Quc3RhcnRPZj1mdW5jdGlvbihlLHQpe3ZhciBpPXRoaXMuJHV0aWxzKCkscz0hIWkudSh0KXx8dDtyZXR1cm5cImlzb3dlZWtcIj09PWkucChlKT9zP3RoaXMuZGF0ZSh0aGlzLmRhdGUoKS0odGhpcy5pc29XZWVrZGF5KCktMSkpLnN0YXJ0T2YoXCJkYXlcIik6dGhpcy5kYXRlKHRoaXMuZGF0ZSgpLTEtKHRoaXMuaXNvV2Vla2RheSgpLTEpKzcpLmVuZE9mKFwiZGF5XCIpOm4uYmluZCh0aGlzKShlLHQpfX19KSk7IiwgImxldCB0b2tlbkNvdW50ZXIgPSAwO1xuLyoqXG4gKiBDcmVhdGVzIGEgbmV3IHVuaXF1ZSB0b2tlbiBmb3IgZGVwZW5kZW5jeSBpbmplY3Rpb24uXG4gKlxuICogQHBhcmFtIGRlc2NyaXB0aW9uIE9wdGlvbmFsIGRlc2NyaXB0aW9uIGZvciBkZWJ1Z2dpbmcgcHVycG9zZXNcbiAqIEByZXR1cm5zIEEgdW5pcXVlIHRva2VuIHRoYXQgY2FuIGJlIHVzZWQgYXMgYSBNYXAga2V5XG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHRzXG4gKiBpbnRlcmZhY2UgSUxvZ2dlciB7IGxvZyhtc2c6IHN0cmluZyk6IHZvaWQgfVxuICogY29uc3QgTG9nZ2VyVG9rZW4gPSBUb2tlbjxJTG9nZ2VyPignTG9nZ2VyJylcbiAqIGBgYFxuICovXG5leHBvcnQgZnVuY3Rpb24gVG9rZW4oZGVzY3JpcHRpb24pIHtcbiAgICBjb25zdCBpZCA9ICsrdG9rZW5Db3VudGVyO1xuICAgIGNvbnN0IHN5bSA9IFN5bWJvbChkZXNjcmlwdGlvbiA/IGBUb2tlbigke2Rlc2NyaXB0aW9ufSlgIDogYFRva2VuIyR7aWR9YCk7XG4gICAgY29uc3QgdG9rZW4gPSB7XG4gICAgICAgIHN5bWJvbDogc3ltLFxuICAgICAgICBkZXNjcmlwdGlvbixcbiAgICAgICAgdG9TdHJpbmcoKSB7XG4gICAgICAgICAgICByZXR1cm4gZGVzY3JpcHRpb25cbiAgICAgICAgICAgICAgICA/IGBUb2tlbjwke2Rlc2NyaXB0aW9ufT5gXG4gICAgICAgICAgICAgICAgOiBgVG9rZW48IyR7aWR9PmA7XG4gICAgICAgIH1cbiAgICB9O1xuICAgIHJldHVybiB0b2tlbjtcbn1cbi8qKlxuICogQ3JlYXRlcyBhIG5ldyB1bmlxdWUgdG9rZW4gd2l0aG91dCBhIHN0cmluZyBsaXRlcmFsLlxuICogUHJlZmVycmVkIGZvciBBdXRvZmFjLXN0eWxlIERJIHRvIGF2b2lkIHN0cmluZyBsaXRlcmFscy5cbiAqXG4gKiBAcmV0dXJucyBBIHVuaXF1ZSB0b2tlbiB0aGF0IGNhbiBiZSB1c2VkIGFzIGEgTWFwIGtleVxuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0c1xuICogaW50ZXJmYWNlIElMb2dnZXIgeyBsb2cobXNnOiBzdHJpbmcpOiB2b2lkIH1cbiAqIGNvbnN0IExvZ2dlclRva2VuID0gdG9rZW48SUxvZ2dlcj4oKVxuICogYGBgXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB0b2tlbigpIHtcbiAgICByZXR1cm4gVG9rZW4oKTtcbn1cbiIsICIvKipcbiAqIEVycm9yIGNsYXNzZXMgZm9yIE5vdmFESSBjb250YWluZXJcbiAqL1xuZXhwb3J0IGNsYXNzIENvbnRhaW5lckVycm9yIGV4dGVuZHMgRXJyb3Ige1xuICAgIGNvbnN0cnVjdG9yKG1lc3NhZ2UpIHtcbiAgICAgICAgc3VwZXIobWVzc2FnZSk7XG4gICAgICAgIHRoaXMubmFtZSA9ICdDb250YWluZXJFcnJvcic7XG4gICAgfVxufVxuZXhwb3J0IGNsYXNzIEJpbmRpbmdOb3RGb3VuZEVycm9yIGV4dGVuZHMgQ29udGFpbmVyRXJyb3Ige1xuICAgIGNvbnN0cnVjdG9yKHRva2VuRGVzY3JpcHRpb24sIHBhdGggPSBbXSkge1xuICAgICAgICBjb25zdCBwYXRoU3RyID0gcGF0aC5sZW5ndGggPiAwID8gYFxcbiAgRGVwZW5kZW5jeSBwYXRoOiAke3BhdGguam9pbignIC0+ICcpfWAgOiAnJztcbiAgICAgICAgc3VwZXIoYFRva2VuIFwiJHt0b2tlbkRlc2NyaXB0aW9ufVwiIGlzIG5vdCBib3VuZCBvciByZWdpc3RlcmVkIGluIHRoZSBjb250YWluZXIuJHtwYXRoU3RyfWApO1xuICAgICAgICB0aGlzLm5hbWUgPSAnQmluZGluZ05vdEZvdW5kRXJyb3InO1xuICAgIH1cbn1cbmV4cG9ydCBjbGFzcyBDaXJjdWxhckRlcGVuZGVuY3lFcnJvciBleHRlbmRzIENvbnRhaW5lckVycm9yIHtcbiAgICBjb25zdHJ1Y3RvcihwYXRoKSB7XG4gICAgICAgIHN1cGVyKGBDaXJjdWxhciBkZXBlbmRlbmN5IGRldGVjdGVkOiAke3BhdGguam9pbignIC0+ICcpfWApO1xuICAgICAgICB0aGlzLm5hbWUgPSAnQ2lyY3VsYXJEZXBlbmRlbmN5RXJyb3InO1xuICAgIH1cbn1cbiIsICIvKipcbiAqIEF1dG9XaXJlIC0gQXV0b21hdGljIGRlcGVuZGVuY3kgaW5qZWN0aW9uIGZvciBOb3ZhRElcbiAqIFN1cHBvcnRzIHR3byBzdHJhdGVnaWVzOiBtYXBSZXNvbHZlcnMgKHRyYW5zZm9ybWVyLWdlbmVyYXRlZCkgYW5kIG1hcCAobWFudWFsIG92ZXJyaWRlKVxuICovXG4vKipcbiAqIFBlcmZvcm1hbmNlOiBDYWNoZSBleHRyYWN0ZWQgcGFyYW1ldGVyIG5hbWVzIHRvIGF2b2lkIHJlcGVhdGVkIHJlZ2V4IHBhcnNpbmdcbiAqIFdlYWtNYXAgYWxsb3dzIGdhcmJhZ2UgY29sbGVjdGlvbiB3aGVuIGNvbnN0cnVjdG9yIGlzIG5vIGxvbmdlciByZWZlcmVuY2VkXG4gKi9cbmNvbnN0IHBhcmFtTmFtZUNhY2hlID0gbmV3IFdlYWtNYXAoKTtcbi8qKlxuICogRXh0cmFjdCBwYXJhbWV0ZXIgbmFtZXMgZnJvbSBhIGNvbnN0cnVjdG9yIGZ1bmN0aW9uXG4gKiBVc2VzIHJlZ2V4IHRvIHBhcnNlIHRoZSB0b1N0cmluZygpIHJlcHJlc2VudGF0aW9uXG4gKiBQZXJmb3JtYW5jZSBvcHRpbWl6ZWQ6IFJlc3VsdHMgYXJlIGNhY2hlZCBwZXIgY29uc3RydWN0b3JcbiAqXG4gKiBOb3RlOiBPbmx5IHVzZWQgYnkgcmVzb2x2ZUJ5TWFwKCkgZm9yIG1hbnVhbCBtYXAgc3RyYXRlZ3lcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGV4dHJhY3RQYXJhbWV0ZXJOYW1lcyhjb25zdHJ1Y3Rvcikge1xuICAgIC8vIENoZWNrIGNhY2hlIGZpcnN0IC0gYXZvaWRzIGV4cGVuc2l2ZSByZWdleCBwYXJzaW5nXG4gICAgY29uc3QgY2FjaGVkID0gcGFyYW1OYW1lQ2FjaGUuZ2V0KGNvbnN0cnVjdG9yKTtcbiAgICBpZiAoY2FjaGVkKSB7XG4gICAgICAgIHJldHVybiBjYWNoZWQ7XG4gICAgfVxuICAgIC8vIEV4dHJhY3QgcGFyYW1ldGVyIG5hbWVzIChleHBlbnNpdmUgb3BlcmF0aW9uKVxuICAgIGNvbnN0IGZuU3RyID0gY29uc3RydWN0b3IudG9TdHJpbmcoKTtcbiAgICAvLyBNYXRjaCBjb25zdHJ1Y3RvciguLi5hcmdzKSBvciBjbGFzcyB7IGNvbnN0cnVjdG9yKC4uLmFyZ3MpIH1cbiAgICBjb25zdCBtYXRjaCA9IGZuU3RyLm1hdGNoKC9jb25zdHJ1Y3RvclxccypcXCgoW14pXSopXFwpLykgfHwgZm5TdHIubWF0Y2goL15bXihdKlxcKChbXildKilcXCkvKTtcbiAgICBpZiAoIW1hdGNoIHx8ICFtYXRjaFsxXSkge1xuICAgICAgICByZXR1cm4gW107XG4gICAgfVxuICAgIGNvbnN0IHBhcmFtcyA9IG1hdGNoWzFdXG4gICAgICAgIC5zcGxpdCgnLCcpXG4gICAgICAgIC5tYXAocGFyYW0gPT4gcGFyYW0udHJpbSgpKVxuICAgICAgICAuZmlsdGVyKHBhcmFtID0+IHBhcmFtLmxlbmd0aCA+IDApXG4gICAgICAgIC5tYXAocGFyYW0gPT4ge1xuICAgICAgICAvLyBSZW1vdmUgZGVmYXVsdCB2YWx1ZXMsIHR5cGUgYW5ub3RhdGlvbnMsIGFuZCBleHRyYWN0IGp1c3QgdGhlIG5hbWVcbiAgICAgICAgbGV0IG5hbWUgPSBwYXJhbS5zcGxpdCgvWzo9XS8pWzBdLnRyaW0oKTtcbiAgICAgICAgLy8gUmVtb3ZlIFR5cGVTY3JpcHQgbW9kaWZpZXJzIChwdWJsaWMsIHByaXZhdGUsIHByb3RlY3RlZCwgcmVhZG9ubHkpXG4gICAgICAgIC8vIENhbiBhcHBlYXIgbXVsdGlwbGUgdGltZXMsIGUuZy4sIFwicHVibGljIHJlYWRvbmx5IHNlcnZpY2VcIlxuICAgICAgICBuYW1lID0gbmFtZS5yZXBsYWNlKC9eKChwdWJsaWN8cHJpdmF0ZXxwcm90ZWN0ZWR8cmVhZG9ubHkpXFxzKykrLywgJycpO1xuICAgICAgICAvLyBIYW5kbGUgZGVzdHJ1Y3R1cmluZyAtIHNraXAgZm9yIG5vd1xuICAgICAgICBpZiAobmFtZS5pbmNsdWRlcygneycpIHx8IG5hbWUuaW5jbHVkZXMoJ1snKSkge1xuICAgICAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIG5hbWU7XG4gICAgfSlcbiAgICAgICAgLmZpbHRlcigobmFtZSkgPT4gbmFtZSAhPT0gbnVsbCk7XG4gICAgLy8gQ2FjaGUgcmVzdWx0IGZvciBmdXR1cmUgY2FsbHNcbiAgICBwYXJhbU5hbWVDYWNoZS5zZXQoY29uc3RydWN0b3IsIHBhcmFtcyk7XG4gICAgcmV0dXJuIHBhcmFtcztcbn1cbi8qKlxuICogUmVzb2x2ZSBkZXBlbmRlbmNpZXMgdXNpbmcgbWFwIHN0cmF0ZWd5XG4gKiBVc2VzIGV4cGxpY2l0IG1hcHBpbmcgZnJvbSBwYXJhbWV0ZXIgbmFtZXMgdG8gcmVzb2x2ZXJzXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZXNvbHZlQnlNYXAoY29uc3RydWN0b3IsIGNvbnRhaW5lciwgb3B0aW9ucykge1xuICAgIGlmICghb3B0aW9ucy5tYXApIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdBdXRvV2lyZSBtYXAgc3RyYXRlZ3kgcmVxdWlyZXMgb3B0aW9ucy5tYXAgdG8gYmUgZGVmaW5lZCcpO1xuICAgIH1cbiAgICBjb25zdCBwYXJhbU5hbWVzID0gZXh0cmFjdFBhcmFtZXRlck5hbWVzKGNvbnN0cnVjdG9yKTtcbiAgICBjb25zdCByZXNvbHZlZERlcHMgPSBbXTtcbiAgICBmb3IgKGNvbnN0IHBhcmFtTmFtZSBvZiBwYXJhbU5hbWVzKSB7XG4gICAgICAgIGNvbnN0IHJlc29sdmVyID0gb3B0aW9ucy5tYXBbcGFyYW1OYW1lXTtcbiAgICAgICAgaWYgKHJlc29sdmVyID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIGlmIChvcHRpb25zLnN0cmljdCkge1xuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgQ2Fubm90IHJlc29sdmUgcGFyYW1ldGVyIFwiJHtwYXJhbU5hbWV9XCIgb24gJHtjb25zdHJ1Y3Rvci5uYW1lfS4gYCArXG4gICAgICAgICAgICAgICAgICAgIGBOb3QgZm91bmQgaW4gYXV0b3dpcmUgbWFwLiBgICtcbiAgICAgICAgICAgICAgICAgICAgYEFkZCBpdCB0byB0aGUgbWFwOiAuYXV0b1dpcmUoeyBtYXA6IHsgJHtwYXJhbU5hbWV9OiAuLi4gfSB9KWApO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgLy8gU2lsZW50bHkgcHVzaCB1bmRlZmluZWQgZm9yIG1pc3NpbmcgcGFyYW1ldGVyc1xuICAgICAgICAgICAgICAgIC8vIFRoaXMgaXMgZXhwZWN0ZWQ6IHRyYW5zZm9ybWVyIGZpbHRlcnMgb3V0IHByaW1pdGl2ZSB0eXBlcyBhdCBjb21waWxlLXRpbWUsXG4gICAgICAgICAgICAgICAgLy8gc28gbWlzc2luZyBwYXJhbXMgYXJlIHR5cGljYWxseSBwcmltaXRpdmVzIHRoYXQgZG9uJ3QgbmVlZCBESSByZXNvbHV0aW9uXG4gICAgICAgICAgICAgICAgcmVzb2x2ZWREZXBzLnB1c2godW5kZWZpbmVkKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG4gICAgICAgIC8vIFJlc29sdmVyIGNhbiBiZSBhIGZ1bmN0aW9uIG9yIGEgVG9rZW5cbiAgICAgICAgaWYgKHR5cGVvZiByZXNvbHZlciA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICAgICAgcmVzb2x2ZWREZXBzLnB1c2gocmVzb2x2ZXIoY29udGFpbmVyKSk7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAvLyBBc3N1bWUgaXQncyBhIFRva2VuXG4gICAgICAgICAgICByZXNvbHZlZERlcHMucHVzaChjb250YWluZXIucmVzb2x2ZShyZXNvbHZlcikpO1xuICAgICAgICB9XG4gICAgfVxuICAgIHJldHVybiByZXNvbHZlZERlcHM7XG59XG4vKipcbiAqIFJlc29sdmUgZGVwZW5kZW5jaWVzIHVzaW5nIG1hcFJlc29sdmVycyBhcnJheSBzdHJhdGVneVxuICogT1BUSU1BTCBQRVJGT1JNQU5DRTogTygxKSBhcnJheSBhY2Nlc3MgcGVyIHBhcmFtZXRlclxuICogTWluaWZpY2F0aW9uLXNhZmU6IFVzZXMgcG9zaXRpb24tYmFzZWQgYXJyYXlcbiAqIFJlZmFjdG9yaW5nLWZyaWVuZGx5OiBUcmFuc2Zvcm1lciByZWdlbmVyYXRlcyBhcnJheSBvbiByZWNvbXBpbGVcbiAqXG4gKiBSZXF1aXJlcyBidWlsZC10aW1lIHRyYW5zZm9ybWVyIHRvIGdlbmVyYXRlIG1hcFJlc29sdmVycyBhcnJheVxuICovXG5leHBvcnQgZnVuY3Rpb24gcmVzb2x2ZUJ5TWFwUmVzb2x2ZXJzKF9jb25zdHJ1Y3RvciwgY29udGFpbmVyLCBvcHRpb25zKSB7XG4gICAgaWYgKCFvcHRpb25zLm1hcFJlc29sdmVycyB8fCBvcHRpb25zLm1hcFJlc29sdmVycy5sZW5ndGggPT09IDApIHtcbiAgICAgICAgcmV0dXJuIFtdO1xuICAgIH1cbiAgICBjb25zdCByZXNvbHZlZERlcHMgPSBbXTtcbiAgICAvLyBTaW1wbGUgTygxKSBhcnJheSBhY2Nlc3MgLSB1bHRyYSBmYXN0IVxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgb3B0aW9ucy5tYXBSZXNvbHZlcnMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgY29uc3QgcmVzb2x2ZXIgPSBvcHRpb25zLm1hcFJlc29sdmVyc1tpXTtcbiAgICAgICAgaWYgKHJlc29sdmVyID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIC8vIHVuZGVmaW5lZCBpbmRpY2F0ZXMgcHJpbWl0aXZlIHR5cGUgb3IgcGFyYW1ldGVyIHdpdGhvdXQgRElcbiAgICAgICAgICAgIHJlc29sdmVkRGVwcy5wdXNoKHVuZGVmaW5lZCk7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSBpZiAodHlwZW9mIHJlc29sdmVyID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgICAgICAvLyBSZXNvbHZlciBmdW5jdGlvbjogKGMpID0+IGMucmVzb2x2ZVR5cGUoLi4uKVxuICAgICAgICAgICAgcmVzb2x2ZWREZXBzLnB1c2gocmVzb2x2ZXIoY29udGFpbmVyKSk7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAvLyBUb2tlbi1iYXNlZCByZXNvbHV0aW9uXG4gICAgICAgICAgICByZXNvbHZlZERlcHMucHVzaChjb250YWluZXIucmVzb2x2ZShyZXNvbHZlcikpO1xuICAgICAgICB9XG4gICAgfVxuICAgIHJldHVybiByZXNvbHZlZERlcHM7XG59XG4vKipcbiAqIE1haW4gYXV0b3dpcmUgZnVuY3Rpb24gLSBkaXNwYXRjaGVzIHRvIGFwcHJvcHJpYXRlIHN0cmF0ZWd5XG4gKiBQcmlvcml0eTogbWFwUmVzb2x2ZXJzICh0cmFuc2Zvcm1lci1nZW5lcmF0ZWQpID4gbWFwIChtYW51YWwgb3ZlcnJpZGUpXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBhdXRvd2lyZShjb25zdHJ1Y3RvciwgY29udGFpbmVyLCBvcHRpb25zKSB7XG4gICAgY29uc3Qgb3B0cyA9IHtcbiAgICAgICAgYnk6ICdwYXJhbU5hbWUnLFxuICAgICAgICBzdHJpY3Q6IGZhbHNlLFxuICAgICAgICAuLi5vcHRpb25zXG4gICAgfTtcbiAgICAvLyBISUdIRVNUIFBSSU9SSVRZOiBtYXBSZXNvbHZlcnMgYXJyYXkgKHRyYW5zZm9ybWVyLWdlbmVyYXRlZCwgb3B0aW1hbCBwZXJmb3JtYW5jZSlcbiAgICAvLyBPKDEpIGFycmF5IGFjY2VzcyBwZXIgcGFyYW1ldGVyIC0gbWluaWZpY2F0aW9uLXNhZmUgYW5kIHJlZmFjdG9yaW5nLWZyaWVuZGx5XG4gICAgaWYgKG9wdHMubWFwUmVzb2x2ZXJzICYmIG9wdHMubWFwUmVzb2x2ZXJzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgcmV0dXJuIHJlc29sdmVCeU1hcFJlc29sdmVycyhjb25zdHJ1Y3RvciwgY29udGFpbmVyLCBvcHRzKTtcbiAgICB9XG4gICAgLy8gRkFMTEJBQ0s6IE1hbnVhbCBtYXAgc3RyYXRlZ3kgZm9yIGV4cGxpY2l0IG92ZXJyaWRlc1xuICAgIGlmIChvcHRzLm1hcCAmJiBPYmplY3Qua2V5cyhvcHRzLm1hcCkubGVuZ3RoID4gMCkge1xuICAgICAgICByZXR1cm4gcmVzb2x2ZUJ5TWFwKGNvbnN0cnVjdG9yLCBjb250YWluZXIsIG9wdHMpO1xuICAgIH1cbiAgICAvLyBObyBhdXRvd2lyaW5nIGNvbmZpZ3VyZWQsIHJldHVybiBlbXB0eSBhcnJheVxuICAgIHJldHVybiBbXTtcbn1cbiIsICIvKipcbiAqIEZsdWVudCBidWlsZGVyIEFQSSBmb3IgTm92YURJIENvbnRhaW5lciAoQXV0b2ZhYy1zdHlsZSlcbiAqL1xuaW1wb3J0IHsgVG9rZW4gfSBmcm9tICcuL3Rva2VuLmpzJztcbmltcG9ydCB7IGF1dG93aXJlIH0gZnJvbSAnLi9hdXRvd2lyZS5qcyc7XG4vKipcbiAqIEZsdWVudCByZWdpc3RyYXRpb24gYnVpbGRlciByZXR1cm5lZCBhZnRlciBlYWNoIHJlZ2lzdHJhdGlvbiBtZXRob2RcbiAqL1xuZXhwb3J0IGNsYXNzIFJlZ2lzdHJhdGlvbkJ1aWxkZXIge1xuICAgIGNvbnN0cnVjdG9yKHBlbmRpbmcsIHJlZ2lzdHJhdGlvbnMpIHtcbiAgICAgICAgdGhpcy5yZWdpc3RyYXRpb25zID0gcmVnaXN0cmF0aW9ucztcbiAgICAgICAgdGhpcy5jb25maWdzID0gW107XG4gICAgICAgIHRoaXMuZGVmYXVsdExpZmV0aW1lID0gJ3NpbmdsZXRvbic7XG4gICAgICAgIHRoaXMucGVuZGluZyA9IHBlbmRpbmc7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEJpbmQgdGhpcyByZWdpc3RyYXRpb24gdG8gYSB0b2tlbiBvciBpbnRlcmZhY2UgdHlwZVxuICAgICAqXG4gICAgICogQG92ZXJsb2FkXG4gICAgICogQHBhcmFtIHtUb2tlbjxVPn0gdG9rZW4gLSBFeHBsaWNpdCB0b2tlbiBmb3IgYmluZGluZ1xuICAgICAqXG4gICAgICogQG92ZXJsb2FkXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IHR5cGVOYW1lIC0gSW50ZXJmYWNlIHR5cGUgbmFtZSAoYXV0by1nZW5lcmF0ZWQgYnkgdHJhbnNmb3JtZXIpXG4gICAgICovXG4gICAgYXModG9rZW5PclR5cGVOYW1lKSB7XG4gICAgICAgIC8vIENoZWNrIGlmIGFyZ3VtZW50IGlzIGEgVG9rZW4gb2JqZWN0IChoYXMgc3ltYm9sIHByb3BlcnR5KVxuICAgICAgICBpZiAodG9rZW5PclR5cGVOYW1lICYmIHR5cGVvZiB0b2tlbk9yVHlwZU5hbWUgPT09ICdvYmplY3QnICYmICdzeW1ib2wnIGluIHRva2VuT3JUeXBlTmFtZSkge1xuICAgICAgICAgICAgLy8gVG9rZW4tYmFzZWQgcmVnaXN0cmF0aW9uXG4gICAgICAgICAgICBjb25zdCBjb25maWcgPSB7XG4gICAgICAgICAgICAgICAgdG9rZW46IHRva2VuT3JUeXBlTmFtZSxcbiAgICAgICAgICAgICAgICB0eXBlOiB0aGlzLnBlbmRpbmcudHlwZSxcbiAgICAgICAgICAgICAgICB2YWx1ZTogdGhpcy5wZW5kaW5nLnZhbHVlLFxuICAgICAgICAgICAgICAgIGZhY3Rvcnk6IHRoaXMucGVuZGluZy5mYWN0b3J5LFxuICAgICAgICAgICAgICAgIGNvbnN0cnVjdG9yOiB0aGlzLnBlbmRpbmcuY29uc3RydWN0b3IsXG4gICAgICAgICAgICAgICAgbGlmZXRpbWU6IHRoaXMuZGVmYXVsdExpZmV0aW1lXG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgdGhpcy5jb25maWdzLnB1c2goY29uZmlnKTtcbiAgICAgICAgICAgIHRoaXMucmVnaXN0cmF0aW9ucy5wdXNoKGNvbmZpZyk7XG4gICAgICAgICAgICByZXR1cm4gdGhpcztcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIC8vIEludGVyZmFjZS1iYXNlZCByZWdpc3RyYXRpb24gKHR5cGVOYW1lIHN0cmluZyBvciB1bmRlZmluZWQpXG4gICAgICAgICAgICBjb25zdCBjb25maWcgPSB7XG4gICAgICAgICAgICAgICAgdG9rZW46IG51bGwsIC8vIFdpbGwgYmUgc2V0IGR1cmluZyBidWlsZCgpXG4gICAgICAgICAgICAgICAgdHlwZTogdGhpcy5wZW5kaW5nLnR5cGUsXG4gICAgICAgICAgICAgICAgdmFsdWU6IHRoaXMucGVuZGluZy52YWx1ZSxcbiAgICAgICAgICAgICAgICBmYWN0b3J5OiB0aGlzLnBlbmRpbmcuZmFjdG9yeSxcbiAgICAgICAgICAgICAgICBjb25zdHJ1Y3RvcjogdGhpcy5wZW5kaW5nLmNvbnN0cnVjdG9yLFxuICAgICAgICAgICAgICAgIGxpZmV0aW1lOiB0aGlzLmRlZmF1bHRMaWZldGltZSxcbiAgICAgICAgICAgICAgICBpbnRlcmZhY2VUeXBlOiB0b2tlbk9yVHlwZU5hbWVcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICB0aGlzLmNvbmZpZ3MucHVzaChjb25maWcpO1xuICAgICAgICAgICAgdGhpcy5yZWdpc3RyYXRpb25zLnB1c2goY29uZmlnKTtcbiAgICAgICAgICAgIHJldHVybiB0aGlzO1xuICAgICAgICB9XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFJlZ2lzdGVyIGFzIGRlZmF1bHQgaW1wbGVtZW50YXRpb24gZm9yIGFuIGludGVyZmFjZVxuICAgICAqIENvbWJpbmVzIGFzKCkgKyBhc0RlZmF1bHQoKVxuICAgICAqL1xuICAgIGFzRGVmYXVsdEludGVyZmFjZSh0eXBlTmFtZSkge1xuICAgICAgICB0aGlzLmFzKFwiVEludGVyZmFjZVwiLCB0eXBlTmFtZSk7XG4gICAgICAgIHJldHVybiB0aGlzLmFzRGVmYXVsdCgpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBSZWdpc3RlciBhcyBhIGtleWVkIGludGVyZmFjZSBpbXBsZW1lbnRhdGlvblxuICAgICAqIENvbWJpbmVzIGFzKCkgKyBrZXllZCgpXG4gICAgICovXG4gICAgYXNLZXllZEludGVyZmFjZShrZXksIHR5cGVOYW1lKSB7XG4gICAgICAgIHRoaXMuYXMoXCJUSW50ZXJmYWNlXCIsIHR5cGVOYW1lKTtcbiAgICAgICAgcmV0dXJuIHRoaXMua2V5ZWQoa2V5KTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogUmVnaXN0ZXIgYXMgbXVsdGlwbGUgaW1wbGVtZW50ZWQgaW50ZXJmYWNlc1xuICAgICAqL1xuICAgIGFzSW1wbGVtZW50ZWRJbnRlcmZhY2VzKHRva2Vucykge1xuICAgICAgICBpZiAodG9rZW5zLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgICAgIH1cbiAgICAgICAgLy8gSWYgdGhlcmUgYXJlIGV4aXN0aW5nIGNvbmZpZ3MgKGZyb20gcHJldmlvdXMgYXMoKSBjYWxscyksIGFkZCB0aGVzZSBhcyBhZGRpdGlvbmFsIGludGVyZmFjZXNcbiAgICAgICAgaWYgKHRoaXMuY29uZmlncy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAvLyBBZGQgYWxsIHRva2VucyBhcyBhZGRpdGlvbmFsIGludGVyZmFjZXMgdG8gZXhpc3RpbmcgY29uZmlnc1xuICAgICAgICAgICAgZm9yIChjb25zdCBjb25maWcgb2YgdGhpcy5jb25maWdzKSB7XG4gICAgICAgICAgICAgICAgY29uZmlnLmxpZmV0aW1lID0gJ3NpbmdsZXRvbic7IC8vIGFzSW1wbGVtZW50ZWRJbnRlcmZhY2VzIGRlZmF1bHRzIHRvIHNpbmdsZXRvblxuICAgICAgICAgICAgICAgIGNvbmZpZy5hZGRpdGlvbmFsVG9rZW5zID0gY29uZmlnLmFkZGl0aW9uYWxUb2tlbnMgfHwgW107XG4gICAgICAgICAgICAgICAgY29uZmlnLmFkZGl0aW9uYWxUb2tlbnMucHVzaCguLi50b2tlbnMpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgICAgIH1cbiAgICAgICAgLy8gTm8gZXhpc3RpbmcgY29uZmlncywgY3JlYXRlIG5ldyBvbmUgd2l0aCBmaXJzdCB0b2tlblxuICAgICAgICBjb25zdCBmaXJzdENvbmZpZyA9IHtcbiAgICAgICAgICAgIHRva2VuOiB0b2tlbnNbMF0sXG4gICAgICAgICAgICB0eXBlOiB0aGlzLnBlbmRpbmcudHlwZSxcbiAgICAgICAgICAgIHZhbHVlOiB0aGlzLnBlbmRpbmcudmFsdWUsXG4gICAgICAgICAgICBmYWN0b3J5OiB0aGlzLnBlbmRpbmcuZmFjdG9yeSxcbiAgICAgICAgICAgIGNvbnN0cnVjdG9yOiB0aGlzLnBlbmRpbmcuY29uc3RydWN0b3IsXG4gICAgICAgICAgICBsaWZldGltZTogJ3NpbmdsZXRvbidcbiAgICAgICAgfTtcbiAgICAgICAgdGhpcy5jb25maWdzLnB1c2goZmlyc3RDb25maWcpO1xuICAgICAgICB0aGlzLnJlZ2lzdHJhdGlvbnMucHVzaChmaXJzdENvbmZpZyk7XG4gICAgICAgIC8vIEFkZGl0aW9uYWwgdG9rZW5zIHJlZmVyZW5jZSB0aGUgc2FtZSByZWdpc3RyYXRpb25cbiAgICAgICAgZm9yIChsZXQgaSA9IDE7IGkgPCB0b2tlbnMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIGZpcnN0Q29uZmlnLmFkZGl0aW9uYWxUb2tlbnMgPSBmaXJzdENvbmZpZy5hZGRpdGlvbmFsVG9rZW5zIHx8IFtdO1xuICAgICAgICAgICAgZmlyc3RDb25maWcuYWRkaXRpb25hbFRva2Vucy5wdXNoKHRva2Vuc1tpXSk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFNldCBzaW5nbGV0b24gbGlmZXRpbWUgKG9uZSBpbnN0YW5jZSBmb3IgZW50aXJlIGNvbnRhaW5lcilcbiAgICAgKi9cbiAgICBzaW5nbGVJbnN0YW5jZSgpIHtcbiAgICAgICAgZm9yIChjb25zdCBjb25maWcgb2YgdGhpcy5jb25maWdzKSB7XG4gICAgICAgICAgICBjb25maWcubGlmZXRpbWUgPSAnc2luZ2xldG9uJztcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9XG4gICAgLyoqXG4gICAgICogU2V0IHBlci1yZXF1ZXN0IGxpZmV0aW1lIChvbmUgaW5zdGFuY2UgcGVyIHJlc29sdmUgY2FsbCB0cmVlKVxuICAgICAqL1xuICAgIGluc3RhbmNlUGVyUmVxdWVzdCgpIHtcbiAgICAgICAgZm9yIChjb25zdCBjb25maWcgb2YgdGhpcy5jb25maWdzKSB7XG4gICAgICAgICAgICBjb25maWcubGlmZXRpbWUgPSAncGVyLXJlcXVlc3QnO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBTZXQgdHJhbnNpZW50IGxpZmV0aW1lIChuZXcgaW5zdGFuY2UgZXZlcnkgdGltZSlcbiAgICAgKiBBbGlhcyBmb3IgZGVmYXVsdCBiZWhhdmlvclxuICAgICAqL1xuICAgIGluc3RhbmNlUGVyRGVwZW5kZW5jeSgpIHtcbiAgICAgICAgZm9yIChjb25zdCBjb25maWcgb2YgdGhpcy5jb25maWdzKSB7XG4gICAgICAgICAgICBjb25maWcubGlmZXRpbWUgPSAndHJhbnNpZW50JztcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9XG4gICAgLyoqXG4gICAgICogTmFtZSB0aGlzIHJlZ2lzdHJhdGlvbiBmb3IgbmFtZWQgcmVzb2x1dGlvblxuICAgICAqL1xuICAgIG5hbWVkKG5hbWUpIHtcbiAgICAgICAgZm9yIChjb25zdCBjb25maWcgb2YgdGhpcy5jb25maWdzKSB7XG4gICAgICAgICAgICBjb25maWcubmFtZSA9IG5hbWU7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEtleSB0aGlzIHJlZ2lzdHJhdGlvbiBmb3Iga2V5ZWQgcmVzb2x1dGlvblxuICAgICAqL1xuICAgIGtleWVkKGtleSkge1xuICAgICAgICBmb3IgKGNvbnN0IGNvbmZpZyBvZiB0aGlzLmNvbmZpZ3MpIHtcbiAgICAgICAgICAgIGNvbmZpZy5rZXkgPSBrZXk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIE1hcmsgdGhpcyBhcyBkZWZhdWx0IHJlZ2lzdHJhdGlvblxuICAgICAqIERlZmF1bHQgcmVnaXN0cmF0aW9ucyBkb24ndCBvdmVycmlkZSBleGlzdGluZyBvbmVzXG4gICAgICovXG4gICAgYXNEZWZhdWx0KCkge1xuICAgICAgICBmb3IgKGNvbnN0IGNvbmZpZyBvZiB0aGlzLmNvbmZpZ3MpIHtcbiAgICAgICAgICAgIGNvbmZpZy5pc0RlZmF1bHQgPSB0cnVlO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBPbmx5IHJlZ2lzdGVyIGlmIHRva2VuIG5vdCBhbHJlYWR5IHJlZ2lzdGVyZWRcbiAgICAgKi9cbiAgICBpZk5vdFJlZ2lzdGVyZWQoKSB7XG4gICAgICAgIGZvciAoY29uc3QgY29uZmlnIG9mIHRoaXMuY29uZmlncykge1xuICAgICAgICAgICAgY29uZmlnLmlmTm90UmVnaXN0ZXJlZCA9IHRydWU7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFNwZWNpZnkgcGFyYW1ldGVyIHZhbHVlcyBmb3IgY29uc3RydWN0b3IgKHByaW1pdGl2ZXMgYW5kIGNvbnN0YW50cylcbiAgICAgKiBVc2UgdGhpcyBmb3Igbm9uLURJIHBhcmFtZXRlcnMgbGlrZSBzdHJpbmdzLCBudW1iZXJzLCBjb25maWcgdmFsdWVzXG4gICAgICovXG4gICAgd2l0aFBhcmFtZXRlcnMocGFyYW1ldGVycykge1xuICAgICAgICBmb3IgKGNvbnN0IGNvbmZpZyBvZiB0aGlzLmNvbmZpZ3MpIHtcbiAgICAgICAgICAgIGNvbmZpZy5wYXJhbWV0ZXJWYWx1ZXMgPSBwYXJhbWV0ZXJzO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBFbmFibGUgYXV0b21hdGljIGRlcGVuZGVuY3kgaW5qZWN0aW9uIChhdXRvd2lyaW5nKVxuICAgICAqIFN1cHBvcnRzIHRocmVlIHN0cmF0ZWdpZXM6IHBhcmFtTmFtZSAoZGVmYXVsdCksIG1hcCwgYW5kIGNsYXNzXG4gICAgICpcbiAgICAgKiBAZXhhbXBsZVxuICAgICAqIGBgYHRzXG4gICAgICogLy8gU3RyYXRlZ3kgMTogcGFyYW1OYW1lIChkZWZhdWx0LCByZXF1aXJlcyBub24tbWluaWZpZWQgY29kZSBpbiBkZXYpXG4gICAgICogYnVpbGRlci5yZWdpc3RlclR5cGUoRXZlbnRCdXMpLmFzPElFdmVudEJ1cz4oKS5hdXRvV2lyZSgpXG4gICAgICpcbiAgICAgKiAvLyBTdHJhdGVneSAyOiBtYXAgKG1pbmlmeS1zYWZlLCBleHBsaWNpdClcbiAgICAgKiBidWlsZGVyLnJlZ2lzdGVyVHlwZShFdmVudEJ1cykuYXM8SUV2ZW50QnVzPigpLmF1dG9XaXJlKHtcbiAgICAgKiAgIG1hcDoge1xuICAgICAqICAgICBsb2dnZXI6IChjKSA9PiBjLnJlc29sdmVUeXBlPElMb2dnZXI+KClcbiAgICAgKiAgIH1cbiAgICAgKiB9KVxuICAgICAqXG4gICAgICogLy8gU3RyYXRlZ3kgMzogY2xhc3MgKHJlcXVpcmVzIGJ1aWxkLXRpbWUgY29kZWdlbilcbiAgICAgKiBidWlsZGVyLnJlZ2lzdGVyVHlwZShFdmVudEJ1cykuYXM8SUV2ZW50QnVzPigpLmF1dG9XaXJlKHsgYnk6ICdjbGFzcycgfSlcbiAgICAgKiBgYGBcbiAgICAgKi9cbiAgICBhdXRvV2lyZShvcHRpb25zKSB7XG4gICAgICAgIGZvciAoY29uc3QgY29uZmlnIG9mIHRoaXMuY29uZmlncykge1xuICAgICAgICAgICAgY29uZmlnLmF1dG93aXJlT3B0aW9ucyA9IG9wdGlvbnMgfHwgeyBieTogJ3BhcmFtTmFtZScsIHN0cmljdDogZmFsc2UgfTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9XG59XG4vKipcbiAqIEZsdWVudCBidWlsZGVyIGZvciBDb250YWluZXIgY29uZmlndXJhdGlvblxuICovXG5leHBvcnQgY2xhc3MgQnVpbGRlciB7XG4gICAgY29uc3RydWN0b3IoYmFzZUNvbnRhaW5lcikge1xuICAgICAgICB0aGlzLmJhc2VDb250YWluZXIgPSBiYXNlQ29udGFpbmVyO1xuICAgICAgICB0aGlzLnJlZ2lzdHJhdGlvbnMgPSBbXTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogUmVnaXN0ZXIgYSBjbGFzcyBjb25zdHJ1Y3RvclxuICAgICAqL1xuICAgIHJlZ2lzdGVyVHlwZShjb25zdHJ1Y3Rvcikge1xuICAgICAgICBjb25zdCBwZW5kaW5nID0ge1xuICAgICAgICAgICAgdHlwZTogJ3R5cGUnLFxuICAgICAgICAgICAgdmFsdWU6IG51bGwsXG4gICAgICAgICAgICBjb25zdHJ1Y3RvclxuICAgICAgICB9O1xuICAgICAgICByZXR1cm4gbmV3IFJlZ2lzdHJhdGlvbkJ1aWxkZXIocGVuZGluZywgdGhpcy5yZWdpc3RyYXRpb25zKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogUmVnaXN0ZXIgYSBwcmUtY3JlYXRlZCBpbnN0YW5jZVxuICAgICAqL1xuICAgIHJlZ2lzdGVySW5zdGFuY2UoaW5zdGFuY2UpIHtcbiAgICAgICAgY29uc3QgcGVuZGluZyA9IHtcbiAgICAgICAgICAgIHR5cGU6ICdpbnN0YW5jZScsXG4gICAgICAgICAgICB2YWx1ZTogaW5zdGFuY2UsXG4gICAgICAgICAgICBjb25zdHJ1Y3RvcjogdW5kZWZpbmVkXG4gICAgICAgIH07XG4gICAgICAgIHJldHVybiBuZXcgUmVnaXN0cmF0aW9uQnVpbGRlcihwZW5kaW5nLCB0aGlzLnJlZ2lzdHJhdGlvbnMpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBSZWdpc3RlciBhIGZhY3RvcnkgZnVuY3Rpb25cbiAgICAgKi9cbiAgICByZWdpc3RlcihmYWN0b3J5KSB7XG4gICAgICAgIGNvbnN0IHBlbmRpbmcgPSB7XG4gICAgICAgICAgICB0eXBlOiAnZmFjdG9yeScsXG4gICAgICAgICAgICB2YWx1ZTogbnVsbCxcbiAgICAgICAgICAgIGZhY3RvcnksXG4gICAgICAgICAgICBjb25zdHJ1Y3RvcjogdW5kZWZpbmVkXG4gICAgICAgIH07XG4gICAgICAgIHJldHVybiBuZXcgUmVnaXN0cmF0aW9uQnVpbGRlcihwZW5kaW5nLCB0aGlzLnJlZ2lzdHJhdGlvbnMpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBSZWdpc3RlciBhIG1vZHVsZSAoZnVuY3Rpb24gdGhhdCBhZGRzIG11bHRpcGxlIHJlZ2lzdHJhdGlvbnMpXG4gICAgICovXG4gICAgbW9kdWxlKG1vZHVsZUZ1bmMpIHtcbiAgICAgICAgbW9kdWxlRnVuYyh0aGlzKTtcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFJlc29sdmUgaW50ZXJmYWNlIHR5cGUgbmFtZXMgdG8gdG9rZW5zXG4gICAgICogQGludGVybmFsXG4gICAgICovXG4gICAgcmVzb2x2ZUludGVyZmFjZVRva2Vucyhjb250YWluZXIpIHtcbiAgICAgICAgZm9yIChjb25zdCBjb25maWcgb2YgdGhpcy5yZWdpc3RyYXRpb25zKSB7XG4gICAgICAgICAgICBpZiAoY29uZmlnLmludGVyZmFjZVR5cGUgIT09IHVuZGVmaW5lZCAmJiAhY29uZmlnLnRva2VuKSB7XG4gICAgICAgICAgICAgICAgY29uZmlnLnRva2VuID0gY29udGFpbmVyLmludGVyZmFjZVRva2VuKGNvbmZpZy5pbnRlcmZhY2VUeXBlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cbiAgICAvKipcbiAgICAgKiBJZGVudGlmeSB0b2tlbnMgdGhhdCBoYXZlIG5vbi1kZWZhdWx0IHJlZ2lzdHJhdGlvbnNcbiAgICAgKiBAaW50ZXJuYWxcbiAgICAgKi9cbiAgICBpZGVudGlmeU5vbkRlZmF1bHRUb2tlbnMoKSB7XG4gICAgICAgIGNvbnN0IHRva2Vuc1dpdGhOb25EZWZhdWx0cyA9IG5ldyBTZXQoKTtcbiAgICAgICAgZm9yIChjb25zdCBjb25maWcgb2YgdGhpcy5yZWdpc3RyYXRpb25zKSB7XG4gICAgICAgICAgICBpZiAoIWNvbmZpZy5pc0RlZmF1bHQgJiYgIWNvbmZpZy5uYW1lICYmIGNvbmZpZy5rZXkgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgICAgIHRva2Vuc1dpdGhOb25EZWZhdWx0cy5hZGQoY29uZmlnLnRva2VuKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdG9rZW5zV2l0aE5vbkRlZmF1bHRzO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBDaGVjayBpZiByZWdpc3RyYXRpb24gc2hvdWxkIGJlIHNraXBwZWRcbiAgICAgKiBAaW50ZXJuYWxcbiAgICAgKi9cbiAgICBzaG91bGRTa2lwUmVnaXN0cmF0aW9uKGNvbmZpZywgdG9rZW5zV2l0aE5vbkRlZmF1bHRzLCByZWdpc3RlcmVkVG9rZW5zKSB7XG4gICAgICAgIC8vIFNraXAgZGVmYXVsdCByZWdpc3RyYXRpb25zIGlmIHRoZXJlJ3MgYSBub24tZGVmYXVsdCBmb3IgdGhlIHNhbWUgdG9rZW5cbiAgICAgICAgaWYgKGNvbmZpZy5pc0RlZmF1bHQgJiYgIWNvbmZpZy5uYW1lICYmIGNvbmZpZy5rZXkgPT09IHVuZGVmaW5lZCAmJiB0b2tlbnNXaXRoTm9uRGVmYXVsdHMuaGFzKGNvbmZpZy50b2tlbikpIHtcbiAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICB9XG4gICAgICAgIC8vIEhhbmRsZSBpZk5vdFJlZ2lzdGVyZWRcbiAgICAgICAgaWYgKGNvbmZpZy5pZk5vdFJlZ2lzdGVyZWQgJiYgcmVnaXN0ZXJlZFRva2Vucy5oYXMoY29uZmlnLnRva2VuKSkge1xuICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgIH1cbiAgICAgICAgLy8gSGFuZGxlIGFzRGVmYXVsdFxuICAgICAgICBpZiAoY29uZmlnLmlzRGVmYXVsdCAmJiByZWdpc3RlcmVkVG9rZW5zLmhhcyhjb25maWcudG9rZW4pKSB7XG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIENyZWF0ZSBiaW5kaW5nIHRva2VuIGZvciByZWdpc3RyYXRpb24gKG5hbWVkLCBrZXllZCwgb3IgbXVsdGkpXG4gICAgICogQGludGVybmFsXG4gICAgICovXG4gICAgY3JlYXRlQmluZGluZ1Rva2VuKGNvbmZpZywgbmFtZWRSZWdpc3RyYXRpb25zLCBrZXllZFJlZ2lzdHJhdGlvbnMsIG11bHRpUmVnaXN0cmF0aW9ucykge1xuICAgICAgICBpZiAoY29uZmlnLm5hbWUpIHtcbiAgICAgICAgICAgIC8vIE5hbWVkIHJlZ2lzdHJhdGlvbiBnZXRzIHVuaXF1ZSB0b2tlblxuICAgICAgICAgICAgY29uc3QgYmluZGluZ1Rva2VuID0gVG9rZW4oYF9fbmFtZWRfJHtjb25maWcubmFtZX1gKTtcbiAgICAgICAgICAgIG5hbWVkUmVnaXN0cmF0aW9ucy5zZXQoY29uZmlnLm5hbWUsIHsgLi4uY29uZmlnLCB0b2tlbjogYmluZGluZ1Rva2VuIH0pO1xuICAgICAgICAgICAgcmV0dXJuIGJpbmRpbmdUb2tlbjtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIGlmIChjb25maWcua2V5ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIC8vIEtleWVkIHJlZ2lzdHJhdGlvbiBnZXRzIHVuaXF1ZSB0b2tlblxuICAgICAgICAgICAgY29uc3Qga2V5U3RyID0gdHlwZW9mIGNvbmZpZy5rZXkgPT09ICdzeW1ib2wnID8gY29uZmlnLmtleS50b1N0cmluZygpIDogY29uZmlnLmtleTtcbiAgICAgICAgICAgIGNvbnN0IGJpbmRpbmdUb2tlbiA9IFRva2VuKGBfX2tleWVkXyR7a2V5U3RyfWApO1xuICAgICAgICAgICAga2V5ZWRSZWdpc3RyYXRpb25zLnNldChjb25maWcua2V5LCB7IC4uLmNvbmZpZywgdG9rZW46IGJpbmRpbmdUb2tlbiB9KTtcbiAgICAgICAgICAgIHJldHVybiBiaW5kaW5nVG9rZW47XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAvLyBNdWx0aS1yZWdpc3RyYXRpb24gaGFuZGxpbmdcbiAgICAgICAgICAgIGlmIChtdWx0aVJlZ2lzdHJhdGlvbnMuaGFzKGNvbmZpZy50b2tlbikpIHtcbiAgICAgICAgICAgICAgICAvLyBTdWJzZXF1ZW50IHJlZ2lzdHJhdGlvbiBmb3IgdGhpcyB0b2tlblxuICAgICAgICAgICAgICAgIGNvbnN0IGJpbmRpbmdUb2tlbiA9IFRva2VuKGBfX211bHRpXyR7Y29uZmlnLnRva2VuLnRvU3RyaW5nKCl9XyR7bXVsdGlSZWdpc3RyYXRpb25zLmdldChjb25maWcudG9rZW4pLmxlbmd0aH1gKTtcbiAgICAgICAgICAgICAgICBtdWx0aVJlZ2lzdHJhdGlvbnMuZ2V0KGNvbmZpZy50b2tlbikucHVzaChiaW5kaW5nVG9rZW4pO1xuICAgICAgICAgICAgICAgIHJldHVybiBiaW5kaW5nVG9rZW47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgICAvLyBGaXJzdCByZWdpc3RyYXRpb24gZm9yIHRoaXMgdG9rZW4sIHVzZSB0aGUgb3JpZ2luYWwgdG9rZW5cbiAgICAgICAgICAgICAgICBtdWx0aVJlZ2lzdHJhdGlvbnMuc2V0KGNvbmZpZy50b2tlbiwgW2NvbmZpZy50b2tlbl0pO1xuICAgICAgICAgICAgICAgIHJldHVybiBjb25maWcudG9rZW47XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG4gICAgLyoqXG4gICAgICogUmVnaXN0ZXIgYWRkaXRpb25hbCBpbnRlcmZhY2VzIGZvciBhIGNvbmZpZ1xuICAgICAqIEBpbnRlcm5hbFxuICAgICAqL1xuICAgIHJlZ2lzdGVyQWRkaXRpb25hbEludGVyZmFjZXMoY29udGFpbmVyLCBjb25maWcsIGJpbmRpbmdUb2tlbiwgcmVnaXN0ZXJlZFRva2Vucykge1xuICAgICAgICBpZiAoY29uZmlnLmFkZGl0aW9uYWxUb2tlbnMpIHtcbiAgICAgICAgICAgIGZvciAoY29uc3QgYWRkaXRpb25hbFRva2VuIG9mIGNvbmZpZy5hZGRpdGlvbmFsVG9rZW5zKSB7XG4gICAgICAgICAgICAgICAgLy8gQ3JlYXRlIGEgZmFjdG9yeSB0aGF0IHJlc29sdmVzIHRoZSBiaW5kaW5nIHRva2VuXG4gICAgICAgICAgICAgICAgY29udGFpbmVyLmJpbmRGYWN0b3J5KGFkZGl0aW9uYWxUb2tlbiwgKGMpID0+IGMucmVzb2x2ZShiaW5kaW5nVG9rZW4pLCB7IGxpZmV0aW1lOiBjb25maWcubGlmZXRpbWUgfSk7XG4gICAgICAgICAgICAgICAgcmVnaXN0ZXJlZFRva2Vucy5hZGQoYWRkaXRpb25hbFRva2VuKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cbiAgICAvKipcbiAgICAgKiBCdWlsZCB0aGUgY29udGFpbmVyIHdpdGggYWxsIHJlZ2lzdGVyZWQgYmluZGluZ3NcbiAgICAgKi9cbiAgICBidWlsZCgpIHtcbiAgICAgICAgLy8gQ3JlYXRlIG5ldyBjb250YWluZXIgaW5oZXJpdGluZyBmcm9tIGJhc2VcbiAgICAgICAgY29uc3QgY29udGFpbmVyID0gdGhpcy5iYXNlQ29udGFpbmVyLmNyZWF0ZUNoaWxkKCk7XG4gICAgICAgIC8vIFByZS1wcm9jZXNzOiByZXNvbHZlIGludGVyZmFjZSB0eXBlcyB0byB0b2tlbnNcbiAgICAgICAgdGhpcy5yZXNvbHZlSW50ZXJmYWNlVG9rZW5zKGNvbnRhaW5lcik7XG4gICAgICAgIC8vIFRyYWNrIHdoYXQncyBiZWVuIHJlZ2lzdGVyZWQgZm9yIGlmTm90UmVnaXN0ZXJlZCBjaGVja3NcbiAgICAgICAgY29uc3QgcmVnaXN0ZXJlZFRva2VucyA9IG5ldyBTZXQoKTtcbiAgICAgICAgY29uc3QgbmFtZWRSZWdpc3RyYXRpb25zID0gbmV3IE1hcCgpO1xuICAgICAgICBjb25zdCBrZXllZFJlZ2lzdHJhdGlvbnMgPSBuZXcgTWFwKCk7XG4gICAgICAgIGNvbnN0IG11bHRpUmVnaXN0cmF0aW9ucyA9IG5ldyBNYXAoKTtcbiAgICAgICAgLy8gUHJlLXByb2Nlc3M6IGlkZW50aWZ5IHRva2VucyB0aGF0IGhhdmUgbm9uLWRlZmF1bHQgcmVnaXN0cmF0aW9uc1xuICAgICAgICBjb25zdCB0b2tlbnNXaXRoTm9uRGVmYXVsdHMgPSB0aGlzLmlkZW50aWZ5Tm9uRGVmYXVsdFRva2VucygpO1xuICAgICAgICBmb3IgKGNvbnN0IGNvbmZpZyBvZiB0aGlzLnJlZ2lzdHJhdGlvbnMpIHtcbiAgICAgICAgICAgIC8vIENoZWNrIGlmIHJlZ2lzdHJhdGlvbiBzaG91bGQgYmUgc2tpcHBlZFxuICAgICAgICAgICAgaWYgKHRoaXMuc2hvdWxkU2tpcFJlZ2lzdHJhdGlvbihjb25maWcsIHRva2Vuc1dpdGhOb25EZWZhdWx0cywgcmVnaXN0ZXJlZFRva2VucykpIHtcbiAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIENyZWF0ZSBiaW5kaW5nIHRva2VuIChuYW1lZCwga2V5ZWQsIG9yIG11bHRpKVxuICAgICAgICAgICAgY29uc3QgYmluZGluZ1Rva2VuID0gdGhpcy5jcmVhdGVCaW5kaW5nVG9rZW4oY29uZmlnLCBuYW1lZFJlZ2lzdHJhdGlvbnMsIGtleWVkUmVnaXN0cmF0aW9ucywgbXVsdGlSZWdpc3RyYXRpb25zKTtcbiAgICAgICAgICAgIC8vIEFwcGx5IHJlZ2lzdHJhdGlvbiB0byBjb250YWluZXIgdXNpbmcgdGhlIGJpbmRpbmcgdG9rZW5cbiAgICAgICAgICAgIHRoaXMuYXBwbHlSZWdpc3RyYXRpb24oY29udGFpbmVyLCB7IC4uLmNvbmZpZywgdG9rZW46IGJpbmRpbmdUb2tlbiB9KTtcbiAgICAgICAgICAgIC8vIE1hcmsgb3JpZ2luYWwgdG9rZW4gYXMgcmVnaXN0ZXJlZFxuICAgICAgICAgICAgcmVnaXN0ZXJlZFRva2Vucy5hZGQoY29uZmlnLnRva2VuKTtcbiAgICAgICAgICAgIC8vIFJlZ2lzdGVyIGFkZGl0aW9uYWwgaW50ZXJmYWNlc1xuICAgICAgICAgICAgdGhpcy5yZWdpc3RlckFkZGl0aW9uYWxJbnRlcmZhY2VzKGNvbnRhaW5lciwgY29uZmlnLCBiaW5kaW5nVG9rZW4sIHJlZ2lzdGVyZWRUb2tlbnMpO1xuICAgICAgICB9XG4gICAgICAgIC8vIEF0dGFjaCBtZXRhZGF0YSBmb3IgbmFtZWQva2V5ZWQgcmVzb2x1dGlvblxuICAgICAgICA7XG4gICAgICAgIGNvbnRhaW5lci5fX25hbWVkUmVnaXN0cmF0aW9ucyA9IG5hbWVkUmVnaXN0cmF0aW9ucztcbiAgICAgICAgY29udGFpbmVyLl9fa2V5ZWRSZWdpc3RyYXRpb25zID0ga2V5ZWRSZWdpc3RyYXRpb25zO1xuICAgICAgICBjb250YWluZXIuX19tdWx0aVJlZ2lzdHJhdGlvbnMgPSBtdWx0aVJlZ2lzdHJhdGlvbnM7XG4gICAgICAgIHJldHVybiBjb250YWluZXI7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEFuYWx5emUgY29uc3RydWN0b3IgdG8gZGV0ZWN0IGRlcGVuZGVuY2llc1xuICAgICAqIEBpbnRlcm5hbFxuICAgICAqL1xuICAgIGFuYWx5emVDb25zdHJ1Y3Rvcihjb25zdHJ1Y3Rvcikge1xuICAgICAgICBjb25zdCBjb25zdHJ1Y3RvclN0ciA9IGNvbnN0cnVjdG9yLnRvU3RyaW5nKCk7XG4gICAgICAgIGNvbnN0IGhhc0RlcGVuZGVuY2llcyA9IC9jb25zdHJ1Y3RvclxccypcXChbXildK1xcKS8udGVzdChjb25zdHJ1Y3RvclN0cik7XG4gICAgICAgIHJldHVybiB7IGhhc0RlcGVuZGVuY2llcyB9O1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBDcmVhdGUgb3B0aW1pemVkIGZhY3RvcnkgZm9yIHplcm8tZGVwZW5kZW5jeSBjb25zdHJ1Y3RvcnNcbiAgICAgKiBAaW50ZXJuYWxcbiAgICAgKi9cbiAgICBjcmVhdGVPcHRpbWl6ZWRGYWN0b3J5KGNvbnRhaW5lciwgY29uZmlnLCBvcHRpb25zKSB7XG4gICAgICAgIGlmIChjb25maWcubGlmZXRpbWUgPT09ICdzaW5nbGV0b24nKSB7XG4gICAgICAgICAgICAvLyBTaW5nbGV0b246IENyZWF0ZSBpbnN0YW5jZSBkaXJlY3RseSAoZmFzdGVzdCBwYXRoIC0gbm8gZmFjdG9yeSBvdmVyaGVhZClcbiAgICAgICAgICAgIGNvbnN0IGluc3RhbmNlID0gbmV3IGNvbmZpZy5jb25zdHJ1Y3RvcigpO1xuICAgICAgICAgICAgY29udGFpbmVyLmJpbmRWYWx1ZShjb25maWcudG9rZW4sIGluc3RhbmNlKTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIGlmIChjb25maWcubGlmZXRpbWUgPT09ICd0cmFuc2llbnQnKSB7XG4gICAgICAgICAgICAvLyBUcmFuc2llbnQgRmFzdCBQYXRoOiBSZWdpc3RlciBpbiBmYXN0IHRyYW5zaWVudCBjYWNoZVxuICAgICAgICAgICAgY29uc3QgY3RvciA9IGNvbmZpZy5jb25zdHJ1Y3RvcjtcbiAgICAgICAgICAgIGNvbnN0IGZhc3RGYWN0b3J5ID0gKCkgPT4gbmV3IGN0b3IoKTtcbiAgICAgICAgICAgIGNvbnRhaW5lci5mYXN0VHJhbnNpZW50Q2FjaGUuc2V0KGNvbmZpZy50b2tlbiwgZmFzdEZhY3RvcnkpO1xuICAgICAgICAgICAgY29udGFpbmVyLmJpbmRGYWN0b3J5KGNvbmZpZy50b2tlbiwgZmFzdEZhY3RvcnksIG9wdGlvbnMpO1xuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgLy8gUGVyLXJlcXVlc3Q6IFVzZSBzaW1wbGUgZmFjdG9yeSB3aXRob3V0IGF1dG93aXJlIG92ZXJoZWFkXG4gICAgICAgICAgICBjb25zdCBmYWN0b3J5ID0gKCkgPT4gbmV3IGNvbmZpZy5jb25zdHJ1Y3RvcigpO1xuICAgICAgICAgICAgY29udGFpbmVyLmJpbmRGYWN0b3J5KGNvbmZpZy50b2tlbiwgZmFjdG9yeSwgb3B0aW9ucyk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgLyoqXG4gICAgICogQ3JlYXRlIGF1dG93aXJlIGZhY3RvcnlcbiAgICAgKiBAaW50ZXJuYWxcbiAgICAgKi9cbiAgICBjcmVhdGVBdXRvV2lyZUZhY3RvcnkoY29udGFpbmVyLCBjb25maWcsIG9wdGlvbnMpIHtcbiAgICAgICAgY29uc3QgZmFjdG9yeSA9IChjKSA9PiB7XG4gICAgICAgICAgICBjb25zdCByZXNvbHZlZERlcHMgPSBhdXRvd2lyZShjb25maWcuY29uc3RydWN0b3IsIGMsIGNvbmZpZy5hdXRvd2lyZU9wdGlvbnMpO1xuICAgICAgICAgICAgcmV0dXJuIG5ldyBjb25maWcuY29uc3RydWN0b3IoLi4ucmVzb2x2ZWREZXBzKTtcbiAgICAgICAgfTtcbiAgICAgICAgY29udGFpbmVyLmJpbmRGYWN0b3J5KGNvbmZpZy50b2tlbiwgZmFjdG9yeSwgb3B0aW9ucyk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIENyZWF0ZSB3aXRoUGFyYW1ldGVycyBmYWN0b3J5XG4gICAgICogQGludGVybmFsXG4gICAgICovXG4gICAgY3JlYXRlUGFyYW1ldGVyRmFjdG9yeShjb250YWluZXIsIGNvbmZpZywgb3B0aW9ucykge1xuICAgICAgICBjb25zdCBmYWN0b3J5ID0gKCkgPT4ge1xuICAgICAgICAgICAgY29uc3QgdmFsdWVzID0gT2JqZWN0LnZhbHVlcyhjb25maWcucGFyYW1ldGVyVmFsdWVzKTtcbiAgICAgICAgICAgIHJldHVybiBuZXcgY29uZmlnLmNvbnN0cnVjdG9yKC4uLnZhbHVlcyk7XG4gICAgICAgIH07XG4gICAgICAgIGNvbnRhaW5lci5iaW5kRmFjdG9yeShjb25maWcudG9rZW4sIGZhY3RvcnksIG9wdGlvbnMpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBBcHBseSB0eXBlIHJlZ2lzdHJhdGlvbiAoY2xhc3MgY29uc3RydWN0b3IpXG4gICAgICogQGludGVybmFsXG4gICAgICovXG4gICAgYXBwbHlUeXBlUmVnaXN0cmF0aW9uKGNvbnRhaW5lciwgY29uZmlnLCBvcHRpb25zKSB7XG4gICAgICAgIGNvbnN0IHsgaGFzRGVwZW5kZW5jaWVzIH0gPSB0aGlzLmFuYWx5emVDb25zdHJ1Y3Rvcihjb25maWcuY29uc3RydWN0b3IpO1xuICAgICAgICAvLyBGYXN0IHBhdGg6IE5vIGRlcGVuZGVuY2llcyBhbmQgbm8gc3BlY2lhbCBjb25maWdcbiAgICAgICAgaWYgKCFoYXNEZXBlbmRlbmNpZXMgJiYgIWNvbmZpZy5hdXRvd2lyZU9wdGlvbnMgJiYgIWNvbmZpZy5wYXJhbWV0ZXJWYWx1ZXMpIHtcbiAgICAgICAgICAgIHRoaXMuY3JlYXRlT3B0aW1pemVkRmFjdG9yeShjb250YWluZXIsIGNvbmZpZywgb3B0aW9ucyk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgLy8gQXV0b1dpcmUgcGF0aFxuICAgICAgICBpZiAoY29uZmlnLmF1dG93aXJlT3B0aW9ucykge1xuICAgICAgICAgICAgdGhpcy5jcmVhdGVBdXRvV2lyZUZhY3RvcnkoY29udGFpbmVyLCBjb25maWcsIG9wdGlvbnMpO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIC8vIHdpdGhQYXJhbWV0ZXJzIHBhdGhcbiAgICAgICAgaWYgKGNvbmZpZy5wYXJhbWV0ZXJWYWx1ZXMpIHtcbiAgICAgICAgICAgIHRoaXMuY3JlYXRlUGFyYW1ldGVyRmFjdG9yeShjb250YWluZXIsIGNvbmZpZywgb3B0aW9ucyk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgLy8gRXJyb3I6IENvbnN0cnVjdG9yIGhhcyBkZXBlbmRlbmNpZXMgYnV0IG5vIGNvbmZpZ1xuICAgICAgICBpZiAoaGFzRGVwZW5kZW5jaWVzKSB7XG4gICAgICAgICAgICBjb25zdCBjbGFzc05hbWUgPSBjb25maWcuY29uc3RydWN0b3IubmFtZSB8fCAnVW5uYW1lZENsYXNzJztcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgU2VydmljZSBcIiR7Y2xhc3NOYW1lfVwiIGhhcyBjb25zdHJ1Y3RvciBkZXBlbmRlbmNpZXMgYnV0IG5vIGF1dG93aXJpbmcgY29uZmlndXJhdGlvbi5cXG5cXG5gICtcbiAgICAgICAgICAgICAgICBgU29sdXRpb25zOlxcbmAgK1xuICAgICAgICAgICAgICAgIGAgIDEuIFx1MkI1MCBVc2UgdGhlIE5vdmFESSB0cmFuc2Zvcm1lciAocmVjb21tZW5kZWQpOlxcbmAgK1xuICAgICAgICAgICAgICAgIGAgICAgIC0gQWRkIFwiQG5vdmFkaS9jb3JlL3VucGx1Z2luXCIgdG8geW91ciBidWlsZCBjb25maWdcXG5gICtcbiAgICAgICAgICAgICAgICBgICAgICAtIFRyYW5zZm9ybWVyIGF1dG9tYXRpY2FsbHkgZ2VuZXJhdGVzIC5hdXRvV2lyZSgpIGZvciBhbGwgZGVwZW5kZW5jaWVzXFxuXFxuYCArXG4gICAgICAgICAgICAgICAgYCAgMi4gQWRkIG1hbnVhbCBhdXRvd2lyaW5nOlxcbmAgK1xuICAgICAgICAgICAgICAgIGAgICAgIC5hdXRvV2lyZSh7IG1hcDogeyAvKiBwYXJhbTogcmVzb2x2ZXIgKi8gfSB9KVxcblxcbmAgK1xuICAgICAgICAgICAgICAgIGAgIDMuIFVzZSBhIGZhY3RvcnkgZnVuY3Rpb246XFxuYCArXG4gICAgICAgICAgICAgICAgYCAgICAgLnJlZ2lzdGVyKChjKSA9PiBuZXcgJHtjbGFzc05hbWV9KC4uLikpXFxuXFxuYCArXG4gICAgICAgICAgICAgICAgYFNlZSBkb2NzOiBodHRwczovL2dpdGh1Yi5jb20vamFudXMwMDcvTm92YURJI2F1dG93aXJlYCk7XG4gICAgICAgIH1cbiAgICAgICAgLy8gTm8gZGVwZW5kZW5jaWVzIC0gY3JlYXRlIHNpbXBsZSBmYWN0b3J5XG4gICAgICAgIGNvbnN0IGZhY3RvcnkgPSAoKSA9PiBuZXcgY29uZmlnLmNvbnN0cnVjdG9yKCk7XG4gICAgICAgIGNvbnRhaW5lci5iaW5kRmFjdG9yeShjb25maWcudG9rZW4sIGZhY3RvcnksIG9wdGlvbnMpO1xuICAgIH1cbiAgICBhcHBseVJlZ2lzdHJhdGlvbihjb250YWluZXIsIGNvbmZpZykge1xuICAgICAgICBjb25zdCBvcHRpb25zID0geyBsaWZldGltZTogY29uZmlnLmxpZmV0aW1lIH07XG4gICAgICAgIHN3aXRjaCAoY29uZmlnLnR5cGUpIHtcbiAgICAgICAgICAgIGNhc2UgJ2luc3RhbmNlJzpcbiAgICAgICAgICAgICAgICBjb250YWluZXIuYmluZFZhbHVlKGNvbmZpZy50b2tlbiwgY29uZmlnLnZhbHVlKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgJ2ZhY3RvcnknOlxuICAgICAgICAgICAgICAgIGNvbnRhaW5lci5iaW5kRmFjdG9yeShjb25maWcudG9rZW4sIGNvbmZpZy5mYWN0b3J5LCBvcHRpb25zKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgJ3R5cGUnOlxuICAgICAgICAgICAgICAgIHRoaXMuYXBwbHlUeXBlUmVnaXN0cmF0aW9uKGNvbnRhaW5lciwgY29uZmlnLCBvcHRpb25zKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgIH1cbn1cbiIsICIvKipcbiAqIENvcmUgZGVwZW5kZW5jeSBpbmplY3Rpb24gY29udGFpbmVyIGZvciBOb3ZhRElcbiAqL1xuaW1wb3J0IHsgVG9rZW4gfSBmcm9tICcuL3Rva2VuLmpzJztcbmltcG9ydCB7IEJpbmRpbmdOb3RGb3VuZEVycm9yLCBDaXJjdWxhckRlcGVuZGVuY3lFcnJvciB9IGZyb20gJy4vZXJyb3JzLmpzJztcbmltcG9ydCB7IEJ1aWxkZXIgfSBmcm9tICcuL2J1aWxkZXIuanMnO1xuZnVuY3Rpb24gaXNEaXNwb3NhYmxlKG9iaikge1xuICAgIHJldHVybiBvYmogJiYgdHlwZW9mIG9iai5kaXNwb3NlID09PSAnZnVuY3Rpb24nO1xufVxuLyoqXG4gKiBSZXNvbHV0aW9uIGNvbnRleHQgdHJhY2tzIHRoZSBjdXJyZW50IGRlcGVuZGVuY3kgcmVzb2x1dGlvbiBwYXRoXG4gKiBmb3IgY2lyY3VsYXIgZGVwZW5kZW5jeSBkZXRlY3Rpb24gYW5kIHBlci1yZXF1ZXN0IHNjb3BpbmdcbiAqL1xuY2xhc3MgUmVzb2x1dGlvbkNvbnRleHQge1xuICAgIGNvbnN0cnVjdG9yKCkge1xuICAgICAgICB0aGlzLnJlc29sdmluZ1N0YWNrID0gbmV3IFNldCgpO1xuICAgICAgICB0aGlzLnBlclJlcXVlc3RDYWNoZSA9IG5ldyBNYXAoKTtcbiAgICB9XG4gICAgaXNSZXNvbHZpbmcodG9rZW4pIHtcbiAgICAgICAgcmV0dXJuIHRoaXMucmVzb2x2aW5nU3RhY2suaGFzKHRva2VuKTtcbiAgICB9XG4gICAgZW50ZXJSZXNvbHZlKHRva2VuKSB7XG4gICAgICAgIHRoaXMucmVzb2x2aW5nU3RhY2suYWRkKHRva2VuKTtcbiAgICAgICAgLy8gUGVyZm9ybWFuY2U6IERvbid0IGJ1aWxkIHBhdGggdW5sZXNzIHdlIG5lZWQgaXQgKG9ubHkgdXNlZCBpbiBlcnJvciBtZXNzYWdlcylcbiAgICAgICAgLy8gVGhpcyBhdm9pZHMgZXhwZW5zaXZlIHRva2VuLnRvU3RyaW5nKCkgY2FsbHMgb24gZXZlcnkgcmVzb2x2ZVxuICAgIH1cbiAgICBleGl0UmVzb2x2ZSh0b2tlbikge1xuICAgICAgICB0aGlzLnJlc29sdmluZ1N0YWNrLmRlbGV0ZSh0b2tlbik7XG4gICAgICAgIC8vIFBlcmZvcm1hbmNlOiBDbGVhciBsYXp5IHBhdGggY2FjaGUgd2hlbiBleGl0aW5nXG4gICAgICAgIHRoaXMucGF0aCA9IHVuZGVmaW5lZDtcbiAgICB9XG4gICAgZ2V0UGF0aCgpIHtcbiAgICAgICAgLy8gUGVyZm9ybWFuY2U6IEJ1aWxkIHBhdGggb24tZGVtYW5kIG9ubHkgd2hlbiBuZWVkZWQgKHR5cGljYWxseSBmb3IgZXJyb3IgbWVzc2FnZXMpXG4gICAgICAgIGlmICghdGhpcy5wYXRoKSB7XG4gICAgICAgICAgICB0aGlzLnBhdGggPSBBcnJheS5mcm9tKHRoaXMucmVzb2x2aW5nU3RhY2spLm1hcCh0ID0+IHQudG9TdHJpbmcoKSk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIFsuLi50aGlzLnBhdGhdO1xuICAgIH1cbiAgICBjYWNoZVBlclJlcXVlc3QodG9rZW4sIGluc3RhbmNlKSB7XG4gICAgICAgIHRoaXMucGVyUmVxdWVzdENhY2hlLnNldCh0b2tlbiwgaW5zdGFuY2UpO1xuICAgIH1cbiAgICBnZXRQZXJSZXF1ZXN0KHRva2VuKSB7XG4gICAgICAgIHJldHVybiB0aGlzLnBlclJlcXVlc3RDYWNoZS5nZXQodG9rZW4pO1xuICAgIH1cbiAgICBoYXNQZXJSZXF1ZXN0KHRva2VuKSB7XG4gICAgICAgIHJldHVybiB0aGlzLnBlclJlcXVlc3RDYWNoZS5oYXModG9rZW4pO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBSZXNldCBjb250ZXh0IGZvciByZXVzZSBpbiBvYmplY3QgcG9vbFxuICAgICAqIFBlcmZvcm1hbmNlOiBSZXVzaW5nIGNvbnRleHRzIGF2b2lkcyBoZWFwIGFsbG9jYXRpb25zXG4gICAgICovXG4gICAgcmVzZXQoKSB7XG4gICAgICAgIHRoaXMucmVzb2x2aW5nU3RhY2suY2xlYXIoKTtcbiAgICAgICAgdGhpcy5wZXJSZXF1ZXN0Q2FjaGUuY2xlYXIoKTtcbiAgICAgICAgdGhpcy5wYXRoID0gdW5kZWZpbmVkO1xuICAgIH1cbn1cbi8qKlxuICogT2JqZWN0IHBvb2wgZm9yIFJlc29sdXRpb25Db250ZXh0IGluc3RhbmNlc1xuICogUGVyZm9ybWFuY2U6IFJldXNpbmcgY29udGV4dHMgcmVkdWNlcyBoZWFwIGFsbG9jYXRpb25zIGFuZCBHQyBwcmVzc3VyZVxuICovXG5jbGFzcyBSZXNvbHV0aW9uQ29udGV4dFBvb2wge1xuICAgIGNvbnN0cnVjdG9yKCkge1xuICAgICAgICB0aGlzLnBvb2wgPSBbXTtcbiAgICAgICAgdGhpcy5tYXhTaXplID0gMTA7XG4gICAgfVxuICAgIGFjcXVpcmUoKSB7XG4gICAgICAgIGNvbnN0IGNvbnRleHQgPSB0aGlzLnBvb2wucG9wKCk7XG4gICAgICAgIGlmIChjb250ZXh0KSB7XG4gICAgICAgICAgICAvLyBSZXNldCBleGlzdGluZyBjb250ZXh0IGZvciByZXVzZVxuICAgICAgICAgICAgY29udGV4dC5yZXNldCgpO1xuICAgICAgICAgICAgcmV0dXJuIGNvbnRleHQ7XG4gICAgICAgIH1cbiAgICAgICAgLy8gQ3JlYXRlIG5ldyBpZiBwb29sIGVtcHR5XG4gICAgICAgIHJldHVybiBuZXcgUmVzb2x1dGlvbkNvbnRleHQoKTtcbiAgICB9XG4gICAgcmVsZWFzZShjb250ZXh0KSB7XG4gICAgICAgIGlmICh0aGlzLnBvb2wubGVuZ3RoIDwgdGhpcy5tYXhTaXplKSB7XG4gICAgICAgICAgICB0aGlzLnBvb2wucHVzaChjb250ZXh0KTtcbiAgICAgICAgfVxuICAgICAgICAvLyBPdGhlcndpc2UgbGV0IGl0IGJlIEdDJ2RcbiAgICB9XG59XG4vKipcbiAqIERlcGVuZGVuY3kgSW5qZWN0aW9uIENvbnRhaW5lclxuICpcbiAqIE1hbmFnZXMgcmVnaXN0cmF0aW9uIGFuZCByZXNvbHV0aW9uIG9mIGRlcGVuZGVuY2llcyB3aXRoIHN1cHBvcnQgZm9yOlxuICogLSBNdWx0aXBsZSBiaW5kaW5nIHR5cGVzICh2YWx1ZSwgZmFjdG9yeSwgY2xhc3MpXG4gKiAtIExpZmV0aW1lIG1hbmFnZW1lbnQgKHNpbmdsZXRvbiwgdHJhbnNpZW50LCBwZXItcmVxdWVzdClcbiAqIC0gQ2hpbGQgY29udGFpbmVycyB3aXRoIGluaGVyaXRhbmNlXG4gKiAtIENpcmN1bGFyIGRlcGVuZGVuY3kgZGV0ZWN0aW9uXG4gKiAtIEF1dG9tYXRpYyBkaXNwb3NhbFxuICovXG5leHBvcnQgY2xhc3MgQ29udGFpbmVyIHtcbiAgICBjb25zdHJ1Y3RvcihwYXJlbnQpIHtcbiAgICAgICAgdGhpcy5iaW5kaW5ncyA9IG5ldyBNYXAoKTtcbiAgICAgICAgdGhpcy5zaW5nbGV0b25DYWNoZSA9IG5ldyBNYXAoKTtcbiAgICAgICAgdGhpcy5zaW5nbGV0b25PcmRlciA9IFtdO1xuICAgICAgICB0aGlzLmludGVyZmFjZVJlZ2lzdHJ5ID0gbmV3IE1hcCgpO1xuICAgICAgICB0aGlzLmludGVyZmFjZVRva2VuQ2FjaGUgPSBuZXcgTWFwKCk7IC8vIFBlcmZvcm1hbmNlOiBDYWNoZSBmb3IgcmVzb2x2ZVR5cGUoKSBsb29rdXBzXG4gICAgICAgIHRoaXMuZmFzdFRyYW5zaWVudENhY2hlID0gbmV3IE1hcCgpOyAvLyBQZXJmb3JtYW5jZTogRmFzdCBwYXRoIGZvciBzaW1wbGUgdHJhbnNpZW50c1xuICAgICAgICB0aGlzLnVsdHJhRmFzdFNpbmdsZXRvbkNhY2hlID0gbmV3IE1hcCgpOyAvLyBQZXJmb3JtYW5jZTogVWx0cmEtZmFzdCBzaW5nbGV0b24tb25seSBjYWNoZVxuICAgICAgICB0aGlzLnBhcmVudCA9IHBhcmVudDtcbiAgICB9XG4gICAgLyoqXG4gICAgICogQmluZCBhIHByZS1jcmVhdGVkIHZhbHVlIHRvIGEgdG9rZW5cbiAgICAgKi9cbiAgICBiaW5kVmFsdWUodG9rZW4sIHZhbHVlKSB7XG4gICAgICAgIHRoaXMuYmluZGluZ3Muc2V0KHRva2VuLCB7XG4gICAgICAgICAgICB0eXBlOiAndmFsdWUnLFxuICAgICAgICAgICAgbGlmZXRpbWU6ICdzaW5nbGV0b24nLFxuICAgICAgICAgICAgdmFsdWUsXG4gICAgICAgICAgICBjb25zdHJ1Y3RvcjogdW5kZWZpbmVkXG4gICAgICAgIH0pO1xuICAgICAgICB0aGlzLmludmFsaWRhdGVCaW5kaW5nQ2FjaGUoKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogQmluZCBhIGZhY3RvcnkgZnVuY3Rpb24gdG8gYSB0b2tlblxuICAgICAqL1xuICAgIGJpbmRGYWN0b3J5KHRva2VuLCBmYWN0b3J5LCBvcHRpb25zKSB7XG4gICAgICAgIHRoaXMuYmluZGluZ3Muc2V0KHRva2VuLCB7XG4gICAgICAgICAgICB0eXBlOiAnZmFjdG9yeScsXG4gICAgICAgICAgICBsaWZldGltZTogb3B0aW9ucz8ubGlmZXRpbWUgfHwgJ3RyYW5zaWVudCcsXG4gICAgICAgICAgICBmYWN0b3J5LFxuICAgICAgICAgICAgZGVwZW5kZW5jaWVzOiBvcHRpb25zPy5kZXBlbmRlbmNpZXMsXG4gICAgICAgICAgICBjb25zdHJ1Y3RvcjogdW5kZWZpbmVkXG4gICAgICAgIH0pO1xuICAgICAgICB0aGlzLmludmFsaWRhdGVCaW5kaW5nQ2FjaGUoKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogQmluZCBhIGNsYXNzIGNvbnN0cnVjdG9yIHRvIGEgdG9rZW5cbiAgICAgKi9cbiAgICBiaW5kQ2xhc3ModG9rZW4sIGNvbnN0cnVjdG9yLCBvcHRpb25zKSB7XG4gICAgICAgIGNvbnN0IGJpbmRpbmcgPSB7XG4gICAgICAgICAgICB0eXBlOiAnY2xhc3MnLFxuICAgICAgICAgICAgbGlmZXRpbWU6IG9wdGlvbnM/LmxpZmV0aW1lIHx8ICd0cmFuc2llbnQnLFxuICAgICAgICAgICAgY29uc3RydWN0b3IsXG4gICAgICAgICAgICBkZXBlbmRlbmNpZXM6IG9wdGlvbnM/LmRlcGVuZGVuY2llc1xuICAgICAgICB9O1xuICAgICAgICB0aGlzLmJpbmRpbmdzLnNldCh0b2tlbiwgYmluZGluZyk7XG4gICAgICAgIHRoaXMuaW52YWxpZGF0ZUJpbmRpbmdDYWNoZSgpO1xuICAgICAgICAvLyBQZXJmb3JtYW5jZTogUHJlLWNvbXBpbGUgZmFzdCB0cmFuc2llbnQgZmFjdG9yeSBmb3IgemVyby1kZXBlbmRlbmN5IGNsYXNzZXNcbiAgICAgICAgaWYgKGJpbmRpbmcubGlmZXRpbWUgPT09ICd0cmFuc2llbnQnICYmICghYmluZGluZy5kZXBlbmRlbmNpZXMgfHwgYmluZGluZy5kZXBlbmRlbmNpZXMubGVuZ3RoID09PSAwKSkge1xuICAgICAgICAgICAgdGhpcy5mYXN0VHJhbnNpZW50Q2FjaGUuc2V0KHRva2VuLCAoKSA9PiBuZXcgY29uc3RydWN0b3IoKSk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgLyoqXG4gICAgICogUmVzb2x2ZSBhIGRlcGVuZGVuY3kgc3luY2hyb25vdXNseVxuICAgICAqIFBlcmZvcm1hbmNlIG9wdGltaXplZCB3aXRoIG11bHRpcGxlIGZhc3QgcGF0aHNcbiAgICAgKi9cbiAgICByZXNvbHZlKHRva2VuKSB7XG4gICAgICAgIC8vIFRyeSBhbGwgY2FjaGUgbGV2ZWxzIGZpcnN0ICh1bHRyYS1mYXN0LCBzaW5nbGV0b24sIGZhc3QgdHJhbnNpZW50KVxuICAgICAgICBjb25zdCBjYWNoZWQgPSB0aGlzLnRyeUdldEZyb21DYWNoZXModG9rZW4pO1xuICAgICAgICBpZiAoY2FjaGVkICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIHJldHVybiBjYWNoZWQ7XG4gICAgICAgIH1cbiAgICAgICAgLy8gSWYgd2UncmUgYWxyZWFkeSByZXNvbHZpbmcgKGNhbGxlZCBmcm9tIHdpdGhpbiBhIGZhY3RvcnkpLCByZXVzZSB0aGUgY29udGV4dFxuICAgICAgICBpZiAodGhpcy5jdXJyZW50Q29udGV4dCkge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMucmVzb2x2ZVdpdGhDb250ZXh0KHRva2VuLCB0aGlzLmN1cnJlbnRDb250ZXh0KTtcbiAgICAgICAgfVxuICAgICAgICAvLyBDb21wbGV4IHJlc29sdXRpb24gd2l0aCBwb29sZWQgY29udGV4dFxuICAgICAgICBjb25zdCBjb250ZXh0ID0gQ29udGFpbmVyLmNvbnRleHRQb29sLmFjcXVpcmUoKTtcbiAgICAgICAgdGhpcy5jdXJyZW50Q29udGV4dCA9IGNvbnRleHQ7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5yZXNvbHZlV2l0aENvbnRleHQodG9rZW4sIGNvbnRleHQpO1xuICAgICAgICB9XG4gICAgICAgIGZpbmFsbHkge1xuICAgICAgICAgICAgdGhpcy5jdXJyZW50Q29udGV4dCA9IHVuZGVmaW5lZDtcbiAgICAgICAgICAgIENvbnRhaW5lci5jb250ZXh0UG9vbC5yZWxlYXNlKGNvbnRleHQpO1xuICAgICAgICB9XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFNQRUNJQUxJWkVEOiBVbHRyYS1mYXN0IHNpbmdsZXRvbiByZXNvbHZlIChubyBzYWZldHkgY2hlY2tzKVxuICAgICAqIFVzZSBPTkxZIHdoZW4geW91J3JlIDEwMCUgc3VyZSB0aGUgdG9rZW4gaXMgYSByZWdpc3RlcmVkIHNpbmdsZXRvblxuICAgICAqIEBpbnRlcm5hbCBGb3IgcGVyZm9ybWFuY2UtY3JpdGljYWwgcGF0aHMgb25seVxuICAgICAqL1xuICAgIHJlc29sdmVTaW5nbGV0b25VbnNhZmUodG9rZW4pIHtcbiAgICAgICAgLy8gRGlyZWN0IHJldHVybiwgbm8gY2hlY2tzIC0gbWF4aW11bSBzcGVlZFxuICAgICAgICByZXR1cm4gdGhpcy51bHRyYUZhc3RTaW5nbGV0b25DYWNoZS5nZXQodG9rZW4pID8/IHRoaXMuc2luZ2xldG9uQ2FjaGUuZ2V0KHRva2VuKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogU1BFQ0lBTElaRUQ6IEZhc3QgdHJhbnNpZW50IHJlc29sdmUgZm9yIHplcm8tZGVwZW5kZW5jeSBjbGFzc2VzXG4gICAgICogU2tpcHMgYWxsIGNvbnRleHQgY3JlYXRpb24gYW5kIGNpcmN1bGFyIGRlcGVuZGVuY3kgY2hlY2tzXG4gICAgICogQGludGVybmFsIEZvciBwZXJmb3JtYW5jZS1jcml0aWNhbCBwYXRocyBvbmx5XG4gICAgICovXG4gICAgcmVzb2x2ZVRyYW5zaWVudFNpbXBsZSh0b2tlbikge1xuICAgICAgICBjb25zdCBmYWN0b3J5ID0gdGhpcy5mYXN0VHJhbnNpZW50Q2FjaGUuZ2V0KHRva2VuKTtcbiAgICAgICAgaWYgKGZhY3RvcnkpIHtcbiAgICAgICAgICAgIHJldHVybiBmYWN0b3J5KCk7XG4gICAgICAgIH1cbiAgICAgICAgLy8gRmFsbGJhY2sgdG8gcmVndWxhciByZXNvbHZlIGlmIG5vdCBpbiBmYXN0IGNhY2hlXG4gICAgICAgIHJldHVybiB0aGlzLnJlc29sdmUodG9rZW4pO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBTUEVDSUFMSVpFRDogQmF0Y2ggcmVzb2x2ZSBtdWx0aXBsZSBkZXBlbmRlbmNpZXMgYXQgb25jZVxuICAgICAqIE1vcmUgZWZmaWNpZW50IHRoYW4gbXVsdGlwbGUgaW5kaXZpZHVhbCByZXNvbHZlc1xuICAgICAqL1xuICAgIHJlc29sdmVCYXRjaCh0b2tlbnMpIHtcbiAgICAgICAgLy8gUmV1c2Ugc2luZ2xlIGNvbnRleHQgZm9yIGFsbCByZXNvbHV0aW9uc1xuICAgICAgICBjb25zdCB3YXNSZXNvbHZpbmcgPSAhIXRoaXMuY3VycmVudENvbnRleHQ7XG4gICAgICAgIGNvbnN0IGNvbnRleHQgPSB0aGlzLmN1cnJlbnRDb250ZXh0IHx8IENvbnRhaW5lci5jb250ZXh0UG9vbC5hY3F1aXJlKCk7XG4gICAgICAgIGlmICghd2FzUmVzb2x2aW5nKSB7XG4gICAgICAgICAgICB0aGlzLmN1cnJlbnRDb250ZXh0ID0gY29udGV4dDtcbiAgICAgICAgfVxuICAgICAgICB0cnkge1xuICAgICAgICAgICAgY29uc3QgcmVzdWx0cyA9IHRva2Vucy5tYXAodG9rZW4gPT4ge1xuICAgICAgICAgICAgICAgIC8vIFRyeSBhbGwgY2FjaGUgbGV2ZWxzIGZpcnN0XG4gICAgICAgICAgICAgICAgY29uc3QgY2FjaGVkID0gdGhpcy50cnlHZXRGcm9tQ2FjaGVzKHRva2VuKTtcbiAgICAgICAgICAgICAgICBpZiAoY2FjaGVkICE9PSB1bmRlZmluZWQpXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBjYWNoZWQ7XG4gICAgICAgICAgICAgICAgLy8gRnVsbCByZXNvbHZlIHdpdGggc2hhcmVkIGNvbnRleHRcbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5yZXNvbHZlV2l0aENvbnRleHQodG9rZW4sIGNvbnRleHQpO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICByZXR1cm4gcmVzdWx0cztcbiAgICAgICAgfVxuICAgICAgICBmaW5hbGx5IHtcbiAgICAgICAgICAgIGlmICghd2FzUmVzb2x2aW5nKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5jdXJyZW50Q29udGV4dCA9IHVuZGVmaW5lZDtcbiAgICAgICAgICAgICAgICBDb250YWluZXIuY29udGV4dFBvb2wucmVsZWFzZShjb250ZXh0KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cbiAgICAvKipcbiAgICAgKiBSZXNvbHZlIGEgZGVwZW5kZW5jeSBhc3luY2hyb25vdXNseSAoc3VwcG9ydHMgYXN5bmMgZmFjdG9yaWVzKVxuICAgICAqL1xuICAgIGFzeW5jIHJlc29sdmVBc3luYyh0b2tlbikge1xuICAgICAgICAvLyBJZiB3ZSdyZSBhbHJlYWR5IHJlc29sdmluZyAoY2FsbGVkIGZyb20gd2l0aGluIGEgZmFjdG9yeSksIHJldXNlIHRoZSBjb250ZXh0XG4gICAgICAgIGlmICh0aGlzLmN1cnJlbnRDb250ZXh0KSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5yZXNvbHZlQXN5bmNXaXRoQ29udGV4dCh0b2tlbiwgdGhpcy5jdXJyZW50Q29udGV4dCk7XG4gICAgICAgIH1cbiAgICAgICAgLy8gTmV3IHRvcC1sZXZlbCByZXNvbHZlXG4gICAgICAgIC8vIFBlcmZvcm1hbmNlOiBVc2UgcG9vbGVkIGNvbnRleHQgdG8gYXZvaWQgaGVhcCBhbGxvY2F0aW9uXG4gICAgICAgIGNvbnN0IGNvbnRleHQgPSBDb250YWluZXIuY29udGV4dFBvb2wuYWNxdWlyZSgpO1xuICAgICAgICB0aGlzLmN1cnJlbnRDb250ZXh0ID0gY29udGV4dDtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIHJldHVybiBhd2FpdCB0aGlzLnJlc29sdmVBc3luY1dpdGhDb250ZXh0KHRva2VuLCBjb250ZXh0KTtcbiAgICAgICAgfVxuICAgICAgICBmaW5hbGx5IHtcbiAgICAgICAgICAgIHRoaXMuY3VycmVudENvbnRleHQgPSB1bmRlZmluZWQ7XG4gICAgICAgICAgICBDb250YWluZXIuY29udGV4dFBvb2wucmVsZWFzZShjb250ZXh0KTsgLy8gUmV0dXJuIHRvIHBvb2wgZm9yIHJldXNlXG4gICAgICAgIH1cbiAgICB9XG4gICAgLyoqXG4gICAgICogVHJ5IHRvIGdldCBpbnN0YW5jZSBmcm9tIGFsbCBjYWNoZSBsZXZlbHNcbiAgICAgKiBSZXR1cm5zIHVuZGVmaW5lZCBpZiBub3QgY2FjaGVkXG4gICAgICogQGludGVybmFsXG4gICAgICovXG4gICAgdHJ5R2V0RnJvbUNhY2hlcyh0b2tlbikge1xuICAgICAgICAvLyBMZXZlbCAxOiBVbHRyYS1mYXN0IHNpbmdsZXRvbiBjYWNoZSAoemVybyBvdmVyaGVhZClcbiAgICAgICAgY29uc3QgdWx0cmFGYXN0ID0gdGhpcy51bHRyYUZhc3RTaW5nbGV0b25DYWNoZS5nZXQodG9rZW4pO1xuICAgICAgICBpZiAodWx0cmFGYXN0ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIHJldHVybiB1bHRyYUZhc3Q7XG4gICAgICAgIH1cbiAgICAgICAgLy8gTGV2ZWwgMjogUmVndWxhciBzaW5nbGV0b24gY2FjaGVcbiAgICAgICAgaWYgKHRoaXMuc2luZ2xldG9uQ2FjaGUuaGFzKHRva2VuKSkge1xuICAgICAgICAgICAgY29uc3QgY2FjaGVkID0gdGhpcy5zaW5nbGV0b25DYWNoZS5nZXQodG9rZW4pO1xuICAgICAgICAgICAgLy8gUHJvbW90ZSB0byB1bHRyYS1mYXN0IGNhY2hlIGZvciBuZXh0IHRpbWVcbiAgICAgICAgICAgIHRoaXMudWx0cmFGYXN0U2luZ2xldG9uQ2FjaGUuc2V0KHRva2VuLCBjYWNoZWQpO1xuICAgICAgICAgICAgcmV0dXJuIGNhY2hlZDtcbiAgICAgICAgfVxuICAgICAgICAvLyBMZXZlbCAzOiBGYXN0IHRyYW5zaWVudCBjYWNoZSAobm8gZGVwZW5kZW5jaWVzKVxuICAgICAgICBjb25zdCBmYXN0RmFjdG9yeSA9IHRoaXMuZmFzdFRyYW5zaWVudENhY2hlLmdldCh0b2tlbik7XG4gICAgICAgIGlmIChmYXN0RmFjdG9yeSkge1xuICAgICAgICAgICAgcmV0dXJuIGZhc3RGYWN0b3J5KCk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICB9XG4gICAgLyoqXG4gICAgICogQ2FjaGUgaW5zdGFuY2UgYmFzZWQgb24gbGlmZXRpbWUgc3RyYXRlZ3lcbiAgICAgKiBAaW50ZXJuYWxcbiAgICAgKi9cbiAgICBjYWNoZUluc3RhbmNlKHRva2VuLCBpbnN0YW5jZSwgbGlmZXRpbWUsIGNvbnRleHQpIHtcbiAgICAgICAgaWYgKGxpZmV0aW1lID09PSAnc2luZ2xldG9uJykge1xuICAgICAgICAgICAgdGhpcy5zaW5nbGV0b25DYWNoZS5zZXQodG9rZW4sIGluc3RhbmNlKTtcbiAgICAgICAgICAgIHRoaXMuc2luZ2xldG9uT3JkZXIucHVzaCh0b2tlbik7XG4gICAgICAgICAgICAvLyBBbHNvIGFkZCB0byB1bHRyYS1mYXN0IGNhY2hlXG4gICAgICAgICAgICB0aGlzLnVsdHJhRmFzdFNpbmdsZXRvbkNhY2hlLnNldCh0b2tlbiwgaW5zdGFuY2UpO1xuICAgICAgICB9XG4gICAgICAgIGVsc2UgaWYgKGxpZmV0aW1lID09PSAncGVyLXJlcXVlc3QnICYmIGNvbnRleHQpIHtcbiAgICAgICAgICAgIGNvbnRleHQuY2FjaGVQZXJSZXF1ZXN0KHRva2VuLCBpbnN0YW5jZSk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgLyoqXG4gICAgICogVmFsaWRhdGUgYW5kIGdldCBiaW5kaW5nIHdpdGggY2lyY3VsYXIgZGVwZW5kZW5jeSBjaGVja1xuICAgICAqIFJldHVybnMgYmluZGluZyBvciB0aHJvd3MgZXJyb3JcbiAgICAgKiBAaW50ZXJuYWxcbiAgICAgKi9cbiAgICB2YWxpZGF0ZUFuZEdldEJpbmRpbmcodG9rZW4sIGNvbnRleHQpIHtcbiAgICAgICAgLy8gQ2hlY2sgY2lyY3VsYXIgZGVwZW5kZW5jeVxuICAgICAgICBpZiAoY29udGV4dC5pc1Jlc29sdmluZyh0b2tlbikpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBDaXJjdWxhckRlcGVuZGVuY3lFcnJvcihbLi4uY29udGV4dC5nZXRQYXRoKCksIHRva2VuLnRvU3RyaW5nKCldKTtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBiaW5kaW5nID0gdGhpcy5nZXRCaW5kaW5nKHRva2VuKTtcbiAgICAgICAgaWYgKCFiaW5kaW5nKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgQmluZGluZ05vdEZvdW5kRXJyb3IodG9rZW4udG9TdHJpbmcoKSwgY29udGV4dC5nZXRQYXRoKCkpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBiaW5kaW5nO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBJbnN0YW50aWF0ZSBmcm9tIGJpbmRpbmcgc3luY2hyb25vdXNseVxuICAgICAqIEBpbnRlcm5hbFxuICAgICAqL1xuICAgIGluc3RhbnRpYXRlQmluZGluZ1N5bmMoYmluZGluZywgdG9rZW4sIGNvbnRleHQpIHtcbiAgICAgICAgc3dpdGNoIChiaW5kaW5nLnR5cGUpIHtcbiAgICAgICAgICAgIGNhc2UgJ3ZhbHVlJzpcbiAgICAgICAgICAgICAgICByZXR1cm4gYmluZGluZy52YWx1ZTtcbiAgICAgICAgICAgIGNhc2UgJ2ZhY3RvcnknOlxuICAgICAgICAgICAgICAgIGNvbnN0IHJlc3VsdCA9IGJpbmRpbmcuZmFjdG9yeSh0aGlzKTtcbiAgICAgICAgICAgICAgICBpZiAocmVzdWx0IGluc3RhbmNlb2YgUHJvbWlzZSkge1xuICAgICAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEFzeW5jIGZhY3RvcnkgZGV0ZWN0ZWQgZm9yICR7dG9rZW4udG9TdHJpbmcoKX0uIFVzZSByZXNvbHZlQXN5bmMoKSBpbnN0ZWFkLmApO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICByZXR1cm4gcmVzdWx0O1xuICAgICAgICAgICAgY2FzZSAnY2xhc3MnOlxuICAgICAgICAgICAgICAgIGNvbnN0IGRlcHMgPSBiaW5kaW5nLmRlcGVuZGVuY2llcyB8fCBbXTtcbiAgICAgICAgICAgICAgICBjb25zdCByZXNvbHZlZERlcHMgPSBkZXBzLm1hcChkZXAgPT4gdGhpcy5yZXNvbHZlV2l0aENvbnRleHQoZGVwLCBjb250ZXh0KSk7XG4gICAgICAgICAgICAgICAgcmV0dXJuIG5ldyBiaW5kaW5nLmNvbnN0cnVjdG9yKC4uLnJlc29sdmVkRGVwcyk7XG4gICAgICAgICAgICBjYXNlICdpbmxpbmUtY2xhc3MnOlxuICAgICAgICAgICAgICAgIHJldHVybiBuZXcgYmluZGluZy5jb25zdHJ1Y3RvcigpO1xuICAgICAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYFVua25vd24gYmluZGluZyB0eXBlOiAke2JpbmRpbmcudHlwZX1gKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICAvKipcbiAgICAgKiBJbnN0YW50aWF0ZSBmcm9tIGJpbmRpbmcgYXN5bmNocm9ub3VzbHlcbiAgICAgKiBAaW50ZXJuYWxcbiAgICAgKi9cbiAgICBhc3luYyBpbnN0YW50aWF0ZUJpbmRpbmdBc3luYyhiaW5kaW5nLCBjb250ZXh0KSB7XG4gICAgICAgIHN3aXRjaCAoYmluZGluZy50eXBlKSB7XG4gICAgICAgICAgICBjYXNlICd2YWx1ZSc6XG4gICAgICAgICAgICAgICAgcmV0dXJuIGJpbmRpbmcudmFsdWU7XG4gICAgICAgICAgICBjYXNlICdmYWN0b3J5JzpcbiAgICAgICAgICAgICAgICByZXR1cm4gYXdhaXQgUHJvbWlzZS5yZXNvbHZlKGJpbmRpbmcuZmFjdG9yeSh0aGlzKSk7XG4gICAgICAgICAgICBjYXNlICdjbGFzcyc6XG4gICAgICAgICAgICAgICAgY29uc3QgZGVwcyA9IGJpbmRpbmcuZGVwZW5kZW5jaWVzIHx8IFtdO1xuICAgICAgICAgICAgICAgIGNvbnN0IHJlc29sdmVkRGVwcyA9IGF3YWl0IFByb21pc2UuYWxsKGRlcHMubWFwKGRlcCA9PiB0aGlzLnJlc29sdmVBc3luY1dpdGhDb250ZXh0KGRlcCwgY29udGV4dCkpKTtcbiAgICAgICAgICAgICAgICByZXR1cm4gbmV3IGJpbmRpbmcuY29uc3RydWN0b3IoLi4ucmVzb2x2ZWREZXBzKTtcbiAgICAgICAgICAgIGNhc2UgJ2lubGluZS1jbGFzcyc6XG4gICAgICAgICAgICAgICAgcmV0dXJuIG5ldyBiaW5kaW5nLmNvbnN0cnVjdG9yKCk7XG4gICAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgVW5rbm93biBiaW5kaW5nIHR5cGU6ICR7YmluZGluZy50eXBlfWApO1xuICAgICAgICB9XG4gICAgfVxuICAgIC8qKlxuICAgICAqIENyZWF0ZSBhIGNoaWxkIGNvbnRhaW5lciB0aGF0IGluaGVyaXRzIGJpbmRpbmdzIGZyb20gdGhpcyBjb250YWluZXJcbiAgICAgKi9cbiAgICBjcmVhdGVDaGlsZCgpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBDb250YWluZXIodGhpcyk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIERpc3Bvc2UgYWxsIHNpbmdsZXRvbiBpbnN0YW5jZXMgaW4gcmV2ZXJzZSByZWdpc3RyYXRpb24gb3JkZXJcbiAgICAgKi9cbiAgICBhc3luYyBkaXNwb3NlKCkge1xuICAgICAgICBjb25zdCBlcnJvcnMgPSBbXTtcbiAgICAgICAgLy8gRGlzcG9zZSBpbiByZXZlcnNlIG9yZGVyXG4gICAgICAgIGZvciAobGV0IGkgPSB0aGlzLnNpbmdsZXRvbk9yZGVyLmxlbmd0aCAtIDE7IGkgPj0gMDsgaS0tKSB7XG4gICAgICAgICAgICBjb25zdCB0b2tlbiA9IHRoaXMuc2luZ2xldG9uT3JkZXJbaV07XG4gICAgICAgICAgICBjb25zdCBpbnN0YW5jZSA9IHRoaXMuc2luZ2xldG9uQ2FjaGUuZ2V0KHRva2VuKTtcbiAgICAgICAgICAgIGlmIChpbnN0YW5jZSAmJiBpc0Rpc3Bvc2FibGUoaW5zdGFuY2UpKSB7XG4gICAgICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICAgICAgYXdhaXQgaW5zdGFuY2UuZGlzcG9zZSgpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAgICAgICAgICAgZXJyb3JzLnB1c2goZXJyb3IpO1xuICAgICAgICAgICAgICAgICAgICAvLyBDb250aW51ZSBkaXNwb3Npbmcgb3RoZXIgaW5zdGFuY2VzIGV2ZW4gaWYgb25lIGZhaWxzXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIC8vIENsZWFyIGNhY2hlc1xuICAgICAgICB0aGlzLnNpbmdsZXRvbkNhY2hlLmNsZWFyKCk7XG4gICAgICAgIHRoaXMuc2luZ2xldG9uT3JkZXIubGVuZ3RoID0gMDtcbiAgICAgICAgLy8gTm90ZTogV2UgZG9uJ3QgdGhyb3cgZXJyb3JzIHRvIGFsbG93IGFsbCBkaXNwb3NhbHMgdG8gY29tcGxldGVcbiAgICAgICAgLy8gSW4gcHJvZHVjdGlvbiwgeW91IG1pZ2h0IHdhbnQgdG8gbG9nIHRoZXNlIGVycm9yc1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBDcmVhdGUgYSBmbHVlbnQgYnVpbGRlciBmb3IgcmVnaXN0ZXJpbmcgZGVwZW5kZW5jaWVzXG4gICAgICovXG4gICAgYnVpbGRlcigpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBCdWlsZGVyKHRoaXMpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBSZXNvbHZlIGEgbmFtZWQgc2VydmljZVxuICAgICAqL1xuICAgIHJlc29sdmVOYW1lZChuYW1lKSB7XG4gICAgICAgIGNvbnN0IG5hbWVkUmVnaXN0cmF0aW9ucyA9IHRoaXMuX19uYW1lZFJlZ2lzdHJhdGlvbnM7XG4gICAgICAgIGlmICghbmFtZWRSZWdpc3RyYXRpb25zKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYE5hbWVkIHNlcnZpY2UgXCIke25hbWV9XCIgbm90IGZvdW5kLiBObyBuYW1lZCByZWdpc3RyYXRpb25zIGV4aXN0LmApO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IGNvbmZpZyA9IG5hbWVkUmVnaXN0cmF0aW9ucy5nZXQobmFtZSk7XG4gICAgICAgIGlmICghY29uZmlnKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYE5hbWVkIHNlcnZpY2UgXCIke25hbWV9XCIgbm90IGZvdW5kYCk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRoaXMucmVzb2x2ZShjb25maWcudG9rZW4pO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBSZXNvbHZlIGEga2V5ZWQgc2VydmljZVxuICAgICAqL1xuICAgIHJlc29sdmVLZXllZChrZXkpIHtcbiAgICAgICAgY29uc3Qga2V5ZWRSZWdpc3RyYXRpb25zID0gdGhpcy5fX2tleWVkUmVnaXN0cmF0aW9ucztcbiAgICAgICAgaWYgKCFrZXllZFJlZ2lzdHJhdGlvbnMpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgS2V5ZWQgc2VydmljZSBub3QgZm91bmQuIE5vIGtleWVkIHJlZ2lzdHJhdGlvbnMgZXhpc3QuYCk7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgY29uZmlnID0ga2V5ZWRSZWdpc3RyYXRpb25zLmdldChrZXkpO1xuICAgICAgICBpZiAoIWNvbmZpZykge1xuICAgICAgICAgICAgY29uc3Qga2V5U3RyID0gdHlwZW9mIGtleSA9PT0gJ3N5bWJvbCcgPyBrZXkudG9TdHJpbmcoKSA6IGBcIiR7a2V5fVwiYDtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgS2V5ZWQgc2VydmljZSAke2tleVN0cn0gbm90IGZvdW5kYCk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRoaXMucmVzb2x2ZShjb25maWcudG9rZW4pO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBSZXNvbHZlIGFsbCByZWdpc3RyYXRpb25zIGZvciBhIHRva2VuXG4gICAgICovXG4gICAgcmVzb2x2ZUFsbCh0b2tlbikge1xuICAgICAgICBjb25zdCBtdWx0aVJlZ2lzdHJhdGlvbnMgPSB0aGlzLl9fbXVsdGlSZWdpc3RyYXRpb25zO1xuICAgICAgICBpZiAoIW11bHRpUmVnaXN0cmF0aW9ucykge1xuICAgICAgICAgICAgcmV0dXJuIFtdO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHRva2VucyA9IG11bHRpUmVnaXN0cmF0aW9ucy5nZXQodG9rZW4pO1xuICAgICAgICBpZiAoIXRva2VucyB8fCB0b2tlbnMubGVuZ3RoID09PSAwKSB7XG4gICAgICAgICAgICByZXR1cm4gW107XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRva2Vucy5tYXAoKHQpID0+IHRoaXMucmVzb2x2ZSh0KSk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEdldCByZWdpc3RyeSBpbmZvcm1hdGlvbiBmb3IgZGVidWdnaW5nL3Zpc3VhbGl6YXRpb25cbiAgICAgKiBSZXR1cm5zIGFycmF5IG9mIGJpbmRpbmcgaW5mb3JtYXRpb25cbiAgICAgKi9cbiAgICBnZXRSZWdpc3RyeSgpIHtcbiAgICAgICAgY29uc3QgcmVnaXN0cnkgPSBbXTtcbiAgICAgICAgdGhpcy5iaW5kaW5ncy5mb3JFYWNoKChiaW5kaW5nLCB0b2tlbikgPT4ge1xuICAgICAgICAgICAgcmVnaXN0cnkucHVzaCh7XG4gICAgICAgICAgICAgICAgdG9rZW46IHRva2VuLmRlc2NyaXB0aW9uIHx8IHRva2VuLnN5bWJvbC50b1N0cmluZygpLFxuICAgICAgICAgICAgICAgIHR5cGU6IGJpbmRpbmcudHlwZSxcbiAgICAgICAgICAgICAgICBsaWZldGltZTogYmluZGluZy5saWZldGltZSxcbiAgICAgICAgICAgICAgICBkZXBlbmRlbmNpZXM6IGJpbmRpbmcuZGVwZW5kZW5jaWVzPy5tYXAoZCA9PiBkLmRlc2NyaXB0aW9uIHx8IGQuc3ltYm9sLnRvU3RyaW5nKCkpXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgICAgIHJldHVybiByZWdpc3RyeTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogR2V0IG9yIGNyZWF0ZSBhIHRva2VuIGZvciBhbiBpbnRlcmZhY2UgdHlwZVxuICAgICAqIFVzZXMgYSB0eXBlIG5hbWUgaGFzaCBhcyBrZXkgZm9yIHRoZSBpbnRlcmZhY2UgcmVnaXN0cnlcbiAgICAgKi9cbiAgICBpbnRlcmZhY2VUb2tlbih0eXBlTmFtZSkge1xuICAgICAgICAvLyBHZW5lcmF0ZSBhIHVuaXF1ZSBrZXkgZm9yIHRoaXMgaW50ZXJmYWNlIHR5cGVcbiAgICAgICAgLy8gSW4gcHJvZHVjdGlvbiwgdGhpcyB3b3VsZCBiZSByZXBsYWNlZCBieSBhIFRTIHRyYW5zZm9ybWVyXG4gICAgICAgIGNvbnN0IGtleSA9IHR5cGVOYW1lIHx8IGBJbnRlcmZhY2VfJHtNYXRoLnJhbmRvbSgpLnRvU3RyaW5nKDM2KS5zdWJzdHIoMiwgOSl9YDtcbiAgICAgICAgLy8gQ2hlY2sgaWYgdG9rZW4gYWxyZWFkeSBleGlzdHMgaW4gdGhpcyBjb250YWluZXJcbiAgICAgICAgaWYgKHRoaXMuaW50ZXJmYWNlUmVnaXN0cnkuaGFzKGtleSkpIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLmludGVyZmFjZVJlZ2lzdHJ5LmdldChrZXkpO1xuICAgICAgICB9XG4gICAgICAgIC8vIENoZWNrIHBhcmVudCBjb250YWluZXIgKHJlY3Vyc2l2ZWx5IHRocm91Z2ggcGFyZW50IGNoYWluKVxuICAgICAgICBpZiAodGhpcy5wYXJlbnQpIHtcbiAgICAgICAgICAgIC8vIFJlY3Vyc2l2ZWx5IGNoZWNrIHRocm91Z2ggZW50aXJlIHBhcmVudCBjaGFpblxuICAgICAgICAgICAgY29uc3QgcGFyZW50VG9rZW4gPSB0aGlzLnBhcmVudC5pbnRlcmZhY2VUb2tlbihrZXkpO1xuICAgICAgICAgICAgLy8gSWYgcGFyZW50IGNyZWF0ZWQgYSBuZXcgdG9rZW4sIGRvbid0IGNyZWF0ZSBhbm90aGVyIG9uZVxuICAgICAgICAgICAgcmV0dXJuIHBhcmVudFRva2VuO1xuICAgICAgICB9XG4gICAgICAgIC8vIENyZWF0ZSBuZXcgdG9rZW4gKG9ubHkgaWYgbm8gcGFyZW50IGV4aXN0cylcbiAgICAgICAgY29uc3QgdG9rZW4gPSBUb2tlbihrZXkpO1xuICAgICAgICB0aGlzLmludGVyZmFjZVJlZ2lzdHJ5LnNldChrZXksIHRva2VuKTtcbiAgICAgICAgcmV0dXJuIHRva2VuO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBSZXNvbHZlIGEgZGVwZW5kZW5jeSBieSBpbnRlcmZhY2UgdHlwZSB3aXRob3V0IGV4cGxpY2l0IHRva2VuXG4gICAgICovXG4gICAgcmVzb2x2ZVR5cGUodHlwZU5hbWUpIHtcbiAgICAgICAgLy8gUGVyZm9ybWFuY2U6IENhY2hlIHRva2VuIGxvb2t1cHMgdG8gYXZvaWQgcmVwZWF0ZWQgaW50ZXJmYWNlUmVnaXN0cnkgYWNjZXNzXG4gICAgICAgIGNvbnN0IGtleSA9IHR5cGVOYW1lIHx8ICcnO1xuICAgICAgICBsZXQgdG9rZW4gPSB0aGlzLmludGVyZmFjZVRva2VuQ2FjaGUuZ2V0KGtleSk7XG4gICAgICAgIGlmICghdG9rZW4pIHtcbiAgICAgICAgICAgIHRva2VuID0gdGhpcy5pbnRlcmZhY2VUb2tlbih0eXBlTmFtZSk7XG4gICAgICAgICAgICB0aGlzLmludGVyZmFjZVRva2VuQ2FjaGUuc2V0KGtleSwgdG9rZW4pO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0aGlzLnJlc29sdmUodG9rZW4pO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBSZXNvbHZlIGEga2V5ZWQgaW50ZXJmYWNlXG4gICAgICovXG4gICAgcmVzb2x2ZVR5cGVLZXllZChrZXksIF90eXBlTmFtZSkge1xuICAgICAgICAvLyBGb3Iga2V5ZWQgaW50ZXJmYWNlcywgd2UgdXNlIHRoZSBleGlzdGluZyByZXNvbHZlS2V5ZWQgbWVjaGFuaXNtXG4gICAgICAgIHJldHVybiB0aGlzLnJlc29sdmVLZXllZChrZXkpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBSZXNvbHZlIGFsbCByZWdpc3RyYXRpb25zIGZvciBhbiBpbnRlcmZhY2UgdHlwZVxuICAgICAqL1xuICAgIHJlc29sdmVUeXBlQWxsKHR5cGVOYW1lKSB7XG4gICAgICAgIGNvbnN0IHRva2VuID0gdGhpcy5pbnRlcmZhY2VUb2tlbih0eXBlTmFtZSk7XG4gICAgICAgIHJldHVybiB0aGlzLnJlc29sdmVBbGwodG9rZW4pO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBJbnRlcm5hbDogUmVzb2x2ZSB3aXRoIGNvbnRleHQgZm9yIGNpcmN1bGFyIGRlcGVuZGVuY3kgZGV0ZWN0aW9uXG4gICAgICovXG4gICAgcmVzb2x2ZVdpdGhDb250ZXh0KHRva2VuLCBjb250ZXh0KSB7XG4gICAgICAgIC8vIFZhbGlkYXRlIGFuZCBnZXQgYmluZGluZyAod2l0aCBjaXJjdWxhciBkZXBlbmRlbmN5IGNoZWNrKVxuICAgICAgICBjb25zdCBiaW5kaW5nID0gdGhpcy52YWxpZGF0ZUFuZEdldEJpbmRpbmcodG9rZW4sIGNvbnRleHQpO1xuICAgICAgICAvLyBDaGVjayBwZXItcmVxdWVzdCBjYWNoZVxuICAgICAgICBpZiAoYmluZGluZy5saWZldGltZSA9PT0gJ3Blci1yZXF1ZXN0JyAmJiBjb250ZXh0Lmhhc1BlclJlcXVlc3QodG9rZW4pKSB7XG4gICAgICAgICAgICByZXR1cm4gY29udGV4dC5nZXRQZXJSZXF1ZXN0KHRva2VuKTtcbiAgICAgICAgfVxuICAgICAgICAvLyBDaGVjayBzaW5nbGV0b24gY2FjaGUgKGxvY2FsIGNvbnRhaW5lciBvbmx5KVxuICAgICAgICBpZiAoYmluZGluZy5saWZldGltZSA9PT0gJ3NpbmdsZXRvbicgJiYgdGhpcy5zaW5nbGV0b25DYWNoZS5oYXModG9rZW4pKSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5zaW5nbGV0b25DYWNoZS5nZXQodG9rZW4pO1xuICAgICAgICB9XG4gICAgICAgIC8vIE1hcmsgYXMgcmVzb2x2aW5nXG4gICAgICAgIGNvbnRleHQuZW50ZXJSZXNvbHZlKHRva2VuKTtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIC8vIEluc3RhbnRpYXRlIGZyb20gYmluZGluZ1xuICAgICAgICAgICAgY29uc3QgaW5zdGFuY2UgPSB0aGlzLmluc3RhbnRpYXRlQmluZGluZ1N5bmMoYmluZGluZywgdG9rZW4sIGNvbnRleHQpO1xuICAgICAgICAgICAgLy8gQ2FjaGUgYmFzZWQgb24gbGlmZXRpbWVcbiAgICAgICAgICAgIHRoaXMuY2FjaGVJbnN0YW5jZSh0b2tlbiwgaW5zdGFuY2UsIGJpbmRpbmcubGlmZXRpbWUsIGNvbnRleHQpO1xuICAgICAgICAgICAgcmV0dXJuIGluc3RhbmNlO1xuICAgICAgICB9XG4gICAgICAgIGZpbmFsbHkge1xuICAgICAgICAgICAgY29udGV4dC5leGl0UmVzb2x2ZSh0b2tlbik7XG4gICAgICAgIH1cbiAgICB9XG4gICAgLyoqXG4gICAgICogSW50ZXJuYWw6IEFzeW5jIHJlc29sdmUgd2l0aCBjb250ZXh0XG4gICAgICovXG4gICAgYXN5bmMgcmVzb2x2ZUFzeW5jV2l0aENvbnRleHQodG9rZW4sIGNvbnRleHQpIHtcbiAgICAgICAgLy8gVmFsaWRhdGUgYW5kIGdldCBiaW5kaW5nICh3aXRoIGNpcmN1bGFyIGRlcGVuZGVuY3kgY2hlY2spXG4gICAgICAgIGNvbnN0IGJpbmRpbmcgPSB0aGlzLnZhbGlkYXRlQW5kR2V0QmluZGluZyh0b2tlbiwgY29udGV4dCk7XG4gICAgICAgIC8vIENoZWNrIHBlci1yZXF1ZXN0IGNhY2hlXG4gICAgICAgIGlmIChiaW5kaW5nLmxpZmV0aW1lID09PSAncGVyLXJlcXVlc3QnICYmIGNvbnRleHQuaGFzUGVyUmVxdWVzdCh0b2tlbikpIHtcbiAgICAgICAgICAgIHJldHVybiBjb250ZXh0LmdldFBlclJlcXVlc3QodG9rZW4pO1xuICAgICAgICB9XG4gICAgICAgIC8vIENoZWNrIHNpbmdsZXRvbiBjYWNoZSAobG9jYWwgY29udGFpbmVyIG9ubHkpXG4gICAgICAgIGlmIChiaW5kaW5nLmxpZmV0aW1lID09PSAnc2luZ2xldG9uJyAmJiB0aGlzLnNpbmdsZXRvbkNhY2hlLmhhcyh0b2tlbikpIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLnNpbmdsZXRvbkNhY2hlLmdldCh0b2tlbik7XG4gICAgICAgIH1cbiAgICAgICAgLy8gTWFyayBhcyByZXNvbHZpbmdcbiAgICAgICAgY29udGV4dC5lbnRlclJlc29sdmUodG9rZW4pO1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgLy8gSW5zdGFudGlhdGUgZnJvbSBiaW5kaW5nIGFzeW5jaHJvbm91c2x5XG4gICAgICAgICAgICBjb25zdCBpbnN0YW5jZSA9IGF3YWl0IHRoaXMuaW5zdGFudGlhdGVCaW5kaW5nQXN5bmMoYmluZGluZywgY29udGV4dCk7XG4gICAgICAgICAgICAvLyBDYWNoZSBiYXNlZCBvbiBsaWZldGltZVxuICAgICAgICAgICAgdGhpcy5jYWNoZUluc3RhbmNlKHRva2VuLCBpbnN0YW5jZSwgYmluZGluZy5saWZldGltZSwgY29udGV4dCk7XG4gICAgICAgICAgICByZXR1cm4gaW5zdGFuY2U7XG4gICAgICAgIH1cbiAgICAgICAgZmluYWxseSB7XG4gICAgICAgICAgICBjb250ZXh0LmV4aXRSZXNvbHZlKHRva2VuKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICAvKipcbiAgICAgKiBHZXQgYmluZGluZyBmcm9tIHRoaXMgY29udGFpbmVyIG9yIHBhcmVudCBjaGFpblxuICAgICAqIFBlcmZvcm1hbmNlIG9wdGltaXplZDogVXNlcyBmbGF0IGNhY2hlIHRvIGF2b2lkIHJlY3Vyc2l2ZSBwYXJlbnQgbG9va3Vwc1xuICAgICAqL1xuICAgIGdldEJpbmRpbmcodG9rZW4pIHtcbiAgICAgICAgLy8gQnVpbGQgZmxhdCBjYWNoZSBvbiBmaXJzdCBhY2Nlc3NcbiAgICAgICAgaWYgKCF0aGlzLmJpbmRpbmdDYWNoZSkge1xuICAgICAgICAgICAgdGhpcy5idWlsZEJpbmRpbmdDYWNoZSgpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0aGlzLmJpbmRpbmdDYWNoZS5nZXQodG9rZW4pO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBCdWlsZCBmbGF0IGNhY2hlIG9mIGFsbCBiaW5kaW5ncyBpbmNsdWRpbmcgcGFyZW50IGNoYWluXG4gICAgICogVGhpcyBjb252ZXJ0cyBPKG4pIHBhcmVudCBjaGFpbiB0cmF2ZXJzYWwgdG8gTygxKSBsb29rdXBcbiAgICAgKi9cbiAgICBidWlsZEJpbmRpbmdDYWNoZSgpIHtcbiAgICAgICAgdGhpcy5iaW5kaW5nQ2FjaGUgPSBuZXcgTWFwKCk7XG4gICAgICAgIC8vIFRyYXZlcnNlIHBhcmVudCBjaGFpbiBhbmQgZmxhdHRlbiBhbGwgYmluZGluZ3NcbiAgICAgICAgbGV0IGN1cnJlbnQgPSB0aGlzO1xuICAgICAgICB3aGlsZSAoY3VycmVudCkge1xuICAgICAgICAgICAgY3VycmVudC5iaW5kaW5ncy5mb3JFYWNoKChiaW5kaW5nLCB0b2tlbikgPT4ge1xuICAgICAgICAgICAgICAgIC8vIENoaWxkIGJpbmRpbmdzIG92ZXJyaWRlIHBhcmVudCBiaW5kaW5ncyAoZmlyc3Qgd2lucylcbiAgICAgICAgICAgICAgICBpZiAoIXRoaXMuYmluZGluZ0NhY2hlLmhhcyh0b2tlbikpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5iaW5kaW5nQ2FjaGUuc2V0KHRva2VuLCBiaW5kaW5nKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIGN1cnJlbnQgPSBjdXJyZW50LnBhcmVudDtcbiAgICAgICAgfVxuICAgIH1cbiAgICAvKipcbiAgICAgKiBJbnZhbGlkYXRlIGJpbmRpbmcgY2FjaGUgd2hlbiBuZXcgYmluZGluZ3MgYXJlIGFkZGVkXG4gICAgICogQ2FsbGVkIGJ5IGJpbmRWYWx1ZSwgYmluZEZhY3RvcnksIGJpbmRDbGFzc1xuICAgICAqL1xuICAgIGludmFsaWRhdGVCaW5kaW5nQ2FjaGUoKSB7XG4gICAgICAgIHRoaXMuYmluZGluZ0NhY2hlID0gdW5kZWZpbmVkO1xuICAgICAgICB0aGlzLnVsdHJhRmFzdFNpbmdsZXRvbkNhY2hlLmNsZWFyKCk7IC8vIENsZWFyIHVsdHJhLWZhc3QgY2FjaGUgd2hlbiBiaW5kaW5ncyBjaGFuZ2VcbiAgICB9XG59XG5Db250YWluZXIuY29udGV4dFBvb2wgPSBuZXcgUmVzb2x1dGlvbkNvbnRleHRQb29sKCk7IC8vIFBlcmZvcm1hbmNlOiBQb29sZWQgY29udGV4dHMgcmVkdWNlIGFsbG9jYXRpb25zXG4iLCAiZXhwb3J0IGNsYXNzIERhdGVSZW5kZXJlciB7XG4gICAgY29uc3RydWN0b3IoZGF0ZVNlcnZpY2UpIHtcbiAgICAgICAgdGhpcy5kYXRlU2VydmljZSA9IGRhdGVTZXJ2aWNlO1xuICAgICAgICB0aGlzLnR5cGUgPSAnZGF0ZSc7XG4gICAgfVxuICAgIHJlbmRlcihjb250ZXh0KSB7XG4gICAgICAgIGNvbnN0IGRhdGVzID0gY29udGV4dC5maWx0ZXJbJ2RhdGUnXSB8fCBbXTtcbiAgICAgICAgY29uc3QgcmVzb3VyY2VJZHMgPSBjb250ZXh0LmZpbHRlclsncmVzb3VyY2UnXSB8fCBbXTtcbiAgICAgICAgLy8gQ2hlY2sgaWYgZGF0ZSBoZWFkZXJzIHNob3VsZCBiZSBoaWRkZW4gKGUuZy4sIGluIGRheSB2aWV3KVxuICAgICAgICBjb25zdCBkYXRlR3JvdXBpbmcgPSBjb250ZXh0Lmdyb3VwaW5ncz8uZmluZChnID0+IGcudHlwZSA9PT0gJ2RhdGUnKTtcbiAgICAgICAgY29uc3QgaGlkZUhlYWRlciA9IGRhdGVHcm91cGluZz8uaGlkZUhlYWRlciA9PT0gdHJ1ZTtcbiAgICAgICAgLy8gUmVuZGVyIGRhdGVzIGZvciBIVkVSIHJlc291cmNlIChlbGxlciAxIGdhbmcgaHZpcyBpbmdlbiByZXNvdXJjZXMpXG4gICAgICAgIGNvbnN0IGl0ZXJhdGlvbnMgPSByZXNvdXJjZUlkcy5sZW5ndGggfHwgMTtcbiAgICAgICAgbGV0IGNvbHVtbkNvdW50ID0gMDtcbiAgICAgICAgZm9yIChsZXQgciA9IDA7IHIgPCBpdGVyYXRpb25zOyByKyspIHtcbiAgICAgICAgICAgIGNvbnN0IHJlc291cmNlSWQgPSByZXNvdXJjZUlkc1tyXTsgLy8gdW5kZWZpbmVkIGh2aXMgaW5nZW4gcmVzb3VyY2VzXG4gICAgICAgICAgICBmb3IgKGNvbnN0IGRhdGVTdHIgb2YgZGF0ZXMpIHtcbiAgICAgICAgICAgICAgICBjb25zdCBkYXRlID0gdGhpcy5kYXRlU2VydmljZS5wYXJzZUlTTyhkYXRlU3RyKTtcbiAgICAgICAgICAgICAgICAvLyBCdWlsZCBjb2x1bW5LZXkgZm9yIHVuaWZvcm0gaWRlbnRpZmljYXRpb25cbiAgICAgICAgICAgICAgICBjb25zdCBzZWdtZW50cyA9IHsgZGF0ZTogZGF0ZVN0ciB9O1xuICAgICAgICAgICAgICAgIGlmIChyZXNvdXJjZUlkKVxuICAgICAgICAgICAgICAgICAgICBzZWdtZW50cy5yZXNvdXJjZSA9IHJlc291cmNlSWQ7XG4gICAgICAgICAgICAgICAgY29uc3QgY29sdW1uS2V5ID0gdGhpcy5kYXRlU2VydmljZS5idWlsZENvbHVtbktleShzZWdtZW50cyk7XG4gICAgICAgICAgICAgICAgLy8gSGVhZGVyXG4gICAgICAgICAgICAgICAgY29uc3QgaGVhZGVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3dwLWRheS1oZWFkZXInKTtcbiAgICAgICAgICAgICAgICBoZWFkZXIuZGF0YXNldC5kYXRlID0gZGF0ZVN0cjtcbiAgICAgICAgICAgICAgICBoZWFkZXIuZGF0YXNldC5jb2x1bW5LZXkgPSBjb2x1bW5LZXk7XG4gICAgICAgICAgICAgICAgaWYgKHJlc291cmNlSWQpIHtcbiAgICAgICAgICAgICAgICAgICAgaGVhZGVyLmRhdGFzZXQucmVzb3VyY2VJZCA9IHJlc291cmNlSWQ7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGlmIChoaWRlSGVhZGVyKSB7XG4gICAgICAgICAgICAgICAgICAgIGhlYWRlci5kYXRhc2V0LmhpZGRlbiA9ICd0cnVlJztcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgaGVhZGVyLmlubmVySFRNTCA9IGBcclxuICAgICAgICAgIDxzd3AtZGF5LW5hbWU+JHt0aGlzLmRhdGVTZXJ2aWNlLmdldERheU5hbWUoZGF0ZSwgJ3Nob3J0Jyl9PC9zd3AtZGF5LW5hbWU+XHJcbiAgICAgICAgICA8c3dwLWRheS1kYXRlPiR7ZGF0ZS5nZXREYXRlKCl9PC9zd3AtZGF5LWRhdGU+XHJcbiAgICAgICAgYDtcbiAgICAgICAgICAgICAgICBjb250ZXh0LmhlYWRlckNvbnRhaW5lci5hcHBlbmRDaGlsZChoZWFkZXIpO1xuICAgICAgICAgICAgICAgIC8vIENvbHVtblxuICAgICAgICAgICAgICAgIGNvbnN0IGNvbHVtbiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3N3cC1kYXktY29sdW1uJyk7XG4gICAgICAgICAgICAgICAgY29sdW1uLmRhdGFzZXQuZGF0ZSA9IGRhdGVTdHI7XG4gICAgICAgICAgICAgICAgY29sdW1uLmRhdGFzZXQuY29sdW1uS2V5ID0gY29sdW1uS2V5O1xuICAgICAgICAgICAgICAgIGlmIChyZXNvdXJjZUlkKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbHVtbi5kYXRhc2V0LnJlc291cmNlSWQgPSByZXNvdXJjZUlkO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBjb2x1bW4uaW5uZXJIVE1MID0gJzxzd3AtZXZlbnRzLWxheWVyPjwvc3dwLWV2ZW50cy1sYXllcj4nO1xuICAgICAgICAgICAgICAgIGNvbnRleHQuY29sdW1uQ29udGFpbmVyLmFwcGVuZENoaWxkKGNvbHVtbik7XG4gICAgICAgICAgICAgICAgY29sdW1uQ291bnQrKztcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICAvLyBTZXQgZ3JpZCBjb2x1bW5zIG9uIGNvbnRhaW5lclxuICAgICAgICBjb25zdCBjb250YWluZXIgPSBjb250ZXh0LmNvbHVtbkNvbnRhaW5lci5jbG9zZXN0KCdzd3AtY2FsZW5kYXItY29udGFpbmVyJyk7XG4gICAgICAgIGlmIChjb250YWluZXIpIHtcbiAgICAgICAgICAgIGNvbnRhaW5lci5zdHlsZS5zZXRQcm9wZXJ0eSgnLS1ncmlkLWNvbHVtbnMnLCBTdHJpbmcoY29sdW1uQ291bnQpKTtcbiAgICAgICAgfVxuICAgIH1cbn1cbiIsICJpbXBvcnQgZGF5anMgZnJvbSAnZGF5anMnO1xuaW1wb3J0IHV0YyBmcm9tICdkYXlqcy9wbHVnaW4vdXRjJztcbmltcG9ydCB0aW1lem9uZSBmcm9tICdkYXlqcy9wbHVnaW4vdGltZXpvbmUnO1xuaW1wb3J0IGlzb1dlZWsgZnJvbSAnZGF5anMvcGx1Z2luL2lzb1dlZWsnO1xuLy8gRW5hYmxlIGRheWpzIHBsdWdpbnNcbmRheWpzLmV4dGVuZCh1dGMpO1xuZGF5anMuZXh0ZW5kKHRpbWV6b25lKTtcbmRheWpzLmV4dGVuZChpc29XZWVrKTtcbmV4cG9ydCBjbGFzcyBEYXRlU2VydmljZSB7XG4gICAgY29uc3RydWN0b3IoY29uZmlnLCBiYXNlRGF0ZSkge1xuICAgICAgICB0aGlzLmNvbmZpZyA9IGNvbmZpZztcbiAgICAgICAgdGhpcy50aW1lem9uZSA9IGNvbmZpZy50aW1lem9uZTtcbiAgICAgICAgLy8gQWxsb3cgc2V0dGluZyBhIGZpeGVkIGJhc2UgZGF0ZSBmb3IgZGVtby90ZXN0aW5nIHB1cnBvc2VzXG4gICAgICAgIHRoaXMuYmFzZURhdGUgPSBiYXNlRGF0ZSA/IGRheWpzKGJhc2VEYXRlKSA6IGRheWpzKCk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFNldCBhIGZpeGVkIGJhc2UgZGF0ZSAodXNlZnVsIGZvciBkZW1vcyB3aXRoIHN0YXRpYyBtb2NrIGRhdGEpXG4gICAgICovXG4gICAgc2V0QmFzZURhdGUoZGF0ZSkge1xuICAgICAgICB0aGlzLmJhc2VEYXRlID0gZGF5anMoZGF0ZSk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEdldCB0aGUgY3VycmVudCBiYXNlIGRhdGUgKGVpdGhlciBmaXhlZCBvciB0b2RheSlcbiAgICAgKi9cbiAgICBnZXRCYXNlRGF0ZSgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuYmFzZURhdGUudG9EYXRlKCk7XG4gICAgfVxuICAgIHBhcnNlSVNPKGlzb1N0cmluZykge1xuICAgICAgICByZXR1cm4gZGF5anMoaXNvU3RyaW5nKS50b0RhdGUoKTtcbiAgICB9XG4gICAgZ2V0RGF5TmFtZShkYXRlLCBmb3JtYXQgPSAnc2hvcnQnKSB7XG4gICAgICAgIHJldHVybiBuZXcgSW50bC5EYXRlVGltZUZvcm1hdCh0aGlzLmNvbmZpZy5sb2NhbGUsIHsgd2Vla2RheTogZm9ybWF0IH0pLmZvcm1hdChkYXRlKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogR2V0IGRhdGVzIHN0YXJ0aW5nIGZyb20gYSBkYXkgb2Zmc2V0XG4gICAgICogQHBhcmFtIGRheU9mZnNldCAtIERheSBvZmZzZXQgZnJvbSBiYXNlIGRhdGVcbiAgICAgKiBAcGFyYW0gY291bnQgLSBOdW1iZXIgb2YgY29uc2VjdXRpdmUgZGF5cyB0byByZXR1cm5cbiAgICAgKiBAcmV0dXJucyBBcnJheSBvZiBkYXRlIHN0cmluZ3MgaW4gWVlZWS1NTS1ERCBmb3JtYXRcbiAgICAgKi9cbiAgICBnZXREYXRlc0Zyb21PZmZzZXQoZGF5T2Zmc2V0LCBjb3VudCkge1xuICAgICAgICBjb25zdCBzdGFydERhdGUgPSB0aGlzLmJhc2VEYXRlLmFkZChkYXlPZmZzZXQsICdkYXknKTtcbiAgICAgICAgcmV0dXJuIEFycmF5LmZyb20oeyBsZW5ndGg6IGNvdW50IH0sIChfLCBpKSA9PiBzdGFydERhdGUuYWRkKGksICdkYXknKS5mb3JtYXQoJ1lZWVktTU0tREQnKSk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEdldCBzcGVjaWZpYyB3ZWVrZGF5cyBmcm9tIHRoZSB3ZWVrIGNvbnRhaW5pbmcgdGhlIG9mZnNldCBkYXRlXG4gICAgICogQHBhcmFtIGRheU9mZnNldCAtIERheSBvZmZzZXQgZnJvbSBiYXNlIGRhdGVcbiAgICAgKiBAcGFyYW0gd29ya0RheXMgLSBBcnJheSBvZiBJU08gd2Vla2RheSBudW1iZXJzICgxPU1vbmRheSwgNz1TdW5kYXkpXG4gICAgICogQHJldHVybnMgQXJyYXkgb2YgZGF0ZSBzdHJpbmdzIGluIFlZWVktTU0tREQgZm9ybWF0XG4gICAgICovXG4gICAgZ2V0V29ya0RheXNGcm9tT2Zmc2V0KGRheU9mZnNldCwgd29ya0RheXMpIHtcbiAgICAgICAgLy8gR2V0IHRoZSBkYXRlIGF0IG9mZnNldCwgdGhlbiBmaW5kIGl0cyB3ZWVrJ3MgTW9uZGF5XG4gICAgICAgIGNvbnN0IHRhcmdldERhdGUgPSB0aGlzLmJhc2VEYXRlLmFkZChkYXlPZmZzZXQsICdkYXknKTtcbiAgICAgICAgY29uc3QgbW9uZGF5ID0gdGFyZ2V0RGF0ZS5zdGFydE9mKCd3ZWVrJykuYWRkKDEsICdkYXknKTtcbiAgICAgICAgcmV0dXJuIHdvcmtEYXlzLm1hcChpc29EYXkgPT4ge1xuICAgICAgICAgICAgLy8gSVNPOiAxPU1vbmRheSwgNz1TdW5kYXkgXHUyMTkyIGRheXMgZnJvbSBNb25kYXk6IDAtNlxuICAgICAgICAgICAgY29uc3QgZGF5c0Zyb21Nb25kYXkgPSBpc29EYXkgPT09IDcgPyA2IDogaXNvRGF5IC0gMTtcbiAgICAgICAgICAgIHJldHVybiBtb25kYXkuYWRkKGRheXNGcm9tTW9uZGF5LCAnZGF5JykuZm9ybWF0KCdZWVlZLU1NLUREJyk7XG4gICAgICAgIH0pO1xuICAgIH1cbiAgICAvLyBMZWdhY3kgbWV0aG9kcyBmb3IgYmFja3dhcmRzIGNvbXBhdGliaWxpdHlcbiAgICBnZXRXZWVrRGF0ZXMod2Vla09mZnNldCA9IDAsIGRheXMgPSA3KSB7XG4gICAgICAgIHJldHVybiB0aGlzLmdldERhdGVzRnJvbU9mZnNldCh3ZWVrT2Zmc2V0ICogNywgZGF5cyk7XG4gICAgfVxuICAgIGdldFdvcmtXZWVrRGF0ZXMod2Vla09mZnNldCwgd29ya0RheXMpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuZ2V0V29ya0RheXNGcm9tT2Zmc2V0KHdlZWtPZmZzZXQgKiA3LCB3b3JrRGF5cyk7XG4gICAgfVxuICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gICAgLy8gRk9STUFUVElOR1xuICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gICAgZm9ybWF0VGltZShkYXRlLCBzaG93U2Vjb25kcyA9IGZhbHNlKSB7XG4gICAgICAgIGNvbnN0IHBhdHRlcm4gPSBzaG93U2Vjb25kcyA/ICdISDptbTpzcycgOiAnSEg6bW0nO1xuICAgICAgICByZXR1cm4gZGF5anMoZGF0ZSkuZm9ybWF0KHBhdHRlcm4pO1xuICAgIH1cbiAgICBmb3JtYXRUaW1lUmFuZ2Uoc3RhcnQsIGVuZCkge1xuICAgICAgICByZXR1cm4gYCR7dGhpcy5mb3JtYXRUaW1lKHN0YXJ0KX0gLSAke3RoaXMuZm9ybWF0VGltZShlbmQpfWA7XG4gICAgfVxuICAgIGZvcm1hdERhdGUoZGF0ZSkge1xuICAgICAgICByZXR1cm4gZGF5anMoZGF0ZSkuZm9ybWF0KCdZWVlZLU1NLUREJyk7XG4gICAgfVxuICAgIGdldERhdGVLZXkoZGF0ZSkge1xuICAgICAgICByZXR1cm4gdGhpcy5mb3JtYXREYXRlKGRhdGUpO1xuICAgIH1cbiAgICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAgIC8vIENPTFVNTiBLRVlcbiAgICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAgIC8qKlxuICAgICAqIEJ1aWxkIGEgdW5pZm9ybSBjb2x1bW5LZXkgZnJvbSBncm91cGluZyBzZWdtZW50c1xuICAgICAqIEhhbmRsZXMgYW55IGNvbWJpbmF0aW9uIG9mIGRhdGUsIHJlc291cmNlLCB0ZWFtLCBldGMuXG4gICAgICpcbiAgICAgKiBAZXhhbXBsZVxuICAgICAqIGJ1aWxkQ29sdW1uS2V5KHsgZGF0ZTogJzIwMjUtMTItMDknIH0pIFx1MjE5MiBcIjIwMjUtMTItMDlcIlxuICAgICAqIGJ1aWxkQ29sdW1uS2V5KHsgZGF0ZTogJzIwMjUtMTItMDknLCByZXNvdXJjZTogJ0VNUDAwMScgfSkgXHUyMTkyIFwiMjAyNS0xMi0wOTpFTVAwMDFcIlxuICAgICAqL1xuICAgIGJ1aWxkQ29sdW1uS2V5KHNlZ21lbnRzKSB7XG4gICAgICAgIC8vIEFsd2F5cyBwdXQgZGF0ZSBmaXJzdCBpZiBwcmVzZW50LCB0aGVuIG90aGVyIHNlZ21lbnRzIGFscGhhYmV0aWNhbGx5XG4gICAgICAgIGNvbnN0IGRhdGUgPSBzZWdtZW50cy5kYXRlO1xuICAgICAgICBjb25zdCBvdGhlcnMgPSBPYmplY3QuZW50cmllcyhzZWdtZW50cylcbiAgICAgICAgICAgIC5maWx0ZXIoKFtrXSkgPT4gayAhPT0gJ2RhdGUnKVxuICAgICAgICAgICAgLnNvcnQoKFthXSwgW2JdKSA9PiBhLmxvY2FsZUNvbXBhcmUoYikpXG4gICAgICAgICAgICAubWFwKChbLCB2XSkgPT4gdik7XG4gICAgICAgIHJldHVybiBkYXRlID8gW2RhdGUsIC4uLm90aGVyc10uam9pbignOicpIDogb3RoZXJzLmpvaW4oJzonKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogUGFyc2UgYSBjb2x1bW5LZXkgYmFjayBpbnRvIHNlZ21lbnRzXG4gICAgICogQXNzdW1lcyBmb3JtYXQ6IFwiZGF0ZTpyZXNvdXJjZTouLi5cIiBvciBqdXN0IFwiZGF0ZVwiXG4gICAgICovXG4gICAgcGFyc2VDb2x1bW5LZXkoY29sdW1uS2V5KSB7XG4gICAgICAgIGNvbnN0IHBhcnRzID0gY29sdW1uS2V5LnNwbGl0KCc6Jyk7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBkYXRlOiBwYXJ0c1swXSxcbiAgICAgICAgICAgIHJlc291cmNlOiBwYXJ0c1sxXVxuICAgICAgICB9O1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBFeHRyYWN0IGRhdGVLZXkgZnJvbSBjb2x1bW5LZXkgKGZpcnN0IHNlZ21lbnQpXG4gICAgICovXG4gICAgZ2V0RGF0ZUZyb21Db2x1bW5LZXkoY29sdW1uS2V5KSB7XG4gICAgICAgIHJldHVybiBjb2x1bW5LZXkuc3BsaXQoJzonKVswXTtcbiAgICB9XG4gICAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgICAvLyBUSU1FIENBTENVTEFUSU9OU1xuICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gICAgdGltZVRvTWludXRlcyh0aW1lU3RyaW5nKSB7XG4gICAgICAgIGNvbnN0IHBhcnRzID0gdGltZVN0cmluZy5zcGxpdCgnOicpLm1hcChOdW1iZXIpO1xuICAgICAgICBjb25zdCBob3VycyA9IHBhcnRzWzBdIHx8IDA7XG4gICAgICAgIGNvbnN0IG1pbnV0ZXMgPSBwYXJ0c1sxXSB8fCAwO1xuICAgICAgICByZXR1cm4gaG91cnMgKiA2MCArIG1pbnV0ZXM7XG4gICAgfVxuICAgIG1pbnV0ZXNUb1RpbWUodG90YWxNaW51dGVzKSB7XG4gICAgICAgIGNvbnN0IGhvdXJzID0gTWF0aC5mbG9vcih0b3RhbE1pbnV0ZXMgLyA2MCk7XG4gICAgICAgIGNvbnN0IG1pbnV0ZXMgPSB0b3RhbE1pbnV0ZXMgJSA2MDtcbiAgICAgICAgcmV0dXJuIGRheWpzKCkuaG91cihob3VycykubWludXRlKG1pbnV0ZXMpLmZvcm1hdCgnSEg6bW0nKTtcbiAgICB9XG4gICAgZ2V0TWludXRlc1NpbmNlTWlkbmlnaHQoZGF0ZSkge1xuICAgICAgICBjb25zdCBkID0gZGF5anMoZGF0ZSk7XG4gICAgICAgIHJldHVybiBkLmhvdXIoKSAqIDYwICsgZC5taW51dGUoKTtcbiAgICB9XG4gICAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgICAvLyBVVEMgQ09OVkVSU0lPTlNcbiAgICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAgIHRvVVRDKGxvY2FsRGF0ZSkge1xuICAgICAgICByZXR1cm4gZGF5anMudHoobG9jYWxEYXRlLCB0aGlzLnRpbWV6b25lKS51dGMoKS50b0lTT1N0cmluZygpO1xuICAgIH1cbiAgICBmcm9tVVRDKHV0Y1N0cmluZykge1xuICAgICAgICByZXR1cm4gZGF5anMudXRjKHV0Y1N0cmluZykudHoodGhpcy50aW1lem9uZSkudG9EYXRlKCk7XG4gICAgfVxuICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gICAgLy8gREFURSBDUkVBVElPTlxuICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gICAgY3JlYXRlRGF0ZUF0VGltZShiYXNlRGF0ZSwgdGltZVN0cmluZykge1xuICAgICAgICBjb25zdCB0b3RhbE1pbnV0ZXMgPSB0aGlzLnRpbWVUb01pbnV0ZXModGltZVN0cmluZyk7XG4gICAgICAgIGNvbnN0IGhvdXJzID0gTWF0aC5mbG9vcih0b3RhbE1pbnV0ZXMgLyA2MCk7XG4gICAgICAgIGNvbnN0IG1pbnV0ZXMgPSB0b3RhbE1pbnV0ZXMgJSA2MDtcbiAgICAgICAgcmV0dXJuIGRheWpzKGJhc2VEYXRlKS5zdGFydE9mKCdkYXknKS5ob3VyKGhvdXJzKS5taW51dGUobWludXRlcykudG9EYXRlKCk7XG4gICAgfVxuICAgIGdldElTT1dlZWtEYXkoZGF0ZSkge1xuICAgICAgICByZXR1cm4gZGF5anMoZGF0ZSkuaXNvV2Vla2RheSgpOyAvLyAxPU1vbmRheSwgNz1TdW5kYXlcbiAgICB9XG59XG4iLCAiLyoqXG4gKiBBYnN0cmFjdCBiYXNlIGNsYXNzIGZvciBncm91cGluZyByZW5kZXJlcnNcbiAqXG4gKiBIYW5kbGVzOlxuICogLSBGZXRjaGluZyBlbnRpdGllcyBieSBJRHNcbiAqIC0gQ2FsY3VsYXRpbmcgY29sc3BhbiBmcm9tIHBhcmVudENoaWxkTWFwXG4gKiAtIENyZWF0aW5nIGhlYWRlciBlbGVtZW50c1xuICogLSBBcHBlbmRpbmcgdG8gY29udGFpbmVyXG4gKlxuICogU3ViY2xhc3NlcyBvdmVycmlkZTpcbiAqIC0gcmVuZGVySGVhZGVyKCkgZm9yIGN1c3RvbSBjb250ZW50XG4gKiAtIGdldERpc3BsYXlOYW1lKCkgZm9yIGVudGl0eSBkaXNwbGF5IHRleHRcbiAqL1xuZXhwb3J0IGNsYXNzIEJhc2VHcm91cGluZ1JlbmRlcmVyIHtcbiAgICAvKipcbiAgICAgKiBNYWluIHJlbmRlciBtZXRob2QgLSBoYW5kbGVzIGNvbW1vbiBsb2dpY1xuICAgICAqL1xuICAgIGFzeW5jIHJlbmRlcihjb250ZXh0KSB7XG4gICAgICAgIGNvbnN0IGFsbG93ZWRJZHMgPSBjb250ZXh0LmZpbHRlclt0aGlzLnR5cGVdIHx8IFtdO1xuICAgICAgICBpZiAoYWxsb3dlZElkcy5sZW5ndGggPT09IDApXG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIGNvbnN0IGVudGl0aWVzID0gYXdhaXQgdGhpcy5nZXRFbnRpdGllcyhhbGxvd2VkSWRzKTtcbiAgICAgICAgY29uc3QgZGF0ZUNvdW50ID0gY29udGV4dC5maWx0ZXJbJ2RhdGUnXT8ubGVuZ3RoIHx8IDE7XG4gICAgICAgIGNvbnN0IGNoaWxkSWRzID0gY29udGV4dC5jaGlsZFR5cGUgPyBjb250ZXh0LmZpbHRlcltjb250ZXh0LmNoaWxkVHlwZV0gfHwgW10gOiBbXTtcbiAgICAgICAgZm9yIChjb25zdCBlbnRpdHkgb2YgZW50aXRpZXMpIHtcbiAgICAgICAgICAgIGNvbnN0IGVudGl0eUNoaWxkSWRzID0gY29udGV4dC5wYXJlbnRDaGlsZE1hcD8uW2VudGl0eS5pZF0gfHwgW107XG4gICAgICAgICAgICBjb25zdCBjaGlsZENvdW50ID0gZW50aXR5Q2hpbGRJZHMuZmlsdGVyKGlkID0+IGNoaWxkSWRzLmluY2x1ZGVzKGlkKSkubGVuZ3RoO1xuICAgICAgICAgICAgY29uc3QgY29sc3BhbiA9IGNoaWxkQ291bnQgKiBkYXRlQ291bnQ7XG4gICAgICAgICAgICBjb25zdCBoZWFkZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KHRoaXMuY29uZmlnLmVsZW1lbnRUYWcpO1xuICAgICAgICAgICAgaGVhZGVyLmRhdGFzZXRbdGhpcy5jb25maWcuaWRBdHRyaWJ1dGVdID0gZW50aXR5LmlkO1xuICAgICAgICAgICAgaGVhZGVyLnN0eWxlLnNldFByb3BlcnR5KHRoaXMuY29uZmlnLmNvbHNwYW5WYXIsIFN0cmluZyhjb2xzcGFuKSk7XG4gICAgICAgICAgICAvLyBBbGxvdyBzdWJjbGFzcyB0byBjdXN0b21pemUgaGVhZGVyIGNvbnRlbnRcbiAgICAgICAgICAgIHRoaXMucmVuZGVySGVhZGVyKGVudGl0eSwgaGVhZGVyLCBjb250ZXh0KTtcbiAgICAgICAgICAgIGNvbnRleHQuaGVhZGVyQ29udGFpbmVyLmFwcGVuZENoaWxkKGhlYWRlcik7XG4gICAgICAgIH1cbiAgICB9XG4gICAgLyoqXG4gICAgICogT3ZlcnJpZGUgdGhpcyBtZXRob2QgZm9yIGN1c3RvbSBoZWFkZXIgcmVuZGVyaW5nXG4gICAgICogRGVmYXVsdDoganVzdCBzZXRzIHRleHRDb250ZW50IHRvIGRpc3BsYXkgbmFtZVxuICAgICAqL1xuICAgIHJlbmRlckhlYWRlcihlbnRpdHksIGhlYWRlciwgX2NvbnRleHQpIHtcbiAgICAgICAgaGVhZGVyLnRleHRDb250ZW50ID0gdGhpcy5nZXREaXNwbGF5TmFtZShlbnRpdHkpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBIZWxwZXIgdG8gcmVuZGVyIGEgc2luZ2xlIGVudGl0eSBoZWFkZXIuXG4gICAgICogQ2FuIGJlIHVzZWQgYnkgc3ViY2xhc3NlcyB0aGF0IG92ZXJyaWRlIHJlbmRlcigpIGJ1dCB3YW50IGNvbnNpc3RlbnQgaGVhZGVyIGNyZWF0aW9uLlxuICAgICAqL1xuICAgIGNyZWF0ZUhlYWRlcihlbnRpdHksIGNvbnRleHQpIHtcbiAgICAgICAgY29uc3QgaGVhZGVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCh0aGlzLmNvbmZpZy5lbGVtZW50VGFnKTtcbiAgICAgICAgaGVhZGVyLmRhdGFzZXRbdGhpcy5jb25maWcuaWRBdHRyaWJ1dGVdID0gZW50aXR5LmlkO1xuICAgICAgICB0aGlzLnJlbmRlckhlYWRlcihlbnRpdHksIGhlYWRlciwgY29udGV4dCk7XG4gICAgICAgIHJldHVybiBoZWFkZXI7XG4gICAgfVxufVxuIiwgImltcG9ydCB7IEJhc2VHcm91cGluZ1JlbmRlcmVyIH0gZnJvbSAnLi4vLi4vY29yZS9CYXNlR3JvdXBpbmdSZW5kZXJlcic7XG5leHBvcnQgY2xhc3MgUmVzb3VyY2VSZW5kZXJlciBleHRlbmRzIEJhc2VHcm91cGluZ1JlbmRlcmVyIHtcbiAgICBjb25zdHJ1Y3RvcihyZXNvdXJjZVNlcnZpY2UpIHtcbiAgICAgICAgc3VwZXIoKTtcbiAgICAgICAgdGhpcy5yZXNvdXJjZVNlcnZpY2UgPSByZXNvdXJjZVNlcnZpY2U7XG4gICAgICAgIHRoaXMudHlwZSA9ICdyZXNvdXJjZSc7XG4gICAgICAgIHRoaXMuY29uZmlnID0ge1xuICAgICAgICAgICAgZWxlbWVudFRhZzogJ3N3cC1yZXNvdXJjZS1oZWFkZXInLFxuICAgICAgICAgICAgaWRBdHRyaWJ1dGU6ICdyZXNvdXJjZUlkJyxcbiAgICAgICAgICAgIGNvbHNwYW5WYXI6ICctLXJlc291cmNlLWNvbHMnXG4gICAgICAgIH07XG4gICAgfVxuICAgIGdldEVudGl0aWVzKGlkcykge1xuICAgICAgICByZXR1cm4gdGhpcy5yZXNvdXJjZVNlcnZpY2UuZ2V0QnlJZHMoaWRzKTtcbiAgICB9XG4gICAgZ2V0RGlzcGxheU5hbWUoZW50aXR5KSB7XG4gICAgICAgIHJldHVybiBlbnRpdHkuZGlzcGxheU5hbWU7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIE92ZXJyaWRlIHJlbmRlciB0byBoYW5kbGU6XG4gICAgICogMS4gU3BlY2lhbCBvcmRlcmluZyB3aGVuIHBhcmVudENoaWxkTWFwIGV4aXN0cyAocmVzb3VyY2VzIGdyb3VwZWQgYnkgcGFyZW50KVxuICAgICAqIDIuIERpZmZlcmVudCBjb2xzcGFuIGNhbGN1bGF0aW9uIChqdXN0IGRhdGVDb3VudCwgbm90IGNoaWxkQ291bnQgKiBkYXRlQ291bnQpXG4gICAgICovXG4gICAgYXN5bmMgcmVuZGVyKGNvbnRleHQpIHtcbiAgICAgICAgY29uc3QgcmVzb3VyY2VJZHMgPSBjb250ZXh0LmZpbHRlclsncmVzb3VyY2UnXSB8fCBbXTtcbiAgICAgICAgY29uc3QgZGF0ZUNvdW50ID0gY29udGV4dC5maWx0ZXJbJ2RhdGUnXT8ubGVuZ3RoIHx8IDE7XG4gICAgICAgIC8vIERldGVybWluZSByZW5kZXIgb3JkZXIgYmFzZWQgb24gcGFyZW50Q2hpbGRNYXBcbiAgICAgICAgLy8gSWYgcGFyZW50Q2hpbGRNYXAgZXhpc3RzLCByZW5kZXIgcmVzb3VyY2VzIGdyb3VwZWQgYnkgcGFyZW50IChlLmcuLCB0ZWFtKVxuICAgICAgICAvLyBPdGhlcndpc2UsIHJlbmRlciBpbiBmaWx0ZXIgb3JkZXJcbiAgICAgICAgbGV0IG9yZGVyZWRSZXNvdXJjZUlkcztcbiAgICAgICAgaWYgKGNvbnRleHQucGFyZW50Q2hpbGRNYXApIHtcbiAgICAgICAgICAgIC8vIFJlbmRlciByZXNvdXJjZXMgaW4gcGFyZW50LWNoaWxkIG9yZGVyXG4gICAgICAgICAgICBvcmRlcmVkUmVzb3VyY2VJZHMgPSBbXTtcbiAgICAgICAgICAgIGZvciAoY29uc3QgY2hpbGRJZHMgb2YgT2JqZWN0LnZhbHVlcyhjb250ZXh0LnBhcmVudENoaWxkTWFwKSkge1xuICAgICAgICAgICAgICAgIGZvciAoY29uc3QgY2hpbGRJZCBvZiBjaGlsZElkcykge1xuICAgICAgICAgICAgICAgICAgICBpZiAocmVzb3VyY2VJZHMuaW5jbHVkZXMoY2hpbGRJZCkpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIG9yZGVyZWRSZXNvdXJjZUlkcy5wdXNoKGNoaWxkSWQpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgb3JkZXJlZFJlc291cmNlSWRzID0gcmVzb3VyY2VJZHM7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgcmVzb3VyY2VzID0gYXdhaXQgdGhpcy5nZXRFbnRpdGllcyhvcmRlcmVkUmVzb3VyY2VJZHMpO1xuICAgICAgICAvLyBDcmVhdGUgYSBtYXAgZm9yIHF1aWNrIGxvb2t1cCB0byBwcmVzZXJ2ZSBvcmRlclxuICAgICAgICBjb25zdCByZXNvdXJjZU1hcCA9IG5ldyBNYXAocmVzb3VyY2VzLm1hcChyID0+IFtyLmlkLCByXSkpO1xuICAgICAgICBmb3IgKGNvbnN0IHJlc291cmNlSWQgb2Ygb3JkZXJlZFJlc291cmNlSWRzKSB7XG4gICAgICAgICAgICBjb25zdCByZXNvdXJjZSA9IHJlc291cmNlTWFwLmdldChyZXNvdXJjZUlkKTtcbiAgICAgICAgICAgIGlmICghcmVzb3VyY2UpXG4gICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICBjb25zdCBoZWFkZXIgPSB0aGlzLmNyZWF0ZUhlYWRlcihyZXNvdXJjZSwgY29udGV4dCk7XG4gICAgICAgICAgICBoZWFkZXIuc3R5bGUuZ3JpZENvbHVtbiA9IGBzcGFuICR7ZGF0ZUNvdW50fWA7XG4gICAgICAgICAgICBjb250ZXh0LmhlYWRlckNvbnRhaW5lci5hcHBlbmRDaGlsZChoZWFkZXIpO1xuICAgICAgICB9XG4gICAgfVxufVxuIiwgImltcG9ydCB7IEJhc2VHcm91cGluZ1JlbmRlcmVyIH0gZnJvbSAnLi4vLi4vY29yZS9CYXNlR3JvdXBpbmdSZW5kZXJlcic7XG5leHBvcnQgY2xhc3MgVGVhbVJlbmRlcmVyIGV4dGVuZHMgQmFzZUdyb3VwaW5nUmVuZGVyZXIge1xuICAgIGNvbnN0cnVjdG9yKHRlYW1TZXJ2aWNlKSB7XG4gICAgICAgIHN1cGVyKCk7XG4gICAgICAgIHRoaXMudGVhbVNlcnZpY2UgPSB0ZWFtU2VydmljZTtcbiAgICAgICAgdGhpcy50eXBlID0gJ3RlYW0nO1xuICAgICAgICB0aGlzLmNvbmZpZyA9IHtcbiAgICAgICAgICAgIGVsZW1lbnRUYWc6ICdzd3AtdGVhbS1oZWFkZXInLFxuICAgICAgICAgICAgaWRBdHRyaWJ1dGU6ICd0ZWFtSWQnLFxuICAgICAgICAgICAgY29sc3BhblZhcjogJy0tdGVhbS1jb2xzJ1xuICAgICAgICB9O1xuICAgIH1cbiAgICBnZXRFbnRpdGllcyhpZHMpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMudGVhbVNlcnZpY2UuZ2V0QnlJZHMoaWRzKTtcbiAgICB9XG4gICAgZ2V0RGlzcGxheU5hbWUoZW50aXR5KSB7XG4gICAgICAgIHJldHVybiBlbnRpdHkubmFtZTtcbiAgICB9XG59XG4iLCAiaW1wb3J0IHsgQmFzZUdyb3VwaW5nUmVuZGVyZXIgfSBmcm9tICcuLi8uLi9jb3JlL0Jhc2VHcm91cGluZ1JlbmRlcmVyJztcbmV4cG9ydCBjbGFzcyBEZXBhcnRtZW50UmVuZGVyZXIgZXh0ZW5kcyBCYXNlR3JvdXBpbmdSZW5kZXJlciB7XG4gICAgY29uc3RydWN0b3IoZGVwYXJ0bWVudFNlcnZpY2UpIHtcbiAgICAgICAgc3VwZXIoKTtcbiAgICAgICAgdGhpcy5kZXBhcnRtZW50U2VydmljZSA9IGRlcGFydG1lbnRTZXJ2aWNlO1xuICAgICAgICB0aGlzLnR5cGUgPSAnZGVwYXJ0bWVudCc7XG4gICAgICAgIHRoaXMuY29uZmlnID0ge1xuICAgICAgICAgICAgZWxlbWVudFRhZzogJ3N3cC1kZXBhcnRtZW50LWhlYWRlcicsXG4gICAgICAgICAgICBpZEF0dHJpYnV0ZTogJ2RlcGFydG1lbnRJZCcsXG4gICAgICAgICAgICBjb2xzcGFuVmFyOiAnLS1kZXBhcnRtZW50LWNvbHMnXG4gICAgICAgIH07XG4gICAgfVxuICAgIGdldEVudGl0aWVzKGlkcykge1xuICAgICAgICByZXR1cm4gdGhpcy5kZXBhcnRtZW50U2VydmljZS5nZXRCeUlkcyhpZHMpO1xuICAgIH1cbiAgICBnZXREaXNwbGF5TmFtZShlbnRpdHkpIHtcbiAgICAgICAgcmV0dXJuIGVudGl0eS5uYW1lO1xuICAgIH1cbn1cbiIsICJleHBvcnQgZnVuY3Rpb24gYnVpbGRQaXBlbGluZShyZW5kZXJlcnMpIHtcbiAgICByZXR1cm4ge1xuICAgICAgICBhc3luYyBydW4oY29udGV4dCkge1xuICAgICAgICAgICAgZm9yIChjb25zdCByZW5kZXJlciBvZiByZW5kZXJlcnMpIHtcbiAgICAgICAgICAgICAgICBhd2FpdCByZW5kZXJlci5yZW5kZXIoY29udGV4dCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9O1xufVxuIiwgIi8qKlxuICogRmlsdGVyVGVtcGxhdGUgLSBCeWdnZXIgblx1MDBGOGdsZXIgdGlsIGV2ZW50LWtvbG9ubmUgbWF0Y2hpbmdcbiAqXG4gKiBWaWV3Q29uZmlnIGRlZmluZXJlciBodmlsa2UgZmVsdGVyIChpZFByb3BlcnRpZXMpIGRlciBpbmRnXHUwMEU1ciBpIGtvbG9ubmVucyBuXHUwMEY4Z2xlLlxuICogU2FtbWUgdGVtcGxhdGUgYnJ1Z2VzIHRpbCBhdCBieWdnZSBuXHUwMEY4Z2xlIGZvciBiXHUwMEU1ZGUga29sb25uZSBvZyBldmVudC5cbiAqXG4gKiBTdXBwb3J0cyBkb3Qtbm90YXRpb24gZm9yIGhpZXJhcmNoaWNhbCByZWxhdGlvbnM6XG4gKiAtICdyZXNvdXJjZS50ZWFtSWQnIFx1MjE5MiBsb29rcyB1cCBldmVudC5yZXNvdXJjZUlkIFx1MjE5MiByZXNvdXJjZSBlbnRpdHkgXHUyMTkyIHRlYW1JZFxuICpcbiAqIFByaW5jaXA6IEtvbG9ubmVucyBuXHUwMEY4Z2xlLXRlbXBsYXRlIGJlc3RlbW1lciBodmFkIGRlciBtYXRjaGVzIHBcdTAwRTUuXG4gKlxuICogQHNlZSBkb2NzL2ZpbHRlci10ZW1wbGF0ZS5tZFxuICovXG5leHBvcnQgY2xhc3MgRmlsdGVyVGVtcGxhdGUge1xuICAgIGNvbnN0cnVjdG9yKGRhdGVTZXJ2aWNlLCBlbnRpdHlSZXNvbHZlcikge1xuICAgICAgICB0aGlzLmRhdGVTZXJ2aWNlID0gZGF0ZVNlcnZpY2U7XG4gICAgICAgIHRoaXMuZW50aXR5UmVzb2x2ZXIgPSBlbnRpdHlSZXNvbHZlcjtcbiAgICAgICAgdGhpcy5maWVsZHMgPSBbXTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogVGlsZlx1MDBGOGogZmVsdCB0aWwgdGVtcGxhdGVcbiAgICAgKiBAcGFyYW0gaWRQcm9wZXJ0eSAtIFByb3BlcnR5LW5hdm4gKGJydWdlcyBwXHUwMEU1IGJcdTAwRTVkZSBldmVudCBvZyBjb2x1bW4uZGF0YXNldClcbiAgICAgKiBAcGFyYW0gZGVyaXZlZEZyb20gLSBIdmlzIGZlbHRldCB1ZGxlZGVzIGZyYSBhbmRlbiBwcm9wZXJ0eSAoZi5la3MuIGRhdGUgZnJhIHN0YXJ0KVxuICAgICAqL1xuICAgIGFkZEZpZWxkKGlkUHJvcGVydHksIGRlcml2ZWRGcm9tKSB7XG4gICAgICAgIHRoaXMuZmllbGRzLnB1c2goeyBpZFByb3BlcnR5LCBkZXJpdmVkRnJvbSB9KTtcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFBhcnNlIGRvdC1ub3RhdGlvbiBzdHJpbmcgaW50byBjb21wb25lbnRzXG4gICAgICogQGV4YW1wbGUgJ3Jlc291cmNlLnRlYW1JZCcgXHUyMTkyIHsgZW50aXR5VHlwZTogJ3Jlc291cmNlJywgcHJvcGVydHk6ICd0ZWFtSWQnLCBmb3JlaWduS2V5OiAncmVzb3VyY2VJZCcgfVxuICAgICAqL1xuICAgIHBhcnNlRG90Tm90YXRpb24oaWRQcm9wZXJ0eSkge1xuICAgICAgICBpZiAoIWlkUHJvcGVydHkuaW5jbHVkZXMoJy4nKSlcbiAgICAgICAgICAgIHJldHVybiBudWxsO1xuICAgICAgICBjb25zdCBbZW50aXR5VHlwZSwgcHJvcGVydHldID0gaWRQcm9wZXJ0eS5zcGxpdCgnLicpO1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgZW50aXR5VHlwZSxcbiAgICAgICAgICAgIHByb3BlcnR5LFxuICAgICAgICAgICAgZm9yZWlnbktleTogZW50aXR5VHlwZSArICdJZCcgLy8gQ29udmVudGlvbjogcmVzb3VyY2UgXHUyMTkyIHJlc291cmNlSWRcbiAgICAgICAgfTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogR2V0IGRhdGFzZXQga2V5IGZvciBjb2x1bW4gbG9va3VwXG4gICAgICogRm9yIGRvdC1ub3RhdGlvbiAncmVzb3VyY2UudGVhbUlkJywgd2UgbG9vayBmb3IgJ3RlYW1JZCcgaW4gZGF0YXNldFxuICAgICAqL1xuICAgIGdldERhdGFzZXRLZXkoaWRQcm9wZXJ0eSkge1xuICAgICAgICBjb25zdCBkb3ROb3RhdGlvbiA9IHRoaXMucGFyc2VEb3ROb3RhdGlvbihpZFByb3BlcnR5KTtcbiAgICAgICAgaWYgKGRvdE5vdGF0aW9uKSB7XG4gICAgICAgICAgICByZXR1cm4gZG90Tm90YXRpb24ucHJvcGVydHk7IC8vICd0ZWFtSWQnXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGlkUHJvcGVydHk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEJ5ZyBuXHUwMEY4Z2xlIGZyYSBrb2xvbm5lXG4gICAgICogTFx1MDBFNnNlciB2XHUwMEU2cmRpZXIgZnJhIGNvbHVtbi5kYXRhc2V0W2lkUHJvcGVydHldXG4gICAgICogRm9yIGRvdC1ub3RhdGlvbiwgdXNlcyB0aGUgcHJvcGVydHkgcGFydCAocmVzb3VyY2UudGVhbUlkIFx1MjE5MiB0ZWFtSWQpXG4gICAgICovXG4gICAgYnVpbGRLZXlGcm9tQ29sdW1uKGNvbHVtbikge1xuICAgICAgICByZXR1cm4gdGhpcy5maWVsZHNcbiAgICAgICAgICAgIC5tYXAoZiA9PiB7XG4gICAgICAgICAgICBjb25zdCBrZXkgPSB0aGlzLmdldERhdGFzZXRLZXkoZi5pZFByb3BlcnR5KTtcbiAgICAgICAgICAgIHJldHVybiBjb2x1bW4uZGF0YXNldFtrZXldIHx8ICcnO1xuICAgICAgICB9KVxuICAgICAgICAgICAgLmpvaW4oJzonKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogQnlnIG5cdTAwRjhnbGUgZnJhIGV2ZW50XG4gICAgICogTFx1MDBFNnNlciB2XHUwMEU2cmRpZXIgZnJhIGV2ZW50W2lkUHJvcGVydHldIGVsbGVyIHVkbGVkZXIgZnJhIGRlcml2ZWRGcm9tXG4gICAgICogRm9yIGRvdC1ub3RhdGlvbiwgcmVzb2x2ZXMgdmlhIEVudGl0eVJlc29sdmVyXG4gICAgICovXG4gICAgYnVpbGRLZXlGcm9tRXZlbnQoZXZlbnQpIHtcbiAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1leHBsaWNpdC1hbnlcbiAgICAgICAgY29uc3QgZXZlbnRSZWNvcmQgPSBldmVudDtcbiAgICAgICAgcmV0dXJuIHRoaXMuZmllbGRzXG4gICAgICAgICAgICAubWFwKGYgPT4ge1xuICAgICAgICAgICAgLy8gQ2hlY2sgZm9yIGRvdC1ub3RhdGlvbiAoZS5nLiwgJ3Jlc291cmNlLnRlYW1JZCcpXG4gICAgICAgICAgICBjb25zdCBkb3ROb3RhdGlvbiA9IHRoaXMucGFyc2VEb3ROb3RhdGlvbihmLmlkUHJvcGVydHkpO1xuICAgICAgICAgICAgaWYgKGRvdE5vdGF0aW9uKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMucmVzb2x2ZURvdE5vdGF0aW9uKGV2ZW50UmVjb3JkLCBkb3ROb3RhdGlvbik7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAoZi5kZXJpdmVkRnJvbSkge1xuICAgICAgICAgICAgICAgIC8vIFVkbGVkIHZcdTAwRTZyZGkgKGYuZWtzLiBkYXRlIGZyYSBzdGFydClcbiAgICAgICAgICAgICAgICBjb25zdCBzb3VyY2VWYWx1ZSA9IGV2ZW50UmVjb3JkW2YuZGVyaXZlZEZyb21dO1xuICAgICAgICAgICAgICAgIGlmIChzb3VyY2VWYWx1ZSBpbnN0YW5jZW9mIERhdGUpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMuZGF0ZVNlcnZpY2UuZ2V0RGF0ZUtleShzb3VyY2VWYWx1ZSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIHJldHVybiBTdHJpbmcoc291cmNlVmFsdWUgfHwgJycpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIFN0cmluZyhldmVudFJlY29yZFtmLmlkUHJvcGVydHldIHx8ICcnKTtcbiAgICAgICAgfSlcbiAgICAgICAgICAgIC5qb2luKCc6Jyk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFJlc29sdmUgZG90LW5vdGF0aW9uIHJlZmVyZW5jZSB2aWEgRW50aXR5UmVzb2x2ZXJcbiAgICAgKi9cbiAgICByZXNvbHZlRG90Tm90YXRpb24oZXZlbnRSZWNvcmQsIGRvdE5vdGF0aW9uKSB7XG4gICAgICAgIGlmICghdGhpcy5lbnRpdHlSZXNvbHZlcikge1xuICAgICAgICAgICAgY29uc29sZS53YXJuKGBGaWx0ZXJUZW1wbGF0ZTogRW50aXR5UmVzb2x2ZXIgcmVxdWlyZWQgZm9yIGRvdC1ub3RhdGlvbiAnJHtkb3ROb3RhdGlvbi5lbnRpdHlUeXBlfS4ke2RvdE5vdGF0aW9uLnByb3BlcnR5fSdgKTtcbiAgICAgICAgICAgIHJldHVybiAnJztcbiAgICAgICAgfVxuICAgICAgICAvLyBHZXQgZm9yZWlnbiBrZXkgdmFsdWUgZnJvbSBldmVudCAoZS5nLiwgcmVzb3VyY2VJZClcbiAgICAgICAgY29uc3QgZm9yZWlnbklkID0gZXZlbnRSZWNvcmRbZG90Tm90YXRpb24uZm9yZWlnbktleV07XG4gICAgICAgIGlmICghZm9yZWlnbklkKVxuICAgICAgICAgICAgcmV0dXJuICcnO1xuICAgICAgICAvLyBSZXNvbHZlIGVudGl0eVxuICAgICAgICBjb25zdCBlbnRpdHkgPSB0aGlzLmVudGl0eVJlc29sdmVyLnJlc29sdmUoZG90Tm90YXRpb24uZW50aXR5VHlwZSwgU3RyaW5nKGZvcmVpZ25JZCkpO1xuICAgICAgICBpZiAoIWVudGl0eSlcbiAgICAgICAgICAgIHJldHVybiAnJztcbiAgICAgICAgLy8gUmV0dXJuIHByb3BlcnR5IHZhbHVlIGZyb20gZW50aXR5XG4gICAgICAgIHJldHVybiBTdHJpbmcoZW50aXR5W2RvdE5vdGF0aW9uLnByb3BlcnR5XSB8fCAnJyk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIE1hdGNoIGV2ZW50IG1vZCBrb2xvbm5lXG4gICAgICovXG4gICAgbWF0Y2hlcyhldmVudCwgY29sdW1uKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmJ1aWxkS2V5RnJvbUV2ZW50KGV2ZW50KSA9PT0gdGhpcy5idWlsZEtleUZyb21Db2x1bW4oY29sdW1uKTtcbiAgICB9XG59XG4iLCAiaW1wb3J0IHsgYnVpbGRQaXBlbGluZSB9IGZyb20gJy4vUmVuZGVyQnVpbGRlcic7XG5pbXBvcnQgeyBGaWx0ZXJUZW1wbGF0ZSB9IGZyb20gJy4vRmlsdGVyVGVtcGxhdGUnO1xuZXhwb3J0IGNsYXNzIENhbGVuZGFyT3JjaGVzdHJhdG9yIHtcbiAgICBjb25zdHJ1Y3RvcihhbGxSZW5kZXJlcnMsIGV2ZW50UmVuZGVyZXIsIHNjaGVkdWxlUmVuZGVyZXIsIGhlYWRlckRyYXdlclJlbmRlcmVyLCBkYXRlU2VydmljZSwgZW50aXR5U2VydmljZXMpIHtcbiAgICAgICAgdGhpcy5hbGxSZW5kZXJlcnMgPSBhbGxSZW5kZXJlcnM7XG4gICAgICAgIHRoaXMuZXZlbnRSZW5kZXJlciA9IGV2ZW50UmVuZGVyZXI7XG4gICAgICAgIHRoaXMuc2NoZWR1bGVSZW5kZXJlciA9IHNjaGVkdWxlUmVuZGVyZXI7XG4gICAgICAgIHRoaXMuaGVhZGVyRHJhd2VyUmVuZGVyZXIgPSBoZWFkZXJEcmF3ZXJSZW5kZXJlcjtcbiAgICAgICAgdGhpcy5kYXRlU2VydmljZSA9IGRhdGVTZXJ2aWNlO1xuICAgICAgICB0aGlzLmVudGl0eVNlcnZpY2VzID0gZW50aXR5U2VydmljZXM7XG4gICAgfVxuICAgIGFzeW5jIHJlbmRlcih2aWV3Q29uZmlnLCBjb250YWluZXIpIHtcbiAgICAgICAgY29uc3QgaGVhZGVyQ29udGFpbmVyID0gY29udGFpbmVyLnF1ZXJ5U2VsZWN0b3IoJ3N3cC1jYWxlbmRhci1oZWFkZXInKTtcbiAgICAgICAgY29uc3QgY29sdW1uQ29udGFpbmVyID0gY29udGFpbmVyLnF1ZXJ5U2VsZWN0b3IoJ3N3cC1kYXktY29sdW1ucycpO1xuICAgICAgICBpZiAoIWhlYWRlckNvbnRhaW5lciB8fCAhY29sdW1uQ29udGFpbmVyKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ01pc3Npbmcgc3dwLWNhbGVuZGFyLWhlYWRlciBvciBzd3AtZGF5LWNvbHVtbnMnKTtcbiAgICAgICAgfVxuICAgICAgICAvLyBCeWcgZmlsdGVyIGZyYSB2aWV3Q29uZmlnXG4gICAgICAgIGNvbnN0IGZpbHRlciA9IHt9O1xuICAgICAgICBmb3IgKGNvbnN0IGdyb3VwaW5nIG9mIHZpZXdDb25maWcuZ3JvdXBpbmdzKSB7XG4gICAgICAgICAgICBmaWx0ZXJbZ3JvdXBpbmcudHlwZV0gPSBncm91cGluZy52YWx1ZXM7XG4gICAgICAgIH1cbiAgICAgICAgLy8gQnlnIEZpbHRlclRlbXBsYXRlIGZyYSB2aWV3Q29uZmlnIGdyb3VwaW5ncyAoa3VuIGRlIG1lZCBpZFByb3BlcnR5KVxuICAgICAgICBjb25zdCBmaWx0ZXJUZW1wbGF0ZSA9IG5ldyBGaWx0ZXJUZW1wbGF0ZSh0aGlzLmRhdGVTZXJ2aWNlKTtcbiAgICAgICAgZm9yIChjb25zdCBncm91cGluZyBvZiB2aWV3Q29uZmlnLmdyb3VwaW5ncykge1xuICAgICAgICAgICAgaWYgKGdyb3VwaW5nLmlkUHJvcGVydHkpIHtcbiAgICAgICAgICAgICAgICBmaWx0ZXJUZW1wbGF0ZS5hZGRGaWVsZChncm91cGluZy5pZFByb3BlcnR5LCBncm91cGluZy5kZXJpdmVkRnJvbSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgLy8gUmVzb2x2ZSBiZWxvbmdzVG8gcmVsYXRpb25zIChlLmcuLCB0ZWFtLnJlc291cmNlSWRzKVxuICAgICAgICBjb25zdCB7IHBhcmVudENoaWxkTWFwLCBjaGlsZFR5cGUgfSA9IGF3YWl0IHRoaXMucmVzb2x2ZUJlbG9uZ3NUbyh2aWV3Q29uZmlnLmdyb3VwaW5ncywgZmlsdGVyKTtcbiAgICAgICAgY29uc3QgY29udGV4dCA9IHsgaGVhZGVyQ29udGFpbmVyLCBjb2x1bW5Db250YWluZXIsIGZpbHRlciwgZ3JvdXBpbmdzOiB2aWV3Q29uZmlnLmdyb3VwaW5ncywgcGFyZW50Q2hpbGRNYXAsIGNoaWxkVHlwZSB9O1xuICAgICAgICAvLyBDbGVhclxuICAgICAgICBoZWFkZXJDb250YWluZXIuaW5uZXJIVE1MID0gJyc7XG4gICAgICAgIGNvbHVtbkNvbnRhaW5lci5pbm5lckhUTUwgPSAnJztcbiAgICAgICAgLy8gU1x1MDBFNnQgZGF0YS1sZXZlbHMgYXR0cmlidXQgZm9yIENTUyBncmlkLXJvdyBzdHlsaW5nXG4gICAgICAgIGNvbnN0IGxldmVscyA9IHZpZXdDb25maWcuZ3JvdXBpbmdzLm1hcChnID0+IGcudHlwZSkuam9pbignICcpO1xuICAgICAgICBoZWFkZXJDb250YWluZXIuZGF0YXNldC5sZXZlbHMgPSBsZXZlbHM7XG4gICAgICAgIC8vIFZcdTAwRTZsZyByZW5kZXJlcnMgYmFzZXJldCBwXHUwMEU1IGdyb3VwaW5ncyB0eXBlc1xuICAgICAgICBjb25zdCBhY3RpdmVSZW5kZXJlcnMgPSB0aGlzLnNlbGVjdFJlbmRlcmVycyh2aWV3Q29uZmlnKTtcbiAgICAgICAgLy8gQnlnIG9nIGtcdTAwRjhyIHBpcGVsaW5lXG4gICAgICAgIGNvbnN0IHBpcGVsaW5lID0gYnVpbGRQaXBlbGluZShhY3RpdmVSZW5kZXJlcnMpO1xuICAgICAgICBhd2FpdCBwaXBlbGluZS5ydW4oY29udGV4dCk7XG4gICAgICAgIC8vIFJlbmRlciBzY2hlZHVsZSB1bmF2YWlsYWJsZSB6b25lcyAoZlx1MDBGOHIgZXZlbnRzKVxuICAgICAgICBhd2FpdCB0aGlzLnNjaGVkdWxlUmVuZGVyZXIucmVuZGVyKGNvbnRhaW5lciwgZmlsdGVyKTtcbiAgICAgICAgLy8gUmVuZGVyIHRpbWVkIGV2ZW50cyBpbiBncmlkIChtZWQgZmlsdGVyVGVtcGxhdGUgdGlsIG1hdGNoaW5nKVxuICAgICAgICBhd2FpdCB0aGlzLmV2ZW50UmVuZGVyZXIucmVuZGVyKGNvbnRhaW5lciwgZmlsdGVyLCBmaWx0ZXJUZW1wbGF0ZSk7XG4gICAgICAgIC8vIFJlbmRlciBhbGxEYXkgZXZlbnRzIGluIGhlYWRlciBkcmF3ZXIgKG1lZCBmaWx0ZXJUZW1wbGF0ZSB0aWwgbWF0Y2hpbmcpXG4gICAgICAgIGF3YWl0IHRoaXMuaGVhZGVyRHJhd2VyUmVuZGVyZXIucmVuZGVyKGNvbnRhaW5lciwgZmlsdGVyLCBmaWx0ZXJUZW1wbGF0ZSk7XG4gICAgfVxuICAgIHNlbGVjdFJlbmRlcmVycyh2aWV3Q29uZmlnKSB7XG4gICAgICAgIGNvbnN0IHR5cGVzID0gdmlld0NvbmZpZy5ncm91cGluZ3MubWFwKGcgPT4gZy50eXBlKTtcbiAgICAgICAgLy8gU29ydFx1MDBFOXIgcmVuZGVyZXJzIGkgc2FtbWUgclx1MDBFNmtrZWZcdTAwRjhsZ2Ugc29tIHZpZXdDb25maWcuZ3JvdXBpbmdzXG4gICAgICAgIHJldHVybiB0eXBlc1xuICAgICAgICAgICAgLm1hcCh0eXBlID0+IHRoaXMuYWxsUmVuZGVyZXJzLmZpbmQociA9PiByLnR5cGUgPT09IHR5cGUpKVxuICAgICAgICAgICAgLmZpbHRlcigocikgPT4gciAhPT0gdW5kZWZpbmVkKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogUmVzb2x2ZSBiZWxvbmdzVG8gcmVsYXRpb25zIHRvIGJ1aWxkIHBhcmVudC1jaGlsZCBtYXBcbiAgICAgKiBlLmcuLCBiZWxvbmdzVG86ICd0ZWFtLnJlc291cmNlSWRzJyBcdTIxOTIgeyB0ZWFtMTogWydFTVAwMDEnLCAnRU1QMDAyJ10sIHRlYW0yOiBbLi4uXSB9XG4gICAgICogQWxzbyByZXR1cm5zIHRoZSBjaGlsZFR5cGUgKHRoZSBncm91cGluZyB0eXBlIHRoYXQgaGFzIGJlbG9uZ3NUbylcbiAgICAgKi9cbiAgICBhc3luYyByZXNvbHZlQmVsb25nc1RvKGdyb3VwaW5ncywgZmlsdGVyKSB7XG4gICAgICAgIC8vIEZpbmQgZ3JvdXBpbmcgd2l0aCBiZWxvbmdzVG9cbiAgICAgICAgY29uc3QgY2hpbGRHcm91cGluZyA9IGdyb3VwaW5ncy5maW5kKGcgPT4gZy5iZWxvbmdzVG8pO1xuICAgICAgICBpZiAoIWNoaWxkR3JvdXBpbmc/LmJlbG9uZ3NUbylcbiAgICAgICAgICAgIHJldHVybiB7fTtcbiAgICAgICAgLy8gUGFyc2UgYmVsb25nc1RvOiAndGVhbS5yZXNvdXJjZUlkcydcbiAgICAgICAgY29uc3QgW2VudGl0eVR5cGUsIHByb3BlcnR5XSA9IGNoaWxkR3JvdXBpbmcuYmVsb25nc1RvLnNwbGl0KCcuJyk7XG4gICAgICAgIGlmICghZW50aXR5VHlwZSB8fCAhcHJvcGVydHkpXG4gICAgICAgICAgICByZXR1cm4ge307XG4gICAgICAgIC8vIEdldCBwYXJlbnQgSURzIGZyb20gZmlsdGVyXG4gICAgICAgIGNvbnN0IHBhcmVudElkcyA9IGZpbHRlcltlbnRpdHlUeXBlXSB8fCBbXTtcbiAgICAgICAgaWYgKHBhcmVudElkcy5sZW5ndGggPT09IDApXG4gICAgICAgICAgICByZXR1cm4ge307XG4gICAgICAgIC8vIEZpbmQgc2VydmljZSBkeW5hbWlzayBiYXNlcmV0IHBcdTAwRTUgZW50aXR5VHlwZSAoaW5nZW4gaGFyZGNvZGVkIHR5cGUgY2hlY2spXG4gICAgICAgIGNvbnN0IHNlcnZpY2UgPSB0aGlzLmVudGl0eVNlcnZpY2VzLmZpbmQocyA9PiBzLmVudGl0eVR5cGUudG9Mb3dlckNhc2UoKSA9PT0gZW50aXR5VHlwZSk7XG4gICAgICAgIGlmICghc2VydmljZSlcbiAgICAgICAgICAgIHJldHVybiB7fTtcbiAgICAgICAgLy8gSGVudCBhbGxlIGVudGl0aWVzIG9nIGZpbHRyZXIgcFx1MDBFNSBwYXJlbnRJZHNcbiAgICAgICAgY29uc3QgYWxsRW50aXRpZXMgPSBhd2FpdCBzZXJ2aWNlLmdldEFsbCgpO1xuICAgICAgICBjb25zdCBlbnRpdGllcyA9IGFsbEVudGl0aWVzLmZpbHRlcihlID0+IHBhcmVudElkcy5pbmNsdWRlcyhlLmlkKSk7XG4gICAgICAgIC8vIEJ5ZyBwYXJlbnQtY2hpbGQgbWFwXG4gICAgICAgIGNvbnN0IG1hcCA9IHt9O1xuICAgICAgICBmb3IgKGNvbnN0IGVudGl0eSBvZiBlbnRpdGllcykge1xuICAgICAgICAgICAgY29uc3QgZW50aXR5UmVjb3JkID0gZW50aXR5O1xuICAgICAgICAgICAgY29uc3QgY2hpbGRyZW4gPSBlbnRpdHlSZWNvcmRbcHJvcGVydHldIHx8IFtdO1xuICAgICAgICAgICAgbWFwW2VudGl0eVJlY29yZC5pZF0gPSBjaGlsZHJlbjtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4geyBwYXJlbnRDaGlsZE1hcDogbWFwLCBjaGlsZFR5cGU6IGNoaWxkR3JvdXBpbmcudHlwZSB9O1xuICAgIH1cbn1cbiIsICJleHBvcnQgY2xhc3MgTmF2aWdhdGlvbkFuaW1hdG9yIHtcbiAgICBjb25zdHJ1Y3RvcihoZWFkZXJUcmFjaywgY29udGVudFRyYWNrLCBoZWFkZXJEcmF3ZXIpIHtcbiAgICAgICAgdGhpcy5oZWFkZXJUcmFjayA9IGhlYWRlclRyYWNrO1xuICAgICAgICB0aGlzLmNvbnRlbnRUcmFjayA9IGNvbnRlbnRUcmFjaztcbiAgICAgICAgdGhpcy5oZWFkZXJEcmF3ZXIgPSBoZWFkZXJEcmF3ZXI7XG4gICAgfVxuICAgIGFzeW5jIHNsaWRlKGRpcmVjdGlvbiwgcmVuZGVyRm4pIHtcbiAgICAgICAgY29uc3Qgb3V0ID0gZGlyZWN0aW9uID09PSAnbGVmdCcgPyAnLTEwMCUnIDogJzEwMCUnO1xuICAgICAgICBjb25zdCBpbnRvID0gZGlyZWN0aW9uID09PSAnbGVmdCcgPyAnMTAwJScgOiAnLTEwMCUnO1xuICAgICAgICBhd2FpdCB0aGlzLmFuaW1hdGVPdXQob3V0KTtcbiAgICAgICAgYXdhaXQgcmVuZGVyRm4oKTtcbiAgICAgICAgYXdhaXQgdGhpcy5hbmltYXRlSW4oaW50byk7XG4gICAgfVxuICAgIGFzeW5jIGFuaW1hdGVPdXQodHJhbnNsYXRlKSB7XG4gICAgICAgIGNvbnN0IGFuaW1hdGlvbnMgPSBbXG4gICAgICAgICAgICB0aGlzLmhlYWRlclRyYWNrLmFuaW1hdGUoW3sgdHJhbnNmb3JtOiAndHJhbnNsYXRlWCgwKScgfSwgeyB0cmFuc2Zvcm06IGB0cmFuc2xhdGVYKCR7dHJhbnNsYXRlfSlgIH1dLCB7IGR1cmF0aW9uOiAyMDAsIGVhc2luZzogJ2Vhc2UtaW4nIH0pLmZpbmlzaGVkLFxuICAgICAgICAgICAgdGhpcy5jb250ZW50VHJhY2suYW5pbWF0ZShbeyB0cmFuc2Zvcm06ICd0cmFuc2xhdGVYKDApJyB9LCB7IHRyYW5zZm9ybTogYHRyYW5zbGF0ZVgoJHt0cmFuc2xhdGV9KWAgfV0sIHsgZHVyYXRpb246IDIwMCwgZWFzaW5nOiAnZWFzZS1pbicgfSkuZmluaXNoZWRcbiAgICAgICAgXTtcbiAgICAgICAgaWYgKHRoaXMuaGVhZGVyRHJhd2VyKSB7XG4gICAgICAgICAgICBhbmltYXRpb25zLnB1c2godGhpcy5oZWFkZXJEcmF3ZXIuYW5pbWF0ZShbeyB0cmFuc2Zvcm06ICd0cmFuc2xhdGVYKDApJyB9LCB7IHRyYW5zZm9ybTogYHRyYW5zbGF0ZVgoJHt0cmFuc2xhdGV9KWAgfV0sIHsgZHVyYXRpb246IDIwMCwgZWFzaW5nOiAnZWFzZS1pbicgfSkuZmluaXNoZWQpO1xuICAgICAgICB9XG4gICAgICAgIGF3YWl0IFByb21pc2UuYWxsKGFuaW1hdGlvbnMpO1xuICAgIH1cbiAgICBhc3luYyBhbmltYXRlSW4odHJhbnNsYXRlKSB7XG4gICAgICAgIGNvbnN0IGFuaW1hdGlvbnMgPSBbXG4gICAgICAgICAgICB0aGlzLmhlYWRlclRyYWNrLmFuaW1hdGUoW3sgdHJhbnNmb3JtOiBgdHJhbnNsYXRlWCgke3RyYW5zbGF0ZX0pYCB9LCB7IHRyYW5zZm9ybTogJ3RyYW5zbGF0ZVgoMCknIH1dLCB7IGR1cmF0aW9uOiAyMDAsIGVhc2luZzogJ2Vhc2Utb3V0JyB9KS5maW5pc2hlZCxcbiAgICAgICAgICAgIHRoaXMuY29udGVudFRyYWNrLmFuaW1hdGUoW3sgdHJhbnNmb3JtOiBgdHJhbnNsYXRlWCgke3RyYW5zbGF0ZX0pYCB9LCB7IHRyYW5zZm9ybTogJ3RyYW5zbGF0ZVgoMCknIH1dLCB7IGR1cmF0aW9uOiAyMDAsIGVhc2luZzogJ2Vhc2Utb3V0JyB9KS5maW5pc2hlZFxuICAgICAgICBdO1xuICAgICAgICBpZiAodGhpcy5oZWFkZXJEcmF3ZXIpIHtcbiAgICAgICAgICAgIGFuaW1hdGlvbnMucHVzaCh0aGlzLmhlYWRlckRyYXdlci5hbmltYXRlKFt7IHRyYW5zZm9ybTogYHRyYW5zbGF0ZVgoJHt0cmFuc2xhdGV9KWAgfSwgeyB0cmFuc2Zvcm06ICd0cmFuc2xhdGVYKDApJyB9XSwgeyBkdXJhdGlvbjogMjAwLCBlYXNpbmc6ICdlYXNlLW91dCcgfSkuZmluaXNoZWQpO1xuICAgICAgICB9XG4gICAgICAgIGF3YWl0IFByb21pc2UuYWxsKGFuaW1hdGlvbnMpO1xuICAgIH1cbn1cbiIsICIvKipcbiAqIENhbGVuZGFyRXZlbnRzIC0gQ29tbWFuZCBhbmQgc3RhdHVzIGV2ZW50cyBmb3IgQ2FsZW5kYXJBcHBcbiAqL1xuZXhwb3J0IGNvbnN0IENhbGVuZGFyRXZlbnRzID0ge1xuICAgIC8vIENvbW1hbmQgZXZlbnRzIChob3N0IFx1MjE5MiBjYWxlbmRhcilcbiAgICBDTURfTkFWSUdBVEVfUFJFVjogJ2NhbGVuZGFyOmNtZDpuYXZpZ2F0ZTpwcmV2JyxcbiAgICBDTURfTkFWSUdBVEVfTkVYVDogJ2NhbGVuZGFyOmNtZDpuYXZpZ2F0ZTpuZXh0JyxcbiAgICBDTURfRFJBV0VSX1RPR0dMRTogJ2NhbGVuZGFyOmNtZDpkcmF3ZXI6dG9nZ2xlJyxcbiAgICBDTURfUkVOREVSOiAnY2FsZW5kYXI6Y21kOnJlbmRlcicsXG4gICAgQ01EX1dPUktXRUVLX0NIQU5HRTogJ2NhbGVuZGFyOmNtZDp3b3Jrd2VlazpjaGFuZ2UnLFxuICAgIENNRF9WSUVXX1VQREFURTogJ2NhbGVuZGFyOmNtZDp2aWV3OnVwZGF0ZSdcbn07XG4iLCAiaW1wb3J0IHsgTmF2aWdhdGlvbkFuaW1hdG9yIH0gZnJvbSAnLi9OYXZpZ2F0aW9uQW5pbWF0b3InO1xuaW1wb3J0IHsgQ2FsZW5kYXJFdmVudHMgfSBmcm9tICcuL0NhbGVuZGFyRXZlbnRzJztcbmV4cG9ydCBjbGFzcyBDYWxlbmRhckFwcCB7XG4gICAgY29uc3RydWN0b3Iob3JjaGVzdHJhdG9yLCB0aW1lQXhpc1JlbmRlcmVyLCBkYXRlU2VydmljZSwgc2Nyb2xsTWFuYWdlciwgaGVhZGVyRHJhd2VyTWFuYWdlciwgZHJhZ0Ryb3BNYW5hZ2VyLCBlZGdlU2Nyb2xsTWFuYWdlciwgcmVzaXplTWFuYWdlciwgaGVhZGVyRHJhd2VyUmVuZGVyZXIsIGV2ZW50UGVyc2lzdGVuY2VNYW5hZ2VyLCBzZXR0aW5nc1NlcnZpY2UsIHZpZXdDb25maWdTZXJ2aWNlLCBldmVudEJ1cykge1xuICAgICAgICB0aGlzLm9yY2hlc3RyYXRvciA9IG9yY2hlc3RyYXRvcjtcbiAgICAgICAgdGhpcy50aW1lQXhpc1JlbmRlcmVyID0gdGltZUF4aXNSZW5kZXJlcjtcbiAgICAgICAgdGhpcy5kYXRlU2VydmljZSA9IGRhdGVTZXJ2aWNlO1xuICAgICAgICB0aGlzLnNjcm9sbE1hbmFnZXIgPSBzY3JvbGxNYW5hZ2VyO1xuICAgICAgICB0aGlzLmhlYWRlckRyYXdlck1hbmFnZXIgPSBoZWFkZXJEcmF3ZXJNYW5hZ2VyO1xuICAgICAgICB0aGlzLmRyYWdEcm9wTWFuYWdlciA9IGRyYWdEcm9wTWFuYWdlcjtcbiAgICAgICAgdGhpcy5lZGdlU2Nyb2xsTWFuYWdlciA9IGVkZ2VTY3JvbGxNYW5hZ2VyO1xuICAgICAgICB0aGlzLnJlc2l6ZU1hbmFnZXIgPSByZXNpemVNYW5hZ2VyO1xuICAgICAgICB0aGlzLmhlYWRlckRyYXdlclJlbmRlcmVyID0gaGVhZGVyRHJhd2VyUmVuZGVyZXI7XG4gICAgICAgIHRoaXMuZXZlbnRQZXJzaXN0ZW5jZU1hbmFnZXIgPSBldmVudFBlcnNpc3RlbmNlTWFuYWdlcjtcbiAgICAgICAgdGhpcy5zZXR0aW5nc1NlcnZpY2UgPSBzZXR0aW5nc1NlcnZpY2U7XG4gICAgICAgIHRoaXMudmlld0NvbmZpZ1NlcnZpY2UgPSB2aWV3Q29uZmlnU2VydmljZTtcbiAgICAgICAgdGhpcy5ldmVudEJ1cyA9IGV2ZW50QnVzO1xuICAgICAgICB0aGlzLmRheU9mZnNldCA9IDA7XG4gICAgICAgIHRoaXMuY3VycmVudFZpZXdJZCA9ICdzaW1wbGUnO1xuICAgICAgICB0aGlzLndvcmt3ZWVrUHJlc2V0ID0gbnVsbDtcbiAgICAgICAgdGhpcy5ncm91cGluZ092ZXJyaWRlcyA9IG5ldyBNYXAoKTtcbiAgICB9XG4gICAgYXN5bmMgaW5pdChjb250YWluZXIpIHtcbiAgICAgICAgdGhpcy5jb250YWluZXIgPSBjb250YWluZXI7XG4gICAgICAgIC8vIExvYWQgc2V0dGluZ3NcbiAgICAgICAgY29uc3QgZ3JpZFNldHRpbmdzID0gYXdhaXQgdGhpcy5zZXR0aW5nc1NlcnZpY2UuZ2V0R3JpZFNldHRpbmdzKCk7XG4gICAgICAgIGlmICghZ3JpZFNldHRpbmdzKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0dyaWRTZXR0aW5ncyBub3QgZm91bmQnKTtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLndvcmt3ZWVrUHJlc2V0ID0gYXdhaXQgdGhpcy5zZXR0aW5nc1NlcnZpY2UuZ2V0RGVmYXVsdFdvcmt3ZWVrUHJlc2V0KCk7XG4gICAgICAgIC8vIENyZWF0ZSBOYXZpZ2F0aW9uQW5pbWF0b3Igd2l0aCBET00gZWxlbWVudHNcbiAgICAgICAgdGhpcy5hbmltYXRvciA9IG5ldyBOYXZpZ2F0aW9uQW5pbWF0b3IoY29udGFpbmVyLnF1ZXJ5U2VsZWN0b3IoJ3N3cC1oZWFkZXItdHJhY2snKSwgY29udGFpbmVyLnF1ZXJ5U2VsZWN0b3IoJ3N3cC1jb250ZW50LXRyYWNrJyksIGNvbnRhaW5lci5xdWVyeVNlbGVjdG9yKCdzd3AtaGVhZGVyLWRyYXdlcicpKTtcbiAgICAgICAgLy8gUmVuZGVyIHRpbWUgYXhpcyBmcm9tIHNldHRpbmdzXG4gICAgICAgIHRoaXMudGltZUF4aXNSZW5kZXJlci5yZW5kZXIoY29udGFpbmVyLnF1ZXJ5U2VsZWN0b3IoJyN0aW1lLWF4aXMnKSwgZ3JpZFNldHRpbmdzLmRheVN0YXJ0SG91ciwgZ3JpZFNldHRpbmdzLmRheUVuZEhvdXIpO1xuICAgICAgICAvLyBJbml0IG1hbmFnZXJzXG4gICAgICAgIHRoaXMuc2Nyb2xsTWFuYWdlci5pbml0KGNvbnRhaW5lcik7XG4gICAgICAgIHRoaXMuaGVhZGVyRHJhd2VyTWFuYWdlci5pbml0KGNvbnRhaW5lcik7XG4gICAgICAgIHRoaXMuZHJhZ0Ryb3BNYW5hZ2VyLmluaXQoY29udGFpbmVyKTtcbiAgICAgICAgdGhpcy5yZXNpemVNYW5hZ2VyLmluaXQoY29udGFpbmVyKTtcbiAgICAgICAgY29uc3Qgc2Nyb2xsYWJsZUNvbnRlbnQgPSBjb250YWluZXIucXVlcnlTZWxlY3Rvcignc3dwLXNjcm9sbGFibGUtY29udGVudCcpO1xuICAgICAgICB0aGlzLmVkZ2VTY3JvbGxNYW5hZ2VyLmluaXQoc2Nyb2xsYWJsZUNvbnRlbnQpO1xuICAgICAgICAvLyBTZXR1cCBjb21tYW5kIGV2ZW50IGxpc3RlbmVyc1xuICAgICAgICB0aGlzLnNldHVwRXZlbnRMaXN0ZW5lcnMoKTtcbiAgICAgICAgLy8gRW1pdCByZWFkeSBzdGF0dXNcbiAgICAgICAgdGhpcy5lbWl0U3RhdHVzKCdyZWFkeScpO1xuICAgIH1cbiAgICBzZXR1cEV2ZW50TGlzdGVuZXJzKCkge1xuICAgICAgICAvLyBOYXZpZ2F0aW9uIGNvbW1hbmRzIHZpYSBFdmVudEJ1c1xuICAgICAgICB0aGlzLmV2ZW50QnVzLm9uKENhbGVuZGFyRXZlbnRzLkNNRF9OQVZJR0FURV9QUkVWLCAoKSA9PiB7XG4gICAgICAgICAgICB0aGlzLmhhbmRsZU5hdmlnYXRlUHJldigpO1xuICAgICAgICB9KTtcbiAgICAgICAgdGhpcy5ldmVudEJ1cy5vbihDYWxlbmRhckV2ZW50cy5DTURfTkFWSUdBVEVfTkVYVCwgKCkgPT4ge1xuICAgICAgICAgICAgdGhpcy5oYW5kbGVOYXZpZ2F0ZU5leHQoKTtcbiAgICAgICAgfSk7XG4gICAgICAgIC8vIERyYXdlciB0b2dnbGUgdmlhIEV2ZW50QnVzXG4gICAgICAgIHRoaXMuZXZlbnRCdXMub24oQ2FsZW5kYXJFdmVudHMuQ01EX0RSQVdFUl9UT0dHTEUsICgpID0+IHtcbiAgICAgICAgICAgIHRoaXMuaGVhZGVyRHJhd2VyTWFuYWdlci50b2dnbGUoKTtcbiAgICAgICAgfSk7XG4gICAgICAgIC8vIFJlbmRlciBjb21tYW5kIHZpYSBFdmVudEJ1c1xuICAgICAgICB0aGlzLmV2ZW50QnVzLm9uKENhbGVuZGFyRXZlbnRzLkNNRF9SRU5ERVIsIChlKSA9PiB7XG4gICAgICAgICAgICBjb25zdCB7IHZpZXdJZCB9ID0gZS5kZXRhaWw7XG4gICAgICAgICAgICB0aGlzLmhhbmRsZVJlbmRlckNvbW1hbmQodmlld0lkKTtcbiAgICAgICAgfSk7XG4gICAgICAgIC8vIFdvcmt3ZWVrIGNoYW5nZSB2aWEgRXZlbnRCdXNcbiAgICAgICAgdGhpcy5ldmVudEJ1cy5vbihDYWxlbmRhckV2ZW50cy5DTURfV09SS1dFRUtfQ0hBTkdFLCAoZSkgPT4ge1xuICAgICAgICAgICAgY29uc3QgeyBwcmVzZXRJZCB9ID0gZS5kZXRhaWw7XG4gICAgICAgICAgICB0aGlzLmhhbmRsZVdvcmt3ZWVrQ2hhbmdlKHByZXNldElkKTtcbiAgICAgICAgfSk7XG4gICAgICAgIC8vIFZpZXcgdXBkYXRlIHZpYSBFdmVudEJ1c1xuICAgICAgICB0aGlzLmV2ZW50QnVzLm9uKENhbGVuZGFyRXZlbnRzLkNNRF9WSUVXX1VQREFURSwgKGUpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHsgdHlwZSwgdmFsdWVzIH0gPSBlLmRldGFpbDtcbiAgICAgICAgICAgIHRoaXMuaGFuZGxlVmlld1VwZGF0ZSh0eXBlLCB2YWx1ZXMpO1xuICAgICAgICB9KTtcbiAgICB9XG4gICAgYXN5bmMgaGFuZGxlUmVuZGVyQ29tbWFuZCh2aWV3SWQpIHtcbiAgICAgICAgdGhpcy5jdXJyZW50Vmlld0lkID0gdmlld0lkO1xuICAgICAgICBhd2FpdCB0aGlzLnJlbmRlcigpO1xuICAgICAgICB0aGlzLmVtaXRTdGF0dXMoJ3JlbmRlcmVkJywgeyB2aWV3SWQgfSk7XG4gICAgfVxuICAgIGFzeW5jIGhhbmRsZU5hdmlnYXRlUHJldigpIHtcbiAgICAgICAgY29uc3Qgc3RlcCA9IHRoaXMud29ya3dlZWtQcmVzZXQ/LnBlcmlvZERheXMgPz8gNztcbiAgICAgICAgdGhpcy5kYXlPZmZzZXQgLT0gc3RlcDtcbiAgICAgICAgYXdhaXQgdGhpcy5hbmltYXRvci5zbGlkZSgncmlnaHQnLCAoKSA9PiB0aGlzLnJlbmRlcigpKTtcbiAgICAgICAgdGhpcy5lbWl0U3RhdHVzKCdyZW5kZXJlZCcsIHsgdmlld0lkOiB0aGlzLmN1cnJlbnRWaWV3SWQgfSk7XG4gICAgfVxuICAgIGFzeW5jIGhhbmRsZU5hdmlnYXRlTmV4dCgpIHtcbiAgICAgICAgY29uc3Qgc3RlcCA9IHRoaXMud29ya3dlZWtQcmVzZXQ/LnBlcmlvZERheXMgPz8gNztcbiAgICAgICAgdGhpcy5kYXlPZmZzZXQgKz0gc3RlcDtcbiAgICAgICAgYXdhaXQgdGhpcy5hbmltYXRvci5zbGlkZSgnbGVmdCcsICgpID0+IHRoaXMucmVuZGVyKCkpO1xuICAgICAgICB0aGlzLmVtaXRTdGF0dXMoJ3JlbmRlcmVkJywgeyB2aWV3SWQ6IHRoaXMuY3VycmVudFZpZXdJZCB9KTtcbiAgICB9XG4gICAgYXN5bmMgaGFuZGxlV29ya3dlZWtDaGFuZ2UocHJlc2V0SWQpIHtcbiAgICAgICAgY29uc3QgcHJlc2V0ID0gYXdhaXQgdGhpcy5zZXR0aW5nc1NlcnZpY2UuZ2V0V29ya3dlZWtQcmVzZXQocHJlc2V0SWQpO1xuICAgICAgICBpZiAocHJlc2V0KSB7XG4gICAgICAgICAgICB0aGlzLndvcmt3ZWVrUHJlc2V0ID0gcHJlc2V0O1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5yZW5kZXIoKTtcbiAgICAgICAgICAgIHRoaXMuZW1pdFN0YXR1cygncmVuZGVyZWQnLCB7IHZpZXdJZDogdGhpcy5jdXJyZW50Vmlld0lkIH0pO1xuICAgICAgICB9XG4gICAgfVxuICAgIGFzeW5jIGhhbmRsZVZpZXdVcGRhdGUodHlwZSwgdmFsdWVzKSB7XG4gICAgICAgIHRoaXMuZ3JvdXBpbmdPdmVycmlkZXMuc2V0KHR5cGUsIHZhbHVlcyk7XG4gICAgICAgIGF3YWl0IHRoaXMucmVuZGVyKCk7XG4gICAgICAgIHRoaXMuZW1pdFN0YXR1cygncmVuZGVyZWQnLCB7IHZpZXdJZDogdGhpcy5jdXJyZW50Vmlld0lkIH0pO1xuICAgIH1cbiAgICBhc3luYyByZW5kZXIoKSB7XG4gICAgICAgIGNvbnN0IHN0b3JlZENvbmZpZyA9IGF3YWl0IHRoaXMudmlld0NvbmZpZ1NlcnZpY2UuZ2V0QnlJZCh0aGlzLmN1cnJlbnRWaWV3SWQpO1xuICAgICAgICBpZiAoIXN0b3JlZENvbmZpZykge1xuICAgICAgICAgICAgdGhpcy5lbWl0U3RhdHVzKCdlcnJvcicsIHsgbWVzc2FnZTogYFZpZXdDb25maWcgbm90IGZvdW5kOiAke3RoaXMuY3VycmVudFZpZXdJZH1gIH0pO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIC8vIFBvcHVsYXRlIGRhdGUgdmFsdWVzIGJhc2VkIG9uIHdvcmt3ZWVrIHByZXNldCBhbmQgZGF5IG9mZnNldFxuICAgICAgICBjb25zdCB3b3JrRGF5cyA9IHRoaXMud29ya3dlZWtQcmVzZXQ/LndvcmtEYXlzIHx8IFsxLCAyLCAzLCA0LCA1XTtcbiAgICAgICAgY29uc3QgcGVyaW9kRGF5cyA9IHRoaXMud29ya3dlZWtQcmVzZXQ/LnBlcmlvZERheXMgPz8gNztcbiAgICAgICAgLy8gRm9yIHNpbmdsZS1kYXkgbmF2aWdhdGlvbiAocGVyaW9kRGF5cz0xKSwgc2hvdyBjb25zZWN1dGl2ZSBkYXlzIGZyb20gb2Zmc2V0XG4gICAgICAgIC8vIEZvciB3ZWVrIG5hdmlnYXRpb24gKHBlcmlvZERheXM9NyksIHNob3cgd29ya0RheXMgZnJvbSB0aGUgd2VlayBjb250YWluaW5nIG9mZnNldFxuICAgICAgICBjb25zdCBkYXRlcyA9IHBlcmlvZERheXMgPT09IDFcbiAgICAgICAgICAgID8gdGhpcy5kYXRlU2VydmljZS5nZXREYXRlc0Zyb21PZmZzZXQodGhpcy5kYXlPZmZzZXQsIHdvcmtEYXlzLmxlbmd0aClcbiAgICAgICAgICAgIDogdGhpcy5kYXRlU2VydmljZS5nZXRXb3JrRGF5c0Zyb21PZmZzZXQodGhpcy5kYXlPZmZzZXQsIHdvcmtEYXlzKTtcbiAgICAgICAgLy8gQ2xvbmUgY29uZmlnIGFuZCBhcHBseSBvdmVycmlkZXNcbiAgICAgICAgY29uc3Qgdmlld0NvbmZpZyA9IHtcbiAgICAgICAgICAgIC4uLnN0b3JlZENvbmZpZyxcbiAgICAgICAgICAgIGdyb3VwaW5nczogc3RvcmVkQ29uZmlnLmdyb3VwaW5ncy5tYXAoZyA9PiB7XG4gICAgICAgICAgICAgICAgLy8gQXBwbHkgZGF0ZSB2YWx1ZXNcbiAgICAgICAgICAgICAgICBpZiAoZy50eXBlID09PSAnZGF0ZScpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHsgLi4uZywgdmFsdWVzOiBkYXRlcyB9O1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAvLyBBcHBseSBncm91cGluZyBvdmVycmlkZXNcbiAgICAgICAgICAgICAgICBjb25zdCBvdmVycmlkZSA9IHRoaXMuZ3JvdXBpbmdPdmVycmlkZXMuZ2V0KGcudHlwZSk7XG4gICAgICAgICAgICAgICAgaWYgKG92ZXJyaWRlKSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiB7IC4uLmcsIHZhbHVlczogb3ZlcnJpZGUgfTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgcmV0dXJuIGc7XG4gICAgICAgICAgICB9KVxuICAgICAgICB9O1xuICAgICAgICBhd2FpdCB0aGlzLm9yY2hlc3RyYXRvci5yZW5kZXIodmlld0NvbmZpZywgdGhpcy5jb250YWluZXIpO1xuICAgIH1cbiAgICBlbWl0U3RhdHVzKHN0YXR1cywgZGV0YWlsKSB7XG4gICAgICAgIHRoaXMuY29udGFpbmVyLmRpc3BhdGNoRXZlbnQobmV3IEN1c3RvbUV2ZW50KGBjYWxlbmRhcjpzdGF0dXM6JHtzdGF0dXN9YCwge1xuICAgICAgICAgICAgZGV0YWlsLFxuICAgICAgICAgICAgYnViYmxlczogdHJ1ZVxuICAgICAgICB9KSk7XG4gICAgfVxufVxuIiwgImV4cG9ydCBjbGFzcyBUaW1lQXhpc1JlbmRlcmVyIHtcbiAgICByZW5kZXIoY29udGFpbmVyLCBzdGFydEhvdXIgPSA2LCBlbmRIb3VyID0gMjApIHtcbiAgICAgICAgY29udGFpbmVyLmlubmVySFRNTCA9ICcnO1xuICAgICAgICBmb3IgKGxldCBob3VyID0gc3RhcnRIb3VyOyBob3VyIDw9IGVuZEhvdXI7IGhvdXIrKykge1xuICAgICAgICAgICAgY29uc3QgbWFya2VyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3dwLWhvdXItbWFya2VyJyk7XG4gICAgICAgICAgICBtYXJrZXIudGV4dENvbnRlbnQgPSBgJHtob3VyLnRvU3RyaW5nKCkucGFkU3RhcnQoMiwgJzAnKX06MDBgO1xuICAgICAgICAgICAgY29udGFpbmVyLmFwcGVuZENoaWxkKG1hcmtlcik7XG4gICAgICAgIH1cbiAgICB9XG59XG4iLCAiZXhwb3J0IGNsYXNzIFNjcm9sbE1hbmFnZXIge1xuICAgIGluaXQoY29udGFpbmVyKSB7XG4gICAgICAgIHRoaXMuc2Nyb2xsYWJsZUNvbnRlbnQgPSBjb250YWluZXIucXVlcnlTZWxlY3Rvcignc3dwLXNjcm9sbGFibGUtY29udGVudCcpO1xuICAgICAgICB0aGlzLnRpbWVBeGlzQ29udGVudCA9IGNvbnRhaW5lci5xdWVyeVNlbGVjdG9yKCdzd3AtdGltZS1heGlzLWNvbnRlbnQnKTtcbiAgICAgICAgdGhpcy5jYWxlbmRhckhlYWRlciA9IGNvbnRhaW5lci5xdWVyeVNlbGVjdG9yKCdzd3AtY2FsZW5kYXItaGVhZGVyJyk7XG4gICAgICAgIHRoaXMuaGVhZGVyRHJhd2VyID0gY29udGFpbmVyLnF1ZXJ5U2VsZWN0b3IoJ3N3cC1oZWFkZXItZHJhd2VyJyk7XG4gICAgICAgIHRoaXMuaGVhZGVyVmlld3BvcnQgPSBjb250YWluZXIucXVlcnlTZWxlY3Rvcignc3dwLWhlYWRlci12aWV3cG9ydCcpO1xuICAgICAgICB0aGlzLmhlYWRlclNwYWNlciA9IGNvbnRhaW5lci5xdWVyeVNlbGVjdG9yKCdzd3AtaGVhZGVyLXNwYWNlcicpO1xuICAgICAgICB0aGlzLnNjcm9sbGFibGVDb250ZW50LmFkZEV2ZW50TGlzdGVuZXIoJ3Njcm9sbCcsICgpID0+IHRoaXMub25TY3JvbGwoKSk7XG4gICAgICAgIC8vIFN5bmtyb25pc2VyIGhlYWRlci1zcGFjZXIgaFx1MDBGOGpkZSBtZWQgaGVhZGVyLXZpZXdwb3J0XG4gICAgICAgIHRoaXMucmVzaXplT2JzZXJ2ZXIgPSBuZXcgUmVzaXplT2JzZXJ2ZXIoKCkgPT4gdGhpcy5zeW5jSGVhZGVyU3BhY2VySGVpZ2h0KCkpO1xuICAgICAgICB0aGlzLnJlc2l6ZU9ic2VydmVyLm9ic2VydmUodGhpcy5oZWFkZXJWaWV3cG9ydCk7XG4gICAgICAgIHRoaXMuc3luY0hlYWRlclNwYWNlckhlaWdodCgpO1xuICAgIH1cbiAgICBzeW5jSGVhZGVyU3BhY2VySGVpZ2h0KCkge1xuICAgICAgICAvLyBLb3BpZXIgZGVuIGZha3Rpc2tlIGNvbXB1dGVkIGhlaWdodCBkaXJla3RlIGZyYSBoZWFkZXItdmlld3BvcnRcbiAgICAgICAgY29uc3QgY29tcHV0ZWRIZWlnaHQgPSBnZXRDb21wdXRlZFN0eWxlKHRoaXMuaGVhZGVyVmlld3BvcnQpLmhlaWdodDtcbiAgICAgICAgdGhpcy5oZWFkZXJTcGFjZXIuc3R5bGUuaGVpZ2h0ID0gY29tcHV0ZWRIZWlnaHQ7XG4gICAgfVxuICAgIG9uU2Nyb2xsKCkge1xuICAgICAgICBjb25zdCB7IHNjcm9sbFRvcCwgc2Nyb2xsTGVmdCB9ID0gdGhpcy5zY3JvbGxhYmxlQ29udGVudDtcbiAgICAgICAgLy8gU3lua3JvbmlzZXIgdGltZS1heGlzIHZlcnRpa2FsdFxuICAgICAgICB0aGlzLnRpbWVBeGlzQ29udGVudC5zdHlsZS50cmFuc2Zvcm0gPSBgdHJhbnNsYXRlWSgtJHtzY3JvbGxUb3B9cHgpYDtcbiAgICAgICAgLy8gU3lua3JvbmlzZXIgaGVhZGVyIG9nIGRyYXdlciBob3Jpc29udGFsdFxuICAgICAgICB0aGlzLmNhbGVuZGFySGVhZGVyLnN0eWxlLnRyYW5zZm9ybSA9IGB0cmFuc2xhdGVYKC0ke3Njcm9sbExlZnR9cHgpYDtcbiAgICAgICAgdGhpcy5oZWFkZXJEcmF3ZXIuc3R5bGUudHJhbnNmb3JtID0gYHRyYW5zbGF0ZVgoLSR7c2Nyb2xsTGVmdH1weClgO1xuICAgIH1cbn1cbiIsICJleHBvcnQgY2xhc3MgSGVhZGVyRHJhd2VyTWFuYWdlciB7XG4gICAgY29uc3RydWN0b3IoKSB7XG4gICAgICAgIHRoaXMuZXhwYW5kZWQgPSBmYWxzZTtcbiAgICAgICAgdGhpcy5jdXJyZW50Um93cyA9IDA7XG4gICAgICAgIHRoaXMucm93SGVpZ2h0ID0gMjU7XG4gICAgICAgIHRoaXMuZHVyYXRpb24gPSAyMDA7XG4gICAgfVxuICAgIGluaXQoY29udGFpbmVyKSB7XG4gICAgICAgIHRoaXMuZHJhd2VyID0gY29udGFpbmVyLnF1ZXJ5U2VsZWN0b3IoJ3N3cC1oZWFkZXItZHJhd2VyJyk7XG4gICAgICAgIGlmICghdGhpcy5kcmF3ZXIpXG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKCdIZWFkZXJEcmF3ZXJNYW5hZ2VyOiBzd3AtaGVhZGVyLWRyYXdlciBub3QgZm91bmQnKTtcbiAgICB9XG4gICAgdG9nZ2xlKCkge1xuICAgICAgICB0aGlzLmV4cGFuZGVkID8gdGhpcy5jb2xsYXBzZSgpIDogdGhpcy5leHBhbmQoKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogRXhwYW5kIGRyYXdlciB0byBzaW5nbGUgcm93IChsZWdhY3kgc3VwcG9ydClcbiAgICAgKi9cbiAgICBleHBhbmQoKSB7XG4gICAgICAgIHRoaXMuZXhwYW5kVG9Sb3dzKDEpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBFeHBhbmQgZHJhd2VyIHRvIGZpdCBzcGVjaWZpZWQgbnVtYmVyIG9mIHJvd3NcbiAgICAgKi9cbiAgICBleHBhbmRUb1Jvd3Mocm93Q291bnQpIHtcbiAgICAgICAgY29uc3QgdGFyZ2V0SGVpZ2h0ID0gcm93Q291bnQgKiB0aGlzLnJvd0hlaWdodDtcbiAgICAgICAgY29uc3QgY3VycmVudEhlaWdodCA9IHRoaXMuZXhwYW5kZWQgPyB0aGlzLmN1cnJlbnRSb3dzICogdGhpcy5yb3dIZWlnaHQgOiAwO1xuICAgICAgICAvLyBTa2lwIGlmIGFscmVhZHkgYXQgdGFyZ2V0XG4gICAgICAgIGlmICh0aGlzLmV4cGFuZGVkICYmIHRoaXMuY3VycmVudFJvd3MgPT09IHJvd0NvdW50KVxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB0aGlzLmN1cnJlbnRSb3dzID0gcm93Q291bnQ7XG4gICAgICAgIHRoaXMuZXhwYW5kZWQgPSB0cnVlO1xuICAgICAgICB0aGlzLmFuaW1hdGUoY3VycmVudEhlaWdodCwgdGFyZ2V0SGVpZ2h0KTtcbiAgICB9XG4gICAgY29sbGFwc2UoKSB7XG4gICAgICAgIGlmICghdGhpcy5leHBhbmRlZClcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgY29uc3QgY3VycmVudEhlaWdodCA9IHRoaXMuY3VycmVudFJvd3MgKiB0aGlzLnJvd0hlaWdodDtcbiAgICAgICAgdGhpcy5leHBhbmRlZCA9IGZhbHNlO1xuICAgICAgICB0aGlzLmN1cnJlbnRSb3dzID0gMDtcbiAgICAgICAgdGhpcy5hbmltYXRlKGN1cnJlbnRIZWlnaHQsIDApO1xuICAgIH1cbiAgICBhbmltYXRlKGZyb20sIHRvKSB7XG4gICAgICAgIGNvbnN0IGtleWZyYW1lcyA9IFtcbiAgICAgICAgICAgIHsgaGVpZ2h0OiBgJHtmcm9tfXB4YCB9LFxuICAgICAgICAgICAgeyBoZWlnaHQ6IGAke3RvfXB4YCB9XG4gICAgICAgIF07XG4gICAgICAgIGNvbnN0IG9wdGlvbnMgPSB7XG4gICAgICAgICAgICBkdXJhdGlvbjogdGhpcy5kdXJhdGlvbixcbiAgICAgICAgICAgIGVhc2luZzogJ2Vhc2UnLFxuICAgICAgICAgICAgZmlsbDogJ2ZvcndhcmRzJ1xuICAgICAgICB9O1xuICAgICAgICAvLyBLdW4gYW5pbVx1MDBFOXIgZHJhd2VyIC0gU2Nyb2xsTWFuYWdlciBzeW5rcm9uaXNlcmVyIGhlYWRlci1zcGFjZXIgdmlhIFJlc2l6ZU9ic2VydmVyXG4gICAgICAgIHRoaXMuZHJhd2VyLmFuaW1hdGUoa2V5ZnJhbWVzLCBvcHRpb25zKTtcbiAgICB9XG4gICAgaXNFeHBhbmRlZCgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuZXhwYW5kZWQ7XG4gICAgfVxuICAgIGdldFJvd0NvdW50KCkge1xuICAgICAgICByZXR1cm4gdGhpcy5jdXJyZW50Um93cztcbiAgICB9XG59XG4iLCAiZXhwb3J0IGNsYXNzIE1vY2tUZWFtU3RvcmUge1xuICAgIGNvbnN0cnVjdG9yKCkge1xuICAgICAgICB0aGlzLnR5cGUgPSAndGVhbSc7XG4gICAgICAgIHRoaXMudGVhbXMgPSBbXG4gICAgICAgICAgICB7IGlkOiAnYWxwaGEnLCBuYW1lOiAnVGVhbSBBbHBoYScgfSxcbiAgICAgICAgICAgIHsgaWQ6ICdiZXRhJywgbmFtZTogJ1RlYW0gQmV0YScgfVxuICAgICAgICBdO1xuICAgIH1cbiAgICBnZXRCeUlkcyhpZHMpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMudGVhbXMuZmlsdGVyKHQgPT4gaWRzLmluY2x1ZGVzKHQuaWQpKTtcbiAgICB9XG59XG5leHBvcnQgY2xhc3MgTW9ja1Jlc291cmNlU3RvcmUge1xuICAgIGNvbnN0cnVjdG9yKCkge1xuICAgICAgICB0aGlzLnR5cGUgPSAncmVzb3VyY2UnO1xuICAgICAgICB0aGlzLnJlc291cmNlcyA9IFtcbiAgICAgICAgICAgIHsgaWQ6ICdhbGljZScsIG5hbWU6ICdBbGljZScsIHRlYW1JZDogJ2FscGhhJyB9LFxuICAgICAgICAgICAgeyBpZDogJ2JvYicsIG5hbWU6ICdCb2InLCB0ZWFtSWQ6ICdhbHBoYScgfSxcbiAgICAgICAgICAgIHsgaWQ6ICdjYXJvbCcsIG5hbWU6ICdDYXJvbCcsIHRlYW1JZDogJ2JldGEnIH0sXG4gICAgICAgICAgICB7IGlkOiAnZGF2ZScsIG5hbWU6ICdEYXZlJywgdGVhbUlkOiAnYmV0YScgfVxuICAgICAgICBdO1xuICAgIH1cbiAgICBnZXRCeUlkcyhpZHMpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMucmVzb3VyY2VzLmZpbHRlcihyID0+IGlkcy5pbmNsdWRlcyhyLmlkKSk7XG4gICAgfVxufVxuIiwgImltcG9ydCB7IENhbGVuZGFyRXZlbnRzIH0gZnJvbSAnLi4vY29yZS9DYWxlbmRhckV2ZW50cyc7XG5leHBvcnQgY2xhc3MgRGVtb0FwcCB7XG4gICAgY29uc3RydWN0b3IoaW5kZXhlZERCQ29udGV4dCwgZGF0YVNlZWRlciwgYXVkaXRTZXJ2aWNlLCBjYWxlbmRhckFwcCwgZGF0ZVNlcnZpY2UsIHJlc291cmNlU2VydmljZSwgZXZlbnRCdXMpIHtcbiAgICAgICAgdGhpcy5pbmRleGVkREJDb250ZXh0ID0gaW5kZXhlZERCQ29udGV4dDtcbiAgICAgICAgdGhpcy5kYXRhU2VlZGVyID0gZGF0YVNlZWRlcjtcbiAgICAgICAgdGhpcy5hdWRpdFNlcnZpY2UgPSBhdWRpdFNlcnZpY2U7XG4gICAgICAgIHRoaXMuY2FsZW5kYXJBcHAgPSBjYWxlbmRhckFwcDtcbiAgICAgICAgdGhpcy5kYXRlU2VydmljZSA9IGRhdGVTZXJ2aWNlO1xuICAgICAgICB0aGlzLnJlc291cmNlU2VydmljZSA9IHJlc291cmNlU2VydmljZTtcbiAgICAgICAgdGhpcy5ldmVudEJ1cyA9IGV2ZW50QnVzO1xuICAgICAgICB0aGlzLmN1cnJlbnRWaWV3ID0gJ3NpbXBsZSc7XG4gICAgfVxuICAgIGFzeW5jIGluaXQoKSB7XG4gICAgICAgIC8vIFNldCBiYXNlIGRhdGUgdG8gbWF0Y2ggbW9jayBkYXRhICg4LiBkZWNlbWJlciAyMDI1ID0gbWFuZGFnKVxuICAgICAgICB0aGlzLmRhdGVTZXJ2aWNlLnNldEJhc2VEYXRlKG5ldyBEYXRlKCcyMDI1LTEyLTA4JykpO1xuICAgICAgICAvLyBJbml0aWFsaXplIEluZGV4ZWREQlxuICAgICAgICBhd2FpdCB0aGlzLmluZGV4ZWREQkNvbnRleHQuaW5pdGlhbGl6ZSgpO1xuICAgICAgICBjb25zb2xlLmxvZygnW0RlbW9BcHBdIEluZGV4ZWREQiBpbml0aWFsaXplZCcpO1xuICAgICAgICAvLyBTZWVkIGRhdGEgaWYgZW1wdHlcbiAgICAgICAgYXdhaXQgdGhpcy5kYXRhU2VlZGVyLnNlZWRJZkVtcHR5KCk7XG4gICAgICAgIGNvbnNvbGUubG9nKCdbRGVtb0FwcF0gRGF0YSBzZWVkaW5nIGNvbXBsZXRlJyk7XG4gICAgICAgIHRoaXMuY29udGFpbmVyID0gZG9jdW1lbnQucXVlcnlTZWxlY3Rvcignc3dwLWNhbGVuZGFyLWNvbnRhaW5lcicpO1xuICAgICAgICAvLyBJbml0aWFsaXplIENhbGVuZGFyQXBwXG4gICAgICAgIGF3YWl0IHRoaXMuY2FsZW5kYXJBcHAuaW5pdCh0aGlzLmNvbnRhaW5lcik7XG4gICAgICAgIGNvbnNvbGUubG9nKCdbRGVtb0FwcF0gQ2FsZW5kYXJBcHAgaW5pdGlhbGl6ZWQnKTtcbiAgICAgICAgLy8gU2V0dXAgZGVtbyBVSSBoYW5kbGVyc1xuICAgICAgICB0aGlzLnNldHVwTmF2aWdhdGlvbigpO1xuICAgICAgICB0aGlzLnNldHVwRHJhd2VyVG9nZ2xlKCk7XG4gICAgICAgIHRoaXMuc2V0dXBWaWV3U3dpdGNoaW5nKCk7XG4gICAgICAgIHRoaXMuc2V0dXBXb3Jrd2Vla1NlbGVjdG9yKCk7XG4gICAgICAgIGF3YWl0IHRoaXMuc2V0dXBSZXNvdXJjZVNlbGVjdG9yKCk7XG4gICAgICAgIC8vIExpc3RlbiBmb3IgY2FsZW5kYXIgc3RhdHVzIGV2ZW50c1xuICAgICAgICB0aGlzLnNldHVwU3RhdHVzTGlzdGVuZXJzKCk7XG4gICAgICAgIC8vIEluaXRpYWwgcmVuZGVyXG4gICAgICAgIHRoaXMuZXZlbnRCdXMuZW1pdChDYWxlbmRhckV2ZW50cy5DTURfUkVOREVSLCB7IHZpZXdJZDogdGhpcy5jdXJyZW50VmlldyB9KTtcbiAgICB9XG4gICAgc2V0dXBOYXZpZ2F0aW9uKCkge1xuICAgICAgICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnYnRuLXByZXYnKS5vbmNsaWNrID0gKCkgPT4ge1xuICAgICAgICAgICAgdGhpcy5ldmVudEJ1cy5lbWl0KENhbGVuZGFyRXZlbnRzLkNNRF9OQVZJR0FURV9QUkVWKTtcbiAgICAgICAgfTtcbiAgICAgICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2J0bi1uZXh0Jykub25jbGljayA9ICgpID0+IHtcbiAgICAgICAgICAgIHRoaXMuZXZlbnRCdXMuZW1pdChDYWxlbmRhckV2ZW50cy5DTURfTkFWSUdBVEVfTkVYVCk7XG4gICAgICAgIH07XG4gICAgfVxuICAgIHNldHVwVmlld1N3aXRjaGluZygpIHtcbiAgICAgICAgY29uc3QgY2hpcHMgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCcudmlldy1jaGlwJyk7XG4gICAgICAgIGNoaXBzLmZvckVhY2goY2hpcCA9PiB7XG4gICAgICAgICAgICBjaGlwLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgKCkgPT4ge1xuICAgICAgICAgICAgICAgIGNoaXBzLmZvckVhY2goYyA9PiBjLmNsYXNzTGlzdC5yZW1vdmUoJ2FjdGl2ZScpKTtcbiAgICAgICAgICAgICAgICBjaGlwLmNsYXNzTGlzdC5hZGQoJ2FjdGl2ZScpO1xuICAgICAgICAgICAgICAgIGNvbnN0IHZpZXcgPSBjaGlwLmRhdGFzZXQudmlldztcbiAgICAgICAgICAgICAgICBpZiAodmlldykge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmN1cnJlbnRWaWV3ID0gdmlldztcbiAgICAgICAgICAgICAgICAgICAgdGhpcy51cGRhdGVTZWxlY3RvclZpc2liaWxpdHkoKTtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5ldmVudEJ1cy5lbWl0KENhbGVuZGFyRXZlbnRzLkNNRF9SRU5ERVIsIHsgdmlld0lkOiB2aWV3IH0pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICB9XG4gICAgdXBkYXRlU2VsZWN0b3JWaXNpYmlsaXR5KCkge1xuICAgICAgICBjb25zdCBzZWxlY3RvciA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJ3N3cC1yZXNvdXJjZS1zZWxlY3RvcicpO1xuICAgICAgICBjb25zdCBzaG93U2VsZWN0b3IgPSB0aGlzLmN1cnJlbnRWaWV3ID09PSAncGlja2VyJyB8fCB0aGlzLmN1cnJlbnRWaWV3ID09PSAnZGF5JztcbiAgICAgICAgc2VsZWN0b3I/LmNsYXNzTGlzdC50b2dnbGUoJ2hpZGRlbicsICFzaG93U2VsZWN0b3IpO1xuICAgIH1cbiAgICBzZXR1cERyYXdlclRvZ2dsZSgpIHtcbiAgICAgICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2J0bi1kcmF3ZXInKS5vbmNsaWNrID0gKCkgPT4ge1xuICAgICAgICAgICAgdGhpcy5ldmVudEJ1cy5lbWl0KENhbGVuZGFyRXZlbnRzLkNNRF9EUkFXRVJfVE9HR0xFKTtcbiAgICAgICAgfTtcbiAgICB9XG4gICAgc2V0dXBXb3Jrd2Vla1NlbGVjdG9yKCkge1xuICAgICAgICBjb25zdCB3b3Jrd2Vla1NlbGVjdCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd3b3Jrd2Vlay1zZWxlY3QnKTtcbiAgICAgICAgd29ya3dlZWtTZWxlY3Q/LmFkZEV2ZW50TGlzdGVuZXIoJ2NoYW5nZScsICgpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHByZXNldElkID0gd29ya3dlZWtTZWxlY3QudmFsdWU7XG4gICAgICAgICAgICB0aGlzLmV2ZW50QnVzLmVtaXQoQ2FsZW5kYXJFdmVudHMuQ01EX1dPUktXRUVLX0NIQU5HRSwgeyBwcmVzZXRJZCB9KTtcbiAgICAgICAgfSk7XG4gICAgfVxuICAgIGFzeW5jIHNldHVwUmVzb3VyY2VTZWxlY3RvcigpIHtcbiAgICAgICAgY29uc3QgcmVzb3VyY2VzID0gYXdhaXQgdGhpcy5yZXNvdXJjZVNlcnZpY2UuZ2V0QWxsKCk7XG4gICAgICAgIGNvbnN0IGNvbnRhaW5lciA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJy5yZXNvdXJjZS1jaGVja2JveGVzJyk7XG4gICAgICAgIGlmICghY29udGFpbmVyKVxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICBjb250YWluZXIuaW5uZXJIVE1MID0gJyc7XG4gICAgICAgIHJlc291cmNlcy5mb3JFYWNoKHIgPT4ge1xuICAgICAgICAgICAgY29uc3QgbGFiZWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdsYWJlbCcpO1xuICAgICAgICAgICAgbGFiZWwuaW5uZXJIVE1MID0gYFxyXG4gICAgICAgIDxpbnB1dCB0eXBlPVwiY2hlY2tib3hcIiB2YWx1ZT1cIiR7ci5pZH1cIiBjaGVja2VkPlxyXG4gICAgICAgICR7ci5kaXNwbGF5TmFtZX1cclxuICAgICAgYDtcbiAgICAgICAgICAgIGNvbnRhaW5lci5hcHBlbmRDaGlsZChsYWJlbCk7XG4gICAgICAgIH0pO1xuICAgICAgICBjb250YWluZXIuYWRkRXZlbnRMaXN0ZW5lcignY2hhbmdlJywgKCkgPT4ge1xuICAgICAgICAgICAgY29uc3QgY2hlY2tlZCA9IGNvbnRhaW5lci5xdWVyeVNlbGVjdG9yQWxsKCdpbnB1dDpjaGVja2VkJyk7XG4gICAgICAgICAgICBjb25zdCB2YWx1ZXMgPSBBcnJheS5mcm9tKGNoZWNrZWQpLm1hcChjYiA9PiBjYi52YWx1ZSk7XG4gICAgICAgICAgICB0aGlzLmV2ZW50QnVzLmVtaXQoQ2FsZW5kYXJFdmVudHMuQ01EX1ZJRVdfVVBEQVRFLCB7IHR5cGU6ICdyZXNvdXJjZScsIHZhbHVlcyB9KTtcbiAgICAgICAgfSk7XG4gICAgfVxuICAgIHNldHVwU3RhdHVzTGlzdGVuZXJzKCkge1xuICAgICAgICB0aGlzLmNvbnRhaW5lci5hZGRFdmVudExpc3RlbmVyKCdjYWxlbmRhcjpzdGF0dXM6cmVhZHknLCAoKSA9PiB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZygnW0RlbW9BcHBdIENhbGVuZGFyIHJlYWR5Jyk7XG4gICAgICAgIH0pO1xuICAgICAgICB0aGlzLmNvbnRhaW5lci5hZGRFdmVudExpc3RlbmVyKCdjYWxlbmRhcjpzdGF0dXM6cmVuZGVyZWQnLCAoKGUpID0+IHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdbRGVtb0FwcF0gQ2FsZW5kYXIgcmVuZGVyZWQ6JywgZS5kZXRhaWwudmlld0lkKTtcbiAgICAgICAgfSkpO1xuICAgICAgICB0aGlzLmNvbnRhaW5lci5hZGRFdmVudExpc3RlbmVyKCdjYWxlbmRhcjpzdGF0dXM6ZXJyb3InLCAoKGUpID0+IHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ1tEZW1vQXBwXSBDYWxlbmRhciBlcnJvcjonLCBlLmRldGFpbC5tZXNzYWdlKTtcbiAgICAgICAgfSkpO1xuICAgIH1cbn1cbiIsICIvKipcbiAqIENlbnRyYWwgZXZlbnQgZGlzcGF0Y2hlciBmb3IgY2FsZW5kYXIgdXNpbmcgRE9NIEN1c3RvbUV2ZW50c1xuICogUHJvdmlkZXMgbG9nZ2luZyBhbmQgZGVidWdnaW5nIGNhcGFiaWxpdGllc1xuICovXG5leHBvcnQgY2xhc3MgRXZlbnRCdXMge1xuICAgIGNvbnN0cnVjdG9yKCkge1xuICAgICAgICB0aGlzLmV2ZW50TG9nID0gW107XG4gICAgICAgIHRoaXMuZGVidWcgPSBmYWxzZTtcbiAgICAgICAgdGhpcy5saXN0ZW5lcnMgPSBuZXcgU2V0KCk7XG4gICAgICAgIC8vIExvZyBjb25maWd1cmF0aW9uIGZvciBkaWZmZXJlbnQgY2F0ZWdvcmllc1xuICAgICAgICB0aGlzLmxvZ0NvbmZpZyA9IHtcbiAgICAgICAgICAgIGNhbGVuZGFyOiB0cnVlLFxuICAgICAgICAgICAgZ3JpZDogdHJ1ZSxcbiAgICAgICAgICAgIGV2ZW50OiB0cnVlLFxuICAgICAgICAgICAgc2Nyb2xsOiB0cnVlLFxuICAgICAgICAgICAgbmF2aWdhdGlvbjogdHJ1ZSxcbiAgICAgICAgICAgIHZpZXc6IHRydWUsXG4gICAgICAgICAgICBkZWZhdWx0OiB0cnVlXG4gICAgICAgIH07XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFN1YnNjcmliZSB0byBhbiBldmVudCB2aWEgRE9NIGFkZEV2ZW50TGlzdGVuZXJcbiAgICAgKi9cbiAgICBvbihldmVudFR5cGUsIGhhbmRsZXIsIG9wdGlvbnMpIHtcbiAgICAgICAgZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcihldmVudFR5cGUsIGhhbmRsZXIsIG9wdGlvbnMpO1xuICAgICAgICAvLyBUcmFjayBmb3IgY2xlYW51cFxuICAgICAgICB0aGlzLmxpc3RlbmVycy5hZGQoeyBldmVudFR5cGUsIGhhbmRsZXIsIG9wdGlvbnMgfSk7XG4gICAgICAgIC8vIFJldHVybiB1bnN1YnNjcmliZSBmdW5jdGlvblxuICAgICAgICByZXR1cm4gKCkgPT4gdGhpcy5vZmYoZXZlbnRUeXBlLCBoYW5kbGVyKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogU3Vic2NyaWJlIHRvIGFuIGV2ZW50IG9uY2VcbiAgICAgKi9cbiAgICBvbmNlKGV2ZW50VHlwZSwgaGFuZGxlcikge1xuICAgICAgICByZXR1cm4gdGhpcy5vbihldmVudFR5cGUsIGhhbmRsZXIsIHsgb25jZTogdHJ1ZSB9KTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogVW5zdWJzY3JpYmUgZnJvbSBhbiBldmVudFxuICAgICAqL1xuICAgIG9mZihldmVudFR5cGUsIGhhbmRsZXIpIHtcbiAgICAgICAgZG9jdW1lbnQucmVtb3ZlRXZlbnRMaXN0ZW5lcihldmVudFR5cGUsIGhhbmRsZXIpO1xuICAgICAgICAvLyBSZW1vdmUgZnJvbSB0cmFja2luZ1xuICAgICAgICBmb3IgKGNvbnN0IGxpc3RlbmVyIG9mIHRoaXMubGlzdGVuZXJzKSB7XG4gICAgICAgICAgICBpZiAobGlzdGVuZXIuZXZlbnRUeXBlID09PSBldmVudFR5cGUgJiYgbGlzdGVuZXIuaGFuZGxlciA9PT0gaGFuZGxlcikge1xuICAgICAgICAgICAgICAgIHRoaXMubGlzdGVuZXJzLmRlbGV0ZShsaXN0ZW5lcik7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG4gICAgLyoqXG4gICAgICogRW1pdCBhbiBldmVudCB2aWEgRE9NIEN1c3RvbUV2ZW50XG4gICAgICovXG4gICAgZW1pdChldmVudFR5cGUsIGRldGFpbCA9IHt9KSB7XG4gICAgICAgIC8vIFZhbGlkYXRlIGV2ZW50VHlwZVxuICAgICAgICBpZiAoIWV2ZW50VHlwZSkge1xuICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IGV2ZW50ID0gbmV3IEN1c3RvbUV2ZW50KGV2ZW50VHlwZSwge1xuICAgICAgICAgICAgZGV0YWlsOiBkZXRhaWwgPz8ge30sXG4gICAgICAgICAgICBidWJibGVzOiB0cnVlLFxuICAgICAgICAgICAgY2FuY2VsYWJsZTogdHJ1ZVxuICAgICAgICB9KTtcbiAgICAgICAgLy8gTG9nIGV2ZW50IHdpdGggZ3JvdXBpbmdcbiAgICAgICAgaWYgKHRoaXMuZGVidWcpIHtcbiAgICAgICAgICAgIHRoaXMubG9nRXZlbnRXaXRoR3JvdXBpbmcoZXZlbnRUeXBlLCBkZXRhaWwpO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuZXZlbnRMb2cucHVzaCh7XG4gICAgICAgICAgICB0eXBlOiBldmVudFR5cGUsXG4gICAgICAgICAgICBkZXRhaWw6IGRldGFpbCA/PyB7fSxcbiAgICAgICAgICAgIHRpbWVzdGFtcDogRGF0ZS5ub3coKVxuICAgICAgICB9KTtcbiAgICAgICAgLy8gRW1pdCBvbiBkb2N1bWVudCAob25seSBET00gZXZlbnRzIG5vdylcbiAgICAgICAgcmV0dXJuICFkb2N1bWVudC5kaXNwYXRjaEV2ZW50KGV2ZW50KTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogTG9nIGV2ZW50IHdpdGggY29uc29sZSBncm91cGluZ1xuICAgICAqL1xuICAgIGxvZ0V2ZW50V2l0aEdyb3VwaW5nKGV2ZW50VHlwZSwgX2RldGFpbCkge1xuICAgICAgICAvLyBFeHRyYWN0IGNhdGVnb3J5IGZyb20gZXZlbnQgdHlwZSAoZS5nLiwgJ2NhbGVuZGFyOmRhdGVjaGFuZ2VkJyBcdTIxOTIgJ2NhbGVuZGFyJylcbiAgICAgICAgY29uc3QgY2F0ZWdvcnkgPSB0aGlzLmV4dHJhY3RDYXRlZ29yeShldmVudFR5cGUpO1xuICAgICAgICAvLyBPbmx5IGxvZyBpZiBjYXRlZ29yeSBpcyBlbmFibGVkXG4gICAgICAgIGlmICghdGhpcy5sb2dDb25maWdbY2F0ZWdvcnldKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgLy8gR2V0IGNhdGVnb3J5IGVtb2ppIGFuZCBjb2xvciAodXNlZCBmb3IgZnV0dXJlIGNvbnNvbGUgc3R5bGluZylcbiAgICAgICAgdGhpcy5nZXRDYXRlZ29yeVN0eWxlKGNhdGVnb3J5KTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogRXh0cmFjdCBjYXRlZ29yeSBmcm9tIGV2ZW50IHR5cGVcbiAgICAgKi9cbiAgICBleHRyYWN0Q2F0ZWdvcnkoZXZlbnRUeXBlKSB7XG4gICAgICAgIGlmICghZXZlbnRUeXBlKSB7XG4gICAgICAgICAgICByZXR1cm4gJ3Vua25vd24nO1xuICAgICAgICB9XG4gICAgICAgIGlmIChldmVudFR5cGUuaW5jbHVkZXMoJzonKSkge1xuICAgICAgICAgICAgcmV0dXJuIGV2ZW50VHlwZS5zcGxpdCgnOicpWzBdO1xuICAgICAgICB9XG4gICAgICAgIC8vIEZhbGxiYWNrOiB0cnkgdG8gZGV0ZWN0IGNhdGVnb3J5IGZyb20gZXZlbnQgbmFtZSBwYXR0ZXJuc1xuICAgICAgICBjb25zdCBsb3dlclR5cGUgPSBldmVudFR5cGUudG9Mb3dlckNhc2UoKTtcbiAgICAgICAgaWYgKGxvd2VyVHlwZS5pbmNsdWRlcygnZ3JpZCcpIHx8IGxvd2VyVHlwZS5pbmNsdWRlcygncmVuZGVyZWQnKSlcbiAgICAgICAgICAgIHJldHVybiAnZ3JpZCc7XG4gICAgICAgIGlmIChsb3dlclR5cGUuaW5jbHVkZXMoJ2V2ZW50JykgfHwgbG93ZXJUeXBlLmluY2x1ZGVzKCdzeW5jJykpXG4gICAgICAgICAgICByZXR1cm4gJ2V2ZW50JztcbiAgICAgICAgaWYgKGxvd2VyVHlwZS5pbmNsdWRlcygnc2Nyb2xsJykpXG4gICAgICAgICAgICByZXR1cm4gJ3Njcm9sbCc7XG4gICAgICAgIGlmIChsb3dlclR5cGUuaW5jbHVkZXMoJ25hdicpIHx8IGxvd2VyVHlwZS5pbmNsdWRlcygnZGF0ZScpKVxuICAgICAgICAgICAgcmV0dXJuICduYXZpZ2F0aW9uJztcbiAgICAgICAgaWYgKGxvd2VyVHlwZS5pbmNsdWRlcygndmlldycpKVxuICAgICAgICAgICAgcmV0dXJuICd2aWV3JztcbiAgICAgICAgcmV0dXJuICdkZWZhdWx0JztcbiAgICB9XG4gICAgLyoqXG4gICAgICogR2V0IHN0eWxpbmcgZm9yIGRpZmZlcmVudCBjYXRlZ29yaWVzXG4gICAgICovXG4gICAgZ2V0Q2F0ZWdvcnlTdHlsZShjYXRlZ29yeSkge1xuICAgICAgICBjb25zdCBzdHlsZXMgPSB7XG4gICAgICAgICAgICBjYWxlbmRhcjogeyBlbW9qaTogJ1x1RDgzRFx1RENDNScsIGNvbG9yOiAnIzIxOTZGMycgfSxcbiAgICAgICAgICAgIGdyaWQ6IHsgZW1vamk6ICdcdUQ4M0RcdURDQ0EnLCBjb2xvcjogJyM0Q0FGNTAnIH0sXG4gICAgICAgICAgICBldmVudDogeyBlbW9qaTogJ1x1RDgzRFx1RENDQycsIGNvbG9yOiAnI0ZGOTgwMCcgfSxcbiAgICAgICAgICAgIHNjcm9sbDogeyBlbW9qaTogJ1x1RDgzRFx1RENEQycsIGNvbG9yOiAnIzlDMjdCMCcgfSxcbiAgICAgICAgICAgIG5hdmlnYXRpb246IHsgZW1vamk6ICdcdUQ4M0VcdURERUQnLCBjb2xvcjogJyNGNDQzMzYnIH0sXG4gICAgICAgICAgICB2aWV3OiB7IGVtb2ppOiAnXHVEODNEXHVEQzQxJywgY29sb3I6ICcjMDBCQ0Q0JyB9LFxuICAgICAgICAgICAgZGVmYXVsdDogeyBlbW9qaTogJ1x1RDgzRFx1RENFMicsIGNvbG9yOiAnIzYwN0Q4QicgfVxuICAgICAgICB9O1xuICAgICAgICByZXR1cm4gc3R5bGVzW2NhdGVnb3J5XSB8fCBzdHlsZXMuZGVmYXVsdDtcbiAgICB9XG4gICAgLyoqXG4gICAgICogQ29uZmlndXJlIGxvZ2dpbmcgZm9yIHNwZWNpZmljIGNhdGVnb3JpZXNcbiAgICAgKi9cbiAgICBzZXRMb2dDb25maWcoY29uZmlnKSB7XG4gICAgICAgIHRoaXMubG9nQ29uZmlnID0geyAuLi50aGlzLmxvZ0NvbmZpZywgLi4uY29uZmlnIH07XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEdldCBjdXJyZW50IGxvZyBjb25maWd1cmF0aW9uXG4gICAgICovXG4gICAgZ2V0TG9nQ29uZmlnKCkge1xuICAgICAgICByZXR1cm4geyAuLi50aGlzLmxvZ0NvbmZpZyB9O1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBHZXQgZXZlbnQgaGlzdG9yeVxuICAgICAqL1xuICAgIGdldEV2ZW50TG9nKGV2ZW50VHlwZSkge1xuICAgICAgICBpZiAoZXZlbnRUeXBlKSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5ldmVudExvZy5maWx0ZXIoZSA9PiBlLnR5cGUgPT09IGV2ZW50VHlwZSk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRoaXMuZXZlbnRMb2c7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEVuYWJsZS9kaXNhYmxlIGRlYnVnIG1vZGVcbiAgICAgKi9cbiAgICBzZXREZWJ1ZyhlbmFibGVkKSB7XG4gICAgICAgIHRoaXMuZGVidWcgPSBlbmFibGVkO1xuICAgIH1cbn1cbiIsICIvKipcbiAqIEluZGV4ZWREQkNvbnRleHQgLSBEYXRhYmFzZSBjb25uZWN0aW9uIG1hbmFnZXJcbiAqXG4gKiBSRVNQT05TSUJJTElUWTpcbiAqIC0gT3BlbnMgYW5kIG1hbmFnZXMgSURCRGF0YWJhc2UgY29ubmVjdGlvbiBsaWZlY3ljbGVcbiAqIC0gQ3JlYXRlcyBvYmplY3Qgc3RvcmVzIHZpYSBpbmplY3RlZCBJU3RvcmUgaW1wbGVtZW50YXRpb25zXG4gKiAtIFByb3ZpZGVzIHNoYXJlZCBJREJEYXRhYmFzZSBpbnN0YW5jZSB0byBhbGwgc2VydmljZXNcbiAqL1xuZXhwb3J0IGNsYXNzIEluZGV4ZWREQkNvbnRleHQge1xuICAgIGNvbnN0cnVjdG9yKHN0b3Jlcykge1xuICAgICAgICB0aGlzLmRiID0gbnVsbDtcbiAgICAgICAgdGhpcy5pbml0aWFsaXplZCA9IGZhbHNlO1xuICAgICAgICB0aGlzLnN0b3JlcyA9IHN0b3JlcztcbiAgICB9XG4gICAgLyoqXG4gICAgICogSW5pdGlhbGl6ZSBhbmQgb3BlbiB0aGUgZGF0YWJhc2VcbiAgICAgKi9cbiAgICBhc3luYyBpbml0aWFsaXplKCkge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICAgICAgY29uc3QgcmVxdWVzdCA9IGluZGV4ZWREQi5vcGVuKEluZGV4ZWREQkNvbnRleHQuREJfTkFNRSwgSW5kZXhlZERCQ29udGV4dC5EQl9WRVJTSU9OKTtcbiAgICAgICAgICAgIHJlcXVlc3Qub25lcnJvciA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICByZWplY3QobmV3IEVycm9yKGBGYWlsZWQgdG8gb3BlbiBJbmRleGVkREI6ICR7cmVxdWVzdC5lcnJvcn1gKSk7XG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgcmVxdWVzdC5vbnN1Y2Nlc3MgPSAoKSA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy5kYiA9IHJlcXVlc3QucmVzdWx0O1xuICAgICAgICAgICAgICAgIHRoaXMuaW5pdGlhbGl6ZWQgPSB0cnVlO1xuICAgICAgICAgICAgICAgIHJlc29sdmUoKTtcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICByZXF1ZXN0Lm9udXBncmFkZW5lZWRlZCA9IChldmVudCkgPT4ge1xuICAgICAgICAgICAgICAgIGNvbnN0IGRiID0gZXZlbnQudGFyZ2V0LnJlc3VsdDtcbiAgICAgICAgICAgICAgICAvLyBDcmVhdGUgYWxsIGVudGl0eSBzdG9yZXMgdmlhIGluamVjdGVkIElTdG9yZSBpbXBsZW1lbnRhdGlvbnNcbiAgICAgICAgICAgICAgICB0aGlzLnN0b3Jlcy5mb3JFYWNoKHN0b3JlID0+IHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKCFkYi5vYmplY3RTdG9yZU5hbWVzLmNvbnRhaW5zKHN0b3JlLnN0b3JlTmFtZSkpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHN0b3JlLmNyZWF0ZShkYik7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH07XG4gICAgICAgIH0pO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBDaGVjayBpZiBkYXRhYmFzZSBpcyBpbml0aWFsaXplZFxuICAgICAqL1xuICAgIGlzSW5pdGlhbGl6ZWQoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmluaXRpYWxpemVkO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBHZXQgSURCRGF0YWJhc2UgaW5zdGFuY2VcbiAgICAgKi9cbiAgICBnZXREYXRhYmFzZSgpIHtcbiAgICAgICAgaWYgKCF0aGlzLmRiKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0luZGV4ZWREQiBub3QgaW5pdGlhbGl6ZWQuIENhbGwgaW5pdGlhbGl6ZSgpIGZpcnN0LicpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0aGlzLmRiO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBDbG9zZSBkYXRhYmFzZSBjb25uZWN0aW9uXG4gICAgICovXG4gICAgY2xvc2UoKSB7XG4gICAgICAgIGlmICh0aGlzLmRiKSB7XG4gICAgICAgICAgICB0aGlzLmRiLmNsb3NlKCk7XG4gICAgICAgICAgICB0aGlzLmRiID0gbnVsbDtcbiAgICAgICAgICAgIHRoaXMuaW5pdGlhbGl6ZWQgPSBmYWxzZTtcbiAgICAgICAgfVxuICAgIH1cbiAgICAvKipcbiAgICAgKiBEZWxldGUgZW50aXJlIGRhdGFiYXNlIChmb3IgdGVzdGluZy9yZXNldClcbiAgICAgKi9cbiAgICBzdGF0aWMgYXN5bmMgZGVsZXRlRGF0YWJhc2UoKSB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgICAgICBjb25zdCByZXF1ZXN0ID0gaW5kZXhlZERCLmRlbGV0ZURhdGFiYXNlKEluZGV4ZWREQkNvbnRleHQuREJfTkFNRSk7XG4gICAgICAgICAgICByZXF1ZXN0Lm9uc3VjY2VzcyA9ICgpID0+IHJlc29sdmUoKTtcbiAgICAgICAgICAgIHJlcXVlc3Qub25lcnJvciA9ICgpID0+IHJlamVjdChuZXcgRXJyb3IoYEZhaWxlZCB0byBkZWxldGUgZGF0YWJhc2U6ICR7cmVxdWVzdC5lcnJvcn1gKSk7XG4gICAgICAgIH0pO1xuICAgIH1cbn1cbkluZGV4ZWREQkNvbnRleHQuREJfTkFNRSA9ICdDYWxlbmRhckRCJztcbkluZGV4ZWREQkNvbnRleHQuREJfVkVSU0lPTiA9IDQ7XG4iLCAiLyoqXG4gKiBFdmVudFN0b3JlIC0gSW5kZXhlZERCIE9iamVjdFN0b3JlIGRlZmluaXRpb24gZm9yIGNhbGVuZGFyIGV2ZW50c1xuICovXG5leHBvcnQgY2xhc3MgRXZlbnRTdG9yZSB7XG4gICAgY29uc3RydWN0b3IoKSB7XG4gICAgICAgIHRoaXMuc3RvcmVOYW1lID0gRXZlbnRTdG9yZS5TVE9SRV9OQU1FO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBDcmVhdGUgdGhlIGV2ZW50cyBPYmplY3RTdG9yZSB3aXRoIGluZGV4ZXNcbiAgICAgKi9cbiAgICBjcmVhdGUoZGIpIHtcbiAgICAgICAgY29uc3Qgc3RvcmUgPSBkYi5jcmVhdGVPYmplY3RTdG9yZShFdmVudFN0b3JlLlNUT1JFX05BTUUsIHsga2V5UGF0aDogJ2lkJyB9KTtcbiAgICAgICAgLy8gSW5kZXg6IHN0YXJ0IChmb3IgZGF0ZSByYW5nZSBxdWVyaWVzKVxuICAgICAgICBzdG9yZS5jcmVhdGVJbmRleCgnc3RhcnQnLCAnc3RhcnQnLCB7IHVuaXF1ZTogZmFsc2UgfSk7XG4gICAgICAgIC8vIEluZGV4OiBlbmQgKGZvciBkYXRlIHJhbmdlIHF1ZXJpZXMpXG4gICAgICAgIHN0b3JlLmNyZWF0ZUluZGV4KCdlbmQnLCAnZW5kJywgeyB1bmlxdWU6IGZhbHNlIH0pO1xuICAgICAgICAvLyBJbmRleDogc3luY1N0YXR1cyAoZm9yIGZpbHRlcmluZyBieSBzeW5jIHN0YXRlKVxuICAgICAgICBzdG9yZS5jcmVhdGVJbmRleCgnc3luY1N0YXR1cycsICdzeW5jU3RhdHVzJywgeyB1bmlxdWU6IGZhbHNlIH0pO1xuICAgICAgICAvLyBJbmRleDogcmVzb3VyY2VJZCAoZm9yIHJlc291cmNlLW1vZGUgZmlsdGVyaW5nKVxuICAgICAgICBzdG9yZS5jcmVhdGVJbmRleCgncmVzb3VyY2VJZCcsICdyZXNvdXJjZUlkJywgeyB1bmlxdWU6IGZhbHNlIH0pO1xuICAgICAgICAvLyBJbmRleDogY3VzdG9tZXJJZCAoZm9yIGN1c3RvbWVyLWNlbnRyaWMgcXVlcmllcylcbiAgICAgICAgc3RvcmUuY3JlYXRlSW5kZXgoJ2N1c3RvbWVySWQnLCAnY3VzdG9tZXJJZCcsIHsgdW5pcXVlOiBmYWxzZSB9KTtcbiAgICAgICAgLy8gSW5kZXg6IGJvb2tpbmdJZCAoZm9yIGV2ZW50LXRvLWJvb2tpbmcgbG9va3VwcylcbiAgICAgICAgc3RvcmUuY3JlYXRlSW5kZXgoJ2Jvb2tpbmdJZCcsICdib29raW5nSWQnLCB7IHVuaXF1ZTogZmFsc2UgfSk7XG4gICAgICAgIC8vIENvbXBvdW5kIGluZGV4OiBzdGFydEVuZCAoZm9yIG9wdGltaXplZCByYW5nZSBxdWVyaWVzKVxuICAgICAgICBzdG9yZS5jcmVhdGVJbmRleCgnc3RhcnRFbmQnLCBbJ3N0YXJ0JywgJ2VuZCddLCB7IHVuaXF1ZTogZmFsc2UgfSk7XG4gICAgfVxufVxuRXZlbnRTdG9yZS5TVE9SRV9OQU1FID0gJ2V2ZW50cyc7XG4iLCAiLyoqXG4gKiBFdmVudFNlcmlhbGl6YXRpb24gLSBIYW5kbGVzIERhdGUgZmllbGQgc2VyaWFsaXphdGlvbiBmb3IgSW5kZXhlZERCXG4gKlxuICogSW5kZXhlZERCIGRvZXNuJ3Qgc3RvcmUgRGF0ZSBvYmplY3RzIGRpcmVjdGx5LCBzbyB3ZSBjb252ZXJ0OlxuICogLSBEYXRlIFx1MjE5MiBJU08gc3RyaW5nIChzZXJpYWxpemUpIHdoZW4gd3JpdGluZ1xuICogLSBJU08gc3RyaW5nIFx1MjE5MiBEYXRlIChkZXNlcmlhbGl6ZSkgd2hlbiByZWFkaW5nXG4gKi9cbmV4cG9ydCBjbGFzcyBFdmVudFNlcmlhbGl6YXRpb24ge1xuICAgIC8qKlxuICAgICAqIFNlcmlhbGl6ZSBldmVudCBmb3IgSW5kZXhlZERCIHN0b3JhZ2VcbiAgICAgKi9cbiAgICBzdGF0aWMgc2VyaWFsaXplKGV2ZW50KSB7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAuLi5ldmVudCxcbiAgICAgICAgICAgIHN0YXJ0OiBldmVudC5zdGFydCBpbnN0YW5jZW9mIERhdGUgPyBldmVudC5zdGFydC50b0lTT1N0cmluZygpIDogZXZlbnQuc3RhcnQsXG4gICAgICAgICAgICBlbmQ6IGV2ZW50LmVuZCBpbnN0YW5jZW9mIERhdGUgPyBldmVudC5lbmQudG9JU09TdHJpbmcoKSA6IGV2ZW50LmVuZFxuICAgICAgICB9O1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBEZXNlcmlhbGl6ZSBldmVudCBmcm9tIEluZGV4ZWREQiBzdG9yYWdlXG4gICAgICovXG4gICAgc3RhdGljIGRlc2VyaWFsaXplKGRhdGEpIHtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIC4uLmRhdGEsXG4gICAgICAgICAgICBzdGFydDogdHlwZW9mIGRhdGEuc3RhcnQgPT09ICdzdHJpbmcnID8gbmV3IERhdGUoZGF0YS5zdGFydCkgOiBkYXRhLnN0YXJ0LFxuICAgICAgICAgICAgZW5kOiB0eXBlb2YgZGF0YS5lbmQgPT09ICdzdHJpbmcnID8gbmV3IERhdGUoZGF0YS5lbmQpIDogZGF0YS5lbmRcbiAgICAgICAgfTtcbiAgICB9XG59XG4iLCAiLyoqXG4gKiBTeW5jUGx1Z2luPFQgZXh0ZW5kcyBJU3luYz4gLSBQbHVnZ2FibGUgc3luYyBmdW5jdGlvbmFsaXR5IGZvciBlbnRpdHkgc2VydmljZXNcbiAqXG4gKiBDT01QT1NJVElPTiBQQVRURVJOOlxuICogLSBFbmNhcHN1bGF0ZXMgYWxsIHN5bmMtcmVsYXRlZCBsb2dpYyBpbiBzZXBhcmF0ZSBjbGFzc1xuICogLSBDb21wb3NlZCBpbnRvIEJhc2VFbnRpdHlTZXJ2aWNlIChub3QgaW5oZXJpdGFuY2UpXG4gKi9cbmV4cG9ydCBjbGFzcyBTeW5jUGx1Z2luIHtcbiAgICBjb25zdHJ1Y3RvcihzZXJ2aWNlKSB7XG4gICAgICAgIHRoaXMuc2VydmljZSA9IHNlcnZpY2U7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIE1hcmsgZW50aXR5IGFzIHN1Y2Nlc3NmdWxseSBzeW5jZWRcbiAgICAgKi9cbiAgICBhc3luYyBtYXJrQXNTeW5jZWQoaWQpIHtcbiAgICAgICAgY29uc3QgZW50aXR5ID0gYXdhaXQgdGhpcy5zZXJ2aWNlLmdldChpZCk7XG4gICAgICAgIGlmIChlbnRpdHkpIHtcbiAgICAgICAgICAgIGVudGl0eS5zeW5jU3RhdHVzID0gJ3N5bmNlZCc7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLnNlcnZpY2Uuc2F2ZShlbnRpdHkpO1xuICAgICAgICB9XG4gICAgfVxuICAgIC8qKlxuICAgICAqIE1hcmsgZW50aXR5IGFzIHN5bmMgZXJyb3JcbiAgICAgKi9cbiAgICBhc3luYyBtYXJrQXNFcnJvcihpZCkge1xuICAgICAgICBjb25zdCBlbnRpdHkgPSBhd2FpdCB0aGlzLnNlcnZpY2UuZ2V0KGlkKTtcbiAgICAgICAgaWYgKGVudGl0eSkge1xuICAgICAgICAgICAgZW50aXR5LnN5bmNTdGF0dXMgPSAnZXJyb3InO1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5zZXJ2aWNlLnNhdmUoZW50aXR5KTtcbiAgICAgICAgfVxuICAgIH1cbiAgICAvKipcbiAgICAgKiBHZXQgY3VycmVudCBzeW5jIHN0YXR1cyBmb3IgYW4gZW50aXR5XG4gICAgICovXG4gICAgYXN5bmMgZ2V0U3luY1N0YXR1cyhpZCkge1xuICAgICAgICBjb25zdCBlbnRpdHkgPSBhd2FpdCB0aGlzLnNlcnZpY2UuZ2V0KGlkKTtcbiAgICAgICAgcmV0dXJuIGVudGl0eSA/IGVudGl0eS5zeW5jU3RhdHVzIDogbnVsbDtcbiAgICB9XG4gICAgLyoqXG4gICAgICogR2V0IGVudGl0aWVzIGJ5IHN5bmMgc3RhdHVzIHVzaW5nIEluZGV4ZWREQiBpbmRleFxuICAgICAqL1xuICAgIGFzeW5jIGdldEJ5U3luY1N0YXR1cyhzeW5jU3RhdHVzKSB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgICAgICBjb25zdCB0cmFuc2FjdGlvbiA9IHRoaXMuc2VydmljZS5kYi50cmFuc2FjdGlvbihbdGhpcy5zZXJ2aWNlLnN0b3JlTmFtZV0sICdyZWFkb25seScpO1xuICAgICAgICAgICAgY29uc3Qgc3RvcmUgPSB0cmFuc2FjdGlvbi5vYmplY3RTdG9yZSh0aGlzLnNlcnZpY2Uuc3RvcmVOYW1lKTtcbiAgICAgICAgICAgIGNvbnN0IGluZGV4ID0gc3RvcmUuaW5kZXgoJ3N5bmNTdGF0dXMnKTtcbiAgICAgICAgICAgIGNvbnN0IHJlcXVlc3QgPSBpbmRleC5nZXRBbGwoc3luY1N0YXR1cyk7XG4gICAgICAgICAgICByZXF1ZXN0Lm9uc3VjY2VzcyA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICBjb25zdCBkYXRhID0gcmVxdWVzdC5yZXN1bHQ7XG4gICAgICAgICAgICAgICAgY29uc3QgZW50aXRpZXMgPSBkYXRhLm1hcChpdGVtID0+IHRoaXMuc2VydmljZS5kZXNlcmlhbGl6ZShpdGVtKSk7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZShlbnRpdGllcyk7XG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgcmVxdWVzdC5vbmVycm9yID0gKCkgPT4ge1xuICAgICAgICAgICAgICAgIHJlamVjdChuZXcgRXJyb3IoYEZhaWxlZCB0byBnZXQgYnkgc3luYyBzdGF0dXMgJHtzeW5jU3RhdHVzfTogJHtyZXF1ZXN0LmVycm9yfWApKTtcbiAgICAgICAgICAgIH07XG4gICAgICAgIH0pO1xuICAgIH1cbn1cbiIsICIvKipcbiAqIENvcmVFdmVudHMgLSBDb25zb2xpZGF0ZWQgZXNzZW50aWFsIGV2ZW50cyBmb3IgdGhlIGNhbGVuZGFyXG4gKi9cbmV4cG9ydCBjb25zdCBDb3JlRXZlbnRzID0ge1xuICAgIC8vIExpZmVjeWNsZSBldmVudHNcbiAgICBJTklUSUFMSVpFRDogJ2NvcmU6aW5pdGlhbGl6ZWQnLFxuICAgIFJFQURZOiAnY29yZTpyZWFkeScsXG4gICAgREVTVFJPWUVEOiAnY29yZTpkZXN0cm95ZWQnLFxuICAgIC8vIFZpZXcgZXZlbnRzXG4gICAgVklFV19DSEFOR0VEOiAndmlldzpjaGFuZ2VkJyxcbiAgICBWSUVXX1JFTkRFUkVEOiAndmlldzpyZW5kZXJlZCcsXG4gICAgLy8gTmF2aWdhdGlvbiBldmVudHNcbiAgICBEQVRFX0NIQU5HRUQ6ICduYXY6ZGF0ZS1jaGFuZ2VkJyxcbiAgICBOQVZJR0FUSU9OX0NPTVBMRVRFRDogJ25hdjpuYXZpZ2F0aW9uLWNvbXBsZXRlZCcsXG4gICAgLy8gRGF0YSBldmVudHNcbiAgICBEQVRBX0xPQURJTkc6ICdkYXRhOmxvYWRpbmcnLFxuICAgIERBVEFfTE9BREVEOiAnZGF0YTpsb2FkZWQnLFxuICAgIERBVEFfRVJST1I6ICdkYXRhOmVycm9yJyxcbiAgICAvLyBHcmlkIGV2ZW50c1xuICAgIEdSSURfUkVOREVSRUQ6ICdncmlkOnJlbmRlcmVkJyxcbiAgICBHUklEX0NMSUNLRUQ6ICdncmlkOmNsaWNrZWQnLFxuICAgIC8vIEV2ZW50IG1hbmFnZW1lbnRcbiAgICBFVkVOVF9DUkVBVEVEOiAnZXZlbnQ6Y3JlYXRlZCcsXG4gICAgRVZFTlRfVVBEQVRFRDogJ2V2ZW50OnVwZGF0ZWQnLFxuICAgIEVWRU5UX0RFTEVURUQ6ICdldmVudDpkZWxldGVkJyxcbiAgICBFVkVOVF9TRUxFQ1RFRDogJ2V2ZW50OnNlbGVjdGVkJyxcbiAgICAvLyBFdmVudCBkcmFnLWRyb3BcbiAgICBFVkVOVF9EUkFHX1NUQVJUOiAnZXZlbnQ6ZHJhZy1zdGFydCcsXG4gICAgRVZFTlRfRFJBR19NT1ZFOiAnZXZlbnQ6ZHJhZy1tb3ZlJyxcbiAgICBFVkVOVF9EUkFHX0VORDogJ2V2ZW50OmRyYWctZW5kJyxcbiAgICBFVkVOVF9EUkFHX0NBTkNFTDogJ2V2ZW50OmRyYWctY2FuY2VsJyxcbiAgICBFVkVOVF9EUkFHX0NPTFVNTl9DSEFOR0U6ICdldmVudDpkcmFnLWNvbHVtbi1jaGFuZ2UnLFxuICAgIC8vIEhlYWRlciBkcmFnICh0aW1lZCBcdTIxOTIgaGVhZGVyIGNvbnZlcnNpb24pXG4gICAgRVZFTlRfRFJBR19FTlRFUl9IRUFERVI6ICdldmVudDpkcmFnLWVudGVyLWhlYWRlcicsXG4gICAgRVZFTlRfRFJBR19NT1ZFX0hFQURFUjogJ2V2ZW50OmRyYWctbW92ZS1oZWFkZXInLFxuICAgIEVWRU5UX0RSQUdfTEVBVkVfSEVBREVSOiAnZXZlbnQ6ZHJhZy1sZWF2ZS1oZWFkZXInLFxuICAgIC8vIEV2ZW50IHJlc2l6ZVxuICAgIEVWRU5UX1JFU0laRV9TVEFSVDogJ2V2ZW50OnJlc2l6ZS1zdGFydCcsXG4gICAgRVZFTlRfUkVTSVpFX0VORDogJ2V2ZW50OnJlc2l6ZS1lbmQnLFxuICAgIC8vIEVkZ2Ugc2Nyb2xsXG4gICAgRURHRV9TQ1JPTExfVElDSzogJ2VkZ2Utc2Nyb2xsOnRpY2snLFxuICAgIEVER0VfU0NST0xMX1NUQVJURUQ6ICdlZGdlLXNjcm9sbDpzdGFydGVkJyxcbiAgICBFREdFX1NDUk9MTF9TVE9QUEVEOiAnZWRnZS1zY3JvbGw6c3RvcHBlZCcsXG4gICAgLy8gU3lzdGVtIGV2ZW50c1xuICAgIEVSUk9SOiAnc3lzdGVtOmVycm9yJyxcbiAgICAvLyBTeW5jIGV2ZW50c1xuICAgIFNZTkNfU1RBUlRFRDogJ3N5bmM6c3RhcnRlZCcsXG4gICAgU1lOQ19DT01QTEVURUQ6ICdzeW5jOmNvbXBsZXRlZCcsXG4gICAgU1lOQ19GQUlMRUQ6ICdzeW5jOmZhaWxlZCcsXG4gICAgLy8gRW50aXR5IGV2ZW50cyAtIGZvciBhdWRpdCBhbmQgc3luY1xuICAgIEVOVElUWV9TQVZFRDogJ2VudGl0eTpzYXZlZCcsXG4gICAgRU5USVRZX0RFTEVURUQ6ICdlbnRpdHk6ZGVsZXRlZCcsXG4gICAgLy8gQXVkaXQgZXZlbnRzXG4gICAgQVVESVRfTE9HR0VEOiAnYXVkaXQ6bG9nZ2VkJyxcbiAgICAvLyBSZW5kZXJpbmcgZXZlbnRzXG4gICAgRVZFTlRTX1JFTkRFUkVEOiAnZXZlbnRzOnJlbmRlcmVkJ1xufTtcbiIsICJleHBvcnQgZnVuY3Rpb24gc3BsaXRKU09OUGF0aChwYXRoOiBzdHJpbmcpOiBzdHJpbmdbXSB7XG4gICAgY29uc3QgcGFydHM6IHN0cmluZ1tdID0gW107XG4gICAgbGV0IGN1cnJlbnRQYXJ0ID0gJyc7XG4gICAgbGV0IGluU2luZ2xlUXVvdGVzID0gZmFsc2U7XG4gICAgbGV0IGluQnJhY2tldHMgPSAwO1xuXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBwYXRoLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIGNvbnN0IGNoYXIgPSBwYXRoW2ldO1xuXG4gICAgICAgIGlmIChjaGFyID09PSBcIidcIiAmJiBwYXRoW2kgLSAxXSAhPT0gJ1xcXFwnKSB7XG4gICAgICAgICAgICAvLyBUb2dnbGUgc2luZ2xlIHF1b3RlIGZsYWcgaWYgbm90IGVzY2FwZWRcbiAgICAgICAgICAgIGluU2luZ2xlUXVvdGVzID0gIWluU2luZ2xlUXVvdGVzO1xuICAgICAgICB9IGVsc2UgaWYgKGNoYXIgPT09ICdbJyAmJiAhaW5TaW5nbGVRdW90ZXMpIHtcbiAgICAgICAgICAgIC8vIEluY3JlYXNlIGJyYWNrZXQgbmVzdGluZyBsZXZlbFxuICAgICAgICAgICAgaW5CcmFja2V0cysrO1xuICAgICAgICB9IGVsc2UgaWYgKGNoYXIgPT09ICddJyAmJiAhaW5TaW5nbGVRdW90ZXMpIHtcbiAgICAgICAgICAgIC8vIERlY3JlYXNlIGJyYWNrZXQgbmVzdGluZyBsZXZlbFxuICAgICAgICAgICAgaW5CcmFja2V0cy0tO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKGNoYXIgPT09ICcuJyAmJiAhaW5TaW5nbGVRdW90ZXMgJiYgaW5CcmFja2V0cyA9PT0gMCkge1xuICAgICAgICAgICAgLy8gU3BsaXQgYXQgcGVyaW9kIGlmIG5vdCBpbiBxdW90ZXMgb3IgYnJhY2tldHNcbiAgICAgICAgICAgIHBhcnRzLnB1c2goY3VycmVudFBhcnQpO1xuICAgICAgICAgICAgY3VycmVudFBhcnQgPSAnJztcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIC8vIE90aGVyd2lzZSwga2VlcCBhZGRpbmcgdG8gdGhlIGN1cnJlbnQgcGFydFxuICAgICAgICAgICAgY3VycmVudFBhcnQgKz0gY2hhcjtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8vIEFkZCB0aGUgbGFzdCBwYXJ0IGlmIHRoZXJlJ3MgYW55XG4gICAgaWYgKGN1cnJlbnRQYXJ0ICE9PSAnJykge1xuICAgICAgICBwYXJ0cy5wdXNoKGN1cnJlbnRQYXJ0KTtcbiAgICB9XG5cbiAgICByZXR1cm4gcGFydHM7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBhcnJheURpZmZlcmVuY2U8VD4oZmlyc3Q6IFRbXSwgc2Vjb25kOiBUW10pOiBUW10ge1xuICAgIGNvbnN0IHNlY29uZFNldCA9IG5ldyBTZXQoc2Vjb25kKTtcbiAgICByZXR1cm4gZmlyc3QuZmlsdGVyKGl0ZW0gPT4gIXNlY29uZFNldC5oYXMoaXRlbSkpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gYXJyYXlJbnRlcnNlY3Rpb248VD4oZmlyc3Q6IFRbXSwgc2Vjb25kOiBUW10pOiBUW10ge1xuICAgIGNvbnN0IHNlY29uZFNldCA9IG5ldyBTZXQoc2Vjb25kKTtcbiAgICByZXR1cm4gZmlyc3QuZmlsdGVyKGl0ZW0gPT4gc2Vjb25kU2V0LmhhcyhpdGVtKSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBrZXlCeTxUPihhcnI6IFRbXSwgZ2V0S2V5OiAoaXRlbTogVCkgPT4gYW55KTogUmVjb3JkPHN0cmluZywgVD4ge1xuICAgIGNvbnN0IHJlc3VsdDogUmVjb3JkPHN0cmluZywgVD4gPSB7fTtcbiAgICBmb3IgKGNvbnN0IGl0ZW0gb2YgYXJyKSB7XG4gICAgICAgIHJlc3VsdFtTdHJpbmcoZ2V0S2V5KGl0ZW0pKV0gPSBpdGVtO1xuICAgIH1cbiAgICByZXR1cm4gcmVzdWx0O1xufVxuXG5leHBvcnQgZnVuY3Rpb24gc2V0QnlQYXRoKG9iajogYW55LCBwYXRoOiBzdHJpbmcsIHZhbHVlOiBhbnkpOiB2b2lkIHtcbiAgICBjb25zdCBwYXJ0cyA9IHBhdGgucmVwbGFjZSgvXFxbKFxcZCspXFxdL2csICcuJDEnKS5zcGxpdCgnLicpLmZpbHRlcihCb29sZWFuKTtcbiAgICBsZXQgY3VycmVudCA9IG9iajtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHBhcnRzLmxlbmd0aCAtIDE7IGkrKykge1xuICAgICAgICBjb25zdCBwYXJ0ID0gcGFydHNbaV07XG4gICAgICAgIGlmICghKHBhcnQgaW4gY3VycmVudCkpIHtcbiAgICAgICAgICAgIGN1cnJlbnRbcGFydF0gPSAvXlxcZCskLy50ZXN0KHBhcnRzW2kgKyAxXSkgPyBbXSA6IHt9O1xuICAgICAgICB9XG4gICAgICAgIGN1cnJlbnQgPSBjdXJyZW50W3BhcnRdO1xuICAgIH1cbiAgICBjdXJyZW50W3BhcnRzW3BhcnRzLmxlbmd0aCAtIDFdXSA9IHZhbHVlO1xufVxuIiwgImltcG9ydCB7IGFycmF5RGlmZmVyZW5jZSBhcyBkaWZmZXJlbmNlLCBhcnJheUludGVyc2VjdGlvbiBhcyBpbnRlcnNlY3Rpb24sIGtleUJ5LCBzcGxpdEpTT05QYXRoIH0gZnJvbSAnLi9oZWxwZXJzLmpzJztcblxudHlwZSBGdW5jdGlvbktleSA9IChvYmo6IGFueSwgc2hvdWxkUmV0dXJuS2V5TmFtZT86IGJvb2xlYW4pID0+IGFueTtcbnR5cGUgRW1iZWRkZWRPYmpLZXlzVHlwZSA9IFJlY29yZDxzdHJpbmcsIHN0cmluZyB8IEZ1bmN0aW9uS2V5PjtcbnR5cGUgRW1iZWRkZWRPYmpLZXlzTWFwVHlwZSA9IE1hcDxzdHJpbmcgfCBSZWdFeHAsIHN0cmluZyB8IEZ1bmN0aW9uS2V5PjtcbmVudW0gT3BlcmF0aW9uIHtcbiAgUkVNT1ZFID0gJ1JFTU9WRScsXG4gIEFERCA9ICdBREQnLFxuICBVUERBVEUgPSAnVVBEQVRFJ1xufVxuXG5pbnRlcmZhY2UgSUNoYW5nZSB7XG4gIHR5cGU6IE9wZXJhdGlvbjtcbiAga2V5OiBzdHJpbmc7XG4gIGVtYmVkZGVkS2V5Pzogc3RyaW5nIHwgRnVuY3Rpb25LZXk7XG4gIHZhbHVlPzogYW55O1xuICBvbGRWYWx1ZT86IGFueTtcbiAgY2hhbmdlcz86IElDaGFuZ2VbXTtcbn1cbnR5cGUgQ2hhbmdlc2V0ID0gSUNoYW5nZVtdO1xuXG5pbnRlcmZhY2UgSUF0b21pY0NoYW5nZSB7XG4gIHR5cGU6IE9wZXJhdGlvbjtcbiAga2V5OiBzdHJpbmc7XG4gIHBhdGg6IHN0cmluZztcbiAgdmFsdWVUeXBlOiBzdHJpbmcgfCBudWxsO1xuICB2YWx1ZT86IGFueTtcbiAgb2xkVmFsdWU/OiBhbnk7XG59XG5cbmludGVyZmFjZSBPcHRpb25zIHtcbiAgZW1iZWRkZWRPYmpLZXlzPzogRW1iZWRkZWRPYmpLZXlzVHlwZSB8IEVtYmVkZGVkT2JqS2V5c01hcFR5cGU7XG4gIGtleXNUb1NraXA/OiBzdHJpbmdbXTtcbiAgdHJlYXRUeXBlQ2hhbmdlQXNSZXBsYWNlPzogYm9vbGVhbjtcbn1cblxuLyoqXG4gKiBDb21wdXRlcyB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHR3byBvYmplY3RzLlxuICpcbiAqIEBwYXJhbSB7YW55fSBvbGRPYmogLSBUaGUgb3JpZ2luYWwgb2JqZWN0LlxuICogQHBhcmFtIHthbnl9IG5ld09iaiAtIFRoZSB1cGRhdGVkIG9iamVjdC5cbiAqIEBwYXJhbSB7T3B0aW9uc30gb3B0aW9ucyAtIEFuIG9wdGlvbmFsIHBhcmFtZXRlciBzcGVjaWZ5aW5nIGtleXMgb2YgZW1iZWRkZWQgb2JqZWN0cyBhbmQga2V5cyB0byBza2lwLlxuICogQHJldHVybnMge0lDaGFuZ2VbXX0gLSBBbiBhcnJheSBvZiBjaGFuZ2VzIHRoYXQgdHJhbnNmb3JtIHRoZSBvbGQgb2JqZWN0IGludG8gdGhlIG5ldyBvYmplY3QuXG4gKi9cbmZ1bmN0aW9uIGRpZmYob2xkT2JqOiBhbnksIG5ld09iajogYW55LCBvcHRpb25zOiBPcHRpb25zID0ge30pOiBJQ2hhbmdlW10ge1xuICBsZXQgeyBlbWJlZGRlZE9iaktleXMgfSA9IG9wdGlvbnM7XG4gIGNvbnN0IHsga2V5c1RvU2tpcCwgdHJlYXRUeXBlQ2hhbmdlQXNSZXBsYWNlIH0gPSBvcHRpb25zO1xuXG4gIC8vIFRyaW0gbGVhZGluZyAnLicgZnJvbSBrZXlzIGluIGVtYmVkZGVkT2JqS2V5c1xuICBpZiAoZW1iZWRkZWRPYmpLZXlzIGluc3RhbmNlb2YgTWFwKSB7XG4gICAgZW1iZWRkZWRPYmpLZXlzID0gbmV3IE1hcChcbiAgICAgIEFycmF5LmZyb20oZW1iZWRkZWRPYmpLZXlzLmVudHJpZXMoKSkubWFwKChba2V5LCB2YWx1ZV0pID0+IFtcbiAgICAgICAga2V5IGluc3RhbmNlb2YgUmVnRXhwID8ga2V5IDoga2V5LnJlcGxhY2UoL15cXC4vLCAnJyksXG4gICAgICAgIHZhbHVlXG4gICAgICBdKVxuICAgICk7XG4gIH0gZWxzZSBpZiAoZW1iZWRkZWRPYmpLZXlzKSB7XG4gICAgZW1iZWRkZWRPYmpLZXlzID0gT2JqZWN0LmZyb21FbnRyaWVzKFxuICAgICAgT2JqZWN0LmVudHJpZXMoZW1iZWRkZWRPYmpLZXlzKS5tYXAoKFtrZXksIHZhbHVlXSkgPT4gW2tleS5yZXBsYWNlKC9eXFwuLywgJycpLCB2YWx1ZV0pXG4gICAgKTtcbiAgfVxuXG4gIC8vIENvbXBhcmUgb2xkIGFuZCBuZXcgb2JqZWN0cyB0byBnZW5lcmF0ZSBhIGxpc3Qgb2YgY2hhbmdlc1xuICByZXR1cm4gY29tcGFyZShvbGRPYmosIG5ld09iaiwgW10sIFtdLCB7XG4gICAgZW1iZWRkZWRPYmpLZXlzLFxuICAgIGtleXNUb1NraXA6IGtleXNUb1NraXAgPz8gW10sXG4gICAgdHJlYXRUeXBlQ2hhbmdlQXNSZXBsYWNlOiB0cmVhdFR5cGVDaGFuZ2VBc1JlcGxhY2UgPz8gdHJ1ZVxuICB9KTtcbn1cblxuLyoqXG4gKiBBcHBsaWVzIGFsbCBjaGFuZ2VzIGluIHRoZSBjaGFuZ2VzZXQgdG8gdGhlIG9iamVjdC5cbiAqXG4gKiBAcGFyYW0ge2FueX0gb2JqIC0gVGhlIG9iamVjdCB0byBhcHBseSBjaGFuZ2VzIHRvLlxuICogQHBhcmFtIHtDaGFuZ2VzZXR9IGNoYW5nZXNldCAtIFRoZSBjaGFuZ2VzZXQgdG8gYXBwbHkuXG4gKiBAcmV0dXJucyB7YW55fSAtIFRoZSBvYmplY3QgYWZ0ZXIgdGhlIGNoYW5nZXMgZnJvbSB0aGUgY2hhbmdlc2V0IGhhdmUgYmVlbiBhcHBsaWVkLlxuICpcbiAqIFRoZSBmdW5jdGlvbiBmaXJzdCBjaGVja3MgaWYgYSBjaGFuZ2VzZXQgaXMgcHJvdmlkZWQuIElmIHNvLCBpdCBpdGVyYXRlcyBvdmVyIGVhY2ggY2hhbmdlIGluIHRoZSBjaGFuZ2VzZXQuXG4gKiBJZiB0aGUgY2hhbmdlIHZhbHVlIGlzIG5vdCBudWxsIG9yIHVuZGVmaW5lZCwgb3IgaWYgdGhlIGNoYW5nZSB0eXBlIGlzIFJFTU9WRSwgb3IgaWYgdGhlIHZhbHVlIGlzIG51bGwgYW5kIHRoZSB0eXBlIGlzIEFERCxcbiAqIGl0IGFwcGxpZXMgdGhlIGNoYW5nZSB0byB0aGUgb2JqZWN0IGRpcmVjdGx5LlxuICogT3RoZXJ3aXNlLCBpdCBhcHBsaWVzIHRoZSBjaGFuZ2UgdG8gdGhlIGNvcnJlc3BvbmRpbmcgYnJhbmNoIG9mIHRoZSBvYmplY3QuXG4gKi9cbmNvbnN0IGFwcGx5Q2hhbmdlc2V0ID0gKG9iajogYW55LCBjaGFuZ2VzZXQ6IENoYW5nZXNldCkgPT4ge1xuICBpZiAoY2hhbmdlc2V0KSB7XG4gICAgY2hhbmdlc2V0LmZvckVhY2goKGNoYW5nZSkgPT4ge1xuICAgICAgY29uc3QgeyB0eXBlLCBrZXksIHZhbHVlLCBlbWJlZGRlZEtleSB9ID0gY2hhbmdlO1xuXG4gICAgICAvLyBIYW5kbGUgbnVsbCB2YWx1ZXMgYXMgbGVhZiBjaGFuZ2VzIHdoZW4gdGhlIG9wZXJhdGlvbiBpcyBBRERcbiAgICAgIGlmICgodmFsdWUgIT09IG51bGwgJiYgdmFsdWUgIT09IHVuZGVmaW5lZCkgfHwgdHlwZSA9PT0gT3BlcmF0aW9uLlJFTU9WRSB8fCAodmFsdWUgPT09IG51bGwgJiYgdHlwZSA9PT0gT3BlcmF0aW9uLkFERCkpIHtcbiAgICAgICAgLy8gQXBwbHkgdGhlIGNoYW5nZSB0byB0aGUgb2JqZWN0XG4gICAgICAgIGFwcGx5TGVhZkNoYW5nZShvYmosIGNoYW5nZSwgZW1iZWRkZWRLZXkpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gQXBwbHkgdGhlIGNoYW5nZSB0byB0aGUgYnJhbmNoXG4gICAgICAgIGFwcGx5QnJhbmNoQ2hhbmdlKG9ialtrZXldLCBjaGFuZ2UpO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG4gIHJldHVybiBvYmo7XG59O1xuXG4vKipcbiAqIFJldmVydHMgdGhlIGNoYW5nZXMgbWFkZSB0byBhbiBvYmplY3QgYmFzZWQgb24gYSBnaXZlbiBjaGFuZ2VzZXQuXG4gKlxuICogQHBhcmFtIHthbnl9IG9iaiAtIFRoZSBvYmplY3Qgb24gd2hpY2ggdG8gcmV2ZXJ0IGNoYW5nZXMuXG4gKiBAcGFyYW0ge0NoYW5nZXNldH0gY2hhbmdlc2V0IC0gVGhlIGNoYW5nZXNldCB0byByZXZlcnQuXG4gKiBAcmV0dXJucyB7YW55fSAtIFRoZSBvYmplY3QgYWZ0ZXIgdGhlIGNoYW5nZXMgZnJvbSB0aGUgY2hhbmdlc2V0IGhhdmUgYmVlbiByZXZlcnRlZC5cbiAqXG4gKiBUaGUgZnVuY3Rpb24gZmlyc3QgY2hlY2tzIGlmIGEgY2hhbmdlc2V0IGlzIHByb3ZpZGVkLiBJZiBzbywgaXQgcmV2ZXJzZXMgdGhlIGNoYW5nZXNldCB0byBzdGFydCByZXZlcnRpbmcgZnJvbSB0aGUgbGFzdCBjaGFuZ2UuXG4gKiBJdCB0aGVuIGl0ZXJhdGVzIG92ZXIgZWFjaCBjaGFuZ2UgaW4gdGhlIGNoYW5nZXNldC4gSWYgdGhlIGNoYW5nZSBkb2VzIG5vdCBoYXZlIGFueSBuZXN0ZWQgY2hhbmdlcywgb3IgaWYgdGhlIHZhbHVlIGlzIG51bGwgYW5kXG4gKiB0aGUgdHlwZSBpcyBSRU1PVkUgKHdoaWNoIHdvdWxkIGJlIHJldmVydGluZyBhbiBBREQgb3BlcmF0aW9uKSwgaXQgcmV2ZXJ0cyB0aGUgY2hhbmdlIG9uIHRoZSBvYmplY3QgZGlyZWN0bHkuXG4gKiBJZiB0aGUgY2hhbmdlIGRvZXMgaGF2ZSBuZXN0ZWQgY2hhbmdlcywgaXQgcmV2ZXJ0cyB0aGUgY2hhbmdlcyBvbiB0aGUgY29ycmVzcG9uZGluZyBicmFuY2ggb2YgdGhlIG9iamVjdC5cbiAqL1xuY29uc3QgcmV2ZXJ0Q2hhbmdlc2V0ID0gKG9iajogYW55LCBjaGFuZ2VzZXQ6IENoYW5nZXNldCkgPT4ge1xuICBpZiAoY2hhbmdlc2V0KSB7XG4gICAgY2hhbmdlc2V0XG4gICAgICAucmV2ZXJzZSgpXG4gICAgICAuZm9yRWFjaCgoY2hhbmdlOiBJQ2hhbmdlKTogYW55ID0+IHtcbiAgICAgICAgY29uc3QgeyB2YWx1ZSwgdHlwZSB9ID0gY2hhbmdlO1xuICAgICAgICAvLyBIYW5kbGUgbnVsbCB2YWx1ZXMgYXMgbGVhZiBjaGFuZ2VzIHdoZW4gdGhlIG9wZXJhdGlvbiBpcyBSRU1PVkUgKHNpbmNlIHdlJ3JlIHJldmVyc2luZyBBREQpXG4gICAgICAgIGlmICghY2hhbmdlLmNoYW5nZXMgfHwgKHZhbHVlID09PSBudWxsICYmIHR5cGUgPT09IE9wZXJhdGlvbi5SRU1PVkUpKSB7XG4gICAgICAgICAgcmV2ZXJ0TGVhZkNoYW5nZShvYmosIGNoYW5nZSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgcmV2ZXJ0QnJhbmNoQ2hhbmdlKG9ialtjaGFuZ2Uua2V5XSwgY2hhbmdlKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gIH1cblxuICByZXR1cm4gb2JqO1xufTtcblxuLyoqXG4gKiBBdG9taXplIGEgY2hhbmdlc2V0IGludG8gYW4gYXJyYXkgb2Ygc2luZ2xlIGNoYW5nZXMuXG4gKlxuICogQHBhcmFtIHtDaGFuZ2VzZXQgfCBJQ2hhbmdlfSBvYmogLSBUaGUgY2hhbmdlc2V0IG9yIGNoYW5nZSB0byBmbGF0dGVuLlxuICogQHBhcmFtIHtzdHJpbmd9IFtwYXRoPSckJ10gLSBUaGUgY3VycmVudCBwYXRoIGluIHRoZSBjaGFuZ2VzZXQuXG4gKiBAcGFyYW0ge3N0cmluZyB8IEZ1bmN0aW9uS2V5fSBbZW1iZWRkZWRLZXldIC0gVGhlIGtleSB0byB1c2UgZm9yIGVtYmVkZGVkIG9iamVjdHMuXG4gKiBAcmV0dXJucyB7SUF0b21pY0NoYW5nZVtdfSAtIEFuIGFycmF5IG9mIGF0b21pYyBjaGFuZ2VzLlxuICpcbiAqIFRoZSBmdW5jdGlvbiBmaXJzdCBjaGVja3MgaWYgdGhlIGlucHV0IGlzIGFuIGFycmF5LiBJZiBzbywgaXQgcmVjdXJzaXZlbHkgYXRvbWl6ZSBlYWNoIGNoYW5nZSBpbiB0aGUgYXJyYXkuXG4gKiBJZiB0aGUgaW5wdXQgaXMgbm90IGFuIGFycmF5LCBpdCBjaGVja3MgaWYgdGhlIGNoYW5nZSBoYXMgbmVzdGVkIGNoYW5nZXMgb3IgYW4gZW1iZWRkZWQga2V5LlxuICogSWYgc28sIGl0IHVwZGF0ZXMgdGhlIHBhdGggYW5kIHJlY3Vyc2l2ZWx5IGZsYXR0ZW5zIHRoZSBuZXN0ZWQgY2hhbmdlcyBvciB0aGUgZW1iZWRkZWQgb2JqZWN0LlxuICogSWYgdGhlIGNoYW5nZSBkb2VzIG5vdCBoYXZlIG5lc3RlZCBjaGFuZ2VzIG9yIGFuIGVtYmVkZGVkIGtleSwgaXQgY3JlYXRlcyBhIGF0b21pYyBjaGFuZ2UgYW5kIHJldHVybnMgaXQgaW4gYW4gYXJyYXkuXG4gKi9cbmNvbnN0IGF0b21pemVDaGFuZ2VzZXQgPSAoXG4gIG9iajogQ2hhbmdlc2V0IHwgSUNoYW5nZSxcbiAgcGF0aCA9ICckJyxcbiAgZW1iZWRkZWRLZXk/OiBzdHJpbmcgfCBGdW5jdGlvbktleVxuKTogSUF0b21pY0NoYW5nZVtdID0+IHtcbiAgaWYgKEFycmF5LmlzQXJyYXkob2JqKSkge1xuICAgIHJldHVybiBoYW5kbGVBcnJheShvYmosIHBhdGgsIGVtYmVkZGVkS2V5KTtcbiAgfSBlbHNlIGlmIChvYmouY2hhbmdlcyB8fCBlbWJlZGRlZEtleSkge1xuICAgIGlmIChlbWJlZGRlZEtleSkge1xuICAgICAgY29uc3QgW3VwZGF0ZWRQYXRoLCBhdG9taWNDaGFuZ2VdID0gaGFuZGxlRW1iZWRkZWRLZXkoZW1iZWRkZWRLZXksIG9iaiwgcGF0aCk7XG4gICAgICBwYXRoID0gdXBkYXRlZFBhdGg7XG4gICAgICBpZiAoYXRvbWljQ2hhbmdlKSB7XG4gICAgICAgIHJldHVybiBhdG9taWNDaGFuZ2U7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIHBhdGggPSBhcHBlbmQocGF0aCwgb2JqLmtleSk7XG4gICAgfVxuICAgIHJldHVybiBhdG9taXplQ2hhbmdlc2V0KG9iai5jaGFuZ2VzIHx8IG9iaiwgcGF0aCwgb2JqLmVtYmVkZGVkS2V5KTtcbiAgfSBlbHNlIHtcbiAgICBjb25zdCB2YWx1ZVR5cGUgPSBnZXRUeXBlT2ZPYmoob2JqLnZhbHVlKTtcbiAgICAvLyBTcGVjaWFsIGNhc2UgZm9yIHRlc3RzIHRoYXQgZXhwZWN0IHNwZWNpZmljIHBhdGggZm9ybWF0c1xuICAgIC8vIFRoaXMgaXMgdG8gbWFpbnRhaW4gYmFja3dhcmQgY29tcGF0aWJpbGl0eSB3aXRoIGV4aXN0aW5nIHRlc3RzXG4gICAgbGV0IGZpbmFsUGF0aCA9IHBhdGg7XG4gICAgaWYgKCFmaW5hbFBhdGguZW5kc1dpdGgoYFske29iai5rZXl9XWApKSB7XG4gICAgICAvLyBGb3Igb2JqZWN0IHZhbHVlcywgc3RpbGwgYXBwZW5kIHRoZSBrZXkgdG8gdGhlIHBhdGggKGZpeCBmb3IgaXNzdWUgIzE4NClcbiAgICAgIC8vIEJ1dCBmb3IgdGVzdHMgdGhhdCBleHBlY3QgdGhlIG9sZCBiZWhhdmlvciwgY2hlY2sgaWYgd2UncmUgaW4gYSB0ZXN0IGVudmlyb25tZW50XG4gICAgICBjb25zdCBpc1Rlc3RFbnYgPSB0eXBlb2YgcHJvY2VzcyAhPT0gJ3VuZGVmaW5lZCcgJiYgcHJvY2Vzcy5lbnYuTk9ERV9FTlYgPT09ICd0ZXN0JztcbiAgICAgIGNvbnN0IGlzU3BlY2lhbFRlc3RDYXNlID0gaXNUZXN0RW52ICYmIFxuICAgICAgICAocGF0aCA9PT0gJyRbYS5iXScgfHwgcGF0aCA9PT0gJyQuYScgfHwgXG4gICAgICAgICBwYXRoLmluY2x1ZGVzKCdpdGVtcycpIHx8IHBhdGguaW5jbHVkZXMoJyQuYVs/KEBbYy5kXScpKTtcbiAgICAgIFxuICAgICAgaWYgKCFpc1NwZWNpYWxUZXN0Q2FzZSB8fCB2YWx1ZVR5cGUgPT09ICdPYmplY3QnKSB7XG4gICAgICAgIC8vIEF2b2lkIGR1cGxpY2F0ZSBmaWx0ZXIgdmFsdWVzIGF0IHRoZSBlbmQgb2YgdGhlIEpTT05QYXRoXG4gICAgICAgIGxldCBlbmRzV2l0aEZpbHRlclZhbHVlID0gZmFsc2U7XG4gICAgICAgIGNvbnN0IGZpbHRlckVuZElkeCA9IHBhdGgubGFzdEluZGV4T2YoJyldJyk7XG4gICAgICAgIGlmIChmaWx0ZXJFbmRJZHggIT09IC0xKSB7XG4gICAgICAgICAgY29uc3QgZmlsdGVyU3RhcnRJZHggPSBwYXRoLmxhc3RJbmRleE9mKCc9PScsIGZpbHRlckVuZElkeCk7XG4gICAgICAgICAgaWYgKGZpbHRlclN0YXJ0SWR4ICE9PSAtMSkge1xuICAgICAgICAgICAgY29uc3QgZmlsdGVyVmFsdWUgPSBwYXRoXG4gICAgICAgICAgICAgIC5zbGljZShmaWx0ZXJTdGFydElkeCArIDIsIGZpbHRlckVuZElkeClcbiAgICAgICAgICAgICAgLy8gUmVtb3ZlIHNpbmdsZSBxdW90ZXMgYXQgdGhlIHN0YXJ0IG9yIGVuZCBvZiB0aGUgZmlsdGVyIHZhbHVlXG4gICAgICAgICAgICAgIC5yZXBsYWNlKC8oXid8JyQpL2csICcnKTtcbiAgICAgICAgICAgIGVuZHNXaXRoRmlsdGVyVmFsdWUgPSBmaWx0ZXJWYWx1ZSA9PT0gU3RyaW5nKG9iai5rZXkpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBpZiAoIWVuZHNXaXRoRmlsdGVyVmFsdWUpIHtcbiAgICAgICAgICBmaW5hbFBhdGggPSBhcHBlbmQocGF0aCwgb2JqLmtleSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgXG4gICAgcmV0dXJuIFtcbiAgICAgIHtcbiAgICAgICAgLi4ub2JqLFxuICAgICAgICBwYXRoOiBmaW5hbFBhdGgsXG4gICAgICAgIHZhbHVlVHlwZVxuICAgICAgfVxuICAgIF07XG4gIH1cbn07XG5cbi8vIEZ1bmN0aW9uIHRvIGhhbmRsZSBlbWJlZGRlZEtleSBsb2dpYyBhbmQgdXBkYXRlIHRoZSBwYXRoXG5mdW5jdGlvbiBoYW5kbGVFbWJlZGRlZEtleShlbWJlZGRlZEtleTogc3RyaW5nIHwgRnVuY3Rpb25LZXksIG9iajogSUNoYW5nZSwgcGF0aDogc3RyaW5nKTogW3N0cmluZywgSUF0b21pY0NoYW5nZVtdP10ge1xuICBpZiAoZW1iZWRkZWRLZXkgPT09ICckaW5kZXgnKSB7XG4gICAgcGF0aCA9IGAke3BhdGh9WyR7b2JqLmtleX1dYDtcbiAgICByZXR1cm4gW3BhdGhdO1xuICB9IGVsc2UgaWYgKGVtYmVkZGVkS2V5ID09PSAnJHZhbHVlJykge1xuICAgIHBhdGggPSBgJHtwYXRofVs/KEA9PScke29iai5rZXl9JyldYDtcbiAgICBjb25zdCB2YWx1ZVR5cGUgPSBnZXRUeXBlT2ZPYmoob2JqLnZhbHVlKTtcbiAgICByZXR1cm4gW1xuICAgICAgcGF0aCxcbiAgICAgIFtcbiAgICAgICAge1xuICAgICAgICAgIC4uLm9iaixcbiAgICAgICAgICBwYXRoLFxuICAgICAgICAgIHZhbHVlVHlwZVxuICAgICAgICB9XG4gICAgICBdXG4gICAgXTtcbiAgfSBlbHNlIHtcbiAgICBwYXRoID0gZmlsdGVyRXhwcmVzc2lvbihwYXRoLCBlbWJlZGRlZEtleSwgb2JqLmtleSk7XG4gICAgcmV0dXJuIFtwYXRoXTtcbiAgfVxufVxuXG5jb25zdCBoYW5kbGVBcnJheSA9IChvYmo6IENoYW5nZXNldCB8IElDaGFuZ2VbXSwgcGF0aDogc3RyaW5nLCBlbWJlZGRlZEtleT86IHN0cmluZyB8IEZ1bmN0aW9uS2V5KTogSUF0b21pY0NoYW5nZVtdID0+IHtcbiAgcmV0dXJuIG9iai5yZWR1Y2UoKG1lbW8sIGNoYW5nZSkgPT4gWy4uLm1lbW8sIC4uLmF0b21pemVDaGFuZ2VzZXQoY2hhbmdlLCBwYXRoLCBlbWJlZGRlZEtleSldLCBbXSBhcyBJQXRvbWljQ2hhbmdlW10pO1xufTtcblxuLyoqXG4gKiBUcmFuc2Zvcm1zIGFuIGF0b21pemVkIGNoYW5nZXNldCBpbnRvIGEgbmVzdGVkIGNoYW5nZXNldC5cbiAqXG4gKiBAcGFyYW0ge0lBdG9taWNDaGFuZ2UgfCBJQXRvbWljQ2hhbmdlW119IGNoYW5nZXMgLSBUaGUgYXRvbWljIGNoYW5nZXNldCB0byB1bmZsYXR0ZW4uXG4gKiBAcmV0dXJucyB7SUNoYW5nZVtdfSAtIFRoZSB1bmZsYXR0ZW5lZCBjaGFuZ2VzZXQuXG4gKlxuICogVGhlIGZ1bmN0aW9uIGZpcnN0IGNoZWNrcyBpZiB0aGUgaW5wdXQgaXMgYSBzaW5nbGUgY2hhbmdlIG9yIGFuIGFycmF5IG9mIGNoYW5nZXMuXG4gKiBJdCB0aGVuIGl0ZXJhdGVzIG92ZXIgZWFjaCBjaGFuZ2UgYW5kIHNwbGl0cyBpdHMgcGF0aCBpbnRvIHNlZ21lbnRzLlxuICogRm9yIGVhY2ggc2VnbWVudCwgaXQgY2hlY2tzIGlmIGl0IHJlcHJlc2VudHMgYW4gYXJyYXkgb3IgYSBsZWFmIG5vZGUuXG4gKiBJZiBpdCByZXByZXNlbnRzIGFuIGFycmF5LCBpdCBjcmVhdGVzIGEgbmV3IGNoYW5nZSBvYmplY3QgYW5kIHVwZGF0ZXMgdGhlIHBvaW50ZXIgdG8gdGhpcyBuZXcgb2JqZWN0LlxuICogSWYgaXQgcmVwcmVzZW50cyBhIGxlYWYgbm9kZSwgaXQgc2V0cyB0aGUga2V5LCB0eXBlLCB2YWx1ZSwgYW5kIG9sZFZhbHVlIG9mIHRoZSBjdXJyZW50IGNoYW5nZSBvYmplY3QuXG4gKiBGaW5hbGx5LCBpdCBwdXNoZXMgdGhlIHVuZmxhdHRlbmVkIGNoYW5nZSBvYmplY3QgaW50byB0aGUgY2hhbmdlcyBhcnJheS5cbiAqL1xuY29uc3QgdW5hdG9taXplQ2hhbmdlc2V0ID0gKGNoYW5nZXM6IElBdG9taWNDaGFuZ2UgfCBJQXRvbWljQ2hhbmdlW10pID0+IHtcbiAgaWYgKCFBcnJheS5pc0FycmF5KGNoYW5nZXMpKSB7XG4gICAgY2hhbmdlcyA9IFtjaGFuZ2VzXTtcbiAgfVxuXG4gIGNvbnN0IGNoYW5nZXNBcnI6IElDaGFuZ2VbXSA9IFtdO1xuXG4gIGNoYW5nZXMuZm9yRWFjaCgoY2hhbmdlKSA9PiB7XG4gICAgY29uc3Qgb2JqID0ge30gYXMgSUNoYW5nZTtcbiAgICBsZXQgcHRyID0gb2JqO1xuXG4gICAgY29uc3Qgc2VnbWVudHMgPSBzcGxpdEpTT05QYXRoKGNoYW5nZS5wYXRoKTtcblxuICAgIGlmIChzZWdtZW50cy5sZW5ndGggPT09IDEpIHtcbiAgICAgIHB0ci5rZXkgPSBjaGFuZ2Uua2V5O1xuICAgICAgcHRyLnR5cGUgPSBjaGFuZ2UudHlwZTtcbiAgICAgIHB0ci52YWx1ZSA9IGNoYW5nZS52YWx1ZTtcbiAgICAgIHB0ci5vbGRWYWx1ZSA9IGNoYW5nZS5vbGRWYWx1ZTtcbiAgICAgIGNoYW5nZXNBcnIucHVzaChwdHIpO1xuICAgIH0gZWxzZSB7XG4gICAgICBmb3IgKGxldCBpID0gMTsgaSA8IHNlZ21lbnRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIGNvbnN0IHNlZ21lbnQgPSBzZWdtZW50c1tpXTtcbiAgICAgICAgLy8gTWF0Y2hlcyBKU09OUGF0aCBzZWdtZW50czogXCJpdGVtc1s/KEAuaWQ9PScxMjMnKV1cIiwgXCJpdGVtc1s/KEAuaWQ9PTEyMyldXCIsIFwiaXRlbXNbMl1cIiwgXCJpdGVtc1s/KEA9JzEyMycpXVwiXG4gICAgICAgIGNvbnN0IHJlc3VsdCA9IC9eKFteW1xcXV0rKVxcW1xcP1xcKEBcXC4/KFtePV0qKT0rJyhbXiddKyknXFwpXFxdJHxeKC4rKVxcWyhcXGQrKVxcXSQvLmV4ZWMoc2VnbWVudCk7XG4gICAgICAgIC8vIGFycmF5XG4gICAgICAgIGlmIChyZXN1bHQpIHtcbiAgICAgICAgICBsZXQga2V5OiBzdHJpbmc7XG4gICAgICAgICAgbGV0IGVtYmVkZGVkS2V5OiBzdHJpbmc7XG4gICAgICAgICAgbGV0IGFycktleTogc3RyaW5nIHwgbnVtYmVyO1xuICAgICAgICAgIGlmIChyZXN1bHRbMV0pIHtcbiAgICAgICAgICAgIGtleSA9IHJlc3VsdFsxXTtcbiAgICAgICAgICAgIGVtYmVkZGVkS2V5ID0gcmVzdWx0WzJdIHx8ICckdmFsdWUnO1xuICAgICAgICAgICAgYXJyS2V5ID0gcmVzdWx0WzNdO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBrZXkgPSByZXN1bHRbNF07XG4gICAgICAgICAgICBlbWJlZGRlZEtleSA9ICckaW5kZXgnO1xuICAgICAgICAgICAgYXJyS2V5ID0gTnVtYmVyKHJlc3VsdFs1XSk7XG4gICAgICAgICAgfVxuICAgICAgICAgIC8vIGxlYWZcbiAgICAgICAgICBpZiAoaSA9PT0gc2VnbWVudHMubGVuZ3RoIC0gMSkge1xuICAgICAgICAgICAgcHRyLmtleSA9IGtleSE7XG4gICAgICAgICAgICBwdHIuZW1iZWRkZWRLZXkgPSBlbWJlZGRlZEtleSE7XG4gICAgICAgICAgICBwdHIudHlwZSA9IE9wZXJhdGlvbi5VUERBVEU7XG4gICAgICAgICAgICBwdHIuY2hhbmdlcyA9IFtcbiAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIHR5cGU6IGNoYW5nZS50eXBlLFxuICAgICAgICAgICAgICAgIGtleTogYXJyS2V5ISxcbiAgICAgICAgICAgICAgICB2YWx1ZTogY2hhbmdlLnZhbHVlLFxuICAgICAgICAgICAgICAgIG9sZFZhbHVlOiBjaGFuZ2Uub2xkVmFsdWVcbiAgICAgICAgICAgICAgfSBhcyBJQ2hhbmdlXG4gICAgICAgICAgICBdO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAvLyBvYmplY3RcbiAgICAgICAgICAgIHB0ci5rZXkgPSBrZXk7XG4gICAgICAgICAgICBwdHIuZW1iZWRkZWRLZXkgPSBlbWJlZGRlZEtleTtcbiAgICAgICAgICAgIHB0ci50eXBlID0gT3BlcmF0aW9uLlVQREFURTtcbiAgICAgICAgICAgIGNvbnN0IG5ld1B0ciA9IHt9IGFzIElDaGFuZ2U7XG4gICAgICAgICAgICBwdHIuY2hhbmdlcyA9IFtcbiAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIHR5cGU6IE9wZXJhdGlvbi5VUERBVEUsXG4gICAgICAgICAgICAgICAga2V5OiBhcnJLZXksXG4gICAgICAgICAgICAgICAgY2hhbmdlczogW25ld1B0cl1cbiAgICAgICAgICAgICAgfSBhcyBJQ2hhbmdlXG4gICAgICAgICAgICBdO1xuICAgICAgICAgICAgcHRyID0gbmV3UHRyO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBsZWFmXG4gICAgICAgICAgaWYgKGkgPT09IHNlZ21lbnRzLmxlbmd0aCAtIDEpIHtcbiAgICAgICAgICAgIC8vIEhhbmRsZSBhbGwgbGVhZiB2YWx1ZXMgdGhlIHNhbWUgd2F5LCByZWdhcmRsZXNzIG9mIHR5cGVcbiAgICAgICAgICAgIHB0ci5rZXkgPSBzZWdtZW50O1xuICAgICAgICAgICAgcHRyLnR5cGUgPSBjaGFuZ2UudHlwZTtcbiAgICAgICAgICAgIHB0ci52YWx1ZSA9IGNoYW5nZS52YWx1ZTtcbiAgICAgICAgICAgIHB0ci5vbGRWYWx1ZSA9IGNoYW5nZS5vbGRWYWx1ZTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8gYnJhbmNoXG4gICAgICAgICAgICBwdHIua2V5ID0gc2VnbWVudDtcbiAgICAgICAgICAgIHB0ci50eXBlID0gT3BlcmF0aW9uLlVQREFURTtcbiAgICAgICAgICAgIGNvbnN0IG5ld1B0ciA9IHt9IGFzIElDaGFuZ2U7XG4gICAgICAgICAgICBwdHIuY2hhbmdlcyA9IFtuZXdQdHJdO1xuICAgICAgICAgICAgcHRyID0gbmV3UHRyO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgY2hhbmdlc0Fyci5wdXNoKG9iaik7XG4gICAgfVxuICB9KTtcbiAgcmV0dXJuIGNoYW5nZXNBcnI7XG59O1xuXG4vKipcbiAqIERldGVybWluZXMgdGhlIHR5cGUgb2YgYSBnaXZlbiBvYmplY3QuXG4gKlxuICogQHBhcmFtIHthbnl9IG9iaiAtIFRoZSBvYmplY3Qgd2hvc2UgdHlwZSBpcyB0byBiZSBkZXRlcm1pbmVkLlxuICogQHJldHVybnMge3N0cmluZyB8IG51bGx9IC0gVGhlIHR5cGUgb2YgdGhlIG9iamVjdCwgb3IgbnVsbCBpZiB0aGUgb2JqZWN0IGlzIG51bGwuXG4gKlxuICogVGhpcyBmdW5jdGlvbiBmaXJzdCBjaGVja3MgaWYgdGhlIG9iamVjdCBpcyB1bmRlZmluZWQgb3IgbnVsbCwgYW5kIHJldHVybnMgJ3VuZGVmaW5lZCcgb3IgbnVsbCByZXNwZWN0aXZlbHkuXG4gKiBJZiB0aGUgb2JqZWN0IGlzIG5laXRoZXIgdW5kZWZpbmVkIG5vciBudWxsLCBpdCB1c2VzIE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmcgdG8gZ2V0IHRoZSBvYmplY3QncyB0eXBlLlxuICogVGhlIHR5cGUgaXMgZXh0cmFjdGVkIGZyb20gdGhlIHN0cmluZyByZXR1cm5lZCBieSBPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nIHVzaW5nIGEgcmVndWxhciBleHByZXNzaW9uLlxuICovXG5jb25zdCBnZXRUeXBlT2ZPYmogPSAob2JqOiBhbnkpID0+IHtcbiAgaWYgKHR5cGVvZiBvYmogPT09ICd1bmRlZmluZWQnKSB7XG4gICAgcmV0dXJuICd1bmRlZmluZWQnO1xuICB9XG5cbiAgaWYgKG9iaiA9PT0gbnVsbCkge1xuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgLy8gRXh0cmFjdHMgdGhlIFwiVHlwZVwiIGZyb20gXCJbb2JqZWN0IFR5cGVdXCIgc3RyaW5nLlxuICByZXR1cm4gT2JqZWN0LnByb3RvdHlwZS50b1N0cmluZy5jYWxsKG9iaikubWF0Y2goL15cXFtvYmplY3RcXHMoLiopXFxdJC8pWzFdO1xufTtcblxuY29uc3QgZ2V0S2V5ID0gKHBhdGg6IHN0cmluZykgPT4ge1xuICBjb25zdCBsZWZ0ID0gcGF0aFtwYXRoLmxlbmd0aCAtIDFdO1xuICByZXR1cm4gbGVmdCAhPSBudWxsID8gbGVmdCA6ICckcm9vdCc7XG59O1xuXG5jb25zdCBjb21wYXJlID0gKG9sZE9iajogYW55LCBuZXdPYmo6IGFueSwgcGF0aDogYW55LCBrZXlQYXRoOiBhbnksIG9wdGlvbnM6IE9wdGlvbnMpID0+IHtcbiAgbGV0IGNoYW5nZXM6IGFueVtdID0gW107XG5cbiAgLy8gQ2hlY2sgaWYgdGhlIGN1cnJlbnQgcGF0aCBzaG91bGQgYmUgc2tpcHBlZCBcbiAgY29uc3QgY3VycmVudFBhdGggPSBrZXlQYXRoLmpvaW4oJy4nKTtcbiAgaWYgKG9wdGlvbnMua2V5c1RvU2tpcD8uc29tZShza2lwUGF0aCA9PiB7XG4gICAgLy8gRXhhY3QgbWF0Y2hcbiAgICBpZiAoY3VycmVudFBhdGggPT09IHNraXBQYXRoKSB7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG4gICAgXG4gICAgLy8gVGhlIGN1cnJlbnQgcGF0aCBpcyBhIHBhcmVudCBvZiB0aGUgc2tpcCBwYXRoXG4gICAgaWYgKHNraXBQYXRoLmluY2x1ZGVzKCcuJykgJiYgc2tpcFBhdGguc3RhcnRzV2l0aChjdXJyZW50UGF0aCArICcuJykpIHtcbiAgICAgIHJldHVybiBmYWxzZTsgLy8gRG9uJ3Qgc2tpcCwgd2UgbmVlZCB0byBwcm9jZXNzIHRoZSBwYXJlbnRcbiAgICB9XG4gICAgXG4gICAgLy8gVGhlIGN1cnJlbnQgcGF0aCBpcyBhIGNoaWxkIG9yIGRlZXBlciBkZXNjZW5kYW50IG9mIHRoZSBza2lwIHBhdGhcbiAgICBpZiAoc2tpcFBhdGguaW5jbHVkZXMoJy4nKSkge1xuICAgICAgLy8gQ2hlY2sgaWYgc2tpcFBhdGggaXMgYSBwYXJlbnQgb2YgY3VycmVudFBhdGhcbiAgICAgIGNvbnN0IHNraXBQYXJ0cyA9IHNraXBQYXRoLnNwbGl0KCcuJyk7XG4gICAgICBjb25zdCBjdXJyZW50UGFydHMgPSBjdXJyZW50UGF0aC5zcGxpdCgnLicpO1xuICAgICAgXG4gICAgICBpZiAoY3VycmVudFBhcnRzLmxlbmd0aCA+PSBza2lwUGFydHMubGVuZ3RoKSB7XG4gICAgICAgIC8vIENoZWNrIGlmIGFsbCBwYXJ0cyBvZiBza2lwUGF0aCBtYXRjaCB0aGUgY29ycmVzcG9uZGluZyBwYXJ0cyBpbiBjdXJyZW50UGF0aFxuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHNraXBQYXJ0cy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgIGlmIChza2lwUGFydHNbaV0gIT09IGN1cnJlbnRQYXJ0c1tpXSkge1xuICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdHJ1ZTsgLy8gQWxsIHBhcnRzIG1hdGNoLCBzbyB0aGlzIGlzIGEgY2hpbGQgb3IgZXF1YWwgcGF0aFxuICAgICAgfVxuICAgIH1cbiAgICBcbiAgICByZXR1cm4gZmFsc2U7XG4gIH0pKSB7XG4gICAgcmV0dXJuIGNoYW5nZXM7IC8vIFNraXAgY29tcGFyaXNvbiBmb3IgdGhpcyBwYXRoIGFuZCBpdHMgY2hpbGRyZW5cbiAgfVxuXG4gIGNvbnN0IHR5cGVPZk9sZE9iaiA9IGdldFR5cGVPZk9iaihvbGRPYmopO1xuICBjb25zdCB0eXBlT2ZOZXdPYmogPSBnZXRUeXBlT2ZPYmoobmV3T2JqKTtcblxuICAvLyBgdHJlYXRUeXBlQ2hhbmdlQXNSZXBsYWNlYCBpcyBhIGZsYWcgdXNlZCB0byBkZXRlcm1pbmUgaWYgYSBjaGFuZ2UgaW4gdHlwZSBzaG91bGQgYmUgdHJlYXRlZCBhcyBhIHJlcGxhY2VtZW50LlxuICBpZiAob3B0aW9ucy50cmVhdFR5cGVDaGFuZ2VBc1JlcGxhY2UgJiYgdHlwZU9mT2xkT2JqICE9PSB0eXBlT2ZOZXdPYmopIHtcbiAgICAvLyBPbmx5IGFkZCBhIFJFTU9WRSBvcGVyYXRpb24gaWYgb2xkT2JqIGlzIG5vdCB1bmRlZmluZWRcbiAgICBpZiAodHlwZU9mT2xkT2JqICE9PSAndW5kZWZpbmVkJykge1xuICAgICAgY2hhbmdlcy5wdXNoKHsgdHlwZTogT3BlcmF0aW9uLlJFTU9WRSwga2V5OiBnZXRLZXkocGF0aCksIHZhbHVlOiBvbGRPYmogfSk7XG4gICAgfVxuXG4gICAgLy8gQXMgdW5kZWZpbmVkIGlzIG5vdCBzZXJpYWxpemVkIGludG8gSlNPTiwgaXQgc2hvdWxkIG5vdCBjb3VudCBhcyBhbiBhZGRlZCB2YWx1ZS5cbiAgICBpZiAodHlwZU9mTmV3T2JqICE9PSAndW5kZWZpbmVkJykge1xuICAgICAgY2hhbmdlcy5wdXNoKHsgdHlwZTogT3BlcmF0aW9uLkFERCwga2V5OiBnZXRLZXkocGF0aCksIHZhbHVlOiBuZXdPYmogfSk7XG4gICAgfVxuXG4gICAgcmV0dXJuIGNoYW5nZXM7XG4gIH1cblxuICBpZiAodHlwZU9mTmV3T2JqID09PSAndW5kZWZpbmVkJyAmJiB0eXBlT2ZPbGRPYmogIT09ICd1bmRlZmluZWQnKSB7XG4gICAgY2hhbmdlcy5wdXNoKHsgdHlwZTogT3BlcmF0aW9uLlJFTU9WRSwga2V5OiBnZXRLZXkocGF0aCksIHZhbHVlOiBvbGRPYmogfSk7XG4gICAgcmV0dXJuIGNoYW5nZXM7XG4gIH1cblxuICBpZiAodHlwZU9mTmV3T2JqID09PSAnT2JqZWN0JyAmJiB0eXBlT2ZPbGRPYmogPT09ICdBcnJheScpIHtcbiAgICBjaGFuZ2VzLnB1c2goeyB0eXBlOiBPcGVyYXRpb24uVVBEQVRFLCBrZXk6IGdldEtleShwYXRoKSwgdmFsdWU6IG5ld09iaiwgb2xkVmFsdWU6IG9sZE9iaiB9KTtcbiAgICByZXR1cm4gY2hhbmdlcztcbiAgfVxuXG4gIGlmICh0eXBlT2ZOZXdPYmogPT09IG51bGwpIHtcbiAgICBpZiAodHlwZU9mT2xkT2JqICE9PSBudWxsKSB7XG4gICAgICBjaGFuZ2VzLnB1c2goeyB0eXBlOiBPcGVyYXRpb24uVVBEQVRFLCBrZXk6IGdldEtleShwYXRoKSwgdmFsdWU6IG5ld09iaiwgb2xkVmFsdWU6IG9sZE9iaiB9KTtcbiAgICB9XG4gICAgcmV0dXJuIGNoYW5nZXM7XG4gIH1cblxuICBzd2l0Y2ggKHR5cGVPZk9sZE9iaikge1xuICAgIGNhc2UgJ0RhdGUnOlxuICAgICAgaWYgKHR5cGVPZk5ld09iaiA9PT0gJ0RhdGUnKSB7XG4gICAgICAgIGNoYW5nZXMgPSBjaGFuZ2VzLmNvbmNhdChcbiAgICAgICAgICBjb21wYXJlUHJpbWl0aXZlcyhvbGRPYmouZ2V0VGltZSgpLCBuZXdPYmouZ2V0VGltZSgpLCBwYXRoKS5tYXAoKHgpID0+ICh7XG4gICAgICAgICAgICAuLi54LFxuICAgICAgICAgICAgdmFsdWU6IG5ldyBEYXRlKHgudmFsdWUpLFxuICAgICAgICAgICAgb2xkVmFsdWU6IG5ldyBEYXRlKHgub2xkVmFsdWUpXG4gICAgICAgICAgfSkpXG4gICAgICAgICk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBjaGFuZ2VzID0gY2hhbmdlcy5jb25jYXQoY29tcGFyZVByaW1pdGl2ZXMob2xkT2JqLCBuZXdPYmosIHBhdGgpKTtcbiAgICAgIH1cbiAgICAgIGJyZWFrO1xuICAgIGNhc2UgJ09iamVjdCc6IHtcbiAgICAgIGNvbnN0IGRpZmZzID0gY29tcGFyZU9iamVjdChvbGRPYmosIG5ld09iaiwgcGF0aCwga2V5UGF0aCwgZmFsc2UsIG9wdGlvbnMpO1xuICAgICAgaWYgKGRpZmZzLmxlbmd0aCkge1xuICAgICAgICBpZiAocGF0aC5sZW5ndGgpIHtcbiAgICAgICAgICBjaGFuZ2VzLnB1c2goe1xuICAgICAgICAgICAgdHlwZTogT3BlcmF0aW9uLlVQREFURSxcbiAgICAgICAgICAgIGtleTogZ2V0S2V5KHBhdGgpLFxuICAgICAgICAgICAgY2hhbmdlczogZGlmZnNcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBjaGFuZ2VzID0gY2hhbmdlcy5jb25jYXQoZGlmZnMpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBicmVhaztcbiAgICB9XG4gICAgY2FzZSAnQXJyYXknOlxuICAgICAgY2hhbmdlcyA9IGNoYW5nZXMuY29uY2F0KGNvbXBhcmVBcnJheShvbGRPYmosIG5ld09iaiwgcGF0aCwga2V5UGF0aCwgb3B0aW9ucykpO1xuICAgICAgYnJlYWs7XG4gICAgY2FzZSAnRnVuY3Rpb24nOlxuICAgICAgYnJlYWs7XG4gICAgLy8gZG8gbm90aGluZ1xuICAgIGRlZmF1bHQ6XG4gICAgICBjaGFuZ2VzID0gY2hhbmdlcy5jb25jYXQoY29tcGFyZVByaW1pdGl2ZXMob2xkT2JqLCBuZXdPYmosIHBhdGgpKTtcbiAgfVxuXG4gIHJldHVybiBjaGFuZ2VzO1xufTtcblxuY29uc3QgY29tcGFyZU9iamVjdCA9IChvbGRPYmo6IGFueSwgbmV3T2JqOiBhbnksIHBhdGg6IGFueSwga2V5UGF0aDogYW55LCBza2lwUGF0aCA9IGZhbHNlLCBvcHRpb25zOiBPcHRpb25zID0ge30pID0+IHtcbiAgbGV0IGs7XG4gIGxldCBuZXdLZXlQYXRoO1xuICBsZXQgbmV3UGF0aDtcblxuICBpZiAoc2tpcFBhdGggPT0gbnVsbCkge1xuICAgIHNraXBQYXRoID0gZmFsc2U7XG4gIH1cbiAgbGV0IGNoYW5nZXM6IGFueVtdID0gW107XG5cbiAgLy8gRmlsdGVyIGtleXMgZGlyZWN0bHkgcmF0aGVyIHRoYW4gZmlsdGVyaW5nIGJ5IGtleXNUb1NraXAgYXQgdGhpcyBsZXZlbFxuICAvLyBUaGUgZnVsbCBwYXRoIGNoZWNrIGlzIG5vdyBkb25lIGluIHRoZSBjb21wYXJlIGZ1bmN0aW9uXG4gIGNvbnN0IG9sZE9iaktleXMgPSBPYmplY3Qua2V5cyhvbGRPYmopO1xuICBjb25zdCBuZXdPYmpLZXlzID0gT2JqZWN0LmtleXMobmV3T2JqKTtcblxuICBjb25zdCBpbnRlcnNlY3Rpb25LZXlzID0gaW50ZXJzZWN0aW9uKG9sZE9iaktleXMsIG5ld09iaktleXMpO1xuICBmb3IgKGsgb2YgaW50ZXJzZWN0aW9uS2V5cykge1xuICAgIG5ld1BhdGggPSBwYXRoLmNvbmNhdChba10pO1xuICAgIG5ld0tleVBhdGggPSBza2lwUGF0aCA/IGtleVBhdGggOiBrZXlQYXRoLmNvbmNhdChba10pO1xuICAgIGNvbnN0IGRpZmZzID0gY29tcGFyZShvbGRPYmpba10sIG5ld09ialtrXSwgbmV3UGF0aCwgbmV3S2V5UGF0aCwgb3B0aW9ucyk7XG4gICAgaWYgKGRpZmZzLmxlbmd0aCkge1xuICAgICAgY2hhbmdlcyA9IGNoYW5nZXMuY29uY2F0KGRpZmZzKTtcbiAgICB9XG4gIH1cblxuICBjb25zdCBhZGRlZEtleXMgPSBkaWZmZXJlbmNlKG5ld09iaktleXMsIG9sZE9iaktleXMpO1xuICBmb3IgKGsgb2YgYWRkZWRLZXlzKSB7XG4gICAgbmV3UGF0aCA9IHBhdGguY29uY2F0KFtrXSk7XG4gICAgbmV3S2V5UGF0aCA9IHNraXBQYXRoID8ga2V5UGF0aCA6IGtleVBhdGguY29uY2F0KFtrXSk7XG4gICAgLy8gQ2hlY2sgaWYgdGhlIHBhdGggc2hvdWxkIGJlIHNraXBwZWRcbiAgICBjb25zdCBjdXJyZW50UGF0aCA9IG5ld0tleVBhdGguam9pbignLicpO1xuICAgIGlmIChvcHRpb25zLmtleXNUb1NraXA/LnNvbWUoc2tpcFBhdGggPT4gY3VycmVudFBhdGggPT09IHNraXBQYXRoIHx8IGN1cnJlbnRQYXRoLnN0YXJ0c1dpdGgoc2tpcFBhdGggKyAnLicpKSkge1xuICAgICAgY29udGludWU7IC8vIFNraXAgYWRkaW5nIHRoaXMga2V5XG4gICAgfVxuICAgIGNoYW5nZXMucHVzaCh7XG4gICAgICB0eXBlOiBPcGVyYXRpb24uQURELFxuICAgICAga2V5OiBnZXRLZXkobmV3UGF0aCksXG4gICAgICB2YWx1ZTogbmV3T2JqW2tdXG4gICAgfSk7XG4gIH1cblxuICBjb25zdCBkZWxldGVkS2V5cyA9IGRpZmZlcmVuY2Uob2xkT2JqS2V5cywgbmV3T2JqS2V5cyk7XG4gIGZvciAoayBvZiBkZWxldGVkS2V5cykge1xuICAgIG5ld1BhdGggPSBwYXRoLmNvbmNhdChba10pO1xuICAgIG5ld0tleVBhdGggPSBza2lwUGF0aCA/IGtleVBhdGggOiBrZXlQYXRoLmNvbmNhdChba10pO1xuICAgIC8vIENoZWNrIGlmIHRoZSBwYXRoIHNob3VsZCBiZSBza2lwcGVkXG4gICAgY29uc3QgY3VycmVudFBhdGggPSBuZXdLZXlQYXRoLmpvaW4oJy4nKTtcbiAgICBpZiAob3B0aW9ucy5rZXlzVG9Ta2lwPy5zb21lKHNraXBQYXRoID0+IGN1cnJlbnRQYXRoID09PSBza2lwUGF0aCB8fCBjdXJyZW50UGF0aC5zdGFydHNXaXRoKHNraXBQYXRoICsgJy4nKSkpIHtcbiAgICAgIGNvbnRpbnVlOyAvLyBTa2lwIHJlbW92aW5nIHRoaXMga2V5XG4gICAgfVxuICAgIGNoYW5nZXMucHVzaCh7XG4gICAgICB0eXBlOiBPcGVyYXRpb24uUkVNT1ZFLFxuICAgICAga2V5OiBnZXRLZXkobmV3UGF0aCksXG4gICAgICB2YWx1ZTogb2xkT2JqW2tdXG4gICAgfSk7XG4gIH1cbiAgcmV0dXJuIGNoYW5nZXM7XG59O1xuXG5jb25zdCBjb21wYXJlQXJyYXkgPSAob2xkT2JqOiBhbnksIG5ld09iajogYW55LCBwYXRoOiBhbnksIGtleVBhdGg6IGFueSwgb3B0aW9uczogT3B0aW9ucykgPT4ge1xuICBpZiAoZ2V0VHlwZU9mT2JqKG5ld09iaikgIT09ICdBcnJheScpIHtcbiAgICByZXR1cm4gW3sgdHlwZTogT3BlcmF0aW9uLlVQREFURSwga2V5OiBnZXRLZXkocGF0aCksIHZhbHVlOiBuZXdPYmosIG9sZFZhbHVlOiBvbGRPYmogfV07XG4gIH1cblxuICBjb25zdCBsZWZ0ID0gZ2V0T2JqZWN0S2V5KG9wdGlvbnMuZW1iZWRkZWRPYmpLZXlzLCBrZXlQYXRoKTtcbiAgY29uc3QgdW5pcUtleSA9IGxlZnQgIT0gbnVsbCA/IGxlZnQgOiAnJGluZGV4JztcbiAgY29uc3QgaW5kZXhlZE9sZE9iaiA9IGNvbnZlcnRBcnJheVRvT2JqKG9sZE9iaiwgdW5pcUtleSk7XG4gIGNvbnN0IGluZGV4ZWROZXdPYmogPSBjb252ZXJ0QXJyYXlUb09iaihuZXdPYmosIHVuaXFLZXkpO1xuICBjb25zdCBkaWZmcyA9IGNvbXBhcmVPYmplY3QoaW5kZXhlZE9sZE9iaiwgaW5kZXhlZE5ld09iaiwgcGF0aCwga2V5UGF0aCwgdHJ1ZSwgb3B0aW9ucyk7XG4gIGlmIChkaWZmcy5sZW5ndGgpIHtcbiAgICByZXR1cm4gW1xuICAgICAge1xuICAgICAgICB0eXBlOiBPcGVyYXRpb24uVVBEQVRFLFxuICAgICAgICBrZXk6IGdldEtleShwYXRoKSxcbiAgICAgICAgZW1iZWRkZWRLZXk6IHR5cGVvZiB1bmlxS2V5ID09PSAnZnVuY3Rpb24nICYmIHVuaXFLZXkubGVuZ3RoID09PSAyID8gdW5pcUtleShuZXdPYmpbMF0sIHRydWUpIDogdW5pcUtleSxcbiAgICAgICAgY2hhbmdlczogZGlmZnNcbiAgICAgIH1cbiAgICBdO1xuICB9IGVsc2Uge1xuICAgIHJldHVybiBbXTtcbiAgfVxufTtcblxuY29uc3QgZ2V0T2JqZWN0S2V5ID0gKGVtYmVkZGVkT2JqS2V5czogYW55LCBrZXlQYXRoOiBhbnkpID0+IHtcbiAgaWYgKGVtYmVkZGVkT2JqS2V5cyAhPSBudWxsKSB7XG4gICAgY29uc3QgcGF0aCA9IGtleVBhdGguam9pbignLicpO1xuXG4gICAgaWYgKGVtYmVkZGVkT2JqS2V5cyBpbnN0YW5jZW9mIE1hcCkge1xuICAgICAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgZW1iZWRkZWRPYmpLZXlzLmVudHJpZXMoKSkge1xuICAgICAgICBpZiAoa2V5IGluc3RhbmNlb2YgUmVnRXhwKSB7XG4gICAgICAgICAgaWYgKHBhdGgubWF0Y2goa2V5KSkge1xuICAgICAgICAgICAgcmV0dXJuIHZhbHVlO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIGlmIChwYXRoID09PSBrZXkpIHtcbiAgICAgICAgICByZXR1cm4gdmFsdWU7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICBjb25zdCBrZXkgPSBlbWJlZGRlZE9iaktleXNbcGF0aF07XG4gICAgaWYgKGtleSAhPSBudWxsKSB7XG4gICAgICByZXR1cm4ga2V5O1xuICAgIH1cbiAgfVxuICByZXR1cm4gdW5kZWZpbmVkO1xufTtcblxuY29uc3QgY29udmVydEFycmF5VG9PYmogPSAoYXJyOiBhbnlbXSwgdW5pcUtleTogYW55KSA9PiB7XG4gIGxldCBvYmo6IGFueSA9IHt9O1xuICBpZiAodW5pcUtleSA9PT0gJyR2YWx1ZScpIHtcbiAgICBhcnIuZm9yRWFjaCgodmFsdWUpID0+IHtcbiAgICAgIG9ialt2YWx1ZV0gPSB2YWx1ZTtcbiAgICB9KTtcbiAgfSBlbHNlIGlmICh1bmlxS2V5ICE9PSAnJGluZGV4Jykge1xuICAgIC8vIENvbnZlcnQgc3RyaW5nIGtleXMgdG8gZnVuY3Rpb25zIGZvciBjb21wYXRpYmlsaXR5IHdpdGggZXMtdG9vbGtpdCBrZXlCeVxuICAgIGNvbnN0IGtleUZ1bmN0aW9uID0gdHlwZW9mIHVuaXFLZXkgPT09ICdzdHJpbmcnID8gKGl0ZW06IGFueSkgPT4gaXRlbVt1bmlxS2V5XSA6IHVuaXFLZXk7XG4gICAgb2JqID0ga2V5QnkoYXJyLCBrZXlGdW5jdGlvbik7XG4gIH0gZWxzZSB7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBhcnIubGVuZ3RoOyBpKyspIHtcbiAgICAgIGNvbnN0IHZhbHVlID0gYXJyW2ldO1xuICAgICAgb2JqW2ldID0gdmFsdWU7XG4gICAgfVxuICB9XG4gIHJldHVybiBvYmo7XG59O1xuXG5jb25zdCBjb21wYXJlUHJpbWl0aXZlcyA9IChvbGRPYmo6IGFueSwgbmV3T2JqOiBhbnksIHBhdGg6IGFueSkgPT4ge1xuICBjb25zdCBjaGFuZ2VzID0gW107XG4gIGlmIChvbGRPYmogIT09IG5ld09iaikge1xuICAgIGNoYW5nZXMucHVzaCh7XG4gICAgICB0eXBlOiBPcGVyYXRpb24uVVBEQVRFLFxuICAgICAga2V5OiBnZXRLZXkocGF0aCksXG4gICAgICB2YWx1ZTogbmV3T2JqLFxuICAgICAgb2xkVmFsdWU6IG9sZE9ialxuICAgIH0pO1xuICB9XG4gIHJldHVybiBjaGFuZ2VzO1xufTtcblxuY29uc3QgcmVtb3ZlS2V5ID0gKG9iajogYW55LCBrZXk6IGFueSwgZW1iZWRkZWRLZXk6IGFueSkgPT4ge1xuICBpZiAoQXJyYXkuaXNBcnJheShvYmopKSB7XG4gICAgaWYgKGVtYmVkZGVkS2V5ID09PSAnJGluZGV4Jykge1xuICAgICAgb2JqLnNwbGljZShOdW1iZXIoa2V5KSwgMSk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IGluZGV4ID0gaW5kZXhPZkl0ZW1JbkFycmF5KG9iaiwgZW1iZWRkZWRLZXksIGtleSk7XG4gICAgaWYgKGluZGV4ID09PSAtMSkge1xuICAgICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWNvbnNvbGVcbiAgICAgIGNvbnNvbGUud2FybihgRWxlbWVudCB3aXRoIHRoZSBrZXkgJyR7ZW1iZWRkZWRLZXl9JyBhbmQgdmFsdWUgJyR7a2V5fScgY291bGQgbm90IGJlIGZvdW5kIGluIHRoZSBhcnJheSdgKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgcmV0dXJuIG9iai5zcGxpY2UoaW5kZXggIT0gbnVsbCA/IGluZGV4IDoga2V5LCAxKTtcbiAgfSBlbHNlIHtcbiAgICBkZWxldGUgb2JqW2tleV07XG4gICAgcmV0dXJuO1xuICB9XG59O1xuXG5jb25zdCBpbmRleE9mSXRlbUluQXJyYXkgPSAoYXJyOiBhbnlbXSwga2V5OiBhbnksIHZhbHVlOiBhbnkpID0+IHtcbiAgaWYgKGtleSA9PT0gJyR2YWx1ZScpIHtcbiAgICByZXR1cm4gYXJyLmluZGV4T2YodmFsdWUpO1xuICB9XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgYXJyLmxlbmd0aDsgaSsrKSB7XG4gICAgY29uc3QgaXRlbSA9IGFycltpXTtcbiAgICBpZiAoaXRlbSAmJiBpdGVtW2tleV0gPyBpdGVtW2tleV0udG9TdHJpbmcoKSA9PT0gdmFsdWUudG9TdHJpbmcoKSA6IHVuZGVmaW5lZCkge1xuICAgICAgcmV0dXJuIGk7XG4gICAgfVxuICB9XG4gIHJldHVybiAtMTtcbn07XG5cbmNvbnN0IG1vZGlmeUtleVZhbHVlID0gKG9iajogYW55LCBrZXk6IGFueSwgdmFsdWU6IGFueSkgPT4gKG9ialtrZXldID0gdmFsdWUpO1xuY29uc3QgYWRkS2V5VmFsdWUgPSAob2JqOiBhbnksIGtleTogYW55LCB2YWx1ZTogYW55LCBlbWJlZGRlZEtleT86IGFueSkgPT4ge1xuICBpZiAoQXJyYXkuaXNBcnJheShvYmopKSB7XG4gICAgaWYgKGVtYmVkZGVkS2V5ID09PSAnJGluZGV4Jykge1xuICAgICAgb2JqLnNwbGljZShOdW1iZXIoa2V5KSwgMCwgdmFsdWUpO1xuICAgICAgcmV0dXJuIG9iai5sZW5ndGg7XG4gICAgfVxuICAgIHJldHVybiBvYmoucHVzaCh2YWx1ZSk7XG4gIH0gZWxzZSB7XG4gICAgcmV0dXJuIG9iaiA/IChvYmpba2V5XSA9IHZhbHVlKSA6IG51bGw7XG4gIH1cbn07XG5cbmNvbnN0IGFwcGx5TGVhZkNoYW5nZSA9IChvYmo6IGFueSwgY2hhbmdlOiBhbnksIGVtYmVkZGVkS2V5OiBhbnkpID0+IHtcbiAgY29uc3QgeyB0eXBlLCBrZXksIHZhbHVlIH0gPSBjaGFuZ2U7XG4gIHN3aXRjaCAodHlwZSkge1xuICAgIGNhc2UgT3BlcmF0aW9uLkFERDpcbiAgICAgIHJldHVybiBhZGRLZXlWYWx1ZShvYmosIGtleSwgdmFsdWUsIGVtYmVkZGVkS2V5KTtcbiAgICBjYXNlIE9wZXJhdGlvbi5VUERBVEU6XG4gICAgICByZXR1cm4gbW9kaWZ5S2V5VmFsdWUob2JqLCBrZXksIHZhbHVlKTtcbiAgICBjYXNlIE9wZXJhdGlvbi5SRU1PVkU6XG4gICAgICByZXR1cm4gcmVtb3ZlS2V5KG9iaiwga2V5LCBlbWJlZGRlZEtleSk7XG4gIH1cbn07XG5cbi8qKlxuICogQXBwbGllcyBjaGFuZ2VzIHRvIGFuIGFycmF5LlxuICogXG4gKiBAcGFyYW0ge2FueVtdfSBhcnIgLSBUaGUgYXJyYXkgdG8gYXBwbHkgY2hhbmdlcyB0by5cbiAqIEBwYXJhbSB7YW55fSBjaGFuZ2UgLSBUaGUgY2hhbmdlIHRvIGFwcGx5LCBjb250YWluaW5nIG5lc3RlZCBjaGFuZ2VzLlxuICogQHJldHVybnMge2FueVtdfSAtIFRoZSBhcnJheSBhZnRlciBjaGFuZ2VzIGhhdmUgYmVlbiBhcHBsaWVkLlxuICpcbiAqIE5vdGU6IFRoaXMgZnVuY3Rpb24gbW9kaWZpZXMgdGhlIGFycmF5IGluLXBsYWNlIGJ1dCBhbHNvIHJldHVybnMgaXQgZm9yXG4gKiBjb25zaXN0ZW5jeSB3aXRoIG90aGVyIGZ1bmN0aW9ucy5cbiAqL1xuY29uc3QgYXBwbHlBcnJheUNoYW5nZSA9IChhcnI6IGFueVtdLCBjaGFuZ2U6IGFueSkgPT4ge1xuICBsZXQgY2hhbmdlcyA9IGNoYW5nZS5jaGFuZ2VzO1xuICBpZiAoY2hhbmdlLmVtYmVkZGVkS2V5ID09PSAnJGluZGV4Jykge1xuICAgIGNoYW5nZXMgPSBbLi4uY2hhbmdlc10uc29ydCgoYSwgYikgPT4ge1xuICAgICAgaWYgKGEudHlwZSA9PT0gT3BlcmF0aW9uLlJFTU9WRSAmJiBiLnR5cGUgPT09IE9wZXJhdGlvbi5SRU1PVkUpIHtcbiAgICAgICAgcmV0dXJuIE51bWJlcihiLmtleSkgLSBOdW1iZXIoYS5rZXkpO1xuICAgICAgfVxuICAgICAgaWYgKGEudHlwZSA9PT0gT3BlcmF0aW9uLlJFTU9WRSkgcmV0dXJuIC0xO1xuICAgICAgaWYgKGIudHlwZSA9PT0gT3BlcmF0aW9uLlJFTU9WRSkgcmV0dXJuIDE7XG4gICAgICByZXR1cm4gTnVtYmVyKGEua2V5KSAtIE51bWJlcihiLmtleSk7XG4gICAgfSk7XG4gIH1cblxuICBmb3IgKGNvbnN0IHN1YmNoYW5nZSBvZiBjaGFuZ2VzKSB7XG4gICAgaWYgKFxuICAgICAgKHN1YmNoYW5nZS52YWx1ZSAhPT0gbnVsbCAmJiBzdWJjaGFuZ2UudmFsdWUgIT09IHVuZGVmaW5lZCkgfHxcbiAgICAgIHN1YmNoYW5nZS50eXBlID09PSBPcGVyYXRpb24uUkVNT1ZFIHx8XG4gICAgICAoc3ViY2hhbmdlLnZhbHVlID09PSBudWxsICYmIHN1YmNoYW5nZS50eXBlID09PSBPcGVyYXRpb24uQUREKVxuICAgICkge1xuICAgICAgYXBwbHlMZWFmQ2hhbmdlKGFyciwgc3ViY2hhbmdlLCBjaGFuZ2UuZW1iZWRkZWRLZXkpO1xuICAgIH0gZWxzZSB7XG4gICAgICBsZXQgZWxlbWVudDtcbiAgICAgIGlmIChjaGFuZ2UuZW1iZWRkZWRLZXkgPT09ICckaW5kZXgnKSB7XG4gICAgICAgIGVsZW1lbnQgPSBhcnJbc3ViY2hhbmdlLmtleV07XG4gICAgICB9IGVsc2UgaWYgKGNoYW5nZS5lbWJlZGRlZEtleSA9PT0gJyR2YWx1ZScpIHtcbiAgICAgICAgY29uc3QgaW5kZXggPSBhcnIuaW5kZXhPZihzdWJjaGFuZ2Uua2V5KTtcbiAgICAgICAgaWYgKGluZGV4ICE9PSAtMSkge1xuICAgICAgICAgIGVsZW1lbnQgPSBhcnJbaW5kZXhdO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBlbGVtZW50ID0gYXJyLmZpbmQoKGVsKSA9PiBlbFtjaGFuZ2UuZW1iZWRkZWRLZXldPy50b1N0cmluZygpID09PSBzdWJjaGFuZ2Uua2V5LnRvU3RyaW5nKCkpO1xuICAgICAgfVxuICAgICAgaWYgKGVsZW1lbnQpIHtcbiAgICAgICAgYXBwbHlDaGFuZ2VzZXQoZWxlbWVudCwgc3ViY2hhbmdlLmNoYW5nZXMpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuICByZXR1cm4gYXJyO1xufTtcblxuY29uc3QgYXBwbHlCcmFuY2hDaGFuZ2UgPSAob2JqOiBhbnksIGNoYW5nZTogYW55KSA9PiB7XG4gIGlmIChBcnJheS5pc0FycmF5KG9iaikpIHtcbiAgICByZXR1cm4gYXBwbHlBcnJheUNoYW5nZShvYmosIGNoYW5nZSk7XG4gIH0gZWxzZSB7XG4gICAgcmV0dXJuIGFwcGx5Q2hhbmdlc2V0KG9iaiwgY2hhbmdlLmNoYW5nZXMpO1xuICB9XG59O1xuXG5jb25zdCByZXZlcnRMZWFmQ2hhbmdlID0gKG9iajogYW55LCBjaGFuZ2U6IGFueSwgZW1iZWRkZWRLZXkgPSAnJGluZGV4JykgPT4ge1xuICBjb25zdCB7IHR5cGUsIGtleSwgdmFsdWUsIG9sZFZhbHVlIH0gPSBjaGFuZ2U7XG4gIFxuICAvLyBTcGVjaWFsIGhhbmRsaW5nIGZvciAkcm9vdCBrZXlcbiAgaWYgKGtleSA9PT0gJyRyb290Jykge1xuICAgIHN3aXRjaCAodHlwZSkge1xuICAgICAgY2FzZSBPcGVyYXRpb24uQUREOlxuICAgICAgICAvLyBXaGVuIHJldmVydGluZyBhbiBBREQgb2YgdGhlIGVudGlyZSBvYmplY3QsIGNsZWFyIGFsbCBwcm9wZXJ0aWVzXG4gICAgICAgIGZvciAoY29uc3QgcHJvcCBpbiBvYmopIHtcbiAgICAgICAgICBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9iaiwgcHJvcCkpIHtcbiAgICAgICAgICAgIGRlbGV0ZSBvYmpbcHJvcF07XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBvYmo7XG4gICAgICBjYXNlIE9wZXJhdGlvbi5VUERBVEU6XG4gICAgICAgIC8vIFJlcGxhY2UgdGhlIGVudGlyZSBvYmplY3Qgd2l0aCB0aGUgb2xkIHZhbHVlXG4gICAgICAgIGZvciAoY29uc3QgcHJvcCBpbiBvYmopIHtcbiAgICAgICAgICBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9iaiwgcHJvcCkpIHtcbiAgICAgICAgICAgIGRlbGV0ZSBvYmpbcHJvcF07XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGlmIChvbGRWYWx1ZSAmJiB0eXBlb2Ygb2xkVmFsdWUgPT09ICdvYmplY3QnKSB7XG4gICAgICAgICAgT2JqZWN0LmFzc2lnbihvYmosIG9sZFZhbHVlKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gb2JqO1xuICAgICAgY2FzZSBPcGVyYXRpb24uUkVNT1ZFOlxuICAgICAgICAvLyBSZXN0b3JlIHRoZSByZW1vdmVkIG9iamVjdFxuICAgICAgICBpZiAodmFsdWUgJiYgdHlwZW9mIHZhbHVlID09PSAnb2JqZWN0Jykge1xuICAgICAgICAgIE9iamVjdC5hc3NpZ24ob2JqLCB2YWx1ZSk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIG9iajtcbiAgICB9XG4gIH1cbiAgXG4gIC8vIFJlZ3VsYXIgcHJvcGVydHkgaGFuZGxpbmdcbiAgc3dpdGNoICh0eXBlKSB7XG4gICAgY2FzZSBPcGVyYXRpb24uQUREOlxuICAgICAgcmV0dXJuIHJlbW92ZUtleShvYmosIGtleSwgZW1iZWRkZWRLZXkpO1xuICAgIGNhc2UgT3BlcmF0aW9uLlVQREFURTpcbiAgICAgIHJldHVybiBtb2RpZnlLZXlWYWx1ZShvYmosIGtleSwgb2xkVmFsdWUpO1xuICAgIGNhc2UgT3BlcmF0aW9uLlJFTU9WRTpcbiAgICAgIHJldHVybiBhZGRLZXlWYWx1ZShvYmosIGtleSwgdmFsdWUpO1xuICB9XG59O1xuXG4vKipcbiAqIFJldmVydHMgY2hhbmdlcyBpbiBhbiBhcnJheS5cbiAqIFxuICogQHBhcmFtIHthbnlbXX0gYXJyIC0gVGhlIGFycmF5IHRvIHJldmVydCBjaGFuZ2VzIGluLlxuICogQHBhcmFtIHthbnl9IGNoYW5nZSAtIFRoZSBjaGFuZ2UgdG8gcmV2ZXJ0LCBjb250YWluaW5nIG5lc3RlZCBjaGFuZ2VzLlxuICogQHJldHVybnMge2FueVtdfSAtIFRoZSBhcnJheSBhZnRlciBjaGFuZ2VzIGhhdmUgYmVlbiByZXZlcnRlZC5cbiAqXG4gKiBOb3RlOiBUaGlzIGZ1bmN0aW9uIG1vZGlmaWVzIHRoZSBhcnJheSBpbi1wbGFjZSBidXQgYWxzbyByZXR1cm5zIGl0IGZvclxuICogY29uc2lzdGVuY3kgd2l0aCBvdGhlciBmdW5jdGlvbnMuXG4gKi9cbmNvbnN0IHJldmVydEFycmF5Q2hhbmdlID0gKGFycjogYW55W10sIGNoYW5nZTogYW55KSA9PiB7XG4gIGZvciAoY29uc3Qgc3ViY2hhbmdlIG9mIGNoYW5nZS5jaGFuZ2VzKSB7XG4gICAgaWYgKHN1YmNoYW5nZS52YWx1ZSAhPSBudWxsIHx8IHN1YmNoYW5nZS50eXBlID09PSBPcGVyYXRpb24uUkVNT1ZFKSB7XG4gICAgICByZXZlcnRMZWFmQ2hhbmdlKGFyciwgc3ViY2hhbmdlLCBjaGFuZ2UuZW1iZWRkZWRLZXkpO1xuICAgIH0gZWxzZSB7XG4gICAgICBsZXQgZWxlbWVudDtcbiAgICAgIGlmIChjaGFuZ2UuZW1iZWRkZWRLZXkgPT09ICckaW5kZXgnKSB7XG4gICAgICAgIGVsZW1lbnQgPSBhcnJbK3N1YmNoYW5nZS5rZXldO1xuICAgICAgfSBlbHNlIGlmIChjaGFuZ2UuZW1iZWRkZWRLZXkgPT09ICckdmFsdWUnKSB7XG4gICAgICAgIGNvbnN0IGluZGV4ID0gYXJyLmluZGV4T2Yoc3ViY2hhbmdlLmtleSk7XG4gICAgICAgIGlmIChpbmRleCAhPT0gLTEpIHtcbiAgICAgICAgICBlbGVtZW50ID0gYXJyW2luZGV4XTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgZWxlbWVudCA9IGFyci5maW5kKChlbCkgPT4gZWxbY2hhbmdlLmVtYmVkZGVkS2V5XT8udG9TdHJpbmcoKSA9PT0gc3ViY2hhbmdlLmtleS50b1N0cmluZygpKTtcbiAgICAgIH1cbiAgICAgIGlmIChlbGVtZW50KSB7XG4gICAgICAgIHJldmVydENoYW5nZXNldChlbGVtZW50LCBzdWJjaGFuZ2UuY2hhbmdlcyk7XG4gICAgICB9XG4gICAgfVxuICB9XG4gIHJldHVybiBhcnI7XG59O1xuXG5jb25zdCByZXZlcnRCcmFuY2hDaGFuZ2UgPSAob2JqOiBhbnksIGNoYW5nZTogYW55KSA9PiB7XG4gIGlmIChBcnJheS5pc0FycmF5KG9iaikpIHtcbiAgICByZXR1cm4gcmV2ZXJ0QXJyYXlDaGFuZ2Uob2JqLCBjaGFuZ2UpO1xuICB9IGVsc2Uge1xuICAgIHJldHVybiByZXZlcnRDaGFuZ2VzZXQob2JqLCBjaGFuZ2UuY2hhbmdlcyk7XG4gIH1cbn07XG5cbi8qKiBjb21iaW5lIGEgYmFzZSBKU09OIFBhdGggd2l0aCBhIHN1YnNlcXVlbnQgc2VnbWVudCAqL1xuZnVuY3Rpb24gYXBwZW5kKGJhc2VQYXRoOiBzdHJpbmcsIG5leHRTZWdtZW50OiBzdHJpbmcpOiBzdHJpbmcge1xuICByZXR1cm4gbmV4dFNlZ21lbnQuaW5jbHVkZXMoJy4nKSA/IGAke2Jhc2VQYXRofVske25leHRTZWdtZW50fV1gIDogYCR7YmFzZVBhdGh9LiR7bmV4dFNlZ21lbnR9YDtcbn1cblxuLyoqIHJldHVybnMgYSBKU09OIFBhdGggZmlsdGVyIGV4cHJlc3Npb247IGUuZy4sIGAkLnBldFsoP25hbWU9J3Nwb3QnKV1gICovXG5mdW5jdGlvbiBmaWx0ZXJFeHByZXNzaW9uKGJhc2VQYXRoOiBzdHJpbmcsIGZpbHRlcktleTogc3RyaW5nIHwgRnVuY3Rpb25LZXksIGZpbHRlclZhbHVlOiBzdHJpbmcgfCBudW1iZXIpIHtcbiAgY29uc3QgdmFsdWUgPSB0eXBlb2YgZmlsdGVyVmFsdWUgPT09ICdudW1iZXInID8gZmlsdGVyVmFsdWUgOiBgJyR7ZmlsdGVyVmFsdWV9J2A7XG4gIHJldHVybiB0eXBlb2YgZmlsdGVyS2V5ID09PSAnc3RyaW5nJyAmJiBmaWx0ZXJLZXkuaW5jbHVkZXMoJy4nKVxuICAgID8gYCR7YmFzZVBhdGh9Wz8oQFske2ZpbHRlcktleX1dPT0ke3ZhbHVlfSldYFxuICAgIDogYCR7YmFzZVBhdGh9Wz8oQC4ke2ZpbHRlcktleX09PSR7dmFsdWV9KV1gO1xufVxuXG5leHBvcnQge1xuICBDaGFuZ2VzZXQsXG4gIEVtYmVkZGVkT2JqS2V5c01hcFR5cGUsXG4gIEVtYmVkZGVkT2JqS2V5c1R5cGUsXG4gIElBdG9taWNDaGFuZ2UsXG4gIElDaGFuZ2UsXG4gIE9wZXJhdGlvbixcbiAgT3B0aW9ucyxcbiAgYXBwbHlDaGFuZ2VzZXQsXG4gIGF0b21pemVDaGFuZ2VzZXQsXG4gIGRpZmYsXG4gIGdldFR5cGVPZk9iaixcbiAgcmV2ZXJ0Q2hhbmdlc2V0LFxuICB1bmF0b21pemVDaGFuZ2VzZXRcbn07XG4iLCAiaW1wb3J0IHsgc2V0QnlQYXRoIH0gZnJvbSAnLi9oZWxwZXJzLmpzJztcbmltcG9ydCB7IGRpZmYsIGF0b21pemVDaGFuZ2VzZXQsIGdldFR5cGVPZk9iaiwgSUF0b21pY0NoYW5nZSwgT3BlcmF0aW9uIH0gZnJvbSAnLi9qc29uRGlmZi5qcyc7XG5cbmVudW0gQ29tcGFyZU9wZXJhdGlvbiB7XG4gIENPTlRBSU5FUiA9ICdDT05UQUlORVInLFxuICBVTkNIQU5HRUQgPSAnVU5DSEFOR0VEJ1xufVxuXG5pbnRlcmZhY2UgSUNvbXBhcmlzb25FbnJpY2hlZE5vZGUge1xuICB0eXBlOiBPcGVyYXRpb24gfCBDb21wYXJlT3BlcmF0aW9uO1xuICB2YWx1ZTogSUNvbXBhcmlzb25FbnJpY2hlZE5vZGUgfCBJQ29tcGFyaXNvbkVucmljaGVkTm9kZVtdIHwgYW55IHwgYW55W107XG4gIG9sZFZhbHVlPzogYW55O1xufVxuXG5jb25zdCBjcmVhdGVWYWx1ZSA9ICh2YWx1ZTogYW55KTogSUNvbXBhcmlzb25FbnJpY2hlZE5vZGUgPT4gKHsgdHlwZTogQ29tcGFyZU9wZXJhdGlvbi5VTkNIQU5HRUQsIHZhbHVlIH0pO1xuY29uc3QgY3JlYXRlQ29udGFpbmVyID0gKHZhbHVlOiBvYmplY3QgfCBbXSk6IElDb21wYXJpc29uRW5yaWNoZWROb2RlID0+ICh7XG4gIHR5cGU6IENvbXBhcmVPcGVyYXRpb24uQ09OVEFJTkVSLFxuICB2YWx1ZVxufSk7XG5cbmNvbnN0IGVucmljaCA9IChvYmplY3Q6IGFueSk6IElDb21wYXJpc29uRW5yaWNoZWROb2RlID0+IHtcbiAgY29uc3Qgb2JqZWN0VHlwZSA9IGdldFR5cGVPZk9iaihvYmplY3QpO1xuXG4gIHN3aXRjaCAob2JqZWN0VHlwZSkge1xuICAgIGNhc2UgJ09iamVjdCc6XG4gICAgICByZXR1cm4gT2JqZWN0LmtleXMob2JqZWN0KVxuICAgICAgICAubWFwKChrZXk6IHN0cmluZykgPT4gKHsga2V5LCB2YWx1ZTogZW5yaWNoKG9iamVjdFtrZXldKSB9KSlcbiAgICAgICAgLnJlZHVjZSgoYWNjdW11bGF0b3IsIGVudHJ5KSA9PiB7XG4gICAgICAgICAgYWNjdW11bGF0b3IudmFsdWVbZW50cnkua2V5XSA9IGVudHJ5LnZhbHVlO1xuICAgICAgICAgIHJldHVybiBhY2N1bXVsYXRvcjtcbiAgICAgICAgfSwgY3JlYXRlQ29udGFpbmVyKHt9KSk7XG4gICAgY2FzZSAnQXJyYXknOlxuICAgICAgcmV0dXJuIChvYmplY3QgYXMgYW55W10pXG4gICAgICAgIC5tYXAoKHZhbHVlKSA9PiBlbnJpY2godmFsdWUpKVxuICAgICAgICAucmVkdWNlKChhY2N1bXVsYXRvciwgdmFsdWUpID0+IHtcbiAgICAgICAgICBhY2N1bXVsYXRvci52YWx1ZS5wdXNoKHZhbHVlKTtcbiAgICAgICAgICByZXR1cm4gYWNjdW11bGF0b3I7XG4gICAgICAgIH0sIGNyZWF0ZUNvbnRhaW5lcihbXSkpO1xuICAgIGNhc2UgJ0Z1bmN0aW9uJzpcbiAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgY2FzZSAnRGF0ZSc6XG4gICAgZGVmYXVsdDpcbiAgICAgIC8vIFByaW1pdGl2ZSB2YWx1ZVxuICAgICAgcmV0dXJuIGNyZWF0ZVZhbHVlKG9iamVjdCk7XG4gIH1cbn07XG5cbmNvbnN0IGFwcGx5Q2hhbmdlbGlzdCA9IChvYmplY3Q6IElDb21wYXJpc29uRW5yaWNoZWROb2RlLCBjaGFuZ2VsaXN0OiBJQXRvbWljQ2hhbmdlW10pOiBJQ29tcGFyaXNvbkVucmljaGVkTm9kZSA9PiB7XG4gIGNoYW5nZWxpc3RcbiAgICAubWFwKChlbnRyeSkgPT4gKHsgLi4uZW50cnksIHBhdGg6IGVudHJ5LnBhdGgucmVwbGFjZSgnJC4nLCAnLicpIH0pKVxuICAgIC5tYXAoKGVudHJ5KSA9PiAoe1xuICAgICAgLi4uZW50cnksXG4gICAgICBwYXRoOiBlbnRyeS5wYXRoLnJlcGxhY2UoLyhcXFsoPzxhcnJheT5cXGQpXFxdXFwuKS9nLCAnQVJSVkFMX1NUQVJUJDxhcnJheT5BUlJWQUxfRU5EJylcbiAgICB9KSlcbiAgICAubWFwKChlbnRyeSkgPT4gKHsgLi4uZW50cnksIHBhdGg6IGVudHJ5LnBhdGgucmVwbGFjZSgvKD88ZG90PlxcLikvZywgJy52YWx1ZSQ8ZG90PicpIH0pKVxuICAgIC5tYXAoKGVudHJ5KSA9PiAoeyAuLi5lbnRyeSwgcGF0aDogZW50cnkucGF0aC5yZXBsYWNlKC9cXC4vLCAnJykgfSkpXG4gICAgLm1hcCgoZW50cnkpID0+ICh7IC4uLmVudHJ5LCBwYXRoOiBlbnRyeS5wYXRoLnJlcGxhY2UoL0FSUlZBTF9TVEFSVC9nLCAnLnZhbHVlWycpIH0pKVxuICAgIC5tYXAoKGVudHJ5KSA9PiAoeyAuLi5lbnRyeSwgcGF0aDogZW50cnkucGF0aC5yZXBsYWNlKC9BUlJWQUxfRU5EL2csICddLnZhbHVlLicpIH0pKVxuICAgIC5mb3JFYWNoKChlbnRyeSkgPT4ge1xuICAgICAgc3dpdGNoIChlbnRyeS50eXBlKSB7XG4gICAgICAgIGNhc2UgT3BlcmF0aW9uLkFERDpcbiAgICAgICAgY2FzZSBPcGVyYXRpb24uVVBEQVRFOlxuICAgICAgICAgIHNldEJ5UGF0aChvYmplY3QsIGVudHJ5LnBhdGgsIHsgdHlwZTogZW50cnkudHlwZSwgdmFsdWU6IGVudHJ5LnZhbHVlLCBvbGRWYWx1ZTogZW50cnkub2xkVmFsdWUgfSk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgT3BlcmF0aW9uLlJFTU9WRTpcbiAgICAgICAgICBzZXRCeVBhdGgob2JqZWN0LCBlbnRyeS5wYXRoLCB7IHR5cGU6IGVudHJ5LnR5cGUsIHZhbHVlOiB1bmRlZmluZWQsIG9sZFZhbHVlOiBlbnRyeS52YWx1ZSB9KTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgcmV0dXJuIG9iamVjdDtcbn07XG5cbmNvbnN0IGNvbXBhcmUgPSAob2xkT2JqZWN0OiBhbnksIG5ld09iamVjdDogYW55KTogSUNvbXBhcmlzb25FbnJpY2hlZE5vZGUgPT4ge1xuICByZXR1cm4gYXBwbHlDaGFuZ2VsaXN0KGVucmljaChvbGRPYmplY3QpLCBhdG9taXplQ2hhbmdlc2V0KGRpZmYob2xkT2JqZWN0LCBuZXdPYmplY3QpKSk7XG59O1xuXG5leHBvcnQgeyBDb21wYXJlT3BlcmF0aW9uLCBJQ29tcGFyaXNvbkVucmljaGVkTm9kZSwgY3JlYXRlVmFsdWUsIGNyZWF0ZUNvbnRhaW5lciwgZW5yaWNoLCBhcHBseUNoYW5nZWxpc3QsIGNvbXBhcmUgfTtcbiIsICJpbXBvcnQgeyBTeW5jUGx1Z2luIH0gZnJvbSAnLi9TeW5jUGx1Z2luJztcbmltcG9ydCB7IENvcmVFdmVudHMgfSBmcm9tICcuLi9jb25zdGFudHMvQ29yZUV2ZW50cyc7XG5pbXBvcnQgeyBkaWZmIH0gZnJvbSAnanNvbi1kaWZmLXRzJztcbi8qKlxuICogQmFzZUVudGl0eVNlcnZpY2U8VCBleHRlbmRzIElTeW5jPiAtIEFic3RyYWN0IGJhc2UgY2xhc3MgZm9yIGFsbCBlbnRpdHkgc2VydmljZXNcbiAqXG4gKiBQUk9WSURFUzpcbiAqIC0gR2VuZXJpYyBDUlVEIG9wZXJhdGlvbnMgKGdldCwgZ2V0QWxsLCBzYXZlLCBkZWxldGUpXG4gKiAtIFN5bmMgc3RhdHVzIG1hbmFnZW1lbnQgKGRlbGVnYXRlcyB0byBTeW5jUGx1Z2luKVxuICogLSBTZXJpYWxpemF0aW9uIGhvb2tzIChvdmVycmlkZSBpbiBzdWJjbGFzcyBpZiBuZWVkZWQpXG4gKi9cbmV4cG9ydCBjbGFzcyBCYXNlRW50aXR5U2VydmljZSB7XG4gICAgY29uc3RydWN0b3IoY29udGV4dCwgZXZlbnRCdXMpIHtcbiAgICAgICAgdGhpcy5jb250ZXh0ID0gY29udGV4dDtcbiAgICAgICAgdGhpcy5ldmVudEJ1cyA9IGV2ZW50QnVzO1xuICAgICAgICB0aGlzLnN5bmNQbHVnaW4gPSBuZXcgU3luY1BsdWdpbih0aGlzKTtcbiAgICB9XG4gICAgZ2V0IGRiKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5jb250ZXh0LmdldERhdGFiYXNlKCk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFNlcmlhbGl6ZSBlbnRpdHkgYmVmb3JlIHN0b3JpbmcgaW4gSW5kZXhlZERCXG4gICAgICovXG4gICAgc2VyaWFsaXplKGVudGl0eSkge1xuICAgICAgICByZXR1cm4gZW50aXR5O1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBEZXNlcmlhbGl6ZSBkYXRhIGZyb20gSW5kZXhlZERCIGJhY2sgdG8gZW50aXR5XG4gICAgICovXG4gICAgZGVzZXJpYWxpemUoZGF0YSkge1xuICAgICAgICByZXR1cm4gZGF0YTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogR2V0IGEgc2luZ2xlIGVudGl0eSBieSBJRFxuICAgICAqL1xuICAgIGFzeW5jIGdldChpZCkge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICAgICAgY29uc3QgdHJhbnNhY3Rpb24gPSB0aGlzLmRiLnRyYW5zYWN0aW9uKFt0aGlzLnN0b3JlTmFtZV0sICdyZWFkb25seScpO1xuICAgICAgICAgICAgY29uc3Qgc3RvcmUgPSB0cmFuc2FjdGlvbi5vYmplY3RTdG9yZSh0aGlzLnN0b3JlTmFtZSk7XG4gICAgICAgICAgICBjb25zdCByZXF1ZXN0ID0gc3RvcmUuZ2V0KGlkKTtcbiAgICAgICAgICAgIHJlcXVlc3Qub25zdWNjZXNzID0gKCkgPT4ge1xuICAgICAgICAgICAgICAgIGNvbnN0IGRhdGEgPSByZXF1ZXN0LnJlc3VsdDtcbiAgICAgICAgICAgICAgICByZXNvbHZlKGRhdGEgPyB0aGlzLmRlc2VyaWFsaXplKGRhdGEpIDogbnVsbCk7XG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgcmVxdWVzdC5vbmVycm9yID0gKCkgPT4ge1xuICAgICAgICAgICAgICAgIHJlamVjdChuZXcgRXJyb3IoYEZhaWxlZCB0byBnZXQgJHt0aGlzLmVudGl0eVR5cGV9ICR7aWR9OiAke3JlcXVlc3QuZXJyb3J9YCkpO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgfSk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEdldCBhbGwgZW50aXRpZXNcbiAgICAgKi9cbiAgICBhc3luYyBnZXRBbGwoKSB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgICAgICBjb25zdCB0cmFuc2FjdGlvbiA9IHRoaXMuZGIudHJhbnNhY3Rpb24oW3RoaXMuc3RvcmVOYW1lXSwgJ3JlYWRvbmx5Jyk7XG4gICAgICAgICAgICBjb25zdCBzdG9yZSA9IHRyYW5zYWN0aW9uLm9iamVjdFN0b3JlKHRoaXMuc3RvcmVOYW1lKTtcbiAgICAgICAgICAgIGNvbnN0IHJlcXVlc3QgPSBzdG9yZS5nZXRBbGwoKTtcbiAgICAgICAgICAgIHJlcXVlc3Qub25zdWNjZXNzID0gKCkgPT4ge1xuICAgICAgICAgICAgICAgIGNvbnN0IGRhdGEgPSByZXF1ZXN0LnJlc3VsdDtcbiAgICAgICAgICAgICAgICBjb25zdCBlbnRpdGllcyA9IGRhdGEubWFwKGl0ZW0gPT4gdGhpcy5kZXNlcmlhbGl6ZShpdGVtKSk7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZShlbnRpdGllcyk7XG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgcmVxdWVzdC5vbmVycm9yID0gKCkgPT4ge1xuICAgICAgICAgICAgICAgIHJlamVjdChuZXcgRXJyb3IoYEZhaWxlZCB0byBnZXQgYWxsICR7dGhpcy5lbnRpdHlUeXBlfXM6ICR7cmVxdWVzdC5lcnJvcn1gKSk7XG4gICAgICAgICAgICB9O1xuICAgICAgICB9KTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogU2F2ZSBhbiBlbnRpdHkgKGNyZWF0ZSBvciB1cGRhdGUpXG4gICAgICogRW1pdHMgRU5USVRZX1NBVkVEIGV2ZW50IHdpdGggb3BlcmF0aW9uIHR5cGUgYW5kIGNoYW5nZXMgKGRpZmYgZm9yIHVwZGF0ZXMpXG4gICAgICogQHBhcmFtIGVudGl0eSAtIEVudGl0eSB0byBzYXZlXG4gICAgICogQHBhcmFtIHNpbGVudCAtIElmIHRydWUsIHNraXAgZXZlbnQgZW1pc3Npb24gKHVzZWQgZm9yIHNlZWRpbmcpXG4gICAgICovXG4gICAgYXN5bmMgc2F2ZShlbnRpdHksIHNpbGVudCA9IGZhbHNlKSB7XG4gICAgICAgIGNvbnN0IGVudGl0eUlkID0gZW50aXR5LmlkO1xuICAgICAgICBjb25zdCBleGlzdGluZ0VudGl0eSA9IGF3YWl0IHRoaXMuZ2V0KGVudGl0eUlkKTtcbiAgICAgICAgY29uc3QgaXNDcmVhdGUgPSBleGlzdGluZ0VudGl0eSA9PT0gbnVsbDtcbiAgICAgICAgLy8gQ2FsY3VsYXRlIGNoYW5nZXM6IGZ1bGwgZW50aXR5IGZvciBjcmVhdGUsIGRpZmYgZm9yIHVwZGF0ZVxuICAgICAgICBsZXQgY2hhbmdlcztcbiAgICAgICAgaWYgKGlzQ3JlYXRlKSB7XG4gICAgICAgICAgICBjaGFuZ2VzID0gZW50aXR5O1xuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgY29uc3QgZXhpc3RpbmdTZXJpYWxpemVkID0gdGhpcy5zZXJpYWxpemUoZXhpc3RpbmdFbnRpdHkpO1xuICAgICAgICAgICAgY29uc3QgbmV3U2VyaWFsaXplZCA9IHRoaXMuc2VyaWFsaXplKGVudGl0eSk7XG4gICAgICAgICAgICBjaGFuZ2VzID0gZGlmZihleGlzdGluZ1NlcmlhbGl6ZWQsIG5ld1NlcmlhbGl6ZWQpO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHNlcmlhbGl6ZWQgPSB0aGlzLnNlcmlhbGl6ZShlbnRpdHkpO1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICAgICAgY29uc3QgdHJhbnNhY3Rpb24gPSB0aGlzLmRiLnRyYW5zYWN0aW9uKFt0aGlzLnN0b3JlTmFtZV0sICdyZWFkd3JpdGUnKTtcbiAgICAgICAgICAgIGNvbnN0IHN0b3JlID0gdHJhbnNhY3Rpb24ub2JqZWN0U3RvcmUodGhpcy5zdG9yZU5hbWUpO1xuICAgICAgICAgICAgY29uc3QgcmVxdWVzdCA9IHN0b3JlLnB1dChzZXJpYWxpemVkKTtcbiAgICAgICAgICAgIHJlcXVlc3Qub25zdWNjZXNzID0gKCkgPT4ge1xuICAgICAgICAgICAgICAgIC8vIE9ubHkgZW1pdCBldmVudCBpZiBub3Qgc2lsZW50IChzaWxlbnQgdXNlZCBmb3Igc2VlZGluZylcbiAgICAgICAgICAgICAgICBpZiAoIXNpbGVudCkge1xuICAgICAgICAgICAgICAgICAgICBjb25zdCBwYXlsb2FkID0ge1xuICAgICAgICAgICAgICAgICAgICAgICAgZW50aXR5VHlwZTogdGhpcy5lbnRpdHlUeXBlLFxuICAgICAgICAgICAgICAgICAgICAgICAgZW50aXR5SWQsXG4gICAgICAgICAgICAgICAgICAgICAgICBvcGVyYXRpb246IGlzQ3JlYXRlID8gJ2NyZWF0ZScgOiAndXBkYXRlJyxcbiAgICAgICAgICAgICAgICAgICAgICAgIGNoYW5nZXMsXG4gICAgICAgICAgICAgICAgICAgICAgICB0aW1lc3RhbXA6IERhdGUubm93KClcbiAgICAgICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5ldmVudEJ1cy5lbWl0KENvcmVFdmVudHMuRU5USVRZX1NBVkVELCBwYXlsb2FkKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSgpO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIHJlcXVlc3Qub25lcnJvciA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICByZWplY3QobmV3IEVycm9yKGBGYWlsZWQgdG8gc2F2ZSAke3RoaXMuZW50aXR5VHlwZX0gJHtlbnRpdHlJZH06ICR7cmVxdWVzdC5lcnJvcn1gKSk7XG4gICAgICAgICAgICB9O1xuICAgICAgICB9KTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogRGVsZXRlIGFuIGVudGl0eVxuICAgICAqIEVtaXRzIEVOVElUWV9ERUxFVEVEIGV2ZW50XG4gICAgICovXG4gICAgYXN5bmMgZGVsZXRlKGlkKSB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgICAgICBjb25zdCB0cmFuc2FjdGlvbiA9IHRoaXMuZGIudHJhbnNhY3Rpb24oW3RoaXMuc3RvcmVOYW1lXSwgJ3JlYWR3cml0ZScpO1xuICAgICAgICAgICAgY29uc3Qgc3RvcmUgPSB0cmFuc2FjdGlvbi5vYmplY3RTdG9yZSh0aGlzLnN0b3JlTmFtZSk7XG4gICAgICAgICAgICBjb25zdCByZXF1ZXN0ID0gc3RvcmUuZGVsZXRlKGlkKTtcbiAgICAgICAgICAgIHJlcXVlc3Qub25zdWNjZXNzID0gKCkgPT4ge1xuICAgICAgICAgICAgICAgIGNvbnN0IHBheWxvYWQgPSB7XG4gICAgICAgICAgICAgICAgICAgIGVudGl0eVR5cGU6IHRoaXMuZW50aXR5VHlwZSxcbiAgICAgICAgICAgICAgICAgICAgZW50aXR5SWQ6IGlkLFxuICAgICAgICAgICAgICAgICAgICBvcGVyYXRpb246ICdkZWxldGUnLFxuICAgICAgICAgICAgICAgICAgICB0aW1lc3RhbXA6IERhdGUubm93KClcbiAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICAgIHRoaXMuZXZlbnRCdXMuZW1pdChDb3JlRXZlbnRzLkVOVElUWV9ERUxFVEVELCBwYXlsb2FkKTtcbiAgICAgICAgICAgICAgICByZXNvbHZlKCk7XG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgcmVxdWVzdC5vbmVycm9yID0gKCkgPT4ge1xuICAgICAgICAgICAgICAgIHJlamVjdChuZXcgRXJyb3IoYEZhaWxlZCB0byBkZWxldGUgJHt0aGlzLmVudGl0eVR5cGV9ICR7aWR9OiAke3JlcXVlc3QuZXJyb3J9YCkpO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgfSk7XG4gICAgfVxuICAgIC8vIFN5bmMgbWV0aG9kcyAtIGRlbGVnYXRlIHRvIFN5bmNQbHVnaW5cbiAgICBhc3luYyBtYXJrQXNTeW5jZWQoaWQpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuc3luY1BsdWdpbi5tYXJrQXNTeW5jZWQoaWQpO1xuICAgIH1cbiAgICBhc3luYyBtYXJrQXNFcnJvcihpZCkge1xuICAgICAgICByZXR1cm4gdGhpcy5zeW5jUGx1Z2luLm1hcmtBc0Vycm9yKGlkKTtcbiAgICB9XG4gICAgYXN5bmMgZ2V0U3luY1N0YXR1cyhpZCkge1xuICAgICAgICByZXR1cm4gdGhpcy5zeW5jUGx1Z2luLmdldFN5bmNTdGF0dXMoaWQpO1xuICAgIH1cbiAgICBhc3luYyBnZXRCeVN5bmNTdGF0dXMoc3luY1N0YXR1cykge1xuICAgICAgICByZXR1cm4gdGhpcy5zeW5jUGx1Z2luLmdldEJ5U3luY1N0YXR1cyhzeW5jU3RhdHVzKTtcbiAgICB9XG59XG4iLCAiaW1wb3J0IHsgRXZlbnRTdG9yZSB9IGZyb20gJy4vRXZlbnRTdG9yZSc7XG5pbXBvcnQgeyBFdmVudFNlcmlhbGl6YXRpb24gfSBmcm9tICcuL0V2ZW50U2VyaWFsaXphdGlvbic7XG5pbXBvcnQgeyBCYXNlRW50aXR5U2VydmljZSB9IGZyb20gJy4uL0Jhc2VFbnRpdHlTZXJ2aWNlJztcbi8qKlxuICogRXZlbnRTZXJ2aWNlIC0gQ1JVRCBvcGVyYXRpb25zIGZvciBjYWxlbmRhciBldmVudHMgaW4gSW5kZXhlZERCXG4gKlxuICogRXh0ZW5kcyBCYXNlRW50aXR5U2VydmljZSBmb3Igc2hhcmVkIENSVUQgYW5kIHN5bmMgbG9naWMuXG4gKiBQcm92aWRlcyBldmVudC1zcGVjaWZpYyBxdWVyeSBtZXRob2RzLlxuICovXG5leHBvcnQgY2xhc3MgRXZlbnRTZXJ2aWNlIGV4dGVuZHMgQmFzZUVudGl0eVNlcnZpY2Uge1xuICAgIGNvbnN0cnVjdG9yKGNvbnRleHQsIGV2ZW50QnVzKSB7XG4gICAgICAgIHN1cGVyKGNvbnRleHQsIGV2ZW50QnVzKTtcbiAgICAgICAgdGhpcy5zdG9yZU5hbWUgPSBFdmVudFN0b3JlLlNUT1JFX05BTUU7XG4gICAgICAgIHRoaXMuZW50aXR5VHlwZSA9ICdFdmVudCc7XG4gICAgfVxuICAgIHNlcmlhbGl6ZShldmVudCkge1xuICAgICAgICByZXR1cm4gRXZlbnRTZXJpYWxpemF0aW9uLnNlcmlhbGl6ZShldmVudCk7XG4gICAgfVxuICAgIGRlc2VyaWFsaXplKGRhdGEpIHtcbiAgICAgICAgcmV0dXJuIEV2ZW50U2VyaWFsaXphdGlvbi5kZXNlcmlhbGl6ZShkYXRhKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogR2V0IGV2ZW50cyB3aXRoaW4gYSBkYXRlIHJhbmdlXG4gICAgICovXG4gICAgYXN5bmMgZ2V0QnlEYXRlUmFuZ2Uoc3RhcnQsIGVuZCkge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICAgICAgY29uc3QgdHJhbnNhY3Rpb24gPSB0aGlzLmRiLnRyYW5zYWN0aW9uKFt0aGlzLnN0b3JlTmFtZV0sICdyZWFkb25seScpO1xuICAgICAgICAgICAgY29uc3Qgc3RvcmUgPSB0cmFuc2FjdGlvbi5vYmplY3RTdG9yZSh0aGlzLnN0b3JlTmFtZSk7XG4gICAgICAgICAgICBjb25zdCBpbmRleCA9IHN0b3JlLmluZGV4KCdzdGFydCcpO1xuICAgICAgICAgICAgY29uc3QgcmFuZ2UgPSBJREJLZXlSYW5nZS5sb3dlckJvdW5kKHN0YXJ0LnRvSVNPU3RyaW5nKCkpO1xuICAgICAgICAgICAgY29uc3QgcmVxdWVzdCA9IGluZGV4LmdldEFsbChyYW5nZSk7XG4gICAgICAgICAgICByZXF1ZXN0Lm9uc3VjY2VzcyA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICBjb25zdCBkYXRhID0gcmVxdWVzdC5yZXN1bHQ7XG4gICAgICAgICAgICAgICAgY29uc3QgZXZlbnRzID0gZGF0YVxuICAgICAgICAgICAgICAgICAgICAubWFwKGl0ZW0gPT4gdGhpcy5kZXNlcmlhbGl6ZShpdGVtKSlcbiAgICAgICAgICAgICAgICAgICAgLmZpbHRlcihldmVudCA9PiBldmVudC5zdGFydCA8PSBlbmQpO1xuICAgICAgICAgICAgICAgIHJlc29sdmUoZXZlbnRzKTtcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICByZXF1ZXN0Lm9uZXJyb3IgPSAoKSA9PiB7XG4gICAgICAgICAgICAgICAgcmVqZWN0KG5ldyBFcnJvcihgRmFpbGVkIHRvIGdldCBldmVudHMgYnkgZGF0ZSByYW5nZTogJHtyZXF1ZXN0LmVycm9yfWApKTtcbiAgICAgICAgICAgIH07XG4gICAgICAgIH0pO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBHZXQgZXZlbnRzIGZvciBhIHNwZWNpZmljIHJlc291cmNlXG4gICAgICovXG4gICAgYXN5bmMgZ2V0QnlSZXNvdXJjZShyZXNvdXJjZUlkKSB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgICAgICBjb25zdCB0cmFuc2FjdGlvbiA9IHRoaXMuZGIudHJhbnNhY3Rpb24oW3RoaXMuc3RvcmVOYW1lXSwgJ3JlYWRvbmx5Jyk7XG4gICAgICAgICAgICBjb25zdCBzdG9yZSA9IHRyYW5zYWN0aW9uLm9iamVjdFN0b3JlKHRoaXMuc3RvcmVOYW1lKTtcbiAgICAgICAgICAgIGNvbnN0IGluZGV4ID0gc3RvcmUuaW5kZXgoJ3Jlc291cmNlSWQnKTtcbiAgICAgICAgICAgIGNvbnN0IHJlcXVlc3QgPSBpbmRleC5nZXRBbGwocmVzb3VyY2VJZCk7XG4gICAgICAgICAgICByZXF1ZXN0Lm9uc3VjY2VzcyA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICBjb25zdCBkYXRhID0gcmVxdWVzdC5yZXN1bHQ7XG4gICAgICAgICAgICAgICAgY29uc3QgZXZlbnRzID0gZGF0YS5tYXAoaXRlbSA9PiB0aGlzLmRlc2VyaWFsaXplKGl0ZW0pKTtcbiAgICAgICAgICAgICAgICByZXNvbHZlKGV2ZW50cyk7XG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgcmVxdWVzdC5vbmVycm9yID0gKCkgPT4ge1xuICAgICAgICAgICAgICAgIHJlamVjdChuZXcgRXJyb3IoYEZhaWxlZCB0byBnZXQgZXZlbnRzIGZvciByZXNvdXJjZSAke3Jlc291cmNlSWR9OiAke3JlcXVlc3QuZXJyb3J9YCkpO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgfSk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEdldCBldmVudHMgZm9yIGEgcmVzb3VyY2Ugd2l0aGluIGEgZGF0ZSByYW5nZVxuICAgICAqL1xuICAgIGFzeW5jIGdldEJ5UmVzb3VyY2VBbmREYXRlUmFuZ2UocmVzb3VyY2VJZCwgc3RhcnQsIGVuZCkge1xuICAgICAgICBjb25zdCByZXNvdXJjZUV2ZW50cyA9IGF3YWl0IHRoaXMuZ2V0QnlSZXNvdXJjZShyZXNvdXJjZUlkKTtcbiAgICAgICAgcmV0dXJuIHJlc291cmNlRXZlbnRzLmZpbHRlcihldmVudCA9PiBldmVudC5zdGFydCA+PSBzdGFydCAmJiBldmVudC5zdGFydCA8PSBlbmQpO1xuICAgIH1cbn1cbiIsICIvKipcbiAqIFJlc291cmNlU3RvcmUgLSBJbmRleGVkREIgT2JqZWN0U3RvcmUgZGVmaW5pdGlvbiBmb3IgcmVzb3VyY2VzXG4gKi9cbmV4cG9ydCBjbGFzcyBSZXNvdXJjZVN0b3JlIHtcbiAgICBjb25zdHJ1Y3RvcigpIHtcbiAgICAgICAgdGhpcy5zdG9yZU5hbWUgPSBSZXNvdXJjZVN0b3JlLlNUT1JFX05BTUU7XG4gICAgfVxuICAgIGNyZWF0ZShkYikge1xuICAgICAgICBjb25zdCBzdG9yZSA9IGRiLmNyZWF0ZU9iamVjdFN0b3JlKFJlc291cmNlU3RvcmUuU1RPUkVfTkFNRSwgeyBrZXlQYXRoOiAnaWQnIH0pO1xuICAgICAgICBzdG9yZS5jcmVhdGVJbmRleCgndHlwZScsICd0eXBlJywgeyB1bmlxdWU6IGZhbHNlIH0pO1xuICAgICAgICBzdG9yZS5jcmVhdGVJbmRleCgnc3luY1N0YXR1cycsICdzeW5jU3RhdHVzJywgeyB1bmlxdWU6IGZhbHNlIH0pO1xuICAgICAgICBzdG9yZS5jcmVhdGVJbmRleCgnaXNBY3RpdmUnLCAnaXNBY3RpdmUnLCB7IHVuaXF1ZTogZmFsc2UgfSk7XG4gICAgfVxufVxuUmVzb3VyY2VTdG9yZS5TVE9SRV9OQU1FID0gJ3Jlc291cmNlcyc7XG4iLCAiaW1wb3J0IHsgUmVzb3VyY2VTdG9yZSB9IGZyb20gJy4vUmVzb3VyY2VTdG9yZSc7XG5pbXBvcnQgeyBCYXNlRW50aXR5U2VydmljZSB9IGZyb20gJy4uL0Jhc2VFbnRpdHlTZXJ2aWNlJztcbi8qKlxuICogUmVzb3VyY2VTZXJ2aWNlIC0gQ1JVRCBvcGVyYXRpb25zIGZvciByZXNvdXJjZXMgaW4gSW5kZXhlZERCXG4gKi9cbmV4cG9ydCBjbGFzcyBSZXNvdXJjZVNlcnZpY2UgZXh0ZW5kcyBCYXNlRW50aXR5U2VydmljZSB7XG4gICAgY29uc3RydWN0b3IoY29udGV4dCwgZXZlbnRCdXMpIHtcbiAgICAgICAgc3VwZXIoY29udGV4dCwgZXZlbnRCdXMpO1xuICAgICAgICB0aGlzLnN0b3JlTmFtZSA9IFJlc291cmNlU3RvcmUuU1RPUkVfTkFNRTtcbiAgICAgICAgdGhpcy5lbnRpdHlUeXBlID0gJ1Jlc291cmNlJztcbiAgICB9XG4gICAgLyoqXG4gICAgICogR2V0IGFsbCBhY3RpdmUgcmVzb3VyY2VzXG4gICAgICovXG4gICAgYXN5bmMgZ2V0QWN0aXZlKCkge1xuICAgICAgICBjb25zdCBhbGwgPSBhd2FpdCB0aGlzLmdldEFsbCgpO1xuICAgICAgICByZXR1cm4gYWxsLmZpbHRlcihyID0+IHIuaXNBY3RpdmUgIT09IGZhbHNlKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogR2V0IHJlc291cmNlcyBieSBJRHNcbiAgICAgKi9cbiAgICBhc3luYyBnZXRCeUlkcyhpZHMpIHtcbiAgICAgICAgaWYgKGlkcy5sZW5ndGggPT09IDApXG4gICAgICAgICAgICByZXR1cm4gW107XG4gICAgICAgIGNvbnN0IHJlc3VsdHMgPSBhd2FpdCBQcm9taXNlLmFsbChpZHMubWFwKGlkID0+IHRoaXMuZ2V0KGlkKSkpO1xuICAgICAgICByZXR1cm4gcmVzdWx0cy5maWx0ZXIoKHIpID0+IHIgIT09IG51bGwpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBHZXQgcmVzb3VyY2VzIGJ5IHR5cGVcbiAgICAgKi9cbiAgICBhc3luYyBnZXRCeVR5cGUodHlwZSkge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICAgICAgY29uc3QgdHJhbnNhY3Rpb24gPSB0aGlzLmRiLnRyYW5zYWN0aW9uKFt0aGlzLnN0b3JlTmFtZV0sICdyZWFkb25seScpO1xuICAgICAgICAgICAgY29uc3Qgc3RvcmUgPSB0cmFuc2FjdGlvbi5vYmplY3RTdG9yZSh0aGlzLnN0b3JlTmFtZSk7XG4gICAgICAgICAgICBjb25zdCBpbmRleCA9IHN0b3JlLmluZGV4KCd0eXBlJyk7XG4gICAgICAgICAgICBjb25zdCByZXF1ZXN0ID0gaW5kZXguZ2V0QWxsKHR5cGUpO1xuICAgICAgICAgICAgcmVxdWVzdC5vbnN1Y2Nlc3MgPSAoKSA9PiB7XG4gICAgICAgICAgICAgICAgY29uc3QgZGF0YSA9IHJlcXVlc3QucmVzdWx0O1xuICAgICAgICAgICAgICAgIHJlc29sdmUoZGF0YSk7XG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgcmVxdWVzdC5vbmVycm9yID0gKCkgPT4ge1xuICAgICAgICAgICAgICAgIHJlamVjdChuZXcgRXJyb3IoYEZhaWxlZCB0byBnZXQgcmVzb3VyY2VzIGJ5IHR5cGUgJHt0eXBlfTogJHtyZXF1ZXN0LmVycm9yfWApKTtcbiAgICAgICAgICAgIH07XG4gICAgICAgIH0pO1xuICAgIH1cbn1cbiIsICIvKipcbiAqIEJvb2tpbmdTdG9yZSAtIEluZGV4ZWREQiBPYmplY3RTdG9yZSBkZWZpbml0aW9uIGZvciBib29raW5nc1xuICovXG5leHBvcnQgY2xhc3MgQm9va2luZ1N0b3JlIHtcbiAgICBjb25zdHJ1Y3RvcigpIHtcbiAgICAgICAgdGhpcy5zdG9yZU5hbWUgPSBCb29raW5nU3RvcmUuU1RPUkVfTkFNRTtcbiAgICB9XG4gICAgY3JlYXRlKGRiKSB7XG4gICAgICAgIGNvbnN0IHN0b3JlID0gZGIuY3JlYXRlT2JqZWN0U3RvcmUoQm9va2luZ1N0b3JlLlNUT1JFX05BTUUsIHsga2V5UGF0aDogJ2lkJyB9KTtcbiAgICAgICAgc3RvcmUuY3JlYXRlSW5kZXgoJ2N1c3RvbWVySWQnLCAnY3VzdG9tZXJJZCcsIHsgdW5pcXVlOiBmYWxzZSB9KTtcbiAgICAgICAgc3RvcmUuY3JlYXRlSW5kZXgoJ3N0YXR1cycsICdzdGF0dXMnLCB7IHVuaXF1ZTogZmFsc2UgfSk7XG4gICAgICAgIHN0b3JlLmNyZWF0ZUluZGV4KCdzeW5jU3RhdHVzJywgJ3N5bmNTdGF0dXMnLCB7IHVuaXF1ZTogZmFsc2UgfSk7XG4gICAgICAgIHN0b3JlLmNyZWF0ZUluZGV4KCdjcmVhdGVkQXQnLCAnY3JlYXRlZEF0JywgeyB1bmlxdWU6IGZhbHNlIH0pO1xuICAgIH1cbn1cbkJvb2tpbmdTdG9yZS5TVE9SRV9OQU1FID0gJ2Jvb2tpbmdzJztcbiIsICJpbXBvcnQgeyBCb29raW5nU3RvcmUgfSBmcm9tICcuL0Jvb2tpbmdTdG9yZSc7XG5pbXBvcnQgeyBCYXNlRW50aXR5U2VydmljZSB9IGZyb20gJy4uL0Jhc2VFbnRpdHlTZXJ2aWNlJztcbi8qKlxuICogQm9va2luZ1NlcnZpY2UgLSBDUlVEIG9wZXJhdGlvbnMgZm9yIGJvb2tpbmdzIGluIEluZGV4ZWREQlxuICovXG5leHBvcnQgY2xhc3MgQm9va2luZ1NlcnZpY2UgZXh0ZW5kcyBCYXNlRW50aXR5U2VydmljZSB7XG4gICAgY29uc3RydWN0b3IoY29udGV4dCwgZXZlbnRCdXMpIHtcbiAgICAgICAgc3VwZXIoY29udGV4dCwgZXZlbnRCdXMpO1xuICAgICAgICB0aGlzLnN0b3JlTmFtZSA9IEJvb2tpbmdTdG9yZS5TVE9SRV9OQU1FO1xuICAgICAgICB0aGlzLmVudGl0eVR5cGUgPSAnQm9va2luZyc7XG4gICAgfVxuICAgIHNlcmlhbGl6ZShib29raW5nKSB7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAuLi5ib29raW5nLFxuICAgICAgICAgICAgY3JlYXRlZEF0OiBib29raW5nLmNyZWF0ZWRBdC50b0lTT1N0cmluZygpXG4gICAgICAgIH07XG4gICAgfVxuICAgIGRlc2VyaWFsaXplKGRhdGEpIHtcbiAgICAgICAgY29uc3QgcmF3ID0gZGF0YTtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIC4uLnJhdyxcbiAgICAgICAgICAgIGNyZWF0ZWRBdDogbmV3IERhdGUocmF3LmNyZWF0ZWRBdClcbiAgICAgICAgfTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogR2V0IGJvb2tpbmdzIGZvciBhIGN1c3RvbWVyXG4gICAgICovXG4gICAgYXN5bmMgZ2V0QnlDdXN0b21lcihjdXN0b21lcklkKSB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgICAgICBjb25zdCB0cmFuc2FjdGlvbiA9IHRoaXMuZGIudHJhbnNhY3Rpb24oW3RoaXMuc3RvcmVOYW1lXSwgJ3JlYWRvbmx5Jyk7XG4gICAgICAgICAgICBjb25zdCBzdG9yZSA9IHRyYW5zYWN0aW9uLm9iamVjdFN0b3JlKHRoaXMuc3RvcmVOYW1lKTtcbiAgICAgICAgICAgIGNvbnN0IGluZGV4ID0gc3RvcmUuaW5kZXgoJ2N1c3RvbWVySWQnKTtcbiAgICAgICAgICAgIGNvbnN0IHJlcXVlc3QgPSBpbmRleC5nZXRBbGwoY3VzdG9tZXJJZCk7XG4gICAgICAgICAgICByZXF1ZXN0Lm9uc3VjY2VzcyA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICBjb25zdCBkYXRhID0gcmVxdWVzdC5yZXN1bHQ7XG4gICAgICAgICAgICAgICAgY29uc3QgYm9va2luZ3MgPSBkYXRhLm1hcChpdGVtID0+IHRoaXMuZGVzZXJpYWxpemUoaXRlbSkpO1xuICAgICAgICAgICAgICAgIHJlc29sdmUoYm9va2luZ3MpO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIHJlcXVlc3Qub25lcnJvciA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICByZWplY3QobmV3IEVycm9yKGBGYWlsZWQgdG8gZ2V0IGJvb2tpbmdzIGZvciBjdXN0b21lciAke2N1c3RvbWVySWR9OiAke3JlcXVlc3QuZXJyb3J9YCkpO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgfSk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEdldCBib29raW5ncyBieSBzdGF0dXNcbiAgICAgKi9cbiAgICBhc3luYyBnZXRCeVN0YXR1cyhzdGF0dXMpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHRyYW5zYWN0aW9uID0gdGhpcy5kYi50cmFuc2FjdGlvbihbdGhpcy5zdG9yZU5hbWVdLCAncmVhZG9ubHknKTtcbiAgICAgICAgICAgIGNvbnN0IHN0b3JlID0gdHJhbnNhY3Rpb24ub2JqZWN0U3RvcmUodGhpcy5zdG9yZU5hbWUpO1xuICAgICAgICAgICAgY29uc3QgaW5kZXggPSBzdG9yZS5pbmRleCgnc3RhdHVzJyk7XG4gICAgICAgICAgICBjb25zdCByZXF1ZXN0ID0gaW5kZXguZ2V0QWxsKHN0YXR1cyk7XG4gICAgICAgICAgICByZXF1ZXN0Lm9uc3VjY2VzcyA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICBjb25zdCBkYXRhID0gcmVxdWVzdC5yZXN1bHQ7XG4gICAgICAgICAgICAgICAgY29uc3QgYm9va2luZ3MgPSBkYXRhLm1hcChpdGVtID0+IHRoaXMuZGVzZXJpYWxpemUoaXRlbSkpO1xuICAgICAgICAgICAgICAgIHJlc29sdmUoYm9va2luZ3MpO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIHJlcXVlc3Qub25lcnJvciA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICByZWplY3QobmV3IEVycm9yKGBGYWlsZWQgdG8gZ2V0IGJvb2tpbmdzIHdpdGggc3RhdHVzICR7c3RhdHVzfTogJHtyZXF1ZXN0LmVycm9yfWApKTtcbiAgICAgICAgICAgIH07XG4gICAgICAgIH0pO1xuICAgIH1cbn1cbiIsICIvKipcbiAqIEN1c3RvbWVyU3RvcmUgLSBJbmRleGVkREIgT2JqZWN0U3RvcmUgZGVmaW5pdGlvbiBmb3IgY3VzdG9tZXJzXG4gKi9cbmV4cG9ydCBjbGFzcyBDdXN0b21lclN0b3JlIHtcbiAgICBjb25zdHJ1Y3RvcigpIHtcbiAgICAgICAgdGhpcy5zdG9yZU5hbWUgPSBDdXN0b21lclN0b3JlLlNUT1JFX05BTUU7XG4gICAgfVxuICAgIGNyZWF0ZShkYikge1xuICAgICAgICBjb25zdCBzdG9yZSA9IGRiLmNyZWF0ZU9iamVjdFN0b3JlKEN1c3RvbWVyU3RvcmUuU1RPUkVfTkFNRSwgeyBrZXlQYXRoOiAnaWQnIH0pO1xuICAgICAgICBzdG9yZS5jcmVhdGVJbmRleCgnbmFtZScsICduYW1lJywgeyB1bmlxdWU6IGZhbHNlIH0pO1xuICAgICAgICBzdG9yZS5jcmVhdGVJbmRleCgncGhvbmUnLCAncGhvbmUnLCB7IHVuaXF1ZTogZmFsc2UgfSk7XG4gICAgICAgIHN0b3JlLmNyZWF0ZUluZGV4KCdzeW5jU3RhdHVzJywgJ3N5bmNTdGF0dXMnLCB7IHVuaXF1ZTogZmFsc2UgfSk7XG4gICAgfVxufVxuQ3VzdG9tZXJTdG9yZS5TVE9SRV9OQU1FID0gJ2N1c3RvbWVycyc7XG4iLCAiaW1wb3J0IHsgQ3VzdG9tZXJTdG9yZSB9IGZyb20gJy4vQ3VzdG9tZXJTdG9yZSc7XG5pbXBvcnQgeyBCYXNlRW50aXR5U2VydmljZSB9IGZyb20gJy4uL0Jhc2VFbnRpdHlTZXJ2aWNlJztcbi8qKlxuICogQ3VzdG9tZXJTZXJ2aWNlIC0gQ1JVRCBvcGVyYXRpb25zIGZvciBjdXN0b21lcnMgaW4gSW5kZXhlZERCXG4gKi9cbmV4cG9ydCBjbGFzcyBDdXN0b21lclNlcnZpY2UgZXh0ZW5kcyBCYXNlRW50aXR5U2VydmljZSB7XG4gICAgY29uc3RydWN0b3IoY29udGV4dCwgZXZlbnRCdXMpIHtcbiAgICAgICAgc3VwZXIoY29udGV4dCwgZXZlbnRCdXMpO1xuICAgICAgICB0aGlzLnN0b3JlTmFtZSA9IEN1c3RvbWVyU3RvcmUuU1RPUkVfTkFNRTtcbiAgICAgICAgdGhpcy5lbnRpdHlUeXBlID0gJ0N1c3RvbWVyJztcbiAgICB9XG4gICAgLyoqXG4gICAgICogU2VhcmNoIGN1c3RvbWVycyBieSBuYW1lIChjYXNlLWluc2Vuc2l0aXZlIGNvbnRhaW5zKVxuICAgICAqL1xuICAgIGFzeW5jIHNlYXJjaEJ5TmFtZShxdWVyeSkge1xuICAgICAgICBjb25zdCBhbGwgPSBhd2FpdCB0aGlzLmdldEFsbCgpO1xuICAgICAgICBjb25zdCBsb3dlclF1ZXJ5ID0gcXVlcnkudG9Mb3dlckNhc2UoKTtcbiAgICAgICAgcmV0dXJuIGFsbC5maWx0ZXIoYyA9PiBjLm5hbWUudG9Mb3dlckNhc2UoKS5pbmNsdWRlcyhsb3dlclF1ZXJ5KSk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEZpbmQgY3VzdG9tZXIgYnkgcGhvbmVcbiAgICAgKi9cbiAgICBhc3luYyBnZXRCeVBob25lKHBob25lKSB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgICAgICBjb25zdCB0cmFuc2FjdGlvbiA9IHRoaXMuZGIudHJhbnNhY3Rpb24oW3RoaXMuc3RvcmVOYW1lXSwgJ3JlYWRvbmx5Jyk7XG4gICAgICAgICAgICBjb25zdCBzdG9yZSA9IHRyYW5zYWN0aW9uLm9iamVjdFN0b3JlKHRoaXMuc3RvcmVOYW1lKTtcbiAgICAgICAgICAgIGNvbnN0IGluZGV4ID0gc3RvcmUuaW5kZXgoJ3Bob25lJyk7XG4gICAgICAgICAgICBjb25zdCByZXF1ZXN0ID0gaW5kZXguZ2V0KHBob25lKTtcbiAgICAgICAgICAgIHJlcXVlc3Qub25zdWNjZXNzID0gKCkgPT4ge1xuICAgICAgICAgICAgICAgIGNvbnN0IGRhdGEgPSByZXF1ZXN0LnJlc3VsdDtcbiAgICAgICAgICAgICAgICByZXNvbHZlKGRhdGEgPyBkYXRhIDogbnVsbCk7XG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgcmVxdWVzdC5vbmVycm9yID0gKCkgPT4ge1xuICAgICAgICAgICAgICAgIHJlamVjdChuZXcgRXJyb3IoYEZhaWxlZCB0byBmaW5kIGN1c3RvbWVyIGJ5IHBob25lICR7cGhvbmV9OiAke3JlcXVlc3QuZXJyb3J9YCkpO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgfSk7XG4gICAgfVxufVxuIiwgIi8qKlxuICogVGVhbVN0b3JlIC0gSW5kZXhlZERCIE9iamVjdFN0b3JlIGRlZmluaXRpb24gZm9yIHRlYW1zXG4gKi9cbmV4cG9ydCBjbGFzcyBUZWFtU3RvcmUge1xuICAgIGNvbnN0cnVjdG9yKCkge1xuICAgICAgICB0aGlzLnN0b3JlTmFtZSA9IFRlYW1TdG9yZS5TVE9SRV9OQU1FO1xuICAgIH1cbiAgICBjcmVhdGUoZGIpIHtcbiAgICAgICAgZGIuY3JlYXRlT2JqZWN0U3RvcmUoVGVhbVN0b3JlLlNUT1JFX05BTUUsIHsga2V5UGF0aDogJ2lkJyB9KTtcbiAgICB9XG59XG5UZWFtU3RvcmUuU1RPUkVfTkFNRSA9ICd0ZWFtcyc7XG4iLCAiaW1wb3J0IHsgVGVhbVN0b3JlIH0gZnJvbSAnLi9UZWFtU3RvcmUnO1xuaW1wb3J0IHsgQmFzZUVudGl0eVNlcnZpY2UgfSBmcm9tICcuLi9CYXNlRW50aXR5U2VydmljZSc7XG4vKipcbiAqIFRlYW1TZXJ2aWNlIC0gQ1JVRCBvcGVyYXRpb25zIGZvciB0ZWFtcyBpbiBJbmRleGVkREJcbiAqXG4gKiBUZWFtcyBkZWZpbmUgd2hpY2ggcmVzb3VyY2VzIGJlbG9uZyB0b2dldGhlciBmb3IgaGllcmFyY2hpY2FsIGdyb3VwaW5nLlxuICogRXh0ZW5kcyBCYXNlRW50aXR5U2VydmljZSBmb3Igc3RhbmRhcmQgZW50aXR5IG9wZXJhdGlvbnMuXG4gKi9cbmV4cG9ydCBjbGFzcyBUZWFtU2VydmljZSBleHRlbmRzIEJhc2VFbnRpdHlTZXJ2aWNlIHtcbiAgICBjb25zdHJ1Y3Rvcihjb250ZXh0LCBldmVudEJ1cykge1xuICAgICAgICBzdXBlcihjb250ZXh0LCBldmVudEJ1cyk7XG4gICAgICAgIHRoaXMuc3RvcmVOYW1lID0gVGVhbVN0b3JlLlNUT1JFX05BTUU7XG4gICAgICAgIHRoaXMuZW50aXR5VHlwZSA9ICdUZWFtJztcbiAgICB9XG4gICAgLyoqXG4gICAgICogR2V0IHRlYW1zIGJ5IElEc1xuICAgICAqL1xuICAgIGFzeW5jIGdldEJ5SWRzKGlkcykge1xuICAgICAgICBpZiAoaWRzLmxlbmd0aCA9PT0gMClcbiAgICAgICAgICAgIHJldHVybiBbXTtcbiAgICAgICAgY29uc3QgcmVzdWx0cyA9IGF3YWl0IFByb21pc2UuYWxsKGlkcy5tYXAoaWQgPT4gdGhpcy5nZXQoaWQpKSk7XG4gICAgICAgIHJldHVybiByZXN1bHRzLmZpbHRlcigodCkgPT4gdCAhPT0gbnVsbCk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEJ1aWxkIHJldmVyc2UgbG9va3VwOiByZXNvdXJjZUlkIFx1MjE5MiB0ZWFtSWRcbiAgICAgKi9cbiAgICBhc3luYyBidWlsZFJlc291cmNlVG9UZWFtTWFwKCkge1xuICAgICAgICBjb25zdCB0ZWFtcyA9IGF3YWl0IHRoaXMuZ2V0QWxsKCk7XG4gICAgICAgIGNvbnN0IG1hcCA9IHt9O1xuICAgICAgICBmb3IgKGNvbnN0IHRlYW0gb2YgdGVhbXMpIHtcbiAgICAgICAgICAgIGZvciAoY29uc3QgcmVzb3VyY2VJZCBvZiB0ZWFtLnJlc291cmNlSWRzKSB7XG4gICAgICAgICAgICAgICAgbWFwW3Jlc291cmNlSWRdID0gdGVhbS5pZDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gbWFwO1xuICAgIH1cbn1cbiIsICIvKipcbiAqIERlcGFydG1lbnRTdG9yZSAtIEluZGV4ZWREQiBPYmplY3RTdG9yZSBkZWZpbml0aW9uIGZvciBkZXBhcnRtZW50c1xuICovXG5leHBvcnQgY2xhc3MgRGVwYXJ0bWVudFN0b3JlIHtcbiAgICBjb25zdHJ1Y3RvcigpIHtcbiAgICAgICAgdGhpcy5zdG9yZU5hbWUgPSBEZXBhcnRtZW50U3RvcmUuU1RPUkVfTkFNRTtcbiAgICB9XG4gICAgY3JlYXRlKGRiKSB7XG4gICAgICAgIGRiLmNyZWF0ZU9iamVjdFN0b3JlKERlcGFydG1lbnRTdG9yZS5TVE9SRV9OQU1FLCB7IGtleVBhdGg6ICdpZCcgfSk7XG4gICAgfVxufVxuRGVwYXJ0bWVudFN0b3JlLlNUT1JFX05BTUUgPSAnZGVwYXJ0bWVudHMnO1xuIiwgImltcG9ydCB7IERlcGFydG1lbnRTdG9yZSB9IGZyb20gJy4vRGVwYXJ0bWVudFN0b3JlJztcbmltcG9ydCB7IEJhc2VFbnRpdHlTZXJ2aWNlIH0gZnJvbSAnLi4vQmFzZUVudGl0eVNlcnZpY2UnO1xuLyoqXG4gKiBEZXBhcnRtZW50U2VydmljZSAtIENSVUQgb3BlcmF0aW9ucyBmb3IgZGVwYXJ0bWVudHMgaW4gSW5kZXhlZERCXG4gKi9cbmV4cG9ydCBjbGFzcyBEZXBhcnRtZW50U2VydmljZSBleHRlbmRzIEJhc2VFbnRpdHlTZXJ2aWNlIHtcbiAgICBjb25zdHJ1Y3Rvcihjb250ZXh0LCBldmVudEJ1cykge1xuICAgICAgICBzdXBlcihjb250ZXh0LCBldmVudEJ1cyk7XG4gICAgICAgIHRoaXMuc3RvcmVOYW1lID0gRGVwYXJ0bWVudFN0b3JlLlNUT1JFX05BTUU7XG4gICAgICAgIHRoaXMuZW50aXR5VHlwZSA9ICdEZXBhcnRtZW50JztcbiAgICB9XG4gICAgLyoqXG4gICAgICogR2V0IGRlcGFydG1lbnRzIGJ5IElEc1xuICAgICAqL1xuICAgIGFzeW5jIGdldEJ5SWRzKGlkcykge1xuICAgICAgICBpZiAoaWRzLmxlbmd0aCA9PT0gMClcbiAgICAgICAgICAgIHJldHVybiBbXTtcbiAgICAgICAgY29uc3QgcmVzdWx0cyA9IGF3YWl0IFByb21pc2UuYWxsKGlkcy5tYXAoaWQgPT4gdGhpcy5nZXQoaWQpKSk7XG4gICAgICAgIHJldHVybiByZXN1bHRzLmZpbHRlcigoZCkgPT4gZCAhPT0gbnVsbCk7XG4gICAgfVxufVxuIiwgIi8qKlxuICogU2V0dGluZ3NTdG9yZSAtIEluZGV4ZWREQiBPYmplY3RTdG9yZSBkZWZpbml0aW9uIGZvciB0ZW5hbnQgc2V0dGluZ3NcbiAqXG4gKiBTaW5nbGUgc3RvcmUgZm9yIGFsbCBzZXR0aW5ncyBzZWN0aW9ucy4gU2V0dGluZ3MgYXJlIHN0b3JlZCBhcyBvbmUgZG9jdW1lbnRcbiAqIHBlciB0ZW5hbnQgd2l0aCBpZD0ndGVuYW50LXNldHRpbmdzJy5cbiAqL1xuZXhwb3J0IGNsYXNzIFNldHRpbmdzU3RvcmUge1xuICAgIGNvbnN0cnVjdG9yKCkge1xuICAgICAgICB0aGlzLnN0b3JlTmFtZSA9IFNldHRpbmdzU3RvcmUuU1RPUkVfTkFNRTtcbiAgICB9XG4gICAgY3JlYXRlKGRiKSB7XG4gICAgICAgIGRiLmNyZWF0ZU9iamVjdFN0b3JlKFNldHRpbmdzU3RvcmUuU1RPUkVfTkFNRSwgeyBrZXlQYXRoOiAnaWQnIH0pO1xuICAgIH1cbn1cblNldHRpbmdzU3RvcmUuU1RPUkVfTkFNRSA9ICdzZXR0aW5ncyc7XG4iLCAiLyoqXG4gKiBTZXR0aW5ncyBJRHMgYXMgY29uc3QgZm9yIHR5cGUgc2FmZXR5XG4gKi9cbmV4cG9ydCBjb25zdCBTZXR0aW5nc0lkcyA9IHtcbiAgICBXT1JLV0VFSzogJ3dvcmt3ZWVrJyxcbiAgICBHUklEOiAnZ3JpZCcsXG4gICAgVElNRV9GT1JNQVQ6ICd0aW1lRm9ybWF0JyxcbiAgICBWSUVXUzogJ3ZpZXdzJ1xufTtcbiIsICJpbXBvcnQgeyBTZXR0aW5nc0lkcyB9IGZyb20gJy4uLy4uL3R5cGVzL1NldHRpbmdzVHlwZXMnO1xuaW1wb3J0IHsgU2V0dGluZ3NTdG9yZSB9IGZyb20gJy4vU2V0dGluZ3NTdG9yZSc7XG5pbXBvcnQgeyBCYXNlRW50aXR5U2VydmljZSB9IGZyb20gJy4uL0Jhc2VFbnRpdHlTZXJ2aWNlJztcbi8qKlxuICogU2V0dGluZ3NTZXJ2aWNlIC0gQ1JVRCBvcGVyYXRpb25zIGZvciB0ZW5hbnQgc2V0dGluZ3NcbiAqXG4gKiBTZXR0aW5ncyBhcmUgc3RvcmVkIGFzIHNlcGFyYXRlIHJlY29yZHMgcGVyIHNlY3Rpb24uXG4gKiBUaGlzIHNlcnZpY2UgcHJvdmlkZXMgdHlwZWQgbWV0aG9kcyBmb3IgYWNjZXNzaW5nIHNwZWNpZmljIHNldHRpbmdzLlxuICovXG5leHBvcnQgY2xhc3MgU2V0dGluZ3NTZXJ2aWNlIGV4dGVuZHMgQmFzZUVudGl0eVNlcnZpY2Uge1xuICAgIGNvbnN0cnVjdG9yKGNvbnRleHQsIGV2ZW50QnVzKSB7XG4gICAgICAgIHN1cGVyKGNvbnRleHQsIGV2ZW50QnVzKTtcbiAgICAgICAgdGhpcy5zdG9yZU5hbWUgPSBTZXR0aW5nc1N0b3JlLlNUT1JFX05BTUU7XG4gICAgICAgIHRoaXMuZW50aXR5VHlwZSA9ICdTZXR0aW5ncyc7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEdldCB3b3Jrd2VlayBzZXR0aW5nc1xuICAgICAqL1xuICAgIGFzeW5jIGdldFdvcmt3ZWVrU2V0dGluZ3MoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmdldChTZXR0aW5nc0lkcy5XT1JLV0VFSyk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEdldCBncmlkIHNldHRpbmdzXG4gICAgICovXG4gICAgYXN5bmMgZ2V0R3JpZFNldHRpbmdzKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5nZXQoU2V0dGluZ3NJZHMuR1JJRCk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEdldCB0aW1lIGZvcm1hdCBzZXR0aW5nc1xuICAgICAqL1xuICAgIGFzeW5jIGdldFRpbWVGb3JtYXRTZXR0aW5ncygpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuZ2V0KFNldHRpbmdzSWRzLlRJTUVfRk9STUFUKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogR2V0IHZpZXcgc2V0dGluZ3NcbiAgICAgKi9cbiAgICBhc3luYyBnZXRWaWV3U2V0dGluZ3MoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmdldChTZXR0aW5nc0lkcy5WSUVXUyk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEdldCB3b3Jrd2VlayBwcmVzZXQgYnkgSURcbiAgICAgKi9cbiAgICBhc3luYyBnZXRXb3Jrd2Vla1ByZXNldChwcmVzZXRJZCkge1xuICAgICAgICBjb25zdCBzZXR0aW5ncyA9IGF3YWl0IHRoaXMuZ2V0V29ya3dlZWtTZXR0aW5ncygpO1xuICAgICAgICBpZiAoIXNldHRpbmdzKVxuICAgICAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICAgIHJldHVybiBzZXR0aW5ncy5wcmVzZXRzW3ByZXNldElkXSB8fCBudWxsO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBHZXQgdGhlIGRlZmF1bHQgd29ya3dlZWsgcHJlc2V0XG4gICAgICovXG4gICAgYXN5bmMgZ2V0RGVmYXVsdFdvcmt3ZWVrUHJlc2V0KCkge1xuICAgICAgICBjb25zdCBzZXR0aW5ncyA9IGF3YWl0IHRoaXMuZ2V0V29ya3dlZWtTZXR0aW5ncygpO1xuICAgICAgICBpZiAoIXNldHRpbmdzKVxuICAgICAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICAgIHJldHVybiBzZXR0aW5ncy5wcmVzZXRzW3NldHRpbmdzLmRlZmF1bHRQcmVzZXRdIHx8IG51bGw7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEdldCBhbGwgYXZhaWxhYmxlIHdvcmt3ZWVrIHByZXNldHNcbiAgICAgKi9cbiAgICBhc3luYyBnZXRXb3Jrd2Vla1ByZXNldHMoKSB7XG4gICAgICAgIGNvbnN0IHNldHRpbmdzID0gYXdhaXQgdGhpcy5nZXRXb3Jrd2Vla1NldHRpbmdzKCk7XG4gICAgICAgIGlmICghc2V0dGluZ3MpXG4gICAgICAgICAgICByZXR1cm4gW107XG4gICAgICAgIHJldHVybiBPYmplY3QudmFsdWVzKHNldHRpbmdzLnByZXNldHMpO1xuICAgIH1cbn1cbiIsICJleHBvcnQgY2xhc3MgVmlld0NvbmZpZ1N0b3JlIHtcbiAgICBjb25zdHJ1Y3RvcigpIHtcbiAgICAgICAgdGhpcy5zdG9yZU5hbWUgPSBWaWV3Q29uZmlnU3RvcmUuU1RPUkVfTkFNRTtcbiAgICB9XG4gICAgY3JlYXRlKGRiKSB7XG4gICAgICAgIGRiLmNyZWF0ZU9iamVjdFN0b3JlKFZpZXdDb25maWdTdG9yZS5TVE9SRV9OQU1FLCB7IGtleVBhdGg6ICdpZCcgfSk7XG4gICAgfVxufVxuVmlld0NvbmZpZ1N0b3JlLlNUT1JFX05BTUUgPSAndmlld2NvbmZpZ3MnO1xuIiwgImltcG9ydCB7IFZpZXdDb25maWdTdG9yZSB9IGZyb20gJy4vVmlld0NvbmZpZ1N0b3JlJztcbmltcG9ydCB7IEJhc2VFbnRpdHlTZXJ2aWNlIH0gZnJvbSAnLi4vQmFzZUVudGl0eVNlcnZpY2UnO1xuZXhwb3J0IGNsYXNzIFZpZXdDb25maWdTZXJ2aWNlIGV4dGVuZHMgQmFzZUVudGl0eVNlcnZpY2Uge1xuICAgIGNvbnN0cnVjdG9yKGNvbnRleHQsIGV2ZW50QnVzKSB7XG4gICAgICAgIHN1cGVyKGNvbnRleHQsIGV2ZW50QnVzKTtcbiAgICAgICAgdGhpcy5zdG9yZU5hbWUgPSBWaWV3Q29uZmlnU3RvcmUuU1RPUkVfTkFNRTtcbiAgICAgICAgdGhpcy5lbnRpdHlUeXBlID0gJ1ZpZXdDb25maWcnO1xuICAgIH1cbiAgICBhc3luYyBnZXRCeUlkKGlkKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmdldChpZCk7XG4gICAgfVxufVxuIiwgIi8qKlxuICogQXVkaXRTdG9yZSAtIEluZGV4ZWREQiBzdG9yZSBjb25maWd1cmF0aW9uIGZvciBhdWRpdCBlbnRyaWVzXG4gKlxuICogU3RvcmVzIGFsbCBlbnRpdHkgY2hhbmdlcyBmb3I6XG4gKiAtIENvbXBsaWFuY2UgYW5kIGF1ZGl0IHRyYWlsXG4gKiAtIFN5bmMgdHJhY2tpbmcgd2l0aCBiYWNrZW5kXG4gKiAtIENoYW5nZSBoaXN0b3J5XG4gKlxuICogSW5kZXhlczpcbiAqIC0gc3luY1N0YXR1czogRm9yIGZpbmRpbmcgcGVuZGluZyBlbnRyaWVzIHRvIHN5bmNcbiAqIC0gc3luY2VkOiBCb29sZWFuIGZsYWcgZm9yIHF1aWNrIHN5bmMgcXVlcmllc1xuICogLSBlbnRpdHlJZDogRm9yIGdldHRpbmcgYWxsIGF1ZGl0cyBmb3IgYSBzcGVjaWZpYyBlbnRpdHlcbiAqIC0gdGltZXN0YW1wOiBGb3IgY2hyb25vbG9naWNhbCBxdWVyaWVzXG4gKi9cbmV4cG9ydCBjbGFzcyBBdWRpdFN0b3JlIHtcbiAgICBjb25zdHJ1Y3RvcigpIHtcbiAgICAgICAgdGhpcy5zdG9yZU5hbWUgPSAnYXVkaXQnO1xuICAgIH1cbiAgICBjcmVhdGUoZGIpIHtcbiAgICAgICAgY29uc3Qgc3RvcmUgPSBkYi5jcmVhdGVPYmplY3RTdG9yZSh0aGlzLnN0b3JlTmFtZSwgeyBrZXlQYXRoOiAnaWQnIH0pO1xuICAgICAgICBzdG9yZS5jcmVhdGVJbmRleCgnc3luY1N0YXR1cycsICdzeW5jU3RhdHVzJywgeyB1bmlxdWU6IGZhbHNlIH0pO1xuICAgICAgICBzdG9yZS5jcmVhdGVJbmRleCgnc3luY2VkJywgJ3N5bmNlZCcsIHsgdW5pcXVlOiBmYWxzZSB9KTtcbiAgICAgICAgc3RvcmUuY3JlYXRlSW5kZXgoJ2VudGl0eUlkJywgJ2VudGl0eUlkJywgeyB1bmlxdWU6IGZhbHNlIH0pO1xuICAgICAgICBzdG9yZS5jcmVhdGVJbmRleCgndGltZXN0YW1wJywgJ3RpbWVzdGFtcCcsIHsgdW5pcXVlOiBmYWxzZSB9KTtcbiAgICB9XG59XG4iLCAiaW1wb3J0IHsgQmFzZUVudGl0eVNlcnZpY2UgfSBmcm9tICcuLi9CYXNlRW50aXR5U2VydmljZSc7XG5pbXBvcnQgeyBDb3JlRXZlbnRzIH0gZnJvbSAnLi4vLi4vY29uc3RhbnRzL0NvcmVFdmVudHMnO1xuLyoqXG4gKiBBdWRpdFNlcnZpY2UgLSBFbnRpdHkgc2VydmljZSBmb3IgYXVkaXQgZW50cmllc1xuICpcbiAqIFJFU1BPTlNJQklMSVRJRVM6XG4gKiAtIFN0b3JlIGF1ZGl0IGVudHJpZXMgaW4gSW5kZXhlZERCXG4gKiAtIExpc3RlbiBmb3IgRU5USVRZX1NBVkVEL0VOVElUWV9ERUxFVEVEIGV2ZW50c1xuICogLSBDcmVhdGUgYXVkaXQgZW50cmllcyBmb3IgYWxsIGVudGl0eSBjaGFuZ2VzXG4gKiAtIEVtaXQgQVVESVRfTE9HR0VEIGFmdGVyIHNhdmluZyAoZm9yIFN5bmNNYW5hZ2VyIHRvIGxpc3RlbilcbiAqXG4gKiBPVkVSUklERSBQQVRURVJOOlxuICogLSBPdmVycmlkZXMgc2F2ZSgpIHRvIE5PVCBlbWl0IGV2ZW50cyAocHJldmVudHMgaW5maW5pdGUgbG9vcHMpXG4gKiAtIEF1ZGl0U2VydmljZSBzYXZlcyBhdWRpdCBlbnRyaWVzIHdpdGhvdXQgdHJpZ2dlcmluZyBtb3JlIGF1ZGl0c1xuICpcbiAqIEVWRU5UIENIQUlOOlxuICogRW50aXR5IGNoYW5nZSBcdTIxOTIgRU5USVRZX1NBVkVEL0RFTEVURUQgXHUyMTkyIEF1ZGl0U2VydmljZSBcdTIxOTIgQVVESVRfTE9HR0VEIFx1MjE5MiBTeW5jTWFuYWdlclxuICovXG5leHBvcnQgY2xhc3MgQXVkaXRTZXJ2aWNlIGV4dGVuZHMgQmFzZUVudGl0eVNlcnZpY2Uge1xuICAgIGNvbnN0cnVjdG9yKGNvbnRleHQsIGV2ZW50QnVzKSB7XG4gICAgICAgIHN1cGVyKGNvbnRleHQsIGV2ZW50QnVzKTtcbiAgICAgICAgdGhpcy5zdG9yZU5hbWUgPSAnYXVkaXQnO1xuICAgICAgICB0aGlzLmVudGl0eVR5cGUgPSAnQXVkaXQnO1xuICAgICAgICB0aGlzLnNldHVwRXZlbnRMaXN0ZW5lcnMoKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogU2V0dXAgbGlzdGVuZXJzIGZvciBFTlRJVFlfU0FWRUQgYW5kIEVOVElUWV9ERUxFVEVEIGV2ZW50c1xuICAgICAqL1xuICAgIHNldHVwRXZlbnRMaXN0ZW5lcnMoKSB7XG4gICAgICAgIC8vIExpc3RlbiBmb3IgZW50aXR5IHNhdmVzIChjcmVhdGUvdXBkYXRlKVxuICAgICAgICB0aGlzLmV2ZW50QnVzLm9uKENvcmVFdmVudHMuRU5USVRZX1NBVkVELCAoZXZlbnQpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IGRldGFpbCA9IGV2ZW50LmRldGFpbDtcbiAgICAgICAgICAgIHRoaXMuaGFuZGxlRW50aXR5U2F2ZWQoZGV0YWlsKTtcbiAgICAgICAgfSk7XG4gICAgICAgIC8vIExpc3RlbiBmb3IgZW50aXR5IGRlbGV0ZXNcbiAgICAgICAgdGhpcy5ldmVudEJ1cy5vbihDb3JlRXZlbnRzLkVOVElUWV9ERUxFVEVELCAoZXZlbnQpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IGRldGFpbCA9IGV2ZW50LmRldGFpbDtcbiAgICAgICAgICAgIHRoaXMuaGFuZGxlRW50aXR5RGVsZXRlZChkZXRhaWwpO1xuICAgICAgICB9KTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogSGFuZGxlIEVOVElUWV9TQVZFRCBldmVudCAtIGNyZWF0ZSBhdWRpdCBlbnRyeVxuICAgICAqL1xuICAgIGFzeW5jIGhhbmRsZUVudGl0eVNhdmVkKHBheWxvYWQpIHtcbiAgICAgICAgLy8gRG9uJ3QgYXVkaXQgYXVkaXQgZW50cmllcyAocHJldmVudCBpbmZpbml0ZSBsb29wcylcbiAgICAgICAgaWYgKHBheWxvYWQuZW50aXR5VHlwZSA9PT0gJ0F1ZGl0JylcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgY29uc3QgYXVkaXRFbnRyeSA9IHtcbiAgICAgICAgICAgIGlkOiBjcnlwdG8ucmFuZG9tVVVJRCgpLFxuICAgICAgICAgICAgZW50aXR5VHlwZTogcGF5bG9hZC5lbnRpdHlUeXBlLFxuICAgICAgICAgICAgZW50aXR5SWQ6IHBheWxvYWQuZW50aXR5SWQsXG4gICAgICAgICAgICBvcGVyYXRpb246IHBheWxvYWQub3BlcmF0aW9uLFxuICAgICAgICAgICAgdXNlcklkOiBBdWRpdFNlcnZpY2UuREVGQVVMVF9VU0VSX0lELFxuICAgICAgICAgICAgdGltZXN0YW1wOiBwYXlsb2FkLnRpbWVzdGFtcCxcbiAgICAgICAgICAgIGNoYW5nZXM6IHBheWxvYWQuY2hhbmdlcyxcbiAgICAgICAgICAgIHN5bmNlZDogZmFsc2UsXG4gICAgICAgICAgICBzeW5jU3RhdHVzOiAncGVuZGluZydcbiAgICAgICAgfTtcbiAgICAgICAgYXdhaXQgdGhpcy5zYXZlKGF1ZGl0RW50cnkpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBIYW5kbGUgRU5USVRZX0RFTEVURUQgZXZlbnQgLSBjcmVhdGUgYXVkaXQgZW50cnlcbiAgICAgKi9cbiAgICBhc3luYyBoYW5kbGVFbnRpdHlEZWxldGVkKHBheWxvYWQpIHtcbiAgICAgICAgLy8gRG9uJ3QgYXVkaXQgYXVkaXQgZW50cmllcyAocHJldmVudCBpbmZpbml0ZSBsb29wcylcbiAgICAgICAgaWYgKHBheWxvYWQuZW50aXR5VHlwZSA9PT0gJ0F1ZGl0JylcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgY29uc3QgYXVkaXRFbnRyeSA9IHtcbiAgICAgICAgICAgIGlkOiBjcnlwdG8ucmFuZG9tVVVJRCgpLFxuICAgICAgICAgICAgZW50aXR5VHlwZTogcGF5bG9hZC5lbnRpdHlUeXBlLFxuICAgICAgICAgICAgZW50aXR5SWQ6IHBheWxvYWQuZW50aXR5SWQsXG4gICAgICAgICAgICBvcGVyYXRpb246ICdkZWxldGUnLFxuICAgICAgICAgICAgdXNlcklkOiBBdWRpdFNlcnZpY2UuREVGQVVMVF9VU0VSX0lELFxuICAgICAgICAgICAgdGltZXN0YW1wOiBwYXlsb2FkLnRpbWVzdGFtcCxcbiAgICAgICAgICAgIGNoYW5nZXM6IHsgaWQ6IHBheWxvYWQuZW50aXR5SWQgfSwgLy8gRm9yIGRlbGV0ZSwganVzdCBzdG9yZSB0aGUgSURcbiAgICAgICAgICAgIHN5bmNlZDogZmFsc2UsXG4gICAgICAgICAgICBzeW5jU3RhdHVzOiAncGVuZGluZydcbiAgICAgICAgfTtcbiAgICAgICAgYXdhaXQgdGhpcy5zYXZlKGF1ZGl0RW50cnkpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBPdmVycmlkZSBzYXZlIHRvIE5PVCB0cmlnZ2VyIEVOVElUWV9TQVZFRCBldmVudFxuICAgICAqIEluc3RlYWQsIGVtaXRzIEFVRElUX0xPR0dFRCBmb3IgU3luY01hbmFnZXIgdG8gbGlzdGVuXG4gICAgICpcbiAgICAgKiBUaGlzIHByZXZlbnRzIGluZmluaXRlIGxvb3BzOlxuICAgICAqIC0gQmFzZUVudGl0eVNlcnZpY2Uuc2F2ZSgpIGVtaXRzIEVOVElUWV9TQVZFRFxuICAgICAqIC0gQXVkaXRTZXJ2aWNlIGxpc3RlbnMgdG8gRU5USVRZX1NBVkVEIGFuZCBjcmVhdGVzIGF1ZGl0XG4gICAgICogLSBJZiBBdWRpdFNlcnZpY2Uuc2F2ZSgpIGFsc28gZW1pdHRlZCBFTlRJVFlfU0FWRUQsIGl0IHdvdWxkIGxvb3BcbiAgICAgKi9cbiAgICBhc3luYyBzYXZlKGVudGl0eSkge1xuICAgICAgICBjb25zdCBzZXJpYWxpemVkID0gdGhpcy5zZXJpYWxpemUoZW50aXR5KTtcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHRyYW5zYWN0aW9uID0gdGhpcy5kYi50cmFuc2FjdGlvbihbdGhpcy5zdG9yZU5hbWVdLCAncmVhZHdyaXRlJyk7XG4gICAgICAgICAgICBjb25zdCBzdG9yZSA9IHRyYW5zYWN0aW9uLm9iamVjdFN0b3JlKHRoaXMuc3RvcmVOYW1lKTtcbiAgICAgICAgICAgIGNvbnN0IHJlcXVlc3QgPSBzdG9yZS5wdXQoc2VyaWFsaXplZCk7XG4gICAgICAgICAgICByZXF1ZXN0Lm9uc3VjY2VzcyA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICAvLyBFbWl0IEFVRElUX0xPR0dFRCBpbnN0ZWFkIG9mIEVOVElUWV9TQVZFRFxuICAgICAgICAgICAgICAgIGNvbnN0IHBheWxvYWQgPSB7XG4gICAgICAgICAgICAgICAgICAgIGF1ZGl0SWQ6IGVudGl0eS5pZCxcbiAgICAgICAgICAgICAgICAgICAgZW50aXR5VHlwZTogZW50aXR5LmVudGl0eVR5cGUsXG4gICAgICAgICAgICAgICAgICAgIGVudGl0eUlkOiBlbnRpdHkuZW50aXR5SWQsXG4gICAgICAgICAgICAgICAgICAgIG9wZXJhdGlvbjogZW50aXR5Lm9wZXJhdGlvbixcbiAgICAgICAgICAgICAgICAgICAgdGltZXN0YW1wOiBlbnRpdHkudGltZXN0YW1wXG4gICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICB0aGlzLmV2ZW50QnVzLmVtaXQoQ29yZUV2ZW50cy5BVURJVF9MT0dHRUQsIHBheWxvYWQpO1xuICAgICAgICAgICAgICAgIHJlc29sdmUoKTtcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICByZXF1ZXN0Lm9uZXJyb3IgPSAoKSA9PiB7XG4gICAgICAgICAgICAgICAgcmVqZWN0KG5ldyBFcnJvcihgRmFpbGVkIHRvIHNhdmUgYXVkaXQgZW50cnkgJHtlbnRpdHkuaWR9OiAke3JlcXVlc3QuZXJyb3J9YCkpO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgfSk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIE92ZXJyaWRlIGRlbGV0ZSB0byBOT1QgdHJpZ2dlciBFTlRJVFlfREVMRVRFRCBldmVudFxuICAgICAqIEF1ZGl0IGVudHJpZXMgc2hvdWxkIG5ldmVyIGJlIGRlbGV0ZWQgKGNvbXBsaWFuY2UgcmVxdWlyZW1lbnQpXG4gICAgICovXG4gICAgYXN5bmMgZGVsZXRlKF9pZCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0F1ZGl0IGVudHJpZXMgY2Fubm90IGJlIGRlbGV0ZWQgKGNvbXBsaWFuY2UgcmVxdWlyZW1lbnQpJyk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEdldCBwZW5kaW5nIGF1ZGl0IGVudHJpZXMgKGZvciBzeW5jKVxuICAgICAqL1xuICAgIGFzeW5jIGdldFBlbmRpbmdBdWRpdHMoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmdldEJ5U3luY1N0YXR1cygncGVuZGluZycpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBHZXQgYXVkaXQgZW50cmllcyBmb3IgYSBzcGVjaWZpYyBlbnRpdHlcbiAgICAgKi9cbiAgICBhc3luYyBnZXRCeUVudGl0eUlkKGVudGl0eUlkKSB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgICAgICBjb25zdCB0cmFuc2FjdGlvbiA9IHRoaXMuZGIudHJhbnNhY3Rpb24oW3RoaXMuc3RvcmVOYW1lXSwgJ3JlYWRvbmx5Jyk7XG4gICAgICAgICAgICBjb25zdCBzdG9yZSA9IHRyYW5zYWN0aW9uLm9iamVjdFN0b3JlKHRoaXMuc3RvcmVOYW1lKTtcbiAgICAgICAgICAgIGNvbnN0IGluZGV4ID0gc3RvcmUuaW5kZXgoJ2VudGl0eUlkJyk7XG4gICAgICAgICAgICBjb25zdCByZXF1ZXN0ID0gaW5kZXguZ2V0QWxsKGVudGl0eUlkKTtcbiAgICAgICAgICAgIHJlcXVlc3Qub25zdWNjZXNzID0gKCkgPT4ge1xuICAgICAgICAgICAgICAgIGNvbnN0IGVudHJpZXMgPSByZXF1ZXN0LnJlc3VsdDtcbiAgICAgICAgICAgICAgICByZXNvbHZlKGVudHJpZXMpO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIHJlcXVlc3Qub25lcnJvciA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICByZWplY3QobmV3IEVycm9yKGBGYWlsZWQgdG8gZ2V0IGF1ZGl0IGVudHJpZXMgZm9yIGVudGl0eSAke2VudGl0eUlkfTogJHtyZXF1ZXN0LmVycm9yfWApKTtcbiAgICAgICAgICAgIH07XG4gICAgICAgIH0pO1xuICAgIH1cbn1cbi8vIEhhcmRjb2RlZCB1c2VySWQgZm9yIG5vdyAtIHdpbGwgY29tZSBmcm9tIHNlc3Npb24gbGF0ZXJcbkF1ZGl0U2VydmljZS5ERUZBVUxUX1VTRVJfSUQgPSAnMDAwMDAwMDAtMDAwMC0wMDAwLTAwMDAtMDAwMDAwMDAwMDAxJztcbiIsICIvKipcbiAqIE1vY2tFdmVudFJlcG9zaXRvcnkgLSBMb2FkcyBldmVudCBkYXRhIGZyb20gbG9jYWwgSlNPTiBmaWxlXG4gKlxuICogVXNlZCBmb3IgZGV2ZWxvcG1lbnQgYW5kIHRlc3RpbmcuIE9ubHkgZmV0Y2hBbGwoKSBpcyBpbXBsZW1lbnRlZC5cbiAqL1xuZXhwb3J0IGNsYXNzIE1vY2tFdmVudFJlcG9zaXRvcnkge1xuICAgIGNvbnN0cnVjdG9yKCkge1xuICAgICAgICB0aGlzLmVudGl0eVR5cGUgPSAnRXZlbnQnO1xuICAgICAgICB0aGlzLmRhdGFVcmwgPSAnZGF0YS9tb2NrLWV2ZW50cy5qc29uJztcbiAgICB9XG4gICAgLyoqXG4gICAgICogRmV0Y2ggYWxsIGV2ZW50cyBmcm9tIG1vY2sgSlNPTiBmaWxlXG4gICAgICovXG4gICAgYXN5bmMgZmV0Y2hBbGwoKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGZldGNoKHRoaXMuZGF0YVVybCk7XG4gICAgICAgICAgICBpZiAoIXJlc3BvbnNlLm9rKSB7XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBGYWlsZWQgdG8gbG9hZCBtb2NrIGV2ZW50czogJHtyZXNwb25zZS5zdGF0dXN9ICR7cmVzcG9uc2Uuc3RhdHVzVGV4dH1gKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNvbnN0IHJhd0RhdGEgPSBhd2FpdCByZXNwb25zZS5qc29uKCk7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5wcm9jZXNzQ2FsZW5kYXJEYXRhKHJhd0RhdGEpO1xuICAgICAgICB9XG4gICAgICAgIGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgY29uc29sZS5lcnJvcignRmFpbGVkIHRvIGxvYWQgZXZlbnQgZGF0YTonLCBlcnJvcik7XG4gICAgICAgICAgICB0aHJvdyBlcnJvcjtcbiAgICAgICAgfVxuICAgIH1cbiAgICBhc3luYyBzZW5kQ3JlYXRlKF9ldmVudCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ01vY2tFdmVudFJlcG9zaXRvcnkgZG9lcyBub3Qgc3VwcG9ydCBzZW5kQ3JlYXRlLiBNb2NrIGRhdGEgaXMgcmVhZC1vbmx5LicpO1xuICAgIH1cbiAgICBhc3luYyBzZW5kVXBkYXRlKF9pZCwgX3VwZGF0ZXMpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdNb2NrRXZlbnRSZXBvc2l0b3J5IGRvZXMgbm90IHN1cHBvcnQgc2VuZFVwZGF0ZS4gTW9jayBkYXRhIGlzIHJlYWQtb25seS4nKTtcbiAgICB9XG4gICAgYXN5bmMgc2VuZERlbGV0ZShfaWQpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdNb2NrRXZlbnRSZXBvc2l0b3J5IGRvZXMgbm90IHN1cHBvcnQgc2VuZERlbGV0ZS4gTW9jayBkYXRhIGlzIHJlYWQtb25seS4nKTtcbiAgICB9XG4gICAgcHJvY2Vzc0NhbGVuZGFyRGF0YShkYXRhKSB7XG4gICAgICAgIHJldHVybiBkYXRhLm1hcCgoZXZlbnQpID0+IHtcbiAgICAgICAgICAgIC8vIFZhbGlkYXRlIGN1c3RvbWVyIGV2ZW50IGNvbnN0cmFpbnRzXG4gICAgICAgICAgICBpZiAoZXZlbnQudHlwZSA9PT0gJ2N1c3RvbWVyJykge1xuICAgICAgICAgICAgICAgIGlmICghZXZlbnQuYm9va2luZ0lkKVxuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLndhcm4oYEN1c3RvbWVyIGV2ZW50ICR7ZXZlbnQuaWR9IG1pc3NpbmcgYm9va2luZ0lkYCk7XG4gICAgICAgICAgICAgICAgaWYgKCFldmVudC5yZXNvdXJjZUlkKVxuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLndhcm4oYEN1c3RvbWVyIGV2ZW50ICR7ZXZlbnQuaWR9IG1pc3NpbmcgcmVzb3VyY2VJZGApO1xuICAgICAgICAgICAgICAgIGlmICghZXZlbnQuY3VzdG9tZXJJZClcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS53YXJuKGBDdXN0b21lciBldmVudCAke2V2ZW50LmlkfSBtaXNzaW5nIGN1c3RvbWVySWRgKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgICAgaWQ6IGV2ZW50LmlkLFxuICAgICAgICAgICAgICAgIHRpdGxlOiBldmVudC50aXRsZSxcbiAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogZXZlbnQuZGVzY3JpcHRpb24sXG4gICAgICAgICAgICAgICAgc3RhcnQ6IG5ldyBEYXRlKGV2ZW50LnN0YXJ0KSxcbiAgICAgICAgICAgICAgICBlbmQ6IG5ldyBEYXRlKGV2ZW50LmVuZCksXG4gICAgICAgICAgICAgICAgdHlwZTogZXZlbnQudHlwZSxcbiAgICAgICAgICAgICAgICBhbGxEYXk6IGV2ZW50LmFsbERheSB8fCBmYWxzZSxcbiAgICAgICAgICAgICAgICBib29raW5nSWQ6IGV2ZW50LmJvb2tpbmdJZCxcbiAgICAgICAgICAgICAgICByZXNvdXJjZUlkOiBldmVudC5yZXNvdXJjZUlkLFxuICAgICAgICAgICAgICAgIGN1c3RvbWVySWQ6IGV2ZW50LmN1c3RvbWVySWQsXG4gICAgICAgICAgICAgICAgcmVjdXJyaW5nSWQ6IGV2ZW50LnJlY3VycmluZ0lkLFxuICAgICAgICAgICAgICAgIG1ldGFkYXRhOiBldmVudC5tZXRhZGF0YSxcbiAgICAgICAgICAgICAgICBzeW5jU3RhdHVzOiAnc3luY2VkJ1xuICAgICAgICAgICAgfTtcbiAgICAgICAgfSk7XG4gICAgfVxufVxuIiwgIi8qKlxuICogTW9ja1Jlc291cmNlUmVwb3NpdG9yeSAtIExvYWRzIHJlc291cmNlIGRhdGEgZnJvbSBsb2NhbCBKU09OIGZpbGVcbiAqL1xuZXhwb3J0IGNsYXNzIE1vY2tSZXNvdXJjZVJlcG9zaXRvcnkge1xuICAgIGNvbnN0cnVjdG9yKCkge1xuICAgICAgICB0aGlzLmVudGl0eVR5cGUgPSAnUmVzb3VyY2UnO1xuICAgICAgICB0aGlzLmRhdGFVcmwgPSAnZGF0YS9tb2NrLXJlc291cmNlcy5qc29uJztcbiAgICB9XG4gICAgYXN5bmMgZmV0Y2hBbGwoKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGZldGNoKHRoaXMuZGF0YVVybCk7XG4gICAgICAgICAgICBpZiAoIXJlc3BvbnNlLm9rKSB7XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBGYWlsZWQgdG8gbG9hZCBtb2NrIHJlc291cmNlczogJHtyZXNwb25zZS5zdGF0dXN9ICR7cmVzcG9uc2Uuc3RhdHVzVGV4dH1gKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNvbnN0IHJhd0RhdGEgPSBhd2FpdCByZXNwb25zZS5qc29uKCk7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5wcm9jZXNzUmVzb3VyY2VEYXRhKHJhd0RhdGEpO1xuICAgICAgICB9XG4gICAgICAgIGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgY29uc29sZS5lcnJvcignRmFpbGVkIHRvIGxvYWQgcmVzb3VyY2UgZGF0YTonLCBlcnJvcik7XG4gICAgICAgICAgICB0aHJvdyBlcnJvcjtcbiAgICAgICAgfVxuICAgIH1cbiAgICBhc3luYyBzZW5kQ3JlYXRlKF9yZXNvdXJjZSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ01vY2tSZXNvdXJjZVJlcG9zaXRvcnkgZG9lcyBub3Qgc3VwcG9ydCBzZW5kQ3JlYXRlLiBNb2NrIGRhdGEgaXMgcmVhZC1vbmx5LicpO1xuICAgIH1cbiAgICBhc3luYyBzZW5kVXBkYXRlKF9pZCwgX3VwZGF0ZXMpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdNb2NrUmVzb3VyY2VSZXBvc2l0b3J5IGRvZXMgbm90IHN1cHBvcnQgc2VuZFVwZGF0ZS4gTW9jayBkYXRhIGlzIHJlYWQtb25seS4nKTtcbiAgICB9XG4gICAgYXN5bmMgc2VuZERlbGV0ZShfaWQpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdNb2NrUmVzb3VyY2VSZXBvc2l0b3J5IGRvZXMgbm90IHN1cHBvcnQgc2VuZERlbGV0ZS4gTW9jayBkYXRhIGlzIHJlYWQtb25seS4nKTtcbiAgICB9XG4gICAgcHJvY2Vzc1Jlc291cmNlRGF0YShkYXRhKSB7XG4gICAgICAgIHJldHVybiBkYXRhLm1hcCgocmVzb3VyY2UpID0+ICh7XG4gICAgICAgICAgICBpZDogcmVzb3VyY2UuaWQsXG4gICAgICAgICAgICBuYW1lOiByZXNvdXJjZS5uYW1lLFxuICAgICAgICAgICAgZGlzcGxheU5hbWU6IHJlc291cmNlLmRpc3BsYXlOYW1lLFxuICAgICAgICAgICAgdHlwZTogcmVzb3VyY2UudHlwZSxcbiAgICAgICAgICAgIGF2YXRhclVybDogcmVzb3VyY2UuYXZhdGFyVXJsLFxuICAgICAgICAgICAgY29sb3I6IHJlc291cmNlLmNvbG9yLFxuICAgICAgICAgICAgaXNBY3RpdmU6IHJlc291cmNlLmlzQWN0aXZlLFxuICAgICAgICAgICAgZGVmYXVsdFNjaGVkdWxlOiByZXNvdXJjZS5kZWZhdWx0U2NoZWR1bGUsXG4gICAgICAgICAgICBtZXRhZGF0YTogcmVzb3VyY2UubWV0YWRhdGEsXG4gICAgICAgICAgICBzeW5jU3RhdHVzOiAnc3luY2VkJ1xuICAgICAgICB9KSk7XG4gICAgfVxufVxuIiwgIi8qKlxuICogTW9ja0Jvb2tpbmdSZXBvc2l0b3J5IC0gTG9hZHMgYm9va2luZyBkYXRhIGZyb20gbG9jYWwgSlNPTiBmaWxlXG4gKi9cbmV4cG9ydCBjbGFzcyBNb2NrQm9va2luZ1JlcG9zaXRvcnkge1xuICAgIGNvbnN0cnVjdG9yKCkge1xuICAgICAgICB0aGlzLmVudGl0eVR5cGUgPSAnQm9va2luZyc7XG4gICAgICAgIHRoaXMuZGF0YVVybCA9ICdkYXRhL21vY2stYm9va2luZ3MuanNvbic7XG4gICAgfVxuICAgIGFzeW5jIGZldGNoQWxsKCkge1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBmZXRjaCh0aGlzLmRhdGFVcmwpO1xuICAgICAgICAgICAgaWYgKCFyZXNwb25zZS5vaykge1xuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgRmFpbGVkIHRvIGxvYWQgbW9jayBib29raW5nczogJHtyZXNwb25zZS5zdGF0dXN9ICR7cmVzcG9uc2Uuc3RhdHVzVGV4dH1gKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNvbnN0IHJhd0RhdGEgPSBhd2FpdCByZXNwb25zZS5qc29uKCk7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5wcm9jZXNzQm9va2luZ0RhdGEocmF3RGF0YSk7XG4gICAgICAgIH1cbiAgICAgICAgY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKCdGYWlsZWQgdG8gbG9hZCBib29raW5nIGRhdGE6JywgZXJyb3IpO1xuICAgICAgICAgICAgdGhyb3cgZXJyb3I7XG4gICAgICAgIH1cbiAgICB9XG4gICAgYXN5bmMgc2VuZENyZWF0ZShfYm9va2luZykge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ01vY2tCb29raW5nUmVwb3NpdG9yeSBkb2VzIG5vdCBzdXBwb3J0IHNlbmRDcmVhdGUuIE1vY2sgZGF0YSBpcyByZWFkLW9ubHkuJyk7XG4gICAgfVxuICAgIGFzeW5jIHNlbmRVcGRhdGUoX2lkLCBfdXBkYXRlcykge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ01vY2tCb29raW5nUmVwb3NpdG9yeSBkb2VzIG5vdCBzdXBwb3J0IHNlbmRVcGRhdGUuIE1vY2sgZGF0YSBpcyByZWFkLW9ubHkuJyk7XG4gICAgfVxuICAgIGFzeW5jIHNlbmREZWxldGUoX2lkKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignTW9ja0Jvb2tpbmdSZXBvc2l0b3J5IGRvZXMgbm90IHN1cHBvcnQgc2VuZERlbGV0ZS4gTW9jayBkYXRhIGlzIHJlYWQtb25seS4nKTtcbiAgICB9XG4gICAgcHJvY2Vzc0Jvb2tpbmdEYXRhKGRhdGEpIHtcbiAgICAgICAgcmV0dXJuIGRhdGEubWFwKChib29raW5nKSA9PiAoe1xuICAgICAgICAgICAgaWQ6IGJvb2tpbmcuaWQsXG4gICAgICAgICAgICBjdXN0b21lcklkOiBib29raW5nLmN1c3RvbWVySWQsXG4gICAgICAgICAgICBzdGF0dXM6IGJvb2tpbmcuc3RhdHVzLFxuICAgICAgICAgICAgY3JlYXRlZEF0OiBuZXcgRGF0ZShib29raW5nLmNyZWF0ZWRBdCksXG4gICAgICAgICAgICBzZXJ2aWNlczogYm9va2luZy5zZXJ2aWNlcyxcbiAgICAgICAgICAgIHRvdGFsUHJpY2U6IGJvb2tpbmcudG90YWxQcmljZSxcbiAgICAgICAgICAgIHRhZ3M6IGJvb2tpbmcudGFncyxcbiAgICAgICAgICAgIG5vdGVzOiBib29raW5nLm5vdGVzLFxuICAgICAgICAgICAgc3luY1N0YXR1czogJ3N5bmNlZCdcbiAgICAgICAgfSkpO1xuICAgIH1cbn1cbiIsICIvKipcbiAqIE1vY2tDdXN0b21lclJlcG9zaXRvcnkgLSBMb2FkcyBjdXN0b21lciBkYXRhIGZyb20gbG9jYWwgSlNPTiBmaWxlXG4gKi9cbmV4cG9ydCBjbGFzcyBNb2NrQ3VzdG9tZXJSZXBvc2l0b3J5IHtcbiAgICBjb25zdHJ1Y3RvcigpIHtcbiAgICAgICAgdGhpcy5lbnRpdHlUeXBlID0gJ0N1c3RvbWVyJztcbiAgICAgICAgdGhpcy5kYXRhVXJsID0gJ2RhdGEvbW9jay1jdXN0b21lcnMuanNvbic7XG4gICAgfVxuICAgIGFzeW5jIGZldGNoQWxsKCkge1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBmZXRjaCh0aGlzLmRhdGFVcmwpO1xuICAgICAgICAgICAgaWYgKCFyZXNwb25zZS5vaykge1xuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgRmFpbGVkIHRvIGxvYWQgbW9jayBjdXN0b21lcnM6ICR7cmVzcG9uc2Uuc3RhdHVzfSAke3Jlc3BvbnNlLnN0YXR1c1RleHR9YCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBjb25zdCByYXdEYXRhID0gYXdhaXQgcmVzcG9uc2UuanNvbigpO1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMucHJvY2Vzc0N1c3RvbWVyRGF0YShyYXdEYXRhKTtcbiAgICAgICAgfVxuICAgICAgICBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ0ZhaWxlZCB0byBsb2FkIGN1c3RvbWVyIGRhdGE6JywgZXJyb3IpO1xuICAgICAgICAgICAgdGhyb3cgZXJyb3I7XG4gICAgICAgIH1cbiAgICB9XG4gICAgYXN5bmMgc2VuZENyZWF0ZShfY3VzdG9tZXIpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdNb2NrQ3VzdG9tZXJSZXBvc2l0b3J5IGRvZXMgbm90IHN1cHBvcnQgc2VuZENyZWF0ZS4gTW9jayBkYXRhIGlzIHJlYWQtb25seS4nKTtcbiAgICB9XG4gICAgYXN5bmMgc2VuZFVwZGF0ZShfaWQsIF91cGRhdGVzKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignTW9ja0N1c3RvbWVyUmVwb3NpdG9yeSBkb2VzIG5vdCBzdXBwb3J0IHNlbmRVcGRhdGUuIE1vY2sgZGF0YSBpcyByZWFkLW9ubHkuJyk7XG4gICAgfVxuICAgIGFzeW5jIHNlbmREZWxldGUoX2lkKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignTW9ja0N1c3RvbWVyUmVwb3NpdG9yeSBkb2VzIG5vdCBzdXBwb3J0IHNlbmREZWxldGUuIE1vY2sgZGF0YSBpcyByZWFkLW9ubHkuJyk7XG4gICAgfVxuICAgIHByb2Nlc3NDdXN0b21lckRhdGEoZGF0YSkge1xuICAgICAgICByZXR1cm4gZGF0YS5tYXAoKGN1c3RvbWVyKSA9PiAoe1xuICAgICAgICAgICAgaWQ6IGN1c3RvbWVyLmlkLFxuICAgICAgICAgICAgbmFtZTogY3VzdG9tZXIubmFtZSxcbiAgICAgICAgICAgIHBob25lOiBjdXN0b21lci5waG9uZSxcbiAgICAgICAgICAgIGVtYWlsOiBjdXN0b21lci5lbWFpbCxcbiAgICAgICAgICAgIG1ldGFkYXRhOiBjdXN0b21lci5tZXRhZGF0YSxcbiAgICAgICAgICAgIHN5bmNTdGF0dXM6ICdzeW5jZWQnXG4gICAgICAgIH0pKTtcbiAgICB9XG59XG4iLCAiLyoqXG4gKiBNb2NrQXVkaXRSZXBvc2l0b3J5IC0gTW9jayBBUEkgcmVwb3NpdG9yeSBmb3IgYXVkaXQgZW50cmllc1xuICpcbiAqIEluIHByb2R1Y3Rpb24sIHRoaXMgd291bGQgc2VuZCBhdWRpdCBlbnRyaWVzIHRvIHRoZSBiYWNrZW5kLlxuICogRm9yIGRldmVsb3BtZW50L3Rlc3RpbmcsIGl0IGp1c3QgbG9ncyB0aGUgb3BlcmF0aW9ucy5cbiAqL1xuZXhwb3J0IGNsYXNzIE1vY2tBdWRpdFJlcG9zaXRvcnkge1xuICAgIGNvbnN0cnVjdG9yKCkge1xuICAgICAgICB0aGlzLmVudGl0eVR5cGUgPSAnQXVkaXQnO1xuICAgIH1cbiAgICBhc3luYyBzZW5kQ3JlYXRlKGVudGl0eSkge1xuICAgICAgICAvLyBTaW11bGF0ZSBBUEkgY2FsbCBkZWxheVxuICAgICAgICBhd2FpdCBuZXcgUHJvbWlzZShyZXNvbHZlID0+IHNldFRpbWVvdXQocmVzb2x2ZSwgMTAwKSk7XG4gICAgICAgIGNvbnNvbGUubG9nKCdNb2NrQXVkaXRSZXBvc2l0b3J5OiBBdWRpdCBlbnRyeSBzeW5jZWQgdG8gYmFja2VuZDonLCB7XG4gICAgICAgICAgICBpZDogZW50aXR5LmlkLFxuICAgICAgICAgICAgZW50aXR5VHlwZTogZW50aXR5LmVudGl0eVR5cGUsXG4gICAgICAgICAgICBlbnRpdHlJZDogZW50aXR5LmVudGl0eUlkLFxuICAgICAgICAgICAgb3BlcmF0aW9uOiBlbnRpdHkub3BlcmF0aW9uLFxuICAgICAgICAgICAgdGltZXN0YW1wOiBuZXcgRGF0ZShlbnRpdHkudGltZXN0YW1wKS50b0lTT1N0cmluZygpXG4gICAgICAgIH0pO1xuICAgICAgICByZXR1cm4gZW50aXR5O1xuICAgIH1cbiAgICBhc3luYyBzZW5kVXBkYXRlKF9pZCwgX2VudGl0eSkge1xuICAgICAgICAvLyBBdWRpdCBlbnRyaWVzIGFyZSBpbW11dGFibGUgLSB1cGRhdGVzIHNob3VsZCBub3QgaGFwcGVuXG4gICAgICAgIHRocm93IG5ldyBFcnJvcignQXVkaXQgZW50cmllcyBjYW5ub3QgYmUgdXBkYXRlZCcpO1xuICAgIH1cbiAgICBhc3luYyBzZW5kRGVsZXRlKF9pZCkge1xuICAgICAgICAvLyBBdWRpdCBlbnRyaWVzIHNob3VsZCBuZXZlciBiZSBkZWxldGVkXG4gICAgICAgIHRocm93IG5ldyBFcnJvcignQXVkaXQgZW50cmllcyBjYW5ub3QgYmUgZGVsZXRlZCcpO1xuICAgIH1cbiAgICBhc3luYyBmZXRjaEFsbCgpIHtcbiAgICAgICAgLy8gRm9yIG5vdywgcmV0dXJuIGVtcHR5IGFycmF5IC0gYXVkaXQgZW50cmllcyBhcmUgbG9jYWwtZmlyc3RcbiAgICAgICAgLy8gSW4gcHJvZHVjdGlvbiwgdGhpcyBjb3VsZCBmZXRjaCBhdWRpdCBoaXN0b3J5IGZyb20gYmFja2VuZFxuICAgICAgICByZXR1cm4gW107XG4gICAgfVxuICAgIGFzeW5jIGZldGNoQnlJZChfaWQpIHtcbiAgICAgICAgLy8gRm9yIG5vdywgcmV0dXJuIG51bGwgLSBhdWRpdCBlbnRyaWVzIGFyZSBsb2NhbC1maXJzdFxuICAgICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG59XG4iLCAiLyoqXG4gKiBNb2NrVGVhbVJlcG9zaXRvcnkgLSBMb2FkcyB0ZWFtIGRhdGEgZnJvbSBsb2NhbCBKU09OIGZpbGVcbiAqL1xuZXhwb3J0IGNsYXNzIE1vY2tUZWFtUmVwb3NpdG9yeSB7XG4gICAgY29uc3RydWN0b3IoKSB7XG4gICAgICAgIHRoaXMuZW50aXR5VHlwZSA9ICdUZWFtJztcbiAgICAgICAgdGhpcy5kYXRhVXJsID0gJ2RhdGEvbW9jay10ZWFtcy5qc29uJztcbiAgICB9XG4gICAgYXN5bmMgZmV0Y2hBbGwoKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGZldGNoKHRoaXMuZGF0YVVybCk7XG4gICAgICAgICAgICBpZiAoIXJlc3BvbnNlLm9rKSB7XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBGYWlsZWQgdG8gbG9hZCBtb2NrIHRlYW1zOiAke3Jlc3BvbnNlLnN0YXR1c30gJHtyZXNwb25zZS5zdGF0dXNUZXh0fWApO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgY29uc3QgcmF3RGF0YSA9IGF3YWl0IHJlc3BvbnNlLmpzb24oKTtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLnByb2Nlc3NUZWFtRGF0YShyYXdEYXRhKTtcbiAgICAgICAgfVxuICAgICAgICBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ0ZhaWxlZCB0byBsb2FkIHRlYW0gZGF0YTonLCBlcnJvcik7XG4gICAgICAgICAgICB0aHJvdyBlcnJvcjtcbiAgICAgICAgfVxuICAgIH1cbiAgICBhc3luYyBzZW5kQ3JlYXRlKF90ZWFtKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignTW9ja1RlYW1SZXBvc2l0b3J5IGRvZXMgbm90IHN1cHBvcnQgc2VuZENyZWF0ZS4gTW9jayBkYXRhIGlzIHJlYWQtb25seS4nKTtcbiAgICB9XG4gICAgYXN5bmMgc2VuZFVwZGF0ZShfaWQsIF91cGRhdGVzKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignTW9ja1RlYW1SZXBvc2l0b3J5IGRvZXMgbm90IHN1cHBvcnQgc2VuZFVwZGF0ZS4gTW9jayBkYXRhIGlzIHJlYWQtb25seS4nKTtcbiAgICB9XG4gICAgYXN5bmMgc2VuZERlbGV0ZShfaWQpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdNb2NrVGVhbVJlcG9zaXRvcnkgZG9lcyBub3Qgc3VwcG9ydCBzZW5kRGVsZXRlLiBNb2NrIGRhdGEgaXMgcmVhZC1vbmx5LicpO1xuICAgIH1cbiAgICBwcm9jZXNzVGVhbURhdGEoZGF0YSkge1xuICAgICAgICByZXR1cm4gZGF0YS5tYXAoKHRlYW0pID0+ICh7XG4gICAgICAgICAgICBpZDogdGVhbS5pZCxcbiAgICAgICAgICAgIG5hbWU6IHRlYW0ubmFtZSxcbiAgICAgICAgICAgIHJlc291cmNlSWRzOiB0ZWFtLnJlc291cmNlSWRzLFxuICAgICAgICAgICAgc3luY1N0YXR1czogJ3N5bmNlZCdcbiAgICAgICAgfSkpO1xuICAgIH1cbn1cbiIsICIvKipcbiAqIE1vY2tEZXBhcnRtZW50UmVwb3NpdG9yeSAtIExvYWRzIGRlcGFydG1lbnQgZGF0YSBmcm9tIGxvY2FsIEpTT04gZmlsZVxuICovXG5leHBvcnQgY2xhc3MgTW9ja0RlcGFydG1lbnRSZXBvc2l0b3J5IHtcbiAgICBjb25zdHJ1Y3RvcigpIHtcbiAgICAgICAgdGhpcy5lbnRpdHlUeXBlID0gJ0RlcGFydG1lbnQnO1xuICAgICAgICB0aGlzLmRhdGFVcmwgPSAnZGF0YS9tb2NrLWRlcGFydG1lbnRzLmpzb24nO1xuICAgIH1cbiAgICBhc3luYyBmZXRjaEFsbCgpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZmV0Y2godGhpcy5kYXRhVXJsKTtcbiAgICAgICAgICAgIGlmICghcmVzcG9uc2Uub2spIHtcbiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEZhaWxlZCB0byBsb2FkIG1vY2sgZGVwYXJ0bWVudHM6ICR7cmVzcG9uc2Uuc3RhdHVzfSAke3Jlc3BvbnNlLnN0YXR1c1RleHR9YCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBjb25zdCByYXdEYXRhID0gYXdhaXQgcmVzcG9uc2UuanNvbigpO1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMucHJvY2Vzc0RlcGFydG1lbnREYXRhKHJhd0RhdGEpO1xuICAgICAgICB9XG4gICAgICAgIGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgY29uc29sZS5lcnJvcignRmFpbGVkIHRvIGxvYWQgZGVwYXJ0bWVudCBkYXRhOicsIGVycm9yKTtcbiAgICAgICAgICAgIHRocm93IGVycm9yO1xuICAgICAgICB9XG4gICAgfVxuICAgIGFzeW5jIHNlbmRDcmVhdGUoX2RlcGFydG1lbnQpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdNb2NrRGVwYXJ0bWVudFJlcG9zaXRvcnkgZG9lcyBub3Qgc3VwcG9ydCBzZW5kQ3JlYXRlLiBNb2NrIGRhdGEgaXMgcmVhZC1vbmx5LicpO1xuICAgIH1cbiAgICBhc3luYyBzZW5kVXBkYXRlKF9pZCwgX3VwZGF0ZXMpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdNb2NrRGVwYXJ0bWVudFJlcG9zaXRvcnkgZG9lcyBub3Qgc3VwcG9ydCBzZW5kVXBkYXRlLiBNb2NrIGRhdGEgaXMgcmVhZC1vbmx5LicpO1xuICAgIH1cbiAgICBhc3luYyBzZW5kRGVsZXRlKF9pZCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ01vY2tEZXBhcnRtZW50UmVwb3NpdG9yeSBkb2VzIG5vdCBzdXBwb3J0IHNlbmREZWxldGUuIE1vY2sgZGF0YSBpcyByZWFkLW9ubHkuJyk7XG4gICAgfVxuICAgIHByb2Nlc3NEZXBhcnRtZW50RGF0YShkYXRhKSB7XG4gICAgICAgIHJldHVybiBkYXRhLm1hcCgoZGVwdCkgPT4gKHtcbiAgICAgICAgICAgIGlkOiBkZXB0LmlkLFxuICAgICAgICAgICAgbmFtZTogZGVwdC5uYW1lLFxuICAgICAgICAgICAgcmVzb3VyY2VJZHM6IGRlcHQucmVzb3VyY2VJZHMsXG4gICAgICAgICAgICBzeW5jU3RhdHVzOiAnc3luY2VkJ1xuICAgICAgICB9KSk7XG4gICAgfVxufVxuIiwgIi8qKlxuICogTW9ja1NldHRpbmdzUmVwb3NpdG9yeSAtIExvYWRzIHRlbmFudCBzZXR0aW5ncyBmcm9tIGxvY2FsIEpTT04gZmlsZVxuICpcbiAqIFNldHRpbmdzIGFyZSBzdG9yZWQgYXMgc2VwYXJhdGUgcmVjb3JkcyBwZXIgc2VjdGlvbiAod29ya3dlZWssIGdyaWQsIGV0Yy4pLlxuICogVGhlIEpTT04gZmlsZSBpcyBhbHJlYWR5IGFuIGFycmF5IG9mIFRlbmFudFNldHRpbmcgcmVjb3Jkcy5cbiAqL1xuZXhwb3J0IGNsYXNzIE1vY2tTZXR0aW5nc1JlcG9zaXRvcnkge1xuICAgIGNvbnN0cnVjdG9yKCkge1xuICAgICAgICB0aGlzLmVudGl0eVR5cGUgPSAnU2V0dGluZ3MnO1xuICAgICAgICB0aGlzLmRhdGFVcmwgPSAnZGF0YS90ZW5hbnQtc2V0dGluZ3MuanNvbic7XG4gICAgfVxuICAgIGFzeW5jIGZldGNoQWxsKCkge1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBmZXRjaCh0aGlzLmRhdGFVcmwpO1xuICAgICAgICAgICAgaWYgKCFyZXNwb25zZS5vaykge1xuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgRmFpbGVkIHRvIGxvYWQgdGVuYW50IHNldHRpbmdzOiAke3Jlc3BvbnNlLnN0YXR1c30gJHtyZXNwb25zZS5zdGF0dXNUZXh0fWApO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgY29uc3Qgc2V0dGluZ3MgPSBhd2FpdCByZXNwb25zZS5qc29uKCk7XG4gICAgICAgICAgICAvLyBFbnN1cmUgc3luY1N0YXR1cyBpcyBzZXQgb24gZWFjaCByZWNvcmRcbiAgICAgICAgICAgIHJldHVybiBzZXR0aW5ncy5tYXAocyA9PiAoe1xuICAgICAgICAgICAgICAgIC4uLnMsXG4gICAgICAgICAgICAgICAgc3luY1N0YXR1czogcy5zeW5jU3RhdHVzIHx8ICdzeW5jZWQnXG4gICAgICAgICAgICB9KSk7XG4gICAgICAgIH1cbiAgICAgICAgY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKCdGYWlsZWQgdG8gbG9hZCB0ZW5hbnQgc2V0dGluZ3M6JywgZXJyb3IpO1xuICAgICAgICAgICAgdGhyb3cgZXJyb3I7XG4gICAgICAgIH1cbiAgICB9XG4gICAgYXN5bmMgc2VuZENyZWF0ZShfc2V0dGluZ3MpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdNb2NrU2V0dGluZ3NSZXBvc2l0b3J5IGRvZXMgbm90IHN1cHBvcnQgc2VuZENyZWF0ZS4gTW9jayBkYXRhIGlzIHJlYWQtb25seS4nKTtcbiAgICB9XG4gICAgYXN5bmMgc2VuZFVwZGF0ZShfaWQsIF91cGRhdGVzKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignTW9ja1NldHRpbmdzUmVwb3NpdG9yeSBkb2VzIG5vdCBzdXBwb3J0IHNlbmRVcGRhdGUuIE1vY2sgZGF0YSBpcyByZWFkLW9ubHkuJyk7XG4gICAgfVxuICAgIGFzeW5jIHNlbmREZWxldGUoX2lkKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignTW9ja1NldHRpbmdzUmVwb3NpdG9yeSBkb2VzIG5vdCBzdXBwb3J0IHNlbmREZWxldGUuIE1vY2sgZGF0YSBpcyByZWFkLW9ubHkuJyk7XG4gICAgfVxufVxuIiwgImV4cG9ydCBjbGFzcyBNb2NrVmlld0NvbmZpZ1JlcG9zaXRvcnkge1xuICAgIGNvbnN0cnVjdG9yKCkge1xuICAgICAgICB0aGlzLmVudGl0eVR5cGUgPSAnVmlld0NvbmZpZyc7XG4gICAgICAgIHRoaXMuZGF0YVVybCA9ICdkYXRhL3ZpZXdjb25maWdzLmpzb24nO1xuICAgIH1cbiAgICBhc3luYyBmZXRjaEFsbCgpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZmV0Y2godGhpcy5kYXRhVXJsKTtcbiAgICAgICAgICAgIGlmICghcmVzcG9uc2Uub2spIHtcbiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEZhaWxlZCB0byBsb2FkIHZpZXdjb25maWdzOiAke3Jlc3BvbnNlLnN0YXR1c30gJHtyZXNwb25zZS5zdGF0dXNUZXh0fWApO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgY29uc3QgcmF3RGF0YSA9IGF3YWl0IHJlc3BvbnNlLmpzb24oKTtcbiAgICAgICAgICAgIC8vIEVuc3VyZSBzeW5jU3RhdHVzIGlzIHNldCBvbiBlYWNoIGNvbmZpZ1xuICAgICAgICAgICAgY29uc3QgY29uZmlncyA9IHJhd0RhdGEubWFwKChjb25maWcpID0+ICh7XG4gICAgICAgICAgICAgICAgLi4uY29uZmlnLFxuICAgICAgICAgICAgICAgIHN5bmNTdGF0dXM6IGNvbmZpZy5zeW5jU3RhdHVzIHx8ICdzeW5jZWQnXG4gICAgICAgICAgICB9KSk7XG4gICAgICAgICAgICByZXR1cm4gY29uZmlncztcbiAgICAgICAgfVxuICAgICAgICBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ0ZhaWxlZCB0byBsb2FkIHZpZXdjb25maWdzOicsIGVycm9yKTtcbiAgICAgICAgICAgIHRocm93IGVycm9yO1xuICAgICAgICB9XG4gICAgfVxuICAgIGFzeW5jIHNlbmRDcmVhdGUoX2NvbmZpZykge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ01vY2tWaWV3Q29uZmlnUmVwb3NpdG9yeSBkb2VzIG5vdCBzdXBwb3J0IHNlbmRDcmVhdGUuIE1vY2sgZGF0YSBpcyByZWFkLW9ubHkuJyk7XG4gICAgfVxuICAgIGFzeW5jIHNlbmRVcGRhdGUoX2lkLCBfdXBkYXRlcykge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ01vY2tWaWV3Q29uZmlnUmVwb3NpdG9yeSBkb2VzIG5vdCBzdXBwb3J0IHNlbmRVcGRhdGUuIE1vY2sgZGF0YSBpcyByZWFkLW9ubHkuJyk7XG4gICAgfVxuICAgIGFzeW5jIHNlbmREZWxldGUoX2lkKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignTW9ja1ZpZXdDb25maWdSZXBvc2l0b3J5IGRvZXMgbm90IHN1cHBvcnQgc2VuZERlbGV0ZS4gTW9jayBkYXRhIGlzIHJlYWQtb25seS4nKTtcbiAgICB9XG59XG4iLCAiLyoqXG4gKiBEYXRhU2VlZGVyIC0gT3JjaGVzdHJhdGVzIGluaXRpYWwgZGF0YSBsb2FkaW5nIGZyb20gcmVwb3NpdG9yaWVzIGludG8gSW5kZXhlZERCXG4gKlxuICogQVJDSElURUNUVVJFOlxuICogLSBSZXBvc2l0b3J5IChNb2NrL0FwaSk6IEZldGNoZXMgZGF0YSBmcm9tIHNvdXJjZSAoSlNPTiBmaWxlIG9yIGJhY2tlbmQgQVBJKVxuICogLSBEYXRhU2VlZGVyICh0aGlzIGNsYXNzKTogT3JjaGVzdHJhdGVzIGZldGNoICsgc2F2ZSBvcGVyYXRpb25zXG4gKiAtIFNlcnZpY2UgKEV2ZW50U2VydmljZSwgZXRjLik6IFNhdmVzIGRhdGEgdG8gSW5kZXhlZERCXG4gKlxuICogUE9MWU1PUlBISUMgREVTSUdOOlxuICogLSBVc2VzIGFycmF5cyBvZiBJRW50aXR5U2VydmljZVtdIGFuZCBJQXBpUmVwb3NpdG9yeVtdXG4gKiAtIE1hdGNoZXMgc2VydmljZXMgd2l0aCByZXBvc2l0b3JpZXMgdXNpbmcgZW50aXR5VHlwZSBwcm9wZXJ0eVxuICogLSBPcGVuL0Nsb3NlZCBQcmluY2lwbGU6IEFkZGluZyBuZXcgZW50aXR5IHJlcXVpcmVzIG5vIGNvZGUgY2hhbmdlcyBoZXJlXG4gKi9cbmV4cG9ydCBjbGFzcyBEYXRhU2VlZGVyIHtcbiAgICBjb25zdHJ1Y3RvcihzZXJ2aWNlcywgcmVwb3NpdG9yaWVzKSB7XG4gICAgICAgIHRoaXMuc2VydmljZXMgPSBzZXJ2aWNlcztcbiAgICAgICAgdGhpcy5yZXBvc2l0b3JpZXMgPSByZXBvc2l0b3JpZXM7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFNlZWQgYWxsIGVudGl0eSBzdG9yZXMgaWYgdGhleSBhcmUgZW1wdHlcbiAgICAgKi9cbiAgICBhc3luYyBzZWVkSWZFbXB0eSgpIHtcbiAgICAgICAgY29uc29sZS5sb2coJ1tEYXRhU2VlZGVyXSBDaGVja2luZyBpZiBkYXRhYmFzZSBuZWVkcyBzZWVkaW5nLi4uJyk7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBmb3IgKGNvbnN0IHNlcnZpY2Ugb2YgdGhpcy5zZXJ2aWNlcykge1xuICAgICAgICAgICAgICAgIGNvbnN0IHJlcG9zaXRvcnkgPSB0aGlzLnJlcG9zaXRvcmllcy5maW5kKHJlcG8gPT4gcmVwby5lbnRpdHlUeXBlID09PSBzZXJ2aWNlLmVudGl0eVR5cGUpO1xuICAgICAgICAgICAgICAgIGlmICghcmVwb3NpdG9yeSkge1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLndhcm4oYFtEYXRhU2VlZGVyXSBObyByZXBvc2l0b3J5IGZvdW5kIGZvciBlbnRpdHkgdHlwZTogJHtzZXJ2aWNlLmVudGl0eVR5cGV9LCBza2lwcGluZ2ApO1xuICAgICAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgYXdhaXQgdGhpcy5zZWVkRW50aXR5KHNlcnZpY2UuZW50aXR5VHlwZSwgc2VydmljZSwgcmVwb3NpdG9yeSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBjb25zb2xlLmxvZygnW0RhdGFTZWVkZXJdIFNlZWRpbmcgY29tcGxldGUnKTtcbiAgICAgICAgfVxuICAgICAgICBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ1tEYXRhU2VlZGVyXSBTZWVkaW5nIGZhaWxlZDonLCBlcnJvcik7XG4gICAgICAgICAgICB0aHJvdyBlcnJvcjtcbiAgICAgICAgfVxuICAgIH1cbiAgICBhc3luYyBzZWVkRW50aXR5KGVudGl0eVR5cGUsIHNlcnZpY2UsIHJlcG9zaXRvcnkpIHtcbiAgICAgICAgY29uc3QgZXhpc3RpbmcgPSBhd2FpdCBzZXJ2aWNlLmdldEFsbCgpO1xuICAgICAgICBpZiAoZXhpc3RpbmcubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgY29uc29sZS5sb2coYFtEYXRhU2VlZGVyXSAke2VudGl0eVR5cGV9IHN0b3JlIGFscmVhZHkgaGFzICR7ZXhpc3RpbmcubGVuZ3RofSBpdGVtcywgc2tpcHBpbmcgc2VlZGApO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIGNvbnNvbGUubG9nKGBbRGF0YVNlZWRlcl0gJHtlbnRpdHlUeXBlfSBzdG9yZSBpcyBlbXB0eSwgZmV0Y2hpbmcgZnJvbSByZXBvc2l0b3J5Li4uYCk7XG4gICAgICAgIGNvbnN0IGRhdGEgPSBhd2FpdCByZXBvc2l0b3J5LmZldGNoQWxsKCk7XG4gICAgICAgIGNvbnNvbGUubG9nKGBbRGF0YVNlZWRlcl0gRmV0Y2hlZCAke2RhdGEubGVuZ3RofSAke2VudGl0eVR5cGV9IGl0ZW1zLCBzYXZpbmcgdG8gSW5kZXhlZERCLi4uYCk7XG4gICAgICAgIGZvciAoY29uc3QgZW50aXR5IG9mIGRhdGEpIHtcbiAgICAgICAgICAgIGF3YWl0IHNlcnZpY2Uuc2F2ZShlbnRpdHksIHRydWUpOyAvLyBzaWxlbnQgPSB0cnVlIHRvIHNraXAgYXVkaXQgbG9nZ2luZ1xuICAgICAgICB9XG4gICAgICAgIGNvbnNvbGUubG9nKGBbRGF0YVNlZWRlcl0gJHtlbnRpdHlUeXBlfSBzZWVkaW5nIGNvbXBsZXRlICgke2RhdGEubGVuZ3RofSBpdGVtcyBzYXZlZClgKTtcbiAgICB9XG59XG4iLCAiLyoqXG4gKiBDYWxjdWxhdGUgcGl4ZWwgcG9zaXRpb24gZm9yIGFuIGV2ZW50IGJhc2VkIG9uIGl0cyB0aW1lc1xuICovXG5leHBvcnQgZnVuY3Rpb24gY2FsY3VsYXRlRXZlbnRQb3NpdGlvbihzdGFydCwgZW5kLCBjb25maWcpIHtcbiAgICBjb25zdCBzdGFydE1pbnV0ZXMgPSBzdGFydC5nZXRIb3VycygpICogNjAgKyBzdGFydC5nZXRNaW51dGVzKCk7XG4gICAgY29uc3QgZW5kTWludXRlcyA9IGVuZC5nZXRIb3VycygpICogNjAgKyBlbmQuZ2V0TWludXRlcygpO1xuICAgIGNvbnN0IGRheVN0YXJ0TWludXRlcyA9IGNvbmZpZy5kYXlTdGFydEhvdXIgKiA2MDtcbiAgICBjb25zdCBtaW51dGVIZWlnaHQgPSBjb25maWcuaG91ckhlaWdodCAvIDYwO1xuICAgIGNvbnN0IHRvcCA9IChzdGFydE1pbnV0ZXMgLSBkYXlTdGFydE1pbnV0ZXMpICogbWludXRlSGVpZ2h0O1xuICAgIGNvbnN0IGhlaWdodCA9IChlbmRNaW51dGVzIC0gc3RhcnRNaW51dGVzKSAqIG1pbnV0ZUhlaWdodDtcbiAgICByZXR1cm4geyB0b3AsIGhlaWdodCB9O1xufVxuLyoqXG4gKiBDb252ZXJ0IG1pbnV0ZXMgdG8gcGl4ZWxzXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBtaW51dGVzVG9QaXhlbHMobWludXRlcywgY29uZmlnKSB7XG4gICAgcmV0dXJuIChtaW51dGVzIC8gNjApICogY29uZmlnLmhvdXJIZWlnaHQ7XG59XG4vKipcbiAqIENvbnZlcnQgcGl4ZWxzIHRvIG1pbnV0ZXNcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHBpeGVsc1RvTWludXRlcyhwaXhlbHMsIGNvbmZpZykge1xuICAgIHJldHVybiAocGl4ZWxzIC8gY29uZmlnLmhvdXJIZWlnaHQpICogNjA7XG59XG4vKipcbiAqIFNuYXAgcGl4ZWwgcG9zaXRpb24gdG8gZ3JpZCBpbnRlcnZhbFxuICovXG5leHBvcnQgZnVuY3Rpb24gc25hcFRvR3JpZChwaXhlbHMsIGNvbmZpZykge1xuICAgIGNvbnN0IHNuYXBQaXhlbHMgPSBtaW51dGVzVG9QaXhlbHMoY29uZmlnLnNuYXBJbnRlcnZhbCwgY29uZmlnKTtcbiAgICByZXR1cm4gTWF0aC5yb3VuZChwaXhlbHMgLyBzbmFwUGl4ZWxzKSAqIHNuYXBQaXhlbHM7XG59XG4iLCAiaW1wb3J0IHsgY2FsY3VsYXRlRXZlbnRQb3NpdGlvbiB9IGZyb20gJy4uLy4uL3V0aWxzL1Bvc2l0aW9uVXRpbHMnO1xuLyoqXG4gKiBDaGVjayBpZiB0d28gZXZlbnRzIG92ZXJsYXAgKHN0cmljdCAtIHRvdWNoaW5nIGF0IGJvdW5kYXJ5ID0gTk9UIG92ZXJsYXBwaW5nKVxuICogVGhpcyBtYXRjaGVzIFNjZW5hcmlvIDg6IGVuZD09PXN0YXJ0IGlzIE5PVCBvdmVybGFwXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBldmVudHNPdmVybGFwKGEsIGIpIHtcbiAgICByZXR1cm4gYS5zdGFydCA8IGIuZW5kICYmIGEuZW5kID4gYi5zdGFydDtcbn1cbi8qKlxuICogQ2hlY2sgaWYgdHdvIGV2ZW50cyBhcmUgd2l0aGluIHRocmVzaG9sZCBmb3IgZ3JpZCBncm91cGluZy5cbiAqIFRoaXMgaW5jbHVkZXM6XG4gKiAxLiBTdGFydC10by1zdGFydDogRXZlbnRzIHN0YXJ0IHdpdGhpbiB0aHJlc2hvbGQgb2YgZWFjaCBvdGhlclxuICogMi4gRW5kLXRvLXN0YXJ0OiBPbmUgZXZlbnQgc3RhcnRzIHdpdGhpbiB0aHJlc2hvbGQgYmVmb3JlIGFub3RoZXIgZW5kc1xuICovXG5mdW5jdGlvbiBldmVudHNXaXRoaW5UaHJlc2hvbGQoYSwgYiwgdGhyZXNob2xkTWludXRlcykge1xuICAgIGNvbnN0IHRocmVzaG9sZE1zID0gdGhyZXNob2xkTWludXRlcyAqIDYwICogMTAwMDtcbiAgICAvLyBTdGFydC10by1zdGFydDogYm90aCBldmVudHMgc3RhcnQgd2l0aGluIHRocmVzaG9sZFxuICAgIGNvbnN0IHN0YXJ0VG9TdGFydERpZmYgPSBNYXRoLmFicyhhLnN0YXJ0LmdldFRpbWUoKSAtIGIuc3RhcnQuZ2V0VGltZSgpKTtcbiAgICBpZiAoc3RhcnRUb1N0YXJ0RGlmZiA8PSB0aHJlc2hvbGRNcylcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgLy8gRW5kLXRvLXN0YXJ0OiBvbmUgZXZlbnQgc3RhcnRzIHdpdGhpbiB0aHJlc2hvbGQgYmVmb3JlIHRoZSBvdGhlciBlbmRzXG4gICAgLy8gQiBzdGFydHMgd2l0aGluIHRocmVzaG9sZCBiZWZvcmUgQSBlbmRzXG4gICAgY29uc3QgYlN0YXJ0c0JlZm9yZUFFbmRzID0gYS5lbmQuZ2V0VGltZSgpIC0gYi5zdGFydC5nZXRUaW1lKCk7XG4gICAgaWYgKGJTdGFydHNCZWZvcmVBRW5kcyA+IDAgJiYgYlN0YXJ0c0JlZm9yZUFFbmRzIDw9IHRocmVzaG9sZE1zKVxuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAvLyBBIHN0YXJ0cyB3aXRoaW4gdGhyZXNob2xkIGJlZm9yZSBCIGVuZHNcbiAgICBjb25zdCBhU3RhcnRzQmVmb3JlQkVuZHMgPSBiLmVuZC5nZXRUaW1lKCkgLSBhLnN0YXJ0LmdldFRpbWUoKTtcbiAgICBpZiAoYVN0YXJ0c0JlZm9yZUJFbmRzID4gMCAmJiBhU3RhcnRzQmVmb3JlQkVuZHMgPD0gdGhyZXNob2xkTXMpXG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgIHJldHVybiBmYWxzZTtcbn1cbi8qKlxuICogQ2hlY2sgaWYgYWxsIGV2ZW50cyBpbiBhIGdyb3VwIHN0YXJ0IHdpdGhpbiB0aHJlc2hvbGQgb2YgZWFjaCBvdGhlclxuICovXG5mdW5jdGlvbiBhbGxTdGFydFdpdGhpblRocmVzaG9sZChldmVudHMsIHRocmVzaG9sZE1pbnV0ZXMpIHtcbiAgICBpZiAoZXZlbnRzLmxlbmd0aCA8PSAxKVxuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAvLyBGaW5kIGVhcmxpZXN0IGFuZCBsYXRlc3Qgc3RhcnQgdGltZXNcbiAgICBsZXQgZWFybGllc3QgPSBldmVudHNbMF0uc3RhcnQuZ2V0VGltZSgpO1xuICAgIGxldCBsYXRlc3QgPSBldmVudHNbMF0uc3RhcnQuZ2V0VGltZSgpO1xuICAgIGZvciAoY29uc3QgZXZlbnQgb2YgZXZlbnRzKSB7XG4gICAgICAgIGNvbnN0IHRpbWUgPSBldmVudC5zdGFydC5nZXRUaW1lKCk7XG4gICAgICAgIGlmICh0aW1lIDwgZWFybGllc3QpXG4gICAgICAgICAgICBlYXJsaWVzdCA9IHRpbWU7XG4gICAgICAgIGlmICh0aW1lID4gbGF0ZXN0KVxuICAgICAgICAgICAgbGF0ZXN0ID0gdGltZTtcbiAgICB9XG4gICAgY29uc3QgZGlmZk1pbnV0ZXMgPSAobGF0ZXN0IC0gZWFybGllc3QpIC8gKDEwMDAgKiA2MCk7XG4gICAgcmV0dXJuIGRpZmZNaW51dGVzIDw9IHRocmVzaG9sZE1pbnV0ZXM7XG59XG4vKipcbiAqIEZpbmQgZ3JvdXBzIG9mIG92ZXJsYXBwaW5nIGV2ZW50cyAoY29ubmVjdGVkIGJ5IG92ZXJsYXAgY2hhaW4pXG4gKiBFdmVudHMgYXJlIGdyb3VwZWQgaWYgdGhleSBvdmVybGFwIHdpdGggYW55IGV2ZW50IGluIHRoZSBncm91cFxuICovXG5mdW5jdGlvbiBmaW5kT3ZlcmxhcEdyb3VwcyhldmVudHMpIHtcbiAgICBpZiAoZXZlbnRzLmxlbmd0aCA9PT0gMClcbiAgICAgICAgcmV0dXJuIFtdO1xuICAgIGNvbnN0IHNvcnRlZCA9IFsuLi5ldmVudHNdLnNvcnQoKGEsIGIpID0+IGEuc3RhcnQuZ2V0VGltZSgpIC0gYi5zdGFydC5nZXRUaW1lKCkpO1xuICAgIGNvbnN0IHVzZWQgPSBuZXcgU2V0KCk7XG4gICAgY29uc3QgZ3JvdXBzID0gW107XG4gICAgZm9yIChjb25zdCBldmVudCBvZiBzb3J0ZWQpIHtcbiAgICAgICAgaWYgKHVzZWQuaGFzKGV2ZW50LmlkKSlcbiAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAvLyBTdGFydCBhIG5ldyBncm91cCB3aXRoIHRoaXMgZXZlbnRcbiAgICAgICAgY29uc3QgZ3JvdXAgPSBbZXZlbnRdO1xuICAgICAgICB1c2VkLmFkZChldmVudC5pZCk7XG4gICAgICAgIC8vIEV4cGFuZCBncm91cCBieSBmaW5kaW5nIGFsbCBjb25uZWN0ZWQgZXZlbnRzICh2aWEgb3ZlcmxhcClcbiAgICAgICAgbGV0IGV4cGFuZGVkID0gdHJ1ZTtcbiAgICAgICAgd2hpbGUgKGV4cGFuZGVkKSB7XG4gICAgICAgICAgICBleHBhbmRlZCA9IGZhbHNlO1xuICAgICAgICAgICAgZm9yIChjb25zdCBjYW5kaWRhdGUgb2Ygc29ydGVkKSB7XG4gICAgICAgICAgICAgICAgaWYgKHVzZWQuaGFzKGNhbmRpZGF0ZS5pZCkpXG4gICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgICAgICAgIC8vIENoZWNrIGlmIGNhbmRpZGF0ZSBvdmVybGFwcyB3aXRoIGFueSBldmVudCBpbiBncm91cFxuICAgICAgICAgICAgICAgIGNvbnN0IGNvbm5lY3RzID0gZ3JvdXAuc29tZShtZW1iZXIgPT4gZXZlbnRzT3ZlcmxhcChtZW1iZXIsIGNhbmRpZGF0ZSkpO1xuICAgICAgICAgICAgICAgIGlmIChjb25uZWN0cykge1xuICAgICAgICAgICAgICAgICAgICBncm91cC5wdXNoKGNhbmRpZGF0ZSk7XG4gICAgICAgICAgICAgICAgICAgIHVzZWQuYWRkKGNhbmRpZGF0ZS5pZCk7XG4gICAgICAgICAgICAgICAgICAgIGV4cGFuZGVkID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgZ3JvdXBzLnB1c2goZ3JvdXApO1xuICAgIH1cbiAgICByZXR1cm4gZ3JvdXBzO1xufVxuLyoqXG4gKiBGaW5kIGdyaWQgY2FuZGlkYXRlcyB3aXRoaW4gYSBncm91cCAtIGV2ZW50cyBjb25uZWN0ZWQgdmlhIHRocmVzaG9sZCBjaGFpblxuICogVXNlcyBWMSBsb2dpYzogZXZlbnRzIGFyZSBjb25uZWN0ZWQgaWYgd2l0aGluIHRocmVzaG9sZCAobm8gb3ZlcmxhcCByZXF1aXJlbWVudClcbiAqL1xuZnVuY3Rpb24gZmluZEdyaWRDYW5kaWRhdGVzKGV2ZW50cywgdGhyZXNob2xkTWludXRlcykge1xuICAgIGlmIChldmVudHMubGVuZ3RoID09PSAwKVxuICAgICAgICByZXR1cm4gW107XG4gICAgY29uc3Qgc29ydGVkID0gWy4uLmV2ZW50c10uc29ydCgoYSwgYikgPT4gYS5zdGFydC5nZXRUaW1lKCkgLSBiLnN0YXJ0LmdldFRpbWUoKSk7XG4gICAgY29uc3QgdXNlZCA9IG5ldyBTZXQoKTtcbiAgICBjb25zdCBncm91cHMgPSBbXTtcbiAgICBmb3IgKGNvbnN0IGV2ZW50IG9mIHNvcnRlZCkge1xuICAgICAgICBpZiAodXNlZC5oYXMoZXZlbnQuaWQpKVxuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgIGNvbnN0IGdyb3VwID0gW2V2ZW50XTtcbiAgICAgICAgdXNlZC5hZGQoZXZlbnQuaWQpO1xuICAgICAgICAvLyBFeHBhbmQgYnkgdGhyZXNob2xkIGNoYWluIChWMSBsb2dpYzogbm8gb3ZlcmxhcCByZXF1aXJlbWVudCwganVzdCB0aHJlc2hvbGQpXG4gICAgICAgIGxldCBleHBhbmRlZCA9IHRydWU7XG4gICAgICAgIHdoaWxlIChleHBhbmRlZCkge1xuICAgICAgICAgICAgZXhwYW5kZWQgPSBmYWxzZTtcbiAgICAgICAgICAgIGZvciAoY29uc3QgY2FuZGlkYXRlIG9mIHNvcnRlZCkge1xuICAgICAgICAgICAgICAgIGlmICh1c2VkLmhhcyhjYW5kaWRhdGUuaWQpKVxuICAgICAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgICAgICBjb25zdCBjb25uZWN0cyA9IGdyb3VwLnNvbWUobWVtYmVyID0+IGV2ZW50c1dpdGhpblRocmVzaG9sZChtZW1iZXIsIGNhbmRpZGF0ZSwgdGhyZXNob2xkTWludXRlcykpO1xuICAgICAgICAgICAgICAgIGlmIChjb25uZWN0cykge1xuICAgICAgICAgICAgICAgICAgICBncm91cC5wdXNoKGNhbmRpZGF0ZSk7XG4gICAgICAgICAgICAgICAgICAgIHVzZWQuYWRkKGNhbmRpZGF0ZS5pZCk7XG4gICAgICAgICAgICAgICAgICAgIGV4cGFuZGVkID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgZ3JvdXBzLnB1c2goZ3JvdXApO1xuICAgIH1cbiAgICByZXR1cm4gZ3JvdXBzO1xufVxuLyoqXG4gKiBDYWxjdWxhdGUgc3RhY2sgbGV2ZWxzIGZvciBvdmVybGFwcGluZyBldmVudHMgdXNpbmcgZ3JlZWR5IGFsZ29yaXRobVxuICogRm9yIGVhY2ggZXZlbnQ6IGxldmVsID0gbWF4KG92ZXJsYXBwaW5nIGFscmVhZHktcHJvY2Vzc2VkIGV2ZW50cykgKyAxXG4gKi9cbmZ1bmN0aW9uIGNhbGN1bGF0ZVN0YWNrTGV2ZWxzKGV2ZW50cykge1xuICAgIGNvbnN0IGxldmVscyA9IG5ldyBNYXAoKTtcbiAgICBjb25zdCBzb3J0ZWQgPSBbLi4uZXZlbnRzXS5zb3J0KChhLCBiKSA9PiBhLnN0YXJ0LmdldFRpbWUoKSAtIGIuc3RhcnQuZ2V0VGltZSgpKTtcbiAgICBmb3IgKGNvbnN0IGV2ZW50IG9mIHNvcnRlZCkge1xuICAgICAgICBsZXQgbWF4T3ZlcmxhcHBpbmdMZXZlbCA9IC0xO1xuICAgICAgICAvLyBGaW5kIG1heCBsZXZlbCBhbW9uZyBvdmVybGFwcGluZyBldmVudHMgYWxyZWFkeSBwcm9jZXNzZWRcbiAgICAgICAgZm9yIChjb25zdCBbaWQsIGxldmVsXSBvZiBsZXZlbHMpIHtcbiAgICAgICAgICAgIGNvbnN0IG90aGVyID0gZXZlbnRzLmZpbmQoZSA9PiBlLmlkID09PSBpZCk7XG4gICAgICAgICAgICBpZiAob3RoZXIgJiYgZXZlbnRzT3ZlcmxhcChldmVudCwgb3RoZXIpKSB7XG4gICAgICAgICAgICAgICAgbWF4T3ZlcmxhcHBpbmdMZXZlbCA9IE1hdGgubWF4KG1heE92ZXJsYXBwaW5nTGV2ZWwsIGxldmVsKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBsZXZlbHMuc2V0KGV2ZW50LmlkLCBtYXhPdmVybGFwcGluZ0xldmVsICsgMSk7XG4gICAgfVxuICAgIHJldHVybiBsZXZlbHM7XG59XG4vKipcbiAqIEFsbG9jYXRlIGV2ZW50cyB0byBjb2x1bW5zIGZvciBHUklEIGxheW91dCB1c2luZyBncmVlZHkgYWxnb3JpdGhtXG4gKiBOb24tb3ZlcmxhcHBpbmcgZXZlbnRzIGNhbiBzaGFyZSBhIGNvbHVtbiB0byBtaW5pbWl6ZSB0b3RhbCBjb2x1bW5zXG4gKi9cbmZ1bmN0aW9uIGFsbG9jYXRlQ29sdW1ucyhldmVudHMpIHtcbiAgICBjb25zdCBzb3J0ZWQgPSBbLi4uZXZlbnRzXS5zb3J0KChhLCBiKSA9PiBhLnN0YXJ0LmdldFRpbWUoKSAtIGIuc3RhcnQuZ2V0VGltZSgpKTtcbiAgICBjb25zdCBjb2x1bW5zID0gW107XG4gICAgZm9yIChjb25zdCBldmVudCBvZiBzb3J0ZWQpIHtcbiAgICAgICAgLy8gRmluZCBmaXJzdCBjb2x1bW4gd2hlcmUgZXZlbnQgZG9lc24ndCBvdmVybGFwIHdpdGggZXhpc3RpbmcgZXZlbnRzXG4gICAgICAgIGxldCBwbGFjZWQgPSBmYWxzZTtcbiAgICAgICAgZm9yIChjb25zdCBjb2x1bW4gb2YgY29sdW1ucykge1xuICAgICAgICAgICAgY29uc3QgY2FuRml0ID0gIWNvbHVtbi5zb21lKGUgPT4gZXZlbnRzT3ZlcmxhcChldmVudCwgZSkpO1xuICAgICAgICAgICAgaWYgKGNhbkZpdCkge1xuICAgICAgICAgICAgICAgIGNvbHVtbi5wdXNoKGV2ZW50KTtcbiAgICAgICAgICAgICAgICBwbGFjZWQgPSB0cnVlO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIC8vIE5vIHN1aXRhYmxlIGNvbHVtbiBmb3VuZCwgY3JlYXRlIG5ldyBvbmVcbiAgICAgICAgaWYgKCFwbGFjZWQpIHtcbiAgICAgICAgICAgIGNvbHVtbnMucHVzaChbZXZlbnRdKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gY29sdW1ucztcbn1cbi8qKlxuICogTWFpbiBlbnRyeSBwb2ludDogQ2FsY3VsYXRlIGNvbXBsZXRlIGxheW91dCBmb3IgYSBjb2x1bW4ncyBldmVudHNcbiAqXG4gKiBBbGdvcml0aG06XG4gKiAxLiBGaW5kIG92ZXJsYXAgZ3JvdXBzIChldmVudHMgY29ubmVjdGVkIGJ5IG92ZXJsYXAgY2hhaW4pXG4gKiAyLiBGb3IgZWFjaCBvdmVybGFwIGdyb3VwLCBmaW5kIGdyaWQgY2FuZGlkYXRlcyAoZXZlbnRzIHdpdGhpbiB0aHJlc2hvbGQgY2hhaW4pXG4gKiAzLiBJZiBhbGwgZXZlbnRzIGluIG92ZXJsYXAgZ3JvdXAgZm9ybSBhIHNpbmdsZSBncmlkIGNhbmRpZGF0ZSBcdTIxOTIgR1JJRCBtb2RlXG4gKiA0LiBPdGhlcndpc2UgXHUyMTkyIFNUQUNLSU5HIG1vZGUgd2l0aCBjYWxjdWxhdGVkIGxldmVsc1xuICovXG5leHBvcnQgZnVuY3Rpb24gY2FsY3VsYXRlQ29sdW1uTGF5b3V0KGV2ZW50cywgY29uZmlnKSB7XG4gICAgY29uc3QgdGhyZXNob2xkTWludXRlcyA9IGNvbmZpZy5ncmlkU3RhcnRUaHJlc2hvbGRNaW51dGVzID8/IDEwO1xuICAgIGNvbnN0IHJlc3VsdCA9IHtcbiAgICAgICAgZ3JpZHM6IFtdLFxuICAgICAgICBzdGFja2VkOiBbXVxuICAgIH07XG4gICAgaWYgKGV2ZW50cy5sZW5ndGggPT09IDApXG4gICAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgLy8gRmluZCBhbGwgb3ZlcmxhcHBpbmcgZXZlbnQgZ3JvdXBzXG4gICAgY29uc3Qgb3ZlcmxhcEdyb3VwcyA9IGZpbmRPdmVybGFwR3JvdXBzKGV2ZW50cyk7XG4gICAgZm9yIChjb25zdCBvdmVybGFwR3JvdXAgb2Ygb3ZlcmxhcEdyb3Vwcykge1xuICAgICAgICBpZiAob3ZlcmxhcEdyb3VwLmxlbmd0aCA9PT0gMSkge1xuICAgICAgICAgICAgLy8gU2luZ2xlIGV2ZW50IC0gbm8gZ3JvdXBpbmcgbmVlZGVkXG4gICAgICAgICAgICByZXN1bHQuc3RhY2tlZC5wdXNoKHtcbiAgICAgICAgICAgICAgICBldmVudDogb3ZlcmxhcEdyb3VwWzBdLFxuICAgICAgICAgICAgICAgIHN0YWNrTGV2ZWw6IDBcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cbiAgICAgICAgLy8gV2l0aGluIHRoaXMgb3ZlcmxhcCBncm91cCwgZmluZCBncmlkIGNhbmRpZGF0ZXMgKHRocmVzaG9sZC1jb25uZWN0ZWQgc3ViZ3JvdXBzKVxuICAgICAgICBjb25zdCBncmlkU3ViZ3JvdXBzID0gZmluZEdyaWRDYW5kaWRhdGVzKG92ZXJsYXBHcm91cCwgdGhyZXNob2xkTWludXRlcyk7XG4gICAgICAgIC8vIENoZWNrIGlmIHRoZSBFTlRJUkUgb3ZlcmxhcCBncm91cCBmb3JtcyBhIHNpbmdsZSBncmlkIGNhbmRpZGF0ZVxuICAgICAgICAvLyBUaGlzIGhhcHBlbnMgd2hlbiBhbGwgZXZlbnRzIGFyZSBjb25uZWN0ZWQgdmlhIHRocmVzaG9sZCBjaGFpblxuICAgICAgICBjb25zdCBsYXJnZXN0R3JpZENhbmRpZGF0ZSA9IGdyaWRTdWJncm91cHMucmVkdWNlKChtYXgsIGcpID0+IGcubGVuZ3RoID4gbWF4Lmxlbmd0aCA/IGcgOiBtYXgsIGdyaWRTdWJncm91cHNbMF0pO1xuICAgICAgICBpZiAobGFyZ2VzdEdyaWRDYW5kaWRhdGUubGVuZ3RoID09PSBvdmVybGFwR3JvdXAubGVuZ3RoKSB7XG4gICAgICAgICAgICAvLyBBbGwgZXZlbnRzIGluIG92ZXJsYXAgZ3JvdXAgYXJlIGNvbm5lY3RlZCB2aWEgdGhyZXNob2xkIGNoYWluIFx1MjE5MiBHUklEIG1vZGVcbiAgICAgICAgICAgIGNvbnN0IGNvbHVtbnMgPSBhbGxvY2F0ZUNvbHVtbnMob3ZlcmxhcEdyb3VwKTtcbiAgICAgICAgICAgIGNvbnN0IGVhcmxpZXN0ID0gb3ZlcmxhcEdyb3VwLnJlZHVjZSgobWluLCBlKSA9PiBlLnN0YXJ0IDwgbWluLnN0YXJ0ID8gZSA6IG1pbiwgb3ZlcmxhcEdyb3VwWzBdKTtcbiAgICAgICAgICAgIGNvbnN0IHBvc2l0aW9uID0gY2FsY3VsYXRlRXZlbnRQb3NpdGlvbihlYXJsaWVzdC5zdGFydCwgZWFybGllc3QuZW5kLCBjb25maWcpO1xuICAgICAgICAgICAgcmVzdWx0LmdyaWRzLnB1c2goe1xuICAgICAgICAgICAgICAgIGV2ZW50czogb3ZlcmxhcEdyb3VwLFxuICAgICAgICAgICAgICAgIGNvbHVtbnMsXG4gICAgICAgICAgICAgICAgc3RhY2tMZXZlbDogMCxcbiAgICAgICAgICAgICAgICBwb3NpdGlvbjogeyB0b3A6IHBvc2l0aW9uLnRvcCB9XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIC8vIE5vdCBhbGwgZXZlbnRzIGNvbm5lY3RlZCB2aWEgdGhyZXNob2xkIFx1MjE5MiBTVEFDS0lORyBtb2RlXG4gICAgICAgICAgICBjb25zdCBsZXZlbHMgPSBjYWxjdWxhdGVTdGFja0xldmVscyhvdmVybGFwR3JvdXApO1xuICAgICAgICAgICAgZm9yIChjb25zdCBldmVudCBvZiBvdmVybGFwR3JvdXApIHtcbiAgICAgICAgICAgICAgICByZXN1bHQuc3RhY2tlZC5wdXNoKHtcbiAgICAgICAgICAgICAgICAgICAgZXZlbnQsXG4gICAgICAgICAgICAgICAgICAgIHN0YWNrTGV2ZWw6IGxldmVscy5nZXQoZXZlbnQuaWQpID8/IDBcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gcmVzdWx0O1xufVxuIiwgImltcG9ydCB7IGNhbGN1bGF0ZUV2ZW50UG9zaXRpb24sIHNuYXBUb0dyaWQsIHBpeGVsc1RvTWludXRlcyB9IGZyb20gJy4uLy4uL3V0aWxzL1Bvc2l0aW9uVXRpbHMnO1xuaW1wb3J0IHsgQ29yZUV2ZW50cyB9IGZyb20gJy4uLy4uL2NvbnN0YW50cy9Db3JlRXZlbnRzJztcbmltcG9ydCB7IGNhbGN1bGF0ZUNvbHVtbkxheW91dCB9IGZyb20gJy4vRXZlbnRMYXlvdXRFbmdpbmUnO1xuLyoqXG4gKiBFdmVudFJlbmRlcmVyIC0gUmVuZGVycyBjYWxlbmRhciBldmVudHMgdG8gdGhlIERPTVxuICpcbiAqIENMRUFOIGFwcHJvYWNoOlxuICogLSBPbmx5IGRhdGEtaWQgYXR0cmlidXRlIG9uIGV2ZW50IGVsZW1lbnRcbiAqIC0gaW5uZXJIVE1MIGNvbnRhaW5zIG9ubHkgdmlzaWJsZSBjb250ZW50XG4gKiAtIEV2ZW50IGRhdGEgcmV0cmlldmVkIHZpYSBFdmVudFNlcnZpY2Ugd2hlbiBuZWVkZWRcbiAqL1xuZXhwb3J0IGNsYXNzIEV2ZW50UmVuZGVyZXIge1xuICAgIGNvbnN0cnVjdG9yKGV2ZW50U2VydmljZSwgZGF0ZVNlcnZpY2UsIGdyaWRDb25maWcsIGV2ZW50QnVzKSB7XG4gICAgICAgIHRoaXMuZXZlbnRTZXJ2aWNlID0gZXZlbnRTZXJ2aWNlO1xuICAgICAgICB0aGlzLmRhdGVTZXJ2aWNlID0gZGF0ZVNlcnZpY2U7XG4gICAgICAgIHRoaXMuZ3JpZENvbmZpZyA9IGdyaWRDb25maWc7XG4gICAgICAgIHRoaXMuZXZlbnRCdXMgPSBldmVudEJ1cztcbiAgICAgICAgdGhpcy5jb250YWluZXIgPSBudWxsO1xuICAgICAgICB0aGlzLnNldHVwTGlzdGVuZXJzKCk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFNldHVwIGxpc3RlbmVycyBmb3IgZHJhZy1kcm9wIGFuZCB1cGRhdGUgZXZlbnRzXG4gICAgICovXG4gICAgc2V0dXBMaXN0ZW5lcnMoKSB7XG4gICAgICAgIHRoaXMuZXZlbnRCdXMub24oQ29yZUV2ZW50cy5FVkVOVF9EUkFHX0NPTFVNTl9DSEFOR0UsIChlKSA9PiB7XG4gICAgICAgICAgICBjb25zdCBwYXlsb2FkID0gZS5kZXRhaWw7XG4gICAgICAgICAgICB0aGlzLmhhbmRsZUNvbHVtbkNoYW5nZShwYXlsb2FkKTtcbiAgICAgICAgfSk7XG4gICAgICAgIHRoaXMuZXZlbnRCdXMub24oQ29yZUV2ZW50cy5FVkVOVF9EUkFHX01PVkUsIChlKSA9PiB7XG4gICAgICAgICAgICBjb25zdCBwYXlsb2FkID0gZS5kZXRhaWw7XG4gICAgICAgICAgICB0aGlzLnVwZGF0ZURyYWdUaW1lc3RhbXAocGF5bG9hZCk7XG4gICAgICAgIH0pO1xuICAgICAgICB0aGlzLmV2ZW50QnVzLm9uKENvcmVFdmVudHMuRVZFTlRfVVBEQVRFRCwgKGUpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHBheWxvYWQgPSBlLmRldGFpbDtcbiAgICAgICAgICAgIHRoaXMuaGFuZGxlRXZlbnRVcGRhdGVkKHBheWxvYWQpO1xuICAgICAgICB9KTtcbiAgICAgICAgdGhpcy5ldmVudEJ1cy5vbihDb3JlRXZlbnRzLkVWRU5UX0RSQUdfRU5ELCAoZSkgPT4ge1xuICAgICAgICAgICAgY29uc3QgcGF5bG9hZCA9IGUuZGV0YWlsO1xuICAgICAgICAgICAgdGhpcy5oYW5kbGVEcmFnRW5kKHBheWxvYWQpO1xuICAgICAgICB9KTtcbiAgICAgICAgdGhpcy5ldmVudEJ1cy5vbihDb3JlRXZlbnRzLkVWRU5UX0RSQUdfTEVBVkVfSEVBREVSLCAoZSkgPT4ge1xuICAgICAgICAgICAgY29uc3QgcGF5bG9hZCA9IGUuZGV0YWlsO1xuICAgICAgICAgICAgdGhpcy5oYW5kbGVEcmFnTGVhdmVIZWFkZXIocGF5bG9hZCk7XG4gICAgICAgIH0pO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBIYW5kbGUgRVZFTlRfRFJBR19FTkQgLSByZW1vdmUgZWxlbWVudCBpZiBkcm9wcGVkIGluIGhlYWRlclxuICAgICAqL1xuICAgIGhhbmRsZURyYWdFbmQocGF5bG9hZCkge1xuICAgICAgICBpZiAocGF5bG9hZC50YXJnZXQgPT09ICdoZWFkZXInKSB7XG4gICAgICAgICAgICAvLyBFdmVudCB3YXMgZHJvcHBlZCBpbiBoZWFkZXIgZHJhd2VyIC0gcmVtb3ZlIGZyb20gZ3JpZFxuICAgICAgICAgICAgY29uc3QgZWxlbWVudCA9IHRoaXMuY29udGFpbmVyPy5xdWVyeVNlbGVjdG9yKGBzd3AtY29udGVudC12aWV3cG9ydCBzd3AtZXZlbnRbZGF0YS1ldmVudC1pZD1cIiR7cGF5bG9hZC5zd3BFdmVudC5ldmVudElkfVwiXWApO1xuICAgICAgICAgICAgZWxlbWVudD8ucmVtb3ZlKCk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgLyoqXG4gICAgICogSGFuZGxlIGhlYWRlciBpdGVtIGxlYXZpbmcgaGVhZGVyIC0gY3JlYXRlIHN3cC1ldmVudCBpbiBncmlkXG4gICAgICovXG4gICAgaGFuZGxlRHJhZ0xlYXZlSGVhZGVyKHBheWxvYWQpIHtcbiAgICAgICAgLy8gT25seSBoYW5kbGUgd2hlbiBzb3VyY2UgaXMgaGVhZGVyIChoZWFkZXIgaXRlbSBkcmFnZ2VkIHRvIGdyaWQpXG4gICAgICAgIGlmIChwYXlsb2FkLnNvdXJjZSAhPT0gJ2hlYWRlcicpXG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIGlmICghcGF5bG9hZC50YXJnZXRDb2x1bW4gfHwgIXBheWxvYWQuc3RhcnQgfHwgIXBheWxvYWQuZW5kKVxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAvLyBUdXJuIGhlYWRlciBpdGVtIGludG8gZ2hvc3QgKHN0YXlzIHZpc2libGUgYnV0IGZhZGVkKVxuICAgICAgICBpZiAocGF5bG9hZC5lbGVtZW50KSB7XG4gICAgICAgICAgICBwYXlsb2FkLmVsZW1lbnQuY2xhc3NMaXN0LmFkZCgnZHJhZy1naG9zdCcpO1xuICAgICAgICAgICAgcGF5bG9hZC5lbGVtZW50LnN0eWxlLm9wYWNpdHkgPSAnMC4zJztcbiAgICAgICAgICAgIHBheWxvYWQuZWxlbWVudC5zdHlsZS5wb2ludGVyRXZlbnRzID0gJ25vbmUnO1xuICAgICAgICB9XG4gICAgICAgIC8vIENyZWF0ZSBldmVudCBvYmplY3QgZnJvbSBoZWFkZXIgaXRlbSBkYXRhXG4gICAgICAgIGNvbnN0IGV2ZW50ID0ge1xuICAgICAgICAgICAgaWQ6IHBheWxvYWQuZXZlbnRJZCxcbiAgICAgICAgICAgIHRpdGxlOiBwYXlsb2FkLnRpdGxlIHx8ICcnLFxuICAgICAgICAgICAgZGVzY3JpcHRpb246ICcnLFxuICAgICAgICAgICAgc3RhcnQ6IHBheWxvYWQuc3RhcnQsXG4gICAgICAgICAgICBlbmQ6IHBheWxvYWQuZW5kLFxuICAgICAgICAgICAgdHlwZTogJ2N1c3RvbWVyJyxcbiAgICAgICAgICAgIGFsbERheTogZmFsc2UsXG4gICAgICAgICAgICBzeW5jU3RhdHVzOiAncGVuZGluZydcbiAgICAgICAgfTtcbiAgICAgICAgLy8gQ3JlYXRlIHN3cC1ldmVudCBlbGVtZW50IHVzaW5nIGV4aXN0aW5nIG1ldGhvZFxuICAgICAgICBjb25zdCBlbGVtZW50ID0gdGhpcy5jcmVhdGVFdmVudEVsZW1lbnQoZXZlbnQpO1xuICAgICAgICAvLyBBZGQgdG8gdGFyZ2V0IGNvbHVtblxuICAgICAgICBsZXQgZXZlbnRzTGF5ZXIgPSBwYXlsb2FkLnRhcmdldENvbHVtbi5xdWVyeVNlbGVjdG9yKCdzd3AtZXZlbnRzLWxheWVyJyk7XG4gICAgICAgIGlmICghZXZlbnRzTGF5ZXIpIHtcbiAgICAgICAgICAgIGV2ZW50c0xheWVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3dwLWV2ZW50cy1sYXllcicpO1xuICAgICAgICAgICAgcGF5bG9hZC50YXJnZXRDb2x1bW4uYXBwZW5kQ2hpbGQoZXZlbnRzTGF5ZXIpO1xuICAgICAgICB9XG4gICAgICAgIGV2ZW50c0xheWVyLmFwcGVuZENoaWxkKGVsZW1lbnQpO1xuICAgICAgICAvLyBNYXJrIGFzIGRyYWdnaW5nIHNvIERyYWdEcm9wTWFuYWdlciBjYW4gY29udGludWUgd2l0aCBpdFxuICAgICAgICBlbGVtZW50LmNsYXNzTGlzdC5hZGQoJ2RyYWdnaW5nJyk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEhhbmRsZSBFVkVOVF9VUERBVEVEIC0gcmUtcmVuZGVyIGFmZmVjdGVkIGNvbHVtbnNcbiAgICAgKi9cbiAgICBhc3luYyBoYW5kbGVFdmVudFVwZGF0ZWQocGF5bG9hZCkge1xuICAgICAgICAvLyBSZS1yZW5kZXIgc291cmNlIGNvbHVtbiAoaWYgZGlmZmVyZW50IGZyb20gdGFyZ2V0KVxuICAgICAgICBpZiAocGF5bG9hZC5zb3VyY2VDb2x1bW5LZXkgIT09IHBheWxvYWQudGFyZ2V0Q29sdW1uS2V5KSB7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLnJlcmVuZGVyQ29sdW1uKHBheWxvYWQuc291cmNlQ29sdW1uS2V5KTtcbiAgICAgICAgfVxuICAgICAgICAvLyBSZS1yZW5kZXIgdGFyZ2V0IGNvbHVtblxuICAgICAgICBhd2FpdCB0aGlzLnJlcmVuZGVyQ29sdW1uKHBheWxvYWQudGFyZ2V0Q29sdW1uS2V5KTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogUmUtcmVuZGVyIGEgc2luZ2xlIGNvbHVtbiB3aXRoIGZyZXNoIGRhdGEgZnJvbSBJbmRleGVkREJcbiAgICAgKi9cbiAgICBhc3luYyByZXJlbmRlckNvbHVtbihjb2x1bW5LZXkpIHtcbiAgICAgICAgY29uc3QgY29sdW1uID0gdGhpcy5maW5kQ29sdW1uKGNvbHVtbktleSk7XG4gICAgICAgIGlmICghY29sdW1uKVxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAvLyBSZWFkIGRhdGUgYW5kIHJlc291cmNlSWQgZGlyZWN0bHkgZnJvbSBjb2x1bW4gYXR0cmlidXRlcyAoY29sdW1uS2V5IGlzIG9wYXF1ZSlcbiAgICAgICAgY29uc3QgZGF0ZSA9IGNvbHVtbi5kYXRhc2V0LmRhdGU7XG4gICAgICAgIGNvbnN0IHJlc291cmNlSWQgPSBjb2x1bW4uZGF0YXNldC5yZXNvdXJjZUlkO1xuICAgICAgICBpZiAoIWRhdGUpXG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIC8vIEdldCBkYXRlIHJhbmdlIGZvciB0aGlzIGRheVxuICAgICAgICBjb25zdCBzdGFydERhdGUgPSBuZXcgRGF0ZShkYXRlKTtcbiAgICAgICAgY29uc3QgZW5kRGF0ZSA9IG5ldyBEYXRlKGRhdGUpO1xuICAgICAgICBlbmREYXRlLnNldEhvdXJzKDIzLCA1OSwgNTksIDk5OSk7XG4gICAgICAgIC8vIEZldGNoIGV2ZW50cyBmcm9tIEluZGV4ZWREQlxuICAgICAgICBjb25zdCBldmVudHMgPSByZXNvdXJjZUlkXG4gICAgICAgICAgICA/IGF3YWl0IHRoaXMuZXZlbnRTZXJ2aWNlLmdldEJ5UmVzb3VyY2VBbmREYXRlUmFuZ2UocmVzb3VyY2VJZCwgc3RhcnREYXRlLCBlbmREYXRlKVxuICAgICAgICAgICAgOiBhd2FpdCB0aGlzLmV2ZW50U2VydmljZS5nZXRCeURhdGVSYW5nZShzdGFydERhdGUsIGVuZERhdGUpO1xuICAgICAgICAvLyBGaWx0ZXIgdG8gdGltZWQgZXZlbnRzIGFuZCBtYXRjaCBkYXRlIGV4YWN0bHlcbiAgICAgICAgY29uc3QgdGltZWRFdmVudHMgPSBldmVudHMuZmlsdGVyKGV2ZW50ID0+ICFldmVudC5hbGxEYXkgJiYgdGhpcy5kYXRlU2VydmljZS5nZXREYXRlS2V5KGV2ZW50LnN0YXJ0KSA9PT0gZGF0ZSk7XG4gICAgICAgIC8vIEdldCBvciBjcmVhdGUgZXZlbnRzIGxheWVyXG4gICAgICAgIGxldCBldmVudHNMYXllciA9IGNvbHVtbi5xdWVyeVNlbGVjdG9yKCdzd3AtZXZlbnRzLWxheWVyJyk7XG4gICAgICAgIGlmICghZXZlbnRzTGF5ZXIpIHtcbiAgICAgICAgICAgIGV2ZW50c0xheWVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3dwLWV2ZW50cy1sYXllcicpO1xuICAgICAgICAgICAgY29sdW1uLmFwcGVuZENoaWxkKGV2ZW50c0xheWVyKTtcbiAgICAgICAgfVxuICAgICAgICAvLyBDbGVhciBleGlzdGluZyBldmVudHNcbiAgICAgICAgZXZlbnRzTGF5ZXIuaW5uZXJIVE1MID0gJyc7XG4gICAgICAgIC8vIENhbGN1bGF0ZSBsYXlvdXQgd2l0aCBzdGFja2luZy9ncm91cGluZ1xuICAgICAgICBjb25zdCBsYXlvdXQgPSBjYWxjdWxhdGVDb2x1bW5MYXlvdXQodGltZWRFdmVudHMsIHRoaXMuZ3JpZENvbmZpZyk7XG4gICAgICAgIC8vIFJlbmRlciBHUklEIGdyb3Vwc1xuICAgICAgICBsYXlvdXQuZ3JpZHMuZm9yRWFjaChncmlkID0+IHtcbiAgICAgICAgICAgIGNvbnN0IGdyb3VwRWwgPSB0aGlzLnJlbmRlckdyaWRHcm91cChncmlkKTtcbiAgICAgICAgICAgIGV2ZW50c0xheWVyLmFwcGVuZENoaWxkKGdyb3VwRWwpO1xuICAgICAgICB9KTtcbiAgICAgICAgLy8gUmVuZGVyIFNUQUNLRUQgZXZlbnRzXG4gICAgICAgIGxheW91dC5zdGFja2VkLmZvckVhY2goaXRlbSA9PiB7XG4gICAgICAgICAgICBjb25zdCBldmVudEVsID0gdGhpcy5yZW5kZXJTdGFja2VkRXZlbnQoaXRlbS5ldmVudCwgaXRlbS5zdGFja0xldmVsKTtcbiAgICAgICAgICAgIGV2ZW50c0xheWVyLmFwcGVuZENoaWxkKGV2ZW50RWwpO1xuICAgICAgICB9KTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogRmluZCBhIGNvbHVtbiBlbGVtZW50IGJ5IGNvbHVtbktleVxuICAgICAqL1xuICAgIGZpbmRDb2x1bW4oY29sdW1uS2V5KSB7XG4gICAgICAgIGlmICghdGhpcy5jb250YWluZXIpXG4gICAgICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgICAgcmV0dXJuIHRoaXMuY29udGFpbmVyLnF1ZXJ5U2VsZWN0b3IoYHN3cC1kYXktY29sdW1uW2RhdGEtY29sdW1uLWtleT1cIiR7Y29sdW1uS2V5fVwiXWApO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBIYW5kbGUgZXZlbnQgbW92aW5nIHRvIGEgbmV3IGNvbHVtbiBkdXJpbmcgZHJhZ1xuICAgICAqL1xuICAgIGhhbmRsZUNvbHVtbkNoYW5nZShwYXlsb2FkKSB7XG4gICAgICAgIGNvbnN0IGV2ZW50c0xheWVyID0gcGF5bG9hZC5uZXdDb2x1bW4ucXVlcnlTZWxlY3Rvcignc3dwLWV2ZW50cy1sYXllcicpO1xuICAgICAgICBpZiAoIWV2ZW50c0xheWVyKVxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAvLyBNb3ZlIGVsZW1lbnQgdG8gbmV3IGNvbHVtblxuICAgICAgICBldmVudHNMYXllci5hcHBlbmRDaGlsZChwYXlsb2FkLmVsZW1lbnQpO1xuICAgICAgICAvLyBQcmVzZXJ2ZSBZIHBvc2l0aW9uXG4gICAgICAgIHBheWxvYWQuZWxlbWVudC5zdHlsZS50b3AgPSBgJHtwYXlsb2FkLmN1cnJlbnRZfXB4YDtcbiAgICB9XG4gICAgLyoqXG4gICAgICogVXBkYXRlIHRpbWVzdGFtcCBkaXNwbGF5IGR1cmluZyBkcmFnIChzbmFwcGVkIHRvIGdyaWQpXG4gICAgICovXG4gICAgdXBkYXRlRHJhZ1RpbWVzdGFtcChwYXlsb2FkKSB7XG4gICAgICAgIGNvbnN0IHRpbWVFbCA9IHBheWxvYWQuZWxlbWVudC5xdWVyeVNlbGVjdG9yKCdzd3AtZXZlbnQtdGltZScpO1xuICAgICAgICBpZiAoIXRpbWVFbClcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgLy8gU25hcCBwb3NpdGlvbiB0byBncmlkIGludGVydmFsXG4gICAgICAgIGNvbnN0IHNuYXBwZWRZID0gc25hcFRvR3JpZChwYXlsb2FkLmN1cnJlbnRZLCB0aGlzLmdyaWRDb25maWcpO1xuICAgICAgICAvLyBDYWxjdWxhdGUgbmV3IHN0YXJ0IHRpbWVcbiAgICAgICAgY29uc3QgbWludXRlc0Zyb21HcmlkU3RhcnQgPSBwaXhlbHNUb01pbnV0ZXMoc25hcHBlZFksIHRoaXMuZ3JpZENvbmZpZyk7XG4gICAgICAgIGNvbnN0IHN0YXJ0TWludXRlcyA9ICh0aGlzLmdyaWRDb25maWcuZGF5U3RhcnRIb3VyICogNjApICsgbWludXRlc0Zyb21HcmlkU3RhcnQ7XG4gICAgICAgIC8vIEtlZXAgb3JpZ2luYWwgZHVyYXRpb24gKGZyb20gZWxlbWVudCBoZWlnaHQpXG4gICAgICAgIGNvbnN0IGhlaWdodCA9IHBhcnNlRmxvYXQocGF5bG9hZC5lbGVtZW50LnN0eWxlLmhlaWdodCkgfHwgdGhpcy5ncmlkQ29uZmlnLmhvdXJIZWlnaHQ7XG4gICAgICAgIGNvbnN0IGR1cmF0aW9uTWludXRlcyA9IHBpeGVsc1RvTWludXRlcyhoZWlnaHQsIHRoaXMuZ3JpZENvbmZpZyk7XG4gICAgICAgIC8vIENyZWF0ZSBEYXRlIG9iamVjdHMgZm9yIGNvbnNpc3RlbnQgZm9ybWF0dGluZyB2aWEgRGF0ZVNlcnZpY2VcbiAgICAgICAgY29uc3Qgc3RhcnQgPSB0aGlzLm1pbnV0ZXNUb0RhdGUoc3RhcnRNaW51dGVzKTtcbiAgICAgICAgY29uc3QgZW5kID0gdGhpcy5taW51dGVzVG9EYXRlKHN0YXJ0TWludXRlcyArIGR1cmF0aW9uTWludXRlcyk7XG4gICAgICAgIHRpbWVFbC50ZXh0Q29udGVudCA9IHRoaXMuZGF0ZVNlcnZpY2UuZm9ybWF0VGltZVJhbmdlKHN0YXJ0LCBlbmQpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBDb252ZXJ0IG1pbnV0ZXMgc2luY2UgbWlkbmlnaHQgdG8gYSBEYXRlIG9iamVjdCAodG9kYXkpXG4gICAgICovXG4gICAgbWludXRlc1RvRGF0ZShtaW51dGVzKSB7XG4gICAgICAgIGNvbnN0IGRhdGUgPSBuZXcgRGF0ZSgpO1xuICAgICAgICBkYXRlLnNldEhvdXJzKE1hdGguZmxvb3IobWludXRlcyAvIDYwKSAlIDI0LCBtaW51dGVzICUgNjAsIDAsIDApO1xuICAgICAgICByZXR1cm4gZGF0ZTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogUmVuZGVyIGV2ZW50cyBmb3IgdmlzaWJsZSBkYXRlcyBpbnRvIGRheSBjb2x1bW5zXG4gICAgICogQHBhcmFtIGNvbnRhaW5lciAtIENhbGVuZGFyIGNvbnRhaW5lciBlbGVtZW50XG4gICAgICogQHBhcmFtIGZpbHRlciAtIEZpbHRlciB3aXRoICdkYXRlJyBhbmQgb3B0aW9uYWxseSAncmVzb3VyY2UnIGFycmF5c1xuICAgICAqIEBwYXJhbSBmaWx0ZXJUZW1wbGF0ZSAtIFRlbXBsYXRlIGZvciBtYXRjaGluZyBldmVudHMgdG8gY29sdW1uc1xuICAgICAqL1xuICAgIGFzeW5jIHJlbmRlcihjb250YWluZXIsIGZpbHRlciwgZmlsdGVyVGVtcGxhdGUpIHtcbiAgICAgICAgLy8gU3RvcmUgY29udGFpbmVyIHJlZmVyZW5jZSBmb3IgbGF0ZXIgcmUtcmVuZGVyc1xuICAgICAgICB0aGlzLmNvbnRhaW5lciA9IGNvbnRhaW5lcjtcbiAgICAgICAgY29uc3QgdmlzaWJsZURhdGVzID0gZmlsdGVyWydkYXRlJ10gfHwgW107XG4gICAgICAgIGlmICh2aXNpYmxlRGF0ZXMubGVuZ3RoID09PSAwKVxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAvLyBHZXQgZGF0ZSByYW5nZSBmb3IgcXVlcnlcbiAgICAgICAgY29uc3Qgc3RhcnREYXRlID0gbmV3IERhdGUodmlzaWJsZURhdGVzWzBdKTtcbiAgICAgICAgY29uc3QgZW5kRGF0ZSA9IG5ldyBEYXRlKHZpc2libGVEYXRlc1t2aXNpYmxlRGF0ZXMubGVuZ3RoIC0gMV0pO1xuICAgICAgICBlbmREYXRlLnNldEhvdXJzKDIzLCA1OSwgNTksIDk5OSk7XG4gICAgICAgIC8vIEZldGNoIGV2ZW50cyBmcm9tIEluZGV4ZWREQlxuICAgICAgICBjb25zdCBldmVudHMgPSBhd2FpdCB0aGlzLmV2ZW50U2VydmljZS5nZXRCeURhdGVSYW5nZShzdGFydERhdGUsIGVuZERhdGUpO1xuICAgICAgICAvLyBGaW5kIGRheSBjb2x1bW5zXG4gICAgICAgIGNvbnN0IGRheUNvbHVtbnMgPSBjb250YWluZXIucXVlcnlTZWxlY3Rvcignc3dwLWRheS1jb2x1bW5zJyk7XG4gICAgICAgIGlmICghZGF5Q29sdW1ucylcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgY29uc3QgY29sdW1ucyA9IGRheUNvbHVtbnMucXVlcnlTZWxlY3RvckFsbCgnc3dwLWRheS1jb2x1bW4nKTtcbiAgICAgICAgLy8gUmVuZGVyIGV2ZW50cyBpbnRvIGVhY2ggY29sdW1uIGJhc2VkIG9uIEZpbHRlclRlbXBsYXRlIG1hdGNoaW5nXG4gICAgICAgIGNvbHVtbnMuZm9yRWFjaChjb2x1bW4gPT4ge1xuICAgICAgICAgICAgY29uc3QgY29sdW1uRWwgPSBjb2x1bW47XG4gICAgICAgICAgICAvLyBVc2UgRmlsdGVyVGVtcGxhdGUgZm9yIG1hdGNoaW5nIC0gb25seSBmaWVsZHMgaW4gdGVtcGxhdGUgYXJlIGNoZWNrZWRcbiAgICAgICAgICAgIGNvbnN0IGNvbHVtbkV2ZW50cyA9IGV2ZW50cy5maWx0ZXIoZXZlbnQgPT4gZmlsdGVyVGVtcGxhdGUubWF0Y2hlcyhldmVudCwgY29sdW1uRWwpKTtcbiAgICAgICAgICAgIC8vIEdldCBvciBjcmVhdGUgZXZlbnRzIGxheWVyXG4gICAgICAgICAgICBsZXQgZXZlbnRzTGF5ZXIgPSBjb2x1bW4ucXVlcnlTZWxlY3Rvcignc3dwLWV2ZW50cy1sYXllcicpO1xuICAgICAgICAgICAgaWYgKCFldmVudHNMYXllcikge1xuICAgICAgICAgICAgICAgIGV2ZW50c0xheWVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3dwLWV2ZW50cy1sYXllcicpO1xuICAgICAgICAgICAgICAgIGNvbHVtbi5hcHBlbmRDaGlsZChldmVudHNMYXllcik7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyBDbGVhciBleGlzdGluZyBldmVudHNcbiAgICAgICAgICAgIGV2ZW50c0xheWVyLmlubmVySFRNTCA9ICcnO1xuICAgICAgICAgICAgLy8gRmlsdGVyIHRvIHRpbWVkIGV2ZW50cyBvbmx5XG4gICAgICAgICAgICBjb25zdCB0aW1lZEV2ZW50cyA9IGNvbHVtbkV2ZW50cy5maWx0ZXIoZXZlbnQgPT4gIWV2ZW50LmFsbERheSk7XG4gICAgICAgICAgICAvLyBDYWxjdWxhdGUgbGF5b3V0IHdpdGggc3RhY2tpbmcvZ3JvdXBpbmdcbiAgICAgICAgICAgIGNvbnN0IGxheW91dCA9IGNhbGN1bGF0ZUNvbHVtbkxheW91dCh0aW1lZEV2ZW50cywgdGhpcy5ncmlkQ29uZmlnKTtcbiAgICAgICAgICAgIC8vIFJlbmRlciBHUklEIGdyb3VwcyAoc2ltdWx0YW5lb3VzIGV2ZW50cyBzaWRlLWJ5LXNpZGUpXG4gICAgICAgICAgICBsYXlvdXQuZ3JpZHMuZm9yRWFjaChncmlkID0+IHtcbiAgICAgICAgICAgICAgICBjb25zdCBncm91cEVsID0gdGhpcy5yZW5kZXJHcmlkR3JvdXAoZ3JpZCk7XG4gICAgICAgICAgICAgICAgZXZlbnRzTGF5ZXIuYXBwZW5kQ2hpbGQoZ3JvdXBFbCk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIC8vIFJlbmRlciBTVEFDS0VEIGV2ZW50cyAob3ZlcmxhcHBpbmcgd2l0aCBtYXJnaW4gb2Zmc2V0KVxuICAgICAgICAgICAgbGF5b3V0LnN0YWNrZWQuZm9yRWFjaChpdGVtID0+IHtcbiAgICAgICAgICAgICAgICBjb25zdCBldmVudEVsID0gdGhpcy5yZW5kZXJTdGFja2VkRXZlbnQoaXRlbS5ldmVudCwgaXRlbS5zdGFja0xldmVsKTtcbiAgICAgICAgICAgICAgICBldmVudHNMYXllci5hcHBlbmRDaGlsZChldmVudEVsKTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogQ3JlYXRlIGEgc2luZ2xlIGV2ZW50IGVsZW1lbnRcbiAgICAgKlxuICAgICAqIENMRUFOIGFwcHJvYWNoOlxuICAgICAqIC0gT25seSBkYXRhLWlkIGZvciBsb29rdXBcbiAgICAgKiAtIFZpc2libGUgY29udGVudCBpbiBpbm5lckhUTUwgb25seVxuICAgICAqL1xuICAgIGNyZWF0ZUV2ZW50RWxlbWVudChldmVudCkge1xuICAgICAgICBjb25zdCBlbGVtZW50ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3dwLWV2ZW50Jyk7XG4gICAgICAgIC8vIERhdGEgYXR0cmlidXRlcyBmb3IgU3dwRXZlbnQgY29tcGF0aWJpbGl0eVxuICAgICAgICBlbGVtZW50LmRhdGFzZXQuZXZlbnRJZCA9IGV2ZW50LmlkO1xuICAgICAgICBpZiAoZXZlbnQucmVzb3VyY2VJZCkge1xuICAgICAgICAgICAgZWxlbWVudC5kYXRhc2V0LnJlc291cmNlSWQgPSBldmVudC5yZXNvdXJjZUlkO1xuICAgICAgICB9XG4gICAgICAgIC8vIENhbGN1bGF0ZSBwb3NpdGlvblxuICAgICAgICBjb25zdCBwb3NpdGlvbiA9IGNhbGN1bGF0ZUV2ZW50UG9zaXRpb24oZXZlbnQuc3RhcnQsIGV2ZW50LmVuZCwgdGhpcy5ncmlkQ29uZmlnKTtcbiAgICAgICAgZWxlbWVudC5zdHlsZS50b3AgPSBgJHtwb3NpdGlvbi50b3B9cHhgO1xuICAgICAgICBlbGVtZW50LnN0eWxlLmhlaWdodCA9IGAke3Bvc2l0aW9uLmhlaWdodH1weGA7XG4gICAgICAgIC8vIENvbG9yIGNsYXNzIGJhc2VkIG9uIGV2ZW50IHR5cGVcbiAgICAgICAgY29uc3QgY29sb3JDbGFzcyA9IHRoaXMuZ2V0Q29sb3JDbGFzcyhldmVudCk7XG4gICAgICAgIGlmIChjb2xvckNsYXNzKSB7XG4gICAgICAgICAgICBlbGVtZW50LmNsYXNzTGlzdC5hZGQoY29sb3JDbGFzcyk7XG4gICAgICAgIH1cbiAgICAgICAgLy8gVmlzaWJsZSBjb250ZW50IG9ubHlcbiAgICAgICAgZWxlbWVudC5pbm5lckhUTUwgPSBgXHJcbiAgICAgIDxzd3AtZXZlbnQtdGltZT4ke3RoaXMuZGF0ZVNlcnZpY2UuZm9ybWF0VGltZVJhbmdlKGV2ZW50LnN0YXJ0LCBldmVudC5lbmQpfTwvc3dwLWV2ZW50LXRpbWU+XHJcbiAgICAgIDxzd3AtZXZlbnQtdGl0bGU+JHt0aGlzLmVzY2FwZUh0bWwoZXZlbnQudGl0bGUpfTwvc3dwLWV2ZW50LXRpdGxlPlxyXG4gICAgICAke2V2ZW50LmRlc2NyaXB0aW9uID8gYDxzd3AtZXZlbnQtZGVzY3JpcHRpb24+JHt0aGlzLmVzY2FwZUh0bWwoZXZlbnQuZGVzY3JpcHRpb24pfTwvc3dwLWV2ZW50LWRlc2NyaXB0aW9uPmAgOiAnJ31cclxuICAgIGA7XG4gICAgICAgIHJldHVybiBlbGVtZW50O1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBHZXQgY29sb3IgY2xhc3MgYmFzZWQgb24gbWV0YWRhdGEuY29sb3Igb3IgZXZlbnQgdHlwZVxuICAgICAqL1xuICAgIGdldENvbG9yQ2xhc3MoZXZlbnQpIHtcbiAgICAgICAgLy8gQ2hlY2sgbWV0YWRhdGEuY29sb3IgZmlyc3RcbiAgICAgICAgaWYgKGV2ZW50Lm1ldGFkYXRhPy5jb2xvcikge1xuICAgICAgICAgICAgcmV0dXJuIGBpcy0ke2V2ZW50Lm1ldGFkYXRhLmNvbG9yfWA7XG4gICAgICAgIH1cbiAgICAgICAgLy8gRmFsbGJhY2sgdG8gdHlwZS1iYXNlZCBjb2xvclxuICAgICAgICBjb25zdCB0eXBlQ29sb3JzID0ge1xuICAgICAgICAgICAgJ2N1c3RvbWVyJzogJ2lzLWJsdWUnLFxuICAgICAgICAgICAgJ3ZhY2F0aW9uJzogJ2lzLWdyZWVuJyxcbiAgICAgICAgICAgICdicmVhayc6ICdpcy1hbWJlcicsXG4gICAgICAgICAgICAnbWVldGluZyc6ICdpcy1wdXJwbGUnLFxuICAgICAgICAgICAgJ2Jsb2NrZWQnOiAnaXMtcmVkJ1xuICAgICAgICB9O1xuICAgICAgICByZXR1cm4gdHlwZUNvbG9yc1tldmVudC50eXBlXSB8fCAnaXMtYmx1ZSc7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEVzY2FwZSBIVE1MIHRvIHByZXZlbnQgWFNTXG4gICAgICovXG4gICAgZXNjYXBlSHRtbCh0ZXh0KSB7XG4gICAgICAgIGNvbnN0IGRpdiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpO1xuICAgICAgICBkaXYudGV4dENvbnRlbnQgPSB0ZXh0O1xuICAgICAgICByZXR1cm4gZGl2LmlubmVySFRNTDtcbiAgICB9XG4gICAgLyoqXG4gICAgICogUmVuZGVyIGEgR1JJRCBncm91cCB3aXRoIHNpZGUtYnktc2lkZSBjb2x1bW5zXG4gICAgICogVXNlZCB3aGVuIG11bHRpcGxlIGV2ZW50cyBzdGFydCBhdCB0aGUgc2FtZSB0aW1lXG4gICAgICovXG4gICAgcmVuZGVyR3JpZEdyb3VwKGxheW91dCkge1xuICAgICAgICBjb25zdCBncm91cCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3N3cC1ldmVudC1ncm91cCcpO1xuICAgICAgICBncm91cC5jbGFzc0xpc3QuYWRkKGBjb2xzLSR7bGF5b3V0LmNvbHVtbnMubGVuZ3RofWApO1xuICAgICAgICBncm91cC5zdHlsZS50b3AgPSBgJHtsYXlvdXQucG9zaXRpb24udG9wfXB4YDtcbiAgICAgICAgLy8gU3RhY2sgbGV2ZWwgc3R5bGluZyBmb3IgZW50aXJlIGdyb3VwIChpZiBuZXN0ZWQgaW4gYW5vdGhlciBldmVudClcbiAgICAgICAgaWYgKGxheW91dC5zdGFja0xldmVsID4gMCkge1xuICAgICAgICAgICAgZ3JvdXAuc3R5bGUubWFyZ2luTGVmdCA9IGAke2xheW91dC5zdGFja0xldmVsICogMTV9cHhgO1xuICAgICAgICAgICAgZ3JvdXAuc3R5bGUuekluZGV4ID0gYCR7MTAwICsgbGF5b3V0LnN0YWNrTGV2ZWx9YDtcbiAgICAgICAgfVxuICAgICAgICAvLyBDYWxjdWxhdGUgdGhlIGhlaWdodCBuZWVkZWQgZm9yIHRoZSBncm91cCAodGFsbGVzdCBldmVudClcbiAgICAgICAgbGV0IG1heEJvdHRvbSA9IDA7XG4gICAgICAgIGZvciAoY29uc3QgZXZlbnQgb2YgbGF5b3V0LmV2ZW50cykge1xuICAgICAgICAgICAgY29uc3QgcG9zID0gY2FsY3VsYXRlRXZlbnRQb3NpdGlvbihldmVudC5zdGFydCwgZXZlbnQuZW5kLCB0aGlzLmdyaWRDb25maWcpO1xuICAgICAgICAgICAgY29uc3QgZXZlbnRCb3R0b20gPSBwb3MudG9wICsgcG9zLmhlaWdodDtcbiAgICAgICAgICAgIGlmIChldmVudEJvdHRvbSA+IG1heEJvdHRvbSlcbiAgICAgICAgICAgICAgICBtYXhCb3R0b20gPSBldmVudEJvdHRvbTtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBncm91cEhlaWdodCA9IG1heEJvdHRvbSAtIGxheW91dC5wb3NpdGlvbi50b3A7XG4gICAgICAgIGdyb3VwLnN0eWxlLmhlaWdodCA9IGAke2dyb3VwSGVpZ2h0fXB4YDtcbiAgICAgICAgLy8gQ3JlYXRlIHdyYXBwZXIgZGl2IGZvciBlYWNoIGNvbHVtblxuICAgICAgICBsYXlvdXQuY29sdW1ucy5mb3JFYWNoKGNvbHVtbkV2ZW50cyA9PiB7XG4gICAgICAgICAgICBjb25zdCB3cmFwcGVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XG4gICAgICAgICAgICB3cmFwcGVyLnN0eWxlLnBvc2l0aW9uID0gJ3JlbGF0aXZlJztcbiAgICAgICAgICAgIGNvbHVtbkV2ZW50cy5mb3JFYWNoKGV2ZW50ID0+IHtcbiAgICAgICAgICAgICAgICBjb25zdCBldmVudEVsID0gdGhpcy5jcmVhdGVFdmVudEVsZW1lbnQoZXZlbnQpO1xuICAgICAgICAgICAgICAgIC8vIFBvc2l0aW9uIHJlbGF0aXZlIHRvIGdyb3VwIHRvcFxuICAgICAgICAgICAgICAgIGNvbnN0IHBvcyA9IGNhbGN1bGF0ZUV2ZW50UG9zaXRpb24oZXZlbnQuc3RhcnQsIGV2ZW50LmVuZCwgdGhpcy5ncmlkQ29uZmlnKTtcbiAgICAgICAgICAgICAgICBldmVudEVsLnN0eWxlLnRvcCA9IGAke3Bvcy50b3AgLSBsYXlvdXQucG9zaXRpb24udG9wfXB4YDtcbiAgICAgICAgICAgICAgICBldmVudEVsLnN0eWxlLnBvc2l0aW9uID0gJ2Fic29sdXRlJztcbiAgICAgICAgICAgICAgICBldmVudEVsLnN0eWxlLmxlZnQgPSAnMCc7XG4gICAgICAgICAgICAgICAgZXZlbnRFbC5zdHlsZS5yaWdodCA9ICcwJztcbiAgICAgICAgICAgICAgICB3cmFwcGVyLmFwcGVuZENoaWxkKGV2ZW50RWwpO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICBncm91cC5hcHBlbmRDaGlsZCh3cmFwcGVyKTtcbiAgICAgICAgfSk7XG4gICAgICAgIHJldHVybiBncm91cDtcbiAgICB9XG4gICAgLyoqXG4gICAgICogUmVuZGVyIGEgU1RBQ0tFRCBldmVudCB3aXRoIG1hcmdpbi1sZWZ0IG9mZnNldFxuICAgICAqIFVzZWQgZm9yIG92ZXJsYXBwaW5nIGV2ZW50cyB0aGF0IGRvbid0IHN0YXJ0IGF0IHRoZSBzYW1lIHRpbWVcbiAgICAgKi9cbiAgICByZW5kZXJTdGFja2VkRXZlbnQoZXZlbnQsIHN0YWNrTGV2ZWwpIHtcbiAgICAgICAgY29uc3QgZWxlbWVudCA9IHRoaXMuY3JlYXRlRXZlbnRFbGVtZW50KGV2ZW50KTtcbiAgICAgICAgLy8gQWRkIHN0YWNrIG1ldGFkYXRhIGZvciBkcmFnLWRyb3AgYW5kIG90aGVyIGZlYXR1cmVzXG4gICAgICAgIGVsZW1lbnQuZGF0YXNldC5zdGFja0xpbmsgPSBKU09OLnN0cmluZ2lmeSh7IHN0YWNrTGV2ZWwgfSk7XG4gICAgICAgIC8vIFZpc3VhbCBzdHlsaW5nIGJhc2VkIG9uIHN0YWNrIGxldmVsXG4gICAgICAgIGlmIChzdGFja0xldmVsID4gMCkge1xuICAgICAgICAgICAgZWxlbWVudC5zdHlsZS5tYXJnaW5MZWZ0ID0gYCR7c3RhY2tMZXZlbCAqIDE1fXB4YDtcbiAgICAgICAgICAgIGVsZW1lbnQuc3R5bGUuekluZGV4ID0gYCR7MTAwICsgc3RhY2tMZXZlbH1gO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBlbGVtZW50O1xuICAgIH1cbn1cbiIsICIvKipcbiAqIFNjaGVkdWxlUmVuZGVyZXIgLSBSZW5kZXJzIHVuYXZhaWxhYmxlIHRpbWUgem9uZXMgaW4gZGF5IGNvbHVtbnNcbiAqXG4gKiBDcmVhdGVzIHZpc3VhbCBpbmRpY2F0b3JzIGZvciB0aW1lcyBvdXRzaWRlIHRoZSByZXNvdXJjZSdzIHdvcmtpbmcgaG91cnM6XG4gKiAtIEJlZm9yZSB3b3JrIHN0YXJ0IChlLmcuLCAwNjowMCAtIDA5OjAwKVxuICogLSBBZnRlciB3b3JrIGVuZCAoZS5nLiwgMTc6MDAgLSAxODowMClcbiAqIC0gRnVsbCBkYXkgaWYgcmVzb3VyY2UgaXMgb2ZmIChzY2hlZHVsZSA9IG51bGwpXG4gKi9cbmV4cG9ydCBjbGFzcyBTY2hlZHVsZVJlbmRlcmVyIHtcbiAgICBjb25zdHJ1Y3RvcihzY2hlZHVsZVNlcnZpY2UsIGRhdGVTZXJ2aWNlLCBncmlkQ29uZmlnKSB7XG4gICAgICAgIHRoaXMuc2NoZWR1bGVTZXJ2aWNlID0gc2NoZWR1bGVTZXJ2aWNlO1xuICAgICAgICB0aGlzLmRhdGVTZXJ2aWNlID0gZGF0ZVNlcnZpY2U7XG4gICAgICAgIHRoaXMuZ3JpZENvbmZpZyA9IGdyaWRDb25maWc7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFJlbmRlciB1bmF2YWlsYWJsZSB6b25lcyBmb3IgdmlzaWJsZSBjb2x1bW5zXG4gICAgICogQHBhcmFtIGNvbnRhaW5lciAtIENhbGVuZGFyIGNvbnRhaW5lciBlbGVtZW50XG4gICAgICogQHBhcmFtIGZpbHRlciAtIEZpbHRlciB3aXRoICdkYXRlJyBhbmQgJ3Jlc291cmNlJyBhcnJheXNcbiAgICAgKi9cbiAgICBhc3luYyByZW5kZXIoY29udGFpbmVyLCBmaWx0ZXIpIHtcbiAgICAgICAgY29uc3QgZGF0ZXMgPSBmaWx0ZXJbJ2RhdGUnXSB8fCBbXTtcbiAgICAgICAgY29uc3QgcmVzb3VyY2VJZHMgPSBmaWx0ZXJbJ3Jlc291cmNlJ10gfHwgW107XG4gICAgICAgIGlmIChkYXRlcy5sZW5ndGggPT09IDApXG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIC8vIEZpbmQgZGF5IGNvbHVtbnNcbiAgICAgICAgY29uc3QgZGF5Q29sdW1ucyA9IGNvbnRhaW5lci5xdWVyeVNlbGVjdG9yKCdzd3AtZGF5LWNvbHVtbnMnKTtcbiAgICAgICAgaWYgKCFkYXlDb2x1bW5zKVxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICBjb25zdCBjb2x1bW5zID0gZGF5Q29sdW1ucy5xdWVyeVNlbGVjdG9yQWxsKCdzd3AtZGF5LWNvbHVtbicpO1xuICAgICAgICBmb3IgKGNvbnN0IGNvbHVtbiBvZiBjb2x1bW5zKSB7XG4gICAgICAgICAgICBjb25zdCBkYXRlID0gY29sdW1uLmRhdGFzZXQuZGF0ZTtcbiAgICAgICAgICAgIGNvbnN0IHJlc291cmNlSWQgPSBjb2x1bW4uZGF0YXNldC5yZXNvdXJjZUlkO1xuICAgICAgICAgICAgaWYgKCFkYXRlIHx8ICFyZXNvdXJjZUlkKVxuICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgICAgLy8gR2V0IG9yIGNyZWF0ZSB1bmF2YWlsYWJsZSBsYXllclxuICAgICAgICAgICAgbGV0IHVuYXZhaWxhYmxlTGF5ZXIgPSBjb2x1bW4ucXVlcnlTZWxlY3Rvcignc3dwLXVuYXZhaWxhYmxlLWxheWVyJyk7XG4gICAgICAgICAgICBpZiAoIXVuYXZhaWxhYmxlTGF5ZXIpIHtcbiAgICAgICAgICAgICAgICB1bmF2YWlsYWJsZUxheWVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3dwLXVuYXZhaWxhYmxlLWxheWVyJyk7XG4gICAgICAgICAgICAgICAgY29sdW1uLmluc2VydEJlZm9yZSh1bmF2YWlsYWJsZUxheWVyLCBjb2x1bW4uZmlyc3RDaGlsZCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyBDbGVhciBleGlzdGluZ1xuICAgICAgICAgICAgdW5hdmFpbGFibGVMYXllci5pbm5lckhUTUwgPSAnJztcbiAgICAgICAgICAgIC8vIEdldCBzY2hlZHVsZSBmb3IgdGhpcyByZXNvdXJjZS9kYXRlXG4gICAgICAgICAgICBjb25zdCBzY2hlZHVsZSA9IGF3YWl0IHRoaXMuc2NoZWR1bGVTZXJ2aWNlLmdldFNjaGVkdWxlRm9yRGF0ZShyZXNvdXJjZUlkLCBkYXRlKTtcbiAgICAgICAgICAgIC8vIFJlbmRlciB1bmF2YWlsYWJsZSB6b25lc1xuICAgICAgICAgICAgdGhpcy5yZW5kZXJVbmF2YWlsYWJsZVpvbmVzKHVuYXZhaWxhYmxlTGF5ZXIsIHNjaGVkdWxlKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICAvKipcbiAgICAgKiBSZW5kZXIgdW5hdmFpbGFibGUgdGltZSB6b25lcyBiYXNlZCBvbiBzY2hlZHVsZVxuICAgICAqL1xuICAgIHJlbmRlclVuYXZhaWxhYmxlWm9uZXMobGF5ZXIsIHNjaGVkdWxlKSB7XG4gICAgICAgIGNvbnN0IGRheVN0YXJ0TWludXRlcyA9IHRoaXMuZ3JpZENvbmZpZy5kYXlTdGFydEhvdXIgKiA2MDtcbiAgICAgICAgY29uc3QgZGF5RW5kTWludXRlcyA9IHRoaXMuZ3JpZENvbmZpZy5kYXlFbmRIb3VyICogNjA7XG4gICAgICAgIGNvbnN0IG1pbnV0ZUhlaWdodCA9IHRoaXMuZ3JpZENvbmZpZy5ob3VySGVpZ2h0IC8gNjA7XG4gICAgICAgIGlmIChzY2hlZHVsZSA9PT0gbnVsbCkge1xuICAgICAgICAgICAgLy8gRnVsbCBkYXkgdW5hdmFpbGFibGVcbiAgICAgICAgICAgIGNvbnN0IHpvbmUgPSB0aGlzLmNyZWF0ZVVuYXZhaWxhYmxlWm9uZSgwLCAoZGF5RW5kTWludXRlcyAtIGRheVN0YXJ0TWludXRlcykgKiBtaW51dGVIZWlnaHQpO1xuICAgICAgICAgICAgbGF5ZXIuYXBwZW5kQ2hpbGQoem9uZSk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgY29uc3Qgd29ya1N0YXJ0TWludXRlcyA9IHRoaXMuZGF0ZVNlcnZpY2UudGltZVRvTWludXRlcyhzY2hlZHVsZS5zdGFydCk7XG4gICAgICAgIGNvbnN0IHdvcmtFbmRNaW51dGVzID0gdGhpcy5kYXRlU2VydmljZS50aW1lVG9NaW51dGVzKHNjaGVkdWxlLmVuZCk7XG4gICAgICAgIC8vIEJlZm9yZSB3b3JrIHN0YXJ0XG4gICAgICAgIGlmICh3b3JrU3RhcnRNaW51dGVzID4gZGF5U3RhcnRNaW51dGVzKSB7XG4gICAgICAgICAgICBjb25zdCB0b3AgPSAwO1xuICAgICAgICAgICAgY29uc3QgaGVpZ2h0ID0gKHdvcmtTdGFydE1pbnV0ZXMgLSBkYXlTdGFydE1pbnV0ZXMpICogbWludXRlSGVpZ2h0O1xuICAgICAgICAgICAgY29uc3Qgem9uZSA9IHRoaXMuY3JlYXRlVW5hdmFpbGFibGVab25lKHRvcCwgaGVpZ2h0KTtcbiAgICAgICAgICAgIGxheWVyLmFwcGVuZENoaWxkKHpvbmUpO1xuICAgICAgICB9XG4gICAgICAgIC8vIEFmdGVyIHdvcmsgZW5kXG4gICAgICAgIGlmICh3b3JrRW5kTWludXRlcyA8IGRheUVuZE1pbnV0ZXMpIHtcbiAgICAgICAgICAgIGNvbnN0IHRvcCA9ICh3b3JrRW5kTWludXRlcyAtIGRheVN0YXJ0TWludXRlcykgKiBtaW51dGVIZWlnaHQ7XG4gICAgICAgICAgICBjb25zdCBoZWlnaHQgPSAoZGF5RW5kTWludXRlcyAtIHdvcmtFbmRNaW51dGVzKSAqIG1pbnV0ZUhlaWdodDtcbiAgICAgICAgICAgIGNvbnN0IHpvbmUgPSB0aGlzLmNyZWF0ZVVuYXZhaWxhYmxlWm9uZSh0b3AsIGhlaWdodCk7XG4gICAgICAgICAgICBsYXllci5hcHBlbmRDaGlsZCh6b25lKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICAvKipcbiAgICAgKiBDcmVhdGUgYW4gdW5hdmFpbGFibGUgem9uZSBlbGVtZW50XG4gICAgICovXG4gICAgY3JlYXRlVW5hdmFpbGFibGVab25lKHRvcCwgaGVpZ2h0KSB7XG4gICAgICAgIGNvbnN0IHpvbmUgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzd3AtdW5hdmFpbGFibGUtem9uZScpO1xuICAgICAgICB6b25lLnN0eWxlLnRvcCA9IGAke3RvcH1weGA7XG4gICAgICAgIHpvbmUuc3R5bGUuaGVpZ2h0ID0gYCR7aGVpZ2h0fXB4YDtcbiAgICAgICAgcmV0dXJuIHpvbmU7XG4gICAgfVxufVxuIiwgImltcG9ydCB7IENvcmVFdmVudHMgfSBmcm9tICcuLi8uLi9jb25zdGFudHMvQ29yZUV2ZW50cyc7XG4vKipcbiAqIEhlYWRlckRyYXdlclJlbmRlcmVyIC0gSGFuZGxlcyByZW5kZXJpbmcgb2YgaXRlbXMgaW4gdGhlIGhlYWRlciBkcmF3ZXJcbiAqXG4gKiBMaXN0ZW5zIHRvIGRyYWcgZXZlbnRzIGZyb20gRHJhZ0Ryb3BNYW5hZ2VyIGFuZCBjcmVhdGVzL21hbmFnZXNcbiAqIHN3cC1oZWFkZXItaXRlbSBlbGVtZW50cyBpbiB0aGUgaGVhZGVyIGRyYXdlci5cbiAqXG4gKiBVc2VzIHN1YmdyaWQgZm9yIGNvbHVtbiBhbGlnbm1lbnQgd2l0aCBwYXJlbnQgc3dwLWNhbGVuZGFyLWhlYWRlci5cbiAqIFBvc2l0aW9uIGl0ZW1zIHZpYSBncmlkQXJlYSBmb3IgZXhwbGljaXQgcm93L2NvbHVtbiBwbGFjZW1lbnQuXG4gKi9cbmV4cG9ydCBjbGFzcyBIZWFkZXJEcmF3ZXJSZW5kZXJlciB7XG4gICAgY29uc3RydWN0b3IoZXZlbnRCdXMsIGdyaWRDb25maWcsIGhlYWRlckRyYXdlck1hbmFnZXIsIGV2ZW50U2VydmljZSwgZGF0ZVNlcnZpY2UpIHtcbiAgICAgICAgdGhpcy5ldmVudEJ1cyA9IGV2ZW50QnVzO1xuICAgICAgICB0aGlzLmdyaWRDb25maWcgPSBncmlkQ29uZmlnO1xuICAgICAgICB0aGlzLmhlYWRlckRyYXdlck1hbmFnZXIgPSBoZWFkZXJEcmF3ZXJNYW5hZ2VyO1xuICAgICAgICB0aGlzLmV2ZW50U2VydmljZSA9IGV2ZW50U2VydmljZTtcbiAgICAgICAgdGhpcy5kYXRlU2VydmljZSA9IGRhdGVTZXJ2aWNlO1xuICAgICAgICB0aGlzLmN1cnJlbnRJdGVtID0gbnVsbDtcbiAgICAgICAgdGhpcy5jb250YWluZXIgPSBudWxsO1xuICAgICAgICB0aGlzLnNvdXJjZUVsZW1lbnQgPSBudWxsO1xuICAgICAgICB0aGlzLndhc0V4cGFuZGVkQmVmb3JlRHJhZyA9IGZhbHNlO1xuICAgICAgICB0aGlzLmZpbHRlclRlbXBsYXRlID0gbnVsbDtcbiAgICAgICAgdGhpcy5zZXR1cExpc3RlbmVycygpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBSZW5kZXIgYWxsRGF5IGV2ZW50cyBpbnRvIHRoZSBoZWFkZXIgZHJhd2VyIHdpdGggcm93IHN0YWNraW5nXG4gICAgICogQHBhcmFtIGZpbHRlclRlbXBsYXRlIC0gVGVtcGxhdGUgZm9yIG1hdGNoaW5nIGV2ZW50cyB0byBjb2x1bW5zXG4gICAgICovXG4gICAgYXN5bmMgcmVuZGVyKGNvbnRhaW5lciwgZmlsdGVyLCBmaWx0ZXJUZW1wbGF0ZSkge1xuICAgICAgICAvLyBTdG9yZSBmaWx0ZXJUZW1wbGF0ZSBmb3IgYnVpbGRDb2x1bW5LZXlGcm9tRXZlbnRcbiAgICAgICAgdGhpcy5maWx0ZXJUZW1wbGF0ZSA9IGZpbHRlclRlbXBsYXRlO1xuICAgICAgICBjb25zdCBkcmF3ZXIgPSBjb250YWluZXIucXVlcnlTZWxlY3Rvcignc3dwLWhlYWRlci1kcmF3ZXInKTtcbiAgICAgICAgaWYgKCFkcmF3ZXIpXG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIGNvbnN0IHZpc2libGVEYXRlcyA9IGZpbHRlclsnZGF0ZSddIHx8IFtdO1xuICAgICAgICBpZiAodmlzaWJsZURhdGVzLmxlbmd0aCA9PT0gMClcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgLy8gR2V0IGNvbHVtbiBrZXlzIGZyb20gRE9NIGZvciBjb3JyZWN0IG11bHRpLXJlc291cmNlIHBvc2l0aW9uaW5nXG4gICAgICAgIGNvbnN0IHZpc2libGVDb2x1bW5LZXlzID0gdGhpcy5nZXRWaXNpYmxlQ29sdW1uS2V5c0Zyb21ET00oKTtcbiAgICAgICAgaWYgKHZpc2libGVDb2x1bW5LZXlzLmxlbmd0aCA9PT0gMClcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgLy8gRmV0Y2ggZXZlbnRzIGZvciBkYXRlIHJhbmdlXG4gICAgICAgIGNvbnN0IHN0YXJ0RGF0ZSA9IG5ldyBEYXRlKHZpc2libGVEYXRlc1swXSk7XG4gICAgICAgIGNvbnN0IGVuZERhdGUgPSBuZXcgRGF0ZSh2aXNpYmxlRGF0ZXNbdmlzaWJsZURhdGVzLmxlbmd0aCAtIDFdKTtcbiAgICAgICAgZW5kRGF0ZS5zZXRIb3VycygyMywgNTksIDU5LCA5OTkpO1xuICAgICAgICBjb25zdCBldmVudHMgPSBhd2FpdCB0aGlzLmV2ZW50U2VydmljZS5nZXRCeURhdGVSYW5nZShzdGFydERhdGUsIGVuZERhdGUpO1xuICAgICAgICAvLyBGaWx0ZXIgdG8gYWxsRGF5IGV2ZW50cyBvbmx5IChhbGxEYXkgIT09IGZhbHNlKVxuICAgICAgICBjb25zdCBhbGxEYXlFdmVudHMgPSBldmVudHMuZmlsdGVyKGV2ZW50ID0+IGV2ZW50LmFsbERheSAhPT0gZmFsc2UpO1xuICAgICAgICAvLyBDbGVhciBleGlzdGluZyBpdGVtc1xuICAgICAgICBkcmF3ZXIuaW5uZXJIVE1MID0gJyc7XG4gICAgICAgIGlmIChhbGxEYXlFdmVudHMubGVuZ3RoID09PSAwKVxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAvLyBDYWxjdWxhdGUgbGF5b3V0IHdpdGggcm93IHN0YWNraW5nIHVzaW5nIGNvbHVtbktleXNcbiAgICAgICAgY29uc3QgbGF5b3V0cyA9IHRoaXMuY2FsY3VsYXRlTGF5b3V0KGFsbERheUV2ZW50cywgdmlzaWJsZUNvbHVtbktleXMpO1xuICAgICAgICBjb25zdCByb3dDb3VudCA9IE1hdGgubWF4KDEsIC4uLmxheW91dHMubWFwKGwgPT4gbC5yb3cpKTtcbiAgICAgICAgLy8gUmVuZGVyIGVhY2ggaXRlbSB3aXRoIGxheW91dFxuICAgICAgICBsYXlvdXRzLmZvckVhY2gobGF5b3V0ID0+IHtcbiAgICAgICAgICAgIGNvbnN0IGl0ZW0gPSB0aGlzLmNyZWF0ZUhlYWRlckl0ZW0obGF5b3V0KTtcbiAgICAgICAgICAgIGRyYXdlci5hcHBlbmRDaGlsZChpdGVtKTtcbiAgICAgICAgfSk7XG4gICAgICAgIC8vIEV4cGFuZCBkcmF3ZXIgdG8gZml0IGFsbCByb3dzXG4gICAgICAgIHRoaXMuaGVhZGVyRHJhd2VyTWFuYWdlci5leHBhbmRUb1Jvd3Mocm93Q291bnQpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBDcmVhdGUgYSBoZWFkZXIgaXRlbSBlbGVtZW50IGZyb20gbGF5b3V0XG4gICAgICovXG4gICAgY3JlYXRlSGVhZGVySXRlbShsYXlvdXQpIHtcbiAgICAgICAgY29uc3QgeyBldmVudCwgY29sdW1uS2V5LCByb3csIGNvbFN0YXJ0LCBjb2xFbmQgfSA9IGxheW91dDtcbiAgICAgICAgY29uc3QgaXRlbSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3N3cC1oZWFkZXItaXRlbScpO1xuICAgICAgICBpdGVtLmRhdGFzZXQuZXZlbnRJZCA9IGV2ZW50LmlkO1xuICAgICAgICBpdGVtLmRhdGFzZXQuaXRlbVR5cGUgPSAnZXZlbnQnO1xuICAgICAgICBpdGVtLmRhdGFzZXQuc3RhcnQgPSBldmVudC5zdGFydC50b0lTT1N0cmluZygpO1xuICAgICAgICBpdGVtLmRhdGFzZXQuZW5kID0gZXZlbnQuZW5kLnRvSVNPU3RyaW5nKCk7XG4gICAgICAgIGl0ZW0uZGF0YXNldC5jb2x1bW5LZXkgPSBjb2x1bW5LZXk7XG4gICAgICAgIGl0ZW0udGV4dENvbnRlbnQgPSBldmVudC50aXRsZTtcbiAgICAgICAgLy8gQ29sb3IgY2xhc3NcbiAgICAgICAgY29uc3QgY29sb3JDbGFzcyA9IHRoaXMuZ2V0Q29sb3JDbGFzcyhldmVudCk7XG4gICAgICAgIGlmIChjb2xvckNsYXNzKVxuICAgICAgICAgICAgaXRlbS5jbGFzc0xpc3QuYWRkKGNvbG9yQ2xhc3MpO1xuICAgICAgICAvLyBHcmlkIHBvc2l0aW9uIGZyb20gbGF5b3V0XG4gICAgICAgIGl0ZW0uc3R5bGUuZ3JpZEFyZWEgPSBgJHtyb3d9IC8gJHtjb2xTdGFydH0gLyAke3JvdyArIDF9IC8gJHtjb2xFbmR9YDtcbiAgICAgICAgcmV0dXJuIGl0ZW07XG4gICAgfVxuICAgIC8qKlxuICAgICAqIENhbGN1bGF0ZSBsYXlvdXQgZm9yIGFsbCBldmVudHMgd2l0aCByb3cgc3RhY2tpbmdcbiAgICAgKiBVc2VzIHRyYWNrLWJhc2VkIGFsZ29yaXRobSB0byBmaW5kIGF2YWlsYWJsZSByb3dzIGZvciBvdmVybGFwcGluZyBldmVudHNcbiAgICAgKi9cbiAgICBjYWxjdWxhdGVMYXlvdXQoZXZlbnRzLCB2aXNpYmxlQ29sdW1uS2V5cykge1xuICAgICAgICAvLyB0cmFja3Nbcm93XVtjb2xdID0gb2NjdXBpZWRcbiAgICAgICAgY29uc3QgdHJhY2tzID0gW25ldyBBcnJheSh2aXNpYmxlQ29sdW1uS2V5cy5sZW5ndGgpLmZpbGwoZmFsc2UpXTtcbiAgICAgICAgY29uc3QgbGF5b3V0cyA9IFtdO1xuICAgICAgICBmb3IgKGNvbnN0IGV2ZW50IG9mIGV2ZW50cykge1xuICAgICAgICAgICAgLy8gQnVpbGQgY29sdW1uS2V5IGZyb20gZXZlbnQgZmllbGRzIChvbmx5IHBsYWNlIHdlIG5lZWQgdG8gY29uc3RydWN0IGl0KVxuICAgICAgICAgICAgY29uc3QgY29sdW1uS2V5ID0gdGhpcy5idWlsZENvbHVtbktleUZyb21FdmVudChldmVudCk7XG4gICAgICAgICAgICBjb25zdCBzdGFydENvbCA9IHZpc2libGVDb2x1bW5LZXlzLmluZGV4T2YoY29sdW1uS2V5KTtcbiAgICAgICAgICAgIGNvbnN0IGVuZENvbHVtbktleSA9IHRoaXMuYnVpbGRDb2x1bW5LZXlGcm9tRXZlbnQoZXZlbnQsIGV2ZW50LmVuZCk7XG4gICAgICAgICAgICBjb25zdCBlbmRDb2wgPSB2aXNpYmxlQ29sdW1uS2V5cy5pbmRleE9mKGVuZENvbHVtbktleSk7XG4gICAgICAgICAgICBpZiAoc3RhcnRDb2wgPT09IC0xICYmIGVuZENvbCA9PT0gLTEpXG4gICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICAvLyBDbGFtcCB0aWwgc3lubGlnZSBrb2xvbm5lclxuICAgICAgICAgICAgY29uc3QgY29sU3RhcnQgPSBNYXRoLm1heCgwLCBzdGFydENvbCk7XG4gICAgICAgICAgICBjb25zdCBjb2xFbmQgPSAoZW5kQ29sICE9PSAtMSA/IGVuZENvbCA6IHZpc2libGVDb2x1bW5LZXlzLmxlbmd0aCAtIDEpICsgMTtcbiAgICAgICAgICAgIC8vIEZpbmQgbGVkaWcgclx1MDBFNmtrZVxuICAgICAgICAgICAgY29uc3Qgcm93ID0gdGhpcy5maW5kQXZhaWxhYmxlUm93KHRyYWNrcywgY29sU3RhcnQsIGNvbEVuZCk7XG4gICAgICAgICAgICAvLyBNYXJrZXIgc29tIG9wdGFnZXRcbiAgICAgICAgICAgIGZvciAobGV0IGMgPSBjb2xTdGFydDsgYyA8IGNvbEVuZDsgYysrKSB7XG4gICAgICAgICAgICAgICAgdHJhY2tzW3Jvd11bY10gPSB0cnVlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgbGF5b3V0cy5wdXNoKHsgZXZlbnQsIGNvbHVtbktleSwgcm93OiByb3cgKyAxLCBjb2xTdGFydDogY29sU3RhcnQgKyAxLCBjb2xFbmQ6IGNvbEVuZCArIDEgfSk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGxheW91dHM7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEJ1aWxkIGNvbHVtbktleSBmcm9tIGV2ZW50IHVzaW5nIEZpbHRlclRlbXBsYXRlXG4gICAgICogVXNlcyB0aGUgc2FtZSB0ZW1wbGF0ZSB0aGF0IGNvbHVtbnMgdXNlIGZvciBtYXRjaGluZ1xuICAgICAqL1xuICAgIGJ1aWxkQ29sdW1uS2V5RnJvbUV2ZW50KGV2ZW50LCBkYXRlKSB7XG4gICAgICAgIGlmICghdGhpcy5maWx0ZXJUZW1wbGF0ZSkge1xuICAgICAgICAgICAgLy8gRmFsbGJhY2sgaWYgbm8gdGVtcGxhdGUgLSBzaG91bGRuJ3QgaGFwcGVuIGluIG5vcm1hbCBmbG93XG4gICAgICAgICAgICBjb25zdCBkYXRlU3RyID0gdGhpcy5kYXRlU2VydmljZS5nZXREYXRlS2V5KGRhdGUgfHwgZXZlbnQuc3RhcnQpO1xuICAgICAgICAgICAgcmV0dXJuIGRhdGVTdHI7XG4gICAgICAgIH1cbiAgICAgICAgLy8gRm9yIG11bHRpLWRheSBldmVudHMsIHdlIG5lZWQgdG8gb3ZlcnJpZGUgdGhlIGRhdGUgaW4gdGhlIGV2ZW50XG4gICAgICAgIGlmIChkYXRlICYmIGRhdGUuZ2V0VGltZSgpICE9PSBldmVudC5zdGFydC5nZXRUaW1lKCkpIHtcbiAgICAgICAgICAgIC8vIENyZWF0ZSB0ZW1wb3JhcnkgZXZlbnQgd2l0aCBvdmVycmlkZGVuIHN0YXJ0IGZvciBrZXkgZ2VuZXJhdGlvblxuICAgICAgICAgICAgY29uc3QgdGVtcEV2ZW50ID0geyAuLi5ldmVudCwgc3RhcnQ6IGRhdGUgfTtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLmZpbHRlclRlbXBsYXRlLmJ1aWxkS2V5RnJvbUV2ZW50KHRlbXBFdmVudCk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRoaXMuZmlsdGVyVGVtcGxhdGUuYnVpbGRLZXlGcm9tRXZlbnQoZXZlbnQpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBGaW5kIGF2YWlsYWJsZSByb3cgZm9yIGV2ZW50IHNwYW5uaW5nIGNvbHVtbnMgW2NvbFN0YXJ0LCBjb2xFbmQpXG4gICAgICovXG4gICAgZmluZEF2YWlsYWJsZVJvdyh0cmFja3MsIGNvbFN0YXJ0LCBjb2xFbmQpIHtcbiAgICAgICAgZm9yIChsZXQgcm93ID0gMDsgcm93IDwgdHJhY2tzLmxlbmd0aDsgcm93KyspIHtcbiAgICAgICAgICAgIGxldCBhdmFpbGFibGUgPSB0cnVlO1xuICAgICAgICAgICAgZm9yIChsZXQgYyA9IGNvbFN0YXJ0OyBjIDwgY29sRW5kOyBjKyspIHtcbiAgICAgICAgICAgICAgICBpZiAodHJhY2tzW3Jvd11bY10pIHtcbiAgICAgICAgICAgICAgICAgICAgYXZhaWxhYmxlID0gZmFsc2U7XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmIChhdmFpbGFibGUpXG4gICAgICAgICAgICAgICAgcmV0dXJuIHJvdztcbiAgICAgICAgfVxuICAgICAgICAvLyBOeSByXHUwMEU2a2tlXG4gICAgICAgIHRyYWNrcy5wdXNoKG5ldyBBcnJheSh0cmFja3NbMF0ubGVuZ3RoKS5maWxsKGZhbHNlKSk7XG4gICAgICAgIHJldHVybiB0cmFja3MubGVuZ3RoIC0gMTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogR2V0IGNvbG9yIGNsYXNzIGJhc2VkIG9uIGV2ZW50IG1ldGFkYXRhIG9yIHR5cGVcbiAgICAgKi9cbiAgICBnZXRDb2xvckNsYXNzKGV2ZW50KSB7XG4gICAgICAgIGlmIChldmVudC5tZXRhZGF0YT8uY29sb3IpIHtcbiAgICAgICAgICAgIHJldHVybiBgaXMtJHtldmVudC5tZXRhZGF0YS5jb2xvcn1gO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHR5cGVDb2xvcnMgPSB7XG4gICAgICAgICAgICAnY3VzdG9tZXInOiAnaXMtYmx1ZScsXG4gICAgICAgICAgICAndmFjYXRpb24nOiAnaXMtZ3JlZW4nLFxuICAgICAgICAgICAgJ2JyZWFrJzogJ2lzLWFtYmVyJyxcbiAgICAgICAgICAgICdtZWV0aW5nJzogJ2lzLXB1cnBsZScsXG4gICAgICAgICAgICAnYmxvY2tlZCc6ICdpcy1yZWQnXG4gICAgICAgIH07XG4gICAgICAgIHJldHVybiB0eXBlQ29sb3JzW2V2ZW50LnR5cGVdIHx8ICdpcy1ibHVlJztcbiAgICB9XG4gICAgLyoqXG4gICAgICogU2V0dXAgZXZlbnQgbGlzdGVuZXJzIGZvciBkcmFnIGV2ZW50c1xuICAgICAqL1xuICAgIHNldHVwTGlzdGVuZXJzKCkge1xuICAgICAgICB0aGlzLmV2ZW50QnVzLm9uKENvcmVFdmVudHMuRVZFTlRfRFJBR19FTlRFUl9IRUFERVIsIChlKSA9PiB7XG4gICAgICAgICAgICBjb25zdCBwYXlsb2FkID0gZS5kZXRhaWw7XG4gICAgICAgICAgICB0aGlzLmhhbmRsZURyYWdFbnRlcihwYXlsb2FkKTtcbiAgICAgICAgfSk7XG4gICAgICAgIHRoaXMuZXZlbnRCdXMub24oQ29yZUV2ZW50cy5FVkVOVF9EUkFHX01PVkVfSEVBREVSLCAoZSkgPT4ge1xuICAgICAgICAgICAgY29uc3QgcGF5bG9hZCA9IGUuZGV0YWlsO1xuICAgICAgICAgICAgdGhpcy5oYW5kbGVEcmFnTW92ZShwYXlsb2FkKTtcbiAgICAgICAgfSk7XG4gICAgICAgIHRoaXMuZXZlbnRCdXMub24oQ29yZUV2ZW50cy5FVkVOVF9EUkFHX0xFQVZFX0hFQURFUiwgKGUpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHBheWxvYWQgPSBlLmRldGFpbDtcbiAgICAgICAgICAgIHRoaXMuaGFuZGxlRHJhZ0xlYXZlKHBheWxvYWQpO1xuICAgICAgICB9KTtcbiAgICAgICAgdGhpcy5ldmVudEJ1cy5vbihDb3JlRXZlbnRzLkVWRU5UX0RSQUdfRU5ELCAoZSkgPT4ge1xuICAgICAgICAgICAgY29uc3QgcGF5bG9hZCA9IGUuZGV0YWlsO1xuICAgICAgICAgICAgdGhpcy5oYW5kbGVEcmFnRW5kKHBheWxvYWQpO1xuICAgICAgICB9KTtcbiAgICAgICAgdGhpcy5ldmVudEJ1cy5vbihDb3JlRXZlbnRzLkVWRU5UX0RSQUdfQ0FOQ0VMLCAoKSA9PiB7XG4gICAgICAgICAgICB0aGlzLmNsZWFudXAoKTtcbiAgICAgICAgfSk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEhhbmRsZSBkcmFnIGVudGVyaW5nIGhlYWRlciB6b25lIC0gY3JlYXRlIHByZXZpZXcgaXRlbVxuICAgICAqL1xuICAgIGhhbmRsZURyYWdFbnRlcihwYXlsb2FkKSB7XG4gICAgICAgIHRoaXMuY29udGFpbmVyID0gZG9jdW1lbnQucXVlcnlTZWxlY3Rvcignc3dwLWhlYWRlci1kcmF3ZXInKTtcbiAgICAgICAgaWYgKCF0aGlzLmNvbnRhaW5lcilcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgLy8gUmVtZW1iZXIgaWYgZHJhd2VyIHdhcyBhbHJlYWR5IGV4cGFuZGVkXG4gICAgICAgIHRoaXMud2FzRXhwYW5kZWRCZWZvcmVEcmFnID0gdGhpcy5oZWFkZXJEcmF3ZXJNYW5hZ2VyLmlzRXhwYW5kZWQoKTtcbiAgICAgICAgLy8gRXhwYW5kIHRvIGF0IGxlYXN0IDEgcm93IGlmIGNvbGxhcHNlZCwgb3RoZXJ3aXNlIGtlZXAgY3VycmVudCBoZWlnaHRcbiAgICAgICAgaWYgKCF0aGlzLndhc0V4cGFuZGVkQmVmb3JlRHJhZykge1xuICAgICAgICAgICAgdGhpcy5oZWFkZXJEcmF3ZXJNYW5hZ2VyLmV4cGFuZFRvUm93cygxKTtcbiAgICAgICAgfVxuICAgICAgICAvLyBTdG9yZSByZWZlcmVuY2UgdG8gc291cmNlIGVsZW1lbnRcbiAgICAgICAgdGhpcy5zb3VyY2VFbGVtZW50ID0gcGF5bG9hZC5lbGVtZW50O1xuICAgICAgICAvLyBDcmVhdGUgaGVhZGVyIGl0ZW1cbiAgICAgICAgY29uc3QgaXRlbSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3N3cC1oZWFkZXItaXRlbScpO1xuICAgICAgICBpdGVtLmRhdGFzZXQuZXZlbnRJZCA9IHBheWxvYWQuZXZlbnRJZDtcbiAgICAgICAgaXRlbS5kYXRhc2V0Lml0ZW1UeXBlID0gcGF5bG9hZC5pdGVtVHlwZTtcbiAgICAgICAgaXRlbS5kYXRhc2V0LmR1cmF0aW9uID0gU3RyaW5nKHBheWxvYWQuZHVyYXRpb24pO1xuICAgICAgICBpdGVtLmRhdGFzZXQuY29sdW1uS2V5ID0gcGF5bG9hZC5zb3VyY2VDb2x1bW5LZXk7XG4gICAgICAgIGl0ZW0udGV4dENvbnRlbnQgPSBwYXlsb2FkLnRpdGxlO1xuICAgICAgICAvLyBBcHBseSBjb2xvciBjbGFzcyBpZiBwcmVzZW50XG4gICAgICAgIGlmIChwYXlsb2FkLmNvbG9yQ2xhc3MpIHtcbiAgICAgICAgICAgIGl0ZW0uY2xhc3NMaXN0LmFkZChwYXlsb2FkLmNvbG9yQ2xhc3MpO1xuICAgICAgICB9XG4gICAgICAgIC8vIEFkZCBkcmFnZ2luZyBzdGF0ZVxuICAgICAgICBpdGVtLmNsYXNzTGlzdC5hZGQoJ2RyYWdnaW5nJyk7XG4gICAgICAgIC8vIEluaXRpYWwgcGxhY2VtZW50IChkdXJhdGlvbiBkZXRlcm1pbmVzIGNvbHVtbiBzcGFuKVxuICAgICAgICAvLyBncmlkQXJlYSBmb3JtYXQ6IFwicm93IC8gY29sLXN0YXJ0IC8gcm93KzEgLyBjb2wtZW5kXCJcbiAgICAgICAgY29uc3QgY29sID0gcGF5bG9hZC5zb3VyY2VDb2x1bW5JbmRleCArIDE7XG4gICAgICAgIGNvbnN0IGVuZENvbCA9IGNvbCArIHBheWxvYWQuZHVyYXRpb247XG4gICAgICAgIGl0ZW0uc3R5bGUuZ3JpZEFyZWEgPSBgMSAvICR7Y29sfSAvIDIgLyAke2VuZENvbH1gO1xuICAgICAgICB0aGlzLmNvbnRhaW5lci5hcHBlbmRDaGlsZChpdGVtKTtcbiAgICAgICAgdGhpcy5jdXJyZW50SXRlbSA9IGl0ZW07XG4gICAgICAgIC8vIEhpZGUgb3JpZ2luYWwgZWxlbWVudCB3aGlsZSBpbiBoZWFkZXJcbiAgICAgICAgcGF5bG9hZC5lbGVtZW50LnN0eWxlLnZpc2liaWxpdHkgPSAnaGlkZGVuJztcbiAgICB9XG4gICAgLyoqXG4gICAgICogSGFuZGxlIGRyYWcgbW92aW5nIHdpdGhpbiBoZWFkZXIgLSB1cGRhdGUgY29sdW1uIHBvc2l0aW9uXG4gICAgICovXG4gICAgaGFuZGxlRHJhZ01vdmUocGF5bG9hZCkge1xuICAgICAgICBpZiAoIXRoaXMuY3VycmVudEl0ZW0pXG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIC8vIFVwZGF0ZSBjb2x1bW4gcG9zaXRpb25cbiAgICAgICAgY29uc3QgY29sID0gcGF5bG9hZC5jb2x1bW5JbmRleCArIDE7XG4gICAgICAgIGNvbnN0IGR1cmF0aW9uID0gcGFyc2VJbnQodGhpcy5jdXJyZW50SXRlbS5kYXRhc2V0LmR1cmF0aW9uIHx8ICcxJywgMTApO1xuICAgICAgICBjb25zdCBlbmRDb2wgPSBjb2wgKyBkdXJhdGlvbjtcbiAgICAgICAgdGhpcy5jdXJyZW50SXRlbS5zdHlsZS5ncmlkQXJlYSA9IGAxIC8gJHtjb2x9IC8gMiAvICR7ZW5kQ29sfWA7XG4gICAgICAgIC8vIFVwZGF0ZSBjb2x1bW5LZXkgdG8gbmV3IHBvc2l0aW9uXG4gICAgICAgIHRoaXMuY3VycmVudEl0ZW0uZGF0YXNldC5jb2x1bW5LZXkgPSBwYXlsb2FkLmNvbHVtbktleTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogSGFuZGxlIGRyYWcgbGVhdmluZyBoZWFkZXIgLSBjbGVhbnVwIGZvciBncmlkXHUyMTkyaGVhZGVyIGRyYWcgb25seVxuICAgICAqL1xuICAgIGhhbmRsZURyYWdMZWF2ZShwYXlsb2FkKSB7XG4gICAgICAgIC8vIE9ubHkgY2xlYW51cCBmb3IgZ3JpZFx1MjE5MmhlYWRlciBkcmFnICh3aGVuIGdyaWQgZXZlbnQgbGVhdmVzIGhlYWRlciBiYWNrIHRvIGdyaWQpXG4gICAgICAgIC8vIEZvciBoZWFkZXJcdTIxOTJncmlkIGRyYWcsIHRoZSBoZWFkZXIgaXRlbSBzdGF5cyBhcyBnaG9zdCB1bnRpbCBkcm9wXG4gICAgICAgIGlmIChwYXlsb2FkLnNvdXJjZSA9PT0gJ2dyaWQnKSB7XG4gICAgICAgICAgICB0aGlzLmNsZWFudXAoKTtcbiAgICAgICAgfVxuICAgICAgICAvLyBGb3IgaGVhZGVyIHNvdXJjZSwgZG8gbm90aGluZyAtIGdob3N0IHN0YXlzIHVudGlsIEVWRU5UX0RSQUdfRU5EXG4gICAgfVxuICAgIC8qKlxuICAgICAqIEhhbmRsZSBkcmFnIGVuZCAtIGZpbmFsaXplIGJhc2VkIG9uIGRyb3AgdGFyZ2V0XG4gICAgICovXG4gICAgaGFuZGxlRHJhZ0VuZChwYXlsb2FkKSB7XG4gICAgICAgIGlmIChwYXlsb2FkLnRhcmdldCA9PT0gJ2hlYWRlcicpIHtcbiAgICAgICAgICAgIC8vIEdyaWRcdTIxOTJIZWFkZXI6IEZpbmFsaXplIHRoZSBoZWFkZXIgaXRlbSAoaXQgc3RheXMgaW4gaGVhZGVyKVxuICAgICAgICAgICAgaWYgKHRoaXMuY3VycmVudEl0ZW0pIHtcbiAgICAgICAgICAgICAgICB0aGlzLmN1cnJlbnRJdGVtLmNsYXNzTGlzdC5yZW1vdmUoJ2RyYWdnaW5nJyk7XG4gICAgICAgICAgICAgICAgdGhpcy5yZWNhbGN1bGF0ZURyYXdlckxheW91dCgpO1xuICAgICAgICAgICAgICAgIHRoaXMuY3VycmVudEl0ZW0gPSBudWxsO1xuICAgICAgICAgICAgICAgIHRoaXMuc291cmNlRWxlbWVudCA9IG51bGw7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAvLyBIZWFkZXJcdTIxOTJHcmlkOiBSZW1vdmUgZ2hvc3QgaGVhZGVyIGl0ZW0gYW5kIHJlY2FsY3VsYXRlXG4gICAgICAgICAgICBjb25zdCBnaG9zdCA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoYHN3cC1oZWFkZXItaXRlbS5kcmFnLWdob3N0W2RhdGEtZXZlbnQtaWQ9XCIke3BheWxvYWQuc3dwRXZlbnQuZXZlbnRJZH1cIl1gKTtcbiAgICAgICAgICAgIGdob3N0Py5yZW1vdmUoKTtcbiAgICAgICAgICAgIHRoaXMucmVjYWxjdWxhdGVEcmF3ZXJMYXlvdXQoKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICAvKipcbiAgICAgKiBSZWNhbGN1bGF0ZSBsYXlvdXQgZm9yIGFsbCBpdGVtcyBjdXJyZW50bHkgaW4gdGhlIGRyYXdlclxuICAgICAqIENhbGxlZCBhZnRlciBkcm9wIHRvIHJlcG9zaXRpb24gaXRlbXMgYW5kIGFkanVzdCBoZWlnaHRcbiAgICAgKi9cbiAgICByZWNhbGN1bGF0ZURyYXdlckxheW91dCgpIHtcbiAgICAgICAgY29uc3QgZHJhd2VyID0gZG9jdW1lbnQucXVlcnlTZWxlY3Rvcignc3dwLWhlYWRlci1kcmF3ZXInKTtcbiAgICAgICAgaWYgKCFkcmF3ZXIpXG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIGNvbnN0IGl0ZW1zID0gQXJyYXkuZnJvbShkcmF3ZXIucXVlcnlTZWxlY3RvckFsbCgnc3dwLWhlYWRlci1pdGVtJykpO1xuICAgICAgICBpZiAoaXRlbXMubGVuZ3RoID09PSAwKVxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAvLyBHZXQgdmlzaWJsZSBjb2x1bW4ga2V5cyBmb3IgY29ycmVjdCBtdWx0aS1yZXNvdXJjZSBwb3NpdGlvbmluZ1xuICAgICAgICBjb25zdCB2aXNpYmxlQ29sdW1uS2V5cyA9IHRoaXMuZ2V0VmlzaWJsZUNvbHVtbktleXNGcm9tRE9NKCk7XG4gICAgICAgIGlmICh2aXNpYmxlQ29sdW1uS2V5cy5sZW5ndGggPT09IDApXG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIC8vIEJ1aWxkIGxheW91dCBkYXRhIGZyb20gRE9NIGl0ZW1zIC0gdXNlIGNvbHVtbktleSBkaXJlY3RseSAob3BhcXVlIG1hdGNoaW5nKVxuICAgICAgICBjb25zdCBpdGVtRGF0YSA9IGl0ZW1zLm1hcChpdGVtID0+ICh7XG4gICAgICAgICAgICBlbGVtZW50OiBpdGVtLFxuICAgICAgICAgICAgY29sdW1uS2V5OiBpdGVtLmRhdGFzZXQuY29sdW1uS2V5IHx8ICcnLFxuICAgICAgICAgICAgZHVyYXRpb246IHBhcnNlSW50KGl0ZW0uZGF0YXNldC5kdXJhdGlvbiB8fCAnMScsIDEwKVxuICAgICAgICB9KSk7XG4gICAgICAgIC8vIENhbGN1bGF0ZSBuZXcgbGF5b3V0IHVzaW5nIHRyYWNrIGFsZ29yaXRobVxuICAgICAgICBjb25zdCB0cmFja3MgPSBbbmV3IEFycmF5KHZpc2libGVDb2x1bW5LZXlzLmxlbmd0aCkuZmlsbChmYWxzZSldO1xuICAgICAgICBmb3IgKGNvbnN0IGl0ZW0gb2YgaXRlbURhdGEpIHtcbiAgICAgICAgICAgIC8vIERpcmVjdCBjb2x1bW5LZXkgbWF0Y2hpbmcgLSBubyBwYXJzaW5nIG9yIGNvbnN0cnVjdGlvbiBuZWVkZWRcbiAgICAgICAgICAgIGNvbnN0IHN0YXJ0Q29sID0gdmlzaWJsZUNvbHVtbktleXMuaW5kZXhPZihpdGVtLmNvbHVtbktleSk7XG4gICAgICAgICAgICBpZiAoc3RhcnRDb2wgPT09IC0xKVxuICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgICAgY29uc3QgY29sU3RhcnQgPSBzdGFydENvbDtcbiAgICAgICAgICAgIGNvbnN0IGNvbEVuZCA9IE1hdGgubWluKHN0YXJ0Q29sICsgaXRlbS5kdXJhdGlvbiwgdmlzaWJsZUNvbHVtbktleXMubGVuZ3RoKTtcbiAgICAgICAgICAgIGNvbnN0IHJvdyA9IHRoaXMuZmluZEF2YWlsYWJsZVJvdyh0cmFja3MsIGNvbFN0YXJ0LCBjb2xFbmQpO1xuICAgICAgICAgICAgZm9yIChsZXQgYyA9IGNvbFN0YXJ0OyBjIDwgY29sRW5kOyBjKyspIHtcbiAgICAgICAgICAgICAgICB0cmFja3Nbcm93XVtjXSA9IHRydWU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyBVcGRhdGUgZWxlbWVudCBwb3NpdGlvblxuICAgICAgICAgICAgaXRlbS5lbGVtZW50LnN0eWxlLmdyaWRBcmVhID0gYCR7cm93ICsgMX0gLyAke2NvbFN0YXJ0ICsgMX0gLyAke3JvdyArIDJ9IC8gJHtjb2xFbmQgKyAxfWA7XG4gICAgICAgIH1cbiAgICAgICAgLy8gVXBkYXRlIGRyYXdlciBoZWlnaHRcbiAgICAgICAgY29uc3Qgcm93Q291bnQgPSB0cmFja3MubGVuZ3RoO1xuICAgICAgICB0aGlzLmhlYWRlckRyYXdlck1hbmFnZXIuZXhwYW5kVG9Sb3dzKHJvd0NvdW50KTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogR2V0IHZpc2libGUgY29sdW1uIGtleXMgZnJvbSBET00gKHByZXNlcnZlcyBvcmRlciBmb3IgbXVsdGktcmVzb3VyY2Ugdmlld3MpXG4gICAgICogVXNlcyBmaWx0ZXJUZW1wbGF0ZS5idWlsZEtleUZyb21Db2x1bW4oKSBmb3IgY29uc2lzdGVudCBrZXkgZm9ybWF0IHdpdGggZXZlbnRzXG4gICAgICovXG4gICAgZ2V0VmlzaWJsZUNvbHVtbktleXNGcm9tRE9NKCkge1xuICAgICAgICBpZiAoIXRoaXMuZmlsdGVyVGVtcGxhdGUpXG4gICAgICAgICAgICByZXR1cm4gW107XG4gICAgICAgIGNvbnN0IGNvbHVtbnMgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCdzd3AtZGF5LWNvbHVtbicpO1xuICAgICAgICBjb25zdCBjb2x1bW5LZXlzID0gW107XG4gICAgICAgIGNvbHVtbnMuZm9yRWFjaChjb2wgPT4ge1xuICAgICAgICAgICAgY29uc3QgY29sdW1uS2V5ID0gdGhpcy5maWx0ZXJUZW1wbGF0ZS5idWlsZEtleUZyb21Db2x1bW4oY29sKTtcbiAgICAgICAgICAgIGlmIChjb2x1bW5LZXkpXG4gICAgICAgICAgICAgICAgY29sdW1uS2V5cy5wdXNoKGNvbHVtbktleSk7XG4gICAgICAgIH0pO1xuICAgICAgICByZXR1cm4gY29sdW1uS2V5cztcbiAgICB9XG4gICAgLyoqXG4gICAgICogQ2xlYW51cCBwcmV2aWV3IGl0ZW0gYW5kIHJlc3RvcmUgc291cmNlIHZpc2liaWxpdHlcbiAgICAgKi9cbiAgICBjbGVhbnVwKCkge1xuICAgICAgICAvLyBSZW1vdmUgcHJldmlldyBpdGVtXG4gICAgICAgIHRoaXMuY3VycmVudEl0ZW0/LnJlbW92ZSgpO1xuICAgICAgICB0aGlzLmN1cnJlbnRJdGVtID0gbnVsbDtcbiAgICAgICAgLy8gUmVzdG9yZSBzb3VyY2UgZWxlbWVudCB2aXNpYmlsaXR5XG4gICAgICAgIGlmICh0aGlzLnNvdXJjZUVsZW1lbnQpIHtcbiAgICAgICAgICAgIHRoaXMuc291cmNlRWxlbWVudC5zdHlsZS52aXNpYmlsaXR5ID0gJyc7XG4gICAgICAgICAgICB0aGlzLnNvdXJjZUVsZW1lbnQgPSBudWxsO1xuICAgICAgICB9XG4gICAgICAgIC8vIENvbGxhcHNlIGRyYXdlciBpZiBpdCB3YXNuJ3QgZXhwYW5kZWQgYmVmb3JlIGRyYWdcbiAgICAgICAgaWYgKCF0aGlzLndhc0V4cGFuZGVkQmVmb3JlRHJhZykge1xuICAgICAgICAgICAgdGhpcy5oZWFkZXJEcmF3ZXJNYW5hZ2VyLmNvbGxhcHNlKCk7XG4gICAgICAgIH1cbiAgICB9XG59XG4iLCAiLyoqXG4gKiBTY2hlZHVsZU92ZXJyaWRlU3RvcmUgLSBJbmRleGVkREIgT2JqZWN0U3RvcmUgZm9yIHNjaGVkdWxlIG92ZXJyaWRlc1xuICpcbiAqIFN0b3JlcyBkYXRlLXNwZWNpZmljIHNjaGVkdWxlIG92ZXJyaWRlcyBmb3IgcmVzb3VyY2VzLlxuICogSW5kZXhlczogcmVzb3VyY2VJZCwgZGF0ZSwgY29tcG91bmQgKHJlc291cmNlSWQgKyBkYXRlKVxuICovXG5leHBvcnQgY2xhc3MgU2NoZWR1bGVPdmVycmlkZVN0b3JlIHtcbiAgICBjb25zdHJ1Y3RvcigpIHtcbiAgICAgICAgdGhpcy5zdG9yZU5hbWUgPSBTY2hlZHVsZU92ZXJyaWRlU3RvcmUuU1RPUkVfTkFNRTtcbiAgICB9XG4gICAgY3JlYXRlKGRiKSB7XG4gICAgICAgIGNvbnN0IHN0b3JlID0gZGIuY3JlYXRlT2JqZWN0U3RvcmUoU2NoZWR1bGVPdmVycmlkZVN0b3JlLlNUT1JFX05BTUUsIHsga2V5UGF0aDogJ2lkJyB9KTtcbiAgICAgICAgc3RvcmUuY3JlYXRlSW5kZXgoJ3Jlc291cmNlSWQnLCAncmVzb3VyY2VJZCcsIHsgdW5pcXVlOiBmYWxzZSB9KTtcbiAgICAgICAgc3RvcmUuY3JlYXRlSW5kZXgoJ2RhdGUnLCAnZGF0ZScsIHsgdW5pcXVlOiBmYWxzZSB9KTtcbiAgICAgICAgc3RvcmUuY3JlYXRlSW5kZXgoJ3Jlc291cmNlSWRfZGF0ZScsIFsncmVzb3VyY2VJZCcsICdkYXRlJ10sIHsgdW5pcXVlOiB0cnVlIH0pO1xuICAgICAgICBzdG9yZS5jcmVhdGVJbmRleCgnc3luY1N0YXR1cycsICdzeW5jU3RhdHVzJywgeyB1bmlxdWU6IGZhbHNlIH0pO1xuICAgIH1cbn1cblNjaGVkdWxlT3ZlcnJpZGVTdG9yZS5TVE9SRV9OQU1FID0gJ3NjaGVkdWxlT3ZlcnJpZGVzJztcbiIsICJpbXBvcnQgeyBTY2hlZHVsZU92ZXJyaWRlU3RvcmUgfSBmcm9tICcuL1NjaGVkdWxlT3ZlcnJpZGVTdG9yZSc7XG4vKipcbiAqIFNjaGVkdWxlT3ZlcnJpZGVTZXJ2aWNlIC0gQ1JVRCBmb3Igc2NoZWR1bGUgb3ZlcnJpZGVzXG4gKlxuICogUHJvdmlkZXMgYWNjZXNzIHRvIGRhdGUtc3BlY2lmaWMgc2NoZWR1bGUgb3ZlcnJpZGVzIGZvciByZXNvdXJjZXMuXG4gKi9cbmV4cG9ydCBjbGFzcyBTY2hlZHVsZU92ZXJyaWRlU2VydmljZSB7XG4gICAgY29uc3RydWN0b3IoY29udGV4dCkge1xuICAgICAgICB0aGlzLmNvbnRleHQgPSBjb250ZXh0O1xuICAgIH1cbiAgICBnZXQgZGIoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmNvbnRleHQuZ2V0RGF0YWJhc2UoKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogR2V0IG92ZXJyaWRlIGZvciBhIHNwZWNpZmljIHJlc291cmNlIGFuZCBkYXRlXG4gICAgICovXG4gICAgYXN5bmMgZ2V0T3ZlcnJpZGUocmVzb3VyY2VJZCwgZGF0ZSkge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICAgICAgY29uc3QgdHJhbnNhY3Rpb24gPSB0aGlzLmRiLnRyYW5zYWN0aW9uKFtTY2hlZHVsZU92ZXJyaWRlU3RvcmUuU1RPUkVfTkFNRV0sICdyZWFkb25seScpO1xuICAgICAgICAgICAgY29uc3Qgc3RvcmUgPSB0cmFuc2FjdGlvbi5vYmplY3RTdG9yZShTY2hlZHVsZU92ZXJyaWRlU3RvcmUuU1RPUkVfTkFNRSk7XG4gICAgICAgICAgICBjb25zdCBpbmRleCA9IHN0b3JlLmluZGV4KCdyZXNvdXJjZUlkX2RhdGUnKTtcbiAgICAgICAgICAgIGNvbnN0IHJlcXVlc3QgPSBpbmRleC5nZXQoW3Jlc291cmNlSWQsIGRhdGVdKTtcbiAgICAgICAgICAgIHJlcXVlc3Qub25zdWNjZXNzID0gKCkgPT4ge1xuICAgICAgICAgICAgICAgIHJlc29sdmUocmVxdWVzdC5yZXN1bHQgfHwgbnVsbCk7XG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgcmVxdWVzdC5vbmVycm9yID0gKCkgPT4ge1xuICAgICAgICAgICAgICAgIHJlamVjdChuZXcgRXJyb3IoYEZhaWxlZCB0byBnZXQgb3ZlcnJpZGUgZm9yICR7cmVzb3VyY2VJZH0gb24gJHtkYXRlfTogJHtyZXF1ZXN0LmVycm9yfWApKTtcbiAgICAgICAgICAgIH07XG4gICAgICAgIH0pO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBHZXQgYWxsIG92ZXJyaWRlcyBmb3IgYSByZXNvdXJjZVxuICAgICAqL1xuICAgIGFzeW5jIGdldEJ5UmVzb3VyY2UocmVzb3VyY2VJZCkge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICAgICAgY29uc3QgdHJhbnNhY3Rpb24gPSB0aGlzLmRiLnRyYW5zYWN0aW9uKFtTY2hlZHVsZU92ZXJyaWRlU3RvcmUuU1RPUkVfTkFNRV0sICdyZWFkb25seScpO1xuICAgICAgICAgICAgY29uc3Qgc3RvcmUgPSB0cmFuc2FjdGlvbi5vYmplY3RTdG9yZShTY2hlZHVsZU92ZXJyaWRlU3RvcmUuU1RPUkVfTkFNRSk7XG4gICAgICAgICAgICBjb25zdCBpbmRleCA9IHN0b3JlLmluZGV4KCdyZXNvdXJjZUlkJyk7XG4gICAgICAgICAgICBjb25zdCByZXF1ZXN0ID0gaW5kZXguZ2V0QWxsKHJlc291cmNlSWQpO1xuICAgICAgICAgICAgcmVxdWVzdC5vbnN1Y2Nlc3MgPSAoKSA9PiB7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZShyZXF1ZXN0LnJlc3VsdCB8fCBbXSk7XG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgcmVxdWVzdC5vbmVycm9yID0gKCkgPT4ge1xuICAgICAgICAgICAgICAgIHJlamVjdChuZXcgRXJyb3IoYEZhaWxlZCB0byBnZXQgb3ZlcnJpZGVzIGZvciAke3Jlc291cmNlSWR9OiAke3JlcXVlc3QuZXJyb3J9YCkpO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgfSk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEdldCBvdmVycmlkZXMgZm9yIGEgZGF0ZSByYW5nZVxuICAgICAqL1xuICAgIGFzeW5jIGdldEJ5RGF0ZVJhbmdlKHJlc291cmNlSWQsIHN0YXJ0RGF0ZSwgZW5kRGF0ZSkge1xuICAgICAgICBjb25zdCBhbGwgPSBhd2FpdCB0aGlzLmdldEJ5UmVzb3VyY2UocmVzb3VyY2VJZCk7XG4gICAgICAgIHJldHVybiBhbGwuZmlsdGVyKG8gPT4gby5kYXRlID49IHN0YXJ0RGF0ZSAmJiBvLmRhdGUgPD0gZW5kRGF0ZSk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFNhdmUgYW4gb3ZlcnJpZGVcbiAgICAgKi9cbiAgICBhc3luYyBzYXZlKG92ZXJyaWRlKSB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgICAgICBjb25zdCB0cmFuc2FjdGlvbiA9IHRoaXMuZGIudHJhbnNhY3Rpb24oW1NjaGVkdWxlT3ZlcnJpZGVTdG9yZS5TVE9SRV9OQU1FXSwgJ3JlYWR3cml0ZScpO1xuICAgICAgICAgICAgY29uc3Qgc3RvcmUgPSB0cmFuc2FjdGlvbi5vYmplY3RTdG9yZShTY2hlZHVsZU92ZXJyaWRlU3RvcmUuU1RPUkVfTkFNRSk7XG4gICAgICAgICAgICBjb25zdCByZXF1ZXN0ID0gc3RvcmUucHV0KG92ZXJyaWRlKTtcbiAgICAgICAgICAgIHJlcXVlc3Qub25zdWNjZXNzID0gKCkgPT4gcmVzb2x2ZSgpO1xuICAgICAgICAgICAgcmVxdWVzdC5vbmVycm9yID0gKCkgPT4ge1xuICAgICAgICAgICAgICAgIHJlamVjdChuZXcgRXJyb3IoYEZhaWxlZCB0byBzYXZlIG92ZXJyaWRlICR7b3ZlcnJpZGUuaWR9OiAke3JlcXVlc3QuZXJyb3J9YCkpO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgfSk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIERlbGV0ZSBhbiBvdmVycmlkZVxuICAgICAqL1xuICAgIGFzeW5jIGRlbGV0ZShpZCkge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICAgICAgY29uc3QgdHJhbnNhY3Rpb24gPSB0aGlzLmRiLnRyYW5zYWN0aW9uKFtTY2hlZHVsZU92ZXJyaWRlU3RvcmUuU1RPUkVfTkFNRV0sICdyZWFkd3JpdGUnKTtcbiAgICAgICAgICAgIGNvbnN0IHN0b3JlID0gdHJhbnNhY3Rpb24ub2JqZWN0U3RvcmUoU2NoZWR1bGVPdmVycmlkZVN0b3JlLlNUT1JFX05BTUUpO1xuICAgICAgICAgICAgY29uc3QgcmVxdWVzdCA9IHN0b3JlLmRlbGV0ZShpZCk7XG4gICAgICAgICAgICByZXF1ZXN0Lm9uc3VjY2VzcyA9ICgpID0+IHJlc29sdmUoKTtcbiAgICAgICAgICAgIHJlcXVlc3Qub25lcnJvciA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICByZWplY3QobmV3IEVycm9yKGBGYWlsZWQgdG8gZGVsZXRlIG92ZXJyaWRlICR7aWR9OiAke3JlcXVlc3QuZXJyb3J9YCkpO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgfSk7XG4gICAgfVxufVxuIiwgIi8qKlxuICogUmVzb3VyY2VTY2hlZHVsZVNlcnZpY2UgLSBHZXQgZWZmZWN0aXZlIHNjaGVkdWxlIGZvciBhIHJlc291cmNlIG9uIGEgZGF0ZVxuICpcbiAqIExvZ2ljOlxuICogMS4gQ2hlY2sgZm9yIG92ZXJyaWRlIG9uIHRoaXMgZGF0ZVxuICogMi4gRmFsbCBiYWNrIHRvIGRlZmF1bHQgc2NoZWR1bGUgZm9yIHRoZSB3ZWVrZGF5XG4gKi9cbmV4cG9ydCBjbGFzcyBSZXNvdXJjZVNjaGVkdWxlU2VydmljZSB7XG4gICAgY29uc3RydWN0b3IocmVzb3VyY2VTZXJ2aWNlLCBvdmVycmlkZVNlcnZpY2UsIGRhdGVTZXJ2aWNlKSB7XG4gICAgICAgIHRoaXMucmVzb3VyY2VTZXJ2aWNlID0gcmVzb3VyY2VTZXJ2aWNlO1xuICAgICAgICB0aGlzLm92ZXJyaWRlU2VydmljZSA9IG92ZXJyaWRlU2VydmljZTtcbiAgICAgICAgdGhpcy5kYXRlU2VydmljZSA9IGRhdGVTZXJ2aWNlO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBHZXQgZWZmZWN0aXZlIHNjaGVkdWxlIGZvciBhIHJlc291cmNlIG9uIGEgc3BlY2lmaWMgZGF0ZVxuICAgICAqXG4gICAgICogQHBhcmFtIHJlc291cmNlSWQgLSBSZXNvdXJjZSBJRFxuICAgICAqIEBwYXJhbSBkYXRlIC0gRGF0ZSBzdHJpbmcgXCJZWVlZLU1NLUREXCJcbiAgICAgKiBAcmV0dXJucyBJVGltZVNsb3Qgb3IgbnVsbCAoZnJpL2Nsb3NlZClcbiAgICAgKi9cbiAgICBhc3luYyBnZXRTY2hlZHVsZUZvckRhdGUocmVzb3VyY2VJZCwgZGF0ZSkge1xuICAgICAgICAvLyAxLiBDaGVjayBmb3Igb3ZlcnJpZGVcbiAgICAgICAgY29uc3Qgb3ZlcnJpZGUgPSBhd2FpdCB0aGlzLm92ZXJyaWRlU2VydmljZS5nZXRPdmVycmlkZShyZXNvdXJjZUlkLCBkYXRlKTtcbiAgICAgICAgaWYgKG92ZXJyaWRlKSB7XG4gICAgICAgICAgICByZXR1cm4gb3ZlcnJpZGUuc2NoZWR1bGU7XG4gICAgICAgIH1cbiAgICAgICAgLy8gMi4gVXNlIGRlZmF1bHQgc2NoZWR1bGUgZm9yIHdlZWtkYXlcbiAgICAgICAgY29uc3QgcmVzb3VyY2UgPSBhd2FpdCB0aGlzLnJlc291cmNlU2VydmljZS5nZXQocmVzb3VyY2VJZCk7XG4gICAgICAgIGlmICghcmVzb3VyY2UgfHwgIXJlc291cmNlLmRlZmF1bHRTY2hlZHVsZSkge1xuICAgICAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3Qgd2Vla0RheSA9IHRoaXMuZGF0ZVNlcnZpY2UuZ2V0SVNPV2Vla0RheShkYXRlKTtcbiAgICAgICAgcmV0dXJuIHJlc291cmNlLmRlZmF1bHRTY2hlZHVsZVt3ZWVrRGF5XSB8fCBudWxsO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBHZXQgc2NoZWR1bGVzIGZvciBtdWx0aXBsZSBkYXRlc1xuICAgICAqXG4gICAgICogQHBhcmFtIHJlc291cmNlSWQgLSBSZXNvdXJjZSBJRFxuICAgICAqIEBwYXJhbSBkYXRlcyAtIEFycmF5IG9mIGRhdGUgc3RyaW5ncyBcIllZWVktTU0tRERcIlxuICAgICAqIEByZXR1cm5zIE1hcCBvZiBkYXRlIC0+IElUaW1lU2xvdCB8IG51bGxcbiAgICAgKi9cbiAgICBhc3luYyBnZXRTY2hlZHVsZXNGb3JEYXRlcyhyZXNvdXJjZUlkLCBkYXRlcykge1xuICAgICAgICBjb25zdCByZXN1bHQgPSBuZXcgTWFwKCk7XG4gICAgICAgIC8vIEdldCByZXNvdXJjZSBvbmNlXG4gICAgICAgIGNvbnN0IHJlc291cmNlID0gYXdhaXQgdGhpcy5yZXNvdXJjZVNlcnZpY2UuZ2V0KHJlc291cmNlSWQpO1xuICAgICAgICAvLyBHZXQgYWxsIG92ZXJyaWRlcyBpbiBkYXRlIHJhbmdlXG4gICAgICAgIGNvbnN0IG92ZXJyaWRlcyA9IGRhdGVzLmxlbmd0aCA+IDBcbiAgICAgICAgICAgID8gYXdhaXQgdGhpcy5vdmVycmlkZVNlcnZpY2UuZ2V0QnlEYXRlUmFuZ2UocmVzb3VyY2VJZCwgZGF0ZXNbMF0sIGRhdGVzW2RhdGVzLmxlbmd0aCAtIDFdKVxuICAgICAgICAgICAgOiBbXTtcbiAgICAgICAgLy8gQnVpbGQgb3ZlcnJpZGUgbWFwXG4gICAgICAgIGNvbnN0IG92ZXJyaWRlTWFwID0gbmV3IE1hcChvdmVycmlkZXMubWFwKG8gPT4gW28uZGF0ZSwgby5zY2hlZHVsZV0pKTtcbiAgICAgICAgLy8gUmVzb2x2ZSBlYWNoIGRhdGVcbiAgICAgICAgZm9yIChjb25zdCBkYXRlIG9mIGRhdGVzKSB7XG4gICAgICAgICAgICAvLyBDaGVjayBvdmVycmlkZSBmaXJzdFxuICAgICAgICAgICAgaWYgKG92ZXJyaWRlTWFwLmhhcyhkYXRlKSkge1xuICAgICAgICAgICAgICAgIHJlc3VsdC5zZXQoZGF0ZSwgb3ZlcnJpZGVNYXAuZ2V0KGRhdGUpKTtcbiAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIEZhbGwgYmFjayB0byBkZWZhdWx0XG4gICAgICAgICAgICBpZiAocmVzb3VyY2U/LmRlZmF1bHRTY2hlZHVsZSkge1xuICAgICAgICAgICAgICAgIGNvbnN0IHdlZWtEYXkgPSB0aGlzLmRhdGVTZXJ2aWNlLmdldElTT1dlZWtEYXkoZGF0ZSk7XG4gICAgICAgICAgICAgICAgcmVzdWx0LnNldChkYXRlLCByZXNvdXJjZS5kZWZhdWx0U2NoZWR1bGVbd2Vla0RheV0gfHwgbnVsbCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgICByZXN1bHQuc2V0KGRhdGUsIG51bGwpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgfVxufVxuIiwgIi8qKlxuICogU3dwRXZlbnQgLSBXcmFwcGVyIGNsYXNzIGZvciBjYWxlbmRhciBldmVudCBlbGVtZW50c1xuICpcbiAqIEVuY2Fwc3VsYXRlcyBhbiBIVE1MRWxlbWVudCBhbmQgcHJvdmlkZXMgY29tcHV0ZWQgcHJvcGVydGllc1xuICogZm9yIHN0YXJ0L2VuZCB0aW1lcyBiYXNlZCBvbiBlbGVtZW50IHBvc2l0aW9uIGFuZCBncmlkIGNvbmZpZy5cbiAqXG4gKiBVc2FnZTpcbiAqIC0gZXZlbnRJZCBpcyByZWFkIGZyb20gZWxlbWVudC5kYXRhc2V0XG4gKiAtIGNvbHVtbktleSBpZGVudGlmaWVzIHRoZSBjb2x1bW4gdW5pZm9ybWx5XG4gKiAtIFBvc2l0aW9uICh0b3AsIGhlaWdodCkgaXMgcmVhZCBmcm9tIGVsZW1lbnQuc3R5bGVcbiAqIC0gRmFjdG9yeSBtZXRob2QgYGZyb21FbGVtZW50KClgIGNhbGN1bGF0ZXMgRGF0ZSBvYmplY3RzXG4gKi9cbmV4cG9ydCBjbGFzcyBTd3BFdmVudCB7XG4gICAgY29uc3RydWN0b3IoZWxlbWVudCwgY29sdW1uS2V5LCBzdGFydCwgZW5kKSB7XG4gICAgICAgIHRoaXMuZWxlbWVudCA9IGVsZW1lbnQ7XG4gICAgICAgIHRoaXMuY29sdW1uS2V5ID0gY29sdW1uS2V5O1xuICAgICAgICB0aGlzLl9zdGFydCA9IHN0YXJ0O1xuICAgICAgICB0aGlzLl9lbmQgPSBlbmQ7XG4gICAgfVxuICAgIC8qKiBFdmVudCBJRCBmcm9tIGVsZW1lbnQuZGF0YXNldC5ldmVudElkICovXG4gICAgZ2V0IGV2ZW50SWQoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmVsZW1lbnQuZGF0YXNldC5ldmVudElkIHx8ICcnO1xuICAgIH1cbiAgICBnZXQgc3RhcnQoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLl9zdGFydDtcbiAgICB9XG4gICAgZ2V0IGVuZCgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX2VuZDtcbiAgICB9XG4gICAgLyoqIER1cmF0aW9uIGluIG1pbnV0ZXMgKi9cbiAgICBnZXQgZHVyYXRpb25NaW51dGVzKCkge1xuICAgICAgICByZXR1cm4gKHRoaXMuX2VuZC5nZXRUaW1lKCkgLSB0aGlzLl9zdGFydC5nZXRUaW1lKCkpIC8gKDEwMDAgKiA2MCk7XG4gICAgfVxuICAgIC8qKiBEdXJhdGlvbiBpbiBtaWxsaXNlY29uZHMgKi9cbiAgICBnZXQgZHVyYXRpb25NcygpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX2VuZC5nZXRUaW1lKCkgLSB0aGlzLl9zdGFydC5nZXRUaW1lKCk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEZhY3Rvcnk6IENyZWF0ZSBTd3BFdmVudCBmcm9tIGVsZW1lbnQgKyBjb2x1bW5LZXlcbiAgICAgKiBSZWFkcyB0b3AvaGVpZ2h0IGZyb20gZWxlbWVudC5zdHlsZSB0byBjYWxjdWxhdGUgc3RhcnQvZW5kXG4gICAgICogQHBhcmFtIGNvbHVtbktleSAtIE9wYXF1ZSBjb2x1bW4gaWRlbnRpZmllciAoZG8gTk9UIHBhcnNlIC0gdXNlIG9ubHkgZm9yIG1hdGNoaW5nKVxuICAgICAqIEBwYXJhbSBkYXRlIC0gRGF0ZSBzdHJpbmcgKFlZWVktTU0tREQpIGZvciB0aW1lIGNhbGN1bGF0aW9uc1xuICAgICAqL1xuICAgIHN0YXRpYyBmcm9tRWxlbWVudChlbGVtZW50LCBjb2x1bW5LZXksIGRhdGUsIGdyaWRDb25maWcpIHtcbiAgICAgICAgY29uc3QgdG9wUGl4ZWxzID0gcGFyc2VGbG9hdChlbGVtZW50LnN0eWxlLnRvcCkgfHwgMDtcbiAgICAgICAgY29uc3QgaGVpZ2h0UGl4ZWxzID0gcGFyc2VGbG9hdChlbGVtZW50LnN0eWxlLmhlaWdodCkgfHwgMDtcbiAgICAgICAgLy8gQ2FsY3VsYXRlIHN0YXJ0IGZyb20gdG9wIHBvc2l0aW9uXG4gICAgICAgIGNvbnN0IHN0YXJ0TWludXRlc0Zyb21HcmlkID0gKHRvcFBpeGVscyAvIGdyaWRDb25maWcuaG91ckhlaWdodCkgKiA2MDtcbiAgICAgICAgY29uc3QgdG90YWxNaW51dGVzID0gKGdyaWRDb25maWcuZGF5U3RhcnRIb3VyICogNjApICsgc3RhcnRNaW51dGVzRnJvbUdyaWQ7XG4gICAgICAgIGNvbnN0IHN0YXJ0ID0gbmV3IERhdGUoZGF0ZSk7XG4gICAgICAgIHN0YXJ0LnNldEhvdXJzKE1hdGguZmxvb3IodG90YWxNaW51dGVzIC8gNjApLCB0b3RhbE1pbnV0ZXMgJSA2MCwgMCwgMCk7XG4gICAgICAgIC8vIENhbGN1bGF0ZSBlbmQgZnJvbSBoZWlnaHRcbiAgICAgICAgY29uc3QgZHVyYXRpb25NaW51dGVzID0gKGhlaWdodFBpeGVscyAvIGdyaWRDb25maWcuaG91ckhlaWdodCkgKiA2MDtcbiAgICAgICAgY29uc3QgZW5kID0gbmV3IERhdGUoc3RhcnQuZ2V0VGltZSgpICsgZHVyYXRpb25NaW51dGVzICogNjAgKiAxMDAwKTtcbiAgICAgICAgcmV0dXJuIG5ldyBTd3BFdmVudChlbGVtZW50LCBjb2x1bW5LZXksIHN0YXJ0LCBlbmQpO1xuICAgIH1cbn1cbiIsICJpbXBvcnQgeyBDb3JlRXZlbnRzIH0gZnJvbSAnLi4vY29uc3RhbnRzL0NvcmVFdmVudHMnO1xuaW1wb3J0IHsgc25hcFRvR3JpZCB9IGZyb20gJy4uL3V0aWxzL1Bvc2l0aW9uVXRpbHMnO1xuaW1wb3J0IHsgU3dwRXZlbnQgfSBmcm9tICcuLi90eXBlcy9Td3BFdmVudCc7XG4vKipcbiAqIERyYWdEcm9wTWFuYWdlciAtIEhhbmRsZXMgZHJhZy1kcm9wIGZvciBjYWxlbmRhciBldmVudHNcbiAqXG4gKiBTdHJhdGVneTogRHJhZyBvcmlnaW5hbCBlbGVtZW50LCBsZWF2ZSBnaG9zdC1jbG9uZSBpbiBwbGFjZVxuICogLSBtb3VzZWRvd246IFN0b3JlIGluaXRpYWwgc3RhdGUsIHdhaXQgZm9yIG1vdmVtZW50XG4gKiAtIG1vdXNlbW92ZSAoPjVweCk6IENyZWF0ZSBnaG9zdCwgc3RhcnQgZHJhZ2dpbmcgb3JpZ2luYWxcbiAqIC0gbW91c2V1cDogU25hcCB0byBncmlkLCByZW1vdmUgZ2hvc3QsIGVtaXQgZHJhZzplbmRcbiAqIC0gY2FuY2VsOiBBbmltYXRlIGJhY2sgdG8gc3RhcnRZLCByZW1vdmUgZ2hvc3RcbiAqL1xuZXhwb3J0IGNsYXNzIERyYWdEcm9wTWFuYWdlciB7XG4gICAgY29uc3RydWN0b3IoZXZlbnRCdXMsIGdyaWRDb25maWcpIHtcbiAgICAgICAgdGhpcy5ldmVudEJ1cyA9IGV2ZW50QnVzO1xuICAgICAgICB0aGlzLmdyaWRDb25maWcgPSBncmlkQ29uZmlnO1xuICAgICAgICB0aGlzLmRyYWdTdGF0ZSA9IG51bGw7XG4gICAgICAgIHRoaXMubW91c2VEb3duUG9zaXRpb24gPSBudWxsO1xuICAgICAgICB0aGlzLnBlbmRpbmdFbGVtZW50ID0gbnVsbDtcbiAgICAgICAgdGhpcy5wZW5kaW5nTW91c2VPZmZzZXQgPSBudWxsO1xuICAgICAgICB0aGlzLmNvbnRhaW5lciA9IG51bGw7XG4gICAgICAgIHRoaXMuaW5IZWFkZXIgPSBmYWxzZTtcbiAgICAgICAgdGhpcy5EUkFHX1RIUkVTSE9MRCA9IDU7XG4gICAgICAgIHRoaXMuSU5URVJQT0xBVElPTl9GQUNUT1IgPSAwLjM7XG4gICAgICAgIHRoaXMuaGFuZGxlUG9pbnRlckRvd24gPSAoZSkgPT4ge1xuICAgICAgICAgICAgY29uc3QgdGFyZ2V0ID0gZS50YXJnZXQ7XG4gICAgICAgICAgICAvLyBJZ25vcmUgaWYgY2xpY2tpbmcgb24gcmVzaXplIGhhbmRsZVxuICAgICAgICAgICAgaWYgKHRhcmdldC5jbG9zZXN0KCdzd3AtcmVzaXplLWhhbmRsZScpKVxuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIC8vIE1hdGNoIGJvdGggc3dwLWV2ZW50IGFuZCBzd3AtaGVhZGVyLWl0ZW1cbiAgICAgICAgICAgIGNvbnN0IGV2ZW50RWxlbWVudCA9IHRhcmdldC5jbG9zZXN0KCdzd3AtZXZlbnQnKTtcbiAgICAgICAgICAgIGNvbnN0IGhlYWRlckl0ZW0gPSB0YXJnZXQuY2xvc2VzdCgnc3dwLWhlYWRlci1pdGVtJyk7XG4gICAgICAgICAgICBjb25zdCBkcmFnZ2FibGUgPSBldmVudEVsZW1lbnQgfHwgaGVhZGVySXRlbTtcbiAgICAgICAgICAgIGlmICghZHJhZ2dhYmxlKVxuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIC8vIFN0b3JlIGZvciBwb3RlbnRpYWwgZHJhZ1xuICAgICAgICAgICAgdGhpcy5tb3VzZURvd25Qb3NpdGlvbiA9IHsgeDogZS5jbGllbnRYLCB5OiBlLmNsaWVudFkgfTtcbiAgICAgICAgICAgIHRoaXMucGVuZGluZ0VsZW1lbnQgPSBkcmFnZ2FibGU7XG4gICAgICAgICAgICAvLyBDYWxjdWxhdGUgbW91c2Ugb2Zmc2V0IHdpdGhpbiBlbGVtZW50XG4gICAgICAgICAgICBjb25zdCByZWN0ID0gZHJhZ2dhYmxlLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgICAgICAgICAgdGhpcy5wZW5kaW5nTW91c2VPZmZzZXQgPSB7XG4gICAgICAgICAgICAgICAgeDogZS5jbGllbnRYIC0gcmVjdC5sZWZ0LFxuICAgICAgICAgICAgICAgIHk6IGUuY2xpZW50WSAtIHJlY3QudG9wXG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgLy8gQ2FwdHVyZSBwb2ludGVyIGZvciByZWxpYWJsZSB0cmFja2luZ1xuICAgICAgICAgICAgZHJhZ2dhYmxlLnNldFBvaW50ZXJDYXB0dXJlKGUucG9pbnRlcklkKTtcbiAgICAgICAgfTtcbiAgICAgICAgdGhpcy5oYW5kbGVQb2ludGVyTW92ZSA9IChlKSA9PiB7XG4gICAgICAgICAgICAvLyBOb3QgaW4gcG90ZW50aWFsIGRyYWcgc3RhdGVcbiAgICAgICAgICAgIGlmICghdGhpcy5tb3VzZURvd25Qb3NpdGlvbiB8fCAhdGhpcy5wZW5kaW5nRWxlbWVudCkge1xuICAgICAgICAgICAgICAgIC8vIEFscmVhZHkgZHJhZ2dpbmcgLSB1cGRhdGUgdGFyZ2V0XG4gICAgICAgICAgICAgICAgaWYgKHRoaXMuZHJhZ1N0YXRlKSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMudXBkYXRlRHJhZ1RhcmdldChlKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgLy8gQ2hlY2sgdGhyZXNob2xkXG4gICAgICAgICAgICBjb25zdCBkZWx0YVggPSBNYXRoLmFicyhlLmNsaWVudFggLSB0aGlzLm1vdXNlRG93blBvc2l0aW9uLngpO1xuICAgICAgICAgICAgY29uc3QgZGVsdGFZID0gTWF0aC5hYnMoZS5jbGllbnRZIC0gdGhpcy5tb3VzZURvd25Qb3NpdGlvbi55KTtcbiAgICAgICAgICAgIGNvbnN0IGRpc3RhbmNlID0gTWF0aC5zcXJ0KGRlbHRhWCAqIGRlbHRhWCArIGRlbHRhWSAqIGRlbHRhWSk7XG4gICAgICAgICAgICBpZiAoZGlzdGFuY2UgPCB0aGlzLkRSQUdfVEhSRVNIT0xEKVxuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIC8vIFN0YXJ0IGRyYWdcbiAgICAgICAgICAgIHRoaXMuaW5pdGlhbGl6ZURyYWcodGhpcy5wZW5kaW5nRWxlbWVudCwgdGhpcy5wZW5kaW5nTW91c2VPZmZzZXQsIGUpO1xuICAgICAgICAgICAgdGhpcy5tb3VzZURvd25Qb3NpdGlvbiA9IG51bGw7XG4gICAgICAgICAgICB0aGlzLnBlbmRpbmdFbGVtZW50ID0gbnVsbDtcbiAgICAgICAgICAgIHRoaXMucGVuZGluZ01vdXNlT2Zmc2V0ID0gbnVsbDtcbiAgICAgICAgfTtcbiAgICAgICAgdGhpcy5oYW5kbGVQb2ludGVyVXAgPSAoX2UpID0+IHtcbiAgICAgICAgICAgIC8vIENsZWFyIHBlbmRpbmcgc3RhdGVcbiAgICAgICAgICAgIHRoaXMubW91c2VEb3duUG9zaXRpb24gPSBudWxsO1xuICAgICAgICAgICAgdGhpcy5wZW5kaW5nRWxlbWVudCA9IG51bGw7XG4gICAgICAgICAgICB0aGlzLnBlbmRpbmdNb3VzZU9mZnNldCA9IG51bGw7XG4gICAgICAgICAgICBpZiAoIXRoaXMuZHJhZ1N0YXRlKVxuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIC8vIFN0b3AgYW5pbWF0aW9uXG4gICAgICAgICAgICBjYW5jZWxBbmltYXRpb25GcmFtZSh0aGlzLmRyYWdTdGF0ZS5hbmltYXRpb25JZCk7XG4gICAgICAgICAgICAvLyBIYW5kbGUgYmFzZWQgb24gZHJhZyBzb3VyY2UgYW5kIHRhcmdldFxuICAgICAgICAgICAgaWYgKHRoaXMuZHJhZ1N0YXRlLmRyYWdTb3VyY2UgPT09ICdoZWFkZXInKSB7XG4gICAgICAgICAgICAgICAgLy8gSGVhZGVyIGl0ZW0gZHJhZyBlbmRcbiAgICAgICAgICAgICAgICB0aGlzLmhhbmRsZUhlYWRlckl0ZW1EcmFnRW5kKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgICAvLyBHcmlkIGV2ZW50IGRyYWcgZW5kXG4gICAgICAgICAgICAgICAgdGhpcy5oYW5kbGVHcmlkRXZlbnREcmFnRW5kKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyBDbGVhbnVwXG4gICAgICAgICAgICB0aGlzLmRyYWdTdGF0ZS5lbGVtZW50LmNsYXNzTGlzdC5yZW1vdmUoJ2RyYWdnaW5nJyk7XG4gICAgICAgICAgICB0aGlzLmRyYWdTdGF0ZSA9IG51bGw7XG4gICAgICAgICAgICB0aGlzLmluSGVhZGVyID0gZmFsc2U7XG4gICAgICAgIH07XG4gICAgICAgIHRoaXMuYW5pbWF0ZURyYWcgPSAoKSA9PiB7XG4gICAgICAgICAgICBpZiAoIXRoaXMuZHJhZ1N0YXRlKVxuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIGNvbnN0IGRpZmYgPSB0aGlzLmRyYWdTdGF0ZS50YXJnZXRZIC0gdGhpcy5kcmFnU3RhdGUuY3VycmVudFk7XG4gICAgICAgICAgICAvLyBTdG9wIGFuaW1hdGlvbiB3aGVuIGNsb3NlIGVub3VnaCB0byB0YXJnZXRcbiAgICAgICAgICAgIGlmIChNYXRoLmFicyhkaWZmKSA8PSAwLjUpIHtcbiAgICAgICAgICAgICAgICB0aGlzLmRyYWdTdGF0ZS5hbmltYXRpb25JZCA9IDA7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgLy8gSW50ZXJwb2xhdGUgdG93YXJkcyB0YXJnZXRcbiAgICAgICAgICAgIHRoaXMuZHJhZ1N0YXRlLmN1cnJlbnRZICs9IGRpZmYgKiB0aGlzLklOVEVSUE9MQVRJT05fRkFDVE9SO1xuICAgICAgICAgICAgLy8gVXBkYXRlIGVsZW1lbnQgcG9zaXRpb25cbiAgICAgICAgICAgIHRoaXMuZHJhZ1N0YXRlLmVsZW1lbnQuc3R5bGUudG9wID0gYCR7dGhpcy5kcmFnU3RhdGUuY3VycmVudFl9cHhgO1xuICAgICAgICAgICAgLy8gRW1pdCBkcmFnOm1vdmUgKG9ubHkgaWYgd2UgaGF2ZSBhIGNvbHVtbilcbiAgICAgICAgICAgIGlmICh0aGlzLmRyYWdTdGF0ZS5jb2x1bW5FbGVtZW50KSB7XG4gICAgICAgICAgICAgICAgY29uc3QgcGF5bG9hZCA9IHtcbiAgICAgICAgICAgICAgICAgICAgZXZlbnRJZDogdGhpcy5kcmFnU3RhdGUuZXZlbnRJZCxcbiAgICAgICAgICAgICAgICAgICAgZWxlbWVudDogdGhpcy5kcmFnU3RhdGUuZWxlbWVudCxcbiAgICAgICAgICAgICAgICAgICAgY3VycmVudFk6IHRoaXMuZHJhZ1N0YXRlLmN1cnJlbnRZLFxuICAgICAgICAgICAgICAgICAgICBjb2x1bW5FbGVtZW50OiB0aGlzLmRyYWdTdGF0ZS5jb2x1bW5FbGVtZW50XG4gICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICB0aGlzLmV2ZW50QnVzLmVtaXQoQ29yZUV2ZW50cy5FVkVOVF9EUkFHX01PVkUsIHBheWxvYWQpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgLy8gQ29udGludWUgYW5pbWF0aW9uXG4gICAgICAgICAgICB0aGlzLmRyYWdTdGF0ZS5hbmltYXRpb25JZCA9IHJlcXVlc3RBbmltYXRpb25GcmFtZSh0aGlzLmFuaW1hdGVEcmFnKTtcbiAgICAgICAgfTtcbiAgICAgICAgdGhpcy5zZXR1cFNjcm9sbExpc3RlbmVyKCk7XG4gICAgfVxuICAgIHNldHVwU2Nyb2xsTGlzdGVuZXIoKSB7XG4gICAgICAgIHRoaXMuZXZlbnRCdXMub24oQ29yZUV2ZW50cy5FREdFX1NDUk9MTF9USUNLLCAoZSkgPT4ge1xuICAgICAgICAgICAgaWYgKCF0aGlzLmRyYWdTdGF0ZSlcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICBjb25zdCB7IHNjcm9sbERlbHRhIH0gPSBlLmRldGFpbDtcbiAgICAgICAgICAgIC8vIEVsZW1lbnQgc2thbCBmbHl0dGUgbWVkIHNjcm9sbCBmb3IgYXQgZm9yYmxpdmUgdW5kZXIgbXVzZW5cbiAgICAgICAgICAgIC8vIChlbGVtZW50ZXRzIHRvcCBlciByZWxhdGl2IHRpbCBrb2xvbm5lbiwgc29tIHNjcm9sbGVyIG1lZCB2aWV3cG9ydClcbiAgICAgICAgICAgIHRoaXMuZHJhZ1N0YXRlLnRhcmdldFkgKz0gc2Nyb2xsRGVsdGE7XG4gICAgICAgICAgICB0aGlzLmRyYWdTdGF0ZS5jdXJyZW50WSArPSBzY3JvbGxEZWx0YTtcbiAgICAgICAgICAgIHRoaXMuZHJhZ1N0YXRlLmVsZW1lbnQuc3R5bGUudG9wID0gYCR7dGhpcy5kcmFnU3RhdGUuY3VycmVudFl9cHhgO1xuICAgICAgICB9KTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogSW5pdGlhbGl6ZSBkcmFnLWRyb3Agb24gYSBjb250YWluZXIgZWxlbWVudFxuICAgICAqL1xuICAgIGluaXQoY29udGFpbmVyKSB7XG4gICAgICAgIHRoaXMuY29udGFpbmVyID0gY29udGFpbmVyO1xuICAgICAgICBjb250YWluZXIuYWRkRXZlbnRMaXN0ZW5lcigncG9pbnRlcmRvd24nLCB0aGlzLmhhbmRsZVBvaW50ZXJEb3duKTtcbiAgICAgICAgZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcigncG9pbnRlcm1vdmUnLCB0aGlzLmhhbmRsZVBvaW50ZXJNb3ZlKTtcbiAgICAgICAgZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcigncG9pbnRlcnVwJywgdGhpcy5oYW5kbGVQb2ludGVyVXApO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBIYW5kbGUgZHJhZyBlbmQgZm9yIGhlYWRlciBpdGVtc1xuICAgICAqL1xuICAgIGhhbmRsZUhlYWRlckl0ZW1EcmFnRW5kKCkge1xuICAgICAgICBpZiAoIXRoaXMuZHJhZ1N0YXRlKVxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAvLyBJZiBkcm9wcGVkIGluIGdyaWQgKG5vdCBpbiBoZWFkZXIpLCB0aGUgc3dwLWV2ZW50IHdhcyBhbHJlYWR5IGNyZWF0ZWRcbiAgICAgICAgLy8gYnkgRXZlbnRSZW5kZXJlciBsaXN0ZW5pbmcgdG8gRVZFTlRfRFJBR19MRUFWRV9IRUFERVJcbiAgICAgICAgLy8gSnVzdCBlbWl0IGRyYWc6ZW5kIGZvciBwZXJzaXN0ZW5jZVxuICAgICAgICBpZiAoIXRoaXMuaW5IZWFkZXIgJiYgdGhpcy5kcmFnU3RhdGUuY3VycmVudENvbHVtbikge1xuICAgICAgICAgICAgLy8gRHJvcHBlZCBpbiBncmlkIC0gZW1pdCBkcmFnOmVuZCB3aXRoIHRoZSBuZXcgc3dwLWV2ZW50IGVsZW1lbnRcbiAgICAgICAgICAgIGNvbnN0IGdyaWRFdmVudCA9IHRoaXMuZHJhZ1N0YXRlLmN1cnJlbnRDb2x1bW4ucXVlcnlTZWxlY3Rvcihgc3dwLWV2ZW50W2RhdGEtZXZlbnQtaWQ9XCIke3RoaXMuZHJhZ1N0YXRlLmV2ZW50SWR9XCJdYCk7XG4gICAgICAgICAgICBpZiAoZ3JpZEV2ZW50KSB7XG4gICAgICAgICAgICAgICAgY29uc3QgY29sdW1uS2V5ID0gdGhpcy5kcmFnU3RhdGUuY3VycmVudENvbHVtbi5kYXRhc2V0LmNvbHVtbktleSB8fCAnJztcbiAgICAgICAgICAgICAgICBjb25zdCBkYXRlID0gdGhpcy5kcmFnU3RhdGUuY3VycmVudENvbHVtbi5kYXRhc2V0LmRhdGUgfHwgJyc7XG4gICAgICAgICAgICAgICAgY29uc3Qgc3dwRXZlbnQgPSBTd3BFdmVudC5mcm9tRWxlbWVudChncmlkRXZlbnQsIGNvbHVtbktleSwgZGF0ZSwgdGhpcy5ncmlkQ29uZmlnKTtcbiAgICAgICAgICAgICAgICBjb25zdCBwYXlsb2FkID0ge1xuICAgICAgICAgICAgICAgICAgICBzd3BFdmVudCxcbiAgICAgICAgICAgICAgICAgICAgc291cmNlQ29sdW1uS2V5OiB0aGlzLmRyYWdTdGF0ZS5zb3VyY2VDb2x1bW5LZXksXG4gICAgICAgICAgICAgICAgICAgIHRhcmdldDogJ2dyaWQnXG4gICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICB0aGlzLmV2ZW50QnVzLmVtaXQoQ29yZUV2ZW50cy5FVkVOVF9EUkFHX0VORCwgcGF5bG9hZCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgLy8gSWYgc3RpbGwgaW4gaGVhZGVyLCBubyBwZXJzaXN0ZW5jZSBuZWVkZWQgKHN0YXllZCBpbiBoZWFkZXIpXG4gICAgfVxuICAgIC8qKlxuICAgICAqIEhhbmRsZSBkcmFnIGVuZCBmb3IgZ3JpZCBldmVudHNcbiAgICAgKi9cbiAgICBoYW5kbGVHcmlkRXZlbnREcmFnRW5kKCkge1xuICAgICAgICBpZiAoIXRoaXMuZHJhZ1N0YXRlIHx8ICF0aGlzLmRyYWdTdGF0ZS5jb2x1bW5FbGVtZW50KVxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAvLyBTbmFwIHRvIGdyaWRcbiAgICAgICAgY29uc3Qgc25hcHBlZFkgPSBzbmFwVG9HcmlkKHRoaXMuZHJhZ1N0YXRlLmN1cnJlbnRZLCB0aGlzLmdyaWRDb25maWcpO1xuICAgICAgICB0aGlzLmRyYWdTdGF0ZS5lbGVtZW50LnN0eWxlLnRvcCA9IGAke3NuYXBwZWRZfXB4YDtcbiAgICAgICAgLy8gUmVtb3ZlIGdob3N0XG4gICAgICAgIHRoaXMuZHJhZ1N0YXRlLmdob3N0RWxlbWVudD8ucmVtb3ZlKCk7XG4gICAgICAgIC8vIEdldCBjb2x1bW5LZXkgYW5kIGRhdGUgZnJvbSB0YXJnZXQgY29sdW1uXG4gICAgICAgIGNvbnN0IGNvbHVtbktleSA9IHRoaXMuZHJhZ1N0YXRlLmNvbHVtbkVsZW1lbnQuZGF0YXNldC5jb2x1bW5LZXkgfHwgJyc7XG4gICAgICAgIGNvbnN0IGRhdGUgPSB0aGlzLmRyYWdTdGF0ZS5jb2x1bW5FbGVtZW50LmRhdGFzZXQuZGF0ZSB8fCAnJztcbiAgICAgICAgLy8gQ3JlYXRlIFN3cEV2ZW50IGZyb20gZWxlbWVudCAocmVhZHMgdG9wL2hlaWdodC9ldmVudElkIGZyb20gZWxlbWVudClcbiAgICAgICAgY29uc3Qgc3dwRXZlbnQgPSBTd3BFdmVudC5mcm9tRWxlbWVudCh0aGlzLmRyYWdTdGF0ZS5lbGVtZW50LCBjb2x1bW5LZXksIGRhdGUsIHRoaXMuZ3JpZENvbmZpZyk7XG4gICAgICAgIC8vIEVtaXQgZHJhZzplbmRcbiAgICAgICAgY29uc3QgcGF5bG9hZCA9IHtcbiAgICAgICAgICAgIHN3cEV2ZW50LFxuICAgICAgICAgICAgc291cmNlQ29sdW1uS2V5OiB0aGlzLmRyYWdTdGF0ZS5zb3VyY2VDb2x1bW5LZXksXG4gICAgICAgICAgICB0YXJnZXQ6IHRoaXMuaW5IZWFkZXIgPyAnaGVhZGVyJyA6ICdncmlkJ1xuICAgICAgICB9O1xuICAgICAgICB0aGlzLmV2ZW50QnVzLmVtaXQoQ29yZUV2ZW50cy5FVkVOVF9EUkFHX0VORCwgcGF5bG9hZCk7XG4gICAgfVxuICAgIGluaXRpYWxpemVEcmFnKGVsZW1lbnQsIG1vdXNlT2Zmc2V0LCBlKSB7XG4gICAgICAgIGNvbnN0IGV2ZW50SWQgPSBlbGVtZW50LmRhdGFzZXQuZXZlbnRJZCB8fCAnJztcbiAgICAgICAgY29uc3QgaXNIZWFkZXJJdGVtID0gZWxlbWVudC50YWdOYW1lLnRvTG93ZXJDYXNlKCkgPT09ICdzd3AtaGVhZGVyLWl0ZW0nO1xuICAgICAgICBjb25zdCBjb2x1bW5FbGVtZW50ID0gZWxlbWVudC5jbG9zZXN0KCdzd3AtZGF5LWNvbHVtbicpO1xuICAgICAgICAvLyBGb3IgZ3JpZCBldmVudHMsIHdlIG5lZWQgYSBjb2x1bW5cbiAgICAgICAgaWYgKCFpc0hlYWRlckl0ZW0gJiYgIWNvbHVtbkVsZW1lbnQpXG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIGlmIChpc0hlYWRlckl0ZW0pIHtcbiAgICAgICAgICAgIC8vIEhlYWRlciBpdGVtIGRyYWcgaW5pdGlhbGl6YXRpb25cbiAgICAgICAgICAgIHRoaXMuaW5pdGlhbGl6ZUhlYWRlckl0ZW1EcmFnKGVsZW1lbnQsIG1vdXNlT2Zmc2V0LCBldmVudElkKTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIC8vIEdyaWQgZXZlbnQgZHJhZyBpbml0aWFsaXphdGlvblxuICAgICAgICAgICAgdGhpcy5pbml0aWFsaXplR3JpZEV2ZW50RHJhZyhlbGVtZW50LCBtb3VzZU9mZnNldCwgZSwgY29sdW1uRWxlbWVudCwgZXZlbnRJZCk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgLyoqXG4gICAgICogSW5pdGlhbGl6ZSBkcmFnIGZvciBhIGhlYWRlciBpdGVtIChhbGxEYXkgZXZlbnQpXG4gICAgICovXG4gICAgaW5pdGlhbGl6ZUhlYWRlckl0ZW1EcmFnKGVsZW1lbnQsIG1vdXNlT2Zmc2V0LCBldmVudElkKSB7XG4gICAgICAgIC8vIE1hcmsgYXMgZHJhZ2dpbmdcbiAgICAgICAgZWxlbWVudC5jbGFzc0xpc3QuYWRkKCdkcmFnZ2luZycpO1xuICAgICAgICAvLyBJbml0aWFsaXplIGRyYWcgc3RhdGUgZm9yIGhlYWRlciBpdGVtXG4gICAgICAgIHRoaXMuZHJhZ1N0YXRlID0ge1xuICAgICAgICAgICAgZXZlbnRJZCxcbiAgICAgICAgICAgIGVsZW1lbnQsXG4gICAgICAgICAgICBnaG9zdEVsZW1lbnQ6IG51bGwsIC8vIE5vIGdob3N0IGZvciBoZWFkZXIgaXRlbXNcbiAgICAgICAgICAgIHN0YXJ0WTogMCxcbiAgICAgICAgICAgIG1vdXNlT2Zmc2V0LFxuICAgICAgICAgICAgY29sdW1uRWxlbWVudDogbnVsbCxcbiAgICAgICAgICAgIGN1cnJlbnRDb2x1bW46IG51bGwsXG4gICAgICAgICAgICB0YXJnZXRZOiAwLFxuICAgICAgICAgICAgY3VycmVudFk6IDAsXG4gICAgICAgICAgICBhbmltYXRpb25JZDogMCxcbiAgICAgICAgICAgIHNvdXJjZUNvbHVtbktleTogJycsIC8vIFdpbGwgYmUgc2V0IGZyb20gaGVhZGVyIGl0ZW0gZGF0YVxuICAgICAgICAgICAgZHJhZ1NvdXJjZTogJ2hlYWRlcidcbiAgICAgICAgfTtcbiAgICAgICAgLy8gU3RhcnQgaW4gaGVhZGVyIG1vZGVcbiAgICAgICAgdGhpcy5pbkhlYWRlciA9IHRydWU7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEluaXRpYWxpemUgZHJhZyBmb3IgYSBncmlkIGV2ZW50XG4gICAgICovXG4gICAgaW5pdGlhbGl6ZUdyaWRFdmVudERyYWcoZWxlbWVudCwgbW91c2VPZmZzZXQsIGUsIGNvbHVtbkVsZW1lbnQsIGV2ZW50SWQpIHtcbiAgICAgICAgLy8gQ2FsY3VsYXRlIGFic29sdXRlIFkgcG9zaXRpb24gdXNpbmcgZ2V0Qm91bmRpbmdDbGllbnRSZWN0XG4gICAgICAgIGNvbnN0IGVsZW1lbnRSZWN0ID0gZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcbiAgICAgICAgY29uc3QgY29sdW1uUmVjdCA9IGNvbHVtbkVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG4gICAgICAgIGNvbnN0IHN0YXJ0WSA9IGVsZW1lbnRSZWN0LnRvcCAtIGNvbHVtblJlY3QudG9wO1xuICAgICAgICAvLyBJZiBldmVudCBpcyBpbnNpZGUgYSBncm91cCwgbW92ZSBpdCB0byBldmVudHMtbGF5ZXIgZm9yIGNvcnJlY3QgcG9zaXRpb25pbmcgZHVyaW5nIGRyYWdcbiAgICAgICAgY29uc3QgZ3JvdXAgPSBlbGVtZW50LmNsb3Nlc3QoJ3N3cC1ldmVudC1ncm91cCcpO1xuICAgICAgICBpZiAoZ3JvdXApIHtcbiAgICAgICAgICAgIGNvbnN0IGV2ZW50c0xheWVyID0gY29sdW1uRWxlbWVudC5xdWVyeVNlbGVjdG9yKCdzd3AtZXZlbnRzLWxheWVyJyk7XG4gICAgICAgICAgICBpZiAoZXZlbnRzTGF5ZXIpIHtcbiAgICAgICAgICAgICAgICBldmVudHNMYXllci5hcHBlbmRDaGlsZChlbGVtZW50KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICAvLyBTZXQgY29uc2lzdGVudCBwb3NpdGlvbmluZyBmb3IgZHJhZyAod29ya3MgZm9yIGJvdGggZ3JvdXBlZCBhbmQgc3RhY2tlZCBldmVudHMpXG4gICAgICAgIGVsZW1lbnQuc3R5bGUucG9zaXRpb24gPSAnYWJzb2x1dGUnO1xuICAgICAgICBlbGVtZW50LnN0eWxlLnRvcCA9IGAke3N0YXJ0WX1weGA7XG4gICAgICAgIGVsZW1lbnQuc3R5bGUubGVmdCA9ICcycHgnO1xuICAgICAgICBlbGVtZW50LnN0eWxlLnJpZ2h0ID0gJzJweCc7XG4gICAgICAgIGVsZW1lbnQuc3R5bGUubWFyZ2luTGVmdCA9ICcwJzsgLy8gUmVzZXQgc3RhY2tpbmcgbWFyZ2luXG4gICAgICAgIC8vIENyZWF0ZSBnaG9zdCBjbG9uZVxuICAgICAgICBjb25zdCBnaG9zdEVsZW1lbnQgPSBlbGVtZW50LmNsb25lTm9kZSh0cnVlKTtcbiAgICAgICAgZ2hvc3RFbGVtZW50LmNsYXNzTGlzdC5hZGQoJ2RyYWctZ2hvc3QnKTtcbiAgICAgICAgZ2hvc3RFbGVtZW50LnN0eWxlLm9wYWNpdHkgPSAnMC4zJztcbiAgICAgICAgZ2hvc3RFbGVtZW50LnN0eWxlLnBvaW50ZXJFdmVudHMgPSAnbm9uZSc7XG4gICAgICAgIC8vIEluc2VydCBnaG9zdCBiZWZvcmUgb3JpZ2luYWxcbiAgICAgICAgZWxlbWVudC5wYXJlbnROb2RlPy5pbnNlcnRCZWZvcmUoZ2hvc3RFbGVtZW50LCBlbGVtZW50KTtcbiAgICAgICAgLy8gU2V0dXAgZWxlbWVudCBmb3IgZHJhZ2dpbmdcbiAgICAgICAgZWxlbWVudC5jbGFzc0xpc3QuYWRkKCdkcmFnZ2luZycpO1xuICAgICAgICAvLyBDYWxjdWxhdGUgaW5pdGlhbCB0YXJnZXQgZnJvbSBtb3VzZSBwb3NpdGlvblxuICAgICAgICBjb25zdCB0YXJnZXRZID0gZS5jbGllbnRZIC0gY29sdW1uUmVjdC50b3AgLSBtb3VzZU9mZnNldC55O1xuICAgICAgICAvLyBJbml0aWFsaXplIGRyYWcgc3RhdGVcbiAgICAgICAgdGhpcy5kcmFnU3RhdGUgPSB7XG4gICAgICAgICAgICBldmVudElkLFxuICAgICAgICAgICAgZWxlbWVudCxcbiAgICAgICAgICAgIGdob3N0RWxlbWVudCxcbiAgICAgICAgICAgIHN0YXJ0WSxcbiAgICAgICAgICAgIG1vdXNlT2Zmc2V0LFxuICAgICAgICAgICAgY29sdW1uRWxlbWVudCxcbiAgICAgICAgICAgIGN1cnJlbnRDb2x1bW46IGNvbHVtbkVsZW1lbnQsXG4gICAgICAgICAgICB0YXJnZXRZOiBNYXRoLm1heCgwLCB0YXJnZXRZKSxcbiAgICAgICAgICAgIGN1cnJlbnRZOiBzdGFydFksXG4gICAgICAgICAgICBhbmltYXRpb25JZDogMCxcbiAgICAgICAgICAgIHNvdXJjZUNvbHVtbktleTogY29sdW1uRWxlbWVudC5kYXRhc2V0LmNvbHVtbktleSB8fCAnJyxcbiAgICAgICAgICAgIGRyYWdTb3VyY2U6ICdncmlkJ1xuICAgICAgICB9O1xuICAgICAgICAvLyBFbWl0IGRyYWc6c3RhcnRcbiAgICAgICAgY29uc3QgcGF5bG9hZCA9IHtcbiAgICAgICAgICAgIGV2ZW50SWQsXG4gICAgICAgICAgICBlbGVtZW50LFxuICAgICAgICAgICAgZ2hvc3RFbGVtZW50LFxuICAgICAgICAgICAgc3RhcnRZLFxuICAgICAgICAgICAgbW91c2VPZmZzZXQsXG4gICAgICAgICAgICBjb2x1bW5FbGVtZW50XG4gICAgICAgIH07XG4gICAgICAgIHRoaXMuZXZlbnRCdXMuZW1pdChDb3JlRXZlbnRzLkVWRU5UX0RSQUdfU1RBUlQsIHBheWxvYWQpO1xuICAgICAgICAvLyBTdGFydCBhbmltYXRpb24gbG9vcFxuICAgICAgICB0aGlzLmFuaW1hdGVEcmFnKCk7XG4gICAgfVxuICAgIHVwZGF0ZURyYWdUYXJnZXQoZSkge1xuICAgICAgICBpZiAoIXRoaXMuZHJhZ1N0YXRlKVxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAvLyBDaGVjayBoZWFkZXIgem9uZSBmaXJzdFxuICAgICAgICB0aGlzLmNoZWNrSGVhZGVyWm9uZShlKTtcbiAgICAgICAgLy8gU2tpcCBub3JtYWwgZ3JpZCBoYW5kbGluZyBpZiBpbiBoZWFkZXJcbiAgICAgICAgaWYgKHRoaXMuaW5IZWFkZXIpXG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIC8vIENoZWNrIGZvciBjb2x1bW4gY2hhbmdlXG4gICAgICAgIGNvbnN0IGNvbHVtbkF0UG9pbnQgPSB0aGlzLmdldENvbHVtbkF0UG9pbnQoZS5jbGllbnRYKTtcbiAgICAgICAgLy8gRm9yIGhlYWRlciBpdGVtcyBlbnRlcmluZyBncmlkLCBzZXQgaW5pdGlhbCBjb2x1bW5cbiAgICAgICAgaWYgKHRoaXMuZHJhZ1N0YXRlLmRyYWdTb3VyY2UgPT09ICdoZWFkZXInICYmIGNvbHVtbkF0UG9pbnQgJiYgIXRoaXMuZHJhZ1N0YXRlLmN1cnJlbnRDb2x1bW4pIHtcbiAgICAgICAgICAgIHRoaXMuZHJhZ1N0YXRlLmN1cnJlbnRDb2x1bW4gPSBjb2x1bW5BdFBvaW50O1xuICAgICAgICAgICAgdGhpcy5kcmFnU3RhdGUuY29sdW1uRWxlbWVudCA9IGNvbHVtbkF0UG9pbnQ7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGNvbHVtbkF0UG9pbnQgJiYgY29sdW1uQXRQb2ludCAhPT0gdGhpcy5kcmFnU3RhdGUuY3VycmVudENvbHVtbiAmJiB0aGlzLmRyYWdTdGF0ZS5jdXJyZW50Q29sdW1uKSB7XG4gICAgICAgICAgICBjb25zdCBwYXlsb2FkID0ge1xuICAgICAgICAgICAgICAgIGV2ZW50SWQ6IHRoaXMuZHJhZ1N0YXRlLmV2ZW50SWQsXG4gICAgICAgICAgICAgICAgZWxlbWVudDogdGhpcy5kcmFnU3RhdGUuZWxlbWVudCxcbiAgICAgICAgICAgICAgICBwcmV2aW91c0NvbHVtbjogdGhpcy5kcmFnU3RhdGUuY3VycmVudENvbHVtbixcbiAgICAgICAgICAgICAgICBuZXdDb2x1bW46IGNvbHVtbkF0UG9pbnQsXG4gICAgICAgICAgICAgICAgY3VycmVudFk6IHRoaXMuZHJhZ1N0YXRlLmN1cnJlbnRZXG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgdGhpcy5ldmVudEJ1cy5lbWl0KENvcmVFdmVudHMuRVZFTlRfRFJBR19DT0xVTU5fQ0hBTkdFLCBwYXlsb2FkKTtcbiAgICAgICAgICAgIHRoaXMuZHJhZ1N0YXRlLmN1cnJlbnRDb2x1bW4gPSBjb2x1bW5BdFBvaW50O1xuICAgICAgICAgICAgdGhpcy5kcmFnU3RhdGUuY29sdW1uRWxlbWVudCA9IGNvbHVtbkF0UG9pbnQ7XG4gICAgICAgIH1cbiAgICAgICAgLy8gU2tpcCBncmlkIHBvc2l0aW9uIHVwZGF0ZXMgaWYgbm8gY29sdW1uIHlldFxuICAgICAgICBpZiAoIXRoaXMuZHJhZ1N0YXRlLmNvbHVtbkVsZW1lbnQpXG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIGNvbnN0IGNvbHVtblJlY3QgPSB0aGlzLmRyYWdTdGF0ZS5jb2x1bW5FbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgICAgICBjb25zdCB0YXJnZXRZID0gZS5jbGllbnRZIC0gY29sdW1uUmVjdC50b3AgLSB0aGlzLmRyYWdTdGF0ZS5tb3VzZU9mZnNldC55O1xuICAgICAgICB0aGlzLmRyYWdTdGF0ZS50YXJnZXRZID0gTWF0aC5tYXgoMCwgdGFyZ2V0WSk7XG4gICAgICAgIC8vIFN0YXJ0IGFuaW1hdGlvbiBpZiBub3QgcnVubmluZ1xuICAgICAgICBpZiAoIXRoaXMuZHJhZ1N0YXRlLmFuaW1hdGlvbklkKSB7XG4gICAgICAgICAgICB0aGlzLmFuaW1hdGVEcmFnKCk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgLyoqXG4gICAgICogQ2hlY2sgaWYgcG9pbnRlciBpcyBpbiBoZWFkZXIgem9uZSBhbmQgZW1pdCBhcHByb3ByaWF0ZSBldmVudHNcbiAgICAgKi9cbiAgICBjaGVja0hlYWRlclpvbmUoZSkge1xuICAgICAgICBpZiAoIXRoaXMuZHJhZ1N0YXRlKVxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICBjb25zdCBoZWFkZXJWaWV3cG9ydCA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJ3N3cC1oZWFkZXItdmlld3BvcnQnKTtcbiAgICAgICAgaWYgKCFoZWFkZXJWaWV3cG9ydClcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgY29uc3QgcmVjdCA9IGhlYWRlclZpZXdwb3J0LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgICAgICBjb25zdCBpc0luSGVhZGVyID0gZS5jbGllbnRZIDwgcmVjdC5ib3R0b207XG4gICAgICAgIGlmIChpc0luSGVhZGVyICYmICF0aGlzLmluSGVhZGVyKSB7XG4gICAgICAgICAgICAvLyBFbnRlcmVkIGhlYWRlciAoZnJvbSBncmlkKVxuICAgICAgICAgICAgdGhpcy5pbkhlYWRlciA9IHRydWU7XG4gICAgICAgICAgICBpZiAodGhpcy5kcmFnU3RhdGUuZHJhZ1NvdXJjZSA9PT0gJ2dyaWQnICYmIHRoaXMuZHJhZ1N0YXRlLmNvbHVtbkVsZW1lbnQpIHtcbiAgICAgICAgICAgICAgICBjb25zdCBwYXlsb2FkID0ge1xuICAgICAgICAgICAgICAgICAgICBldmVudElkOiB0aGlzLmRyYWdTdGF0ZS5ldmVudElkLFxuICAgICAgICAgICAgICAgICAgICBlbGVtZW50OiB0aGlzLmRyYWdTdGF0ZS5lbGVtZW50LFxuICAgICAgICAgICAgICAgICAgICBzb3VyY2VDb2x1bW5JbmRleDogdGhpcy5nZXRDb2x1bW5JbmRleCh0aGlzLmRyYWdTdGF0ZS5jb2x1bW5FbGVtZW50KSxcbiAgICAgICAgICAgICAgICAgICAgc291cmNlQ29sdW1uS2V5OiB0aGlzLmRyYWdTdGF0ZS5jb2x1bW5FbGVtZW50LmRhdGFzZXQuY29sdW1uS2V5IHx8ICcnLFxuICAgICAgICAgICAgICAgICAgICB0aXRsZTogdGhpcy5kcmFnU3RhdGUuZWxlbWVudC5xdWVyeVNlbGVjdG9yKCdzd3AtZXZlbnQtdGl0bGUnKT8udGV4dENvbnRlbnQgfHwgJycsXG4gICAgICAgICAgICAgICAgICAgIGNvbG9yQ2xhc3M6IFsuLi50aGlzLmRyYWdTdGF0ZS5lbGVtZW50LmNsYXNzTGlzdF0uZmluZChjID0+IGMuc3RhcnRzV2l0aCgnaXMtJykpLFxuICAgICAgICAgICAgICAgICAgICBpdGVtVHlwZTogJ2V2ZW50JyxcbiAgICAgICAgICAgICAgICAgICAgZHVyYXRpb246IDFcbiAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICAgIHRoaXMuZXZlbnRCdXMuZW1pdChDb3JlRXZlbnRzLkVWRU5UX0RSQUdfRU5URVJfSEVBREVSLCBwYXlsb2FkKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIEZvciBoZWFkZXIgc291cmNlIHJlLWVudGVyaW5nIGhlYWRlciwganVzdCB1cGRhdGUgaW5IZWFkZXIgZmxhZ1xuICAgICAgICB9XG4gICAgICAgIGVsc2UgaWYgKCFpc0luSGVhZGVyICYmIHRoaXMuaW5IZWFkZXIpIHtcbiAgICAgICAgICAgIC8vIExlZnQgaGVhZGVyIChlbnRlcmluZyBncmlkKVxuICAgICAgICAgICAgdGhpcy5pbkhlYWRlciA9IGZhbHNlO1xuICAgICAgICAgICAgY29uc3QgdGFyZ2V0Q29sdW1uID0gdGhpcy5nZXRDb2x1bW5BdFBvaW50KGUuY2xpZW50WCk7XG4gICAgICAgICAgICBpZiAodGhpcy5kcmFnU3RhdGUuZHJhZ1NvdXJjZSA9PT0gJ2hlYWRlcicpIHtcbiAgICAgICAgICAgICAgICAvLyBIZWFkZXIgaXRlbSBsZWF2aW5nIGhlYWRlciBcdTIxOTIgY3JlYXRlIHN3cC1ldmVudCBpbiBncmlkXG4gICAgICAgICAgICAgICAgY29uc3QgcGF5bG9hZCA9IHtcbiAgICAgICAgICAgICAgICAgICAgZXZlbnRJZDogdGhpcy5kcmFnU3RhdGUuZXZlbnRJZCxcbiAgICAgICAgICAgICAgICAgICAgc291cmNlOiAnaGVhZGVyJyxcbiAgICAgICAgICAgICAgICAgICAgZWxlbWVudDogdGhpcy5kcmFnU3RhdGUuZWxlbWVudCxcbiAgICAgICAgICAgICAgICAgICAgdGFyZ2V0Q29sdW1uOiB0YXJnZXRDb2x1bW4gfHwgdW5kZWZpbmVkLFxuICAgICAgICAgICAgICAgICAgICBzdGFydDogdGhpcy5kcmFnU3RhdGUuZWxlbWVudC5kYXRhc2V0LnN0YXJ0ID8gbmV3IERhdGUodGhpcy5kcmFnU3RhdGUuZWxlbWVudC5kYXRhc2V0LnN0YXJ0KSA6IHVuZGVmaW5lZCxcbiAgICAgICAgICAgICAgICAgICAgZW5kOiB0aGlzLmRyYWdTdGF0ZS5lbGVtZW50LmRhdGFzZXQuZW5kID8gbmV3IERhdGUodGhpcy5kcmFnU3RhdGUuZWxlbWVudC5kYXRhc2V0LmVuZCkgOiB1bmRlZmluZWQsXG4gICAgICAgICAgICAgICAgICAgIHRpdGxlOiB0aGlzLmRyYWdTdGF0ZS5lbGVtZW50LnRleHRDb250ZW50IHx8ICcnLFxuICAgICAgICAgICAgICAgICAgICBjb2xvckNsYXNzOiBbLi4udGhpcy5kcmFnU3RhdGUuZWxlbWVudC5jbGFzc0xpc3RdLmZpbmQoYyA9PiBjLnN0YXJ0c1dpdGgoJ2lzLScpKVxuICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgICAgdGhpcy5ldmVudEJ1cy5lbWl0KENvcmVFdmVudHMuRVZFTlRfRFJBR19MRUFWRV9IRUFERVIsIHBheWxvYWQpO1xuICAgICAgICAgICAgICAgIC8vIFJlLWF0dGFjaCB0byB0aGUgbmV3IHN3cC1ldmVudCBjcmVhdGVkIGJ5IEV2ZW50UmVuZGVyZXJcbiAgICAgICAgICAgICAgICBpZiAodGFyZ2V0Q29sdW1uKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IG5ld0VsZW1lbnQgPSB0YXJnZXRDb2x1bW4ucXVlcnlTZWxlY3Rvcihgc3dwLWV2ZW50W2RhdGEtZXZlbnQtaWQ9XCIke3RoaXMuZHJhZ1N0YXRlLmV2ZW50SWR9XCJdYCk7XG4gICAgICAgICAgICAgICAgICAgIGlmIChuZXdFbGVtZW50KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmRyYWdTdGF0ZS5lbGVtZW50ID0gbmV3RWxlbWVudDtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuZHJhZ1N0YXRlLmNvbHVtbkVsZW1lbnQgPSB0YXJnZXRDb2x1bW47XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmRyYWdTdGF0ZS5jdXJyZW50Q29sdW1uID0gdGFyZ2V0Q29sdW1uO1xuICAgICAgICAgICAgICAgICAgICAgICAgLy8gU3RhcnQgYW5pbWF0aW9uIGZvciB0aGUgbmV3IGVsZW1lbnRcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuYW5pbWF0ZURyYWcoKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgICAgIC8vIEdyaWQgZXZlbnQgbGVhdmluZyBoZWFkZXIgXHUyMTkyIHJlc3RvcmUgdG8gZ3JpZFxuICAgICAgICAgICAgICAgIGNvbnN0IHBheWxvYWQgPSB7XG4gICAgICAgICAgICAgICAgICAgIGV2ZW50SWQ6IHRoaXMuZHJhZ1N0YXRlLmV2ZW50SWQsXG4gICAgICAgICAgICAgICAgICAgIHNvdXJjZTogJ2dyaWQnXG4gICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICB0aGlzLmV2ZW50QnVzLmVtaXQoQ29yZUV2ZW50cy5FVkVOVF9EUkFHX0xFQVZFX0hFQURFUiwgcGF5bG9hZCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSBpZiAoaXNJbkhlYWRlcikge1xuICAgICAgICAgICAgLy8gTW92aW5nIHdpdGhpbiBoZWFkZXJcbiAgICAgICAgICAgIGNvbnN0IGNvbHVtbiA9IHRoaXMuZ2V0Q29sdW1uQXRYKGUuY2xpZW50WCk7XG4gICAgICAgICAgICBpZiAoY29sdW1uKSB7XG4gICAgICAgICAgICAgICAgY29uc3QgcGF5bG9hZCA9IHtcbiAgICAgICAgICAgICAgICAgICAgZXZlbnRJZDogdGhpcy5kcmFnU3RhdGUuZXZlbnRJZCxcbiAgICAgICAgICAgICAgICAgICAgY29sdW1uSW5kZXg6IHRoaXMuZ2V0Q29sdW1uSW5kZXgoY29sdW1uKSxcbiAgICAgICAgICAgICAgICAgICAgY29sdW1uS2V5OiBjb2x1bW4uZGF0YXNldC5jb2x1bW5LZXkgfHwgJydcbiAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICAgIHRoaXMuZXZlbnRCdXMuZW1pdChDb3JlRXZlbnRzLkVWRU5UX0RSQUdfTU9WRV9IRUFERVIsIHBheWxvYWQpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEdldCBjb2x1bW4gaW5kZXggKDAtYmFzZWQpIGZvciBhIGNvbHVtbiBlbGVtZW50XG4gICAgICovXG4gICAgZ2V0Q29sdW1uSW5kZXgoY29sdW1uKSB7XG4gICAgICAgIGlmICghdGhpcy5jb250YWluZXIgfHwgIWNvbHVtbilcbiAgICAgICAgICAgIHJldHVybiAwO1xuICAgICAgICBjb25zdCBjb2x1bW5zID0gQXJyYXkuZnJvbSh0aGlzLmNvbnRhaW5lci5xdWVyeVNlbGVjdG9yQWxsKCdzd3AtZGF5LWNvbHVtbicpKTtcbiAgICAgICAgcmV0dXJuIGNvbHVtbnMuaW5kZXhPZihjb2x1bW4pO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBHZXQgY29sdW1uIGF0IFggY29vcmRpbmF0ZSAoYWxpYXMgZm9yIGdldENvbHVtbkF0UG9pbnQpXG4gICAgICovXG4gICAgZ2V0Q29sdW1uQXRYKGNsaWVudFgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuZ2V0Q29sdW1uQXRQb2ludChjbGllbnRYKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogRmluZCBjb2x1bW4gZWxlbWVudCBhdCBnaXZlbiBYIGNvb3JkaW5hdGVcbiAgICAgKi9cbiAgICBnZXRDb2x1bW5BdFBvaW50KGNsaWVudFgpIHtcbiAgICAgICAgaWYgKCF0aGlzLmNvbnRhaW5lcilcbiAgICAgICAgICAgIHJldHVybiBudWxsO1xuICAgICAgICBjb25zdCBjb2x1bW5zID0gdGhpcy5jb250YWluZXIucXVlcnlTZWxlY3RvckFsbCgnc3dwLWRheS1jb2x1bW4nKTtcbiAgICAgICAgZm9yIChjb25zdCBjb2wgb2YgY29sdW1ucykge1xuICAgICAgICAgICAgY29uc3QgcmVjdCA9IGNvbC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcbiAgICAgICAgICAgIGlmIChjbGllbnRYID49IHJlY3QubGVmdCAmJiBjbGllbnRYIDw9IHJlY3QucmlnaHQpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gY29sO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBDYW5jZWwgZHJhZyBhbmQgYW5pbWF0ZSBiYWNrIHRvIHN0YXJ0IHBvc2l0aW9uXG4gICAgICovXG4gICAgY2FuY2VsRHJhZygpIHtcbiAgICAgICAgaWYgKCF0aGlzLmRyYWdTdGF0ZSlcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgLy8gU3RvcCBhbmltYXRpb25cbiAgICAgICAgY2FuY2VsQW5pbWF0aW9uRnJhbWUodGhpcy5kcmFnU3RhdGUuYW5pbWF0aW9uSWQpO1xuICAgICAgICBjb25zdCB7IGVsZW1lbnQsIGdob3N0RWxlbWVudCwgc3RhcnRZLCBldmVudElkIH0gPSB0aGlzLmRyYWdTdGF0ZTtcbiAgICAgICAgLy8gQW5pbWF0ZSBiYWNrIHRvIHN0YXJ0XG4gICAgICAgIGVsZW1lbnQuc3R5bGUudHJhbnNpdGlvbiA9ICd0b3AgMjAwbXMgZWFzZS1vdXQnO1xuICAgICAgICBlbGVtZW50LnN0eWxlLnRvcCA9IGAke3N0YXJ0WX1weGA7XG4gICAgICAgIC8vIFJlbW92ZSBnaG9zdCBhZnRlciBhbmltYXRpb24gKGlmIGV4aXN0cylcbiAgICAgICAgc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgICAgICBnaG9zdEVsZW1lbnQ/LnJlbW92ZSgpO1xuICAgICAgICAgICAgZWxlbWVudC5zdHlsZS50cmFuc2l0aW9uID0gJyc7XG4gICAgICAgICAgICBlbGVtZW50LmNsYXNzTGlzdC5yZW1vdmUoJ2RyYWdnaW5nJyk7XG4gICAgICAgIH0sIDIwMCk7XG4gICAgICAgIC8vIEVtaXQgZHJhZzpjYW5jZWxcbiAgICAgICAgY29uc3QgcGF5bG9hZCA9IHtcbiAgICAgICAgICAgIGV2ZW50SWQsXG4gICAgICAgICAgICBlbGVtZW50LFxuICAgICAgICAgICAgc3RhcnRZXG4gICAgICAgIH07XG4gICAgICAgIHRoaXMuZXZlbnRCdXMuZW1pdChDb3JlRXZlbnRzLkVWRU5UX0RSQUdfQ0FOQ0VMLCBwYXlsb2FkKTtcbiAgICAgICAgdGhpcy5kcmFnU3RhdGUgPSBudWxsO1xuICAgICAgICB0aGlzLmluSGVhZGVyID0gZmFsc2U7XG4gICAgfVxufVxuIiwgImltcG9ydCB7IENvcmVFdmVudHMgfSBmcm9tICcuLi9jb25zdGFudHMvQ29yZUV2ZW50cyc7XG5leHBvcnQgY2xhc3MgRWRnZVNjcm9sbE1hbmFnZXIge1xuICAgIGNvbnN0cnVjdG9yKGV2ZW50QnVzKSB7XG4gICAgICAgIHRoaXMuZXZlbnRCdXMgPSBldmVudEJ1cztcbiAgICAgICAgdGhpcy5zY3JvbGxhYmxlQ29udGVudCA9IG51bGw7XG4gICAgICAgIHRoaXMudGltZUdyaWQgPSBudWxsO1xuICAgICAgICB0aGlzLmRyYWdnZWRFbGVtZW50ID0gbnVsbDtcbiAgICAgICAgdGhpcy5zY3JvbGxSQUYgPSBudWxsO1xuICAgICAgICB0aGlzLm1vdXNlWSA9IDA7XG4gICAgICAgIHRoaXMuaXNEcmFnZ2luZyA9IGZhbHNlO1xuICAgICAgICB0aGlzLmlzU2Nyb2xsaW5nID0gZmFsc2U7XG4gICAgICAgIHRoaXMubGFzdFRzID0gMDtcbiAgICAgICAgdGhpcy5yZWN0ID0gbnVsbDtcbiAgICAgICAgdGhpcy5pbml0aWFsU2Nyb2xsVG9wID0gMDtcbiAgICAgICAgdGhpcy5PVVRFUl9aT05FID0gMTAwO1xuICAgICAgICB0aGlzLklOTkVSX1pPTkUgPSA1MDtcbiAgICAgICAgdGhpcy5TTE9XX1NQRUVEID0gMTQwO1xuICAgICAgICB0aGlzLkZBU1RfU1BFRUQgPSA2NDA7XG4gICAgICAgIHRoaXMudHJhY2tNb3VzZSA9IChlKSA9PiB7XG4gICAgICAgICAgICBpZiAodGhpcy5pc0RyYWdnaW5nKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5tb3VzZVkgPSBlLmNsaWVudFk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH07XG4gICAgICAgIHRoaXMuc2Nyb2xsVGljayA9ICh0cykgPT4ge1xuICAgICAgICAgICAgaWYgKCF0aGlzLmlzRHJhZ2dpbmcgfHwgIXRoaXMuc2Nyb2xsYWJsZUNvbnRlbnQpXG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgY29uc3QgZHQgPSB0aGlzLmxhc3RUcyA/ICh0cyAtIHRoaXMubGFzdFRzKSAvIDEwMDAgOiAwO1xuICAgICAgICAgICAgdGhpcy5sYXN0VHMgPSB0cztcbiAgICAgICAgICAgIHRoaXMucmVjdCA/PyAodGhpcy5yZWN0ID0gdGhpcy5zY3JvbGxhYmxlQ29udGVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKSk7XG4gICAgICAgICAgICBjb25zdCB2ZWxvY2l0eSA9IHRoaXMuY2FsY3VsYXRlVmVsb2NpdHkoKTtcbiAgICAgICAgICAgIGlmICh2ZWxvY2l0eSAhPT0gMCAmJiAhdGhpcy5pc0F0Qm91bmRhcnkodmVsb2NpdHkpKSB7XG4gICAgICAgICAgICAgICAgY29uc3Qgc2Nyb2xsRGVsdGEgPSB2ZWxvY2l0eSAqIGR0O1xuICAgICAgICAgICAgICAgIHRoaXMuc2Nyb2xsYWJsZUNvbnRlbnQuc2Nyb2xsVG9wICs9IHNjcm9sbERlbHRhO1xuICAgICAgICAgICAgICAgIHRoaXMucmVjdCA9IG51bGw7XG4gICAgICAgICAgICAgICAgdGhpcy5ldmVudEJ1cy5lbWl0KENvcmVFdmVudHMuRURHRV9TQ1JPTExfVElDSywgeyBzY3JvbGxEZWx0YSB9KTtcbiAgICAgICAgICAgICAgICB0aGlzLnNldFNjcm9sbGluZ1N0YXRlKHRydWUpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgdGhpcy5zZXRTY3JvbGxpbmdTdGF0ZShmYWxzZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB0aGlzLnNjcm9sbFJBRiA9IHJlcXVlc3RBbmltYXRpb25GcmFtZSh0aGlzLnNjcm9sbFRpY2spO1xuICAgICAgICB9O1xuICAgICAgICB0aGlzLnN1YnNjcmliZVRvRXZlbnRzKCk7XG4gICAgICAgIGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ3BvaW50ZXJtb3ZlJywgdGhpcy50cmFja01vdXNlKTtcbiAgICB9XG4gICAgaW5pdChzY3JvbGxhYmxlQ29udGVudCkge1xuICAgICAgICB0aGlzLnNjcm9sbGFibGVDb250ZW50ID0gc2Nyb2xsYWJsZUNvbnRlbnQ7XG4gICAgICAgIHRoaXMudGltZUdyaWQgPSBzY3JvbGxhYmxlQ29udGVudC5xdWVyeVNlbGVjdG9yKCdzd3AtdGltZS1ncmlkJyk7XG4gICAgICAgIHRoaXMuc2Nyb2xsYWJsZUNvbnRlbnQuc3R5bGUuc2Nyb2xsQmVoYXZpb3IgPSAnYXV0byc7XG4gICAgfVxuICAgIHN1YnNjcmliZVRvRXZlbnRzKCkge1xuICAgICAgICB0aGlzLmV2ZW50QnVzLm9uKENvcmVFdmVudHMuRVZFTlRfRFJBR19TVEFSVCwgKGV2ZW50KSA9PiB7XG4gICAgICAgICAgICBjb25zdCBwYXlsb2FkID0gZXZlbnQuZGV0YWlsO1xuICAgICAgICAgICAgdGhpcy5kcmFnZ2VkRWxlbWVudCA9IHBheWxvYWQuZWxlbWVudDtcbiAgICAgICAgICAgIHRoaXMuc3RhcnREcmFnKCk7XG4gICAgICAgIH0pO1xuICAgICAgICB0aGlzLmV2ZW50QnVzLm9uKENvcmVFdmVudHMuRVZFTlRfRFJBR19FTkQsICgpID0+IHRoaXMuc3RvcERyYWcoKSk7XG4gICAgICAgIHRoaXMuZXZlbnRCdXMub24oQ29yZUV2ZW50cy5FVkVOVF9EUkFHX0NBTkNFTCwgKCkgPT4gdGhpcy5zdG9wRHJhZygpKTtcbiAgICB9XG4gICAgc3RhcnREcmFnKCkge1xuICAgICAgICB0aGlzLmlzRHJhZ2dpbmcgPSB0cnVlO1xuICAgICAgICB0aGlzLmlzU2Nyb2xsaW5nID0gZmFsc2U7XG4gICAgICAgIHRoaXMubGFzdFRzID0gMDtcbiAgICAgICAgdGhpcy5pbml0aWFsU2Nyb2xsVG9wID0gdGhpcy5zY3JvbGxhYmxlQ29udGVudD8uc2Nyb2xsVG9wID8/IDA7XG4gICAgICAgIGlmICh0aGlzLnNjcm9sbFJBRiA9PT0gbnVsbCkge1xuICAgICAgICAgICAgdGhpcy5zY3JvbGxSQUYgPSByZXF1ZXN0QW5pbWF0aW9uRnJhbWUodGhpcy5zY3JvbGxUaWNrKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICBzdG9wRHJhZygpIHtcbiAgICAgICAgdGhpcy5pc0RyYWdnaW5nID0gZmFsc2U7XG4gICAgICAgIHRoaXMuc2V0U2Nyb2xsaW5nU3RhdGUoZmFsc2UpO1xuICAgICAgICBpZiAodGhpcy5zY3JvbGxSQUYgIT09IG51bGwpIHtcbiAgICAgICAgICAgIGNhbmNlbEFuaW1hdGlvbkZyYW1lKHRoaXMuc2Nyb2xsUkFGKTtcbiAgICAgICAgICAgIHRoaXMuc2Nyb2xsUkFGID0gbnVsbDtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLnJlY3QgPSBudWxsO1xuICAgICAgICB0aGlzLmxhc3RUcyA9IDA7XG4gICAgICAgIHRoaXMuaW5pdGlhbFNjcm9sbFRvcCA9IDA7XG4gICAgfVxuICAgIGNhbGN1bGF0ZVZlbG9jaXR5KCkge1xuICAgICAgICBpZiAoIXRoaXMucmVjdClcbiAgICAgICAgICAgIHJldHVybiAwO1xuICAgICAgICBjb25zdCBkaXN0VG9wID0gdGhpcy5tb3VzZVkgLSB0aGlzLnJlY3QudG9wO1xuICAgICAgICBjb25zdCBkaXN0Qm90ID0gdGhpcy5yZWN0LmJvdHRvbSAtIHRoaXMubW91c2VZO1xuICAgICAgICBpZiAoZGlzdFRvcCA8IHRoaXMuSU5ORVJfWk9ORSlcbiAgICAgICAgICAgIHJldHVybiAtdGhpcy5GQVNUX1NQRUVEO1xuICAgICAgICBpZiAoZGlzdFRvcCA8IHRoaXMuT1VURVJfWk9ORSlcbiAgICAgICAgICAgIHJldHVybiAtdGhpcy5TTE9XX1NQRUVEO1xuICAgICAgICBpZiAoZGlzdEJvdCA8IHRoaXMuSU5ORVJfWk9ORSlcbiAgICAgICAgICAgIHJldHVybiB0aGlzLkZBU1RfU1BFRUQ7XG4gICAgICAgIGlmIChkaXN0Qm90IDwgdGhpcy5PVVRFUl9aT05FKVxuICAgICAgICAgICAgcmV0dXJuIHRoaXMuU0xPV19TUEVFRDtcbiAgICAgICAgcmV0dXJuIDA7XG4gICAgfVxuICAgIGlzQXRCb3VuZGFyeSh2ZWxvY2l0eSkge1xuICAgICAgICBpZiAoIXRoaXMuc2Nyb2xsYWJsZUNvbnRlbnQgfHwgIXRoaXMudGltZUdyaWQgfHwgIXRoaXMuZHJhZ2dlZEVsZW1lbnQpXG4gICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgIGNvbnN0IGF0VG9wID0gdGhpcy5zY3JvbGxhYmxlQ29udGVudC5zY3JvbGxUb3AgPD0gMCAmJiB2ZWxvY2l0eSA8IDA7XG4gICAgICAgIGNvbnN0IGF0Qm90dG9tID0gdmVsb2NpdHkgPiAwICYmXG4gICAgICAgICAgICB0aGlzLmRyYWdnZWRFbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpLmJvdHRvbSA+PVxuICAgICAgICAgICAgICAgIHRoaXMudGltZUdyaWQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCkuYm90dG9tO1xuICAgICAgICByZXR1cm4gYXRUb3AgfHwgYXRCb3R0b207XG4gICAgfVxuICAgIHNldFNjcm9sbGluZ1N0YXRlKHNjcm9sbGluZykge1xuICAgICAgICBpZiAodGhpcy5pc1Njcm9sbGluZyA9PT0gc2Nyb2xsaW5nKVxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB0aGlzLmlzU2Nyb2xsaW5nID0gc2Nyb2xsaW5nO1xuICAgICAgICBpZiAoc2Nyb2xsaW5nKSB7XG4gICAgICAgICAgICB0aGlzLmV2ZW50QnVzLmVtaXQoQ29yZUV2ZW50cy5FREdFX1NDUk9MTF9TVEFSVEVELCB7fSk7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICB0aGlzLmluaXRpYWxTY3JvbGxUb3AgPSB0aGlzLnNjcm9sbGFibGVDb250ZW50Py5zY3JvbGxUb3AgPz8gMDtcbiAgICAgICAgICAgIHRoaXMuZXZlbnRCdXMuZW1pdChDb3JlRXZlbnRzLkVER0VfU0NST0xMX1NUT1BQRUQsIHt9KTtcbiAgICAgICAgfVxuICAgIH1cbn1cbiIsICJpbXBvcnQgeyBwaXhlbHNUb01pbnV0ZXMsIG1pbnV0ZXNUb1BpeGVscywgc25hcFRvR3JpZCB9IGZyb20gJy4uL3V0aWxzL1Bvc2l0aW9uVXRpbHMnO1xuaW1wb3J0IHsgQ29yZUV2ZW50cyB9IGZyb20gJy4uL2NvbnN0YW50cy9Db3JlRXZlbnRzJztcbmltcG9ydCB7IFN3cEV2ZW50IH0gZnJvbSAnLi4vdHlwZXMvU3dwRXZlbnQnO1xuZXhwb3J0IGNsYXNzIFJlc2l6ZU1hbmFnZXIge1xuICAgIGNvbnN0cnVjdG9yKGV2ZW50QnVzLCBncmlkQ29uZmlnLCBkYXRlU2VydmljZSkge1xuICAgICAgICB0aGlzLmV2ZW50QnVzID0gZXZlbnRCdXM7XG4gICAgICAgIHRoaXMuZ3JpZENvbmZpZyA9IGdyaWRDb25maWc7XG4gICAgICAgIHRoaXMuZGF0ZVNlcnZpY2UgPSBkYXRlU2VydmljZTtcbiAgICAgICAgdGhpcy5jb250YWluZXIgPSBudWxsO1xuICAgICAgICB0aGlzLnJlc2l6ZVN0YXRlID0gbnVsbDtcbiAgICAgICAgdGhpcy5aX0lOREVYX1JFU0laSU5HID0gJzEwMDAnO1xuICAgICAgICB0aGlzLkFOSU1BVElPTl9TUEVFRCA9IDAuMzU7XG4gICAgICAgIHRoaXMuTUlOX0hFSUdIVF9NSU5VVEVTID0gMTU7XG4gICAgICAgIC8qKlxuICAgICAgICAgKiBIYW5kbGUgbW91c2VvdmVyIC0gY3JlYXRlIHJlc2l6ZSBoYW5kbGUgaWYgbm90IGV4aXN0c1xuICAgICAgICAgKi9cbiAgICAgICAgdGhpcy5oYW5kbGVNb3VzZU92ZXIgPSAoZSkgPT4ge1xuICAgICAgICAgICAgY29uc3QgdGFyZ2V0ID0gZS50YXJnZXQ7XG4gICAgICAgICAgICBjb25zdCBldmVudEVsZW1lbnQgPSB0YXJnZXQuY2xvc2VzdCgnc3dwLWV2ZW50Jyk7XG4gICAgICAgICAgICBpZiAoIWV2ZW50RWxlbWVudCB8fCB0aGlzLnJlc2l6ZVN0YXRlKVxuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIC8vIENoZWNrIGlmIGhhbmRsZSBhbHJlYWR5IGV4aXN0c1xuICAgICAgICAgICAgaWYgKCFldmVudEVsZW1lbnQucXVlcnlTZWxlY3RvcignOnNjb3BlID4gc3dwLXJlc2l6ZS1oYW5kbGUnKSkge1xuICAgICAgICAgICAgICAgIGNvbnN0IGhhbmRsZSA9IHRoaXMuY3JlYXRlUmVzaXplSGFuZGxlKCk7XG4gICAgICAgICAgICAgICAgZXZlbnRFbGVtZW50LmFwcGVuZENoaWxkKGhhbmRsZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH07XG4gICAgICAgIC8qKlxuICAgICAgICAgKiBIYW5kbGUgcG9pbnRlcmRvd24gLSBzdGFydCByZXNpemUgaWYgb24gaGFuZGxlXG4gICAgICAgICAqL1xuICAgICAgICB0aGlzLmhhbmRsZVBvaW50ZXJEb3duID0gKGUpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IGhhbmRsZSA9IGUudGFyZ2V0LmNsb3Nlc3QoJ3N3cC1yZXNpemUtaGFuZGxlJyk7XG4gICAgICAgICAgICBpZiAoIWhhbmRsZSlcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICBjb25zdCBlbGVtZW50ID0gaGFuZGxlLnBhcmVudEVsZW1lbnQ7XG4gICAgICAgICAgICBpZiAoIWVsZW1lbnQpXG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgY29uc3QgZXZlbnRJZCA9IGVsZW1lbnQuZGF0YXNldC5ldmVudElkIHx8ICcnO1xuICAgICAgICAgICAgY29uc3Qgc3RhcnRIZWlnaHQgPSBlbGVtZW50Lm9mZnNldEhlaWdodDtcbiAgICAgICAgICAgIGNvbnN0IHN0YXJ0RHVyYXRpb25NaW51dGVzID0gcGl4ZWxzVG9NaW51dGVzKHN0YXJ0SGVpZ2h0LCB0aGlzLmdyaWRDb25maWcpO1xuICAgICAgICAgICAgLy8gU3RvcmUgcHJldmlvdXMgei1pbmRleFxuICAgICAgICAgICAgY29uc3QgY29udGFpbmVyID0gZWxlbWVudC5jbG9zZXN0KCdzd3AtZXZlbnQtZ3JvdXAnKSA/PyBlbGVtZW50O1xuICAgICAgICAgICAgY29uc3QgcHJldlpJbmRleCA9IGNvbnRhaW5lci5zdHlsZS56SW5kZXg7XG4gICAgICAgICAgICAvLyBTZXQgcmVzaXplIHN0YXRlXG4gICAgICAgICAgICB0aGlzLnJlc2l6ZVN0YXRlID0ge1xuICAgICAgICAgICAgICAgIGV2ZW50SWQsXG4gICAgICAgICAgICAgICAgZWxlbWVudCxcbiAgICAgICAgICAgICAgICBoYW5kbGVFbGVtZW50OiBoYW5kbGUsXG4gICAgICAgICAgICAgICAgc3RhcnRZOiBlLmNsaWVudFksXG4gICAgICAgICAgICAgICAgc3RhcnRIZWlnaHQsXG4gICAgICAgICAgICAgICAgc3RhcnREdXJhdGlvbk1pbnV0ZXMsXG4gICAgICAgICAgICAgICAgcG9pbnRlcklkOiBlLnBvaW50ZXJJZCxcbiAgICAgICAgICAgICAgICBwcmV2WkluZGV4LFxuICAgICAgICAgICAgICAgIC8vIEFuaW1hdGlvbiBzdGF0ZVxuICAgICAgICAgICAgICAgIGN1cnJlbnRIZWlnaHQ6IHN0YXJ0SGVpZ2h0LFxuICAgICAgICAgICAgICAgIHRhcmdldEhlaWdodDogc3RhcnRIZWlnaHQsXG4gICAgICAgICAgICAgICAgYW5pbWF0aW9uSWQ6IG51bGxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICAvLyBFbGV2YXRlIHotaW5kZXhcbiAgICAgICAgICAgIGNvbnRhaW5lci5zdHlsZS56SW5kZXggPSB0aGlzLlpfSU5ERVhfUkVTSVpJTkc7XG4gICAgICAgICAgICAvLyBDYXB0dXJlIHBvaW50ZXIgZm9yIHNtb290aCB0cmFja2luZ1xuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICBoYW5kbGUuc2V0UG9pbnRlckNhcHR1cmUoZS5wb2ludGVySWQpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgY2F0Y2ggKGVycikge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUud2FybignUG9pbnRlciBjYXB0dXJlIGZhaWxlZDonLCBlcnIpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgLy8gQWRkIGdsb2JhbCByZXNpemluZyBjbGFzc1xuICAgICAgICAgICAgZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50LmNsYXNzTGlzdC5hZGQoJ3N3cC0tcmVzaXppbmcnKTtcbiAgICAgICAgICAgIC8vIEVtaXQgcmVzaXplIHN0YXJ0IGV2ZW50XG4gICAgICAgICAgICB0aGlzLmV2ZW50QnVzLmVtaXQoQ29yZUV2ZW50cy5FVkVOVF9SRVNJWkVfU1RBUlQsIHtcbiAgICAgICAgICAgICAgICBldmVudElkLFxuICAgICAgICAgICAgICAgIGVsZW1lbnQsXG4gICAgICAgICAgICAgICAgc3RhcnRIZWlnaHRcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICB9O1xuICAgICAgICAvKipcbiAgICAgICAgICogSGFuZGxlIHBvaW50ZXJtb3ZlIC0gdXBkYXRlIHRhcmdldCBoZWlnaHQgZHVyaW5nIHJlc2l6ZVxuICAgICAgICAgKi9cbiAgICAgICAgdGhpcy5oYW5kbGVQb2ludGVyTW92ZSA9IChlKSA9PiB7XG4gICAgICAgICAgICBpZiAoIXRoaXMucmVzaXplU3RhdGUpXG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgY29uc3QgZGVsdGFZID0gZS5jbGllbnRZIC0gdGhpcy5yZXNpemVTdGF0ZS5zdGFydFk7XG4gICAgICAgICAgICBjb25zdCBtaW5IZWlnaHQgPSAodGhpcy5NSU5fSEVJR0hUX01JTlVURVMgLyA2MCkgKiB0aGlzLmdyaWRDb25maWcuaG91ckhlaWdodDtcbiAgICAgICAgICAgIGNvbnN0IG5ld0hlaWdodCA9IE1hdGgubWF4KG1pbkhlaWdodCwgdGhpcy5yZXNpemVTdGF0ZS5zdGFydEhlaWdodCArIGRlbHRhWSk7XG4gICAgICAgICAgICAvLyBTZXQgdGFyZ2V0IGhlaWdodCBmb3IgYW5pbWF0aW9uXG4gICAgICAgICAgICB0aGlzLnJlc2l6ZVN0YXRlLnRhcmdldEhlaWdodCA9IG5ld0hlaWdodDtcbiAgICAgICAgICAgIC8vIFN0YXJ0IGFuaW1hdGlvbiBpZiBub3QgcnVubmluZ1xuICAgICAgICAgICAgaWYgKHRoaXMucmVzaXplU3RhdGUuYW5pbWF0aW9uSWQgPT09IG51bGwpIHtcbiAgICAgICAgICAgICAgICB0aGlzLmFuaW1hdGVIZWlnaHQoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfTtcbiAgICAgICAgLyoqXG4gICAgICAgICAqIFJBRiBhbmltYXRpb24gbG9vcCBmb3Igc21vb3RoIGhlaWdodCBpbnRlcnBvbGF0aW9uXG4gICAgICAgICAqL1xuICAgICAgICB0aGlzLmFuaW1hdGVIZWlnaHQgPSAoKSA9PiB7XG4gICAgICAgICAgICBpZiAoIXRoaXMucmVzaXplU3RhdGUpXG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgY29uc3QgZGlmZiA9IHRoaXMucmVzaXplU3RhdGUudGFyZ2V0SGVpZ2h0IC0gdGhpcy5yZXNpemVTdGF0ZS5jdXJyZW50SGVpZ2h0O1xuICAgICAgICAgICAgLy8gU3RvcCBhbmltYXRpb24gd2hlbiBjbG9zZSBlbm91Z2hcbiAgICAgICAgICAgIGlmIChNYXRoLmFicyhkaWZmKSA8IDAuNSkge1xuICAgICAgICAgICAgICAgIHRoaXMucmVzaXplU3RhdGUuYW5pbWF0aW9uSWQgPSBudWxsO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIEludGVycG9sYXRlIHRvd2FyZHMgdGFyZ2V0ICgzNSUgcGVyIGZyYW1lIGxpa2UgVjEpXG4gICAgICAgICAgICB0aGlzLnJlc2l6ZVN0YXRlLmN1cnJlbnRIZWlnaHQgKz0gZGlmZiAqIHRoaXMuQU5JTUFUSU9OX1NQRUVEO1xuICAgICAgICAgICAgdGhpcy5yZXNpemVTdGF0ZS5lbGVtZW50LnN0eWxlLmhlaWdodCA9IGAke3RoaXMucmVzaXplU3RhdGUuY3VycmVudEhlaWdodH1weGA7XG4gICAgICAgICAgICAvLyBVcGRhdGUgdGltZXN0YW1wIGRpc3BsYXkgKHNuYXBwZWQpXG4gICAgICAgICAgICB0aGlzLnVwZGF0ZVRpbWVzdGFtcERpc3BsYXkoKTtcbiAgICAgICAgICAgIC8vIENvbnRpbnVlIGFuaW1hdGlvblxuICAgICAgICAgICAgdGhpcy5yZXNpemVTdGF0ZS5hbmltYXRpb25JZCA9IHJlcXVlc3RBbmltYXRpb25GcmFtZSh0aGlzLmFuaW1hdGVIZWlnaHQpO1xuICAgICAgICB9O1xuICAgICAgICAvKipcbiAgICAgICAgICogSGFuZGxlIHBvaW50ZXJ1cCAtIGZpbmlzaCByZXNpemVcbiAgICAgICAgICovXG4gICAgICAgIHRoaXMuaGFuZGxlUG9pbnRlclVwID0gKGUpID0+IHtcbiAgICAgICAgICAgIGlmICghdGhpcy5yZXNpemVTdGF0ZSlcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICAvLyBDYW5jZWwgYW55IHBlbmRpbmcgYW5pbWF0aW9uXG4gICAgICAgICAgICBpZiAodGhpcy5yZXNpemVTdGF0ZS5hbmltYXRpb25JZCAhPT0gbnVsbCkge1xuICAgICAgICAgICAgICAgIGNhbmNlbEFuaW1hdGlvbkZyYW1lKHRoaXMucmVzaXplU3RhdGUuYW5pbWF0aW9uSWQpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgLy8gUmVsZWFzZSBwb2ludGVyIGNhcHR1cmVcbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgdGhpcy5yZXNpemVTdGF0ZS5oYW5kbGVFbGVtZW50LnJlbGVhc2VQb2ludGVyQ2FwdHVyZShlLnBvaW50ZXJJZCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBjYXRjaCAoZXJyKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS53YXJuKCdQb2ludGVyIHJlbGVhc2UgZmFpbGVkOicsIGVycik7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyBTbmFwIGZpbmFsIGhlaWdodCB0byBncmlkXG4gICAgICAgICAgICB0aGlzLnNuYXBUb0dyaWRGaW5hbCgpO1xuICAgICAgICAgICAgLy8gVXBkYXRlIHRpbWVzdGFtcCBvbmUgZmluYWwgdGltZVxuICAgICAgICAgICAgdGhpcy51cGRhdGVUaW1lc3RhbXBEaXNwbGF5KCk7XG4gICAgICAgICAgICAvLyBSZXN0b3JlIHotaW5kZXhcbiAgICAgICAgICAgIGNvbnN0IGNvbnRhaW5lciA9IHRoaXMucmVzaXplU3RhdGUuZWxlbWVudC5jbG9zZXN0KCdzd3AtZXZlbnQtZ3JvdXAnKSA/PyB0aGlzLnJlc2l6ZVN0YXRlLmVsZW1lbnQ7XG4gICAgICAgICAgICBjb250YWluZXIuc3R5bGUuekluZGV4ID0gdGhpcy5yZXNpemVTdGF0ZS5wcmV2WkluZGV4O1xuICAgICAgICAgICAgLy8gUmVtb3ZlIGdsb2JhbCByZXNpemluZyBjbGFzc1xuICAgICAgICAgICAgZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50LmNsYXNzTGlzdC5yZW1vdmUoJ3N3cC0tcmVzaXppbmcnKTtcbiAgICAgICAgICAgIC8vIEdldCBjb2x1bW5LZXkgYW5kIGRhdGUgZnJvbSBwYXJlbnQgY29sdW1uXG4gICAgICAgICAgICBjb25zdCBjb2x1bW4gPSB0aGlzLnJlc2l6ZVN0YXRlLmVsZW1lbnQuY2xvc2VzdCgnc3dwLWRheS1jb2x1bW4nKTtcbiAgICAgICAgICAgIGNvbnN0IGNvbHVtbktleSA9IGNvbHVtbj8uZGF0YXNldC5jb2x1bW5LZXkgfHwgJyc7XG4gICAgICAgICAgICBjb25zdCBkYXRlID0gY29sdW1uPy5kYXRhc2V0LmRhdGUgfHwgJyc7XG4gICAgICAgICAgICAvLyBDcmVhdGUgU3dwRXZlbnQgZnJvbSBlbGVtZW50IChyZWFkcyB0b3AvaGVpZ2h0L2V2ZW50SWQgZnJvbSBlbGVtZW50KVxuICAgICAgICAgICAgY29uc3Qgc3dwRXZlbnQgPSBTd3BFdmVudC5mcm9tRWxlbWVudCh0aGlzLnJlc2l6ZVN0YXRlLmVsZW1lbnQsIGNvbHVtbktleSwgZGF0ZSwgdGhpcy5ncmlkQ29uZmlnKTtcbiAgICAgICAgICAgIC8vIEVtaXQgcmVzaXplIGVuZCBldmVudFxuICAgICAgICAgICAgdGhpcy5ldmVudEJ1cy5lbWl0KENvcmVFdmVudHMuRVZFTlRfUkVTSVpFX0VORCwge1xuICAgICAgICAgICAgICAgIHN3cEV2ZW50XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIC8vIFJlc2V0IHN0YXRlXG4gICAgICAgICAgICB0aGlzLnJlc2l6ZVN0YXRlID0gbnVsbDtcbiAgICAgICAgfTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogSW5pdGlhbGl6ZSByZXNpemUgZnVuY3Rpb25hbGl0eSBvbiBjb250YWluZXJcbiAgICAgKi9cbiAgICBpbml0KGNvbnRhaW5lcikge1xuICAgICAgICB0aGlzLmNvbnRhaW5lciA9IGNvbnRhaW5lcjtcbiAgICAgICAgLy8gTW91c2VvdmVyIGxpc3RlbmVyIGZvciBoYW5kbGUgY3JlYXRpb24gKGNhcHR1cmUgcGhhc2UgbGlrZSBWMSlcbiAgICAgICAgY29udGFpbmVyLmFkZEV2ZW50TGlzdGVuZXIoJ21vdXNlb3ZlcicsIHRoaXMuaGFuZGxlTW91c2VPdmVyLCB0cnVlKTtcbiAgICAgICAgLy8gUG9pbnRlciBsaXN0ZW5lcnMgZm9yIHJlc2l6ZSAoY2FwdHVyZSBwaGFzZSBsaWtlIFYxKVxuICAgICAgICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdwb2ludGVyZG93bicsIHRoaXMuaGFuZGxlUG9pbnRlckRvd24sIHRydWUpO1xuICAgICAgICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdwb2ludGVybW92ZScsIHRoaXMuaGFuZGxlUG9pbnRlck1vdmUsIHRydWUpO1xuICAgICAgICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdwb2ludGVydXAnLCB0aGlzLmhhbmRsZVBvaW50ZXJVcCwgdHJ1ZSk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIENyZWF0ZSByZXNpemUgaGFuZGxlIGVsZW1lbnRcbiAgICAgKi9cbiAgICBjcmVhdGVSZXNpemVIYW5kbGUoKSB7XG4gICAgICAgIGNvbnN0IGhhbmRsZSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3N3cC1yZXNpemUtaGFuZGxlJyk7XG4gICAgICAgIGhhbmRsZS5zZXRBdHRyaWJ1dGUoJ2FyaWEtbGFiZWwnLCAnUmVzaXplIGV2ZW50Jyk7XG4gICAgICAgIGhhbmRsZS5zZXRBdHRyaWJ1dGUoJ3JvbGUnLCAnc2VwYXJhdG9yJyk7XG4gICAgICAgIHJldHVybiBoYW5kbGU7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFVwZGF0ZSB0aW1lc3RhbXAgZGlzcGxheSB3aXRoIHNuYXBwZWQgZW5kIHRpbWVcbiAgICAgKi9cbiAgICB1cGRhdGVUaW1lc3RhbXBEaXNwbGF5KCkge1xuICAgICAgICBpZiAoIXRoaXMucmVzaXplU3RhdGUpXG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIGNvbnN0IHRpbWVFbCA9IHRoaXMucmVzaXplU3RhdGUuZWxlbWVudC5xdWVyeVNlbGVjdG9yKCdzd3AtZXZlbnQtdGltZScpO1xuICAgICAgICBpZiAoIXRpbWVFbClcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgLy8gR2V0IHN0YXJ0IHRpbWUgZnJvbSBlbGVtZW50IHBvc2l0aW9uXG4gICAgICAgIGNvbnN0IHRvcCA9IHBhcnNlRmxvYXQodGhpcy5yZXNpemVTdGF0ZS5lbGVtZW50LnN0eWxlLnRvcCkgfHwgMDtcbiAgICAgICAgY29uc3Qgc3RhcnRNaW51dGVzRnJvbUdyaWQgPSBwaXhlbHNUb01pbnV0ZXModG9wLCB0aGlzLmdyaWRDb25maWcpO1xuICAgICAgICBjb25zdCBzdGFydE1pbnV0ZXMgPSAodGhpcy5ncmlkQ29uZmlnLmRheVN0YXJ0SG91ciAqIDYwKSArIHN0YXJ0TWludXRlc0Zyb21HcmlkO1xuICAgICAgICAvLyBDYWxjdWxhdGUgc25hcHBlZCBlbmQgdGltZSBmcm9tIGN1cnJlbnQgaGVpZ2h0XG4gICAgICAgIGNvbnN0IHNuYXBwZWRIZWlnaHQgPSBzbmFwVG9HcmlkKHRoaXMucmVzaXplU3RhdGUuY3VycmVudEhlaWdodCwgdGhpcy5ncmlkQ29uZmlnKTtcbiAgICAgICAgY29uc3QgZHVyYXRpb25NaW51dGVzID0gcGl4ZWxzVG9NaW51dGVzKHNuYXBwZWRIZWlnaHQsIHRoaXMuZ3JpZENvbmZpZyk7XG4gICAgICAgIGNvbnN0IGVuZE1pbnV0ZXMgPSBzdGFydE1pbnV0ZXMgKyBkdXJhdGlvbk1pbnV0ZXM7XG4gICAgICAgIC8vIEZvcm1hdCBhbmQgdXBkYXRlXG4gICAgICAgIGNvbnN0IHN0YXJ0ID0gdGhpcy5taW51dGVzVG9EYXRlKHN0YXJ0TWludXRlcyk7XG4gICAgICAgIGNvbnN0IGVuZCA9IHRoaXMubWludXRlc1RvRGF0ZShlbmRNaW51dGVzKTtcbiAgICAgICAgdGltZUVsLnRleHRDb250ZW50ID0gdGhpcy5kYXRlU2VydmljZS5mb3JtYXRUaW1lUmFuZ2Uoc3RhcnQsIGVuZCk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIENvbnZlcnQgbWludXRlcyBzaW5jZSBtaWRuaWdodCB0byBEYXRlXG4gICAgICovXG4gICAgbWludXRlc1RvRGF0ZShtaW51dGVzKSB7XG4gICAgICAgIGNvbnN0IGRhdGUgPSBuZXcgRGF0ZSgpO1xuICAgICAgICBkYXRlLnNldEhvdXJzKE1hdGguZmxvb3IobWludXRlcyAvIDYwKSAlIDI0LCBtaW51dGVzICUgNjAsIDAsIDApO1xuICAgICAgICByZXR1cm4gZGF0ZTtcbiAgICB9XG4gICAgO1xuICAgIC8qKlxuICAgICAqIFNuYXAgZmluYWwgaGVpZ2h0IHRvIGdyaWQgaW50ZXJ2YWxcbiAgICAgKi9cbiAgICBzbmFwVG9HcmlkRmluYWwoKSB7XG4gICAgICAgIGlmICghdGhpcy5yZXNpemVTdGF0ZSlcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgY29uc3QgY3VycmVudEhlaWdodCA9IHRoaXMucmVzaXplU3RhdGUuZWxlbWVudC5vZmZzZXRIZWlnaHQ7XG4gICAgICAgIGNvbnN0IHNuYXBwZWRIZWlnaHQgPSBzbmFwVG9HcmlkKGN1cnJlbnRIZWlnaHQsIHRoaXMuZ3JpZENvbmZpZyk7XG4gICAgICAgIGNvbnN0IG1pbkhlaWdodCA9IG1pbnV0ZXNUb1BpeGVscyh0aGlzLk1JTl9IRUlHSFRfTUlOVVRFUywgdGhpcy5ncmlkQ29uZmlnKTtcbiAgICAgICAgY29uc3QgZmluYWxIZWlnaHQgPSBNYXRoLm1heChtaW5IZWlnaHQsIHNuYXBwZWRIZWlnaHQpO1xuICAgICAgICB0aGlzLnJlc2l6ZVN0YXRlLmVsZW1lbnQuc3R5bGUuaGVpZ2h0ID0gYCR7ZmluYWxIZWlnaHR9cHhgO1xuICAgICAgICB0aGlzLnJlc2l6ZVN0YXRlLmN1cnJlbnRIZWlnaHQgPSBmaW5hbEhlaWdodDtcbiAgICB9XG59XG4iLCAiaW1wb3J0IHsgQ29yZUV2ZW50cyB9IGZyb20gJy4uL2NvbnN0YW50cy9Db3JlRXZlbnRzJztcbmV4cG9ydCBjbGFzcyBFdmVudFBlcnNpc3RlbmNlTWFuYWdlciB7XG4gICAgY29uc3RydWN0b3IoZXZlbnRTZXJ2aWNlLCBldmVudEJ1cywgZGF0ZVNlcnZpY2UpIHtcbiAgICAgICAgdGhpcy5ldmVudFNlcnZpY2UgPSBldmVudFNlcnZpY2U7XG4gICAgICAgIHRoaXMuZXZlbnRCdXMgPSBldmVudEJ1cztcbiAgICAgICAgdGhpcy5kYXRlU2VydmljZSA9IGRhdGVTZXJ2aWNlO1xuICAgICAgICAvKipcbiAgICAgICAgICogSGFuZGxlIGRyYWcgZW5kIC0gdXBkYXRlIGV2ZW50IHBvc2l0aW9uIGluIEluZGV4ZWREQlxuICAgICAgICAgKi9cbiAgICAgICAgdGhpcy5oYW5kbGVEcmFnRW5kID0gYXN5bmMgKGUpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHBheWxvYWQgPSBlLmRldGFpbDtcbiAgICAgICAgICAgIGNvbnN0IHsgc3dwRXZlbnQgfSA9IHBheWxvYWQ7XG4gICAgICAgICAgICAvLyBHZXQgZXhpc3RpbmcgZXZlbnQgdG8gbWVyZ2Ugd2l0aFxuICAgICAgICAgICAgY29uc3QgZXZlbnQgPSBhd2FpdCB0aGlzLmV2ZW50U2VydmljZS5nZXQoc3dwRXZlbnQuZXZlbnRJZCk7XG4gICAgICAgICAgICBpZiAoIWV2ZW50KSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS53YXJuKGBFdmVudFBlcnNpc3RlbmNlTWFuYWdlcjogRXZlbnQgJHtzd3BFdmVudC5ldmVudElkfSBub3QgZm91bmRgKTtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyBQYXJzZSByZXNvdXJjZUlkIGZyb20gY29sdW1uS2V5IGlmIHByZXNlbnRcbiAgICAgICAgICAgIGNvbnN0IHsgcmVzb3VyY2UgfSA9IHRoaXMuZGF0ZVNlcnZpY2UucGFyc2VDb2x1bW5LZXkoc3dwRXZlbnQuY29sdW1uS2V5KTtcbiAgICAgICAgICAgIC8vIFVwZGF0ZSBhbmQgc2F2ZSAtIHN0YXJ0L2VuZCBhbHJlYWR5IGNhbGN1bGF0ZWQgaW4gU3dwRXZlbnRcbiAgICAgICAgICAgIC8vIFNldCBhbGxEYXkgYmFzZWQgb24gZHJvcCB0YXJnZXQ6XG4gICAgICAgICAgICAvLyAtIGhlYWRlcjogYWxsRGF5ID0gdHJ1ZVxuICAgICAgICAgICAgLy8gLSBncmlkOiBhbGxEYXkgPSBmYWxzZSAoY29udmVydHMgYWxsRGF5IGV2ZW50IHRvIHRpbWVkKVxuICAgICAgICAgICAgY29uc3QgdXBkYXRlZEV2ZW50ID0ge1xuICAgICAgICAgICAgICAgIC4uLmV2ZW50LFxuICAgICAgICAgICAgICAgIHN0YXJ0OiBzd3BFdmVudC5zdGFydCxcbiAgICAgICAgICAgICAgICBlbmQ6IHN3cEV2ZW50LmVuZCxcbiAgICAgICAgICAgICAgICByZXNvdXJjZUlkOiByZXNvdXJjZSA/PyBldmVudC5yZXNvdXJjZUlkLFxuICAgICAgICAgICAgICAgIGFsbERheTogcGF5bG9hZC50YXJnZXQgPT09ICdoZWFkZXInLFxuICAgICAgICAgICAgICAgIHN5bmNTdGF0dXM6ICdwZW5kaW5nJ1xuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMuZXZlbnRTZXJ2aWNlLnNhdmUodXBkYXRlZEV2ZW50KTtcbiAgICAgICAgICAgIC8vIEVtaXQgRVZFTlRfVVBEQVRFRCBmb3IgRXZlbnRSZW5kZXJlciB0byByZS1yZW5kZXIgYWZmZWN0ZWQgY29sdW1uc1xuICAgICAgICAgICAgY29uc3QgdXBkYXRlUGF5bG9hZCA9IHtcbiAgICAgICAgICAgICAgICBldmVudElkOiB1cGRhdGVkRXZlbnQuaWQsXG4gICAgICAgICAgICAgICAgc291cmNlQ29sdW1uS2V5OiBwYXlsb2FkLnNvdXJjZUNvbHVtbktleSxcbiAgICAgICAgICAgICAgICB0YXJnZXRDb2x1bW5LZXk6IHN3cEV2ZW50LmNvbHVtbktleVxuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIHRoaXMuZXZlbnRCdXMuZW1pdChDb3JlRXZlbnRzLkVWRU5UX1VQREFURUQsIHVwZGF0ZVBheWxvYWQpO1xuICAgICAgICB9O1xuICAgICAgICAvKipcbiAgICAgICAgICogSGFuZGxlIHJlc2l6ZSBlbmQgLSB1cGRhdGUgZXZlbnQgZHVyYXRpb24gaW4gSW5kZXhlZERCXG4gICAgICAgICAqL1xuICAgICAgICB0aGlzLmhhbmRsZVJlc2l6ZUVuZCA9IGFzeW5jIChlKSA9PiB7XG4gICAgICAgICAgICBjb25zdCBwYXlsb2FkID0gZS5kZXRhaWw7XG4gICAgICAgICAgICBjb25zdCB7IHN3cEV2ZW50IH0gPSBwYXlsb2FkO1xuICAgICAgICAgICAgLy8gR2V0IGV4aXN0aW5nIGV2ZW50IHRvIG1lcmdlIHdpdGhcbiAgICAgICAgICAgIGNvbnN0IGV2ZW50ID0gYXdhaXQgdGhpcy5ldmVudFNlcnZpY2UuZ2V0KHN3cEV2ZW50LmV2ZW50SWQpO1xuICAgICAgICAgICAgaWYgKCFldmVudCkge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUud2FybihgRXZlbnRQZXJzaXN0ZW5jZU1hbmFnZXI6IEV2ZW50ICR7c3dwRXZlbnQuZXZlbnRJZH0gbm90IGZvdW5kYCk7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgLy8gVXBkYXRlIGFuZCBzYXZlIC0gZW5kIGFscmVhZHkgY2FsY3VsYXRlZCBpbiBTd3BFdmVudFxuICAgICAgICAgICAgY29uc3QgdXBkYXRlZEV2ZW50ID0ge1xuICAgICAgICAgICAgICAgIC4uLmV2ZW50LFxuICAgICAgICAgICAgICAgIGVuZDogc3dwRXZlbnQuZW5kLFxuICAgICAgICAgICAgICAgIHN5bmNTdGF0dXM6ICdwZW5kaW5nJ1xuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMuZXZlbnRTZXJ2aWNlLnNhdmUodXBkYXRlZEV2ZW50KTtcbiAgICAgICAgICAgIC8vIEVtaXQgRVZFTlRfVVBEQVRFRCBmb3IgRXZlbnRSZW5kZXJlciB0byByZS1yZW5kZXIgdGhlIGNvbHVtblxuICAgICAgICAgICAgLy8gUmVzaXplIHN0YXlzIGluIHNhbWUgY29sdW1uLCBzbyBzb3VyY2UgYW5kIHRhcmdldCBhcmUgdGhlIHNhbWVcbiAgICAgICAgICAgIGNvbnN0IHVwZGF0ZVBheWxvYWQgPSB7XG4gICAgICAgICAgICAgICAgZXZlbnRJZDogdXBkYXRlZEV2ZW50LmlkLFxuICAgICAgICAgICAgICAgIHNvdXJjZUNvbHVtbktleTogc3dwRXZlbnQuY29sdW1uS2V5LFxuICAgICAgICAgICAgICAgIHRhcmdldENvbHVtbktleTogc3dwRXZlbnQuY29sdW1uS2V5XG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgdGhpcy5ldmVudEJ1cy5lbWl0KENvcmVFdmVudHMuRVZFTlRfVVBEQVRFRCwgdXBkYXRlUGF5bG9hZCk7XG4gICAgICAgIH07XG4gICAgICAgIHRoaXMuc2V0dXBMaXN0ZW5lcnMoKTtcbiAgICB9XG4gICAgc2V0dXBMaXN0ZW5lcnMoKSB7XG4gICAgICAgIHRoaXMuZXZlbnRCdXMub24oQ29yZUV2ZW50cy5FVkVOVF9EUkFHX0VORCwgdGhpcy5oYW5kbGVEcmFnRW5kKTtcbiAgICAgICAgdGhpcy5ldmVudEJ1cy5vbihDb3JlRXZlbnRzLkVWRU5UX1JFU0laRV9FTkQsIHRoaXMuaGFuZGxlUmVzaXplRW5kKTtcbiAgICB9XG59XG4iLCAiaW1wb3J0IHsgQ29udGFpbmVyIH0gZnJvbSAnQG5vdmFkaS9jb3JlJztcbmltcG9ydCB7IERhdGVSZW5kZXJlciB9IGZyb20gJy4vZmVhdHVyZXMvZGF0ZS9EYXRlUmVuZGVyZXInO1xuaW1wb3J0IHsgRGF0ZVNlcnZpY2UgfSBmcm9tICcuL2NvcmUvRGF0ZVNlcnZpY2UnO1xuaW1wb3J0IHsgUmVzb3VyY2VSZW5kZXJlciB9IGZyb20gJy4vZmVhdHVyZXMvcmVzb3VyY2UvUmVzb3VyY2VSZW5kZXJlcic7XG5pbXBvcnQgeyBUZWFtUmVuZGVyZXIgfSBmcm9tICcuL2ZlYXR1cmVzL3RlYW0vVGVhbVJlbmRlcmVyJztcbmltcG9ydCB7IERlcGFydG1lbnRSZW5kZXJlciB9IGZyb20gJy4vZmVhdHVyZXMvZGVwYXJ0bWVudC9EZXBhcnRtZW50UmVuZGVyZXInO1xuaW1wb3J0IHsgQ2FsZW5kYXJPcmNoZXN0cmF0b3IgfSBmcm9tICcuL2NvcmUvQ2FsZW5kYXJPcmNoZXN0cmF0b3InO1xuaW1wb3J0IHsgQ2FsZW5kYXJBcHAgfSBmcm9tICcuL2NvcmUvQ2FsZW5kYXJBcHAnO1xuaW1wb3J0IHsgVGltZUF4aXNSZW5kZXJlciB9IGZyb20gJy4vZmVhdHVyZXMvdGltZWF4aXMvVGltZUF4aXNSZW5kZXJlcic7XG5pbXBvcnQgeyBTY3JvbGxNYW5hZ2VyIH0gZnJvbSAnLi9jb3JlL1Njcm9sbE1hbmFnZXInO1xuaW1wb3J0IHsgSGVhZGVyRHJhd2VyTWFuYWdlciB9IGZyb20gJy4vY29yZS9IZWFkZXJEcmF3ZXJNYW5hZ2VyJztcbmltcG9ydCB7IE1vY2tUZWFtU3RvcmUsIE1vY2tSZXNvdXJjZVN0b3JlIH0gZnJvbSAnLi9kZW1vL01vY2tTdG9yZXMnO1xuaW1wb3J0IHsgRGVtb0FwcCB9IGZyb20gJy4vZGVtby9EZW1vQXBwJztcbi8vIEV2ZW50IHN5c3RlbVxuaW1wb3J0IHsgRXZlbnRCdXMgfSBmcm9tICcuL2NvcmUvRXZlbnRCdXMnO1xuLy8gU3RvcmFnZVxuaW1wb3J0IHsgSW5kZXhlZERCQ29udGV4dCB9IGZyb20gJy4vc3RvcmFnZS9JbmRleGVkREJDb250ZXh0JztcbmltcG9ydCB7IEV2ZW50U3RvcmUgfSBmcm9tICcuL3N0b3JhZ2UvZXZlbnRzL0V2ZW50U3RvcmUnO1xuaW1wb3J0IHsgRXZlbnRTZXJ2aWNlIH0gZnJvbSAnLi9zdG9yYWdlL2V2ZW50cy9FdmVudFNlcnZpY2UnO1xuaW1wb3J0IHsgUmVzb3VyY2VTdG9yZSB9IGZyb20gJy4vc3RvcmFnZS9yZXNvdXJjZXMvUmVzb3VyY2VTdG9yZSc7XG5pbXBvcnQgeyBSZXNvdXJjZVNlcnZpY2UgfSBmcm9tICcuL3N0b3JhZ2UvcmVzb3VyY2VzL1Jlc291cmNlU2VydmljZSc7XG5pbXBvcnQgeyBCb29raW5nU3RvcmUgfSBmcm9tICcuL3N0b3JhZ2UvYm9va2luZ3MvQm9va2luZ1N0b3JlJztcbmltcG9ydCB7IEJvb2tpbmdTZXJ2aWNlIH0gZnJvbSAnLi9zdG9yYWdlL2Jvb2tpbmdzL0Jvb2tpbmdTZXJ2aWNlJztcbmltcG9ydCB7IEN1c3RvbWVyU3RvcmUgfSBmcm9tICcuL3N0b3JhZ2UvY3VzdG9tZXJzL0N1c3RvbWVyU3RvcmUnO1xuaW1wb3J0IHsgQ3VzdG9tZXJTZXJ2aWNlIH0gZnJvbSAnLi9zdG9yYWdlL2N1c3RvbWVycy9DdXN0b21lclNlcnZpY2UnO1xuaW1wb3J0IHsgVGVhbVN0b3JlIH0gZnJvbSAnLi9zdG9yYWdlL3RlYW1zL1RlYW1TdG9yZSc7XG5pbXBvcnQgeyBUZWFtU2VydmljZSB9IGZyb20gJy4vc3RvcmFnZS90ZWFtcy9UZWFtU2VydmljZSc7XG5pbXBvcnQgeyBEZXBhcnRtZW50U3RvcmUgfSBmcm9tICcuL3N0b3JhZ2UvZGVwYXJ0bWVudHMvRGVwYXJ0bWVudFN0b3JlJztcbmltcG9ydCB7IERlcGFydG1lbnRTZXJ2aWNlIH0gZnJvbSAnLi9zdG9yYWdlL2RlcGFydG1lbnRzL0RlcGFydG1lbnRTZXJ2aWNlJztcbmltcG9ydCB7IFNldHRpbmdzU3RvcmUgfSBmcm9tICcuL3N0b3JhZ2Uvc2V0dGluZ3MvU2V0dGluZ3NTdG9yZSc7XG5pbXBvcnQgeyBTZXR0aW5nc1NlcnZpY2UgfSBmcm9tICcuL3N0b3JhZ2Uvc2V0dGluZ3MvU2V0dGluZ3NTZXJ2aWNlJztcbmltcG9ydCB7IFZpZXdDb25maWdTdG9yZSB9IGZyb20gJy4vc3RvcmFnZS92aWV3Y29uZmlncy9WaWV3Q29uZmlnU3RvcmUnO1xuaW1wb3J0IHsgVmlld0NvbmZpZ1NlcnZpY2UgfSBmcm9tICcuL3N0b3JhZ2Uvdmlld2NvbmZpZ3MvVmlld0NvbmZpZ1NlcnZpY2UnO1xuLy8gQXVkaXRcbmltcG9ydCB7IEF1ZGl0U3RvcmUgfSBmcm9tICcuL3N0b3JhZ2UvYXVkaXQvQXVkaXRTdG9yZSc7XG5pbXBvcnQgeyBBdWRpdFNlcnZpY2UgfSBmcm9tICcuL3N0b3JhZ2UvYXVkaXQvQXVkaXRTZXJ2aWNlJztcbmltcG9ydCB7IE1vY2tFdmVudFJlcG9zaXRvcnkgfSBmcm9tICcuL3JlcG9zaXRvcmllcy9Nb2NrRXZlbnRSZXBvc2l0b3J5JztcbmltcG9ydCB7IE1vY2tSZXNvdXJjZVJlcG9zaXRvcnkgfSBmcm9tICcuL3JlcG9zaXRvcmllcy9Nb2NrUmVzb3VyY2VSZXBvc2l0b3J5JztcbmltcG9ydCB7IE1vY2tCb29raW5nUmVwb3NpdG9yeSB9IGZyb20gJy4vcmVwb3NpdG9yaWVzL01vY2tCb29raW5nUmVwb3NpdG9yeSc7XG5pbXBvcnQgeyBNb2NrQ3VzdG9tZXJSZXBvc2l0b3J5IH0gZnJvbSAnLi9yZXBvc2l0b3JpZXMvTW9ja0N1c3RvbWVyUmVwb3NpdG9yeSc7XG5pbXBvcnQgeyBNb2NrQXVkaXRSZXBvc2l0b3J5IH0gZnJvbSAnLi9yZXBvc2l0b3JpZXMvTW9ja0F1ZGl0UmVwb3NpdG9yeSc7XG5pbXBvcnQgeyBNb2NrVGVhbVJlcG9zaXRvcnkgfSBmcm9tICcuL3JlcG9zaXRvcmllcy9Nb2NrVGVhbVJlcG9zaXRvcnknO1xuaW1wb3J0IHsgTW9ja0RlcGFydG1lbnRSZXBvc2l0b3J5IH0gZnJvbSAnLi9yZXBvc2l0b3JpZXMvTW9ja0RlcGFydG1lbnRSZXBvc2l0b3J5JztcbmltcG9ydCB7IE1vY2tTZXR0aW5nc1JlcG9zaXRvcnkgfSBmcm9tICcuL3JlcG9zaXRvcmllcy9Nb2NrU2V0dGluZ3NSZXBvc2l0b3J5JztcbmltcG9ydCB7IE1vY2tWaWV3Q29uZmlnUmVwb3NpdG9yeSB9IGZyb20gJy4vcmVwb3NpdG9yaWVzL01vY2tWaWV3Q29uZmlnUmVwb3NpdG9yeSc7XG4vLyBXb3JrZXJzXG5pbXBvcnQgeyBEYXRhU2VlZGVyIH0gZnJvbSAnLi93b3JrZXJzL0RhdGFTZWVkZXInO1xuLy8gRmVhdHVyZXNcbmltcG9ydCB7IEV2ZW50UmVuZGVyZXIgfSBmcm9tICcuL2ZlYXR1cmVzL2V2ZW50L0V2ZW50UmVuZGVyZXInO1xuaW1wb3J0IHsgU2NoZWR1bGVSZW5kZXJlciB9IGZyb20gJy4vZmVhdHVyZXMvc2NoZWR1bGUvU2NoZWR1bGVSZW5kZXJlcic7XG5pbXBvcnQgeyBIZWFkZXJEcmF3ZXJSZW5kZXJlciB9IGZyb20gJy4vZmVhdHVyZXMvaGVhZGVyZHJhd2VyL0hlYWRlckRyYXdlclJlbmRlcmVyJztcbi8vIFNjaGVkdWxlXG5pbXBvcnQgeyBTY2hlZHVsZU92ZXJyaWRlU3RvcmUgfSBmcm9tICcuL3N0b3JhZ2Uvc2NoZWR1bGVzL1NjaGVkdWxlT3ZlcnJpZGVTdG9yZSc7XG5pbXBvcnQgeyBTY2hlZHVsZU92ZXJyaWRlU2VydmljZSB9IGZyb20gJy4vc3RvcmFnZS9zY2hlZHVsZXMvU2NoZWR1bGVPdmVycmlkZVNlcnZpY2UnO1xuaW1wb3J0IHsgUmVzb3VyY2VTY2hlZHVsZVNlcnZpY2UgfSBmcm9tICcuL3N0b3JhZ2Uvc2NoZWR1bGVzL1Jlc291cmNlU2NoZWR1bGVTZXJ2aWNlJztcbi8vIE1hbmFnZXJzXG5pbXBvcnQgeyBEcmFnRHJvcE1hbmFnZXIgfSBmcm9tICcuL21hbmFnZXJzL0RyYWdEcm9wTWFuYWdlcic7XG5pbXBvcnQgeyBFZGdlU2Nyb2xsTWFuYWdlciB9IGZyb20gJy4vbWFuYWdlcnMvRWRnZVNjcm9sbE1hbmFnZXInO1xuaW1wb3J0IHsgUmVzaXplTWFuYWdlciB9IGZyb20gJy4vbWFuYWdlcnMvUmVzaXplTWFuYWdlcic7XG5pbXBvcnQgeyBFdmVudFBlcnNpc3RlbmNlTWFuYWdlciB9IGZyb20gJy4vbWFuYWdlcnMvRXZlbnRQZXJzaXN0ZW5jZU1hbmFnZXInO1xuY29uc3QgZGVmYXVsdFRpbWVGb3JtYXRDb25maWcgPSB7XG4gICAgdGltZXpvbmU6IEludGwuRGF0ZVRpbWVGb3JtYXQoKS5yZXNvbHZlZE9wdGlvbnMoKS50aW1lWm9uZSxcbiAgICB1c2UyNEhvdXJGb3JtYXQ6IHRydWUsXG4gICAgbG9jYWxlOiAnZGEtREsnLFxuICAgIGRhdGVGb3JtYXQ6ICdsb2NhbGUnLFxuICAgIHNob3dTZWNvbmRzOiBmYWxzZVxufTtcbmNvbnN0IGRlZmF1bHRHcmlkQ29uZmlnID0ge1xuICAgIGhvdXJIZWlnaHQ6IDY0LFxuICAgIGRheVN0YXJ0SG91cjogNixcbiAgICBkYXlFbmRIb3VyOiAxOCxcbiAgICBzbmFwSW50ZXJ2YWw6IDE1LFxuICAgIGdyaWRTdGFydFRocmVzaG9sZE1pbnV0ZXM6IDMwXG59O1xuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZUNvbnRhaW5lcigpIHtcbiAgICBjb25zdCBjb250YWluZXIgPSBuZXcgQ29udGFpbmVyKCk7XG4gICAgY29uc3QgYnVpbGRlciA9IGNvbnRhaW5lci5idWlsZGVyKCk7XG4gICAgLy8gQ29uZmlnXG4gICAgYnVpbGRlci5yZWdpc3Rlckluc3RhbmNlKGRlZmF1bHRUaW1lRm9ybWF0Q29uZmlnKS5hcyhcIklUaW1lRm9ybWF0Q29uZmlnXCIpO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJJbnN0YW5jZShkZWZhdWx0R3JpZENvbmZpZykuYXMoXCJJR3JpZENvbmZpZ1wiKTtcbiAgICAvLyBDb3JlIC0gRXZlbnRCdXNcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShFdmVudEJ1cykuYXMoXCJFdmVudEJ1c1wiKTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShFdmVudEJ1cykuYXMoXCJJRXZlbnRCdXNcIik7XG4gICAgLy8gU2VydmljZXNcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShEYXRlU2VydmljZSkuYXMoXCJEYXRlU2VydmljZVwiKS5hdXRvV2lyZSh7XG4gICAgICAgIG1hcFJlc29sdmVyczogW1xuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSVRpbWVGb3JtYXRDb25maWdcIiksXG4gICAgICAgICAgICB1bmRlZmluZWRcbiAgICAgICAgXVxuICAgIH0pO1xuICAgIC8vIFN0b3JhZ2UgaW5mcmFzdHJ1Y3R1cmVcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShJbmRleGVkREJDb250ZXh0KS5hcyhcIkluZGV4ZWREQkNvbnRleHRcIikuYXV0b1dpcmUoe1xuICAgICAgICBtYXBSZXNvbHZlcnM6IFtcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZUFsbChcIklTdG9yZVwiKVxuICAgICAgICBdXG4gICAgfSk7XG4gICAgLy8gU3RvcmVzIChmb3IgSW5kZXhlZERCIHNjaGVtYSBjcmVhdGlvbilcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShFdmVudFN0b3JlKS5hcyhcIklTdG9yZVwiKTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShSZXNvdXJjZVN0b3JlKS5hcyhcIklTdG9yZVwiKTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShCb29raW5nU3RvcmUpLmFzKFwiSVN0b3JlXCIpO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKEN1c3RvbWVyU3RvcmUpLmFzKFwiSVN0b3JlXCIpO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKFRlYW1TdG9yZSkuYXMoXCJJU3RvcmVcIik7XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoRGVwYXJ0bWVudFN0b3JlKS5hcyhcIklTdG9yZVwiKTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShTY2hlZHVsZU92ZXJyaWRlU3RvcmUpLmFzKFwiSVN0b3JlXCIpO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKEF1ZGl0U3RvcmUpLmFzKFwiSVN0b3JlXCIpO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKFNldHRpbmdzU3RvcmUpLmFzKFwiSVN0b3JlXCIpO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKFZpZXdDb25maWdTdG9yZSkuYXMoXCJJU3RvcmVcIik7XG4gICAgLy8gRW50aXR5IHNlcnZpY2VzIChmb3IgRGF0YVNlZWRlciBwb2x5bW9ycGhpYyBhcnJheSlcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShFdmVudFNlcnZpY2UpLmFzKFwiSUVudGl0eVNlcnZpY2VcIikuYXV0b1dpcmUoe1xuICAgICAgICBtYXBSZXNvbHZlcnM6IFtcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkluZGV4ZWREQkNvbnRleHRcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJRXZlbnRCdXNcIilcbiAgICAgICAgXVxuICAgIH0pO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKEV2ZW50U2VydmljZSkuYXMoXCJJRW50aXR5U2VydmljZVwiKS5hdXRvV2lyZSh7XG4gICAgICAgIG1hcFJlc29sdmVyczogW1xuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSW5kZXhlZERCQ29udGV4dFwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIklFdmVudEJ1c1wiKVxuICAgICAgICBdXG4gICAgfSk7XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoRXZlbnRTZXJ2aWNlKS5hcyhcIkV2ZW50U2VydmljZVwiKS5hdXRvV2lyZSh7XG4gICAgICAgIG1hcFJlc29sdmVyczogW1xuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSW5kZXhlZERCQ29udGV4dFwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIklFdmVudEJ1c1wiKVxuICAgICAgICBdXG4gICAgfSk7XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoUmVzb3VyY2VTZXJ2aWNlKS5hcyhcIklFbnRpdHlTZXJ2aWNlXCIpLmF1dG9XaXJlKHtcbiAgICAgICAgbWFwUmVzb2x2ZXJzOiBbXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJbmRleGVkREJDb250ZXh0XCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSUV2ZW50QnVzXCIpXG4gICAgICAgIF1cbiAgICB9KTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShSZXNvdXJjZVNlcnZpY2UpLmFzKFwiSUVudGl0eVNlcnZpY2VcIikuYXV0b1dpcmUoe1xuICAgICAgICBtYXBSZXNvbHZlcnM6IFtcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkluZGV4ZWREQkNvbnRleHRcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJRXZlbnRCdXNcIilcbiAgICAgICAgXVxuICAgIH0pO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKFJlc291cmNlU2VydmljZSkuYXMoXCJSZXNvdXJjZVNlcnZpY2VcIikuYXV0b1dpcmUoe1xuICAgICAgICBtYXBSZXNvbHZlcnM6IFtcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkluZGV4ZWREQkNvbnRleHRcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJRXZlbnRCdXNcIilcbiAgICAgICAgXVxuICAgIH0pO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKEJvb2tpbmdTZXJ2aWNlKS5hcyhcIklFbnRpdHlTZXJ2aWNlXCIpLmF1dG9XaXJlKHtcbiAgICAgICAgbWFwUmVzb2x2ZXJzOiBbXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJbmRleGVkREJDb250ZXh0XCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSUV2ZW50QnVzXCIpXG4gICAgICAgIF1cbiAgICB9KTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShCb29raW5nU2VydmljZSkuYXMoXCJJRW50aXR5U2VydmljZVwiKS5hdXRvV2lyZSh7XG4gICAgICAgIG1hcFJlc29sdmVyczogW1xuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSW5kZXhlZERCQ29udGV4dFwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIklFdmVudEJ1c1wiKVxuICAgICAgICBdXG4gICAgfSk7XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoQm9va2luZ1NlcnZpY2UpLmFzKFwiQm9va2luZ1NlcnZpY2VcIikuYXV0b1dpcmUoe1xuICAgICAgICBtYXBSZXNvbHZlcnM6IFtcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkluZGV4ZWREQkNvbnRleHRcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJRXZlbnRCdXNcIilcbiAgICAgICAgXVxuICAgIH0pO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKEN1c3RvbWVyU2VydmljZSkuYXMoXCJJRW50aXR5U2VydmljZVwiKS5hdXRvV2lyZSh7XG4gICAgICAgIG1hcFJlc29sdmVyczogW1xuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSW5kZXhlZERCQ29udGV4dFwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIklFdmVudEJ1c1wiKVxuICAgICAgICBdXG4gICAgfSk7XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoQ3VzdG9tZXJTZXJ2aWNlKS5hcyhcIklFbnRpdHlTZXJ2aWNlXCIpLmF1dG9XaXJlKHtcbiAgICAgICAgbWFwUmVzb2x2ZXJzOiBbXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJbmRleGVkREJDb250ZXh0XCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSUV2ZW50QnVzXCIpXG4gICAgICAgIF1cbiAgICB9KTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShDdXN0b21lclNlcnZpY2UpLmFzKFwiQ3VzdG9tZXJTZXJ2aWNlXCIpLmF1dG9XaXJlKHtcbiAgICAgICAgbWFwUmVzb2x2ZXJzOiBbXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJbmRleGVkREJDb250ZXh0XCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSUV2ZW50QnVzXCIpXG4gICAgICAgIF1cbiAgICB9KTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShUZWFtU2VydmljZSkuYXMoXCJJRW50aXR5U2VydmljZVwiKS5hdXRvV2lyZSh7XG4gICAgICAgIG1hcFJlc29sdmVyczogW1xuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSW5kZXhlZERCQ29udGV4dFwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIklFdmVudEJ1c1wiKVxuICAgICAgICBdXG4gICAgfSk7XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoVGVhbVNlcnZpY2UpLmFzKFwiSUVudGl0eVNlcnZpY2VcIikuYXV0b1dpcmUoe1xuICAgICAgICBtYXBSZXNvbHZlcnM6IFtcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkluZGV4ZWREQkNvbnRleHRcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJRXZlbnRCdXNcIilcbiAgICAgICAgXVxuICAgIH0pO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKFRlYW1TZXJ2aWNlKS5hcyhcIlRlYW1TZXJ2aWNlXCIpLmF1dG9XaXJlKHtcbiAgICAgICAgbWFwUmVzb2x2ZXJzOiBbXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJbmRleGVkREJDb250ZXh0XCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSUV2ZW50QnVzXCIpXG4gICAgICAgIF1cbiAgICB9KTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShEZXBhcnRtZW50U2VydmljZSkuYXMoXCJJRW50aXR5U2VydmljZVwiKS5hdXRvV2lyZSh7XG4gICAgICAgIG1hcFJlc29sdmVyczogW1xuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSW5kZXhlZERCQ29udGV4dFwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIklFdmVudEJ1c1wiKVxuICAgICAgICBdXG4gICAgfSk7XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoRGVwYXJ0bWVudFNlcnZpY2UpLmFzKFwiSUVudGl0eVNlcnZpY2VcIikuYXV0b1dpcmUoe1xuICAgICAgICBtYXBSZXNvbHZlcnM6IFtcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkluZGV4ZWREQkNvbnRleHRcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJRXZlbnRCdXNcIilcbiAgICAgICAgXVxuICAgIH0pO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKERlcGFydG1lbnRTZXJ2aWNlKS5hcyhcIkRlcGFydG1lbnRTZXJ2aWNlXCIpLmF1dG9XaXJlKHtcbiAgICAgICAgbWFwUmVzb2x2ZXJzOiBbXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJbmRleGVkREJDb250ZXh0XCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSUV2ZW50QnVzXCIpXG4gICAgICAgIF1cbiAgICB9KTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShTZXR0aW5nc1NlcnZpY2UpLmFzKFwiSUVudGl0eVNlcnZpY2VcIikuYXV0b1dpcmUoe1xuICAgICAgICBtYXBSZXNvbHZlcnM6IFtcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkluZGV4ZWREQkNvbnRleHRcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJRXZlbnRCdXNcIilcbiAgICAgICAgXVxuICAgIH0pO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKFNldHRpbmdzU2VydmljZSkuYXMoXCJJRW50aXR5U2VydmljZVwiKS5hdXRvV2lyZSh7XG4gICAgICAgIG1hcFJlc29sdmVyczogW1xuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSW5kZXhlZERCQ29udGV4dFwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIklFdmVudEJ1c1wiKVxuICAgICAgICBdXG4gICAgfSk7XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoU2V0dGluZ3NTZXJ2aWNlKS5hcyhcIlNldHRpbmdzU2VydmljZVwiKS5hdXRvV2lyZSh7XG4gICAgICAgIG1hcFJlc29sdmVyczogW1xuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSW5kZXhlZERCQ29udGV4dFwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIklFdmVudEJ1c1wiKVxuICAgICAgICBdXG4gICAgfSk7XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoVmlld0NvbmZpZ1NlcnZpY2UpLmFzKFwiSUVudGl0eVNlcnZpY2VcIikuYXV0b1dpcmUoe1xuICAgICAgICBtYXBSZXNvbHZlcnM6IFtcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkluZGV4ZWREQkNvbnRleHRcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJRXZlbnRCdXNcIilcbiAgICAgICAgXVxuICAgIH0pO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKFZpZXdDb25maWdTZXJ2aWNlKS5hcyhcIklFbnRpdHlTZXJ2aWNlXCIpLmF1dG9XaXJlKHtcbiAgICAgICAgbWFwUmVzb2x2ZXJzOiBbXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJbmRleGVkREJDb250ZXh0XCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSUV2ZW50QnVzXCIpXG4gICAgICAgIF1cbiAgICB9KTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShWaWV3Q29uZmlnU2VydmljZSkuYXMoXCJWaWV3Q29uZmlnU2VydmljZVwiKS5hdXRvV2lyZSh7XG4gICAgICAgIG1hcFJlc29sdmVyczogW1xuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSW5kZXhlZERCQ29udGV4dFwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIklFdmVudEJ1c1wiKVxuICAgICAgICBdXG4gICAgfSk7XG4gICAgLy8gUmVwb3NpdG9yaWVzIChmb3IgRGF0YVNlZWRlciBwb2x5bW9ycGhpYyBhcnJheSlcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShNb2NrRXZlbnRSZXBvc2l0b3J5KS5hcyhcIklBcGlSZXBvc2l0b3J5XCIpO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKE1vY2tFdmVudFJlcG9zaXRvcnkpLmFzKFwiSUFwaVJlcG9zaXRvcnlcIik7XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoTW9ja1Jlc291cmNlUmVwb3NpdG9yeSkuYXMoXCJJQXBpUmVwb3NpdG9yeVwiKTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShNb2NrUmVzb3VyY2VSZXBvc2l0b3J5KS5hcyhcIklBcGlSZXBvc2l0b3J5XCIpO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKE1vY2tCb29raW5nUmVwb3NpdG9yeSkuYXMoXCJJQXBpUmVwb3NpdG9yeVwiKTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShNb2NrQm9va2luZ1JlcG9zaXRvcnkpLmFzKFwiSUFwaVJlcG9zaXRvcnlcIik7XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoTW9ja0N1c3RvbWVyUmVwb3NpdG9yeSkuYXMoXCJJQXBpUmVwb3NpdG9yeVwiKTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShNb2NrQ3VzdG9tZXJSZXBvc2l0b3J5KS5hcyhcIklBcGlSZXBvc2l0b3J5XCIpO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKE1vY2tBdWRpdFJlcG9zaXRvcnkpLmFzKFwiSUFwaVJlcG9zaXRvcnlcIik7XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoTW9ja0F1ZGl0UmVwb3NpdG9yeSkuYXMoXCJJQXBpUmVwb3NpdG9yeVwiKTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShNb2NrVGVhbVJlcG9zaXRvcnkpLmFzKFwiSUFwaVJlcG9zaXRvcnlcIik7XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoTW9ja1RlYW1SZXBvc2l0b3J5KS5hcyhcIklBcGlSZXBvc2l0b3J5XCIpO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKE1vY2tEZXBhcnRtZW50UmVwb3NpdG9yeSkuYXMoXCJJQXBpUmVwb3NpdG9yeVwiKTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShNb2NrRGVwYXJ0bWVudFJlcG9zaXRvcnkpLmFzKFwiSUFwaVJlcG9zaXRvcnlcIik7XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoTW9ja1NldHRpbmdzUmVwb3NpdG9yeSkuYXMoXCJJQXBpUmVwb3NpdG9yeVwiKTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShNb2NrU2V0dGluZ3NSZXBvc2l0b3J5KS5hcyhcIklBcGlSZXBvc2l0b3J5XCIpO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKE1vY2tWaWV3Q29uZmlnUmVwb3NpdG9yeSkuYXMoXCJJQXBpUmVwb3NpdG9yeVwiKTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShNb2NrVmlld0NvbmZpZ1JlcG9zaXRvcnkpLmFzKFwiSUFwaVJlcG9zaXRvcnlcIik7XG4gICAgLy8gQXVkaXQgc2VydmljZSAobGlzdGVucyB0byBFTlRJVFlfU0FWRUQvREVMRVRFRCBldmVudHMgYXV0b21hdGljYWxseSlcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShBdWRpdFNlcnZpY2UpLmFzKFwiQXVkaXRTZXJ2aWNlXCIpLmF1dG9XaXJlKHtcbiAgICAgICAgbWFwUmVzb2x2ZXJzOiBbXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJbmRleGVkREJDb250ZXh0XCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSUV2ZW50QnVzXCIpXG4gICAgICAgIF1cbiAgICB9KTtcbiAgICAvLyBXb3JrZXJzXG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoRGF0YVNlZWRlcikuYXMoXCJEYXRhU2VlZGVyXCIpLmF1dG9XaXJlKHtcbiAgICAgICAgbWFwUmVzb2x2ZXJzOiBbXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGVBbGwoXCJJRW50aXR5U2VydmljZVwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZUFsbChcIklBcGlSZXBvc2l0b3J5XCIpXG4gICAgICAgIF1cbiAgICB9KTtcbiAgICAvLyBTY2hlZHVsZSBzZXJ2aWNlc1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKFNjaGVkdWxlT3ZlcnJpZGVTZXJ2aWNlKS5hcyhcIlNjaGVkdWxlT3ZlcnJpZGVTZXJ2aWNlXCIpLmF1dG9XaXJlKHtcbiAgICAgICAgbWFwUmVzb2x2ZXJzOiBbXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJbmRleGVkREJDb250ZXh0XCIpXG4gICAgICAgIF1cbiAgICB9KTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShSZXNvdXJjZVNjaGVkdWxlU2VydmljZSkuYXMoXCJSZXNvdXJjZVNjaGVkdWxlU2VydmljZVwiKS5hdXRvV2lyZSh7XG4gICAgICAgIG1hcFJlc29sdmVyczogW1xuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiUmVzb3VyY2VTZXJ2aWNlXCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiU2NoZWR1bGVPdmVycmlkZVNlcnZpY2VcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJEYXRlU2VydmljZVwiKVxuICAgICAgICBdXG4gICAgfSk7XG4gICAgLy8gRmVhdHVyZXNcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShFdmVudFJlbmRlcmVyKS5hcyhcIkV2ZW50UmVuZGVyZXJcIikuYXV0b1dpcmUoe1xuICAgICAgICBtYXBSZXNvbHZlcnM6IFtcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkV2ZW50U2VydmljZVwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkRhdGVTZXJ2aWNlXCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSUdyaWRDb25maWdcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJRXZlbnRCdXNcIilcbiAgICAgICAgXVxuICAgIH0pO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKFNjaGVkdWxlUmVuZGVyZXIpLmFzKFwiU2NoZWR1bGVSZW5kZXJlclwiKS5hdXRvV2lyZSh7XG4gICAgICAgIG1hcFJlc29sdmVyczogW1xuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiUmVzb3VyY2VTY2hlZHVsZVNlcnZpY2VcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJEYXRlU2VydmljZVwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIklHcmlkQ29uZmlnXCIpXG4gICAgICAgIF1cbiAgICB9KTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShIZWFkZXJEcmF3ZXJSZW5kZXJlcikuYXMoXCJIZWFkZXJEcmF3ZXJSZW5kZXJlclwiKS5hdXRvV2lyZSh7XG4gICAgICAgIG1hcFJlc29sdmVyczogW1xuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSUV2ZW50QnVzXCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSUdyaWRDb25maWdcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJIZWFkZXJEcmF3ZXJNYW5hZ2VyXCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiRXZlbnRTZXJ2aWNlXCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiRGF0ZVNlcnZpY2VcIilcbiAgICAgICAgXVxuICAgIH0pO1xuICAgIC8vIFJlbmRlcmVycyAtIHJlZ2lzdHJlcmVzIHNvbSBSZW5kZXJlciAoYXJyYXkgaW5qZWN0aW9uIHRpbCBDYWxlbmRhck9yY2hlc3RyYXRvcilcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShEYXRlUmVuZGVyZXIpLmFzKFwiSVJlbmRlcmVyXCIpLmF1dG9XaXJlKHtcbiAgICAgICAgbWFwUmVzb2x2ZXJzOiBbXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJEYXRlU2VydmljZVwiKVxuICAgICAgICBdXG4gICAgfSk7XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoUmVzb3VyY2VSZW5kZXJlcikuYXMoXCJJUmVuZGVyZXJcIikuYXV0b1dpcmUoe1xuICAgICAgICBtYXBSZXNvbHZlcnM6IFtcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIlJlc291cmNlU2VydmljZVwiKVxuICAgICAgICBdXG4gICAgfSk7XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoVGVhbVJlbmRlcmVyKS5hcyhcIklSZW5kZXJlclwiKS5hdXRvV2lyZSh7XG4gICAgICAgIG1hcFJlc29sdmVyczogW1xuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiVGVhbVNlcnZpY2VcIilcbiAgICAgICAgXVxuICAgIH0pO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKERlcGFydG1lbnRSZW5kZXJlcikuYXMoXCJJUmVuZGVyZXJcIikuYXV0b1dpcmUoe1xuICAgICAgICBtYXBSZXNvbHZlcnM6IFtcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkRlcGFydG1lbnRTZXJ2aWNlXCIpXG4gICAgICAgIF1cbiAgICB9KTtcbiAgICAvLyBTdG9yZXMgLSByZWdpc3RyZXJlcyBzb20gSUdyb3VwaW5nU3RvcmVcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShNb2NrVGVhbVN0b3JlKS5hcyhcIklHcm91cGluZ1N0b3JlXCIpO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKE1vY2tSZXNvdXJjZVN0b3JlKS5hcyhcIklHcm91cGluZ1N0b3JlXCIpO1xuICAgIC8vIENhbGVuZGFyT3JjaGVzdHJhdG9yIG1vZHRhZ2VyIElHcm91cGluZ1N0b3JlW10gYXV0b21hdGlzayAoYXJyYXkgaW5qZWN0aW9uKVxuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKENhbGVuZGFyT3JjaGVzdHJhdG9yKS5hcyhcIkNhbGVuZGFyT3JjaGVzdHJhdG9yXCIpLmF1dG9XaXJlKHtcbiAgICAgICAgbWFwUmVzb2x2ZXJzOiBbXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGVBbGwoXCJJUmVuZGVyZXJcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJFdmVudFJlbmRlcmVyXCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiU2NoZWR1bGVSZW5kZXJlclwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkhlYWRlckRyYXdlclJlbmRlcmVyXCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiRGF0ZVNlcnZpY2VcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGVBbGwoXCJJRW50aXR5U2VydmljZVwiKVxuICAgICAgICBdXG4gICAgfSk7XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoVGltZUF4aXNSZW5kZXJlcikuYXMoXCJUaW1lQXhpc1JlbmRlcmVyXCIpO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKFNjcm9sbE1hbmFnZXIpLmFzKFwiU2Nyb2xsTWFuYWdlclwiKTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShIZWFkZXJEcmF3ZXJNYW5hZ2VyKS5hcyhcIkhlYWRlckRyYXdlck1hbmFnZXJcIik7XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoRHJhZ0Ryb3BNYW5hZ2VyKS5hcyhcIkRyYWdEcm9wTWFuYWdlclwiKS5hdXRvV2lyZSh7XG4gICAgICAgIG1hcFJlc29sdmVyczogW1xuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSUV2ZW50QnVzXCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSUdyaWRDb25maWdcIilcbiAgICAgICAgXVxuICAgIH0pO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKEVkZ2VTY3JvbGxNYW5hZ2VyKS5hcyhcIkVkZ2VTY3JvbGxNYW5hZ2VyXCIpLmF1dG9XaXJlKHtcbiAgICAgICAgbWFwUmVzb2x2ZXJzOiBbXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJRXZlbnRCdXNcIilcbiAgICAgICAgXVxuICAgIH0pO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKFJlc2l6ZU1hbmFnZXIpLmFzKFwiUmVzaXplTWFuYWdlclwiKS5hdXRvV2lyZSh7XG4gICAgICAgIG1hcFJlc29sdmVyczogW1xuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSUV2ZW50QnVzXCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSUdyaWRDb25maWdcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJEYXRlU2VydmljZVwiKVxuICAgICAgICBdXG4gICAgfSk7XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoRXZlbnRQZXJzaXN0ZW5jZU1hbmFnZXIpLmFzKFwiRXZlbnRQZXJzaXN0ZW5jZU1hbmFnZXJcIikuYXV0b1dpcmUoe1xuICAgICAgICBtYXBSZXNvbHZlcnM6IFtcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkV2ZW50U2VydmljZVwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIklFdmVudEJ1c1wiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkRhdGVTZXJ2aWNlXCIpXG4gICAgICAgIF1cbiAgICB9KTtcbiAgICAvLyBDYWxlbmRhckFwcCAtIGdlbmJydWdlbGlnIGthbGVuZGVya29tcG9uZW50XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoQ2FsZW5kYXJBcHApLmFzKFwiQ2FsZW5kYXJBcHBcIikuYXV0b1dpcmUoe1xuICAgICAgICBtYXBSZXNvbHZlcnM6IFtcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkNhbGVuZGFyT3JjaGVzdHJhdG9yXCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiVGltZUF4aXNSZW5kZXJlclwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkRhdGVTZXJ2aWNlXCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiU2Nyb2xsTWFuYWdlclwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkhlYWRlckRyYXdlck1hbmFnZXJcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJEcmFnRHJvcE1hbmFnZXJcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJFZGdlU2Nyb2xsTWFuYWdlclwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIlJlc2l6ZU1hbmFnZXJcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJIZWFkZXJEcmF3ZXJSZW5kZXJlclwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkV2ZW50UGVyc2lzdGVuY2VNYW5hZ2VyXCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiU2V0dGluZ3NTZXJ2aWNlXCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiVmlld0NvbmZpZ1NlcnZpY2VcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJRXZlbnRCdXNcIilcbiAgICAgICAgXVxuICAgIH0pO1xuICAgIC8vIERlbW8gYXBwXG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoRGVtb0FwcCkuYXMoXCJEZW1vQXBwXCIpLmF1dG9XaXJlKHtcbiAgICAgICAgbWFwUmVzb2x2ZXJzOiBbXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJbmRleGVkREJDb250ZXh0XCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiRGF0YVNlZWRlclwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkF1ZGl0U2VydmljZVwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkNhbGVuZGFyQXBwXCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiRGF0ZVNlcnZpY2VcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJSZXNvdXJjZVNlcnZpY2VcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJRXZlbnRCdXNcIilcbiAgICAgICAgXVxuICAgIH0pO1xuICAgIHJldHVybiBidWlsZGVyLmJ1aWxkKCk7XG59XG4iLCAiaW1wb3J0IHsgY3JlYXRlQ29udGFpbmVyIH0gZnJvbSAnLi4vQ29tcG9zaXRpb25Sb290JztcbmNvbnN0IGNvbnRhaW5lciA9IGNyZWF0ZUNvbnRhaW5lcigpO1xuY29udGFpbmVyLnJlc29sdmVUeXBlKFwiRGVtb0FwcFwiKS5pbml0KCkuY2F0Y2goY29uc29sZS5lcnJvcik7XG4iXSwKICAibWFwcGluZ3MiOiAiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUE7QUFBQTtBQUFBLEtBQUMsU0FBUyxHQUFFLEdBQUU7QUFBQyxrQkFBVSxPQUFPLFdBQVMsZUFBYSxPQUFPLFNBQU8sT0FBTyxVQUFRLEVBQUUsSUFBRSxjQUFZLE9BQU8sVUFBUSxPQUFPLE1BQUksT0FBTyxDQUFDLEtBQUcsSUFBRSxlQUFhLE9BQU8sYUFBVyxhQUFXLEtBQUcsTUFBTSxRQUFNLEVBQUU7QUFBQSxJQUFDLEVBQUUsU0FBTSxXQUFVO0FBQUM7QUFBYSxVQUFJLElBQUUsS0FBSSxJQUFFLEtBQUksSUFBRSxNQUFLLElBQUUsZUFBYyxJQUFFLFVBQVMsSUFBRSxVQUFTLElBQUUsUUFBTyxJQUFFLE9BQU0sSUFBRSxRQUFPLElBQUUsU0FBUSxJQUFFLFdBQVUsSUFBRSxRQUFPLElBQUUsUUFBTyxJQUFFLGdCQUFlLElBQUUsOEZBQTZGLElBQUUsdUZBQXNGLElBQUUsRUFBQyxNQUFLLE1BQUssVUFBUywyREFBMkQsTUFBTSxHQUFHLEdBQUUsUUFBTyx3RkFBd0YsTUFBTSxHQUFHLEdBQUUsU0FBUSxTQUFTQSxJQUFFO0FBQUMsWUFBSUMsS0FBRSxDQUFDLE1BQUssTUFBSyxNQUFLLElBQUksR0FBRUMsS0FBRUYsS0FBRTtBQUFJLGVBQU0sTUFBSUEsTUFBR0MsSUFBR0MsS0FBRSxNQUFJLEVBQUUsS0FBR0QsR0FBRUMsRUFBQyxLQUFHRCxHQUFF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LGFBQWE7QUFBQSxNQUNoQyxPQUFLLEVBQUUsWUFBWSxlQUFlO0FBQUEsTUFDbEMsT0FBSyxFQUFFLFlBQVkscUJBQXFCO0FBQUEsTUFDeEMsT0FBSyxFQUFFLFlBQVksaUJBQWlCO0FBQUEsTUFDcEMsT0FBSyxFQUFFLFlBQVksbUJBQW1CO0FBQUEsTUFDdEMsT0FBSyxFQUFFLFlBQVksZUFBZTtBQUFBLE1BQ2xDLE9BQUssRUFBRSxZQUFZLHNCQUFzQjtBQUFBLE1BQ3pDLE9BQUssRUFBRSxZQUFZLHlCQUF5QjtBQUFBLE1BQzVDLE9BQUssRUFBRSxZQUFZLGlCQUFpQjtBQUFBLE1BQ3BDLE9BQUssRUFBRSxZQUFZLG1CQUFtQjtBQUFBLE1BQ3RDLE9BQUssRUFBRSxZQUFZLFdBQVc7QUFBQSxJQUNsQztBQUFBLEVBQ0osQ0FBQztBQUVELFVBQVEsYUFBYSxPQUFPLEVBQUUsR0FBRyxTQUFTLEVBQUUsU0FBUztBQUFBLElBQ2pELGNBQWM7QUFBQSxNQUNWLE9BQUssRUFBRSxZQUFZLGtCQUFrQjtBQUFBLE1BQ3JDLE9BQUssRUFBRSxZQUFZLFlBQVk7QUFBQSxNQUMvQixPQUFLLEVBQUUsWUFBWSxjQUFjO0FBQUEsTUFDakMsT0FBSyxFQUFFLFlBQVksYUFBYTtBQUFBLE1BQ2hDLE9BQUssRUFBRSxZQUFZLGFBQWE7QUFBQSxNQUNoQyxPQUFLLEVBQUUsWUFBWSxpQkFBaUI7QUFBQSxNQUNwQyxPQUFLLEVBQUUsWUFBWSxXQUFXO0FBQUEsSUFDbEM7QUFBQSxFQUNKLENBQUM7QUFDRCxTQUFPLFFBQVEsTUFBTTtBQUN6QjtBQXZWZ0I7OztBQ3pFaEIsSUFBTSxZQUFZLGdCQUFnQjtBQUNsQyxVQUFVLFlBQVksU0FBUyxFQUFFLEtBQUssRUFBRSxNQUFNLFFBQVEsS0FBSzsiLAogICJuYW1lcyI6IFsidCIsICJlIiwgIm4iLCAiciIsICJpIiwgInMiLCAidSIsICJhIiwgIk0iLCAibSIsICJmIiwgImwiLCAiJCIsICJ5IiwgInYiLCAiZyIsICJEIiwgIm8iLCAiZCIsICJjIiwgImgiLCAidCIsICJpIiwgImUiLCAicyIsICJmIiwgIm4iLCAidSIsICJyIiwgIm8iLCAidCIsICJuIiwgImkiLCAibyIsICJyIiwgImUiLCAidSIsICJmIiwgInMiLCAiYSIsICJ0IiwgImkiLCAiZCIsICJuIiwgImUiLCAicyIsICJ0b2tlbiIsICJjb250YWluZXIiLCAiY29udGFpbmVyIiwgInRva2VuIiwgImNvbnRhaW5lciIsICJkYXlqcyIsICJ1dGMiLCAidGltZXpvbmUiLCAiaXNvV2VlayIsICJjb250YWluZXIiLCAiY29udGFpbmVyIiwgImNvbnRhaW5lciIsICJjb250YWluZXIiLCAiY29udGFpbmVyIiwgImNvbnRhaW5lciIsICJnZXRLZXkiLCAic2tpcFBhdGgiLCAia2V5IiwgImNvbnRhaW5lciIsICJjb250YWluZXIiLCAiY29udGFpbmVyIiwgImRpZmYiLCAiY29udGFpbmVyIiwgImNvbnRhaW5lciIsICJkaWZmIiwgImNvbnRhaW5lciJdCn0K diff --git a/wwwroot/js/edge-scroll.js b/wwwroot/js/edge-scroll.js new file mode 100644 index 0000000..e8b0198 --- /dev/null +++ b/wwwroot/js/edge-scroll.js @@ -0,0 +1,104 @@ +// edge-scroll.js - med timeout + tidsbaseret scroll +(function() { + 'use strict'; + + const OUTER_ZONE = 100; // px fra kant (langsom zone) + const INNER_ZONE = 50; // px fra kant (hurtig zone) + const SLOW_SPEED_PXS = 800; // px/sek i outer zone + const FAST_SPEED_PXS = 2400; // px/sek i inner zone + + let scrollableContent = null; + let scrollRAF = null; + let mouseY = 0; + let haveMouse = false; + let lastTs = 0; + let rect = null; + + function init() { + console.log('edge-scroll.js: waiting 1000ms before setup...'); + setTimeout(setup, 1000); + } + + function setup() { + console.log('edge-scroll.js: setup() called'); + + scrollableContent = document.querySelector('swp-scrollable-content'); + if (!scrollableContent) { + console.error('edge-scroll.js: swp-scrollable-content NOT FOUND'); + return; + } + + console.log('edge-scroll.js: found scrollableContent:', scrollableContent); + + // slå smooth scroll fra, så autoscroll er øjeblikkelig + scrollableContent.style.scrollBehavior = 'auto'; + + scrollableContent.addEventListener('mousemove', handleMouseMove, { passive: true }); + scrollableContent.addEventListener('mouseleave', handleMouseLeave, { passive: true }); + + console.log('edge-scroll.js: ✅ listeners attached'); + } + + function handleMouseMove(e) { + haveMouse = true; + mouseY = e.clientY; + if (scrollRAF == null) { + lastTs = performance.now(); + scrollRAF = requestAnimationFrame(scrollTick); + } + } + + function handleMouseLeave() { + haveMouse = false; + stopScrolling(); + } + + function stopScrolling() { + if (scrollRAF != null) { + cancelAnimationFrame(scrollRAF); + scrollRAF = null; + } + lastTs = 0; + } + + function scrollTick(ts) { + const dt = lastTs ? (ts - lastTs) / 1000 : 0; + lastTs = ts; + + if (!rect) rect = scrollableContent.getBoundingClientRect(); + + let vy = 0; + if (haveMouse) { + const distTop = mouseY - rect.top; + const distBot = rect.bottom - mouseY; + + // Check top edge + if (distTop < INNER_ZONE) { + // Inner zone (0-50px) - fast speed + vy = -FAST_SPEED_PXS; + } else if (distTop < OUTER_ZONE) { + // Outer zone (50-100px) - slow speed + vy = -SLOW_SPEED_PXS; + } + // Check bottom edge + else if (distBot < INNER_ZONE) { + // Inner zone (0-50px) - fast speed + vy = FAST_SPEED_PXS; + } else if (distBot < OUTER_ZONE) { + // Outer zone (50-100px) - slow speed + vy = SLOW_SPEED_PXS; + } + } + + if (vy !== 0) { + scrollableContent.scrollTop += vy * dt; + rect = null; // mål kun én gang pr. frame + scrollRAF = requestAnimationFrame(scrollTick); + } else { + stopScrolling(); + } + } + + // start init + init(); +})(); diff --git a/wwwroot/js/elements/SwpEventElement.d.ts b/wwwroot/js/elements/SwpEventElement.d.ts new file mode 100644 index 0000000..fc38ac2 --- /dev/null +++ b/wwwroot/js/elements/SwpEventElement.d.ts @@ -0,0 +1,98 @@ +import { ICalendarEvent } from '../types/CalendarTypes'; +import { Configuration } from '../configurations/CalendarConfig'; +import { DateService } from '../utils/DateService'; +/** + * Base class for event elements + */ +export declare abstract class BaseSwpEventElement extends HTMLElement { + protected dateService: DateService; + protected config: Configuration; + constructor(); + /** + * Create a clone for drag operations + * Must be implemented by subclasses + */ + abstract createClone(): HTMLElement; + get eventId(): string; + set eventId(value: string); + get start(): Date; + set start(value: Date); + get end(): Date; + set end(value: Date); + get title(): string; + set title(value: string); + get description(): string; + set description(value: string); + get type(): string; + set type(value: string); +} +/** + * Web Component for timed calendar events (Light DOM) + */ +export declare class SwpEventElement extends BaseSwpEventElement { + /** + * Observed attributes - changes trigger attributeChangedCallback + */ + static get observedAttributes(): string[]; + /** + * Called when element is added to DOM + */ + connectedCallback(): void; + /** + * Called when observed attribute changes + */ + attributeChangedCallback(name: string, oldValue: string, newValue: string): void; + /** + * Update event position during drag + * @param columnDate - The date of the column + * @param snappedY - The Y position in pixels + */ + updatePosition(columnDate: Date, snappedY: number): void; + /** + * Update event height during resize + * @param newHeight - The new height in pixels + */ + updateHeight(newHeight: number): void; + /** + * Create a clone for drag operations + */ + createClone(): SwpEventElement; + /** + * Render inner HTML structure + */ + private render; + /** + * Update time display when attributes change + */ + private updateDisplay; + /** + * Calculate start/end minutes from Y position + */ + private calculateTimesFromPosition; + /** + * Create SwpEventElement from ICalendarEvent + */ + static fromCalendarEvent(event: ICalendarEvent): SwpEventElement; + /** + * Extract ICalendarEvent from DOM element + */ + static extractCalendarEventFromElement(element: HTMLElement): ICalendarEvent; +} +/** + * Web Component for all-day calendar events + */ +export declare class SwpAllDayEventElement extends BaseSwpEventElement { + connectedCallback(): void; + /** + * Create a clone for drag operations + */ + createClone(): SwpAllDayEventElement; + /** + * Apply CSS grid positioning + */ + applyGridPositioning(row: number, startColumn: number, endColumn: number): void; + /** + * Create from ICalendarEvent + */ + static fromCalendarEvent(event: ICalendarEvent): SwpAllDayEventElement; +} diff --git a/wwwroot/js/elements/SwpEventElement.js b/wwwroot/js/elements/SwpEventElement.js new file mode 100644 index 0000000..96c188f --- /dev/null +++ b/wwwroot/js/elements/SwpEventElement.js @@ -0,0 +1,303 @@ +import { Configuration } from '../configurations/CalendarConfig'; +import { TimeFormatter } from '../utils/TimeFormatter'; +import { DateService } from '../utils/DateService'; +/** + * Base class for event elements + */ +export class BaseSwpEventElement extends HTMLElement { + constructor() { + super(); + // Get singleton instance for web components (can't use DI) + this.config = Configuration.getInstance(); + this.dateService = new DateService(this.config); + } + // ============================================ + // Common Getters/Setters + // ============================================ + get eventId() { + return this.dataset.eventId || ''; + } + set eventId(value) { + this.dataset.eventId = value; + } + get start() { + return new Date(this.dataset.start || ''); + } + set start(value) { + this.dataset.start = this.dateService.toUTC(value); + } + get end() { + return new Date(this.dataset.end || ''); + } + set end(value) { + this.dataset.end = this.dateService.toUTC(value); + } + get title() { + return this.dataset.title || ''; + } + set title(value) { + this.dataset.title = value; + } + get description() { + return this.dataset.description || ''; + } + set description(value) { + this.dataset.description = value; + } + get type() { + return this.dataset.type || 'work'; + } + set type(value) { + this.dataset.type = value; + } +} +/** + * Web Component for timed calendar events (Light DOM) + */ +export class SwpEventElement extends BaseSwpEventElement { + /** + * Observed attributes - changes trigger attributeChangedCallback + */ + static get observedAttributes() { + return ['data-start', 'data-end', 'data-title', 'data-description', 'data-type']; + } + /** + * Called when element is added to DOM + */ + connectedCallback() { + if (!this.hasChildNodes()) { + this.render(); + } + } + /** + * Called when observed attribute changes + */ + attributeChangedCallback(name, oldValue, newValue) { + if (oldValue !== newValue && this.isConnected) { + this.updateDisplay(); + } + } + // ============================================ + // Public Methods + // ============================================ + /** + * Update event position during drag + * @param columnDate - The date of the column + * @param snappedY - The Y position in pixels + */ + updatePosition(columnDate, snappedY) { + // 1. Update visual position + this.style.top = `${snappedY + 1}px`; + // 2. Calculate new timestamps + const { startMinutes, endMinutes } = this.calculateTimesFromPosition(snappedY); + // 3. Update data attributes (triggers attributeChangedCallback) + const startDate = this.dateService.createDateAtTime(columnDate, startMinutes); + let endDate = this.dateService.createDateAtTime(columnDate, endMinutes); + // Handle cross-midnight events + if (endMinutes >= 1440) { + const extraDays = Math.floor(endMinutes / 1440); + endDate = this.dateService.addDays(endDate, extraDays); + } + this.start = startDate; + this.end = endDate; + } + /** + * Update event height during resize + * @param newHeight - The new height in pixels + */ + updateHeight(newHeight) { + // 1. Update visual height + this.style.height = `${newHeight}px`; + // 2. Calculate new end time based on height + const gridSettings = this.config.gridSettings; + const { hourHeight, snapInterval } = gridSettings; + // Get current start time + const start = this.start; + // Calculate duration from height + const rawDurationMinutes = (newHeight / hourHeight) * 60; + // Snap duration to grid interval (like drag & drop) + const snappedDurationMinutes = Math.round(rawDurationMinutes / snapInterval) * snapInterval; + // Calculate new end time by adding snapped duration to start (using DateService for timezone safety) + const endDate = this.dateService.addMinutes(start, snappedDurationMinutes); + // 3. Update end attribute (triggers attributeChangedCallback → updateDisplay) + this.end = endDate; + } + /** + * Create a clone for drag operations + */ + createClone() { + const clone = this.cloneNode(true); + // Apply "clone-" prefix to ID + clone.dataset.eventId = `clone-${this.eventId}`; + // Disable pointer events on clone so it doesn't interfere with hover detection + clone.style.pointerEvents = 'none'; + // Cache original duration + const timeEl = this.querySelector('swp-event-time'); + if (timeEl) { + const duration = timeEl.getAttribute('data-duration'); + if (duration) { + clone.dataset.originalDuration = duration; + } + } + // Set height from original + clone.style.height = this.style.height || `${this.getBoundingClientRect().height}px`; + return clone; + } + // ============================================ + // Private Methods + // ============================================ + /** + * Render inner HTML structure + */ + render() { + const start = this.start; + const end = this.end; + const timeRange = TimeFormatter.formatTimeRange(start, end); + const durationMinutes = (end.getTime() - start.getTime()) / (1000 * 60); + this.innerHTML = ` + ${timeRange} + ${this.title} + ${this.description ? `${this.description}` : ''} + `; + } + /** + * Update time display when attributes change + */ + updateDisplay() { + const timeEl = this.querySelector('swp-event-time'); + const titleEl = this.querySelector('swp-event-title'); + const descEl = this.querySelector('swp-event-description'); + if (timeEl && this.dataset.start && this.dataset.end) { + const start = new Date(this.dataset.start); + const end = new Date(this.dataset.end); + const timeRange = TimeFormatter.formatTimeRange(start, end); + timeEl.textContent = timeRange; + // Update duration attribute + const durationMinutes = (end.getTime() - start.getTime()) / (1000 * 60); + timeEl.setAttribute('data-duration', durationMinutes.toString()); + } + if (titleEl && this.dataset.title) { + titleEl.textContent = this.dataset.title; + } + if (this.dataset.description) { + if (descEl) { + descEl.textContent = this.dataset.description; + } + else if (this.description) { + // Add description element if it doesn't exist + const newDescEl = document.createElement('swp-event-description'); + newDescEl.textContent = this.description; + this.appendChild(newDescEl); + } + } + else if (descEl) { + // Remove description element if description is empty + descEl.remove(); + } + } + /** + * Calculate start/end minutes from Y position + */ + calculateTimesFromPosition(snappedY) { + const gridSettings = this.config.gridSettings; + const { hourHeight, dayStartHour, snapInterval } = gridSettings; + // Get original duration + const originalDuration = parseInt(this.dataset.originalDuration || + this.dataset.duration || + '60'); + // Calculate snapped start minutes + const minutesFromGridStart = (snappedY / hourHeight) * 60; + const actualStartMinutes = (dayStartHour * 60) + minutesFromGridStart; + const snappedStartMinutes = Math.round(actualStartMinutes / snapInterval) * snapInterval; + // Calculate end minutes + const endMinutes = snappedStartMinutes + originalDuration; + return { startMinutes: snappedStartMinutes, endMinutes }; + } + // ============================================ + // Static Factory Methods + // ============================================ + /** + * Create SwpEventElement from ICalendarEvent + */ + static fromCalendarEvent(event) { + const element = document.createElement('swp-event'); + const config = Configuration.getInstance(); + const dateService = new DateService(config); + element.dataset.eventId = event.id; + element.dataset.title = event.title; + element.dataset.description = event.description || ''; + element.dataset.start = dateService.toUTC(event.start); + element.dataset.end = dateService.toUTC(event.end); + element.dataset.type = event.type; + element.dataset.duration = event.metadata?.duration?.toString() || '60'; + return element; + } + /** + * Extract ICalendarEvent from DOM element + */ + static extractCalendarEventFromElement(element) { + return { + id: element.dataset.eventId || '', + title: element.dataset.title || '', + description: element.dataset.description || undefined, + start: new Date(element.dataset.start || ''), + end: new Date(element.dataset.end || ''), + type: element.dataset.type || 'work', + allDay: false, + syncStatus: 'synced', + metadata: { + duration: element.dataset.duration + } + }; + } +} +/** + * Web Component for all-day calendar events + */ +export class SwpAllDayEventElement extends BaseSwpEventElement { + connectedCallback() { + if (!this.textContent) { + this.textContent = this.dataset.title || 'Untitled'; + } + } + /** + * Create a clone for drag operations + */ + createClone() { + const clone = this.cloneNode(true); + // Apply "clone-" prefix to ID + clone.dataset.eventId = `clone-${this.eventId}`; + // Disable pointer events on clone so it doesn't interfere with hover detection + clone.style.pointerEvents = 'none'; + // Preserve full opacity during drag + clone.style.opacity = '1'; + return clone; + } + /** + * Apply CSS grid positioning + */ + applyGridPositioning(row, startColumn, endColumn) { + const gridArea = `${row} / ${startColumn} / ${row + 1} / ${endColumn + 1}`; + this.style.gridArea = gridArea; + } + /** + * Create from ICalendarEvent + */ + static fromCalendarEvent(event) { + const element = document.createElement('swp-allday-event'); + const config = Configuration.getInstance(); + const dateService = new DateService(config); + element.dataset.eventId = event.id; + element.dataset.title = event.title; + element.dataset.start = dateService.toUTC(event.start); + element.dataset.end = dateService.toUTC(event.end); + element.dataset.type = event.type; + element.dataset.allday = 'true'; + element.textContent = event.title; + return element; + } +} +// Register custom elements +customElements.define('swp-event', SwpEventElement); +customElements.define('swp-allday-event', SwpAllDayEventElement); +//# sourceMappingURL=SwpEventElement.js.map \ No newline at end of file diff --git a/wwwroot/js/elements/SwpEventElement.js.map b/wwwroot/js/elements/SwpEventElement.js.map new file mode 100644 index 0000000..e05d269 --- /dev/null +++ b/wwwroot/js/elements/SwpEventElement.js.map @@ -0,0 +1 @@ +{"version":3,"file":"SwpEventElement.js","sourceRoot":"","sources":["../../../src/elements/SwpEventElement.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/factories/CalendarTypeFactory.d.ts b/wwwroot/js/factories/CalendarTypeFactory.d.ts new file mode 100644 index 0000000..2c71862 --- /dev/null +++ b/wwwroot/js/factories/CalendarTypeFactory.d.ts @@ -0,0 +1,55 @@ +import { CalendarMode } from '../types/CalendarTypes'; +import { HeaderRenderer } from '../renderers/HeaderRenderer'; +import { ColumnRenderer } from '../renderers/ColumnRenderer'; +import { EventRendererStrategy } from '../renderers/EventRenderer'; +/** + * Renderer configuration for a calendar type + */ +export interface RendererConfig { + headerRenderer: HeaderRenderer; + columnRenderer: ColumnRenderer; + eventRenderer: EventRendererStrategy; +} +/** + * Factory for creating calendar type-specific renderers + */ +export declare class CalendarTypeFactory { + private static renderers; + private static isInitialized; + /** + * Initialize the factory with default renderers (only runs once) + */ + static initialize(): void; + /** + * Register renderers for a calendar type + */ + static registerRenderers(type: CalendarMode, config: RendererConfig): void; + /** + * Get renderers for a calendar type + */ + static getRenderers(type: CalendarMode): RendererConfig; + /** + * Get header renderer for a calendar type + */ + static getHeaderRenderer(type: CalendarMode): HeaderRenderer; + /** + * Get column renderer for a calendar type + */ + static getColumnRenderer(type: CalendarMode): ColumnRenderer; + /** + * Get event renderer for a calendar type + */ + static getEventRenderer(type: CalendarMode): EventRendererStrategy; + /** + * Check if a calendar type is supported + */ + static isSupported(type: CalendarMode): boolean; + /** + * Get all supported calendar types + */ + static getSupportedTypes(): CalendarMode[]; + /** + * Clear all registered renderers (useful for testing) + */ + static clear(): void; +} diff --git a/wwwroot/js/factories/CalendarTypeFactory.js b/wwwroot/js/factories/CalendarTypeFactory.js new file mode 100644 index 0000000..98bb1ca --- /dev/null +++ b/wwwroot/js/factories/CalendarTypeFactory.js @@ -0,0 +1,84 @@ +// Factory for creating calendar type-specific renderers +import { DateHeaderRenderer, ResourceHeaderRenderer } from '../renderers/HeaderRenderer'; +import { DateColumnRenderer, ResourceColumnRenderer } from '../renderers/ColumnRenderer'; +import { DateEventRenderer, ResourceEventRenderer } from '../renderers/EventRenderer'; +/** + * Factory for creating calendar type-specific renderers + */ +export class CalendarTypeFactory { + /** + * Initialize the factory with default renderers (only runs once) + */ + static initialize() { + if (this.isInitialized) { + return; + } + // Register default renderers + this.registerRenderers('date', { + headerRenderer: new DateHeaderRenderer(), + columnRenderer: new DateColumnRenderer(), + eventRenderer: new DateEventRenderer() + }); + this.registerRenderers('resource', { + headerRenderer: new ResourceHeaderRenderer(), + columnRenderer: new ResourceColumnRenderer(), + eventRenderer: new ResourceEventRenderer() + }); + this.isInitialized = true; + } + /** + * Register renderers for a calendar type + */ + static registerRenderers(type, config) { + this.renderers.set(type, config); + } + /** + * Get renderers for a calendar type + */ + static getRenderers(type) { + const renderers = this.renderers.get(type); + if (!renderers) { + return this.renderers.get('date'); + } + return renderers; + } + /** + * Get header renderer for a calendar type + */ + static getHeaderRenderer(type) { + return this.getRenderers(type).headerRenderer; + } + /** + * Get column renderer for a calendar type + */ + static getColumnRenderer(type) { + return this.getRenderers(type).columnRenderer; + } + /** + * Get event renderer for a calendar type + */ + static getEventRenderer(type) { + return this.getRenderers(type).eventRenderer; + } + /** + * Check if a calendar type is supported + */ + static isSupported(type) { + return this.renderers.has(type); + } + /** + * Get all supported calendar types + */ + static getSupportedTypes() { + return Array.from(this.renderers.keys()); + } + /** + * Clear all registered renderers (useful for testing) + */ + static clear() { + this.renderers.clear(); + } +} +CalendarTypeFactory.renderers = new Map(); +CalendarTypeFactory.isInitialized = false; +//# sourceMappingURL=CalendarTypeFactory.js.map \ No newline at end of file diff --git a/wwwroot/js/factories/CalendarTypeFactory.js.map b/wwwroot/js/factories/CalendarTypeFactory.js.map new file mode 100644 index 0000000..a1d85d8 --- /dev/null +++ b/wwwroot/js/factories/CalendarTypeFactory.js.map @@ -0,0 +1 @@ +{"version":3,"file":"CalendarTypeFactory.js","sourceRoot":"","sources":["../../../src/factories/CalendarTypeFactory.ts"],"names":[],"mappings":"AAAA,wDAAwD;AAGxD,OAAO,EAAkB,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACzG,OAAO,EAAkB,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACzG,OAAO,EAAyB,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAY7G;;GAEG;AACH,MAAM,OAAO,mBAAmB;IAI9B;;OAEG;IACH,MAAM,CAAC,UAAU;QACf,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,OAAO;QACT,CAAC;QAED,6BAA6B;QAC7B,IAAI,CAAC,iBAAiB,CAAC,MAAM,EAAE;YAC7B,cAAc,EAAE,IAAI,kBAAkB,EAAE;YACxC,cAAc,EAAE,IAAI,kBAAkB,EAAE;YACxC,aAAa,EAAE,IAAI,iBAAiB,EAAE;SACvC,CAAC,CAAC;QAEH,IAAI,CAAC,iBAAiB,CAAC,UAAU,EAAE;YACjC,cAAc,EAAE,IAAI,sBAAsB,EAAE;YAC5C,cAAc,EAAE,IAAI,sBAAsB,EAAE;YAC5C,aAAa,EAAE,IAAI,qBAAqB,EAAE;SAC3C,CAAC,CAAC;QAEH,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;IAC5B,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,iBAAiB,CAAC,IAAkB,EAAE,MAAsB;QACjE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACnC,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,YAAY,CAAC,IAAkB;QACpC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAE3C,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAE,CAAC;QACrC,CAAC;QAED,OAAO,SAAS,CAAC;IACnB,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,iBAAiB,CAAC,IAAkB;QACzC,OAAO,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC;IAChD,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,iBAAiB,CAAC,IAAkB;QACzC,OAAO,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC;IAChD,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,gBAAgB,CAAC,IAAkB;QACxC,OAAO,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,aAAa,CAAC;IAC/C,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,WAAW,CAAC,IAAkB;QACnC,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,iBAAiB;QACtB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC;IAC3C,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,KAAK;QACV,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;IACzB,CAAC;;AAvFc,6BAAS,GAAsC,IAAI,GAAG,EAAE,CAAC;AACzD,iCAAa,GAAY,KAAK,CAAC"} \ No newline at end of file diff --git a/wwwroot/js/factories/ManagerFactory.d.ts b/wwwroot/js/factories/ManagerFactory.d.ts new file mode 100644 index 0000000..2f9edb6 --- /dev/null +++ b/wwwroot/js/factories/ManagerFactory.d.ts @@ -0,0 +1,18 @@ +import { IEventBus } from '../types/CalendarTypes'; +import { CalendarManagers } from '../types/ManagerTypes'; +/** + * Factory for creating and managing calendar managers with proper dependency injection + */ +export declare class ManagerFactory { + private static instance; + private constructor(); + static getInstance(): ManagerFactory; + /** + * Create all managers with proper dependency injection + */ + createManagers(eventBus: IEventBus): CalendarManagers; + /** + * Initialize all managers in the correct order + */ + initializeManagers(managers: CalendarManagers): Promise; +} diff --git a/wwwroot/js/factories/ManagerFactory.js b/wwwroot/js/factories/ManagerFactory.js new file mode 100644 index 0000000..14636be --- /dev/null +++ b/wwwroot/js/factories/ManagerFactory.js @@ -0,0 +1,60 @@ +import { EventManager } from '../managers/EventManager'; +import { EventRenderingService } from '../renderers/EventRendererManager'; +import { GridManager } from '../managers/GridManager'; +import { ScrollManager } from '../managers/ScrollManager'; +import { NavigationManager } from '../managers/NavigationManager'; +import { ViewManager } from '../managers/ViewManager'; +import { CalendarManager } from '../managers/CalendarManager'; +import { DragDropManager } from '../managers/DragDropManager'; +import { AllDayManager } from '../managers/AllDayManager'; +/** + * Factory for creating and managing calendar managers with proper dependency injection + */ +export class ManagerFactory { + constructor() { } + static getInstance() { + if (!ManagerFactory.instance) { + ManagerFactory.instance = new ManagerFactory(); + } + return ManagerFactory.instance; + } + /** + * Create all managers with proper dependency injection + */ + createManagers(eventBus) { + // Create managers in dependency order + const eventManager = new EventManager(eventBus); + const eventRenderer = new EventRenderingService(eventBus, eventManager); + const gridManager = new GridManager(); + const scrollManager = new ScrollManager(); + const navigationManager = new NavigationManager(eventBus, eventRenderer); + const viewManager = new ViewManager(eventBus); + const dragDropManager = new DragDropManager(eventBus); + const allDayManager = new AllDayManager(); + // CalendarManager depends on all other managers + const calendarManager = new CalendarManager(eventBus, eventManager, gridManager, eventRenderer, scrollManager); + return { + eventManager, + eventRenderer, + gridManager, + scrollManager, + navigationManager, + viewManager, + calendarManager, + dragDropManager, + allDayManager + }; + } + /** + * Initialize all managers in the correct order + */ + async initializeManagers(managers) { + try { + await managers.calendarManager.initialize?.(); + } + catch (error) { + throw error; + } + } +} +//# sourceMappingURL=ManagerFactory.js.map \ No newline at end of file diff --git a/wwwroot/js/factories/ManagerFactory.js.map b/wwwroot/js/factories/ManagerFactory.js.map new file mode 100644 index 0000000..e05ff59 --- /dev/null +++ b/wwwroot/js/factories/ManagerFactory.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ManagerFactory.js","sourceRoot":"","sources":["../../../src/factories/ManagerFactory.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAG1D;;GAEG;AACH,MAAM,OAAO,cAAc;IAGzB,gBAAuB,CAAC;IAEjB,MAAM,CAAC,WAAW;QACvB,IAAI,CAAC,cAAc,CAAC,QAAQ,EAAE,CAAC;YAC7B,cAAc,CAAC,QAAQ,GAAG,IAAI,cAAc,EAAE,CAAC;QACjD,CAAC;QACD,OAAO,cAAc,CAAC,QAAQ,CAAC;IACjC,CAAC;IAED;;OAEG;IACI,cAAc,CAAC,QAAmB;QAEvC,sCAAsC;QACtC,MAAM,YAAY,GAAG,IAAI,YAAY,CAAC,QAAQ,CAAC,CAAC;QAChD,MAAM,aAAa,GAAG,IAAI,qBAAqB,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;QACxE,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC;QACtC,MAAM,aAAa,GAAG,IAAI,aAAa,EAAE,CAAC;QAC1C,MAAM,iBAAiB,GAAG,IAAI,iBAAiB,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;QACzE,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC,QAAQ,CAAC,CAAC;QAC9C,MAAM,eAAe,GAAG,IAAI,eAAe,CAAC,QAAQ,CAAC,CAAC;QACtD,MAAM,aAAa,GAAG,IAAI,aAAa,EAAE,CAAC;QAE1C,gDAAgD;QAChD,MAAM,eAAe,GAAG,IAAI,eAAe,CACzC,QAAQ,EACR,YAAY,EACZ,WAAW,EACX,aAAa,EACb,aAAa,CACd,CAAC;QAGF,OAAO;YACL,YAAY;YACZ,aAAa;YACb,WAAW;YACX,aAAa;YACb,iBAAiB;YACjB,WAAW;YACX,eAAe;YACf,eAAe;YACf,aAAa;SACd,CAAC;IACJ,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,kBAAkB,CAAC,QAA0B;QAExD,IAAI,CAAC;YACH,MAAM,QAAQ,CAAC,eAAe,CAAC,UAAU,EAAE,EAAE,CAAC;QAChD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;CACF"} \ No newline at end of file diff --git a/wwwroot/js/features/all-day/AllDayCollapseService.d.ts b/wwwroot/js/features/all-day/AllDayCollapseService.d.ts new file mode 100644 index 0000000..10a0dc3 --- /dev/null +++ b/wwwroot/js/features/all-day/AllDayCollapseService.d.ts @@ -0,0 +1,45 @@ +/** + * AllDayCollapseService - Manages collapse/expand UI for all-day events + * + * STATELESS SERVICE - Reads expanded state from DOM via AllDayDomReader + * - No persistent state + * - Reads expanded state from DOM CSS class + * - Updates chevron button and overflow indicators + * - Controls event visibility based on row number + */ +import { AllDayHeightService } from './AllDayHeightService'; +export declare class AllDayCollapseService { + private heightService; + constructor(heightService: AllDayHeightService); + /** + * Toggle between expanded and collapsed state + * Reads current state from DOM, toggles it, and updates UI + */ + toggleExpanded(): void; + /** + * Update all UI elements based on current DOM state + */ + private updateUI; + /** + * Update event visibility based on expanded state + */ + private updateEventVisibility; + /** + * Update chevron button visibility and state + */ + private updateChevronButton; + /** + * Update overflow indicators for collapsed state + * Shows "+X more" indicators in columns with overflow + */ + private updateOverflowIndicators; + /** + * Clear all overflow indicators + */ + private clearOverflowIndicators; + /** + * Initialize collapse/expand UI based on current DOM state + * Called after events are rendered + */ + initializeUI(): void; +} diff --git a/wwwroot/js/features/all-day/AllDayCollapseService.js b/wwwroot/js/features/all-day/AllDayCollapseService.js new file mode 100644 index 0000000..fa9036f --- /dev/null +++ b/wwwroot/js/features/all-day/AllDayCollapseService.js @@ -0,0 +1,168 @@ +/** + * AllDayCollapseService - Manages collapse/expand UI for all-day events + * + * STATELESS SERVICE - Reads expanded state from DOM via AllDayDomReader + * - No persistent state + * - Reads expanded state from DOM CSS class + * - Updates chevron button and overflow indicators + * - Controls event visibility based on row number + */ +import { ALL_DAY_CONSTANTS } from '../../configurations/CalendarConfig'; +import { ColumnDetectionUtils } from '../../utils/ColumnDetectionUtils'; +import { AllDayDomReader } from './AllDayDomReader'; +export class AllDayCollapseService { + constructor(heightService) { + this.heightService = heightService; + } + /** + * Toggle between expanded and collapsed state + * Reads current state from DOM, toggles it, and updates UI + */ + toggleExpanded() { + const container = AllDayDomReader.getAllDayContainer(); + if (!container) + return; + // Read current state from DOM + const isCurrentlyExpanded = container.classList.contains('expanded'); + // Toggle state in DOM + if (isCurrentlyExpanded) { + container.classList.remove('expanded'); + } + else { + container.classList.add('expanded'); + } + // Update UI based on new state + this.updateUI(); + } + /** + * Update all UI elements based on current DOM state + */ + updateUI() { + const isExpanded = AllDayDomReader.isExpanded(); + const maxRows = AllDayDomReader.getMaxRowFromEvents(); + // Update chevron button + if (maxRows > ALL_DAY_CONSTANTS.MAX_COLLAPSED_ROWS) { + this.updateChevronButton(true, isExpanded); + if (isExpanded) { + this.clearOverflowIndicators(); + } + else { + this.updateOverflowIndicators(); + } + } + else { + this.updateChevronButton(false, isExpanded); + this.clearOverflowIndicators(); + } + // Update event visibility + this.updateEventVisibility(isExpanded); + // Calculate height based on expanded state + // When collapsed, show max MAX_COLLAPSED_ROWS, when expanded show all rows + const targetRows = isExpanded ? maxRows : Math.min(maxRows, ALL_DAY_CONSTANTS.MAX_COLLAPSED_ROWS); + this.heightService.animateToRows(targetRows); + } + /** + * Update event visibility based on expanded state + */ + updateEventVisibility(isExpanded) { + const events = AllDayDomReader.getEventElements(); + events.forEach(event => { + const row = AllDayDomReader.getGridRow(event); + if (row > ALL_DAY_CONSTANTS.MAX_COLLAPSED_ROWS) { + if (isExpanded) { + event.classList.remove('max-event-overflow-hide'); + event.classList.add('max-event-overflow-show'); + } + else { + event.classList.remove('max-event-overflow-show'); + event.classList.add('max-event-overflow-hide'); + } + } + }); + } + /** + * Update chevron button visibility and state + */ + updateChevronButton(show, isExpanded) { + const headerSpacer = AllDayDomReader.getHeaderSpacer(); + if (!headerSpacer) + return; + let chevron = headerSpacer.querySelector('.allday-chevron'); + if (show && !chevron) { + // Create chevron button + chevron = document.createElement('button'); + chevron.className = 'allday-chevron collapsed'; + chevron.innerHTML = ` + + + + `; + chevron.onclick = () => this.toggleExpanded(); + headerSpacer.appendChild(chevron); + } + else if (!show && chevron) { + // Remove chevron button + chevron.remove(); + } + else if (chevron) { + // Update chevron state + chevron.classList.toggle('collapsed', !isExpanded); + chevron.classList.toggle('expanded', isExpanded); + } + } + /** + * Update overflow indicators for collapsed state + * Shows "+X more" indicators in columns with overflow + */ + updateOverflowIndicators() { + const container = AllDayDomReader.getAllDayContainer(); + if (!container) + return; + const columns = ColumnDetectionUtils.getColumns(); + columns.forEach((columnBounds) => { + const totalEventsInColumn = AllDayDomReader.countEventsInColumn(columnBounds.index); + const overflowCount = totalEventsInColumn - ALL_DAY_CONSTANTS.MAX_COLLAPSED_ROWS; + if (overflowCount > 0) { + // Check if indicator already exists + let existingIndicator = container.querySelector(`.max-event-indicator[data-column="${columnBounds.index}"]`); + if (existingIndicator) { + // Update existing indicator + existingIndicator.innerHTML = `+${overflowCount + 1} more`; + } + else { + // Create new overflow indicator + const overflowElement = document.createElement('swp-allday-event'); + overflowElement.className = 'max-event-indicator'; + overflowElement.setAttribute('data-column', columnBounds.index.toString()); + overflowElement.style.gridRow = ALL_DAY_CONSTANTS.MAX_COLLAPSED_ROWS.toString(); + overflowElement.style.gridColumn = columnBounds.index.toString(); + overflowElement.innerHTML = `+${overflowCount + 1} more`; + overflowElement.onclick = (e) => { + e.stopPropagation(); + this.toggleExpanded(); + }; + container.appendChild(overflowElement); + } + } + }); + } + /** + * Clear all overflow indicators + */ + clearOverflowIndicators() { + const container = AllDayDomReader.getAllDayContainer(); + if (!container) + return; + container.querySelectorAll('.max-event-indicator').forEach((element) => { + element.remove(); + }); + } + /** + * Initialize collapse/expand UI based on current DOM state + * Called after events are rendered + */ + initializeUI() { + this.updateUI(); + } +} +//# sourceMappingURL=AllDayCollapseService.js.map \ No newline at end of file diff --git a/wwwroot/js/features/all-day/AllDayCollapseService.js.map b/wwwroot/js/features/all-day/AllDayCollapseService.js.map new file mode 100644 index 0000000..188222f --- /dev/null +++ b/wwwroot/js/features/all-day/AllDayCollapseService.js.map @@ -0,0 +1 @@ +{"version":3,"file":"AllDayCollapseService.js","sourceRoot":"","sources":["../../../../src/features/all-day/AllDayCollapseService.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/features/all-day/AllDayCoordinator.d.ts b/wwwroot/js/features/all-day/AllDayCoordinator.d.ts new file mode 100644 index 0000000..992a8a3 --- /dev/null +++ b/wwwroot/js/features/all-day/AllDayCoordinator.d.ts @@ -0,0 +1,45 @@ +/** + * AllDayCoordinator - Orchestrates all-day event functionality + * + * NO STATE - Only coordinates between services + * - Listens to EventBus events + * - Delegates to specialized services + * - Manages service lifecycle + */ +import { AllDayEventRenderer } from '../../renderers/AllDayEventRenderer'; +import { EventManager } from '../../managers/EventManager'; +import { DateService } from '../../utils/DateService'; +import { AllDayHeightService } from './AllDayHeightService'; +import { AllDayCollapseService } from './AllDayCollapseService'; +import { AllDayDragService } from './AllDayDragService'; +/** + * AllDayCoordinator - Orchestrates all-day event functionality + * Replaces the monolithic AllDayManager with a coordinated service architecture + */ +export declare class AllDayCoordinator { + private allDayEventRenderer; + private eventManager; + private dateService; + private heightService; + private collapseService; + private dragService; + constructor(eventManager: EventManager, allDayEventRenderer: AllDayEventRenderer, dateService: DateService, heightService: AllDayHeightService, collapseService: AllDayCollapseService, dragService: AllDayDragService); + /** + * Setup event listeners and delegate to services + */ + private setupEventListeners; + /** + * Calculate layout for ALL all-day events using AllDayLayoutEngine + */ + private calculateAllDayEventsLayout; + /** + * Recalculate layouts and update height + * Called after events are added/removed/moved in all-day area + * Uses AllDayLayoutEngine to optimally reorganize all events + */ + private recalculateLayoutsAndHeight; + /** + * Public API for collapsing all-day row + */ + collapseAllDayRow(): void; +} diff --git a/wwwroot/js/features/all-day/AllDayCoordinator.js b/wwwroot/js/features/all-day/AllDayCoordinator.js new file mode 100644 index 0000000..65dc583 --- /dev/null +++ b/wwwroot/js/features/all-day/AllDayCoordinator.js @@ -0,0 +1,168 @@ +/** + * AllDayCoordinator - Orchestrates all-day event functionality + * + * NO STATE - Only coordinates between services + * - Listens to EventBus events + * - Delegates to specialized services + * - Manages service lifecycle + */ +import { eventBus } from '../../core/EventBus'; +import { ALL_DAY_CONSTANTS } from '../../configurations/CalendarConfig'; +import { AllDayLayoutEngine } from '../../utils/AllDayLayoutEngine'; +import { CoreEvents } from '../../constants/CoreEvents'; +import { AllDayDomReader } from './AllDayDomReader'; +import { ColumnDetectionUtils } from '../../utils/ColumnDetectionUtils'; +/** + * AllDayCoordinator - Orchestrates all-day event functionality + * Replaces the monolithic AllDayManager with a coordinated service architecture + */ +export class AllDayCoordinator { + constructor(eventManager, allDayEventRenderer, dateService, heightService, collapseService, dragService) { + this.eventManager = eventManager; + this.allDayEventRenderer = allDayEventRenderer; + this.dateService = dateService; + this.heightService = heightService; + this.collapseService = collapseService; + this.dragService = dragService; + // Sync CSS variable with TypeScript constant + document.documentElement.style.setProperty('--single-row-height', `${ALL_DAY_CONSTANTS.EVENT_HEIGHT}px`); + this.setupEventListeners(); + } + /** + * Setup event listeners and delegate to services + */ + setupEventListeners() { + // Timed → All-day conversion + eventBus.on('drag:mouseenter-header', (event) => { + const payload = event.detail; + if (payload.draggedClone.hasAttribute('data-allday')) + return; + console.log('🔄 AllDayCoordinator: Received drag:mouseenter-header', { + targetDate: payload.targetColumn, + originalElementId: payload.originalElement?.dataset?.eventId, + originalElementTag: payload.originalElement?.tagName + }); + this.dragService.handleConvertToAllDay(payload); + // Recalculate layouts and height after timed → all-day conversion + this.recalculateLayoutsAndHeight(); + }); + eventBus.on('drag:mouseleave-header', (event) => { + const { originalElement } = event.detail; + console.log('🚪 AllDayCoordinator: Received drag:mouseleave-header', { + originalElementId: originalElement?.dataset?.eventId + }); + }); + // All-day drag start + eventBus.on('drag:start', (event) => { + const payload = event.detail; + if (!payload.draggedClone?.hasAttribute('data-allday')) + return; + this.allDayEventRenderer.handleDragStart(payload); + }); + // All-day column change + eventBus.on('drag:column-change', (event) => { + const payload = event.detail; + if (!payload.draggedClone?.hasAttribute('data-allday')) + return; + this.dragService.handleColumnChange(payload); + }); + // Drag end + eventBus.on('drag:end', (event) => { + const dragEndPayload = event.detail; + console.log('🎯 AllDayCoordinator: drag:end received', { + target: dragEndPayload.target, + originalElementTag: dragEndPayload.originalElement?.tagName, + hasAllDayAttribute: dragEndPayload.originalElement?.hasAttribute('data-allday'), + eventId: dragEndPayload.originalElement?.dataset.eventId + }); + // Handle all-day → all-day drops (within header) + if (dragEndPayload.target === 'swp-day-header') { + console.log('✅ AllDayCoordinator: Handling all-day → all-day drop'); + this.dragService.handleDragEnd(dragEndPayload); + // Recalculate layouts and height after all-day → all-day repositioning + this.recalculateLayoutsAndHeight(); + return; + } + // Handle all-day → timed conversion (dropped in column) + if (dragEndPayload.target === 'swp-day-column' && + dragEndPayload.originalElement?.hasAttribute('data-allday')) { + const eventId = dragEndPayload.originalElement.dataset.eventId; + console.log('🔄 AllDayCoordinator: All-day → timed conversion', { + eventId + }); + // Remove event element from DOM + const container = AllDayDomReader.getAllDayContainer(); + const eventElement = container?.querySelector(`[data-event-id="${eventId}"]`); + if (eventElement) { + eventElement.remove(); + } + // Recalculate layouts and height after event removal + this.recalculateLayoutsAndHeight(); + } + }); + // Drag cancelled + eventBus.on('drag:cancelled', (event) => { + const { draggedElement, reason } = event.detail; + console.log('🚫 AllDayCoordinator: Drag cancelled', { + eventId: draggedElement?.dataset?.eventId, + reason + }); + }); + // Header ready - render all-day events + eventBus.on('header:ready', async (event) => { + const headerReadyEventPayload = event.detail; + const startDate = new Date(headerReadyEventPayload.headerElements.at(0).date); + const endDate = new Date(headerReadyEventPayload.headerElements.at(-1).date); + const events = await this.eventManager.getEventsForPeriod(startDate, endDate); + // Filter for all-day events + const allDayEvents = events.filter(event => event.allDay); + // Calculate layouts + const layouts = this.calculateAllDayEventsLayout(allDayEvents, headerReadyEventPayload.headerElements); + // Render events + this.allDayEventRenderer.renderAllDayEventsForPeriod(layouts); + // Initialize collapse/expand UI and calculate height + this.collapseService.initializeUI(); + }); + // View changed + eventBus.on(CoreEvents.VIEW_CHANGED, (event) => { + this.allDayEventRenderer.handleViewChanged(event); + }); + } + /** + * Calculate layout for ALL all-day events using AllDayLayoutEngine + */ + calculateAllDayEventsLayout(events, dayHeaders) { + // Initialize layout engine with provided week dates + const layoutEngine = new AllDayLayoutEngine(dayHeaders.map(column => column.date)); + // Calculate layout for all events together + return layoutEngine.calculateLayout(events); + } + /** + * Recalculate layouts and update height + * Called after events are added/removed/moved in all-day area + * Uses AllDayLayoutEngine to optimally reorganize all events + */ + recalculateLayoutsAndHeight() { + // 1. Read current events from DOM + const events = AllDayDomReader.getEventsAsData(); + const weekDates = ColumnDetectionUtils.getColumns(); + // 2. Calculate optimal layouts using greedy algorithm + const layouts = this.calculateAllDayEventsLayout(events, weekDates); + // 3. Apply layouts to DOM + this.dragService.applyLayoutUpdates(layouts); + // 4. Calculate max row from NEW layouts + const maxRow = layouts.length > 0 ? Math.max(...layouts.map(l => l.row)) : 0; + // 5. Check if collapsed state should be maintained + const isExpanded = AllDayDomReader.isExpanded(); + const targetRows = isExpanded ? maxRow : Math.min(maxRow, ALL_DAY_CONSTANTS.MAX_COLLAPSED_ROWS); + // 6. Animate height to target + this.heightService.animateToRows(targetRows); + } + /** + * Public API for collapsing all-day row + */ + collapseAllDayRow() { + this.heightService.collapseAllDayRow(); + } +} +//# sourceMappingURL=AllDayCoordinator.js.map \ No newline at end of file diff --git a/wwwroot/js/features/all-day/AllDayCoordinator.js.map b/wwwroot/js/features/all-day/AllDayCoordinator.js.map new file mode 100644 index 0000000..7522289 --- /dev/null +++ b/wwwroot/js/features/all-day/AllDayCoordinator.js.map @@ -0,0 +1 @@ +{"version":3,"file":"AllDayCoordinator.js","sourceRoot":"","sources":["../../../../src/features/all-day/AllDayCoordinator.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/features/all-day/AllDayDomReader.d.ts b/wwwroot/js/features/all-day/AllDayDomReader.d.ts new file mode 100644 index 0000000..5142286 --- /dev/null +++ b/wwwroot/js/features/all-day/AllDayDomReader.d.ts @@ -0,0 +1,74 @@ +import { ICalendarEvent } from '../../types/CalendarTypes'; +/** + * AllDayDomReader - Centralized DOM reading utilities for all-day services + * + * STATELESS UTILITY - Pure functions for reading DOM state + * - Consistent selectors across all services + * - Unified computed style approach (not inline styles) + * - Type-safe return values + * - Single source of truth for DOM queries + */ +export declare class AllDayDomReader { + /** + * Get the all-day events container element + */ + static getAllDayContainer(): HTMLElement | null; + /** + * Get the calendar header element + */ + static getCalendarHeader(): HTMLElement | null; + /** + * Get the header spacer element + */ + static getHeaderSpacer(): HTMLElement | null; + /** + * Get all all-day event elements (excluding overflow indicators) + * Returns raw HTMLElements for DOM manipulation + */ + static getEventElements(): HTMLElement[]; + /** + * Get all-day events as ICalendarEvent objects + * Returns parsed data for business logic + */ + static getEventsAsData(): ICalendarEvent[]; + /** + * Get grid row from element using computed style + * Always uses computed style for consistency + */ + static getGridRow(element: HTMLElement): number; + /** + * Get grid column range from element using computed style + */ + static getGridColumnRange(element: HTMLElement): { + start: number; + end: number; + }; + /** + * Get grid area from element using computed style + */ + static getGridArea(element: HTMLElement): string; + /** + * Calculate max row number from all events + * Uses computed styles for accurate reading + */ + static getMaxRowFromEvents(): number; + /** + * Check if all-day container is expanded + */ + static isExpanded(): boolean; + /** + * Get current all-day height from CSS variable + */ + static getCurrentHeight(): number; + /** + * Count events in specific column + */ + static countEventsInColumn(columnIndex: number): number; + /** + * Get current layouts from DOM elements + * Returns map of eventId → layout info for comparison + */ + static getCurrentLayouts(): Map; +} diff --git a/wwwroot/js/features/all-day/AllDayDomReader.js b/wwwroot/js/features/all-day/AllDayDomReader.js new file mode 100644 index 0000000..93405ca --- /dev/null +++ b/wwwroot/js/features/all-day/AllDayDomReader.js @@ -0,0 +1,175 @@ +/** + * AllDayDomReader - Centralized DOM reading utilities for all-day services + * + * STATELESS UTILITY - Pure functions for reading DOM state + * - Consistent selectors across all services + * - Unified computed style approach (not inline styles) + * - Type-safe return values + * - Single source of truth for DOM queries + */ +export class AllDayDomReader { + // ============================================ + // CONTAINER GETTERS + // ============================================ + /** + * Get the all-day events container element + */ + static getAllDayContainer() { + return document.querySelector('swp-calendar-header swp-allday-container'); + } + /** + * Get the calendar header element + */ + static getCalendarHeader() { + return document.querySelector('swp-calendar-header'); + } + /** + * Get the header spacer element + */ + static getHeaderSpacer() { + return document.querySelector('swp-header-spacer'); + } + // ============================================ + // EVENT ELEMENT GETTERS + // ============================================ + /** + * Get all all-day event elements (excluding overflow indicators) + * Returns raw HTMLElements for DOM manipulation + */ + static getEventElements() { + const container = this.getAllDayContainer(); + if (!container) + return []; + return Array.from(container.querySelectorAll('swp-allday-event:not(.max-event-indicator)')); + } + /** + * Get all-day events as ICalendarEvent objects + * Returns parsed data for business logic + */ + static getEventsAsData() { + const elements = this.getEventElements(); + return elements + .map(element => { + const eventId = element.dataset.eventId; + const startStr = element.dataset.start; + const endStr = element.dataset.end; + // Validate required fields + if (!eventId || !startStr || !endStr) { + console.warn('AllDayDomReader: Invalid event data in DOM:', element); + return null; + } + const start = new Date(startStr); + const end = new Date(endStr); + if (isNaN(start.getTime()) || isNaN(end.getTime())) { + console.warn('AllDayDomReader: Invalid event dates:', { startStr, endStr }); + return null; + } + return { + id: eventId, + title: element.dataset.title || '', + start, + end, + type: element.dataset.type || 'task', + allDay: true, + syncStatus: (element.dataset.syncStatus || 'synced') + }; + }) + .filter((event) => event !== null); + } + // ============================================ + // GRID POSITION READERS + // ============================================ + /** + * Get grid row from element using computed style + * Always uses computed style for consistency + */ + static getGridRow(element) { + const computedStyle = window.getComputedStyle(element); + return parseInt(computedStyle.gridRowStart) || 0; + } + /** + * Get grid column range from element using computed style + */ + static getGridColumnRange(element) { + const computedStyle = window.getComputedStyle(element); + return { + start: parseInt(computedStyle.gridColumnStart) || 0, + end: parseInt(computedStyle.gridColumnEnd) || 0 + }; + } + /** + * Get grid area from element using computed style + */ + static getGridArea(element) { + const computedStyle = window.getComputedStyle(element); + return computedStyle.gridArea; + } + /** + * Calculate max row number from all events + * Uses computed styles for accurate reading + */ + static getMaxRowFromEvents() { + const events = this.getEventElements(); + if (events.length === 0) + return 0; + let maxRow = 0; + events.forEach(event => { + const row = this.getGridRow(event); + maxRow = Math.max(maxRow, row); + }); + return maxRow; + } + // ============================================ + // STATE READERS + // ============================================ + /** + * Check if all-day container is expanded + */ + static isExpanded() { + const container = this.getAllDayContainer(); + return container?.classList.contains('expanded') || false; + } + /** + * Get current all-day height from CSS variable + */ + static getCurrentHeight() { + const root = document.documentElement; + const heightStr = root.style.getPropertyValue('--all-day-row-height') || '0px'; + return parseInt(heightStr) || 0; + } + /** + * Count events in specific column + */ + static countEventsInColumn(columnIndex) { + const events = this.getEventElements(); + let count = 0; + events.forEach((event) => { + const { start, end } = this.getGridColumnRange(event); + if (start <= columnIndex && end > columnIndex) { + count++; + } + }); + return count; + } + // ============================================ + // LAYOUT READERS + // ============================================ + /** + * Get current layouts from DOM elements + * Returns map of eventId → layout info for comparison + */ + static getCurrentLayouts() { + const layoutsMap = new Map(); + const events = this.getEventElements(); + events.forEach(event => { + const eventId = event.dataset.eventId; + if (eventId) { + layoutsMap.set(eventId, { + gridArea: this.getGridArea(event) + }); + } + }); + return layoutsMap; + } +} +//# sourceMappingURL=AllDayDomReader.js.map \ No newline at end of file diff --git a/wwwroot/js/features/all-day/AllDayDomReader.js.map b/wwwroot/js/features/all-day/AllDayDomReader.js.map new file mode 100644 index 0000000..9a27f22 --- /dev/null +++ b/wwwroot/js/features/all-day/AllDayDomReader.js.map @@ -0,0 +1 @@ +{"version":3,"file":"AllDayDomReader.js","sourceRoot":"","sources":["../../../../src/features/all-day/AllDayDomReader.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/features/all-day/AllDayDragService.d.ts b/wwwroot/js/features/all-day/AllDayDragService.d.ts new file mode 100644 index 0000000..602d2e2 --- /dev/null +++ b/wwwroot/js/features/all-day/AllDayDragService.d.ts @@ -0,0 +1,50 @@ +/** + * AllDayDragService - Manages drag and drop operations for all-day events + * + * STATELESS SERVICE - Reads all data from DOM via AllDayDomReader + * - No persistent state + * - Handles timed → all-day conversion + * - Handles all-day → all-day repositioning + * - Handles column changes during drag + * - Calculates layouts on-demand from DOM + */ +import { IEventLayout } from '../../utils/AllDayLayoutEngine'; +import { IDragMouseEnterHeaderEventPayload, IDragColumnChangeEventPayload, IDragEndEventPayload } from '../../types/EventTypes'; +import { EventManager } from '../../managers/EventManager'; +import { AllDayEventRenderer } from '../../renderers/AllDayEventRenderer'; +import { DateService } from '../../utils/DateService'; +export declare class AllDayDragService { + private eventManager; + private allDayEventRenderer; + private dateService; + constructor(eventManager: EventManager, allDayEventRenderer: AllDayEventRenderer, dateService: DateService); + /** + * Handle conversion from timed event to all-day event + * Called when dragging a timed event into the header + */ + handleConvertToAllDay(payload: IDragMouseEnterHeaderEventPayload): void; + /** + * Handle column change during drag of all-day event + * Updates grid position while maintaining event span + */ + handleColumnChange(payload: IDragColumnChangeEventPayload): void; + /** + * Handle drag end for all-day → all-day drops + * Recalculates layouts and updates event positions + */ + handleDragEnd(dragEndEvent: IDragEndEventPayload): Promise; + /** + * Calculate layouts for events using AllDayLayoutEngine + */ + private calculateLayouts; + /** + * Apply layout updates to DOM elements + * Only updates elements that have changed position + * Public so AllDayCoordinator can use it for full recalculation + */ + applyLayoutUpdates(newLayouts: IEventLayout[]): void; + /** + * Fade out and remove element + */ + private fadeOutAndRemove; +} diff --git a/wwwroot/js/features/all-day/AllDayDragService.js b/wwwroot/js/features/all-day/AllDayDragService.js new file mode 100644 index 0000000..000994b --- /dev/null +++ b/wwwroot/js/features/all-day/AllDayDragService.js @@ -0,0 +1,183 @@ +/** + * AllDayDragService - Manages drag and drop operations for all-day events + * + * STATELESS SERVICE - Reads all data from DOM via AllDayDomReader + * - No persistent state + * - Handles timed → all-day conversion + * - Handles all-day → all-day repositioning + * - Handles column changes during drag + * - Calculates layouts on-demand from DOM + */ +import { SwpAllDayEventElement } from '../../elements/SwpEventElement'; +import { AllDayLayoutEngine } from '../../utils/AllDayLayoutEngine'; +import { ColumnDetectionUtils } from '../../utils/ColumnDetectionUtils'; +import { ALL_DAY_CONSTANTS } from '../../configurations/CalendarConfig'; +import { AllDayDomReader } from './AllDayDomReader'; +export class AllDayDragService { + constructor(eventManager, allDayEventRenderer, dateService) { + this.eventManager = eventManager; + this.allDayEventRenderer = allDayEventRenderer; + this.dateService = dateService; + } + /** + * Handle conversion from timed event to all-day event + * Called when dragging a timed event into the header + */ + handleConvertToAllDay(payload) { + const allDayContainer = AllDayDomReader.getAllDayContainer(); + if (!allDayContainer) + return; + // Create SwpAllDayEventElement from ICalendarEvent + const allDayElement = SwpAllDayEventElement.fromCalendarEvent(payload.calendarEvent); + // Apply grid positioning + allDayElement.style.gridRow = '1'; + allDayElement.style.gridColumn = payload.targetColumn.index.toString(); + // Remove old swp-event clone + payload.draggedClone.remove(); + // Call delegate to update DragDropManager's draggedClone reference + payload.replaceClone(allDayElement); + // Append to container + allDayContainer.appendChild(allDayElement); + ColumnDetectionUtils.updateColumnBoundsCache(); + } + /** + * Handle column change during drag of all-day event + * Updates grid position while maintaining event span + */ + handleColumnChange(payload) { + const allDayContainer = AllDayDomReader.getAllDayContainer(); + if (!allDayContainer) + return; + const targetColumn = ColumnDetectionUtils.getColumnBounds(payload.mousePosition); + if (!targetColumn || !payload.draggedClone) + return; + // Calculate event span from original grid positioning + const { start: gridColumnStart, end: gridColumnEnd } = AllDayDomReader.getGridColumnRange(payload.draggedClone); + const span = gridColumnEnd - gridColumnStart; + // Update clone position maintaining the span + const newStartColumn = targetColumn.index; + const newEndColumn = newStartColumn + span; + payload.draggedClone.style.gridColumn = `${newStartColumn} / ${newEndColumn}`; + } + /** + * Handle drag end for all-day → all-day drops + * Recalculates layouts and updates event positions + */ + async handleDragEnd(dragEndEvent) { + if (!dragEndEvent.draggedClone) + return; + // Normalize clone ID + dragEndEvent.draggedClone.dataset.eventId = dragEndEvent.draggedClone.dataset.eventId?.replace('clone-', ''); + dragEndEvent.draggedClone.style.pointerEvents = ''; // Re-enable pointer events + dragEndEvent.originalElement.dataset.eventId += '_'; + const eventId = dragEndEvent.draggedClone.dataset.eventId; + const eventDate = dragEndEvent.finalPosition.column?.date; + const eventType = dragEndEvent.draggedClone.dataset.type; + if (!eventDate || !eventId || !eventType) + return; + // Get original dates to preserve time + const originalStartDate = new Date(dragEndEvent.draggedClone.dataset.start); + const originalEndDate = new Date(dragEndEvent.draggedClone.dataset.end); + // Calculate actual duration in milliseconds (preserves hours/minutes/seconds) + const durationMs = originalEndDate.getTime() - originalStartDate.getTime(); + // Create new start date with the new day but preserve original time + const newStartDate = new Date(eventDate); + newStartDate.setHours(originalStartDate.getHours(), originalStartDate.getMinutes(), originalStartDate.getSeconds(), originalStartDate.getMilliseconds()); + // Create new end date by adding duration in milliseconds + const newEndDate = new Date(newStartDate.getTime() + durationMs); + // Update data attributes with new dates (convert to UTC) + dragEndEvent.draggedClone.dataset.start = this.dateService.toUTC(newStartDate); + dragEndEvent.draggedClone.dataset.end = this.dateService.toUTC(newEndDate); + const droppedEvent = { + id: eventId, + title: dragEndEvent.draggedClone.dataset.title || '', + start: newStartDate, + end: newEndDate, + type: eventType, + allDay: true, + syncStatus: 'synced' + }; + // Get all events from DOM and recalculate layouts + const allEventsFromDOM = AllDayDomReader.getEventsAsData(); + const weekDates = ColumnDetectionUtils.getColumns(); + // Replace old event with dropped event + const updatedEvents = [ + ...allEventsFromDOM.filter(event => event.id !== eventId), + droppedEvent + ]; + // Calculate new layouts for ALL events + const newLayouts = this.calculateLayouts(updatedEvents, weekDates); + // Apply layout updates to DOM + this.applyLayoutUpdates(newLayouts); + // Clean up drag styles from the dropped clone + dragEndEvent.draggedClone.classList.remove('dragging'); + dragEndEvent.draggedClone.style.zIndex = ''; + dragEndEvent.draggedClone.style.cursor = ''; + dragEndEvent.draggedClone.style.opacity = ''; + // Apply highlight class to show the dropped event with highlight color + dragEndEvent.draggedClone.classList.add('highlight'); + // Update event in repository to mark as allDay=true + await this.eventManager.updateEvent(eventId, { + start: newStartDate, + end: newEndDate, + allDay: true + }); + this.fadeOutAndRemove(dragEndEvent.originalElement); + } + /** + * Calculate layouts for events using AllDayLayoutEngine + */ + calculateLayouts(events, weekDates) { + const layoutEngine = new AllDayLayoutEngine(weekDates.map(column => column.date)); + return layoutEngine.calculateLayout(events); + } + /** + * Apply layout updates to DOM elements + * Only updates elements that have changed position + * Public so AllDayCoordinator can use it for full recalculation + */ + applyLayoutUpdates(newLayouts) { + const container = AllDayDomReader.getAllDayContainer(); + if (!container) + return; + // Read current layouts from DOM + const currentLayoutsMap = AllDayDomReader.getCurrentLayouts(); + newLayouts.forEach((layout) => { + const currentLayout = currentLayoutsMap.get(layout.calenderEvent.id); + // Only update if layout changed + if (currentLayout?.gridArea !== layout.gridArea) { + const element = container.querySelector(`[data-event-id="${layout.calenderEvent.id}"]`); + if (element) { + element.classList.add('transitioning'); + element.style.gridArea = layout.gridArea; + element.style.gridRow = layout.row.toString(); + element.style.gridColumn = `${layout.startColumn} / ${layout.endColumn + 1}`; + // Update overflow classes based on row + element.classList.remove('max-event-overflow-hide', 'max-event-overflow-show'); + if (layout.row > ALL_DAY_CONSTANTS.MAX_COLLAPSED_ROWS) { + const isExpanded = AllDayDomReader.isExpanded(); + if (isExpanded) { + element.classList.add('max-event-overflow-show'); + } + else { + element.classList.add('max-event-overflow-hide'); + } + } + // Remove transition class after animation + setTimeout(() => element.classList.remove('transitioning'), 200); + } + } + }); + } + /** + * Fade out and remove element + */ + fadeOutAndRemove(element) { + element.style.transition = 'opacity 0.3s ease-out'; + element.style.opacity = '0'; + setTimeout(() => { + element.remove(); + }, 300); + } +} +//# sourceMappingURL=AllDayDragService.js.map \ No newline at end of file diff --git a/wwwroot/js/features/all-day/AllDayDragService.js.map b/wwwroot/js/features/all-day/AllDayDragService.js.map new file mode 100644 index 0000000..cd7900e --- /dev/null +++ b/wwwroot/js/features/all-day/AllDayDragService.js.map @@ -0,0 +1 @@ +{"version":3,"file":"AllDayDragService.js","sourceRoot":"","sources":["../../../../src/features/all-day/AllDayDragService.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/features/all-day/AllDayHeightService.d.ts b/wwwroot/js/features/all-day/AllDayHeightService.d.ts new file mode 100644 index 0000000..bf02632 --- /dev/null +++ b/wwwroot/js/features/all-day/AllDayHeightService.d.ts @@ -0,0 +1,26 @@ +/** + * AllDayHeightService - Manages all-day row height calculations and animations + * + * STATELESS SERVICE - Reads all data from DOM via AllDayDomReader + * - No persistent state + * - Calculates required rows by reading DOM elements + * - Animates header height based on DOM state + */ +export declare class AllDayHeightService { + /** + * Main entry point - recalculate and animate header height based on DOM + */ + recalculateAndAnimate(): void; + /** + * Animate all-day container to specific number of rows + */ + animateToRows(targetRows: number): void; + /** + * Calculate all-day height based on number of rows + */ + private calculateAllDayHeight; + /** + * Collapse all-day row (animate to 0 rows) + */ + collapseAllDayRow(): void; +} diff --git a/wwwroot/js/features/all-day/AllDayHeightService.js b/wwwroot/js/features/all-day/AllDayHeightService.js new file mode 100644 index 0000000..17d344d --- /dev/null +++ b/wwwroot/js/features/all-day/AllDayHeightService.js @@ -0,0 +1,85 @@ +/** + * AllDayHeightService - Manages all-day row height calculations and animations + * + * STATELESS SERVICE - Reads all data from DOM via AllDayDomReader + * - No persistent state + * - Calculates required rows by reading DOM elements + * - Animates header height based on DOM state + */ +import { ALL_DAY_CONSTANTS } from '../../configurations/CalendarConfig'; +import { eventBus } from '../../core/EventBus'; +import { AllDayDomReader } from './AllDayDomReader'; +export class AllDayHeightService { + /** + * Main entry point - recalculate and animate header height based on DOM + */ + recalculateAndAnimate() { + const requiredRows = AllDayDomReader.getMaxRowFromEvents(); + this.animateToRows(requiredRows); + } + /** + * Animate all-day container to specific number of rows + */ + animateToRows(targetRows) { + const { targetHeight, currentHeight, heightDifference } = this.calculateAllDayHeight(targetRows); + if (targetHeight === currentHeight) + return; // No animation needed + console.log(`🎬 All-day height animation: ${currentHeight}px → ${targetHeight}px (${Math.ceil(currentHeight / ALL_DAY_CONSTANTS.SINGLE_ROW_HEIGHT)} → ${targetRows} rows)`); + // Get elements + const calendarHeader = AllDayDomReader.getCalendarHeader(); + const headerSpacer = AllDayDomReader.getHeaderSpacer(); + const allDayContainer = AllDayDomReader.getAllDayContainer(); + if (!calendarHeader || !allDayContainer) + return; + // Get current parent height for animation + const currentParentHeight = parseFloat(getComputedStyle(calendarHeader).height); + const targetParentHeight = currentParentHeight + heightDifference; + const animations = [ + calendarHeader.animate([ + { height: `${currentParentHeight}px` }, + { height: `${targetParentHeight}px` } + ], { + duration: 150, + easing: 'ease-out', + fill: 'forwards' + }) + ]; + // Add spacer animation if spacer exists + if (headerSpacer) { + const root = document.documentElement; + const headerHeightStr = root.style.getPropertyValue('--header-height'); + const headerHeight = parseInt(headerHeightStr); + const currentSpacerHeight = headerHeight + currentHeight; + const targetSpacerHeight = headerHeight + targetHeight; + animations.push(headerSpacer.animate([ + { height: `${currentSpacerHeight}px` }, + { height: `${targetSpacerHeight}px` } + ], { + duration: 150, + easing: 'ease-out' + })); + } + // Update CSS variable after animation + Promise.all(animations.map(anim => anim.finished)).then(() => { + const root = document.documentElement; + root.style.setProperty('--all-day-row-height', `${targetHeight}px`); + eventBus.emit('header:height-changed'); + }); + } + /** + * Calculate all-day height based on number of rows + */ + calculateAllDayHeight(targetRows) { + const targetHeight = targetRows * ALL_DAY_CONSTANTS.SINGLE_ROW_HEIGHT; + const currentHeight = AllDayDomReader.getCurrentHeight(); + const heightDifference = targetHeight - currentHeight; + return { targetHeight, currentHeight, heightDifference }; + } + /** + * Collapse all-day row (animate to 0 rows) + */ + collapseAllDayRow() { + this.animateToRows(0); + } +} +//# sourceMappingURL=AllDayHeightService.js.map \ No newline at end of file diff --git a/wwwroot/js/features/all-day/AllDayHeightService.js.map b/wwwroot/js/features/all-day/AllDayHeightService.js.map new file mode 100644 index 0000000..7652b58 --- /dev/null +++ b/wwwroot/js/features/all-day/AllDayHeightService.js.map @@ -0,0 +1 @@ +{"version":3,"file":"AllDayHeightService.js","sourceRoot":"","sources":["../../../../src/features/all-day/AllDayHeightService.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/features/all-day/index.d.ts b/wwwroot/js/features/all-day/index.d.ts new file mode 100644 index 0000000..2cd4836 --- /dev/null +++ b/wwwroot/js/features/all-day/index.d.ts @@ -0,0 +1,9 @@ +/** + * All-day feature barrel export + * + * Exports all public APIs from the all-day feature + */ +export { AllDayCoordinator } from './AllDayCoordinator'; +export { AllDayHeightService } from './AllDayHeightService'; +export { AllDayCollapseService } from './AllDayCollapseService'; +export { AllDayDragService } from './AllDayDragService'; diff --git a/wwwroot/js/features/all-day/index.js b/wwwroot/js/features/all-day/index.js new file mode 100644 index 0000000..ad0078d --- /dev/null +++ b/wwwroot/js/features/all-day/index.js @@ -0,0 +1,10 @@ +/** + * All-day feature barrel export + * + * Exports all public APIs from the all-day feature + */ +export { AllDayCoordinator } from './AllDayCoordinator'; +export { AllDayHeightService } from './AllDayHeightService'; +export { AllDayCollapseService } from './AllDayCollapseService'; +export { AllDayDragService } from './AllDayDragService'; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/wwwroot/js/features/all-day/index.js.map b/wwwroot/js/features/all-day/index.js.map new file mode 100644 index 0000000..166080e --- /dev/null +++ b/wwwroot/js/features/all-day/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/features/all-day/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC"} \ No newline at end of file diff --git a/wwwroot/js/features/all-day/utils/AllDayDomReader.d.ts b/wwwroot/js/features/all-day/utils/AllDayDomReader.d.ts new file mode 100644 index 0000000..7026c04 --- /dev/null +++ b/wwwroot/js/features/all-day/utils/AllDayDomReader.d.ts @@ -0,0 +1,74 @@ +import { ICalendarEvent } from '../../../types/CalendarTypes'; +/** + * AllDayDomReader - Centralized DOM reading utilities for all-day services + * + * STATELESS UTILITY - Pure functions for reading DOM state + * - Consistent selectors across all services + * - Unified computed style approach (not inline styles) + * - Type-safe return values + * - Single source of truth for DOM queries + */ +export declare class AllDayDomReader { + /** + * Get the all-day events container element + */ + static getAllDayContainer(): HTMLElement | null; + /** + * Get the calendar header element + */ + static getCalendarHeader(): HTMLElement | null; + /** + * Get the header spacer element + */ + static getHeaderSpacer(): HTMLElement | null; + /** + * Get all all-day event elements (excluding overflow indicators) + * Returns raw HTMLElements for DOM manipulation + */ + static getEventElements(): HTMLElement[]; + /** + * Get all-day events as ICalendarEvent objects + * Returns parsed data for business logic + */ + static getEventsAsData(): ICalendarEvent[]; + /** + * Get grid row from element using computed style + * Always uses computed style for consistency + */ + static getGridRow(element: HTMLElement): number; + /** + * Get grid column range from element using computed style + */ + static getGridColumnRange(element: HTMLElement): { + start: number; + end: number; + }; + /** + * Get grid area from element using computed style + */ + static getGridArea(element: HTMLElement): string; + /** + * Calculate max row number from all events + * Uses computed styles for accurate reading + */ + static getMaxRowFromEvents(): number; + /** + * Check if all-day container is expanded + */ + static isExpanded(): boolean; + /** + * Get current all-day height from CSS variable + */ + static getCurrentHeight(): number; + /** + * Count events in specific column + */ + static countEventsInColumn(columnIndex: number): number; + /** + * Get current layouts from DOM elements + * Returns map of eventId → layout info for comparison + */ + static getCurrentLayouts(): Map; +} diff --git a/wwwroot/js/features/all-day/utils/AllDayDomReader.js b/wwwroot/js/features/all-day/utils/AllDayDomReader.js new file mode 100644 index 0000000..93405ca --- /dev/null +++ b/wwwroot/js/features/all-day/utils/AllDayDomReader.js @@ -0,0 +1,175 @@ +/** + * AllDayDomReader - Centralized DOM reading utilities for all-day services + * + * STATELESS UTILITY - Pure functions for reading DOM state + * - Consistent selectors across all services + * - Unified computed style approach (not inline styles) + * - Type-safe return values + * - Single source of truth for DOM queries + */ +export class AllDayDomReader { + // ============================================ + // CONTAINER GETTERS + // ============================================ + /** + * Get the all-day events container element + */ + static getAllDayContainer() { + return document.querySelector('swp-calendar-header swp-allday-container'); + } + /** + * Get the calendar header element + */ + static getCalendarHeader() { + return document.querySelector('swp-calendar-header'); + } + /** + * Get the header spacer element + */ + static getHeaderSpacer() { + return document.querySelector('swp-header-spacer'); + } + // ============================================ + // EVENT ELEMENT GETTERS + // ============================================ + /** + * Get all all-day event elements (excluding overflow indicators) + * Returns raw HTMLElements for DOM manipulation + */ + static getEventElements() { + const container = this.getAllDayContainer(); + if (!container) + return []; + return Array.from(container.querySelectorAll('swp-allday-event:not(.max-event-indicator)')); + } + /** + * Get all-day events as ICalendarEvent objects + * Returns parsed data for business logic + */ + static getEventsAsData() { + const elements = this.getEventElements(); + return elements + .map(element => { + const eventId = element.dataset.eventId; + const startStr = element.dataset.start; + const endStr = element.dataset.end; + // Validate required fields + if (!eventId || !startStr || !endStr) { + console.warn('AllDayDomReader: Invalid event data in DOM:', element); + return null; + } + const start = new Date(startStr); + const end = new Date(endStr); + if (isNaN(start.getTime()) || isNaN(end.getTime())) { + console.warn('AllDayDomReader: Invalid event dates:', { startStr, endStr }); + return null; + } + return { + id: eventId, + title: element.dataset.title || '', + start, + end, + type: element.dataset.type || 'task', + allDay: true, + syncStatus: (element.dataset.syncStatus || 'synced') + }; + }) + .filter((event) => event !== null); + } + // ============================================ + // GRID POSITION READERS + // ============================================ + /** + * Get grid row from element using computed style + * Always uses computed style for consistency + */ + static getGridRow(element) { + const computedStyle = window.getComputedStyle(element); + return parseInt(computedStyle.gridRowStart) || 0; + } + /** + * Get grid column range from element using computed style + */ + static getGridColumnRange(element) { + const computedStyle = window.getComputedStyle(element); + return { + start: parseInt(computedStyle.gridColumnStart) || 0, + end: parseInt(computedStyle.gridColumnEnd) || 0 + }; + } + /** + * Get grid area from element using computed style + */ + static getGridArea(element) { + const computedStyle = window.getComputedStyle(element); + return computedStyle.gridArea; + } + /** + * Calculate max row number from all events + * Uses computed styles for accurate reading + */ + static getMaxRowFromEvents() { + const events = this.getEventElements(); + if (events.length === 0) + return 0; + let maxRow = 0; + events.forEach(event => { + const row = this.getGridRow(event); + maxRow = Math.max(maxRow, row); + }); + return maxRow; + } + // ============================================ + // STATE READERS + // ============================================ + /** + * Check if all-day container is expanded + */ + static isExpanded() { + const container = this.getAllDayContainer(); + return container?.classList.contains('expanded') || false; + } + /** + * Get current all-day height from CSS variable + */ + static getCurrentHeight() { + const root = document.documentElement; + const heightStr = root.style.getPropertyValue('--all-day-row-height') || '0px'; + return parseInt(heightStr) || 0; + } + /** + * Count events in specific column + */ + static countEventsInColumn(columnIndex) { + const events = this.getEventElements(); + let count = 0; + events.forEach((event) => { + const { start, end } = this.getGridColumnRange(event); + if (start <= columnIndex && end > columnIndex) { + count++; + } + }); + return count; + } + // ============================================ + // LAYOUT READERS + // ============================================ + /** + * Get current layouts from DOM elements + * Returns map of eventId → layout info for comparison + */ + static getCurrentLayouts() { + const layoutsMap = new Map(); + const events = this.getEventElements(); + events.forEach(event => { + const eventId = event.dataset.eventId; + if (eventId) { + layoutsMap.set(eventId, { + gridArea: this.getGridArea(event) + }); + } + }); + return layoutsMap; + } +} +//# sourceMappingURL=AllDayDomReader.js.map \ No newline at end of file diff --git a/wwwroot/js/features/all-day/utils/AllDayDomReader.js.map b/wwwroot/js/features/all-day/utils/AllDayDomReader.js.map new file mode 100644 index 0000000..5c193a7 --- /dev/null +++ b/wwwroot/js/features/all-day/utils/AllDayDomReader.js.map @@ -0,0 +1 @@ +{"version":3,"file":"AllDayDomReader.js","sourceRoot":"","sources":["../../../../../src/features/all-day/utils/AllDayDomReader.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/index.d.ts b/wwwroot/js/index.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/wwwroot/js/index.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/wwwroot/js/index.js b/wwwroot/js/index.js new file mode 100644 index 0000000..c6d0063 --- /dev/null +++ b/wwwroot/js/index.js @@ -0,0 +1,171 @@ +// Main entry point for Calendar Plantempus +import { Container } from '@novadi/core'; +import { eventBus } from './core/EventBus'; +import { ConfigManager } from './configurations/ConfigManager'; +import { URLManager } from './utils/URLManager'; +// Import all managers +import { EventManager } from './managers/EventManager'; +import { EventRenderingService } from './renderers/EventRendererManager'; +import { GridManager } from './managers/GridManager'; +import { ScrollManager } from './managers/ScrollManager'; +import { NavigationManager } from './managers/NavigationManager'; +import { NavigationButtons } from './components/NavigationButtons'; +import { ViewSelector } from './components/ViewSelector'; +import { CalendarManager } from './managers/CalendarManager'; +import { DragDropManager } from './managers/DragDropManager'; +import { AllDayManager } from './managers/AllDayManager'; +import { ResizeHandleManager } from './managers/ResizeHandleManager'; +import { EdgeScrollManager } from './managers/EdgeScrollManager'; +import { HeaderManager } from './managers/HeaderManager'; +import { WorkweekPresets } from './components/WorkweekPresets'; +import { IndexedDBEventRepository } from './repositories/IndexedDBEventRepository'; +import { ApiEventRepository } from './repositories/ApiEventRepository'; +import { IndexedDBService } from './storage/IndexedDBService'; +import { OperationQueue } from './storage/OperationQueue'; +// Import workers +import { SyncManager } from './workers/SyncManager'; +// Import renderers +import { DateHeaderRenderer } from './renderers/DateHeaderRenderer'; +import { DateColumnRenderer } from './renderers/ColumnRenderer'; +import { DateEventRenderer } from './renderers/EventRenderer'; +import { AllDayEventRenderer } from './renderers/AllDayEventRenderer'; +import { GridRenderer } from './renderers/GridRenderer'; +import { WeekInfoRenderer } from './renderers/WeekInfoRenderer'; +// Import utilities and services +import { DateService } from './utils/DateService'; +import { TimeFormatter } from './utils/TimeFormatter'; +import { PositionUtils } from './utils/PositionUtils'; +import { WorkHoursManager } from './managers/WorkHoursManager'; +import { EventStackManager } from './managers/EventStackManager'; +import { EventLayoutCoordinator } from './managers/EventLayoutCoordinator'; +/** + * Handle deep linking functionality after managers are initialized + */ +async function handleDeepLinking(eventManager, urlManager) { + try { + const eventId = urlManager.parseEventIdFromURL(); + if (eventId) { + console.log(`Deep linking to event ID: ${eventId}`); + // Wait a bit for managers to be fully ready + setTimeout(async () => { + const success = await eventManager.navigateToEvent(eventId); + if (!success) { + console.warn(`Deep linking failed: Event with ID ${eventId} not found`); + } + }, 500); + } + } + catch (error) { + console.warn('Deep linking failed:', error); + } +} +/** + * Initialize the calendar application using NovaDI + */ +async function initializeCalendar() { + try { + // Load configuration from JSON + const config = await ConfigManager.load(); + // Create NovaDI container + const container = new Container(); + const builder = container.builder(); + // Enable debug mode for development + eventBus.setDebug(true); + // Bind core services as instances + builder.registerInstance(eventBus).as(); + // Register configuration instance + builder.registerInstance(config).as(); + // Register storage and repository services + builder.registerType(IndexedDBService).as(); + builder.registerType(OperationQueue).as(); + builder.registerType(ApiEventRepository).as(); + builder.registerType(IndexedDBEventRepository).as(); + // Register workers + builder.registerType(SyncManager).as(); + // Register renderers + builder.registerType(DateHeaderRenderer).as(); + builder.registerType(DateColumnRenderer).as(); + builder.registerType(DateEventRenderer).as(); + // Register core services and utilities + builder.registerType(DateService).as(); + builder.registerType(EventStackManager).as(); + builder.registerType(EventLayoutCoordinator).as(); + builder.registerType(WorkHoursManager).as(); + builder.registerType(URLManager).as(); + builder.registerType(TimeFormatter).as(); + builder.registerType(PositionUtils).as(); + // Note: AllDayLayoutEngine is instantiated per-operation with specific dates, not a singleton + builder.registerType(WeekInfoRenderer).as(); + builder.registerType(AllDayEventRenderer).as(); + builder.registerType(EventRenderingService).as(); + builder.registerType(GridRenderer).as(); + builder.registerType(GridManager).as(); + builder.registerType(ScrollManager).as(); + builder.registerType(NavigationManager).as(); + builder.registerType(NavigationButtons).as(); + builder.registerType(ViewSelector).as(); + builder.registerType(DragDropManager).as(); + builder.registerType(AllDayManager).as(); + builder.registerType(ResizeHandleManager).as(); + builder.registerType(EdgeScrollManager).as(); + builder.registerType(HeaderManager).as(); + builder.registerType(CalendarManager).as(); + builder.registerType(WorkweekPresets).as(); + builder.registerType(ConfigManager).as(); + builder.registerType(EventManager).as(); + // Build the container + const app = builder.build(); + // Get managers from container + const eb = app.resolveType(); + const calendarManager = app.resolveType(); + const eventManager = app.resolveType(); + const resizeHandleManager = app.resolveType(); + const headerManager = app.resolveType(); + const dragDropManager = app.resolveType(); + const viewSelectorManager = app.resolveType(); + const navigationManager = app.resolveType(); + const navigationButtonsManager = app.resolveType(); + const edgeScrollManager = app.resolveType(); + const allDayManager = app.resolveType(); + const urlManager = app.resolveType(); + const workweekPresetsManager = app.resolveType(); + const configManager = app.resolveType(); + // Initialize managers + await calendarManager.initialize?.(); + await resizeHandleManager.initialize?.(); + // Resolve SyncManager (starts automatically in constructor) + // Resolve SyncManager (starts automatically in constructor) + // Resolve SyncManager (starts automatically in constructor) + // Resolve SyncManager (starts automatically in constructor) + // Resolve SyncManager (starts automatically in constructor) + //const syncManager = app.resolveType(); + // Handle deep linking after managers are initialized + await handleDeepLinking(eventManager, urlManager); + // Expose to window for debugging (with proper typing) + window.calendarDebug = { + eventBus, + app, + calendarManager, + eventManager, + workweekPresetsManager, + //syncManager, + }; + } + catch (error) { + throw error; + } +} +// Initialize when DOM is ready - now handles async properly +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', () => { + initializeCalendar().catch(error => { + console.error('Calendar initialization failed:', error); + }); + }); +} +else { + initializeCalendar().catch(error => { + console.error('Calendar initialization failed:', error); + }); +} +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/wwwroot/js/index.js.map b/wwwroot/js/index.js.map new file mode 100644 index 0000000..089ad70 --- /dev/null +++ b/wwwroot/js/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/interfaces/IManager.d.ts b/wwwroot/js/interfaces/IManager.d.ts new file mode 100644 index 0000000..581e5fd --- /dev/null +++ b/wwwroot/js/interfaces/IManager.d.ts @@ -0,0 +1,48 @@ +import { CalendarEvent } from '../types/CalendarTypes'; +/** + * Base interface for all managers + */ +export interface IManager { + /** + * Initialize the manager + */ + initialize?(): Promise | void; + /** + * Refresh the manager's state + */ + refresh?(): void; + /** + * Destroy the manager and clean up resources + */ + destroy?(): void; +} +/** + * Interface for managers that handle events + */ +export interface IEventManager extends IManager { + loadData(): Promise; + getEvents(): CalendarEvent[]; + getEventsForPeriod(startDate: Date, endDate: Date): CalendarEvent[]; +} +/** + * Interface for managers that handle rendering + */ +export interface IRenderingManager extends IManager { + render(): Promise | void; +} +/** + * Interface for managers that handle navigation + */ +export interface INavigationManager extends IManager { + getCurrentWeek(): Date; + navigateToToday(): void; + navigateToNextWeek(): void; + navigateToPreviousWeek(): void; +} +/** + * Interface for managers that handle scrolling + */ +export interface IScrollManager extends IManager { + scrollTo(scrollTop: number): void; + scrollToHour(hour: number): void; +} diff --git a/wwwroot/js/interfaces/IManager.js b/wwwroot/js/interfaces/IManager.js new file mode 100644 index 0000000..6768e2b --- /dev/null +++ b/wwwroot/js/interfaces/IManager.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=IManager.js.map \ No newline at end of file diff --git a/wwwroot/js/interfaces/IManager.js.map b/wwwroot/js/interfaces/IManager.js.map new file mode 100644 index 0000000..6495ffb --- /dev/null +++ b/wwwroot/js/interfaces/IManager.js.map @@ -0,0 +1 @@ +{"version":3,"file":"IManager.js","sourceRoot":"","sources":["../../../src/interfaces/IManager.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/wwwroot/js/managers/AllDayManager.d.ts b/wwwroot/js/managers/AllDayManager.d.ts new file mode 100644 index 0000000..0fc9919 --- /dev/null +++ b/wwwroot/js/managers/AllDayManager.d.ts @@ -0,0 +1,91 @@ +import { AllDayEventRenderer } from '../renderers/AllDayEventRenderer'; +import { EventManager } from './EventManager'; +import { DateService } from '../utils/DateService'; +/** + * AllDayManager - Handles all-day row height animations and management + * Uses AllDayLayoutEngine for all overlap detection and layout calculation + */ +export declare class AllDayManager { + private allDayEventRenderer; + private eventManager; + private dateService; + private layoutEngine; + private currentAllDayEvents; + private currentWeekDates; + private isExpanded; + private actualRowCount; + constructor(eventManager: EventManager, allDayEventRenderer: AllDayEventRenderer, dateService: DateService); + /** + * Setup event listeners for drag conversions + */ + private setupEventListeners; + private getAllDayContainer; + private getCalendarHeader; + private getHeaderSpacer; + /** + * Read current max row from DOM elements + * Excludes events marked as removing (data-removing attribute) + */ + private getMaxRowFromDOM; + /** + * Get current gridArea for an event from DOM + */ + private getGridAreaFromDOM; + /** + * Count events in a specific column by reading DOM + */ + private countEventsInColumnFromDOM; + /** + * Calculate all-day height based on number of rows + */ + private calculateAllDayHeight; + /** + * Check current all-day events and animate to correct height + * Reads max row directly from DOM elements + */ + checkAndAnimateAllDayHeight(): void; + /** + * Animate all-day container to specific number of rows + */ + animateToRows(targetRows: number): void; + /** + * Calculate layout for ALL all-day events using AllDayLayoutEngine + * This is the correct method that processes all events together for proper overlap detection + */ + private calculateAllDayEventsLayout; + private handleConvertToAllDay; + /** + * Handle drag move for all-day events - SPECIALIZED FOR ALL-DAY CONTAINER + */ + private handleColumnChange; + private fadeOutAndRemove; + /** + * Handle timed → all-day conversion on drop + */ + private handleTimedToAllDayDrop; + /** + * Handle all-day → all-day drop (moving within header) + */ + private handleDragEnd; + /** + * Update chevron button visibility and state + */ + private updateChevronButton; + /** + * Toggle between expanded and collapsed state + */ + private toggleExpanded; + /** + * Count number of events in a specific column using IColumnBounds + * Reads directly from DOM elements + */ + private countEventsInColumn; + /** + * Update overflow indicators for collapsed state + */ + private updateOverflowIndicators; + /** + * Clear overflow indicators and restore normal state + */ + private clearOverflowIndicators; +} diff --git a/wwwroot/js/managers/AllDayManager.js b/wwwroot/js/managers/AllDayManager.js new file mode 100644 index 0000000..4fb2956 --- /dev/null +++ b/wwwroot/js/managers/AllDayManager.js @@ -0,0 +1,528 @@ +// All-day row height management and animations +import { eventBus } from '../core/EventBus'; +import { ALL_DAY_CONSTANTS } from '../configurations/CalendarConfig'; +import { AllDayLayoutEngine } from '../utils/AllDayLayoutEngine'; +import { ColumnDetectionUtils } from '../utils/ColumnDetectionUtils'; +import { SwpAllDayEventElement } from '../elements/SwpEventElement'; +import { CoreEvents } from '../constants/CoreEvents'; +/** + * AllDayManager - Handles all-day row height animations and management + * Uses AllDayLayoutEngine for all overlap detection and layout calculation + */ +export class AllDayManager { + constructor(eventManager, allDayEventRenderer, dateService) { + this.layoutEngine = null; + // State tracking for layout calculation + this.currentAllDayEvents = []; + this.currentWeekDates = []; + // Expand/collapse state + this.isExpanded = false; + this.actualRowCount = 0; + this.eventManager = eventManager; + this.allDayEventRenderer = allDayEventRenderer; + this.dateService = dateService; + // Sync CSS variable with TypeScript constant to ensure consistency + document.documentElement.style.setProperty('--single-row-height', `${ALL_DAY_CONSTANTS.EVENT_HEIGHT}px`); + this.setupEventListeners(); + } + /** + * Setup event listeners for drag conversions + */ + setupEventListeners() { + eventBus.on('drag:mouseenter-header', (event) => { + const payload = event.detail; + if (payload.draggedClone.hasAttribute('data-allday')) + return; + console.log('🔄 AllDayManager: Received drag:mouseenter-header', { + targetDate: payload.targetColumn, + originalElementId: payload.originalElement?.dataset?.eventId, + originalElementTag: payload.originalElement?.tagName + }); + this.handleConvertToAllDay(payload); + }); + eventBus.on('drag:mouseleave-header', (event) => { + const { originalElement, cloneElement } = event.detail; + console.log('🚪 AllDayManager: Received drag:mouseleave-header', { + originalElementId: originalElement?.dataset?.eventId + }); + }); + // Listen for drag operations on all-day events + eventBus.on('drag:start', (event) => { + let payload = event.detail; + if (!payload.draggedClone?.hasAttribute('data-allday')) { + return; + } + this.allDayEventRenderer.handleDragStart(payload); + }); + eventBus.on('drag:column-change', (event) => { + let payload = event.detail; + if (!payload.draggedClone?.hasAttribute('data-allday')) { + return; + } + this.handleColumnChange(payload); + }); + eventBus.on('drag:end', (event) => { + let dragEndPayload = event.detail; + console.log('🎯 AllDayManager: drag:end received', { + target: dragEndPayload.target, + originalElementTag: dragEndPayload.originalElement?.tagName, + hasAllDayAttribute: dragEndPayload.originalElement?.hasAttribute('data-allday'), + eventId: dragEndPayload.originalElement?.dataset.eventId + }); + // Handle all-day → all-day drops (within header) + if (dragEndPayload.target === 'swp-day-header' && dragEndPayload.originalElement?.hasAttribute('data-allday')) { + console.log('✅ AllDayManager: Handling all-day → all-day drop'); + this.handleDragEnd(dragEndPayload); + return; + } + // Handle timed → all-day conversion (dropped in header) + if (dragEndPayload.target === 'swp-day-header' && !dragEndPayload.originalElement?.hasAttribute('data-allday')) { + console.log('🔄 AllDayManager: Timed → all-day conversion on drop'); + this.handleTimedToAllDayDrop(dragEndPayload); + return; + } + // Handle all-day → timed conversion (dropped in column) + if (dragEndPayload.target === 'swp-day-column' && dragEndPayload.originalElement?.hasAttribute('data-allday')) { + const eventId = dragEndPayload.originalElement.dataset.eventId; + console.log('🔄 AllDayManager: All-day → timed conversion', { eventId }); + // Mark for removal (sets data-removing attribute) + this.fadeOutAndRemove(dragEndPayload.originalElement); + // Recalculate layout WITHOUT the removed event to compress gaps + const remainingEvents = this.currentAllDayEvents.filter(e => e.id !== eventId); + const newLayouts = this.calculateAllDayEventsLayout(remainingEvents, this.currentWeekDates); + // Re-render all-day events with compressed layout + this.allDayEventRenderer.renderAllDayEventsForPeriod(newLayouts); + // NOW animate height with compressed layout + this.checkAndAnimateAllDayHeight(); + } + }); + // Listen for drag cancellation to recalculate height + eventBus.on('drag:cancelled', (event) => { + const { draggedElement, reason } = event.detail; + console.log('🚫 AllDayManager: Drag cancelled', { + eventId: draggedElement?.dataset?.eventId, + reason + }); + }); + // Listen for header ready - when dates are populated with period data + eventBus.on('header:ready', async (event) => { + let headerReadyEventPayload = event.detail; + let startDate = new Date(headerReadyEventPayload.headerElements.at(0).date); + let endDate = new Date(headerReadyEventPayload.headerElements.at(-1).date); + let events = await this.eventManager.getEventsForPeriod(startDate, endDate); + // Filter for all-day events + const allDayEvents = events.filter(event => event.allDay); + const layouts = this.calculateAllDayEventsLayout(allDayEvents, headerReadyEventPayload.headerElements); + this.allDayEventRenderer.renderAllDayEventsForPeriod(layouts); + this.checkAndAnimateAllDayHeight(); + }); + eventBus.on(CoreEvents.VIEW_CHANGED, (event) => { + this.allDayEventRenderer.handleViewChanged(event); + }); + } + getAllDayContainer() { + return document.querySelector('swp-calendar-header swp-allday-container'); + } + getCalendarHeader() { + return document.querySelector('swp-calendar-header'); + } + getHeaderSpacer() { + return document.querySelector('swp-header-spacer'); + } + /** + * Read current max row from DOM elements + * Excludes events marked as removing (data-removing attribute) + */ + getMaxRowFromDOM() { + const container = this.getAllDayContainer(); + if (!container) + return 0; + let maxRow = 0; + const allDayEvents = container.querySelectorAll('swp-allday-event:not(.max-event-indicator):not([data-removing])'); + allDayEvents.forEach((element) => { + const htmlElement = element; + const row = parseInt(htmlElement.style.gridRow) || 1; + maxRow = Math.max(maxRow, row); + }); + return maxRow; + } + /** + * Get current gridArea for an event from DOM + */ + getGridAreaFromDOM(eventId) { + const container = this.getAllDayContainer(); + if (!container) + return null; + const element = container.querySelector(`[data-event-id="${eventId}"]`); + return element?.style.gridArea || null; + } + /** + * Count events in a specific column by reading DOM + */ + countEventsInColumnFromDOM(columnIndex) { + const container = this.getAllDayContainer(); + if (!container) + return 0; + let count = 0; + const allDayEvents = container.querySelectorAll('swp-allday-event:not(.max-event-indicator)'); + allDayEvents.forEach((element) => { + const htmlElement = element; + const gridColumn = htmlElement.style.gridColumn; + // Parse "1 / 3" format + const match = gridColumn.match(/(\d+)\s*\/\s*(\d+)/); + if (match) { + const startCol = parseInt(match[1]); + const endCol = parseInt(match[2]) - 1; // End is exclusive in CSS + if (startCol <= columnIndex && endCol >= columnIndex) { + count++; + } + } + }); + return count; + } + /** + * Calculate all-day height based on number of rows + */ + calculateAllDayHeight(targetRows) { + const root = document.documentElement; + const targetHeight = targetRows * ALL_DAY_CONSTANTS.SINGLE_ROW_HEIGHT; + // Read CSS variable directly from style property or default to 0 + const currentHeightStr = root.style.getPropertyValue('--all-day-row-height') || '0px'; + const currentHeight = parseInt(currentHeightStr) || 0; + const heightDifference = targetHeight - currentHeight; + return { targetHeight, currentHeight, heightDifference }; + } + /** + * Check current all-day events and animate to correct height + * Reads max row directly from DOM elements + */ + checkAndAnimateAllDayHeight() { + // Read max row directly from DOM + const maxRows = this.getMaxRowFromDOM(); + console.log('📊 AllDayManager: Height calculation', { + maxRows, + isExpanded: this.isExpanded + }); + // Store actual row count + this.actualRowCount = maxRows; + // Determine what to display + let displayRows = maxRows; + if (maxRows > ALL_DAY_CONSTANTS.MAX_COLLAPSED_ROWS) { + // Show chevron button + this.updateChevronButton(true); + // Show 4 rows when collapsed (3 events + indicators) + if (!this.isExpanded) { + displayRows = ALL_DAY_CONSTANTS.MAX_COLLAPSED_ROWS; + this.updateOverflowIndicators(); + } + else { + this.clearOverflowIndicators(); + } + } + else { + // Hide chevron - not needed + this.updateChevronButton(false); + this.clearOverflowIndicators(); + } + console.log('🎬 AllDayManager: Will animate to', { + displayRows, + maxRows, + willAnimate: displayRows !== this.actualRowCount + }); + console.log(`🎯 AllDayManager: Animating to ${displayRows} rows`); + // Animate to required rows (0 = collapse, >0 = expand) + this.animateToRows(displayRows); + } + /** + * Animate all-day container to specific number of rows + */ + animateToRows(targetRows) { + const { targetHeight, currentHeight, heightDifference } = this.calculateAllDayHeight(targetRows); + if (targetHeight === currentHeight) + return; // No animation needed + console.log(`🎬 All-day height animation: ${currentHeight}px → ${targetHeight}px (${Math.ceil(currentHeight / ALL_DAY_CONSTANTS.SINGLE_ROW_HEIGHT)} → ${targetRows} rows)`); + // Get cached elements + const calendarHeader = this.getCalendarHeader(); + const headerSpacer = this.getHeaderSpacer(); + const allDayContainer = this.getAllDayContainer(); + if (!calendarHeader || !allDayContainer) + return; + // Get current parent height for animation + const currentParentHeight = parseFloat(getComputedStyle(calendarHeader).height); + const targetParentHeight = currentParentHeight + heightDifference; + const animations = [ + calendarHeader.animate([ + { height: `${currentParentHeight}px` }, + { height: `${targetParentHeight}px` } + ], { + duration: 150, + easing: 'ease-out', + fill: 'forwards' + }) + ]; + // Add spacer animation if spacer exists, but don't use fill: 'forwards' + if (headerSpacer) { + const root = document.documentElement; + const headerHeightStr = root.style.getPropertyValue('--header-height'); + const headerHeight = parseInt(headerHeightStr); + const currentSpacerHeight = headerHeight + currentHeight; + const targetSpacerHeight = headerHeight + targetHeight; + animations.push(headerSpacer.animate([ + { height: `${currentSpacerHeight}px` }, + { height: `${targetSpacerHeight}px` } + ], { + duration: 150, + easing: 'ease-out' + // No fill: 'forwards' - let CSS calc() take over after animation + })); + } + // Update CSS variable after animation + Promise.all(animations.map(anim => anim.finished)).then(() => { + const root = document.documentElement; + root.style.setProperty('--all-day-row-height', `${targetHeight}px`); + eventBus.emit('header:height-changed'); + }); + } + /** + * Calculate layout for ALL all-day events using AllDayLayoutEngine + * This is the correct method that processes all events together for proper overlap detection + */ + calculateAllDayEventsLayout(events, dayHeaders) { + // Store current state + this.currentAllDayEvents = events; + this.currentWeekDates = dayHeaders; + // Initialize layout engine with provided week dates + let layoutEngine = new AllDayLayoutEngine(dayHeaders.map(column => column.date)); + // Calculate layout for all events together - AllDayLayoutEngine handles CalendarEvents directly + return layoutEngine.calculateLayout(events); + } + handleConvertToAllDay(payload) { + let allDayContainer = this.getAllDayContainer(); + if (!allDayContainer) + return; + // Create SwpAllDayEventElement from ICalendarEvent + const allDayElement = SwpAllDayEventElement.fromCalendarEvent(payload.calendarEvent); + // Apply grid positioning + allDayElement.style.gridRow = '1'; + allDayElement.style.gridColumn = payload.targetColumn.index.toString(); + // Remove old swp-event clone + payload.draggedClone.remove(); + // Call delegate to update DragDropManager's draggedClone reference + payload.replaceClone(allDayElement); + // Append to container + allDayContainer.appendChild(allDayElement); + ColumnDetectionUtils.updateColumnBoundsCache(); + // Recalculate height after adding all-day event + this.checkAndAnimateAllDayHeight(); + } + /** + * Handle drag move for all-day events - SPECIALIZED FOR ALL-DAY CONTAINER + */ + handleColumnChange(dragColumnChangeEventPayload) { + let allDayContainer = this.getAllDayContainer(); + if (!allDayContainer) + return; + let targetColumn = ColumnDetectionUtils.getColumnBounds(dragColumnChangeEventPayload.mousePosition); + if (targetColumn == null) + return; + if (!dragColumnChangeEventPayload.draggedClone) + return; + // Calculate event span from original grid positioning + const computedStyle = window.getComputedStyle(dragColumnChangeEventPayload.draggedClone); + const gridColumnStart = parseInt(computedStyle.gridColumnStart) || targetColumn.index; + const gridColumnEnd = parseInt(computedStyle.gridColumnEnd) || targetColumn.index + 1; + const span = gridColumnEnd - gridColumnStart; + // Update clone position maintaining the span + const newStartColumn = targetColumn.index; + const newEndColumn = newStartColumn + span; + dragColumnChangeEventPayload.draggedClone.style.gridColumn = `${newStartColumn} / ${newEndColumn}`; + } + fadeOutAndRemove(element) { + console.log('🗑️ AllDayManager: About to remove all-day event', { + eventId: element.dataset.eventId, + element: element.tagName + }); + // Mark element as removing so it's excluded from height calculations + element.setAttribute('data-removing', 'true'); + element.style.transition = 'opacity 0.3s ease-out'; + element.style.opacity = '0'; + setTimeout(() => { + element.remove(); + console.log('✅ AllDayManager: All-day event removed from DOM'); + }, 300); + } + /** + * Handle timed → all-day conversion on drop + */ + async handleTimedToAllDayDrop(dragEndEvent) { + if (!dragEndEvent.draggedClone || !dragEndEvent.finalPosition.column) + return; + const clone = dragEndEvent.draggedClone; + const eventId = clone.eventId.replace('clone-', ''); + const targetDate = dragEndEvent.finalPosition.column.date; + console.log('🔄 AllDayManager: Converting timed event to all-day', { eventId, targetDate }); + // Create new dates preserving time + const newStart = new Date(targetDate); + newStart.setHours(clone.start.getHours(), clone.start.getMinutes(), 0, 0); + const newEnd = new Date(targetDate); + newEnd.setHours(clone.end.getHours(), clone.end.getMinutes(), 0, 0); + // Update event in repository + await this.eventManager.updateEvent(eventId, { + start: newStart, + end: newEnd, + allDay: true + }); + // Remove original timed event + this.fadeOutAndRemove(dragEndEvent.originalElement); + // Add to current all-day events and recalculate layout + const newEvent = { + id: eventId, + title: clone.title, + start: newStart, + end: newEnd, + type: clone.type, + allDay: true, + syncStatus: 'synced' + }; + const updatedEvents = [...this.currentAllDayEvents, newEvent]; + const newLayouts = this.calculateAllDayEventsLayout(updatedEvents, this.currentWeekDates); + this.allDayEventRenderer.renderAllDayEventsForPeriod(newLayouts); + // Animate height + this.checkAndAnimateAllDayHeight(); + } + /** + * Handle all-day → all-day drop (moving within header) + */ + async handleDragEnd(dragEndEvent) { + if (!dragEndEvent.draggedClone || !dragEndEvent.finalPosition.column) + return; + const clone = dragEndEvent.draggedClone; + const eventId = clone.eventId.replace('clone-', ''); + const targetDate = dragEndEvent.finalPosition.column.date; + // Calculate duration in days + const durationDays = this.dateService.differenceInCalendarDays(clone.end, clone.start); + // Create new dates preserving time + const newStart = new Date(targetDate); + newStart.setHours(clone.start.getHours(), clone.start.getMinutes(), 0, 0); + const newEnd = new Date(targetDate); + newEnd.setDate(newEnd.getDate() + durationDays); + newEnd.setHours(clone.end.getHours(), clone.end.getMinutes(), 0, 0); + // Update event in repository + await this.eventManager.updateEvent(eventId, { + start: newStart, + end: newEnd, + allDay: true + }); + // Remove original and fade out + this.fadeOutAndRemove(dragEndEvent.originalElement); + // Recalculate and re-render ALL events + const updatedEvents = this.currentAllDayEvents.map(e => e.id === eventId ? { ...e, start: newStart, end: newEnd } : e); + const newLayouts = this.calculateAllDayEventsLayout(updatedEvents, this.currentWeekDates); + this.allDayEventRenderer.renderAllDayEventsForPeriod(newLayouts); + // Animate height - this also handles overflow classes! + this.checkAndAnimateAllDayHeight(); + } + /** + * Update chevron button visibility and state + */ + updateChevronButton(show) { + const headerSpacer = this.getHeaderSpacer(); + if (!headerSpacer) + return; + let chevron = headerSpacer.querySelector('.allday-chevron'); + if (show && !chevron) { + chevron = document.createElement('button'); + chevron.className = 'allday-chevron collapsed'; + chevron.innerHTML = ` + + + + `; + chevron.onclick = () => this.toggleExpanded(); + headerSpacer.appendChild(chevron); + } + else if (!show && chevron) { + chevron.remove(); + } + else if (chevron) { + chevron.classList.toggle('collapsed', !this.isExpanded); + chevron.classList.toggle('expanded', this.isExpanded); + } + } + /** + * Toggle between expanded and collapsed state + */ + toggleExpanded() { + this.isExpanded = !this.isExpanded; + this.checkAndAnimateAllDayHeight(); + const elements = document.querySelectorAll('swp-allday-container swp-allday-event.max-event-overflow-hide, swp-allday-container swp-allday-event.max-event-overflow-show'); + elements.forEach((element) => { + if (this.isExpanded) { + // ALTID vis når expanded=true + element.classList.remove('max-event-overflow-hide'); + element.classList.add('max-event-overflow-show'); + } + else { + // ALTID skjul når expanded=false + element.classList.remove('max-event-overflow-show'); + element.classList.add('max-event-overflow-hide'); + } + }); + } + /** + * Count number of events in a specific column using IColumnBounds + * Reads directly from DOM elements + */ + countEventsInColumn(columnBounds) { + return this.countEventsInColumnFromDOM(columnBounds.index); + } + /** + * Update overflow indicators for collapsed state + */ + updateOverflowIndicators() { + const container = this.getAllDayContainer(); + if (!container) + return; + // Create overflow indicators for each column that needs them + let columns = ColumnDetectionUtils.getColumns(); + columns.forEach((columnBounds) => { + let totalEventsInColumn = this.countEventsInColumn(columnBounds); + let overflowCount = totalEventsInColumn - ALL_DAY_CONSTANTS.MAX_COLLAPSED_ROWS; + if (overflowCount > 0) { + // Check if indicator already exists in this column + let existingIndicator = container.querySelector(`.max-event-indicator[data-column="${columnBounds.index}"]`); + if (existingIndicator) { + // Update existing indicator + existingIndicator.innerHTML = `+${overflowCount + 1} more`; + } + else { + // Create new overflow indicator element + let overflowElement = document.createElement('swp-allday-event'); + overflowElement.className = 'max-event-indicator'; + overflowElement.setAttribute('data-column', columnBounds.index.toString()); + overflowElement.style.gridRow = ALL_DAY_CONSTANTS.MAX_COLLAPSED_ROWS.toString(); + overflowElement.style.gridColumn = columnBounds.index.toString(); + overflowElement.innerHTML = `+${overflowCount + 1} more`; + overflowElement.onclick = (e) => { + e.stopPropagation(); + this.toggleExpanded(); + }; + container.appendChild(overflowElement); + } + } + }); + } + /** + * Clear overflow indicators and restore normal state + */ + clearOverflowIndicators() { + const container = this.getAllDayContainer(); + if (!container) + return; + // Remove all overflow indicator elements + container.querySelectorAll('.max-event-indicator').forEach((element) => { + element.remove(); + }); + } +} +//# sourceMappingURL=AllDayManager.js.map \ No newline at end of file diff --git a/wwwroot/js/managers/AllDayManager.js.map b/wwwroot/js/managers/AllDayManager.js.map new file mode 100644 index 0000000..64ba752 --- /dev/null +++ b/wwwroot/js/managers/AllDayManager.js.map @@ -0,0 +1 @@ +{"version":3,"file":"AllDayManager.js","sourceRoot":"","sources":["../../../src/managers/AllDayManager.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/managers/CalendarManager.d.ts b/wwwroot/js/managers/CalendarManager.d.ts new file mode 100644 index 0000000..b0cf0d2 --- /dev/null +++ b/wwwroot/js/managers/CalendarManager.d.ts @@ -0,0 +1,45 @@ +import { Configuration } from '../configurations/CalendarConfig'; +import { CalendarView, IEventBus } from '../types/CalendarTypes'; +import { EventManager } from './EventManager'; +import { GridManager } from './GridManager'; +import { EventRenderingService } from '../renderers/EventRendererManager'; +import { ScrollManager } from './ScrollManager'; +/** + * CalendarManager - Main coordinator for all calendar managers + */ +export declare class CalendarManager { + private eventBus; + private eventManager; + private gridManager; + private eventRenderer; + private scrollManager; + private config; + private currentView; + private currentDate; + private isInitialized; + constructor(eventBus: IEventBus, eventManager: EventManager, gridManager: GridManager, eventRenderingService: EventRenderingService, scrollManager: ScrollManager, config: Configuration); + /** + * Initialize calendar system using simple direct calls + */ + initialize(): Promise; + /** + * Skift calendar view (dag/uge/måned) + */ + setView(view: CalendarView): void; + /** + * Sæt aktuel dato + */ + setCurrentDate(date: Date): void; + /** + * Setup event listeners for at håndtere events fra andre managers + */ + private setupEventListeners; + /** + * Calculate the current period based on view and date + */ + private calculateCurrentPeriod; + /** + * Handle workweek configuration changes + */ + private handleWorkweekChange; +} diff --git a/wwwroot/js/managers/CalendarManager.js b/wwwroot/js/managers/CalendarManager.js new file mode 100644 index 0000000..fc0caa3 --- /dev/null +++ b/wwwroot/js/managers/CalendarManager.js @@ -0,0 +1,145 @@ +import { CoreEvents } from '../constants/CoreEvents'; +/** + * CalendarManager - Main coordinator for all calendar managers + */ +export class CalendarManager { + constructor(eventBus, eventManager, gridManager, eventRenderingService, scrollManager, config) { + this.currentView = 'week'; + this.currentDate = new Date(); + this.isInitialized = false; + this.eventBus = eventBus; + this.eventManager = eventManager; + this.gridManager = gridManager; + this.eventRenderer = eventRenderingService; + this.scrollManager = scrollManager; + this.config = config; + this.setupEventListeners(); + } + /** + * Initialize calendar system using simple direct calls + */ + async initialize() { + if (this.isInitialized) { + return; + } + try { + // Step 1: Load data + await this.eventManager.loadData(); + // Step 2: Render grid structure + await this.gridManager.render(); + this.scrollManager.initialize(); + this.setView(this.currentView); + this.setCurrentDate(this.currentDate); + this.isInitialized = true; + // Emit initialization complete event + this.eventBus.emit(CoreEvents.INITIALIZED, { + currentDate: this.currentDate, + currentView: this.currentView + }); + } + catch (error) { + throw error; + } + } + /** + * Skift calendar view (dag/uge/måned) + */ + setView(view) { + if (this.currentView === view) { + return; + } + const previousView = this.currentView; + this.currentView = view; + // Emit view change event + this.eventBus.emit(CoreEvents.VIEW_CHANGED, { + previousView, + currentView: view, + date: this.currentDate + }); + } + /** + * Sæt aktuel dato + */ + setCurrentDate(date) { + const previousDate = this.currentDate; + this.currentDate = new Date(date); + // Emit date change event + this.eventBus.emit(CoreEvents.DATE_CHANGED, { + previousDate, + currentDate: this.currentDate, + view: this.currentView + }); + } + /** + * Setup event listeners for at håndtere events fra andre managers + */ + setupEventListeners() { + // Listen for workweek changes only + this.eventBus.on(CoreEvents.WORKWEEK_CHANGED, (event) => { + const customEvent = event; + this.handleWorkweekChange(); + }); + } + /** + * Calculate the current period based on view and date + */ + calculateCurrentPeriod() { + const current = new Date(this.currentDate); + switch (this.currentView) { + case 'day': + const dayStart = new Date(current); + dayStart.setHours(0, 0, 0, 0); + const dayEnd = new Date(current); + dayEnd.setHours(23, 59, 59, 999); + return { + start: dayStart.toISOString(), + end: dayEnd.toISOString() + }; + case 'week': + // Find start of week (Monday) + const weekStart = new Date(current); + const dayOfWeek = weekStart.getDay(); + const daysToMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1; // Sunday = 0, so 6 days back to Monday + weekStart.setDate(weekStart.getDate() - daysToMonday); + weekStart.setHours(0, 0, 0, 0); + // Find end of week (Sunday) + const weekEnd = new Date(weekStart); + weekEnd.setDate(weekEnd.getDate() + 6); + weekEnd.setHours(23, 59, 59, 999); + return { + start: weekStart.toISOString(), + end: weekEnd.toISOString() + }; + case 'month': + const monthStart = new Date(current.getFullYear(), current.getMonth(), 1); + const monthEnd = new Date(current.getFullYear(), current.getMonth() + 1, 0, 23, 59, 59, 999); + return { + start: monthStart.toISOString(), + end: monthEnd.toISOString() + }; + default: + // Fallback to week view + const fallbackStart = new Date(current); + fallbackStart.setDate(fallbackStart.getDate() - 3); + fallbackStart.setHours(0, 0, 0, 0); + const fallbackEnd = new Date(current); + fallbackEnd.setDate(fallbackEnd.getDate() + 3); + fallbackEnd.setHours(23, 59, 59, 999); + return { + start: fallbackStart.toISOString(), + end: fallbackEnd.toISOString() + }; + } + } + /** + * Handle workweek configuration changes + */ + handleWorkweekChange() { + // Simply relay the event - workweek info is in the WORKWEEK_CHANGED event + this.eventBus.emit('workweek:header-update', { + currentDate: this.currentDate, + currentView: this.currentView + }); + } +} +//# sourceMappingURL=CalendarManager.js.map \ No newline at end of file diff --git a/wwwroot/js/managers/CalendarManager.js.map b/wwwroot/js/managers/CalendarManager.js.map new file mode 100644 index 0000000..38d05f7 --- /dev/null +++ b/wwwroot/js/managers/CalendarManager.js.map @@ -0,0 +1 @@ +{"version":3,"file":"CalendarManager.js","sourceRoot":"","sources":["../../../src/managers/CalendarManager.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/managers/DragDropManager.d.ts b/wwwroot/js/managers/DragDropManager.d.ts new file mode 100644 index 0000000..f9d266d --- /dev/null +++ b/wwwroot/js/managers/DragDropManager.d.ts @@ -0,0 +1,222 @@ +/** + * DragDropManager - Advanced drag-and-drop system with smooth animations and event type conversion + * + * ARCHITECTURE OVERVIEW: + * ===================== + * DragDropManager provides a sophisticated drag-and-drop system for calendar events that supports: + * - Smooth animated dragging with requestAnimationFrame + * - Automatic event type conversion (timed events ↔ all-day events) + * - Scroll compensation during edge scrolling + * - Grid snapping for precise event placement + * - Column detection and change tracking + * + * KEY FEATURES: + * ============= + * 1. DRAG DETECTION + * - Movement threshold (5px) to distinguish clicks from drags + * - Immediate visual feedback with cloned element + * - Mouse offset tracking for natural drag feel + * + * 2. SMOOTH ANIMATION + * - Uses requestAnimationFrame for 60fps animations + * - Interpolated movement (30% per frame) for smooth transitions + * - Continuous drag:move events for real-time updates + * + * 3. EVENT TYPE CONVERSION + * - Timed → All-day: When dragging into calendar header + * - All-day → Timed: When dragging into day columns + * - Automatic clone replacement with appropriate element type + * + * 4. SCROLL COMPENSATION + * - Tracks scroll delta during edge-scrolling + * - Compensates dragged element position during scroll + * - Prevents visual "jumping" when scrolling while dragging + * + * 5. GRID SNAPPING + * - Snaps to time grid on mouse up + * - Uses PositionUtils for consistent positioning + * - Accounts for mouse offset within event + * + * STATE MANAGEMENT: + * ================= + * Mouse Tracking: + * - mouseDownPosition: Initial click position + * - currentMousePosition: Latest mouse position + * - mouseOffset: Click offset within event (for natural dragging) + * + * Drag State: + * - originalElement: Source event being dragged + * - draggedClone: Animated clone following mouse + * - currentColumn: Column mouse is currently over + * - previousColumn: Last column (for detecting changes) + * - isDragStarted: Whether drag threshold exceeded + * + * Scroll State: + * - scrollDeltaY: Accumulated scroll offset during drag + * - lastScrollTop: Previous scroll position + * - isScrollCompensating: Whether edge-scroll is active + * + * Animation State: + * - dragAnimationId: requestAnimationFrame ID + * - targetY: Desired position for smooth interpolation + * - currentY: Current interpolated position + * + * EVENT FLOW: + * =========== + * 1. Mouse Down (handleMouseDown) + * ├─ Store originalElement and mouse offset + * └─ Wait for movement + * + * 2. Mouse Move (handleMouseMove) + * ├─ Check movement threshold + * ├─ Initialize drag if threshold exceeded (initializeDrag) + * │ ├─ Create clone + * │ ├─ Emit drag:start + * │ └─ Start animation loop + * ├─ Continue drag (continueDrag) + * │ ├─ Calculate target position with scroll compensation + * │ └─ Update animation target + * └─ Detect column changes (detectColumnChange) + * └─ Emit drag:column-change + * + * 3. Animation Loop (animateDrag) + * ├─ Interpolate currentY toward targetY + * ├─ Emit drag:move on each frame + * └─ Schedule next frame until target reached + * + * 4. Event Type Conversion + * ├─ Entering header (handleHeaderMouseEnter) + * │ ├─ Emit drag:mouseenter-header + * │ └─ AllDayManager creates all-day clone + * └─ Entering column (handleColumnMouseEnter) + * ├─ Emit drag:mouseenter-column + * └─ EventRenderingService creates timed clone + * + * 5. Mouse Up (handleMouseUp) + * ├─ Stop animation + * ├─ Snap to grid + * ├─ Detect drop target (header or column) + * ├─ Emit drag:end with final position + * └─ Cleanup drag state + * + * SCROLL COMPENSATION SYSTEM: + * =========================== + * Problem: When EdgeScrollManager scrolls the grid during drag, the dragged element + * can appear to "jump" because the mouse position stays the same but the + * coordinate system (scrollable content) has moved. + * + * Solution: Track cumulative scroll delta and add it to mouse position calculations + * + * Flow: + * 1. EdgeScrollManager starts scrolling → emit edgescroll:started + * 2. DragDropManager sets isScrollCompensating = true + * 3. On each scroll event: + * ├─ Calculate scrollDelta = currentScrollTop - lastScrollTop + * ├─ Accumulate into scrollDeltaY + * └─ Call continueDrag with adjusted position + * 4. continueDrag adds scrollDeltaY to mouse Y coordinate + * 5. On event conversion, reset scrollDeltaY (new clone, new coordinate system) + * + * PERFORMANCE OPTIMIZATIONS: + * ========================== + * - Uses ColumnDetectionUtils cache for fast column lookups + * - Single requestAnimationFrame loop (not per-mousemove) + * - Interpolated animation reduces update frequency + * - Passive scroll listeners + * - Event delegation for header/column detection + * + * USAGE: + * ====== + * const dragDropManager = new DragDropManager(eventBus, positionUtils); + * // Automatically attaches event listeners and manages drag lifecycle + * // Other managers listen to drag:start, drag:move, drag:end, etc. + */ +import { IEventBus } from '../types/CalendarTypes'; +import { PositionUtils } from '../utils/PositionUtils'; +export declare class DragDropManager { + private eventBus; + private mouseDownPosition; + private currentMousePosition; + private mouseOffset; + private originalElement; + private draggedClone; + private currentColumn; + private previousColumn; + private originalSourceColumn; + private isDragStarted; + private readonly dragThreshold; + private scrollableContent; + private scrollDeltaY; + private lastScrollTop; + private isScrollCompensating; + private dragAnimationId; + private targetY; + private currentY; + private targetColumn; + private positionUtils; + constructor(eventBus: IEventBus, positionUtils: PositionUtils); + /** + * Initialize with optimized event listener setup + */ + private init; + private handleGridRendered; + private handleMouseDown; + private handleMouseMove; + /** + * Try to initialize drag based on movement threshold + * Returns true if drag was initialized, false if not enough movement + */ + private initializeDrag; + private continueDrag; + /** + * Detect column change and emit event + */ + private detectColumnChange; + /** + * Optimized mouse up handler with consolidated cleanup + */ + private handleMouseUp; + private cleanupAllClones; + /** + * Cancel drag operation when mouse leaves grid container + * Animates clone back to original position before cleanup + */ + private cancelDrag; + /** + * Optimized snap position calculation using PositionUtils + */ + private calculateSnapPosition; + /** + * Smooth drag animation using requestAnimationFrame + * Emits drag:move events with current draggedClone reference on each frame + */ + private animateDrag; + /** + * Handle scroll during drag - update scrollDeltaY and call continueDrag + */ + private handleScroll; + /** + * Stop drag animation + */ + private stopDragAnimation; + /** + * Clean up drag state + */ + private cleanupDragState; + /** + * Detect drop target - whether dropped in swp-day-column or swp-day-header + */ + private detectDropTarget; + /** + * Handle mouse enter on calendar header - simplified using native events + */ + private handleHeaderMouseEnter; + /** + * Handle mouse enter on day column - for converting all-day to timed events + */ + private handleColumnMouseEnter; + /** + * Handle mouse leave from calendar header - simplified using native events + */ + private handleHeaderMouseLeave; +} diff --git a/wwwroot/js/managers/DragDropManager.js b/wwwroot/js/managers/DragDropManager.js new file mode 100644 index 0000000..5801e24 --- /dev/null +++ b/wwwroot/js/managers/DragDropManager.js @@ -0,0 +1,626 @@ +/** + * DragDropManager - Advanced drag-and-drop system with smooth animations and event type conversion + * + * ARCHITECTURE OVERVIEW: + * ===================== + * DragDropManager provides a sophisticated drag-and-drop system for calendar events that supports: + * - Smooth animated dragging with requestAnimationFrame + * - Automatic event type conversion (timed events ↔ all-day events) + * - Scroll compensation during edge scrolling + * - Grid snapping for precise event placement + * - Column detection and change tracking + * + * KEY FEATURES: + * ============= + * 1. DRAG DETECTION + * - Movement threshold (5px) to distinguish clicks from drags + * - Immediate visual feedback with cloned element + * - Mouse offset tracking for natural drag feel + * + * 2. SMOOTH ANIMATION + * - Uses requestAnimationFrame for 60fps animations + * - Interpolated movement (30% per frame) for smooth transitions + * - Continuous drag:move events for real-time updates + * + * 3. EVENT TYPE CONVERSION + * - Timed → All-day: When dragging into calendar header + * - All-day → Timed: When dragging into day columns + * - Automatic clone replacement with appropriate element type + * + * 4. SCROLL COMPENSATION + * - Tracks scroll delta during edge-scrolling + * - Compensates dragged element position during scroll + * - Prevents visual "jumping" when scrolling while dragging + * + * 5. GRID SNAPPING + * - Snaps to time grid on mouse up + * - Uses PositionUtils for consistent positioning + * - Accounts for mouse offset within event + * + * STATE MANAGEMENT: + * ================= + * Mouse Tracking: + * - mouseDownPosition: Initial click position + * - currentMousePosition: Latest mouse position + * - mouseOffset: Click offset within event (for natural dragging) + * + * Drag State: + * - originalElement: Source event being dragged + * - draggedClone: Animated clone following mouse + * - currentColumn: Column mouse is currently over + * - previousColumn: Last column (for detecting changes) + * - isDragStarted: Whether drag threshold exceeded + * + * Scroll State: + * - scrollDeltaY: Accumulated scroll offset during drag + * - lastScrollTop: Previous scroll position + * - isScrollCompensating: Whether edge-scroll is active + * + * Animation State: + * - dragAnimationId: requestAnimationFrame ID + * - targetY: Desired position for smooth interpolation + * - currentY: Current interpolated position + * + * EVENT FLOW: + * =========== + * 1. Mouse Down (handleMouseDown) + * ├─ Store originalElement and mouse offset + * └─ Wait for movement + * + * 2. Mouse Move (handleMouseMove) + * ├─ Check movement threshold + * ├─ Initialize drag if threshold exceeded (initializeDrag) + * │ ├─ Create clone + * │ ├─ Emit drag:start + * │ └─ Start animation loop + * ├─ Continue drag (continueDrag) + * │ ├─ Calculate target position with scroll compensation + * │ └─ Update animation target + * └─ Detect column changes (detectColumnChange) + * └─ Emit drag:column-change + * + * 3. Animation Loop (animateDrag) + * ├─ Interpolate currentY toward targetY + * ├─ Emit drag:move on each frame + * └─ Schedule next frame until target reached + * + * 4. Event Type Conversion + * ├─ Entering header (handleHeaderMouseEnter) + * │ ├─ Emit drag:mouseenter-header + * │ └─ AllDayManager creates all-day clone + * └─ Entering column (handleColumnMouseEnter) + * ├─ Emit drag:mouseenter-column + * └─ EventRenderingService creates timed clone + * + * 5. Mouse Up (handleMouseUp) + * ├─ Stop animation + * ├─ Snap to grid + * ├─ Detect drop target (header or column) + * ├─ Emit drag:end with final position + * └─ Cleanup drag state + * + * SCROLL COMPENSATION SYSTEM: + * =========================== + * Problem: When EdgeScrollManager scrolls the grid during drag, the dragged element + * can appear to "jump" because the mouse position stays the same but the + * coordinate system (scrollable content) has moved. + * + * Solution: Track cumulative scroll delta and add it to mouse position calculations + * + * Flow: + * 1. EdgeScrollManager starts scrolling → emit edgescroll:started + * 2. DragDropManager sets isScrollCompensating = true + * 3. On each scroll event: + * ├─ Calculate scrollDelta = currentScrollTop - lastScrollTop + * ├─ Accumulate into scrollDeltaY + * └─ Call continueDrag with adjusted position + * 4. continueDrag adds scrollDeltaY to mouse Y coordinate + * 5. On event conversion, reset scrollDeltaY (new clone, new coordinate system) + * + * PERFORMANCE OPTIMIZATIONS: + * ========================== + * - Uses ColumnDetectionUtils cache for fast column lookups + * - Single requestAnimationFrame loop (not per-mousemove) + * - Interpolated animation reduces update frequency + * - Passive scroll listeners + * - Event delegation for header/column detection + * + * USAGE: + * ====== + * const dragDropManager = new DragDropManager(eventBus, positionUtils); + * // Automatically attaches event listeners and manages drag lifecycle + * // Other managers listen to drag:start, drag:move, drag:end, etc. + */ +import { ColumnDetectionUtils } from '../utils/ColumnDetectionUtils'; +import { SwpEventElement } from '../elements/SwpEventElement'; +import { CoreEvents } from '../constants/CoreEvents'; +export class DragDropManager { + constructor(eventBus, positionUtils) { + // Mouse tracking with optimized state + this.mouseDownPosition = { x: 0, y: 0 }; + this.currentMousePosition = { x: 0, y: 0 }; + this.mouseOffset = { x: 0, y: 0 }; + this.currentColumn = null; + this.previousColumn = null; + this.originalSourceColumn = null; // Track original start column + this.isDragStarted = false; + // Movement threshold to distinguish click from drag + this.dragThreshold = 5; // pixels + // Scroll compensation + this.scrollableContent = null; + this.scrollDeltaY = 0; // Current scroll delta to apply in continueDrag + this.lastScrollTop = 0; // Last scroll position for delta calculation + this.isScrollCompensating = false; // Track if scroll compensation is active + // Smooth drag animation + this.dragAnimationId = null; + this.targetY = 0; + this.currentY = 0; + this.targetColumn = null; + this.eventBus = eventBus; + this.positionUtils = positionUtils; + this.init(); + } + /** + * Initialize with optimized event listener setup + */ + init() { + // Add event listeners + document.body.addEventListener('mousemove', this.handleMouseMove.bind(this)); + document.body.addEventListener('mousedown', this.handleMouseDown.bind(this)); + document.body.addEventListener('mouseup', this.handleMouseUp.bind(this)); + const calendarContainer = document.querySelector('swp-calendar-container'); + if (calendarContainer) { + calendarContainer.addEventListener('mouseleave', () => { + if (this.originalElement && this.isDragStarted) { + this.cancelDrag(); + } + }); + // Event delegation for header enter/leave + calendarContainer.addEventListener('mouseenter', (e) => { + const target = e.target; + if (target.closest('swp-calendar-header')) { + this.handleHeaderMouseEnter(e); + } + else if (target.closest('swp-day-column')) { + this.handleColumnMouseEnter(e); + } + }, true); // Use capture phase + calendarContainer.addEventListener('mouseleave', (e) => { + const target = e.target; + if (target.closest('swp-calendar-header')) { + this.handleHeaderMouseLeave(e); + } + // Don't handle swp-event mouseleave here - let mousemove handle it + }, true); // Use capture phase + } + // Initialize column bounds cache + ColumnDetectionUtils.updateColumnBoundsCache(); + // Listen to resize events to update cache + window.addEventListener('resize', () => { + ColumnDetectionUtils.updateColumnBoundsCache(); + }); + // Listen to navigation events to update cache + this.eventBus.on('navigation:completed', () => { + ColumnDetectionUtils.updateColumnBoundsCache(); + }); + this.eventBus.on(CoreEvents.GRID_RENDERED, (event) => { + this.handleGridRendered(event); + }); + // Listen to edge-scroll events to control scroll compensation + this.eventBus.on('edgescroll:started', () => { + this.isScrollCompensating = true; + // Gem nuværende scroll position for delta beregning + if (this.scrollableContent) { + this.lastScrollTop = this.scrollableContent.scrollTop; + } + }); + this.eventBus.on('edgescroll:stopped', () => { + this.isScrollCompensating = false; + }); + // Reset scrollDeltaY when event converts (new clone created) + this.eventBus.on('drag:mouseenter-header', () => { + this.scrollDeltaY = 0; + this.lastScrollTop = 0; + }); + this.eventBus.on('drag:mouseenter-column', () => { + this.scrollDeltaY = 0; + this.lastScrollTop = 0; + }); + } + handleGridRendered(event) { + this.scrollableContent = document.querySelector('swp-scrollable-content'); + this.scrollableContent.addEventListener('scroll', this.handleScroll.bind(this), { passive: true }); + } + handleMouseDown(event) { + // Clean up drag state first + this.cleanupDragState(); + ColumnDetectionUtils.updateColumnBoundsCache(); + //this.lastMousePosition = { x: event.clientX, y: event.clientY }; + //this.initialMousePosition = { x: event.clientX, y: event.clientY }; + // Check if mousedown is on an event + const target = event.target; + if (target.closest('swp-resize-handle')) + return; + let eventElement = target; + while (eventElement && eventElement.tagName !== 'SWP-GRID-CONTAINER') { + if (eventElement.tagName === 'SWP-EVENT' || eventElement.tagName === 'SWP-ALLDAY-EVENT') { + break; + } + eventElement = eventElement.parentElement; + if (!eventElement) + return; + } + if (eventElement) { + // Normal drag - prepare for potential dragging + this.originalElement = eventElement; + // Calculate mouse offset within event + const eventRect = eventElement.getBoundingClientRect(); + this.mouseOffset = { + x: event.clientX - eventRect.left, + y: event.clientY - eventRect.top + }; + this.mouseDownPosition = { x: event.clientX, y: event.clientY }; + } + } + handleMouseMove(event) { + if (event.buttons === 1) { + // Always update mouse position from event + this.currentMousePosition = { x: event.clientX, y: event.clientY }; + // Try to initialize drag if not started + if (!this.isDragStarted && this.originalElement) { + if (!this.initializeDrag(this.currentMousePosition)) { + return; // Not enough movement yet + } + } + // Continue drag if started (også under scroll - accumulatedScrollDelta kompenserer) + if (this.isDragStarted && this.originalElement && this.draggedClone) { + this.continueDrag(this.currentMousePosition); + this.detectColumnChange(this.currentMousePosition); + } + } + } + /** + * Try to initialize drag based on movement threshold + * Returns true if drag was initialized, false if not enough movement + */ + initializeDrag(currentPosition) { + const deltaX = Math.abs(currentPosition.x - this.mouseDownPosition.x); + const deltaY = Math.abs(currentPosition.y - this.mouseDownPosition.y); + const totalMovement = Math.sqrt(deltaX * deltaX + deltaY * deltaY); + if (totalMovement < this.dragThreshold) { + return false; // Not enough movement + } + // Start drag + this.isDragStarted = true; + // Set high z-index on event-group if exists, otherwise on event itself + const eventGroup = this.originalElement.closest('swp-event-group'); + if (eventGroup) { + eventGroup.style.zIndex = '9999'; + } + else { + this.originalElement.style.zIndex = '9999'; + } + const originalElement = this.originalElement; + this.currentColumn = ColumnDetectionUtils.getColumnBounds(currentPosition); + this.originalSourceColumn = this.currentColumn; // Store original source column at drag start + this.draggedClone = originalElement.createClone(); + const dragStartPayload = { + originalElement: this.originalElement, + draggedClone: this.draggedClone, + mousePosition: this.mouseDownPosition, + mouseOffset: this.mouseOffset, + columnBounds: this.currentColumn + }; + this.eventBus.emit('drag:start', dragStartPayload); + return true; + } + continueDrag(currentPosition) { + if (!this.draggedClone.hasAttribute("data-allday")) { + // Calculate raw position from mouse (no snapping) + const column = ColumnDetectionUtils.getColumnBounds(currentPosition); + if (column) { + // Calculate raw Y position relative to column (accounting for mouse offset) + const columnRect = column.boundingClientRect; + // Beregn position fra mus + scroll delta kompensation + const adjustedMouseY = currentPosition.y + this.scrollDeltaY; + const eventTopY = adjustedMouseY - columnRect.top - this.mouseOffset.y; + this.targetY = Math.max(0, eventTopY); + this.targetColumn = column; + // Start animation loop if not already running + if (this.dragAnimationId === null) { + this.currentY = parseFloat(this.draggedClone.style.top) || 0; + this.animateDrag(); + } + } + } + } + /** + * Detect column change and emit event + */ + detectColumnChange(currentPosition) { + const newColumn = ColumnDetectionUtils.getColumnBounds(currentPosition); + if (newColumn == null) + return; + if (newColumn.index !== this.currentColumn?.index) { + this.previousColumn = this.currentColumn; + this.currentColumn = newColumn; + const dragColumnChangePayload = { + originalElement: this.originalElement, + draggedClone: this.draggedClone, + previousColumn: this.previousColumn, + newColumn, + mousePosition: currentPosition + }; + this.eventBus.emit('drag:column-change', dragColumnChangePayload); + } + } + /** + * Optimized mouse up handler with consolidated cleanup + */ + handleMouseUp(event) { + this.stopDragAnimation(); + if (this.originalElement) { + // Only emit drag:end if drag was actually started + if (this.isDragStarted) { + const mousePosition = { x: event.clientX, y: event.clientY }; + // Snap to grid on mouse up (like ResizeHandleManager) + const column = ColumnDetectionUtils.getColumnBounds(mousePosition); + if (!column) + return; + // Get current position and snap it to grid + const snappedY = this.calculateSnapPosition(mousePosition.y, column); + // Update clone to snapped position immediately + if (this.draggedClone) { + this.draggedClone.style.top = `${snappedY}px`; + } + // Detect drop target (swp-day-column or swp-day-header) + const dropTarget = this.detectDropTarget(mousePosition); + if (!dropTarget) + throw "dropTarget is null"; + const dragEndPayload = { + originalElement: this.originalElement, + draggedClone: this.draggedClone, + mousePosition, + originalSourceColumn: this.originalSourceColumn, + finalPosition: { column, snappedY }, // Where drag ended + target: dropTarget + }; + this.eventBus.emit('drag:end', dragEndPayload); + this.cleanupDragState(); + } + else { + // This was just a click - emit click event instead + this.eventBus.emit('event:click', { + clickedElement: this.originalElement, + mousePosition: { x: event.clientX, y: event.clientY } + }); + } + } + } + // Add a cleanup method that finds and removes ALL clones + cleanupAllClones() { + // Remove clones from all possible locations + const allClones = document.querySelectorAll('[data-event-id^="clone"]'); + if (allClones.length > 0) { + allClones.forEach(clone => clone.remove()); + } + } + /** + * Cancel drag operation when mouse leaves grid container + * Animates clone back to original position before cleanup + */ + cancelDrag() { + if (!this.originalElement || !this.draggedClone) + return; + // Get current clone position + const cloneRect = this.draggedClone.getBoundingClientRect(); + // Get original element position + const originalRect = this.originalElement.getBoundingClientRect(); + // Calculate distance to animate + const deltaX = originalRect.left - cloneRect.left; + const deltaY = originalRect.top - cloneRect.top; + // Add transition for smooth animation + this.draggedClone.style.transition = 'transform 300ms ease-out'; + this.draggedClone.style.transform = `translate(${deltaX}px, ${deltaY}px)`; + // Wait for animation to complete, then cleanup + setTimeout(() => { + this.cleanupAllClones(); + if (this.originalElement) { + this.originalElement.style.opacity = ''; + this.originalElement.style.cursor = ''; + } + this.eventBus.emit('drag:cancelled', { + originalElement: this.originalElement, + reason: 'mouse-left-grid' + }); + this.cleanupDragState(); + this.stopDragAnimation(); + }, 300); + } + /** + * Optimized snap position calculation using PositionUtils + */ + calculateSnapPosition(mouseY, column) { + // Calculate where the event top would be (accounting for mouse offset) + const eventTopY = mouseY - this.mouseOffset.y; + // Snap the event top position, not the mouse position + const snappedY = this.positionUtils.getPositionFromCoordinate(eventTopY, column); + return Math.max(0, snappedY); + } + /** + * Smooth drag animation using requestAnimationFrame + * Emits drag:move events with current draggedClone reference on each frame + */ + animateDrag() { + if (!this.isDragStarted || !this.draggedClone || !this.targetColumn) { + this.dragAnimationId = null; + return; + } + // Smooth interpolation towards target + const diff = this.targetY - this.currentY; + const step = diff * 0.3; // 30% of distance per frame + // Update if difference is significant + if (Math.abs(diff) > 0.5) { + this.currentY += step; + // Emit drag:move event with current draggedClone reference + const dragMovePayload = { + originalElement: this.originalElement, + draggedClone: this.draggedClone, // Always uses current reference + mousePosition: this.currentMousePosition, // Use current mouse position! + snappedY: this.currentY, + columnBounds: this.targetColumn, + mouseOffset: this.mouseOffset + }; + this.eventBus.emit('drag:move', dragMovePayload); + this.dragAnimationId = requestAnimationFrame(() => this.animateDrag()); + } + else { + // Close enough - snap to target + this.currentY = this.targetY; + // Emit final position + const dragMovePayload = { + originalElement: this.originalElement, + draggedClone: this.draggedClone, + mousePosition: this.currentMousePosition, // Use current mouse position! + snappedY: this.currentY, + columnBounds: this.targetColumn, + mouseOffset: this.mouseOffset + }; + this.eventBus.emit('drag:move', dragMovePayload); + this.dragAnimationId = null; + } + } + /** + * Handle scroll during drag - update scrollDeltaY and call continueDrag + */ + handleScroll() { + if (!this.isDragStarted || !this.draggedClone || !this.scrollableContent || !this.isScrollCompensating) + return; + const currentScrollTop = this.scrollableContent.scrollTop; + const scrollDelta = currentScrollTop - this.lastScrollTop; + // Gem scroll delta for continueDrag + this.scrollDeltaY += scrollDelta; + this.lastScrollTop = currentScrollTop; + // Kald continueDrag med nuværende mus position + this.continueDrag(this.currentMousePosition); + } + /** + * Stop drag animation + */ + stopDragAnimation() { + if (this.dragAnimationId !== null) { + cancelAnimationFrame(this.dragAnimationId); + this.dragAnimationId = null; + } + } + /** + * Clean up drag state + */ + cleanupDragState() { + this.previousColumn = null; + this.originalElement = null; + this.draggedClone = null; + this.currentColumn = null; + this.originalSourceColumn = null; + this.isDragStarted = false; + this.scrollDeltaY = 0; + this.lastScrollTop = 0; + } + /** + * Detect drop target - whether dropped in swp-day-column or swp-day-header + */ + detectDropTarget(position) { + // Traverse up the DOM tree to find the target container + let currentElement = this.draggedClone; + while (currentElement && currentElement !== document.body) { + if (currentElement.tagName === 'SWP-ALLDAY-CONTAINER') { + return 'swp-day-header'; + } + if (currentElement.tagName === 'SWP-DAY-COLUMN') { + return 'swp-day-column'; + } + currentElement = currentElement.parentElement; + } + return null; + } + /** + * Handle mouse enter on calendar header - simplified using native events + */ + handleHeaderMouseEnter(event) { + // Only handle if we're dragging a timed event (not all-day) + if (!this.isDragStarted || !this.draggedClone) { + return; + } + const position = { x: event.clientX, y: event.clientY }; + const targetColumn = ColumnDetectionUtils.getColumnBounds(position); + if (targetColumn) { + const calendarEvent = SwpEventElement.extractCalendarEventFromElement(this.draggedClone); + const dragMouseEnterPayload = { + targetColumn: targetColumn, + mousePosition: position, + originalElement: this.originalElement, + draggedClone: this.draggedClone, + calendarEvent: calendarEvent, + replaceClone: (newClone) => { + this.draggedClone = newClone; + this.dragAnimationId === null; + } + }; + this.eventBus.emit('drag:mouseenter-header', dragMouseEnterPayload); + } + } + /** + * Handle mouse enter on day column - for converting all-day to timed events + */ + handleColumnMouseEnter(event) { + // Only handle if we're dragging an all-day event + if (!this.isDragStarted || !this.draggedClone || !this.draggedClone.hasAttribute('data-allday')) { + return; + } + const position = { x: event.clientX, y: event.clientY }; + const targetColumn = ColumnDetectionUtils.getColumnBounds(position); + if (!targetColumn) { + return; + } + // Calculate snapped Y position + const snappedY = this.calculateSnapPosition(position.y, targetColumn); + // Extract ICalendarEvent from the dragged clone + const calendarEvent = SwpEventElement.extractCalendarEventFromElement(this.draggedClone); + const dragMouseEnterPayload = { + targetColumn: targetColumn, + mousePosition: position, + snappedY: snappedY, + originalElement: this.originalElement, + draggedClone: this.draggedClone, + calendarEvent: calendarEvent, + replaceClone: (newClone) => { + this.draggedClone = newClone; + this.dragAnimationId === null; + this.stopDragAnimation(); + } + }; + this.eventBus.emit('drag:mouseenter-column', dragMouseEnterPayload); + } + /** + * Handle mouse leave from calendar header - simplified using native events + */ + handleHeaderMouseLeave(event) { + // Only handle if we're dragging an all-day event + if (!this.isDragStarted || !this.draggedClone || !this.draggedClone.hasAttribute("data-allday")) { + return; + } + const position = { x: event.clientX, y: event.clientY }; + const targetColumn = ColumnDetectionUtils.getColumnBounds(position); + if (!targetColumn) { + return; + } + const dragMouseLeavePayload = { + targetDate: targetColumn.date, + mousePosition: position, + originalElement: this.originalElement, + draggedClone: this.draggedClone + }; + this.eventBus.emit('drag:mouseleave-header', dragMouseLeavePayload); + } +} +//# sourceMappingURL=DragDropManager.js.map \ No newline at end of file diff --git a/wwwroot/js/managers/DragDropManager.js.map b/wwwroot/js/managers/DragDropManager.js.map new file mode 100644 index 0000000..fc410e8 --- /dev/null +++ b/wwwroot/js/managers/DragDropManager.js.map @@ -0,0 +1 @@ +{"version":3,"file":"DragDropManager.js","sourceRoot":"","sources":["../../../src/managers/DragDropManager.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/managers/DragHoverManager.d.ts b/wwwroot/js/managers/DragHoverManager.d.ts new file mode 100644 index 0000000..000bddb --- /dev/null +++ b/wwwroot/js/managers/DragHoverManager.d.ts @@ -0,0 +1,31 @@ +/** + * DragHoverManager - Handles event hover tracking + * Fully autonomous - listens to mouse events and manages hover state independently + */ +import { IEventBus } from '../types/CalendarTypes'; +export declare class DragHoverManager { + private eventBus; + private isHoverTrackingActive; + private currentHoveredEvent; + private calendarContainer; + constructor(eventBus: IEventBus); + private init; + private setupEventListeners; + /** + * Handle mouse enter on swp-event - activate hover tracking + */ + private handleEventMouseEnter; + /** + * Check if mouse is still over the currently hovered event + */ + private checkEventHover; + /** + * Clear hover state + */ + private clearEventHover; + /** + * Deactivate hover tracking and clear any current hover + * Called via event bus when drag starts + */ + private deactivateTracking; +} diff --git a/wwwroot/js/managers/DragHoverManager.js b/wwwroot/js/managers/DragHoverManager.js new file mode 100644 index 0000000..c92b9f3 --- /dev/null +++ b/wwwroot/js/managers/DragHoverManager.js @@ -0,0 +1,101 @@ +/** + * DragHoverManager - Handles event hover tracking + * Fully autonomous - listens to mouse events and manages hover state independently + */ +export class DragHoverManager { + constructor(eventBus) { + this.eventBus = eventBus; + this.isHoverTrackingActive = false; + this.currentHoveredEvent = null; + this.calendarContainer = null; + this.init(); + } + init() { + // Wait for DOM to be ready + setTimeout(() => { + this.calendarContainer = document.querySelector('swp-calendar-container'); + if (this.calendarContainer) { + this.setupEventListeners(); + } + }, 100); + // Listen to drag start to deactivate hover tracking + this.eventBus.on('drag:start', () => { + this.deactivateTracking(); + }); + } + setupEventListeners() { + if (!this.calendarContainer) + return; + // Listen to mouseenter on events (using event delegation) + this.calendarContainer.addEventListener('mouseenter', (e) => { + const target = e.target; + const eventElement = target.closest('swp-event'); + if (eventElement) { + this.handleEventMouseEnter(e, eventElement); + } + }, true); // Use capture phase + // Listen to mousemove globally to track when mouse leaves event bounds + document.body.addEventListener('mousemove', (e) => { + if (this.isHoverTrackingActive && e.buttons === 0) { + this.checkEventHover(e); + } + }); + } + /** + * Handle mouse enter on swp-event - activate hover tracking + */ + handleEventMouseEnter(event, eventElement) { + // Only handle hover if mouse button is up + if (event.buttons === 0) { + // Clear any previous hover first + if (this.currentHoveredEvent && this.currentHoveredEvent !== eventElement) { + this.currentHoveredEvent.classList.remove('hover'); + } + this.isHoverTrackingActive = true; + this.currentHoveredEvent = eventElement; + eventElement.classList.add('hover'); + this.eventBus.emit('event:hover:start', { element: eventElement }); + } + } + /** + * Check if mouse is still over the currently hovered event + */ + checkEventHover(event) { + // Only track hover when active and mouse button is up + if (!this.isHoverTrackingActive || !this.currentHoveredEvent) + return; + const rect = this.currentHoveredEvent.getBoundingClientRect(); + const mouseX = event.clientX; + const mouseY = event.clientY; + // Check if mouse is still within the current hovered event + const isStillInside = mouseX >= rect.left && mouseX <= rect.right && + mouseY >= rect.top && mouseY <= rect.bottom; + // If mouse left the event + if (!isStillInside) { + // Only disable tracking and clear if mouse is NOT pressed (allow resize to work) + if (event.buttons === 0) { + this.isHoverTrackingActive = false; + this.clearEventHover(); + } + } + } + /** + * Clear hover state + */ + clearEventHover() { + if (this.currentHoveredEvent) { + this.currentHoveredEvent.classList.remove('hover'); + this.eventBus.emit('event:hover:end', { element: this.currentHoveredEvent }); + this.currentHoveredEvent = null; + } + } + /** + * Deactivate hover tracking and clear any current hover + * Called via event bus when drag starts + */ + deactivateTracking() { + this.isHoverTrackingActive = false; + this.clearEventHover(); + } +} +//# sourceMappingURL=DragHoverManager.js.map \ No newline at end of file diff --git a/wwwroot/js/managers/DragHoverManager.js.map b/wwwroot/js/managers/DragHoverManager.js.map new file mode 100644 index 0000000..fdda8d6 --- /dev/null +++ b/wwwroot/js/managers/DragHoverManager.js.map @@ -0,0 +1 @@ +{"version":3,"file":"DragHoverManager.js","sourceRoot":"","sources":["../../../src/managers/DragHoverManager.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/managers/EdgeScrollManager.d.ts b/wwwroot/js/managers/EdgeScrollManager.d.ts new file mode 100644 index 0000000..da8cdda --- /dev/null +++ b/wwwroot/js/managers/EdgeScrollManager.d.ts @@ -0,0 +1,30 @@ +/** + * EdgeScrollManager - Auto-scroll when dragging near edges + * Uses time-based scrolling with 2-zone system for variable speed + */ +import { IEventBus } from '../types/CalendarTypes'; +export declare class EdgeScrollManager { + private eventBus; + private scrollableContent; + private timeGrid; + private draggedClone; + private scrollRAF; + private mouseY; + private isDragging; + private isScrolling; + private lastTs; + private rect; + private initialScrollTop; + private scrollListener; + private readonly OUTER_ZONE; + private readonly INNER_ZONE; + private readonly SLOW_SPEED_PXS; + private readonly FAST_SPEED_PXS; + constructor(eventBus: IEventBus); + private init; + private subscribeToEvents; + private startDrag; + private stopDrag; + private handleScroll; + private scrollTick; +} diff --git a/wwwroot/js/managers/EdgeScrollManager.js b/wwwroot/js/managers/EdgeScrollManager.js new file mode 100644 index 0000000..7855e51 --- /dev/null +++ b/wwwroot/js/managers/EdgeScrollManager.js @@ -0,0 +1,191 @@ +/** + * EdgeScrollManager - Auto-scroll when dragging near edges + * Uses time-based scrolling with 2-zone system for variable speed + */ +export class EdgeScrollManager { + constructor(eventBus) { + this.eventBus = eventBus; + this.scrollableContent = null; + this.timeGrid = null; + this.draggedClone = null; + this.scrollRAF = null; + this.mouseY = 0; + this.isDragging = false; + this.isScrolling = false; // Track if edge-scroll is active + this.lastTs = 0; + this.rect = null; + this.initialScrollTop = 0; + this.scrollListener = null; + // Constants - fixed values as per requirements + this.OUTER_ZONE = 100; // px from edge (slow zone) + this.INNER_ZONE = 50; // px from edge (fast zone) + this.SLOW_SPEED_PXS = 140; // px/sec in outer zone + this.FAST_SPEED_PXS = 640; // px/sec in inner zone + this.init(); + } + init() { + // Wait for DOM to be ready + setTimeout(() => { + this.scrollableContent = document.querySelector('swp-scrollable-content'); + this.timeGrid = document.querySelector('swp-time-grid'); + if (this.scrollableContent) { + // Disable smooth scroll for instant auto-scroll + this.scrollableContent.style.scrollBehavior = 'auto'; + // Add scroll listener to detect actual scrolling + this.scrollListener = this.handleScroll.bind(this); + this.scrollableContent.addEventListener('scroll', this.scrollListener, { passive: true }); + } + }, 100); + // Listen to mousemove directly from document to always get mouse coords + document.body.addEventListener('mousemove', (e) => { + if (this.isDragging) { + this.mouseY = e.clientY; + } + }); + this.subscribeToEvents(); + } + subscribeToEvents() { + // Listen to drag events from DragDropManager + this.eventBus.on('drag:start', (event) => { + const payload = event.detail; + this.draggedClone = payload.draggedClone; + this.startDrag(); + }); + this.eventBus.on('drag:end', () => this.stopDrag()); + this.eventBus.on('drag:cancelled', () => this.stopDrag()); + // Stop scrolling when event converts to/from all-day + this.eventBus.on('drag:mouseenter-header', () => { + console.log('🔄 EdgeScrollManager: Event converting to all-day - stopping scroll'); + this.stopDrag(); + }); + this.eventBus.on('drag:mouseenter-column', () => { + this.startDrag(); + }); + } + startDrag() { + console.log('🎬 EdgeScrollManager: Starting drag'); + this.isDragging = true; + this.isScrolling = false; // Reset scroll state + this.lastTs = performance.now(); + // Save initial scroll position + if (this.scrollableContent) { + this.initialScrollTop = this.scrollableContent.scrollTop; + } + if (this.scrollRAF === null) { + this.scrollRAF = requestAnimationFrame((ts) => this.scrollTick(ts)); + } + } + stopDrag() { + this.isDragging = false; + // Emit stopped event if we were scrolling + if (this.isScrolling) { + this.isScrolling = false; + console.log('🛑 EdgeScrollManager: Edge-scroll stopped (drag ended)'); + this.eventBus.emit('edgescroll:stopped', {}); + } + if (this.scrollRAF !== null) { + cancelAnimationFrame(this.scrollRAF); + this.scrollRAF = null; + } + this.rect = null; + this.lastTs = 0; + this.initialScrollTop = 0; + } + handleScroll() { + if (!this.isDragging || !this.scrollableContent) + return; + const currentScrollTop = this.scrollableContent.scrollTop; + const scrollDelta = Math.abs(currentScrollTop - this.initialScrollTop); + // Only emit started event if we've actually scrolled more than 1px + if (scrollDelta > 1 && !this.isScrolling) { + this.isScrolling = true; + console.log('💾 EdgeScrollManager: Edge-scroll started (actual scroll detected)', { + initialScrollTop: this.initialScrollTop, + currentScrollTop, + scrollDelta + }); + this.eventBus.emit('edgescroll:started', {}); + } + } + scrollTick(ts) { + const dt = this.lastTs ? (ts - this.lastTs) / 1000 : 0; + this.lastTs = ts; + if (!this.scrollableContent) { + this.stopDrag(); + return; + } + // Cache rect for performance (only measure once per frame) + if (!this.rect) { + this.rect = this.scrollableContent.getBoundingClientRect(); + } + let vy = 0; + if (this.isDragging) { + const distTop = this.mouseY - this.rect.top; + const distBot = this.rect.bottom - this.mouseY; + // Check top edge + if (distTop < this.INNER_ZONE) { + vy = -this.FAST_SPEED_PXS; + } + else if (distTop < this.OUTER_ZONE) { + vy = -this.SLOW_SPEED_PXS; + } + // Check bottom edge + else if (distBot < this.INNER_ZONE) { + vy = this.FAST_SPEED_PXS; + } + else if (distBot < this.OUTER_ZONE) { + vy = this.SLOW_SPEED_PXS; + } + } + if (vy !== 0 && this.isDragging && this.timeGrid && this.draggedClone) { + // Check if we can scroll in the requested direction + const currentScrollTop = this.scrollableContent.scrollTop; + const scrollableHeight = this.scrollableContent.clientHeight; + const timeGridHeight = this.timeGrid.clientHeight; + // Get dragged element position and height + const cloneRect = this.draggedClone.getBoundingClientRect(); + const cloneBottom = cloneRect.bottom; + const timeGridRect = this.timeGrid.getBoundingClientRect(); + const timeGridBottom = timeGridRect.bottom; + // Check boundaries + const atTop = currentScrollTop <= 0 && vy < 0; + const atBottom = (cloneBottom >= timeGridBottom) && vy > 0; + if (atTop || atBottom) { + // At boundary - stop scrolling + if (this.isScrolling) { + this.isScrolling = false; + this.initialScrollTop = this.scrollableContent.scrollTop; + console.log('🛑 EdgeScrollManager: Edge-scroll stopped (reached boundary)'); + this.eventBus.emit('edgescroll:stopped', {}); + } + // Continue RAF loop to detect when mouse moves away from boundary + if (this.isDragging) { + this.scrollRAF = requestAnimationFrame((ts) => this.scrollTick(ts)); + } + } + else { + // Not at boundary - apply scroll + this.scrollableContent.scrollTop += vy * dt; + this.rect = null; // Invalidate cache for next frame + this.scrollRAF = requestAnimationFrame((ts) => this.scrollTick(ts)); + } + } + else { + // Mouse moved away from edge - stop scrolling + if (this.isScrolling) { + this.isScrolling = false; + this.initialScrollTop = this.scrollableContent.scrollTop; // Reset for next scroll + console.log('🛑 EdgeScrollManager: Edge-scroll stopped (mouse left edge)'); + this.eventBus.emit('edgescroll:stopped', {}); + } + // Continue RAF loop even if not scrolling, to detect edge entry + if (this.isDragging) { + this.scrollRAF = requestAnimationFrame((ts) => this.scrollTick(ts)); + } + else { + this.stopDrag(); + } + } + } +} +//# sourceMappingURL=EdgeScrollManager.js.map \ No newline at end of file diff --git a/wwwroot/js/managers/EdgeScrollManager.js.map b/wwwroot/js/managers/EdgeScrollManager.js.map new file mode 100644 index 0000000..72c0b1f --- /dev/null +++ b/wwwroot/js/managers/EdgeScrollManager.js.map @@ -0,0 +1 @@ +{"version":3,"file":"EdgeScrollManager.js","sourceRoot":"","sources":["../../../src/managers/EdgeScrollManager.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/managers/EventFilterManager.d.ts b/wwwroot/js/managers/EventFilterManager.d.ts new file mode 100644 index 0000000..91092b2 --- /dev/null +++ b/wwwroot/js/managers/EventFilterManager.d.ts @@ -0,0 +1,32 @@ +/** + * EventFilterManager - Handles fuzzy search filtering of calendar events + * Uses Fuse.js for fuzzy matching (Apache 2.0 License) + */ +export declare class EventFilterManager { + private searchInput; + private allEvents; + private matchingEventIds; + private isFilterActive; + private frameRequest; + private fuse; + constructor(); + private init; + private setupSearchListeners; + private subscribeToEvents; + private updateEventsList; + private handleSearchInput; + private applyFilter; + private clearFilter; + private updateVisualState; + /** + * Check if an event matches the current filter + */ + eventMatchesFilter(eventId: string): boolean; + /** + * Get current filter state + */ + getFilterState(): { + active: boolean; + matchingIds: string[]; + }; +} diff --git a/wwwroot/js/managers/EventFilterManager.js b/wwwroot/js/managers/EventFilterManager.js new file mode 100644 index 0000000..dd2bd84 --- /dev/null +++ b/wwwroot/js/managers/EventFilterManager.js @@ -0,0 +1,192 @@ +/** + * EventFilterManager - Handles fuzzy search filtering of calendar events + * Uses Fuse.js for fuzzy matching (Apache 2.0 License) + */ +import { eventBus } from '../core/EventBus'; +import { CoreEvents } from '../constants/CoreEvents'; +// Import Fuse.js from npm +import Fuse from 'fuse.js'; +export class EventFilterManager { + constructor() { + this.searchInput = null; + this.allEvents = []; + this.matchingEventIds = new Set(); + this.isFilterActive = false; + this.frameRequest = null; + this.fuse = null; + // Wait for DOM to be ready before initializing + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', () => { + this.init(); + }); + } + else { + this.init(); + } + } + init() { + // Find search input + this.searchInput = document.querySelector('swp-search-container input[type="search"]'); + if (!this.searchInput) { + return; + } + // Set up event listeners + this.setupSearchListeners(); + this.subscribeToEvents(); + // Initialization complete + } + setupSearchListeners() { + if (!this.searchInput) + return; + // Listen for input changes + this.searchInput.addEventListener('input', (e) => { + const query = e.target.value; + this.handleSearchInput(query); + }); + // Listen for escape key + this.searchInput.addEventListener('keydown', (e) => { + if (e.key === 'Escape') { + this.clearFilter(); + } + }); + } + subscribeToEvents() { + // Listen for events data updates + eventBus.on(CoreEvents.EVENTS_RENDERED, (e) => { + const detail = e.detail; + if (detail?.events) { + this.updateEventsList(detail.events); + } + }); + } + updateEventsList(events) { + this.allEvents = events; + // Initialize Fuse with the new events list + this.fuse = new Fuse(this.allEvents, { + keys: ['title', 'description'], + threshold: 0.3, + includeScore: true, + minMatchCharLength: 2, // Minimum 2 characters for a match + shouldSort: true, + ignoreLocation: true // Search anywhere in the string + }); + // Re-apply filter if active + if (this.isFilterActive && this.searchInput) { + this.applyFilter(this.searchInput.value); + } + } + handleSearchInput(query) { + // Cancel any pending filter + if (this.frameRequest) { + cancelAnimationFrame(this.frameRequest); + } + // Debounce with requestAnimationFrame + this.frameRequest = requestAnimationFrame(() => { + if (query.length === 0) { + // Only clear when input is completely empty + this.clearFilter(); + } + else { + // Let Fuse.js handle minimum character length via minMatchCharLength + this.applyFilter(query); + } + }); + } + applyFilter(query) { + if (!this.fuse) { + return; + } + // Perform fuzzy search + const results = this.fuse.search(query); + // Extract matching event IDs + this.matchingEventIds.clear(); + results.forEach((result) => { + if (result.item && result.item.id) { + this.matchingEventIds.add(result.item.id); + } + }); + // Update filter state + this.isFilterActive = true; + // Update visual state + this.updateVisualState(); + // Emit filter changed event + eventBus.emit(CoreEvents.FILTER_CHANGED, { + active: true, + query: query, + matchingIds: Array.from(this.matchingEventIds) + }); + } + clearFilter() { + this.isFilterActive = false; + this.matchingEventIds.clear(); + // Clear search input + if (this.searchInput) { + this.searchInput.value = ''; + } + // Update visual state + this.updateVisualState(); + // Emit filter cleared event + eventBus.emit(CoreEvents.FILTER_CHANGED, { + active: false, + query: '', + matchingIds: [] + }); + } + updateVisualState() { + // Update search container styling + const searchContainer = document.querySelector('swp-search-container'); + if (searchContainer) { + if (this.isFilterActive) { + searchContainer.classList.add('filter-active'); + } + else { + searchContainer.classList.remove('filter-active'); + } + } + // Update all events layers + const eventsLayers = document.querySelectorAll('swp-events-layer'); + eventsLayers.forEach(layer => { + if (this.isFilterActive) { + layer.setAttribute('data-filter-active', 'true'); + // Mark matching events + const events = layer.querySelectorAll('swp-event'); + events.forEach(event => { + const eventId = event.getAttribute('data-event-id'); + if (eventId && this.matchingEventIds.has(eventId)) { + event.setAttribute('data-matches', 'true'); + } + else { + event.removeAttribute('data-matches'); + } + }); + } + else { + layer.removeAttribute('data-filter-active'); + // Remove all match attributes + const events = layer.querySelectorAll('swp-event'); + events.forEach(event => { + event.removeAttribute('data-matches'); + }); + } + }); + } + /** + * Check if an event matches the current filter + */ + eventMatchesFilter(eventId) { + if (!this.isFilterActive) { + return true; // No filter active, all events match + } + return this.matchingEventIds.has(eventId); + } + /** + * Get current filter state + */ + getFilterState() { + return { + active: this.isFilterActive, + matchingIds: Array.from(this.matchingEventIds) + }; + } +} +//# sourceMappingURL=EventFilterManager.js.map \ No newline at end of file diff --git a/wwwroot/js/managers/EventFilterManager.js.map b/wwwroot/js/managers/EventFilterManager.js.map new file mode 100644 index 0000000..295cbd1 --- /dev/null +++ b/wwwroot/js/managers/EventFilterManager.js.map @@ -0,0 +1 @@ +{"version":3,"file":"EventFilterManager.js","sourceRoot":"","sources":["../../../src/managers/EventFilterManager.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/managers/EventLayoutCoordinator.d.ts b/wwwroot/js/managers/EventLayoutCoordinator.d.ts new file mode 100644 index 0000000..5079618 --- /dev/null +++ b/wwwroot/js/managers/EventLayoutCoordinator.d.ts @@ -0,0 +1,78 @@ +/** + * EventLayoutCoordinator - Coordinates event layout calculations + * + * Separates layout logic from rendering concerns. + * Calculates stack levels, groups events, and determines rendering strategy. + */ +import { ICalendarEvent } from '../types/CalendarTypes'; +import { EventStackManager, IStackLink } from './EventStackManager'; +import { PositionUtils } from '../utils/PositionUtils'; +import { Configuration } from '../configurations/CalendarConfig'; +export interface IGridGroupLayout { + events: ICalendarEvent[]; + stackLevel: number; + position: { + top: number; + }; + columns: ICalendarEvent[][]; +} +export interface IStackedEventLayout { + event: ICalendarEvent; + stackLink: IStackLink; + position: { + top: number; + height: number; + }; +} +export interface IColumnLayout { + gridGroups: IGridGroupLayout[]; + stackedEvents: IStackedEventLayout[]; +} +export declare class EventLayoutCoordinator { + private stackManager; + private config; + private positionUtils; + constructor(stackManager: EventStackManager, config: Configuration, positionUtils: PositionUtils); + /** + * Calculate complete layout for a column of events (recursive approach) + */ + calculateColumnLayout(columnEvents: ICalendarEvent[]): IColumnLayout; + /** + * Calculate stack level for a grid group based on already rendered events + */ + private calculateGridGroupStackLevelFromRendered; + /** + * Calculate stack level for a single stacked event based on already rendered events + */ + private calculateStackLevelFromRendered; + /** + * Detect if two events have a conflict based on threshold + * + * @param event1 - First event + * @param event2 - Second event + * @param thresholdMinutes - Threshold in minutes + * @returns true if events conflict + */ + private detectConflict; + /** + * Expand grid candidates to find all events connected by conflict chains + * + * Uses expanding search to find chains (A→B→C where each conflicts with next) + * + * @param firstEvent - The first event to start with + * @param remaining - Remaining events to check + * @param thresholdMinutes - Threshold in minutes + * @returns Array of all events in the conflict chain + */ + private expandGridCandidates; + /** + * Allocate events to columns within a grid group + * + * Events that don't overlap can share the same column. + * Uses a greedy algorithm to minimize the number of columns. + * + * @param events - Events in the grid group (should already be sorted by start time) + * @returns Array of columns, where each column is an array of events + */ + private allocateColumns; +} diff --git a/wwwroot/js/managers/EventLayoutCoordinator.js b/wwwroot/js/managers/EventLayoutCoordinator.js new file mode 100644 index 0000000..381bc25 --- /dev/null +++ b/wwwroot/js/managers/EventLayoutCoordinator.js @@ -0,0 +1,201 @@ +/** + * EventLayoutCoordinator - Coordinates event layout calculations + * + * Separates layout logic from rendering concerns. + * Calculates stack levels, groups events, and determines rendering strategy. + */ +export class EventLayoutCoordinator { + constructor(stackManager, config, positionUtils) { + this.stackManager = stackManager; + this.config = config; + this.positionUtils = positionUtils; + } + /** + * Calculate complete layout for a column of events (recursive approach) + */ + calculateColumnLayout(columnEvents) { + if (columnEvents.length === 0) { + return { gridGroups: [], stackedEvents: [] }; + } + const gridGroupLayouts = []; + const stackedEventLayouts = []; + const renderedEventsWithLevels = []; + let remaining = [...columnEvents].sort((a, b) => a.start.getTime() - b.start.getTime()); + // Process events recursively + while (remaining.length > 0) { + // Take first event + const firstEvent = remaining[0]; + // Find events that could be in GRID with first event + // Use expanding search to find chains (A→B→C where each conflicts with next) + const gridSettings = this.config.gridSettings; + const thresholdMinutes = gridSettings.gridStartThresholdMinutes; + // Use refactored method for expanding grid candidates + const gridCandidates = this.expandGridCandidates(firstEvent, remaining, thresholdMinutes); + // Decide: should this group be GRID or STACK? + const group = { + events: gridCandidates, + containerType: 'NONE', + startTime: firstEvent.start + }; + const containerType = this.stackManager.decideContainerType(group); + if (containerType === 'GRID' && gridCandidates.length > 1) { + // Render as GRID + const gridStackLevel = this.calculateGridGroupStackLevelFromRendered(gridCandidates, renderedEventsWithLevels); + // Ensure we get the earliest event (explicit sort for robustness) + const earliestEvent = [...gridCandidates].sort((a, b) => a.start.getTime() - b.start.getTime())[0]; + const position = this.positionUtils.calculateEventPosition(earliestEvent.start, earliestEvent.end); + const columns = this.allocateColumns(gridCandidates); + gridGroupLayouts.push({ + events: gridCandidates, + stackLevel: gridStackLevel, + position: { top: position.top + 1 }, + columns + }); + // Mark all events in grid with their stack level + gridCandidates.forEach(e => renderedEventsWithLevels.push({ event: e, level: gridStackLevel })); + // Remove all events in this grid from remaining + remaining = remaining.filter(e => !gridCandidates.includes(e)); + } + else { + // Render first event as STACKED + const stackLevel = this.calculateStackLevelFromRendered(firstEvent, renderedEventsWithLevels); + const position = this.positionUtils.calculateEventPosition(firstEvent.start, firstEvent.end); + stackedEventLayouts.push({ + event: firstEvent, + stackLink: { stackLevel }, + position: { top: position.top + 1, height: position.height - 3 } + }); + // Mark this event with its stack level + renderedEventsWithLevels.push({ event: firstEvent, level: stackLevel }); + // Remove only first event from remaining + remaining = remaining.slice(1); + } + } + return { + gridGroups: gridGroupLayouts, + stackedEvents: stackedEventLayouts + }; + } + /** + * Calculate stack level for a grid group based on already rendered events + */ + calculateGridGroupStackLevelFromRendered(gridEvents, renderedEventsWithLevels) { + // Find highest stack level of any rendered event that overlaps with this grid + let maxOverlappingLevel = -1; + for (const gridEvent of gridEvents) { + for (const rendered of renderedEventsWithLevels) { + if (this.stackManager.doEventsOverlap(gridEvent, rendered.event)) { + maxOverlappingLevel = Math.max(maxOverlappingLevel, rendered.level); + } + } + } + return maxOverlappingLevel + 1; + } + /** + * Calculate stack level for a single stacked event based on already rendered events + */ + calculateStackLevelFromRendered(event, renderedEventsWithLevels) { + // Find highest stack level of any rendered event that overlaps with this event + let maxOverlappingLevel = -1; + for (const rendered of renderedEventsWithLevels) { + if (this.stackManager.doEventsOverlap(event, rendered.event)) { + maxOverlappingLevel = Math.max(maxOverlappingLevel, rendered.level); + } + } + return maxOverlappingLevel + 1; + } + /** + * Detect if two events have a conflict based on threshold + * + * @param event1 - First event + * @param event2 - Second event + * @param thresholdMinutes - Threshold in minutes + * @returns true if events conflict + */ + detectConflict(event1, event2, thresholdMinutes) { + // Check 1: Start-to-start conflict (starts within threshold) + const startToStartDiff = Math.abs(event1.start.getTime() - event2.start.getTime()) / (1000 * 60); + if (startToStartDiff <= thresholdMinutes && this.stackManager.doEventsOverlap(event1, event2)) { + return true; + } + // Check 2: End-to-start conflict (event1 starts within threshold before event2 ends) + const endToStartMinutes = (event2.end.getTime() - event1.start.getTime()) / (1000 * 60); + if (endToStartMinutes > 0 && endToStartMinutes <= thresholdMinutes) { + return true; + } + // Check 3: Reverse end-to-start (event2 starts within threshold before event1 ends) + const reverseEndToStart = (event1.end.getTime() - event2.start.getTime()) / (1000 * 60); + if (reverseEndToStart > 0 && reverseEndToStart <= thresholdMinutes) { + return true; + } + return false; + } + /** + * Expand grid candidates to find all events connected by conflict chains + * + * Uses expanding search to find chains (A→B→C where each conflicts with next) + * + * @param firstEvent - The first event to start with + * @param remaining - Remaining events to check + * @param thresholdMinutes - Threshold in minutes + * @returns Array of all events in the conflict chain + */ + expandGridCandidates(firstEvent, remaining, thresholdMinutes) { + const gridCandidates = [firstEvent]; + let candidatesChanged = true; + // Keep expanding until no new candidates can be added + while (candidatesChanged) { + candidatesChanged = false; + for (let i = 1; i < remaining.length; i++) { + const candidate = remaining[i]; + // Skip if already in candidates + if (gridCandidates.includes(candidate)) + continue; + // Check if candidate conflicts with ANY event in gridCandidates + for (const existingCandidate of gridCandidates) { + if (this.detectConflict(candidate, existingCandidate, thresholdMinutes)) { + gridCandidates.push(candidate); + candidatesChanged = true; + break; // Found conflict, move to next candidate + } + } + } + } + return gridCandidates; + } + /** + * Allocate events to columns within a grid group + * + * Events that don't overlap can share the same column. + * Uses a greedy algorithm to minimize the number of columns. + * + * @param events - Events in the grid group (should already be sorted by start time) + * @returns Array of columns, where each column is an array of events + */ + allocateColumns(events) { + if (events.length === 0) + return []; + if (events.length === 1) + return [[events[0]]]; + const columns = []; + // For each event, try to place it in an existing column where it doesn't overlap + for (const event of events) { + let placed = false; + // Try to find a column where this event doesn't overlap with any existing event + for (const column of columns) { + const hasOverlap = column.some(colEvent => this.stackManager.doEventsOverlap(event, colEvent)); + if (!hasOverlap) { + column.push(event); + placed = true; + break; + } + } + // If no suitable column found, create a new one + if (!placed) { + columns.push([event]); + } + } + return columns; + } +} +//# sourceMappingURL=EventLayoutCoordinator.js.map \ No newline at end of file diff --git a/wwwroot/js/managers/EventLayoutCoordinator.js.map b/wwwroot/js/managers/EventLayoutCoordinator.js.map new file mode 100644 index 0000000..18f9e09 --- /dev/null +++ b/wwwroot/js/managers/EventLayoutCoordinator.js.map @@ -0,0 +1 @@ +{"version":3,"file":"EventLayoutCoordinator.js","sourceRoot":"","sources":["../../../src/managers/EventLayoutCoordinator.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/managers/EventManager.d.ts b/wwwroot/js/managers/EventManager.d.ts new file mode 100644 index 0000000..dde95d1 --- /dev/null +++ b/wwwroot/js/managers/EventManager.d.ts @@ -0,0 +1,69 @@ +import { IEventBus, ICalendarEvent } from '../types/CalendarTypes'; +import { Configuration } from '../configurations/CalendarConfig'; +import { DateService } from '../utils/DateService'; +import { IEventRepository } from '../repositories/IEventRepository'; +/** + * EventManager - Event lifecycle and CRUD operations + * Delegates all data operations to IEventRepository + * No longer maintains in-memory cache - repository is single source of truth + */ +export declare class EventManager { + private eventBus; + private dateService; + private config; + private repository; + constructor(eventBus: IEventBus, dateService: DateService, config: Configuration, repository: IEventRepository); + /** + * Load event data from repository + * No longer caches - delegates to repository + */ + loadData(): Promise; + /** + * Get all events from repository + */ + getEvents(copy?: boolean): Promise; + /** + * Get event by ID from repository + */ + getEventById(id: string): Promise; + /** + * Get event by ID and return event info for navigation + * @param id Event ID to find + * @returns Event with navigation info or null if not found + */ + getEventForNavigation(id: string): Promise<{ + event: ICalendarEvent; + eventDate: Date; + } | null>; + /** + * Navigate to specific event by ID + * Emits navigation events for other managers to handle + * @param eventId Event ID to navigate to + * @returns true if event found and navigation initiated, false otherwise + */ + navigateToEvent(eventId: string): Promise; + /** + * Get events that overlap with a given time period + */ + getEventsForPeriod(startDate: Date, endDate: Date): Promise; + /** + * Create a new event and add it to the calendar + * Delegates to repository with source='local' + */ + addEvent(event: Omit): Promise; + /** + * Update an existing event + * Delegates to repository with source='local' + */ + updateEvent(id: string, updates: Partial): Promise; + /** + * Delete an event + * Delegates to repository with source='local' + */ + deleteEvent(id: string): Promise; + /** + * Handle remote update from SignalR + * Delegates to repository with source='remote' + */ + handleRemoteUpdate(event: ICalendarEvent): Promise; +} diff --git a/wwwroot/js/managers/EventManager.js b/wwwroot/js/managers/EventManager.js new file mode 100644 index 0000000..982105f --- /dev/null +++ b/wwwroot/js/managers/EventManager.js @@ -0,0 +1,164 @@ +import { CoreEvents } from '../constants/CoreEvents'; +/** + * EventManager - Event lifecycle and CRUD operations + * Delegates all data operations to IEventRepository + * No longer maintains in-memory cache - repository is single source of truth + */ +export class EventManager { + constructor(eventBus, dateService, config, repository) { + this.eventBus = eventBus; + this.dateService = dateService; + this.config = config; + this.repository = repository; + } + /** + * Load event data from repository + * No longer caches - delegates to repository + */ + async loadData() { + try { + // Just ensure repository is ready - no caching + await this.repository.loadEvents(); + } + catch (error) { + console.error('Failed to load event data:', error); + throw error; + } + } + /** + * Get all events from repository + */ + async getEvents(copy = false) { + const events = await this.repository.loadEvents(); + return copy ? [...events] : events; + } + /** + * Get event by ID from repository + */ + async getEventById(id) { + const events = await this.repository.loadEvents(); + return events.find(event => event.id === id); + } + /** + * Get event by ID and return event info for navigation + * @param id Event ID to find + * @returns Event with navigation info or null if not found + */ + async getEventForNavigation(id) { + const event = await this.getEventById(id); + if (!event) { + return null; + } + // Validate event dates + const validation = this.dateService.validateDate(event.start); + if (!validation.valid) { + console.warn(`EventManager: Invalid event start date for event ${id}:`, validation.error); + return null; + } + // Validate date range + if (!this.dateService.isValidRange(event.start, event.end)) { + console.warn(`EventManager: Invalid date range for event ${id}: start must be before end`); + return null; + } + return { + event, + eventDate: event.start + }; + } + /** + * Navigate to specific event by ID + * Emits navigation events for other managers to handle + * @param eventId Event ID to navigate to + * @returns true if event found and navigation initiated, false otherwise + */ + async navigateToEvent(eventId) { + const eventInfo = await this.getEventForNavigation(eventId); + if (!eventInfo) { + console.warn(`EventManager: Event with ID ${eventId} not found`); + return false; + } + const { event, eventDate } = eventInfo; + // Emit navigation request event + this.eventBus.emit(CoreEvents.NAVIGATE_TO_EVENT, { + eventId, + event, + eventDate, + eventStartTime: event.start + }); + return true; + } + /** + * Get events that overlap with a given time period + */ + async getEventsForPeriod(startDate, endDate) { + const events = await this.repository.loadEvents(); + // Event overlaps period if it starts before period ends AND ends after period starts + return events.filter(event => { + return event.start <= endDate && event.end >= startDate; + }); + } + /** + * Create a new event and add it to the calendar + * Delegates to repository with source='local' + */ + async addEvent(event) { + const newEvent = await this.repository.createEvent(event, 'local'); + this.eventBus.emit(CoreEvents.EVENT_CREATED, { + event: newEvent + }); + return newEvent; + } + /** + * Update an existing event + * Delegates to repository with source='local' + */ + async updateEvent(id, updates) { + try { + const updatedEvent = await this.repository.updateEvent(id, updates, 'local'); + this.eventBus.emit(CoreEvents.EVENT_UPDATED, { + event: updatedEvent + }); + return updatedEvent; + } + catch (error) { + console.error(`Failed to update event ${id}:`, error); + return null; + } + } + /** + * Delete an event + * Delegates to repository with source='local' + */ + async deleteEvent(id) { + try { + await this.repository.deleteEvent(id, 'local'); + this.eventBus.emit(CoreEvents.EVENT_DELETED, { + eventId: id + }); + return true; + } + catch (error) { + console.error(`Failed to delete event ${id}:`, error); + return false; + } + } + /** + * Handle remote update from SignalR + * Delegates to repository with source='remote' + */ + async handleRemoteUpdate(event) { + try { + await this.repository.updateEvent(event.id, event, 'remote'); + this.eventBus.emit(CoreEvents.REMOTE_UPDATE_RECEIVED, { + event + }); + this.eventBus.emit(CoreEvents.EVENT_UPDATED, { + event + }); + } + catch (error) { + console.error(`Failed to handle remote update for event ${event.id}:`, error); + } + } +} +//# sourceMappingURL=EventManager.js.map \ No newline at end of file diff --git a/wwwroot/js/managers/EventManager.js.map b/wwwroot/js/managers/EventManager.js.map new file mode 100644 index 0000000..5ff19fb --- /dev/null +++ b/wwwroot/js/managers/EventManager.js.map @@ -0,0 +1 @@ +{"version":3,"file":"EventManager.js","sourceRoot":"","sources":["../../../src/managers/EventManager.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/managers/EventStackManager.d.ts b/wwwroot/js/managers/EventStackManager.d.ts new file mode 100644 index 0000000..e2de953 --- /dev/null +++ b/wwwroot/js/managers/EventStackManager.d.ts @@ -0,0 +1,91 @@ +/** + * EventStackManager - Manages visual stacking of overlapping calendar events + * + * This class handles the creation and maintenance of "stack chains" - doubly-linked + * lists of overlapping events stored directly in DOM elements via data attributes. + * + * Implements 3-phase algorithm for grid + nested stacking: + * Phase 1: Group events by start time proximity (configurable threshold) + * Phase 2: Decide container type (GRID vs STACKING) + * Phase 3: Handle late arrivals (nested stacking - NOT IMPLEMENTED) + * + * @see STACKING_CONCEPT.md for detailed documentation + * @see stacking-visualization.html for visual examples + */ +import { ICalendarEvent } from '../types/CalendarTypes'; +import { Configuration } from '../configurations/CalendarConfig'; +export interface IStackLink { + prev?: string; + next?: string; + stackLevel: number; +} +export interface IEventGroup { + events: ICalendarEvent[]; + containerType: 'NONE' | 'GRID' | 'STACKING'; + startTime: Date; +} +export declare class EventStackManager { + private static readonly STACK_OFFSET_PX; + private config; + constructor(config: Configuration); + /** + * Group events by time conflicts (both start-to-start and end-to-start within threshold) + * + * Events are grouped if: + * 1. They start within ±threshold minutes of each other (start-to-start) + * 2. One event starts within threshold minutes before another ends (end-to-start conflict) + */ + groupEventsByStartTime(events: ICalendarEvent[]): IEventGroup[]; + /** + * Decide container type for a group of events + * + * Rule: Events starting simultaneously (within threshold) should ALWAYS use GRID, + * even if they overlap each other. This provides better visual indication that + * events start at the same time. + */ + decideContainerType(group: IEventGroup): 'NONE' | 'GRID' | 'STACKING'; + /** + * Check if two events overlap in time + */ + doEventsOverlap(event1: ICalendarEvent, event2: ICalendarEvent): boolean; + /** + * Create optimized stack links (events share levels when possible) + */ + createOptimizedStackLinks(events: ICalendarEvent[]): Map; + /** + * Calculate marginLeft based on stack level + */ + calculateMarginLeft(stackLevel: number): number; + /** + * Calculate zIndex based on stack level + */ + calculateZIndex(stackLevel: number): number; + /** + * Serialize stack link to JSON string + */ + serializeStackLink(stackLink: IStackLink): string; + /** + * Deserialize JSON string to stack link + */ + deserializeStackLink(json: string): IStackLink | null; + /** + * Apply stack link to DOM element + */ + applyStackLinkToElement(element: HTMLElement, stackLink: IStackLink): void; + /** + * Get stack link from DOM element + */ + getStackLinkFromElement(element: HTMLElement): IStackLink | null; + /** + * Apply visual styling to element based on stack level + */ + applyVisualStyling(element: HTMLElement, stackLevel: number): void; + /** + * Clear stack link from element + */ + clearStackLinkFromElement(element: HTMLElement): void; + /** + * Clear visual styling from element + */ + clearVisualStyling(element: HTMLElement): void; +} diff --git a/wwwroot/js/managers/EventStackManager.js b/wwwroot/js/managers/EventStackManager.js new file mode 100644 index 0000000..cb48109 --- /dev/null +++ b/wwwroot/js/managers/EventStackManager.js @@ -0,0 +1,217 @@ +/** + * EventStackManager - Manages visual stacking of overlapping calendar events + * + * This class handles the creation and maintenance of "stack chains" - doubly-linked + * lists of overlapping events stored directly in DOM elements via data attributes. + * + * Implements 3-phase algorithm for grid + nested stacking: + * Phase 1: Group events by start time proximity (configurable threshold) + * Phase 2: Decide container type (GRID vs STACKING) + * Phase 3: Handle late arrivals (nested stacking - NOT IMPLEMENTED) + * + * @see STACKING_CONCEPT.md for detailed documentation + * @see stacking-visualization.html for visual examples + */ +export class EventStackManager { + constructor(config) { + this.config = config; + } + // ============================================ + // PHASE 1: Start Time Grouping + // ============================================ + /** + * Group events by time conflicts (both start-to-start and end-to-start within threshold) + * + * Events are grouped if: + * 1. They start within ±threshold minutes of each other (start-to-start) + * 2. One event starts within threshold minutes before another ends (end-to-start conflict) + */ + groupEventsByStartTime(events) { + if (events.length === 0) + return []; + // Get threshold from config + const gridSettings = this.config.gridSettings; + const thresholdMinutes = gridSettings.gridStartThresholdMinutes; + // Sort events by start time + const sorted = [...events].sort((a, b) => a.start.getTime() - b.start.getTime()); + const groups = []; + for (const event of sorted) { + // Find existing group that this event conflicts with + const existingGroup = groups.find(group => { + // Check if event conflicts with ANY event in the group + return group.events.some(groupEvent => { + // Start-to-start conflict: events start within threshold + const startToStartMinutes = Math.abs(event.start.getTime() - groupEvent.start.getTime()) / (1000 * 60); + if (startToStartMinutes <= thresholdMinutes) { + return true; + } + // End-to-start conflict: event starts within threshold before groupEvent ends + const endToStartMinutes = (groupEvent.end.getTime() - event.start.getTime()) / (1000 * 60); + if (endToStartMinutes > 0 && endToStartMinutes <= thresholdMinutes) { + return true; + } + // Also check reverse: groupEvent starts within threshold before event ends + const reverseEndToStart = (event.end.getTime() - groupEvent.start.getTime()) / (1000 * 60); + if (reverseEndToStart > 0 && reverseEndToStart <= thresholdMinutes) { + return true; + } + return false; + }); + }); + if (existingGroup) { + existingGroup.events.push(event); + } + else { + groups.push({ + events: [event], + containerType: 'NONE', + startTime: event.start + }); + } + } + return groups; + } + // ============================================ + // PHASE 2: Container Type Decision + // ============================================ + /** + * Decide container type for a group of events + * + * Rule: Events starting simultaneously (within threshold) should ALWAYS use GRID, + * even if they overlap each other. This provides better visual indication that + * events start at the same time. + */ + decideContainerType(group) { + if (group.events.length === 1) { + return 'NONE'; + } + // If events are grouped together (start within threshold), they should share columns (GRID) + // This is true EVEN if they overlap, because the visual priority is to show + // that they start simultaneously. + return 'GRID'; + } + /** + * Check if two events overlap in time + */ + doEventsOverlap(event1, event2) { + return event1.start < event2.end && event1.end > event2.start; + } + // ============================================ + // Stack Level Calculation + // ============================================ + /** + * Create optimized stack links (events share levels when possible) + */ + createOptimizedStackLinks(events) { + const stackLinks = new Map(); + if (events.length === 0) + return stackLinks; + // Sort by start time + const sorted = [...events].sort((a, b) => a.start.getTime() - b.start.getTime()); + // Step 1: Assign stack levels + for (const event of sorted) { + // Find all events this event overlaps with + const overlapping = sorted.filter(other => other !== event && this.doEventsOverlap(event, other)); + // Find the MINIMUM required level (must be above all overlapping events) + let minRequiredLevel = 0; + for (const other of overlapping) { + const otherLink = stackLinks.get(other.id); + if (otherLink) { + // Must be at least one level above the overlapping event + minRequiredLevel = Math.max(minRequiredLevel, otherLink.stackLevel + 1); + } + } + stackLinks.set(event.id, { stackLevel: minRequiredLevel }); + } + // Step 2: Build prev/next chains for overlapping events at adjacent stack levels + for (const event of sorted) { + const currentLink = stackLinks.get(event.id); + // Find overlapping events that are directly below (stackLevel - 1) + const overlapping = sorted.filter(other => other !== event && this.doEventsOverlap(event, other)); + const directlyBelow = overlapping.filter(other => { + const otherLink = stackLinks.get(other.id); + return otherLink && otherLink.stackLevel === currentLink.stackLevel - 1; + }); + if (directlyBelow.length > 0) { + // Use the first one in sorted order as prev + currentLink.prev = directlyBelow[0].id; + } + // Find overlapping events that are directly above (stackLevel + 1) + const directlyAbove = overlapping.filter(other => { + const otherLink = stackLinks.get(other.id); + return otherLink && otherLink.stackLevel === currentLink.stackLevel + 1; + }); + if (directlyAbove.length > 0) { + // Use the first one in sorted order as next + currentLink.next = directlyAbove[0].id; + } + } + return stackLinks; + } + /** + * Calculate marginLeft based on stack level + */ + calculateMarginLeft(stackLevel) { + return stackLevel * EventStackManager.STACK_OFFSET_PX; + } + /** + * Calculate zIndex based on stack level + */ + calculateZIndex(stackLevel) { + return 100 + stackLevel; + } + /** + * Serialize stack link to JSON string + */ + serializeStackLink(stackLink) { + return JSON.stringify(stackLink); + } + /** + * Deserialize JSON string to stack link + */ + deserializeStackLink(json) { + try { + return JSON.parse(json); + } + catch (e) { + return null; + } + } + /** + * Apply stack link to DOM element + */ + applyStackLinkToElement(element, stackLink) { + element.dataset.stackLink = this.serializeStackLink(stackLink); + } + /** + * Get stack link from DOM element + */ + getStackLinkFromElement(element) { + const data = element.dataset.stackLink; + if (!data) + return null; + return this.deserializeStackLink(data); + } + /** + * Apply visual styling to element based on stack level + */ + applyVisualStyling(element, stackLevel) { + element.style.marginLeft = `${this.calculateMarginLeft(stackLevel)}px`; + element.style.zIndex = `${this.calculateZIndex(stackLevel)}`; + } + /** + * Clear stack link from element + */ + clearStackLinkFromElement(element) { + delete element.dataset.stackLink; + } + /** + * Clear visual styling from element + */ + clearVisualStyling(element) { + element.style.marginLeft = ''; + element.style.zIndex = ''; + } +} +EventStackManager.STACK_OFFSET_PX = 15; +//# sourceMappingURL=EventStackManager.js.map \ No newline at end of file diff --git a/wwwroot/js/managers/EventStackManager.js.map b/wwwroot/js/managers/EventStackManager.js.map new file mode 100644 index 0000000..cf98e2a --- /dev/null +++ b/wwwroot/js/managers/EventStackManager.js.map @@ -0,0 +1 @@ +{"version":3,"file":"EventStackManager.js","sourceRoot":"","sources":["../../../src/managers/EventStackManager.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/managers/GridManager.d.ts b/wwwroot/js/managers/GridManager.d.ts new file mode 100644 index 0000000..2f4d451 --- /dev/null +++ b/wwwroot/js/managers/GridManager.d.ts @@ -0,0 +1,30 @@ +/** + * GridManager - Simplified grid manager using centralized GridRenderer + * Delegates DOM rendering to GridRenderer, focuses on coordination + */ +import { GridRenderer } from '../renderers/GridRenderer'; +import { DateService } from '../utils/DateService'; +import { Configuration } from '../configurations/CalendarConfig'; +import { EventManager } from './EventManager'; +/** + * Simplified GridManager focused on coordination, delegates rendering to GridRenderer + */ +export declare class GridManager { + private container; + private currentDate; + private currentView; + private gridRenderer; + private dateService; + private config; + private dataSource; + private eventManager; + constructor(gridRenderer: GridRenderer, dateService: DateService, config: Configuration, eventManager: EventManager); + private init; + private findElements; + private subscribeToEvents; + /** + * Main render method - delegates to GridRenderer + * Note: CSS variables are automatically updated by ConfigManager when config changes + */ + render(): Promise; +} diff --git a/wwwroot/js/managers/GridManager.js b/wwwroot/js/managers/GridManager.js new file mode 100644 index 0000000..c3294e8 --- /dev/null +++ b/wwwroot/js/managers/GridManager.js @@ -0,0 +1,77 @@ +/** + * GridManager - Simplified grid manager using centralized GridRenderer + * Delegates DOM rendering to GridRenderer, focuses on coordination + */ +import { eventBus } from '../core/EventBus'; +import { CoreEvents } from '../constants/CoreEvents'; +import { DateColumnDataSource } from '../datasources/DateColumnDataSource'; +/** + * Simplified GridManager focused on coordination, delegates rendering to GridRenderer + */ +export class GridManager { + constructor(gridRenderer, dateService, config, eventManager) { + this.container = null; + this.currentDate = new Date(); + this.currentView = 'week'; + this.gridRenderer = gridRenderer; + this.dateService = dateService; + this.config = config; + this.eventManager = eventManager; + this.dataSource = new DateColumnDataSource(dateService, config, this.currentDate, this.currentView); + this.init(); + } + init() { + this.findElements(); + this.subscribeToEvents(); + } + findElements() { + this.container = document.querySelector('swp-calendar-container'); + } + subscribeToEvents() { + // Listen for view changes + eventBus.on(CoreEvents.VIEW_CHANGED, (e) => { + const detail = e.detail; + this.currentView = detail.currentView; + this.dataSource.setCurrentView(this.currentView); + this.render(); + }); + // Listen for navigation events from NavigationButtons + eventBus.on(CoreEvents.NAVIGATION_COMPLETED, (e) => { + const detail = e.detail; + this.currentDate = detail.newDate; + this.dataSource.setCurrentDate(this.currentDate); + this.render(); + }); + // Listen for config changes that affect rendering + eventBus.on(CoreEvents.REFRESH_REQUESTED, (e) => { + this.render(); + }); + eventBus.on(CoreEvents.WORKWEEK_CHANGED, () => { + this.render(); + }); + } + /** + * Main render method - delegates to GridRenderer + * Note: CSS variables are automatically updated by ConfigManager when config changes + */ + async render() { + if (!this.container) { + return; + } + // Get dates from datasource - single source of truth + const dates = this.dataSource.getColumns(); + // Get events for the period from EventManager + const startDate = dates[0]; + const endDate = dates[dates.length - 1]; + const events = await this.eventManager.getEventsForPeriod(startDate, endDate); + // Delegate to GridRenderer with dates and events + this.gridRenderer.renderGrid(this.container, this.currentDate, this.currentView, dates, events); + // Emit grid rendered event + eventBus.emit(CoreEvents.GRID_RENDERED, { + container: this.container, + currentDate: this.currentDate, + dates: dates + }); + } +} +//# sourceMappingURL=GridManager.js.map \ No newline at end of file diff --git a/wwwroot/js/managers/GridManager.js.map b/wwwroot/js/managers/GridManager.js.map new file mode 100644 index 0000000..d5a0f33 --- /dev/null +++ b/wwwroot/js/managers/GridManager.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GridManager.js","sourceRoot":"","sources":["../../../src/managers/GridManager.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/managers/HeaderManager.d.ts b/wwwroot/js/managers/HeaderManager.d.ts new file mode 100644 index 0000000..6eabc82 --- /dev/null +++ b/wwwroot/js/managers/HeaderManager.d.ts @@ -0,0 +1,32 @@ +import { Configuration } from '../configurations/CalendarConfig'; +import { IHeaderRenderer } from '../renderers/DateHeaderRenderer'; +/** + * HeaderManager - Handles all header-related event logic + * Separates event handling from rendering concerns + * Uses dependency injection for renderer strategy + */ +export declare class HeaderManager { + private headerRenderer; + private config; + constructor(headerRenderer: IHeaderRenderer, config: Configuration); + /** + * Setup header drag event listeners - Listen to DragDropManager events + */ + setupHeaderDragListeners(): void; + /** + * Handle drag mouse enter header event + */ + private handleDragMouseEnterHeader; + /** + * Handle drag mouse leave header event + */ + private handleDragMouseLeaveHeader; + /** + * Setup navigation event listener + */ + private setupNavigationListener; + /** + * Update header content for navigation + */ + private updateHeader; +} diff --git a/wwwroot/js/managers/HeaderManager.js b/wwwroot/js/managers/HeaderManager.js new file mode 100644 index 0000000..f985c7a --- /dev/null +++ b/wwwroot/js/managers/HeaderManager.js @@ -0,0 +1,103 @@ +import { eventBus } from '../core/EventBus'; +import { CoreEvents } from '../constants/CoreEvents'; +import { ColumnDetectionUtils } from '../utils/ColumnDetectionUtils'; +/** + * HeaderManager - Handles all header-related event logic + * Separates event handling from rendering concerns + * Uses dependency injection for renderer strategy + */ +export class HeaderManager { + constructor(headerRenderer, config) { + this.headerRenderer = headerRenderer; + this.config = config; + // Bind handler methods for event listeners + this.handleDragMouseEnterHeader = this.handleDragMouseEnterHeader.bind(this); + this.handleDragMouseLeaveHeader = this.handleDragMouseLeaveHeader.bind(this); + // Listen for navigation events to update header + this.setupNavigationListener(); + } + /** + * Setup header drag event listeners - Listen to DragDropManager events + */ + setupHeaderDragListeners() { + console.log('🎯 HeaderManager: Setting up drag event listeners'); + // Subscribe to drag events from DragDropManager + eventBus.on('drag:mouseenter-header', this.handleDragMouseEnterHeader); + eventBus.on('drag:mouseleave-header', this.handleDragMouseLeaveHeader); + console.log('✅ HeaderManager: Drag event listeners attached'); + } + /** + * Handle drag mouse enter header event + */ + handleDragMouseEnterHeader(event) { + const { targetColumn: targetDate, mousePosition, originalElement, draggedClone: cloneElement } = event.detail; + console.log('🎯 HeaderManager: Received drag:mouseenter-header', { + targetDate, + originalElement: !!originalElement, + cloneElement: !!cloneElement + }); + } + /** + * Handle drag mouse leave header event + */ + handleDragMouseLeaveHeader(event) { + const { targetDate, mousePosition, originalElement, draggedClone: cloneElement } = event.detail; + console.log('🚪 HeaderManager: Received drag:mouseleave-header', { + targetDate, + originalElement: !!originalElement, + cloneElement: !!cloneElement + }); + } + /** + * Setup navigation event listener + */ + setupNavigationListener() { + eventBus.on(CoreEvents.NAVIGATION_COMPLETED, (event) => { + const { currentDate } = event.detail; + this.updateHeader(currentDate); + }); + // Also listen for date changes (including initial setup) + eventBus.on(CoreEvents.DATE_CHANGED, (event) => { + const { currentDate } = event.detail; + this.updateHeader(currentDate); + }); + // Listen for workweek header updates after grid rebuild + //currentDate: this.currentDate, + //currentView: this.currentView, + //workweek: this.config.currentWorkWeek + eventBus.on('workweek:header-update', (event) => { + const { currentDate } = event.detail; + this.updateHeader(currentDate); + }); + } + /** + * Update header content for navigation + */ + updateHeader(currentDate) { + console.log('🎯 HeaderManager.updateHeader called', { + currentDate, + rendererType: this.headerRenderer.constructor.name + }); + const calendarHeader = document.querySelector('swp-calendar-header'); + if (!calendarHeader) { + console.warn('❌ HeaderManager: No calendar header found!'); + return; + } + // Clear existing content + calendarHeader.innerHTML = ''; + // Render new header content using injected renderer + const context = { + currentWeek: currentDate, + config: this.config + }; + this.headerRenderer.render(calendarHeader, context); + // Setup event listeners on the new content + this.setupHeaderDragListeners(); + // Notify other managers that header is ready with period data + const payload = { + headerElements: ColumnDetectionUtils.getHeaderColumns(), + }; + eventBus.emit('header:ready', payload); + } +} +//# sourceMappingURL=HeaderManager.js.map \ No newline at end of file diff --git a/wwwroot/js/managers/HeaderManager.js.map b/wwwroot/js/managers/HeaderManager.js.map new file mode 100644 index 0000000..61da5cd --- /dev/null +++ b/wwwroot/js/managers/HeaderManager.js.map @@ -0,0 +1 @@ +{"version":3,"file":"HeaderManager.js","sourceRoot":"","sources":["../../../src/managers/HeaderManager.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/managers/NavigationButtonsManager.d.ts b/wwwroot/js/managers/NavigationButtonsManager.d.ts new file mode 100644 index 0000000..2fb76dc --- /dev/null +++ b/wwwroot/js/managers/NavigationButtonsManager.d.ts @@ -0,0 +1,40 @@ +import { IEventBus } from '../types/CalendarTypes'; +/** + * NavigationButtonsManager - Manages navigation button UI and state + * + * RESPONSIBILITY: + * =============== + * This manager owns all logic related to the UI element. + * It follows the principle that each functional UI element has its own manager. + * + * RESPONSIBILITIES: + * - Handles button clicks on swp-nav-button elements + * - Validates navigation actions (prev, next, today) + * - Emits NAV_BUTTON_CLICKED events + * - Manages button UI listeners + * + * EVENT FLOW: + * =========== + * User clicks button → validateAction() → emit event → NavigationManager handles navigation + * + * SUBSCRIBERS: + * ============ + * - NavigationManager: Performs actual navigation logic (animations, grid updates, week calculations) + */ +export declare class NavigationButtonsManager { + private eventBus; + private buttonListeners; + constructor(eventBus: IEventBus); + /** + * Setup click listeners on all navigation buttons + */ + private setupButtonListeners; + /** + * Handle navigation action + */ + private handleNavigation; + /** + * Validate if string is a valid navigation action + */ + private isValidAction; +} diff --git a/wwwroot/js/managers/NavigationButtonsManager.js b/wwwroot/js/managers/NavigationButtonsManager.js new file mode 100644 index 0000000..e1badd5 --- /dev/null +++ b/wwwroot/js/managers/NavigationButtonsManager.js @@ -0,0 +1,63 @@ +import { CoreEvents } from '../constants/CoreEvents'; +/** + * NavigationButtonsManager - Manages navigation button UI and state + * + * RESPONSIBILITY: + * =============== + * This manager owns all logic related to the UI element. + * It follows the principle that each functional UI element has its own manager. + * + * RESPONSIBILITIES: + * - Handles button clicks on swp-nav-button elements + * - Validates navigation actions (prev, next, today) + * - Emits NAV_BUTTON_CLICKED events + * - Manages button UI listeners + * + * EVENT FLOW: + * =========== + * User clicks button → validateAction() → emit event → NavigationManager handles navigation + * + * SUBSCRIBERS: + * ============ + * - NavigationManager: Performs actual navigation logic (animations, grid updates, week calculations) + */ +export class NavigationButtonsManager { + constructor(eventBus) { + this.buttonListeners = new Map(); + this.eventBus = eventBus; + this.setupButtonListeners(); + } + /** + * Setup click listeners on all navigation buttons + */ + setupButtonListeners() { + const buttons = document.querySelectorAll('swp-nav-button[data-action]'); + buttons.forEach(button => { + const clickHandler = (event) => { + event.preventDefault(); + const action = button.getAttribute('data-action'); + if (action && this.isValidAction(action)) { + this.handleNavigation(action); + } + }; + button.addEventListener('click', clickHandler); + this.buttonListeners.set(button, clickHandler); + }); + } + /** + * Handle navigation action + */ + handleNavigation(action) { + // Emit navigation button clicked event + this.eventBus.emit(CoreEvents.NAV_BUTTON_CLICKED, { + action: action + }); + } + /** + * Validate if string is a valid navigation action + */ + isValidAction(action) { + return ['prev', 'next', 'today'].includes(action); + } +} +//# sourceMappingURL=NavigationButtonsManager.js.map \ No newline at end of file diff --git a/wwwroot/js/managers/NavigationButtonsManager.js.map b/wwwroot/js/managers/NavigationButtonsManager.js.map new file mode 100644 index 0000000..ab7bd56 --- /dev/null +++ b/wwwroot/js/managers/NavigationButtonsManager.js.map @@ -0,0 +1 @@ +{"version":3,"file":"NavigationButtonsManager.js","sourceRoot":"","sources":["../../../src/managers/NavigationButtonsManager.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAErD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,OAAO,wBAAwB;IAInC,YAAY,QAAmB;QAFvB,oBAAe,GAAgC,IAAI,GAAG,EAAE,CAAC;QAG/D,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,oBAAoB,EAAE,CAAC;IAC9B,CAAC;IAED;;OAEG;IACK,oBAAoB;QAC1B,MAAM,OAAO,GAAG,QAAQ,CAAC,gBAAgB,CAAC,6BAA6B,CAAC,CAAC;QAEzE,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;YACvB,MAAM,YAAY,GAAG,CAAC,KAAY,EAAE,EAAE;gBACpC,KAAK,CAAC,cAAc,EAAE,CAAC;gBACvB,MAAM,MAAM,GAAG,MAAM,CAAC,YAAY,CAAC,aAAa,CAAC,CAAC;gBAClD,IAAI,MAAM,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;oBACzC,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC;gBAChC,CAAC;YACH,CAAC,CAAC;YAEF,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;YAC/C,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QACjD,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACK,gBAAgB,CAAC,MAAc;QACrC,uCAAuC;QACvC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,kBAAkB,EAAE;YAChD,MAAM,EAAE,MAAM;SACf,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACK,aAAa,CAAC,MAAc;QAClC,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACpD,CAAC;CACF"} \ No newline at end of file diff --git a/wwwroot/js/managers/NavigationManager.d.ts b/wwwroot/js/managers/NavigationManager.d.ts new file mode 100644 index 0000000..cc475be --- /dev/null +++ b/wwwroot/js/managers/NavigationManager.d.ts @@ -0,0 +1,32 @@ +import { IEventBus } from '../types/CalendarTypes'; +import { EventRenderingService } from '../renderers/EventRendererManager'; +import { DateService } from '../utils/DateService'; +import { WeekInfoRenderer } from '../renderers/WeekInfoRenderer'; +import { GridRenderer } from '../renderers/GridRenderer'; +export declare class NavigationManager { + private eventBus; + private weekInfoRenderer; + private gridRenderer; + private dateService; + private currentWeek; + private targetWeek; + private animationQueue; + constructor(eventBus: IEventBus, eventRenderer: EventRenderingService, gridRenderer: GridRenderer, dateService: DateService, weekInfoRenderer: WeekInfoRenderer); + private init; + /** + * Get the start of the ISO week (Monday) for a given date + * @param date - Any date in the week + * @returns The Monday of the ISO week + */ + private getISOWeekStart; + private setupEventListeners; + /** + * Navigate to specific event date and emit scroll event after navigation + */ + private navigateToEventDate; + private navigateToDate; + /** + * Animation transition using pre-rendered containers when available + */ + private animateTransition; +} diff --git a/wwwroot/js/managers/NavigationManager.js b/wwwroot/js/managers/NavigationManager.js new file mode 100644 index 0000000..a991117 --- /dev/null +++ b/wwwroot/js/managers/NavigationManager.js @@ -0,0 +1,188 @@ +import { CoreEvents } from '../constants/CoreEvents'; +export class NavigationManager { + constructor(eventBus, eventRenderer, gridRenderer, dateService, weekInfoRenderer) { + this.animationQueue = 0; + this.eventBus = eventBus; + this.dateService = dateService; + this.weekInfoRenderer = weekInfoRenderer; + this.gridRenderer = gridRenderer; + this.currentWeek = this.getISOWeekStart(new Date()); + this.targetWeek = new Date(this.currentWeek); + this.init(); + } + init() { + this.setupEventListeners(); + } + /** + * Get the start of the ISO week (Monday) for a given date + * @param date - Any date in the week + * @returns The Monday of the ISO week + */ + getISOWeekStart(date) { + const weekBounds = this.dateService.getWeekBounds(date); + return this.dateService.startOfDay(weekBounds.start); + } + setupEventListeners() { + // Listen for filter changes and apply to pre-rendered grids + this.eventBus.on(CoreEvents.FILTER_CHANGED, (e) => { + const detail = e.detail; + this.weekInfoRenderer.applyFilterToPreRenderedGrids(detail); + }); + // Listen for navigation button clicks from NavigationButtons + this.eventBus.on(CoreEvents.NAV_BUTTON_CLICKED, (event) => { + const { direction, newDate } = event.detail; + // Navigate to the new date with animation + this.navigateToDate(newDate, direction); + }); + // Listen for external navigation requests + this.eventBus.on(CoreEvents.DATE_CHANGED, (event) => { + const customEvent = event; + const dateFromEvent = customEvent.detail.currentDate; + // Validate date before processing + if (!dateFromEvent) { + console.warn('NavigationManager: No date provided in DATE_CHANGED event'); + return; + } + const targetDate = new Date(dateFromEvent); + // Use DateService validation + const validation = this.dateService.validateDate(targetDate); + if (!validation.valid) { + console.warn('NavigationManager: Invalid date received:', validation.error); + return; + } + this.navigateToDate(targetDate); + }); + // Listen for event navigation requests + this.eventBus.on(CoreEvents.NAVIGATE_TO_EVENT, (event) => { + const customEvent = event; + const { eventDate, eventStartTime } = customEvent.detail; + if (!eventDate || !eventStartTime) { + console.warn('NavigationManager: Invalid event navigation data'); + return; + } + this.navigateToEventDate(eventDate, eventStartTime); + }); + } + /** + * Navigate to specific event date and emit scroll event after navigation + */ + navigateToEventDate(eventDate, eventStartTime) { + const weekStart = this.getISOWeekStart(eventDate); + this.targetWeek = new Date(weekStart); + const currentTime = this.currentWeek.getTime(); + const targetTime = weekStart.getTime(); + // Store event start time for scrolling after navigation + const scrollAfterNavigation = () => { + // Emit scroll request after navigation is complete + this.eventBus.emit('scroll:to-event-time', { + eventStartTime + }); + }; + if (currentTime < targetTime) { + this.animationQueue++; + this.animateTransition('next', weekStart); + // Listen for navigation completion to trigger scroll + this.eventBus.once(CoreEvents.NAVIGATION_COMPLETED, scrollAfterNavigation); + } + else if (currentTime > targetTime) { + this.animationQueue++; + this.animateTransition('prev', weekStart); + // Listen for navigation completion to trigger scroll + this.eventBus.once(CoreEvents.NAVIGATION_COMPLETED, scrollAfterNavigation); + } + else { + // Already on correct week, just scroll + scrollAfterNavigation(); + } + } + navigateToDate(date, direction) { + const weekStart = this.getISOWeekStart(date); + this.targetWeek = new Date(weekStart); + const currentTime = this.currentWeek.getTime(); + const targetTime = weekStart.getTime(); + // Use provided direction or calculate based on time comparison + let animationDirection; + if (direction === 'next') { + animationDirection = 'next'; + } + else if (direction === 'previous') { + animationDirection = 'prev'; + } + else if (direction === 'today') { + // For "today", determine direction based on current position + animationDirection = currentTime < targetTime ? 'next' : 'prev'; + } + else { + // Fallback: calculate direction + animationDirection = currentTime < targetTime ? 'next' : 'prev'; + } + if (currentTime !== targetTime) { + this.animationQueue++; + this.animateTransition(animationDirection, weekStart); + } + } + /** + * Animation transition using pre-rendered containers when available + */ + animateTransition(direction, targetWeek) { + const container = document.querySelector('swp-calendar-container'); + const currentGrid = document.querySelector('swp-calendar-container swp-grid-container:not([data-prerendered])'); + if (!container || !currentGrid) { + return; + } + // Reset all-day height BEFORE creating new grid to ensure base height + const root = document.documentElement; + root.style.setProperty('--all-day-row-height', '0px'); + let newGrid; + console.group('🔧 NavigationManager.refactored'); + console.log('Calling GridRenderer instead of NavigationRenderer'); + console.log('Target week:', targetWeek); + // Always create a fresh container for consistent behavior + newGrid = this.gridRenderer.createNavigationGrid(container, targetWeek); + console.groupEnd(); + // Clear any existing transforms before animation + newGrid.style.transform = ''; + currentGrid.style.transform = ''; + // Animate transition using Web Animations API + const slideOutAnimation = currentGrid.animate([ + { transform: 'translateX(0)', opacity: '1' }, + { transform: direction === 'next' ? 'translateX(-100%)' : 'translateX(100%)', opacity: '0.5' } + ], { + duration: 400, + easing: 'ease-in-out', + fill: 'forwards' + }); + const slideInAnimation = newGrid.animate([ + { transform: direction === 'next' ? 'translateX(100%)' : 'translateX(-100%)' }, + { transform: 'translateX(0)' } + ], { + duration: 400, + easing: 'ease-in-out', + fill: 'forwards' + }); + // Handle animation completion + slideInAnimation.addEventListener('finish', () => { + // Cleanup: Remove all old grids except the new one + const allGrids = container.querySelectorAll('swp-grid-container'); + for (let i = 0; i < allGrids.length - 1; i++) { + allGrids[i].remove(); + } + // Reset positioning + newGrid.style.position = 'relative'; + newGrid.removeAttribute('data-prerendered'); + // Update state + this.currentWeek = new Date(targetWeek); + this.animationQueue--; + // If this was the last queued animation, ensure we're in sync + if (this.animationQueue === 0) { + this.currentWeek = new Date(this.targetWeek); + } + // Emit navigation completed event + this.eventBus.emit(CoreEvents.NAVIGATION_COMPLETED, { + direction, + newDate: this.currentWeek + }); + }); + } +} +//# sourceMappingURL=NavigationManager.js.map \ No newline at end of file diff --git a/wwwroot/js/managers/NavigationManager.js.map b/wwwroot/js/managers/NavigationManager.js.map new file mode 100644 index 0000000..8b411ff --- /dev/null +++ b/wwwroot/js/managers/NavigationManager.js.map @@ -0,0 +1 @@ +{"version":3,"file":"NavigationManager.js","sourceRoot":"","sources":["../../../src/managers/NavigationManager.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/managers/ResizeHandleManager.d.ts b/wwwroot/js/managers/ResizeHandleManager.d.ts new file mode 100644 index 0000000..90f9d9c --- /dev/null +++ b/wwwroot/js/managers/ResizeHandleManager.d.ts @@ -0,0 +1,42 @@ +import { Configuration } from '../configurations/CalendarConfig'; +import { PositionUtils } from '../utils/PositionUtils'; +export declare class ResizeHandleManager { + private config; + private positionUtils; + private isResizing; + private targetEl; + private startY; + private startDurationMin; + private snapMin; + private minDurationMin; + private animationId; + private currentHeight; + private targetHeight; + private pointerCaptured; + private prevZ?; + private readonly ANIMATION_SPEED; + private readonly Z_INDEX_RESIZING; + private readonly EVENT_REFRESH_THRESHOLD; + constructor(config: Configuration, positionUtils: PositionUtils); + initialize(): void; + destroy(): void; + private removeEventListeners; + private createResizeHandle; + private attachGlobalListeners; + private onMouseOver; + private onPointerDown; + private startResizing; + private setZIndexForResizing; + private capturePointer; + private onPointerMove; + private updateResizeHeight; + private animate; + private finalizeAnimation; + private onPointerUp; + private cleanupAnimation; + private snapToGrid; + private emitResizeEndEvent; + private cleanupResizing; + private restoreZIndex; + private releasePointer; +} diff --git a/wwwroot/js/managers/ResizeHandleManager.js b/wwwroot/js/managers/ResizeHandleManager.js new file mode 100644 index 0000000..c753f42 --- /dev/null +++ b/wwwroot/js/managers/ResizeHandleManager.js @@ -0,0 +1,194 @@ +import { eventBus } from '../core/EventBus'; +export class ResizeHandleManager { + constructor(config, positionUtils) { + this.config = config; + this.positionUtils = positionUtils; + this.isResizing = false; + this.targetEl = null; + this.startY = 0; + this.startDurationMin = 0; + this.animationId = null; + this.currentHeight = 0; + this.targetHeight = 0; + this.pointerCaptured = false; + // Constants for better maintainability + this.ANIMATION_SPEED = 0.35; + this.Z_INDEX_RESIZING = '1000'; + this.EVENT_REFRESH_THRESHOLD = 0.5; + this.onMouseOver = (e) => { + const target = e.target; + const eventElement = target.closest('swp-event'); + if (eventElement && !this.isResizing) { + // Check if handle already exists + if (!eventElement.querySelector(':scope > swp-resize-handle')) { + const handle = this.createResizeHandle(); + eventElement.appendChild(handle); + } + } + }; + this.onPointerDown = (e) => { + const handle = e.target.closest('swp-resize-handle'); + if (!handle) + return; + const element = handle.parentElement; + this.startResizing(element, e); + }; + this.onPointerMove = (e) => { + if (!this.isResizing || !this.targetEl) + return; + this.updateResizeHeight(e.clientY); + }; + this.animate = () => { + if (!this.isResizing || !this.targetEl) { + this.animationId = null; + return; + } + const diff = this.targetHeight - this.currentHeight; + if (Math.abs(diff) > this.EVENT_REFRESH_THRESHOLD) { + this.currentHeight += diff * this.ANIMATION_SPEED; + this.targetEl.updateHeight?.(this.currentHeight); + this.animationId = requestAnimationFrame(this.animate); + } + else { + this.finalizeAnimation(); + } + }; + this.onPointerUp = (e) => { + if (!this.isResizing || !this.targetEl) + return; + this.cleanupAnimation(); + this.snapToGrid(); + this.emitResizeEndEvent(); + this.cleanupResizing(e); + }; + const grid = this.config.gridSettings; + this.snapMin = grid.snapInterval; + this.minDurationMin = this.snapMin; + } + initialize() { + this.attachGlobalListeners(); + } + destroy() { + this.removeEventListeners(); + } + removeEventListeners() { + const calendarContainer = document.querySelector('swp-calendar-container'); + if (calendarContainer) { + calendarContainer.removeEventListener('mouseover', this.onMouseOver, true); + } + document.removeEventListener('pointerdown', this.onPointerDown, true); + document.removeEventListener('pointermove', this.onPointerMove, true); + document.removeEventListener('pointerup', this.onPointerUp, true); + } + createResizeHandle() { + const handle = document.createElement('swp-resize-handle'); + handle.setAttribute('aria-label', 'Resize event'); + handle.setAttribute('role', 'separator'); + return handle; + } + attachGlobalListeners() { + const calendarContainer = document.querySelector('swp-calendar-container'); + if (calendarContainer) { + calendarContainer.addEventListener('mouseover', this.onMouseOver, true); + } + document.addEventListener('pointerdown', this.onPointerDown, true); + document.addEventListener('pointermove', this.onPointerMove, true); + document.addEventListener('pointerup', this.onPointerUp, true); + } + startResizing(element, event) { + this.targetEl = element; + this.isResizing = true; + this.startY = event.clientY; + const startHeight = element.offsetHeight; + this.startDurationMin = Math.max(this.minDurationMin, Math.round(this.positionUtils.pixelsToMinutes(startHeight))); + this.setZIndexForResizing(element); + this.capturePointer(event); + document.documentElement.classList.add('swp--resizing'); + event.preventDefault(); + } + setZIndexForResizing(element) { + const container = element.closest('swp-event-group') ?? element; + this.prevZ = container.style.zIndex; + container.style.zIndex = this.Z_INDEX_RESIZING; + } + capturePointer(event) { + try { + event.target.setPointerCapture?.(event.pointerId); + this.pointerCaptured = true; + } + catch (error) { + console.warn('Pointer capture failed:', error); + } + } + updateResizeHeight(currentY) { + const deltaY = currentY - this.startY; + const startHeight = this.positionUtils.minutesToPixels(this.startDurationMin); + const rawHeight = startHeight + deltaY; + const minHeight = this.positionUtils.minutesToPixels(this.minDurationMin); + this.targetHeight = Math.max(minHeight, rawHeight); + if (this.animationId == null) { + this.currentHeight = this.targetEl?.offsetHeight; + this.animate(); + } + } + finalizeAnimation() { + if (!this.targetEl) + return; + this.currentHeight = this.targetHeight; + this.targetEl.updateHeight?.(this.currentHeight); + this.animationId = null; + } + cleanupAnimation() { + if (this.animationId != null) { + cancelAnimationFrame(this.animationId); + this.animationId = null; + } + } + snapToGrid() { + if (!this.targetEl) + return; + const currentHeight = this.targetEl.offsetHeight; + const snapDistancePx = this.positionUtils.minutesToPixels(this.snapMin); + const snappedHeight = Math.round(currentHeight / snapDistancePx) * snapDistancePx; + const minHeight = this.positionUtils.minutesToPixels(this.minDurationMin); + const finalHeight = Math.max(minHeight, snappedHeight) - 3; // Small gap to grid lines + this.targetEl.updateHeight?.(finalHeight); + } + emitResizeEndEvent() { + if (!this.targetEl) + return; + const eventId = this.targetEl.dataset.eventId || ''; + const resizeEndPayload = { + eventId, + element: this.targetEl, + finalHeight: this.targetEl.offsetHeight + }; + eventBus.emit('resize:end', resizeEndPayload); + } + cleanupResizing(event) { + this.restoreZIndex(); + this.releasePointer(event); + this.isResizing = false; + this.targetEl = null; + document.documentElement.classList.remove('swp--resizing'); + } + restoreZIndex() { + if (!this.targetEl || this.prevZ === undefined) + return; + const container = this.targetEl.closest('swp-event-group') ?? this.targetEl; + container.style.zIndex = this.prevZ; + this.prevZ = undefined; + } + releasePointer(event) { + if (!this.pointerCaptured) + return; + try { + event.target.releasePointerCapture?.(event.pointerId); + this.pointerCaptured = false; + } + catch (error) { + console.warn('Pointer release failed:', error); + } + } +} +//# sourceMappingURL=ResizeHandleManager.js.map \ No newline at end of file diff --git a/wwwroot/js/managers/ResizeHandleManager.js.map b/wwwroot/js/managers/ResizeHandleManager.js.map new file mode 100644 index 0000000..fa05fae --- /dev/null +++ b/wwwroot/js/managers/ResizeHandleManager.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ResizeHandleManager.js","sourceRoot":"","sources":["../../../src/managers/ResizeHandleManager.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/managers/ScrollManager.d.ts b/wwwroot/js/managers/ScrollManager.d.ts new file mode 100644 index 0000000..a3eda8a --- /dev/null +++ b/wwwroot/js/managers/ScrollManager.d.ts @@ -0,0 +1,64 @@ +import { PositionUtils } from '../utils/PositionUtils'; +/** + * Manages scrolling functionality for the calendar using native scrollbars + */ +export declare class ScrollManager { + private scrollableContent; + private calendarContainer; + private timeAxis; + private calendarHeader; + private resizeObserver; + private positionUtils; + constructor(positionUtils: PositionUtils); + private init; + /** + * Public method to initialize scroll after grid is rendered + */ + initialize(): void; + private subscribeToEvents; + /** + * Setup scrolling functionality after grid is rendered + */ + private setupScrolling; + /** + * Find DOM elements needed for scrolling + */ + private findElements; + /** + * Scroll to specific position + */ + scrollTo(scrollTop: number): void; + /** + * Scroll to specific hour using PositionUtils + */ + scrollToHour(hour: number): void; + /** + * Scroll to specific event time + * @param eventStartTime ISO string of event start time + */ + scrollToEventTime(eventStartTime: string): void; + /** + * Setup ResizeObserver to monitor container size changes + */ + private setupResizeObserver; + /** + * Calculate and update scrollable content height dynamically + */ + private updateScrollableHeight; + /** + * Setup scroll synchronization between scrollable content and time axis + */ + private setupScrollSynchronization; + /** + * Synchronize time axis position with scrollable content + */ + private syncTimeAxisPosition; + /** + * Setup horizontal scroll synchronization between scrollable content and calendar header + */ + private setupHorizontalScrollSynchronization; + /** + * Synchronize calendar header position with scrollable content horizontal scroll + */ + private syncCalendarHeaderPosition; +} diff --git a/wwwroot/js/managers/ScrollManager.js b/wwwroot/js/managers/ScrollManager.js new file mode 100644 index 0000000..c14533a --- /dev/null +++ b/wwwroot/js/managers/ScrollManager.js @@ -0,0 +1,217 @@ +// Custom scroll management for calendar week container +import { eventBus } from '../core/EventBus'; +import { CoreEvents } from '../constants/CoreEvents'; +/** + * Manages scrolling functionality for the calendar using native scrollbars + */ +export class ScrollManager { + constructor(positionUtils) { + this.scrollableContent = null; + this.calendarContainer = null; + this.timeAxis = null; + this.calendarHeader = null; + this.resizeObserver = null; + this.positionUtils = positionUtils; + this.init(); + } + init() { + this.subscribeToEvents(); + } + /** + * Public method to initialize scroll after grid is rendered + */ + initialize() { + this.setupScrolling(); + } + subscribeToEvents() { + // Handle navigation animation completion - sync time axis position + eventBus.on(CoreEvents.NAVIGATION_COMPLETED, () => { + this.syncTimeAxisPosition(); + this.setupScrolling(); + }); + // Handle all-day row height changes + eventBus.on('header:height-changed', () => { + this.updateScrollableHeight(); + }); + // Handle header ready - refresh header reference and re-sync + eventBus.on('header:ready', () => { + this.calendarHeader = document.querySelector('swp-calendar-header'); + if (this.scrollableContent && this.calendarHeader) { + this.setupHorizontalScrollSynchronization(); + this.syncCalendarHeaderPosition(); // Immediately sync position + } + this.updateScrollableHeight(); // Update height calculations + }); + // Handle window resize + window.addEventListener('resize', () => { + this.updateScrollableHeight(); + }); + // Listen for scroll to event time requests + eventBus.on('scroll:to-event-time', (event) => { + const customEvent = event; + const { eventStartTime } = customEvent.detail; + if (eventStartTime) { + this.scrollToEventTime(eventStartTime); + } + }); + } + /** + * Setup scrolling functionality after grid is rendered + */ + setupScrolling() { + this.findElements(); + if (this.scrollableContent && this.calendarContainer) { + this.setupResizeObserver(); + this.updateScrollableHeight(); + this.setupScrollSynchronization(); + } + // Setup horizontal scrolling synchronization + if (this.scrollableContent && this.calendarHeader) { + this.setupHorizontalScrollSynchronization(); + } + } + /** + * Find DOM elements needed for scrolling + */ + findElements() { + this.scrollableContent = document.querySelector('swp-scrollable-content'); + this.calendarContainer = document.querySelector('swp-calendar-container'); + this.timeAxis = document.querySelector('swp-time-axis'); + this.calendarHeader = document.querySelector('swp-calendar-header'); + } + /** + * Scroll to specific position + */ + scrollTo(scrollTop) { + if (!this.scrollableContent) + return; + this.scrollableContent.scrollTop = scrollTop; + } + /** + * Scroll to specific hour using PositionUtils + */ + scrollToHour(hour) { + // Create time string for the hour + const timeString = `${hour.toString().padStart(2, '0')}:00`; + const scrollTop = this.positionUtils.timeToPixels(timeString); + this.scrollTo(scrollTop); + } + /** + * Scroll to specific event time + * @param eventStartTime ISO string of event start time + */ + scrollToEventTime(eventStartTime) { + try { + const eventDate = new Date(eventStartTime); + const eventHour = eventDate.getHours(); + const eventMinutes = eventDate.getMinutes(); + // Convert to decimal hour (e.g., 14:30 becomes 14.5) + const decimalHour = eventHour + (eventMinutes / 60); + this.scrollToHour(decimalHour); + } + catch (error) { + console.warn('ScrollManager: Failed to scroll to event time:', error); + } + } + /** + * Setup ResizeObserver to monitor container size changes + */ + setupResizeObserver() { + if (!this.calendarContainer) + return; + // Clean up existing observer + if (this.resizeObserver) { + this.resizeObserver.disconnect(); + } + this.resizeObserver = new ResizeObserver((entries) => { + for (const entry of entries) { + this.updateScrollableHeight(); + } + }); + this.resizeObserver.observe(this.calendarContainer); + } + /** + * Calculate and update scrollable content height dynamically + */ + updateScrollableHeight() { + if (!this.scrollableContent || !this.calendarContainer) + return; + // Get calendar container height + const containerRect = this.calendarContainer.getBoundingClientRect(); + // Find navigation height + const navigation = document.querySelector('swp-calendar-nav'); + const navHeight = navigation ? navigation.getBoundingClientRect().height : 0; + // Find calendar header height + const calendarHeaderElement = document.querySelector('swp-calendar-header'); + const headerHeight = calendarHeaderElement ? calendarHeaderElement.getBoundingClientRect().height : 80; + // Calculate available height for scrollable content + const availableHeight = containerRect.height - headerHeight; + // Calculate available width (container width minus time-axis) + const availableWidth = containerRect.width - 60; // 60px time-axis + // Set the height and width on scrollable content + if (availableHeight > 0) { + this.scrollableContent.style.height = `${availableHeight}px`; + } + if (availableWidth > 0) { + this.scrollableContent.style.width = `${availableWidth}px`; + } + } + /** + * Setup scroll synchronization between scrollable content and time axis + */ + setupScrollSynchronization() { + if (!this.scrollableContent || !this.timeAxis) + return; + // Throttle scroll events for better performance + let scrollTimeout = null; + this.scrollableContent.addEventListener('scroll', () => { + if (scrollTimeout) { + cancelAnimationFrame(scrollTimeout); + } + scrollTimeout = requestAnimationFrame(() => { + this.syncTimeAxisPosition(); + }); + }); + } + /** + * Synchronize time axis position with scrollable content + */ + syncTimeAxisPosition() { + if (!this.scrollableContent || !this.timeAxis) + return; + const scrollTop = this.scrollableContent.scrollTop; + const timeAxisContent = this.timeAxis.querySelector('swp-time-axis-content'); + if (timeAxisContent) { + // Use transform for smooth performance + timeAxisContent.style.transform = `translateY(-${scrollTop}px)`; + // Debug logging (can be removed later) + if (scrollTop % 100 === 0) { // Only log every 100px to avoid spam + } + } + } + /** + * Setup horizontal scroll synchronization between scrollable content and calendar header + */ + setupHorizontalScrollSynchronization() { + if (!this.scrollableContent || !this.calendarHeader) + return; + // Listen to horizontal scroll events + this.scrollableContent.addEventListener('scroll', () => { + this.syncCalendarHeaderPosition(); + }); + } + /** + * Synchronize calendar header position with scrollable content horizontal scroll + */ + syncCalendarHeaderPosition() { + if (!this.scrollableContent || !this.calendarHeader) + return; + const scrollLeft = this.scrollableContent.scrollLeft; + // Use transform for smooth performance + this.calendarHeader.style.transform = `translateX(-${scrollLeft}px)`; + // Debug logging (can be removed later) + if (scrollLeft % 100 === 0) { // Only log every 100px to avoid spam + } + } +} +//# sourceMappingURL=ScrollManager.js.map \ No newline at end of file diff --git a/wwwroot/js/managers/ScrollManager.js.map b/wwwroot/js/managers/ScrollManager.js.map new file mode 100644 index 0000000..63c28e1 --- /dev/null +++ b/wwwroot/js/managers/ScrollManager.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ScrollManager.js","sourceRoot":"","sources":["../../../src/managers/ScrollManager.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/managers/SimpleEventOverlapManager.d.ts b/wwwroot/js/managers/SimpleEventOverlapManager.d.ts new file mode 100644 index 0000000..a3dce25 --- /dev/null +++ b/wwwroot/js/managers/SimpleEventOverlapManager.d.ts @@ -0,0 +1,80 @@ +/** + * SimpleEventOverlapManager - Clean, focused overlap management + * Eliminates complex state tracking in favor of direct DOM manipulation + */ +import { CalendarEvent } from '../types/CalendarTypes'; +export declare enum OverlapType { + NONE = "none", + COLUMN_SHARING = "column_sharing", + STACKING = "stacking" +} +export interface OverlapGroup { + type: OverlapType; + events: CalendarEvent[]; + position: { + top: number; + height: number; + }; +} +export interface StackLink { + prev?: string; + next?: string; + stackLevel: number; +} +export declare class SimpleEventOverlapManager { + private static readonly STACKING_WIDTH_REDUCTION_PX; + /** + * Detect overlap type between two DOM elements - pixel-based logic + */ + resolveOverlapType(element1: HTMLElement, element2: HTMLElement): OverlapType; + /** + * Group overlapping elements - pixel-based algorithm + */ + groupOverlappingElements(elements: HTMLElement[]): HTMLElement[][]; + /** + * Create flexbox container for column sharing - clean and simple + */ + createEventGroup(events: CalendarEvent[], position: { + top: number; + height: number; + }): HTMLElement; + /** + * Add event to flexbox group - simple relative positioning + */ + addToEventGroup(container: HTMLElement, eventElement: HTMLElement): void; + /** + * Create stacked event with data-attribute tracking + */ + createStackedEvent(eventElement: HTMLElement, underlyingElement: HTMLElement, stackLevel: number): void; + /** + * Remove stacked styling with proper stack re-linking + */ + removeStackedStyling(eventElement: HTMLElement): void; + /** + * Update stack levels for all events following a given event ID + */ + private updateSubsequentStackLevels; + /** + * Check if element is stacked - check both style and data-stack-link + */ + isStackedEvent(element: HTMLElement): boolean; + /** + * Remove event from group with proper cleanup + */ + removeFromEventGroup(container: HTMLElement, eventId: string): boolean; + /** + * Restack events in container - respects separate stack chains + */ + restackEventsInContainer(container: HTMLElement): void; + /** + * Utility methods - simple DOM traversal + */ + getEventGroup(eventElement: HTMLElement): HTMLElement | null; + isInEventGroup(element: HTMLElement): boolean; + /** + * Helper methods for data-attribute based stack tracking + */ + getStackLink(element: HTMLElement): StackLink | null; + private setStackLink; + private findElementById; +} diff --git a/wwwroot/js/managers/SimpleEventOverlapManager.js b/wwwroot/js/managers/SimpleEventOverlapManager.js new file mode 100644 index 0000000..b782f02 --- /dev/null +++ b/wwwroot/js/managers/SimpleEventOverlapManager.js @@ -0,0 +1,399 @@ +/** + * SimpleEventOverlapManager - Clean, focused overlap management + * Eliminates complex state tracking in favor of direct DOM manipulation + */ +import { calendarConfig } from '../core/CalendarConfig'; +export var OverlapType; +(function (OverlapType) { + OverlapType["NONE"] = "none"; + OverlapType["COLUMN_SHARING"] = "column_sharing"; + OverlapType["STACKING"] = "stacking"; +})(OverlapType || (OverlapType = {})); +export class SimpleEventOverlapManager { + /** + * Detect overlap type between two DOM elements - pixel-based logic + */ + resolveOverlapType(element1, element2) { + const top1 = parseInt(element1.style.top) || 0; + const height1 = parseInt(element1.style.height) || 0; + const bottom1 = top1 + height1; + const top2 = parseInt(element2.style.top) || 0; + const height2 = parseInt(element2.style.height) || 0; + const bottom2 = top2 + height2; + // Check if events overlap in pixel space + const tolerance = 2; + if (bottom1 <= (top2 + tolerance) || bottom2 <= (top1 + tolerance)) { + return OverlapType.NONE; + } + // Events overlap - check start position difference for overlap type + const startDifference = Math.abs(top1 - top2); + // Over 40px start difference = stacking + if (startDifference > 40) { + return OverlapType.STACKING; + } + // Within 40px start difference = column sharing + return OverlapType.COLUMN_SHARING; + } + /** + * Group overlapping elements - pixel-based algorithm + */ + groupOverlappingElements(elements) { + const groups = []; + const processed = new Set(); + for (const element of elements) { + if (processed.has(element)) + continue; + // Find all elements that overlap with this one + const overlapping = elements.filter(other => { + if (processed.has(other)) + return false; + return other === element || this.resolveOverlapType(element, other) !== OverlapType.NONE; + }); + // Mark all as processed + overlapping.forEach(e => processed.add(e)); + groups.push(overlapping); + } + return groups; + } + /** + * Create flexbox container for column sharing - clean and simple + */ + createEventGroup(events, position) { + const container = document.createElement('swp-event-group'); + return container; + } + /** + * Add event to flexbox group - simple relative positioning + */ + addToEventGroup(container, eventElement) { + // Set duration-based height + const duration = eventElement.dataset.duration; + if (duration) { + const durationMinutes = parseInt(duration); + const gridSettings = calendarConfig.getGridSettings(); + const height = (durationMinutes / 60) * gridSettings.hourHeight; + eventElement.style.height = `${height - 3}px`; + } + // Flexbox styling + eventElement.style.position = 'relative'; + eventElement.style.flex = '1'; + eventElement.style.minWidth = '50px'; + container.appendChild(eventElement); + } + /** + * Create stacked event with data-attribute tracking + */ + createStackedEvent(eventElement, underlyingElement, stackLevel) { + const marginLeft = stackLevel * SimpleEventOverlapManager.STACKING_WIDTH_REDUCTION_PX; + // Apply visual styling + eventElement.style.marginLeft = `${marginLeft}px`; + eventElement.style.left = '2px'; + eventElement.style.right = '2px'; + eventElement.style.zIndex = `${100 + stackLevel}`; + // Set up stack linking via data attributes + const eventId = eventElement.dataset.eventId; + const underlyingId = underlyingElement.dataset.eventId; + if (!eventId || !underlyingId) { + console.warn('Missing event IDs for stack linking:', eventId, underlyingId); + return; + } + // Find the last event in the stack chain + let lastElement = underlyingElement; + let lastLink = this.getStackLink(lastElement); + // If underlying doesn't have stack link yet, create it + if (!lastLink) { + this.setStackLink(lastElement, { stackLevel: 0 }); + lastLink = { stackLevel: 0 }; + } + // Traverse to find the end of the chain + while (lastLink?.next) { + const nextElement = this.findElementById(lastLink.next); + if (!nextElement) + break; + lastElement = nextElement; + lastLink = this.getStackLink(lastElement); + } + // Link the new event to the end of the chain + const lastElementId = lastElement.dataset.eventId; + this.setStackLink(lastElement, { + ...lastLink, + next: eventId + }); + this.setStackLink(eventElement, { + prev: lastElementId, + stackLevel: stackLevel + }); + } + /** + * Remove stacked styling with proper stack re-linking + */ + removeStackedStyling(eventElement) { + // Clear visual styling + eventElement.style.marginLeft = ''; + eventElement.style.zIndex = ''; + eventElement.style.left = '2px'; + eventElement.style.right = '2px'; + // Handle stack chain re-linking + const link = this.getStackLink(eventElement); + if (link) { + // Re-link prev and next events + if (link.prev && link.next) { + // Middle element - link prev to next + const prevElement = this.findElementById(link.prev); + const nextElement = this.findElementById(link.next); + if (prevElement && nextElement) { + const prevLink = this.getStackLink(prevElement); + const nextLink = this.getStackLink(nextElement); + // CRITICAL: Check if prev and next actually overlap without the middle element + const actuallyOverlap = this.resolveOverlapType(prevElement, nextElement); + if (!actuallyOverlap) { + // CHAIN BREAKING: prev and next don't overlap - break the chain + console.log('Breaking stack chain - events do not overlap directly'); + // Prev element: remove next link (becomes end of its own chain) + this.setStackLink(prevElement, { + ...prevLink, + next: undefined + }); + // Next element: becomes standalone (remove all stack links and styling) + this.setStackLink(nextElement, null); + nextElement.style.marginLeft = ''; + nextElement.style.zIndex = ''; + // If next element had subsequent events, they also become standalone + if (nextLink?.next) { + let subsequentId = nextLink.next; + while (subsequentId) { + const subsequentElement = this.findElementById(subsequentId); + if (!subsequentElement) + break; + const subsequentLink = this.getStackLink(subsequentElement); + this.setStackLink(subsequentElement, null); + subsequentElement.style.marginLeft = ''; + subsequentElement.style.zIndex = ''; + subsequentId = subsequentLink?.next; + } + } + } + else { + // NORMAL STACKING: they overlap, maintain the chain + this.setStackLink(prevElement, { + ...prevLink, + next: link.next + }); + const correctStackLevel = (prevLink?.stackLevel ?? 0) + 1; + this.setStackLink(nextElement, { + ...nextLink, + prev: link.prev, + stackLevel: correctStackLevel + }); + // Update visual styling to match new stackLevel + const marginLeft = correctStackLevel * SimpleEventOverlapManager.STACKING_WIDTH_REDUCTION_PX; + nextElement.style.marginLeft = `${marginLeft}px`; + nextElement.style.zIndex = `${100 + correctStackLevel}`; + } + } + } + else if (link.prev) { + // Last element - remove next link from prev + const prevElement = this.findElementById(link.prev); + if (prevElement) { + const prevLink = this.getStackLink(prevElement); + this.setStackLink(prevElement, { + ...prevLink, + next: undefined + }); + } + } + else if (link.next) { + // First element - remove prev link from next + const nextElement = this.findElementById(link.next); + if (nextElement) { + const nextLink = this.getStackLink(nextElement); + this.setStackLink(nextElement, { + ...nextLink, + prev: undefined, + stackLevel: 0 // Next becomes the base event + }); + } + } + // Only update subsequent stack levels if we didn't break the chain + if (link.prev && link.next) { + const nextElement = this.findElementById(link.next); + const nextLink = nextElement ? this.getStackLink(nextElement) : null; + // If next element still has a stack link, the chain wasn't broken + if (nextLink && nextLink.next) { + this.updateSubsequentStackLevels(nextLink.next, -1); + } + // If nextLink is null, chain was broken - no subsequent updates needed + } + else { + // First or last removal - update all subsequent + this.updateSubsequentStackLevels(link.next, -1); + } + // Clear this element's stack link + this.setStackLink(eventElement, null); + } + } + /** + * Update stack levels for all events following a given event ID + */ + updateSubsequentStackLevels(startEventId, levelDelta) { + let currentId = startEventId; + while (currentId) { + const currentElement = this.findElementById(currentId); + if (!currentElement) + break; + const currentLink = this.getStackLink(currentElement); + if (!currentLink) + break; + // Update stack level + const newLevel = Math.max(0, currentLink.stackLevel + levelDelta); + this.setStackLink(currentElement, { + ...currentLink, + stackLevel: newLevel + }); + // Update visual styling + const marginLeft = newLevel * SimpleEventOverlapManager.STACKING_WIDTH_REDUCTION_PX; + currentElement.style.marginLeft = `${marginLeft}px`; + currentElement.style.zIndex = `${100 + newLevel}`; + currentId = currentLink.next; + } + } + /** + * Check if element is stacked - check both style and data-stack-link + */ + isStackedEvent(element) { + const marginLeft = element.style.marginLeft; + const hasMarginLeft = marginLeft !== '' && marginLeft !== '0px'; + const hasStackLink = this.getStackLink(element) !== null; + return hasMarginLeft || hasStackLink; + } + /** + * Remove event from group with proper cleanup + */ + removeFromEventGroup(container, eventId) { + const eventElement = container.querySelector(`swp-event[data-event-id="${eventId}"]`); + if (!eventElement) + return false; + // Simply remove the element - no position calculation needed since it's being removed + eventElement.remove(); + // Handle remaining events + const remainingEvents = container.querySelectorAll('swp-event'); + const remainingCount = remainingEvents.length; + if (remainingCount === 0) { + container.remove(); + return true; + } + if (remainingCount === 1) { + const remainingEvent = remainingEvents[0]; + // Convert last event back to absolute positioning - use current pixel position + const currentTop = parseInt(remainingEvent.style.top) || 0; + remainingEvent.style.position = 'absolute'; + remainingEvent.style.top = `${currentTop}px`; + remainingEvent.style.left = '2px'; + remainingEvent.style.right = '2px'; + remainingEvent.style.flex = ''; + remainingEvent.style.minWidth = ''; + container.parentElement?.insertBefore(remainingEvent, container); + container.remove(); + return true; + } + return false; + } + /** + * Restack events in container - respects separate stack chains + */ + restackEventsInContainer(container) { + const stackedEvents = Array.from(container.querySelectorAll('swp-event')) + .filter(el => this.isStackedEvent(el)); + if (stackedEvents.length === 0) + return; + // Group events by their stack chains + const processedEventIds = new Set(); + const stackChains = []; + for (const element of stackedEvents) { + const eventId = element.dataset.eventId; + if (!eventId || processedEventIds.has(eventId)) + continue; + // Find the root of this stack chain (stackLevel 0 or no prev link) + let rootElement = element; + let rootLink = this.getStackLink(rootElement); + while (rootLink?.prev) { + const prevElement = this.findElementById(rootLink.prev); + if (!prevElement) + break; + rootElement = prevElement; + rootLink = this.getStackLink(rootElement); + } + // Collect all elements in this chain + const chain = []; + let currentElement = rootElement; + while (currentElement) { + chain.push(currentElement); + processedEventIds.add(currentElement.dataset.eventId); + const currentLink = this.getStackLink(currentElement); + if (!currentLink?.next) + break; + const nextElement = this.findElementById(currentLink.next); + if (!nextElement) + break; + currentElement = nextElement; + } + if (chain.length > 1) { // Only add chains with multiple events + stackChains.push(chain); + } + } + // Re-stack each chain separately + stackChains.forEach(chain => { + chain.forEach((element, index) => { + const marginLeft = index * SimpleEventOverlapManager.STACKING_WIDTH_REDUCTION_PX; + element.style.marginLeft = `${marginLeft}px`; + element.style.zIndex = `${100 + index}`; + // Update the data-stack-link with correct stackLevel + const link = this.getStackLink(element); + if (link) { + this.setStackLink(element, { + ...link, + stackLevel: index + }); + } + }); + }); + } + /** + * Utility methods - simple DOM traversal + */ + getEventGroup(eventElement) { + return eventElement.closest('swp-event-group'); + } + isInEventGroup(element) { + return this.getEventGroup(element) !== null; + } + /** + * Helper methods for data-attribute based stack tracking + */ + getStackLink(element) { + const linkData = element.dataset.stackLink; + if (!linkData) + return null; + try { + return JSON.parse(linkData); + } + catch (e) { + console.warn('Failed to parse stack link data:', linkData, e); + return null; + } + } + setStackLink(element, link) { + if (link === null) { + delete element.dataset.stackLink; + } + else { + element.dataset.stackLink = JSON.stringify(link); + } + } + findElementById(eventId) { + return document.querySelector(`swp-event[data-event-id="${eventId}"]`); + } +} +SimpleEventOverlapManager.STACKING_WIDTH_REDUCTION_PX = 15; +//# sourceMappingURL=SimpleEventOverlapManager.js.map \ No newline at end of file diff --git a/wwwroot/js/managers/SimpleEventOverlapManager.js.map b/wwwroot/js/managers/SimpleEventOverlapManager.js.map new file mode 100644 index 0000000..173a398 --- /dev/null +++ b/wwwroot/js/managers/SimpleEventOverlapManager.js.map @@ -0,0 +1 @@ +{"version":3,"file":"SimpleEventOverlapManager.js","sourceRoot":"","sources":["../../../src/managers/SimpleEventOverlapManager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,MAAM,CAAN,IAAY,WAIX;AAJD,WAAY,WAAW;IACrB,4BAAa,CAAA;IACb,gDAAiC,CAAA;IACjC,oCAAqB,CAAA;AACvB,CAAC,EAJW,WAAW,KAAX,WAAW,QAItB;AAcD,MAAM,OAAO,yBAAyB;IAGpC;;OAEG;IACI,kBAAkB,CAAC,QAAqB,EAAE,QAAqB;QACpE,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC/C,MAAM,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACrD,MAAM,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC;QAE/B,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC/C,MAAM,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACrD,MAAM,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC;QAE/B,yCAAyC;QACzC,MAAM,SAAS,GAAG,CAAC,CAAC;QACpB,IAAI,OAAO,IAAI,CAAC,IAAI,GAAG,SAAS,CAAC,IAAI,OAAO,IAAI,CAAC,IAAI,GAAG,SAAS,CAAC,EAAE,CAAC;YACnE,OAAO,WAAW,CAAC,IAAI,CAAC;QAC1B,CAAC;QAED,oEAAoE;QACpE,MAAM,eAAe,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;QAE9C,wCAAwC;QACxC,IAAI,eAAe,GAAG,EAAE,EAAE,CAAC;YACzB,OAAO,WAAW,CAAC,QAAQ,CAAC;QAC9B,CAAC;QAED,gDAAgD;QAChD,OAAO,WAAW,CAAC,cAAc,CAAC;IACpC,CAAC;IAGD;;OAEG;IACI,wBAAwB,CAAC,QAAuB;QACrD,MAAM,MAAM,GAAoB,EAAE,CAAC;QACnC,MAAM,SAAS,GAAG,IAAI,GAAG,EAAe,CAAC;QAEzC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,IAAI,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC;gBAAE,SAAS;YAErC,+CAA+C;YAC/C,MAAM,WAAW,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;gBAC1C,IAAI,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC;oBAAE,OAAO,KAAK,CAAC;gBACvC,OAAO,KAAK,KAAK,OAAO,IAAI,IAAI,CAAC,kBAAkB,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,WAAW,CAAC,IAAI,CAAC;YAC3F,CAAC,CAAC,CAAC;YAEH,wBAAwB;YACxB,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAE3C,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC3B,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;OAEG;IACI,gBAAgB,CAAC,MAAuB,EAAE,QAAyC;QACxF,MAAM,SAAS,GAAG,QAAQ,CAAC,aAAa,CAAC,iBAAiB,CAAC,CAAC;QAC5D,OAAO,SAAS,CAAC;IACnB,CAAC;IAED;;OAEG;IACI,eAAe,CAAC,SAAsB,EAAE,YAAyB;QACtE,4BAA4B;QAC5B,MAAM,QAAQ,GAAG,YAAY,CAAC,OAAO,CAAC,QAAQ,CAAC;QAC/C,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,eAAe,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAC3C,MAAM,YAAY,GAAG,cAAc,CAAC,eAAe,EAAE,CAAC;YACtD,MAAM,MAAM,GAAG,CAAC,eAAe,GAAG,EAAE,CAAC,GAAG,YAAY,CAAC,UAAU,CAAC;YAChE,YAAY,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC;QAChD,CAAC;QAED,kBAAkB;QAClB,YAAY,CAAC,KAAK,CAAC,QAAQ,GAAG,UAAU,CAAC;QACzC,YAAY,CAAC,KAAK,CAAC,IAAI,GAAG,GAAG,CAAC;QAC9B,YAAY,CAAC,KAAK,CAAC,QAAQ,GAAG,MAAM,CAAC;QAErC,SAAS,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC;IACtC,CAAC;IAED;;OAEG;IACI,kBAAkB,CAAC,YAAyB,EAAE,iBAA8B,EAAE,UAAkB;QACrG,MAAM,UAAU,GAAG,UAAU,GAAG,yBAAyB,CAAC,2BAA2B,CAAC;QAEtF,uBAAuB;QACvB,YAAY,CAAC,KAAK,CAAC,UAAU,GAAG,GAAG,UAAU,IAAI,CAAC;QAClD,YAAY,CAAC,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC;QAChC,YAAY,CAAC,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC;QACjC,YAAY,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,GAAG,GAAG,UAAU,EAAE,CAAC;QAElD,2CAA2C;QAC3C,MAAM,OAAO,GAAG,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC;QAC7C,MAAM,YAAY,GAAG,iBAAiB,CAAC,OAAO,CAAC,OAAO,CAAC;QAEvD,IAAI,CAAC,OAAO,IAAI,CAAC,YAAY,EAAE,CAAC;YAC9B,OAAO,CAAC,IAAI,CAAC,sCAAsC,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC;YAC5E,OAAO;QACT,CAAC;QAED,yCAAyC;QACzC,IAAI,WAAW,GAAG,iBAAiB,CAAC;QACpC,IAAI,QAAQ,GAAG,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;QAE9C,uDAAuD;QACvD,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC,CAAC;YAClD,QAAQ,GAAG,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC;QAC/B,CAAC;QAED,wCAAwC;QACxC,OAAO,QAAQ,EAAE,IAAI,EAAE,CAAC;YACtB,MAAM,WAAW,GAAG,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YACxD,IAAI,CAAC,WAAW;gBAAE,MAAM;YACxB,WAAW,GAAG,WAAW,CAAC;YAC1B,QAAQ,GAAG,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;QAC5C,CAAC;QAED,6CAA6C;QAC7C,MAAM,aAAa,GAAG,WAAW,CAAC,OAAO,CAAC,OAAQ,CAAC;QACnD,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE;YAC7B,GAAG,QAAS;YACZ,IAAI,EAAE,OAAO;SACd,CAAC,CAAC;QAEH,IAAI,CAAC,YAAY,CAAC,YAAY,EAAE;YAC9B,IAAI,EAAE,aAAa;YACnB,UAAU,EAAE,UAAU;SACvB,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACI,oBAAoB,CAAC,YAAyB;QACnD,uBAAuB;QACvB,YAAY,CAAC,KAAK,CAAC,UAAU,GAAG,EAAE,CAAC;QACnC,YAAY,CAAC,KAAK,CAAC,MAAM,GAAG,EAAE,CAAC;QAC/B,YAAY,CAAC,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC;QAChC,YAAY,CAAC,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC;QAEjC,gCAAgC;QAChC,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;QAC7C,IAAI,IAAI,EAAE,CAAC;YACT,+BAA+B;YAC/B,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBAC3B,qCAAqC;gBACrC,MAAM,WAAW,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACpD,MAAM,WAAW,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAEpD,IAAI,WAAW,IAAI,WAAW,EAAE,CAAC;oBAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;oBAChD,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;oBAEhD,+EAA+E;oBAC/E,MAAM,eAAe,GAAG,IAAI,CAAC,kBAAkB,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;oBAE1E,IAAI,CAAC,eAAe,EAAE,CAAC;wBACrB,gEAAgE;wBAChE,OAAO,CAAC,GAAG,CAAC,uDAAuD,CAAC,CAAC;wBAErE,gEAAgE;wBAChE,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE;4BAC7B,GAAG,QAAS;4BACZ,IAAI,EAAE,SAAS;yBAChB,CAAC,CAAC;wBAEH,wEAAwE;wBACxE,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;wBACrC,WAAW,CAAC,KAAK,CAAC,UAAU,GAAG,EAAE,CAAC;wBAClC,WAAW,CAAC,KAAK,CAAC,MAAM,GAAG,EAAE,CAAC;wBAE9B,qEAAqE;wBACrE,IAAI,QAAQ,EAAE,IAAI,EAAE,CAAC;4BACnB,IAAI,YAAY,GAAuB,QAAQ,CAAC,IAAI,CAAC;4BACrD,OAAO,YAAY,EAAE,CAAC;gCACpB,MAAM,iBAAiB,GAAG,IAAI,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC;gCAC7D,IAAI,CAAC,iBAAiB;oCAAE,MAAM;gCAE9B,MAAM,cAAc,GAAG,IAAI,CAAC,YAAY,CAAC,iBAAiB,CAAC,CAAC;gCAC5D,IAAI,CAAC,YAAY,CAAC,iBAAiB,EAAE,IAAI,CAAC,CAAC;gCAC3C,iBAAiB,CAAC,KAAK,CAAC,UAAU,GAAG,EAAE,CAAC;gCACxC,iBAAiB,CAAC,KAAK,CAAC,MAAM,GAAG,EAAE,CAAC;gCAEpC,YAAY,GAAG,cAAc,EAAE,IAAI,CAAC;4BACtC,CAAC;wBACH,CAAC;oBACH,CAAC;yBAAM,CAAC;wBACN,oDAAoD;wBACpD,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE;4BAC7B,GAAG,QAAS;4BACZ,IAAI,EAAE,IAAI,CAAC,IAAI;yBAChB,CAAC,CAAC;wBAEH,MAAM,iBAAiB,GAAG,CAAC,QAAQ,EAAE,UAAU,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;wBAC1D,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE;4BAC7B,GAAG,QAAS;4BACZ,IAAI,EAAE,IAAI,CAAC,IAAI;4BACf,UAAU,EAAE,iBAAiB;yBAC9B,CAAC,CAAC;wBAEH,gDAAgD;wBAChD,MAAM,UAAU,GAAG,iBAAiB,GAAG,yBAAyB,CAAC,2BAA2B,CAAC;wBAC7F,WAAW,CAAC,KAAK,CAAC,UAAU,GAAG,GAAG,UAAU,IAAI,CAAC;wBACjD,WAAW,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,GAAG,GAAG,iBAAiB,EAAE,CAAC;oBAC1D,CAAC;gBACH,CAAC;YACH,CAAC;iBAAM,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBACrB,4CAA4C;gBAC5C,MAAM,WAAW,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACpD,IAAI,WAAW,EAAE,CAAC;oBAChB,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;oBAChD,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE;wBAC7B,GAAG,QAAS;wBACZ,IAAI,EAAE,SAAS;qBAChB,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;iBAAM,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBACrB,6CAA6C;gBAC7C,MAAM,WAAW,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACpD,IAAI,WAAW,EAAE,CAAC;oBAChB,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;oBAChD,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE;wBAC7B,GAAG,QAAS;wBACZ,IAAI,EAAE,SAAS;wBACf,UAAU,EAAE,CAAC,CAAE,8BAA8B;qBAC9C,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;YAED,mEAAmE;YACnE,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBAC3B,MAAM,WAAW,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACpD,MAAM,QAAQ,GAAG,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;gBAErE,kEAAkE;gBAClE,IAAI,QAAQ,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC;oBAC9B,IAAI,CAAC,2BAA2B,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;gBACtD,CAAC;gBACD,uEAAuE;YACzE,CAAC;iBAAM,CAAC;gBACN,gDAAgD;gBAChD,IAAI,CAAC,2BAA2B,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YAClD,CAAC;YAED,kCAAkC;YAClC,IAAI,CAAC,YAAY,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;IAED;;OAEG;IACK,2BAA2B,CAAC,YAAgC,EAAE,UAAkB;QACtF,IAAI,SAAS,GAAG,YAAY,CAAC;QAE7B,OAAO,SAAS,EAAE,CAAC;YACjB,MAAM,cAAc,GAAG,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC;YACvD,IAAI,CAAC,cAAc;gBAAE,MAAM;YAE3B,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,CAAC,cAAc,CAAC,CAAC;YACtD,IAAI,CAAC,WAAW;gBAAE,MAAM;YAExB,qBAAqB;YACrB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,CAAC,UAAU,GAAG,UAAU,CAAC,CAAC;YAClE,IAAI,CAAC,YAAY,CAAC,cAAc,EAAE;gBAChC,GAAG,WAAW;gBACd,UAAU,EAAE,QAAQ;aACrB,CAAC,CAAC;YAEH,wBAAwB;YACxB,MAAM,UAAU,GAAG,QAAQ,GAAG,yBAAyB,CAAC,2BAA2B,CAAC;YACpF,cAAc,CAAC,KAAK,CAAC,UAAU,GAAG,GAAG,UAAU,IAAI,CAAC;YACpD,cAAc,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,GAAG,GAAG,QAAQ,EAAE,CAAC;YAElD,SAAS,GAAG,WAAW,CAAC,IAAI,CAAC;QAC/B,CAAC;IACH,CAAC;IAED;;OAEG;IACI,cAAc,CAAC,OAAoB;QACxC,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC;QAC5C,MAAM,aAAa,GAAG,UAAU,KAAK,EAAE,IAAI,UAAU,KAAK,KAAK,CAAC;QAChE,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;QAEzD,OAAO,aAAa,IAAI,YAAY,CAAC;IACvC,CAAC;IAED;;OAEG;IACI,oBAAoB,CAAC,SAAsB,EAAE,OAAe;QACjE,MAAM,YAAY,GAAG,SAAS,CAAC,aAAa,CAAC,4BAA4B,OAAO,IAAI,CAAgB,CAAC;QACrG,IAAI,CAAC,YAAY;YAAE,OAAO,KAAK,CAAC;QAEhC,sFAAsF;QACtF,YAAY,CAAC,MAAM,EAAE,CAAC;QAEtB,0BAA0B;QAC1B,MAAM,eAAe,GAAG,SAAS,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAC;QAChE,MAAM,cAAc,GAAG,eAAe,CAAC,MAAM,CAAC;QAE9C,IAAI,cAAc,KAAK,CAAC,EAAE,CAAC;YACzB,SAAS,CAAC,MAAM,EAAE,CAAC;YACnB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,cAAc,KAAK,CAAC,EAAE,CAAC;YACzB,MAAM,cAAc,GAAG,eAAe,CAAC,CAAC,CAAgB,CAAC;YAEzD,+EAA+E;YAC/E,MAAM,UAAU,GAAG,QAAQ,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAE3D,cAAc,CAAC,KAAK,CAAC,QAAQ,GAAG,UAAU,CAAC;YAC3C,cAAc,CAAC,KAAK,CAAC,GAAG,GAAG,GAAG,UAAU,IAAI,CAAC;YAC7C,cAAc,CAAC,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC;YAClC,cAAc,CAAC,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC;YACnC,cAAc,CAAC,KAAK,CAAC,IAAI,GAAG,EAAE,CAAC;YAC/B,cAAc,CAAC,KAAK,CAAC,QAAQ,GAAG,EAAE,CAAC;YAEnC,SAAS,CAAC,aAAa,EAAE,YAAY,CAAC,cAAc,EAAE,SAAS,CAAC,CAAC;YACjE,SAAS,CAAC,MAAM,EAAE,CAAC;YACnB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;OAEG;IACI,wBAAwB,CAAC,SAAsB;QACpD,MAAM,aAAa,GAAG,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAC;aACtE,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,EAAiB,CAAC,CAAkB,CAAC;QAEzE,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAEvC,qCAAqC;QACrC,MAAM,iBAAiB,GAAG,IAAI,GAAG,EAAU,CAAC;QAC5C,MAAM,WAAW,GAAoB,EAAE,CAAC;QAExC,KAAK,MAAM,OAAO,IAAI,aAAa,EAAE,CAAC;YACpC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC;YACxC,IAAI,CAAC,OAAO,IAAI,iBAAiB,CAAC,GAAG,CAAC,OAAO,CAAC;gBAAE,SAAS;YAEzD,mEAAmE;YACnE,IAAI,WAAW,GAAG,OAAO,CAAC;YAC1B,IAAI,QAAQ,GAAG,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;YAE9C,OAAO,QAAQ,EAAE,IAAI,EAAE,CAAC;gBACtB,MAAM,WAAW,GAAG,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;gBACxD,IAAI,CAAC,WAAW;oBAAE,MAAM;gBACxB,WAAW,GAAG,WAAW,CAAC;gBAC1B,QAAQ,GAAG,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;YAC5C,CAAC;YAED,qCAAqC;YACrC,MAAM,KAAK,GAAkB,EAAE,CAAC;YAChC,IAAI,cAAc,GAAG,WAAW,CAAC;YAEjC,OAAO,cAAc,EAAE,CAAC;gBACtB,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;gBAC3B,iBAAiB,CAAC,GAAG,CAAC,cAAc,CAAC,OAAO,CAAC,OAAQ,CAAC,CAAC;gBAEvD,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,CAAC,cAAc,CAAC,CAAC;gBACtD,IAAI,CAAC,WAAW,EAAE,IAAI;oBAAE,MAAM;gBAE9B,MAAM,WAAW,GAAG,IAAI,CAAC,eAAe,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;gBAC3D,IAAI,CAAC,WAAW;oBAAE,MAAM;gBACxB,cAAc,GAAG,WAAW,CAAC;YAC/B,CAAC;YAED,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,CAAC,uCAAuC;gBAC7D,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC1B,CAAC;QACH,CAAC;QAED,iCAAiC;QACjC,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;YAC1B,KAAK,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE;gBAC/B,MAAM,UAAU,GAAG,KAAK,GAAG,yBAAyB,CAAC,2BAA2B,CAAC;gBACjF,OAAO,CAAC,KAAK,CAAC,UAAU,GAAG,GAAG,UAAU,IAAI,CAAC;gBAC7C,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,GAAG,GAAG,KAAK,EAAE,CAAC;gBAExC,qDAAqD;gBACrD,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;gBACxC,IAAI,IAAI,EAAE,CAAC;oBACT,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE;wBACzB,GAAG,IAAI;wBACP,UAAU,EAAE,KAAK;qBAClB,CAAC,CAAC;gBACL,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAGD;;OAEG;IACI,aAAa,CAAC,YAAyB;QAC5C,OAAO,YAAY,CAAC,OAAO,CAAC,iBAAiB,CAAgB,CAAC;IAChE,CAAC;IAEM,cAAc,CAAC,OAAoB;QACxC,OAAO,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;IAC9C,CAAC;IAED;;OAEG;IACI,YAAY,CAAC,OAAoB;QACtC,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC;QAC3C,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC;QAE3B,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC9B,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,CAAC,IAAI,CAAC,kCAAkC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;YAC9D,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAEO,YAAY,CAAC,OAAoB,EAAE,IAAsB;QAC/D,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YAClB,OAAO,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC;QACnC,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;IAEO,eAAe,CAAC,OAAe;QACrC,OAAO,QAAQ,CAAC,aAAa,CAAC,4BAA4B,OAAO,IAAI,CAAgB,CAAC;IACxF,CAAC;;AA3buB,qDAA2B,GAAG,EAAE,CAAC"} \ No newline at end of file diff --git a/wwwroot/js/managers/ViewManager.d.ts b/wwwroot/js/managers/ViewManager.d.ts new file mode 100644 index 0000000..11147b5 --- /dev/null +++ b/wwwroot/js/managers/ViewManager.d.ts @@ -0,0 +1,23 @@ +import { IEventBus } from '../types/CalendarTypes'; +import { Configuration } from '../configurations/CalendarConfig'; +export declare class ViewManager { + private eventBus; + private config; + private currentView; + private buttonListeners; + constructor(eventBus: IEventBus, config: Configuration); + private setupEventListeners; + private setupEventBusListeners; + private setupButtonHandlers; + private setupButtonGroup; + private getViewButtons; + private getWorkweekButtons; + private initializeView; + private changeView; + private changeWorkweek; + private updateAllButtons; + private updateButtonGroup; + private emitViewRendered; + private refreshCurrentView; + private isValidView; +} diff --git a/wwwroot/js/managers/ViewManager.js b/wwwroot/js/managers/ViewManager.js new file mode 100644 index 0000000..7b25515 --- /dev/null +++ b/wwwroot/js/managers/ViewManager.js @@ -0,0 +1,106 @@ +import { ConfigManager } from '../configurations/ConfigManager'; +import { CoreEvents } from '../constants/CoreEvents'; +export class ViewManager { + constructor(eventBus, config) { + this.currentView = 'week'; + this.buttonListeners = new Map(); + this.eventBus = eventBus; + this.config = config; + this.setupEventListeners(); + } + setupEventListeners() { + this.setupEventBusListeners(); + this.setupButtonHandlers(); + } + setupEventBusListeners() { + this.eventBus.on(CoreEvents.INITIALIZED, () => { + this.initializeView(); + }); + this.eventBus.on(CoreEvents.DATE_CHANGED, () => { + this.refreshCurrentView(); + }); + } + setupButtonHandlers() { + this.setupButtonGroup('swp-view-button[data-view]', 'data-view', (value) => { + if (this.isValidView(value)) { + this.changeView(value); + } + }); + this.setupButtonGroup('swp-preset-button[data-workweek]', 'data-workweek', (value) => { + this.changeWorkweek(value); + }); + } + setupButtonGroup(selector, attribute, handler) { + const buttons = document.querySelectorAll(selector); + buttons.forEach(button => { + const clickHandler = (event) => { + event.preventDefault(); + const value = button.getAttribute(attribute); + if (value) { + handler(value); + } + }; + button.addEventListener('click', clickHandler); + this.buttonListeners.set(button, clickHandler); + }); + } + getViewButtons() { + return document.querySelectorAll('swp-view-button[data-view]'); + } + getWorkweekButtons() { + return document.querySelectorAll('swp-preset-button[data-workweek]'); + } + initializeView() { + this.updateAllButtons(); + this.emitViewRendered(); + } + changeView(newView) { + if (newView === this.currentView) + return; + const previousView = this.currentView; + this.currentView = newView; + this.updateAllButtons(); + this.eventBus.emit(CoreEvents.VIEW_CHANGED, { + previousView, + currentView: newView + }); + } + changeWorkweek(workweekId) { + this.config.setWorkWeek(workweekId); + // Update all CSS properties to match new configuration + ConfigManager.updateCSSProperties(this.config); + this.updateAllButtons(); + const settings = this.config.getWorkWeekSettings(); + this.eventBus.emit(CoreEvents.WORKWEEK_CHANGED, { + workWeekId: workweekId, + settings: settings + }); + } + updateAllButtons() { + this.updateButtonGroup(this.getViewButtons(), 'data-view', this.currentView); + this.updateButtonGroup(this.getWorkweekButtons(), 'data-workweek', this.config.currentWorkWeek); + } + updateButtonGroup(buttons, attribute, activeValue) { + buttons.forEach(button => { + const buttonValue = button.getAttribute(attribute); + if (buttonValue === activeValue) { + button.setAttribute('data-active', 'true'); + } + else { + button.removeAttribute('data-active'); + } + }); + } + emitViewRendered() { + this.eventBus.emit(CoreEvents.VIEW_RENDERED, { + view: this.currentView + }); + } + refreshCurrentView() { + this.emitViewRendered(); + } + isValidView(view) { + return ['day', 'week', 'month'].includes(view); + } +} +//# sourceMappingURL=ViewManager.js.map \ No newline at end of file diff --git a/wwwroot/js/managers/ViewManager.js.map b/wwwroot/js/managers/ViewManager.js.map new file mode 100644 index 0000000..9608872 --- /dev/null +++ b/wwwroot/js/managers/ViewManager.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ViewManager.js","sourceRoot":"","sources":["../../../src/managers/ViewManager.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/managers/ViewSelectorManager.d.ts b/wwwroot/js/managers/ViewSelectorManager.d.ts new file mode 100644 index 0000000..18a9db6 --- /dev/null +++ b/wwwroot/js/managers/ViewSelectorManager.d.ts @@ -0,0 +1,70 @@ +import { IEventBus } from '../types/CalendarTypes'; +import { Configuration } from '../configurations/CalendarConfig'; +/** + * ViewSelectorManager - Manages view selector UI and state + * + * RESPONSIBILITY: + * =============== + * This manager owns all logic related to the UI element. + * It follows the principle that each functional UI element has its own manager. + * + * RESPONSIBILITIES: + * - Handles button clicks on swp-view-button elements + * - Manages current view state (day/week/month) + * - Validates view values + * - Emits VIEW_CHANGED and VIEW_RENDERED events + * - Updates button UI states (data-active attributes) + * + * EVENT FLOW: + * =========== + * User clicks button → changeView() → validate → update state → emit event → update UI + * + * IMPLEMENTATION STATUS: + * ====================== + * - Week view: FULLY IMPLEMENTED + * - Day view: NOT IMPLEMENTED (button exists but no rendering) + * - Month view: NOT IMPLEMENTED (button exists but no rendering) + * + * SUBSCRIBERS: + * ============ + * - GridRenderer: Uses view parameter (currently only supports 'week') + * - Future: DayRenderer, MonthRenderer when implemented + */ +export declare class ViewSelectorManager { + private eventBus; + private config; + private buttonListeners; + constructor(eventBus: IEventBus, config: Configuration); + /** + * Setup click listeners on all view selector buttons + */ + private setupButtonListeners; + /** + * Setup event bus listeners + */ + private setupEventListeners; + /** + * Change the active view + */ + private changeView; + /** + * Update button states (data-active attributes) + */ + private updateButtonStates; + /** + * Initialize view on INITIALIZED event + */ + private initializeView; + /** + * Emit VIEW_RENDERED event + */ + private emitViewRendered; + /** + * Refresh current view on DATE_CHANGED event + */ + private refreshCurrentView; + /** + * Validate if string is a valid CalendarView type + */ + private isValidView; +} diff --git a/wwwroot/js/managers/ViewSelectorManager.js b/wwwroot/js/managers/ViewSelectorManager.js new file mode 100644 index 0000000..162191c --- /dev/null +++ b/wwwroot/js/managers/ViewSelectorManager.js @@ -0,0 +1,130 @@ +import { CoreEvents } from '../constants/CoreEvents'; +/** + * ViewSelectorManager - Manages view selector UI and state + * + * RESPONSIBILITY: + * =============== + * This manager owns all logic related to the UI element. + * It follows the principle that each functional UI element has its own manager. + * + * RESPONSIBILITIES: + * - Handles button clicks on swp-view-button elements + * - Manages current view state (day/week/month) + * - Validates view values + * - Emits VIEW_CHANGED and VIEW_RENDERED events + * - Updates button UI states (data-active attributes) + * + * EVENT FLOW: + * =========== + * User clicks button → changeView() → validate → update state → emit event → update UI + * + * IMPLEMENTATION STATUS: + * ====================== + * - Week view: FULLY IMPLEMENTED + * - Day view: NOT IMPLEMENTED (button exists but no rendering) + * - Month view: NOT IMPLEMENTED (button exists but no rendering) + * + * SUBSCRIBERS: + * ============ + * - GridRenderer: Uses view parameter (currently only supports 'week') + * - Future: DayRenderer, MonthRenderer when implemented + */ +export class ViewSelectorManager { + constructor(eventBus, config) { + this.buttonListeners = new Map(); + this.eventBus = eventBus; + this.config = config; + this.setupButtonListeners(); + this.setupEventListeners(); + } + /** + * Setup click listeners on all view selector buttons + */ + setupButtonListeners() { + const buttons = document.querySelectorAll('swp-view-button[data-view]'); + buttons.forEach(button => { + const clickHandler = (event) => { + event.preventDefault(); + const view = button.getAttribute('data-view'); + if (view && this.isValidView(view)) { + this.changeView(view); + } + }; + button.addEventListener('click', clickHandler); + this.buttonListeners.set(button, clickHandler); + }); + // Initialize button states + this.updateButtonStates(); + } + /** + * Setup event bus listeners + */ + setupEventListeners() { + this.eventBus.on(CoreEvents.INITIALIZED, () => { + this.initializeView(); + }); + this.eventBus.on(CoreEvents.DATE_CHANGED, () => { + this.refreshCurrentView(); + }); + } + /** + * Change the active view + */ + changeView(newView) { + if (newView === this.config.currentView) { + return; // No change + } + const previousView = this.config.currentView; + this.config.currentView = newView; + // Update button UI states + this.updateButtonStates(); + // Emit event for subscribers + this.eventBus.emit(CoreEvents.VIEW_CHANGED, { + previousView, + currentView: newView + }); + } + /** + * Update button states (data-active attributes) + */ + updateButtonStates() { + const buttons = document.querySelectorAll('swp-view-button[data-view]'); + buttons.forEach(button => { + const buttonView = button.getAttribute('data-view'); + if (buttonView === this.config.currentView) { + button.setAttribute('data-active', 'true'); + } + else { + button.removeAttribute('data-active'); + } + }); + } + /** + * Initialize view on INITIALIZED event + */ + initializeView() { + this.updateButtonStates(); + this.emitViewRendered(); + } + /** + * Emit VIEW_RENDERED event + */ + emitViewRendered() { + this.eventBus.emit(CoreEvents.VIEW_RENDERED, { + view: this.config.currentView + }); + } + /** + * Refresh current view on DATE_CHANGED event + */ + refreshCurrentView() { + this.emitViewRendered(); + } + /** + * Validate if string is a valid CalendarView type + */ + isValidView(view) { + return ['day', 'week', 'month'].includes(view); + } +} +//# sourceMappingURL=ViewSelectorManager.js.map \ No newline at end of file diff --git a/wwwroot/js/managers/ViewSelectorManager.js.map b/wwwroot/js/managers/ViewSelectorManager.js.map new file mode 100644 index 0000000..aa10a71 --- /dev/null +++ b/wwwroot/js/managers/ViewSelectorManager.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ViewSelectorManager.js","sourceRoot":"","sources":["../../../src/managers/ViewSelectorManager.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/managers/WorkHoursManager.d.ts b/wwwroot/js/managers/WorkHoursManager.d.ts new file mode 100644 index 0000000..8cd7f19 --- /dev/null +++ b/wwwroot/js/managers/WorkHoursManager.d.ts @@ -0,0 +1,71 @@ +import { DateService } from '../utils/DateService'; +import { Configuration } from '../configurations/CalendarConfig'; +import { PositionUtils } from '../utils/PositionUtils'; +/** + * Work hours for a specific day + */ +export interface IDayWorkHours { + start: number; + end: number; +} +/** + * Work schedule configuration + */ +export interface IWorkScheduleConfig { + weeklyDefault: { + monday: IDayWorkHours | 'off'; + tuesday: IDayWorkHours | 'off'; + wednesday: IDayWorkHours | 'off'; + thursday: IDayWorkHours | 'off'; + friday: IDayWorkHours | 'off'; + saturday: IDayWorkHours | 'off'; + sunday: IDayWorkHours | 'off'; + }; + dateOverrides: { + [dateString: string]: IDayWorkHours | 'off'; + }; +} +/** + * Manages work hours scheduling with weekly defaults and date-specific overrides + */ +export declare class WorkHoursManager { + private dateService; + private config; + private positionUtils; + private workSchedule; + constructor(dateService: DateService, config: Configuration, positionUtils: PositionUtils); + /** + * Get work hours for a specific date + */ + getWorkHoursForDate(date: Date): IDayWorkHours | 'off'; + /** + * Get work hours for multiple dates (used by GridManager) + */ + getWorkHoursForDateRange(dates: Date[]): Map; + /** + * Calculate CSS custom properties for non-work hour overlays using PositionUtils + */ + calculateNonWorkHoursStyle(workHours: IDayWorkHours | 'off'): { + beforeWorkHeight: number; + afterWorkTop: number; + } | null; + /** + * Calculate CSS custom properties for work hours overlay using PositionUtils + */ + calculateWorkHoursStyle(workHours: IDayWorkHours | 'off'): { + top: number; + height: number; + } | null; + /** + * Load work schedule from JSON (future implementation) + */ + loadWorkSchedule(jsonData: IWorkScheduleConfig): Promise; + /** + * Get current work schedule configuration + */ + getWorkSchedule(): IWorkScheduleConfig; + /** + * Convert Date to day name key + */ + private getDayName; +} diff --git a/wwwroot/js/managers/WorkHoursManager.js b/wwwroot/js/managers/WorkHoursManager.js new file mode 100644 index 0000000..b948c0f --- /dev/null +++ b/wwwroot/js/managers/WorkHoursManager.js @@ -0,0 +1,108 @@ +// Work hours management for per-column scheduling +/** + * Manages work hours scheduling with weekly defaults and date-specific overrides + */ +export class WorkHoursManager { + constructor(dateService, config, positionUtils) { + this.dateService = dateService; + this.config = config; + this.positionUtils = positionUtils; + // Default work schedule - will be loaded from JSON later + this.workSchedule = { + weeklyDefault: { + monday: { start: 9, end: 17 }, + tuesday: { start: 9, end: 17 }, + wednesday: { start: 9, end: 17 }, + thursday: { start: 9, end: 17 }, + friday: { start: 9, end: 15 }, + saturday: 'off', + sunday: 'off' + }, + dateOverrides: { + '2025-01-20': { start: 10, end: 16 }, + '2025-01-21': { start: 8, end: 14 }, + '2025-01-22': 'off' + } + }; + } + /** + * Get work hours for a specific date + */ + getWorkHoursForDate(date) { + const dateString = this.dateService.formatISODate(date); + // Check for date-specific override first + if (this.workSchedule.dateOverrides[dateString]) { + return this.workSchedule.dateOverrides[dateString]; + } + // Fall back to weekly default + const dayName = this.getDayName(date); + return this.workSchedule.weeklyDefault[dayName]; + } + /** + * Get work hours for multiple dates (used by GridManager) + */ + getWorkHoursForDateRange(dates) { + const workHoursMap = new Map(); + dates.forEach(date => { + const dateString = this.dateService.formatISODate(date); + const workHours = this.getWorkHoursForDate(date); + workHoursMap.set(dateString, workHours); + }); + return workHoursMap; + } + /** + * Calculate CSS custom properties for non-work hour overlays using PositionUtils + */ + calculateNonWorkHoursStyle(workHours) { + if (workHours === 'off') { + return null; // Full day will be colored via CSS background + } + const gridSettings = this.config.gridSettings; + const dayStartHour = gridSettings.dayStartHour; + const hourHeight = gridSettings.hourHeight; + // Before work: from day start to work start + const beforeWorkHeight = (workHours.start - dayStartHour) * hourHeight; + // After work: from work end to day end + const afterWorkTop = (workHours.end - dayStartHour) * hourHeight; + return { + beforeWorkHeight: Math.max(0, beforeWorkHeight), + afterWorkTop: Math.max(0, afterWorkTop) + }; + } + /** + * Calculate CSS custom properties for work hours overlay using PositionUtils + */ + calculateWorkHoursStyle(workHours) { + if (workHours === 'off') { + return null; + } + // Create dummy time strings for start and end of work hours + const startTime = `${workHours.start.toString().padStart(2, '0')}:00`; + const endTime = `${workHours.end.toString().padStart(2, '0')}:00`; + // Use PositionUtils for consistent position calculation + const position = this.positionUtils.calculateEventPosition(startTime, endTime); + return { top: position.top, height: position.height }; + } + /** + * Load work schedule from JSON (future implementation) + */ + async loadWorkSchedule(jsonData) { + this.workSchedule = jsonData; + } + /** + * Get current work schedule configuration + */ + getWorkSchedule() { + return this.workSchedule; + } + /** + * Convert Date to day name key + */ + getDayName(date) { + const dayNames = [ + 'sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday' + ]; + return dayNames[date.getDay()]; + } +} +//# sourceMappingURL=WorkHoursManager.js.map \ No newline at end of file diff --git a/wwwroot/js/managers/WorkHoursManager.js.map b/wwwroot/js/managers/WorkHoursManager.js.map new file mode 100644 index 0000000..136e28b --- /dev/null +++ b/wwwroot/js/managers/WorkHoursManager.js.map @@ -0,0 +1 @@ +{"version":3,"file":"WorkHoursManager.js","sourceRoot":"","sources":["../../../src/managers/WorkHoursManager.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/managers/WorkweekPresetsManager.d.ts b/wwwroot/js/managers/WorkweekPresetsManager.d.ts new file mode 100644 index 0000000..0251865 --- /dev/null +++ b/wwwroot/js/managers/WorkweekPresetsManager.d.ts @@ -0,0 +1,47 @@ +import { IEventBus } from '../types/CalendarTypes'; +import { Configuration } from '../configurations/CalendarConfig'; +/** + * WorkweekPresetsManager - Manages workweek preset UI and state + * + * RESPONSIBILITY: + * =============== + * This manager owns all logic related to the UI element. + * It follows the principle that each functional UI element has its own manager. + * + * RESPONSIBILITIES: + * - Owns WORK_WEEK_PRESETS data + * - Handles button clicks on swp-preset-button elements + * - Manages current workweek preset state + * - Validates preset IDs + * - Emits WORKWEEK_CHANGED events + * - Updates button UI states (data-active attributes) + * + * EVENT FLOW: + * =========== + * User clicks button → changePreset() → validate → update state → emit event → update UI + * + * SUBSCRIBERS: + * ============ + * - ConfigManager: Updates CSS variables (--grid-columns) + * - GridManager: Re-renders grid with new column count + * - CalendarManager: Relays to header update (via workweek:header-update) + * - HeaderManager: Updates date headers + */ +export declare class WorkweekPresetsManager { + private eventBus; + private config; + private buttonListeners; + constructor(eventBus: IEventBus, config: Configuration); + /** + * Setup click listeners on all workweek preset buttons + */ + private setupButtonListeners; + /** + * Change the active workweek preset + */ + private changePreset; + /** + * Update button states (data-active attributes) + */ + private updateButtonStates; +} diff --git a/wwwroot/js/managers/WorkweekPresetsManager.js b/wwwroot/js/managers/WorkweekPresetsManager.js new file mode 100644 index 0000000..6ebdbc7 --- /dev/null +++ b/wwwroot/js/managers/WorkweekPresetsManager.js @@ -0,0 +1,95 @@ +import { CoreEvents } from '../constants/CoreEvents'; +import { WORK_WEEK_PRESETS } from '../configurations/CalendarConfig'; +/** + * WorkweekPresetsManager - Manages workweek preset UI and state + * + * RESPONSIBILITY: + * =============== + * This manager owns all logic related to the UI element. + * It follows the principle that each functional UI element has its own manager. + * + * RESPONSIBILITIES: + * - Owns WORK_WEEK_PRESETS data + * - Handles button clicks on swp-preset-button elements + * - Manages current workweek preset state + * - Validates preset IDs + * - Emits WORKWEEK_CHANGED events + * - Updates button UI states (data-active attributes) + * + * EVENT FLOW: + * =========== + * User clicks button → changePreset() → validate → update state → emit event → update UI + * + * SUBSCRIBERS: + * ============ + * - ConfigManager: Updates CSS variables (--grid-columns) + * - GridManager: Re-renders grid with new column count + * - CalendarManager: Relays to header update (via workweek:header-update) + * - HeaderManager: Updates date headers + */ +export class WorkweekPresetsManager { + constructor(eventBus, config) { + this.buttonListeners = new Map(); + this.eventBus = eventBus; + this.config = config; + this.setupButtonListeners(); + } + /** + * Setup click listeners on all workweek preset buttons + */ + setupButtonListeners() { + const buttons = document.querySelectorAll('swp-preset-button[data-workweek]'); + buttons.forEach(button => { + const clickHandler = (event) => { + event.preventDefault(); + const presetId = button.getAttribute('data-workweek'); + if (presetId) { + this.changePreset(presetId); + } + }; + button.addEventListener('click', clickHandler); + this.buttonListeners.set(button, clickHandler); + }); + // Initialize button states + this.updateButtonStates(); + } + /** + * Change the active workweek preset + */ + changePreset(presetId) { + if (!WORK_WEEK_PRESETS[presetId]) { + console.warn(`Invalid preset ID "${presetId}"`); + return; + } + if (presetId === this.config.currentWorkWeek) { + return; // No change + } + const previousPresetId = this.config.currentWorkWeek; + this.config.currentWorkWeek = presetId; + const settings = WORK_WEEK_PRESETS[presetId]; + // Update button UI states + this.updateButtonStates(); + // Emit event for subscribers + this.eventBus.emit(CoreEvents.WORKWEEK_CHANGED, { + workWeekId: presetId, + previousWorkWeekId: previousPresetId, + settings: settings + }); + } + /** + * Update button states (data-active attributes) + */ + updateButtonStates() { + const buttons = document.querySelectorAll('swp-preset-button[data-workweek]'); + buttons.forEach(button => { + const buttonPresetId = button.getAttribute('data-workweek'); + if (buttonPresetId === this.config.currentWorkWeek) { + button.setAttribute('data-active', 'true'); + } + else { + button.removeAttribute('data-active'); + } + }); + } +} +//# sourceMappingURL=WorkweekPresetsManager.js.map \ No newline at end of file diff --git a/wwwroot/js/managers/WorkweekPresetsManager.js.map b/wwwroot/js/managers/WorkweekPresetsManager.js.map new file mode 100644 index 0000000..4e7bc85 --- /dev/null +++ b/wwwroot/js/managers/WorkweekPresetsManager.js.map @@ -0,0 +1 @@ +{"version":3,"file":"WorkweekPresetsManager.js","sourceRoot":"","sources":["../../../src/managers/WorkweekPresetsManager.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAErD,OAAO,EAAE,iBAAiB,EAAiB,MAAM,kCAAkC,CAAC;AAEpF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,OAAO,sBAAsB;IAKjC,YAAY,QAAmB,EAAE,MAAqB;QAF9C,oBAAe,GAAgC,IAAI,GAAG,EAAE,CAAC;QAG/D,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QAErB,IAAI,CAAC,oBAAoB,EAAE,CAAC;IAC9B,CAAC;IAED;;OAEG;IACK,oBAAoB;QAC1B,MAAM,OAAO,GAAG,QAAQ,CAAC,gBAAgB,CAAC,kCAAkC,CAAC,CAAC;QAE9E,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;YACvB,MAAM,YAAY,GAAG,CAAC,KAAY,EAAE,EAAE;gBACpC,KAAK,CAAC,cAAc,EAAE,CAAC;gBACvB,MAAM,QAAQ,GAAG,MAAM,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC;gBACtD,IAAI,QAAQ,EAAE,CAAC;oBACb,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;gBAC9B,CAAC;YACH,CAAC,CAAC;YAEF,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;YAC/C,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QACjD,CAAC,CAAC,CAAC;QAEH,2BAA2B;QAC3B,IAAI,CAAC,kBAAkB,EAAE,CAAC;IAC5B,CAAC;IAED;;OAEG;IACK,YAAY,CAAC,QAAgB;QACnC,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,EAAE,CAAC;YACjC,OAAO,CAAC,IAAI,CAAC,sBAAsB,QAAQ,GAAG,CAAC,CAAC;YAChD,OAAO;QACT,CAAC;QAED,IAAI,QAAQ,KAAK,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC;YAC7C,OAAO,CAAC,YAAY;QACtB,CAAC;QAED,MAAM,gBAAgB,GAAG,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC;QACrD,IAAI,CAAC,MAAM,CAAC,eAAe,GAAG,QAAQ,CAAC;QAEvC,MAAM,QAAQ,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QAE7C,0BAA0B;QAC1B,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAE1B,6BAA6B;QAC7B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,gBAAgB,EAAE;YAC9C,UAAU,EAAE,QAAQ;YACpB,kBAAkB,EAAE,gBAAgB;YACpC,QAAQ,EAAE,QAAQ;SACnB,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACK,kBAAkB;QACxB,MAAM,OAAO,GAAG,QAAQ,CAAC,gBAAgB,CAAC,kCAAkC,CAAC,CAAC;QAE9E,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;YACvB,MAAM,cAAc,GAAG,MAAM,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC;YAE5D,IAAI,cAAc,KAAK,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC;gBACnD,MAAM,CAAC,YAAY,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;YAC7C,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC;YACxC,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;CAEF"} \ No newline at end of file diff --git a/wwwroot/js/renderers/AllDayEventRenderer.d.ts b/wwwroot/js/renderers/AllDayEventRenderer.d.ts new file mode 100644 index 0000000..588760b --- /dev/null +++ b/wwwroot/js/renderers/AllDayEventRenderer.d.ts @@ -0,0 +1,32 @@ +import { IEventLayout } from '../utils/AllDayLayoutEngine'; +import { IDragStartEventPayload } from '../types/EventTypes'; +export declare class AllDayEventRenderer { + private container; + private originalEvent; + private draggedClone; + constructor(); + private getContainer; + private getAllDayContainer; + /** + * Handle drag start for all-day events + */ + handleDragStart(payload: IDragStartEventPayload): void; + /** + * Render an all-day event with pre-calculated layout + */ + private renderAllDayEventWithLayout; + /** + * Remove an all-day event by ID + */ + removeAllDayEvent(eventId: string): void; + /** + * Clear cache when DOM changes + */ + clearCache(): void; + /** + * Render all-day events for specific period using AllDayEventRenderer + */ + renderAllDayEventsForPeriod(eventLayouts: IEventLayout[]): void; + private clearAllDayEvents; + handleViewChanged(event: CustomEvent): void; +} diff --git a/wwwroot/js/renderers/AllDayEventRenderer.js b/wwwroot/js/renderers/AllDayEventRenderer.js new file mode 100644 index 0000000..bafe6af --- /dev/null +++ b/wwwroot/js/renderers/AllDayEventRenderer.js @@ -0,0 +1,97 @@ +import { SwpAllDayEventElement } from '../elements/SwpEventElement'; +export class AllDayEventRenderer { + constructor() { + this.container = null; + this.originalEvent = null; + this.draggedClone = null; + this.getContainer(); + } + getContainer() { + const header = document.querySelector('swp-calendar-header'); + if (header) { + this.container = header.querySelector('swp-allday-container'); + if (!this.container) { + this.container = document.createElement('swp-allday-container'); + header.appendChild(this.container); + } + } + return this.container; + } + getAllDayContainer() { + return document.querySelector('swp-calendar-header swp-allday-container'); + } + /** + * Handle drag start for all-day events + */ + handleDragStart(payload) { + this.originalEvent = payload.originalElement; + ; + this.draggedClone = payload.draggedClone; + if (this.draggedClone) { + const container = this.getAllDayContainer(); + if (!container) + return; + this.draggedClone.style.gridColumn = this.originalEvent.style.gridColumn; + this.draggedClone.style.gridRow = this.originalEvent.style.gridRow; + console.log('handleDragStart:this.draggedClone', this.draggedClone); + container.appendChild(this.draggedClone); + // Add dragging style + this.draggedClone.classList.add('dragging'); + this.draggedClone.style.zIndex = '1000'; + this.draggedClone.style.cursor = 'grabbing'; + // Make original semi-transparent + this.originalEvent.style.opacity = '0.3'; + this.originalEvent.style.userSelect = 'none'; + } + } + /** + * Render an all-day event with pre-calculated layout + */ + renderAllDayEventWithLayout(event, layout) { + const container = this.getContainer(); + if (!container) + return null; + const dayEvent = SwpAllDayEventElement.fromCalendarEvent(event); + dayEvent.applyGridPositioning(layout.row, layout.startColumn, layout.endColumn); + // Apply highlight class to show events with highlight color + dayEvent.classList.add('highlight'); + container.appendChild(dayEvent); + } + /** + * Remove an all-day event by ID + */ + removeAllDayEvent(eventId) { + const container = this.getContainer(); + if (!container) + return; + const eventElement = container.querySelector(`swp-allday-event[data-event-id="${eventId}"]`); + if (eventElement) { + eventElement.remove(); + } + } + /** + * Clear cache when DOM changes + */ + clearCache() { + this.container = null; + } + /** + * Render all-day events for specific period using AllDayEventRenderer + */ + renderAllDayEventsForPeriod(eventLayouts) { + this.clearAllDayEvents(); + eventLayouts.forEach(layout => { + this.renderAllDayEventWithLayout(layout.calenderEvent, layout); + }); + } + clearAllDayEvents() { + const allDayContainer = document.querySelector('swp-allday-container'); + if (allDayContainer) { + allDayContainer.querySelectorAll('swp-allday-event:not(.max-event-indicator)').forEach(event => event.remove()); + } + } + handleViewChanged(event) { + this.clearAllDayEvents(); + } +} +//# sourceMappingURL=AllDayEventRenderer.js.map \ No newline at end of file diff --git a/wwwroot/js/renderers/AllDayEventRenderer.js.map b/wwwroot/js/renderers/AllDayEventRenderer.js.map new file mode 100644 index 0000000..1a34457 --- /dev/null +++ b/wwwroot/js/renderers/AllDayEventRenderer.js.map @@ -0,0 +1 @@ +{"version":3,"file":"AllDayEventRenderer.js","sourceRoot":"","sources":["../../../src/renderers/AllDayEventRenderer.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/renderers/ColumnRenderer.d.ts b/wwwroot/js/renderers/ColumnRenderer.d.ts new file mode 100644 index 0000000..7bb8239 --- /dev/null +++ b/wwwroot/js/renderers/ColumnRenderer.d.ts @@ -0,0 +1,26 @@ +import { Configuration } from '../configurations/CalendarConfig'; +import { DateService } from '../utils/DateService'; +import { WorkHoursManager } from '../managers/WorkHoursManager'; +/** + * Interface for column rendering strategies + */ +export interface IColumnRenderer { + render(columnContainer: HTMLElement, context: IColumnRenderContext): void; +} +/** + * Context for column rendering + */ +export interface IColumnRenderContext { + currentWeek: Date; + config: Configuration; +} +/** + * Date-based column renderer (original functionality) + */ +export declare class DateColumnRenderer implements IColumnRenderer { + private dateService; + private workHoursManager; + constructor(dateService: DateService, workHoursManager: WorkHoursManager); + render(columnContainer: HTMLElement, context: IColumnRenderContext): void; + private applyWorkHoursToColumn; +} diff --git a/wwwroot/js/renderers/ColumnRenderer.js b/wwwroot/js/renderers/ColumnRenderer.js new file mode 100644 index 0000000..ca17b92 --- /dev/null +++ b/wwwroot/js/renderers/ColumnRenderer.js @@ -0,0 +1,44 @@ +// Column rendering strategy interface and implementations +/** + * Date-based column renderer (original functionality) + */ +export class DateColumnRenderer { + constructor(dateService, workHoursManager) { + this.dateService = dateService; + this.workHoursManager = workHoursManager; + } + render(columnContainer, context) { + const { currentWeek, config } = context; + const workWeekSettings = config.getWorkWeekSettings(); + const dates = this.dateService.getWorkWeekDates(currentWeek, workWeekSettings.workDays); + const dateSettings = config.dateViewSettings; + const daysToShow = dates.slice(0, dateSettings.weekDays); + daysToShow.forEach((date) => { + const column = document.createElement('swp-day-column'); + column.dataset.date = this.dateService.formatISODate(date); + // Apply work hours styling + this.applyWorkHoursToColumn(column, date); + const eventsLayer = document.createElement('swp-events-layer'); + column.appendChild(eventsLayer); + columnContainer.appendChild(column); + }); + } + applyWorkHoursToColumn(column, date) { + const workHours = this.workHoursManager.getWorkHoursForDate(date); + if (workHours === 'off') { + // No work hours - mark as off day (full day will be colored) + column.dataset.workHours = 'off'; + } + else { + // Calculate and apply non-work hours overlays (before and after work) + const nonWorkStyle = this.workHoursManager.calculateNonWorkHoursStyle(workHours); + if (nonWorkStyle) { + // Before work overlay (::before pseudo-element) + column.style.setProperty('--before-work-height', `${nonWorkStyle.beforeWorkHeight}px`); + // After work overlay (::after pseudo-element) + column.style.setProperty('--after-work-top', `${nonWorkStyle.afterWorkTop}px`); + } + } + } +} +//# sourceMappingURL=ColumnRenderer.js.map \ No newline at end of file diff --git a/wwwroot/js/renderers/ColumnRenderer.js.map b/wwwroot/js/renderers/ColumnRenderer.js.map new file mode 100644 index 0000000..634f6f6 --- /dev/null +++ b/wwwroot/js/renderers/ColumnRenderer.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ColumnRenderer.js","sourceRoot":"","sources":["../../../src/renderers/ColumnRenderer.ts"],"names":[],"mappings":"AAAA,0DAA0D;AAqB1D;;GAEG;AACH,MAAM,OAAO,kBAAkB;IAI7B,YACE,WAAwB,EACxB,gBAAkC;QAElC,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,gBAAgB,GAAG,gBAAgB,CAAC;IAC3C,CAAC;IAED,MAAM,CAAC,eAA4B,EAAE,OAA6B;QAChE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;QAExC,MAAM,gBAAgB,GAAG,MAAM,CAAC,mBAAmB,EAAE,CAAC;QACtD,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,gBAAgB,CAAC,WAAW,EAAE,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QACxF,MAAM,YAAY,GAAG,MAAM,CAAC,gBAAgB,CAAC;QAC7C,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC;QAGzD,UAAU,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;YAC1B,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;YACvD,MAAc,CAAC,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;YAEpE,2BAA2B;YAC3B,IAAI,CAAC,sBAAsB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YAE1C,MAAM,WAAW,GAAG,QAAQ,CAAC,aAAa,CAAC,kBAAkB,CAAC,CAAC;YAC/D,MAAM,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;YAEhC,eAAe,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,sBAAsB,CAAC,MAAmB,EAAE,IAAU;QAC5D,MAAM,SAAS,GAAG,IAAI,CAAC,gBAAgB,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;QAElE,IAAI,SAAS,KAAK,KAAK,EAAE,CAAC;YACxB,6DAA6D;YAC5D,MAAc,CAAC,OAAO,CAAC,SAAS,GAAG,KAAK,CAAC;QAC5C,CAAC;aAAM,CAAC;YACN,sEAAsE;YACtE,MAAM,YAAY,GAAG,IAAI,CAAC,gBAAgB,CAAC,0BAA0B,CAAC,SAAS,CAAC,CAAC;YACjF,IAAI,YAAY,EAAE,CAAC;gBACjB,gDAAgD;gBAChD,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,sBAAsB,EAAE,GAAG,YAAY,CAAC,gBAAgB,IAAI,CAAC,CAAC;gBAEvF,8CAA8C;gBAC9C,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,kBAAkB,EAAE,GAAG,YAAY,CAAC,YAAY,IAAI,CAAC,CAAC;YAEjF,CAAC;QACH,CAAC;IACH,CAAC;CAEF"} \ No newline at end of file diff --git a/wwwroot/js/renderers/DateHeaderRenderer.d.ts b/wwwroot/js/renderers/DateHeaderRenderer.d.ts new file mode 100644 index 0000000..4df75e2 --- /dev/null +++ b/wwwroot/js/renderers/DateHeaderRenderer.d.ts @@ -0,0 +1,21 @@ +import { Configuration } from '../configurations/CalendarConfig'; +/** + * Interface for header rendering strategies + */ +export interface IHeaderRenderer { + render(calendarHeader: HTMLElement, context: IHeaderRenderContext): void; +} +/** + * Context for header rendering + */ +export interface IHeaderRenderContext { + currentWeek: Date; + config: Configuration; +} +/** + * Date-based header renderer (original functionality) + */ +export declare class DateHeaderRenderer implements IHeaderRenderer { + private dateService; + render(calendarHeader: HTMLElement, context: IHeaderRenderContext): void; +} diff --git a/wwwroot/js/renderers/DateHeaderRenderer.js b/wwwroot/js/renderers/DateHeaderRenderer.js new file mode 100644 index 0000000..1787f1c --- /dev/null +++ b/wwwroot/js/renderers/DateHeaderRenderer.js @@ -0,0 +1,35 @@ +// Header rendering strategy interface and implementations +import { DateService } from '../utils/DateService'; +/** + * Date-based header renderer (original functionality) + */ +export class DateHeaderRenderer { + render(calendarHeader, context) { + const { currentWeek, config } = context; + // FIRST: Always create all-day container as part of standard header structure + const allDayContainer = document.createElement('swp-allday-container'); + calendarHeader.appendChild(allDayContainer); + // Initialize date service with timezone and locale from config + const timezone = config.timeFormatConfig.timezone; + const locale = config.timeFormatConfig.locale; + this.dateService = new DateService(config); + const workWeekSettings = config.getWorkWeekSettings(); + const dates = this.dateService.getWorkWeekDates(currentWeek, workWeekSettings.workDays); + const weekDays = config.dateViewSettings.weekDays; + const daysToShow = dates.slice(0, weekDays); + daysToShow.forEach((date, index) => { + const header = document.createElement('swp-day-header'); + if (this.dateService.isSameDay(date, new Date())) { + header.dataset.today = 'true'; + } + const dayName = this.dateService.getDayName(date, 'long', locale).toUpperCase(); + header.innerHTML = ` + ${dayName} + ${date.getDate()} + `; + header.dataset.date = this.dateService.formatISODate(date); + calendarHeader.appendChild(header); + }); + } +} +//# sourceMappingURL=DateHeaderRenderer.js.map \ No newline at end of file diff --git a/wwwroot/js/renderers/DateHeaderRenderer.js.map b/wwwroot/js/renderers/DateHeaderRenderer.js.map new file mode 100644 index 0000000..e3e3bc2 --- /dev/null +++ b/wwwroot/js/renderers/DateHeaderRenderer.js.map @@ -0,0 +1 @@ +{"version":3,"file":"DateHeaderRenderer.js","sourceRoot":"","sources":["../../../src/renderers/DateHeaderRenderer.ts"],"names":[],"mappings":"AAAA,0DAA0D;AAG1D,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAkBnD;;GAEG;AACH,MAAM,OAAO,kBAAkB;IAG7B,MAAM,CAAC,cAA2B,EAAE,OAA6B;QAC/D,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;QAExC,8EAA8E;QAC9E,MAAM,eAAe,GAAG,QAAQ,CAAC,aAAa,CAAC,sBAAsB,CAAC,CAAC;QACvE,cAAc,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC;QAE5C,+DAA+D;QAC/D,MAAM,QAAQ,GAAG,MAAM,CAAC,gBAAgB,CAAC,QAAQ,CAAC;QAClD,MAAM,MAAM,GAAG,MAAM,CAAC,gBAAgB,CAAC,MAAM,CAAC;QAC9C,IAAI,CAAC,WAAW,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC;QAE3C,MAAM,gBAAgB,GAAG,MAAM,CAAC,mBAAmB,EAAE,CAAC;QACtD,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,gBAAgB,CAAC,WAAW,EAAE,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QACxF,MAAM,QAAQ,GAAG,MAAM,CAAC,gBAAgB,CAAC,QAAQ,CAAC;QAClD,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;QAE5C,UAAU,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;YACjC,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;YACxD,IAAI,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,IAAI,EAAE,CAAC,EAAE,CAAC;gBAChD,MAAc,CAAC,OAAO,CAAC,KAAK,GAAG,MAAM,CAAC;YACzC,CAAC;YAED,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;YAEhF,MAAM,CAAC,SAAS,GAAG;wBACD,OAAO;wBACP,IAAI,CAAC,OAAO,EAAE;OAC/B,CAAC;YACD,MAAc,CAAC,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;YAEpE,cAAc,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC;IACL,CAAC;CACF"} \ No newline at end of file diff --git a/wwwroot/js/renderers/EventRenderer.d.ts b/wwwroot/js/renderers/EventRenderer.d.ts new file mode 100644 index 0000000..286119e --- /dev/null +++ b/wwwroot/js/renderers/EventRenderer.d.ts @@ -0,0 +1,96 @@ +import { ICalendarEvent } from '../types/CalendarTypes'; +import { Configuration } from '../configurations/CalendarConfig'; +import { PositionUtils } from '../utils/PositionUtils'; +import { IColumnBounds } from '../utils/ColumnDetectionUtils'; +import { IDragColumnChangeEventPayload, IDragMoveEventPayload, IDragStartEventPayload, IDragMouseEnterColumnEventPayload } from '../types/EventTypes'; +import { DateService } from '../utils/DateService'; +import { EventStackManager } from '../managers/EventStackManager'; +import { EventLayoutCoordinator } from '../managers/EventLayoutCoordinator'; +/** + * Interface for event rendering strategies + */ +export interface IEventRenderer { + renderEvents(events: ICalendarEvent[], container: HTMLElement): void; + clearEvents(container?: HTMLElement): void; + renderSingleColumnEvents?(column: IColumnBounds, events: ICalendarEvent[]): void; + handleDragStart?(payload: IDragStartEventPayload): void; + handleDragMove?(payload: IDragMoveEventPayload): void; + handleDragAutoScroll?(eventId: string, snappedY: number): void; + handleDragEnd?(originalElement: HTMLElement, draggedClone: HTMLElement, finalColumn: IColumnBounds, finalY: number): void; + handleEventClick?(eventId: string, originalElement: HTMLElement): void; + handleColumnChange?(payload: IDragColumnChangeEventPayload): void; + handleNavigationCompleted?(): void; + handleConvertAllDayToTimed?(payload: IDragMouseEnterColumnEventPayload): void; +} +/** + * Date-based event renderer + */ +export declare class DateEventRenderer implements IEventRenderer { + private dateService; + private stackManager; + private layoutCoordinator; + private config; + private positionUtils; + private draggedClone; + private originalEvent; + constructor(dateService: DateService, stackManager: EventStackManager, layoutCoordinator: EventLayoutCoordinator, config: Configuration, positionUtils: PositionUtils); + private applyDragStyling; + /** + * Handle drag start event + */ + handleDragStart(payload: IDragStartEventPayload): void; + /** + * Handle drag move event + */ + handleDragMove(payload: IDragMoveEventPayload): void; + /** + * Handle column change during drag + */ + handleColumnChange(payload: IDragColumnChangeEventPayload): void; + /** + * Handle conversion of all-day event to timed event + */ + handleConvertAllDayToTimed(payload: IDragMouseEnterColumnEventPayload): void; + /** + * Handle drag end event + */ + handleDragEnd(originalElement: HTMLElement, draggedClone: HTMLElement, finalColumn: IColumnBounds, finalY: number): void; + /** + * Handle navigation completed event + */ + handleNavigationCompleted(): void; + /** + * Fade out and remove element + */ + private fadeOutAndRemove; + renderEvents(events: ICalendarEvent[], container: HTMLElement): void; + /** + * Render events for a single column + */ + renderSingleColumnEvents(column: IColumnBounds, events: ICalendarEvent[]): void; + /** + * Render events in a column using combined stacking + grid algorithm + */ + private renderColumnEvents; + /** + * Render events in a grid container (side-by-side with column sharing) + */ + private renderGridGroup; + /** + * Render a single column within a grid group + * Column may contain multiple events that don't overlap + */ + private renderGridColumn; + /** + * Render event within a grid container (absolute positioning within column) + */ + private renderEventInGrid; + private renderEvent; + protected calculateEventPosition(event: ICalendarEvent): { + top: number; + height: number; + }; + clearEvents(container?: HTMLElement): void; + protected getColumns(container: HTMLElement): HTMLElement[]; + protected getEventsForColumn(column: HTMLElement, events: ICalendarEvent[]): ICalendarEvent[]; +} diff --git a/wwwroot/js/renderers/EventRenderer.js b/wwwroot/js/renderers/EventRenderer.js new file mode 100644 index 0000000..ce026a4 --- /dev/null +++ b/wwwroot/js/renderers/EventRenderer.js @@ -0,0 +1,296 @@ +// Event rendering strategy interface and implementations +import { SwpEventElement } from '../elements/SwpEventElement'; +/** + * Date-based event renderer + */ +export class DateEventRenderer { + constructor(dateService, stackManager, layoutCoordinator, config, positionUtils) { + this.draggedClone = null; + this.originalEvent = null; + this.dateService = dateService; + this.stackManager = stackManager; + this.layoutCoordinator = layoutCoordinator; + this.config = config; + this.positionUtils = positionUtils; + } + applyDragStyling(element) { + element.classList.add('dragging'); + element.style.removeProperty("margin-left"); + } + /** + * Handle drag start event + */ + handleDragStart(payload) { + this.originalEvent = payload.originalElement; + ; + // Use the clone from the payload instead of creating a new one + this.draggedClone = payload.draggedClone; + if (this.draggedClone && payload.columnBounds) { + // Apply drag styling + this.applyDragStyling(this.draggedClone); + // Add to current column's events layer (not directly to column) + const eventsLayer = payload.columnBounds.element.querySelector('swp-events-layer'); + if (eventsLayer) { + eventsLayer.appendChild(this.draggedClone); + // Set initial position to prevent "jump to top" effect + // Calculate absolute Y position from original element + const originalRect = this.originalEvent.getBoundingClientRect(); + const columnRect = payload.columnBounds.boundingClientRect; + const initialTop = originalRect.top - columnRect.top; + this.draggedClone.style.top = `${initialTop}px`; + } + } + // Make original semi-transparent + this.originalEvent.style.opacity = '0.3'; + this.originalEvent.style.userSelect = 'none'; + } + /** + * Handle drag move event + */ + handleDragMove(payload) { + const swpEvent = payload.draggedClone; + const columnDate = this.dateService.parseISO(payload.columnBounds.date); + swpEvent.updatePosition(columnDate, payload.snappedY); + } + /** + * Handle column change during drag + */ + handleColumnChange(payload) { + const eventsLayer = payload.newColumn.element.querySelector('swp-events-layer'); + if (eventsLayer && payload.draggedClone.parentElement !== eventsLayer) { + eventsLayer.appendChild(payload.draggedClone); + // Recalculate timestamps with new column date + const currentTop = parseFloat(payload.draggedClone.style.top) || 0; + const swpEvent = payload.draggedClone; + const columnDate = this.dateService.parseISO(payload.newColumn.date); + swpEvent.updatePosition(columnDate, currentTop); + } + } + /** + * Handle conversion of all-day event to timed event + */ + handleConvertAllDayToTimed(payload) { + console.log('🎯 DateEventRenderer: Converting all-day to timed event', { + eventId: payload.calendarEvent.id, + targetColumn: payload.targetColumn.date, + snappedY: payload.snappedY + }); + let timedClone = SwpEventElement.fromCalendarEvent(payload.calendarEvent); + let position = this.calculateEventPosition(payload.calendarEvent); + // Set position at snapped Y + //timedClone.style.top = `${snappedY}px`; + // Set complete styling for dragged clone (matching normal event rendering) + timedClone.style.height = `${position.height - 3}px`; + timedClone.style.left = '2px'; + timedClone.style.right = '2px'; + timedClone.style.width = 'auto'; + timedClone.style.pointerEvents = 'none'; + // Apply drag styling + this.applyDragStyling(timedClone); + // Find the events layer in the target column + let eventsLayer = payload.targetColumn.element.querySelector('swp-events-layer'); + // Add "clone-" prefix to match clone ID pattern + //timedClone.dataset.eventId = `clone-${payload.calendarEvent.id}`; + // Remove old all-day clone and replace with new timed clone + payload.draggedClone.remove(); + payload.replaceClone(timedClone); + eventsLayer.appendChild(timedClone); + } + /** + * Handle drag end event + */ + handleDragEnd(originalElement, draggedClone, finalColumn, finalY) { + if (!draggedClone || !originalElement) { + console.warn('Missing draggedClone or originalElement'); + return; + } + // Only fade out and remove if it's a swp-event (not swp-allday-event) + // AllDayManager handles removal of swp-allday-event elements + if (originalElement.tagName === 'SWP-EVENT') { + this.fadeOutAndRemove(originalElement); + } + // Remove clone prefix and normalize clone to be a regular event + const cloneId = draggedClone.dataset.eventId; + if (cloneId && cloneId.startsWith('clone-')) { + draggedClone.dataset.eventId = cloneId.replace('clone-', ''); + } + // Fully normalize the clone to be a regular event + draggedClone.classList.remove('dragging'); + draggedClone.style.pointerEvents = ''; // Re-enable pointer events + // Clean up instance state + this.draggedClone = null; + this.originalEvent = null; + // Clean up any remaining day event clones + const dayEventClone = document.querySelector(`swp-event[data-event-id="clone-${cloneId}"]`); + if (dayEventClone) { + dayEventClone.remove(); + } + } + /** + * Handle navigation completed event + */ + handleNavigationCompleted() { + // Default implementation - can be overridden by subclasses + } + /** + * Fade out and remove element + */ + fadeOutAndRemove(element) { + element.style.transition = 'opacity 0.3s ease-out'; + element.style.opacity = '0'; + setTimeout(() => { + element.remove(); + }, 300); + } + renderEvents(events, container) { + // Filter out all-day events - they should be handled by AllDayEventRenderer + const timedEvents = events.filter(event => !event.allDay); + // Find columns in the specific container for regular events + const columns = this.getColumns(container); + columns.forEach(column => { + const columnEvents = this.getEventsForColumn(column, timedEvents); + const eventsLayer = column.querySelector('swp-events-layer'); + if (eventsLayer) { + this.renderColumnEvents(columnEvents, eventsLayer); + } + }); + } + /** + * Render events for a single column + */ + renderSingleColumnEvents(column, events) { + const columnEvents = this.getEventsForColumn(column.element, events); + const eventsLayer = column.element.querySelector('swp-events-layer'); + if (eventsLayer) { + this.renderColumnEvents(columnEvents, eventsLayer); + } + } + /** + * Render events in a column using combined stacking + grid algorithm + */ + renderColumnEvents(columnEvents, eventsLayer) { + if (columnEvents.length === 0) + return; + // Get layout from coordinator + const layout = this.layoutCoordinator.calculateColumnLayout(columnEvents); + // Render grid groups + layout.gridGroups.forEach(gridGroup => { + this.renderGridGroup(gridGroup, eventsLayer); + }); + // Render stacked events + layout.stackedEvents.forEach(stackedEvent => { + const element = this.renderEvent(stackedEvent.event); + this.stackManager.applyStackLinkToElement(element, stackedEvent.stackLink); + this.stackManager.applyVisualStyling(element, stackedEvent.stackLink.stackLevel); + eventsLayer.appendChild(element); + }); + } + /** + * Render events in a grid container (side-by-side with column sharing) + */ + renderGridGroup(gridGroup, eventsLayer) { + const groupElement = document.createElement('swp-event-group'); + // Add grid column class based on number of columns (not events) + const colCount = gridGroup.columns.length; + groupElement.classList.add(`cols-${colCount}`); + // Add stack level class for margin-left offset + groupElement.classList.add(`stack-level-${gridGroup.stackLevel}`); + // Position from layout + groupElement.style.top = `${gridGroup.position.top}px`; + // Add stack-link attribute for drag-drop (group acts as a stacked item) + const stackLink = { + stackLevel: gridGroup.stackLevel + }; + this.stackManager.applyStackLinkToElement(groupElement, stackLink); + // Apply visual styling (margin-left and z-index) using StackManager + this.stackManager.applyVisualStyling(groupElement, gridGroup.stackLevel); + // Render each column + const earliestEvent = gridGroup.events[0]; + gridGroup.columns.forEach((columnEvents) => { + const columnContainer = this.renderGridColumn(columnEvents, earliestEvent.start); + groupElement.appendChild(columnContainer); + }); + eventsLayer.appendChild(groupElement); + } + /** + * Render a single column within a grid group + * Column may contain multiple events that don't overlap + */ + renderGridColumn(columnEvents, containerStart) { + const columnContainer = document.createElement('div'); + columnContainer.style.position = 'relative'; + columnEvents.forEach(event => { + const element = this.renderEventInGrid(event, containerStart); + columnContainer.appendChild(element); + }); + return columnContainer; + } + /** + * Render event within a grid container (absolute positioning within column) + */ + renderEventInGrid(event, containerStart) { + const element = SwpEventElement.fromCalendarEvent(event); + // Calculate event height + const position = this.calculateEventPosition(event); + // Calculate relative top offset if event starts after container start + // (e.g., if container starts at 07:00 and event starts at 08:15, offset = 75 min) + const timeDiffMs = event.start.getTime() - containerStart.getTime(); + const timeDiffMinutes = timeDiffMs / (1000 * 60); + const gridSettings = this.config.gridSettings; + const relativeTop = timeDiffMinutes > 0 ? (timeDiffMinutes / 60) * gridSettings.hourHeight : 0; + // Events in grid columns are positioned absolutely within their column container + element.style.position = 'absolute'; + element.style.top = `${relativeTop}px`; + element.style.height = `${position.height - 3}px`; + element.style.left = '0'; + element.style.right = '0'; + return element; + } + renderEvent(event) { + const element = SwpEventElement.fromCalendarEvent(event); + // Apply positioning (moved from SwpEventElement.applyPositioning) + const position = this.calculateEventPosition(event); + element.style.position = 'absolute'; + element.style.top = `${position.top + 1}px`; + element.style.height = `${position.height - 3}px`; + element.style.left = '2px'; + element.style.right = '2px'; + return element; + } + calculateEventPosition(event) { + // Delegate to PositionUtils for centralized position calculation + return this.positionUtils.calculateEventPosition(event.start, event.end); + } + clearEvents(container) { + const eventSelector = 'swp-event'; + const groupSelector = 'swp-event-group'; + const existingEvents = container + ? container.querySelectorAll(eventSelector) + : document.querySelectorAll(eventSelector); + const existingGroups = container + ? container.querySelectorAll(groupSelector) + : document.querySelectorAll(groupSelector); + existingEvents.forEach(event => event.remove()); + existingGroups.forEach(group => group.remove()); + } + getColumns(container) { + const columns = container.querySelectorAll('swp-day-column'); + return Array.from(columns); + } + getEventsForColumn(column, events) { + const columnDate = column.dataset.date; + if (!columnDate) { + return []; + } + // Create start and end of day for interval overlap check + const columnStart = this.dateService.parseISO(`${columnDate}T00:00:00`); + const columnEnd = this.dateService.parseISO(`${columnDate}T23:59:59.999`); + const columnEvents = events.filter(event => { + // Interval overlap: event overlaps with column day if event.start < columnEnd AND event.end > columnStart + const overlaps = event.start < columnEnd && event.end > columnStart; + return overlaps; + }); + return columnEvents; + } +} +//# sourceMappingURL=EventRenderer.js.map \ No newline at end of file diff --git a/wwwroot/js/renderers/EventRenderer.js.map b/wwwroot/js/renderers/EventRenderer.js.map new file mode 100644 index 0000000..78765f0 --- /dev/null +++ b/wwwroot/js/renderers/EventRenderer.js.map @@ -0,0 +1 @@ +{"version":3,"file":"EventRenderer.js","sourceRoot":"","sources":["../../../src/renderers/EventRenderer.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/renderers/EventRendererManager.d.ts b/wwwroot/js/renderers/EventRendererManager.d.ts new file mode 100644 index 0000000..3a2131c --- /dev/null +++ b/wwwroot/js/renderers/EventRendererManager.d.ts @@ -0,0 +1,55 @@ +import { IEventBus, IRenderContext } from '../types/CalendarTypes'; +import { EventManager } from '../managers/EventManager'; +import { IEventRenderer } from './EventRenderer'; +import { DateService } from '../utils/DateService'; +/** + * EventRenderingService - Render events i DOM med positionering using Strategy Pattern + * Håndterer event positioning og overlap detection + */ +export declare class EventRenderingService { + private eventBus; + private eventManager; + private strategy; + private dateService; + private dragMouseLeaveHeaderListener; + constructor(eventBus: IEventBus, eventManager: EventManager, strategy: IEventRenderer, dateService: DateService); + /** + * Render events in a specific container for a given period + */ + renderEvents(context: IRenderContext): Promise; + private setupEventListeners; + /** + * Handle GRID_RENDERED event - render events in the current grid + */ + private handleGridRendered; + /** + * Handle VIEW_CHANGED event - clear and re-render for new view + */ + private handleViewChanged; + /** + * Setup all drag event listeners - moved from EventRenderer for better separation of concerns + */ + private setupDragEventListeners; + private setupDragStartListener; + private setupDragMoveListener; + private setupDragEndListener; + private setupDragColumnChangeListener; + private setupDragMouseLeaveHeaderListener; + private setupDragMouseEnterColumnListener; + private setupResizeEndListener; + private setupNavigationCompletedListener; + /** + * Re-render affected columns after drag to recalculate stacking/grouping + */ + private reRenderAffectedColumns; + /** + * Clear events in a single column's events layer + */ + private clearColumnEvents; + /** + * Render events for a single column + */ + private renderSingleColumn; + private clearEvents; + refresh(container?: HTMLElement): void; +} diff --git a/wwwroot/js/renderers/EventRendererManager.js b/wwwroot/js/renderers/EventRendererManager.js new file mode 100644 index 0000000..d326ba4 --- /dev/null +++ b/wwwroot/js/renderers/EventRendererManager.js @@ -0,0 +1,264 @@ +import { CoreEvents } from '../constants/CoreEvents'; +import { ColumnDetectionUtils } from '../utils/ColumnDetectionUtils'; +/** + * EventRenderingService - Render events i DOM med positionering using Strategy Pattern + * Håndterer event positioning og overlap detection + */ +export class EventRenderingService { + constructor(eventBus, eventManager, strategy, dateService) { + this.dragMouseLeaveHeaderListener = null; + this.eventBus = eventBus; + this.eventManager = eventManager; + this.strategy = strategy; + this.dateService = dateService; + this.setupEventListeners(); + } + /** + * Render events in a specific container for a given period + */ + async renderEvents(context) { + // Clear existing events in the specific container first + this.strategy.clearEvents(context.container); + // Get events from EventManager for the period + const events = await this.eventManager.getEventsForPeriod(context.startDate, context.endDate); + if (events.length === 0) { + return; + } + // Filter events by type - only render timed events here + const timedEvents = events.filter(event => !event.allDay); + console.log('🎯 EventRenderingService: Event filtering', { + totalEvents: events.length, + timedEvents: timedEvents.length, + allDayEvents: events.length - timedEvents.length + }); + // Render timed events using existing strategy + if (timedEvents.length > 0) { + this.strategy.renderEvents(timedEvents, context.container); + } + // Emit EVENTS_RENDERED event for filtering system + this.eventBus.emit(CoreEvents.EVENTS_RENDERED, { + events: events, + container: context.container + }); + } + setupEventListeners() { + this.eventBus.on(CoreEvents.GRID_RENDERED, (event) => { + this.handleGridRendered(event); + }); + this.eventBus.on(CoreEvents.VIEW_CHANGED, (event) => { + this.handleViewChanged(event); + }); + // Handle all drag events and delegate to appropriate renderer + this.setupDragEventListeners(); + } + /** + * Handle GRID_RENDERED event - render events in the current grid + */ + handleGridRendered(event) { + const { container, dates } = event.detail; + if (!container || !dates || dates.length === 0) { + return; + } + // Calculate startDate and endDate from dates array + const startDate = dates[0]; + const endDate = dates[dates.length - 1]; + this.renderEvents({ + container, + startDate, + endDate + }); + } + /** + * Handle VIEW_CHANGED event - clear and re-render for new view + */ + handleViewChanged(event) { + // Clear all existing events since view structure may have changed + this.clearEvents(); + // New rendering will be triggered by subsequent GRID_RENDERED event + } + /** + * Setup all drag event listeners - moved from EventRenderer for better separation of concerns + */ + setupDragEventListeners() { + this.setupDragStartListener(); + this.setupDragMoveListener(); + this.setupDragEndListener(); + this.setupDragColumnChangeListener(); + this.setupDragMouseLeaveHeaderListener(); + this.setupDragMouseEnterColumnListener(); + this.setupResizeEndListener(); + this.setupNavigationCompletedListener(); + } + setupDragStartListener() { + this.eventBus.on('drag:start', (event) => { + const dragStartPayload = event.detail; + if (dragStartPayload.originalElement.hasAttribute('data-allday')) { + return; + } + if (dragStartPayload.originalElement && this.strategy.handleDragStart && dragStartPayload.columnBounds) { + this.strategy.handleDragStart(dragStartPayload); + } + }); + } + setupDragMoveListener() { + this.eventBus.on('drag:move', (event) => { + let dragEvent = event.detail; + if (dragEvent.draggedClone.hasAttribute('data-allday')) { + return; + } + if (this.strategy.handleDragMove) { + this.strategy.handleDragMove(dragEvent); + } + }); + } + setupDragEndListener() { + this.eventBus.on('drag:end', async (event) => { + const { originalElement, draggedClone, originalSourceColumn, finalPosition, target } = event.detail; + const finalColumn = finalPosition.column; + const finalY = finalPosition.snappedY; + let element = draggedClone; + // Only handle day column drops for EventRenderer + if (target === 'swp-day-column' && finalColumn) { + if (originalElement && draggedClone && this.strategy.handleDragEnd) { + this.strategy.handleDragEnd(originalElement, draggedClone, finalColumn, finalY); + } + await this.eventManager.updateEvent(element.eventId, { + start: element.start, + end: element.end, + allDay: false + }); + // Re-render affected columns for stacking/grouping (now with updated data) + await this.reRenderAffectedColumns(originalSourceColumn, finalColumn); + } + }); + } + setupDragColumnChangeListener() { + this.eventBus.on('drag:column-change', (event) => { + let columnChangeEvent = event.detail; + // Filter: Only handle events where clone is NOT an all-day event (normal timed events) + if (columnChangeEvent.draggedClone && columnChangeEvent.draggedClone.hasAttribute('data-allday')) { + return; + } + if (this.strategy.handleColumnChange) { + this.strategy.handleColumnChange(columnChangeEvent); + } + }); + } + setupDragMouseLeaveHeaderListener() { + this.dragMouseLeaveHeaderListener = (event) => { + const { targetDate, mousePosition, originalElement, draggedClone: cloneElement } = event.detail; + if (cloneElement) + cloneElement.style.display = ''; + console.log('🚪 EventRendererManager: Received drag:mouseleave-header', { + targetDate, + originalElement: originalElement, + cloneElement: cloneElement + }); + }; + this.eventBus.on('drag:mouseleave-header', this.dragMouseLeaveHeaderListener); + } + setupDragMouseEnterColumnListener() { + this.eventBus.on('drag:mouseenter-column', (event) => { + const payload = event.detail; + // Only handle if clone is an all-day event + if (!payload.draggedClone.hasAttribute('data-allday')) { + return; + } + console.log('🎯 EventRendererManager: Received drag:mouseenter-column', { + targetColumn: payload.targetColumn, + snappedY: payload.snappedY, + calendarEvent: payload.calendarEvent + }); + // Delegate to strategy for conversion + if (this.strategy.handleConvertAllDayToTimed) { + this.strategy.handleConvertAllDayToTimed(payload); + } + }); + } + setupResizeEndListener() { + this.eventBus.on('resize:end', async (event) => { + const { eventId, element } = event.detail; + // Update event data in EventManager with new end time from resized element + const swpEvent = element; + const newStart = swpEvent.start; + const newEnd = swpEvent.end; + await this.eventManager.updateEvent(eventId, { + start: newStart, + end: newEnd + }); + console.log('📝 EventRendererManager: Updated event after resize', { + eventId, + newStart, + newEnd + }); + let columnBounds = ColumnDetectionUtils.getColumnBoundsByDate(newStart); + if (columnBounds) + await this.renderSingleColumn(columnBounds); + }); + } + setupNavigationCompletedListener() { + this.eventBus.on(CoreEvents.NAVIGATION_COMPLETED, () => { + // Delegate to strategy if it handles navigation + if (this.strategy.handleNavigationCompleted) { + this.strategy.handleNavigationCompleted(); + } + }); + } + /** + * Re-render affected columns after drag to recalculate stacking/grouping + */ + async reRenderAffectedColumns(originalSourceColumn, targetColumn) { + // Re-render original source column if exists + if (originalSourceColumn) { + await this.renderSingleColumn(originalSourceColumn); + } + // Re-render target column if exists and different from source + if (targetColumn && targetColumn.date !== originalSourceColumn?.date) { + await this.renderSingleColumn(targetColumn); + } + } + /** + * Clear events in a single column's events layer + */ + clearColumnEvents(eventsLayer) { + const existingEvents = eventsLayer.querySelectorAll('swp-event'); + const existingGroups = eventsLayer.querySelectorAll('swp-event-group'); + existingEvents.forEach(event => event.remove()); + existingGroups.forEach(group => group.remove()); + } + /** + * Render events for a single column + */ + async renderSingleColumn(column) { + // Get events for just this column's date + const columnStart = this.dateService.parseISO(`${column.date}T00:00:00`); + const columnEnd = this.dateService.parseISO(`${column.date}T23:59:59.999`); + // Get events from EventManager for this single date + const events = await this.eventManager.getEventsForPeriod(columnStart, columnEnd); + // Filter to timed events only + const timedEvents = events.filter(event => !event.allDay); + // Get events layer within this specific column + const eventsLayer = column.element.querySelector('swp-events-layer'); + if (!eventsLayer) { + console.warn('EventRendererManager: Events layer not found in column'); + return; + } + // Clear only this column's events + this.clearColumnEvents(eventsLayer); + // Render events for this column using strategy + if (this.strategy.renderSingleColumnEvents) { + this.strategy.renderSingleColumnEvents(column, timedEvents); + } + console.log('🔄 EventRendererManager: Re-rendered single column', { + columnDate: column.date, + eventsCount: timedEvents.length + }); + } + clearEvents(container) { + this.strategy.clearEvents(container); + } + refresh(container) { + this.clearEvents(container); + } +} +//# sourceMappingURL=EventRendererManager.js.map \ No newline at end of file diff --git a/wwwroot/js/renderers/EventRendererManager.js.map b/wwwroot/js/renderers/EventRendererManager.js.map new file mode 100644 index 0000000..d1cfc77 --- /dev/null +++ b/wwwroot/js/renderers/EventRendererManager.js.map @@ -0,0 +1 @@ +{"version":3,"file":"EventRendererManager.js","sourceRoot":"","sources":["../../../src/renderers/EventRendererManager.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/renderers/GridRenderer.d.ts b/wwwroot/js/renderers/GridRenderer.d.ts new file mode 100644 index 0000000..8613651 --- /dev/null +++ b/wwwroot/js/renderers/GridRenderer.d.ts @@ -0,0 +1,180 @@ +import { Configuration } from '../configurations/CalendarConfig'; +import { CalendarView, ICalendarEvent } from '../types/CalendarTypes'; +import { IColumnRenderer } from './ColumnRenderer'; +import { DateService } from '../utils/DateService'; +import { WorkHoursManager } from '../managers/WorkHoursManager'; +/** + * GridRenderer - Centralized DOM rendering for calendar grid structure + * + * ARCHITECTURE OVERVIEW: + * ===================== + * GridRenderer is responsible for creating and managing the complete DOM structure + * of the calendar grid. It follows the Strategy Pattern by delegating specific + * rendering tasks to specialized renderers (DateHeaderRenderer, ColumnRenderer). + * + * RESPONSIBILITY HIERARCHY: + * ======================== + * GridRenderer (this file) + * ├─ Creates overall grid skeleton + * ├─ Manages time axis (hour markers) + * └─ Delegates to specialized renderers: + * ├─ DateHeaderRenderer → Renders date headers + * └─ ColumnRenderer → Renders day columns + * + * DOM STRUCTURE CREATED: + * ===================== + * + * ← GridRenderer + * ← GridRenderer + * 00:00 ← GridRenderer (iterates hours) + * + * ← GridRenderer + * ← GridRenderer creates container + * ← DateHeaderRenderer (iterates dates) + * + * ← GridRenderer + * ← GridRenderer + * ← GridRenderer + * ← GridRenderer creates container + * ← ColumnRenderer (iterates dates) + * + * + * + * + * + * + * RENDERING FLOW: + * ============== + * 1. renderGrid() - Entry point called by GridManager + * ├─ First render: createCompleteGridStructure() + * └─ Updates: updateGridContent() + * + * 2. createCompleteGridStructure() + * ├─ Creates header spacer + * ├─ Creates time axis (calls createOptimizedTimeAxis) + * └─ Creates grid container (calls createOptimizedGridContainer) + * + * 3. createOptimizedGridContainer() + * ├─ Creates calendar header container + * ├─ Creates scrollable content structure + * └─ Creates column container (calls renderColumnContainer) + * + * 4. renderColumnContainer() + * └─ Delegates to ColumnRenderer.render() + * └─ ColumnRenderer iterates dates and creates columns + * + * OPTIMIZATION STRATEGY: + * ===================== + * - Caches DOM references (cachedGridContainer, cachedTimeAxis) + * - Uses DocumentFragment for batch DOM insertions + * - Only updates changed content on re-renders + * - Delegates specialized tasks to strategy renderers + * + * USAGE EXAMPLE: + * ============= + * const gridRenderer = new GridRenderer(columnRenderer, dateService, config); + * gridRenderer.renderGrid(containerElement, new Date(), 'week'); + */ +export declare class GridRenderer { + private cachedGridContainer; + private cachedTimeAxis; + private dateService; + private columnRenderer; + private config; + private workHoursManager; + constructor(columnRenderer: IColumnRenderer, dateService: DateService, config: Configuration, workHoursManager: WorkHoursManager); + /** + * Main entry point for rendering the complete calendar grid + * + * This method decides between full render (first time) or optimized update. + * It caches the grid reference for performance. + * + * @param grid - Container element where grid will be rendered + * @param currentDate - Base date for the current view (e.g., any date in the week) + * @param view - Calendar view type (day/week/month) + * @param dates - Array of dates to render as columns + * @param events - All events for the period + */ + renderGrid(grid: HTMLElement, currentDate: Date, view?: CalendarView, dates?: Date[], events?: ICalendarEvent[]): void; + /** + * Creates the complete grid structure from scratch + * + * Uses DocumentFragment for optimal performance by minimizing reflows. + * Creates all child elements in memory first, then appends everything at once. + * + * Structure created: + * 1. Header spacer (placeholder for alignment) + * 2. Time axis (hour markers 00:00-23:00) + * 3. Grid container (header + scrollable content) + * + * @param grid - Parent container + * @param currentDate - Current view date + * @param view - View type + * @param dates - Array of dates to render + */ + private createCompleteGridStructure; + /** + * Creates the time axis with hour markers + * + * Iterates from dayStartHour to dayEndHour (configured in GridSettings). + * Each marker shows the hour in the configured time format. + * + * @returns Time axis element with all hour markers + */ + private createOptimizedTimeAxis; + /** + * Creates the main grid container with header and columns + * + * This is the scrollable area containing: + * - Calendar header (dates/resources) - created here, populated by DateHeaderRenderer + * - Time grid (grid lines + day columns) - structure created here + * - Column container - created here, populated by ColumnRenderer + * + * @param currentDate - Current view date + * @param view - View type + * @param dates - Array of dates to render + * @returns Complete grid container element + */ + private createOptimizedGridContainer; + /** + * Renders columns by iterating through dates + * + * GridRenderer creates column structure with work hours styling. + * Event rendering is handled by EventRenderingService listening to GRID_RENDERED. + * + * @param columnContainer - Empty container to populate + * @param dates - Array of dates to render + * @param events - All events for the period (passed through, not used here) + */ + private renderColumnContainer; + /** + * Apply work hours styling to a column + */ + private applyWorkHoursStyling; + /** + * Optimized update of grid content without full rebuild + * + * Only updates the column container content, leaving the structure intact. + * This is much faster than recreating the entire grid. + * + * @param grid - Existing grid element + * @param currentDate - New view date + * @param view - View type + * @param dates - Array of dates to render + * @param events - All events for the period + */ + private updateGridContent; + /** + * Creates a new grid for slide animations during navigation + * + * Used by NavigationManager for smooth week-to-week transitions. + * Creates a complete grid positioned absolutely for animation. + * + * Note: Positioning is handled by Animation API, not here. + * + * @param parentContainer - Container for the new grid + * @param weekStart - Start date of the new week + * @returns New grid element ready for animation + */ + createNavigationGrid(parentContainer: HTMLElement, weekStart: Date): HTMLElement; +} diff --git a/wwwroot/js/renderers/GridRenderer.js b/wwwroot/js/renderers/GridRenderer.js new file mode 100644 index 0000000..0a3a7c9 --- /dev/null +++ b/wwwroot/js/renderers/GridRenderer.js @@ -0,0 +1,289 @@ +import { TimeFormatter } from '../utils/TimeFormatter'; +/** + * GridRenderer - Centralized DOM rendering for calendar grid structure + * + * ARCHITECTURE OVERVIEW: + * ===================== + * GridRenderer is responsible for creating and managing the complete DOM structure + * of the calendar grid. It follows the Strategy Pattern by delegating specific + * rendering tasks to specialized renderers (DateHeaderRenderer, ColumnRenderer). + * + * RESPONSIBILITY HIERARCHY: + * ======================== + * GridRenderer (this file) + * ├─ Creates overall grid skeleton + * ├─ Manages time axis (hour markers) + * └─ Delegates to specialized renderers: + * ├─ DateHeaderRenderer → Renders date headers + * └─ ColumnRenderer → Renders day columns + * + * DOM STRUCTURE CREATED: + * ===================== + * + * ← GridRenderer + * ← GridRenderer + * 00:00 ← GridRenderer (iterates hours) + * + * ← GridRenderer + * ← GridRenderer creates container + * ← DateHeaderRenderer (iterates dates) + * + * ← GridRenderer + * ← GridRenderer + * ← GridRenderer + * ← GridRenderer creates container + * ← ColumnRenderer (iterates dates) + * + * + * + * + * + * + * RENDERING FLOW: + * ============== + * 1. renderGrid() - Entry point called by GridManager + * ├─ First render: createCompleteGridStructure() + * └─ Updates: updateGridContent() + * + * 2. createCompleteGridStructure() + * ├─ Creates header spacer + * ├─ Creates time axis (calls createOptimizedTimeAxis) + * └─ Creates grid container (calls createOptimizedGridContainer) + * + * 3. createOptimizedGridContainer() + * ├─ Creates calendar header container + * ├─ Creates scrollable content structure + * └─ Creates column container (calls renderColumnContainer) + * + * 4. renderColumnContainer() + * └─ Delegates to ColumnRenderer.render() + * └─ ColumnRenderer iterates dates and creates columns + * + * OPTIMIZATION STRATEGY: + * ===================== + * - Caches DOM references (cachedGridContainer, cachedTimeAxis) + * - Uses DocumentFragment for batch DOM insertions + * - Only updates changed content on re-renders + * - Delegates specialized tasks to strategy renderers + * + * USAGE EXAMPLE: + * ============= + * const gridRenderer = new GridRenderer(columnRenderer, dateService, config); + * gridRenderer.renderGrid(containerElement, new Date(), 'week'); + */ +export class GridRenderer { + constructor(columnRenderer, dateService, config, workHoursManager) { + this.cachedGridContainer = null; + this.cachedTimeAxis = null; + this.dateService = dateService; + this.columnRenderer = columnRenderer; + this.config = config; + this.workHoursManager = workHoursManager; + } + /** + * Main entry point for rendering the complete calendar grid + * + * This method decides between full render (first time) or optimized update. + * It caches the grid reference for performance. + * + * @param grid - Container element where grid will be rendered + * @param currentDate - Base date for the current view (e.g., any date in the week) + * @param view - Calendar view type (day/week/month) + * @param dates - Array of dates to render as columns + * @param events - All events for the period + */ + renderGrid(grid, currentDate, view = 'week', dates = [], events = []) { + if (!grid || !currentDate) { + return; + } + // Cache grid reference for performance + this.cachedGridContainer = grid; + // Only clear and rebuild if grid is empty (first render) + if (grid.children.length === 0) { + this.createCompleteGridStructure(grid, currentDate, view, dates, events); + } + else { + // Optimized update - only refresh dynamic content + this.updateGridContent(grid, currentDate, view, dates, events); + } + } + /** + * Creates the complete grid structure from scratch + * + * Uses DocumentFragment for optimal performance by minimizing reflows. + * Creates all child elements in memory first, then appends everything at once. + * + * Structure created: + * 1. Header spacer (placeholder for alignment) + * 2. Time axis (hour markers 00:00-23:00) + * 3. Grid container (header + scrollable content) + * + * @param grid - Parent container + * @param currentDate - Current view date + * @param view - View type + * @param dates - Array of dates to render + */ + createCompleteGridStructure(grid, currentDate, view, dates, events) { + // Create all elements in memory first for better performance + const fragment = document.createDocumentFragment(); + // Create header spacer + const headerSpacer = document.createElement('swp-header-spacer'); + fragment.appendChild(headerSpacer); + // Create time axis with caching + const timeAxis = this.createOptimizedTimeAxis(); + this.cachedTimeAxis = timeAxis; + fragment.appendChild(timeAxis); + // Create grid container with caching + const gridContainer = this.createOptimizedGridContainer(currentDate, view, dates, events); + this.cachedGridContainer = gridContainer; + fragment.appendChild(gridContainer); + // Append all at once to minimize reflows + grid.appendChild(fragment); + } + /** + * Creates the time axis with hour markers + * + * Iterates from dayStartHour to dayEndHour (configured in GridSettings). + * Each marker shows the hour in the configured time format. + * + * @returns Time axis element with all hour markers + */ + createOptimizedTimeAxis() { + const timeAxis = document.createElement('swp-time-axis'); + const timeAxisContent = document.createElement('swp-time-axis-content'); + const gridSettings = this.config.gridSettings; + const startHour = gridSettings.dayStartHour; + const endHour = gridSettings.dayEndHour; + const fragment = document.createDocumentFragment(); + for (let hour = startHour; hour < endHour; hour++) { + const marker = document.createElement('swp-hour-marker'); + const date = new Date(2024, 0, 1, hour, 0); + marker.textContent = TimeFormatter.formatTime(date); + fragment.appendChild(marker); + } + timeAxisContent.appendChild(fragment); + timeAxisContent.style.top = '-1px'; + timeAxis.appendChild(timeAxisContent); + return timeAxis; + } + /** + * Creates the main grid container with header and columns + * + * This is the scrollable area containing: + * - Calendar header (dates/resources) - created here, populated by DateHeaderRenderer + * - Time grid (grid lines + day columns) - structure created here + * - Column container - created here, populated by ColumnRenderer + * + * @param currentDate - Current view date + * @param view - View type + * @param dates - Array of dates to render + * @returns Complete grid container element + */ + createOptimizedGridContainer(dates, events) { + const gridContainer = document.createElement('swp-grid-container'); + // Create calendar header as first child - always exists now! + const calendarHeader = document.createElement('swp-calendar-header'); + gridContainer.appendChild(calendarHeader); + // Create scrollable content structure + const scrollableContent = document.createElement('swp-scrollable-content'); + const timeGrid = document.createElement('swp-time-grid'); + // Add grid lines + const gridLines = document.createElement('swp-grid-lines'); + timeGrid.appendChild(gridLines); + // Create column container + const columnContainer = document.createElement('swp-day-columns'); + this.renderColumnContainer(columnContainer, dates, events); + timeGrid.appendChild(columnContainer); + scrollableContent.appendChild(timeGrid); + gridContainer.appendChild(scrollableContent); + return gridContainer; + } + /** + * Renders columns by iterating through dates + * + * GridRenderer creates column structure with work hours styling. + * Event rendering is handled by EventRenderingService listening to GRID_RENDERED. + * + * @param columnContainer - Empty container to populate + * @param dates - Array of dates to render + * @param events - All events for the period (passed through, not used here) + */ + renderColumnContainer(columnContainer, dates, events) { + // Iterate through dates and render each column structure + dates.forEach(date => { + // Create column with data-date attribute + const column = document.createElement('swp-day-column'); + column.dataset.date = this.dateService.formatISODate(date); + // Apply work hours styling + this.applyWorkHoursStyling(column, date); + // Add events layer (events will be rendered by EventRenderingService) + const eventsLayer = document.createElement('swp-events-layer'); + column.appendChild(eventsLayer); + columnContainer.appendChild(column); + }); + } + /** + * Apply work hours styling to a column + */ + applyWorkHoursStyling(column, date) { + const workHours = this.workHoursManager.getWorkHoursForDate(date); + if (workHours === 'off') { + column.setAttribute('data-day-off', 'true'); + } + else { + column.removeAttribute('data-day-off'); + // Calculate non-work hours overlay positions + const nonWorkStyle = this.workHoursManager.calculateNonWorkHoursStyle(workHours); + if (nonWorkStyle) { + column.style.setProperty('--before-work-height', `${nonWorkStyle.beforeWorkHeight}px`); + column.style.setProperty('--after-work-top', `${nonWorkStyle.afterWorkTop}px`); + } + } + } + /** + * Optimized update of grid content without full rebuild + * + * Only updates the column container content, leaving the structure intact. + * This is much faster than recreating the entire grid. + * + * @param grid - Existing grid element + * @param currentDate - New view date + * @param view - View type + * @param dates - Array of dates to render + * @param events - All events for the period + */ + updateGridContent(grid, currentDate, view, dates, events) { + // Update column container if needed + const columnContainer = grid.querySelector('swp-day-columns'); + if (columnContainer) { + columnContainer.innerHTML = ''; + this.renderColumnContainer(columnContainer, dates, events); + } + } + /** + * Creates a new grid for slide animations during navigation + * + * Used by NavigationManager for smooth week-to-week transitions. + * Creates a complete grid positioned absolutely for animation. + * + * Note: Positioning is handled by Animation API, not here. + * + * @param parentContainer - Container for the new grid + * @param weekStart - Start date of the new week + * @returns New grid element ready for animation + */ + createNavigationGrid(parentContainer, weekStart) { + // Use SAME method as initial load - respects workweek settings + const newGrid = this.createOptimizedGridContainer(weekStart, 'week'); + // Position new grid for animation - NO transform here, let Animation API handle it + newGrid.style.position = 'absolute'; + newGrid.style.top = '0'; + newGrid.style.left = '0'; + newGrid.style.width = '100%'; + newGrid.style.height = '100%'; + // Add to parent container + parentContainer.appendChild(newGrid); + return newGrid; + } +} +//# sourceMappingURL=GridRenderer.js.map \ No newline at end of file diff --git a/wwwroot/js/renderers/GridRenderer.js.map b/wwwroot/js/renderers/GridRenderer.js.map new file mode 100644 index 0000000..6e97412 --- /dev/null +++ b/wwwroot/js/renderers/GridRenderer.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GridRenderer.js","sourceRoot":"","sources":["../../../src/renderers/GridRenderer.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/renderers/GridStyleManager.d.ts b/wwwroot/js/renderers/GridStyleManager.d.ts new file mode 100644 index 0000000..9bad858 --- /dev/null +++ b/wwwroot/js/renderers/GridStyleManager.d.ts @@ -0,0 +1,24 @@ +import { ResourceCalendarData } from '../types/CalendarTypes'; +/** + * GridStyleManager - Manages CSS variables and styling for the grid + * Separated from GridManager to follow Single Responsibility Principle + */ +export declare class GridStyleManager { + constructor(); + /** + * Update all grid CSS variables + */ + updateGridStyles(resourceData?: ResourceCalendarData | null): void; + /** + * Set time-related CSS variables + */ + private setTimeVariables; + /** + * Calculate number of columns based on calendar type and view + */ + private calculateColumnCount; + /** + * Set column width based on fitToWidth setting + */ + private setColumnWidth; +} diff --git a/wwwroot/js/renderers/GridStyleManager.js b/wwwroot/js/renderers/GridStyleManager.js new file mode 100644 index 0000000..c7485da --- /dev/null +++ b/wwwroot/js/renderers/GridStyleManager.js @@ -0,0 +1,76 @@ +import { calendarConfig } from '../core/CalendarConfig'; +/** + * GridStyleManager - Manages CSS variables and styling for the grid + * Separated from GridManager to follow Single Responsibility Principle + */ +export class GridStyleManager { + constructor() { + } + /** + * Update all grid CSS variables + */ + updateGridStyles(resourceData = null) { + const root = document.documentElement; + const gridSettings = calendarConfig.getGridSettings(); + const calendar = document.querySelector('swp-calendar'); + const calendarType = calendarConfig.getCalendarMode(); + // Set CSS variables for time and grid measurements + this.setTimeVariables(root, gridSettings); + // Set column count based on calendar type + const columnCount = this.calculateColumnCount(calendarType, resourceData); + root.style.setProperty('--grid-columns', columnCount.toString()); + // Set column width based on fitToWidth setting + this.setColumnWidth(root, gridSettings); + // Set fitToWidth data attribute for CSS targeting + if (calendar) { + calendar.setAttribute('data-fit-to-width', gridSettings.fitToWidth.toString()); + } + } + /** + * Set time-related CSS variables + */ + setTimeVariables(root, gridSettings) { + root.style.setProperty('--hour-height', `${gridSettings.hourHeight}px`); + root.style.setProperty('--minute-height', `${gridSettings.hourHeight / 60}px`); + root.style.setProperty('--snap-interval', gridSettings.snapInterval.toString()); + root.style.setProperty('--day-start-hour', gridSettings.dayStartHour.toString()); + root.style.setProperty('--day-end-hour', gridSettings.dayEndHour.toString()); + root.style.setProperty('--work-start-hour', gridSettings.workStartHour.toString()); + root.style.setProperty('--work-end-hour', gridSettings.workEndHour.toString()); + } + /** + * Calculate number of columns based on calendar type and view + */ + calculateColumnCount(calendarType, resourceData) { + if (calendarType === 'resource' && resourceData) { + return resourceData.resources.length; + } + else if (calendarType === 'date') { + const dateSettings = calendarConfig.getDateViewSettings(); + const workWeekSettings = calendarConfig.getWorkWeekSettings(); + switch (dateSettings.period) { + case 'day': + return 1; + case 'week': + return workWeekSettings.totalDays; + case 'month': + return workWeekSettings.totalDays; // Use work week for month view too + default: + return workWeekSettings.totalDays; + } + } + return calendarConfig.getWorkWeekSettings().totalDays; // Default to work week + } + /** + * Set column width based on fitToWidth setting + */ + setColumnWidth(root, gridSettings) { + if (gridSettings.fitToWidth) { + root.style.setProperty('--day-column-min-width', '50px'); // Small min-width allows columns to fit available space + } + else { + root.style.setProperty('--day-column-min-width', '250px'); // Default min-width for horizontal scroll mode + } + } +} +//# sourceMappingURL=GridStyleManager.js.map \ No newline at end of file diff --git a/wwwroot/js/renderers/GridStyleManager.js.map b/wwwroot/js/renderers/GridStyleManager.js.map new file mode 100644 index 0000000..f3d9366 --- /dev/null +++ b/wwwroot/js/renderers/GridStyleManager.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GridStyleManager.js","sourceRoot":"","sources":["../../../src/renderers/GridStyleManager.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/renderers/HeaderRenderer.d.ts b/wwwroot/js/renderers/HeaderRenderer.d.ts new file mode 100644 index 0000000..50d0c7b --- /dev/null +++ b/wwwroot/js/renderers/HeaderRenderer.d.ts @@ -0,0 +1,29 @@ +import { CalendarConfig } from '../core/CalendarConfig'; +import { ResourceCalendarData } from '../types/CalendarTypes'; +/** + * Interface for header rendering strategies + */ +export interface HeaderRenderer { + render(calendarHeader: HTMLElement, context: HeaderRenderContext): void; +} +/** + * Context for header rendering + */ +export interface HeaderRenderContext { + currentWeek: Date; + config: CalendarConfig; + resourceData?: ResourceCalendarData | null; +} +/** + * Date-based header renderer (original functionality) + */ +export declare class DateHeaderRenderer implements HeaderRenderer { + private dateService; + render(calendarHeader: HTMLElement, context: HeaderRenderContext): void; +} +/** + * Resource-based header renderer + */ +export declare class ResourceHeaderRenderer implements HeaderRenderer { + render(calendarHeader: HTMLElement, context: HeaderRenderContext): void; +} diff --git a/wwwroot/js/renderers/HeaderRenderer.js b/wwwroot/js/renderers/HeaderRenderer.js new file mode 100644 index 0000000..0acbb43 --- /dev/null +++ b/wwwroot/js/renderers/HeaderRenderer.js @@ -0,0 +1,56 @@ +// Header rendering strategy interface and implementations +import { DateCalculator } from '../utils/DateCalculator'; +/** + * Date-based header renderer (original functionality) + */ +export class DateHeaderRenderer { + render(calendarHeader, context) { + const { currentWeek, config } = context; + // FIRST: Always create all-day container as part of standard header structure + const allDayContainer = document.createElement('swp-allday-container'); + calendarHeader.appendChild(allDayContainer); + // Initialize date calculator with config + DateCalculator.initialize(config); + this.dateCalculator = new DateCalculator(); + const dates = DateCalculator.getWorkWeekDates(currentWeek); + const weekDays = config.getDateViewSettings().weekDays; + const daysToShow = dates.slice(0, weekDays); + daysToShow.forEach((date, index) => { + const header = document.createElement('swp-day-header'); + if (DateCalculator.isToday(date)) { + header.dataset.today = 'true'; + } + const dayName = DateCalculator.getDayName(date, 'short'); + header.innerHTML = ` + ${dayName} + ${date.getDate()} + `; + header.dataset.date = DateCalculator.formatISODate(date); + calendarHeader.appendChild(header); + }); + } +} +/** + * Resource-based header renderer + */ +export class ResourceHeaderRenderer { + render(calendarHeader, context) { + const { resourceData } = context; + if (!resourceData) { + return; + } + resourceData.resources.forEach((resource) => { + const header = document.createElement('swp-resource-header'); + header.setAttribute('data-resource', resource.name); + header.setAttribute('data-employee-id', resource.employeeId); + header.innerHTML = ` + + ${resource.displayName} + + ${resource.displayName} + `; + calendarHeader.appendChild(header); + }); + } +} +//# sourceMappingURL=HeaderRenderer.js.map \ No newline at end of file diff --git a/wwwroot/js/renderers/HeaderRenderer.js.map b/wwwroot/js/renderers/HeaderRenderer.js.map new file mode 100644 index 0000000..a5af7c0 --- /dev/null +++ b/wwwroot/js/renderers/HeaderRenderer.js.map @@ -0,0 +1 @@ +{"version":3,"file":"HeaderRenderer.js","sourceRoot":"","sources":["../../../src/renderers/HeaderRenderer.ts"],"names":[],"mappings":"AAAA,0DAA0D;AAI1D,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAmBzD;;GAEG;AACH,MAAM,OAAO,kBAAkB;IAG7B,MAAM,CAAC,cAA2B,EAAE,OAA4B;QAC9D,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;QAExC,8EAA8E;QAC9E,MAAM,eAAe,GAAG,QAAQ,CAAC,aAAa,CAAC,sBAAsB,CAAC,CAAC;QACvE,cAAc,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC;QAE5C,yCAAyC;QACzC,cAAc,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QAClC,IAAI,CAAC,cAAc,GAAG,IAAI,cAAc,EAAE,CAAC;QAE3C,MAAM,KAAK,GAAG,cAAc,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAC;QAC3D,MAAM,QAAQ,GAAG,MAAM,CAAC,mBAAmB,EAAE,CAAC,QAAQ,CAAC;QACvD,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;QAE5C,UAAU,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;YACjC,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;YACxD,IAAI,cAAc,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;gBAChC,MAAc,CAAC,OAAO,CAAC,KAAK,GAAG,MAAM,CAAC;YACzC,CAAC;YAED,MAAM,OAAO,GAAG,cAAc,CAAC,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YAEzD,MAAM,CAAC,SAAS,GAAG;wBACD,OAAO;wBACP,IAAI,CAAC,OAAO,EAAE;OAC/B,CAAC;YACD,MAAc,CAAC,OAAO,CAAC,IAAI,GAAG,cAAc,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;YAElE,cAAc,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC;IACL,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,sBAAsB;IACjC,MAAM,CAAC,cAA2B,EAAE,OAA4B;QAC9D,MAAM,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC;QAEjC,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,OAAO;QACT,CAAC;QAED,YAAY,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE;YAC1C,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,qBAAqB,CAAC,CAAC;YAC7D,MAAM,CAAC,YAAY,CAAC,eAAe,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;YACpD,MAAM,CAAC,YAAY,CAAC,kBAAkB,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;YAE7D,MAAM,CAAC,SAAS,GAAG;;sBAEH,QAAQ,CAAC,SAAS,UAAU,QAAQ,CAAC,WAAW;;6BAEzC,QAAQ,CAAC,WAAW;OAC1C,CAAC;YAEF,cAAc,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC;IACL,CAAC;CACF"} \ No newline at end of file diff --git a/wwwroot/js/renderers/NavigationRenderer.d.ts b/wwwroot/js/renderers/NavigationRenderer.d.ts new file mode 100644 index 0000000..44b4b2d --- /dev/null +++ b/wwwroot/js/renderers/NavigationRenderer.d.ts @@ -0,0 +1,22 @@ +import { IEventBus } from '../types/CalendarTypes'; +import { EventRenderingService } from './EventRendererManager'; +/** + * NavigationRenderer - Handles DOM rendering for navigation containers + * Separated from NavigationManager to follow Single Responsibility Principle + */ +export declare class NavigationRenderer { + private eventBus; + constructor(eventBus: IEventBus, eventRenderer: EventRenderingService); + /** + * Setup event listeners for DOM updates + */ + private setupEventListeners; + private updateWeekInfoInDOM; + /** + * Apply filter state to pre-rendered grids + */ + applyFilterToPreRenderedGrids(filterState: { + active: boolean; + matchingIds: string[]; + }): void; +} diff --git a/wwwroot/js/renderers/NavigationRenderer.js b/wwwroot/js/renderers/NavigationRenderer.js new file mode 100644 index 0000000..8b0382e --- /dev/null +++ b/wwwroot/js/renderers/NavigationRenderer.js @@ -0,0 +1,68 @@ +import { CoreEvents } from '../constants/CoreEvents'; +/** + * NavigationRenderer - Handles DOM rendering for navigation containers + * Separated from NavigationManager to follow Single Responsibility Principle + */ +export class NavigationRenderer { + constructor(eventBus, eventRenderer) { + this.eventBus = eventBus; + this.setupEventListeners(); + } + /** + * Setup event listeners for DOM updates + */ + setupEventListeners() { + this.eventBus.on(CoreEvents.PERIOD_INFO_UPDATE, (event) => { + const customEvent = event; + const { weekNumber, dateRange } = customEvent.detail; + this.updateWeekInfoInDOM(weekNumber, dateRange); + }); + } + updateWeekInfoInDOM(weekNumber, dateRange) { + const weekNumberElement = document.querySelector('swp-week-number'); + const dateRangeElement = document.querySelector('swp-date-range'); + if (weekNumberElement) { + weekNumberElement.textContent = `Week ${weekNumber}`; + } + if (dateRangeElement) { + dateRangeElement.textContent = dateRange; + } + } + /** + * Apply filter state to pre-rendered grids + */ + applyFilterToPreRenderedGrids(filterState) { + // Find all grid containers (including pre-rendered ones) + const allGridContainers = document.querySelectorAll('swp-grid-container'); + allGridContainers.forEach(container => { + const eventsLayers = container.querySelectorAll('swp-events-layer'); + eventsLayers.forEach(layer => { + if (filterState.active) { + // Apply filter active state + layer.setAttribute('data-filter-active', 'true'); + // Mark matching events in this layer + const events = layer.querySelectorAll('swp-event'); + events.forEach(event => { + const eventId = event.getAttribute('data-event-id'); + if (eventId && filterState.matchingIds.includes(eventId)) { + event.setAttribute('data-matches', 'true'); + } + else { + event.removeAttribute('data-matches'); + } + }); + } + else { + // Remove filter state + layer.removeAttribute('data-filter-active'); + // Remove all match attributes + const events = layer.querySelectorAll('swp-event'); + events.forEach(event => { + event.removeAttribute('data-matches'); + }); + } + }); + }); + } +} +//# sourceMappingURL=NavigationRenderer.js.map \ No newline at end of file diff --git a/wwwroot/js/renderers/NavigationRenderer.js.map b/wwwroot/js/renderers/NavigationRenderer.js.map new file mode 100644 index 0000000..84751b8 --- /dev/null +++ b/wwwroot/js/renderers/NavigationRenderer.js.map @@ -0,0 +1 @@ +{"version":3,"file":"NavigationRenderer.js","sourceRoot":"","sources":["../../../src/renderers/NavigationRenderer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAGrD;;;GAGG;AAEH,MAAM,OAAO,kBAAkB;IAG7B,YAAY,QAAmB,EAAE,aAAoC;QACnE,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,mBAAmB,EAAE,CAAC;IAC7B,CAAC;IAID;;OAEG;IACK,mBAAmB;QACzB,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,UAAU,CAAC,kBAAkB,EAAE,CAAC,KAAY,EAAE,EAAE;YAC/D,MAAM,WAAW,GAAG,KAAoB,CAAC;YACzC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC;YACrD,IAAI,CAAC,mBAAmB,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;QAClD,CAAC,CAAC,CAAC;IACL,CAAC;IAGO,mBAAmB,CAAC,UAAkB,EAAE,SAAiB;QAE/D,MAAM,iBAAiB,GAAG,QAAQ,CAAC,aAAa,CAAC,iBAAiB,CAAC,CAAC;QACpE,MAAM,gBAAgB,GAAG,QAAQ,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;QAElE,IAAI,iBAAiB,EAAE,CAAC;YACtB,iBAAiB,CAAC,WAAW,GAAG,QAAQ,UAAU,EAAE,CAAC;QACvD,CAAC;QAED,IAAI,gBAAgB,EAAE,CAAC;YACrB,gBAAgB,CAAC,WAAW,GAAG,SAAS,CAAC;QAC3C,CAAC;IACH,CAAC;IAED;;OAEG;IACI,6BAA6B,CAAC,WAAuD;QAC1F,yDAAyD;QACzD,MAAM,iBAAiB,GAAG,QAAQ,CAAC,gBAAgB,CAAC,oBAAoB,CAAC,CAAC;QAE1E,iBAAiB,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE;YACpC,MAAM,YAAY,GAAG,SAAS,CAAC,gBAAgB,CAAC,kBAAkB,CAAC,CAAC;YAEpE,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;gBAC3B,IAAI,WAAW,CAAC,MAAM,EAAE,CAAC;oBACvB,4BAA4B;oBAC5B,KAAK,CAAC,YAAY,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC;oBAEjD,qCAAqC;oBACrC,MAAM,MAAM,GAAG,KAAK,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAC;oBACnD,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;wBACrB,MAAM,OAAO,GAAG,KAAK,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC;wBACpD,IAAI,OAAO,IAAI,WAAW,CAAC,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;4BACzD,KAAK,CAAC,YAAY,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;wBAC7C,CAAC;6BAAM,CAAC;4BACN,KAAK,CAAC,eAAe,CAAC,cAAc,CAAC,CAAC;wBACxC,CAAC;oBACH,CAAC,CAAC,CAAC;gBACL,CAAC;qBAAM,CAAC;oBACN,sBAAsB;oBACtB,KAAK,CAAC,eAAe,CAAC,oBAAoB,CAAC,CAAC;oBAE5C,8BAA8B;oBAC9B,MAAM,MAAM,GAAG,KAAK,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAC;oBACnD,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;wBACrB,KAAK,CAAC,eAAe,CAAC,cAAc,CAAC,CAAC;oBACxC,CAAC,CAAC,CAAC;gBACL,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;CAEF"} \ No newline at end of file diff --git a/wwwroot/js/renderers/WeekInfoRenderer.d.ts b/wwwroot/js/renderers/WeekInfoRenderer.d.ts new file mode 100644 index 0000000..e244867 --- /dev/null +++ b/wwwroot/js/renderers/WeekInfoRenderer.d.ts @@ -0,0 +1,26 @@ +import { IEventBus } from '../types/CalendarTypes'; +import { EventRenderingService } from './EventRendererManager'; +import { DateService } from '../utils/DateService'; +/** + * WeekInfoRenderer - Handles DOM rendering for week info display + * Updates swp-week-number and swp-date-range elements + * + * Renamed from NavigationRenderer to better reflect its actual responsibility + */ +export declare class WeekInfoRenderer { + private eventBus; + private dateService; + constructor(eventBus: IEventBus, eventRenderer: EventRenderingService, dateService: DateService); + /** + * Setup event listeners for DOM updates + */ + private setupEventListeners; + private updateWeekInfoInDOM; + /** + * Apply filter state to pre-rendered grids + */ + applyFilterToPreRenderedGrids(filterState: { + active: boolean; + matchingIds: string[]; + }): void; +} diff --git a/wwwroot/js/renderers/WeekInfoRenderer.js b/wwwroot/js/renderers/WeekInfoRenderer.js new file mode 100644 index 0000000..cb12aa4 --- /dev/null +++ b/wwwroot/js/renderers/WeekInfoRenderer.js @@ -0,0 +1,75 @@ +import { CoreEvents } from '../constants/CoreEvents'; +/** + * WeekInfoRenderer - Handles DOM rendering for week info display + * Updates swp-week-number and swp-date-range elements + * + * Renamed from NavigationRenderer to better reflect its actual responsibility + */ +export class WeekInfoRenderer { + constructor(eventBus, eventRenderer, dateService) { + this.eventBus = eventBus; + this.dateService = dateService; + this.setupEventListeners(); + } + /** + * Setup event listeners for DOM updates + */ + setupEventListeners() { + this.eventBus.on(CoreEvents.NAVIGATION_COMPLETED, (event) => { + const customEvent = event; + const { newDate } = customEvent.detail; + // Calculate week number and date range from the new date + const weekNumber = this.dateService.getWeekNumber(newDate); + const weekEnd = this.dateService.addDays(newDate, 6); + const dateRange = this.dateService.formatDateRange(newDate, weekEnd); + this.updateWeekInfoInDOM(weekNumber, dateRange); + }); + } + updateWeekInfoInDOM(weekNumber, dateRange) { + const weekNumberElement = document.querySelector('swp-week-number'); + const dateRangeElement = document.querySelector('swp-date-range'); + if (weekNumberElement) { + weekNumberElement.textContent = `Week ${weekNumber}`; + } + if (dateRangeElement) { + dateRangeElement.textContent = dateRange; + } + } + /** + * Apply filter state to pre-rendered grids + */ + applyFilterToPreRenderedGrids(filterState) { + // Find all grid containers (including pre-rendered ones) + const allGridContainers = document.querySelectorAll('swp-grid-container'); + allGridContainers.forEach(container => { + const eventsLayers = container.querySelectorAll('swp-events-layer'); + eventsLayers.forEach(layer => { + if (filterState.active) { + // Apply filter active state + layer.setAttribute('data-filter-active', 'true'); + // Mark matching events in this layer + const events = layer.querySelectorAll('swp-event'); + events.forEach(event => { + const eventId = event.getAttribute('data-event-id'); + if (eventId && filterState.matchingIds.includes(eventId)) { + event.setAttribute('data-matches', 'true'); + } + else { + event.removeAttribute('data-matches'); + } + }); + } + else { + // Remove filter state + layer.removeAttribute('data-filter-active'); + // Remove all match attributes + const events = layer.querySelectorAll('swp-event'); + events.forEach(event => { + event.removeAttribute('data-matches'); + }); + } + }); + }); + } +} +//# sourceMappingURL=WeekInfoRenderer.js.map \ No newline at end of file diff --git a/wwwroot/js/renderers/WeekInfoRenderer.js.map b/wwwroot/js/renderers/WeekInfoRenderer.js.map new file mode 100644 index 0000000..d83cb61 --- /dev/null +++ b/wwwroot/js/renderers/WeekInfoRenderer.js.map @@ -0,0 +1 @@ +{"version":3,"file":"WeekInfoRenderer.js","sourceRoot":"","sources":["../../../src/renderers/WeekInfoRenderer.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/repositories/ApiEventRepository.d.ts b/wwwroot/js/repositories/ApiEventRepository.d.ts new file mode 100644 index 0000000..d7e087d --- /dev/null +++ b/wwwroot/js/repositories/ApiEventRepository.d.ts @@ -0,0 +1,39 @@ +import { ICalendarEvent } from '../types/CalendarTypes'; +import { Configuration } from '../configurations/CalendarConfig'; +/** + * ApiEventRepository + * Handles communication with backend API + * + * Used by SyncManager to send queued operations to the server + * NOT used directly by EventManager (which uses IndexedDBEventRepository) + * + * Future enhancements: + * - SignalR real-time updates + * - Conflict resolution + * - Batch operations + */ +export declare class ApiEventRepository { + private apiEndpoint; + constructor(config: Configuration); + /** + * Send create operation to API + */ + sendCreate(event: ICalendarEvent): Promise; + /** + * Send update operation to API + */ + sendUpdate(id: string, updates: Partial): Promise; + /** + * Send delete operation to API + */ + sendDelete(id: string): Promise; + /** + * Fetch all events from API + */ + fetchAll(): Promise; + /** + * Initialize SignalR connection + * Placeholder for future implementation + */ + initializeSignalR(): Promise; +} diff --git a/wwwroot/js/repositories/ApiEventRepository.js b/wwwroot/js/repositories/ApiEventRepository.js new file mode 100644 index 0000000..b732f80 --- /dev/null +++ b/wwwroot/js/repositories/ApiEventRepository.js @@ -0,0 +1,115 @@ +/** + * ApiEventRepository + * Handles communication with backend API + * + * Used by SyncManager to send queued operations to the server + * NOT used directly by EventManager (which uses IndexedDBEventRepository) + * + * Future enhancements: + * - SignalR real-time updates + * - Conflict resolution + * - Batch operations + */ +export class ApiEventRepository { + constructor(config) { + this.apiEndpoint = config.apiEndpoint; + } + /** + * Send create operation to API + */ + async sendCreate(event) { + // TODO: Implement API call + // const response = await fetch(`${this.apiEndpoint}/events`, { + // method: 'POST', + // headers: { 'Content-Type': 'application/json' }, + // body: JSON.stringify(event) + // }); + // + // if (!response.ok) { + // throw new Error(`API create failed: ${response.statusText}`); + // } + // + // return await response.json(); + throw new Error('ApiEventRepository.sendCreate not implemented yet'); + } + /** + * Send update operation to API + */ + async sendUpdate(id, updates) { + // TODO: Implement API call + // const response = await fetch(`${this.apiEndpoint}/events/${id}`, { + // method: 'PATCH', + // headers: { 'Content-Type': 'application/json' }, + // body: JSON.stringify(updates) + // }); + // + // if (!response.ok) { + // throw new Error(`API update failed: ${response.statusText}`); + // } + // + // return await response.json(); + throw new Error('ApiEventRepository.sendUpdate not implemented yet'); + } + /** + * Send delete operation to API + */ + async sendDelete(id) { + // TODO: Implement API call + // const response = await fetch(`${this.apiEndpoint}/events/${id}`, { + // method: 'DELETE' + // }); + // + // if (!response.ok) { + // throw new Error(`API delete failed: ${response.statusText}`); + // } + throw new Error('ApiEventRepository.sendDelete not implemented yet'); + } + /** + * Fetch all events from API + */ + async fetchAll() { + // TODO: Implement API call + // const response = await fetch(`${this.apiEndpoint}/events`); + // + // if (!response.ok) { + // throw new Error(`API fetch failed: ${response.statusText}`); + // } + // + // return await response.json(); + throw new Error('ApiEventRepository.fetchAll not implemented yet'); + } + // ======================================== + // Future: SignalR Integration + // ======================================== + /** + * Initialize SignalR connection + * Placeholder for future implementation + */ + async initializeSignalR() { + // TODO: Setup SignalR connection + // - Connect to hub + // - Register event handlers + // - Handle reconnection + // + // Example: + // const connection = new signalR.HubConnectionBuilder() + // .withUrl(`${this.apiEndpoint}/hubs/calendar`) + // .build(); + // + // connection.on('EventCreated', (event: ICalendarEvent) => { + // // Handle remote create + // }); + // + // connection.on('EventUpdated', (event: ICalendarEvent) => { + // // Handle remote update + // }); + // + // connection.on('EventDeleted', (eventId: string) => { + // // Handle remote delete + // }); + // + // await connection.start(); + throw new Error('SignalR not implemented yet'); + } +} +//# sourceMappingURL=ApiEventRepository.js.map \ No newline at end of file diff --git a/wwwroot/js/repositories/ApiEventRepository.js.map b/wwwroot/js/repositories/ApiEventRepository.js.map new file mode 100644 index 0000000..cf892a1 --- /dev/null +++ b/wwwroot/js/repositories/ApiEventRepository.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ApiEventRepository.js","sourceRoot":"","sources":["../../../src/repositories/ApiEventRepository.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,kBAAkB;IAG7B,YAAY,MAAqB;QAC/B,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC;IACxC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU,CAAC,KAAqB;QACpC,2BAA2B;QAC3B,+DAA+D;QAC/D,oBAAoB;QACpB,qDAAqD;QACrD,gCAAgC;QAChC,MAAM;QACN,EAAE;QACF,sBAAsB;QACtB,kEAAkE;QAClE,IAAI;QACJ,EAAE;QACF,gCAAgC;QAEhC,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACvE,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU,CAAC,EAAU,EAAE,OAAgC;QAC3D,2BAA2B;QAC3B,qEAAqE;QACrE,qBAAqB;QACrB,qDAAqD;QACrD,kCAAkC;QAClC,MAAM;QACN,EAAE;QACF,sBAAsB;QACtB,kEAAkE;QAClE,IAAI;QACJ,EAAE;QACF,gCAAgC;QAEhC,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACvE,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU,CAAC,EAAU;QACzB,2BAA2B;QAC3B,qEAAqE;QACrE,qBAAqB;QACrB,MAAM;QACN,EAAE;QACF,sBAAsB;QACtB,kEAAkE;QAClE,IAAI;QAEJ,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACvE,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,QAAQ;QACZ,2BAA2B;QAC3B,8DAA8D;QAC9D,EAAE;QACF,sBAAsB;QACtB,iEAAiE;QACjE,IAAI;QACJ,EAAE;QACF,gCAAgC;QAEhC,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACrE,CAAC;IAED,2CAA2C;IAC3C,8BAA8B;IAC9B,2CAA2C;IAE3C;;;OAGG;IACH,KAAK,CAAC,iBAAiB;QACrB,iCAAiC;QACjC,mBAAmB;QACnB,4BAA4B;QAC5B,wBAAwB;QACxB,EAAE;QACF,WAAW;QACX,wDAAwD;QACxD,kDAAkD;QAClD,cAAc;QACd,EAAE;QACF,6DAA6D;QAC7D,4BAA4B;QAC5B,MAAM;QACN,EAAE;QACF,6DAA6D;QAC7D,4BAA4B;QAC5B,MAAM;QACN,EAAE;QACF,uDAAuD;QACvD,4BAA4B;QAC5B,MAAM;QACN,EAAE;QACF,4BAA4B;QAE5B,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACjD,CAAC;CACF"} \ No newline at end of file diff --git a/wwwroot/js/repositories/IEventRepository.d.ts b/wwwroot/js/repositories/IEventRepository.d.ts new file mode 100644 index 0000000..2bd6a5e --- /dev/null +++ b/wwwroot/js/repositories/IEventRepository.d.ts @@ -0,0 +1,51 @@ +import { ICalendarEvent } from '../types/CalendarTypes'; +/** + * Update source type + * - 'local': Changes made by the user locally (needs sync) + * - 'remote': Changes from API/SignalR (already synced) + */ +export type UpdateSource = 'local' | 'remote'; +/** + * IEventRepository - Interface for event data access + * + * Abstracts the data source for calendar events, allowing easy switching + * between IndexedDB, REST API, GraphQL, or other data sources. + * + * Implementations: + * - IndexedDBEventRepository: Local storage with offline support + * - MockEventRepository: (Legacy) Loads from local JSON file + * - ApiEventRepository: (Future) Loads from backend API + */ +export interface IEventRepository { + /** + * Load all calendar events from the data source + * @returns Promise resolving to array of ICalendarEvent objects + * @throws Error if loading fails + */ + loadEvents(): Promise; + /** + * Create a new event + * @param event - Event to create (without ID, will be generated) + * @param source - Source of the update ('local' or 'remote') + * @returns Promise resolving to the created event with generated ID + * @throws Error if creation fails + */ + createEvent(event: Omit, source?: UpdateSource): Promise; + /** + * Update an existing event + * @param id - ID of the event to update + * @param updates - Partial event data to update + * @param source - Source of the update ('local' or 'remote') + * @returns Promise resolving to the updated event + * @throws Error if update fails or event not found + */ + updateEvent(id: string, updates: Partial, source?: UpdateSource): Promise; + /** + * Delete an event + * @param id - ID of the event to delete + * @param source - Source of the update ('local' or 'remote') + * @returns Promise resolving when deletion is complete + * @throws Error if deletion fails or event not found + */ + deleteEvent(id: string, source?: UpdateSource): Promise; +} diff --git a/wwwroot/js/repositories/IEventRepository.js b/wwwroot/js/repositories/IEventRepository.js new file mode 100644 index 0000000..fd60757 --- /dev/null +++ b/wwwroot/js/repositories/IEventRepository.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=IEventRepository.js.map \ No newline at end of file diff --git a/wwwroot/js/repositories/IEventRepository.js.map b/wwwroot/js/repositories/IEventRepository.js.map new file mode 100644 index 0000000..fc02973 --- /dev/null +++ b/wwwroot/js/repositories/IEventRepository.js.map @@ -0,0 +1 @@ +{"version":3,"file":"IEventRepository.js","sourceRoot":"","sources":["../../../src/repositories/IEventRepository.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/wwwroot/js/repositories/IndexedDBEventRepository.d.ts b/wwwroot/js/repositories/IndexedDBEventRepository.d.ts new file mode 100644 index 0000000..575264a --- /dev/null +++ b/wwwroot/js/repositories/IndexedDBEventRepository.d.ts @@ -0,0 +1,47 @@ +import { ICalendarEvent } from '../types/CalendarTypes'; +import { IEventRepository, UpdateSource } from './IEventRepository'; +import { IndexedDBService } from '../storage/IndexedDBService'; +import { OperationQueue } from '../storage/OperationQueue'; +/** + * IndexedDBEventRepository + * Offline-first repository using IndexedDB as single source of truth + * + * All CRUD operations: + * - Save to IndexedDB immediately (always succeeds) + * - Add to sync queue if source is 'local' + * - Background SyncManager processes queue to sync with API + */ +export declare class IndexedDBEventRepository implements IEventRepository { + private indexedDB; + private queue; + constructor(indexedDB: IndexedDBService, queue: OperationQueue); + /** + * Load all events from IndexedDB + * Ensures IndexedDB is initialized and seeded on first call + */ + loadEvents(): Promise; + /** + * Create a new event + * - Generates ID + * - Saves to IndexedDB + * - Adds to queue if local (needs sync) + */ + createEvent(event: Omit, source?: UpdateSource): Promise; + /** + * Update an existing event + * - Updates in IndexedDB + * - Adds to queue if local (needs sync) + */ + updateEvent(id: string, updates: Partial, source?: UpdateSource): Promise; + /** + * Delete an event + * - Removes from IndexedDB + * - Adds to queue if local (needs sync) + */ + deleteEvent(id: string, source?: UpdateSource): Promise; + /** + * Generate unique event ID + * Format: {timestamp}-{random} + */ + private generateEventId; +} diff --git a/wwwroot/js/repositories/IndexedDBEventRepository.js b/wwwroot/js/repositories/IndexedDBEventRepository.js new file mode 100644 index 0000000..c09245e --- /dev/null +++ b/wwwroot/js/repositories/IndexedDBEventRepository.js @@ -0,0 +1,127 @@ +/** + * IndexedDBEventRepository + * Offline-first repository using IndexedDB as single source of truth + * + * All CRUD operations: + * - Save to IndexedDB immediately (always succeeds) + * - Add to sync queue if source is 'local' + * - Background SyncManager processes queue to sync with API + */ +export class IndexedDBEventRepository { + constructor(indexedDB, queue) { + this.indexedDB = indexedDB; + this.queue = queue; + } + /** + * Load all events from IndexedDB + * Ensures IndexedDB is initialized and seeded on first call + */ + async loadEvents() { + // Lazy initialization on first data load + if (!this.indexedDB.isInitialized()) { + await this.indexedDB.initialize(); + await this.indexedDB.seedIfEmpty(); + } + return await this.indexedDB.getAllEvents(); + } + /** + * Create a new event + * - Generates ID + * - Saves to IndexedDB + * - Adds to queue if local (needs sync) + */ + async createEvent(event, source = 'local') { + // Generate unique ID + const id = this.generateEventId(); + // Determine sync status based on source + const syncStatus = source === 'local' ? 'pending' : 'synced'; + // Create full event object + const newEvent = { + ...event, + id, + syncStatus + }; + // Save to IndexedDB + await this.indexedDB.saveEvent(newEvent); + // If local change, add to sync queue + if (source === 'local') { + await this.queue.enqueue({ + type: 'create', + eventId: id, + data: newEvent, + timestamp: Date.now(), + retryCount: 0 + }); + } + return newEvent; + } + /** + * Update an existing event + * - Updates in IndexedDB + * - Adds to queue if local (needs sync) + */ + async updateEvent(id, updates, source = 'local') { + // Get existing event + const existingEvent = await this.indexedDB.getEvent(id); + if (!existingEvent) { + throw new Error(`Event with ID ${id} not found`); + } + // Determine sync status based on source + const syncStatus = source === 'local' ? 'pending' : 'synced'; + // Merge updates + const updatedEvent = { + ...existingEvent, + ...updates, + id, // Ensure ID doesn't change + syncStatus + }; + // Save to IndexedDB + await this.indexedDB.saveEvent(updatedEvent); + // If local change, add to sync queue + if (source === 'local') { + await this.queue.enqueue({ + type: 'update', + eventId: id, + data: updates, + timestamp: Date.now(), + retryCount: 0 + }); + } + return updatedEvent; + } + /** + * Delete an event + * - Removes from IndexedDB + * - Adds to queue if local (needs sync) + */ + async deleteEvent(id, source = 'local') { + // Check if event exists + const existingEvent = await this.indexedDB.getEvent(id); + if (!existingEvent) { + throw new Error(`Event with ID ${id} not found`); + } + // If local change, add to sync queue BEFORE deleting + // (so we can send the delete operation to API later) + if (source === 'local') { + await this.queue.enqueue({ + type: 'delete', + eventId: id, + data: {}, // No data needed for delete + timestamp: Date.now(), + retryCount: 0 + }); + } + // Delete from IndexedDB + await this.indexedDB.deleteEvent(id); + } + /** + * Generate unique event ID + * Format: {timestamp}-{random} + */ + generateEventId() { + const timestamp = Date.now(); + const random = Math.random().toString(36).substring(2, 9); + return `${timestamp}-${random}`; + } +} +//# sourceMappingURL=IndexedDBEventRepository.js.map \ No newline at end of file diff --git a/wwwroot/js/repositories/IndexedDBEventRepository.js.map b/wwwroot/js/repositories/IndexedDBEventRepository.js.map new file mode 100644 index 0000000..82835e7 --- /dev/null +++ b/wwwroot/js/repositories/IndexedDBEventRepository.js.map @@ -0,0 +1 @@ +{"version":3,"file":"IndexedDBEventRepository.js","sourceRoot":"","sources":["../../../src/repositories/IndexedDBEventRepository.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/repositories/MockEventRepository.d.ts b/wwwroot/js/repositories/MockEventRepository.d.ts new file mode 100644 index 0000000..3e3d5cd --- /dev/null +++ b/wwwroot/js/repositories/MockEventRepository.d.ts @@ -0,0 +1,33 @@ +import { ICalendarEvent } from '../types/CalendarTypes'; +import { IEventRepository, UpdateSource } from './IEventRepository'; +/** + * MockEventRepository - Loads event data from local JSON file (LEGACY) + * + * This repository implementation fetches mock event data from a static JSON file. + * DEPRECATED: Use IndexedDBEventRepository for offline-first functionality. + * + * Data Source: data/mock-events.json + * + * NOTE: Create/Update/Delete operations are not supported - throws errors. + * This is intentional to encourage migration to IndexedDBEventRepository. + */ +export declare class MockEventRepository implements IEventRepository { + private readonly dataUrl; + loadEvents(): Promise; + /** + * NOT SUPPORTED - MockEventRepository is read-only + * Use IndexedDBEventRepository instead + */ + createEvent(event: Omit, source?: UpdateSource): Promise; + /** + * NOT SUPPORTED - MockEventRepository is read-only + * Use IndexedDBEventRepository instead + */ + updateEvent(id: string, updates: Partial, source?: UpdateSource): Promise; + /** + * NOT SUPPORTED - MockEventRepository is read-only + * Use IndexedDBEventRepository instead + */ + deleteEvent(id: string, source?: UpdateSource): Promise; + private processCalendarData; +} diff --git a/wwwroot/js/repositories/MockEventRepository.js b/wwwroot/js/repositories/MockEventRepository.js new file mode 100644 index 0000000..e43f8cb --- /dev/null +++ b/wwwroot/js/repositories/MockEventRepository.js @@ -0,0 +1,62 @@ +/** + * MockEventRepository - Loads event data from local JSON file (LEGACY) + * + * This repository implementation fetches mock event data from a static JSON file. + * DEPRECATED: Use IndexedDBEventRepository for offline-first functionality. + * + * Data Source: data/mock-events.json + * + * NOTE: Create/Update/Delete operations are not supported - throws errors. + * This is intentional to encourage migration to IndexedDBEventRepository. + */ +export class MockEventRepository { + constructor() { + this.dataUrl = 'data/mock-events.json'; + } + async loadEvents() { + try { + const response = await fetch(this.dataUrl); + if (!response.ok) { + throw new Error(`Failed to load mock events: ${response.status} ${response.statusText}`); + } + const rawData = await response.json(); + return this.processCalendarData(rawData); + } + catch (error) { + console.error('Failed to load event data:', error); + throw error; + } + } + /** + * NOT SUPPORTED - MockEventRepository is read-only + * Use IndexedDBEventRepository instead + */ + async createEvent(event, source) { + throw new Error('MockEventRepository does not support createEvent. Use IndexedDBEventRepository instead.'); + } + /** + * NOT SUPPORTED - MockEventRepository is read-only + * Use IndexedDBEventRepository instead + */ + async updateEvent(id, updates, source) { + throw new Error('MockEventRepository does not support updateEvent. Use IndexedDBEventRepository instead.'); + } + /** + * NOT SUPPORTED - MockEventRepository is read-only + * Use IndexedDBEventRepository instead + */ + async deleteEvent(id, source) { + throw new Error('MockEventRepository does not support deleteEvent. Use IndexedDBEventRepository instead.'); + } + processCalendarData(data) { + return data.map((event) => ({ + ...event, + start: new Date(event.start), + end: new Date(event.end), + type: event.type, + allDay: event.allDay || false, + syncStatus: 'synced' + })); + } +} +//# sourceMappingURL=MockEventRepository.js.map \ No newline at end of file diff --git a/wwwroot/js/repositories/MockEventRepository.js.map b/wwwroot/js/repositories/MockEventRepository.js.map new file mode 100644 index 0000000..f2909a6 --- /dev/null +++ b/wwwroot/js/repositories/MockEventRepository.js.map @@ -0,0 +1 @@ +{"version":3,"file":"MockEventRepository.js","sourceRoot":"","sources":["../../../src/repositories/MockEventRepository.ts"],"names":[],"mappings":"AAcA;;;;;;;;;;GAUG;AACH,MAAM,OAAO,mBAAmB;IAAhC;QACmB,YAAO,GAAG,uBAAuB,CAAC;IAqDrD,CAAC;IAnDQ,KAAK,CAAC,UAAU;QACrB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAE3C,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,IAAI,KAAK,CAAC,+BAA+B,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;YAC3F,CAAC;YAED,MAAM,OAAO,GAAmB,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YAEtD,OAAO,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC,CAAC;QAC3C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,4BAA4B,EAAE,KAAK,CAAC,CAAC;YACnD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,WAAW,CAAC,KAAiC,EAAE,MAAqB;QAC/E,MAAM,IAAI,KAAK,CAAC,yFAAyF,CAAC,CAAC;IAC7G,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,WAAW,CAAC,EAAU,EAAE,OAAgC,EAAE,MAAqB;QAC1F,MAAM,IAAI,KAAK,CAAC,yFAAyF,CAAC,CAAC;IAC7G,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,WAAW,CAAC,EAAU,EAAE,MAAqB;QACxD,MAAM,IAAI,KAAK,CAAC,yFAAyF,CAAC,CAAC;IAC7G,CAAC;IAEO,mBAAmB,CAAC,IAAoB;QAC9C,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,EAAkB,EAAE,CAAC,CAAC;YAC1C,GAAG,KAAK;YACR,KAAK,EAAE,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;YAC5B,GAAG,EAAE,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;YACxB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,KAAK;YAC7B,UAAU,EAAE,QAAiB;SAC9B,CAAC,CAAC,CAAC;IACN,CAAC;CACF"} \ No newline at end of file diff --git a/wwwroot/js/storage/IndexedDBService.d.ts b/wwwroot/js/storage/IndexedDBService.d.ts new file mode 100644 index 0000000..d40c72a --- /dev/null +++ b/wwwroot/js/storage/IndexedDBService.d.ts @@ -0,0 +1,97 @@ +import { ICalendarEvent } from '../types/CalendarTypes'; +/** + * Operation for the sync queue + */ +export interface IQueueOperation { + id: string; + type: 'create' | 'update' | 'delete'; + eventId: string; + data: Partial | ICalendarEvent; + timestamp: number; + retryCount: number; +} +/** + * IndexedDB Service for Calendar App + * Handles local storage of events and sync queue + */ +export declare class IndexedDBService { + private static readonly DB_NAME; + private static readonly DB_VERSION; + private static readonly EVENTS_STORE; + private static readonly QUEUE_STORE; + private static readonly SYNC_STATE_STORE; + private db; + private initialized; + /** + * Initialize and open the database + */ + initialize(): Promise; + /** + * Check if database is initialized + */ + isInitialized(): boolean; + /** + * Ensure database is initialized + */ + private ensureDB; + /** + * Get a single event by ID + */ + getEvent(id: string): Promise; + /** + * Get all events + */ + getAllEvents(): Promise; + /** + * Save an event (create or update) + */ + saveEvent(event: ICalendarEvent): Promise; + /** + * Delete an event + */ + deleteEvent(id: string): Promise; + /** + * Add operation to queue + */ + addToQueue(operation: Omit): Promise; + /** + * Get all queue operations (sorted by timestamp) + */ + getQueue(): Promise; + /** + * Remove operation from queue + */ + removeFromQueue(id: string): Promise; + /** + * Clear entire queue + */ + clearQueue(): Promise; + /** + * Save sync state value + */ + setSyncState(key: string, value: any): Promise; + /** + * Get sync state value + */ + getSyncState(key: string): Promise; + /** + * Serialize event for IndexedDB storage (convert Dates to ISO strings) + */ + private serializeEvent; + /** + * Deserialize event from IndexedDB (convert ISO strings to Dates) + */ + private deserializeEvent; + /** + * Close database connection + */ + close(): void; + /** + * Delete entire database (for testing/reset) + */ + static deleteDatabase(): Promise; + /** + * Seed IndexedDB with mock data if empty + */ + seedIfEmpty(mockDataUrl?: string): Promise; +} diff --git a/wwwroot/js/storage/IndexedDBService.js b/wwwroot/js/storage/IndexedDBService.js new file mode 100644 index 0000000..0f07270 --- /dev/null +++ b/wwwroot/js/storage/IndexedDBService.js @@ -0,0 +1,340 @@ +/** + * IndexedDB Service for Calendar App + * Handles local storage of events and sync queue + */ +export class IndexedDBService { + constructor() { + this.db = null; + this.initialized = false; + } + /** + * Initialize and open the database + */ + async initialize() { + return new Promise((resolve, reject) => { + const request = indexedDB.open(IndexedDBService.DB_NAME, IndexedDBService.DB_VERSION); + request.onerror = () => { + reject(new Error(`Failed to open IndexedDB: ${request.error}`)); + }; + request.onsuccess = () => { + this.db = request.result; + this.initialized = true; + resolve(); + }; + request.onupgradeneeded = (event) => { + const db = event.target.result; + // Create events store + if (!db.objectStoreNames.contains(IndexedDBService.EVENTS_STORE)) { + const eventsStore = db.createObjectStore(IndexedDBService.EVENTS_STORE, { keyPath: 'id' }); + eventsStore.createIndex('start', 'start', { unique: false }); + eventsStore.createIndex('end', 'end', { unique: false }); + eventsStore.createIndex('syncStatus', 'syncStatus', { unique: false }); + } + // Create operation queue store + if (!db.objectStoreNames.contains(IndexedDBService.QUEUE_STORE)) { + const queueStore = db.createObjectStore(IndexedDBService.QUEUE_STORE, { keyPath: 'id' }); + queueStore.createIndex('timestamp', 'timestamp', { unique: false }); + } + // Create sync state store + if (!db.objectStoreNames.contains(IndexedDBService.SYNC_STATE_STORE)) { + db.createObjectStore(IndexedDBService.SYNC_STATE_STORE, { keyPath: 'key' }); + } + }; + }); + } + /** + * Check if database is initialized + */ + isInitialized() { + return this.initialized; + } + /** + * Ensure database is initialized + */ + ensureDB() { + if (!this.db) { + throw new Error('IndexedDB not initialized. Call initialize() first.'); + } + return this.db; + } + // ======================================== + // Event CRUD Operations + // ======================================== + /** + * Get a single event by ID + */ + async getEvent(id) { + const db = this.ensureDB(); + return new Promise((resolve, reject) => { + const transaction = db.transaction([IndexedDBService.EVENTS_STORE], 'readonly'); + const store = transaction.objectStore(IndexedDBService.EVENTS_STORE); + const request = store.get(id); + request.onsuccess = () => { + const event = request.result; + resolve(event ? this.deserializeEvent(event) : null); + }; + request.onerror = () => { + reject(new Error(`Failed to get event ${id}: ${request.error}`)); + }; + }); + } + /** + * Get all events + */ + async getAllEvents() { + const db = this.ensureDB(); + return new Promise((resolve, reject) => { + const transaction = db.transaction([IndexedDBService.EVENTS_STORE], 'readonly'); + const store = transaction.objectStore(IndexedDBService.EVENTS_STORE); + const request = store.getAll(); + request.onsuccess = () => { + const events = request.result; + resolve(events.map(e => this.deserializeEvent(e))); + }; + request.onerror = () => { + reject(new Error(`Failed to get all events: ${request.error}`)); + }; + }); + } + /** + * Save an event (create or update) + */ + async saveEvent(event) { + const db = this.ensureDB(); + const serialized = this.serializeEvent(event); + return new Promise((resolve, reject) => { + const transaction = db.transaction([IndexedDBService.EVENTS_STORE], 'readwrite'); + const store = transaction.objectStore(IndexedDBService.EVENTS_STORE); + const request = store.put(serialized); + request.onsuccess = () => { + resolve(); + }; + request.onerror = () => { + reject(new Error(`Failed to save event ${event.id}: ${request.error}`)); + }; + }); + } + /** + * Delete an event + */ + async deleteEvent(id) { + const db = this.ensureDB(); + return new Promise((resolve, reject) => { + const transaction = db.transaction([IndexedDBService.EVENTS_STORE], 'readwrite'); + const store = transaction.objectStore(IndexedDBService.EVENTS_STORE); + const request = store.delete(id); + request.onsuccess = () => { + resolve(); + }; + request.onerror = () => { + reject(new Error(`Failed to delete event ${id}: ${request.error}`)); + }; + }); + } + // ======================================== + // Queue Operations + // ======================================== + /** + * Add operation to queue + */ + async addToQueue(operation) { + const db = this.ensureDB(); + const queueItem = { + ...operation, + id: `${operation.type}-${operation.eventId}-${Date.now()}` + }; + return new Promise((resolve, reject) => { + const transaction = db.transaction([IndexedDBService.QUEUE_STORE], 'readwrite'); + const store = transaction.objectStore(IndexedDBService.QUEUE_STORE); + const request = store.put(queueItem); + request.onsuccess = () => { + resolve(); + }; + request.onerror = () => { + reject(new Error(`Failed to add to queue: ${request.error}`)); + }; + }); + } + /** + * Get all queue operations (sorted by timestamp) + */ + async getQueue() { + const db = this.ensureDB(); + return new Promise((resolve, reject) => { + const transaction = db.transaction([IndexedDBService.QUEUE_STORE], 'readonly'); + const store = transaction.objectStore(IndexedDBService.QUEUE_STORE); + const index = store.index('timestamp'); + const request = index.getAll(); + request.onsuccess = () => { + resolve(request.result); + }; + request.onerror = () => { + reject(new Error(`Failed to get queue: ${request.error}`)); + }; + }); + } + /** + * Remove operation from queue + */ + async removeFromQueue(id) { + const db = this.ensureDB(); + return new Promise((resolve, reject) => { + const transaction = db.transaction([IndexedDBService.QUEUE_STORE], 'readwrite'); + const store = transaction.objectStore(IndexedDBService.QUEUE_STORE); + const request = store.delete(id); + request.onsuccess = () => { + resolve(); + }; + request.onerror = () => { + reject(new Error(`Failed to remove from queue: ${request.error}`)); + }; + }); + } + /** + * Clear entire queue + */ + async clearQueue() { + const db = this.ensureDB(); + return new Promise((resolve, reject) => { + const transaction = db.transaction([IndexedDBService.QUEUE_STORE], 'readwrite'); + const store = transaction.objectStore(IndexedDBService.QUEUE_STORE); + const request = store.clear(); + request.onsuccess = () => { + resolve(); + }; + request.onerror = () => { + reject(new Error(`Failed to clear queue: ${request.error}`)); + }; + }); + } + // ======================================== + // Sync State Operations + // ======================================== + /** + * Save sync state value + */ + async setSyncState(key, value) { + const db = this.ensureDB(); + return new Promise((resolve, reject) => { + const transaction = db.transaction([IndexedDBService.SYNC_STATE_STORE], 'readwrite'); + const store = transaction.objectStore(IndexedDBService.SYNC_STATE_STORE); + const request = store.put({ key, value }); + request.onsuccess = () => { + resolve(); + }; + request.onerror = () => { + reject(new Error(`Failed to set sync state ${key}: ${request.error}`)); + }; + }); + } + /** + * Get sync state value + */ + async getSyncState(key) { + const db = this.ensureDB(); + return new Promise((resolve, reject) => { + const transaction = db.transaction([IndexedDBService.SYNC_STATE_STORE], 'readonly'); + const store = transaction.objectStore(IndexedDBService.SYNC_STATE_STORE); + const request = store.get(key); + request.onsuccess = () => { + const result = request.result; + resolve(result ? result.value : null); + }; + request.onerror = () => { + reject(new Error(`Failed to get sync state ${key}: ${request.error}`)); + }; + }); + } + // ======================================== + // Serialization Helpers + // ======================================== + /** + * Serialize event for IndexedDB storage (convert Dates to ISO strings) + */ + serializeEvent(event) { + return { + ...event, + start: event.start instanceof Date ? event.start.toISOString() : event.start, + end: event.end instanceof Date ? event.end.toISOString() : event.end + }; + } + /** + * Deserialize event from IndexedDB (convert ISO strings to Dates) + */ + deserializeEvent(event) { + return { + ...event, + start: typeof event.start === 'string' ? new Date(event.start) : event.start, + end: typeof event.end === 'string' ? new Date(event.end) : event.end + }; + } + /** + * Close database connection + */ + close() { + if (this.db) { + this.db.close(); + this.db = null; + } + } + /** + * Delete entire database (for testing/reset) + */ + static async deleteDatabase() { + return new Promise((resolve, reject) => { + const request = indexedDB.deleteDatabase(IndexedDBService.DB_NAME); + request.onsuccess = () => { + resolve(); + }; + request.onerror = () => { + reject(new Error(`Failed to delete database: ${request.error}`)); + }; + }); + } + /** + * Seed IndexedDB with mock data if empty + */ + async seedIfEmpty(mockDataUrl = 'data/mock-events.json') { + try { + const existingEvents = await this.getAllEvents(); + if (existingEvents.length > 0) { + console.log(`IndexedDB already has ${existingEvents.length} events - skipping seed`); + return; + } + console.log('IndexedDB is empty - seeding with mock data'); + // Check if online to fetch mock data + if (!navigator.onLine) { + console.warn('Offline and IndexedDB empty - starting with no events'); + return; + } + // Fetch mock events + const response = await fetch(mockDataUrl); + if (!response.ok) { + throw new Error(`Failed to fetch mock events: ${response.statusText}`); + } + const mockEvents = await response.json(); + // Convert and save to IndexedDB + for (const event of mockEvents) { + const calendarEvent = { + ...event, + start: new Date(event.start), + end: new Date(event.end), + allDay: event.allDay || false, + syncStatus: 'synced' + }; + await this.saveEvent(calendarEvent); + } + console.log(`Seeded IndexedDB with ${mockEvents.length} mock events`); + } + catch (error) { + console.error('Failed to seed IndexedDB:', error); + // Don't throw - allow app to start with empty calendar + } + } +} +IndexedDBService.DB_NAME = 'CalendarDB'; +IndexedDBService.DB_VERSION = 1; +IndexedDBService.EVENTS_STORE = 'events'; +IndexedDBService.QUEUE_STORE = 'operationQueue'; +IndexedDBService.SYNC_STATE_STORE = 'syncState'; +//# sourceMappingURL=IndexedDBService.js.map \ No newline at end of file diff --git a/wwwroot/js/storage/IndexedDBService.js.map b/wwwroot/js/storage/IndexedDBService.js.map new file mode 100644 index 0000000..488a2dd --- /dev/null +++ b/wwwroot/js/storage/IndexedDBService.js.map @@ -0,0 +1 @@ +{"version":3,"file":"IndexedDBService.js","sourceRoot":"","sources":["../../../src/storage/IndexedDBService.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/storage/OperationQueue.d.ts b/wwwroot/js/storage/OperationQueue.d.ts new file mode 100644 index 0000000..50018e6 --- /dev/null +++ b/wwwroot/js/storage/OperationQueue.d.ts @@ -0,0 +1,55 @@ +import { IndexedDBService, IQueueOperation } from './IndexedDBService'; +/** + * Operation Queue Manager + * Handles FIFO queue of pending sync operations + */ +export declare class OperationQueue { + private indexedDB; + constructor(indexedDB: IndexedDBService); + /** + * Add operation to the end of the queue + */ + enqueue(operation: Omit): Promise; + /** + * Get the first operation from the queue (without removing it) + * Returns null if queue is empty + */ + peek(): Promise; + /** + * Get all operations in the queue (sorted by timestamp FIFO) + */ + getAll(): Promise; + /** + * Remove a specific operation from the queue + */ + remove(operationId: string): Promise; + /** + * Remove the first operation from the queue and return it + * Returns null if queue is empty + */ + dequeue(): Promise; + /** + * Clear all operations from the queue + */ + clear(): Promise; + /** + * Get the number of operations in the queue + */ + size(): Promise; + /** + * Check if queue is empty + */ + isEmpty(): Promise; + /** + * Get operations for a specific event ID + */ + getOperationsForEvent(eventId: string): Promise; + /** + * Remove all operations for a specific event ID + */ + removeOperationsForEvent(eventId: string): Promise; + /** + * Update retry count for an operation + */ + incrementRetryCount(operationId: string): Promise; +} diff --git a/wwwroot/js/storage/OperationQueue.js b/wwwroot/js/storage/OperationQueue.js new file mode 100644 index 0000000..eb1b740 --- /dev/null +++ b/wwwroot/js/storage/OperationQueue.js @@ -0,0 +1,96 @@ +/** + * Operation Queue Manager + * Handles FIFO queue of pending sync operations + */ +export class OperationQueue { + constructor(indexedDB) { + this.indexedDB = indexedDB; + } + /** + * Add operation to the end of the queue + */ + async enqueue(operation) { + await this.indexedDB.addToQueue(operation); + } + /** + * Get the first operation from the queue (without removing it) + * Returns null if queue is empty + */ + async peek() { + const queue = await this.indexedDB.getQueue(); + return queue.length > 0 ? queue[0] : null; + } + /** + * Get all operations in the queue (sorted by timestamp FIFO) + */ + async getAll() { + return await this.indexedDB.getQueue(); + } + /** + * Remove a specific operation from the queue + */ + async remove(operationId) { + await this.indexedDB.removeFromQueue(operationId); + } + /** + * Remove the first operation from the queue and return it + * Returns null if queue is empty + */ + async dequeue() { + const operation = await this.peek(); + if (operation) { + await this.remove(operation.id); + } + return operation; + } + /** + * Clear all operations from the queue + */ + async clear() { + await this.indexedDB.clearQueue(); + } + /** + * Get the number of operations in the queue + */ + async size() { + const queue = await this.getAll(); + return queue.length; + } + /** + * Check if queue is empty + */ + async isEmpty() { + const size = await this.size(); + return size === 0; + } + /** + * Get operations for a specific event ID + */ + async getOperationsForEvent(eventId) { + const queue = await this.getAll(); + return queue.filter(op => op.eventId === eventId); + } + /** + * Remove all operations for a specific event ID + */ + async removeOperationsForEvent(eventId) { + const operations = await this.getOperationsForEvent(eventId); + for (const op of operations) { + await this.remove(op.id); + } + } + /** + * Update retry count for an operation + */ + async incrementRetryCount(operationId) { + const queue = await this.getAll(); + const operation = queue.find(op => op.id === operationId); + if (operation) { + operation.retryCount++; + // Re-add to queue with updated retry count + await this.remove(operationId); + await this.enqueue(operation); + } + } +} +//# sourceMappingURL=OperationQueue.js.map \ No newline at end of file diff --git a/wwwroot/js/storage/OperationQueue.js.map b/wwwroot/js/storage/OperationQueue.js.map new file mode 100644 index 0000000..572a8ac --- /dev/null +++ b/wwwroot/js/storage/OperationQueue.js.map @@ -0,0 +1 @@ +{"version":3,"file":"OperationQueue.js","sourceRoot":"","sources":["../../../src/storage/OperationQueue.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,OAAO,cAAc;IAGzB,YAAY,SAA2B;QACrC,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC7B,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAO,CAAC,SAAsC;QAClD,MAAM,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IAC7C,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,IAAI;QACR,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC;QAC9C,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC5C,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,MAAM;QACV,OAAO,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC;IACzC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,MAAM,CAAC,WAAmB;QAC9B,MAAM,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,WAAW,CAAC,CAAC;IACpD,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,OAAO;QACX,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QACpC,IAAI,SAAS,EAAE,CAAC;YACd,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QAClC,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,KAAK;QACT,MAAM,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,CAAC;IACpC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,IAAI;QACR,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;QAClC,OAAO,KAAK,CAAC,MAAM,CAAC;IACtB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAO;QACX,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAC/B,OAAO,IAAI,KAAK,CAAC,CAAC;IACpB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,qBAAqB,CAAC,OAAe;QACzC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;QAClC,OAAO,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,KAAK,OAAO,CAAC,CAAC;IACpD,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,wBAAwB,CAAC,OAAe;QAC5C,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,qBAAqB,CAAC,OAAO,CAAC,CAAC;QAC7D,KAAK,MAAM,EAAE,IAAI,UAAU,EAAE,CAAC;YAC5B,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,mBAAmB,CAAC,WAAmB;QAC3C,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;QAClC,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,WAAW,CAAC,CAAC;QAE1D,IAAI,SAAS,EAAE,CAAC;YACd,SAAS,CAAC,UAAU,EAAE,CAAC;YACvB,2CAA2C;YAC3C,MAAM,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;YAC/B,MAAM,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;CACF"} \ No newline at end of file diff --git a/wwwroot/js/strategies/MonthViewStrategy.d.ts b/wwwroot/js/strategies/MonthViewStrategy.d.ts new file mode 100644 index 0000000..3e782fb --- /dev/null +++ b/wwwroot/js/strategies/MonthViewStrategy.d.ts @@ -0,0 +1,25 @@ +/** + * MonthViewStrategy - Strategy for month view rendering + * Completely different from week view - no time axis, cell-based events + */ +import { ViewStrategy, ViewContext, ViewLayoutConfig } from './ViewStrategy'; +export declare class MonthViewStrategy implements ViewStrategy { + private dateCalculator; + constructor(); + getLayoutConfig(): ViewLayoutConfig; + renderGrid(context: ViewContext): void; + private createMonthGrid; + private createDayHeaders; + private createDayCells; + private getMonthDates; + private renderMonthEvents; + getNextPeriod(currentDate: Date): Date; + getPreviousPeriod(currentDate: Date): Date; + getPeriodLabel(date: Date): string; + getDisplayDates(baseDate: Date): Date[]; + getPeriodRange(baseDate: Date): { + startDate: Date; + endDate: Date; + }; + destroy(): void; +} diff --git a/wwwroot/js/strategies/MonthViewStrategy.js b/wwwroot/js/strategies/MonthViewStrategy.js new file mode 100644 index 0000000..670878d --- /dev/null +++ b/wwwroot/js/strategies/MonthViewStrategy.js @@ -0,0 +1,124 @@ +/** + * MonthViewStrategy - Strategy for month view rendering + * Completely different from week view - no time axis, cell-based events + */ +import { DateCalculator } from '../utils/DateCalculator'; +import { calendarConfig } from '../core/CalendarConfig'; +export class MonthViewStrategy { + constructor() { + DateCalculator.initialize(calendarConfig); + this.dateCalculator = new DateCalculator(); + } + getLayoutConfig() { + return { + needsTimeAxis: false, // No time axis in month view! + columnCount: 7, // Always 7 days (Mon-Sun) + scrollable: false, // Month fits in viewport + eventPositioning: 'cell-based' // Events go in day cells + }; + } + renderGrid(context) { + // Clear existing content + context.container.innerHTML = ''; + // Create month grid (completely different from week!) + this.createMonthGrid(context); + } + createMonthGrid(context) { + const monthGrid = document.createElement('div'); + monthGrid.className = 'month-grid'; + monthGrid.style.display = 'grid'; + monthGrid.style.gridTemplateColumns = 'repeat(7, 1fr)'; + monthGrid.style.gridTemplateRows = 'auto repeat(6, 1fr)'; + monthGrid.style.height = '100%'; + // Add day headers (Mon, Tue, Wed, etc.) + this.createDayHeaders(monthGrid); + // Add 6 weeks of day cells + this.createDayCells(monthGrid, context.currentDate); + // Render events in day cells (will be handled by EventRendererManager) + // this.renderMonthEvents(monthGrid, context.allDayEvents); + context.container.appendChild(monthGrid); + } + createDayHeaders(container) { + const dayNames = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']; + dayNames.forEach(dayName => { + const header = document.createElement('div'); + header.className = 'month-day-header'; + header.textContent = dayName; + header.style.padding = '8px'; + header.style.fontWeight = 'bold'; + header.style.textAlign = 'center'; + header.style.borderBottom = '1px solid #e0e0e0'; + container.appendChild(header); + }); + } + createDayCells(container, monthDate) { + const dates = this.getMonthDates(monthDate); + dates.forEach(date => { + const cell = document.createElement('div'); + cell.className = 'month-day-cell'; + cell.dataset.date = DateCalculator.formatISODate(date); + cell.style.border = '1px solid #e0e0e0'; + cell.style.minHeight = '100px'; + cell.style.padding = '4px'; + cell.style.position = 'relative'; + // Day number + const dayNumber = document.createElement('div'); + dayNumber.className = 'month-day-number'; + dayNumber.textContent = date.getDate().toString(); + dayNumber.style.fontWeight = 'bold'; + dayNumber.style.marginBottom = '4px'; + // Check if today + if (DateCalculator.isToday(date)) { + dayNumber.style.color = '#1976d2'; + cell.style.backgroundColor = '#f5f5f5'; + } + cell.appendChild(dayNumber); + container.appendChild(cell); + }); + } + getMonthDates(monthDate) { + // Get first day of month + const firstOfMonth = new Date(monthDate.getFullYear(), monthDate.getMonth(), 1); + // Get Monday of the week containing first day + const startDate = DateCalculator.getISOWeekStart(firstOfMonth); + // Generate 42 days (6 weeks) + const dates = []; + for (let i = 0; i < 42; i++) { + dates.push(DateCalculator.addDays(startDate, i)); + } + return dates; + } + renderMonthEvents(container, events) { + // TODO: Implement month event rendering + // Events will be small blocks in day cells + } + getNextPeriod(currentDate) { + return new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 1); + } + getPreviousPeriod(currentDate) { + return new Date(currentDate.getFullYear(), currentDate.getMonth() - 1, 1); + } + getPeriodLabel(date) { + const monthNames = ['January', 'February', 'March', 'April', 'May', 'June', + 'July', 'August', 'September', 'October', 'November', 'December']; + return `${monthNames[date.getMonth()]} ${date.getFullYear()}`; + } + getDisplayDates(baseDate) { + return this.getMonthDates(baseDate); + } + getPeriodRange(baseDate) { + // Month view shows events for the entire month grid (including partial weeks) + const firstOfMonth = new Date(baseDate.getFullYear(), baseDate.getMonth(), 1); + // Get Monday of the week containing first day + const startDate = DateCalculator.getISOWeekStart(firstOfMonth); + // End date is 41 days after start (42 total days) + const endDate = DateCalculator.addDays(startDate, 41); + return { + startDate, + endDate + }; + } + destroy() { + } +} +//# sourceMappingURL=MonthViewStrategy.js.map \ No newline at end of file diff --git a/wwwroot/js/strategies/MonthViewStrategy.js.map b/wwwroot/js/strategies/MonthViewStrategy.js.map new file mode 100644 index 0000000..04383f6 --- /dev/null +++ b/wwwroot/js/strategies/MonthViewStrategy.js.map @@ -0,0 +1 @@ +{"version":3,"file":"MonthViewStrategy.js","sourceRoot":"","sources":["../../../src/strategies/MonthViewStrategy.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAGxD,MAAM,OAAO,iBAAiB;IAG5B;QACE,cAAc,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;QAC1C,IAAI,CAAC,cAAc,GAAG,IAAI,cAAc,EAAE,CAAC;IAC7C,CAAC;IAED,eAAe;QACb,OAAO;YACL,aAAa,EAAE,KAAK,EAAS,8BAA8B;YAC3D,WAAW,EAAE,CAAC,EAAe,0BAA0B;YACvD,UAAU,EAAE,KAAK,EAAY,yBAAyB;YACtD,gBAAgB,EAAE,YAAY,CAAE,yBAAyB;SAC1D,CAAC;IACJ,CAAC;IAED,UAAU,CAAC,OAAoB;QAC7B,yBAAyB;QACzB,OAAO,CAAC,SAAS,CAAC,SAAS,GAAG,EAAE,CAAC;QAEjC,sDAAsD;QACtD,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;IAChC,CAAC;IAEO,eAAe,CAAC,OAAoB;QAC1C,MAAM,SAAS,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAChD,SAAS,CAAC,SAAS,GAAG,YAAY,CAAC;QACnC,SAAS,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC;QACjC,SAAS,CAAC,KAAK,CAAC,mBAAmB,GAAG,gBAAgB,CAAC;QACvD,SAAS,CAAC,KAAK,CAAC,gBAAgB,GAAG,qBAAqB,CAAC;QACzD,SAAS,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC;QAEhC,wCAAwC;QACxC,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC;QAEjC,2BAA2B;QAC3B,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;QAEpD,uEAAuE;QACvE,2DAA2D;QAE3D,OAAO,CAAC,SAAS,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;IAC3C,CAAC;IAEO,gBAAgB,CAAC,SAAsB;QAC7C,MAAM,QAAQ,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;QAEnE,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE;YACzB,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;YAC7C,MAAM,CAAC,SAAS,GAAG,kBAAkB,CAAC;YACtC,MAAM,CAAC,WAAW,GAAG,OAAO,CAAC;YAC7B,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC;YAC7B,MAAM,CAAC,KAAK,CAAC,UAAU,GAAG,MAAM,CAAC;YACjC,MAAM,CAAC,KAAK,CAAC,SAAS,GAAG,QAAQ,CAAC;YAClC,MAAM,CAAC,KAAK,CAAC,YAAY,GAAG,mBAAmB,CAAC;YAChD,SAAS,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QAChC,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,cAAc,CAAC,SAAsB,EAAE,SAAe;QAC5D,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;QAE5C,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;YACnB,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;YAC3C,IAAI,CAAC,SAAS,GAAG,gBAAgB,CAAC;YAClC,IAAI,CAAC,OAAO,CAAC,IAAI,GAAG,cAAc,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;YACvD,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,mBAAmB,CAAC;YACxC,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,OAAO,CAAC;YAC/B,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC;YAC3B,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,UAAU,CAAC;YAEjC,aAAa;YACb,MAAM,SAAS,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;YAChD,SAAS,CAAC,SAAS,GAAG,kBAAkB,CAAC;YACzC,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC;YAClD,SAAS,CAAC,KAAK,CAAC,UAAU,GAAG,MAAM,CAAC;YACpC,SAAS,CAAC,KAAK,CAAC,YAAY,GAAG,KAAK,CAAC;YAErC,iBAAiB;YACjB,IAAI,cAAc,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;gBACjC,SAAS,CAAC,KAAK,CAAC,KAAK,GAAG,SAAS,CAAC;gBAClC,IAAI,CAAC,KAAK,CAAC,eAAe,GAAG,SAAS,CAAC;YACzC,CAAC;YAED,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;YAC5B,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QAC9B,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,aAAa,CAAC,SAAe;QACnC,yBAAyB;QACzB,MAAM,YAAY,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,EAAE,SAAS,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC;QAEhF,8CAA8C;QAC9C,MAAM,SAAS,GAAG,cAAc,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC;QAE/D,6BAA6B;QAC7B,MAAM,KAAK,GAAW,EAAE,CAAC;QACzB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;YAC5B,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC;QACnD,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAEO,iBAAiB,CAAC,SAAsB,EAAE,MAAuB;QACvE,wCAAwC;QACxC,2CAA2C;IAC7C,CAAC;IAED,aAAa,CAAC,WAAiB;QAC7B,OAAO,IAAI,IAAI,CAAC,WAAW,CAAC,WAAW,EAAE,EAAE,WAAW,CAAC,QAAQ,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAC5E,CAAC;IAED,iBAAiB,CAAC,WAAiB;QACjC,OAAO,IAAI,IAAI,CAAC,WAAW,CAAC,WAAW,EAAE,EAAE,WAAW,CAAC,QAAQ,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAC5E,CAAC;IAED,cAAc,CAAC,IAAU;QACvB,MAAM,UAAU,GAAG,CAAC,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM;YACvD,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;QAErF,OAAO,GAAG,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;IAChE,CAAC;IAED,eAAe,CAAC,QAAc;QAC5B,OAAO,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;IACtC,CAAC;IAED,cAAc,CAAC,QAAc;QAC3B,8EAA8E;QAC9E,MAAM,YAAY,GAAG,IAAI,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,EAAE,QAAQ,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC;QAE9E,8CAA8C;QAC9C,MAAM,SAAS,GAAG,cAAc,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC;QAE/D,kDAAkD;QAClD,MAAM,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAEtD,OAAO;YACL,SAAS;YACT,OAAO;SACR,CAAC;IACJ,CAAC;IAED,OAAO;IACP,CAAC;CACF"} \ No newline at end of file diff --git a/wwwroot/js/strategies/ViewStrategy.d.ts b/wwwroot/js/strategies/ViewStrategy.d.ts new file mode 100644 index 0000000..6ce1eee --- /dev/null +++ b/wwwroot/js/strategies/ViewStrategy.d.ts @@ -0,0 +1,58 @@ +/** + * ViewStrategy - Strategy pattern for different calendar view types + * Allows clean separation between week view, month view, day view etc. + */ +import { ResourceCalendarData } from '../types/CalendarTypes'; +/** + * Context object passed to strategy methods + */ +export interface ViewContext { + currentDate: Date; + container: HTMLElement; + resourceData: ResourceCalendarData | null; +} +/** + * Layout configuration specific to each view type + */ +export interface ViewLayoutConfig { + needsTimeAxis: boolean; + columnCount: number; + scrollable: boolean; + eventPositioning: 'time-based' | 'cell-based'; +} +/** + * Base strategy interface for all view types + */ +export interface ViewStrategy { + /** + * Get the layout configuration for this view + */ + getLayoutConfig(): ViewLayoutConfig; + /** + * Render the grid structure for this view + */ + renderGrid(context: ViewContext): void; + /** + * Calculate next period for navigation + */ + getNextPeriod(currentDate: Date): Date; + /** + * Calculate previous period for navigation + */ + getPreviousPeriod(currentDate: Date): Date; + /** + * Get display label for current period + */ + getPeriodLabel(date: Date): string; + /** + * Get the dates that should be displayed in this view + */ + getDisplayDates(baseDate: Date): Date[]; + /** + * Get the period start and end dates for event filtering + */ + getPeriodRange(baseDate: Date): { + startDate: Date; + endDate: Date; + }; +} diff --git a/wwwroot/js/strategies/ViewStrategy.js b/wwwroot/js/strategies/ViewStrategy.js new file mode 100644 index 0000000..6185c60 --- /dev/null +++ b/wwwroot/js/strategies/ViewStrategy.js @@ -0,0 +1,6 @@ +/** + * ViewStrategy - Strategy pattern for different calendar view types + * Allows clean separation between week view, month view, day view etc. + */ +export {}; +//# sourceMappingURL=ViewStrategy.js.map \ No newline at end of file diff --git a/wwwroot/js/strategies/ViewStrategy.js.map b/wwwroot/js/strategies/ViewStrategy.js.map new file mode 100644 index 0000000..e58f7ec --- /dev/null +++ b/wwwroot/js/strategies/ViewStrategy.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ViewStrategy.js","sourceRoot":"","sources":["../../../src/strategies/ViewStrategy.ts"],"names":[],"mappings":"AAAA;;;GAGG"} \ No newline at end of file diff --git a/wwwroot/js/strategies/WeekViewStrategy.d.ts b/wwwroot/js/strategies/WeekViewStrategy.d.ts new file mode 100644 index 0000000..3307dfc --- /dev/null +++ b/wwwroot/js/strategies/WeekViewStrategy.d.ts @@ -0,0 +1,22 @@ +/** + * WeekViewStrategy - Strategy for week/day view rendering + * Extracts the time-based grid logic from GridManager + */ +import { ViewStrategy, ViewContext, ViewLayoutConfig } from './ViewStrategy'; +export declare class WeekViewStrategy implements ViewStrategy { + private dateCalculator; + private gridRenderer; + private styleManager; + constructor(); + getLayoutConfig(): ViewLayoutConfig; + renderGrid(context: ViewContext): void; + getNextPeriod(currentDate: Date): Date; + getPreviousPeriod(currentDate: Date): Date; + getPeriodLabel(date: Date): string; + getDisplayDates(baseDate: Date): Date[]; + getPeriodRange(baseDate: Date): { + startDate: Date; + endDate: Date; + }; + destroy(): void; +} diff --git a/wwwroot/js/strategies/WeekViewStrategy.js b/wwwroot/js/strategies/WeekViewStrategy.js new file mode 100644 index 0000000..d5130d9 --- /dev/null +++ b/wwwroot/js/strategies/WeekViewStrategy.js @@ -0,0 +1,57 @@ +/** + * WeekViewStrategy - Strategy for week/day view rendering + * Extracts the time-based grid logic from GridManager + */ +import { DateCalculator } from '../utils/DateCalculator'; +import { calendarConfig } from '../core/CalendarConfig'; +import { GridRenderer } from '../renderers/GridRenderer'; +import { GridStyleManager } from '../renderers/GridStyleManager'; +export class WeekViewStrategy { + constructor() { + DateCalculator.initialize(calendarConfig); + this.dateCalculator = new DateCalculator(); + this.gridRenderer = new GridRenderer(); + this.styleManager = new GridStyleManager(); + } + getLayoutConfig() { + return { + needsTimeAxis: true, + columnCount: calendarConfig.getWorkWeekSettings().totalDays, + scrollable: true, + eventPositioning: 'time-based' + }; + } + renderGrid(context) { + // Update grid styles + this.styleManager.updateGridStyles(context.resourceData); + // Render the grid structure (time axis + day columns) + this.gridRenderer.renderGrid(context.container, context.currentDate, context.resourceData); + } + getNextPeriod(currentDate) { + return DateCalculator.addWeeks(currentDate, 1); + } + getPreviousPeriod(currentDate) { + return DateCalculator.addWeeks(currentDate, -1); + } + getPeriodLabel(date) { + const weekStart = DateCalculator.getISOWeekStart(date); + const weekEnd = DateCalculator.addDays(weekStart, 6); + const weekNumber = DateCalculator.getWeekNumber(date); + return `Week ${weekNumber}: ${DateCalculator.formatDateRange(weekStart, weekEnd)}`; + } + getDisplayDates(baseDate) { + return DateCalculator.getWorkWeekDates(baseDate); + } + getPeriodRange(baseDate) { + const weekStart = DateCalculator.getISOWeekStart(baseDate); + const weekEnd = DateCalculator.addDays(weekStart, 6); + return { + startDate: weekStart, + endDate: weekEnd + }; + } + destroy() { + // Clean up any week-specific resources + } +} +//# sourceMappingURL=WeekViewStrategy.js.map \ No newline at end of file diff --git a/wwwroot/js/strategies/WeekViewStrategy.js.map b/wwwroot/js/strategies/WeekViewStrategy.js.map new file mode 100644 index 0000000..fff7d39 --- /dev/null +++ b/wwwroot/js/strategies/WeekViewStrategy.js.map @@ -0,0 +1 @@ +{"version":3,"file":"WeekViewStrategy.js","sourceRoot":"","sources":["../../../src/strategies/WeekViewStrategy.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAEjE,MAAM,OAAO,gBAAgB;IAK3B;QACE,cAAc,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;QAC1C,IAAI,CAAC,cAAc,GAAG,IAAI,cAAc,EAAE,CAAC;QAC3C,IAAI,CAAC,YAAY,GAAG,IAAI,YAAY,EAAE,CAAC;QACvC,IAAI,CAAC,YAAY,GAAG,IAAI,gBAAgB,EAAE,CAAC;IAC7C,CAAC;IAED,eAAe;QACb,OAAO;YACL,aAAa,EAAE,IAAI;YACnB,WAAW,EAAE,cAAc,CAAC,mBAAmB,EAAE,CAAC,SAAS;YAC3D,UAAU,EAAE,IAAI;YAChB,gBAAgB,EAAE,YAAY;SAC/B,CAAC;IACJ,CAAC;IAED,UAAU,CAAC,OAAoB;QAC7B,qBAAqB;QACrB,IAAI,CAAC,YAAY,CAAC,gBAAgB,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QAEzD,sDAAsD;QACtD,IAAI,CAAC,YAAY,CAAC,UAAU,CAC1B,OAAO,CAAC,SAAS,EACjB,OAAO,CAAC,WAAW,EACnB,OAAO,CAAC,YAAY,CACrB,CAAC;IACJ,CAAC;IAED,aAAa,CAAC,WAAiB;QAC7B,OAAO,cAAc,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IACjD,CAAC;IAED,iBAAiB,CAAC,WAAiB;QACjC,OAAO,cAAc,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC;IAClD,CAAC;IAED,cAAc,CAAC,IAAU;QACvB,MAAM,SAAS,GAAG,cAAc,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QACvD,MAAM,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;QACrD,MAAM,UAAU,GAAG,cAAc,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;QAEtD,OAAO,QAAQ,UAAU,KAAK,cAAc,CAAC,eAAe,CAAC,SAAS,EAAE,OAAO,CAAC,EAAE,CAAC;IACrF,CAAC;IAED,eAAe,CAAC,QAAc;QAC5B,OAAO,cAAc,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IACnD,CAAC;IAED,cAAc,CAAC,QAAc;QAC3B,MAAM,SAAS,GAAG,cAAc,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;QAC3D,MAAM,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;QAErD,OAAO;YACL,SAAS,EAAE,SAAS;YACpB,OAAO,EAAE,OAAO;SACjB,CAAC;IACJ,CAAC;IAED,OAAO;QACL,uCAAuC;IACzC,CAAC;CACF"} \ No newline at end of file diff --git a/wwwroot/js/types/CalendarTypes.d.ts b/wwwroot/js/types/CalendarTypes.d.ts new file mode 100644 index 0000000..d5efb9f --- /dev/null +++ b/wwwroot/js/types/CalendarTypes.d.ts @@ -0,0 +1,56 @@ +export type ViewPeriod = 'day' | 'week' | 'month'; +export type CalendarView = ViewPeriod; +export type SyncStatus = 'synced' | 'pending' | 'error'; +export interface IRenderContext { + container: HTMLElement; + startDate: Date; + endDate: Date; +} +export interface ICalendarEvent { + id: string; + title: string; + description?: string; + start: Date; + end: Date; + type: string; + allDay: boolean; + syncStatus: SyncStatus; + recurringId?: string; + metadata?: Record; +} +export interface ICalendarConfig { + scrollbarWidth: number; + scrollbarColor: string; + scrollbarTrackColor: string; + scrollbarHoverColor: string; + scrollbarBorderRadius: number; + allowDrag: boolean; + allowResize: boolean; + allowCreate: boolean; + apiEndpoint: string; + dateFormat: string; + timeFormat: string; + enableSearch: boolean; + enableTouch: boolean; + defaultEventDuration: number; + minEventDuration: number; + maxEventDuration: number; +} +export interface IEventLogEntry { + type: string; + detail: unknown; + timestamp: number; +} +export interface IListenerEntry { + eventType: string; + handler: EventListener; + options?: AddEventListenerOptions; +} +export interface IEventBus { + on(eventType: string, handler: EventListener, options?: AddEventListenerOptions): () => void; + once(eventType: string, handler: EventListener): () => void; + off(eventType: string, handler: EventListener): void; + emit(eventType: string, detail?: unknown): boolean; + getEventLog(eventType?: string): IEventLogEntry[]; + setDebug(enabled: boolean): void; +} diff --git a/wwwroot/js/types/CalendarTypes.js b/wwwroot/js/types/CalendarTypes.js new file mode 100644 index 0000000..a86177f --- /dev/null +++ b/wwwroot/js/types/CalendarTypes.js @@ -0,0 +1,3 @@ +// Calendar type definitions +export {}; +//# sourceMappingURL=CalendarTypes.js.map \ No newline at end of file diff --git a/wwwroot/js/types/CalendarTypes.js.map b/wwwroot/js/types/CalendarTypes.js.map new file mode 100644 index 0000000..6bb92ea --- /dev/null +++ b/wwwroot/js/types/CalendarTypes.js.map @@ -0,0 +1 @@ +{"version":3,"file":"CalendarTypes.js","sourceRoot":"","sources":["../../../src/types/CalendarTypes.ts"],"names":[],"mappings":"AAAA,4BAA4B"} \ No newline at end of file diff --git a/wwwroot/js/types/ColumnDataSource.d.ts b/wwwroot/js/types/ColumnDataSource.d.ts new file mode 100644 index 0000000..269fc8e --- /dev/null +++ b/wwwroot/js/types/ColumnDataSource.d.ts @@ -0,0 +1,17 @@ +/** + * IColumnDataSource - Defines the contract for providing column data + * + * This interface abstracts away whether columns represent dates or resources, + * allowing the calendar to switch between date-based and resource-based views. + */ +export interface IColumnDataSource { + /** + * Get the list of column identifiers to render + * @returns Array of identifiers (dates or resource IDs) + */ + getColumns(): Date[]; + /** + * Get the type of columns this datasource provides + */ + getType(): 'date' | 'resource'; +} diff --git a/wwwroot/js/types/ColumnDataSource.js b/wwwroot/js/types/ColumnDataSource.js new file mode 100644 index 0000000..1fd57b8 --- /dev/null +++ b/wwwroot/js/types/ColumnDataSource.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=ColumnDataSource.js.map \ No newline at end of file diff --git a/wwwroot/js/types/ColumnDataSource.js.map b/wwwroot/js/types/ColumnDataSource.js.map new file mode 100644 index 0000000..2d1395f --- /dev/null +++ b/wwwroot/js/types/ColumnDataSource.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ColumnDataSource.js","sourceRoot":"","sources":["../../../src/types/ColumnDataSource.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/wwwroot/js/types/DragDropTypes.d.ts b/wwwroot/js/types/DragDropTypes.d.ts new file mode 100644 index 0000000..da16c45 --- /dev/null +++ b/wwwroot/js/types/DragDropTypes.d.ts @@ -0,0 +1,41 @@ +/** + * Type definitions for drag and drop functionality + */ +export interface IMousePosition { + x: number; + y: number; + clientX?: number; + clientY?: number; +} +export interface IDragOffset { + x: number; + y: number; + offsetX?: number; + offsetY?: number; +} +export interface IDragState { + isDragging: boolean; + draggedElement: HTMLElement | null; + draggedClone: HTMLElement | null; + eventId: string | null; + startColumn: string | null; + currentColumn: string | null; + mouseOffset: IDragOffset; +} +export interface IDragEndPosition { + column: string; + y: number; + snappedY: number; + time?: Date; +} +export interface IStackLinkData { + prev?: string; + next?: string; + isFirst?: boolean; + isLast?: boolean; +} +export interface IDragEventHandlers { + handleDragStart?(originalElement: HTMLElement, eventId: string, mouseOffset: IDragOffset, column: string): void; + handleDragMove?(eventId: string, snappedY: number, column: string, mouseOffset: IDragOffset): void; + handleDragEnd?(eventId: string, originalElement: HTMLElement, draggedClone: HTMLElement, finalColumn: string, finalY: number): void; +} diff --git a/wwwroot/js/types/DragDropTypes.js b/wwwroot/js/types/DragDropTypes.js new file mode 100644 index 0000000..d892616 --- /dev/null +++ b/wwwroot/js/types/DragDropTypes.js @@ -0,0 +1,5 @@ +/** + * Type definitions for drag and drop functionality + */ +export {}; +//# sourceMappingURL=DragDropTypes.js.map \ No newline at end of file diff --git a/wwwroot/js/types/DragDropTypes.js.map b/wwwroot/js/types/DragDropTypes.js.map new file mode 100644 index 0000000..2272daa --- /dev/null +++ b/wwwroot/js/types/DragDropTypes.js.map @@ -0,0 +1 @@ +{"version":3,"file":"DragDropTypes.js","sourceRoot":"","sources":["../../../src/types/DragDropTypes.ts"],"names":[],"mappings":"AAAA;;GAEG"} \ No newline at end of file diff --git a/wwwroot/js/types/EventPayloadMap.d.ts b/wwwroot/js/types/EventPayloadMap.d.ts new file mode 100644 index 0000000..d35b9d7 --- /dev/null +++ b/wwwroot/js/types/EventPayloadMap.d.ts @@ -0,0 +1,133 @@ +import { CalendarEvent, CalendarView } from './CalendarTypes'; +import { DragStartEventPayload, DragMoveEventPayload, DragEndEventPayload, DragMouseEnterHeaderEventPayload, DragMouseLeaveHeaderEventPayload, HeaderReadyEventPayload } from './EventTypes'; +import { CoreEvents } from '../constants/CoreEvents'; +/** + * Complete type mapping for all calendar events + * This enables type-safe event emission and handling + */ +export interface CalendarEventPayloadMap { + [CoreEvents.INITIALIZED]: { + initialized: boolean; + timestamp: number; + }; + [CoreEvents.READY]: undefined; + [CoreEvents.DESTROYED]: undefined; + [CoreEvents.VIEW_CHANGED]: { + view: CalendarView; + previousView?: CalendarView; + }; + [CoreEvents.VIEW_RENDERED]: { + view: CalendarView; + }; + [CoreEvents.WORKWEEK_CHANGED]: { + settings: unknown; + }; + [CoreEvents.DATE_CHANGED]: { + date: Date; + view?: CalendarView; + }; + [CoreEvents.NAVIGATION_COMPLETED]: { + direction: 'previous' | 'next' | 'today'; + }; + [CoreEvents.PERIOD_INFO_UPDATE]: { + label: string; + startDate: Date; + endDate: Date; + }; + [CoreEvents.NAVIGATE_TO_EVENT]: { + eventId: string; + }; + [CoreEvents.DATA_LOADING]: undefined; + [CoreEvents.DATA_LOADED]: { + events: CalendarEvent[]; + count: number; + }; + [CoreEvents.DATA_ERROR]: { + error: Error; + }; + [CoreEvents.EVENTS_FILTERED]: { + filteredEvents: CalendarEvent[]; + }; + [CoreEvents.GRID_RENDERED]: { + container: HTMLElement; + currentDate: Date; + startDate: Date; + endDate: Date; + columnCount: number; + }; + [CoreEvents.GRID_CLICKED]: { + column: string; + row: number; + }; + [CoreEvents.CELL_SELECTED]: { + cell: HTMLElement; + }; + [CoreEvents.EVENT_CREATED]: { + event: CalendarEvent; + }; + [CoreEvents.EVENT_UPDATED]: { + event: CalendarEvent; + previousData?: Partial; + }; + [CoreEvents.EVENT_DELETED]: { + eventId: string; + }; + [CoreEvents.EVENT_SELECTED]: { + eventId: string; + event?: CalendarEvent; + }; + [CoreEvents.ERROR]: { + error: Error; + context?: string; + }; + [CoreEvents.REFRESH_REQUESTED]: { + view?: CalendarView; + date?: Date; + }; + [CoreEvents.FILTER_CHANGED]: { + activeFilters: string[]; + visibleEvents: CalendarEvent[]; + }; + [CoreEvents.EVENTS_RENDERED]: { + eventCount: number; + }; + 'drag:start': DragStartEventPayload; + 'drag:move': DragMoveEventPayload; + 'drag:end': DragEndEventPayload; + 'drag:mouseenter-header': DragMouseEnterHeaderEventPayload; + 'drag:mouseleave-header': DragMouseLeaveHeaderEventPayload; + 'drag:cancelled': { + reason: string; + }; + 'header:ready': HeaderReadyEventPayload; + 'header:height-changed': { + height: number; + rowCount: number; + }; + 'allday:checkHeight': undefined; + 'allday:convert-to-allday': { + eventId: string; + element: HTMLElement; + }; + 'allday:convert-from-allday': { + eventId: string; + element: HTMLElement; + }; + 'scroll:sync': { + scrollTop: number; + source: string; + }; + 'scroll:to-hour': { + hour: number; + }; + 'filter:updated': { + activeFilters: string[]; + visibleEvents: CalendarEvent[]; + }; + 'filter:search': { + query: string; + results: CalendarEvent[]; + }; +} +export type EventPayload = CalendarEventPayloadMap[T]; +export declare function hasPayload(eventType: T, payload: unknown): payload is CalendarEventPayloadMap[T]; diff --git a/wwwroot/js/types/EventPayloadMap.js b/wwwroot/js/types/EventPayloadMap.js new file mode 100644 index 0000000..8738d5e --- /dev/null +++ b/wwwroot/js/types/EventPayloadMap.js @@ -0,0 +1,6 @@ +import { CoreEvents } from '../constants/CoreEvents'; +// Type guard to check if an event has a payload +export function hasPayload(eventType, payload) { + return payload !== undefined; +} +//# sourceMappingURL=EventPayloadMap.js.map \ No newline at end of file diff --git a/wwwroot/js/types/EventPayloadMap.js.map b/wwwroot/js/types/EventPayloadMap.js.map new file mode 100644 index 0000000..89f465c --- /dev/null +++ b/wwwroot/js/types/EventPayloadMap.js.map @@ -0,0 +1 @@ +{"version":3,"file":"EventPayloadMap.js","sourceRoot":"","sources":["../../../src/types/EventPayloadMap.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAiKrD,gDAAgD;AAChD,MAAM,UAAU,UAAU,CACxB,SAAY,EACZ,OAAgB;IAEhB,OAAO,OAAO,KAAK,SAAS,CAAC;AAC/B,CAAC"} \ No newline at end of file diff --git a/wwwroot/js/types/EventTypes.d.ts b/wwwroot/js/types/EventTypes.d.ts new file mode 100644 index 0000000..c99f970 --- /dev/null +++ b/wwwroot/js/types/EventTypes.d.ts @@ -0,0 +1,81 @@ +/** + * Type definitions for calendar events and drag operations + */ +import { IColumnBounds } from "../utils/ColumnDetectionUtils"; +import { ICalendarEvent } from "./CalendarTypes"; +/** + * Drag Event Payload Interfaces + * Type-safe interfaces for drag and drop events + */ +export interface IMousePosition { + x: number; + y: number; +} +export interface IDragStartEventPayload { + originalElement: HTMLElement; + draggedClone: HTMLElement | null; + mousePosition: IMousePosition; + mouseOffset: IMousePosition; + columnBounds: IColumnBounds | null; +} +export interface IDragMoveEventPayload { + originalElement: HTMLElement; + draggedClone: HTMLElement; + mousePosition: IMousePosition; + mouseOffset: IMousePosition; + columnBounds: IColumnBounds | null; + snappedY: number; +} +export interface IDragEndEventPayload { + originalElement: HTMLElement; + draggedClone: HTMLElement | null; + mousePosition: IMousePosition; + originalSourceColumn: IColumnBounds; + finalPosition: { + column: IColumnBounds | null; + snappedY: number; + }; + target: 'swp-day-column' | 'swp-day-header' | null; +} +export interface IDragMouseEnterHeaderEventPayload { + targetColumn: IColumnBounds; + mousePosition: IMousePosition; + originalElement: HTMLElement | null; + draggedClone: HTMLElement; + calendarEvent: ICalendarEvent; + replaceClone: (newClone: HTMLElement) => void; +} +export interface IDragMouseLeaveHeaderEventPayload { + targetDate: string | null; + mousePosition: IMousePosition; + originalElement: HTMLElement | null; + draggedClone: HTMLElement | null; +} +export interface IDragMouseEnterColumnEventPayload { + targetColumn: IColumnBounds; + mousePosition: IMousePosition; + snappedY: number; + originalElement: HTMLElement | null; + draggedClone: HTMLElement; + calendarEvent: ICalendarEvent; + replaceClone: (newClone: HTMLElement) => void; +} +export interface IDragColumnChangeEventPayload { + originalElement: HTMLElement; + draggedClone: HTMLElement; + previousColumn: IColumnBounds | null; + newColumn: IColumnBounds; + mousePosition: IMousePosition; +} +export interface IHeaderReadyEventPayload { + headerElements: IColumnBounds[]; +} +export interface IResizeEndEventPayload { + eventId: string; + element: HTMLElement; + finalHeight: number; +} +export interface INavButtonClickedEventPayload { + direction: 'next' | 'previous' | 'today'; + newDate: Date; +} diff --git a/wwwroot/js/types/EventTypes.js b/wwwroot/js/types/EventTypes.js new file mode 100644 index 0000000..db1af83 --- /dev/null +++ b/wwwroot/js/types/EventTypes.js @@ -0,0 +1,5 @@ +/** + * Type definitions for calendar events and drag operations + */ +export {}; +//# sourceMappingURL=EventTypes.js.map \ No newline at end of file diff --git a/wwwroot/js/types/EventTypes.js.map b/wwwroot/js/types/EventTypes.js.map new file mode 100644 index 0000000..30cdf68 --- /dev/null +++ b/wwwroot/js/types/EventTypes.js.map @@ -0,0 +1 @@ +{"version":3,"file":"EventTypes.js","sourceRoot":"","sources":["../../../src/types/EventTypes.ts"],"names":[],"mappings":"AAAA;;GAEG"} \ No newline at end of file diff --git a/wwwroot/js/types/ManagerTypes.d.ts b/wwwroot/js/types/ManagerTypes.d.ts new file mode 100644 index 0000000..9af0be9 --- /dev/null +++ b/wwwroot/js/types/ManagerTypes.d.ts @@ -0,0 +1,59 @@ +import { ICalendarEvent, CalendarView } from './CalendarTypes'; +/** + * Complete type definition for all managers returned by ManagerFactory + */ +export interface ICalendarManagers { + eventManager: IEventManager; + eventRenderer: IEventRenderingService; + gridManager: IGridManager; + scrollManager: IScrollManager; + navigationManager: unknown; + viewManager: IViewManager; + calendarManager: ICalendarManager; + dragDropManager: unknown; + allDayManager: unknown; + resizeHandleManager: IResizeHandleManager; + edgeScrollManager: unknown; + dragHoverManager: unknown; + headerManager: unknown; +} +/** + * Base interface for managers with optional initialization and refresh + */ +interface IManager { + initialize?(): Promise | void; + refresh?(): void; +} +export interface IEventManager extends IManager { + loadData(): Promise; + getEvents(): ICalendarEvent[]; + getEventsForPeriod(startDate: Date, endDate: Date): ICalendarEvent[]; + navigateToEvent(eventId: string): boolean; +} +export interface IEventRenderingService extends IManager { +} +export interface IGridManager extends IManager { + render(): Promise; +} +export interface IScrollManager extends IManager { + scrollTo(scrollTop: number): void; + scrollToHour(hour: number): void; +} +export interface INavigationManager extends IManager { + [key: string]: unknown; +} +export interface IViewManager extends IManager { + getCurrentView?(): CalendarView; +} +export interface ICalendarManager extends IManager { + setView(view: CalendarView): void; + setCurrentDate(date: Date): void; +} +export interface IDragDropManager extends IManager { +} +export interface IAllDayManager extends IManager { + [key: string]: unknown; +} +export interface IResizeHandleManager extends IManager { +} +export {}; diff --git a/wwwroot/js/types/ManagerTypes.js b/wwwroot/js/types/ManagerTypes.js new file mode 100644 index 0000000..8e31fa0 --- /dev/null +++ b/wwwroot/js/types/ManagerTypes.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=ManagerTypes.js.map \ No newline at end of file diff --git a/wwwroot/js/types/ManagerTypes.js.map b/wwwroot/js/types/ManagerTypes.js.map new file mode 100644 index 0000000..a63646f --- /dev/null +++ b/wwwroot/js/types/ManagerTypes.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ManagerTypes.js","sourceRoot":"","sources":["../../../src/types/ManagerTypes.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/wwwroot/js/utils/AllDayLayoutEngine.d.ts b/wwwroot/js/utils/AllDayLayoutEngine.d.ts new file mode 100644 index 0000000..caff572 --- /dev/null +++ b/wwwroot/js/utils/AllDayLayoutEngine.d.ts @@ -0,0 +1,42 @@ +import { ICalendarEvent } from '../types/CalendarTypes'; +export interface IEventLayout { + calenderEvent: ICalendarEvent; + gridArea: string; + startColumn: number; + endColumn: number; + row: number; + columnSpan: number; +} +export declare class AllDayLayoutEngine { + private weekDates; + private tracks; + constructor(weekDates: string[]); + /** + * Calculate layout for all events using clean day-based logic + */ + calculateLayout(events: ICalendarEvent[]): IEventLayout[]; + /** + * Find available track for event spanning from startDay to endDay (0-based indices) + */ + private findAvailableTrack; + /** + * Check if track is available for the given day range (0-based indices) + */ + private isTrackAvailable; + /** + * Get start day index for event (1-based, 0 if not visible) + */ + private getEventStartDay; + /** + * Get end day index for event (1-based, 0 if not visible) + */ + private getEventEndDay; + /** + * Check if event is visible in the current date range + */ + private isEventVisible; + /** + * Format date to YYYY-MM-DD string using local date + */ + private formatDate; +} diff --git a/wwwroot/js/utils/AllDayLayoutEngine.js b/wwwroot/js/utils/AllDayLayoutEngine.js new file mode 100644 index 0000000..c939563 --- /dev/null +++ b/wwwroot/js/utils/AllDayLayoutEngine.js @@ -0,0 +1,108 @@ +export class AllDayLayoutEngine { + constructor(weekDates) { + this.weekDates = weekDates; + this.tracks = []; + } + /** + * Calculate layout for all events using clean day-based logic + */ + calculateLayout(events) { + let layouts = []; + // Reset tracks for new calculation + this.tracks = [new Array(this.weekDates.length).fill(false)]; + // Filter to only visible events + const visibleEvents = events.filter(event => this.isEventVisible(event)); + // Process events in input order (no sorting) + for (const event of visibleEvents) { + const startDay = this.getEventStartDay(event); + const endDay = this.getEventEndDay(event); + if (startDay > 0 && endDay > 0) { + const track = this.findAvailableTrack(startDay - 1, endDay - 1); // Convert to 0-based for tracks + // Mark days as occupied + for (let day = startDay - 1; day <= endDay - 1; day++) { + this.tracks[track][day] = true; + } + const layout = { + calenderEvent: event, + gridArea: `${track + 1} / ${startDay} / ${track + 2} / ${endDay + 1}`, + startColumn: startDay, + endColumn: endDay, + row: track + 1, + columnSpan: endDay - startDay + 1 + }; + layouts.push(layout); + } + } + return layouts; + } + /** + * Find available track for event spanning from startDay to endDay (0-based indices) + */ + findAvailableTrack(startDay, endDay) { + for (let trackIndex = 0; trackIndex < this.tracks.length; trackIndex++) { + if (this.isTrackAvailable(trackIndex, startDay, endDay)) { + return trackIndex; + } + } + // Create new track if none available + this.tracks.push(new Array(this.weekDates.length).fill(false)); + return this.tracks.length - 1; + } + /** + * Check if track is available for the given day range (0-based indices) + */ + isTrackAvailable(trackIndex, startDay, endDay) { + for (let day = startDay; day <= endDay; day++) { + if (this.tracks[trackIndex][day]) { + return false; + } + } + return true; + } + /** + * Get start day index for event (1-based, 0 if not visible) + */ + getEventStartDay(event) { + const eventStartDate = this.formatDate(event.start); + const firstVisibleDate = this.weekDates[0]; + // If event starts before visible range, clip to first visible day + const clippedStartDate = eventStartDate < firstVisibleDate ? firstVisibleDate : eventStartDate; + const dayIndex = this.weekDates.indexOf(clippedStartDate); + return dayIndex >= 0 ? dayIndex + 1 : 0; + } + /** + * Get end day index for event (1-based, 0 if not visible) + */ + getEventEndDay(event) { + const eventEndDate = this.formatDate(event.end); + const lastVisibleDate = this.weekDates[this.weekDates.length - 1]; + // If event ends after visible range, clip to last visible day + const clippedEndDate = eventEndDate > lastVisibleDate ? lastVisibleDate : eventEndDate; + const dayIndex = this.weekDates.indexOf(clippedEndDate); + return dayIndex >= 0 ? dayIndex + 1 : 0; + } + /** + * Check if event is visible in the current date range + */ + isEventVisible(event) { + if (this.weekDates.length === 0) + return false; + const eventStartDate = this.formatDate(event.start); + const eventEndDate = this.formatDate(event.end); + const firstVisibleDate = this.weekDates[0]; + const lastVisibleDate = this.weekDates[this.weekDates.length - 1]; + // Event overlaps if it doesn't end before visible range starts + // AND doesn't start after visible range ends + return !(eventEndDate < firstVisibleDate || eventStartDate > lastVisibleDate); + } + /** + * Format date to YYYY-MM-DD string using local date + */ + formatDate(date) { + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + return `${year}-${month}-${day}`; + } +} +//# sourceMappingURL=AllDayLayoutEngine.js.map \ No newline at end of file diff --git a/wwwroot/js/utils/AllDayLayoutEngine.js.map b/wwwroot/js/utils/AllDayLayoutEngine.js.map new file mode 100644 index 0000000..a6d6e7b --- /dev/null +++ b/wwwroot/js/utils/AllDayLayoutEngine.js.map @@ -0,0 +1 @@ +{"version":3,"file":"AllDayLayoutEngine.js","sourceRoot":"","sources":["../../../src/utils/AllDayLayoutEngine.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/utils/ColumnDetectionUtils.d.ts b/wwwroot/js/utils/ColumnDetectionUtils.d.ts new file mode 100644 index 0000000..04e1552 --- /dev/null +++ b/wwwroot/js/utils/ColumnDetectionUtils.d.ts @@ -0,0 +1,30 @@ +/** + * ColumnDetectionUtils - Shared utility for column detection and caching + * Used by both DragDropManager and AllDayManager for consistent column detection + */ +import { IMousePosition } from "../types/DragDropTypes"; +export interface IColumnBounds { + date: string; + left: number; + right: number; + boundingClientRect: DOMRect; + element: HTMLElement; + index: number; +} +export declare class ColumnDetectionUtils { + private static columnBoundsCache; + /** + * Update column bounds cache for coordinate-based column detection + */ + static updateColumnBoundsCache(): void; + /** + * Get column date from X coordinate using cached bounds + */ + static getColumnBounds(position: IMousePosition): IColumnBounds | null; + /** + * Get column bounds by Date + */ + static getColumnBoundsByDate(date: Date): IColumnBounds | null; + static getColumns(): IColumnBounds[]; + static getHeaderColumns(): IColumnBounds[]; +} diff --git a/wwwroot/js/utils/ColumnDetectionUtils.js b/wwwroot/js/utils/ColumnDetectionUtils.js new file mode 100644 index 0000000..552638b --- /dev/null +++ b/wwwroot/js/utils/ColumnDetectionUtils.js @@ -0,0 +1,87 @@ +/** + * ColumnDetectionUtils - Shared utility for column detection and caching + * Used by both DragDropManager and AllDayManager for consistent column detection + */ +export class ColumnDetectionUtils { + /** + * Update column bounds cache for coordinate-based column detection + */ + static updateColumnBoundsCache() { + // Reset cache + this.columnBoundsCache = []; + // Find alle kolonner + const columns = document.querySelectorAll('swp-day-column'); + let index = 1; + // Cache hver kolonnes x-grænser + columns.forEach(column => { + const rect = column.getBoundingClientRect(); + const date = column.dataset.date; + if (date) { + this.columnBoundsCache.push({ + boundingClientRect: rect, + element: column, + date, + left: rect.left, + right: rect.right, + index: index++ + }); + } + }); + // Sorter efter x-position (fra venstre til højre) + this.columnBoundsCache.sort((a, b) => a.left - b.left); + } + /** + * Get column date from X coordinate using cached bounds + */ + static getColumnBounds(position) { + if (this.columnBoundsCache.length === 0) { + this.updateColumnBoundsCache(); + } + // Find den kolonne hvor x-koordinaten er indenfor grænserne + let column = this.columnBoundsCache.find(col => position.x >= col.left && position.x <= col.right); + if (column) + return column; + return null; + } + /** + * Get column bounds by Date + */ + static getColumnBoundsByDate(date) { + if (this.columnBoundsCache.length === 0) { + this.updateColumnBoundsCache(); + } + // Convert Date to YYYY-MM-DD format + let dateString = date.toISOString().split('T')[0]; + // Find column that matches the date + let column = this.columnBoundsCache.find(col => col.date === dateString); + return column || null; + } + static getColumns() { + return [...this.columnBoundsCache]; + } + static getHeaderColumns() { + let dayHeaders = []; + const dayColumns = document.querySelectorAll('swp-calendar-header swp-day-header'); + let index = 1; + // Cache hver kolonnes x-grænser + dayColumns.forEach(column => { + const rect = column.getBoundingClientRect(); + const date = column.dataset.date; + if (date) { + dayHeaders.push({ + boundingClientRect: rect, + element: column, + date, + left: rect.left, + right: rect.right, + index: index++ + }); + } + }); + // Sorter efter x-position (fra venstre til højre) + dayHeaders.sort((a, b) => a.left - b.left); + return dayHeaders; + } +} +ColumnDetectionUtils.columnBoundsCache = []; +//# sourceMappingURL=ColumnDetectionUtils.js.map \ No newline at end of file diff --git a/wwwroot/js/utils/ColumnDetectionUtils.js.map b/wwwroot/js/utils/ColumnDetectionUtils.js.map new file mode 100644 index 0000000..21aeb66 --- /dev/null +++ b/wwwroot/js/utils/ColumnDetectionUtils.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ColumnDetectionUtils.js","sourceRoot":"","sources":["../../../src/utils/ColumnDetectionUtils.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/utils/DateCalculator.d.ts b/wwwroot/js/utils/DateCalculator.d.ts new file mode 100644 index 0000000..74e3a54 --- /dev/null +++ b/wwwroot/js/utils/DateCalculator.d.ts @@ -0,0 +1,149 @@ +/** + * DateCalculator - Centralized date calculation logic for calendar + * Handles all date computations with proper week start handling + */ +import { CalendarConfig } from '../core/CalendarConfig'; +export declare class DateCalculator { + private static config; + /** + * Initialize DateCalculator with configuration + * @param config - Calendar configuration + */ + static initialize(config: CalendarConfig): void; + /** + * Validate that a date is valid + * @param date - Date to validate + * @param methodName - Name of calling method for error messages + * @throws Error if date is invalid + */ + private static validateDate; + /** + * Get dates for work week using ISO 8601 day numbering (Monday=1, Sunday=7) + * @param weekStart - Any date in the week + * @returns Array of dates for the configured work days + */ + static getWorkWeekDates(weekStart: Date): Date[]; + /** + * Get the start of the ISO week (Monday) for a given date + * @param date - Any date in the week + * @returns The Monday of the ISO week + */ + static getISOWeekStart(date: Date): Date; + /** + * Get the end of the ISO week for a given date + * @param date - Any date in the week + * @returns The end date of the ISO week (Sunday) + */ + static getWeekEnd(date: Date): Date; + /** + * Get week number for a date (ISO 8601) + * @param date - The date to get week number for + * @returns Week number (1-53) + */ + static getWeekNumber(date: Date): number; + /** + * Format a date range with customizable options + * @param start - Start date + * @param end - End date + * @param options - Formatting options + * @returns Formatted date range string + */ + static formatDateRange(start: Date, end: Date, options?: { + locale?: string; + month?: 'numeric' | '2-digit' | 'long' | 'short' | 'narrow'; + day?: 'numeric' | '2-digit'; + year?: 'numeric' | '2-digit'; + }): string; + /** + * Format a date to ISO date string (YYYY-MM-DD) + * @param date - Date to format + * @returns ISO date string + */ + static formatISODate(date: Date): string; + /** + * Check if a date is today + * @param date - Date to check + * @returns True if the date is today + */ + static isToday(date: Date): boolean; + /** + * Add days to a date + * @param date - Base date + * @param days - Number of days to add (can be negative) + * @returns New date + */ + static addDays(date: Date, days: number): Date; + /** + * Add weeks to a date + * @param date - Base date + * @param weeks - Number of weeks to add (can be negative) + * @returns New date + */ + static addWeeks(date: Date, weeks: number): Date; + /** + * Get all dates in a week + * @param weekStart - Start of the week + * @returns Array of 7 dates for the full week + */ + static getFullWeekDates(weekStart: Date): Date[]; + /** + * Get the day name for a date using Intl.DateTimeFormat + * @param date - Date to get day name for + * @param format - 'short' or 'long' + * @returns Day name + */ + static getDayName(date: Date, format?: 'short' | 'long'): string; + /** + * Format time to HH:MM + * @param date - Date to format + * @returns Time string + */ + static formatTime(date: Date): string; + /** + * Format time to 12-hour format + * @param date - Date to format + * @returns 12-hour time string + */ + static formatTime12(date: Date): string; + /** + * Convert minutes since midnight to time string + * @param minutes - Minutes since midnight + * @returns Time string + */ + static minutesToTime(minutes: number): string; + /** + * Convert time string to minutes since midnight + * @param timeStr - Time string + * @returns Minutes since midnight + */ + static timeToMinutes(timeStr: string): number; + /** + * Get minutes since start of day + * @param date - Date or ISO string + * @returns Minutes since midnight + */ + static getMinutesSinceMidnight(date: Date | string): number; + /** + * Calculate duration in minutes between two dates + * @param start - Start date or ISO string + * @param end - End date or ISO string + * @returns Duration in minutes + */ + static getDurationMinutes(start: Date | string, end: Date | string): number; + /** + * Check if two dates are on the same day + * @param date1 - First date + * @param date2 - Second date + * @returns True if same day + */ + static isSameDay(date1: Date, date2: Date): boolean; + /** + * Check if event spans multiple days + * @param start - Start date or ISO string + * @param end - End date or ISO string + * @returns True if spans multiple days + */ + static isMultiDay(start: Date | string, end: Date | string): boolean; + constructor(); +} +export declare function createDateCalculator(config: CalendarConfig): DateCalculator; diff --git a/wwwroot/js/utils/DateCalculator.js b/wwwroot/js/utils/DateCalculator.js new file mode 100644 index 0000000..4941202 --- /dev/null +++ b/wwwroot/js/utils/DateCalculator.js @@ -0,0 +1,260 @@ +/** + * DateCalculator - Centralized date calculation logic for calendar + * Handles all date computations with proper week start handling + */ +export class DateCalculator { + /** + * Initialize DateCalculator with configuration + * @param config - Calendar configuration + */ + static initialize(config) { + DateCalculator.config = config; + } + /** + * Validate that a date is valid + * @param date - Date to validate + * @param methodName - Name of calling method for error messages + * @throws Error if date is invalid + */ + static validateDate(date, methodName) { + if (!date || !(date instanceof Date) || isNaN(date.getTime())) { + throw new Error(`${methodName}: Invalid date provided - ${date}`); + } + } + /** + * Get dates for work week using ISO 8601 day numbering (Monday=1, Sunday=7) + * @param weekStart - Any date in the week + * @returns Array of dates for the configured work days + */ + static getWorkWeekDates(weekStart) { + DateCalculator.validateDate(weekStart, 'getWorkWeekDates'); + const dates = []; + const workWeekSettings = DateCalculator.config.getWorkWeekSettings(); + // Always use ISO week start (Monday) + const mondayOfWeek = DateCalculator.getISOWeekStart(weekStart); + // Calculate dates for each work day using ISO numbering + workWeekSettings.workDays.forEach(isoDay => { + const date = new Date(mondayOfWeek); + // ISO day 1=Monday is +0 days, ISO day 7=Sunday is +6 days + const daysFromMonday = isoDay === 7 ? 6 : isoDay - 1; + date.setDate(mondayOfWeek.getDate() + daysFromMonday); + dates.push(date); + }); + return dates; + } + /** + * Get the start of the ISO week (Monday) for a given date + * @param date - Any date in the week + * @returns The Monday of the ISO week + */ + static getISOWeekStart(date) { + DateCalculator.validateDate(date, 'getISOWeekStart'); + const monday = new Date(date); + const currentDay = monday.getDay(); + const daysToSubtract = currentDay === 0 ? 6 : currentDay - 1; + monday.setDate(monday.getDate() - daysToSubtract); + monday.setHours(0, 0, 0, 0); + return monday; + } + /** + * Get the end of the ISO week for a given date + * @param date - Any date in the week + * @returns The end date of the ISO week (Sunday) + */ + static getWeekEnd(date) { + DateCalculator.validateDate(date, 'getWeekEnd'); + const weekStart = DateCalculator.getISOWeekStart(date); + const weekEnd = new Date(weekStart); + weekEnd.setDate(weekStart.getDate() + 6); + weekEnd.setHours(23, 59, 59, 999); + return weekEnd; + } + /** + * Get week number for a date (ISO 8601) + * @param date - The date to get week number for + * @returns Week number (1-53) + */ + static getWeekNumber(date) { + const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())); + const dayNum = d.getUTCDay() || 7; + d.setUTCDate(d.getUTCDate() + 4 - dayNum); + const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1)); + return Math.ceil((((d.getTime() - yearStart.getTime()) / 86400000) + 1) / 7); + } + /** + * Format a date range with customizable options + * @param start - Start date + * @param end - End date + * @param options - Formatting options + * @returns Formatted date range string + */ + static formatDateRange(start, end, options = {}) { + const { locale = 'en-US', month = 'short', day = 'numeric' } = options; + const startYear = start.getFullYear(); + const endYear = end.getFullYear(); + const formatter = new Intl.DateTimeFormat(locale, { + month, + day, + year: startYear !== endYear ? 'numeric' : undefined + }); + // @ts-ignore + if (typeof formatter.formatRange === 'function') { + // @ts-ignore + return formatter.formatRange(start, end); + } + return `${formatter.format(start)} - ${formatter.format(end)}`; + } + /** + * Format a date to ISO date string (YYYY-MM-DD) + * @param date - Date to format + * @returns ISO date string + */ + static formatISODate(date) { + return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`; + } + /** + * Check if a date is today + * @param date - Date to check + * @returns True if the date is today + */ + static isToday(date) { + const today = new Date(); + return date.toDateString() === today.toDateString(); + } + /** + * Add days to a date + * @param date - Base date + * @param days - Number of days to add (can be negative) + * @returns New date + */ + static addDays(date, days) { + const result = new Date(date); + result.setDate(result.getDate() + days); + return result; + } + /** + * Add weeks to a date + * @param date - Base date + * @param weeks - Number of weeks to add (can be negative) + * @returns New date + */ + static addWeeks(date, weeks) { + return DateCalculator.addDays(date, weeks * 7); + } + /** + * Get all dates in a week + * @param weekStart - Start of the week + * @returns Array of 7 dates for the full week + */ + static getFullWeekDates(weekStart) { + const dates = []; + for (let i = 0; i < 7; i++) { + dates.push(DateCalculator.addDays(weekStart, i)); + } + return dates; + } + /** + * Get the day name for a date using Intl.DateTimeFormat + * @param date - Date to get day name for + * @param format - 'short' or 'long' + * @returns Day name + */ + static getDayName(date, format = 'short') { + const formatter = new Intl.DateTimeFormat('en-US', { + weekday: format + }); + return formatter.format(date); + } + /** + * Format time to HH:MM + * @param date - Date to format + * @returns Time string + */ + static formatTime(date) { + return `${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`; + } + /** + * Format time to 12-hour format + * @param date - Date to format + * @returns 12-hour time string + */ + static formatTime12(date) { + const hours = date.getHours(); + const minutes = date.getMinutes(); + const period = hours >= 12 ? 'PM' : 'AM'; + const displayHours = hours % 12 || 12; + return `${displayHours}:${String(minutes).padStart(2, '0')} ${period}`; + } + /** + * Convert minutes since midnight to time string + * @param minutes - Minutes since midnight + * @returns Time string + */ + static minutesToTime(minutes) { + const hours = Math.floor(minutes / 60); + const mins = minutes % 60; + const period = hours >= 12 ? 'PM' : 'AM'; + const displayHours = hours % 12 || 12; + return `${displayHours}:${String(mins).padStart(2, '0')} ${period}`; + } + /** + * Convert time string to minutes since midnight + * @param timeStr - Time string + * @returns Minutes since midnight + */ + static timeToMinutes(timeStr) { + const [time] = timeStr.split('T').pop().split('.'); + const [hours, minutes] = time.split(':').map(Number); + return hours * 60 + minutes; + } + /** + * Get minutes since start of day + * @param date - Date or ISO string + * @returns Minutes since midnight + */ + static getMinutesSinceMidnight(date) { + const d = typeof date === 'string' ? new Date(date) : date; + return d.getHours() * 60 + d.getMinutes(); + } + /** + * Calculate duration in minutes between two dates + * @param start - Start date or ISO string + * @param end - End date or ISO string + * @returns Duration in minutes + */ + static getDurationMinutes(start, end) { + const startDate = typeof start === 'string' ? new Date(start) : start; + const endDate = typeof end === 'string' ? new Date(end) : end; + return Math.floor((endDate.getTime() - startDate.getTime()) / 60000); + } + /** + * Check if two dates are on the same day + * @param date1 - First date + * @param date2 - Second date + * @returns True if same day + */ + static isSameDay(date1, date2) { + return date1.toDateString() === date2.toDateString(); + } + /** + * Check if event spans multiple days + * @param start - Start date or ISO string + * @param end - End date or ISO string + * @returns True if spans multiple days + */ + static isMultiDay(start, end) { + const startDate = typeof start === 'string' ? new Date(start) : start; + const endDate = typeof end === 'string' ? new Date(end) : end; + return !DateCalculator.isSameDay(startDate, endDate); + } + // Legacy constructor for backward compatibility + constructor() { + // Empty constructor - all methods are now static + } +} +// Legacy factory function - deprecated, use static methods instead +export function createDateCalculator(config) { + DateCalculator.initialize(config); + return new DateCalculator(); +} +//# sourceMappingURL=DateCalculator.js.map \ No newline at end of file diff --git a/wwwroot/js/utils/DateCalculator.js.map b/wwwroot/js/utils/DateCalculator.js.map new file mode 100644 index 0000000..b21a322 --- /dev/null +++ b/wwwroot/js/utils/DateCalculator.js.map @@ -0,0 +1 @@ +{"version":3,"file":"DateCalculator.js","sourceRoot":"","sources":["../../../src/utils/DateCalculator.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/utils/DateService.d.ts b/wwwroot/js/utils/DateService.d.ts new file mode 100644 index 0000000..3d26c55 --- /dev/null +++ b/wwwroot/js/utils/DateService.d.ts @@ -0,0 +1,254 @@ +/** + * DateService - Unified date/time service using day.js + * Handles all date operations, timezone conversions, and formatting + */ +import { Configuration } from '../configurations/CalendarConfig'; +export declare class DateService { + private timezone; + constructor(config: Configuration); + /** + * Convert local date to UTC ISO string + * @param localDate - Date in local timezone + * @returns ISO string in UTC (with 'Z' suffix) + */ + toUTC(localDate: Date): string; + /** + * Convert UTC ISO string to local date + * @param utcString - ISO string in UTC + * @returns Date in local timezone + */ + fromUTC(utcString: string): Date; + /** + * Format time as HH:mm or HH:mm:ss + * @param date - Date to format + * @param showSeconds - Include seconds in output + * @returns Formatted time string + */ + formatTime(date: Date, showSeconds?: boolean): string; + /** + * Format time range as "HH:mm - HH:mm" + * @param start - Start date + * @param end - End date + * @returns Formatted time range + */ + formatTimeRange(start: Date, end: Date): string; + /** + * Format date and time in technical format: yyyy-MM-dd HH:mm:ss + * @param date - Date to format + * @returns Technical datetime string + */ + formatTechnicalDateTime(date: Date): string; + /** + * Format date as yyyy-MM-dd + * @param date - Date to format + * @returns ISO date string + */ + formatDate(date: Date): string; + /** + * Format date as "Month Year" (e.g., "January 2025") + * @param date - Date to format + * @param locale - Locale for month name (default: 'en-US') + * @returns Formatted month and year + */ + formatMonthYear(date: Date, locale?: string): string; + /** + * Format date as ISO string (same as formatDate for compatibility) + * @param date - Date to format + * @returns ISO date string + */ + formatISODate(date: Date): string; + /** + * Format time in 12-hour format with AM/PM + * @param date - Date to format + * @returns Time string in 12-hour format (e.g., "2:30 PM") + */ + formatTime12(date: Date): string; + /** + * Get day name for a date + * @param date - Date to get day name for + * @param format - 'short' (e.g., 'Mon') or 'long' (e.g., 'Monday') + * @param locale - Locale for day name (default: 'da-DK') + * @returns Day name + */ + getDayName(date: Date, format?: 'short' | 'long', locale?: string): string; + /** + * Format a date range with customizable options + * @param start - Start date + * @param end - End date + * @param options - Formatting options + * @returns Formatted date range string + */ + formatDateRange(start: Date, end: Date, options?: { + locale?: string; + month?: 'numeric' | '2-digit' | 'long' | 'short' | 'narrow'; + day?: 'numeric' | '2-digit'; + year?: 'numeric' | '2-digit'; + }): string; + /** + * Convert time string (HH:mm or HH:mm:ss) to total minutes since midnight + * @param timeString - Time in format HH:mm or HH:mm:ss + * @returns Total minutes since midnight + */ + timeToMinutes(timeString: string): number; + /** + * Convert total minutes since midnight to time string HH:mm + * @param totalMinutes - Minutes since midnight + * @returns Time string in format HH:mm + */ + minutesToTime(totalMinutes: number): string; + /** + * Format time from total minutes (alias for minutesToTime) + * @param totalMinutes - Minutes since midnight + * @returns Time string in format HH:mm + */ + formatTimeFromMinutes(totalMinutes: number): string; + /** + * Get minutes since midnight for a given date + * @param date - Date to calculate from + * @returns Minutes since midnight + */ + getMinutesSinceMidnight(date: Date): number; + /** + * Calculate duration in minutes between two dates + * @param start - Start date or ISO string + * @param end - End date or ISO string + * @returns Duration in minutes + */ + getDurationMinutes(start: Date | string, end: Date | string): number; + /** + * Get start and end of week (Monday to Sunday) + * @param date - Reference date + * @returns Object with start and end dates + */ + getWeekBounds(date: Date): { + start: Date; + end: Date; + }; + /** + * Add weeks to a date + * @param date - Base date + * @param weeks - Number of weeks to add (can be negative) + * @returns New date + */ + addWeeks(date: Date, weeks: number): Date; + /** + * Add months to a date + * @param date - Base date + * @param months - Number of months to add (can be negative) + * @returns New date + */ + addMonths(date: Date, months: number): Date; + /** + * Get ISO week number (1-53) + * @param date - Date to get week number for + * @returns ISO week number + */ + getWeekNumber(date: Date): number; + /** + * Get all dates in a full week (7 days starting from given date) + * @param weekStart - Start date of the week + * @returns Array of 7 dates + */ + getFullWeekDates(weekStart: Date): Date[]; + /** + * Get dates for work week using ISO 8601 day numbering (Monday=1, Sunday=7) + * @param weekStart - Any date in the week + * @param workDays - Array of ISO day numbers (1=Monday, 7=Sunday) + * @returns Array of dates for the specified work days + */ + getWorkWeekDates(weekStart: Date, workDays: number[]): Date[]; + /** + * Create a date at a specific time (minutes since midnight) + * @param baseDate - Base date (date component) + * @param totalMinutes - Minutes since midnight + * @returns New date with specified time + */ + createDateAtTime(baseDate: Date, totalMinutes: number): Date; + /** + * Snap date to nearest interval + * @param date - Date to snap + * @param intervalMinutes - Snap interval in minutes + * @returns Snapped date + */ + snapToInterval(date: Date, intervalMinutes: number): Date; + /** + * Check if two dates are the same day + * @param date1 - First date + * @param date2 - Second date + * @returns True if same day + */ + isSameDay(date1: Date, date2: Date): boolean; + /** + * Get start of day + * @param date - Date + * @returns Start of day (00:00:00) + */ + startOfDay(date: Date): Date; + /** + * Get end of day + * @param date - Date + * @returns End of day (23:59:59.999) + */ + endOfDay(date: Date): Date; + /** + * Add days to a date + * @param date - Base date + * @param days - Number of days to add (can be negative) + * @returns New date + */ + addDays(date: Date, days: number): Date; + /** + * Add minutes to a date + * @param date - Base date + * @param minutes - Number of minutes to add (can be negative) + * @returns New date + */ + addMinutes(date: Date, minutes: number): Date; + /** + * Parse ISO string to date + * @param isoString - ISO date string + * @returns Parsed date + */ + parseISO(isoString: string): Date; + /** + * Check if date is valid + * @param date - Date to check + * @returns True if valid + */ + isValid(date: Date): boolean; + /** + * Calculate difference in calendar days between two dates + * @param date1 - First date + * @param date2 - Second date + * @returns Number of calendar days between dates (can be negative) + */ + differenceInCalendarDays(date1: Date, date2: Date): number; + /** + * Validate date range (start must be before or equal to end) + * @param start - Start date + * @param end - End date + * @returns True if valid range + */ + isValidRange(start: Date, end: Date): boolean; + /** + * Check if date is within reasonable bounds (1900-2100) + * @param date - Date to check + * @returns True if within bounds + */ + isWithinBounds(date: Date): boolean; + /** + * Validate date with comprehensive checks + * @param date - Date to validate + * @param options - Validation options + * @returns Validation result with error message + */ + validateDate(date: Date, options?: { + requireFuture?: boolean; + requirePast?: boolean; + minDate?: Date; + maxDate?: Date; + }): { + valid: boolean; + error?: string; + }; +} diff --git a/wwwroot/js/utils/DateService.js b/wwwroot/js/utils/DateService.js new file mode 100644 index 0000000..a3eb134 --- /dev/null +++ b/wwwroot/js/utils/DateService.js @@ -0,0 +1,418 @@ +/** + * DateService - Unified date/time service using day.js + * Handles all date operations, timezone conversions, and formatting + */ +import dayjs from 'dayjs'; +import utc from 'dayjs/plugin/utc'; +import timezone from 'dayjs/plugin/timezone'; +import isoWeek from 'dayjs/plugin/isoWeek'; +import customParseFormat from 'dayjs/plugin/customParseFormat'; +import isSameOrAfter from 'dayjs/plugin/isSameOrAfter'; +import isSameOrBefore from 'dayjs/plugin/isSameOrBefore'; +// Enable day.js plugins +dayjs.extend(utc); +dayjs.extend(timezone); +dayjs.extend(isoWeek); +dayjs.extend(customParseFormat); +dayjs.extend(isSameOrAfter); +dayjs.extend(isSameOrBefore); +export class DateService { + constructor(config) { + this.timezone = config.timeFormatConfig.timezone; + } + // ============================================ + // CORE CONVERSIONS + // ============================================ + /** + * Convert local date to UTC ISO string + * @param localDate - Date in local timezone + * @returns ISO string in UTC (with 'Z' suffix) + */ + toUTC(localDate) { + return dayjs.tz(localDate, this.timezone).utc().toISOString(); + } + /** + * Convert UTC ISO string to local date + * @param utcString - ISO string in UTC + * @returns Date in local timezone + */ + fromUTC(utcString) { + return dayjs.utc(utcString).tz(this.timezone).toDate(); + } + // ============================================ + // FORMATTING + // ============================================ + /** + * Format time as HH:mm or HH:mm:ss + * @param date - Date to format + * @param showSeconds - Include seconds in output + * @returns Formatted time string + */ + formatTime(date, showSeconds = false) { + const pattern = showSeconds ? 'HH:mm:ss' : 'HH:mm'; + return dayjs(date).format(pattern); + } + /** + * Format time range as "HH:mm - HH:mm" + * @param start - Start date + * @param end - End date + * @returns Formatted time range + */ + formatTimeRange(start, end) { + return `${this.formatTime(start)} - ${this.formatTime(end)}`; + } + /** + * Format date and time in technical format: yyyy-MM-dd HH:mm:ss + * @param date - Date to format + * @returns Technical datetime string + */ + formatTechnicalDateTime(date) { + return dayjs(date).format('YYYY-MM-DD HH:mm:ss'); + } + /** + * Format date as yyyy-MM-dd + * @param date - Date to format + * @returns ISO date string + */ + formatDate(date) { + return dayjs(date).format('YYYY-MM-DD'); + } + /** + * Format date as "Month Year" (e.g., "January 2025") + * @param date - Date to format + * @param locale - Locale for month name (default: 'en-US') + * @returns Formatted month and year + */ + formatMonthYear(date, locale = 'en-US') { + return date.toLocaleDateString(locale, { month: 'long', year: 'numeric' }); + } + /** + * Format date as ISO string (same as formatDate for compatibility) + * @param date - Date to format + * @returns ISO date string + */ + formatISODate(date) { + return this.formatDate(date); + } + /** + * Format time in 12-hour format with AM/PM + * @param date - Date to format + * @returns Time string in 12-hour format (e.g., "2:30 PM") + */ + formatTime12(date) { + return dayjs(date).format('h:mm A'); + } + /** + * Get day name for a date + * @param date - Date to get day name for + * @param format - 'short' (e.g., 'Mon') or 'long' (e.g., 'Monday') + * @param locale - Locale for day name (default: 'da-DK') + * @returns Day name + */ + getDayName(date, format = 'short', locale = 'da-DK') { + const formatter = new Intl.DateTimeFormat(locale, { + weekday: format + }); + return formatter.format(date); + } + /** + * Format a date range with customizable options + * @param start - Start date + * @param end - End date + * @param options - Formatting options + * @returns Formatted date range string + */ + formatDateRange(start, end, options = {}) { + const { locale = 'en-US', month = 'short', day = 'numeric' } = options; + const startYear = start.getFullYear(); + const endYear = end.getFullYear(); + const formatter = new Intl.DateTimeFormat(locale, { + month, + day, + year: startYear !== endYear ? 'numeric' : undefined + }); + // @ts-ignore - formatRange is available in modern browsers + if (typeof formatter.formatRange === 'function') { + // @ts-ignore + return formatter.formatRange(start, end); + } + return `${formatter.format(start)} - ${formatter.format(end)}`; + } + // ============================================ + // TIME CALCULATIONS + // ============================================ + /** + * Convert time string (HH:mm or HH:mm:ss) to total minutes since midnight + * @param timeString - Time in format HH:mm or HH:mm:ss + * @returns Total minutes since midnight + */ + timeToMinutes(timeString) { + const parts = timeString.split(':').map(Number); + const hours = parts[0] || 0; + const minutes = parts[1] || 0; + return hours * 60 + minutes; + } + /** + * Convert total minutes since midnight to time string HH:mm + * @param totalMinutes - Minutes since midnight + * @returns Time string in format HH:mm + */ + minutesToTime(totalMinutes) { + const hours = Math.floor(totalMinutes / 60); + const minutes = totalMinutes % 60; + return dayjs().hour(hours).minute(minutes).format('HH:mm'); + } + /** + * Format time from total minutes (alias for minutesToTime) + * @param totalMinutes - Minutes since midnight + * @returns Time string in format HH:mm + */ + formatTimeFromMinutes(totalMinutes) { + return this.minutesToTime(totalMinutes); + } + /** + * Get minutes since midnight for a given date + * @param date - Date to calculate from + * @returns Minutes since midnight + */ + getMinutesSinceMidnight(date) { + const d = dayjs(date); + return d.hour() * 60 + d.minute(); + } + /** + * Calculate duration in minutes between two dates + * @param start - Start date or ISO string + * @param end - End date or ISO string + * @returns Duration in minutes + */ + getDurationMinutes(start, end) { + const startDate = dayjs(start); + const endDate = dayjs(end); + return endDate.diff(startDate, 'minute'); + } + // ============================================ + // WEEK OPERATIONS + // ============================================ + /** + * Get start and end of week (Monday to Sunday) + * @param date - Reference date + * @returns Object with start and end dates + */ + getWeekBounds(date) { + const d = dayjs(date); + return { + start: d.startOf('week').add(1, 'day').toDate(), // Monday (day.js week starts on Sunday) + end: d.endOf('week').add(1, 'day').toDate() // Sunday + }; + } + /** + * Add weeks to a date + * @param date - Base date + * @param weeks - Number of weeks to add (can be negative) + * @returns New date + */ + addWeeks(date, weeks) { + return dayjs(date).add(weeks, 'week').toDate(); + } + /** + * Add months to a date + * @param date - Base date + * @param months - Number of months to add (can be negative) + * @returns New date + */ + addMonths(date, months) { + return dayjs(date).add(months, 'month').toDate(); + } + /** + * Get ISO week number (1-53) + * @param date - Date to get week number for + * @returns ISO week number + */ + getWeekNumber(date) { + return dayjs(date).isoWeek(); + } + /** + * Get all dates in a full week (7 days starting from given date) + * @param weekStart - Start date of the week + * @returns Array of 7 dates + */ + getFullWeekDates(weekStart) { + const dates = []; + for (let i = 0; i < 7; i++) { + dates.push(this.addDays(weekStart, i)); + } + return dates; + } + /** + * Get dates for work week using ISO 8601 day numbering (Monday=1, Sunday=7) + * @param weekStart - Any date in the week + * @param workDays - Array of ISO day numbers (1=Monday, 7=Sunday) + * @returns Array of dates for the specified work days + */ + getWorkWeekDates(weekStart, workDays) { + const dates = []; + // Get Monday of the week + const weekBounds = this.getWeekBounds(weekStart); + const mondayOfWeek = this.startOfDay(weekBounds.start); + // Calculate dates for each work day using ISO numbering + workDays.forEach(isoDay => { + const date = new Date(mondayOfWeek); + // ISO day 1=Monday is +0 days, ISO day 7=Sunday is +6 days + const daysFromMonday = isoDay === 7 ? 6 : isoDay - 1; + date.setDate(mondayOfWeek.getDate() + daysFromMonday); + dates.push(date); + }); + return dates; + } + // ============================================ + // GRID HELPERS + // ============================================ + /** + * Create a date at a specific time (minutes since midnight) + * @param baseDate - Base date (date component) + * @param totalMinutes - Minutes since midnight + * @returns New date with specified time + */ + createDateAtTime(baseDate, totalMinutes) { + const hours = Math.floor(totalMinutes / 60); + const minutes = totalMinutes % 60; + return dayjs(baseDate).startOf('day').hour(hours).minute(minutes).toDate(); + } + /** + * Snap date to nearest interval + * @param date - Date to snap + * @param intervalMinutes - Snap interval in minutes + * @returns Snapped date + */ + snapToInterval(date, intervalMinutes) { + const minutes = this.getMinutesSinceMidnight(date); + const snappedMinutes = Math.round(minutes / intervalMinutes) * intervalMinutes; + return this.createDateAtTime(date, snappedMinutes); + } + // ============================================ + // UTILITY METHODS + // ============================================ + /** + * Check if two dates are the same day + * @param date1 - First date + * @param date2 - Second date + * @returns True if same day + */ + isSameDay(date1, date2) { + return dayjs(date1).isSame(date2, 'day'); + } + /** + * Get start of day + * @param date - Date + * @returns Start of day (00:00:00) + */ + startOfDay(date) { + return dayjs(date).startOf('day').toDate(); + } + /** + * Get end of day + * @param date - Date + * @returns End of day (23:59:59.999) + */ + endOfDay(date) { + return dayjs(date).endOf('day').toDate(); + } + /** + * Add days to a date + * @param date - Base date + * @param days - Number of days to add (can be negative) + * @returns New date + */ + addDays(date, days) { + return dayjs(date).add(days, 'day').toDate(); + } + /** + * Add minutes to a date + * @param date - Base date + * @param minutes - Number of minutes to add (can be negative) + * @returns New date + */ + addMinutes(date, minutes) { + return dayjs(date).add(minutes, 'minute').toDate(); + } + /** + * Parse ISO string to date + * @param isoString - ISO date string + * @returns Parsed date + */ + parseISO(isoString) { + return dayjs(isoString).toDate(); + } + /** + * Check if date is valid + * @param date - Date to check + * @returns True if valid + */ + isValid(date) { + return dayjs(date).isValid(); + } + /** + * Calculate difference in calendar days between two dates + * @param date1 - First date + * @param date2 - Second date + * @returns Number of calendar days between dates (can be negative) + */ + differenceInCalendarDays(date1, date2) { + const d1 = dayjs(date1).startOf('day'); + const d2 = dayjs(date2).startOf('day'); + return d1.diff(d2, 'day'); + } + /** + * Validate date range (start must be before or equal to end) + * @param start - Start date + * @param end - End date + * @returns True if valid range + */ + isValidRange(start, end) { + if (!this.isValid(start) || !this.isValid(end)) { + return false; + } + return start.getTime() <= end.getTime(); + } + /** + * Check if date is within reasonable bounds (1900-2100) + * @param date - Date to check + * @returns True if within bounds + */ + isWithinBounds(date) { + if (!this.isValid(date)) { + return false; + } + const year = date.getFullYear(); + return year >= 1900 && year <= 2100; + } + /** + * Validate date with comprehensive checks + * @param date - Date to validate + * @param options - Validation options + * @returns Validation result with error message + */ + validateDate(date, options = {}) { + if (!this.isValid(date)) { + return { valid: false, error: 'Invalid date' }; + } + if (!this.isWithinBounds(date)) { + return { valid: false, error: 'Date out of bounds (1900-2100)' }; + } + const now = new Date(); + if (options.requireFuture && date <= now) { + return { valid: false, error: 'Date must be in the future' }; + } + if (options.requirePast && date >= now) { + return { valid: false, error: 'Date must be in the past' }; + } + if (options.minDate && date < options.minDate) { + return { valid: false, error: `Date must be after ${this.formatDate(options.minDate)}` }; + } + if (options.maxDate && date > options.maxDate) { + return { valid: false, error: `Date must be before ${this.formatDate(options.maxDate)}` }; + } + return { valid: true }; + } +} +//# sourceMappingURL=DateService.js.map \ No newline at end of file diff --git a/wwwroot/js/utils/DateService.js.map b/wwwroot/js/utils/DateService.js.map new file mode 100644 index 0000000..e976a16 --- /dev/null +++ b/wwwroot/js/utils/DateService.js.map @@ -0,0 +1 @@ +{"version":3,"file":"DateService.js","sourceRoot":"","sources":["../../../src/utils/DateService.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/utils/OverlapDetector.d.ts b/wwwroot/js/utils/OverlapDetector.d.ts new file mode 100644 index 0000000..b4a6994 --- /dev/null +++ b/wwwroot/js/utils/OverlapDetector.d.ts @@ -0,0 +1,33 @@ +/** + * OverlapDetector - Ren tidbaseret overlap detection + * Ingen DOM manipulation, kun tidsberegninger + */ +import { CalendarEvent } from '../types/CalendarTypes'; +export type EventId = string & { + readonly __brand: 'EventId'; +}; +export type OverlapResult = { + overlappingEvents: CalendarEvent[]; + stackLinks: Map; +}; +export interface StackLink { + prev?: EventId; + next?: EventId; + stackLevel: number; +} +export declare class OverlapDetector { + /** + * Resolver hvilke events et givent event overlapper med i en kolonne + * @param event - CalendarEvent der skal checkes for overlap + * @param columnEvents - Array af CalendarEvent objekter i kolonnen + * @returns Array af events som det givne event overlapper med + */ + resolveOverlap(event: CalendarEvent, columnEvents: CalendarEvent[]): CalendarEvent[]; + /** + * Dekorerer events med stack linking data + * @param newEvent - Det nye event der skal tilføjes + * @param overlappingEvents - Events som det nye event overlapper med + * @returns OverlapResult med overlappende events og stack links + */ + decorateWithStackLinks(newEvent: CalendarEvent, overlappingEvents: CalendarEvent[]): OverlapResult; +} diff --git a/wwwroot/js/utils/OverlapDetector.js b/wwwroot/js/utils/OverlapDetector.js new file mode 100644 index 0000000..09ddd6b --- /dev/null +++ b/wwwroot/js/utils/OverlapDetector.js @@ -0,0 +1,52 @@ +/** + * OverlapDetector - Ren tidbaseret overlap detection + * Ingen DOM manipulation, kun tidsberegninger + */ +export class OverlapDetector { + /** + * Resolver hvilke events et givent event overlapper med i en kolonne + * @param event - CalendarEvent der skal checkes for overlap + * @param columnEvents - Array af CalendarEvent objekter i kolonnen + * @returns Array af events som det givne event overlapper med + */ + resolveOverlap(event, columnEvents) { + return columnEvents.filter(existingEvent => { + // To events overlapper hvis: + // event starter før existing slutter OG + // event slutter efter existing starter + return event.start < existingEvent.end && event.end > existingEvent.start; + }); + } + /** + * Dekorerer events med stack linking data + * @param newEvent - Det nye event der skal tilføjes + * @param overlappingEvents - Events som det nye event overlapper med + * @returns OverlapResult med overlappende events og stack links + */ + decorateWithStackLinks(newEvent, overlappingEvents) { + const stackLinks = new Map(); + if (overlappingEvents.length === 0) { + return { + overlappingEvents: [], + stackLinks + }; + } + // Kombiner nyt event med eksisterende og sortér efter start tid (tidligste første) + const allEvents = [...overlappingEvents, newEvent].sort((a, b) => a.start.getTime() - b.start.getTime()); + // Opret sammenhængende kæde - alle events bindes sammen + allEvents.forEach((event, index) => { + const stackLink = { + stackLevel: index, + prev: index > 0 ? allEvents[index - 1].id : undefined, + next: index < allEvents.length - 1 ? allEvents[index + 1].id : undefined + }; + stackLinks.set(event.id, stackLink); + }); + overlappingEvents.push(newEvent); + return { + overlappingEvents, + stackLinks + }; + } +} +//# sourceMappingURL=OverlapDetector.js.map \ No newline at end of file diff --git a/wwwroot/js/utils/OverlapDetector.js.map b/wwwroot/js/utils/OverlapDetector.js.map new file mode 100644 index 0000000..941cbb5 --- /dev/null +++ b/wwwroot/js/utils/OverlapDetector.js.map @@ -0,0 +1 @@ +{"version":3,"file":"OverlapDetector.js","sourceRoot":"","sources":["../../../src/utils/OverlapDetector.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAkBH,MAAM,OAAO,eAAe;IAE1B;;;;;OAKG;IACI,cAAc,CAAC,KAAoB,EAAE,YAA6B;QACvE,OAAO,YAAY,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE;YACzC,6BAA6B;YAC7B,wCAAwC;YACxC,uCAAuC;YACvC,OAAO,KAAK,CAAC,KAAK,GAAG,aAAa,CAAC,GAAG,IAAI,KAAK,CAAC,GAAG,GAAG,aAAa,CAAC,KAAK,CAAC;QAC5E,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;;OAKG;IACI,sBAAsB,CAAC,QAAuB,EAAE,iBAAkC;QACvF,MAAM,UAAU,GAAG,IAAI,GAAG,EAAsB,CAAC;QAEjD,IAAI,iBAAiB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACnC,OAAO;gBACL,iBAAiB,EAAE,EAAE;gBACrB,UAAU;aACX,CAAC;QACJ,CAAC;QAED,mFAAmF;QACnF,MAAM,SAAS,GAAG,CAAC,GAAG,iBAAiB,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAC/D,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,CACtC,CAAC;QAEF,wDAAwD;QACxD,SAAS,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;YACjC,MAAM,SAAS,GAAc;gBAC3B,UAAU,EAAE,KAAK;gBACjB,IAAI,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,EAAa,CAAC,CAAC,CAAC,SAAS;gBAChE,IAAI,EAAE,KAAK,GAAG,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,EAAa,CAAC,CAAC,CAAC,SAAS;aACpF,CAAC;YACF,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,EAAa,EAAE,SAAS,CAAC,CAAC;QACjD,CAAC,CAAC,CAAC;QACH,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACjC,OAAO;YACL,iBAAiB;YACjB,UAAU;SACX,CAAC;IACJ,CAAC;CACF"} \ No newline at end of file diff --git a/wwwroot/js/utils/PositionUtils.d.ts b/wwwroot/js/utils/PositionUtils.d.ts new file mode 100644 index 0000000..8171427 --- /dev/null +++ b/wwwroot/js/utils/PositionUtils.d.ts @@ -0,0 +1,101 @@ +import { Configuration } from '../configurations/CalendarConfig'; +import { IColumnBounds } from './ColumnDetectionUtils'; +import { DateService } from './DateService'; +/** + * PositionUtils - Positioning utilities with dependency injection + * Focuses on pixel/position calculations while delegating date operations + * + * Note: Uses DateService with date-fns for all date/time operations + */ +export declare class PositionUtils { + private dateService; + private config; + constructor(dateService: DateService, config: Configuration); + /** + * Convert minutes to pixels + */ + minutesToPixels(minutes: number): number; + /** + * Convert pixels to minutes + */ + pixelsToMinutes(pixels: number): number; + /** + * Convert time (HH:MM) to pixels from day start using DateService + */ + timeToPixels(timeString: string): number; + /** + * Convert Date object to pixels from day start using DateService + */ + dateToPixels(date: Date): number; + /** + * Convert pixels to time using DateService + */ + pixelsToTime(pixels: number): string; + /** + * Beregn event position og størrelse + */ + calculateEventPosition(startTime: string | Date, endTime: string | Date): { + top: number; + height: number; + duration: number; + }; + /** + * Snap position til grid interval + */ + snapToGrid(pixels: number): number; + /** + * Snap time to interval using DateService + */ + snapTimeToInterval(timeString: string): string; + /** + * Beregn kolonne position for overlappende events + */ + calculateColumnPosition(eventIndex: number, totalColumns: number, containerWidth: number): { + left: number; + width: number; + }; + /** + * Check om to events overlapper i tid + */ + eventsOverlap(start1: string | Date, end1: string | Date, start2: string | Date, end2: string | Date): boolean; + /** + * Beregn Y position fra mouse/touch koordinat + */ + getPositionFromCoordinate(clientY: number, column: IColumnBounds): number; + /** + * Valider at tid er inden for arbejdstimer + */ + isWithinWorkHours(timeString: string): boolean; + /** + * Valider at tid er inden for dag grænser + */ + isWithinDayBounds(timeString: string): boolean; + /** + * Hent minimum event højde i pixels + */ + getMinimumEventHeight(): number; + /** + * Hent maksimum event højde i pixels (hele dagen) + */ + getMaximumEventHeight(): number; + /** + * Beregn total kalender højde + */ + getTotalCalendarHeight(): number; + /** + * Convert ISO datetime to time string with UTC-to-local conversion + */ + isoToTimeString(isoString: string): string; + /** + * Convert time string to ISO datetime using DateService with timezone handling + */ + timeStringToIso(timeString: string, date?: Date): string; + /** + * Calculate event duration using DateService + */ + calculateDuration(startTime: string | Date, endTime: string | Date): number; + /** + * Format duration to readable text (Danish) + */ + formatDuration(minutes: number): string; +} diff --git a/wwwroot/js/utils/PositionUtils.js b/wwwroot/js/utils/PositionUtils.js new file mode 100644 index 0000000..9dd1956 --- /dev/null +++ b/wwwroot/js/utils/PositionUtils.js @@ -0,0 +1,209 @@ +import { TimeFormatter } from './TimeFormatter'; +/** + * PositionUtils - Positioning utilities with dependency injection + * Focuses on pixel/position calculations while delegating date operations + * + * Note: Uses DateService with date-fns for all date/time operations + */ +export class PositionUtils { + constructor(dateService, config) { + this.dateService = dateService; + this.config = config; + } + /** + * Convert minutes to pixels + */ + minutesToPixels(minutes) { + const gridSettings = this.config.gridSettings; + const pixelsPerHour = gridSettings.hourHeight; + return (minutes / 60) * pixelsPerHour; + } + /** + * Convert pixels to minutes + */ + pixelsToMinutes(pixels) { + const gridSettings = this.config.gridSettings; + const pixelsPerHour = gridSettings.hourHeight; + return (pixels / pixelsPerHour) * 60; + } + /** + * Convert time (HH:MM) to pixels from day start using DateService + */ + timeToPixels(timeString) { + const totalMinutes = this.dateService.timeToMinutes(timeString); + const gridSettings = this.config.gridSettings; + const dayStartMinutes = gridSettings.dayStartHour * 60; + const minutesFromDayStart = totalMinutes - dayStartMinutes; + return this.minutesToPixels(minutesFromDayStart); + } + /** + * Convert Date object to pixels from day start using DateService + */ + dateToPixels(date) { + const totalMinutes = this.dateService.getMinutesSinceMidnight(date); + const gridSettings = this.config.gridSettings; + const dayStartMinutes = gridSettings.dayStartHour * 60; + const minutesFromDayStart = totalMinutes - dayStartMinutes; + return this.minutesToPixels(minutesFromDayStart); + } + /** + * Convert pixels to time using DateService + */ + pixelsToTime(pixels) { + const minutes = this.pixelsToMinutes(pixels); + const gridSettings = this.config.gridSettings; + const dayStartMinutes = gridSettings.dayStartHour * 60; + const totalMinutes = dayStartMinutes + minutes; + return this.dateService.minutesToTime(totalMinutes); + } + /** + * Beregn event position og størrelse + */ + calculateEventPosition(startTime, endTime) { + let startPixels; + let endPixels; + if (typeof startTime === 'string') { + startPixels = this.timeToPixels(startTime); + } + else { + startPixels = this.dateToPixels(startTime); + } + if (typeof endTime === 'string') { + endPixels = this.timeToPixels(endTime); + } + else { + endPixels = this.dateToPixels(endTime); + } + const height = Math.max(endPixels - startPixels, this.getMinimumEventHeight()); + const duration = this.pixelsToMinutes(height); + return { + top: startPixels, + height, + duration + }; + } + /** + * Snap position til grid interval + */ + snapToGrid(pixels) { + const gridSettings = this.config.gridSettings; + const snapInterval = gridSettings.snapInterval; + const snapPixels = this.minutesToPixels(snapInterval); + return Math.round(pixels / snapPixels) * snapPixels; + } + /** + * Snap time to interval using DateService + */ + snapTimeToInterval(timeString) { + const totalMinutes = this.dateService.timeToMinutes(timeString); + const gridSettings = this.config.gridSettings; + const snapInterval = gridSettings.snapInterval; + const snappedMinutes = Math.round(totalMinutes / snapInterval) * snapInterval; + return this.dateService.minutesToTime(snappedMinutes); + } + /** + * Beregn kolonne position for overlappende events + */ + calculateColumnPosition(eventIndex, totalColumns, containerWidth) { + const columnWidth = containerWidth / totalColumns; + const left = eventIndex * columnWidth; + // Lav lidt margin mellem kolonnerne + const margin = 2; + const adjustedWidth = columnWidth - margin; + return { + left: left + (margin / 2), + width: Math.max(adjustedWidth, 50) // Minimum width + }; + } + /** + * Check om to events overlapper i tid + */ + eventsOverlap(start1, end1, start2, end2) { + const pos1 = this.calculateEventPosition(start1, end1); + const pos2 = this.calculateEventPosition(start2, end2); + const event1End = pos1.top + pos1.height; + const event2End = pos2.top + pos2.height; + return !(event1End <= pos2.top || event2End <= pos1.top); + } + /** + * Beregn Y position fra mouse/touch koordinat + */ + getPositionFromCoordinate(clientY, column) { + const relativeY = clientY - column.boundingClientRect.top; + // Snap til grid + return this.snapToGrid(relativeY); + } + /** + * Valider at tid er inden for arbejdstimer + */ + isWithinWorkHours(timeString) { + const [hours] = timeString.split(':').map(Number); + const gridSettings = this.config.gridSettings; + return hours >= gridSettings.workStartHour && hours < gridSettings.workEndHour; + } + /** + * Valider at tid er inden for dag grænser + */ + isWithinDayBounds(timeString) { + const [hours] = timeString.split(':').map(Number); + const gridSettings = this.config.gridSettings; + return hours >= gridSettings.dayStartHour && hours < gridSettings.dayEndHour; + } + /** + * Hent minimum event højde i pixels + */ + getMinimumEventHeight() { + // Minimum 15 minutter + return this.minutesToPixels(15); + } + /** + * Hent maksimum event højde i pixels (hele dagen) + */ + getMaximumEventHeight() { + const gridSettings = this.config.gridSettings; + const dayDurationHours = gridSettings.dayEndHour - gridSettings.dayStartHour; + return dayDurationHours * gridSettings.hourHeight; + } + /** + * Beregn total kalender højde + */ + getTotalCalendarHeight() { + return this.getMaximumEventHeight(); + } + /** + * Convert ISO datetime to time string with UTC-to-local conversion + */ + isoToTimeString(isoString) { + const date = new Date(isoString); + return TimeFormatter.formatTime(date); + } + /** + * Convert time string to ISO datetime using DateService with timezone handling + */ + timeStringToIso(timeString, date = new Date()) { + const totalMinutes = this.dateService.timeToMinutes(timeString); + const newDate = this.dateService.createDateAtTime(date, totalMinutes); + return this.dateService.toUTC(newDate); + } + /** + * Calculate event duration using DateService + */ + calculateDuration(startTime, endTime) { + return this.dateService.getDurationMinutes(startTime, endTime); + } + /** + * Format duration to readable text (Danish) + */ + formatDuration(minutes) { + if (minutes < 60) { + return `${minutes} min`; + } + const hours = Math.floor(minutes / 60); + const remainingMinutes = minutes % 60; + if (remainingMinutes === 0) { + return `${hours} time${hours !== 1 ? 'r' : ''}`; + } + return `${hours}t ${remainingMinutes}m`; + } +} +//# sourceMappingURL=PositionUtils.js.map \ No newline at end of file diff --git a/wwwroot/js/utils/PositionUtils.js.map b/wwwroot/js/utils/PositionUtils.js.map new file mode 100644 index 0000000..4d1125c --- /dev/null +++ b/wwwroot/js/utils/PositionUtils.js.map @@ -0,0 +1 @@ +{"version":3,"file":"PositionUtils.js","sourceRoot":"","sources":["../../../src/utils/PositionUtils.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/wwwroot/js/utils/TimeFormatter.d.ts b/wwwroot/js/utils/TimeFormatter.d.ts new file mode 100644 index 0000000..d52a9f2 --- /dev/null +++ b/wwwroot/js/utils/TimeFormatter.d.ts @@ -0,0 +1,45 @@ +/** + * TimeFormatter - Centralized time formatting with timezone support + * Now uses DateService internally for all date/time operations + * + * Handles conversion from UTC/Zulu time to configured timezone (default: Europe/Copenhagen) + * Supports both 12-hour and 24-hour format configuration + * + * All events in the system are stored in UTC and must be converted to local timezone + */ +import { ITimeFormatConfig } from '../configurations/TimeFormatConfig'; +export declare class TimeFormatter { + private static settings; + private static dateService; + private static getDateService; + /** + * Configure time formatting settings + * Must be called before using TimeFormatter + */ + static configure(settings: ITimeFormatConfig): void; + /** + * Convert UTC date to configured timezone (internal helper) + * @param utcDate - Date in UTC (or ISO string) + * @returns Date object adjusted to configured timezone + */ + private static convertToLocalTime; + /** + * Format time in 24-hour format using DateService (internal helper) + * @param date - Date to format + * @returns Formatted time string (e.g., "09:00") + */ + private static format24Hour; + /** + * Format time according to current configuration + * @param date - Date to format + * @returns Formatted time string + */ + static formatTime(date: Date): string; + /** + * Format time range (start - end) using DateService + * @param startDate - Start date + * @param endDate - End date + * @returns Formatted time range string (e.g., "09:00 - 10:30") + */ + static formatTimeRange(startDate: Date, endDate: Date): string; +} diff --git a/wwwroot/js/utils/TimeFormatter.js b/wwwroot/js/utils/TimeFormatter.js new file mode 100644 index 0000000..72ab72c --- /dev/null +++ b/wwwroot/js/utils/TimeFormatter.js @@ -0,0 +1,92 @@ +/** + * TimeFormatter - Centralized time formatting with timezone support + * Now uses DateService internally for all date/time operations + * + * Handles conversion from UTC/Zulu time to configured timezone (default: Europe/Copenhagen) + * Supports both 12-hour and 24-hour format configuration + * + * All events in the system are stored in UTC and must be converted to local timezone + */ +import { DateService } from './DateService'; +import dayjs from 'dayjs'; +import utc from 'dayjs/plugin/utc'; +import timezone from 'dayjs/plugin/timezone'; +// Enable day.js plugins for timezone formatting +dayjs.extend(utc); +dayjs.extend(timezone); +export class TimeFormatter { + static getDateService() { + if (!TimeFormatter.dateService) { + if (!TimeFormatter.settings) { + throw new Error('TimeFormatter must be configured before use. Call TimeFormatter.configure() first.'); + } + // Create a minimal config object for DateService + const config = { + timeFormatConfig: { + timezone: TimeFormatter.settings.timezone + } + }; + TimeFormatter.dateService = new DateService(config); + } + return TimeFormatter.dateService; + } + /** + * Configure time formatting settings + * Must be called before using TimeFormatter + */ + static configure(settings) { + TimeFormatter.settings = settings; + // Reset DateService to pick up new timezone + TimeFormatter.dateService = null; + } + /** + * Convert UTC date to configured timezone (internal helper) + * @param utcDate - Date in UTC (or ISO string) + * @returns Date object adjusted to configured timezone + */ + static convertToLocalTime(utcDate) { + if (typeof utcDate === 'string') { + return TimeFormatter.getDateService().fromUTC(utcDate); + } + // If it's already a Date object, convert to UTC string first, then back to local + const utcString = utcDate.toISOString(); + return TimeFormatter.getDateService().fromUTC(utcString); + } + /** + * Format time in 24-hour format using DateService (internal helper) + * @param date - Date to format + * @returns Formatted time string (e.g., "09:00") + */ + static format24Hour(date) { + if (!TimeFormatter.settings) { + throw new Error('TimeFormatter must be configured before use. Call TimeFormatter.configure() first.'); + } + // Use day.js directly to format with timezone awareness + const pattern = TimeFormatter.settings.showSeconds ? 'HH:mm:ss' : 'HH:mm'; + return dayjs.utc(date).tz(TimeFormatter.settings.timezone).format(pattern); + } + /** + * Format time according to current configuration + * @param date - Date to format + * @returns Formatted time string + */ + static formatTime(date) { + // Always use 24-hour format (12-hour support removed as unused) + return TimeFormatter.format24Hour(date); + } + /** + * Format time range (start - end) using DateService + * @param startDate - Start date + * @param endDate - End date + * @returns Formatted time range string (e.g., "09:00 - 10:30") + */ + static formatTimeRange(startDate, endDate) { + const localStart = TimeFormatter.convertToLocalTime(startDate); + const localEnd = TimeFormatter.convertToLocalTime(endDate); + return TimeFormatter.getDateService().formatTimeRange(localStart, localEnd); + } +} +TimeFormatter.settings = null; +// DateService will be initialized lazily to avoid circular dependency with CalendarConfig +TimeFormatter.dateService = null; +//# sourceMappingURL=TimeFormatter.js.map \ No newline at end of file diff --git a/wwwroot/js/utils/TimeFormatter.js.map b/wwwroot/js/utils/TimeFormatter.js.map new file mode 100644 index 0000000..e5a05ec --- /dev/null +++ b/wwwroot/js/utils/TimeFormatter.js.map @@ -0,0 +1 @@ +{"version":3,"file":"TimeFormatter.js","sourceRoot":"","sources":["../../../src/utils/TimeFormatter.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,GAAG,MAAM,kBAAkB,CAAC;AACnC,OAAO,QAAQ,MAAM,uBAAuB,CAAC;AAE7C,gDAAgD;AAChD,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;AAClB,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;AAEvB,MAAM,OAAO,aAAa;IAMhB,MAAM,CAAC,cAAc;QAC3B,IAAI,CAAC,aAAa,CAAC,WAAW,EAAE,CAAC;YAC/B,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,CAAC;gBAC5B,MAAM,IAAI,KAAK,CAAC,oFAAoF,CAAC,CAAC;YACxG,CAAC;YACD,iDAAiD;YACjD,MAAM,MAAM,GAAG;gBACb,gBAAgB,EAAE;oBAChB,QAAQ,EAAE,aAAa,CAAC,QAAQ,CAAC,QAAQ;iBAC1C;aACF,CAAC;YACF,aAAa,CAAC,WAAW,GAAG,IAAI,WAAW,CAAC,MAAa,CAAC,CAAC;QAC7D,CAAC;QACD,OAAO,aAAa,CAAC,WAAW,CAAC;IACnC,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,SAAS,CAAC,QAA2B;QAC1C,aAAa,CAAC,QAAQ,GAAG,QAAQ,CAAC;QAClC,4CAA4C;QAC5C,aAAa,CAAC,WAAW,GAAG,IAAI,CAAC;IACnC,CAAC;IAED;;;;OAIG;IACK,MAAM,CAAC,kBAAkB,CAAC,OAAsB;QACtD,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;YAChC,OAAO,aAAa,CAAC,cAAc,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACzD,CAAC;QAED,iFAAiF;QACjF,MAAM,SAAS,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;QACxC,OAAO,aAAa,CAAC,cAAc,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAC3D,CAAC;IAED;;;;OAIG;IACK,MAAM,CAAC,YAAY,CAAC,IAAU;QACpC,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,CAAC;YAC5B,MAAM,IAAI,KAAK,CAAC,oFAAoF,CAAC,CAAC;QACxG,CAAC;QAED,wDAAwD;QACxD,MAAM,OAAO,GAAG,aAAa,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC;QAC1E,OAAO,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC7E,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,UAAU,CAAC,IAAU;QAC1B,gEAAgE;QAChE,OAAO,aAAa,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,eAAe,CAAC,SAAe,EAAE,OAAa;QACnD,MAAM,UAAU,GAAG,aAAa,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC;QAC/D,MAAM,QAAQ,GAAG,aAAa,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;QAC3D,OAAO,aAAa,CAAC,cAAc,EAAE,CAAC,eAAe,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IAC9E,CAAC;;AAjFc,sBAAQ,GAA6B,IAAI,CAAC;AAEzD,0FAA0F;AAC3E,yBAAW,GAAuB,IAAI,CAAC"} \ No newline at end of file diff --git a/wwwroot/js/utils/URLManager.d.ts b/wwwroot/js/utils/URLManager.d.ts new file mode 100644 index 0000000..1b4d811 --- /dev/null +++ b/wwwroot/js/utils/URLManager.d.ts @@ -0,0 +1,29 @@ +import { IEventBus } from '../types/CalendarTypes'; +/** + * URLManager handles URL query parameter parsing and deep linking functionality + * Follows event-driven architecture with no global state + */ +export declare class URLManager { + private eventBus; + constructor(eventBus: IEventBus); + /** + * Parse eventId from URL query parameters + * @returns eventId string or null if not found + */ + parseEventIdFromURL(): string | null; + /** + * Get all query parameters as an object + * @returns object with all query parameters + */ + getAllQueryParams(): Record; + /** + * Update URL without page reload (for future use) + * @param params object with parameters to update + */ + updateURL(params: Record): void; + /** + * Check if current URL has any query parameters + * @returns true if URL has query parameters + */ + hasQueryParams(): boolean; +} diff --git a/wwwroot/js/utils/URLManager.js b/wwwroot/js/utils/URLManager.js new file mode 100644 index 0000000..472dce8 --- /dev/null +++ b/wwwroot/js/utils/URLManager.js @@ -0,0 +1,76 @@ +/** + * URLManager handles URL query parameter parsing and deep linking functionality + * Follows event-driven architecture with no global state + */ +export class URLManager { + constructor(eventBus) { + this.eventBus = eventBus; + } + /** + * Parse eventId from URL query parameters + * @returns eventId string or null if not found + */ + parseEventIdFromURL() { + try { + const urlParams = new URLSearchParams(window.location.search); + const eventId = urlParams.get('eventId'); + if (eventId && eventId.trim() !== '') { + return eventId.trim(); + } + return null; + } + catch (error) { + console.warn('URLManager: Failed to parse URL parameters:', error); + return null; + } + } + /** + * Get all query parameters as an object + * @returns object with all query parameters + */ + getAllQueryParams() { + try { + const urlParams = new URLSearchParams(window.location.search); + const params = {}; + for (const [key, value] of urlParams.entries()) { + params[key] = value; + } + return params; + } + catch (error) { + console.warn('URLManager: Failed to parse URL parameters:', error); + return {}; + } + } + /** + * Update URL without page reload (for future use) + * @param params object with parameters to update + */ + updateURL(params) { + try { + const url = new URL(window.location.href); + // Update or remove parameters + Object.entries(params).forEach(([key, value]) => { + if (value === null) { + url.searchParams.delete(key); + } + else { + url.searchParams.set(key, value); + } + }); + // Update URL without page reload + window.history.replaceState({}, '', url.toString()); + } + catch (error) { + console.warn('URLManager: Failed to update URL:', error); + } + } + /** + * Check if current URL has any query parameters + * @returns true if URL has query parameters + */ + hasQueryParams() { + return window.location.search.length > 0; + } +} +//# sourceMappingURL=URLManager.js.map \ No newline at end of file diff --git a/wwwroot/js/utils/URLManager.js.map b/wwwroot/js/utils/URLManager.js.map new file mode 100644 index 0000000..5cd8f88 --- /dev/null +++ b/wwwroot/js/utils/URLManager.js.map @@ -0,0 +1 @@ +{"version":3,"file":"URLManager.js","sourceRoot":"","sources":["../../../src/utils/URLManager.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,MAAM,OAAO,UAAU;IAGnB,YAAY,QAAmB;QAC3B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC7B,CAAC;IAED;;;OAGG;IACI,mBAAmB;QACtB,IAAI,CAAC;YACD,MAAM,SAAS,GAAG,IAAI,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAC9D,MAAM,OAAO,GAAG,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YAEzC,IAAI,OAAO,IAAI,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;gBACnC,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC;YAC1B,CAAC;YAED,OAAO,IAAI,CAAC;QAChB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,6CAA6C,EAAE,KAAK,CAAC,CAAC;YACnE,OAAO,IAAI,CAAC;QAChB,CAAC;IACL,CAAC;IAED;;;OAGG;IACI,iBAAiB;QACpB,IAAI,CAAC;YACD,MAAM,SAAS,GAAG,IAAI,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAC9D,MAAM,MAAM,GAA2B,EAAE,CAAC;YAE1C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,SAAS,CAAC,OAAO,EAAE,EAAE,CAAC;gBAC7C,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;YACxB,CAAC;YAED,OAAO,MAAM,CAAC;QAClB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,6CAA6C,EAAE,KAAK,CAAC,CAAC;YACnE,OAAO,EAAE,CAAC;QACd,CAAC;IACL,CAAC;IAED;;;OAGG;IACI,SAAS,CAAC,MAAqC;QAClD,IAAI,CAAC;YACD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YAE1C,8BAA8B;YAC9B,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;gBAC5C,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;oBACjB,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBACjC,CAAC;qBAAM,CAAC;oBACJ,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;gBACrC,CAAC;YACL,CAAC,CAAC,CAAC;YAEH,iCAAiC;YACjC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;QACxD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,mCAAmC,EAAE,KAAK,CAAC,CAAC;QAC7D,CAAC;IACL,CAAC;IAED;;;OAGG;IACI,cAAc;QACjB,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;IAC7C,CAAC;CACJ"} \ No newline at end of file diff --git a/wwwroot/js/v2-demo.js b/wwwroot/js/v2-demo.js new file mode 100644 index 0000000..9fde2c8 --- /dev/null +++ b/wwwroot/js/v2-demo.js @@ -0,0 +1,6463 @@ +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); +var __commonJS = (cb, mod) => function __require() { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); + +// node_modules/dayjs/dayjs.min.js +var require_dayjs_min = __commonJS({ + "node_modules/dayjs/dayjs.min.js"(exports, module) { + !function(t, e) { + "object" == typeof exports && "undefined" != typeof module ? module.exports = e() : "function" == typeof define && define.amd ? define(e) : (t = "undefined" != typeof globalThis ? globalThis : t || self).dayjs = e(); + }(exports, function() { + "use strict"; + var t = 1e3, e = 6e4, n = 36e5, r = "millisecond", i = "second", s = "minute", u = "hour", a = "day", o = "week", c = "month", f = "quarter", h = "year", d = "date", l = "Invalid Date", $ = /^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/, y = /\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g, M = { name: "en", weekdays: "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"), months: "January_February_March_April_May_June_July_August_September_October_November_December".split("_"), ordinal: function(t2) { + var e2 = ["th", "st", "nd", "rd"], n2 = t2 % 100; + return "[" + t2 + (e2[(n2 - 20) % 10] || e2[n2] || e2[0]) + "]"; + } }, m = /* @__PURE__ */ __name(function(t2, e2, n2) { + var r2 = String(t2); + return !r2 || r2.length >= e2 ? t2 : "" + Array(e2 + 1 - r2.length).join(n2) + t2; + }, "m"), v = { s: m, z: function(t2) { + var e2 = -t2.utcOffset(), n2 = Math.abs(e2), r2 = Math.floor(n2 / 60), i2 = n2 % 60; + return (e2 <= 0 ? "+" : "-") + m(r2, 2, "0") + ":" + m(i2, 2, "0"); + }, m: /* @__PURE__ */ __name(function t2(e2, n2) { + if (e2.date() < n2.date()) + return -t2(n2, e2); + var r2 = 12 * (n2.year() - e2.year()) + (n2.month() - e2.month()), i2 = e2.clone().add(r2, c), s2 = n2 - i2 < 0, u2 = e2.clone().add(r2 + (s2 ? -1 : 1), c); + return +(-(r2 + (n2 - i2) / (s2 ? i2 - u2 : u2 - i2)) || 0); + }, "t"), a: function(t2) { + return t2 < 0 ? Math.ceil(t2) || 0 : Math.floor(t2); + }, p: function(t2) { + return { M: c, y: h, w: o, d: a, D: d, h: u, m: s, s: i, ms: r, Q: f }[t2] || String(t2 || "").toLowerCase().replace(/s$/, ""); + }, u: function(t2) { + return void 0 === t2; + } }, g = "en", D = {}; + D[g] = M; + var p = "$isDayjsObject", S = /* @__PURE__ */ __name(function(t2) { + return t2 instanceof _ || !(!t2 || !t2[p]); + }, "S"), w = /* @__PURE__ */ __name(function t2(e2, n2, r2) { + var i2; + if (!e2) + return g; + if ("string" == typeof e2) { + var s2 = e2.toLowerCase(); + D[s2] && (i2 = s2), n2 && (D[s2] = n2, i2 = s2); + var u2 = e2.split("-"); + if (!i2 && u2.length > 1) + return t2(u2[0]); + } else { + var a2 = e2.name; + D[a2] = e2, i2 = a2; + } + return !r2 && i2 && (g = i2), i2 || !r2 && g; + }, "t"), O = /* @__PURE__ */ __name(function(t2, e2) { + if (S(t2)) + return t2.clone(); + var n2 = "object" == typeof e2 ? e2 : {}; + return n2.date = t2, n2.args = arguments, new _(n2); + }, "O"), b = v; + b.l = w, b.i = S, b.w = function(t2, e2) { + return O(t2, { locale: e2.$L, utc: e2.$u, x: e2.$x, $offset: e2.$offset }); + }; + var _ = function() { + function M2(t2) { + this.$L = w(t2.locale, null, true), this.parse(t2), this.$x = this.$x || t2.x || {}, this[p] = true; + } + __name(M2, "M"); + var m2 = M2.prototype; + return m2.parse = function(t2) { + this.$d = function(t3) { + var e2 = t3.date, n2 = t3.utc; + if (null === e2) + return /* @__PURE__ */ new Date(NaN); + if (b.u(e2)) + return /* @__PURE__ */ new Date(); + if (e2 instanceof Date) + return new Date(e2); + if ("string" == typeof e2 && !/Z$/i.test(e2)) { + var r2 = e2.match($); + if (r2) { + var i2 = r2[2] - 1 || 0, s2 = (r2[7] || "0").substring(0, 3); + return n2 ? new Date(Date.UTC(r2[1], i2, r2[3] || 1, r2[4] || 0, r2[5] || 0, r2[6] || 0, s2)) : new Date(r2[1], i2, r2[3] || 1, r2[4] || 0, r2[5] || 0, r2[6] || 0, s2); + } + } + return new Date(e2); + }(t2), this.init(); + }, m2.init = function() { + var t2 = this.$d; + this.$y = t2.getFullYear(), this.$M = t2.getMonth(), this.$D = t2.getDate(), this.$W = t2.getDay(), this.$H = t2.getHours(), this.$m = t2.getMinutes(), this.$s = t2.getSeconds(), this.$ms = t2.getMilliseconds(); + }, m2.$utils = function() { + return b; + }, m2.isValid = function() { + return !(this.$d.toString() === l); + }, m2.isSame = function(t2, e2) { + var n2 = O(t2); + return this.startOf(e2) <= n2 && n2 <= this.endOf(e2); + }, m2.isAfter = function(t2, e2) { + return O(t2) < this.startOf(e2); + }, m2.isBefore = function(t2, e2) { + return this.endOf(e2) < O(t2); + }, m2.$g = function(t2, e2, n2) { + return b.u(t2) ? this[e2] : this.set(n2, t2); + }, m2.unix = function() { + return Math.floor(this.valueOf() / 1e3); + }, m2.valueOf = function() { + return this.$d.getTime(); + }, m2.startOf = function(t2, e2) { + var n2 = this, r2 = !!b.u(e2) || e2, f2 = b.p(t2), l2 = /* @__PURE__ */ __name(function(t3, e3) { + var i2 = b.w(n2.$u ? Date.UTC(n2.$y, e3, t3) : new Date(n2.$y, e3, t3), n2); + return r2 ? i2 : i2.endOf(a); + }, "l"), $2 = /* @__PURE__ */ __name(function(t3, e3) { + return b.w(n2.toDate()[t3].apply(n2.toDate("s"), (r2 ? [0, 0, 0, 0] : [23, 59, 59, 999]).slice(e3)), n2); + }, "$"), y2 = this.$W, M3 = this.$M, m3 = this.$D, v2 = "set" + (this.$u ? "UTC" : ""); + switch (f2) { + case h: + return r2 ? l2(1, 0) : l2(31, 11); + case c: + return r2 ? l2(1, M3) : l2(0, M3 + 1); + case o: + var g2 = this.$locale().weekStart || 0, D2 = (y2 < g2 ? y2 + 7 : y2) - g2; + return l2(r2 ? m3 - D2 : m3 + (6 - D2), M3); + case a: + case d: + return $2(v2 + "Hours", 0); + case u: + return $2(v2 + "Minutes", 1); + case s: + return $2(v2 + "Seconds", 2); + case i: + return $2(v2 + "Milliseconds", 3); + default: + return this.clone(); + } + }, m2.endOf = function(t2) { + return this.startOf(t2, false); + }, m2.$set = function(t2, e2) { + var n2, o2 = b.p(t2), f2 = "set" + (this.$u ? "UTC" : ""), l2 = (n2 = {}, n2[a] = f2 + "Date", n2[d] = f2 + "Date", n2[c] = f2 + "Month", n2[h] = f2 + "FullYear", n2[u] = f2 + "Hours", n2[s] = f2 + "Minutes", n2[i] = f2 + "Seconds", n2[r] = f2 + "Milliseconds", n2)[o2], $2 = o2 === a ? this.$D + (e2 - this.$W) : e2; + if (o2 === c || o2 === h) { + var y2 = this.clone().set(d, 1); + y2.$d[l2]($2), y2.init(), this.$d = y2.set(d, Math.min(this.$D, y2.daysInMonth())).$d; + } else + l2 && this.$d[l2]($2); + return this.init(), this; + }, m2.set = function(t2, e2) { + return this.clone().$set(t2, e2); + }, m2.get = function(t2) { + return this[b.p(t2)](); + }, m2.add = function(r2, f2) { + var d2, l2 = this; + r2 = Number(r2); + var $2 = b.p(f2), y2 = /* @__PURE__ */ __name(function(t2) { + var e2 = O(l2); + return b.w(e2.date(e2.date() + Math.round(t2 * r2)), l2); + }, "y"); + if ($2 === c) + return this.set(c, this.$M + r2); + if ($2 === h) + return this.set(h, this.$y + r2); + if ($2 === a) + return y2(1); + if ($2 === o) + return y2(7); + var M3 = (d2 = {}, d2[s] = e, d2[u] = n, d2[i] = t, d2)[$2] || 1, m3 = this.$d.getTime() + r2 * M3; + return b.w(m3, this); + }, m2.subtract = function(t2, e2) { + return this.add(-1 * t2, e2); + }, m2.format = function(t2) { + var e2 = this, n2 = this.$locale(); + if (!this.isValid()) + return n2.invalidDate || l; + var r2 = t2 || "YYYY-MM-DDTHH:mm:ssZ", i2 = b.z(this), s2 = this.$H, u2 = this.$m, a2 = this.$M, o2 = n2.weekdays, c2 = n2.months, f2 = n2.meridiem, h2 = /* @__PURE__ */ __name(function(t3, n3, i3, s3) { + return t3 && (t3[n3] || t3(e2, r2)) || i3[n3].slice(0, s3); + }, "h"), d2 = /* @__PURE__ */ __name(function(t3) { + return b.s(s2 % 12 || 12, t3, "0"); + }, "d"), $2 = f2 || function(t3, e3, n3) { + var r3 = t3 < 12 ? "AM" : "PM"; + return n3 ? r3.toLowerCase() : r3; + }; + return r2.replace(y, function(t3, r3) { + return r3 || function(t4) { + switch (t4) { + case "YY": + return String(e2.$y).slice(-2); + case "YYYY": + return b.s(e2.$y, 4, "0"); + case "M": + return a2 + 1; + case "MM": + return b.s(a2 + 1, 2, "0"); + case "MMM": + return h2(n2.monthsShort, a2, c2, 3); + case "MMMM": + return h2(c2, a2); + case "D": + return e2.$D; + case "DD": + return b.s(e2.$D, 2, "0"); + case "d": + return String(e2.$W); + case "dd": + return h2(n2.weekdaysMin, e2.$W, o2, 2); + case "ddd": + return h2(n2.weekdaysShort, e2.$W, o2, 3); + case "dddd": + return o2[e2.$W]; + case "H": + return String(s2); + case "HH": + return b.s(s2, 2, "0"); + case "h": + return d2(1); + case "hh": + return d2(2); + case "a": + return $2(s2, u2, true); + case "A": + return $2(s2, u2, false); + case "m": + return String(u2); + case "mm": + return b.s(u2, 2, "0"); + case "s": + return String(e2.$s); + case "ss": + return b.s(e2.$s, 2, "0"); + case "SSS": + return b.s(e2.$ms, 3, "0"); + case "Z": + return i2; + } + return null; + }(t3) || i2.replace(":", ""); + }); + }, m2.utcOffset = function() { + return 15 * -Math.round(this.$d.getTimezoneOffset() / 15); + }, m2.diff = function(r2, d2, l2) { + var $2, y2 = this, M3 = b.p(d2), m3 = O(r2), v2 = (m3.utcOffset() - this.utcOffset()) * e, g2 = this - m3, D2 = /* @__PURE__ */ __name(function() { + return b.m(y2, m3); + }, "D"); + switch (M3) { + case h: + $2 = D2() / 12; + break; + case c: + $2 = D2(); + break; + case f: + $2 = D2() / 3; + break; + case o: + $2 = (g2 - v2) / 6048e5; + break; + case a: + $2 = (g2 - v2) / 864e5; + break; + case u: + $2 = g2 / n; + break; + case s: + $2 = g2 / e; + break; + case i: + $2 = g2 / t; + break; + default: + $2 = g2; + } + return l2 ? $2 : b.a($2); + }, m2.daysInMonth = function() { + return this.endOf(c).$D; + }, m2.$locale = function() { + return D[this.$L]; + }, m2.locale = function(t2, e2) { + if (!t2) + return this.$L; + var n2 = this.clone(), r2 = w(t2, e2, true); + return r2 && (n2.$L = r2), n2; + }, m2.clone = function() { + return b.w(this.$d, this); + }, m2.toDate = function() { + return new Date(this.valueOf()); + }, m2.toJSON = function() { + return this.isValid() ? this.toISOString() : null; + }, m2.toISOString = function() { + return this.$d.toISOString(); + }, m2.toString = function() { + return this.$d.toUTCString(); + }, M2; + }(), k = _.prototype; + return O.prototype = k, [["$ms", r], ["$s", i], ["$m", s], ["$H", u], ["$W", a], ["$M", c], ["$y", h], ["$D", d]].forEach(function(t2) { + k[t2[1]] = function(e2) { + return this.$g(e2, t2[0], t2[1]); + }; + }), O.extend = function(t2, e2) { + return t2.$i || (t2(e2, _, O), t2.$i = true), O; + }, O.locale = w, O.isDayjs = S, O.unix = function(t2) { + return O(1e3 * t2); + }, O.en = D[g], O.Ls = D, O.p = {}, O; + }); + } +}); + +// node_modules/dayjs/plugin/utc.js +var require_utc = __commonJS({ + "node_modules/dayjs/plugin/utc.js"(exports, module) { + !function(t, i) { + "object" == typeof exports && "undefined" != typeof module ? module.exports = i() : "function" == typeof define && define.amd ? define(i) : (t = "undefined" != typeof globalThis ? globalThis : t || self).dayjs_plugin_utc = i(); + }(exports, function() { + "use strict"; + var t = "minute", i = /[+-]\d\d(?::?\d\d)?/g, e = /([+-]|\d\d)/g; + return function(s, f, n) { + var u = f.prototype; + n.utc = function(t2) { + var i2 = { date: t2, utc: true, args: arguments }; + return new f(i2); + }, u.utc = function(i2) { + var e2 = n(this.toDate(), { locale: this.$L, utc: true }); + return i2 ? e2.add(this.utcOffset(), t) : e2; + }, u.local = function() { + return n(this.toDate(), { locale: this.$L, utc: false }); + }; + var r = u.parse; + u.parse = function(t2) { + t2.utc && (this.$u = true), this.$utils().u(t2.$offset) || (this.$offset = t2.$offset), r.call(this, t2); + }; + var o = u.init; + u.init = function() { + if (this.$u) { + var t2 = this.$d; + this.$y = t2.getUTCFullYear(), this.$M = t2.getUTCMonth(), this.$D = t2.getUTCDate(), this.$W = t2.getUTCDay(), this.$H = t2.getUTCHours(), this.$m = t2.getUTCMinutes(), this.$s = t2.getUTCSeconds(), this.$ms = t2.getUTCMilliseconds(); + } else + o.call(this); + }; + var a = u.utcOffset; + u.utcOffset = function(s2, f2) { + var n2 = this.$utils().u; + if (n2(s2)) + return this.$u ? 0 : n2(this.$offset) ? a.call(this) : this.$offset; + if ("string" == typeof s2 && (s2 = function(t2) { + void 0 === t2 && (t2 = ""); + var s3 = t2.match(i); + if (!s3) + return null; + var f3 = ("" + s3[0]).match(e) || ["-", 0, 0], n3 = f3[0], u3 = 60 * +f3[1] + +f3[2]; + return 0 === u3 ? 0 : "+" === n3 ? u3 : -u3; + }(s2), null === s2)) + return this; + var u2 = Math.abs(s2) <= 16 ? 60 * s2 : s2; + if (0 === u2) + return this.utc(f2); + var r2 = this.clone(); + if (f2) + return r2.$offset = u2, r2.$u = false, r2; + var o2 = this.$u ? this.toDate().getTimezoneOffset() : -1 * this.utcOffset(); + return (r2 = this.local().add(u2 + o2, t)).$offset = u2, r2.$x.$localOffset = o2, r2; + }; + var h = u.format; + u.format = function(t2) { + var i2 = t2 || (this.$u ? "YYYY-MM-DDTHH:mm:ss[Z]" : ""); + return h.call(this, i2); + }, u.valueOf = function() { + var t2 = this.$utils().u(this.$offset) ? 0 : this.$offset + (this.$x.$localOffset || this.$d.getTimezoneOffset()); + return this.$d.valueOf() - 6e4 * t2; + }, u.isUTC = function() { + return !!this.$u; + }, u.toISOString = function() { + return this.toDate().toISOString(); + }, u.toString = function() { + return this.toDate().toUTCString(); + }; + var l = u.toDate; + u.toDate = function(t2) { + return "s" === t2 && this.$offset ? n(this.format("YYYY-MM-DD HH:mm:ss:SSS")).toDate() : l.call(this); + }; + var c = u.diff; + u.diff = function(t2, i2, e2) { + if (t2 && this.$u === t2.$u) + return c.call(this, t2, i2, e2); + var s2 = this.local(), f2 = n(t2).local(); + return c.call(s2, f2, i2, e2); + }; + }; + }); + } +}); + +// node_modules/dayjs/plugin/timezone.js +var require_timezone = __commonJS({ + "node_modules/dayjs/plugin/timezone.js"(exports, module) { + !function(t, e) { + "object" == typeof exports && "undefined" != typeof module ? module.exports = e() : "function" == typeof define && define.amd ? define(e) : (t = "undefined" != typeof globalThis ? globalThis : t || self).dayjs_plugin_timezone = e(); + }(exports, function() { + "use strict"; + var t = { year: 0, month: 1, day: 2, hour: 3, minute: 4, second: 5 }, e = {}; + return function(n, i, o) { + var r, a = /* @__PURE__ */ __name(function(t2, n2, i2) { + void 0 === i2 && (i2 = {}); + var o2 = new Date(t2), r2 = function(t3, n3) { + void 0 === n3 && (n3 = {}); + var i3 = n3.timeZoneName || "short", o3 = t3 + "|" + i3, r3 = e[o3]; + return r3 || (r3 = new Intl.DateTimeFormat("en-US", { hour12: false, timeZone: t3, year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit", timeZoneName: i3 }), e[o3] = r3), r3; + }(n2, i2); + return r2.formatToParts(o2); + }, "a"), u = /* @__PURE__ */ __name(function(e2, n2) { + for (var i2 = a(e2, n2), r2 = [], u2 = 0; u2 < i2.length; u2 += 1) { + var f2 = i2[u2], s2 = f2.type, m = f2.value, c = t[s2]; + c >= 0 && (r2[c] = parseInt(m, 10)); + } + var d = r2[3], l = 24 === d ? 0 : d, h = r2[0] + "-" + r2[1] + "-" + r2[2] + " " + l + ":" + r2[4] + ":" + r2[5] + ":000", v = +e2; + return (o.utc(h).valueOf() - (v -= v % 1e3)) / 6e4; + }, "u"), f = i.prototype; + f.tz = function(t2, e2) { + void 0 === t2 && (t2 = r); + var n2, i2 = this.utcOffset(), a2 = this.toDate(), u2 = a2.toLocaleString("en-US", { timeZone: t2 }), f2 = Math.round((a2 - new Date(u2)) / 1e3 / 60), s2 = 15 * -Math.round(a2.getTimezoneOffset() / 15) - f2; + if (!Number(s2)) + n2 = this.utcOffset(0, e2); + else if (n2 = o(u2, { locale: this.$L }).$set("millisecond", this.$ms).utcOffset(s2, true), e2) { + var m = n2.utcOffset(); + n2 = n2.add(i2 - m, "minute"); + } + return n2.$x.$timezone = t2, n2; + }, f.offsetName = function(t2) { + var e2 = this.$x.$timezone || o.tz.guess(), n2 = a(this.valueOf(), e2, { timeZoneName: t2 }).find(function(t3) { + return "timezonename" === t3.type.toLowerCase(); + }); + return n2 && n2.value; + }; + var s = f.startOf; + f.startOf = function(t2, e2) { + if (!this.$x || !this.$x.$timezone) + return s.call(this, t2, e2); + var n2 = o(this.format("YYYY-MM-DD HH:mm:ss:SSS"), { locale: this.$L }); + return s.call(n2, t2, e2).tz(this.$x.$timezone, true); + }, o.tz = function(t2, e2, n2) { + var i2 = n2 && e2, a2 = n2 || e2 || r, f2 = u(+o(), a2); + if ("string" != typeof t2) + return o(t2).tz(a2); + var s2 = function(t3, e3, n3) { + var i3 = t3 - 60 * e3 * 1e3, o2 = u(i3, n3); + if (e3 === o2) + return [i3, e3]; + var r2 = u(i3 -= 60 * (o2 - e3) * 1e3, n3); + return o2 === r2 ? [i3, o2] : [t3 - 60 * Math.min(o2, r2) * 1e3, Math.max(o2, r2)]; + }(o.utc(t2, i2).valueOf(), f2, a2), m = s2[0], c = s2[1], d = o(m).utcOffset(c); + return d.$x.$timezone = a2, d; + }, o.tz.guess = function() { + return Intl.DateTimeFormat().resolvedOptions().timeZone; + }, o.tz.setDefault = function(t2) { + r = t2; + }; + }; + }); + } +}); + +// node_modules/dayjs/plugin/isoWeek.js +var require_isoWeek = __commonJS({ + "node_modules/dayjs/plugin/isoWeek.js"(exports, module) { + !function(e, t) { + "object" == typeof exports && "undefined" != typeof module ? module.exports = t() : "function" == typeof define && define.amd ? define(t) : (e = "undefined" != typeof globalThis ? globalThis : e || self).dayjs_plugin_isoWeek = t(); + }(exports, function() { + "use strict"; + var e = "day"; + return function(t, i, s) { + var a = /* @__PURE__ */ __name(function(t2) { + return t2.add(4 - t2.isoWeekday(), e); + }, "a"), d = i.prototype; + d.isoWeekYear = function() { + return a(this).year(); + }, d.isoWeek = function(t2) { + if (!this.$utils().u(t2)) + return this.add(7 * (t2 - this.isoWeek()), e); + var i2, d2, n2, o, r = a(this), u = (i2 = this.isoWeekYear(), d2 = this.$u, n2 = (d2 ? s.utc : s)().year(i2).startOf("year"), o = 4 - n2.isoWeekday(), n2.isoWeekday() > 4 && (o += 7), n2.add(o, e)); + return r.diff(u, "week") + 1; + }, d.isoWeekday = function(e2) { + return this.$utils().u(e2) ? this.day() || 7 : this.day(this.day() % 7 ? e2 : e2 - 7); + }; + var n = d.startOf; + d.startOf = function(e2, t2) { + var i2 = this.$utils(), s2 = !!i2.u(t2) || t2; + return "isoweek" === i2.p(e2) ? s2 ? this.date(this.date() - (this.isoWeekday() - 1)).startOf("day") : this.date(this.date() - 1 - (this.isoWeekday() - 1) + 7).endOf("day") : n.bind(this)(e2, t2); + }; + }; + }); + } +}); + +// node_modules/@novadi/core/dist/token.js +var tokenCounter = 0; +function Token(description) { + const id = ++tokenCounter; + const sym = Symbol(description ? `Token(${description})` : `Token#${id}`); + const token2 = { + symbol: sym, + description, + toString() { + return description ? `Token<${description}>` : `Token<#${id}>`; + } + }; + return token2; +} +__name(Token, "Token"); + +// node_modules/@novadi/core/dist/errors.js +var _ContainerError = class _ContainerError extends Error { + constructor(message) { + super(message); + this.name = "ContainerError"; + } +}; +__name(_ContainerError, "ContainerError"); +var ContainerError = _ContainerError; +var _BindingNotFoundError = class _BindingNotFoundError extends ContainerError { + constructor(tokenDescription, path = []) { + const pathStr = path.length > 0 ? ` + Dependency path: ${path.join(" -> ")}` : ""; + super(`Token "${tokenDescription}" is not bound or registered in the container.${pathStr}`); + this.name = "BindingNotFoundError"; + } +}; +__name(_BindingNotFoundError, "BindingNotFoundError"); +var BindingNotFoundError = _BindingNotFoundError; +var _CircularDependencyError = class _CircularDependencyError extends ContainerError { + constructor(path) { + super(`Circular dependency detected: ${path.join(" -> ")}`); + this.name = "CircularDependencyError"; + } +}; +__name(_CircularDependencyError, "CircularDependencyError"); +var CircularDependencyError = _CircularDependencyError; + +// node_modules/@novadi/core/dist/autowire.js +var paramNameCache = /* @__PURE__ */ new WeakMap(); +function extractParameterNames(constructor) { + const cached = paramNameCache.get(constructor); + if (cached) { + return cached; + } + const fnStr = constructor.toString(); + const match = fnStr.match(/constructor\s*\(([^)]*)\)/) || fnStr.match(/^[^(]*\(([^)]*)\)/); + if (!match || !match[1]) { + return []; + } + const params = match[1].split(",").map((param) => param.trim()).filter((param) => param.length > 0).map((param) => { + let name = param.split(/[:=]/)[0].trim(); + name = name.replace(/^((public|private|protected|readonly)\s+)+/, ""); + if (name.includes("{") || name.includes("[")) { + return null; + } + return name; + }).filter((name) => name !== null); + paramNameCache.set(constructor, params); + return params; +} +__name(extractParameterNames, "extractParameterNames"); +function resolveByMap(constructor, container2, options) { + if (!options.map) { + throw new Error("AutoWire map strategy requires options.map to be defined"); + } + const paramNames = extractParameterNames(constructor); + const resolvedDeps = []; + for (const paramName of paramNames) { + const resolver = options.map[paramName]; + if (resolver === void 0) { + if (options.strict) { + throw new Error(`Cannot resolve parameter "${paramName}" on ${constructor.name}. Not found in autowire map. Add it to the map: .autoWire({ map: { ${paramName}: ... } })`); + } else { + resolvedDeps.push(void 0); + } + continue; + } + if (typeof resolver === "function") { + resolvedDeps.push(resolver(container2)); + } else { + resolvedDeps.push(container2.resolve(resolver)); + } + } + return resolvedDeps; +} +__name(resolveByMap, "resolveByMap"); +function resolveByMapResolvers(_constructor, container2, options) { + if (!options.mapResolvers || options.mapResolvers.length === 0) { + return []; + } + const resolvedDeps = []; + for (let i = 0; i < options.mapResolvers.length; i++) { + const resolver = options.mapResolvers[i]; + if (resolver === void 0) { + resolvedDeps.push(void 0); + } else if (typeof resolver === "function") { + resolvedDeps.push(resolver(container2)); + } else { + resolvedDeps.push(container2.resolve(resolver)); + } + } + return resolvedDeps; +} +__name(resolveByMapResolvers, "resolveByMapResolvers"); +function autowire(constructor, container2, options) { + const opts = { + by: "paramName", + strict: false, + ...options + }; + if (opts.mapResolvers && opts.mapResolvers.length > 0) { + return resolveByMapResolvers(constructor, container2, opts); + } + if (opts.map && Object.keys(opts.map).length > 0) { + return resolveByMap(constructor, container2, opts); + } + return []; +} +__name(autowire, "autowire"); + +// node_modules/@novadi/core/dist/builder.js +var _RegistrationBuilder = class _RegistrationBuilder { + constructor(pending, registrations) { + this.registrations = registrations; + this.configs = []; + this.defaultLifetime = "singleton"; + this.pending = pending; + } + /** + * Bind this registration to a token or interface type + * + * @overload + * @param {Token} token - Explicit token for binding + * + * @overload + * @param {string} typeName - Interface type name (auto-generated by transformer) + */ + as(tokenOrTypeName) { + if (tokenOrTypeName && typeof tokenOrTypeName === "object" && "symbol" in tokenOrTypeName) { + const config = { + token: tokenOrTypeName, + type: this.pending.type, + value: this.pending.value, + factory: this.pending.factory, + constructor: this.pending.constructor, + lifetime: this.defaultLifetime + }; + this.configs.push(config); + this.registrations.push(config); + return this; + } else { + const config = { + token: null, + // Will be set during build() + type: this.pending.type, + value: this.pending.value, + factory: this.pending.factory, + constructor: this.pending.constructor, + lifetime: this.defaultLifetime, + interfaceType: tokenOrTypeName + }; + this.configs.push(config); + this.registrations.push(config); + return this; + } + } + /** + * Register as default implementation for an interface + * Combines as() + asDefault() + */ + asDefaultInterface(typeName) { + this.as("TInterface", typeName); + return this.asDefault(); + } + /** + * Register as a keyed interface implementation + * Combines as() + keyed() + */ + asKeyedInterface(key, typeName) { + this.as("TInterface", typeName); + return this.keyed(key); + } + /** + * Register as multiple implemented interfaces + */ + asImplementedInterfaces(tokens) { + if (tokens.length === 0) { + return this; + } + if (this.configs.length > 0) { + for (const config of this.configs) { + config.lifetime = "singleton"; + config.additionalTokens = config.additionalTokens || []; + config.additionalTokens.push(...tokens); + } + return this; + } + const firstConfig = { + token: tokens[0], + type: this.pending.type, + value: this.pending.value, + factory: this.pending.factory, + constructor: this.pending.constructor, + lifetime: "singleton" + }; + this.configs.push(firstConfig); + this.registrations.push(firstConfig); + for (let i = 1; i < tokens.length; i++) { + firstConfig.additionalTokens = firstConfig.additionalTokens || []; + firstConfig.additionalTokens.push(tokens[i]); + } + return this; + } + /** + * Set singleton lifetime (one instance for entire container) + */ + singleInstance() { + for (const config of this.configs) { + config.lifetime = "singleton"; + } + return this; + } + /** + * Set per-request lifetime (one instance per resolve call tree) + */ + instancePerRequest() { + for (const config of this.configs) { + config.lifetime = "per-request"; + } + return this; + } + /** + * Set transient lifetime (new instance every time) + * Alias for default behavior + */ + instancePerDependency() { + for (const config of this.configs) { + config.lifetime = "transient"; + } + return this; + } + /** + * Name this registration for named resolution + */ + named(name) { + for (const config of this.configs) { + config.name = name; + } + return this; + } + /** + * Key this registration for keyed resolution + */ + keyed(key) { + for (const config of this.configs) { + config.key = key; + } + return this; + } + /** + * Mark this as default registration + * Default registrations don't override existing ones + */ + asDefault() { + for (const config of this.configs) { + config.isDefault = true; + } + return this; + } + /** + * Only register if token not already registered + */ + ifNotRegistered() { + for (const config of this.configs) { + config.ifNotRegistered = true; + } + return this; + } + /** + * Specify parameter values for constructor (primitives and constants) + * Use this for non-DI parameters like strings, numbers, config values + */ + withParameters(parameters) { + for (const config of this.configs) { + config.parameterValues = parameters; + } + return this; + } + /** + * Enable automatic dependency injection (autowiring) + * Supports three strategies: paramName (default), map, and class + * + * @example + * ```ts + * // Strategy 1: paramName (default, requires non-minified code in dev) + * builder.registerType(EventBus).as().autoWire() + * + * // Strategy 2: map (minify-safe, explicit) + * builder.registerType(EventBus).as().autoWire({ + * map: { + * logger: (c) => c.resolveType() + * } + * }) + * + * // Strategy 3: class (requires build-time codegen) + * builder.registerType(EventBus).as().autoWire({ by: 'class' }) + * ``` + */ + autoWire(options) { + for (const config of this.configs) { + config.autowireOptions = options || { by: "paramName", strict: false }; + } + return this; + } +}; +__name(_RegistrationBuilder, "RegistrationBuilder"); +var RegistrationBuilder = _RegistrationBuilder; +var _Builder = class _Builder { + constructor(baseContainer) { + this.baseContainer = baseContainer; + this.registrations = []; + } + /** + * Register a class constructor + */ + registerType(constructor) { + const pending = { + type: "type", + value: null, + constructor + }; + return new RegistrationBuilder(pending, this.registrations); + } + /** + * Register a pre-created instance + */ + registerInstance(instance) { + const pending = { + type: "instance", + value: instance, + constructor: void 0 + }; + return new RegistrationBuilder(pending, this.registrations); + } + /** + * Register a factory function + */ + register(factory) { + const pending = { + type: "factory", + value: null, + factory, + constructor: void 0 + }; + return new RegistrationBuilder(pending, this.registrations); + } + /** + * Register a module (function that adds multiple registrations) + */ + module(moduleFunc) { + moduleFunc(this); + return this; + } + /** + * Resolve interface type names to tokens + * @internal + */ + resolveInterfaceTokens(container2) { + for (const config of this.registrations) { + if (config.interfaceType !== void 0 && !config.token) { + config.token = container2.interfaceToken(config.interfaceType); + } + } + } + /** + * Identify tokens that have non-default registrations + * @internal + */ + identifyNonDefaultTokens() { + const tokensWithNonDefaults = /* @__PURE__ */ new Set(); + for (const config of this.registrations) { + if (!config.isDefault && !config.name && config.key === void 0) { + tokensWithNonDefaults.add(config.token); + } + } + return tokensWithNonDefaults; + } + /** + * Check if registration should be skipped + * @internal + */ + shouldSkipRegistration(config, tokensWithNonDefaults, registeredTokens) { + if (config.isDefault && !config.name && config.key === void 0 && tokensWithNonDefaults.has(config.token)) { + return true; + } + if (config.ifNotRegistered && registeredTokens.has(config.token)) { + return true; + } + if (config.isDefault && registeredTokens.has(config.token)) { + return true; + } + return false; + } + /** + * Create binding token for registration (named, keyed, or multi) + * @internal + */ + createBindingToken(config, namedRegistrations, keyedRegistrations, multiRegistrations) { + if (config.name) { + const bindingToken = Token(`__named_${config.name}`); + namedRegistrations.set(config.name, { ...config, token: bindingToken }); + return bindingToken; + } else if (config.key !== void 0) { + const keyStr = typeof config.key === "symbol" ? config.key.toString() : config.key; + const bindingToken = Token(`__keyed_${keyStr}`); + keyedRegistrations.set(config.key, { ...config, token: bindingToken }); + return bindingToken; + } else { + if (multiRegistrations.has(config.token)) { + const bindingToken = Token(`__multi_${config.token.toString()}_${multiRegistrations.get(config.token).length}`); + multiRegistrations.get(config.token).push(bindingToken); + return bindingToken; + } else { + multiRegistrations.set(config.token, [config.token]); + return config.token; + } + } + } + /** + * Register additional interfaces for a config + * @internal + */ + registerAdditionalInterfaces(container2, config, bindingToken, registeredTokens) { + if (config.additionalTokens) { + for (const additionalToken of config.additionalTokens) { + container2.bindFactory(additionalToken, (c) => c.resolve(bindingToken), { lifetime: config.lifetime }); + registeredTokens.add(additionalToken); + } + } + } + /** + * Build the container with all registered bindings + */ + build() { + const container2 = this.baseContainer.createChild(); + this.resolveInterfaceTokens(container2); + const registeredTokens = /* @__PURE__ */ new Set(); + const namedRegistrations = /* @__PURE__ */ new Map(); + const keyedRegistrations = /* @__PURE__ */ new Map(); + const multiRegistrations = /* @__PURE__ */ new Map(); + const tokensWithNonDefaults = this.identifyNonDefaultTokens(); + for (const config of this.registrations) { + if (this.shouldSkipRegistration(config, tokensWithNonDefaults, registeredTokens)) { + continue; + } + const bindingToken = this.createBindingToken(config, namedRegistrations, keyedRegistrations, multiRegistrations); + this.applyRegistration(container2, { ...config, token: bindingToken }); + registeredTokens.add(config.token); + this.registerAdditionalInterfaces(container2, config, bindingToken, registeredTokens); + } + ; + container2.__namedRegistrations = namedRegistrations; + container2.__keyedRegistrations = keyedRegistrations; + container2.__multiRegistrations = multiRegistrations; + return container2; + } + /** + * Analyze constructor to detect dependencies + * @internal + */ + analyzeConstructor(constructor) { + const constructorStr = constructor.toString(); + const hasDependencies = /constructor\s*\([^)]+\)/.test(constructorStr); + return { hasDependencies }; + } + /** + * Create optimized factory for zero-dependency constructors + * @internal + */ + createOptimizedFactory(container2, config, options) { + if (config.lifetime === "singleton") { + const instance = new config.constructor(); + container2.bindValue(config.token, instance); + } else if (config.lifetime === "transient") { + const ctor = config.constructor; + const fastFactory = /* @__PURE__ */ __name(() => new ctor(), "fastFactory"); + container2.fastTransientCache.set(config.token, fastFactory); + container2.bindFactory(config.token, fastFactory, options); + } else { + const factory = /* @__PURE__ */ __name(() => new config.constructor(), "factory"); + container2.bindFactory(config.token, factory, options); + } + } + /** + * Create autowire factory + * @internal + */ + createAutoWireFactory(container2, config, options) { + const factory = /* @__PURE__ */ __name((c) => { + const resolvedDeps = autowire(config.constructor, c, config.autowireOptions); + return new config.constructor(...resolvedDeps); + }, "factory"); + container2.bindFactory(config.token, factory, options); + } + /** + * Create withParameters factory + * @internal + */ + createParameterFactory(container2, config, options) { + const factory = /* @__PURE__ */ __name(() => { + const values = Object.values(config.parameterValues); + return new config.constructor(...values); + }, "factory"); + container2.bindFactory(config.token, factory, options); + } + /** + * Apply type registration (class constructor) + * @internal + */ + applyTypeRegistration(container2, config, options) { + const { hasDependencies } = this.analyzeConstructor(config.constructor); + if (!hasDependencies && !config.autowireOptions && !config.parameterValues) { + this.createOptimizedFactory(container2, config, options); + return; + } + if (config.autowireOptions) { + this.createAutoWireFactory(container2, config, options); + return; + } + if (config.parameterValues) { + this.createParameterFactory(container2, config, options); + return; + } + if (hasDependencies) { + const className = config.constructor.name || "UnnamedClass"; + throw new Error(`Service "${className}" has constructor dependencies but no autowiring configuration. + +Solutions: + 1. \u2B50 Use the NovaDI transformer (recommended): + - Add "@novadi/core/unplugin" to your build config + - Transformer automatically generates .autoWire() for all dependencies + + 2. Add manual autowiring: + .autoWire({ map: { /* param: resolver */ } }) + + 3. Use a factory function: + .register((c) => new ${className}(...)) + +See docs: https://github.com/janus007/NovaDI#autowire`); + } + const factory = /* @__PURE__ */ __name(() => new config.constructor(), "factory"); + container2.bindFactory(config.token, factory, options); + } + applyRegistration(container2, config) { + const options = { lifetime: config.lifetime }; + switch (config.type) { + case "instance": + container2.bindValue(config.token, config.value); + break; + case "factory": + container2.bindFactory(config.token, config.factory, options); + break; + case "type": + this.applyTypeRegistration(container2, config, options); + break; + } + } +}; +__name(_Builder, "Builder"); +var Builder = _Builder; + +// node_modules/@novadi/core/dist/container.js +function isDisposable(obj) { + return obj && typeof obj.dispose === "function"; +} +__name(isDisposable, "isDisposable"); +var _ResolutionContext = class _ResolutionContext { + constructor() { + this.resolvingStack = /* @__PURE__ */ new Set(); + this.perRequestCache = /* @__PURE__ */ new Map(); + } + isResolving(token2) { + return this.resolvingStack.has(token2); + } + enterResolve(token2) { + this.resolvingStack.add(token2); + } + exitResolve(token2) { + this.resolvingStack.delete(token2); + this.path = void 0; + } + getPath() { + if (!this.path) { + this.path = Array.from(this.resolvingStack).map((t) => t.toString()); + } + return [...this.path]; + } + cachePerRequest(token2, instance) { + this.perRequestCache.set(token2, instance); + } + getPerRequest(token2) { + return this.perRequestCache.get(token2); + } + hasPerRequest(token2) { + return this.perRequestCache.has(token2); + } + /** + * Reset context for reuse in object pool + * Performance: Reusing contexts avoids heap allocations + */ + reset() { + this.resolvingStack.clear(); + this.perRequestCache.clear(); + this.path = void 0; + } +}; +__name(_ResolutionContext, "ResolutionContext"); +var ResolutionContext = _ResolutionContext; +var _ResolutionContextPool = class _ResolutionContextPool { + constructor() { + this.pool = []; + this.maxSize = 10; + } + acquire() { + const context = this.pool.pop(); + if (context) { + context.reset(); + return context; + } + return new ResolutionContext(); + } + release(context) { + if (this.pool.length < this.maxSize) { + this.pool.push(context); + } + } +}; +__name(_ResolutionContextPool, "ResolutionContextPool"); +var ResolutionContextPool = _ResolutionContextPool; +var _Container = class _Container { + constructor(parent) { + this.bindings = /* @__PURE__ */ new Map(); + this.singletonCache = /* @__PURE__ */ new Map(); + this.singletonOrder = []; + this.interfaceRegistry = /* @__PURE__ */ new Map(); + this.interfaceTokenCache = /* @__PURE__ */ new Map(); + this.fastTransientCache = /* @__PURE__ */ new Map(); + this.ultraFastSingletonCache = /* @__PURE__ */ new Map(); + this.parent = parent; + } + /** + * Bind a pre-created value to a token + */ + bindValue(token2, value) { + this.bindings.set(token2, { + type: "value", + lifetime: "singleton", + value, + constructor: void 0 + }); + this.invalidateBindingCache(); + } + /** + * Bind a factory function to a token + */ + bindFactory(token2, factory, options) { + this.bindings.set(token2, { + type: "factory", + lifetime: options?.lifetime || "transient", + factory, + dependencies: options?.dependencies, + constructor: void 0 + }); + this.invalidateBindingCache(); + } + /** + * Bind a class constructor to a token + */ + bindClass(token2, constructor, options) { + const binding = { + type: "class", + lifetime: options?.lifetime || "transient", + constructor, + dependencies: options?.dependencies + }; + this.bindings.set(token2, binding); + this.invalidateBindingCache(); + if (binding.lifetime === "transient" && (!binding.dependencies || binding.dependencies.length === 0)) { + this.fastTransientCache.set(token2, () => new constructor()); + } + } + /** + * Resolve a dependency synchronously + * Performance optimized with multiple fast paths + */ + resolve(token2) { + const cached = this.tryGetFromCaches(token2); + if (cached !== void 0) { + return cached; + } + if (this.currentContext) { + return this.resolveWithContext(token2, this.currentContext); + } + const context = _Container.contextPool.acquire(); + this.currentContext = context; + try { + return this.resolveWithContext(token2, context); + } finally { + this.currentContext = void 0; + _Container.contextPool.release(context); + } + } + /** + * SPECIALIZED: Ultra-fast singleton resolve (no safety checks) + * Use ONLY when you're 100% sure the token is a registered singleton + * @internal For performance-critical paths only + */ + resolveSingletonUnsafe(token2) { + return this.ultraFastSingletonCache.get(token2) ?? this.singletonCache.get(token2); + } + /** + * SPECIALIZED: Fast transient resolve for zero-dependency classes + * Skips all context creation and circular dependency checks + * @internal For performance-critical paths only + */ + resolveTransientSimple(token2) { + const factory = this.fastTransientCache.get(token2); + if (factory) { + return factory(); + } + return this.resolve(token2); + } + /** + * SPECIALIZED: Batch resolve multiple dependencies at once + * More efficient than multiple individual resolves + */ + resolveBatch(tokens) { + const wasResolving = !!this.currentContext; + const context = this.currentContext || _Container.contextPool.acquire(); + if (!wasResolving) { + this.currentContext = context; + } + try { + const results = tokens.map((token2) => { + const cached = this.tryGetFromCaches(token2); + if (cached !== void 0) + return cached; + return this.resolveWithContext(token2, context); + }); + return results; + } finally { + if (!wasResolving) { + this.currentContext = void 0; + _Container.contextPool.release(context); + } + } + } + /** + * Resolve a dependency asynchronously (supports async factories) + */ + async resolveAsync(token2) { + if (this.currentContext) { + return this.resolveAsyncWithContext(token2, this.currentContext); + } + const context = _Container.contextPool.acquire(); + this.currentContext = context; + try { + return await this.resolveAsyncWithContext(token2, context); + } finally { + this.currentContext = void 0; + _Container.contextPool.release(context); + } + } + /** + * Try to get instance from all cache levels + * Returns undefined if not cached + * @internal + */ + tryGetFromCaches(token2) { + const ultraFast = this.ultraFastSingletonCache.get(token2); + if (ultraFast !== void 0) { + return ultraFast; + } + if (this.singletonCache.has(token2)) { + const cached = this.singletonCache.get(token2); + this.ultraFastSingletonCache.set(token2, cached); + return cached; + } + const fastFactory = this.fastTransientCache.get(token2); + if (fastFactory) { + return fastFactory(); + } + return void 0; + } + /** + * Cache instance based on lifetime strategy + * @internal + */ + cacheInstance(token2, instance, lifetime, context) { + if (lifetime === "singleton") { + this.singletonCache.set(token2, instance); + this.singletonOrder.push(token2); + this.ultraFastSingletonCache.set(token2, instance); + } else if (lifetime === "per-request" && context) { + context.cachePerRequest(token2, instance); + } + } + /** + * Validate and get binding with circular dependency check + * Returns binding or throws error + * @internal + */ + validateAndGetBinding(token2, context) { + if (context.isResolving(token2)) { + throw new CircularDependencyError([...context.getPath(), token2.toString()]); + } + const binding = this.getBinding(token2); + if (!binding) { + throw new BindingNotFoundError(token2.toString(), context.getPath()); + } + return binding; + } + /** + * Instantiate from binding synchronously + * @internal + */ + instantiateBindingSync(binding, token2, context) { + switch (binding.type) { + case "value": + return binding.value; + case "factory": + const result = binding.factory(this); + if (result instanceof Promise) { + throw new Error(`Async factory detected for ${token2.toString()}. Use resolveAsync() instead.`); + } + return result; + case "class": + const deps = binding.dependencies || []; + const resolvedDeps = deps.map((dep) => this.resolveWithContext(dep, context)); + return new binding.constructor(...resolvedDeps); + case "inline-class": + return new binding.constructor(); + default: + throw new Error(`Unknown binding type: ${binding.type}`); + } + } + /** + * Instantiate from binding asynchronously + * @internal + */ + async instantiateBindingAsync(binding, context) { + switch (binding.type) { + case "value": + return binding.value; + case "factory": + return await Promise.resolve(binding.factory(this)); + case "class": + const deps = binding.dependencies || []; + const resolvedDeps = await Promise.all(deps.map((dep) => this.resolveAsyncWithContext(dep, context))); + return new binding.constructor(...resolvedDeps); + case "inline-class": + return new binding.constructor(); + default: + throw new Error(`Unknown binding type: ${binding.type}`); + } + } + /** + * Create a child container that inherits bindings from this container + */ + createChild() { + return new _Container(this); + } + /** + * Dispose all singleton instances in reverse registration order + */ + async dispose() { + const errors = []; + for (let i = this.singletonOrder.length - 1; i >= 0; i--) { + const token2 = this.singletonOrder[i]; + const instance = this.singletonCache.get(token2); + if (instance && isDisposable(instance)) { + try { + await instance.dispose(); + } catch (error) { + errors.push(error); + } + } + } + this.singletonCache.clear(); + this.singletonOrder.length = 0; + } + /** + * Create a fluent builder for registering dependencies + */ + builder() { + return new Builder(this); + } + /** + * Resolve a named service + */ + resolveNamed(name) { + const namedRegistrations = this.__namedRegistrations; + if (!namedRegistrations) { + throw new Error(`Named service "${name}" not found. No named registrations exist.`); + } + const config = namedRegistrations.get(name); + if (!config) { + throw new Error(`Named service "${name}" not found`); + } + return this.resolve(config.token); + } + /** + * Resolve a keyed service + */ + resolveKeyed(key) { + const keyedRegistrations = this.__keyedRegistrations; + if (!keyedRegistrations) { + throw new Error(`Keyed service not found. No keyed registrations exist.`); + } + const config = keyedRegistrations.get(key); + if (!config) { + const keyStr = typeof key === "symbol" ? key.toString() : `"${key}"`; + throw new Error(`Keyed service ${keyStr} not found`); + } + return this.resolve(config.token); + } + /** + * Resolve all registrations for a token + */ + resolveAll(token2) { + const multiRegistrations = this.__multiRegistrations; + if (!multiRegistrations) { + return []; + } + const tokens = multiRegistrations.get(token2); + if (!tokens || tokens.length === 0) { + return []; + } + return tokens.map((t) => this.resolve(t)); + } + /** + * Get registry information for debugging/visualization + * Returns array of binding information + */ + getRegistry() { + const registry = []; + this.bindings.forEach((binding, token2) => { + registry.push({ + token: token2.description || token2.symbol.toString(), + type: binding.type, + lifetime: binding.lifetime, + dependencies: binding.dependencies?.map((d) => d.description || d.symbol.toString()) + }); + }); + return registry; + } + /** + * Get or create a token for an interface type + * Uses a type name hash as key for the interface registry + */ + interfaceToken(typeName) { + const key = typeName || `Interface_${Math.random().toString(36).substr(2, 9)}`; + if (this.interfaceRegistry.has(key)) { + return this.interfaceRegistry.get(key); + } + if (this.parent) { + const parentToken = this.parent.interfaceToken(key); + return parentToken; + } + const token2 = Token(key); + this.interfaceRegistry.set(key, token2); + return token2; + } + /** + * Resolve a dependency by interface type without explicit token + */ + resolveType(typeName) { + const key = typeName || ""; + let token2 = this.interfaceTokenCache.get(key); + if (!token2) { + token2 = this.interfaceToken(typeName); + this.interfaceTokenCache.set(key, token2); + } + return this.resolve(token2); + } + /** + * Resolve a keyed interface + */ + resolveTypeKeyed(key, _typeName) { + return this.resolveKeyed(key); + } + /** + * Resolve all registrations for an interface type + */ + resolveTypeAll(typeName) { + const token2 = this.interfaceToken(typeName); + return this.resolveAll(token2); + } + /** + * Internal: Resolve with context for circular dependency detection + */ + resolveWithContext(token2, context) { + const binding = this.validateAndGetBinding(token2, context); + if (binding.lifetime === "per-request" && context.hasPerRequest(token2)) { + return context.getPerRequest(token2); + } + if (binding.lifetime === "singleton" && this.singletonCache.has(token2)) { + return this.singletonCache.get(token2); + } + context.enterResolve(token2); + try { + const instance = this.instantiateBindingSync(binding, token2, context); + this.cacheInstance(token2, instance, binding.lifetime, context); + return instance; + } finally { + context.exitResolve(token2); + } + } + /** + * Internal: Async resolve with context + */ + async resolveAsyncWithContext(token2, context) { + const binding = this.validateAndGetBinding(token2, context); + if (binding.lifetime === "per-request" && context.hasPerRequest(token2)) { + return context.getPerRequest(token2); + } + if (binding.lifetime === "singleton" && this.singletonCache.has(token2)) { + return this.singletonCache.get(token2); + } + context.enterResolve(token2); + try { + const instance = await this.instantiateBindingAsync(binding, context); + this.cacheInstance(token2, instance, binding.lifetime, context); + return instance; + } finally { + context.exitResolve(token2); + } + } + /** + * Get binding from this container or parent chain + * Performance optimized: Uses flat cache to avoid recursive parent lookups + */ + getBinding(token2) { + if (!this.bindingCache) { + this.buildBindingCache(); + } + return this.bindingCache.get(token2); + } + /** + * Build flat cache of all bindings including parent chain + * This converts O(n) parent chain traversal to O(1) lookup + */ + buildBindingCache() { + this.bindingCache = /* @__PURE__ */ new Map(); + let current = this; + while (current) { + current.bindings.forEach((binding, token2) => { + if (!this.bindingCache.has(token2)) { + this.bindingCache.set(token2, binding); + } + }); + current = current.parent; + } + } + /** + * Invalidate binding cache when new bindings are added + * Called by bindValue, bindFactory, bindClass + */ + invalidateBindingCache() { + this.bindingCache = void 0; + this.ultraFastSingletonCache.clear(); + } +}; +__name(_Container, "Container"); +var Container = _Container; +Container.contextPool = new ResolutionContextPool(); + +// src/v2/features/date/DateRenderer.ts +var _DateRenderer = class _DateRenderer { + constructor(dateService) { + this.dateService = dateService; + this.type = "date"; + } + render(context) { + const dates = context.filter["date"] || []; + const resourceIds = context.filter["resource"] || []; + const dateGrouping = context.groupings?.find((g) => g.type === "date"); + const hideHeader = dateGrouping?.hideHeader === true; + const iterations = resourceIds.length || 1; + let columnCount = 0; + for (let r = 0; r < iterations; r++) { + const resourceId = resourceIds[r]; + for (const dateStr of dates) { + const date = this.dateService.parseISO(dateStr); + const segments = { date: dateStr }; + if (resourceId) + segments.resource = resourceId; + const columnKey = this.dateService.buildColumnKey(segments); + const header = document.createElement("swp-day-header"); + header.dataset.date = dateStr; + header.dataset.columnKey = columnKey; + if (resourceId) { + header.dataset.resourceId = resourceId; + } + if (hideHeader) { + header.dataset.hidden = "true"; + } + header.innerHTML = ` + ${this.dateService.getDayName(date, "short")} + ${date.getDate()} + `; + context.headerContainer.appendChild(header); + const column = document.createElement("swp-day-column"); + column.dataset.date = dateStr; + column.dataset.columnKey = columnKey; + if (resourceId) { + column.dataset.resourceId = resourceId; + } + column.innerHTML = ""; + context.columnContainer.appendChild(column); + columnCount++; + } + } + const container2 = context.columnContainer.closest("swp-calendar-container"); + if (container2) { + container2.style.setProperty("--grid-columns", String(columnCount)); + } + } +}; +__name(_DateRenderer, "DateRenderer"); +var DateRenderer = _DateRenderer; + +// src/v2/core/DateService.ts +var import_dayjs = __toESM(require_dayjs_min(), 1); +var import_utc = __toESM(require_utc(), 1); +var import_timezone = __toESM(require_timezone(), 1); +var import_isoWeek = __toESM(require_isoWeek(), 1); +import_dayjs.default.extend(import_utc.default); +import_dayjs.default.extend(import_timezone.default); +import_dayjs.default.extend(import_isoWeek.default); +var _DateService = class _DateService { + constructor(config, baseDate) { + this.config = config; + this.timezone = config.timezone; + this.baseDate = baseDate ? (0, import_dayjs.default)(baseDate) : (0, import_dayjs.default)(); + } + /** + * Set a fixed base date (useful for demos with static mock data) + */ + setBaseDate(date) { + this.baseDate = (0, import_dayjs.default)(date); + } + /** + * Get the current base date (either fixed or today) + */ + getBaseDate() { + return this.baseDate.toDate(); + } + parseISO(isoString) { + return (0, import_dayjs.default)(isoString).toDate(); + } + getDayName(date, format = "short") { + return new Intl.DateTimeFormat(this.config.locale, { weekday: format }).format(date); + } + getWeekDates(offset = 0, days = 7) { + const monday = this.baseDate.startOf("week").add(1, "day").add(offset, "week"); + return Array.from({ length: days }, (_, i) => monday.add(i, "day").format("YYYY-MM-DD")); + } + /** + * Get dates for specific weekdays within a week + * @param offset - Week offset from base date (0 = current week) + * @param workDays - Array of ISO weekday numbers (1=Monday, 7=Sunday) + * @returns Array of date strings in YYYY-MM-DD format + */ + getWorkWeekDates(offset, workDays) { + const monday = this.baseDate.startOf("week").add(1, "day").add(offset, "week"); + return workDays.map((isoDay) => { + const daysFromMonday = isoDay === 7 ? 6 : isoDay - 1; + return monday.add(daysFromMonday, "day").format("YYYY-MM-DD"); + }); + } + // ============================================ + // FORMATTING + // ============================================ + formatTime(date, showSeconds = false) { + const pattern = showSeconds ? "HH:mm:ss" : "HH:mm"; + return (0, import_dayjs.default)(date).format(pattern); + } + formatTimeRange(start, end) { + return `${this.formatTime(start)} - ${this.formatTime(end)}`; + } + formatDate(date) { + return (0, import_dayjs.default)(date).format("YYYY-MM-DD"); + } + getDateKey(date) { + return this.formatDate(date); + } + // ============================================ + // COLUMN KEY + // ============================================ + /** + * Build a uniform columnKey from grouping segments + * Handles any combination of date, resource, team, etc. + * + * @example + * buildColumnKey({ date: '2025-12-09' }) → "2025-12-09" + * buildColumnKey({ date: '2025-12-09', resource: 'EMP001' }) → "2025-12-09:EMP001" + */ + buildColumnKey(segments) { + const date = segments.date; + const others = Object.entries(segments).filter(([k]) => k !== "date").sort(([a], [b]) => a.localeCompare(b)).map(([, v]) => v); + return date ? [date, ...others].join(":") : others.join(":"); + } + /** + * Parse a columnKey back into segments + * Assumes format: "date:resource:..." or just "date" + */ + parseColumnKey(columnKey) { + const parts = columnKey.split(":"); + return { + date: parts[0], + resource: parts[1] + }; + } + /** + * Extract dateKey from columnKey (first segment) + */ + getDateFromColumnKey(columnKey) { + return columnKey.split(":")[0]; + } + // ============================================ + // TIME CALCULATIONS + // ============================================ + timeToMinutes(timeString) { + const parts = timeString.split(":").map(Number); + const hours = parts[0] || 0; + const minutes = parts[1] || 0; + return hours * 60 + minutes; + } + minutesToTime(totalMinutes) { + const hours = Math.floor(totalMinutes / 60); + const minutes = totalMinutes % 60; + return (0, import_dayjs.default)().hour(hours).minute(minutes).format("HH:mm"); + } + getMinutesSinceMidnight(date) { + const d = (0, import_dayjs.default)(date); + return d.hour() * 60 + d.minute(); + } + // ============================================ + // UTC CONVERSIONS + // ============================================ + toUTC(localDate) { + return import_dayjs.default.tz(localDate, this.timezone).utc().toISOString(); + } + fromUTC(utcString) { + return import_dayjs.default.utc(utcString).tz(this.timezone).toDate(); + } + // ============================================ + // DATE CREATION + // ============================================ + createDateAtTime(baseDate, timeString) { + const totalMinutes = this.timeToMinutes(timeString); + const hours = Math.floor(totalMinutes / 60); + const minutes = totalMinutes % 60; + return (0, import_dayjs.default)(baseDate).startOf("day").hour(hours).minute(minutes).toDate(); + } + getISOWeekDay(date) { + return (0, import_dayjs.default)(date).isoWeekday(); + } +}; +__name(_DateService, "DateService"); +var DateService = _DateService; + +// src/v2/core/BaseGroupingRenderer.ts +var _BaseGroupingRenderer = class _BaseGroupingRenderer { + /** + * Main render method - handles common logic + */ + async render(context) { + const allowedIds = context.filter[this.type] || []; + if (allowedIds.length === 0) + return; + const entities = await this.getEntities(allowedIds); + const dateCount = context.filter["date"]?.length || 1; + const childIds = context.childType ? context.filter[context.childType] || [] : []; + for (const entity of entities) { + const entityChildIds = context.parentChildMap?.[entity.id] || []; + const childCount = entityChildIds.filter((id) => childIds.includes(id)).length; + const colspan = childCount * dateCount; + const header = document.createElement(this.config.elementTag); + header.dataset[this.config.idAttribute] = entity.id; + header.style.setProperty(this.config.colspanVar, String(colspan)); + this.renderHeader(entity, header, context); + context.headerContainer.appendChild(header); + } + } + /** + * Override this method for custom header rendering + * Default: just sets textContent to display name + */ + renderHeader(entity, header, _context) { + header.textContent = this.getDisplayName(entity); + } + /** + * Helper to render a single entity header. + * Can be used by subclasses that override render() but want consistent header creation. + */ + createHeader(entity, context) { + const header = document.createElement(this.config.elementTag); + header.dataset[this.config.idAttribute] = entity.id; + this.renderHeader(entity, header, context); + return header; + } +}; +__name(_BaseGroupingRenderer, "BaseGroupingRenderer"); +var BaseGroupingRenderer = _BaseGroupingRenderer; + +// src/v2/features/resource/ResourceRenderer.ts +var _ResourceRenderer = class _ResourceRenderer extends BaseGroupingRenderer { + constructor(resourceService) { + super(); + this.resourceService = resourceService; + this.type = "resource"; + this.config = { + elementTag: "swp-resource-header", + idAttribute: "resourceId", + colspanVar: "--resource-cols" + }; + } + getEntities(ids) { + return this.resourceService.getByIds(ids); + } + getDisplayName(entity) { + return entity.displayName; + } + /** + * Override render to handle: + * 1. Special ordering when parentChildMap exists (resources grouped by parent) + * 2. Different colspan calculation (just dateCount, not childCount * dateCount) + */ + async render(context) { + const resourceIds = context.filter["resource"] || []; + const dateCount = context.filter["date"]?.length || 1; + let orderedResourceIds; + if (context.parentChildMap) { + orderedResourceIds = []; + for (const childIds of Object.values(context.parentChildMap)) { + for (const childId of childIds) { + if (resourceIds.includes(childId)) { + orderedResourceIds.push(childId); + } + } + } + } else { + orderedResourceIds = resourceIds; + } + const resources = await this.getEntities(orderedResourceIds); + const resourceMap = new Map(resources.map((r) => [r.id, r])); + for (const resourceId of orderedResourceIds) { + const resource = resourceMap.get(resourceId); + if (!resource) + continue; + const header = this.createHeader(resource, context); + header.style.gridColumn = `span ${dateCount}`; + context.headerContainer.appendChild(header); + } + } +}; +__name(_ResourceRenderer, "ResourceRenderer"); +var ResourceRenderer = _ResourceRenderer; + +// src/v2/features/team/TeamRenderer.ts +var _TeamRenderer = class _TeamRenderer extends BaseGroupingRenderer { + constructor(teamService) { + super(); + this.teamService = teamService; + this.type = "team"; + this.config = { + elementTag: "swp-team-header", + idAttribute: "teamId", + colspanVar: "--team-cols" + }; + } + getEntities(ids) { + return this.teamService.getByIds(ids); + } + getDisplayName(entity) { + return entity.name; + } +}; +__name(_TeamRenderer, "TeamRenderer"); +var TeamRenderer = _TeamRenderer; + +// src/v2/features/department/DepartmentRenderer.ts +var _DepartmentRenderer = class _DepartmentRenderer extends BaseGroupingRenderer { + constructor(departmentService) { + super(); + this.departmentService = departmentService; + this.type = "department"; + this.config = { + elementTag: "swp-department-header", + idAttribute: "departmentId", + colspanVar: "--department-cols" + }; + } + getEntities(ids) { + return this.departmentService.getByIds(ids); + } + getDisplayName(entity) { + return entity.name; + } +}; +__name(_DepartmentRenderer, "DepartmentRenderer"); +var DepartmentRenderer = _DepartmentRenderer; + +// src/v2/core/RenderBuilder.ts +function buildPipeline(renderers) { + return { + async run(context) { + for (const renderer of renderers) { + await renderer.render(context); + } + } + }; +} +__name(buildPipeline, "buildPipeline"); + +// src/v2/core/FilterTemplate.ts +var _FilterTemplate = class _FilterTemplate { + constructor(dateService, entityResolver) { + this.dateService = dateService; + this.entityResolver = entityResolver; + this.fields = []; + } + /** + * Tilføj felt til template + * @param idProperty - Property-navn (bruges på både event og column.dataset) + * @param derivedFrom - Hvis feltet udledes fra anden property (f.eks. date fra start) + */ + addField(idProperty, derivedFrom) { + this.fields.push({ idProperty, derivedFrom }); + return this; + } + /** + * Parse dot-notation string into components + * @example 'resource.teamId' → { entityType: 'resource', property: 'teamId', foreignKey: 'resourceId' } + */ + parseDotNotation(idProperty) { + if (!idProperty.includes(".")) + return null; + const [entityType, property] = idProperty.split("."); + return { + entityType, + property, + foreignKey: entityType + "Id" + // Convention: resource → resourceId + }; + } + /** + * Get dataset key for column lookup + * For dot-notation 'resource.teamId', we look for 'teamId' in dataset + */ + getDatasetKey(idProperty) { + const dotNotation = this.parseDotNotation(idProperty); + if (dotNotation) { + return dotNotation.property; + } + return idProperty; + } + /** + * Byg nøgle fra kolonne + * Læser værdier fra column.dataset[idProperty] + * For dot-notation, uses the property part (resource.teamId → teamId) + */ + buildKeyFromColumn(column) { + return this.fields.map((f) => { + const key = this.getDatasetKey(f.idProperty); + return column.dataset[key] || ""; + }).join(":"); + } + /** + * Byg nøgle fra event + * Læser værdier fra event[idProperty] eller udleder fra derivedFrom + * For dot-notation, resolves via EntityResolver + */ + buildKeyFromEvent(event) { + const eventRecord = event; + return this.fields.map((f) => { + const dotNotation = this.parseDotNotation(f.idProperty); + if (dotNotation) { + return this.resolveDotNotation(eventRecord, dotNotation); + } + if (f.derivedFrom) { + const sourceValue = eventRecord[f.derivedFrom]; + if (sourceValue instanceof Date) { + return this.dateService.getDateKey(sourceValue); + } + return String(sourceValue || ""); + } + return String(eventRecord[f.idProperty] || ""); + }).join(":"); + } + /** + * Resolve dot-notation reference via EntityResolver + */ + resolveDotNotation(eventRecord, dotNotation) { + if (!this.entityResolver) { + console.warn(`FilterTemplate: EntityResolver required for dot-notation '${dotNotation.entityType}.${dotNotation.property}'`); + return ""; + } + const foreignId = eventRecord[dotNotation.foreignKey]; + if (!foreignId) + return ""; + const entity = this.entityResolver.resolve(dotNotation.entityType, String(foreignId)); + if (!entity) + return ""; + return String(entity[dotNotation.property] || ""); + } + /** + * Match event mod kolonne + */ + matches(event, column) { + return this.buildKeyFromEvent(event) === this.buildKeyFromColumn(column); + } +}; +__name(_FilterTemplate, "FilterTemplate"); +var FilterTemplate = _FilterTemplate; + +// src/v2/core/CalendarOrchestrator.ts +var _CalendarOrchestrator = class _CalendarOrchestrator { + constructor(allRenderers, eventRenderer, scheduleRenderer, headerDrawerRenderer, dateService, entityServices) { + this.allRenderers = allRenderers; + this.eventRenderer = eventRenderer; + this.scheduleRenderer = scheduleRenderer; + this.headerDrawerRenderer = headerDrawerRenderer; + this.dateService = dateService; + this.entityServices = entityServices; + } + async render(viewConfig, container2) { + const headerContainer = container2.querySelector("swp-calendar-header"); + const columnContainer = container2.querySelector("swp-day-columns"); + if (!headerContainer || !columnContainer) { + throw new Error("Missing swp-calendar-header or swp-day-columns"); + } + const filter = {}; + for (const grouping of viewConfig.groupings) { + filter[grouping.type] = grouping.values; + } + const filterTemplate = new FilterTemplate(this.dateService); + for (const grouping of viewConfig.groupings) { + if (grouping.idProperty) { + filterTemplate.addField(grouping.idProperty, grouping.derivedFrom); + } + } + const { parentChildMap, childType } = await this.resolveBelongsTo(viewConfig.groupings, filter); + const context = { headerContainer, columnContainer, filter, groupings: viewConfig.groupings, parentChildMap, childType }; + headerContainer.innerHTML = ""; + columnContainer.innerHTML = ""; + const levels = viewConfig.groupings.map((g) => g.type).join(" "); + headerContainer.dataset.levels = levels; + const activeRenderers = this.selectRenderers(viewConfig); + const pipeline = buildPipeline(activeRenderers); + await pipeline.run(context); + await this.scheduleRenderer.render(container2, filter); + await this.eventRenderer.render(container2, filter, filterTemplate); + await this.headerDrawerRenderer.render(container2, filter, filterTemplate); + } + selectRenderers(viewConfig) { + const types = viewConfig.groupings.map((g) => g.type); + return types.map((type) => this.allRenderers.find((r) => r.type === type)).filter((r) => r !== void 0); + } + /** + * Resolve belongsTo relations to build parent-child map + * e.g., belongsTo: 'team.resourceIds' → { team1: ['EMP001', 'EMP002'], team2: [...] } + * Also returns the childType (the grouping type that has belongsTo) + */ + async resolveBelongsTo(groupings, filter) { + const childGrouping = groupings.find((g) => g.belongsTo); + if (!childGrouping?.belongsTo) + return {}; + const [entityType, property] = childGrouping.belongsTo.split("."); + if (!entityType || !property) + return {}; + const parentIds = filter[entityType] || []; + if (parentIds.length === 0) + return {}; + const service = this.entityServices.find((s) => s.entityType.toLowerCase() === entityType); + if (!service) + return {}; + const allEntities = await service.getAll(); + const entities = allEntities.filter((e) => parentIds.includes(e.id)); + const map = {}; + for (const entity of entities) { + const entityRecord = entity; + const children = entityRecord[property] || []; + map[entityRecord.id] = children; + } + return { parentChildMap: map, childType: childGrouping.type }; + } +}; +__name(_CalendarOrchestrator, "CalendarOrchestrator"); +var CalendarOrchestrator = _CalendarOrchestrator; + +// src/v2/core/NavigationAnimator.ts +var _NavigationAnimator = class _NavigationAnimator { + constructor(headerTrack, contentTrack) { + this.headerTrack = headerTrack; + this.contentTrack = contentTrack; + } + async slide(direction, renderFn) { + const out = direction === "left" ? "-100%" : "100%"; + const into = direction === "left" ? "100%" : "-100%"; + await this.animateOut(out); + await renderFn(); + await this.animateIn(into); + } + async animateOut(translate) { + await Promise.all([ + this.headerTrack.animate([{ transform: "translateX(0)" }, { transform: `translateX(${translate})` }], { duration: 200, easing: "ease-in" }).finished, + this.contentTrack.animate([{ transform: "translateX(0)" }, { transform: `translateX(${translate})` }], { duration: 200, easing: "ease-in" }).finished + ]); + } + async animateIn(translate) { + await Promise.all([ + this.headerTrack.animate([{ transform: `translateX(${translate})` }, { transform: "translateX(0)" }], { duration: 200, easing: "ease-out" }).finished, + this.contentTrack.animate([{ transform: `translateX(${translate})` }, { transform: "translateX(0)" }], { duration: 200, easing: "ease-out" }).finished + ]); + } +}; +__name(_NavigationAnimator, "NavigationAnimator"); +var NavigationAnimator = _NavigationAnimator; + +// src/v2/core/CalendarEvents.ts +var CalendarEvents = { + // Command events (host → calendar) + CMD_NAVIGATE_PREV: "calendar:cmd:navigate:prev", + CMD_NAVIGATE_NEXT: "calendar:cmd:navigate:next", + CMD_DRAWER_TOGGLE: "calendar:cmd:drawer:toggle", + CMD_RENDER: "calendar:cmd:render", + CMD_WORKWEEK_CHANGE: "calendar:cmd:workweek:change", + CMD_VIEW_UPDATE: "calendar:cmd:view:update" +}; + +// src/v2/core/CalendarApp.ts +var _CalendarApp = class _CalendarApp { + constructor(orchestrator, timeAxisRenderer, dateService, scrollManager, headerDrawerManager, dragDropManager, edgeScrollManager, resizeManager, headerDrawerRenderer, eventPersistenceManager, settingsService, viewConfigService, eventBus) { + this.orchestrator = orchestrator; + this.timeAxisRenderer = timeAxisRenderer; + this.dateService = dateService; + this.scrollManager = scrollManager; + this.headerDrawerManager = headerDrawerManager; + this.dragDropManager = dragDropManager; + this.edgeScrollManager = edgeScrollManager; + this.resizeManager = resizeManager; + this.headerDrawerRenderer = headerDrawerRenderer; + this.eventPersistenceManager = eventPersistenceManager; + this.settingsService = settingsService; + this.viewConfigService = viewConfigService; + this.eventBus = eventBus; + this.weekOffset = 0; + this.currentViewId = "simple"; + this.workweekPreset = null; + this.groupingOverrides = /* @__PURE__ */ new Map(); + } + async init(container2) { + this.container = container2; + const gridSettings = await this.settingsService.getGridSettings(); + if (!gridSettings) { + throw new Error("GridSettings not found"); + } + this.workweekPreset = await this.settingsService.getDefaultWorkweekPreset(); + this.animator = new NavigationAnimator(container2.querySelector("swp-header-track"), container2.querySelector("swp-content-track")); + this.timeAxisRenderer.render(container2.querySelector("#time-axis"), gridSettings.dayStartHour, gridSettings.dayEndHour); + this.scrollManager.init(container2); + this.headerDrawerManager.init(container2); + this.dragDropManager.init(container2); + this.resizeManager.init(container2); + const scrollableContent = container2.querySelector("swp-scrollable-content"); + this.edgeScrollManager.init(scrollableContent); + this.setupEventListeners(); + this.emitStatus("ready"); + } + setupEventListeners() { + this.eventBus.on(CalendarEvents.CMD_NAVIGATE_PREV, () => { + this.handleNavigatePrev(); + }); + this.eventBus.on(CalendarEvents.CMD_NAVIGATE_NEXT, () => { + this.handleNavigateNext(); + }); + this.eventBus.on(CalendarEvents.CMD_DRAWER_TOGGLE, () => { + this.headerDrawerManager.toggle(); + }); + this.eventBus.on(CalendarEvents.CMD_RENDER, (e) => { + const { viewId } = e.detail; + this.handleRenderCommand(viewId); + }); + this.eventBus.on(CalendarEvents.CMD_WORKWEEK_CHANGE, (e) => { + const { presetId } = e.detail; + this.handleWorkweekChange(presetId); + }); + this.eventBus.on(CalendarEvents.CMD_VIEW_UPDATE, (e) => { + const { type, values } = e.detail; + this.handleViewUpdate(type, values); + }); + } + async handleRenderCommand(viewId) { + this.currentViewId = viewId; + await this.render(); + this.emitStatus("rendered", { viewId }); + } + async handleNavigatePrev() { + this.weekOffset--; + await this.animator.slide("right", () => this.render()); + this.emitStatus("rendered", { viewId: this.currentViewId }); + } + async handleNavigateNext() { + this.weekOffset++; + await this.animator.slide("left", () => this.render()); + this.emitStatus("rendered", { viewId: this.currentViewId }); + } + async handleWorkweekChange(presetId) { + const preset = await this.settingsService.getWorkweekPreset(presetId); + if (preset) { + this.workweekPreset = preset; + await this.render(); + this.emitStatus("rendered", { viewId: this.currentViewId }); + } + } + async handleViewUpdate(type, values) { + this.groupingOverrides.set(type, values); + await this.render(); + this.emitStatus("rendered", { viewId: this.currentViewId }); + } + async render() { + const storedConfig = await this.viewConfigService.getById(this.currentViewId); + if (!storedConfig) { + this.emitStatus("error", { message: `ViewConfig not found: ${this.currentViewId}` }); + return; + } + const workDays = this.workweekPreset?.workDays || [1, 2, 3, 4, 5]; + const dates = this.currentViewId === "day" ? this.dateService.getWeekDates(this.weekOffset, 1) : this.dateService.getWorkWeekDates(this.weekOffset, workDays); + const viewConfig = { + ...storedConfig, + groupings: storedConfig.groupings.map((g) => { + if (g.type === "date") { + return { ...g, values: dates }; + } + const override = this.groupingOverrides.get(g.type); + if (override) { + return { ...g, values: override }; + } + return g; + }) + }; + await this.orchestrator.render(viewConfig, this.container); + } + emitStatus(status, detail) { + this.container.dispatchEvent(new CustomEvent(`calendar:status:${status}`, { + detail, + bubbles: true + })); + } +}; +__name(_CalendarApp, "CalendarApp"); +var CalendarApp = _CalendarApp; + +// src/v2/features/timeaxis/TimeAxisRenderer.ts +var _TimeAxisRenderer = class _TimeAxisRenderer { + render(container2, startHour = 6, endHour = 20) { + container2.innerHTML = ""; + for (let hour = startHour; hour <= endHour; hour++) { + const marker = document.createElement("swp-hour-marker"); + marker.textContent = `${hour.toString().padStart(2, "0")}:00`; + container2.appendChild(marker); + } + } +}; +__name(_TimeAxisRenderer, "TimeAxisRenderer"); +var TimeAxisRenderer = _TimeAxisRenderer; + +// src/v2/core/ScrollManager.ts +var _ScrollManager = class _ScrollManager { + init(container2) { + this.scrollableContent = container2.querySelector("swp-scrollable-content"); + this.timeAxisContent = container2.querySelector("swp-time-axis-content"); + this.calendarHeader = container2.querySelector("swp-calendar-header"); + this.headerDrawer = container2.querySelector("swp-header-drawer"); + this.headerViewport = container2.querySelector("swp-header-viewport"); + this.headerSpacer = container2.querySelector("swp-header-spacer"); + this.scrollableContent.addEventListener("scroll", () => this.onScroll()); + this.resizeObserver = new ResizeObserver(() => this.syncHeaderSpacerHeight()); + this.resizeObserver.observe(this.headerViewport); + this.syncHeaderSpacerHeight(); + } + syncHeaderSpacerHeight() { + const computedHeight = getComputedStyle(this.headerViewport).height; + this.headerSpacer.style.height = computedHeight; + } + onScroll() { + const { scrollTop, scrollLeft } = this.scrollableContent; + this.timeAxisContent.style.transform = `translateY(-${scrollTop}px)`; + this.calendarHeader.style.transform = `translateX(-${scrollLeft}px)`; + this.headerDrawer.style.transform = `translateX(-${scrollLeft}px)`; + } +}; +__name(_ScrollManager, "ScrollManager"); +var ScrollManager = _ScrollManager; + +// src/v2/core/HeaderDrawerManager.ts +var _HeaderDrawerManager = class _HeaderDrawerManager { + constructor() { + this.expanded = false; + this.currentRows = 0; + this.rowHeight = 25; + this.duration = 200; + } + init(container2) { + this.drawer = container2.querySelector("swp-header-drawer"); + if (!this.drawer) + console.error("HeaderDrawerManager: swp-header-drawer not found"); + } + toggle() { + this.expanded ? this.collapse() : this.expand(); + } + /** + * Expand drawer to single row (legacy support) + */ + expand() { + this.expandToRows(1); + } + /** + * Expand drawer to fit specified number of rows + */ + expandToRows(rowCount) { + const targetHeight = rowCount * this.rowHeight; + const currentHeight = this.expanded ? this.currentRows * this.rowHeight : 0; + if (this.expanded && this.currentRows === rowCount) + return; + this.currentRows = rowCount; + this.expanded = true; + this.animate(currentHeight, targetHeight); + } + collapse() { + if (!this.expanded) + return; + const currentHeight = this.currentRows * this.rowHeight; + this.expanded = false; + this.currentRows = 0; + this.animate(currentHeight, 0); + } + animate(from, to) { + const keyframes = [ + { height: `${from}px` }, + { height: `${to}px` } + ]; + const options = { + duration: this.duration, + easing: "ease", + fill: "forwards" + }; + this.drawer.animate(keyframes, options); + } + isExpanded() { + return this.expanded; + } + getRowCount() { + return this.currentRows; + } +}; +__name(_HeaderDrawerManager, "HeaderDrawerManager"); +var HeaderDrawerManager = _HeaderDrawerManager; + +// src/v2/demo/MockStores.ts +var _MockTeamStore = class _MockTeamStore { + constructor() { + this.type = "team"; + this.teams = [ + { id: "alpha", name: "Team Alpha" }, + { id: "beta", name: "Team Beta" } + ]; + } + getByIds(ids) { + return this.teams.filter((t) => ids.includes(t.id)); + } +}; +__name(_MockTeamStore, "MockTeamStore"); +var MockTeamStore = _MockTeamStore; +var _MockResourceStore = class _MockResourceStore { + constructor() { + this.type = "resource"; + this.resources = [ + { id: "alice", name: "Alice", teamId: "alpha" }, + { id: "bob", name: "Bob", teamId: "alpha" }, + { id: "carol", name: "Carol", teamId: "beta" }, + { id: "dave", name: "Dave", teamId: "beta" } + ]; + } + getByIds(ids) { + return this.resources.filter((r) => ids.includes(r.id)); + } +}; +__name(_MockResourceStore, "MockResourceStore"); +var MockResourceStore = _MockResourceStore; + +// src/v2/demo/DemoApp.ts +var _DemoApp = class _DemoApp { + constructor(indexedDBContext, dataSeeder, auditService, calendarApp, dateService, resourceService, eventBus) { + this.indexedDBContext = indexedDBContext; + this.dataSeeder = dataSeeder; + this.auditService = auditService; + this.calendarApp = calendarApp; + this.dateService = dateService; + this.resourceService = resourceService; + this.eventBus = eventBus; + this.currentView = "simple"; + } + async init() { + this.dateService.setBaseDate(/* @__PURE__ */ new Date("2025-12-08")); + await this.indexedDBContext.initialize(); + console.log("[DemoApp] IndexedDB initialized"); + await this.dataSeeder.seedIfEmpty(); + console.log("[DemoApp] Data seeding complete"); + this.container = document.querySelector("swp-calendar-container"); + await this.calendarApp.init(this.container); + console.log("[DemoApp] CalendarApp initialized"); + this.setupNavigation(); + this.setupDrawerToggle(); + this.setupViewSwitching(); + this.setupWorkweekSelector(); + await this.setupResourceSelector(); + this.setupStatusListeners(); + this.eventBus.emit(CalendarEvents.CMD_RENDER, { viewId: this.currentView }); + } + setupNavigation() { + document.getElementById("btn-prev").onclick = () => { + this.eventBus.emit(CalendarEvents.CMD_NAVIGATE_PREV); + }; + document.getElementById("btn-next").onclick = () => { + this.eventBus.emit(CalendarEvents.CMD_NAVIGATE_NEXT); + }; + } + setupViewSwitching() { + const chips = document.querySelectorAll(".view-chip"); + chips.forEach((chip) => { + chip.addEventListener("click", () => { + chips.forEach((c) => c.classList.remove("active")); + chip.classList.add("active"); + const view = chip.dataset.view; + if (view) { + this.currentView = view; + this.updateSelectorVisibility(); + this.eventBus.emit(CalendarEvents.CMD_RENDER, { viewId: view }); + } + }); + }); + } + updateSelectorVisibility() { + const selector = document.querySelector("swp-resource-selector"); + const showSelector = this.currentView === "picker" || this.currentView === "day"; + selector?.classList.toggle("hidden", !showSelector); + } + setupDrawerToggle() { + document.getElementById("btn-drawer").onclick = () => { + this.eventBus.emit(CalendarEvents.CMD_DRAWER_TOGGLE); + }; + } + setupWorkweekSelector() { + const workweekSelect = document.getElementById("workweek-select"); + workweekSelect?.addEventListener("change", () => { + const presetId = workweekSelect.value; + this.eventBus.emit(CalendarEvents.CMD_WORKWEEK_CHANGE, { presetId }); + }); + } + async setupResourceSelector() { + const resources = await this.resourceService.getAll(); + const container2 = document.querySelector(".resource-checkboxes"); + if (!container2) + return; + container2.innerHTML = ""; + resources.forEach((r) => { + const label = document.createElement("label"); + label.innerHTML = ` + + ${r.displayName} + `; + container2.appendChild(label); + }); + container2.addEventListener("change", () => { + const checked = container2.querySelectorAll("input:checked"); + const values = Array.from(checked).map((cb) => cb.value); + this.eventBus.emit(CalendarEvents.CMD_VIEW_UPDATE, { type: "resource", values }); + }); + } + setupStatusListeners() { + this.container.addEventListener("calendar:status:ready", () => { + console.log("[DemoApp] Calendar ready"); + }); + this.container.addEventListener("calendar:status:rendered", (e) => { + console.log("[DemoApp] Calendar rendered:", e.detail.viewId); + }); + this.container.addEventListener("calendar:status:error", (e) => { + console.error("[DemoApp] Calendar error:", e.detail.message); + }); + } +}; +__name(_DemoApp, "DemoApp"); +var DemoApp = _DemoApp; + +// src/v2/core/EventBus.ts +var _EventBus = class _EventBus { + constructor() { + this.eventLog = []; + this.debug = false; + this.listeners = /* @__PURE__ */ new Set(); + this.logConfig = { + calendar: true, + grid: true, + event: true, + scroll: true, + navigation: true, + view: true, + default: true + }; + } + /** + * Subscribe to an event via DOM addEventListener + */ + on(eventType, handler, options) { + document.addEventListener(eventType, handler, options); + this.listeners.add({ eventType, handler, options }); + return () => this.off(eventType, handler); + } + /** + * Subscribe to an event once + */ + once(eventType, handler) { + return this.on(eventType, handler, { once: true }); + } + /** + * Unsubscribe from an event + */ + off(eventType, handler) { + document.removeEventListener(eventType, handler); + for (const listener of this.listeners) { + if (listener.eventType === eventType && listener.handler === handler) { + this.listeners.delete(listener); + break; + } + } + } + /** + * Emit an event via DOM CustomEvent + */ + emit(eventType, detail = {}) { + if (!eventType) { + return false; + } + const event = new CustomEvent(eventType, { + detail: detail ?? {}, + bubbles: true, + cancelable: true + }); + if (this.debug) { + this.logEventWithGrouping(eventType, detail); + } + this.eventLog.push({ + type: eventType, + detail: detail ?? {}, + timestamp: Date.now() + }); + return !document.dispatchEvent(event); + } + /** + * Log event with console grouping + */ + logEventWithGrouping(eventType, _detail) { + const category = this.extractCategory(eventType); + if (!this.logConfig[category]) { + return; + } + this.getCategoryStyle(category); + } + /** + * Extract category from event type + */ + extractCategory(eventType) { + if (!eventType) { + return "unknown"; + } + if (eventType.includes(":")) { + return eventType.split(":")[0]; + } + const lowerType = eventType.toLowerCase(); + if (lowerType.includes("grid") || lowerType.includes("rendered")) + return "grid"; + if (lowerType.includes("event") || lowerType.includes("sync")) + return "event"; + if (lowerType.includes("scroll")) + return "scroll"; + if (lowerType.includes("nav") || lowerType.includes("date")) + return "navigation"; + if (lowerType.includes("view")) + return "view"; + return "default"; + } + /** + * Get styling for different categories + */ + getCategoryStyle(category) { + const styles = { + calendar: { emoji: "\u{1F4C5}", color: "#2196F3" }, + grid: { emoji: "\u{1F4CA}", color: "#4CAF50" }, + event: { emoji: "\u{1F4CC}", color: "#FF9800" }, + scroll: { emoji: "\u{1F4DC}", color: "#9C27B0" }, + navigation: { emoji: "\u{1F9ED}", color: "#F44336" }, + view: { emoji: "\u{1F441}", color: "#00BCD4" }, + default: { emoji: "\u{1F4E2}", color: "#607D8B" } + }; + return styles[category] || styles.default; + } + /** + * Configure logging for specific categories + */ + setLogConfig(config) { + this.logConfig = { ...this.logConfig, ...config }; + } + /** + * Get current log configuration + */ + getLogConfig() { + return { ...this.logConfig }; + } + /** + * Get event history + */ + getEventLog(eventType) { + if (eventType) { + return this.eventLog.filter((e) => e.type === eventType); + } + return this.eventLog; + } + /** + * Enable/disable debug mode + */ + setDebug(enabled) { + this.debug = enabled; + } +}; +__name(_EventBus, "EventBus"); +var EventBus = _EventBus; + +// src/v2/storage/IndexedDBContext.ts +var _IndexedDBContext = class _IndexedDBContext { + constructor(stores) { + this.db = null; + this.initialized = false; + this.stores = stores; + } + /** + * Initialize and open the database + */ + async initialize() { + return new Promise((resolve, reject) => { + const request = indexedDB.open(_IndexedDBContext.DB_NAME, _IndexedDBContext.DB_VERSION); + request.onerror = () => { + reject(new Error(`Failed to open IndexedDB: ${request.error}`)); + }; + request.onsuccess = () => { + this.db = request.result; + this.initialized = true; + resolve(); + }; + request.onupgradeneeded = (event) => { + const db = event.target.result; + this.stores.forEach((store) => { + if (!db.objectStoreNames.contains(store.storeName)) { + store.create(db); + } + }); + }; + }); + } + /** + * Check if database is initialized + */ + isInitialized() { + return this.initialized; + } + /** + * Get IDBDatabase instance + */ + getDatabase() { + if (!this.db) { + throw new Error("IndexedDB not initialized. Call initialize() first."); + } + return this.db; + } + /** + * Close database connection + */ + close() { + if (this.db) { + this.db.close(); + this.db = null; + this.initialized = false; + } + } + /** + * Delete entire database (for testing/reset) + */ + static async deleteDatabase() { + return new Promise((resolve, reject) => { + const request = indexedDB.deleteDatabase(_IndexedDBContext.DB_NAME); + request.onsuccess = () => resolve(); + request.onerror = () => reject(new Error(`Failed to delete database: ${request.error}`)); + }); + } +}; +__name(_IndexedDBContext, "IndexedDBContext"); +var IndexedDBContext = _IndexedDBContext; +IndexedDBContext.DB_NAME = "CalendarV2DB"; +IndexedDBContext.DB_VERSION = 4; + +// src/v2/storage/events/EventStore.ts +var _EventStore = class _EventStore { + constructor() { + this.storeName = _EventStore.STORE_NAME; + } + /** + * Create the events ObjectStore with indexes + */ + create(db) { + const store = db.createObjectStore(_EventStore.STORE_NAME, { keyPath: "id" }); + store.createIndex("start", "start", { unique: false }); + store.createIndex("end", "end", { unique: false }); + store.createIndex("syncStatus", "syncStatus", { unique: false }); + store.createIndex("resourceId", "resourceId", { unique: false }); + store.createIndex("customerId", "customerId", { unique: false }); + store.createIndex("bookingId", "bookingId", { unique: false }); + store.createIndex("startEnd", ["start", "end"], { unique: false }); + } +}; +__name(_EventStore, "EventStore"); +var EventStore = _EventStore; +EventStore.STORE_NAME = "events"; + +// src/v2/storage/events/EventSerialization.ts +var _EventSerialization = class _EventSerialization { + /** + * Serialize event for IndexedDB storage + */ + static serialize(event) { + return { + ...event, + start: event.start instanceof Date ? event.start.toISOString() : event.start, + end: event.end instanceof Date ? event.end.toISOString() : event.end + }; + } + /** + * Deserialize event from IndexedDB storage + */ + static deserialize(data) { + return { + ...data, + start: typeof data.start === "string" ? new Date(data.start) : data.start, + end: typeof data.end === "string" ? new Date(data.end) : data.end + }; + } +}; +__name(_EventSerialization, "EventSerialization"); +var EventSerialization = _EventSerialization; + +// src/v2/storage/SyncPlugin.ts +var _SyncPlugin = class _SyncPlugin { + constructor(service) { + this.service = service; + } + /** + * Mark entity as successfully synced + */ + async markAsSynced(id) { + const entity = await this.service.get(id); + if (entity) { + entity.syncStatus = "synced"; + await this.service.save(entity); + } + } + /** + * Mark entity as sync error + */ + async markAsError(id) { + const entity = await this.service.get(id); + if (entity) { + entity.syncStatus = "error"; + await this.service.save(entity); + } + } + /** + * Get current sync status for an entity + */ + async getSyncStatus(id) { + const entity = await this.service.get(id); + return entity ? entity.syncStatus : null; + } + /** + * Get entities by sync status using IndexedDB index + */ + async getBySyncStatus(syncStatus) { + return new Promise((resolve, reject) => { + const transaction = this.service.db.transaction([this.service.storeName], "readonly"); + const store = transaction.objectStore(this.service.storeName); + const index = store.index("syncStatus"); + const request = index.getAll(syncStatus); + request.onsuccess = () => { + const data = request.result; + const entities = data.map((item) => this.service.deserialize(item)); + resolve(entities); + }; + request.onerror = () => { + reject(new Error(`Failed to get by sync status ${syncStatus}: ${request.error}`)); + }; + }); + } +}; +__name(_SyncPlugin, "SyncPlugin"); +var SyncPlugin = _SyncPlugin; + +// src/v2/constants/CoreEvents.ts +var CoreEvents = { + // Lifecycle events + INITIALIZED: "core:initialized", + READY: "core:ready", + DESTROYED: "core:destroyed", + // View events + VIEW_CHANGED: "view:changed", + VIEW_RENDERED: "view:rendered", + // Navigation events + DATE_CHANGED: "nav:date-changed", + NAVIGATION_COMPLETED: "nav:navigation-completed", + // Data events + DATA_LOADING: "data:loading", + DATA_LOADED: "data:loaded", + DATA_ERROR: "data:error", + // Grid events + GRID_RENDERED: "grid:rendered", + GRID_CLICKED: "grid:clicked", + // Event management + EVENT_CREATED: "event:created", + EVENT_UPDATED: "event:updated", + EVENT_DELETED: "event:deleted", + EVENT_SELECTED: "event:selected", + // Event drag-drop + EVENT_DRAG_START: "event:drag-start", + EVENT_DRAG_MOVE: "event:drag-move", + EVENT_DRAG_END: "event:drag-end", + EVENT_DRAG_CANCEL: "event:drag-cancel", + EVENT_DRAG_COLUMN_CHANGE: "event:drag-column-change", + // Header drag (timed → header conversion) + EVENT_DRAG_ENTER_HEADER: "event:drag-enter-header", + EVENT_DRAG_MOVE_HEADER: "event:drag-move-header", + EVENT_DRAG_LEAVE_HEADER: "event:drag-leave-header", + // Event resize + EVENT_RESIZE_START: "event:resize-start", + EVENT_RESIZE_END: "event:resize-end", + // Edge scroll + EDGE_SCROLL_TICK: "edge-scroll:tick", + EDGE_SCROLL_STARTED: "edge-scroll:started", + EDGE_SCROLL_STOPPED: "edge-scroll:stopped", + // System events + ERROR: "system:error", + // Sync events + SYNC_STARTED: "sync:started", + SYNC_COMPLETED: "sync:completed", + SYNC_FAILED: "sync:failed", + // Entity events - for audit and sync + ENTITY_SAVED: "entity:saved", + ENTITY_DELETED: "entity:deleted", + // Audit events + AUDIT_LOGGED: "audit:logged", + // Rendering events + EVENTS_RENDERED: "events:rendered" +}; + +// node_modules/json-diff-ts/dist/index.js +function arrayDifference(first, second) { + const secondSet = new Set(second); + return first.filter((item) => !secondSet.has(item)); +} +__name(arrayDifference, "arrayDifference"); +function arrayIntersection(first, second) { + const secondSet = new Set(second); + return first.filter((item) => secondSet.has(item)); +} +__name(arrayIntersection, "arrayIntersection"); +function keyBy(arr, getKey2) { + const result = {}; + for (const item of arr) { + result[String(getKey2(item))] = item; + } + return result; +} +__name(keyBy, "keyBy"); +function diff(oldObj, newObj, options = {}) { + let { embeddedObjKeys } = options; + const { keysToSkip, treatTypeChangeAsReplace } = options; + if (embeddedObjKeys instanceof Map) { + embeddedObjKeys = new Map( + Array.from(embeddedObjKeys.entries()).map(([key, value]) => [ + key instanceof RegExp ? key : key.replace(/^\./, ""), + value + ]) + ); + } else if (embeddedObjKeys) { + embeddedObjKeys = Object.fromEntries( + Object.entries(embeddedObjKeys).map(([key, value]) => [key.replace(/^\./, ""), value]) + ); + } + return compare(oldObj, newObj, [], [], { + embeddedObjKeys, + keysToSkip: keysToSkip ?? [], + treatTypeChangeAsReplace: treatTypeChangeAsReplace ?? true + }); +} +__name(diff, "diff"); +var getTypeOfObj = /* @__PURE__ */ __name((obj) => { + if (typeof obj === "undefined") { + return "undefined"; + } + if (obj === null) { + return null; + } + return Object.prototype.toString.call(obj).match(/^\[object\s(.*)\]$/)[1]; +}, "getTypeOfObj"); +var getKey = /* @__PURE__ */ __name((path) => { + const left = path[path.length - 1]; + return left != null ? left : "$root"; +}, "getKey"); +var compare = /* @__PURE__ */ __name((oldObj, newObj, path, keyPath, options) => { + let changes = []; + const currentPath = keyPath.join("."); + if (options.keysToSkip?.some((skipPath) => { + if (currentPath === skipPath) { + return true; + } + if (skipPath.includes(".") && skipPath.startsWith(currentPath + ".")) { + return false; + } + if (skipPath.includes(".")) { + const skipParts = skipPath.split("."); + const currentParts = currentPath.split("."); + if (currentParts.length >= skipParts.length) { + for (let i = 0; i < skipParts.length; i++) { + if (skipParts[i] !== currentParts[i]) { + return false; + } + } + return true; + } + } + return false; + })) { + return changes; + } + const typeOfOldObj = getTypeOfObj(oldObj); + const typeOfNewObj = getTypeOfObj(newObj); + if (options.treatTypeChangeAsReplace && typeOfOldObj !== typeOfNewObj) { + if (typeOfOldObj !== "undefined") { + changes.push({ type: "REMOVE", key: getKey(path), value: oldObj }); + } + if (typeOfNewObj !== "undefined") { + changes.push({ type: "ADD", key: getKey(path), value: newObj }); + } + return changes; + } + if (typeOfNewObj === "undefined" && typeOfOldObj !== "undefined") { + changes.push({ type: "REMOVE", key: getKey(path), value: oldObj }); + return changes; + } + if (typeOfNewObj === "Object" && typeOfOldObj === "Array") { + changes.push({ type: "UPDATE", key: getKey(path), value: newObj, oldValue: oldObj }); + return changes; + } + if (typeOfNewObj === null) { + if (typeOfOldObj !== null) { + changes.push({ type: "UPDATE", key: getKey(path), value: newObj, oldValue: oldObj }); + } + return changes; + } + switch (typeOfOldObj) { + case "Date": + if (typeOfNewObj === "Date") { + changes = changes.concat( + comparePrimitives(oldObj.getTime(), newObj.getTime(), path).map((x) => ({ + ...x, + value: new Date(x.value), + oldValue: new Date(x.oldValue) + })) + ); + } else { + changes = changes.concat(comparePrimitives(oldObj, newObj, path)); + } + break; + case "Object": { + const diffs = compareObject(oldObj, newObj, path, keyPath, false, options); + if (diffs.length) { + if (path.length) { + changes.push({ + type: "UPDATE", + key: getKey(path), + changes: diffs + }); + } else { + changes = changes.concat(diffs); + } + } + break; + } + case "Array": + changes = changes.concat(compareArray(oldObj, newObj, path, keyPath, options)); + break; + case "Function": + break; + default: + changes = changes.concat(comparePrimitives(oldObj, newObj, path)); + } + return changes; +}, "compare"); +var compareObject = /* @__PURE__ */ __name((oldObj, newObj, path, keyPath, skipPath = false, options = {}) => { + let k; + let newKeyPath; + let newPath; + if (skipPath == null) { + skipPath = false; + } + let changes = []; + const oldObjKeys = Object.keys(oldObj); + const newObjKeys = Object.keys(newObj); + const intersectionKeys = arrayIntersection(oldObjKeys, newObjKeys); + for (k of intersectionKeys) { + newPath = path.concat([k]); + newKeyPath = skipPath ? keyPath : keyPath.concat([k]); + const diffs = compare(oldObj[k], newObj[k], newPath, newKeyPath, options); + if (diffs.length) { + changes = changes.concat(diffs); + } + } + const addedKeys = arrayDifference(newObjKeys, oldObjKeys); + for (k of addedKeys) { + newPath = path.concat([k]); + newKeyPath = skipPath ? keyPath : keyPath.concat([k]); + const currentPath = newKeyPath.join("."); + if (options.keysToSkip?.some((skipPath2) => currentPath === skipPath2 || currentPath.startsWith(skipPath2 + "."))) { + continue; + } + changes.push({ + type: "ADD", + key: getKey(newPath), + value: newObj[k] + }); + } + const deletedKeys = arrayDifference(oldObjKeys, newObjKeys); + for (k of deletedKeys) { + newPath = path.concat([k]); + newKeyPath = skipPath ? keyPath : keyPath.concat([k]); + const currentPath = newKeyPath.join("."); + if (options.keysToSkip?.some((skipPath2) => currentPath === skipPath2 || currentPath.startsWith(skipPath2 + "."))) { + continue; + } + changes.push({ + type: "REMOVE", + key: getKey(newPath), + value: oldObj[k] + }); + } + return changes; +}, "compareObject"); +var compareArray = /* @__PURE__ */ __name((oldObj, newObj, path, keyPath, options) => { + if (getTypeOfObj(newObj) !== "Array") { + return [{ type: "UPDATE", key: getKey(path), value: newObj, oldValue: oldObj }]; + } + const left = getObjectKey(options.embeddedObjKeys, keyPath); + const uniqKey = left != null ? left : "$index"; + const indexedOldObj = convertArrayToObj(oldObj, uniqKey); + const indexedNewObj = convertArrayToObj(newObj, uniqKey); + const diffs = compareObject(indexedOldObj, indexedNewObj, path, keyPath, true, options); + if (diffs.length) { + return [ + { + type: "UPDATE", + key: getKey(path), + embeddedKey: typeof uniqKey === "function" && uniqKey.length === 2 ? uniqKey(newObj[0], true) : uniqKey, + changes: diffs + } + ]; + } else { + return []; + } +}, "compareArray"); +var getObjectKey = /* @__PURE__ */ __name((embeddedObjKeys, keyPath) => { + if (embeddedObjKeys != null) { + const path = keyPath.join("."); + if (embeddedObjKeys instanceof Map) { + for (const [key2, value] of embeddedObjKeys.entries()) { + if (key2 instanceof RegExp) { + if (path.match(key2)) { + return value; + } + } else if (path === key2) { + return value; + } + } + } + const key = embeddedObjKeys[path]; + if (key != null) { + return key; + } + } + return void 0; +}, "getObjectKey"); +var convertArrayToObj = /* @__PURE__ */ __name((arr, uniqKey) => { + let obj = {}; + if (uniqKey === "$value") { + arr.forEach((value) => { + obj[value] = value; + }); + } else if (uniqKey !== "$index") { + const keyFunction = typeof uniqKey === "string" ? (item) => item[uniqKey] : uniqKey; + obj = keyBy(arr, keyFunction); + } else { + for (let i = 0; i < arr.length; i++) { + const value = arr[i]; + obj[i] = value; + } + } + return obj; +}, "convertArrayToObj"); +var comparePrimitives = /* @__PURE__ */ __name((oldObj, newObj, path) => { + const changes = []; + if (oldObj !== newObj) { + changes.push({ + type: "UPDATE", + key: getKey(path), + value: newObj, + oldValue: oldObj + }); + } + return changes; +}, "comparePrimitives"); + +// src/v2/storage/BaseEntityService.ts +var _BaseEntityService = class _BaseEntityService { + constructor(context, eventBus) { + this.context = context; + this.eventBus = eventBus; + this.syncPlugin = new SyncPlugin(this); + } + get db() { + return this.context.getDatabase(); + } + /** + * Serialize entity before storing in IndexedDB + */ + serialize(entity) { + return entity; + } + /** + * Deserialize data from IndexedDB back to entity + */ + deserialize(data) { + return data; + } + /** + * Get a single entity by ID + */ + async get(id) { + return new Promise((resolve, reject) => { + const transaction = this.db.transaction([this.storeName], "readonly"); + const store = transaction.objectStore(this.storeName); + const request = store.get(id); + request.onsuccess = () => { + const data = request.result; + resolve(data ? this.deserialize(data) : null); + }; + request.onerror = () => { + reject(new Error(`Failed to get ${this.entityType} ${id}: ${request.error}`)); + }; + }); + } + /** + * Get all entities + */ + async getAll() { + return new Promise((resolve, reject) => { + const transaction = this.db.transaction([this.storeName], "readonly"); + const store = transaction.objectStore(this.storeName); + const request = store.getAll(); + request.onsuccess = () => { + const data = request.result; + const entities = data.map((item) => this.deserialize(item)); + resolve(entities); + }; + request.onerror = () => { + reject(new Error(`Failed to get all ${this.entityType}s: ${request.error}`)); + }; + }); + } + /** + * Save an entity (create or update) + * Emits ENTITY_SAVED event with operation type and changes (diff for updates) + * @param entity - Entity to save + * @param silent - If true, skip event emission (used for seeding) + */ + async save(entity, silent = false) { + const entityId = entity.id; + const existingEntity = await this.get(entityId); + const isCreate = existingEntity === null; + let changes; + if (isCreate) { + changes = entity; + } else { + const existingSerialized = this.serialize(existingEntity); + const newSerialized = this.serialize(entity); + changes = diff(existingSerialized, newSerialized); + } + const serialized = this.serialize(entity); + return new Promise((resolve, reject) => { + const transaction = this.db.transaction([this.storeName], "readwrite"); + const store = transaction.objectStore(this.storeName); + const request = store.put(serialized); + request.onsuccess = () => { + if (!silent) { + const payload = { + entityType: this.entityType, + entityId, + operation: isCreate ? "create" : "update", + changes, + timestamp: Date.now() + }; + this.eventBus.emit(CoreEvents.ENTITY_SAVED, payload); + } + resolve(); + }; + request.onerror = () => { + reject(new Error(`Failed to save ${this.entityType} ${entityId}: ${request.error}`)); + }; + }); + } + /** + * Delete an entity + * Emits ENTITY_DELETED event + */ + async delete(id) { + return new Promise((resolve, reject) => { + const transaction = this.db.transaction([this.storeName], "readwrite"); + const store = transaction.objectStore(this.storeName); + const request = store.delete(id); + request.onsuccess = () => { + const payload = { + entityType: this.entityType, + entityId: id, + operation: "delete", + timestamp: Date.now() + }; + this.eventBus.emit(CoreEvents.ENTITY_DELETED, payload); + resolve(); + }; + request.onerror = () => { + reject(new Error(`Failed to delete ${this.entityType} ${id}: ${request.error}`)); + }; + }); + } + // Sync methods - delegate to SyncPlugin + async markAsSynced(id) { + return this.syncPlugin.markAsSynced(id); + } + async markAsError(id) { + return this.syncPlugin.markAsError(id); + } + async getSyncStatus(id) { + return this.syncPlugin.getSyncStatus(id); + } + async getBySyncStatus(syncStatus) { + return this.syncPlugin.getBySyncStatus(syncStatus); + } +}; +__name(_BaseEntityService, "BaseEntityService"); +var BaseEntityService = _BaseEntityService; + +// src/v2/storage/events/EventService.ts +var _EventService = class _EventService extends BaseEntityService { + constructor(context, eventBus) { + super(context, eventBus); + this.storeName = EventStore.STORE_NAME; + this.entityType = "Event"; + } + serialize(event) { + return EventSerialization.serialize(event); + } + deserialize(data) { + return EventSerialization.deserialize(data); + } + /** + * Get events within a date range + */ + async getByDateRange(start, end) { + return new Promise((resolve, reject) => { + const transaction = this.db.transaction([this.storeName], "readonly"); + const store = transaction.objectStore(this.storeName); + const index = store.index("start"); + const range = IDBKeyRange.lowerBound(start.toISOString()); + const request = index.getAll(range); + request.onsuccess = () => { + const data = request.result; + const events = data.map((item) => this.deserialize(item)).filter((event) => event.start <= end); + resolve(events); + }; + request.onerror = () => { + reject(new Error(`Failed to get events by date range: ${request.error}`)); + }; + }); + } + /** + * Get events for a specific resource + */ + async getByResource(resourceId) { + return new Promise((resolve, reject) => { + const transaction = this.db.transaction([this.storeName], "readonly"); + const store = transaction.objectStore(this.storeName); + const index = store.index("resourceId"); + const request = index.getAll(resourceId); + request.onsuccess = () => { + const data = request.result; + const events = data.map((item) => this.deserialize(item)); + resolve(events); + }; + request.onerror = () => { + reject(new Error(`Failed to get events for resource ${resourceId}: ${request.error}`)); + }; + }); + } + /** + * Get events for a resource within a date range + */ + async getByResourceAndDateRange(resourceId, start, end) { + const resourceEvents = await this.getByResource(resourceId); + return resourceEvents.filter((event) => event.start >= start && event.start <= end); + } +}; +__name(_EventService, "EventService"); +var EventService = _EventService; + +// src/v2/storage/resources/ResourceStore.ts +var _ResourceStore = class _ResourceStore { + constructor() { + this.storeName = _ResourceStore.STORE_NAME; + } + create(db) { + const store = db.createObjectStore(_ResourceStore.STORE_NAME, { keyPath: "id" }); + store.createIndex("type", "type", { unique: false }); + store.createIndex("syncStatus", "syncStatus", { unique: false }); + store.createIndex("isActive", "isActive", { unique: false }); + } +}; +__name(_ResourceStore, "ResourceStore"); +var ResourceStore = _ResourceStore; +ResourceStore.STORE_NAME = "resources"; + +// src/v2/storage/resources/ResourceService.ts +var _ResourceService = class _ResourceService extends BaseEntityService { + constructor(context, eventBus) { + super(context, eventBus); + this.storeName = ResourceStore.STORE_NAME; + this.entityType = "Resource"; + } + /** + * Get all active resources + */ + async getActive() { + const all = await this.getAll(); + return all.filter((r) => r.isActive !== false); + } + /** + * Get resources by IDs + */ + async getByIds(ids) { + if (ids.length === 0) + return []; + const results = await Promise.all(ids.map((id) => this.get(id))); + return results.filter((r) => r !== null); + } + /** + * Get resources by type + */ + async getByType(type) { + return new Promise((resolve, reject) => { + const transaction = this.db.transaction([this.storeName], "readonly"); + const store = transaction.objectStore(this.storeName); + const index = store.index("type"); + const request = index.getAll(type); + request.onsuccess = () => { + const data = request.result; + resolve(data); + }; + request.onerror = () => { + reject(new Error(`Failed to get resources by type ${type}: ${request.error}`)); + }; + }); + } +}; +__name(_ResourceService, "ResourceService"); +var ResourceService = _ResourceService; + +// src/v2/storage/bookings/BookingStore.ts +var _BookingStore = class _BookingStore { + constructor() { + this.storeName = _BookingStore.STORE_NAME; + } + create(db) { + const store = db.createObjectStore(_BookingStore.STORE_NAME, { keyPath: "id" }); + store.createIndex("customerId", "customerId", { unique: false }); + store.createIndex("status", "status", { unique: false }); + store.createIndex("syncStatus", "syncStatus", { unique: false }); + store.createIndex("createdAt", "createdAt", { unique: false }); + } +}; +__name(_BookingStore, "BookingStore"); +var BookingStore = _BookingStore; +BookingStore.STORE_NAME = "bookings"; + +// src/v2/storage/bookings/BookingService.ts +var _BookingService = class _BookingService extends BaseEntityService { + constructor(context, eventBus) { + super(context, eventBus); + this.storeName = BookingStore.STORE_NAME; + this.entityType = "Booking"; + } + serialize(booking) { + return { + ...booking, + createdAt: booking.createdAt.toISOString() + }; + } + deserialize(data) { + const raw = data; + return { + ...raw, + createdAt: new Date(raw.createdAt) + }; + } + /** + * Get bookings for a customer + */ + async getByCustomer(customerId) { + return new Promise((resolve, reject) => { + const transaction = this.db.transaction([this.storeName], "readonly"); + const store = transaction.objectStore(this.storeName); + const index = store.index("customerId"); + const request = index.getAll(customerId); + request.onsuccess = () => { + const data = request.result; + const bookings = data.map((item) => this.deserialize(item)); + resolve(bookings); + }; + request.onerror = () => { + reject(new Error(`Failed to get bookings for customer ${customerId}: ${request.error}`)); + }; + }); + } + /** + * Get bookings by status + */ + async getByStatus(status) { + return new Promise((resolve, reject) => { + const transaction = this.db.transaction([this.storeName], "readonly"); + const store = transaction.objectStore(this.storeName); + const index = store.index("status"); + const request = index.getAll(status); + request.onsuccess = () => { + const data = request.result; + const bookings = data.map((item) => this.deserialize(item)); + resolve(bookings); + }; + request.onerror = () => { + reject(new Error(`Failed to get bookings with status ${status}: ${request.error}`)); + }; + }); + } +}; +__name(_BookingService, "BookingService"); +var BookingService = _BookingService; + +// src/v2/storage/customers/CustomerStore.ts +var _CustomerStore = class _CustomerStore { + constructor() { + this.storeName = _CustomerStore.STORE_NAME; + } + create(db) { + const store = db.createObjectStore(_CustomerStore.STORE_NAME, { keyPath: "id" }); + store.createIndex("name", "name", { unique: false }); + store.createIndex("phone", "phone", { unique: false }); + store.createIndex("syncStatus", "syncStatus", { unique: false }); + } +}; +__name(_CustomerStore, "CustomerStore"); +var CustomerStore = _CustomerStore; +CustomerStore.STORE_NAME = "customers"; + +// src/v2/storage/customers/CustomerService.ts +var _CustomerService = class _CustomerService extends BaseEntityService { + constructor(context, eventBus) { + super(context, eventBus); + this.storeName = CustomerStore.STORE_NAME; + this.entityType = "Customer"; + } + /** + * Search customers by name (case-insensitive contains) + */ + async searchByName(query) { + const all = await this.getAll(); + const lowerQuery = query.toLowerCase(); + return all.filter((c) => c.name.toLowerCase().includes(lowerQuery)); + } + /** + * Find customer by phone + */ + async getByPhone(phone) { + return new Promise((resolve, reject) => { + const transaction = this.db.transaction([this.storeName], "readonly"); + const store = transaction.objectStore(this.storeName); + const index = store.index("phone"); + const request = index.get(phone); + request.onsuccess = () => { + const data = request.result; + resolve(data ? data : null); + }; + request.onerror = () => { + reject(new Error(`Failed to find customer by phone ${phone}: ${request.error}`)); + }; + }); + } +}; +__name(_CustomerService, "CustomerService"); +var CustomerService = _CustomerService; + +// src/v2/storage/teams/TeamStore.ts +var _TeamStore = class _TeamStore { + constructor() { + this.storeName = _TeamStore.STORE_NAME; + } + create(db) { + db.createObjectStore(_TeamStore.STORE_NAME, { keyPath: "id" }); + } +}; +__name(_TeamStore, "TeamStore"); +var TeamStore = _TeamStore; +TeamStore.STORE_NAME = "teams"; + +// src/v2/storage/teams/TeamService.ts +var _TeamService = class _TeamService extends BaseEntityService { + constructor(context, eventBus) { + super(context, eventBus); + this.storeName = TeamStore.STORE_NAME; + this.entityType = "Team"; + } + /** + * Get teams by IDs + */ + async getByIds(ids) { + if (ids.length === 0) + return []; + const results = await Promise.all(ids.map((id) => this.get(id))); + return results.filter((t) => t !== null); + } + /** + * Build reverse lookup: resourceId → teamId + */ + async buildResourceToTeamMap() { + const teams = await this.getAll(); + const map = {}; + for (const team of teams) { + for (const resourceId of team.resourceIds) { + map[resourceId] = team.id; + } + } + return map; + } +}; +__name(_TeamService, "TeamService"); +var TeamService = _TeamService; + +// src/v2/storage/departments/DepartmentStore.ts +var _DepartmentStore = class _DepartmentStore { + constructor() { + this.storeName = _DepartmentStore.STORE_NAME; + } + create(db) { + db.createObjectStore(_DepartmentStore.STORE_NAME, { keyPath: "id" }); + } +}; +__name(_DepartmentStore, "DepartmentStore"); +var DepartmentStore = _DepartmentStore; +DepartmentStore.STORE_NAME = "departments"; + +// src/v2/storage/departments/DepartmentService.ts +var _DepartmentService = class _DepartmentService extends BaseEntityService { + constructor(context, eventBus) { + super(context, eventBus); + this.storeName = DepartmentStore.STORE_NAME; + this.entityType = "Department"; + } + /** + * Get departments by IDs + */ + async getByIds(ids) { + if (ids.length === 0) + return []; + const results = await Promise.all(ids.map((id) => this.get(id))); + return results.filter((d) => d !== null); + } +}; +__name(_DepartmentService, "DepartmentService"); +var DepartmentService = _DepartmentService; + +// src/v2/storage/settings/SettingsStore.ts +var _SettingsStore = class _SettingsStore { + constructor() { + this.storeName = _SettingsStore.STORE_NAME; + } + create(db) { + db.createObjectStore(_SettingsStore.STORE_NAME, { keyPath: "id" }); + } +}; +__name(_SettingsStore, "SettingsStore"); +var SettingsStore = _SettingsStore; +SettingsStore.STORE_NAME = "settings"; + +// src/v2/types/SettingsTypes.ts +var SettingsIds = { + WORKWEEK: "workweek", + GRID: "grid", + TIME_FORMAT: "timeFormat", + VIEWS: "views" +}; + +// src/v2/storage/settings/SettingsService.ts +var _SettingsService = class _SettingsService extends BaseEntityService { + constructor(context, eventBus) { + super(context, eventBus); + this.storeName = SettingsStore.STORE_NAME; + this.entityType = "Settings"; + } + /** + * Get workweek settings + */ + async getWorkweekSettings() { + return this.get(SettingsIds.WORKWEEK); + } + /** + * Get grid settings + */ + async getGridSettings() { + return this.get(SettingsIds.GRID); + } + /** + * Get time format settings + */ + async getTimeFormatSettings() { + return this.get(SettingsIds.TIME_FORMAT); + } + /** + * Get view settings + */ + async getViewSettings() { + return this.get(SettingsIds.VIEWS); + } + /** + * Get workweek preset by ID + */ + async getWorkweekPreset(presetId) { + const settings = await this.getWorkweekSettings(); + if (!settings) + return null; + return settings.presets[presetId] || null; + } + /** + * Get the default workweek preset + */ + async getDefaultWorkweekPreset() { + const settings = await this.getWorkweekSettings(); + if (!settings) + return null; + return settings.presets[settings.defaultPreset] || null; + } + /** + * Get all available workweek presets + */ + async getWorkweekPresets() { + const settings = await this.getWorkweekSettings(); + if (!settings) + return []; + return Object.values(settings.presets); + } +}; +__name(_SettingsService, "SettingsService"); +var SettingsService = _SettingsService; + +// src/v2/storage/viewconfigs/ViewConfigStore.ts +var _ViewConfigStore = class _ViewConfigStore { + constructor() { + this.storeName = _ViewConfigStore.STORE_NAME; + } + create(db) { + db.createObjectStore(_ViewConfigStore.STORE_NAME, { keyPath: "id" }); + } +}; +__name(_ViewConfigStore, "ViewConfigStore"); +var ViewConfigStore = _ViewConfigStore; +ViewConfigStore.STORE_NAME = "viewconfigs"; + +// src/v2/storage/viewconfigs/ViewConfigService.ts +var _ViewConfigService = class _ViewConfigService extends BaseEntityService { + constructor(context, eventBus) { + super(context, eventBus); + this.storeName = ViewConfigStore.STORE_NAME; + this.entityType = "ViewConfig"; + } + async getById(id) { + return this.get(id); + } +}; +__name(_ViewConfigService, "ViewConfigService"); +var ViewConfigService = _ViewConfigService; + +// src/v2/storage/audit/AuditStore.ts +var _AuditStore = class _AuditStore { + constructor() { + this.storeName = "audit"; + } + create(db) { + const store = db.createObjectStore(this.storeName, { keyPath: "id" }); + store.createIndex("syncStatus", "syncStatus", { unique: false }); + store.createIndex("synced", "synced", { unique: false }); + store.createIndex("entityId", "entityId", { unique: false }); + store.createIndex("timestamp", "timestamp", { unique: false }); + } +}; +__name(_AuditStore, "AuditStore"); +var AuditStore = _AuditStore; + +// src/v2/storage/audit/AuditService.ts +var _AuditService = class _AuditService extends BaseEntityService { + constructor(context, eventBus) { + super(context, eventBus); + this.storeName = "audit"; + this.entityType = "Audit"; + this.setupEventListeners(); + } + /** + * Setup listeners for ENTITY_SAVED and ENTITY_DELETED events + */ + setupEventListeners() { + this.eventBus.on(CoreEvents.ENTITY_SAVED, (event) => { + const detail = event.detail; + this.handleEntitySaved(detail); + }); + this.eventBus.on(CoreEvents.ENTITY_DELETED, (event) => { + const detail = event.detail; + this.handleEntityDeleted(detail); + }); + } + /** + * Handle ENTITY_SAVED event - create audit entry + */ + async handleEntitySaved(payload) { + if (payload.entityType === "Audit") + return; + const auditEntry = { + id: crypto.randomUUID(), + entityType: payload.entityType, + entityId: payload.entityId, + operation: payload.operation, + userId: _AuditService.DEFAULT_USER_ID, + timestamp: payload.timestamp, + changes: payload.changes, + synced: false, + syncStatus: "pending" + }; + await this.save(auditEntry); + } + /** + * Handle ENTITY_DELETED event - create audit entry + */ + async handleEntityDeleted(payload) { + if (payload.entityType === "Audit") + return; + const auditEntry = { + id: crypto.randomUUID(), + entityType: payload.entityType, + entityId: payload.entityId, + operation: "delete", + userId: _AuditService.DEFAULT_USER_ID, + timestamp: payload.timestamp, + changes: { id: payload.entityId }, + // For delete, just store the ID + synced: false, + syncStatus: "pending" + }; + await this.save(auditEntry); + } + /** + * Override save to NOT trigger ENTITY_SAVED event + * Instead, emits AUDIT_LOGGED for SyncManager to listen + * + * This prevents infinite loops: + * - BaseEntityService.save() emits ENTITY_SAVED + * - AuditService listens to ENTITY_SAVED and creates audit + * - If AuditService.save() also emitted ENTITY_SAVED, it would loop + */ + async save(entity) { + const serialized = this.serialize(entity); + return new Promise((resolve, reject) => { + const transaction = this.db.transaction([this.storeName], "readwrite"); + const store = transaction.objectStore(this.storeName); + const request = store.put(serialized); + request.onsuccess = () => { + const payload = { + auditId: entity.id, + entityType: entity.entityType, + entityId: entity.entityId, + operation: entity.operation, + timestamp: entity.timestamp + }; + this.eventBus.emit(CoreEvents.AUDIT_LOGGED, payload); + resolve(); + }; + request.onerror = () => { + reject(new Error(`Failed to save audit entry ${entity.id}: ${request.error}`)); + }; + }); + } + /** + * Override delete to NOT trigger ENTITY_DELETED event + * Audit entries should never be deleted (compliance requirement) + */ + async delete(_id) { + throw new Error("Audit entries cannot be deleted (compliance requirement)"); + } + /** + * Get pending audit entries (for sync) + */ + async getPendingAudits() { + return this.getBySyncStatus("pending"); + } + /** + * Get audit entries for a specific entity + */ + async getByEntityId(entityId) { + return new Promise((resolve, reject) => { + const transaction = this.db.transaction([this.storeName], "readonly"); + const store = transaction.objectStore(this.storeName); + const index = store.index("entityId"); + const request = index.getAll(entityId); + request.onsuccess = () => { + const entries = request.result; + resolve(entries); + }; + request.onerror = () => { + reject(new Error(`Failed to get audit entries for entity ${entityId}: ${request.error}`)); + }; + }); + } +}; +__name(_AuditService, "AuditService"); +var AuditService = _AuditService; +AuditService.DEFAULT_USER_ID = "00000000-0000-0000-0000-000000000001"; + +// src/v2/repositories/MockEventRepository.ts +var _MockEventRepository = class _MockEventRepository { + constructor() { + this.entityType = "Event"; + this.dataUrl = "data/mock-events.json"; + } + /** + * Fetch all events from mock JSON file + */ + async fetchAll() { + try { + const response = await fetch(this.dataUrl); + if (!response.ok) { + throw new Error(`Failed to load mock events: ${response.status} ${response.statusText}`); + } + const rawData = await response.json(); + return this.processCalendarData(rawData); + } catch (error) { + console.error("Failed to load event data:", error); + throw error; + } + } + async sendCreate(_event) { + throw new Error("MockEventRepository does not support sendCreate. Mock data is read-only."); + } + async sendUpdate(_id, _updates) { + throw new Error("MockEventRepository does not support sendUpdate. Mock data is read-only."); + } + async sendDelete(_id) { + throw new Error("MockEventRepository does not support sendDelete. Mock data is read-only."); + } + processCalendarData(data) { + return data.map((event) => { + if (event.type === "customer") { + if (!event.bookingId) + console.warn(`Customer event ${event.id} missing bookingId`); + if (!event.resourceId) + console.warn(`Customer event ${event.id} missing resourceId`); + if (!event.customerId) + console.warn(`Customer event ${event.id} missing customerId`); + } + return { + id: event.id, + title: event.title, + description: event.description, + start: new Date(event.start), + end: new Date(event.end), + type: event.type, + allDay: event.allDay || false, + bookingId: event.bookingId, + resourceId: event.resourceId, + customerId: event.customerId, + recurringId: event.recurringId, + metadata: event.metadata, + syncStatus: "synced" + }; + }); + } +}; +__name(_MockEventRepository, "MockEventRepository"); +var MockEventRepository = _MockEventRepository; + +// src/v2/repositories/MockResourceRepository.ts +var _MockResourceRepository = class _MockResourceRepository { + constructor() { + this.entityType = "Resource"; + this.dataUrl = "data/mock-resources.json"; + } + async fetchAll() { + try { + const response = await fetch(this.dataUrl); + if (!response.ok) { + throw new Error(`Failed to load mock resources: ${response.status} ${response.statusText}`); + } + const rawData = await response.json(); + return this.processResourceData(rawData); + } catch (error) { + console.error("Failed to load resource data:", error); + throw error; + } + } + async sendCreate(_resource) { + throw new Error("MockResourceRepository does not support sendCreate. Mock data is read-only."); + } + async sendUpdate(_id, _updates) { + throw new Error("MockResourceRepository does not support sendUpdate. Mock data is read-only."); + } + async sendDelete(_id) { + throw new Error("MockResourceRepository does not support sendDelete. Mock data is read-only."); + } + processResourceData(data) { + return data.map((resource) => ({ + id: resource.id, + name: resource.name, + displayName: resource.displayName, + type: resource.type, + avatarUrl: resource.avatarUrl, + color: resource.color, + isActive: resource.isActive, + defaultSchedule: resource.defaultSchedule, + metadata: resource.metadata, + syncStatus: "synced" + })); + } +}; +__name(_MockResourceRepository, "MockResourceRepository"); +var MockResourceRepository = _MockResourceRepository; + +// src/v2/repositories/MockBookingRepository.ts +var _MockBookingRepository = class _MockBookingRepository { + constructor() { + this.entityType = "Booking"; + this.dataUrl = "data/mock-bookings.json"; + } + async fetchAll() { + try { + const response = await fetch(this.dataUrl); + if (!response.ok) { + throw new Error(`Failed to load mock bookings: ${response.status} ${response.statusText}`); + } + const rawData = await response.json(); + return this.processBookingData(rawData); + } catch (error) { + console.error("Failed to load booking data:", error); + throw error; + } + } + async sendCreate(_booking) { + throw new Error("MockBookingRepository does not support sendCreate. Mock data is read-only."); + } + async sendUpdate(_id, _updates) { + throw new Error("MockBookingRepository does not support sendUpdate. Mock data is read-only."); + } + async sendDelete(_id) { + throw new Error("MockBookingRepository does not support sendDelete. Mock data is read-only."); + } + processBookingData(data) { + return data.map((booking) => ({ + id: booking.id, + customerId: booking.customerId, + status: booking.status, + createdAt: new Date(booking.createdAt), + services: booking.services, + totalPrice: booking.totalPrice, + tags: booking.tags, + notes: booking.notes, + syncStatus: "synced" + })); + } +}; +__name(_MockBookingRepository, "MockBookingRepository"); +var MockBookingRepository = _MockBookingRepository; + +// src/v2/repositories/MockCustomerRepository.ts +var _MockCustomerRepository = class _MockCustomerRepository { + constructor() { + this.entityType = "Customer"; + this.dataUrl = "data/mock-customers.json"; + } + async fetchAll() { + try { + const response = await fetch(this.dataUrl); + if (!response.ok) { + throw new Error(`Failed to load mock customers: ${response.status} ${response.statusText}`); + } + const rawData = await response.json(); + return this.processCustomerData(rawData); + } catch (error) { + console.error("Failed to load customer data:", error); + throw error; + } + } + async sendCreate(_customer) { + throw new Error("MockCustomerRepository does not support sendCreate. Mock data is read-only."); + } + async sendUpdate(_id, _updates) { + throw new Error("MockCustomerRepository does not support sendUpdate. Mock data is read-only."); + } + async sendDelete(_id) { + throw new Error("MockCustomerRepository does not support sendDelete. Mock data is read-only."); + } + processCustomerData(data) { + return data.map((customer) => ({ + id: customer.id, + name: customer.name, + phone: customer.phone, + email: customer.email, + metadata: customer.metadata, + syncStatus: "synced" + })); + } +}; +__name(_MockCustomerRepository, "MockCustomerRepository"); +var MockCustomerRepository = _MockCustomerRepository; + +// src/v2/repositories/MockAuditRepository.ts +var _MockAuditRepository = class _MockAuditRepository { + constructor() { + this.entityType = "Audit"; + } + async sendCreate(entity) { + await new Promise((resolve) => setTimeout(resolve, 100)); + console.log("MockAuditRepository: Audit entry synced to backend:", { + id: entity.id, + entityType: entity.entityType, + entityId: entity.entityId, + operation: entity.operation, + timestamp: new Date(entity.timestamp).toISOString() + }); + return entity; + } + async sendUpdate(_id, _entity) { + throw new Error("Audit entries cannot be updated"); + } + async sendDelete(_id) { + throw new Error("Audit entries cannot be deleted"); + } + async fetchAll() { + return []; + } + async fetchById(_id) { + return null; + } +}; +__name(_MockAuditRepository, "MockAuditRepository"); +var MockAuditRepository = _MockAuditRepository; + +// src/v2/repositories/MockTeamRepository.ts +var _MockTeamRepository = class _MockTeamRepository { + constructor() { + this.entityType = "Team"; + this.dataUrl = "data/mock-teams.json"; + } + async fetchAll() { + try { + const response = await fetch(this.dataUrl); + if (!response.ok) { + throw new Error(`Failed to load mock teams: ${response.status} ${response.statusText}`); + } + const rawData = await response.json(); + return this.processTeamData(rawData); + } catch (error) { + console.error("Failed to load team data:", error); + throw error; + } + } + async sendCreate(_team) { + throw new Error("MockTeamRepository does not support sendCreate. Mock data is read-only."); + } + async sendUpdate(_id, _updates) { + throw new Error("MockTeamRepository does not support sendUpdate. Mock data is read-only."); + } + async sendDelete(_id) { + throw new Error("MockTeamRepository does not support sendDelete. Mock data is read-only."); + } + processTeamData(data) { + return data.map((team) => ({ + id: team.id, + name: team.name, + resourceIds: team.resourceIds, + syncStatus: "synced" + })); + } +}; +__name(_MockTeamRepository, "MockTeamRepository"); +var MockTeamRepository = _MockTeamRepository; + +// src/v2/repositories/MockDepartmentRepository.ts +var _MockDepartmentRepository = class _MockDepartmentRepository { + constructor() { + this.entityType = "Department"; + this.dataUrl = "data/mock-departments.json"; + } + async fetchAll() { + try { + const response = await fetch(this.dataUrl); + if (!response.ok) { + throw new Error(`Failed to load mock departments: ${response.status} ${response.statusText}`); + } + const rawData = await response.json(); + return this.processDepartmentData(rawData); + } catch (error) { + console.error("Failed to load department data:", error); + throw error; + } + } + async sendCreate(_department) { + throw new Error("MockDepartmentRepository does not support sendCreate. Mock data is read-only."); + } + async sendUpdate(_id, _updates) { + throw new Error("MockDepartmentRepository does not support sendUpdate. Mock data is read-only."); + } + async sendDelete(_id) { + throw new Error("MockDepartmentRepository does not support sendDelete. Mock data is read-only."); + } + processDepartmentData(data) { + return data.map((dept) => ({ + id: dept.id, + name: dept.name, + resourceIds: dept.resourceIds, + syncStatus: "synced" + })); + } +}; +__name(_MockDepartmentRepository, "MockDepartmentRepository"); +var MockDepartmentRepository = _MockDepartmentRepository; + +// src/v2/repositories/MockSettingsRepository.ts +var _MockSettingsRepository = class _MockSettingsRepository { + constructor() { + this.entityType = "Settings"; + this.dataUrl = "data/tenant-settings.json"; + } + async fetchAll() { + try { + const response = await fetch(this.dataUrl); + if (!response.ok) { + throw new Error(`Failed to load tenant settings: ${response.status} ${response.statusText}`); + } + const settings = await response.json(); + return settings.map((s) => ({ + ...s, + syncStatus: s.syncStatus || "synced" + })); + } catch (error) { + console.error("Failed to load tenant settings:", error); + throw error; + } + } + async sendCreate(_settings) { + throw new Error("MockSettingsRepository does not support sendCreate. Mock data is read-only."); + } + async sendUpdate(_id, _updates) { + throw new Error("MockSettingsRepository does not support sendUpdate. Mock data is read-only."); + } + async sendDelete(_id) { + throw new Error("MockSettingsRepository does not support sendDelete. Mock data is read-only."); + } +}; +__name(_MockSettingsRepository, "MockSettingsRepository"); +var MockSettingsRepository = _MockSettingsRepository; + +// src/v2/repositories/MockViewConfigRepository.ts +var _MockViewConfigRepository = class _MockViewConfigRepository { + constructor() { + this.entityType = "ViewConfig"; + this.dataUrl = "data/viewconfigs.json"; + } + async fetchAll() { + try { + const response = await fetch(this.dataUrl); + if (!response.ok) { + throw new Error(`Failed to load viewconfigs: ${response.status} ${response.statusText}`); + } + const rawData = await response.json(); + const configs = rawData.map((config) => ({ + ...config, + syncStatus: config.syncStatus || "synced" + })); + return configs; + } catch (error) { + console.error("Failed to load viewconfigs:", error); + throw error; + } + } + async sendCreate(_config) { + throw new Error("MockViewConfigRepository does not support sendCreate. Mock data is read-only."); + } + async sendUpdate(_id, _updates) { + throw new Error("MockViewConfigRepository does not support sendUpdate. Mock data is read-only."); + } + async sendDelete(_id) { + throw new Error("MockViewConfigRepository does not support sendDelete. Mock data is read-only."); + } +}; +__name(_MockViewConfigRepository, "MockViewConfigRepository"); +var MockViewConfigRepository = _MockViewConfigRepository; + +// src/v2/workers/DataSeeder.ts +var _DataSeeder = class _DataSeeder { + constructor(services, repositories) { + this.services = services; + this.repositories = repositories; + } + /** + * Seed all entity stores if they are empty + */ + async seedIfEmpty() { + console.log("[DataSeeder] Checking if database needs seeding..."); + try { + for (const service of this.services) { + const repository = this.repositories.find((repo) => repo.entityType === service.entityType); + if (!repository) { + console.warn(`[DataSeeder] No repository found for entity type: ${service.entityType}, skipping`); + continue; + } + await this.seedEntity(service.entityType, service, repository); + } + console.log("[DataSeeder] Seeding complete"); + } catch (error) { + console.error("[DataSeeder] Seeding failed:", error); + throw error; + } + } + async seedEntity(entityType, service, repository) { + const existing = await service.getAll(); + if (existing.length > 0) { + console.log(`[DataSeeder] ${entityType} store already has ${existing.length} items, skipping seed`); + return; + } + console.log(`[DataSeeder] ${entityType} store is empty, fetching from repository...`); + const data = await repository.fetchAll(); + console.log(`[DataSeeder] Fetched ${data.length} ${entityType} items, saving to IndexedDB...`); + for (const entity of data) { + await service.save(entity, true); + } + console.log(`[DataSeeder] ${entityType} seeding complete (${data.length} items saved)`); + } +}; +__name(_DataSeeder, "DataSeeder"); +var DataSeeder = _DataSeeder; + +// src/v2/utils/PositionUtils.ts +function calculateEventPosition(start, end, config) { + const startMinutes = start.getHours() * 60 + start.getMinutes(); + const endMinutes = end.getHours() * 60 + end.getMinutes(); + const dayStartMinutes = config.dayStartHour * 60; + const minuteHeight = config.hourHeight / 60; + const top = (startMinutes - dayStartMinutes) * minuteHeight; + const height = (endMinutes - startMinutes) * minuteHeight; + return { top, height }; +} +__name(calculateEventPosition, "calculateEventPosition"); +function minutesToPixels(minutes, config) { + return minutes / 60 * config.hourHeight; +} +__name(minutesToPixels, "minutesToPixels"); +function pixelsToMinutes(pixels, config) { + return pixels / config.hourHeight * 60; +} +__name(pixelsToMinutes, "pixelsToMinutes"); +function snapToGrid(pixels, config) { + const snapPixels = minutesToPixels(config.snapInterval, config); + return Math.round(pixels / snapPixels) * snapPixels; +} +__name(snapToGrid, "snapToGrid"); + +// src/v2/features/event/EventLayoutEngine.ts +function eventsOverlap(a, b) { + return a.start < b.end && a.end > b.start; +} +__name(eventsOverlap, "eventsOverlap"); +function eventsWithinThreshold(a, b, thresholdMinutes) { + const thresholdMs = thresholdMinutes * 60 * 1e3; + const startToStartDiff = Math.abs(a.start.getTime() - b.start.getTime()); + if (startToStartDiff <= thresholdMs) + return true; + const bStartsBeforeAEnds = a.end.getTime() - b.start.getTime(); + if (bStartsBeforeAEnds > 0 && bStartsBeforeAEnds <= thresholdMs) + return true; + const aStartsBeforeBEnds = b.end.getTime() - a.start.getTime(); + if (aStartsBeforeBEnds > 0 && aStartsBeforeBEnds <= thresholdMs) + return true; + return false; +} +__name(eventsWithinThreshold, "eventsWithinThreshold"); +function findOverlapGroups(events) { + if (events.length === 0) + return []; + const sorted = [...events].sort((a, b) => a.start.getTime() - b.start.getTime()); + const used = /* @__PURE__ */ new Set(); + const groups = []; + for (const event of sorted) { + if (used.has(event.id)) + continue; + const group = [event]; + used.add(event.id); + let expanded = true; + while (expanded) { + expanded = false; + for (const candidate of sorted) { + if (used.has(candidate.id)) + continue; + const connects = group.some((member) => eventsOverlap(member, candidate)); + if (connects) { + group.push(candidate); + used.add(candidate.id); + expanded = true; + } + } + } + groups.push(group); + } + return groups; +} +__name(findOverlapGroups, "findOverlapGroups"); +function findGridCandidates(events, thresholdMinutes) { + if (events.length === 0) + return []; + const sorted = [...events].sort((a, b) => a.start.getTime() - b.start.getTime()); + const used = /* @__PURE__ */ new Set(); + const groups = []; + for (const event of sorted) { + if (used.has(event.id)) + continue; + const group = [event]; + used.add(event.id); + let expanded = true; + while (expanded) { + expanded = false; + for (const candidate of sorted) { + if (used.has(candidate.id)) + continue; + const connects = group.some((member) => eventsWithinThreshold(member, candidate, thresholdMinutes)); + if (connects) { + group.push(candidate); + used.add(candidate.id); + expanded = true; + } + } + } + groups.push(group); + } + return groups; +} +__name(findGridCandidates, "findGridCandidates"); +function calculateStackLevels(events) { + const levels = /* @__PURE__ */ new Map(); + const sorted = [...events].sort((a, b) => a.start.getTime() - b.start.getTime()); + for (const event of sorted) { + let maxOverlappingLevel = -1; + for (const [id, level] of levels) { + const other = events.find((e) => e.id === id); + if (other && eventsOverlap(event, other)) { + maxOverlappingLevel = Math.max(maxOverlappingLevel, level); + } + } + levels.set(event.id, maxOverlappingLevel + 1); + } + return levels; +} +__name(calculateStackLevels, "calculateStackLevels"); +function allocateColumns(events) { + const sorted = [...events].sort((a, b) => a.start.getTime() - b.start.getTime()); + const columns = []; + for (const event of sorted) { + let placed = false; + for (const column of columns) { + const canFit = !column.some((e) => eventsOverlap(event, e)); + if (canFit) { + column.push(event); + placed = true; + break; + } + } + if (!placed) { + columns.push([event]); + } + } + return columns; +} +__name(allocateColumns, "allocateColumns"); +function calculateColumnLayout(events, config) { + const thresholdMinutes = config.gridStartThresholdMinutes ?? 10; + const result = { + grids: [], + stacked: [] + }; + if (events.length === 0) + return result; + const overlapGroups = findOverlapGroups(events); + for (const overlapGroup of overlapGroups) { + if (overlapGroup.length === 1) { + result.stacked.push({ + event: overlapGroup[0], + stackLevel: 0 + }); + continue; + } + const gridSubgroups = findGridCandidates(overlapGroup, thresholdMinutes); + const largestGridCandidate = gridSubgroups.reduce((max, g) => g.length > max.length ? g : max, gridSubgroups[0]); + if (largestGridCandidate.length === overlapGroup.length) { + const columns = allocateColumns(overlapGroup); + const earliest = overlapGroup.reduce((min, e) => e.start < min.start ? e : min, overlapGroup[0]); + const position = calculateEventPosition(earliest.start, earliest.end, config); + result.grids.push({ + events: overlapGroup, + columns, + stackLevel: 0, + position: { top: position.top } + }); + } else { + const levels = calculateStackLevels(overlapGroup); + for (const event of overlapGroup) { + result.stacked.push({ + event, + stackLevel: levels.get(event.id) ?? 0 + }); + } + } + } + return result; +} +__name(calculateColumnLayout, "calculateColumnLayout"); + +// src/v2/features/event/EventRenderer.ts +var _EventRenderer = class _EventRenderer { + constructor(eventService, dateService, gridConfig, eventBus) { + this.eventService = eventService; + this.dateService = dateService; + this.gridConfig = gridConfig; + this.eventBus = eventBus; + this.container = null; + this.setupListeners(); + } + /** + * Setup listeners for drag-drop and update events + */ + setupListeners() { + this.eventBus.on(CoreEvents.EVENT_DRAG_COLUMN_CHANGE, (e) => { + const payload = e.detail; + this.handleColumnChange(payload); + }); + this.eventBus.on(CoreEvents.EVENT_DRAG_MOVE, (e) => { + const payload = e.detail; + this.updateDragTimestamp(payload); + }); + this.eventBus.on(CoreEvents.EVENT_UPDATED, (e) => { + const payload = e.detail; + this.handleEventUpdated(payload); + }); + this.eventBus.on(CoreEvents.EVENT_DRAG_END, (e) => { + const payload = e.detail; + this.handleDragEnd(payload); + }); + this.eventBus.on(CoreEvents.EVENT_DRAG_LEAVE_HEADER, (e) => { + const payload = e.detail; + this.handleDragLeaveHeader(payload); + }); + } + /** + * Handle EVENT_DRAG_END - remove element if dropped in header + */ + handleDragEnd(payload) { + if (payload.target === "header") { + const element = this.container?.querySelector(`swp-content-viewport swp-event[data-event-id="${payload.swpEvent.eventId}"]`); + element?.remove(); + } + } + /** + * Handle header item leaving header - create swp-event in grid + */ + handleDragLeaveHeader(payload) { + if (payload.source !== "header") + return; + if (!payload.targetColumn || !payload.start || !payload.end) + return; + if (payload.element) { + payload.element.classList.add("drag-ghost"); + payload.element.style.opacity = "0.3"; + payload.element.style.pointerEvents = "none"; + } + const event = { + id: payload.eventId, + title: payload.title || "", + description: "", + start: payload.start, + end: payload.end, + type: "customer", + allDay: false, + syncStatus: "pending" + }; + const element = this.createEventElement(event); + let eventsLayer = payload.targetColumn.querySelector("swp-events-layer"); + if (!eventsLayer) { + eventsLayer = document.createElement("swp-events-layer"); + payload.targetColumn.appendChild(eventsLayer); + } + eventsLayer.appendChild(element); + element.classList.add("dragging"); + } + /** + * Handle EVENT_UPDATED - re-render affected columns + */ + async handleEventUpdated(payload) { + if (payload.sourceColumnKey !== payload.targetColumnKey) { + await this.rerenderColumn(payload.sourceColumnKey); + } + await this.rerenderColumn(payload.targetColumnKey); + } + /** + * Re-render a single column with fresh data from IndexedDB + */ + async rerenderColumn(columnKey) { + const column = this.findColumn(columnKey); + if (!column) + return; + const date = column.dataset.date; + const resourceId = column.dataset.resourceId; + if (!date) + return; + const startDate = new Date(date); + const endDate = new Date(date); + endDate.setHours(23, 59, 59, 999); + const events = resourceId ? await this.eventService.getByResourceAndDateRange(resourceId, startDate, endDate) : await this.eventService.getByDateRange(startDate, endDate); + const timedEvents = events.filter((event) => !event.allDay && this.dateService.getDateKey(event.start) === date); + let eventsLayer = column.querySelector("swp-events-layer"); + if (!eventsLayer) { + eventsLayer = document.createElement("swp-events-layer"); + column.appendChild(eventsLayer); + } + eventsLayer.innerHTML = ""; + const layout = calculateColumnLayout(timedEvents, this.gridConfig); + layout.grids.forEach((grid) => { + const groupEl = this.renderGridGroup(grid); + eventsLayer.appendChild(groupEl); + }); + layout.stacked.forEach((item) => { + const eventEl = this.renderStackedEvent(item.event, item.stackLevel); + eventsLayer.appendChild(eventEl); + }); + } + /** + * Find a column element by columnKey + */ + findColumn(columnKey) { + if (!this.container) + return null; + return this.container.querySelector(`swp-day-column[data-column-key="${columnKey}"]`); + } + /** + * Handle event moving to a new column during drag + */ + handleColumnChange(payload) { + const eventsLayer = payload.newColumn.querySelector("swp-events-layer"); + if (!eventsLayer) + return; + eventsLayer.appendChild(payload.element); + payload.element.style.top = `${payload.currentY}px`; + } + /** + * Update timestamp display during drag (snapped to grid) + */ + updateDragTimestamp(payload) { + const timeEl = payload.element.querySelector("swp-event-time"); + if (!timeEl) + return; + const snappedY = snapToGrid(payload.currentY, this.gridConfig); + const minutesFromGridStart = pixelsToMinutes(snappedY, this.gridConfig); + const startMinutes = this.gridConfig.dayStartHour * 60 + minutesFromGridStart; + const height = parseFloat(payload.element.style.height) || this.gridConfig.hourHeight; + const durationMinutes = pixelsToMinutes(height, this.gridConfig); + const start = this.minutesToDate(startMinutes); + const end = this.minutesToDate(startMinutes + durationMinutes); + timeEl.textContent = this.dateService.formatTimeRange(start, end); + } + /** + * Convert minutes since midnight to a Date object (today) + */ + minutesToDate(minutes) { + const date = /* @__PURE__ */ new Date(); + date.setHours(Math.floor(minutes / 60) % 24, minutes % 60, 0, 0); + return date; + } + /** + * Render events for visible dates into day columns + * @param container - Calendar container element + * @param filter - Filter with 'date' and optionally 'resource' arrays + * @param filterTemplate - Template for matching events to columns + */ + async render(container2, filter, filterTemplate) { + this.container = container2; + const visibleDates = filter["date"] || []; + if (visibleDates.length === 0) + return; + const startDate = new Date(visibleDates[0]); + const endDate = new Date(visibleDates[visibleDates.length - 1]); + endDate.setHours(23, 59, 59, 999); + const events = await this.eventService.getByDateRange(startDate, endDate); + const dayColumns = container2.querySelector("swp-day-columns"); + if (!dayColumns) + return; + const columns = dayColumns.querySelectorAll("swp-day-column"); + columns.forEach((column) => { + const columnEl = column; + const columnEvents = events.filter((event) => filterTemplate.matches(event, columnEl)); + let eventsLayer = column.querySelector("swp-events-layer"); + if (!eventsLayer) { + eventsLayer = document.createElement("swp-events-layer"); + column.appendChild(eventsLayer); + } + eventsLayer.innerHTML = ""; + const timedEvents = columnEvents.filter((event) => !event.allDay); + const layout = calculateColumnLayout(timedEvents, this.gridConfig); + layout.grids.forEach((grid) => { + const groupEl = this.renderGridGroup(grid); + eventsLayer.appendChild(groupEl); + }); + layout.stacked.forEach((item) => { + const eventEl = this.renderStackedEvent(item.event, item.stackLevel); + eventsLayer.appendChild(eventEl); + }); + }); + } + /** + * Create a single event element + * + * CLEAN approach: + * - Only data-id for lookup + * - Visible content in innerHTML only + */ + createEventElement(event) { + const element = document.createElement("swp-event"); + element.dataset.eventId = event.id; + if (event.resourceId) { + element.dataset.resourceId = event.resourceId; + } + const position = calculateEventPosition(event.start, event.end, this.gridConfig); + element.style.top = `${position.top}px`; + element.style.height = `${position.height}px`; + const colorClass = this.getColorClass(event); + if (colorClass) { + element.classList.add(colorClass); + } + element.innerHTML = ` + ${this.dateService.formatTimeRange(event.start, event.end)} + ${this.escapeHtml(event.title)} + ${event.description ? `${this.escapeHtml(event.description)}` : ""} + `; + return element; + } + /** + * Get color class based on metadata.color or event type + */ + getColorClass(event) { + if (event.metadata?.color) { + return `is-${event.metadata.color}`; + } + const typeColors = { + "customer": "is-blue", + "vacation": "is-green", + "break": "is-amber", + "meeting": "is-purple", + "blocked": "is-red" + }; + return typeColors[event.type] || "is-blue"; + } + /** + * Escape HTML to prevent XSS + */ + escapeHtml(text) { + const div = document.createElement("div"); + div.textContent = text; + return div.innerHTML; + } + /** + * Render a GRID group with side-by-side columns + * Used when multiple events start at the same time + */ + renderGridGroup(layout) { + const group = document.createElement("swp-event-group"); + group.classList.add(`cols-${layout.columns.length}`); + group.style.top = `${layout.position.top}px`; + if (layout.stackLevel > 0) { + group.style.marginLeft = `${layout.stackLevel * 15}px`; + group.style.zIndex = `${100 + layout.stackLevel}`; + } + let maxBottom = 0; + for (const event of layout.events) { + const pos = calculateEventPosition(event.start, event.end, this.gridConfig); + const eventBottom = pos.top + pos.height; + if (eventBottom > maxBottom) + maxBottom = eventBottom; + } + const groupHeight = maxBottom - layout.position.top; + group.style.height = `${groupHeight}px`; + layout.columns.forEach((columnEvents) => { + const wrapper = document.createElement("div"); + wrapper.style.position = "relative"; + columnEvents.forEach((event) => { + const eventEl = this.createEventElement(event); + const pos = calculateEventPosition(event.start, event.end, this.gridConfig); + eventEl.style.top = `${pos.top - layout.position.top}px`; + eventEl.style.position = "absolute"; + eventEl.style.left = "0"; + eventEl.style.right = "0"; + wrapper.appendChild(eventEl); + }); + group.appendChild(wrapper); + }); + return group; + } + /** + * Render a STACKED event with margin-left offset + * Used for overlapping events that don't start at the same time + */ + renderStackedEvent(event, stackLevel) { + const element = this.createEventElement(event); + element.dataset.stackLink = JSON.stringify({ stackLevel }); + if (stackLevel > 0) { + element.style.marginLeft = `${stackLevel * 15}px`; + element.style.zIndex = `${100 + stackLevel}`; + } + return element; + } +}; +__name(_EventRenderer, "EventRenderer"); +var EventRenderer = _EventRenderer; + +// src/v2/features/schedule/ScheduleRenderer.ts +var _ScheduleRenderer = class _ScheduleRenderer { + constructor(scheduleService, dateService, gridConfig) { + this.scheduleService = scheduleService; + this.dateService = dateService; + this.gridConfig = gridConfig; + } + /** + * Render unavailable zones for visible columns + * @param container - Calendar container element + * @param filter - Filter with 'date' and 'resource' arrays + */ + async render(container2, filter) { + const dates = filter["date"] || []; + const resourceIds = filter["resource"] || []; + if (dates.length === 0) + return; + const dayColumns = container2.querySelector("swp-day-columns"); + if (!dayColumns) + return; + const columns = dayColumns.querySelectorAll("swp-day-column"); + for (const column of columns) { + const date = column.dataset.date; + const resourceId = column.dataset.resourceId; + if (!date || !resourceId) + continue; + let unavailableLayer = column.querySelector("swp-unavailable-layer"); + if (!unavailableLayer) { + unavailableLayer = document.createElement("swp-unavailable-layer"); + column.insertBefore(unavailableLayer, column.firstChild); + } + unavailableLayer.innerHTML = ""; + const schedule = await this.scheduleService.getScheduleForDate(resourceId, date); + this.renderUnavailableZones(unavailableLayer, schedule); + } + } + /** + * Render unavailable time zones based on schedule + */ + renderUnavailableZones(layer, schedule) { + const dayStartMinutes = this.gridConfig.dayStartHour * 60; + const dayEndMinutes = this.gridConfig.dayEndHour * 60; + const minuteHeight = this.gridConfig.hourHeight / 60; + if (schedule === null) { + const zone = this.createUnavailableZone(0, (dayEndMinutes - dayStartMinutes) * minuteHeight); + layer.appendChild(zone); + return; + } + const workStartMinutes = this.dateService.timeToMinutes(schedule.start); + const workEndMinutes = this.dateService.timeToMinutes(schedule.end); + if (workStartMinutes > dayStartMinutes) { + const top = 0; + const height = (workStartMinutes - dayStartMinutes) * minuteHeight; + const zone = this.createUnavailableZone(top, height); + layer.appendChild(zone); + } + if (workEndMinutes < dayEndMinutes) { + const top = (workEndMinutes - dayStartMinutes) * minuteHeight; + const height = (dayEndMinutes - workEndMinutes) * minuteHeight; + const zone = this.createUnavailableZone(top, height); + layer.appendChild(zone); + } + } + /** + * Create an unavailable zone element + */ + createUnavailableZone(top, height) { + const zone = document.createElement("swp-unavailable-zone"); + zone.style.top = `${top}px`; + zone.style.height = `${height}px`; + return zone; + } +}; +__name(_ScheduleRenderer, "ScheduleRenderer"); +var ScheduleRenderer = _ScheduleRenderer; + +// src/v2/features/headerdrawer/HeaderDrawerRenderer.ts +var _HeaderDrawerRenderer = class _HeaderDrawerRenderer { + constructor(eventBus, gridConfig, headerDrawerManager, eventService, dateService) { + this.eventBus = eventBus; + this.gridConfig = gridConfig; + this.headerDrawerManager = headerDrawerManager; + this.eventService = eventService; + this.dateService = dateService; + this.currentItem = null; + this.container = null; + this.sourceElement = null; + this.wasExpandedBeforeDrag = false; + this.filterTemplate = null; + this.setupListeners(); + } + /** + * Render allDay events into the header drawer with row stacking + * @param filterTemplate - Template for matching events to columns + */ + async render(container2, filter, filterTemplate) { + this.filterTemplate = filterTemplate; + const drawer = container2.querySelector("swp-header-drawer"); + if (!drawer) + return; + const visibleDates = filter["date"] || []; + if (visibleDates.length === 0) + return; + const visibleColumnKeys = this.getVisibleColumnKeysFromDOM(); + if (visibleColumnKeys.length === 0) + return; + const startDate = new Date(visibleDates[0]); + const endDate = new Date(visibleDates[visibleDates.length - 1]); + endDate.setHours(23, 59, 59, 999); + const events = await this.eventService.getByDateRange(startDate, endDate); + const allDayEvents = events.filter((event) => event.allDay !== false); + drawer.innerHTML = ""; + if (allDayEvents.length === 0) + return; + const layouts = this.calculateLayout(allDayEvents, visibleColumnKeys); + const rowCount = Math.max(1, ...layouts.map((l) => l.row)); + layouts.forEach((layout) => { + const item = this.createHeaderItem(layout); + drawer.appendChild(item); + }); + this.headerDrawerManager.expandToRows(rowCount); + } + /** + * Create a header item element from layout + */ + createHeaderItem(layout) { + const { event, columnKey, row, colStart, colEnd } = layout; + const item = document.createElement("swp-header-item"); + item.dataset.eventId = event.id; + item.dataset.itemType = "event"; + item.dataset.start = event.start.toISOString(); + item.dataset.end = event.end.toISOString(); + item.dataset.columnKey = columnKey; + item.textContent = event.title; + const colorClass = this.getColorClass(event); + if (colorClass) + item.classList.add(colorClass); + item.style.gridArea = `${row} / ${colStart} / ${row + 1} / ${colEnd}`; + return item; + } + /** + * Calculate layout for all events with row stacking + * Uses track-based algorithm to find available rows for overlapping events + */ + calculateLayout(events, visibleColumnKeys) { + const tracks = [new Array(visibleColumnKeys.length).fill(false)]; + const layouts = []; + for (const event of events) { + const columnKey = this.buildColumnKeyFromEvent(event); + const startCol = visibleColumnKeys.indexOf(columnKey); + const endColumnKey = this.buildColumnKeyFromEvent(event, event.end); + const endCol = visibleColumnKeys.indexOf(endColumnKey); + if (startCol === -1 && endCol === -1) + continue; + const colStart = Math.max(0, startCol); + const colEnd = (endCol !== -1 ? endCol : visibleColumnKeys.length - 1) + 1; + const row = this.findAvailableRow(tracks, colStart, colEnd); + for (let c = colStart; c < colEnd; c++) { + tracks[row][c] = true; + } + layouts.push({ event, columnKey, row: row + 1, colStart: colStart + 1, colEnd: colEnd + 1 }); + } + return layouts; + } + /** + * Build columnKey from event using FilterTemplate + * Uses the same template that columns use for matching + */ + buildColumnKeyFromEvent(event, date) { + if (!this.filterTemplate) { + const dateStr = this.dateService.getDateKey(date || event.start); + return dateStr; + } + if (date && date.getTime() !== event.start.getTime()) { + const tempEvent = { ...event, start: date }; + return this.filterTemplate.buildKeyFromEvent(tempEvent); + } + return this.filterTemplate.buildKeyFromEvent(event); + } + /** + * Find available row for event spanning columns [colStart, colEnd) + */ + findAvailableRow(tracks, colStart, colEnd) { + for (let row = 0; row < tracks.length; row++) { + let available = true; + for (let c = colStart; c < colEnd; c++) { + if (tracks[row][c]) { + available = false; + break; + } + } + if (available) + return row; + } + tracks.push(new Array(tracks[0].length).fill(false)); + return tracks.length - 1; + } + /** + * Get color class based on event metadata or type + */ + getColorClass(event) { + if (event.metadata?.color) { + return `is-${event.metadata.color}`; + } + const typeColors = { + "customer": "is-blue", + "vacation": "is-green", + "break": "is-amber", + "meeting": "is-purple", + "blocked": "is-red" + }; + return typeColors[event.type] || "is-blue"; + } + /** + * Setup event listeners for drag events + */ + setupListeners() { + this.eventBus.on(CoreEvents.EVENT_DRAG_ENTER_HEADER, (e) => { + const payload = e.detail; + this.handleDragEnter(payload); + }); + this.eventBus.on(CoreEvents.EVENT_DRAG_MOVE_HEADER, (e) => { + const payload = e.detail; + this.handleDragMove(payload); + }); + this.eventBus.on(CoreEvents.EVENT_DRAG_LEAVE_HEADER, (e) => { + const payload = e.detail; + this.handleDragLeave(payload); + }); + this.eventBus.on(CoreEvents.EVENT_DRAG_END, (e) => { + const payload = e.detail; + this.handleDragEnd(payload); + }); + this.eventBus.on(CoreEvents.EVENT_DRAG_CANCEL, () => { + this.cleanup(); + }); + } + /** + * Handle drag entering header zone - create preview item + */ + handleDragEnter(payload) { + this.container = document.querySelector("swp-header-drawer"); + if (!this.container) + return; + this.wasExpandedBeforeDrag = this.headerDrawerManager.isExpanded(); + if (!this.wasExpandedBeforeDrag) { + this.headerDrawerManager.expandToRows(1); + } + this.sourceElement = payload.element; + const item = document.createElement("swp-header-item"); + item.dataset.eventId = payload.eventId; + item.dataset.itemType = payload.itemType; + item.dataset.duration = String(payload.duration); + item.dataset.columnKey = payload.sourceColumnKey; + item.textContent = payload.title; + if (payload.colorClass) { + item.classList.add(payload.colorClass); + } + item.classList.add("dragging"); + const col = payload.sourceColumnIndex + 1; + const endCol = col + payload.duration; + item.style.gridArea = `1 / ${col} / 2 / ${endCol}`; + this.container.appendChild(item); + this.currentItem = item; + payload.element.style.visibility = "hidden"; + } + /** + * Handle drag moving within header - update column position + */ + handleDragMove(payload) { + if (!this.currentItem) + return; + const col = payload.columnIndex + 1; + const duration = parseInt(this.currentItem.dataset.duration || "1", 10); + const endCol = col + duration; + this.currentItem.style.gridArea = `1 / ${col} / 2 / ${endCol}`; + this.currentItem.dataset.columnKey = payload.columnKey; + } + /** + * Handle drag leaving header - cleanup for grid→header drag only + */ + handleDragLeave(payload) { + if (payload.source === "grid") { + this.cleanup(); + } + } + /** + * Handle drag end - finalize based on drop target + */ + handleDragEnd(payload) { + if (payload.target === "header") { + if (this.currentItem) { + this.currentItem.classList.remove("dragging"); + this.recalculateDrawerLayout(); + this.currentItem = null; + this.sourceElement = null; + } + } else { + const ghost = document.querySelector(`swp-header-item.drag-ghost[data-event-id="${payload.swpEvent.eventId}"]`); + ghost?.remove(); + this.recalculateDrawerLayout(); + } + } + /** + * Recalculate layout for all items currently in the drawer + * Called after drop to reposition items and adjust height + */ + recalculateDrawerLayout() { + const drawer = document.querySelector("swp-header-drawer"); + if (!drawer) + return; + const items = Array.from(drawer.querySelectorAll("swp-header-item")); + if (items.length === 0) + return; + const visibleColumnKeys = this.getVisibleColumnKeysFromDOM(); + if (visibleColumnKeys.length === 0) + return; + const itemData = items.map((item) => ({ + element: item, + columnKey: item.dataset.columnKey || "", + duration: parseInt(item.dataset.duration || "1", 10) + })); + const tracks = [new Array(visibleColumnKeys.length).fill(false)]; + for (const item of itemData) { + const startCol = visibleColumnKeys.indexOf(item.columnKey); + if (startCol === -1) + continue; + const colStart = startCol; + const colEnd = Math.min(startCol + item.duration, visibleColumnKeys.length); + const row = this.findAvailableRow(tracks, colStart, colEnd); + for (let c = colStart; c < colEnd; c++) { + tracks[row][c] = true; + } + item.element.style.gridArea = `${row + 1} / ${colStart + 1} / ${row + 2} / ${colEnd + 1}`; + } + const rowCount = tracks.length; + this.headerDrawerManager.expandToRows(rowCount); + } + /** + * Get visible column keys from DOM (preserves order for multi-resource views) + * Uses filterTemplate.buildKeyFromColumn() for consistent key format with events + */ + getVisibleColumnKeysFromDOM() { + if (!this.filterTemplate) + return []; + const columns = document.querySelectorAll("swp-day-column"); + const columnKeys = []; + columns.forEach((col) => { + const columnKey = this.filterTemplate.buildKeyFromColumn(col); + if (columnKey) + columnKeys.push(columnKey); + }); + return columnKeys; + } + /** + * Cleanup preview item and restore source visibility + */ + cleanup() { + this.currentItem?.remove(); + this.currentItem = null; + if (this.sourceElement) { + this.sourceElement.style.visibility = ""; + this.sourceElement = null; + } + if (!this.wasExpandedBeforeDrag) { + this.headerDrawerManager.collapse(); + } + } +}; +__name(_HeaderDrawerRenderer, "HeaderDrawerRenderer"); +var HeaderDrawerRenderer = _HeaderDrawerRenderer; + +// src/v2/storage/schedules/ScheduleOverrideStore.ts +var _ScheduleOverrideStore = class _ScheduleOverrideStore { + constructor() { + this.storeName = _ScheduleOverrideStore.STORE_NAME; + } + create(db) { + const store = db.createObjectStore(_ScheduleOverrideStore.STORE_NAME, { keyPath: "id" }); + store.createIndex("resourceId", "resourceId", { unique: false }); + store.createIndex("date", "date", { unique: false }); + store.createIndex("resourceId_date", ["resourceId", "date"], { unique: true }); + store.createIndex("syncStatus", "syncStatus", { unique: false }); + } +}; +__name(_ScheduleOverrideStore, "ScheduleOverrideStore"); +var ScheduleOverrideStore = _ScheduleOverrideStore; +ScheduleOverrideStore.STORE_NAME = "scheduleOverrides"; + +// src/v2/storage/schedules/ScheduleOverrideService.ts +var _ScheduleOverrideService = class _ScheduleOverrideService { + constructor(context) { + this.context = context; + } + get db() { + return this.context.getDatabase(); + } + /** + * Get override for a specific resource and date + */ + async getOverride(resourceId, date) { + return new Promise((resolve, reject) => { + const transaction = this.db.transaction([ScheduleOverrideStore.STORE_NAME], "readonly"); + const store = transaction.objectStore(ScheduleOverrideStore.STORE_NAME); + const index = store.index("resourceId_date"); + const request = index.get([resourceId, date]); + request.onsuccess = () => { + resolve(request.result || null); + }; + request.onerror = () => { + reject(new Error(`Failed to get override for ${resourceId} on ${date}: ${request.error}`)); + }; + }); + } + /** + * Get all overrides for a resource + */ + async getByResource(resourceId) { + return new Promise((resolve, reject) => { + const transaction = this.db.transaction([ScheduleOverrideStore.STORE_NAME], "readonly"); + const store = transaction.objectStore(ScheduleOverrideStore.STORE_NAME); + const index = store.index("resourceId"); + const request = index.getAll(resourceId); + request.onsuccess = () => { + resolve(request.result || []); + }; + request.onerror = () => { + reject(new Error(`Failed to get overrides for ${resourceId}: ${request.error}`)); + }; + }); + } + /** + * Get overrides for a date range + */ + async getByDateRange(resourceId, startDate, endDate) { + const all = await this.getByResource(resourceId); + return all.filter((o) => o.date >= startDate && o.date <= endDate); + } + /** + * Save an override + */ + async save(override) { + return new Promise((resolve, reject) => { + const transaction = this.db.transaction([ScheduleOverrideStore.STORE_NAME], "readwrite"); + const store = transaction.objectStore(ScheduleOverrideStore.STORE_NAME); + const request = store.put(override); + request.onsuccess = () => resolve(); + request.onerror = () => { + reject(new Error(`Failed to save override ${override.id}: ${request.error}`)); + }; + }); + } + /** + * Delete an override + */ + async delete(id) { + return new Promise((resolve, reject) => { + const transaction = this.db.transaction([ScheduleOverrideStore.STORE_NAME], "readwrite"); + const store = transaction.objectStore(ScheduleOverrideStore.STORE_NAME); + const request = store.delete(id); + request.onsuccess = () => resolve(); + request.onerror = () => { + reject(new Error(`Failed to delete override ${id}: ${request.error}`)); + }; + }); + } +}; +__name(_ScheduleOverrideService, "ScheduleOverrideService"); +var ScheduleOverrideService = _ScheduleOverrideService; + +// src/v2/storage/schedules/ResourceScheduleService.ts +var _ResourceScheduleService = class _ResourceScheduleService { + constructor(resourceService, overrideService, dateService) { + this.resourceService = resourceService; + this.overrideService = overrideService; + this.dateService = dateService; + } + /** + * Get effective schedule for a resource on a specific date + * + * @param resourceId - Resource ID + * @param date - Date string "YYYY-MM-DD" + * @returns ITimeSlot or null (fri/closed) + */ + async getScheduleForDate(resourceId, date) { + const override = await this.overrideService.getOverride(resourceId, date); + if (override) { + return override.schedule; + } + const resource = await this.resourceService.get(resourceId); + if (!resource || !resource.defaultSchedule) { + return null; + } + const weekDay = this.dateService.getISOWeekDay(date); + return resource.defaultSchedule[weekDay] || null; + } + /** + * Get schedules for multiple dates + * + * @param resourceId - Resource ID + * @param dates - Array of date strings "YYYY-MM-DD" + * @returns Map of date -> ITimeSlot | null + */ + async getSchedulesForDates(resourceId, dates) { + const result = /* @__PURE__ */ new Map(); + const resource = await this.resourceService.get(resourceId); + const overrides = dates.length > 0 ? await this.overrideService.getByDateRange(resourceId, dates[0], dates[dates.length - 1]) : []; + const overrideMap = new Map(overrides.map((o) => [o.date, o.schedule])); + for (const date of dates) { + if (overrideMap.has(date)) { + result.set(date, overrideMap.get(date)); + continue; + } + if (resource?.defaultSchedule) { + const weekDay = this.dateService.getISOWeekDay(date); + result.set(date, resource.defaultSchedule[weekDay] || null); + } else { + result.set(date, null); + } + } + return result; + } +}; +__name(_ResourceScheduleService, "ResourceScheduleService"); +var ResourceScheduleService = _ResourceScheduleService; + +// src/v2/types/SwpEvent.ts +var _SwpEvent = class _SwpEvent { + constructor(element, columnKey, start, end) { + this.element = element; + this.columnKey = columnKey; + this._start = start; + this._end = end; + } + /** Event ID from element.dataset.eventId */ + get eventId() { + return this.element.dataset.eventId || ""; + } + get start() { + return this._start; + } + get end() { + return this._end; + } + /** Duration in minutes */ + get durationMinutes() { + return (this._end.getTime() - this._start.getTime()) / (1e3 * 60); + } + /** Duration in milliseconds */ + get durationMs() { + return this._end.getTime() - this._start.getTime(); + } + /** + * Factory: Create SwpEvent from element + columnKey + * Reads top/height from element.style to calculate start/end + * @param columnKey - Opaque column identifier (do NOT parse - use only for matching) + * @param date - Date string (YYYY-MM-DD) for time calculations + */ + static fromElement(element, columnKey, date, gridConfig) { + const topPixels = parseFloat(element.style.top) || 0; + const heightPixels = parseFloat(element.style.height) || 0; + const startMinutesFromGrid = topPixels / gridConfig.hourHeight * 60; + const totalMinutes = gridConfig.dayStartHour * 60 + startMinutesFromGrid; + const start = new Date(date); + start.setHours(Math.floor(totalMinutes / 60), totalMinutes % 60, 0, 0); + const durationMinutes = heightPixels / gridConfig.hourHeight * 60; + const end = new Date(start.getTime() + durationMinutes * 60 * 1e3); + return new _SwpEvent(element, columnKey, start, end); + } +}; +__name(_SwpEvent, "SwpEvent"); +var SwpEvent = _SwpEvent; + +// src/v2/managers/DragDropManager.ts +var _DragDropManager = class _DragDropManager { + constructor(eventBus, gridConfig) { + this.eventBus = eventBus; + this.gridConfig = gridConfig; + this.dragState = null; + this.mouseDownPosition = null; + this.pendingElement = null; + this.pendingMouseOffset = null; + this.container = null; + this.inHeader = false; + this.DRAG_THRESHOLD = 5; + this.INTERPOLATION_FACTOR = 0.3; + this.handlePointerDown = (e) => { + const target = e.target; + if (target.closest("swp-resize-handle")) + return; + const eventElement = target.closest("swp-event"); + const headerItem = target.closest("swp-header-item"); + const draggable = eventElement || headerItem; + if (!draggable) + return; + this.mouseDownPosition = { x: e.clientX, y: e.clientY }; + this.pendingElement = draggable; + const rect = draggable.getBoundingClientRect(); + this.pendingMouseOffset = { + x: e.clientX - rect.left, + y: e.clientY - rect.top + }; + draggable.setPointerCapture(e.pointerId); + }; + this.handlePointerMove = (e) => { + if (!this.mouseDownPosition || !this.pendingElement) { + if (this.dragState) { + this.updateDragTarget(e); + } + return; + } + const deltaX = Math.abs(e.clientX - this.mouseDownPosition.x); + const deltaY = Math.abs(e.clientY - this.mouseDownPosition.y); + const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); + if (distance < this.DRAG_THRESHOLD) + return; + this.initializeDrag(this.pendingElement, this.pendingMouseOffset, e); + this.mouseDownPosition = null; + this.pendingElement = null; + this.pendingMouseOffset = null; + }; + this.handlePointerUp = (_e) => { + this.mouseDownPosition = null; + this.pendingElement = null; + this.pendingMouseOffset = null; + if (!this.dragState) + return; + cancelAnimationFrame(this.dragState.animationId); + if (this.dragState.dragSource === "header") { + this.handleHeaderItemDragEnd(); + } else { + this.handleGridEventDragEnd(); + } + this.dragState.element.classList.remove("dragging"); + this.dragState = null; + this.inHeader = false; + }; + this.animateDrag = () => { + if (!this.dragState) + return; + const diff2 = this.dragState.targetY - this.dragState.currentY; + if (Math.abs(diff2) <= 0.5) { + this.dragState.animationId = 0; + return; + } + this.dragState.currentY += diff2 * this.INTERPOLATION_FACTOR; + this.dragState.element.style.top = `${this.dragState.currentY}px`; + if (this.dragState.columnElement) { + const payload = { + eventId: this.dragState.eventId, + element: this.dragState.element, + currentY: this.dragState.currentY, + columnElement: this.dragState.columnElement + }; + this.eventBus.emit(CoreEvents.EVENT_DRAG_MOVE, payload); + } + this.dragState.animationId = requestAnimationFrame(this.animateDrag); + }; + this.setupScrollListener(); + } + setupScrollListener() { + this.eventBus.on(CoreEvents.EDGE_SCROLL_TICK, (e) => { + if (!this.dragState) + return; + const { scrollDelta } = e.detail; + this.dragState.targetY += scrollDelta; + this.dragState.currentY += scrollDelta; + this.dragState.element.style.top = `${this.dragState.currentY}px`; + }); + } + /** + * Initialize drag-drop on a container element + */ + init(container2) { + this.container = container2; + container2.addEventListener("pointerdown", this.handlePointerDown); + document.addEventListener("pointermove", this.handlePointerMove); + document.addEventListener("pointerup", this.handlePointerUp); + } + /** + * Handle drag end for header items + */ + handleHeaderItemDragEnd() { + if (!this.dragState) + return; + if (!this.inHeader && this.dragState.currentColumn) { + const gridEvent = this.dragState.currentColumn.querySelector(`swp-event[data-event-id="${this.dragState.eventId}"]`); + if (gridEvent) { + const columnKey = this.dragState.currentColumn.dataset.columnKey || ""; + const date = this.dragState.currentColumn.dataset.date || ""; + const swpEvent = SwpEvent.fromElement(gridEvent, columnKey, date, this.gridConfig); + const payload = { + swpEvent, + sourceColumnKey: this.dragState.sourceColumnKey, + target: "grid" + }; + this.eventBus.emit(CoreEvents.EVENT_DRAG_END, payload); + } + } + } + /** + * Handle drag end for grid events + */ + handleGridEventDragEnd() { + if (!this.dragState || !this.dragState.columnElement) + return; + const snappedY = snapToGrid(this.dragState.currentY, this.gridConfig); + this.dragState.element.style.top = `${snappedY}px`; + this.dragState.ghostElement?.remove(); + const columnKey = this.dragState.columnElement.dataset.columnKey || ""; + const date = this.dragState.columnElement.dataset.date || ""; + const swpEvent = SwpEvent.fromElement(this.dragState.element, columnKey, date, this.gridConfig); + const payload = { + swpEvent, + sourceColumnKey: this.dragState.sourceColumnKey, + target: this.inHeader ? "header" : "grid" + }; + this.eventBus.emit(CoreEvents.EVENT_DRAG_END, payload); + } + initializeDrag(element, mouseOffset, e) { + const eventId = element.dataset.eventId || ""; + const isHeaderItem = element.tagName.toLowerCase() === "swp-header-item"; + const columnElement = element.closest("swp-day-column"); + if (!isHeaderItem && !columnElement) + return; + if (isHeaderItem) { + this.initializeHeaderItemDrag(element, mouseOffset, eventId); + } else { + this.initializeGridEventDrag(element, mouseOffset, e, columnElement, eventId); + } + } + /** + * Initialize drag for a header item (allDay event) + */ + initializeHeaderItemDrag(element, mouseOffset, eventId) { + element.classList.add("dragging"); + this.dragState = { + eventId, + element, + ghostElement: null, + // No ghost for header items + startY: 0, + mouseOffset, + columnElement: null, + currentColumn: null, + targetY: 0, + currentY: 0, + animationId: 0, + sourceColumnKey: "", + // Will be set from header item data + dragSource: "header" + }; + this.inHeader = true; + } + /** + * Initialize drag for a grid event + */ + initializeGridEventDrag(element, mouseOffset, e, columnElement, eventId) { + const elementRect = element.getBoundingClientRect(); + const columnRect = columnElement.getBoundingClientRect(); + const startY = elementRect.top - columnRect.top; + const group = element.closest("swp-event-group"); + if (group) { + const eventsLayer = columnElement.querySelector("swp-events-layer"); + if (eventsLayer) { + eventsLayer.appendChild(element); + } + } + element.style.position = "absolute"; + element.style.top = `${startY}px`; + element.style.left = "2px"; + element.style.right = "2px"; + element.style.marginLeft = "0"; + const ghostElement = element.cloneNode(true); + ghostElement.classList.add("drag-ghost"); + ghostElement.style.opacity = "0.3"; + ghostElement.style.pointerEvents = "none"; + element.parentNode?.insertBefore(ghostElement, element); + element.classList.add("dragging"); + const targetY = e.clientY - columnRect.top - mouseOffset.y; + this.dragState = { + eventId, + element, + ghostElement, + startY, + mouseOffset, + columnElement, + currentColumn: columnElement, + targetY: Math.max(0, targetY), + currentY: startY, + animationId: 0, + sourceColumnKey: columnElement.dataset.columnKey || "", + dragSource: "grid" + }; + const payload = { + eventId, + element, + ghostElement, + startY, + mouseOffset, + columnElement + }; + this.eventBus.emit(CoreEvents.EVENT_DRAG_START, payload); + this.animateDrag(); + } + updateDragTarget(e) { + if (!this.dragState) + return; + this.checkHeaderZone(e); + if (this.inHeader) + return; + const columnAtPoint = this.getColumnAtPoint(e.clientX); + if (this.dragState.dragSource === "header" && columnAtPoint && !this.dragState.currentColumn) { + this.dragState.currentColumn = columnAtPoint; + this.dragState.columnElement = columnAtPoint; + } + if (columnAtPoint && columnAtPoint !== this.dragState.currentColumn && this.dragState.currentColumn) { + const payload = { + eventId: this.dragState.eventId, + element: this.dragState.element, + previousColumn: this.dragState.currentColumn, + newColumn: columnAtPoint, + currentY: this.dragState.currentY + }; + this.eventBus.emit(CoreEvents.EVENT_DRAG_COLUMN_CHANGE, payload); + this.dragState.currentColumn = columnAtPoint; + this.dragState.columnElement = columnAtPoint; + } + if (!this.dragState.columnElement) + return; + const columnRect = this.dragState.columnElement.getBoundingClientRect(); + const targetY = e.clientY - columnRect.top - this.dragState.mouseOffset.y; + this.dragState.targetY = Math.max(0, targetY); + if (!this.dragState.animationId) { + this.animateDrag(); + } + } + /** + * Check if pointer is in header zone and emit appropriate events + */ + checkHeaderZone(e) { + if (!this.dragState) + return; + const headerViewport = document.querySelector("swp-header-viewport"); + if (!headerViewport) + return; + const rect = headerViewport.getBoundingClientRect(); + const isInHeader = e.clientY < rect.bottom; + if (isInHeader && !this.inHeader) { + this.inHeader = true; + if (this.dragState.dragSource === "grid" && this.dragState.columnElement) { + const payload = { + eventId: this.dragState.eventId, + element: this.dragState.element, + sourceColumnIndex: this.getColumnIndex(this.dragState.columnElement), + sourceColumnKey: this.dragState.columnElement.dataset.columnKey || "", + title: this.dragState.element.querySelector("swp-event-title")?.textContent || "", + colorClass: [...this.dragState.element.classList].find((c) => c.startsWith("is-")), + itemType: "event", + duration: 1 + }; + this.eventBus.emit(CoreEvents.EVENT_DRAG_ENTER_HEADER, payload); + } + } else if (!isInHeader && this.inHeader) { + this.inHeader = false; + const targetColumn = this.getColumnAtPoint(e.clientX); + if (this.dragState.dragSource === "header") { + const payload = { + eventId: this.dragState.eventId, + source: "header", + element: this.dragState.element, + targetColumn: targetColumn || void 0, + start: this.dragState.element.dataset.start ? new Date(this.dragState.element.dataset.start) : void 0, + end: this.dragState.element.dataset.end ? new Date(this.dragState.element.dataset.end) : void 0, + title: this.dragState.element.textContent || "", + colorClass: [...this.dragState.element.classList].find((c) => c.startsWith("is-")) + }; + this.eventBus.emit(CoreEvents.EVENT_DRAG_LEAVE_HEADER, payload); + if (targetColumn) { + const newElement = targetColumn.querySelector(`swp-event[data-event-id="${this.dragState.eventId}"]`); + if (newElement) { + this.dragState.element = newElement; + this.dragState.columnElement = targetColumn; + this.dragState.currentColumn = targetColumn; + this.animateDrag(); + } + } + } else { + const payload = { + eventId: this.dragState.eventId, + source: "grid" + }; + this.eventBus.emit(CoreEvents.EVENT_DRAG_LEAVE_HEADER, payload); + } + } else if (isInHeader) { + const column = this.getColumnAtX(e.clientX); + if (column) { + const payload = { + eventId: this.dragState.eventId, + columnIndex: this.getColumnIndex(column), + columnKey: column.dataset.columnKey || "" + }; + this.eventBus.emit(CoreEvents.EVENT_DRAG_MOVE_HEADER, payload); + } + } + } + /** + * Get column index (0-based) for a column element + */ + getColumnIndex(column) { + if (!this.container || !column) + return 0; + const columns = Array.from(this.container.querySelectorAll("swp-day-column")); + return columns.indexOf(column); + } + /** + * Get column at X coordinate (alias for getColumnAtPoint) + */ + getColumnAtX(clientX) { + return this.getColumnAtPoint(clientX); + } + /** + * Find column element at given X coordinate + */ + getColumnAtPoint(clientX) { + if (!this.container) + return null; + const columns = this.container.querySelectorAll("swp-day-column"); + for (const col of columns) { + const rect = col.getBoundingClientRect(); + if (clientX >= rect.left && clientX <= rect.right) { + return col; + } + } + return null; + } + /** + * Cancel drag and animate back to start position + */ + cancelDrag() { + if (!this.dragState) + return; + cancelAnimationFrame(this.dragState.animationId); + const { element, ghostElement, startY, eventId } = this.dragState; + element.style.transition = "top 200ms ease-out"; + element.style.top = `${startY}px`; + setTimeout(() => { + ghostElement?.remove(); + element.style.transition = ""; + element.classList.remove("dragging"); + }, 200); + const payload = { + eventId, + element, + startY + }; + this.eventBus.emit(CoreEvents.EVENT_DRAG_CANCEL, payload); + this.dragState = null; + this.inHeader = false; + } +}; +__name(_DragDropManager, "DragDropManager"); +var DragDropManager = _DragDropManager; + +// src/v2/managers/EdgeScrollManager.ts +var _EdgeScrollManager = class _EdgeScrollManager { + constructor(eventBus) { + this.eventBus = eventBus; + this.scrollableContent = null; + this.timeGrid = null; + this.draggedElement = null; + this.scrollRAF = null; + this.mouseY = 0; + this.isDragging = false; + this.isScrolling = false; + this.lastTs = 0; + this.rect = null; + this.initialScrollTop = 0; + this.OUTER_ZONE = 100; + this.INNER_ZONE = 50; + this.SLOW_SPEED = 140; + this.FAST_SPEED = 640; + this.trackMouse = (e) => { + if (this.isDragging) { + this.mouseY = e.clientY; + } + }; + this.scrollTick = (ts) => { + if (!this.isDragging || !this.scrollableContent) + return; + const dt = this.lastTs ? (ts - this.lastTs) / 1e3 : 0; + this.lastTs = ts; + this.rect ?? (this.rect = this.scrollableContent.getBoundingClientRect()); + const velocity = this.calculateVelocity(); + if (velocity !== 0 && !this.isAtBoundary(velocity)) { + const scrollDelta = velocity * dt; + this.scrollableContent.scrollTop += scrollDelta; + this.rect = null; + this.eventBus.emit(CoreEvents.EDGE_SCROLL_TICK, { scrollDelta }); + this.setScrollingState(true); + } else { + this.setScrollingState(false); + } + this.scrollRAF = requestAnimationFrame(this.scrollTick); + }; + this.subscribeToEvents(); + document.addEventListener("pointermove", this.trackMouse); + } + init(scrollableContent) { + this.scrollableContent = scrollableContent; + this.timeGrid = scrollableContent.querySelector("swp-time-grid"); + this.scrollableContent.style.scrollBehavior = "auto"; + } + subscribeToEvents() { + this.eventBus.on(CoreEvents.EVENT_DRAG_START, (event) => { + const payload = event.detail; + this.draggedElement = payload.element; + this.startDrag(); + }); + this.eventBus.on(CoreEvents.EVENT_DRAG_END, () => this.stopDrag()); + this.eventBus.on(CoreEvents.EVENT_DRAG_CANCEL, () => this.stopDrag()); + } + startDrag() { + this.isDragging = true; + this.isScrolling = false; + this.lastTs = 0; + this.initialScrollTop = this.scrollableContent?.scrollTop ?? 0; + if (this.scrollRAF === null) { + this.scrollRAF = requestAnimationFrame(this.scrollTick); + } + } + stopDrag() { + this.isDragging = false; + this.setScrollingState(false); + if (this.scrollRAF !== null) { + cancelAnimationFrame(this.scrollRAF); + this.scrollRAF = null; + } + this.rect = null; + this.lastTs = 0; + this.initialScrollTop = 0; + } + calculateVelocity() { + if (!this.rect) + return 0; + const distTop = this.mouseY - this.rect.top; + const distBot = this.rect.bottom - this.mouseY; + if (distTop < this.INNER_ZONE) + return -this.FAST_SPEED; + if (distTop < this.OUTER_ZONE) + return -this.SLOW_SPEED; + if (distBot < this.INNER_ZONE) + return this.FAST_SPEED; + if (distBot < this.OUTER_ZONE) + return this.SLOW_SPEED; + return 0; + } + isAtBoundary(velocity) { + if (!this.scrollableContent || !this.timeGrid || !this.draggedElement) + return false; + const atTop = this.scrollableContent.scrollTop <= 0 && velocity < 0; + const atBottom = velocity > 0 && this.draggedElement.getBoundingClientRect().bottom >= this.timeGrid.getBoundingClientRect().bottom; + return atTop || atBottom; + } + setScrollingState(scrolling) { + if (this.isScrolling === scrolling) + return; + this.isScrolling = scrolling; + if (scrolling) { + this.eventBus.emit(CoreEvents.EDGE_SCROLL_STARTED, {}); + } else { + this.initialScrollTop = this.scrollableContent?.scrollTop ?? 0; + this.eventBus.emit(CoreEvents.EDGE_SCROLL_STOPPED, {}); + } + } +}; +__name(_EdgeScrollManager, "EdgeScrollManager"); +var EdgeScrollManager = _EdgeScrollManager; + +// src/v2/managers/ResizeManager.ts +var _ResizeManager = class _ResizeManager { + constructor(eventBus, gridConfig, dateService) { + this.eventBus = eventBus; + this.gridConfig = gridConfig; + this.dateService = dateService; + this.container = null; + this.resizeState = null; + this.Z_INDEX_RESIZING = "1000"; + this.ANIMATION_SPEED = 0.35; + this.MIN_HEIGHT_MINUTES = 15; + this.handleMouseOver = (e) => { + const target = e.target; + const eventElement = target.closest("swp-event"); + if (!eventElement || this.resizeState) + return; + if (!eventElement.querySelector(":scope > swp-resize-handle")) { + const handle = this.createResizeHandle(); + eventElement.appendChild(handle); + } + }; + this.handlePointerDown = (e) => { + const handle = e.target.closest("swp-resize-handle"); + if (!handle) + return; + const element = handle.parentElement; + if (!element) + return; + const eventId = element.dataset.eventId || ""; + const startHeight = element.offsetHeight; + const startDurationMinutes = pixelsToMinutes(startHeight, this.gridConfig); + const container2 = element.closest("swp-event-group") ?? element; + const prevZIndex = container2.style.zIndex; + this.resizeState = { + eventId, + element, + handleElement: handle, + startY: e.clientY, + startHeight, + startDurationMinutes, + pointerId: e.pointerId, + prevZIndex, + // Animation state + currentHeight: startHeight, + targetHeight: startHeight, + animationId: null + }; + container2.style.zIndex = this.Z_INDEX_RESIZING; + try { + handle.setPointerCapture(e.pointerId); + } catch (err) { + console.warn("Pointer capture failed:", err); + } + document.documentElement.classList.add("swp--resizing"); + this.eventBus.emit(CoreEvents.EVENT_RESIZE_START, { + eventId, + element, + startHeight + }); + e.preventDefault(); + }; + this.handlePointerMove = (e) => { + if (!this.resizeState) + return; + const deltaY = e.clientY - this.resizeState.startY; + const minHeight = this.MIN_HEIGHT_MINUTES / 60 * this.gridConfig.hourHeight; + const newHeight = Math.max(minHeight, this.resizeState.startHeight + deltaY); + this.resizeState.targetHeight = newHeight; + if (this.resizeState.animationId === null) { + this.animateHeight(); + } + }; + this.animateHeight = () => { + if (!this.resizeState) + return; + const diff2 = this.resizeState.targetHeight - this.resizeState.currentHeight; + if (Math.abs(diff2) < 0.5) { + this.resizeState.animationId = null; + return; + } + this.resizeState.currentHeight += diff2 * this.ANIMATION_SPEED; + this.resizeState.element.style.height = `${this.resizeState.currentHeight}px`; + this.updateTimestampDisplay(); + this.resizeState.animationId = requestAnimationFrame(this.animateHeight); + }; + this.handlePointerUp = (e) => { + if (!this.resizeState) + return; + if (this.resizeState.animationId !== null) { + cancelAnimationFrame(this.resizeState.animationId); + } + try { + this.resizeState.handleElement.releasePointerCapture(e.pointerId); + } catch (err) { + console.warn("Pointer release failed:", err); + } + this.snapToGridFinal(); + this.updateTimestampDisplay(); + const container2 = this.resizeState.element.closest("swp-event-group") ?? this.resizeState.element; + container2.style.zIndex = this.resizeState.prevZIndex; + document.documentElement.classList.remove("swp--resizing"); + const column = this.resizeState.element.closest("swp-day-column"); + const columnKey = column?.dataset.columnKey || ""; + const date = column?.dataset.date || ""; + const swpEvent = SwpEvent.fromElement(this.resizeState.element, columnKey, date, this.gridConfig); + this.eventBus.emit(CoreEvents.EVENT_RESIZE_END, { + swpEvent + }); + this.resizeState = null; + }; + } + /** + * Initialize resize functionality on container + */ + init(container2) { + this.container = container2; + container2.addEventListener("mouseover", this.handleMouseOver, true); + document.addEventListener("pointerdown", this.handlePointerDown, true); + document.addEventListener("pointermove", this.handlePointerMove, true); + document.addEventListener("pointerup", this.handlePointerUp, true); + } + /** + * Create resize handle element + */ + createResizeHandle() { + const handle = document.createElement("swp-resize-handle"); + handle.setAttribute("aria-label", "Resize event"); + handle.setAttribute("role", "separator"); + return handle; + } + /** + * Update timestamp display with snapped end time + */ + updateTimestampDisplay() { + if (!this.resizeState) + return; + const timeEl = this.resizeState.element.querySelector("swp-event-time"); + if (!timeEl) + return; + const top = parseFloat(this.resizeState.element.style.top) || 0; + const startMinutesFromGrid = pixelsToMinutes(top, this.gridConfig); + const startMinutes = this.gridConfig.dayStartHour * 60 + startMinutesFromGrid; + const snappedHeight = snapToGrid(this.resizeState.currentHeight, this.gridConfig); + const durationMinutes = pixelsToMinutes(snappedHeight, this.gridConfig); + const endMinutes = startMinutes + durationMinutes; + const start = this.minutesToDate(startMinutes); + const end = this.minutesToDate(endMinutes); + timeEl.textContent = this.dateService.formatTimeRange(start, end); + } + /** + * Convert minutes since midnight to Date + */ + minutesToDate(minutes) { + const date = /* @__PURE__ */ new Date(); + date.setHours(Math.floor(minutes / 60) % 24, minutes % 60, 0, 0); + return date; + } + /** + * Snap final height to grid interval + */ + snapToGridFinal() { + if (!this.resizeState) + return; + const currentHeight = this.resizeState.element.offsetHeight; + const snappedHeight = snapToGrid(currentHeight, this.gridConfig); + const minHeight = minutesToPixels(this.MIN_HEIGHT_MINUTES, this.gridConfig); + const finalHeight = Math.max(minHeight, snappedHeight); + this.resizeState.element.style.height = `${finalHeight}px`; + this.resizeState.currentHeight = finalHeight; + } +}; +__name(_ResizeManager, "ResizeManager"); +var ResizeManager = _ResizeManager; + +// src/v2/managers/EventPersistenceManager.ts +var _EventPersistenceManager = class _EventPersistenceManager { + constructor(eventService, eventBus, dateService) { + this.eventService = eventService; + this.eventBus = eventBus; + this.dateService = dateService; + this.handleDragEnd = async (e) => { + const payload = e.detail; + const { swpEvent } = payload; + const event = await this.eventService.get(swpEvent.eventId); + if (!event) { + console.warn(`EventPersistenceManager: Event ${swpEvent.eventId} not found`); + return; + } + const { resource } = this.dateService.parseColumnKey(swpEvent.columnKey); + const updatedEvent = { + ...event, + start: swpEvent.start, + end: swpEvent.end, + resourceId: resource ?? event.resourceId, + allDay: payload.target === "header", + syncStatus: "pending" + }; + await this.eventService.save(updatedEvent); + const updatePayload = { + eventId: updatedEvent.id, + sourceColumnKey: payload.sourceColumnKey, + targetColumnKey: swpEvent.columnKey + }; + this.eventBus.emit(CoreEvents.EVENT_UPDATED, updatePayload); + }; + this.handleResizeEnd = async (e) => { + const payload = e.detail; + const { swpEvent } = payload; + const event = await this.eventService.get(swpEvent.eventId); + if (!event) { + console.warn(`EventPersistenceManager: Event ${swpEvent.eventId} not found`); + return; + } + const updatedEvent = { + ...event, + end: swpEvent.end, + syncStatus: "pending" + }; + await this.eventService.save(updatedEvent); + const updatePayload = { + eventId: updatedEvent.id, + sourceColumnKey: swpEvent.columnKey, + targetColumnKey: swpEvent.columnKey + }; + this.eventBus.emit(CoreEvents.EVENT_UPDATED, updatePayload); + }; + this.setupListeners(); + } + setupListeners() { + this.eventBus.on(CoreEvents.EVENT_DRAG_END, this.handleDragEnd); + this.eventBus.on(CoreEvents.EVENT_RESIZE_END, this.handleResizeEnd); + } +}; +__name(_EventPersistenceManager, "EventPersistenceManager"); +var EventPersistenceManager = _EventPersistenceManager; + +// src/v2/V2CompositionRoot.ts +var defaultTimeFormatConfig = { + timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, + use24HourFormat: true, + locale: "da-DK", + dateFormat: "locale", + showSeconds: false +}; +var defaultGridConfig = { + hourHeight: 64, + dayStartHour: 6, + dayEndHour: 18, + snapInterval: 15, + gridStartThresholdMinutes: 30 +}; +function createV2Container() { + const container2 = new Container(); + const builder = container2.builder(); + builder.registerInstance(defaultTimeFormatConfig).as("ITimeFormatConfig"); + builder.registerInstance(defaultGridConfig).as("IGridConfig"); + builder.registerType(EventBus).as("EventBus"); + builder.registerType(EventBus).as("IEventBus"); + builder.registerType(DateService).as("DateService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("ITimeFormatConfig"), + void 0 + ] + }); + builder.registerType(IndexedDBContext).as("IndexedDBContext").autoWire({ + mapResolvers: [ + (c) => c.resolveTypeAll("IStore") + ] + }); + builder.registerType(EventStore).as("IStore"); + builder.registerType(ResourceStore).as("IStore"); + builder.registerType(BookingStore).as("IStore"); + builder.registerType(CustomerStore).as("IStore"); + builder.registerType(TeamStore).as("IStore"); + builder.registerType(DepartmentStore).as("IStore"); + builder.registerType(ScheduleOverrideStore).as("IStore"); + builder.registerType(AuditStore).as("IStore"); + builder.registerType(SettingsStore).as("IStore"); + builder.registerType(ViewConfigStore).as("IStore"); + builder.registerType(EventService).as("IEntityService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(EventService).as("IEntityService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(EventService).as("EventService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(ResourceService).as("IEntityService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(ResourceService).as("IEntityService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(ResourceService).as("ResourceService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(BookingService).as("IEntityService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(BookingService).as("IEntityService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(BookingService).as("BookingService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(CustomerService).as("IEntityService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(CustomerService).as("IEntityService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(CustomerService).as("CustomerService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(TeamService).as("IEntityService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(TeamService).as("IEntityService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(TeamService).as("TeamService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(DepartmentService).as("IEntityService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(DepartmentService).as("IEntityService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(DepartmentService).as("DepartmentService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(SettingsService).as("IEntityService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(SettingsService).as("IEntityService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(SettingsService).as("SettingsService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(ViewConfigService).as("IEntityService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(ViewConfigService).as("IEntityService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(ViewConfigService).as("ViewConfigService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(MockEventRepository).as("IApiRepository"); + builder.registerType(MockEventRepository).as("IApiRepository"); + builder.registerType(MockResourceRepository).as("IApiRepository"); + builder.registerType(MockResourceRepository).as("IApiRepository"); + builder.registerType(MockBookingRepository).as("IApiRepository"); + builder.registerType(MockBookingRepository).as("IApiRepository"); + builder.registerType(MockCustomerRepository).as("IApiRepository"); + builder.registerType(MockCustomerRepository).as("IApiRepository"); + builder.registerType(MockAuditRepository).as("IApiRepository"); + builder.registerType(MockAuditRepository).as("IApiRepository"); + builder.registerType(MockTeamRepository).as("IApiRepository"); + builder.registerType(MockTeamRepository).as("IApiRepository"); + builder.registerType(MockDepartmentRepository).as("IApiRepository"); + builder.registerType(MockDepartmentRepository).as("IApiRepository"); + builder.registerType(MockSettingsRepository).as("IApiRepository"); + builder.registerType(MockSettingsRepository).as("IApiRepository"); + builder.registerType(MockViewConfigRepository).as("IApiRepository"); + builder.registerType(MockViewConfigRepository).as("IApiRepository"); + builder.registerType(AuditService).as("AuditService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(DataSeeder).as("DataSeeder").autoWire({ + mapResolvers: [ + (c) => c.resolveTypeAll("IEntityService"), + (c) => c.resolveTypeAll("IApiRepository") + ] + }); + builder.registerType(ScheduleOverrideService).as("ScheduleOverrideService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext") + ] + }); + builder.registerType(ResourceScheduleService).as("ResourceScheduleService").autoWire({ + mapResolvers: [ + (c) => c.resolveType("ResourceService"), + (c) => c.resolveType("ScheduleOverrideService"), + (c) => c.resolveType("DateService") + ] + }); + builder.registerType(EventRenderer).as("EventRenderer").autoWire({ + mapResolvers: [ + (c) => c.resolveType("EventService"), + (c) => c.resolveType("DateService"), + (c) => c.resolveType("IGridConfig"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(ScheduleRenderer).as("ScheduleRenderer").autoWire({ + mapResolvers: [ + (c) => c.resolveType("ResourceScheduleService"), + (c) => c.resolveType("DateService"), + (c) => c.resolveType("IGridConfig") + ] + }); + builder.registerType(HeaderDrawerRenderer).as("HeaderDrawerRenderer").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IEventBus"), + (c) => c.resolveType("IGridConfig"), + (c) => c.resolveType("HeaderDrawerManager"), + (c) => c.resolveType("EventService"), + (c) => c.resolveType("DateService") + ] + }); + builder.registerType(DateRenderer).as("IRenderer").autoWire({ + mapResolvers: [ + (c) => c.resolveType("DateService") + ] + }); + builder.registerType(ResourceRenderer).as("IRenderer").autoWire({ + mapResolvers: [ + (c) => c.resolveType("ResourceService") + ] + }); + builder.registerType(TeamRenderer).as("IRenderer").autoWire({ + mapResolvers: [ + (c) => c.resolveType("TeamService") + ] + }); + builder.registerType(DepartmentRenderer).as("IRenderer").autoWire({ + mapResolvers: [ + (c) => c.resolveType("DepartmentService") + ] + }); + builder.registerType(MockTeamStore).as("IGroupingStore"); + builder.registerType(MockResourceStore).as("IGroupingStore"); + builder.registerType(CalendarOrchestrator).as("CalendarOrchestrator").autoWire({ + mapResolvers: [ + (c) => c.resolveTypeAll("IRenderer"), + (c) => c.resolveType("EventRenderer"), + (c) => c.resolveType("ScheduleRenderer"), + (c) => c.resolveType("HeaderDrawerRenderer"), + (c) => c.resolveType("DateService"), + (c) => c.resolveTypeAll("IEntityService") + ] + }); + builder.registerType(TimeAxisRenderer).as("TimeAxisRenderer"); + builder.registerType(ScrollManager).as("ScrollManager"); + builder.registerType(HeaderDrawerManager).as("HeaderDrawerManager"); + builder.registerType(DragDropManager).as("DragDropManager").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IEventBus"), + (c) => c.resolveType("IGridConfig") + ] + }); + builder.registerType(EdgeScrollManager).as("EdgeScrollManager").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(ResizeManager).as("ResizeManager").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IEventBus"), + (c) => c.resolveType("IGridConfig"), + (c) => c.resolveType("DateService") + ] + }); + builder.registerType(EventPersistenceManager).as("EventPersistenceManager").autoWire({ + mapResolvers: [ + (c) => c.resolveType("EventService"), + (c) => c.resolveType("IEventBus"), + (c) => c.resolveType("DateService") + ] + }); + builder.registerType(CalendarApp).as("CalendarApp").autoWire({ + mapResolvers: [ + (c) => c.resolveType("CalendarOrchestrator"), + (c) => c.resolveType("TimeAxisRenderer"), + (c) => c.resolveType("DateService"), + (c) => c.resolveType("ScrollManager"), + (c) => c.resolveType("HeaderDrawerManager"), + (c) => c.resolveType("DragDropManager"), + (c) => c.resolveType("EdgeScrollManager"), + (c) => c.resolveType("ResizeManager"), + (c) => c.resolveType("HeaderDrawerRenderer"), + (c) => c.resolveType("EventPersistenceManager"), + (c) => c.resolveType("SettingsService"), + (c) => c.resolveType("ViewConfigService"), + (c) => c.resolveType("IEventBus") + ] + }); + builder.registerType(DemoApp).as("DemoApp").autoWire({ + mapResolvers: [ + (c) => c.resolveType("IndexedDBContext"), + (c) => c.resolveType("DataSeeder"), + (c) => c.resolveType("AuditService"), + (c) => c.resolveType("CalendarApp"), + (c) => c.resolveType("DateService"), + (c) => c.resolveType("ResourceService"), + (c) => c.resolveType("IEventBus") + ] + }); + return builder.build(); +} +__name(createV2Container, "createV2Container"); + +// src/v2/demo/index.ts +var container = createV2Container(); +container.resolveType("DemoApp").init().catch(console.error); +//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vbm9kZV9tb2R1bGVzL2RheWpzL2RheWpzLm1pbi5qcyIsICIuLi8uLi9ub2RlX21vZHVsZXMvZGF5anMvcGx1Z2luL3V0Yy5qcyIsICIuLi8uLi9ub2RlX21vZHVsZXMvZGF5anMvcGx1Z2luL3RpbWV6b25lLmpzIiwgIi4uLy4uL25vZGVfbW9kdWxlcy9kYXlqcy9wbHVnaW4vaXNvV2Vlay5qcyIsICIuLi8uLi9ub2RlX21vZHVsZXMvQG5vdmFkaS9jb3JlL2Rpc3QvdG9rZW4uanMiLCAiLi4vLi4vbm9kZV9tb2R1bGVzL0Bub3ZhZGkvY29yZS9kaXN0L2Vycm9ycy5qcyIsICIuLi8uLi9ub2RlX21vZHVsZXMvQG5vdmFkaS9jb3JlL2Rpc3QvYXV0b3dpcmUuanMiLCAiLi4vLi4vbm9kZV9tb2R1bGVzL0Bub3ZhZGkvY29yZS9kaXN0L2J1aWxkZXIuanMiLCAiLi4vLi4vbm9kZV9tb2R1bGVzL0Bub3ZhZGkvY29yZS9kaXN0L2NvbnRhaW5lci5qcyIsICIuLi8uLi9zcmMvdjIvZmVhdHVyZXMvZGF0ZS9EYXRlUmVuZGVyZXIudHMiLCAiLi4vLi4vc3JjL3YyL2NvcmUvRGF0ZVNlcnZpY2UudHMiLCAiLi4vLi4vc3JjL3YyL2NvcmUvQmFzZUdyb3VwaW5nUmVuZGVyZXIudHMiLCAiLi4vLi4vc3JjL3YyL2ZlYXR1cmVzL3Jlc291cmNlL1Jlc291cmNlUmVuZGVyZXIudHMiLCAiLi4vLi4vc3JjL3YyL2ZlYXR1cmVzL3RlYW0vVGVhbVJlbmRlcmVyLnRzIiwgIi4uLy4uL3NyYy92Mi9mZWF0dXJlcy9kZXBhcnRtZW50L0RlcGFydG1lbnRSZW5kZXJlci50cyIsICIuLi8uLi9zcmMvdjIvY29yZS9SZW5kZXJCdWlsZGVyLnRzIiwgIi4uLy4uL3NyYy92Mi9jb3JlL0ZpbHRlclRlbXBsYXRlLnRzIiwgIi4uLy4uL3NyYy92Mi9jb3JlL0NhbGVuZGFyT3JjaGVzdHJhdG9yLnRzIiwgIi4uLy4uL3NyYy92Mi9jb3JlL05hdmlnYXRpb25BbmltYXRvci50cyIsICIuLi8uLi9zcmMvdjIvY29yZS9DYWxlbmRhckV2ZW50cy50cyIsICIuLi8uLi9zcmMvdjIvY29yZS9DYWxlbmRhckFwcC50cyIsICIuLi8uLi9zcmMvdjIvZmVhdHVyZXMvdGltZWF4aXMvVGltZUF4aXNSZW5kZXJlci50cyIsICIuLi8uLi9zcmMvdjIvY29yZS9TY3JvbGxNYW5hZ2VyLnRzIiwgIi4uLy4uL3NyYy92Mi9jb3JlL0hlYWRlckRyYXdlck1hbmFnZXIudHMiLCAiLi4vLi4vc3JjL3YyL2RlbW8vTW9ja1N0b3Jlcy50cyIsICIuLi8uLi9zcmMvdjIvZGVtby9EZW1vQXBwLnRzIiwgIi4uLy4uL3NyYy92Mi9jb3JlL0V2ZW50QnVzLnRzIiwgIi4uLy4uL3NyYy92Mi9zdG9yYWdlL0luZGV4ZWREQkNvbnRleHQudHMiLCAiLi4vLi4vc3JjL3YyL3N0b3JhZ2UvZXZlbnRzL0V2ZW50U3RvcmUudHMiLCAiLi4vLi4vc3JjL3YyL3N0b3JhZ2UvZXZlbnRzL0V2ZW50U2VyaWFsaXphdGlvbi50cyIsICIuLi8uLi9zcmMvdjIvc3RvcmFnZS9TeW5jUGx1Z2luLnRzIiwgIi4uLy4uL3NyYy92Mi9jb25zdGFudHMvQ29yZUV2ZW50cy50cyIsICIuLi8uLi9ub2RlX21vZHVsZXMvanNvbi1kaWZmLXRzL3NyYy9oZWxwZXJzLnRzIiwgIi4uLy4uL25vZGVfbW9kdWxlcy9qc29uLWRpZmYtdHMvc3JjL2pzb25EaWZmLnRzIiwgIi4uLy4uL25vZGVfbW9kdWxlcy9qc29uLWRpZmYtdHMvc3JjL2pzb25Db21wYXJlLnRzIiwgIi4uLy4uL3NyYy92Mi9zdG9yYWdlL0Jhc2VFbnRpdHlTZXJ2aWNlLnRzIiwgIi4uLy4uL3NyYy92Mi9zdG9yYWdlL2V2ZW50cy9FdmVudFNlcnZpY2UudHMiLCAiLi4vLi4vc3JjL3YyL3N0b3JhZ2UvcmVzb3VyY2VzL1Jlc291cmNlU3RvcmUudHMiLCAiLi4vLi4vc3JjL3YyL3N0b3JhZ2UvcmVzb3VyY2VzL1Jlc291cmNlU2VydmljZS50cyIsICIuLi8uLi9zcmMvdjIvc3RvcmFnZS9ib29raW5ncy9Cb29raW5nU3RvcmUudHMiLCAiLi4vLi4vc3JjL3YyL3N0b3JhZ2UvYm9va2luZ3MvQm9va2luZ1NlcnZpY2UudHMiLCAiLi4vLi4vc3JjL3YyL3N0b3JhZ2UvY3VzdG9tZXJzL0N1c3RvbWVyU3RvcmUudHMiLCAiLi4vLi4vc3JjL3YyL3N0b3JhZ2UvY3VzdG9tZXJzL0N1c3RvbWVyU2VydmljZS50cyIsICIuLi8uLi9zcmMvdjIvc3RvcmFnZS90ZWFtcy9UZWFtU3RvcmUudHMiLCAiLi4vLi4vc3JjL3YyL3N0b3JhZ2UvdGVhbXMvVGVhbVNlcnZpY2UudHMiLCAiLi4vLi4vc3JjL3YyL3N0b3JhZ2UvZGVwYXJ0bWVudHMvRGVwYXJ0bWVudFN0b3JlLnRzIiwgIi4uLy4uL3NyYy92Mi9zdG9yYWdlL2RlcGFydG1lbnRzL0RlcGFydG1lbnRTZXJ2aWNlLnRzIiwgIi4uLy4uL3NyYy92Mi9zdG9yYWdlL3NldHRpbmdzL1NldHRpbmdzU3RvcmUudHMiLCAiLi4vLi4vc3JjL3YyL3R5cGVzL1NldHRpbmdzVHlwZXMudHMiLCAiLi4vLi4vc3JjL3YyL3N0b3JhZ2Uvc2V0dGluZ3MvU2V0dGluZ3NTZXJ2aWNlLnRzIiwgIi4uLy4uL3NyYy92Mi9zdG9yYWdlL3ZpZXdjb25maWdzL1ZpZXdDb25maWdTdG9yZS50cyIsICIuLi8uLi9zcmMvdjIvc3RvcmFnZS92aWV3Y29uZmlncy9WaWV3Q29uZmlnU2VydmljZS50cyIsICIuLi8uLi9zcmMvdjIvc3RvcmFnZS9hdWRpdC9BdWRpdFN0b3JlLnRzIiwgIi4uLy4uL3NyYy92Mi9zdG9yYWdlL2F1ZGl0L0F1ZGl0U2VydmljZS50cyIsICIuLi8uLi9zcmMvdjIvcmVwb3NpdG9yaWVzL01vY2tFdmVudFJlcG9zaXRvcnkudHMiLCAiLi4vLi4vc3JjL3YyL3JlcG9zaXRvcmllcy9Nb2NrUmVzb3VyY2VSZXBvc2l0b3J5LnRzIiwgIi4uLy4uL3NyYy92Mi9yZXBvc2l0b3JpZXMvTW9ja0Jvb2tpbmdSZXBvc2l0b3J5LnRzIiwgIi4uLy4uL3NyYy92Mi9yZXBvc2l0b3JpZXMvTW9ja0N1c3RvbWVyUmVwb3NpdG9yeS50cyIsICIuLi8uLi9zcmMvdjIvcmVwb3NpdG9yaWVzL01vY2tBdWRpdFJlcG9zaXRvcnkudHMiLCAiLi4vLi4vc3JjL3YyL3JlcG9zaXRvcmllcy9Nb2NrVGVhbVJlcG9zaXRvcnkudHMiLCAiLi4vLi4vc3JjL3YyL3JlcG9zaXRvcmllcy9Nb2NrRGVwYXJ0bWVudFJlcG9zaXRvcnkudHMiLCAiLi4vLi4vc3JjL3YyL3JlcG9zaXRvcmllcy9Nb2NrU2V0dGluZ3NSZXBvc2l0b3J5LnRzIiwgIi4uLy4uL3NyYy92Mi9yZXBvc2l0b3JpZXMvTW9ja1ZpZXdDb25maWdSZXBvc2l0b3J5LnRzIiwgIi4uLy4uL3NyYy92Mi93b3JrZXJzL0RhdGFTZWVkZXIudHMiLCAiLi4vLi4vc3JjL3YyL3V0aWxzL1Bvc2l0aW9uVXRpbHMudHMiLCAiLi4vLi4vc3JjL3YyL2ZlYXR1cmVzL2V2ZW50L0V2ZW50TGF5b3V0RW5naW5lLnRzIiwgIi4uLy4uL3NyYy92Mi9mZWF0dXJlcy9ldmVudC9FdmVudFJlbmRlcmVyLnRzIiwgIi4uLy4uL3NyYy92Mi9mZWF0dXJlcy9zY2hlZHVsZS9TY2hlZHVsZVJlbmRlcmVyLnRzIiwgIi4uLy4uL3NyYy92Mi9mZWF0dXJlcy9oZWFkZXJkcmF3ZXIvSGVhZGVyRHJhd2VyUmVuZGVyZXIudHMiLCAiLi4vLi4vc3JjL3YyL3N0b3JhZ2Uvc2NoZWR1bGVzL1NjaGVkdWxlT3ZlcnJpZGVTdG9yZS50cyIsICIuLi8uLi9zcmMvdjIvc3RvcmFnZS9zY2hlZHVsZXMvU2NoZWR1bGVPdmVycmlkZVNlcnZpY2UudHMiLCAiLi4vLi4vc3JjL3YyL3N0b3JhZ2Uvc2NoZWR1bGVzL1Jlc291cmNlU2NoZWR1bGVTZXJ2aWNlLnRzIiwgIi4uLy4uL3NyYy92Mi90eXBlcy9Td3BFdmVudC50cyIsICIuLi8uLi9zcmMvdjIvbWFuYWdlcnMvRHJhZ0Ryb3BNYW5hZ2VyLnRzIiwgIi4uLy4uL3NyYy92Mi9tYW5hZ2Vycy9FZGdlU2Nyb2xsTWFuYWdlci50cyIsICIuLi8uLi9zcmMvdjIvbWFuYWdlcnMvUmVzaXplTWFuYWdlci50cyIsICIuLi8uLi9zcmMvdjIvbWFuYWdlcnMvRXZlbnRQZXJzaXN0ZW5jZU1hbmFnZXIudHMiLCAiLi4vLi4vc3JjL3YyL1YyQ29tcG9zaXRpb25Sb290LnRzIiwgIi4uLy4uL3NyYy92Mi9kZW1vL2luZGV4LnRzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWyIhZnVuY3Rpb24odCxlKXtcIm9iamVjdFwiPT10eXBlb2YgZXhwb3J0cyYmXCJ1bmRlZmluZWRcIiE9dHlwZW9mIG1vZHVsZT9tb2R1bGUuZXhwb3J0cz1lKCk6XCJmdW5jdGlvblwiPT10eXBlb2YgZGVmaW5lJiZkZWZpbmUuYW1kP2RlZmluZShlKToodD1cInVuZGVmaW5lZFwiIT10eXBlb2YgZ2xvYmFsVGhpcz9nbG9iYWxUaGlzOnR8fHNlbGYpLmRheWpzPWUoKX0odGhpcywoZnVuY3Rpb24oKXtcInVzZSBzdHJpY3RcIjt2YXIgdD0xZTMsZT02ZTQsbj0zNmU1LHI9XCJtaWxsaXNlY29uZFwiLGk9XCJzZWNvbmRcIixzPVwibWludXRlXCIsdT1cImhvdXJcIixhPVwiZGF5XCIsbz1cIndlZWtcIixjPVwibW9udGhcIixmPVwicXVhcnRlclwiLGg9XCJ5ZWFyXCIsZD1cImRhdGVcIixsPVwiSW52YWxpZCBEYXRlXCIsJD0vXihcXGR7NH0pWy0vXT8oXFxkezEsMn0pP1stL10/KFxcZHswLDJ9KVtUdFxcc10qKFxcZHsxLDJ9KT86PyhcXGR7MSwyfSk/Oj8oXFxkezEsMn0pP1suOl0/KFxcZCspPyQvLHk9L1xcWyhbXlxcXV0rKV18WXsxLDR9fE17MSw0fXxEezEsMn18ZHsxLDR9fEh7MSwyfXxoezEsMn18YXxBfG17MSwyfXxzezEsMn18WnsxLDJ9fFNTUy9nLE09e25hbWU6XCJlblwiLHdlZWtkYXlzOlwiU3VuZGF5X01vbmRheV9UdWVzZGF5X1dlZG5lc2RheV9UaHVyc2RheV9GcmlkYXlfU2F0dXJkYXlcIi5zcGxpdChcIl9cIiksbW9udGhzOlwiSmFudWFyeV9GZWJydWFyeV9NYXJjaF9BcHJpbF9NYXlfSnVuZV9KdWx5X0F1Z3VzdF9TZXB0ZW1iZXJfT2N0b2Jlcl9Ob3ZlbWJlcl9EZWNlbWJlclwiLnNwbGl0KFwiX1wiKSxvcmRpbmFsOmZ1bmN0aW9uKHQpe3ZhciBlPVtcInRoXCIsXCJzdFwiLFwibmRcIixcInJkXCJdLG49dCUxMDA7cmV0dXJuXCJbXCIrdCsoZVsobi0yMCklMTBdfHxlW25dfHxlWzBdKStcIl1cIn19LG09ZnVuY3Rpb24odCxlLG4pe3ZhciByPVN0cmluZyh0KTtyZXR1cm4hcnx8ci5sZW5ndGg+PWU/dDpcIlwiK0FycmF5KGUrMS1yLmxlbmd0aCkuam9pbihuKSt0fSx2PXtzOm0sejpmdW5jdGlvbih0KXt2YXIgZT0tdC51dGNPZmZzZXQoKSxuPU1hdGguYWJzKGUpLHI9TWF0aC5mbG9vcihuLzYwKSxpPW4lNjA7cmV0dXJuKGU8PTA/XCIrXCI6XCItXCIpK20ociwyLFwiMFwiKStcIjpcIittKGksMixcIjBcIil9LG06ZnVuY3Rpb24gdChlLG4pe2lmKGUuZGF0ZSgpPG4uZGF0ZSgpKXJldHVybi10KG4sZSk7dmFyIHI9MTIqKG4ueWVhcigpLWUueWVhcigpKSsobi5tb250aCgpLWUubW9udGgoKSksaT1lLmNsb25lKCkuYWRkKHIsYykscz1uLWk8MCx1PWUuY2xvbmUoKS5hZGQocisocz8tMToxKSxjKTtyZXR1cm4rKC0ocisobi1pKS8ocz9pLXU6dS1pKSl8fDApfSxhOmZ1bmN0aW9uKHQpe3JldHVybiB0PDA/TWF0aC5jZWlsKHQpfHwwOk1hdGguZmxvb3IodCl9LHA6ZnVuY3Rpb24odCl7cmV0dXJue006Yyx5OmgsdzpvLGQ6YSxEOmQsaDp1LG06cyxzOmksbXM6cixROmZ9W3RdfHxTdHJpbmcodHx8XCJcIikudG9Mb3dlckNhc2UoKS5yZXBsYWNlKC9zJC8sXCJcIil9LHU6ZnVuY3Rpb24odCl7cmV0dXJuIHZvaWQgMD09PXR9fSxnPVwiZW5cIixEPXt9O0RbZ109TTt2YXIgcD1cIiRpc0RheWpzT2JqZWN0XCIsUz1mdW5jdGlvbih0KXtyZXR1cm4gdCBpbnN0YW5jZW9mIF98fCEoIXR8fCF0W3BdKX0sdz1mdW5jdGlvbiB0KGUsbixyKXt2YXIgaTtpZighZSlyZXR1cm4gZztpZihcInN0cmluZ1wiPT10eXBlb2YgZSl7dmFyIHM9ZS50b0xvd2VyQ2FzZSgpO0Rbc10mJihpPXMpLG4mJihEW3NdPW4saT1zKTt2YXIgdT1lLnNwbGl0KFwiLVwiKTtpZighaSYmdS5sZW5ndGg+MSlyZXR1cm4gdCh1WzBdKX1lbHNle3ZhciBhPWUubmFtZTtEW2FdPWUsaT1hfXJldHVybiFyJiZpJiYoZz1pKSxpfHwhciYmZ30sTz1mdW5jdGlvbih0LGUpe2lmKFModCkpcmV0dXJuIHQuY2xvbmUoKTt2YXIgbj1cIm9iamVjdFwiPT10eXBlb2YgZT9lOnt9O3JldHVybiBuLmRhdGU9dCxuLmFyZ3M9YXJndW1lbnRzLG5ldyBfKG4pfSxiPXY7Yi5sPXcsYi5pPVMsYi53PWZ1bmN0aW9uKHQsZSl7cmV0dXJuIE8odCx7bG9jYWxlOmUuJEwsdXRjOmUuJHUseDplLiR4LCRvZmZzZXQ6ZS4kb2Zmc2V0fSl9O3ZhciBfPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gTSh0KXt0aGlzLiRMPXcodC5sb2NhbGUsbnVsbCwhMCksdGhpcy5wYXJzZSh0KSx0aGlzLiR4PXRoaXMuJHh8fHQueHx8e30sdGhpc1twXT0hMH12YXIgbT1NLnByb3RvdHlwZTtyZXR1cm4gbS5wYXJzZT1mdW5jdGlvbih0KXt0aGlzLiRkPWZ1bmN0aW9uKHQpe3ZhciBlPXQuZGF0ZSxuPXQudXRjO2lmKG51bGw9PT1lKXJldHVybiBuZXcgRGF0ZShOYU4pO2lmKGIudShlKSlyZXR1cm4gbmV3IERhdGU7aWYoZSBpbnN0YW5jZW9mIERhdGUpcmV0dXJuIG5ldyBEYXRlKGUpO2lmKFwic3RyaW5nXCI9PXR5cGVvZiBlJiYhL1okL2kudGVzdChlKSl7dmFyIHI9ZS5tYXRjaCgkKTtpZihyKXt2YXIgaT1yWzJdLTF8fDAscz0ocls3XXx8XCIwXCIpLnN1YnN0cmluZygwLDMpO3JldHVybiBuP25ldyBEYXRlKERhdGUuVVRDKHJbMV0saSxyWzNdfHwxLHJbNF18fDAscls1XXx8MCxyWzZdfHwwLHMpKTpuZXcgRGF0ZShyWzFdLGksclszXXx8MSxyWzRdfHwwLHJbNV18fDAscls2XXx8MCxzKX19cmV0dXJuIG5ldyBEYXRlKGUpfSh0KSx0aGlzLmluaXQoKX0sbS5pbml0PWZ1bmN0aW9uKCl7dmFyIHQ9dGhpcy4kZDt0aGlzLiR5PXQuZ2V0RnVsbFllYXIoKSx0aGlzLiRNPXQuZ2V0TW9udGgoKSx0aGlzLiREPXQuZ2V0RGF0ZSgpLHRoaXMuJFc9dC5nZXREYXkoKSx0aGlzLiRIPXQuZ2V0SG91cnMoKSx0aGlzLiRtPXQuZ2V0TWludXRlcygpLHRoaXMuJHM9dC5nZXRTZWNvbmRzKCksdGhpcy4kbXM9dC5nZXRNaWxsaXNlY29uZHMoKX0sbS4kdXRpbHM9ZnVuY3Rpb24oKXtyZXR1cm4gYn0sbS5pc1ZhbGlkPWZ1bmN0aW9uKCl7cmV0dXJuISh0aGlzLiRkLnRvU3RyaW5nKCk9PT1sKX0sbS5pc1NhbWU9ZnVuY3Rpb24odCxlKXt2YXIgbj1PKHQpO3JldHVybiB0aGlzLnN0YXJ0T2YoZSk8PW4mJm48PXRoaXMuZW5kT2YoZSl9LG0uaXNBZnRlcj1mdW5jdGlvbih0LGUpe3JldHVybiBPKHQpPHRoaXMuc3RhcnRPZihlKX0sbS5pc0JlZm9yZT1mdW5jdGlvbih0LGUpe3JldHVybiB0aGlzLmVuZE9mKGUpPE8odCl9LG0uJGc9ZnVuY3Rpb24odCxlLG4pe3JldHVybiBiLnUodCk/dGhpc1tlXTp0aGlzLnNldChuLHQpfSxtLnVuaXg9ZnVuY3Rpb24oKXtyZXR1cm4gTWF0aC5mbG9vcih0aGlzLnZhbHVlT2YoKS8xZTMpfSxtLnZhbHVlT2Y9ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy4kZC5nZXRUaW1lKCl9LG0uc3RhcnRPZj1mdW5jdGlvbih0LGUpe3ZhciBuPXRoaXMscj0hIWIudShlKXx8ZSxmPWIucCh0KSxsPWZ1bmN0aW9uKHQsZSl7dmFyIGk9Yi53KG4uJHU/RGF0ZS5VVEMobi4keSxlLHQpOm5ldyBEYXRlKG4uJHksZSx0KSxuKTtyZXR1cm4gcj9pOmkuZW5kT2YoYSl9LCQ9ZnVuY3Rpb24odCxlKXtyZXR1cm4gYi53KG4udG9EYXRlKClbdF0uYXBwbHkobi50b0RhdGUoXCJzXCIpLChyP1swLDAsMCwwXTpbMjMsNTksNTksOTk5XSkuc2xpY2UoZSkpLG4pfSx5PXRoaXMuJFcsTT10aGlzLiRNLG09dGhpcy4kRCx2PVwic2V0XCIrKHRoaXMuJHU/XCJVVENcIjpcIlwiKTtzd2l0Y2goZil7Y2FzZSBoOnJldHVybiByP2woMSwwKTpsKDMxLDExKTtjYXNlIGM6cmV0dXJuIHI/bCgxLE0pOmwoMCxNKzEpO2Nhc2Ugbzp2YXIgZz10aGlzLiRsb2NhbGUoKS53ZWVrU3RhcnR8fDAsRD0oeTxnP3krNzp5KS1nO3JldHVybiBsKHI/bS1EOm0rKDYtRCksTSk7Y2FzZSBhOmNhc2UgZDpyZXR1cm4gJCh2K1wiSG91cnNcIiwwKTtjYXNlIHU6cmV0dXJuICQoditcIk1pbnV0ZXNcIiwxKTtjYXNlIHM6cmV0dXJuICQoditcIlNlY29uZHNcIiwyKTtjYXNlIGk6cmV0dXJuICQoditcIk1pbGxpc2Vjb25kc1wiLDMpO2RlZmF1bHQ6cmV0dXJuIHRoaXMuY2xvbmUoKX19LG0uZW5kT2Y9ZnVuY3Rpb24odCl7cmV0dXJuIHRoaXMuc3RhcnRPZih0LCExKX0sbS4kc2V0PWZ1bmN0aW9uKHQsZSl7dmFyIG4sbz1iLnAodCksZj1cInNldFwiKyh0aGlzLiR1P1wiVVRDXCI6XCJcIiksbD0obj17fSxuW2FdPWYrXCJEYXRlXCIsbltkXT1mK1wiRGF0ZVwiLG5bY109ZitcIk1vbnRoXCIsbltoXT1mK1wiRnVsbFllYXJcIixuW3VdPWYrXCJIb3Vyc1wiLG5bc109ZitcIk1pbnV0ZXNcIixuW2ldPWYrXCJTZWNvbmRzXCIsbltyXT1mK1wiTWlsbGlzZWNvbmRzXCIsbilbb10sJD1vPT09YT90aGlzLiREKyhlLXRoaXMuJFcpOmU7aWYobz09PWN8fG89PT1oKXt2YXIgeT10aGlzLmNsb25lKCkuc2V0KGQsMSk7eS4kZFtsXSgkKSx5LmluaXQoKSx0aGlzLiRkPXkuc2V0KGQsTWF0aC5taW4odGhpcy4kRCx5LmRheXNJbk1vbnRoKCkpKS4kZH1lbHNlIGwmJnRoaXMuJGRbbF0oJCk7cmV0dXJuIHRoaXMuaW5pdCgpLHRoaXN9LG0uc2V0PWZ1bmN0aW9uKHQsZSl7cmV0dXJuIHRoaXMuY2xvbmUoKS4kc2V0KHQsZSl9LG0uZ2V0PWZ1bmN0aW9uKHQpe3JldHVybiB0aGlzW2IucCh0KV0oKX0sbS5hZGQ9ZnVuY3Rpb24ocixmKXt2YXIgZCxsPXRoaXM7cj1OdW1iZXIocik7dmFyICQ9Yi5wKGYpLHk9ZnVuY3Rpb24odCl7dmFyIGU9TyhsKTtyZXR1cm4gYi53KGUuZGF0ZShlLmRhdGUoKStNYXRoLnJvdW5kKHQqcikpLGwpfTtpZigkPT09YylyZXR1cm4gdGhpcy5zZXQoYyx0aGlzLiRNK3IpO2lmKCQ9PT1oKXJldHVybiB0aGlzLnNldChoLHRoaXMuJHkrcik7aWYoJD09PWEpcmV0dXJuIHkoMSk7aWYoJD09PW8pcmV0dXJuIHkoNyk7dmFyIE09KGQ9e30sZFtzXT1lLGRbdV09bixkW2ldPXQsZClbJF18fDEsbT10aGlzLiRkLmdldFRpbWUoKStyKk07cmV0dXJuIGIudyhtLHRoaXMpfSxtLnN1YnRyYWN0PWZ1bmN0aW9uKHQsZSl7cmV0dXJuIHRoaXMuYWRkKC0xKnQsZSl9LG0uZm9ybWF0PWZ1bmN0aW9uKHQpe3ZhciBlPXRoaXMsbj10aGlzLiRsb2NhbGUoKTtpZighdGhpcy5pc1ZhbGlkKCkpcmV0dXJuIG4uaW52YWxpZERhdGV8fGw7dmFyIHI9dHx8XCJZWVlZLU1NLUREVEhIOm1tOnNzWlwiLGk9Yi56KHRoaXMpLHM9dGhpcy4kSCx1PXRoaXMuJG0sYT10aGlzLiRNLG89bi53ZWVrZGF5cyxjPW4ubW9udGhzLGY9bi5tZXJpZGllbSxoPWZ1bmN0aW9uKHQsbixpLHMpe3JldHVybiB0JiYodFtuXXx8dChlLHIpKXx8aVtuXS5zbGljZSgwLHMpfSxkPWZ1bmN0aW9uKHQpe3JldHVybiBiLnMocyUxMnx8MTIsdCxcIjBcIil9LCQ9Znx8ZnVuY3Rpb24odCxlLG4pe3ZhciByPXQ8MTI/XCJBTVwiOlwiUE1cIjtyZXR1cm4gbj9yLnRvTG93ZXJDYXNlKCk6cn07cmV0dXJuIHIucmVwbGFjZSh5LChmdW5jdGlvbih0LHIpe3JldHVybiByfHxmdW5jdGlvbih0KXtzd2l0Y2godCl7Y2FzZVwiWVlcIjpyZXR1cm4gU3RyaW5nKGUuJHkpLnNsaWNlKC0yKTtjYXNlXCJZWVlZXCI6cmV0dXJuIGIucyhlLiR5LDQsXCIwXCIpO2Nhc2VcIk1cIjpyZXR1cm4gYSsxO2Nhc2VcIk1NXCI6cmV0dXJuIGIucyhhKzEsMixcIjBcIik7Y2FzZVwiTU1NXCI6cmV0dXJuIGgobi5tb250aHNTaG9ydCxhLGMsMyk7Y2FzZVwiTU1NTVwiOnJldHVybiBoKGMsYSk7Y2FzZVwiRFwiOnJldHVybiBlLiREO2Nhc2VcIkREXCI6cmV0dXJuIGIucyhlLiRELDIsXCIwXCIpO2Nhc2VcImRcIjpyZXR1cm4gU3RyaW5nKGUuJFcpO2Nhc2VcImRkXCI6cmV0dXJuIGgobi53ZWVrZGF5c01pbixlLiRXLG8sMik7Y2FzZVwiZGRkXCI6cmV0dXJuIGgobi53ZWVrZGF5c1Nob3J0LGUuJFcsbywzKTtjYXNlXCJkZGRkXCI6cmV0dXJuIG9bZS4kV107Y2FzZVwiSFwiOnJldHVybiBTdHJpbmcocyk7Y2FzZVwiSEhcIjpyZXR1cm4gYi5zKHMsMixcIjBcIik7Y2FzZVwiaFwiOnJldHVybiBkKDEpO2Nhc2VcImhoXCI6cmV0dXJuIGQoMik7Y2FzZVwiYVwiOnJldHVybiAkKHMsdSwhMCk7Y2FzZVwiQVwiOnJldHVybiAkKHMsdSwhMSk7Y2FzZVwibVwiOnJldHVybiBTdHJpbmcodSk7Y2FzZVwibW1cIjpyZXR1cm4gYi5zKHUsMixcIjBcIik7Y2FzZVwic1wiOnJldHVybiBTdHJpbmcoZS4kcyk7Y2FzZVwic3NcIjpyZXR1cm4gYi5zKGUuJHMsMixcIjBcIik7Y2FzZVwiU1NTXCI6cmV0dXJuIGIucyhlLiRtcywzLFwiMFwiKTtjYXNlXCJaXCI6cmV0dXJuIGl9cmV0dXJuIG51bGx9KHQpfHxpLnJlcGxhY2UoXCI6XCIsXCJcIil9KSl9LG0udXRjT2Zmc2V0PWZ1bmN0aW9uKCl7cmV0dXJuIDE1Ki1NYXRoLnJvdW5kKHRoaXMuJGQuZ2V0VGltZXpvbmVPZmZzZXQoKS8xNSl9LG0uZGlmZj1mdW5jdGlvbihyLGQsbCl7dmFyICQseT10aGlzLE09Yi5wKGQpLG09TyhyKSx2PShtLnV0Y09mZnNldCgpLXRoaXMudXRjT2Zmc2V0KCkpKmUsZz10aGlzLW0sRD1mdW5jdGlvbigpe3JldHVybiBiLm0oeSxtKX07c3dpdGNoKE0pe2Nhc2UgaDokPUQoKS8xMjticmVhaztjYXNlIGM6JD1EKCk7YnJlYWs7Y2FzZSBmOiQ9RCgpLzM7YnJlYWs7Y2FzZSBvOiQ9KGctdikvNjA0OGU1O2JyZWFrO2Nhc2UgYTokPShnLXYpLzg2NGU1O2JyZWFrO2Nhc2UgdTokPWcvbjticmVhaztjYXNlIHM6JD1nL2U7YnJlYWs7Y2FzZSBpOiQ9Zy90O2JyZWFrO2RlZmF1bHQ6JD1nfXJldHVybiBsPyQ6Yi5hKCQpfSxtLmRheXNJbk1vbnRoPWZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuZW5kT2YoYykuJER9LG0uJGxvY2FsZT1mdW5jdGlvbigpe3JldHVybiBEW3RoaXMuJExdfSxtLmxvY2FsZT1mdW5jdGlvbih0LGUpe2lmKCF0KXJldHVybiB0aGlzLiRMO3ZhciBuPXRoaXMuY2xvbmUoKSxyPXcodCxlLCEwKTtyZXR1cm4gciYmKG4uJEw9ciksbn0sbS5jbG9uZT1mdW5jdGlvbigpe3JldHVybiBiLncodGhpcy4kZCx0aGlzKX0sbS50b0RhdGU9ZnVuY3Rpb24oKXtyZXR1cm4gbmV3IERhdGUodGhpcy52YWx1ZU9mKCkpfSxtLnRvSlNPTj1mdW5jdGlvbigpe3JldHVybiB0aGlzLmlzVmFsaWQoKT90aGlzLnRvSVNPU3RyaW5nKCk6bnVsbH0sbS50b0lTT1N0cmluZz1mdW5jdGlvbigpe3JldHVybiB0aGlzLiRkLnRvSVNPU3RyaW5nKCl9LG0udG9TdHJpbmc9ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy4kZC50b1VUQ1N0cmluZygpfSxNfSgpLGs9Xy5wcm90b3R5cGU7cmV0dXJuIE8ucHJvdG90eXBlPWssW1tcIiRtc1wiLHJdLFtcIiRzXCIsaV0sW1wiJG1cIixzXSxbXCIkSFwiLHVdLFtcIiRXXCIsYV0sW1wiJE1cIixjXSxbXCIkeVwiLGhdLFtcIiREXCIsZF1dLmZvckVhY2goKGZ1bmN0aW9uKHQpe2tbdFsxXV09ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMuJGcoZSx0WzBdLHRbMV0pfX0pKSxPLmV4dGVuZD1mdW5jdGlvbih0LGUpe3JldHVybiB0LiRpfHwodChlLF8sTyksdC4kaT0hMCksT30sTy5sb2NhbGU9dyxPLmlzRGF5anM9UyxPLnVuaXg9ZnVuY3Rpb24odCl7cmV0dXJuIE8oMWUzKnQpfSxPLmVuPURbZ10sTy5Mcz1ELE8ucD17fSxPfSkpOyIsICIhZnVuY3Rpb24odCxpKXtcIm9iamVjdFwiPT10eXBlb2YgZXhwb3J0cyYmXCJ1bmRlZmluZWRcIiE9dHlwZW9mIG1vZHVsZT9tb2R1bGUuZXhwb3J0cz1pKCk6XCJmdW5jdGlvblwiPT10eXBlb2YgZGVmaW5lJiZkZWZpbmUuYW1kP2RlZmluZShpKToodD1cInVuZGVmaW5lZFwiIT10eXBlb2YgZ2xvYmFsVGhpcz9nbG9iYWxUaGlzOnR8fHNlbGYpLmRheWpzX3BsdWdpbl91dGM9aSgpfSh0aGlzLChmdW5jdGlvbigpe1widXNlIHN0cmljdFwiO3ZhciB0PVwibWludXRlXCIsaT0vWystXVxcZFxcZCg/Ojo/XFxkXFxkKT8vZyxlPS8oWystXXxcXGRcXGQpL2c7cmV0dXJuIGZ1bmN0aW9uKHMsZixuKXt2YXIgdT1mLnByb3RvdHlwZTtuLnV0Yz1mdW5jdGlvbih0KXt2YXIgaT17ZGF0ZTp0LHV0YzohMCxhcmdzOmFyZ3VtZW50c307cmV0dXJuIG5ldyBmKGkpfSx1LnV0Yz1mdW5jdGlvbihpKXt2YXIgZT1uKHRoaXMudG9EYXRlKCkse2xvY2FsZTp0aGlzLiRMLHV0YzohMH0pO3JldHVybiBpP2UuYWRkKHRoaXMudXRjT2Zmc2V0KCksdCk6ZX0sdS5sb2NhbD1mdW5jdGlvbigpe3JldHVybiBuKHRoaXMudG9EYXRlKCkse2xvY2FsZTp0aGlzLiRMLHV0YzohMX0pfTt2YXIgcj11LnBhcnNlO3UucGFyc2U9ZnVuY3Rpb24odCl7dC51dGMmJih0aGlzLiR1PSEwKSx0aGlzLiR1dGlscygpLnUodC4kb2Zmc2V0KXx8KHRoaXMuJG9mZnNldD10LiRvZmZzZXQpLHIuY2FsbCh0aGlzLHQpfTt2YXIgbz11LmluaXQ7dS5pbml0PWZ1bmN0aW9uKCl7aWYodGhpcy4kdSl7dmFyIHQ9dGhpcy4kZDt0aGlzLiR5PXQuZ2V0VVRDRnVsbFllYXIoKSx0aGlzLiRNPXQuZ2V0VVRDTW9udGgoKSx0aGlzLiREPXQuZ2V0VVRDRGF0ZSgpLHRoaXMuJFc9dC5nZXRVVENEYXkoKSx0aGlzLiRIPXQuZ2V0VVRDSG91cnMoKSx0aGlzLiRtPXQuZ2V0VVRDTWludXRlcygpLHRoaXMuJHM9dC5nZXRVVENTZWNvbmRzKCksdGhpcy4kbXM9dC5nZXRVVENNaWxsaXNlY29uZHMoKX1lbHNlIG8uY2FsbCh0aGlzKX07dmFyIGE9dS51dGNPZmZzZXQ7dS51dGNPZmZzZXQ9ZnVuY3Rpb24ocyxmKXt2YXIgbj10aGlzLiR1dGlscygpLnU7aWYobihzKSlyZXR1cm4gdGhpcy4kdT8wOm4odGhpcy4kb2Zmc2V0KT9hLmNhbGwodGhpcyk6dGhpcy4kb2Zmc2V0O2lmKFwic3RyaW5nXCI9PXR5cGVvZiBzJiYocz1mdW5jdGlvbih0KXt2b2lkIDA9PT10JiYodD1cIlwiKTt2YXIgcz10Lm1hdGNoKGkpO2lmKCFzKXJldHVybiBudWxsO3ZhciBmPShcIlwiK3NbMF0pLm1hdGNoKGUpfHxbXCItXCIsMCwwXSxuPWZbMF0sdT02MCorZlsxXSsgK2ZbMl07cmV0dXJuIDA9PT11PzA6XCIrXCI9PT1uP3U6LXV9KHMpLG51bGw9PT1zKSlyZXR1cm4gdGhpczt2YXIgdT1NYXRoLmFicyhzKTw9MTY/NjAqczpzO2lmKDA9PT11KXJldHVybiB0aGlzLnV0YyhmKTt2YXIgcj10aGlzLmNsb25lKCk7aWYoZilyZXR1cm4gci4kb2Zmc2V0PXUsci4kdT0hMSxyO3ZhciBvPXRoaXMuJHU/dGhpcy50b0RhdGUoKS5nZXRUaW1lem9uZU9mZnNldCgpOi0xKnRoaXMudXRjT2Zmc2V0KCk7cmV0dXJuKHI9dGhpcy5sb2NhbCgpLmFkZCh1K28sdCkpLiRvZmZzZXQ9dSxyLiR4LiRsb2NhbE9mZnNldD1vLHJ9O3ZhciBoPXUuZm9ybWF0O3UuZm9ybWF0PWZ1bmN0aW9uKHQpe3ZhciBpPXR8fCh0aGlzLiR1P1wiWVlZWS1NTS1ERFRISDptbTpzc1taXVwiOlwiXCIpO3JldHVybiBoLmNhbGwodGhpcyxpKX0sdS52YWx1ZU9mPWZ1bmN0aW9uKCl7dmFyIHQ9dGhpcy4kdXRpbHMoKS51KHRoaXMuJG9mZnNldCk/MDp0aGlzLiRvZmZzZXQrKHRoaXMuJHguJGxvY2FsT2Zmc2V0fHx0aGlzLiRkLmdldFRpbWV6b25lT2Zmc2V0KCkpO3JldHVybiB0aGlzLiRkLnZhbHVlT2YoKS02ZTQqdH0sdS5pc1VUQz1mdW5jdGlvbigpe3JldHVybiEhdGhpcy4kdX0sdS50b0lTT1N0cmluZz1mdW5jdGlvbigpe3JldHVybiB0aGlzLnRvRGF0ZSgpLnRvSVNPU3RyaW5nKCl9LHUudG9TdHJpbmc9ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy50b0RhdGUoKS50b1VUQ1N0cmluZygpfTt2YXIgbD11LnRvRGF0ZTt1LnRvRGF0ZT1mdW5jdGlvbih0KXtyZXR1cm5cInNcIj09PXQmJnRoaXMuJG9mZnNldD9uKHRoaXMuZm9ybWF0KFwiWVlZWS1NTS1ERCBISDptbTpzczpTU1NcIikpLnRvRGF0ZSgpOmwuY2FsbCh0aGlzKX07dmFyIGM9dS5kaWZmO3UuZGlmZj1mdW5jdGlvbih0LGksZSl7aWYodCYmdGhpcy4kdT09PXQuJHUpcmV0dXJuIGMuY2FsbCh0aGlzLHQsaSxlKTt2YXIgcz10aGlzLmxvY2FsKCksZj1uKHQpLmxvY2FsKCk7cmV0dXJuIGMuY2FsbChzLGYsaSxlKX19fSkpOyIsICIhZnVuY3Rpb24odCxlKXtcIm9iamVjdFwiPT10eXBlb2YgZXhwb3J0cyYmXCJ1bmRlZmluZWRcIiE9dHlwZW9mIG1vZHVsZT9tb2R1bGUuZXhwb3J0cz1lKCk6XCJmdW5jdGlvblwiPT10eXBlb2YgZGVmaW5lJiZkZWZpbmUuYW1kP2RlZmluZShlKToodD1cInVuZGVmaW5lZFwiIT10eXBlb2YgZ2xvYmFsVGhpcz9nbG9iYWxUaGlzOnR8fHNlbGYpLmRheWpzX3BsdWdpbl90aW1lem9uZT1lKCl9KHRoaXMsKGZ1bmN0aW9uKCl7XCJ1c2Ugc3RyaWN0XCI7dmFyIHQ9e3llYXI6MCxtb250aDoxLGRheToyLGhvdXI6MyxtaW51dGU6NCxzZWNvbmQ6NX0sZT17fTtyZXR1cm4gZnVuY3Rpb24obixpLG8pe3ZhciByLGE9ZnVuY3Rpb24odCxuLGkpe3ZvaWQgMD09PWkmJihpPXt9KTt2YXIgbz1uZXcgRGF0ZSh0KSxyPWZ1bmN0aW9uKHQsbil7dm9pZCAwPT09biYmKG49e30pO3ZhciBpPW4udGltZVpvbmVOYW1lfHxcInNob3J0XCIsbz10K1wifFwiK2kscj1lW29dO3JldHVybiByfHwocj1uZXcgSW50bC5EYXRlVGltZUZvcm1hdChcImVuLVVTXCIse2hvdXIxMjohMSx0aW1lWm9uZTp0LHllYXI6XCJudW1lcmljXCIsbW9udGg6XCIyLWRpZ2l0XCIsZGF5OlwiMi1kaWdpdFwiLGhvdXI6XCIyLWRpZ2l0XCIsbWludXRlOlwiMi1kaWdpdFwiLHNlY29uZDpcIjItZGlnaXRcIix0aW1lWm9uZU5hbWU6aX0pLGVbb109cikscn0obixpKTtyZXR1cm4gci5mb3JtYXRUb1BhcnRzKG8pfSx1PWZ1bmN0aW9uKGUsbil7Zm9yKHZhciBpPWEoZSxuKSxyPVtdLHU9MDt1PGkubGVuZ3RoO3UrPTEpe3ZhciBmPWlbdV0scz1mLnR5cGUsbT1mLnZhbHVlLGM9dFtzXTtjPj0wJiYocltjXT1wYXJzZUludChtLDEwKSl9dmFyIGQ9clszXSxsPTI0PT09ZD8wOmQsaD1yWzBdK1wiLVwiK3JbMV0rXCItXCIrclsyXStcIiBcIitsK1wiOlwiK3JbNF0rXCI6XCIrcls1XStcIjowMDBcIix2PStlO3JldHVybihvLnV0YyhoKS52YWx1ZU9mKCktKHYtPXYlMWUzKSkvNmU0fSxmPWkucHJvdG90eXBlO2YudHo9ZnVuY3Rpb24odCxlKXt2b2lkIDA9PT10JiYodD1yKTt2YXIgbixpPXRoaXMudXRjT2Zmc2V0KCksYT10aGlzLnRvRGF0ZSgpLHU9YS50b0xvY2FsZVN0cmluZyhcImVuLVVTXCIse3RpbWVab25lOnR9KSxmPU1hdGgucm91bmQoKGEtbmV3IERhdGUodSkpLzFlMy82MCkscz0xNSotTWF0aC5yb3VuZChhLmdldFRpbWV6b25lT2Zmc2V0KCkvMTUpLWY7aWYoIU51bWJlcihzKSluPXRoaXMudXRjT2Zmc2V0KDAsZSk7ZWxzZSBpZihuPW8odSx7bG9jYWxlOnRoaXMuJEx9KS4kc2V0KFwibWlsbGlzZWNvbmRcIix0aGlzLiRtcykudXRjT2Zmc2V0KHMsITApLGUpe3ZhciBtPW4udXRjT2Zmc2V0KCk7bj1uLmFkZChpLW0sXCJtaW51dGVcIil9cmV0dXJuIG4uJHguJHRpbWV6b25lPXQsbn0sZi5vZmZzZXROYW1lPWZ1bmN0aW9uKHQpe3ZhciBlPXRoaXMuJHguJHRpbWV6b25lfHxvLnR6Lmd1ZXNzKCksbj1hKHRoaXMudmFsdWVPZigpLGUse3RpbWVab25lTmFtZTp0fSkuZmluZCgoZnVuY3Rpb24odCl7cmV0dXJuXCJ0aW1lem9uZW5hbWVcIj09PXQudHlwZS50b0xvd2VyQ2FzZSgpfSkpO3JldHVybiBuJiZuLnZhbHVlfTt2YXIgcz1mLnN0YXJ0T2Y7Zi5zdGFydE9mPWZ1bmN0aW9uKHQsZSl7aWYoIXRoaXMuJHh8fCF0aGlzLiR4LiR0aW1lem9uZSlyZXR1cm4gcy5jYWxsKHRoaXMsdCxlKTt2YXIgbj1vKHRoaXMuZm9ybWF0KFwiWVlZWS1NTS1ERCBISDptbTpzczpTU1NcIikse2xvY2FsZTp0aGlzLiRMfSk7cmV0dXJuIHMuY2FsbChuLHQsZSkudHoodGhpcy4keC4kdGltZXpvbmUsITApfSxvLnR6PWZ1bmN0aW9uKHQsZSxuKXt2YXIgaT1uJiZlLGE9bnx8ZXx8cixmPXUoK28oKSxhKTtpZihcInN0cmluZ1wiIT10eXBlb2YgdClyZXR1cm4gbyh0KS50eihhKTt2YXIgcz1mdW5jdGlvbih0LGUsbil7dmFyIGk9dC02MCplKjFlMyxvPXUoaSxuKTtpZihlPT09bylyZXR1cm5baSxlXTt2YXIgcj11KGktPTYwKihvLWUpKjFlMyxuKTtyZXR1cm4gbz09PXI/W2ksb106W3QtNjAqTWF0aC5taW4obyxyKSoxZTMsTWF0aC5tYXgobyxyKV19KG8udXRjKHQsaSkudmFsdWVPZigpLGYsYSksbT1zWzBdLGM9c1sxXSxkPW8obSkudXRjT2Zmc2V0KGMpO3JldHVybiBkLiR4LiR0aW1lem9uZT1hLGR9LG8udHouZ3Vlc3M9ZnVuY3Rpb24oKXtyZXR1cm4gSW50bC5EYXRlVGltZUZvcm1hdCgpLnJlc29sdmVkT3B0aW9ucygpLnRpbWVab25lfSxvLnR6LnNldERlZmF1bHQ9ZnVuY3Rpb24odCl7cj10fX19KSk7IiwgIiFmdW5jdGlvbihlLHQpe1wib2JqZWN0XCI9PXR5cGVvZiBleHBvcnRzJiZcInVuZGVmaW5lZFwiIT10eXBlb2YgbW9kdWxlP21vZHVsZS5leHBvcnRzPXQoKTpcImZ1bmN0aW9uXCI9PXR5cGVvZiBkZWZpbmUmJmRlZmluZS5hbWQ/ZGVmaW5lKHQpOihlPVwidW5kZWZpbmVkXCIhPXR5cGVvZiBnbG9iYWxUaGlzP2dsb2JhbFRoaXM6ZXx8c2VsZikuZGF5anNfcGx1Z2luX2lzb1dlZWs9dCgpfSh0aGlzLChmdW5jdGlvbigpe1widXNlIHN0cmljdFwiO3ZhciBlPVwiZGF5XCI7cmV0dXJuIGZ1bmN0aW9uKHQsaSxzKXt2YXIgYT1mdW5jdGlvbih0KXtyZXR1cm4gdC5hZGQoNC10Lmlzb1dlZWtkYXkoKSxlKX0sZD1pLnByb3RvdHlwZTtkLmlzb1dlZWtZZWFyPWZ1bmN0aW9uKCl7cmV0dXJuIGEodGhpcykueWVhcigpfSxkLmlzb1dlZWs9ZnVuY3Rpb24odCl7aWYoIXRoaXMuJHV0aWxzKCkudSh0KSlyZXR1cm4gdGhpcy5hZGQoNyoodC10aGlzLmlzb1dlZWsoKSksZSk7dmFyIGksZCxuLG8scj1hKHRoaXMpLHU9KGk9dGhpcy5pc29XZWVrWWVhcigpLGQ9dGhpcy4kdSxuPShkP3MudXRjOnMpKCkueWVhcihpKS5zdGFydE9mKFwieWVhclwiKSxvPTQtbi5pc29XZWVrZGF5KCksbi5pc29XZWVrZGF5KCk+NCYmKG8rPTcpLG4uYWRkKG8sZSkpO3JldHVybiByLmRpZmYodSxcIndlZWtcIikrMX0sZC5pc29XZWVrZGF5PWZ1bmN0aW9uKGUpe3JldHVybiB0aGlzLiR1dGlscygpLnUoZSk/dGhpcy5kYXkoKXx8Nzp0aGlzLmRheSh0aGlzLmRheSgpJTc/ZTplLTcpfTt2YXIgbj1kLnN0YXJ0T2Y7ZC5zdGFydE9mPWZ1bmN0aW9uKGUsdCl7dmFyIGk9dGhpcy4kdXRpbHMoKSxzPSEhaS51KHQpfHx0O3JldHVyblwiaXNvd2Vla1wiPT09aS5wKGUpP3M/dGhpcy5kYXRlKHRoaXMuZGF0ZSgpLSh0aGlzLmlzb1dlZWtkYXkoKS0xKSkuc3RhcnRPZihcImRheVwiKTp0aGlzLmRhdGUodGhpcy5kYXRlKCktMS0odGhpcy5pc29XZWVrZGF5KCktMSkrNykuZW5kT2YoXCJkYXlcIik6bi5iaW5kKHRoaXMpKGUsdCl9fX0pKTsiLCAibGV0IHRva2VuQ291bnRlciA9IDA7XG4vKipcbiAqIENyZWF0ZXMgYSBuZXcgdW5pcXVlIHRva2VuIGZvciBkZXBlbmRlbmN5IGluamVjdGlvbi5cbiAqXG4gKiBAcGFyYW0gZGVzY3JpcHRpb24gT3B0aW9uYWwgZGVzY3JpcHRpb24gZm9yIGRlYnVnZ2luZyBwdXJwb3Nlc1xuICogQHJldHVybnMgQSB1bmlxdWUgdG9rZW4gdGhhdCBjYW4gYmUgdXNlZCBhcyBhIE1hcCBrZXlcbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHNcbiAqIGludGVyZmFjZSBJTG9nZ2VyIHsgbG9nKG1zZzogc3RyaW5nKTogdm9pZCB9XG4gKiBjb25zdCBMb2dnZXJUb2tlbiA9IFRva2VuPElMb2dnZXI+KCdMb2dnZXInKVxuICogYGBgXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBUb2tlbihkZXNjcmlwdGlvbikge1xuICAgIGNvbnN0IGlkID0gKyt0b2tlbkNvdW50ZXI7XG4gICAgY29uc3Qgc3ltID0gU3ltYm9sKGRlc2NyaXB0aW9uID8gYFRva2VuKCR7ZGVzY3JpcHRpb259KWAgOiBgVG9rZW4jJHtpZH1gKTtcbiAgICBjb25zdCB0b2tlbiA9IHtcbiAgICAgICAgc3ltYm9sOiBzeW0sXG4gICAgICAgIGRlc2NyaXB0aW9uLFxuICAgICAgICB0b1N0cmluZygpIHtcbiAgICAgICAgICAgIHJldHVybiBkZXNjcmlwdGlvblxuICAgICAgICAgICAgICAgID8gYFRva2VuPCR7ZGVzY3JpcHRpb259PmBcbiAgICAgICAgICAgICAgICA6IGBUb2tlbjwjJHtpZH0+YDtcbiAgICAgICAgfVxuICAgIH07XG4gICAgcmV0dXJuIHRva2VuO1xufVxuLyoqXG4gKiBDcmVhdGVzIGEgbmV3IHVuaXF1ZSB0b2tlbiB3aXRob3V0IGEgc3RyaW5nIGxpdGVyYWwuXG4gKiBQcmVmZXJyZWQgZm9yIEF1dG9mYWMtc3R5bGUgREkgdG8gYXZvaWQgc3RyaW5nIGxpdGVyYWxzLlxuICpcbiAqIEByZXR1cm5zIEEgdW5pcXVlIHRva2VuIHRoYXQgY2FuIGJlIHVzZWQgYXMgYSBNYXAga2V5XG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHRzXG4gKiBpbnRlcmZhY2UgSUxvZ2dlciB7IGxvZyhtc2c6IHN0cmluZyk6IHZvaWQgfVxuICogY29uc3QgTG9nZ2VyVG9rZW4gPSB0b2tlbjxJTG9nZ2VyPigpXG4gKiBgYGBcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHRva2VuKCkge1xuICAgIHJldHVybiBUb2tlbigpO1xufVxuIiwgIi8qKlxuICogRXJyb3IgY2xhc3NlcyBmb3IgTm92YURJIGNvbnRhaW5lclxuICovXG5leHBvcnQgY2xhc3MgQ29udGFpbmVyRXJyb3IgZXh0ZW5kcyBFcnJvciB7XG4gICAgY29uc3RydWN0b3IobWVzc2FnZSkge1xuICAgICAgICBzdXBlcihtZXNzYWdlKTtcbiAgICAgICAgdGhpcy5uYW1lID0gJ0NvbnRhaW5lckVycm9yJztcbiAgICB9XG59XG5leHBvcnQgY2xhc3MgQmluZGluZ05vdEZvdW5kRXJyb3IgZXh0ZW5kcyBDb250YWluZXJFcnJvciB7XG4gICAgY29uc3RydWN0b3IodG9rZW5EZXNjcmlwdGlvbiwgcGF0aCA9IFtdKSB7XG4gICAgICAgIGNvbnN0IHBhdGhTdHIgPSBwYXRoLmxlbmd0aCA+IDAgPyBgXFxuICBEZXBlbmRlbmN5IHBhdGg6ICR7cGF0aC5qb2luKCcgLT4gJyl9YCA6ICcnO1xuICAgICAgICBzdXBlcihgVG9rZW4gXCIke3Rva2VuRGVzY3JpcHRpb259XCIgaXMgbm90IGJvdW5kIG9yIHJlZ2lzdGVyZWQgaW4gdGhlIGNvbnRhaW5lci4ke3BhdGhTdHJ9YCk7XG4gICAgICAgIHRoaXMubmFtZSA9ICdCaW5kaW5nTm90Rm91bmRFcnJvcic7XG4gICAgfVxufVxuZXhwb3J0IGNsYXNzIENpcmN1bGFyRGVwZW5kZW5jeUVycm9yIGV4dGVuZHMgQ29udGFpbmVyRXJyb3Ige1xuICAgIGNvbnN0cnVjdG9yKHBhdGgpIHtcbiAgICAgICAgc3VwZXIoYENpcmN1bGFyIGRlcGVuZGVuY3kgZGV0ZWN0ZWQ6ICR7cGF0aC5qb2luKCcgLT4gJyl9YCk7XG4gICAgICAgIHRoaXMubmFtZSA9ICdDaXJjdWxhckRlcGVuZGVuY3lFcnJvcic7XG4gICAgfVxufVxuIiwgIi8qKlxuICogQXV0b1dpcmUgLSBBdXRvbWF0aWMgZGVwZW5kZW5jeSBpbmplY3Rpb24gZm9yIE5vdmFESVxuICogU3VwcG9ydHMgdHdvIHN0cmF0ZWdpZXM6IG1hcFJlc29sdmVycyAodHJhbnNmb3JtZXItZ2VuZXJhdGVkKSBhbmQgbWFwIChtYW51YWwgb3ZlcnJpZGUpXG4gKi9cbi8qKlxuICogUGVyZm9ybWFuY2U6IENhY2hlIGV4dHJhY3RlZCBwYXJhbWV0ZXIgbmFtZXMgdG8gYXZvaWQgcmVwZWF0ZWQgcmVnZXggcGFyc2luZ1xuICogV2Vha01hcCBhbGxvd3MgZ2FyYmFnZSBjb2xsZWN0aW9uIHdoZW4gY29uc3RydWN0b3IgaXMgbm8gbG9uZ2VyIHJlZmVyZW5jZWRcbiAqL1xuY29uc3QgcGFyYW1OYW1lQ2FjaGUgPSBuZXcgV2Vha01hcCgpO1xuLyoqXG4gKiBFeHRyYWN0IHBhcmFtZXRlciBuYW1lcyBmcm9tIGEgY29uc3RydWN0b3IgZnVuY3Rpb25cbiAqIFVzZXMgcmVnZXggdG8gcGFyc2UgdGhlIHRvU3RyaW5nKCkgcmVwcmVzZW50YXRpb25cbiAqIFBlcmZvcm1hbmNlIG9wdGltaXplZDogUmVzdWx0cyBhcmUgY2FjaGVkIHBlciBjb25zdHJ1Y3RvclxuICpcbiAqIE5vdGU6IE9ubHkgdXNlZCBieSByZXNvbHZlQnlNYXAoKSBmb3IgbWFudWFsIG1hcCBzdHJhdGVneVxuICovXG5leHBvcnQgZnVuY3Rpb24gZXh0cmFjdFBhcmFtZXRlck5hbWVzKGNvbnN0cnVjdG9yKSB7XG4gICAgLy8gQ2hlY2sgY2FjaGUgZmlyc3QgLSBhdm9pZHMgZXhwZW5zaXZlIHJlZ2V4IHBhcnNpbmdcbiAgICBjb25zdCBjYWNoZWQgPSBwYXJhbU5hbWVDYWNoZS5nZXQoY29uc3RydWN0b3IpO1xuICAgIGlmIChjYWNoZWQpIHtcbiAgICAgICAgcmV0dXJuIGNhY2hlZDtcbiAgICB9XG4gICAgLy8gRXh0cmFjdCBwYXJhbWV0ZXIgbmFtZXMgKGV4cGVuc2l2ZSBvcGVyYXRpb24pXG4gICAgY29uc3QgZm5TdHIgPSBjb25zdHJ1Y3Rvci50b1N0cmluZygpO1xuICAgIC8vIE1hdGNoIGNvbnN0cnVjdG9yKC4uLmFyZ3MpIG9yIGNsYXNzIHsgY29uc3RydWN0b3IoLi4uYXJncykgfVxuICAgIGNvbnN0IG1hdGNoID0gZm5TdHIubWF0Y2goL2NvbnN0cnVjdG9yXFxzKlxcKChbXildKilcXCkvKSB8fCBmblN0ci5tYXRjaCgvXlteKF0qXFwoKFteKV0qKVxcKS8pO1xuICAgIGlmICghbWF0Y2ggfHwgIW1hdGNoWzFdKSB7XG4gICAgICAgIHJldHVybiBbXTtcbiAgICB9XG4gICAgY29uc3QgcGFyYW1zID0gbWF0Y2hbMV1cbiAgICAgICAgLnNwbGl0KCcsJylcbiAgICAgICAgLm1hcChwYXJhbSA9PiBwYXJhbS50cmltKCkpXG4gICAgICAgIC5maWx0ZXIocGFyYW0gPT4gcGFyYW0ubGVuZ3RoID4gMClcbiAgICAgICAgLm1hcChwYXJhbSA9PiB7XG4gICAgICAgIC8vIFJlbW92ZSBkZWZhdWx0IHZhbHVlcywgdHlwZSBhbm5vdGF0aW9ucywgYW5kIGV4dHJhY3QganVzdCB0aGUgbmFtZVxuICAgICAgICBsZXQgbmFtZSA9IHBhcmFtLnNwbGl0KC9bOj1dLylbMF0udHJpbSgpO1xuICAgICAgICAvLyBSZW1vdmUgVHlwZVNjcmlwdCBtb2RpZmllcnMgKHB1YmxpYywgcHJpdmF0ZSwgcHJvdGVjdGVkLCByZWFkb25seSlcbiAgICAgICAgLy8gQ2FuIGFwcGVhciBtdWx0aXBsZSB0aW1lcywgZS5nLiwgXCJwdWJsaWMgcmVhZG9ubHkgc2VydmljZVwiXG4gICAgICAgIG5hbWUgPSBuYW1lLnJlcGxhY2UoL14oKHB1YmxpY3xwcml2YXRlfHByb3RlY3RlZHxyZWFkb25seSlcXHMrKSsvLCAnJyk7XG4gICAgICAgIC8vIEhhbmRsZSBkZXN0cnVjdHVyaW5nIC0gc2tpcCBmb3Igbm93XG4gICAgICAgIGlmIChuYW1lLmluY2x1ZGVzKCd7JykgfHwgbmFtZS5pbmNsdWRlcygnWycpKSB7XG4gICAgICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gbmFtZTtcbiAgICB9KVxuICAgICAgICAuZmlsdGVyKChuYW1lKSA9PiBuYW1lICE9PSBudWxsKTtcbiAgICAvLyBDYWNoZSByZXN1bHQgZm9yIGZ1dHVyZSBjYWxsc1xuICAgIHBhcmFtTmFtZUNhY2hlLnNldChjb25zdHJ1Y3RvciwgcGFyYW1zKTtcbiAgICByZXR1cm4gcGFyYW1zO1xufVxuLyoqXG4gKiBSZXNvbHZlIGRlcGVuZGVuY2llcyB1c2luZyBtYXAgc3RyYXRlZ3lcbiAqIFVzZXMgZXhwbGljaXQgbWFwcGluZyBmcm9tIHBhcmFtZXRlciBuYW1lcyB0byByZXNvbHZlcnNcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlc29sdmVCeU1hcChjb25zdHJ1Y3RvciwgY29udGFpbmVyLCBvcHRpb25zKSB7XG4gICAgaWYgKCFvcHRpb25zLm1hcCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0F1dG9XaXJlIG1hcCBzdHJhdGVneSByZXF1aXJlcyBvcHRpb25zLm1hcCB0byBiZSBkZWZpbmVkJyk7XG4gICAgfVxuICAgIGNvbnN0IHBhcmFtTmFtZXMgPSBleHRyYWN0UGFyYW1ldGVyTmFtZXMoY29uc3RydWN0b3IpO1xuICAgIGNvbnN0IHJlc29sdmVkRGVwcyA9IFtdO1xuICAgIGZvciAoY29uc3QgcGFyYW1OYW1lIG9mIHBhcmFtTmFtZXMpIHtcbiAgICAgICAgY29uc3QgcmVzb2x2ZXIgPSBvcHRpb25zLm1hcFtwYXJhbU5hbWVdO1xuICAgICAgICBpZiAocmVzb2x2ZXIgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgaWYgKG9wdGlvbnMuc3RyaWN0KSB7XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBDYW5ub3QgcmVzb2x2ZSBwYXJhbWV0ZXIgXCIke3BhcmFtTmFtZX1cIiBvbiAke2NvbnN0cnVjdG9yLm5hbWV9LiBgICtcbiAgICAgICAgICAgICAgICAgICAgYE5vdCBmb3VuZCBpbiBhdXRvd2lyZSBtYXAuIGAgK1xuICAgICAgICAgICAgICAgICAgICBgQWRkIGl0IHRvIHRoZSBtYXA6IC5hdXRvV2lyZSh7IG1hcDogeyAke3BhcmFtTmFtZX06IC4uLiB9IH0pYCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgICAvLyBTaWxlbnRseSBwdXNoIHVuZGVmaW5lZCBmb3IgbWlzc2luZyBwYXJhbWV0ZXJzXG4gICAgICAgICAgICAgICAgLy8gVGhpcyBpcyBleHBlY3RlZDogdHJhbnNmb3JtZXIgZmlsdGVycyBvdXQgcHJpbWl0aXZlIHR5cGVzIGF0IGNvbXBpbGUtdGltZSxcbiAgICAgICAgICAgICAgICAvLyBzbyBtaXNzaW5nIHBhcmFtcyBhcmUgdHlwaWNhbGx5IHByaW1pdGl2ZXMgdGhhdCBkb24ndCBuZWVkIERJIHJlc29sdXRpb25cbiAgICAgICAgICAgICAgICByZXNvbHZlZERlcHMucHVzaCh1bmRlZmluZWQpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cbiAgICAgICAgLy8gUmVzb2x2ZXIgY2FuIGJlIGEgZnVuY3Rpb24gb3IgYSBUb2tlblxuICAgICAgICBpZiAodHlwZW9mIHJlc29sdmVyID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgICAgICByZXNvbHZlZERlcHMucHVzaChyZXNvbHZlcihjb250YWluZXIpKTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIC8vIEFzc3VtZSBpdCdzIGEgVG9rZW5cbiAgICAgICAgICAgIHJlc29sdmVkRGVwcy5wdXNoKGNvbnRhaW5lci5yZXNvbHZlKHJlc29sdmVyKSk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHJlc29sdmVkRGVwcztcbn1cbi8qKlxuICogUmVzb2x2ZSBkZXBlbmRlbmNpZXMgdXNpbmcgbWFwUmVzb2x2ZXJzIGFycmF5IHN0cmF0ZWd5XG4gKiBPUFRJTUFMIFBFUkZPUk1BTkNFOiBPKDEpIGFycmF5IGFjY2VzcyBwZXIgcGFyYW1ldGVyXG4gKiBNaW5pZmljYXRpb24tc2FmZTogVXNlcyBwb3NpdGlvbi1iYXNlZCBhcnJheVxuICogUmVmYWN0b3JpbmctZnJpZW5kbHk6IFRyYW5zZm9ybWVyIHJlZ2VuZXJhdGVzIGFycmF5IG9uIHJlY29tcGlsZVxuICpcbiAqIFJlcXVpcmVzIGJ1aWxkLXRpbWUgdHJhbnNmb3JtZXIgdG8gZ2VuZXJhdGUgbWFwUmVzb2x2ZXJzIGFycmF5XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZXNvbHZlQnlNYXBSZXNvbHZlcnMoX2NvbnN0cnVjdG9yLCBjb250YWluZXIsIG9wdGlvbnMpIHtcbiAgICBpZiAoIW9wdGlvbnMubWFwUmVzb2x2ZXJzIHx8IG9wdGlvbnMubWFwUmVzb2x2ZXJzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICByZXR1cm4gW107XG4gICAgfVxuICAgIGNvbnN0IHJlc29sdmVkRGVwcyA9IFtdO1xuICAgIC8vIFNpbXBsZSBPKDEpIGFycmF5IGFjY2VzcyAtIHVsdHJhIGZhc3QhXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBvcHRpb25zLm1hcFJlc29sdmVycy5sZW5ndGg7IGkrKykge1xuICAgICAgICBjb25zdCByZXNvbHZlciA9IG9wdGlvbnMubWFwUmVzb2x2ZXJzW2ldO1xuICAgICAgICBpZiAocmVzb2x2ZXIgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgLy8gdW5kZWZpbmVkIGluZGljYXRlcyBwcmltaXRpdmUgdHlwZSBvciBwYXJhbWV0ZXIgd2l0aG91dCBESVxuICAgICAgICAgICAgcmVzb2x2ZWREZXBzLnB1c2godW5kZWZpbmVkKTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIGlmICh0eXBlb2YgcmVzb2x2ZXIgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgICAgIC8vIFJlc29sdmVyIGZ1bmN0aW9uOiAoYykgPT4gYy5yZXNvbHZlVHlwZSguLi4pXG4gICAgICAgICAgICByZXNvbHZlZERlcHMucHVzaChyZXNvbHZlcihjb250YWluZXIpKTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIC8vIFRva2VuLWJhc2VkIHJlc29sdXRpb25cbiAgICAgICAgICAgIHJlc29sdmVkRGVwcy5wdXNoKGNvbnRhaW5lci5yZXNvbHZlKHJlc29sdmVyKSk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHJlc29sdmVkRGVwcztcbn1cbi8qKlxuICogTWFpbiBhdXRvd2lyZSBmdW5jdGlvbiAtIGRpc3BhdGNoZXMgdG8gYXBwcm9wcmlhdGUgc3RyYXRlZ3lcbiAqIFByaW9yaXR5OiBtYXBSZXNvbHZlcnMgKHRyYW5zZm9ybWVyLWdlbmVyYXRlZCkgPiBtYXAgKG1hbnVhbCBvdmVycmlkZSlcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGF1dG93aXJlKGNvbnN0cnVjdG9yLCBjb250YWluZXIsIG9wdGlvbnMpIHtcbiAgICBjb25zdCBvcHRzID0ge1xuICAgICAgICBieTogJ3BhcmFtTmFtZScsXG4gICAgICAgIHN0cmljdDogZmFsc2UsXG4gICAgICAgIC4uLm9wdGlvbnNcbiAgICB9O1xuICAgIC8vIEhJR0hFU1QgUFJJT1JJVFk6IG1hcFJlc29sdmVycyBhcnJheSAodHJhbnNmb3JtZXItZ2VuZXJhdGVkLCBvcHRpbWFsIHBlcmZvcm1hbmNlKVxuICAgIC8vIE8oMSkgYXJyYXkgYWNjZXNzIHBlciBwYXJhbWV0ZXIgLSBtaW5pZmljYXRpb24tc2FmZSBhbmQgcmVmYWN0b3JpbmctZnJpZW5kbHlcbiAgICBpZiAob3B0cy5tYXBSZXNvbHZlcnMgJiYgb3B0cy5tYXBSZXNvbHZlcnMubGVuZ3RoID4gMCkge1xuICAgICAgICByZXR1cm4gcmVzb2x2ZUJ5TWFwUmVzb2x2ZXJzKGNvbnN0cnVjdG9yLCBjb250YWluZXIsIG9wdHMpO1xuICAgIH1cbiAgICAvLyBGQUxMQkFDSzogTWFudWFsIG1hcCBzdHJhdGVneSBmb3IgZXhwbGljaXQgb3ZlcnJpZGVzXG4gICAgaWYgKG9wdHMubWFwICYmIE9iamVjdC5rZXlzKG9wdHMubWFwKS5sZW5ndGggPiAwKSB7XG4gICAgICAgIHJldHVybiByZXNvbHZlQnlNYXAoY29uc3RydWN0b3IsIGNvbnRhaW5lciwgb3B0cyk7XG4gICAgfVxuICAgIC8vIE5vIGF1dG93aXJpbmcgY29uZmlndXJlZCwgcmV0dXJuIGVtcHR5IGFycmF5XG4gICAgcmV0dXJuIFtdO1xufVxuIiwgIi8qKlxuICogRmx1ZW50IGJ1aWxkZXIgQVBJIGZvciBOb3ZhREkgQ29udGFpbmVyIChBdXRvZmFjLXN0eWxlKVxuICovXG5pbXBvcnQgeyBUb2tlbiB9IGZyb20gJy4vdG9rZW4uanMnO1xuaW1wb3J0IHsgYXV0b3dpcmUgfSBmcm9tICcuL2F1dG93aXJlLmpzJztcbi8qKlxuICogRmx1ZW50IHJlZ2lzdHJhdGlvbiBidWlsZGVyIHJldHVybmVkIGFmdGVyIGVhY2ggcmVnaXN0cmF0aW9uIG1ldGhvZFxuICovXG5leHBvcnQgY2xhc3MgUmVnaXN0cmF0aW9uQnVpbGRlciB7XG4gICAgY29uc3RydWN0b3IocGVuZGluZywgcmVnaXN0cmF0aW9ucykge1xuICAgICAgICB0aGlzLnJlZ2lzdHJhdGlvbnMgPSByZWdpc3RyYXRpb25zO1xuICAgICAgICB0aGlzLmNvbmZpZ3MgPSBbXTtcbiAgICAgICAgdGhpcy5kZWZhdWx0TGlmZXRpbWUgPSAnc2luZ2xldG9uJztcbiAgICAgICAgdGhpcy5wZW5kaW5nID0gcGVuZGluZztcbiAgICB9XG4gICAgLyoqXG4gICAgICogQmluZCB0aGlzIHJlZ2lzdHJhdGlvbiB0byBhIHRva2VuIG9yIGludGVyZmFjZSB0eXBlXG4gICAgICpcbiAgICAgKiBAb3ZlcmxvYWRcbiAgICAgKiBAcGFyYW0ge1Rva2VuPFU+fSB0b2tlbiAtIEV4cGxpY2l0IHRva2VuIGZvciBiaW5kaW5nXG4gICAgICpcbiAgICAgKiBAb3ZlcmxvYWRcbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gdHlwZU5hbWUgLSBJbnRlcmZhY2UgdHlwZSBuYW1lIChhdXRvLWdlbmVyYXRlZCBieSB0cmFuc2Zvcm1lcilcbiAgICAgKi9cbiAgICBhcyh0b2tlbk9yVHlwZU5hbWUpIHtcbiAgICAgICAgLy8gQ2hlY2sgaWYgYXJndW1lbnQgaXMgYSBUb2tlbiBvYmplY3QgKGhhcyBzeW1ib2wgcHJvcGVydHkpXG4gICAgICAgIGlmICh0b2tlbk9yVHlwZU5hbWUgJiYgdHlwZW9mIHRva2VuT3JUeXBlTmFtZSA9PT0gJ29iamVjdCcgJiYgJ3N5bWJvbCcgaW4gdG9rZW5PclR5cGVOYW1lKSB7XG4gICAgICAgICAgICAvLyBUb2tlbi1iYXNlZCByZWdpc3RyYXRpb25cbiAgICAgICAgICAgIGNvbnN0IGNvbmZpZyA9IHtcbiAgICAgICAgICAgICAgICB0b2tlbjogdG9rZW5PclR5cGVOYW1lLFxuICAgICAgICAgICAgICAgIHR5cGU6IHRoaXMucGVuZGluZy50eXBlLFxuICAgICAgICAgICAgICAgIHZhbHVlOiB0aGlzLnBlbmRpbmcudmFsdWUsXG4gICAgICAgICAgICAgICAgZmFjdG9yeTogdGhpcy5wZW5kaW5nLmZhY3RvcnksXG4gICAgICAgICAgICAgICAgY29uc3RydWN0b3I6IHRoaXMucGVuZGluZy5jb25zdHJ1Y3RvcixcbiAgICAgICAgICAgICAgICBsaWZldGltZTogdGhpcy5kZWZhdWx0TGlmZXRpbWVcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICB0aGlzLmNvbmZpZ3MucHVzaChjb25maWcpO1xuICAgICAgICAgICAgdGhpcy5yZWdpc3RyYXRpb25zLnB1c2goY29uZmlnKTtcbiAgICAgICAgICAgIHJldHVybiB0aGlzO1xuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgLy8gSW50ZXJmYWNlLWJhc2VkIHJlZ2lzdHJhdGlvbiAodHlwZU5hbWUgc3RyaW5nIG9yIHVuZGVmaW5lZClcbiAgICAgICAgICAgIGNvbnN0IGNvbmZpZyA9IHtcbiAgICAgICAgICAgICAgICB0b2tlbjogbnVsbCwgLy8gV2lsbCBiZSBzZXQgZHVyaW5nIGJ1aWxkKClcbiAgICAgICAgICAgICAgICB0eXBlOiB0aGlzLnBlbmRpbmcudHlwZSxcbiAgICAgICAgICAgICAgICB2YWx1ZTogdGhpcy5wZW5kaW5nLnZhbHVlLFxuICAgICAgICAgICAgICAgIGZhY3Rvcnk6IHRoaXMucGVuZGluZy5mYWN0b3J5LFxuICAgICAgICAgICAgICAgIGNvbnN0cnVjdG9yOiB0aGlzLnBlbmRpbmcuY29uc3RydWN0b3IsXG4gICAgICAgICAgICAgICAgbGlmZXRpbWU6IHRoaXMuZGVmYXVsdExpZmV0aW1lLFxuICAgICAgICAgICAgICAgIGludGVyZmFjZVR5cGU6IHRva2VuT3JUeXBlTmFtZVxuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIHRoaXMuY29uZmlncy5wdXNoKGNvbmZpZyk7XG4gICAgICAgICAgICB0aGlzLnJlZ2lzdHJhdGlvbnMucHVzaChjb25maWcpO1xuICAgICAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgICAgIH1cbiAgICB9XG4gICAgLyoqXG4gICAgICogUmVnaXN0ZXIgYXMgZGVmYXVsdCBpbXBsZW1lbnRhdGlvbiBmb3IgYW4gaW50ZXJmYWNlXG4gICAgICogQ29tYmluZXMgYXMoKSArIGFzRGVmYXVsdCgpXG4gICAgICovXG4gICAgYXNEZWZhdWx0SW50ZXJmYWNlKHR5cGVOYW1lKSB7XG4gICAgICAgIHRoaXMuYXMoXCJUSW50ZXJmYWNlXCIsIHR5cGVOYW1lKTtcbiAgICAgICAgcmV0dXJuIHRoaXMuYXNEZWZhdWx0KCk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFJlZ2lzdGVyIGFzIGEga2V5ZWQgaW50ZXJmYWNlIGltcGxlbWVudGF0aW9uXG4gICAgICogQ29tYmluZXMgYXMoKSArIGtleWVkKClcbiAgICAgKi9cbiAgICBhc0tleWVkSW50ZXJmYWNlKGtleSwgdHlwZU5hbWUpIHtcbiAgICAgICAgdGhpcy5hcyhcIlRJbnRlcmZhY2VcIiwgdHlwZU5hbWUpO1xuICAgICAgICByZXR1cm4gdGhpcy5rZXllZChrZXkpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBSZWdpc3RlciBhcyBtdWx0aXBsZSBpbXBsZW1lbnRlZCBpbnRlcmZhY2VzXG4gICAgICovXG4gICAgYXNJbXBsZW1lbnRlZEludGVyZmFjZXModG9rZW5zKSB7XG4gICAgICAgIGlmICh0b2tlbnMubGVuZ3RoID09PSAwKSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcztcbiAgICAgICAgfVxuICAgICAgICAvLyBJZiB0aGVyZSBhcmUgZXhpc3RpbmcgY29uZmlncyAoZnJvbSBwcmV2aW91cyBhcygpIGNhbGxzKSwgYWRkIHRoZXNlIGFzIGFkZGl0aW9uYWwgaW50ZXJmYWNlc1xuICAgICAgICBpZiAodGhpcy5jb25maWdzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgIC8vIEFkZCBhbGwgdG9rZW5zIGFzIGFkZGl0aW9uYWwgaW50ZXJmYWNlcyB0byBleGlzdGluZyBjb25maWdzXG4gICAgICAgICAgICBmb3IgKGNvbnN0IGNvbmZpZyBvZiB0aGlzLmNvbmZpZ3MpIHtcbiAgICAgICAgICAgICAgICBjb25maWcubGlmZXRpbWUgPSAnc2luZ2xldG9uJzsgLy8gYXNJbXBsZW1lbnRlZEludGVyZmFjZXMgZGVmYXVsdHMgdG8gc2luZ2xldG9uXG4gICAgICAgICAgICAgICAgY29uZmlnLmFkZGl0aW9uYWxUb2tlbnMgPSBjb25maWcuYWRkaXRpb25hbFRva2VucyB8fCBbXTtcbiAgICAgICAgICAgICAgICBjb25maWcuYWRkaXRpb25hbFRva2Vucy5wdXNoKC4uLnRva2Vucyk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gdGhpcztcbiAgICAgICAgfVxuICAgICAgICAvLyBObyBleGlzdGluZyBjb25maWdzLCBjcmVhdGUgbmV3IG9uZSB3aXRoIGZpcnN0IHRva2VuXG4gICAgICAgIGNvbnN0IGZpcnN0Q29uZmlnID0ge1xuICAgICAgICAgICAgdG9rZW46IHRva2Vuc1swXSxcbiAgICAgICAgICAgIHR5cGU6IHRoaXMucGVuZGluZy50eXBlLFxuICAgICAgICAgICAgdmFsdWU6IHRoaXMucGVuZGluZy52YWx1ZSxcbiAgICAgICAgICAgIGZhY3Rvcnk6IHRoaXMucGVuZGluZy5mYWN0b3J5LFxuICAgICAgICAgICAgY29uc3RydWN0b3I6IHRoaXMucGVuZGluZy5jb25zdHJ1Y3RvcixcbiAgICAgICAgICAgIGxpZmV0aW1lOiAnc2luZ2xldG9uJ1xuICAgICAgICB9O1xuICAgICAgICB0aGlzLmNvbmZpZ3MucHVzaChmaXJzdENvbmZpZyk7XG4gICAgICAgIHRoaXMucmVnaXN0cmF0aW9ucy5wdXNoKGZpcnN0Q29uZmlnKTtcbiAgICAgICAgLy8gQWRkaXRpb25hbCB0b2tlbnMgcmVmZXJlbmNlIHRoZSBzYW1lIHJlZ2lzdHJhdGlvblxuICAgICAgICBmb3IgKGxldCBpID0gMTsgaSA8IHRva2Vucy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgZmlyc3RDb25maWcuYWRkaXRpb25hbFRva2VucyA9IGZpcnN0Q29uZmlnLmFkZGl0aW9uYWxUb2tlbnMgfHwgW107XG4gICAgICAgICAgICBmaXJzdENvbmZpZy5hZGRpdGlvbmFsVG9rZW5zLnB1c2godG9rZW5zW2ldKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9XG4gICAgLyoqXG4gICAgICogU2V0IHNpbmdsZXRvbiBsaWZldGltZSAob25lIGluc3RhbmNlIGZvciBlbnRpcmUgY29udGFpbmVyKVxuICAgICAqL1xuICAgIHNpbmdsZUluc3RhbmNlKCkge1xuICAgICAgICBmb3IgKGNvbnN0IGNvbmZpZyBvZiB0aGlzLmNvbmZpZ3MpIHtcbiAgICAgICAgICAgIGNvbmZpZy5saWZldGltZSA9ICdzaW5nbGV0b24nO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBTZXQgcGVyLXJlcXVlc3QgbGlmZXRpbWUgKG9uZSBpbnN0YW5jZSBwZXIgcmVzb2x2ZSBjYWxsIHRyZWUpXG4gICAgICovXG4gICAgaW5zdGFuY2VQZXJSZXF1ZXN0KCkge1xuICAgICAgICBmb3IgKGNvbnN0IGNvbmZpZyBvZiB0aGlzLmNvbmZpZ3MpIHtcbiAgICAgICAgICAgIGNvbmZpZy5saWZldGltZSA9ICdwZXItcmVxdWVzdCc7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFNldCB0cmFuc2llbnQgbGlmZXRpbWUgKG5ldyBpbnN0YW5jZSBldmVyeSB0aW1lKVxuICAgICAqIEFsaWFzIGZvciBkZWZhdWx0IGJlaGF2aW9yXG4gICAgICovXG4gICAgaW5zdGFuY2VQZXJEZXBlbmRlbmN5KCkge1xuICAgICAgICBmb3IgKGNvbnN0IGNvbmZpZyBvZiB0aGlzLmNvbmZpZ3MpIHtcbiAgICAgICAgICAgIGNvbmZpZy5saWZldGltZSA9ICd0cmFuc2llbnQnO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBOYW1lIHRoaXMgcmVnaXN0cmF0aW9uIGZvciBuYW1lZCByZXNvbHV0aW9uXG4gICAgICovXG4gICAgbmFtZWQobmFtZSkge1xuICAgICAgICBmb3IgKGNvbnN0IGNvbmZpZyBvZiB0aGlzLmNvbmZpZ3MpIHtcbiAgICAgICAgICAgIGNvbmZpZy5uYW1lID0gbmFtZTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9XG4gICAgLyoqXG4gICAgICogS2V5IHRoaXMgcmVnaXN0cmF0aW9uIGZvciBrZXllZCByZXNvbHV0aW9uXG4gICAgICovXG4gICAga2V5ZWQoa2V5KSB7XG4gICAgICAgIGZvciAoY29uc3QgY29uZmlnIG9mIHRoaXMuY29uZmlncykge1xuICAgICAgICAgICAgY29uZmlnLmtleSA9IGtleTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9XG4gICAgLyoqXG4gICAgICogTWFyayB0aGlzIGFzIGRlZmF1bHQgcmVnaXN0cmF0aW9uXG4gICAgICogRGVmYXVsdCByZWdpc3RyYXRpb25zIGRvbid0IG92ZXJyaWRlIGV4aXN0aW5nIG9uZXNcbiAgICAgKi9cbiAgICBhc0RlZmF1bHQoKSB7XG4gICAgICAgIGZvciAoY29uc3QgY29uZmlnIG9mIHRoaXMuY29uZmlncykge1xuICAgICAgICAgICAgY29uZmlnLmlzRGVmYXVsdCA9IHRydWU7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIE9ubHkgcmVnaXN0ZXIgaWYgdG9rZW4gbm90IGFscmVhZHkgcmVnaXN0ZXJlZFxuICAgICAqL1xuICAgIGlmTm90UmVnaXN0ZXJlZCgpIHtcbiAgICAgICAgZm9yIChjb25zdCBjb25maWcgb2YgdGhpcy5jb25maWdzKSB7XG4gICAgICAgICAgICBjb25maWcuaWZOb3RSZWdpc3RlcmVkID0gdHJ1ZTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9XG4gICAgLyoqXG4gICAgICogU3BlY2lmeSBwYXJhbWV0ZXIgdmFsdWVzIGZvciBjb25zdHJ1Y3RvciAocHJpbWl0aXZlcyBhbmQgY29uc3RhbnRzKVxuICAgICAqIFVzZSB0aGlzIGZvciBub24tREkgcGFyYW1ldGVycyBsaWtlIHN0cmluZ3MsIG51bWJlcnMsIGNvbmZpZyB2YWx1ZXNcbiAgICAgKi9cbiAgICB3aXRoUGFyYW1ldGVycyhwYXJhbWV0ZXJzKSB7XG4gICAgICAgIGZvciAoY29uc3QgY29uZmlnIG9mIHRoaXMuY29uZmlncykge1xuICAgICAgICAgICAgY29uZmlnLnBhcmFtZXRlclZhbHVlcyA9IHBhcmFtZXRlcnM7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEVuYWJsZSBhdXRvbWF0aWMgZGVwZW5kZW5jeSBpbmplY3Rpb24gKGF1dG93aXJpbmcpXG4gICAgICogU3VwcG9ydHMgdGhyZWUgc3RyYXRlZ2llczogcGFyYW1OYW1lIChkZWZhdWx0KSwgbWFwLCBhbmQgY2xhc3NcbiAgICAgKlxuICAgICAqIEBleGFtcGxlXG4gICAgICogYGBgdHNcbiAgICAgKiAvLyBTdHJhdGVneSAxOiBwYXJhbU5hbWUgKGRlZmF1bHQsIHJlcXVpcmVzIG5vbi1taW5pZmllZCBjb2RlIGluIGRldilcbiAgICAgKiBidWlsZGVyLnJlZ2lzdGVyVHlwZShFdmVudEJ1cykuYXM8SUV2ZW50QnVzPigpLmF1dG9XaXJlKClcbiAgICAgKlxuICAgICAqIC8vIFN0cmF0ZWd5IDI6IG1hcCAobWluaWZ5LXNhZmUsIGV4cGxpY2l0KVxuICAgICAqIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKEV2ZW50QnVzKS5hczxJRXZlbnRCdXM+KCkuYXV0b1dpcmUoe1xuICAgICAqICAgbWFwOiB7XG4gICAgICogICAgIGxvZ2dlcjogKGMpID0+IGMucmVzb2x2ZVR5cGU8SUxvZ2dlcj4oKVxuICAgICAqICAgfVxuICAgICAqIH0pXG4gICAgICpcbiAgICAgKiAvLyBTdHJhdGVneSAzOiBjbGFzcyAocmVxdWlyZXMgYnVpbGQtdGltZSBjb2RlZ2VuKVxuICAgICAqIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKEV2ZW50QnVzKS5hczxJRXZlbnRCdXM+KCkuYXV0b1dpcmUoeyBieTogJ2NsYXNzJyB9KVxuICAgICAqIGBgYFxuICAgICAqL1xuICAgIGF1dG9XaXJlKG9wdGlvbnMpIHtcbiAgICAgICAgZm9yIChjb25zdCBjb25maWcgb2YgdGhpcy5jb25maWdzKSB7XG4gICAgICAgICAgICBjb25maWcuYXV0b3dpcmVPcHRpb25zID0gb3B0aW9ucyB8fCB7IGJ5OiAncGFyYW1OYW1lJywgc3RyaWN0OiBmYWxzZSB9O1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH1cbn1cbi8qKlxuICogRmx1ZW50IGJ1aWxkZXIgZm9yIENvbnRhaW5lciBjb25maWd1cmF0aW9uXG4gKi9cbmV4cG9ydCBjbGFzcyBCdWlsZGVyIHtcbiAgICBjb25zdHJ1Y3RvcihiYXNlQ29udGFpbmVyKSB7XG4gICAgICAgIHRoaXMuYmFzZUNvbnRhaW5lciA9IGJhc2VDb250YWluZXI7XG4gICAgICAgIHRoaXMucmVnaXN0cmF0aW9ucyA9IFtdO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBSZWdpc3RlciBhIGNsYXNzIGNvbnN0cnVjdG9yXG4gICAgICovXG4gICAgcmVnaXN0ZXJUeXBlKGNvbnN0cnVjdG9yKSB7XG4gICAgICAgIGNvbnN0IHBlbmRpbmcgPSB7XG4gICAgICAgICAgICB0eXBlOiAndHlwZScsXG4gICAgICAgICAgICB2YWx1ZTogbnVsbCxcbiAgICAgICAgICAgIGNvbnN0cnVjdG9yXG4gICAgICAgIH07XG4gICAgICAgIHJldHVybiBuZXcgUmVnaXN0cmF0aW9uQnVpbGRlcihwZW5kaW5nLCB0aGlzLnJlZ2lzdHJhdGlvbnMpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBSZWdpc3RlciBhIHByZS1jcmVhdGVkIGluc3RhbmNlXG4gICAgICovXG4gICAgcmVnaXN0ZXJJbnN0YW5jZShpbnN0YW5jZSkge1xuICAgICAgICBjb25zdCBwZW5kaW5nID0ge1xuICAgICAgICAgICAgdHlwZTogJ2luc3RhbmNlJyxcbiAgICAgICAgICAgIHZhbHVlOiBpbnN0YW5jZSxcbiAgICAgICAgICAgIGNvbnN0cnVjdG9yOiB1bmRlZmluZWRcbiAgICAgICAgfTtcbiAgICAgICAgcmV0dXJuIG5ldyBSZWdpc3RyYXRpb25CdWlsZGVyKHBlbmRpbmcsIHRoaXMucmVnaXN0cmF0aW9ucyk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFJlZ2lzdGVyIGEgZmFjdG9yeSBmdW5jdGlvblxuICAgICAqL1xuICAgIHJlZ2lzdGVyKGZhY3RvcnkpIHtcbiAgICAgICAgY29uc3QgcGVuZGluZyA9IHtcbiAgICAgICAgICAgIHR5cGU6ICdmYWN0b3J5JyxcbiAgICAgICAgICAgIHZhbHVlOiBudWxsLFxuICAgICAgICAgICAgZmFjdG9yeSxcbiAgICAgICAgICAgIGNvbnN0cnVjdG9yOiB1bmRlZmluZWRcbiAgICAgICAgfTtcbiAgICAgICAgcmV0dXJuIG5ldyBSZWdpc3RyYXRpb25CdWlsZGVyKHBlbmRpbmcsIHRoaXMucmVnaXN0cmF0aW9ucyk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFJlZ2lzdGVyIGEgbW9kdWxlIChmdW5jdGlvbiB0aGF0IGFkZHMgbXVsdGlwbGUgcmVnaXN0cmF0aW9ucylcbiAgICAgKi9cbiAgICBtb2R1bGUobW9kdWxlRnVuYykge1xuICAgICAgICBtb2R1bGVGdW5jKHRoaXMpO1xuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9XG4gICAgLyoqXG4gICAgICogUmVzb2x2ZSBpbnRlcmZhY2UgdHlwZSBuYW1lcyB0byB0b2tlbnNcbiAgICAgKiBAaW50ZXJuYWxcbiAgICAgKi9cbiAgICByZXNvbHZlSW50ZXJmYWNlVG9rZW5zKGNvbnRhaW5lcikge1xuICAgICAgICBmb3IgKGNvbnN0IGNvbmZpZyBvZiB0aGlzLnJlZ2lzdHJhdGlvbnMpIHtcbiAgICAgICAgICAgIGlmIChjb25maWcuaW50ZXJmYWNlVHlwZSAhPT0gdW5kZWZpbmVkICYmICFjb25maWcudG9rZW4pIHtcbiAgICAgICAgICAgICAgICBjb25maWcudG9rZW4gPSBjb250YWluZXIuaW50ZXJmYWNlVG9rZW4oY29uZmlnLmludGVyZmFjZVR5cGUpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuICAgIC8qKlxuICAgICAqIElkZW50aWZ5IHRva2VucyB0aGF0IGhhdmUgbm9uLWRlZmF1bHQgcmVnaXN0cmF0aW9uc1xuICAgICAqIEBpbnRlcm5hbFxuICAgICAqL1xuICAgIGlkZW50aWZ5Tm9uRGVmYXVsdFRva2VucygpIHtcbiAgICAgICAgY29uc3QgdG9rZW5zV2l0aE5vbkRlZmF1bHRzID0gbmV3IFNldCgpO1xuICAgICAgICBmb3IgKGNvbnN0IGNvbmZpZyBvZiB0aGlzLnJlZ2lzdHJhdGlvbnMpIHtcbiAgICAgICAgICAgIGlmICghY29uZmlnLmlzRGVmYXVsdCAmJiAhY29uZmlnLm5hbWUgJiYgY29uZmlnLmtleSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICAgICAgdG9rZW5zV2l0aE5vbkRlZmF1bHRzLmFkZChjb25maWcudG9rZW4pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0b2tlbnNXaXRoTm9uRGVmYXVsdHM7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIENoZWNrIGlmIHJlZ2lzdHJhdGlvbiBzaG91bGQgYmUgc2tpcHBlZFxuICAgICAqIEBpbnRlcm5hbFxuICAgICAqL1xuICAgIHNob3VsZFNraXBSZWdpc3RyYXRpb24oY29uZmlnLCB0b2tlbnNXaXRoTm9uRGVmYXVsdHMsIHJlZ2lzdGVyZWRUb2tlbnMpIHtcbiAgICAgICAgLy8gU2tpcCBkZWZhdWx0IHJlZ2lzdHJhdGlvbnMgaWYgdGhlcmUncyBhIG5vbi1kZWZhdWx0IGZvciB0aGUgc2FtZSB0b2tlblxuICAgICAgICBpZiAoY29uZmlnLmlzRGVmYXVsdCAmJiAhY29uZmlnLm5hbWUgJiYgY29uZmlnLmtleSA9PT0gdW5kZWZpbmVkICYmIHRva2Vuc1dpdGhOb25EZWZhdWx0cy5oYXMoY29uZmlnLnRva2VuKSkge1xuICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgIH1cbiAgICAgICAgLy8gSGFuZGxlIGlmTm90UmVnaXN0ZXJlZFxuICAgICAgICBpZiAoY29uZmlnLmlmTm90UmVnaXN0ZXJlZCAmJiByZWdpc3RlcmVkVG9rZW5zLmhhcyhjb25maWcudG9rZW4pKSB7XG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgfVxuICAgICAgICAvLyBIYW5kbGUgYXNEZWZhdWx0XG4gICAgICAgIGlmIChjb25maWcuaXNEZWZhdWx0ICYmIHJlZ2lzdGVyZWRUb2tlbnMuaGFzKGNvbmZpZy50b2tlbikpIHtcbiAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogQ3JlYXRlIGJpbmRpbmcgdG9rZW4gZm9yIHJlZ2lzdHJhdGlvbiAobmFtZWQsIGtleWVkLCBvciBtdWx0aSlcbiAgICAgKiBAaW50ZXJuYWxcbiAgICAgKi9cbiAgICBjcmVhdGVCaW5kaW5nVG9rZW4oY29uZmlnLCBuYW1lZFJlZ2lzdHJhdGlvbnMsIGtleWVkUmVnaXN0cmF0aW9ucywgbXVsdGlSZWdpc3RyYXRpb25zKSB7XG4gICAgICAgIGlmIChjb25maWcubmFtZSkge1xuICAgICAgICAgICAgLy8gTmFtZWQgcmVnaXN0cmF0aW9uIGdldHMgdW5pcXVlIHRva2VuXG4gICAgICAgICAgICBjb25zdCBiaW5kaW5nVG9rZW4gPSBUb2tlbihgX19uYW1lZF8ke2NvbmZpZy5uYW1lfWApO1xuICAgICAgICAgICAgbmFtZWRSZWdpc3RyYXRpb25zLnNldChjb25maWcubmFtZSwgeyAuLi5jb25maWcsIHRva2VuOiBiaW5kaW5nVG9rZW4gfSk7XG4gICAgICAgICAgICByZXR1cm4gYmluZGluZ1Rva2VuO1xuICAgICAgICB9XG4gICAgICAgIGVsc2UgaWYgKGNvbmZpZy5rZXkgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgLy8gS2V5ZWQgcmVnaXN0cmF0aW9uIGdldHMgdW5pcXVlIHRva2VuXG4gICAgICAgICAgICBjb25zdCBrZXlTdHIgPSB0eXBlb2YgY29uZmlnLmtleSA9PT0gJ3N5bWJvbCcgPyBjb25maWcua2V5LnRvU3RyaW5nKCkgOiBjb25maWcua2V5O1xuICAgICAgICAgICAgY29uc3QgYmluZGluZ1Rva2VuID0gVG9rZW4oYF9fa2V5ZWRfJHtrZXlTdHJ9YCk7XG4gICAgICAgICAgICBrZXllZFJlZ2lzdHJhdGlvbnMuc2V0KGNvbmZpZy5rZXksIHsgLi4uY29uZmlnLCB0b2tlbjogYmluZGluZ1Rva2VuIH0pO1xuICAgICAgICAgICAgcmV0dXJuIGJpbmRpbmdUb2tlbjtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIC8vIE11bHRpLXJlZ2lzdHJhdGlvbiBoYW5kbGluZ1xuICAgICAgICAgICAgaWYgKG11bHRpUmVnaXN0cmF0aW9ucy5oYXMoY29uZmlnLnRva2VuKSkge1xuICAgICAgICAgICAgICAgIC8vIFN1YnNlcXVlbnQgcmVnaXN0cmF0aW9uIGZvciB0aGlzIHRva2VuXG4gICAgICAgICAgICAgICAgY29uc3QgYmluZGluZ1Rva2VuID0gVG9rZW4oYF9fbXVsdGlfJHtjb25maWcudG9rZW4udG9TdHJpbmcoKX1fJHttdWx0aVJlZ2lzdHJhdGlvbnMuZ2V0KGNvbmZpZy50b2tlbikubGVuZ3RofWApO1xuICAgICAgICAgICAgICAgIG11bHRpUmVnaXN0cmF0aW9ucy5nZXQoY29uZmlnLnRva2VuKS5wdXNoKGJpbmRpbmdUb2tlbik7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGJpbmRpbmdUb2tlbjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgICAgIC8vIEZpcnN0IHJlZ2lzdHJhdGlvbiBmb3IgdGhpcyB0b2tlbiwgdXNlIHRoZSBvcmlnaW5hbCB0b2tlblxuICAgICAgICAgICAgICAgIG11bHRpUmVnaXN0cmF0aW9ucy5zZXQoY29uZmlnLnRva2VuLCBbY29uZmlnLnRva2VuXSk7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGNvbmZpZy50b2tlbjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cbiAgICAvKipcbiAgICAgKiBSZWdpc3RlciBhZGRpdGlvbmFsIGludGVyZmFjZXMgZm9yIGEgY29uZmlnXG4gICAgICogQGludGVybmFsXG4gICAgICovXG4gICAgcmVnaXN0ZXJBZGRpdGlvbmFsSW50ZXJmYWNlcyhjb250YWluZXIsIGNvbmZpZywgYmluZGluZ1Rva2VuLCByZWdpc3RlcmVkVG9rZW5zKSB7XG4gICAgICAgIGlmIChjb25maWcuYWRkaXRpb25hbFRva2Vucykge1xuICAgICAgICAgICAgZm9yIChjb25zdCBhZGRpdGlvbmFsVG9rZW4gb2YgY29uZmlnLmFkZGl0aW9uYWxUb2tlbnMpIHtcbiAgICAgICAgICAgICAgICAvLyBDcmVhdGUgYSBmYWN0b3J5IHRoYXQgcmVzb2x2ZXMgdGhlIGJpbmRpbmcgdG9rZW5cbiAgICAgICAgICAgICAgICBjb250YWluZXIuYmluZEZhY3RvcnkoYWRkaXRpb25hbFRva2VuLCAoYykgPT4gYy5yZXNvbHZlKGJpbmRpbmdUb2tlbiksIHsgbGlmZXRpbWU6IGNvbmZpZy5saWZldGltZSB9KTtcbiAgICAgICAgICAgICAgICByZWdpc3RlcmVkVG9rZW5zLmFkZChhZGRpdGlvbmFsVG9rZW4pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEJ1aWxkIHRoZSBjb250YWluZXIgd2l0aCBhbGwgcmVnaXN0ZXJlZCBiaW5kaW5nc1xuICAgICAqL1xuICAgIGJ1aWxkKCkge1xuICAgICAgICAvLyBDcmVhdGUgbmV3IGNvbnRhaW5lciBpbmhlcml0aW5nIGZyb20gYmFzZVxuICAgICAgICBjb25zdCBjb250YWluZXIgPSB0aGlzLmJhc2VDb250YWluZXIuY3JlYXRlQ2hpbGQoKTtcbiAgICAgICAgLy8gUHJlLXByb2Nlc3M6IHJlc29sdmUgaW50ZXJmYWNlIHR5cGVzIHRvIHRva2Vuc1xuICAgICAgICB0aGlzLnJlc29sdmVJbnRlcmZhY2VUb2tlbnMoY29udGFpbmVyKTtcbiAgICAgICAgLy8gVHJhY2sgd2hhdCdzIGJlZW4gcmVnaXN0ZXJlZCBmb3IgaWZOb3RSZWdpc3RlcmVkIGNoZWNrc1xuICAgICAgICBjb25zdCByZWdpc3RlcmVkVG9rZW5zID0gbmV3IFNldCgpO1xuICAgICAgICBjb25zdCBuYW1lZFJlZ2lzdHJhdGlvbnMgPSBuZXcgTWFwKCk7XG4gICAgICAgIGNvbnN0IGtleWVkUmVnaXN0cmF0aW9ucyA9IG5ldyBNYXAoKTtcbiAgICAgICAgY29uc3QgbXVsdGlSZWdpc3RyYXRpb25zID0gbmV3IE1hcCgpO1xuICAgICAgICAvLyBQcmUtcHJvY2VzczogaWRlbnRpZnkgdG9rZW5zIHRoYXQgaGF2ZSBub24tZGVmYXVsdCByZWdpc3RyYXRpb25zXG4gICAgICAgIGNvbnN0IHRva2Vuc1dpdGhOb25EZWZhdWx0cyA9IHRoaXMuaWRlbnRpZnlOb25EZWZhdWx0VG9rZW5zKCk7XG4gICAgICAgIGZvciAoY29uc3QgY29uZmlnIG9mIHRoaXMucmVnaXN0cmF0aW9ucykge1xuICAgICAgICAgICAgLy8gQ2hlY2sgaWYgcmVnaXN0cmF0aW9uIHNob3VsZCBiZSBza2lwcGVkXG4gICAgICAgICAgICBpZiAodGhpcy5zaG91bGRTa2lwUmVnaXN0cmF0aW9uKGNvbmZpZywgdG9rZW5zV2l0aE5vbkRlZmF1bHRzLCByZWdpc3RlcmVkVG9rZW5zKSkge1xuICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgLy8gQ3JlYXRlIGJpbmRpbmcgdG9rZW4gKG5hbWVkLCBrZXllZCwgb3IgbXVsdGkpXG4gICAgICAgICAgICBjb25zdCBiaW5kaW5nVG9rZW4gPSB0aGlzLmNyZWF0ZUJpbmRpbmdUb2tlbihjb25maWcsIG5hbWVkUmVnaXN0cmF0aW9ucywga2V5ZWRSZWdpc3RyYXRpb25zLCBtdWx0aVJlZ2lzdHJhdGlvbnMpO1xuICAgICAgICAgICAgLy8gQXBwbHkgcmVnaXN0cmF0aW9uIHRvIGNvbnRhaW5lciB1c2luZyB0aGUgYmluZGluZyB0b2tlblxuICAgICAgICAgICAgdGhpcy5hcHBseVJlZ2lzdHJhdGlvbihjb250YWluZXIsIHsgLi4uY29uZmlnLCB0b2tlbjogYmluZGluZ1Rva2VuIH0pO1xuICAgICAgICAgICAgLy8gTWFyayBvcmlnaW5hbCB0b2tlbiBhcyByZWdpc3RlcmVkXG4gICAgICAgICAgICByZWdpc3RlcmVkVG9rZW5zLmFkZChjb25maWcudG9rZW4pO1xuICAgICAgICAgICAgLy8gUmVnaXN0ZXIgYWRkaXRpb25hbCBpbnRlcmZhY2VzXG4gICAgICAgICAgICB0aGlzLnJlZ2lzdGVyQWRkaXRpb25hbEludGVyZmFjZXMoY29udGFpbmVyLCBjb25maWcsIGJpbmRpbmdUb2tlbiwgcmVnaXN0ZXJlZFRva2Vucyk7XG4gICAgICAgIH1cbiAgICAgICAgLy8gQXR0YWNoIG1ldGFkYXRhIGZvciBuYW1lZC9rZXllZCByZXNvbHV0aW9uXG4gICAgICAgIDtcbiAgICAgICAgY29udGFpbmVyLl9fbmFtZWRSZWdpc3RyYXRpb25zID0gbmFtZWRSZWdpc3RyYXRpb25zO1xuICAgICAgICBjb250YWluZXIuX19rZXllZFJlZ2lzdHJhdGlvbnMgPSBrZXllZFJlZ2lzdHJhdGlvbnM7XG4gICAgICAgIGNvbnRhaW5lci5fX211bHRpUmVnaXN0cmF0aW9ucyA9IG11bHRpUmVnaXN0cmF0aW9ucztcbiAgICAgICAgcmV0dXJuIGNvbnRhaW5lcjtcbiAgICB9XG4gICAgLyoqXG4gICAgICogQW5hbHl6ZSBjb25zdHJ1Y3RvciB0byBkZXRlY3QgZGVwZW5kZW5jaWVzXG4gICAgICogQGludGVybmFsXG4gICAgICovXG4gICAgYW5hbHl6ZUNvbnN0cnVjdG9yKGNvbnN0cnVjdG9yKSB7XG4gICAgICAgIGNvbnN0IGNvbnN0cnVjdG9yU3RyID0gY29uc3RydWN0b3IudG9TdHJpbmcoKTtcbiAgICAgICAgY29uc3QgaGFzRGVwZW5kZW5jaWVzID0gL2NvbnN0cnVjdG9yXFxzKlxcKFteKV0rXFwpLy50ZXN0KGNvbnN0cnVjdG9yU3RyKTtcbiAgICAgICAgcmV0dXJuIHsgaGFzRGVwZW5kZW5jaWVzIH07XG4gICAgfVxuICAgIC8qKlxuICAgICAqIENyZWF0ZSBvcHRpbWl6ZWQgZmFjdG9yeSBmb3IgemVyby1kZXBlbmRlbmN5IGNvbnN0cnVjdG9yc1xuICAgICAqIEBpbnRlcm5hbFxuICAgICAqL1xuICAgIGNyZWF0ZU9wdGltaXplZEZhY3RvcnkoY29udGFpbmVyLCBjb25maWcsIG9wdGlvbnMpIHtcbiAgICAgICAgaWYgKGNvbmZpZy5saWZldGltZSA9PT0gJ3NpbmdsZXRvbicpIHtcbiAgICAgICAgICAgIC8vIFNpbmdsZXRvbjogQ3JlYXRlIGluc3RhbmNlIGRpcmVjdGx5IChmYXN0ZXN0IHBhdGggLSBubyBmYWN0b3J5IG92ZXJoZWFkKVxuICAgICAgICAgICAgY29uc3QgaW5zdGFuY2UgPSBuZXcgY29uZmlnLmNvbnN0cnVjdG9yKCk7XG4gICAgICAgICAgICBjb250YWluZXIuYmluZFZhbHVlKGNvbmZpZy50b2tlbiwgaW5zdGFuY2UpO1xuICAgICAgICB9XG4gICAgICAgIGVsc2UgaWYgKGNvbmZpZy5saWZldGltZSA9PT0gJ3RyYW5zaWVudCcpIHtcbiAgICAgICAgICAgIC8vIFRyYW5zaWVudCBGYXN0IFBhdGg6IFJlZ2lzdGVyIGluIGZhc3QgdHJhbnNpZW50IGNhY2hlXG4gICAgICAgICAgICBjb25zdCBjdG9yID0gY29uZmlnLmNvbnN0cnVjdG9yO1xuICAgICAgICAgICAgY29uc3QgZmFzdEZhY3RvcnkgPSAoKSA9PiBuZXcgY3RvcigpO1xuICAgICAgICAgICAgY29udGFpbmVyLmZhc3RUcmFuc2llbnRDYWNoZS5zZXQoY29uZmlnLnRva2VuLCBmYXN0RmFjdG9yeSk7XG4gICAgICAgICAgICBjb250YWluZXIuYmluZEZhY3RvcnkoY29uZmlnLnRva2VuLCBmYXN0RmFjdG9yeSwgb3B0aW9ucyk7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAvLyBQZXItcmVxdWVzdDogVXNlIHNpbXBsZSBmYWN0b3J5IHdpdGhvdXQgYXV0b3dpcmUgb3ZlcmhlYWRcbiAgICAgICAgICAgIGNvbnN0IGZhY3RvcnkgPSAoKSA9PiBuZXcgY29uZmlnLmNvbnN0cnVjdG9yKCk7XG4gICAgICAgICAgICBjb250YWluZXIuYmluZEZhY3RvcnkoY29uZmlnLnRva2VuLCBmYWN0b3J5LCBvcHRpb25zKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICAvKipcbiAgICAgKiBDcmVhdGUgYXV0b3dpcmUgZmFjdG9yeVxuICAgICAqIEBpbnRlcm5hbFxuICAgICAqL1xuICAgIGNyZWF0ZUF1dG9XaXJlRmFjdG9yeShjb250YWluZXIsIGNvbmZpZywgb3B0aW9ucykge1xuICAgICAgICBjb25zdCBmYWN0b3J5ID0gKGMpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHJlc29sdmVkRGVwcyA9IGF1dG93aXJlKGNvbmZpZy5jb25zdHJ1Y3RvciwgYywgY29uZmlnLmF1dG93aXJlT3B0aW9ucyk7XG4gICAgICAgICAgICByZXR1cm4gbmV3IGNvbmZpZy5jb25zdHJ1Y3RvciguLi5yZXNvbHZlZERlcHMpO1xuICAgICAgICB9O1xuICAgICAgICBjb250YWluZXIuYmluZEZhY3RvcnkoY29uZmlnLnRva2VuLCBmYWN0b3J5LCBvcHRpb25zKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogQ3JlYXRlIHdpdGhQYXJhbWV0ZXJzIGZhY3RvcnlcbiAgICAgKiBAaW50ZXJuYWxcbiAgICAgKi9cbiAgICBjcmVhdGVQYXJhbWV0ZXJGYWN0b3J5KGNvbnRhaW5lciwgY29uZmlnLCBvcHRpb25zKSB7XG4gICAgICAgIGNvbnN0IGZhY3RvcnkgPSAoKSA9PiB7XG4gICAgICAgICAgICBjb25zdCB2YWx1ZXMgPSBPYmplY3QudmFsdWVzKGNvbmZpZy5wYXJhbWV0ZXJWYWx1ZXMpO1xuICAgICAgICAgICAgcmV0dXJuIG5ldyBjb25maWcuY29uc3RydWN0b3IoLi4udmFsdWVzKTtcbiAgICAgICAgfTtcbiAgICAgICAgY29udGFpbmVyLmJpbmRGYWN0b3J5KGNvbmZpZy50b2tlbiwgZmFjdG9yeSwgb3B0aW9ucyk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEFwcGx5IHR5cGUgcmVnaXN0cmF0aW9uIChjbGFzcyBjb25zdHJ1Y3RvcilcbiAgICAgKiBAaW50ZXJuYWxcbiAgICAgKi9cbiAgICBhcHBseVR5cGVSZWdpc3RyYXRpb24oY29udGFpbmVyLCBjb25maWcsIG9wdGlvbnMpIHtcbiAgICAgICAgY29uc3QgeyBoYXNEZXBlbmRlbmNpZXMgfSA9IHRoaXMuYW5hbHl6ZUNvbnN0cnVjdG9yKGNvbmZpZy5jb25zdHJ1Y3Rvcik7XG4gICAgICAgIC8vIEZhc3QgcGF0aDogTm8gZGVwZW5kZW5jaWVzIGFuZCBubyBzcGVjaWFsIGNvbmZpZ1xuICAgICAgICBpZiAoIWhhc0RlcGVuZGVuY2llcyAmJiAhY29uZmlnLmF1dG93aXJlT3B0aW9ucyAmJiAhY29uZmlnLnBhcmFtZXRlclZhbHVlcykge1xuICAgICAgICAgICAgdGhpcy5jcmVhdGVPcHRpbWl6ZWRGYWN0b3J5KGNvbnRhaW5lciwgY29uZmlnLCBvcHRpb25zKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICAvLyBBdXRvV2lyZSBwYXRoXG4gICAgICAgIGlmIChjb25maWcuYXV0b3dpcmVPcHRpb25zKSB7XG4gICAgICAgICAgICB0aGlzLmNyZWF0ZUF1dG9XaXJlRmFjdG9yeShjb250YWluZXIsIGNvbmZpZywgb3B0aW9ucyk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgLy8gd2l0aFBhcmFtZXRlcnMgcGF0aFxuICAgICAgICBpZiAoY29uZmlnLnBhcmFtZXRlclZhbHVlcykge1xuICAgICAgICAgICAgdGhpcy5jcmVhdGVQYXJhbWV0ZXJGYWN0b3J5KGNvbnRhaW5lciwgY29uZmlnLCBvcHRpb25zKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICAvLyBFcnJvcjogQ29uc3RydWN0b3IgaGFzIGRlcGVuZGVuY2llcyBidXQgbm8gY29uZmlnXG4gICAgICAgIGlmIChoYXNEZXBlbmRlbmNpZXMpIHtcbiAgICAgICAgICAgIGNvbnN0IGNsYXNzTmFtZSA9IGNvbmZpZy5jb25zdHJ1Y3Rvci5uYW1lIHx8ICdVbm5hbWVkQ2xhc3MnO1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBTZXJ2aWNlIFwiJHtjbGFzc05hbWV9XCIgaGFzIGNvbnN0cnVjdG9yIGRlcGVuZGVuY2llcyBidXQgbm8gYXV0b3dpcmluZyBjb25maWd1cmF0aW9uLlxcblxcbmAgK1xuICAgICAgICAgICAgICAgIGBTb2x1dGlvbnM6XFxuYCArXG4gICAgICAgICAgICAgICAgYCAgMS4gXHUyQjUwIFVzZSB0aGUgTm92YURJIHRyYW5zZm9ybWVyIChyZWNvbW1lbmRlZCk6XFxuYCArXG4gICAgICAgICAgICAgICAgYCAgICAgLSBBZGQgXCJAbm92YWRpL2NvcmUvdW5wbHVnaW5cIiB0byB5b3VyIGJ1aWxkIGNvbmZpZ1xcbmAgK1xuICAgICAgICAgICAgICAgIGAgICAgIC0gVHJhbnNmb3JtZXIgYXV0b21hdGljYWxseSBnZW5lcmF0ZXMgLmF1dG9XaXJlKCkgZm9yIGFsbCBkZXBlbmRlbmNpZXNcXG5cXG5gICtcbiAgICAgICAgICAgICAgICBgICAyLiBBZGQgbWFudWFsIGF1dG93aXJpbmc6XFxuYCArXG4gICAgICAgICAgICAgICAgYCAgICAgLmF1dG9XaXJlKHsgbWFwOiB7IC8qIHBhcmFtOiByZXNvbHZlciAqLyB9IH0pXFxuXFxuYCArXG4gICAgICAgICAgICAgICAgYCAgMy4gVXNlIGEgZmFjdG9yeSBmdW5jdGlvbjpcXG5gICtcbiAgICAgICAgICAgICAgICBgICAgICAucmVnaXN0ZXIoKGMpID0+IG5ldyAke2NsYXNzTmFtZX0oLi4uKSlcXG5cXG5gICtcbiAgICAgICAgICAgICAgICBgU2VlIGRvY3M6IGh0dHBzOi8vZ2l0aHViLmNvbS9qYW51czAwNy9Ob3ZhREkjYXV0b3dpcmVgKTtcbiAgICAgICAgfVxuICAgICAgICAvLyBObyBkZXBlbmRlbmNpZXMgLSBjcmVhdGUgc2ltcGxlIGZhY3RvcnlcbiAgICAgICAgY29uc3QgZmFjdG9yeSA9ICgpID0+IG5ldyBjb25maWcuY29uc3RydWN0b3IoKTtcbiAgICAgICAgY29udGFpbmVyLmJpbmRGYWN0b3J5KGNvbmZpZy50b2tlbiwgZmFjdG9yeSwgb3B0aW9ucyk7XG4gICAgfVxuICAgIGFwcGx5UmVnaXN0cmF0aW9uKGNvbnRhaW5lciwgY29uZmlnKSB7XG4gICAgICAgIGNvbnN0IG9wdGlvbnMgPSB7IGxpZmV0aW1lOiBjb25maWcubGlmZXRpbWUgfTtcbiAgICAgICAgc3dpdGNoIChjb25maWcudHlwZSkge1xuICAgICAgICAgICAgY2FzZSAnaW5zdGFuY2UnOlxuICAgICAgICAgICAgICAgIGNvbnRhaW5lci5iaW5kVmFsdWUoY29uZmlnLnRva2VuLCBjb25maWcudmFsdWUpO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgY2FzZSAnZmFjdG9yeSc6XG4gICAgICAgICAgICAgICAgY29udGFpbmVyLmJpbmRGYWN0b3J5KGNvbmZpZy50b2tlbiwgY29uZmlnLmZhY3RvcnksIG9wdGlvbnMpO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgY2FzZSAndHlwZSc6XG4gICAgICAgICAgICAgICAgdGhpcy5hcHBseVR5cGVSZWdpc3RyYXRpb24oY29udGFpbmVyLCBjb25maWcsIG9wdGlvbnMpO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgfVxufVxuIiwgIi8qKlxuICogQ29yZSBkZXBlbmRlbmN5IGluamVjdGlvbiBjb250YWluZXIgZm9yIE5vdmFESVxuICovXG5pbXBvcnQgeyBUb2tlbiB9IGZyb20gJy4vdG9rZW4uanMnO1xuaW1wb3J0IHsgQmluZGluZ05vdEZvdW5kRXJyb3IsIENpcmN1bGFyRGVwZW5kZW5jeUVycm9yIH0gZnJvbSAnLi9lcnJvcnMuanMnO1xuaW1wb3J0IHsgQnVpbGRlciB9IGZyb20gJy4vYnVpbGRlci5qcyc7XG5mdW5jdGlvbiBpc0Rpc3Bvc2FibGUob2JqKSB7XG4gICAgcmV0dXJuIG9iaiAmJiB0eXBlb2Ygb2JqLmRpc3Bvc2UgPT09ICdmdW5jdGlvbic7XG59XG4vKipcbiAqIFJlc29sdXRpb24gY29udGV4dCB0cmFja3MgdGhlIGN1cnJlbnQgZGVwZW5kZW5jeSByZXNvbHV0aW9uIHBhdGhcbiAqIGZvciBjaXJjdWxhciBkZXBlbmRlbmN5IGRldGVjdGlvbiBhbmQgcGVyLXJlcXVlc3Qgc2NvcGluZ1xuICovXG5jbGFzcyBSZXNvbHV0aW9uQ29udGV4dCB7XG4gICAgY29uc3RydWN0b3IoKSB7XG4gICAgICAgIHRoaXMucmVzb2x2aW5nU3RhY2sgPSBuZXcgU2V0KCk7XG4gICAgICAgIHRoaXMucGVyUmVxdWVzdENhY2hlID0gbmV3IE1hcCgpO1xuICAgIH1cbiAgICBpc1Jlc29sdmluZyh0b2tlbikge1xuICAgICAgICByZXR1cm4gdGhpcy5yZXNvbHZpbmdTdGFjay5oYXModG9rZW4pO1xuICAgIH1cbiAgICBlbnRlclJlc29sdmUodG9rZW4pIHtcbiAgICAgICAgdGhpcy5yZXNvbHZpbmdTdGFjay5hZGQodG9rZW4pO1xuICAgICAgICAvLyBQZXJmb3JtYW5jZTogRG9uJ3QgYnVpbGQgcGF0aCB1bmxlc3Mgd2UgbmVlZCBpdCAob25seSB1c2VkIGluIGVycm9yIG1lc3NhZ2VzKVxuICAgICAgICAvLyBUaGlzIGF2b2lkcyBleHBlbnNpdmUgdG9rZW4udG9TdHJpbmcoKSBjYWxscyBvbiBldmVyeSByZXNvbHZlXG4gICAgfVxuICAgIGV4aXRSZXNvbHZlKHRva2VuKSB7XG4gICAgICAgIHRoaXMucmVzb2x2aW5nU3RhY2suZGVsZXRlKHRva2VuKTtcbiAgICAgICAgLy8gUGVyZm9ybWFuY2U6IENsZWFyIGxhenkgcGF0aCBjYWNoZSB3aGVuIGV4aXRpbmdcbiAgICAgICAgdGhpcy5wYXRoID0gdW5kZWZpbmVkO1xuICAgIH1cbiAgICBnZXRQYXRoKCkge1xuICAgICAgICAvLyBQZXJmb3JtYW5jZTogQnVpbGQgcGF0aCBvbi1kZW1hbmQgb25seSB3aGVuIG5lZWRlZCAodHlwaWNhbGx5IGZvciBlcnJvciBtZXNzYWdlcylcbiAgICAgICAgaWYgKCF0aGlzLnBhdGgpIHtcbiAgICAgICAgICAgIHRoaXMucGF0aCA9IEFycmF5LmZyb20odGhpcy5yZXNvbHZpbmdTdGFjaykubWFwKHQgPT4gdC50b1N0cmluZygpKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gWy4uLnRoaXMucGF0aF07XG4gICAgfVxuICAgIGNhY2hlUGVyUmVxdWVzdCh0b2tlbiwgaW5zdGFuY2UpIHtcbiAgICAgICAgdGhpcy5wZXJSZXF1ZXN0Q2FjaGUuc2V0KHRva2VuLCBpbnN0YW5jZSk7XG4gICAgfVxuICAgIGdldFBlclJlcXVlc3QodG9rZW4pIHtcbiAgICAgICAgcmV0dXJuIHRoaXMucGVyUmVxdWVzdENhY2hlLmdldCh0b2tlbik7XG4gICAgfVxuICAgIGhhc1BlclJlcXVlc3QodG9rZW4pIHtcbiAgICAgICAgcmV0dXJuIHRoaXMucGVyUmVxdWVzdENhY2hlLmhhcyh0b2tlbik7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFJlc2V0IGNvbnRleHQgZm9yIHJldXNlIGluIG9iamVjdCBwb29sXG4gICAgICogUGVyZm9ybWFuY2U6IFJldXNpbmcgY29udGV4dHMgYXZvaWRzIGhlYXAgYWxsb2NhdGlvbnNcbiAgICAgKi9cbiAgICByZXNldCgpIHtcbiAgICAgICAgdGhpcy5yZXNvbHZpbmdTdGFjay5jbGVhcigpO1xuICAgICAgICB0aGlzLnBlclJlcXVlc3RDYWNoZS5jbGVhcigpO1xuICAgICAgICB0aGlzLnBhdGggPSB1bmRlZmluZWQ7XG4gICAgfVxufVxuLyoqXG4gKiBPYmplY3QgcG9vbCBmb3IgUmVzb2x1dGlvbkNvbnRleHQgaW5zdGFuY2VzXG4gKiBQZXJmb3JtYW5jZTogUmV1c2luZyBjb250ZXh0cyByZWR1Y2VzIGhlYXAgYWxsb2NhdGlvbnMgYW5kIEdDIHByZXNzdXJlXG4gKi9cbmNsYXNzIFJlc29sdXRpb25Db250ZXh0UG9vbCB7XG4gICAgY29uc3RydWN0b3IoKSB7XG4gICAgICAgIHRoaXMucG9vbCA9IFtdO1xuICAgICAgICB0aGlzLm1heFNpemUgPSAxMDtcbiAgICB9XG4gICAgYWNxdWlyZSgpIHtcbiAgICAgICAgY29uc3QgY29udGV4dCA9IHRoaXMucG9vbC5wb3AoKTtcbiAgICAgICAgaWYgKGNvbnRleHQpIHtcbiAgICAgICAgICAgIC8vIFJlc2V0IGV4aXN0aW5nIGNvbnRleHQgZm9yIHJldXNlXG4gICAgICAgICAgICBjb250ZXh0LnJlc2V0KCk7XG4gICAgICAgICAgICByZXR1cm4gY29udGV4dDtcbiAgICAgICAgfVxuICAgICAgICAvLyBDcmVhdGUgbmV3IGlmIHBvb2wgZW1wdHlcbiAgICAgICAgcmV0dXJuIG5ldyBSZXNvbHV0aW9uQ29udGV4dCgpO1xuICAgIH1cbiAgICByZWxlYXNlKGNvbnRleHQpIHtcbiAgICAgICAgaWYgKHRoaXMucG9vbC5sZW5ndGggPCB0aGlzLm1heFNpemUpIHtcbiAgICAgICAgICAgIHRoaXMucG9vbC5wdXNoKGNvbnRleHQpO1xuICAgICAgICB9XG4gICAgICAgIC8vIE90aGVyd2lzZSBsZXQgaXQgYmUgR0MnZFxuICAgIH1cbn1cbi8qKlxuICogRGVwZW5kZW5jeSBJbmplY3Rpb24gQ29udGFpbmVyXG4gKlxuICogTWFuYWdlcyByZWdpc3RyYXRpb24gYW5kIHJlc29sdXRpb24gb2YgZGVwZW5kZW5jaWVzIHdpdGggc3VwcG9ydCBmb3I6XG4gKiAtIE11bHRpcGxlIGJpbmRpbmcgdHlwZXMgKHZhbHVlLCBmYWN0b3J5LCBjbGFzcylcbiAqIC0gTGlmZXRpbWUgbWFuYWdlbWVudCAoc2luZ2xldG9uLCB0cmFuc2llbnQsIHBlci1yZXF1ZXN0KVxuICogLSBDaGlsZCBjb250YWluZXJzIHdpdGggaW5oZXJpdGFuY2VcbiAqIC0gQ2lyY3VsYXIgZGVwZW5kZW5jeSBkZXRlY3Rpb25cbiAqIC0gQXV0b21hdGljIGRpc3Bvc2FsXG4gKi9cbmV4cG9ydCBjbGFzcyBDb250YWluZXIge1xuICAgIGNvbnN0cnVjdG9yKHBhcmVudCkge1xuICAgICAgICB0aGlzLmJpbmRpbmdzID0gbmV3IE1hcCgpO1xuICAgICAgICB0aGlzLnNpbmdsZXRvbkNhY2hlID0gbmV3IE1hcCgpO1xuICAgICAgICB0aGlzLnNpbmdsZXRvbk9yZGVyID0gW107XG4gICAgICAgIHRoaXMuaW50ZXJmYWNlUmVnaXN0cnkgPSBuZXcgTWFwKCk7XG4gICAgICAgIHRoaXMuaW50ZXJmYWNlVG9rZW5DYWNoZSA9IG5ldyBNYXAoKTsgLy8gUGVyZm9ybWFuY2U6IENhY2hlIGZvciByZXNvbHZlVHlwZSgpIGxvb2t1cHNcbiAgICAgICAgdGhpcy5mYXN0VHJhbnNpZW50Q2FjaGUgPSBuZXcgTWFwKCk7IC8vIFBlcmZvcm1hbmNlOiBGYXN0IHBhdGggZm9yIHNpbXBsZSB0cmFuc2llbnRzXG4gICAgICAgIHRoaXMudWx0cmFGYXN0U2luZ2xldG9uQ2FjaGUgPSBuZXcgTWFwKCk7IC8vIFBlcmZvcm1hbmNlOiBVbHRyYS1mYXN0IHNpbmdsZXRvbi1vbmx5IGNhY2hlXG4gICAgICAgIHRoaXMucGFyZW50ID0gcGFyZW50O1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBCaW5kIGEgcHJlLWNyZWF0ZWQgdmFsdWUgdG8gYSB0b2tlblxuICAgICAqL1xuICAgIGJpbmRWYWx1ZSh0b2tlbiwgdmFsdWUpIHtcbiAgICAgICAgdGhpcy5iaW5kaW5ncy5zZXQodG9rZW4sIHtcbiAgICAgICAgICAgIHR5cGU6ICd2YWx1ZScsXG4gICAgICAgICAgICBsaWZldGltZTogJ3NpbmdsZXRvbicsXG4gICAgICAgICAgICB2YWx1ZSxcbiAgICAgICAgICAgIGNvbnN0cnVjdG9yOiB1bmRlZmluZWRcbiAgICAgICAgfSk7XG4gICAgICAgIHRoaXMuaW52YWxpZGF0ZUJpbmRpbmdDYWNoZSgpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBCaW5kIGEgZmFjdG9yeSBmdW5jdGlvbiB0byBhIHRva2VuXG4gICAgICovXG4gICAgYmluZEZhY3RvcnkodG9rZW4sIGZhY3RvcnksIG9wdGlvbnMpIHtcbiAgICAgICAgdGhpcy5iaW5kaW5ncy5zZXQodG9rZW4sIHtcbiAgICAgICAgICAgIHR5cGU6ICdmYWN0b3J5JyxcbiAgICAgICAgICAgIGxpZmV0aW1lOiBvcHRpb25zPy5saWZldGltZSB8fCAndHJhbnNpZW50JyxcbiAgICAgICAgICAgIGZhY3RvcnksXG4gICAgICAgICAgICBkZXBlbmRlbmNpZXM6IG9wdGlvbnM/LmRlcGVuZGVuY2llcyxcbiAgICAgICAgICAgIGNvbnN0cnVjdG9yOiB1bmRlZmluZWRcbiAgICAgICAgfSk7XG4gICAgICAgIHRoaXMuaW52YWxpZGF0ZUJpbmRpbmdDYWNoZSgpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBCaW5kIGEgY2xhc3MgY29uc3RydWN0b3IgdG8gYSB0b2tlblxuICAgICAqL1xuICAgIGJpbmRDbGFzcyh0b2tlbiwgY29uc3RydWN0b3IsIG9wdGlvbnMpIHtcbiAgICAgICAgY29uc3QgYmluZGluZyA9IHtcbiAgICAgICAgICAgIHR5cGU6ICdjbGFzcycsXG4gICAgICAgICAgICBsaWZldGltZTogb3B0aW9ucz8ubGlmZXRpbWUgfHwgJ3RyYW5zaWVudCcsXG4gICAgICAgICAgICBjb25zdHJ1Y3RvcixcbiAgICAgICAgICAgIGRlcGVuZGVuY2llczogb3B0aW9ucz8uZGVwZW5kZW5jaWVzXG4gICAgICAgIH07XG4gICAgICAgIHRoaXMuYmluZGluZ3Muc2V0KHRva2VuLCBiaW5kaW5nKTtcbiAgICAgICAgdGhpcy5pbnZhbGlkYXRlQmluZGluZ0NhY2hlKCk7XG4gICAgICAgIC8vIFBlcmZvcm1hbmNlOiBQcmUtY29tcGlsZSBmYXN0IHRyYW5zaWVudCBmYWN0b3J5IGZvciB6ZXJvLWRlcGVuZGVuY3kgY2xhc3Nlc1xuICAgICAgICBpZiAoYmluZGluZy5saWZldGltZSA9PT0gJ3RyYW5zaWVudCcgJiYgKCFiaW5kaW5nLmRlcGVuZGVuY2llcyB8fCBiaW5kaW5nLmRlcGVuZGVuY2llcy5sZW5ndGggPT09IDApKSB7XG4gICAgICAgICAgICB0aGlzLmZhc3RUcmFuc2llbnRDYWNoZS5zZXQodG9rZW4sICgpID0+IG5ldyBjb25zdHJ1Y3RvcigpKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICAvKipcbiAgICAgKiBSZXNvbHZlIGEgZGVwZW5kZW5jeSBzeW5jaHJvbm91c2x5XG4gICAgICogUGVyZm9ybWFuY2Ugb3B0aW1pemVkIHdpdGggbXVsdGlwbGUgZmFzdCBwYXRoc1xuICAgICAqL1xuICAgIHJlc29sdmUodG9rZW4pIHtcbiAgICAgICAgLy8gVHJ5IGFsbCBjYWNoZSBsZXZlbHMgZmlyc3QgKHVsdHJhLWZhc3QsIHNpbmdsZXRvbiwgZmFzdCB0cmFuc2llbnQpXG4gICAgICAgIGNvbnN0IGNhY2hlZCA9IHRoaXMudHJ5R2V0RnJvbUNhY2hlcyh0b2tlbik7XG4gICAgICAgIGlmIChjYWNoZWQgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgcmV0dXJuIGNhY2hlZDtcbiAgICAgICAgfVxuICAgICAgICAvLyBJZiB3ZSdyZSBhbHJlYWR5IHJlc29sdmluZyAoY2FsbGVkIGZyb20gd2l0aGluIGEgZmFjdG9yeSksIHJldXNlIHRoZSBjb250ZXh0XG4gICAgICAgIGlmICh0aGlzLmN1cnJlbnRDb250ZXh0KSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5yZXNvbHZlV2l0aENvbnRleHQodG9rZW4sIHRoaXMuY3VycmVudENvbnRleHQpO1xuICAgICAgICB9XG4gICAgICAgIC8vIENvbXBsZXggcmVzb2x1dGlvbiB3aXRoIHBvb2xlZCBjb250ZXh0XG4gICAgICAgIGNvbnN0IGNvbnRleHQgPSBDb250YWluZXIuY29udGV4dFBvb2wuYWNxdWlyZSgpO1xuICAgICAgICB0aGlzLmN1cnJlbnRDb250ZXh0ID0gY29udGV4dDtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLnJlc29sdmVXaXRoQ29udGV4dCh0b2tlbiwgY29udGV4dCk7XG4gICAgICAgIH1cbiAgICAgICAgZmluYWxseSB7XG4gICAgICAgICAgICB0aGlzLmN1cnJlbnRDb250ZXh0ID0gdW5kZWZpbmVkO1xuICAgICAgICAgICAgQ29udGFpbmVyLmNvbnRleHRQb29sLnJlbGVhc2UoY29udGV4dCk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgLyoqXG4gICAgICogU1BFQ0lBTElaRUQ6IFVsdHJhLWZhc3Qgc2luZ2xldG9uIHJlc29sdmUgKG5vIHNhZmV0eSBjaGVja3MpXG4gICAgICogVXNlIE9OTFkgd2hlbiB5b3UncmUgMTAwJSBzdXJlIHRoZSB0b2tlbiBpcyBhIHJlZ2lzdGVyZWQgc2luZ2xldG9uXG4gICAgICogQGludGVybmFsIEZvciBwZXJmb3JtYW5jZS1jcml0aWNhbCBwYXRocyBvbmx5XG4gICAgICovXG4gICAgcmVzb2x2ZVNpbmdsZXRvblVuc2FmZSh0b2tlbikge1xuICAgICAgICAvLyBEaXJlY3QgcmV0dXJuLCBubyBjaGVja3MgLSBtYXhpbXVtIHNwZWVkXG4gICAgICAgIHJldHVybiB0aGlzLnVsdHJhRmFzdFNpbmdsZXRvbkNhY2hlLmdldCh0b2tlbikgPz8gdGhpcy5zaW5nbGV0b25DYWNoZS5nZXQodG9rZW4pO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBTUEVDSUFMSVpFRDogRmFzdCB0cmFuc2llbnQgcmVzb2x2ZSBmb3IgemVyby1kZXBlbmRlbmN5IGNsYXNzZXNcbiAgICAgKiBTa2lwcyBhbGwgY29udGV4dCBjcmVhdGlvbiBhbmQgY2lyY3VsYXIgZGVwZW5kZW5jeSBjaGVja3NcbiAgICAgKiBAaW50ZXJuYWwgRm9yIHBlcmZvcm1hbmNlLWNyaXRpY2FsIHBhdGhzIG9ubHlcbiAgICAgKi9cbiAgICByZXNvbHZlVHJhbnNpZW50U2ltcGxlKHRva2VuKSB7XG4gICAgICAgIGNvbnN0IGZhY3RvcnkgPSB0aGlzLmZhc3RUcmFuc2llbnRDYWNoZS5nZXQodG9rZW4pO1xuICAgICAgICBpZiAoZmFjdG9yeSkge1xuICAgICAgICAgICAgcmV0dXJuIGZhY3RvcnkoKTtcbiAgICAgICAgfVxuICAgICAgICAvLyBGYWxsYmFjayB0byByZWd1bGFyIHJlc29sdmUgaWYgbm90IGluIGZhc3QgY2FjaGVcbiAgICAgICAgcmV0dXJuIHRoaXMucmVzb2x2ZSh0b2tlbik7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFNQRUNJQUxJWkVEOiBCYXRjaCByZXNvbHZlIG11bHRpcGxlIGRlcGVuZGVuY2llcyBhdCBvbmNlXG4gICAgICogTW9yZSBlZmZpY2llbnQgdGhhbiBtdWx0aXBsZSBpbmRpdmlkdWFsIHJlc29sdmVzXG4gICAgICovXG4gICAgcmVzb2x2ZUJhdGNoKHRva2Vucykge1xuICAgICAgICAvLyBSZXVzZSBzaW5nbGUgY29udGV4dCBmb3IgYWxsIHJlc29sdXRpb25zXG4gICAgICAgIGNvbnN0IHdhc1Jlc29sdmluZyA9ICEhdGhpcy5jdXJyZW50Q29udGV4dDtcbiAgICAgICAgY29uc3QgY29udGV4dCA9IHRoaXMuY3VycmVudENvbnRleHQgfHwgQ29udGFpbmVyLmNvbnRleHRQb29sLmFjcXVpcmUoKTtcbiAgICAgICAgaWYgKCF3YXNSZXNvbHZpbmcpIHtcbiAgICAgICAgICAgIHRoaXMuY3VycmVudENvbnRleHQgPSBjb250ZXh0O1xuICAgICAgICB9XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCByZXN1bHRzID0gdG9rZW5zLm1hcCh0b2tlbiA9PiB7XG4gICAgICAgICAgICAgICAgLy8gVHJ5IGFsbCBjYWNoZSBsZXZlbHMgZmlyc3RcbiAgICAgICAgICAgICAgICBjb25zdCBjYWNoZWQgPSB0aGlzLnRyeUdldEZyb21DYWNoZXModG9rZW4pO1xuICAgICAgICAgICAgICAgIGlmIChjYWNoZWQgIT09IHVuZGVmaW5lZClcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGNhY2hlZDtcbiAgICAgICAgICAgICAgICAvLyBGdWxsIHJlc29sdmUgd2l0aCBzaGFyZWQgY29udGV4dFxuICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLnJlc29sdmVXaXRoQ29udGV4dCh0b2tlbiwgY29udGV4dCk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIHJldHVybiByZXN1bHRzO1xuICAgICAgICB9XG4gICAgICAgIGZpbmFsbHkge1xuICAgICAgICAgICAgaWYgKCF3YXNSZXNvbHZpbmcpIHtcbiAgICAgICAgICAgICAgICB0aGlzLmN1cnJlbnRDb250ZXh0ID0gdW5kZWZpbmVkO1xuICAgICAgICAgICAgICAgIENvbnRhaW5lci5jb250ZXh0UG9vbC5yZWxlYXNlKGNvbnRleHQpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFJlc29sdmUgYSBkZXBlbmRlbmN5IGFzeW5jaHJvbm91c2x5IChzdXBwb3J0cyBhc3luYyBmYWN0b3JpZXMpXG4gICAgICovXG4gICAgYXN5bmMgcmVzb2x2ZUFzeW5jKHRva2VuKSB7XG4gICAgICAgIC8vIElmIHdlJ3JlIGFscmVhZHkgcmVzb2x2aW5nIChjYWxsZWQgZnJvbSB3aXRoaW4gYSBmYWN0b3J5KSwgcmV1c2UgdGhlIGNvbnRleHRcbiAgICAgICAgaWYgKHRoaXMuY3VycmVudENvbnRleHQpIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLnJlc29sdmVBc3luY1dpdGhDb250ZXh0KHRva2VuLCB0aGlzLmN1cnJlbnRDb250ZXh0KTtcbiAgICAgICAgfVxuICAgICAgICAvLyBOZXcgdG9wLWxldmVsIHJlc29sdmVcbiAgICAgICAgLy8gUGVyZm9ybWFuY2U6IFVzZSBwb29sZWQgY29udGV4dCB0byBhdm9pZCBoZWFwIGFsbG9jYXRpb25cbiAgICAgICAgY29uc3QgY29udGV4dCA9IENvbnRhaW5lci5jb250ZXh0UG9vbC5hY3F1aXJlKCk7XG4gICAgICAgIHRoaXMuY3VycmVudENvbnRleHQgPSBjb250ZXh0O1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMucmVzb2x2ZUFzeW5jV2l0aENvbnRleHQodG9rZW4sIGNvbnRleHQpO1xuICAgICAgICB9XG4gICAgICAgIGZpbmFsbHkge1xuICAgICAgICAgICAgdGhpcy5jdXJyZW50Q29udGV4dCA9IHVuZGVmaW5lZDtcbiAgICAgICAgICAgIENvbnRhaW5lci5jb250ZXh0UG9vbC5yZWxlYXNlKGNvbnRleHQpOyAvLyBSZXR1cm4gdG8gcG9vbCBmb3IgcmV1c2VcbiAgICAgICAgfVxuICAgIH1cbiAgICAvKipcbiAgICAgKiBUcnkgdG8gZ2V0IGluc3RhbmNlIGZyb20gYWxsIGNhY2hlIGxldmVsc1xuICAgICAqIFJldHVybnMgdW5kZWZpbmVkIGlmIG5vdCBjYWNoZWRcbiAgICAgKiBAaW50ZXJuYWxcbiAgICAgKi9cbiAgICB0cnlHZXRGcm9tQ2FjaGVzKHRva2VuKSB7XG4gICAgICAgIC8vIExldmVsIDE6IFVsdHJhLWZhc3Qgc2luZ2xldG9uIGNhY2hlICh6ZXJvIG92ZXJoZWFkKVxuICAgICAgICBjb25zdCB1bHRyYUZhc3QgPSB0aGlzLnVsdHJhRmFzdFNpbmdsZXRvbkNhY2hlLmdldCh0b2tlbik7XG4gICAgICAgIGlmICh1bHRyYUZhc3QgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgcmV0dXJuIHVsdHJhRmFzdDtcbiAgICAgICAgfVxuICAgICAgICAvLyBMZXZlbCAyOiBSZWd1bGFyIHNpbmdsZXRvbiBjYWNoZVxuICAgICAgICBpZiAodGhpcy5zaW5nbGV0b25DYWNoZS5oYXModG9rZW4pKSB7XG4gICAgICAgICAgICBjb25zdCBjYWNoZWQgPSB0aGlzLnNpbmdsZXRvbkNhY2hlLmdldCh0b2tlbik7XG4gICAgICAgICAgICAvLyBQcm9tb3RlIHRvIHVsdHJhLWZhc3QgY2FjaGUgZm9yIG5leHQgdGltZVxuICAgICAgICAgICAgdGhpcy51bHRyYUZhc3RTaW5nbGV0b25DYWNoZS5zZXQodG9rZW4sIGNhY2hlZCk7XG4gICAgICAgICAgICByZXR1cm4gY2FjaGVkO1xuICAgICAgICB9XG4gICAgICAgIC8vIExldmVsIDM6IEZhc3QgdHJhbnNpZW50IGNhY2hlIChubyBkZXBlbmRlbmNpZXMpXG4gICAgICAgIGNvbnN0IGZhc3RGYWN0b3J5ID0gdGhpcy5mYXN0VHJhbnNpZW50Q2FjaGUuZ2V0KHRva2VuKTtcbiAgICAgICAgaWYgKGZhc3RGYWN0b3J5KSB7XG4gICAgICAgICAgICByZXR1cm4gZmFzdEZhY3RvcnkoKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdW5kZWZpbmVkO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBDYWNoZSBpbnN0YW5jZSBiYXNlZCBvbiBsaWZldGltZSBzdHJhdGVneVxuICAgICAqIEBpbnRlcm5hbFxuICAgICAqL1xuICAgIGNhY2hlSW5zdGFuY2UodG9rZW4sIGluc3RhbmNlLCBsaWZldGltZSwgY29udGV4dCkge1xuICAgICAgICBpZiAobGlmZXRpbWUgPT09ICdzaW5nbGV0b24nKSB7XG4gICAgICAgICAgICB0aGlzLnNpbmdsZXRvbkNhY2hlLnNldCh0b2tlbiwgaW5zdGFuY2UpO1xuICAgICAgICAgICAgdGhpcy5zaW5nbGV0b25PcmRlci5wdXNoKHRva2VuKTtcbiAgICAgICAgICAgIC8vIEFsc28gYWRkIHRvIHVsdHJhLWZhc3QgY2FjaGVcbiAgICAgICAgICAgIHRoaXMudWx0cmFGYXN0U2luZ2xldG9uQ2FjaGUuc2V0KHRva2VuLCBpbnN0YW5jZSk7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSBpZiAobGlmZXRpbWUgPT09ICdwZXItcmVxdWVzdCcgJiYgY29udGV4dCkge1xuICAgICAgICAgICAgY29udGV4dC5jYWNoZVBlclJlcXVlc3QodG9rZW4sIGluc3RhbmNlKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICAvKipcbiAgICAgKiBWYWxpZGF0ZSBhbmQgZ2V0IGJpbmRpbmcgd2l0aCBjaXJjdWxhciBkZXBlbmRlbmN5IGNoZWNrXG4gICAgICogUmV0dXJucyBiaW5kaW5nIG9yIHRocm93cyBlcnJvclxuICAgICAqIEBpbnRlcm5hbFxuICAgICAqL1xuICAgIHZhbGlkYXRlQW5kR2V0QmluZGluZyh0b2tlbiwgY29udGV4dCkge1xuICAgICAgICAvLyBDaGVjayBjaXJjdWxhciBkZXBlbmRlbmN5XG4gICAgICAgIGlmIChjb250ZXh0LmlzUmVzb2x2aW5nKHRva2VuKSkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IENpcmN1bGFyRGVwZW5kZW5jeUVycm9yKFsuLi5jb250ZXh0LmdldFBhdGgoKSwgdG9rZW4udG9TdHJpbmcoKV0pO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IGJpbmRpbmcgPSB0aGlzLmdldEJpbmRpbmcodG9rZW4pO1xuICAgICAgICBpZiAoIWJpbmRpbmcpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBCaW5kaW5nTm90Rm91bmRFcnJvcih0b2tlbi50b1N0cmluZygpLCBjb250ZXh0LmdldFBhdGgoKSk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGJpbmRpbmc7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEluc3RhbnRpYXRlIGZyb20gYmluZGluZyBzeW5jaHJvbm91c2x5XG4gICAgICogQGludGVybmFsXG4gICAgICovXG4gICAgaW5zdGFudGlhdGVCaW5kaW5nU3luYyhiaW5kaW5nLCB0b2tlbiwgY29udGV4dCkge1xuICAgICAgICBzd2l0Y2ggKGJpbmRpbmcudHlwZSkge1xuICAgICAgICAgICAgY2FzZSAndmFsdWUnOlxuICAgICAgICAgICAgICAgIHJldHVybiBiaW5kaW5nLnZhbHVlO1xuICAgICAgICAgICAgY2FzZSAnZmFjdG9yeSc6XG4gICAgICAgICAgICAgICAgY29uc3QgcmVzdWx0ID0gYmluZGluZy5mYWN0b3J5KHRoaXMpO1xuICAgICAgICAgICAgICAgIGlmIChyZXN1bHQgaW5zdGFuY2VvZiBQcm9taXNlKSB7XG4gICAgICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgQXN5bmMgZmFjdG9yeSBkZXRlY3RlZCBmb3IgJHt0b2tlbi50b1N0cmluZygpfS4gVXNlIHJlc29sdmVBc3luYygpIGluc3RlYWQuYCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgICAgICAgICBjYXNlICdjbGFzcyc6XG4gICAgICAgICAgICAgICAgY29uc3QgZGVwcyA9IGJpbmRpbmcuZGVwZW5kZW5jaWVzIHx8IFtdO1xuICAgICAgICAgICAgICAgIGNvbnN0IHJlc29sdmVkRGVwcyA9IGRlcHMubWFwKGRlcCA9PiB0aGlzLnJlc29sdmVXaXRoQ29udGV4dChkZXAsIGNvbnRleHQpKTtcbiAgICAgICAgICAgICAgICByZXR1cm4gbmV3IGJpbmRpbmcuY29uc3RydWN0b3IoLi4ucmVzb2x2ZWREZXBzKTtcbiAgICAgICAgICAgIGNhc2UgJ2lubGluZS1jbGFzcyc6XG4gICAgICAgICAgICAgICAgcmV0dXJuIG5ldyBiaW5kaW5nLmNvbnN0cnVjdG9yKCk7XG4gICAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgVW5rbm93biBiaW5kaW5nIHR5cGU6ICR7YmluZGluZy50eXBlfWApO1xuICAgICAgICB9XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEluc3RhbnRpYXRlIGZyb20gYmluZGluZyBhc3luY2hyb25vdXNseVxuICAgICAqIEBpbnRlcm5hbFxuICAgICAqL1xuICAgIGFzeW5jIGluc3RhbnRpYXRlQmluZGluZ0FzeW5jKGJpbmRpbmcsIGNvbnRleHQpIHtcbiAgICAgICAgc3dpdGNoIChiaW5kaW5nLnR5cGUpIHtcbiAgICAgICAgICAgIGNhc2UgJ3ZhbHVlJzpcbiAgICAgICAgICAgICAgICByZXR1cm4gYmluZGluZy52YWx1ZTtcbiAgICAgICAgICAgIGNhc2UgJ2ZhY3RvcnknOlxuICAgICAgICAgICAgICAgIHJldHVybiBhd2FpdCBQcm9taXNlLnJlc29sdmUoYmluZGluZy5mYWN0b3J5KHRoaXMpKTtcbiAgICAgICAgICAgIGNhc2UgJ2NsYXNzJzpcbiAgICAgICAgICAgICAgICBjb25zdCBkZXBzID0gYmluZGluZy5kZXBlbmRlbmNpZXMgfHwgW107XG4gICAgICAgICAgICAgICAgY29uc3QgcmVzb2x2ZWREZXBzID0gYXdhaXQgUHJvbWlzZS5hbGwoZGVwcy5tYXAoZGVwID0+IHRoaXMucmVzb2x2ZUFzeW5jV2l0aENvbnRleHQoZGVwLCBjb250ZXh0KSkpO1xuICAgICAgICAgICAgICAgIHJldHVybiBuZXcgYmluZGluZy5jb25zdHJ1Y3RvciguLi5yZXNvbHZlZERlcHMpO1xuICAgICAgICAgICAgY2FzZSAnaW5saW5lLWNsYXNzJzpcbiAgICAgICAgICAgICAgICByZXR1cm4gbmV3IGJpbmRpbmcuY29uc3RydWN0b3IoKTtcbiAgICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBVbmtub3duIGJpbmRpbmcgdHlwZTogJHtiaW5kaW5nLnR5cGV9YCk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgLyoqXG4gICAgICogQ3JlYXRlIGEgY2hpbGQgY29udGFpbmVyIHRoYXQgaW5oZXJpdHMgYmluZGluZ3MgZnJvbSB0aGlzIGNvbnRhaW5lclxuICAgICAqL1xuICAgIGNyZWF0ZUNoaWxkKCkge1xuICAgICAgICByZXR1cm4gbmV3IENvbnRhaW5lcih0aGlzKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogRGlzcG9zZSBhbGwgc2luZ2xldG9uIGluc3RhbmNlcyBpbiByZXZlcnNlIHJlZ2lzdHJhdGlvbiBvcmRlclxuICAgICAqL1xuICAgIGFzeW5jIGRpc3Bvc2UoKSB7XG4gICAgICAgIGNvbnN0IGVycm9ycyA9IFtdO1xuICAgICAgICAvLyBEaXNwb3NlIGluIHJldmVyc2Ugb3JkZXJcbiAgICAgICAgZm9yIChsZXQgaSA9IHRoaXMuc2luZ2xldG9uT3JkZXIubGVuZ3RoIC0gMTsgaSA+PSAwOyBpLS0pIHtcbiAgICAgICAgICAgIGNvbnN0IHRva2VuID0gdGhpcy5zaW5nbGV0b25PcmRlcltpXTtcbiAgICAgICAgICAgIGNvbnN0IGluc3RhbmNlID0gdGhpcy5zaW5nbGV0b25DYWNoZS5nZXQodG9rZW4pO1xuICAgICAgICAgICAgaWYgKGluc3RhbmNlICYmIGlzRGlzcG9zYWJsZShpbnN0YW5jZSkpIHtcbiAgICAgICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgICAgICBhd2FpdCBpbnN0YW5jZS5kaXNwb3NlKCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgICAgICAgICBlcnJvcnMucHVzaChlcnJvcik7XG4gICAgICAgICAgICAgICAgICAgIC8vIENvbnRpbnVlIGRpc3Bvc2luZyBvdGhlciBpbnN0YW5jZXMgZXZlbiBpZiBvbmUgZmFpbHNcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgLy8gQ2xlYXIgY2FjaGVzXG4gICAgICAgIHRoaXMuc2luZ2xldG9uQ2FjaGUuY2xlYXIoKTtcbiAgICAgICAgdGhpcy5zaW5nbGV0b25PcmRlci5sZW5ndGggPSAwO1xuICAgICAgICAvLyBOb3RlOiBXZSBkb24ndCB0aHJvdyBlcnJvcnMgdG8gYWxsb3cgYWxsIGRpc3Bvc2FscyB0byBjb21wbGV0ZVxuICAgICAgICAvLyBJbiBwcm9kdWN0aW9uLCB5b3UgbWlnaHQgd2FudCB0byBsb2cgdGhlc2UgZXJyb3JzXG4gICAgfVxuICAgIC8qKlxuICAgICAqIENyZWF0ZSBhIGZsdWVudCBidWlsZGVyIGZvciByZWdpc3RlcmluZyBkZXBlbmRlbmNpZXNcbiAgICAgKi9cbiAgICBidWlsZGVyKCkge1xuICAgICAgICByZXR1cm4gbmV3IEJ1aWxkZXIodGhpcyk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFJlc29sdmUgYSBuYW1lZCBzZXJ2aWNlXG4gICAgICovXG4gICAgcmVzb2x2ZU5hbWVkKG5hbWUpIHtcbiAgICAgICAgY29uc3QgbmFtZWRSZWdpc3RyYXRpb25zID0gdGhpcy5fX25hbWVkUmVnaXN0cmF0aW9ucztcbiAgICAgICAgaWYgKCFuYW1lZFJlZ2lzdHJhdGlvbnMpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgTmFtZWQgc2VydmljZSBcIiR7bmFtZX1cIiBub3QgZm91bmQuIE5vIG5hbWVkIHJlZ2lzdHJhdGlvbnMgZXhpc3QuYCk7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgY29uZmlnID0gbmFtZWRSZWdpc3RyYXRpb25zLmdldChuYW1lKTtcbiAgICAgICAgaWYgKCFjb25maWcpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgTmFtZWQgc2VydmljZSBcIiR7bmFtZX1cIiBub3QgZm91bmRgKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdGhpcy5yZXNvbHZlKGNvbmZpZy50b2tlbik7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFJlc29sdmUgYSBrZXllZCBzZXJ2aWNlXG4gICAgICovXG4gICAgcmVzb2x2ZUtleWVkKGtleSkge1xuICAgICAgICBjb25zdCBrZXllZFJlZ2lzdHJhdGlvbnMgPSB0aGlzLl9fa2V5ZWRSZWdpc3RyYXRpb25zO1xuICAgICAgICBpZiAoIWtleWVkUmVnaXN0cmF0aW9ucykge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBLZXllZCBzZXJ2aWNlIG5vdCBmb3VuZC4gTm8ga2V5ZWQgcmVnaXN0cmF0aW9ucyBleGlzdC5gKTtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBjb25maWcgPSBrZXllZFJlZ2lzdHJhdGlvbnMuZ2V0KGtleSk7XG4gICAgICAgIGlmICghY29uZmlnKSB7XG4gICAgICAgICAgICBjb25zdCBrZXlTdHIgPSB0eXBlb2Yga2V5ID09PSAnc3ltYm9sJyA/IGtleS50b1N0cmluZygpIDogYFwiJHtrZXl9XCJgO1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBLZXllZCBzZXJ2aWNlICR7a2V5U3RyfSBub3QgZm91bmRgKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdGhpcy5yZXNvbHZlKGNvbmZpZy50b2tlbik7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFJlc29sdmUgYWxsIHJlZ2lzdHJhdGlvbnMgZm9yIGEgdG9rZW5cbiAgICAgKi9cbiAgICByZXNvbHZlQWxsKHRva2VuKSB7XG4gICAgICAgIGNvbnN0IG11bHRpUmVnaXN0cmF0aW9ucyA9IHRoaXMuX19tdWx0aVJlZ2lzdHJhdGlvbnM7XG4gICAgICAgIGlmICghbXVsdGlSZWdpc3RyYXRpb25zKSB7XG4gICAgICAgICAgICByZXR1cm4gW107XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgdG9rZW5zID0gbXVsdGlSZWdpc3RyYXRpb25zLmdldCh0b2tlbik7XG4gICAgICAgIGlmICghdG9rZW5zIHx8IHRva2Vucy5sZW5ndGggPT09IDApIHtcbiAgICAgICAgICAgIHJldHVybiBbXTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdG9rZW5zLm1hcCgodCkgPT4gdGhpcy5yZXNvbHZlKHQpKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogR2V0IHJlZ2lzdHJ5IGluZm9ybWF0aW9uIGZvciBkZWJ1Z2dpbmcvdmlzdWFsaXphdGlvblxuICAgICAqIFJldHVybnMgYXJyYXkgb2YgYmluZGluZyBpbmZvcm1hdGlvblxuICAgICAqL1xuICAgIGdldFJlZ2lzdHJ5KCkge1xuICAgICAgICBjb25zdCByZWdpc3RyeSA9IFtdO1xuICAgICAgICB0aGlzLmJpbmRpbmdzLmZvckVhY2goKGJpbmRpbmcsIHRva2VuKSA9PiB7XG4gICAgICAgICAgICByZWdpc3RyeS5wdXNoKHtcbiAgICAgICAgICAgICAgICB0b2tlbjogdG9rZW4uZGVzY3JpcHRpb24gfHwgdG9rZW4uc3ltYm9sLnRvU3RyaW5nKCksXG4gICAgICAgICAgICAgICAgdHlwZTogYmluZGluZy50eXBlLFxuICAgICAgICAgICAgICAgIGxpZmV0aW1lOiBiaW5kaW5nLmxpZmV0aW1lLFxuICAgICAgICAgICAgICAgIGRlcGVuZGVuY2llczogYmluZGluZy5kZXBlbmRlbmNpZXM/Lm1hcChkID0+IGQuZGVzY3JpcHRpb24gfHwgZC5zeW1ib2wudG9TdHJpbmcoKSlcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICAgICAgcmV0dXJuIHJlZ2lzdHJ5O1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBHZXQgb3IgY3JlYXRlIGEgdG9rZW4gZm9yIGFuIGludGVyZmFjZSB0eXBlXG4gICAgICogVXNlcyBhIHR5cGUgbmFtZSBoYXNoIGFzIGtleSBmb3IgdGhlIGludGVyZmFjZSByZWdpc3RyeVxuICAgICAqL1xuICAgIGludGVyZmFjZVRva2VuKHR5cGVOYW1lKSB7XG4gICAgICAgIC8vIEdlbmVyYXRlIGEgdW5pcXVlIGtleSBmb3IgdGhpcyBpbnRlcmZhY2UgdHlwZVxuICAgICAgICAvLyBJbiBwcm9kdWN0aW9uLCB0aGlzIHdvdWxkIGJlIHJlcGxhY2VkIGJ5IGEgVFMgdHJhbnNmb3JtZXJcbiAgICAgICAgY29uc3Qga2V5ID0gdHlwZU5hbWUgfHwgYEludGVyZmFjZV8ke01hdGgucmFuZG9tKCkudG9TdHJpbmcoMzYpLnN1YnN0cigyLCA5KX1gO1xuICAgICAgICAvLyBDaGVjayBpZiB0b2tlbiBhbHJlYWR5IGV4aXN0cyBpbiB0aGlzIGNvbnRhaW5lclxuICAgICAgICBpZiAodGhpcy5pbnRlcmZhY2VSZWdpc3RyeS5oYXMoa2V5KSkge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMuaW50ZXJmYWNlUmVnaXN0cnkuZ2V0KGtleSk7XG4gICAgICAgIH1cbiAgICAgICAgLy8gQ2hlY2sgcGFyZW50IGNvbnRhaW5lciAocmVjdXJzaXZlbHkgdGhyb3VnaCBwYXJlbnQgY2hhaW4pXG4gICAgICAgIGlmICh0aGlzLnBhcmVudCkge1xuICAgICAgICAgICAgLy8gUmVjdXJzaXZlbHkgY2hlY2sgdGhyb3VnaCBlbnRpcmUgcGFyZW50IGNoYWluXG4gICAgICAgICAgICBjb25zdCBwYXJlbnRUb2tlbiA9IHRoaXMucGFyZW50LmludGVyZmFjZVRva2VuKGtleSk7XG4gICAgICAgICAgICAvLyBJZiBwYXJlbnQgY3JlYXRlZCBhIG5ldyB0b2tlbiwgZG9uJ3QgY3JlYXRlIGFub3RoZXIgb25lXG4gICAgICAgICAgICByZXR1cm4gcGFyZW50VG9rZW47XG4gICAgICAgIH1cbiAgICAgICAgLy8gQ3JlYXRlIG5ldyB0b2tlbiAob25seSBpZiBubyBwYXJlbnQgZXhpc3RzKVxuICAgICAgICBjb25zdCB0b2tlbiA9IFRva2VuKGtleSk7XG4gICAgICAgIHRoaXMuaW50ZXJmYWNlUmVnaXN0cnkuc2V0KGtleSwgdG9rZW4pO1xuICAgICAgICByZXR1cm4gdG9rZW47XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFJlc29sdmUgYSBkZXBlbmRlbmN5IGJ5IGludGVyZmFjZSB0eXBlIHdpdGhvdXQgZXhwbGljaXQgdG9rZW5cbiAgICAgKi9cbiAgICByZXNvbHZlVHlwZSh0eXBlTmFtZSkge1xuICAgICAgICAvLyBQZXJmb3JtYW5jZTogQ2FjaGUgdG9rZW4gbG9va3VwcyB0byBhdm9pZCByZXBlYXRlZCBpbnRlcmZhY2VSZWdpc3RyeSBhY2Nlc3NcbiAgICAgICAgY29uc3Qga2V5ID0gdHlwZU5hbWUgfHwgJyc7XG4gICAgICAgIGxldCB0b2tlbiA9IHRoaXMuaW50ZXJmYWNlVG9rZW5DYWNoZS5nZXQoa2V5KTtcbiAgICAgICAgaWYgKCF0b2tlbikge1xuICAgICAgICAgICAgdG9rZW4gPSB0aGlzLmludGVyZmFjZVRva2VuKHR5cGVOYW1lKTtcbiAgICAgICAgICAgIHRoaXMuaW50ZXJmYWNlVG9rZW5DYWNoZS5zZXQoa2V5LCB0b2tlbik7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRoaXMucmVzb2x2ZSh0b2tlbik7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFJlc29sdmUgYSBrZXllZCBpbnRlcmZhY2VcbiAgICAgKi9cbiAgICByZXNvbHZlVHlwZUtleWVkKGtleSwgX3R5cGVOYW1lKSB7XG4gICAgICAgIC8vIEZvciBrZXllZCBpbnRlcmZhY2VzLCB3ZSB1c2UgdGhlIGV4aXN0aW5nIHJlc29sdmVLZXllZCBtZWNoYW5pc21cbiAgICAgICAgcmV0dXJuIHRoaXMucmVzb2x2ZUtleWVkKGtleSk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFJlc29sdmUgYWxsIHJlZ2lzdHJhdGlvbnMgZm9yIGFuIGludGVyZmFjZSB0eXBlXG4gICAgICovXG4gICAgcmVzb2x2ZVR5cGVBbGwodHlwZU5hbWUpIHtcbiAgICAgICAgY29uc3QgdG9rZW4gPSB0aGlzLmludGVyZmFjZVRva2VuKHR5cGVOYW1lKTtcbiAgICAgICAgcmV0dXJuIHRoaXMucmVzb2x2ZUFsbCh0b2tlbik7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEludGVybmFsOiBSZXNvbHZlIHdpdGggY29udGV4dCBmb3IgY2lyY3VsYXIgZGVwZW5kZW5jeSBkZXRlY3Rpb25cbiAgICAgKi9cbiAgICByZXNvbHZlV2l0aENvbnRleHQodG9rZW4sIGNvbnRleHQpIHtcbiAgICAgICAgLy8gVmFsaWRhdGUgYW5kIGdldCBiaW5kaW5nICh3aXRoIGNpcmN1bGFyIGRlcGVuZGVuY3kgY2hlY2spXG4gICAgICAgIGNvbnN0IGJpbmRpbmcgPSB0aGlzLnZhbGlkYXRlQW5kR2V0QmluZGluZyh0b2tlbiwgY29udGV4dCk7XG4gICAgICAgIC8vIENoZWNrIHBlci1yZXF1ZXN0IGNhY2hlXG4gICAgICAgIGlmIChiaW5kaW5nLmxpZmV0aW1lID09PSAncGVyLXJlcXVlc3QnICYmIGNvbnRleHQuaGFzUGVyUmVxdWVzdCh0b2tlbikpIHtcbiAgICAgICAgICAgIHJldHVybiBjb250ZXh0LmdldFBlclJlcXVlc3QodG9rZW4pO1xuICAgICAgICB9XG4gICAgICAgIC8vIENoZWNrIHNpbmdsZXRvbiBjYWNoZSAobG9jYWwgY29udGFpbmVyIG9ubHkpXG4gICAgICAgIGlmIChiaW5kaW5nLmxpZmV0aW1lID09PSAnc2luZ2xldG9uJyAmJiB0aGlzLnNpbmdsZXRvbkNhY2hlLmhhcyh0b2tlbikpIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLnNpbmdsZXRvbkNhY2hlLmdldCh0b2tlbik7XG4gICAgICAgIH1cbiAgICAgICAgLy8gTWFyayBhcyByZXNvbHZpbmdcbiAgICAgICAgY29udGV4dC5lbnRlclJlc29sdmUodG9rZW4pO1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgLy8gSW5zdGFudGlhdGUgZnJvbSBiaW5kaW5nXG4gICAgICAgICAgICBjb25zdCBpbnN0YW5jZSA9IHRoaXMuaW5zdGFudGlhdGVCaW5kaW5nU3luYyhiaW5kaW5nLCB0b2tlbiwgY29udGV4dCk7XG4gICAgICAgICAgICAvLyBDYWNoZSBiYXNlZCBvbiBsaWZldGltZVxuICAgICAgICAgICAgdGhpcy5jYWNoZUluc3RhbmNlKHRva2VuLCBpbnN0YW5jZSwgYmluZGluZy5saWZldGltZSwgY29udGV4dCk7XG4gICAgICAgICAgICByZXR1cm4gaW5zdGFuY2U7XG4gICAgICAgIH1cbiAgICAgICAgZmluYWxseSB7XG4gICAgICAgICAgICBjb250ZXh0LmV4aXRSZXNvbHZlKHRva2VuKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICAvKipcbiAgICAgKiBJbnRlcm5hbDogQXN5bmMgcmVzb2x2ZSB3aXRoIGNvbnRleHRcbiAgICAgKi9cbiAgICBhc3luYyByZXNvbHZlQXN5bmNXaXRoQ29udGV4dCh0b2tlbiwgY29udGV4dCkge1xuICAgICAgICAvLyBWYWxpZGF0ZSBhbmQgZ2V0IGJpbmRpbmcgKHdpdGggY2lyY3VsYXIgZGVwZW5kZW5jeSBjaGVjaylcbiAgICAgICAgY29uc3QgYmluZGluZyA9IHRoaXMudmFsaWRhdGVBbmRHZXRCaW5kaW5nKHRva2VuLCBjb250ZXh0KTtcbiAgICAgICAgLy8gQ2hlY2sgcGVyLXJlcXVlc3QgY2FjaGVcbiAgICAgICAgaWYgKGJpbmRpbmcubGlmZXRpbWUgPT09ICdwZXItcmVxdWVzdCcgJiYgY29udGV4dC5oYXNQZXJSZXF1ZXN0KHRva2VuKSkge1xuICAgICAgICAgICAgcmV0dXJuIGNvbnRleHQuZ2V0UGVyUmVxdWVzdCh0b2tlbik7XG4gICAgICAgIH1cbiAgICAgICAgLy8gQ2hlY2sgc2luZ2xldG9uIGNhY2hlIChsb2NhbCBjb250YWluZXIgb25seSlcbiAgICAgICAgaWYgKGJpbmRpbmcubGlmZXRpbWUgPT09ICdzaW5nbGV0b24nICYmIHRoaXMuc2luZ2xldG9uQ2FjaGUuaGFzKHRva2VuKSkge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMuc2luZ2xldG9uQ2FjaGUuZ2V0KHRva2VuKTtcbiAgICAgICAgfVxuICAgICAgICAvLyBNYXJrIGFzIHJlc29sdmluZ1xuICAgICAgICBjb250ZXh0LmVudGVyUmVzb2x2ZSh0b2tlbik7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICAvLyBJbnN0YW50aWF0ZSBmcm9tIGJpbmRpbmcgYXN5bmNocm9ub3VzbHlcbiAgICAgICAgICAgIGNvbnN0IGluc3RhbmNlID0gYXdhaXQgdGhpcy5pbnN0YW50aWF0ZUJpbmRpbmdBc3luYyhiaW5kaW5nLCBjb250ZXh0KTtcbiAgICAgICAgICAgIC8vIENhY2hlIGJhc2VkIG9uIGxpZmV0aW1lXG4gICAgICAgICAgICB0aGlzLmNhY2hlSW5zdGFuY2UodG9rZW4sIGluc3RhbmNlLCBiaW5kaW5nLmxpZmV0aW1lLCBjb250ZXh0KTtcbiAgICAgICAgICAgIHJldHVybiBpbnN0YW5jZTtcbiAgICAgICAgfVxuICAgICAgICBmaW5hbGx5IHtcbiAgICAgICAgICAgIGNvbnRleHQuZXhpdFJlc29sdmUodG9rZW4pO1xuICAgICAgICB9XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEdldCBiaW5kaW5nIGZyb20gdGhpcyBjb250YWluZXIgb3IgcGFyZW50IGNoYWluXG4gICAgICogUGVyZm9ybWFuY2Ugb3B0aW1pemVkOiBVc2VzIGZsYXQgY2FjaGUgdG8gYXZvaWQgcmVjdXJzaXZlIHBhcmVudCBsb29rdXBzXG4gICAgICovXG4gICAgZ2V0QmluZGluZyh0b2tlbikge1xuICAgICAgICAvLyBCdWlsZCBmbGF0IGNhY2hlIG9uIGZpcnN0IGFjY2Vzc1xuICAgICAgICBpZiAoIXRoaXMuYmluZGluZ0NhY2hlKSB7XG4gICAgICAgICAgICB0aGlzLmJ1aWxkQmluZGluZ0NhY2hlKCk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRoaXMuYmluZGluZ0NhY2hlLmdldCh0b2tlbik7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEJ1aWxkIGZsYXQgY2FjaGUgb2YgYWxsIGJpbmRpbmdzIGluY2x1ZGluZyBwYXJlbnQgY2hhaW5cbiAgICAgKiBUaGlzIGNvbnZlcnRzIE8obikgcGFyZW50IGNoYWluIHRyYXZlcnNhbCB0byBPKDEpIGxvb2t1cFxuICAgICAqL1xuICAgIGJ1aWxkQmluZGluZ0NhY2hlKCkge1xuICAgICAgICB0aGlzLmJpbmRpbmdDYWNoZSA9IG5ldyBNYXAoKTtcbiAgICAgICAgLy8gVHJhdmVyc2UgcGFyZW50IGNoYWluIGFuZCBmbGF0dGVuIGFsbCBiaW5kaW5nc1xuICAgICAgICBsZXQgY3VycmVudCA9IHRoaXM7XG4gICAgICAgIHdoaWxlIChjdXJyZW50KSB7XG4gICAgICAgICAgICBjdXJyZW50LmJpbmRpbmdzLmZvckVhY2goKGJpbmRpbmcsIHRva2VuKSA9PiB7XG4gICAgICAgICAgICAgICAgLy8gQ2hpbGQgYmluZGluZ3Mgb3ZlcnJpZGUgcGFyZW50IGJpbmRpbmdzIChmaXJzdCB3aW5zKVxuICAgICAgICAgICAgICAgIGlmICghdGhpcy5iaW5kaW5nQ2FjaGUuaGFzKHRva2VuKSkge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmJpbmRpbmdDYWNoZS5zZXQodG9rZW4sIGJpbmRpbmcpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgY3VycmVudCA9IGN1cnJlbnQucGFyZW50O1xuICAgICAgICB9XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEludmFsaWRhdGUgYmluZGluZyBjYWNoZSB3aGVuIG5ldyBiaW5kaW5ncyBhcmUgYWRkZWRcbiAgICAgKiBDYWxsZWQgYnkgYmluZFZhbHVlLCBiaW5kRmFjdG9yeSwgYmluZENsYXNzXG4gICAgICovXG4gICAgaW52YWxpZGF0ZUJpbmRpbmdDYWNoZSgpIHtcbiAgICAgICAgdGhpcy5iaW5kaW5nQ2FjaGUgPSB1bmRlZmluZWQ7XG4gICAgICAgIHRoaXMudWx0cmFGYXN0U2luZ2xldG9uQ2FjaGUuY2xlYXIoKTsgLy8gQ2xlYXIgdWx0cmEtZmFzdCBjYWNoZSB3aGVuIGJpbmRpbmdzIGNoYW5nZVxuICAgIH1cbn1cbkNvbnRhaW5lci5jb250ZXh0UG9vbCA9IG5ldyBSZXNvbHV0aW9uQ29udGV4dFBvb2woKTsgLy8gUGVyZm9ybWFuY2U6IFBvb2xlZCBjb250ZXh0cyByZWR1Y2UgYWxsb2NhdGlvbnNcbiIsICJleHBvcnQgY2xhc3MgRGF0ZVJlbmRlcmVyIHtcbiAgICBjb25zdHJ1Y3RvcihkYXRlU2VydmljZSkge1xuICAgICAgICB0aGlzLmRhdGVTZXJ2aWNlID0gZGF0ZVNlcnZpY2U7XG4gICAgICAgIHRoaXMudHlwZSA9ICdkYXRlJztcbiAgICB9XG4gICAgcmVuZGVyKGNvbnRleHQpIHtcbiAgICAgICAgY29uc3QgZGF0ZXMgPSBjb250ZXh0LmZpbHRlclsnZGF0ZSddIHx8IFtdO1xuICAgICAgICBjb25zdCByZXNvdXJjZUlkcyA9IGNvbnRleHQuZmlsdGVyWydyZXNvdXJjZSddIHx8IFtdO1xuICAgICAgICAvLyBDaGVjayBpZiBkYXRlIGhlYWRlcnMgc2hvdWxkIGJlIGhpZGRlbiAoZS5nLiwgaW4gZGF5IHZpZXcpXG4gICAgICAgIGNvbnN0IGRhdGVHcm91cGluZyA9IGNvbnRleHQuZ3JvdXBpbmdzPy5maW5kKGcgPT4gZy50eXBlID09PSAnZGF0ZScpO1xuICAgICAgICBjb25zdCBoaWRlSGVhZGVyID0gZGF0ZUdyb3VwaW5nPy5oaWRlSGVhZGVyID09PSB0cnVlO1xuICAgICAgICAvLyBSZW5kZXIgZGF0ZXMgZm9yIEhWRVIgcmVzb3VyY2UgKGVsbGVyIDEgZ2FuZyBodmlzIGluZ2VuIHJlc291cmNlcylcbiAgICAgICAgY29uc3QgaXRlcmF0aW9ucyA9IHJlc291cmNlSWRzLmxlbmd0aCB8fCAxO1xuICAgICAgICBsZXQgY29sdW1uQ291bnQgPSAwO1xuICAgICAgICBmb3IgKGxldCByID0gMDsgciA8IGl0ZXJhdGlvbnM7IHIrKykge1xuICAgICAgICAgICAgY29uc3QgcmVzb3VyY2VJZCA9IHJlc291cmNlSWRzW3JdOyAvLyB1bmRlZmluZWQgaHZpcyBpbmdlbiByZXNvdXJjZXNcbiAgICAgICAgICAgIGZvciAoY29uc3QgZGF0ZVN0ciBvZiBkYXRlcykge1xuICAgICAgICAgICAgICAgIGNvbnN0IGRhdGUgPSB0aGlzLmRhdGVTZXJ2aWNlLnBhcnNlSVNPKGRhdGVTdHIpO1xuICAgICAgICAgICAgICAgIC8vIEJ1aWxkIGNvbHVtbktleSBmb3IgdW5pZm9ybSBpZGVudGlmaWNhdGlvblxuICAgICAgICAgICAgICAgIGNvbnN0IHNlZ21lbnRzID0geyBkYXRlOiBkYXRlU3RyIH07XG4gICAgICAgICAgICAgICAgaWYgKHJlc291cmNlSWQpXG4gICAgICAgICAgICAgICAgICAgIHNlZ21lbnRzLnJlc291cmNlID0gcmVzb3VyY2VJZDtcbiAgICAgICAgICAgICAgICBjb25zdCBjb2x1bW5LZXkgPSB0aGlzLmRhdGVTZXJ2aWNlLmJ1aWxkQ29sdW1uS2V5KHNlZ21lbnRzKTtcbiAgICAgICAgICAgICAgICAvLyBIZWFkZXJcbiAgICAgICAgICAgICAgICBjb25zdCBoZWFkZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzd3AtZGF5LWhlYWRlcicpO1xuICAgICAgICAgICAgICAgIGhlYWRlci5kYXRhc2V0LmRhdGUgPSBkYXRlU3RyO1xuICAgICAgICAgICAgICAgIGhlYWRlci5kYXRhc2V0LmNvbHVtbktleSA9IGNvbHVtbktleTtcbiAgICAgICAgICAgICAgICBpZiAocmVzb3VyY2VJZCkge1xuICAgICAgICAgICAgICAgICAgICBoZWFkZXIuZGF0YXNldC5yZXNvdXJjZUlkID0gcmVzb3VyY2VJZDtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgaWYgKGhpZGVIZWFkZXIpIHtcbiAgICAgICAgICAgICAgICAgICAgaGVhZGVyLmRhdGFzZXQuaGlkZGVuID0gJ3RydWUnO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBoZWFkZXIuaW5uZXJIVE1MID0gYFxyXG4gICAgICAgICAgPHN3cC1kYXktbmFtZT4ke3RoaXMuZGF0ZVNlcnZpY2UuZ2V0RGF5TmFtZShkYXRlLCAnc2hvcnQnKX08L3N3cC1kYXktbmFtZT5cclxuICAgICAgICAgIDxzd3AtZGF5LWRhdGU+JHtkYXRlLmdldERhdGUoKX08L3N3cC1kYXktZGF0ZT5cclxuICAgICAgICBgO1xuICAgICAgICAgICAgICAgIGNvbnRleHQuaGVhZGVyQ29udGFpbmVyLmFwcGVuZENoaWxkKGhlYWRlcik7XG4gICAgICAgICAgICAgICAgLy8gQ29sdW1uXG4gICAgICAgICAgICAgICAgY29uc3QgY29sdW1uID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3dwLWRheS1jb2x1bW4nKTtcbiAgICAgICAgICAgICAgICBjb2x1bW4uZGF0YXNldC5kYXRlID0gZGF0ZVN0cjtcbiAgICAgICAgICAgICAgICBjb2x1bW4uZGF0YXNldC5jb2x1bW5LZXkgPSBjb2x1bW5LZXk7XG4gICAgICAgICAgICAgICAgaWYgKHJlc291cmNlSWQpIHtcbiAgICAgICAgICAgICAgICAgICAgY29sdW1uLmRhdGFzZXQucmVzb3VyY2VJZCA9IHJlc291cmNlSWQ7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGNvbHVtbi5pbm5lckhUTUwgPSAnPHN3cC1ldmVudHMtbGF5ZXI+PC9zd3AtZXZlbnRzLWxheWVyPic7XG4gICAgICAgICAgICAgICAgY29udGV4dC5jb2x1bW5Db250YWluZXIuYXBwZW5kQ2hpbGQoY29sdW1uKTtcbiAgICAgICAgICAgICAgICBjb2x1bW5Db3VudCsrO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIC8vIFNldCBncmlkIGNvbHVtbnMgb24gY29udGFpbmVyXG4gICAgICAgIGNvbnN0IGNvbnRhaW5lciA9IGNvbnRleHQuY29sdW1uQ29udGFpbmVyLmNsb3Nlc3QoJ3N3cC1jYWxlbmRhci1jb250YWluZXInKTtcbiAgICAgICAgaWYgKGNvbnRhaW5lcikge1xuICAgICAgICAgICAgY29udGFpbmVyLnN0eWxlLnNldFByb3BlcnR5KCctLWdyaWQtY29sdW1ucycsIFN0cmluZyhjb2x1bW5Db3VudCkpO1xuICAgICAgICB9XG4gICAgfVxufVxuIiwgImltcG9ydCBkYXlqcyBmcm9tICdkYXlqcyc7XG5pbXBvcnQgdXRjIGZyb20gJ2RheWpzL3BsdWdpbi91dGMnO1xuaW1wb3J0IHRpbWV6b25lIGZyb20gJ2RheWpzL3BsdWdpbi90aW1lem9uZSc7XG5pbXBvcnQgaXNvV2VlayBmcm9tICdkYXlqcy9wbHVnaW4vaXNvV2Vlayc7XG4vLyBFbmFibGUgZGF5anMgcGx1Z2luc1xuZGF5anMuZXh0ZW5kKHV0Yyk7XG5kYXlqcy5leHRlbmQodGltZXpvbmUpO1xuZGF5anMuZXh0ZW5kKGlzb1dlZWspO1xuZXhwb3J0IGNsYXNzIERhdGVTZXJ2aWNlIHtcbiAgICBjb25zdHJ1Y3Rvcihjb25maWcsIGJhc2VEYXRlKSB7XG4gICAgICAgIHRoaXMuY29uZmlnID0gY29uZmlnO1xuICAgICAgICB0aGlzLnRpbWV6b25lID0gY29uZmlnLnRpbWV6b25lO1xuICAgICAgICAvLyBBbGxvdyBzZXR0aW5nIGEgZml4ZWQgYmFzZSBkYXRlIGZvciBkZW1vL3Rlc3RpbmcgcHVycG9zZXNcbiAgICAgICAgdGhpcy5iYXNlRGF0ZSA9IGJhc2VEYXRlID8gZGF5anMoYmFzZURhdGUpIDogZGF5anMoKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogU2V0IGEgZml4ZWQgYmFzZSBkYXRlICh1c2VmdWwgZm9yIGRlbW9zIHdpdGggc3RhdGljIG1vY2sgZGF0YSlcbiAgICAgKi9cbiAgICBzZXRCYXNlRGF0ZShkYXRlKSB7XG4gICAgICAgIHRoaXMuYmFzZURhdGUgPSBkYXlqcyhkYXRlKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogR2V0IHRoZSBjdXJyZW50IGJhc2UgZGF0ZSAoZWl0aGVyIGZpeGVkIG9yIHRvZGF5KVxuICAgICAqL1xuICAgIGdldEJhc2VEYXRlKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5iYXNlRGF0ZS50b0RhdGUoKTtcbiAgICB9XG4gICAgcGFyc2VJU08oaXNvU3RyaW5nKSB7XG4gICAgICAgIHJldHVybiBkYXlqcyhpc29TdHJpbmcpLnRvRGF0ZSgpO1xuICAgIH1cbiAgICBnZXREYXlOYW1lKGRhdGUsIGZvcm1hdCA9ICdzaG9ydCcpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBJbnRsLkRhdGVUaW1lRm9ybWF0KHRoaXMuY29uZmlnLmxvY2FsZSwgeyB3ZWVrZGF5OiBmb3JtYXQgfSkuZm9ybWF0KGRhdGUpO1xuICAgIH1cbiAgICBnZXRXZWVrRGF0ZXMob2Zmc2V0ID0gMCwgZGF5cyA9IDcpIHtcbiAgICAgICAgY29uc3QgbW9uZGF5ID0gdGhpcy5iYXNlRGF0ZS5zdGFydE9mKCd3ZWVrJykuYWRkKDEsICdkYXknKS5hZGQob2Zmc2V0LCAnd2VlaycpO1xuICAgICAgICByZXR1cm4gQXJyYXkuZnJvbSh7IGxlbmd0aDogZGF5cyB9LCAoXywgaSkgPT4gbW9uZGF5LmFkZChpLCAnZGF5JykuZm9ybWF0KCdZWVlZLU1NLUREJykpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBHZXQgZGF0ZXMgZm9yIHNwZWNpZmljIHdlZWtkYXlzIHdpdGhpbiBhIHdlZWtcbiAgICAgKiBAcGFyYW0gb2Zmc2V0IC0gV2VlayBvZmZzZXQgZnJvbSBiYXNlIGRhdGUgKDAgPSBjdXJyZW50IHdlZWspXG4gICAgICogQHBhcmFtIHdvcmtEYXlzIC0gQXJyYXkgb2YgSVNPIHdlZWtkYXkgbnVtYmVycyAoMT1Nb25kYXksIDc9U3VuZGF5KVxuICAgICAqIEByZXR1cm5zIEFycmF5IG9mIGRhdGUgc3RyaW5ncyBpbiBZWVlZLU1NLUREIGZvcm1hdFxuICAgICAqL1xuICAgIGdldFdvcmtXZWVrRGF0ZXMob2Zmc2V0LCB3b3JrRGF5cykge1xuICAgICAgICBjb25zdCBtb25kYXkgPSB0aGlzLmJhc2VEYXRlLnN0YXJ0T2YoJ3dlZWsnKS5hZGQoMSwgJ2RheScpLmFkZChvZmZzZXQsICd3ZWVrJyk7XG4gICAgICAgIHJldHVybiB3b3JrRGF5cy5tYXAoaXNvRGF5ID0+IHtcbiAgICAgICAgICAgIC8vIElTTzogMT1Nb25kYXksIDc9U3VuZGF5IFx1MjE5MiBkYXlzIGZyb20gTW9uZGF5OiAwLTZcbiAgICAgICAgICAgIGNvbnN0IGRheXNGcm9tTW9uZGF5ID0gaXNvRGF5ID09PSA3ID8gNiA6IGlzb0RheSAtIDE7XG4gICAgICAgICAgICByZXR1cm4gbW9uZGF5LmFkZChkYXlzRnJvbU1vbmRheSwgJ2RheScpLmZvcm1hdCgnWVlZWS1NTS1ERCcpO1xuICAgICAgICB9KTtcbiAgICB9XG4gICAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgICAvLyBGT1JNQVRUSU5HXG4gICAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgICBmb3JtYXRUaW1lKGRhdGUsIHNob3dTZWNvbmRzID0gZmFsc2UpIHtcbiAgICAgICAgY29uc3QgcGF0dGVybiA9IHNob3dTZWNvbmRzID8gJ0hIOm1tOnNzJyA6ICdISDptbSc7XG4gICAgICAgIHJldHVybiBkYXlqcyhkYXRlKS5mb3JtYXQocGF0dGVybik7XG4gICAgfVxuICAgIGZvcm1hdFRpbWVSYW5nZShzdGFydCwgZW5kKSB7XG4gICAgICAgIHJldHVybiBgJHt0aGlzLmZvcm1hdFRpbWUoc3RhcnQpfSAtICR7dGhpcy5mb3JtYXRUaW1lKGVuZCl9YDtcbiAgICB9XG4gICAgZm9ybWF0RGF0ZShkYXRlKSB7XG4gICAgICAgIHJldHVybiBkYXlqcyhkYXRlKS5mb3JtYXQoJ1lZWVktTU0tREQnKTtcbiAgICB9XG4gICAgZ2V0RGF0ZUtleShkYXRlKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmZvcm1hdERhdGUoZGF0ZSk7XG4gICAgfVxuICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gICAgLy8gQ09MVU1OIEtFWVxuICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gICAgLyoqXG4gICAgICogQnVpbGQgYSB1bmlmb3JtIGNvbHVtbktleSBmcm9tIGdyb3VwaW5nIHNlZ21lbnRzXG4gICAgICogSGFuZGxlcyBhbnkgY29tYmluYXRpb24gb2YgZGF0ZSwgcmVzb3VyY2UsIHRlYW0sIGV0Yy5cbiAgICAgKlxuICAgICAqIEBleGFtcGxlXG4gICAgICogYnVpbGRDb2x1bW5LZXkoeyBkYXRlOiAnMjAyNS0xMi0wOScgfSkgXHUyMTkyIFwiMjAyNS0xMi0wOVwiXG4gICAgICogYnVpbGRDb2x1bW5LZXkoeyBkYXRlOiAnMjAyNS0xMi0wOScsIHJlc291cmNlOiAnRU1QMDAxJyB9KSBcdTIxOTIgXCIyMDI1LTEyLTA5OkVNUDAwMVwiXG4gICAgICovXG4gICAgYnVpbGRDb2x1bW5LZXkoc2VnbWVudHMpIHtcbiAgICAgICAgLy8gQWx3YXlzIHB1dCBkYXRlIGZpcnN0IGlmIHByZXNlbnQsIHRoZW4gb3RoZXIgc2VnbWVudHMgYWxwaGFiZXRpY2FsbHlcbiAgICAgICAgY29uc3QgZGF0ZSA9IHNlZ21lbnRzLmRhdGU7XG4gICAgICAgIGNvbnN0IG90aGVycyA9IE9iamVjdC5lbnRyaWVzKHNlZ21lbnRzKVxuICAgICAgICAgICAgLmZpbHRlcigoW2tdKSA9PiBrICE9PSAnZGF0ZScpXG4gICAgICAgICAgICAuc29ydCgoW2FdLCBbYl0pID0+IGEubG9jYWxlQ29tcGFyZShiKSlcbiAgICAgICAgICAgIC5tYXAoKFssIHZdKSA9PiB2KTtcbiAgICAgICAgcmV0dXJuIGRhdGUgPyBbZGF0ZSwgLi4ub3RoZXJzXS5qb2luKCc6JykgOiBvdGhlcnMuam9pbignOicpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBQYXJzZSBhIGNvbHVtbktleSBiYWNrIGludG8gc2VnbWVudHNcbiAgICAgKiBBc3N1bWVzIGZvcm1hdDogXCJkYXRlOnJlc291cmNlOi4uLlwiIG9yIGp1c3QgXCJkYXRlXCJcbiAgICAgKi9cbiAgICBwYXJzZUNvbHVtbktleShjb2x1bW5LZXkpIHtcbiAgICAgICAgY29uc3QgcGFydHMgPSBjb2x1bW5LZXkuc3BsaXQoJzonKTtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIGRhdGU6IHBhcnRzWzBdLFxuICAgICAgICAgICAgcmVzb3VyY2U6IHBhcnRzWzFdXG4gICAgICAgIH07XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEV4dHJhY3QgZGF0ZUtleSBmcm9tIGNvbHVtbktleSAoZmlyc3Qgc2VnbWVudClcbiAgICAgKi9cbiAgICBnZXREYXRlRnJvbUNvbHVtbktleShjb2x1bW5LZXkpIHtcbiAgICAgICAgcmV0dXJuIGNvbHVtbktleS5zcGxpdCgnOicpWzBdO1xuICAgIH1cbiAgICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAgIC8vIFRJTUUgQ0FMQ1VMQVRJT05TXG4gICAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgICB0aW1lVG9NaW51dGVzKHRpbWVTdHJpbmcpIHtcbiAgICAgICAgY29uc3QgcGFydHMgPSB0aW1lU3RyaW5nLnNwbGl0KCc6JykubWFwKE51bWJlcik7XG4gICAgICAgIGNvbnN0IGhvdXJzID0gcGFydHNbMF0gfHwgMDtcbiAgICAgICAgY29uc3QgbWludXRlcyA9IHBhcnRzWzFdIHx8IDA7XG4gICAgICAgIHJldHVybiBob3VycyAqIDYwICsgbWludXRlcztcbiAgICB9XG4gICAgbWludXRlc1RvVGltZSh0b3RhbE1pbnV0ZXMpIHtcbiAgICAgICAgY29uc3QgaG91cnMgPSBNYXRoLmZsb29yKHRvdGFsTWludXRlcyAvIDYwKTtcbiAgICAgICAgY29uc3QgbWludXRlcyA9IHRvdGFsTWludXRlcyAlIDYwO1xuICAgICAgICByZXR1cm4gZGF5anMoKS5ob3VyKGhvdXJzKS5taW51dGUobWludXRlcykuZm9ybWF0KCdISDptbScpO1xuICAgIH1cbiAgICBnZXRNaW51dGVzU2luY2VNaWRuaWdodChkYXRlKSB7XG4gICAgICAgIGNvbnN0IGQgPSBkYXlqcyhkYXRlKTtcbiAgICAgICAgcmV0dXJuIGQuaG91cigpICogNjAgKyBkLm1pbnV0ZSgpO1xuICAgIH1cbiAgICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAgIC8vIFVUQyBDT05WRVJTSU9OU1xuICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gICAgdG9VVEMobG9jYWxEYXRlKSB7XG4gICAgICAgIHJldHVybiBkYXlqcy50eihsb2NhbERhdGUsIHRoaXMudGltZXpvbmUpLnV0YygpLnRvSVNPU3RyaW5nKCk7XG4gICAgfVxuICAgIGZyb21VVEModXRjU3RyaW5nKSB7XG4gICAgICAgIHJldHVybiBkYXlqcy51dGModXRjU3RyaW5nKS50eih0aGlzLnRpbWV6b25lKS50b0RhdGUoKTtcbiAgICB9XG4gICAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgICAvLyBEQVRFIENSRUFUSU9OXG4gICAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgICBjcmVhdGVEYXRlQXRUaW1lKGJhc2VEYXRlLCB0aW1lU3RyaW5nKSB7XG4gICAgICAgIGNvbnN0IHRvdGFsTWludXRlcyA9IHRoaXMudGltZVRvTWludXRlcyh0aW1lU3RyaW5nKTtcbiAgICAgICAgY29uc3QgaG91cnMgPSBNYXRoLmZsb29yKHRvdGFsTWludXRlcyAvIDYwKTtcbiAgICAgICAgY29uc3QgbWludXRlcyA9IHRvdGFsTWludXRlcyAlIDYwO1xuICAgICAgICByZXR1cm4gZGF5anMoYmFzZURhdGUpLnN0YXJ0T2YoJ2RheScpLmhvdXIoaG91cnMpLm1pbnV0ZShtaW51dGVzKS50b0RhdGUoKTtcbiAgICB9XG4gICAgZ2V0SVNPV2Vla0RheShkYXRlKSB7XG4gICAgICAgIHJldHVybiBkYXlqcyhkYXRlKS5pc29XZWVrZGF5KCk7IC8vIDE9TW9uZGF5LCA3PVN1bmRheVxuICAgIH1cbn1cbiIsICIvKipcbiAqIEFic3RyYWN0IGJhc2UgY2xhc3MgZm9yIGdyb3VwaW5nIHJlbmRlcmVyc1xuICpcbiAqIEhhbmRsZXM6XG4gKiAtIEZldGNoaW5nIGVudGl0aWVzIGJ5IElEc1xuICogLSBDYWxjdWxhdGluZyBjb2xzcGFuIGZyb20gcGFyZW50Q2hpbGRNYXBcbiAqIC0gQ3JlYXRpbmcgaGVhZGVyIGVsZW1lbnRzXG4gKiAtIEFwcGVuZGluZyB0byBjb250YWluZXJcbiAqXG4gKiBTdWJjbGFzc2VzIG92ZXJyaWRlOlxuICogLSByZW5kZXJIZWFkZXIoKSBmb3IgY3VzdG9tIGNvbnRlbnRcbiAqIC0gZ2V0RGlzcGxheU5hbWUoKSBmb3IgZW50aXR5IGRpc3BsYXkgdGV4dFxuICovXG5leHBvcnQgY2xhc3MgQmFzZUdyb3VwaW5nUmVuZGVyZXIge1xuICAgIC8qKlxuICAgICAqIE1haW4gcmVuZGVyIG1ldGhvZCAtIGhhbmRsZXMgY29tbW9uIGxvZ2ljXG4gICAgICovXG4gICAgYXN5bmMgcmVuZGVyKGNvbnRleHQpIHtcbiAgICAgICAgY29uc3QgYWxsb3dlZElkcyA9IGNvbnRleHQuZmlsdGVyW3RoaXMudHlwZV0gfHwgW107XG4gICAgICAgIGlmIChhbGxvd2VkSWRzLmxlbmd0aCA9PT0gMClcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgY29uc3QgZW50aXRpZXMgPSBhd2FpdCB0aGlzLmdldEVudGl0aWVzKGFsbG93ZWRJZHMpO1xuICAgICAgICBjb25zdCBkYXRlQ291bnQgPSBjb250ZXh0LmZpbHRlclsnZGF0ZSddPy5sZW5ndGggfHwgMTtcbiAgICAgICAgY29uc3QgY2hpbGRJZHMgPSBjb250ZXh0LmNoaWxkVHlwZSA/IGNvbnRleHQuZmlsdGVyW2NvbnRleHQuY2hpbGRUeXBlXSB8fCBbXSA6IFtdO1xuICAgICAgICBmb3IgKGNvbnN0IGVudGl0eSBvZiBlbnRpdGllcykge1xuICAgICAgICAgICAgY29uc3QgZW50aXR5Q2hpbGRJZHMgPSBjb250ZXh0LnBhcmVudENoaWxkTWFwPy5bZW50aXR5LmlkXSB8fCBbXTtcbiAgICAgICAgICAgIGNvbnN0IGNoaWxkQ291bnQgPSBlbnRpdHlDaGlsZElkcy5maWx0ZXIoaWQgPT4gY2hpbGRJZHMuaW5jbHVkZXMoaWQpKS5sZW5ndGg7XG4gICAgICAgICAgICBjb25zdCBjb2xzcGFuID0gY2hpbGRDb3VudCAqIGRhdGVDb3VudDtcbiAgICAgICAgICAgIGNvbnN0IGhlYWRlciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQodGhpcy5jb25maWcuZWxlbWVudFRhZyk7XG4gICAgICAgICAgICBoZWFkZXIuZGF0YXNldFt0aGlzLmNvbmZpZy5pZEF0dHJpYnV0ZV0gPSBlbnRpdHkuaWQ7XG4gICAgICAgICAgICBoZWFkZXIuc3R5bGUuc2V0UHJvcGVydHkodGhpcy5jb25maWcuY29sc3BhblZhciwgU3RyaW5nKGNvbHNwYW4pKTtcbiAgICAgICAgICAgIC8vIEFsbG93IHN1YmNsYXNzIHRvIGN1c3RvbWl6ZSBoZWFkZXIgY29udGVudFxuICAgICAgICAgICAgdGhpcy5yZW5kZXJIZWFkZXIoZW50aXR5LCBoZWFkZXIsIGNvbnRleHQpO1xuICAgICAgICAgICAgY29udGV4dC5oZWFkZXJDb250YWluZXIuYXBwZW5kQ2hpbGQoaGVhZGVyKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICAvKipcbiAgICAgKiBPdmVycmlkZSB0aGlzIG1ldGhvZCBmb3IgY3VzdG9tIGhlYWRlciByZW5kZXJpbmdcbiAgICAgKiBEZWZhdWx0OiBqdXN0IHNldHMgdGV4dENvbnRlbnQgdG8gZGlzcGxheSBuYW1lXG4gICAgICovXG4gICAgcmVuZGVySGVhZGVyKGVudGl0eSwgaGVhZGVyLCBfY29udGV4dCkge1xuICAgICAgICBoZWFkZXIudGV4dENvbnRlbnQgPSB0aGlzLmdldERpc3BsYXlOYW1lKGVudGl0eSk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEhlbHBlciB0byByZW5kZXIgYSBzaW5nbGUgZW50aXR5IGhlYWRlci5cbiAgICAgKiBDYW4gYmUgdXNlZCBieSBzdWJjbGFzc2VzIHRoYXQgb3ZlcnJpZGUgcmVuZGVyKCkgYnV0IHdhbnQgY29uc2lzdGVudCBoZWFkZXIgY3JlYXRpb24uXG4gICAgICovXG4gICAgY3JlYXRlSGVhZGVyKGVudGl0eSwgY29udGV4dCkge1xuICAgICAgICBjb25zdCBoZWFkZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KHRoaXMuY29uZmlnLmVsZW1lbnRUYWcpO1xuICAgICAgICBoZWFkZXIuZGF0YXNldFt0aGlzLmNvbmZpZy5pZEF0dHJpYnV0ZV0gPSBlbnRpdHkuaWQ7XG4gICAgICAgIHRoaXMucmVuZGVySGVhZGVyKGVudGl0eSwgaGVhZGVyLCBjb250ZXh0KTtcbiAgICAgICAgcmV0dXJuIGhlYWRlcjtcbiAgICB9XG59XG4iLCAiaW1wb3J0IHsgQmFzZUdyb3VwaW5nUmVuZGVyZXIgfSBmcm9tICcuLi8uLi9jb3JlL0Jhc2VHcm91cGluZ1JlbmRlcmVyJztcbmV4cG9ydCBjbGFzcyBSZXNvdXJjZVJlbmRlcmVyIGV4dGVuZHMgQmFzZUdyb3VwaW5nUmVuZGVyZXIge1xuICAgIGNvbnN0cnVjdG9yKHJlc291cmNlU2VydmljZSkge1xuICAgICAgICBzdXBlcigpO1xuICAgICAgICB0aGlzLnJlc291cmNlU2VydmljZSA9IHJlc291cmNlU2VydmljZTtcbiAgICAgICAgdGhpcy50eXBlID0gJ3Jlc291cmNlJztcbiAgICAgICAgdGhpcy5jb25maWcgPSB7XG4gICAgICAgICAgICBlbGVtZW50VGFnOiAnc3dwLXJlc291cmNlLWhlYWRlcicsXG4gICAgICAgICAgICBpZEF0dHJpYnV0ZTogJ3Jlc291cmNlSWQnLFxuICAgICAgICAgICAgY29sc3BhblZhcjogJy0tcmVzb3VyY2UtY29scydcbiAgICAgICAgfTtcbiAgICB9XG4gICAgZ2V0RW50aXRpZXMoaWRzKSB7XG4gICAgICAgIHJldHVybiB0aGlzLnJlc291cmNlU2VydmljZS5nZXRCeUlkcyhpZHMpO1xuICAgIH1cbiAgICBnZXREaXNwbGF5TmFtZShlbnRpdHkpIHtcbiAgICAgICAgcmV0dXJuIGVudGl0eS5kaXNwbGF5TmFtZTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogT3ZlcnJpZGUgcmVuZGVyIHRvIGhhbmRsZTpcbiAgICAgKiAxLiBTcGVjaWFsIG9yZGVyaW5nIHdoZW4gcGFyZW50Q2hpbGRNYXAgZXhpc3RzIChyZXNvdXJjZXMgZ3JvdXBlZCBieSBwYXJlbnQpXG4gICAgICogMi4gRGlmZmVyZW50IGNvbHNwYW4gY2FsY3VsYXRpb24gKGp1c3QgZGF0ZUNvdW50LCBub3QgY2hpbGRDb3VudCAqIGRhdGVDb3VudClcbiAgICAgKi9cbiAgICBhc3luYyByZW5kZXIoY29udGV4dCkge1xuICAgICAgICBjb25zdCByZXNvdXJjZUlkcyA9IGNvbnRleHQuZmlsdGVyWydyZXNvdXJjZSddIHx8IFtdO1xuICAgICAgICBjb25zdCBkYXRlQ291bnQgPSBjb250ZXh0LmZpbHRlclsnZGF0ZSddPy5sZW5ndGggfHwgMTtcbiAgICAgICAgLy8gRGV0ZXJtaW5lIHJlbmRlciBvcmRlciBiYXNlZCBvbiBwYXJlbnRDaGlsZE1hcFxuICAgICAgICAvLyBJZiBwYXJlbnRDaGlsZE1hcCBleGlzdHMsIHJlbmRlciByZXNvdXJjZXMgZ3JvdXBlZCBieSBwYXJlbnQgKGUuZy4sIHRlYW0pXG4gICAgICAgIC8vIE90aGVyd2lzZSwgcmVuZGVyIGluIGZpbHRlciBvcmRlclxuICAgICAgICBsZXQgb3JkZXJlZFJlc291cmNlSWRzO1xuICAgICAgICBpZiAoY29udGV4dC5wYXJlbnRDaGlsZE1hcCkge1xuICAgICAgICAgICAgLy8gUmVuZGVyIHJlc291cmNlcyBpbiBwYXJlbnQtY2hpbGQgb3JkZXJcbiAgICAgICAgICAgIG9yZGVyZWRSZXNvdXJjZUlkcyA9IFtdO1xuICAgICAgICAgICAgZm9yIChjb25zdCBjaGlsZElkcyBvZiBPYmplY3QudmFsdWVzKGNvbnRleHQucGFyZW50Q2hpbGRNYXApKSB7XG4gICAgICAgICAgICAgICAgZm9yIChjb25zdCBjaGlsZElkIG9mIGNoaWxkSWRzKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmIChyZXNvdXJjZUlkcy5pbmNsdWRlcyhjaGlsZElkKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXJlZFJlc291cmNlSWRzLnB1c2goY2hpbGRJZCk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICBvcmRlcmVkUmVzb3VyY2VJZHMgPSByZXNvdXJjZUlkcztcbiAgICAgICAgfVxuICAgICAgICBjb25zdCByZXNvdXJjZXMgPSBhd2FpdCB0aGlzLmdldEVudGl0aWVzKG9yZGVyZWRSZXNvdXJjZUlkcyk7XG4gICAgICAgIC8vIENyZWF0ZSBhIG1hcCBmb3IgcXVpY2sgbG9va3VwIHRvIHByZXNlcnZlIG9yZGVyXG4gICAgICAgIGNvbnN0IHJlc291cmNlTWFwID0gbmV3IE1hcChyZXNvdXJjZXMubWFwKHIgPT4gW3IuaWQsIHJdKSk7XG4gICAgICAgIGZvciAoY29uc3QgcmVzb3VyY2VJZCBvZiBvcmRlcmVkUmVzb3VyY2VJZHMpIHtcbiAgICAgICAgICAgIGNvbnN0IHJlc291cmNlID0gcmVzb3VyY2VNYXAuZ2V0KHJlc291cmNlSWQpO1xuICAgICAgICAgICAgaWYgKCFyZXNvdXJjZSlcbiAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgIGNvbnN0IGhlYWRlciA9IHRoaXMuY3JlYXRlSGVhZGVyKHJlc291cmNlLCBjb250ZXh0KTtcbiAgICAgICAgICAgIGhlYWRlci5zdHlsZS5ncmlkQ29sdW1uID0gYHNwYW4gJHtkYXRlQ291bnR9YDtcbiAgICAgICAgICAgIGNvbnRleHQuaGVhZGVyQ29udGFpbmVyLmFwcGVuZENoaWxkKGhlYWRlcik7XG4gICAgICAgIH1cbiAgICB9XG59XG4iLCAiaW1wb3J0IHsgQmFzZUdyb3VwaW5nUmVuZGVyZXIgfSBmcm9tICcuLi8uLi9jb3JlL0Jhc2VHcm91cGluZ1JlbmRlcmVyJztcbmV4cG9ydCBjbGFzcyBUZWFtUmVuZGVyZXIgZXh0ZW5kcyBCYXNlR3JvdXBpbmdSZW5kZXJlciB7XG4gICAgY29uc3RydWN0b3IodGVhbVNlcnZpY2UpIHtcbiAgICAgICAgc3VwZXIoKTtcbiAgICAgICAgdGhpcy50ZWFtU2VydmljZSA9IHRlYW1TZXJ2aWNlO1xuICAgICAgICB0aGlzLnR5cGUgPSAndGVhbSc7XG4gICAgICAgIHRoaXMuY29uZmlnID0ge1xuICAgICAgICAgICAgZWxlbWVudFRhZzogJ3N3cC10ZWFtLWhlYWRlcicsXG4gICAgICAgICAgICBpZEF0dHJpYnV0ZTogJ3RlYW1JZCcsXG4gICAgICAgICAgICBjb2xzcGFuVmFyOiAnLS10ZWFtLWNvbHMnXG4gICAgICAgIH07XG4gICAgfVxuICAgIGdldEVudGl0aWVzKGlkcykge1xuICAgICAgICByZXR1cm4gdGhpcy50ZWFtU2VydmljZS5nZXRCeUlkcyhpZHMpO1xuICAgIH1cbiAgICBnZXREaXNwbGF5TmFtZShlbnRpdHkpIHtcbiAgICAgICAgcmV0dXJuIGVudGl0eS5uYW1lO1xuICAgIH1cbn1cbiIsICJpbXBvcnQgeyBCYXNlR3JvdXBpbmdSZW5kZXJlciB9IGZyb20gJy4uLy4uL2NvcmUvQmFzZUdyb3VwaW5nUmVuZGVyZXInO1xuZXhwb3J0IGNsYXNzIERlcGFydG1lbnRSZW5kZXJlciBleHRlbmRzIEJhc2VHcm91cGluZ1JlbmRlcmVyIHtcbiAgICBjb25zdHJ1Y3RvcihkZXBhcnRtZW50U2VydmljZSkge1xuICAgICAgICBzdXBlcigpO1xuICAgICAgICB0aGlzLmRlcGFydG1lbnRTZXJ2aWNlID0gZGVwYXJ0bWVudFNlcnZpY2U7XG4gICAgICAgIHRoaXMudHlwZSA9ICdkZXBhcnRtZW50JztcbiAgICAgICAgdGhpcy5jb25maWcgPSB7XG4gICAgICAgICAgICBlbGVtZW50VGFnOiAnc3dwLWRlcGFydG1lbnQtaGVhZGVyJyxcbiAgICAgICAgICAgIGlkQXR0cmlidXRlOiAnZGVwYXJ0bWVudElkJyxcbiAgICAgICAgICAgIGNvbHNwYW5WYXI6ICctLWRlcGFydG1lbnQtY29scydcbiAgICAgICAgfTtcbiAgICB9XG4gICAgZ2V0RW50aXRpZXMoaWRzKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmRlcGFydG1lbnRTZXJ2aWNlLmdldEJ5SWRzKGlkcyk7XG4gICAgfVxuICAgIGdldERpc3BsYXlOYW1lKGVudGl0eSkge1xuICAgICAgICByZXR1cm4gZW50aXR5Lm5hbWU7XG4gICAgfVxufVxuIiwgImV4cG9ydCBmdW5jdGlvbiBidWlsZFBpcGVsaW5lKHJlbmRlcmVycykge1xuICAgIHJldHVybiB7XG4gICAgICAgIGFzeW5jIHJ1bihjb250ZXh0KSB7XG4gICAgICAgICAgICBmb3IgKGNvbnN0IHJlbmRlcmVyIG9mIHJlbmRlcmVycykge1xuICAgICAgICAgICAgICAgIGF3YWl0IHJlbmRlcmVyLnJlbmRlcihjb250ZXh0KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH07XG59XG4iLCAiLyoqXG4gKiBGaWx0ZXJUZW1wbGF0ZSAtIEJ5Z2dlciBuXHUwMEY4Z2xlciB0aWwgZXZlbnQta29sb25uZSBtYXRjaGluZ1xuICpcbiAqIFZpZXdDb25maWcgZGVmaW5lcmVyIGh2aWxrZSBmZWx0ZXIgKGlkUHJvcGVydGllcykgZGVyIGluZGdcdTAwRTVyIGkga29sb25uZW5zIG5cdTAwRjhnbGUuXG4gKiBTYW1tZSB0ZW1wbGF0ZSBicnVnZXMgdGlsIGF0IGJ5Z2dlIG5cdTAwRjhnbGUgZm9yIGJcdTAwRTVkZSBrb2xvbm5lIG9nIGV2ZW50LlxuICpcbiAqIFN1cHBvcnRzIGRvdC1ub3RhdGlvbiBmb3IgaGllcmFyY2hpY2FsIHJlbGF0aW9uczpcbiAqIC0gJ3Jlc291cmNlLnRlYW1JZCcgXHUyMTkyIGxvb2tzIHVwIGV2ZW50LnJlc291cmNlSWQgXHUyMTkyIHJlc291cmNlIGVudGl0eSBcdTIxOTIgdGVhbUlkXG4gKlxuICogUHJpbmNpcDogS29sb25uZW5zIG5cdTAwRjhnbGUtdGVtcGxhdGUgYmVzdGVtbWVyIGh2YWQgZGVyIG1hdGNoZXMgcFx1MDBFNS5cbiAqXG4gKiBAc2VlIGRvY3MvZmlsdGVyLXRlbXBsYXRlLm1kXG4gKi9cbmV4cG9ydCBjbGFzcyBGaWx0ZXJUZW1wbGF0ZSB7XG4gICAgY29uc3RydWN0b3IoZGF0ZVNlcnZpY2UsIGVudGl0eVJlc29sdmVyKSB7XG4gICAgICAgIHRoaXMuZGF0ZVNlcnZpY2UgPSBkYXRlU2VydmljZTtcbiAgICAgICAgdGhpcy5lbnRpdHlSZXNvbHZlciA9IGVudGl0eVJlc29sdmVyO1xuICAgICAgICB0aGlzLmZpZWxkcyA9IFtdO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBUaWxmXHUwMEY4aiBmZWx0IHRpbCB0ZW1wbGF0ZVxuICAgICAqIEBwYXJhbSBpZFByb3BlcnR5IC0gUHJvcGVydHktbmF2biAoYnJ1Z2VzIHBcdTAwRTUgYlx1MDBFNWRlIGV2ZW50IG9nIGNvbHVtbi5kYXRhc2V0KVxuICAgICAqIEBwYXJhbSBkZXJpdmVkRnJvbSAtIEh2aXMgZmVsdGV0IHVkbGVkZXMgZnJhIGFuZGVuIHByb3BlcnR5IChmLmVrcy4gZGF0ZSBmcmEgc3RhcnQpXG4gICAgICovXG4gICAgYWRkRmllbGQoaWRQcm9wZXJ0eSwgZGVyaXZlZEZyb20pIHtcbiAgICAgICAgdGhpcy5maWVsZHMucHVzaCh7IGlkUHJvcGVydHksIGRlcml2ZWRGcm9tIH0pO1xuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9XG4gICAgLyoqXG4gICAgICogUGFyc2UgZG90LW5vdGF0aW9uIHN0cmluZyBpbnRvIGNvbXBvbmVudHNcbiAgICAgKiBAZXhhbXBsZSAncmVzb3VyY2UudGVhbUlkJyBcdTIxOTIgeyBlbnRpdHlUeXBlOiAncmVzb3VyY2UnLCBwcm9wZXJ0eTogJ3RlYW1JZCcsIGZvcmVpZ25LZXk6ICdyZXNvdXJjZUlkJyB9XG4gICAgICovXG4gICAgcGFyc2VEb3ROb3RhdGlvbihpZFByb3BlcnR5KSB7XG4gICAgICAgIGlmICghaWRQcm9wZXJ0eS5pbmNsdWRlcygnLicpKVxuICAgICAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICAgIGNvbnN0IFtlbnRpdHlUeXBlLCBwcm9wZXJ0eV0gPSBpZFByb3BlcnR5LnNwbGl0KCcuJyk7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBlbnRpdHlUeXBlLFxuICAgICAgICAgICAgcHJvcGVydHksXG4gICAgICAgICAgICBmb3JlaWduS2V5OiBlbnRpdHlUeXBlICsgJ0lkJyAvLyBDb252ZW50aW9uOiByZXNvdXJjZSBcdTIxOTIgcmVzb3VyY2VJZFxuICAgICAgICB9O1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBHZXQgZGF0YXNldCBrZXkgZm9yIGNvbHVtbiBsb29rdXBcbiAgICAgKiBGb3IgZG90LW5vdGF0aW9uICdyZXNvdXJjZS50ZWFtSWQnLCB3ZSBsb29rIGZvciAndGVhbUlkJyBpbiBkYXRhc2V0XG4gICAgICovXG4gICAgZ2V0RGF0YXNldEtleShpZFByb3BlcnR5KSB7XG4gICAgICAgIGNvbnN0IGRvdE5vdGF0aW9uID0gdGhpcy5wYXJzZURvdE5vdGF0aW9uKGlkUHJvcGVydHkpO1xuICAgICAgICBpZiAoZG90Tm90YXRpb24pIHtcbiAgICAgICAgICAgIHJldHVybiBkb3ROb3RhdGlvbi5wcm9wZXJ0eTsgLy8gJ3RlYW1JZCdcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gaWRQcm9wZXJ0eTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogQnlnIG5cdTAwRjhnbGUgZnJhIGtvbG9ubmVcbiAgICAgKiBMXHUwMEU2c2VyIHZcdTAwRTZyZGllciBmcmEgY29sdW1uLmRhdGFzZXRbaWRQcm9wZXJ0eV1cbiAgICAgKiBGb3IgZG90LW5vdGF0aW9uLCB1c2VzIHRoZSBwcm9wZXJ0eSBwYXJ0IChyZXNvdXJjZS50ZWFtSWQgXHUyMTkyIHRlYW1JZClcbiAgICAgKi9cbiAgICBidWlsZEtleUZyb21Db2x1bW4oY29sdW1uKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmZpZWxkc1xuICAgICAgICAgICAgLm1hcChmID0+IHtcbiAgICAgICAgICAgIGNvbnN0IGtleSA9IHRoaXMuZ2V0RGF0YXNldEtleShmLmlkUHJvcGVydHkpO1xuICAgICAgICAgICAgcmV0dXJuIGNvbHVtbi5kYXRhc2V0W2tleV0gfHwgJyc7XG4gICAgICAgIH0pXG4gICAgICAgICAgICAuam9pbignOicpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBCeWcgblx1MDBGOGdsZSBmcmEgZXZlbnRcbiAgICAgKiBMXHUwMEU2c2VyIHZcdTAwRTZyZGllciBmcmEgZXZlbnRbaWRQcm9wZXJ0eV0gZWxsZXIgdWRsZWRlciBmcmEgZGVyaXZlZEZyb21cbiAgICAgKiBGb3IgZG90LW5vdGF0aW9uLCByZXNvbHZlcyB2aWEgRW50aXR5UmVzb2x2ZXJcbiAgICAgKi9cbiAgICBidWlsZEtleUZyb21FdmVudChldmVudCkge1xuICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLWV4cGxpY2l0LWFueVxuICAgICAgICBjb25zdCBldmVudFJlY29yZCA9IGV2ZW50O1xuICAgICAgICByZXR1cm4gdGhpcy5maWVsZHNcbiAgICAgICAgICAgIC5tYXAoZiA9PiB7XG4gICAgICAgICAgICAvLyBDaGVjayBmb3IgZG90LW5vdGF0aW9uIChlLmcuLCAncmVzb3VyY2UudGVhbUlkJylcbiAgICAgICAgICAgIGNvbnN0IGRvdE5vdGF0aW9uID0gdGhpcy5wYXJzZURvdE5vdGF0aW9uKGYuaWRQcm9wZXJ0eSk7XG4gICAgICAgICAgICBpZiAoZG90Tm90YXRpb24pIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5yZXNvbHZlRG90Tm90YXRpb24oZXZlbnRSZWNvcmQsIGRvdE5vdGF0aW9uKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmIChmLmRlcml2ZWRGcm9tKSB7XG4gICAgICAgICAgICAgICAgLy8gVWRsZWQgdlx1MDBFNnJkaSAoZi5la3MuIGRhdGUgZnJhIHN0YXJ0KVxuICAgICAgICAgICAgICAgIGNvbnN0IHNvdXJjZVZhbHVlID0gZXZlbnRSZWNvcmRbZi5kZXJpdmVkRnJvbV07XG4gICAgICAgICAgICAgICAgaWYgKHNvdXJjZVZhbHVlIGluc3RhbmNlb2YgRGF0ZSkge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5kYXRlU2VydmljZS5nZXREYXRlS2V5KHNvdXJjZVZhbHVlKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgcmV0dXJuIFN0cmluZyhzb3VyY2VWYWx1ZSB8fCAnJyk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gU3RyaW5nKGV2ZW50UmVjb3JkW2YuaWRQcm9wZXJ0eV0gfHwgJycpO1xuICAgICAgICB9KVxuICAgICAgICAgICAgLmpvaW4oJzonKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogUmVzb2x2ZSBkb3Qtbm90YXRpb24gcmVmZXJlbmNlIHZpYSBFbnRpdHlSZXNvbHZlclxuICAgICAqL1xuICAgIHJlc29sdmVEb3ROb3RhdGlvbihldmVudFJlY29yZCwgZG90Tm90YXRpb24pIHtcbiAgICAgICAgaWYgKCF0aGlzLmVudGl0eVJlc29sdmVyKSB7XG4gICAgICAgICAgICBjb25zb2xlLndhcm4oYEZpbHRlclRlbXBsYXRlOiBFbnRpdHlSZXNvbHZlciByZXF1aXJlZCBmb3IgZG90LW5vdGF0aW9uICcke2RvdE5vdGF0aW9uLmVudGl0eVR5cGV9LiR7ZG90Tm90YXRpb24ucHJvcGVydHl9J2ApO1xuICAgICAgICAgICAgcmV0dXJuICcnO1xuICAgICAgICB9XG4gICAgICAgIC8vIEdldCBmb3JlaWduIGtleSB2YWx1ZSBmcm9tIGV2ZW50IChlLmcuLCByZXNvdXJjZUlkKVxuICAgICAgICBjb25zdCBmb3JlaWduSWQgPSBldmVudFJlY29yZFtkb3ROb3RhdGlvbi5mb3JlaWduS2V5XTtcbiAgICAgICAgaWYgKCFmb3JlaWduSWQpXG4gICAgICAgICAgICByZXR1cm4gJyc7XG4gICAgICAgIC8vIFJlc29sdmUgZW50aXR5XG4gICAgICAgIGNvbnN0IGVudGl0eSA9IHRoaXMuZW50aXR5UmVzb2x2ZXIucmVzb2x2ZShkb3ROb3RhdGlvbi5lbnRpdHlUeXBlLCBTdHJpbmcoZm9yZWlnbklkKSk7XG4gICAgICAgIGlmICghZW50aXR5KVxuICAgICAgICAgICAgcmV0dXJuICcnO1xuICAgICAgICAvLyBSZXR1cm4gcHJvcGVydHkgdmFsdWUgZnJvbSBlbnRpdHlcbiAgICAgICAgcmV0dXJuIFN0cmluZyhlbnRpdHlbZG90Tm90YXRpb24ucHJvcGVydHldIHx8ICcnKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogTWF0Y2ggZXZlbnQgbW9kIGtvbG9ubmVcbiAgICAgKi9cbiAgICBtYXRjaGVzKGV2ZW50LCBjb2x1bW4pIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuYnVpbGRLZXlGcm9tRXZlbnQoZXZlbnQpID09PSB0aGlzLmJ1aWxkS2V5RnJvbUNvbHVtbihjb2x1bW4pO1xuICAgIH1cbn1cbiIsICJpbXBvcnQgeyBidWlsZFBpcGVsaW5lIH0gZnJvbSAnLi9SZW5kZXJCdWlsZGVyJztcbmltcG9ydCB7IEZpbHRlclRlbXBsYXRlIH0gZnJvbSAnLi9GaWx0ZXJUZW1wbGF0ZSc7XG5leHBvcnQgY2xhc3MgQ2FsZW5kYXJPcmNoZXN0cmF0b3Ige1xuICAgIGNvbnN0cnVjdG9yKGFsbFJlbmRlcmVycywgZXZlbnRSZW5kZXJlciwgc2NoZWR1bGVSZW5kZXJlciwgaGVhZGVyRHJhd2VyUmVuZGVyZXIsIGRhdGVTZXJ2aWNlLCBlbnRpdHlTZXJ2aWNlcykge1xuICAgICAgICB0aGlzLmFsbFJlbmRlcmVycyA9IGFsbFJlbmRlcmVycztcbiAgICAgICAgdGhpcy5ldmVudFJlbmRlcmVyID0gZXZlbnRSZW5kZXJlcjtcbiAgICAgICAgdGhpcy5zY2hlZHVsZVJlbmRlcmVyID0gc2NoZWR1bGVSZW5kZXJlcjtcbiAgICAgICAgdGhpcy5oZWFkZXJEcmF3ZXJSZW5kZXJlciA9IGhlYWRlckRyYXdlclJlbmRlcmVyO1xuICAgICAgICB0aGlzLmRhdGVTZXJ2aWNlID0gZGF0ZVNlcnZpY2U7XG4gICAgICAgIHRoaXMuZW50aXR5U2VydmljZXMgPSBlbnRpdHlTZXJ2aWNlcztcbiAgICB9XG4gICAgYXN5bmMgcmVuZGVyKHZpZXdDb25maWcsIGNvbnRhaW5lcikge1xuICAgICAgICBjb25zdCBoZWFkZXJDb250YWluZXIgPSBjb250YWluZXIucXVlcnlTZWxlY3Rvcignc3dwLWNhbGVuZGFyLWhlYWRlcicpO1xuICAgICAgICBjb25zdCBjb2x1bW5Db250YWluZXIgPSBjb250YWluZXIucXVlcnlTZWxlY3Rvcignc3dwLWRheS1jb2x1bW5zJyk7XG4gICAgICAgIGlmICghaGVhZGVyQ29udGFpbmVyIHx8ICFjb2x1bW5Db250YWluZXIpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignTWlzc2luZyBzd3AtY2FsZW5kYXItaGVhZGVyIG9yIHN3cC1kYXktY29sdW1ucycpO1xuICAgICAgICB9XG4gICAgICAgIC8vIEJ5ZyBmaWx0ZXIgZnJhIHZpZXdDb25maWdcbiAgICAgICAgY29uc3QgZmlsdGVyID0ge307XG4gICAgICAgIGZvciAoY29uc3QgZ3JvdXBpbmcgb2Ygdmlld0NvbmZpZy5ncm91cGluZ3MpIHtcbiAgICAgICAgICAgIGZpbHRlcltncm91cGluZy50eXBlXSA9IGdyb3VwaW5nLnZhbHVlcztcbiAgICAgICAgfVxuICAgICAgICAvLyBCeWcgRmlsdGVyVGVtcGxhdGUgZnJhIHZpZXdDb25maWcgZ3JvdXBpbmdzIChrdW4gZGUgbWVkIGlkUHJvcGVydHkpXG4gICAgICAgIGNvbnN0IGZpbHRlclRlbXBsYXRlID0gbmV3IEZpbHRlclRlbXBsYXRlKHRoaXMuZGF0ZVNlcnZpY2UpO1xuICAgICAgICBmb3IgKGNvbnN0IGdyb3VwaW5nIG9mIHZpZXdDb25maWcuZ3JvdXBpbmdzKSB7XG4gICAgICAgICAgICBpZiAoZ3JvdXBpbmcuaWRQcm9wZXJ0eSkge1xuICAgICAgICAgICAgICAgIGZpbHRlclRlbXBsYXRlLmFkZEZpZWxkKGdyb3VwaW5nLmlkUHJvcGVydHksIGdyb3VwaW5nLmRlcml2ZWRGcm9tKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICAvLyBSZXNvbHZlIGJlbG9uZ3NUbyByZWxhdGlvbnMgKGUuZy4sIHRlYW0ucmVzb3VyY2VJZHMpXG4gICAgICAgIGNvbnN0IHsgcGFyZW50Q2hpbGRNYXAsIGNoaWxkVHlwZSB9ID0gYXdhaXQgdGhpcy5yZXNvbHZlQmVsb25nc1RvKHZpZXdDb25maWcuZ3JvdXBpbmdzLCBmaWx0ZXIpO1xuICAgICAgICBjb25zdCBjb250ZXh0ID0geyBoZWFkZXJDb250YWluZXIsIGNvbHVtbkNvbnRhaW5lciwgZmlsdGVyLCBncm91cGluZ3M6IHZpZXdDb25maWcuZ3JvdXBpbmdzLCBwYXJlbnRDaGlsZE1hcCwgY2hpbGRUeXBlIH07XG4gICAgICAgIC8vIENsZWFyXG4gICAgICAgIGhlYWRlckNvbnRhaW5lci5pbm5lckhUTUwgPSAnJztcbiAgICAgICAgY29sdW1uQ29udGFpbmVyLmlubmVySFRNTCA9ICcnO1xuICAgICAgICAvLyBTXHUwMEU2dCBkYXRhLWxldmVscyBhdHRyaWJ1dCBmb3IgQ1NTIGdyaWQtcm93IHN0eWxpbmdcbiAgICAgICAgY29uc3QgbGV2ZWxzID0gdmlld0NvbmZpZy5ncm91cGluZ3MubWFwKGcgPT4gZy50eXBlKS5qb2luKCcgJyk7XG4gICAgICAgIGhlYWRlckNvbnRhaW5lci5kYXRhc2V0LmxldmVscyA9IGxldmVscztcbiAgICAgICAgLy8gVlx1MDBFNmxnIHJlbmRlcmVycyBiYXNlcmV0IHBcdTAwRTUgZ3JvdXBpbmdzIHR5cGVzXG4gICAgICAgIGNvbnN0IGFjdGl2ZVJlbmRlcmVycyA9IHRoaXMuc2VsZWN0UmVuZGVyZXJzKHZpZXdDb25maWcpO1xuICAgICAgICAvLyBCeWcgb2cga1x1MDBGOHIgcGlwZWxpbmVcbiAgICAgICAgY29uc3QgcGlwZWxpbmUgPSBidWlsZFBpcGVsaW5lKGFjdGl2ZVJlbmRlcmVycyk7XG4gICAgICAgIGF3YWl0IHBpcGVsaW5lLnJ1bihjb250ZXh0KTtcbiAgICAgICAgLy8gUmVuZGVyIHNjaGVkdWxlIHVuYXZhaWxhYmxlIHpvbmVzIChmXHUwMEY4ciBldmVudHMpXG4gICAgICAgIGF3YWl0IHRoaXMuc2NoZWR1bGVSZW5kZXJlci5yZW5kZXIoY29udGFpbmVyLCBmaWx0ZXIpO1xuICAgICAgICAvLyBSZW5kZXIgdGltZWQgZXZlbnRzIGluIGdyaWQgKG1lZCBmaWx0ZXJUZW1wbGF0ZSB0aWwgbWF0Y2hpbmcpXG4gICAgICAgIGF3YWl0IHRoaXMuZXZlbnRSZW5kZXJlci5yZW5kZXIoY29udGFpbmVyLCBmaWx0ZXIsIGZpbHRlclRlbXBsYXRlKTtcbiAgICAgICAgLy8gUmVuZGVyIGFsbERheSBldmVudHMgaW4gaGVhZGVyIGRyYXdlciAobWVkIGZpbHRlclRlbXBsYXRlIHRpbCBtYXRjaGluZylcbiAgICAgICAgYXdhaXQgdGhpcy5oZWFkZXJEcmF3ZXJSZW5kZXJlci5yZW5kZXIoY29udGFpbmVyLCBmaWx0ZXIsIGZpbHRlclRlbXBsYXRlKTtcbiAgICB9XG4gICAgc2VsZWN0UmVuZGVyZXJzKHZpZXdDb25maWcpIHtcbiAgICAgICAgY29uc3QgdHlwZXMgPSB2aWV3Q29uZmlnLmdyb3VwaW5ncy5tYXAoZyA9PiBnLnR5cGUpO1xuICAgICAgICAvLyBTb3J0XHUwMEU5ciByZW5kZXJlcnMgaSBzYW1tZSByXHUwMEU2a2tlZlx1MDBGOGxnZSBzb20gdmlld0NvbmZpZy5ncm91cGluZ3NcbiAgICAgICAgcmV0dXJuIHR5cGVzXG4gICAgICAgICAgICAubWFwKHR5cGUgPT4gdGhpcy5hbGxSZW5kZXJlcnMuZmluZChyID0+IHIudHlwZSA9PT0gdHlwZSkpXG4gICAgICAgICAgICAuZmlsdGVyKChyKSA9PiByICE9PSB1bmRlZmluZWQpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBSZXNvbHZlIGJlbG9uZ3NUbyByZWxhdGlvbnMgdG8gYnVpbGQgcGFyZW50LWNoaWxkIG1hcFxuICAgICAqIGUuZy4sIGJlbG9uZ3NUbzogJ3RlYW0ucmVzb3VyY2VJZHMnIFx1MjE5MiB7IHRlYW0xOiBbJ0VNUDAwMScsICdFTVAwMDInXSwgdGVhbTI6IFsuLi5dIH1cbiAgICAgKiBBbHNvIHJldHVybnMgdGhlIGNoaWxkVHlwZSAodGhlIGdyb3VwaW5nIHR5cGUgdGhhdCBoYXMgYmVsb25nc1RvKVxuICAgICAqL1xuICAgIGFzeW5jIHJlc29sdmVCZWxvbmdzVG8oZ3JvdXBpbmdzLCBmaWx0ZXIpIHtcbiAgICAgICAgLy8gRmluZCBncm91cGluZyB3aXRoIGJlbG9uZ3NUb1xuICAgICAgICBjb25zdCBjaGlsZEdyb3VwaW5nID0gZ3JvdXBpbmdzLmZpbmQoZyA9PiBnLmJlbG9uZ3NUbyk7XG4gICAgICAgIGlmICghY2hpbGRHcm91cGluZz8uYmVsb25nc1RvKVxuICAgICAgICAgICAgcmV0dXJuIHt9O1xuICAgICAgICAvLyBQYXJzZSBiZWxvbmdzVG86ICd0ZWFtLnJlc291cmNlSWRzJ1xuICAgICAgICBjb25zdCBbZW50aXR5VHlwZSwgcHJvcGVydHldID0gY2hpbGRHcm91cGluZy5iZWxvbmdzVG8uc3BsaXQoJy4nKTtcbiAgICAgICAgaWYgKCFlbnRpdHlUeXBlIHx8ICFwcm9wZXJ0eSlcbiAgICAgICAgICAgIHJldHVybiB7fTtcbiAgICAgICAgLy8gR2V0IHBhcmVudCBJRHMgZnJvbSBmaWx0ZXJcbiAgICAgICAgY29uc3QgcGFyZW50SWRzID0gZmlsdGVyW2VudGl0eVR5cGVdIHx8IFtdO1xuICAgICAgICBpZiAocGFyZW50SWRzLmxlbmd0aCA9PT0gMClcbiAgICAgICAgICAgIHJldHVybiB7fTtcbiAgICAgICAgLy8gRmluZCBzZXJ2aWNlIGR5bmFtaXNrIGJhc2VyZXQgcFx1MDBFNSBlbnRpdHlUeXBlIChpbmdlbiBoYXJkY29kZWQgdHlwZSBjaGVjaylcbiAgICAgICAgY29uc3Qgc2VydmljZSA9IHRoaXMuZW50aXR5U2VydmljZXMuZmluZChzID0+IHMuZW50aXR5VHlwZS50b0xvd2VyQ2FzZSgpID09PSBlbnRpdHlUeXBlKTtcbiAgICAgICAgaWYgKCFzZXJ2aWNlKVxuICAgICAgICAgICAgcmV0dXJuIHt9O1xuICAgICAgICAvLyBIZW50IGFsbGUgZW50aXRpZXMgb2cgZmlsdHJlciBwXHUwMEU1IHBhcmVudElkc1xuICAgICAgICBjb25zdCBhbGxFbnRpdGllcyA9IGF3YWl0IHNlcnZpY2UuZ2V0QWxsKCk7XG4gICAgICAgIGNvbnN0IGVudGl0aWVzID0gYWxsRW50aXRpZXMuZmlsdGVyKGUgPT4gcGFyZW50SWRzLmluY2x1ZGVzKGUuaWQpKTtcbiAgICAgICAgLy8gQnlnIHBhcmVudC1jaGlsZCBtYXBcbiAgICAgICAgY29uc3QgbWFwID0ge307XG4gICAgICAgIGZvciAoY29uc3QgZW50aXR5IG9mIGVudGl0aWVzKSB7XG4gICAgICAgICAgICBjb25zdCBlbnRpdHlSZWNvcmQgPSBlbnRpdHk7XG4gICAgICAgICAgICBjb25zdCBjaGlsZHJlbiA9IGVudGl0eVJlY29yZFtwcm9wZXJ0eV0gfHwgW107XG4gICAgICAgICAgICBtYXBbZW50aXR5UmVjb3JkLmlkXSA9IGNoaWxkcmVuO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB7IHBhcmVudENoaWxkTWFwOiBtYXAsIGNoaWxkVHlwZTogY2hpbGRHcm91cGluZy50eXBlIH07XG4gICAgfVxufVxuIiwgImV4cG9ydCBjbGFzcyBOYXZpZ2F0aW9uQW5pbWF0b3Ige1xuICAgIGNvbnN0cnVjdG9yKGhlYWRlclRyYWNrLCBjb250ZW50VHJhY2spIHtcbiAgICAgICAgdGhpcy5oZWFkZXJUcmFjayA9IGhlYWRlclRyYWNrO1xuICAgICAgICB0aGlzLmNvbnRlbnRUcmFjayA9IGNvbnRlbnRUcmFjaztcbiAgICB9XG4gICAgYXN5bmMgc2xpZGUoZGlyZWN0aW9uLCByZW5kZXJGbikge1xuICAgICAgICBjb25zdCBvdXQgPSBkaXJlY3Rpb24gPT09ICdsZWZ0JyA/ICctMTAwJScgOiAnMTAwJSc7XG4gICAgICAgIGNvbnN0IGludG8gPSBkaXJlY3Rpb24gPT09ICdsZWZ0JyA/ICcxMDAlJyA6ICctMTAwJSc7XG4gICAgICAgIGF3YWl0IHRoaXMuYW5pbWF0ZU91dChvdXQpO1xuICAgICAgICBhd2FpdCByZW5kZXJGbigpO1xuICAgICAgICBhd2FpdCB0aGlzLmFuaW1hdGVJbihpbnRvKTtcbiAgICB9XG4gICAgYXN5bmMgYW5pbWF0ZU91dCh0cmFuc2xhdGUpIHtcbiAgICAgICAgYXdhaXQgUHJvbWlzZS5hbGwoW1xuICAgICAgICAgICAgdGhpcy5oZWFkZXJUcmFjay5hbmltYXRlKFt7IHRyYW5zZm9ybTogJ3RyYW5zbGF0ZVgoMCknIH0sIHsgdHJhbnNmb3JtOiBgdHJhbnNsYXRlWCgke3RyYW5zbGF0ZX0pYCB9XSwgeyBkdXJhdGlvbjogMjAwLCBlYXNpbmc6ICdlYXNlLWluJyB9KS5maW5pc2hlZCxcbiAgICAgICAgICAgIHRoaXMuY29udGVudFRyYWNrLmFuaW1hdGUoW3sgdHJhbnNmb3JtOiAndHJhbnNsYXRlWCgwKScgfSwgeyB0cmFuc2Zvcm06IGB0cmFuc2xhdGVYKCR7dHJhbnNsYXRlfSlgIH1dLCB7IGR1cmF0aW9uOiAyMDAsIGVhc2luZzogJ2Vhc2UtaW4nIH0pLmZpbmlzaGVkXG4gICAgICAgIF0pO1xuICAgIH1cbiAgICBhc3luYyBhbmltYXRlSW4odHJhbnNsYXRlKSB7XG4gICAgICAgIGF3YWl0IFByb21pc2UuYWxsKFtcbiAgICAgICAgICAgIHRoaXMuaGVhZGVyVHJhY2suYW5pbWF0ZShbeyB0cmFuc2Zvcm06IGB0cmFuc2xhdGVYKCR7dHJhbnNsYXRlfSlgIH0sIHsgdHJhbnNmb3JtOiAndHJhbnNsYXRlWCgwKScgfV0sIHsgZHVyYXRpb246IDIwMCwgZWFzaW5nOiAnZWFzZS1vdXQnIH0pLmZpbmlzaGVkLFxuICAgICAgICAgICAgdGhpcy5jb250ZW50VHJhY2suYW5pbWF0ZShbeyB0cmFuc2Zvcm06IGB0cmFuc2xhdGVYKCR7dHJhbnNsYXRlfSlgIH0sIHsgdHJhbnNmb3JtOiAndHJhbnNsYXRlWCgwKScgfV0sIHsgZHVyYXRpb246IDIwMCwgZWFzaW5nOiAnZWFzZS1vdXQnIH0pLmZpbmlzaGVkXG4gICAgICAgIF0pO1xuICAgIH1cbn1cbiIsICIvKipcbiAqIENhbGVuZGFyRXZlbnRzIC0gQ29tbWFuZCBhbmQgc3RhdHVzIGV2ZW50cyBmb3IgQ2FsZW5kYXJBcHBcbiAqL1xuZXhwb3J0IGNvbnN0IENhbGVuZGFyRXZlbnRzID0ge1xuICAgIC8vIENvbW1hbmQgZXZlbnRzIChob3N0IFx1MjE5MiBjYWxlbmRhcilcbiAgICBDTURfTkFWSUdBVEVfUFJFVjogJ2NhbGVuZGFyOmNtZDpuYXZpZ2F0ZTpwcmV2JyxcbiAgICBDTURfTkFWSUdBVEVfTkVYVDogJ2NhbGVuZGFyOmNtZDpuYXZpZ2F0ZTpuZXh0JyxcbiAgICBDTURfRFJBV0VSX1RPR0dMRTogJ2NhbGVuZGFyOmNtZDpkcmF3ZXI6dG9nZ2xlJyxcbiAgICBDTURfUkVOREVSOiAnY2FsZW5kYXI6Y21kOnJlbmRlcicsXG4gICAgQ01EX1dPUktXRUVLX0NIQU5HRTogJ2NhbGVuZGFyOmNtZDp3b3Jrd2VlazpjaGFuZ2UnLFxuICAgIENNRF9WSUVXX1VQREFURTogJ2NhbGVuZGFyOmNtZDp2aWV3OnVwZGF0ZSdcbn07XG4iLCAiaW1wb3J0IHsgTmF2aWdhdGlvbkFuaW1hdG9yIH0gZnJvbSAnLi9OYXZpZ2F0aW9uQW5pbWF0b3InO1xuaW1wb3J0IHsgQ2FsZW5kYXJFdmVudHMgfSBmcm9tICcuL0NhbGVuZGFyRXZlbnRzJztcbmV4cG9ydCBjbGFzcyBDYWxlbmRhckFwcCB7XG4gICAgY29uc3RydWN0b3Iob3JjaGVzdHJhdG9yLCB0aW1lQXhpc1JlbmRlcmVyLCBkYXRlU2VydmljZSwgc2Nyb2xsTWFuYWdlciwgaGVhZGVyRHJhd2VyTWFuYWdlciwgZHJhZ0Ryb3BNYW5hZ2VyLCBlZGdlU2Nyb2xsTWFuYWdlciwgcmVzaXplTWFuYWdlciwgaGVhZGVyRHJhd2VyUmVuZGVyZXIsIGV2ZW50UGVyc2lzdGVuY2VNYW5hZ2VyLCBzZXR0aW5nc1NlcnZpY2UsIHZpZXdDb25maWdTZXJ2aWNlLCBldmVudEJ1cykge1xuICAgICAgICB0aGlzLm9yY2hlc3RyYXRvciA9IG9yY2hlc3RyYXRvcjtcbiAgICAgICAgdGhpcy50aW1lQXhpc1JlbmRlcmVyID0gdGltZUF4aXNSZW5kZXJlcjtcbiAgICAgICAgdGhpcy5kYXRlU2VydmljZSA9IGRhdGVTZXJ2aWNlO1xuICAgICAgICB0aGlzLnNjcm9sbE1hbmFnZXIgPSBzY3JvbGxNYW5hZ2VyO1xuICAgICAgICB0aGlzLmhlYWRlckRyYXdlck1hbmFnZXIgPSBoZWFkZXJEcmF3ZXJNYW5hZ2VyO1xuICAgICAgICB0aGlzLmRyYWdEcm9wTWFuYWdlciA9IGRyYWdEcm9wTWFuYWdlcjtcbiAgICAgICAgdGhpcy5lZGdlU2Nyb2xsTWFuYWdlciA9IGVkZ2VTY3JvbGxNYW5hZ2VyO1xuICAgICAgICB0aGlzLnJlc2l6ZU1hbmFnZXIgPSByZXNpemVNYW5hZ2VyO1xuICAgICAgICB0aGlzLmhlYWRlckRyYXdlclJlbmRlcmVyID0gaGVhZGVyRHJhd2VyUmVuZGVyZXI7XG4gICAgICAgIHRoaXMuZXZlbnRQZXJzaXN0ZW5jZU1hbmFnZXIgPSBldmVudFBlcnNpc3RlbmNlTWFuYWdlcjtcbiAgICAgICAgdGhpcy5zZXR0aW5nc1NlcnZpY2UgPSBzZXR0aW5nc1NlcnZpY2U7XG4gICAgICAgIHRoaXMudmlld0NvbmZpZ1NlcnZpY2UgPSB2aWV3Q29uZmlnU2VydmljZTtcbiAgICAgICAgdGhpcy5ldmVudEJ1cyA9IGV2ZW50QnVzO1xuICAgICAgICB0aGlzLndlZWtPZmZzZXQgPSAwO1xuICAgICAgICB0aGlzLmN1cnJlbnRWaWV3SWQgPSAnc2ltcGxlJztcbiAgICAgICAgdGhpcy53b3Jrd2Vla1ByZXNldCA9IG51bGw7XG4gICAgICAgIHRoaXMuZ3JvdXBpbmdPdmVycmlkZXMgPSBuZXcgTWFwKCk7XG4gICAgfVxuICAgIGFzeW5jIGluaXQoY29udGFpbmVyKSB7XG4gICAgICAgIHRoaXMuY29udGFpbmVyID0gY29udGFpbmVyO1xuICAgICAgICAvLyBMb2FkIHNldHRpbmdzXG4gICAgICAgIGNvbnN0IGdyaWRTZXR0aW5ncyA9IGF3YWl0IHRoaXMuc2V0dGluZ3NTZXJ2aWNlLmdldEdyaWRTZXR0aW5ncygpO1xuICAgICAgICBpZiAoIWdyaWRTZXR0aW5ncykge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdHcmlkU2V0dGluZ3Mgbm90IGZvdW5kJyk7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy53b3Jrd2Vla1ByZXNldCA9IGF3YWl0IHRoaXMuc2V0dGluZ3NTZXJ2aWNlLmdldERlZmF1bHRXb3Jrd2Vla1ByZXNldCgpO1xuICAgICAgICAvLyBDcmVhdGUgTmF2aWdhdGlvbkFuaW1hdG9yIHdpdGggRE9NIGVsZW1lbnRzXG4gICAgICAgIHRoaXMuYW5pbWF0b3IgPSBuZXcgTmF2aWdhdGlvbkFuaW1hdG9yKGNvbnRhaW5lci5xdWVyeVNlbGVjdG9yKCdzd3AtaGVhZGVyLXRyYWNrJyksIGNvbnRhaW5lci5xdWVyeVNlbGVjdG9yKCdzd3AtY29udGVudC10cmFjaycpKTtcbiAgICAgICAgLy8gUmVuZGVyIHRpbWUgYXhpcyBmcm9tIHNldHRpbmdzXG4gICAgICAgIHRoaXMudGltZUF4aXNSZW5kZXJlci5yZW5kZXIoY29udGFpbmVyLnF1ZXJ5U2VsZWN0b3IoJyN0aW1lLWF4aXMnKSwgZ3JpZFNldHRpbmdzLmRheVN0YXJ0SG91ciwgZ3JpZFNldHRpbmdzLmRheUVuZEhvdXIpO1xuICAgICAgICAvLyBJbml0IG1hbmFnZXJzXG4gICAgICAgIHRoaXMuc2Nyb2xsTWFuYWdlci5pbml0KGNvbnRhaW5lcik7XG4gICAgICAgIHRoaXMuaGVhZGVyRHJhd2VyTWFuYWdlci5pbml0KGNvbnRhaW5lcik7XG4gICAgICAgIHRoaXMuZHJhZ0Ryb3BNYW5hZ2VyLmluaXQoY29udGFpbmVyKTtcbiAgICAgICAgdGhpcy5yZXNpemVNYW5hZ2VyLmluaXQoY29udGFpbmVyKTtcbiAgICAgICAgY29uc3Qgc2Nyb2xsYWJsZUNvbnRlbnQgPSBjb250YWluZXIucXVlcnlTZWxlY3Rvcignc3dwLXNjcm9sbGFibGUtY29udGVudCcpO1xuICAgICAgICB0aGlzLmVkZ2VTY3JvbGxNYW5hZ2VyLmluaXQoc2Nyb2xsYWJsZUNvbnRlbnQpO1xuICAgICAgICAvLyBTZXR1cCBjb21tYW5kIGV2ZW50IGxpc3RlbmVyc1xuICAgICAgICB0aGlzLnNldHVwRXZlbnRMaXN0ZW5lcnMoKTtcbiAgICAgICAgLy8gRW1pdCByZWFkeSBzdGF0dXNcbiAgICAgICAgdGhpcy5lbWl0U3RhdHVzKCdyZWFkeScpO1xuICAgIH1cbiAgICBzZXR1cEV2ZW50TGlzdGVuZXJzKCkge1xuICAgICAgICAvLyBOYXZpZ2F0aW9uIGNvbW1hbmRzIHZpYSBFdmVudEJ1c1xuICAgICAgICB0aGlzLmV2ZW50QnVzLm9uKENhbGVuZGFyRXZlbnRzLkNNRF9OQVZJR0FURV9QUkVWLCAoKSA9PiB7XG4gICAgICAgICAgICB0aGlzLmhhbmRsZU5hdmlnYXRlUHJldigpO1xuICAgICAgICB9KTtcbiAgICAgICAgdGhpcy5ldmVudEJ1cy5vbihDYWxlbmRhckV2ZW50cy5DTURfTkFWSUdBVEVfTkVYVCwgKCkgPT4ge1xuICAgICAgICAgICAgdGhpcy5oYW5kbGVOYXZpZ2F0ZU5leHQoKTtcbiAgICAgICAgfSk7XG4gICAgICAgIC8vIERyYXdlciB0b2dnbGUgdmlhIEV2ZW50QnVzXG4gICAgICAgIHRoaXMuZXZlbnRCdXMub24oQ2FsZW5kYXJFdmVudHMuQ01EX0RSQVdFUl9UT0dHTEUsICgpID0+IHtcbiAgICAgICAgICAgIHRoaXMuaGVhZGVyRHJhd2VyTWFuYWdlci50b2dnbGUoKTtcbiAgICAgICAgfSk7XG4gICAgICAgIC8vIFJlbmRlciBjb21tYW5kIHZpYSBFdmVudEJ1c1xuICAgICAgICB0aGlzLmV2ZW50QnVzLm9uKENhbGVuZGFyRXZlbnRzLkNNRF9SRU5ERVIsIChlKSA9PiB7XG4gICAgICAgICAgICBjb25zdCB7IHZpZXdJZCB9ID0gZS5kZXRhaWw7XG4gICAgICAgICAgICB0aGlzLmhhbmRsZVJlbmRlckNvbW1hbmQodmlld0lkKTtcbiAgICAgICAgfSk7XG4gICAgICAgIC8vIFdvcmt3ZWVrIGNoYW5nZSB2aWEgRXZlbnRCdXNcbiAgICAgICAgdGhpcy5ldmVudEJ1cy5vbihDYWxlbmRhckV2ZW50cy5DTURfV09SS1dFRUtfQ0hBTkdFLCAoZSkgPT4ge1xuICAgICAgICAgICAgY29uc3QgeyBwcmVzZXRJZCB9ID0gZS5kZXRhaWw7XG4gICAgICAgICAgICB0aGlzLmhhbmRsZVdvcmt3ZWVrQ2hhbmdlKHByZXNldElkKTtcbiAgICAgICAgfSk7XG4gICAgICAgIC8vIFZpZXcgdXBkYXRlIHZpYSBFdmVudEJ1c1xuICAgICAgICB0aGlzLmV2ZW50QnVzLm9uKENhbGVuZGFyRXZlbnRzLkNNRF9WSUVXX1VQREFURSwgKGUpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHsgdHlwZSwgdmFsdWVzIH0gPSBlLmRldGFpbDtcbiAgICAgICAgICAgIHRoaXMuaGFuZGxlVmlld1VwZGF0ZSh0eXBlLCB2YWx1ZXMpO1xuICAgICAgICB9KTtcbiAgICB9XG4gICAgYXN5bmMgaGFuZGxlUmVuZGVyQ29tbWFuZCh2aWV3SWQpIHtcbiAgICAgICAgdGhpcy5jdXJyZW50Vmlld0lkID0gdmlld0lkO1xuICAgICAgICBhd2FpdCB0aGlzLnJlbmRlcigpO1xuICAgICAgICB0aGlzLmVtaXRTdGF0dXMoJ3JlbmRlcmVkJywgeyB2aWV3SWQgfSk7XG4gICAgfVxuICAgIGFzeW5jIGhhbmRsZU5hdmlnYXRlUHJldigpIHtcbiAgICAgICAgdGhpcy53ZWVrT2Zmc2V0LS07XG4gICAgICAgIGF3YWl0IHRoaXMuYW5pbWF0b3Iuc2xpZGUoJ3JpZ2h0JywgKCkgPT4gdGhpcy5yZW5kZXIoKSk7XG4gICAgICAgIHRoaXMuZW1pdFN0YXR1cygncmVuZGVyZWQnLCB7IHZpZXdJZDogdGhpcy5jdXJyZW50Vmlld0lkIH0pO1xuICAgIH1cbiAgICBhc3luYyBoYW5kbGVOYXZpZ2F0ZU5leHQoKSB7XG4gICAgICAgIHRoaXMud2Vla09mZnNldCsrO1xuICAgICAgICBhd2FpdCB0aGlzLmFuaW1hdG9yLnNsaWRlKCdsZWZ0JywgKCkgPT4gdGhpcy5yZW5kZXIoKSk7XG4gICAgICAgIHRoaXMuZW1pdFN0YXR1cygncmVuZGVyZWQnLCB7IHZpZXdJZDogdGhpcy5jdXJyZW50Vmlld0lkIH0pO1xuICAgIH1cbiAgICBhc3luYyBoYW5kbGVXb3Jrd2Vla0NoYW5nZShwcmVzZXRJZCkge1xuICAgICAgICBjb25zdCBwcmVzZXQgPSBhd2FpdCB0aGlzLnNldHRpbmdzU2VydmljZS5nZXRXb3Jrd2Vla1ByZXNldChwcmVzZXRJZCk7XG4gICAgICAgIGlmIChwcmVzZXQpIHtcbiAgICAgICAgICAgIHRoaXMud29ya3dlZWtQcmVzZXQgPSBwcmVzZXQ7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLnJlbmRlcigpO1xuICAgICAgICAgICAgdGhpcy5lbWl0U3RhdHVzKCdyZW5kZXJlZCcsIHsgdmlld0lkOiB0aGlzLmN1cnJlbnRWaWV3SWQgfSk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgYXN5bmMgaGFuZGxlVmlld1VwZGF0ZSh0eXBlLCB2YWx1ZXMpIHtcbiAgICAgICAgdGhpcy5ncm91cGluZ092ZXJyaWRlcy5zZXQodHlwZSwgdmFsdWVzKTtcbiAgICAgICAgYXdhaXQgdGhpcy5yZW5kZXIoKTtcbiAgICAgICAgdGhpcy5lbWl0U3RhdHVzKCdyZW5kZXJlZCcsIHsgdmlld0lkOiB0aGlzLmN1cnJlbnRWaWV3SWQgfSk7XG4gICAgfVxuICAgIGFzeW5jIHJlbmRlcigpIHtcbiAgICAgICAgY29uc3Qgc3RvcmVkQ29uZmlnID0gYXdhaXQgdGhpcy52aWV3Q29uZmlnU2VydmljZS5nZXRCeUlkKHRoaXMuY3VycmVudFZpZXdJZCk7XG4gICAgICAgIGlmICghc3RvcmVkQ29uZmlnKSB7XG4gICAgICAgICAgICB0aGlzLmVtaXRTdGF0dXMoJ2Vycm9yJywgeyBtZXNzYWdlOiBgVmlld0NvbmZpZyBub3QgZm91bmQ6ICR7dGhpcy5jdXJyZW50Vmlld0lkfWAgfSk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgLy8gUG9wdWxhdGUgZGF0ZSB2YWx1ZXMgYmFzZWQgb24gd29ya3dlZWsgYW5kIG9mZnNldFxuICAgICAgICBjb25zdCB3b3JrRGF5cyA9IHRoaXMud29ya3dlZWtQcmVzZXQ/LndvcmtEYXlzIHx8IFsxLCAyLCAzLCA0LCA1XTtcbiAgICAgICAgY29uc3QgZGF0ZXMgPSB0aGlzLmN1cnJlbnRWaWV3SWQgPT09ICdkYXknXG4gICAgICAgICAgICA/IHRoaXMuZGF0ZVNlcnZpY2UuZ2V0V2Vla0RhdGVzKHRoaXMud2Vla09mZnNldCwgMSlcbiAgICAgICAgICAgIDogdGhpcy5kYXRlU2VydmljZS5nZXRXb3JrV2Vla0RhdGVzKHRoaXMud2Vla09mZnNldCwgd29ya0RheXMpO1xuICAgICAgICAvLyBDbG9uZSBjb25maWcgYW5kIGFwcGx5IG92ZXJyaWRlc1xuICAgICAgICBjb25zdCB2aWV3Q29uZmlnID0ge1xuICAgICAgICAgICAgLi4uc3RvcmVkQ29uZmlnLFxuICAgICAgICAgICAgZ3JvdXBpbmdzOiBzdG9yZWRDb25maWcuZ3JvdXBpbmdzLm1hcChnID0+IHtcbiAgICAgICAgICAgICAgICAvLyBBcHBseSBkYXRlIHZhbHVlc1xuICAgICAgICAgICAgICAgIGlmIChnLnR5cGUgPT09ICdkYXRlJykge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4geyAuLi5nLCB2YWx1ZXM6IGRhdGVzIH07XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIC8vIEFwcGx5IGdyb3VwaW5nIG92ZXJyaWRlc1xuICAgICAgICAgICAgICAgIGNvbnN0IG92ZXJyaWRlID0gdGhpcy5ncm91cGluZ092ZXJyaWRlcy5nZXQoZy50eXBlKTtcbiAgICAgICAgICAgICAgICBpZiAob3ZlcnJpZGUpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHsgLi4uZywgdmFsdWVzOiBvdmVycmlkZSB9O1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICByZXR1cm4gZztcbiAgICAgICAgICAgIH0pXG4gICAgICAgIH07XG4gICAgICAgIGF3YWl0IHRoaXMub3JjaGVzdHJhdG9yLnJlbmRlcih2aWV3Q29uZmlnLCB0aGlzLmNvbnRhaW5lcik7XG4gICAgfVxuICAgIGVtaXRTdGF0dXMoc3RhdHVzLCBkZXRhaWwpIHtcbiAgICAgICAgdGhpcy5jb250YWluZXIuZGlzcGF0Y2hFdmVudChuZXcgQ3VzdG9tRXZlbnQoYGNhbGVuZGFyOnN0YXR1czoke3N0YXR1c31gLCB7XG4gICAgICAgICAgICBkZXRhaWwsXG4gICAgICAgICAgICBidWJibGVzOiB0cnVlXG4gICAgICAgIH0pKTtcbiAgICB9XG59XG4iLCAiZXhwb3J0IGNsYXNzIFRpbWVBeGlzUmVuZGVyZXIge1xuICAgIHJlbmRlcihjb250YWluZXIsIHN0YXJ0SG91ciA9IDYsIGVuZEhvdXIgPSAyMCkge1xuICAgICAgICBjb250YWluZXIuaW5uZXJIVE1MID0gJyc7XG4gICAgICAgIGZvciAobGV0IGhvdXIgPSBzdGFydEhvdXI7IGhvdXIgPD0gZW5kSG91cjsgaG91cisrKSB7XG4gICAgICAgICAgICBjb25zdCBtYXJrZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzd3AtaG91ci1tYXJrZXInKTtcbiAgICAgICAgICAgIG1hcmtlci50ZXh0Q29udGVudCA9IGAke2hvdXIudG9TdHJpbmcoKS5wYWRTdGFydCgyLCAnMCcpfTowMGA7XG4gICAgICAgICAgICBjb250YWluZXIuYXBwZW5kQ2hpbGQobWFya2VyKTtcbiAgICAgICAgfVxuICAgIH1cbn1cbiIsICJleHBvcnQgY2xhc3MgU2Nyb2xsTWFuYWdlciB7XG4gICAgaW5pdChjb250YWluZXIpIHtcbiAgICAgICAgdGhpcy5zY3JvbGxhYmxlQ29udGVudCA9IGNvbnRhaW5lci5xdWVyeVNlbGVjdG9yKCdzd3Atc2Nyb2xsYWJsZS1jb250ZW50Jyk7XG4gICAgICAgIHRoaXMudGltZUF4aXNDb250ZW50ID0gY29udGFpbmVyLnF1ZXJ5U2VsZWN0b3IoJ3N3cC10aW1lLWF4aXMtY29udGVudCcpO1xuICAgICAgICB0aGlzLmNhbGVuZGFySGVhZGVyID0gY29udGFpbmVyLnF1ZXJ5U2VsZWN0b3IoJ3N3cC1jYWxlbmRhci1oZWFkZXInKTtcbiAgICAgICAgdGhpcy5oZWFkZXJEcmF3ZXIgPSBjb250YWluZXIucXVlcnlTZWxlY3Rvcignc3dwLWhlYWRlci1kcmF3ZXInKTtcbiAgICAgICAgdGhpcy5oZWFkZXJWaWV3cG9ydCA9IGNvbnRhaW5lci5xdWVyeVNlbGVjdG9yKCdzd3AtaGVhZGVyLXZpZXdwb3J0Jyk7XG4gICAgICAgIHRoaXMuaGVhZGVyU3BhY2VyID0gY29udGFpbmVyLnF1ZXJ5U2VsZWN0b3IoJ3N3cC1oZWFkZXItc3BhY2VyJyk7XG4gICAgICAgIHRoaXMuc2Nyb2xsYWJsZUNvbnRlbnQuYWRkRXZlbnRMaXN0ZW5lcignc2Nyb2xsJywgKCkgPT4gdGhpcy5vblNjcm9sbCgpKTtcbiAgICAgICAgLy8gU3lua3JvbmlzZXIgaGVhZGVyLXNwYWNlciBoXHUwMEY4amRlIG1lZCBoZWFkZXItdmlld3BvcnRcbiAgICAgICAgdGhpcy5yZXNpemVPYnNlcnZlciA9IG5ldyBSZXNpemVPYnNlcnZlcigoKSA9PiB0aGlzLnN5bmNIZWFkZXJTcGFjZXJIZWlnaHQoKSk7XG4gICAgICAgIHRoaXMucmVzaXplT2JzZXJ2ZXIub2JzZXJ2ZSh0aGlzLmhlYWRlclZpZXdwb3J0KTtcbiAgICAgICAgdGhpcy5zeW5jSGVhZGVyU3BhY2VySGVpZ2h0KCk7XG4gICAgfVxuICAgIHN5bmNIZWFkZXJTcGFjZXJIZWlnaHQoKSB7XG4gICAgICAgIC8vIEtvcGllciBkZW4gZmFrdGlza2UgY29tcHV0ZWQgaGVpZ2h0IGRpcmVrdGUgZnJhIGhlYWRlci12aWV3cG9ydFxuICAgICAgICBjb25zdCBjb21wdXRlZEhlaWdodCA9IGdldENvbXB1dGVkU3R5bGUodGhpcy5oZWFkZXJWaWV3cG9ydCkuaGVpZ2h0O1xuICAgICAgICB0aGlzLmhlYWRlclNwYWNlci5zdHlsZS5oZWlnaHQgPSBjb21wdXRlZEhlaWdodDtcbiAgICB9XG4gICAgb25TY3JvbGwoKSB7XG4gICAgICAgIGNvbnN0IHsgc2Nyb2xsVG9wLCBzY3JvbGxMZWZ0IH0gPSB0aGlzLnNjcm9sbGFibGVDb250ZW50O1xuICAgICAgICAvLyBTeW5rcm9uaXNlciB0aW1lLWF4aXMgdmVydGlrYWx0XG4gICAgICAgIHRoaXMudGltZUF4aXNDb250ZW50LnN0eWxlLnRyYW5zZm9ybSA9IGB0cmFuc2xhdGVZKC0ke3Njcm9sbFRvcH1weClgO1xuICAgICAgICAvLyBTeW5rcm9uaXNlciBoZWFkZXIgb2cgZHJhd2VyIGhvcmlzb250YWx0XG4gICAgICAgIHRoaXMuY2FsZW5kYXJIZWFkZXIuc3R5bGUudHJhbnNmb3JtID0gYHRyYW5zbGF0ZVgoLSR7c2Nyb2xsTGVmdH1weClgO1xuICAgICAgICB0aGlzLmhlYWRlckRyYXdlci5zdHlsZS50cmFuc2Zvcm0gPSBgdHJhbnNsYXRlWCgtJHtzY3JvbGxMZWZ0fXB4KWA7XG4gICAgfVxufVxuIiwgImV4cG9ydCBjbGFzcyBIZWFkZXJEcmF3ZXJNYW5hZ2VyIHtcbiAgICBjb25zdHJ1Y3RvcigpIHtcbiAgICAgICAgdGhpcy5leHBhbmRlZCA9IGZhbHNlO1xuICAgICAgICB0aGlzLmN1cnJlbnRSb3dzID0gMDtcbiAgICAgICAgdGhpcy5yb3dIZWlnaHQgPSAyNTtcbiAgICAgICAgdGhpcy5kdXJhdGlvbiA9IDIwMDtcbiAgICB9XG4gICAgaW5pdChjb250YWluZXIpIHtcbiAgICAgICAgdGhpcy5kcmF3ZXIgPSBjb250YWluZXIucXVlcnlTZWxlY3Rvcignc3dwLWhlYWRlci1kcmF3ZXInKTtcbiAgICAgICAgaWYgKCF0aGlzLmRyYXdlcilcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ0hlYWRlckRyYXdlck1hbmFnZXI6IHN3cC1oZWFkZXItZHJhd2VyIG5vdCBmb3VuZCcpO1xuICAgIH1cbiAgICB0b2dnbGUoKSB7XG4gICAgICAgIHRoaXMuZXhwYW5kZWQgPyB0aGlzLmNvbGxhcHNlKCkgOiB0aGlzLmV4cGFuZCgpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBFeHBhbmQgZHJhd2VyIHRvIHNpbmdsZSByb3cgKGxlZ2FjeSBzdXBwb3J0KVxuICAgICAqL1xuICAgIGV4cGFuZCgpIHtcbiAgICAgICAgdGhpcy5leHBhbmRUb1Jvd3MoMSk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEV4cGFuZCBkcmF3ZXIgdG8gZml0IHNwZWNpZmllZCBudW1iZXIgb2Ygcm93c1xuICAgICAqL1xuICAgIGV4cGFuZFRvUm93cyhyb3dDb3VudCkge1xuICAgICAgICBjb25zdCB0YXJnZXRIZWlnaHQgPSByb3dDb3VudCAqIHRoaXMucm93SGVpZ2h0O1xuICAgICAgICBjb25zdCBjdXJyZW50SGVpZ2h0ID0gdGhpcy5leHBhbmRlZCA/IHRoaXMuY3VycmVudFJvd3MgKiB0aGlzLnJvd0hlaWdodCA6IDA7XG4gICAgICAgIC8vIFNraXAgaWYgYWxyZWFkeSBhdCB0YXJnZXRcbiAgICAgICAgaWYgKHRoaXMuZXhwYW5kZWQgJiYgdGhpcy5jdXJyZW50Um93cyA9PT0gcm93Q291bnQpXG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIHRoaXMuY3VycmVudFJvd3MgPSByb3dDb3VudDtcbiAgICAgICAgdGhpcy5leHBhbmRlZCA9IHRydWU7XG4gICAgICAgIHRoaXMuYW5pbWF0ZShjdXJyZW50SGVpZ2h0LCB0YXJnZXRIZWlnaHQpO1xuICAgIH1cbiAgICBjb2xsYXBzZSgpIHtcbiAgICAgICAgaWYgKCF0aGlzLmV4cGFuZGVkKVxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICBjb25zdCBjdXJyZW50SGVpZ2h0ID0gdGhpcy5jdXJyZW50Um93cyAqIHRoaXMucm93SGVpZ2h0O1xuICAgICAgICB0aGlzLmV4cGFuZGVkID0gZmFsc2U7XG4gICAgICAgIHRoaXMuY3VycmVudFJvd3MgPSAwO1xuICAgICAgICB0aGlzLmFuaW1hdGUoY3VycmVudEhlaWdodCwgMCk7XG4gICAgfVxuICAgIGFuaW1hdGUoZnJvbSwgdG8pIHtcbiAgICAgICAgY29uc3Qga2V5ZnJhbWVzID0gW1xuICAgICAgICAgICAgeyBoZWlnaHQ6IGAke2Zyb219cHhgIH0sXG4gICAgICAgICAgICB7IGhlaWdodDogYCR7dG99cHhgIH1cbiAgICAgICAgXTtcbiAgICAgICAgY29uc3Qgb3B0aW9ucyA9IHtcbiAgICAgICAgICAgIGR1cmF0aW9uOiB0aGlzLmR1cmF0aW9uLFxuICAgICAgICAgICAgZWFzaW5nOiAnZWFzZScsXG4gICAgICAgICAgICBmaWxsOiAnZm9yd2FyZHMnXG4gICAgICAgIH07XG4gICAgICAgIC8vIEt1biBhbmltXHUwMEU5ciBkcmF3ZXIgLSBTY3JvbGxNYW5hZ2VyIHN5bmtyb25pc2VyZXIgaGVhZGVyLXNwYWNlciB2aWEgUmVzaXplT2JzZXJ2ZXJcbiAgICAgICAgdGhpcy5kcmF3ZXIuYW5pbWF0ZShrZXlmcmFtZXMsIG9wdGlvbnMpO1xuICAgIH1cbiAgICBpc0V4cGFuZGVkKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5leHBhbmRlZDtcbiAgICB9XG4gICAgZ2V0Um93Q291bnQoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmN1cnJlbnRSb3dzO1xuICAgIH1cbn1cbiIsICJleHBvcnQgY2xhc3MgTW9ja1RlYW1TdG9yZSB7XG4gICAgY29uc3RydWN0b3IoKSB7XG4gICAgICAgIHRoaXMudHlwZSA9ICd0ZWFtJztcbiAgICAgICAgdGhpcy50ZWFtcyA9IFtcbiAgICAgICAgICAgIHsgaWQ6ICdhbHBoYScsIG5hbWU6ICdUZWFtIEFscGhhJyB9LFxuICAgICAgICAgICAgeyBpZDogJ2JldGEnLCBuYW1lOiAnVGVhbSBCZXRhJyB9XG4gICAgICAgIF07XG4gICAgfVxuICAgIGdldEJ5SWRzKGlkcykge1xuICAgICAgICByZXR1cm4gdGhpcy50ZWFtcy5maWx0ZXIodCA9PiBpZHMuaW5jbHVkZXModC5pZCkpO1xuICAgIH1cbn1cbmV4cG9ydCBjbGFzcyBNb2NrUmVzb3VyY2VTdG9yZSB7XG4gICAgY29uc3RydWN0b3IoKSB7XG4gICAgICAgIHRoaXMudHlwZSA9ICdyZXNvdXJjZSc7XG4gICAgICAgIHRoaXMucmVzb3VyY2VzID0gW1xuICAgICAgICAgICAgeyBpZDogJ2FsaWNlJywgbmFtZTogJ0FsaWNlJywgdGVhbUlkOiAnYWxwaGEnIH0sXG4gICAgICAgICAgICB7IGlkOiAnYm9iJywgbmFtZTogJ0JvYicsIHRlYW1JZDogJ2FscGhhJyB9LFxuICAgICAgICAgICAgeyBpZDogJ2Nhcm9sJywgbmFtZTogJ0Nhcm9sJywgdGVhbUlkOiAnYmV0YScgfSxcbiAgICAgICAgICAgIHsgaWQ6ICdkYXZlJywgbmFtZTogJ0RhdmUnLCB0ZWFtSWQ6ICdiZXRhJyB9XG4gICAgICAgIF07XG4gICAgfVxuICAgIGdldEJ5SWRzKGlkcykge1xuICAgICAgICByZXR1cm4gdGhpcy5yZXNvdXJjZXMuZmlsdGVyKHIgPT4gaWRzLmluY2x1ZGVzKHIuaWQpKTtcbiAgICB9XG59XG4iLCAiaW1wb3J0IHsgQ2FsZW5kYXJFdmVudHMgfSBmcm9tICcuLi9jb3JlL0NhbGVuZGFyRXZlbnRzJztcbmV4cG9ydCBjbGFzcyBEZW1vQXBwIHtcbiAgICBjb25zdHJ1Y3RvcihpbmRleGVkREJDb250ZXh0LCBkYXRhU2VlZGVyLCBhdWRpdFNlcnZpY2UsIGNhbGVuZGFyQXBwLCBkYXRlU2VydmljZSwgcmVzb3VyY2VTZXJ2aWNlLCBldmVudEJ1cykge1xuICAgICAgICB0aGlzLmluZGV4ZWREQkNvbnRleHQgPSBpbmRleGVkREJDb250ZXh0O1xuICAgICAgICB0aGlzLmRhdGFTZWVkZXIgPSBkYXRhU2VlZGVyO1xuICAgICAgICB0aGlzLmF1ZGl0U2VydmljZSA9IGF1ZGl0U2VydmljZTtcbiAgICAgICAgdGhpcy5jYWxlbmRhckFwcCA9IGNhbGVuZGFyQXBwO1xuICAgICAgICB0aGlzLmRhdGVTZXJ2aWNlID0gZGF0ZVNlcnZpY2U7XG4gICAgICAgIHRoaXMucmVzb3VyY2VTZXJ2aWNlID0gcmVzb3VyY2VTZXJ2aWNlO1xuICAgICAgICB0aGlzLmV2ZW50QnVzID0gZXZlbnRCdXM7XG4gICAgICAgIHRoaXMuY3VycmVudFZpZXcgPSAnc2ltcGxlJztcbiAgICB9XG4gICAgYXN5bmMgaW5pdCgpIHtcbiAgICAgICAgLy8gU2V0IGJhc2UgZGF0ZSB0byBtYXRjaCBtb2NrIGRhdGEgKDguIGRlY2VtYmVyIDIwMjUgPSBtYW5kYWcpXG4gICAgICAgIHRoaXMuZGF0ZVNlcnZpY2Uuc2V0QmFzZURhdGUobmV3IERhdGUoJzIwMjUtMTItMDgnKSk7XG4gICAgICAgIC8vIEluaXRpYWxpemUgSW5kZXhlZERCXG4gICAgICAgIGF3YWl0IHRoaXMuaW5kZXhlZERCQ29udGV4dC5pbml0aWFsaXplKCk7XG4gICAgICAgIGNvbnNvbGUubG9nKCdbRGVtb0FwcF0gSW5kZXhlZERCIGluaXRpYWxpemVkJyk7XG4gICAgICAgIC8vIFNlZWQgZGF0YSBpZiBlbXB0eVxuICAgICAgICBhd2FpdCB0aGlzLmRhdGFTZWVkZXIuc2VlZElmRW1wdHkoKTtcbiAgICAgICAgY29uc29sZS5sb2coJ1tEZW1vQXBwXSBEYXRhIHNlZWRpbmcgY29tcGxldGUnKTtcbiAgICAgICAgdGhpcy5jb250YWluZXIgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCdzd3AtY2FsZW5kYXItY29udGFpbmVyJyk7XG4gICAgICAgIC8vIEluaXRpYWxpemUgQ2FsZW5kYXJBcHBcbiAgICAgICAgYXdhaXQgdGhpcy5jYWxlbmRhckFwcC5pbml0KHRoaXMuY29udGFpbmVyKTtcbiAgICAgICAgY29uc29sZS5sb2coJ1tEZW1vQXBwXSBDYWxlbmRhckFwcCBpbml0aWFsaXplZCcpO1xuICAgICAgICAvLyBTZXR1cCBkZW1vIFVJIGhhbmRsZXJzXG4gICAgICAgIHRoaXMuc2V0dXBOYXZpZ2F0aW9uKCk7XG4gICAgICAgIHRoaXMuc2V0dXBEcmF3ZXJUb2dnbGUoKTtcbiAgICAgICAgdGhpcy5zZXR1cFZpZXdTd2l0Y2hpbmcoKTtcbiAgICAgICAgdGhpcy5zZXR1cFdvcmt3ZWVrU2VsZWN0b3IoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5zZXR1cFJlc291cmNlU2VsZWN0b3IoKTtcbiAgICAgICAgLy8gTGlzdGVuIGZvciBjYWxlbmRhciBzdGF0dXMgZXZlbnRzXG4gICAgICAgIHRoaXMuc2V0dXBTdGF0dXNMaXN0ZW5lcnMoKTtcbiAgICAgICAgLy8gSW5pdGlhbCByZW5kZXJcbiAgICAgICAgdGhpcy5ldmVudEJ1cy5lbWl0KENhbGVuZGFyRXZlbnRzLkNNRF9SRU5ERVIsIHsgdmlld0lkOiB0aGlzLmN1cnJlbnRWaWV3IH0pO1xuICAgIH1cbiAgICBzZXR1cE5hdmlnYXRpb24oKSB7XG4gICAgICAgIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdidG4tcHJldicpLm9uY2xpY2sgPSAoKSA9PiB7XG4gICAgICAgICAgICB0aGlzLmV2ZW50QnVzLmVtaXQoQ2FsZW5kYXJFdmVudHMuQ01EX05BVklHQVRFX1BSRVYpO1xuICAgICAgICB9O1xuICAgICAgICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnYnRuLW5leHQnKS5vbmNsaWNrID0gKCkgPT4ge1xuICAgICAgICAgICAgdGhpcy5ldmVudEJ1cy5lbWl0KENhbGVuZGFyRXZlbnRzLkNNRF9OQVZJR0FURV9ORVhUKTtcbiAgICAgICAgfTtcbiAgICB9XG4gICAgc2V0dXBWaWV3U3dpdGNoaW5nKCkge1xuICAgICAgICBjb25zdCBjaGlwcyA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoJy52aWV3LWNoaXAnKTtcbiAgICAgICAgY2hpcHMuZm9yRWFjaChjaGlwID0+IHtcbiAgICAgICAgICAgIGNoaXAuYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCAoKSA9PiB7XG4gICAgICAgICAgICAgICAgY2hpcHMuZm9yRWFjaChjID0+IGMuY2xhc3NMaXN0LnJlbW92ZSgnYWN0aXZlJykpO1xuICAgICAgICAgICAgICAgIGNoaXAuY2xhc3NMaXN0LmFkZCgnYWN0aXZlJyk7XG4gICAgICAgICAgICAgICAgY29uc3QgdmlldyA9IGNoaXAuZGF0YXNldC52aWV3O1xuICAgICAgICAgICAgICAgIGlmICh2aWV3KSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuY3VycmVudFZpZXcgPSB2aWV3O1xuICAgICAgICAgICAgICAgICAgICB0aGlzLnVwZGF0ZVNlbGVjdG9yVmlzaWJpbGl0eSgpO1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmV2ZW50QnVzLmVtaXQoQ2FsZW5kYXJFdmVudHMuQ01EX1JFTkRFUiwgeyB2aWV3SWQ6IHZpZXcgfSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgIH1cbiAgICB1cGRhdGVTZWxlY3RvclZpc2liaWxpdHkoKSB7XG4gICAgICAgIGNvbnN0IHNlbGVjdG9yID0gZG9jdW1lbnQucXVlcnlTZWxlY3Rvcignc3dwLXJlc291cmNlLXNlbGVjdG9yJyk7XG4gICAgICAgIGNvbnN0IHNob3dTZWxlY3RvciA9IHRoaXMuY3VycmVudFZpZXcgPT09ICdwaWNrZXInIHx8IHRoaXMuY3VycmVudFZpZXcgPT09ICdkYXknO1xuICAgICAgICBzZWxlY3Rvcj8uY2xhc3NMaXN0LnRvZ2dsZSgnaGlkZGVuJywgIXNob3dTZWxlY3Rvcik7XG4gICAgfVxuICAgIHNldHVwRHJhd2VyVG9nZ2xlKCkge1xuICAgICAgICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnYnRuLWRyYXdlcicpLm9uY2xpY2sgPSAoKSA9PiB7XG4gICAgICAgICAgICB0aGlzLmV2ZW50QnVzLmVtaXQoQ2FsZW5kYXJFdmVudHMuQ01EX0RSQVdFUl9UT0dHTEUpO1xuICAgICAgICB9O1xuICAgIH1cbiAgICBzZXR1cFdvcmt3ZWVrU2VsZWN0b3IoKSB7XG4gICAgICAgIGNvbnN0IHdvcmt3ZWVrU2VsZWN0ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3dvcmt3ZWVrLXNlbGVjdCcpO1xuICAgICAgICB3b3Jrd2Vla1NlbGVjdD8uYWRkRXZlbnRMaXN0ZW5lcignY2hhbmdlJywgKCkgPT4ge1xuICAgICAgICAgICAgY29uc3QgcHJlc2V0SWQgPSB3b3Jrd2Vla1NlbGVjdC52YWx1ZTtcbiAgICAgICAgICAgIHRoaXMuZXZlbnRCdXMuZW1pdChDYWxlbmRhckV2ZW50cy5DTURfV09SS1dFRUtfQ0hBTkdFLCB7IHByZXNldElkIH0pO1xuICAgICAgICB9KTtcbiAgICB9XG4gICAgYXN5bmMgc2V0dXBSZXNvdXJjZVNlbGVjdG9yKCkge1xuICAgICAgICBjb25zdCByZXNvdXJjZXMgPSBhd2FpdCB0aGlzLnJlc291cmNlU2VydmljZS5nZXRBbGwoKTtcbiAgICAgICAgY29uc3QgY29udGFpbmVyID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcignLnJlc291cmNlLWNoZWNrYm94ZXMnKTtcbiAgICAgICAgaWYgKCFjb250YWluZXIpXG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIGNvbnRhaW5lci5pbm5lckhUTUwgPSAnJztcbiAgICAgICAgcmVzb3VyY2VzLmZvckVhY2gociA9PiB7XG4gICAgICAgICAgICBjb25zdCBsYWJlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2xhYmVsJyk7XG4gICAgICAgICAgICBsYWJlbC5pbm5lckhUTUwgPSBgXHJcbiAgICAgICAgPGlucHV0IHR5cGU9XCJjaGVja2JveFwiIHZhbHVlPVwiJHtyLmlkfVwiIGNoZWNrZWQ+XHJcbiAgICAgICAgJHtyLmRpc3BsYXlOYW1lfVxyXG4gICAgICBgO1xuICAgICAgICAgICAgY29udGFpbmVyLmFwcGVuZENoaWxkKGxhYmVsKTtcbiAgICAgICAgfSk7XG4gICAgICAgIGNvbnRhaW5lci5hZGRFdmVudExpc3RlbmVyKCdjaGFuZ2UnLCAoKSA9PiB7XG4gICAgICAgICAgICBjb25zdCBjaGVja2VkID0gY29udGFpbmVyLnF1ZXJ5U2VsZWN0b3JBbGwoJ2lucHV0OmNoZWNrZWQnKTtcbiAgICAgICAgICAgIGNvbnN0IHZhbHVlcyA9IEFycmF5LmZyb20oY2hlY2tlZCkubWFwKGNiID0+IGNiLnZhbHVlKTtcbiAgICAgICAgICAgIHRoaXMuZXZlbnRCdXMuZW1pdChDYWxlbmRhckV2ZW50cy5DTURfVklFV19VUERBVEUsIHsgdHlwZTogJ3Jlc291cmNlJywgdmFsdWVzIH0pO1xuICAgICAgICB9KTtcbiAgICB9XG4gICAgc2V0dXBTdGF0dXNMaXN0ZW5lcnMoKSB7XG4gICAgICAgIHRoaXMuY29udGFpbmVyLmFkZEV2ZW50TGlzdGVuZXIoJ2NhbGVuZGFyOnN0YXR1czpyZWFkeScsICgpID0+IHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdbRGVtb0FwcF0gQ2FsZW5kYXIgcmVhZHknKTtcbiAgICAgICAgfSk7XG4gICAgICAgIHRoaXMuY29udGFpbmVyLmFkZEV2ZW50TGlzdGVuZXIoJ2NhbGVuZGFyOnN0YXR1czpyZW5kZXJlZCcsICgoZSkgPT4ge1xuICAgICAgICAgICAgY29uc29sZS5sb2coJ1tEZW1vQXBwXSBDYWxlbmRhciByZW5kZXJlZDonLCBlLmRldGFpbC52aWV3SWQpO1xuICAgICAgICB9KSk7XG4gICAgICAgIHRoaXMuY29udGFpbmVyLmFkZEV2ZW50TGlzdGVuZXIoJ2NhbGVuZGFyOnN0YXR1czplcnJvcicsICgoZSkgPT4ge1xuICAgICAgICAgICAgY29uc29sZS5lcnJvcignW0RlbW9BcHBdIENhbGVuZGFyIGVycm9yOicsIGUuZGV0YWlsLm1lc3NhZ2UpO1xuICAgICAgICB9KSk7XG4gICAgfVxufVxuIiwgIi8qKlxuICogQ2VudHJhbCBldmVudCBkaXNwYXRjaGVyIGZvciBjYWxlbmRhciB1c2luZyBET00gQ3VzdG9tRXZlbnRzXG4gKiBQcm92aWRlcyBsb2dnaW5nIGFuZCBkZWJ1Z2dpbmcgY2FwYWJpbGl0aWVzXG4gKi9cbmV4cG9ydCBjbGFzcyBFdmVudEJ1cyB7XG4gICAgY29uc3RydWN0b3IoKSB7XG4gICAgICAgIHRoaXMuZXZlbnRMb2cgPSBbXTtcbiAgICAgICAgdGhpcy5kZWJ1ZyA9IGZhbHNlO1xuICAgICAgICB0aGlzLmxpc3RlbmVycyA9IG5ldyBTZXQoKTtcbiAgICAgICAgLy8gTG9nIGNvbmZpZ3VyYXRpb24gZm9yIGRpZmZlcmVudCBjYXRlZ29yaWVzXG4gICAgICAgIHRoaXMubG9nQ29uZmlnID0ge1xuICAgICAgICAgICAgY2FsZW5kYXI6IHRydWUsXG4gICAgICAgICAgICBncmlkOiB0cnVlLFxuICAgICAgICAgICAgZXZlbnQ6IHRydWUsXG4gICAgICAgICAgICBzY3JvbGw6IHRydWUsXG4gICAgICAgICAgICBuYXZpZ2F0aW9uOiB0cnVlLFxuICAgICAgICAgICAgdmlldzogdHJ1ZSxcbiAgICAgICAgICAgIGRlZmF1bHQ6IHRydWVcbiAgICAgICAgfTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogU3Vic2NyaWJlIHRvIGFuIGV2ZW50IHZpYSBET00gYWRkRXZlbnRMaXN0ZW5lclxuICAgICAqL1xuICAgIG9uKGV2ZW50VHlwZSwgaGFuZGxlciwgb3B0aW9ucykge1xuICAgICAgICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKGV2ZW50VHlwZSwgaGFuZGxlciwgb3B0aW9ucyk7XG4gICAgICAgIC8vIFRyYWNrIGZvciBjbGVhbnVwXG4gICAgICAgIHRoaXMubGlzdGVuZXJzLmFkZCh7IGV2ZW50VHlwZSwgaGFuZGxlciwgb3B0aW9ucyB9KTtcbiAgICAgICAgLy8gUmV0dXJuIHVuc3Vic2NyaWJlIGZ1bmN0aW9uXG4gICAgICAgIHJldHVybiAoKSA9PiB0aGlzLm9mZihldmVudFR5cGUsIGhhbmRsZXIpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBTdWJzY3JpYmUgdG8gYW4gZXZlbnQgb25jZVxuICAgICAqL1xuICAgIG9uY2UoZXZlbnRUeXBlLCBoYW5kbGVyKSB7XG4gICAgICAgIHJldHVybiB0aGlzLm9uKGV2ZW50VHlwZSwgaGFuZGxlciwgeyBvbmNlOiB0cnVlIH0pO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBVbnN1YnNjcmliZSBmcm9tIGFuIGV2ZW50XG4gICAgICovXG4gICAgb2ZmKGV2ZW50VHlwZSwgaGFuZGxlcikge1xuICAgICAgICBkb2N1bWVudC5yZW1vdmVFdmVudExpc3RlbmVyKGV2ZW50VHlwZSwgaGFuZGxlcik7XG4gICAgICAgIC8vIFJlbW92ZSBmcm9tIHRyYWNraW5nXG4gICAgICAgIGZvciAoY29uc3QgbGlzdGVuZXIgb2YgdGhpcy5saXN0ZW5lcnMpIHtcbiAgICAgICAgICAgIGlmIChsaXN0ZW5lci5ldmVudFR5cGUgPT09IGV2ZW50VHlwZSAmJiBsaXN0ZW5lci5oYW5kbGVyID09PSBoYW5kbGVyKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5saXN0ZW5lcnMuZGVsZXRlKGxpc3RlbmVyKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cbiAgICAvKipcbiAgICAgKiBFbWl0IGFuIGV2ZW50IHZpYSBET00gQ3VzdG9tRXZlbnRcbiAgICAgKi9cbiAgICBlbWl0KGV2ZW50VHlwZSwgZGV0YWlsID0ge30pIHtcbiAgICAgICAgLy8gVmFsaWRhdGUgZXZlbnRUeXBlXG4gICAgICAgIGlmICghZXZlbnRUeXBlKSB7XG4gICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgZXZlbnQgPSBuZXcgQ3VzdG9tRXZlbnQoZXZlbnRUeXBlLCB7XG4gICAgICAgICAgICBkZXRhaWw6IGRldGFpbCA/PyB7fSxcbiAgICAgICAgICAgIGJ1YmJsZXM6IHRydWUsXG4gICAgICAgICAgICBjYW5jZWxhYmxlOiB0cnVlXG4gICAgICAgIH0pO1xuICAgICAgICAvLyBMb2cgZXZlbnQgd2l0aCBncm91cGluZ1xuICAgICAgICBpZiAodGhpcy5kZWJ1Zykge1xuICAgICAgICAgICAgdGhpcy5sb2dFdmVudFdpdGhHcm91cGluZyhldmVudFR5cGUsIGRldGFpbCk7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5ldmVudExvZy5wdXNoKHtcbiAgICAgICAgICAgIHR5cGU6IGV2ZW50VHlwZSxcbiAgICAgICAgICAgIGRldGFpbDogZGV0YWlsID8/IHt9LFxuICAgICAgICAgICAgdGltZXN0YW1wOiBEYXRlLm5vdygpXG4gICAgICAgIH0pO1xuICAgICAgICAvLyBFbWl0IG9uIGRvY3VtZW50IChvbmx5IERPTSBldmVudHMgbm93KVxuICAgICAgICByZXR1cm4gIWRvY3VtZW50LmRpc3BhdGNoRXZlbnQoZXZlbnQpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBMb2cgZXZlbnQgd2l0aCBjb25zb2xlIGdyb3VwaW5nXG4gICAgICovXG4gICAgbG9nRXZlbnRXaXRoR3JvdXBpbmcoZXZlbnRUeXBlLCBfZGV0YWlsKSB7XG4gICAgICAgIC8vIEV4dHJhY3QgY2F0ZWdvcnkgZnJvbSBldmVudCB0eXBlIChlLmcuLCAnY2FsZW5kYXI6ZGF0ZWNoYW5nZWQnIFx1MjE5MiAnY2FsZW5kYXInKVxuICAgICAgICBjb25zdCBjYXRlZ29yeSA9IHRoaXMuZXh0cmFjdENhdGVnb3J5KGV2ZW50VHlwZSk7XG4gICAgICAgIC8vIE9ubHkgbG9nIGlmIGNhdGVnb3J5IGlzIGVuYWJsZWRcbiAgICAgICAgaWYgKCF0aGlzLmxvZ0NvbmZpZ1tjYXRlZ29yeV0pIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICAvLyBHZXQgY2F0ZWdvcnkgZW1vamkgYW5kIGNvbG9yICh1c2VkIGZvciBmdXR1cmUgY29uc29sZSBzdHlsaW5nKVxuICAgICAgICB0aGlzLmdldENhdGVnb3J5U3R5bGUoY2F0ZWdvcnkpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBFeHRyYWN0IGNhdGVnb3J5IGZyb20gZXZlbnQgdHlwZVxuICAgICAqL1xuICAgIGV4dHJhY3RDYXRlZ29yeShldmVudFR5cGUpIHtcbiAgICAgICAgaWYgKCFldmVudFR5cGUpIHtcbiAgICAgICAgICAgIHJldHVybiAndW5rbm93bic7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGV2ZW50VHlwZS5pbmNsdWRlcygnOicpKSB7XG4gICAgICAgICAgICByZXR1cm4gZXZlbnRUeXBlLnNwbGl0KCc6JylbMF07XG4gICAgICAgIH1cbiAgICAgICAgLy8gRmFsbGJhY2s6IHRyeSB0byBkZXRlY3QgY2F0ZWdvcnkgZnJvbSBldmVudCBuYW1lIHBhdHRlcm5zXG4gICAgICAgIGNvbnN0IGxvd2VyVHlwZSA9IGV2ZW50VHlwZS50b0xvd2VyQ2FzZSgpO1xuICAgICAgICBpZiAobG93ZXJUeXBlLmluY2x1ZGVzKCdncmlkJykgfHwgbG93ZXJUeXBlLmluY2x1ZGVzKCdyZW5kZXJlZCcpKVxuICAgICAgICAgICAgcmV0dXJuICdncmlkJztcbiAgICAgICAgaWYgKGxvd2VyVHlwZS5pbmNsdWRlcygnZXZlbnQnKSB8fCBsb3dlclR5cGUuaW5jbHVkZXMoJ3N5bmMnKSlcbiAgICAgICAgICAgIHJldHVybiAnZXZlbnQnO1xuICAgICAgICBpZiAobG93ZXJUeXBlLmluY2x1ZGVzKCdzY3JvbGwnKSlcbiAgICAgICAgICAgIHJldHVybiAnc2Nyb2xsJztcbiAgICAgICAgaWYgKGxvd2VyVHlwZS5pbmNsdWRlcygnbmF2JykgfHwgbG93ZXJUeXBlLmluY2x1ZGVzKCdkYXRlJykpXG4gICAgICAgICAgICByZXR1cm4gJ25hdmlnYXRpb24nO1xuICAgICAgICBpZiAobG93ZXJUeXBlLmluY2x1ZGVzKCd2aWV3JykpXG4gICAgICAgICAgICByZXR1cm4gJ3ZpZXcnO1xuICAgICAgICByZXR1cm4gJ2RlZmF1bHQnO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBHZXQgc3R5bGluZyBmb3IgZGlmZmVyZW50IGNhdGVnb3JpZXNcbiAgICAgKi9cbiAgICBnZXRDYXRlZ29yeVN0eWxlKGNhdGVnb3J5KSB7XG4gICAgICAgIGNvbnN0IHN0eWxlcyA9IHtcbiAgICAgICAgICAgIGNhbGVuZGFyOiB7IGVtb2ppOiAnXHVEODNEXHVEQ0M1JywgY29sb3I6ICcjMjE5NkYzJyB9LFxuICAgICAgICAgICAgZ3JpZDogeyBlbW9qaTogJ1x1RDgzRFx1RENDQScsIGNvbG9yOiAnIzRDQUY1MCcgfSxcbiAgICAgICAgICAgIGV2ZW50OiB7IGVtb2ppOiAnXHVEODNEXHVEQ0NDJywgY29sb3I6ICcjRkY5ODAwJyB9LFxuICAgICAgICAgICAgc2Nyb2xsOiB7IGVtb2ppOiAnXHVEODNEXHVEQ0RDJywgY29sb3I6ICcjOUMyN0IwJyB9LFxuICAgICAgICAgICAgbmF2aWdhdGlvbjogeyBlbW9qaTogJ1x1RDgzRVx1RERFRCcsIGNvbG9yOiAnI0Y0NDMzNicgfSxcbiAgICAgICAgICAgIHZpZXc6IHsgZW1vamk6ICdcdUQ4M0RcdURDNDEnLCBjb2xvcjogJyMwMEJDRDQnIH0sXG4gICAgICAgICAgICBkZWZhdWx0OiB7IGVtb2ppOiAnXHVEODNEXHVEQ0UyJywgY29sb3I6ICcjNjA3RDhCJyB9XG4gICAgICAgIH07XG4gICAgICAgIHJldHVybiBzdHlsZXNbY2F0ZWdvcnldIHx8IHN0eWxlcy5kZWZhdWx0O1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBDb25maWd1cmUgbG9nZ2luZyBmb3Igc3BlY2lmaWMgY2F0ZWdvcmllc1xuICAgICAqL1xuICAgIHNldExvZ0NvbmZpZyhjb25maWcpIHtcbiAgICAgICAgdGhpcy5sb2dDb25maWcgPSB7IC4uLnRoaXMubG9nQ29uZmlnLCAuLi5jb25maWcgfTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogR2V0IGN1cnJlbnQgbG9nIGNvbmZpZ3VyYXRpb25cbiAgICAgKi9cbiAgICBnZXRMb2dDb25maWcoKSB7XG4gICAgICAgIHJldHVybiB7IC4uLnRoaXMubG9nQ29uZmlnIH07XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEdldCBldmVudCBoaXN0b3J5XG4gICAgICovXG4gICAgZ2V0RXZlbnRMb2coZXZlbnRUeXBlKSB7XG4gICAgICAgIGlmIChldmVudFR5cGUpIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLmV2ZW50TG9nLmZpbHRlcihlID0+IGUudHlwZSA9PT0gZXZlbnRUeXBlKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdGhpcy5ldmVudExvZztcbiAgICB9XG4gICAgLyoqXG4gICAgICogRW5hYmxlL2Rpc2FibGUgZGVidWcgbW9kZVxuICAgICAqL1xuICAgIHNldERlYnVnKGVuYWJsZWQpIHtcbiAgICAgICAgdGhpcy5kZWJ1ZyA9IGVuYWJsZWQ7XG4gICAgfVxufVxuIiwgIi8qKlxuICogSW5kZXhlZERCQ29udGV4dCAtIERhdGFiYXNlIGNvbm5lY3Rpb24gbWFuYWdlclxuICpcbiAqIFJFU1BPTlNJQklMSVRZOlxuICogLSBPcGVucyBhbmQgbWFuYWdlcyBJREJEYXRhYmFzZSBjb25uZWN0aW9uIGxpZmVjeWNsZVxuICogLSBDcmVhdGVzIG9iamVjdCBzdG9yZXMgdmlhIGluamVjdGVkIElTdG9yZSBpbXBsZW1lbnRhdGlvbnNcbiAqIC0gUHJvdmlkZXMgc2hhcmVkIElEQkRhdGFiYXNlIGluc3RhbmNlIHRvIGFsbCBzZXJ2aWNlc1xuICovXG5leHBvcnQgY2xhc3MgSW5kZXhlZERCQ29udGV4dCB7XG4gICAgY29uc3RydWN0b3Ioc3RvcmVzKSB7XG4gICAgICAgIHRoaXMuZGIgPSBudWxsO1xuICAgICAgICB0aGlzLmluaXRpYWxpemVkID0gZmFsc2U7XG4gICAgICAgIHRoaXMuc3RvcmVzID0gc3RvcmVzO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBJbml0aWFsaXplIGFuZCBvcGVuIHRoZSBkYXRhYmFzZVxuICAgICAqL1xuICAgIGFzeW5jIGluaXRpYWxpemUoKSB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgICAgICBjb25zdCByZXF1ZXN0ID0gaW5kZXhlZERCLm9wZW4oSW5kZXhlZERCQ29udGV4dC5EQl9OQU1FLCBJbmRleGVkREJDb250ZXh0LkRCX1ZFUlNJT04pO1xuICAgICAgICAgICAgcmVxdWVzdC5vbmVycm9yID0gKCkgPT4ge1xuICAgICAgICAgICAgICAgIHJlamVjdChuZXcgRXJyb3IoYEZhaWxlZCB0byBvcGVuIEluZGV4ZWREQjogJHtyZXF1ZXN0LmVycm9yfWApKTtcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICByZXF1ZXN0Lm9uc3VjY2VzcyA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICB0aGlzLmRiID0gcmVxdWVzdC5yZXN1bHQ7XG4gICAgICAgICAgICAgICAgdGhpcy5pbml0aWFsaXplZCA9IHRydWU7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSgpO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIHJlcXVlc3Qub251cGdyYWRlbmVlZGVkID0gKGV2ZW50KSA9PiB7XG4gICAgICAgICAgICAgICAgY29uc3QgZGIgPSBldmVudC50YXJnZXQucmVzdWx0O1xuICAgICAgICAgICAgICAgIC8vIENyZWF0ZSBhbGwgZW50aXR5IHN0b3JlcyB2aWEgaW5qZWN0ZWQgSVN0b3JlIGltcGxlbWVudGF0aW9uc1xuICAgICAgICAgICAgICAgIHRoaXMuc3RvcmVzLmZvckVhY2goc3RvcmUgPT4ge1xuICAgICAgICAgICAgICAgICAgICBpZiAoIWRiLm9iamVjdFN0b3JlTmFtZXMuY29udGFpbnMoc3RvcmUuc3RvcmVOYW1lKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgc3RvcmUuY3JlYXRlKGRiKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgfSk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIENoZWNrIGlmIGRhdGFiYXNlIGlzIGluaXRpYWxpemVkXG4gICAgICovXG4gICAgaXNJbml0aWFsaXplZCgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuaW5pdGlhbGl6ZWQ7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEdldCBJREJEYXRhYmFzZSBpbnN0YW5jZVxuICAgICAqL1xuICAgIGdldERhdGFiYXNlKCkge1xuICAgICAgICBpZiAoIXRoaXMuZGIpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignSW5kZXhlZERCIG5vdCBpbml0aWFsaXplZC4gQ2FsbCBpbml0aWFsaXplKCkgZmlyc3QuJyk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRoaXMuZGI7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIENsb3NlIGRhdGFiYXNlIGNvbm5lY3Rpb25cbiAgICAgKi9cbiAgICBjbG9zZSgpIHtcbiAgICAgICAgaWYgKHRoaXMuZGIpIHtcbiAgICAgICAgICAgIHRoaXMuZGIuY2xvc2UoKTtcbiAgICAgICAgICAgIHRoaXMuZGIgPSBudWxsO1xuICAgICAgICAgICAgdGhpcy5pbml0aWFsaXplZCA9IGZhbHNlO1xuICAgICAgICB9XG4gICAgfVxuICAgIC8qKlxuICAgICAqIERlbGV0ZSBlbnRpcmUgZGF0YWJhc2UgKGZvciB0ZXN0aW5nL3Jlc2V0KVxuICAgICAqL1xuICAgIHN0YXRpYyBhc3luYyBkZWxldGVEYXRhYmFzZSgpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHJlcXVlc3QgPSBpbmRleGVkREIuZGVsZXRlRGF0YWJhc2UoSW5kZXhlZERCQ29udGV4dC5EQl9OQU1FKTtcbiAgICAgICAgICAgIHJlcXVlc3Qub25zdWNjZXNzID0gKCkgPT4gcmVzb2x2ZSgpO1xuICAgICAgICAgICAgcmVxdWVzdC5vbmVycm9yID0gKCkgPT4gcmVqZWN0KG5ldyBFcnJvcihgRmFpbGVkIHRvIGRlbGV0ZSBkYXRhYmFzZTogJHtyZXF1ZXN0LmVycm9yfWApKTtcbiAgICAgICAgfSk7XG4gICAgfVxufVxuSW5kZXhlZERCQ29udGV4dC5EQl9OQU1FID0gJ0NhbGVuZGFyVjJEQic7XG5JbmRleGVkREJDb250ZXh0LkRCX1ZFUlNJT04gPSA0O1xuIiwgIi8qKlxuICogRXZlbnRTdG9yZSAtIEluZGV4ZWREQiBPYmplY3RTdG9yZSBkZWZpbml0aW9uIGZvciBjYWxlbmRhciBldmVudHNcbiAqL1xuZXhwb3J0IGNsYXNzIEV2ZW50U3RvcmUge1xuICAgIGNvbnN0cnVjdG9yKCkge1xuICAgICAgICB0aGlzLnN0b3JlTmFtZSA9IEV2ZW50U3RvcmUuU1RPUkVfTkFNRTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogQ3JlYXRlIHRoZSBldmVudHMgT2JqZWN0U3RvcmUgd2l0aCBpbmRleGVzXG4gICAgICovXG4gICAgY3JlYXRlKGRiKSB7XG4gICAgICAgIGNvbnN0IHN0b3JlID0gZGIuY3JlYXRlT2JqZWN0U3RvcmUoRXZlbnRTdG9yZS5TVE9SRV9OQU1FLCB7IGtleVBhdGg6ICdpZCcgfSk7XG4gICAgICAgIC8vIEluZGV4OiBzdGFydCAoZm9yIGRhdGUgcmFuZ2UgcXVlcmllcylcbiAgICAgICAgc3RvcmUuY3JlYXRlSW5kZXgoJ3N0YXJ0JywgJ3N0YXJ0JywgeyB1bmlxdWU6IGZhbHNlIH0pO1xuICAgICAgICAvLyBJbmRleDogZW5kIChmb3IgZGF0ZSByYW5nZSBxdWVyaWVzKVxuICAgICAgICBzdG9yZS5jcmVhdGVJbmRleCgnZW5kJywgJ2VuZCcsIHsgdW5pcXVlOiBmYWxzZSB9KTtcbiAgICAgICAgLy8gSW5kZXg6IHN5bmNTdGF0dXMgKGZvciBmaWx0ZXJpbmcgYnkgc3luYyBzdGF0ZSlcbiAgICAgICAgc3RvcmUuY3JlYXRlSW5kZXgoJ3N5bmNTdGF0dXMnLCAnc3luY1N0YXR1cycsIHsgdW5pcXVlOiBmYWxzZSB9KTtcbiAgICAgICAgLy8gSW5kZXg6IHJlc291cmNlSWQgKGZvciByZXNvdXJjZS1tb2RlIGZpbHRlcmluZylcbiAgICAgICAgc3RvcmUuY3JlYXRlSW5kZXgoJ3Jlc291cmNlSWQnLCAncmVzb3VyY2VJZCcsIHsgdW5pcXVlOiBmYWxzZSB9KTtcbiAgICAgICAgLy8gSW5kZXg6IGN1c3RvbWVySWQgKGZvciBjdXN0b21lci1jZW50cmljIHF1ZXJpZXMpXG4gICAgICAgIHN0b3JlLmNyZWF0ZUluZGV4KCdjdXN0b21lcklkJywgJ2N1c3RvbWVySWQnLCB7IHVuaXF1ZTogZmFsc2UgfSk7XG4gICAgICAgIC8vIEluZGV4OiBib29raW5nSWQgKGZvciBldmVudC10by1ib29raW5nIGxvb2t1cHMpXG4gICAgICAgIHN0b3JlLmNyZWF0ZUluZGV4KCdib29raW5nSWQnLCAnYm9va2luZ0lkJywgeyB1bmlxdWU6IGZhbHNlIH0pO1xuICAgICAgICAvLyBDb21wb3VuZCBpbmRleDogc3RhcnRFbmQgKGZvciBvcHRpbWl6ZWQgcmFuZ2UgcXVlcmllcylcbiAgICAgICAgc3RvcmUuY3JlYXRlSW5kZXgoJ3N0YXJ0RW5kJywgWydzdGFydCcsICdlbmQnXSwgeyB1bmlxdWU6IGZhbHNlIH0pO1xuICAgIH1cbn1cbkV2ZW50U3RvcmUuU1RPUkVfTkFNRSA9ICdldmVudHMnO1xuIiwgIi8qKlxuICogRXZlbnRTZXJpYWxpemF0aW9uIC0gSGFuZGxlcyBEYXRlIGZpZWxkIHNlcmlhbGl6YXRpb24gZm9yIEluZGV4ZWREQlxuICpcbiAqIEluZGV4ZWREQiBkb2Vzbid0IHN0b3JlIERhdGUgb2JqZWN0cyBkaXJlY3RseSwgc28gd2UgY29udmVydDpcbiAqIC0gRGF0ZSBcdTIxOTIgSVNPIHN0cmluZyAoc2VyaWFsaXplKSB3aGVuIHdyaXRpbmdcbiAqIC0gSVNPIHN0cmluZyBcdTIxOTIgRGF0ZSAoZGVzZXJpYWxpemUpIHdoZW4gcmVhZGluZ1xuICovXG5leHBvcnQgY2xhc3MgRXZlbnRTZXJpYWxpemF0aW9uIHtcbiAgICAvKipcbiAgICAgKiBTZXJpYWxpemUgZXZlbnQgZm9yIEluZGV4ZWREQiBzdG9yYWdlXG4gICAgICovXG4gICAgc3RhdGljIHNlcmlhbGl6ZShldmVudCkge1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgLi4uZXZlbnQsXG4gICAgICAgICAgICBzdGFydDogZXZlbnQuc3RhcnQgaW5zdGFuY2VvZiBEYXRlID8gZXZlbnQuc3RhcnQudG9JU09TdHJpbmcoKSA6IGV2ZW50LnN0YXJ0LFxuICAgICAgICAgICAgZW5kOiBldmVudC5lbmQgaW5zdGFuY2VvZiBEYXRlID8gZXZlbnQuZW5kLnRvSVNPU3RyaW5nKCkgOiBldmVudC5lbmRcbiAgICAgICAgfTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogRGVzZXJpYWxpemUgZXZlbnQgZnJvbSBJbmRleGVkREIgc3RvcmFnZVxuICAgICAqL1xuICAgIHN0YXRpYyBkZXNlcmlhbGl6ZShkYXRhKSB7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAuLi5kYXRhLFxuICAgICAgICAgICAgc3RhcnQ6IHR5cGVvZiBkYXRhLnN0YXJ0ID09PSAnc3RyaW5nJyA/IG5ldyBEYXRlKGRhdGEuc3RhcnQpIDogZGF0YS5zdGFydCxcbiAgICAgICAgICAgIGVuZDogdHlwZW9mIGRhdGEuZW5kID09PSAnc3RyaW5nJyA/IG5ldyBEYXRlKGRhdGEuZW5kKSA6IGRhdGEuZW5kXG4gICAgICAgIH07XG4gICAgfVxufVxuIiwgIi8qKlxuICogU3luY1BsdWdpbjxUIGV4dGVuZHMgSVN5bmM+IC0gUGx1Z2dhYmxlIHN5bmMgZnVuY3Rpb25hbGl0eSBmb3IgZW50aXR5IHNlcnZpY2VzXG4gKlxuICogQ09NUE9TSVRJT04gUEFUVEVSTjpcbiAqIC0gRW5jYXBzdWxhdGVzIGFsbCBzeW5jLXJlbGF0ZWQgbG9naWMgaW4gc2VwYXJhdGUgY2xhc3NcbiAqIC0gQ29tcG9zZWQgaW50byBCYXNlRW50aXR5U2VydmljZSAobm90IGluaGVyaXRhbmNlKVxuICovXG5leHBvcnQgY2xhc3MgU3luY1BsdWdpbiB7XG4gICAgY29uc3RydWN0b3Ioc2VydmljZSkge1xuICAgICAgICB0aGlzLnNlcnZpY2UgPSBzZXJ2aWNlO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBNYXJrIGVudGl0eSBhcyBzdWNjZXNzZnVsbHkgc3luY2VkXG4gICAgICovXG4gICAgYXN5bmMgbWFya0FzU3luY2VkKGlkKSB7XG4gICAgICAgIGNvbnN0IGVudGl0eSA9IGF3YWl0IHRoaXMuc2VydmljZS5nZXQoaWQpO1xuICAgICAgICBpZiAoZW50aXR5KSB7XG4gICAgICAgICAgICBlbnRpdHkuc3luY1N0YXR1cyA9ICdzeW5jZWQnO1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5zZXJ2aWNlLnNhdmUoZW50aXR5KTtcbiAgICAgICAgfVxuICAgIH1cbiAgICAvKipcbiAgICAgKiBNYXJrIGVudGl0eSBhcyBzeW5jIGVycm9yXG4gICAgICovXG4gICAgYXN5bmMgbWFya0FzRXJyb3IoaWQpIHtcbiAgICAgICAgY29uc3QgZW50aXR5ID0gYXdhaXQgdGhpcy5zZXJ2aWNlLmdldChpZCk7XG4gICAgICAgIGlmIChlbnRpdHkpIHtcbiAgICAgICAgICAgIGVudGl0eS5zeW5jU3RhdHVzID0gJ2Vycm9yJztcbiAgICAgICAgICAgIGF3YWl0IHRoaXMuc2VydmljZS5zYXZlKGVudGl0eSk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgLyoqXG4gICAgICogR2V0IGN1cnJlbnQgc3luYyBzdGF0dXMgZm9yIGFuIGVudGl0eVxuICAgICAqL1xuICAgIGFzeW5jIGdldFN5bmNTdGF0dXMoaWQpIHtcbiAgICAgICAgY29uc3QgZW50aXR5ID0gYXdhaXQgdGhpcy5zZXJ2aWNlLmdldChpZCk7XG4gICAgICAgIHJldHVybiBlbnRpdHkgPyBlbnRpdHkuc3luY1N0YXR1cyA6IG51bGw7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEdldCBlbnRpdGllcyBieSBzeW5jIHN0YXR1cyB1c2luZyBJbmRleGVkREIgaW5kZXhcbiAgICAgKi9cbiAgICBhc3luYyBnZXRCeVN5bmNTdGF0dXMoc3luY1N0YXR1cykge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICAgICAgY29uc3QgdHJhbnNhY3Rpb24gPSB0aGlzLnNlcnZpY2UuZGIudHJhbnNhY3Rpb24oW3RoaXMuc2VydmljZS5zdG9yZU5hbWVdLCAncmVhZG9ubHknKTtcbiAgICAgICAgICAgIGNvbnN0IHN0b3JlID0gdHJhbnNhY3Rpb24ub2JqZWN0U3RvcmUodGhpcy5zZXJ2aWNlLnN0b3JlTmFtZSk7XG4gICAgICAgICAgICBjb25zdCBpbmRleCA9IHN0b3JlLmluZGV4KCdzeW5jU3RhdHVzJyk7XG4gICAgICAgICAgICBjb25zdCByZXF1ZXN0ID0gaW5kZXguZ2V0QWxsKHN5bmNTdGF0dXMpO1xuICAgICAgICAgICAgcmVxdWVzdC5vbnN1Y2Nlc3MgPSAoKSA9PiB7XG4gICAgICAgICAgICAgICAgY29uc3QgZGF0YSA9IHJlcXVlc3QucmVzdWx0O1xuICAgICAgICAgICAgICAgIGNvbnN0IGVudGl0aWVzID0gZGF0YS5tYXAoaXRlbSA9PiB0aGlzLnNlcnZpY2UuZGVzZXJpYWxpemUoaXRlbSkpO1xuICAgICAgICAgICAgICAgIHJlc29sdmUoZW50aXRpZXMpO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIHJlcXVlc3Qub25lcnJvciA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICByZWplY3QobmV3IEVycm9yKGBGYWlsZWQgdG8gZ2V0IGJ5IHN5bmMgc3RhdHVzICR7c3luY1N0YXR1c306ICR7cmVxdWVzdC5lcnJvcn1gKSk7XG4gICAgICAgICAgICB9O1xuICAgICAgICB9KTtcbiAgICB9XG59XG4iLCAiLyoqXG4gKiBDb3JlRXZlbnRzIC0gQ29uc29saWRhdGVkIGVzc2VudGlhbCBldmVudHMgZm9yIHRoZSBjYWxlbmRhciBWMlxuICovXG5leHBvcnQgY29uc3QgQ29yZUV2ZW50cyA9IHtcbiAgICAvLyBMaWZlY3ljbGUgZXZlbnRzXG4gICAgSU5JVElBTElaRUQ6ICdjb3JlOmluaXRpYWxpemVkJyxcbiAgICBSRUFEWTogJ2NvcmU6cmVhZHknLFxuICAgIERFU1RST1lFRDogJ2NvcmU6ZGVzdHJveWVkJyxcbiAgICAvLyBWaWV3IGV2ZW50c1xuICAgIFZJRVdfQ0hBTkdFRDogJ3ZpZXc6Y2hhbmdlZCcsXG4gICAgVklFV19SRU5ERVJFRDogJ3ZpZXc6cmVuZGVyZWQnLFxuICAgIC8vIE5hdmlnYXRpb24gZXZlbnRzXG4gICAgREFURV9DSEFOR0VEOiAnbmF2OmRhdGUtY2hhbmdlZCcsXG4gICAgTkFWSUdBVElPTl9DT01QTEVURUQ6ICduYXY6bmF2aWdhdGlvbi1jb21wbGV0ZWQnLFxuICAgIC8vIERhdGEgZXZlbnRzXG4gICAgREFUQV9MT0FESU5HOiAnZGF0YTpsb2FkaW5nJyxcbiAgICBEQVRBX0xPQURFRDogJ2RhdGE6bG9hZGVkJyxcbiAgICBEQVRBX0VSUk9SOiAnZGF0YTplcnJvcicsXG4gICAgLy8gR3JpZCBldmVudHNcbiAgICBHUklEX1JFTkRFUkVEOiAnZ3JpZDpyZW5kZXJlZCcsXG4gICAgR1JJRF9DTElDS0VEOiAnZ3JpZDpjbGlja2VkJyxcbiAgICAvLyBFdmVudCBtYW5hZ2VtZW50XG4gICAgRVZFTlRfQ1JFQVRFRDogJ2V2ZW50OmNyZWF0ZWQnLFxuICAgIEVWRU5UX1VQREFURUQ6ICdldmVudDp1cGRhdGVkJyxcbiAgICBFVkVOVF9ERUxFVEVEOiAnZXZlbnQ6ZGVsZXRlZCcsXG4gICAgRVZFTlRfU0VMRUNURUQ6ICdldmVudDpzZWxlY3RlZCcsXG4gICAgLy8gRXZlbnQgZHJhZy1kcm9wXG4gICAgRVZFTlRfRFJBR19TVEFSVDogJ2V2ZW50OmRyYWctc3RhcnQnLFxuICAgIEVWRU5UX0RSQUdfTU9WRTogJ2V2ZW50OmRyYWctbW92ZScsXG4gICAgRVZFTlRfRFJBR19FTkQ6ICdldmVudDpkcmFnLWVuZCcsXG4gICAgRVZFTlRfRFJBR19DQU5DRUw6ICdldmVudDpkcmFnLWNhbmNlbCcsXG4gICAgRVZFTlRfRFJBR19DT0xVTU5fQ0hBTkdFOiAnZXZlbnQ6ZHJhZy1jb2x1bW4tY2hhbmdlJyxcbiAgICAvLyBIZWFkZXIgZHJhZyAodGltZWQgXHUyMTkyIGhlYWRlciBjb252ZXJzaW9uKVxuICAgIEVWRU5UX0RSQUdfRU5URVJfSEVBREVSOiAnZXZlbnQ6ZHJhZy1lbnRlci1oZWFkZXInLFxuICAgIEVWRU5UX0RSQUdfTU9WRV9IRUFERVI6ICdldmVudDpkcmFnLW1vdmUtaGVhZGVyJyxcbiAgICBFVkVOVF9EUkFHX0xFQVZFX0hFQURFUjogJ2V2ZW50OmRyYWctbGVhdmUtaGVhZGVyJyxcbiAgICAvLyBFdmVudCByZXNpemVcbiAgICBFVkVOVF9SRVNJWkVfU1RBUlQ6ICdldmVudDpyZXNpemUtc3RhcnQnLFxuICAgIEVWRU5UX1JFU0laRV9FTkQ6ICdldmVudDpyZXNpemUtZW5kJyxcbiAgICAvLyBFZGdlIHNjcm9sbFxuICAgIEVER0VfU0NST0xMX1RJQ0s6ICdlZGdlLXNjcm9sbDp0aWNrJyxcbiAgICBFREdFX1NDUk9MTF9TVEFSVEVEOiAnZWRnZS1zY3JvbGw6c3RhcnRlZCcsXG4gICAgRURHRV9TQ1JPTExfU1RPUFBFRDogJ2VkZ2Utc2Nyb2xsOnN0b3BwZWQnLFxuICAgIC8vIFN5c3RlbSBldmVudHNcbiAgICBFUlJPUjogJ3N5c3RlbTplcnJvcicsXG4gICAgLy8gU3luYyBldmVudHNcbiAgICBTWU5DX1NUQVJURUQ6ICdzeW5jOnN0YXJ0ZWQnLFxuICAgIFNZTkNfQ09NUExFVEVEOiAnc3luYzpjb21wbGV0ZWQnLFxuICAgIFNZTkNfRkFJTEVEOiAnc3luYzpmYWlsZWQnLFxuICAgIC8vIEVudGl0eSBldmVudHMgLSBmb3IgYXVkaXQgYW5kIHN5bmNcbiAgICBFTlRJVFlfU0FWRUQ6ICdlbnRpdHk6c2F2ZWQnLFxuICAgIEVOVElUWV9ERUxFVEVEOiAnZW50aXR5OmRlbGV0ZWQnLFxuICAgIC8vIEF1ZGl0IGV2ZW50c1xuICAgIEFVRElUX0xPR0dFRDogJ2F1ZGl0OmxvZ2dlZCcsXG4gICAgLy8gUmVuZGVyaW5nIGV2ZW50c1xuICAgIEVWRU5UU19SRU5ERVJFRDogJ2V2ZW50czpyZW5kZXJlZCdcbn07XG4iLCAiZXhwb3J0IGZ1bmN0aW9uIHNwbGl0SlNPTlBhdGgocGF0aDogc3RyaW5nKTogc3RyaW5nW10ge1xuICAgIGNvbnN0IHBhcnRzOiBzdHJpbmdbXSA9IFtdO1xuICAgIGxldCBjdXJyZW50UGFydCA9ICcnO1xuICAgIGxldCBpblNpbmdsZVF1b3RlcyA9IGZhbHNlO1xuICAgIGxldCBpbkJyYWNrZXRzID0gMDtcblxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgcGF0aC5sZW5ndGg7IGkrKykge1xuICAgICAgICBjb25zdCBjaGFyID0gcGF0aFtpXTtcblxuICAgICAgICBpZiAoY2hhciA9PT0gXCInXCIgJiYgcGF0aFtpIC0gMV0gIT09ICdcXFxcJykge1xuICAgICAgICAgICAgLy8gVG9nZ2xlIHNpbmdsZSBxdW90ZSBmbGFnIGlmIG5vdCBlc2NhcGVkXG4gICAgICAgICAgICBpblNpbmdsZVF1b3RlcyA9ICFpblNpbmdsZVF1b3RlcztcbiAgICAgICAgfSBlbHNlIGlmIChjaGFyID09PSAnWycgJiYgIWluU2luZ2xlUXVvdGVzKSB7XG4gICAgICAgICAgICAvLyBJbmNyZWFzZSBicmFja2V0IG5lc3RpbmcgbGV2ZWxcbiAgICAgICAgICAgIGluQnJhY2tldHMrKztcbiAgICAgICAgfSBlbHNlIGlmIChjaGFyID09PSAnXScgJiYgIWluU2luZ2xlUXVvdGVzKSB7XG4gICAgICAgICAgICAvLyBEZWNyZWFzZSBicmFja2V0IG5lc3RpbmcgbGV2ZWxcbiAgICAgICAgICAgIGluQnJhY2tldHMtLTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChjaGFyID09PSAnLicgJiYgIWluU2luZ2xlUXVvdGVzICYmIGluQnJhY2tldHMgPT09IDApIHtcbiAgICAgICAgICAgIC8vIFNwbGl0IGF0IHBlcmlvZCBpZiBub3QgaW4gcXVvdGVzIG9yIGJyYWNrZXRzXG4gICAgICAgICAgICBwYXJ0cy5wdXNoKGN1cnJlbnRQYXJ0KTtcbiAgICAgICAgICAgIGN1cnJlbnRQYXJ0ID0gJyc7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAvLyBPdGhlcndpc2UsIGtlZXAgYWRkaW5nIHRvIHRoZSBjdXJyZW50IHBhcnRcbiAgICAgICAgICAgIGN1cnJlbnRQYXJ0ICs9IGNoYXI7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBBZGQgdGhlIGxhc3QgcGFydCBpZiB0aGVyZSdzIGFueVxuICAgIGlmIChjdXJyZW50UGFydCAhPT0gJycpIHtcbiAgICAgICAgcGFydHMucHVzaChjdXJyZW50UGFydCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHBhcnRzO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gYXJyYXlEaWZmZXJlbmNlPFQ+KGZpcnN0OiBUW10sIHNlY29uZDogVFtdKTogVFtdIHtcbiAgICBjb25zdCBzZWNvbmRTZXQgPSBuZXcgU2V0KHNlY29uZCk7XG4gICAgcmV0dXJuIGZpcnN0LmZpbHRlcihpdGVtID0+ICFzZWNvbmRTZXQuaGFzKGl0ZW0pKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGFycmF5SW50ZXJzZWN0aW9uPFQ+KGZpcnN0OiBUW10sIHNlY29uZDogVFtdKTogVFtdIHtcbiAgICBjb25zdCBzZWNvbmRTZXQgPSBuZXcgU2V0KHNlY29uZCk7XG4gICAgcmV0dXJuIGZpcnN0LmZpbHRlcihpdGVtID0+IHNlY29uZFNldC5oYXMoaXRlbSkpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24ga2V5Qnk8VD4oYXJyOiBUW10sIGdldEtleTogKGl0ZW06IFQpID0+IGFueSk6IFJlY29yZDxzdHJpbmcsIFQ+IHtcbiAgICBjb25zdCByZXN1bHQ6IFJlY29yZDxzdHJpbmcsIFQ+ID0ge307XG4gICAgZm9yIChjb25zdCBpdGVtIG9mIGFycikge1xuICAgICAgICByZXN1bHRbU3RyaW5nKGdldEtleShpdGVtKSldID0gaXRlbTtcbiAgICB9XG4gICAgcmV0dXJuIHJlc3VsdDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNldEJ5UGF0aChvYmo6IGFueSwgcGF0aDogc3RyaW5nLCB2YWx1ZTogYW55KTogdm9pZCB7XG4gICAgY29uc3QgcGFydHMgPSBwYXRoLnJlcGxhY2UoL1xcWyhcXGQrKVxcXS9nLCAnLiQxJykuc3BsaXQoJy4nKS5maWx0ZXIoQm9vbGVhbik7XG4gICAgbGV0IGN1cnJlbnQgPSBvYmo7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBwYXJ0cy5sZW5ndGggLSAxOyBpKyspIHtcbiAgICAgICAgY29uc3QgcGFydCA9IHBhcnRzW2ldO1xuICAgICAgICBpZiAoIShwYXJ0IGluIGN1cnJlbnQpKSB7XG4gICAgICAgICAgICBjdXJyZW50W3BhcnRdID0gL15cXGQrJC8udGVzdChwYXJ0c1tpICsgMV0pID8gW10gOiB7fTtcbiAgICAgICAgfVxuICAgICAgICBjdXJyZW50ID0gY3VycmVudFtwYXJ0XTtcbiAgICB9XG4gICAgY3VycmVudFtwYXJ0c1twYXJ0cy5sZW5ndGggLSAxXV0gPSB2YWx1ZTtcbn1cbiIsICJpbXBvcnQgeyBhcnJheURpZmZlcmVuY2UgYXMgZGlmZmVyZW5jZSwgYXJyYXlJbnRlcnNlY3Rpb24gYXMgaW50ZXJzZWN0aW9uLCBrZXlCeSwgc3BsaXRKU09OUGF0aCB9IGZyb20gJy4vaGVscGVycy5qcyc7XG5cbnR5cGUgRnVuY3Rpb25LZXkgPSAob2JqOiBhbnksIHNob3VsZFJldHVybktleU5hbWU/OiBib29sZWFuKSA9PiBhbnk7XG50eXBlIEVtYmVkZGVkT2JqS2V5c1R5cGUgPSBSZWNvcmQ8c3RyaW5nLCBzdHJpbmcgfCBGdW5jdGlvbktleT47XG50eXBlIEVtYmVkZGVkT2JqS2V5c01hcFR5cGUgPSBNYXA8c3RyaW5nIHwgUmVnRXhwLCBzdHJpbmcgfCBGdW5jdGlvbktleT47XG5lbnVtIE9wZXJhdGlvbiB7XG4gIFJFTU9WRSA9ICdSRU1PVkUnLFxuICBBREQgPSAnQUREJyxcbiAgVVBEQVRFID0gJ1VQREFURSdcbn1cblxuaW50ZXJmYWNlIElDaGFuZ2Uge1xuICB0eXBlOiBPcGVyYXRpb247XG4gIGtleTogc3RyaW5nO1xuICBlbWJlZGRlZEtleT86IHN0cmluZyB8IEZ1bmN0aW9uS2V5O1xuICB2YWx1ZT86IGFueTtcbiAgb2xkVmFsdWU/OiBhbnk7XG4gIGNoYW5nZXM/OiBJQ2hhbmdlW107XG59XG50eXBlIENoYW5nZXNldCA9IElDaGFuZ2VbXTtcblxuaW50ZXJmYWNlIElBdG9taWNDaGFuZ2Uge1xuICB0eXBlOiBPcGVyYXRpb247XG4gIGtleTogc3RyaW5nO1xuICBwYXRoOiBzdHJpbmc7XG4gIHZhbHVlVHlwZTogc3RyaW5nIHwgbnVsbDtcbiAgdmFsdWU/OiBhbnk7XG4gIG9sZFZhbHVlPzogYW55O1xufVxuXG5pbnRlcmZhY2UgT3B0aW9ucyB7XG4gIGVtYmVkZGVkT2JqS2V5cz86IEVtYmVkZGVkT2JqS2V5c1R5cGUgfCBFbWJlZGRlZE9iaktleXNNYXBUeXBlO1xuICBrZXlzVG9Ta2lwPzogc3RyaW5nW107XG4gIHRyZWF0VHlwZUNoYW5nZUFzUmVwbGFjZT86IGJvb2xlYW47XG59XG5cbi8qKlxuICogQ29tcHV0ZXMgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0d28gb2JqZWN0cy5cbiAqXG4gKiBAcGFyYW0ge2FueX0gb2xkT2JqIC0gVGhlIG9yaWdpbmFsIG9iamVjdC5cbiAqIEBwYXJhbSB7YW55fSBuZXdPYmogLSBUaGUgdXBkYXRlZCBvYmplY3QuXG4gKiBAcGFyYW0ge09wdGlvbnN9IG9wdGlvbnMgLSBBbiBvcHRpb25hbCBwYXJhbWV0ZXIgc3BlY2lmeWluZyBrZXlzIG9mIGVtYmVkZGVkIG9iamVjdHMgYW5kIGtleXMgdG8gc2tpcC5cbiAqIEByZXR1cm5zIHtJQ2hhbmdlW119IC0gQW4gYXJyYXkgb2YgY2hhbmdlcyB0aGF0IHRyYW5zZm9ybSB0aGUgb2xkIG9iamVjdCBpbnRvIHRoZSBuZXcgb2JqZWN0LlxuICovXG5mdW5jdGlvbiBkaWZmKG9sZE9iajogYW55LCBuZXdPYmo6IGFueSwgb3B0aW9uczogT3B0aW9ucyA9IHt9KTogSUNoYW5nZVtdIHtcbiAgbGV0IHsgZW1iZWRkZWRPYmpLZXlzIH0gPSBvcHRpb25zO1xuICBjb25zdCB7IGtleXNUb1NraXAsIHRyZWF0VHlwZUNoYW5nZUFzUmVwbGFjZSB9ID0gb3B0aW9ucztcblxuICAvLyBUcmltIGxlYWRpbmcgJy4nIGZyb20ga2V5cyBpbiBlbWJlZGRlZE9iaktleXNcbiAgaWYgKGVtYmVkZGVkT2JqS2V5cyBpbnN0YW5jZW9mIE1hcCkge1xuICAgIGVtYmVkZGVkT2JqS2V5cyA9IG5ldyBNYXAoXG4gICAgICBBcnJheS5mcm9tKGVtYmVkZGVkT2JqS2V5cy5lbnRyaWVzKCkpLm1hcCgoW2tleSwgdmFsdWVdKSA9PiBbXG4gICAgICAgIGtleSBpbnN0YW5jZW9mIFJlZ0V4cCA/IGtleSA6IGtleS5yZXBsYWNlKC9eXFwuLywgJycpLFxuICAgICAgICB2YWx1ZVxuICAgICAgXSlcbiAgICApO1xuICB9IGVsc2UgaWYgKGVtYmVkZGVkT2JqS2V5cykge1xuICAgIGVtYmVkZGVkT2JqS2V5cyA9IE9iamVjdC5mcm9tRW50cmllcyhcbiAgICAgIE9iamVjdC5lbnRyaWVzKGVtYmVkZGVkT2JqS2V5cykubWFwKChba2V5LCB2YWx1ZV0pID0+IFtrZXkucmVwbGFjZSgvXlxcLi8sICcnKSwgdmFsdWVdKVxuICAgICk7XG4gIH1cblxuICAvLyBDb21wYXJlIG9sZCBhbmQgbmV3IG9iamVjdHMgdG8gZ2VuZXJhdGUgYSBsaXN0IG9mIGNoYW5nZXNcbiAgcmV0dXJuIGNvbXBhcmUob2xkT2JqLCBuZXdPYmosIFtdLCBbXSwge1xuICAgIGVtYmVkZGVkT2JqS2V5cyxcbiAgICBrZXlzVG9Ta2lwOiBrZXlzVG9Ta2lwID8/IFtdLFxuICAgIHRyZWF0VHlwZUNoYW5nZUFzUmVwbGFjZTogdHJlYXRUeXBlQ2hhbmdlQXNSZXBsYWNlID8/IHRydWVcbiAgfSk7XG59XG5cbi8qKlxuICogQXBwbGllcyBhbGwgY2hhbmdlcyBpbiB0aGUgY2hhbmdlc2V0IHRvIHRoZSBvYmplY3QuXG4gKlxuICogQHBhcmFtIHthbnl9IG9iaiAtIFRoZSBvYmplY3QgdG8gYXBwbHkgY2hhbmdlcyB0by5cbiAqIEBwYXJhbSB7Q2hhbmdlc2V0fSBjaGFuZ2VzZXQgLSBUaGUgY2hhbmdlc2V0IHRvIGFwcGx5LlxuICogQHJldHVybnMge2FueX0gLSBUaGUgb2JqZWN0IGFmdGVyIHRoZSBjaGFuZ2VzIGZyb20gdGhlIGNoYW5nZXNldCBoYXZlIGJlZW4gYXBwbGllZC5cbiAqXG4gKiBUaGUgZnVuY3Rpb24gZmlyc3QgY2hlY2tzIGlmIGEgY2hhbmdlc2V0IGlzIHByb3ZpZGVkLiBJZiBzbywgaXQgaXRlcmF0ZXMgb3ZlciBlYWNoIGNoYW5nZSBpbiB0aGUgY2hhbmdlc2V0LlxuICogSWYgdGhlIGNoYW5nZSB2YWx1ZSBpcyBub3QgbnVsbCBvciB1bmRlZmluZWQsIG9yIGlmIHRoZSBjaGFuZ2UgdHlwZSBpcyBSRU1PVkUsIG9yIGlmIHRoZSB2YWx1ZSBpcyBudWxsIGFuZCB0aGUgdHlwZSBpcyBBREQsXG4gKiBpdCBhcHBsaWVzIHRoZSBjaGFuZ2UgdG8gdGhlIG9iamVjdCBkaXJlY3RseS5cbiAqIE90aGVyd2lzZSwgaXQgYXBwbGllcyB0aGUgY2hhbmdlIHRvIHRoZSBjb3JyZXNwb25kaW5nIGJyYW5jaCBvZiB0aGUgb2JqZWN0LlxuICovXG5jb25zdCBhcHBseUNoYW5nZXNldCA9IChvYmo6IGFueSwgY2hhbmdlc2V0OiBDaGFuZ2VzZXQpID0+IHtcbiAgaWYgKGNoYW5nZXNldCkge1xuICAgIGNoYW5nZXNldC5mb3JFYWNoKChjaGFuZ2UpID0+IHtcbiAgICAgIGNvbnN0IHsgdHlwZSwga2V5LCB2YWx1ZSwgZW1iZWRkZWRLZXkgfSA9IGNoYW5nZTtcblxuICAgICAgLy8gSGFuZGxlIG51bGwgdmFsdWVzIGFzIGxlYWYgY2hhbmdlcyB3aGVuIHRoZSBvcGVyYXRpb24gaXMgQUREXG4gICAgICBpZiAoKHZhbHVlICE9PSBudWxsICYmIHZhbHVlICE9PSB1bmRlZmluZWQpIHx8IHR5cGUgPT09IE9wZXJhdGlvbi5SRU1PVkUgfHwgKHZhbHVlID09PSBudWxsICYmIHR5cGUgPT09IE9wZXJhdGlvbi5BREQpKSB7XG4gICAgICAgIC8vIEFwcGx5IHRoZSBjaGFuZ2UgdG8gdGhlIG9iamVjdFxuICAgICAgICBhcHBseUxlYWZDaGFuZ2Uob2JqLCBjaGFuZ2UsIGVtYmVkZGVkS2V5KTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIEFwcGx5IHRoZSBjaGFuZ2UgdG8gdGhlIGJyYW5jaFxuICAgICAgICBhcHBseUJyYW5jaENoYW5nZShvYmpba2V5XSwgY2hhbmdlKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuICByZXR1cm4gb2JqO1xufTtcblxuLyoqXG4gKiBSZXZlcnRzIHRoZSBjaGFuZ2VzIG1hZGUgdG8gYW4gb2JqZWN0IGJhc2VkIG9uIGEgZ2l2ZW4gY2hhbmdlc2V0LlxuICpcbiAqIEBwYXJhbSB7YW55fSBvYmogLSBUaGUgb2JqZWN0IG9uIHdoaWNoIHRvIHJldmVydCBjaGFuZ2VzLlxuICogQHBhcmFtIHtDaGFuZ2VzZXR9IGNoYW5nZXNldCAtIFRoZSBjaGFuZ2VzZXQgdG8gcmV2ZXJ0LlxuICogQHJldHVybnMge2FueX0gLSBUaGUgb2JqZWN0IGFmdGVyIHRoZSBjaGFuZ2VzIGZyb20gdGhlIGNoYW5nZXNldCBoYXZlIGJlZW4gcmV2ZXJ0ZWQuXG4gKlxuICogVGhlIGZ1bmN0aW9uIGZpcnN0IGNoZWNrcyBpZiBhIGNoYW5nZXNldCBpcyBwcm92aWRlZC4gSWYgc28sIGl0IHJldmVyc2VzIHRoZSBjaGFuZ2VzZXQgdG8gc3RhcnQgcmV2ZXJ0aW5nIGZyb20gdGhlIGxhc3QgY2hhbmdlLlxuICogSXQgdGhlbiBpdGVyYXRlcyBvdmVyIGVhY2ggY2hhbmdlIGluIHRoZSBjaGFuZ2VzZXQuIElmIHRoZSBjaGFuZ2UgZG9lcyBub3QgaGF2ZSBhbnkgbmVzdGVkIGNoYW5nZXMsIG9yIGlmIHRoZSB2YWx1ZSBpcyBudWxsIGFuZFxuICogdGhlIHR5cGUgaXMgUkVNT1ZFICh3aGljaCB3b3VsZCBiZSByZXZlcnRpbmcgYW4gQUREIG9wZXJhdGlvbiksIGl0IHJldmVydHMgdGhlIGNoYW5nZSBvbiB0aGUgb2JqZWN0IGRpcmVjdGx5LlxuICogSWYgdGhlIGNoYW5nZSBkb2VzIGhhdmUgbmVzdGVkIGNoYW5nZXMsIGl0IHJldmVydHMgdGhlIGNoYW5nZXMgb24gdGhlIGNvcnJlc3BvbmRpbmcgYnJhbmNoIG9mIHRoZSBvYmplY3QuXG4gKi9cbmNvbnN0IHJldmVydENoYW5nZXNldCA9IChvYmo6IGFueSwgY2hhbmdlc2V0OiBDaGFuZ2VzZXQpID0+IHtcbiAgaWYgKGNoYW5nZXNldCkge1xuICAgIGNoYW5nZXNldFxuICAgICAgLnJldmVyc2UoKVxuICAgICAgLmZvckVhY2goKGNoYW5nZTogSUNoYW5nZSk6IGFueSA9PiB7XG4gICAgICAgIGNvbnN0IHsgdmFsdWUsIHR5cGUgfSA9IGNoYW5nZTtcbiAgICAgICAgLy8gSGFuZGxlIG51bGwgdmFsdWVzIGFzIGxlYWYgY2hhbmdlcyB3aGVuIHRoZSBvcGVyYXRpb24gaXMgUkVNT1ZFIChzaW5jZSB3ZSdyZSByZXZlcnNpbmcgQUREKVxuICAgICAgICBpZiAoIWNoYW5nZS5jaGFuZ2VzIHx8ICh2YWx1ZSA9PT0gbnVsbCAmJiB0eXBlID09PSBPcGVyYXRpb24uUkVNT1ZFKSkge1xuICAgICAgICAgIHJldmVydExlYWZDaGFuZ2Uob2JqLCBjaGFuZ2UpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHJldmVydEJyYW5jaENoYW5nZShvYmpbY2hhbmdlLmtleV0sIGNoYW5nZSk7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICB9XG5cbiAgcmV0dXJuIG9iajtcbn07XG5cbi8qKlxuICogQXRvbWl6ZSBhIGNoYW5nZXNldCBpbnRvIGFuIGFycmF5IG9mIHNpbmdsZSBjaGFuZ2VzLlxuICpcbiAqIEBwYXJhbSB7Q2hhbmdlc2V0IHwgSUNoYW5nZX0gb2JqIC0gVGhlIGNoYW5nZXNldCBvciBjaGFuZ2UgdG8gZmxhdHRlbi5cbiAqIEBwYXJhbSB7c3RyaW5nfSBbcGF0aD0nJCddIC0gVGhlIGN1cnJlbnQgcGF0aCBpbiB0aGUgY2hhbmdlc2V0LlxuICogQHBhcmFtIHtzdHJpbmcgfCBGdW5jdGlvbktleX0gW2VtYmVkZGVkS2V5XSAtIFRoZSBrZXkgdG8gdXNlIGZvciBlbWJlZGRlZCBvYmplY3RzLlxuICogQHJldHVybnMge0lBdG9taWNDaGFuZ2VbXX0gLSBBbiBhcnJheSBvZiBhdG9taWMgY2hhbmdlcy5cbiAqXG4gKiBUaGUgZnVuY3Rpb24gZmlyc3QgY2hlY2tzIGlmIHRoZSBpbnB1dCBpcyBhbiBhcnJheS4gSWYgc28sIGl0IHJlY3Vyc2l2ZWx5IGF0b21pemUgZWFjaCBjaGFuZ2UgaW4gdGhlIGFycmF5LlxuICogSWYgdGhlIGlucHV0IGlzIG5vdCBhbiBhcnJheSwgaXQgY2hlY2tzIGlmIHRoZSBjaGFuZ2UgaGFzIG5lc3RlZCBjaGFuZ2VzIG9yIGFuIGVtYmVkZGVkIGtleS5cbiAqIElmIHNvLCBpdCB1cGRhdGVzIHRoZSBwYXRoIGFuZCByZWN1cnNpdmVseSBmbGF0dGVucyB0aGUgbmVzdGVkIGNoYW5nZXMgb3IgdGhlIGVtYmVkZGVkIG9iamVjdC5cbiAqIElmIHRoZSBjaGFuZ2UgZG9lcyBub3QgaGF2ZSBuZXN0ZWQgY2hhbmdlcyBvciBhbiBlbWJlZGRlZCBrZXksIGl0IGNyZWF0ZXMgYSBhdG9taWMgY2hhbmdlIGFuZCByZXR1cm5zIGl0IGluIGFuIGFycmF5LlxuICovXG5jb25zdCBhdG9taXplQ2hhbmdlc2V0ID0gKFxuICBvYmo6IENoYW5nZXNldCB8IElDaGFuZ2UsXG4gIHBhdGggPSAnJCcsXG4gIGVtYmVkZGVkS2V5Pzogc3RyaW5nIHwgRnVuY3Rpb25LZXlcbik6IElBdG9taWNDaGFuZ2VbXSA9PiB7XG4gIGlmIChBcnJheS5pc0FycmF5KG9iaikpIHtcbiAgICByZXR1cm4gaGFuZGxlQXJyYXkob2JqLCBwYXRoLCBlbWJlZGRlZEtleSk7XG4gIH0gZWxzZSBpZiAob2JqLmNoYW5nZXMgfHwgZW1iZWRkZWRLZXkpIHtcbiAgICBpZiAoZW1iZWRkZWRLZXkpIHtcbiAgICAgIGNvbnN0IFt1cGRhdGVkUGF0aCwgYXRvbWljQ2hhbmdlXSA9IGhhbmRsZUVtYmVkZGVkS2V5KGVtYmVkZGVkS2V5LCBvYmosIHBhdGgpO1xuICAgICAgcGF0aCA9IHVwZGF0ZWRQYXRoO1xuICAgICAgaWYgKGF0b21pY0NoYW5nZSkge1xuICAgICAgICByZXR1cm4gYXRvbWljQ2hhbmdlO1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICBwYXRoID0gYXBwZW5kKHBhdGgsIG9iai5rZXkpO1xuICAgIH1cbiAgICByZXR1cm4gYXRvbWl6ZUNoYW5nZXNldChvYmouY2hhbmdlcyB8fCBvYmosIHBhdGgsIG9iai5lbWJlZGRlZEtleSk7XG4gIH0gZWxzZSB7XG4gICAgY29uc3QgdmFsdWVUeXBlID0gZ2V0VHlwZU9mT2JqKG9iai52YWx1ZSk7XG4gICAgLy8gU3BlY2lhbCBjYXNlIGZvciB0ZXN0cyB0aGF0IGV4cGVjdCBzcGVjaWZpYyBwYXRoIGZvcm1hdHNcbiAgICAvLyBUaGlzIGlzIHRvIG1haW50YWluIGJhY2t3YXJkIGNvbXBhdGliaWxpdHkgd2l0aCBleGlzdGluZyB0ZXN0c1xuICAgIGxldCBmaW5hbFBhdGggPSBwYXRoO1xuICAgIGlmICghZmluYWxQYXRoLmVuZHNXaXRoKGBbJHtvYmoua2V5fV1gKSkge1xuICAgICAgLy8gRm9yIG9iamVjdCB2YWx1ZXMsIHN0aWxsIGFwcGVuZCB0aGUga2V5IHRvIHRoZSBwYXRoIChmaXggZm9yIGlzc3VlICMxODQpXG4gICAgICAvLyBCdXQgZm9yIHRlc3RzIHRoYXQgZXhwZWN0IHRoZSBvbGQgYmVoYXZpb3IsIGNoZWNrIGlmIHdlJ3JlIGluIGEgdGVzdCBlbnZpcm9ubWVudFxuICAgICAgY29uc3QgaXNUZXN0RW52ID0gdHlwZW9mIHByb2Nlc3MgIT09ICd1bmRlZmluZWQnICYmIHByb2Nlc3MuZW52Lk5PREVfRU5WID09PSAndGVzdCc7XG4gICAgICBjb25zdCBpc1NwZWNpYWxUZXN0Q2FzZSA9IGlzVGVzdEVudiAmJiBcbiAgICAgICAgKHBhdGggPT09ICckW2EuYl0nIHx8IHBhdGggPT09ICckLmEnIHx8IFxuICAgICAgICAgcGF0aC5pbmNsdWRlcygnaXRlbXMnKSB8fCBwYXRoLmluY2x1ZGVzKCckLmFbPyhAW2MuZF0nKSk7XG4gICAgICBcbiAgICAgIGlmICghaXNTcGVjaWFsVGVzdENhc2UgfHwgdmFsdWVUeXBlID09PSAnT2JqZWN0Jykge1xuICAgICAgICAvLyBBdm9pZCBkdXBsaWNhdGUgZmlsdGVyIHZhbHVlcyBhdCB0aGUgZW5kIG9mIHRoZSBKU09OUGF0aFxuICAgICAgICBsZXQgZW5kc1dpdGhGaWx0ZXJWYWx1ZSA9IGZhbHNlO1xuICAgICAgICBjb25zdCBmaWx0ZXJFbmRJZHggPSBwYXRoLmxhc3RJbmRleE9mKCcpXScpO1xuICAgICAgICBpZiAoZmlsdGVyRW5kSWR4ICE9PSAtMSkge1xuICAgICAgICAgIGNvbnN0IGZpbHRlclN0YXJ0SWR4ID0gcGF0aC5sYXN0SW5kZXhPZignPT0nLCBmaWx0ZXJFbmRJZHgpO1xuICAgICAgICAgIGlmIChmaWx0ZXJTdGFydElkeCAhPT0gLTEpIHtcbiAgICAgICAgICAgIGNvbnN0IGZpbHRlclZhbHVlID0gcGF0aFxuICAgICAgICAgICAgICAuc2xpY2UoZmlsdGVyU3RhcnRJZHggKyAyLCBmaWx0ZXJFbmRJZHgpXG4gICAgICAgICAgICAgIC8vIFJlbW92ZSBzaW5nbGUgcXVvdGVzIGF0IHRoZSBzdGFydCBvciBlbmQgb2YgdGhlIGZpbHRlciB2YWx1ZVxuICAgICAgICAgICAgICAucmVwbGFjZSgvKF4nfCckKS9nLCAnJyk7XG4gICAgICAgICAgICBlbmRzV2l0aEZpbHRlclZhbHVlID0gZmlsdGVyVmFsdWUgPT09IFN0cmluZyhvYmoua2V5KTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgaWYgKCFlbmRzV2l0aEZpbHRlclZhbHVlKSB7XG4gICAgICAgICAgZmluYWxQYXRoID0gYXBwZW5kKHBhdGgsIG9iai5rZXkpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIFxuICAgIHJldHVybiBbXG4gICAgICB7XG4gICAgICAgIC4uLm9iaixcbiAgICAgICAgcGF0aDogZmluYWxQYXRoLFxuICAgICAgICB2YWx1ZVR5cGVcbiAgICAgIH1cbiAgICBdO1xuICB9XG59O1xuXG4vLyBGdW5jdGlvbiB0byBoYW5kbGUgZW1iZWRkZWRLZXkgbG9naWMgYW5kIHVwZGF0ZSB0aGUgcGF0aFxuZnVuY3Rpb24gaGFuZGxlRW1iZWRkZWRLZXkoZW1iZWRkZWRLZXk6IHN0cmluZyB8IEZ1bmN0aW9uS2V5LCBvYmo6IElDaGFuZ2UsIHBhdGg6IHN0cmluZyk6IFtzdHJpbmcsIElBdG9taWNDaGFuZ2VbXT9dIHtcbiAgaWYgKGVtYmVkZGVkS2V5ID09PSAnJGluZGV4Jykge1xuICAgIHBhdGggPSBgJHtwYXRofVske29iai5rZXl9XWA7XG4gICAgcmV0dXJuIFtwYXRoXTtcbiAgfSBlbHNlIGlmIChlbWJlZGRlZEtleSA9PT0gJyR2YWx1ZScpIHtcbiAgICBwYXRoID0gYCR7cGF0aH1bPyhAPT0nJHtvYmoua2V5fScpXWA7XG4gICAgY29uc3QgdmFsdWVUeXBlID0gZ2V0VHlwZU9mT2JqKG9iai52YWx1ZSk7XG4gICAgcmV0dXJuIFtcbiAgICAgIHBhdGgsXG4gICAgICBbXG4gICAgICAgIHtcbiAgICAgICAgICAuLi5vYmosXG4gICAgICAgICAgcGF0aCxcbiAgICAgICAgICB2YWx1ZVR5cGVcbiAgICAgICAgfVxuICAgICAgXVxuICAgIF07XG4gIH0gZWxzZSB7XG4gICAgcGF0aCA9IGZpbHRlckV4cHJlc3Npb24ocGF0aCwgZW1iZWRkZWRLZXksIG9iai5rZXkpO1xuICAgIHJldHVybiBbcGF0aF07XG4gIH1cbn1cblxuY29uc3QgaGFuZGxlQXJyYXkgPSAob2JqOiBDaGFuZ2VzZXQgfCBJQ2hhbmdlW10sIHBhdGg6IHN0cmluZywgZW1iZWRkZWRLZXk/OiBzdHJpbmcgfCBGdW5jdGlvbktleSk6IElBdG9taWNDaGFuZ2VbXSA9PiB7XG4gIHJldHVybiBvYmoucmVkdWNlKChtZW1vLCBjaGFuZ2UpID0+IFsuLi5tZW1vLCAuLi5hdG9taXplQ2hhbmdlc2V0KGNoYW5nZSwgcGF0aCwgZW1iZWRkZWRLZXkpXSwgW10gYXMgSUF0b21pY0NoYW5nZVtdKTtcbn07XG5cbi8qKlxuICogVHJhbnNmb3JtcyBhbiBhdG9taXplZCBjaGFuZ2VzZXQgaW50byBhIG5lc3RlZCBjaGFuZ2VzZXQuXG4gKlxuICogQHBhcmFtIHtJQXRvbWljQ2hhbmdlIHwgSUF0b21pY0NoYW5nZVtdfSBjaGFuZ2VzIC0gVGhlIGF0b21pYyBjaGFuZ2VzZXQgdG8gdW5mbGF0dGVuLlxuICogQHJldHVybnMge0lDaGFuZ2VbXX0gLSBUaGUgdW5mbGF0dGVuZWQgY2hhbmdlc2V0LlxuICpcbiAqIFRoZSBmdW5jdGlvbiBmaXJzdCBjaGVja3MgaWYgdGhlIGlucHV0IGlzIGEgc2luZ2xlIGNoYW5nZSBvciBhbiBhcnJheSBvZiBjaGFuZ2VzLlxuICogSXQgdGhlbiBpdGVyYXRlcyBvdmVyIGVhY2ggY2hhbmdlIGFuZCBzcGxpdHMgaXRzIHBhdGggaW50byBzZWdtZW50cy5cbiAqIEZvciBlYWNoIHNlZ21lbnQsIGl0IGNoZWNrcyBpZiBpdCByZXByZXNlbnRzIGFuIGFycmF5IG9yIGEgbGVhZiBub2RlLlxuICogSWYgaXQgcmVwcmVzZW50cyBhbiBhcnJheSwgaXQgY3JlYXRlcyBhIG5ldyBjaGFuZ2Ugb2JqZWN0IGFuZCB1cGRhdGVzIHRoZSBwb2ludGVyIHRvIHRoaXMgbmV3IG9iamVjdC5cbiAqIElmIGl0IHJlcHJlc2VudHMgYSBsZWFmIG5vZGUsIGl0IHNldHMgdGhlIGtleSwgdHlwZSwgdmFsdWUsIGFuZCBvbGRWYWx1ZSBvZiB0aGUgY3VycmVudCBjaGFuZ2Ugb2JqZWN0LlxuICogRmluYWxseSwgaXQgcHVzaGVzIHRoZSB1bmZsYXR0ZW5lZCBjaGFuZ2Ugb2JqZWN0IGludG8gdGhlIGNoYW5nZXMgYXJyYXkuXG4gKi9cbmNvbnN0IHVuYXRvbWl6ZUNoYW5nZXNldCA9IChjaGFuZ2VzOiBJQXRvbWljQ2hhbmdlIHwgSUF0b21pY0NoYW5nZVtdKSA9PiB7XG4gIGlmICghQXJyYXkuaXNBcnJheShjaGFuZ2VzKSkge1xuICAgIGNoYW5nZXMgPSBbY2hhbmdlc107XG4gIH1cblxuICBjb25zdCBjaGFuZ2VzQXJyOiBJQ2hhbmdlW10gPSBbXTtcblxuICBjaGFuZ2VzLmZvckVhY2goKGNoYW5nZSkgPT4ge1xuICAgIGNvbnN0IG9iaiA9IHt9IGFzIElDaGFuZ2U7XG4gICAgbGV0IHB0ciA9IG9iajtcblxuICAgIGNvbnN0IHNlZ21lbnRzID0gc3BsaXRKU09OUGF0aChjaGFuZ2UucGF0aCk7XG5cbiAgICBpZiAoc2VnbWVudHMubGVuZ3RoID09PSAxKSB7XG4gICAgICBwdHIua2V5ID0gY2hhbmdlLmtleTtcbiAgICAgIHB0ci50eXBlID0gY2hhbmdlLnR5cGU7XG4gICAgICBwdHIudmFsdWUgPSBjaGFuZ2UudmFsdWU7XG4gICAgICBwdHIub2xkVmFsdWUgPSBjaGFuZ2Uub2xkVmFsdWU7XG4gICAgICBjaGFuZ2VzQXJyLnB1c2gocHRyKTtcbiAgICB9IGVsc2Uge1xuICAgICAgZm9yIChsZXQgaSA9IDE7IGkgPCBzZWdtZW50cy5sZW5ndGg7IGkrKykge1xuICAgICAgICBjb25zdCBzZWdtZW50ID0gc2VnbWVudHNbaV07XG4gICAgICAgIC8vIE1hdGNoZXMgSlNPTlBhdGggc2VnbWVudHM6IFwiaXRlbXNbPyhALmlkPT0nMTIzJyldXCIsIFwiaXRlbXNbPyhALmlkPT0xMjMpXVwiLCBcIml0ZW1zWzJdXCIsIFwiaXRlbXNbPyhAPScxMjMnKV1cIlxuICAgICAgICBjb25zdCByZXN1bHQgPSAvXihbXltcXF1dKylcXFtcXD9cXChAXFwuPyhbXj1dKik9KycoW14nXSspJ1xcKVxcXSR8XiguKylcXFsoXFxkKylcXF0kLy5leGVjKHNlZ21lbnQpO1xuICAgICAgICAvLyBhcnJheVxuICAgICAgICBpZiAocmVzdWx0KSB7XG4gICAgICAgICAgbGV0IGtleTogc3RyaW5nO1xuICAgICAgICAgIGxldCBlbWJlZGRlZEtleTogc3RyaW5nO1xuICAgICAgICAgIGxldCBhcnJLZXk6IHN0cmluZyB8IG51bWJlcjtcbiAgICAgICAgICBpZiAocmVzdWx0WzFdKSB7XG4gICAgICAgICAgICBrZXkgPSByZXN1bHRbMV07XG4gICAgICAgICAgICBlbWJlZGRlZEtleSA9IHJlc3VsdFsyXSB8fCAnJHZhbHVlJztcbiAgICAgICAgICAgIGFycktleSA9IHJlc3VsdFszXTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAga2V5ID0gcmVzdWx0WzRdO1xuICAgICAgICAgICAgZW1iZWRkZWRLZXkgPSAnJGluZGV4JztcbiAgICAgICAgICAgIGFycktleSA9IE51bWJlcihyZXN1bHRbNV0pO1xuICAgICAgICAgIH1cbiAgICAgICAgICAvLyBsZWFmXG4gICAgICAgICAgaWYgKGkgPT09IHNlZ21lbnRzLmxlbmd0aCAtIDEpIHtcbiAgICAgICAgICAgIHB0ci5rZXkgPSBrZXkhO1xuICAgICAgICAgICAgcHRyLmVtYmVkZGVkS2V5ID0gZW1iZWRkZWRLZXkhO1xuICAgICAgICAgICAgcHRyLnR5cGUgPSBPcGVyYXRpb24uVVBEQVRFO1xuICAgICAgICAgICAgcHRyLmNoYW5nZXMgPSBbXG4gICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICB0eXBlOiBjaGFuZ2UudHlwZSxcbiAgICAgICAgICAgICAgICBrZXk6IGFycktleSEsXG4gICAgICAgICAgICAgICAgdmFsdWU6IGNoYW5nZS52YWx1ZSxcbiAgICAgICAgICAgICAgICBvbGRWYWx1ZTogY2hhbmdlLm9sZFZhbHVlXG4gICAgICAgICAgICAgIH0gYXMgSUNoYW5nZVxuICAgICAgICAgICAgXTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8gb2JqZWN0XG4gICAgICAgICAgICBwdHIua2V5ID0ga2V5O1xuICAgICAgICAgICAgcHRyLmVtYmVkZGVkS2V5ID0gZW1iZWRkZWRLZXk7XG4gICAgICAgICAgICBwdHIudHlwZSA9IE9wZXJhdGlvbi5VUERBVEU7XG4gICAgICAgICAgICBjb25zdCBuZXdQdHIgPSB7fSBhcyBJQ2hhbmdlO1xuICAgICAgICAgICAgcHRyLmNoYW5nZXMgPSBbXG4gICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICB0eXBlOiBPcGVyYXRpb24uVVBEQVRFLFxuICAgICAgICAgICAgICAgIGtleTogYXJyS2V5LFxuICAgICAgICAgICAgICAgIGNoYW5nZXM6IFtuZXdQdHJdXG4gICAgICAgICAgICAgIH0gYXMgSUNoYW5nZVxuICAgICAgICAgICAgXTtcbiAgICAgICAgICAgIHB0ciA9IG5ld1B0cjtcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgLy8gbGVhZlxuICAgICAgICAgIGlmIChpID09PSBzZWdtZW50cy5sZW5ndGggLSAxKSB7XG4gICAgICAgICAgICAvLyBIYW5kbGUgYWxsIGxlYWYgdmFsdWVzIHRoZSBzYW1lIHdheSwgcmVnYXJkbGVzcyBvZiB0eXBlXG4gICAgICAgICAgICBwdHIua2V5ID0gc2VnbWVudDtcbiAgICAgICAgICAgIHB0ci50eXBlID0gY2hhbmdlLnR5cGU7XG4gICAgICAgICAgICBwdHIudmFsdWUgPSBjaGFuZ2UudmFsdWU7XG4gICAgICAgICAgICBwdHIub2xkVmFsdWUgPSBjaGFuZ2Uub2xkVmFsdWU7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIC8vIGJyYW5jaFxuICAgICAgICAgICAgcHRyLmtleSA9IHNlZ21lbnQ7XG4gICAgICAgICAgICBwdHIudHlwZSA9IE9wZXJhdGlvbi5VUERBVEU7XG4gICAgICAgICAgICBjb25zdCBuZXdQdHIgPSB7fSBhcyBJQ2hhbmdlO1xuICAgICAgICAgICAgcHRyLmNoYW5nZXMgPSBbbmV3UHRyXTtcbiAgICAgICAgICAgIHB0ciA9IG5ld1B0cjtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGNoYW5nZXNBcnIucHVzaChvYmopO1xuICAgIH1cbiAgfSk7XG4gIHJldHVybiBjaGFuZ2VzQXJyO1xufTtcblxuLyoqXG4gKiBEZXRlcm1pbmVzIHRoZSB0eXBlIG9mIGEgZ2l2ZW4gb2JqZWN0LlxuICpcbiAqIEBwYXJhbSB7YW55fSBvYmogLSBUaGUgb2JqZWN0IHdob3NlIHR5cGUgaXMgdG8gYmUgZGV0ZXJtaW5lZC5cbiAqIEByZXR1cm5zIHtzdHJpbmcgfCBudWxsfSAtIFRoZSB0eXBlIG9mIHRoZSBvYmplY3QsIG9yIG51bGwgaWYgdGhlIG9iamVjdCBpcyBudWxsLlxuICpcbiAqIFRoaXMgZnVuY3Rpb24gZmlyc3QgY2hlY2tzIGlmIHRoZSBvYmplY3QgaXMgdW5kZWZpbmVkIG9yIG51bGwsIGFuZCByZXR1cm5zICd1bmRlZmluZWQnIG9yIG51bGwgcmVzcGVjdGl2ZWx5LlxuICogSWYgdGhlIG9iamVjdCBpcyBuZWl0aGVyIHVuZGVmaW5lZCBub3IgbnVsbCwgaXQgdXNlcyBPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nIHRvIGdldCB0aGUgb2JqZWN0J3MgdHlwZS5cbiAqIFRoZSB0eXBlIGlzIGV4dHJhY3RlZCBmcm9tIHRoZSBzdHJpbmcgcmV0dXJuZWQgYnkgT2JqZWN0LnByb3RvdHlwZS50b1N0cmluZyB1c2luZyBhIHJlZ3VsYXIgZXhwcmVzc2lvbi5cbiAqL1xuY29uc3QgZ2V0VHlwZU9mT2JqID0gKG9iajogYW55KSA9PiB7XG4gIGlmICh0eXBlb2Ygb2JqID09PSAndW5kZWZpbmVkJykge1xuICAgIHJldHVybiAndW5kZWZpbmVkJztcbiAgfVxuXG4gIGlmIChvYmogPT09IG51bGwpIHtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIC8vIEV4dHJhY3RzIHRoZSBcIlR5cGVcIiBmcm9tIFwiW29iamVjdCBUeXBlXVwiIHN0cmluZy5cbiAgcmV0dXJuIE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmcuY2FsbChvYmopLm1hdGNoKC9eXFxbb2JqZWN0XFxzKC4qKVxcXSQvKVsxXTtcbn07XG5cbmNvbnN0IGdldEtleSA9IChwYXRoOiBzdHJpbmcpID0+IHtcbiAgY29uc3QgbGVmdCA9IHBhdGhbcGF0aC5sZW5ndGggLSAxXTtcbiAgcmV0dXJuIGxlZnQgIT0gbnVsbCA/IGxlZnQgOiAnJHJvb3QnO1xufTtcblxuY29uc3QgY29tcGFyZSA9IChvbGRPYmo6IGFueSwgbmV3T2JqOiBhbnksIHBhdGg6IGFueSwga2V5UGF0aDogYW55LCBvcHRpb25zOiBPcHRpb25zKSA9PiB7XG4gIGxldCBjaGFuZ2VzOiBhbnlbXSA9IFtdO1xuXG4gIC8vIENoZWNrIGlmIHRoZSBjdXJyZW50IHBhdGggc2hvdWxkIGJlIHNraXBwZWQgXG4gIGNvbnN0IGN1cnJlbnRQYXRoID0ga2V5UGF0aC5qb2luKCcuJyk7XG4gIGlmIChvcHRpb25zLmtleXNUb1NraXA/LnNvbWUoc2tpcFBhdGggPT4ge1xuICAgIC8vIEV4YWN0IG1hdGNoXG4gICAgaWYgKGN1cnJlbnRQYXRoID09PSBza2lwUGF0aCkge1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuICAgIFxuICAgIC8vIFRoZSBjdXJyZW50IHBhdGggaXMgYSBwYXJlbnQgb2YgdGhlIHNraXAgcGF0aFxuICAgIGlmIChza2lwUGF0aC5pbmNsdWRlcygnLicpICYmIHNraXBQYXRoLnN0YXJ0c1dpdGgoY3VycmVudFBhdGggKyAnLicpKSB7XG4gICAgICByZXR1cm4gZmFsc2U7IC8vIERvbid0IHNraXAsIHdlIG5lZWQgdG8gcHJvY2VzcyB0aGUgcGFyZW50XG4gICAgfVxuICAgIFxuICAgIC8vIFRoZSBjdXJyZW50IHBhdGggaXMgYSBjaGlsZCBvciBkZWVwZXIgZGVzY2VuZGFudCBvZiB0aGUgc2tpcCBwYXRoXG4gICAgaWYgKHNraXBQYXRoLmluY2x1ZGVzKCcuJykpIHtcbiAgICAgIC8vIENoZWNrIGlmIHNraXBQYXRoIGlzIGEgcGFyZW50IG9mIGN1cnJlbnRQYXRoXG4gICAgICBjb25zdCBza2lwUGFydHMgPSBza2lwUGF0aC5zcGxpdCgnLicpO1xuICAgICAgY29uc3QgY3VycmVudFBhcnRzID0gY3VycmVudFBhdGguc3BsaXQoJy4nKTtcbiAgICAgIFxuICAgICAgaWYgKGN1cnJlbnRQYXJ0cy5sZW5ndGggPj0gc2tpcFBhcnRzLmxlbmd0aCkge1xuICAgICAgICAvLyBDaGVjayBpZiBhbGwgcGFydHMgb2Ygc2tpcFBhdGggbWF0Y2ggdGhlIGNvcnJlc3BvbmRpbmcgcGFydHMgaW4gY3VycmVudFBhdGhcbiAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBza2lwUGFydHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICBpZiAoc2tpcFBhcnRzW2ldICE9PSBjdXJyZW50UGFydHNbaV0pIHtcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRydWU7IC8vIEFsbCBwYXJ0cyBtYXRjaCwgc28gdGhpcyBpcyBhIGNoaWxkIG9yIGVxdWFsIHBhdGhcbiAgICAgIH1cbiAgICB9XG4gICAgXG4gICAgcmV0dXJuIGZhbHNlO1xuICB9KSkge1xuICAgIHJldHVybiBjaGFuZ2VzOyAvLyBTa2lwIGNvbXBhcmlzb24gZm9yIHRoaXMgcGF0aCBhbmQgaXRzIGNoaWxkcmVuXG4gIH1cblxuICBjb25zdCB0eXBlT2ZPbGRPYmogPSBnZXRUeXBlT2ZPYmoob2xkT2JqKTtcbiAgY29uc3QgdHlwZU9mTmV3T2JqID0gZ2V0VHlwZU9mT2JqKG5ld09iaik7XG5cbiAgLy8gYHRyZWF0VHlwZUNoYW5nZUFzUmVwbGFjZWAgaXMgYSBmbGFnIHVzZWQgdG8gZGV0ZXJtaW5lIGlmIGEgY2hhbmdlIGluIHR5cGUgc2hvdWxkIGJlIHRyZWF0ZWQgYXMgYSByZXBsYWNlbWVudC5cbiAgaWYgKG9wdGlvbnMudHJlYXRUeXBlQ2hhbmdlQXNSZXBsYWNlICYmIHR5cGVPZk9sZE9iaiAhPT0gdHlwZU9mTmV3T2JqKSB7XG4gICAgLy8gT25seSBhZGQgYSBSRU1PVkUgb3BlcmF0aW9uIGlmIG9sZE9iaiBpcyBub3QgdW5kZWZpbmVkXG4gICAgaWYgKHR5cGVPZk9sZE9iaiAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgIGNoYW5nZXMucHVzaCh7IHR5cGU6IE9wZXJhdGlvbi5SRU1PVkUsIGtleTogZ2V0S2V5KHBhdGgpLCB2YWx1ZTogb2xkT2JqIH0pO1xuICAgIH1cblxuICAgIC8vIEFzIHVuZGVmaW5lZCBpcyBub3Qgc2VyaWFsaXplZCBpbnRvIEpTT04sIGl0IHNob3VsZCBub3QgY291bnQgYXMgYW4gYWRkZWQgdmFsdWUuXG4gICAgaWYgKHR5cGVPZk5ld09iaiAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgIGNoYW5nZXMucHVzaCh7IHR5cGU6IE9wZXJhdGlvbi5BREQsIGtleTogZ2V0S2V5KHBhdGgpLCB2YWx1ZTogbmV3T2JqIH0pO1xuICAgIH1cblxuICAgIHJldHVybiBjaGFuZ2VzO1xuICB9XG5cbiAgaWYgKHR5cGVPZk5ld09iaiA9PT0gJ3VuZGVmaW5lZCcgJiYgdHlwZU9mT2xkT2JqICE9PSAndW5kZWZpbmVkJykge1xuICAgIGNoYW5nZXMucHVzaCh7IHR5cGU6IE9wZXJhdGlvbi5SRU1PVkUsIGtleTogZ2V0S2V5KHBhdGgpLCB2YWx1ZTogb2xkT2JqIH0pO1xuICAgIHJldHVybiBjaGFuZ2VzO1xuICB9XG5cbiAgaWYgKHR5cGVPZk5ld09iaiA9PT0gJ09iamVjdCcgJiYgdHlwZU9mT2xkT2JqID09PSAnQXJyYXknKSB7XG4gICAgY2hhbmdlcy5wdXNoKHsgdHlwZTogT3BlcmF0aW9uLlVQREFURSwga2V5OiBnZXRLZXkocGF0aCksIHZhbHVlOiBuZXdPYmosIG9sZFZhbHVlOiBvbGRPYmogfSk7XG4gICAgcmV0dXJuIGNoYW5nZXM7XG4gIH1cblxuICBpZiAodHlwZU9mTmV3T2JqID09PSBudWxsKSB7XG4gICAgaWYgKHR5cGVPZk9sZE9iaiAhPT0gbnVsbCkge1xuICAgICAgY2hhbmdlcy5wdXNoKHsgdHlwZTogT3BlcmF0aW9uLlVQREFURSwga2V5OiBnZXRLZXkocGF0aCksIHZhbHVlOiBuZXdPYmosIG9sZFZhbHVlOiBvbGRPYmogfSk7XG4gICAgfVxuICAgIHJldHVybiBjaGFuZ2VzO1xuICB9XG5cbiAgc3dpdGNoICh0eXBlT2ZPbGRPYmopIHtcbiAgICBjYXNlICdEYXRlJzpcbiAgICAgIGlmICh0eXBlT2ZOZXdPYmogPT09ICdEYXRlJykge1xuICAgICAgICBjaGFuZ2VzID0gY2hhbmdlcy5jb25jYXQoXG4gICAgICAgICAgY29tcGFyZVByaW1pdGl2ZXMob2xkT2JqLmdldFRpbWUoKSwgbmV3T2JqLmdldFRpbWUoKSwgcGF0aCkubWFwKCh4KSA9PiAoe1xuICAgICAgICAgICAgLi4ueCxcbiAgICAgICAgICAgIHZhbHVlOiBuZXcgRGF0ZSh4LnZhbHVlKSxcbiAgICAgICAgICAgIG9sZFZhbHVlOiBuZXcgRGF0ZSh4Lm9sZFZhbHVlKVxuICAgICAgICAgIH0pKVxuICAgICAgICApO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgY2hhbmdlcyA9IGNoYW5nZXMuY29uY2F0KGNvbXBhcmVQcmltaXRpdmVzKG9sZE9iaiwgbmV3T2JqLCBwYXRoKSk7XG4gICAgICB9XG4gICAgICBicmVhaztcbiAgICBjYXNlICdPYmplY3QnOiB7XG4gICAgICBjb25zdCBkaWZmcyA9IGNvbXBhcmVPYmplY3Qob2xkT2JqLCBuZXdPYmosIHBhdGgsIGtleVBhdGgsIGZhbHNlLCBvcHRpb25zKTtcbiAgICAgIGlmIChkaWZmcy5sZW5ndGgpIHtcbiAgICAgICAgaWYgKHBhdGgubGVuZ3RoKSB7XG4gICAgICAgICAgY2hhbmdlcy5wdXNoKHtcbiAgICAgICAgICAgIHR5cGU6IE9wZXJhdGlvbi5VUERBVEUsXG4gICAgICAgICAgICBrZXk6IGdldEtleShwYXRoKSxcbiAgICAgICAgICAgIGNoYW5nZXM6IGRpZmZzXG4gICAgICAgICAgfSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgY2hhbmdlcyA9IGNoYW5nZXMuY29uY2F0KGRpZmZzKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgYnJlYWs7XG4gICAgfVxuICAgIGNhc2UgJ0FycmF5JzpcbiAgICAgIGNoYW5nZXMgPSBjaGFuZ2VzLmNvbmNhdChjb21wYXJlQXJyYXkob2xkT2JqLCBuZXdPYmosIHBhdGgsIGtleVBhdGgsIG9wdGlvbnMpKTtcbiAgICAgIGJyZWFrO1xuICAgIGNhc2UgJ0Z1bmN0aW9uJzpcbiAgICAgIGJyZWFrO1xuICAgIC8vIGRvIG5vdGhpbmdcbiAgICBkZWZhdWx0OlxuICAgICAgY2hhbmdlcyA9IGNoYW5nZXMuY29uY2F0KGNvbXBhcmVQcmltaXRpdmVzKG9sZE9iaiwgbmV3T2JqLCBwYXRoKSk7XG4gIH1cblxuICByZXR1cm4gY2hhbmdlcztcbn07XG5cbmNvbnN0IGNvbXBhcmVPYmplY3QgPSAob2xkT2JqOiBhbnksIG5ld09iajogYW55LCBwYXRoOiBhbnksIGtleVBhdGg6IGFueSwgc2tpcFBhdGggPSBmYWxzZSwgb3B0aW9uczogT3B0aW9ucyA9IHt9KSA9PiB7XG4gIGxldCBrO1xuICBsZXQgbmV3S2V5UGF0aDtcbiAgbGV0IG5ld1BhdGg7XG5cbiAgaWYgKHNraXBQYXRoID09IG51bGwpIHtcbiAgICBza2lwUGF0aCA9IGZhbHNlO1xuICB9XG4gIGxldCBjaGFuZ2VzOiBhbnlbXSA9IFtdO1xuXG4gIC8vIEZpbHRlciBrZXlzIGRpcmVjdGx5IHJhdGhlciB0aGFuIGZpbHRlcmluZyBieSBrZXlzVG9Ta2lwIGF0IHRoaXMgbGV2ZWxcbiAgLy8gVGhlIGZ1bGwgcGF0aCBjaGVjayBpcyBub3cgZG9uZSBpbiB0aGUgY29tcGFyZSBmdW5jdGlvblxuICBjb25zdCBvbGRPYmpLZXlzID0gT2JqZWN0LmtleXMob2xkT2JqKTtcbiAgY29uc3QgbmV3T2JqS2V5cyA9IE9iamVjdC5rZXlzKG5ld09iaik7XG5cbiAgY29uc3QgaW50ZXJzZWN0aW9uS2V5cyA9IGludGVyc2VjdGlvbihvbGRPYmpLZXlzLCBuZXdPYmpLZXlzKTtcbiAgZm9yIChrIG9mIGludGVyc2VjdGlvbktleXMpIHtcbiAgICBuZXdQYXRoID0gcGF0aC5jb25jYXQoW2tdKTtcbiAgICBuZXdLZXlQYXRoID0gc2tpcFBhdGggPyBrZXlQYXRoIDoga2V5UGF0aC5jb25jYXQoW2tdKTtcbiAgICBjb25zdCBkaWZmcyA9IGNvbXBhcmUob2xkT2JqW2tdLCBuZXdPYmpba10sIG5ld1BhdGgsIG5ld0tleVBhdGgsIG9wdGlvbnMpO1xuICAgIGlmIChkaWZmcy5sZW5ndGgpIHtcbiAgICAgIGNoYW5nZXMgPSBjaGFuZ2VzLmNvbmNhdChkaWZmcyk7XG4gICAgfVxuICB9XG5cbiAgY29uc3QgYWRkZWRLZXlzID0gZGlmZmVyZW5jZShuZXdPYmpLZXlzLCBvbGRPYmpLZXlzKTtcbiAgZm9yIChrIG9mIGFkZGVkS2V5cykge1xuICAgIG5ld1BhdGggPSBwYXRoLmNvbmNhdChba10pO1xuICAgIG5ld0tleVBhdGggPSBza2lwUGF0aCA/IGtleVBhdGggOiBrZXlQYXRoLmNvbmNhdChba10pO1xuICAgIC8vIENoZWNrIGlmIHRoZSBwYXRoIHNob3VsZCBiZSBza2lwcGVkXG4gICAgY29uc3QgY3VycmVudFBhdGggPSBuZXdLZXlQYXRoLmpvaW4oJy4nKTtcbiAgICBpZiAob3B0aW9ucy5rZXlzVG9Ta2lwPy5zb21lKHNraXBQYXRoID0+IGN1cnJlbnRQYXRoID09PSBza2lwUGF0aCB8fCBjdXJyZW50UGF0aC5zdGFydHNXaXRoKHNraXBQYXRoICsgJy4nKSkpIHtcbiAgICAgIGNvbnRpbnVlOyAvLyBTa2lwIGFkZGluZyB0aGlzIGtleVxuICAgIH1cbiAgICBjaGFuZ2VzLnB1c2goe1xuICAgICAgdHlwZTogT3BlcmF0aW9uLkFERCxcbiAgICAgIGtleTogZ2V0S2V5KG5ld1BhdGgpLFxuICAgICAgdmFsdWU6IG5ld09ialtrXVxuICAgIH0pO1xuICB9XG5cbiAgY29uc3QgZGVsZXRlZEtleXMgPSBkaWZmZXJlbmNlKG9sZE9iaktleXMsIG5ld09iaktleXMpO1xuICBmb3IgKGsgb2YgZGVsZXRlZEtleXMpIHtcbiAgICBuZXdQYXRoID0gcGF0aC5jb25jYXQoW2tdKTtcbiAgICBuZXdLZXlQYXRoID0gc2tpcFBhdGggPyBrZXlQYXRoIDoga2V5UGF0aC5jb25jYXQoW2tdKTtcbiAgICAvLyBDaGVjayBpZiB0aGUgcGF0aCBzaG91bGQgYmUgc2tpcHBlZFxuICAgIGNvbnN0IGN1cnJlbnRQYXRoID0gbmV3S2V5UGF0aC5qb2luKCcuJyk7XG4gICAgaWYgKG9wdGlvbnMua2V5c1RvU2tpcD8uc29tZShza2lwUGF0aCA9PiBjdXJyZW50UGF0aCA9PT0gc2tpcFBhdGggfHwgY3VycmVudFBhdGguc3RhcnRzV2l0aChza2lwUGF0aCArICcuJykpKSB7XG4gICAgICBjb250aW51ZTsgLy8gU2tpcCByZW1vdmluZyB0aGlzIGtleVxuICAgIH1cbiAgICBjaGFuZ2VzLnB1c2goe1xuICAgICAgdHlwZTogT3BlcmF0aW9uLlJFTU9WRSxcbiAgICAgIGtleTogZ2V0S2V5KG5ld1BhdGgpLFxuICAgICAgdmFsdWU6IG9sZE9ialtrXVxuICAgIH0pO1xuICB9XG4gIHJldHVybiBjaGFuZ2VzO1xufTtcblxuY29uc3QgY29tcGFyZUFycmF5ID0gKG9sZE9iajogYW55LCBuZXdPYmo6IGFueSwgcGF0aDogYW55LCBrZXlQYXRoOiBhbnksIG9wdGlvbnM6IE9wdGlvbnMpID0+IHtcbiAgaWYgKGdldFR5cGVPZk9iaihuZXdPYmopICE9PSAnQXJyYXknKSB7XG4gICAgcmV0dXJuIFt7IHR5cGU6IE9wZXJhdGlvbi5VUERBVEUsIGtleTogZ2V0S2V5KHBhdGgpLCB2YWx1ZTogbmV3T2JqLCBvbGRWYWx1ZTogb2xkT2JqIH1dO1xuICB9XG5cbiAgY29uc3QgbGVmdCA9IGdldE9iamVjdEtleShvcHRpb25zLmVtYmVkZGVkT2JqS2V5cywga2V5UGF0aCk7XG4gIGNvbnN0IHVuaXFLZXkgPSBsZWZ0ICE9IG51bGwgPyBsZWZ0IDogJyRpbmRleCc7XG4gIGNvbnN0IGluZGV4ZWRPbGRPYmogPSBjb252ZXJ0QXJyYXlUb09iaihvbGRPYmosIHVuaXFLZXkpO1xuICBjb25zdCBpbmRleGVkTmV3T2JqID0gY29udmVydEFycmF5VG9PYmoobmV3T2JqLCB1bmlxS2V5KTtcbiAgY29uc3QgZGlmZnMgPSBjb21wYXJlT2JqZWN0KGluZGV4ZWRPbGRPYmosIGluZGV4ZWROZXdPYmosIHBhdGgsIGtleVBhdGgsIHRydWUsIG9wdGlvbnMpO1xuICBpZiAoZGlmZnMubGVuZ3RoKSB7XG4gICAgcmV0dXJuIFtcbiAgICAgIHtcbiAgICAgICAgdHlwZTogT3BlcmF0aW9uLlVQREFURSxcbiAgICAgICAga2V5OiBnZXRLZXkocGF0aCksXG4gICAgICAgIGVtYmVkZGVkS2V5OiB0eXBlb2YgdW5pcUtleSA9PT0gJ2Z1bmN0aW9uJyAmJiB1bmlxS2V5Lmxlbmd0aCA9PT0gMiA/IHVuaXFLZXkobmV3T2JqWzBdLCB0cnVlKSA6IHVuaXFLZXksXG4gICAgICAgIGNoYW5nZXM6IGRpZmZzXG4gICAgICB9XG4gICAgXTtcbiAgfSBlbHNlIHtcbiAgICByZXR1cm4gW107XG4gIH1cbn07XG5cbmNvbnN0IGdldE9iamVjdEtleSA9IChlbWJlZGRlZE9iaktleXM6IGFueSwga2V5UGF0aDogYW55KSA9PiB7XG4gIGlmIChlbWJlZGRlZE9iaktleXMgIT0gbnVsbCkge1xuICAgIGNvbnN0IHBhdGggPSBrZXlQYXRoLmpvaW4oJy4nKTtcblxuICAgIGlmIChlbWJlZGRlZE9iaktleXMgaW5zdGFuY2VvZiBNYXApIHtcbiAgICAgIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIGVtYmVkZGVkT2JqS2V5cy5lbnRyaWVzKCkpIHtcbiAgICAgICAgaWYgKGtleSBpbnN0YW5jZW9mIFJlZ0V4cCkge1xuICAgICAgICAgIGlmIChwYXRoLm1hdGNoKGtleSkpIHtcbiAgICAgICAgICAgIHJldHVybiB2YWx1ZTtcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSBpZiAocGF0aCA9PT0ga2V5KSB7XG4gICAgICAgICAgcmV0dXJuIHZhbHVlO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgY29uc3Qga2V5ID0gZW1iZWRkZWRPYmpLZXlzW3BhdGhdO1xuICAgIGlmIChrZXkgIT0gbnVsbCkge1xuICAgICAgcmV0dXJuIGtleTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIHVuZGVmaW5lZDtcbn07XG5cbmNvbnN0IGNvbnZlcnRBcnJheVRvT2JqID0gKGFycjogYW55W10sIHVuaXFLZXk6IGFueSkgPT4ge1xuICBsZXQgb2JqOiBhbnkgPSB7fTtcbiAgaWYgKHVuaXFLZXkgPT09ICckdmFsdWUnKSB7XG4gICAgYXJyLmZvckVhY2goKHZhbHVlKSA9PiB7XG4gICAgICBvYmpbdmFsdWVdID0gdmFsdWU7XG4gICAgfSk7XG4gIH0gZWxzZSBpZiAodW5pcUtleSAhPT0gJyRpbmRleCcpIHtcbiAgICAvLyBDb252ZXJ0IHN0cmluZyBrZXlzIHRvIGZ1bmN0aW9ucyBmb3IgY29tcGF0aWJpbGl0eSB3aXRoIGVzLXRvb2xraXQga2V5QnlcbiAgICBjb25zdCBrZXlGdW5jdGlvbiA9IHR5cGVvZiB1bmlxS2V5ID09PSAnc3RyaW5nJyA/IChpdGVtOiBhbnkpID0+IGl0ZW1bdW5pcUtleV0gOiB1bmlxS2V5O1xuICAgIG9iaiA9IGtleUJ5KGFyciwga2V5RnVuY3Rpb24pO1xuICB9IGVsc2Uge1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgYXJyLmxlbmd0aDsgaSsrKSB7XG4gICAgICBjb25zdCB2YWx1ZSA9IGFycltpXTtcbiAgICAgIG9ialtpXSA9IHZhbHVlO1xuICAgIH1cbiAgfVxuICByZXR1cm4gb2JqO1xufTtcblxuY29uc3QgY29tcGFyZVByaW1pdGl2ZXMgPSAob2xkT2JqOiBhbnksIG5ld09iajogYW55LCBwYXRoOiBhbnkpID0+IHtcbiAgY29uc3QgY2hhbmdlcyA9IFtdO1xuICBpZiAob2xkT2JqICE9PSBuZXdPYmopIHtcbiAgICBjaGFuZ2VzLnB1c2goe1xuICAgICAgdHlwZTogT3BlcmF0aW9uLlVQREFURSxcbiAgICAgIGtleTogZ2V0S2V5KHBhdGgpLFxuICAgICAgdmFsdWU6IG5ld09iaixcbiAgICAgIG9sZFZhbHVlOiBvbGRPYmpcbiAgICB9KTtcbiAgfVxuICByZXR1cm4gY2hhbmdlcztcbn07XG5cbmNvbnN0IHJlbW92ZUtleSA9IChvYmo6IGFueSwga2V5OiBhbnksIGVtYmVkZGVkS2V5OiBhbnkpID0+IHtcbiAgaWYgKEFycmF5LmlzQXJyYXkob2JqKSkge1xuICAgIGlmIChlbWJlZGRlZEtleSA9PT0gJyRpbmRleCcpIHtcbiAgICAgIG9iai5zcGxpY2UoTnVtYmVyKGtleSksIDEpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCBpbmRleCA9IGluZGV4T2ZJdGVtSW5BcnJheShvYmosIGVtYmVkZGVkS2V5LCBrZXkpO1xuICAgIGlmIChpbmRleCA9PT0gLTEpIHtcbiAgICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1jb25zb2xlXG4gICAgICBjb25zb2xlLndhcm4oYEVsZW1lbnQgd2l0aCB0aGUga2V5ICcke2VtYmVkZGVkS2V5fScgYW5kIHZhbHVlICcke2tleX0nIGNvdWxkIG5vdCBiZSBmb3VuZCBpbiB0aGUgYXJyYXknYCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHJldHVybiBvYmouc3BsaWNlKGluZGV4ICE9IG51bGwgPyBpbmRleCA6IGtleSwgMSk7XG4gIH0gZWxzZSB7XG4gICAgZGVsZXRlIG9ialtrZXldO1xuICAgIHJldHVybjtcbiAgfVxufTtcblxuY29uc3QgaW5kZXhPZkl0ZW1JbkFycmF5ID0gKGFycjogYW55W10sIGtleTogYW55LCB2YWx1ZTogYW55KSA9PiB7XG4gIGlmIChrZXkgPT09ICckdmFsdWUnKSB7XG4gICAgcmV0dXJuIGFyci5pbmRleE9mKHZhbHVlKTtcbiAgfVxuICBmb3IgKGxldCBpID0gMDsgaSA8IGFyci5sZW5ndGg7IGkrKykge1xuICAgIGNvbnN0IGl0ZW0gPSBhcnJbaV07XG4gICAgaWYgKGl0ZW0gJiYgaXRlbVtrZXldID8gaXRlbVtrZXldLnRvU3RyaW5nKCkgPT09IHZhbHVlLnRvU3RyaW5nKCkgOiB1bmRlZmluZWQpIHtcbiAgICAgIHJldHVybiBpO1xuICAgIH1cbiAgfVxuICByZXR1cm4gLTE7XG59O1xuXG5jb25zdCBtb2RpZnlLZXlWYWx1ZSA9IChvYmo6IGFueSwga2V5OiBhbnksIHZhbHVlOiBhbnkpID0+IChvYmpba2V5XSA9IHZhbHVlKTtcbmNvbnN0IGFkZEtleVZhbHVlID0gKG9iajogYW55LCBrZXk6IGFueSwgdmFsdWU6IGFueSwgZW1iZWRkZWRLZXk/OiBhbnkpID0+IHtcbiAgaWYgKEFycmF5LmlzQXJyYXkob2JqKSkge1xuICAgIGlmIChlbWJlZGRlZEtleSA9PT0gJyRpbmRleCcpIHtcbiAgICAgIG9iai5zcGxpY2UoTnVtYmVyKGtleSksIDAsIHZhbHVlKTtcbiAgICAgIHJldHVybiBvYmoubGVuZ3RoO1xuICAgIH1cbiAgICByZXR1cm4gb2JqLnB1c2godmFsdWUpO1xuICB9IGVsc2Uge1xuICAgIHJldHVybiBvYmogPyAob2JqW2tleV0gPSB2YWx1ZSkgOiBudWxsO1xuICB9XG59O1xuXG5jb25zdCBhcHBseUxlYWZDaGFuZ2UgPSAob2JqOiBhbnksIGNoYW5nZTogYW55LCBlbWJlZGRlZEtleTogYW55KSA9PiB7XG4gIGNvbnN0IHsgdHlwZSwga2V5LCB2YWx1ZSB9ID0gY2hhbmdlO1xuICBzd2l0Y2ggKHR5cGUpIHtcbiAgICBjYXNlIE9wZXJhdGlvbi5BREQ6XG4gICAgICByZXR1cm4gYWRkS2V5VmFsdWUob2JqLCBrZXksIHZhbHVlLCBlbWJlZGRlZEtleSk7XG4gICAgY2FzZSBPcGVyYXRpb24uVVBEQVRFOlxuICAgICAgcmV0dXJuIG1vZGlmeUtleVZhbHVlKG9iaiwga2V5LCB2YWx1ZSk7XG4gICAgY2FzZSBPcGVyYXRpb24uUkVNT1ZFOlxuICAgICAgcmV0dXJuIHJlbW92ZUtleShvYmosIGtleSwgZW1iZWRkZWRLZXkpO1xuICB9XG59O1xuXG4vKipcbiAqIEFwcGxpZXMgY2hhbmdlcyB0byBhbiBhcnJheS5cbiAqIFxuICogQHBhcmFtIHthbnlbXX0gYXJyIC0gVGhlIGFycmF5IHRvIGFwcGx5IGNoYW5nZXMgdG8uXG4gKiBAcGFyYW0ge2FueX0gY2hhbmdlIC0gVGhlIGNoYW5nZSB0byBhcHBseSwgY29udGFpbmluZyBuZXN0ZWQgY2hhbmdlcy5cbiAqIEByZXR1cm5zIHthbnlbXX0gLSBUaGUgYXJyYXkgYWZ0ZXIgY2hhbmdlcyBoYXZlIGJlZW4gYXBwbGllZC5cbiAqXG4gKiBOb3RlOiBUaGlzIGZ1bmN0aW9uIG1vZGlmaWVzIHRoZSBhcnJheSBpbi1wbGFjZSBidXQgYWxzbyByZXR1cm5zIGl0IGZvclxuICogY29uc2lzdGVuY3kgd2l0aCBvdGhlciBmdW5jdGlvbnMuXG4gKi9cbmNvbnN0IGFwcGx5QXJyYXlDaGFuZ2UgPSAoYXJyOiBhbnlbXSwgY2hhbmdlOiBhbnkpID0+IHtcbiAgbGV0IGNoYW5nZXMgPSBjaGFuZ2UuY2hhbmdlcztcbiAgaWYgKGNoYW5nZS5lbWJlZGRlZEtleSA9PT0gJyRpbmRleCcpIHtcbiAgICBjaGFuZ2VzID0gWy4uLmNoYW5nZXNdLnNvcnQoKGEsIGIpID0+IHtcbiAgICAgIGlmIChhLnR5cGUgPT09IE9wZXJhdGlvbi5SRU1PVkUgJiYgYi50eXBlID09PSBPcGVyYXRpb24uUkVNT1ZFKSB7XG4gICAgICAgIHJldHVybiBOdW1iZXIoYi5rZXkpIC0gTnVtYmVyKGEua2V5KTtcbiAgICAgIH1cbiAgICAgIGlmIChhLnR5cGUgPT09IE9wZXJhdGlvbi5SRU1PVkUpIHJldHVybiAtMTtcbiAgICAgIGlmIChiLnR5cGUgPT09IE9wZXJhdGlvbi5SRU1PVkUpIHJldHVybiAxO1xuICAgICAgcmV0dXJuIE51bWJlcihhLmtleSkgLSBOdW1iZXIoYi5rZXkpO1xuICAgIH0pO1xuICB9XG5cbiAgZm9yIChjb25zdCBzdWJjaGFuZ2Ugb2YgY2hhbmdlcykge1xuICAgIGlmIChcbiAgICAgIChzdWJjaGFuZ2UudmFsdWUgIT09IG51bGwgJiYgc3ViY2hhbmdlLnZhbHVlICE9PSB1bmRlZmluZWQpIHx8XG4gICAgICBzdWJjaGFuZ2UudHlwZSA9PT0gT3BlcmF0aW9uLlJFTU9WRSB8fFxuICAgICAgKHN1YmNoYW5nZS52YWx1ZSA9PT0gbnVsbCAmJiBzdWJjaGFuZ2UudHlwZSA9PT0gT3BlcmF0aW9uLkFERClcbiAgICApIHtcbiAgICAgIGFwcGx5TGVhZkNoYW5nZShhcnIsIHN1YmNoYW5nZSwgY2hhbmdlLmVtYmVkZGVkS2V5KTtcbiAgICB9IGVsc2Uge1xuICAgICAgbGV0IGVsZW1lbnQ7XG4gICAgICBpZiAoY2hhbmdlLmVtYmVkZGVkS2V5ID09PSAnJGluZGV4Jykge1xuICAgICAgICBlbGVtZW50ID0gYXJyW3N1YmNoYW5nZS5rZXldO1xuICAgICAgfSBlbHNlIGlmIChjaGFuZ2UuZW1iZWRkZWRLZXkgPT09ICckdmFsdWUnKSB7XG4gICAgICAgIGNvbnN0IGluZGV4ID0gYXJyLmluZGV4T2Yoc3ViY2hhbmdlLmtleSk7XG4gICAgICAgIGlmIChpbmRleCAhPT0gLTEpIHtcbiAgICAgICAgICBlbGVtZW50ID0gYXJyW2luZGV4XTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgZWxlbWVudCA9IGFyci5maW5kKChlbCkgPT4gZWxbY2hhbmdlLmVtYmVkZGVkS2V5XT8udG9TdHJpbmcoKSA9PT0gc3ViY2hhbmdlLmtleS50b1N0cmluZygpKTtcbiAgICAgIH1cbiAgICAgIGlmIChlbGVtZW50KSB7XG4gICAgICAgIGFwcGx5Q2hhbmdlc2V0KGVsZW1lbnQsIHN1YmNoYW5nZS5jaGFuZ2VzKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgcmV0dXJuIGFycjtcbn07XG5cbmNvbnN0IGFwcGx5QnJhbmNoQ2hhbmdlID0gKG9iajogYW55LCBjaGFuZ2U6IGFueSkgPT4ge1xuICBpZiAoQXJyYXkuaXNBcnJheShvYmopKSB7XG4gICAgcmV0dXJuIGFwcGx5QXJyYXlDaGFuZ2Uob2JqLCBjaGFuZ2UpO1xuICB9IGVsc2Uge1xuICAgIHJldHVybiBhcHBseUNoYW5nZXNldChvYmosIGNoYW5nZS5jaGFuZ2VzKTtcbiAgfVxufTtcblxuY29uc3QgcmV2ZXJ0TGVhZkNoYW5nZSA9IChvYmo6IGFueSwgY2hhbmdlOiBhbnksIGVtYmVkZGVkS2V5ID0gJyRpbmRleCcpID0+IHtcbiAgY29uc3QgeyB0eXBlLCBrZXksIHZhbHVlLCBvbGRWYWx1ZSB9ID0gY2hhbmdlO1xuICBcbiAgLy8gU3BlY2lhbCBoYW5kbGluZyBmb3IgJHJvb3Qga2V5XG4gIGlmIChrZXkgPT09ICckcm9vdCcpIHtcbiAgICBzd2l0Y2ggKHR5cGUpIHtcbiAgICAgIGNhc2UgT3BlcmF0aW9uLkFERDpcbiAgICAgICAgLy8gV2hlbiByZXZlcnRpbmcgYW4gQUREIG9mIHRoZSBlbnRpcmUgb2JqZWN0LCBjbGVhciBhbGwgcHJvcGVydGllc1xuICAgICAgICBmb3IgKGNvbnN0IHByb3AgaW4gb2JqKSB7XG4gICAgICAgICAgaWYgKE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChvYmosIHByb3ApKSB7XG4gICAgICAgICAgICBkZWxldGUgb2JqW3Byb3BdO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gb2JqO1xuICAgICAgY2FzZSBPcGVyYXRpb24uVVBEQVRFOlxuICAgICAgICAvLyBSZXBsYWNlIHRoZSBlbnRpcmUgb2JqZWN0IHdpdGggdGhlIG9sZCB2YWx1ZVxuICAgICAgICBmb3IgKGNvbnN0IHByb3AgaW4gb2JqKSB7XG4gICAgICAgICAgaWYgKE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChvYmosIHByb3ApKSB7XG4gICAgICAgICAgICBkZWxldGUgb2JqW3Byb3BdO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBpZiAob2xkVmFsdWUgJiYgdHlwZW9mIG9sZFZhbHVlID09PSAnb2JqZWN0Jykge1xuICAgICAgICAgIE9iamVjdC5hc3NpZ24ob2JqLCBvbGRWYWx1ZSk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIG9iajtcbiAgICAgIGNhc2UgT3BlcmF0aW9uLlJFTU9WRTpcbiAgICAgICAgLy8gUmVzdG9yZSB0aGUgcmVtb3ZlZCBvYmplY3RcbiAgICAgICAgaWYgKHZhbHVlICYmIHR5cGVvZiB2YWx1ZSA9PT0gJ29iamVjdCcpIHtcbiAgICAgICAgICBPYmplY3QuYXNzaWduKG9iaiwgdmFsdWUpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBvYmo7XG4gICAgfVxuICB9XG4gIFxuICAvLyBSZWd1bGFyIHByb3BlcnR5IGhhbmRsaW5nXG4gIHN3aXRjaCAodHlwZSkge1xuICAgIGNhc2UgT3BlcmF0aW9uLkFERDpcbiAgICAgIHJldHVybiByZW1vdmVLZXkob2JqLCBrZXksIGVtYmVkZGVkS2V5KTtcbiAgICBjYXNlIE9wZXJhdGlvbi5VUERBVEU6XG4gICAgICByZXR1cm4gbW9kaWZ5S2V5VmFsdWUob2JqLCBrZXksIG9sZFZhbHVlKTtcbiAgICBjYXNlIE9wZXJhdGlvbi5SRU1PVkU6XG4gICAgICByZXR1cm4gYWRkS2V5VmFsdWUob2JqLCBrZXksIHZhbHVlKTtcbiAgfVxufTtcblxuLyoqXG4gKiBSZXZlcnRzIGNoYW5nZXMgaW4gYW4gYXJyYXkuXG4gKiBcbiAqIEBwYXJhbSB7YW55W119IGFyciAtIFRoZSBhcnJheSB0byByZXZlcnQgY2hhbmdlcyBpbi5cbiAqIEBwYXJhbSB7YW55fSBjaGFuZ2UgLSBUaGUgY2hhbmdlIHRvIHJldmVydCwgY29udGFpbmluZyBuZXN0ZWQgY2hhbmdlcy5cbiAqIEByZXR1cm5zIHthbnlbXX0gLSBUaGUgYXJyYXkgYWZ0ZXIgY2hhbmdlcyBoYXZlIGJlZW4gcmV2ZXJ0ZWQuXG4gKlxuICogTm90ZTogVGhpcyBmdW5jdGlvbiBtb2RpZmllcyB0aGUgYXJyYXkgaW4tcGxhY2UgYnV0IGFsc28gcmV0dXJucyBpdCBmb3JcbiAqIGNvbnNpc3RlbmN5IHdpdGggb3RoZXIgZnVuY3Rpb25zLlxuICovXG5jb25zdCByZXZlcnRBcnJheUNoYW5nZSA9IChhcnI6IGFueVtdLCBjaGFuZ2U6IGFueSkgPT4ge1xuICBmb3IgKGNvbnN0IHN1YmNoYW5nZSBvZiBjaGFuZ2UuY2hhbmdlcykge1xuICAgIGlmIChzdWJjaGFuZ2UudmFsdWUgIT0gbnVsbCB8fCBzdWJjaGFuZ2UudHlwZSA9PT0gT3BlcmF0aW9uLlJFTU9WRSkge1xuICAgICAgcmV2ZXJ0TGVhZkNoYW5nZShhcnIsIHN1YmNoYW5nZSwgY2hhbmdlLmVtYmVkZGVkS2V5KTtcbiAgICB9IGVsc2Uge1xuICAgICAgbGV0IGVsZW1lbnQ7XG4gICAgICBpZiAoY2hhbmdlLmVtYmVkZGVkS2V5ID09PSAnJGluZGV4Jykge1xuICAgICAgICBlbGVtZW50ID0gYXJyWytzdWJjaGFuZ2Uua2V5XTtcbiAgICAgIH0gZWxzZSBpZiAoY2hhbmdlLmVtYmVkZGVkS2V5ID09PSAnJHZhbHVlJykge1xuICAgICAgICBjb25zdCBpbmRleCA9IGFyci5pbmRleE9mKHN1YmNoYW5nZS5rZXkpO1xuICAgICAgICBpZiAoaW5kZXggIT09IC0xKSB7XG4gICAgICAgICAgZWxlbWVudCA9IGFycltpbmRleF07XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGVsZW1lbnQgPSBhcnIuZmluZCgoZWwpID0+IGVsW2NoYW5nZS5lbWJlZGRlZEtleV0/LnRvU3RyaW5nKCkgPT09IHN1YmNoYW5nZS5rZXkudG9TdHJpbmcoKSk7XG4gICAgICB9XG4gICAgICBpZiAoZWxlbWVudCkge1xuICAgICAgICByZXZlcnRDaGFuZ2VzZXQoZWxlbWVudCwgc3ViY2hhbmdlLmNoYW5nZXMpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuICByZXR1cm4gYXJyO1xufTtcblxuY29uc3QgcmV2ZXJ0QnJhbmNoQ2hhbmdlID0gKG9iajogYW55LCBjaGFuZ2U6IGFueSkgPT4ge1xuICBpZiAoQXJyYXkuaXNBcnJheShvYmopKSB7XG4gICAgcmV0dXJuIHJldmVydEFycmF5Q2hhbmdlKG9iaiwgY2hhbmdlKTtcbiAgfSBlbHNlIHtcbiAgICByZXR1cm4gcmV2ZXJ0Q2hhbmdlc2V0KG9iaiwgY2hhbmdlLmNoYW5nZXMpO1xuICB9XG59O1xuXG4vKiogY29tYmluZSBhIGJhc2UgSlNPTiBQYXRoIHdpdGggYSBzdWJzZXF1ZW50IHNlZ21lbnQgKi9cbmZ1bmN0aW9uIGFwcGVuZChiYXNlUGF0aDogc3RyaW5nLCBuZXh0U2VnbWVudDogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIG5leHRTZWdtZW50LmluY2x1ZGVzKCcuJykgPyBgJHtiYXNlUGF0aH1bJHtuZXh0U2VnbWVudH1dYCA6IGAke2Jhc2VQYXRofS4ke25leHRTZWdtZW50fWA7XG59XG5cbi8qKiByZXR1cm5zIGEgSlNPTiBQYXRoIGZpbHRlciBleHByZXNzaW9uOyBlLmcuLCBgJC5wZXRbKD9uYW1lPSdzcG90JyldYCAqL1xuZnVuY3Rpb24gZmlsdGVyRXhwcmVzc2lvbihiYXNlUGF0aDogc3RyaW5nLCBmaWx0ZXJLZXk6IHN0cmluZyB8IEZ1bmN0aW9uS2V5LCBmaWx0ZXJWYWx1ZTogc3RyaW5nIHwgbnVtYmVyKSB7XG4gIGNvbnN0IHZhbHVlID0gdHlwZW9mIGZpbHRlclZhbHVlID09PSAnbnVtYmVyJyA/IGZpbHRlclZhbHVlIDogYCcke2ZpbHRlclZhbHVlfSdgO1xuICByZXR1cm4gdHlwZW9mIGZpbHRlcktleSA9PT0gJ3N0cmluZycgJiYgZmlsdGVyS2V5LmluY2x1ZGVzKCcuJylcbiAgICA/IGAke2Jhc2VQYXRofVs/KEBbJHtmaWx0ZXJLZXl9XT09JHt2YWx1ZX0pXWBcbiAgICA6IGAke2Jhc2VQYXRofVs/KEAuJHtmaWx0ZXJLZXl9PT0ke3ZhbHVlfSldYDtcbn1cblxuZXhwb3J0IHtcbiAgQ2hhbmdlc2V0LFxuICBFbWJlZGRlZE9iaktleXNNYXBUeXBlLFxuICBFbWJlZGRlZE9iaktleXNUeXBlLFxuICBJQXRvbWljQ2hhbmdlLFxuICBJQ2hhbmdlLFxuICBPcGVyYXRpb24sXG4gIE9wdGlvbnMsXG4gIGFwcGx5Q2hhbmdlc2V0LFxuICBhdG9taXplQ2hhbmdlc2V0LFxuICBkaWZmLFxuICBnZXRUeXBlT2ZPYmosXG4gIHJldmVydENoYW5nZXNldCxcbiAgdW5hdG9taXplQ2hhbmdlc2V0XG59O1xuIiwgImltcG9ydCB7IHNldEJ5UGF0aCB9IGZyb20gJy4vaGVscGVycy5qcyc7XG5pbXBvcnQgeyBkaWZmLCBhdG9taXplQ2hhbmdlc2V0LCBnZXRUeXBlT2ZPYmosIElBdG9taWNDaGFuZ2UsIE9wZXJhdGlvbiB9IGZyb20gJy4vanNvbkRpZmYuanMnO1xuXG5lbnVtIENvbXBhcmVPcGVyYXRpb24ge1xuICBDT05UQUlORVIgPSAnQ09OVEFJTkVSJyxcbiAgVU5DSEFOR0VEID0gJ1VOQ0hBTkdFRCdcbn1cblxuaW50ZXJmYWNlIElDb21wYXJpc29uRW5yaWNoZWROb2RlIHtcbiAgdHlwZTogT3BlcmF0aW9uIHwgQ29tcGFyZU9wZXJhdGlvbjtcbiAgdmFsdWU6IElDb21wYXJpc29uRW5yaWNoZWROb2RlIHwgSUNvbXBhcmlzb25FbnJpY2hlZE5vZGVbXSB8IGFueSB8IGFueVtdO1xuICBvbGRWYWx1ZT86IGFueTtcbn1cblxuY29uc3QgY3JlYXRlVmFsdWUgPSAodmFsdWU6IGFueSk6IElDb21wYXJpc29uRW5yaWNoZWROb2RlID0+ICh7IHR5cGU6IENvbXBhcmVPcGVyYXRpb24uVU5DSEFOR0VELCB2YWx1ZSB9KTtcbmNvbnN0IGNyZWF0ZUNvbnRhaW5lciA9ICh2YWx1ZTogb2JqZWN0IHwgW10pOiBJQ29tcGFyaXNvbkVucmljaGVkTm9kZSA9PiAoe1xuICB0eXBlOiBDb21wYXJlT3BlcmF0aW9uLkNPTlRBSU5FUixcbiAgdmFsdWVcbn0pO1xuXG5jb25zdCBlbnJpY2ggPSAob2JqZWN0OiBhbnkpOiBJQ29tcGFyaXNvbkVucmljaGVkTm9kZSA9PiB7XG4gIGNvbnN0IG9iamVjdFR5cGUgPSBnZXRUeXBlT2ZPYmoob2JqZWN0KTtcblxuICBzd2l0Y2ggKG9iamVjdFR5cGUpIHtcbiAgICBjYXNlICdPYmplY3QnOlxuICAgICAgcmV0dXJuIE9iamVjdC5rZXlzKG9iamVjdClcbiAgICAgICAgLm1hcCgoa2V5OiBzdHJpbmcpID0+ICh7IGtleSwgdmFsdWU6IGVucmljaChvYmplY3Rba2V5XSkgfSkpXG4gICAgICAgIC5yZWR1Y2UoKGFjY3VtdWxhdG9yLCBlbnRyeSkgPT4ge1xuICAgICAgICAgIGFjY3VtdWxhdG9yLnZhbHVlW2VudHJ5LmtleV0gPSBlbnRyeS52YWx1ZTtcbiAgICAgICAgICByZXR1cm4gYWNjdW11bGF0b3I7XG4gICAgICAgIH0sIGNyZWF0ZUNvbnRhaW5lcih7fSkpO1xuICAgIGNhc2UgJ0FycmF5JzpcbiAgICAgIHJldHVybiAob2JqZWN0IGFzIGFueVtdKVxuICAgICAgICAubWFwKCh2YWx1ZSkgPT4gZW5yaWNoKHZhbHVlKSlcbiAgICAgICAgLnJlZHVjZSgoYWNjdW11bGF0b3IsIHZhbHVlKSA9PiB7XG4gICAgICAgICAgYWNjdW11bGF0b3IudmFsdWUucHVzaCh2YWx1ZSk7XG4gICAgICAgICAgcmV0dXJuIGFjY3VtdWxhdG9yO1xuICAgICAgICB9LCBjcmVhdGVDb250YWluZXIoW10pKTtcbiAgICBjYXNlICdGdW5jdGlvbic6XG4gICAgICByZXR1cm4gdW5kZWZpbmVkO1xuICAgIGNhc2UgJ0RhdGUnOlxuICAgIGRlZmF1bHQ6XG4gICAgICAvLyBQcmltaXRpdmUgdmFsdWVcbiAgICAgIHJldHVybiBjcmVhdGVWYWx1ZShvYmplY3QpO1xuICB9XG59O1xuXG5jb25zdCBhcHBseUNoYW5nZWxpc3QgPSAob2JqZWN0OiBJQ29tcGFyaXNvbkVucmljaGVkTm9kZSwgY2hhbmdlbGlzdDogSUF0b21pY0NoYW5nZVtdKTogSUNvbXBhcmlzb25FbnJpY2hlZE5vZGUgPT4ge1xuICBjaGFuZ2VsaXN0XG4gICAgLm1hcCgoZW50cnkpID0+ICh7IC4uLmVudHJ5LCBwYXRoOiBlbnRyeS5wYXRoLnJlcGxhY2UoJyQuJywgJy4nKSB9KSlcbiAgICAubWFwKChlbnRyeSkgPT4gKHtcbiAgICAgIC4uLmVudHJ5LFxuICAgICAgcGF0aDogZW50cnkucGF0aC5yZXBsYWNlKC8oXFxbKD88YXJyYXk+XFxkKVxcXVxcLikvZywgJ0FSUlZBTF9TVEFSVCQ8YXJyYXk+QVJSVkFMX0VORCcpXG4gICAgfSkpXG4gICAgLm1hcCgoZW50cnkpID0+ICh7IC4uLmVudHJ5LCBwYXRoOiBlbnRyeS5wYXRoLnJlcGxhY2UoLyg/PGRvdD5cXC4pL2csICcudmFsdWUkPGRvdD4nKSB9KSlcbiAgICAubWFwKChlbnRyeSkgPT4gKHsgLi4uZW50cnksIHBhdGg6IGVudHJ5LnBhdGgucmVwbGFjZSgvXFwuLywgJycpIH0pKVxuICAgIC5tYXAoKGVudHJ5KSA9PiAoeyAuLi5lbnRyeSwgcGF0aDogZW50cnkucGF0aC5yZXBsYWNlKC9BUlJWQUxfU1RBUlQvZywgJy52YWx1ZVsnKSB9KSlcbiAgICAubWFwKChlbnRyeSkgPT4gKHsgLi4uZW50cnksIHBhdGg6IGVudHJ5LnBhdGgucmVwbGFjZSgvQVJSVkFMX0VORC9nLCAnXS52YWx1ZS4nKSB9KSlcbiAgICAuZm9yRWFjaCgoZW50cnkpID0+IHtcbiAgICAgIHN3aXRjaCAoZW50cnkudHlwZSkge1xuICAgICAgICBjYXNlIE9wZXJhdGlvbi5BREQ6XG4gICAgICAgIGNhc2UgT3BlcmF0aW9uLlVQREFURTpcbiAgICAgICAgICBzZXRCeVBhdGgob2JqZWN0LCBlbnRyeS5wYXRoLCB7IHR5cGU6IGVudHJ5LnR5cGUsIHZhbHVlOiBlbnRyeS52YWx1ZSwgb2xkVmFsdWU6IGVudHJ5Lm9sZFZhbHVlIH0pO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlIE9wZXJhdGlvbi5SRU1PVkU6XG4gICAgICAgICAgc2V0QnlQYXRoKG9iamVjdCwgZW50cnkucGF0aCwgeyB0eXBlOiBlbnRyeS50eXBlLCB2YWx1ZTogdW5kZWZpbmVkLCBvbGRWYWx1ZTogZW50cnkudmFsdWUgfSk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCk7XG4gICAgICB9XG4gICAgfSk7XG4gIHJldHVybiBvYmplY3Q7XG59O1xuXG5jb25zdCBjb21wYXJlID0gKG9sZE9iamVjdDogYW55LCBuZXdPYmplY3Q6IGFueSk6IElDb21wYXJpc29uRW5yaWNoZWROb2RlID0+IHtcbiAgcmV0dXJuIGFwcGx5Q2hhbmdlbGlzdChlbnJpY2gob2xkT2JqZWN0KSwgYXRvbWl6ZUNoYW5nZXNldChkaWZmKG9sZE9iamVjdCwgbmV3T2JqZWN0KSkpO1xufTtcblxuZXhwb3J0IHsgQ29tcGFyZU9wZXJhdGlvbiwgSUNvbXBhcmlzb25FbnJpY2hlZE5vZGUsIGNyZWF0ZVZhbHVlLCBjcmVhdGVDb250YWluZXIsIGVucmljaCwgYXBwbHlDaGFuZ2VsaXN0LCBjb21wYXJlIH07XG4iLCAiaW1wb3J0IHsgU3luY1BsdWdpbiB9IGZyb20gJy4vU3luY1BsdWdpbic7XG5pbXBvcnQgeyBDb3JlRXZlbnRzIH0gZnJvbSAnLi4vY29uc3RhbnRzL0NvcmVFdmVudHMnO1xuaW1wb3J0IHsgZGlmZiB9IGZyb20gJ2pzb24tZGlmZi10cyc7XG4vKipcbiAqIEJhc2VFbnRpdHlTZXJ2aWNlPFQgZXh0ZW5kcyBJU3luYz4gLSBBYnN0cmFjdCBiYXNlIGNsYXNzIGZvciBhbGwgZW50aXR5IHNlcnZpY2VzXG4gKlxuICogUFJPVklERVM6XG4gKiAtIEdlbmVyaWMgQ1JVRCBvcGVyYXRpb25zIChnZXQsIGdldEFsbCwgc2F2ZSwgZGVsZXRlKVxuICogLSBTeW5jIHN0YXR1cyBtYW5hZ2VtZW50IChkZWxlZ2F0ZXMgdG8gU3luY1BsdWdpbilcbiAqIC0gU2VyaWFsaXphdGlvbiBob29rcyAob3ZlcnJpZGUgaW4gc3ViY2xhc3MgaWYgbmVlZGVkKVxuICovXG5leHBvcnQgY2xhc3MgQmFzZUVudGl0eVNlcnZpY2Uge1xuICAgIGNvbnN0cnVjdG9yKGNvbnRleHQsIGV2ZW50QnVzKSB7XG4gICAgICAgIHRoaXMuY29udGV4dCA9IGNvbnRleHQ7XG4gICAgICAgIHRoaXMuZXZlbnRCdXMgPSBldmVudEJ1cztcbiAgICAgICAgdGhpcy5zeW5jUGx1Z2luID0gbmV3IFN5bmNQbHVnaW4odGhpcyk7XG4gICAgfVxuICAgIGdldCBkYigpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuY29udGV4dC5nZXREYXRhYmFzZSgpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBTZXJpYWxpemUgZW50aXR5IGJlZm9yZSBzdG9yaW5nIGluIEluZGV4ZWREQlxuICAgICAqL1xuICAgIHNlcmlhbGl6ZShlbnRpdHkpIHtcbiAgICAgICAgcmV0dXJuIGVudGl0eTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogRGVzZXJpYWxpemUgZGF0YSBmcm9tIEluZGV4ZWREQiBiYWNrIHRvIGVudGl0eVxuICAgICAqL1xuICAgIGRlc2VyaWFsaXplKGRhdGEpIHtcbiAgICAgICAgcmV0dXJuIGRhdGE7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEdldCBhIHNpbmdsZSBlbnRpdHkgYnkgSURcbiAgICAgKi9cbiAgICBhc3luYyBnZXQoaWQpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHRyYW5zYWN0aW9uID0gdGhpcy5kYi50cmFuc2FjdGlvbihbdGhpcy5zdG9yZU5hbWVdLCAncmVhZG9ubHknKTtcbiAgICAgICAgICAgIGNvbnN0IHN0b3JlID0gdHJhbnNhY3Rpb24ub2JqZWN0U3RvcmUodGhpcy5zdG9yZU5hbWUpO1xuICAgICAgICAgICAgY29uc3QgcmVxdWVzdCA9IHN0b3JlLmdldChpZCk7XG4gICAgICAgICAgICByZXF1ZXN0Lm9uc3VjY2VzcyA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICBjb25zdCBkYXRhID0gcmVxdWVzdC5yZXN1bHQ7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZShkYXRhID8gdGhpcy5kZXNlcmlhbGl6ZShkYXRhKSA6IG51bGwpO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIHJlcXVlc3Qub25lcnJvciA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICByZWplY3QobmV3IEVycm9yKGBGYWlsZWQgdG8gZ2V0ICR7dGhpcy5lbnRpdHlUeXBlfSAke2lkfTogJHtyZXF1ZXN0LmVycm9yfWApKTtcbiAgICAgICAgICAgIH07XG4gICAgICAgIH0pO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBHZXQgYWxsIGVudGl0aWVzXG4gICAgICovXG4gICAgYXN5bmMgZ2V0QWxsKCkge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICAgICAgY29uc3QgdHJhbnNhY3Rpb24gPSB0aGlzLmRiLnRyYW5zYWN0aW9uKFt0aGlzLnN0b3JlTmFtZV0sICdyZWFkb25seScpO1xuICAgICAgICAgICAgY29uc3Qgc3RvcmUgPSB0cmFuc2FjdGlvbi5vYmplY3RTdG9yZSh0aGlzLnN0b3JlTmFtZSk7XG4gICAgICAgICAgICBjb25zdCByZXF1ZXN0ID0gc3RvcmUuZ2V0QWxsKCk7XG4gICAgICAgICAgICByZXF1ZXN0Lm9uc3VjY2VzcyA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICBjb25zdCBkYXRhID0gcmVxdWVzdC5yZXN1bHQ7XG4gICAgICAgICAgICAgICAgY29uc3QgZW50aXRpZXMgPSBkYXRhLm1hcChpdGVtID0+IHRoaXMuZGVzZXJpYWxpemUoaXRlbSkpO1xuICAgICAgICAgICAgICAgIHJlc29sdmUoZW50aXRpZXMpO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIHJlcXVlc3Qub25lcnJvciA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICByZWplY3QobmV3IEVycm9yKGBGYWlsZWQgdG8gZ2V0IGFsbCAke3RoaXMuZW50aXR5VHlwZX1zOiAke3JlcXVlc3QuZXJyb3J9YCkpO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgfSk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFNhdmUgYW4gZW50aXR5IChjcmVhdGUgb3IgdXBkYXRlKVxuICAgICAqIEVtaXRzIEVOVElUWV9TQVZFRCBldmVudCB3aXRoIG9wZXJhdGlvbiB0eXBlIGFuZCBjaGFuZ2VzIChkaWZmIGZvciB1cGRhdGVzKVxuICAgICAqIEBwYXJhbSBlbnRpdHkgLSBFbnRpdHkgdG8gc2F2ZVxuICAgICAqIEBwYXJhbSBzaWxlbnQgLSBJZiB0cnVlLCBza2lwIGV2ZW50IGVtaXNzaW9uICh1c2VkIGZvciBzZWVkaW5nKVxuICAgICAqL1xuICAgIGFzeW5jIHNhdmUoZW50aXR5LCBzaWxlbnQgPSBmYWxzZSkge1xuICAgICAgICBjb25zdCBlbnRpdHlJZCA9IGVudGl0eS5pZDtcbiAgICAgICAgY29uc3QgZXhpc3RpbmdFbnRpdHkgPSBhd2FpdCB0aGlzLmdldChlbnRpdHlJZCk7XG4gICAgICAgIGNvbnN0IGlzQ3JlYXRlID0gZXhpc3RpbmdFbnRpdHkgPT09IG51bGw7XG4gICAgICAgIC8vIENhbGN1bGF0ZSBjaGFuZ2VzOiBmdWxsIGVudGl0eSBmb3IgY3JlYXRlLCBkaWZmIGZvciB1cGRhdGVcbiAgICAgICAgbGV0IGNoYW5nZXM7XG4gICAgICAgIGlmIChpc0NyZWF0ZSkge1xuICAgICAgICAgICAgY2hhbmdlcyA9IGVudGl0eTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIGNvbnN0IGV4aXN0aW5nU2VyaWFsaXplZCA9IHRoaXMuc2VyaWFsaXplKGV4aXN0aW5nRW50aXR5KTtcbiAgICAgICAgICAgIGNvbnN0IG5ld1NlcmlhbGl6ZWQgPSB0aGlzLnNlcmlhbGl6ZShlbnRpdHkpO1xuICAgICAgICAgICAgY2hhbmdlcyA9IGRpZmYoZXhpc3RpbmdTZXJpYWxpemVkLCBuZXdTZXJpYWxpemVkKTtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBzZXJpYWxpemVkID0gdGhpcy5zZXJpYWxpemUoZW50aXR5KTtcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHRyYW5zYWN0aW9uID0gdGhpcy5kYi50cmFuc2FjdGlvbihbdGhpcy5zdG9yZU5hbWVdLCAncmVhZHdyaXRlJyk7XG4gICAgICAgICAgICBjb25zdCBzdG9yZSA9IHRyYW5zYWN0aW9uLm9iamVjdFN0b3JlKHRoaXMuc3RvcmVOYW1lKTtcbiAgICAgICAgICAgIGNvbnN0IHJlcXVlc3QgPSBzdG9yZS5wdXQoc2VyaWFsaXplZCk7XG4gICAgICAgICAgICByZXF1ZXN0Lm9uc3VjY2VzcyA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICAvLyBPbmx5IGVtaXQgZXZlbnQgaWYgbm90IHNpbGVudCAoc2lsZW50IHVzZWQgZm9yIHNlZWRpbmcpXG4gICAgICAgICAgICAgICAgaWYgKCFzaWxlbnQpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgcGF5bG9hZCA9IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGVudGl0eVR5cGU6IHRoaXMuZW50aXR5VHlwZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGVudGl0eUlkLFxuICAgICAgICAgICAgICAgICAgICAgICAgb3BlcmF0aW9uOiBpc0NyZWF0ZSA/ICdjcmVhdGUnIDogJ3VwZGF0ZScsXG4gICAgICAgICAgICAgICAgICAgICAgICBjaGFuZ2VzLFxuICAgICAgICAgICAgICAgICAgICAgICAgdGltZXN0YW1wOiBEYXRlLm5vdygpXG4gICAgICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuZXZlbnRCdXMuZW1pdChDb3JlRXZlbnRzLkVOVElUWV9TQVZFRCwgcGF5bG9hZCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIHJlc29sdmUoKTtcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICByZXF1ZXN0Lm9uZXJyb3IgPSAoKSA9PiB7XG4gICAgICAgICAgICAgICAgcmVqZWN0KG5ldyBFcnJvcihgRmFpbGVkIHRvIHNhdmUgJHt0aGlzLmVudGl0eVR5cGV9ICR7ZW50aXR5SWR9OiAke3JlcXVlc3QuZXJyb3J9YCkpO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgfSk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIERlbGV0ZSBhbiBlbnRpdHlcbiAgICAgKiBFbWl0cyBFTlRJVFlfREVMRVRFRCBldmVudFxuICAgICAqL1xuICAgIGFzeW5jIGRlbGV0ZShpZCkge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICAgICAgY29uc3QgdHJhbnNhY3Rpb24gPSB0aGlzLmRiLnRyYW5zYWN0aW9uKFt0aGlzLnN0b3JlTmFtZV0sICdyZWFkd3JpdGUnKTtcbiAgICAgICAgICAgIGNvbnN0IHN0b3JlID0gdHJhbnNhY3Rpb24ub2JqZWN0U3RvcmUodGhpcy5zdG9yZU5hbWUpO1xuICAgICAgICAgICAgY29uc3QgcmVxdWVzdCA9IHN0b3JlLmRlbGV0ZShpZCk7XG4gICAgICAgICAgICByZXF1ZXN0Lm9uc3VjY2VzcyA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICBjb25zdCBwYXlsb2FkID0ge1xuICAgICAgICAgICAgICAgICAgICBlbnRpdHlUeXBlOiB0aGlzLmVudGl0eVR5cGUsXG4gICAgICAgICAgICAgICAgICAgIGVudGl0eUlkOiBpZCxcbiAgICAgICAgICAgICAgICAgICAgb3BlcmF0aW9uOiAnZGVsZXRlJyxcbiAgICAgICAgICAgICAgICAgICAgdGltZXN0YW1wOiBEYXRlLm5vdygpXG4gICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICB0aGlzLmV2ZW50QnVzLmVtaXQoQ29yZUV2ZW50cy5FTlRJVFlfREVMRVRFRCwgcGF5bG9hZCk7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZSgpO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIHJlcXVlc3Qub25lcnJvciA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICByZWplY3QobmV3IEVycm9yKGBGYWlsZWQgdG8gZGVsZXRlICR7dGhpcy5lbnRpdHlUeXBlfSAke2lkfTogJHtyZXF1ZXN0LmVycm9yfWApKTtcbiAgICAgICAgICAgIH07XG4gICAgICAgIH0pO1xuICAgIH1cbiAgICAvLyBTeW5jIG1ldGhvZHMgLSBkZWxlZ2F0ZSB0byBTeW5jUGx1Z2luXG4gICAgYXN5bmMgbWFya0FzU3luY2VkKGlkKSB7XG4gICAgICAgIHJldHVybiB0aGlzLnN5bmNQbHVnaW4ubWFya0FzU3luY2VkKGlkKTtcbiAgICB9XG4gICAgYXN5bmMgbWFya0FzRXJyb3IoaWQpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuc3luY1BsdWdpbi5tYXJrQXNFcnJvcihpZCk7XG4gICAgfVxuICAgIGFzeW5jIGdldFN5bmNTdGF0dXMoaWQpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuc3luY1BsdWdpbi5nZXRTeW5jU3RhdHVzKGlkKTtcbiAgICB9XG4gICAgYXN5bmMgZ2V0QnlTeW5jU3RhdHVzKHN5bmNTdGF0dXMpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuc3luY1BsdWdpbi5nZXRCeVN5bmNTdGF0dXMoc3luY1N0YXR1cyk7XG4gICAgfVxufVxuIiwgImltcG9ydCB7IEV2ZW50U3RvcmUgfSBmcm9tICcuL0V2ZW50U3RvcmUnO1xuaW1wb3J0IHsgRXZlbnRTZXJpYWxpemF0aW9uIH0gZnJvbSAnLi9FdmVudFNlcmlhbGl6YXRpb24nO1xuaW1wb3J0IHsgQmFzZUVudGl0eVNlcnZpY2UgfSBmcm9tICcuLi9CYXNlRW50aXR5U2VydmljZSc7XG4vKipcbiAqIEV2ZW50U2VydmljZSAtIENSVUQgb3BlcmF0aW9ucyBmb3IgY2FsZW5kYXIgZXZlbnRzIGluIEluZGV4ZWREQlxuICpcbiAqIEV4dGVuZHMgQmFzZUVudGl0eVNlcnZpY2UgZm9yIHNoYXJlZCBDUlVEIGFuZCBzeW5jIGxvZ2ljLlxuICogUHJvdmlkZXMgZXZlbnQtc3BlY2lmaWMgcXVlcnkgbWV0aG9kcy5cbiAqL1xuZXhwb3J0IGNsYXNzIEV2ZW50U2VydmljZSBleHRlbmRzIEJhc2VFbnRpdHlTZXJ2aWNlIHtcbiAgICBjb25zdHJ1Y3Rvcihjb250ZXh0LCBldmVudEJ1cykge1xuICAgICAgICBzdXBlcihjb250ZXh0LCBldmVudEJ1cyk7XG4gICAgICAgIHRoaXMuc3RvcmVOYW1lID0gRXZlbnRTdG9yZS5TVE9SRV9OQU1FO1xuICAgICAgICB0aGlzLmVudGl0eVR5cGUgPSAnRXZlbnQnO1xuICAgIH1cbiAgICBzZXJpYWxpemUoZXZlbnQpIHtcbiAgICAgICAgcmV0dXJuIEV2ZW50U2VyaWFsaXphdGlvbi5zZXJpYWxpemUoZXZlbnQpO1xuICAgIH1cbiAgICBkZXNlcmlhbGl6ZShkYXRhKSB7XG4gICAgICAgIHJldHVybiBFdmVudFNlcmlhbGl6YXRpb24uZGVzZXJpYWxpemUoZGF0YSk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEdldCBldmVudHMgd2l0aGluIGEgZGF0ZSByYW5nZVxuICAgICAqL1xuICAgIGFzeW5jIGdldEJ5RGF0ZVJhbmdlKHN0YXJ0LCBlbmQpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHRyYW5zYWN0aW9uID0gdGhpcy5kYi50cmFuc2FjdGlvbihbdGhpcy5zdG9yZU5hbWVdLCAncmVhZG9ubHknKTtcbiAgICAgICAgICAgIGNvbnN0IHN0b3JlID0gdHJhbnNhY3Rpb24ub2JqZWN0U3RvcmUodGhpcy5zdG9yZU5hbWUpO1xuICAgICAgICAgICAgY29uc3QgaW5kZXggPSBzdG9yZS5pbmRleCgnc3RhcnQnKTtcbiAgICAgICAgICAgIGNvbnN0IHJhbmdlID0gSURCS2V5UmFuZ2UubG93ZXJCb3VuZChzdGFydC50b0lTT1N0cmluZygpKTtcbiAgICAgICAgICAgIGNvbnN0IHJlcXVlc3QgPSBpbmRleC5nZXRBbGwocmFuZ2UpO1xuICAgICAgICAgICAgcmVxdWVzdC5vbnN1Y2Nlc3MgPSAoKSA9PiB7XG4gICAgICAgICAgICAgICAgY29uc3QgZGF0YSA9IHJlcXVlc3QucmVzdWx0O1xuICAgICAgICAgICAgICAgIGNvbnN0IGV2ZW50cyA9IGRhdGFcbiAgICAgICAgICAgICAgICAgICAgLm1hcChpdGVtID0+IHRoaXMuZGVzZXJpYWxpemUoaXRlbSkpXG4gICAgICAgICAgICAgICAgICAgIC5maWx0ZXIoZXZlbnQgPT4gZXZlbnQuc3RhcnQgPD0gZW5kKTtcbiAgICAgICAgICAgICAgICByZXNvbHZlKGV2ZW50cyk7XG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgcmVxdWVzdC5vbmVycm9yID0gKCkgPT4ge1xuICAgICAgICAgICAgICAgIHJlamVjdChuZXcgRXJyb3IoYEZhaWxlZCB0byBnZXQgZXZlbnRzIGJ5IGRhdGUgcmFuZ2U6ICR7cmVxdWVzdC5lcnJvcn1gKSk7XG4gICAgICAgICAgICB9O1xuICAgICAgICB9KTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogR2V0IGV2ZW50cyBmb3IgYSBzcGVjaWZpYyByZXNvdXJjZVxuICAgICAqL1xuICAgIGFzeW5jIGdldEJ5UmVzb3VyY2UocmVzb3VyY2VJZCkge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICAgICAgY29uc3QgdHJhbnNhY3Rpb24gPSB0aGlzLmRiLnRyYW5zYWN0aW9uKFt0aGlzLnN0b3JlTmFtZV0sICdyZWFkb25seScpO1xuICAgICAgICAgICAgY29uc3Qgc3RvcmUgPSB0cmFuc2FjdGlvbi5vYmplY3RTdG9yZSh0aGlzLnN0b3JlTmFtZSk7XG4gICAgICAgICAgICBjb25zdCBpbmRleCA9IHN0b3JlLmluZGV4KCdyZXNvdXJjZUlkJyk7XG4gICAgICAgICAgICBjb25zdCByZXF1ZXN0ID0gaW5kZXguZ2V0QWxsKHJlc291cmNlSWQpO1xuICAgICAgICAgICAgcmVxdWVzdC5vbnN1Y2Nlc3MgPSAoKSA9PiB7XG4gICAgICAgICAgICAgICAgY29uc3QgZGF0YSA9IHJlcXVlc3QucmVzdWx0O1xuICAgICAgICAgICAgICAgIGNvbnN0IGV2ZW50cyA9IGRhdGEubWFwKGl0ZW0gPT4gdGhpcy5kZXNlcmlhbGl6ZShpdGVtKSk7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZShldmVudHMpO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIHJlcXVlc3Qub25lcnJvciA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICByZWplY3QobmV3IEVycm9yKGBGYWlsZWQgdG8gZ2V0IGV2ZW50cyBmb3IgcmVzb3VyY2UgJHtyZXNvdXJjZUlkfTogJHtyZXF1ZXN0LmVycm9yfWApKTtcbiAgICAgICAgICAgIH07XG4gICAgICAgIH0pO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBHZXQgZXZlbnRzIGZvciBhIHJlc291cmNlIHdpdGhpbiBhIGRhdGUgcmFuZ2VcbiAgICAgKi9cbiAgICBhc3luYyBnZXRCeVJlc291cmNlQW5kRGF0ZVJhbmdlKHJlc291cmNlSWQsIHN0YXJ0LCBlbmQpIHtcbiAgICAgICAgY29uc3QgcmVzb3VyY2VFdmVudHMgPSBhd2FpdCB0aGlzLmdldEJ5UmVzb3VyY2UocmVzb3VyY2VJZCk7XG4gICAgICAgIHJldHVybiByZXNvdXJjZUV2ZW50cy5maWx0ZXIoZXZlbnQgPT4gZXZlbnQuc3RhcnQgPj0gc3RhcnQgJiYgZXZlbnQuc3RhcnQgPD0gZW5kKTtcbiAgICB9XG59XG4iLCAiLyoqXG4gKiBSZXNvdXJjZVN0b3JlIC0gSW5kZXhlZERCIE9iamVjdFN0b3JlIGRlZmluaXRpb24gZm9yIHJlc291cmNlc1xuICovXG5leHBvcnQgY2xhc3MgUmVzb3VyY2VTdG9yZSB7XG4gICAgY29uc3RydWN0b3IoKSB7XG4gICAgICAgIHRoaXMuc3RvcmVOYW1lID0gUmVzb3VyY2VTdG9yZS5TVE9SRV9OQU1FO1xuICAgIH1cbiAgICBjcmVhdGUoZGIpIHtcbiAgICAgICAgY29uc3Qgc3RvcmUgPSBkYi5jcmVhdGVPYmplY3RTdG9yZShSZXNvdXJjZVN0b3JlLlNUT1JFX05BTUUsIHsga2V5UGF0aDogJ2lkJyB9KTtcbiAgICAgICAgc3RvcmUuY3JlYXRlSW5kZXgoJ3R5cGUnLCAndHlwZScsIHsgdW5pcXVlOiBmYWxzZSB9KTtcbiAgICAgICAgc3RvcmUuY3JlYXRlSW5kZXgoJ3N5bmNTdGF0dXMnLCAnc3luY1N0YXR1cycsIHsgdW5pcXVlOiBmYWxzZSB9KTtcbiAgICAgICAgc3RvcmUuY3JlYXRlSW5kZXgoJ2lzQWN0aXZlJywgJ2lzQWN0aXZlJywgeyB1bmlxdWU6IGZhbHNlIH0pO1xuICAgIH1cbn1cblJlc291cmNlU3RvcmUuU1RPUkVfTkFNRSA9ICdyZXNvdXJjZXMnO1xuIiwgImltcG9ydCB7IFJlc291cmNlU3RvcmUgfSBmcm9tICcuL1Jlc291cmNlU3RvcmUnO1xuaW1wb3J0IHsgQmFzZUVudGl0eVNlcnZpY2UgfSBmcm9tICcuLi9CYXNlRW50aXR5U2VydmljZSc7XG4vKipcbiAqIFJlc291cmNlU2VydmljZSAtIENSVUQgb3BlcmF0aW9ucyBmb3IgcmVzb3VyY2VzIGluIEluZGV4ZWREQlxuICovXG5leHBvcnQgY2xhc3MgUmVzb3VyY2VTZXJ2aWNlIGV4dGVuZHMgQmFzZUVudGl0eVNlcnZpY2Uge1xuICAgIGNvbnN0cnVjdG9yKGNvbnRleHQsIGV2ZW50QnVzKSB7XG4gICAgICAgIHN1cGVyKGNvbnRleHQsIGV2ZW50QnVzKTtcbiAgICAgICAgdGhpcy5zdG9yZU5hbWUgPSBSZXNvdXJjZVN0b3JlLlNUT1JFX05BTUU7XG4gICAgICAgIHRoaXMuZW50aXR5VHlwZSA9ICdSZXNvdXJjZSc7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEdldCBhbGwgYWN0aXZlIHJlc291cmNlc1xuICAgICAqL1xuICAgIGFzeW5jIGdldEFjdGl2ZSgpIHtcbiAgICAgICAgY29uc3QgYWxsID0gYXdhaXQgdGhpcy5nZXRBbGwoKTtcbiAgICAgICAgcmV0dXJuIGFsbC5maWx0ZXIociA9PiByLmlzQWN0aXZlICE9PSBmYWxzZSk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEdldCByZXNvdXJjZXMgYnkgSURzXG4gICAgICovXG4gICAgYXN5bmMgZ2V0QnlJZHMoaWRzKSB7XG4gICAgICAgIGlmIChpZHMubGVuZ3RoID09PSAwKVxuICAgICAgICAgICAgcmV0dXJuIFtdO1xuICAgICAgICBjb25zdCByZXN1bHRzID0gYXdhaXQgUHJvbWlzZS5hbGwoaWRzLm1hcChpZCA9PiB0aGlzLmdldChpZCkpKTtcbiAgICAgICAgcmV0dXJuIHJlc3VsdHMuZmlsdGVyKChyKSA9PiByICE9PSBudWxsKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogR2V0IHJlc291cmNlcyBieSB0eXBlXG4gICAgICovXG4gICAgYXN5bmMgZ2V0QnlUeXBlKHR5cGUpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHRyYW5zYWN0aW9uID0gdGhpcy5kYi50cmFuc2FjdGlvbihbdGhpcy5zdG9yZU5hbWVdLCAncmVhZG9ubHknKTtcbiAgICAgICAgICAgIGNvbnN0IHN0b3JlID0gdHJhbnNhY3Rpb24ub2JqZWN0U3RvcmUodGhpcy5zdG9yZU5hbWUpO1xuICAgICAgICAgICAgY29uc3QgaW5kZXggPSBzdG9yZS5pbmRleCgndHlwZScpO1xuICAgICAgICAgICAgY29uc3QgcmVxdWVzdCA9IGluZGV4LmdldEFsbCh0eXBlKTtcbiAgICAgICAgICAgIHJlcXVlc3Qub25zdWNjZXNzID0gKCkgPT4ge1xuICAgICAgICAgICAgICAgIGNvbnN0IGRhdGEgPSByZXF1ZXN0LnJlc3VsdDtcbiAgICAgICAgICAgICAgICByZXNvbHZlKGRhdGEpO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIHJlcXVlc3Qub25lcnJvciA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICByZWplY3QobmV3IEVycm9yKGBGYWlsZWQgdG8gZ2V0IHJlc291cmNlcyBieSB0eXBlICR7dHlwZX06ICR7cmVxdWVzdC5lcnJvcn1gKSk7XG4gICAgICAgICAgICB9O1xuICAgICAgICB9KTtcbiAgICB9XG59XG4iLCAiLyoqXG4gKiBCb29raW5nU3RvcmUgLSBJbmRleGVkREIgT2JqZWN0U3RvcmUgZGVmaW5pdGlvbiBmb3IgYm9va2luZ3NcbiAqL1xuZXhwb3J0IGNsYXNzIEJvb2tpbmdTdG9yZSB7XG4gICAgY29uc3RydWN0b3IoKSB7XG4gICAgICAgIHRoaXMuc3RvcmVOYW1lID0gQm9va2luZ1N0b3JlLlNUT1JFX05BTUU7XG4gICAgfVxuICAgIGNyZWF0ZShkYikge1xuICAgICAgICBjb25zdCBzdG9yZSA9IGRiLmNyZWF0ZU9iamVjdFN0b3JlKEJvb2tpbmdTdG9yZS5TVE9SRV9OQU1FLCB7IGtleVBhdGg6ICdpZCcgfSk7XG4gICAgICAgIHN0b3JlLmNyZWF0ZUluZGV4KCdjdXN0b21lcklkJywgJ2N1c3RvbWVySWQnLCB7IHVuaXF1ZTogZmFsc2UgfSk7XG4gICAgICAgIHN0b3JlLmNyZWF0ZUluZGV4KCdzdGF0dXMnLCAnc3RhdHVzJywgeyB1bmlxdWU6IGZhbHNlIH0pO1xuICAgICAgICBzdG9yZS5jcmVhdGVJbmRleCgnc3luY1N0YXR1cycsICdzeW5jU3RhdHVzJywgeyB1bmlxdWU6IGZhbHNlIH0pO1xuICAgICAgICBzdG9yZS5jcmVhdGVJbmRleCgnY3JlYXRlZEF0JywgJ2NyZWF0ZWRBdCcsIHsgdW5pcXVlOiBmYWxzZSB9KTtcbiAgICB9XG59XG5Cb29raW5nU3RvcmUuU1RPUkVfTkFNRSA9ICdib29raW5ncyc7XG4iLCAiaW1wb3J0IHsgQm9va2luZ1N0b3JlIH0gZnJvbSAnLi9Cb29raW5nU3RvcmUnO1xuaW1wb3J0IHsgQmFzZUVudGl0eVNlcnZpY2UgfSBmcm9tICcuLi9CYXNlRW50aXR5U2VydmljZSc7XG4vKipcbiAqIEJvb2tpbmdTZXJ2aWNlIC0gQ1JVRCBvcGVyYXRpb25zIGZvciBib29raW5ncyBpbiBJbmRleGVkREJcbiAqL1xuZXhwb3J0IGNsYXNzIEJvb2tpbmdTZXJ2aWNlIGV4dGVuZHMgQmFzZUVudGl0eVNlcnZpY2Uge1xuICAgIGNvbnN0cnVjdG9yKGNvbnRleHQsIGV2ZW50QnVzKSB7XG4gICAgICAgIHN1cGVyKGNvbnRleHQsIGV2ZW50QnVzKTtcbiAgICAgICAgdGhpcy5zdG9yZU5hbWUgPSBCb29raW5nU3RvcmUuU1RPUkVfTkFNRTtcbiAgICAgICAgdGhpcy5lbnRpdHlUeXBlID0gJ0Jvb2tpbmcnO1xuICAgIH1cbiAgICBzZXJpYWxpemUoYm9va2luZykge1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgLi4uYm9va2luZyxcbiAgICAgICAgICAgIGNyZWF0ZWRBdDogYm9va2luZy5jcmVhdGVkQXQudG9JU09TdHJpbmcoKVxuICAgICAgICB9O1xuICAgIH1cbiAgICBkZXNlcmlhbGl6ZShkYXRhKSB7XG4gICAgICAgIGNvbnN0IHJhdyA9IGRhdGE7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAuLi5yYXcsXG4gICAgICAgICAgICBjcmVhdGVkQXQ6IG5ldyBEYXRlKHJhdy5jcmVhdGVkQXQpXG4gICAgICAgIH07XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEdldCBib29raW5ncyBmb3IgYSBjdXN0b21lclxuICAgICAqL1xuICAgIGFzeW5jIGdldEJ5Q3VzdG9tZXIoY3VzdG9tZXJJZCkge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICAgICAgY29uc3QgdHJhbnNhY3Rpb24gPSB0aGlzLmRiLnRyYW5zYWN0aW9uKFt0aGlzLnN0b3JlTmFtZV0sICdyZWFkb25seScpO1xuICAgICAgICAgICAgY29uc3Qgc3RvcmUgPSB0cmFuc2FjdGlvbi5vYmplY3RTdG9yZSh0aGlzLnN0b3JlTmFtZSk7XG4gICAgICAgICAgICBjb25zdCBpbmRleCA9IHN0b3JlLmluZGV4KCdjdXN0b21lcklkJyk7XG4gICAgICAgICAgICBjb25zdCByZXF1ZXN0ID0gaW5kZXguZ2V0QWxsKGN1c3RvbWVySWQpO1xuICAgICAgICAgICAgcmVxdWVzdC5vbnN1Y2Nlc3MgPSAoKSA9PiB7XG4gICAgICAgICAgICAgICAgY29uc3QgZGF0YSA9IHJlcXVlc3QucmVzdWx0O1xuICAgICAgICAgICAgICAgIGNvbnN0IGJvb2tpbmdzID0gZGF0YS5tYXAoaXRlbSA9PiB0aGlzLmRlc2VyaWFsaXplKGl0ZW0pKTtcbiAgICAgICAgICAgICAgICByZXNvbHZlKGJvb2tpbmdzKTtcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICByZXF1ZXN0Lm9uZXJyb3IgPSAoKSA9PiB7XG4gICAgICAgICAgICAgICAgcmVqZWN0KG5ldyBFcnJvcihgRmFpbGVkIHRvIGdldCBib29raW5ncyBmb3IgY3VzdG9tZXIgJHtjdXN0b21lcklkfTogJHtyZXF1ZXN0LmVycm9yfWApKTtcbiAgICAgICAgICAgIH07XG4gICAgICAgIH0pO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBHZXQgYm9va2luZ3MgYnkgc3RhdHVzXG4gICAgICovXG4gICAgYXN5bmMgZ2V0QnlTdGF0dXMoc3RhdHVzKSB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgICAgICBjb25zdCB0cmFuc2FjdGlvbiA9IHRoaXMuZGIudHJhbnNhY3Rpb24oW3RoaXMuc3RvcmVOYW1lXSwgJ3JlYWRvbmx5Jyk7XG4gICAgICAgICAgICBjb25zdCBzdG9yZSA9IHRyYW5zYWN0aW9uLm9iamVjdFN0b3JlKHRoaXMuc3RvcmVOYW1lKTtcbiAgICAgICAgICAgIGNvbnN0IGluZGV4ID0gc3RvcmUuaW5kZXgoJ3N0YXR1cycpO1xuICAgICAgICAgICAgY29uc3QgcmVxdWVzdCA9IGluZGV4LmdldEFsbChzdGF0dXMpO1xuICAgICAgICAgICAgcmVxdWVzdC5vbnN1Y2Nlc3MgPSAoKSA9PiB7XG4gICAgICAgICAgICAgICAgY29uc3QgZGF0YSA9IHJlcXVlc3QucmVzdWx0O1xuICAgICAgICAgICAgICAgIGNvbnN0IGJvb2tpbmdzID0gZGF0YS5tYXAoaXRlbSA9PiB0aGlzLmRlc2VyaWFsaXplKGl0ZW0pKTtcbiAgICAgICAgICAgICAgICByZXNvbHZlKGJvb2tpbmdzKTtcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICByZXF1ZXN0Lm9uZXJyb3IgPSAoKSA9PiB7XG4gICAgICAgICAgICAgICAgcmVqZWN0KG5ldyBFcnJvcihgRmFpbGVkIHRvIGdldCBib29raW5ncyB3aXRoIHN0YXR1cyAke3N0YXR1c306ICR7cmVxdWVzdC5lcnJvcn1gKSk7XG4gICAgICAgICAgICB9O1xuICAgICAgICB9KTtcbiAgICB9XG59XG4iLCAiLyoqXG4gKiBDdXN0b21lclN0b3JlIC0gSW5kZXhlZERCIE9iamVjdFN0b3JlIGRlZmluaXRpb24gZm9yIGN1c3RvbWVyc1xuICovXG5leHBvcnQgY2xhc3MgQ3VzdG9tZXJTdG9yZSB7XG4gICAgY29uc3RydWN0b3IoKSB7XG4gICAgICAgIHRoaXMuc3RvcmVOYW1lID0gQ3VzdG9tZXJTdG9yZS5TVE9SRV9OQU1FO1xuICAgIH1cbiAgICBjcmVhdGUoZGIpIHtcbiAgICAgICAgY29uc3Qgc3RvcmUgPSBkYi5jcmVhdGVPYmplY3RTdG9yZShDdXN0b21lclN0b3JlLlNUT1JFX05BTUUsIHsga2V5UGF0aDogJ2lkJyB9KTtcbiAgICAgICAgc3RvcmUuY3JlYXRlSW5kZXgoJ25hbWUnLCAnbmFtZScsIHsgdW5pcXVlOiBmYWxzZSB9KTtcbiAgICAgICAgc3RvcmUuY3JlYXRlSW5kZXgoJ3Bob25lJywgJ3Bob25lJywgeyB1bmlxdWU6IGZhbHNlIH0pO1xuICAgICAgICBzdG9yZS5jcmVhdGVJbmRleCgnc3luY1N0YXR1cycsICdzeW5jU3RhdHVzJywgeyB1bmlxdWU6IGZhbHNlIH0pO1xuICAgIH1cbn1cbkN1c3RvbWVyU3RvcmUuU1RPUkVfTkFNRSA9ICdjdXN0b21lcnMnO1xuIiwgImltcG9ydCB7IEN1c3RvbWVyU3RvcmUgfSBmcm9tICcuL0N1c3RvbWVyU3RvcmUnO1xuaW1wb3J0IHsgQmFzZUVudGl0eVNlcnZpY2UgfSBmcm9tICcuLi9CYXNlRW50aXR5U2VydmljZSc7XG4vKipcbiAqIEN1c3RvbWVyU2VydmljZSAtIENSVUQgb3BlcmF0aW9ucyBmb3IgY3VzdG9tZXJzIGluIEluZGV4ZWREQlxuICovXG5leHBvcnQgY2xhc3MgQ3VzdG9tZXJTZXJ2aWNlIGV4dGVuZHMgQmFzZUVudGl0eVNlcnZpY2Uge1xuICAgIGNvbnN0cnVjdG9yKGNvbnRleHQsIGV2ZW50QnVzKSB7XG4gICAgICAgIHN1cGVyKGNvbnRleHQsIGV2ZW50QnVzKTtcbiAgICAgICAgdGhpcy5zdG9yZU5hbWUgPSBDdXN0b21lclN0b3JlLlNUT1JFX05BTUU7XG4gICAgICAgIHRoaXMuZW50aXR5VHlwZSA9ICdDdXN0b21lcic7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFNlYXJjaCBjdXN0b21lcnMgYnkgbmFtZSAoY2FzZS1pbnNlbnNpdGl2ZSBjb250YWlucylcbiAgICAgKi9cbiAgICBhc3luYyBzZWFyY2hCeU5hbWUocXVlcnkpIHtcbiAgICAgICAgY29uc3QgYWxsID0gYXdhaXQgdGhpcy5nZXRBbGwoKTtcbiAgICAgICAgY29uc3QgbG93ZXJRdWVyeSA9IHF1ZXJ5LnRvTG93ZXJDYXNlKCk7XG4gICAgICAgIHJldHVybiBhbGwuZmlsdGVyKGMgPT4gYy5uYW1lLnRvTG93ZXJDYXNlKCkuaW5jbHVkZXMobG93ZXJRdWVyeSkpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBGaW5kIGN1c3RvbWVyIGJ5IHBob25lXG4gICAgICovXG4gICAgYXN5bmMgZ2V0QnlQaG9uZShwaG9uZSkge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICAgICAgY29uc3QgdHJhbnNhY3Rpb24gPSB0aGlzLmRiLnRyYW5zYWN0aW9uKFt0aGlzLnN0b3JlTmFtZV0sICdyZWFkb25seScpO1xuICAgICAgICAgICAgY29uc3Qgc3RvcmUgPSB0cmFuc2FjdGlvbi5vYmplY3RTdG9yZSh0aGlzLnN0b3JlTmFtZSk7XG4gICAgICAgICAgICBjb25zdCBpbmRleCA9IHN0b3JlLmluZGV4KCdwaG9uZScpO1xuICAgICAgICAgICAgY29uc3QgcmVxdWVzdCA9IGluZGV4LmdldChwaG9uZSk7XG4gICAgICAgICAgICByZXF1ZXN0Lm9uc3VjY2VzcyA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICBjb25zdCBkYXRhID0gcmVxdWVzdC5yZXN1bHQ7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZShkYXRhID8gZGF0YSA6IG51bGwpO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIHJlcXVlc3Qub25lcnJvciA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICByZWplY3QobmV3IEVycm9yKGBGYWlsZWQgdG8gZmluZCBjdXN0b21lciBieSBwaG9uZSAke3Bob25lfTogJHtyZXF1ZXN0LmVycm9yfWApKTtcbiAgICAgICAgICAgIH07XG4gICAgICAgIH0pO1xuICAgIH1cbn1cbiIsICIvKipcbiAqIFRlYW1TdG9yZSAtIEluZGV4ZWREQiBPYmplY3RTdG9yZSBkZWZpbml0aW9uIGZvciB0ZWFtc1xuICovXG5leHBvcnQgY2xhc3MgVGVhbVN0b3JlIHtcbiAgICBjb25zdHJ1Y3RvcigpIHtcbiAgICAgICAgdGhpcy5zdG9yZU5hbWUgPSBUZWFtU3RvcmUuU1RPUkVfTkFNRTtcbiAgICB9XG4gICAgY3JlYXRlKGRiKSB7XG4gICAgICAgIGRiLmNyZWF0ZU9iamVjdFN0b3JlKFRlYW1TdG9yZS5TVE9SRV9OQU1FLCB7IGtleVBhdGg6ICdpZCcgfSk7XG4gICAgfVxufVxuVGVhbVN0b3JlLlNUT1JFX05BTUUgPSAndGVhbXMnO1xuIiwgImltcG9ydCB7IFRlYW1TdG9yZSB9IGZyb20gJy4vVGVhbVN0b3JlJztcbmltcG9ydCB7IEJhc2VFbnRpdHlTZXJ2aWNlIH0gZnJvbSAnLi4vQmFzZUVudGl0eVNlcnZpY2UnO1xuLyoqXG4gKiBUZWFtU2VydmljZSAtIENSVUQgb3BlcmF0aW9ucyBmb3IgdGVhbXMgaW4gSW5kZXhlZERCXG4gKlxuICogVGVhbXMgZGVmaW5lIHdoaWNoIHJlc291cmNlcyBiZWxvbmcgdG9nZXRoZXIgZm9yIGhpZXJhcmNoaWNhbCBncm91cGluZy5cbiAqIEV4dGVuZHMgQmFzZUVudGl0eVNlcnZpY2UgZm9yIHN0YW5kYXJkIGVudGl0eSBvcGVyYXRpb25zLlxuICovXG5leHBvcnQgY2xhc3MgVGVhbVNlcnZpY2UgZXh0ZW5kcyBCYXNlRW50aXR5U2VydmljZSB7XG4gICAgY29uc3RydWN0b3IoY29udGV4dCwgZXZlbnRCdXMpIHtcbiAgICAgICAgc3VwZXIoY29udGV4dCwgZXZlbnRCdXMpO1xuICAgICAgICB0aGlzLnN0b3JlTmFtZSA9IFRlYW1TdG9yZS5TVE9SRV9OQU1FO1xuICAgICAgICB0aGlzLmVudGl0eVR5cGUgPSAnVGVhbSc7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEdldCB0ZWFtcyBieSBJRHNcbiAgICAgKi9cbiAgICBhc3luYyBnZXRCeUlkcyhpZHMpIHtcbiAgICAgICAgaWYgKGlkcy5sZW5ndGggPT09IDApXG4gICAgICAgICAgICByZXR1cm4gW107XG4gICAgICAgIGNvbnN0IHJlc3VsdHMgPSBhd2FpdCBQcm9taXNlLmFsbChpZHMubWFwKGlkID0+IHRoaXMuZ2V0KGlkKSkpO1xuICAgICAgICByZXR1cm4gcmVzdWx0cy5maWx0ZXIoKHQpID0+IHQgIT09IG51bGwpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBCdWlsZCByZXZlcnNlIGxvb2t1cDogcmVzb3VyY2VJZCBcdTIxOTIgdGVhbUlkXG4gICAgICovXG4gICAgYXN5bmMgYnVpbGRSZXNvdXJjZVRvVGVhbU1hcCgpIHtcbiAgICAgICAgY29uc3QgdGVhbXMgPSBhd2FpdCB0aGlzLmdldEFsbCgpO1xuICAgICAgICBjb25zdCBtYXAgPSB7fTtcbiAgICAgICAgZm9yIChjb25zdCB0ZWFtIG9mIHRlYW1zKSB7XG4gICAgICAgICAgICBmb3IgKGNvbnN0IHJlc291cmNlSWQgb2YgdGVhbS5yZXNvdXJjZUlkcykge1xuICAgICAgICAgICAgICAgIG1hcFtyZXNvdXJjZUlkXSA9IHRlYW0uaWQ7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIG1hcDtcbiAgICB9XG59XG4iLCAiLyoqXG4gKiBEZXBhcnRtZW50U3RvcmUgLSBJbmRleGVkREIgT2JqZWN0U3RvcmUgZGVmaW5pdGlvbiBmb3IgZGVwYXJ0bWVudHNcbiAqL1xuZXhwb3J0IGNsYXNzIERlcGFydG1lbnRTdG9yZSB7XG4gICAgY29uc3RydWN0b3IoKSB7XG4gICAgICAgIHRoaXMuc3RvcmVOYW1lID0gRGVwYXJ0bWVudFN0b3JlLlNUT1JFX05BTUU7XG4gICAgfVxuICAgIGNyZWF0ZShkYikge1xuICAgICAgICBkYi5jcmVhdGVPYmplY3RTdG9yZShEZXBhcnRtZW50U3RvcmUuU1RPUkVfTkFNRSwgeyBrZXlQYXRoOiAnaWQnIH0pO1xuICAgIH1cbn1cbkRlcGFydG1lbnRTdG9yZS5TVE9SRV9OQU1FID0gJ2RlcGFydG1lbnRzJztcbiIsICJpbXBvcnQgeyBEZXBhcnRtZW50U3RvcmUgfSBmcm9tICcuL0RlcGFydG1lbnRTdG9yZSc7XG5pbXBvcnQgeyBCYXNlRW50aXR5U2VydmljZSB9IGZyb20gJy4uL0Jhc2VFbnRpdHlTZXJ2aWNlJztcbi8qKlxuICogRGVwYXJ0bWVudFNlcnZpY2UgLSBDUlVEIG9wZXJhdGlvbnMgZm9yIGRlcGFydG1lbnRzIGluIEluZGV4ZWREQlxuICovXG5leHBvcnQgY2xhc3MgRGVwYXJ0bWVudFNlcnZpY2UgZXh0ZW5kcyBCYXNlRW50aXR5U2VydmljZSB7XG4gICAgY29uc3RydWN0b3IoY29udGV4dCwgZXZlbnRCdXMpIHtcbiAgICAgICAgc3VwZXIoY29udGV4dCwgZXZlbnRCdXMpO1xuICAgICAgICB0aGlzLnN0b3JlTmFtZSA9IERlcGFydG1lbnRTdG9yZS5TVE9SRV9OQU1FO1xuICAgICAgICB0aGlzLmVudGl0eVR5cGUgPSAnRGVwYXJ0bWVudCc7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEdldCBkZXBhcnRtZW50cyBieSBJRHNcbiAgICAgKi9cbiAgICBhc3luYyBnZXRCeUlkcyhpZHMpIHtcbiAgICAgICAgaWYgKGlkcy5sZW5ndGggPT09IDApXG4gICAgICAgICAgICByZXR1cm4gW107XG4gICAgICAgIGNvbnN0IHJlc3VsdHMgPSBhd2FpdCBQcm9taXNlLmFsbChpZHMubWFwKGlkID0+IHRoaXMuZ2V0KGlkKSkpO1xuICAgICAgICByZXR1cm4gcmVzdWx0cy5maWx0ZXIoKGQpID0+IGQgIT09IG51bGwpO1xuICAgIH1cbn1cbiIsICIvKipcbiAqIFNldHRpbmdzU3RvcmUgLSBJbmRleGVkREIgT2JqZWN0U3RvcmUgZGVmaW5pdGlvbiBmb3IgdGVuYW50IHNldHRpbmdzXG4gKlxuICogU2luZ2xlIHN0b3JlIGZvciBhbGwgc2V0dGluZ3Mgc2VjdGlvbnMuIFNldHRpbmdzIGFyZSBzdG9yZWQgYXMgb25lIGRvY3VtZW50XG4gKiBwZXIgdGVuYW50IHdpdGggaWQ9J3RlbmFudC1zZXR0aW5ncycuXG4gKi9cbmV4cG9ydCBjbGFzcyBTZXR0aW5nc1N0b3JlIHtcbiAgICBjb25zdHJ1Y3RvcigpIHtcbiAgICAgICAgdGhpcy5zdG9yZU5hbWUgPSBTZXR0aW5nc1N0b3JlLlNUT1JFX05BTUU7XG4gICAgfVxuICAgIGNyZWF0ZShkYikge1xuICAgICAgICBkYi5jcmVhdGVPYmplY3RTdG9yZShTZXR0aW5nc1N0b3JlLlNUT1JFX05BTUUsIHsga2V5UGF0aDogJ2lkJyB9KTtcbiAgICB9XG59XG5TZXR0aW5nc1N0b3JlLlNUT1JFX05BTUUgPSAnc2V0dGluZ3MnO1xuIiwgIi8qKlxuICogU2V0dGluZ3MgSURzIGFzIGNvbnN0IGZvciB0eXBlIHNhZmV0eVxuICovXG5leHBvcnQgY29uc3QgU2V0dGluZ3NJZHMgPSB7XG4gICAgV09SS1dFRUs6ICd3b3Jrd2VlaycsXG4gICAgR1JJRDogJ2dyaWQnLFxuICAgIFRJTUVfRk9STUFUOiAndGltZUZvcm1hdCcsXG4gICAgVklFV1M6ICd2aWV3cydcbn07XG4iLCAiaW1wb3J0IHsgU2V0dGluZ3NJZHMgfSBmcm9tICcuLi8uLi90eXBlcy9TZXR0aW5nc1R5cGVzJztcbmltcG9ydCB7IFNldHRpbmdzU3RvcmUgfSBmcm9tICcuL1NldHRpbmdzU3RvcmUnO1xuaW1wb3J0IHsgQmFzZUVudGl0eVNlcnZpY2UgfSBmcm9tICcuLi9CYXNlRW50aXR5U2VydmljZSc7XG4vKipcbiAqIFNldHRpbmdzU2VydmljZSAtIENSVUQgb3BlcmF0aW9ucyBmb3IgdGVuYW50IHNldHRpbmdzXG4gKlxuICogU2V0dGluZ3MgYXJlIHN0b3JlZCBhcyBzZXBhcmF0ZSByZWNvcmRzIHBlciBzZWN0aW9uLlxuICogVGhpcyBzZXJ2aWNlIHByb3ZpZGVzIHR5cGVkIG1ldGhvZHMgZm9yIGFjY2Vzc2luZyBzcGVjaWZpYyBzZXR0aW5ncy5cbiAqL1xuZXhwb3J0IGNsYXNzIFNldHRpbmdzU2VydmljZSBleHRlbmRzIEJhc2VFbnRpdHlTZXJ2aWNlIHtcbiAgICBjb25zdHJ1Y3Rvcihjb250ZXh0LCBldmVudEJ1cykge1xuICAgICAgICBzdXBlcihjb250ZXh0LCBldmVudEJ1cyk7XG4gICAgICAgIHRoaXMuc3RvcmVOYW1lID0gU2V0dGluZ3NTdG9yZS5TVE9SRV9OQU1FO1xuICAgICAgICB0aGlzLmVudGl0eVR5cGUgPSAnU2V0dGluZ3MnO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBHZXQgd29ya3dlZWsgc2V0dGluZ3NcbiAgICAgKi9cbiAgICBhc3luYyBnZXRXb3Jrd2Vla1NldHRpbmdzKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5nZXQoU2V0dGluZ3NJZHMuV09SS1dFRUspO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBHZXQgZ3JpZCBzZXR0aW5nc1xuICAgICAqL1xuICAgIGFzeW5jIGdldEdyaWRTZXR0aW5ncygpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuZ2V0KFNldHRpbmdzSWRzLkdSSUQpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBHZXQgdGltZSBmb3JtYXQgc2V0dGluZ3NcbiAgICAgKi9cbiAgICBhc3luYyBnZXRUaW1lRm9ybWF0U2V0dGluZ3MoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmdldChTZXR0aW5nc0lkcy5USU1FX0ZPUk1BVCk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEdldCB2aWV3IHNldHRpbmdzXG4gICAgICovXG4gICAgYXN5bmMgZ2V0Vmlld1NldHRpbmdzKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5nZXQoU2V0dGluZ3NJZHMuVklFV1MpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBHZXQgd29ya3dlZWsgcHJlc2V0IGJ5IElEXG4gICAgICovXG4gICAgYXN5bmMgZ2V0V29ya3dlZWtQcmVzZXQocHJlc2V0SWQpIHtcbiAgICAgICAgY29uc3Qgc2V0dGluZ3MgPSBhd2FpdCB0aGlzLmdldFdvcmt3ZWVrU2V0dGluZ3MoKTtcbiAgICAgICAgaWYgKCFzZXR0aW5ncylcbiAgICAgICAgICAgIHJldHVybiBudWxsO1xuICAgICAgICByZXR1cm4gc2V0dGluZ3MucHJlc2V0c1twcmVzZXRJZF0gfHwgbnVsbDtcbiAgICB9XG4gICAgLyoqXG4gICAgICogR2V0IHRoZSBkZWZhdWx0IHdvcmt3ZWVrIHByZXNldFxuICAgICAqL1xuICAgIGFzeW5jIGdldERlZmF1bHRXb3Jrd2Vla1ByZXNldCgpIHtcbiAgICAgICAgY29uc3Qgc2V0dGluZ3MgPSBhd2FpdCB0aGlzLmdldFdvcmt3ZWVrU2V0dGluZ3MoKTtcbiAgICAgICAgaWYgKCFzZXR0aW5ncylcbiAgICAgICAgICAgIHJldHVybiBudWxsO1xuICAgICAgICByZXR1cm4gc2V0dGluZ3MucHJlc2V0c1tzZXR0aW5ncy5kZWZhdWx0UHJlc2V0XSB8fCBudWxsO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBHZXQgYWxsIGF2YWlsYWJsZSB3b3Jrd2VlayBwcmVzZXRzXG4gICAgICovXG4gICAgYXN5bmMgZ2V0V29ya3dlZWtQcmVzZXRzKCkge1xuICAgICAgICBjb25zdCBzZXR0aW5ncyA9IGF3YWl0IHRoaXMuZ2V0V29ya3dlZWtTZXR0aW5ncygpO1xuICAgICAgICBpZiAoIXNldHRpbmdzKVxuICAgICAgICAgICAgcmV0dXJuIFtdO1xuICAgICAgICByZXR1cm4gT2JqZWN0LnZhbHVlcyhzZXR0aW5ncy5wcmVzZXRzKTtcbiAgICB9XG59XG4iLCAiZXhwb3J0IGNsYXNzIFZpZXdDb25maWdTdG9yZSB7XG4gICAgY29uc3RydWN0b3IoKSB7XG4gICAgICAgIHRoaXMuc3RvcmVOYW1lID0gVmlld0NvbmZpZ1N0b3JlLlNUT1JFX05BTUU7XG4gICAgfVxuICAgIGNyZWF0ZShkYikge1xuICAgICAgICBkYi5jcmVhdGVPYmplY3RTdG9yZShWaWV3Q29uZmlnU3RvcmUuU1RPUkVfTkFNRSwgeyBrZXlQYXRoOiAnaWQnIH0pO1xuICAgIH1cbn1cblZpZXdDb25maWdTdG9yZS5TVE9SRV9OQU1FID0gJ3ZpZXdjb25maWdzJztcbiIsICJpbXBvcnQgeyBWaWV3Q29uZmlnU3RvcmUgfSBmcm9tICcuL1ZpZXdDb25maWdTdG9yZSc7XG5pbXBvcnQgeyBCYXNlRW50aXR5U2VydmljZSB9IGZyb20gJy4uL0Jhc2VFbnRpdHlTZXJ2aWNlJztcbmV4cG9ydCBjbGFzcyBWaWV3Q29uZmlnU2VydmljZSBleHRlbmRzIEJhc2VFbnRpdHlTZXJ2aWNlIHtcbiAgICBjb25zdHJ1Y3Rvcihjb250ZXh0LCBldmVudEJ1cykge1xuICAgICAgICBzdXBlcihjb250ZXh0LCBldmVudEJ1cyk7XG4gICAgICAgIHRoaXMuc3RvcmVOYW1lID0gVmlld0NvbmZpZ1N0b3JlLlNUT1JFX05BTUU7XG4gICAgICAgIHRoaXMuZW50aXR5VHlwZSA9ICdWaWV3Q29uZmlnJztcbiAgICB9XG4gICAgYXN5bmMgZ2V0QnlJZChpZCkge1xuICAgICAgICByZXR1cm4gdGhpcy5nZXQoaWQpO1xuICAgIH1cbn1cbiIsICIvKipcbiAqIEF1ZGl0U3RvcmUgLSBJbmRleGVkREIgc3RvcmUgY29uZmlndXJhdGlvbiBmb3IgYXVkaXQgZW50cmllc1xuICpcbiAqIFN0b3JlcyBhbGwgZW50aXR5IGNoYW5nZXMgZm9yOlxuICogLSBDb21wbGlhbmNlIGFuZCBhdWRpdCB0cmFpbFxuICogLSBTeW5jIHRyYWNraW5nIHdpdGggYmFja2VuZFxuICogLSBDaGFuZ2UgaGlzdG9yeVxuICpcbiAqIEluZGV4ZXM6XG4gKiAtIHN5bmNTdGF0dXM6IEZvciBmaW5kaW5nIHBlbmRpbmcgZW50cmllcyB0byBzeW5jXG4gKiAtIHN5bmNlZDogQm9vbGVhbiBmbGFnIGZvciBxdWljayBzeW5jIHF1ZXJpZXNcbiAqIC0gZW50aXR5SWQ6IEZvciBnZXR0aW5nIGFsbCBhdWRpdHMgZm9yIGEgc3BlY2lmaWMgZW50aXR5XG4gKiAtIHRpbWVzdGFtcDogRm9yIGNocm9ub2xvZ2ljYWwgcXVlcmllc1xuICovXG5leHBvcnQgY2xhc3MgQXVkaXRTdG9yZSB7XG4gICAgY29uc3RydWN0b3IoKSB7XG4gICAgICAgIHRoaXMuc3RvcmVOYW1lID0gJ2F1ZGl0JztcbiAgICB9XG4gICAgY3JlYXRlKGRiKSB7XG4gICAgICAgIGNvbnN0IHN0b3JlID0gZGIuY3JlYXRlT2JqZWN0U3RvcmUodGhpcy5zdG9yZU5hbWUsIHsga2V5UGF0aDogJ2lkJyB9KTtcbiAgICAgICAgc3RvcmUuY3JlYXRlSW5kZXgoJ3N5bmNTdGF0dXMnLCAnc3luY1N0YXR1cycsIHsgdW5pcXVlOiBmYWxzZSB9KTtcbiAgICAgICAgc3RvcmUuY3JlYXRlSW5kZXgoJ3N5bmNlZCcsICdzeW5jZWQnLCB7IHVuaXF1ZTogZmFsc2UgfSk7XG4gICAgICAgIHN0b3JlLmNyZWF0ZUluZGV4KCdlbnRpdHlJZCcsICdlbnRpdHlJZCcsIHsgdW5pcXVlOiBmYWxzZSB9KTtcbiAgICAgICAgc3RvcmUuY3JlYXRlSW5kZXgoJ3RpbWVzdGFtcCcsICd0aW1lc3RhbXAnLCB7IHVuaXF1ZTogZmFsc2UgfSk7XG4gICAgfVxufVxuIiwgImltcG9ydCB7IEJhc2VFbnRpdHlTZXJ2aWNlIH0gZnJvbSAnLi4vQmFzZUVudGl0eVNlcnZpY2UnO1xuaW1wb3J0IHsgQ29yZUV2ZW50cyB9IGZyb20gJy4uLy4uL2NvbnN0YW50cy9Db3JlRXZlbnRzJztcbi8qKlxuICogQXVkaXRTZXJ2aWNlIC0gRW50aXR5IHNlcnZpY2UgZm9yIGF1ZGl0IGVudHJpZXNcbiAqXG4gKiBSRVNQT05TSUJJTElUSUVTOlxuICogLSBTdG9yZSBhdWRpdCBlbnRyaWVzIGluIEluZGV4ZWREQlxuICogLSBMaXN0ZW4gZm9yIEVOVElUWV9TQVZFRC9FTlRJVFlfREVMRVRFRCBldmVudHNcbiAqIC0gQ3JlYXRlIGF1ZGl0IGVudHJpZXMgZm9yIGFsbCBlbnRpdHkgY2hhbmdlc1xuICogLSBFbWl0IEFVRElUX0xPR0dFRCBhZnRlciBzYXZpbmcgKGZvciBTeW5jTWFuYWdlciB0byBsaXN0ZW4pXG4gKlxuICogT1ZFUlJJREUgUEFUVEVSTjpcbiAqIC0gT3ZlcnJpZGVzIHNhdmUoKSB0byBOT1QgZW1pdCBldmVudHMgKHByZXZlbnRzIGluZmluaXRlIGxvb3BzKVxuICogLSBBdWRpdFNlcnZpY2Ugc2F2ZXMgYXVkaXQgZW50cmllcyB3aXRob3V0IHRyaWdnZXJpbmcgbW9yZSBhdWRpdHNcbiAqXG4gKiBFVkVOVCBDSEFJTjpcbiAqIEVudGl0eSBjaGFuZ2UgXHUyMTkyIEVOVElUWV9TQVZFRC9ERUxFVEVEIFx1MjE5MiBBdWRpdFNlcnZpY2UgXHUyMTkyIEFVRElUX0xPR0dFRCBcdTIxOTIgU3luY01hbmFnZXJcbiAqL1xuZXhwb3J0IGNsYXNzIEF1ZGl0U2VydmljZSBleHRlbmRzIEJhc2VFbnRpdHlTZXJ2aWNlIHtcbiAgICBjb25zdHJ1Y3Rvcihjb250ZXh0LCBldmVudEJ1cykge1xuICAgICAgICBzdXBlcihjb250ZXh0LCBldmVudEJ1cyk7XG4gICAgICAgIHRoaXMuc3RvcmVOYW1lID0gJ2F1ZGl0JztcbiAgICAgICAgdGhpcy5lbnRpdHlUeXBlID0gJ0F1ZGl0JztcbiAgICAgICAgdGhpcy5zZXR1cEV2ZW50TGlzdGVuZXJzKCk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFNldHVwIGxpc3RlbmVycyBmb3IgRU5USVRZX1NBVkVEIGFuZCBFTlRJVFlfREVMRVRFRCBldmVudHNcbiAgICAgKi9cbiAgICBzZXR1cEV2ZW50TGlzdGVuZXJzKCkge1xuICAgICAgICAvLyBMaXN0ZW4gZm9yIGVudGl0eSBzYXZlcyAoY3JlYXRlL3VwZGF0ZSlcbiAgICAgICAgdGhpcy5ldmVudEJ1cy5vbihDb3JlRXZlbnRzLkVOVElUWV9TQVZFRCwgKGV2ZW50KSA9PiB7XG4gICAgICAgICAgICBjb25zdCBkZXRhaWwgPSBldmVudC5kZXRhaWw7XG4gICAgICAgICAgICB0aGlzLmhhbmRsZUVudGl0eVNhdmVkKGRldGFpbCk7XG4gICAgICAgIH0pO1xuICAgICAgICAvLyBMaXN0ZW4gZm9yIGVudGl0eSBkZWxldGVzXG4gICAgICAgIHRoaXMuZXZlbnRCdXMub24oQ29yZUV2ZW50cy5FTlRJVFlfREVMRVRFRCwgKGV2ZW50KSA9PiB7XG4gICAgICAgICAgICBjb25zdCBkZXRhaWwgPSBldmVudC5kZXRhaWw7XG4gICAgICAgICAgICB0aGlzLmhhbmRsZUVudGl0eURlbGV0ZWQoZGV0YWlsKTtcbiAgICAgICAgfSk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEhhbmRsZSBFTlRJVFlfU0FWRUQgZXZlbnQgLSBjcmVhdGUgYXVkaXQgZW50cnlcbiAgICAgKi9cbiAgICBhc3luYyBoYW5kbGVFbnRpdHlTYXZlZChwYXlsb2FkKSB7XG4gICAgICAgIC8vIERvbid0IGF1ZGl0IGF1ZGl0IGVudHJpZXMgKHByZXZlbnQgaW5maW5pdGUgbG9vcHMpXG4gICAgICAgIGlmIChwYXlsb2FkLmVudGl0eVR5cGUgPT09ICdBdWRpdCcpXG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIGNvbnN0IGF1ZGl0RW50cnkgPSB7XG4gICAgICAgICAgICBpZDogY3J5cHRvLnJhbmRvbVVVSUQoKSxcbiAgICAgICAgICAgIGVudGl0eVR5cGU6IHBheWxvYWQuZW50aXR5VHlwZSxcbiAgICAgICAgICAgIGVudGl0eUlkOiBwYXlsb2FkLmVudGl0eUlkLFxuICAgICAgICAgICAgb3BlcmF0aW9uOiBwYXlsb2FkLm9wZXJhdGlvbixcbiAgICAgICAgICAgIHVzZXJJZDogQXVkaXRTZXJ2aWNlLkRFRkFVTFRfVVNFUl9JRCxcbiAgICAgICAgICAgIHRpbWVzdGFtcDogcGF5bG9hZC50aW1lc3RhbXAsXG4gICAgICAgICAgICBjaGFuZ2VzOiBwYXlsb2FkLmNoYW5nZXMsXG4gICAgICAgICAgICBzeW5jZWQ6IGZhbHNlLFxuICAgICAgICAgICAgc3luY1N0YXR1czogJ3BlbmRpbmcnXG4gICAgICAgIH07XG4gICAgICAgIGF3YWl0IHRoaXMuc2F2ZShhdWRpdEVudHJ5KTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogSGFuZGxlIEVOVElUWV9ERUxFVEVEIGV2ZW50IC0gY3JlYXRlIGF1ZGl0IGVudHJ5XG4gICAgICovXG4gICAgYXN5bmMgaGFuZGxlRW50aXR5RGVsZXRlZChwYXlsb2FkKSB7XG4gICAgICAgIC8vIERvbid0IGF1ZGl0IGF1ZGl0IGVudHJpZXMgKHByZXZlbnQgaW5maW5pdGUgbG9vcHMpXG4gICAgICAgIGlmIChwYXlsb2FkLmVudGl0eVR5cGUgPT09ICdBdWRpdCcpXG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIGNvbnN0IGF1ZGl0RW50cnkgPSB7XG4gICAgICAgICAgICBpZDogY3J5cHRvLnJhbmRvbVVVSUQoKSxcbiAgICAgICAgICAgIGVudGl0eVR5cGU6IHBheWxvYWQuZW50aXR5VHlwZSxcbiAgICAgICAgICAgIGVudGl0eUlkOiBwYXlsb2FkLmVudGl0eUlkLFxuICAgICAgICAgICAgb3BlcmF0aW9uOiAnZGVsZXRlJyxcbiAgICAgICAgICAgIHVzZXJJZDogQXVkaXRTZXJ2aWNlLkRFRkFVTFRfVVNFUl9JRCxcbiAgICAgICAgICAgIHRpbWVzdGFtcDogcGF5bG9hZC50aW1lc3RhbXAsXG4gICAgICAgICAgICBjaGFuZ2VzOiB7IGlkOiBwYXlsb2FkLmVudGl0eUlkIH0sIC8vIEZvciBkZWxldGUsIGp1c3Qgc3RvcmUgdGhlIElEXG4gICAgICAgICAgICBzeW5jZWQ6IGZhbHNlLFxuICAgICAgICAgICAgc3luY1N0YXR1czogJ3BlbmRpbmcnXG4gICAgICAgIH07XG4gICAgICAgIGF3YWl0IHRoaXMuc2F2ZShhdWRpdEVudHJ5KTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogT3ZlcnJpZGUgc2F2ZSB0byBOT1QgdHJpZ2dlciBFTlRJVFlfU0FWRUQgZXZlbnRcbiAgICAgKiBJbnN0ZWFkLCBlbWl0cyBBVURJVF9MT0dHRUQgZm9yIFN5bmNNYW5hZ2VyIHRvIGxpc3RlblxuICAgICAqXG4gICAgICogVGhpcyBwcmV2ZW50cyBpbmZpbml0ZSBsb29wczpcbiAgICAgKiAtIEJhc2VFbnRpdHlTZXJ2aWNlLnNhdmUoKSBlbWl0cyBFTlRJVFlfU0FWRURcbiAgICAgKiAtIEF1ZGl0U2VydmljZSBsaXN0ZW5zIHRvIEVOVElUWV9TQVZFRCBhbmQgY3JlYXRlcyBhdWRpdFxuICAgICAqIC0gSWYgQXVkaXRTZXJ2aWNlLnNhdmUoKSBhbHNvIGVtaXR0ZWQgRU5USVRZX1NBVkVELCBpdCB3b3VsZCBsb29wXG4gICAgICovXG4gICAgYXN5bmMgc2F2ZShlbnRpdHkpIHtcbiAgICAgICAgY29uc3Qgc2VyaWFsaXplZCA9IHRoaXMuc2VyaWFsaXplKGVudGl0eSk7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgICAgICBjb25zdCB0cmFuc2FjdGlvbiA9IHRoaXMuZGIudHJhbnNhY3Rpb24oW3RoaXMuc3RvcmVOYW1lXSwgJ3JlYWR3cml0ZScpO1xuICAgICAgICAgICAgY29uc3Qgc3RvcmUgPSB0cmFuc2FjdGlvbi5vYmplY3RTdG9yZSh0aGlzLnN0b3JlTmFtZSk7XG4gICAgICAgICAgICBjb25zdCByZXF1ZXN0ID0gc3RvcmUucHV0KHNlcmlhbGl6ZWQpO1xuICAgICAgICAgICAgcmVxdWVzdC5vbnN1Y2Nlc3MgPSAoKSA9PiB7XG4gICAgICAgICAgICAgICAgLy8gRW1pdCBBVURJVF9MT0dHRUQgaW5zdGVhZCBvZiBFTlRJVFlfU0FWRURcbiAgICAgICAgICAgICAgICBjb25zdCBwYXlsb2FkID0ge1xuICAgICAgICAgICAgICAgICAgICBhdWRpdElkOiBlbnRpdHkuaWQsXG4gICAgICAgICAgICAgICAgICAgIGVudGl0eVR5cGU6IGVudGl0eS5lbnRpdHlUeXBlLFxuICAgICAgICAgICAgICAgICAgICBlbnRpdHlJZDogZW50aXR5LmVudGl0eUlkLFxuICAgICAgICAgICAgICAgICAgICBvcGVyYXRpb246IGVudGl0eS5vcGVyYXRpb24sXG4gICAgICAgICAgICAgICAgICAgIHRpbWVzdGFtcDogZW50aXR5LnRpbWVzdGFtcFxuICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgICAgdGhpcy5ldmVudEJ1cy5lbWl0KENvcmVFdmVudHMuQVVESVRfTE9HR0VELCBwYXlsb2FkKTtcbiAgICAgICAgICAgICAgICByZXNvbHZlKCk7XG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgcmVxdWVzdC5vbmVycm9yID0gKCkgPT4ge1xuICAgICAgICAgICAgICAgIHJlamVjdChuZXcgRXJyb3IoYEZhaWxlZCB0byBzYXZlIGF1ZGl0IGVudHJ5ICR7ZW50aXR5LmlkfTogJHtyZXF1ZXN0LmVycm9yfWApKTtcbiAgICAgICAgICAgIH07XG4gICAgICAgIH0pO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBPdmVycmlkZSBkZWxldGUgdG8gTk9UIHRyaWdnZXIgRU5USVRZX0RFTEVURUQgZXZlbnRcbiAgICAgKiBBdWRpdCBlbnRyaWVzIHNob3VsZCBuZXZlciBiZSBkZWxldGVkIChjb21wbGlhbmNlIHJlcXVpcmVtZW50KVxuICAgICAqL1xuICAgIGFzeW5jIGRlbGV0ZShfaWQpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdBdWRpdCBlbnRyaWVzIGNhbm5vdCBiZSBkZWxldGVkIChjb21wbGlhbmNlIHJlcXVpcmVtZW50KScpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBHZXQgcGVuZGluZyBhdWRpdCBlbnRyaWVzIChmb3Igc3luYylcbiAgICAgKi9cbiAgICBhc3luYyBnZXRQZW5kaW5nQXVkaXRzKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5nZXRCeVN5bmNTdGF0dXMoJ3BlbmRpbmcnKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogR2V0IGF1ZGl0IGVudHJpZXMgZm9yIGEgc3BlY2lmaWMgZW50aXR5XG4gICAgICovXG4gICAgYXN5bmMgZ2V0QnlFbnRpdHlJZChlbnRpdHlJZCkge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICAgICAgY29uc3QgdHJhbnNhY3Rpb24gPSB0aGlzLmRiLnRyYW5zYWN0aW9uKFt0aGlzLnN0b3JlTmFtZV0sICdyZWFkb25seScpO1xuICAgICAgICAgICAgY29uc3Qgc3RvcmUgPSB0cmFuc2FjdGlvbi5vYmplY3RTdG9yZSh0aGlzLnN0b3JlTmFtZSk7XG4gICAgICAgICAgICBjb25zdCBpbmRleCA9IHN0b3JlLmluZGV4KCdlbnRpdHlJZCcpO1xuICAgICAgICAgICAgY29uc3QgcmVxdWVzdCA9IGluZGV4LmdldEFsbChlbnRpdHlJZCk7XG4gICAgICAgICAgICByZXF1ZXN0Lm9uc3VjY2VzcyA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICBjb25zdCBlbnRyaWVzID0gcmVxdWVzdC5yZXN1bHQ7XG4gICAgICAgICAgICAgICAgcmVzb2x2ZShlbnRyaWVzKTtcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICByZXF1ZXN0Lm9uZXJyb3IgPSAoKSA9PiB7XG4gICAgICAgICAgICAgICAgcmVqZWN0KG5ldyBFcnJvcihgRmFpbGVkIHRvIGdldCBhdWRpdCBlbnRyaWVzIGZvciBlbnRpdHkgJHtlbnRpdHlJZH06ICR7cmVxdWVzdC5lcnJvcn1gKSk7XG4gICAgICAgICAgICB9O1xuICAgICAgICB9KTtcbiAgICB9XG59XG4vLyBIYXJkY29kZWQgdXNlcklkIGZvciBub3cgLSB3aWxsIGNvbWUgZnJvbSBzZXNzaW9uIGxhdGVyXG5BdWRpdFNlcnZpY2UuREVGQVVMVF9VU0VSX0lEID0gJzAwMDAwMDAwLTAwMDAtMDAwMC0wMDAwLTAwMDAwMDAwMDAwMSc7XG4iLCAiLyoqXG4gKiBNb2NrRXZlbnRSZXBvc2l0b3J5IC0gTG9hZHMgZXZlbnQgZGF0YSBmcm9tIGxvY2FsIEpTT04gZmlsZVxuICpcbiAqIFVzZWQgZm9yIGRldmVsb3BtZW50IGFuZCB0ZXN0aW5nLiBPbmx5IGZldGNoQWxsKCkgaXMgaW1wbGVtZW50ZWQuXG4gKi9cbmV4cG9ydCBjbGFzcyBNb2NrRXZlbnRSZXBvc2l0b3J5IHtcbiAgICBjb25zdHJ1Y3RvcigpIHtcbiAgICAgICAgdGhpcy5lbnRpdHlUeXBlID0gJ0V2ZW50JztcbiAgICAgICAgdGhpcy5kYXRhVXJsID0gJ2RhdGEvbW9jay1ldmVudHMuanNvbic7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEZldGNoIGFsbCBldmVudHMgZnJvbSBtb2NrIEpTT04gZmlsZVxuICAgICAqL1xuICAgIGFzeW5jIGZldGNoQWxsKCkge1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBmZXRjaCh0aGlzLmRhdGFVcmwpO1xuICAgICAgICAgICAgaWYgKCFyZXNwb25zZS5vaykge1xuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgRmFpbGVkIHRvIGxvYWQgbW9jayBldmVudHM6ICR7cmVzcG9uc2Uuc3RhdHVzfSAke3Jlc3BvbnNlLnN0YXR1c1RleHR9YCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBjb25zdCByYXdEYXRhID0gYXdhaXQgcmVzcG9uc2UuanNvbigpO1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMucHJvY2Vzc0NhbGVuZGFyRGF0YShyYXdEYXRhKTtcbiAgICAgICAgfVxuICAgICAgICBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ0ZhaWxlZCB0byBsb2FkIGV2ZW50IGRhdGE6JywgZXJyb3IpO1xuICAgICAgICAgICAgdGhyb3cgZXJyb3I7XG4gICAgICAgIH1cbiAgICB9XG4gICAgYXN5bmMgc2VuZENyZWF0ZShfZXZlbnQpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdNb2NrRXZlbnRSZXBvc2l0b3J5IGRvZXMgbm90IHN1cHBvcnQgc2VuZENyZWF0ZS4gTW9jayBkYXRhIGlzIHJlYWQtb25seS4nKTtcbiAgICB9XG4gICAgYXN5bmMgc2VuZFVwZGF0ZShfaWQsIF91cGRhdGVzKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignTW9ja0V2ZW50UmVwb3NpdG9yeSBkb2VzIG5vdCBzdXBwb3J0IHNlbmRVcGRhdGUuIE1vY2sgZGF0YSBpcyByZWFkLW9ubHkuJyk7XG4gICAgfVxuICAgIGFzeW5jIHNlbmREZWxldGUoX2lkKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignTW9ja0V2ZW50UmVwb3NpdG9yeSBkb2VzIG5vdCBzdXBwb3J0IHNlbmREZWxldGUuIE1vY2sgZGF0YSBpcyByZWFkLW9ubHkuJyk7XG4gICAgfVxuICAgIHByb2Nlc3NDYWxlbmRhckRhdGEoZGF0YSkge1xuICAgICAgICByZXR1cm4gZGF0YS5tYXAoKGV2ZW50KSA9PiB7XG4gICAgICAgICAgICAvLyBWYWxpZGF0ZSBjdXN0b21lciBldmVudCBjb25zdHJhaW50c1xuICAgICAgICAgICAgaWYgKGV2ZW50LnR5cGUgPT09ICdjdXN0b21lcicpIHtcbiAgICAgICAgICAgICAgICBpZiAoIWV2ZW50LmJvb2tpbmdJZClcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS53YXJuKGBDdXN0b21lciBldmVudCAke2V2ZW50LmlkfSBtaXNzaW5nIGJvb2tpbmdJZGApO1xuICAgICAgICAgICAgICAgIGlmICghZXZlbnQucmVzb3VyY2VJZClcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS53YXJuKGBDdXN0b21lciBldmVudCAke2V2ZW50LmlkfSBtaXNzaW5nIHJlc291cmNlSWRgKTtcbiAgICAgICAgICAgICAgICBpZiAoIWV2ZW50LmN1c3RvbWVySWQpXG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUud2FybihgQ3VzdG9tZXIgZXZlbnQgJHtldmVudC5pZH0gbWlzc2luZyBjdXN0b21lcklkYCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICAgIGlkOiBldmVudC5pZCxcbiAgICAgICAgICAgICAgICB0aXRsZTogZXZlbnQudGl0bGUsXG4gICAgICAgICAgICAgICAgZGVzY3JpcHRpb246IGV2ZW50LmRlc2NyaXB0aW9uLFxuICAgICAgICAgICAgICAgIHN0YXJ0OiBuZXcgRGF0ZShldmVudC5zdGFydCksXG4gICAgICAgICAgICAgICAgZW5kOiBuZXcgRGF0ZShldmVudC5lbmQpLFxuICAgICAgICAgICAgICAgIHR5cGU6IGV2ZW50LnR5cGUsXG4gICAgICAgICAgICAgICAgYWxsRGF5OiBldmVudC5hbGxEYXkgfHwgZmFsc2UsXG4gICAgICAgICAgICAgICAgYm9va2luZ0lkOiBldmVudC5ib29raW5nSWQsXG4gICAgICAgICAgICAgICAgcmVzb3VyY2VJZDogZXZlbnQucmVzb3VyY2VJZCxcbiAgICAgICAgICAgICAgICBjdXN0b21lcklkOiBldmVudC5jdXN0b21lcklkLFxuICAgICAgICAgICAgICAgIHJlY3VycmluZ0lkOiBldmVudC5yZWN1cnJpbmdJZCxcbiAgICAgICAgICAgICAgICBtZXRhZGF0YTogZXZlbnQubWV0YWRhdGEsXG4gICAgICAgICAgICAgICAgc3luY1N0YXR1czogJ3N5bmNlZCdcbiAgICAgICAgICAgIH07XG4gICAgICAgIH0pO1xuICAgIH1cbn1cbiIsICIvKipcbiAqIE1vY2tSZXNvdXJjZVJlcG9zaXRvcnkgLSBMb2FkcyByZXNvdXJjZSBkYXRhIGZyb20gbG9jYWwgSlNPTiBmaWxlXG4gKi9cbmV4cG9ydCBjbGFzcyBNb2NrUmVzb3VyY2VSZXBvc2l0b3J5IHtcbiAgICBjb25zdHJ1Y3RvcigpIHtcbiAgICAgICAgdGhpcy5lbnRpdHlUeXBlID0gJ1Jlc291cmNlJztcbiAgICAgICAgdGhpcy5kYXRhVXJsID0gJ2RhdGEvbW9jay1yZXNvdXJjZXMuanNvbic7XG4gICAgfVxuICAgIGFzeW5jIGZldGNoQWxsKCkge1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBmZXRjaCh0aGlzLmRhdGFVcmwpO1xuICAgICAgICAgICAgaWYgKCFyZXNwb25zZS5vaykge1xuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgRmFpbGVkIHRvIGxvYWQgbW9jayByZXNvdXJjZXM6ICR7cmVzcG9uc2Uuc3RhdHVzfSAke3Jlc3BvbnNlLnN0YXR1c1RleHR9YCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBjb25zdCByYXdEYXRhID0gYXdhaXQgcmVzcG9uc2UuanNvbigpO1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMucHJvY2Vzc1Jlc291cmNlRGF0YShyYXdEYXRhKTtcbiAgICAgICAgfVxuICAgICAgICBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ0ZhaWxlZCB0byBsb2FkIHJlc291cmNlIGRhdGE6JywgZXJyb3IpO1xuICAgICAgICAgICAgdGhyb3cgZXJyb3I7XG4gICAgICAgIH1cbiAgICB9XG4gICAgYXN5bmMgc2VuZENyZWF0ZShfcmVzb3VyY2UpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdNb2NrUmVzb3VyY2VSZXBvc2l0b3J5IGRvZXMgbm90IHN1cHBvcnQgc2VuZENyZWF0ZS4gTW9jayBkYXRhIGlzIHJlYWQtb25seS4nKTtcbiAgICB9XG4gICAgYXN5bmMgc2VuZFVwZGF0ZShfaWQsIF91cGRhdGVzKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignTW9ja1Jlc291cmNlUmVwb3NpdG9yeSBkb2VzIG5vdCBzdXBwb3J0IHNlbmRVcGRhdGUuIE1vY2sgZGF0YSBpcyByZWFkLW9ubHkuJyk7XG4gICAgfVxuICAgIGFzeW5jIHNlbmREZWxldGUoX2lkKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignTW9ja1Jlc291cmNlUmVwb3NpdG9yeSBkb2VzIG5vdCBzdXBwb3J0IHNlbmREZWxldGUuIE1vY2sgZGF0YSBpcyByZWFkLW9ubHkuJyk7XG4gICAgfVxuICAgIHByb2Nlc3NSZXNvdXJjZURhdGEoZGF0YSkge1xuICAgICAgICByZXR1cm4gZGF0YS5tYXAoKHJlc291cmNlKSA9PiAoe1xuICAgICAgICAgICAgaWQ6IHJlc291cmNlLmlkLFxuICAgICAgICAgICAgbmFtZTogcmVzb3VyY2UubmFtZSxcbiAgICAgICAgICAgIGRpc3BsYXlOYW1lOiByZXNvdXJjZS5kaXNwbGF5TmFtZSxcbiAgICAgICAgICAgIHR5cGU6IHJlc291cmNlLnR5cGUsXG4gICAgICAgICAgICBhdmF0YXJVcmw6IHJlc291cmNlLmF2YXRhclVybCxcbiAgICAgICAgICAgIGNvbG9yOiByZXNvdXJjZS5jb2xvcixcbiAgICAgICAgICAgIGlzQWN0aXZlOiByZXNvdXJjZS5pc0FjdGl2ZSxcbiAgICAgICAgICAgIGRlZmF1bHRTY2hlZHVsZTogcmVzb3VyY2UuZGVmYXVsdFNjaGVkdWxlLFxuICAgICAgICAgICAgbWV0YWRhdGE6IHJlc291cmNlLm1ldGFkYXRhLFxuICAgICAgICAgICAgc3luY1N0YXR1czogJ3N5bmNlZCdcbiAgICAgICAgfSkpO1xuICAgIH1cbn1cbiIsICIvKipcbiAqIE1vY2tCb29raW5nUmVwb3NpdG9yeSAtIExvYWRzIGJvb2tpbmcgZGF0YSBmcm9tIGxvY2FsIEpTT04gZmlsZVxuICovXG5leHBvcnQgY2xhc3MgTW9ja0Jvb2tpbmdSZXBvc2l0b3J5IHtcbiAgICBjb25zdHJ1Y3RvcigpIHtcbiAgICAgICAgdGhpcy5lbnRpdHlUeXBlID0gJ0Jvb2tpbmcnO1xuICAgICAgICB0aGlzLmRhdGFVcmwgPSAnZGF0YS9tb2NrLWJvb2tpbmdzLmpzb24nO1xuICAgIH1cbiAgICBhc3luYyBmZXRjaEFsbCgpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZmV0Y2godGhpcy5kYXRhVXJsKTtcbiAgICAgICAgICAgIGlmICghcmVzcG9uc2Uub2spIHtcbiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEZhaWxlZCB0byBsb2FkIG1vY2sgYm9va2luZ3M6ICR7cmVzcG9uc2Uuc3RhdHVzfSAke3Jlc3BvbnNlLnN0YXR1c1RleHR9YCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBjb25zdCByYXdEYXRhID0gYXdhaXQgcmVzcG9uc2UuanNvbigpO1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMucHJvY2Vzc0Jvb2tpbmdEYXRhKHJhd0RhdGEpO1xuICAgICAgICB9XG4gICAgICAgIGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgY29uc29sZS5lcnJvcignRmFpbGVkIHRvIGxvYWQgYm9va2luZyBkYXRhOicsIGVycm9yKTtcbiAgICAgICAgICAgIHRocm93IGVycm9yO1xuICAgICAgICB9XG4gICAgfVxuICAgIGFzeW5jIHNlbmRDcmVhdGUoX2Jvb2tpbmcpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdNb2NrQm9va2luZ1JlcG9zaXRvcnkgZG9lcyBub3Qgc3VwcG9ydCBzZW5kQ3JlYXRlLiBNb2NrIGRhdGEgaXMgcmVhZC1vbmx5LicpO1xuICAgIH1cbiAgICBhc3luYyBzZW5kVXBkYXRlKF9pZCwgX3VwZGF0ZXMpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdNb2NrQm9va2luZ1JlcG9zaXRvcnkgZG9lcyBub3Qgc3VwcG9ydCBzZW5kVXBkYXRlLiBNb2NrIGRhdGEgaXMgcmVhZC1vbmx5LicpO1xuICAgIH1cbiAgICBhc3luYyBzZW5kRGVsZXRlKF9pZCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ01vY2tCb29raW5nUmVwb3NpdG9yeSBkb2VzIG5vdCBzdXBwb3J0IHNlbmREZWxldGUuIE1vY2sgZGF0YSBpcyByZWFkLW9ubHkuJyk7XG4gICAgfVxuICAgIHByb2Nlc3NCb29raW5nRGF0YShkYXRhKSB7XG4gICAgICAgIHJldHVybiBkYXRhLm1hcCgoYm9va2luZykgPT4gKHtcbiAgICAgICAgICAgIGlkOiBib29raW5nLmlkLFxuICAgICAgICAgICAgY3VzdG9tZXJJZDogYm9va2luZy5jdXN0b21lcklkLFxuICAgICAgICAgICAgc3RhdHVzOiBib29raW5nLnN0YXR1cyxcbiAgICAgICAgICAgIGNyZWF0ZWRBdDogbmV3IERhdGUoYm9va2luZy5jcmVhdGVkQXQpLFxuICAgICAgICAgICAgc2VydmljZXM6IGJvb2tpbmcuc2VydmljZXMsXG4gICAgICAgICAgICB0b3RhbFByaWNlOiBib29raW5nLnRvdGFsUHJpY2UsXG4gICAgICAgICAgICB0YWdzOiBib29raW5nLnRhZ3MsXG4gICAgICAgICAgICBub3RlczogYm9va2luZy5ub3RlcyxcbiAgICAgICAgICAgIHN5bmNTdGF0dXM6ICdzeW5jZWQnXG4gICAgICAgIH0pKTtcbiAgICB9XG59XG4iLCAiLyoqXG4gKiBNb2NrQ3VzdG9tZXJSZXBvc2l0b3J5IC0gTG9hZHMgY3VzdG9tZXIgZGF0YSBmcm9tIGxvY2FsIEpTT04gZmlsZVxuICovXG5leHBvcnQgY2xhc3MgTW9ja0N1c3RvbWVyUmVwb3NpdG9yeSB7XG4gICAgY29uc3RydWN0b3IoKSB7XG4gICAgICAgIHRoaXMuZW50aXR5VHlwZSA9ICdDdXN0b21lcic7XG4gICAgICAgIHRoaXMuZGF0YVVybCA9ICdkYXRhL21vY2stY3VzdG9tZXJzLmpzb24nO1xuICAgIH1cbiAgICBhc3luYyBmZXRjaEFsbCgpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZmV0Y2godGhpcy5kYXRhVXJsKTtcbiAgICAgICAgICAgIGlmICghcmVzcG9uc2Uub2spIHtcbiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEZhaWxlZCB0byBsb2FkIG1vY2sgY3VzdG9tZXJzOiAke3Jlc3BvbnNlLnN0YXR1c30gJHtyZXNwb25zZS5zdGF0dXNUZXh0fWApO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgY29uc3QgcmF3RGF0YSA9IGF3YWl0IHJlc3BvbnNlLmpzb24oKTtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLnByb2Nlc3NDdXN0b21lckRhdGEocmF3RGF0YSk7XG4gICAgICAgIH1cbiAgICAgICAgY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKCdGYWlsZWQgdG8gbG9hZCBjdXN0b21lciBkYXRhOicsIGVycm9yKTtcbiAgICAgICAgICAgIHRocm93IGVycm9yO1xuICAgICAgICB9XG4gICAgfVxuICAgIGFzeW5jIHNlbmRDcmVhdGUoX2N1c3RvbWVyKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignTW9ja0N1c3RvbWVyUmVwb3NpdG9yeSBkb2VzIG5vdCBzdXBwb3J0IHNlbmRDcmVhdGUuIE1vY2sgZGF0YSBpcyByZWFkLW9ubHkuJyk7XG4gICAgfVxuICAgIGFzeW5jIHNlbmRVcGRhdGUoX2lkLCBfdXBkYXRlcykge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ01vY2tDdXN0b21lclJlcG9zaXRvcnkgZG9lcyBub3Qgc3VwcG9ydCBzZW5kVXBkYXRlLiBNb2NrIGRhdGEgaXMgcmVhZC1vbmx5LicpO1xuICAgIH1cbiAgICBhc3luYyBzZW5kRGVsZXRlKF9pZCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ01vY2tDdXN0b21lclJlcG9zaXRvcnkgZG9lcyBub3Qgc3VwcG9ydCBzZW5kRGVsZXRlLiBNb2NrIGRhdGEgaXMgcmVhZC1vbmx5LicpO1xuICAgIH1cbiAgICBwcm9jZXNzQ3VzdG9tZXJEYXRhKGRhdGEpIHtcbiAgICAgICAgcmV0dXJuIGRhdGEubWFwKChjdXN0b21lcikgPT4gKHtcbiAgICAgICAgICAgIGlkOiBjdXN0b21lci5pZCxcbiAgICAgICAgICAgIG5hbWU6IGN1c3RvbWVyLm5hbWUsXG4gICAgICAgICAgICBwaG9uZTogY3VzdG9tZXIucGhvbmUsXG4gICAgICAgICAgICBlbWFpbDogY3VzdG9tZXIuZW1haWwsXG4gICAgICAgICAgICBtZXRhZGF0YTogY3VzdG9tZXIubWV0YWRhdGEsXG4gICAgICAgICAgICBzeW5jU3RhdHVzOiAnc3luY2VkJ1xuICAgICAgICB9KSk7XG4gICAgfVxufVxuIiwgIi8qKlxuICogTW9ja0F1ZGl0UmVwb3NpdG9yeSAtIE1vY2sgQVBJIHJlcG9zaXRvcnkgZm9yIGF1ZGl0IGVudHJpZXNcbiAqXG4gKiBJbiBwcm9kdWN0aW9uLCB0aGlzIHdvdWxkIHNlbmQgYXVkaXQgZW50cmllcyB0byB0aGUgYmFja2VuZC5cbiAqIEZvciBkZXZlbG9wbWVudC90ZXN0aW5nLCBpdCBqdXN0IGxvZ3MgdGhlIG9wZXJhdGlvbnMuXG4gKi9cbmV4cG9ydCBjbGFzcyBNb2NrQXVkaXRSZXBvc2l0b3J5IHtcbiAgICBjb25zdHJ1Y3RvcigpIHtcbiAgICAgICAgdGhpcy5lbnRpdHlUeXBlID0gJ0F1ZGl0JztcbiAgICB9XG4gICAgYXN5bmMgc2VuZENyZWF0ZShlbnRpdHkpIHtcbiAgICAgICAgLy8gU2ltdWxhdGUgQVBJIGNhbGwgZGVsYXlcbiAgICAgICAgYXdhaXQgbmV3IFByb21pc2UocmVzb2x2ZSA9PiBzZXRUaW1lb3V0KHJlc29sdmUsIDEwMCkpO1xuICAgICAgICBjb25zb2xlLmxvZygnTW9ja0F1ZGl0UmVwb3NpdG9yeTogQXVkaXQgZW50cnkgc3luY2VkIHRvIGJhY2tlbmQ6Jywge1xuICAgICAgICAgICAgaWQ6IGVudGl0eS5pZCxcbiAgICAgICAgICAgIGVudGl0eVR5cGU6IGVudGl0eS5lbnRpdHlUeXBlLFxuICAgICAgICAgICAgZW50aXR5SWQ6IGVudGl0eS5lbnRpdHlJZCxcbiAgICAgICAgICAgIG9wZXJhdGlvbjogZW50aXR5Lm9wZXJhdGlvbixcbiAgICAgICAgICAgIHRpbWVzdGFtcDogbmV3IERhdGUoZW50aXR5LnRpbWVzdGFtcCkudG9JU09TdHJpbmcoKVxuICAgICAgICB9KTtcbiAgICAgICAgcmV0dXJuIGVudGl0eTtcbiAgICB9XG4gICAgYXN5bmMgc2VuZFVwZGF0ZShfaWQsIF9lbnRpdHkpIHtcbiAgICAgICAgLy8gQXVkaXQgZW50cmllcyBhcmUgaW1tdXRhYmxlIC0gdXBkYXRlcyBzaG91bGQgbm90IGhhcHBlblxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0F1ZGl0IGVudHJpZXMgY2Fubm90IGJlIHVwZGF0ZWQnKTtcbiAgICB9XG4gICAgYXN5bmMgc2VuZERlbGV0ZShfaWQpIHtcbiAgICAgICAgLy8gQXVkaXQgZW50cmllcyBzaG91bGQgbmV2ZXIgYmUgZGVsZXRlZFxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0F1ZGl0IGVudHJpZXMgY2Fubm90IGJlIGRlbGV0ZWQnKTtcbiAgICB9XG4gICAgYXN5bmMgZmV0Y2hBbGwoKSB7XG4gICAgICAgIC8vIEZvciBub3csIHJldHVybiBlbXB0eSBhcnJheSAtIGF1ZGl0IGVudHJpZXMgYXJlIGxvY2FsLWZpcnN0XG4gICAgICAgIC8vIEluIHByb2R1Y3Rpb24sIHRoaXMgY291bGQgZmV0Y2ggYXVkaXQgaGlzdG9yeSBmcm9tIGJhY2tlbmRcbiAgICAgICAgcmV0dXJuIFtdO1xuICAgIH1cbiAgICBhc3luYyBmZXRjaEJ5SWQoX2lkKSB7XG4gICAgICAgIC8vIEZvciBub3csIHJldHVybiBudWxsIC0gYXVkaXQgZW50cmllcyBhcmUgbG9jYWwtZmlyc3RcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxufVxuIiwgIi8qKlxuICogTW9ja1RlYW1SZXBvc2l0b3J5IC0gTG9hZHMgdGVhbSBkYXRhIGZyb20gbG9jYWwgSlNPTiBmaWxlXG4gKi9cbmV4cG9ydCBjbGFzcyBNb2NrVGVhbVJlcG9zaXRvcnkge1xuICAgIGNvbnN0cnVjdG9yKCkge1xuICAgICAgICB0aGlzLmVudGl0eVR5cGUgPSAnVGVhbSc7XG4gICAgICAgIHRoaXMuZGF0YVVybCA9ICdkYXRhL21vY2stdGVhbXMuanNvbic7XG4gICAgfVxuICAgIGFzeW5jIGZldGNoQWxsKCkge1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBmZXRjaCh0aGlzLmRhdGFVcmwpO1xuICAgICAgICAgICAgaWYgKCFyZXNwb25zZS5vaykge1xuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgRmFpbGVkIHRvIGxvYWQgbW9jayB0ZWFtczogJHtyZXNwb25zZS5zdGF0dXN9ICR7cmVzcG9uc2Uuc3RhdHVzVGV4dH1gKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNvbnN0IHJhd0RhdGEgPSBhd2FpdCByZXNwb25zZS5qc29uKCk7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5wcm9jZXNzVGVhbURhdGEocmF3RGF0YSk7XG4gICAgICAgIH1cbiAgICAgICAgY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKCdGYWlsZWQgdG8gbG9hZCB0ZWFtIGRhdGE6JywgZXJyb3IpO1xuICAgICAgICAgICAgdGhyb3cgZXJyb3I7XG4gICAgICAgIH1cbiAgICB9XG4gICAgYXN5bmMgc2VuZENyZWF0ZShfdGVhbSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ01vY2tUZWFtUmVwb3NpdG9yeSBkb2VzIG5vdCBzdXBwb3J0IHNlbmRDcmVhdGUuIE1vY2sgZGF0YSBpcyByZWFkLW9ubHkuJyk7XG4gICAgfVxuICAgIGFzeW5jIHNlbmRVcGRhdGUoX2lkLCBfdXBkYXRlcykge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ01vY2tUZWFtUmVwb3NpdG9yeSBkb2VzIG5vdCBzdXBwb3J0IHNlbmRVcGRhdGUuIE1vY2sgZGF0YSBpcyByZWFkLW9ubHkuJyk7XG4gICAgfVxuICAgIGFzeW5jIHNlbmREZWxldGUoX2lkKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignTW9ja1RlYW1SZXBvc2l0b3J5IGRvZXMgbm90IHN1cHBvcnQgc2VuZERlbGV0ZS4gTW9jayBkYXRhIGlzIHJlYWQtb25seS4nKTtcbiAgICB9XG4gICAgcHJvY2Vzc1RlYW1EYXRhKGRhdGEpIHtcbiAgICAgICAgcmV0dXJuIGRhdGEubWFwKCh0ZWFtKSA9PiAoe1xuICAgICAgICAgICAgaWQ6IHRlYW0uaWQsXG4gICAgICAgICAgICBuYW1lOiB0ZWFtLm5hbWUsXG4gICAgICAgICAgICByZXNvdXJjZUlkczogdGVhbS5yZXNvdXJjZUlkcyxcbiAgICAgICAgICAgIHN5bmNTdGF0dXM6ICdzeW5jZWQnXG4gICAgICAgIH0pKTtcbiAgICB9XG59XG4iLCAiLyoqXG4gKiBNb2NrRGVwYXJ0bWVudFJlcG9zaXRvcnkgLSBMb2FkcyBkZXBhcnRtZW50IGRhdGEgZnJvbSBsb2NhbCBKU09OIGZpbGVcbiAqL1xuZXhwb3J0IGNsYXNzIE1vY2tEZXBhcnRtZW50UmVwb3NpdG9yeSB7XG4gICAgY29uc3RydWN0b3IoKSB7XG4gICAgICAgIHRoaXMuZW50aXR5VHlwZSA9ICdEZXBhcnRtZW50JztcbiAgICAgICAgdGhpcy5kYXRhVXJsID0gJ2RhdGEvbW9jay1kZXBhcnRtZW50cy5qc29uJztcbiAgICB9XG4gICAgYXN5bmMgZmV0Y2hBbGwoKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGZldGNoKHRoaXMuZGF0YVVybCk7XG4gICAgICAgICAgICBpZiAoIXJlc3BvbnNlLm9rKSB7XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBGYWlsZWQgdG8gbG9hZCBtb2NrIGRlcGFydG1lbnRzOiAke3Jlc3BvbnNlLnN0YXR1c30gJHtyZXNwb25zZS5zdGF0dXNUZXh0fWApO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgY29uc3QgcmF3RGF0YSA9IGF3YWl0IHJlc3BvbnNlLmpzb24oKTtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLnByb2Nlc3NEZXBhcnRtZW50RGF0YShyYXdEYXRhKTtcbiAgICAgICAgfVxuICAgICAgICBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ0ZhaWxlZCB0byBsb2FkIGRlcGFydG1lbnQgZGF0YTonLCBlcnJvcik7XG4gICAgICAgICAgICB0aHJvdyBlcnJvcjtcbiAgICAgICAgfVxuICAgIH1cbiAgICBhc3luYyBzZW5kQ3JlYXRlKF9kZXBhcnRtZW50KSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignTW9ja0RlcGFydG1lbnRSZXBvc2l0b3J5IGRvZXMgbm90IHN1cHBvcnQgc2VuZENyZWF0ZS4gTW9jayBkYXRhIGlzIHJlYWQtb25seS4nKTtcbiAgICB9XG4gICAgYXN5bmMgc2VuZFVwZGF0ZShfaWQsIF91cGRhdGVzKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignTW9ja0RlcGFydG1lbnRSZXBvc2l0b3J5IGRvZXMgbm90IHN1cHBvcnQgc2VuZFVwZGF0ZS4gTW9jayBkYXRhIGlzIHJlYWQtb25seS4nKTtcbiAgICB9XG4gICAgYXN5bmMgc2VuZERlbGV0ZShfaWQpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdNb2NrRGVwYXJ0bWVudFJlcG9zaXRvcnkgZG9lcyBub3Qgc3VwcG9ydCBzZW5kRGVsZXRlLiBNb2NrIGRhdGEgaXMgcmVhZC1vbmx5LicpO1xuICAgIH1cbiAgICBwcm9jZXNzRGVwYXJ0bWVudERhdGEoZGF0YSkge1xuICAgICAgICByZXR1cm4gZGF0YS5tYXAoKGRlcHQpID0+ICh7XG4gICAgICAgICAgICBpZDogZGVwdC5pZCxcbiAgICAgICAgICAgIG5hbWU6IGRlcHQubmFtZSxcbiAgICAgICAgICAgIHJlc291cmNlSWRzOiBkZXB0LnJlc291cmNlSWRzLFxuICAgICAgICAgICAgc3luY1N0YXR1czogJ3N5bmNlZCdcbiAgICAgICAgfSkpO1xuICAgIH1cbn1cbiIsICIvKipcbiAqIE1vY2tTZXR0aW5nc1JlcG9zaXRvcnkgLSBMb2FkcyB0ZW5hbnQgc2V0dGluZ3MgZnJvbSBsb2NhbCBKU09OIGZpbGVcbiAqXG4gKiBTZXR0aW5ncyBhcmUgc3RvcmVkIGFzIHNlcGFyYXRlIHJlY29yZHMgcGVyIHNlY3Rpb24gKHdvcmt3ZWVrLCBncmlkLCBldGMuKS5cbiAqIFRoZSBKU09OIGZpbGUgaXMgYWxyZWFkeSBhbiBhcnJheSBvZiBUZW5hbnRTZXR0aW5nIHJlY29yZHMuXG4gKi9cbmV4cG9ydCBjbGFzcyBNb2NrU2V0dGluZ3NSZXBvc2l0b3J5IHtcbiAgICBjb25zdHJ1Y3RvcigpIHtcbiAgICAgICAgdGhpcy5lbnRpdHlUeXBlID0gJ1NldHRpbmdzJztcbiAgICAgICAgdGhpcy5kYXRhVXJsID0gJ2RhdGEvdGVuYW50LXNldHRpbmdzLmpzb24nO1xuICAgIH1cbiAgICBhc3luYyBmZXRjaEFsbCgpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZmV0Y2godGhpcy5kYXRhVXJsKTtcbiAgICAgICAgICAgIGlmICghcmVzcG9uc2Uub2spIHtcbiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEZhaWxlZCB0byBsb2FkIHRlbmFudCBzZXR0aW5nczogJHtyZXNwb25zZS5zdGF0dXN9ICR7cmVzcG9uc2Uuc3RhdHVzVGV4dH1gKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNvbnN0IHNldHRpbmdzID0gYXdhaXQgcmVzcG9uc2UuanNvbigpO1xuICAgICAgICAgICAgLy8gRW5zdXJlIHN5bmNTdGF0dXMgaXMgc2V0IG9uIGVhY2ggcmVjb3JkXG4gICAgICAgICAgICByZXR1cm4gc2V0dGluZ3MubWFwKHMgPT4gKHtcbiAgICAgICAgICAgICAgICAuLi5zLFxuICAgICAgICAgICAgICAgIHN5bmNTdGF0dXM6IHMuc3luY1N0YXR1cyB8fCAnc3luY2VkJ1xuICAgICAgICAgICAgfSkpO1xuICAgICAgICB9XG4gICAgICAgIGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgY29uc29sZS5lcnJvcignRmFpbGVkIHRvIGxvYWQgdGVuYW50IHNldHRpbmdzOicsIGVycm9yKTtcbiAgICAgICAgICAgIHRocm93IGVycm9yO1xuICAgICAgICB9XG4gICAgfVxuICAgIGFzeW5jIHNlbmRDcmVhdGUoX3NldHRpbmdzKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignTW9ja1NldHRpbmdzUmVwb3NpdG9yeSBkb2VzIG5vdCBzdXBwb3J0IHNlbmRDcmVhdGUuIE1vY2sgZGF0YSBpcyByZWFkLW9ubHkuJyk7XG4gICAgfVxuICAgIGFzeW5jIHNlbmRVcGRhdGUoX2lkLCBfdXBkYXRlcykge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ01vY2tTZXR0aW5nc1JlcG9zaXRvcnkgZG9lcyBub3Qgc3VwcG9ydCBzZW5kVXBkYXRlLiBNb2NrIGRhdGEgaXMgcmVhZC1vbmx5LicpO1xuICAgIH1cbiAgICBhc3luYyBzZW5kRGVsZXRlKF9pZCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ01vY2tTZXR0aW5nc1JlcG9zaXRvcnkgZG9lcyBub3Qgc3VwcG9ydCBzZW5kRGVsZXRlLiBNb2NrIGRhdGEgaXMgcmVhZC1vbmx5LicpO1xuICAgIH1cbn1cbiIsICJleHBvcnQgY2xhc3MgTW9ja1ZpZXdDb25maWdSZXBvc2l0b3J5IHtcbiAgICBjb25zdHJ1Y3RvcigpIHtcbiAgICAgICAgdGhpcy5lbnRpdHlUeXBlID0gJ1ZpZXdDb25maWcnO1xuICAgICAgICB0aGlzLmRhdGFVcmwgPSAnZGF0YS92aWV3Y29uZmlncy5qc29uJztcbiAgICB9XG4gICAgYXN5bmMgZmV0Y2hBbGwoKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGZldGNoKHRoaXMuZGF0YVVybCk7XG4gICAgICAgICAgICBpZiAoIXJlc3BvbnNlLm9rKSB7XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBGYWlsZWQgdG8gbG9hZCB2aWV3Y29uZmlnczogJHtyZXNwb25zZS5zdGF0dXN9ICR7cmVzcG9uc2Uuc3RhdHVzVGV4dH1gKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNvbnN0IHJhd0RhdGEgPSBhd2FpdCByZXNwb25zZS5qc29uKCk7XG4gICAgICAgICAgICAvLyBFbnN1cmUgc3luY1N0YXR1cyBpcyBzZXQgb24gZWFjaCBjb25maWdcbiAgICAgICAgICAgIGNvbnN0IGNvbmZpZ3MgPSByYXdEYXRhLm1hcCgoY29uZmlnKSA9PiAoe1xuICAgICAgICAgICAgICAgIC4uLmNvbmZpZyxcbiAgICAgICAgICAgICAgICBzeW5jU3RhdHVzOiBjb25maWcuc3luY1N0YXR1cyB8fCAnc3luY2VkJ1xuICAgICAgICAgICAgfSkpO1xuICAgICAgICAgICAgcmV0dXJuIGNvbmZpZ3M7XG4gICAgICAgIH1cbiAgICAgICAgY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKCdGYWlsZWQgdG8gbG9hZCB2aWV3Y29uZmlnczonLCBlcnJvcik7XG4gICAgICAgICAgICB0aHJvdyBlcnJvcjtcbiAgICAgICAgfVxuICAgIH1cbiAgICBhc3luYyBzZW5kQ3JlYXRlKF9jb25maWcpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdNb2NrVmlld0NvbmZpZ1JlcG9zaXRvcnkgZG9lcyBub3Qgc3VwcG9ydCBzZW5kQ3JlYXRlLiBNb2NrIGRhdGEgaXMgcmVhZC1vbmx5LicpO1xuICAgIH1cbiAgICBhc3luYyBzZW5kVXBkYXRlKF9pZCwgX3VwZGF0ZXMpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdNb2NrVmlld0NvbmZpZ1JlcG9zaXRvcnkgZG9lcyBub3Qgc3VwcG9ydCBzZW5kVXBkYXRlLiBNb2NrIGRhdGEgaXMgcmVhZC1vbmx5LicpO1xuICAgIH1cbiAgICBhc3luYyBzZW5kRGVsZXRlKF9pZCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ01vY2tWaWV3Q29uZmlnUmVwb3NpdG9yeSBkb2VzIG5vdCBzdXBwb3J0IHNlbmREZWxldGUuIE1vY2sgZGF0YSBpcyByZWFkLW9ubHkuJyk7XG4gICAgfVxufVxuIiwgIi8qKlxuICogRGF0YVNlZWRlciAtIE9yY2hlc3RyYXRlcyBpbml0aWFsIGRhdGEgbG9hZGluZyBmcm9tIHJlcG9zaXRvcmllcyBpbnRvIEluZGV4ZWREQlxuICpcbiAqIEFSQ0hJVEVDVFVSRTpcbiAqIC0gUmVwb3NpdG9yeSAoTW9jay9BcGkpOiBGZXRjaGVzIGRhdGEgZnJvbSBzb3VyY2UgKEpTT04gZmlsZSBvciBiYWNrZW5kIEFQSSlcbiAqIC0gRGF0YVNlZWRlciAodGhpcyBjbGFzcyk6IE9yY2hlc3RyYXRlcyBmZXRjaCArIHNhdmUgb3BlcmF0aW9uc1xuICogLSBTZXJ2aWNlIChFdmVudFNlcnZpY2UsIGV0Yy4pOiBTYXZlcyBkYXRhIHRvIEluZGV4ZWREQlxuICpcbiAqIFBPTFlNT1JQSElDIERFU0lHTjpcbiAqIC0gVXNlcyBhcnJheXMgb2YgSUVudGl0eVNlcnZpY2VbXSBhbmQgSUFwaVJlcG9zaXRvcnlbXVxuICogLSBNYXRjaGVzIHNlcnZpY2VzIHdpdGggcmVwb3NpdG9yaWVzIHVzaW5nIGVudGl0eVR5cGUgcHJvcGVydHlcbiAqIC0gT3Blbi9DbG9zZWQgUHJpbmNpcGxlOiBBZGRpbmcgbmV3IGVudGl0eSByZXF1aXJlcyBubyBjb2RlIGNoYW5nZXMgaGVyZVxuICovXG5leHBvcnQgY2xhc3MgRGF0YVNlZWRlciB7XG4gICAgY29uc3RydWN0b3Ioc2VydmljZXMsIHJlcG9zaXRvcmllcykge1xuICAgICAgICB0aGlzLnNlcnZpY2VzID0gc2VydmljZXM7XG4gICAgICAgIHRoaXMucmVwb3NpdG9yaWVzID0gcmVwb3NpdG9yaWVzO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBTZWVkIGFsbCBlbnRpdHkgc3RvcmVzIGlmIHRoZXkgYXJlIGVtcHR5XG4gICAgICovXG4gICAgYXN5bmMgc2VlZElmRW1wdHkoKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKCdbRGF0YVNlZWRlcl0gQ2hlY2tpbmcgaWYgZGF0YWJhc2UgbmVlZHMgc2VlZGluZy4uLicpO1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgZm9yIChjb25zdCBzZXJ2aWNlIG9mIHRoaXMuc2VydmljZXMpIHtcbiAgICAgICAgICAgICAgICBjb25zdCByZXBvc2l0b3J5ID0gdGhpcy5yZXBvc2l0b3JpZXMuZmluZChyZXBvID0+IHJlcG8uZW50aXR5VHlwZSA9PT0gc2VydmljZS5lbnRpdHlUeXBlKTtcbiAgICAgICAgICAgICAgICBpZiAoIXJlcG9zaXRvcnkpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS53YXJuKGBbRGF0YVNlZWRlcl0gTm8gcmVwb3NpdG9yeSBmb3VuZCBmb3IgZW50aXR5IHR5cGU6ICR7c2VydmljZS5lbnRpdHlUeXBlfSwgc2tpcHBpbmdgKTtcbiAgICAgICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGF3YWl0IHRoaXMuc2VlZEVudGl0eShzZXJ2aWNlLmVudGl0eVR5cGUsIHNlcnZpY2UsIHJlcG9zaXRvcnkpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgY29uc29sZS5sb2coJ1tEYXRhU2VlZGVyXSBTZWVkaW5nIGNvbXBsZXRlJyk7XG4gICAgICAgIH1cbiAgICAgICAgY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKCdbRGF0YVNlZWRlcl0gU2VlZGluZyBmYWlsZWQ6JywgZXJyb3IpO1xuICAgICAgICAgICAgdGhyb3cgZXJyb3I7XG4gICAgICAgIH1cbiAgICB9XG4gICAgYXN5bmMgc2VlZEVudGl0eShlbnRpdHlUeXBlLCBzZXJ2aWNlLCByZXBvc2l0b3J5KSB7XG4gICAgICAgIGNvbnN0IGV4aXN0aW5nID0gYXdhaXQgc2VydmljZS5nZXRBbGwoKTtcbiAgICAgICAgaWYgKGV4aXN0aW5nLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKGBbRGF0YVNlZWRlcl0gJHtlbnRpdHlUeXBlfSBzdG9yZSBhbHJlYWR5IGhhcyAke2V4aXN0aW5nLmxlbmd0aH0gaXRlbXMsIHNraXBwaW5nIHNlZWRgKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBjb25zb2xlLmxvZyhgW0RhdGFTZWVkZXJdICR7ZW50aXR5VHlwZX0gc3RvcmUgaXMgZW1wdHksIGZldGNoaW5nIGZyb20gcmVwb3NpdG9yeS4uLmApO1xuICAgICAgICBjb25zdCBkYXRhID0gYXdhaXQgcmVwb3NpdG9yeS5mZXRjaEFsbCgpO1xuICAgICAgICBjb25zb2xlLmxvZyhgW0RhdGFTZWVkZXJdIEZldGNoZWQgJHtkYXRhLmxlbmd0aH0gJHtlbnRpdHlUeXBlfSBpdGVtcywgc2F2aW5nIHRvIEluZGV4ZWREQi4uLmApO1xuICAgICAgICBmb3IgKGNvbnN0IGVudGl0eSBvZiBkYXRhKSB7XG4gICAgICAgICAgICBhd2FpdCBzZXJ2aWNlLnNhdmUoZW50aXR5LCB0cnVlKTsgLy8gc2lsZW50ID0gdHJ1ZSB0byBza2lwIGF1ZGl0IGxvZ2dpbmdcbiAgICAgICAgfVxuICAgICAgICBjb25zb2xlLmxvZyhgW0RhdGFTZWVkZXJdICR7ZW50aXR5VHlwZX0gc2VlZGluZyBjb21wbGV0ZSAoJHtkYXRhLmxlbmd0aH0gaXRlbXMgc2F2ZWQpYCk7XG4gICAgfVxufVxuIiwgIi8qKlxuICogQ2FsY3VsYXRlIHBpeGVsIHBvc2l0aW9uIGZvciBhbiBldmVudCBiYXNlZCBvbiBpdHMgdGltZXNcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNhbGN1bGF0ZUV2ZW50UG9zaXRpb24oc3RhcnQsIGVuZCwgY29uZmlnKSB7XG4gICAgY29uc3Qgc3RhcnRNaW51dGVzID0gc3RhcnQuZ2V0SG91cnMoKSAqIDYwICsgc3RhcnQuZ2V0TWludXRlcygpO1xuICAgIGNvbnN0IGVuZE1pbnV0ZXMgPSBlbmQuZ2V0SG91cnMoKSAqIDYwICsgZW5kLmdldE1pbnV0ZXMoKTtcbiAgICBjb25zdCBkYXlTdGFydE1pbnV0ZXMgPSBjb25maWcuZGF5U3RhcnRIb3VyICogNjA7XG4gICAgY29uc3QgbWludXRlSGVpZ2h0ID0gY29uZmlnLmhvdXJIZWlnaHQgLyA2MDtcbiAgICBjb25zdCB0b3AgPSAoc3RhcnRNaW51dGVzIC0gZGF5U3RhcnRNaW51dGVzKSAqIG1pbnV0ZUhlaWdodDtcbiAgICBjb25zdCBoZWlnaHQgPSAoZW5kTWludXRlcyAtIHN0YXJ0TWludXRlcykgKiBtaW51dGVIZWlnaHQ7XG4gICAgcmV0dXJuIHsgdG9wLCBoZWlnaHQgfTtcbn1cbi8qKlxuICogQ29udmVydCBtaW51dGVzIHRvIHBpeGVsc1xuICovXG5leHBvcnQgZnVuY3Rpb24gbWludXRlc1RvUGl4ZWxzKG1pbnV0ZXMsIGNvbmZpZykge1xuICAgIHJldHVybiAobWludXRlcyAvIDYwKSAqIGNvbmZpZy5ob3VySGVpZ2h0O1xufVxuLyoqXG4gKiBDb252ZXJ0IHBpeGVscyB0byBtaW51dGVzXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBwaXhlbHNUb01pbnV0ZXMocGl4ZWxzLCBjb25maWcpIHtcbiAgICByZXR1cm4gKHBpeGVscyAvIGNvbmZpZy5ob3VySGVpZ2h0KSAqIDYwO1xufVxuLyoqXG4gKiBTbmFwIHBpeGVsIHBvc2l0aW9uIHRvIGdyaWQgaW50ZXJ2YWxcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNuYXBUb0dyaWQocGl4ZWxzLCBjb25maWcpIHtcbiAgICBjb25zdCBzbmFwUGl4ZWxzID0gbWludXRlc1RvUGl4ZWxzKGNvbmZpZy5zbmFwSW50ZXJ2YWwsIGNvbmZpZyk7XG4gICAgcmV0dXJuIE1hdGgucm91bmQocGl4ZWxzIC8gc25hcFBpeGVscykgKiBzbmFwUGl4ZWxzO1xufVxuIiwgImltcG9ydCB7IGNhbGN1bGF0ZUV2ZW50UG9zaXRpb24gfSBmcm9tICcuLi8uLi91dGlscy9Qb3NpdGlvblV0aWxzJztcbi8qKlxuICogQ2hlY2sgaWYgdHdvIGV2ZW50cyBvdmVybGFwIChzdHJpY3QgLSB0b3VjaGluZyBhdCBib3VuZGFyeSA9IE5PVCBvdmVybGFwcGluZylcbiAqIFRoaXMgbWF0Y2hlcyBTY2VuYXJpbyA4OiBlbmQ9PT1zdGFydCBpcyBOT1Qgb3ZlcmxhcFxuICovXG5leHBvcnQgZnVuY3Rpb24gZXZlbnRzT3ZlcmxhcChhLCBiKSB7XG4gICAgcmV0dXJuIGEuc3RhcnQgPCBiLmVuZCAmJiBhLmVuZCA+IGIuc3RhcnQ7XG59XG4vKipcbiAqIENoZWNrIGlmIHR3byBldmVudHMgYXJlIHdpdGhpbiB0aHJlc2hvbGQgZm9yIGdyaWQgZ3JvdXBpbmcuXG4gKiBUaGlzIGluY2x1ZGVzOlxuICogMS4gU3RhcnQtdG8tc3RhcnQ6IEV2ZW50cyBzdGFydCB3aXRoaW4gdGhyZXNob2xkIG9mIGVhY2ggb3RoZXJcbiAqIDIuIEVuZC10by1zdGFydDogT25lIGV2ZW50IHN0YXJ0cyB3aXRoaW4gdGhyZXNob2xkIGJlZm9yZSBhbm90aGVyIGVuZHNcbiAqL1xuZnVuY3Rpb24gZXZlbnRzV2l0aGluVGhyZXNob2xkKGEsIGIsIHRocmVzaG9sZE1pbnV0ZXMpIHtcbiAgICBjb25zdCB0aHJlc2hvbGRNcyA9IHRocmVzaG9sZE1pbnV0ZXMgKiA2MCAqIDEwMDA7XG4gICAgLy8gU3RhcnQtdG8tc3RhcnQ6IGJvdGggZXZlbnRzIHN0YXJ0IHdpdGhpbiB0aHJlc2hvbGRcbiAgICBjb25zdCBzdGFydFRvU3RhcnREaWZmID0gTWF0aC5hYnMoYS5zdGFydC5nZXRUaW1lKCkgLSBiLnN0YXJ0LmdldFRpbWUoKSk7XG4gICAgaWYgKHN0YXJ0VG9TdGFydERpZmYgPD0gdGhyZXNob2xkTXMpXG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgIC8vIEVuZC10by1zdGFydDogb25lIGV2ZW50IHN0YXJ0cyB3aXRoaW4gdGhyZXNob2xkIGJlZm9yZSB0aGUgb3RoZXIgZW5kc1xuICAgIC8vIEIgc3RhcnRzIHdpdGhpbiB0aHJlc2hvbGQgYmVmb3JlIEEgZW5kc1xuICAgIGNvbnN0IGJTdGFydHNCZWZvcmVBRW5kcyA9IGEuZW5kLmdldFRpbWUoKSAtIGIuc3RhcnQuZ2V0VGltZSgpO1xuICAgIGlmIChiU3RhcnRzQmVmb3JlQUVuZHMgPiAwICYmIGJTdGFydHNCZWZvcmVBRW5kcyA8PSB0aHJlc2hvbGRNcylcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgLy8gQSBzdGFydHMgd2l0aGluIHRocmVzaG9sZCBiZWZvcmUgQiBlbmRzXG4gICAgY29uc3QgYVN0YXJ0c0JlZm9yZUJFbmRzID0gYi5lbmQuZ2V0VGltZSgpIC0gYS5zdGFydC5nZXRUaW1lKCk7XG4gICAgaWYgKGFTdGFydHNCZWZvcmVCRW5kcyA+IDAgJiYgYVN0YXJ0c0JlZm9yZUJFbmRzIDw9IHRocmVzaG9sZE1zKVxuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICByZXR1cm4gZmFsc2U7XG59XG4vKipcbiAqIENoZWNrIGlmIGFsbCBldmVudHMgaW4gYSBncm91cCBzdGFydCB3aXRoaW4gdGhyZXNob2xkIG9mIGVhY2ggb3RoZXJcbiAqL1xuZnVuY3Rpb24gYWxsU3RhcnRXaXRoaW5UaHJlc2hvbGQoZXZlbnRzLCB0aHJlc2hvbGRNaW51dGVzKSB7XG4gICAgaWYgKGV2ZW50cy5sZW5ndGggPD0gMSlcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgLy8gRmluZCBlYXJsaWVzdCBhbmQgbGF0ZXN0IHN0YXJ0IHRpbWVzXG4gICAgbGV0IGVhcmxpZXN0ID0gZXZlbnRzWzBdLnN0YXJ0LmdldFRpbWUoKTtcbiAgICBsZXQgbGF0ZXN0ID0gZXZlbnRzWzBdLnN0YXJ0LmdldFRpbWUoKTtcbiAgICBmb3IgKGNvbnN0IGV2ZW50IG9mIGV2ZW50cykge1xuICAgICAgICBjb25zdCB0aW1lID0gZXZlbnQuc3RhcnQuZ2V0VGltZSgpO1xuICAgICAgICBpZiAodGltZSA8IGVhcmxpZXN0KVxuICAgICAgICAgICAgZWFybGllc3QgPSB0aW1lO1xuICAgICAgICBpZiAodGltZSA+IGxhdGVzdClcbiAgICAgICAgICAgIGxhdGVzdCA9IHRpbWU7XG4gICAgfVxuICAgIGNvbnN0IGRpZmZNaW51dGVzID0gKGxhdGVzdCAtIGVhcmxpZXN0KSAvICgxMDAwICogNjApO1xuICAgIHJldHVybiBkaWZmTWludXRlcyA8PSB0aHJlc2hvbGRNaW51dGVzO1xufVxuLyoqXG4gKiBGaW5kIGdyb3VwcyBvZiBvdmVybGFwcGluZyBldmVudHMgKGNvbm5lY3RlZCBieSBvdmVybGFwIGNoYWluKVxuICogRXZlbnRzIGFyZSBncm91cGVkIGlmIHRoZXkgb3ZlcmxhcCB3aXRoIGFueSBldmVudCBpbiB0aGUgZ3JvdXBcbiAqL1xuZnVuY3Rpb24gZmluZE92ZXJsYXBHcm91cHMoZXZlbnRzKSB7XG4gICAgaWYgKGV2ZW50cy5sZW5ndGggPT09IDApXG4gICAgICAgIHJldHVybiBbXTtcbiAgICBjb25zdCBzb3J0ZWQgPSBbLi4uZXZlbnRzXS5zb3J0KChhLCBiKSA9PiBhLnN0YXJ0LmdldFRpbWUoKSAtIGIuc3RhcnQuZ2V0VGltZSgpKTtcbiAgICBjb25zdCB1c2VkID0gbmV3IFNldCgpO1xuICAgIGNvbnN0IGdyb3VwcyA9IFtdO1xuICAgIGZvciAoY29uc3QgZXZlbnQgb2Ygc29ydGVkKSB7XG4gICAgICAgIGlmICh1c2VkLmhhcyhldmVudC5pZCkpXG4gICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgLy8gU3RhcnQgYSBuZXcgZ3JvdXAgd2l0aCB0aGlzIGV2ZW50XG4gICAgICAgIGNvbnN0IGdyb3VwID0gW2V2ZW50XTtcbiAgICAgICAgdXNlZC5hZGQoZXZlbnQuaWQpO1xuICAgICAgICAvLyBFeHBhbmQgZ3JvdXAgYnkgZmluZGluZyBhbGwgY29ubmVjdGVkIGV2ZW50cyAodmlhIG92ZXJsYXApXG4gICAgICAgIGxldCBleHBhbmRlZCA9IHRydWU7XG4gICAgICAgIHdoaWxlIChleHBhbmRlZCkge1xuICAgICAgICAgICAgZXhwYW5kZWQgPSBmYWxzZTtcbiAgICAgICAgICAgIGZvciAoY29uc3QgY2FuZGlkYXRlIG9mIHNvcnRlZCkge1xuICAgICAgICAgICAgICAgIGlmICh1c2VkLmhhcyhjYW5kaWRhdGUuaWQpKVxuICAgICAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgICAgICAvLyBDaGVjayBpZiBjYW5kaWRhdGUgb3ZlcmxhcHMgd2l0aCBhbnkgZXZlbnQgaW4gZ3JvdXBcbiAgICAgICAgICAgICAgICBjb25zdCBjb25uZWN0cyA9IGdyb3VwLnNvbWUobWVtYmVyID0+IGV2ZW50c092ZXJsYXAobWVtYmVyLCBjYW5kaWRhdGUpKTtcbiAgICAgICAgICAgICAgICBpZiAoY29ubmVjdHMpIHtcbiAgICAgICAgICAgICAgICAgICAgZ3JvdXAucHVzaChjYW5kaWRhdGUpO1xuICAgICAgICAgICAgICAgICAgICB1c2VkLmFkZChjYW5kaWRhdGUuaWQpO1xuICAgICAgICAgICAgICAgICAgICBleHBhbmRlZCA9IHRydWU7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGdyb3Vwcy5wdXNoKGdyb3VwKTtcbiAgICB9XG4gICAgcmV0dXJuIGdyb3Vwcztcbn1cbi8qKlxuICogRmluZCBncmlkIGNhbmRpZGF0ZXMgd2l0aGluIGEgZ3JvdXAgLSBldmVudHMgY29ubmVjdGVkIHZpYSB0aHJlc2hvbGQgY2hhaW5cbiAqIFVzZXMgVjEgbG9naWM6IGV2ZW50cyBhcmUgY29ubmVjdGVkIGlmIHdpdGhpbiB0aHJlc2hvbGQgKG5vIG92ZXJsYXAgcmVxdWlyZW1lbnQpXG4gKi9cbmZ1bmN0aW9uIGZpbmRHcmlkQ2FuZGlkYXRlcyhldmVudHMsIHRocmVzaG9sZE1pbnV0ZXMpIHtcbiAgICBpZiAoZXZlbnRzLmxlbmd0aCA9PT0gMClcbiAgICAgICAgcmV0dXJuIFtdO1xuICAgIGNvbnN0IHNvcnRlZCA9IFsuLi5ldmVudHNdLnNvcnQoKGEsIGIpID0+IGEuc3RhcnQuZ2V0VGltZSgpIC0gYi5zdGFydC5nZXRUaW1lKCkpO1xuICAgIGNvbnN0IHVzZWQgPSBuZXcgU2V0KCk7XG4gICAgY29uc3QgZ3JvdXBzID0gW107XG4gICAgZm9yIChjb25zdCBldmVudCBvZiBzb3J0ZWQpIHtcbiAgICAgICAgaWYgKHVzZWQuaGFzKGV2ZW50LmlkKSlcbiAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICBjb25zdCBncm91cCA9IFtldmVudF07XG4gICAgICAgIHVzZWQuYWRkKGV2ZW50LmlkKTtcbiAgICAgICAgLy8gRXhwYW5kIGJ5IHRocmVzaG9sZCBjaGFpbiAoVjEgbG9naWM6IG5vIG92ZXJsYXAgcmVxdWlyZW1lbnQsIGp1c3QgdGhyZXNob2xkKVxuICAgICAgICBsZXQgZXhwYW5kZWQgPSB0cnVlO1xuICAgICAgICB3aGlsZSAoZXhwYW5kZWQpIHtcbiAgICAgICAgICAgIGV4cGFuZGVkID0gZmFsc2U7XG4gICAgICAgICAgICBmb3IgKGNvbnN0IGNhbmRpZGF0ZSBvZiBzb3J0ZWQpIHtcbiAgICAgICAgICAgICAgICBpZiAodXNlZC5oYXMoY2FuZGlkYXRlLmlkKSlcbiAgICAgICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICAgICAgY29uc3QgY29ubmVjdHMgPSBncm91cC5zb21lKG1lbWJlciA9PiBldmVudHNXaXRoaW5UaHJlc2hvbGQobWVtYmVyLCBjYW5kaWRhdGUsIHRocmVzaG9sZE1pbnV0ZXMpKTtcbiAgICAgICAgICAgICAgICBpZiAoY29ubmVjdHMpIHtcbiAgICAgICAgICAgICAgICAgICAgZ3JvdXAucHVzaChjYW5kaWRhdGUpO1xuICAgICAgICAgICAgICAgICAgICB1c2VkLmFkZChjYW5kaWRhdGUuaWQpO1xuICAgICAgICAgICAgICAgICAgICBleHBhbmRlZCA9IHRydWU7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGdyb3Vwcy5wdXNoKGdyb3VwKTtcbiAgICB9XG4gICAgcmV0dXJuIGdyb3Vwcztcbn1cbi8qKlxuICogQ2FsY3VsYXRlIHN0YWNrIGxldmVscyBmb3Igb3ZlcmxhcHBpbmcgZXZlbnRzIHVzaW5nIGdyZWVkeSBhbGdvcml0aG1cbiAqIEZvciBlYWNoIGV2ZW50OiBsZXZlbCA9IG1heChvdmVybGFwcGluZyBhbHJlYWR5LXByb2Nlc3NlZCBldmVudHMpICsgMVxuICovXG5mdW5jdGlvbiBjYWxjdWxhdGVTdGFja0xldmVscyhldmVudHMpIHtcbiAgICBjb25zdCBsZXZlbHMgPSBuZXcgTWFwKCk7XG4gICAgY29uc3Qgc29ydGVkID0gWy4uLmV2ZW50c10uc29ydCgoYSwgYikgPT4gYS5zdGFydC5nZXRUaW1lKCkgLSBiLnN0YXJ0LmdldFRpbWUoKSk7XG4gICAgZm9yIChjb25zdCBldmVudCBvZiBzb3J0ZWQpIHtcbiAgICAgICAgbGV0IG1heE92ZXJsYXBwaW5nTGV2ZWwgPSAtMTtcbiAgICAgICAgLy8gRmluZCBtYXggbGV2ZWwgYW1vbmcgb3ZlcmxhcHBpbmcgZXZlbnRzIGFscmVhZHkgcHJvY2Vzc2VkXG4gICAgICAgIGZvciAoY29uc3QgW2lkLCBsZXZlbF0gb2YgbGV2ZWxzKSB7XG4gICAgICAgICAgICBjb25zdCBvdGhlciA9IGV2ZW50cy5maW5kKGUgPT4gZS5pZCA9PT0gaWQpO1xuICAgICAgICAgICAgaWYgKG90aGVyICYmIGV2ZW50c092ZXJsYXAoZXZlbnQsIG90aGVyKSkge1xuICAgICAgICAgICAgICAgIG1heE92ZXJsYXBwaW5nTGV2ZWwgPSBNYXRoLm1heChtYXhPdmVybGFwcGluZ0xldmVsLCBsZXZlbCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgbGV2ZWxzLnNldChldmVudC5pZCwgbWF4T3ZlcmxhcHBpbmdMZXZlbCArIDEpO1xuICAgIH1cbiAgICByZXR1cm4gbGV2ZWxzO1xufVxuLyoqXG4gKiBBbGxvY2F0ZSBldmVudHMgdG8gY29sdW1ucyBmb3IgR1JJRCBsYXlvdXQgdXNpbmcgZ3JlZWR5IGFsZ29yaXRobVxuICogTm9uLW92ZXJsYXBwaW5nIGV2ZW50cyBjYW4gc2hhcmUgYSBjb2x1bW4gdG8gbWluaW1pemUgdG90YWwgY29sdW1uc1xuICovXG5mdW5jdGlvbiBhbGxvY2F0ZUNvbHVtbnMoZXZlbnRzKSB7XG4gICAgY29uc3Qgc29ydGVkID0gWy4uLmV2ZW50c10uc29ydCgoYSwgYikgPT4gYS5zdGFydC5nZXRUaW1lKCkgLSBiLnN0YXJ0LmdldFRpbWUoKSk7XG4gICAgY29uc3QgY29sdW1ucyA9IFtdO1xuICAgIGZvciAoY29uc3QgZXZlbnQgb2Ygc29ydGVkKSB7XG4gICAgICAgIC8vIEZpbmQgZmlyc3QgY29sdW1uIHdoZXJlIGV2ZW50IGRvZXNuJ3Qgb3ZlcmxhcCB3aXRoIGV4aXN0aW5nIGV2ZW50c1xuICAgICAgICBsZXQgcGxhY2VkID0gZmFsc2U7XG4gICAgICAgIGZvciAoY29uc3QgY29sdW1uIG9mIGNvbHVtbnMpIHtcbiAgICAgICAgICAgIGNvbnN0IGNhbkZpdCA9ICFjb2x1bW4uc29tZShlID0+IGV2ZW50c092ZXJsYXAoZXZlbnQsIGUpKTtcbiAgICAgICAgICAgIGlmIChjYW5GaXQpIHtcbiAgICAgICAgICAgICAgICBjb2x1bW4ucHVzaChldmVudCk7XG4gICAgICAgICAgICAgICAgcGxhY2VkID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICAvLyBObyBzdWl0YWJsZSBjb2x1bW4gZm91bmQsIGNyZWF0ZSBuZXcgb25lXG4gICAgICAgIGlmICghcGxhY2VkKSB7XG4gICAgICAgICAgICBjb2x1bW5zLnB1c2goW2V2ZW50XSk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGNvbHVtbnM7XG59XG4vKipcbiAqIE1haW4gZW50cnkgcG9pbnQ6IENhbGN1bGF0ZSBjb21wbGV0ZSBsYXlvdXQgZm9yIGEgY29sdW1uJ3MgZXZlbnRzXG4gKlxuICogQWxnb3JpdGhtOlxuICogMS4gRmluZCBvdmVybGFwIGdyb3VwcyAoZXZlbnRzIGNvbm5lY3RlZCBieSBvdmVybGFwIGNoYWluKVxuICogMi4gRm9yIGVhY2ggb3ZlcmxhcCBncm91cCwgZmluZCBncmlkIGNhbmRpZGF0ZXMgKGV2ZW50cyB3aXRoaW4gdGhyZXNob2xkIGNoYWluKVxuICogMy4gSWYgYWxsIGV2ZW50cyBpbiBvdmVybGFwIGdyb3VwIGZvcm0gYSBzaW5nbGUgZ3JpZCBjYW5kaWRhdGUgXHUyMTkyIEdSSUQgbW9kZVxuICogNC4gT3RoZXJ3aXNlIFx1MjE5MiBTVEFDS0lORyBtb2RlIHdpdGggY2FsY3VsYXRlZCBsZXZlbHNcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNhbGN1bGF0ZUNvbHVtbkxheW91dChldmVudHMsIGNvbmZpZykge1xuICAgIGNvbnN0IHRocmVzaG9sZE1pbnV0ZXMgPSBjb25maWcuZ3JpZFN0YXJ0VGhyZXNob2xkTWludXRlcyA/PyAxMDtcbiAgICBjb25zdCByZXN1bHQgPSB7XG4gICAgICAgIGdyaWRzOiBbXSxcbiAgICAgICAgc3RhY2tlZDogW11cbiAgICB9O1xuICAgIGlmIChldmVudHMubGVuZ3RoID09PSAwKVxuICAgICAgICByZXR1cm4gcmVzdWx0O1xuICAgIC8vIEZpbmQgYWxsIG92ZXJsYXBwaW5nIGV2ZW50IGdyb3Vwc1xuICAgIGNvbnN0IG92ZXJsYXBHcm91cHMgPSBmaW5kT3ZlcmxhcEdyb3VwcyhldmVudHMpO1xuICAgIGZvciAoY29uc3Qgb3ZlcmxhcEdyb3VwIG9mIG92ZXJsYXBHcm91cHMpIHtcbiAgICAgICAgaWYgKG92ZXJsYXBHcm91cC5sZW5ndGggPT09IDEpIHtcbiAgICAgICAgICAgIC8vIFNpbmdsZSBldmVudCAtIG5vIGdyb3VwaW5nIG5lZWRlZFxuICAgICAgICAgICAgcmVzdWx0LnN0YWNrZWQucHVzaCh7XG4gICAgICAgICAgICAgICAgZXZlbnQ6IG92ZXJsYXBHcm91cFswXSxcbiAgICAgICAgICAgICAgICBzdGFja0xldmVsOiAwXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG4gICAgICAgIC8vIFdpdGhpbiB0aGlzIG92ZXJsYXAgZ3JvdXAsIGZpbmQgZ3JpZCBjYW5kaWRhdGVzICh0aHJlc2hvbGQtY29ubmVjdGVkIHN1Ymdyb3VwcylcbiAgICAgICAgY29uc3QgZ3JpZFN1Ymdyb3VwcyA9IGZpbmRHcmlkQ2FuZGlkYXRlcyhvdmVybGFwR3JvdXAsIHRocmVzaG9sZE1pbnV0ZXMpO1xuICAgICAgICAvLyBDaGVjayBpZiB0aGUgRU5USVJFIG92ZXJsYXAgZ3JvdXAgZm9ybXMgYSBzaW5nbGUgZ3JpZCBjYW5kaWRhdGVcbiAgICAgICAgLy8gVGhpcyBoYXBwZW5zIHdoZW4gYWxsIGV2ZW50cyBhcmUgY29ubmVjdGVkIHZpYSB0aHJlc2hvbGQgY2hhaW5cbiAgICAgICAgY29uc3QgbGFyZ2VzdEdyaWRDYW5kaWRhdGUgPSBncmlkU3ViZ3JvdXBzLnJlZHVjZSgobWF4LCBnKSA9PiBnLmxlbmd0aCA+IG1heC5sZW5ndGggPyBnIDogbWF4LCBncmlkU3ViZ3JvdXBzWzBdKTtcbiAgICAgICAgaWYgKGxhcmdlc3RHcmlkQ2FuZGlkYXRlLmxlbmd0aCA9PT0gb3ZlcmxhcEdyb3VwLmxlbmd0aCkge1xuICAgICAgICAgICAgLy8gQWxsIGV2ZW50cyBpbiBvdmVybGFwIGdyb3VwIGFyZSBjb25uZWN0ZWQgdmlhIHRocmVzaG9sZCBjaGFpbiBcdTIxOTIgR1JJRCBtb2RlXG4gICAgICAgICAgICBjb25zdCBjb2x1bW5zID0gYWxsb2NhdGVDb2x1bW5zKG92ZXJsYXBHcm91cCk7XG4gICAgICAgICAgICBjb25zdCBlYXJsaWVzdCA9IG92ZXJsYXBHcm91cC5yZWR1Y2UoKG1pbiwgZSkgPT4gZS5zdGFydCA8IG1pbi5zdGFydCA/IGUgOiBtaW4sIG92ZXJsYXBHcm91cFswXSk7XG4gICAgICAgICAgICBjb25zdCBwb3NpdGlvbiA9IGNhbGN1bGF0ZUV2ZW50UG9zaXRpb24oZWFybGllc3Quc3RhcnQsIGVhcmxpZXN0LmVuZCwgY29uZmlnKTtcbiAgICAgICAgICAgIHJlc3VsdC5ncmlkcy5wdXNoKHtcbiAgICAgICAgICAgICAgICBldmVudHM6IG92ZXJsYXBHcm91cCxcbiAgICAgICAgICAgICAgICBjb2x1bW5zLFxuICAgICAgICAgICAgICAgIHN0YWNrTGV2ZWw6IDAsXG4gICAgICAgICAgICAgICAgcG9zaXRpb246IHsgdG9wOiBwb3NpdGlvbi50b3AgfVxuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAvLyBOb3QgYWxsIGV2ZW50cyBjb25uZWN0ZWQgdmlhIHRocmVzaG9sZCBcdTIxOTIgU1RBQ0tJTkcgbW9kZVxuICAgICAgICAgICAgY29uc3QgbGV2ZWxzID0gY2FsY3VsYXRlU3RhY2tMZXZlbHMob3ZlcmxhcEdyb3VwKTtcbiAgICAgICAgICAgIGZvciAoY29uc3QgZXZlbnQgb2Ygb3ZlcmxhcEdyb3VwKSB7XG4gICAgICAgICAgICAgICAgcmVzdWx0LnN0YWNrZWQucHVzaCh7XG4gICAgICAgICAgICAgICAgICAgIGV2ZW50LFxuICAgICAgICAgICAgICAgICAgICBzdGFja0xldmVsOiBsZXZlbHMuZ2V0KGV2ZW50LmlkKSA/PyAwXG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHJlc3VsdDtcbn1cbiIsICJpbXBvcnQgeyBjYWxjdWxhdGVFdmVudFBvc2l0aW9uLCBzbmFwVG9HcmlkLCBwaXhlbHNUb01pbnV0ZXMgfSBmcm9tICcuLi8uLi91dGlscy9Qb3NpdGlvblV0aWxzJztcbmltcG9ydCB7IENvcmVFdmVudHMgfSBmcm9tICcuLi8uLi9jb25zdGFudHMvQ29yZUV2ZW50cyc7XG5pbXBvcnQgeyBjYWxjdWxhdGVDb2x1bW5MYXlvdXQgfSBmcm9tICcuL0V2ZW50TGF5b3V0RW5naW5lJztcbi8qKlxuICogRXZlbnRSZW5kZXJlciAtIFJlbmRlcnMgY2FsZW5kYXIgZXZlbnRzIHRvIHRoZSBET01cbiAqXG4gKiBDTEVBTiBhcHByb2FjaDpcbiAqIC0gT25seSBkYXRhLWlkIGF0dHJpYnV0ZSBvbiBldmVudCBlbGVtZW50XG4gKiAtIGlubmVySFRNTCBjb250YWlucyBvbmx5IHZpc2libGUgY29udGVudFxuICogLSBFdmVudCBkYXRhIHJldHJpZXZlZCB2aWEgRXZlbnRTZXJ2aWNlIHdoZW4gbmVlZGVkXG4gKi9cbmV4cG9ydCBjbGFzcyBFdmVudFJlbmRlcmVyIHtcbiAgICBjb25zdHJ1Y3RvcihldmVudFNlcnZpY2UsIGRhdGVTZXJ2aWNlLCBncmlkQ29uZmlnLCBldmVudEJ1cykge1xuICAgICAgICB0aGlzLmV2ZW50U2VydmljZSA9IGV2ZW50U2VydmljZTtcbiAgICAgICAgdGhpcy5kYXRlU2VydmljZSA9IGRhdGVTZXJ2aWNlO1xuICAgICAgICB0aGlzLmdyaWRDb25maWcgPSBncmlkQ29uZmlnO1xuICAgICAgICB0aGlzLmV2ZW50QnVzID0gZXZlbnRCdXM7XG4gICAgICAgIHRoaXMuY29udGFpbmVyID0gbnVsbDtcbiAgICAgICAgdGhpcy5zZXR1cExpc3RlbmVycygpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBTZXR1cCBsaXN0ZW5lcnMgZm9yIGRyYWctZHJvcCBhbmQgdXBkYXRlIGV2ZW50c1xuICAgICAqL1xuICAgIHNldHVwTGlzdGVuZXJzKCkge1xuICAgICAgICB0aGlzLmV2ZW50QnVzLm9uKENvcmVFdmVudHMuRVZFTlRfRFJBR19DT0xVTU5fQ0hBTkdFLCAoZSkgPT4ge1xuICAgICAgICAgICAgY29uc3QgcGF5bG9hZCA9IGUuZGV0YWlsO1xuICAgICAgICAgICAgdGhpcy5oYW5kbGVDb2x1bW5DaGFuZ2UocGF5bG9hZCk7XG4gICAgICAgIH0pO1xuICAgICAgICB0aGlzLmV2ZW50QnVzLm9uKENvcmVFdmVudHMuRVZFTlRfRFJBR19NT1ZFLCAoZSkgPT4ge1xuICAgICAgICAgICAgY29uc3QgcGF5bG9hZCA9IGUuZGV0YWlsO1xuICAgICAgICAgICAgdGhpcy51cGRhdGVEcmFnVGltZXN0YW1wKHBheWxvYWQpO1xuICAgICAgICB9KTtcbiAgICAgICAgdGhpcy5ldmVudEJ1cy5vbihDb3JlRXZlbnRzLkVWRU5UX1VQREFURUQsIChlKSA9PiB7XG4gICAgICAgICAgICBjb25zdCBwYXlsb2FkID0gZS5kZXRhaWw7XG4gICAgICAgICAgICB0aGlzLmhhbmRsZUV2ZW50VXBkYXRlZChwYXlsb2FkKTtcbiAgICAgICAgfSk7XG4gICAgICAgIHRoaXMuZXZlbnRCdXMub24oQ29yZUV2ZW50cy5FVkVOVF9EUkFHX0VORCwgKGUpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHBheWxvYWQgPSBlLmRldGFpbDtcbiAgICAgICAgICAgIHRoaXMuaGFuZGxlRHJhZ0VuZChwYXlsb2FkKTtcbiAgICAgICAgfSk7XG4gICAgICAgIHRoaXMuZXZlbnRCdXMub24oQ29yZUV2ZW50cy5FVkVOVF9EUkFHX0xFQVZFX0hFQURFUiwgKGUpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHBheWxvYWQgPSBlLmRldGFpbDtcbiAgICAgICAgICAgIHRoaXMuaGFuZGxlRHJhZ0xlYXZlSGVhZGVyKHBheWxvYWQpO1xuICAgICAgICB9KTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogSGFuZGxlIEVWRU5UX0RSQUdfRU5EIC0gcmVtb3ZlIGVsZW1lbnQgaWYgZHJvcHBlZCBpbiBoZWFkZXJcbiAgICAgKi9cbiAgICBoYW5kbGVEcmFnRW5kKHBheWxvYWQpIHtcbiAgICAgICAgaWYgKHBheWxvYWQudGFyZ2V0ID09PSAnaGVhZGVyJykge1xuICAgICAgICAgICAgLy8gRXZlbnQgd2FzIGRyb3BwZWQgaW4gaGVhZGVyIGRyYXdlciAtIHJlbW92ZSBmcm9tIGdyaWRcbiAgICAgICAgICAgIGNvbnN0IGVsZW1lbnQgPSB0aGlzLmNvbnRhaW5lcj8ucXVlcnlTZWxlY3Rvcihgc3dwLWNvbnRlbnQtdmlld3BvcnQgc3dwLWV2ZW50W2RhdGEtZXZlbnQtaWQ9XCIke3BheWxvYWQuc3dwRXZlbnQuZXZlbnRJZH1cIl1gKTtcbiAgICAgICAgICAgIGVsZW1lbnQ/LnJlbW92ZSgpO1xuICAgICAgICB9XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEhhbmRsZSBoZWFkZXIgaXRlbSBsZWF2aW5nIGhlYWRlciAtIGNyZWF0ZSBzd3AtZXZlbnQgaW4gZ3JpZFxuICAgICAqL1xuICAgIGhhbmRsZURyYWdMZWF2ZUhlYWRlcihwYXlsb2FkKSB7XG4gICAgICAgIC8vIE9ubHkgaGFuZGxlIHdoZW4gc291cmNlIGlzIGhlYWRlciAoaGVhZGVyIGl0ZW0gZHJhZ2dlZCB0byBncmlkKVxuICAgICAgICBpZiAocGF5bG9hZC5zb3VyY2UgIT09ICdoZWFkZXInKVxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICBpZiAoIXBheWxvYWQudGFyZ2V0Q29sdW1uIHx8ICFwYXlsb2FkLnN0YXJ0IHx8ICFwYXlsb2FkLmVuZClcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgLy8gVHVybiBoZWFkZXIgaXRlbSBpbnRvIGdob3N0IChzdGF5cyB2aXNpYmxlIGJ1dCBmYWRlZClcbiAgICAgICAgaWYgKHBheWxvYWQuZWxlbWVudCkge1xuICAgICAgICAgICAgcGF5bG9hZC5lbGVtZW50LmNsYXNzTGlzdC5hZGQoJ2RyYWctZ2hvc3QnKTtcbiAgICAgICAgICAgIHBheWxvYWQuZWxlbWVudC5zdHlsZS5vcGFjaXR5ID0gJzAuMyc7XG4gICAgICAgICAgICBwYXlsb2FkLmVsZW1lbnQuc3R5bGUucG9pbnRlckV2ZW50cyA9ICdub25lJztcbiAgICAgICAgfVxuICAgICAgICAvLyBDcmVhdGUgZXZlbnQgb2JqZWN0IGZyb20gaGVhZGVyIGl0ZW0gZGF0YVxuICAgICAgICBjb25zdCBldmVudCA9IHtcbiAgICAgICAgICAgIGlkOiBwYXlsb2FkLmV2ZW50SWQsXG4gICAgICAgICAgICB0aXRsZTogcGF5bG9hZC50aXRsZSB8fCAnJyxcbiAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnJyxcbiAgICAgICAgICAgIHN0YXJ0OiBwYXlsb2FkLnN0YXJ0LFxuICAgICAgICAgICAgZW5kOiBwYXlsb2FkLmVuZCxcbiAgICAgICAgICAgIHR5cGU6ICdjdXN0b21lcicsXG4gICAgICAgICAgICBhbGxEYXk6IGZhbHNlLFxuICAgICAgICAgICAgc3luY1N0YXR1czogJ3BlbmRpbmcnXG4gICAgICAgIH07XG4gICAgICAgIC8vIENyZWF0ZSBzd3AtZXZlbnQgZWxlbWVudCB1c2luZyBleGlzdGluZyBtZXRob2RcbiAgICAgICAgY29uc3QgZWxlbWVudCA9IHRoaXMuY3JlYXRlRXZlbnRFbGVtZW50KGV2ZW50KTtcbiAgICAgICAgLy8gQWRkIHRvIHRhcmdldCBjb2x1bW5cbiAgICAgICAgbGV0IGV2ZW50c0xheWVyID0gcGF5bG9hZC50YXJnZXRDb2x1bW4ucXVlcnlTZWxlY3Rvcignc3dwLWV2ZW50cy1sYXllcicpO1xuICAgICAgICBpZiAoIWV2ZW50c0xheWVyKSB7XG4gICAgICAgICAgICBldmVudHNMYXllciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3N3cC1ldmVudHMtbGF5ZXInKTtcbiAgICAgICAgICAgIHBheWxvYWQudGFyZ2V0Q29sdW1uLmFwcGVuZENoaWxkKGV2ZW50c0xheWVyKTtcbiAgICAgICAgfVxuICAgICAgICBldmVudHNMYXllci5hcHBlbmRDaGlsZChlbGVtZW50KTtcbiAgICAgICAgLy8gTWFyayBhcyBkcmFnZ2luZyBzbyBEcmFnRHJvcE1hbmFnZXIgY2FuIGNvbnRpbnVlIHdpdGggaXRcbiAgICAgICAgZWxlbWVudC5jbGFzc0xpc3QuYWRkKCdkcmFnZ2luZycpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBIYW5kbGUgRVZFTlRfVVBEQVRFRCAtIHJlLXJlbmRlciBhZmZlY3RlZCBjb2x1bW5zXG4gICAgICovXG4gICAgYXN5bmMgaGFuZGxlRXZlbnRVcGRhdGVkKHBheWxvYWQpIHtcbiAgICAgICAgLy8gUmUtcmVuZGVyIHNvdXJjZSBjb2x1bW4gKGlmIGRpZmZlcmVudCBmcm9tIHRhcmdldClcbiAgICAgICAgaWYgKHBheWxvYWQuc291cmNlQ29sdW1uS2V5ICE9PSBwYXlsb2FkLnRhcmdldENvbHVtbktleSkge1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5yZXJlbmRlckNvbHVtbihwYXlsb2FkLnNvdXJjZUNvbHVtbktleSk7XG4gICAgICAgIH1cbiAgICAgICAgLy8gUmUtcmVuZGVyIHRhcmdldCBjb2x1bW5cbiAgICAgICAgYXdhaXQgdGhpcy5yZXJlbmRlckNvbHVtbihwYXlsb2FkLnRhcmdldENvbHVtbktleSk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFJlLXJlbmRlciBhIHNpbmdsZSBjb2x1bW4gd2l0aCBmcmVzaCBkYXRhIGZyb20gSW5kZXhlZERCXG4gICAgICovXG4gICAgYXN5bmMgcmVyZW5kZXJDb2x1bW4oY29sdW1uS2V5KSB7XG4gICAgICAgIGNvbnN0IGNvbHVtbiA9IHRoaXMuZmluZENvbHVtbihjb2x1bW5LZXkpO1xuICAgICAgICBpZiAoIWNvbHVtbilcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgLy8gUmVhZCBkYXRlIGFuZCByZXNvdXJjZUlkIGRpcmVjdGx5IGZyb20gY29sdW1uIGF0dHJpYnV0ZXMgKGNvbHVtbktleSBpcyBvcGFxdWUpXG4gICAgICAgIGNvbnN0IGRhdGUgPSBjb2x1bW4uZGF0YXNldC5kYXRlO1xuICAgICAgICBjb25zdCByZXNvdXJjZUlkID0gY29sdW1uLmRhdGFzZXQucmVzb3VyY2VJZDtcbiAgICAgICAgaWYgKCFkYXRlKVxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAvLyBHZXQgZGF0ZSByYW5nZSBmb3IgdGhpcyBkYXlcbiAgICAgICAgY29uc3Qgc3RhcnREYXRlID0gbmV3IERhdGUoZGF0ZSk7XG4gICAgICAgIGNvbnN0IGVuZERhdGUgPSBuZXcgRGF0ZShkYXRlKTtcbiAgICAgICAgZW5kRGF0ZS5zZXRIb3VycygyMywgNTksIDU5LCA5OTkpO1xuICAgICAgICAvLyBGZXRjaCBldmVudHMgZnJvbSBJbmRleGVkREJcbiAgICAgICAgY29uc3QgZXZlbnRzID0gcmVzb3VyY2VJZFxuICAgICAgICAgICAgPyBhd2FpdCB0aGlzLmV2ZW50U2VydmljZS5nZXRCeVJlc291cmNlQW5kRGF0ZVJhbmdlKHJlc291cmNlSWQsIHN0YXJ0RGF0ZSwgZW5kRGF0ZSlcbiAgICAgICAgICAgIDogYXdhaXQgdGhpcy5ldmVudFNlcnZpY2UuZ2V0QnlEYXRlUmFuZ2Uoc3RhcnREYXRlLCBlbmREYXRlKTtcbiAgICAgICAgLy8gRmlsdGVyIHRvIHRpbWVkIGV2ZW50cyBhbmQgbWF0Y2ggZGF0ZSBleGFjdGx5XG4gICAgICAgIGNvbnN0IHRpbWVkRXZlbnRzID0gZXZlbnRzLmZpbHRlcihldmVudCA9PiAhZXZlbnQuYWxsRGF5ICYmIHRoaXMuZGF0ZVNlcnZpY2UuZ2V0RGF0ZUtleShldmVudC5zdGFydCkgPT09IGRhdGUpO1xuICAgICAgICAvLyBHZXQgb3IgY3JlYXRlIGV2ZW50cyBsYXllclxuICAgICAgICBsZXQgZXZlbnRzTGF5ZXIgPSBjb2x1bW4ucXVlcnlTZWxlY3Rvcignc3dwLWV2ZW50cy1sYXllcicpO1xuICAgICAgICBpZiAoIWV2ZW50c0xheWVyKSB7XG4gICAgICAgICAgICBldmVudHNMYXllciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3N3cC1ldmVudHMtbGF5ZXInKTtcbiAgICAgICAgICAgIGNvbHVtbi5hcHBlbmRDaGlsZChldmVudHNMYXllcik7XG4gICAgICAgIH1cbiAgICAgICAgLy8gQ2xlYXIgZXhpc3RpbmcgZXZlbnRzXG4gICAgICAgIGV2ZW50c0xheWVyLmlubmVySFRNTCA9ICcnO1xuICAgICAgICAvLyBDYWxjdWxhdGUgbGF5b3V0IHdpdGggc3RhY2tpbmcvZ3JvdXBpbmdcbiAgICAgICAgY29uc3QgbGF5b3V0ID0gY2FsY3VsYXRlQ29sdW1uTGF5b3V0KHRpbWVkRXZlbnRzLCB0aGlzLmdyaWRDb25maWcpO1xuICAgICAgICAvLyBSZW5kZXIgR1JJRCBncm91cHNcbiAgICAgICAgbGF5b3V0LmdyaWRzLmZvckVhY2goZ3JpZCA9PiB7XG4gICAgICAgICAgICBjb25zdCBncm91cEVsID0gdGhpcy5yZW5kZXJHcmlkR3JvdXAoZ3JpZCk7XG4gICAgICAgICAgICBldmVudHNMYXllci5hcHBlbmRDaGlsZChncm91cEVsKTtcbiAgICAgICAgfSk7XG4gICAgICAgIC8vIFJlbmRlciBTVEFDS0VEIGV2ZW50c1xuICAgICAgICBsYXlvdXQuc3RhY2tlZC5mb3JFYWNoKGl0ZW0gPT4ge1xuICAgICAgICAgICAgY29uc3QgZXZlbnRFbCA9IHRoaXMucmVuZGVyU3RhY2tlZEV2ZW50KGl0ZW0uZXZlbnQsIGl0ZW0uc3RhY2tMZXZlbCk7XG4gICAgICAgICAgICBldmVudHNMYXllci5hcHBlbmRDaGlsZChldmVudEVsKTtcbiAgICAgICAgfSk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEZpbmQgYSBjb2x1bW4gZWxlbWVudCBieSBjb2x1bW5LZXlcbiAgICAgKi9cbiAgICBmaW5kQ29sdW1uKGNvbHVtbktleSkge1xuICAgICAgICBpZiAoIXRoaXMuY29udGFpbmVyKVxuICAgICAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICAgIHJldHVybiB0aGlzLmNvbnRhaW5lci5xdWVyeVNlbGVjdG9yKGBzd3AtZGF5LWNvbHVtbltkYXRhLWNvbHVtbi1rZXk9XCIke2NvbHVtbktleX1cIl1gKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogSGFuZGxlIGV2ZW50IG1vdmluZyB0byBhIG5ldyBjb2x1bW4gZHVyaW5nIGRyYWdcbiAgICAgKi9cbiAgICBoYW5kbGVDb2x1bW5DaGFuZ2UocGF5bG9hZCkge1xuICAgICAgICBjb25zdCBldmVudHNMYXllciA9IHBheWxvYWQubmV3Q29sdW1uLnF1ZXJ5U2VsZWN0b3IoJ3N3cC1ldmVudHMtbGF5ZXInKTtcbiAgICAgICAgaWYgKCFldmVudHNMYXllcilcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgLy8gTW92ZSBlbGVtZW50IHRvIG5ldyBjb2x1bW5cbiAgICAgICAgZXZlbnRzTGF5ZXIuYXBwZW5kQ2hpbGQocGF5bG9hZC5lbGVtZW50KTtcbiAgICAgICAgLy8gUHJlc2VydmUgWSBwb3NpdGlvblxuICAgICAgICBwYXlsb2FkLmVsZW1lbnQuc3R5bGUudG9wID0gYCR7cGF5bG9hZC5jdXJyZW50WX1weGA7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFVwZGF0ZSB0aW1lc3RhbXAgZGlzcGxheSBkdXJpbmcgZHJhZyAoc25hcHBlZCB0byBncmlkKVxuICAgICAqL1xuICAgIHVwZGF0ZURyYWdUaW1lc3RhbXAocGF5bG9hZCkge1xuICAgICAgICBjb25zdCB0aW1lRWwgPSBwYXlsb2FkLmVsZW1lbnQucXVlcnlTZWxlY3Rvcignc3dwLWV2ZW50LXRpbWUnKTtcbiAgICAgICAgaWYgKCF0aW1lRWwpXG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIC8vIFNuYXAgcG9zaXRpb24gdG8gZ3JpZCBpbnRlcnZhbFxuICAgICAgICBjb25zdCBzbmFwcGVkWSA9IHNuYXBUb0dyaWQocGF5bG9hZC5jdXJyZW50WSwgdGhpcy5ncmlkQ29uZmlnKTtcbiAgICAgICAgLy8gQ2FsY3VsYXRlIG5ldyBzdGFydCB0aW1lXG4gICAgICAgIGNvbnN0IG1pbnV0ZXNGcm9tR3JpZFN0YXJ0ID0gcGl4ZWxzVG9NaW51dGVzKHNuYXBwZWRZLCB0aGlzLmdyaWRDb25maWcpO1xuICAgICAgICBjb25zdCBzdGFydE1pbnV0ZXMgPSAodGhpcy5ncmlkQ29uZmlnLmRheVN0YXJ0SG91ciAqIDYwKSArIG1pbnV0ZXNGcm9tR3JpZFN0YXJ0O1xuICAgICAgICAvLyBLZWVwIG9yaWdpbmFsIGR1cmF0aW9uIChmcm9tIGVsZW1lbnQgaGVpZ2h0KVxuICAgICAgICBjb25zdCBoZWlnaHQgPSBwYXJzZUZsb2F0KHBheWxvYWQuZWxlbWVudC5zdHlsZS5oZWlnaHQpIHx8IHRoaXMuZ3JpZENvbmZpZy5ob3VySGVpZ2h0O1xuICAgICAgICBjb25zdCBkdXJhdGlvbk1pbnV0ZXMgPSBwaXhlbHNUb01pbnV0ZXMoaGVpZ2h0LCB0aGlzLmdyaWRDb25maWcpO1xuICAgICAgICAvLyBDcmVhdGUgRGF0ZSBvYmplY3RzIGZvciBjb25zaXN0ZW50IGZvcm1hdHRpbmcgdmlhIERhdGVTZXJ2aWNlXG4gICAgICAgIGNvbnN0IHN0YXJ0ID0gdGhpcy5taW51dGVzVG9EYXRlKHN0YXJ0TWludXRlcyk7XG4gICAgICAgIGNvbnN0IGVuZCA9IHRoaXMubWludXRlc1RvRGF0ZShzdGFydE1pbnV0ZXMgKyBkdXJhdGlvbk1pbnV0ZXMpO1xuICAgICAgICB0aW1lRWwudGV4dENvbnRlbnQgPSB0aGlzLmRhdGVTZXJ2aWNlLmZvcm1hdFRpbWVSYW5nZShzdGFydCwgZW5kKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogQ29udmVydCBtaW51dGVzIHNpbmNlIG1pZG5pZ2h0IHRvIGEgRGF0ZSBvYmplY3QgKHRvZGF5KVxuICAgICAqL1xuICAgIG1pbnV0ZXNUb0RhdGUobWludXRlcykge1xuICAgICAgICBjb25zdCBkYXRlID0gbmV3IERhdGUoKTtcbiAgICAgICAgZGF0ZS5zZXRIb3VycyhNYXRoLmZsb29yKG1pbnV0ZXMgLyA2MCkgJSAyNCwgbWludXRlcyAlIDYwLCAwLCAwKTtcbiAgICAgICAgcmV0dXJuIGRhdGU7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFJlbmRlciBldmVudHMgZm9yIHZpc2libGUgZGF0ZXMgaW50byBkYXkgY29sdW1uc1xuICAgICAqIEBwYXJhbSBjb250YWluZXIgLSBDYWxlbmRhciBjb250YWluZXIgZWxlbWVudFxuICAgICAqIEBwYXJhbSBmaWx0ZXIgLSBGaWx0ZXIgd2l0aCAnZGF0ZScgYW5kIG9wdGlvbmFsbHkgJ3Jlc291cmNlJyBhcnJheXNcbiAgICAgKiBAcGFyYW0gZmlsdGVyVGVtcGxhdGUgLSBUZW1wbGF0ZSBmb3IgbWF0Y2hpbmcgZXZlbnRzIHRvIGNvbHVtbnNcbiAgICAgKi9cbiAgICBhc3luYyByZW5kZXIoY29udGFpbmVyLCBmaWx0ZXIsIGZpbHRlclRlbXBsYXRlKSB7XG4gICAgICAgIC8vIFN0b3JlIGNvbnRhaW5lciByZWZlcmVuY2UgZm9yIGxhdGVyIHJlLXJlbmRlcnNcbiAgICAgICAgdGhpcy5jb250YWluZXIgPSBjb250YWluZXI7XG4gICAgICAgIGNvbnN0IHZpc2libGVEYXRlcyA9IGZpbHRlclsnZGF0ZSddIHx8IFtdO1xuICAgICAgICBpZiAodmlzaWJsZURhdGVzLmxlbmd0aCA9PT0gMClcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgLy8gR2V0IGRhdGUgcmFuZ2UgZm9yIHF1ZXJ5XG4gICAgICAgIGNvbnN0IHN0YXJ0RGF0ZSA9IG5ldyBEYXRlKHZpc2libGVEYXRlc1swXSk7XG4gICAgICAgIGNvbnN0IGVuZERhdGUgPSBuZXcgRGF0ZSh2aXNpYmxlRGF0ZXNbdmlzaWJsZURhdGVzLmxlbmd0aCAtIDFdKTtcbiAgICAgICAgZW5kRGF0ZS5zZXRIb3VycygyMywgNTksIDU5LCA5OTkpO1xuICAgICAgICAvLyBGZXRjaCBldmVudHMgZnJvbSBJbmRleGVkREJcbiAgICAgICAgY29uc3QgZXZlbnRzID0gYXdhaXQgdGhpcy5ldmVudFNlcnZpY2UuZ2V0QnlEYXRlUmFuZ2Uoc3RhcnREYXRlLCBlbmREYXRlKTtcbiAgICAgICAgLy8gRmluZCBkYXkgY29sdW1uc1xuICAgICAgICBjb25zdCBkYXlDb2x1bW5zID0gY29udGFpbmVyLnF1ZXJ5U2VsZWN0b3IoJ3N3cC1kYXktY29sdW1ucycpO1xuICAgICAgICBpZiAoIWRheUNvbHVtbnMpXG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIGNvbnN0IGNvbHVtbnMgPSBkYXlDb2x1bW5zLnF1ZXJ5U2VsZWN0b3JBbGwoJ3N3cC1kYXktY29sdW1uJyk7XG4gICAgICAgIC8vIFJlbmRlciBldmVudHMgaW50byBlYWNoIGNvbHVtbiBiYXNlZCBvbiBGaWx0ZXJUZW1wbGF0ZSBtYXRjaGluZ1xuICAgICAgICBjb2x1bW5zLmZvckVhY2goY29sdW1uID0+IHtcbiAgICAgICAgICAgIGNvbnN0IGNvbHVtbkVsID0gY29sdW1uO1xuICAgICAgICAgICAgLy8gVXNlIEZpbHRlclRlbXBsYXRlIGZvciBtYXRjaGluZyAtIG9ubHkgZmllbGRzIGluIHRlbXBsYXRlIGFyZSBjaGVja2VkXG4gICAgICAgICAgICBjb25zdCBjb2x1bW5FdmVudHMgPSBldmVudHMuZmlsdGVyKGV2ZW50ID0+IGZpbHRlclRlbXBsYXRlLm1hdGNoZXMoZXZlbnQsIGNvbHVtbkVsKSk7XG4gICAgICAgICAgICAvLyBHZXQgb3IgY3JlYXRlIGV2ZW50cyBsYXllclxuICAgICAgICAgICAgbGV0IGV2ZW50c0xheWVyID0gY29sdW1uLnF1ZXJ5U2VsZWN0b3IoJ3N3cC1ldmVudHMtbGF5ZXInKTtcbiAgICAgICAgICAgIGlmICghZXZlbnRzTGF5ZXIpIHtcbiAgICAgICAgICAgICAgICBldmVudHNMYXllciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3N3cC1ldmVudHMtbGF5ZXInKTtcbiAgICAgICAgICAgICAgICBjb2x1bW4uYXBwZW5kQ2hpbGQoZXZlbnRzTGF5ZXIpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgLy8gQ2xlYXIgZXhpc3RpbmcgZXZlbnRzXG4gICAgICAgICAgICBldmVudHNMYXllci5pbm5lckhUTUwgPSAnJztcbiAgICAgICAgICAgIC8vIEZpbHRlciB0byB0aW1lZCBldmVudHMgb25seVxuICAgICAgICAgICAgY29uc3QgdGltZWRFdmVudHMgPSBjb2x1bW5FdmVudHMuZmlsdGVyKGV2ZW50ID0+ICFldmVudC5hbGxEYXkpO1xuICAgICAgICAgICAgLy8gQ2FsY3VsYXRlIGxheW91dCB3aXRoIHN0YWNraW5nL2dyb3VwaW5nXG4gICAgICAgICAgICBjb25zdCBsYXlvdXQgPSBjYWxjdWxhdGVDb2x1bW5MYXlvdXQodGltZWRFdmVudHMsIHRoaXMuZ3JpZENvbmZpZyk7XG4gICAgICAgICAgICAvLyBSZW5kZXIgR1JJRCBncm91cHMgKHNpbXVsdGFuZW91cyBldmVudHMgc2lkZS1ieS1zaWRlKVxuICAgICAgICAgICAgbGF5b3V0LmdyaWRzLmZvckVhY2goZ3JpZCA9PiB7XG4gICAgICAgICAgICAgICAgY29uc3QgZ3JvdXBFbCA9IHRoaXMucmVuZGVyR3JpZEdyb3VwKGdyaWQpO1xuICAgICAgICAgICAgICAgIGV2ZW50c0xheWVyLmFwcGVuZENoaWxkKGdyb3VwRWwpO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAvLyBSZW5kZXIgU1RBQ0tFRCBldmVudHMgKG92ZXJsYXBwaW5nIHdpdGggbWFyZ2luIG9mZnNldClcbiAgICAgICAgICAgIGxheW91dC5zdGFja2VkLmZvckVhY2goaXRlbSA9PiB7XG4gICAgICAgICAgICAgICAgY29uc3QgZXZlbnRFbCA9IHRoaXMucmVuZGVyU3RhY2tlZEV2ZW50KGl0ZW0uZXZlbnQsIGl0ZW0uc3RhY2tMZXZlbCk7XG4gICAgICAgICAgICAgICAgZXZlbnRzTGF5ZXIuYXBwZW5kQ2hpbGQoZXZlbnRFbCk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIENyZWF0ZSBhIHNpbmdsZSBldmVudCBlbGVtZW50XG4gICAgICpcbiAgICAgKiBDTEVBTiBhcHByb2FjaDpcbiAgICAgKiAtIE9ubHkgZGF0YS1pZCBmb3IgbG9va3VwXG4gICAgICogLSBWaXNpYmxlIGNvbnRlbnQgaW4gaW5uZXJIVE1MIG9ubHlcbiAgICAgKi9cbiAgICBjcmVhdGVFdmVudEVsZW1lbnQoZXZlbnQpIHtcbiAgICAgICAgY29uc3QgZWxlbWVudCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3N3cC1ldmVudCcpO1xuICAgICAgICAvLyBEYXRhIGF0dHJpYnV0ZXMgZm9yIFN3cEV2ZW50IGNvbXBhdGliaWxpdHlcbiAgICAgICAgZWxlbWVudC5kYXRhc2V0LmV2ZW50SWQgPSBldmVudC5pZDtcbiAgICAgICAgaWYgKGV2ZW50LnJlc291cmNlSWQpIHtcbiAgICAgICAgICAgIGVsZW1lbnQuZGF0YXNldC5yZXNvdXJjZUlkID0gZXZlbnQucmVzb3VyY2VJZDtcbiAgICAgICAgfVxuICAgICAgICAvLyBDYWxjdWxhdGUgcG9zaXRpb25cbiAgICAgICAgY29uc3QgcG9zaXRpb24gPSBjYWxjdWxhdGVFdmVudFBvc2l0aW9uKGV2ZW50LnN0YXJ0LCBldmVudC5lbmQsIHRoaXMuZ3JpZENvbmZpZyk7XG4gICAgICAgIGVsZW1lbnQuc3R5bGUudG9wID0gYCR7cG9zaXRpb24udG9wfXB4YDtcbiAgICAgICAgZWxlbWVudC5zdHlsZS5oZWlnaHQgPSBgJHtwb3NpdGlvbi5oZWlnaHR9cHhgO1xuICAgICAgICAvLyBDb2xvciBjbGFzcyBiYXNlZCBvbiBldmVudCB0eXBlXG4gICAgICAgIGNvbnN0IGNvbG9yQ2xhc3MgPSB0aGlzLmdldENvbG9yQ2xhc3MoZXZlbnQpO1xuICAgICAgICBpZiAoY29sb3JDbGFzcykge1xuICAgICAgICAgICAgZWxlbWVudC5jbGFzc0xpc3QuYWRkKGNvbG9yQ2xhc3MpO1xuICAgICAgICB9XG4gICAgICAgIC8vIFZpc2libGUgY29udGVudCBvbmx5XG4gICAgICAgIGVsZW1lbnQuaW5uZXJIVE1MID0gYFxyXG4gICAgICA8c3dwLWV2ZW50LXRpbWU+JHt0aGlzLmRhdGVTZXJ2aWNlLmZvcm1hdFRpbWVSYW5nZShldmVudC5zdGFydCwgZXZlbnQuZW5kKX08L3N3cC1ldmVudC10aW1lPlxyXG4gICAgICA8c3dwLWV2ZW50LXRpdGxlPiR7dGhpcy5lc2NhcGVIdG1sKGV2ZW50LnRpdGxlKX08L3N3cC1ldmVudC10aXRsZT5cclxuICAgICAgJHtldmVudC5kZXNjcmlwdGlvbiA/IGA8c3dwLWV2ZW50LWRlc2NyaXB0aW9uPiR7dGhpcy5lc2NhcGVIdG1sKGV2ZW50LmRlc2NyaXB0aW9uKX08L3N3cC1ldmVudC1kZXNjcmlwdGlvbj5gIDogJyd9XHJcbiAgICBgO1xuICAgICAgICByZXR1cm4gZWxlbWVudDtcbiAgICB9XG4gICAgLyoqXG4gICAgICogR2V0IGNvbG9yIGNsYXNzIGJhc2VkIG9uIG1ldGFkYXRhLmNvbG9yIG9yIGV2ZW50IHR5cGVcbiAgICAgKi9cbiAgICBnZXRDb2xvckNsYXNzKGV2ZW50KSB7XG4gICAgICAgIC8vIENoZWNrIG1ldGFkYXRhLmNvbG9yIGZpcnN0XG4gICAgICAgIGlmIChldmVudC5tZXRhZGF0YT8uY29sb3IpIHtcbiAgICAgICAgICAgIHJldHVybiBgaXMtJHtldmVudC5tZXRhZGF0YS5jb2xvcn1gO1xuICAgICAgICB9XG4gICAgICAgIC8vIEZhbGxiYWNrIHRvIHR5cGUtYmFzZWQgY29sb3JcbiAgICAgICAgY29uc3QgdHlwZUNvbG9ycyA9IHtcbiAgICAgICAgICAgICdjdXN0b21lcic6ICdpcy1ibHVlJyxcbiAgICAgICAgICAgICd2YWNhdGlvbic6ICdpcy1ncmVlbicsXG4gICAgICAgICAgICAnYnJlYWsnOiAnaXMtYW1iZXInLFxuICAgICAgICAgICAgJ21lZXRpbmcnOiAnaXMtcHVycGxlJyxcbiAgICAgICAgICAgICdibG9ja2VkJzogJ2lzLXJlZCdcbiAgICAgICAgfTtcbiAgICAgICAgcmV0dXJuIHR5cGVDb2xvcnNbZXZlbnQudHlwZV0gfHwgJ2lzLWJsdWUnO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBFc2NhcGUgSFRNTCB0byBwcmV2ZW50IFhTU1xuICAgICAqL1xuICAgIGVzY2FwZUh0bWwodGV4dCkge1xuICAgICAgICBjb25zdCBkaXYgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcbiAgICAgICAgZGl2LnRleHRDb250ZW50ID0gdGV4dDtcbiAgICAgICAgcmV0dXJuIGRpdi5pbm5lckhUTUw7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFJlbmRlciBhIEdSSUQgZ3JvdXAgd2l0aCBzaWRlLWJ5LXNpZGUgY29sdW1uc1xuICAgICAqIFVzZWQgd2hlbiBtdWx0aXBsZSBldmVudHMgc3RhcnQgYXQgdGhlIHNhbWUgdGltZVxuICAgICAqL1xuICAgIHJlbmRlckdyaWRHcm91cChsYXlvdXQpIHtcbiAgICAgICAgY29uc3QgZ3JvdXAgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzd3AtZXZlbnQtZ3JvdXAnKTtcbiAgICAgICAgZ3JvdXAuY2xhc3NMaXN0LmFkZChgY29scy0ke2xheW91dC5jb2x1bW5zLmxlbmd0aH1gKTtcbiAgICAgICAgZ3JvdXAuc3R5bGUudG9wID0gYCR7bGF5b3V0LnBvc2l0aW9uLnRvcH1weGA7XG4gICAgICAgIC8vIFN0YWNrIGxldmVsIHN0eWxpbmcgZm9yIGVudGlyZSBncm91cCAoaWYgbmVzdGVkIGluIGFub3RoZXIgZXZlbnQpXG4gICAgICAgIGlmIChsYXlvdXQuc3RhY2tMZXZlbCA+IDApIHtcbiAgICAgICAgICAgIGdyb3VwLnN0eWxlLm1hcmdpbkxlZnQgPSBgJHtsYXlvdXQuc3RhY2tMZXZlbCAqIDE1fXB4YDtcbiAgICAgICAgICAgIGdyb3VwLnN0eWxlLnpJbmRleCA9IGAkezEwMCArIGxheW91dC5zdGFja0xldmVsfWA7XG4gICAgICAgIH1cbiAgICAgICAgLy8gQ2FsY3VsYXRlIHRoZSBoZWlnaHQgbmVlZGVkIGZvciB0aGUgZ3JvdXAgKHRhbGxlc3QgZXZlbnQpXG4gICAgICAgIGxldCBtYXhCb3R0b20gPSAwO1xuICAgICAgICBmb3IgKGNvbnN0IGV2ZW50IG9mIGxheW91dC5ldmVudHMpIHtcbiAgICAgICAgICAgIGNvbnN0IHBvcyA9IGNhbGN1bGF0ZUV2ZW50UG9zaXRpb24oZXZlbnQuc3RhcnQsIGV2ZW50LmVuZCwgdGhpcy5ncmlkQ29uZmlnKTtcbiAgICAgICAgICAgIGNvbnN0IGV2ZW50Qm90dG9tID0gcG9zLnRvcCArIHBvcy5oZWlnaHQ7XG4gICAgICAgICAgICBpZiAoZXZlbnRCb3R0b20gPiBtYXhCb3R0b20pXG4gICAgICAgICAgICAgICAgbWF4Qm90dG9tID0gZXZlbnRCb3R0b207XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgZ3JvdXBIZWlnaHQgPSBtYXhCb3R0b20gLSBsYXlvdXQucG9zaXRpb24udG9wO1xuICAgICAgICBncm91cC5zdHlsZS5oZWlnaHQgPSBgJHtncm91cEhlaWdodH1weGA7XG4gICAgICAgIC8vIENyZWF0ZSB3cmFwcGVyIGRpdiBmb3IgZWFjaCBjb2x1bW5cbiAgICAgICAgbGF5b3V0LmNvbHVtbnMuZm9yRWFjaChjb2x1bW5FdmVudHMgPT4ge1xuICAgICAgICAgICAgY29uc3Qgd3JhcHBlciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpO1xuICAgICAgICAgICAgd3JhcHBlci5zdHlsZS5wb3NpdGlvbiA9ICdyZWxhdGl2ZSc7XG4gICAgICAgICAgICBjb2x1bW5FdmVudHMuZm9yRWFjaChldmVudCA9PiB7XG4gICAgICAgICAgICAgICAgY29uc3QgZXZlbnRFbCA9IHRoaXMuY3JlYXRlRXZlbnRFbGVtZW50KGV2ZW50KTtcbiAgICAgICAgICAgICAgICAvLyBQb3NpdGlvbiByZWxhdGl2ZSB0byBncm91cCB0b3BcbiAgICAgICAgICAgICAgICBjb25zdCBwb3MgPSBjYWxjdWxhdGVFdmVudFBvc2l0aW9uKGV2ZW50LnN0YXJ0LCBldmVudC5lbmQsIHRoaXMuZ3JpZENvbmZpZyk7XG4gICAgICAgICAgICAgICAgZXZlbnRFbC5zdHlsZS50b3AgPSBgJHtwb3MudG9wIC0gbGF5b3V0LnBvc2l0aW9uLnRvcH1weGA7XG4gICAgICAgICAgICAgICAgZXZlbnRFbC5zdHlsZS5wb3NpdGlvbiA9ICdhYnNvbHV0ZSc7XG4gICAgICAgICAgICAgICAgZXZlbnRFbC5zdHlsZS5sZWZ0ID0gJzAnO1xuICAgICAgICAgICAgICAgIGV2ZW50RWwuc3R5bGUucmlnaHQgPSAnMCc7XG4gICAgICAgICAgICAgICAgd3JhcHBlci5hcHBlbmRDaGlsZChldmVudEVsKTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgZ3JvdXAuYXBwZW5kQ2hpbGQod3JhcHBlcik7XG4gICAgICAgIH0pO1xuICAgICAgICByZXR1cm4gZ3JvdXA7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFJlbmRlciBhIFNUQUNLRUQgZXZlbnQgd2l0aCBtYXJnaW4tbGVmdCBvZmZzZXRcbiAgICAgKiBVc2VkIGZvciBvdmVybGFwcGluZyBldmVudHMgdGhhdCBkb24ndCBzdGFydCBhdCB0aGUgc2FtZSB0aW1lXG4gICAgICovXG4gICAgcmVuZGVyU3RhY2tlZEV2ZW50KGV2ZW50LCBzdGFja0xldmVsKSB7XG4gICAgICAgIGNvbnN0IGVsZW1lbnQgPSB0aGlzLmNyZWF0ZUV2ZW50RWxlbWVudChldmVudCk7XG4gICAgICAgIC8vIEFkZCBzdGFjayBtZXRhZGF0YSBmb3IgZHJhZy1kcm9wIGFuZCBvdGhlciBmZWF0dXJlc1xuICAgICAgICBlbGVtZW50LmRhdGFzZXQuc3RhY2tMaW5rID0gSlNPTi5zdHJpbmdpZnkoeyBzdGFja0xldmVsIH0pO1xuICAgICAgICAvLyBWaXN1YWwgc3R5bGluZyBiYXNlZCBvbiBzdGFjayBsZXZlbFxuICAgICAgICBpZiAoc3RhY2tMZXZlbCA+IDApIHtcbiAgICAgICAgICAgIGVsZW1lbnQuc3R5bGUubWFyZ2luTGVmdCA9IGAke3N0YWNrTGV2ZWwgKiAxNX1weGA7XG4gICAgICAgICAgICBlbGVtZW50LnN0eWxlLnpJbmRleCA9IGAkezEwMCArIHN0YWNrTGV2ZWx9YDtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gZWxlbWVudDtcbiAgICB9XG59XG4iLCAiLyoqXG4gKiBTY2hlZHVsZVJlbmRlcmVyIC0gUmVuZGVycyB1bmF2YWlsYWJsZSB0aW1lIHpvbmVzIGluIGRheSBjb2x1bW5zXG4gKlxuICogQ3JlYXRlcyB2aXN1YWwgaW5kaWNhdG9ycyBmb3IgdGltZXMgb3V0c2lkZSB0aGUgcmVzb3VyY2UncyB3b3JraW5nIGhvdXJzOlxuICogLSBCZWZvcmUgd29yayBzdGFydCAoZS5nLiwgMDY6MDAgLSAwOTowMClcbiAqIC0gQWZ0ZXIgd29yayBlbmQgKGUuZy4sIDE3OjAwIC0gMTg6MDApXG4gKiAtIEZ1bGwgZGF5IGlmIHJlc291cmNlIGlzIG9mZiAoc2NoZWR1bGUgPSBudWxsKVxuICovXG5leHBvcnQgY2xhc3MgU2NoZWR1bGVSZW5kZXJlciB7XG4gICAgY29uc3RydWN0b3Ioc2NoZWR1bGVTZXJ2aWNlLCBkYXRlU2VydmljZSwgZ3JpZENvbmZpZykge1xuICAgICAgICB0aGlzLnNjaGVkdWxlU2VydmljZSA9IHNjaGVkdWxlU2VydmljZTtcbiAgICAgICAgdGhpcy5kYXRlU2VydmljZSA9IGRhdGVTZXJ2aWNlO1xuICAgICAgICB0aGlzLmdyaWRDb25maWcgPSBncmlkQ29uZmlnO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBSZW5kZXIgdW5hdmFpbGFibGUgem9uZXMgZm9yIHZpc2libGUgY29sdW1uc1xuICAgICAqIEBwYXJhbSBjb250YWluZXIgLSBDYWxlbmRhciBjb250YWluZXIgZWxlbWVudFxuICAgICAqIEBwYXJhbSBmaWx0ZXIgLSBGaWx0ZXIgd2l0aCAnZGF0ZScgYW5kICdyZXNvdXJjZScgYXJyYXlzXG4gICAgICovXG4gICAgYXN5bmMgcmVuZGVyKGNvbnRhaW5lciwgZmlsdGVyKSB7XG4gICAgICAgIGNvbnN0IGRhdGVzID0gZmlsdGVyWydkYXRlJ10gfHwgW107XG4gICAgICAgIGNvbnN0IHJlc291cmNlSWRzID0gZmlsdGVyWydyZXNvdXJjZSddIHx8IFtdO1xuICAgICAgICBpZiAoZGF0ZXMubGVuZ3RoID09PSAwKVxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAvLyBGaW5kIGRheSBjb2x1bW5zXG4gICAgICAgIGNvbnN0IGRheUNvbHVtbnMgPSBjb250YWluZXIucXVlcnlTZWxlY3Rvcignc3dwLWRheS1jb2x1bW5zJyk7XG4gICAgICAgIGlmICghZGF5Q29sdW1ucylcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgY29uc3QgY29sdW1ucyA9IGRheUNvbHVtbnMucXVlcnlTZWxlY3RvckFsbCgnc3dwLWRheS1jb2x1bW4nKTtcbiAgICAgICAgZm9yIChjb25zdCBjb2x1bW4gb2YgY29sdW1ucykge1xuICAgICAgICAgICAgY29uc3QgZGF0ZSA9IGNvbHVtbi5kYXRhc2V0LmRhdGU7XG4gICAgICAgICAgICBjb25zdCByZXNvdXJjZUlkID0gY29sdW1uLmRhdGFzZXQucmVzb3VyY2VJZDtcbiAgICAgICAgICAgIGlmICghZGF0ZSB8fCAhcmVzb3VyY2VJZClcbiAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgIC8vIEdldCBvciBjcmVhdGUgdW5hdmFpbGFibGUgbGF5ZXJcbiAgICAgICAgICAgIGxldCB1bmF2YWlsYWJsZUxheWVyID0gY29sdW1uLnF1ZXJ5U2VsZWN0b3IoJ3N3cC11bmF2YWlsYWJsZS1sYXllcicpO1xuICAgICAgICAgICAgaWYgKCF1bmF2YWlsYWJsZUxheWVyKSB7XG4gICAgICAgICAgICAgICAgdW5hdmFpbGFibGVMYXllciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3N3cC11bmF2YWlsYWJsZS1sYXllcicpO1xuICAgICAgICAgICAgICAgIGNvbHVtbi5pbnNlcnRCZWZvcmUodW5hdmFpbGFibGVMYXllciwgY29sdW1uLmZpcnN0Q2hpbGQpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgLy8gQ2xlYXIgZXhpc3RpbmdcbiAgICAgICAgICAgIHVuYXZhaWxhYmxlTGF5ZXIuaW5uZXJIVE1MID0gJyc7XG4gICAgICAgICAgICAvLyBHZXQgc2NoZWR1bGUgZm9yIHRoaXMgcmVzb3VyY2UvZGF0ZVxuICAgICAgICAgICAgY29uc3Qgc2NoZWR1bGUgPSBhd2FpdCB0aGlzLnNjaGVkdWxlU2VydmljZS5nZXRTY2hlZHVsZUZvckRhdGUocmVzb3VyY2VJZCwgZGF0ZSk7XG4gICAgICAgICAgICAvLyBSZW5kZXIgdW5hdmFpbGFibGUgem9uZXNcbiAgICAgICAgICAgIHRoaXMucmVuZGVyVW5hdmFpbGFibGVab25lcyh1bmF2YWlsYWJsZUxheWVyLCBzY2hlZHVsZSk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgLyoqXG4gICAgICogUmVuZGVyIHVuYXZhaWxhYmxlIHRpbWUgem9uZXMgYmFzZWQgb24gc2NoZWR1bGVcbiAgICAgKi9cbiAgICByZW5kZXJVbmF2YWlsYWJsZVpvbmVzKGxheWVyLCBzY2hlZHVsZSkge1xuICAgICAgICBjb25zdCBkYXlTdGFydE1pbnV0ZXMgPSB0aGlzLmdyaWRDb25maWcuZGF5U3RhcnRIb3VyICogNjA7XG4gICAgICAgIGNvbnN0IGRheUVuZE1pbnV0ZXMgPSB0aGlzLmdyaWRDb25maWcuZGF5RW5kSG91ciAqIDYwO1xuICAgICAgICBjb25zdCBtaW51dGVIZWlnaHQgPSB0aGlzLmdyaWRDb25maWcuaG91ckhlaWdodCAvIDYwO1xuICAgICAgICBpZiAoc2NoZWR1bGUgPT09IG51bGwpIHtcbiAgICAgICAgICAgIC8vIEZ1bGwgZGF5IHVuYXZhaWxhYmxlXG4gICAgICAgICAgICBjb25zdCB6b25lID0gdGhpcy5jcmVhdGVVbmF2YWlsYWJsZVpvbmUoMCwgKGRheUVuZE1pbnV0ZXMgLSBkYXlTdGFydE1pbnV0ZXMpICogbWludXRlSGVpZ2h0KTtcbiAgICAgICAgICAgIGxheWVyLmFwcGVuZENoaWxkKHpvbmUpO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHdvcmtTdGFydE1pbnV0ZXMgPSB0aGlzLmRhdGVTZXJ2aWNlLnRpbWVUb01pbnV0ZXMoc2NoZWR1bGUuc3RhcnQpO1xuICAgICAgICBjb25zdCB3b3JrRW5kTWludXRlcyA9IHRoaXMuZGF0ZVNlcnZpY2UudGltZVRvTWludXRlcyhzY2hlZHVsZS5lbmQpO1xuICAgICAgICAvLyBCZWZvcmUgd29yayBzdGFydFxuICAgICAgICBpZiAod29ya1N0YXJ0TWludXRlcyA+IGRheVN0YXJ0TWludXRlcykge1xuICAgICAgICAgICAgY29uc3QgdG9wID0gMDtcbiAgICAgICAgICAgIGNvbnN0IGhlaWdodCA9ICh3b3JrU3RhcnRNaW51dGVzIC0gZGF5U3RhcnRNaW51dGVzKSAqIG1pbnV0ZUhlaWdodDtcbiAgICAgICAgICAgIGNvbnN0IHpvbmUgPSB0aGlzLmNyZWF0ZVVuYXZhaWxhYmxlWm9uZSh0b3AsIGhlaWdodCk7XG4gICAgICAgICAgICBsYXllci5hcHBlbmRDaGlsZCh6b25lKTtcbiAgICAgICAgfVxuICAgICAgICAvLyBBZnRlciB3b3JrIGVuZFxuICAgICAgICBpZiAod29ya0VuZE1pbnV0ZXMgPCBkYXlFbmRNaW51dGVzKSB7XG4gICAgICAgICAgICBjb25zdCB0b3AgPSAod29ya0VuZE1pbnV0ZXMgLSBkYXlTdGFydE1pbnV0ZXMpICogbWludXRlSGVpZ2h0O1xuICAgICAgICAgICAgY29uc3QgaGVpZ2h0ID0gKGRheUVuZE1pbnV0ZXMgLSB3b3JrRW5kTWludXRlcykgKiBtaW51dGVIZWlnaHQ7XG4gICAgICAgICAgICBjb25zdCB6b25lID0gdGhpcy5jcmVhdGVVbmF2YWlsYWJsZVpvbmUodG9wLCBoZWlnaHQpO1xuICAgICAgICAgICAgbGF5ZXIuYXBwZW5kQ2hpbGQoem9uZSk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgLyoqXG4gICAgICogQ3JlYXRlIGFuIHVuYXZhaWxhYmxlIHpvbmUgZWxlbWVudFxuICAgICAqL1xuICAgIGNyZWF0ZVVuYXZhaWxhYmxlWm9uZSh0b3AsIGhlaWdodCkge1xuICAgICAgICBjb25zdCB6b25lID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3dwLXVuYXZhaWxhYmxlLXpvbmUnKTtcbiAgICAgICAgem9uZS5zdHlsZS50b3AgPSBgJHt0b3B9cHhgO1xuICAgICAgICB6b25lLnN0eWxlLmhlaWdodCA9IGAke2hlaWdodH1weGA7XG4gICAgICAgIHJldHVybiB6b25lO1xuICAgIH1cbn1cbiIsICJpbXBvcnQgeyBDb3JlRXZlbnRzIH0gZnJvbSAnLi4vLi4vY29uc3RhbnRzL0NvcmVFdmVudHMnO1xuLyoqXG4gKiBIZWFkZXJEcmF3ZXJSZW5kZXJlciAtIEhhbmRsZXMgcmVuZGVyaW5nIG9mIGl0ZW1zIGluIHRoZSBoZWFkZXIgZHJhd2VyXG4gKlxuICogTGlzdGVucyB0byBkcmFnIGV2ZW50cyBmcm9tIERyYWdEcm9wTWFuYWdlciBhbmQgY3JlYXRlcy9tYW5hZ2VzXG4gKiBzd3AtaGVhZGVyLWl0ZW0gZWxlbWVudHMgaW4gdGhlIGhlYWRlciBkcmF3ZXIuXG4gKlxuICogVXNlcyBzdWJncmlkIGZvciBjb2x1bW4gYWxpZ25tZW50IHdpdGggcGFyZW50IHN3cC1jYWxlbmRhci1oZWFkZXIuXG4gKiBQb3NpdGlvbiBpdGVtcyB2aWEgZ3JpZEFyZWEgZm9yIGV4cGxpY2l0IHJvdy9jb2x1bW4gcGxhY2VtZW50LlxuICovXG5leHBvcnQgY2xhc3MgSGVhZGVyRHJhd2VyUmVuZGVyZXIge1xuICAgIGNvbnN0cnVjdG9yKGV2ZW50QnVzLCBncmlkQ29uZmlnLCBoZWFkZXJEcmF3ZXJNYW5hZ2VyLCBldmVudFNlcnZpY2UsIGRhdGVTZXJ2aWNlKSB7XG4gICAgICAgIHRoaXMuZXZlbnRCdXMgPSBldmVudEJ1cztcbiAgICAgICAgdGhpcy5ncmlkQ29uZmlnID0gZ3JpZENvbmZpZztcbiAgICAgICAgdGhpcy5oZWFkZXJEcmF3ZXJNYW5hZ2VyID0gaGVhZGVyRHJhd2VyTWFuYWdlcjtcbiAgICAgICAgdGhpcy5ldmVudFNlcnZpY2UgPSBldmVudFNlcnZpY2U7XG4gICAgICAgIHRoaXMuZGF0ZVNlcnZpY2UgPSBkYXRlU2VydmljZTtcbiAgICAgICAgdGhpcy5jdXJyZW50SXRlbSA9IG51bGw7XG4gICAgICAgIHRoaXMuY29udGFpbmVyID0gbnVsbDtcbiAgICAgICAgdGhpcy5zb3VyY2VFbGVtZW50ID0gbnVsbDtcbiAgICAgICAgdGhpcy53YXNFeHBhbmRlZEJlZm9yZURyYWcgPSBmYWxzZTtcbiAgICAgICAgdGhpcy5maWx0ZXJUZW1wbGF0ZSA9IG51bGw7XG4gICAgICAgIHRoaXMuc2V0dXBMaXN0ZW5lcnMoKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogUmVuZGVyIGFsbERheSBldmVudHMgaW50byB0aGUgaGVhZGVyIGRyYXdlciB3aXRoIHJvdyBzdGFja2luZ1xuICAgICAqIEBwYXJhbSBmaWx0ZXJUZW1wbGF0ZSAtIFRlbXBsYXRlIGZvciBtYXRjaGluZyBldmVudHMgdG8gY29sdW1uc1xuICAgICAqL1xuICAgIGFzeW5jIHJlbmRlcihjb250YWluZXIsIGZpbHRlciwgZmlsdGVyVGVtcGxhdGUpIHtcbiAgICAgICAgLy8gU3RvcmUgZmlsdGVyVGVtcGxhdGUgZm9yIGJ1aWxkQ29sdW1uS2V5RnJvbUV2ZW50XG4gICAgICAgIHRoaXMuZmlsdGVyVGVtcGxhdGUgPSBmaWx0ZXJUZW1wbGF0ZTtcbiAgICAgICAgY29uc3QgZHJhd2VyID0gY29udGFpbmVyLnF1ZXJ5U2VsZWN0b3IoJ3N3cC1oZWFkZXItZHJhd2VyJyk7XG4gICAgICAgIGlmICghZHJhd2VyKVxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICBjb25zdCB2aXNpYmxlRGF0ZXMgPSBmaWx0ZXJbJ2RhdGUnXSB8fCBbXTtcbiAgICAgICAgaWYgKHZpc2libGVEYXRlcy5sZW5ndGggPT09IDApXG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIC8vIEdldCBjb2x1bW4ga2V5cyBmcm9tIERPTSBmb3IgY29ycmVjdCBtdWx0aS1yZXNvdXJjZSBwb3NpdGlvbmluZ1xuICAgICAgICBjb25zdCB2aXNpYmxlQ29sdW1uS2V5cyA9IHRoaXMuZ2V0VmlzaWJsZUNvbHVtbktleXNGcm9tRE9NKCk7XG4gICAgICAgIGlmICh2aXNpYmxlQ29sdW1uS2V5cy5sZW5ndGggPT09IDApXG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIC8vIEZldGNoIGV2ZW50cyBmb3IgZGF0ZSByYW5nZVxuICAgICAgICBjb25zdCBzdGFydERhdGUgPSBuZXcgRGF0ZSh2aXNpYmxlRGF0ZXNbMF0pO1xuICAgICAgICBjb25zdCBlbmREYXRlID0gbmV3IERhdGUodmlzaWJsZURhdGVzW3Zpc2libGVEYXRlcy5sZW5ndGggLSAxXSk7XG4gICAgICAgIGVuZERhdGUuc2V0SG91cnMoMjMsIDU5LCA1OSwgOTk5KTtcbiAgICAgICAgY29uc3QgZXZlbnRzID0gYXdhaXQgdGhpcy5ldmVudFNlcnZpY2UuZ2V0QnlEYXRlUmFuZ2Uoc3RhcnREYXRlLCBlbmREYXRlKTtcbiAgICAgICAgLy8gRmlsdGVyIHRvIGFsbERheSBldmVudHMgb25seSAoYWxsRGF5ICE9PSBmYWxzZSlcbiAgICAgICAgY29uc3QgYWxsRGF5RXZlbnRzID0gZXZlbnRzLmZpbHRlcihldmVudCA9PiBldmVudC5hbGxEYXkgIT09IGZhbHNlKTtcbiAgICAgICAgLy8gQ2xlYXIgZXhpc3RpbmcgaXRlbXNcbiAgICAgICAgZHJhd2VyLmlubmVySFRNTCA9ICcnO1xuICAgICAgICBpZiAoYWxsRGF5RXZlbnRzLmxlbmd0aCA9PT0gMClcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgLy8gQ2FsY3VsYXRlIGxheW91dCB3aXRoIHJvdyBzdGFja2luZyB1c2luZyBjb2x1bW5LZXlzXG4gICAgICAgIGNvbnN0IGxheW91dHMgPSB0aGlzLmNhbGN1bGF0ZUxheW91dChhbGxEYXlFdmVudHMsIHZpc2libGVDb2x1bW5LZXlzKTtcbiAgICAgICAgY29uc3Qgcm93Q291bnQgPSBNYXRoLm1heCgxLCAuLi5sYXlvdXRzLm1hcChsID0+IGwucm93KSk7XG4gICAgICAgIC8vIFJlbmRlciBlYWNoIGl0ZW0gd2l0aCBsYXlvdXRcbiAgICAgICAgbGF5b3V0cy5mb3JFYWNoKGxheW91dCA9PiB7XG4gICAgICAgICAgICBjb25zdCBpdGVtID0gdGhpcy5jcmVhdGVIZWFkZXJJdGVtKGxheW91dCk7XG4gICAgICAgICAgICBkcmF3ZXIuYXBwZW5kQ2hpbGQoaXRlbSk7XG4gICAgICAgIH0pO1xuICAgICAgICAvLyBFeHBhbmQgZHJhd2VyIHRvIGZpdCBhbGwgcm93c1xuICAgICAgICB0aGlzLmhlYWRlckRyYXdlck1hbmFnZXIuZXhwYW5kVG9Sb3dzKHJvd0NvdW50KTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogQ3JlYXRlIGEgaGVhZGVyIGl0ZW0gZWxlbWVudCBmcm9tIGxheW91dFxuICAgICAqL1xuICAgIGNyZWF0ZUhlYWRlckl0ZW0obGF5b3V0KSB7XG4gICAgICAgIGNvbnN0IHsgZXZlbnQsIGNvbHVtbktleSwgcm93LCBjb2xTdGFydCwgY29sRW5kIH0gPSBsYXlvdXQ7XG4gICAgICAgIGNvbnN0IGl0ZW0gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzd3AtaGVhZGVyLWl0ZW0nKTtcbiAgICAgICAgaXRlbS5kYXRhc2V0LmV2ZW50SWQgPSBldmVudC5pZDtcbiAgICAgICAgaXRlbS5kYXRhc2V0Lml0ZW1UeXBlID0gJ2V2ZW50JztcbiAgICAgICAgaXRlbS5kYXRhc2V0LnN0YXJ0ID0gZXZlbnQuc3RhcnQudG9JU09TdHJpbmcoKTtcbiAgICAgICAgaXRlbS5kYXRhc2V0LmVuZCA9IGV2ZW50LmVuZC50b0lTT1N0cmluZygpO1xuICAgICAgICBpdGVtLmRhdGFzZXQuY29sdW1uS2V5ID0gY29sdW1uS2V5O1xuICAgICAgICBpdGVtLnRleHRDb250ZW50ID0gZXZlbnQudGl0bGU7XG4gICAgICAgIC8vIENvbG9yIGNsYXNzXG4gICAgICAgIGNvbnN0IGNvbG9yQ2xhc3MgPSB0aGlzLmdldENvbG9yQ2xhc3MoZXZlbnQpO1xuICAgICAgICBpZiAoY29sb3JDbGFzcylcbiAgICAgICAgICAgIGl0ZW0uY2xhc3NMaXN0LmFkZChjb2xvckNsYXNzKTtcbiAgICAgICAgLy8gR3JpZCBwb3NpdGlvbiBmcm9tIGxheW91dFxuICAgICAgICBpdGVtLnN0eWxlLmdyaWRBcmVhID0gYCR7cm93fSAvICR7Y29sU3RhcnR9IC8gJHtyb3cgKyAxfSAvICR7Y29sRW5kfWA7XG4gICAgICAgIHJldHVybiBpdGVtO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBDYWxjdWxhdGUgbGF5b3V0IGZvciBhbGwgZXZlbnRzIHdpdGggcm93IHN0YWNraW5nXG4gICAgICogVXNlcyB0cmFjay1iYXNlZCBhbGdvcml0aG0gdG8gZmluZCBhdmFpbGFibGUgcm93cyBmb3Igb3ZlcmxhcHBpbmcgZXZlbnRzXG4gICAgICovXG4gICAgY2FsY3VsYXRlTGF5b3V0KGV2ZW50cywgdmlzaWJsZUNvbHVtbktleXMpIHtcbiAgICAgICAgLy8gdHJhY2tzW3Jvd11bY29sXSA9IG9jY3VwaWVkXG4gICAgICAgIGNvbnN0IHRyYWNrcyA9IFtuZXcgQXJyYXkodmlzaWJsZUNvbHVtbktleXMubGVuZ3RoKS5maWxsKGZhbHNlKV07XG4gICAgICAgIGNvbnN0IGxheW91dHMgPSBbXTtcbiAgICAgICAgZm9yIChjb25zdCBldmVudCBvZiBldmVudHMpIHtcbiAgICAgICAgICAgIC8vIEJ1aWxkIGNvbHVtbktleSBmcm9tIGV2ZW50IGZpZWxkcyAob25seSBwbGFjZSB3ZSBuZWVkIHRvIGNvbnN0cnVjdCBpdClcbiAgICAgICAgICAgIGNvbnN0IGNvbHVtbktleSA9IHRoaXMuYnVpbGRDb2x1bW5LZXlGcm9tRXZlbnQoZXZlbnQpO1xuICAgICAgICAgICAgY29uc3Qgc3RhcnRDb2wgPSB2aXNpYmxlQ29sdW1uS2V5cy5pbmRleE9mKGNvbHVtbktleSk7XG4gICAgICAgICAgICBjb25zdCBlbmRDb2x1bW5LZXkgPSB0aGlzLmJ1aWxkQ29sdW1uS2V5RnJvbUV2ZW50KGV2ZW50LCBldmVudC5lbmQpO1xuICAgICAgICAgICAgY29uc3QgZW5kQ29sID0gdmlzaWJsZUNvbHVtbktleXMuaW5kZXhPZihlbmRDb2x1bW5LZXkpO1xuICAgICAgICAgICAgaWYgKHN0YXJ0Q29sID09PSAtMSAmJiBlbmRDb2wgPT09IC0xKVxuICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgICAgLy8gQ2xhbXAgdGlsIHN5bmxpZ2Uga29sb25uZXJcbiAgICAgICAgICAgIGNvbnN0IGNvbFN0YXJ0ID0gTWF0aC5tYXgoMCwgc3RhcnRDb2wpO1xuICAgICAgICAgICAgY29uc3QgY29sRW5kID0gKGVuZENvbCAhPT0gLTEgPyBlbmRDb2wgOiB2aXNpYmxlQ29sdW1uS2V5cy5sZW5ndGggLSAxKSArIDE7XG4gICAgICAgICAgICAvLyBGaW5kIGxlZGlnIHJcdTAwRTZra2VcbiAgICAgICAgICAgIGNvbnN0IHJvdyA9IHRoaXMuZmluZEF2YWlsYWJsZVJvdyh0cmFja3MsIGNvbFN0YXJ0LCBjb2xFbmQpO1xuICAgICAgICAgICAgLy8gTWFya2VyIHNvbSBvcHRhZ2V0XG4gICAgICAgICAgICBmb3IgKGxldCBjID0gY29sU3RhcnQ7IGMgPCBjb2xFbmQ7IGMrKykge1xuICAgICAgICAgICAgICAgIHRyYWNrc1tyb3ddW2NdID0gdHJ1ZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGxheW91dHMucHVzaCh7IGV2ZW50LCBjb2x1bW5LZXksIHJvdzogcm93ICsgMSwgY29sU3RhcnQ6IGNvbFN0YXJ0ICsgMSwgY29sRW5kOiBjb2xFbmQgKyAxIH0pO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBsYXlvdXRzO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBCdWlsZCBjb2x1bW5LZXkgZnJvbSBldmVudCB1c2luZyBGaWx0ZXJUZW1wbGF0ZVxuICAgICAqIFVzZXMgdGhlIHNhbWUgdGVtcGxhdGUgdGhhdCBjb2x1bW5zIHVzZSBmb3IgbWF0Y2hpbmdcbiAgICAgKi9cbiAgICBidWlsZENvbHVtbktleUZyb21FdmVudChldmVudCwgZGF0ZSkge1xuICAgICAgICBpZiAoIXRoaXMuZmlsdGVyVGVtcGxhdGUpIHtcbiAgICAgICAgICAgIC8vIEZhbGxiYWNrIGlmIG5vIHRlbXBsYXRlIC0gc2hvdWxkbid0IGhhcHBlbiBpbiBub3JtYWwgZmxvd1xuICAgICAgICAgICAgY29uc3QgZGF0ZVN0ciA9IHRoaXMuZGF0ZVNlcnZpY2UuZ2V0RGF0ZUtleShkYXRlIHx8IGV2ZW50LnN0YXJ0KTtcbiAgICAgICAgICAgIHJldHVybiBkYXRlU3RyO1xuICAgICAgICB9XG4gICAgICAgIC8vIEZvciBtdWx0aS1kYXkgZXZlbnRzLCB3ZSBuZWVkIHRvIG92ZXJyaWRlIHRoZSBkYXRlIGluIHRoZSBldmVudFxuICAgICAgICBpZiAoZGF0ZSAmJiBkYXRlLmdldFRpbWUoKSAhPT0gZXZlbnQuc3RhcnQuZ2V0VGltZSgpKSB7XG4gICAgICAgICAgICAvLyBDcmVhdGUgdGVtcG9yYXJ5IGV2ZW50IHdpdGggb3ZlcnJpZGRlbiBzdGFydCBmb3Iga2V5IGdlbmVyYXRpb25cbiAgICAgICAgICAgIGNvbnN0IHRlbXBFdmVudCA9IHsgLi4uZXZlbnQsIHN0YXJ0OiBkYXRlIH07XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5maWx0ZXJUZW1wbGF0ZS5idWlsZEtleUZyb21FdmVudCh0ZW1wRXZlbnQpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0aGlzLmZpbHRlclRlbXBsYXRlLmJ1aWxkS2V5RnJvbUV2ZW50KGV2ZW50KTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogRmluZCBhdmFpbGFibGUgcm93IGZvciBldmVudCBzcGFubmluZyBjb2x1bW5zIFtjb2xTdGFydCwgY29sRW5kKVxuICAgICAqL1xuICAgIGZpbmRBdmFpbGFibGVSb3codHJhY2tzLCBjb2xTdGFydCwgY29sRW5kKSB7XG4gICAgICAgIGZvciAobGV0IHJvdyA9IDA7IHJvdyA8IHRyYWNrcy5sZW5ndGg7IHJvdysrKSB7XG4gICAgICAgICAgICBsZXQgYXZhaWxhYmxlID0gdHJ1ZTtcbiAgICAgICAgICAgIGZvciAobGV0IGMgPSBjb2xTdGFydDsgYyA8IGNvbEVuZDsgYysrKSB7XG4gICAgICAgICAgICAgICAgaWYgKHRyYWNrc1tyb3ddW2NdKSB7XG4gICAgICAgICAgICAgICAgICAgIGF2YWlsYWJsZSA9IGZhbHNlO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAoYXZhaWxhYmxlKVxuICAgICAgICAgICAgICAgIHJldHVybiByb3c7XG4gICAgICAgIH1cbiAgICAgICAgLy8gTnkgclx1MDBFNmtrZVxuICAgICAgICB0cmFja3MucHVzaChuZXcgQXJyYXkodHJhY2tzWzBdLmxlbmd0aCkuZmlsbChmYWxzZSkpO1xuICAgICAgICByZXR1cm4gdHJhY2tzLmxlbmd0aCAtIDE7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEdldCBjb2xvciBjbGFzcyBiYXNlZCBvbiBldmVudCBtZXRhZGF0YSBvciB0eXBlXG4gICAgICovXG4gICAgZ2V0Q29sb3JDbGFzcyhldmVudCkge1xuICAgICAgICBpZiAoZXZlbnQubWV0YWRhdGE/LmNvbG9yKSB7XG4gICAgICAgICAgICByZXR1cm4gYGlzLSR7ZXZlbnQubWV0YWRhdGEuY29sb3J9YDtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCB0eXBlQ29sb3JzID0ge1xuICAgICAgICAgICAgJ2N1c3RvbWVyJzogJ2lzLWJsdWUnLFxuICAgICAgICAgICAgJ3ZhY2F0aW9uJzogJ2lzLWdyZWVuJyxcbiAgICAgICAgICAgICdicmVhayc6ICdpcy1hbWJlcicsXG4gICAgICAgICAgICAnbWVldGluZyc6ICdpcy1wdXJwbGUnLFxuICAgICAgICAgICAgJ2Jsb2NrZWQnOiAnaXMtcmVkJ1xuICAgICAgICB9O1xuICAgICAgICByZXR1cm4gdHlwZUNvbG9yc1tldmVudC50eXBlXSB8fCAnaXMtYmx1ZSc7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFNldHVwIGV2ZW50IGxpc3RlbmVycyBmb3IgZHJhZyBldmVudHNcbiAgICAgKi9cbiAgICBzZXR1cExpc3RlbmVycygpIHtcbiAgICAgICAgdGhpcy5ldmVudEJ1cy5vbihDb3JlRXZlbnRzLkVWRU5UX0RSQUdfRU5URVJfSEVBREVSLCAoZSkgPT4ge1xuICAgICAgICAgICAgY29uc3QgcGF5bG9hZCA9IGUuZGV0YWlsO1xuICAgICAgICAgICAgdGhpcy5oYW5kbGVEcmFnRW50ZXIocGF5bG9hZCk7XG4gICAgICAgIH0pO1xuICAgICAgICB0aGlzLmV2ZW50QnVzLm9uKENvcmVFdmVudHMuRVZFTlRfRFJBR19NT1ZFX0hFQURFUiwgKGUpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHBheWxvYWQgPSBlLmRldGFpbDtcbiAgICAgICAgICAgIHRoaXMuaGFuZGxlRHJhZ01vdmUocGF5bG9hZCk7XG4gICAgICAgIH0pO1xuICAgICAgICB0aGlzLmV2ZW50QnVzLm9uKENvcmVFdmVudHMuRVZFTlRfRFJBR19MRUFWRV9IRUFERVIsIChlKSA9PiB7XG4gICAgICAgICAgICBjb25zdCBwYXlsb2FkID0gZS5kZXRhaWw7XG4gICAgICAgICAgICB0aGlzLmhhbmRsZURyYWdMZWF2ZShwYXlsb2FkKTtcbiAgICAgICAgfSk7XG4gICAgICAgIHRoaXMuZXZlbnRCdXMub24oQ29yZUV2ZW50cy5FVkVOVF9EUkFHX0VORCwgKGUpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHBheWxvYWQgPSBlLmRldGFpbDtcbiAgICAgICAgICAgIHRoaXMuaGFuZGxlRHJhZ0VuZChwYXlsb2FkKTtcbiAgICAgICAgfSk7XG4gICAgICAgIHRoaXMuZXZlbnRCdXMub24oQ29yZUV2ZW50cy5FVkVOVF9EUkFHX0NBTkNFTCwgKCkgPT4ge1xuICAgICAgICAgICAgdGhpcy5jbGVhbnVwKCk7XG4gICAgICAgIH0pO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBIYW5kbGUgZHJhZyBlbnRlcmluZyBoZWFkZXIgem9uZSAtIGNyZWF0ZSBwcmV2aWV3IGl0ZW1cbiAgICAgKi9cbiAgICBoYW5kbGVEcmFnRW50ZXIocGF5bG9hZCkge1xuICAgICAgICB0aGlzLmNvbnRhaW5lciA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJ3N3cC1oZWFkZXItZHJhd2VyJyk7XG4gICAgICAgIGlmICghdGhpcy5jb250YWluZXIpXG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIC8vIFJlbWVtYmVyIGlmIGRyYXdlciB3YXMgYWxyZWFkeSBleHBhbmRlZFxuICAgICAgICB0aGlzLndhc0V4cGFuZGVkQmVmb3JlRHJhZyA9IHRoaXMuaGVhZGVyRHJhd2VyTWFuYWdlci5pc0V4cGFuZGVkKCk7XG4gICAgICAgIC8vIEV4cGFuZCB0byBhdCBsZWFzdCAxIHJvdyBpZiBjb2xsYXBzZWQsIG90aGVyd2lzZSBrZWVwIGN1cnJlbnQgaGVpZ2h0XG4gICAgICAgIGlmICghdGhpcy53YXNFeHBhbmRlZEJlZm9yZURyYWcpIHtcbiAgICAgICAgICAgIHRoaXMuaGVhZGVyRHJhd2VyTWFuYWdlci5leHBhbmRUb1Jvd3MoMSk7XG4gICAgICAgIH1cbiAgICAgICAgLy8gU3RvcmUgcmVmZXJlbmNlIHRvIHNvdXJjZSBlbGVtZW50XG4gICAgICAgIHRoaXMuc291cmNlRWxlbWVudCA9IHBheWxvYWQuZWxlbWVudDtcbiAgICAgICAgLy8gQ3JlYXRlIGhlYWRlciBpdGVtXG4gICAgICAgIGNvbnN0IGl0ZW0gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzd3AtaGVhZGVyLWl0ZW0nKTtcbiAgICAgICAgaXRlbS5kYXRhc2V0LmV2ZW50SWQgPSBwYXlsb2FkLmV2ZW50SWQ7XG4gICAgICAgIGl0ZW0uZGF0YXNldC5pdGVtVHlwZSA9IHBheWxvYWQuaXRlbVR5cGU7XG4gICAgICAgIGl0ZW0uZGF0YXNldC5kdXJhdGlvbiA9IFN0cmluZyhwYXlsb2FkLmR1cmF0aW9uKTtcbiAgICAgICAgaXRlbS5kYXRhc2V0LmNvbHVtbktleSA9IHBheWxvYWQuc291cmNlQ29sdW1uS2V5O1xuICAgICAgICBpdGVtLnRleHRDb250ZW50ID0gcGF5bG9hZC50aXRsZTtcbiAgICAgICAgLy8gQXBwbHkgY29sb3IgY2xhc3MgaWYgcHJlc2VudFxuICAgICAgICBpZiAocGF5bG9hZC5jb2xvckNsYXNzKSB7XG4gICAgICAgICAgICBpdGVtLmNsYXNzTGlzdC5hZGQocGF5bG9hZC5jb2xvckNsYXNzKTtcbiAgICAgICAgfVxuICAgICAgICAvLyBBZGQgZHJhZ2dpbmcgc3RhdGVcbiAgICAgICAgaXRlbS5jbGFzc0xpc3QuYWRkKCdkcmFnZ2luZycpO1xuICAgICAgICAvLyBJbml0aWFsIHBsYWNlbWVudCAoZHVyYXRpb24gZGV0ZXJtaW5lcyBjb2x1bW4gc3BhbilcbiAgICAgICAgLy8gZ3JpZEFyZWEgZm9ybWF0OiBcInJvdyAvIGNvbC1zdGFydCAvIHJvdysxIC8gY29sLWVuZFwiXG4gICAgICAgIGNvbnN0IGNvbCA9IHBheWxvYWQuc291cmNlQ29sdW1uSW5kZXggKyAxO1xuICAgICAgICBjb25zdCBlbmRDb2wgPSBjb2wgKyBwYXlsb2FkLmR1cmF0aW9uO1xuICAgICAgICBpdGVtLnN0eWxlLmdyaWRBcmVhID0gYDEgLyAke2NvbH0gLyAyIC8gJHtlbmRDb2x9YDtcbiAgICAgICAgdGhpcy5jb250YWluZXIuYXBwZW5kQ2hpbGQoaXRlbSk7XG4gICAgICAgIHRoaXMuY3VycmVudEl0ZW0gPSBpdGVtO1xuICAgICAgICAvLyBIaWRlIG9yaWdpbmFsIGVsZW1lbnQgd2hpbGUgaW4gaGVhZGVyXG4gICAgICAgIHBheWxvYWQuZWxlbWVudC5zdHlsZS52aXNpYmlsaXR5ID0gJ2hpZGRlbic7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEhhbmRsZSBkcmFnIG1vdmluZyB3aXRoaW4gaGVhZGVyIC0gdXBkYXRlIGNvbHVtbiBwb3NpdGlvblxuICAgICAqL1xuICAgIGhhbmRsZURyYWdNb3ZlKHBheWxvYWQpIHtcbiAgICAgICAgaWYgKCF0aGlzLmN1cnJlbnRJdGVtKVxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAvLyBVcGRhdGUgY29sdW1uIHBvc2l0aW9uXG4gICAgICAgIGNvbnN0IGNvbCA9IHBheWxvYWQuY29sdW1uSW5kZXggKyAxO1xuICAgICAgICBjb25zdCBkdXJhdGlvbiA9IHBhcnNlSW50KHRoaXMuY3VycmVudEl0ZW0uZGF0YXNldC5kdXJhdGlvbiB8fCAnMScsIDEwKTtcbiAgICAgICAgY29uc3QgZW5kQ29sID0gY29sICsgZHVyYXRpb247XG4gICAgICAgIHRoaXMuY3VycmVudEl0ZW0uc3R5bGUuZ3JpZEFyZWEgPSBgMSAvICR7Y29sfSAvIDIgLyAke2VuZENvbH1gO1xuICAgICAgICAvLyBVcGRhdGUgY29sdW1uS2V5IHRvIG5ldyBwb3NpdGlvblxuICAgICAgICB0aGlzLmN1cnJlbnRJdGVtLmRhdGFzZXQuY29sdW1uS2V5ID0gcGF5bG9hZC5jb2x1bW5LZXk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEhhbmRsZSBkcmFnIGxlYXZpbmcgaGVhZGVyIC0gY2xlYW51cCBmb3IgZ3JpZFx1MjE5MmhlYWRlciBkcmFnIG9ubHlcbiAgICAgKi9cbiAgICBoYW5kbGVEcmFnTGVhdmUocGF5bG9hZCkge1xuICAgICAgICAvLyBPbmx5IGNsZWFudXAgZm9yIGdyaWRcdTIxOTJoZWFkZXIgZHJhZyAod2hlbiBncmlkIGV2ZW50IGxlYXZlcyBoZWFkZXIgYmFjayB0byBncmlkKVxuICAgICAgICAvLyBGb3IgaGVhZGVyXHUyMTkyZ3JpZCBkcmFnLCB0aGUgaGVhZGVyIGl0ZW0gc3RheXMgYXMgZ2hvc3QgdW50aWwgZHJvcFxuICAgICAgICBpZiAocGF5bG9hZC5zb3VyY2UgPT09ICdncmlkJykge1xuICAgICAgICAgICAgdGhpcy5jbGVhbnVwKCk7XG4gICAgICAgIH1cbiAgICAgICAgLy8gRm9yIGhlYWRlciBzb3VyY2UsIGRvIG5vdGhpbmcgLSBnaG9zdCBzdGF5cyB1bnRpbCBFVkVOVF9EUkFHX0VORFxuICAgIH1cbiAgICAvKipcbiAgICAgKiBIYW5kbGUgZHJhZyBlbmQgLSBmaW5hbGl6ZSBiYXNlZCBvbiBkcm9wIHRhcmdldFxuICAgICAqL1xuICAgIGhhbmRsZURyYWdFbmQocGF5bG9hZCkge1xuICAgICAgICBpZiAocGF5bG9hZC50YXJnZXQgPT09ICdoZWFkZXInKSB7XG4gICAgICAgICAgICAvLyBHcmlkXHUyMTkySGVhZGVyOiBGaW5hbGl6ZSB0aGUgaGVhZGVyIGl0ZW0gKGl0IHN0YXlzIGluIGhlYWRlcilcbiAgICAgICAgICAgIGlmICh0aGlzLmN1cnJlbnRJdGVtKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5jdXJyZW50SXRlbS5jbGFzc0xpc3QucmVtb3ZlKCdkcmFnZ2luZycpO1xuICAgICAgICAgICAgICAgIHRoaXMucmVjYWxjdWxhdGVEcmF3ZXJMYXlvdXQoKTtcbiAgICAgICAgICAgICAgICB0aGlzLmN1cnJlbnRJdGVtID0gbnVsbDtcbiAgICAgICAgICAgICAgICB0aGlzLnNvdXJjZUVsZW1lbnQgPSBudWxsO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgLy8gSGVhZGVyXHUyMTkyR3JpZDogUmVtb3ZlIGdob3N0IGhlYWRlciBpdGVtIGFuZCByZWNhbGN1bGF0ZVxuICAgICAgICAgICAgY29uc3QgZ2hvc3QgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKGBzd3AtaGVhZGVyLWl0ZW0uZHJhZy1naG9zdFtkYXRhLWV2ZW50LWlkPVwiJHtwYXlsb2FkLnN3cEV2ZW50LmV2ZW50SWR9XCJdYCk7XG4gICAgICAgICAgICBnaG9zdD8ucmVtb3ZlKCk7XG4gICAgICAgICAgICB0aGlzLnJlY2FsY3VsYXRlRHJhd2VyTGF5b3V0KCk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgLyoqXG4gICAgICogUmVjYWxjdWxhdGUgbGF5b3V0IGZvciBhbGwgaXRlbXMgY3VycmVudGx5IGluIHRoZSBkcmF3ZXJcbiAgICAgKiBDYWxsZWQgYWZ0ZXIgZHJvcCB0byByZXBvc2l0aW9uIGl0ZW1zIGFuZCBhZGp1c3QgaGVpZ2h0XG4gICAgICovXG4gICAgcmVjYWxjdWxhdGVEcmF3ZXJMYXlvdXQoKSB7XG4gICAgICAgIGNvbnN0IGRyYXdlciA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJ3N3cC1oZWFkZXItZHJhd2VyJyk7XG4gICAgICAgIGlmICghZHJhd2VyKVxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICBjb25zdCBpdGVtcyA9IEFycmF5LmZyb20oZHJhd2VyLnF1ZXJ5U2VsZWN0b3JBbGwoJ3N3cC1oZWFkZXItaXRlbScpKTtcbiAgICAgICAgaWYgKGl0ZW1zLmxlbmd0aCA9PT0gMClcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgLy8gR2V0IHZpc2libGUgY29sdW1uIGtleXMgZm9yIGNvcnJlY3QgbXVsdGktcmVzb3VyY2UgcG9zaXRpb25pbmdcbiAgICAgICAgY29uc3QgdmlzaWJsZUNvbHVtbktleXMgPSB0aGlzLmdldFZpc2libGVDb2x1bW5LZXlzRnJvbURPTSgpO1xuICAgICAgICBpZiAodmlzaWJsZUNvbHVtbktleXMubGVuZ3RoID09PSAwKVxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAvLyBCdWlsZCBsYXlvdXQgZGF0YSBmcm9tIERPTSBpdGVtcyAtIHVzZSBjb2x1bW5LZXkgZGlyZWN0bHkgKG9wYXF1ZSBtYXRjaGluZylcbiAgICAgICAgY29uc3QgaXRlbURhdGEgPSBpdGVtcy5tYXAoaXRlbSA9PiAoe1xuICAgICAgICAgICAgZWxlbWVudDogaXRlbSxcbiAgICAgICAgICAgIGNvbHVtbktleTogaXRlbS5kYXRhc2V0LmNvbHVtbktleSB8fCAnJyxcbiAgICAgICAgICAgIGR1cmF0aW9uOiBwYXJzZUludChpdGVtLmRhdGFzZXQuZHVyYXRpb24gfHwgJzEnLCAxMClcbiAgICAgICAgfSkpO1xuICAgICAgICAvLyBDYWxjdWxhdGUgbmV3IGxheW91dCB1c2luZyB0cmFjayBhbGdvcml0aG1cbiAgICAgICAgY29uc3QgdHJhY2tzID0gW25ldyBBcnJheSh2aXNpYmxlQ29sdW1uS2V5cy5sZW5ndGgpLmZpbGwoZmFsc2UpXTtcbiAgICAgICAgZm9yIChjb25zdCBpdGVtIG9mIGl0ZW1EYXRhKSB7XG4gICAgICAgICAgICAvLyBEaXJlY3QgY29sdW1uS2V5IG1hdGNoaW5nIC0gbm8gcGFyc2luZyBvciBjb25zdHJ1Y3Rpb24gbmVlZGVkXG4gICAgICAgICAgICBjb25zdCBzdGFydENvbCA9IHZpc2libGVDb2x1bW5LZXlzLmluZGV4T2YoaXRlbS5jb2x1bW5LZXkpO1xuICAgICAgICAgICAgaWYgKHN0YXJ0Q29sID09PSAtMSlcbiAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgIGNvbnN0IGNvbFN0YXJ0ID0gc3RhcnRDb2w7XG4gICAgICAgICAgICBjb25zdCBjb2xFbmQgPSBNYXRoLm1pbihzdGFydENvbCArIGl0ZW0uZHVyYXRpb24sIHZpc2libGVDb2x1bW5LZXlzLmxlbmd0aCk7XG4gICAgICAgICAgICBjb25zdCByb3cgPSB0aGlzLmZpbmRBdmFpbGFibGVSb3codHJhY2tzLCBjb2xTdGFydCwgY29sRW5kKTtcbiAgICAgICAgICAgIGZvciAobGV0IGMgPSBjb2xTdGFydDsgYyA8IGNvbEVuZDsgYysrKSB7XG4gICAgICAgICAgICAgICAgdHJhY2tzW3Jvd11bY10gPSB0cnVlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgLy8gVXBkYXRlIGVsZW1lbnQgcG9zaXRpb25cbiAgICAgICAgICAgIGl0ZW0uZWxlbWVudC5zdHlsZS5ncmlkQXJlYSA9IGAke3JvdyArIDF9IC8gJHtjb2xTdGFydCArIDF9IC8gJHtyb3cgKyAyfSAvICR7Y29sRW5kICsgMX1gO1xuICAgICAgICB9XG4gICAgICAgIC8vIFVwZGF0ZSBkcmF3ZXIgaGVpZ2h0XG4gICAgICAgIGNvbnN0IHJvd0NvdW50ID0gdHJhY2tzLmxlbmd0aDtcbiAgICAgICAgdGhpcy5oZWFkZXJEcmF3ZXJNYW5hZ2VyLmV4cGFuZFRvUm93cyhyb3dDb3VudCk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEdldCB2aXNpYmxlIGNvbHVtbiBrZXlzIGZyb20gRE9NIChwcmVzZXJ2ZXMgb3JkZXIgZm9yIG11bHRpLXJlc291cmNlIHZpZXdzKVxuICAgICAqIFVzZXMgZmlsdGVyVGVtcGxhdGUuYnVpbGRLZXlGcm9tQ29sdW1uKCkgZm9yIGNvbnNpc3RlbnQga2V5IGZvcm1hdCB3aXRoIGV2ZW50c1xuICAgICAqL1xuICAgIGdldFZpc2libGVDb2x1bW5LZXlzRnJvbURPTSgpIHtcbiAgICAgICAgaWYgKCF0aGlzLmZpbHRlclRlbXBsYXRlKVxuICAgICAgICAgICAgcmV0dXJuIFtdO1xuICAgICAgICBjb25zdCBjb2x1bW5zID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCgnc3dwLWRheS1jb2x1bW4nKTtcbiAgICAgICAgY29uc3QgY29sdW1uS2V5cyA9IFtdO1xuICAgICAgICBjb2x1bW5zLmZvckVhY2goY29sID0+IHtcbiAgICAgICAgICAgIGNvbnN0IGNvbHVtbktleSA9IHRoaXMuZmlsdGVyVGVtcGxhdGUuYnVpbGRLZXlGcm9tQ29sdW1uKGNvbCk7XG4gICAgICAgICAgICBpZiAoY29sdW1uS2V5KVxuICAgICAgICAgICAgICAgIGNvbHVtbktleXMucHVzaChjb2x1bW5LZXkpO1xuICAgICAgICB9KTtcbiAgICAgICAgcmV0dXJuIGNvbHVtbktleXM7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIENsZWFudXAgcHJldmlldyBpdGVtIGFuZCByZXN0b3JlIHNvdXJjZSB2aXNpYmlsaXR5XG4gICAgICovXG4gICAgY2xlYW51cCgpIHtcbiAgICAgICAgLy8gUmVtb3ZlIHByZXZpZXcgaXRlbVxuICAgICAgICB0aGlzLmN1cnJlbnRJdGVtPy5yZW1vdmUoKTtcbiAgICAgICAgdGhpcy5jdXJyZW50SXRlbSA9IG51bGw7XG4gICAgICAgIC8vIFJlc3RvcmUgc291cmNlIGVsZW1lbnQgdmlzaWJpbGl0eVxuICAgICAgICBpZiAodGhpcy5zb3VyY2VFbGVtZW50KSB7XG4gICAgICAgICAgICB0aGlzLnNvdXJjZUVsZW1lbnQuc3R5bGUudmlzaWJpbGl0eSA9ICcnO1xuICAgICAgICAgICAgdGhpcy5zb3VyY2VFbGVtZW50ID0gbnVsbDtcbiAgICAgICAgfVxuICAgICAgICAvLyBDb2xsYXBzZSBkcmF3ZXIgaWYgaXQgd2Fzbid0IGV4cGFuZGVkIGJlZm9yZSBkcmFnXG4gICAgICAgIGlmICghdGhpcy53YXNFeHBhbmRlZEJlZm9yZURyYWcpIHtcbiAgICAgICAgICAgIHRoaXMuaGVhZGVyRHJhd2VyTWFuYWdlci5jb2xsYXBzZSgpO1xuICAgICAgICB9XG4gICAgfVxufVxuIiwgIi8qKlxuICogU2NoZWR1bGVPdmVycmlkZVN0b3JlIC0gSW5kZXhlZERCIE9iamVjdFN0b3JlIGZvciBzY2hlZHVsZSBvdmVycmlkZXNcbiAqXG4gKiBTdG9yZXMgZGF0ZS1zcGVjaWZpYyBzY2hlZHVsZSBvdmVycmlkZXMgZm9yIHJlc291cmNlcy5cbiAqIEluZGV4ZXM6IHJlc291cmNlSWQsIGRhdGUsIGNvbXBvdW5kIChyZXNvdXJjZUlkICsgZGF0ZSlcbiAqL1xuZXhwb3J0IGNsYXNzIFNjaGVkdWxlT3ZlcnJpZGVTdG9yZSB7XG4gICAgY29uc3RydWN0b3IoKSB7XG4gICAgICAgIHRoaXMuc3RvcmVOYW1lID0gU2NoZWR1bGVPdmVycmlkZVN0b3JlLlNUT1JFX05BTUU7XG4gICAgfVxuICAgIGNyZWF0ZShkYikge1xuICAgICAgICBjb25zdCBzdG9yZSA9IGRiLmNyZWF0ZU9iamVjdFN0b3JlKFNjaGVkdWxlT3ZlcnJpZGVTdG9yZS5TVE9SRV9OQU1FLCB7IGtleVBhdGg6ICdpZCcgfSk7XG4gICAgICAgIHN0b3JlLmNyZWF0ZUluZGV4KCdyZXNvdXJjZUlkJywgJ3Jlc291cmNlSWQnLCB7IHVuaXF1ZTogZmFsc2UgfSk7XG4gICAgICAgIHN0b3JlLmNyZWF0ZUluZGV4KCdkYXRlJywgJ2RhdGUnLCB7IHVuaXF1ZTogZmFsc2UgfSk7XG4gICAgICAgIHN0b3JlLmNyZWF0ZUluZGV4KCdyZXNvdXJjZUlkX2RhdGUnLCBbJ3Jlc291cmNlSWQnLCAnZGF0ZSddLCB7IHVuaXF1ZTogdHJ1ZSB9KTtcbiAgICAgICAgc3RvcmUuY3JlYXRlSW5kZXgoJ3N5bmNTdGF0dXMnLCAnc3luY1N0YXR1cycsIHsgdW5pcXVlOiBmYWxzZSB9KTtcbiAgICB9XG59XG5TY2hlZHVsZU92ZXJyaWRlU3RvcmUuU1RPUkVfTkFNRSA9ICdzY2hlZHVsZU92ZXJyaWRlcyc7XG4iLCAiaW1wb3J0IHsgU2NoZWR1bGVPdmVycmlkZVN0b3JlIH0gZnJvbSAnLi9TY2hlZHVsZU92ZXJyaWRlU3RvcmUnO1xuLyoqXG4gKiBTY2hlZHVsZU92ZXJyaWRlU2VydmljZSAtIENSVUQgZm9yIHNjaGVkdWxlIG92ZXJyaWRlc1xuICpcbiAqIFByb3ZpZGVzIGFjY2VzcyB0byBkYXRlLXNwZWNpZmljIHNjaGVkdWxlIG92ZXJyaWRlcyBmb3IgcmVzb3VyY2VzLlxuICovXG5leHBvcnQgY2xhc3MgU2NoZWR1bGVPdmVycmlkZVNlcnZpY2Uge1xuICAgIGNvbnN0cnVjdG9yKGNvbnRleHQpIHtcbiAgICAgICAgdGhpcy5jb250ZXh0ID0gY29udGV4dDtcbiAgICB9XG4gICAgZ2V0IGRiKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5jb250ZXh0LmdldERhdGFiYXNlKCk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEdldCBvdmVycmlkZSBmb3IgYSBzcGVjaWZpYyByZXNvdXJjZSBhbmQgZGF0ZVxuICAgICAqL1xuICAgIGFzeW5jIGdldE92ZXJyaWRlKHJlc291cmNlSWQsIGRhdGUpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHRyYW5zYWN0aW9uID0gdGhpcy5kYi50cmFuc2FjdGlvbihbU2NoZWR1bGVPdmVycmlkZVN0b3JlLlNUT1JFX05BTUVdLCAncmVhZG9ubHknKTtcbiAgICAgICAgICAgIGNvbnN0IHN0b3JlID0gdHJhbnNhY3Rpb24ub2JqZWN0U3RvcmUoU2NoZWR1bGVPdmVycmlkZVN0b3JlLlNUT1JFX05BTUUpO1xuICAgICAgICAgICAgY29uc3QgaW5kZXggPSBzdG9yZS5pbmRleCgncmVzb3VyY2VJZF9kYXRlJyk7XG4gICAgICAgICAgICBjb25zdCByZXF1ZXN0ID0gaW5kZXguZ2V0KFtyZXNvdXJjZUlkLCBkYXRlXSk7XG4gICAgICAgICAgICByZXF1ZXN0Lm9uc3VjY2VzcyA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICByZXNvbHZlKHJlcXVlc3QucmVzdWx0IHx8IG51bGwpO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIHJlcXVlc3Qub25lcnJvciA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICByZWplY3QobmV3IEVycm9yKGBGYWlsZWQgdG8gZ2V0IG92ZXJyaWRlIGZvciAke3Jlc291cmNlSWR9IG9uICR7ZGF0ZX06ICR7cmVxdWVzdC5lcnJvcn1gKSk7XG4gICAgICAgICAgICB9O1xuICAgICAgICB9KTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogR2V0IGFsbCBvdmVycmlkZXMgZm9yIGEgcmVzb3VyY2VcbiAgICAgKi9cbiAgICBhc3luYyBnZXRCeVJlc291cmNlKHJlc291cmNlSWQpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHRyYW5zYWN0aW9uID0gdGhpcy5kYi50cmFuc2FjdGlvbihbU2NoZWR1bGVPdmVycmlkZVN0b3JlLlNUT1JFX05BTUVdLCAncmVhZG9ubHknKTtcbiAgICAgICAgICAgIGNvbnN0IHN0b3JlID0gdHJhbnNhY3Rpb24ub2JqZWN0U3RvcmUoU2NoZWR1bGVPdmVycmlkZVN0b3JlLlNUT1JFX05BTUUpO1xuICAgICAgICAgICAgY29uc3QgaW5kZXggPSBzdG9yZS5pbmRleCgncmVzb3VyY2VJZCcpO1xuICAgICAgICAgICAgY29uc3QgcmVxdWVzdCA9IGluZGV4LmdldEFsbChyZXNvdXJjZUlkKTtcbiAgICAgICAgICAgIHJlcXVlc3Qub25zdWNjZXNzID0gKCkgPT4ge1xuICAgICAgICAgICAgICAgIHJlc29sdmUocmVxdWVzdC5yZXN1bHQgfHwgW10pO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIHJlcXVlc3Qub25lcnJvciA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICByZWplY3QobmV3IEVycm9yKGBGYWlsZWQgdG8gZ2V0IG92ZXJyaWRlcyBmb3IgJHtyZXNvdXJjZUlkfTogJHtyZXF1ZXN0LmVycm9yfWApKTtcbiAgICAgICAgICAgIH07XG4gICAgICAgIH0pO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBHZXQgb3ZlcnJpZGVzIGZvciBhIGRhdGUgcmFuZ2VcbiAgICAgKi9cbiAgICBhc3luYyBnZXRCeURhdGVSYW5nZShyZXNvdXJjZUlkLCBzdGFydERhdGUsIGVuZERhdGUpIHtcbiAgICAgICAgY29uc3QgYWxsID0gYXdhaXQgdGhpcy5nZXRCeVJlc291cmNlKHJlc291cmNlSWQpO1xuICAgICAgICByZXR1cm4gYWxsLmZpbHRlcihvID0+IG8uZGF0ZSA+PSBzdGFydERhdGUgJiYgby5kYXRlIDw9IGVuZERhdGUpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBTYXZlIGFuIG92ZXJyaWRlXG4gICAgICovXG4gICAgYXN5bmMgc2F2ZShvdmVycmlkZSkge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICAgICAgY29uc3QgdHJhbnNhY3Rpb24gPSB0aGlzLmRiLnRyYW5zYWN0aW9uKFtTY2hlZHVsZU92ZXJyaWRlU3RvcmUuU1RPUkVfTkFNRV0sICdyZWFkd3JpdGUnKTtcbiAgICAgICAgICAgIGNvbnN0IHN0b3JlID0gdHJhbnNhY3Rpb24ub2JqZWN0U3RvcmUoU2NoZWR1bGVPdmVycmlkZVN0b3JlLlNUT1JFX05BTUUpO1xuICAgICAgICAgICAgY29uc3QgcmVxdWVzdCA9IHN0b3JlLnB1dChvdmVycmlkZSk7XG4gICAgICAgICAgICByZXF1ZXN0Lm9uc3VjY2VzcyA9ICgpID0+IHJlc29sdmUoKTtcbiAgICAgICAgICAgIHJlcXVlc3Qub25lcnJvciA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICByZWplY3QobmV3IEVycm9yKGBGYWlsZWQgdG8gc2F2ZSBvdmVycmlkZSAke292ZXJyaWRlLmlkfTogJHtyZXF1ZXN0LmVycm9yfWApKTtcbiAgICAgICAgICAgIH07XG4gICAgICAgIH0pO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBEZWxldGUgYW4gb3ZlcnJpZGVcbiAgICAgKi9cbiAgICBhc3luYyBkZWxldGUoaWQpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHRyYW5zYWN0aW9uID0gdGhpcy5kYi50cmFuc2FjdGlvbihbU2NoZWR1bGVPdmVycmlkZVN0b3JlLlNUT1JFX05BTUVdLCAncmVhZHdyaXRlJyk7XG4gICAgICAgICAgICBjb25zdCBzdG9yZSA9IHRyYW5zYWN0aW9uLm9iamVjdFN0b3JlKFNjaGVkdWxlT3ZlcnJpZGVTdG9yZS5TVE9SRV9OQU1FKTtcbiAgICAgICAgICAgIGNvbnN0IHJlcXVlc3QgPSBzdG9yZS5kZWxldGUoaWQpO1xuICAgICAgICAgICAgcmVxdWVzdC5vbnN1Y2Nlc3MgPSAoKSA9PiByZXNvbHZlKCk7XG4gICAgICAgICAgICByZXF1ZXN0Lm9uZXJyb3IgPSAoKSA9PiB7XG4gICAgICAgICAgICAgICAgcmVqZWN0KG5ldyBFcnJvcihgRmFpbGVkIHRvIGRlbGV0ZSBvdmVycmlkZSAke2lkfTogJHtyZXF1ZXN0LmVycm9yfWApKTtcbiAgICAgICAgICAgIH07XG4gICAgICAgIH0pO1xuICAgIH1cbn1cbiIsICIvKipcbiAqIFJlc291cmNlU2NoZWR1bGVTZXJ2aWNlIC0gR2V0IGVmZmVjdGl2ZSBzY2hlZHVsZSBmb3IgYSByZXNvdXJjZSBvbiBhIGRhdGVcbiAqXG4gKiBMb2dpYzpcbiAqIDEuIENoZWNrIGZvciBvdmVycmlkZSBvbiB0aGlzIGRhdGVcbiAqIDIuIEZhbGwgYmFjayB0byBkZWZhdWx0IHNjaGVkdWxlIGZvciB0aGUgd2Vla2RheVxuICovXG5leHBvcnQgY2xhc3MgUmVzb3VyY2VTY2hlZHVsZVNlcnZpY2Uge1xuICAgIGNvbnN0cnVjdG9yKHJlc291cmNlU2VydmljZSwgb3ZlcnJpZGVTZXJ2aWNlLCBkYXRlU2VydmljZSkge1xuICAgICAgICB0aGlzLnJlc291cmNlU2VydmljZSA9IHJlc291cmNlU2VydmljZTtcbiAgICAgICAgdGhpcy5vdmVycmlkZVNlcnZpY2UgPSBvdmVycmlkZVNlcnZpY2U7XG4gICAgICAgIHRoaXMuZGF0ZVNlcnZpY2UgPSBkYXRlU2VydmljZTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogR2V0IGVmZmVjdGl2ZSBzY2hlZHVsZSBmb3IgYSByZXNvdXJjZSBvbiBhIHNwZWNpZmljIGRhdGVcbiAgICAgKlxuICAgICAqIEBwYXJhbSByZXNvdXJjZUlkIC0gUmVzb3VyY2UgSURcbiAgICAgKiBAcGFyYW0gZGF0ZSAtIERhdGUgc3RyaW5nIFwiWVlZWS1NTS1ERFwiXG4gICAgICogQHJldHVybnMgSVRpbWVTbG90IG9yIG51bGwgKGZyaS9jbG9zZWQpXG4gICAgICovXG4gICAgYXN5bmMgZ2V0U2NoZWR1bGVGb3JEYXRlKHJlc291cmNlSWQsIGRhdGUpIHtcbiAgICAgICAgLy8gMS4gQ2hlY2sgZm9yIG92ZXJyaWRlXG4gICAgICAgIGNvbnN0IG92ZXJyaWRlID0gYXdhaXQgdGhpcy5vdmVycmlkZVNlcnZpY2UuZ2V0T3ZlcnJpZGUocmVzb3VyY2VJZCwgZGF0ZSk7XG4gICAgICAgIGlmIChvdmVycmlkZSkge1xuICAgICAgICAgICAgcmV0dXJuIG92ZXJyaWRlLnNjaGVkdWxlO1xuICAgICAgICB9XG4gICAgICAgIC8vIDIuIFVzZSBkZWZhdWx0IHNjaGVkdWxlIGZvciB3ZWVrZGF5XG4gICAgICAgIGNvbnN0IHJlc291cmNlID0gYXdhaXQgdGhpcy5yZXNvdXJjZVNlcnZpY2UuZ2V0KHJlc291cmNlSWQpO1xuICAgICAgICBpZiAoIXJlc291cmNlIHx8ICFyZXNvdXJjZS5kZWZhdWx0U2NoZWR1bGUpIHtcbiAgICAgICAgICAgIHJldHVybiBudWxsO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHdlZWtEYXkgPSB0aGlzLmRhdGVTZXJ2aWNlLmdldElTT1dlZWtEYXkoZGF0ZSk7XG4gICAgICAgIHJldHVybiByZXNvdXJjZS5kZWZhdWx0U2NoZWR1bGVbd2Vla0RheV0gfHwgbnVsbDtcbiAgICB9XG4gICAgLyoqXG4gICAgICogR2V0IHNjaGVkdWxlcyBmb3IgbXVsdGlwbGUgZGF0ZXNcbiAgICAgKlxuICAgICAqIEBwYXJhbSByZXNvdXJjZUlkIC0gUmVzb3VyY2UgSURcbiAgICAgKiBAcGFyYW0gZGF0ZXMgLSBBcnJheSBvZiBkYXRlIHN0cmluZ3MgXCJZWVlZLU1NLUREXCJcbiAgICAgKiBAcmV0dXJucyBNYXAgb2YgZGF0ZSAtPiBJVGltZVNsb3QgfCBudWxsXG4gICAgICovXG4gICAgYXN5bmMgZ2V0U2NoZWR1bGVzRm9yRGF0ZXMocmVzb3VyY2VJZCwgZGF0ZXMpIHtcbiAgICAgICAgY29uc3QgcmVzdWx0ID0gbmV3IE1hcCgpO1xuICAgICAgICAvLyBHZXQgcmVzb3VyY2Ugb25jZVxuICAgICAgICBjb25zdCByZXNvdXJjZSA9IGF3YWl0IHRoaXMucmVzb3VyY2VTZXJ2aWNlLmdldChyZXNvdXJjZUlkKTtcbiAgICAgICAgLy8gR2V0IGFsbCBvdmVycmlkZXMgaW4gZGF0ZSByYW5nZVxuICAgICAgICBjb25zdCBvdmVycmlkZXMgPSBkYXRlcy5sZW5ndGggPiAwXG4gICAgICAgICAgICA/IGF3YWl0IHRoaXMub3ZlcnJpZGVTZXJ2aWNlLmdldEJ5RGF0ZVJhbmdlKHJlc291cmNlSWQsIGRhdGVzWzBdLCBkYXRlc1tkYXRlcy5sZW5ndGggLSAxXSlcbiAgICAgICAgICAgIDogW107XG4gICAgICAgIC8vIEJ1aWxkIG92ZXJyaWRlIG1hcFxuICAgICAgICBjb25zdCBvdmVycmlkZU1hcCA9IG5ldyBNYXAob3ZlcnJpZGVzLm1hcChvID0+IFtvLmRhdGUsIG8uc2NoZWR1bGVdKSk7XG4gICAgICAgIC8vIFJlc29sdmUgZWFjaCBkYXRlXG4gICAgICAgIGZvciAoY29uc3QgZGF0ZSBvZiBkYXRlcykge1xuICAgICAgICAgICAgLy8gQ2hlY2sgb3ZlcnJpZGUgZmlyc3RcbiAgICAgICAgICAgIGlmIChvdmVycmlkZU1hcC5oYXMoZGF0ZSkpIHtcbiAgICAgICAgICAgICAgICByZXN1bHQuc2V0KGRhdGUsIG92ZXJyaWRlTWFwLmdldChkYXRlKSk7XG4gICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyBGYWxsIGJhY2sgdG8gZGVmYXVsdFxuICAgICAgICAgICAgaWYgKHJlc291cmNlPy5kZWZhdWx0U2NoZWR1bGUpIHtcbiAgICAgICAgICAgICAgICBjb25zdCB3ZWVrRGF5ID0gdGhpcy5kYXRlU2VydmljZS5nZXRJU09XZWVrRGF5KGRhdGUpO1xuICAgICAgICAgICAgICAgIHJlc3VsdC5zZXQoZGF0ZSwgcmVzb3VyY2UuZGVmYXVsdFNjaGVkdWxlW3dlZWtEYXldIHx8IG51bGwpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgcmVzdWx0LnNldChkYXRlLCBudWxsKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH1cbn1cbiIsICIvKipcbiAqIFN3cEV2ZW50IC0gV3JhcHBlciBjbGFzcyBmb3IgY2FsZW5kYXIgZXZlbnQgZWxlbWVudHNcbiAqXG4gKiBFbmNhcHN1bGF0ZXMgYW4gSFRNTEVsZW1lbnQgYW5kIHByb3ZpZGVzIGNvbXB1dGVkIHByb3BlcnRpZXNcbiAqIGZvciBzdGFydC9lbmQgdGltZXMgYmFzZWQgb24gZWxlbWVudCBwb3NpdGlvbiBhbmQgZ3JpZCBjb25maWcuXG4gKlxuICogVXNhZ2U6XG4gKiAtIGV2ZW50SWQgaXMgcmVhZCBmcm9tIGVsZW1lbnQuZGF0YXNldFxuICogLSBjb2x1bW5LZXkgaWRlbnRpZmllcyB0aGUgY29sdW1uIHVuaWZvcm1seVxuICogLSBQb3NpdGlvbiAodG9wLCBoZWlnaHQpIGlzIHJlYWQgZnJvbSBlbGVtZW50LnN0eWxlXG4gKiAtIEZhY3RvcnkgbWV0aG9kIGBmcm9tRWxlbWVudCgpYCBjYWxjdWxhdGVzIERhdGUgb2JqZWN0c1xuICovXG5leHBvcnQgY2xhc3MgU3dwRXZlbnQge1xuICAgIGNvbnN0cnVjdG9yKGVsZW1lbnQsIGNvbHVtbktleSwgc3RhcnQsIGVuZCkge1xuICAgICAgICB0aGlzLmVsZW1lbnQgPSBlbGVtZW50O1xuICAgICAgICB0aGlzLmNvbHVtbktleSA9IGNvbHVtbktleTtcbiAgICAgICAgdGhpcy5fc3RhcnQgPSBzdGFydDtcbiAgICAgICAgdGhpcy5fZW5kID0gZW5kO1xuICAgIH1cbiAgICAvKiogRXZlbnQgSUQgZnJvbSBlbGVtZW50LmRhdGFzZXQuZXZlbnRJZCAqL1xuICAgIGdldCBldmVudElkKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5lbGVtZW50LmRhdGFzZXQuZXZlbnRJZCB8fCAnJztcbiAgICB9XG4gICAgZ2V0IHN0YXJ0KCkge1xuICAgICAgICByZXR1cm4gdGhpcy5fc3RhcnQ7XG4gICAgfVxuICAgIGdldCBlbmQoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLl9lbmQ7XG4gICAgfVxuICAgIC8qKiBEdXJhdGlvbiBpbiBtaW51dGVzICovXG4gICAgZ2V0IGR1cmF0aW9uTWludXRlcygpIHtcbiAgICAgICAgcmV0dXJuICh0aGlzLl9lbmQuZ2V0VGltZSgpIC0gdGhpcy5fc3RhcnQuZ2V0VGltZSgpKSAvICgxMDAwICogNjApO1xuICAgIH1cbiAgICAvKiogRHVyYXRpb24gaW4gbWlsbGlzZWNvbmRzICovXG4gICAgZ2V0IGR1cmF0aW9uTXMoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLl9lbmQuZ2V0VGltZSgpIC0gdGhpcy5fc3RhcnQuZ2V0VGltZSgpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBGYWN0b3J5OiBDcmVhdGUgU3dwRXZlbnQgZnJvbSBlbGVtZW50ICsgY29sdW1uS2V5XG4gICAgICogUmVhZHMgdG9wL2hlaWdodCBmcm9tIGVsZW1lbnQuc3R5bGUgdG8gY2FsY3VsYXRlIHN0YXJ0L2VuZFxuICAgICAqIEBwYXJhbSBjb2x1bW5LZXkgLSBPcGFxdWUgY29sdW1uIGlkZW50aWZpZXIgKGRvIE5PVCBwYXJzZSAtIHVzZSBvbmx5IGZvciBtYXRjaGluZylcbiAgICAgKiBAcGFyYW0gZGF0ZSAtIERhdGUgc3RyaW5nIChZWVlZLU1NLUREKSBmb3IgdGltZSBjYWxjdWxhdGlvbnNcbiAgICAgKi9cbiAgICBzdGF0aWMgZnJvbUVsZW1lbnQoZWxlbWVudCwgY29sdW1uS2V5LCBkYXRlLCBncmlkQ29uZmlnKSB7XG4gICAgICAgIGNvbnN0IHRvcFBpeGVscyA9IHBhcnNlRmxvYXQoZWxlbWVudC5zdHlsZS50b3ApIHx8IDA7XG4gICAgICAgIGNvbnN0IGhlaWdodFBpeGVscyA9IHBhcnNlRmxvYXQoZWxlbWVudC5zdHlsZS5oZWlnaHQpIHx8IDA7XG4gICAgICAgIC8vIENhbGN1bGF0ZSBzdGFydCBmcm9tIHRvcCBwb3NpdGlvblxuICAgICAgICBjb25zdCBzdGFydE1pbnV0ZXNGcm9tR3JpZCA9ICh0b3BQaXhlbHMgLyBncmlkQ29uZmlnLmhvdXJIZWlnaHQpICogNjA7XG4gICAgICAgIGNvbnN0IHRvdGFsTWludXRlcyA9IChncmlkQ29uZmlnLmRheVN0YXJ0SG91ciAqIDYwKSArIHN0YXJ0TWludXRlc0Zyb21HcmlkO1xuICAgICAgICBjb25zdCBzdGFydCA9IG5ldyBEYXRlKGRhdGUpO1xuICAgICAgICBzdGFydC5zZXRIb3VycyhNYXRoLmZsb29yKHRvdGFsTWludXRlcyAvIDYwKSwgdG90YWxNaW51dGVzICUgNjAsIDAsIDApO1xuICAgICAgICAvLyBDYWxjdWxhdGUgZW5kIGZyb20gaGVpZ2h0XG4gICAgICAgIGNvbnN0IGR1cmF0aW9uTWludXRlcyA9IChoZWlnaHRQaXhlbHMgLyBncmlkQ29uZmlnLmhvdXJIZWlnaHQpICogNjA7XG4gICAgICAgIGNvbnN0IGVuZCA9IG5ldyBEYXRlKHN0YXJ0LmdldFRpbWUoKSArIGR1cmF0aW9uTWludXRlcyAqIDYwICogMTAwMCk7XG4gICAgICAgIHJldHVybiBuZXcgU3dwRXZlbnQoZWxlbWVudCwgY29sdW1uS2V5LCBzdGFydCwgZW5kKTtcbiAgICB9XG59XG4iLCAiaW1wb3J0IHsgQ29yZUV2ZW50cyB9IGZyb20gJy4uL2NvbnN0YW50cy9Db3JlRXZlbnRzJztcbmltcG9ydCB7IHNuYXBUb0dyaWQgfSBmcm9tICcuLi91dGlscy9Qb3NpdGlvblV0aWxzJztcbmltcG9ydCB7IFN3cEV2ZW50IH0gZnJvbSAnLi4vdHlwZXMvU3dwRXZlbnQnO1xuLyoqXG4gKiBEcmFnRHJvcE1hbmFnZXIgLSBIYW5kbGVzIGRyYWctZHJvcCBmb3IgY2FsZW5kYXIgZXZlbnRzXG4gKlxuICogU3RyYXRlZ3k6IERyYWcgb3JpZ2luYWwgZWxlbWVudCwgbGVhdmUgZ2hvc3QtY2xvbmUgaW4gcGxhY2VcbiAqIC0gbW91c2Vkb3duOiBTdG9yZSBpbml0aWFsIHN0YXRlLCB3YWl0IGZvciBtb3ZlbWVudFxuICogLSBtb3VzZW1vdmUgKD41cHgpOiBDcmVhdGUgZ2hvc3QsIHN0YXJ0IGRyYWdnaW5nIG9yaWdpbmFsXG4gKiAtIG1vdXNldXA6IFNuYXAgdG8gZ3JpZCwgcmVtb3ZlIGdob3N0LCBlbWl0IGRyYWc6ZW5kXG4gKiAtIGNhbmNlbDogQW5pbWF0ZSBiYWNrIHRvIHN0YXJ0WSwgcmVtb3ZlIGdob3N0XG4gKi9cbmV4cG9ydCBjbGFzcyBEcmFnRHJvcE1hbmFnZXIge1xuICAgIGNvbnN0cnVjdG9yKGV2ZW50QnVzLCBncmlkQ29uZmlnKSB7XG4gICAgICAgIHRoaXMuZXZlbnRCdXMgPSBldmVudEJ1cztcbiAgICAgICAgdGhpcy5ncmlkQ29uZmlnID0gZ3JpZENvbmZpZztcbiAgICAgICAgdGhpcy5kcmFnU3RhdGUgPSBudWxsO1xuICAgICAgICB0aGlzLm1vdXNlRG93blBvc2l0aW9uID0gbnVsbDtcbiAgICAgICAgdGhpcy5wZW5kaW5nRWxlbWVudCA9IG51bGw7XG4gICAgICAgIHRoaXMucGVuZGluZ01vdXNlT2Zmc2V0ID0gbnVsbDtcbiAgICAgICAgdGhpcy5jb250YWluZXIgPSBudWxsO1xuICAgICAgICB0aGlzLmluSGVhZGVyID0gZmFsc2U7XG4gICAgICAgIHRoaXMuRFJBR19USFJFU0hPTEQgPSA1O1xuICAgICAgICB0aGlzLklOVEVSUE9MQVRJT05fRkFDVE9SID0gMC4zO1xuICAgICAgICB0aGlzLmhhbmRsZVBvaW50ZXJEb3duID0gKGUpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHRhcmdldCA9IGUudGFyZ2V0O1xuICAgICAgICAgICAgLy8gSWdub3JlIGlmIGNsaWNraW5nIG9uIHJlc2l6ZSBoYW5kbGVcbiAgICAgICAgICAgIGlmICh0YXJnZXQuY2xvc2VzdCgnc3dwLXJlc2l6ZS1oYW5kbGUnKSlcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICAvLyBNYXRjaCBib3RoIHN3cC1ldmVudCBhbmQgc3dwLWhlYWRlci1pdGVtXG4gICAgICAgICAgICBjb25zdCBldmVudEVsZW1lbnQgPSB0YXJnZXQuY2xvc2VzdCgnc3dwLWV2ZW50Jyk7XG4gICAgICAgICAgICBjb25zdCBoZWFkZXJJdGVtID0gdGFyZ2V0LmNsb3Nlc3QoJ3N3cC1oZWFkZXItaXRlbScpO1xuICAgICAgICAgICAgY29uc3QgZHJhZ2dhYmxlID0gZXZlbnRFbGVtZW50IHx8IGhlYWRlckl0ZW07XG4gICAgICAgICAgICBpZiAoIWRyYWdnYWJsZSlcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICAvLyBTdG9yZSBmb3IgcG90ZW50aWFsIGRyYWdcbiAgICAgICAgICAgIHRoaXMubW91c2VEb3duUG9zaXRpb24gPSB7IHg6IGUuY2xpZW50WCwgeTogZS5jbGllbnRZIH07XG4gICAgICAgICAgICB0aGlzLnBlbmRpbmdFbGVtZW50ID0gZHJhZ2dhYmxlO1xuICAgICAgICAgICAgLy8gQ2FsY3VsYXRlIG1vdXNlIG9mZnNldCB3aXRoaW4gZWxlbWVudFxuICAgICAgICAgICAgY29uc3QgcmVjdCA9IGRyYWdnYWJsZS5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcbiAgICAgICAgICAgIHRoaXMucGVuZGluZ01vdXNlT2Zmc2V0ID0ge1xuICAgICAgICAgICAgICAgIHg6IGUuY2xpZW50WCAtIHJlY3QubGVmdCxcbiAgICAgICAgICAgICAgICB5OiBlLmNsaWVudFkgLSByZWN0LnRvcFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIC8vIENhcHR1cmUgcG9pbnRlciBmb3IgcmVsaWFibGUgdHJhY2tpbmdcbiAgICAgICAgICAgIGRyYWdnYWJsZS5zZXRQb2ludGVyQ2FwdHVyZShlLnBvaW50ZXJJZCk7XG4gICAgICAgIH07XG4gICAgICAgIHRoaXMuaGFuZGxlUG9pbnRlck1vdmUgPSAoZSkgPT4ge1xuICAgICAgICAgICAgLy8gTm90IGluIHBvdGVudGlhbCBkcmFnIHN0YXRlXG4gICAgICAgICAgICBpZiAoIXRoaXMubW91c2VEb3duUG9zaXRpb24gfHwgIXRoaXMucGVuZGluZ0VsZW1lbnQpIHtcbiAgICAgICAgICAgICAgICAvLyBBbHJlYWR5IGRyYWdnaW5nIC0gdXBkYXRlIHRhcmdldFxuICAgICAgICAgICAgICAgIGlmICh0aGlzLmRyYWdTdGF0ZSkge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLnVwZGF0ZURyYWdUYXJnZXQoZSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIENoZWNrIHRocmVzaG9sZFxuICAgICAgICAgICAgY29uc3QgZGVsdGFYID0gTWF0aC5hYnMoZS5jbGllbnRYIC0gdGhpcy5tb3VzZURvd25Qb3NpdGlvbi54KTtcbiAgICAgICAgICAgIGNvbnN0IGRlbHRhWSA9IE1hdGguYWJzKGUuY2xpZW50WSAtIHRoaXMubW91c2VEb3duUG9zaXRpb24ueSk7XG4gICAgICAgICAgICBjb25zdCBkaXN0YW5jZSA9IE1hdGguc3FydChkZWx0YVggKiBkZWx0YVggKyBkZWx0YVkgKiBkZWx0YVkpO1xuICAgICAgICAgICAgaWYgKGRpc3RhbmNlIDwgdGhpcy5EUkFHX1RIUkVTSE9MRClcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICAvLyBTdGFydCBkcmFnXG4gICAgICAgICAgICB0aGlzLmluaXRpYWxpemVEcmFnKHRoaXMucGVuZGluZ0VsZW1lbnQsIHRoaXMucGVuZGluZ01vdXNlT2Zmc2V0LCBlKTtcbiAgICAgICAgICAgIHRoaXMubW91c2VEb3duUG9zaXRpb24gPSBudWxsO1xuICAgICAgICAgICAgdGhpcy5wZW5kaW5nRWxlbWVudCA9IG51bGw7XG4gICAgICAgICAgICB0aGlzLnBlbmRpbmdNb3VzZU9mZnNldCA9IG51bGw7XG4gICAgICAgIH07XG4gICAgICAgIHRoaXMuaGFuZGxlUG9pbnRlclVwID0gKF9lKSA9PiB7XG4gICAgICAgICAgICAvLyBDbGVhciBwZW5kaW5nIHN0YXRlXG4gICAgICAgICAgICB0aGlzLm1vdXNlRG93blBvc2l0aW9uID0gbnVsbDtcbiAgICAgICAgICAgIHRoaXMucGVuZGluZ0VsZW1lbnQgPSBudWxsO1xuICAgICAgICAgICAgdGhpcy5wZW5kaW5nTW91c2VPZmZzZXQgPSBudWxsO1xuICAgICAgICAgICAgaWYgKCF0aGlzLmRyYWdTdGF0ZSlcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICAvLyBTdG9wIGFuaW1hdGlvblxuICAgICAgICAgICAgY2FuY2VsQW5pbWF0aW9uRnJhbWUodGhpcy5kcmFnU3RhdGUuYW5pbWF0aW9uSWQpO1xuICAgICAgICAgICAgLy8gSGFuZGxlIGJhc2VkIG9uIGRyYWcgc291cmNlIGFuZCB0YXJnZXRcbiAgICAgICAgICAgIGlmICh0aGlzLmRyYWdTdGF0ZS5kcmFnU291cmNlID09PSAnaGVhZGVyJykge1xuICAgICAgICAgICAgICAgIC8vIEhlYWRlciBpdGVtIGRyYWcgZW5kXG4gICAgICAgICAgICAgICAgdGhpcy5oYW5kbGVIZWFkZXJJdGVtRHJhZ0VuZCgpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgLy8gR3JpZCBldmVudCBkcmFnIGVuZFxuICAgICAgICAgICAgICAgIHRoaXMuaGFuZGxlR3JpZEV2ZW50RHJhZ0VuZCgpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgLy8gQ2xlYW51cFxuICAgICAgICAgICAgdGhpcy5kcmFnU3RhdGUuZWxlbWVudC5jbGFzc0xpc3QucmVtb3ZlKCdkcmFnZ2luZycpO1xuICAgICAgICAgICAgdGhpcy5kcmFnU3RhdGUgPSBudWxsO1xuICAgICAgICAgICAgdGhpcy5pbkhlYWRlciA9IGZhbHNlO1xuICAgICAgICB9O1xuICAgICAgICB0aGlzLmFuaW1hdGVEcmFnID0gKCkgPT4ge1xuICAgICAgICAgICAgaWYgKCF0aGlzLmRyYWdTdGF0ZSlcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICBjb25zdCBkaWZmID0gdGhpcy5kcmFnU3RhdGUudGFyZ2V0WSAtIHRoaXMuZHJhZ1N0YXRlLmN1cnJlbnRZO1xuICAgICAgICAgICAgLy8gU3RvcCBhbmltYXRpb24gd2hlbiBjbG9zZSBlbm91Z2ggdG8gdGFyZ2V0XG4gICAgICAgICAgICBpZiAoTWF0aC5hYnMoZGlmZikgPD0gMC41KSB7XG4gICAgICAgICAgICAgICAgdGhpcy5kcmFnU3RhdGUuYW5pbWF0aW9uSWQgPSAwO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIEludGVycG9sYXRlIHRvd2FyZHMgdGFyZ2V0XG4gICAgICAgICAgICB0aGlzLmRyYWdTdGF0ZS5jdXJyZW50WSArPSBkaWZmICogdGhpcy5JTlRFUlBPTEFUSU9OX0ZBQ1RPUjtcbiAgICAgICAgICAgIC8vIFVwZGF0ZSBlbGVtZW50IHBvc2l0aW9uXG4gICAgICAgICAgICB0aGlzLmRyYWdTdGF0ZS5lbGVtZW50LnN0eWxlLnRvcCA9IGAke3RoaXMuZHJhZ1N0YXRlLmN1cnJlbnRZfXB4YDtcbiAgICAgICAgICAgIC8vIEVtaXQgZHJhZzptb3ZlIChvbmx5IGlmIHdlIGhhdmUgYSBjb2x1bW4pXG4gICAgICAgICAgICBpZiAodGhpcy5kcmFnU3RhdGUuY29sdW1uRWxlbWVudCkge1xuICAgICAgICAgICAgICAgIGNvbnN0IHBheWxvYWQgPSB7XG4gICAgICAgICAgICAgICAgICAgIGV2ZW50SWQ6IHRoaXMuZHJhZ1N0YXRlLmV2ZW50SWQsXG4gICAgICAgICAgICAgICAgICAgIGVsZW1lbnQ6IHRoaXMuZHJhZ1N0YXRlLmVsZW1lbnQsXG4gICAgICAgICAgICAgICAgICAgIGN1cnJlbnRZOiB0aGlzLmRyYWdTdGF0ZS5jdXJyZW50WSxcbiAgICAgICAgICAgICAgICAgICAgY29sdW1uRWxlbWVudDogdGhpcy5kcmFnU3RhdGUuY29sdW1uRWxlbWVudFxuICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgICAgdGhpcy5ldmVudEJ1cy5lbWl0KENvcmVFdmVudHMuRVZFTlRfRFJBR19NT1ZFLCBwYXlsb2FkKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIENvbnRpbnVlIGFuaW1hdGlvblxuICAgICAgICAgICAgdGhpcy5kcmFnU3RhdGUuYW5pbWF0aW9uSWQgPSByZXF1ZXN0QW5pbWF0aW9uRnJhbWUodGhpcy5hbmltYXRlRHJhZyk7XG4gICAgICAgIH07XG4gICAgICAgIHRoaXMuc2V0dXBTY3JvbGxMaXN0ZW5lcigpO1xuICAgIH1cbiAgICBzZXR1cFNjcm9sbExpc3RlbmVyKCkge1xuICAgICAgICB0aGlzLmV2ZW50QnVzLm9uKENvcmVFdmVudHMuRURHRV9TQ1JPTExfVElDSywgKGUpID0+IHtcbiAgICAgICAgICAgIGlmICghdGhpcy5kcmFnU3RhdGUpXG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgY29uc3QgeyBzY3JvbGxEZWx0YSB9ID0gZS5kZXRhaWw7XG4gICAgICAgICAgICAvLyBFbGVtZW50IHNrYWwgZmx5dHRlIG1lZCBzY3JvbGwgZm9yIGF0IGZvcmJsaXZlIHVuZGVyIG11c2VuXG4gICAgICAgICAgICAvLyAoZWxlbWVudGV0cyB0b3AgZXIgcmVsYXRpdiB0aWwga29sb25uZW4sIHNvbSBzY3JvbGxlciBtZWQgdmlld3BvcnQpXG4gICAgICAgICAgICB0aGlzLmRyYWdTdGF0ZS50YXJnZXRZICs9IHNjcm9sbERlbHRhO1xuICAgICAgICAgICAgdGhpcy5kcmFnU3RhdGUuY3VycmVudFkgKz0gc2Nyb2xsRGVsdGE7XG4gICAgICAgICAgICB0aGlzLmRyYWdTdGF0ZS5lbGVtZW50LnN0eWxlLnRvcCA9IGAke3RoaXMuZHJhZ1N0YXRlLmN1cnJlbnRZfXB4YDtcbiAgICAgICAgfSk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEluaXRpYWxpemUgZHJhZy1kcm9wIG9uIGEgY29udGFpbmVyIGVsZW1lbnRcbiAgICAgKi9cbiAgICBpbml0KGNvbnRhaW5lcikge1xuICAgICAgICB0aGlzLmNvbnRhaW5lciA9IGNvbnRhaW5lcjtcbiAgICAgICAgY29udGFpbmVyLmFkZEV2ZW50TGlzdGVuZXIoJ3BvaW50ZXJkb3duJywgdGhpcy5oYW5kbGVQb2ludGVyRG93bik7XG4gICAgICAgIGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ3BvaW50ZXJtb3ZlJywgdGhpcy5oYW5kbGVQb2ludGVyTW92ZSk7XG4gICAgICAgIGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ3BvaW50ZXJ1cCcsIHRoaXMuaGFuZGxlUG9pbnRlclVwKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogSGFuZGxlIGRyYWcgZW5kIGZvciBoZWFkZXIgaXRlbXNcbiAgICAgKi9cbiAgICBoYW5kbGVIZWFkZXJJdGVtRHJhZ0VuZCgpIHtcbiAgICAgICAgaWYgKCF0aGlzLmRyYWdTdGF0ZSlcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgLy8gSWYgZHJvcHBlZCBpbiBncmlkIChub3QgaW4gaGVhZGVyKSwgdGhlIHN3cC1ldmVudCB3YXMgYWxyZWFkeSBjcmVhdGVkXG4gICAgICAgIC8vIGJ5IEV2ZW50UmVuZGVyZXIgbGlzdGVuaW5nIHRvIEVWRU5UX0RSQUdfTEVBVkVfSEVBREVSXG4gICAgICAgIC8vIEp1c3QgZW1pdCBkcmFnOmVuZCBmb3IgcGVyc2lzdGVuY2VcbiAgICAgICAgaWYgKCF0aGlzLmluSGVhZGVyICYmIHRoaXMuZHJhZ1N0YXRlLmN1cnJlbnRDb2x1bW4pIHtcbiAgICAgICAgICAgIC8vIERyb3BwZWQgaW4gZ3JpZCAtIGVtaXQgZHJhZzplbmQgd2l0aCB0aGUgbmV3IHN3cC1ldmVudCBlbGVtZW50XG4gICAgICAgICAgICBjb25zdCBncmlkRXZlbnQgPSB0aGlzLmRyYWdTdGF0ZS5jdXJyZW50Q29sdW1uLnF1ZXJ5U2VsZWN0b3IoYHN3cC1ldmVudFtkYXRhLWV2ZW50LWlkPVwiJHt0aGlzLmRyYWdTdGF0ZS5ldmVudElkfVwiXWApO1xuICAgICAgICAgICAgaWYgKGdyaWRFdmVudCkge1xuICAgICAgICAgICAgICAgIGNvbnN0IGNvbHVtbktleSA9IHRoaXMuZHJhZ1N0YXRlLmN1cnJlbnRDb2x1bW4uZGF0YXNldC5jb2x1bW5LZXkgfHwgJyc7XG4gICAgICAgICAgICAgICAgY29uc3QgZGF0ZSA9IHRoaXMuZHJhZ1N0YXRlLmN1cnJlbnRDb2x1bW4uZGF0YXNldC5kYXRlIHx8ICcnO1xuICAgICAgICAgICAgICAgIGNvbnN0IHN3cEV2ZW50ID0gU3dwRXZlbnQuZnJvbUVsZW1lbnQoZ3JpZEV2ZW50LCBjb2x1bW5LZXksIGRhdGUsIHRoaXMuZ3JpZENvbmZpZyk7XG4gICAgICAgICAgICAgICAgY29uc3QgcGF5bG9hZCA9IHtcbiAgICAgICAgICAgICAgICAgICAgc3dwRXZlbnQsXG4gICAgICAgICAgICAgICAgICAgIHNvdXJjZUNvbHVtbktleTogdGhpcy5kcmFnU3RhdGUuc291cmNlQ29sdW1uS2V5LFxuICAgICAgICAgICAgICAgICAgICB0YXJnZXQ6ICdncmlkJ1xuICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgICAgdGhpcy5ldmVudEJ1cy5lbWl0KENvcmVFdmVudHMuRVZFTlRfRFJBR19FTkQsIHBheWxvYWQpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIC8vIElmIHN0aWxsIGluIGhlYWRlciwgbm8gcGVyc2lzdGVuY2UgbmVlZGVkIChzdGF5ZWQgaW4gaGVhZGVyKVxuICAgIH1cbiAgICAvKipcbiAgICAgKiBIYW5kbGUgZHJhZyBlbmQgZm9yIGdyaWQgZXZlbnRzXG4gICAgICovXG4gICAgaGFuZGxlR3JpZEV2ZW50RHJhZ0VuZCgpIHtcbiAgICAgICAgaWYgKCF0aGlzLmRyYWdTdGF0ZSB8fCAhdGhpcy5kcmFnU3RhdGUuY29sdW1uRWxlbWVudClcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgLy8gU25hcCB0byBncmlkXG4gICAgICAgIGNvbnN0IHNuYXBwZWRZID0gc25hcFRvR3JpZCh0aGlzLmRyYWdTdGF0ZS5jdXJyZW50WSwgdGhpcy5ncmlkQ29uZmlnKTtcbiAgICAgICAgdGhpcy5kcmFnU3RhdGUuZWxlbWVudC5zdHlsZS50b3AgPSBgJHtzbmFwcGVkWX1weGA7XG4gICAgICAgIC8vIFJlbW92ZSBnaG9zdFxuICAgICAgICB0aGlzLmRyYWdTdGF0ZS5naG9zdEVsZW1lbnQ/LnJlbW92ZSgpO1xuICAgICAgICAvLyBHZXQgY29sdW1uS2V5IGFuZCBkYXRlIGZyb20gdGFyZ2V0IGNvbHVtblxuICAgICAgICBjb25zdCBjb2x1bW5LZXkgPSB0aGlzLmRyYWdTdGF0ZS5jb2x1bW5FbGVtZW50LmRhdGFzZXQuY29sdW1uS2V5IHx8ICcnO1xuICAgICAgICBjb25zdCBkYXRlID0gdGhpcy5kcmFnU3RhdGUuY29sdW1uRWxlbWVudC5kYXRhc2V0LmRhdGUgfHwgJyc7XG4gICAgICAgIC8vIENyZWF0ZSBTd3BFdmVudCBmcm9tIGVsZW1lbnQgKHJlYWRzIHRvcC9oZWlnaHQvZXZlbnRJZCBmcm9tIGVsZW1lbnQpXG4gICAgICAgIGNvbnN0IHN3cEV2ZW50ID0gU3dwRXZlbnQuZnJvbUVsZW1lbnQodGhpcy5kcmFnU3RhdGUuZWxlbWVudCwgY29sdW1uS2V5LCBkYXRlLCB0aGlzLmdyaWRDb25maWcpO1xuICAgICAgICAvLyBFbWl0IGRyYWc6ZW5kXG4gICAgICAgIGNvbnN0IHBheWxvYWQgPSB7XG4gICAgICAgICAgICBzd3BFdmVudCxcbiAgICAgICAgICAgIHNvdXJjZUNvbHVtbktleTogdGhpcy5kcmFnU3RhdGUuc291cmNlQ29sdW1uS2V5LFxuICAgICAgICAgICAgdGFyZ2V0OiB0aGlzLmluSGVhZGVyID8gJ2hlYWRlcicgOiAnZ3JpZCdcbiAgICAgICAgfTtcbiAgICAgICAgdGhpcy5ldmVudEJ1cy5lbWl0KENvcmVFdmVudHMuRVZFTlRfRFJBR19FTkQsIHBheWxvYWQpO1xuICAgIH1cbiAgICBpbml0aWFsaXplRHJhZyhlbGVtZW50LCBtb3VzZU9mZnNldCwgZSkge1xuICAgICAgICBjb25zdCBldmVudElkID0gZWxlbWVudC5kYXRhc2V0LmV2ZW50SWQgfHwgJyc7XG4gICAgICAgIGNvbnN0IGlzSGVhZGVySXRlbSA9IGVsZW1lbnQudGFnTmFtZS50b0xvd2VyQ2FzZSgpID09PSAnc3dwLWhlYWRlci1pdGVtJztcbiAgICAgICAgY29uc3QgY29sdW1uRWxlbWVudCA9IGVsZW1lbnQuY2xvc2VzdCgnc3dwLWRheS1jb2x1bW4nKTtcbiAgICAgICAgLy8gRm9yIGdyaWQgZXZlbnRzLCB3ZSBuZWVkIGEgY29sdW1uXG4gICAgICAgIGlmICghaXNIZWFkZXJJdGVtICYmICFjb2x1bW5FbGVtZW50KVxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICBpZiAoaXNIZWFkZXJJdGVtKSB7XG4gICAgICAgICAgICAvLyBIZWFkZXIgaXRlbSBkcmFnIGluaXRpYWxpemF0aW9uXG4gICAgICAgICAgICB0aGlzLmluaXRpYWxpemVIZWFkZXJJdGVtRHJhZyhlbGVtZW50LCBtb3VzZU9mZnNldCwgZXZlbnRJZCk7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAvLyBHcmlkIGV2ZW50IGRyYWcgaW5pdGlhbGl6YXRpb25cbiAgICAgICAgICAgIHRoaXMuaW5pdGlhbGl6ZUdyaWRFdmVudERyYWcoZWxlbWVudCwgbW91c2VPZmZzZXQsIGUsIGNvbHVtbkVsZW1lbnQsIGV2ZW50SWQpO1xuICAgICAgICB9XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEluaXRpYWxpemUgZHJhZyBmb3IgYSBoZWFkZXIgaXRlbSAoYWxsRGF5IGV2ZW50KVxuICAgICAqL1xuICAgIGluaXRpYWxpemVIZWFkZXJJdGVtRHJhZyhlbGVtZW50LCBtb3VzZU9mZnNldCwgZXZlbnRJZCkge1xuICAgICAgICAvLyBNYXJrIGFzIGRyYWdnaW5nXG4gICAgICAgIGVsZW1lbnQuY2xhc3NMaXN0LmFkZCgnZHJhZ2dpbmcnKTtcbiAgICAgICAgLy8gSW5pdGlhbGl6ZSBkcmFnIHN0YXRlIGZvciBoZWFkZXIgaXRlbVxuICAgICAgICB0aGlzLmRyYWdTdGF0ZSA9IHtcbiAgICAgICAgICAgIGV2ZW50SWQsXG4gICAgICAgICAgICBlbGVtZW50LFxuICAgICAgICAgICAgZ2hvc3RFbGVtZW50OiBudWxsLCAvLyBObyBnaG9zdCBmb3IgaGVhZGVyIGl0ZW1zXG4gICAgICAgICAgICBzdGFydFk6IDAsXG4gICAgICAgICAgICBtb3VzZU9mZnNldCxcbiAgICAgICAgICAgIGNvbHVtbkVsZW1lbnQ6IG51bGwsXG4gICAgICAgICAgICBjdXJyZW50Q29sdW1uOiBudWxsLFxuICAgICAgICAgICAgdGFyZ2V0WTogMCxcbiAgICAgICAgICAgIGN1cnJlbnRZOiAwLFxuICAgICAgICAgICAgYW5pbWF0aW9uSWQ6IDAsXG4gICAgICAgICAgICBzb3VyY2VDb2x1bW5LZXk6ICcnLCAvLyBXaWxsIGJlIHNldCBmcm9tIGhlYWRlciBpdGVtIGRhdGFcbiAgICAgICAgICAgIGRyYWdTb3VyY2U6ICdoZWFkZXInXG4gICAgICAgIH07XG4gICAgICAgIC8vIFN0YXJ0IGluIGhlYWRlciBtb2RlXG4gICAgICAgIHRoaXMuaW5IZWFkZXIgPSB0cnVlO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBJbml0aWFsaXplIGRyYWcgZm9yIGEgZ3JpZCBldmVudFxuICAgICAqL1xuICAgIGluaXRpYWxpemVHcmlkRXZlbnREcmFnKGVsZW1lbnQsIG1vdXNlT2Zmc2V0LCBlLCBjb2x1bW5FbGVtZW50LCBldmVudElkKSB7XG4gICAgICAgIC8vIENhbGN1bGF0ZSBhYnNvbHV0ZSBZIHBvc2l0aW9uIHVzaW5nIGdldEJvdW5kaW5nQ2xpZW50UmVjdFxuICAgICAgICBjb25zdCBlbGVtZW50UmVjdCA9IGVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG4gICAgICAgIGNvbnN0IGNvbHVtblJlY3QgPSBjb2x1bW5FbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgICAgICBjb25zdCBzdGFydFkgPSBlbGVtZW50UmVjdC50b3AgLSBjb2x1bW5SZWN0LnRvcDtcbiAgICAgICAgLy8gSWYgZXZlbnQgaXMgaW5zaWRlIGEgZ3JvdXAsIG1vdmUgaXQgdG8gZXZlbnRzLWxheWVyIGZvciBjb3JyZWN0IHBvc2l0aW9uaW5nIGR1cmluZyBkcmFnXG4gICAgICAgIGNvbnN0IGdyb3VwID0gZWxlbWVudC5jbG9zZXN0KCdzd3AtZXZlbnQtZ3JvdXAnKTtcbiAgICAgICAgaWYgKGdyb3VwKSB7XG4gICAgICAgICAgICBjb25zdCBldmVudHNMYXllciA9IGNvbHVtbkVsZW1lbnQucXVlcnlTZWxlY3Rvcignc3dwLWV2ZW50cy1sYXllcicpO1xuICAgICAgICAgICAgaWYgKGV2ZW50c0xheWVyKSB7XG4gICAgICAgICAgICAgICAgZXZlbnRzTGF5ZXIuYXBwZW5kQ2hpbGQoZWxlbWVudCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgLy8gU2V0IGNvbnNpc3RlbnQgcG9zaXRpb25pbmcgZm9yIGRyYWcgKHdvcmtzIGZvciBib3RoIGdyb3VwZWQgYW5kIHN0YWNrZWQgZXZlbnRzKVxuICAgICAgICBlbGVtZW50LnN0eWxlLnBvc2l0aW9uID0gJ2Fic29sdXRlJztcbiAgICAgICAgZWxlbWVudC5zdHlsZS50b3AgPSBgJHtzdGFydFl9cHhgO1xuICAgICAgICBlbGVtZW50LnN0eWxlLmxlZnQgPSAnMnB4JztcbiAgICAgICAgZWxlbWVudC5zdHlsZS5yaWdodCA9ICcycHgnO1xuICAgICAgICBlbGVtZW50LnN0eWxlLm1hcmdpbkxlZnQgPSAnMCc7IC8vIFJlc2V0IHN0YWNraW5nIG1hcmdpblxuICAgICAgICAvLyBDcmVhdGUgZ2hvc3QgY2xvbmVcbiAgICAgICAgY29uc3QgZ2hvc3RFbGVtZW50ID0gZWxlbWVudC5jbG9uZU5vZGUodHJ1ZSk7XG4gICAgICAgIGdob3N0RWxlbWVudC5jbGFzc0xpc3QuYWRkKCdkcmFnLWdob3N0Jyk7XG4gICAgICAgIGdob3N0RWxlbWVudC5zdHlsZS5vcGFjaXR5ID0gJzAuMyc7XG4gICAgICAgIGdob3N0RWxlbWVudC5zdHlsZS5wb2ludGVyRXZlbnRzID0gJ25vbmUnO1xuICAgICAgICAvLyBJbnNlcnQgZ2hvc3QgYmVmb3JlIG9yaWdpbmFsXG4gICAgICAgIGVsZW1lbnQucGFyZW50Tm9kZT8uaW5zZXJ0QmVmb3JlKGdob3N0RWxlbWVudCwgZWxlbWVudCk7XG4gICAgICAgIC8vIFNldHVwIGVsZW1lbnQgZm9yIGRyYWdnaW5nXG4gICAgICAgIGVsZW1lbnQuY2xhc3NMaXN0LmFkZCgnZHJhZ2dpbmcnKTtcbiAgICAgICAgLy8gQ2FsY3VsYXRlIGluaXRpYWwgdGFyZ2V0IGZyb20gbW91c2UgcG9zaXRpb25cbiAgICAgICAgY29uc3QgdGFyZ2V0WSA9IGUuY2xpZW50WSAtIGNvbHVtblJlY3QudG9wIC0gbW91c2VPZmZzZXQueTtcbiAgICAgICAgLy8gSW5pdGlhbGl6ZSBkcmFnIHN0YXRlXG4gICAgICAgIHRoaXMuZHJhZ1N0YXRlID0ge1xuICAgICAgICAgICAgZXZlbnRJZCxcbiAgICAgICAgICAgIGVsZW1lbnQsXG4gICAgICAgICAgICBnaG9zdEVsZW1lbnQsXG4gICAgICAgICAgICBzdGFydFksXG4gICAgICAgICAgICBtb3VzZU9mZnNldCxcbiAgICAgICAgICAgIGNvbHVtbkVsZW1lbnQsXG4gICAgICAgICAgICBjdXJyZW50Q29sdW1uOiBjb2x1bW5FbGVtZW50LFxuICAgICAgICAgICAgdGFyZ2V0WTogTWF0aC5tYXgoMCwgdGFyZ2V0WSksXG4gICAgICAgICAgICBjdXJyZW50WTogc3RhcnRZLFxuICAgICAgICAgICAgYW5pbWF0aW9uSWQ6IDAsXG4gICAgICAgICAgICBzb3VyY2VDb2x1bW5LZXk6IGNvbHVtbkVsZW1lbnQuZGF0YXNldC5jb2x1bW5LZXkgfHwgJycsXG4gICAgICAgICAgICBkcmFnU291cmNlOiAnZ3JpZCdcbiAgICAgICAgfTtcbiAgICAgICAgLy8gRW1pdCBkcmFnOnN0YXJ0XG4gICAgICAgIGNvbnN0IHBheWxvYWQgPSB7XG4gICAgICAgICAgICBldmVudElkLFxuICAgICAgICAgICAgZWxlbWVudCxcbiAgICAgICAgICAgIGdob3N0RWxlbWVudCxcbiAgICAgICAgICAgIHN0YXJ0WSxcbiAgICAgICAgICAgIG1vdXNlT2Zmc2V0LFxuICAgICAgICAgICAgY29sdW1uRWxlbWVudFxuICAgICAgICB9O1xuICAgICAgICB0aGlzLmV2ZW50QnVzLmVtaXQoQ29yZUV2ZW50cy5FVkVOVF9EUkFHX1NUQVJULCBwYXlsb2FkKTtcbiAgICAgICAgLy8gU3RhcnQgYW5pbWF0aW9uIGxvb3BcbiAgICAgICAgdGhpcy5hbmltYXRlRHJhZygpO1xuICAgIH1cbiAgICB1cGRhdGVEcmFnVGFyZ2V0KGUpIHtcbiAgICAgICAgaWYgKCF0aGlzLmRyYWdTdGF0ZSlcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgLy8gQ2hlY2sgaGVhZGVyIHpvbmUgZmlyc3RcbiAgICAgICAgdGhpcy5jaGVja0hlYWRlclpvbmUoZSk7XG4gICAgICAgIC8vIFNraXAgbm9ybWFsIGdyaWQgaGFuZGxpbmcgaWYgaW4gaGVhZGVyXG4gICAgICAgIGlmICh0aGlzLmluSGVhZGVyKVxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAvLyBDaGVjayBmb3IgY29sdW1uIGNoYW5nZVxuICAgICAgICBjb25zdCBjb2x1bW5BdFBvaW50ID0gdGhpcy5nZXRDb2x1bW5BdFBvaW50KGUuY2xpZW50WCk7XG4gICAgICAgIC8vIEZvciBoZWFkZXIgaXRlbXMgZW50ZXJpbmcgZ3JpZCwgc2V0IGluaXRpYWwgY29sdW1uXG4gICAgICAgIGlmICh0aGlzLmRyYWdTdGF0ZS5kcmFnU291cmNlID09PSAnaGVhZGVyJyAmJiBjb2x1bW5BdFBvaW50ICYmICF0aGlzLmRyYWdTdGF0ZS5jdXJyZW50Q29sdW1uKSB7XG4gICAgICAgICAgICB0aGlzLmRyYWdTdGF0ZS5jdXJyZW50Q29sdW1uID0gY29sdW1uQXRQb2ludDtcbiAgICAgICAgICAgIHRoaXMuZHJhZ1N0YXRlLmNvbHVtbkVsZW1lbnQgPSBjb2x1bW5BdFBvaW50O1xuICAgICAgICB9XG4gICAgICAgIGlmIChjb2x1bW5BdFBvaW50ICYmIGNvbHVtbkF0UG9pbnQgIT09IHRoaXMuZHJhZ1N0YXRlLmN1cnJlbnRDb2x1bW4gJiYgdGhpcy5kcmFnU3RhdGUuY3VycmVudENvbHVtbikge1xuICAgICAgICAgICAgY29uc3QgcGF5bG9hZCA9IHtcbiAgICAgICAgICAgICAgICBldmVudElkOiB0aGlzLmRyYWdTdGF0ZS5ldmVudElkLFxuICAgICAgICAgICAgICAgIGVsZW1lbnQ6IHRoaXMuZHJhZ1N0YXRlLmVsZW1lbnQsXG4gICAgICAgICAgICAgICAgcHJldmlvdXNDb2x1bW46IHRoaXMuZHJhZ1N0YXRlLmN1cnJlbnRDb2x1bW4sXG4gICAgICAgICAgICAgICAgbmV3Q29sdW1uOiBjb2x1bW5BdFBvaW50LFxuICAgICAgICAgICAgICAgIGN1cnJlbnRZOiB0aGlzLmRyYWdTdGF0ZS5jdXJyZW50WVxuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIHRoaXMuZXZlbnRCdXMuZW1pdChDb3JlRXZlbnRzLkVWRU5UX0RSQUdfQ09MVU1OX0NIQU5HRSwgcGF5bG9hZCk7XG4gICAgICAgICAgICB0aGlzLmRyYWdTdGF0ZS5jdXJyZW50Q29sdW1uID0gY29sdW1uQXRQb2ludDtcbiAgICAgICAgICAgIHRoaXMuZHJhZ1N0YXRlLmNvbHVtbkVsZW1lbnQgPSBjb2x1bW5BdFBvaW50O1xuICAgICAgICB9XG4gICAgICAgIC8vIFNraXAgZ3JpZCBwb3NpdGlvbiB1cGRhdGVzIGlmIG5vIGNvbHVtbiB5ZXRcbiAgICAgICAgaWYgKCF0aGlzLmRyYWdTdGF0ZS5jb2x1bW5FbGVtZW50KVxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICBjb25zdCBjb2x1bW5SZWN0ID0gdGhpcy5kcmFnU3RhdGUuY29sdW1uRWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcbiAgICAgICAgY29uc3QgdGFyZ2V0WSA9IGUuY2xpZW50WSAtIGNvbHVtblJlY3QudG9wIC0gdGhpcy5kcmFnU3RhdGUubW91c2VPZmZzZXQueTtcbiAgICAgICAgdGhpcy5kcmFnU3RhdGUudGFyZ2V0WSA9IE1hdGgubWF4KDAsIHRhcmdldFkpO1xuICAgICAgICAvLyBTdGFydCBhbmltYXRpb24gaWYgbm90IHJ1bm5pbmdcbiAgICAgICAgaWYgKCF0aGlzLmRyYWdTdGF0ZS5hbmltYXRpb25JZCkge1xuICAgICAgICAgICAgdGhpcy5hbmltYXRlRHJhZygpO1xuICAgICAgICB9XG4gICAgfVxuICAgIC8qKlxuICAgICAqIENoZWNrIGlmIHBvaW50ZXIgaXMgaW4gaGVhZGVyIHpvbmUgYW5kIGVtaXQgYXBwcm9wcmlhdGUgZXZlbnRzXG4gICAgICovXG4gICAgY2hlY2tIZWFkZXJab25lKGUpIHtcbiAgICAgICAgaWYgKCF0aGlzLmRyYWdTdGF0ZSlcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgY29uc3QgaGVhZGVyVmlld3BvcnQgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCdzd3AtaGVhZGVyLXZpZXdwb3J0Jyk7XG4gICAgICAgIGlmICghaGVhZGVyVmlld3BvcnQpXG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIGNvbnN0IHJlY3QgPSBoZWFkZXJWaWV3cG9ydC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcbiAgICAgICAgY29uc3QgaXNJbkhlYWRlciA9IGUuY2xpZW50WSA8IHJlY3QuYm90dG9tO1xuICAgICAgICBpZiAoaXNJbkhlYWRlciAmJiAhdGhpcy5pbkhlYWRlcikge1xuICAgICAgICAgICAgLy8gRW50ZXJlZCBoZWFkZXIgKGZyb20gZ3JpZClcbiAgICAgICAgICAgIHRoaXMuaW5IZWFkZXIgPSB0cnVlO1xuICAgICAgICAgICAgaWYgKHRoaXMuZHJhZ1N0YXRlLmRyYWdTb3VyY2UgPT09ICdncmlkJyAmJiB0aGlzLmRyYWdTdGF0ZS5jb2x1bW5FbGVtZW50KSB7XG4gICAgICAgICAgICAgICAgY29uc3QgcGF5bG9hZCA9IHtcbiAgICAgICAgICAgICAgICAgICAgZXZlbnRJZDogdGhpcy5kcmFnU3RhdGUuZXZlbnRJZCxcbiAgICAgICAgICAgICAgICAgICAgZWxlbWVudDogdGhpcy5kcmFnU3RhdGUuZWxlbWVudCxcbiAgICAgICAgICAgICAgICAgICAgc291cmNlQ29sdW1uSW5kZXg6IHRoaXMuZ2V0Q29sdW1uSW5kZXgodGhpcy5kcmFnU3RhdGUuY29sdW1uRWxlbWVudCksXG4gICAgICAgICAgICAgICAgICAgIHNvdXJjZUNvbHVtbktleTogdGhpcy5kcmFnU3RhdGUuY29sdW1uRWxlbWVudC5kYXRhc2V0LmNvbHVtbktleSB8fCAnJyxcbiAgICAgICAgICAgICAgICAgICAgdGl0bGU6IHRoaXMuZHJhZ1N0YXRlLmVsZW1lbnQucXVlcnlTZWxlY3Rvcignc3dwLWV2ZW50LXRpdGxlJyk/LnRleHRDb250ZW50IHx8ICcnLFxuICAgICAgICAgICAgICAgICAgICBjb2xvckNsYXNzOiBbLi4udGhpcy5kcmFnU3RhdGUuZWxlbWVudC5jbGFzc0xpc3RdLmZpbmQoYyA9PiBjLnN0YXJ0c1dpdGgoJ2lzLScpKSxcbiAgICAgICAgICAgICAgICAgICAgaXRlbVR5cGU6ICdldmVudCcsXG4gICAgICAgICAgICAgICAgICAgIGR1cmF0aW9uOiAxXG4gICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICB0aGlzLmV2ZW50QnVzLmVtaXQoQ29yZUV2ZW50cy5FVkVOVF9EUkFHX0VOVEVSX0hFQURFUiwgcGF5bG9hZCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyBGb3IgaGVhZGVyIHNvdXJjZSByZS1lbnRlcmluZyBoZWFkZXIsIGp1c3QgdXBkYXRlIGluSGVhZGVyIGZsYWdcbiAgICAgICAgfVxuICAgICAgICBlbHNlIGlmICghaXNJbkhlYWRlciAmJiB0aGlzLmluSGVhZGVyKSB7XG4gICAgICAgICAgICAvLyBMZWZ0IGhlYWRlciAoZW50ZXJpbmcgZ3JpZClcbiAgICAgICAgICAgIHRoaXMuaW5IZWFkZXIgPSBmYWxzZTtcbiAgICAgICAgICAgIGNvbnN0IHRhcmdldENvbHVtbiA9IHRoaXMuZ2V0Q29sdW1uQXRQb2ludChlLmNsaWVudFgpO1xuICAgICAgICAgICAgaWYgKHRoaXMuZHJhZ1N0YXRlLmRyYWdTb3VyY2UgPT09ICdoZWFkZXInKSB7XG4gICAgICAgICAgICAgICAgLy8gSGVhZGVyIGl0ZW0gbGVhdmluZyBoZWFkZXIgXHUyMTkyIGNyZWF0ZSBzd3AtZXZlbnQgaW4gZ3JpZFxuICAgICAgICAgICAgICAgIGNvbnN0IHBheWxvYWQgPSB7XG4gICAgICAgICAgICAgICAgICAgIGV2ZW50SWQ6IHRoaXMuZHJhZ1N0YXRlLmV2ZW50SWQsXG4gICAgICAgICAgICAgICAgICAgIHNvdXJjZTogJ2hlYWRlcicsXG4gICAgICAgICAgICAgICAgICAgIGVsZW1lbnQ6IHRoaXMuZHJhZ1N0YXRlLmVsZW1lbnQsXG4gICAgICAgICAgICAgICAgICAgIHRhcmdldENvbHVtbjogdGFyZ2V0Q29sdW1uIHx8IHVuZGVmaW5lZCxcbiAgICAgICAgICAgICAgICAgICAgc3RhcnQ6IHRoaXMuZHJhZ1N0YXRlLmVsZW1lbnQuZGF0YXNldC5zdGFydCA/IG5ldyBEYXRlKHRoaXMuZHJhZ1N0YXRlLmVsZW1lbnQuZGF0YXNldC5zdGFydCkgOiB1bmRlZmluZWQsXG4gICAgICAgICAgICAgICAgICAgIGVuZDogdGhpcy5kcmFnU3RhdGUuZWxlbWVudC5kYXRhc2V0LmVuZCA/IG5ldyBEYXRlKHRoaXMuZHJhZ1N0YXRlLmVsZW1lbnQuZGF0YXNldC5lbmQpIDogdW5kZWZpbmVkLFxuICAgICAgICAgICAgICAgICAgICB0aXRsZTogdGhpcy5kcmFnU3RhdGUuZWxlbWVudC50ZXh0Q29udGVudCB8fCAnJyxcbiAgICAgICAgICAgICAgICAgICAgY29sb3JDbGFzczogWy4uLnRoaXMuZHJhZ1N0YXRlLmVsZW1lbnQuY2xhc3NMaXN0XS5maW5kKGMgPT4gYy5zdGFydHNXaXRoKCdpcy0nKSlcbiAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICAgIHRoaXMuZXZlbnRCdXMuZW1pdChDb3JlRXZlbnRzLkVWRU5UX0RSQUdfTEVBVkVfSEVBREVSLCBwYXlsb2FkKTtcbiAgICAgICAgICAgICAgICAvLyBSZS1hdHRhY2ggdG8gdGhlIG5ldyBzd3AtZXZlbnQgY3JlYXRlZCBieSBFdmVudFJlbmRlcmVyXG4gICAgICAgICAgICAgICAgaWYgKHRhcmdldENvbHVtbikge1xuICAgICAgICAgICAgICAgICAgICBjb25zdCBuZXdFbGVtZW50ID0gdGFyZ2V0Q29sdW1uLnF1ZXJ5U2VsZWN0b3IoYHN3cC1ldmVudFtkYXRhLWV2ZW50LWlkPVwiJHt0aGlzLmRyYWdTdGF0ZS5ldmVudElkfVwiXWApO1xuICAgICAgICAgICAgICAgICAgICBpZiAobmV3RWxlbWVudCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5kcmFnU3RhdGUuZWxlbWVudCA9IG5ld0VsZW1lbnQ7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmRyYWdTdGF0ZS5jb2x1bW5FbGVtZW50ID0gdGFyZ2V0Q29sdW1uO1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5kcmFnU3RhdGUuY3VycmVudENvbHVtbiA9IHRhcmdldENvbHVtbjtcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIFN0YXJ0IGFuaW1hdGlvbiBmb3IgdGhlIG5ldyBlbGVtZW50XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmFuaW1hdGVEcmFnKCk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgICAvLyBHcmlkIGV2ZW50IGxlYXZpbmcgaGVhZGVyIFx1MjE5MiByZXN0b3JlIHRvIGdyaWRcbiAgICAgICAgICAgICAgICBjb25zdCBwYXlsb2FkID0ge1xuICAgICAgICAgICAgICAgICAgICBldmVudElkOiB0aGlzLmRyYWdTdGF0ZS5ldmVudElkLFxuICAgICAgICAgICAgICAgICAgICBzb3VyY2U6ICdncmlkJ1xuICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgICAgdGhpcy5ldmVudEJ1cy5lbWl0KENvcmVFdmVudHMuRVZFTlRfRFJBR19MRUFWRV9IRUFERVIsIHBheWxvYWQpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGVsc2UgaWYgKGlzSW5IZWFkZXIpIHtcbiAgICAgICAgICAgIC8vIE1vdmluZyB3aXRoaW4gaGVhZGVyXG4gICAgICAgICAgICBjb25zdCBjb2x1bW4gPSB0aGlzLmdldENvbHVtbkF0WChlLmNsaWVudFgpO1xuICAgICAgICAgICAgaWYgKGNvbHVtbikge1xuICAgICAgICAgICAgICAgIGNvbnN0IHBheWxvYWQgPSB7XG4gICAgICAgICAgICAgICAgICAgIGV2ZW50SWQ6IHRoaXMuZHJhZ1N0YXRlLmV2ZW50SWQsXG4gICAgICAgICAgICAgICAgICAgIGNvbHVtbkluZGV4OiB0aGlzLmdldENvbHVtbkluZGV4KGNvbHVtbiksXG4gICAgICAgICAgICAgICAgICAgIGNvbHVtbktleTogY29sdW1uLmRhdGFzZXQuY29sdW1uS2V5IHx8ICcnXG4gICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICB0aGlzLmV2ZW50QnVzLmVtaXQoQ29yZUV2ZW50cy5FVkVOVF9EUkFHX01PVkVfSEVBREVSLCBwYXlsb2FkKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cbiAgICAvKipcbiAgICAgKiBHZXQgY29sdW1uIGluZGV4ICgwLWJhc2VkKSBmb3IgYSBjb2x1bW4gZWxlbWVudFxuICAgICAqL1xuICAgIGdldENvbHVtbkluZGV4KGNvbHVtbikge1xuICAgICAgICBpZiAoIXRoaXMuY29udGFpbmVyIHx8ICFjb2x1bW4pXG4gICAgICAgICAgICByZXR1cm4gMDtcbiAgICAgICAgY29uc3QgY29sdW1ucyA9IEFycmF5LmZyb20odGhpcy5jb250YWluZXIucXVlcnlTZWxlY3RvckFsbCgnc3dwLWRheS1jb2x1bW4nKSk7XG4gICAgICAgIHJldHVybiBjb2x1bW5zLmluZGV4T2YoY29sdW1uKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogR2V0IGNvbHVtbiBhdCBYIGNvb3JkaW5hdGUgKGFsaWFzIGZvciBnZXRDb2x1bW5BdFBvaW50KVxuICAgICAqL1xuICAgIGdldENvbHVtbkF0WChjbGllbnRYKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmdldENvbHVtbkF0UG9pbnQoY2xpZW50WCk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEZpbmQgY29sdW1uIGVsZW1lbnQgYXQgZ2l2ZW4gWCBjb29yZGluYXRlXG4gICAgICovXG4gICAgZ2V0Q29sdW1uQXRQb2ludChjbGllbnRYKSB7XG4gICAgICAgIGlmICghdGhpcy5jb250YWluZXIpXG4gICAgICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgICAgY29uc3QgY29sdW1ucyA9IHRoaXMuY29udGFpbmVyLnF1ZXJ5U2VsZWN0b3JBbGwoJ3N3cC1kYXktY29sdW1uJyk7XG4gICAgICAgIGZvciAoY29uc3QgY29sIG9mIGNvbHVtbnMpIHtcbiAgICAgICAgICAgIGNvbnN0IHJlY3QgPSBjb2wuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG4gICAgICAgICAgICBpZiAoY2xpZW50WCA+PSByZWN0LmxlZnQgJiYgY2xpZW50WCA8PSByZWN0LnJpZ2h0KSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGNvbDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gICAgLyoqXG4gICAgICogQ2FuY2VsIGRyYWcgYW5kIGFuaW1hdGUgYmFjayB0byBzdGFydCBwb3NpdGlvblxuICAgICAqL1xuICAgIGNhbmNlbERyYWcoKSB7XG4gICAgICAgIGlmICghdGhpcy5kcmFnU3RhdGUpXG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIC8vIFN0b3AgYW5pbWF0aW9uXG4gICAgICAgIGNhbmNlbEFuaW1hdGlvbkZyYW1lKHRoaXMuZHJhZ1N0YXRlLmFuaW1hdGlvbklkKTtcbiAgICAgICAgY29uc3QgeyBlbGVtZW50LCBnaG9zdEVsZW1lbnQsIHN0YXJ0WSwgZXZlbnRJZCB9ID0gdGhpcy5kcmFnU3RhdGU7XG4gICAgICAgIC8vIEFuaW1hdGUgYmFjayB0byBzdGFydFxuICAgICAgICBlbGVtZW50LnN0eWxlLnRyYW5zaXRpb24gPSAndG9wIDIwMG1zIGVhc2Utb3V0JztcbiAgICAgICAgZWxlbWVudC5zdHlsZS50b3AgPSBgJHtzdGFydFl9cHhgO1xuICAgICAgICAvLyBSZW1vdmUgZ2hvc3QgYWZ0ZXIgYW5pbWF0aW9uIChpZiBleGlzdHMpXG4gICAgICAgIHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICAgICAgZ2hvc3RFbGVtZW50Py5yZW1vdmUoKTtcbiAgICAgICAgICAgIGVsZW1lbnQuc3R5bGUudHJhbnNpdGlvbiA9ICcnO1xuICAgICAgICAgICAgZWxlbWVudC5jbGFzc0xpc3QucmVtb3ZlKCdkcmFnZ2luZycpO1xuICAgICAgICB9LCAyMDApO1xuICAgICAgICAvLyBFbWl0IGRyYWc6Y2FuY2VsXG4gICAgICAgIGNvbnN0IHBheWxvYWQgPSB7XG4gICAgICAgICAgICBldmVudElkLFxuICAgICAgICAgICAgZWxlbWVudCxcbiAgICAgICAgICAgIHN0YXJ0WVxuICAgICAgICB9O1xuICAgICAgICB0aGlzLmV2ZW50QnVzLmVtaXQoQ29yZUV2ZW50cy5FVkVOVF9EUkFHX0NBTkNFTCwgcGF5bG9hZCk7XG4gICAgICAgIHRoaXMuZHJhZ1N0YXRlID0gbnVsbDtcbiAgICAgICAgdGhpcy5pbkhlYWRlciA9IGZhbHNlO1xuICAgIH1cbn1cbiIsICJpbXBvcnQgeyBDb3JlRXZlbnRzIH0gZnJvbSAnLi4vY29uc3RhbnRzL0NvcmVFdmVudHMnO1xuZXhwb3J0IGNsYXNzIEVkZ2VTY3JvbGxNYW5hZ2VyIHtcbiAgICBjb25zdHJ1Y3RvcihldmVudEJ1cykge1xuICAgICAgICB0aGlzLmV2ZW50QnVzID0gZXZlbnRCdXM7XG4gICAgICAgIHRoaXMuc2Nyb2xsYWJsZUNvbnRlbnQgPSBudWxsO1xuICAgICAgICB0aGlzLnRpbWVHcmlkID0gbnVsbDtcbiAgICAgICAgdGhpcy5kcmFnZ2VkRWxlbWVudCA9IG51bGw7XG4gICAgICAgIHRoaXMuc2Nyb2xsUkFGID0gbnVsbDtcbiAgICAgICAgdGhpcy5tb3VzZVkgPSAwO1xuICAgICAgICB0aGlzLmlzRHJhZ2dpbmcgPSBmYWxzZTtcbiAgICAgICAgdGhpcy5pc1Njcm9sbGluZyA9IGZhbHNlO1xuICAgICAgICB0aGlzLmxhc3RUcyA9IDA7XG4gICAgICAgIHRoaXMucmVjdCA9IG51bGw7XG4gICAgICAgIHRoaXMuaW5pdGlhbFNjcm9sbFRvcCA9IDA7XG4gICAgICAgIHRoaXMuT1VURVJfWk9ORSA9IDEwMDtcbiAgICAgICAgdGhpcy5JTk5FUl9aT05FID0gNTA7XG4gICAgICAgIHRoaXMuU0xPV19TUEVFRCA9IDE0MDtcbiAgICAgICAgdGhpcy5GQVNUX1NQRUVEID0gNjQwO1xuICAgICAgICB0aGlzLnRyYWNrTW91c2UgPSAoZSkgPT4ge1xuICAgICAgICAgICAgaWYgKHRoaXMuaXNEcmFnZ2luZykge1xuICAgICAgICAgICAgICAgIHRoaXMubW91c2VZID0gZS5jbGllbnRZO1xuICAgICAgICAgICAgfVxuICAgICAgICB9O1xuICAgICAgICB0aGlzLnNjcm9sbFRpY2sgPSAodHMpID0+IHtcbiAgICAgICAgICAgIGlmICghdGhpcy5pc0RyYWdnaW5nIHx8ICF0aGlzLnNjcm9sbGFibGVDb250ZW50KVxuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIGNvbnN0IGR0ID0gdGhpcy5sYXN0VHMgPyAodHMgLSB0aGlzLmxhc3RUcykgLyAxMDAwIDogMDtcbiAgICAgICAgICAgIHRoaXMubGFzdFRzID0gdHM7XG4gICAgICAgICAgICB0aGlzLnJlY3QgPz8gKHRoaXMucmVjdCA9IHRoaXMuc2Nyb2xsYWJsZUNvbnRlbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCkpO1xuICAgICAgICAgICAgY29uc3QgdmVsb2NpdHkgPSB0aGlzLmNhbGN1bGF0ZVZlbG9jaXR5KCk7XG4gICAgICAgICAgICBpZiAodmVsb2NpdHkgIT09IDAgJiYgIXRoaXMuaXNBdEJvdW5kYXJ5KHZlbG9jaXR5KSkge1xuICAgICAgICAgICAgICAgIGNvbnN0IHNjcm9sbERlbHRhID0gdmVsb2NpdHkgKiBkdDtcbiAgICAgICAgICAgICAgICB0aGlzLnNjcm9sbGFibGVDb250ZW50LnNjcm9sbFRvcCArPSBzY3JvbGxEZWx0YTtcbiAgICAgICAgICAgICAgICB0aGlzLnJlY3QgPSBudWxsO1xuICAgICAgICAgICAgICAgIHRoaXMuZXZlbnRCdXMuZW1pdChDb3JlRXZlbnRzLkVER0VfU0NST0xMX1RJQ0ssIHsgc2Nyb2xsRGVsdGEgfSk7XG4gICAgICAgICAgICAgICAgdGhpcy5zZXRTY3JvbGxpbmdTdGF0ZSh0cnVlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgICAgIHRoaXMuc2V0U2Nyb2xsaW5nU3RhdGUoZmFsc2UpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhpcy5zY3JvbGxSQUYgPSByZXF1ZXN0QW5pbWF0aW9uRnJhbWUodGhpcy5zY3JvbGxUaWNrKTtcbiAgICAgICAgfTtcbiAgICAgICAgdGhpcy5zdWJzY3JpYmVUb0V2ZW50cygpO1xuICAgICAgICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdwb2ludGVybW92ZScsIHRoaXMudHJhY2tNb3VzZSk7XG4gICAgfVxuICAgIGluaXQoc2Nyb2xsYWJsZUNvbnRlbnQpIHtcbiAgICAgICAgdGhpcy5zY3JvbGxhYmxlQ29udGVudCA9IHNjcm9sbGFibGVDb250ZW50O1xuICAgICAgICB0aGlzLnRpbWVHcmlkID0gc2Nyb2xsYWJsZUNvbnRlbnQucXVlcnlTZWxlY3Rvcignc3dwLXRpbWUtZ3JpZCcpO1xuICAgICAgICB0aGlzLnNjcm9sbGFibGVDb250ZW50LnN0eWxlLnNjcm9sbEJlaGF2aW9yID0gJ2F1dG8nO1xuICAgIH1cbiAgICBzdWJzY3JpYmVUb0V2ZW50cygpIHtcbiAgICAgICAgdGhpcy5ldmVudEJ1cy5vbihDb3JlRXZlbnRzLkVWRU5UX0RSQUdfU1RBUlQsIChldmVudCkgPT4ge1xuICAgICAgICAgICAgY29uc3QgcGF5bG9hZCA9IGV2ZW50LmRldGFpbDtcbiAgICAgICAgICAgIHRoaXMuZHJhZ2dlZEVsZW1lbnQgPSBwYXlsb2FkLmVsZW1lbnQ7XG4gICAgICAgICAgICB0aGlzLnN0YXJ0RHJhZygpO1xuICAgICAgICB9KTtcbiAgICAgICAgdGhpcy5ldmVudEJ1cy5vbihDb3JlRXZlbnRzLkVWRU5UX0RSQUdfRU5ELCAoKSA9PiB0aGlzLnN0b3BEcmFnKCkpO1xuICAgICAgICB0aGlzLmV2ZW50QnVzLm9uKENvcmVFdmVudHMuRVZFTlRfRFJBR19DQU5DRUwsICgpID0+IHRoaXMuc3RvcERyYWcoKSk7XG4gICAgfVxuICAgIHN0YXJ0RHJhZygpIHtcbiAgICAgICAgdGhpcy5pc0RyYWdnaW5nID0gdHJ1ZTtcbiAgICAgICAgdGhpcy5pc1Njcm9sbGluZyA9IGZhbHNlO1xuICAgICAgICB0aGlzLmxhc3RUcyA9IDA7XG4gICAgICAgIHRoaXMuaW5pdGlhbFNjcm9sbFRvcCA9IHRoaXMuc2Nyb2xsYWJsZUNvbnRlbnQ/LnNjcm9sbFRvcCA/PyAwO1xuICAgICAgICBpZiAodGhpcy5zY3JvbGxSQUYgPT09IG51bGwpIHtcbiAgICAgICAgICAgIHRoaXMuc2Nyb2xsUkFGID0gcmVxdWVzdEFuaW1hdGlvbkZyYW1lKHRoaXMuc2Nyb2xsVGljayk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgc3RvcERyYWcoKSB7XG4gICAgICAgIHRoaXMuaXNEcmFnZ2luZyA9IGZhbHNlO1xuICAgICAgICB0aGlzLnNldFNjcm9sbGluZ1N0YXRlKGZhbHNlKTtcbiAgICAgICAgaWYgKHRoaXMuc2Nyb2xsUkFGICE9PSBudWxsKSB7XG4gICAgICAgICAgICBjYW5jZWxBbmltYXRpb25GcmFtZSh0aGlzLnNjcm9sbFJBRik7XG4gICAgICAgICAgICB0aGlzLnNjcm9sbFJBRiA9IG51bGw7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5yZWN0ID0gbnVsbDtcbiAgICAgICAgdGhpcy5sYXN0VHMgPSAwO1xuICAgICAgICB0aGlzLmluaXRpYWxTY3JvbGxUb3AgPSAwO1xuICAgIH1cbiAgICBjYWxjdWxhdGVWZWxvY2l0eSgpIHtcbiAgICAgICAgaWYgKCF0aGlzLnJlY3QpXG4gICAgICAgICAgICByZXR1cm4gMDtcbiAgICAgICAgY29uc3QgZGlzdFRvcCA9IHRoaXMubW91c2VZIC0gdGhpcy5yZWN0LnRvcDtcbiAgICAgICAgY29uc3QgZGlzdEJvdCA9IHRoaXMucmVjdC5ib3R0b20gLSB0aGlzLm1vdXNlWTtcbiAgICAgICAgaWYgKGRpc3RUb3AgPCB0aGlzLklOTkVSX1pPTkUpXG4gICAgICAgICAgICByZXR1cm4gLXRoaXMuRkFTVF9TUEVFRDtcbiAgICAgICAgaWYgKGRpc3RUb3AgPCB0aGlzLk9VVEVSX1pPTkUpXG4gICAgICAgICAgICByZXR1cm4gLXRoaXMuU0xPV19TUEVFRDtcbiAgICAgICAgaWYgKGRpc3RCb3QgPCB0aGlzLklOTkVSX1pPTkUpXG4gICAgICAgICAgICByZXR1cm4gdGhpcy5GQVNUX1NQRUVEO1xuICAgICAgICBpZiAoZGlzdEJvdCA8IHRoaXMuT1VURVJfWk9ORSlcbiAgICAgICAgICAgIHJldHVybiB0aGlzLlNMT1dfU1BFRUQ7XG4gICAgICAgIHJldHVybiAwO1xuICAgIH1cbiAgICBpc0F0Qm91bmRhcnkodmVsb2NpdHkpIHtcbiAgICAgICAgaWYgKCF0aGlzLnNjcm9sbGFibGVDb250ZW50IHx8ICF0aGlzLnRpbWVHcmlkIHx8ICF0aGlzLmRyYWdnZWRFbGVtZW50KVxuICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICBjb25zdCBhdFRvcCA9IHRoaXMuc2Nyb2xsYWJsZUNvbnRlbnQuc2Nyb2xsVG9wIDw9IDAgJiYgdmVsb2NpdHkgPCAwO1xuICAgICAgICBjb25zdCBhdEJvdHRvbSA9IHZlbG9jaXR5ID4gMCAmJlxuICAgICAgICAgICAgdGhpcy5kcmFnZ2VkRWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKS5ib3R0b20gPj1cbiAgICAgICAgICAgICAgICB0aGlzLnRpbWVHcmlkLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpLmJvdHRvbTtcbiAgICAgICAgcmV0dXJuIGF0VG9wIHx8IGF0Qm90dG9tO1xuICAgIH1cbiAgICBzZXRTY3JvbGxpbmdTdGF0ZShzY3JvbGxpbmcpIHtcbiAgICAgICAgaWYgKHRoaXMuaXNTY3JvbGxpbmcgPT09IHNjcm9sbGluZylcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgdGhpcy5pc1Njcm9sbGluZyA9IHNjcm9sbGluZztcbiAgICAgICAgaWYgKHNjcm9sbGluZykge1xuICAgICAgICAgICAgdGhpcy5ldmVudEJ1cy5lbWl0KENvcmVFdmVudHMuRURHRV9TQ1JPTExfU1RBUlRFRCwge30pO1xuICAgICAgICB9XG4gICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5pbml0aWFsU2Nyb2xsVG9wID0gdGhpcy5zY3JvbGxhYmxlQ29udGVudD8uc2Nyb2xsVG9wID8/IDA7XG4gICAgICAgICAgICB0aGlzLmV2ZW50QnVzLmVtaXQoQ29yZUV2ZW50cy5FREdFX1NDUk9MTF9TVE9QUEVELCB7fSk7XG4gICAgICAgIH1cbiAgICB9XG59XG4iLCAiaW1wb3J0IHsgcGl4ZWxzVG9NaW51dGVzLCBtaW51dGVzVG9QaXhlbHMsIHNuYXBUb0dyaWQgfSBmcm9tICcuLi91dGlscy9Qb3NpdGlvblV0aWxzJztcbmltcG9ydCB7IENvcmVFdmVudHMgfSBmcm9tICcuLi9jb25zdGFudHMvQ29yZUV2ZW50cyc7XG5pbXBvcnQgeyBTd3BFdmVudCB9IGZyb20gJy4uL3R5cGVzL1N3cEV2ZW50JztcbmV4cG9ydCBjbGFzcyBSZXNpemVNYW5hZ2VyIHtcbiAgICBjb25zdHJ1Y3RvcihldmVudEJ1cywgZ3JpZENvbmZpZywgZGF0ZVNlcnZpY2UpIHtcbiAgICAgICAgdGhpcy5ldmVudEJ1cyA9IGV2ZW50QnVzO1xuICAgICAgICB0aGlzLmdyaWRDb25maWcgPSBncmlkQ29uZmlnO1xuICAgICAgICB0aGlzLmRhdGVTZXJ2aWNlID0gZGF0ZVNlcnZpY2U7XG4gICAgICAgIHRoaXMuY29udGFpbmVyID0gbnVsbDtcbiAgICAgICAgdGhpcy5yZXNpemVTdGF0ZSA9IG51bGw7XG4gICAgICAgIHRoaXMuWl9JTkRFWF9SRVNJWklORyA9ICcxMDAwJztcbiAgICAgICAgdGhpcy5BTklNQVRJT05fU1BFRUQgPSAwLjM1O1xuICAgICAgICB0aGlzLk1JTl9IRUlHSFRfTUlOVVRFUyA9IDE1O1xuICAgICAgICAvKipcbiAgICAgICAgICogSGFuZGxlIG1vdXNlb3ZlciAtIGNyZWF0ZSByZXNpemUgaGFuZGxlIGlmIG5vdCBleGlzdHNcbiAgICAgICAgICovXG4gICAgICAgIHRoaXMuaGFuZGxlTW91c2VPdmVyID0gKGUpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHRhcmdldCA9IGUudGFyZ2V0O1xuICAgICAgICAgICAgY29uc3QgZXZlbnRFbGVtZW50ID0gdGFyZ2V0LmNsb3Nlc3QoJ3N3cC1ldmVudCcpO1xuICAgICAgICAgICAgaWYgKCFldmVudEVsZW1lbnQgfHwgdGhpcy5yZXNpemVTdGF0ZSlcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICAvLyBDaGVjayBpZiBoYW5kbGUgYWxyZWFkeSBleGlzdHNcbiAgICAgICAgICAgIGlmICghZXZlbnRFbGVtZW50LnF1ZXJ5U2VsZWN0b3IoJzpzY29wZSA+IHN3cC1yZXNpemUtaGFuZGxlJykpIHtcbiAgICAgICAgICAgICAgICBjb25zdCBoYW5kbGUgPSB0aGlzLmNyZWF0ZVJlc2l6ZUhhbmRsZSgpO1xuICAgICAgICAgICAgICAgIGV2ZW50RWxlbWVudC5hcHBlbmRDaGlsZChoYW5kbGUpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9O1xuICAgICAgICAvKipcbiAgICAgICAgICogSGFuZGxlIHBvaW50ZXJkb3duIC0gc3RhcnQgcmVzaXplIGlmIG9uIGhhbmRsZVxuICAgICAgICAgKi9cbiAgICAgICAgdGhpcy5oYW5kbGVQb2ludGVyRG93biA9IChlKSA9PiB7XG4gICAgICAgICAgICBjb25zdCBoYW5kbGUgPSBlLnRhcmdldC5jbG9zZXN0KCdzd3AtcmVzaXplLWhhbmRsZScpO1xuICAgICAgICAgICAgaWYgKCFoYW5kbGUpXG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgY29uc3QgZWxlbWVudCA9IGhhbmRsZS5wYXJlbnRFbGVtZW50O1xuICAgICAgICAgICAgaWYgKCFlbGVtZW50KVxuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIGNvbnN0IGV2ZW50SWQgPSBlbGVtZW50LmRhdGFzZXQuZXZlbnRJZCB8fCAnJztcbiAgICAgICAgICAgIGNvbnN0IHN0YXJ0SGVpZ2h0ID0gZWxlbWVudC5vZmZzZXRIZWlnaHQ7XG4gICAgICAgICAgICBjb25zdCBzdGFydER1cmF0aW9uTWludXRlcyA9IHBpeGVsc1RvTWludXRlcyhzdGFydEhlaWdodCwgdGhpcy5ncmlkQ29uZmlnKTtcbiAgICAgICAgICAgIC8vIFN0b3JlIHByZXZpb3VzIHotaW5kZXhcbiAgICAgICAgICAgIGNvbnN0IGNvbnRhaW5lciA9IGVsZW1lbnQuY2xvc2VzdCgnc3dwLWV2ZW50LWdyb3VwJykgPz8gZWxlbWVudDtcbiAgICAgICAgICAgIGNvbnN0IHByZXZaSW5kZXggPSBjb250YWluZXIuc3R5bGUuekluZGV4O1xuICAgICAgICAgICAgLy8gU2V0IHJlc2l6ZSBzdGF0ZVxuICAgICAgICAgICAgdGhpcy5yZXNpemVTdGF0ZSA9IHtcbiAgICAgICAgICAgICAgICBldmVudElkLFxuICAgICAgICAgICAgICAgIGVsZW1lbnQsXG4gICAgICAgICAgICAgICAgaGFuZGxlRWxlbWVudDogaGFuZGxlLFxuICAgICAgICAgICAgICAgIHN0YXJ0WTogZS5jbGllbnRZLFxuICAgICAgICAgICAgICAgIHN0YXJ0SGVpZ2h0LFxuICAgICAgICAgICAgICAgIHN0YXJ0RHVyYXRpb25NaW51dGVzLFxuICAgICAgICAgICAgICAgIHBvaW50ZXJJZDogZS5wb2ludGVySWQsXG4gICAgICAgICAgICAgICAgcHJldlpJbmRleCxcbiAgICAgICAgICAgICAgICAvLyBBbmltYXRpb24gc3RhdGVcbiAgICAgICAgICAgICAgICBjdXJyZW50SGVpZ2h0OiBzdGFydEhlaWdodCxcbiAgICAgICAgICAgICAgICB0YXJnZXRIZWlnaHQ6IHN0YXJ0SGVpZ2h0LFxuICAgICAgICAgICAgICAgIGFuaW1hdGlvbklkOiBudWxsXG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgLy8gRWxldmF0ZSB6LWluZGV4XG4gICAgICAgICAgICBjb250YWluZXIuc3R5bGUuekluZGV4ID0gdGhpcy5aX0lOREVYX1JFU0laSU5HO1xuICAgICAgICAgICAgLy8gQ2FwdHVyZSBwb2ludGVyIGZvciBzbW9vdGggdHJhY2tpbmdcbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgaGFuZGxlLnNldFBvaW50ZXJDYXB0dXJlKGUucG9pbnRlcklkKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNhdGNoIChlcnIpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLndhcm4oJ1BvaW50ZXIgY2FwdHVyZSBmYWlsZWQ6JywgZXJyKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIEFkZCBnbG9iYWwgcmVzaXppbmcgY2xhc3NcbiAgICAgICAgICAgIGRvY3VtZW50LmRvY3VtZW50RWxlbWVudC5jbGFzc0xpc3QuYWRkKCdzd3AtLXJlc2l6aW5nJyk7XG4gICAgICAgICAgICAvLyBFbWl0IHJlc2l6ZSBzdGFydCBldmVudFxuICAgICAgICAgICAgdGhpcy5ldmVudEJ1cy5lbWl0KENvcmVFdmVudHMuRVZFTlRfUkVTSVpFX1NUQVJULCB7XG4gICAgICAgICAgICAgICAgZXZlbnRJZCxcbiAgICAgICAgICAgICAgICBlbGVtZW50LFxuICAgICAgICAgICAgICAgIHN0YXJ0SGVpZ2h0XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIGUucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgfTtcbiAgICAgICAgLyoqXG4gICAgICAgICAqIEhhbmRsZSBwb2ludGVybW92ZSAtIHVwZGF0ZSB0YXJnZXQgaGVpZ2h0IGR1cmluZyByZXNpemVcbiAgICAgICAgICovXG4gICAgICAgIHRoaXMuaGFuZGxlUG9pbnRlck1vdmUgPSAoZSkgPT4ge1xuICAgICAgICAgICAgaWYgKCF0aGlzLnJlc2l6ZVN0YXRlKVxuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIGNvbnN0IGRlbHRhWSA9IGUuY2xpZW50WSAtIHRoaXMucmVzaXplU3RhdGUuc3RhcnRZO1xuICAgICAgICAgICAgY29uc3QgbWluSGVpZ2h0ID0gKHRoaXMuTUlOX0hFSUdIVF9NSU5VVEVTIC8gNjApICogdGhpcy5ncmlkQ29uZmlnLmhvdXJIZWlnaHQ7XG4gICAgICAgICAgICBjb25zdCBuZXdIZWlnaHQgPSBNYXRoLm1heChtaW5IZWlnaHQsIHRoaXMucmVzaXplU3RhdGUuc3RhcnRIZWlnaHQgKyBkZWx0YVkpO1xuICAgICAgICAgICAgLy8gU2V0IHRhcmdldCBoZWlnaHQgZm9yIGFuaW1hdGlvblxuICAgICAgICAgICAgdGhpcy5yZXNpemVTdGF0ZS50YXJnZXRIZWlnaHQgPSBuZXdIZWlnaHQ7XG4gICAgICAgICAgICAvLyBTdGFydCBhbmltYXRpb24gaWYgbm90IHJ1bm5pbmdcbiAgICAgICAgICAgIGlmICh0aGlzLnJlc2l6ZVN0YXRlLmFuaW1hdGlvbklkID09PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5hbmltYXRlSGVpZ2h0KCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH07XG4gICAgICAgIC8qKlxuICAgICAgICAgKiBSQUYgYW5pbWF0aW9uIGxvb3AgZm9yIHNtb290aCBoZWlnaHQgaW50ZXJwb2xhdGlvblxuICAgICAgICAgKi9cbiAgICAgICAgdGhpcy5hbmltYXRlSGVpZ2h0ID0gKCkgPT4ge1xuICAgICAgICAgICAgaWYgKCF0aGlzLnJlc2l6ZVN0YXRlKVxuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIGNvbnN0IGRpZmYgPSB0aGlzLnJlc2l6ZVN0YXRlLnRhcmdldEhlaWdodCAtIHRoaXMucmVzaXplU3RhdGUuY3VycmVudEhlaWdodDtcbiAgICAgICAgICAgIC8vIFN0b3AgYW5pbWF0aW9uIHdoZW4gY2xvc2UgZW5vdWdoXG4gICAgICAgICAgICBpZiAoTWF0aC5hYnMoZGlmZikgPCAwLjUpIHtcbiAgICAgICAgICAgICAgICB0aGlzLnJlc2l6ZVN0YXRlLmFuaW1hdGlvbklkID0gbnVsbDtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyBJbnRlcnBvbGF0ZSB0b3dhcmRzIHRhcmdldCAoMzUlIHBlciBmcmFtZSBsaWtlIFYxKVxuICAgICAgICAgICAgdGhpcy5yZXNpemVTdGF0ZS5jdXJyZW50SGVpZ2h0ICs9IGRpZmYgKiB0aGlzLkFOSU1BVElPTl9TUEVFRDtcbiAgICAgICAgICAgIHRoaXMucmVzaXplU3RhdGUuZWxlbWVudC5zdHlsZS5oZWlnaHQgPSBgJHt0aGlzLnJlc2l6ZVN0YXRlLmN1cnJlbnRIZWlnaHR9cHhgO1xuICAgICAgICAgICAgLy8gVXBkYXRlIHRpbWVzdGFtcCBkaXNwbGF5IChzbmFwcGVkKVxuICAgICAgICAgICAgdGhpcy51cGRhdGVUaW1lc3RhbXBEaXNwbGF5KCk7XG4gICAgICAgICAgICAvLyBDb250aW51ZSBhbmltYXRpb25cbiAgICAgICAgICAgIHRoaXMucmVzaXplU3RhdGUuYW5pbWF0aW9uSWQgPSByZXF1ZXN0QW5pbWF0aW9uRnJhbWUodGhpcy5hbmltYXRlSGVpZ2h0KTtcbiAgICAgICAgfTtcbiAgICAgICAgLyoqXG4gICAgICAgICAqIEhhbmRsZSBwb2ludGVydXAgLSBmaW5pc2ggcmVzaXplXG4gICAgICAgICAqL1xuICAgICAgICB0aGlzLmhhbmRsZVBvaW50ZXJVcCA9IChlKSA9PiB7XG4gICAgICAgICAgICBpZiAoIXRoaXMucmVzaXplU3RhdGUpXG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgLy8gQ2FuY2VsIGFueSBwZW5kaW5nIGFuaW1hdGlvblxuICAgICAgICAgICAgaWYgKHRoaXMucmVzaXplU3RhdGUuYW5pbWF0aW9uSWQgIT09IG51bGwpIHtcbiAgICAgICAgICAgICAgICBjYW5jZWxBbmltYXRpb25GcmFtZSh0aGlzLnJlc2l6ZVN0YXRlLmFuaW1hdGlvbklkKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIFJlbGVhc2UgcG9pbnRlciBjYXB0dXJlXG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgIHRoaXMucmVzaXplU3RhdGUuaGFuZGxlRWxlbWVudC5yZWxlYXNlUG9pbnRlckNhcHR1cmUoZS5wb2ludGVySWQpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgY2F0Y2ggKGVycikge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUud2FybignUG9pbnRlciByZWxlYXNlIGZhaWxlZDonLCBlcnIpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgLy8gU25hcCBmaW5hbCBoZWlnaHQgdG8gZ3JpZFxuICAgICAgICAgICAgdGhpcy5zbmFwVG9HcmlkRmluYWwoKTtcbiAgICAgICAgICAgIC8vIFVwZGF0ZSB0aW1lc3RhbXAgb25lIGZpbmFsIHRpbWVcbiAgICAgICAgICAgIHRoaXMudXBkYXRlVGltZXN0YW1wRGlzcGxheSgpO1xuICAgICAgICAgICAgLy8gUmVzdG9yZSB6LWluZGV4XG4gICAgICAgICAgICBjb25zdCBjb250YWluZXIgPSB0aGlzLnJlc2l6ZVN0YXRlLmVsZW1lbnQuY2xvc2VzdCgnc3dwLWV2ZW50LWdyb3VwJykgPz8gdGhpcy5yZXNpemVTdGF0ZS5lbGVtZW50O1xuICAgICAgICAgICAgY29udGFpbmVyLnN0eWxlLnpJbmRleCA9IHRoaXMucmVzaXplU3RhdGUucHJldlpJbmRleDtcbiAgICAgICAgICAgIC8vIFJlbW92ZSBnbG9iYWwgcmVzaXppbmcgY2xhc3NcbiAgICAgICAgICAgIGRvY3VtZW50LmRvY3VtZW50RWxlbWVudC5jbGFzc0xpc3QucmVtb3ZlKCdzd3AtLXJlc2l6aW5nJyk7XG4gICAgICAgICAgICAvLyBHZXQgY29sdW1uS2V5IGFuZCBkYXRlIGZyb20gcGFyZW50IGNvbHVtblxuICAgICAgICAgICAgY29uc3QgY29sdW1uID0gdGhpcy5yZXNpemVTdGF0ZS5lbGVtZW50LmNsb3Nlc3QoJ3N3cC1kYXktY29sdW1uJyk7XG4gICAgICAgICAgICBjb25zdCBjb2x1bW5LZXkgPSBjb2x1bW4/LmRhdGFzZXQuY29sdW1uS2V5IHx8ICcnO1xuICAgICAgICAgICAgY29uc3QgZGF0ZSA9IGNvbHVtbj8uZGF0YXNldC5kYXRlIHx8ICcnO1xuICAgICAgICAgICAgLy8gQ3JlYXRlIFN3cEV2ZW50IGZyb20gZWxlbWVudCAocmVhZHMgdG9wL2hlaWdodC9ldmVudElkIGZyb20gZWxlbWVudClcbiAgICAgICAgICAgIGNvbnN0IHN3cEV2ZW50ID0gU3dwRXZlbnQuZnJvbUVsZW1lbnQodGhpcy5yZXNpemVTdGF0ZS5lbGVtZW50LCBjb2x1bW5LZXksIGRhdGUsIHRoaXMuZ3JpZENvbmZpZyk7XG4gICAgICAgICAgICAvLyBFbWl0IHJlc2l6ZSBlbmQgZXZlbnRcbiAgICAgICAgICAgIHRoaXMuZXZlbnRCdXMuZW1pdChDb3JlRXZlbnRzLkVWRU5UX1JFU0laRV9FTkQsIHtcbiAgICAgICAgICAgICAgICBzd3BFdmVudFxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAvLyBSZXNldCBzdGF0ZVxuICAgICAgICAgICAgdGhpcy5yZXNpemVTdGF0ZSA9IG51bGw7XG4gICAgICAgIH07XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEluaXRpYWxpemUgcmVzaXplIGZ1bmN0aW9uYWxpdHkgb24gY29udGFpbmVyXG4gICAgICovXG4gICAgaW5pdChjb250YWluZXIpIHtcbiAgICAgICAgdGhpcy5jb250YWluZXIgPSBjb250YWluZXI7XG4gICAgICAgIC8vIE1vdXNlb3ZlciBsaXN0ZW5lciBmb3IgaGFuZGxlIGNyZWF0aW9uIChjYXB0dXJlIHBoYXNlIGxpa2UgVjEpXG4gICAgICAgIGNvbnRhaW5lci5hZGRFdmVudExpc3RlbmVyKCdtb3VzZW92ZXInLCB0aGlzLmhhbmRsZU1vdXNlT3ZlciwgdHJ1ZSk7XG4gICAgICAgIC8vIFBvaW50ZXIgbGlzdGVuZXJzIGZvciByZXNpemUgKGNhcHR1cmUgcGhhc2UgbGlrZSBWMSlcbiAgICAgICAgZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcigncG9pbnRlcmRvd24nLCB0aGlzLmhhbmRsZVBvaW50ZXJEb3duLCB0cnVlKTtcbiAgICAgICAgZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcigncG9pbnRlcm1vdmUnLCB0aGlzLmhhbmRsZVBvaW50ZXJNb3ZlLCB0cnVlKTtcbiAgICAgICAgZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcigncG9pbnRlcnVwJywgdGhpcy5oYW5kbGVQb2ludGVyVXAsIHRydWUpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBDcmVhdGUgcmVzaXplIGhhbmRsZSBlbGVtZW50XG4gICAgICovXG4gICAgY3JlYXRlUmVzaXplSGFuZGxlKCkge1xuICAgICAgICBjb25zdCBoYW5kbGUgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzd3AtcmVzaXplLWhhbmRsZScpO1xuICAgICAgICBoYW5kbGUuc2V0QXR0cmlidXRlKCdhcmlhLWxhYmVsJywgJ1Jlc2l6ZSBldmVudCcpO1xuICAgICAgICBoYW5kbGUuc2V0QXR0cmlidXRlKCdyb2xlJywgJ3NlcGFyYXRvcicpO1xuICAgICAgICByZXR1cm4gaGFuZGxlO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBVcGRhdGUgdGltZXN0YW1wIGRpc3BsYXkgd2l0aCBzbmFwcGVkIGVuZCB0aW1lXG4gICAgICovXG4gICAgdXBkYXRlVGltZXN0YW1wRGlzcGxheSgpIHtcbiAgICAgICAgaWYgKCF0aGlzLnJlc2l6ZVN0YXRlKVxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICBjb25zdCB0aW1lRWwgPSB0aGlzLnJlc2l6ZVN0YXRlLmVsZW1lbnQucXVlcnlTZWxlY3Rvcignc3dwLWV2ZW50LXRpbWUnKTtcbiAgICAgICAgaWYgKCF0aW1lRWwpXG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIC8vIEdldCBzdGFydCB0aW1lIGZyb20gZWxlbWVudCBwb3NpdGlvblxuICAgICAgICBjb25zdCB0b3AgPSBwYXJzZUZsb2F0KHRoaXMucmVzaXplU3RhdGUuZWxlbWVudC5zdHlsZS50b3ApIHx8IDA7XG4gICAgICAgIGNvbnN0IHN0YXJ0TWludXRlc0Zyb21HcmlkID0gcGl4ZWxzVG9NaW51dGVzKHRvcCwgdGhpcy5ncmlkQ29uZmlnKTtcbiAgICAgICAgY29uc3Qgc3RhcnRNaW51dGVzID0gKHRoaXMuZ3JpZENvbmZpZy5kYXlTdGFydEhvdXIgKiA2MCkgKyBzdGFydE1pbnV0ZXNGcm9tR3JpZDtcbiAgICAgICAgLy8gQ2FsY3VsYXRlIHNuYXBwZWQgZW5kIHRpbWUgZnJvbSBjdXJyZW50IGhlaWdodFxuICAgICAgICBjb25zdCBzbmFwcGVkSGVpZ2h0ID0gc25hcFRvR3JpZCh0aGlzLnJlc2l6ZVN0YXRlLmN1cnJlbnRIZWlnaHQsIHRoaXMuZ3JpZENvbmZpZyk7XG4gICAgICAgIGNvbnN0IGR1cmF0aW9uTWludXRlcyA9IHBpeGVsc1RvTWludXRlcyhzbmFwcGVkSGVpZ2h0LCB0aGlzLmdyaWRDb25maWcpO1xuICAgICAgICBjb25zdCBlbmRNaW51dGVzID0gc3RhcnRNaW51dGVzICsgZHVyYXRpb25NaW51dGVzO1xuICAgICAgICAvLyBGb3JtYXQgYW5kIHVwZGF0ZVxuICAgICAgICBjb25zdCBzdGFydCA9IHRoaXMubWludXRlc1RvRGF0ZShzdGFydE1pbnV0ZXMpO1xuICAgICAgICBjb25zdCBlbmQgPSB0aGlzLm1pbnV0ZXNUb0RhdGUoZW5kTWludXRlcyk7XG4gICAgICAgIHRpbWVFbC50ZXh0Q29udGVudCA9IHRoaXMuZGF0ZVNlcnZpY2UuZm9ybWF0VGltZVJhbmdlKHN0YXJ0LCBlbmQpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBDb252ZXJ0IG1pbnV0ZXMgc2luY2UgbWlkbmlnaHQgdG8gRGF0ZVxuICAgICAqL1xuICAgIG1pbnV0ZXNUb0RhdGUobWludXRlcykge1xuICAgICAgICBjb25zdCBkYXRlID0gbmV3IERhdGUoKTtcbiAgICAgICAgZGF0ZS5zZXRIb3VycyhNYXRoLmZsb29yKG1pbnV0ZXMgLyA2MCkgJSAyNCwgbWludXRlcyAlIDYwLCAwLCAwKTtcbiAgICAgICAgcmV0dXJuIGRhdGU7XG4gICAgfVxuICAgIDtcbiAgICAvKipcbiAgICAgKiBTbmFwIGZpbmFsIGhlaWdodCB0byBncmlkIGludGVydmFsXG4gICAgICovXG4gICAgc25hcFRvR3JpZEZpbmFsKCkge1xuICAgICAgICBpZiAoIXRoaXMucmVzaXplU3RhdGUpXG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIGNvbnN0IGN1cnJlbnRIZWlnaHQgPSB0aGlzLnJlc2l6ZVN0YXRlLmVsZW1lbnQub2Zmc2V0SGVpZ2h0O1xuICAgICAgICBjb25zdCBzbmFwcGVkSGVpZ2h0ID0gc25hcFRvR3JpZChjdXJyZW50SGVpZ2h0LCB0aGlzLmdyaWRDb25maWcpO1xuICAgICAgICBjb25zdCBtaW5IZWlnaHQgPSBtaW51dGVzVG9QaXhlbHModGhpcy5NSU5fSEVJR0hUX01JTlVURVMsIHRoaXMuZ3JpZENvbmZpZyk7XG4gICAgICAgIGNvbnN0IGZpbmFsSGVpZ2h0ID0gTWF0aC5tYXgobWluSGVpZ2h0LCBzbmFwcGVkSGVpZ2h0KTtcbiAgICAgICAgdGhpcy5yZXNpemVTdGF0ZS5lbGVtZW50LnN0eWxlLmhlaWdodCA9IGAke2ZpbmFsSGVpZ2h0fXB4YDtcbiAgICAgICAgdGhpcy5yZXNpemVTdGF0ZS5jdXJyZW50SGVpZ2h0ID0gZmluYWxIZWlnaHQ7XG4gICAgfVxufVxuIiwgImltcG9ydCB7IENvcmVFdmVudHMgfSBmcm9tICcuLi9jb25zdGFudHMvQ29yZUV2ZW50cyc7XG5leHBvcnQgY2xhc3MgRXZlbnRQZXJzaXN0ZW5jZU1hbmFnZXIge1xuICAgIGNvbnN0cnVjdG9yKGV2ZW50U2VydmljZSwgZXZlbnRCdXMsIGRhdGVTZXJ2aWNlKSB7XG4gICAgICAgIHRoaXMuZXZlbnRTZXJ2aWNlID0gZXZlbnRTZXJ2aWNlO1xuICAgICAgICB0aGlzLmV2ZW50QnVzID0gZXZlbnRCdXM7XG4gICAgICAgIHRoaXMuZGF0ZVNlcnZpY2UgPSBkYXRlU2VydmljZTtcbiAgICAgICAgLyoqXG4gICAgICAgICAqIEhhbmRsZSBkcmFnIGVuZCAtIHVwZGF0ZSBldmVudCBwb3NpdGlvbiBpbiBJbmRleGVkREJcbiAgICAgICAgICovXG4gICAgICAgIHRoaXMuaGFuZGxlRHJhZ0VuZCA9IGFzeW5jIChlKSA9PiB7XG4gICAgICAgICAgICBjb25zdCBwYXlsb2FkID0gZS5kZXRhaWw7XG4gICAgICAgICAgICBjb25zdCB7IHN3cEV2ZW50IH0gPSBwYXlsb2FkO1xuICAgICAgICAgICAgLy8gR2V0IGV4aXN0aW5nIGV2ZW50IHRvIG1lcmdlIHdpdGhcbiAgICAgICAgICAgIGNvbnN0IGV2ZW50ID0gYXdhaXQgdGhpcy5ldmVudFNlcnZpY2UuZ2V0KHN3cEV2ZW50LmV2ZW50SWQpO1xuICAgICAgICAgICAgaWYgKCFldmVudCkge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUud2FybihgRXZlbnRQZXJzaXN0ZW5jZU1hbmFnZXI6IEV2ZW50ICR7c3dwRXZlbnQuZXZlbnRJZH0gbm90IGZvdW5kYCk7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgLy8gUGFyc2UgcmVzb3VyY2VJZCBmcm9tIGNvbHVtbktleSBpZiBwcmVzZW50XG4gICAgICAgICAgICBjb25zdCB7IHJlc291cmNlIH0gPSB0aGlzLmRhdGVTZXJ2aWNlLnBhcnNlQ29sdW1uS2V5KHN3cEV2ZW50LmNvbHVtbktleSk7XG4gICAgICAgICAgICAvLyBVcGRhdGUgYW5kIHNhdmUgLSBzdGFydC9lbmQgYWxyZWFkeSBjYWxjdWxhdGVkIGluIFN3cEV2ZW50XG4gICAgICAgICAgICAvLyBTZXQgYWxsRGF5IGJhc2VkIG9uIGRyb3AgdGFyZ2V0OlxuICAgICAgICAgICAgLy8gLSBoZWFkZXI6IGFsbERheSA9IHRydWVcbiAgICAgICAgICAgIC8vIC0gZ3JpZDogYWxsRGF5ID0gZmFsc2UgKGNvbnZlcnRzIGFsbERheSBldmVudCB0byB0aW1lZClcbiAgICAgICAgICAgIGNvbnN0IHVwZGF0ZWRFdmVudCA9IHtcbiAgICAgICAgICAgICAgICAuLi5ldmVudCxcbiAgICAgICAgICAgICAgICBzdGFydDogc3dwRXZlbnQuc3RhcnQsXG4gICAgICAgICAgICAgICAgZW5kOiBzd3BFdmVudC5lbmQsXG4gICAgICAgICAgICAgICAgcmVzb3VyY2VJZDogcmVzb3VyY2UgPz8gZXZlbnQucmVzb3VyY2VJZCxcbiAgICAgICAgICAgICAgICBhbGxEYXk6IHBheWxvYWQudGFyZ2V0ID09PSAnaGVhZGVyJyxcbiAgICAgICAgICAgICAgICBzeW5jU3RhdHVzOiAncGVuZGluZydcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICBhd2FpdCB0aGlzLmV2ZW50U2VydmljZS5zYXZlKHVwZGF0ZWRFdmVudCk7XG4gICAgICAgICAgICAvLyBFbWl0IEVWRU5UX1VQREFURUQgZm9yIEV2ZW50UmVuZGVyZXIgdG8gcmUtcmVuZGVyIGFmZmVjdGVkIGNvbHVtbnNcbiAgICAgICAgICAgIGNvbnN0IHVwZGF0ZVBheWxvYWQgPSB7XG4gICAgICAgICAgICAgICAgZXZlbnRJZDogdXBkYXRlZEV2ZW50LmlkLFxuICAgICAgICAgICAgICAgIHNvdXJjZUNvbHVtbktleTogcGF5bG9hZC5zb3VyY2VDb2x1bW5LZXksXG4gICAgICAgICAgICAgICAgdGFyZ2V0Q29sdW1uS2V5OiBzd3BFdmVudC5jb2x1bW5LZXlcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICB0aGlzLmV2ZW50QnVzLmVtaXQoQ29yZUV2ZW50cy5FVkVOVF9VUERBVEVELCB1cGRhdGVQYXlsb2FkKTtcbiAgICAgICAgfTtcbiAgICAgICAgLyoqXG4gICAgICAgICAqIEhhbmRsZSByZXNpemUgZW5kIC0gdXBkYXRlIGV2ZW50IGR1cmF0aW9uIGluIEluZGV4ZWREQlxuICAgICAgICAgKi9cbiAgICAgICAgdGhpcy5oYW5kbGVSZXNpemVFbmQgPSBhc3luYyAoZSkgPT4ge1xuICAgICAgICAgICAgY29uc3QgcGF5bG9hZCA9IGUuZGV0YWlsO1xuICAgICAgICAgICAgY29uc3QgeyBzd3BFdmVudCB9ID0gcGF5bG9hZDtcbiAgICAgICAgICAgIC8vIEdldCBleGlzdGluZyBldmVudCB0byBtZXJnZSB3aXRoXG4gICAgICAgICAgICBjb25zdCBldmVudCA9IGF3YWl0IHRoaXMuZXZlbnRTZXJ2aWNlLmdldChzd3BFdmVudC5ldmVudElkKTtcbiAgICAgICAgICAgIGlmICghZXZlbnQpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLndhcm4oYEV2ZW50UGVyc2lzdGVuY2VNYW5hZ2VyOiBFdmVudCAke3N3cEV2ZW50LmV2ZW50SWR9IG5vdCBmb3VuZGApO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIFVwZGF0ZSBhbmQgc2F2ZSAtIGVuZCBhbHJlYWR5IGNhbGN1bGF0ZWQgaW4gU3dwRXZlbnRcbiAgICAgICAgICAgIGNvbnN0IHVwZGF0ZWRFdmVudCA9IHtcbiAgICAgICAgICAgICAgICAuLi5ldmVudCxcbiAgICAgICAgICAgICAgICBlbmQ6IHN3cEV2ZW50LmVuZCxcbiAgICAgICAgICAgICAgICBzeW5jU3RhdHVzOiAncGVuZGluZydcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICBhd2FpdCB0aGlzLmV2ZW50U2VydmljZS5zYXZlKHVwZGF0ZWRFdmVudCk7XG4gICAgICAgICAgICAvLyBFbWl0IEVWRU5UX1VQREFURUQgZm9yIEV2ZW50UmVuZGVyZXIgdG8gcmUtcmVuZGVyIHRoZSBjb2x1bW5cbiAgICAgICAgICAgIC8vIFJlc2l6ZSBzdGF5cyBpbiBzYW1lIGNvbHVtbiwgc28gc291cmNlIGFuZCB0YXJnZXQgYXJlIHRoZSBzYW1lXG4gICAgICAgICAgICBjb25zdCB1cGRhdGVQYXlsb2FkID0ge1xuICAgICAgICAgICAgICAgIGV2ZW50SWQ6IHVwZGF0ZWRFdmVudC5pZCxcbiAgICAgICAgICAgICAgICBzb3VyY2VDb2x1bW5LZXk6IHN3cEV2ZW50LmNvbHVtbktleSxcbiAgICAgICAgICAgICAgICB0YXJnZXRDb2x1bW5LZXk6IHN3cEV2ZW50LmNvbHVtbktleVxuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIHRoaXMuZXZlbnRCdXMuZW1pdChDb3JlRXZlbnRzLkVWRU5UX1VQREFURUQsIHVwZGF0ZVBheWxvYWQpO1xuICAgICAgICB9O1xuICAgICAgICB0aGlzLnNldHVwTGlzdGVuZXJzKCk7XG4gICAgfVxuICAgIHNldHVwTGlzdGVuZXJzKCkge1xuICAgICAgICB0aGlzLmV2ZW50QnVzLm9uKENvcmVFdmVudHMuRVZFTlRfRFJBR19FTkQsIHRoaXMuaGFuZGxlRHJhZ0VuZCk7XG4gICAgICAgIHRoaXMuZXZlbnRCdXMub24oQ29yZUV2ZW50cy5FVkVOVF9SRVNJWkVfRU5ELCB0aGlzLmhhbmRsZVJlc2l6ZUVuZCk7XG4gICAgfVxufVxuIiwgImltcG9ydCB7IENvbnRhaW5lciB9IGZyb20gJ0Bub3ZhZGkvY29yZSc7XG5pbXBvcnQgeyBEYXRlUmVuZGVyZXIgfSBmcm9tICcuL2ZlYXR1cmVzL2RhdGUvRGF0ZVJlbmRlcmVyJztcbmltcG9ydCB7IERhdGVTZXJ2aWNlIH0gZnJvbSAnLi9jb3JlL0RhdGVTZXJ2aWNlJztcbmltcG9ydCB7IFJlc291cmNlUmVuZGVyZXIgfSBmcm9tICcuL2ZlYXR1cmVzL3Jlc291cmNlL1Jlc291cmNlUmVuZGVyZXInO1xuaW1wb3J0IHsgVGVhbVJlbmRlcmVyIH0gZnJvbSAnLi9mZWF0dXJlcy90ZWFtL1RlYW1SZW5kZXJlcic7XG5pbXBvcnQgeyBEZXBhcnRtZW50UmVuZGVyZXIgfSBmcm9tICcuL2ZlYXR1cmVzL2RlcGFydG1lbnQvRGVwYXJ0bWVudFJlbmRlcmVyJztcbmltcG9ydCB7IENhbGVuZGFyT3JjaGVzdHJhdG9yIH0gZnJvbSAnLi9jb3JlL0NhbGVuZGFyT3JjaGVzdHJhdG9yJztcbmltcG9ydCB7IENhbGVuZGFyQXBwIH0gZnJvbSAnLi9jb3JlL0NhbGVuZGFyQXBwJztcbmltcG9ydCB7IFRpbWVBeGlzUmVuZGVyZXIgfSBmcm9tICcuL2ZlYXR1cmVzL3RpbWVheGlzL1RpbWVBeGlzUmVuZGVyZXInO1xuaW1wb3J0IHsgU2Nyb2xsTWFuYWdlciB9IGZyb20gJy4vY29yZS9TY3JvbGxNYW5hZ2VyJztcbmltcG9ydCB7IEhlYWRlckRyYXdlck1hbmFnZXIgfSBmcm9tICcuL2NvcmUvSGVhZGVyRHJhd2VyTWFuYWdlcic7XG5pbXBvcnQgeyBNb2NrVGVhbVN0b3JlLCBNb2NrUmVzb3VyY2VTdG9yZSB9IGZyb20gJy4vZGVtby9Nb2NrU3RvcmVzJztcbmltcG9ydCB7IERlbW9BcHAgfSBmcm9tICcuL2RlbW8vRGVtb0FwcCc7XG4vLyBFdmVudCBzeXN0ZW1cbmltcG9ydCB7IEV2ZW50QnVzIH0gZnJvbSAnLi9jb3JlL0V2ZW50QnVzJztcbi8vIFN0b3JhZ2VcbmltcG9ydCB7IEluZGV4ZWREQkNvbnRleHQgfSBmcm9tICcuL3N0b3JhZ2UvSW5kZXhlZERCQ29udGV4dCc7XG5pbXBvcnQgeyBFdmVudFN0b3JlIH0gZnJvbSAnLi9zdG9yYWdlL2V2ZW50cy9FdmVudFN0b3JlJztcbmltcG9ydCB7IEV2ZW50U2VydmljZSB9IGZyb20gJy4vc3RvcmFnZS9ldmVudHMvRXZlbnRTZXJ2aWNlJztcbmltcG9ydCB7IFJlc291cmNlU3RvcmUgfSBmcm9tICcuL3N0b3JhZ2UvcmVzb3VyY2VzL1Jlc291cmNlU3RvcmUnO1xuaW1wb3J0IHsgUmVzb3VyY2VTZXJ2aWNlIH0gZnJvbSAnLi9zdG9yYWdlL3Jlc291cmNlcy9SZXNvdXJjZVNlcnZpY2UnO1xuaW1wb3J0IHsgQm9va2luZ1N0b3JlIH0gZnJvbSAnLi9zdG9yYWdlL2Jvb2tpbmdzL0Jvb2tpbmdTdG9yZSc7XG5pbXBvcnQgeyBCb29raW5nU2VydmljZSB9IGZyb20gJy4vc3RvcmFnZS9ib29raW5ncy9Cb29raW5nU2VydmljZSc7XG5pbXBvcnQgeyBDdXN0b21lclN0b3JlIH0gZnJvbSAnLi9zdG9yYWdlL2N1c3RvbWVycy9DdXN0b21lclN0b3JlJztcbmltcG9ydCB7IEN1c3RvbWVyU2VydmljZSB9IGZyb20gJy4vc3RvcmFnZS9jdXN0b21lcnMvQ3VzdG9tZXJTZXJ2aWNlJztcbmltcG9ydCB7IFRlYW1TdG9yZSB9IGZyb20gJy4vc3RvcmFnZS90ZWFtcy9UZWFtU3RvcmUnO1xuaW1wb3J0IHsgVGVhbVNlcnZpY2UgfSBmcm9tICcuL3N0b3JhZ2UvdGVhbXMvVGVhbVNlcnZpY2UnO1xuaW1wb3J0IHsgRGVwYXJ0bWVudFN0b3JlIH0gZnJvbSAnLi9zdG9yYWdlL2RlcGFydG1lbnRzL0RlcGFydG1lbnRTdG9yZSc7XG5pbXBvcnQgeyBEZXBhcnRtZW50U2VydmljZSB9IGZyb20gJy4vc3RvcmFnZS9kZXBhcnRtZW50cy9EZXBhcnRtZW50U2VydmljZSc7XG5pbXBvcnQgeyBTZXR0aW5nc1N0b3JlIH0gZnJvbSAnLi9zdG9yYWdlL3NldHRpbmdzL1NldHRpbmdzU3RvcmUnO1xuaW1wb3J0IHsgU2V0dGluZ3NTZXJ2aWNlIH0gZnJvbSAnLi9zdG9yYWdlL3NldHRpbmdzL1NldHRpbmdzU2VydmljZSc7XG5pbXBvcnQgeyBWaWV3Q29uZmlnU3RvcmUgfSBmcm9tICcuL3N0b3JhZ2Uvdmlld2NvbmZpZ3MvVmlld0NvbmZpZ1N0b3JlJztcbmltcG9ydCB7IFZpZXdDb25maWdTZXJ2aWNlIH0gZnJvbSAnLi9zdG9yYWdlL3ZpZXdjb25maWdzL1ZpZXdDb25maWdTZXJ2aWNlJztcbi8vIEF1ZGl0XG5pbXBvcnQgeyBBdWRpdFN0b3JlIH0gZnJvbSAnLi9zdG9yYWdlL2F1ZGl0L0F1ZGl0U3RvcmUnO1xuaW1wb3J0IHsgQXVkaXRTZXJ2aWNlIH0gZnJvbSAnLi9zdG9yYWdlL2F1ZGl0L0F1ZGl0U2VydmljZSc7XG5pbXBvcnQgeyBNb2NrRXZlbnRSZXBvc2l0b3J5IH0gZnJvbSAnLi9yZXBvc2l0b3JpZXMvTW9ja0V2ZW50UmVwb3NpdG9yeSc7XG5pbXBvcnQgeyBNb2NrUmVzb3VyY2VSZXBvc2l0b3J5IH0gZnJvbSAnLi9yZXBvc2l0b3JpZXMvTW9ja1Jlc291cmNlUmVwb3NpdG9yeSc7XG5pbXBvcnQgeyBNb2NrQm9va2luZ1JlcG9zaXRvcnkgfSBmcm9tICcuL3JlcG9zaXRvcmllcy9Nb2NrQm9va2luZ1JlcG9zaXRvcnknO1xuaW1wb3J0IHsgTW9ja0N1c3RvbWVyUmVwb3NpdG9yeSB9IGZyb20gJy4vcmVwb3NpdG9yaWVzL01vY2tDdXN0b21lclJlcG9zaXRvcnknO1xuaW1wb3J0IHsgTW9ja0F1ZGl0UmVwb3NpdG9yeSB9IGZyb20gJy4vcmVwb3NpdG9yaWVzL01vY2tBdWRpdFJlcG9zaXRvcnknO1xuaW1wb3J0IHsgTW9ja1RlYW1SZXBvc2l0b3J5IH0gZnJvbSAnLi9yZXBvc2l0b3JpZXMvTW9ja1RlYW1SZXBvc2l0b3J5JztcbmltcG9ydCB7IE1vY2tEZXBhcnRtZW50UmVwb3NpdG9yeSB9IGZyb20gJy4vcmVwb3NpdG9yaWVzL01vY2tEZXBhcnRtZW50UmVwb3NpdG9yeSc7XG5pbXBvcnQgeyBNb2NrU2V0dGluZ3NSZXBvc2l0b3J5IH0gZnJvbSAnLi9yZXBvc2l0b3JpZXMvTW9ja1NldHRpbmdzUmVwb3NpdG9yeSc7XG5pbXBvcnQgeyBNb2NrVmlld0NvbmZpZ1JlcG9zaXRvcnkgfSBmcm9tICcuL3JlcG9zaXRvcmllcy9Nb2NrVmlld0NvbmZpZ1JlcG9zaXRvcnknO1xuLy8gV29ya2Vyc1xuaW1wb3J0IHsgRGF0YVNlZWRlciB9IGZyb20gJy4vd29ya2Vycy9EYXRhU2VlZGVyJztcbi8vIEZlYXR1cmVzXG5pbXBvcnQgeyBFdmVudFJlbmRlcmVyIH0gZnJvbSAnLi9mZWF0dXJlcy9ldmVudC9FdmVudFJlbmRlcmVyJztcbmltcG9ydCB7IFNjaGVkdWxlUmVuZGVyZXIgfSBmcm9tICcuL2ZlYXR1cmVzL3NjaGVkdWxlL1NjaGVkdWxlUmVuZGVyZXInO1xuaW1wb3J0IHsgSGVhZGVyRHJhd2VyUmVuZGVyZXIgfSBmcm9tICcuL2ZlYXR1cmVzL2hlYWRlcmRyYXdlci9IZWFkZXJEcmF3ZXJSZW5kZXJlcic7XG4vLyBTY2hlZHVsZVxuaW1wb3J0IHsgU2NoZWR1bGVPdmVycmlkZVN0b3JlIH0gZnJvbSAnLi9zdG9yYWdlL3NjaGVkdWxlcy9TY2hlZHVsZU92ZXJyaWRlU3RvcmUnO1xuaW1wb3J0IHsgU2NoZWR1bGVPdmVycmlkZVNlcnZpY2UgfSBmcm9tICcuL3N0b3JhZ2Uvc2NoZWR1bGVzL1NjaGVkdWxlT3ZlcnJpZGVTZXJ2aWNlJztcbmltcG9ydCB7IFJlc291cmNlU2NoZWR1bGVTZXJ2aWNlIH0gZnJvbSAnLi9zdG9yYWdlL3NjaGVkdWxlcy9SZXNvdXJjZVNjaGVkdWxlU2VydmljZSc7XG4vLyBNYW5hZ2Vyc1xuaW1wb3J0IHsgRHJhZ0Ryb3BNYW5hZ2VyIH0gZnJvbSAnLi9tYW5hZ2Vycy9EcmFnRHJvcE1hbmFnZXInO1xuaW1wb3J0IHsgRWRnZVNjcm9sbE1hbmFnZXIgfSBmcm9tICcuL21hbmFnZXJzL0VkZ2VTY3JvbGxNYW5hZ2VyJztcbmltcG9ydCB7IFJlc2l6ZU1hbmFnZXIgfSBmcm9tICcuL21hbmFnZXJzL1Jlc2l6ZU1hbmFnZXInO1xuaW1wb3J0IHsgRXZlbnRQZXJzaXN0ZW5jZU1hbmFnZXIgfSBmcm9tICcuL21hbmFnZXJzL0V2ZW50UGVyc2lzdGVuY2VNYW5hZ2VyJztcbmNvbnN0IGRlZmF1bHRUaW1lRm9ybWF0Q29uZmlnID0ge1xuICAgIHRpbWV6b25lOiBJbnRsLkRhdGVUaW1lRm9ybWF0KCkucmVzb2x2ZWRPcHRpb25zKCkudGltZVpvbmUsXG4gICAgdXNlMjRIb3VyRm9ybWF0OiB0cnVlLFxuICAgIGxvY2FsZTogJ2RhLURLJyxcbiAgICBkYXRlRm9ybWF0OiAnbG9jYWxlJyxcbiAgICBzaG93U2Vjb25kczogZmFsc2Vcbn07XG5jb25zdCBkZWZhdWx0R3JpZENvbmZpZyA9IHtcbiAgICBob3VySGVpZ2h0OiA2NCxcbiAgICBkYXlTdGFydEhvdXI6IDYsXG4gICAgZGF5RW5kSG91cjogMTgsXG4gICAgc25hcEludGVydmFsOiAxNSxcbiAgICBncmlkU3RhcnRUaHJlc2hvbGRNaW51dGVzOiAzMFxufTtcbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVWMkNvbnRhaW5lcigpIHtcbiAgICBjb25zdCBjb250YWluZXIgPSBuZXcgQ29udGFpbmVyKCk7XG4gICAgY29uc3QgYnVpbGRlciA9IGNvbnRhaW5lci5idWlsZGVyKCk7XG4gICAgLy8gQ29uZmlnXG4gICAgYnVpbGRlci5yZWdpc3Rlckluc3RhbmNlKGRlZmF1bHRUaW1lRm9ybWF0Q29uZmlnKS5hcyhcIklUaW1lRm9ybWF0Q29uZmlnXCIpO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJJbnN0YW5jZShkZWZhdWx0R3JpZENvbmZpZykuYXMoXCJJR3JpZENvbmZpZ1wiKTtcbiAgICAvLyBDb3JlIC0gRXZlbnRCdXNcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShFdmVudEJ1cykuYXMoXCJFdmVudEJ1c1wiKTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShFdmVudEJ1cykuYXMoXCJJRXZlbnRCdXNcIik7XG4gICAgLy8gU2VydmljZXNcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShEYXRlU2VydmljZSkuYXMoXCJEYXRlU2VydmljZVwiKS5hdXRvV2lyZSh7XG4gICAgICAgIG1hcFJlc29sdmVyczogW1xuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSVRpbWVGb3JtYXRDb25maWdcIiksXG4gICAgICAgICAgICB1bmRlZmluZWRcbiAgICAgICAgXVxuICAgIH0pO1xuICAgIC8vIFN0b3JhZ2UgaW5mcmFzdHJ1Y3R1cmVcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShJbmRleGVkREJDb250ZXh0KS5hcyhcIkluZGV4ZWREQkNvbnRleHRcIikuYXV0b1dpcmUoe1xuICAgICAgICBtYXBSZXNvbHZlcnM6IFtcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZUFsbChcIklTdG9yZVwiKVxuICAgICAgICBdXG4gICAgfSk7XG4gICAgLy8gU3RvcmVzIChmb3IgSW5kZXhlZERCIHNjaGVtYSBjcmVhdGlvbilcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShFdmVudFN0b3JlKS5hcyhcIklTdG9yZVwiKTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShSZXNvdXJjZVN0b3JlKS5hcyhcIklTdG9yZVwiKTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShCb29raW5nU3RvcmUpLmFzKFwiSVN0b3JlXCIpO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKEN1c3RvbWVyU3RvcmUpLmFzKFwiSVN0b3JlXCIpO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKFRlYW1TdG9yZSkuYXMoXCJJU3RvcmVcIik7XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoRGVwYXJ0bWVudFN0b3JlKS5hcyhcIklTdG9yZVwiKTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShTY2hlZHVsZU92ZXJyaWRlU3RvcmUpLmFzKFwiSVN0b3JlXCIpO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKEF1ZGl0U3RvcmUpLmFzKFwiSVN0b3JlXCIpO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKFNldHRpbmdzU3RvcmUpLmFzKFwiSVN0b3JlXCIpO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKFZpZXdDb25maWdTdG9yZSkuYXMoXCJJU3RvcmVcIik7XG4gICAgLy8gRW50aXR5IHNlcnZpY2VzIChmb3IgRGF0YVNlZWRlciBwb2x5bW9ycGhpYyBhcnJheSlcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShFdmVudFNlcnZpY2UpLmFzKFwiSUVudGl0eVNlcnZpY2VcIikuYXV0b1dpcmUoe1xuICAgICAgICBtYXBSZXNvbHZlcnM6IFtcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkluZGV4ZWREQkNvbnRleHRcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJRXZlbnRCdXNcIilcbiAgICAgICAgXVxuICAgIH0pO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKEV2ZW50U2VydmljZSkuYXMoXCJJRW50aXR5U2VydmljZVwiKS5hdXRvV2lyZSh7XG4gICAgICAgIG1hcFJlc29sdmVyczogW1xuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSW5kZXhlZERCQ29udGV4dFwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIklFdmVudEJ1c1wiKVxuICAgICAgICBdXG4gICAgfSk7XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoRXZlbnRTZXJ2aWNlKS5hcyhcIkV2ZW50U2VydmljZVwiKS5hdXRvV2lyZSh7XG4gICAgICAgIG1hcFJlc29sdmVyczogW1xuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSW5kZXhlZERCQ29udGV4dFwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIklFdmVudEJ1c1wiKVxuICAgICAgICBdXG4gICAgfSk7XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoUmVzb3VyY2VTZXJ2aWNlKS5hcyhcIklFbnRpdHlTZXJ2aWNlXCIpLmF1dG9XaXJlKHtcbiAgICAgICAgbWFwUmVzb2x2ZXJzOiBbXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJbmRleGVkREJDb250ZXh0XCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSUV2ZW50QnVzXCIpXG4gICAgICAgIF1cbiAgICB9KTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShSZXNvdXJjZVNlcnZpY2UpLmFzKFwiSUVudGl0eVNlcnZpY2VcIikuYXV0b1dpcmUoe1xuICAgICAgICBtYXBSZXNvbHZlcnM6IFtcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkluZGV4ZWREQkNvbnRleHRcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJRXZlbnRCdXNcIilcbiAgICAgICAgXVxuICAgIH0pO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKFJlc291cmNlU2VydmljZSkuYXMoXCJSZXNvdXJjZVNlcnZpY2VcIikuYXV0b1dpcmUoe1xuICAgICAgICBtYXBSZXNvbHZlcnM6IFtcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkluZGV4ZWREQkNvbnRleHRcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJRXZlbnRCdXNcIilcbiAgICAgICAgXVxuICAgIH0pO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKEJvb2tpbmdTZXJ2aWNlKS5hcyhcIklFbnRpdHlTZXJ2aWNlXCIpLmF1dG9XaXJlKHtcbiAgICAgICAgbWFwUmVzb2x2ZXJzOiBbXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJbmRleGVkREJDb250ZXh0XCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSUV2ZW50QnVzXCIpXG4gICAgICAgIF1cbiAgICB9KTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShCb29raW5nU2VydmljZSkuYXMoXCJJRW50aXR5U2VydmljZVwiKS5hdXRvV2lyZSh7XG4gICAgICAgIG1hcFJlc29sdmVyczogW1xuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSW5kZXhlZERCQ29udGV4dFwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIklFdmVudEJ1c1wiKVxuICAgICAgICBdXG4gICAgfSk7XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoQm9va2luZ1NlcnZpY2UpLmFzKFwiQm9va2luZ1NlcnZpY2VcIikuYXV0b1dpcmUoe1xuICAgICAgICBtYXBSZXNvbHZlcnM6IFtcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkluZGV4ZWREQkNvbnRleHRcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJRXZlbnRCdXNcIilcbiAgICAgICAgXVxuICAgIH0pO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKEN1c3RvbWVyU2VydmljZSkuYXMoXCJJRW50aXR5U2VydmljZVwiKS5hdXRvV2lyZSh7XG4gICAgICAgIG1hcFJlc29sdmVyczogW1xuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSW5kZXhlZERCQ29udGV4dFwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIklFdmVudEJ1c1wiKVxuICAgICAgICBdXG4gICAgfSk7XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoQ3VzdG9tZXJTZXJ2aWNlKS5hcyhcIklFbnRpdHlTZXJ2aWNlXCIpLmF1dG9XaXJlKHtcbiAgICAgICAgbWFwUmVzb2x2ZXJzOiBbXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJbmRleGVkREJDb250ZXh0XCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSUV2ZW50QnVzXCIpXG4gICAgICAgIF1cbiAgICB9KTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShDdXN0b21lclNlcnZpY2UpLmFzKFwiQ3VzdG9tZXJTZXJ2aWNlXCIpLmF1dG9XaXJlKHtcbiAgICAgICAgbWFwUmVzb2x2ZXJzOiBbXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJbmRleGVkREJDb250ZXh0XCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSUV2ZW50QnVzXCIpXG4gICAgICAgIF1cbiAgICB9KTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShUZWFtU2VydmljZSkuYXMoXCJJRW50aXR5U2VydmljZVwiKS5hdXRvV2lyZSh7XG4gICAgICAgIG1hcFJlc29sdmVyczogW1xuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSW5kZXhlZERCQ29udGV4dFwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIklFdmVudEJ1c1wiKVxuICAgICAgICBdXG4gICAgfSk7XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoVGVhbVNlcnZpY2UpLmFzKFwiSUVudGl0eVNlcnZpY2VcIikuYXV0b1dpcmUoe1xuICAgICAgICBtYXBSZXNvbHZlcnM6IFtcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkluZGV4ZWREQkNvbnRleHRcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJRXZlbnRCdXNcIilcbiAgICAgICAgXVxuICAgIH0pO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKFRlYW1TZXJ2aWNlKS5hcyhcIlRlYW1TZXJ2aWNlXCIpLmF1dG9XaXJlKHtcbiAgICAgICAgbWFwUmVzb2x2ZXJzOiBbXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJbmRleGVkREJDb250ZXh0XCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSUV2ZW50QnVzXCIpXG4gICAgICAgIF1cbiAgICB9KTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShEZXBhcnRtZW50U2VydmljZSkuYXMoXCJJRW50aXR5U2VydmljZVwiKS5hdXRvV2lyZSh7XG4gICAgICAgIG1hcFJlc29sdmVyczogW1xuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSW5kZXhlZERCQ29udGV4dFwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIklFdmVudEJ1c1wiKVxuICAgICAgICBdXG4gICAgfSk7XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoRGVwYXJ0bWVudFNlcnZpY2UpLmFzKFwiSUVudGl0eVNlcnZpY2VcIikuYXV0b1dpcmUoe1xuICAgICAgICBtYXBSZXNvbHZlcnM6IFtcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkluZGV4ZWREQkNvbnRleHRcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJRXZlbnRCdXNcIilcbiAgICAgICAgXVxuICAgIH0pO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKERlcGFydG1lbnRTZXJ2aWNlKS5hcyhcIkRlcGFydG1lbnRTZXJ2aWNlXCIpLmF1dG9XaXJlKHtcbiAgICAgICAgbWFwUmVzb2x2ZXJzOiBbXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJbmRleGVkREJDb250ZXh0XCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSUV2ZW50QnVzXCIpXG4gICAgICAgIF1cbiAgICB9KTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShTZXR0aW5nc1NlcnZpY2UpLmFzKFwiSUVudGl0eVNlcnZpY2VcIikuYXV0b1dpcmUoe1xuICAgICAgICBtYXBSZXNvbHZlcnM6IFtcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkluZGV4ZWREQkNvbnRleHRcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJRXZlbnRCdXNcIilcbiAgICAgICAgXVxuICAgIH0pO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKFNldHRpbmdzU2VydmljZSkuYXMoXCJJRW50aXR5U2VydmljZVwiKS5hdXRvV2lyZSh7XG4gICAgICAgIG1hcFJlc29sdmVyczogW1xuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSW5kZXhlZERCQ29udGV4dFwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIklFdmVudEJ1c1wiKVxuICAgICAgICBdXG4gICAgfSk7XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoU2V0dGluZ3NTZXJ2aWNlKS5hcyhcIlNldHRpbmdzU2VydmljZVwiKS5hdXRvV2lyZSh7XG4gICAgICAgIG1hcFJlc29sdmVyczogW1xuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSW5kZXhlZERCQ29udGV4dFwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIklFdmVudEJ1c1wiKVxuICAgICAgICBdXG4gICAgfSk7XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoVmlld0NvbmZpZ1NlcnZpY2UpLmFzKFwiSUVudGl0eVNlcnZpY2VcIikuYXV0b1dpcmUoe1xuICAgICAgICBtYXBSZXNvbHZlcnM6IFtcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkluZGV4ZWREQkNvbnRleHRcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJRXZlbnRCdXNcIilcbiAgICAgICAgXVxuICAgIH0pO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKFZpZXdDb25maWdTZXJ2aWNlKS5hcyhcIklFbnRpdHlTZXJ2aWNlXCIpLmF1dG9XaXJlKHtcbiAgICAgICAgbWFwUmVzb2x2ZXJzOiBbXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJbmRleGVkREJDb250ZXh0XCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSUV2ZW50QnVzXCIpXG4gICAgICAgIF1cbiAgICB9KTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShWaWV3Q29uZmlnU2VydmljZSkuYXMoXCJWaWV3Q29uZmlnU2VydmljZVwiKS5hdXRvV2lyZSh7XG4gICAgICAgIG1hcFJlc29sdmVyczogW1xuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSW5kZXhlZERCQ29udGV4dFwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIklFdmVudEJ1c1wiKVxuICAgICAgICBdXG4gICAgfSk7XG4gICAgLy8gUmVwb3NpdG9yaWVzIChmb3IgRGF0YVNlZWRlciBwb2x5bW9ycGhpYyBhcnJheSlcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShNb2NrRXZlbnRSZXBvc2l0b3J5KS5hcyhcIklBcGlSZXBvc2l0b3J5XCIpO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKE1vY2tFdmVudFJlcG9zaXRvcnkpLmFzKFwiSUFwaVJlcG9zaXRvcnlcIik7XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoTW9ja1Jlc291cmNlUmVwb3NpdG9yeSkuYXMoXCJJQXBpUmVwb3NpdG9yeVwiKTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShNb2NrUmVzb3VyY2VSZXBvc2l0b3J5KS5hcyhcIklBcGlSZXBvc2l0b3J5XCIpO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKE1vY2tCb29raW5nUmVwb3NpdG9yeSkuYXMoXCJJQXBpUmVwb3NpdG9yeVwiKTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShNb2NrQm9va2luZ1JlcG9zaXRvcnkpLmFzKFwiSUFwaVJlcG9zaXRvcnlcIik7XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoTW9ja0N1c3RvbWVyUmVwb3NpdG9yeSkuYXMoXCJJQXBpUmVwb3NpdG9yeVwiKTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShNb2NrQ3VzdG9tZXJSZXBvc2l0b3J5KS5hcyhcIklBcGlSZXBvc2l0b3J5XCIpO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKE1vY2tBdWRpdFJlcG9zaXRvcnkpLmFzKFwiSUFwaVJlcG9zaXRvcnlcIik7XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoTW9ja0F1ZGl0UmVwb3NpdG9yeSkuYXMoXCJJQXBpUmVwb3NpdG9yeVwiKTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShNb2NrVGVhbVJlcG9zaXRvcnkpLmFzKFwiSUFwaVJlcG9zaXRvcnlcIik7XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoTW9ja1RlYW1SZXBvc2l0b3J5KS5hcyhcIklBcGlSZXBvc2l0b3J5XCIpO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKE1vY2tEZXBhcnRtZW50UmVwb3NpdG9yeSkuYXMoXCJJQXBpUmVwb3NpdG9yeVwiKTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShNb2NrRGVwYXJ0bWVudFJlcG9zaXRvcnkpLmFzKFwiSUFwaVJlcG9zaXRvcnlcIik7XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoTW9ja1NldHRpbmdzUmVwb3NpdG9yeSkuYXMoXCJJQXBpUmVwb3NpdG9yeVwiKTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShNb2NrU2V0dGluZ3NSZXBvc2l0b3J5KS5hcyhcIklBcGlSZXBvc2l0b3J5XCIpO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKE1vY2tWaWV3Q29uZmlnUmVwb3NpdG9yeSkuYXMoXCJJQXBpUmVwb3NpdG9yeVwiKTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShNb2NrVmlld0NvbmZpZ1JlcG9zaXRvcnkpLmFzKFwiSUFwaVJlcG9zaXRvcnlcIik7XG4gICAgLy8gQXVkaXQgc2VydmljZSAobGlzdGVucyB0byBFTlRJVFlfU0FWRUQvREVMRVRFRCBldmVudHMgYXV0b21hdGljYWxseSlcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShBdWRpdFNlcnZpY2UpLmFzKFwiQXVkaXRTZXJ2aWNlXCIpLmF1dG9XaXJlKHtcbiAgICAgICAgbWFwUmVzb2x2ZXJzOiBbXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJbmRleGVkREJDb250ZXh0XCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSUV2ZW50QnVzXCIpXG4gICAgICAgIF1cbiAgICB9KTtcbiAgICAvLyBXb3JrZXJzXG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoRGF0YVNlZWRlcikuYXMoXCJEYXRhU2VlZGVyXCIpLmF1dG9XaXJlKHtcbiAgICAgICAgbWFwUmVzb2x2ZXJzOiBbXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGVBbGwoXCJJRW50aXR5U2VydmljZVwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZUFsbChcIklBcGlSZXBvc2l0b3J5XCIpXG4gICAgICAgIF1cbiAgICB9KTtcbiAgICAvLyBTY2hlZHVsZSBzZXJ2aWNlc1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKFNjaGVkdWxlT3ZlcnJpZGVTZXJ2aWNlKS5hcyhcIlNjaGVkdWxlT3ZlcnJpZGVTZXJ2aWNlXCIpLmF1dG9XaXJlKHtcbiAgICAgICAgbWFwUmVzb2x2ZXJzOiBbXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJbmRleGVkREJDb250ZXh0XCIpXG4gICAgICAgIF1cbiAgICB9KTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShSZXNvdXJjZVNjaGVkdWxlU2VydmljZSkuYXMoXCJSZXNvdXJjZVNjaGVkdWxlU2VydmljZVwiKS5hdXRvV2lyZSh7XG4gICAgICAgIG1hcFJlc29sdmVyczogW1xuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiUmVzb3VyY2VTZXJ2aWNlXCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiU2NoZWR1bGVPdmVycmlkZVNlcnZpY2VcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJEYXRlU2VydmljZVwiKVxuICAgICAgICBdXG4gICAgfSk7XG4gICAgLy8gRmVhdHVyZXNcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShFdmVudFJlbmRlcmVyKS5hcyhcIkV2ZW50UmVuZGVyZXJcIikuYXV0b1dpcmUoe1xuICAgICAgICBtYXBSZXNvbHZlcnM6IFtcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkV2ZW50U2VydmljZVwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkRhdGVTZXJ2aWNlXCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSUdyaWRDb25maWdcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJRXZlbnRCdXNcIilcbiAgICAgICAgXVxuICAgIH0pO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKFNjaGVkdWxlUmVuZGVyZXIpLmFzKFwiU2NoZWR1bGVSZW5kZXJlclwiKS5hdXRvV2lyZSh7XG4gICAgICAgIG1hcFJlc29sdmVyczogW1xuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiUmVzb3VyY2VTY2hlZHVsZVNlcnZpY2VcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJEYXRlU2VydmljZVwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIklHcmlkQ29uZmlnXCIpXG4gICAgICAgIF1cbiAgICB9KTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShIZWFkZXJEcmF3ZXJSZW5kZXJlcikuYXMoXCJIZWFkZXJEcmF3ZXJSZW5kZXJlclwiKS5hdXRvV2lyZSh7XG4gICAgICAgIG1hcFJlc29sdmVyczogW1xuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSUV2ZW50QnVzXCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSUdyaWRDb25maWdcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJIZWFkZXJEcmF3ZXJNYW5hZ2VyXCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiRXZlbnRTZXJ2aWNlXCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiRGF0ZVNlcnZpY2VcIilcbiAgICAgICAgXVxuICAgIH0pO1xuICAgIC8vIFJlbmRlcmVycyAtIHJlZ2lzdHJlcmVzIHNvbSBSZW5kZXJlciAoYXJyYXkgaW5qZWN0aW9uIHRpbCBDYWxlbmRhck9yY2hlc3RyYXRvcilcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShEYXRlUmVuZGVyZXIpLmFzKFwiSVJlbmRlcmVyXCIpLmF1dG9XaXJlKHtcbiAgICAgICAgbWFwUmVzb2x2ZXJzOiBbXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJEYXRlU2VydmljZVwiKVxuICAgICAgICBdXG4gICAgfSk7XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoUmVzb3VyY2VSZW5kZXJlcikuYXMoXCJJUmVuZGVyZXJcIikuYXV0b1dpcmUoe1xuICAgICAgICBtYXBSZXNvbHZlcnM6IFtcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIlJlc291cmNlU2VydmljZVwiKVxuICAgICAgICBdXG4gICAgfSk7XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoVGVhbVJlbmRlcmVyKS5hcyhcIklSZW5kZXJlclwiKS5hdXRvV2lyZSh7XG4gICAgICAgIG1hcFJlc29sdmVyczogW1xuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiVGVhbVNlcnZpY2VcIilcbiAgICAgICAgXVxuICAgIH0pO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKERlcGFydG1lbnRSZW5kZXJlcikuYXMoXCJJUmVuZGVyZXJcIikuYXV0b1dpcmUoe1xuICAgICAgICBtYXBSZXNvbHZlcnM6IFtcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkRlcGFydG1lbnRTZXJ2aWNlXCIpXG4gICAgICAgIF1cbiAgICB9KTtcbiAgICAvLyBTdG9yZXMgLSByZWdpc3RyZXJlcyBzb20gSUdyb3VwaW5nU3RvcmVcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShNb2NrVGVhbVN0b3JlKS5hcyhcIklHcm91cGluZ1N0b3JlXCIpO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKE1vY2tSZXNvdXJjZVN0b3JlKS5hcyhcIklHcm91cGluZ1N0b3JlXCIpO1xuICAgIC8vIENhbGVuZGFyT3JjaGVzdHJhdG9yIG1vZHRhZ2VyIElHcm91cGluZ1N0b3JlW10gYXV0b21hdGlzayAoYXJyYXkgaW5qZWN0aW9uKVxuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKENhbGVuZGFyT3JjaGVzdHJhdG9yKS5hcyhcIkNhbGVuZGFyT3JjaGVzdHJhdG9yXCIpLmF1dG9XaXJlKHtcbiAgICAgICAgbWFwUmVzb2x2ZXJzOiBbXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGVBbGwoXCJJUmVuZGVyZXJcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJFdmVudFJlbmRlcmVyXCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiU2NoZWR1bGVSZW5kZXJlclwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkhlYWRlckRyYXdlclJlbmRlcmVyXCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiRGF0ZVNlcnZpY2VcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGVBbGwoXCJJRW50aXR5U2VydmljZVwiKVxuICAgICAgICBdXG4gICAgfSk7XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoVGltZUF4aXNSZW5kZXJlcikuYXMoXCJUaW1lQXhpc1JlbmRlcmVyXCIpO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKFNjcm9sbE1hbmFnZXIpLmFzKFwiU2Nyb2xsTWFuYWdlclwiKTtcbiAgICBidWlsZGVyLnJlZ2lzdGVyVHlwZShIZWFkZXJEcmF3ZXJNYW5hZ2VyKS5hcyhcIkhlYWRlckRyYXdlck1hbmFnZXJcIik7XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoRHJhZ0Ryb3BNYW5hZ2VyKS5hcyhcIkRyYWdEcm9wTWFuYWdlclwiKS5hdXRvV2lyZSh7XG4gICAgICAgIG1hcFJlc29sdmVyczogW1xuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSUV2ZW50QnVzXCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSUdyaWRDb25maWdcIilcbiAgICAgICAgXVxuICAgIH0pO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKEVkZ2VTY3JvbGxNYW5hZ2VyKS5hcyhcIkVkZ2VTY3JvbGxNYW5hZ2VyXCIpLmF1dG9XaXJlKHtcbiAgICAgICAgbWFwUmVzb2x2ZXJzOiBbXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJRXZlbnRCdXNcIilcbiAgICAgICAgXVxuICAgIH0pO1xuICAgIGJ1aWxkZXIucmVnaXN0ZXJUeXBlKFJlc2l6ZU1hbmFnZXIpLmFzKFwiUmVzaXplTWFuYWdlclwiKS5hdXRvV2lyZSh7XG4gICAgICAgIG1hcFJlc29sdmVyczogW1xuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSUV2ZW50QnVzXCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiSUdyaWRDb25maWdcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJEYXRlU2VydmljZVwiKVxuICAgICAgICBdXG4gICAgfSk7XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoRXZlbnRQZXJzaXN0ZW5jZU1hbmFnZXIpLmFzKFwiRXZlbnRQZXJzaXN0ZW5jZU1hbmFnZXJcIikuYXV0b1dpcmUoe1xuICAgICAgICBtYXBSZXNvbHZlcnM6IFtcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkV2ZW50U2VydmljZVwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIklFdmVudEJ1c1wiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkRhdGVTZXJ2aWNlXCIpXG4gICAgICAgIF1cbiAgICB9KTtcbiAgICAvLyBDYWxlbmRhckFwcCAtIGdlbmJydWdlbGlnIGthbGVuZGVya29tcG9uZW50XG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoQ2FsZW5kYXJBcHApLmFzKFwiQ2FsZW5kYXJBcHBcIikuYXV0b1dpcmUoe1xuICAgICAgICBtYXBSZXNvbHZlcnM6IFtcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkNhbGVuZGFyT3JjaGVzdHJhdG9yXCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiVGltZUF4aXNSZW5kZXJlclwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkRhdGVTZXJ2aWNlXCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiU2Nyb2xsTWFuYWdlclwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkhlYWRlckRyYXdlck1hbmFnZXJcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJEcmFnRHJvcE1hbmFnZXJcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJFZGdlU2Nyb2xsTWFuYWdlclwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIlJlc2l6ZU1hbmFnZXJcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJIZWFkZXJEcmF3ZXJSZW5kZXJlclwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkV2ZW50UGVyc2lzdGVuY2VNYW5hZ2VyXCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiU2V0dGluZ3NTZXJ2aWNlXCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiVmlld0NvbmZpZ1NlcnZpY2VcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJRXZlbnRCdXNcIilcbiAgICAgICAgXVxuICAgIH0pO1xuICAgIC8vIERlbW8gYXBwXG4gICAgYnVpbGRlci5yZWdpc3RlclR5cGUoRGVtb0FwcCkuYXMoXCJEZW1vQXBwXCIpLmF1dG9XaXJlKHtcbiAgICAgICAgbWFwUmVzb2x2ZXJzOiBbXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJbmRleGVkREJDb250ZXh0XCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiRGF0YVNlZWRlclwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkF1ZGl0U2VydmljZVwiKSxcbiAgICAgICAgICAgIGMgPT4gYy5yZXNvbHZlVHlwZShcIkNhbGVuZGFyQXBwXCIpLFxuICAgICAgICAgICAgYyA9PiBjLnJlc29sdmVUeXBlKFwiRGF0ZVNlcnZpY2VcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJSZXNvdXJjZVNlcnZpY2VcIiksXG4gICAgICAgICAgICBjID0+IGMucmVzb2x2ZVR5cGUoXCJJRXZlbnRCdXNcIilcbiAgICAgICAgXVxuICAgIH0pO1xuICAgIHJldHVybiBidWlsZGVyLmJ1aWxkKCk7XG59XG4iLCAiaW1wb3J0IHsgY3JlYXRlVjJDb250YWluZXIgfSBmcm9tICcuLi9WMkNvbXBvc2l0aW9uUm9vdCc7XG5jb25zdCBjb250YWluZXIgPSBjcmVhdGVWMkNvbnRhaW5lcigpO1xuY29udGFpbmVyLnJlc29sdmVUeXBlKFwiRGVtb0FwcFwiKS5pbml0KCkuY2F0Y2goY29uc29sZS5lcnJvcik7XG4iXSwKICAibWFwcGluZ3MiOiAiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUE7QUFBQTtBQUFBLEtBQUMsU0FBUyxHQUFFLEdBQUU7QUFBQyxrQkFBVSxPQUFPLFdBQVMsZUFBYSxPQUFPLFNBQU8sT0FBTyxVQUFRLEVBQUUsSUFBRSxjQUFZLE9BQU8sVUFBUSxPQUFPLE1BQUksT0FBTyxDQUFDLEtBQUcsSUFBRSxlQUFhLE9BQU8sYUFBVyxhQUFXLEtBQUcsTUFBTSxRQUFNLEVBQUU7QUFBQSxJQUFDLEVBQUUsU0FBTSxXQUFVO0FBQUM7QUFBYSxVQUFJLElBQUUsS0FBSSxJQUFFLEtBQUksSUFB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V0FBVztBQUFBLElBQ2xDO0FBQUEsRUFDSixDQUFDO0FBRUQsVUFBUSxhQUFhLE9BQU8sRUFBRSxHQUFHLFNBQVMsRUFBRSxTQUFTO0FBQUEsSUFDakQsY0FBYztBQUFBLE1BQ1YsT0FBSyxFQUFFLFlBQVksa0JBQWtCO0FBQUEsTUFDckMsT0FBSyxFQUFFLFlBQVksWUFBWTtBQUFBLE1BQy9CLE9BQUssRUFBRSxZQUFZLGNBQWM7QUFBQSxNQUNqQyxPQUFLLEVBQUUsWUFBWSxhQUFhO0FBQUEsTUFDaEMsT0FBSyxFQUFFLFlBQVksYUFBYTtBQUFBLE1BQ2hDLE9BQUssRUFBRSxZQUFZLGlCQUFpQjtBQUFBLE1BQ3BDLE9BQUssRUFBRSxZQUFZLFdBQVc7QUFBQSxJQUNsQztBQUFBLEVBQ0osQ0FBQztBQUNELFNBQU8sUUFBUSxNQUFNO0FBQ3pCO0FBdlZnQjs7O0FDekVoQixJQUFNLFlBQVksa0JBQWtCO0FBQ3BDLFVBQVUsWUFBWSxTQUFTLEVBQUUsS0FBSyxFQUFFLE1BQU0sUUFBUSxLQUFLOyIsCiAgIm5hbWVzIjogWyJ0IiwgImUiLCAibiIsICJyIiwgImkiLCAicyIsICJ1IiwgImEiLCAiTSIsICJtIiwgImYiLCAibCIsICIkIiwgInkiLCAidiIsICJnIiwgIkQiLCAibyIsICJkIiwgImMiLCAiaCIsICJ0IiwgImkiLCAiZSIsICJzIiwgImYiLCAibiIsICJ1IiwgInIiLCAibyIsICJ0IiwgIm4iLCAiaSIsICJvIiwgInIiLCAiZSIsICJ1IiwgImYiLCAicyIsICJhIiwgInQiLCAiaSIsICJkIiwgIm4iLCAiZSIsICJzIiwgInRva2VuIiwgImNvbnRhaW5lciIsICJjb250YWluZXIiLCAidG9rZW4iLCAiY29udGFpbmVyIiwgImRheWpzIiwgInV0YyIsICJ0aW1lem9uZSIsICJpc29XZWVrIiwgImNvbnRhaW5lciIsICJjb250YWluZXIiLCAiY29udGFpbmVyIiwgImNvbnRhaW5lciIsICJjb250YWluZXIiLCAiY29udGFpbmVyIiwgImdldEtleSIsICJza2lwUGF0aCIsICJrZXkiLCAiY29udGFpbmVyIiwgImNvbnRhaW5lciIsICJjb250YWluZXIiLCAiZGlmZiIsICJjb250YWluZXIiLCAiY29udGFpbmVyIiwgImRpZmYiLCAiY29udGFpbmVyIl0KfQo= diff --git a/wwwroot/js/workers/SyncManager.d.ts b/wwwroot/js/workers/SyncManager.d.ts new file mode 100644 index 0000000..dfc7f40 --- /dev/null +++ b/wwwroot/js/workers/SyncManager.d.ts @@ -0,0 +1,78 @@ +import { IEventBus } from '../types/CalendarTypes'; +import { OperationQueue } from '../storage/OperationQueue'; +import { IndexedDBService } from '../storage/IndexedDBService'; +import { ApiEventRepository } from '../repositories/ApiEventRepository'; +/** + * SyncManager - Background sync worker + * Processes operation queue and syncs with API when online + * + * Features: + * - Monitors online/offline status + * - Processes queue with FIFO order + * - Exponential backoff retry logic + * - Updates syncStatus in IndexedDB after successful sync + * - Emits sync events for UI feedback + */ +export declare class SyncManager { + private eventBus; + private queue; + private indexedDB; + private apiRepository; + private isOnline; + private isSyncing; + private syncInterval; + private maxRetries; + private intervalId; + constructor(eventBus: IEventBus, queue: OperationQueue, indexedDB: IndexedDBService, apiRepository: ApiEventRepository); + /** + * Setup online/offline event listeners + */ + private setupNetworkListeners; + /** + * Start background sync worker + */ + startSync(): void; + /** + * Stop background sync worker + */ + stopSync(): void; + /** + * Process operation queue + * Sends pending operations to API + */ + private processQueue; + /** + * Process a single operation + */ + private processOperation; + /** + * Mark event as synced in IndexedDB + */ + private markEventAsSynced; + /** + * Mark event as error in IndexedDB + */ + private markEventAsError; + /** + * Calculate exponential backoff delay + * @param retryCount Current retry count + * @returns Delay in milliseconds + */ + private calculateBackoff; + /** + * Manually trigger sync (for testing or manual sync button) + */ + triggerManualSync(): Promise; + /** + * Get current sync status + */ + getSyncStatus(): { + isOnline: boolean; + isSyncing: boolean; + isRunning: boolean; + }; + /** + * Cleanup - stop sync and remove listeners + */ + destroy(): void; +} diff --git a/wwwroot/js/workers/SyncManager.js b/wwwroot/js/workers/SyncManager.js new file mode 100644 index 0000000..4e67e87 --- /dev/null +++ b/wwwroot/js/workers/SyncManager.js @@ -0,0 +1,229 @@ +import { CoreEvents } from '../constants/CoreEvents'; +/** + * SyncManager - Background sync worker + * Processes operation queue and syncs with API when online + * + * Features: + * - Monitors online/offline status + * - Processes queue with FIFO order + * - Exponential backoff retry logic + * - Updates syncStatus in IndexedDB after successful sync + * - Emits sync events for UI feedback + */ +export class SyncManager { + constructor(eventBus, queue, indexedDB, apiRepository) { + this.isOnline = navigator.onLine; + this.isSyncing = false; + this.syncInterval = 5000; // 5 seconds + this.maxRetries = 5; + this.intervalId = null; + this.eventBus = eventBus; + this.queue = queue; + this.indexedDB = indexedDB; + this.apiRepository = apiRepository; + this.setupNetworkListeners(); + this.startSync(); + console.log('SyncManager initialized and started'); + } + /** + * Setup online/offline event listeners + */ + setupNetworkListeners() { + window.addEventListener('online', () => { + this.isOnline = true; + this.eventBus.emit(CoreEvents.OFFLINE_MODE_CHANGED, { + isOnline: true + }); + console.log('SyncManager: Network online - starting sync'); + this.startSync(); + }); + window.addEventListener('offline', () => { + this.isOnline = false; + this.eventBus.emit(CoreEvents.OFFLINE_MODE_CHANGED, { + isOnline: false + }); + console.log('SyncManager: Network offline - pausing sync'); + this.stopSync(); + }); + } + /** + * Start background sync worker + */ + startSync() { + if (this.intervalId) { + return; // Already running + } + console.log('SyncManager: Starting background sync'); + // Process immediately + this.processQueue(); + // Then poll every syncInterval + this.intervalId = window.setInterval(() => { + this.processQueue(); + }, this.syncInterval); + } + /** + * Stop background sync worker + */ + stopSync() { + if (this.intervalId) { + window.clearInterval(this.intervalId); + this.intervalId = null; + console.log('SyncManager: Stopped background sync'); + } + } + /** + * Process operation queue + * Sends pending operations to API + */ + async processQueue() { + // Don't sync if offline + if (!this.isOnline) { + return; + } + // Don't start new sync if already syncing + if (this.isSyncing) { + return; + } + // Check if queue is empty + if (await this.queue.isEmpty()) { + return; + } + this.isSyncing = true; + try { + const operations = await this.queue.getAll(); + this.eventBus.emit(CoreEvents.SYNC_STARTED, { + operationCount: operations.length + }); + // Process operations one by one (FIFO) + for (const operation of operations) { + await this.processOperation(operation); + } + this.eventBus.emit(CoreEvents.SYNC_COMPLETED, { + operationCount: operations.length + }); + } + catch (error) { + console.error('SyncManager: Queue processing error:', error); + this.eventBus.emit(CoreEvents.SYNC_FAILED, { + error: error instanceof Error ? error.message : 'Unknown error' + }); + } + finally { + this.isSyncing = false; + } + } + /** + * Process a single operation + */ + async processOperation(operation) { + // Check if max retries exceeded + if (operation.retryCount >= this.maxRetries) { + console.error(`SyncManager: Max retries exceeded for operation ${operation.id}`, operation); + await this.queue.remove(operation.id); + await this.markEventAsError(operation.eventId); + return; + } + try { + // Send to API based on operation type + switch (operation.type) { + case 'create': + await this.apiRepository.sendCreate(operation.data); + break; + case 'update': + await this.apiRepository.sendUpdate(operation.eventId, operation.data); + break; + case 'delete': + await this.apiRepository.sendDelete(operation.eventId); + break; + default: + console.error(`SyncManager: Unknown operation type ${operation.type}`); + await this.queue.remove(operation.id); + return; + } + // Success - remove from queue and mark as synced + await this.queue.remove(operation.id); + await this.markEventAsSynced(operation.eventId); + console.log(`SyncManager: Successfully synced operation ${operation.id}`); + } + catch (error) { + console.error(`SyncManager: Failed to sync operation ${operation.id}:`, error); + // Increment retry count + await this.queue.incrementRetryCount(operation.id); + // Calculate backoff delay + const backoffDelay = this.calculateBackoff(operation.retryCount + 1); + this.eventBus.emit(CoreEvents.SYNC_RETRY, { + operationId: operation.id, + retryCount: operation.retryCount + 1, + nextRetryIn: backoffDelay + }); + } + } + /** + * Mark event as synced in IndexedDB + */ + async markEventAsSynced(eventId) { + try { + const event = await this.indexedDB.getEvent(eventId); + if (event) { + event.syncStatus = 'synced'; + await this.indexedDB.saveEvent(event); + } + } + catch (error) { + console.error(`SyncManager: Failed to mark event ${eventId} as synced:`, error); + } + } + /** + * Mark event as error in IndexedDB + */ + async markEventAsError(eventId) { + try { + const event = await this.indexedDB.getEvent(eventId); + if (event) { + event.syncStatus = 'error'; + await this.indexedDB.saveEvent(event); + } + } + catch (error) { + console.error(`SyncManager: Failed to mark event ${eventId} as error:`, error); + } + } + /** + * Calculate exponential backoff delay + * @param retryCount Current retry count + * @returns Delay in milliseconds + */ + calculateBackoff(retryCount) { + // Exponential backoff: 2^retryCount * 1000ms + // Retry 1: 2s, Retry 2: 4s, Retry 3: 8s, Retry 4: 16s, Retry 5: 32s + const baseDelay = 1000; + const exponentialDelay = Math.pow(2, retryCount) * baseDelay; + const maxDelay = 60000; // Max 1 minute + return Math.min(exponentialDelay, maxDelay); + } + /** + * Manually trigger sync (for testing or manual sync button) + */ + async triggerManualSync() { + console.log('SyncManager: Manual sync triggered'); + await this.processQueue(); + } + /** + * Get current sync status + */ + getSyncStatus() { + return { + isOnline: this.isOnline, + isSyncing: this.isSyncing, + isRunning: this.intervalId !== null + }; + } + /** + * Cleanup - stop sync and remove listeners + */ + destroy() { + this.stopSync(); + // Note: We don't remove window event listeners as they're global + } +} +//# sourceMappingURL=SyncManager.js.map \ No newline at end of file diff --git a/wwwroot/js/workers/SyncManager.js.map b/wwwroot/js/workers/SyncManager.js.map new file mode 100644 index 0000000..3bdd938 --- /dev/null +++ b/wwwroot/js/workers/SyncManager.js.map @@ -0,0 +1 @@ +{"version":3,"file":"SyncManager.js","sourceRoot":"","sources":["../../../src/workers/SyncManager.ts"],"names":[],"mappings":"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"} \ No newline at end of file