ngx-explorer-dnd
Version:
## _Drag & Drop in Angular like in a desktop explorer!_
643 lines • 93.2 kB
JavaScript
import { DOCUMENT } from '@angular/common';
import { Directive, EventEmitter, Inject, InjectionToken, Input, Output, } from '@angular/core';
import { extendStyles } from '../core/css-helper';
import { DragRowPosition, } from '../core/utils';
import * as i0 from "@angular/core";
import * as i1 from "../services/dnd.service";
export const EXPLORER_DND_CONTAINER = new InjectionToken('EXPLORER_DND_CONTAINER');
export class NgxExplorerContainerDirective {
//#endregion
constructor(_ngZone, _document, dndService) {
this._ngZone = _ngZone;
this._document = _document;
this.dndService = dndService;
/** The registered elements as array from type `NgxExplorerItemDirective`. */
this._dragElements = [];
/** If drag startet. */
this.dragStarted = false;
/** Check if the current element can be moved with the intern `isMinRangeMoved` method. */
this.canMove = false;
/** A unsorted list of all registered components */
this.unsortedItems = [];
/** A sorted list of all registered components (sorted by position in the DOM) */
this.sortedItems = [];
/** Event emitted when the drag progress was started. */
this.dragInProgress = new EventEmitter();
/** Event emitted when the files are dropped with `mouseup`. */
this.drop = new EventEmitter();
/** Event emitted when a target under the mouse will change */
this.targetChange = new EventEmitter();
/** Cancel move back animation after `mouseup` event */
this.cancelAnimation = false;
/** Can the file/folder components are sorted */
this.sortingEnabled = false;
this.onMouseMove = (event) => {
if (this.dragStarted) {
if (this.isMinRangeMoved(event.x, event.y)) {
this.canMove = true;
const htmlElement = this._currentDragElement?.htmlElement;
if (htmlElement)
this.toggleVisibility(htmlElement, true);
}
if (!this.canMove)
return;
if (this.sortingEnabled) {
this.setCurrentPositionOfDraggedElement(this._currentDragElement?.htmlElement, { x: event.x, y: event.y });
}
this.setPreviewElementPosition(event.x, event.y);
}
event.stopPropagation();
event.preventDefault();
};
this.onMouseUp = (event) => {
this.dragStarted = false;
this.canMove = false;
let indexData;
if (this.previewElement) {
if (this._currentDragElement && this.sortingEnabled) {
indexData = this.getCurrentIndexPosition(event.x, event.y, this._currentDragElement?.htmlElement);
this.positionDifference = { left: indexData?.x, top: indexData?.y };
}
this.previewElement.style.transition = '0.2s ease-in-out';
let posX = (+this.previewElement.style.left.replace('px', '').trim() -
(this.positionDifference
? this.positionDifference.left
: this.position.left)) *
-1;
let posY = (+this.previewElement.style.top.replace('px', '').trim() -
(this.positionDifference
? this.positionDifference.top
: this.position.top)) *
-1;
this.previewElement.style.transform =
'translate(' + posX + 'px, ' + posY + 'px)';
event.preventDefault();
event.stopPropagation();
setTimeout(() => {
this.drop.emit({
item: this._currentDragElement?.data,
target: this._currentTargetElement?.data,
optionalDragData: this.dragData,
oldIndex: indexData ? indexData.oldIndex : null,
newIndex: indexData ? indexData.newIndex : null,
});
// Clear positionDifference
this.positionDifference = null;
if (this.previewElement)
this._document.body.removeChild(this.previewElement);
this.previewElement = null;
const htmlElement = this._currentDragElement?.htmlElement;
if (htmlElement) {
this.toggleVisibility(htmlElement, false);
}
this.resetPositions(this._currentDragElement?.htmlElement);
this.dndService.setCurrentDragElement(null);
this._currentDragElement = null;
this._currentTargetElement = null;
}, this.cancelAnimation ? 0 : 201);
this.dragInProgress.emit(false);
}
};
}
//#region Helper
/** Deep clone the current element for a preview element. */
deepCloneNode(node, badge) {
const clone = node.cloneNode(true);
if (badge) {
const badgeElement = document.createElement('div');
badgeElement.innerHTML = badge;
extendStyles(badgeElement.style, {
margin: '0',
position: 'absolute',
top: '0',
right: '0',
'text-align': 'center',
'border-style': 'solid',
'border-radius': '50%',
'border-width': '0',
'background-color': 'red',
width: '20px',
height: '20px',
color: 'white',
});
clone.appendChild(badgeElement);
}
const descendantsWithId = clone.querySelectorAll('[id]');
// Remove the `id` to avoid having multiple elements with the same id on the page.
clone.removeAttribute('id');
for (let i = 0; i < descendantsWithId.length; i++) {
descendantsWithId[i].removeAttribute('id');
}
return clone;
}
/** Hide/show the dragged component */
toggleVisibility(element, visible) {
extendStyles(element.style, {
opacity: visible ? '0' : '',
// position: visible ? 'fixed' : '',
// left: visible ? '0' : '',
// top: visible ? '-500em' : '',
});
}
/** Creates a list with all registered components and their positions */
createUnsortedList() {
const unsortedItems = [];
// All registered components
for (let data of this._dragElements) {
const clientRect = data.htmlElement.getBoundingClientRect();
const posData = {
x: clientRect.x,
y: clientRect.y,
width: clientRect.width,
height: clientRect.height,
};
unsortedItems.push({ htmlElement: data.htmlElement, posData });
}
return unsortedItems;
}
/** Gets the registered items in the list, sorted by their position in the DOM. */
getSortedItems() {
return Array.from(this.createUnsortedList()).sort((a, b) => {
const documentPosition = a.htmlElement.compareDocumentPosition(b.htmlElement);
// `compareDocumentPosition` returns a bitmask so we have to use a bitwise operator.
// https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition
// tslint:disable-next-line:no-bitwise
return documentPosition & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1;
});
}
/** Check if the element has a translate3d style and returns the "real" values of x, y, width and height. */
getCurrentPositionInclusiveTranslate3d(element) {
const rect = element.getBoundingClientRect();
let translateX = 0, translateY = 0;
if (element.style.transform) {
translateX =
+element.style.transform
.replace(/translate3d|px|\(|\)/gi, '')
.split(',')[0] || 0;
translateY =
+element.style.transform
.replace(/translate3d|px|\(|\)/gi, '')
.split(',')[1] || 0;
}
return {
startX: rect.x - translateX,
startY: rect.y - translateY,
x: rect.x,
y: rect.y,
width: rect.width,
height: rect.height,
halfWidthX: rect.x + rect.width / 2,
halfHeightY: rect.y + rect.height / 2,
};
}
/** Checks goes the new position of the element outside of the parent and set new row position */
needToMoveUpOrDown(parentRect, indexOfElement, addDirection) {
let transX = '0px', transY = '0px';
let posChanged = false;
const currentElement = this.sortedItems[indexOfElement];
if (currentElement.posData.x + addDirection < parentRect.x &&
indexOfElement > 0) {
transX =
this.sortedItems[indexOfElement - 1].posData.x -
this.sortedItems[indexOfElement].posData.x +
'px';
transY =
this.sortedItems[indexOfElement - 1].posData.y -
this.sortedItems[indexOfElement].posData.y +
'px';
posChanged = true;
}
else if (currentElement.posData.x + currentElement.posData.width + addDirection >
parentRect.x + parentRect.width) {
transX =
this.sortedItems[indexOfElement + 1].posData.x -
this.sortedItems[indexOfElement].posData.x +
'px';
transY =
this.sortedItems[indexOfElement + 1].posData.y -
this.sortedItems[indexOfElement].posData.y +
'px';
posChanged = true;
}
return posChanged ? { transX, transY } : false;
}
/** Set the position of the dragged element */
setCurrentPositionOfDraggedElement(draggedElement, mousePosition) {
if (!draggedElement)
return;
const parentRect = draggedElement.parentElement?.getBoundingClientRect();
if (!parentRect)
return;
// Intern helpers ------------------------------------------
/** If the cursor moving in same row like the dragged element */
const isInSameLine = () => {
const el = draggedElement.getBoundingClientRect();
if (mousePosition.y >= el.y && mousePosition.y <= el.y + el.height)
return {
sameRow: true,
dragRowPosition: DragRowPosition.SAME,
sameLineAsMouse: DragRowPosition.UNDEFINED,
};
else if (mousePosition.y < el.y)
return {
sameRow: false,
dragRowPosition: DragRowPosition.SMALLER,
sameLineAsMouse: DragRowPosition.UNDEFINED,
};
else
return {
sameRow: false,
dragRowPosition: DragRowPosition.GREATER,
sameLineAsMouse: DragRowPosition.UNDEFINED,
};
};
/** If the element is in the same line as the dragged element */
const amIAtTheSameRow = (y, height) => {
const el = draggedElement.getBoundingClientRect();
let mouseRowPosition = DragRowPosition.UNDEFINED;
if (height) {
if (mousePosition.y > y + height)
mouseRowPosition = DragRowPosition.SMALLER;
else if (mousePosition.y < y)
mouseRowPosition = DragRowPosition.GREATER;
else if (mousePosition.y >= y && mousePosition.y <= y + height)
mouseRowPosition = DragRowPosition.SAME;
}
if (y === el.y) {
return {
sameRow: true,
dragRowPosition: DragRowPosition.SAME,
sameLineAsMouse: mouseRowPosition,
};
}
else if (y > el.y) {
return {
sameRow: false,
dragRowPosition: DragRowPosition.GREATER,
sameLineAsMouse: mouseRowPosition,
};
}
else {
return {
sameRow: false,
dragRowPosition: DragRowPosition.SMALLER,
sameLineAsMouse: mouseRowPosition,
};
}
};
// Mouse is right to new elmement
const isRightToElement = (halfWidth) => {
if (mousePosition.x > halfWidth)
return true;
return false;
};
// Mouse is left to new element
const isLeftToElement = (halfWidth) => {
if (mousePosition.x < halfWidth)
return true;
return false;
};
/** If the start position of the current element before `true`or after `false` the dragged element */
const posIsBeforeDraggedElement = (x, y) => {
const el = draggedElement.getBoundingClientRect();
if (y) {
if (y < el.y)
return true;
if (y > el.y)
return false;
if (x < el.x && y === el.y)
return true;
return false;
}
if (x < el.x)
return true;
return false;
};
// ---------------------------------------------------------
// Only if the mouse is inside the parent
if (mousePosition.x >= parentRect.x &&
mousePosition.x <= parentRect.x + parentRect?.width &&
mousePosition.y >= parentRect.y &&
mousePosition.y <= parentRect.y + parentRect?.height) {
const draggedRect = draggedElement.getBoundingClientRect();
this.positionDifference = { ...this.position };
let index = 0; // Index of the current unsorted item
for (let element of this.sortedItems) {
if (element.htmlElement !== draggedElement) {
const rect = this.getCurrentPositionInclusiveTranslate3d(element.htmlElement);
// if (
// element.htmlElement.getAttribute('ng-reflect-file-name') &&
// element.htmlElement.getAttribute('ng-reflect-file-name') ===
// 'File 6'
// ) {
//console.log(rect.x, rect.startX, rect.startY);
// console.log(
// mousePosition.x,
// rect.halfWidthX,
// rect.startX,
// mousePosition.y,
// rect.startY,
// rect.startY + rect.height
// );
// }
let translateXValue = '0px', translateYValue = '0px';
// Part: in same line
// Are the current elements all in the same line as the dragged element, handle it here
if (isInSameLine().sameRow &&
amIAtTheSameRow(rect.y).sameRow &&
isLeftToElement(rect.halfWidthX) &&
posIsBeforeDraggedElement(rect.startX)) {
translateXValue = draggedRect.width + 'px';
}
else if (isInSameLine().sameRow &&
amIAtTheSameRow(rect.y).sameRow &&
isRightToElement(rect.halfWidthX) &&
!posIsBeforeDraggedElement(rect.startX)) {
translateXValue = -draggedRect.width + 'px';
}
// Part: in other line
// Are the current elements are greater or smaller than the dragged element, handle it here
if (!isInSameLine().sameRow) {
// Check all elements their position are smaller then the current
if (isInSameLine().dragRowPosition == DragRowPosition.GREATER) {
// Mouse goes under the dragged element; all elements they are greater as dragged element, but smaller/same as mouse will be handled here
if ((amIAtTheSameRow(rect.y).dragRowPosition ===
DragRowPosition.GREATER ||
amIAtTheSameRow(rect.y).dragRowPosition ===
DragRowPosition.SAME) &&
(amIAtTheSameRow(rect.y, rect.height).sameLineAsMouse ===
DragRowPosition.SAME ||
amIAtTheSameRow(rect.y, rect.height).sameLineAsMouse ===
DragRowPosition.SMALLER) &&
!posIsBeforeDraggedElement(rect.startX, rect.startY)) {
if (isRightToElement(rect.halfWidthX) ||
amIAtTheSameRow(rect.y).dragRowPosition ==
DragRowPosition.SAME ||
(amIAtTheSameRow(rect.y).dragRowPosition ==
DragRowPosition.GREATER &&
amIAtTheSameRow(rect.y, rect.height).sameLineAsMouse !==
DragRowPosition.SAME)) {
translateXValue = -draggedRect.width + 'px';
}
}
}
else if (isInSameLine().dragRowPosition == DragRowPosition.SMALLER) {
if ((amIAtTheSameRow(rect.y).dragRowPosition ===
DragRowPosition.SMALLER ||
amIAtTheSameRow(rect.y).dragRowPosition ===
DragRowPosition.SAME) &&
(amIAtTheSameRow(rect.y, rect.height).sameLineAsMouse ===
DragRowPosition.SAME ||
amIAtTheSameRow(rect.y, rect.height).sameLineAsMouse ===
DragRowPosition.GREATER) &&
posIsBeforeDraggedElement(rect.startX, rect.startY)) {
if (isLeftToElement(rect.halfWidthX) ||
amIAtTheSameRow(rect.y).dragRowPosition ==
DragRowPosition.SAME ||
(amIAtTheSameRow(rect.y).dragRowPosition ==
DragRowPosition.SMALLER &&
amIAtTheSameRow(rect.y, rect.height).sameLineAsMouse !==
DragRowPosition.SAME)) {
translateXValue = draggedRect.width + 'px';
}
}
}
}
// Check goes any element outside the parent. So change it's row position
const checkNeedToMoveUpOrDown = this.needToMoveUpOrDown(parentRect, index, +translateXValue.replace('px', ''));
if (checkNeedToMoveUpOrDown) {
translateXValue = (checkNeedToMoveUpOrDown).transX;
translateYValue = (checkNeedToMoveUpOrDown).transY;
}
extendStyles(element.htmlElement.style, {
transform: `translate3d(${translateXValue},${translateYValue},0)`,
});
}
index++;
}
}
}
/** Removes the transform3d positions from all elements */
resetPositions(draggedElement) {
if (!draggedElement)
return;
for (let element of this.unsortedItems) {
if (element.htmlElement !== draggedElement) {
extendStyles(element.htmlElement.style, {
transform: '',
});
}
}
}
/** Set the position of the preview element */
setPreviewElementPosition(posX, posY) {
if (this.previewElement) {
if (this.previewElement.style.display === 'none') {
this.previewElement.style.display = 'unset';
}
this.previewElement.style.left =
posX - this.previewElement.clientWidth / 2 + 'px';
this.previewElement.style.top =
posY - this.previewElement.clientHeight / 2 + 'px';
}
}
/** Dragging start if the pressed mouse moved the min range. */
isMinRangeMoved(posX, posY) {
// Standard is 8px
if (this.previewElement) {
if (this.startPosition.left - 7 > posX ||
this.startPosition.left + 7 < posX ||
this.startPosition.top - 7 > posY ||
this.startPosition.top + 7 < posY) {
return true;
}
}
return false;
}
/** Check if element is inside rect. */
checkIfElementInsideRect(rect, selectionBorder) {
if (((selectionBorder.x >= rect.x &&
selectionBorder.x <= rect.x + rect.width) ||
(selectionBorder.x <= rect.x &&
selectionBorder.x + selectionBorder.width >= rect.x)) &&
((selectionBorder.y >= rect.y &&
selectionBorder.y <= rect.y + rect.height) ||
(selectionBorder.y <= rect.y &&
selectionBorder.y + selectionBorder.height >= rect.y))) {
return true;
}
else {
return false;
}
}
/** Get all elements of type `NgxExplorerItemDirective` from an event. */
getTargetHandle(event) {
return this._dragElements.find((handle) => {
return (event.target &&
(event.target === handle.htmlElement ||
handle.htmlElement.contains(event.target)));
});
}
/** Get all elements of type `NgxExplorerItemDirective` from a HTMLElement. */
getTargetHandleFromHTMLElement(element) {
return this._dragElements.find((handle) => {
return (element === handle.htmlElement || element.contains(element));
})?.htmlElement;
}
/** Get the current index of the dragged element in the unsorted list */
getCurrentIndexPosition(lastMouseX, lastMouseY, draggedElement) {
const parentRect = draggedElement.parentElement?.getBoundingClientRect();
let currentPosition = undefined;
if (parentRect) {
let newIndex = 0, oldIndex = 0;
for (let elIndex = 0; elIndex < this.sortedItems.length; elIndex++) {
if (this.sortedItems[elIndex].htmlElement === draggedElement) {
oldIndex = elIndex;
break;
}
}
for (let el of this.sortedItems) {
if (el.htmlElement !== draggedElement) {
// Mouse has position of Object
if (lastMouseX >= el.posData.x &&
lastMouseX <= el.posData.x + el.posData.width &&
lastMouseY >= el.posData.y &&
lastMouseY <= el.posData.y + el.posData.height) {
currentPosition = {
x: el.posData.x,
y: el.posData.y,
oldIndex,
newIndex,
};
break;
}
}
newIndex++;
}
}
return currentPosition;
}
/** Returns a list of all elements of type `NgxExplorerItemDirective` they are inside a specific rect. */
getElementsInsideSelectionDiv(x, y, width, height) {
const result = [];
this._dragElements.forEach((data) => {
const rect = data.htmlElement.getBoundingClientRect();
if (this.checkIfElementInsideRect(rect, {
x,
y,
width,
height,
})) {
result.push(data);
}
});
return result;
}
/** Initialize all event listener. */
initAll() {
// Use arrow functions to use "this" in onMouseDown (as example) or use .....bind(this)
// We can use @HostListener instead.
this._document.addEventListener('mousedown', this.onMouseDown.bind(this));
this._document.addEventListener('mousemove', this.onMouseMove);
this._document.addEventListener('mouseup', this.onMouseUp);
return this;
}
/** Remove all event listener. */
clearAll() {
this._document.removeEventListener('mousedown', this.onMouseDown);
this._document.removeEventListener('mousemove', this.onMouseMove);
this._document.removeEventListener('mouseup', this.onMouseUp);
}
ngOnInit() {
this.initAll();
}
ngOnDestroy() {
this.clearAll();
}
onMouseDown(event) {
if (event.button !== 0) {
event.preventDefault();
return;
}
// List of the current registered elements
if (this.sortingEnabled) {
this.unsortedItems = this.createUnsortedList();
this.sortedItems = this.getSortedItems();
}
this.dragStarted = true;
this._currentDragElement = this.getTargetHandle(event) || null;
const htmlElement = this._currentDragElement?.htmlElement;
if (htmlElement) {
this.previewElement = this.deepCloneNode(htmlElement, this.badge);
this.position = {
left: htmlElement.getBoundingClientRect().left,
top: htmlElement.getBoundingClientRect().top,
};
this.startPosition = {
left: event.x,
top: event.y,
};
extendStyles(this.previewElement.style, {
'pointer-events': 'none',
margin: '0',
position: 'fixed',
top: '0',
left: '0',
'z-index': '1000',
display: 'none',
}, new Set(['position']));
this._document.body.appendChild(this.previewElement);
this.dndService.setCurrentDragElement(htmlElement);
event.stopPropagation();
event.preventDefault();
this.dragInProgress.emit(true);
}
}
//#region Public Voids
/** `ngx-explorer-item` will call this method to register itself. */
addDragElement(element) {
this._dragElements.push(element);
}
/** `ngx-explorer-target` will call this method to register itself as target on mouseenter */
setCurrentTarget(element) {
this._currentTargetElement = element;
this.targetChange.emit({ target: element?.getHostComponent() });
}
}
NgxExplorerContainerDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0, type: NgxExplorerContainerDirective, deps: [{ token: i0.NgZone }, { token: DOCUMENT }, { token: i1.DndService }], target: i0.ɵɵFactoryTarget.Directive });
NgxExplorerContainerDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.0.1", type: NgxExplorerContainerDirective, selector: "[ngxExplorerDndContainer]", inputs: { dragData: "dragData", badge: "badge", cancelAnimation: "cancelAnimation", sortingEnabled: "sortingEnabled" }, outputs: { dragInProgress: "dragInProgress", drop: "drop", targetChange: "targetChange" }, providers: [
{
provide: EXPLORER_DND_CONTAINER,
useExisting: NgxExplorerContainerDirective,
},
], ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0, type: NgxExplorerContainerDirective, decorators: [{
type: Directive,
args: [{
selector: '[ngxExplorerDndContainer]',
providers: [
{
provide: EXPLORER_DND_CONTAINER,
useExisting: NgxExplorerContainerDirective,
},
],
}]
}], ctorParameters: function () { return [{ type: i0.NgZone }, { type: Document, decorators: [{
type: Inject,
args: [DOCUMENT]
}] }, { type: i1.DndService }]; }, propDecorators: { dragInProgress: [{
type: Output
}], drop: [{
type: Output
}], targetChange: [{
type: Output
}], dragData: [{
type: Input
}], badge: [{
type: Input
}], cancelAnimation: [{
type: Input
}], sortingEnabled: [{
type: Input
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmd4LWV4cGxvcmVyLWNvbnRhaW5lci5kaXJlY3RpdmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9uZ3gtZXhwbG9yZXItZG5kL3NyYy9saWIvZGlyZWN0aXZlcy9uZ3gtZXhwbG9yZXItY29udGFpbmVyLmRpcmVjdGl2ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDM0MsT0FBTyxFQUNMLFNBQVMsRUFDVCxZQUFZLEVBQ1osTUFBTSxFQUNOLGNBQWMsRUFDZCxLQUFLLEVBSUwsTUFBTSxHQUNQLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUNsRCxPQUFPLEVBQ0wsZUFBZSxHQUdoQixNQUFNLGVBQWUsQ0FBQzs7O0FBS3ZCLE1BQU0sQ0FBQyxNQUFNLHNCQUFzQixHQUNqQyxJQUFJLGNBQWMsQ0FBZ0Msd0JBQXdCLENBQUMsQ0FBQztBQVc5RSxNQUFNLE9BQU8sNkJBQTZCO0lBb25CeEMsWUFBWTtJQUVaLFlBQ1UsT0FBZSxFQUNHLFNBQW1CLEVBQ3JDLFVBQXNCO1FBRnRCLFlBQU8sR0FBUCxPQUFPLENBQVE7UUFDRyxjQUFTLEdBQVQsU0FBUyxDQUFVO1FBQ3JDLGVBQVUsR0FBVixVQUFVLENBQVk7UUFobkJoQyw2RUFBNkU7UUFDckUsa0JBQWEsR0FBK0IsRUFBRSxDQUFDO1FBS3ZELHVCQUF1QjtRQUNmLGdCQUFXLEdBQUcsS0FBSyxDQUFDO1FBRTVCLDBGQUEwRjtRQUNsRixZQUFPLEdBQUcsS0FBSyxDQUFDO1FBV3hCLG1EQUFtRDtRQUMzQyxrQkFBYSxHQUdmLEVBQUUsQ0FBQztRQUVULGlGQUFpRjtRQUN6RSxnQkFBVyxHQUdiLEVBQUUsQ0FBQztRQUVULHdEQUF3RDtRQUM5QyxtQkFBYyxHQUEwQixJQUFJLFlBQVksRUFBVyxDQUFDO1FBRTlF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