2021-07-16 16:41:58 +02:00
|
|
|
/*
|
|
|
|
* Copyright [2015] [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.
|
|
|
|
*/
|
2021-12-02 01:41:56 +01:00
|
|
|
import Events from '../Events';
|
|
|
|
import RootedTreeSet from './RootedTreeSet';
|
|
|
|
import OriginalLayout from './OriginalLayout';
|
|
|
|
import ChangeEvent from './ChangeEvent';
|
2021-07-16 16:41:58 +02:00
|
|
|
|
2021-09-02 18:32:23 +02:00
|
|
|
const LayoutManager = new Class(
|
2021-10-05 02:05:34 +02:00
|
|
|
/** @lends LayoutManager */ {
|
|
|
|
Extends: Events,
|
|
|
|
/**
|
2021-09-02 18:32:23 +02:00
|
|
|
* @constructs
|
|
|
|
* @extends mindplot.Events
|
|
|
|
* @param {} rootNodeId
|
|
|
|
* @param {} rootSize
|
|
|
|
* @throws will throw an error if the root node id is null or undefined
|
|
|
|
* @throws will throw an error if the root size is null
|
|
|
|
*/
|
2021-10-05 02:05:34 +02:00
|
|
|
initialize(rootNodeId, rootSize) {
|
|
|
|
$assert($defined(rootNodeId), 'rootNodeId can not be null');
|
|
|
|
$assert(rootSize, 'rootSize can not be null');
|
|
|
|
var position = position || { x: 0, y: 0 };
|
2021-09-02 18:32:23 +02:00
|
|
|
|
2021-10-05 02:05:34 +02:00
|
|
|
this._treeSet = new RootedTreeSet();
|
|
|
|
this._layout = new OriginalLayout(this._treeSet);
|
2021-09-02 18:32:23 +02:00
|
|
|
|
2021-10-05 02:05:34 +02:00
|
|
|
const rootNode = this._layout.createNode(rootNodeId, rootSize, position, 'root');
|
|
|
|
this._treeSet.setRoot(rootNode);
|
|
|
|
this._events = [];
|
|
|
|
},
|
2021-09-02 18:32:23 +02:00
|
|
|
|
2021-10-05 02:05:34 +02:00
|
|
|
/**
|
2021-09-02 18:32:23 +02:00
|
|
|
* @param id
|
|
|
|
* @param size
|
|
|
|
* @throws will throw an error if id is null or undefined
|
|
|
|
*/
|
2021-10-05 02:05:34 +02:00
|
|
|
updateNodeSize(id, size) {
|
|
|
|
$assert($defined(id), 'id can not be null');
|
2021-09-02 18:32:23 +02:00
|
|
|
|
2021-10-05 02:05:34 +02:00
|
|
|
const node = this._treeSet.find(id);
|
|
|
|
node.setSize(size);
|
|
|
|
},
|
2021-09-02 18:32:23 +02:00
|
|
|
|
2021-10-05 02:05:34 +02:00
|
|
|
/**
|
2021-09-02 18:32:23 +02:00
|
|
|
* @param id
|
|
|
|
* @param value
|
|
|
|
* @throws will throw an error if id is null or undefined
|
|
|
|
* @throws will throw an error if value is null or undefined
|
|
|
|
* @return this
|
|
|
|
*/
|
2021-10-05 02:05:34 +02:00
|
|
|
updateShrinkState(id, value) {
|
|
|
|
$assert($defined(id), 'id can not be null');
|
|
|
|
$assert($defined(value), 'value can not be null');
|
2021-09-02 18:32:23 +02:00
|
|
|
|
2021-10-05 02:05:34 +02:00
|
|
|
const node = this._treeSet.find(id);
|
|
|
|
node.setShrunken(value);
|
2021-09-02 18:32:23 +02:00
|
|
|
|
2021-10-05 02:05:34 +02:00
|
|
|
return this;
|
|
|
|
},
|
2021-09-02 18:32:23 +02:00
|
|
|
|
2021-10-05 02:05:34 +02:00
|
|
|
/**
|
2021-09-02 18:32:23 +02:00
|
|
|
* @param id
|
|
|
|
* @return {@link RootedTreeSet}.find(id)
|
|
|
|
*/
|
2021-10-05 02:05:34 +02:00
|
|
|
find(id) {
|
|
|
|
return this._treeSet.find(id);
|
|
|
|
},
|
2021-09-02 18:32:23 +02:00
|
|
|
|
2021-10-05 02:05:34 +02:00
|
|
|
/**
|
2021-09-02 18:32:23 +02:00
|
|
|
* @param id
|
|
|
|
* @param position
|
|
|
|
* @throws will throw an error if id is null or undefined
|
|
|
|
* @throws will throw an error if position is null or undefined
|
|
|
|
* @throws will throw an error if the position's x property is null or undefined
|
|
|
|
* @throws will throw an error if the position's y property is null or undefined
|
|
|
|
*/
|
2021-10-05 02:05:34 +02:00
|
|
|
moveNode(id, position) {
|
|
|
|
$assert($defined(id), 'id cannot be null');
|
|
|
|
$assert($defined(position), 'position cannot be null');
|
|
|
|
$assert($defined(position.x), 'x can not be null');
|
|
|
|
$assert($defined(position.y), 'y can not be null');
|
|
|
|
|
|
|
|
const node = this._treeSet.find(id);
|
|
|
|
// @Todo: this should not be here. This is broking the isolated node support...
|
|
|
|
// node.setFree(true);
|
|
|
|
// node.setFreeDisplacement({x:position.x - node.getPosition().x, y:position.y - node.getPosition().y});
|
|
|
|
node.setPosition(position);
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2021-09-02 18:32:23 +02:00
|
|
|
* @param parentId
|
|
|
|
* @param childId
|
|
|
|
* @param order
|
|
|
|
* @throws will throw an error if parentId is null or undefined
|
|
|
|
* @throws will throw an error if childId is null or undefined
|
|
|
|
* @throws will throw an error if order is null or undefined
|
|
|
|
* @return this
|
|
|
|
*/
|
2021-10-05 02:05:34 +02:00
|
|
|
connectNode(parentId, childId, order) {
|
|
|
|
$assert($defined(parentId), 'parentId cannot be null');
|
|
|
|
$assert($defined(childId), 'childId cannot be null');
|
|
|
|
$assert($defined(order), 'order cannot be null');
|
2021-09-02 18:32:23 +02:00
|
|
|
|
2021-10-05 02:05:34 +02:00
|
|
|
this._layout.connectNode(parentId, childId, order);
|
2021-09-02 18:32:23 +02:00
|
|
|
|
2021-10-05 02:05:34 +02:00
|
|
|
return this;
|
|
|
|
},
|
2021-09-02 18:32:23 +02:00
|
|
|
|
2021-10-05 02:05:34 +02:00
|
|
|
/**
|
2021-09-02 18:32:23 +02:00
|
|
|
* @param id
|
|
|
|
* @throws will throw an error if id is null or undefined
|
|
|
|
* @return this
|
|
|
|
*/
|
2021-10-05 02:05:34 +02:00
|
|
|
disconnectNode(id) {
|
|
|
|
$assert($defined(id), 'id can not be null');
|
|
|
|
this._layout.disconnectNode(id);
|
2021-09-02 18:32:23 +02:00
|
|
|
|
2021-10-05 02:05:34 +02:00
|
|
|
return this;
|
|
|
|
},
|
2021-09-02 18:32:23 +02:00
|
|
|
|
2021-10-05 02:05:34 +02:00
|
|
|
/**
|
2021-09-02 18:32:23 +02:00
|
|
|
* @param id
|
|
|
|
* @param size
|
|
|
|
* @param position
|
|
|
|
* @throws will throw an error if id is null or undefined
|
|
|
|
* @return this
|
|
|
|
*/
|
2021-10-05 02:05:34 +02:00
|
|
|
addNode(id, size, position) {
|
|
|
|
$assert($defined(id), 'id can not be null');
|
|
|
|
const result = this._layout.createNode(id, size, position, 'topic');
|
|
|
|
this._treeSet.add(result);
|
2021-09-02 18:32:23 +02:00
|
|
|
|
2021-10-05 02:05:34 +02:00
|
|
|
return this;
|
|
|
|
},
|
2021-09-02 18:32:23 +02:00
|
|
|
|
2021-10-05 02:05:34 +02:00
|
|
|
/**
|
2021-09-02 18:32:23 +02:00
|
|
|
* removes a node and its connection to parent if existing
|
|
|
|
* @param id
|
|
|
|
* @throws will throw an error if id is null or undefined
|
|
|
|
* @return this
|
|
|
|
*/
|
2021-10-05 02:05:34 +02:00
|
|
|
removeNode(id) {
|
|
|
|
$assert($defined(id), 'id can not be null');
|
|
|
|
const node = this._treeSet.find(id);
|
2021-09-02 18:32:23 +02:00
|
|
|
|
2021-10-05 02:05:34 +02:00
|
|
|
// Is It connected ?
|
|
|
|
if (this._treeSet.getParent(node)) {
|
|
|
|
this.disconnectNode(id);
|
|
|
|
}
|
2021-09-02 18:32:23 +02:00
|
|
|
|
2021-10-05 02:05:34 +02:00
|
|
|
// Remove the all the branch ...
|
|
|
|
this._treeSet.remove(id);
|
2021-09-02 18:32:23 +02:00
|
|
|
|
2021-10-05 02:05:34 +02:00
|
|
|
return this;
|
|
|
|
},
|
2021-09-02 18:32:23 +02:00
|
|
|
|
2021-10-05 02:05:34 +02:00
|
|
|
/**
|
2021-09-02 18:32:23 +02:00
|
|
|
* @param {Number} parentId
|
|
|
|
* @param {Number=} nodeId
|
|
|
|
* @param {String=} position the position to use as mindplot.layout.Node.properties position
|
|
|
|
* property as '(x,y)'
|
|
|
|
* @param {Boolean=} free true specifies free node positioning
|
|
|
|
* @throws will throw an error if parentId is null or undefined
|
|
|
|
*/
|
2021-10-05 02:05:34 +02:00
|
|
|
predict(parentId, nodeId, position, free) {
|
|
|
|
$assert($defined(parentId), 'parentId can not be null');
|
2021-09-02 18:32:23 +02:00
|
|
|
|
2021-10-05 02:05:34 +02:00
|
|
|
const parent = this._treeSet.find(parentId);
|
|
|
|
const node = nodeId ? this._treeSet.find(nodeId) : null;
|
|
|
|
const sorter = parent.getSorter();
|
2021-09-02 18:32:23 +02:00
|
|
|
|
2021-10-05 02:05:34 +02:00
|
|
|
const result = sorter.predict(this._treeSet, parent, node, position, free);
|
|
|
|
return { order: result[0], position: result[1] };
|
|
|
|
},
|
2021-09-02 18:32:23 +02:00
|
|
|
|
2021-10-05 02:05:34 +02:00
|
|
|
/**
|
2021-09-02 18:32:23 +02:00
|
|
|
* logs dump to console
|
|
|
|
*/
|
2021-10-05 02:05:34 +02:00
|
|
|
dump() {
|
|
|
|
console.log(this._treeSet.dump());
|
|
|
|
},
|
2021-09-02 18:32:23 +02:00
|
|
|
|
2021-10-05 02:05:34 +02:00
|
|
|
/**
|
2021-09-02 18:32:23 +02:00
|
|
|
* @param containerId
|
|
|
|
* @param {width:Number, height:Number} size
|
|
|
|
* @throws will throw an error if containerId is null or undefined
|
|
|
|
* @return canvas
|
|
|
|
*/
|
2021-10-05 02:05:34 +02:00
|
|
|
plot(containerId, size) {
|
2021-12-02 01:41:56 +01:00
|
|
|
// this method is only used from tests that include Raphael
|
|
|
|
if (!global.Raphael) {
|
|
|
|
console.warn('Raphael.js not found, exiting plot()');
|
|
|
|
return null;
|
|
|
|
}
|
2021-10-05 02:05:34 +02:00
|
|
|
$assert(containerId, 'containerId cannot be null');
|
|
|
|
size = size || { width: 200, height: 200 };
|
|
|
|
const squaresize = 10;
|
2021-12-02 01:41:56 +01:00
|
|
|
const canvas = global.Raphael(containerId, size.width, size.height);
|
2021-10-05 02:05:34 +02:00
|
|
|
canvas.drawGrid(
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
size.width,
|
|
|
|
size.height,
|
|
|
|
size.width / squaresize,
|
|
|
|
size.height / squaresize,
|
|
|
|
);
|
|
|
|
this._treeSet.plot(canvas);
|
|
|
|
|
|
|
|
return canvas;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2021-09-02 18:32:23 +02:00
|
|
|
* initializes the layout to be updated
|
|
|
|
* @param fireEvents
|
|
|
|
* @return this
|
|
|
|
*/
|
2021-10-05 02:05:34 +02:00
|
|
|
layout(fireEvents) {
|
|
|
|
// File repositioning ...
|
|
|
|
this._layout.layout();
|
2021-09-02 18:32:23 +02:00
|
|
|
|
2021-10-05 02:05:34 +02:00
|
|
|
// Collect changes ...
|
|
|
|
this._collectChanges();
|
2021-09-02 18:32:23 +02:00
|
|
|
|
2021-10-05 02:05:34 +02:00
|
|
|
if ($(fireEvents).length > 0 || fireEvents) {
|
|
|
|
this._flushEvents();
|
|
|
|
}
|
2021-07-16 16:41:58 +02:00
|
|
|
|
2021-10-05 02:05:34 +02:00
|
|
|
return this;
|
|
|
|
},
|
2021-09-02 18:32:23 +02:00
|
|
|
|
2021-10-05 02:05:34 +02:00
|
|
|
_flushEvents() {
|
|
|
|
_.each(
|
|
|
|
this._events,
|
|
|
|
function (event) {
|
|
|
|
this.fireEvent('change', event);
|
2021-09-02 18:32:23 +02:00
|
|
|
},
|
2021-10-05 02:05:34 +02:00
|
|
|
this,
|
|
|
|
);
|
|
|
|
this._events = [];
|
|
|
|
},
|
|
|
|
|
|
|
|
_collectChanges(nodes) {
|
|
|
|
if (!nodes) nodes = this._treeSet.getTreeRoots();
|
|
|
|
|
|
|
|
_.each(
|
|
|
|
nodes,
|
|
|
|
function (node) {
|
|
|
|
if (node.hasOrderChanged() || node.hasPositionChanged()) {
|
|
|
|
// Find or create a event ...
|
|
|
|
const id = node.getId();
|
|
|
|
let event = this._events.some((event) => event.id == id);
|
|
|
|
if (!event) {
|
|
|
|
event = new ChangeEvent(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update nodes ...
|
|
|
|
event.setOrder(node.getOrder());
|
|
|
|
event.setPosition(node.getPosition());
|
2021-09-02 18:32:23 +02:00
|
|
|
|
2021-10-05 02:05:34 +02:00
|
|
|
node.resetPositionState();
|
|
|
|
node.resetOrderState();
|
|
|
|
node.resetFreeState();
|
|
|
|
this._events.push(event);
|
|
|
|
}
|
|
|
|
this._collectChanges(this._treeSet.getChildren(node));
|
2021-09-02 18:32:23 +02:00
|
|
|
},
|
2021-10-05 02:05:34 +02:00
|
|
|
this,
|
|
|
|
);
|
|
|
|
},
|
|
|
|
},
|
2021-09-02 18:32:23 +02:00
|
|
|
);
|
2021-07-16 16:41:58 +02:00
|
|
|
|
|
|
|
export default LayoutManager;
|