Null point check enforcement

This commit is contained in:
Paulo Gustavo Veiga 2022-11-29 21:48:53 -08:00
parent d3159d8a20
commit 74a50dd639
10 changed files with 176 additions and 123 deletions

View File

@ -72,7 +72,7 @@ class ConnectionLine {
protected _createLine(lineTypeParam: number, defaultStyle: number): Line { protected _createLine(lineTypeParam: number, defaultStyle: number): Line {
const lineType = $defined(lineTypeParam) ? lineTypeParam : defaultStyle; const lineType = $defined(lineTypeParam) ? lineTypeParam : defaultStyle;
this._lineType = lineType; this._lineType = lineType;
let line = null; let line: ConnectionLine;
switch (lineType) { switch (lineType) {
case ConnectionLine.POLYLINE: case ConnectionLine.POLYLINE:
line = new PolyLine(); line = new PolyLine();
@ -82,7 +82,7 @@ class ConnectionLine {
break; break;
case ConnectionLine.SIMPLE_CURVED: case ConnectionLine.SIMPLE_CURVED:
line = new CurvedLine(); line = new CurvedLine();
line.setStyle(CurvedLine.SIMPLE_LINE); (line as CurvedLine).setStyle(CurvedLine.SIMPLE_LINE);
break; break;
default: default:
line = new Line(); line = new Line();
@ -141,7 +141,7 @@ class ConnectionLine {
y -= offset; y -= offset;
const connector = targetTopic.getShrinkConnector(); const connector = targetTopic.getShrinkConnector();
if ($defined(connector)) { if (connector) {
if (Math.sign(targetPosition.x) > 0) { if (Math.sign(targetPosition.x) > 0) {
x = targetTopicSize.width; x = targetTopicSize.width;
connector.setPosition(x, y); connector.setPosition(x, y);

View File

@ -32,7 +32,7 @@ class DesignerUndoManager {
this._baseId = 0; this._baseId = 0;
} }
enqueue(command: Command) { enqueue(command: Command): void {
$assert(command, 'Command can not be null'); $assert(command, 'Command can not be null');
const { length } = this._undoQueue; const { length } = this._undoQueue;
if (command.getDiscardDuplicated() && length > 0) { if (command.getDiscardDuplicated() && length > 0) {
@ -47,28 +47,31 @@ class DesignerUndoManager {
this._redoQueue = []; this._redoQueue = [];
} }
execUndo(commandContext: CommandContext) { execUndo(commandContext: CommandContext): void {
if (this._undoQueue.length > 0) { if (this._undoQueue.length > 0) {
const command = this._undoQueue.pop(); const command = this._undoQueue.pop();
if (command) {
this._redoQueue.push(command); this._redoQueue.push(command);
command.undoExecute(commandContext); command.undoExecute(commandContext);
} }
} }
}
execRedo(commandContext) { execRedo(commandContext: CommandContext): void {
if (this._redoQueue.length > 0) { if (this._redoQueue.length > 0) {
const command = this._redoQueue.pop(); const command = this._redoQueue.pop();
if (command) {
this._undoQueue.push(command); this._undoQueue.push(command);
command.execute(commandContext); command.execute(commandContext);
} }
} }
}
buildEvent() { buildEvent() {
return { undoSteps: this._undoQueue.length, redoSteps: this._redoQueue.length }; return { undoSteps: this._undoQueue.length, redoSteps: this._redoQueue.length };
} }
markAsChangeBase() { markAsChangeBase(): void {
const undoLength = this._undoQueue.length; const undoLength = this._undoQueue.length;
if (undoLength > 0) { if (undoLength > 0) {
const command = this._undoQueue[undoLength - 1]; const command = this._undoQueue[undoLength - 1];
@ -78,7 +81,7 @@ class DesignerUndoManager {
} }
} }
hasBeenChanged() { hasBeenChanged(): boolean {
let result = true; let result = true;
const undoLength = this._undoQueue.length; const undoLength = this._undoQueue.length;
if (undoLength === 0 && this._baseId === 0) { if (undoLength === 0 && this._baseId === 0) {

View File

@ -48,19 +48,26 @@ class LocalStorageManager extends PersistenceManager {
loadMapDom(mapId: string): Promise<Document> { loadMapDom(mapId: string): Promise<Document> {
let result: Promise<Document>; let result: Promise<Document>;
let localStorate; let localStorate: string | null = null;
if (!this.readOnly) { if (!this.readOnly) {
localStorate = localStorage.getItem(`${mapId}-xml`); localStorate = localStorage.getItem(`${mapId}-xml`);
} }
if (localStorate == null || this.forceLoad) { if (localStorate == null || this.forceLoad) {
const url = this.documentUrl.replace('{id}', mapId); const url = this.documentUrl.replace('{id}', mapId);
const csrfToken = this.getCSRFToken();
result = fetch(url, { result = fetch(url, {
method: 'get', method: 'get',
headers: { headers:
csrfToken != null
? {
'Content-Type': 'text/plain',
Accept: 'application/xml',
'X-CSRF-Token': csrfToken,
}
: {
'Content-Type': 'text/plain', 'Content-Type': 'text/plain',
Accept: 'application/xml', Accept: 'application/xml',
'X-CSRF-Token': this.getCSRFToken(),
}, },
}) })
.then((response: Response) => { .then((response: Response) => {

View File

@ -34,7 +34,7 @@ class RelationshipPivot {
private _onTopicClick: (event: MouseEvent) => void; private _onTopicClick: (event: MouseEvent) => void;
private _sourceTopic: Topic; private _sourceTopic: Topic | null;
private _pivot: CurvedLine; private _pivot: CurvedLine;
@ -46,8 +46,8 @@ class RelationshipPivot {
this._workspace = workspace; this._workspace = workspace;
this._designer = designer; this._designer = designer;
this._mouseMoveEvent = this._mouseMove.bind(this); this._mouseMoveEvent = this.mouseMoveHandler.bind(this);
this._onClickEvent = this._cleanOnMouseClick.bind(this); this._onClickEvent = this.cleanOnMouseClick.bind(this);
this._onTopicClick = this._connectOnFocus.bind(this); this._onTopicClick = this._connectOnFocus.bind(this);
} }
@ -57,7 +57,6 @@ class RelationshipPivot {
this.dispose(); this.dispose();
this._sourceTopic = sourceTopic; this._sourceTopic = sourceTopic;
if (sourceTopic != null) {
this._workspace.enableWorkspaceEvents(false); this._workspace.enableWorkspaceEvents(false);
const sourcePos = sourceTopic.getPosition(); const sourcePos = sourceTopic.getPosition();
@ -91,7 +90,6 @@ class RelationshipPivot {
topic.addEvent('ontfocus', this._onTopicClick); topic.addEvent('ontfocus', this._onTopicClick);
}); });
} }
}
dispose(): void { dispose(): void {
const workspace = this._workspace; const workspace = this._workspace;
@ -117,12 +115,12 @@ class RelationshipPivot {
} }
} }
_mouseMove(event: MouseEvent): boolean { private mouseMoveHandler(event: MouseEvent): boolean {
const screen = this._workspace.getScreenManager(); const screen = this._workspace.getScreenManager();
const pos = screen.getWorkspaceMousePosition(event); const pos = screen.getWorkspaceMousePosition(event);
// Leave the arrow a couple of pixels away from the cursor. // Leave the arrow a couple of pixels away from the cursor.
const sourcePosition = this._sourceTopic.getPosition(); const sourcePosition = this._sourceTopic!.getPosition();
const gapDistance = Math.sign(pos.x - sourcePosition.x) * 5; const gapDistance = Math.sign(pos.x - sourcePosition.x) * 5;
const sPos = this._calculateFromPosition(pos); const sPos = this._calculateFromPosition(pos);
@ -139,7 +137,7 @@ class RelationshipPivot {
return false; return false;
} }
_cleanOnMouseClick(event: MouseEvent): void { private cleanOnMouseClick(event: MouseEvent): void {
// The user clicks on a desktop on in other element that is not a node. // The user clicks on a desktop on in other element that is not a node.
this.dispose(); this.dispose();
event.stopPropagation(); event.stopPropagation();
@ -147,8 +145,8 @@ class RelationshipPivot {
private _calculateFromPosition(toPosition: Point): Point { private _calculateFromPosition(toPosition: Point): Point {
// Calculate origin position ... // Calculate origin position ...
let sourcePosition = this._sourceTopic.getPosition(); let sourcePosition = this._sourceTopic!.getPosition();
if (this._sourceTopic.getType() === 'CentralTopic') { if (this._sourceTopic!.getType() === 'CentralTopic') {
sourcePosition = Shape.workoutIncomingConnectionPoint(this._sourceTopic, toPosition); sourcePosition = Shape.workoutIncomingConnectionPoint(this._sourceTopic, toPosition);
} }
const controlPoint = Shape.calculateDefaultControlPoints(sourcePosition, toPosition); const controlPoint = Shape.calculateDefaultControlPoints(sourcePosition, toPosition);
@ -164,8 +162,8 @@ class RelationshipPivot {
const mindmap = this._designer.getMindmap(); const mindmap = this._designer.getMindmap();
// Avoid circular connections ... // Avoid circular connections ...
if (targetTopic.getId() !== sourceTopic.getId()) { if (targetTopic.getId() !== sourceTopic!.getId()) {
const relModel = mindmap.createRelationship(targetTopic.getId(), sourceTopic.getId()); const relModel = mindmap.createRelationship(targetTopic.getId(), sourceTopic!.getId());
this._designer.getActionDispatcher().addRelationship(relModel); this._designer.getActionDispatcher().addRelationship(relModel);
} }
this.dispose(); this.dispose();

View File

@ -59,14 +59,20 @@ class RESTPersistenceManager extends PersistenceManager {
}, 10000); }, 10000);
const persistence = this; const persistence = this;
const crfs = this.getCSRFToken();
fetch(`${this.documentUrl.replace('{id}', mapId)}?${query}`, { fetch(`${this.documentUrl.replace('{id}', mapId)}?${query}`, {
method: 'PUT', method: 'PUT',
// Blob helps to resuce the memory on large payload. // Blob helps to resuce the memory on large payload.
body: new Blob([JSON.stringify(data)], { type: 'text/plain' }), body: new Blob([JSON.stringify(data)], { type: 'text/plain' }),
headers: { headers: crfs
? {
'Content-Type': 'application/json; charset=utf-8',
Accept: 'application/json',
'X-CSRF-Token': crfs,
}
: {
'Content-Type': 'application/json; charset=utf-8', 'Content-Type': 'application/json; charset=utf-8',
Accept: 'application/json', Accept: 'application/json',
'X-CSRF-Token': this.getCSRFToken(),
}, },
}) })
.then(async (response: Response) => { .then(async (response: Response) => {
@ -74,7 +80,7 @@ class RESTPersistenceManager extends PersistenceManager {
events.onSuccess(); events.onSuccess();
} else { } else {
console.log(`Saving error: ${response.status}`); console.log(`Saving error: ${response.status}`);
let userMsg; let userMsg: PersistenceError | null = null;
if (response.status === 405) { if (response.status === 405) {
userMsg = { userMsg = {
severity: 'SEVERE', severity: 'SEVERE',
@ -85,19 +91,21 @@ class RESTPersistenceManager extends PersistenceManager {
const responseText = await response.text(); const responseText = await response.text();
const contentType = response.headers['Content-Type']; const contentType = response.headers['Content-Type'];
if (contentType != null && contentType.indexOf('application/json') !== -1) { if (contentType != null && contentType.indexOf('application/json') !== -1) {
let serverMsg = null; let serverMsg: null | { globalSeverity: string } = null;
try { try {
serverMsg = JSON.parse(responseText); serverMsg = JSON.parse(responseText);
serverMsg = serverMsg.globalSeverity ? serverMsg : null; serverMsg = serverMsg && serverMsg.globalSeverity ? serverMsg : null;
} catch (e) { } catch (e) {
// Message could not be decoded ... // Message could not be decoded ...
} }
userMsg = persistence._buildError(serverMsg); userMsg = persistence._buildError(serverMsg);
} }
} }
if (userMsg) {
this.triggerError(userMsg); this.triggerError(userMsg);
events.onError(userMsg); events.onError(userMsg);
} }
}
// Clear event timeout ... // Clear event timeout ...
if (persistence.clearTimeout) { if (persistence.clearTimeout) {
@ -123,21 +131,36 @@ class RESTPersistenceManager extends PersistenceManager {
} }
} }
discardChanges(mapId: string) { discardChanges(mapId: string): void {
const csrfToken = this.getCSRFToken();
fetch(this.revertUrl.replace('{id}', mapId), { fetch(this.revertUrl.replace('{id}', mapId), {
method: 'POST', method: 'POST',
headers: { headers: csrfToken
? {
'Content-Type': 'application/json; charset=utf-8',
Accept: 'application/json',
'X-CSRF-Token': csrfToken,
}
: {
'Content-Type': 'application/json; charset=utf-8', 'Content-Type': 'application/json; charset=utf-8',
Accept: 'application/json', Accept: 'application/json',
'X-CSRF-Token': this.getCSRFToken(),
}, },
}); });
} }
unlockMap(mapId: string): void { unlockMap(mapId: string): void {
const csrfToken = this.getCSRFToken();
fetch(this.lockUrl.replace('{id}', mapId), { fetch(this.lockUrl.replace('{id}', mapId), {
method: 'PUT', method: 'PUT',
headers: { 'Content-Type': 'text/plain', 'X-CSRF-Token': this.getCSRFToken() }, headers: csrfToken
? {
'Content-Type': 'text/plain',
'X-CSRF-Token': csrfToken,
}
: {
'Content-Type': 'text/plain',
},
body: 'false', body: 'false',
}); });
} }
@ -158,12 +181,18 @@ class RESTPersistenceManager extends PersistenceManager {
loadMapDom(mapId: string): Promise<Document> { loadMapDom(mapId: string): Promise<Document> {
const url = `${this.documentUrl.replace('{id}', mapId)}/xml`; const url = `${this.documentUrl.replace('{id}', mapId)}/xml`;
const csrfToken = this.getCSRFToken();
return fetch(url, { return fetch(url, {
method: 'get', method: 'get',
headers: { headers: csrfToken
? {
'Content-Type': 'text/plain',
Accept: 'application/xml',
'X-CSRF-Token': csrfToken,
}
: {
'Content-Type': 'text/plain', 'Content-Type': 'text/plain',
Accept: 'application/xml', Accept: 'application/xml',
'X-CSRF-Token': this.getCSRFToken(),
}, },
}) })
.then((response: Response) => { .then((response: Response) => {

View File

@ -23,7 +23,7 @@ import NodeModel from '../model/NodeModel';
class AddTopicCommand extends Command { class AddTopicCommand extends Command {
private _models: NodeModel[]; private _models: NodeModel[];
private _parentsIds: number[]; private _parentsIds: number[] | null;
/** /**
* @classdesc This command class handles do/undo of adding one or multiple topics to * @classdesc This command class handles do/undo of adding one or multiple topics to
@ -42,14 +42,13 @@ class AddTopicCommand extends Command {
} }
execute(commandContext: CommandContext) { execute(commandContext: CommandContext) {
const me = this;
this._models.forEach((model, index) => { this._models.forEach((model, index) => {
// Add a new topic ... // Add a new topic ...
const topic = commandContext.createTopic(model); const topic = commandContext.createTopic(model);
// Connect to topic ... // Connect to topic ...
if (this._parentsIds) { if (this._parentsIds) {
const parentId = me._parentsIds[index]; const parentId = this._parentsIds[index];
if ($defined(parentId)) { if ($defined(parentId)) {
const parentTopic = commandContext.findTopics([parentId])[0]; const parentTopic = commandContext.findTopics([parentId])[0];
commandContext.connect(topic, parentTopic); commandContext.connect(topic, parentTopic);

View File

@ -38,7 +38,6 @@ class RemoveFeatureFromTopicCommand extends Command {
super(); super();
this._topicId = topicId; this._topicId = topicId;
this._featureId = featureId; this._featureId = featureId;
this._oldFeature = null;
} }
/** /**
@ -48,7 +47,6 @@ class RemoveFeatureFromTopicCommand extends Command {
const topic = commandContext.findTopics([this._topicId])[0]; const topic = commandContext.findTopics([this._topicId])[0];
const feature = topic.findFeatureById(this._featureId); const feature = topic.findFeatureById(this._featureId);
topic.removeFeature(feature); topic.removeFeature(feature);
this._oldFeature = feature;
} }
/** /**
@ -58,7 +56,6 @@ class RemoveFeatureFromTopicCommand extends Command {
undoExecute(commandContext: CommandContext) { undoExecute(commandContext: CommandContext) {
const topic = commandContext.findTopics([this._topicId])[0]; const topic = commandContext.findTopics([this._topicId])[0];
topic.addFeature(this._oldFeature); topic.addFeature(this._oldFeature);
this._oldFeature = null;
} }
} }

View File

@ -77,7 +77,7 @@ class BinaryImageExporter extends Exporter {
const img = new Image(); const img = new Image();
const result = new Promise<string>((resolve) => { const result = new Promise<string>((resolve) => {
img.onload = () => { img.onload = () => {
const ctx = canvas.getContext('2d'); const ctx = canvas.getContext('2d')!;
// Scale for retina ... // Scale for retina ...
ctx.scale(dpr, dpr); ctx.scale(dpr, dpr);
ctx.drawImage(img, 0, 0); ctx.drawImage(img, 0, 0);

View File

@ -22,7 +22,7 @@ import Font from './freemind/Font';
class FreemindExporter extends Exporter { class FreemindExporter extends Exporter {
private mindmap: Mindmap; private mindmap: Mindmap;
private nodeMap: Map<number, FreeminNode> = null; private nodeMap: Map<number, FreeminNode>;
private version: VersionNumber = FreemindConstant.SUPPORTED_FREEMIND_VERSION; private version: VersionNumber = FreemindConstant.SUPPORTED_FREEMIND_VERSION;
@ -87,13 +87,16 @@ class FreemindExporter extends Exporter {
const relationships: Array<RelationshipModel> = this.mindmap.getRelationships(); const relationships: Array<RelationshipModel> = this.mindmap.getRelationships();
relationships.forEach((relationship: RelationshipModel) => { relationships.forEach((relationship: RelationshipModel) => {
const srcNode: FreeminNode = this.nodeMap.get(relationship.getFromNode()); const srcNode: FreeminNode | undefined = this.nodeMap.get(relationship.getFromNode());
const destNode: FreeminNode = this.nodeMap.get(relationship.getToNode()); const destNode: FreeminNode | undefined = this.nodeMap.get(relationship.getToNode());
if (srcNode && destNode) { if (srcNode && destNode) {
const arrowlink: Arrowlink = this.objectFactory.crateArrowlink(); const arrowlink: Arrowlink = this.objectFactory.crateArrowlink();
arrowlink.setDestination(destNode.getId()); const idRel = destNode.getId();
if (idRel) {
arrowlink.setDestination(idRel);
}
if (relationship.getEndArrow() && relationship.getEndArrow()) { if (relationship.getEndArrow() && relationship.getEndArrow()) {
arrowlink.setEndarrow('Default'); arrowlink.setEndarrow('Default');
@ -287,7 +290,10 @@ class FreemindExporter extends Exporter {
if (fontNodeNeeded) { if (fontNodeNeeded) {
if (!font.getSize()) { if (!font.getSize()) {
font.setSize(FreemindExporter.wisweToFreeFontSize.get(8).toString()); const size = FreemindExporter.wisweToFreeFontSize.get(8);
if (size) {
font.setSize(size.toString());
}
} }
freemindNode.setArrowlinkOrCloudOrEdge(font); freemindNode.setArrowlinkOrCloudOrEdge(font);
} }

View File

@ -19,6 +19,7 @@ import NoteModel from '../model/NoteModel';
import FeatureModelFactory from '../model/FeatureModelFactory'; import FeatureModelFactory from '../model/FeatureModelFactory';
import FeatureModel from '../model/FeatureModel'; import FeatureModel from '../model/FeatureModel';
import XMLSerializerFactory from '../persistence/XMLSerializerFactory'; import XMLSerializerFactory from '../persistence/XMLSerializerFactory';
import { off } from 'process';
export default class FreemindImporter extends Importer { export default class FreemindImporter extends Importer {
private mindmap: Mindmap; private mindmap: Mindmap;
@ -98,22 +99,28 @@ export default class FreemindImporter extends Importer {
// Fix dest ID // Fix dest ID
const destId: string = relationship.getDestCtrlPoint(); const destId: string = relationship.getDestCtrlPoint();
const destTopic: NodeModel = this.nodesmap.get(destId); const destTopic: NodeModel | undefined = this.nodesmap.get(destId);
if (destTopic) {
relationship.setDestCtrlPoint(destTopic.getId()); relationship.setDestCtrlPoint(destTopic.getId());
}
// Fix src ID // Fix src ID
const srcId: string = relationship.getSrcCtrlPoint(); const srcId: string = relationship.getSrcCtrlPoint();
const srcTopic: NodeModel = this.nodesmap.get(srcId); const srcTopic: NodeModel | undefined = this.nodesmap.get(srcId);
if (srcTopic) {
relationship.setSrcCtrlPoint(srcTopic.getId()); relationship.setSrcCtrlPoint(srcTopic.getId());
}
mapRelaitonship.push(relationship); mapRelaitonship.push(relationship);
}); });
} }
private fixRelationshipControlPoints(relationship: RelationshipModel): void { private fixRelationshipControlPoints(relationship: RelationshipModel): void {
const srcTopic: NodeModel = this.nodesmap.get(relationship.getToNode().toString()); const srcTopic: NodeModel | undefined = this.nodesmap.get(relationship.getToNode().toString());
const destNode: NodeModel = this.nodesmap.get(relationship.getFromNode().toString()); const destNode: NodeModel | undefined = this.nodesmap.get(
relationship.getFromNode().toString(),
);
if (srcTopic && destNode) {
// Fix x coord // Fix x coord
const srcCtrlPoint: string = relationship.getSrcCtrlPoint(); const srcCtrlPoint: string = relationship.getSrcCtrlPoint();
if (srcCtrlPoint) { if (srcCtrlPoint) {
@ -145,6 +152,7 @@ export default class FreemindImporter extends Importer {
} }
} }
} }
}
private convertNodeProperties( private convertNodeProperties(
freeNode: FreemindNode, freeNode: FreemindNode,
@ -169,7 +177,7 @@ export default class FreemindImporter extends Importer {
} }
// Check for style... // Check for style...
const fontStyle = this.generateFontStyle(freeNode, null); const fontStyle = this.generateFontStyle(freeNode, undefined);
if (fontStyle && fontStyle !== ';;;;') wiseTopic.setFontStyle(fontStyle); if (fontStyle && fontStyle !== ';;;;') wiseTopic.setFontStyle(fontStyle);
// Is there any link... // Is there any link...
@ -200,7 +208,10 @@ export default class FreemindImporter extends Importer {
const wiseId = this.getIdNode(child); const wiseId = this.getIdNode(child);
const wiseChild = mindmap.createNode('MainTopic', wiseId); const wiseChild = mindmap.createNode('MainTopic', wiseId);
this.nodesmap.set(child.getId(), wiseChild); const id = child.getId();
if (id !== null) {
this.nodesmap.set(id, wiseChild);
}
let norder: number; let norder: number;
if (depth !== 1) { if (depth !== 1) {
@ -347,7 +358,7 @@ export default class FreemindImporter extends Importer {
this.idDefault++; this.idDefault++;
idFreeToIdWise = this.idDefault; idFreeToIdWise = this.idDefault;
} else { } else {
idFreeToIdWise = parseInt(id.split('_').pop(), 10); idFreeToIdWise = parseInt(id.split('_').pop()!, 10);
} }
} else { } else {
this.idDefault++; this.idDefault++;
@ -398,7 +409,10 @@ export default class FreemindImporter extends Importer {
// Font family // Font family
if (font) { if (font) {
fontStyle.push(font.getName()); const name = font.getName();
if (name) {
fontStyle.push(name);
}
} }
fontStyle.push(';'); fontStyle.push(';');
@ -485,6 +499,6 @@ export default class FreemindImporter extends Importer {
private html2Text(content: string): string { private html2Text(content: string): string {
const temporalDivElement = document.createElement('div'); const temporalDivElement = document.createElement('div');
temporalDivElement.innerHTML = content; temporalDivElement.innerHTML = content;
return temporalDivElement.textContent.trim() || temporalDivElement.innerText.trim() || ''; return temporalDivElement.textContent?.trim() || temporalDivElement.innerText.trim() || '';
} }
} }