Add string validation

This commit is contained in:
Paulo Gustavo Veiga 2023-01-13 22:33:17 -08:00
parent 1cf42269b7
commit 0194407c7c
76 changed files with 697 additions and 755 deletions

View File

@ -1,7 +1,7 @@
version: '3'
services:
e2e:
image: cypress/included:12.3.0
image: cypress/included:12.2.0
container_name: wisemapping-integration-tests
entrypoint: '/bin/sh -c "yarn install && yarn build && yarn test:integration"'
working_dir: /e2e

View File

@ -1,6 +1,6 @@
{
"compilerOptions": {
"baseUrl": ".",
"baseUrl": "."
"module": "ES6",
},
"exclude": ["node_modules"]

View File

@ -3,7 +3,7 @@
"src/**/*"
],
"compilerOptions": {
"jsx": "react",
"jsx": "react-jsx",
"outDir": "./dist/",
"sourceMap": true,
"noImplicitAny": false,
@ -13,8 +13,8 @@
"target": "es6",
"allowJs": true,
"esModuleInterop": true,
"declaration": true,
"strictNullChecks": false,
"declaration": true,
"rootDirs": [
"src",
],

View File

@ -35,8 +35,6 @@ class ConnectionLine {
protected _sourceTopic: Topic;
protected _lineType: LineType;
protected _line: Line;
private _type: LineType;
@ -50,7 +48,7 @@ class ConnectionLine {
this._sourceTopic = sourceNode;
this._type = type;
this._line = this.createLine(type);
this.updateColor();
this._color = this.updateColor();
}
private _getCtrlPoints(sourceNode: Topic, targetNode: Topic) {
@ -64,7 +62,6 @@ class ConnectionLine {
}
protected createLine(lineType: LineType): ConnectionLine {
this._lineType = lineType;
let line: ConnectionLine;
switch (lineType) {
case LineType.POLYLINE_MIDDLE:
@ -88,7 +85,7 @@ class ConnectionLine {
return line;
}
private updateColor(): void {
private updateColor(): string {
// In case that the main topic has changed the color, overwrite the main topic definiton.
let color = this._targetTopic.getConnectionColor();
if (this._targetTopic.isCentralTopic()) {
@ -96,7 +93,7 @@ class ConnectionLine {
}
this._color = color;
switch (this._lineType) {
switch (this._type) {
case LineType.POLYLINE_MIDDLE:
this._line.setStroke(1, 'solid', color, 1);
break;
@ -112,8 +109,9 @@ class ConnectionLine {
this._line.setFill(color, 1);
break;
default:
throw new Error(`Unexpected line type. ${this._lineType}`);
throw new Error(`Unexpected line type. ${this._type}`);
}
return color;
}
setVisibility(value: boolean, fade = 0): void {
@ -207,7 +205,7 @@ class ConnectionLine {
}
getLineType(): number {
return this._lineType;
return this._type;
}
getLine(): Line {

View File

@ -58,7 +58,7 @@ import { TopicShapeType } from './model/INodeModel';
import { LineType } from './ConnectionLine';
class Designer extends Events {
private _mindmap: Mindmap;
private _mindmap: Mindmap | null;
private _options: DesignerOptions;
@ -70,13 +70,13 @@ class Designer extends Events {
_eventBussDispatcher: EventBusDispatcher;
private _dragManager: DragManager;
private _dragManager!: DragManager;
private _relPivot: RelationshipPivot;
private _clipboard: NodeModel[];
private _cleanScreen: () => void;
private _cleanScreen!: () => void;
constructor(options: DesignerOptions, divElement: JQuery) {
super();
@ -133,6 +133,7 @@ class Designer extends Events {
// Hack: There are static reference to designer variable. Needs to be reviewed.
globalThis.designer = this;
this._mindmap = null;
}
private _registerWheelEvents(): void {
@ -539,7 +540,6 @@ class Designer extends Events {
}
loadMap(mindmap: Mindmap): Promise<void> {
$assert(mindmap, 'mindmapModel can not be null');
this._mindmap = mindmap;
this._workspace.enableQueueRender(true);
@ -587,7 +587,7 @@ class Designer extends Events {
}
getMindmap(): Mindmap {
return this._mindmap;
return this._mindmap!;
}
undo(): void {

View File

@ -16,7 +16,6 @@
* limitations under the License.
*/
import { Text, Group, ElementClass, Point } from '@wisemapping/web2d';
import { $assert } from '@wisemapping/core-js';
import Icon from './Icon';
import IconGroup from './IconGroup';
@ -28,17 +27,15 @@ import ActionDispatcher from './ActionDispatcher';
class EmojiCharIcon implements Icon {
private element: ElementClass;
private group: IconGroup;
private _group: IconGroup | null;
private iconModel: SvgIconModel;
private _iconModel: SvgIconModel;
private topic: Topic;
private _topic: Topic;
constructor(topic: Topic, iconModel: SvgIconModel, readOnly: boolean) {
$assert(iconModel, 'iconModel can not be null');
$assert(topic, 'topic can not be null');
this.iconModel = iconModel;
this.topic = topic;
this._iconModel = iconModel;
this._topic = topic;
this.element = new Group({
width: 90,
@ -57,6 +54,7 @@ class EmojiCharIcon implements Icon {
if (!readOnly) {
this.element.setCursor('pointer');
}
this._group = null;
}
getElement(): ElementClass {
@ -64,19 +62,19 @@ class EmojiCharIcon implements Icon {
}
setGroup(group: IconGroup) {
this.group = group;
this._group = group;
}
getGroup(): IconGroup {
return this.group;
return this._group!;
}
getSize(): SizeType {
return this.group.getSize();
return this._group!.getSize();
}
getPosition(): Point {
return this.group.getPosition();
return this._group!.getPosition();
}
addEvent(type: string, fnc: (e: object) => void): void {
@ -85,12 +83,12 @@ class EmojiCharIcon implements Icon {
remove() {
const actionDispatcher = ActionDispatcher.getInstance();
const featureId = this.iconModel.getId();
actionDispatcher.removeFeatureFromTopic(this.topic.getId(), featureId);
const featureId = this._iconModel.getId();
actionDispatcher.removeFeatureFromTopic(this._topic.getId(), featureId);
}
getModel(): SvgIconModel {
return this.iconModel;
return this._iconModel;
}
}

View File

@ -8,7 +8,7 @@ interface Icon {
setGroup(group: IconGroup): Group;
getGroup(): IconGroup;
getGroup(): IconGroup | null;
getSize(): SizeType;

View File

@ -36,14 +36,11 @@ class IconGroup {
private _removeTip: IconGroupRemoveTip;
private _iconSize: SizeType;
private _iconSize: SizeType | null;
private _topicId: number;
constructor(topicId: number, iconSize: number) {
$assert($defined(topicId), 'topicId can not be null');
$assert($defined(iconSize), 'iconSize can not be null');
this._topicId = topicId;
this._icons = [];
this._group = new Group({
@ -57,6 +54,7 @@ class IconGroup {
this._removeTip = new IconGroupRemoveTip(this._group);
this.seIconSize(iconSize, iconSize);
this._registerListeners();
this._iconSize = null;
}
setPosition(x: number, y: number): void {
@ -142,8 +140,6 @@ class IconGroup {
}
private _removeIcon(icon: Icon) {
$assert(icon, 'icon can not be null');
this._removeTip.close(0);
this._group.removeChild(icon.getElement());
@ -174,10 +170,12 @@ class IconGroup {
}
private _resize(iconsLength: number) {
this._group.setSize(iconsLength * this._iconSize.width, this._iconSize.height);
if (this._iconSize) {
this._group.setSize(iconsLength * this._iconSize.width, this._iconSize.height);
const iconSize = ImageIcon.SIZE + IconGroup.ICON_PADDING * 2;
this._group.setCoordSize(iconsLength * iconSize, iconSize);
const iconSize = ImageIcon.SIZE + IconGroup.ICON_PADDING * 2;
this._group.setCoordSize(iconsLength * iconSize, iconSize);
}
}
private _positionIcon(icon: Icon, order: number) {

View File

@ -14,6 +14,7 @@ class IconGroupRemoveTip {
constructor(group: Group) {
$assert(group, 'group can not be null');
this._group = group;
this._activeIcon = null;
}
show(topicId: number, icon: ImageIcon) {

View File

@ -25,13 +25,14 @@ import Icon from './Icon';
abstract class ImageIcon implements Icon {
private _image: Image;
private _group: IconGroup;
private _group: IconGroup | null;
constructor(url: string) {
$assert(url, 'image url can not be null');
this._image = new Image();
this._image.setHref(url);
this._image.setSize(ImageIcon.SIZE, ImageIcon.SIZE);
this._group = null;
}
getElement(): ElementClass {
@ -42,7 +43,7 @@ abstract class ImageIcon implements Icon {
this._group = group;
}
getGroup(): IconGroup {
getGroup(): IconGroup | null {
return this._group;
}

View File

@ -43,9 +43,9 @@ export type MindplotWebComponentInterface = {
class MindplotWebComponent extends HTMLElement {
private _shadowRoot: ShadowRoot;
private _designer: Designer;
private _designer: Designer | null;
private saveRequired: boolean;
private _saveRequired: boolean;
private _isLoaded: boolean;
@ -62,12 +62,15 @@ class MindplotWebComponent extends HTMLElement {
wrapper.setAttribute('id', 'mindplot');
this._shadowRoot.appendChild(wrapper);
this._isLoaded = false;
this._saveRequired = false;
this._designer = null;
}
/**
* @returns the designer
*/
getDesigner(): Designer {
getDesigner(): Designer | null {
return this._designer;
}
@ -124,24 +127,24 @@ class MindplotWebComponent extends HTMLElement {
}
setSaveRequired(value: boolean) {
this.saveRequired = value;
this._saveRequired = value;
}
getSaveRequired() {
return this.saveRequired;
return this._saveRequired;
}
loadMap(id: string): Promise<void> {
const instance = PersistenceManager.getInstance();
return instance.load(id).then((mindmap) => this._designer.loadMap(mindmap));
return instance.load(id).then((mindmap) => this._designer!.loadMap(mindmap));
}
save(saveHistory: boolean) {
if (!saveHistory && !this.getSaveRequired()) return;
console.log('Saving...');
// Load map content ...
const mindmap = this._designer.getMindmap();
const mindmapProp = this._designer.getMindmapProperties();
const mindmap = this._designer!.getMindmap();
const mindmapProp = this._designer!.getMindmapProperties();
// Display save message ..
if (saveHistory) {
@ -166,7 +169,7 @@ class MindplotWebComponent extends HTMLElement {
}
unlockMap() {
const mindmap = this._designer.getMindmap();
const mindmap = this._designer!.getMindmap();
const persistenceManager = PersistenceManager.getInstance();
// If the map could not be loaded, partial map load could happen.

View File

@ -256,31 +256,35 @@ class MultitTextEditor {
// eslint-disable-next-line no-use-before-define
private static instance: MultitTextEditor = new MultitTextEditor();
private component: EditorComponent | null;
private _component: EditorComponent | null;
constructor() {
this._component = null;
}
static getInstance(): MultitTextEditor {
return MultitTextEditor.instance;
}
isActive(): boolean {
return this.component !== null;
return this._component !== null;
}
show(topic: Topic, textOverwrite?: string): void {
// Is it active ?
if (this.component) {
if (this._component) {
console.error('Editor was already displayed. Please, clouse it');
this.component.close(false);
this._component.close(false);
}
// Create a new instance
this.component = new EditorComponent(topic);
this.component.show(textOverwrite);
this._component = new EditorComponent(topic);
this._component.show(textOverwrite);
}
close(update: boolean): void {
if (this.component) {
this.component.close(update);
this.component = null;
if (this._component) {
this._component.close(update);
this._component = null;
}
}
}

View File

@ -46,7 +46,7 @@ abstract class NodeGraph {
this._options = options;
this._mouseEvents = true;
this.setModel(nodeModel);
this._model = nodeModel;
this._onFocus = false;
this._size = { width: 50, height: 20 };
}
@ -70,7 +70,6 @@ abstract class NodeGraph {
}
get2DElement(): ElementClass {
$assert(this._elem2d, 'NodeGraph has not been initialized properly');
return this._elem2d;
}
@ -118,7 +117,7 @@ abstract class NodeGraph {
return this._model;
}
setModel(model: NodeModel) {
setModel(model: NodeModel): void {
$assert(model, 'Model can not be null');
this._model = model;
}

View File

@ -36,7 +36,7 @@ class ControlPivotLine {
private _pivotType: PivotType;
private _workspace: Workspace;
private _workspace: Workspace | null;
private _relationship: Relationship;
@ -93,6 +93,9 @@ class ControlPivotLine {
this._mouseMoveHandler = (e: MouseEvent) => this.mouseMoveHandler(e);
this._mouseUpHandler = () => this.mouseUpHandler();
this._mouseDownHandler = (event: MouseEvent) => this.mouseDownHandler(event);
this._isVisible = false;
this._workspace = null;
}
private mouseDownHandler(event: MouseEvent) {
@ -153,7 +156,7 @@ class ControlPivotLine {
}
private mouseMoveHandler(event: MouseEvent) {
const screen = this._workspace.getScreenManager();
const screen = this._workspace!.getScreenManager();
const mousePosition = screen.getWorkspaceMousePosition(event);
// Update relatioship position ...
@ -213,8 +216,6 @@ class RelationshipControlPoints {
private _relationship: Relationship;
private _relationshipLinePositions: [PositionType, PositionType];
constructor(relationship: Relationship) {
this._relationship = relationship;
const startControlLine = new ControlPivotLine(
@ -278,10 +279,6 @@ class RelationshipControlPoints {
getControlPointPosition(pivotType: PivotType): PositionType {
return this._pivotLines[pivotType].getPosition();
}
getRelationshipPosition(index: number): PositionType {
return { ...this._relationshipLinePositions[index] };
}
}
export default RelationshipControlPoints;

View File

@ -32,7 +32,7 @@ class RelationshipPivot {
private _onClickEvent: (event: MouseEvent) => void;
private _onTopicClick: (event: MouseEvent) => void;
private _onTopicClick: (event: MouseEvent, targetTopic: Topic) => void;
private _sourceTopic: Topic | null;
@ -49,6 +49,7 @@ class RelationshipPivot {
this._mouseMoveEvent = this.mouseMoveHandler.bind(this);
this._onClickEvent = this.cleanOnMouseClick.bind(this);
this._onTopicClick = this._connectOnFocus.bind(this);
this._sourceTopic = null;
}
start(sourceTopic: Topic, targetPos: Point) {
@ -156,7 +157,7 @@ class RelationshipPivot {
return Shape.calculateRelationShipPointCoordinates(sourceTopic, point);
}
private _connectOnFocus(event: string, targetTopic: Topic): void {
private _connectOnFocus(event: MouseEvent, targetTopic: Topic): void {
const sourceTopic = this._sourceTopic;
const mindmap = this._designer.getMindmap();

View File

@ -39,6 +39,7 @@ class RESTPersistenceManager extends PersistenceManager {
this.documentUrl = options.documentUrl;
this.revertUrl = options.revertUrl;
this.lockUrl = options.lockUrl;
this.onSave = false;
}
saveMapXml(mapId: string, mapXml: Document, pref: string, saveHistory: boolean, events): void {

View File

@ -52,6 +52,7 @@ class ScreenManager {
event.preventDefault();
},
);
this._scale = 1;
}
/**
@ -65,7 +66,6 @@ class ScreenManager {
}
setScale(scale: number) {
$assert(scale, 'Screen scale can not be null');
this._scale = scale;
}

View File

@ -157,7 +157,6 @@ class StandaloneActionDispatcher extends ActionDispatcher {
this.execute(command);
}
/** */
changeBackgroundColorToTopic(topicsIds: number[], color: string | undefined) {
const commandFunc = (topic: Topic, value: string | undefined) => {
const result = topic.getBackgroundColor();

View File

@ -67,11 +67,11 @@ abstract class Topic extends NodeGraph {
private _text: Text | null;
private _iconsGroup: IconGroup;
private _iconsGroup!: IconGroup;
private _connector: ShirinkConnector;
private _connector!: ShirinkConnector;
private _outgoingLine: ConnectionLine | null;
private _outgoingLine!: ConnectionLine | null;
constructor(model: NodeModel, options: NodeOption) {
super(model, options);
@ -83,7 +83,7 @@ abstract class Topic extends NodeGraph {
// Position a topic ....
const pos = model.getPosition();
if (pos != null && this.isCentralTopic()) {
if (pos && this.isCentralTopic()) {
this.setPosition(pos);
}
@ -121,7 +121,7 @@ abstract class Topic extends NodeGraph {
protected redrawShapeType() {
const oldInnerShape = this.getInnerShape();
if (oldInnerShape != null) {
if (oldInnerShape) {
this._removeInnerShape();
// Create a new one ...
@ -208,7 +208,7 @@ abstract class Topic extends NodeGraph {
}
getInnerShape(): ElementClass {
if (!$defined(this._innerShape)) {
if (!this._innerShape) {
// Create inner box.
this._innerShape = this._buildShape(TopicConfig.INNER_RECT_ATTRIBUTES, this.getShapeType());
@ -285,16 +285,17 @@ abstract class Topic extends NodeGraph {
private getOrBuildIconGroup(): Group {
if (!this._iconsGroup) {
this._iconsGroup = this._buildIconGroup();
const iconGroup = this._buildIconGroup();
const group = this.get2DElement();
group.append(this._iconsGroup.getNativeElement());
this._iconsGroup.moveToFront();
group.append(iconGroup.getNativeElement());
iconGroup.moveToFront();
this._iconsGroup = iconGroup;
}
return this._iconsGroup;
}
private getIconGroup(): IconGroup {
private getIconGroup(): IconGroup | null {
return this._iconsGroup;
}
@ -351,7 +352,7 @@ abstract class Topic extends NodeGraph {
// Removing the icon from UI
const iconGroup = this.getIconGroup();
if ($defined(iconGroup)) {
if (iconGroup) {
iconGroup.removeIconByModel(featureModel);
}
this.redraw();
@ -837,7 +838,7 @@ abstract class Topic extends NodeGraph {
setBranchVisibility(value: boolean): void {
let current: Topic = this;
let parent: Topic | null = this;
while (parent != null && !parent.isCentralTopic()) {
while (parent && !parent.isCentralTopic()) {
current = parent;
parent = current.getParent();
}
@ -896,8 +897,8 @@ abstract class Topic extends NodeGraph {
const sourceParent = sourceTopic.getModel().getParent();
relationship.setVisibility(
value &&
(targetParent == null || !targetParent.areChildrenShrunken()) &&
(sourceParent == null || !sourceParent.areChildrenShrunken()),
(!targetParent || !targetParent.areChildrenShrunken()) &&
(!sourceParent || !sourceParent.areChildrenShrunken()),
fade,
);
});
@ -1242,7 +1243,7 @@ abstract class Topic extends NodeGraph {
// Force the repaint in case that the main topic color has changed.
if (this.getParent()) {
this._connector.setColor(borderColor);
this._connector!.setColor(borderColor);
if (this.getParent()?.isCentralTopic()) {
this._outgoingLine?.redraw();

View File

@ -33,7 +33,7 @@ class Workspace {
private _eventsEnabled: boolean;
private _visibleAreaSize: SizeType;
private _visibleAreaSize!: SizeType;
private _renderQueue: Element2D[];
@ -61,8 +61,9 @@ class Workspace {
workspace.addItAsChildTo(divContainer);
this.setZoom(zoom, true);
this._renderQueue = [];
this._eventsEnabled = false;
this._queueRenderEnabled = false;
}
private _adjustWorkspace(): void {
@ -163,11 +164,11 @@ class Workspace {
}
}
addEvent(type: string, listener: (event: Event) => void): void {
addEvent(type: string, listener: (event: MouseEvent) => void): void {
this._workspace.addEvent(type, listener);
}
removeEvent(type: string, listener: (event: Event) => void): void {
removeEvent(type: string, listener: (event: MouseEvent) => void): void {
$assert(type, 'type can not be null');
$assert(listener, 'listener can not be null');
this._workspace.removeEvent(type, listener);

View File

@ -15,7 +15,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { $assert, $defined } from '@wisemapping/core-js';
import { $assert } from '@wisemapping/core-js';
import Point from '@wisemapping/web2d';
import Command from '../Command';
import CommandContext from '../CommandContext';
@ -30,18 +30,10 @@ class DragTopicCommand extends Command {
private _order: number;
/**
* @classdesc This command class handles do/undo of dragging a topic to a new position.
* @constructs
*/
constructor(topicId: number, position: Point, order: number, parentTopic: Topic) {
$assert(topicId, 'topicId must be defined');
super();
this._topicsId = topicId;
if ($defined(parentTopic)) {
this._parentId = parentTopic.getId();
}
this._parentId = parentTopic ? parentTopic.getId() : null;
this._position = position;
this._order = order;
@ -59,7 +51,7 @@ class DragTopicCommand extends Command {
const origPosition = topic.getPosition();
// Disconnect topic ..
if ($defined(origParentTopic)) {
if (origParentTopic) {
commandContext.disconnect(topic);
}

View File

@ -15,37 +15,28 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { $assert, $defined } from '@wisemapping/core-js';
import Command from '../Command';
import CommandContext from '../CommandContext';
import Topic from '../Topic';
type CommandTypes = string | object | boolean | number | undefined;
class GenericFunctionCommand extends Command {
private _value: CommandTypes;
class GenericFunctionCommand<T> extends Command {
private _value: T;
private _topicsIds: number[];
private _commandFunc: (topic: Topic, value: CommandTypes) => CommandTypes;
private _commandFunc: (topic: Topic, value: T) => T;
private _oldValues: CommandTypes[];
private _oldValues: T[];
private _applied: boolean;
constructor(
commandFunc: (topic: Topic, value: CommandTypes) => CommandTypes,
topicsIds: number[],
value: CommandTypes,
) {
$assert(commandFunc, 'commandFunc must be defined');
$assert($defined(topicsIds), 'topicsIds must be defined');
constructor(commandFunc: (topic: Topic, value: T) => T, topicsIds: number[], value: T) {
super();
this._value = value;
this._topicsIds = topicsIds;
this._commandFunc = commandFunc;
this._oldValues = [];
this._applied = false;
}
/**

View File

@ -25,7 +25,7 @@ class RemoveFeatureFromTopicCommand extends Command {
private _featureId: number;
private _oldFeature: FeatureModel;
private _oldFeature: FeatureModel | null;
/**
* @classdesc This command handles do/undo of removing a feature from a topic, e.g. an icon or
@ -38,6 +38,7 @@ class RemoveFeatureFromTopicCommand extends Command {
super();
this._topicId = topicId;
this._featureId = featureId;
this._oldFeature = null;
}
/**
@ -56,7 +57,7 @@ class RemoveFeatureFromTopicCommand extends Command {
*/
undoExecute(commandContext: CommandContext) {
const topic = commandContext.findTopics([this._topicId])[0];
topic.addFeature(this._oldFeature);
topic.addFeature(this._oldFeature!);
}
}

View File

@ -22,11 +22,11 @@ import Font from './freemind/Font';
class FreemindExporter extends Exporter {
private mindmap: Mindmap;
private nodeMap: Map<number, FreeminNode>;
private nodeMap!: Map<number, FreeminNode>;
private version: VersionNumber = FreemindConstant.SUPPORTED_FREEMIND_VERSION;
private objectFactory: ObjectFactory;
private objectFactory!: ObjectFactory;
private static wisweToFreeFontSize: Map<number, number> = new Map<number, number>();

View File

@ -1,43 +1,43 @@
export default class Arrowlink {
protected COLOR: string;
protected COLOR: string | undefined;
protected DESTINATION: string;
protected DESTINATION: string | undefined;
protected ENDARROW: string;
protected ENDARROW: string | undefined;
protected ENDINCLINATION: string;
protected ENDINCLINATION: string | undefined;
protected ID: string;
protected ID: string | undefined;
protected STARTARROW: string;
protected STARTARROW: string | undefined;
protected STARTINCLINATION: string;
protected STARTINCLINATION: string | undefined;
getColor(): string {
getColor(): string | undefined {
return this.COLOR;
}
getDestination(): string {
getDestination(): string | undefined {
return this.DESTINATION;
}
getEndarrow(): string {
getEndarrow(): string | undefined {
return this.ENDARROW;
}
getEndInclination(): string {
getEndInclination(): string | undefined {
return this.ENDINCLINATION;
}
getId(): string {
getId(): string | undefined {
return this.ID;
}
getStartarrow(): string {
getStartarrow(): string | undefined {
return this.STARTARROW;
}
getStartinclination(): string {
getStartinclination(): string | undefined {
return this.STARTINCLINATION;
}
@ -72,8 +72,12 @@ export default class Arrowlink {
toXml(document: Document): HTMLElement {
const arrowlinkElem = document.createElement('arrowlink');
arrowlinkElem.setAttribute('DESTINATION', this.DESTINATION);
arrowlinkElem.setAttribute('STARTARROW', this.STARTARROW);
if (this.DESTINATION) {
arrowlinkElem.setAttribute('DESTINATION', this.DESTINATION);
}
if (this.STARTARROW) {
arrowlinkElem.setAttribute('STARTARROW', this.STARTARROW);
}
if (this.COLOR) {
arrowlinkElem.setAttribute('COLOR', this.COLOR);

View File

@ -1,7 +1,7 @@
export default class Cloud {
protected COLOR: string;
protected COLOR: string | undefined;
getColor(): string {
getColor(): string | undefined {
return this.COLOR;
}
@ -12,8 +12,9 @@ export default class Cloud {
toXml(document: Document): HTMLElement {
// Set node attributes
const cloudElem = document.createElement('cloud');
cloudElem.setAttribute('COLOR', this.COLOR);
if (this.COLOR) {
cloudElem.setAttribute('COLOR', this.COLOR);
}
return cloudElem;
}

View File

@ -1,19 +1,36 @@
/*
* Copyright [2021] [wisemapping]
*
* Licensed under WiseMapping Public License, Version 1.0 (the "License").
* It is basically the Apache License, Version 2.0 (the "License") plus the
* "powered by wisemapping" text requirement on every single page;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the license at
*
* http://www.wisemapping.org/license
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export default class Edge {
protected COLOR: string;
protected COLOR: string | undefined;
protected STYLE: string;
protected STYLE: string | undefined;
protected WIDTH: string;
protected WIDTH: string | undefined;
getColor(): string {
getColor(): string | undefined {
return this.COLOR;
}
getStyle(): string {
getStyle(): string | undefined {
return this.STYLE;
}
getWidth(): string {
getWidth(): string | undefined {
return this.WIDTH;
}
@ -32,10 +49,15 @@ export default class Edge {
toXml(document: Document): HTMLElement {
// Set node attributes
const edgeElem = document.createElement('edge');
edgeElem.setAttribute('COLOR', this.COLOR);
if (this.STYLE) edgeElem.setAttribute('STYLE', this.STYLE);
if (this.WIDTH) edgeElem.setAttribute('WIDTH', this.WIDTH);
if (this.COLOR) {
edgeElem.setAttribute('COLOR', this.COLOR);
}
if (this.STYLE) {
edgeElem.setAttribute('STYLE', this.STYLE);
}
if (this.WIDTH) {
edgeElem.setAttribute('WIDTH', this.WIDTH);
}
return edgeElem;
}

View File

@ -1,11 +1,11 @@
export default class Font {
protected BOLD?: string;
protected BOLD: string | undefined;
protected ITALIC?: string;
protected ITALIC: string | undefined;
protected NAME?: string;
protected NAME: string | undefined;
protected SIZE: string;
protected SIZE: string | undefined;
getBold(): string | undefined {
return this.BOLD;
@ -19,7 +19,7 @@ export default class Font {
return this.NAME;
}
getSize(): string {
getSize(): string | undefined {
return this.SIZE;
}

View File

@ -1,21 +1,21 @@
import Parameters from './Parameters';
export default class Hook {
protected PARAMETERS: Parameters;
protected PARAMETERS: Parameters | undefined;
protected TEXT: string;
protected TEXT: string | undefined;
protected NAME: string;
protected NAME: string | undefined;
getParameters(): Parameters {
getParameters(): Parameters | undefined {
return this.PARAMETERS;
}
getText(): string {
getText(): string | undefined {
return this.TEXT;
}
getName(): string {
getName(): string | undefined {
return this.NAME;
}

View File

@ -1,7 +1,7 @@
export default class Icon {
protected BUILTIN: string;
protected BUILTIN: string | undefined;
getBuiltin(): string {
getBuiltin(): string | undefined {
return this.BUILTIN;
}
@ -12,8 +12,9 @@ export default class Icon {
toXml(document: Document): HTMLElement {
// Set node attributes
const iconElem = document.createElement('icon');
iconElem.setAttribute('BUILTIN', this.BUILTIN);
if (this.BUILTIN) {
iconElem.setAttribute('BUILTIN', this.BUILTIN);
}
return iconElem;
}

View File

@ -8,15 +8,15 @@ import Node, { Choise } from './Node';
import Richcontent from './Richcontent';
export default class Freemap {
protected node: Node;
protected node: Node | undefined;
protected version: string;
protected version: string | undefined;
getNode(): Node {
getNode(): Node | undefined {
return this.node;
}
getVersion(): string {
getVersion(): string | undefined {
return this.version;
}
@ -38,7 +38,7 @@ export default class Freemap {
document.appendChild(mapElem);
// Create main node
const mainNode: Node = this.node;
const mainNode: Node = this.node!;
mainNode.setCentralTopic(true);
const mainNodeElem = mainNode.toXml(document);
mapElem.appendChild(mainNodeElem);

View File

@ -7,43 +7,43 @@ import Icon from './Icon';
import Richcontent from './Richcontent';
class Node {
protected arrowlinkOrCloudOrEdge: Array<
Arrowlink | Cloud | Edge | Font | Hook | Icon | Richcontent | this
>;
protected arrowlinkOrCloudOrEdge:
| Array<Arrowlink | Cloud | Edge | Font | Hook | Icon | Richcontent | this>
| undefined;
protected BACKGROUND_COLOR: string;
protected BACKGROUND_COLOR: string | undefined;
protected COLOR: string;
protected COLOR: string | undefined;
protected FOLDED: string;
protected FOLDED: string | undefined;
protected ID: string;
protected ID: string | undefined;
protected LINK: string;
protected LINK: string | undefined;
protected POSITION: string;
protected POSITION: string | undefined;
protected STYLE: string;
protected STYLE: string | undefined;
protected TEXT: string;
protected TEXT: string | undefined;
protected CREATED: string;
protected CREATED: string | undefined;
protected MODIFIED: string;
protected MODIFIED: string | undefined;
protected HGAP: string;
protected HGAP: string | undefined;
protected VGAP: string;
protected VGAP: string | undefined;
protected WCOORDS: string;
protected WCOORDS: string | undefined;
protected WORDER: string;
protected WORDER: string | undefined;
protected VSHIFT: string;
protected VSHIFT: string | undefined;
protected ENCRYPTED_CONTENT: string;
protected ENCRYPTED_CONTENT: string | undefined;
private centralTopic: boolean;
private centralTopic = false;
getArrowlinkOrCloudOrEdge(): Array<
/* eslint-disable */
@ -57,67 +57,67 @@ class Node {
return this.arrowlinkOrCloudOrEdge;
}
getBackgroundColor(): string {
getBackgroundColor(): string | undefined {
return this.BACKGROUND_COLOR;
}
getColor(): string {
getColor(): string | undefined {
return this.COLOR;
}
getFolded(): string {
getFolded(): string | undefined {
return this.FOLDED;
}
getId(): string | null {
getId(): string | undefined {
return this.ID;
}
getLink(): string {
getLink(): string | undefined {
return this.LINK;
}
getPosition(): string {
getPosition(): string | undefined {
return this.POSITION;
}
getStyle(): string {
getStyle(): string | undefined {
return this.STYLE;
}
getText(): string {
getText(): string | undefined {
return this.TEXT;
}
getCreated(): string {
getCreated(): string | undefined {
return this.CREATED;
}
getModified(): string {
getModified(): string | undefined {
return this.MODIFIED;
}
getHgap(): string {
getHgap(): string | undefined {
return this.HGAP;
}
getVgap(): string {
getVgap(): string | undefined {
return this.VGAP;
}
getWcoords(): string {
getWcoords(): string | undefined {
return this.WCOORDS;
}
getWorder(): string {
getWorder(): string | undefined {
return this.WORDER;
}
getVshift(): string {
getVshift(): string | undefined {
return this.VSHIFT;
}
getEncryptedContent(): string {
getEncryptedContent(): string | undefined {
return this.ENCRYPTED_CONTENT;
}

View File

@ -1,7 +1,7 @@
export default class Parameters {
protected REMINDUSERAT: number;
protected REMINDUSERAT: number | undefined;
getReminduserat(): number {
getReminduserat(): number | undefined {
return this.REMINDUSERAT;
}

View File

@ -1,13 +1,13 @@
export default class Richcontent {
protected html: string;
protected html: string | undefined;
protected type: string;
protected type: string | undefined;
getHtml(): string {
getHtml(): string | undefined {
return this.html;
}
getType(): string {
getType(): string | undefined {
return this.type;
}
@ -22,9 +22,9 @@ export default class Richcontent {
toXml(document: Document): HTMLElement {
// Set node attributes
const richcontentElem = document.createElement('richcontent');
richcontentElem.setAttribute('TYPE', this.type);
if (this.type) {
richcontentElem.setAttribute('TYPE', this.type);
}
if (this.html) {
const htmlElement: DocumentFragment = document
.createRange()

View File

@ -6,7 +6,6 @@ import NodeModel from '../model/NodeModel';
import FreemindConstant from '../export/freemind/FreemindConstant';
import FreemindMap from '../export/freemind/Map';
import FreemindNode, { Choise } from '../export/freemind/Node';
import FreemindFont from '../export/freemind/Font';
import FreemindEdge from '../export/freemind/Edge';
import FreemindIcon from '../export/freemind/Icon';
import FreemindHook from '../export/freemind/Hook';
@ -21,15 +20,15 @@ import XMLSerializerFactory from '../persistence/XMLSerializerFactory';
import { TopicShapeType } from '../model/INodeModel';
export default class FreemindImporter extends Importer {
private mindmap: Mindmap;
private mindmap!: Mindmap;
private freemindInput: string;
private freemindMap: FreemindMap;
private freemindMap!: FreemindMap;
private nodesmap: Map<string, NodeModel>;
private nodesmap!: Map<string, NodeModel>;
private relationship: Array<RelationshipModel>;
private relationship!: Array<RelationshipModel>;
private idDefault = 0;
@ -47,7 +46,7 @@ export default class FreemindImporter extends Importer {
const freemindDoc = parser.parseFromString(this.freemindInput, 'application/xml');
this.freemindMap = new FreemindMap().loadFromDom(freemindDoc);
const version: string = this.freemindMap.getVersion();
const version: string | undefined = this.freemindMap.getVersion();
if (!version || version.startsWith('freeplane')) {
throw new Error(
@ -60,10 +59,10 @@ export default class FreemindImporter extends Importer {
}
}
const freeNode: FreemindNode = this.freemindMap.getNode();
const freeNode = this.freemindMap.getNode()!;
this.mindmap.setVersion(FreemindConstant.CODE_VERSION);
const wiseTopicId = this.getIdNode(this.freemindMap.getNode());
const wiseTopicId = this.getIdNode(freeNode);
const wiseTopic = this.mindmap.createNode('CentralTopic');
wiseTopic.setPosition(0, 0);
wiseTopic.setId(wiseTopicId);
@ -158,7 +157,7 @@ export default class FreemindImporter extends Importer {
wiseTopic: NodeModel,
centralTopic: boolean,
): void {
const text: string = freeNode.getText();
const text = freeNode.getText();
if (text) {
if (!centralTopic && text.length > 100) {
wiseTopic.setText(text.replace(/([^\n]{1,100})\s/g, '$1\n'));
@ -167,7 +166,7 @@ export default class FreemindImporter extends Importer {
}
}
const bgColor: string = freeNode.getBackgroundColor();
const bgColor = freeNode.getBackgroundColor();
if (bgColor) {
wiseTopic.setBackgroundColor(bgColor);
}
@ -186,7 +185,7 @@ export default class FreemindImporter extends Importer {
// }
// Is there any link...
const url: string = freeNode.getLink();
const url = freeNode.getLink();
if (url) {
const link: FeatureModel = FeatureModelFactory.createModel('link', { url });
wiseTopic.addFeature(link);
@ -214,7 +213,7 @@ export default class FreemindImporter extends Importer {
const wiseChild = mindmap.createNode('MainTopic', wiseId);
const id = child.getId();
if (id !== null) {
if (id !== undefined) {
this.nodesmap.set(id, wiseChild);
}
@ -269,13 +268,15 @@ export default class FreemindImporter extends Importer {
if (child instanceof FreemindIcon) {
const freeIcon: FreemindIcon = child as FreemindIcon;
const iconId: string = freeIcon.getBuiltin();
const wiseIconId = FreemindIconConverter.toWiseId(iconId);
if (wiseIconId) {
const mindmapIcon: FeatureModel = FeatureModelFactory.createModel('icon', {
id: wiseIconId,
});
currentWiseTopic.addFeature(mindmapIcon);
const iconId = freeIcon.getBuiltin();
if (iconId) {
const wiseIconId = FreemindIconConverter.toWiseId(iconId);
if (wiseIconId) {
const mindmapIcon: FeatureModel = FeatureModelFactory.createModel('icon', {
id: wiseIconId,
});
currentWiseTopic.addFeature(mindmapIcon);
}
}
}
@ -283,7 +284,7 @@ export default class FreemindImporter extends Importer {
const hook: FreemindHook = child as FreemindHook;
const mindmapNote: NoteModel = new NoteModel({ text: '' });
let textNote: string = hook.getText();
let textNote = hook.getText();
if (!textNote) {
textNote = FreemindConstant.EMPTY_NOTE;
mindmapNote.setText(textNote);
@ -294,27 +295,28 @@ export default class FreemindImporter extends Importer {
if (child instanceof FreemindRichcontent) {
const type = child.getType();
const html = child.getHtml();
const text = this.html2Text(html);
if (html) {
const text = this.html2Text(html);
switch (type) {
case 'NOTE': {
const noteModel: FeatureModel = FeatureModelFactory.createModel('note', {
text: text || FreemindConstant.EMPTY_NOTE,
});
currentWiseTopic.addFeature(noteModel);
break;
}
switch (type) {
case 'NOTE': {
const noteModel: FeatureModel = FeatureModelFactory.createModel('note', {
text: text || FreemindConstant.EMPTY_NOTE,
});
currentWiseTopic.addFeature(noteModel);
break;
}
case 'NODE': {
currentWiseTopic.setText(text);
break;
}
case 'NODE': {
currentWiseTopic.setText(text);
break;
}
default: {
const noteModel: FeatureModel = FeatureModelFactory.createModel('note', {
text: text || FreemindConstant.EMPTY_NOTE,
});
currentWiseTopic.addFeature(noteModel);
default: {
const noteModel: FeatureModel = FeatureModelFactory.createModel('note', {
text: text || FreemindConstant.EMPTY_NOTE,
});
currentWiseTopic.addFeature(noteModel);
}
}
}
}
@ -322,28 +324,28 @@ export default class FreemindImporter extends Importer {
if (child instanceof FreemindArrowLink) {
const arrow: FreemindArrowLink = child as FreemindArrowLink;
const relationship: RelationshipModel = new RelationshipModel(0, 0);
const destId: string = arrow.getDestination();
const destId = arrow.getDestination();
relationship.setSrcCtrlPoint(destId);
relationship.setDestCtrlPoint(freeParent.getId());
const endinclination: string = arrow.getEndInclination();
const endinclination = arrow.getEndInclination();
if (endinclination) {
const inclination: Array<string> = endinclination.split(';');
relationship.setDestCtrlPoint(`${inclination[0]},${inclination[1]}`);
}
const startinclination: string = arrow.getStartinclination();
const startinclination = arrow.getStartinclination();
if (startinclination) {
const inclination: Array<string> = startinclination.split(';');
relationship.setSrcCtrlPoint(`${inclination[0]},${inclination[1]}`);
}
const endarrow: string = arrow.getEndarrow();
const endarrow = arrow.getEndarrow();
if (endarrow) {
relationship.setEndArrow(endarrow.toLowerCase() !== 'none');
}
const startarrow: string = arrow.getStartarrow();
const startarrow = arrow.getStartarrow();
if (startarrow) {
relationship.setStartArrow(startarrow.toLowerCase() !== 'none');
}
@ -375,7 +377,7 @@ export default class FreemindImporter extends Importer {
private getChildrenCountSameSide(freeChilden: Array<Choise>, freeChild: FreemindNode): number {
let result = 0;
let childSide: string = freeChild.getPosition();
let childSide = freeChild.getPosition();
if (!childSide) {
childSide = FreemindConstant.POSITION_RIGHT;
@ -410,55 +412,54 @@ export default class FreemindImporter extends Importer {
return result;
}
private generateFontStyle(node: FreemindNode, font: FreemindFont | undefined): string {
const fontStyle: Array<string> = [];
// private generateFontStyle(node: FreemindNode, font: FreemindFont | undefined): string {
// const fontStyle: Array<string> = [];
// Font family
if (font) {
const name = font.getName();
if (name) {
fontStyle.push(name);
}
}
fontStyle.push(';');
// // Font family
// if (font) {
// const name = font.getName();
// if (name) {
// fontStyle.push(name);
// }
// }
// fontStyle.push(';');
// Font Size
if (font) {
const fontSize: number =
!font.getSize() || parseInt(font.getSize(), 10) < 8
? FreemindConstant.FONT_SIZE_NORMAL
: parseInt(font.getSize(), 10);
let wiseFontSize: number = FreemindConstant.FONT_SIZE_SMALL;
if (fontSize >= 24) {
wiseFontSize = FreemindConstant.FONT_SIZE_HUGE;
}
if (fontSize >= 16) {
wiseFontSize = FreemindConstant.FONT_SIZE_LARGE;
}
if (fontSize >= 8) {
wiseFontSize = FreemindConstant.FONT_SIZE_NORMAL;
}
fontStyle.push(wiseFontSize.toString());
}
fontStyle.push(';');
// // Font Size
// if (font) {
// const size = font.getSize();
// const fontSize: number =
// !size || parseInt(size, 10) < 8 ? FreemindConstant.FONT_SIZE_NORMAL : parseInt(size, 10);
// let wiseFontSize: number = FreemindConstant.FONT_SIZE_SMALL;
// if (fontSize >= 24) {
// wiseFontSize = FreemindConstant.FONT_SIZE_HUGE;
// }
// if (fontSize >= 16) {
// wiseFontSize = FreemindConstant.FONT_SIZE_LARGE;
// }
// if (fontSize >= 8) {
// wiseFontSize = FreemindConstant.FONT_SIZE_NORMAL;
// }
// fontStyle.push(wiseFontSize.toString());
// }
// fontStyle.push(';');
// Font Color
const color: string = node.getColor();
if (color && color !== '') {
fontStyle.push(color);
}
fontStyle.push(';');
// // Font Color
// const color = node.getColor();
// if (color && color !== '') {
// fontStyle.push(color);
// }
// fontStyle.push(';');
// Font Italic
if (font) {
const hasItalic = Boolean(font.getItalic());
fontStyle.push(hasItalic ? FreemindConstant.ITALIC : '');
}
fontStyle.push(';');
// // Font Italic
// if (font) {
// const hasItalic = Boolean(font.getItalic());
// fontStyle.push(hasItalic ? FreemindConstant.ITALIC : '');
// }
// fontStyle.push(';');
const result: string = fontStyle.join('');
return result;
}
// const result: string = fontStyle.join('');
// return result;
// }
private convertPosition(
wiseParent: NodeModel,
@ -471,7 +472,7 @@ export default class FreemindImporter extends Importer {
FreemindConstant.CENTRAL_TO_TOPIC_DISTANCE +
(depth - 1) * FreemindConstant.TOPIC_TO_TOPIC_DISTANCE;
if (depth === 1) {
const side: string = freeChild.getPosition();
const side = freeChild.getPosition();
x *= side && FreemindConstant.POSITION_LEFT === side ? -1 : 1;
} else {
const position = wiseParent.getPosition();

View File

@ -1,12 +1,9 @@
import Mindmap from '../model/Mindmap';
import XMLSerializerFactory from '../persistence/XMLSerializerFactory';
import Importer from './Importer';
export default class WisemappingImporter extends Importer {
private wisemappingInput: string;
private mindmap: Mindmap;
constructor(map: string) {
super();
this.wisemappingInput = map;
@ -17,11 +14,11 @@ export default class WisemappingImporter extends Importer {
const wiseDoc = parser.parseFromString(this.wisemappingInput, 'application/xml');
const serialize = XMLSerializerFactory.createInstanceFromDocument(wiseDoc);
this.mindmap = serialize.loadFromDom(wiseDoc, nameMap);
const mindmap = serialize.loadFromDom(wiseDoc, nameMap);
this.mindmap.setDescription(description);
mindmap.setDescription(description);
const mindmapToXml = serialize.toXML(this.mindmap);
const mindmapToXml = serialize.toXML(mindmap);
const xmlStr = new XMLSerializer().serializeToString(mindmapToXml);
return Promise.resolve(xmlStr);

View File

@ -22,10 +22,11 @@ import EventBus from './EventBus';
import LayoutManager from './LayoutManager';
class EventBusDispatcher {
private _layoutManager: LayoutManager;
private _layoutManager: LayoutManager | null;
constructor() {
this.registerBusEvents();
this._layoutManager = null;
}
setLayoutManager(layoutManager: LayoutManager) {
@ -44,19 +45,19 @@ class EventBusDispatcher {
}
private _topicResizeEvent(args: { node: Topic; size: SizeType }) {
this._layoutManager.updateNodeSize(args.node.getId(), args.size);
this._layoutManager!.updateNodeSize(args.node.getId(), args.size);
}
private _topicMoved(args: { node: Topic; position: PositionType }) {
this._layoutManager.moveNode(args.node.getId(), args.position);
this._layoutManager!.moveNode(args.node.getId(), args.position);
}
private _topicDisconect(node: Topic) {
this._layoutManager.disconnectNode(node.getId());
this._layoutManager!.disconnectNode(node.getId());
}
private _topicConnected(args: { parentNode: Topic; childNode: Topic }) {
this._layoutManager.connectNode(
this._layoutManager!.connectNode(
args.parentNode.getId(),
args.childNode.getId(),
args.childNode.getOrder(),
@ -64,27 +65,27 @@ class EventBusDispatcher {
}
private _childShrinked(node: Topic) {
this._layoutManager.updateShrinkState(node.getId(), node.areChildrenShrunken());
this._layoutManager!.updateShrinkState(node.getId(), node.areChildrenShrunken());
}
private _topicAdded(node: Topic) {
// Central topic must not be added twice ...
if (node.getId() !== 0) {
this._layoutManager.addNode(node.getId(), { width: 10, height: 10 }, node.getPosition());
this._layoutManager.updateShrinkState(node.getId(), node.areChildrenShrunken());
this._layoutManager!.addNode(node.getId(), { width: 10, height: 10 }, node.getPosition());
this._layoutManager!.updateShrinkState(node.getId(), node.areChildrenShrunken());
}
}
private _topicRemoved(node: Topic) {
this._layoutManager.removeNode(node.getId());
this._layoutManager!.removeNode(node.getId());
}
private _forceLayout() {
this._layoutManager.layout(true);
this._layoutManager!.layout(true);
}
getLayoutManager() {
return this._layoutManager;
getLayoutManager(): LayoutManager {
return this._layoutManager!;
}
}

View File

@ -24,21 +24,17 @@ class Node {
private _id: number;
// eslint-disable-next-line no-use-before-define
_parent: Node | null;
_parent!: Node | null;
private _sorter: ChildrenSorterStrategy;
private _properties;
// eslint-disable-next-line no-use-before-define
_children: Node[];
_children!: Node[];
constructor(id: number, size: SizeType, position: PositionType, sorter: ChildrenSorterStrategy) {
$assert(typeof id === 'number' && Number.isFinite(id), 'id can not be null');
$assert(size, 'size can not be null');
$assert(position, 'position can not be null');
$assert(sorter, 'sorter can not be null');
this._id = id;
this._sorter = sorter;
this._properties = {};
@ -49,7 +45,7 @@ class Node {
}
/** */
getId() {
getId(): number {
return this._id;
}
@ -74,7 +70,7 @@ class Node {
}
/** */
setShrunken(value) {
setShrunken(value: boolean) {
this._setProperty('shrink', value);
}
@ -132,31 +128,27 @@ class Node {
}
/** */
hasSizeChanged() {
hasSizeChanged(): boolean {
return this._isPropertyChanged('size');
}
/** */
getPosition() {
getPosition(): PositionType {
return this._getProperty('position');
}
/** */
setSize(size) {
setSize(size: SizeType) {
$assert($defined(size), 'Size can not be null');
this._setProperty('size', { ...size });
}
/** */
getSize() {
getSize(): SizeType {
return this._getProperty('size');
}
/** */
setFreeDisplacement(displacement) {
$assert($defined(displacement), 'Position can not be null');
$assert($defined(displacement.x), 'x can not be null');
$assert($defined(displacement.y), 'y can not be null');
setFreeDisplacement(displacement: PositionType) {
const oldDisplacement = this.getFreeDisplacement();
const newDisplacement = {
x: oldDisplacement.x + displacement.x,
@ -177,12 +169,7 @@ class Node {
return freeDisplacement || { x: 0, y: 0 };
}
/** */
setPosition(position: PositionType) {
$assert($defined(position), 'Position can not be null');
$assert($defined(position.x), 'x can not be null');
$assert($defined(position.y), 'y can not be null');
// This is a performance improvement to avoid movements that really could be avoided.
const currentPos = this.getPosition();
if (
@ -213,7 +200,7 @@ class Node {
this._properties[key] = prop;
}
_getProperty(key) {
_getProperty(key: string) {
const prop = this._properties[key];
return $defined(prop) ? prop.value : null;
}
@ -229,7 +216,7 @@ class Node {
}
/** @return {String} returns id, order, position, size and shrink information */
toString() {
toString(): string {
return `[id:${this.getId()}, order:${this.getOrder()}, position: {${this.getPosition().x},${
this.getPosition().y
}}, size: {${this.getSize().width},${

View File

@ -15,21 +15,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { $assert, $defined } from '@wisemapping/core-js';
import { $assert } from '@wisemapping/core-js';
import PositionType from '../PositionType';
import Node from './Node';
class RootedTreeSet {
private _rootNodes: Node[];
protected _children: Node[];
protected _children!: Node[];
constructor() {
this._rootNodes = [];
}
setRoot(root: Node) {
$assert(root, 'root can not be null');
this._rootNodes.push(this._decodate(root));
}
@ -43,7 +42,6 @@ class RootedTreeSet {
}
add(node: Node) {
$assert(node, 'node can not be null');
if (this.find(node.getId(), false)) {
throw new Error(`node already exits with this id. Id:${node.getId()}: ${this.dump()}`);
}
@ -56,7 +54,6 @@ class RootedTreeSet {
* @throws will throw an error if nodeId is null or undefined
*/
remove(nodeId: number) {
$assert($defined(nodeId), 'nodeId can not be null');
const node = this.find(nodeId);
this._rootNodes = this._rootNodes.filter((n) => n !== node);
}
@ -69,9 +66,6 @@ class RootedTreeSet {
* @throws will throw an error if node with id childId is already a child of parent
*/
connect(parentId: number, childId: number) {
$assert($defined(parentId), 'parent can not be null');
$assert($defined(childId), 'child can not be null');
const parent = this.find(parentId);
const child = this.find(childId, true);
$assert(
@ -90,7 +84,6 @@ class RootedTreeSet {
* @throws will throw an error if node is not connected
*/
disconnect(nodeId: number) {
$assert($defined(nodeId), 'nodeId can not be null');
const node = this.find(nodeId);
$assert(node._parent, 'Node is not connected');
@ -107,8 +100,6 @@ class RootedTreeSet {
* @return node
*/
find(id: number, validate = true): Node {
$assert($defined(id), 'id can not be null');
const graphs = this._rootNodes;
let result: Node | null = null;
for (let i = 0; i < graphs.length; i++) {

View File

@ -35,8 +35,6 @@ class NodeModel extends INodeModel {
private _parent: NodeModel | null;
constructor(type: NodeModelType, mindmap: Mindmap, id?: number) {
$assert(type, 'Node type can not be null');
$assert(mindmap, 'mindmap can not be null');
super(mindmap);
this._properties = {};
@ -46,6 +44,7 @@ class NodeModel extends INodeModel {
this._children = [];
this._features = [];
this._parent = null;
}
/**

View File

@ -20,7 +20,6 @@ import Mindmap from '../model/Mindmap';
import FeatureModelFactory from '../model/FeatureModelFactory';
import NodeModel from '../model/NodeModel';
import XMLMindmapSerializer from './XMLMindmapSerializer';
import FeatureModel from '../model/FeatureModel';
class XMLSerializerBeta implements XMLMindmapSerializer {
private static MAP_ROOT_NODE = 'map';
@ -297,12 +296,6 @@ class XMLSerializerBeta implements XMLMindmapSerializer {
return topic;
}
private _deserializeIcon(domElem: Element): FeatureModel {
let icon = domElem.getAttribute('id');
icon = icon ? icon.replace('images/', 'icons/legacy/') : 'missing';
return FeatureModelFactory.createModel('icon', { id: icon });
}
_deserializeLink(domElem: Element) {
return FeatureModelFactory.createModel('link', { url: domElem.getAttribute('url') });
}

View File

@ -33,6 +33,10 @@ class XMLSerializerTango implements XMLMindmapSerializer {
private _idsMap: Record<number, Element>;
constructor() {
this._idsMap = {};
}
toXML(mindmap: Mindmap): Document {
$assert(mindmap, 'Can not save a null mindmap');
@ -242,7 +246,6 @@ class XMLSerializerTango implements XMLMindmapSerializer {
`This seem not to be a map document. Found tag: ${rootElem.tagName}`,
);
this._idsMap = {};
// Start the loading process ...
const version = rootElem.getAttribute('version') || 'pela';
const mindmap = new Mindmap(mapId, version);

View File

@ -20,9 +20,7 @@ import SizeType from '../SizeType';
import Topic from '../Topic';
class LineTopicShape extends Line {
private _topic: Topic;
private _size: SizeType;
private _size: SizeType | null;
constructor(topic: Topic) {
const stokeColor = topic.getConnectionColor();
@ -30,7 +28,7 @@ class LineTopicShape extends Line {
strokeColor: stokeColor,
strokeWidth: 1,
});
this._topic = topic;
this._size = null;
}
setSize(width: number, height: number): void {
@ -39,7 +37,7 @@ class LineTopicShape extends Line {
super.setTo(width, height);
}
getSize() {
getSize(): SizeType | null {
return this._size;
}

View File

@ -83,12 +83,12 @@ class SymmetricTestSuite extends TestSuite {
'Symmetry is not respected',
);
$assert(
manager.find(8).getPosition().y - manager.find(1).getPosition().y ==
manager.find(8).getPosition().y - manager.find(1).getPosition().y ===
-(manager.find(11).getPosition().y - manager.find(1).getPosition().y),
'Symmetry is not respected',
);
$assert(
manager.find(9).getPosition().y - manager.find(1).getPosition().y ==
manager.find(9).getPosition().y - manager.find(1).getPosition().y ===
-(manager.find(11).getPosition().y - manager.find(1).getPosition().y),
'Symmetry is not respected',
);
@ -136,20 +136,20 @@ class SymmetricTestSuite extends TestSuite {
this._plotPrediction(graph1, prediction1a);
$assert(
prediction1a.position.x < manager.find(9).getPosition().x &&
prediction1a.position.y == manager.find(9).getPosition().y,
prediction1a.position.y === manager.find(9).getPosition().y,
'Prediction incorrectly positioned',
);
$assert(prediction1a.order == 0, 'Prediction order should be 0');
$assert(prediction1a.order === 0, 'Prediction order should be 0');
console.log('\tAdded as child of node 1 and dropped at (155, -90):');
const prediction1b = manager.predict(1, null, { x: -155, y: -90 });
this._plotPrediction(graph1, prediction1b);
$assert(
prediction1b.position.x > manager.find(1).getPosition().x &&
prediction1b.position.y == manager.find(1).getPosition().y,
prediction1b.position.y === manager.find(1).getPosition().y,
'Prediction is incorrectly positioned',
);
$assert(prediction1b.order == 0, 'Prediction order should be 0');
$assert(prediction1b.order === 0, 'Prediction order should be 0');
// Graph 2
const graph2 = manager.plot('testSymmetricPredict2', { width: 1000, height: 400 });
@ -161,10 +161,10 @@ class SymmetricTestSuite extends TestSuite {
// Prediction calculator error
$assert(
prediction2d.position.y < manager.find(7).getPosition().y &&
prediction2d.position.x == manager.find(7).getPosition().x,
prediction2d.position.x === manager.find(7).getPosition().x,
'Prediction is incorrectly positioned',
);
$assert(prediction2d.order == 0, 'Prediction order should be 0');
$assert(prediction2d.order === 0, 'Prediction order should be 0');
console.log('\tAdded as child of node 5 and dropped at (375, 15):');
const prediction2a = manager.predict(5, null, { x: 375, y: 15 });
@ -173,10 +173,10 @@ class SymmetricTestSuite extends TestSuite {
$assert(
prediction2a.position.y > manager.find(7).getPosition().y &&
prediction2a.position.y < manager.find(8).getPosition().y &&
prediction2a.position.x == manager.find(7).getPosition().x,
prediction2a.position.x === manager.find(7).getPosition().x,
'Prediction is incorrectly positioned',
);
$assert(prediction2a.order == 1, 'Prediction order should be 1');
$assert(prediction2a.order === 1, 'Prediction order should be 1');
console.log('\tAdded as child of node 5 and dropped at (375, 45):');
const prediction2b = manager.predict(5, null, { x: 375, y: 45 });
@ -184,20 +184,20 @@ class SymmetricTestSuite extends TestSuite {
$assert(
prediction2b.position.y > manager.find(8).getPosition().y &&
prediction2b.position.y < manager.find(11).getPosition().y &&
prediction2b.position.x == manager.find(7).getPosition().x,
prediction2b.position.x === manager.find(7).getPosition().x,
'Prediction is incorrectly positioned',
);
$assert(prediction2b.order == 2, 'Prediction order should be 2');
$assert(prediction2b.order === 2, 'Prediction order should be 2');
console.log('\tAdded as child of node 5 and dropped at (375, 45):');
const prediction2c = manager.predict(5, null, { x: 375, y: 65 });
this._plotPrediction(graph2, prediction2c);
$assert(
prediction2c.position.y > manager.find(11).getPosition().y &&
prediction2c.position.x == manager.find(11).getPosition().x,
prediction2c.position.x === manager.find(11).getPosition().x,
'Prediction is incorrectly positioned',
);
$assert(prediction2c.order == 3, 'Prediction order should be 3');
$assert(prediction2c.order === 3, 'Prediction order should be 3');
// Graph 3
const graph3 = manager.plot('testSymmetricPredict3', { width: 1000, height: 400 });
@ -208,20 +208,20 @@ class SymmetricTestSuite extends TestSuite {
$assert(
prediction3a.position.y > manager.find(5).getPosition().y &&
prediction3a.position.y < manager.find(6).getPosition().y &&
prediction3a.position.x == manager.find(5).getPosition().x,
prediction3a.position.x === manager.find(5).getPosition().x,
'Prediction is incorrectly positioned',
);
$assert(prediction3a.order == 2, 'Prediction order should be 2');
$assert(prediction3a.order === 2, 'Prediction order should be 2');
console.log('\tAdded as child of node 3 and dropped at (255, 110):');
const prediction3b = manager.predict(3, null, { x: 255, y: 110 });
this._plotPrediction(graph3, prediction3b);
$assert(
prediction3b.position.y > manager.find(6).getPosition().y &&
prediction3b.position.x == manager.find(6).getPosition().x,
prediction3b.position.x === manager.find(6).getPosition().x,
'Prediction incorrectly positioned',
);
$assert(prediction3b.order == 3, 'Prediction order should be 3');
$assert(prediction3b.order === 3, 'Prediction order should be 3');
// Graph 4
console.log('\tAdded as child of node 2 and dropped at (-260, 0):');
@ -231,10 +231,10 @@ class SymmetricTestSuite extends TestSuite {
$assert(
prediction4.position.y > manager.find(9).getPosition().y &&
prediction4.position.y < manager.find(10).getPosition().y &&
prediction4.position.x == manager.find(9).getPosition().x,
prediction4.position.x === manager.find(9).getPosition().x,
'Prediction is incorrectly positioned',
);
$assert(prediction4.order == 1, 'Prediction order should be 1');
$assert(prediction4.order === 1, 'Prediction order should be 1');
// Graph 5
console.log('\tPredict nodes added with no position:');
@ -242,40 +242,40 @@ class SymmetricTestSuite extends TestSuite {
const prediction5a = manager.predict(1, null, null);
this._plotPrediction(graph5, prediction5a);
$assert(
prediction5a.position.y == manager.find(1).getPosition().y &&
prediction5a.position.y === manager.find(1).getPosition().y &&
prediction5a.position.x > manager.find(1).getPosition().x,
'Prediction is incorrectly positioned',
);
$assert(prediction5a.order == 0, 'Prediction order should be 0');
$assert(prediction5a.order === 0, 'Prediction order should be 0');
const prediction5b = manager.predict(2, null, null);
this._plotPrediction(graph5, prediction5b);
$assert(
prediction5b.position.y > manager.find(10).getPosition().y &&
prediction5b.position.x < manager.find(2).getPosition().x &&
prediction5b.position.x == manager.find(10).getPosition().x,
prediction5b.position.x === manager.find(10).getPosition().x,
'Prediction is incorrectly positioned',
);
$assert(prediction5b.order == 2, 'Prediction order should be 2');
$assert(prediction5b.order === 2, 'Prediction order should be 2');
const prediction5c = manager.predict(3, null, null);
this._plotPrediction(graph5, prediction5c);
$assert(
prediction5c.position.y > manager.find(6).getPosition().y &&
prediction5c.position.x > manager.find(3).getPosition().x &&
prediction5c.position.x == manager.find(6).getPosition().x,
prediction5c.position.x === manager.find(6).getPosition().x,
'Prediction is incorrectly positioned',
);
$assert(prediction5c.order == 3, 'Prediction order should be 3');
$assert(prediction5c.order === 3, 'Prediction order should be 3');
const prediction5d = manager.predict(10, null, null);
this._plotPrediction(graph5, prediction5d);
$assert(
prediction5d.position.y == manager.find(10).getPosition().y &&
prediction5d.position.y === manager.find(10).getPosition().y &&
prediction5d.position.x < manager.find(10).getPosition().x,
'Prediction is incorrectly positioned',
);
$assert(prediction5d.order == 0, 'Prediction order should be 0');
$assert(prediction5d.order === 0, 'Prediction order should be 0');
console.log('OK!\n\n');
}
@ -295,54 +295,54 @@ class SymmetricTestSuite extends TestSuite {
const prediction1a = manager.predict(1, 2, { x: -250, y: -20 });
this._plotPrediction(graph1, prediction1a);
$assert(
prediction1a.position.x == manager.find(2).getPosition().x &&
prediction1a.position.y == manager.find(2).getPosition().y,
prediction1a.position.x === manager.find(2).getPosition().x &&
prediction1a.position.y === manager.find(2).getPosition().y,
'Prediction position should be the same as node 2',
);
$assert(
prediction1a.order == manager.find(2).getOrder(),
prediction1a.order === manager.find(2).getOrder(),
'Predicition order should be the same as node 2',
);
const prediction1b = manager.predict(1, 2, { x: -250, y: 20 });
this._plotPrediction(graph1, prediction1b);
$assert(
prediction1b.position.x == manager.find(2).getPosition().x &&
prediction1b.position.y == manager.find(2).getPosition().y,
prediction1b.position.x === manager.find(2).getPosition().x &&
prediction1b.position.y === manager.find(2).getPosition().y,
'Prediction position should be the same as node 2',
);
$assert(
prediction1b.order == manager.find(2).getOrder(),
prediction1b.order === manager.find(2).getOrder(),
'Predicition order should be the same as node 2',
);
const prediction1c = manager.predict(0, 2, { x: -100, y: -20 });
this._plotPrediction(graph1, prediction1c);
$assert(
prediction1c.position.x == manager.find(1).getPosition().x &&
prediction1c.position.x === manager.find(1).getPosition().x &&
prediction1c.position.y < manager.find(1).getPosition().y,
'Prediction is incorrectly positioned',
);
$assert(prediction1c.order == 1, 'Prediction order should be 1');
$assert(prediction1c.order === 1, 'Prediction order should be 1');
const prediction1d = manager.predict(0, 2, { x: -100, y: 20 });
this._plotPrediction(graph1, prediction1d);
$assert(
prediction1d.position.x == manager.find(1).getPosition().x &&
prediction1d.position.x === manager.find(1).getPosition().x &&
prediction1d.position.y > manager.find(1).getPosition().y,
'Prediction is incorrectly positioned',
);
$assert(prediction1d.order == 3, 'Prediction order should be 3');
$assert(prediction1d.order === 3, 'Prediction order should be 3');
const prediction1e = manager.predict(1, 2, { x: -250, y: 0 });
this._plotPrediction(graph1, prediction1e);
$assert(
prediction1e.position.x == manager.find(2).getPosition().x &&
prediction1e.position.y == manager.find(2).getPosition().y,
prediction1e.position.x === manager.find(2).getPosition().x &&
prediction1e.position.y === manager.find(2).getPosition().y,
'Prediction position should be the same as node 2',
);
$assert(
prediction1e.order == manager.find(2).getOrder(),
prediction1e.order === manager.find(2).getOrder(),
'Predicition order should be the same as node 2',
);

View File

@ -197,7 +197,7 @@ unencrypted.]]></text>
<topic position="1100,26" order="0" text="but it can't encrypted" shape="line" id="1190504719"/>
<topic position="1190,51" order="1" text="unless you have some UDP SSL thing" shape="line" id="878085542"/>
</topic>
<topic position="-1190,76" order="3" shape="line" id="252326982" bgColor="undefined">
<topic position="-1190,76" order="3" shape="line" id="252326982">
<text><![CDATA[Egerstad said the process of snooping on the traffic is trivial. The problem is not with Tor, which
still works as intended, but with users' expectations: the Tor system is designed to merely
anonymize Internet traffic and does not perform end-to-end

View File

@ -320,7 +320,7 @@
</topic>
</topic>
<topic position="1190,2" order="1" text="Spin-offs" shape="line" id="309">
<topic position="-1190,-10" order="0" text="undefined" shape="line" id="300"/>
<topic position="-1190,-10" order="0" shape="line" id="300"/>
</topic>
<topic position="1280,27" order="2" text="Industry contracts" shape="line" id="310">
<topic position="-1280,-35" order="0" text="Industry revenue per staff " shape="line" id="297"/>
@ -330,7 +330,7 @@
per 1,000 researchers [contracts/researchers]]]></text>
</topic>
<topic position="-1460,15" order="2" text="Share of industry income from foreign companies" shape="line" id="307"/>
<topic position="-1550,40" order="3" text="undefined" shape="line" id="90"/>
<topic position="-1550,40" order="3" shape="line" id="90"/>
<topic position="-1640,65" order="4" text="Difficulties faced by research organization in collaborating with SMEs" shape="line" id="311"/>
</topic>
</topic>

View File

@ -3,7 +3,7 @@
<topic position="-200,-50" order="1" shape="rectangle" id="5" bgColor="#cccccc">
<text><![CDATA[Baraloto et al. 2010. Functional trait variation and sampling strategies in species-rich plant
communities]]></text>
<topic position="-200,-350" order="1" text="undefined" shape="line" id="6"/>
<topic position="-200,-350" order="1" shape="line" id="6"/>
<topic position="290,-150" order="0" shape="line" id="7">
<text><![CDATA[However, the fast pace of
development of plant trait meta-analyses also suggests that
@ -46,7 +46,6 @@ tb.]]></text>
failed to accurately estimate the variance of trait values. This
indicates that in situations where accurate estimation of plotlevel
variance is desired, complete censuses are essential.]]></text>
<note><![CDATA[undefined]]></note>
</topic>
<topic position="830,0" order="6" shape="line" id="15">
<text><![CDATA[We suggest that, in these studies,
@ -104,9 +103,9 @@ construir classificações mais detalhadas e com mais dados confiáveis.
colonization groups based on the timing of seedling, sapling, and
tree recruitment in secondary forests.]]></text>
</topic>
<topic position="740,-12" order="5" text="undefined" shape="line" id="27"/>
<topic position="830,13" order="6" text="undefined" shape="line" id="28"/>
<topic position="920,38" order="7" text="undefined" shape="line" id="29"/>
<topic position="740,-12" order="5" shape="line" id="27"/>
<topic position="830,13" order="6" shape="line" id="28"/>
<topic position="920,38" order="7" shape="line" id="29"/>
<topic position="1010,63" order="8" shape="line" id="30">
<text><![CDATA[Classifying functional types
based on functional traits with low plasticity, such as wood density

View File

@ -1,17 +1,17 @@
<map name="freeMind_resources" version="tango">
<topic central="true" text="FreeMind Resources" id="1330730879">
<topic position="200,-100" order="0" text="Application" shape="line" id="906353570" bgColor="undefined">
<topic position="200,-50" order="0" text="What is it?" shape="line" id="293234618" bgColor="undefined">
<topic position="200,-100" order="0" text="Application" shape="line" id="906353570">
<topic position="200,-50" order="0" text="What is it?" shape="line" id="293234618">
<topic position="200,-50" order="0" text="A Freeware (as in free beer) Mindmapping tool coded in Java" shape="line" id="666230517"/>
<topic position="-290,-75" order="0" text="Expects Java 1.4 or above on client machine" shape="line" id="339571721"/>
</topic>
<topic position="-290,-125" order="0" text="How to do?" shape="line" id="39960632" bgColor="undefined">
<topic position="-290,-125" order="0" text="How to do?" shape="line" id="39960632">
<topic position="290,-162" order="0" text="Install it" shape="line" id="904501221">
<topic position="-290,-212" order="0" text="Links" shape="line" id="1911559485">
<topic position="290,-237" order="0" text="Download the Java Runtime Environment (at least J2RE1.4)" shape="line" id="1031016126" bgColor="undefined">
<topic position="290,-237" order="0" text="Download the Java Runtime Environment (at least J2RE1.4)" shape="line" id="1031016126">
<link url="http://java.sun.com/j2se" urlType="url"/>
</topic>
<topic position="380,-212" order="1" text="Download the Application" shape="line" id="1612101865" bgColor="undefined">
<topic position="380,-212" order="1" text="Download the Application" shape="line" id="1612101865">
<link url="http://freemind.sourceforge.net/wiki/index.php/Main_Page#Download_and_install" urlType="url"/>
</topic>
</topic>
@ -64,12 +64,12 @@ Ctrl + F in your browser?]]></note>
</topic>
</topic>
</topic>
<topic position="-290,-37" order="0" text="Applet Browser" shape="line" id="1647062097" bgColor="undefined">
<topic position="290,-62" order="0" text="What is it?" shape="rounded rectangle" id="287577997" bgColor="undefined">
<topic position="-290,-37" order="0" text="Applet Browser" shape="line" id="1647062097">
<topic position="290,-62" order="0" text="What is it?" shape="rounded rectangle" id="287577997">
<topic position="-290,-87" order="0" text="A Java applet based browser " shape="line" id="1855944960"/>
<topic position="-380,-62" order="1" text="Expects Java 1.4 or above on client machine" shape="line" id="300344325"/>
</topic>
<topic position="380,-37" order="1" text="How to do?" shape="line" shrink="true" id="1254385465" bgColor="undefined">
<topic position="380,-37" order="1" text="How to do?" shape="line" shrink="true" id="1254385465">
<topic position="-380,-62" order="0" text="See examples" shape="line" id="1491154453">
<topic position="380,-87" order="0" text="Public Maps" shape="line" id="1082166644">
<link url="http://freemind.sourceforge.net/PublicMaps.html" urlType="url"/>
@ -101,8 +101,8 @@ site.]]></text>
</topic>
</topic>
</topic>
<topic position="-380,-12" order="1" text="Flash Browser" shape="line" id="866838286" bgColor="undefined">
<topic position="380,-37" order="0" text="What is it?" shape="line" id="1079211058" bgColor="undefined">
<topic position="-380,-12" order="1" text="Flash Browser" shape="line" id="866838286">
<topic position="380,-37" order="0" text="What is it?" shape="line" id="1079211058">
<topic position="-380,-74" order="0" text="A browser which uses Shockwave Flash" shape="line" id="1719479201"/>
<topic position="-470,-49" order="1" text="Does not require Java 1.4 or above on client machine" shape="line" id="838930706"/>
<topic position="-560,-24" order="2" text="Expects Flash Player on client machine" shape="line" id="961870575">
@ -110,7 +110,7 @@ site.]]></text>
<topic position="650,-24" order="1" text="Recommend Flash Player 8" shape="line" id="913615141"/>
</topic>
</topic>
<topic position="470,-12" order="1" text="How to do?" shape="line" id="1824115095" bgColor="undefined">
<topic position="470,-12" order="1" text="How to do?" shape="line" id="1824115095">
<topic position="-470,-37" order="0" text="See examples" shape="line" id="1242194014">
<topic position="470,-62" order="0" text="Hardware Support for Linux" shape="line" id="1249980290">
<link url="http://eric.lavar.de/comp/linux/hw/" urlType="url"/>

View File

@ -2,19 +2,19 @@
<topic central="true" id="704795576">
<text><![CDATA[Writing an Essay
With FreeMind]]></text>
<topic position="200,-200" order="0" text="Getting Started" shape="line" shrink="true" id="60307947" bgColor="undefined">
<topic position="200,-150" order="0" shape="line" id="484974719" bgColor="undefined">
<topic position="200,-200" order="0" text="Getting Started" shape="line" shrink="true" id="60307947">
<topic position="200,-150" order="0" shape="line" id="484974719">
<text><![CDATA[The first thing you'll want to do is to create a new FreeMind file for your essay. Select File->New
on the menu, and a blank file will appear.
]]></text>
</topic>
<topic position="-290,-250" order="0" shape="line" id="1072554383" bgColor="undefined">
<topic position="-290,-250" order="0" shape="line" id="1072554383">
<text><![CDATA[Next, click in the centre of the map, and replace the text there with the essay title you have
chosen. It's good to have the question you're answering before you the whole time, so you can
immediately see how your ideas relate to
it.]]></text>
</topic>
<topic position="-380,-225" order="1" shape="line" id="1631286703" bgColor="undefined">
<topic position="-380,-225" order="1" shape="line" id="1631286703">
<text><![CDATA[<!--
p { margin-top: 0 }
-->
@ -28,22 +28,22 @@ it.]]></text>
(When you're a bit more familiar with FreeMind, you'll find it quicker to use the Shortcut Keys -- you can also use Alt + Shift + Left, or Alt + Shift + Right to move between maps.)]]></text>
</topic>
<topic position="-470,-200" order="2" text="You're now ready to start work on the essay." shape="line" id="538231078" bgColor="undefined"/>
<topic position="-470,-200" order="2" text="You're now ready to start work on the essay." shape="line" id="538231078"/>
</topic>
<topic position="-290,-62" order="0" text="The process of research" shape="line" shrink="true" id="1886958546" bgColor="undefined">
<topic position="290,-124" order="0" shape="line" id="499702363" bgColor="undefined">
<topic position="-290,-62" order="0" text="The process of research" shape="line" shrink="true" id="1886958546">
<topic position="290,-124" order="0" shape="line" id="499702363">
<text><![CDATA[If a question is worth asking at all (and be generous to your teachers, and assume that their
question is!), then it's not going to have the kind of answer that you can just make up on the spot.
It will require
research.]]></text>
</topic>
<topic position="380,-99" order="1" text="Research requires both finding things out about the world, and hard thinking." shape="line" id="1374284784" bgColor="undefined"/>
<topic position="470,-74" order="2" shape="line" id="1038997740" bgColor="undefined">
<topic position="380,-99" order="1" text="Research requires both finding things out about the world, and hard thinking." shape="line" id="1374284784"/>
<topic position="470,-74" order="2" shape="line" id="1038997740">
<text><![CDATA[FreeMind helps you with both aspects of research. FreeMind helps you to capture all the new
information you need for your essay, and also to order your
thoughts.]]></text>
</topic>
<topic position="560,-49" order="3" text="FreeMind does this by facilitating:" shape="line" id="1522470300" bgColor="undefined">
<topic position="560,-49" order="3" text="FreeMind does this by facilitating:" shape="line" id="1522470300">
<topic position="-560,-99" order="0" text="Taking Notes" shape="line" id="1963065372">
<link url="http://#Freemind_Link_513978291" urlType="url"/>
</topic>
@ -57,7 +57,7 @@ thoughts.]]></text>
<link url="http://#Freemind_Link_341601142" urlType="url"/>
</topic>
</topic>
<topic position="650,-24" order="4" text="When you've made your outline you can" shape="line" id="759053646" bgColor="undefined">
<topic position="650,-24" order="4" text="When you've made your outline you can" shape="line" id="759053646">
<topic position="-650,-61" order="0" text="Print your map" shape="line" id="996906209"/>
<topic position="-740,-36" order="1" text="Export to your wordprocessor" shape="line" id="1501502447">
<link url="http://#Freemind_Link_877777095" urlType="url"/>
@ -67,8 +67,8 @@ thoughts.]]></text>
</topic>
</topic>
</topic>
<topic position="-380,-37" order="1" text="Planning an Essay" shape="line" shrink="true" id="1034561457" bgColor="undefined">
<topic position="380,-87" order="0" text="Brainstorming" shape="line" shrink="true" id="1" bgColor="undefined">
<topic position="-380,-37" order="1" text="Planning an Essay" shape="line" shrink="true" id="1034561457">
<topic position="380,-87" order="0" text="Brainstorming" shape="line" shrink="true" id="1">
<topic position="-380,-162" order="0" shape="line" id="1420002558">
<text><![CDATA[Brainstorming is a way of trying to geting all your ideas about a topic down on paper as quickly as
possible.]]></text>
@ -83,18 +83,18 @@ later.]]></text>
<topic position="-740,-62" order="4" text="Pressing Insert adds another idea as a subidea of the one you've currently selected." shape="line" id="1371557121"/>
<topic position="-830,-37" order="5" text="(The imagination you'll have to supply yourself!)" shape="line" id="443781940"/>
</topic>
<topic position="470,-62" order="1" text="Taking Notes" shape="line" shrink="true" id="513978291" bgColor="undefined">
<topic position="-470,-124" order="0" shape="line" id="709453371" bgColor="undefined">
<topic position="470,-62" order="1" text="Taking Notes" shape="line" shrink="true" id="513978291">
<topic position="-470,-124" order="0" shape="line" id="709453371">
<text><![CDATA[Different people take notes in different ways. Whichever way you take notes, you should find
FreeMind
helpful.]]></text>
</topic>
<topic position="-560,-99" order="1" shape="line" id="249608113" bgColor="undefined">
<topic position="-560,-99" order="1" shape="line" id="249608113">
<text><![CDATA[One good way of using FreeMind is to add one node per book or article you read. Name the node a
short version of the article title.
]]></text>
</topic>
<topic position="-650,-74" order="2" shape="line" shrink="true" id="1470413282" bgColor="undefined">
<topic position="-650,-74" order="2" shape="line" shrink="true" id="1470413282">
<text><![CDATA[Then add your notes on the article as a node note. To insert a note, go to the View menu, and
select "Show Note Window". You can then add your notes
below.]]></text>
@ -104,8 +104,8 @@ helpful to hide it after you've added your note, by selecting "Show Note Window"
menu.]]></text>
</topic>
</topic>
<topic position="-740,-49" order="3" text="Don't forget to take full references for all the quotations you use. " shape="line" id="1226928695" bgColor="undefined"/>
<topic position="-830,-24" order="4" text="FreeMind can also do some clever things with web links and email addresses." shape="line" shrink="true" id="1195317986" bgColor="undefined">
<topic position="-740,-49" order="3" text="Don't forget to take full references for all the quotations you use. " shape="line" id="1226928695"/>
<topic position="-830,-24" order="4" text="FreeMind can also do some clever things with web links and email addresses." shape="line" shrink="true" id="1195317986">
<topic position="830,-86" order="0" shape="line" id="1167090962">
<text><![CDATA[If you're researching on the web, then FreeMind can automatically link your note to the web page
you're writing about.
@ -132,7 +132,7 @@ address.]]></text>
</topic>
</topic>
</topic>
<topic position="560,-37" order="2" text="Sifting and organising your ideas" shape="line" shrink="true" id="331016293" bgColor="undefined">
<topic position="560,-37" order="2" text="Sifting and organising your ideas" shape="line" shrink="true" id="331016293">
<topic position="-560,-124" order="0" shape="line" id="136989740">
<text><![CDATA[After you've done a bit of brainstorming, and have written all the ideas you can think of, it's time
to start sorting your ideas
@ -174,7 +174,7 @@ menu, and call the new one it something like "Essay_Outline v1".
]]></text>
</topic>
</topic>
<topic position="650,-12" order="3" text="Outlining" shape="line" shrink="true" id="341601142" bgColor="undefined">
<topic position="650,-12" order="3" text="Outlining" shape="line" shrink="true" id="341601142">
<topic position="-650,-87" order="0" text="Now you know what your main point is, you can write an outline of the argument of the essay." shape="line" id="190098699"/>
<topic position="-740,-62" order="1" text="The point of producing an outline is to work out how you will argue for your essay's main claim." shape="line" id="132833105"/>
<topic position="-830,-37" order="2" shape="line" id="1132818589">
@ -237,14 +237,14 @@ point.]]></text>
</topic>
</topic>
</topic>
<topic position="-470,-12" order="2" text="From Outline to Finished Work" shape="line" shrink="true" id="1755853195" bgColor="undefined">
<topic position="470,-74" order="0" shape="line" id="1449950809" bgColor="undefined">
<topic position="-470,-12" order="2" text="From Outline to Finished Work" shape="line" shrink="true" id="1755853195">
<topic position="470,-74" order="0" shape="line" id="1449950809">
<text><![CDATA[Writing the outline of your essay is the hard part. Once you have a good outline, it's much easier
to write a good essay or a good
presentation.]]></text>
</topic>
<topic position="560,-49" order="1" text="You'll probably find it easier to to the actual writing of the essay in your wordprocessor." shape="line" id="1607766784" bgColor="undefined"/>
<topic position="650,-24" order="2" text="Exporting your outline to your Wordprocessor" shape="line" shrink="true" id="877777095" bgColor="undefined">
<topic position="560,-49" order="1" text="You'll probably find it easier to to the actual writing of the essay in your wordprocessor." shape="line" id="1607766784"/>
<topic position="650,-24" order="2" text="Exporting your outline to your Wordprocessor" shape="line" shrink="true" id="877777095">
<topic position="-650,-99" order="0" shape="line" shrink="true" id="1821901940">
<text><![CDATA[FreeMind currently exports much better to OpenOffice than to Microsoft Word. So if you don't yet
have OpenOffice, it's well worth downloading it (it's
@ -280,7 +280,7 @@ wordprocessor as
normal.]]></text>
</topic>
</topic>
<topic position="740,1" order="3" text="Making a presentation" shape="line" shrink="true" id="781772166" bgColor="undefined">
<topic position="740,1" order="3" text="Making a presentation" shape="line" shrink="true" id="781772166">
<topic position="-740,-74" order="0" text="FreeMind also provides a good way of outlining PowerPoint presentations." shape="line" id="1714771405"/>
<topic position="-830,-49" order="1" text="You'll need OpenOffice again." shape="line" id="233010529"/>
<topic position="-920,-24" order="2" text="Follow the instructions above to export your outline to OpenOffice." shape="line" id="1394183501"/>
@ -296,50 +296,50 @@ best
results.]]></text>
</topic>
</topic>
<topic position="830,26" order="4" text="Things to check before you submit your essay" shape="line" shrink="true" id="1657086138" bgColor="undefined">
<topic position="830,26" order="4" text="Things to check before you submit your essay" shape="line" shrink="true" id="1657086138">
<topic position="-830,-111" order="0" shape="line" id="1855894453">
<text><![CDATA[(This advice doesn't tell you anything about how to use FreeMind, but it may help you get a better
mark in your
essay!)]]></text>
</topic>
<topic position="-920,-86" order="1" text="Does my essay have a main claim?" shape="line" id="1803078302" bgColor="undefined">
<topic position="-920,-86" order="1" text="Does my essay have a main claim?" shape="line" id="1803078302">
<note><![CDATA[Another way of asking this question is can you sum up in a sentence what the main point is that your essay is making?) If you don't have a main claim (or don't know what your main claim is!), then your essay will not get a good mark. You are assessed on the quality of the argument you put forward for your main claim, and in order to be able to do this we (and you) need to know what your main claim is.]]></note>
</topic>
<topic position="-1010,-61" order="2" text="Does my main claim provide a full answer the question that I have been asked?" shape="line" id="107276913" bgColor="undefined">
<topic position="-1010,-61" order="2" text="Does my main claim provide a full answer the question that I have been asked?" shape="line" id="107276913">
<note><![CDATA[You must be honest with yourself at this point: if you suspect that you haven't fully answered the question, then you must either (a) revise your answer so that you do have a full answer to the question, or (b) provide an argument for why it is that the angle you want to bring to the question is legitimate (for example, explain why it is legitimate to focus on just one aspect of the question).]]></note>
</topic>
<topic position="-1100,-36" order="3" text="Have I made it obvious what my main claim is?" shape="line" id="1628680821" bgColor="undefined">
<topic position="-1100,-36" order="3" text="Have I made it obvious what my main claim is?" shape="line" id="1628680821">
<note><![CDATA[You should state what your main claim is in at least two places, first in the introduction, and second in the conclusion. (The bits in between should be devoted to arguing for your main claim).]]></note>
</topic>
<topic position="-1190,-11" order="4" text="Do I have an argument for my main claim?" shape="line" id="927542090" bgColor="undefined">
<topic position="-1190,-11" order="4" text="Do I have an argument for my main claim?" shape="line" id="927542090">
<note><![CDATA[What reasons have you put forward as to why a reasonable (but sceptical) person should accept that your main claim is true? If you don't have any reasons (but merely a gut intuition) then you need to go back and revise, and find some arguments.]]></note>
</topic>
<topic position="-1280,14" order="5" text="Is my argument for my main claim sound?" shape="line" id="337592890" bgColor="undefined">
<topic position="-1280,14" order="5" text="Is my argument for my main claim sound?" shape="line" id="337592890">
<note><![CDATA[Does your main claim follow logically from the supporting reasons you put forward? And are those supporting reasons themselves true (or at least plausibly true)?]]></note>
</topic>
<topic position="-1370,39" order="6" text="Do I have an introduction which provides an overview of the structure of my argument?" shape="line" id="1338320981" bgColor="undefined">
<topic position="-1370,39" order="6" text="Do I have an introduction which provides an overview of the structure of my argument?" shape="line" id="1338320981">
<note><![CDATA[It is not enough e.g. to say that “I will be looking at arguments on both sides of this issue and coming to a conclusion”. You should tell us which arguments you will be looking at, whatyour evaluation of each of these arguments will be, and howthis analysis justifies the overall main claim you will be making. There are two reasons to give an overview of the structure of your argument: (a) it makes it much easier for the reader to grasp what you are saying, and why; (b) writing a summary of the structure of your argument is a good way of testing that you do in fact have a coherent argument.]]></note>
</topic>
<topic position="-1460,64" order="7" text="What reasons might a reasonable person have for doubting my main claim?" shape="line" id="1521390030" bgColor="undefined">
<topic position="-1460,64" order="7" text="What reasons might a reasonable person have for doubting my main claim?" shape="line" id="1521390030">
<note><![CDATA[Remember that in any academic debate, anything worth saying will be disputed. If you can't think of any reasons why someone might doubt your main claim, it's likely that you are in the grip of a dogmatic certainty that you are right. This is not good: your essay will come across as a rant, which is the last thing you want.]]></note>
</topic>
<topic position="-1550,89" order="8" text="Have I responded to these reasons for doubting my main claim in a convincing way?" shape="line" id="1843933327" bgColor="undefined">
<topic position="-1550,89" order="8" text="Have I responded to these reasons for doubting my main claim in a convincing way?" shape="line" id="1843933327">
<note><![CDATA[To be convincing, you might show that the doubts, while reasonable, are not well founded; or you could make your main claim more specific or nuanced in deference to them.]]></note>
</topic>
<topic position="-1640,114" order="9" text="Is there any material in my essay which might seem irrelevant to establishing my main point?" shape="line" id="982795475" bgColor="undefined">
<topic position="-1640,114" order="9" text="Is there any material in my essay which might seem irrelevant to establishing my main point?" shape="line" id="982795475">
<note><![CDATA[If there is, then either delete this material, or explain why this material is after all relevant.]]></note>
</topic>
<topic position="-1730,139" order="10" text="Have I fully acknowledged all my sources, and marked all quotations as quotations?" shape="line" id="606334721" bgColor="undefined">
<topic position="-1730,139" order="10" text="Have I fully acknowledged all my sources, and marked all quotations as quotations?" shape="line" id="606334721">
<note><![CDATA[If not then you are guilty of plagiarism. This is a serious offence, and you are likely to fail your course..]]></note>
</topic>
</topic>
</topic>
<topic position="-560,13" order="3" text="About this map" shape="line" shrink="true" id="854745518" bgColor="undefined">
<topic position="560,-24" order="0" text="Version 0.1, Jan 2 2008" shape="line" id="1464926185" bgColor="undefined"/>
<topic position="650,1" order="1" text="James Wilson" shape="line" id="1351075037" bgColor="undefined">
<topic position="-560,13" order="3" text="About this map" shape="line" shrink="true" id="854745518">
<topic position="560,-24" order="0" text="Version 0.1, Jan 2 2008" shape="line" id="1464926185"/>
<topic position="650,1" order="1" text="James Wilson" shape="line" id="1351075037">
<link url="http://mailto:j.g.wilson@peak.keele.ac.uk" urlType="mail"/>
</topic>
<topic position="740,26" order="2" text="Notes on this map" shape="line" id="1843583599" bgColor="undefined">
<topic position="740,26" order="2" text="Notes on this map" shape="line" id="1843583599">
<note><![CDATA[This map is intended to help someone who has to write an argumentative essay in a subject such as history or philosophy to write better essays with the help of FreeMind. Copyright for this map resides with the author. Released under the Creative Commons Attribution-ShareAlike licence v.2.00. http://creativecommons.org/licenses/by-sa/2.0/uk/

View File

@ -2,15 +2,22 @@
"compilerOptions": {
"outDir": "./dist/",
"sourceMap": true,
"noImplicitAny": false,
"module": "amd",
"moduleResolution": "node",
"target": "ES2020",
"target": "es6",
"allowJs": true,
"strict": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"declaration": true,
"strictNullChecks": true,
"noImplicitAny": false,
"noImplicitReturns": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"strictFunctionTypes": true,
"rootDirs": [
"src",
]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 78 KiB

View File

@ -7,7 +7,7 @@
<meta charset="utf-8" />
<link rel="icon" href="<%=PUBLIC_URL%>/favicon.ico" />
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@100;200;300;400;600&display=swap" rel="stylesheet" onload="if(media!='all')media='all'" media="none"/>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@100;200;300;400;600&display=swap" rel="stylesheet" rel="preload"/>
<meta name="viewport" content="width=device-width" />
<meta name="theme-color" content="#000000" />

View File

@ -1,3 +1,20 @@
/*
* Copyright [2021] [wisemapping]
*
* Licensed under WiseMapping Public License, Version 1.0 (the "License").
* It is basically the Apache License, Version 2.0 (the "License") plus the
* "powered by wisemapping" text requirement on every single page;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the license at
*
* http://www.wisemapping.org/license
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, { ReactElement, Suspense, useEffect } from 'react';
import { FormattedMessage, IntlProvider } from 'react-intl';
import { Route, Routes, BrowserRouter as Router, useNavigate } from 'react-router-dom';
@ -22,12 +39,14 @@ const EditorPage = React.lazy(() => import('./components/editor-page'));
const MapsPage = React.lazy(() => import('./components/maps-page'));
// Google Analytics Initialization.
ReactGA.initialize([
{
trackingId: AppConfig.getGoogleAnalyticsAccount(),
},
]);
const trackingId = AppConfig.getGoogleAnalyticsAccount();
if (trackingId) {
ReactGA.initialize([
{
trackingId: trackingId,
},
]);
}
const queryClient = new QueryClient({
defaultOptions: {
queries: {

View File

@ -17,7 +17,6 @@
*/
import Client from '../client';
import CacheDecoratorClient from '../client/cache-decorator-client';
import MockClient from '../client/mock-client';
import RestClient from '../client/rest-client';
@ -91,8 +90,7 @@ class _AppConfig {
result = new MockClient();
}
// Wrap with a cache decorator ...
return new CacheDecoratorClient(result);
return result;
}
getBaseUrl(): string {

View File

@ -57,8 +57,8 @@ export default abstract class AppI18n {
public static getDefaultLocale(): Locale {
// Fetch local from local storage ...
let result: Locale;
const userLocaleCode: string = localStorage.getItem(AppI18n.LOCAL_STORAGE_KEY);
let result: Locale | null = null;
const userLocaleCode: string | null = localStorage.getItem(AppI18n.LOCAL_STORAGE_KEY);
if (userLocaleCode) {
result = localeFromStr(userLocaleCode);
}

View File

@ -1,143 +0,0 @@
import Client, {
AccountInfo,
BasicMapInfo,
ChangeHistory,
ImportMapInfo,
Label,
MapInfo,
NewUser,
Permission,
Oauth2CallbackResult,
ForgotPasswordResult,
} from '..';
import { LocaleCode } from '../../app-i18n';
class CacheDecoratorClient implements Client {
private client: Client;
constructor(client: Client) {
this.client = client;
}
processGoogleCallback(code: string): Promise<Oauth2CallbackResult> {
return this.client.processGoogleCallback(code);
}
confirmAccountSync(email: string, code: string): Promise<void> {
return this.client.confirmAccountSync(email, code);
}
fetchStarred(id: number): Promise<boolean> {
return this.client.fetchStarred(id);
}
onSessionExpired(callback?: () => void): () => void {
return this.client.onSessionExpired(callback);
}
deleteAccount(): Promise<void> {
return this.client.deleteAccount();
}
importMap(model: ImportMapInfo): Promise<number> {
return this.client.importMap(model);
}
createMap(map: BasicMapInfo): Promise<number> {
return this.client.createMap(map);
}
deleteMaps(ids: number[]): Promise<void> {
return this.client.deleteMaps(ids);
}
deleteMap(id: number): Promise<void> {
return this.client.deleteMap(id);
}
renameMap(id: number, basicInfo: BasicMapInfo): Promise<void> {
return this.client.renameMap(id, basicInfo);
}
fetchAllMaps(): Promise<MapInfo[]> {
return this.client.fetchAllMaps();
}
fetchMapPermissions(id: number): Promise<Permission[]> {
return this.client.fetchMapPermissions(id);
}
addMapPermissions(id: number, message: string, permissions: Permission[]): Promise<void> {
return this.client.addMapPermissions(id, message, permissions);
}
deleteMapPermission(id: number, email: string): Promise<void> {
return this.client.deleteMapPermission(id, email);
}
duplicateMap(id: number, basicInfo: BasicMapInfo): Promise<number> {
return this.client.duplicateMap(id, basicInfo);
}
updateAccountLanguage(locale: LocaleCode): Promise<void> {
return this.client.updateAccountLanguage(locale);
}
updateAccountPassword(pasword: string): Promise<void> {
return this.client.updateAccountPassword(pasword);
}
updateAccountInfo(firstname: string, lastname: string): Promise<void> {
return this.client.updateAccountInfo(firstname, lastname);
}
updateStarred(id: number, starred: boolean): Promise<void> {
return this.client.updateStarred(id, starred);
}
updateMapToPublic(id: number, isPublic: boolean): Promise<void> {
return this.client.updateMapToPublic(id, isPublic);
}
fetchLabels(): Promise<Label[]> {
return this.client.fetchLabels();
}
createLabel(title: string, color: string): Promise<number> {
return this.client.createLabel(title, color);
}
deleteLabel(id: number): Promise<void> {
return this.client.deleteLabel(id);
}
addLabelToMap(labelId: number, mapId: number): Promise<void> {
return this.client.addLabelToMap(labelId, mapId);
}
deleteLabelFromMap(labelId: number, mapId: number): Promise<void> {
return this.client.deleteLabelFromMap(labelId, mapId);
}
fetchAccountInfo(): Promise<AccountInfo> {
return this.client.fetchAccountInfo();
}
registerNewUser(user: NewUser): Promise<void> {
return this.client.registerNewUser(user);
}
resetPassword(email: string): Promise<ForgotPasswordResult> {
return this.client.resetPassword(email);
}
fetchHistory(id: number): Promise<ChangeHistory[]> {
return this.client.fetchHistory(id);
}
revertHistory(id: number, cid: number): Promise<void> {
return this.client.revertHistory(id, cid);
}
}
export default CacheDecoratorClient;

View File

@ -118,12 +118,12 @@ interface Client {
registerNewUser(user: NewUser): Promise<void>;
resetPassword(email: string): Promise<ForgotPasswordResult>;
processGoogleCallback(code: string): Promise<Oauth2CallbackResult>;
confirmAccountSync(email: string, code: string): Promise<void>;
confirmAccountSync(email: string, code?: string): Promise<void>;
fetchHistory(id: number): Promise<ChangeHistory[]>;
revertHistory(id: number, cid: number): Promise<void>;
onSessionExpired(callback?: () => void): () => void;
onSessionExpired(callback?: () => void): (() => void) | undefined;
}
export default Client;

View File

@ -128,10 +128,10 @@ class MockClient implements Client {
}
fetchStarred(id: number): Promise<boolean> {
return Promise.resolve(this.maps.find((m) => m.id == id).starred);
return Promise.resolve(Boolean(this.maps.find((m) => m.id == id)?.starred));
}
onSessionExpired(callback?: () => void): () => void {
onSessionExpired(callback?: () => void): (() => void) | undefined {
return callback;
}

View File

@ -1,32 +0,0 @@
/* eslint-disable react/display-name */
import React, { ComponentType, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import Client from '../../classes/client';
import ClientHealthSentinel from '../../classes/client/client-health-sentinel';
import { activeInstance, sessionExpired } from '../../redux/clientSlice';
function withSessionExpirationHandling<T>(Component: ComponentType<T>) {
return (hocProps: T): React.ReactElement => {
const client: Client = useSelector(activeInstance);
const dispatch = useDispatch();
useEffect(() => {
if (client) {
client.onSessionExpired(() => {
dispatch(sessionExpired());
});
} else {
console.warn('Session expiration wont be handled because could not find client');
}
}, []);
return (
<>
<ClientHealthSentinel />
<Component {...hocProps} />
</>
);
};
}
export default withSessionExpirationHandling;

View File

@ -0,0 +1,10 @@
import React from 'react';
import { getCsrfToken, getCsrfTokenParameter } from '../../../utils';
const CSRFInput = (): React.ReactElement => {
const token = getCsrfToken();
const tokenParam = getCsrfTokenParameter();
return <>{token && tokenParam && <input type="hidden" value={token} name={tokenParam} />}</>;
};
export default CSRFInput;

View File

@ -1,3 +1,20 @@
/*
* Copyright [2021] [wisemapping]
*
* Licensed under WiseMapping Public License, Version 1.0 (the "License").
* It is basically the Apache License, Version 2.0 (the "License") plus the
* "powered by wisemapping" text requirement on every single page;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the license at
*
* http://www.wisemapping.org/license
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { css } from '@emotion/react';
import { Button } from '@mui/material';

View File

@ -1,3 +1,20 @@
/*
* Copyright [2021] [wisemapping]
*
* Licensed under WiseMapping Public License, Version 1.0 (the "License").
* It is basically the Apache License, Version 2.0 (the "License") plus the
* "powered by wisemapping" text requirement on every single page;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the license at
*
* http://www.wisemapping.org/license
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { useTheme } from '@mui/material/styles';
import { containerStyle, lineStyle, textStyle } from './style';

View File

@ -2,7 +2,7 @@ import { css, SerializedStyles } from '@emotion/react';
export const containerStyle = (
responsive: boolean,
maxWidth: number,
maxWidth: number | undefined,
breakPointDownMd: string,
): SerializedStyles => {
return css([

View File

@ -26,19 +26,23 @@ import {
} from '@wisemapping/editor';
import { IntlProvider } from 'react-intl';
import AppI18n, { Locales } from '../../classes/app-i18n';
import { useSelector } from 'react-redux';
import { useDispatch, useSelector } from 'react-redux';
import { hotkeysEnabled } from '../../redux/editorSlice';
import ReactGA from 'react-ga4';
import { useFetchAccount, useFetchMapById } from '../../redux/clientSlice';
import {
useFetchAccount,
useFetchMapById,
activeInstance,
sessionExpired,
} from '../../redux/clientSlice';
import EditorOptionsBuilder from './EditorOptionsBuilder';
import { useTheme } from '@mui/material/styles';
import MapInfoImpl from '../../classes/editor-map-info';
import { MapInfo } from '@wisemapping/editor';
import { activeInstance } from '../../redux/clientSlice';
import Client from '../../classes/client';
import AppConfig from '../../classes/app-config';
import exampleMap from '../../classes/client/mock-client/example-map.wxml';
import withSessionExpirationHandling from '../HOCs/withSessionExpirationHandling';
import ClientHealthSentinel from '../common/client-health-sentinel';
const buildPersistenceManagerForEditor = (mode: string): PersistenceManager => {
let persistenceManager: PersistenceManager;
@ -72,6 +76,7 @@ const buildPersistenceManagerForEditor = (mode: string): PersistenceManager => {
export type EditorPropsType = {
isTryMode: boolean;
};
type ActionType =
| 'open'
| 'share'
@ -98,6 +103,17 @@ const EditorPage = ({ isTryMode }: EditorPropsType): React.ReactElement => {
const userLocale = AppI18n.getUserLocale();
const theme = useTheme();
const client: Client = useSelector(activeInstance);
const dispatch = useDispatch();
useEffect(() => {
if (client) {
client.onSessionExpired(() => {
dispatch(sessionExpired());
});
} else {
console.warn('Session expiration wont be handled because could not find client');
}
}, []);
useEffect(() => {
ReactGA.send({ hitType: 'pageview', page: window.location.pathname, title: `Map Editor` });
@ -163,6 +179,7 @@ const EditorPage = ({ isTryMode }: EditorPropsType): React.ReactElement => {
defaultLocale={Locales.EN.code}
messages={userLocale.message as Record<string, string>}
>
<ClientHealthSentinel />
<Editor
onAction={setActiveDialog}
options={options}
@ -197,4 +214,4 @@ const EditorPage = ({ isTryMode }: EditorPropsType): React.ReactElement => {
);
};
export default withSessionExpirationHandling(EditorPage);
export default EditorPage;

View File

@ -15,9 +15,9 @@ import ReactGA from 'react-ga4';
import { Link as RouterLink } from 'react-router-dom';
import Typography from '@mui/material/Typography';
import { getCsrfToken, getCsrfTokenParameter } from '../../utils';
import { useNavigate } from 'react-router-dom';
import { Button } from '@mui/material';
import CSRFInput from '../common/csrf-input';
const ForgotPassword = () => {
const [email, setEmail] = useState<string>('');
@ -84,7 +84,7 @@ const ForgotPassword = () => {
<GlobalError error={error} />
<form onSubmit={handleOnSubmit}>
<input type="hidden" value={getCsrfToken()} name={getCsrfTokenParameter()} />
<CSRFInput />
<Input
type="email"
name="email"

View File

@ -11,10 +11,10 @@ import Typography from '@mui/material/Typography';
import FormControl from '@mui/material/FormControl';
import Link from '@mui/material/Link';
import ReactGA from 'react-ga4';
import { getCsrfToken, getCsrfTokenParameter } from '../../utils';
import Separator from '../common/separator';
import GoogleButton from '../common/google-button';
import AppConfig from '../../classes/app-config';
import CSRFInput from '../common/csrf-input';
const LoginError = () => {
// @Todo: This must be reviewed to be based on navigation state.
@ -70,7 +70,7 @@ const LoginPage = (): React.ReactElement => {
<FormControl>
<form action="/c/perform-login" method="POST">
<input type="hidden" value={getCsrfToken()} name={getCsrfTokenParameter()} />
<CSRFInput />
<Input
name="username"
type="email"
@ -121,7 +121,12 @@ const LoginPage = (): React.ReactElement => {
defaultMessage: 'Sign in with Google',
})}
onClick={() => {
window.location.href = AppConfig.getGoogleOauth2Url();
const authUrl = AppConfig.getGoogleOauth2Url();
if (authUrl) {
window.location.href = authUrl;
} else {
console.log('GoogleOauth2Url is not configured.');
}
}}
/>
</FormContainer>

View File

@ -89,7 +89,7 @@ const ImportDialog = ({ onClose }: CreateProps): React.ReactElement => {
const extensionFile = file.name.split('.').pop();
const extensionAccept = ['wxml', 'mm'];
if (!extensionAccept.includes(extensionFile)) {
if (!extensionAccept.includes(extensionFile!)) {
setErrorFile({
error: true,
message: intl.formatMessage(
@ -108,8 +108,8 @@ const ImportDialog = ({ onClose }: CreateProps): React.ReactElement => {
model.contentType = 'application/xml';
const fileContent = event?.target?.result;
const mapConent: string =
typeof fileContent === 'string' ? fileContent : fileContent.toString();
const mapConent: string | undefined =
typeof fileContent === 'string' ? fileContent : fileContent?.toString();
try {
const importer: Importer = TextImporterFactory.create(extensionFile, mapConent);

View File

@ -1,3 +1,20 @@
/*
* Copyright [2021] [wisemapping]
*
* Licensed under WiseMapping Public License, Version 1.0 (the "License").
* It is basically the Apache License, Version 2.0 (the "License") plus the
* "powered by wisemapping" text requirement on every single page;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the license at
*
* http://www.wisemapping.org/license
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { useSelector } from 'react-redux';
import { FormattedMessage, useIntl } from 'react-intl';
@ -19,13 +36,11 @@ const LabelDialog = ({ mapsId, onClose }: MultiDialogProps): React.ReactElement
const client: Client = useSelector(activeInstance);
const queryClient = useQueryClient();
// TODO: pass down map data instead of using query?
const { data } = useQuery<unknown, ErrorInfo, MapInfo[]>('maps', () => {
return client.fetchAllMaps();
});
const [error, setError] = React.useState<ErrorInfo>();
const maps = data.filter((m) => mapsId.includes(m.id));
const maps = data?.filter((m) => mapsId.includes(m.id));
const changeLabelMutation = useMutation<
void,
@ -44,11 +59,9 @@ const LabelDialog = ({ mapsId, onClose }: MultiDialogProps): React.ReactElement
const handleChangesInLabels = (label: Label, checked: boolean) => {
setError(undefined);
changeLabelMutation.mutate({
maps,
label,
checked,
});
if (maps) {
changeLabelMutation.mutate({ maps, label, checked });
}
};
return (
@ -66,21 +79,27 @@ const LabelDialog = ({ mapsId, onClose }: MultiDialogProps): React.ReactElement
paperCss={classes.paper}
error={error}
>
<>
<Typography variant="body2" marginTop="10px" css={classes.title as Interpolation<Theme>}>
<FormattedMessage id="label.add-for" defaultMessage="Editing labels for " />
{maps.length > 1 ? (
<FormattedMessage
id="label.maps-count"
defaultMessage="{count} maps"
values={{ count: maps.length }}
/>
) : (
maps.map((m) => m.title).join(', ')
)}
</Typography>
<LabelSelector onChange={handleChangesInLabels} maps={maps} />
</>
{maps && (
<>
<Typography
variant="body2"
marginTop="10px"
css={classes.title as Interpolation<Theme>}
>
<FormattedMessage id="label.add-for" defaultMessage="Editing labels for " />
{maps && maps.length > 1 ? (
<FormattedMessage
id="label.maps-count"
defaultMessage="{count} maps"
values={{ count: maps.length }}
/>
) : (
maps.map((m) => m.title).join(', ')
)}
</Typography>
<LabelSelector onChange={handleChangesInLabels} maps={maps} />
</>
)}
</BaseDialog>
</div>
);

View File

@ -1,3 +1,20 @@
/*
* Copyright [2021] [wisemapping]
*
* Licensed under WiseMapping Public License, Version 1.0 (the "License").
* It is basically the Apache License, Version 2.0 (the "License") plus the
* "powered by wisemapping" text requirement on every single page;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the license at
*
* http://www.wisemapping.org/license
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, { ErrorInfo, ReactElement, useEffect } from 'react';
import Drawer from '@mui/material/Drawer';
import AppBar from '@mui/material/AppBar';
@ -200,7 +217,7 @@ const MapsPage = (): ReactElement => {
);
const container = document !== undefined ? () => document.body : undefined;
const label = labels.find((l) => l.id === labelToDelete);
return (
<IntlProvider
locale={userLocale.code}
@ -210,7 +227,7 @@ const MapsPage = (): ReactElement => {
<div css={classes.root}>
<AppBar
position="fixed"
css={[classes.appBar, open && classes.appBarShift]}
css={[classes.appBar, classes.appBarShift]}
variant="outlined"
elevation={0}
>
@ -321,14 +338,14 @@ const MapsPage = (): ReactElement => {
<MapsList filter={filter} />
</main>
</div>
{labelToDelete && (
{label && labelToDelete && (
<LabelDeleteConfirm
onClose={() => setLabelToDelete(null)}
onConfirm={() => {
handleLabelDelete(labelToDelete);
setLabelToDelete(null);
}}
label={labels.find((l) => l.id === labelToDelete)}
label={label}
/>
)}
</IntlProvider>

View File

@ -20,7 +20,7 @@ const RegistrationCallbackPage = (): React.ReactElement => {
const client: Client = useSelector(activeInstance);
const [showError, setShowError] = useState(false);
const [callbackResult, setCallbackResult] = useState<Oauth2CallbackResult>(undefined);
const [callbackResult, setCallbackResult] = useState<Oauth2CallbackResult>();
const navigate = useNavigate();
useEffect(() => {
@ -37,6 +37,10 @@ const RegistrationCallbackPage = (): React.ReactElement => {
useEffect(() => {
const googleOauthCode = new URLSearchParams(window.location.search).get('code');
if (!googleOauthCode) {
throw new Error(`Missing code definition: ${window.location.search}`);
}
client
.processGoogleCallback(googleOauthCode)
.then((result) => {
@ -54,8 +58,13 @@ const RegistrationCallbackPage = (): React.ReactElement => {
}, []);
const confirmAccountSynching = () => {
const callback = callbackResult;
if (!callback) {
throw new Error(`callbackResult can not be null`);
}
client
.confirmAccountSync(callbackResult.email, callbackResult.syncCode)
.confirmAccountSync(callback.email, callback.syncCode)
.then(() => {
navigate('/c/maps/');
})

View File

@ -69,7 +69,12 @@ const RegistrationForm = () => {
const maxFormWidth = 350;
const handleRegisterWithGoogleClick = () => {
window.location.href = AppConfig.getGoogleOauth2Url();
const url = AppConfig.getGoogleOauth2Url();
if (url) {
window.location.href = url;
} else {
console.error('Auth callback url is null');
}
};
return (

View File

@ -55,7 +55,7 @@ export const clientSlice = createSlice({
type MapLoadResult = {
isLoading: boolean;
error: ErrorInfo | null;
map: MapInfo | null;
map: MapInfo | undefined;
};
export const useFetchMapById = (id: number): MapLoadResult => {
@ -65,12 +65,12 @@ export const useFetchMapById = (id: number): MapLoadResult => {
});
// If the map can not be loaded, create an error object.
let map: MapInfo;
let errorMsg: ErrorInfo = error;
let map: MapInfo | undefined;
let errorMsg: ErrorInfo | null = error;
if (!isLoading) {
// Sanitize error structure ...
if (errorMsg) {
errorMsg = Object.keys(error).length !== 0 ? error : null;
errorMsg = Object.keys(errorMsg).length !== 0 ? error : null;
}
// Seach for object...
map = data?.find((m) => m.id == id);

View File

@ -1,18 +0,0 @@
{
"compilerOptions": {
"outDir": "./dist/",
"sourceMap": true,
"noImplicitAny": false,
"module": "amd",
"moduleResolution": "node",
"strict": false,
"target": "es6",
"allowJs": true,
"esModuleInterop": true,
"declaration": true,
"jsx": "react-jsx",
"jsxImportSource": "@emotion/react"
}
}

View File

@ -6,7 +6,7 @@ export const getCsrfToken = (): string | null => {
return meta.getAttribute('content');
};
export const getCsrfTokenParameter = (): string => {
export const getCsrfTokenParameter = (): string | null => {
const meta = document.head.querySelector('meta[name="_csrf_parameter"]');
if (!meta) {
return '';

View File

@ -1,15 +1,27 @@
{
"compilerOptions": {
"outDir": "./dist/",
"sourceMap": true,
"noImplicitAny": false,
"module": "es6",
"target": "es6",
"allowJs": true,
"esModuleInterop": true,
"jsx": "react-jsx",
"jsxImportSource": "@emotion/react"
},
"exclude": ["node_modules"]
}
{
"include": [
"src/**/*"
],
"compilerOptions": {
"jsx": "react-jsx",
"outDir": "./dist/",
"sourceMap": true,
"noImplicitAny": false,
"module": "amd",
"moduleResolution": "node",
"strict": false,
"target": "es6",
"allowJs": true,
"esModuleInterop": true,
"declaration": true,
"strictNullChecks": true,
"rootDirs": [
"src",
],
"jsxImportSource": "@emotion/react",
"resolveJsonModule": true
},
"exclude": [
"node_modules"
]
}