ngx-diagrams
Version:
> Light Weight, Modular, Typed Diagram Engine for Angular
567 lines • 82.7 kB
JavaScript
import { __decorate, __metadata } from "tslib";
import { DOCUMENT } from '@angular/common';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, Inject, Input, NgZone, Output, Renderer2, ViewChild, ViewContainerRef } from '@angular/core';
import { BehaviorSubject, combineLatest, fromEvent, merge, ReplaySubject } from 'rxjs';
import { filter, take, takeUntil, tap } from 'rxjs/operators';
import { InvalidLinkDestroyed, LinkCreatedAction, MoveCanvasAction, SelectingAction } from '../../actions';
import { LooseLinkDestroyed } from '../../actions/loose-link-destroyed.action';
import { MoveItemsAction } from '../../actions/move-items.action';
import { DiagramModel } from '../../models/diagram.model';
import { NodeModel } from '../../models/node.model';
import { PointModel } from '../../models/point.model';
import { PortModel } from '../../models/port.model';
import { OutsideZone } from '../../utils';
export class NgxDiagramComponent {
constructor(document, ngZone, renderer, cdRef, elRef) {
this.document = document;
this.ngZone = ngZone;
this.renderer = renderer;
this.cdRef = cdRef;
this.elRef = elRef;
this.allowCanvasZoom = true;
this.allowCanvasTranslation = true;
this.inverseZoom = true;
this.allowLooseLinks = true;
this.maxZoomOut = null;
this.maxZoomIn = null;
this.portMagneticRadius = 30;
this.smartRouting = false;
this.actionStartedFiring = new EventEmitter();
this.actionStillFiring = new EventEmitter();
this.actionStoppedFiring = new EventEmitter();
this.action$ = new BehaviorSubject(null);
this.nodesRendered$ = new BehaviorSubject(false);
this.destroyed$ = new ReplaySubject(1);
}
get host() {
return this.elRef.nativeElement;
}
// TODO: handle destruction of container, resetting all observables to avoid memory leaks!
ngAfterViewInit() {
if (this.diagramModel) {
this.initNodes();
this.initLinks();
this.initSubs();
}
}
ngOnDestroy() {
this.destroyed$.next(true);
this.destroyed$.complete();
}
/**
* fire the action registered and notify subscribers
*/
fireAction() {
if (this.action$.value) {
this.actionStillFiring.emit(this.action$.value);
}
}
/**
* Unregister the action, post firing and notify subscribers
*/
stopFiringAction(shouldSkipEvent) {
if (!shouldSkipEvent) {
this.actionStoppedFiring.emit(this.action$.value);
}
this.action$.next(null);
}
/**
* Register the new action, pre firing and notify subscribers
*/
startFiringAction(action) {
this.action$.next(action);
this.actionStartedFiring.emit(action);
}
selectAction() {
return this.action$;
}
shouldDrawSelectionBox() {
const action = this.action$.getValue();
if (action instanceof SelectingAction) {
action.getBoxDimensions();
return true;
}
return false;
}
getMouseElement(event) {
const target = event.target;
// is it a port?
let element = target.closest('[data-portid]');
if (element) {
// get the relevant node and return the port.
const nodeEl = target.closest('[data-nodeid]');
return {
model: this.diagramModel
.getNode(nodeEl.getAttribute('data-nodeid'))
.getPort(element.getAttribute('data-portid')),
element
};
}
// look for a point
element = target.closest('[data-pointid]');
if (element) {
return {
model: this.diagramModel
.getLink(element.getAttribute('data-linkid'))
.getPointModel(element.getAttribute('data-pointid')),
element
};
}
// look for a link
element = target.closest('[data-linkid]');
if (element) {
return {
model: this.diagramModel.getLink(element.getAttribute('data-linkid')),
element
};
}
// a node maybe
element = target.closest('[data-nodeid]');
if (element) {
return {
model: this.diagramModel.getNode(element.getAttribute('data-nodeid')),
element
};
}
// just the canvas
return null;
}
onMouseUp(event) {
const diagramEngine = this.diagramModel.getDiagramEngine();
const action = this.action$.getValue();
// are we going to connect a link to something?
if (action instanceof MoveItemsAction) {
const element = this.getMouseElement(event);
action.selectionModels.forEach((model) => {
// only care about points connecting to things
if (!model || !(model.model instanceof PointModel)) {
return;
}
let el;
if (model.magnet) {
el = model.magnet;
}
else if (element && element.model) {
el = element.model;
}
if (el instanceof PortModel && !diagramEngine.isModelLocked(el)) {
const link = model.model.getLink();
if (link.getTargetPort() !== null) {
// if this was a valid link already and we are adding a node in the middle, create 2 links from the original
if (link.getTargetPort() !== el && link.getSourcePort() !== el) {
const targetPort = link.getTargetPort();
const newLink = link.clone({});
newLink.setSourcePort(el);
newLink.setTargetPort(targetPort);
link.setTargetPort(el);
targetPort.removeLink(link);
newLink.removePointsBefore(newLink.getPoints()[link.getPointIndex(model.model)]);
link.removePointsAfter(model.model);
diagramEngine.getDiagramModel().addLink(newLink);
// if we are connecting to the same target or source, destroy tweener points
}
else if (link.getTargetPort() === el) {
link.removePointsAfter(model.model);
}
else if (link.getSourcePort() === el) {
link.removePointsBefore(model.model);
}
}
else {
link.setTargetPort(el);
const targetPort = link.getTargetPort();
const srcPort = link.getSourcePort();
if (targetPort.id !== srcPort.id && srcPort.canLinkToPort(targetPort)) {
// link is valid, fire the event
this.startFiringAction(new LinkCreatedAction(event.clientX, event.clientY, link));
}
}
}
// reset current magent
model.magnet = undefined;
});
// check for / destroy any loose links in any models which have been moved
if (!this.allowLooseLinks) {
action.selectionModels.forEach((model) => {
// only care about points connecting to things
if (!model || !(model.model instanceof PointModel)) {
return;
}
const selectedPoint = model.model;
const link = selectedPoint.getLink();
if (link.getSourcePort() === null || link.getTargetPort() === null) {
link.destroy();
this.startFiringAction(new LooseLinkDestroyed(event.clientX, event.clientY, link));
}
});
}
// destroy any invalid links
action.selectionModels.forEach((model) => {
// only care about points connecting to things
if (!model || !(model.model instanceof PointModel)) {
return;
}
const link = model.model.getLink();
const sourcePort = link.getSourcePort();
const targetPort = link.getTargetPort();
if (sourcePort !== null && targetPort !== null) {
if (!sourcePort.canLinkToPort(targetPort)) {
// link not allowed
link.destroy();
this.startFiringAction(new InvalidLinkDestroyed(event.clientX, event.clientY, link));
}
else if (targetPort
.getLinksArray()
.some((link) => link !== link && (link.getSourcePort() === sourcePort || link.getTargetPort() === sourcePort))) {
// link is a duplicate
link.destroy();
}
}
});
this.stopFiringAction();
}
else {
this.stopFiringAction();
}
this.action$.next(null);
}
/**
* @description Mouse Move Event Handler
* @param event MouseEvent
*/
onMouseMove(event) {
const action = this.action$.getValue();
if (action === null || action === undefined) {
return;
}
if (action instanceof SelectingAction) {
const relative = this.diagramModel.getDiagramEngine().getRelativePoint(event.clientX, event.clientY);
this.diagramModel.getNodes().forEach((node) => {
if (action.containsElement(node.getCoords(), this.diagramModel)) {
node.setSelected();
}
else {
node.setSelected(false);
}
});
this.diagramModel.getLinks().forEach((link) => {
let allSelected = true;
link.getPoints().forEach((point) => {
if (action.containsElement(point.getCoords(), this.diagramModel)) {
point.setSelected();
}
else {
point.setSelected(false);
allSelected = false;
}
});
if (allSelected) {
link.setSelected();
}
});
action.mouseX2 = relative.x;
action.mouseY2 = relative.y;
this.fireAction();
this.action$.next(action);
return;
}
else if (action instanceof MoveItemsAction) {
const coords = {
x: event.clientX - action.mouseX,
y: event.clientY - action.mouseY
};
const amountZoom = this.diagramModel.getZoomLevel() / 100;
action.selectionModels.forEach((selectionModel) => {
// reset all previous magnets if any
selectionModel.magnet = undefined;
// in this case we need to also work out the relative grid position
if (selectionModel.model instanceof NodeModel ||
(selectionModel.model instanceof PointModel && !selectionModel.model.isConnectedToPort())) {
const newCoords = {
x: selectionModel.initialX + coords.x / amountZoom,
y: selectionModel.initialY + coords.y / amountZoom
};
const gridRelativeCoords = this.diagramModel.getGridPosition(newCoords);
// magnetic inputs handling
if (selectionModel.model instanceof PointModel && this.portMagneticRadius) {
// get all ports on canvas, check distances, if smaller then defined radius, magnetize!
const portsMap = this.diagramModel.getAllPorts({ filter: (p) => p.getMagnetic() });
for (const port of portsMap.values()) {
const portCoords = port.getCoords();
const distance = Math.hypot(portCoords.x - newCoords.x, portCoords.y - newCoords.y);
if (distance <= this.portMagneticRadius) {
const portCenter = this.diagramModel.getDiagramEngine().getPortCenter(port);
selectionModel.model.setCoords(portCenter);
selectionModel.magnet = port;
return;
}
}
}
selectionModel.model.setCoords(gridRelativeCoords);
if (selectionModel.model instanceof NodeModel) {
// update port coordinates as well
selectionModel.model.getPorts().forEach((port) => {
const portCoords = this.diagramModel.getDiagramEngine().getPortCoords(port);
port.updateCoords(portCoords);
});
}
if (this.diagramModel.getDiagramEngine().getSmartRouting()) {
setTimeout(() => {
this.diagramModel.getDiagramEngine().calculateRoutingMatrix();
}, 1);
}
}
else if (selectionModel.model instanceof PointModel) {
// will only run here when trying to create a point on an existing link
// we want points that are connected to ports, to not necessarily snap to grid
// this stuff needs to be pixel perfect, dont touch it
const newCoords = this.diagramModel.getGridPosition({ x: coords.x / amountZoom, y: coords.y / amountZoom });
selectionModel.model.setCoords({
x: selectionModel.initialX + newCoords.x,
y: selectionModel.initialY + newCoords.y
});
}
});
this.fireAction();
}
else if (action instanceof MoveCanvasAction) {
if (this.allowCanvasTranslation) {
this.diagramModel.setOffset(action.initialOffsetX + (event.clientX - action.mouseX), action.initialOffsetY + (event.clientY - action.mouseY));
this.fireAction();
}
}
}
onMouseDown(event) {
if (event.button === 3) {
return;
}
const selectedModel = this.getMouseElement(event);
// canvas selected
if (selectedModel === null) {
// multiple selection
if (event.shiftKey) {
// initiate multiple selection selector
const relative = this.diagramModel.getDiagramEngine().getRelativePoint(event.clientX, event.clientY);
this.startFiringAction(new SelectingAction(relative.x, relative.y));
}
else {
// drag canvas action
this.diagramModel.clearSelection();
this.startFiringAction(new MoveCanvasAction(event.clientX, event.clientY, this.diagramModel));
}
}
else if (selectedModel.model instanceof PortModel) {
// its a port element, we want to drag a link
if (!selectedModel.model.isLocked() && selectedModel.model.getCanCreateLinks()) {
const relative = this.diagramModel.getDiagramEngine().getRelativeMousePoint(event);
const sourcePort = selectedModel.model;
const link = sourcePort.createLinkModel();
// if we don't have a link then we have reached the max amount, or we cannot create new ones
if (link) {
link.setSourcePort(sourcePort);
link.removeMiddlePoints();
if (link.getSourcePort() !== sourcePort) {
link.setSourcePort(sourcePort);
}
link.setTargetPort(null);
link.getFirstPoint().setCoords(relative);
link.getLastPoint().setCoords(relative);
this.diagramModel.clearSelection();
link.getLastPoint().setSelected();
this.diagramModel.addLink(link);
this.startFiringAction(new MoveItemsAction(event.clientX, event.clientY, this.diagramModel.getDiagramEngine()));
}
}
else {
this.diagramModel.clearSelection();
}
}
else if (selectedModel.model instanceof PointModel && selectedModel.model.isConnectedToPort()) {
this.diagramModel.clearSelection();
}
else {
// its some other element, probably want to move it
if (!event.shiftKey && !selectedModel.model.getSelected()) {
this.diagramModel.clearSelection();
}
selectedModel.model.setSelected();
this.startFiringAction(new MoveItemsAction(event.clientX, event.clientY, this.diagramModel.getDiagramEngine()));
}
this.createMouseListeners();
}
onMouseWheel(event) {
if (!this.allowCanvasZoom) {
return;
}
event.preventDefault();
event.stopPropagation();
const currentZoomLevel = this.diagramModel.getZoomLevel();
const oldZoomFactor = currentZoomLevel / 100;
let scrollDelta = this.inverseZoom ? -event.deltaY : event.deltaY;
// check if it is pinch gesture
if (event.ctrlKey && scrollDelta % 1 !== 0) {
/* Chrome and Firefox sends wheel event with deltaY that
have fractional part, also `ctrlKey` prop of the event is true
though ctrl isn't pressed
*/
scrollDelta /= 3;
}
else {
scrollDelta /= 60;
}
if (currentZoomLevel + scrollDelta > 10) {
const newZoomLvl = currentZoomLevel + scrollDelta;
this.diagramModel.setZoomLevel(newZoomLvl);
}
const updatedZoomLvl = this.diagramModel.getZoomLevel();
const zoomFactor = updatedZoomLvl / 100;
const boundingRect = event.currentTarget.getBoundingClientRect();
const clientWidth = boundingRect.width;
const clientHeight = boundingRect.height;
// compute difference between rect before and after scroll
const widthDiff = clientWidth * zoomFactor - clientWidth * oldZoomFactor;
const heightDiff = clientHeight * zoomFactor - clientHeight * oldZoomFactor;
// compute mouse coords relative to canvas
const clientX = event.clientX - boundingRect.left;
const clientY = event.clientY - boundingRect.top;
// compute width and height increment factor
const xFactor = (clientX - this.diagramModel.getOffsetX()) / oldZoomFactor / clientWidth;
const yFactor = (clientY - this.diagramModel.getOffsetY()) / oldZoomFactor / clientHeight;
const updatedXOffset = this.diagramModel.getOffsetX() - widthDiff * xFactor;
const updatedYOffset = this.diagramModel.getOffsetY() - heightDiff * yFactor;
this.diagramModel.setOffset(updatedXOffset, updatedYOffset);
}
setLayerStyles(x, y, zoom) {
const nodesLayer = this.getNodesLayer();
const linksLayer = this.getLinksLayer();
const style = 'transform';
const value = `translate(${x}px, ${y}px) scale(${zoom / 100.0})`;
this.renderer.setStyle(nodesLayer, style, value);
this.renderer.setStyle(linksLayer, style, value);
}
initNodes() {
this.nodes$ = this.diagramModel.selectNodes();
this.diagramModel.getDiagramEngine().setCanvas(this.canvas.nativeElement);
this.diagramModel.getDiagramEngine().setSmartRoutingStatus(this.smartRouting);
this.diagramModel.setMaxZoomIn(this.maxZoomIn);
this.diagramModel.setMaxZoomOut(this.maxZoomOut);
this.nodes$.pipe(takeUntil(this.destroyed$)).subscribe((nodes) => {
this.nodesRendered$.next(false);
for (const node of nodes.values()) {
if (!node.getPainted()) {
this.diagramModel.getDiagramEngine().generateWidgetForNode(node, this.nodesLayer);
this.cdRef.detectChanges();
}
}
this.nodesRendered$.next(true);
});
}
initLinks() {
this.links$ = this.diagramModel.selectLinks();
combineLatest([this.nodesRendered$, this.links$])
.pipe(filter(([nodesRendered]) => !!nodesRendered), takeUntil(this.destroyed$))
.subscribe(([, links]) => {
for (const link of links.values()) {
if (!link.getPainted() && link.getSourcePort().getPainted()) {
if (link.getSourcePort() !== null) {
const portCenter = this.diagramModel.getDiagramEngine().getPortCenter(link.getSourcePort());
link.getPoints()[0].setCoords(portCenter);
const portCoords = this.diagramModel.getDiagramEngine().getPortCoords(link.getSourcePort());
link.getSourcePort().updateCoords(portCoords);
}
if (link.getTargetPort() !== null) {
const portCenter = this.diagramModel.getDiagramEngine().getPortCenter(link.getTargetPort());
link.getPoints()[link.getPoints().length - 1].setCoords(portCenter);
const portCoords = this.diagramModel.getDiagramEngine().getPortCoords(link.getTargetPort());
link.getTargetPort().updateCoords(portCoords);
}
this.diagramModel.getDiagramEngine().generateWidgetForLink(link, this.linksLayer);
this.cdRef.detectChanges();
}
}
});
}
initSubs() {
combineLatest([
this.diagramModel.selectOffsetX(),
this.diagramModel.selectOffsetY(),
this.diagramModel.selectZoomLevel()
])
.pipe(tap(([x, y, zoom]) => this.setLayerStyles(x, y, zoom)), takeUntil(this.destroyed$))
.subscribe();
}
getNodesLayer() {
return this.host.querySelector('.ngdx-nodes-layer');
}
getLinksLayer() {
return this.host.querySelector('.ngdx-links-layer');
}
createMouseListeners() {
const mouseUp$ = fromEvent(this.document, 'mouseup').pipe(tap((e) => this.onMouseUp(e)), take(1));
const mouseMove$ = fromEvent(this.document, 'mousemove').pipe(tap((e) => this.onMouseMove(e)), takeUntil(mouseUp$));
merge(mouseMove$, mouseUp$).subscribe();
}
}
NgxDiagramComponent.decorators = [
{ type: Component, args: [{
selector: 'ngdx-diagram',
template: "<div class=\"ngdx-diagram\" #canvas (mousedown)=\"onMouseDown($event)\" (wheel)=\"onMouseWheel($event)\">\n\t<!-- Nodes Layer -->\n\t<div class=\"ngdx-nodes-layer\">\n\t\t<ng-template #nodesLayer></ng-template>\n\t</div>\n\n\t<!-- Links Layer -->\n\t<div class=\"ngdx-links-layer\">\n\t\t<ng-template #linksLayer></ng-template>\n\t</div>\n\n\t<!-- Selection Box -->\n\t<div\n\t\t*ngIf=\"shouldDrawSelectionBox() && selectAction() | async as action\"\n\t\tclass=\"selector\"\n\t\t[ngStyle]=\"{\n\t\t\ttop: action.dimensions.top + 'px',\n\t\t\tleft: action.dimensions.left + 'px',\n\t\t\twidth: action.dimensions.width + 'px',\n\t\t\theight: action.dimensions.height + 'px'\n\t\t}\"></div>\n</div>\n",
changeDetection: ChangeDetectionStrategy.OnPush,
styles: [".ngdx-diagram{cursor:move;display:flex;flex-grow:1;overflow:hidden;position:relative}.ngdx-diagram .selector{background-color:rgba(0,192,255,.2);border:2px solid #00c0ff;position:absolute}.ngdx-diagram .ngdx-nodes-layer{z-index:150}.ngdx-diagram .ngdx-links-layer,.ngdx-diagram .ngdx-nodes-layer{bottom:0;height:100%;left:0;overflow:visible!important;pointer-events:none;position:absolute;right:0;top:0;transform-origin:0 0;width:100%}.ngdx-diagram .ngdx-links-layer{z-index:100}"]
},] }
];
NgxDiagramComponent.ctorParameters = () => [
{ type: undefined, decorators: [{ type: Inject, args: [DOCUMENT,] }] },
{ type: NgZone },
{ type: Renderer2 },
{ type: ChangeDetectorRef },
{ type: ElementRef }
];
NgxDiagramComponent.propDecorators = {
diagramModel: [{ type: Input, args: ['model',] }],
allowCanvasZoom: [{ type: Input }],
allowCanvasTranslation: [{ type: Input }],
inverseZoom: [{ type: Input }],
allowLooseLinks: [{ type: Input }],
maxZoomOut: [{ type: Input }],
maxZoomIn: [{ type: Input }],
portMagneticRadius: [{ type: Input }],
smartRouting: [{ type: Input }],
actionStartedFiring: [{ type: Output }],
actionStillFiring: [{ type: Output }],
actionStoppedFiring: [{ type: Output }],
nodesLayer: [{ type: ViewChild, args: ['nodesLayer', { read: ViewContainerRef },] }],
linksLayer: [{ type: ViewChild, args: ['linksLayer', { read: ViewContainerRef },] }],
canvas: [{ type: ViewChild, args: ['canvas', { read: ElementRef },] }]
};
__decorate([
OutsideZone,
__metadata("design:type", Function),
__metadata("design:paramtypes", [MouseEvent]),
__metadata("design:returntype", void 0)
], NgxDiagramComponent.prototype, "onMouseUp", null);
__decorate([
OutsideZone,
__metadata("design:type", Function),
__metadata("design:paramtypes", [MouseEvent]),
__metadata("design:returntype", void 0)
], NgxDiagramComponent.prototype, "onMouseMove", null);
__decorate([
OutsideZone,
__metadata("design:type", Function),
__metadata("design:paramtypes", [MouseEvent]),
__metadata("design:returntype", void 0)
], NgxDiagramComponent.prototype, "onMouseDown", null);
__decorate([
OutsideZone,
__metadata("design:type", Function),
__metadata("design:paramtypes", [WheelEvent]),
__metadata("design:returntype", void 0)
], NgxDiagramComponent.prototype, "onMouseWheel", null);
__decorate([
OutsideZone,
__metadata("design:type", Function),
__metadata("design:paramtypes", [Number, Number, Number]),
__metadata("design:returntype", void 0)
], NgxDiagramComponent.prototype, "setLayerStyles", null);
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGlhZ3JhbS5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiLi4vLi4vLi4vLi4vbGlicy9uZ3gtZGlhZ3JhbXMvc3JjLyIsInNvdXJjZXMiOlsibGliL2NvbnRhaW5lcnMvZGlhZ3JhbS9kaWFncmFtLmNvbXBvbmVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUEsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQzNDLE9BQU8sRUFFTCx1QkFBdUIsRUFDdkIsaUJBQWlCLEVBQ2pCLFNBQVMsRUFDVCxVQUFVLEVBQ1YsWUFBWSxFQUNaLE1BQU0sRUFDTixLQUFLLEVBQ0wsTUFBTSxFQUVOLE1BQU0sRUFDTixTQUFTLEVBQ1QsU0FBUyxFQUNULGdCQUFnQixFQUNqQixNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQUUsZUFBZSxFQUFFLGFBQWEsRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFjLGFBQWEsRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUNuRyxPQUFPLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBRSxTQUFTLEVBQUUsR0FBRyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDOUQsT0FBTyxFQUFjLG9CQUFvQixFQUFFLGlCQUFpQixFQUFFLGdCQUFnQixFQUFFLGVBQWUsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUN2SCxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSwyQ0FBMkMsQ0FBQztBQUMvRSxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFHbEUsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBRTFELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUNwRCxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDdEQsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQ3BELE9BQU8sRUFBYSxXQUFXLEVBQWMsTUFBTSxh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