@senx/warpview-editor
Version:
WarpView Editor Elements
697 lines • 108 kB
JavaScript
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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQ29kZVJldmlldy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL3dhcnB2aWV3LWVkaXRvci1uZy9zcmMvbGliL2VsZW1lbnRzL3dhcnAtdmlldy1lZGl0b3IvcHJvdmlkZXJzL0NvZGVSZXZpZXcudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBZ0JBLE9BQU8sRUFFTCxjQUFjLEVBRWQsY0FBYyxFQUdkLHdCQUF3QixFQUV4QixtQkFBbUIsR0FDcEIsTUFBTSw0QkFBNEIsQ0FBQztBQUNwQyxPQUFPLEtBQUssSUFBSSxNQUFNLE1BQU0sQ0FBQztBQUU3QixPQUFPLEVBQXlDLGNBQWMsRUFBQyxDQUFDO0FBTWhFLE1BQU0sWUFBWSxHQUFJLE1BQThCLENBQUM7QUFFckQsSUFBSyxtQkFHSjtBQUhELFdBQUssbUJBQW1CO0lBQ3RCLDZEQUFRLENBQUE7SUFDUiw2REFBUSxDQUFBO0FBQ1YsQ0FBQyxFQUhJLG1CQUFtQixLQUFuQixtQkFBbUIsUUFHdkI7QUFFRCxNQUFNLENBQU4sSUFBWSxVQUlYO0FBSkQsV0FBWSxVQUFVO0lBQ3BCLDZEQUFpQixDQUFBO0lBQ2pCLHlEQUFlLENBQUE7SUFDZixpREFBVyxDQUFBO0FBQ2IsQ0FBQyxFQUpXLFVBQVUsS0FBVixVQUFVLFFBSXJCO0FBRUQsTUFBTSxVQUFVLG1CQUFtQixDQUNqQyxNQUFXLEVBQ1gsV0FBbUIsRUFDbkIsT0FBOEIsRUFDOUIsUUFBMkIsRUFDM0IsTUFBNEIsRUFDNUIsT0FBaUI7SUFFakIsNkNBQTZDO0lBQzdDLE1BQU0sRUFBRSxHQUFHLElBQUksYUFBYSxDQUFDLE1BQU0sRUFBRSxXQUFXLEVBQUUsUUFBUSxFQUFFLE1BQU0sRUFBRSxPQUFPLENBQUMsQ0FBQztJQUM3RSxFQUFFLENBQUMsSUFBSSxDQUFDLE9BQU8sSUFBSSxFQUFFLENBQUMsQ0FBQztJQUN2QixPQUFPLEVBQUUsQ0FBQztBQUNaLENBQUM7QUFnRkQsTUFB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