UNPKG

@senx/warpview-editor

Version:

WarpView Editor Elements

697 lines 108 kB
import { commentReducer, reduceComments, ReviewCommentRenderState, ReviewCommentStatus, } from './events-comments-reducers'; import * as uuid from 'uuid'; export { reduceComments }; const monacoWindow = window; var NavigationDirection; (function (NavigationDirection) { NavigationDirection[NavigationDirection["next"] = 1] = "next"; NavigationDirection[NavigationDirection["prev"] = 2] = "prev"; })(NavigationDirection || (NavigationDirection = {})); export var EditorMode; (function (EditorMode) { EditorMode[EditorMode["insertComment"] = 1] = "insertComment"; EditorMode[EditorMode["editComment"] = 2] = "editComment"; EditorMode[EditorMode["toolbar"] = 3] = "toolbar"; })(EditorMode || (EditorMode = {})); export function createReviewManager(editor, currentUser, actions, onChange, config, verbose) { //For Debug: (window as any).editor = editor; const rm = new ReviewManager(editor, currentUser, onChange, config, verbose); rm.load(actions || []); return rm; } const defaultReviewManagerConfig = { commentIndent: 20, commentIndentOffset: 20, editButtonEditText: 'Edit', editButtonEnableEdit: true, editButtonEnableRemove: true, editButtonOffset: '-10px', formatDate: null, readOnly: false, rulerMarkerColor: 'darkorange', rulerMarkerDarkColor: 'darkorange', showAddCommentGlyph: true, showInRuler: true, verticalOffset: 0, cancelButton: { label: 'Cancel', class: '' }, addButton: { label: 'Add comment', class: '' }, replyButton: { label: 'Reply', class: '' }, removeButton: { label: 'Remove', class: '' }, editButton: { label: 'Edit', class: '' } }; const CONTROL_ATTR_NAME = 'ReviewManagerControl'; const POSITION_BELOW = 2; //above=1, below=2, exact=0 const POSITION_EXACT = 0; export class ReviewManager { constructor(editor, currentUser, onChange, config, verbose) { this.currentUser = currentUser; this.editor = editor; this.activeComment = null; //TODO - consider moving onto the store this.widgetInlineToolbar = null; this.widgetInlineCommentEditor = null; this.onChange = onChange; this.editorMode = EditorMode.toolbar; this.config = { ...defaultReviewManagerConfig, ...(config || {}) }; this.currentLineDecorations = []; this.currentCommentDecorations = []; this.currentLineDecorationLineNumber = null; this.events = []; this.store = { comments: {} }; //, viewZoneIdsToDelete: [] }; this.renderStore = {}; this.verbose = verbose; this.editorConfig = this.editor.getRawOptions(); this.editor.onDidChangeConfiguration(() => (this.editorConfig = this.editor.getRawOptions())); this.editor.onMouseDown(this.handleMouseDown.bind(this)); this.canAddCondition = this.editor.createContextKey('add-context-key', !this.config.readOnly); this.inlineToolbarElements = this.createInlineToolbarWidget(); this.editorElements = this.createInlineEditorWidget(); this.addActions(); if (this.config.showAddCommentGlyph) { this.editor.onMouseMove(this.handleMouseMove.bind(this)); } } setReadOnlyMode(value) { this.config.readOnly = value; this.canAddCondition.set(!value); this.renderAddCommentLineDecoration(null); } load(events) { const store = reduceComments(events); this.loadFromStore(store, events); } loadFromStore(store, events) { this.editor.changeViewZones((changeAccessor) => { // Remove all the existing comments for (const viewState of Object.values(this.store.comments)) { const x = this.getRenderState(viewState.comment.id); if (x && x.viewZoneId !== null) { changeAccessor.removeZone(x.viewZoneId); } } this.events = events; this.store = store; this.store.deletedCommentIds = null; this.store.dirtyCommentIds = null; this.renderStore = {}; this.refreshComments(); this.verbose && console.debug('Events Loaded:', events.length, 'Review Comments:', Object.values(this.store.comments).length); }); } getThemedColor(name) { // editor.background: e {rgba: e} // editor.foreground: e {rgba: e} // editor.inactiveSelectionBackground: e {rgba: e} // editor.selectionHighlightBackground: e {rgba: e} // editorIndentGuide.activeBackground: e {rgba: e} // editorIndentGuide.background: e {rgba: e} const theme = this.editor._themeService._theme; let value = theme.getColor(name); // HACK - Buttons themes are not in monaco ... so just hack in theme for dark const missingThemes = { /*dark: { "button.background": "#0e639c", "button.foreground": "#ffffff", }, light: { "button.background": "#007acc", "button.foreground": "#ffffff", },*/ }; if (!value) { value = missingThemes[theme.themeName.indexOf('dark') > -1 ? 'dark' : 'light'][name]; } return value; } createInlineEditButtonsElement() { const root = document.createElement('div'); root.className = 'editButtonsContainer'; root.style.marginLeft = this.config.editButtonOffset; const add = document.createElement('span'); add.innerText = this.config.replyButton.label; add.className = this.config.replyButton.class || 'editButton add'; add.setAttribute(CONTROL_ATTR_NAME, ''); add.onclick = () => this.setEditorMode(EditorMode.insertComment, 'add-comment'); root.appendChild(add); let remove = null; let edit = null; let spacer = null; if (this.config.editButtonEnableRemove) { spacer = document.createElement('div'); spacer.innerText = ' '; root.appendChild(spacer); remove = document.createElement('span'); remove.setAttribute(CONTROL_ATTR_NAME, ''); remove.innerText = this.config.removeButton.label; remove.className = this.config.removeButton.class || 'editButton remove'; remove.onclick = () => this.activeComment && this.removeComment(this.activeComment.id); root.appendChild(remove); } if (this.config.editButtonEnableEdit) { spacer = document.createElement('div'); spacer.innerText = ' '; root.appendChild(spacer); edit = document.createElement('span'); edit.setAttribute(CONTROL_ATTR_NAME, ''); edit.innerText = this.config.editButton.label || 'Edit'; edit.className = this.config.editButton.class || 'editButton edit'; edit.onclick = () => this.setEditorMode(EditorMode.editComment, 'edit'); root.appendChild(edit); } return { root, add, remove, edit }; } handleCancel() { this.setEditorMode(EditorMode.toolbar, 'cancel'); this.editor.focus(); } handleAddComment() { const lineNumber = this.activeComment ? this.activeComment.lineNumber : this.editor.getSelection().endLineNumber; const text = this.editorElements.textarea.value; const selection = this.activeComment ? null : this.editor.getSelection(); this.addComment(lineNumber, text, selection); this.setEditorMode(EditorMode.toolbar, 'add-comment-1'); this.editor.focus(); } handleTextAreaKeyDown(e) { if (e.code === 'Escape') { this.handleCancel(); e.preventDefault(); console.info('preventDefault: Escape Key'); } else if (e.code === 'Enter' && e.ctrlKey) { this.handleAddComment(); e.preventDefault(); console.info('preventDefault: ctrl+Enter'); } } createInlineEditorElement() { const theme = this.editor._themeService._theme.themeName.indexOf('dark') > -1 ? 'dark' : 'light'; const root = document.createElement('div'); root.className = 'reviewCommentEditor ' + theme; const textarea = document.createElement('textarea'); textarea.setAttribute(CONTROL_ATTR_NAME, ''); textarea.className = 'reviewCommentEditor text'; textarea.innerText = ''; textarea.style.resize = 'none'; textarea.style.width = '100%'; textarea.name = 'text'; textarea.onkeydown = this.handleTextAreaKeyDown.bind(this); const confirm = document.createElement('button'); confirm.setAttribute(CONTROL_ATTR_NAME, ''); confirm.className = this.config.addButton.class || 'reviewCommentEditor save'; confirm.innerText = this.config.addButton.label || 'Add Comment'; confirm.onclick = this.handleAddComment.bind(this); const cancel = document.createElement('button'); cancel.setAttribute(CONTROL_ATTR_NAME, ''); cancel.className = this.config.cancelButton.class || 'reviewCommentEditor cancel'; cancel.innerText = this.config.cancelButton.label || 'Cancel'; cancel.onclick = this.handleCancel.bind(this); root.appendChild(textarea); root.appendChild(cancel); root.appendChild(confirm); return { root, confirm, cancel, textarea }; } createInlineToolbarWidget() { const buttonsElement = this.createInlineEditButtonsElement(); const this_ = this; this.widgetInlineToolbar = { allowEditorOverflow: true, getId: () => { return 'widgetInlineToolbar'; }, getDomNode: () => { return buttonsElement.root; }, getPosition: () => { if (this_.activeComment && this_.editorMode == EditorMode.toolbar && !this_.config.readOnly) { return { position: { lineNumber: this_.activeComment.lineNumber + 1, column: 1, }, preference: [POSITION_EXACT], }; } }, }; this.editor.addContentWidget(this.widgetInlineToolbar); return buttonsElement; } createInlineEditorWidget() { // doesn't re-theme when const editorElement = this.createInlineEditorElement(); this.widgetInlineCommentEditor = { allowEditorOverflow: true, getId: () => { return 'widgetInlineEditor'; }, getDomNode: () => { console.log('getDomNode'); return editorElement.root; }, getPosition: () => { if (this.editorMode == EditorMode.insertComment || this.editorMode == EditorMode.editComment) { const position = this.editor.getPosition(); return { position: { lineNumber: this.activeComment ? this.activeComment.lineNumber : position.lineNumber + 1, column: position.column, }, preference: [POSITION_EXACT], }; } }, }; this.editor.addContentWidget(this.widgetInlineCommentEditor); return editorElement; } setActiveComment(comment) { this.verbose && console.debug('setActiveComment', comment); const lineNumbersToMakeDirty = []; if (this.activeComment && (!comment || this.activeComment.lineNumber !== comment.lineNumber)) { lineNumbersToMakeDirty.push(this.activeComment.lineNumber); } if (comment) { lineNumbersToMakeDirty.push(comment.lineNumber); } this.activeComment = comment; if (lineNumbersToMakeDirty.length > 0) { this.filterAndMapComments(lineNumbersToMakeDirty, (comment) => { this.renderStore[comment.id].renderStatus = ReviewCommentRenderState.dirty; }); } } filterAndMapComments(lineNumbers, fn) { for (const cs of Object.values(this.store.comments)) { if (lineNumbers.indexOf(cs.comment.lineNumber) > -1) { fn(cs.comment); } } } handleMouseMove(ev) { if (ev.target && ev.target.position && ev.target.position.lineNumber) { this.currentLineDecorationLineNumber = ev.target.position.lineNumber; this.renderAddCommentLineDecoration(this.config.readOnly === true ? null : this.currentLineDecorationLineNumber); } } renderAddCommentLineDecoration(lineNumber) { const lines = lineNumber ? [ { range: new monacoWindow.monaco.Range(lineNumber, 0, lineNumber, 0), options: { marginClassName: 'activeLineMarginClass', zIndex: 100, }, }, ] : []; this.currentLineDecorations = this.editor.deltaDecorations(this.currentLineDecorations, lines); } handleMouseDown(ev) { // Not ideal - but couldn't figure out a different way to identify the glyph event if (ev.target.element.className && ev.target.element.className.indexOf('activeLineMarginClass') > -1) { this.editor.setPosition({ lineNumber: this.currentLineDecorationLineNumber, column: 1, }); this.setEditorMode(EditorMode.insertComment, 'mouse-down-1'); } else if (!ev.target.element.hasAttribute(CONTROL_ATTR_NAME)) { let activeComment = null; if (ev.target.detail && ev.target.detail.viewZoneId !== null) { for (const comment of Object.values(this.store.comments).map((c) => c.comment)) { const x = this.getRenderState(comment.id); if (x.viewZoneId == ev.target.detail.viewZoneId) { activeComment = comment; break; } } } this.setActiveComment(activeComment); this.refreshComments(); this.setEditorMode(EditorMode.toolbar, 'mouse-down-2'); } } calculateMarginTopOffset(includeActiveCommentHeight) { let count = 0; let marginTop = 0; const lineHeight = this.editorConfig.lineHeight; if (this.activeComment) { for (let item of this.iterateComments()) { if (item.state.comment.lineNumber === this.activeComment.lineNumber && (item.state.comment != this.activeComment || includeActiveCommentHeight)) { count += this.calculateNumberOfLines(item.state.comment.text); } if (item.state.comment == this.activeComment) { break; } } marginTop = count * lineHeight; } return marginTop + this.config.verticalOffset; } layoutInlineToolbar() { this.inlineToolbarElements.root.style.backgroundColor = this.getThemedColor('editor.background'); this.inlineToolbarElements.root.style.marginTop = `${this.calculateMarginTopOffset(false)}px`; if (this.inlineToolbarElements.remove) { const hasChildren = this.activeComment && this.iterateComments((c) => c.comment.id === this.activeComment.id).length > 1; const isSameUser = this.activeComment && this.activeComment.author === this.currentUser; this.inlineToolbarElements.remove.style.display = hasChildren ? 'none' : ''; this.inlineToolbarElements.edit.style.display = hasChildren || !isSameUser ? 'none' : ''; } this.editor.layoutContentWidget(this.widgetInlineToolbar); } layoutInlineCommentEditor() { [this.editorElements.root, this.editorElements.textarea].forEach((e) => { e.style.backgroundColor = this.getThemedColor('editor.background'); e.style.color = this.getThemedColor('editor.foreground'); }); this.editorElements.confirm.innerText = this.editorMode === EditorMode.insertComment ? this.config.addButton.label || 'Add Comment' : 'Edit Comment'; // this.editorElements.root.style.marginTop = `${this.calculateMarginTopOffset( // true // )}px`; this.editor.layoutContentWidget(this.widgetInlineCommentEditor); } setEditorMode(mode, why = null) { this.editorMode = this.config.readOnly ? EditorMode.toolbar : mode; this.layoutInlineToolbar(); this.layoutInlineCommentEditor(); if (mode === EditorMode.insertComment || mode === EditorMode.editComment) { if (mode === EditorMode.insertComment) { this.editorElements.textarea.value = ''; } else if (mode === EditorMode.editComment) { this.editorElements.textarea.value = this.activeComment ? this.activeComment.text : ''; } // HACK - because the event in monaco doesn't have preventdefault which means editor takes focus back... setTimeout(() => this.editorElements.textarea.focus(), 100); //TODO - make configurable } } getDateTimeNow() { return new Date(); } recurseComments(allComments, filterFn, depth, results) { const comments = Object.values(allComments).filter(filterFn); for (const cs of comments) { const comment = cs.comment; delete allComments[comment.id]; results.push({ depth, state: cs, }); this.recurseComments(allComments, (x) => x.comment.parentId === comment.id, depth + 1, results); } } iterateComments(filterFn) { if (!filterFn) { filterFn = (cs) => !cs.comment.parentId; } const copyCommentState = { ...this.store.comments }; const results = []; this.recurseComments(copyCommentState, filterFn, 0, results); return results; } removeComment(id) { return this.addEvent({ type: 'delete', targetId: id }); } addComment(lineNumber, text, selection) { const event = this.editorMode === EditorMode.editComment ? { type: 'edit', text, targetId: this.activeComment.id } : { type: 'create', text, lineNumber, selection, targetId: this.activeComment && this.activeComment.id, }; return this.addEvent(event); } addEvent(event) { event.createdBy = this.currentUser; event.createdAt = this.getDateTimeNow(); event.id = uuid.v4(); this.events.push(event); this.store = commentReducer(event, this.store); if (this.activeComment && !this.store.comments[this.activeComment.id]) { this.setActiveComment(null); } else if (this.activeComment && this.activeComment.status === ReviewCommentStatus.deleted) { this.setActiveComment(null); } this.refreshComments(); this.layoutInlineToolbar(); if (this.onChange) { this.onChange(this.events); } return event; } formatDate(dt) { if (this.config.formatDate) { return this.config.formatDate(dt); } else if (dt instanceof Date) { return dt.toISOString(); } else { return dt; } } static createElement(text, className, tagName = null) { const span = document.createElement(tagName || 'span'); span.className = className; span.innerText = text; return span; } getRenderState(commentId) { if (!this.renderStore[commentId]) { this.renderStore[commentId] = { viewZoneId: null, renderStatus: null }; } return this.renderStore[commentId]; } refreshComments() { this.editor.changeViewZones((changeAccessor) => { const lineNumbers = {}; for (const cid of Array.from(this.store.deletedCommentIds || [])) { const viewZoneId = this.renderStore[cid]?.viewZoneId; changeAccessor.removeZone(viewZoneId); this.verbose && console.debug('Zone.Delete', viewZoneId); } this.store.deletedCommentIds = null; for (const cid of Array.from(this.store.dirtyCommentIds || [])) { this.getRenderState(cid).renderStatus = ReviewCommentRenderState.dirty; } this.store.dirtyCommentIds = null; for (const item of this.iterateComments()) { const rs = this.getRenderState(item.state.comment.id); if (rs.renderStatus === ReviewCommentRenderState.hidden) { this.verbose && console.debug('Zone.Hidden', item.state.comment.id); changeAccessor.removeZone(rs.viewZoneId); rs.viewZoneId = null; continue; } if (rs.renderStatus === ReviewCommentRenderState.dirty) { this.verbose && console.debug('Zone.Dirty', item.state.comment.id); changeAccessor.removeZone(rs.viewZoneId); rs.viewZoneId = null; rs.renderStatus = ReviewCommentRenderState.normal; } if (!lineNumbers[item.state.comment.lineNumber]) { lineNumbers[item.state.comment.lineNumber] = item.state.comment.selection; } if (rs.viewZoneId == null) { this.verbose && console.debug('Zone.Create', item.state.comment.id); const isActive = this.activeComment == item.state.comment; const domNode = ReviewManager.createElement('', `reviewComment ${isActive ? 'active' : ' inactive'}`); domNode.style.paddingLeft = this.config.commentIndent * (item.depth + 1) + this.config.commentIndentOffset + 'px'; domNode.style.backgroundColor = this.getThemedColor('editor.selectionHighlightBackground'); // For Debug - domNode.appendChild(this.createElement(`${item.state.comment.id}`, 'reviewComment id')) domNode.appendChild(ReviewManager.createElement(`${item.state.comment.author || ' '}`, 'reviewComment author')); domNode.appendChild(ReviewManager.createElement(' at ' + this.formatDate(item.state.comment.dt), 'reviewComment dt')); if (item.state.history.length > 1) { domNode.appendChild(ReviewManager.createElement(`(Edited ${item.state.history.length - 1} times)`, 'reviewComment history')); } domNode.appendChild(ReviewManager.createElement(`${item.state.comment.text}`, 'reviewComment text', 'div')); //todo jxb fixme // function getTextWidth() { // text = document.createElement("span"); // document.body.appendChild(text); // text.style.font = "times new roman"; // text.style.fontSize = 16 + "px"; // text.style.height = 'auto'; // text.style.width = 'auto'; // text.style.position = 'absolute'; // text.style.whiteSpace = 'no-wrap'; // text.innerHTML = 'Hello World'; // width = Math.ceil(text.clientWidth); // formattedWidth = width + "px"; // document.querySelector('.output').textContent // = formattedWidth; // document.body.removeChild(text); // } rs.viewZoneId = changeAccessor.addZone({ afterLineNumber: item.state.comment.lineNumber, heightInLines: this.calculateNumberOfLines(item.state.comment.text), domNode: domNode, suppressMouseDown: true, // This stops focus being lost the editor - meaning keyboard shortcuts keeps working }); } } if (this.config.showInRuler) { const decorators = []; for (const [ln, selection] of Object.entries(lineNumbers)) { decorators.push({ range: new monacoWindow.monaco.Range(ln, 0, ln, 0), options: { isWholeLine: true, overviewRuler: { color: this.config.rulerMarkerColor, darkColor: this.config.rulerMarkerDarkColor, position: 2, }, }, }); if (selection) { decorators.push({ range: new monacoWindow.monaco.Range(selection.startLineNumber, selection.startColumn, selection.endLineNumber, selection.endColumn), options: { className: 'reviewComment selection', }, }); } } this.currentCommentDecorations = this.editor.deltaDecorations(this.currentCommentDecorations, decorators); } }); } calculateNumberOfLines(text) { return text ? text.split(/\r*\n/).length + 1 : 1; } addActions() { this.editor.addAction({ id: 'my-unique-id-add', label: this.config.addButton.label || 'Add Comment', keybindings: [ monacoWindow.monaco?.KeyMod.CtrlCmd | monacoWindow.monaco?.KeyCode.F10, ], precondition: 'add-context-key', keybindingContext: null, contextMenuGroupId: 'navigation', contextMenuOrder: 0, run: () => { this.setEditorMode(EditorMode.insertComment, 'add-comment-x'); }, }); this.editor.addAction({ id: 'my-unique-id-next', label: 'Next Comment', keybindings: [ monacoWindow.monaco?.KeyMod.CtrlCmd | monacoWindow.monaco?.KeyCode.F12, ], precondition: null, keybindingContext: null, contextMenuGroupId: 'navigation', contextMenuOrder: 0.101, run: () => { this.navigateToComment(NavigationDirection.next); }, }); this.editor.addAction({ id: 'my-unique-id-prev', label: 'Prev Comment', keybindings: [ monacoWindow.monaco?.KeyMod.CtrlCmd | monacoWindow.monaco?.KeyCode.F11, ], precondition: null, keybindingContext: null, contextMenuGroupId: 'navigation', contextMenuOrder: 0.102, run: () => { this.navigateToComment(NavigationDirection.prev); }, }); } navigateToComment(direction) { let currentLine = 0; if (this.activeComment) { currentLine = this.activeComment.lineNumber; } else { currentLine = this.editor.getPosition().lineNumber; } const comments = Object.values(this.store.comments) .map((cs) => cs.comment) .filter((c) => { if (!c.parentId) { if (direction === NavigationDirection.next) { return c.lineNumber > currentLine; } else if (direction === NavigationDirection.prev) { return c.lineNumber < currentLine; } } }); if (comments.length) { comments.sort((a, b) => { if (direction === NavigationDirection.next) { return a.lineNumber - b.lineNumber; } else if (direction === NavigationDirection.prev) { return b.lineNumber - a.lineNumber; } }); const comment = comments[0]; this.setActiveComment(comment); this.refreshComments(); this.layoutInlineToolbar(); this.editor.revealLineInCenter(comment.lineNumber); } } updateConfig(config) { this.config = { ...defaultReviewManagerConfig, ...(config || {}) }; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQ29kZVJldmlldy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL3dhcnB2aWV3LWVkaXRvci1uZy9zcmMvbGliL2VsZW1lbnRzL3dhcnAtdmlldy1lZGl0b3IvcHJvdmlkZXJzL0NvZGVSZXZpZXcudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBZ0JBLE9BQU8sRUFFTCxjQUFjLEVBRWQsY0FBYyxFQUdkLHdCQUF3QixFQUV4QixtQkFBbUIsR0FDcEIsTUFBTSw0QkFBNEIsQ0FBQztBQUNwQyxPQUFPLEtBQUssSUFBSSxNQUFNLE1BQU0sQ0FBQztBQUU3QixPQUFPLEVBQXlDLGNBQWMsRUFBQyxDQUFDO0FBTWhFLE1BQU0sWUFBWSxHQUFJLE1BQThCLENBQUM7QUFFckQsSUFBSyxtQkFHSjtBQUhELFdBQUssbUJBQW1CO0lBQ3RCLDZEQUFRLENBQUE7SUFDUiw2REFBUSxDQUFBO0FBQ1YsQ0FBQyxFQUhJLG1CQUFtQixLQUFuQixtQkFBbUIsUUFHdkI7QUFFRCxNQUFNLENBQU4sSUFBWSxVQUlYO0FBSkQsV0FBWSxVQUFVO0lBQ3BCLDZEQUFpQixDQUFBO0lBQ2pCLHlEQUFlLENBQUE7SUFDZixpREFBVyxDQUFBO0FBQ2IsQ0FBQyxFQUpXLFVBQVUsS0FBVixVQUFVLFFBSXJCO0FBRUQsTUFBTSxVQUFVLG1CQUFtQixDQUNqQyxNQUFXLEVBQ1gsV0FBbUIsRUFDbkIsT0FBOEIsRUFDOUIsUUFBMkIsRUFDM0IsTUFBNEIsRUFDNUIsT0FBaUI7SUFFakIsNkNBQTZDO0lBQzdDLE1BQU0sRUFBRSxHQUFHLElBQUksYUFBYSxDQUFDLE1BQU0sRUFBRSxXQUFXLEVBQUUsUUFBUSxFQUFFLE1BQU0sRUFBRSxPQUFPLENBQUMsQ0FBQztJQUM3RSxFQUFFLENBQUMsSUFBSSxDQUFDLE9BQU8sSUFBSSxFQUFFLENBQUMsQ0FBQztJQUN2QixPQUFPLEVBQUUsQ0FBQztBQUNaLENBQUM7QUFnRkQsTUFBTSwwQkFBMEIsR0FBK0I7SUFDN0QsYUFBYSxFQUFFLEVBQUU7SUFDakIsbUJBQW1CLEVBQUUsRUFBRTtJQUN2QixrQkFBa0IsRUFBRSxNQUFNO0lBQzFCLG9CQUFvQixFQUFFLElBQUk7SUFDMUIsc0JBQXNCLEVBQUUsSUFBSTtJQUM1QixnQkFBZ0IsRUFBRSxPQUFPO0lBQ3pCLFVBQVUsRUFBRSxJQUFJO0lBQ2hCLFFBQVEsRUFBRSxLQUFLO0lBQ2YsZ0JBQWdCLEVBQUUsWUFBWTtJQUM5QixvQkFBb0IsRUFBRSxZQUFZO0lBQ2xDLG1CQUFtQixFQUFFLElBQUk7SUFDekIsV0FBVyxFQUFFLElBQUk7SUFDakIsY0FBYyxFQUFFLENBQUM7SUFDakIsWUFBWSxFQUFFO1FBQ1osS0FBSyxFQUFFLFFBQVE7UUFDZixLQUFLLEVBQUUsRUFBRTtLQUNWO0lBQ0QsU0FBUyxFQUFFO1FBQ1QsS0FBSyxFQUFFLGFBQWE7UUFDcEIsS0FBSyxFQUFFLEVBQUU7S0FDVjtJQUNELFdBQVcsRUFBRTtRQUNYLEtBQUssRUFBRSxPQUFPO1FBQ2QsS0FBSyxFQUFFLEVBQUU7S0FDVjtJQUNELFlBQVksRUFBRTtRQUNaLEtBQUssRUFBRSxRQUFRO1FBQ2YsS0FBSyxFQUFFLEVBQUU7S0FDVjtJQUNELFVBQVUsRUFBRTtRQUNWLEtBQUssRUFBRSxNQUFNO1FBQ2IsS0FBSyxFQUFFLEVBQUU7S0FDVjtDQUNGLENBQUM7QUFFRixNQUFNLGlCQUFpQixHQUFHLHNCQUFzQixDQUFDO0FBQ2pELE1BQU0sY0FBYyxHQUFHLENBQUMsQ0FBQyxDQUFDLDJCQUEyQjtBQUNyRCxNQUFNLGNBQWMsR0FBRyxDQUFDLENBQUM7QUFxQnpCLE1BQU0sT0FBTyxhQUFhO0lBdUJ4QixZQUNFLE1BQVcsRUFDWCxXQUFtQixFQUNuQixRQUEwQixFQUMxQixNQUE0QixFQUM1QixPQUFpQjtRQUVqQixJQUFJLENBQUMsV0FBVyxHQUFHLFdBQVcsQ0FBQztRQUMvQixJQUFJLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQztRQUNyQixJQUFJLENBQUMsYUFBYSxHQUFHLElBQUksQ0FBQyxDQUFDLHVDQUF1QztRQUNsRSxJQUFJLENBQUMsbUJBQW1CLEdBQUcsSUFBSSxDQUFDO1FBQ2hDLElBQUksQ0FBQyx5QkFBeUIsR0FBRyxJQUFJLENBQUM7UUFDdEMsSUFBSSxDQUFDLFFBQVEsR0FBRyxRQUFRLENBQUM7UUFDekIsSUFBSSxDQUFDLFVBQVUsR0FBRyxVQUFVLENBQUMsT0FBTyxDQUFDO1FBQ3JDLElBQUksQ0FBQyxNQUFNLEdBQUcsRUFBQyxHQUFHLDBCQUEwQixFQUFFLEdBQUcsQ0FBQyxNQUFNLElBQUksRUFBRSxDQUFDLEVBQUMsQ0FBQztRQUNqRSxJQUFJLENBQUMsc0JBQXNCLEdBQUcsRUFBRSxDQUFDO1FBQ2pDLElBQUksQ0FBQyx5QkFBeUIsR0FBRyxFQUFFLENBQUM7UUFDcEMsSUFBSSxDQUFDLCtCQUErQixHQUFHLElBQUksQ0FBQztRQUM1QyxJQUFJLENBQUMsTUFBTSxHQUFHLEVBQUUsQ0FBQztRQUNqQixJQUFJLENBQUMsS0FBSyxHQUFHLEVBQUMsUUFBUSxFQUFFLEVBQUUsRUFBQyxDQUFDLENBQUMsOEJBQThCO1FBQzNELElBQUksQ0FBQyxXQUFXLEdBQUcsRUFBRSxDQUFDO1FBRXRCLElBQUksQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO1FBRXZCLElBQUksQ0FBQyxZQUFZLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxhQUFhLEVBQUUsQ0FBQztRQUNoRCxJQUFJLENBQUMsTUFBTSxDQUFDLHdCQUF3QixDQUNsQyxHQUFHLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxZQUFZLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxhQUFhLEVBQUUsQ0FBQyxDQUN4RCxDQUFDO1FBQ0YsSUFBSSxDQUFDLE1BQU0sQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLGVBQWUsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztRQUN6RCxJQUFJLENBQUMsZUFBZSxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsZ0JBQWdCLENBQ2pELGlCQUFpQixFQUNqQixDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUN0QixDQUFDO1FBQ0YsSUFBSSxDQUFDLHFCQUFxQixHQUFHLElBQUksQ0FBQyx5QkFBeUIsRUFBRSxDQUFDO1FBQzlELElBQUksQ0FBQyxjQUFjLEdBQUcsSUFBSSxDQUFDLHdCQUF3QixFQUFFLENBQUM7UUFDdEQsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO1FBRWxCLElBQUksSUFBSSxDQUFDLE1BQU0sQ0FBQyxtQkFBbUIsRUFBRTtZQUNuQyxJQUFJLENBQUMsTUFBTSxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsZUFBZSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1NBQzFEO0lBQ0gsQ0FBQztJQUVELGVBQWUsQ0FBQyxLQUFjO1FBQzVCLElBQUksQ0FBQyxNQUFNLENBQUMsUUFBUSxHQUFHLEtBQUssQ0FBQztRQUM3QixJQUFJLENBQUMsZUFBZSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQ2pDLElBQUksQ0FBQyw4QkFBOEIsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUM1QyxDQUFDO0lBRUQsSUFBSSxDQUFDLE1BQTRCO1FBQy9CLE1BQU0sS0FBSyxHQUFHLGNBQWMsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUNyQyxJQUFJLENBQUMsYUFBYSxDQUFDLEtBQUssRUFBRSxNQUFNLENBQUMsQ0FBQztJQUNwQyxDQUFDO0lBRUQsYUFBYSxDQUFDLEtBQXlCLEVBQUUsTUFBNEI7UUFDbkUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxlQUFlLENBQ3pCLENBQUMsY0FBMkQsRUFBRSxFQUFFO1lBQzlELG1DQUFtQztZQUNuQyxLQUFLLE1BQU0sU0FBUyxJQUFJLE1BQU0sQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxRQUFRLENBQUMsRUFBRTtnQkFDMUQsTUFBTSxDQUFDLEdBQUcsSUFBSSxDQUFDLGNBQWMsQ0FBQyxTQUFTLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQyxDQUFDO2dCQUNwRCxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsVUFBVSxLQUFLLElBQUksRUFBRTtvQkFDOUIsY0FBYyxDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUMsVUFBVSxDQUFDLENBQUM7aUJBQ3pDO2FBQ0Y7WUFFRCxJQUFJLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQztZQUNyQixJQUFJLENBQUMsS0FBSyxHQUFHLEtBQUssQ0FBQztZQUNuQixJQUFJLENBQUMsS0FBSyxDQUFDLGlCQUFpQixHQUFHLElBQUksQ0FBQztZQUNwQyxJQUFJLENBQUMsS0FBSyxDQUFDLGVBQWUsR0FBRyxJQUFJLENBQUM7WUFDbEMsSUFBSSxDQUFDLFdBQVcsR0FBRyxFQUFFLENBQUM7WUFFdEIsSUFBSSxDQUFDLGVBQWUsRUFBRSxDQUFDO1lBRXZCLElBQUksQ0FBQyxPQUFPO2dCQUNaLE9BQU8sQ0FBQyxLQUFLLENBQ1gsZ0JBQWdCLEVBQ2hCLE1BQU0sQ0FBQyxNQUFNLEVBQ2Isa0JBQWtCLEVBQ2xCLE1BQU0sQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxRQUFRLENBQUMsQ0FBQyxNQUFNLENBQzFDLENBQUM7UUFDSixDQUFDLENBQ0YsQ0FBQztJQUNKLENBQUM7SUFFRCxjQUFjLENBQUMsSUFBWTtRQUN6QixpQ0FBaUM7UUFDakMsaUNBQWlDO1FBQ2pDLGtEQUFrRDtRQUNsRCxtREFBbUQ7UUFDbkQsa0RBQWtEO1FBQ2xELDRDQUE0QztRQUM1QyxNQUFNLEtBQUssR0FBSSxJQUFJLENBQUMsTUFBYyxDQUFDLGFBQWEsQ0FBQyxNQUFNLENBQUM7UUFDeEQsSUFBSSxLQUFLLEdBQUcsS0FBSyxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUVqQyw2RUFBNkU7UUFDN0UsTUFBTSxhQUFhLEdBQUc7UUFDcEI7Ozs7Ozs7WUFPSTtTQUNMLENBQUM7UUFDRixJQUFJLENBQUMsS0FBSyxFQUFFO1lBQ1YsS0FBSztnQkFDSCxhQUFhLENBQUMsS0FBSyxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLENBQ3BFLElBQUksQ0FDSCxDQUFDO1NBQ1A7UUFDRCxPQUFPLEtBQUssQ0FBQztJQUNmLENBQUM7SUFFRCw4QkFBOEI7UUFDNUIsTUFBTSxJQUFJLEdBQUcsUUFBUSxDQUFDLGFBQWEsQ0FBQyxLQUFLLENBQW1CLENBQUM7UUFDN0QsSUFBSSxDQUFDLFNBQVMsR0FBRyxzQkFBc0IsQ0FBQztRQUN4QyxJQUFJLENBQUMsS0FBSyxDQUFDLFVBQVUsR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLGdCQUFnQixDQUFDO1FBRXJELE1BQU0sR0FBRyxHQUFHLFFBQVEsQ0FBQyxhQUFhLENBQUMsTUFBTSxDQUFvQixDQUFDO1FBQzlELEdBQUcsQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxXQUFXLENBQUMsS0FBSyxDQUFDO1FBQzlDLEdBQUcsQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxXQUFXLENBQUMsS0FBSyxJQUFJLGdCQUFnQixDQUFDO1FBQ2xFLEdBQUcsQ0FBQyxZQUFZLENBQUMsaUJBQWlCLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFDeEMsR0FBRyxDQUFDLE9BQU8sR0FBRyxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUFDLFVBQVUsQ0FBQyxhQUFhLEVBQUUsYUFBYSxDQUFDLENBQUM7UUFDaEYsSUFBSSxDQUFDLFdBQVcsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUV0QixJQUFJLE1BQU0sR0FBRyxJQUFJLENBQUM7UUFDbEIsSUFBSSxJQUFJLEdBQUcsSUFBSSxDQUFDO1FBQ2hCLElBQUksTUFBTSxHQUFHLElBQUksQ0FBQztRQUVsQixJQUFJLElBQUksQ0FBQyxNQUFNLENBQUMsc0JBQXNCLEVBQUU7WUFDdEMsTUFBTSxHQUFHLFFBQVEsQ0FBQyxhQUFhLENBQUMsS0FBSyxDQUFtQixDQUFDO1lBQ3pELE1BQU0sQ0FBQyxTQUFTLEdBQUcsR0FBRyxDQUFDO1lBQ3ZCLElBQUksQ0FBQyxXQUFXLENBQUMsTUFBTSxDQUFDLENBQUM7WUFFekIsTUFBTSxHQUFHLFFBQVEsQ0FBQyxhQUFhLENBQUMsTUFBTSxDQUFvQixDQUFDO1lBQzNELE1BQU0sQ0FBQyxZQUFZLENBQUMsaUJBQWlCLEVBQUUsRUFBRSxDQUFDLENBQUM7WUFDM0MsTUFBTSxDQUFDLFNBQVMsR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLFlBQVksQ0FBQyxLQUFLLENBQUM7WUFDbEQsTUFBTSxDQUFDLFNBQVMsR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLFlBQVksQ0FBQyxLQUFLLElBQUksbUJBQW1CLENBQUM7WUFDekUsTUFBTSxDQUFDLE9BQU8sR0FBRyxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsYUFBYSxJQUFJLElBQUksQ0FBQyxhQUFhLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxFQUFFLENBQUMsQ0FBQztZQUN2RixJQUFJLENBQUMsV0FBVyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1NBQzFCO1FBRUQsSUFBSSxJQUFJLENBQUMsTUFBTSxDQUFDLG9CQUFvQixFQUFFO1lBQ3BDLE1BQU0sR0FBRyxRQUFRLENBQUMsYUFBYSxDQUFDLEtBQUssQ0FBbUIsQ0FBQztZQUN6RCxNQUFNLENBQUMsU0FBUyxHQUFHLEdBQUcsQ0FBQztZQUN2QixJQUFJLENBQUMsV0FBVyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1lBRXpCLElBQUksR0FBRyxRQUFRLENBQUMsYUFBYSxDQUFDLE1BQU0sQ0FBb0IsQ0FBQztZQUN6RCxJQUFJLENBQUMsWUFBWSxDQUFDLGlCQUFpQixFQUFFLEVBQUUsQ0FBQyxDQUFDO1lBQ3pDLElBQUksQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxVQUFVLENBQUMsS0FBSyxJQUFJLE1BQU0sQ0FBQztZQUN4RCxJQUFJLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsVUFBVSxDQUFDLEtBQUssSUFBSSxpQkFBaUIsQ0FBQztZQUNuRSxJQUFJLENBQUMsT0FBTyxHQUFHLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxhQUFhLENBQUMsVUFBVSxDQUFDLFdBQVcsRUFBRSxNQUFNLENBQUMsQ0FBQztZQUN4RSxJQUFJLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxDQUFDO1NBQ3hCO1FBRUQsT0FBTyxFQUFDLElBQUksRUFBRSxHQUFHLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBQyxDQUFDO0lBQ25DLENBQUM7SUFFRCxZQUFZO1FBQ1YsSUFBSSxDQUFDLGFBQWEsQ0FBQyxVQUFVLENBQUMsT0FBTyxFQUFFLFFBQVEsQ0FBQyxDQUFDO1FBQ2pELElBQUksQ0FBQyxNQUFNLENBQUMsS0FBSyxFQUFFLENBQUM7SUFDdEIsQ0FBQztJQUVELGdCQUFnQjtRQUNkLE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxhQUFhO1lBQ25DLENBQUMsQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUFDLFVBQVU7WUFDL0IsQ0FBQyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsWUFBWSxFQUFFLENBQUMsYUFBYSxDQUFDO1FBQzdDLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxjQUFjLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQztRQUNoRCxNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsYUFBYTtZQUNsQyxDQUFDLENBQUMsSUFBSTtZQUNOLENBQUMsQ0FBRSxJQUFJLENBQUMsTUFBTSxDQUFDLFlBQVksRUFBb0IsQ0FBQztRQUNsRCxJQUFJLENBQUMsVUFBVSxDQUFDLFVBQVUsRUFBRSxJQUFJLEVBQUUsU0FBUyxDQUFDLENBQUM7UUFDN0MsSUFBSSxDQUFDLGFBQWEsQ0FBQyxVQUFVLENBQUMsT0FBTyxFQUFFLGVBQWUsQ0FBQyxDQUFDO1FBQ3hELElBQUksQ0FBQyxNQUFNLENBQUMsS0FBSyxFQUFFLENBQUM7SUFDdEIsQ0FBQztJQUVELHFCQUFxQixDQUFDLENBQWdCO1FBQ3BDLElBQUksQ0FBQyxDQUFDLElBQUksS0FBSyxRQUFRLEVBQUU7WUFDdkIsSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFDO1lBQ3BCLENBQUMsQ0FBQyxjQUFjLEVBQUUsQ0FBQztZQUNuQixPQUFPLENBQUMsSUFBSSxDQUFDLDRCQUE0QixDQUFDLENBQUM7U0FDNUM7YUFBTSxJQUFJLENBQUMsQ0FBQyxJQUFJLEtBQUssT0FBTyxJQUFJLENBQUMsQ0FBQyxPQUFPLEVBQUU7WUFDMUMsSUFBSSxDQUFDLGdCQUFnQixFQUFFLENBQUM7WUFDeEIsQ0FBQyxDQUFDLGNBQWMsRUFBRSxDQUFDO1lBQ25CLE9BQU8sQ0FBQyxJQUFJLENBQUMsNEJBQTRCLENBQUMsQ0FBQztTQUM1QztJQUNILENBQUM7SUFFRCx5QkFBeUI7UUFDeEIsTUFBTSxLQUFLLEdBQUssSUFBSSxDQUFDLE1BQWMsQ0FBQyxhQUFhLENBQUMsTUFBTSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFBO1FBQ3pHLE1BQU0sSUFBSSxHQUFHLFFBQVEsQ0FBQyxhQUFhLENBQUMsS0FBSyxDQUFtQixDQUFDO1FBQzdELElBQUksQ0FBQyxTQUFTLEdBQUcsc0JBQXNCLEdBQUcsS0FBSyxDQUFDO1FBRWhELE1BQU0sUUFBUSxHQUFHLFFBQVEsQ0FBQyxhQUFhLENBQUMsVUFBVSxDQUF3QixDQUFDO1FBQzNFLFFBQVEsQ0FBQyxZQUFZLENBQUMsaUJBQWlCLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFDN0MsUUFBUSxDQUFDLFNBQVMsR0FBRywwQkFBMEIsQ0FBQztRQUNoRCxRQUFRLENBQUMsU0FBUyxHQUFHLEVBQUUsQ0FBQztRQUN4QixRQUFRLENBQUMsS0FBSyxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7UUFDL0IsUUFBUSxDQUFDLEtBQUssQ0FBQyxLQUFLLEdBQUcsTUFBTSxDQUFDO1FBQzlCLFFBQVEsQ0FBQyxJQUFJLEdBQUcsTUFBTSxDQUFDO1FBQ3ZCLFFBQVEsQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDLHFCQUFxQixDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUUzRCxNQUFNLE9BQU8sR0FBRyxRQUFRLENBQUMsYUFBYSxDQUFDLFFBQVEsQ0FBc0IsQ0FBQztRQUN0RSxPQUFPLENBQUMsWUFBWSxDQUFDLGlCQUFpQixFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBQzVDLE9BQU8sQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsS0FBSyxJQUFJLDBCQUEwQixDQUFDO1FBQzlFLE9BQU8sQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsS0FBSyxJQUFJLGFBQWEsQ0FBQztRQUNqRSxPQUFPLENBQUMsT0FBTyxHQUFHLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7UUFFbkQsTUFBTSxNQUFNLEdBQUcsUUFBUSxDQUFDLGFBQWEsQ0FBQyxRQUFRLENBQXNCLENBQUM7UUFDckUsTUFBTSxDQUFDLFlBQVksQ0FBQyxpQkFBaUIsRUFBRSxFQUFFLENBQUMsQ0FBQztRQUMzQyxNQUFNLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsWUFBWSxDQUFDLEtBQUssSUFBSSw0QkFBNEIsQ0FBQztRQUNsRixNQUFNLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsWUFBWSxDQUFDLEtBQUssSUFBSSxRQUFRLENBQUM7UUFDOUQsTUFBTSxDQUFDLE9BQU8sR0FBRyxJQUFJLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUU5QyxJQUFJLENBQUMsV0FBVyxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQzNCLElBQUksQ0FBQyxXQUFXLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDekIsSUFBSSxDQUFDLFdBQVcsQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUUxQixPQUFPLEVBQUMsSUFBSSxFQUFFLE9BQU8sRUFBRSxNQUFNLEVBQUUsUUFBUSxFQUFDLENBQUM7SUFDM0MsQ0FBQztJQUVELHlCQUF5QjtRQUN2QixNQUFNLGNBQWMsR0FBRyxJQUFJLENBQUMsOEJBQThCLEVBQUUsQ0FBQztRQUM3RCxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUM7UUFFbkIsSUFBSSxDQUFDLG1CQUFtQixHQUFHO1lBQ3pCLG1CQUFtQixFQUFFLElBQUk7WUFDekIsS0FBSyxFQUFFLEdBQUcsRUFBRTtnQkFDVixPQUFPLHFCQUFxQixDQUFDO1lBQy9CLENBQUM7WUFDRCxVQUFVLEVBQUUsR0FBRyxFQUFFO2dCQUNmLE9BQU8sY0FBYyxDQUFDLElBQUksQ0FBQztZQUM3QixDQUFDO1lBQ0QsV0FBVyxFQUFFLEdBQUcsRUFBRTtnQkFDaEIsSUFDRSxLQUFLLENBQUMsYUFBYTtvQkFDbkIsS0FBSyxDQUFDLFVBQVUsSUFBSSxVQUFVLENBQUMsT0FBTztvQkFDdEMsQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLFFBQVEsRUFDdEI7b0JBQ0EsT0FBTzt3QkFDTCxRQUFRLEVBQUU7NEJBQ1IsVUFBVSxFQUFFLEtBQUssQ0FBQyxhQUFhLENBQUMsVUFBVSxHQUFHLENBQUM7NEJBQzlDLE1BQU0sRUFBRSxDQUFDO3lCQUNWO3dCQUNELFVBQVUsRUFBRSxDQUFDLGNBQWMsQ0FBQztxQkFDN0IsQ0FBQztpQkFDSDtZQUNILENBQUM7U0FDRixDQUFDO1FBRUYsSUFBSSxDQUFDLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQyxJQUFJLENBQUMsbUJBQW1CLENBQUMsQ0FBQztRQUN2RCxPQUFPLGNBQWMsQ0FBQztJQUN4QixDQUFDO0lBRUQsd0JBQXdCO1FBQ3RCLHdCQUF3QjtRQUN4QixNQUFNLGFBQWEsR0FBRyxJQUFJLENBQUMseUJBQXlCLEVBQUUsQ0FBQztRQUV2RCxJQUFJLENBQUMseUJBQXlCLEdBQUc7WUFDL0IsbUJBQW1CLEVBQUUsSUFBSTtZQUN6QixLQUFLLEVBQUUsR0FBRyxFQUFFO2dCQUNWLE9BQU8sb0JBQW9CLENBQUM7WUFDOUIsQ0FBQztZQUNELFVBQVUsRUFBRSxHQUFHLEVBQUU7Z0JBQ2YsT0FBTyxDQUFDLEdBQUcsQ0FBQyxZQUFZLENBQUMsQ0FBQztnQkFDMUIsT0FBTyxhQUFhLENBQUMsSUFBSSxDQUFDO1lBQzVCLENBQUM7WUFDRCxXQUFXLEVBQUUsR0FBRyxFQUFFO2dCQUNoQixJQUNFLElBQUksQ0FBQyxVQUFVLElBQUksVUFBVSxDQUFDLGFBQWE7b0JBQzNDLElBQUksQ0FBQyxVQUFVLElBQUksVUFBVSxDQUFDLFdBQVcsRUFDekM7b0JBQ0EsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxXQUFXLEVBQUUsQ0FBQztvQkFFM0MsT0FBTzt3QkFDTCxRQUFRLEVBQUU7NEJBQ1IsVUFBVSxFQUFFLElBQUksQ0FBQyxhQUFhO2dDQUM1QixDQUFDLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxVQUFVO2dDQUMvQixDQUFDLENBQUMsUUFBUSxDQUFDLFVBQVUsR0FBRyxDQUFDOzRCQUMzQixNQUFNLEVBQUUsUUFBUSxDQUFDLE1BQU07eUJBQ3hCO3dCQUNELFVBQVUsRUFBRSxDQUFDLGNBQWMsQ0FBQztxQkFDN0IsQ0FBQztpQkFDSDtZQUNILENBQUM7U0FDRixDQUFDO1FBRUYsSUFBSSxDQUFDLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQyxJQUFJLENBQUMseUJBQXlCLENBQUMsQ0FBQztRQUM3RCxPQUFPLGFBQWEsQ0FBQztJQUN2QixDQUFDO0lBRUQsZ0JBQWdCLENBQUMsT0FBc0I7UUFDckMsSUFBSSxDQUFDLE9BQU8sSUFBSSxPQUFPLENBQUMsS0FBSyxDQUFDLGtCQUFrQixFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBRTNELE1BQU0sc0JBQXNCLEdBQUcsRUFBRSxDQUFDO1FBQ2xDLElBQ0UsSUFBSSxDQUFDLGFBQWE7WUFDbEIsQ0FBQyxDQUFDLE9BQU8sSUFBSSxJQUFJLENBQUMsYUFBYSxDQUFDLFVBQVUsS0FBSyxPQUFPLENBQUMsVUFBVSxDQUFDLEVBQ2xFO1lBQ0Esc0JBQXNCLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxhQUFhLENBQUMsVUFBVSxDQUFDLENBQUM7U0FDNUQ7UUFDRCxJQUFJLE9BQU8sRUFBRTtZQUNYLHNCQUFzQixDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLENBQUM7U0FDakQ7UUFFRCxJQUFJLENBQUMsYUFBYSxHQUFHLE9BQU8sQ0FBQztRQUM3QixJQUFJLHNCQUFzQixDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUU7WUFDckMsSUFBSSxDQUFDLG9CQUFvQixDQUFDLHNCQUFzQixFQUFFLENBQUMsT0FBTyxFQUFFLEVBQUU7Z0JBQzVELElBQUksQ0FBQyxXQUFXLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQyxDQUFDLFlBQVk7b0JBQ3ZDLHdCQUF3QixDQUFDLEtBQUssQ0FBQztZQUNuQyxDQUFDLENBQUMsQ0FBQztTQUNKO0lBQ0gsQ0FBQztJQUVELG9CQUFvQixDQUNsQixXQUFxQixFQUNyQixFQUFzQztRQUV0QyxLQUFLLE1BQU0sRUFBRSxJQUFJLE1BQU0sQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxRQUFRLENBQUMsRUFBRTtZQUNuRCxJQUFJLFdBQVcsQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDLE9BQU8sQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRTtnQkFDbkQsRUFBRSxDQUFDLEVBQUUsQ0FBQyxPQUFPLENBQUMsQ0FBQzthQUNoQjtTQUNGO0lBQ0gsQ0FBQztJQUVELGVBQWUsQ0FBQyxFQUF5QztRQUN2RCxJQUFJLEVBQUUsQ0FBQyxNQUFNLElBQUksRUFBRSxDQUFDLE1BQU0sQ0FBQyxRQUFRLElBQUksRUFBRSxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsVUFBVSxFQUFFO1lBQ3BFLElBQUksQ0FBQywrQkFBK0IsR0FBRyxFQUFFLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxVQUFVLENBQUM7WUFDckUsSUFBSSxDQUFDLDhCQUE4QixDQUNqQyxJQUFJLENBQUMsTUFBTSxDQUFDLFFBQVEsS0FBSyxJQUFJO2dCQUMzQixDQUFDLENBQUMsSUFBSTtnQkFDTixDQUFDLENBQUMsSUFBSSxDQUFDLCtCQUErQixDQUN6QyxDQUFDO1NBQ0g7SUFDSCxDQUFDO0lBRUQsOEJBQThCLENBQUMsVUFBbUI7UUFDaEQsTUFBTSxLQUFLLEdBQUcsVUFBVTtZQUN0QixDQUFDLENBQUM7Z0JBQ0E7b0JBQ0UsS0FBSyxFQUFFLElBQUksWUFBWSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsVUFBVSxFQUFFLENBQUMsRUFBRSxVQUFVLEVBQUUsQ0FBQyxDQUFDO29CQUNsRSxPQUFPLEVBQUU7d0JBQ1AsZUFBZSxFQUFFLHVCQUF1Qjt3QkFDeEMsTUFBTSxFQUFFLEdBQUc7cUJBQ1o7aUJBQ0Y7YUFDRjtZQUNELENBQUMsQ0FBQyxFQUFFLENBQUM7UUFDUCxJQUFJLENBQUMsc0JBQXNCLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FDeEQsSUFBSSxDQUFDLHNCQUFzQixFQUMzQixLQUFLLENBQ04sQ0FBQztJQUNKLENBQUM7SUFFRCxlQUFlLENBQUMsRUFLZjtRQUNDLGtGQUFrRjtRQUNsRixJQUNFLEVBQUUsQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLFNBQVM7WUFDM0IsRUFBRSxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyx1QkFBdUIsQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUNqRTtZQUNBLElBQUksQ0FBQyxNQUFNLENBQUMsV0FBVyxDQUFDO2dCQUN0QixVQUFVLEVBQUUsSUFBSSxDQUFDLCtCQUErQjtnQkFDaEQsTUFBTSxFQUFFLENBQUM7YUFDVixDQUFDLENBQUM7WUFDSCxJQUFJLENBQUMsYUFBYSxDQUFDLFVBQVUsQ0FBQyxhQUFhLEVBQUUsY0FBYyxDQUFDLENBQUM7U0FDOUQ7YUFBTSxJQUFJLENBQUMsRUFBRSxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsWUFBWSxDQUFDLGlCQUFpQixDQUFDLEVBQUU7WUFDN0QsSUFBSSxhQUFhLEdBQWtCLElBQUksQ0FBQztZQUV4QyxJQUFJLEVBQUUsQ0FBQyxNQUFNLENBQUMsTUFBTSxJQUFJLEVBQUUsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLFVBQVUsS0FBSyxJQUFJLEVBQUU7Z0JBQzVELEtBQUssTUFBTSxPQUFPLElBQUksTUFBTSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLFFBQVEsQ0FBQyxDQUFDLEdBQUcsQ0FDMUQsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQ2pCLEVBQUU7b0JBQ0QsTUFBTSxDQUFDLEdBQUcsSUFBSSxDQUFDLGNBQWMsQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDLENBQUM7b0JBQzFDLElBQUksQ0FBQyxDQUFDLFVBQVUsSUFBSSxFQUFFLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxVQUFVLEVBQUU7d0JBQy9DLGFBQWEsR0FBRyxPQUFPLENBQUM7d0JBQ3hCLE1BQU07cUJBQ1A7aUJBQ0Y7YUFDRjtZQUNELElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxhQUFhLENBQUMsQ0FBQztZQUNyQyxJQUFJLENBQUMsZUFBZSxFQUFFLENBQUM7WUFDdkIsSUFBSSxDQUFDLGFBQWEsQ0FBQyxVQUFVLENBQUMsT0FBTyxFQUFFLGNBQWMsQ0FBQyxDQUFDO1NBQ3hEO0lBQ0gsQ0FBQztJQUVPLHdCQUF3QixDQUM5QiwwQkFBbUM7UUFFbkMsSUFBSSxLQUFLLEdBQUcsQ0FBQyxDQUFDO1FBQ2QsSUFBSSxTQUFTLEdBQUcsQ0FBQyxDQUFDO1FBQ2xCLE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxZQUFZLENBQUMsVUFBVSxDQUFDO1FBRWhELElBQUksSUFBSSxDQUFDLGFBQWEsRUFBRTtZQUN0QixLQUFLLElBQUksSUFBSSxJQUFJLElBQUksQ0FBQyxlQUFlLEVBQUUsRUFBRTtnQkFDdkMsSUFDRSxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxVQUFVLEtBQUssSUFBSSxDQUFDLGFBQWEsQ0FBQyxVQUFVO29CQUMvRCxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxJQUFJLElBQUksQ0FBQyxhQUFhO3dCQUN2QywwQkFBMEIsQ0FBQyxFQUM3QjtvQkFDQSxLQUFLLElBQUksSUFBSSxDQUFDLHNCQUFzQixDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDO2lCQUMvRDtnQkFFRCxJQUFJLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxJQUFJLElBQUksQ0FBQyxhQUFhLEVBQUU7b0JBQzVDLE1BQU07aUJBQ1A7YUFDRjtZQUNELFNBQVMsR0FBRyxLQUFLLEdBQUcsVUFBVSxDQUFDO1NBQ2hDO1FBQ0QsT0FBTyxTQUFTLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxjQUFjLENBQUM7SUFDaEQsQ0FBQztJQUVELG1CQUFtQjtRQUNqQixJQUFJLENBQUMscUJBQXFCLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxlQUFlLEdBQUcsSUFBSSxDQUFDLGNBQWMsQ0FBQyxtQkFBbUIsQ0FBQyxDQUFDO1FBQ2pHLElBQUksQ0FBQyxxQkFBcUIsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLFNBQVMsR0FBRyxHQUFHLElBQUksQ0FBQyx3QkFBd0IsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDO1FBRTlGLElBQUksSUFBSSxDQUFDLHFCQUFxQixDQUFDLE1BQU0sRUFBRTtZQUNyQyxNQUFNLFdBQVcsR0FDZixJQUFJLENBQUMsYUFBYTtnQkFDbEIsSUFBSSxDQUFDLGVBQWUsQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxFQUFFLEtBQUssSUFBSSxDQUFDLGFBQWEsQ0FBQyxFQUFFLENBQUMsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDO1lBQ2pGLE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxhQUFhLElBQUksSUFBSSxDQUFDLGFBQWEsQ0FBQyxNQUFNLEtBQUssSUFBSSxDQUFDLFdBQVcsQ0FBQztZQUN4RixJQUFJLENBQUMscUJBQXFCLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxPQUFPLEdBQUcsV0FBVyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztZQUM1RSxJQUFJLENBQUMscUJBQXFCLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLEdBQUcsV0FBVyxJQUFJLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztTQUMxRjtRQUVELElBQUksQ0FBQyxNQUFNLENBQUMsbUJBQW1CLENBQUMsSUFBSSxDQUFDLG1CQUFtQixDQUFDLENBQUM7SUFDNUQsQ0FBQztJQUVELHlCQUF5QjtRQUN2QixDQUFDLElBQUksQ0FBQyxjQUFjLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxjQUFjLENBQUMsUUFBUSxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUU7WUFDckUsQ0FBQyxDQUFDLEtBQUssQ0FBQyxlQUFlLEdBQUcsSUFBSSxDQUFDLGNBQWMsQ0FBQyxtQkFBbUIsQ0FBQyxDQUFDO1lBQ25FLENBQUMsQ0FBQyxLQUFLLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQyxjQUFjLENBQUMsbUJBQW1CLENBQUMsQ0FBQztRQUMzRCxDQUFDLENBQUMsQ0FBQztRQUVILElBQUksQ0FBQyxjQUFjLENBQUMsT0FBTyxDQUFDLFNBQVM7WUFDbkMsSUFBSSxDQUFDLFVBQVUsS0FBSyxVQUFVLENBQUMsYUFBYTtnQkFDMUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLEtBQUssSUFBSSxhQUFhO2dCQUM5QyxDQUFDLENBQUMsY0FBYyxDQUFDO1FBQ3JCLCtFQUErRTtRQUMvRSxTQUFTO1FBQ1QsU0FBUztRQUNULElBQUksQ0FBQyxNQUFNLENBQUMsbUJBQW1CLENBQUMs