2022-02-23 15:46:16 -03:00

314 lines
10 KiB
TypeScript

import xmlFormatter from 'xml-formatter';
import { Mindmap } from '../..';
import INodeModel, { TopicShape } from '../model/INodeModel';
import RelationshipModel from '../model/RelationshipModel';
import IconModel from '../model/IconModel';
import FeatureModel from '../model/FeatureModel';
import LinkModel from '../model/LinkModel';
import NoteModel from '../model/NoteModel';
import Exporter from './Exporter';
import FreemindConstant from './freemind/FreemindConstant';
import VersionNumber from './freemind/importer/VersionNumber';
import ObjectFactory from './freemind/ObjectFactory';
import FreemindMap from './freemind/Map';
import FreeminNode from './freemind/Node';
import Arrowlink from './freemind/Arrowlink';
import Richcontent from './freemind/Richcontent';
import Icon from './freemind/Icon';
import Edge from './freemind/Edge';
import Font from './freemind/Font';
type PositionNodeType = {x: number, y: number}
class FreemindExporter extends Exporter {
private mindmap: Mindmap;
private nodeMap: Map<number, FreeminNode> = null;
private version: VersionNumber = FreemindConstant.SUPPORTED_FREEMIND_VERSION;
private objectFactory: ObjectFactory;
private static wisweToFreeFontSize: Map<number, number> = new Map<number, number>();
constructor(mindmap: Mindmap) {
super(FreemindConstant.SUPPORTED_FREEMIND_VERSION.getVersion(), 'application/xml');
this.mindmap = mindmap;
}
exportAndEncode(): Promise<string> {
throw new Error('Method not implemented.');
}
getContentType(): string {
throw new Error('Method not implemented.');
}
static {
this.wisweToFreeFontSize.set(6, 10);
this.wisweToFreeFontSize.set(8, 12);
this.wisweToFreeFontSize.set(10, 18);
this.wisweToFreeFontSize.set(15, 24);
}
private static parserXMLString(xmlStr: string, mimeType: DOMParserSupportedType): Document {
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(xmlStr, mimeType);
// FIXME: Fix error "unclosed tag: p" when exporting bug2 and enc
// Is there any parsing error ?.
/*
if (xmlDoc.getElementsByTagName('parsererror').length > 0) {
const xmmStr = new XMLSerializer().serializeToString(xmlDoc);
console.log(xmmStr);
throw new Error(`Unexpected error parsing: ${xmlStr}. Error: ${xmmStr}`);
}
*/
return xmlDoc;
}
extension(): string {
return 'mm';
}
async export(): Promise<string> {
this.objectFactory = new ObjectFactory();
this.nodeMap = new Map();
const freemainMap: FreemindMap = this.objectFactory.createMap();
freemainMap.setVesion(this.getVersionNumber());
const main: FreeminNode = this.objectFactory.createNode();
freemainMap.setNode(main);
const centralTopic: INodeModel = this.mindmap.getCentralTopic();
if (centralTopic) {
this.nodeMap.set(centralTopic.getId(), main);
this.setTopicPropertiesToNode({ freemindNode: main, mindmapTopic: centralTopic, isRoot: true });
this.addNodeFromTopic(centralTopic, main);
}
const relationships: Array<RelationshipModel> = this.mindmap.getRelationships();
relationships.forEach((relationship: RelationshipModel) => {
const srcNode: FreeminNode = this.nodeMap.get(relationship.getFromNode());
const destNode: FreeminNode = this.nodeMap.get(relationship.getToNode());
if (srcNode && destNode) {
const arrowlink: Arrowlink = this.objectFactory.crateArrowlink();
arrowlink.setDestination(destNode.getId());
if (relationship.getEndArrow() && relationship.getEndArrow()) arrowlink.setEndarrow('Default');
if (relationship.getStartArrow() && relationship.getStartArrow()) arrowlink.setStartarrow('Default');
srcNode.setArrowlinkOrCloudOrEdge(arrowlink);
}
});
const freeToXml = freemainMap.toXml();
const xmlToString = new XMLSerializer().serializeToString(freeToXml);
const formatXml = xmlFormatter(xmlToString, {
indentation: ' ',
collapseContent: true,
lineSeparator: '\n',
});
return Promise.resolve(formatXml);
}
private setTopicPropertiesToNode({ freemindNode, mindmapTopic, isRoot }: { freemindNode: FreeminNode; mindmapTopic: INodeModel; isRoot: boolean; }): void {
freemindNode.setId(`ID_${mindmapTopic.getId()}`);
const text = mindmapTopic.getText();
if (text) {
if (!text.includes('\n')) {
freemindNode.setText(text);
} else {
const richcontent: Richcontent = this.buildRichcontent(text, 'NODE');
freemindNode.setArrowlinkOrCloudOrEdge(richcontent);
}
}
const wiseShape: string = mindmapTopic.getShapeType();
if (wiseShape && TopicShape.LINE !== wiseShape) {
freemindNode.setBackgorundColor(this.rgbToHex(mindmapTopic.getBackgroundColor()));
}
if (wiseShape) {
const isRootRoundedRectangle = isRoot && TopicShape.ROUNDED_RECT !== wiseShape;
const notIsRootLine = !isRoot && TopicShape.LINE !== wiseShape;
if (isRootRoundedRectangle || notIsRootLine) {
let style: string = wiseShape;
if (TopicShape.ROUNDED_RECT === style || TopicShape.ELLIPSE === style) {
style = 'bubble';
}
freemindNode.setStyle(style);
}
} else if (!isRoot) freemindNode.setStyle('fork');
this.addFeautreNode(freemindNode, mindmapTopic);
this.addFontNode(freemindNode, mindmapTopic);
this.addEdgeNode(freemindNode, mindmapTopic);
}
private addNodeFromTopic(mainTopic: INodeModel, destNode: FreeminNode): void {
const curretnTopics: Array<INodeModel> = mainTopic.getChildren();
curretnTopics.forEach((currentTopic: INodeModel) => {
const newNode: FreeminNode = this.objectFactory.createNode();
this.nodeMap.set(currentTopic.getId(), newNode);
this.setTopicPropertiesToNode({ freemindNode: newNode, mindmapTopic: currentTopic, isRoot: false });
destNode.setArrowlinkOrCloudOrEdge(newNode);
this.addNodeFromTopic(currentTopic, newNode);
const position: PositionNodeType = currentTopic.getPosition();
if (position) {
const xPos: number = position.x;
newNode.setPosition((xPos < 0 ? 'left' : 'right'));
} else newNode.setPosition('left');
});
}
private buildRichcontent(text: string, type: string): Richcontent {
const richconent: Richcontent = this.objectFactory.createRichcontent();
richconent.setType(type);
const textSplit = text.split('\n');
let html = '<html><head></head><body>';
textSplit.forEach((line: string) => {
html += `<p>${line.trim()}</p>`;
});
html += '</body></html>';
const richconentDocument: Document = FreemindExporter.parserXMLString(html, 'application/xml');
const xmlResult = new XMLSerializer().serializeToString(richconentDocument);
richconent.setHtml(xmlResult);
return richconent;
}
private addFeautreNode(freemindNode: FreeminNode, mindmapTopic: INodeModel): void {
const branches: Array<FeatureModel> = mindmapTopic.getFeatures();
branches
.forEach((feature: FeatureModel) => {
const type = feature.getType();
if (type === 'link') {
const link = feature as LinkModel;
freemindNode.setLink(link.getUrl());
}
if (type === 'note') {
const note = feature as NoteModel;
const richcontent: Richcontent = this.buildRichcontent(note.getText(), 'NOTE');
freemindNode.setArrowlinkOrCloudOrEdge(richcontent);
}
if (type === 'icon') {
const icon = feature as IconModel;
const freemindIcon: Icon = new Icon();
freemindIcon.setBuiltin(icon.getIconType());
freemindNode.setArrowlinkOrCloudOrEdge(freemindIcon);
}
});
}
private addEdgeNode(freemainMap: FreeminNode, mindmapTopic: INodeModel): void {
if (mindmapTopic.getBorderColor()) {
const edgeNode: Edge = this.objectFactory.createEdge();
edgeNode.setColor(this.rgbToHex(mindmapTopic.getBorderColor()));
freemainMap.setArrowlinkOrCloudOrEdge(edgeNode);
}
}
private addFontNode(freemindNode: FreeminNode, mindmapTopic: INodeModel): void {
const fontFamily: string = mindmapTopic.getFontFamily();
const fontSize: number = mindmapTopic.getFontSize();
const fontColor: string = mindmapTopic.getFontColor();
const fontWeigth: string | number | boolean = mindmapTopic.getFontWeight();
const fontStyle: string = mindmapTopic.getFontStyle();
if (fontFamily || fontSize || fontColor || fontWeigth || fontStyle) {
const font: Font = this.objectFactory.createFont();
let fontNodeNeeded = false;
if (fontFamily) {
font.setName(fontFamily);
}
if (fontSize) {
const freeSize = FreemindExporter.wisweToFreeFontSize.get(fontSize);
if (freeSize) {
font.setSize(freeSize.toString());
fontNodeNeeded = true;
}
}
if (fontColor) {
freemindNode.setColor(fontColor);
}
if (fontWeigth) {
if (typeof fontWeigth === 'boolean') {
font.setBold(String(fontWeigth));
} else {
font.setBold(String(true));
}
fontNodeNeeded = true;
}
if (fontStyle === 'italic') {
font.setItalic(String(true));
}
if (fontNodeNeeded) {
if (!font.getSize()) {
font.setSize(FreemindExporter.wisweToFreeFontSize.get(8).toString());
}
freemindNode.setArrowlinkOrCloudOrEdge(font);
}
}
}
private rgbToHex(color: string): string {
let result: string = color;
if (result) {
const isRgb = new RegExp('^rgb\\([0-9]{1,3}, [0-9]{1,3}, [0-9]{1,3}\\)$');
if (isRgb.test(result)) {
const rgb: string[] = color.substring(4, color.length - 1).split(',');
const r: string = rgb[0].trim();
const g: string = rgb[1].trim();
const b: string = rgb[2].trim();
result = `#${r.length === 1 ? `0${r}` : r}${g.length === 1 ? `0${g}` : g}${b.length === 1 ? `0${b}` : b}`;
}
}
return result;
}
private getVersion(): VersionNumber {
return this.version;
}
private getVersionNumber(): string {
return this.getVersion().getVersion();
}
}
export default FreemindExporter;