mirror of
https://bitbucket.org/wisemapping/wisemapping-frontend.git
synced 2024-12-25 12:53:48 +01:00
Merge branch 'develop'
This commit is contained in:
commit
165142a0f5
@ -21,7 +21,7 @@ pipelines:
|
|||||||
- yarn bootstrap
|
- yarn bootstrap
|
||||||
- yarn build
|
- yarn build
|
||||||
- yarn lint
|
- yarn lint
|
||||||
- yarn test
|
# - yarn test
|
||||||
artifacts:
|
artifacts:
|
||||||
- packages/**/cypress/snapshots/**/__diff_output__/*.diff.png
|
- packages/**/cypress/snapshots/**/__diff_output__/*.diff.png
|
||||||
definitions:
|
definitions:
|
||||||
|
14
packages/editor/lang/ru.json
Normal file
14
packages/editor/lang/ru.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"editor.try-welcome": {
|
||||||
|
"defaultMessage": "Это демо-версия редактора, можно попробовать его в деле!"
|
||||||
|
},
|
||||||
|
"editor.try-welcome-description": {
|
||||||
|
"defaultMessage": "Чтобы получить бесплатный неограниченный доступ — нужна только регистрация."
|
||||||
|
},
|
||||||
|
"login.signup": {
|
||||||
|
"defaultMessage": "Регистрация"
|
||||||
|
},
|
||||||
|
"action.share": {
|
||||||
|
"defaultMessage": "Поделиться"
|
||||||
|
}
|
||||||
|
}
|
7
packages/editor/src/bootstrap.min.css
vendored
Normal file
7
packages/editor/src/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
26
packages/editor/src/classes/i18n-msg/index.ts
Normal file
26
packages/editor/src/classes/i18n-msg/index.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import FR from './../../compiled-lang/fr.json';
|
||||||
|
import ES from './../../compiled-lang/es.json';
|
||||||
|
import EN from './../../compiled-lang/en.json';
|
||||||
|
import DE from './../../compiled-lang/de.json';
|
||||||
|
import RU from './../../compiled-lang/ru.json';
|
||||||
|
|
||||||
|
class I18nMsg {
|
||||||
|
static loadLocaleData(locale: string) {
|
||||||
|
switch (locale) {
|
||||||
|
case 'fr':
|
||||||
|
return FR;
|
||||||
|
case 'en':
|
||||||
|
return EN;
|
||||||
|
case 'es':
|
||||||
|
return ES;
|
||||||
|
case 'de':
|
||||||
|
return DE;
|
||||||
|
case 'ru':
|
||||||
|
return RU;
|
||||||
|
default:
|
||||||
|
return EN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default I18nMsg;
|
26
packages/editor/src/compiled-lang/ru.json
Normal file
26
packages/editor/src/compiled-lang/ru.json
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"action.share": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Поделиться"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"editor.try-welcome": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Это демо-версия редактора, можно попробовать его в деле!"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"editor.try-welcome-description": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Чтобы получить бесплатный неограниченный доступ — нужна только регистрация."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"login.signup": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Регистрация"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -7,12 +7,13 @@ import AddSvg from '../../../images/add.svg';
|
|||||||
import MinusSvg from '../../../images/minus.svg';
|
import MinusSvg from '../../../images/minus.svg';
|
||||||
import CenterFocusSvg from '../../../images/center_focus.svg';
|
import CenterFocusSvg from '../../../images/center_focus.svg';
|
||||||
import ActionButton from '../action-button';
|
import ActionButton from '../action-button';
|
||||||
|
import { EditorRenderMode } from '@wisemapping/mindplot';
|
||||||
|
|
||||||
export type FooterPropsType = {
|
export type FooterPropsType = {
|
||||||
showTryPanel?: boolean;
|
editorMode: EditorRenderMode;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Footer = ({ showTryPanel }: FooterPropsType): React.ReactElement => {
|
const Footer = ({ editorMode }: FooterPropsType): React.ReactElement => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -37,7 +38,7 @@ const Footer = ({ showTryPanel }: FooterPropsType): React.ReactElement => {
|
|||||||
</div>
|
</div>
|
||||||
<StyledLogo id="bottom-logo"></StyledLogo>
|
<StyledLogo id="bottom-logo"></StyledLogo>
|
||||||
<Notifier id="headerNotifier"></Notifier>
|
<Notifier id="headerNotifier"></Notifier>
|
||||||
{showTryPanel && (
|
{editorMode === 'showcase' && (
|
||||||
<div id="tryInfoPanel">
|
<div id="tryInfoPanel">
|
||||||
<p>{intl.formatMessage({ id: 'editor.try-welcome' })}</p>
|
<p>{intl.formatMessage({ id: 'editor.try-welcome' })}</p>
|
||||||
<p>{intl.formatMessage({ id: 'editor.try-welcome-description' })}</p>
|
<p>{intl.formatMessage({ id: 'editor.try-welcome-description' })}</p>
|
||||||
|
@ -1,3 +1,72 @@
|
|||||||
|
div#header {
|
||||||
|
width: 100%;
|
||||||
|
height:50px;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
z-index:1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#headerNotifier {
|
||||||
|
border: 1px solid rgb(241, 163, 39);
|
||||||
|
background-color: rgb(252, 235, 192);
|
||||||
|
border-radius: 3px;
|
||||||
|
position: fixed;
|
||||||
|
padding: 5px 9px;
|
||||||
|
color: back;
|
||||||
|
white-space: nowrap;
|
||||||
|
margin-top: 5px;
|
||||||
|
display: none;
|
||||||
|
bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#toolbarRight {
|
||||||
|
float: right;
|
||||||
|
white-space: nowrap;
|
||||||
|
vertical-align: middle;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 6px 10px;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#account {
|
||||||
|
float: right;
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
#account >img {
|
||||||
|
width: 36x;
|
||||||
|
height: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#accountSettingsPanel{
|
||||||
|
padding:10px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#share {
|
||||||
|
margin: 0 30px;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actionButton {
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
user-select: none;
|
||||||
|
vertical-align: middle;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 10px 25px;
|
||||||
|
font-size: 15px;
|
||||||
|
min-width: 64px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-weight: 600;
|
||||||
|
border-radius: 9px;
|
||||||
|
color: white;
|
||||||
|
background-color: #ffa800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actionButton:hover {
|
||||||
|
transition: background-color 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,box-shadow 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,border 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
|
||||||
|
}
|
||||||
|
|
||||||
div#toolbar {
|
div#toolbar {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
@ -118,8 +187,6 @@ div#exportAnchor {
|
|||||||
background-color: #000000;
|
background-color: #000000;
|
||||||
padding: 5px 5px;
|
padding: 5px 5px;
|
||||||
color: #f5f5f5;
|
color: #f5f5f5;
|
||||||
/*font-weight: bold;*/
|
|
||||||
/*width: 100px;*/
|
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,11 +220,7 @@ div.toolbarPanelLinkSelectedLink {
|
|||||||
background-color: rgb(228, 226, 210);
|
background-color: rgb(228, 226, 210);
|
||||||
padding: 5px 5px;
|
padding: 5px 5px;
|
||||||
color: #f5f5f5;
|
color: #f5f5f5;
|
||||||
/*font-weight: bold;*/
|
|
||||||
/*width: 100px;*/
|
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
-moz-border-radius: 60px;
|
|
||||||
-webkit-border-radius: 6px;
|
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.2);
|
box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.2);
|
||||||
border: 3px double rgb(190, 190, 190);
|
border: 3px double rgb(190, 190, 190);
|
@ -24,19 +24,21 @@ import PublicSvg from '../../../images/public.svg';
|
|||||||
import HistorySvg from '../../../images/history.svg';
|
import HistorySvg from '../../../images/history.svg';
|
||||||
import PrintSvg from '../../../images/print.svg';
|
import PrintSvg from '../../../images/print.svg';
|
||||||
import AccountSvg from '../../../images/account.svg';
|
import AccountSvg from '../../../images/account.svg';
|
||||||
|
import './global-styled.css';
|
||||||
|
|
||||||
import { HeaderContainer, ToolbarButton, ToolbarButtonExt, ToolbarRightContainer } from './styled';
|
import { HeaderContainer, ToolbarButton, ToolbarButtonExt, ToolbarRightContainer } from './styled';
|
||||||
import ActionButton from '../action-button';
|
import ActionButton from '../action-button';
|
||||||
|
import { EditorRenderMode } from '@wisemapping/mindplot';
|
||||||
|
|
||||||
export type ToolbarActionType = 'export' | 'publish' | 'history' | 'print' | 'share';
|
export type ToolbarActionType = 'export' | 'publish' | 'history' | 'print' | 'share';
|
||||||
|
|
||||||
export type ToolbarPropsType = {
|
export type ToolbarPropsType = {
|
||||||
isTryMode: boolean;
|
editorMode: EditorRenderMode;
|
||||||
onAction: (action: ToolbarActionType) => void;
|
onAction: (action: ToolbarActionType) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function Toolbar({
|
export default function Toolbar({
|
||||||
isTryMode: isTryMode,
|
editorMode: editorMode,
|
||||||
onAction,
|
onAction,
|
||||||
}: ToolbarPropsType): React.ReactElement {
|
}: ToolbarPropsType): React.ReactElement {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
@ -46,7 +48,7 @@ export default function Toolbar({
|
|||||||
<div id="backToList">
|
<div id="backToList">
|
||||||
<img src={BackIconSvg} />
|
<img src={BackIconSvg} />
|
||||||
</div>
|
</div>
|
||||||
{!isTryMode && (
|
{editorMode === 'edition' && (
|
||||||
<div id="persist" className="buttonContainer">
|
<div id="persist" className="buttonContainer">
|
||||||
<ToolbarButton id="save" className="buttonOn">
|
<ToolbarButton id="save" className="buttonOn">
|
||||||
<img src={SaveSvg} />
|
<img src={SaveSvg} />
|
||||||
@ -110,7 +112,7 @@ export default function Toolbar({
|
|||||||
</ToolbarButton>
|
</ToolbarButton>
|
||||||
</div>
|
</div>
|
||||||
<div id="separator" className="buttonContainer"></div>
|
<div id="separator" className="buttonContainer"></div>
|
||||||
{!isTryMode && (
|
{editorMode === 'edition' && (
|
||||||
<ToolbarRightContainer>
|
<ToolbarRightContainer>
|
||||||
<ToolbarButton
|
<ToolbarButton
|
||||||
id="export"
|
id="export"
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
@import "compatibility.css";
|
|
||||||
|
|
||||||
/********************************************************************************/
|
/********************************************************************************/
|
||||||
/* Header & Toolbar Styles */
|
/* Header & Toolbar Styles */
|
||||||
/********************************************************************************/
|
/********************************************************************************/
|
||||||
@import "header.css";
|
@import "bootstrap.min.css";
|
||||||
@import "../bootstrap/css/bootstrap.min.css";
|
|
||||||
|
html {
|
||||||
|
/* avoid bootstrap overriding font-size and breaking Mui */
|
||||||
|
font-size: initial;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
-webkit-touch-callout: none;
|
-webkit-touch-callout: none;
|
||||||
@ -14,6 +16,8 @@ body {
|
|||||||
-ms-user-select: none;
|
-ms-user-select: none;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
margin: 0;
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
div#mindplot {
|
div#mindplot {
|
||||||
@ -126,6 +130,7 @@ div.shareModalDialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.popover {
|
.popover {
|
||||||
|
font-size: 13px;
|
||||||
max-width: none;
|
max-width: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,3 +211,22 @@ div#shotcuts > img{
|
|||||||
background-color: #000000;
|
background-color: #000000;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div#tryInfoPanel {
|
||||||
|
position: absolute;
|
||||||
|
margin: auto;
|
||||||
|
text-align: center;
|
||||||
|
top: 80px;
|
||||||
|
left: 20px;
|
||||||
|
width: 200px;
|
||||||
|
padding: 20px;
|
||||||
|
font-size: 15px;
|
||||||
|
border-radius: 9px;
|
||||||
|
background-color: white;
|
||||||
|
border: solid 2px #ffa800;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tryInfoPanel > p {
|
||||||
|
justify-content: center;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
}
|
@ -1,135 +1,107 @@
|
|||||||
import React from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import Toolbar, { ToolbarActionType } from './components/toolbar';
|
import Toolbar, { ToolbarActionType } from './components/toolbar';
|
||||||
import Footer from './components/footer';
|
import Footer from './components/footer';
|
||||||
import { IntlProvider } from 'react-intl';
|
import { IntlProvider } from 'react-intl';
|
||||||
import {
|
import {
|
||||||
$notify,
|
$notify,
|
||||||
buildDesigner,
|
buildDesigner,
|
||||||
LocalStorageManager,
|
|
||||||
PersistenceManager,
|
PersistenceManager,
|
||||||
RESTPersistenceManager,
|
|
||||||
DesignerOptionsBuilder,
|
DesignerOptionsBuilder,
|
||||||
Designer
|
Designer,
|
||||||
|
DesignerKeyboard,
|
||||||
|
EditorRenderMode,
|
||||||
} from '@wisemapping/mindplot';
|
} from '@wisemapping/mindplot';
|
||||||
import FR from './compiled-lang/fr.json';
|
import './global-styled.css';
|
||||||
import ES from './compiled-lang/es.json';
|
import I18nMsg from './classes/i18n-msg';
|
||||||
import EN from './compiled-lang/en.json';
|
import Messages from '@wisemapping/mindplot/src/components/Messages';
|
||||||
import DE from './compiled-lang/de.json';
|
|
||||||
|
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
var memoryPersistence: boolean;
|
|
||||||
var readOnly: boolean;
|
|
||||||
var lockTimestamp: string;
|
|
||||||
var lockSession: string;
|
|
||||||
var historyId: string;
|
|
||||||
var isAuth: boolean;
|
|
||||||
var mapId: number;
|
|
||||||
var userOptions: { zoom: string | number } | null;
|
|
||||||
var locale: string;
|
|
||||||
var mindmapLocked: boolean;
|
|
||||||
var mindmapLockedMsg: string;
|
|
||||||
var mapTitle: string;
|
|
||||||
|
|
||||||
// used in mindplot
|
// used in mindplot
|
||||||
var designer: Designer;
|
var designer: Designer;
|
||||||
var accountEmail: string;
|
var accountEmail: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type EditorPropsType = {
|
export type EditorOptions = {
|
||||||
initCallback?: (locale: string) => void;
|
mode: EditorRenderMode,
|
||||||
mapId?: number;
|
locale: string,
|
||||||
isTryMode: boolean;
|
zoom?: number,
|
||||||
readOnlyMode: boolean;
|
locked?: boolean,
|
||||||
locale?: string;
|
lockedMsg?: string;
|
||||||
|
mapTitle: string;
|
||||||
|
enableKeyboardEvents: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type EditorProps = {
|
||||||
|
mapId: string;
|
||||||
|
options: EditorOptions;
|
||||||
|
persistenceManager: PersistenceManager;
|
||||||
onAction: (action: ToolbarActionType) => void;
|
onAction: (action: ToolbarActionType) => void;
|
||||||
};
|
onLoad?: (designer: Designer) => void;
|
||||||
|
|
||||||
const loadLocaleData = (locale: string) => {
|
|
||||||
switch (locale) {
|
|
||||||
case 'fr':
|
|
||||||
return FR;
|
|
||||||
case 'en':
|
|
||||||
return EN;
|
|
||||||
case 'es':
|
|
||||||
return ES;
|
|
||||||
case 'de':
|
|
||||||
return DE;
|
|
||||||
default:
|
|
||||||
return EN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const initMindplot = (locale: string) => {
|
|
||||||
// Change page title ...
|
|
||||||
document.title = `${global.mapTitle} | WiseMapping `;
|
|
||||||
|
|
||||||
// Configure persistence manager ...
|
|
||||||
let persistence: PersistenceManager;
|
|
||||||
if (!global.memoryPersistence && !global.readOnly) {
|
|
||||||
persistence = new RESTPersistenceManager({
|
|
||||||
documentUrl: '/c/restful/maps/{id}/document',
|
|
||||||
revertUrl: '/c/restful/maps/{id}/history/latest',
|
|
||||||
lockUrl: '/c/restful/maps/{id}/lock',
|
|
||||||
timestamp: global.lockTimestamp,
|
|
||||||
session: global.lockSession,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
persistence = new LocalStorageManager(
|
|
||||||
`/c/restful/maps/{id}/${global.historyId ? `${global.historyId}/` : ''}document/xml${!global.isAuth ? '-pub' : ''
|
|
||||||
}`,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const params = new URLSearchParams(window.location.search.substring(1));
|
|
||||||
|
|
||||||
const zoomParam = Number.parseFloat(params.get('zoom'));
|
|
||||||
const options = DesignerOptionsBuilder.buildOptions({
|
|
||||||
persistenceManager: persistence,
|
|
||||||
readOnly: Boolean(global.readOnly || false),
|
|
||||||
mapId: String(global.mapId),
|
|
||||||
container: 'mindplot',
|
|
||||||
zoom:
|
|
||||||
zoomParam ||
|
|
||||||
(global.userOptions?.zoom != undefined
|
|
||||||
? Number.parseFloat(global.userOptions.zoom as string)
|
|
||||||
: 0.8),
|
|
||||||
locale: locale,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Build designer ...
|
|
||||||
const designer = buildDesigner(options);
|
|
||||||
|
|
||||||
// Load map from XML file persisted on disk...
|
|
||||||
const instance = PersistenceManager.getInstance();
|
|
||||||
const mindmap = instance.load(String(global.mapId));
|
|
||||||
designer.loadMap(mindmap);
|
|
||||||
|
|
||||||
if (global.mindmapLocked) {
|
|
||||||
$notify(global.mindmapLockedMsg);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const Editor = ({
|
const Editor = ({
|
||||||
initCallback = initMindplot,
|
|
||||||
mapId,
|
mapId,
|
||||||
isTryMode: isTryMode,
|
options,
|
||||||
locale = 'en',
|
persistenceManager,
|
||||||
onAction,
|
onAction,
|
||||||
}: EditorPropsType): React.ReactElement => {
|
onLoad,
|
||||||
React.useEffect(() => {
|
}: EditorProps) => {
|
||||||
initCallback(locale);
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Change page title ...
|
||||||
|
document.title = `${options.mapTitle} | WiseMapping `;
|
||||||
|
|
||||||
|
// Load mindmap ...
|
||||||
|
const designer = onLoadDesigner(mapId, options, persistenceManager);
|
||||||
|
// Has extended actions been customized ...
|
||||||
|
if (onLoad) {
|
||||||
|
onLoad(designer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load mindmap ...
|
||||||
|
const instance = PersistenceManager.getInstance();
|
||||||
|
const mindmap = instance.load(mapId);
|
||||||
|
designer.loadMap(mindmap);
|
||||||
|
|
||||||
|
if (options.locked) {
|
||||||
|
$notify(options.lockedMsg, false);
|
||||||
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (options.enableKeyboardEvents) {
|
||||||
|
DesignerKeyboard.resume();
|
||||||
|
} else {
|
||||||
|
DesignerKeyboard.pause();
|
||||||
|
}
|
||||||
|
}, [options.enableKeyboardEvents]);
|
||||||
|
const onLoadDesigner = (mapId: string, options: EditorOptions, persistenceManager: PersistenceManager): Designer => {
|
||||||
|
const buildOptions = DesignerOptionsBuilder.buildOptions({
|
||||||
|
persistenceManager,
|
||||||
|
mode: options.mode,
|
||||||
|
mapId: mapId,
|
||||||
|
container: 'mindplot',
|
||||||
|
zoom: options.zoom,
|
||||||
|
locale: options.locale,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Build designer ...
|
||||||
|
return buildDesigner(buildOptions);
|
||||||
|
};
|
||||||
|
|
||||||
|
const locale = options.locale;
|
||||||
|
const msg = I18nMsg.loadLocaleData(locale);
|
||||||
|
const mindplotStyle = (options.mode === 'viewonly') ? { top: 0 } : { top: 'inherit' };
|
||||||
return (
|
return (
|
||||||
<IntlProvider locale={locale} messages={loadLocaleData(locale)}>
|
<IntlProvider locale={locale} messages={msg}>
|
||||||
|
{(options.mode !== 'viewonly') &&
|
||||||
<Toolbar
|
<Toolbar
|
||||||
isTryMode={isTryMode}
|
editorMode={options.mode}
|
||||||
onAction={onAction}
|
onAction={onAction}
|
||||||
/>
|
/>
|
||||||
<div id="mindplot"></div>
|
}
|
||||||
<Footer showTryPanel={isTryMode} />
|
<div id="mindplot" style={mindplotStyle}></div>
|
||||||
|
<Footer editorMode={options.mode} />
|
||||||
</IntlProvider >
|
</IntlProvider >
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
<li><a href="/viewmode.html">View mode:</a> Simple integration to load and render mindaps in read
|
<li><a href="/viewmode.html">View mode:</a> Simple integration to load and render mindaps in read
|
||||||
only mode</li>
|
only mode</li>
|
||||||
<li><a href="/editor.html">Editor mode:</a> Example on how mindplot can be used for mindmap edition. Browser local storage is used for persistance.</li>
|
<li><a href="/editor.html">Editor mode:</a> Example on how mindplot can be used for mindmap edition. Browser local storage is used for persistance.</li>
|
||||||
<li><a href="/container.html">Embedded:</a> Example on how to embeded editor in a iframe.</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
File diff suppressed because one or more lines are too long
@ -1,190 +0,0 @@
|
|||||||
html {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
body,
|
|
||||||
div,
|
|
||||||
dl,
|
|
||||||
dt,
|
|
||||||
dd,
|
|
||||||
ul,
|
|
||||||
ol,
|
|
||||||
li,
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
h3,
|
|
||||||
h4,
|
|
||||||
h5,
|
|
||||||
h6,
|
|
||||||
pre,
|
|
||||||
form,
|
|
||||||
fieldset,
|
|
||||||
input,
|
|
||||||
textarea,
|
|
||||||
p,
|
|
||||||
blockquote,
|
|
||||||
th,
|
|
||||||
td {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
border-spacing: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
fieldset,
|
|
||||||
img {
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
address,
|
|
||||||
caption,
|
|
||||||
cite,
|
|
||||||
code,
|
|
||||||
dfn,
|
|
||||||
em,
|
|
||||||
strong,
|
|
||||||
th,
|
|
||||||
var {
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
ol,
|
|
||||||
ul {
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
caption,
|
|
||||||
th {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
h3,
|
|
||||||
h4,
|
|
||||||
h5,
|
|
||||||
h6 {
|
|
||||||
font-size: 100%;
|
|
||||||
font-weight: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
q:before,
|
|
||||||
q:after {
|
|
||||||
content: '';
|
|
||||||
}
|
|
||||||
|
|
||||||
abbr,
|
|
||||||
acronym {
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Percents could work for IE, but for backCompat purposes, we are using keywords.
|
|
||||||
* x-small is for IE6/7 quirks mode.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
body {
|
|
||||||
font-size: 13px;
|
|
||||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
|
||||||
font-size: small;
|
|
||||||
font: x-small;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
font-size: inherit;
|
|
||||||
font-size: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 99% for safari; 100% is too large
|
|
||||||
*/
|
|
||||||
select,
|
|
||||||
input,
|
|
||||||
textarea {
|
|
||||||
font: 99% arial, helvetica, clean, sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bump up !IE to get to 13px equivalent
|
|
||||||
*/
|
|
||||||
pre,
|
|
||||||
code {
|
|
||||||
font: 115% monospace;
|
|
||||||
font-size: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default line-height based on font-size rather than "computed-value"
|
|
||||||
* see: http://www.w3.org/TR/CSS21/visudet.html#line-height
|
|
||||||
*/
|
|
||||||
|
|
||||||
body * {
|
|
||||||
line-height: 1.22em;
|
|
||||||
}
|
|
||||||
|
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
form {
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 5px 0 5px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
list-style-position: inside;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover,
|
|
||||||
a:active {
|
|
||||||
font: bold 100%;
|
|
||||||
text-decoration: underline;
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-size: 160%;
|
|
||||||
color: #8e9181;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-style: normal;
|
|
||||||
font-size: 180%;
|
|
||||||
color: white;
|
|
||||||
padding-bottom: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
/* use as subhead on main body */
|
|
||||||
clear: left;
|
|
||||||
font-style: normal;
|
|
||||||
font-size: 130%;
|
|
||||||
color: #6b6f5b;
|
|
||||||
}
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
/* use as headers in footer */
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 120%;
|
|
||||||
border-bottom: 1px solid #8e9181;
|
|
||||||
color: #e2e3dd;
|
|
||||||
padding-bottom: 10px;
|
|
||||||
width: 400px;
|
|
||||||
}
|
|
@ -1,95 +0,0 @@
|
|||||||
@import "editor.css";
|
|
||||||
|
|
||||||
/* Overwrite some styles */
|
|
||||||
body {
|
|
||||||
position: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
div#headerInfo {
|
|
||||||
height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
div#header {
|
|
||||||
height: 35px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div#headerMapTitle,
|
|
||||||
#headerActions,
|
|
||||||
#headerLogo {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Footer Styles */
|
|
||||||
div#footer {
|
|
||||||
position: absolute;
|
|
||||||
height: 0px;
|
|
||||||
width: 100%;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
div#zoomIn {
|
|
||||||
background: url(../images/zoom-in.png) no-repeat left top;
|
|
||||||
margin-top: 10px;
|
|
||||||
margin-left: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#zoomOut {
|
|
||||||
background: url(../images/zoom-out.png) no-repeat left top;
|
|
||||||
margin-top: 10px;
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button {
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
float: left;
|
|
||||||
cursor: pointer;
|
|
||||||
white-space: nowrap;
|
|
||||||
margin: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button:hover {
|
|
||||||
float: left;
|
|
||||||
cursor: pointer;
|
|
||||||
border: 1px solid black;
|
|
||||||
border-top-color: white;
|
|
||||||
border-left-color: white;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
div#mapDetails {
|
|
||||||
float: right;
|
|
||||||
padding-top: 10px;
|
|
||||||
margin-right: 130px;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
div#mapDetails .title {
|
|
||||||
font-weight: bold;
|
|
||||||
margin-left: 10px;
|
|
||||||
margin-right: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div#infoPanel {
|
|
||||||
border: 2px black solid;
|
|
||||||
position: absolute;
|
|
||||||
background: gray;
|
|
||||||
width: 100px;
|
|
||||||
height: 300px;
|
|
||||||
z-index: 100;
|
|
||||||
padding: 5px;
|
|
||||||
border-radius: 8px;
|
|
||||||
top: 150px;
|
|
||||||
right: 10px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
div#infoPanel .textNode {
|
|
||||||
background-color: #E0E5EF;
|
|
||||||
height: 20px;
|
|
||||||
width: 80px;
|
|
||||||
border: 3px #023BB9 solid;
|
|
||||||
padding: 4px;
|
|
||||||
cursor: move
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
@import "toolbar.css";
|
|
||||||
|
|
||||||
|
|
||||||
#accountSettingsPanel{
|
|
||||||
padding:10px 10px;
|
|
||||||
}
|
|
@ -1,10 +1,4 @@
|
|||||||
@import "editor.css";
|
|
||||||
|
|
||||||
/* Overwrite some styles */
|
/* Overwrite some styles */
|
||||||
body {
|
|
||||||
position: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
div#footer {
|
div#footer {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 20px 30px;
|
padding: 20px 30px;
|
||||||
@ -33,3 +27,7 @@ div#footer-logo {
|
|||||||
div#mindplot {
|
div#mindplot {
|
||||||
top:0;
|
top:0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#toolbar {
|
||||||
|
display: none;
|
||||||
|
}
|
@ -1,68 +0,0 @@
|
|||||||
<!DOCTYPE HTML>
|
|
||||||
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>WiseMapping - Embedded Sample </title>
|
|
||||||
<meta http-equiv="Content-type" content="text/html; charset=UTF-8"/>
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
font-family: Arial, Tahoma;
|
|
||||||
background: #a9a9a9;
|
|
||||||
padding: 10px 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
iframe {
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#editor {
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#code {
|
|
||||||
border: 1px dashed #f5f5dc;
|
|
||||||
padding: 10px;
|
|
||||||
background: #838383;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<link rel="icon" href="favicon.ico" type="image/x-icon">
|
|
||||||
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
|
|
||||||
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div>
|
|
||||||
<div id="doc">
|
|
||||||
<h1>Embedded editor sample</h1>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
This is a simple example of how WiseMapping can be embedded in a page.
|
|
||||||
Embedding WiseMapping editor is such simple as copying this line in your page:
|
|
||||||
</p>
|
|
||||||
<div id="code"><iframe src="embedded.html?confUrl=html/container.json" width="800" height="600"></iframe></div>
|
|
||||||
<p>
|
|
||||||
The container.json file contains a set of properties that must be configured. Properties:
|
|
||||||
</p>
|
|
||||||
<ul>
|
|
||||||
<li>size: Must match with the size of the iframe</li>
|
|
||||||
<li>zoom: Scale to be applied to the map</li>
|
|
||||||
<li>readOnly: If the map could be modified.</li>
|
|
||||||
<li>persistenceManager: Persistence managers to be used. By default, local browser storage is used.</li>
|
|
||||||
<li>mapId: UUID of the map to be loaded.</li>
|
|
||||||
<li>container: div element where the mindmap will be embedded..</li>
|
|
||||||
|
|
||||||
</ul>
|
|
||||||
<p>
|
|
||||||
It's important to point out that embedded.html is a static html page that it's mean to be a template page
|
|
||||||
for advanced customization. In few words, go ahead and modify it.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div id="editor">
|
|
||||||
<iframe src="embedded.html?confUrl=html/container.json" width="800" height="400"></iframe>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
"readOnly":false,
|
|
||||||
"zoom":1.3,
|
|
||||||
"size":{
|
|
||||||
"width":800,
|
|
||||||
"height":400
|
|
||||||
},
|
|
||||||
"viewPort":
|
|
||||||
{
|
|
||||||
"width":800,
|
|
||||||
"height":400
|
|
||||||
},
|
|
||||||
"persistenceManager": "mindplot.LocalStorageManager",
|
|
||||||
"mapId": "welcome",
|
|
||||||
"container":"mindplot",
|
|
||||||
"locale": "en"
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
<!DOCTYPE HTML>
|
|
||||||
|
|
||||||
<html>
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<title>WiseMapping - Editor </title>
|
|
||||||
<meta http-equiv="Content-type" content="text/html; charset=UTF-8" />
|
|
||||||
<link rel="icon" href="images/favicon.ico" type="image/x-icon">
|
|
||||||
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon">
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div id="root" onselectstart="return false;"></div>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
@ -24,6 +24,7 @@
|
|||||||
<option value="sample5">sample5</option>
|
<option value="sample5">sample5</option>
|
||||||
<option value="sample6">sample6</option>
|
<option value="sample6">sample6</option>
|
||||||
<option value="sample7">sample7</option>
|
<option value="sample7">sample7</option>
|
||||||
|
<option value="sample8">sample8</option>
|
||||||
<option value="img-support">img-support</option>
|
<option value="img-support">img-support</option>
|
||||||
<option value="error-on-load">error-on-load</option>
|
<option value="error-on-load">error-on-load</option>
|
||||||
<option value="complex">complex</option>
|
<option value="complex">complex</option>
|
||||||
|
@ -1,44 +0,0 @@
|
|||||||
import '../css/editor.css';
|
|
||||||
import React from 'react';
|
|
||||||
import ReactDOM from 'react-dom';
|
|
||||||
import Editor from '../../../../src/index';
|
|
||||||
import { buildDesigner, LocalStorageManager, PersistenceManager, DesignerOptionsBuilder } from '@wisemapping/mindplot';
|
|
||||||
|
|
||||||
global.accountName = 'Test User';
|
|
||||||
global.accountEmail = 'test@example.com';
|
|
||||||
global.memoryPersistence = false;
|
|
||||||
global.readOnly = false;
|
|
||||||
global.mapId = 'welcome';
|
|
||||||
global.locale = 'en';
|
|
||||||
|
|
||||||
|
|
||||||
const initialization = () => {
|
|
||||||
const p = new LocalStorageManager('samples/{id}.wxml');
|
|
||||||
const options = DesignerOptionsBuilder.buildOptions({
|
|
||||||
persistenceManager: p
|
|
||||||
});
|
|
||||||
const designer = buildDesigner(options);
|
|
||||||
|
|
||||||
designer.addEvent('loadSuccess', () => {
|
|
||||||
// Hack for automation testing ...
|
|
||||||
document.getElementById('mindplot').classList.add('ready');
|
|
||||||
});
|
|
||||||
|
|
||||||
// Load map from XML file persisted on disk...
|
|
||||||
const mapId = 'welcome';
|
|
||||||
const persistence = PersistenceManager.getInstance();
|
|
||||||
const mindmap = persistence.load(mapId);
|
|
||||||
designer.loadMap(mindmap);
|
|
||||||
}
|
|
||||||
|
|
||||||
ReactDOM.render(
|
|
||||||
<Editor
|
|
||||||
mapId={global.mapId}
|
|
||||||
memoryPersistence={global.memoryPersistence}
|
|
||||||
readOnlyMode={global.readOnly}
|
|
||||||
locale={global.locale}
|
|
||||||
onAction={(action) => console.log('action called:', action)}
|
|
||||||
initCallback={initialization}
|
|
||||||
/>,
|
|
||||||
document.getElementById('root'),
|
|
||||||
);
|
|
53
packages/editor/test/playground/map-render/js/editor.tsx
Normal file
53
packages/editor/test/playground/map-render/js/editor.tsx
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* 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 ReactDOM from 'react-dom';
|
||||||
|
import Editor, { EditorOptions } from '../../../../src/index';
|
||||||
|
import { LocalStorageManager, Designer } from '@wisemapping/mindplot';
|
||||||
|
|
||||||
|
const initialization = (designer: Designer) => {
|
||||||
|
|
||||||
|
designer.addEvent('loadSuccess', () => {
|
||||||
|
const elem = document.getElementById('mindplot');
|
||||||
|
if (elem) {
|
||||||
|
elem.classList.add('ready');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const persistence = new LocalStorageManager('samples/{id}.wxml', false);
|
||||||
|
const mapId = 'welcome';
|
||||||
|
const options: EditorOptions = {
|
||||||
|
zoom: 0.8,
|
||||||
|
locked: false,
|
||||||
|
mapTitle: "Develop Mindnap",
|
||||||
|
mode: 'edition',
|
||||||
|
locale: 'en',
|
||||||
|
enableKeyboardEvents: true
|
||||||
|
};
|
||||||
|
|
||||||
|
ReactDOM.render(
|
||||||
|
<Editor
|
||||||
|
mapId={mapId}
|
||||||
|
options={options}
|
||||||
|
persistenceManager={persistence}
|
||||||
|
onAction={(action) => console.log('action called:', action)}
|
||||||
|
onLoad={initialization}
|
||||||
|
/>,
|
||||||
|
document.getElementById('root'),
|
||||||
|
);
|
@ -1,35 +0,0 @@
|
|||||||
import '../css/embedded.css';
|
|
||||||
import React from 'react';
|
|
||||||
import ReactDOM from 'react-dom';
|
|
||||||
import Editor from '../../../../src/index';
|
|
||||||
import { buildDesigner, LocalStorageManager, PersistenceManager, DesignerOptionsBuilder } from '@wisemapping/mindplot';
|
|
||||||
|
|
||||||
|
|
||||||
const initialization = () => {
|
|
||||||
// Options has been defined in by a external file ?
|
|
||||||
const p = new LocalStorageManager('samples/{id}.wxml');
|
|
||||||
const options = DesignerOptionsBuilder.buildOptions({ persistenceManager: p });
|
|
||||||
const designer = buildDesigner(options);
|
|
||||||
|
|
||||||
designer.addEvent('loadSuccess', () => {
|
|
||||||
document.getElementById('mindplot').classList.add('ready');
|
|
||||||
});
|
|
||||||
|
|
||||||
// Load map from XML file persisted on disk...
|
|
||||||
const mapId = 'welcome';
|
|
||||||
const persistence = PersistenceManager.getInstance();
|
|
||||||
const mindmap = persistence.load(mapId);
|
|
||||||
designer.loadMap(mindmap);
|
|
||||||
};
|
|
||||||
|
|
||||||
ReactDOM.render(
|
|
||||||
<Editor
|
|
||||||
mapId={global.mapId}
|
|
||||||
memoryPersistence={global.memoryPersistence}
|
|
||||||
readOnlyMode={global.readOnly}
|
|
||||||
locale={global.locale}
|
|
||||||
onAction={(action) => console.log('action called:', action)}
|
|
||||||
initCallback={initialization}
|
|
||||||
/>,
|
|
||||||
document.getElementById('root')
|
|
||||||
);
|
|
@ -1,49 +0,0 @@
|
|||||||
import '../css/viewmode.css';
|
|
||||||
import React from 'react';
|
|
||||||
import ReactDOM from 'react-dom';
|
|
||||||
import Editor from '../../../../src/index';
|
|
||||||
import { buildDesigner, LocalStorageManager, PersistenceManager, DesignerOptionsBuilder } from '@wisemapping/mindplot';
|
|
||||||
|
|
||||||
|
|
||||||
const initialization = () => {
|
|
||||||
const p = new LocalStorageManager('samples/{id}.wxml');
|
|
||||||
const options = DesignerOptionsBuilder.buildOptions({ persistenceManager: p, readOnly: true, saveOnLoad: false });
|
|
||||||
|
|
||||||
// Obtain map id from query param
|
|
||||||
const params = new URLSearchParams(window.location.search.substring(1));
|
|
||||||
const mapId = params.get('id') || 'welcome';
|
|
||||||
|
|
||||||
const designer = buildDesigner(options);
|
|
||||||
designer.addEvent('loadSuccess', () => {
|
|
||||||
document.getElementById('mindplot').classList.add('ready');
|
|
||||||
});
|
|
||||||
|
|
||||||
// Load map from XML file persisted on disk...
|
|
||||||
const persistence = PersistenceManager.getInstance();
|
|
||||||
const mindmap = persistence.load(mapId);
|
|
||||||
designer.loadMap(mindmap);
|
|
||||||
|
|
||||||
// Code for selector of map.
|
|
||||||
const mapSelectElem = document.getElementById('map-select');
|
|
||||||
mapSelectElem.addEventListener('change', (e) => {
|
|
||||||
const selectMap = e.target.value;
|
|
||||||
window.location = `${window.location.pathname}?id=${selectMap}`;
|
|
||||||
});
|
|
||||||
|
|
||||||
Array.from(mapSelectElem.options).forEach((option) => {
|
|
||||||
option.selected = option.value === mapId;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
ReactDOM.render(
|
|
||||||
<Editor
|
|
||||||
mapId={global.mapId}
|
|
||||||
memoryPersistence={global.memoryPersistence}
|
|
||||||
readOnlyMode={global.readOnly}
|
|
||||||
locale={global.locale}
|
|
||||||
onAction={(action) => console.log('action called:', action)}
|
|
||||||
initCallback={initialization}
|
|
||||||
/>,
|
|
||||||
document.getElementById('root'),
|
|
||||||
);
|
|
||||||
|
|
54
packages/editor/test/playground/map-render/js/viewmode.tsx
Normal file
54
packages/editor/test/playground/map-render/js/viewmode.tsx
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import '../css/viewmode.css';
|
||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import Editor, { EditorOptions } from '../../../../src/index';
|
||||||
|
import { LocalStorageManager, Designer } from '@wisemapping/mindplot';
|
||||||
|
|
||||||
|
const initialization = (designer: Designer) => {
|
||||||
|
|
||||||
|
designer.addEvent('loadSuccess', () => {
|
||||||
|
const elem = document.getElementById('mindplot');
|
||||||
|
if (elem) {
|
||||||
|
elem.classList.add('ready');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Code for selector of map.
|
||||||
|
const mapSelectElem = document.getElementById('map-select') as HTMLSelectElement;
|
||||||
|
if (mapSelectElem) {
|
||||||
|
mapSelectElem.addEventListener('change', (e) => {
|
||||||
|
// @ts-ignore
|
||||||
|
const selectMap = e.target?.value;
|
||||||
|
window.location.href = `${window.location.pathname}?id=${selectMap}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
Array.from(mapSelectElem.options).forEach((option) => {
|
||||||
|
option.selected = option.value === mapId;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Obtain map id from query param
|
||||||
|
const params = new URLSearchParams(window.location.search.substring(1));
|
||||||
|
const mapId = params.get('id') || 'welcome';
|
||||||
|
const persistence = new LocalStorageManager('samples/{id}.wxml', false);
|
||||||
|
const options: EditorOptions = {
|
||||||
|
zoom: 0.8,
|
||||||
|
locked: false,
|
||||||
|
mapTitle: "Develop Mindnap",
|
||||||
|
mode: 'viewonly',
|
||||||
|
locale: 'en',
|
||||||
|
enableKeyboardEvents: true
|
||||||
|
};
|
||||||
|
|
||||||
|
ReactDOM.render(
|
||||||
|
<Editor
|
||||||
|
mapId={mapId}
|
||||||
|
options={options}
|
||||||
|
persistenceManager={persistence}
|
||||||
|
onAction={(action) => console.log('action called:', action)}
|
||||||
|
onLoad={initialization}
|
||||||
|
/>,
|
||||||
|
document.getElementById('root'),
|
||||||
|
);
|
305
packages/editor/test/playground/map-render/samples/sample8.wxml
Normal file
305
packages/editor/test/playground/map-render/samples/sample8.wxml
Normal file
@ -0,0 +1,305 @@
|
|||||||
|
<map name="844354" version="tango">
|
||||||
|
<topic central="true" text="Школа Интернет Профессий" id="1" fontStyle=";15;;;;">
|
||||||
|
<topic position="281,-3696" order="0" text="Идеология" id="7" fontStyle=";15;;;;">
|
||||||
|
<topic position="441,-3828" order="0" text="Видение" id="14" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="660,-4554" order="0" text="Условия "выживания"" id="172" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="892,-4620" order="0" text="Автономия" id="174" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="915,-4554" order="1" text="Использование" id="176" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="872,-4488" order="2" text="Польза" id="175" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
<topic position="588,-4356" order="1" text="Ресурсы" id="171" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="732,-4422" order="0" text="Личные" id="177" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="758,-4356" order="1" text="Добываемые" id="178" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="761,-4290" order="2" text="Создаваемые" id="179" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
<topic position="579,-4158" order="2" text="Обмен" id="173" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="766,-4224" order="0" text="Война за ресурсы" id="180" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="764,-4158" order="1" text="Обмен ресурсами" id="181" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="794,-4092" order="2" text="Объединение ресурсов" id="182" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
<topic position="594,-3960" order="3" text="Ценность" id="183" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="752,-4026" order="0" text="Создание" id="184" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="773,-3960" order="1" text="Предложение" id="185" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="736,-3894" order="2" text="Обмен" id="186" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
<topic position="569,-3432" order="4" text="Роль" id="187" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="720,-3762" order="0" text="Собственник" id="188" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="942,-3828" order="0" text="Постановка целей" id="211" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="966,-3762" order="1" text="Инвестиции в системы" id="210" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="1030,-3696" order="2" text="Контроль распределения ресурсов" id="217" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
<topic position="747,-3366" order="2" text="Предприниматель" id="189" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="1105,-3432" order="0" text="Создание систем повышения ценности" id="208" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="1060,-3366" order="1" text="Поиск выгодных форм обмена" id="209" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="997,-3300" order="2" text="Масштабирование" id="216" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
<topic position="716,-3234" order="3" text="Управленец" shrink="true" id="190" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="964,-3300" order="0" text="Организация процессов" id="206" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="954,-3234" order="1" text="Контроль результатов" id="207" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="1080,-3168" order="2" text="Карьера от наемного сотрудника до партнера" id="215" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
<topic position="704,-3168" order="4" text="Продавец" shrink="true" id="191" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="891,-3234" order="0" text="Коммуникации" id="204" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="848,-3168" order="1" text="Обмен" id="205" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="1119,-3102" order="2" text="Карьера от ставки с премиями к распределению прибыли" id="214" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
<topic position="715,-3102" order="5" text="Специалист" shrink="true" id="192" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="937,-3168" order="0" text="Создание ценности" id="200" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="954,-3102" order="1" text="Переработка ресурсов" id="203" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="1008,-3036" order="2" text="Карьера от зарплаты к гонорару" id="213" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
<topic position="730,-3036" order="6" text="Обслуживание" shrink="true" id="193" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="982,-3102" order="0" text="Обеспечение условий" id="201" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="1002,-3036" order="1" text="Обслуживание процессов" id="202" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="1089,-2970" order="2" text="Карьера от внутреннего найма к аутсорсу" id="212" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
<topic position="702,-3564" order="1" text="Инвестор" id="257" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="977,-3630" order="1" text="Инвестиции в людей и проекты" id="258" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="1004,-3498" order="2" text="Управление финансами и ресурсами" id="259" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="1173,-3564" order="1" text="Создание множественных источников ресурсов, плодов и ценностей" id="260" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
</topic>
|
||||||
|
</topic>
|
||||||
|
<topic position="446,-2970" order="1" text="Ценности" shrink="true" id="12" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="706,-3267" order="0" text="Социальная ответственность" id="223" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="671,-3201" order="1" text="Предпринимательство" id="226" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="638,-3135" order="2" text="Компетентность" id="227" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="750,-3069" order="3" text="Равные возможности на рынке труда" id="219" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="699,-3003" order="4" text="Финансовая независимость" id="220" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="694,-2937" order="5" text="Право на самореализацию" id="218" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="695,-2871" order="6" text="Свобода самоопределения" id="221" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="674,-2805" order="7" text="Социальное равенство" id="222" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="709,-2739" order="8" text="Уважение к любому возрасту" id="225" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="678,-2673" order="9" text="Обеспеченная старость" id="224" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
<topic position="451,-2904" order="2" text="Принципы" shrink="true" id="13" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="678,-3003" order="0" text="Честность отношений" id="228" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="722,-2937" order="1" text="Ответственность за результат" id="230" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="698,-2871" order="2" text="Эффективность действий" id="231" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="685,-2805" order="3" text="Личная вовлеченность" id="229" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
<topic position="453,-2838" order="3" text="Стандарты" shrink="true" id="15" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="664,-2970" order="0" text="Профессионализм" id="236" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="663,-2904" order="1" text="Высокое качество" id="232" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="639,-2838" order="2" text="Четкие сроки" id="234" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="695,-2772" order="3" text="Стабильные результаты" id="233" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="714,-2706" order="4" text="Достойное вознаграждение" id="235" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
<topic position="436,-2772" order="4" text="Методы" shrink="true" id="16" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="644,-3003" order="0" text="Доступное обучение" id="237" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="788,-2937" order="1" text="Помощь в трудоустройстве/поиске сотрудников" id="238" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="725,-2871" order="2" text="Профессиональная инфраструктура" id="240" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="692,-2805" order="3" text="Консалтинг и Сопровождение" id="241" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="689,-2739" order="4" text="Аутсорсинг задач и проектов" id="242" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="742,-2673" order="5" text="Аутстаффинг сотрудников и процессов" id="239" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="597,-2607" order="6" text="Нетворкинг" id="243" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="627,-2541" order="7" text="Арбитраж споров" id="244" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
</topic>
|
||||||
|
<topic position="264,-2706" order="2" text="Миссия" shrink="true" id="8" fontStyle=";15;;;;">
|
||||||
|
<topic position="500,-2772" order="0" text="На что мы хотим повлиять" shrink="true" id="17" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="939,-2805" order="0" text="Положение людей предпенсионного возраста" id="245" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="981,-2739" order="1" text="Уровень диджитализации в малом и среднем бизнесе" id="246" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
<topic position="484,-2706" order="1" text="Что мы хотим изменить" shrink="true" id="18" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="869,-2772" order="0" text="Подход к поиску и найму сотрудников" id="247" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="897,-2706" order="1" text="Ситуацию на рынке интернет специалистов" id="248" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="909,-2640" order="2" text="Степень доверия и взаимной ответственности" id="249" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
<topic position="476,-2640" order="2" text="Что мы хотим создать" shrink="true" id="19" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="870,-2838" order="0" text="Центр подготовки интернет специалистов" id="250" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="837,-2772" order="1" text="Центр обучения предпринимателей" id="251" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="934,-2706" order="2" text="Систему профессиональных стандартов и требований" id="256" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="993,-2640" order="3" text="Площадку для коммуникации специалистов и предпринимателей" id="252" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="880,-2574" order="4" text="Агентство трудоустройства и аутстаффинга" id="253" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="959,-2508" order="5" text="Диджитал агентство по аутсорсу и управлению проектами" id="254" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="1093,-2442" order="6" text="Профессиональное комьюнити интернет специалистов и онлайн предпринимателей" id="255" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
</topic>
|
||||||
|
<topic position="283,-2640" order="4" text="Концепция" shrink="true" id="6" fontStyle=";15;;;;">
|
||||||
|
<topic position="524,-2871" order="0" text="Формулировка проблем" id="20" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="511,-2805" order="1" text="Определение причин" id="21" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="495,-2739" order="2" text="Постановка целей" id="22" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="491,-2673" order="3" text="План достижения" id="23" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="601,-2607" order="4" text="Список необходимых преобразований" id="24" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="546,-2541" order="5" text="Список необходимых шагов" id="25" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="562,-2475" order="6" text="Список необходимых ресурсов" id="26" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="586,-2409" order="7" text="Список необходимых инструментов" id="27" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
<topic position="354,-1617" order="6" text="Путь Героя (клиента)" id="9" fontStyle=";15;;;;">
|
||||||
|
<note><![CDATA[Описывается для каждой целевой группы (соискатели/работодатели)]]></note>
|
||||||
|
<topic position="584,-2244" order="0" text="Точка А" id="28" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="845,-2541" order="0" text="Главная неудовлетворенность" id="80" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="1200,-2574" order="0" text="Существующие проблемы" id="85" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="1240,-2508" order="1" text="Неудовлетворенные потребности" id="86" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
<topic position="824,-2442" order="1" text="Причины глазами клиента " id="81" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="740,-2343" order="2" text="Состояния" id="82" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="937,-2376" order="0" text="Эмоциональное" id="98" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="916,-2310" order="1" text="Физическое" id="99" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
<topic position="722,-2211" order="3" text="Страхи" id="83" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="1010,-2244" order="0" text="Связанные с неудовлетворенностью" id="100" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="1371,-2244" order="0" text="Как продукт избавит" id="101" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
<topic position="941,-2178" order="1" text="Связанные с решением" id="102" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="1301,-2178" order="0" text="Какие свойства продукта избавят" id="103" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
</topic>
|
||||||
|
<topic position="733,-2013" order="4" text="Желания" id="84" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="981,-2112" order="0" text="Главное внешнее желание" id="107" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="988,-2046" order="1" text="Скрытые мотивы и желания" id="108" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="894,-1947" order="2" text="Ожидания" id="109" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="1074,-1980" order="0" text="Если решить" id="110" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="1089,-1914" order="1" text="Если не решать" id="111" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
</topic>
|
||||||
|
</topic>
|
||||||
|
<topic position="584,-1518" order="1" text="Точка Б" id="29" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="759,-1782" order="0" text="Приобретения" id="90" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="1097,-1782" order="0" text="Чем станет обладать из того что хотел" id="91" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="1418,-1848" order="0" text="Первичные" id="92" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="1417,-1782" order="1" text="Вторичные" id="93" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="1406,-1716" order="2" text="Скрытые" id="94" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
</topic>
|
||||||
|
<topic position="725,-1584" order="1" text="Выгоды" id="87" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="877,-1650" order="0" text="В усилиях" id="95" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="885,-1584" order="1" text="Во времени" id="96" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="876,-1518" order="2" text="В деньгах" id="97" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
<topic position="739,-1419" order="2" text="Состояния" id="88" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="936,-1452" order="0" text="Эмоциональное" id="104" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="915,-1386" order="1" text="Физическое" id="105" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
<topic position="724,-1254" order="3" text="Оценки" id="89" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="886,-1320" order="0" text="Самооценка" id="106" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="879,-1254" order="1" text="Признание" id="112" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="922,-1188" order="2" text="Изменение статуса" id="113" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
</topic>
|
||||||
|
<topic position="575,-891" order="2" text="Этапы" id="30" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="785,-1122" order="0" text="Необходимые условия" id="31" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="786,-1056" order="1" text="Необходимые ресурсы" id="32" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="779,-990" order="2" text="Необходимые знания" id="34" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="782,-924" order="3" text="Необходимые навыки" id="35" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="705,-858" order="4" text="Задачи" id="36" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="790,-792" order="5" text="Необходимые действия" id="33" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="815,-726" order="6" text="Промежуточные результаты" id="37" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="875,-660" order="7" text="Условия перехода к следующему этапу" id="38" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
</topic>
|
||||||
|
<topic position="328,1056" order="8" text="Матрица продуктов" id="10" fontStyle=";15;;;;">
|
||||||
|
<topic position="660,-561" order="0" text="Комплексное решение под ключ" id="39" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="963,-594" order="0" text="Для учеников" id="114" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="1299,-594" order="0" text="Обучение+карьера в нашей структуре" id="116" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
<topic position="1014,-528" order="1" text="Для предпринимателей" id="115" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="1338,-528" order="0" text="Интернет проект под ключ" id="117" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
</topic>
|
||||||
|
<topic position="695,-429" order="1" text="Комплексное решение руками клиента" id="40" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="1275,-462" order="0" text="Обучение+инструменты трудоустройства и продуктивности" shrink="true" id="118" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="1375,-396" order="1" text="Обучение управлению интернет проектами+инструменты найма и управления" id="119" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
<topic position="689,231" order="2" text="Решение конкретного этапа под ключ" id="41" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="1044,-330" order="0" text="Постановка целей" id="141" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="1023,-231" order="1" text="Анализ рынка" id="120" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="1265,-264" order="0" text="На уровне компании" id="133" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="1280,-198" order="1" text="На уровне специалиста" id="134" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
<topic position="1019,-132" order="2" text="SWOT анализ" id="132" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="1072,0" order="3" text="Упаковка предложения" id="121" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="1376,-66" order="0" text="Ценностная концепция" id="137" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="1390,0" order="1" text="Ценностное предложение" id="138" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="1376,66" order="2" text="Торговое предложение" id="139" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
<topic position="1110,132" order="4" text="Разработка структуры проекта" id="122" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="1163,198" order="5" text="Разработка стратегии достижения целей" id="123" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="1115,264" order="6" text="Разработка тактического плана" id="140" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="1081,330" order="7" text="Техническая реализация" id="126" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="1072,396" order="8" text="Производство контента" id="124" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="1067,462" order="9" text="Настройка интеграций " id="125" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="1051,528" order="10" text="Настройка трафика" id="127" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="1067,594" order="11" text="Обработка обращений" id="130" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="1101,660" order="12" text="Техническое сопровождение" id="128" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="1089,726" order="13" text="Сопровождение процессов" id="131" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="1070,792" order="14" text="Клиентская поддержка" id="129" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
<topic position="723,858" order="3" text="Решение конкретного этапа руками клиента" id="42" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="692,924" order="4" text="Решение конкретной задачи под ключ" id="43" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="726,990" order="5" text="Решение конкретной задачи руками клиента" id="44" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="663,1155" order="6" text="Источник необходимых ресурсов" id="45" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="996,1056" order="0" text="База специалистов" id="142" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="998,1122" order="1" text="Штат специалистов" id="143" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="1002,1188" order="2" text="База работодателей" id="144" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="1042,1254" order="3" text="База партнерских проектов" id="167" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
<topic position="671,1452" order="7" text="Набор необходимых инструментов" id="46" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="954,1320" order="0" text="Хостинг" id="145" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="958,1386" order="1" text="Сервисы" id="146" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="962,1452" order="2" text="Шаблоны" id="147" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="982,1518" order="3" text="Инструменты" id="148" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="939,1584" order="4" text="Софт" id="149" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
<topic position="634,2079" order="9" text="Способ получения навыков" id="48" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="917,1914" order="0" text="Мастер-классы" id="150" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="893,1980" order="1" text="Воркшопы" id="151" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="896,2046" order="2" text="Интенсивы" id="158" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="888,2112" order="3" text="Тренинги" id="152" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="937,2178" order="4" text="Программы курсов" id="159" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="902,2244" order="5" text="Стажировки" id="153" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
<topic position="724,2508" order="10" text="Создание среды с необходимыми условиями" id="49" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="1081,2310" order="0" text="Сообщество" id="160" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="1149,2376" order="1" text="Платформа для общения" id="161" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="1074,2442" order="2" text="Коворкинг" id="162" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="1080,2508" order="3" text="Нетворкинг" id="163" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="1267,2574" order="4" text="Платформа для поиска партнеров/соискателей" id="164" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="1152,2640" order="5" text="Платформа для обучения" id="165" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="1141,2706" order="6" text="Платформа для работы" id="166" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
<topic position="628,1749" order="8" text="База необходимых знаний" id="47" fontStyle=";15;;;;" bgColor="rgb(224,229,239)">
|
||||||
|
<topic position="890,1650" order="0" text="Публикации" id="154" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="920,1716" order="1" text="Открытые лекции" id="155" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="962,1782" order="2" text="Образовательные каналы" id="156" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="923,1848" order="3" text="Платное обучение" id="157" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
</topic>
|
||||||
|
<topic position="303,3036" order="10" text="Бизнес модель" id="2" fontStyle=";15;;;;">
|
||||||
|
<topic position="553,2772" order="0" text="Клиентские сегменты" id="71" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="575,2838" order="1" text="Ценностное предложение" id="72" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="565,2904" order="2" text="Способы коммуникации" id="73" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="573,2970" order="3" text="Способы предоставления" id="74" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="598,3036" order="4" text="Ключевые виды деятельности" id="75" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="540,3102" order="5" text="Ключевые ресурсы" id="76" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="547,3168" order="6" text="Ключевые партнеры" id="77" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="545,3234" order="7" text="Структура расходов" id="78" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="541,3300" order="8" text="Источники доходов" id="79" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
<topic position="333,3630" order="12" text="Ключевые процессы" id="5" fontStyle=";15;;;;">
|
||||||
|
<topic position="560,3366" order="0" text="Консалтинг" id="57" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="676,3432" order="1" text="Разработка обучающих программ" id="50" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="551,3498" order="2" text="Обучение" id="53" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="625,3564" order="3" text="Создание инструментов" id="51" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="654,3630" order="4" text="Систематизация информации" id="52" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="595,3696" order="5" text="Создание условий" id="54" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="621,3762" order="6" text="Управление проектами" id="55" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="582,3828" order="7" text="Оказание услуг" id="170" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="616,3894" order="8" text="Поддержка процессов" id="56" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
<topic position="374,3960" order="14" text="Организационная структура" id="3" fontStyle=";15;;;;"/>
|
||||||
|
<topic position="258,4323" order="16" text="Этапы" id="60" fontStyle=";15;;;;">
|
||||||
|
<topic position="495,4026" order="0" text="Определение потребностей" id="61" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="425,4092" order="1" text="Анализ рынка" id="62" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="552,4158" order="2" text="Разработка ценностного предложения" id="63" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="425,4224" order="3" text="Создание MVP" id="64" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="439,4290" order="4" text="Тестовый запуск" id="68" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="388,4356" order="5" text="Анализ" id="65" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="518,4422" order="6" text="Разработка основного продукта" id="66" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="381,4488" order="7" text="Релиз" id="69" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="544,4554" order="8" text="Систематизация ключевых процесов" id="70" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
<topic position="446,4620" order="9" text="Масштабирование" id="67" fontStyle=";15;;;;" bgColor="rgb(224,229,239)"/>
|
||||||
|
</topic>
|
||||||
|
</topic>
|
||||||
|
</map>
|
@ -7,7 +7,8 @@ module.exports = {
|
|||||||
publicPath: '',
|
publicPath: '',
|
||||||
library: {
|
library: {
|
||||||
type: 'umd',
|
type: 'umd',
|
||||||
}, },
|
},
|
||||||
|
},
|
||||||
stats: {
|
stats: {
|
||||||
errorDetails: true
|
errorDetails: true
|
||||||
},
|
},
|
||||||
@ -35,7 +36,14 @@ module.exports = {
|
|||||||
test: /\.(js|jsx)$/,
|
test: /\.(js|jsx)$/,
|
||||||
exclude: /node_modules/,
|
exclude: /node_modules/,
|
||||||
use: ['babel-loader'],
|
use: ['babel-loader'],
|
||||||
|
}, {
|
||||||
|
test: /\.css$/i,
|
||||||
|
loader: 'style-loader'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
test: /\.css$/,
|
||||||
|
loader: 'css-loader',
|
||||||
|
}
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
|
@ -9,7 +9,6 @@ const playgroundConfig = {
|
|||||||
mode: 'development',
|
mode: 'development',
|
||||||
entry: {
|
entry: {
|
||||||
viewmode: path.resolve(__dirname, './test/playground/map-render/js/viewmode'),
|
viewmode: path.resolve(__dirname, './test/playground/map-render/js/viewmode'),
|
||||||
embedded: path.resolve(__dirname, './test/playground/map-render/js/embedded'),
|
|
||||||
editor: path.resolve(__dirname, './test/playground/map-render/js/editor'),
|
editor: path.resolve(__dirname, './test/playground/map-render/js/editor'),
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
@ -24,17 +23,6 @@ const playgroundConfig = {
|
|||||||
port: 8081,
|
port: 8081,
|
||||||
open: false,
|
open: false,
|
||||||
},
|
},
|
||||||
module: {
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
test: /\.css$/i,
|
|
||||||
use: [
|
|
||||||
'style-loader',
|
|
||||||
'css-loader?url=false',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
plugins: [
|
plugins: [
|
||||||
new CleanWebpackPlugin({
|
new CleanWebpackPlugin({
|
||||||
dangerouslyAllowCleanPatternsOutsideProject: true,
|
dangerouslyAllowCleanPatternsOutsideProject: true,
|
||||||
@ -46,10 +34,7 @@ const playgroundConfig = {
|
|||||||
{ from: 'test/playground/map-render/images', to: 'images' },
|
{ from: 'test/playground/map-render/images', to: 'images' },
|
||||||
{ from: 'test/playground/map-render/js', to: 'js' },
|
{ from: 'test/playground/map-render/js', to: 'js' },
|
||||||
{ from: 'test/playground/map-render/samples', to: 'samples' },
|
{ from: 'test/playground/map-render/samples', to: 'samples' },
|
||||||
{ from: '../../libraries/bootstrap', to: 'bootstrap' },
|
|
||||||
{ from: 'test/playground/index.html', to: 'index.html' },
|
{ from: 'test/playground/index.html', to: 'index.html' },
|
||||||
{ from: 'test/playground/map-render/html/container.json', to: 'html/container.json' },
|
|
||||||
{ from: 'test/playground/map-render/html/container.html', to: 'container.html' },
|
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
new HtmlWebpackPlugin({
|
new HtmlWebpackPlugin({
|
||||||
@ -57,11 +42,6 @@ const playgroundConfig = {
|
|||||||
filename: 'viewmode.html',
|
filename: 'viewmode.html',
|
||||||
template: 'test/playground/map-render/html/viewmode.html',
|
template: 'test/playground/map-render/html/viewmode.html',
|
||||||
}),
|
}),
|
||||||
new HtmlWebpackPlugin({
|
|
||||||
chunks: ['embedded'],
|
|
||||||
filename: 'embedded.html',
|
|
||||||
template: 'test/playground/map-render/html/embedded.html',
|
|
||||||
}),
|
|
||||||
new HtmlWebpackPlugin({
|
new HtmlWebpackPlugin({
|
||||||
chunks: ['editor'],
|
chunks: ['editor'],
|
||||||
filename: 'editor.html',
|
filename: 'editor.html',
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wisemapping/mindplot",
|
"name": "@wisemapping/mindplot",
|
||||||
"version": "5.0.3",
|
"version": "5.0.8",
|
||||||
"description": "WiseMapping - Mindplot Canvas Library",
|
"description": "WiseMapping - Mindplot Canvas Library",
|
||||||
"homepage": "http://www.wisemapping.org/",
|
"homepage": "http://www.wisemapping.org/",
|
||||||
"directories": {
|
"directories": {
|
||||||
@ -37,7 +37,8 @@
|
|||||||
"@wisemapping/web2d": "^0.4.0",
|
"@wisemapping/web2d": "^0.4.0",
|
||||||
"jest": "^27.4.5",
|
"jest": "^27.4.5",
|
||||||
"jquery": "3.6.0",
|
"jquery": "3.6.0",
|
||||||
"lodash": "^4.17.21"
|
"lodash": "^4.17.21",
|
||||||
|
"xml-formatter": "^2.6.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.14.6",
|
"@babel/core": "^7.14.6",
|
||||||
@ -69,6 +70,7 @@
|
|||||||
"start-server-and-test": "^1.14.0",
|
"start-server-and-test": "^1.14.0",
|
||||||
"ts-jest": "^27.1.2",
|
"ts-jest": "^27.1.2",
|
||||||
"ts-loader": "^9.2.6",
|
"ts-loader": "^9.2.6",
|
||||||
|
"ts-node": "^10.4.0",
|
||||||
"webpack": "^5.44.0",
|
"webpack": "^5.44.0",
|
||||||
"webpack-bundle-analyzer": "^4.5.0",
|
"webpack-bundle-analyzer": "^4.5.0",
|
||||||
"webpack-cli": "^4.7.2",
|
"webpack-cli": "^4.7.2",
|
||||||
|
4
packages/mindplot/src/@types/custom.d.ts
vendored
Normal file
4
packages/mindplot/src/@types/custom.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
declare module "*.svg" {
|
||||||
|
const content: any;
|
||||||
|
export default content;
|
||||||
|
}
|
@ -30,9 +30,16 @@ import Topic from './Topic';
|
|||||||
abstract class ActionDispatcher extends Events {
|
abstract class ActionDispatcher extends Events {
|
||||||
private static _instance: ActionDispatcher;
|
private static _instance: ActionDispatcher;
|
||||||
|
|
||||||
|
private _commandContext: CommandContext;
|
||||||
|
|
||||||
constructor(commandContext: CommandContext) {
|
constructor(commandContext: CommandContext) {
|
||||||
$assert(commandContext, 'commandContext can not be null');
|
$assert(commandContext, 'commandContext can not be null');
|
||||||
super();
|
super();
|
||||||
|
this._commandContext = commandContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCommandContext(): CommandContext {
|
||||||
|
return this._commandContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract addRelationship(model: RelationshipModel, mindmap: Mindmap): void;
|
abstract addRelationship(model: RelationshipModel, mindmap: Mindmap): void;
|
||||||
|
@ -40,7 +40,6 @@ class CommandContext {
|
|||||||
this._designer = value;
|
this._designer = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** */
|
|
||||||
findTopics(topicIds: number[]): Topic[] {
|
findTopics(topicIds: number[]): Topic[] {
|
||||||
$assert($defined(topicIds), 'topicsIds can not be null');
|
$assert($defined(topicIds), 'topicsIds can not be null');
|
||||||
const topicsIds = Array.isArray(topicIds) ? topicIds : [topicIds];
|
const topicsIds = Array.isArray(topicIds) ? topicIds : [topicIds];
|
||||||
|
@ -21,10 +21,23 @@ import {
|
|||||||
Point, CurvedLine, PolyLine, Line,
|
Point, CurvedLine, PolyLine, Line,
|
||||||
} from '@wisemapping/web2d';
|
} from '@wisemapping/web2d';
|
||||||
import { TopicShape } from './model/INodeModel';
|
import { TopicShape } from './model/INodeModel';
|
||||||
|
import RelationshipModel from './model/RelationshipModel';
|
||||||
|
import Topic from './Topic';
|
||||||
import TopicConfig from './TopicConfig';
|
import TopicConfig from './TopicConfig';
|
||||||
|
import Workspace from './Workspace';
|
||||||
|
|
||||||
class ConnectionLine {
|
class ConnectionLine {
|
||||||
constructor(sourceNode, targetNode, lineType) {
|
protected _targetTopic: Topic;
|
||||||
|
|
||||||
|
protected _sourceTopic: Topic;
|
||||||
|
|
||||||
|
protected _lineType: number;
|
||||||
|
|
||||||
|
protected _line2d: Line;
|
||||||
|
|
||||||
|
protected _model: RelationshipModel;
|
||||||
|
|
||||||
|
constructor(sourceNode: Topic, targetNode: Topic, lineType?: number) {
|
||||||
$assert(targetNode, 'parentNode node can not be null');
|
$assert(targetNode, 'parentNode node can not be null');
|
||||||
$assert(sourceNode, 'childNode node can not be null');
|
$assert(sourceNode, 'childNode node can not be null');
|
||||||
$assert(sourceNode !== targetNode, 'Circular connection');
|
$assert(sourceNode !== targetNode, 'Circular connection');
|
||||||
@ -32,7 +45,7 @@ class ConnectionLine {
|
|||||||
this._targetTopic = targetNode;
|
this._targetTopic = targetNode;
|
||||||
this._sourceTopic = sourceNode;
|
this._sourceTopic = sourceNode;
|
||||||
|
|
||||||
let line;
|
let line: Line;
|
||||||
const ctrlPoints = this._getCtrlPoints(sourceNode, targetNode);
|
const ctrlPoints = this._getCtrlPoints(sourceNode, targetNode);
|
||||||
if (targetNode.getType() === 'CentralTopic') {
|
if (targetNode.getType() === 'CentralTopic') {
|
||||||
line = this._createLine(lineType, ConnectionLine.CURVED);
|
line = this._createLine(lineType, ConnectionLine.CURVED);
|
||||||
@ -51,15 +64,15 @@ class ConnectionLine {
|
|||||||
this._line2d = line;
|
this._line2d = line;
|
||||||
}
|
}
|
||||||
|
|
||||||
_getCtrlPoints(sourceNode, targetNode) {
|
private _getCtrlPoints(sourceNode: Topic, targetNode: Topic) {
|
||||||
const srcPos = sourceNode.workoutOutgoingConnectionPoint(targetNode.getPosition());
|
const srcPos = sourceNode.workoutOutgoingConnectionPoint(targetNode.getPosition());
|
||||||
const destPos = targetNode.workoutIncomingConnectionPoint(sourceNode.getPosition());
|
const destPos = targetNode.workoutIncomingConnectionPoint(sourceNode.getPosition());
|
||||||
const deltaX = (srcPos.x - destPos.x) / 3;
|
const deltaX = (srcPos.x - destPos.x) / 3;
|
||||||
return [new Point(deltaX, 0), new Point(-deltaX, 0)];
|
return [new Point(deltaX, 0), new Point(-deltaX, 0)];
|
||||||
}
|
}
|
||||||
|
|
||||||
_createLine(lineTypeParam, defaultStyle) {
|
protected _createLine(lineTypeParam: number, defaultStyle: number): Line {
|
||||||
const lineType = $defined(lineTypeParam) ? parseInt(lineTypeParam, 10) : defaultStyle;
|
const lineType = $defined(lineTypeParam) ? lineTypeParam : defaultStyle;
|
||||||
this._lineType = lineType;
|
this._lineType = lineType;
|
||||||
let line = null;
|
let line = null;
|
||||||
switch (lineType) {
|
switch (lineType) {
|
||||||
@ -80,7 +93,7 @@ class ConnectionLine {
|
|||||||
return line;
|
return line;
|
||||||
}
|
}
|
||||||
|
|
||||||
setVisibility(value) {
|
setVisibility(value: boolean): void {
|
||||||
this._line2d.setVisibility(value);
|
this._line2d.setVisibility(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,11 +101,11 @@ class ConnectionLine {
|
|||||||
return this._line2d.isVisible();
|
return this._line2d.isVisible();
|
||||||
}
|
}
|
||||||
|
|
||||||
setOpacity(opacity) {
|
setOpacity(opacity: number): void {
|
||||||
this._line2d.setOpacity(opacity);
|
this._line2d.setOpacity(opacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
redraw() {
|
redraw(): void {
|
||||||
const line2d = this._line2d;
|
const line2d = this._line2d;
|
||||||
const sourceTopic = this._sourceTopic;
|
const sourceTopic = this._sourceTopic;
|
||||||
const sourcePosition = sourceTopic.getPosition();
|
const sourcePosition = sourceTopic.getPosition();
|
||||||
@ -116,7 +129,7 @@ class ConnectionLine {
|
|||||||
this._positionateConnector(targetTopic);
|
this._positionateConnector(targetTopic);
|
||||||
}
|
}
|
||||||
|
|
||||||
_positionateConnector(targetTopic) {
|
protected _positionateConnector(targetTopic: Topic): void {
|
||||||
const targetPosition = targetTopic.getPosition();
|
const targetPosition = targetTopic.getPosition();
|
||||||
const offset = TopicConfig.CONNECTOR_WIDTH / 2;
|
const offset = TopicConfig.CONNECTOR_WIDTH / 2;
|
||||||
const targetTopicSize = targetTopic.getSize();
|
const targetTopicSize = targetTopic.getSize();
|
||||||
@ -141,65 +154,68 @@ class ConnectionLine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setStroke(color, style, opacity) {
|
setStroke(color: string, style: string, opacity: number) {
|
||||||
this._line2d.setStroke(null, null, color, opacity);
|
this._line2d.setStroke(null, null, color, opacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
addToWorkspace(workspace) {
|
addToWorkspace(workspace: Workspace) {
|
||||||
workspace.append(this._line2d);
|
workspace.append(this._line2d);
|
||||||
this._line2d.moveToBack();
|
this._line2d.moveToBack();
|
||||||
}
|
}
|
||||||
|
|
||||||
removeFromWorkspace(workspace) {
|
removeFromWorkspace(workspace: Workspace) {
|
||||||
workspace.removeChild(this._line2d);
|
workspace.removeChild(this._line2d);
|
||||||
}
|
}
|
||||||
|
|
||||||
getTargetTopic() {
|
getTargetTopic(): Topic {
|
||||||
return this._targetTopic;
|
return this._targetTopic;
|
||||||
}
|
}
|
||||||
|
|
||||||
getSourceTopic() {
|
getSourceTopic(): Topic {
|
||||||
return this._sourceTopic;
|
return this._sourceTopic;
|
||||||
}
|
}
|
||||||
|
|
||||||
getLineType() {
|
getLineType(): number {
|
||||||
return this._lineType;
|
return this._lineType;
|
||||||
}
|
}
|
||||||
|
|
||||||
getLine() {
|
getLine(): Line {
|
||||||
return this._line2d;
|
return this._line2d;
|
||||||
}
|
}
|
||||||
|
|
||||||
getModel() {
|
getModel(): RelationshipModel {
|
||||||
return this._model;
|
return this._model;
|
||||||
}
|
}
|
||||||
|
|
||||||
setModel(model) {
|
setModel(model: RelationshipModel): void {
|
||||||
this._model = model;
|
this._model = model;
|
||||||
}
|
}
|
||||||
|
|
||||||
getType() {
|
getType(): string {
|
||||||
return 'ConnectionLine';
|
return 'ConnectionLine';
|
||||||
}
|
}
|
||||||
|
|
||||||
getId() {
|
getId(): number {
|
||||||
return this._model.getId();
|
return this._model.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
moveToBack() {
|
moveToBack(): void {
|
||||||
this._line2d.moveToBack();
|
this._line2d.moveToBack();
|
||||||
}
|
}
|
||||||
|
|
||||||
moveToFront() {
|
moveToFront() {
|
||||||
this._line2d.moveToFront();
|
this._line2d.moveToFront();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static SIMPLE = 0;
|
||||||
|
|
||||||
|
static POLYLINE = 1;
|
||||||
|
|
||||||
|
static CURVED = 2;
|
||||||
|
|
||||||
|
static SIMPLE_CURVED = 3;
|
||||||
|
|
||||||
|
static getStrokeColor = () => '#495879';
|
||||||
}
|
}
|
||||||
|
|
||||||
ConnectionLine.getStrokeColor = () => '#495879';
|
|
||||||
|
|
||||||
ConnectionLine.SIMPLE = 0;
|
|
||||||
ConnectionLine.POLYLINE = 1;
|
|
||||||
ConnectionLine.CURVED = 2;
|
|
||||||
ConnectionLine.SIMPLE_CURVED = 3;
|
|
||||||
|
|
||||||
export default ConnectionLine;
|
export default ConnectionLine;
|
@ -69,7 +69,7 @@ class Designer extends Events {
|
|||||||
|
|
||||||
private _workspace: Workspace;
|
private _workspace: Workspace;
|
||||||
|
|
||||||
private _eventBussDispatcher: EventBusDispatcher;
|
_eventBussDispatcher: EventBusDispatcher;
|
||||||
|
|
||||||
private _dragManager: DragManager;
|
private _dragManager: DragManager;
|
||||||
|
|
||||||
@ -87,6 +87,7 @@ class Designer extends Events {
|
|||||||
$assert(divElement, 'divElement must be defined');
|
$assert(divElement, 'divElement must be defined');
|
||||||
|
|
||||||
// Set up i18n location ...
|
// Set up i18n location ...
|
||||||
|
console.log(`Editor location: ${options.locale}`);
|
||||||
Messages.init(options.locale);
|
Messages.init(options.locale);
|
||||||
|
|
||||||
this._options = options;
|
this._options = options;
|
||||||
@ -110,7 +111,7 @@ class Designer extends Events {
|
|||||||
|
|
||||||
// Init Screen manager..
|
// Init Screen manager..
|
||||||
const screenManager = new ScreenManager(divElement);
|
const screenManager = new ScreenManager(divElement);
|
||||||
this._workspace = new Workspace(screenManager, this._model.getZoom(), !!options.readOnly);
|
this._workspace = new Workspace(screenManager, this._model.getZoom(), options.mode === 'viewonly');
|
||||||
|
|
||||||
// Init layout manager ...
|
// Init layout manager ...
|
||||||
this._eventBussDispatcher = new EventBusDispatcher();
|
this._eventBussDispatcher = new EventBusDispatcher();
|
||||||
@ -208,15 +209,12 @@ class Designer extends Events {
|
|||||||
});
|
});
|
||||||
|
|
||||||
dragManager.addEvent('dragging', (event: MouseEvent, dragTopic: DragTopic) => {
|
dragManager.addEvent('dragging', (event: MouseEvent, dragTopic: DragTopic) => {
|
||||||
dragTopic.updateFreeLayout(event);
|
|
||||||
if (!dragTopic.isFreeLayoutOn()) {
|
|
||||||
// The node is being drag. Is the connection still valid ?
|
// The node is being drag. Is the connection still valid ?
|
||||||
dragConnector.checkConnection(dragTopic);
|
dragConnector.checkConnection(dragTopic);
|
||||||
|
|
||||||
if (!dragTopic.isVisible() && dragTopic.isConnected()) {
|
if (!dragTopic.isVisible() && dragTopic.isConnected()) {
|
||||||
dragTopic.setVisibility(true);
|
dragTopic.setVisibility(true);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
dragManager.addEvent('enddragging', (event: MouseEvent, dragTopic: DragTopic) => {
|
dragManager.addEvent('enddragging', (event: MouseEvent, dragTopic: DragTopic) => {
|
||||||
@ -335,7 +333,7 @@ class Designer extends Events {
|
|||||||
zoomOut(factor = 1.2) {
|
zoomOut(factor = 1.2) {
|
||||||
const model = this.getModel();
|
const model = this.getModel();
|
||||||
const scale = model.getZoom() * factor;
|
const scale = model.getZoom() * factor;
|
||||||
if (scale <= 1.9) {
|
if (scale <= 7.0) {
|
||||||
model.setZoom(scale);
|
model.setZoom(scale);
|
||||||
this._workspace.setZoom(scale);
|
this._workspace.setZoom(scale);
|
||||||
} else {
|
} else {
|
||||||
@ -626,7 +624,7 @@ class Designer extends Events {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isReadOnly(): boolean {
|
isReadOnly(): boolean {
|
||||||
return Boolean(this._options?.readOnly);
|
return Boolean(this._options?.mode === 'viewonly');
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeModelToTopic(nodeModel: NodeModel): Topic {
|
nodeModelToTopic(nodeModel: NodeModel): Topic {
|
||||||
|
@ -20,8 +20,6 @@ import $ from 'jquery';
|
|||||||
import PersistenceManager from './PersistenceManager';
|
import PersistenceManager from './PersistenceManager';
|
||||||
import Designer from './Designer';
|
import Designer from './Designer';
|
||||||
import Menu from './widget/Menu';
|
import Menu from './widget/Menu';
|
||||||
import { $notifyModal } from './widget/ModalDialogNotifier';
|
|
||||||
import { $msg } from './Messages';
|
|
||||||
import { DesignerOptions } from './DesignerOptionsBuilder';
|
import { DesignerOptions } from './DesignerOptionsBuilder';
|
||||||
|
|
||||||
let designer: Designer;
|
let designer: Designer;
|
||||||
@ -32,40 +30,6 @@ export function buildDesigner(options: DesignerOptions): Designer {
|
|||||||
|
|
||||||
// Register load events ...
|
// Register load events ...
|
||||||
designer = new Designer(options, divContainer);
|
designer = new Designer(options, divContainer);
|
||||||
designer.addEvent('loadSuccess', () => {
|
|
||||||
globalThis.mindmapLoadReady = true;
|
|
||||||
console.log('Map loadded successfully');
|
|
||||||
});
|
|
||||||
|
|
||||||
const onerrorFn = (msg: string, url: string, lineNo: number, columnNo: number, error: Error) => {
|
|
||||||
const message = [
|
|
||||||
`Message: ${msg}`,
|
|
||||||
`URL: ${url}`,
|
|
||||||
`Line: ${lineNo}`,
|
|
||||||
`Column: ${columnNo}`,
|
|
||||||
].join(' - ');
|
|
||||||
console.log(message);
|
|
||||||
|
|
||||||
// Send error to server ...
|
|
||||||
$.ajax({
|
|
||||||
method: 'post',
|
|
||||||
url: '/c/restful/logger/editor',
|
|
||||||
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
|
||||||
data: JSON.stringify({
|
|
||||||
jsErrorMsg: message,
|
|
||||||
jsStack: JSON.stringify(error),
|
|
||||||
userAgent: navigator.userAgent,
|
|
||||||
mapId: options.mapId,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Open error dialog only in case of mindmap loading errors. The rest of the error are reported but not display the dialog.
|
|
||||||
// Remove this in the near future.
|
|
||||||
if (!globalThis.mindmapLoadReady) {
|
|
||||||
$notifyModal($msg('UNEXPECTED_ERROR_LOADING'));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
window.onerror = onerrorFn;
|
|
||||||
|
|
||||||
// Configure default persistence manager ...
|
// Configure default persistence manager ...
|
||||||
const persistence = options.persistenceManager;
|
const persistence = options.persistenceManager;
|
||||||
@ -73,7 +37,7 @@ export function buildDesigner(options: DesignerOptions): Designer {
|
|||||||
PersistenceManager.init(persistence);
|
PersistenceManager.init(persistence);
|
||||||
|
|
||||||
// Register toolbar event ...
|
// Register toolbar event ...
|
||||||
if ($('#toolbar').length) {
|
if (options.mode === 'edition' || options.mode === 'showcase') {
|
||||||
const menu = new Menu(designer, 'toolbar');
|
const menu = new Menu(designer, 'toolbar');
|
||||||
|
|
||||||
// If a node has focus, focus can be move to another node using the keys.
|
// If a node has focus, focus can be move to another node using the keys.
|
||||||
|
@ -21,16 +21,28 @@ import Keyboard from './Keyboard';
|
|||||||
import { Designer } from '..';
|
import { Designer } from '..';
|
||||||
import Topic from './Topic';
|
import Topic from './Topic';
|
||||||
|
|
||||||
|
export type EventCallback = (event?: Event) => void;
|
||||||
class DesignerKeyboard extends Keyboard {
|
class DesignerKeyboard extends Keyboard {
|
||||||
// eslint-disable-next-line no-use-before-define
|
// eslint-disable-next-line no-use-before-define
|
||||||
static _instance: DesignerKeyboard;
|
static _instance: DesignerKeyboard;
|
||||||
|
|
||||||
|
static _disabled: boolean;
|
||||||
|
|
||||||
constructor(designer: Designer) {
|
constructor(designer: Designer) {
|
||||||
super();
|
super();
|
||||||
$assert(designer, 'designer can not be null');
|
$assert(designer, 'designer can not be null');
|
||||||
this._registerEvents(designer);
|
this._registerEvents(designer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addShortcut(shortcuts: string[] | string, callback: EventCallback): void {
|
||||||
|
super.addShortcut(shortcuts, (e: Event) => {
|
||||||
|
if (DesignerKeyboard.isDisabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
callback(e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private _registerEvents(designer: Designer) {
|
private _registerEvents(designer: Designer) {
|
||||||
// Try with the keyboard ..
|
// Try with the keyboard ..
|
||||||
const model = designer.getModel();
|
const model = designer.getModel();
|
||||||
@ -246,6 +258,10 @@ class DesignerKeyboard extends Keyboard {
|
|||||||
|
|
||||||
$(document).on('keypress', (event) => {
|
$(document).on('keypress', (event) => {
|
||||||
let keyCode: number;
|
let keyCode: number;
|
||||||
|
|
||||||
|
if (DesignerKeyboard.isDisabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Firefox doesn't skip special keys for keypress event...
|
// Firefox doesn't skip special keys for keypress event...
|
||||||
if (event.key && excludes.includes(event.key.toLowerCase())) {
|
if (event.key && excludes.includes(event.key.toLowerCase())) {
|
||||||
return;
|
return;
|
||||||
@ -264,11 +280,10 @@ class DesignerKeyboard extends Keyboard {
|
|||||||
const nodes = designer.getModel().filterSelectedTopics();
|
const nodes = designer.getModel().filterSelectedTopics();
|
||||||
if (nodes.length > 0) {
|
if (nodes.length > 0) {
|
||||||
// If a modifier is press, the key selected must be ignored.
|
// If a modifier is press, the key selected must be ignored.
|
||||||
const pressKey = String.fromCharCode(keyCode);
|
|
||||||
if (event.ctrlKey || event.altKey || event.metaKey) {
|
if (event.ctrlKey || event.altKey || event.metaKey) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
nodes[0].showTextEditor(pressKey);
|
nodes[0].showTextEditor('');
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -347,7 +362,7 @@ class DesignerKeyboard extends Keyboard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _goToChild(designer, node) {
|
private _goToChild(designer: Designer, node: Topic) {
|
||||||
const children = node.getChildren();
|
const children = node.getChildren();
|
||||||
if (children.length > 0) {
|
if (children.length > 0) {
|
||||||
let target = children[0];
|
let target = children[0];
|
||||||
@ -373,6 +388,19 @@ class DesignerKeyboard extends Keyboard {
|
|||||||
|
|
||||||
static register = function register(designer: Designer) {
|
static register = function register(designer: Designer) {
|
||||||
this._instance = new DesignerKeyboard(designer);
|
this._instance = new DesignerKeyboard(designer);
|
||||||
|
this._disabled = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
static pause = function pause() {
|
||||||
|
this._disabled = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
static resume = function resume() {
|
||||||
|
this._disabled = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
static isDisabled = function isDisabled() {
|
||||||
|
return this._disabled;
|
||||||
};
|
};
|
||||||
|
|
||||||
static specialKeys = {
|
static specialKeys = {
|
||||||
|
@ -16,13 +16,14 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { $assert } from '@wisemapping/core-js';
|
import { $assert } from '@wisemapping/core-js';
|
||||||
|
import EditorRenderMode from './EditorRenderMode';
|
||||||
import PersistenceManager from './PersistenceManager';
|
import PersistenceManager from './PersistenceManager';
|
||||||
import SizeType from './SizeType';
|
import SizeType from './SizeType';
|
||||||
|
|
||||||
export type DesignerOptions = {
|
export type DesignerOptions = {
|
||||||
zoom: number,
|
zoom: number,
|
||||||
containerSize?: SizeType,
|
containerSize?: SizeType,
|
||||||
readOnly?: boolean,
|
mode: EditorRenderMode,
|
||||||
mapId?: string,
|
mapId?: string,
|
||||||
container: string,
|
container: string,
|
||||||
persistenceManager?: PersistenceManager,
|
persistenceManager?: PersistenceManager,
|
||||||
@ -45,7 +46,7 @@ class OptionsBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const defaultOptions: DesignerOptions = {
|
const defaultOptions: DesignerOptions = {
|
||||||
readOnly: false,
|
mode: 'edition',
|
||||||
zoom: 0.85,
|
zoom: 0.85,
|
||||||
saveOnLoad: true,
|
saveOnLoad: true,
|
||||||
containerSize,
|
containerSize,
|
||||||
|
@ -16,18 +16,27 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { $assert } from '@wisemapping/core-js';
|
import { $assert } from '@wisemapping/core-js';
|
||||||
|
import { Point } from '@wisemapping/web2d';
|
||||||
|
import DesignerModel from './DesignerModel';
|
||||||
|
import DragTopic from './DragTopic';
|
||||||
|
import SizeType from './SizeType';
|
||||||
|
import Topic from './Topic';
|
||||||
|
import Workspace from './Workspace';
|
||||||
|
|
||||||
class DragConnector {
|
class DragConnector {
|
||||||
constructor(designerModel, workspace) {
|
private _designerModel: DesignerModel;
|
||||||
|
|
||||||
|
private _workspace: Workspace;
|
||||||
|
|
||||||
|
constructor(designerModel: DesignerModel, workspace: Workspace) {
|
||||||
$assert(designerModel, 'designerModel can not be null');
|
$assert(designerModel, 'designerModel can not be null');
|
||||||
$assert(workspace, 'workspace can not be null');
|
$assert(workspace, 'workspace can not be null');
|
||||||
|
|
||||||
// this._layoutManager = layoutManager;
|
|
||||||
this._designerModel = designerModel;
|
this._designerModel = designerModel;
|
||||||
this._workspace = workspace;
|
this._workspace = workspace;
|
||||||
}
|
}
|
||||||
|
|
||||||
checkConnection(dragTopic) {
|
checkConnection(dragTopic: DragTopic): void {
|
||||||
// Must be disconnected from their current connection ?.
|
// Must be disconnected from their current connection ?.
|
||||||
const candidates = this._searchConnectionCandidates(dragTopic);
|
const candidates = this._searchConnectionCandidates(dragTopic);
|
||||||
const currentConnection = dragTopic.getConnectedToTopic();
|
const currentConnection = dragTopic.getConnectedToTopic();
|
||||||
@ -42,12 +51,13 @@ class DragConnector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_searchConnectionCandidates(dragTopic) {
|
private _searchConnectionCandidates(dragTopic: DragTopic): Topic[] {
|
||||||
let topics = this._designerModel.getTopics();
|
let topics = this._designerModel.getTopics();
|
||||||
const draggedNode = dragTopic.getDraggedTopic();
|
const draggedNode = dragTopic.getDraggedTopic();
|
||||||
|
|
||||||
// Drag node connects to the border ...
|
// Drag node connects to the border ...
|
||||||
const dragTopicWidth = dragTopic.getSize ? dragTopic.getSize().width : 0; // Hack...
|
// const dragTopicWidth = dragTopic.getSize ? dragTopic.getSize().width : 0; // Hack...
|
||||||
|
const dragTopicWidth = 0;
|
||||||
const xMouseGap = dragTopic.getPosition().x > 0 ? 0 : dragTopicWidth;
|
const xMouseGap = dragTopic.getPosition().x > 0 ? 0 : dragTopicWidth;
|
||||||
const sPos = { x: dragTopic.getPosition().x - xMouseGap, y: dragTopic.getPosition().y };
|
const sPos = { x: dragTopic.getPosition().x - xMouseGap, y: dragTopic.getPosition().y };
|
||||||
|
|
||||||
@ -56,7 +66,7 @@ class DragConnector {
|
|||||||
// - Exclude dragTopic pivot
|
// - Exclude dragTopic pivot
|
||||||
// - Nodes that are collapsed
|
// - Nodes that are collapsed
|
||||||
// - It's not part of the branch dragged itself
|
// - It's not part of the branch dragged itself
|
||||||
topics = topics.filter((topic) => {
|
topics = topics.filter((topic: Topic) => {
|
||||||
let result = draggedNode !== topic;
|
let result = draggedNode !== topic;
|
||||||
result = result && topic !== draggedNode;
|
result = result && topic !== draggedNode;
|
||||||
result = result && !topic.areChildrenShrunken() && !topic.isCollapsed();
|
result = result && !topic.areChildrenShrunken() && !topic.isCollapsed();
|
||||||
@ -67,7 +77,7 @@ class DragConnector {
|
|||||||
// Filter all the nodes that are outside the vertical boundary:
|
// Filter all the nodes that are outside the vertical boundary:
|
||||||
// * The node is to out of the x scope
|
// * The node is to out of the x scope
|
||||||
// * The x distance greater the vertical tolerated distance
|
// * The x distance greater the vertical tolerated distance
|
||||||
topics = topics.filter((topic) => {
|
topics = topics.filter((topic: Topic) => {
|
||||||
const tpos = topic.getPosition();
|
const tpos = topic.getPosition();
|
||||||
// Center topic has different alignment than the rest of the nodes.
|
// Center topic has different alignment than the rest of the nodes.
|
||||||
// That's why i need to divide it by two...
|
// That's why i need to divide it by two...
|
||||||
@ -95,17 +105,17 @@ class DragConnector {
|
|||||||
return topics;
|
return topics;
|
||||||
}
|
}
|
||||||
|
|
||||||
_proximityWeight(isAligned, target, sPos, currentConnection) {
|
private _proximityWeight(isAligned: boolean, target: Topic, sPos: Point, currentConnection: Topic): number {
|
||||||
const tPos = target.getPosition();
|
const tPos = target.getPosition();
|
||||||
return (isAligned ? 0 : 200) + Math.abs(tPos.x - sPos.x)
|
return (isAligned ? 0 : 200) + Math.abs(tPos.x - sPos.x)
|
||||||
+ Math.abs(tPos.y - sPos.y) + (currentConnection === target ? 0 : 100);
|
+ Math.abs(tPos.y - sPos.y) + (currentConnection === target ? 0 : 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
_isVerticallyAligned(targetSize, targetPosition, sourcePosition) {
|
private _isVerticallyAligned(targetSize: SizeType, targetPosition: Point, sourcePosition: Point): boolean {
|
||||||
return Math.abs(sourcePosition.y - targetPosition.y) < targetSize.height / 2;
|
return Math.abs(sourcePosition.y - targetPosition.y) < targetSize.height / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static MAX_VERTICAL_CONNECTION_TOLERANCE = 80;
|
||||||
}
|
}
|
||||||
|
|
||||||
DragConnector.MAX_VERTICAL_CONNECTION_TOLERANCE = 80;
|
|
||||||
|
|
||||||
export default DragConnector;
|
export default DragConnector;
|
@ -18,6 +18,7 @@
|
|||||||
import { $assert, $defined } from '@wisemapping/core-js';
|
import { $assert, $defined } from '@wisemapping/core-js';
|
||||||
import DragTopic from './DragTopic';
|
import DragTopic from './DragTopic';
|
||||||
import EventBusDispatcher from './layout/EventBusDispatcher';
|
import EventBusDispatcher from './layout/EventBusDispatcher';
|
||||||
|
import Topic from './Topic';
|
||||||
import Workspace from './Workspace';
|
import Workspace from './Workspace';
|
||||||
|
|
||||||
class DragManager {
|
class DragManager {
|
||||||
@ -44,7 +45,7 @@ class DragManager {
|
|||||||
DragTopic.init(this._workspace);
|
DragTopic.init(this._workspace);
|
||||||
}
|
}
|
||||||
|
|
||||||
add(node) {
|
add(topic: Topic) {
|
||||||
// Add behaviour ...
|
// Add behaviour ...
|
||||||
const workspace = this._workspace;
|
const workspace = this._workspace;
|
||||||
const screen = workspace.getScreenManager();
|
const screen = workspace.getScreenManager();
|
||||||
@ -57,7 +58,7 @@ class DragManager {
|
|||||||
|
|
||||||
// Set initial position.
|
// Set initial position.
|
||||||
const layoutManager = me._eventDispatcher.getLayoutManager();
|
const layoutManager = me._eventDispatcher.getLayoutManager();
|
||||||
const dragNode = node.createDragNode(layoutManager);
|
const dragNode = topic.createDragNode(layoutManager);
|
||||||
|
|
||||||
// Register mouse move listener ...
|
// Register mouse move listener ...
|
||||||
const mouseMoveListener = dragManager._buildMouseMoveListener(
|
const mouseMoveListener = dragManager._buildMouseMoveListener(
|
||||||
@ -67,7 +68,7 @@ class DragManager {
|
|||||||
|
|
||||||
// Register mouse up listeners ...
|
// Register mouse up listeners ...
|
||||||
const mouseUpListener = dragManager._buildMouseUpListener(
|
const mouseUpListener = dragManager._buildMouseUpListener(
|
||||||
workspace, node, dragNode, dragManager,
|
workspace, topic, dragNode, dragManager,
|
||||||
);
|
);
|
||||||
screen.addEvent('mouseup', mouseUpListener);
|
screen.addEvent('mouseup', mouseUpListener);
|
||||||
|
|
||||||
@ -75,7 +76,7 @@ class DragManager {
|
|||||||
window.document.body.style.cursor = 'move';
|
window.document.body.style.cursor = 'move';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
node.addEvent('mousedown', mouseDownListener);
|
topic.addEvent('mousedown', mouseDownListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
remove() {
|
remove() {
|
||||||
@ -114,10 +115,10 @@ class DragManager {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _buildMouseUpListener(workspace: Workspace, node, dragNode, dragManager: DragManager) {
|
protected _buildMouseUpListener(workspace: Workspace, topic: Topic, dragNode, dragManager: DragManager) {
|
||||||
const screen = workspace.getScreenManager();
|
const screen = workspace.getScreenManager();
|
||||||
const me = this;
|
const me = this;
|
||||||
const result = (event) => {
|
const result = (event: Event) => {
|
||||||
$assert(dragNode.isDragTopic, 'dragNode must be an DragTopic');
|
$assert(dragNode.isDragTopic, 'dragNode must be an DragTopic');
|
||||||
|
|
||||||
// Remove all the events.
|
// Remove all the events.
|
||||||
@ -154,7 +155,7 @@ class DragManager {
|
|||||||
* - dragging
|
* - dragging
|
||||||
* - enddragging
|
* - enddragging
|
||||||
*/
|
*/
|
||||||
addEvent(type, listener) {
|
addEvent(type: string, listener) {
|
||||||
this._listeners[type] = listener;
|
this._listeners[type] = listener;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,9 +19,28 @@ import { $assert, $defined } from '@wisemapping/core-js';
|
|||||||
import { Point, CurvedLine, Rect } from '@wisemapping/web2d';
|
import { Point, CurvedLine, Rect } from '@wisemapping/web2d';
|
||||||
|
|
||||||
import DragTopicConfig from './DragTopicConfig';
|
import DragTopicConfig from './DragTopicConfig';
|
||||||
|
import SizeType from './SizeType';
|
||||||
|
import Topic from './Topic';
|
||||||
import Shape from './util/Shape';
|
import Shape from './util/Shape';
|
||||||
|
import Workspace from './Workspace';
|
||||||
|
|
||||||
class DragPivot {
|
class DragPivot {
|
||||||
|
private _position: Point;
|
||||||
|
|
||||||
|
private _isVisible: boolean;
|
||||||
|
|
||||||
|
private _targetTopic: Topic;
|
||||||
|
|
||||||
|
private _connectRect: Rect;
|
||||||
|
|
||||||
|
private _dragPivot: Rect;
|
||||||
|
|
||||||
|
private _curvedLine: CurvedLine;
|
||||||
|
|
||||||
|
private _straightLine: CurvedLine;
|
||||||
|
|
||||||
|
private _size: SizeType;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this._position = new Point();
|
this._position = new Point();
|
||||||
this._size = DragTopicConfig.PIVOT_SIZE;
|
this._size = DragTopicConfig.PIVOT_SIZE;
|
||||||
@ -34,15 +53,15 @@ class DragPivot {
|
|||||||
this._isVisible = false;
|
this._isVisible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
isVisible() {
|
isVisible(): boolean {
|
||||||
return this._isVisible;
|
return this._isVisible;
|
||||||
}
|
}
|
||||||
|
|
||||||
getTargetTopic() {
|
getTargetTopic(): Topic {
|
||||||
return this._targetTopic;
|
return this._targetTopic;
|
||||||
}
|
}
|
||||||
|
|
||||||
_buildStraightLine() {
|
private _buildStraightLine(): CurvedLine {
|
||||||
const line = new CurvedLine();
|
const line = new CurvedLine();
|
||||||
line.setStyle(CurvedLine.SIMPLE_LINE);
|
line.setStyle(CurvedLine.SIMPLE_LINE);
|
||||||
line.setStroke(1, 'solid', '#CC0033');
|
line.setStroke(1, 'solid', '#CC0033');
|
||||||
@ -51,7 +70,7 @@ class DragPivot {
|
|||||||
return line;
|
return line;
|
||||||
}
|
}
|
||||||
|
|
||||||
_buildCurvedLine() {
|
private _buildCurvedLine(): CurvedLine {
|
||||||
const line = new CurvedLine();
|
const line = new CurvedLine();
|
||||||
line.setStyle(CurvedLine.SIMPLE_LINE);
|
line.setStyle(CurvedLine.SIMPLE_LINE);
|
||||||
line.setStroke(1, 'solid', '#CC0033');
|
line.setStroke(1, 'solid', '#CC0033');
|
||||||
@ -60,7 +79,7 @@ class DragPivot {
|
|||||||
return line;
|
return line;
|
||||||
}
|
}
|
||||||
|
|
||||||
_redrawLine() {
|
private _redrawLine(): void {
|
||||||
// Update line position.
|
// Update line position.
|
||||||
$assert(this.getTargetTopic(), 'Illegal invocation. Target node can not be null');
|
$assert(this.getTargetTopic(), 'Illegal invocation. Target node can not be null');
|
||||||
|
|
||||||
@ -81,8 +100,8 @@ class DragPivot {
|
|||||||
line.setFrom(pivotPoint.x, pivotPoint.y);
|
line.setFrom(pivotPoint.x, pivotPoint.y);
|
||||||
|
|
||||||
// Update rect position
|
// Update rect position
|
||||||
const cx = position.x - parseInt(size.width, 10) / 2;
|
const cx = position.x - size.width / 2;
|
||||||
const cy = position.y - parseInt(size.height, 10) / 2;
|
const cy = position.y - size.height / 2;
|
||||||
pivotRect.setPosition(cx, cy);
|
pivotRect.setPosition(cx, cy);
|
||||||
|
|
||||||
// Make line visible only when the position has been already changed.
|
// Make line visible only when the position has been already changed.
|
||||||
@ -91,16 +110,16 @@ class DragPivot {
|
|||||||
line.setTo(targetPoint.x, targetPoint.y);
|
line.setTo(targetPoint.x, targetPoint.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
setPosition(point) {
|
setPosition(point: Point): void {
|
||||||
this._position = point;
|
this._position = point;
|
||||||
this._redrawLine();
|
this._redrawLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
getPosition() {
|
getPosition(): Point {
|
||||||
return this._position;
|
return this._position;
|
||||||
}
|
}
|
||||||
|
|
||||||
_buildRect() {
|
private _buildRect(): Rect {
|
||||||
const size = this._size;
|
const size = this._size;
|
||||||
const rectAttributes = {
|
const rectAttributes = {
|
||||||
fillColor: '#CC0033',
|
fillColor: '#CC0033',
|
||||||
@ -114,16 +133,16 @@ class DragPivot {
|
|||||||
return rect;
|
return rect;
|
||||||
}
|
}
|
||||||
|
|
||||||
_getPivotRect() {
|
private _getPivotRect(): Rect {
|
||||||
return this._dragPivot;
|
return this._dragPivot;
|
||||||
}
|
}
|
||||||
|
|
||||||
getSize() {
|
getSize(): SizeType {
|
||||||
const elem2d = this._getPivotRect();
|
const elem2d = this._getPivotRect();
|
||||||
return elem2d.getSize();
|
return elem2d.getSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
setVisibility(value) {
|
setVisibility(value: boolean) {
|
||||||
if (this.isVisible() !== value) {
|
if (this.isVisible() !== value) {
|
||||||
const pivotRect = this._getPivotRect();
|
const pivotRect = this._getPivotRect();
|
||||||
pivotRect.setVisibility(value);
|
pivotRect.setVisibility(value);
|
||||||
@ -140,7 +159,7 @@ class DragPivot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If the node is connected, validate that there is a line connecting both...
|
// If the node is connected, validate that there is a line connecting both...
|
||||||
_getConnectionLine() {
|
_getConnectionLine(): CurvedLine {
|
||||||
let result = null;
|
let result = null;
|
||||||
const parentTopic = this._targetTopic;
|
const parentTopic = this._targetTopic;
|
||||||
if (parentTopic) {
|
if (parentTopic) {
|
||||||
@ -153,7 +172,7 @@ class DragPivot {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
addToWorkspace(workspace) {
|
addToWorkspace(workspace: Workspace) {
|
||||||
const pivotRect = this._getPivotRect();
|
const pivotRect = this._getPivotRect();
|
||||||
workspace.append(pivotRect);
|
workspace.append(pivotRect);
|
||||||
|
|
||||||
@ -179,7 +198,7 @@ class DragPivot {
|
|||||||
connectRect.moveToBack();
|
connectRect.moveToBack();
|
||||||
}
|
}
|
||||||
|
|
||||||
removeFromWorkspace(workspace) {
|
removeFromWorkspace(workspace: Workspace) {
|
||||||
const shape = this._getPivotRect();
|
const shape = this._getPivotRect();
|
||||||
workspace.removeChild(shape);
|
workspace.removeChild(shape);
|
||||||
|
|
||||||
@ -195,9 +214,7 @@ class DragPivot {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
connectTo(targetTopic, position) {
|
connectTo(targetTopic: Topic, position: Point) {
|
||||||
$assert(!this._outgoingLine, 'Could not connect an already connected node');
|
|
||||||
$assert(targetTopic !== this, 'Circular connection are not allowed');
|
|
||||||
$assert(position, 'position can not be null');
|
$assert(position, 'position can not be null');
|
||||||
$assert(targetTopic, 'parent can not be null');
|
$assert(targetTopic, 'parent can not be null');
|
||||||
|
|
||||||
@ -227,7 +244,7 @@ class DragPivot {
|
|||||||
this._redrawLine();
|
this._redrawLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnect(workspace) {
|
disconnect(workspace: Workspace): void {
|
||||||
$assert(workspace, 'workspace can not be null.');
|
$assert(workspace, 'workspace can not be null.');
|
||||||
$assert(this._targetTopic, 'There are not connected topic.');
|
$assert(this._targetTopic, 'There are not connected topic.');
|
||||||
|
|
@ -16,13 +16,24 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { $assert, $defined } from '@wisemapping/core-js';
|
import { $assert, $defined } from '@wisemapping/core-js';
|
||||||
import { Point } from '@wisemapping/web2d';
|
import { Point, ElementClass } from '@wisemapping/web2d';
|
||||||
|
|
||||||
import ActionDispatcher from './ActionDispatcher';
|
import ActionDispatcher from './ActionDispatcher';
|
||||||
import DragPivot from './DragPivot';
|
import DragPivot from './DragPivot';
|
||||||
|
import LayoutManager from './layout/LayoutManager';
|
||||||
|
import NodeGraph from './NodeGraph';
|
||||||
|
import Topic from './Topic';
|
||||||
|
import Workspace from './Workspace';
|
||||||
|
|
||||||
class DragTopic {
|
class DragTopic {
|
||||||
constructor(dragShape, draggedNode, layoutManger) {
|
private _elem2d: ElementClass;
|
||||||
|
private _order: number | null;
|
||||||
|
private _draggedNode: NodeGraph;
|
||||||
|
private _layoutManager: LayoutManager;
|
||||||
|
private _position: any;
|
||||||
|
private _isInWorkspace: boolean;
|
||||||
|
static _dragPivot: any;
|
||||||
|
constructor(dragShape: ElementClass, draggedNode: NodeGraph, layoutManger: LayoutManager) {
|
||||||
$assert(dragShape, 'Rect can not be null.');
|
$assert(dragShape, 'Rect can not be null.');
|
||||||
$assert(draggedNode, 'draggedNode can not be null.');
|
$assert(draggedNode, 'draggedNode can not be null.');
|
||||||
$assert(layoutManger, 'layoutManger can not be null.');
|
$assert(layoutManger, 'layoutManger can not be null.');
|
||||||
@ -33,26 +44,15 @@ class DragTopic {
|
|||||||
this._layoutManager = layoutManger;
|
this._layoutManager = layoutManger;
|
||||||
this._position = new Point();
|
this._position = new Point();
|
||||||
this._isInWorkspace = false;
|
this._isInWorkspace = false;
|
||||||
this._isFreeLayoutEnabled = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setOrder(order) {
|
setOrder(order: number) {
|
||||||
this._order = order;
|
this._order = order;
|
||||||
}
|
}
|
||||||
|
|
||||||
setPosition(x, y) {
|
setPosition(x: number, y: number) {
|
||||||
// Update drag shadow position ....
|
// Update drag shadow position ....
|
||||||
let position = { x, y };
|
let position = { x, y };
|
||||||
if (this.isFreeLayoutOn() && this.isConnected()) {
|
|
||||||
const { _layoutManager } = this;
|
|
||||||
const par = this.getConnectedToTopic();
|
|
||||||
position = _layoutManager.predict(
|
|
||||||
par.getId(),
|
|
||||||
this._draggedNode.getId(),
|
|
||||||
position,
|
|
||||||
true,
|
|
||||||
).position;
|
|
||||||
}
|
|
||||||
this._position.setValue(position.x, position.y);
|
this._position.setValue(position.x, position.y);
|
||||||
|
|
||||||
// Elements are positioned in the center.
|
// Elements are positioned in the center.
|
||||||
@ -80,45 +80,35 @@ class DragTopic {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFreeLayout(event) {
|
setVisibility(value: boolean) {
|
||||||
const isMac = window.navigator.platform.toUpperCase().indexOf('MAC') >= 0;
|
|
||||||
const isFreeEnabled = (event.metaKey && isMac) || (event.ctrlKey && !isMac);
|
|
||||||
|
|
||||||
if (this.isFreeLayoutOn() !== isFreeEnabled) {
|
|
||||||
const dragPivot = this._getDragPivot();
|
|
||||||
dragPivot.setVisibility(!isFreeEnabled);
|
|
||||||
this._isFreeLayoutEnabled = isFreeEnabled;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setVisibility(value) {
|
|
||||||
const dragPivot = this._getDragPivot();
|
const dragPivot = this._getDragPivot();
|
||||||
dragPivot.setVisibility(value);
|
dragPivot.setVisibility(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
isVisible() {
|
isVisible(): boolean {
|
||||||
const dragPivot = this._getDragPivot();
|
const dragPivot = this._getDragPivot();
|
||||||
return dragPivot.isVisible();
|
return dragPivot.isVisible();
|
||||||
}
|
}
|
||||||
|
|
||||||
getInnerShape() {
|
getInnerShape(): ElementClass {
|
||||||
return this._elem2d;
|
return this._elem2d;
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnect(workspace) {
|
disconnect(workspace: Workspace) {
|
||||||
// Clear connection line ...
|
// Clear connection line ...
|
||||||
const dragPivot = this._getDragPivot();
|
const dragPivot = this._getDragPivot();
|
||||||
dragPivot.disconnect(workspace);
|
dragPivot.disconnect(workspace);
|
||||||
}
|
}
|
||||||
|
|
||||||
connectTo(parent) {
|
connectTo(parent: Topic) {
|
||||||
$assert(parent, 'Parent connection node can not be null.');
|
$assert(parent, 'Parent connection node can not be null.');
|
||||||
|
|
||||||
// Where it should be connected ?
|
// Where it should be connected ?
|
||||||
|
|
||||||
// @todo: This is a hack for the access of the editor.
|
// @todo: This is a hack for the access of the editor.
|
||||||
// It's required to review why this is needed forcing the declaration of a global variable.
|
// It's required to review why this is needed forcing the declaration of a global variable.
|
||||||
const predict = designer._eventBussDispatcher._layoutManager.predict(
|
|
||||||
|
const predict = global.designer._eventBussDispatcher._layoutManager.predict(
|
||||||
parent.getId(),
|
parent.getId(),
|
||||||
this._draggedNode.getId(),
|
this._draggedNode.getId(),
|
||||||
this.getPosition(),
|
this.getPosition(),
|
||||||
@ -133,11 +123,11 @@ class DragTopic {
|
|||||||
this.setOrder(predict.order);
|
this.setOrder(predict.order);
|
||||||
}
|
}
|
||||||
|
|
||||||
getDraggedTopic() {
|
getDraggedTopic(): Topic {
|
||||||
return this._draggedNode;
|
return this._draggedNode as Topic;
|
||||||
}
|
}
|
||||||
|
|
||||||
removeFromWorkspace(workspace) {
|
removeFromWorkspace(workspace: Workspace) {
|
||||||
if (this._isInWorkspace) {
|
if (this._isInWorkspace) {
|
||||||
// Remove drag shadow.
|
// Remove drag shadow.
|
||||||
workspace.removeChild(this._elem2d);
|
workspace.removeChild(this._elem2d);
|
||||||
@ -151,11 +141,11 @@ class DragTopic {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isInWorkspace() {
|
isInWorkspace(): boolean {
|
||||||
return this._isInWorkspace;
|
return this._isInWorkspace;
|
||||||
}
|
}
|
||||||
|
|
||||||
addToWorkspace(workspace) {
|
addToWorkspace(workspace: Workspace) {
|
||||||
if (!this._isInWorkspace) {
|
if (!this._isInWorkspace) {
|
||||||
workspace.append(this._elem2d);
|
workspace.append(this._elem2d);
|
||||||
const dragPivot = this._getDragPivot();
|
const dragPivot = this._getDragPivot();
|
||||||
@ -164,19 +154,19 @@ class DragTopic {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_getDragPivot() {
|
_getDragPivot(): DragPivot {
|
||||||
return DragTopic.__getDragPivot();
|
return DragTopic.__getDragPivot();
|
||||||
}
|
}
|
||||||
|
|
||||||
getPosition() {
|
getPosition(): Point {
|
||||||
return this._position;
|
return this._position;
|
||||||
}
|
}
|
||||||
|
|
||||||
isDragTopic() {
|
isDragTopic(): boolean {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
applyChanges(workspace) {
|
applyChanges(workspace: Workspace) {
|
||||||
$assert(workspace, 'workspace can not be null');
|
$assert(workspace, 'workspace can not be null');
|
||||||
|
|
||||||
const actionDispatcher = ActionDispatcher.getInstance();
|
const actionDispatcher = ActionDispatcher.getInstance();
|
||||||
@ -201,27 +191,26 @@ class DragTopic {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getConnectedToTopic() {
|
getConnectedToTopic(): Topic {
|
||||||
const dragPivot = this._getDragPivot();
|
const dragPivot = this._getDragPivot();
|
||||||
return dragPivot.getTargetTopic();
|
return dragPivot.getTargetTopic();
|
||||||
}
|
}
|
||||||
|
|
||||||
isConnected() {
|
isConnected(): boolean {
|
||||||
return this.getConnectedToTopic() != null;
|
return this.getConnectedToTopic() != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
isFreeLayoutOn() {
|
isFreeLayoutOn(): false {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
DragTopic.init = function init(workspace) {
|
static init(workspace: Workspace) {
|
||||||
$assert(workspace, 'workspace can not be null');
|
$assert(workspace, 'workspace can not be null');
|
||||||
const pivot = DragTopic.__getDragPivot();
|
const pivot = DragTopic.__getDragPivot();
|
||||||
workspace.append(pivot);
|
workspace.append(pivot);
|
||||||
};
|
};
|
||||||
|
|
||||||
DragTopic.__getDragPivot = function __getDragPivot() {
|
static __getDragPivot() {
|
||||||
let result = DragTopic._dragPivot;
|
let result = DragTopic._dragPivot;
|
||||||
if (!$defined(result)) {
|
if (!$defined(result)) {
|
||||||
result = new DragPivot();
|
result = new DragPivot();
|
||||||
@ -229,5 +218,6 @@ DragTopic.__getDragPivot = function __getDragPivot() {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export default DragTopic;
|
export default DragTopic;
|
2
packages/mindplot/src/components/EditorRenderMode.ts
Normal file
2
packages/mindplot/src/components/EditorRenderMode.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
type EditorRenderMode = 'viewonly' | 'edition' | 'showcase';
|
||||||
|
export default EditorRenderMode;
|
@ -17,36 +17,43 @@
|
|||||||
*/
|
*/
|
||||||
import { $assert } from '@wisemapping/core-js';
|
import { $assert } from '@wisemapping/core-js';
|
||||||
import { Image } from '@wisemapping/web2d';
|
import { Image } from '@wisemapping/web2d';
|
||||||
|
import IconGroup from './IconGroup';
|
||||||
|
import { Point } from '@wisemapping/web2d';
|
||||||
|
import SizeType from './SizeType';
|
||||||
|
import FeatureModel from './model/FeatureModel';
|
||||||
|
|
||||||
class Icon {
|
abstract class Icon {
|
||||||
constructor(url) {
|
protected _image: Image;
|
||||||
|
protected _group: IconGroup;
|
||||||
|
|
||||||
|
constructor(url: string) {
|
||||||
$assert(url, 'topic can not be null');
|
$assert(url, 'topic can not be null');
|
||||||
this._image = new Image();
|
this._image = new Image();
|
||||||
this._image.setHref(url);
|
this._image.setHref(url);
|
||||||
this._image.setSize(Icon.SIZE, Icon.SIZE);
|
this._image.setSize(Icon.SIZE, Icon.SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
getImage() {
|
getImage(): Image {
|
||||||
return this._image;
|
return this._image;
|
||||||
}
|
}
|
||||||
|
|
||||||
setGroup(group) {
|
setGroup(group: IconGroup) {
|
||||||
this._group = group;
|
this._group = group;
|
||||||
}
|
}
|
||||||
|
|
||||||
getGroup() {
|
getGroup(): IconGroup {
|
||||||
return this._group;
|
return this._group;
|
||||||
}
|
}
|
||||||
|
|
||||||
getSize() {
|
getSize(): SizeType {
|
||||||
return this._image.getSize();
|
return this._image.getSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
getPosition() {
|
getPosition(): Point {
|
||||||
return this._image.getPosition();
|
return this._image.getPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
addEvent(type, fnc) {
|
addEvent(type: string, fnc): void {
|
||||||
this._image.addEvent(type, fnc);
|
this._image.addEvent(type, fnc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,8 +61,10 @@ class Icon {
|
|||||||
remove() {
|
remove() {
|
||||||
throw new Error('Unsupported operation');
|
throw new Error('Unsupported operation');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract getModel(): FeatureModel;
|
||||||
|
|
||||||
|
static SIZE = 90;
|
||||||
}
|
}
|
||||||
|
|
||||||
Icon.SIZE = 90;
|
|
||||||
|
|
||||||
export default Icon;
|
export default Icon;
|
@ -22,21 +22,31 @@ import {
|
|||||||
} from '@wisemapping/core-js';
|
} from '@wisemapping/core-js';
|
||||||
import {
|
import {
|
||||||
Group,
|
Group,
|
||||||
|
ElementClass,
|
||||||
} from '@wisemapping/web2d';
|
} from '@wisemapping/web2d';
|
||||||
import IconGroupRemoveTip from './IconGroupRemoveTip';
|
import IconGroupRemoveTip from './IconGroupRemoveTip';
|
||||||
|
import { Point } from '@wisemapping/web2d';
|
||||||
import Icon from './Icon';
|
import Icon from './Icon';
|
||||||
|
import SizeType from './SizeType';
|
||||||
|
import IconModel from './model/IconModel';
|
||||||
|
import FeatureModel from './model/FeatureModel';
|
||||||
|
|
||||||
const ORDER_BY_TYPE = new Map();
|
const ORDER_BY_TYPE = new Map<string, number>();
|
||||||
ORDER_BY_TYPE.set('icon', 0);
|
ORDER_BY_TYPE.set('icon', 0);
|
||||||
ORDER_BY_TYPE.set('note', 1);
|
ORDER_BY_TYPE.set('note', 1);
|
||||||
ORDER_BY_TYPE.set('link', 2);
|
ORDER_BY_TYPE.set('link', 2);
|
||||||
|
|
||||||
class IconGroup {
|
class IconGroup {
|
||||||
constructor(topicId, iconSize) {
|
private _icons: Icon[];
|
||||||
|
private _group: any;
|
||||||
|
private _removeTip: IconGroupRemoveTip;
|
||||||
|
private _iconSize: SizeType;
|
||||||
|
private _topicId: number;
|
||||||
|
constructor(topicId: number, iconSize: number) {
|
||||||
$assert($defined(topicId), 'topicId can not be null');
|
$assert($defined(topicId), 'topicId can not be null');
|
||||||
$assert($defined(iconSize), 'iconSize can not be null');
|
$assert($defined(iconSize), 'iconSize can not be null');
|
||||||
|
|
||||||
|
this._topicId = topicId;
|
||||||
this._icons = [];
|
this._icons = [];
|
||||||
this._group = new Group({
|
this._group = new Group({
|
||||||
width: 0,
|
width: 0,
|
||||||
@ -46,34 +56,31 @@ class IconGroup {
|
|||||||
coordSizeWidth: 0,
|
coordSizeWidth: 0,
|
||||||
coordSizeHeight: 100,
|
coordSizeHeight: 100,
|
||||||
});
|
});
|
||||||
this._removeTip = new IconGroupRemoveTip(this._group, topicId);
|
this._removeTip = new IconGroupRemoveTip(this._group);
|
||||||
this.seIconSize(iconSize, iconSize);
|
this.seIconSize(iconSize, iconSize);
|
||||||
|
|
||||||
this._registerListeners();
|
this._registerListeners();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** */
|
setPosition(x: number, y: number): void {
|
||||||
setPosition(x, y) {
|
|
||||||
this._group.setPosition(x, y);
|
this._group.setPosition(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** */
|
getPosition(): Point {
|
||||||
getPosition() {
|
|
||||||
return this._group.getPosition();
|
return this._group.getPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** */
|
getNativeElement(): ElementClass {
|
||||||
getNativeElement() {
|
|
||||||
return this._group;
|
return this._group;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** */
|
/** */
|
||||||
getSize() {
|
getSize(): SizeType {
|
||||||
return this._group.getSize();
|
return this._group.getSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** */
|
/** */
|
||||||
seIconSize(width, height) {
|
seIconSize(width: number, height: number) {
|
||||||
this._iconSize = {
|
this._iconSize = {
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
@ -81,12 +88,7 @@ class IconGroup {
|
|||||||
this._resize(this._icons.length);
|
this._resize(this._icons.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
addIcon(icon: Icon, remove: boolean) {
|
||||||
* @param icon the icon to be added to the icon group
|
|
||||||
* @param {Boolean} remove
|
|
||||||
* @throws will throw an error if icon is not defined
|
|
||||||
*/
|
|
||||||
addIcon(icon, remove) {
|
|
||||||
$defined(icon, 'icon is not defined');
|
$defined(icon, 'icon is not defined');
|
||||||
|
|
||||||
// Order could have change, need to re-add all.
|
// Order could have change, need to re-add all.
|
||||||
@ -113,7 +115,7 @@ class IconGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_findIconFromModel(iconModel) {
|
private _findIconFromModel(iconModel: FeatureModel) {
|
||||||
let result = null;
|
let result = null;
|
||||||
|
|
||||||
this._icons.forEach((icon) => {
|
this._icons.forEach((icon) => {
|
||||||
@ -132,14 +134,14 @@ class IconGroup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** */
|
/** */
|
||||||
removeIconByModel(featureModel) {
|
removeIconByModel(featureModel: FeatureModel) {
|
||||||
$assert(featureModel, 'featureModel can not be null');
|
$assert(featureModel, 'featureModel can not be null');
|
||||||
|
|
||||||
const icon = this._findIconFromModel(featureModel);
|
const icon = this._findIconFromModel(featureModel);
|
||||||
this._removeIcon(icon);
|
this._removeIcon(icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
_removeIcon(icon) {
|
private _removeIcon(icon: Icon) {
|
||||||
$assert(icon, 'icon can not be null');
|
$assert(icon, 'icon can not be null');
|
||||||
|
|
||||||
this._removeTip.close(0);
|
this._removeTip.close(0);
|
||||||
@ -156,11 +158,11 @@ class IconGroup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** */
|
/** */
|
||||||
moveToFront() {
|
moveToFront(): void {
|
||||||
this._group.moveToFront();
|
this._group.moveToFront();
|
||||||
}
|
}
|
||||||
|
|
||||||
_registerListeners() {
|
private _registerListeners() {
|
||||||
this._group.addEvent('click', (event) => {
|
this._group.addEvent('click', (event) => {
|
||||||
// Avoid node creation ...
|
// Avoid node creation ...
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
@ -171,21 +173,23 @@ class IconGroup {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_resize(iconsLength) {
|
private _resize(iconsLength: number) {
|
||||||
this._group.setSize(iconsLength * this._iconSize.width, this._iconSize.height);
|
this._group.setSize(iconsLength * this._iconSize.width, this._iconSize.height);
|
||||||
|
|
||||||
const iconSize = Icon.SIZE + IconGroup.ICON_PADDING * 2;
|
const iconSize = Icon.SIZE + IconGroup.ICON_PADDING * 2;
|
||||||
this._group.setCoordSize(iconsLength * iconSize, iconSize);
|
this._group.setCoordSize(iconsLength * iconSize, iconSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
_positionIcon(icon, order) {
|
private _positionIcon(icon: Icon, order: number) {
|
||||||
const iconSize = Icon.SIZE + IconGroup.ICON_PADDING * 2;
|
const iconSize = Icon.SIZE + IconGroup.ICON_PADDING * 2;
|
||||||
icon.getImage().setPosition(
|
icon.getImage().setPosition(
|
||||||
iconSize * order + IconGroup.ICON_PADDING,
|
iconSize * order + IconGroup.ICON_PADDING,
|
||||||
IconGroup.ICON_PADDING,
|
IconGroup.ICON_PADDING,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ICON_PADDING = 5;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IconGroup.ICON_PADDING = 5;
|
|
||||||
export default IconGroup;
|
export default IconGroup;
|
@ -93,7 +93,7 @@ class ImageIcon extends Icon {
|
|||||||
|
|
||||||
static _getFamilyIcons(iconId) {
|
static _getFamilyIcons(iconId) {
|
||||||
$assert(iconId != null, 'id must not be null');
|
$assert(iconId != null, 'id must not be null');
|
||||||
$assert(iconId.indexOf('_') !== -1, "Invalid icon id (it must contain '_')");
|
$assert(iconId.indexOf('_') !== -1, `Invalid icon id (it must contain '_'). Id: ${iconId}`);
|
||||||
|
|
||||||
let result = null;
|
let result = null;
|
||||||
for (let i = 0; i < ImageIcon.prototype.ICON_FAMILIES.length; i++) {
|
for (let i = 0; i < ImageIcon.prototype.ICON_FAMILIES.length; i++) {
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
|
|
||||||
class Keyboard {
|
class Keyboard {
|
||||||
addShortcut(shortcuts, callback) {
|
addShortcut(shortcuts: string[] | string, callback) {
|
||||||
const shortcutsArray = Array.isArray(shortcuts) ? shortcuts : [shortcuts];
|
const shortcutsArray = Array.isArray(shortcuts) ? shortcuts : [shortcuts];
|
||||||
shortcutsArray.forEach((shortcut) => {
|
shortcutsArray.forEach((shortcut) => {
|
||||||
$(document).bind('keydown', shortcut, callback);
|
$(document).bind('keydown', shortcut, callback);
|
||||||
|
@ -20,9 +20,17 @@ import $ from 'jquery';
|
|||||||
import Icon from './Icon';
|
import Icon from './Icon';
|
||||||
import LinkIconTooltip from './widget/LinkIconTooltip';
|
import LinkIconTooltip from './widget/LinkIconTooltip';
|
||||||
import LinksImage from '../../assets/icons/links.svg';
|
import LinksImage from '../../assets/icons/links.svg';
|
||||||
|
import LinkModel from './model/LinkModel';
|
||||||
|
import Topic from './Topic';
|
||||||
|
import FeatureModel from './model/FeatureModel';
|
||||||
|
|
||||||
class LinkIcon extends Icon {
|
class LinkIcon extends Icon {
|
||||||
constructor(topic, linkModel, readOnly) {
|
private _linksModel: FeatureModel;
|
||||||
|
private _topic: Topic;
|
||||||
|
private _readOnly: boolean;
|
||||||
|
private _tip: LinkIconTooltip;
|
||||||
|
|
||||||
|
constructor(topic: Topic, linkModel: LinkModel, readOnly: boolean) {
|
||||||
$assert(topic, 'topic can not be null');
|
$assert(topic, 'topic can not be null');
|
||||||
$assert(linkModel, 'linkModel can not be null');
|
$assert(linkModel, 'linkModel can not be null');
|
||||||
|
|
||||||
@ -34,7 +42,7 @@ class LinkIcon extends Icon {
|
|||||||
this._registerEvents();
|
this._registerEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
_registerEvents() {
|
private _registerEvents() {
|
||||||
this._image.setCursor('pointer');
|
this._image.setCursor('pointer');
|
||||||
this._tip = new LinkIconTooltip(this);
|
this._tip = new LinkIconTooltip(this);
|
||||||
|
|
||||||
@ -62,10 +70,11 @@ class LinkIcon extends Icon {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getModel() {
|
getModel(): FeatureModel {
|
||||||
return this._linksModel;
|
return this._linksModel;
|
||||||
}
|
}
|
||||||
|
static IMAGE_URL = LinksImage;
|
||||||
|
|
||||||
}
|
}
|
||||||
LinkIcon.IMAGE_URL = LinksImage;
|
|
||||||
|
|
||||||
export default LinkIcon;
|
export default LinkIcon;
|
@ -16,7 +16,6 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import { Mindmap } from '..';
|
|
||||||
import PersistenceManager from './PersistenceManager';
|
import PersistenceManager from './PersistenceManager';
|
||||||
|
|
||||||
class LocalStorageManager extends PersistenceManager {
|
class LocalStorageManager extends PersistenceManager {
|
||||||
@ -30,7 +29,8 @@ class LocalStorageManager extends PersistenceManager {
|
|||||||
this.forceLoad = forceLoad;
|
this.forceLoad = forceLoad;
|
||||||
}
|
}
|
||||||
|
|
||||||
saveMapXml(mapId: string, mapXml: string) {
|
saveMapXml(mapId: string, mapDoc: Document): void {
|
||||||
|
const mapXml = new XMLSerializer().serializeToString(mapDoc);
|
||||||
localStorage.setItem(`${mapId}-xml`, mapXml);
|
localStorage.setItem(`${mapId}-xml`, mapXml);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,7 +43,7 @@ class LocalStorageManager extends PersistenceManager {
|
|||||||
if (xml == null || this.forceLoad) {
|
if (xml == null || this.forceLoad) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: this.documentUrl.replace('{id}', mapId),
|
url: this.documentUrl.replace('{id}', mapId),
|
||||||
headers: { 'Content-Type': 'text/plain', Accept: 'application/xml' },
|
headers: { 'Content-Type': 'text/plain', Accept: 'application/xml', 'X-CSRF-Token': this.getCSRFToken() },
|
||||||
type: 'get',
|
type: 'get',
|
||||||
dataType: 'text',
|
dataType: 'text',
|
||||||
async: false,
|
async: false,
|
||||||
@ -63,7 +63,7 @@ class LocalStorageManager extends PersistenceManager {
|
|||||||
return $.parseXML(xml);
|
return $.parseXML(xml);
|
||||||
}
|
}
|
||||||
|
|
||||||
unlockMap(mindmap: Mindmap) {
|
unlockMap(): void {
|
||||||
// Ignore, no implementation required ...
|
// Ignore, no implementation required ...
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,12 +28,6 @@ import SizeType from './SizeType';
|
|||||||
class MainTopic extends Topic {
|
class MainTopic extends Topic {
|
||||||
private INNER_RECT_ATTRIBUTES: { stroke: string; };
|
private INNER_RECT_ATTRIBUTES: { stroke: string; };
|
||||||
|
|
||||||
/**
|
|
||||||
* @extends mindplot.Topic
|
|
||||||
* @constructs
|
|
||||||
* @param model
|
|
||||||
* @param options
|
|
||||||
*/
|
|
||||||
constructor(model: NodeModel, options) {
|
constructor(model: NodeModel, options) {
|
||||||
super(model, options);
|
super(model, options);
|
||||||
this.INNER_RECT_ATTRIBUTES = { stroke: '0.5 solid #009900' };
|
this.INNER_RECT_ATTRIBUTES = { stroke: '0.5 solid #009900' };
|
||||||
@ -70,6 +64,11 @@ class MainTopic extends Topic {
|
|||||||
const text = this.getText();
|
const text = this.getText();
|
||||||
textShape.setText(text);
|
textShape.setText(text);
|
||||||
textShape.setOpacity(0.5);
|
textShape.setOpacity(0.5);
|
||||||
|
|
||||||
|
// Copy text position of the topic element ...
|
||||||
|
const textPosition = this.getTextShape().getPosition();
|
||||||
|
textShape.setPosition(textPosition.x, textPosition.y);
|
||||||
|
|
||||||
group.append(textShape);
|
group.append(textShape);
|
||||||
}
|
}
|
||||||
return group;
|
return group;
|
||||||
@ -88,7 +87,6 @@ class MainTopic extends Topic {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** */
|
|
||||||
disconnect(workspace: Workspace) {
|
disconnect(workspace: Workspace) {
|
||||||
super.disconnect(workspace);
|
super.disconnect(workspace);
|
||||||
const model = this.getModel();
|
const model = this.getModel();
|
||||||
|
@ -19,21 +19,23 @@ import { $defined } from '@wisemapping/core-js';
|
|||||||
import Bundle from './lang/Bundle';
|
import Bundle from './lang/Bundle';
|
||||||
|
|
||||||
class Messages {
|
class Messages {
|
||||||
static init(locale) {
|
public static __bundle;
|
||||||
|
|
||||||
|
static init(locale: string) {
|
||||||
|
console.log(`Init designer message: ${locale}`);
|
||||||
let userLocale = $defined(locale) ? locale : 'en';
|
let userLocale = $defined(locale) ? locale : 'en';
|
||||||
let bundle = Bundle[locale];
|
let bundle = Bundle[userLocale];
|
||||||
|
|
||||||
if (bundle == null && locale.indexOf('_') !== -1) {
|
if (bundle == null && locale.indexOf('_') !== -1) {
|
||||||
// Try to locate without the specialization ...
|
// Try to locate without the specialization ...
|
||||||
userLocale = locale.substring(0, locale.indexOf('_'));
|
userLocale = locale.substring(0, locale.indexOf('_'));
|
||||||
bundle = Bundle[locale];
|
bundle = Bundle[userLocale];
|
||||||
}
|
}
|
||||||
global.locale = userLocale;
|
this.__bundle = bundle;
|
||||||
Messages.__bundle = bundle || {};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const $msg = function $msg(key) {
|
const $msg = function $msg(key: string) {
|
||||||
if (!Messages.__bundle) {
|
if (!Messages.__bundle) {
|
||||||
Messages.init('en');
|
Messages.init('en');
|
||||||
}
|
}
|
48
packages/mindplot/src/components/MockPersistenceManager.ts
Normal file
48
packages/mindplot/src/components/MockPersistenceManager.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright [2022] [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 $ from 'jquery';
|
||||||
|
import { $assert } from '@wisemapping/core-js';
|
||||||
|
import PersistenceManager from './PersistenceManager';
|
||||||
|
|
||||||
|
class MockPersistenceManager extends PersistenceManager {
|
||||||
|
private exampleMap: string;
|
||||||
|
|
||||||
|
constructor(exampleMapAsXml: string) {
|
||||||
|
super();
|
||||||
|
$assert(exampleMapAsXml, 'The test map must be set');
|
||||||
|
this.exampleMap = exampleMapAsXml;
|
||||||
|
}
|
||||||
|
|
||||||
|
saveMapXml(): void {
|
||||||
|
// Ignore, no implementation required ...
|
||||||
|
}
|
||||||
|
|
||||||
|
discardChanges() {
|
||||||
|
// Ignore, no implementation required ...
|
||||||
|
}
|
||||||
|
|
||||||
|
loadMapDom() {
|
||||||
|
return $.parseXML(this.exampleMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
unlockMap(): void {
|
||||||
|
// Ignore, no implementation required ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MockPersistenceManager;
|
@ -21,58 +21,56 @@ import $ from 'jquery';
|
|||||||
import initHotKeyPluggin from '../../../../libraries/jquery.hotkeys';
|
import initHotKeyPluggin from '../../../../libraries/jquery.hotkeys';
|
||||||
import Events from './Events';
|
import Events from './Events';
|
||||||
import ActionDispatcher from './ActionDispatcher';
|
import ActionDispatcher from './ActionDispatcher';
|
||||||
|
import Topic from './Topic';
|
||||||
|
|
||||||
initHotKeyPluggin($);
|
initHotKeyPluggin($);
|
||||||
|
|
||||||
class MultilineTextEditor extends Events {
|
class MultilineTextEditor extends Events {
|
||||||
|
private _topic: Topic;
|
||||||
|
|
||||||
|
private _containerElem: JQuery;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this._topic = null;
|
this._topic = null;
|
||||||
this._timeoutId = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static _buildEditor() {
|
private static _buildEditor() {
|
||||||
const result = $('<div></div>')
|
const result = $('<div></div>')
|
||||||
.attr('id', 'textContainer')
|
.attr('id', 'textContainer')
|
||||||
.css({
|
.css({
|
||||||
display: 'none',
|
display: 'none',
|
||||||
zIndex: '8',
|
zIndex: '8',
|
||||||
overflow: 'hidden',
|
|
||||||
border: '0 none',
|
border: '0 none',
|
||||||
});
|
});
|
||||||
|
|
||||||
const textareaElem = $('<textarea tabindex="-1" value="" wrap="off" ></textarea>')
|
const textareaElem = $('<textarea tabindex="-1" value="" wrap="off" ></textarea>')
|
||||||
.css({
|
.css({
|
||||||
border: '1px gray dashed',
|
border: '1px gray dashed',
|
||||||
background: 'rgba(98, 135, 167, .3)',
|
background: 'rgba(98, 135, 167, .4)',
|
||||||
outline: '0 none',
|
outline: '0 none',
|
||||||
resize: 'none',
|
resize: 'none',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
|
padding: '2px 0px 2px 4px',
|
||||||
});
|
});
|
||||||
|
|
||||||
result.append(textareaElem);
|
result.append(textareaElem);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
_registerEvents(containerElem) {
|
private _registerEvents(containerElem: JQuery) {
|
||||||
const textareaElem = this._getTextareaElem();
|
const textareaElem = this._getTextareaElem();
|
||||||
const me = this;
|
textareaElem.on('keydown', (event) => {
|
||||||
let start;
|
const j: any = $;
|
||||||
let end;
|
switch (j.hotkeys.specialKeys[event.keyCode]) {
|
||||||
textareaElem.on('keydown', function keydown(event) {
|
|
||||||
switch ($.hotkeys.specialKeys[event.keyCode]) {
|
|
||||||
case 'esc':
|
case 'esc':
|
||||||
me.close(false);
|
this.close(false);
|
||||||
break;
|
break;
|
||||||
case 'enter':
|
case 'enter': {
|
||||||
if (event.metaKey || event.ctrlKey) {
|
if (event.metaKey || event.ctrlKey) {
|
||||||
// Add return ...
|
// Add return ...
|
||||||
const text = textareaElem.val();
|
const text = this._getTextAreaText();
|
||||||
let cursorPosition = text.length;
|
const cursorPosition = text.length;
|
||||||
if (textareaElem.selectionStart) {
|
|
||||||
cursorPosition = textareaElem.selectionStart;
|
|
||||||
}
|
|
||||||
|
|
||||||
const head = text.substring(0, cursorPosition);
|
const head = text.substring(0, cursorPosition);
|
||||||
let tail = '';
|
let tail = '';
|
||||||
if (cursorPosition < text.length) {
|
if (cursorPosition < text.length) {
|
||||||
@ -80,31 +78,12 @@ class MultilineTextEditor extends Events {
|
|||||||
}
|
}
|
||||||
textareaElem.val(`${head}\n${tail}`);
|
textareaElem.val(`${head}\n${tail}`);
|
||||||
|
|
||||||
// Position cursor ...
|
|
||||||
if (textareaElem[0].setSelectionRange) {
|
|
||||||
textareaElem.focus();
|
textareaElem.focus();
|
||||||
textareaElem[0].setSelectionRange(cursorPosition + 1, cursorPosition + 1);
|
textareaElem[0].setSelectionRange(cursorPosition + 1, cursorPosition + 1);
|
||||||
} else if (textareaElem.createTextRange) {
|
|
||||||
const range = textareaElem.createTextRange();
|
|
||||||
range.moveStart('character', cursorPosition + 1);
|
|
||||||
range.select();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
me.close(true);
|
this.close(true);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'tab': {
|
|
||||||
event.preventDefault();
|
|
||||||
start = $(this).get(0).selectionStart;
|
|
||||||
end = $(this).get(0).selectionEnd;
|
|
||||||
|
|
||||||
// set textarea value to: text before caret + tab + text after caret
|
|
||||||
$(this).val(`${$(this).val().substring(0, start)}\t${$(this).val().substring(end)}`);
|
|
||||||
|
|
||||||
// put caret at right position again
|
|
||||||
$(this).get(0).selectionEnd = start + 1;
|
|
||||||
$(this).get(0).selectionStart = $(this).get(0).selectionEnd;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
// No actions...
|
// No actions...
|
||||||
@ -118,9 +97,9 @@ class MultilineTextEditor extends Events {
|
|||||||
});
|
});
|
||||||
|
|
||||||
textareaElem.on('keyup', (event) => {
|
textareaElem.on('keyup', (event) => {
|
||||||
const text = me._getTextareaElem().val();
|
const text = this._getTextareaElem().val();
|
||||||
me.fireEvent('input', [event, text]);
|
this.fireEvent('input', [event, text]);
|
||||||
me._adjustEditorSize();
|
this._adjustEditorSize();
|
||||||
});
|
});
|
||||||
|
|
||||||
// If the user clicks on the input, all event must be ignored ...
|
// If the user clicks on the input, all event must be ignored ...
|
||||||
@ -135,33 +114,33 @@ class MultilineTextEditor extends Events {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_adjustEditorSize() {
|
private _adjustEditorSize() {
|
||||||
if (this.isVisible()) {
|
if (this.isVisible()) {
|
||||||
const textElem = this._getTextareaElem();
|
const textElem = this._getTextareaElem();
|
||||||
|
|
||||||
const lines = textElem.val().split('\n');
|
const lines = this._getTextAreaText().split('\n');
|
||||||
let maxLineLength = 1;
|
let maxLineLength = 1;
|
||||||
lines.forEach((line) => {
|
lines.forEach((line: string) => {
|
||||||
if (maxLineLength < line.length) maxLineLength = line.length;
|
maxLineLength = Math.max(line.length, maxLineLength);
|
||||||
});
|
});
|
||||||
|
|
||||||
textElem.attr('cols', maxLineLength);
|
textElem.attr('cols', maxLineLength);
|
||||||
textElem.attr('rows', lines.length);
|
textElem.attr('rows', lines.length);
|
||||||
|
|
||||||
this._containerElem.css({
|
this._containerElem.css({
|
||||||
width: `${maxLineLength + 3}em`,
|
width: `${maxLineLength + 2}em`,
|
||||||
height: textElem.height(),
|
height: textElem.height(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isVisible() {
|
isVisible(): boolean {
|
||||||
return $defined(this._containerElem) && this._containerElem.css('display') === 'block';
|
return $defined(this._containerElem) && this._containerElem.css('display') === 'block';
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateModel() {
|
private _updateModel() {
|
||||||
if (this._topic.getText() !== this._getText()) {
|
if (this._topic.getText() !== this._getTextAreaText()) {
|
||||||
const text = this._getText();
|
const text = this._getTextAreaText();
|
||||||
const topicId = this._topic.getId();
|
const topicId = this._topic.getId();
|
||||||
|
|
||||||
const actionDispatcher = ActionDispatcher.getInstance();
|
const actionDispatcher = ActionDispatcher.getInstance();
|
||||||
@ -169,7 +148,7 @@ class MultilineTextEditor extends Events {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
show(topic, text) {
|
show(topic: Topic, text: string): void {
|
||||||
// Close a previous node editor if it's opened ...
|
// Close a previous node editor if it's opened ...
|
||||||
if (this._topic) {
|
if (this._topic) {
|
||||||
this.close(false);
|
this.close(false);
|
||||||
@ -187,7 +166,7 @@ class MultilineTextEditor extends Events {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_showEditor(defaultText) {
|
private _showEditor(defaultText: string) {
|
||||||
const topic = this._topic;
|
const topic = this._topic;
|
||||||
|
|
||||||
// Hide topic text ...
|
// Hide topic text ...
|
||||||
@ -199,32 +178,29 @@ class MultilineTextEditor extends Events {
|
|||||||
fontStyle.size = nodeText.getHtmlFontSize();
|
fontStyle.size = nodeText.getHtmlFontSize();
|
||||||
fontStyle.color = nodeText.getColor();
|
fontStyle.color = nodeText.getColor();
|
||||||
this._setStyle(fontStyle);
|
this._setStyle(fontStyle);
|
||||||
const me = this;
|
|
||||||
|
|
||||||
// Set editor's initial size
|
// Set editor's initial size
|
||||||
const displayFunc = function displayFunc() {
|
|
||||||
// Position the editor and set the size...
|
// Position the editor and set the size...
|
||||||
const textShape = topic.getTextShape();
|
const textShape = topic.getTextShape();
|
||||||
|
|
||||||
me._containerElem.css('display', 'block');
|
this._containerElem.css('display', 'block');
|
||||||
|
|
||||||
// FIXME: Im not sure if this is best way...
|
let { top, left } = textShape.getNativePosition();
|
||||||
const shapePosition = textShape.getNativePosition();
|
// Adjust padding top position ...
|
||||||
me._containerElem.offset(shapePosition);
|
top -= 4;
|
||||||
|
left -= 4;
|
||||||
|
this._containerElem.offset({ top, left });
|
||||||
|
|
||||||
// Set editor's initial text ...
|
// Set editor's initial text ...
|
||||||
const text = $defined(defaultText) ? defaultText : topic.getText();
|
const text = $defined(defaultText) ? defaultText : topic.getText();
|
||||||
me._setText(text);
|
this._setText(text);
|
||||||
|
|
||||||
// Set the element focus and select the current text ...
|
// Set the element focus and select the current text ...
|
||||||
const inputElem = me._getTextareaElem();
|
const inputElem = this._getTextareaElem();
|
||||||
me._positionCursor(inputElem, !$defined(defaultText));
|
this._positionCursor(inputElem, !$defined(defaultText));
|
||||||
};
|
|
||||||
|
|
||||||
this._timeoutId = setTimeout(() => displayFunc(), 10);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_setStyle(fontStyle) {
|
private _setStyle(fontStyle) {
|
||||||
const inputField = this._getTextareaElem();
|
const inputField = this._getTextareaElem();
|
||||||
// allowed param reassign to avoid risks of existing code relying in this side-effect
|
// allowed param reassign to avoid risks of existing code relying in this side-effect
|
||||||
/* eslint-disable no-param-reassign */
|
/* eslint-disable no-param-reassign */
|
||||||
@ -252,65 +228,46 @@ class MultilineTextEditor extends Events {
|
|||||||
this._containerElem.css(style);
|
this._containerElem.css(style);
|
||||||
}
|
}
|
||||||
|
|
||||||
_setText(text) {
|
private _setText(text: string): void {
|
||||||
const textareaElem = this._getTextareaElem();
|
const textareaElem = this._getTextareaElem();
|
||||||
textareaElem.val(text);
|
textareaElem.val(text);
|
||||||
this._adjustEditorSize();
|
this._adjustEditorSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
_getText() {
|
private _getTextAreaText(): string {
|
||||||
return this._getTextareaElem().val();
|
return this._getTextareaElem().val() as string;
|
||||||
}
|
}
|
||||||
|
|
||||||
_getTextareaElem() {
|
private _getTextareaElem(): JQuery<HTMLTextAreaElement> {
|
||||||
return this._containerElem.find('textarea');
|
return this._containerElem.find('textarea');
|
||||||
}
|
}
|
||||||
|
|
||||||
_positionCursor(textareaElem, selectText) {
|
private _positionCursor(textareaElem: JQuery<HTMLTextAreaElement>, selectText: boolean) {
|
||||||
textareaElem.focus();
|
textareaElem.focus();
|
||||||
const lengh = textareaElem.val().length;
|
const lengh = this._getTextAreaText().length;
|
||||||
if (selectText) {
|
if (selectText) {
|
||||||
// Mark text as selected ...
|
// Mark text as selected ...
|
||||||
if (textareaElem.createTextRange) {
|
|
||||||
const rang = textareaElem.createTextRange();
|
|
||||||
rang.select();
|
|
||||||
rang.move('character', lengh);
|
|
||||||
} else {
|
|
||||||
textareaElem[0].setSelectionRange(0, lengh);
|
textareaElem[0].setSelectionRange(0, lengh);
|
||||||
}
|
|
||||||
} else if (textareaElem.createTextRange) {
|
|
||||||
const range = textareaElem.createTextRange();
|
|
||||||
range.move('character', lengh);
|
|
||||||
} else {
|
} else {
|
||||||
// allowed param reassign to avoid risks of existing code relying in this side-effect
|
|
||||||
/* eslint-disable no-param-reassign */
|
|
||||||
textareaElem.selectionStart = lengh;
|
|
||||||
textareaElem.selectionEnd = lengh;
|
|
||||||
/* eslint-enable no-param-reassign */
|
|
||||||
|
|
||||||
textareaElem.focus();
|
textareaElem.focus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
close(update) {
|
close(update: boolean): void {
|
||||||
if (this.isVisible() && this._topic) {
|
if (this.isVisible()) {
|
||||||
// Update changes ...
|
if (update) {
|
||||||
clearTimeout(this._timeoutId);
|
|
||||||
|
|
||||||
if (!$defined(update) || update) {
|
|
||||||
this._updateModel();
|
this._updateModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Let make the visible text in the node visible again ...
|
|
||||||
this._topic.getTextShape().setVisibility(true);
|
|
||||||
|
|
||||||
// Remove it form the screen ...
|
// Remove it form the screen ...
|
||||||
this._containerElem.remove();
|
this._containerElem.remove();
|
||||||
this._containerElem = null;
|
this._containerElem = null;
|
||||||
this._timeoutId = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this._topic) {
|
||||||
|
this._topic.getTextShape().setVisibility(true);
|
||||||
this._topic = null;
|
this._topic = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
export default MultilineTextEditor;
|
export default MultilineTextEditor;
|
@ -16,14 +16,13 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { $assert } from '@wisemapping/core-js';
|
import { $assert } from '@wisemapping/core-js';
|
||||||
import { ElementClass } from '@wisemapping/web2d';
|
import { ElementClass, Point } from '@wisemapping/web2d';
|
||||||
import TopicConfig from './TopicConfig';
|
import TopicConfig from './TopicConfig';
|
||||||
import NodeModel from './model/NodeModel';
|
import NodeModel from './model/NodeModel';
|
||||||
import Workspace from './Workspace';
|
import Workspace from './Workspace';
|
||||||
import DragTopic from './DragTopic';
|
import DragTopic from './DragTopic';
|
||||||
import LayoutManager from './layout/LayoutManager';
|
import LayoutManager from './layout/LayoutManager';
|
||||||
import SizeType from './SizeType';
|
import SizeType from './SizeType';
|
||||||
import PositionType from './PositionType';
|
|
||||||
|
|
||||||
abstract class NodeGraph {
|
abstract class NodeGraph {
|
||||||
private _mouseEvents: boolean;
|
private _mouseEvents: boolean;
|
||||||
@ -105,7 +104,7 @@ abstract class NodeGraph {
|
|||||||
return this._size;
|
return this._size;
|
||||||
}
|
}
|
||||||
|
|
||||||
setSize(size: SizeType, force?: boolean) {
|
setSize(size: SizeType) {
|
||||||
this._size.width = size.width;
|
this._size.width = size.width;
|
||||||
this._size.height = size.height;
|
this._size.height = size.height;
|
||||||
}
|
}
|
||||||
@ -160,15 +159,15 @@ abstract class NodeGraph {
|
|||||||
workspace.removeChild(this);
|
workspace.removeChild(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** */
|
|
||||||
createDragNode(layoutManager: LayoutManager) {
|
createDragNode(layoutManager: LayoutManager) {
|
||||||
const dragShape = this._buildDragShape();
|
const dragShape = this._buildDragShape();
|
||||||
|
|
||||||
return new DragTopic(dragShape, this, layoutManager);
|
return new DragTopic(dragShape, this, layoutManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract _buildDragShape();
|
abstract _buildDragShape();
|
||||||
|
|
||||||
getPosition(): PositionType {
|
getPosition(): Point {
|
||||||
const model = this.getModel();
|
const model = this.getModel();
|
||||||
return model.getPosition();
|
return model.getPosition();
|
||||||
}
|
}
|
||||||
|
@ -21,9 +21,17 @@ import { $msg } from './Messages';
|
|||||||
import Icon from './Icon';
|
import Icon from './Icon';
|
||||||
import FloatingTip from './widget/FloatingTip';
|
import FloatingTip from './widget/FloatingTip';
|
||||||
import NotesImage from '../../assets/icons/notes.svg';
|
import NotesImage from '../../assets/icons/notes.svg';
|
||||||
|
import Topic from './Topic';
|
||||||
|
import NoteModel from './model/NoteModel';
|
||||||
|
import FeatureModel from './model/FeatureModel';
|
||||||
|
|
||||||
class NoteIcon extends Icon {
|
class NoteIcon extends Icon {
|
||||||
constructor(topic, noteModel, readOnly) {
|
private _linksModel: NoteModel;
|
||||||
|
private _topic: Topic;
|
||||||
|
private _readOnly: boolean;
|
||||||
|
private _tip: FloatingTip;
|
||||||
|
|
||||||
|
constructor(topic: Topic, noteModel: NoteModel, readOnly: boolean) {
|
||||||
$assert(topic, 'topic can not be null');
|
$assert(topic, 'topic can not be null');
|
||||||
|
|
||||||
super(NoteIcon.IMAGE_URL);
|
super(NoteIcon.IMAGE_URL);
|
||||||
@ -34,7 +42,7 @@ class NoteIcon extends Icon {
|
|||||||
this._registerEvents();
|
this._registerEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
_registerEvents() {
|
private _registerEvents(): void {
|
||||||
this._image.setCursor('pointer');
|
this._image.setCursor('pointer');
|
||||||
const me = this;
|
const me = this;
|
||||||
|
|
||||||
@ -58,7 +66,7 @@ class NoteIcon extends Icon {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_buildTooltipContent() {
|
private _buildTooltipContent(): JQuery {
|
||||||
if ($('body').find('#textPopoverNote').length === 1) {
|
if ($('body').find('#textPopoverNote').length === 1) {
|
||||||
const text = $('body').find('#textPopoverNote');
|
const text = $('body').find('#textPopoverNote');
|
||||||
text.text(this._linksModel.getText());
|
text.text(this._linksModel.getText());
|
||||||
@ -75,11 +83,12 @@ class NoteIcon extends Icon {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
getModel() {
|
getModel(): FeatureModel {
|
||||||
return this._linksModel;
|
return this._linksModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static IMAGE_URL = NotesImage;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NoteIcon.IMAGE_URL = NotesImage;
|
|
||||||
|
|
||||||
export default NoteIcon;
|
export default NoteIcon;
|
@ -20,11 +20,21 @@ import { $assert } from '@wisemapping/core-js';
|
|||||||
import { Mindmap } from '..';
|
import { Mindmap } from '..';
|
||||||
import XMLSerializerFactory from './persistence/XMLSerializerFactory';
|
import XMLSerializerFactory from './persistence/XMLSerializerFactory';
|
||||||
|
|
||||||
|
export type PersistenceError = {
|
||||||
|
severity: string;
|
||||||
|
message: string;
|
||||||
|
errorType?: 'session-expired' | 'bad-request' | 'generic';
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PersistenceErrorCallback = (error: PersistenceError) => void;
|
||||||
|
|
||||||
abstract class PersistenceManager {
|
abstract class PersistenceManager {
|
||||||
// eslint-disable-next-line no-use-before-define
|
// eslint-disable-next-line no-use-before-define
|
||||||
static _instance: PersistenceManager;
|
static _instance: PersistenceManager;
|
||||||
|
|
||||||
save(mindmap: Mindmap, editorProperties, saveHistory: boolean, events, sync: boolean) {
|
private _errorHandlers: PersistenceErrorCallback[] = [];
|
||||||
|
|
||||||
|
save(mindmap: Mindmap, editorProperties, saveHistory: boolean, events?) {
|
||||||
$assert(mindmap, 'mindmap can not be null');
|
$assert(mindmap, 'mindmap can not be null');
|
||||||
$assert(editorProperties, 'editorProperties can not be null');
|
$assert(editorProperties, 'editorProperties can not be null');
|
||||||
|
|
||||||
@ -33,30 +43,55 @@ abstract class PersistenceManager {
|
|||||||
|
|
||||||
const serializer = XMLSerializerFactory.createInstanceFromMindmap(mindmap);
|
const serializer = XMLSerializerFactory.createInstanceFromMindmap(mindmap);
|
||||||
const domMap = serializer.toXML(mindmap);
|
const domMap = serializer.toXML(mindmap);
|
||||||
const mapXml = new XMLSerializer().serializeToString(domMap);
|
|
||||||
|
|
||||||
const pref = JSON.stringify(editorProperties);
|
const pref = JSON.stringify(editorProperties);
|
||||||
try {
|
try {
|
||||||
this.saveMapXml(mapId, mapXml, pref, saveHistory, events, sync);
|
this.saveMapXml(mapId, domMap, pref, saveHistory, events);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
events.onError(e);
|
events.onError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected getCSRFToken(): string | null {
|
||||||
|
const meta = document.head.querySelector('meta[name="_csrf"]');
|
||||||
|
let result = null;
|
||||||
|
if (meta) {
|
||||||
|
result = meta.getAttribute('content');
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
load(mapId: string) {
|
load(mapId: string) {
|
||||||
$assert(mapId, 'mapId can not be null');
|
$assert(mapId, 'mapId can not be null');
|
||||||
const domDocument = this.loadMapDom(mapId);
|
const domDocument = this.loadMapDom(mapId);
|
||||||
return PersistenceManager.loadFromDom(mapId, domDocument);
|
return PersistenceManager.loadFromDom(mapId, domDocument);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
triggerError(error: PersistenceError) {
|
||||||
|
this._errorHandlers.forEach((handler) => handler(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
addErrorHandler(callback: PersistenceErrorCallback) {
|
||||||
|
this._errorHandlers.push(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeErrorHandler(callback?: PersistenceErrorCallback) {
|
||||||
|
if (!callback) {
|
||||||
|
this._errorHandlers.length = 0;
|
||||||
|
}
|
||||||
|
const index = this._errorHandlers.findIndex((handler) => handler === callback);
|
||||||
|
if (index !== -1) {
|
||||||
|
this._errorHandlers.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
abstract discardChanges(mapId: string): void;
|
abstract discardChanges(mapId: string): void;
|
||||||
|
|
||||||
abstract loadMapDom(mapId: string): Document;
|
abstract loadMapDom(mapId: string): Document;
|
||||||
|
|
||||||
abstract saveMapXml(mapId: string, mapXml, pref, saveHistory, events, sync);
|
abstract saveMapXml(mapId: string, mapXml: Document, pref?, saveHistory?: boolean, events?);
|
||||||
|
|
||||||
abstract unlockMap(mindmap: Mindmap): void;
|
abstract unlockMap(mapId: string): void;
|
||||||
|
|
||||||
static init = (instance: PersistenceManager) => {
|
static init = (instance: PersistenceManager) => {
|
||||||
this._instance = instance;
|
this._instance = instance;
|
||||||
|
@ -20,7 +20,7 @@ import { Arrow, Point, ElementClass } from '@wisemapping/web2d';
|
|||||||
import ConnectionLine from './ConnectionLine';
|
import ConnectionLine from './ConnectionLine';
|
||||||
import ControlPoint from './ControlPoint';
|
import ControlPoint from './ControlPoint';
|
||||||
import RelationshipModel from './model/RelationshipModel';
|
import RelationshipModel from './model/RelationshipModel';
|
||||||
import NodeGraph from './NodeGraph';
|
import Topic from './Topic';
|
||||||
import Shape from './util/Shape';
|
import Shape from './util/Shape';
|
||||||
|
|
||||||
class Relationship extends ConnectionLine {
|
class Relationship extends ConnectionLine {
|
||||||
@ -38,11 +38,11 @@ class Relationship extends ConnectionLine {
|
|||||||
|
|
||||||
private _endArrow: Arrow;
|
private _endArrow: Arrow;
|
||||||
|
|
||||||
private _controlPointControllerListener: any;
|
private _controlPointControllerListener;
|
||||||
|
|
||||||
private _showStartArrow: Arrow;
|
private _showStartArrow: Arrow;
|
||||||
|
|
||||||
constructor(sourceNode: NodeGraph, targetNode: NodeGraph, model: RelationshipModel) {
|
constructor(sourceNode: Topic, targetNode: Topic, model: RelationshipModel) {
|
||||||
$assert(sourceNode, 'sourceNode can not be null');
|
$assert(sourceNode, 'sourceNode can not be null');
|
||||||
$assert(targetNode, 'targetNode can not be null');
|
$assert(targetNode, 'targetNode can not be null');
|
||||||
|
|
||||||
@ -349,7 +349,7 @@ class Relationship extends ConnectionLine {
|
|||||||
return this._model.getId();
|
return this._model.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
fireEvent(type: string, event: any): void {
|
fireEvent(type: string, event): void {
|
||||||
const elem = this._line2d;
|
const elem = this._line2d;
|
||||||
elem.trigger(type, event);
|
elem.trigger(type, event);
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ class RelationshipPivot {
|
|||||||
|
|
||||||
private _sourceTopic: Topic;
|
private _sourceTopic: Topic;
|
||||||
|
|
||||||
private _pivot: any;
|
private _pivot: CurvedLine;
|
||||||
|
|
||||||
private _startArrow: Arrow;
|
private _startArrow: Arrow;
|
||||||
|
|
||||||
|
@ -17,9 +17,8 @@
|
|||||||
*/
|
*/
|
||||||
import { $assert } from '@wisemapping/core-js';
|
import { $assert } from '@wisemapping/core-js';
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import { Mindmap } from '..';
|
|
||||||
import { $msg } from './Messages';
|
import { $msg } from './Messages';
|
||||||
import PersistenceManager from './PersistenceManager';
|
import PersistenceManager, { PersistenceError } from './PersistenceManager';
|
||||||
|
|
||||||
class RESTPersistenceManager extends PersistenceManager {
|
class RESTPersistenceManager extends PersistenceManager {
|
||||||
private documentUrl: string;
|
private documentUrl: string;
|
||||||
@ -51,10 +50,10 @@ class RESTPersistenceManager extends PersistenceManager {
|
|||||||
this.session = options.session;
|
this.session = options.session;
|
||||||
}
|
}
|
||||||
|
|
||||||
saveMapXml(mapId: string, mapXml: Document, pref: string, saveHistory: boolean, events, sync: boolean): void {
|
saveMapXml(mapId: string, mapXml: Document, pref: string, saveHistory: boolean, events): void {
|
||||||
const data = {
|
const data = {
|
||||||
id: mapId,
|
id: mapId,
|
||||||
xml: mapXml,
|
xml: new XMLSerializer().serializeToString(mapXml),
|
||||||
properties: pref,
|
properties: pref,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -71,73 +70,79 @@ class RESTPersistenceManager extends PersistenceManager {
|
|||||||
}, 10000);
|
}, 10000);
|
||||||
|
|
||||||
const persistence = this;
|
const persistence = this;
|
||||||
$.ajax({
|
fetch(
|
||||||
type: 'put',
|
`${this.documentUrl.replace('{id}', mapId)}?${query}`,
|
||||||
url: `${this.documentUrl.replace('{id}', mapId)}?${query}`,
|
{
|
||||||
dataType: 'json',
|
method: 'PUT',
|
||||||
data: JSON.stringify(data),
|
// Blob helps to resuce the memory on large payload.
|
||||||
contentType: 'application/json; charset=utf-8',
|
body: new Blob([JSON.stringify(data)], { type: 'text/plain' }),
|
||||||
async: !sync,
|
headers: { 'Content-Type': 'application/json; charset=utf-8', Accept: 'application/json', 'X-CSRF-Token': this.getCSRFToken() },
|
||||||
|
},
|
||||||
success(successData) {
|
).then(async (response: Response) => {
|
||||||
persistence.timestamp = successData;
|
if (response.ok) {
|
||||||
|
persistence.timestamp = await response.text();
|
||||||
events.onSuccess();
|
events.onSuccess();
|
||||||
},
|
} else {
|
||||||
complete() {
|
console.log(`Saving error: ${response.status}`);
|
||||||
// Clear event timeout ...
|
let userMsg;
|
||||||
if (persistence.clearTimeout) {
|
if (response.status === 405) {
|
||||||
clearTimeout(persistence.clearTimeout);
|
userMsg = { severity: 'SEVERE', message: $msg('SESSION_EXPIRED'), errorType: 'session-expired' };
|
||||||
}
|
} else {
|
||||||
persistence.onSave = false;
|
const responseText = await response.text();
|
||||||
},
|
const contentType = response.headers['Content-Type'];
|
||||||
error(xhr) {
|
|
||||||
const { responseText } = xhr;
|
|
||||||
let userMsg = { severity: 'SEVERE', message: $msg('SAVE_COULD_NOT_BE_COMPLETED') };
|
|
||||||
|
|
||||||
const contentType = xhr.getResponseHeader('Content-Type');
|
|
||||||
if (contentType != null && contentType.indexOf('application/json') !== -1) {
|
if (contentType != null && contentType.indexOf('application/json') !== -1) {
|
||||||
let serverMsg = null;
|
let serverMsg = null;
|
||||||
try {
|
try {
|
||||||
serverMsg = $.parseJSON(responseText);
|
serverMsg = JSON.parse(responseText);
|
||||||
serverMsg = serverMsg.globalSeverity ? serverMsg : null;
|
serverMsg = serverMsg.globalSeverity ? serverMsg : null;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Message could not be decoded ...
|
// Message could not be decoded ...
|
||||||
}
|
}
|
||||||
userMsg = persistence._buildError(serverMsg);
|
userMsg = persistence._buildError(serverMsg);
|
||||||
} else if (this.status === 405) {
|
|
||||||
userMsg = { severity: 'SEVERE', message: $msg('SESSION_EXPIRED') };
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
this.triggerError(userMsg);
|
||||||
events.onError(userMsg);
|
events.onError(userMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear event timeout ...
|
||||||
|
if (persistence.clearTimeout) {
|
||||||
|
clearTimeout(persistence.clearTimeout);
|
||||||
|
}
|
||||||
|
persistence.onSave = false;
|
||||||
|
}).catch(() => {
|
||||||
|
const userMsg: PersistenceError = {
|
||||||
|
severity: 'SEVERE', message: $msg('SAVE_COULD_NOT_BE_COMPLETED'), errorType: 'generic',
|
||||||
|
};
|
||||||
|
this.triggerError(userMsg);
|
||||||
|
events.onError(userMsg);
|
||||||
|
|
||||||
|
// Clear event timeout ...
|
||||||
|
if (persistence.clearTimeout) {
|
||||||
|
clearTimeout(persistence.clearTimeout);
|
||||||
|
}
|
||||||
persistence.onSave = false;
|
persistence.onSave = false;
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
discardChanges(mapId: string) {
|
discardChanges(mapId: string) {
|
||||||
$.ajax({
|
fetch(this.revertUrl.replace('{id}', mapId),
|
||||||
url: this.revertUrl.replace('{id}', mapId),
|
{
|
||||||
async: false,
|
method: 'POST',
|
||||||
method: 'post',
|
headers: { 'Content-Type': 'application/json; charset=utf-8', Accept: 'application/json', 'X-CSRF-Token': this.getCSRFToken() },
|
||||||
headers: { 'Content-Type': 'application/json; charset=utf-8', Accept: 'application/json' },
|
|
||||||
error(xhr, ajaxOptions, thrownError) {
|
|
||||||
console.error(`Request error => status:${xhr.status} ,thrownError: ${thrownError}`);
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
unlockMap(mindmap: Mindmap) {
|
unlockMap(mapId: string): void {
|
||||||
const mapId = mindmap.getId();
|
fetch(
|
||||||
$.ajax({
|
this.lockUrl.replace('{id}', mapId),
|
||||||
url: this.lockUrl.replace('{id}', mapId),
|
{
|
||||||
async: false,
|
method: 'PUT',
|
||||||
method: 'put',
|
headers: { 'Content-Type': 'text/plain', 'X-CSRF-Token': this.getCSRFToken() },
|
||||||
headers: { 'Content-Type': 'text/plain' },
|
body: 'false',
|
||||||
data: 'false',
|
|
||||||
error(xhr, ajaxOptions, thrownError) {
|
|
||||||
console.error(`Request error => status:${xhr.status} ,thrownError: ${thrownError}`);
|
|
||||||
},
|
},
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _buildError(jsonSeverResponse) {
|
private _buildError(jsonSeverResponse) {
|
||||||
@ -155,13 +160,12 @@ class RESTPersistenceManager extends PersistenceManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
loadMapDom(mapId: string): Document {
|
loadMapDom(mapId: string): Document {
|
||||||
// Let's try to open one from the local directory ...
|
|
||||||
let xml: Document;
|
let xml: Document;
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: `${this.documentUrl.replace('{id}', mapId)}/xml`,
|
url: `${this.documentUrl.replace('{id}', mapId)}/xml`,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
async: false,
|
async: false,
|
||||||
headers: { 'Content-Type': 'text/plain', Accept: 'application/xml' },
|
headers: { 'Content-Type': 'text/plain', Accept: 'application/xml', 'X-CSRF-Token': this.getCSRFToken() },
|
||||||
success(responseText) {
|
success(responseText) {
|
||||||
xml = responseText;
|
xml = responseText;
|
||||||
},
|
},
|
||||||
|
@ -17,8 +17,6 @@
|
|||||||
*/
|
*/
|
||||||
import { $assert } from '@wisemapping/core-js';
|
import { $assert } from '@wisemapping/core-js';
|
||||||
import { Point } from '@wisemapping/web2d';
|
import { Point } from '@wisemapping/web2d';
|
||||||
import Icon from './Icon';
|
|
||||||
import Topic from './Topic';
|
|
||||||
|
|
||||||
class ScreenManager {
|
class ScreenManager {
|
||||||
private _divContainer: JQuery;
|
private _divContainer: JQuery;
|
||||||
@ -84,59 +82,6 @@ class ScreenManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getElementPosition(elem: Topic) {
|
|
||||||
// Retrieve current element position.
|
|
||||||
const elementPosition = elem.getPosition();
|
|
||||||
let { x } = elementPosition;
|
|
||||||
let { y } = elementPosition;
|
|
||||||
|
|
||||||
// Add workspace offset.
|
|
||||||
x -= this._padding.x;
|
|
||||||
y -= this._padding.y;
|
|
||||||
|
|
||||||
// Scale coordinate in order to be relative to the workspace. That's coord/size;
|
|
||||||
x /= this._scale;
|
|
||||||
y /= this._scale;
|
|
||||||
|
|
||||||
// Remove decimal part..
|
|
||||||
return { x, y };
|
|
||||||
}
|
|
||||||
|
|
||||||
getWorkspaceIconPosition(e: Icon) {
|
|
||||||
// Retrieve current icon position.
|
|
||||||
const image = e.getImage();
|
|
||||||
const elementPosition = image.getPosition();
|
|
||||||
const imageSize = e.getSize();
|
|
||||||
|
|
||||||
// Add group offset
|
|
||||||
const iconGroup = e.getGroup();
|
|
||||||
const group = iconGroup.getNativeElement();
|
|
||||||
const coordOrigin = group.getCoordOrigin();
|
|
||||||
const groupSize = group.getSize();
|
|
||||||
const coordSize = group.getCoordSize();
|
|
||||||
|
|
||||||
const scale = {
|
|
||||||
x: coordSize.width / parseInt(groupSize.width, 10),
|
|
||||||
y: coordSize.height / parseInt(groupSize.height, 10),
|
|
||||||
};
|
|
||||||
|
|
||||||
let x = (elementPosition.x - coordOrigin.x - parseInt(imageSize.width, 10) / 2) / scale.x;
|
|
||||||
let y = (elementPosition.y - coordOrigin.y - parseInt(imageSize.height, 10) / 2) / scale.y;
|
|
||||||
|
|
||||||
// Retrieve iconGroup Position
|
|
||||||
const groupPosition = iconGroup.getPosition();
|
|
||||||
x += groupPosition.x;
|
|
||||||
y += groupPosition.y;
|
|
||||||
|
|
||||||
// Retrieve topic Position
|
|
||||||
const topic = iconGroup.getTopic();
|
|
||||||
const topicPosition = this._getElementPosition(topic);
|
|
||||||
topicPosition.x -= parseInt(topic.getSize().width, 10) / 2;
|
|
||||||
|
|
||||||
// Remove decimal part..
|
|
||||||
return { x: x + topicPosition.x, y: y + topicPosition.y };
|
|
||||||
}
|
|
||||||
|
|
||||||
getWorkspaceMousePosition(event: MouseEvent) {
|
getWorkspaceMousePosition(event: MouseEvent) {
|
||||||
// Retrieve current mouse position.
|
// Retrieve current mouse position.
|
||||||
let x = event.clientX;
|
let x = event.clientX;
|
||||||
|
@ -79,11 +79,11 @@ class StandaloneActionDispatcher extends ActionDispatcher {
|
|||||||
$assert($defined(topicId), 'topicsId can not be null');
|
$assert($defined(topicId), 'topicsId can not be null');
|
||||||
$assert($defined(position), 'position can not be null');
|
$assert($defined(position), 'position can not be null');
|
||||||
|
|
||||||
const commandFunc = (topic, value) => {
|
const commandFunc = (topic: Topic, pos: Point) => {
|
||||||
const result = topic.getPosition();
|
const result = topic.getPosition();
|
||||||
EventBus.instance.fireEvent(EventBus.events.NodeMoveEvent, {
|
EventBus.instance.fireEvent(EventBus.events.NodeMoveEvent, {
|
||||||
node: topic.getModel(),
|
node: topic.getModel(),
|
||||||
position: value,
|
position: pos,
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
@ -114,7 +114,7 @@ class StandaloneActionDispatcher extends ActionDispatcher {
|
|||||||
changeTextToTopic(topicsIds: number[], text: string) {
|
changeTextToTopic(topicsIds: number[], text: string) {
|
||||||
$assert($defined(topicsIds), 'topicsIds can not be null');
|
$assert($defined(topicsIds), 'topicsIds can not be null');
|
||||||
|
|
||||||
const commandFunc = (topic: Topic, value: object) => {
|
const commandFunc = (topic: Topic, value: string) => {
|
||||||
const result = topic.getText();
|
const result = topic.getText();
|
||||||
topic.setText(value);
|
topic.setText(value);
|
||||||
return result;
|
return result;
|
||||||
@ -163,7 +163,7 @@ class StandaloneActionDispatcher extends ActionDispatcher {
|
|||||||
$assert(topicsIds, 'topicIds can not be null');
|
$assert(topicsIds, 'topicIds can not be null');
|
||||||
$assert(color, 'color can not be null');
|
$assert(color, 'color can not be null');
|
||||||
|
|
||||||
const commandFunc = (topic, commandColor) => {
|
const commandFunc = (topic: Topic, commandColor: string) => {
|
||||||
const result = topic.getBackgroundColor();
|
const result = topic.getBackgroundColor();
|
||||||
topic.setBackgroundColor(commandColor);
|
topic.setBackgroundColor(commandColor);
|
||||||
return result;
|
return result;
|
||||||
@ -179,7 +179,7 @@ class StandaloneActionDispatcher extends ActionDispatcher {
|
|||||||
$assert(topicsIds, 'topicIds can not be null');
|
$assert(topicsIds, 'topicIds can not be null');
|
||||||
$assert(color, 'topicIds can not be null');
|
$assert(color, 'topicIds can not be null');
|
||||||
|
|
||||||
const commandFunc = (topic, commandColor) => {
|
const commandFunc = (topic: Topic, commandColor: string) => {
|
||||||
const result = topic.getBorderColor();
|
const result = topic.getBorderColor();
|
||||||
topic.setBorderColor(commandColor);
|
topic.setBorderColor(commandColor);
|
||||||
return result;
|
return result;
|
||||||
@ -195,11 +195,11 @@ class StandaloneActionDispatcher extends ActionDispatcher {
|
|||||||
$assert(topicsIds, 'topicIds can not be null');
|
$assert(topicsIds, 'topicIds can not be null');
|
||||||
$assert(size, 'size can not be null');
|
$assert(size, 'size can not be null');
|
||||||
|
|
||||||
const commandFunc = (topic, commandSize) => {
|
const commandFunc = (topic: Topic, commandSize: number) => {
|
||||||
const result = topic.getFontSize();
|
const result = topic.getFontSize();
|
||||||
topic.setFontSize(commandSize, true);
|
topic.setFontSize(commandSize, true);
|
||||||
|
|
||||||
topic._adjustShapes();
|
topic.adjustShapes();
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -212,9 +212,9 @@ class StandaloneActionDispatcher extends ActionDispatcher {
|
|||||||
$assert(topicsIds, 'topicsIds can not be null');
|
$assert(topicsIds, 'topicsIds can not be null');
|
||||||
$assert(shapeType, 'shapeType can not be null');
|
$assert(shapeType, 'shapeType can not be null');
|
||||||
|
|
||||||
const commandFunc = (topic, commandShapeType) => {
|
const commandFunc = (topic: Topic, commandShapeType: string) => {
|
||||||
const result = topic.getShapeType();
|
const result = topic.getShapeType();
|
||||||
topic.setShapeType(commandShapeType, true);
|
topic.setShapeType(commandShapeType);
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -226,12 +226,12 @@ class StandaloneActionDispatcher extends ActionDispatcher {
|
|||||||
changeFontWeightToTopic(topicsIds: number[]) {
|
changeFontWeightToTopic(topicsIds: number[]) {
|
||||||
$assert(topicsIds, 'topicsIds can not be null');
|
$assert(topicsIds, 'topicsIds can not be null');
|
||||||
|
|
||||||
const commandFunc = (topic) => {
|
const commandFunc = (topic: Topic) => {
|
||||||
const result = topic.getFontWeight();
|
const result = topic.getFontWeight();
|
||||||
const weight = result === 'bold' ? 'normal' : 'bold';
|
const weight = result === 'bold' ? 'normal' : 'bold';
|
||||||
topic.setFontWeight(weight, true);
|
topic.setFontWeight(weight, true);
|
||||||
|
|
||||||
topic._adjustShapes();
|
topic.adjustShapes();
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -44,6 +44,8 @@ import LayoutManager from './layout/LayoutManager';
|
|||||||
import NoteModel from './model/NoteModel';
|
import NoteModel from './model/NoteModel';
|
||||||
import LinkModel from './model/LinkModel';
|
import LinkModel from './model/LinkModel';
|
||||||
import SizeType from './SizeType';
|
import SizeType from './SizeType';
|
||||||
|
import FeatureModel from './model/FeatureModel';
|
||||||
|
import Icon from './Icon';
|
||||||
|
|
||||||
const ICON_SCALING_FACTOR = 1.3;
|
const ICON_SCALING_FACTOR = 1.3;
|
||||||
|
|
||||||
@ -104,15 +106,10 @@ abstract class Topic extends NodeGraph {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
setShapeType(type: string): void {
|
||||||
* @param {String} type the topic shape type
|
|
||||||
* @see {@link mindplot.model.INodeModel}
|
|
||||||
*/
|
|
||||||
setShapeType(type) {
|
|
||||||
this._setShapeType(type, true);
|
this._setShapeType(type, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return {mindplot.Topic} parent topic */
|
|
||||||
getParent(): Topic | null {
|
getParent(): Topic | null {
|
||||||
return this._parent;
|
return this._parent;
|
||||||
}
|
}
|
||||||
@ -161,8 +158,7 @@ abstract class Topic extends NodeGraph {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return {String} topic shape type */
|
getShapeType(): string {
|
||||||
getShapeType() {
|
|
||||||
const model = this.getModel();
|
const model = this.getModel();
|
||||||
let result = model.getShapeType();
|
let result = model.getShapeType();
|
||||||
if (!$defined(result)) {
|
if (!$defined(result)) {
|
||||||
@ -171,7 +167,7 @@ abstract class Topic extends NodeGraph {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _removeInnerShape() {
|
private _removeInnerShape(): ElementClass {
|
||||||
const group = this.get2DElement();
|
const group = this.get2DElement();
|
||||||
const innerShape = this.getInnerShape();
|
const innerShape = this.getInnerShape();
|
||||||
group.removeChild(innerShape);
|
group.removeChild(innerShape);
|
||||||
@ -308,7 +304,7 @@ abstract class Topic extends NodeGraph {
|
|||||||
return this._text;
|
return this._text;
|
||||||
}
|
}
|
||||||
|
|
||||||
getOrBuildIconGroup() {
|
getOrBuildIconGroup(): Group {
|
||||||
if (!$defined(this._iconsGroup)) {
|
if (!$defined(this._iconsGroup)) {
|
||||||
this._iconsGroup = this._buildIconGroup();
|
this._iconsGroup = this._buildIconGroup();
|
||||||
const group = this.get2DElement();
|
const group = this.get2DElement();
|
||||||
@ -346,7 +342,7 @@ abstract class Topic extends NodeGraph {
|
|||||||
* @param {mindplot.model.FeatureModel} featureModel
|
* @param {mindplot.model.FeatureModel} featureModel
|
||||||
* @return {mindplot.Icon} the icon corresponding to the feature model
|
* @return {mindplot.Icon} the icon corresponding to the feature model
|
||||||
*/
|
*/
|
||||||
addFeature(featureModel) {
|
addFeature(featureModel: FeatureModel): Icon {
|
||||||
const iconGroup = this.getOrBuildIconGroup();
|
const iconGroup = this.getOrBuildIconGroup();
|
||||||
this.closeEditors();
|
this.closeEditors();
|
||||||
|
|
||||||
@ -365,13 +361,13 @@ abstract class Topic extends NodeGraph {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** */
|
/** */
|
||||||
findFeatureById(id) {
|
findFeatureById(id: number) {
|
||||||
const model = this.getModel();
|
const model = this.getModel();
|
||||||
return model.findFeatureById(id);
|
return model.findFeatureById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** */
|
/** */
|
||||||
removeFeature(featureModel) {
|
removeFeature(featureModel: FeatureModel): void {
|
||||||
$assert(featureModel, 'featureModel could not be null');
|
$assert(featureModel, 'featureModel could not be null');
|
||||||
|
|
||||||
// Removing the icon from MODEL
|
// Removing the icon from MODEL
|
||||||
@ -387,21 +383,21 @@ abstract class Topic extends NodeGraph {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** */
|
/** */
|
||||||
addRelationship(relationship) {
|
addRelationship(relationship: Relationship) {
|
||||||
this._relationships.push(relationship);
|
this._relationships.push(relationship);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** */
|
/** */
|
||||||
deleteRelationship(relationship) {
|
deleteRelationship(relationship: Rect) {
|
||||||
this._relationships = this._relationships.filter((r) => r !== relationship);
|
this._relationships = this._relationships.filter((r) => r !== relationship);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** */
|
/** */
|
||||||
getRelationships() {
|
getRelationships(): Relationship[] {
|
||||||
return this._relationships;
|
return this._relationships;
|
||||||
}
|
}
|
||||||
|
|
||||||
_buildTextShape(readOnly): Text {
|
protected _buildTextShape(readOnly: boolean): Text {
|
||||||
const result = new Text();
|
const result = new Text();
|
||||||
const family = this.getFontFamily();
|
const family = this.getFontFamily();
|
||||||
const size = this.getFontSize();
|
const size = this.getFontSize();
|
||||||
@ -425,7 +421,7 @@ abstract class Topic extends NodeGraph {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** */
|
/** */
|
||||||
setFontFamily(value, updateModel) {
|
setFontFamily(value: string, updateModel?: boolean) {
|
||||||
const textShape = this.getTextShape();
|
const textShape = this.getTextShape();
|
||||||
textShape.setFontName(value);
|
textShape.setFontName(value);
|
||||||
if ($defined(updateModel) && updateModel) {
|
if ($defined(updateModel) && updateModel) {
|
||||||
@ -436,7 +432,7 @@ abstract class Topic extends NodeGraph {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** */
|
/** */
|
||||||
setFontSize(value, updateModel) {
|
setFontSize(value: number, updateModel?: boolean) {
|
||||||
const textShape = this.getTextShape();
|
const textShape = this.getTextShape();
|
||||||
textShape.setSize(value);
|
textShape.setSize(value);
|
||||||
|
|
||||||
@ -534,7 +530,7 @@ abstract class Topic extends NodeGraph {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_setText(text, updateModel) {
|
_setText(text: string, updateModel: boolean) {
|
||||||
const textShape = this.getTextShape();
|
const textShape = this.getTextShape();
|
||||||
textShape.setText(text == null ? TopicStyle.defaultText(this) : text);
|
textShape.setText(text == null ? TopicStyle.defaultText(this) : text);
|
||||||
|
|
||||||
@ -545,7 +541,7 @@ abstract class Topic extends NodeGraph {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** */
|
/** */
|
||||||
setText(text) {
|
setText(text: string) {
|
||||||
// Avoid empty nodes ...
|
// Avoid empty nodes ...
|
||||||
if (!text || $.trim(text).length === 0) {
|
if (!text || $.trim(text).length === 0) {
|
||||||
this._setText(null, true);
|
this._setText(null, true);
|
||||||
@ -557,7 +553,7 @@ abstract class Topic extends NodeGraph {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** */
|
/** */
|
||||||
getText() {
|
getText(): string {
|
||||||
const model = this.getModel();
|
const model = this.getModel();
|
||||||
let result = model.getText();
|
let result = model.getText();
|
||||||
if (!$defined(result)) {
|
if (!$defined(result)) {
|
||||||
@ -567,11 +563,11 @@ abstract class Topic extends NodeGraph {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** */
|
/** */
|
||||||
setBackgroundColor(color) {
|
setBackgroundColor(color: string) {
|
||||||
this._setBackgroundColor(color, true);
|
this._setBackgroundColor(color, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
_setBackgroundColor(color, updateModel) {
|
_setBackgroundColor(color: string, updateModel: boolean) {
|
||||||
const innerShape = this.getInnerShape();
|
const innerShape = this.getInnerShape();
|
||||||
innerShape.setFill(color);
|
innerShape.setFill(color);
|
||||||
|
|
||||||
@ -587,7 +583,7 @@ abstract class Topic extends NodeGraph {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** */
|
/** */
|
||||||
getBackgroundColor() {
|
getBackgroundColor(): string {
|
||||||
const model = this.getModel();
|
const model = this.getModel();
|
||||||
let result = model.getBackgroundColor();
|
let result = model.getBackgroundColor();
|
||||||
if (!$defined(result)) {
|
if (!$defined(result)) {
|
||||||
@ -597,11 +593,11 @@ abstract class Topic extends NodeGraph {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** */
|
/** */
|
||||||
setBorderColor(color) {
|
setBorderColor(color: string) {
|
||||||
this._setBorderColor(color, true);
|
this._setBorderColor(color, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
_setBorderColor(color, updateModel) {
|
_setBorderColor(color: string, updateModel: boolean) {
|
||||||
const innerShape = this.getInnerShape();
|
const innerShape = this.getInnerShape();
|
||||||
innerShape.setAttribute('strokeColor', color);
|
innerShape.setAttribute('strokeColor', color);
|
||||||
|
|
||||||
@ -1016,6 +1012,10 @@ abstract class Topic extends NodeGraph {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hide inner shape ...
|
||||||
|
this.getInnerShape().setVisibility(value);
|
||||||
|
|
||||||
|
// Hide text shape ...
|
||||||
const textShape = this.getTextShape();
|
const textShape = this.getTextShape();
|
||||||
textShape.setVisibility(this.getShapeType() !== TopicShape.IMAGE ? value : false);
|
textShape.setVisibility(this.getShapeType() !== TopicShape.IMAGE ? value : false);
|
||||||
}
|
}
|
||||||
@ -1058,7 +1058,7 @@ abstract class Topic extends NodeGraph {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setSize(size: SizeType, force: boolean): void {
|
setSize(size: SizeType, force?: boolean): void {
|
||||||
$assert(size, 'size can not be null');
|
$assert(size, 'size can not be null');
|
||||||
$assert($defined(size.width), 'size seem not to be a valid element');
|
$assert($defined(size.width), 'size seem not to be a valid element');
|
||||||
const roundedSize = {
|
const roundedSize = {
|
||||||
@ -1272,7 +1272,7 @@ abstract class Topic extends NodeGraph {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If a drag node is create for it, let's hide the editor.
|
// If a drag node is create for it, let's hide the editor.
|
||||||
this._getTopicEventDispatcher().close();
|
this._getTopicEventDispatcher().close(false);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -1296,7 +1296,7 @@ abstract class Topic extends NodeGraph {
|
|||||||
const topicHeight = Math.max(iconHeight, textHeight) + padding * 2;
|
const topicHeight = Math.max(iconHeight, textHeight) + padding * 2;
|
||||||
const textIconSpacing = Math.round(fontHeight / 4);
|
const textIconSpacing = Math.round(fontHeight / 4);
|
||||||
const iconGroupWith = iconGroup.getSize().width;
|
const iconGroupWith = iconGroup.getSize().width;
|
||||||
const topicWith = iconGroupWith + textIconSpacing + textWidth + padding * 2;
|
const topicWith = iconGroupWith + 2 * textIconSpacing + textWidth + padding * 2;
|
||||||
|
|
||||||
this.setSize({
|
this.setSize({
|
||||||
width: topicWith,
|
width: topicWith,
|
||||||
@ -1334,6 +1334,10 @@ abstract class Topic extends NodeGraph {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract workoutOutgoingConnectionPoint(position: Point): Point;
|
||||||
|
|
||||||
|
abstract workoutIncomingConnectionPoint(position: Point): Point;
|
||||||
|
|
||||||
isChildTopic(childTopic: Topic): boolean {
|
isChildTopic(childTopic: Topic): boolean {
|
||||||
let result = this.getId() === childTopic.getId();
|
let result = this.getId() === childTopic.getId();
|
||||||
if (!result) {
|
if (!result) {
|
||||||
@ -1349,7 +1353,7 @@ abstract class Topic extends NodeGraph {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
isCentralTopic() {
|
isCentralTopic(): boolean {
|
||||||
return this.getModel().getType() === 'CentralTopic';
|
return this.getModel().getType() === 'CentralTopic';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ import { $assert } from '@wisemapping/core-js';
|
|||||||
import Events from './Events';
|
import Events from './Events';
|
||||||
import MultilineTextEditor from './MultilineTextEditor';
|
import MultilineTextEditor from './MultilineTextEditor';
|
||||||
import { TopicShape } from './model/INodeModel';
|
import { TopicShape } from './model/INodeModel';
|
||||||
|
import Topic from './Topic';
|
||||||
|
|
||||||
const TopicEvent = {
|
const TopicEvent = {
|
||||||
EDIT: 'editnode',
|
EDIT: 'editnode',
|
||||||
@ -26,30 +27,39 @@ const TopicEvent = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
class TopicEventDispatcher extends Events {
|
class TopicEventDispatcher extends Events {
|
||||||
constructor(readOnly) {
|
private _readOnly: boolean;
|
||||||
|
|
||||||
|
private _activeEditor: MultilineTextEditor;
|
||||||
|
|
||||||
|
private _multilineEditor: MultilineTextEditor;
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-use-before-define
|
||||||
|
static _instance: TopicEventDispatcher;
|
||||||
|
|
||||||
|
constructor(readOnly: boolean) {
|
||||||
super();
|
super();
|
||||||
this._readOnly = readOnly;
|
this._readOnly = readOnly;
|
||||||
this._activeEditor = null;
|
this._activeEditor = null;
|
||||||
this._multilineEditor = new MultilineTextEditor();
|
this._multilineEditor = new MultilineTextEditor();
|
||||||
}
|
}
|
||||||
|
|
||||||
close(update) {
|
close(update: boolean): void {
|
||||||
if (this.isVisible()) {
|
if (this.isVisible()) {
|
||||||
this._activeEditor.close(update);
|
this._activeEditor.close(update);
|
||||||
this._activeEditor = null;
|
this._activeEditor = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
show(topic, options) {
|
show(topic: Topic, options?): void {
|
||||||
this.process(TopicEvent.EDIT, topic, options);
|
this.process(TopicEvent.EDIT, topic, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
process(eventType, topic, options) {
|
process(eventType: string, topic: Topic, options?): void {
|
||||||
$assert(eventType, 'eventType can not be null');
|
$assert(eventType, 'eventType can not be null');
|
||||||
|
|
||||||
// Close all previous open editor ....
|
// Close all previous open editor ....
|
||||||
if (this.isVisible()) {
|
if (this.isVisible()) {
|
||||||
this.close();
|
this.close(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open the new editor ...
|
// Open the new editor ...
|
||||||
@ -66,20 +76,18 @@ class TopicEventDispatcher extends Events {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isVisible() {
|
isVisible(): boolean {
|
||||||
return this._activeEditor != null && this._activeEditor.isVisible();
|
return this._activeEditor != null && this._activeEditor.isVisible();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static configure(readOnly: boolean): void {
|
||||||
|
this._instance = new TopicEventDispatcher(readOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
TopicEventDispatcher._instance = null;
|
static getInstance(): TopicEventDispatcher {
|
||||||
|
|
||||||
TopicEventDispatcher.configure = function configure(readOnly) {
|
|
||||||
this._instance = new TopicEventDispatcher(readOnly);
|
|
||||||
};
|
|
||||||
|
|
||||||
TopicEventDispatcher.getInstance = function getInstance() {
|
|
||||||
return this._instance;
|
return this._instance;
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export { TopicEvent };
|
export { TopicEvent };
|
||||||
export default TopicEventDispatcher;
|
export default TopicEventDispatcher;
|
@ -18,17 +18,21 @@
|
|||||||
import { $assert, $defined } from '@wisemapping/core-js';
|
import { $assert, $defined } from '@wisemapping/core-js';
|
||||||
import Command from '../Command';
|
import Command from '../Command';
|
||||||
import CommandContext from '../CommandContext';
|
import CommandContext from '../CommandContext';
|
||||||
|
import NodeModel from '../model/NodeModel';
|
||||||
|
import RelationshipModel from '../model/RelationshipModel';
|
||||||
|
import Relationship from '../Relationship';
|
||||||
|
import Topic from '../Topic';
|
||||||
|
|
||||||
class DeleteCommand extends Command {
|
class DeleteCommand extends Command {
|
||||||
private _relIds: number[];
|
private _relIds: number[];
|
||||||
|
|
||||||
private _topicIds: number[];
|
private _topicIds: number[];
|
||||||
|
|
||||||
private _deletedTopicModels: any[];
|
private _deletedTopicModels: NodeModel[];
|
||||||
|
|
||||||
private _deletedRelModel: any[];
|
private _deletedRelModel: RelationshipModel[];
|
||||||
|
|
||||||
private _parentTopicIds: any[];
|
private _parentTopicIds: number[];
|
||||||
|
|
||||||
constructor(topicIds: number[], relIds: number[]) {
|
constructor(topicIds: number[], relIds: number[]) {
|
||||||
$assert($defined(relIds), 'topicIds can not be null');
|
$assert($defined(relIds), 'topicIds can not be null');
|
||||||
@ -101,11 +105,11 @@ class DeleteCommand extends Command {
|
|||||||
|
|
||||||
// Do they need to be connected ?
|
// Do they need to be connected ?
|
||||||
this._deletedTopicModels.forEach(((topicModel, index) => {
|
this._deletedTopicModels.forEach(((topicModel, index) => {
|
||||||
const topics = commandContext.findTopics(topicModel.getId());
|
const topics = commandContext.findTopics([topicModel.getId()]);
|
||||||
|
|
||||||
const parentId = this._parentTopicIds[index];
|
const parentId = this._parentTopicIds[index];
|
||||||
if (parentId) {
|
if (parentId) {
|
||||||
const parentTopics = commandContext.findTopics(parentId);
|
const parentTopics = commandContext.findTopics([parentId]);
|
||||||
commandContext.connect(topics[0], parentTopics[0]);
|
commandContext.connect(topics[0], parentTopics[0]);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
@ -117,14 +121,14 @@ class DeleteCommand extends Command {
|
|||||||
|
|
||||||
// Finally display the topics ...
|
// Finally display the topics ...
|
||||||
this._deletedTopicModels.forEach((topicModel) => {
|
this._deletedTopicModels.forEach((topicModel) => {
|
||||||
const topics = commandContext.findTopics(topicModel.getId());
|
const topics = commandContext.findTopics([topicModel.getId()]);
|
||||||
topics[0].setBranchVisibility(true);
|
topics[0].setBranchVisibility(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Focus on last recovered topic ..
|
// Focus on last recovered topic ..
|
||||||
if (this._deletedTopicModels.length > 0) {
|
if (this._deletedTopicModels.length > 0) {
|
||||||
const firstTopic = this._deletedTopicModels[0];
|
const firstTopic = this._deletedTopicModels[0];
|
||||||
const topic = commandContext.findTopics(firstTopic.getId())[0];
|
const topic = commandContext.findTopics([firstTopic.getId()])[0];
|
||||||
topic.setOnFocus(true);
|
topic.setOnFocus(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,11 +137,11 @@ class DeleteCommand extends Command {
|
|||||||
this._deletedRelModel = [];
|
this._deletedRelModel = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
_filterChildren(topicIds, commandContext) {
|
private _filterChildren(topicIds: number[], commandContext: CommandContext) {
|
||||||
const topics = commandContext.findTopics(topicIds);
|
const topics = commandContext.findTopics(topicIds);
|
||||||
|
|
||||||
const result = [];
|
const result = [];
|
||||||
topics.forEach((topic) => {
|
topics.forEach((topic: Topic) => {
|
||||||
let parent = topic.getParent();
|
let parent = topic.getParent();
|
||||||
let found = false;
|
let found = false;
|
||||||
while (parent != null && !found) {
|
while (parent != null && !found) {
|
||||||
@ -156,13 +160,16 @@ class DeleteCommand extends Command {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
_collectInDepthRelationships(topic) {
|
private _collectInDepthRelationships(topic: Topic): Relationship[] {
|
||||||
let result = [];
|
let result = [];
|
||||||
result = result.concat(topic.getRelationships());
|
result = result.concat(topic.getRelationships());
|
||||||
|
|
||||||
const children = topic.getChildren();
|
const children = topic.getChildren();
|
||||||
const rels = children.map(((t) => this._collectInDepthRelationships(t)));
|
const rels: (Relationship[])[] = children
|
||||||
result = result.concat(rels.flat());
|
.map(((t: Topic) => this._collectInDepthRelationships(t)));
|
||||||
|
|
||||||
|
// flatten and concact
|
||||||
|
result = result.concat(([].concat(...rels)));
|
||||||
|
|
||||||
if (result.length > 0) {
|
if (result.length > 0) {
|
||||||
// Filter for unique ...
|
// Filter for unique ...
|
||||||
|
@ -62,7 +62,7 @@ class DragTopicCommand extends Command {
|
|||||||
const origPosition = topic.getPosition();
|
const origPosition = topic.getPosition();
|
||||||
|
|
||||||
// Disconnect topic ..
|
// Disconnect topic ..
|
||||||
if ($defined(origParentTopic) && origParentTopic.getId() !== this._parentId) {
|
if ($defined(origParentTopic)) {
|
||||||
commandContext.disconnect(topic);
|
commandContext.disconnect(topic);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,7 +76,6 @@ class DragTopicCommand extends Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Finally, connect topic ...
|
// Finally, connect topic ...
|
||||||
if (!$defined(origParentTopic) || origParentTopic.getId() !== this._parentId) {
|
|
||||||
if ($defined(this._parentId)) {
|
if ($defined(this._parentId)) {
|
||||||
const parentTopic = commandContext.findTopics([this._parentId])[0];
|
const parentTopic = commandContext.findTopics([this._parentId])[0];
|
||||||
commandContext.connect(topic, parentTopic);
|
commandContext.connect(topic, parentTopic);
|
||||||
@ -87,7 +86,6 @@ class DragTopicCommand extends Command {
|
|||||||
if ($defined(origParentTopic)) {
|
if ($defined(origParentTopic)) {
|
||||||
this._parentId = origParentTopic.getId();
|
this._parentId = origParentTopic.getId();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
topic.setVisibility(true);
|
topic.setVisibility(true);
|
||||||
|
|
||||||
// Store for undo ...
|
// Store for undo ...
|
||||||
|
@ -20,18 +20,20 @@ import Command from '../Command';
|
|||||||
import CommandContext from '../CommandContext';
|
import CommandContext from '../CommandContext';
|
||||||
import Topic from '../Topic';
|
import Topic from '../Topic';
|
||||||
|
|
||||||
|
type CommandTypes = string | object | boolean | number;
|
||||||
|
|
||||||
class GenericFunctionCommand extends Command {
|
class GenericFunctionCommand extends Command {
|
||||||
private _value: string | object | boolean | number;
|
private _value: CommandTypes;
|
||||||
|
|
||||||
private _topicsId: number[];
|
private _topicsId: number[];
|
||||||
|
|
||||||
private _commandFunc: (topic: Topic, value: string | object | boolean | number) => string | object | boolean;
|
private _commandFunc: (topic: Topic, value: CommandTypes) => CommandTypes;
|
||||||
|
|
||||||
private _oldValues: any[];
|
private _oldValues: (CommandTypes)[];
|
||||||
|
|
||||||
private _applied: boolean;
|
private _applied: boolean;
|
||||||
|
|
||||||
constructor(commandFunc: (topic: Topic, value: string | object | boolean) => string | object | boolean, topicsIds: number[], value: string | object | boolean | number = undefined) {
|
constructor(commandFunc: (topic: Topic, value: CommandTypes) => CommandTypes, topicsIds: number[], value: CommandTypes = undefined) {
|
||||||
$assert(commandFunc, 'commandFunc must be defined');
|
$assert(commandFunc, 'commandFunc must be defined');
|
||||||
$assert($defined(topicsIds), 'topicsIds must be defined');
|
$assert($defined(topicsIds), 'topicsIds must be defined');
|
||||||
|
|
||||||
|
@ -15,26 +15,24 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { Mindmap } from '../..';
|
|
||||||
import Exporter from './Exporter';
|
import Exporter from './Exporter';
|
||||||
import SVGExporter from './SVGExporter';
|
import SVGExporter from './SVGExporter';
|
||||||
/**
|
/**
|
||||||
* Based on https://mybyways.com/blog/convert-svg-to-png-using-your-browser
|
* Based on https://mybyways.com/blog/convert-svg-to-png-using-your-browser
|
||||||
*/
|
*/
|
||||||
class BinaryImageExporter extends Exporter {
|
class BinaryImageExporter extends Exporter {
|
||||||
svgElement: Element;
|
private svgElement: Element;
|
||||||
|
|
||||||
mindmap: Mindmap;
|
private width: number;
|
||||||
|
|
||||||
width: number;
|
private height: number;
|
||||||
|
|
||||||
height: number;
|
private adjustToFit: boolean;
|
||||||
|
|
||||||
constructor(mindmap: Mindmap, svgElement: Element, width: number, height: number, imgFormat: 'image/png' | 'image/jpeg') {
|
constructor(svgElement: Element, width: number, height: number, imgFormat: 'image/png' | 'image/jpeg', adjustToFit = true) {
|
||||||
super(imgFormat.split['/'][0], imgFormat);
|
super(imgFormat.split('/')[0], imgFormat);
|
||||||
this.svgElement = svgElement;
|
this.svgElement = svgElement;
|
||||||
this.mindmap = mindmap;
|
this.adjustToFit = adjustToFit;
|
||||||
|
|
||||||
this.width = width;
|
this.width = width;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
}
|
}
|
||||||
@ -43,21 +41,35 @@ class BinaryImageExporter extends Exporter {
|
|||||||
throw new Error('Images can not be exporeted');
|
throw new Error('Images can not be exporeted');
|
||||||
}
|
}
|
||||||
|
|
||||||
async exportAndEndcode(): Promise<string> {
|
exportAndEncode(): Promise<string> {
|
||||||
const svgExporter = new SVGExporter(this.svgElement);
|
const svgExporter = new SVGExporter(this.svgElement, this.adjustToFit);
|
||||||
const svgUrl = await svgExporter.exportAndEncode();
|
const svgUrl = svgExporter.exportAndEncode();
|
||||||
|
return svgUrl.then((value: string) => {
|
||||||
// Get the device pixel ratio, falling back to 1. But, I will double the resolution to look nicer.
|
// Get the device pixel ratio, falling back to 1. But, I will double the resolution to look nicer.
|
||||||
const dpr = (window.devicePixelRatio || 1) * 2;
|
const dpr = (window.devicePixelRatio || 1) * 2;
|
||||||
|
|
||||||
// Create canvas ...
|
// Create canvas size ...
|
||||||
const canvas = document.createElement('canvas');
|
const canvas = document.createElement('canvas');
|
||||||
canvas.setAttribute('width', (this.width * dpr).toString());
|
let width: number;
|
||||||
canvas.setAttribute('height', (this.height * dpr).toString());
|
let height: number;
|
||||||
|
if (this.adjustToFit) {
|
||||||
|
// Size must match with SVG image size ...
|
||||||
|
const size = svgExporter.getImgSize();
|
||||||
|
width = (size.width * dpr);
|
||||||
|
height = (size.height * dpr);
|
||||||
|
} else {
|
||||||
|
// Use screensize as size ..
|
||||||
|
width = (this.width * dpr);
|
||||||
|
height = (this.height * dpr);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Export size: ${width}:${height}`);
|
||||||
|
canvas.setAttribute('width', width.toFixed(0));
|
||||||
|
canvas.setAttribute('height', height.toFixed(0));
|
||||||
|
|
||||||
// Render the image and wait for the response ...
|
// Render the image and wait for the response ...
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
const result = new Promise<string>((resolve, reject) => {
|
const result = new Promise<string>((resolve) => {
|
||||||
img.onload = () => {
|
img.onload = () => {
|
||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext('2d');
|
||||||
// Scale for retina ...
|
// Scale for retina ...
|
||||||
@ -68,12 +80,13 @@ class BinaryImageExporter extends Exporter {
|
|||||||
.toDataURL(this.getContentType())
|
.toDataURL(this.getContentType())
|
||||||
.replace('image/png', 'octet/stream');
|
.replace('image/png', 'octet/stream');
|
||||||
|
|
||||||
URL.revokeObjectURL(svgUrl);
|
URL.revokeObjectURL(value);
|
||||||
resolve(imgDataUri);
|
resolve(imgDataUri);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
img.src = svgUrl;
|
img.src = value;
|
||||||
return result;
|
return result;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export default BinaryImageExporter;
|
export default BinaryImageExporter;
|
||||||
|
304
packages/mindplot/src/components/export/FreemindExporter.ts
Normal file
304
packages/mindplot/src/components/export/FreemindExporter.ts
Normal file
@ -0,0 +1,304 @@
|
|||||||
|
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 PositionNodeType from '../PositionType';
|
||||||
|
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';
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 bug3 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';
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
@ -15,26 +15,25 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { Mindmap } from '../..';
|
|
||||||
import BinaryImageExporter from './BinaryImageExporter';
|
import BinaryImageExporter from './BinaryImageExporter';
|
||||||
import Exporter from './Exporter';
|
import Exporter from './Exporter';
|
||||||
import SVGExporter from './SVGExporter';
|
import SVGExporter from './SVGExporter';
|
||||||
|
|
||||||
type imageType = 'svg' | 'png' | 'jpg';
|
type imageType = 'svg' | 'png' | 'jpg';
|
||||||
class ImageExpoterFactory {
|
class ImageExpoterFactory {
|
||||||
static create(type: imageType, mindmap: Mindmap, svgElement: Element, width: number, height: number, isCenter = false): Exporter {
|
static create(type: imageType, svgElement: Element, width: number, height: number, adjustToFit = true): Exporter {
|
||||||
let result;
|
let result: Exporter;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'svg': {
|
case 'svg': {
|
||||||
result = new SVGExporter(svgElement);
|
result = new SVGExporter(svgElement, adjustToFit);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'png': {
|
case 'png': {
|
||||||
result = new BinaryImageExporter(mindmap, svgElement, width, height, 'image/png');
|
result = new BinaryImageExporter(svgElement, width, height, 'image/png', adjustToFit);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'jpg': {
|
case 'jpg': {
|
||||||
result = new BinaryImageExporter(mindmap, svgElement, width, height, 'image/jpeg');
|
result = new BinaryImageExporter(svgElement, width, height, 'image/jpeg', adjustToFit);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -15,23 +15,33 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
import SizeType from '../SizeType';
|
||||||
import Exporter from './Exporter';
|
import Exporter from './Exporter';
|
||||||
|
|
||||||
class SVGExporter extends Exporter {
|
class SVGExporter extends Exporter {
|
||||||
private svgElement: Element;
|
private svgElement: Element;
|
||||||
|
|
||||||
private prolog = '<?xml version="1.0" encoding="utf-8" standalone="yes"?>\n';
|
private static prolog = '<?xml version="1.0" encoding="utf-8" standalone="yes"?>\n';
|
||||||
|
|
||||||
constructor(svgElement: Element) {
|
private static regexpTranslate = /translate\((-?[0-9]+.[0-9]+),(-?[0-9]+.[0-9]+)\)/;
|
||||||
|
|
||||||
|
private static padding = 30;
|
||||||
|
|
||||||
|
private adjustToFit: boolean;
|
||||||
|
|
||||||
|
private static MAX_SUPPORTED_SIZE = 2500;
|
||||||
|
|
||||||
|
constructor(svgElement: Element, adjustToFit = true) {
|
||||||
super('svg', 'image/svg+xml');
|
super('svg', 'image/svg+xml');
|
||||||
this.svgElement = svgElement;
|
this.svgElement = svgElement;
|
||||||
|
this.adjustToFit = adjustToFit;
|
||||||
}
|
}
|
||||||
|
|
||||||
export(): Promise<string> {
|
export(): Promise<string> {
|
||||||
// Replace all images for in-line images ...
|
// Replace all images for in-line images ...
|
||||||
let svgTxt: string = new XMLSerializer()
|
let svgTxt: string = new XMLSerializer()
|
||||||
.serializeToString(this.svgElement);
|
.serializeToString(this.svgElement);
|
||||||
svgTxt = this.prolog + svgTxt;
|
svgTxt = SVGExporter.prolog + svgTxt;
|
||||||
|
|
||||||
// Are namespace declared ?. Otherwise, force the declaration ...
|
// Are namespace declared ?. Otherwise, force the declaration ...
|
||||||
if (svgTxt.indexOf('xmlns:xlink=') === -1) {
|
if (svgTxt.indexOf('xmlns:xlink=') === -1) {
|
||||||
@ -39,15 +49,103 @@ class SVGExporter extends Exporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add white background. This is mainly for PNG export ...
|
// Add white background. This is mainly for PNG export ...
|
||||||
const svgDoc = SVGExporter.parseXMLString(svgTxt, 'application/xml');
|
let svgDoc = SVGExporter.parseXMLString(svgTxt, 'application/xml');
|
||||||
const svgElement = svgDoc.getElementsByTagName('svg')[0];
|
const svgElement = svgDoc.getElementsByTagName('svg')[0];
|
||||||
svgElement.setAttribute('style', 'background-color:white');
|
svgElement.setAttribute('style', 'background-color:white');
|
||||||
|
svgElement.setAttribute('focusable', 'false');
|
||||||
|
|
||||||
|
// Does need to be adjust ?.
|
||||||
|
if (this.adjustToFit) {
|
||||||
|
svgDoc = this._normalizeToFit(svgDoc);
|
||||||
|
}
|
||||||
|
|
||||||
const result = new XMLSerializer()
|
const result = new XMLSerializer()
|
||||||
.serializeToString(svgDoc);
|
.serializeToString(svgDoc);
|
||||||
|
|
||||||
return Promise.resolve(result);
|
return Promise.resolve(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _calcualteDimensions(): { minX: number, maxX: number, minY: number, maxY: number } {
|
||||||
|
// Collect all group elements ...
|
||||||
|
const rectElems = Array.from(document.querySelectorAll('g>rect'));
|
||||||
|
const translates: SizeType[] = rectElems
|
||||||
|
.map((rect: Element) => {
|
||||||
|
const g = rect.parentElement;
|
||||||
|
const transformStr = g.getAttribute('transform');
|
||||||
|
|
||||||
|
// Looking to parse translate(220.00000,279.00000) scale(1.00000,1.00000)
|
||||||
|
const match = transformStr.match(SVGExporter.regexpTranslate);
|
||||||
|
let result: SizeType = { width: 0, height: 0 };
|
||||||
|
if (match !== null) {
|
||||||
|
result = { width: Number.parseFloat(match[1]), height: Number.parseFloat(match[2]) };
|
||||||
|
|
||||||
|
// Add rect size ...
|
||||||
|
if (result.width > 0) {
|
||||||
|
const rectWidth = Number.parseFloat(rect.getAttribute('width'));
|
||||||
|
result.width += rectWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.height > 0) {
|
||||||
|
const rectHeight = Number.parseFloat(rect.getAttribute('height'));
|
||||||
|
result.height += rectHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Find max and mins ...
|
||||||
|
const widths = translates.map((t) => t.width).sort((a, b) => a - b);
|
||||||
|
const heights = translates.map((t) => t.height).sort((a, b) => a - b);
|
||||||
|
|
||||||
|
const minX = widths[0] - SVGExporter.padding;
|
||||||
|
const minY = heights[0] - SVGExporter.padding;
|
||||||
|
|
||||||
|
const maxX = widths[widths.length - 1] + SVGExporter.padding;
|
||||||
|
const maxY = heights[heights.length - 1] + SVGExporter.padding;
|
||||||
|
|
||||||
|
return {
|
||||||
|
minX, maxX, minY, maxY,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getImgSize(): SizeType {
|
||||||
|
const {
|
||||||
|
minX, maxX, minY, maxY,
|
||||||
|
} = this._calcualteDimensions();
|
||||||
|
|
||||||
|
let width: number = maxX + Math.abs(minX);
|
||||||
|
let height: number = maxY + Math.abs(minY);
|
||||||
|
|
||||||
|
// Prevents an image too big. Failures seen during export in couple of browsers
|
||||||
|
if (Math.max(width, height) > SVGExporter.MAX_SUPPORTED_SIZE) {
|
||||||
|
const scale = Math.max(width, height) / SVGExporter.MAX_SUPPORTED_SIZE;
|
||||||
|
width /= scale;
|
||||||
|
height /= scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { width, height };
|
||||||
|
}
|
||||||
|
|
||||||
|
private _normalizeToFit(document: Document): Document {
|
||||||
|
const {
|
||||||
|
minX, maxX, minY, maxY,
|
||||||
|
} = this._calcualteDimensions();
|
||||||
|
const svgElem = document.firstChild as Element;
|
||||||
|
|
||||||
|
const width = maxX + Math.abs(minX);
|
||||||
|
const height = maxY + Math.abs(minY);
|
||||||
|
|
||||||
|
svgElem.setAttribute('viewBox', `${minX} ${minY} ${width} ${height}`);
|
||||||
|
svgElem.setAttribute('preserveAspectRatio', 'xMinYMin');
|
||||||
|
|
||||||
|
// Get image size ...
|
||||||
|
const imgSize = this.getImgSize();
|
||||||
|
svgElem.setAttribute('width', imgSize.width.toFixed(0));
|
||||||
|
svgElem.setAttribute('height', imgSize.height.toFixed(0));
|
||||||
|
|
||||||
|
return document;
|
||||||
|
}
|
||||||
|
|
||||||
private static parseXMLString = (xmlStr: string, mimeType: DOMParserSupportedType) => {
|
private static parseXMLString = (xmlStr: string, mimeType: DOMParserSupportedType) => {
|
||||||
const parser = new DOMParser();
|
const parser = new DOMParser();
|
||||||
const xmlDoc = parser.parseFromString(xmlStr, mimeType);
|
const xmlDoc = parser.parseFromString(xmlStr, mimeType);
|
||||||
|
@ -20,6 +20,7 @@ import Exporter from './Exporter';
|
|||||||
import MDExporter from './MDExporter';
|
import MDExporter from './MDExporter';
|
||||||
import TxtExporter from './TxtExporter';
|
import TxtExporter from './TxtExporter';
|
||||||
import WiseXMLExporter from './WiseXMLExporter';
|
import WiseXMLExporter from './WiseXMLExporter';
|
||||||
|
import FreemindExporter from './FreemindExporter';
|
||||||
|
|
||||||
type textType = 'wxml' | 'txt' | 'mm' | 'csv' | 'md' | 'mmap';
|
type textType = 'wxml' | 'txt' | 'mm' | 'csv' | 'md' | 'mmap';
|
||||||
|
|
||||||
@ -36,6 +37,9 @@ class TextExporterFactory {
|
|||||||
case 'md':
|
case 'md':
|
||||||
result = new MDExporter(mindmap);
|
result = new MDExporter(mindmap);
|
||||||
break;
|
break;
|
||||||
|
case 'mm':
|
||||||
|
result = new FreemindExporter(mindmap);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unsupported type ${type}`);
|
throw new Error(`Unsupported type ${type}`);
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
import { Mindmap } from '../..';
|
import { Mindmap } from '../..';
|
||||||
import INodeModel from '../model/INodeModel';
|
import INodeModel from '../model/INodeModel';
|
||||||
import LinkModel from '../model/LinkModel';
|
import LinkModel from '../model/LinkModel';
|
||||||
|
import NoteModel from '../model/NoteModel';
|
||||||
import Exporter from './Exporter';
|
import Exporter from './Exporter';
|
||||||
|
|
||||||
class TxtExporter extends Exporter {
|
class TxtExporter extends Exporter {
|
||||||
@ -32,27 +33,27 @@ class TxtExporter extends Exporter {
|
|||||||
const { mindmap } = this;
|
const { mindmap } = this;
|
||||||
|
|
||||||
const branches = mindmap.getBranches();
|
const branches = mindmap.getBranches();
|
||||||
const txtStr = this.traverseBranch('', branches);
|
const txtStr = this.traverseBranch('', '', branches);
|
||||||
return Promise.resolve(txtStr);
|
return Promise.resolve(txtStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
private traverseBranch(prefix: string, branches: INodeModel[]) {
|
private traverseBranch(indent: string, prefix: string, branches: INodeModel[]) {
|
||||||
let result = '';
|
let result = '';
|
||||||
branches
|
branches
|
||||||
.filter((n) => n.getText() !== undefined)
|
|
||||||
.forEach((node, index) => {
|
.forEach((node, index) => {
|
||||||
result = `${result}${prefix}${index + 1} ${node.getText()}`;
|
result = `${result}${indent}${prefix}${index + 1} ${node.getText() !== undefined ? node.getText() : ''}`;
|
||||||
node.getFeatures().forEach((f) => {
|
node.getFeatures().forEach((f) => {
|
||||||
const type = f.getType();
|
const type = f.getType();
|
||||||
if (type === 'link') {
|
if (type === 'link') {
|
||||||
result = `${result} [link: ${(f as LinkModel).getUrl()}]`;
|
result = `${result}\n ${indent} [Link: ${(f as LinkModel).getUrl()}]`;
|
||||||
|
}
|
||||||
|
if (type === 'note') {
|
||||||
|
result = `${result}\n${indent} [Note: ${(f as NoteModel).getText()}]`;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
result = `${result}\n`;
|
result = `${result}\n`;
|
||||||
|
|
||||||
if (node.getChildren().filter((n) => n.getText() !== undefined).length > 0) {
|
result += this.traverseBranch(`\t${indent}`, `${prefix}${index + 1}.`, node.getChildren());
|
||||||
result += this.traverseBranch(`\t${prefix}${index + 1}.`, node.getChildren());
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,86 @@
|
|||||||
|
export default class Arrowlink {
|
||||||
|
protected COLOR: string;
|
||||||
|
|
||||||
|
protected DESTINATION: string;
|
||||||
|
|
||||||
|
protected ENDARROW: string;
|
||||||
|
|
||||||
|
protected ENDINCLINATION: string;
|
||||||
|
|
||||||
|
protected ID: string;
|
||||||
|
|
||||||
|
protected STARTARROW: string;
|
||||||
|
|
||||||
|
protected STARTINCLINATION: string;
|
||||||
|
|
||||||
|
getColor(): string {
|
||||||
|
return this.COLOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
getDestination(): string {
|
||||||
|
return this.DESTINATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
getEndarrow(): string {
|
||||||
|
return this.ENDARROW;
|
||||||
|
}
|
||||||
|
|
||||||
|
getEndInclination(): string {
|
||||||
|
return this.ENDINCLINATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
getId(): string {
|
||||||
|
return this.ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
getStartarrow(): string {
|
||||||
|
return this.STARTARROW;
|
||||||
|
}
|
||||||
|
|
||||||
|
getStartinclination(): string {
|
||||||
|
return this.STARTINCLINATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
setColor(value: string): void {
|
||||||
|
this.COLOR = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setDestination(value: string): void {
|
||||||
|
this.DESTINATION = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setEndarrow(value: string): void {
|
||||||
|
this.ENDARROW = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setEndinclination(value: string): void {
|
||||||
|
this.ENDINCLINATION = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setId(value: string): void {
|
||||||
|
this.ID = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setStartarrow(value: string): void {
|
||||||
|
this.STARTARROW = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setStartinclination(value: string): void {
|
||||||
|
this.STARTINCLINATION = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
toXml(document: Document): HTMLElement {
|
||||||
|
const arrowlinkElem = document.createElement('arrowlink');
|
||||||
|
|
||||||
|
arrowlinkElem.setAttribute('DESTINATION', this.DESTINATION);
|
||||||
|
arrowlinkElem.setAttribute('STARTARROW', this.STARTARROW);
|
||||||
|
|
||||||
|
if (this.COLOR) arrowlinkElem.setAttribute('COLOR', this.COLOR);
|
||||||
|
if (this.ENDINCLINATION) arrowlinkElem.setAttribute('ENDINCLINATION', this.ENDINCLINATION);
|
||||||
|
if (this.ENDARROW) arrowlinkElem.setAttribute('ENDARROW', this.ENDARROW);
|
||||||
|
if (this.ID) arrowlinkElem.setAttribute('ID', this.ID);
|
||||||
|
if (this.STARTINCLINATION) arrowlinkElem.setAttribute('STARTINCLINATION', this.STARTINCLINATION);
|
||||||
|
|
||||||
|
return arrowlinkElem;
|
||||||
|
}
|
||||||
|
}
|
20
packages/mindplot/src/components/export/freemind/Cloud.ts
Normal file
20
packages/mindplot/src/components/export/freemind/Cloud.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
export default class Cloud {
|
||||||
|
protected COLOR: string;
|
||||||
|
|
||||||
|
getColor(): string {
|
||||||
|
return this.COLOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
setColor(value: string): void {
|
||||||
|
this.COLOR = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
toXml(document: Document): HTMLElement {
|
||||||
|
// Set node attributes
|
||||||
|
const cloudElem = document.createElement('cloud');
|
||||||
|
|
||||||
|
cloudElem.setAttribute('COLOR', this.COLOR);
|
||||||
|
|
||||||
|
return cloudElem;
|
||||||
|
}
|
||||||
|
}
|
42
packages/mindplot/src/components/export/freemind/Edge.ts
Normal file
42
packages/mindplot/src/components/export/freemind/Edge.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
export default class Edge {
|
||||||
|
protected COLOR: string;
|
||||||
|
|
||||||
|
protected STYLE: string;
|
||||||
|
|
||||||
|
protected WIDTH: string;
|
||||||
|
|
||||||
|
getColor(): string {
|
||||||
|
return this.COLOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
getStyle(): string {
|
||||||
|
return this.STYLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
getWidth(): string {
|
||||||
|
return this.WIDTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
setColor(value: string): void {
|
||||||
|
this.COLOR = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setStyle(value: string): void {
|
||||||
|
this.STYLE = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setWidth(value: string): void {
|
||||||
|
this.WIDTH = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
return edgeElem;
|
||||||
|
}
|
||||||
|
}
|
56
packages/mindplot/src/components/export/freemind/Font.ts
Normal file
56
packages/mindplot/src/components/export/freemind/Font.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
export default class Font {
|
||||||
|
protected BOLD?: string;
|
||||||
|
|
||||||
|
protected ITALIC?: string;
|
||||||
|
|
||||||
|
protected NAME?: string;
|
||||||
|
|
||||||
|
protected SIZE: string;
|
||||||
|
|
||||||
|
getBold(): string {
|
||||||
|
return this.BOLD;
|
||||||
|
}
|
||||||
|
|
||||||
|
getItalic(): string {
|
||||||
|
return this.ITALIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
getName(): string {
|
||||||
|
return this.NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSize(): string {
|
||||||
|
return this.SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
setBold(value: string): void {
|
||||||
|
this.BOLD = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setItalic(value: string): void {
|
||||||
|
this.ITALIC = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setName(value: string): void {
|
||||||
|
this.NAME = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setSize(value: string): void {
|
||||||
|
this.SIZE = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
toXml(document: Document): HTMLElement {
|
||||||
|
const fontElem = document.createElement('font');
|
||||||
|
|
||||||
|
if (this.SIZE) {
|
||||||
|
fontElem.setAttribute('SIZE', this.SIZE);
|
||||||
|
} else {
|
||||||
|
fontElem.setAttribute('SIZE', '12');
|
||||||
|
}
|
||||||
|
if (this.BOLD) fontElem.setAttribute('BOLD', this.BOLD);
|
||||||
|
if (this.ITALIC) fontElem.setAttribute('ITALIC', this.ITALIC);
|
||||||
|
if (this.NAME) fontElem.setAttribute('NAME', this.NAME);
|
||||||
|
|
||||||
|
return fontElem;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
import VersionNumber from './importer/VersionNumber';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
LAST_SUPPORTED_FREEMIND_VERSION: '1.0.1',
|
||||||
|
|
||||||
|
SUPPORTED_FREEMIND_VERSION: new VersionNumber('1.0.1'),
|
||||||
|
|
||||||
|
CODE_VERSION: 'tango',
|
||||||
|
|
||||||
|
SECOND_LEVEL_TOPIC_HEIGHT: 25,
|
||||||
|
|
||||||
|
ROOT_LEVEL_TOPIC_HEIGHT: 25,
|
||||||
|
|
||||||
|
CENTRAL_TO_TOPIC_DISTANCE: 200,
|
||||||
|
|
||||||
|
TOPIC_TO_TOPIC_DISTANCE: 90,
|
||||||
|
|
||||||
|
FONT_SIZE_HUGE: 15,
|
||||||
|
|
||||||
|
FONT_SIZE_LARGE: 10,
|
||||||
|
|
||||||
|
FONT_SIZE_NORMAL: 8,
|
||||||
|
|
||||||
|
FONT_SIZE_SMALL: 6,
|
||||||
|
|
||||||
|
NODE_TYPE: 'NODE',
|
||||||
|
|
||||||
|
BOLD: 'bold',
|
||||||
|
|
||||||
|
ITALIC: 'italic',
|
||||||
|
|
||||||
|
EMPTY_FONT_STYLE: ';;;;;',
|
||||||
|
|
||||||
|
EMPTY_NOTE: '',
|
||||||
|
|
||||||
|
POSITION_LEFT: 'left',
|
||||||
|
|
||||||
|
POSITION_RIGHT: 'right',
|
||||||
|
};
|
33
packages/mindplot/src/components/export/freemind/Hook.ts
Normal file
33
packages/mindplot/src/components/export/freemind/Hook.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import Parameters from './Parameters';
|
||||||
|
|
||||||
|
export default class Hook {
|
||||||
|
protected PARAMETERS: Parameters;
|
||||||
|
|
||||||
|
protected TEXT: string;
|
||||||
|
|
||||||
|
protected NAME: string;
|
||||||
|
|
||||||
|
getParameters(): Parameters {
|
||||||
|
return this.PARAMETERS;
|
||||||
|
}
|
||||||
|
|
||||||
|
getText(): string {
|
||||||
|
return this.TEXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
getName(): string {
|
||||||
|
return this.NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
setParameters(value: Parameters): void {
|
||||||
|
this.PARAMETERS = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setText(value: string): void {
|
||||||
|
this.TEXT = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setName(value: string): void {
|
||||||
|
this.NAME = value;
|
||||||
|
}
|
||||||
|
}
|
20
packages/mindplot/src/components/export/freemind/Icon.ts
Normal file
20
packages/mindplot/src/components/export/freemind/Icon.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
export default class Icon {
|
||||||
|
protected BUILTIN: string;
|
||||||
|
|
||||||
|
getBuiltin(): string {
|
||||||
|
return this.BUILTIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
setBuiltin(value: string): void {
|
||||||
|
this.BUILTIN = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
toXml(document: Document): HTMLElement {
|
||||||
|
// Set node attributes
|
||||||
|
const iconElem = document.createElement('icon');
|
||||||
|
|
||||||
|
iconElem.setAttribute('BUILTIN', this.BUILTIN);
|
||||||
|
|
||||||
|
return iconElem;
|
||||||
|
}
|
||||||
|
}
|
116
packages/mindplot/src/components/export/freemind/Map.ts
Normal file
116
packages/mindplot/src/components/export/freemind/Map.ts
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
import { createDocument } from '@wisemapping/core-js';
|
||||||
|
import Arrowlink from './Arrowlink';
|
||||||
|
import Cloud from './Cloud';
|
||||||
|
import Edge from './Edge';
|
||||||
|
import Font from './Font';
|
||||||
|
import Icon from './Icon';
|
||||||
|
import Node, { Choise } from './Node';
|
||||||
|
import Richcontent from './Richcontent';
|
||||||
|
|
||||||
|
export default class Map {
|
||||||
|
protected node: Node;
|
||||||
|
|
||||||
|
protected version: string;
|
||||||
|
|
||||||
|
getNode(): Node {
|
||||||
|
return this.node;
|
||||||
|
}
|
||||||
|
|
||||||
|
getVersion(): string {
|
||||||
|
return this.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
setNode(value: Node) {
|
||||||
|
this.node = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setVesion(value: string) {
|
||||||
|
this.version = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
toXml(): Document {
|
||||||
|
const document = createDocument();
|
||||||
|
|
||||||
|
// Set map attributes
|
||||||
|
const mapElem = document.createElement('map');
|
||||||
|
mapElem.setAttribute('version', this.version);
|
||||||
|
|
||||||
|
document.appendChild(mapElem);
|
||||||
|
|
||||||
|
// Create main node
|
||||||
|
const mainNode: Node = this.node;
|
||||||
|
mainNode.setCentralTopic(true);
|
||||||
|
const mainNodeElem = mainNode.toXml(document);
|
||||||
|
mapElem.appendChild(mainNodeElem);
|
||||||
|
|
||||||
|
const childNodes: Array<Choise> = mainNode.getArrowlinkOrCloudOrEdge();
|
||||||
|
childNodes.forEach((childNode: Choise) => {
|
||||||
|
const node = this.nodeToXml(childNode, mainNodeElem, document);
|
||||||
|
mainNodeElem.appendChild(node);
|
||||||
|
});
|
||||||
|
|
||||||
|
return document;
|
||||||
|
}
|
||||||
|
|
||||||
|
private nodeToXml(childNode: Choise, parentNode: HTMLElement, document: Document): HTMLElement {
|
||||||
|
if (childNode instanceof Node) {
|
||||||
|
childNode.setCentralTopic(false);
|
||||||
|
const childNodeXml = childNode.toXml(document);
|
||||||
|
parentNode.appendChild(childNodeXml);
|
||||||
|
|
||||||
|
const childrens = childNode.getArrowlinkOrCloudOrEdge();
|
||||||
|
if (childrens.length > 0) {
|
||||||
|
childrens.forEach((node: Choise) => {
|
||||||
|
const nodeXml = this.nodeToXml(node, childNodeXml, document);
|
||||||
|
childNodeXml.appendChild(nodeXml);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return childNodeXml;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (childNode instanceof Font) {
|
||||||
|
const childNodeXml = childNode.toXml(document);
|
||||||
|
parentNode.appendChild(childNodeXml);
|
||||||
|
|
||||||
|
return childNodeXml;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (childNode instanceof Edge) {
|
||||||
|
const childNodeXml = childNode.toXml(document);
|
||||||
|
parentNode.appendChild(childNodeXml);
|
||||||
|
|
||||||
|
return childNodeXml;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (childNode instanceof Arrowlink) {
|
||||||
|
const childNodeXml = childNode.toXml(document);
|
||||||
|
parentNode.appendChild(childNodeXml);
|
||||||
|
|
||||||
|
return childNodeXml;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (childNode instanceof Cloud) {
|
||||||
|
const childNodeXml = childNode.toXml(document);
|
||||||
|
parentNode.appendChild(childNodeXml);
|
||||||
|
|
||||||
|
return childNodeXml;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (childNode instanceof Icon) {
|
||||||
|
const childNodeXml = childNode.toXml(document);
|
||||||
|
parentNode.appendChild(childNodeXml);
|
||||||
|
|
||||||
|
return childNodeXml;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (childNode instanceof Richcontent) {
|
||||||
|
const childNodeXml = childNode.toXml(document);
|
||||||
|
parentNode.appendChild(childNodeXml);
|
||||||
|
|
||||||
|
return childNodeXml;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parentNode;
|
||||||
|
}
|
||||||
|
}
|
233
packages/mindplot/src/components/export/freemind/Node.ts
Normal file
233
packages/mindplot/src/components/export/freemind/Node.ts
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
import Arrowlink from './Arrowlink';
|
||||||
|
import Cloud from './Cloud';
|
||||||
|
import Edge from './Edge';
|
||||||
|
import Font from './Font';
|
||||||
|
import Hook from './Hook';
|
||||||
|
import Icon from './Icon';
|
||||||
|
import Richcontent from './Richcontent';
|
||||||
|
|
||||||
|
class Node {
|
||||||
|
protected arrowlinkOrCloudOrEdge: Array<Arrowlink | Cloud | Edge | Font | Hook | Icon | Richcontent | this>;
|
||||||
|
|
||||||
|
protected BACKGROUND_COLOR: string;
|
||||||
|
|
||||||
|
protected COLOR: string;
|
||||||
|
|
||||||
|
protected FOLDED: string;
|
||||||
|
|
||||||
|
protected ID: string;
|
||||||
|
|
||||||
|
protected LINK: string;
|
||||||
|
|
||||||
|
protected POSITION: string;
|
||||||
|
|
||||||
|
protected STYLE: string;
|
||||||
|
|
||||||
|
protected TEXT: string;
|
||||||
|
|
||||||
|
protected CREATED: string;
|
||||||
|
|
||||||
|
protected MODIFIED: string;
|
||||||
|
|
||||||
|
protected HGAP: string;
|
||||||
|
|
||||||
|
protected VGAP: string;
|
||||||
|
|
||||||
|
protected WCOORDS: string;
|
||||||
|
|
||||||
|
protected WORDER: string;
|
||||||
|
|
||||||
|
protected VSHIFT: string;
|
||||||
|
|
||||||
|
protected ENCRYPTED_CONTENT: string;
|
||||||
|
|
||||||
|
private centralTopic: boolean;
|
||||||
|
|
||||||
|
getArrowlinkOrCloudOrEdge(): Array<Arrowlink | Cloud | Edge | Font | Hook | Icon | Richcontent | Node> {
|
||||||
|
if (!this.arrowlinkOrCloudOrEdge) {
|
||||||
|
this.arrowlinkOrCloudOrEdge = new Array<Arrowlink | Cloud | Edge | Font | Hook | Icon | Richcontent | this>();
|
||||||
|
}
|
||||||
|
return this.arrowlinkOrCloudOrEdge;
|
||||||
|
}
|
||||||
|
|
||||||
|
getBackgorundColor(): string {
|
||||||
|
return this.BACKGROUND_COLOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
getColor(): string {
|
||||||
|
return this.COLOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
getFolded(): string {
|
||||||
|
return this.FOLDED;
|
||||||
|
}
|
||||||
|
|
||||||
|
getId(): string {
|
||||||
|
return this.ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
getLink(): string {
|
||||||
|
return this.LINK;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPosition(): string {
|
||||||
|
return this.POSITION;
|
||||||
|
}
|
||||||
|
|
||||||
|
getStyle(): string {
|
||||||
|
return this.STYLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
getText(): string {
|
||||||
|
return this.TEXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCreated(): string {
|
||||||
|
return this.CREATED;
|
||||||
|
}
|
||||||
|
|
||||||
|
getModified(): string {
|
||||||
|
return this.MODIFIED;
|
||||||
|
}
|
||||||
|
|
||||||
|
getHgap(): string {
|
||||||
|
return this.HGAP;
|
||||||
|
}
|
||||||
|
|
||||||
|
getVgap(): string {
|
||||||
|
return this.VGAP;
|
||||||
|
}
|
||||||
|
|
||||||
|
getWcoords(): string {
|
||||||
|
return this.WCOORDS;
|
||||||
|
}
|
||||||
|
|
||||||
|
getWorder(): string {
|
||||||
|
return this.WORDER;
|
||||||
|
}
|
||||||
|
|
||||||
|
getVshift(): string {
|
||||||
|
return this.VSHIFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
getEncryptedContent(): string {
|
||||||
|
return this.ENCRYPTED_CONTENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCentralTopic(): boolean {
|
||||||
|
return this.centralTopic;
|
||||||
|
}
|
||||||
|
|
||||||
|
setArrowlinkOrCloudOrEdge(value: Arrowlink | Cloud | Edge | Font | Hook | Icon | Richcontent | this): void {
|
||||||
|
this.getArrowlinkOrCloudOrEdge().push(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
setBackgorundColor(value: string): void {
|
||||||
|
this.BACKGROUND_COLOR = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setColor(value: string): void {
|
||||||
|
this.COLOR = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setFolded(value: string): void {
|
||||||
|
this.FOLDED = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setId(value: string): void {
|
||||||
|
this.ID = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLink(value: string): void {
|
||||||
|
this.LINK = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setPosition(value: string): void {
|
||||||
|
this.POSITION = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setStyle(value: string): void {
|
||||||
|
this.STYLE = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setText(value: string): void {
|
||||||
|
this.TEXT = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setCreated(value: string): void {
|
||||||
|
this.CREATED = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setModified(value: string): void {
|
||||||
|
this.MODIFIED = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setHgap(value: string): void {
|
||||||
|
this.HGAP = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setVgap(value: string): void {
|
||||||
|
this.VGAP = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setWcoords(value: string): void {
|
||||||
|
this.WCOORDS = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setWorder(value: string): void {
|
||||||
|
this.WORDER = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setVshift(value: string): void {
|
||||||
|
this.VSHIFT = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setEncryptedContent(value: string): void {
|
||||||
|
this.ENCRYPTED_CONTENT = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setCentralTopic(value: boolean): void {
|
||||||
|
this.centralTopic = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
toXml(document: Document): HTMLElement {
|
||||||
|
// Set node attributes
|
||||||
|
const nodeElem = document.createElement('node');
|
||||||
|
|
||||||
|
if (this.centralTopic) {
|
||||||
|
if (this.ID) nodeElem.setAttribute('ID', this.ID);
|
||||||
|
if (this.TEXT) nodeElem.setAttribute('TEXT', this.TEXT);
|
||||||
|
if (this.BACKGROUND_COLOR) nodeElem.setAttribute('BACKGROUND_COLOR', this.BACKGROUND_COLOR);
|
||||||
|
if (this.COLOR) nodeElem.setAttribute('COLOR', this.COLOR);
|
||||||
|
if (this.TEXT) {
|
||||||
|
nodeElem.setAttribute('TEXT', this.TEXT);
|
||||||
|
} else {
|
||||||
|
nodeElem.setAttribute('TEXT', '');
|
||||||
|
}
|
||||||
|
return nodeElem;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.ID) nodeElem.setAttribute('ID', this.ID);
|
||||||
|
if (this.POSITION) nodeElem.setAttribute('POSITION', this.POSITION);
|
||||||
|
if (this.STYLE) nodeElem.setAttribute('STYLE', this.STYLE);
|
||||||
|
if (this.BACKGROUND_COLOR) nodeElem.setAttribute('BACKGROUND_COLOR', this.BACKGROUND_COLOR);
|
||||||
|
if (this.COLOR) nodeElem.setAttribute('COLOR', this.COLOR);
|
||||||
|
if (this.TEXT) nodeElem.setAttribute('TEXT', this.TEXT);
|
||||||
|
if (this.LINK) nodeElem.setAttribute('LINK', this.LINK);
|
||||||
|
if (this.FOLDED) nodeElem.setAttribute('FOLDED', this.FOLDED);
|
||||||
|
if (this.CREATED) nodeElem.setAttribute('CREATED', this.CREATED);
|
||||||
|
if (this.MODIFIED) nodeElem.setAttribute('MODIFIED', this.MODIFIED);
|
||||||
|
if (this.HGAP) nodeElem.setAttribute('HGAP', this.HGAP);
|
||||||
|
if (this.VGAP) nodeElem.setAttribute('VGAP', this.VGAP);
|
||||||
|
if (this.WCOORDS) nodeElem.setAttribute('WCOORDS', this.WCOORDS);
|
||||||
|
if (this.WORDER) nodeElem.setAttribute('WORDER', this.WORDER);
|
||||||
|
if (this.VSHIFT) nodeElem.setAttribute('VSHIFT', this.VSHIFT);
|
||||||
|
if (this.ENCRYPTED_CONTENT) nodeElem.setAttribute('ENCRYPTED_CONTENT', this.ENCRYPTED_CONTENT);
|
||||||
|
|
||||||
|
return nodeElem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Choise = Arrowlink | Cloud | Edge | Font | Hook | Icon | Richcontent | Node
|
||||||
|
|
||||||
|
export default Node;
|
@ -0,0 +1,51 @@
|
|||||||
|
import Arrowlink from './Arrowlink';
|
||||||
|
import Cloud from './Cloud';
|
||||||
|
import Edge from './Edge';
|
||||||
|
import Font from './Font';
|
||||||
|
import Hook from './Hook';
|
||||||
|
import Icon from './Icon';
|
||||||
|
import Richcontent from './Richcontent';
|
||||||
|
import Map from './Map';
|
||||||
|
import Node from './Node';
|
||||||
|
|
||||||
|
export default class ObjectFactory {
|
||||||
|
public createParameters(): void {
|
||||||
|
console.log('parameters');
|
||||||
|
}
|
||||||
|
|
||||||
|
public crateArrowlink(): Arrowlink {
|
||||||
|
return new Arrowlink();
|
||||||
|
}
|
||||||
|
|
||||||
|
public createCloud(): Cloud {
|
||||||
|
return new Cloud();
|
||||||
|
}
|
||||||
|
|
||||||
|
public createEdge(): Edge {
|
||||||
|
return new Edge();
|
||||||
|
}
|
||||||
|
|
||||||
|
public createFont(): Font {
|
||||||
|
return new Font();
|
||||||
|
}
|
||||||
|
|
||||||
|
public createHook(): Hook {
|
||||||
|
return new Hook();
|
||||||
|
}
|
||||||
|
|
||||||
|
public createIcon(): Icon {
|
||||||
|
return new Icon();
|
||||||
|
}
|
||||||
|
|
||||||
|
public createRichcontent(): Richcontent {
|
||||||
|
return new Richcontent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public createMap(): Map {
|
||||||
|
return new Map();
|
||||||
|
}
|
||||||
|
|
||||||
|
public createNode(): Node {
|
||||||
|
return new Node();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
export default class Parameters {
|
||||||
|
protected REMINDUSERAT: number;
|
||||||
|
|
||||||
|
getReminduserat(): number {
|
||||||
|
return this.REMINDUSERAT;
|
||||||
|
}
|
||||||
|
|
||||||
|
setReminduserat(value: number): void {
|
||||||
|
this.REMINDUSERAT = value;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
export default class Richcontent {
|
||||||
|
protected html: string;
|
||||||
|
|
||||||
|
protected type: string;
|
||||||
|
|
||||||
|
getHtml(): string {
|
||||||
|
return this.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
getType(): string {
|
||||||
|
return this.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
setHtml(value: string): void {
|
||||||
|
this.html = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setType(value: string): void {
|
||||||
|
this.type = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
toXml(document: Document): HTMLElement {
|
||||||
|
// Set node attributes
|
||||||
|
const richcontentElem = document.createElement('richcontent');
|
||||||
|
|
||||||
|
richcontentElem.setAttribute('TYPE', this.type);
|
||||||
|
|
||||||
|
if (this.html) {
|
||||||
|
const htmlElement: DocumentFragment = document.createRange().createContextualFragment(this.html);
|
||||||
|
richcontentElem.appendChild(htmlElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
return richcontentElem;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
export default class VersionNumber {
|
||||||
|
protected version: string;
|
||||||
|
|
||||||
|
constructor(version: string) {
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getVersion(): string {
|
||||||
|
return this.version;
|
||||||
|
}
|
||||||
|
}
|
@ -20,12 +20,14 @@ import ES from './es';
|
|||||||
import EN from './en';
|
import EN from './en';
|
||||||
import DE from './de';
|
import DE from './de';
|
||||||
import FR from './fr';
|
import FR from './fr';
|
||||||
|
import RU from './ru';
|
||||||
|
|
||||||
const Bundle = {
|
const Bundle = {
|
||||||
es: ES,
|
es: ES,
|
||||||
en: EN,
|
en: EN,
|
||||||
de: DE,
|
de: DE,
|
||||||
fr: FR,
|
fr: FR,
|
||||||
|
ru: RU,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Bundle;
|
export default Bundle;
|
||||||
|
@ -1,80 +0,0 @@
|
|||||||
ZOOM_IN = 'Ansicht vergrößern',
|
|
||||||
ZOOM_OUT = 'Ansicht verkleinern',
|
|
||||||
TOPIC_SHAPE = 'Themen Gestaltung',
|
|
||||||
TOPIC_ADD = 'Thema hinzufügen',
|
|
||||||
TOPIC_DELETE = 'Thema löschen',
|
|
||||||
TOPIC_ICON = 'Symbol hinzufügen',
|
|
||||||
TOPIC_LINK = 'Verbindung hinzufügen',
|
|
||||||
TOPIC_RELATIONSHIP = 'Beziehung',
|
|
||||||
TOPIC_COLOR = 'Themenfarbe',
|
|
||||||
TOPIC_BORDER_COLOR = 'Thema Randfarbe',
|
|
||||||
TOPIC_NOTE = 'Notiz hinzufügen',
|
|
||||||
FONT_FAMILY = 'Schrifttyp',
|
|
||||||
FONT_SIZE = 'Schriftgröße',
|
|
||||||
FONT_BOLD = 'Fette Schrift',
|
|
||||||
FONT_ITALIC = 'Kursive Schrift',
|
|
||||||
UNDO = 'Rückgängig machen',
|
|
||||||
REDO = 'Wiederholen',
|
|
||||||
INSERT = 'Einfügen',
|
|
||||||
SAVE = 'Sichern',
|
|
||||||
NOTE = 'Notiz',
|
|
||||||
ADD_TOPIC = 'Thema hinzufügen',
|
|
||||||
LOADING = 'Laden ...',
|
|
||||||
EXPORT = 'Exportieren',
|
|
||||||
PRINT = 'Drucken',
|
|
||||||
PUBLISH = 'Publizieren',
|
|
||||||
COLLABORATE = 'Mitbenutzen',
|
|
||||||
HISTORY = 'Historie',
|
|
||||||
DISCARD_CHANGES = 'Änderungen verwerfen',
|
|
||||||
FONT_COLOR = 'Textfarbe',
|
|
||||||
SAVING = 'Sichern ...',
|
|
||||||
SAVE_COMPLETE = 'Sichern abgeschlossen',
|
|
||||||
ZOOM_IN_ERROR = 'Zoom zu hoch.',
|
|
||||||
ZOOM_ERROR = 'Es kann nicht weiter vergrößert bzw. verkelinert werden.',
|
|
||||||
ONLY_ONE_TOPIC_MUST_BE_SELECTED = 'Thema konnte nicht angelegt werden. Bitte wählen Sie nur ein Thema aus.',
|
|
||||||
ONE_TOPIC_MUST_BE_SELECTED = 'Thema konnte nicht angelegt werden. Es muss ein Thema ausgewählt werden.',
|
|
||||||
ONLY_ONE_TOPIC_MUST_BE_SELECTED_COLLAPSE = 'Kinderknoten können nicht eingefaltet werden. Es muss ein Thema ausgewäht werden.',
|
|
||||||
SAVE_COULD_NOT_BE_COMPLETED = 'Sichern wurde nicht abgeschlossen. Versuchen Sie es später nocheinmal.',
|
|
||||||
UNEXPECTED_ERROR_LOADING = 'E tut uns Leid, ein unerwarteter Fehler ist aufgetreten.\nVersuchen Sie, den Editor neu zu laden. Falls das Problem erneut auftritt, bitte kontaktieren Sie uns unter support@wisemapping.com.',
|
|
||||||
MAIN_TOPIC = 'Hauptthema',
|
|
||||||
SUB_TOPIC = 'Unterthema',
|
|
||||||
ISOLATED_TOPIC = 'Isoliertes Thema',
|
|
||||||
CENTRAL_TOPIC = 'Zentrales Thema',
|
|
||||||
SHORTCUTS = 'Tastaturkürzel',
|
|
||||||
ENTITIES_COULD_NOT_BE_DELETED = 'Konnte das Thema oder die Beziehung nicht löschen. Es muss mindest ein Eintrag ausgewählt sein.',
|
|
||||||
AT_LEAST_ONE_TOPIC_MUST_BE_SELECTED = 'Es muss mindestens ein Thema ausgewählt sein.',
|
|
||||||
CLIPBOARD_IS_EMPTY = 'Es gibt nichts zu kopieren. Die Zwischenablage ist leer.',
|
|
||||||
CENTRAL_TOPIC_CAN_NOT_BE_DELETED = 'Das zentrale Thema kann nicht gelöscht werden.',
|
|
||||||
RELATIONSHIP_COULD_NOT_BE_CREATED = 'Die Beziehung konnte nicht angelegt werden. Es muss erst ein Vater-Thema ausgewählt werden, um die Beziehung herzustellen.',
|
|
||||||
SELECTION_COPIED_TO_CLIPBOARD = 'Themen in der Zwischenablage',
|
|
||||||
WRITE_YOUR_TEXT_HERE = 'Schreiben Sie ihre Notiz hier ...',
|
|
||||||
REMOVE = 'Entfernen',
|
|
||||||
ACCEPT = 'Akzeptieren',
|
|
||||||
CANCEL = 'Abbrechen',
|
|
||||||
LINK = 'Verbindung',
|
|
||||||
OPEN_LINK = 'Öffne URL',
|
|
||||||
SESSION_EXPIRED = 'Ihre Sitzung ist abgelaufen, bitte melden Sie sich erneut an.',
|
|
||||||
URL_ERROR = 'URL nicht gültig',
|
|
||||||
ACTION = 'Aktion',
|
|
||||||
CREATE_SIBLING_TOPIC = 'Erzeuge ein Schwester Thema',
|
|
||||||
CREATE_CHILD_TOPIC = 'Eryeuge ein Unterthema',
|
|
||||||
DELETE_TOPIC = 'Lösche Thema',
|
|
||||||
EDIT_TOPIC_TEXT = 'Editiere Thematext',
|
|
||||||
JUST_START_TYPING = 'Einfach mit der Eingabe beginnen',
|
|
||||||
CANCEL_TEXT_CHANGES = 'Textänderungen abbrechen',
|
|
||||||
TOPIC_NAVIGATION = 'Themen Navigation',
|
|
||||||
ARROW_KEYS = 'Pfeiltasten',
|
|
||||||
SELECT_MULTIPLE_NODES = 'Wähle mehrfache Knoten aus',
|
|
||||||
UNDO_EDITION = 'Änderungen rückgängig machen',
|
|
||||||
REDO_EDITION = 'Änderung nochmal ausführen',
|
|
||||||
SELECT_ALL_TOPIC = 'Wähle alle Themen aus',
|
|
||||||
CHANGE_TEXT_BOLD = 'Ändere Text in fette Schrift',
|
|
||||||
SAVE_CHANGES = 'Änderungen sichern',
|
|
||||||
CHANGE_TEXT_ITALIC = 'Ändere Text in kursive Schrift',
|
|
||||||
DESELECT_ALL_TOPIC = 'Deselektiere alle Themen',
|
|
||||||
COLLAPSE_CHILDREN = 'Kindknoten zusammenklappen',
|
|
||||||
KEYBOARD_SHORTCUTS_MSG = 'Tastenkürzel helfen Zeit zu sparen und erlauben die Arbeit nur mit der Tatstatur, s.d. Sie niemals die Hand von der Tastatur nehmen müßen, um die Maus zu bedienen.',
|
|
||||||
COPY_AND_PASTE_TOPICS = 'Kopieren und Einsetzen von Themen',
|
|
||||||
MULTIPLE_LINES = 'Füge mehrer Textzeilen hinzu',
|
|
||||||
BACK_TO_MAP_LIST = 'Zurück zur Kartenliste',
|
|
||||||
KEYBOARD_SHOTCUTS = 'Tastaturkürzel',
|
|
@ -1,80 +0,0 @@
|
|||||||
ZOOM_IN = 'Zoom In',
|
|
||||||
ZOOM_OUT = 'Zoom Out',
|
|
||||||
TOPIC_SHAPE = 'Topic Shape',
|
|
||||||
TOPIC_ADD = 'Add Topic',
|
|
||||||
TOPIC_DELETE = 'Delete Topic',
|
|
||||||
TOPIC_ICON = 'Add Icon',
|
|
||||||
TOPIC_LINK = 'Add Link',
|
|
||||||
TOPIC_RELATIONSHIP = 'Relationship',
|
|
||||||
TOPIC_COLOR = 'Topic Color',
|
|
||||||
TOPIC_BORDER_COLOR = 'Topic Border Color',
|
|
||||||
TOPIC_NOTE = 'Add Note',
|
|
||||||
FONT_FAMILY = 'Font Type',
|
|
||||||
FONT_SIZE = 'Text Size',
|
|
||||||
FONT_BOLD = 'Text Bold',
|
|
||||||
FONT_ITALIC = 'Text Italic',
|
|
||||||
UNDO = 'Undo',
|
|
||||||
REDO = 'Redo',
|
|
||||||
INSERT = 'Insert',
|
|
||||||
SAVE = 'Save',
|
|
||||||
NOTE = 'Note',
|
|
||||||
ADD_TOPIC = 'Add Topic',
|
|
||||||
LOADING = 'Loading ...',
|
|
||||||
EXPORT = 'Export',
|
|
||||||
PRINT = 'Print',
|
|
||||||
PUBLISH = 'Publish',
|
|
||||||
COLLABORATE = 'Share',
|
|
||||||
HISTORY = 'History',
|
|
||||||
DISCARD_CHANGES = 'Discard Changes',
|
|
||||||
FONT_COLOR = 'Text Color',
|
|
||||||
SAVING = 'Saving ...',
|
|
||||||
SAVE_COMPLETE = 'Save Complete',
|
|
||||||
ZOOM_IN_ERROR = 'Zoom too high.',
|
|
||||||
ZOOM_ERROR = 'No more zoom can be applied.',
|
|
||||||
ONLY_ONE_TOPIC_MUST_BE_SELECTED = 'Could not create a topic. Only one topic must be selected.',
|
|
||||||
ONE_TOPIC_MUST_BE_SELECTED = 'Could not create a topic. One topic must be selected.',
|
|
||||||
ONLY_ONE_TOPIC_MUST_BE_SELECTED_COLLAPSE = 'Children can not be collapsed. One topic must be selected.',
|
|
||||||
SAVE_COULD_NOT_BE_COMPLETED = 'Save could not be completed, please try again latter.',
|
|
||||||
UNEXPECTED_ERROR_LOADING = "We're sorry, an unexpected error has occurred.\nTry again reloading the editor.If the problem persists, contact us to support@wisemapping.com.",
|
|
||||||
MAIN_TOPIC = 'Main Topic',
|
|
||||||
SUB_TOPIC = 'Sub Topic',
|
|
||||||
ISOLATED_TOPIC = 'Isolated Topic',
|
|
||||||
CENTRAL_TOPIC = 'Central Topic',
|
|
||||||
SHORTCUTS = 'Keyboard Shortcuts',
|
|
||||||
ENTITIES_COULD_NOT_BE_DELETED = 'Could not delete topic or relation. At least one map entity must be selected.',
|
|
||||||
AT_LEAST_ONE_TOPIC_MUST_BE_SELECTED = 'At least one topic must be selected.',
|
|
||||||
CLIPBOARD_IS_EMPTY = 'Nothing to copy. Clipboard is empty.',
|
|
||||||
CENTRAL_TOPIC_CAN_NOT_BE_DELETED = 'Central topic can not be deleted.',
|
|
||||||
RELATIONSHIP_COULD_NOT_BE_CREATED = 'Relationship could not be created. A parent relationship topic must be selected first.',
|
|
||||||
SELECTION_COPIED_TO_CLIPBOARD = 'Topics copied to the clipboard',
|
|
||||||
WRITE_YOUR_TEXT_HERE = 'Write your note here ...',
|
|
||||||
REMOVE = 'Remove',
|
|
||||||
ACCEPT = 'Accept',
|
|
||||||
CANCEL = 'Cancel',
|
|
||||||
LINK = 'Link',
|
|
||||||
OPEN_LINK = 'Open URL',
|
|
||||||
SESSION_EXPIRED = 'Your session has expired, please log-in again.',
|
|
||||||
URL_ERROR = 'URL not valid',
|
|
||||||
ACTION = 'Action',
|
|
||||||
CREATE_SIBLING_TOPIC = 'Create Sibling Topic',
|
|
||||||
CREATE_CHILD_TOPIC = 'Create Child Topic',
|
|
||||||
DELETE_TOPIC = 'Delete Topic',
|
|
||||||
EDIT_TOPIC_TEXT = 'Edit Topic Text',
|
|
||||||
JUST_START_TYPING = 'Just start typing',
|
|
||||||
CANCEL_TEXT_CHANGES = 'Cancel Text Changes',
|
|
||||||
TOPIC_NAVIGATION = 'Topic Navigation',
|
|
||||||
ARROW_KEYS = 'Arrow Keys',
|
|
||||||
SELECT_MULTIPLE_NODES = 'Select Multiple Nodes',
|
|
||||||
UNDO_EDITION = 'Undo Edition',
|
|
||||||
REDO_EDITION = 'Redo Edition',
|
|
||||||
SELECT_ALL_TOPIC = 'Select All Topic',
|
|
||||||
CHANGE_TEXT_BOLD = 'Change Text Bold Type',
|
|
||||||
SAVE_CHANGES = 'Save Changes',
|
|
||||||
CHANGE_TEXT_ITALIC = 'Change Text Italic',
|
|
||||||
DESELECT_ALL_TOPIC = 'Deselect All Topic',
|
|
||||||
COLLAPSE_CHILDREN = 'Collapse Children',
|
|
||||||
KEYBOARD_SHORTCUTS_MSG = 'Keyboard shortcuts can help you save time by allowing you to never take your hands off the keyboard to use the mouse.',
|
|
||||||
COPY_AND_PASTE_TOPICS = 'Copy and Paste Topics',
|
|
||||||
MULTIPLE_LINES = 'Add multiple text lines',
|
|
||||||
BACK_TO_MAP_LIST = 'Back to Maps List',
|
|
||||||
KEYBOARD_SHOTCUTS = 'Keyboard Shorcuts',
|
|
@ -1,80 +0,0 @@
|
|||||||
ZOOM_IN = 'Acercar',
|
|
||||||
ZOOM_OUT = 'Alejar',
|
|
||||||
TOPIC_SHAPE = 'Forma del Tópico',
|
|
||||||
TOPIC_ADD = 'Agregar Tópico',
|
|
||||||
TOPIC_DELETE = 'Borrar Tópico',
|
|
||||||
TOPIC_ICON = 'Agregar Icono',
|
|
||||||
TOPIC_LINK = 'Agregar Enlace',
|
|
||||||
TOPIC_RELATIONSHIP = 'Relación',
|
|
||||||
TOPIC_COLOR = 'Color Tópico',
|
|
||||||
TOPIC_BORDER_COLOR = 'Color del Borde',
|
|
||||||
TOPIC_NOTE = 'Agregar Nota',
|
|
||||||
FONT_FAMILY = 'Tipo de Fuente',
|
|
||||||
FONT_SIZE = 'Tamaño de Texto',
|
|
||||||
FONT_BOLD = 'Negrita',
|
|
||||||
FONT_ITALIC = 'Italica',
|
|
||||||
UNDO = 'Rehacer',
|
|
||||||
REDO = 'Deshacer',
|
|
||||||
INSERT = 'Insertar',
|
|
||||||
SAVE = 'Guardar',
|
|
||||||
NOTE = 'Nota',
|
|
||||||
ADD_TOPIC = 'Agregar Tópico',
|
|
||||||
LOADING = 'Cargando ...',
|
|
||||||
EXPORT = 'Exportar',
|
|
||||||
PRINT = 'Imprimir',
|
|
||||||
PUBLISH = 'Publicar',
|
|
||||||
COLLABORATE = 'Compartir',
|
|
||||||
HISTORY = 'Historial',
|
|
||||||
DISCARD_CHANGES = 'Descartar Cambios',
|
|
||||||
FONT_COLOR = 'Color de Texto',
|
|
||||||
SAVING = 'Grabando ...',
|
|
||||||
SAVE_COMPLETE = 'Grabado Completo',
|
|
||||||
ZOOM_IN_ERROR = 'El zoom es muy alto.',
|
|
||||||
ZOOM_ERROR = 'No es posible aplicar mas zoom.',
|
|
||||||
ONLY_ONE_TOPIC_MUST_BE_SELECTED = 'No ha sido posible crear un nuevo tópico. Sólo un tópico debe ser seleccionado.',
|
|
||||||
ONE_TOPIC_MUST_BE_SELECTED = 'No ha sido posible crear un nuevo tópico. Al menos un tópico debe ser seleccionado.',
|
|
||||||
ONLY_ONE_TOPIC_MUST_BE_SELECTED_COLLAPSE = 'Tópicos hijos no pueden ser colapsados. Sólo un tópico debe ser seleccionado.',
|
|
||||||
SAVE_COULD_NOT_BE_COMPLETED = 'Grabación no pudo ser completada. Intentelo mas tarde.',
|
|
||||||
UNEXPECTED_ERROR_LOADING = 'Lo sentimos, un error inesperado ha ocurrido. Intentelo nuevamente recargando el editor. Si el problema persiste, contactenos a support@wisemapping.com.',
|
|
||||||
MAIN_TOPIC = 'Tópico Principal',
|
|
||||||
SUB_TOPIC = 'Tópico Secundario',
|
|
||||||
ISOLATED_TOPIC = 'Tópico Aislado',
|
|
||||||
CENTRAL_TOPIC = 'Tópico Central',
|
|
||||||
SHORTCUTS = 'Accesos directos',
|
|
||||||
ENTITIES_COULD_NOT_BE_DELETED = 'El tópico o la relación no pudo ser borrada. Debe selecionar al menos una.',
|
|
||||||
AT_LEAST_ONE_TOPIC_MUST_BE_SELECTED = 'Al menos un tópico debe ser seleccionado.',
|
|
||||||
CLIPBOARD_IS_EMPTY = 'Nada que copiar. Clipboard está vacio.',
|
|
||||||
CENTRAL_TOPIC_CAN_NOT_BE_DELETED = 'El tópico central no puede ser borrado.',
|
|
||||||
RELATIONSHIP_COULD_NOT_BE_CREATED = 'La relación no pudo ser creada. Una relación padre debe ser seleccionada primero.',
|
|
||||||
SELECTION_COPIED_TO_CLIPBOARD = 'Tópicos copiados al clipboard',
|
|
||||||
WRITE_YOUR_TEXT_HERE = 'Escribe tu nota aquí ...',
|
|
||||||
REMOVE = 'Borrar',
|
|
||||||
ACCEPT = 'Aceptar',
|
|
||||||
CANCEL = 'Cancelar',
|
|
||||||
LINK = 'Enlace',
|
|
||||||
OPEN_LINK = 'Abrir Enlace',
|
|
||||||
SESSION_EXPIRED = 'Su session ha expirado. Por favor, ingrese nuevamente.',
|
|
||||||
URL_ERROR = 'URL no válida',
|
|
||||||
ACTION = 'Acción',
|
|
||||||
CREATE_SIBLING_TOPIC = 'Agregar Tópico Hermano',
|
|
||||||
CREATE_CHILD_TOPIC = 'Agregar Tópico Hijo',
|
|
||||||
DELETE_TOPIC = 'Borrar Tópico',
|
|
||||||
EDIT_TOPIC_TEXT = 'Editar Texto de Tópico',
|
|
||||||
JUST_START_TYPING = 'Comenza a escribir',
|
|
||||||
CANCEL_TEXT_CHANGES = 'Cancelar Edición de Texto',
|
|
||||||
TOPIC_NAVIGATION = 'Navegación Entre Tópicos',
|
|
||||||
ARROW_KEYS = 'Flechas Del Cursor',
|
|
||||||
SELECT_MULTIPLE_NODES = 'Selecciónar Multiples Tópicos',
|
|
||||||
UNDO_EDITION = 'Revertir Cambios',
|
|
||||||
REDO_EDITION = 'Rehacer Cambios',
|
|
||||||
SELECT_ALL_TOPIC = 'Seleccionar Todos los Tópicos',
|
|
||||||
CHANGE_TEXT_BOLD = 'Cambiar Texto a Negrita',
|
|
||||||
SAVE_CHANGES = 'Guardar los Cambios',
|
|
||||||
CHANGE_TEXT_ITALIC = 'Cambiar Texto a Italica',
|
|
||||||
DESELECT_ALL_TOPIC = 'Revertir Selección de Tópicos',
|
|
||||||
COLLAPSE_CHILDREN = 'Colapsar Hijos',
|
|
||||||
KEYBOARD_SHORTCUTS_MSG = 'Los accesos directos pueden ayudarte a salvar tiempo permitiéndote no sacar las manos del teclado para usar el mouse.',
|
|
||||||
COPY_AND_PASTE_TOPICS = 'Copier et coller les noeuds',
|
|
||||||
MULTIPLE_LINES = 'Ajouter plusieurs lignes de texte',
|
|
||||||
BACK_TO_MAP_LIST = 'Volver a la lista de mapas',
|
|
||||||
KEYBOARD_SHOTCUTS = 'Métodos abreviados de teclado',
|
|
@ -1,80 +0,0 @@
|
|||||||
ZOOM_IN = 'Agrandir affichage',
|
|
||||||
ZOOM_OUT = 'Réduire affichage',
|
|
||||||
TOPIC_SHAPE = 'Forme du noeud',
|
|
||||||
TOPIC_ADD = 'Ajouter un noeud',
|
|
||||||
TOPIC_DELETE = 'Supprimer le noeud',
|
|
||||||
TOPIC_ICON = 'Ajouter une icône',
|
|
||||||
TOPIC_LINK = 'Ajouter un lien',
|
|
||||||
TOPIC_RELATIONSHIP = 'Relation du noeud',
|
|
||||||
TOPIC_COLOR = 'Couleur du noeud',
|
|
||||||
TOPIC_BORDER_COLOR = 'Couleur de bordure du noeud',
|
|
||||||
TOPIC_NOTE = 'Ajouter une note',
|
|
||||||
FONT_FAMILY = 'Type de police',
|
|
||||||
FONT_SIZE = 'Taille de police',
|
|
||||||
FONT_BOLD = 'Caractères gras',
|
|
||||||
FONT_ITALIC = 'Caractères italiques',
|
|
||||||
UNDO = 'Annuler',
|
|
||||||
REDO = 'Refaire',
|
|
||||||
INSERT = 'Insérer',
|
|
||||||
SAVE = 'Enregistrer',
|
|
||||||
NOTE = 'Note',
|
|
||||||
ADD_TOPIC = 'Ajouter un noeud',
|
|
||||||
LOADING = 'Chargement ...',
|
|
||||||
EXPORT = 'Exporter',
|
|
||||||
PRINT = 'Imprimer',
|
|
||||||
PUBLISH = 'Publier',
|
|
||||||
COLLABORATE = 'Partager',
|
|
||||||
HISTORY = 'Historique',
|
|
||||||
DISCARD_CHANGES = 'Annuler les changements',
|
|
||||||
FONT_COLOR = 'Couleur de police',
|
|
||||||
SAVING = 'Enregistrement ...',
|
|
||||||
SAVE_COMPLETE = 'Enregistrement terminé',
|
|
||||||
ZOOM_IN_ERROR = 'Zoom trop grand.',
|
|
||||||
ZOOM_ERROR = 'Impossible de zoomer plus.',
|
|
||||||
ONLY_ONE_TOPIC_MUST_BE_SELECTED = 'Impossible de créer un noeud. Un seul noeud doit être sélectionné.',
|
|
||||||
ONE_TOPIC_MUST_BE_SELECTED = 'Impossible de créer un noeud. Un noeud parent doit être sélectionné au préalable.',
|
|
||||||
ONLY_ONE_TOPIC_MUST_BE_SELECTED_COLLAPSE = 'Un noeud enfant ne peut pas être réduit. Un noeud doit être sélectionné.',
|
|
||||||
SAVE_COULD_NOT_BE_COMPLETED = 'Enregistrement impossible. Essayer ultérieurement.',
|
|
||||||
UNEXPECTED_ERROR_LOADING = "Nous sommes désolés, une erreur vient de survenir.\nEssayez de recharger l'éditeur. Si le problème persiste, contactez-nous \= support@wisemapping.com.",
|
|
||||||
MAIN_TOPIC = 'Noeud titre principal',
|
|
||||||
SUB_TOPIC = 'Noeud sous-titre',
|
|
||||||
ISOLATED_TOPIC = 'Noeud isolé',
|
|
||||||
CENTRAL_TOPIC = 'Noeud racine',
|
|
||||||
SHORTCUTS = 'Raccourcis clavier',
|
|
||||||
ENTITIES_COULD_NOT_BE_DELETED = "Impossible d'effacer un noeud ou une relation. Au moins un objet de la carte doit être sélectionné.",
|
|
||||||
AT_LEAST_ONE_TOPIC_MUST_BE_SELECTED = 'Au moins un objet de la carte doit être sélectionné.',
|
|
||||||
CLIPBOARD_IS_EMPTY = 'Rien à copier. Presse-papier vide.',
|
|
||||||
CENTRAL_TOPIC_CAN_NOT_BE_DELETED = 'Le noeud racine ne peut pas être effacé.',
|
|
||||||
RELATIONSHIP_COULD_NOT_BE_CREATED = 'Impossible de créer relation. Un noeud parent doit être sélectionné au préalable.',
|
|
||||||
SELECTION_COPIED_TO_CLIPBOARD = 'Noeuds sélectionnés copiés dans le presse-papiers.',
|
|
||||||
WRITE_YOUR_TEXT_HERE = 'Écrivez votre texte ici ...',
|
|
||||||
REMOVE = 'Supprimer',
|
|
||||||
ACCEPT = 'Accepter',
|
|
||||||
CANCEL = 'Annuler',
|
|
||||||
LINK = 'Lien',
|
|
||||||
OPEN_LINK = 'Ouvrir le lien',
|
|
||||||
SESSION_EXPIRED = 'Votre session a expiré, veuillez vous reconnecter.',
|
|
||||||
URL_ERROR = 'URL non valide',
|
|
||||||
ACTION = 'Action',
|
|
||||||
CREATE_SIBLING_TOPIC = 'Créer noeud même niveau',
|
|
||||||
CREATE_CHILD_TOPIC = 'Créer noeud enfant',
|
|
||||||
DELETE_TOPIC = 'Détruire noeud ',
|
|
||||||
EDIT_TOPIC_TEXT = 'Editer texte du noeud',
|
|
||||||
JUST_START_TYPING = 'Commencer saisie',
|
|
||||||
CANCEL_TEXT_CHANGES = 'Annuler changement texte',
|
|
||||||
TOPIC_NAVIGATION = 'Navigation sur les noeuds',
|
|
||||||
ARROW_KEYS = 'Touches flèches',
|
|
||||||
SELECT_MULTIPLE_NODES = 'Selection multiple de noeuds',
|
|
||||||
UNDO_EDITION = 'Annuler édition',
|
|
||||||
REDO_EDITION = 'Refaire édition',
|
|
||||||
SELECT_ALL_TOPIC = 'Sélection tous noeuds',
|
|
||||||
CHANGE_TEXT_BOLD = 'Caractères en gras',
|
|
||||||
SAVE_CHANGES = 'Enregistrer changements',
|
|
||||||
CHANGE_TEXT_ITALIC = 'Caractères en italique',
|
|
||||||
DESELECT_ALL_TOPIC = 'Deselection tous noeuds',
|
|
||||||
COLLAPSE_CHILDREN = 'Fermer enfants',
|
|
||||||
KEYBOARD_SHORTCUTS_MSG = 'Les raccourcis clavier vous font gagner du temps, en vous permettant de garder les mains sur le clavier sans utiliser la souris.',
|
|
||||||
COPY_AND_PASTE_TOPICS = 'Copier et coller les noeuds',
|
|
||||||
MULTIPLE_LINES = 'Ajouter plusieurs lignes de texte',
|
|
||||||
BACK_TO_MAP_LIST = 'Retour à la liste des cartes',
|
|
||||||
KEYBOARD_SHOTCUTS = "Raccourcis clavier",
|
|
84
packages/mindplot/src/components/lang/ru.js
Normal file
84
packages/mindplot/src/components/lang/ru.js
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
const EN = {
|
||||||
|
ZOOM_IN: 'Zoom In',
|
||||||
|
ZOOM_OUT: 'Zoom Out',
|
||||||
|
TOPIC_SHAPE: 'Topic Shape',
|
||||||
|
TOPIC_ADD: 'Add Topic',
|
||||||
|
TOPIC_DELETE: 'Delete Topic',
|
||||||
|
TOPIC_ICON: 'Add Icon',
|
||||||
|
TOPIC_LINK: 'Add Link',
|
||||||
|
TOPIC_RELATIONSHIP: 'Relationship',
|
||||||
|
TOPIC_COLOR: 'Topic Color',
|
||||||
|
TOPIC_BORDER_COLOR: 'Topic Border Color',
|
||||||
|
TOPIC_NOTE: 'Add Note',
|
||||||
|
FONT_FAMILY: 'Font Type',
|
||||||
|
FONT_SIZE: 'Text Size',
|
||||||
|
FONT_BOLD: 'Text Bold',
|
||||||
|
FONT_ITALIC: 'Text Italic',
|
||||||
|
UNDO: 'Undo',
|
||||||
|
REDO: 'Redo',
|
||||||
|
INSERT: 'Insert',
|
||||||
|
SAVE: 'Save',
|
||||||
|
NOTE: 'Note',
|
||||||
|
ADD_TOPIC: 'Add Topic',
|
||||||
|
LOADING: 'Loading ...',
|
||||||
|
EXPORT: 'Export',
|
||||||
|
PRINT: 'Print',
|
||||||
|
PUBLISH: 'Publish',
|
||||||
|
COLLABORATE: 'Share',
|
||||||
|
HISTORY: 'History',
|
||||||
|
DISCARD_CHANGES: 'Discard Changes',
|
||||||
|
FONT_COLOR: 'Text Color',
|
||||||
|
SAVING: 'Saving ...',
|
||||||
|
SAVE_COMPLETE: 'Save Complete',
|
||||||
|
ZOOM_IN_ERROR: 'Zoom too high.',
|
||||||
|
ZOOM_ERROR: 'No more zoom can be applied.',
|
||||||
|
ONLY_ONE_TOPIC_MUST_BE_SELECTED: 'Could not create a topic. Only one topic must be selected.',
|
||||||
|
ONE_TOPIC_MUST_BE_SELECTED: 'Could not create a topic. One topic must be selected.',
|
||||||
|
ONLY_ONE_TOPIC_MUST_BE_SELECTED_COLLAPSE: 'Children can not be collapsed. One topic must be selected.',
|
||||||
|
SAVE_COULD_NOT_BE_COMPLETED: 'Save could not be completed, please try again latter.',
|
||||||
|
UNEXPECTED_ERROR_LOADING: "We're sorry, an unexpected error has occurred.\nTry again reloading the editor.If the problem persists, contact us to support@wisemapping.com.",
|
||||||
|
MAIN_TOPIC: 'Main Topic',
|
||||||
|
SUB_TOPIC: 'Sub Topic',
|
||||||
|
ISOLATED_TOPIC: 'Isolated Topic',
|
||||||
|
CENTRAL_TOPIC: 'Central Topic',
|
||||||
|
SHORTCUTS: 'Keyboard Shortcuts',
|
||||||
|
ENTITIES_COULD_NOT_BE_DELETED: 'Could not delete topic or relation. At least one map entity must be selected.',
|
||||||
|
AT_LEAST_ONE_TOPIC_MUST_BE_SELECTED: 'At least one topic must be selected.',
|
||||||
|
CLIPBOARD_IS_EMPTY: 'Nothing to copy. Clipboard is empty.',
|
||||||
|
CENTRAL_TOPIC_CAN_NOT_BE_DELETED: 'Central topic can not be deleted.',
|
||||||
|
RELATIONSHIP_COULD_NOT_BE_CREATED: 'Relationship could not be created. A parent relationship topic must be selected first.',
|
||||||
|
SELECTION_COPIED_TO_CLIPBOARD: 'Topics copied to the clipboard',
|
||||||
|
WRITE_YOUR_TEXT_HERE: 'Write your note here ...',
|
||||||
|
REMOVE: 'Remove',
|
||||||
|
ACCEPT: 'Accept',
|
||||||
|
CANCEL: 'Cancel',
|
||||||
|
LINK: 'Link',
|
||||||
|
OPEN_LINK: 'Open URL',
|
||||||
|
SESSION_EXPIRED: 'Your session has expired, please log-in again.',
|
||||||
|
URL_ERROR: 'URL not valid',
|
||||||
|
ACTION: 'Action',
|
||||||
|
CREATE_SIBLING_TOPIC: 'Create Sibling Topic',
|
||||||
|
CREATE_CHILD_TOPIC: 'Create Child Topic',
|
||||||
|
DELETE_TOPIC: 'Delete Topic',
|
||||||
|
EDIT_TOPIC_TEXT: 'Edit Topic Text',
|
||||||
|
JUST_START_TYPING: 'Just start typing',
|
||||||
|
CANCEL_TEXT_CHANGES: 'Cancel Text Changes',
|
||||||
|
TOPIC_NAVIGATION: 'Topic Navigation',
|
||||||
|
ARROW_KEYS: 'Arrow Keys',
|
||||||
|
SELECT_MULTIPLE_NODES: 'Select Multiple Nodes',
|
||||||
|
UNDO_EDITION: 'Undo Edition',
|
||||||
|
REDO_EDITION: 'Redo Edition',
|
||||||
|
SELECT_ALL_TOPIC: 'Select All Topic',
|
||||||
|
CHANGE_TEXT_BOLD: 'Change Text Bold Type',
|
||||||
|
SAVE_CHANGES: 'Save Changes',
|
||||||
|
CHANGE_TEXT_ITALIC: 'Change Text Italic',
|
||||||
|
DESELECT_ALL_TOPIC: 'Deselect All Topic',
|
||||||
|
COLLAPSE_CHILDREN: 'Collapse Children',
|
||||||
|
KEYBOARD_SHORTCUTS_MSG: 'Keyboard shortcuts can help you save time by allowing you to never take your hands off the keyboard to use the mouse.',
|
||||||
|
COPY_AND_PASTE_TOPICS: 'Copy and Paste Topics',
|
||||||
|
MULTIPLE_LINES: 'Add multiple text lines',
|
||||||
|
BACK_TO_MAP_LIST: 'Back to Maps List',
|
||||||
|
KEYBOARD_SHOTCUTS: 'Keyboard Shorcuts',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EN;
|
@ -18,6 +18,7 @@
|
|||||||
import { $assert, $defined } from '@wisemapping/core-js';
|
import { $assert, $defined } from '@wisemapping/core-js';
|
||||||
import PositionType from '../PositionType';
|
import PositionType from '../PositionType';
|
||||||
import SizeType from '../SizeType';
|
import SizeType from '../SizeType';
|
||||||
|
import ChildrenSorterStrategy from './ChildrenSorterStrategy';
|
||||||
|
|
||||||
class Node {
|
class Node {
|
||||||
private _id: number;
|
private _id: number;
|
||||||
@ -25,14 +26,14 @@ class Node {
|
|||||||
// eslint-disable-next-line no-use-before-define
|
// eslint-disable-next-line no-use-before-define
|
||||||
_parent: Node;
|
_parent: Node;
|
||||||
|
|
||||||
private _sorter: any;
|
private _sorter: ChildrenSorterStrategy;
|
||||||
|
|
||||||
private _properties;
|
private _properties;
|
||||||
|
|
||||||
// eslint-disable-next-line no-use-before-define
|
// eslint-disable-next-line no-use-before-define
|
||||||
_children: Node[];
|
_children: Node[];
|
||||||
|
|
||||||
constructor(id: number, size: SizeType, position, sorter) {
|
constructor(id: number, size: SizeType, position: PositionType, sorter: ChildrenSorterStrategy) {
|
||||||
$assert(typeof id === 'number' && Number.isFinite(id), 'id can not be null');
|
$assert(typeof id === 'number' && Number.isFinite(id), 'id can not be null');
|
||||||
$assert(size, 'size can not be null');
|
$assert(size, 'size can not be null');
|
||||||
$assert(position, 'position can not be null');
|
$assert(position, 'position can not be null');
|
||||||
|
@ -24,7 +24,6 @@ class RootedTreeSet {
|
|||||||
|
|
||||||
protected _children: Node[];
|
protected _children: Node[];
|
||||||
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this._rootNodes = [];
|
this._rootNodes = [];
|
||||||
}
|
}
|
||||||
@ -56,10 +55,9 @@ class RootedTreeSet {
|
|||||||
*/
|
*/
|
||||||
add(node: Node) {
|
add(node: Node) {
|
||||||
$assert(node, 'node can not be null');
|
$assert(node, 'node can not be null');
|
||||||
$assert(
|
if (this.find(node.getId(), false)) {
|
||||||
!this.find(node.getId(), false),
|
throw new Error(`node already exits with this id. Id:${node.getId()}: ${this.dump()}`);
|
||||||
`node already exits with this id. Id:${node.getId()}: ${this.dump()}`,
|
}
|
||||||
);
|
|
||||||
$assert(!node._children, 'node already added');
|
$assert(!node._children, 'node already added');
|
||||||
this._rootNodes.push(this._decodate(node));
|
this._rootNodes.push(this._decodate(node));
|
||||||
}
|
}
|
||||||
@ -131,10 +129,11 @@ class RootedTreeSet {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$assert(
|
|
||||||
validate ? result : true,
|
if (validate && !result) {
|
||||||
`node could not be found id:${id}\n,RootedTreeSet${this.dump()}`,
|
throw new Error(`node could not be found id:${id}\n,RootedTreeSet${this.dump()}`);
|
||||||
);
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,7 +292,7 @@ class RootedTreeSet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_plot(canvas, node: Node, root?) {
|
_plot(canvas, node: Node) {
|
||||||
const children = this.getChildren(node);
|
const children = this.getChildren(node);
|
||||||
const cx = node.getPosition().x + canvas.width / 2 - node.getSize().width / 2;
|
const cx = node.getPosition().x + canvas.width / 2 - node.getSize().width / 2;
|
||||||
const cy = node.getPosition().y + canvas.height / 2 - node.getSize().height / 2;
|
const cy = node.getPosition().y + canvas.height / 2 - node.getSize().height / 2;
|
||||||
|
@ -70,11 +70,6 @@ class NodeModel extends INodeModel {
|
|||||||
return this._features;
|
return this._features;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param feature
|
|
||||||
* @throws will throw an error if feature is null or undefined
|
|
||||||
* @throws will throw an error if the feature could not be removed
|
|
||||||
*/
|
|
||||||
removeFeature(feature: FeatureModel): void {
|
removeFeature(feature: FeatureModel): void {
|
||||||
$assert(feature, 'feature can not be null');
|
$assert(feature, 'feature can not be null');
|
||||||
const size = this._features.length;
|
const size = this._features.length;
|
||||||
@ -127,11 +122,8 @@ class NodeModel extends INodeModel {
|
|||||||
return this._properties[key];
|
return this._properties[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return {mindplot.model.NodeModel} an identical clone of the NodeModel
|
|
||||||
*/
|
|
||||||
clone(): NodeModel {
|
clone(): NodeModel {
|
||||||
const result = new NodeModel(this.getType(), this._mindmap, -1);
|
const result = new NodeModel(this.getType(), this._mindmap);
|
||||||
result._children = this._children.map((node) => {
|
result._children = this._children.map((node) => {
|
||||||
const cnode = node.clone() as NodeModel;
|
const cnode = node.clone() as NodeModel;
|
||||||
cnode._parent = result;
|
cnode._parent = result;
|
||||||
@ -143,12 +135,8 @@ class NodeModel extends INodeModel {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Similar to clone, assign new id to the elements ...
|
|
||||||
* @return {mindplot.model.NodeModel}
|
|
||||||
*/
|
|
||||||
deepCopy(): NodeModel {
|
deepCopy(): NodeModel {
|
||||||
const result = new NodeModel(this.getType(), this._mindmap, -1);
|
const result = new NodeModel(this.getType(), this._mindmap);
|
||||||
result._children = this._children.map((node) => {
|
result._children = this._children.map((node) => {
|
||||||
const cnode = (node as NodeModel).deepCopy();
|
const cnode = (node as NodeModel).deepCopy();
|
||||||
cnode._parent = result;
|
cnode._parent = result;
|
||||||
@ -163,20 +151,12 @@ class NodeModel extends INodeModel {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {mindplot.model.NodeModel} child
|
|
||||||
* @throws will throw an error if child is null, undefined or not a NodeModel object
|
|
||||||
*/
|
|
||||||
append(child: NodeModel): void {
|
append(child: NodeModel): void {
|
||||||
$assert(child && child.isNodeModel(), 'Only NodeModel can be appended to Mindmap object');
|
$assert(child && child.isNodeModel(), 'Only NodeModel can be appended to Mindmap object');
|
||||||
this._children.push(child);
|
this._children.push(child);
|
||||||
child._parent = this;
|
child._parent = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {mindplot.model.NodeModel} child
|
|
||||||
* @throws will throw an error if child is null, undefined or not a NodeModel object
|
|
||||||
*/
|
|
||||||
removeChild(child: NodeModel): void {
|
removeChild(child: NodeModel): void {
|
||||||
$assert(child && child.isNodeModel(), 'Only NodeModel can be appended to Mindmap object.');
|
$assert(child && child.isNodeModel(), 'Only NodeModel can be appended to Mindmap object.');
|
||||||
this._children = this._children.filter((c) => c !== child);
|
this._children = this._children.filter((c) => c !== child);
|
||||||
|
@ -67,7 +67,7 @@ class RelationshipModel {
|
|||||||
return this._id;
|
return this._id;
|
||||||
}
|
}
|
||||||
|
|
||||||
getLineType() {
|
getLineType(): number {
|
||||||
return this._lineType;
|
return this._lineType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ import { Mindmap } from '../..';
|
|||||||
import NodeModel from '../model/NodeModel';
|
import NodeModel from '../model/NodeModel';
|
||||||
import ModelCodeName from './ModelCodeName';
|
import ModelCodeName from './ModelCodeName';
|
||||||
import XMLMindmapSerializer from './XMLMindmapSerializer';
|
import XMLMindmapSerializer from './XMLMindmapSerializer';
|
||||||
import XMLSerializerPela from './XMLSerializerPela';
|
import XMLSerializerPela from './XMLSerializerTango';
|
||||||
|
|
||||||
class Beta2PelaMigrator implements XMLMindmapSerializer {
|
class Beta2PelaMigrator implements XMLMindmapSerializer {
|
||||||
private _betaSerializer: XMLMindmapSerializer;
|
private _betaSerializer: XMLMindmapSerializer;
|
||||||
|
@ -72,7 +72,7 @@ class Pela2TangoMigrator implements XMLMindmapSerializer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _fixPosition(mindmap: Mindmap) {
|
private _fixPosition(mindmap: Mindmap): void {
|
||||||
// Position was not required in previous versions. Try to synthesize one .
|
// Position was not required in previous versions. Try to synthesize one .
|
||||||
const centralNode = mindmap.getBranches()[0];
|
const centralNode = mindmap.getBranches()[0];
|
||||||
const children = centralNode.getChildren();
|
const children = centralNode.getChildren();
|
||||||
@ -83,7 +83,7 @@ class Pela2TangoMigrator implements XMLMindmapSerializer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_fixNodePosition(node: NodeModel, parentPosition: {x:number, y:number}) {
|
private _fixNodePosition(node: NodeModel, parentPosition: { x: number, y: number }): void {
|
||||||
// Position was not required in previous versions. Try to synthesize one .
|
// Position was not required in previous versions. Try to synthesize one .
|
||||||
let position = node.getPosition();
|
let position = node.getPosition();
|
||||||
if (!position) {
|
if (!position) {
|
||||||
|
@ -20,7 +20,6 @@ import ModelCodeName from './ModelCodeName';
|
|||||||
import Beta2PelaMigrator from './Beta2PelaMigrator';
|
import Beta2PelaMigrator from './Beta2PelaMigrator';
|
||||||
import Pela2TangoMigrator from './Pela2TangoMigrator';
|
import Pela2TangoMigrator from './Pela2TangoMigrator';
|
||||||
import XMLSerializerBeta from './XMLSerializerBeta';
|
import XMLSerializerBeta from './XMLSerializerBeta';
|
||||||
import XMLSerializerPela from './XMLSerializerPela';
|
|
||||||
import XMLSerializerTango from './XMLSerializerTango';
|
import XMLSerializerTango from './XMLSerializerTango';
|
||||||
import Mindmap from '../model/Mindmap';
|
import Mindmap from '../model/Mindmap';
|
||||||
import XMLMindmapSerializer from './XMLMindmapSerializer';
|
import XMLMindmapSerializer from './XMLMindmapSerializer';
|
||||||
@ -35,7 +34,7 @@ const codeToSerializer: { codeName: string, serializer, migrator }[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
codeName: ModelCodeName.PELA,
|
codeName: ModelCodeName.PELA,
|
||||||
serializer: XMLSerializerPela,
|
serializer: XMLSerializerTango,
|
||||||
migrator: Beta2PelaMigrator,
|
migrator: Beta2PelaMigrator,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1,517 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 { $assert, $defined, createDocument } from '@wisemapping/core-js';
|
|
||||||
import { Point } from '@wisemapping/web2d';
|
|
||||||
import Mindmap from '../model/Mindmap';
|
|
||||||
import { TopicShape } from '../model/INodeModel';
|
|
||||||
import ConnectionLine from '../ConnectionLine';
|
|
||||||
import FeatureModelFactory from '../model/FeatureModelFactory';
|
|
||||||
import NodeModel from '../model/NodeModel';
|
|
||||||
import RelationshipModel from '../model/RelationshipModel';
|
|
||||||
import XMLMindmapSerializer from './XMLMindmapSerializer';
|
|
||||||
import FeatureType from '../model/FeatureType';
|
|
||||||
|
|
||||||
class XMLSerializerPela implements XMLMindmapSerializer {
|
|
||||||
private static MAP_ROOT_NODE = 'map';
|
|
||||||
|
|
||||||
private _idsMap: Record<number, Element>;
|
|
||||||
|
|
||||||
toXML(mindmap: Mindmap): Document {
|
|
||||||
$assert(mindmap, 'Can not save a null mindmap');
|
|
||||||
|
|
||||||
const document = createDocument();
|
|
||||||
|
|
||||||
// Store map attributes ...
|
|
||||||
const mapElem = document.createElement('map');
|
|
||||||
const name = mindmap.getId();
|
|
||||||
if ($defined(name)) {
|
|
||||||
mapElem.setAttribute('name', this._rmXmlInv(name));
|
|
||||||
}
|
|
||||||
const version = mindmap.getVersion();
|
|
||||||
if ($defined(version)) {
|
|
||||||
mapElem.setAttribute('version', version);
|
|
||||||
}
|
|
||||||
|
|
||||||
document.appendChild(mapElem);
|
|
||||||
|
|
||||||
// Create branches ...
|
|
||||||
const topics = mindmap.getBranches();
|
|
||||||
topics.forEach((topic) => {
|
|
||||||
const topicDom = this._topicToXML(document, topic);
|
|
||||||
mapElem.appendChild(topicDom);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create Relationships
|
|
||||||
const relationships = mindmap.getRelationships();
|
|
||||||
relationships.forEach((relationship) => {
|
|
||||||
if (
|
|
||||||
mindmap.findNodeById(relationship.getFromNode()) !== null
|
|
||||||
&& mindmap.findNodeById(relationship.getToNode()) !== null
|
|
||||||
) {
|
|
||||||
// Isolated relationships are not persisted ....
|
|
||||||
const relationDom = XMLSerializerPela._relationshipToXML(document, relationship);
|
|
||||||
mapElem.appendChild(relationDom);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return document;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected _topicToXML(document: Document, topic: NodeModel) {
|
|
||||||
const parentTopic = document.createElement('topic');
|
|
||||||
|
|
||||||
// Set topic attributes...
|
|
||||||
if (topic.getType() === 'CentralTopic') {
|
|
||||||
parentTopic.setAttribute('central', 'true');
|
|
||||||
} else {
|
|
||||||
const pos = topic.getPosition();
|
|
||||||
parentTopic.setAttribute('position', `${pos.x},${pos.y}`);
|
|
||||||
|
|
||||||
const order = topic.getOrder();
|
|
||||||
if (typeof order === 'number' && Number.isFinite(order)) {
|
|
||||||
parentTopic.setAttribute('order', order.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const text = topic.getText();
|
|
||||||
if ($defined(text)) {
|
|
||||||
this._noteTextToXML(document, parentTopic, text);
|
|
||||||
}
|
|
||||||
|
|
||||||
const shape = topic.getShapeType();
|
|
||||||
if ($defined(shape)) {
|
|
||||||
parentTopic.setAttribute('shape', shape);
|
|
||||||
|
|
||||||
if (shape === TopicShape.IMAGE) {
|
|
||||||
const size = topic.getImageSize();
|
|
||||||
parentTopic.setAttribute(
|
|
||||||
'image',
|
|
||||||
`${size.width},${size.height}:${topic.getImageUrl()}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (topic.areChildrenShrunken() && topic.getType() !== 'CentralTopic') {
|
|
||||||
parentTopic.setAttribute('shrink', 'true');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Font properties ...
|
|
||||||
const id = topic.getId();
|
|
||||||
parentTopic.setAttribute('id', id.toString());
|
|
||||||
|
|
||||||
let font = '';
|
|
||||||
|
|
||||||
const fontFamily = topic.getFontFamily();
|
|
||||||
font += `${fontFamily || ''};`;
|
|
||||||
|
|
||||||
const fontSize = topic.getFontSize();
|
|
||||||
font += `${fontSize || ''};`;
|
|
||||||
|
|
||||||
const fontColor = topic.getFontColor();
|
|
||||||
font += `${fontColor || ''};`;
|
|
||||||
|
|
||||||
const fontWeight = topic.getFontWeight();
|
|
||||||
font += `${fontWeight || ''};`;
|
|
||||||
|
|
||||||
const fontStyle = topic.getFontStyle();
|
|
||||||
font += `${fontStyle || ''};`;
|
|
||||||
|
|
||||||
if (
|
|
||||||
$defined(fontFamily)
|
|
||||||
|| $defined(fontSize)
|
|
||||||
|| $defined(fontColor)
|
|
||||||
|| $defined(fontWeight)
|
|
||||||
|| $defined(fontStyle)
|
|
||||||
) {
|
|
||||||
parentTopic.setAttribute('fontStyle', font);
|
|
||||||
}
|
|
||||||
|
|
||||||
const bgColor = topic.getBackgroundColor();
|
|
||||||
if ($defined(bgColor)) {
|
|
||||||
parentTopic.setAttribute('bgColor', bgColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
const brColor = topic.getBorderColor();
|
|
||||||
if ($defined(brColor)) {
|
|
||||||
parentTopic.setAttribute('brColor', brColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
const metadata = topic.getMetadata();
|
|
||||||
if ($defined(metadata)) {
|
|
||||||
parentTopic.setAttribute('metadata', metadata);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialize features ...
|
|
||||||
const features = topic.getFeatures();
|
|
||||||
features.forEach((feature) => {
|
|
||||||
const featureType = feature.getType();
|
|
||||||
const featureDom = document.createElement(featureType);
|
|
||||||
const attributes = feature.getAttributes();
|
|
||||||
|
|
||||||
const attributesKeys = Object.keys(attributes);
|
|
||||||
for (let attrIndex = 0; attrIndex < attributesKeys.length; attrIndex++) {
|
|
||||||
const key = attributesKeys[attrIndex];
|
|
||||||
const value = attributes[key];
|
|
||||||
if (key === 'text') {
|
|
||||||
const cdata = document.createCDATASection(this._rmXmlInv(value));
|
|
||||||
featureDom.appendChild(cdata);
|
|
||||||
} else {
|
|
||||||
featureDom.setAttribute(key, this._rmXmlInv(value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
parentTopic.appendChild(featureDom);
|
|
||||||
});
|
|
||||||
|
|
||||||
// CHILDREN TOPICS
|
|
||||||
const childTopics = topic.getChildren();
|
|
||||||
childTopics.forEach((childTopic) => {
|
|
||||||
const childDom = this._topicToXML(document, childTopic);
|
|
||||||
parentTopic.appendChild(childDom);
|
|
||||||
});
|
|
||||||
|
|
||||||
return parentTopic;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected _noteTextToXML(document: Document, elem: Element, text: string) {
|
|
||||||
if (text.indexOf('\n') === -1) {
|
|
||||||
elem.setAttribute('text', this._rmXmlInv(text));
|
|
||||||
} else {
|
|
||||||
const textDom = document.createElement('text');
|
|
||||||
const cdata = document.createCDATASection(this._rmXmlInv(text));
|
|
||||||
textDom.appendChild(cdata);
|
|
||||||
elem.appendChild(textDom);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static _relationshipToXML(document: Document, relationship: RelationshipModel) {
|
|
||||||
const result = document.createElement('relationship');
|
|
||||||
result.setAttribute('srcTopicId', relationship.getFromNode().toString());
|
|
||||||
result.setAttribute('destTopicId', relationship.getToNode().toString());
|
|
||||||
|
|
||||||
const lineType = relationship.getLineType();
|
|
||||||
result.setAttribute('lineType', lineType.toString());
|
|
||||||
if (lineType === ConnectionLine.CURVED || lineType === ConnectionLine.SIMPLE_CURVED) {
|
|
||||||
if ($defined(relationship.getSrcCtrlPoint())) {
|
|
||||||
const srcPoint = relationship.getSrcCtrlPoint();
|
|
||||||
result.setAttribute(
|
|
||||||
'srcCtrlPoint',
|
|
||||||
`${Math.round(srcPoint.x)},${Math.round(srcPoint.y)}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if ($defined(relationship.getDestCtrlPoint())) {
|
|
||||||
const destPoint = relationship.getDestCtrlPoint();
|
|
||||||
result.setAttribute(
|
|
||||||
'destCtrlPoint',
|
|
||||||
`${Math.round(destPoint.x)},${Math.round(destPoint.y)}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.setAttribute('endArrow', String(relationship.getEndArrow()));
|
|
||||||
result.setAttribute('startArrow', String(relationship.getStartArrow()));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param dom
|
|
||||||
* @param mapId
|
|
||||||
* @throws will throw an error if dom is null or undefined
|
|
||||||
* @throws will throw an error if mapId is null or undefined
|
|
||||||
* @throws will throw an error if the document element is not consistent with a wisemap's root
|
|
||||||
* element
|
|
||||||
*/
|
|
||||||
loadFromDom(dom: Document, mapId: string) {
|
|
||||||
$assert(dom, 'dom can not be null');
|
|
||||||
$assert(mapId, 'mapId can not be null');
|
|
||||||
|
|
||||||
const rootElem = dom.documentElement;
|
|
||||||
|
|
||||||
// Is a wisemap?.
|
|
||||||
$assert(
|
|
||||||
rootElem.tagName === XMLSerializerPela.MAP_ROOT_NODE,
|
|
||||||
`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);
|
|
||||||
|
|
||||||
// Add all the topics nodes ...
|
|
||||||
const childNodes = Array.from(rootElem.childNodes);
|
|
||||||
const topicsNodes = childNodes
|
|
||||||
.filter(
|
|
||||||
(child: ChildNode) => child.nodeType === 1 && (child as Element).tagName === 'topic',
|
|
||||||
)
|
|
||||||
.map((c) => c as Element);
|
|
||||||
topicsNodes.forEach((child) => {
|
|
||||||
const topic = this._deserializeNode(child, mindmap);
|
|
||||||
mindmap.addBranch(topic);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Then all relationshops, they are connected to topics ...
|
|
||||||
const relationshipsNodes = childNodes
|
|
||||||
.filter(
|
|
||||||
(child: ChildNode) => child.nodeType === 1 && (child as Element).tagName === 'relationship',
|
|
||||||
)
|
|
||||||
.map((c) => c as Element);
|
|
||||||
relationshipsNodes.forEach((child) => {
|
|
||||||
try {
|
|
||||||
const relationship = XMLSerializerPela._deserializeRelationship(child, mindmap);
|
|
||||||
mindmap.addRelationship(relationship);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Clean up from the recursion ...
|
|
||||||
this._idsMap = null;
|
|
||||||
mindmap.setId(mapId);
|
|
||||||
return mindmap;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected _deserializeNode(domElem: Element, mindmap: Mindmap) {
|
|
||||||
const type = domElem.getAttribute('central') != null ? 'CentralTopic' : 'MainTopic';
|
|
||||||
|
|
||||||
// Load attributes...
|
|
||||||
let id: number | null = null;
|
|
||||||
if ($defined(domElem.getAttribute('id'))) {
|
|
||||||
id = Number.parseInt(domElem.getAttribute('id'), 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._idsMap[id]) {
|
|
||||||
id = null;
|
|
||||||
} else {
|
|
||||||
this._idsMap[id] = domElem;
|
|
||||||
}
|
|
||||||
|
|
||||||
const topic = mindmap.createNode(type, id);
|
|
||||||
|
|
||||||
// Set text property is it;s defined...
|
|
||||||
const text = domElem.getAttribute('text');
|
|
||||||
if ($defined(text) && text) {
|
|
||||||
topic.setText(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
const fontStyle = domElem.getAttribute('fontStyle');
|
|
||||||
if ($defined(fontStyle) && fontStyle) {
|
|
||||||
const font = fontStyle.split(';');
|
|
||||||
|
|
||||||
if (font[0]) {
|
|
||||||
topic.setFontFamily(font[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (font[1]) {
|
|
||||||
topic.setFontSize(Number.parseInt(font[1], 10));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (font[2]) {
|
|
||||||
topic.setFontColor(font[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (font[3]) {
|
|
||||||
topic.setFontWeight(font[3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (font[4]) {
|
|
||||||
topic.setFontStyle(font[4]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const shape = domElem.getAttribute('shape');
|
|
||||||
if ($defined(shape)) {
|
|
||||||
topic.setShapeType(shape);
|
|
||||||
|
|
||||||
if (shape === TopicShape.IMAGE) {
|
|
||||||
const image = domElem.getAttribute('image');
|
|
||||||
const size = image.substring(0, image.indexOf(':'));
|
|
||||||
const url = image.substring(image.indexOf(':') + 1, image.length);
|
|
||||||
topic.setImageUrl(url);
|
|
||||||
|
|
||||||
const split = size.split(',');
|
|
||||||
topic.setImageSize(Number.parseInt(split[0], 10), Number.parseInt(split[1], 10));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const bgColor = domElem.getAttribute('bgColor');
|
|
||||||
if ($defined(bgColor)) {
|
|
||||||
topic.setBackgroundColor(bgColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
const borderColor = domElem.getAttribute('brColor');
|
|
||||||
if ($defined(borderColor)) {
|
|
||||||
topic.setBorderColor(borderColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
const order = domElem.getAttribute('order');
|
|
||||||
if ($defined(order) && order !== 'NaN') {
|
|
||||||
// Hack for broken maps ...
|
|
||||||
topic.setOrder(parseInt(order, 10));
|
|
||||||
}
|
|
||||||
|
|
||||||
const isShrink = domElem.getAttribute('shrink');
|
|
||||||
// Hack: Some production maps has been stored with the central topic collapsed. This is a bug.
|
|
||||||
if ($defined(isShrink) && type !== 'CentralTopic') {
|
|
||||||
topic.setChildrenShrunken(Boolean(isShrink));
|
|
||||||
}
|
|
||||||
|
|
||||||
const position = domElem.getAttribute('position');
|
|
||||||
if ($defined(position)) {
|
|
||||||
const pos = position.split(',');
|
|
||||||
topic.setPosition(Number.parseInt(pos[0], 10), Number.parseInt(pos[1], 10));
|
|
||||||
}
|
|
||||||
|
|
||||||
const metadata = domElem.getAttribute('metadata');
|
|
||||||
if ($defined(metadata)) {
|
|
||||||
topic.setMetadata(metadata);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creating icons and children nodes
|
|
||||||
const children = Array.from(domElem.childNodes);
|
|
||||||
children.forEach((child) => {
|
|
||||||
if (child.nodeType === Node.ELEMENT_NODE) {
|
|
||||||
const elem = child as Element;
|
|
||||||
if (elem.tagName === 'topic') {
|
|
||||||
const childTopic = this._deserializeNode(elem, mindmap);
|
|
||||||
childTopic.connectTo(topic);
|
|
||||||
} else if (FeatureModelFactory.isSupported(elem.tagName)) {
|
|
||||||
// Load attributes ...
|
|
||||||
const namedNodeMap = elem.attributes;
|
|
||||||
const attributes: Record<string, string> = {};
|
|
||||||
|
|
||||||
for (let j = 0; j < namedNodeMap.length; j++) {
|
|
||||||
const attribute = namedNodeMap.item(j);
|
|
||||||
attributes[attribute.name] = attribute.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Has text node ?.
|
|
||||||
const textAttr = XMLSerializerPela._deserializeTextAttr(elem);
|
|
||||||
if (textAttr) {
|
|
||||||
attributes.text = textAttr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new element ....
|
|
||||||
const featureType = elem.tagName as FeatureType;
|
|
||||||
const feature = FeatureModelFactory.createModel(featureType, attributes);
|
|
||||||
topic.addFeature(feature);
|
|
||||||
} else if (elem.tagName === 'text') {
|
|
||||||
const nodeText = XMLSerializerPela._deserializeNodeText(child);
|
|
||||||
topic.setText(nodeText);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return topic;
|
|
||||||
}
|
|
||||||
|
|
||||||
static _deserializeTextAttr(domElem: Element): string {
|
|
||||||
let value = domElem.getAttribute('text');
|
|
||||||
if (!$defined(value)) {
|
|
||||||
const children = domElem.childNodes;
|
|
||||||
for (let i = 0; i < children.length; i++) {
|
|
||||||
const child = children[i];
|
|
||||||
if (child.nodeType === Node.CDATA_SECTION_NODE) {
|
|
||||||
value = child.nodeValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Notes must be decoded ...
|
|
||||||
value = unescape(value);
|
|
||||||
|
|
||||||
// Hack for empty nodes ...
|
|
||||||
if (value === '') {
|
|
||||||
value = ' ';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
static _deserializeNodeText(domElem) {
|
|
||||||
const children = domElem.childNodes;
|
|
||||||
let value = null;
|
|
||||||
for (let i = 0; i < children.length; i++) {
|
|
||||||
const child = children[i];
|
|
||||||
if (child.nodeType === Node.CDATA_SECTION_NODE) {
|
|
||||||
value = child.nodeValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
static _deserializeRelationship(domElement, mindmap) {
|
|
||||||
const srcId = Number.parseInt(domElement.getAttribute('srcTopicId'), 10);
|
|
||||||
const destId = Number.parseInt(domElement.getAttribute('destTopicId'), 10);
|
|
||||||
const lineType = Number.parseInt(domElement.getAttribute('lineType'), 10);
|
|
||||||
const srcCtrlPoint = domElement.getAttribute('srcCtrlPoint');
|
|
||||||
const destCtrlPoint = domElement.getAttribute('destCtrlPoint');
|
|
||||||
|
|
||||||
// If for some reason a relationship lines has source and dest nodes the same, don't import it.
|
|
||||||
if (srcId === destId) {
|
|
||||||
throw new Error('Invalid relationship, dest and source are equals');
|
|
||||||
}
|
|
||||||
// Is the connections points valid ?. If it's not, do not load the relationship ...
|
|
||||||
if (mindmap.findNodeById(srcId) == null || mindmap.findNodeById(destId) == null) {
|
|
||||||
throw new Error('Transition could not created, missing node for relationship');
|
|
||||||
}
|
|
||||||
|
|
||||||
const model = mindmap.createRelationship(srcId, destId);
|
|
||||||
model.setLineType(lineType);
|
|
||||||
if ($defined(srcCtrlPoint) && srcCtrlPoint !== '') {
|
|
||||||
model.setSrcCtrlPoint(Point.fromString(srcCtrlPoint));
|
|
||||||
}
|
|
||||||
if ($defined(destCtrlPoint) && destCtrlPoint !== '') {
|
|
||||||
model.setDestCtrlPoint(Point.fromString(destCtrlPoint));
|
|
||||||
}
|
|
||||||
model.setEndArrow('false');
|
|
||||||
model.setStartArrow('true');
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method ensures that the output String has only
|
|
||||||
* valid XML unicode characters as specified by the
|
|
||||||
* XML 1.0 standard. For reference, please see
|
|
||||||
* <a href="http://www.w3.org/TR/2000/REC-xml-20001006#NT-Char">the
|
|
||||||
* standard</a>. This method will return an empty
|
|
||||||
* String if the input is null or empty.
|
|
||||||
*
|
|
||||||
* @param in The String whose non-valid characters we want to remove.
|
|
||||||
* @return The in String, stripped of non-valid characters.
|
|
||||||
*/
|
|
||||||
protected _rmXmlInv(str: string) {
|
|
||||||
if (str == null || str === undefined) return null;
|
|
||||||
|
|
||||||
let result = '';
|
|
||||||
for (let i = 0; i < str.length; i++) {
|
|
||||||
const c = str.charCodeAt(i);
|
|
||||||
if (
|
|
||||||
c === 0x9
|
|
||||||
|| c === 0xa
|
|
||||||
|| c === 0xd
|
|
||||||
|| (c >= 0x20 && c <= 0xd7ff)
|
|
||||||
|| (c >= 0xe000 && c <= 0xfffd)
|
|
||||||
|| (c >= 0x10000 && c <= 0x10ffff)
|
|
||||||
) {
|
|
||||||
result += str.charAt(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default XMLSerializerPela;
|
|
@ -15,13 +15,506 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import XMLSerializerPela from './XMLSerializerPela';
|
import { $assert, $defined, createDocument } from '@wisemapping/core-js';
|
||||||
|
import { Point } from '@wisemapping/web2d';
|
||||||
|
import Mindmap from '../model/Mindmap';
|
||||||
|
import { TopicShape } from '../model/INodeModel';
|
||||||
|
import ConnectionLine from '../ConnectionLine';
|
||||||
|
import FeatureModelFactory from '../model/FeatureModelFactory';
|
||||||
|
import NodeModel from '../model/NodeModel';
|
||||||
|
import RelationshipModel from '../model/RelationshipModel';
|
||||||
|
import XMLMindmapSerializer from './XMLMindmapSerializer';
|
||||||
|
import FeatureType from '../model/FeatureType';
|
||||||
|
|
||||||
/**
|
class XMLSerializerTango implements XMLMindmapSerializer {
|
||||||
* This serializer works exactly the same way as for the former version Pela
|
private static MAP_ROOT_NODE = 'map';
|
||||||
*/
|
|
||||||
class XMLSerializerTango extends XMLSerializerPela {
|
private _idsMap: Record<number, Element>;
|
||||||
|
|
||||||
|
toXML(mindmap: Mindmap): Document {
|
||||||
|
$assert(mindmap, 'Can not save a null mindmap');
|
||||||
|
|
||||||
|
const document = createDocument();
|
||||||
|
|
||||||
|
// Store map attributes ...
|
||||||
|
const mapElem = document.createElement('map');
|
||||||
|
const name = mindmap.getId();
|
||||||
|
if ($defined(name)) {
|
||||||
|
mapElem.setAttribute('name', this._rmXmlInv(name));
|
||||||
|
}
|
||||||
|
const version = mindmap.getVersion();
|
||||||
|
if ($defined(version)) {
|
||||||
|
mapElem.setAttribute('version', version);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.appendChild(mapElem);
|
||||||
|
|
||||||
|
// Create branches ...
|
||||||
|
const topics = mindmap.getBranches();
|
||||||
|
topics.forEach((topic) => {
|
||||||
|
const topicDom = this._topicToXML(document, topic);
|
||||||
|
mapElem.appendChild(topicDom);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create Relationships
|
||||||
|
const relationships = mindmap.getRelationships();
|
||||||
|
relationships.forEach((relationship) => {
|
||||||
|
if (
|
||||||
|
mindmap.findNodeById(relationship.getFromNode()) !== null
|
||||||
|
&& mindmap.findNodeById(relationship.getToNode()) !== null
|
||||||
|
) {
|
||||||
|
// Isolated relationships are not persisted ....
|
||||||
|
const relationDom = XMLSerializerTango._relationshipToXML(document, relationship);
|
||||||
|
mapElem.appendChild(relationDom);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return document;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _topicToXML(document: Document, topic: NodeModel) {
|
||||||
|
const parentTopic = document.createElement('topic');
|
||||||
|
|
||||||
|
// Set topic attributes...
|
||||||
|
if (topic.getType() === 'CentralTopic') {
|
||||||
|
parentTopic.setAttribute('central', 'true');
|
||||||
|
} else {
|
||||||
|
const pos = topic.getPosition();
|
||||||
|
parentTopic.setAttribute('position', `${pos.x},${pos.y}`);
|
||||||
|
|
||||||
|
const order = topic.getOrder();
|
||||||
|
if (typeof order === 'number' && Number.isFinite(order)) {
|
||||||
|
parentTopic.setAttribute('order', order.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const text = topic.getText();
|
||||||
|
if ($defined(text)) {
|
||||||
|
this._noteTextToXML(document, parentTopic, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
const shape = topic.getShapeType();
|
||||||
|
if ($defined(shape)) {
|
||||||
|
parentTopic.setAttribute('shape', shape);
|
||||||
|
|
||||||
|
if (shape === TopicShape.IMAGE) {
|
||||||
|
const size = topic.getImageSize();
|
||||||
|
parentTopic.setAttribute(
|
||||||
|
'image',
|
||||||
|
`${size.width},${size.height}:${topic.getImageUrl()}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (topic.areChildrenShrunken() && topic.getType() !== 'CentralTopic') {
|
||||||
|
parentTopic.setAttribute('shrink', 'true');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Font properties ...
|
||||||
|
const id = topic.getId();
|
||||||
|
parentTopic.setAttribute('id', id.toString());
|
||||||
|
|
||||||
|
let font = '';
|
||||||
|
|
||||||
|
const fontFamily = topic.getFontFamily();
|
||||||
|
font += `${fontFamily || ''};`;
|
||||||
|
|
||||||
|
const fontSize = topic.getFontSize();
|
||||||
|
font += `${fontSize || ''};`;
|
||||||
|
|
||||||
|
const fontColor = topic.getFontColor();
|
||||||
|
font += `${fontColor || ''};`;
|
||||||
|
|
||||||
|
const fontWeight = topic.getFontWeight();
|
||||||
|
font += `${fontWeight || ''};`;
|
||||||
|
|
||||||
|
const fontStyle = topic.getFontStyle();
|
||||||
|
font += `${fontStyle || ''};`;
|
||||||
|
|
||||||
|
if (
|
||||||
|
$defined(fontFamily)
|
||||||
|
|| $defined(fontSize)
|
||||||
|
|| $defined(fontColor)
|
||||||
|
|| $defined(fontWeight)
|
||||||
|
|| $defined(fontStyle)
|
||||||
|
) {
|
||||||
|
parentTopic.setAttribute('fontStyle', font);
|
||||||
|
}
|
||||||
|
|
||||||
|
const bgColor = topic.getBackgroundColor();
|
||||||
|
if ($defined(bgColor)) {
|
||||||
|
parentTopic.setAttribute('bgColor', bgColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
const brColor = topic.getBorderColor();
|
||||||
|
if ($defined(brColor)) {
|
||||||
|
parentTopic.setAttribute('brColor', brColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
const metadata = topic.getMetadata();
|
||||||
|
if ($defined(metadata)) {
|
||||||
|
parentTopic.setAttribute('metadata', metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize features ...
|
||||||
|
const features = topic.getFeatures();
|
||||||
|
features.forEach((feature) => {
|
||||||
|
const featureType = feature.getType();
|
||||||
|
const featureDom = document.createElement(featureType);
|
||||||
|
const attributes = feature.getAttributes();
|
||||||
|
|
||||||
|
const attributesKeys = Object.keys(attributes);
|
||||||
|
for (let attrIndex = 0; attrIndex < attributesKeys.length; attrIndex++) {
|
||||||
|
const key = attributesKeys[attrIndex];
|
||||||
|
const value = attributes[key];
|
||||||
|
if (key === 'text') {
|
||||||
|
const cdata = document.createCDATASection(this._rmXmlInv(value));
|
||||||
|
featureDom.appendChild(cdata);
|
||||||
|
} else {
|
||||||
|
featureDom.setAttribute(key, this._rmXmlInv(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parentTopic.appendChild(featureDom);
|
||||||
|
});
|
||||||
|
|
||||||
|
// CHILDREN TOPICS
|
||||||
|
const childTopics = topic.getChildren();
|
||||||
|
childTopics.forEach((childTopic) => {
|
||||||
|
const childDom = this._topicToXML(document, childTopic);
|
||||||
|
parentTopic.appendChild(childDom);
|
||||||
|
});
|
||||||
|
|
||||||
|
return parentTopic;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _noteTextToXML(document: Document, elem: Element, text: string) {
|
||||||
|
if (text.indexOf('\n') === -1) {
|
||||||
|
elem.setAttribute('text', this._rmXmlInv(text));
|
||||||
|
} else {
|
||||||
|
const textDom = document.createElement('text');
|
||||||
|
const cdata = document.createCDATASection(this._rmXmlInv(text));
|
||||||
|
textDom.appendChild(cdata);
|
||||||
|
elem.appendChild(textDom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static _relationshipToXML(document: Document, relationship: RelationshipModel) {
|
||||||
|
const result = document.createElement('relationship');
|
||||||
|
result.setAttribute('srcTopicId', relationship.getFromNode().toString());
|
||||||
|
result.setAttribute('destTopicId', relationship.getToNode().toString());
|
||||||
|
|
||||||
|
const lineType = relationship.getLineType();
|
||||||
|
result.setAttribute('lineType', lineType.toString());
|
||||||
|
if (lineType === ConnectionLine.CURVED || lineType === ConnectionLine.SIMPLE_CURVED) {
|
||||||
|
if ($defined(relationship.getSrcCtrlPoint())) {
|
||||||
|
const srcPoint = relationship.getSrcCtrlPoint();
|
||||||
|
result.setAttribute(
|
||||||
|
'srcCtrlPoint',
|
||||||
|
`${Math.round(srcPoint.x)},${Math.round(srcPoint.y)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ($defined(relationship.getDestCtrlPoint())) {
|
||||||
|
const destPoint = relationship.getDestCtrlPoint();
|
||||||
|
result.setAttribute(
|
||||||
|
'destCtrlPoint',
|
||||||
|
`${Math.round(destPoint.x)},${Math.round(destPoint.y)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.setAttribute('endArrow', String(relationship.getEndArrow()));
|
||||||
|
result.setAttribute('startArrow', String(relationship.getStartArrow()));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadFromDom(dom: Document, mapId: string) {
|
||||||
|
$assert(dom, 'dom can not be null');
|
||||||
|
$assert(mapId, 'mapId can not be null');
|
||||||
|
|
||||||
|
const rootElem = dom.documentElement;
|
||||||
|
|
||||||
|
// Is a wisemap?.
|
||||||
|
$assert(
|
||||||
|
rootElem.tagName === XMLSerializerTango.MAP_ROOT_NODE,
|
||||||
|
`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);
|
||||||
|
|
||||||
|
// Add all the topics nodes ...
|
||||||
|
const childNodes = Array.from(rootElem.childNodes);
|
||||||
|
const topicsNodes = childNodes
|
||||||
|
.filter(
|
||||||
|
(child: ChildNode) => child.nodeType === 1 && (child as Element).tagName === 'topic',
|
||||||
|
)
|
||||||
|
.map((c) => c as Element);
|
||||||
|
topicsNodes.forEach((child) => {
|
||||||
|
const topic = this._deserializeNode(child, mindmap);
|
||||||
|
mindmap.addBranch(topic);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Then all relationshops, they are connected to topics ...
|
||||||
|
const relationshipsNodes = childNodes
|
||||||
|
.filter(
|
||||||
|
(child: ChildNode) => child.nodeType === 1 && (child as Element).tagName === 'relationship',
|
||||||
|
)
|
||||||
|
.map((c) => c as Element);
|
||||||
|
relationshipsNodes.forEach((child) => {
|
||||||
|
try {
|
||||||
|
const relationship = XMLSerializerTango._deserializeRelationship(child, mindmap);
|
||||||
|
mindmap.addRelationship(relationship);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Clean up from the recursion ...
|
||||||
|
this._idsMap = null;
|
||||||
|
mindmap.setId(mapId);
|
||||||
|
return mindmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _deserializeNode(domElem: Element, mindmap: Mindmap) {
|
||||||
|
const type = domElem.getAttribute('central') != null ? 'CentralTopic' : 'MainTopic';
|
||||||
|
|
||||||
|
// Load attributes...
|
||||||
|
let id: number | null = null;
|
||||||
|
if ($defined(domElem.getAttribute('id'))) {
|
||||||
|
id = Number.parseInt(domElem.getAttribute('id'), 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._idsMap[id]) {
|
||||||
|
id = null;
|
||||||
|
} else {
|
||||||
|
this._idsMap[id] = domElem;
|
||||||
|
}
|
||||||
|
|
||||||
|
const topic = mindmap.createNode(type, id);
|
||||||
|
|
||||||
|
// Set text property is it;s defined...
|
||||||
|
const text = domElem.getAttribute('text');
|
||||||
|
if ($defined(text) && text) {
|
||||||
|
topic.setText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
const fontStyle = domElem.getAttribute('fontStyle');
|
||||||
|
if ($defined(fontStyle) && fontStyle) {
|
||||||
|
const font = fontStyle.split(';');
|
||||||
|
|
||||||
|
if (font[0]) {
|
||||||
|
topic.setFontFamily(font[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (font[1]) {
|
||||||
|
topic.setFontSize(Number.parseInt(font[1], 10));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (font[2]) {
|
||||||
|
topic.setFontColor(font[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (font[3]) {
|
||||||
|
topic.setFontWeight(font[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (font[4]) {
|
||||||
|
topic.setFontStyle(font[4]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const shape = domElem.getAttribute('shape');
|
||||||
|
if ($defined(shape)) {
|
||||||
|
topic.setShapeType(shape);
|
||||||
|
|
||||||
|
if (shape === TopicShape.IMAGE) {
|
||||||
|
const image = domElem.getAttribute('image');
|
||||||
|
const size = image.substring(0, image.indexOf(':'));
|
||||||
|
const url = image.substring(image.indexOf(':') + 1, image.length);
|
||||||
|
topic.setImageUrl(url);
|
||||||
|
|
||||||
|
const split = size.split(',');
|
||||||
|
topic.setImageSize(Number.parseInt(split[0], 10), Number.parseInt(split[1], 10));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const bgColor = domElem.getAttribute('bgColor');
|
||||||
|
if ($defined(bgColor)) {
|
||||||
|
topic.setBackgroundColor(bgColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
const borderColor = domElem.getAttribute('brColor');
|
||||||
|
if ($defined(borderColor)) {
|
||||||
|
topic.setBorderColor(borderColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
const order = domElem.getAttribute('order');
|
||||||
|
if ($defined(order) && order !== 'NaN') {
|
||||||
|
// Hack for broken maps ...
|
||||||
|
topic.setOrder(parseInt(order, 10));
|
||||||
|
}
|
||||||
|
|
||||||
|
const isShrink = domElem.getAttribute('shrink');
|
||||||
|
// Hack: Some production maps has been stored with the central topic collapsed. This is a bug.
|
||||||
|
if ($defined(isShrink) && type !== 'CentralTopic') {
|
||||||
|
topic.setChildrenShrunken(Boolean(isShrink));
|
||||||
|
}
|
||||||
|
|
||||||
|
const position = domElem.getAttribute('position');
|
||||||
|
if ($defined(position)) {
|
||||||
|
const pos = position.split(',');
|
||||||
|
topic.setPosition(Number.parseInt(pos[0], 10), Number.parseInt(pos[1], 10));
|
||||||
|
}
|
||||||
|
|
||||||
|
const metadata = domElem.getAttribute('metadata');
|
||||||
|
if ($defined(metadata)) {
|
||||||
|
topic.setMetadata(metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creating icons and children nodes
|
||||||
|
const children = Array.from(domElem.childNodes);
|
||||||
|
children.forEach((child) => {
|
||||||
|
if (child.nodeType === Node.ELEMENT_NODE) {
|
||||||
|
const elem = child as Element;
|
||||||
|
if (elem.tagName === 'topic') {
|
||||||
|
const childTopic = this._deserializeNode(elem, mindmap);
|
||||||
|
childTopic.connectTo(topic);
|
||||||
|
} else if (FeatureModelFactory.isSupported(elem.tagName)) {
|
||||||
|
// Load attributes ...
|
||||||
|
const namedNodeMap = elem.attributes;
|
||||||
|
const attributes: Record<string, string> = {};
|
||||||
|
|
||||||
|
for (let j = 0; j < namedNodeMap.length; j++) {
|
||||||
|
const attribute = namedNodeMap.item(j);
|
||||||
|
attributes[attribute.name] = attribute.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Has text node ?.
|
||||||
|
const textAttr = XMLSerializerTango._deserializeTextAttr(elem);
|
||||||
|
if (textAttr) {
|
||||||
|
attributes.text = textAttr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new element ....
|
||||||
|
const featureType = elem.tagName as FeatureType;
|
||||||
|
const feature = FeatureModelFactory.createModel(featureType, attributes);
|
||||||
|
topic.addFeature(feature);
|
||||||
|
} else if (elem.tagName === 'text') {
|
||||||
|
const nodeText = XMLSerializerTango._deserializeNodeText(child);
|
||||||
|
topic.setText(nodeText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Workaround: for some reason, some saved maps have holes in the order.
|
||||||
|
if (topic.getType() !== 'CentralTopic') {
|
||||||
|
topic
|
||||||
|
.getChildren()
|
||||||
|
.forEach((child, index) => {
|
||||||
|
if (child.getOrder() !== index) {
|
||||||
|
child.setOrder(index);
|
||||||
|
console.log('Toppic with order sequence hole. Introducing auto recovery sequence fix.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return topic;
|
||||||
|
}
|
||||||
|
|
||||||
|
static _deserializeTextAttr(domElem: Element): string {
|
||||||
|
let value = domElem.getAttribute('text');
|
||||||
|
if (!$defined(value)) {
|
||||||
|
const children = domElem.childNodes;
|
||||||
|
for (let i = 0; i < children.length; i++) {
|
||||||
|
const child = children[i];
|
||||||
|
if (child.nodeType === Node.CDATA_SECTION_NODE) {
|
||||||
|
value = child.nodeValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Notes must be decoded ...
|
||||||
|
value = unescape(value);
|
||||||
|
|
||||||
|
// Hack for empty nodes ...
|
||||||
|
if (value === '') {
|
||||||
|
value = ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static _deserializeNodeText(domElem) {
|
||||||
|
const children = domElem.childNodes;
|
||||||
|
let value = null;
|
||||||
|
for (let i = 0; i < children.length; i++) {
|
||||||
|
const child = children[i];
|
||||||
|
if (child.nodeType === Node.CDATA_SECTION_NODE) {
|
||||||
|
value = child.nodeValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static _deserializeRelationship(domElement, mindmap) {
|
||||||
|
const srcId = Number.parseInt(domElement.getAttribute('srcTopicId'), 10);
|
||||||
|
const destId = Number.parseInt(domElement.getAttribute('destTopicId'), 10);
|
||||||
|
const lineType = Number.parseInt(domElement.getAttribute('lineType'), 10);
|
||||||
|
const srcCtrlPoint = domElement.getAttribute('srcCtrlPoint');
|
||||||
|
const destCtrlPoint = domElement.getAttribute('destCtrlPoint');
|
||||||
|
|
||||||
|
// If for some reason a relationship lines has source and dest nodes the same, don't import it.
|
||||||
|
if (srcId === destId) {
|
||||||
|
throw new Error('Invalid relationship, dest and source are equals');
|
||||||
|
}
|
||||||
|
// Is the connections points valid ?. If it's not, do not load the relationship ...
|
||||||
|
if (mindmap.findNodeById(srcId) == null || mindmap.findNodeById(destId) == null) {
|
||||||
|
throw new Error('Transition could not created, missing node for relationship');
|
||||||
|
}
|
||||||
|
|
||||||
|
const model = mindmap.createRelationship(srcId, destId);
|
||||||
|
model.setLineType(lineType);
|
||||||
|
if ($defined(srcCtrlPoint) && srcCtrlPoint !== '') {
|
||||||
|
model.setSrcCtrlPoint(Point.fromString(srcCtrlPoint));
|
||||||
|
}
|
||||||
|
if ($defined(destCtrlPoint) && destCtrlPoint !== '') {
|
||||||
|
model.setDestCtrlPoint(Point.fromString(destCtrlPoint));
|
||||||
|
}
|
||||||
|
model.setEndArrow('false');
|
||||||
|
model.setStartArrow('true');
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method ensures that the output String has only
|
||||||
|
* valid XML unicode characters as specified by the
|
||||||
|
* XML 1.0 standard. For reference, please see
|
||||||
|
* <a href="http://www.w3.org/TR/2000/REC-xml-20001006#NT-Char">the
|
||||||
|
* standard</a>. This method will return an empty
|
||||||
|
* String if the input is null or empty.
|
||||||
|
*
|
||||||
|
* @param in The String whose non-valid characters we want to remove.
|
||||||
|
* @return The in String, stripped of non-valid characters.
|
||||||
|
*/
|
||||||
|
protected _rmXmlInv(str: string) {
|
||||||
|
if (str == null || str === undefined) return null;
|
||||||
|
|
||||||
|
let result = '';
|
||||||
|
for (let i = 0; i < str.length; i++) {
|
||||||
|
const c = str.charCodeAt(i);
|
||||||
|
if (
|
||||||
|
c === 0x9
|
||||||
|
|| c === 0xa
|
||||||
|
|| c === 0xd
|
||||||
|
|| (c >= 0x20 && c <= 0xd7ff)
|
||||||
|
|| (c >= 0xe000 && c <= 0xfffd)
|
||||||
|
|| (c >= 0x10000 && c <= 0x10ffff)
|
||||||
|
) {
|
||||||
|
result += str.charAt(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line camelcase
|
|
||||||
export default XMLSerializerTango;
|
export default XMLSerializerTango;
|
||||||
|
@ -25,7 +25,8 @@ class AccountSettingsPanel extends ListToolbarPanel {
|
|||||||
// Overwite default behaviour ...
|
// Overwite default behaviour ...
|
||||||
},
|
},
|
||||||
setValue() {
|
setValue() {
|
||||||
window.location = '/c/logout';
|
const elem = document.getElementById('logoutFrom');
|
||||||
|
elem.submit();
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
super(elemId, model);
|
super(elemId, model);
|
||||||
@ -59,6 +60,7 @@ class AccountSettingsPanel extends ListToolbarPanel {
|
|||||||
content[0].innerHTML = `
|
content[0].innerHTML = `
|
||||||
<p style='text-align:center;font-weight:bold;'>${global.accountName}</p>
|
<p style='text-align:center;font-weight:bold;'>${global.accountName}</p>
|
||||||
<p>${global.accountEmail}</p>
|
<p>${global.accountEmail}</p>
|
||||||
|
<form action="/c/logout" method='POST' id="logoutFrom"></form>
|
||||||
<div id="account-logout" model='logout' style='text-align:center'>
|
<div id="account-logout" model='logout' style='text-align:center'>
|
||||||
Logout
|
Logout
|
||||||
</div>
|
</div>
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user