Commit 518eec4e authored by techird's avatar techird

1.4.0

parents fe784d12 51eaa58a
......@@ -9,12 +9,12 @@ KityMinder 是一款强大的脑图可视化/编辑工具,由百度 FEX 团队
* 包括脑图数据的可视化展示(Json 格式)
* 包括简单的编辑功能(节点创建、编辑、删除)。更加强大编辑功能的 KityMinder 编辑器请移步 [kityminder-editor](https://github.com/fex-team/kityminder-editor)
* 不包含第三方格式(FreeMind、XMind、MindManager、纯文本、Markdown 等)的支持,可以加载 [kityminder-protocol](https://github.com/fex-team/kityminder-protocol) 来扩展第三方格式支持。
* 不包含第三方格式(FreeMind、XMind、MindManager)的支持,可以加载 [kityminder-protocol](https://github.com/fex-team/kityminder-third-party-protocol) 来扩展第三方格式支持。
* 不包含文件存储的支持,需要自行实现存储。可参照[百度脑图](https://github.com/fex-team/naotu.baidu.com)中的开源的 fio + 百度网盘方案进行实现。
## 使用
可以参考 [example.html](example.html) 进行使用,代码类似:
可以参考 [example.html](example.html) 进行使用
```js
<div id="minder-container"></div>
......@@ -59,6 +59,12 @@ npm install
grunt
```
想偷懒?可以用下面这个一行安装脚本:
```bash
https://gist.githubusercontent.com/techird/72b420c7ea05154ce821/raw/6416f2709ce82a3a0d86a50763de1ce3ca7f3ca2/setup-km-core
```
## 联系我们
问题和建议反馈:[Github Issues](https://github.com/fex-team/kityminder-core/issues)
......
......@@ -2,7 +2,7 @@
"name": "kityminder-core",
"title": "Kity Minder Core",
"description": "Powerful online mind graphic visualization and editor (command based)",
"version": "1.4.0",
"version": "1.4.1",
"main": [
"release/kityminder.core.min.js",
"release/kityminder.core.css"
......@@ -30,4 +30,19 @@
"name": "Baidu FEX",
"url": "http://fex.baidu.com"
}
],
"bugs": {
"url": "https://github.com/fex-team/kityminder-core/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/fex-team/kityminder-core.git"
},
"author": {
"name": "Baidu FEX",
"url": "http://fex.baidu.com"
},
"dependencies": {
"json-diff": "*"
}
}
/**
* @fileOverview
*
* 调试工具:为 kity.Box 提供一个可视化的渲染
*
* @author: techird
* @copyright: Baidu FEX, 2014
*/
define(function(require, exports, module) {
var kity = require('./kity');
var Minder = require('./minder');
if (location.href.indexOf('boxv') != -1) {
var vrect;
Object.defineProperty(kity.Box.prototype, 'visualization', {
get: function() {
if (!vrect) return null;
return vrect.setBox(this);
}
});
Minder.registerInitHook(function() {
this.on('paperrender', function() {
vrect = new kity.Rect();
vrect.fill('rgba(200, 200, 200, .5)');
vrect.stroke('orange');
this.getRenderContainer().addShape(vrect);
});
});
}
});
\ No newline at end of file
......@@ -134,23 +134,18 @@ define(function(require, exports, module) {
return false;
}
if (!this._hasEnterExecCommand && cmd.isNeedUndo()) {
if (!this._hasEnterExecCommand) {
this._hasEnterExecCommand = true;
stoped = this._fire(new MinderEvent('beforeExecCommand', eventParams, true));
if (!stoped) {
//保存场景
this._fire(new MinderEvent('saveScene'));
this._fire(new MinderEvent('preExecCommand', eventParams, false));
result = cmd.execute.apply(cmd, [me].concat(cmdArgs));
this._fire(new MinderEvent('execCommand', eventParams, false));
//保存场景
this._fire(new MinderEvent('saveScene'));
if (cmd.isContentChanged()) {
this._firePharse(new MinderEvent('contentchange'));
}
......
......@@ -46,12 +46,10 @@ define(function(require, exports, module) {
var exported = {};
exported.data = node.getData();
var childNodes = node.getChildren();
if (childNodes.length) {
exported.children = [];
for (var i = 0; i < childNodes.length; i++) {
exported.children.push(exportNode(childNodes[i]));
}
}
return exported;
}
......@@ -63,7 +61,7 @@ define(function(require, exports, module) {
json.theme = this.getTheme();
json.version = Minder.version;
return json;
return JSON.parse(JSON.stringify(json));
},
/**
......@@ -140,7 +138,7 @@ define(function(require, exports, module) {
*
* @param {string} protocol 指定的数据协议(默认内置五种数据协议 `json`、`text`、`markdown`、`svg` 和 `png`)
*/
exportData: function(protocolName) {
exportData: function(protocolName, option) {
var json, protocol;
json = this.exportJson();
......@@ -161,7 +159,7 @@ define(function(require, exports, module) {
protocol: protocol
}));
return Promise.resolve(protocol.encode(json, this));
return Promise.resolve(protocol.encode(json, this, option));
},
/**
......@@ -174,7 +172,7 @@ define(function(require, exports, module) {
* @param {string} protocol 指定的用于解析数据的数据协议(默认内置三种数据协议 `json`、`text` 和 `markdown` 的支持)
* @param {any} data 要导入的数据
*/
importData: function(protocolName, data) {
importData: function(protocolName, data, option) {
var json, protocol;
var minder = this;
......@@ -196,7 +194,7 @@ define(function(require, exports, module) {
// 导入前抛事件
this._fire(new MinderEvent('beforeimport', params));
return Promise.resolve(protocol.decode(data, this)).then(function(json) {
return Promise.resolve(protocol.decode(data, this, option)).then(function(json) {
minder.importJson(json);
return json;
});
......
......@@ -34,7 +34,7 @@ define(function(require, exports, module) {
* children[i].setLayoutTransform(new kity.Matrix().translate(x, y));
* }
*/
doLayout: function(node) {
doLayout: function(parent, children) {
throw new Error('Not Implement: Layout.doLayout()');
},
......@@ -234,10 +234,6 @@ define(function(require, exports, module) {
return this.parent.getLayoutInstance().getOrderHint(this);
},
getExpandPosition: function() {
return this.getLayoutInstance().getExpandPosition();
},
/**
* 获取当前节点相对于父节点的布局变换
*/
......@@ -255,7 +251,7 @@ define(function(require, exports, module) {
var matrix = this.getLayoutTransform();
var offset = this.getLayoutOffset();
if (offset) {
matrix.translate(offset.x, offset.y);
matrix = matrix.clone().translate(offset.x, offset.y);
}
return pMatrix.merge(matrix);
},
......@@ -359,16 +355,10 @@ define(function(require, exports, module) {
setLayoutOffset: function(p) {
if (!this.parent) return this;
if (p && !this.hasLayoutOffset()) {
var m = this.getLayoutTransform().m;
p = p.offset(m.e, m.f);
this.setLayoutTransform(null);
}
this.setData('layout_' + this.parent.getLayout() + '_offset', p ? {
x: p.x,
y: p.y
} : null);
} : undefined);
return this;
},
......@@ -421,7 +411,7 @@ define(function(require, exports, module) {
var childrenInFlow = node.getChildren().filter(function(child) {
return !child.hasLayoutOffset();
});
layout.doLayout(node, childrenInFlow, round);
layout.doLayout(node, node.getChildren(), round);
}
// 第一轮布局
......@@ -471,7 +461,7 @@ define(function(require, exports, module) {
}
function apply(node, pMatrix) {
var matrix = node.getLayoutTransform().merge(pMatrix);
var matrix = node.getLayoutTransform().merge(pMatrix.clone());
var lastMatrix = node.getGlobalLayoutTransform() || new kity.Matrix();
var offset = node.getLayoutOffset();
......@@ -480,7 +470,6 @@ define(function(require, exports, module) {
matrix.m.e = Math.round(matrix.m.e);
matrix.m.f = Math.round(matrix.m.f);
// 如果当前有动画,停止动画
if (node._layoutTimeline) {
node._layoutTimeline.stop();
......@@ -518,10 +507,9 @@ define(function(require, exports, module) {
apply(node.children[i], matrix);
}
}
apply(root, root.parent ? root.parent.getGlobalLayoutTransform() : new kity.Matrix());
return this;
},
}
});
module.exports = Layout;
......
......@@ -72,6 +72,16 @@ define(function(require, exports, module) {
return this.parent;
},
getSiblings: function() {
var children = this.parent.children;
var siblings = [];
var self = this;
children.forEach(function(child) {
if (child != self) siblings.push(child);
});
return siblings;
},
/**
* 获得节点的深度
*/
......@@ -122,7 +132,16 @@ define(function(require, exports, module) {
},
setData: function(key, value) {
if (typeof key == 'object') {
var data = key;
for (key in data) if (data.hasOwnProperty(key)) {
this.data[key] = data[key];
}
}
else {
this.data[key] = value;
}
return this;
},
/**
......@@ -305,6 +324,29 @@ define(function(require, exports, module) {
root.minder = this;
},
getAllNode: function() {
var nodes = [];
this.getRoot().traverse(function(node) {
nodes.push(node);
});
return nodes;
},
getNodeById: function(id) {
return this.getNodesById([id])[0];
},
getNodesById: function(ids) {
var nodes = this.getAllNode();
var result = [];
nodes.forEach(function(node) {
if (ids.indexOf(node.getData('id')) != -1) {
result.push(node);
}
});
return result;
},
createNode: function(textOrData, parent, index) {
var node = new MinderNode(textOrData);
this.fire('nodecreate', {
......
/**
* @fileOverview
*
* 打补丁
*
* @author: techird
* @copyright: Baidu FEX, 2014
*/
define(function(require, exports, module) {
var kity = require('./kity');
var Minder = require('./minder');
function insertNode(minder, info, parent, index) {
parent = minder.createNode(info.data, parent, index);
info.children.forEach(function(childInfo, index) {
insertNode(minder, childInfo, parent, index);
});
return parent;
}
function applyPatch(minder, patch) {
// patch.op - 操作,包括 remove, add, replace
// patch.path - 路径,如 '/root/children/1/data'
// patch.value - 数据,如 { text: "思路" }
var path = patch.path.split('/');
path.shift();
var changed = path.shift();
var node;
if (changed == 'root') {
var dataIndex = path.indexOf('data');
if (dataIndex > -1) {
changed = 'data';
var dataPath = path.splice(dataIndex + 1);
patch.dataPath = dataPath;
} else {
changed = 'node';
}
node = minder.getRoot();
var segment, index;
while (segment = path.shift()) {
if (segment == 'children') continue;
if (typeof index != 'undefined') node = node.getChild(index);
index = +segment;
}
patch.index = index;
patch.node = node;
}
var express = patch.express = [changed, patch.op].join('.');
switch (express) {
case 'theme.replace':
minder.useTheme(patch.value);
break;
case 'template.replace':
minder.useTemplate(patch.value);
break;
case 'node.add':
insertNode(minder, patch.value, patch.node, patch.index).renderTree();
minder.layout();
break;
case 'node.remove':
minder.removeNode(patch.node.getChild(patch.index));
minder.layout();
break;
case 'data.add':
case 'data.replace':
case 'data.remove':
var data = patch.node.data;
var field;
path = patch.dataPath.slice();
while (data && path.length > 1) {
field = path.shift();
if (field in data) {
data = data[field];
} else if (patch.op != 'remove') {
data = data[field] = {};
}
}
if (data) {
field = path.shift();
data[field] = patch.value;
}
if (field == 'expandState') {
node.renderTree();
} else {
node.render();
}
minder.layout();
}
minder.fire('patch', { 'patch' : patch } );
}
kity.extendClass(Minder, {
applyPatches: function(patches) {
for (var i = 0; i < patches.length; i++) {
applyPatch(this, patches[i]);
}
this.fire('contentchange');
return this;
}
});
});
\ No newline at end of file
......@@ -80,7 +80,11 @@ define(function(require, exports, module) {
this.renderChangedSelection(lastSelect);
return this;
},
selectById: function(ids, isSingleSelect) {
ids = utils.isArray(ids) ? ids : [ids];
var nodes = this.getNodesById(ids);
return this.select(nodes, isSingleSelect);
},
//当前选区中的节点在给定的节点范围内的保留选中状态,
//没在给定范围的取消选中,给定范围中的但没在当前选中范围的也做选中效果
toggleSelect: function(node) {
......
......@@ -64,6 +64,7 @@ define(function(require, exports, module) {
},
setTheme: function(name) {
if (name && !_themes[name]) throw new Error('Theme ' + name + ' not exists!');
var lastTheme = this._theme;
this._theme = name || null;
var container = this.getRenderTarget();
......
......@@ -39,6 +39,8 @@ define(function(require, exports, module) {
kityminder.Theme = require('./core/theme');
kityminder.Template = require('./core/template');
kityminder.Promise = require('./core/promise');
require('./core/_boxv');
require('./core/patch');
// 模块依赖
require('./module/arrange');
......@@ -47,7 +49,6 @@ define(function(require, exports, module) {
require('./module/dragtree');
require('./module/expand');
require('./module/font');
require('./module/history');
require('./module/hyperlink');
require('./module/image');
require('./module/keynav');
......@@ -81,6 +82,7 @@ define(function(require, exports, module) {
require('./theme/fresh');
require('./theme/fish');
require('./theme/snow');
require('./theme/wire');
require('./connect/arc');
require('./connect/bezier');
......
......@@ -112,7 +112,7 @@ define(function(require, exports, module) {
this.stack(children, oppsite[axis]);
var bbox = this.getBranchBox(children);
var xAdjust, yAdjust;
var xAdjust = 0, yAdjust = 0;
if (axis == 'x') {
xAdjust = pbox[name];
......
......@@ -29,7 +29,7 @@ define(function(require, exports, module) {
function sendToClipboard(nodes) {
if (!nodes.length) return;
nodes.sort(function(a, b) {
return b.getIndex() - a.getIndex();
return a.getIndex() - b.getIndex();
});
_clipboardNodes = nodes.map(function(node) {
return node.clone();
......
......@@ -77,7 +77,8 @@ define(function(require, exports, module) {
},
queryState: function(km) {
return km.getSelectedNode() ? 0 : -1;
var node = km.getSelectedNode();
return node && !node.isRoot() && !node.isExpanded() ? 0 : -1;
}
});
......@@ -100,6 +101,31 @@ define(function(require, exports, module) {
enableReadOnly: true
});
/**
* @command Collapse
* @description 收起当前节点的子树
* @state
* 0: 当前有选中的节点
* -1: 当前没有选中的节点
*/
var CollapseCommand = kity.createClass('CollapseCommand', {
base: Command,
execute: function(km) {
var node = km.getSelectedNode();
if (!node) return;
node.collapse();
node.renderTree();
km.layout();
},
queryState: function(km) {
var node = km.getSelectedNode();
return node && !node.isRoot() && node.isExpanded() ? 0 : -1;
}
});
var Expander = kity.createClass('Expander', {
base: kity.Group,
......@@ -175,10 +201,12 @@ define(function(require, exports, module) {
this.expander.setTranslate(position);
}
});
return {
commands: {
'expand': ExpandCommand,
'expandtolevel': ExpandToLevelCommand
'expandtolevel': ExpandToLevelCommand,
'collapse': CollapseCommand
},
events: {
'layoutapply': function(e) {
......
This diff is collapsed.
......@@ -31,8 +31,7 @@ define(function(require, exports, module) {
bottom: p.y + p.height,
width: p.width,
height: p.height,
node: node,
text: node.getText()
node: node
});
}
});
......@@ -57,10 +56,19 @@ define(function(require, exports, module) {
else if (yDist < 0) dist = xDist;
else dist = sqrt(xDist * xDist + yDist * yDist);
return {
cx: dist,
cy: dist
};
var node1 = box1.node;
var node2 = box2.node;
// sibling
if (node1.parent == node2.parent) {
dist /= 10;
}
// parent
if (node2.parent == node1) {
dist /= 5;
}
return dist;
}
function findClosestPointsFor(pointIndexes, iFind) {
......@@ -78,9 +86,9 @@ define(function(require, exports, module) {
// left check
if (current.right < find.left) {
if (!most.left || dist.cx < most.left.dist) {
if (!most.left || dist < most.left.dist) {
most.left = {
dist: dist.cx,
dist: dist,
node: current.node
};
}
......@@ -88,9 +96,9 @@ define(function(require, exports, module) {
// right check
if (current.left > find.right) {
if (!most.right || dist.cx < most.right.dist) {
if (!most.right || dist < most.right.dist) {
most.right = {
dist: dist.cx,
dist: dist,
node: current.node
};
}
......@@ -98,9 +106,9 @@ define(function(require, exports, module) {
// top check
if (current.bottom < find.top) {
if (!most.top || dist.cy < most.top.dist) {
if (!most.top || dist < most.top.dist) {
most.top = {
dist: dist.cy,
dist: dist,
node: current.node
};
}
......@@ -108,9 +116,9 @@ define(function(require, exports, module) {
// bottom check
if (current.top > find.bottom) {
if (!most.down || dist.cy < most.down.dist) {
if (!most.down || dist < most.down.dist) {
most.down = {
dist: dist.cy,
dist: dist,
node: current.node
};
}
......@@ -153,6 +161,7 @@ define(function(require, exports, module) {
['left', 'right', 'up', 'down'].forEach(function(key) {
if (e.isShortcutKey(key)) {
navigateTo(minder, key == 'up' ? 'top' : key);
e.preventDefault();
}
});
}
......
......@@ -23,10 +23,15 @@ define(function(require, exports, module) {
if (!parent) {
return null;
}
parent.expand();
var node = km.createNode(text, parent);
km.select(node, true);
if (parent.isExpanded()) {
node.render();
}
else {
parent.expand();
parent.renderTree();
}
km.layout(600);
},
queryState: function(km) {
......@@ -90,7 +95,7 @@ define(function(require, exports, module) {
},
queryState: function(km) {
var selectedNode = km.getSelectedNode();
return selectedNode ? 0 : -1;
return selectedNode && !selectedNode.isRoot() ? 0 : -1;
}
});
......@@ -115,7 +120,7 @@ define(function(require, exports, module) {
},
queryState: function(km) {
var nodes = km.getSelectedNodes();
if (!nodes.length) return;
if (!nodes.length) return -1;
var parent = nodes[0].parent;
if (!parent) return -1;
for (var i = 1; i < nodes.length; i++) {
......
......@@ -117,7 +117,7 @@ define(function(require, exports, module) {
});
return {
'commands': {
'priority': PriorityCommand,
'priority': PriorityCommand
},
'renderers': {
left: kity.createClass('PriorityRenderer', {
......
......@@ -132,7 +132,7 @@ define(function(require, exports, module) {
// 点中了节点,并且按了 shift 键:
// 被点中的节点切换选中状态
else if (e.originEvent.shiftKey) {
else if (e.isShortcutKey('Ctrl')) {
this.toggleSelect(downNode);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment