Commit 518eec4e authored by techird's avatar techird

1.4.0

parents fe784d12 51eaa58a
...@@ -9,12 +9,12 @@ KityMinder 是一款强大的脑图可视化/编辑工具,由百度 FEX 团队 ...@@ -9,12 +9,12 @@ KityMinder 是一款强大的脑图可视化/编辑工具,由百度 FEX 团队
* 包括脑图数据的可视化展示(Json 格式) * 包括脑图数据的可视化展示(Json 格式)
* 包括简单的编辑功能(节点创建、编辑、删除)。更加强大编辑功能的 KityMinder 编辑器请移步 [kityminder-editor](https://github.com/fex-team/kityminder-editor) * 包括简单的编辑功能(节点创建、编辑、删除)。更加强大编辑功能的 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 + 百度网盘方案进行实现。 * 不包含文件存储的支持,需要自行实现存储。可参照[百度脑图](https://github.com/fex-team/naotu.baidu.com)中的开源的 fio + 百度网盘方案进行实现。
## 使用 ## 使用
可以参考 [example.html](example.html) 进行使用,代码类似: 可以参考 [example.html](example.html) 进行使用
```js ```js
<div id="minder-container"></div> <div id="minder-container"></div>
...@@ -59,6 +59,12 @@ npm install ...@@ -59,6 +59,12 @@ npm install
grunt grunt
``` ```
想偷懒?可以用下面这个一行安装脚本:
```bash
https://gist.githubusercontent.com/techird/72b420c7ea05154ce821/raw/6416f2709ce82a3a0d86a50763de1ce3ca7f3ca2/setup-km-core
```
## 联系我们 ## 联系我们
问题和建议反馈:[Github Issues](https://github.com/fex-team/kityminder-core/issues) 问题和建议反馈:[Github Issues](https://github.com/fex-team/kityminder-core/issues)
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
"name": "kityminder-core", "name": "kityminder-core",
"title": "Kity Minder Core", "title": "Kity Minder Core",
"description": "Powerful online mind graphic visualization and editor (command based)", "description": "Powerful online mind graphic visualization and editor (command based)",
"version": "1.4.0", "version": "1.4.1",
"main": [ "main": [
"release/kityminder.core.min.js", "release/kityminder.core.min.js",
"release/kityminder.core.css" "release/kityminder.core.css"
...@@ -30,4 +30,19 @@ ...@@ -30,4 +30,19 @@
"name": "Baidu FEX", "name": "Baidu FEX",
"url": "http://fex.baidu.com" "url": "http://fex.baidu.com"
} }
} ],
\ No newline at end of file "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) { ...@@ -134,23 +134,18 @@ define(function(require, exports, module) {
return false; return false;
} }
if (!this._hasEnterExecCommand && cmd.isNeedUndo()) { if (!this._hasEnterExecCommand) {
this._hasEnterExecCommand = true; this._hasEnterExecCommand = true;
stoped = this._fire(new MinderEvent('beforeExecCommand', eventParams, true)); stoped = this._fire(new MinderEvent('beforeExecCommand', eventParams, true));
if (!stoped) { if (!stoped) {
//保存场景
this._fire(new MinderEvent('saveScene'));
this._fire(new MinderEvent('preExecCommand', eventParams, false)); this._fire(new MinderEvent('preExecCommand', eventParams, false));
result = cmd.execute.apply(cmd, [me].concat(cmdArgs)); result = cmd.execute.apply(cmd, [me].concat(cmdArgs));
this._fire(new MinderEvent('execCommand', eventParams, false)); this._fire(new MinderEvent('execCommand', eventParams, false));
//保存场景
this._fire(new MinderEvent('saveScene'));
if (cmd.isContentChanged()) { if (cmd.isContentChanged()) {
this._firePharse(new MinderEvent('contentchange')); this._firePharse(new MinderEvent('contentchange'));
} }
......
...@@ -46,11 +46,9 @@ define(function(require, exports, module) { ...@@ -46,11 +46,9 @@ define(function(require, exports, module) {
var exported = {}; var exported = {};
exported.data = node.getData(); exported.data = node.getData();
var childNodes = node.getChildren(); var childNodes = node.getChildren();
if (childNodes.length) { exported.children = [];
exported.children = []; for (var i = 0; i < childNodes.length; i++) {
for (var i = 0; i < childNodes.length; i++) { exported.children.push(exportNode(childNodes[i]));
exported.children.push(exportNode(childNodes[i]));
}
} }
return exported; return exported;
} }
...@@ -63,7 +61,7 @@ define(function(require, exports, module) { ...@@ -63,7 +61,7 @@ define(function(require, exports, module) {
json.theme = this.getTheme(); json.theme = this.getTheme();
json.version = Minder.version; json.version = Minder.version;
return json; return JSON.parse(JSON.stringify(json));
}, },
/** /**
...@@ -140,7 +138,7 @@ define(function(require, exports, module) { ...@@ -140,7 +138,7 @@ define(function(require, exports, module) {
* *
* @param {string} protocol 指定的数据协议(默认内置五种数据协议 `json`、`text`、`markdown`、`svg` 和 `png`) * @param {string} protocol 指定的数据协议(默认内置五种数据协议 `json`、`text`、`markdown`、`svg` 和 `png`)
*/ */
exportData: function(protocolName) { exportData: function(protocolName, option) {
var json, protocol; var json, protocol;
json = this.exportJson(); json = this.exportJson();
...@@ -161,7 +159,7 @@ define(function(require, exports, module) { ...@@ -161,7 +159,7 @@ define(function(require, exports, module) {
protocol: protocol 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) { ...@@ -174,7 +172,7 @@ define(function(require, exports, module) {
* @param {string} protocol 指定的用于解析数据的数据协议(默认内置三种数据协议 `json`、`text` 和 `markdown` 的支持) * @param {string} protocol 指定的用于解析数据的数据协议(默认内置三种数据协议 `json`、`text` 和 `markdown` 的支持)
* @param {any} data 要导入的数据 * @param {any} data 要导入的数据
*/ */
importData: function(protocolName, data) { importData: function(protocolName, data, option) {
var json, protocol; var json, protocol;
var minder = this; var minder = this;
...@@ -196,7 +194,7 @@ define(function(require, exports, module) { ...@@ -196,7 +194,7 @@ define(function(require, exports, module) {
// 导入前抛事件 // 导入前抛事件
this._fire(new MinderEvent('beforeimport', params)); 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); minder.importJson(json);
return json; return json;
}); });
......
...@@ -34,7 +34,7 @@ define(function(require, exports, module) { ...@@ -34,7 +34,7 @@ define(function(require, exports, module) {
* children[i].setLayoutTransform(new kity.Matrix().translate(x, y)); * children[i].setLayoutTransform(new kity.Matrix().translate(x, y));
* } * }
*/ */
doLayout: function(node) { doLayout: function(parent, children) {
throw new Error('Not Implement: Layout.doLayout()'); throw new Error('Not Implement: Layout.doLayout()');
}, },
...@@ -234,10 +234,6 @@ define(function(require, exports, module) { ...@@ -234,10 +234,6 @@ define(function(require, exports, module) {
return this.parent.getLayoutInstance().getOrderHint(this); return this.parent.getLayoutInstance().getOrderHint(this);
}, },
getExpandPosition: function() {
return this.getLayoutInstance().getExpandPosition();
},
/** /**
* 获取当前节点相对于父节点的布局变换 * 获取当前节点相对于父节点的布局变换
*/ */
...@@ -255,7 +251,7 @@ define(function(require, exports, module) { ...@@ -255,7 +251,7 @@ define(function(require, exports, module) {
var matrix = this.getLayoutTransform(); var matrix = this.getLayoutTransform();
var offset = this.getLayoutOffset(); var offset = this.getLayoutOffset();
if (offset) { if (offset) {
matrix.translate(offset.x, offset.y); matrix = matrix.clone().translate(offset.x, offset.y);
} }
return pMatrix.merge(matrix); return pMatrix.merge(matrix);
}, },
...@@ -359,16 +355,10 @@ define(function(require, exports, module) { ...@@ -359,16 +355,10 @@ define(function(require, exports, module) {
setLayoutOffset: function(p) { setLayoutOffset: function(p) {
if (!this.parent) return this; 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 ? { this.setData('layout_' + this.parent.getLayout() + '_offset', p ? {
x: p.x, x: p.x,
y: p.y y: p.y
} : null); } : undefined);
return this; return this;
}, },
...@@ -421,7 +411,7 @@ define(function(require, exports, module) { ...@@ -421,7 +411,7 @@ define(function(require, exports, module) {
var childrenInFlow = node.getChildren().filter(function(child) { var childrenInFlow = node.getChildren().filter(function(child) {
return !child.hasLayoutOffset(); return !child.hasLayoutOffset();
}); });
layout.doLayout(node, childrenInFlow, round); layout.doLayout(node, node.getChildren(), round);
} }
// 第一轮布局 // 第一轮布局
...@@ -471,7 +461,7 @@ define(function(require, exports, module) { ...@@ -471,7 +461,7 @@ define(function(require, exports, module) {
} }
function apply(node, pMatrix) { 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 lastMatrix = node.getGlobalLayoutTransform() || new kity.Matrix();
var offset = node.getLayoutOffset(); var offset = node.getLayoutOffset();
...@@ -480,7 +470,6 @@ define(function(require, exports, module) { ...@@ -480,7 +470,6 @@ define(function(require, exports, module) {
matrix.m.e = Math.round(matrix.m.e); matrix.m.e = Math.round(matrix.m.e);
matrix.m.f = Math.round(matrix.m.f); matrix.m.f = Math.round(matrix.m.f);
// 如果当前有动画,停止动画 // 如果当前有动画,停止动画
if (node._layoutTimeline) { if (node._layoutTimeline) {
node._layoutTimeline.stop(); node._layoutTimeline.stop();
...@@ -518,10 +507,9 @@ define(function(require, exports, module) { ...@@ -518,10 +507,9 @@ define(function(require, exports, module) {
apply(node.children[i], matrix); apply(node.children[i], matrix);
} }
} }
apply(root, root.parent ? root.parent.getGlobalLayoutTransform() : new kity.Matrix()); apply(root, root.parent ? root.parent.getGlobalLayoutTransform() : new kity.Matrix());
return this; return this;
}, }
}); });
module.exports = Layout; module.exports = Layout;
......
...@@ -72,6 +72,16 @@ define(function(require, exports, module) { ...@@ -72,6 +72,16 @@ define(function(require, exports, module) {
return this.parent; 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) { ...@@ -122,7 +132,16 @@ define(function(require, exports, module) {
}, },
setData: function(key, value) { setData: function(key, value) {
this.data[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) { ...@@ -305,6 +324,29 @@ define(function(require, exports, module) {
root.minder = this; 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) { createNode: function(textOrData, parent, index) {
var node = new MinderNode(textOrData); var node = new MinderNode(textOrData);
this.fire('nodecreate', { 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) { ...@@ -80,7 +80,11 @@ define(function(require, exports, module) {
this.renderChangedSelection(lastSelect); this.renderChangedSelection(lastSelect);
return this; 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) { toggleSelect: function(node) {
......
...@@ -64,6 +64,7 @@ define(function(require, exports, module) { ...@@ -64,6 +64,7 @@ define(function(require, exports, module) {
}, },
setTheme: function(name) { setTheme: function(name) {
if (name && !_themes[name]) throw new Error('Theme ' + name + ' not exists!');
var lastTheme = this._theme; var lastTheme = this._theme;
this._theme = name || null; this._theme = name || null;
var container = this.getRenderTarget(); var container = this.getRenderTarget();
......
...@@ -39,6 +39,8 @@ define(function(require, exports, module) { ...@@ -39,6 +39,8 @@ define(function(require, exports, module) {
kityminder.Theme = require('./core/theme'); kityminder.Theme = require('./core/theme');
kityminder.Template = require('./core/template'); kityminder.Template = require('./core/template');
kityminder.Promise = require('./core/promise'); kityminder.Promise = require('./core/promise');
require('./core/_boxv');
require('./core/patch');
// 模块依赖 // 模块依赖
require('./module/arrange'); require('./module/arrange');
...@@ -47,7 +49,6 @@ define(function(require, exports, module) { ...@@ -47,7 +49,6 @@ define(function(require, exports, module) {
require('./module/dragtree'); require('./module/dragtree');
require('./module/expand'); require('./module/expand');
require('./module/font'); require('./module/font');
require('./module/history');
require('./module/hyperlink'); require('./module/hyperlink');
require('./module/image'); require('./module/image');
require('./module/keynav'); require('./module/keynav');
...@@ -81,6 +82,7 @@ define(function(require, exports, module) { ...@@ -81,6 +82,7 @@ define(function(require, exports, module) {
require('./theme/fresh'); require('./theme/fresh');
require('./theme/fish'); require('./theme/fish');
require('./theme/snow'); require('./theme/snow');
require('./theme/wire');
require('./connect/arc'); require('./connect/arc');
require('./connect/bezier'); require('./connect/bezier');
......
...@@ -112,7 +112,7 @@ define(function(require, exports, module) { ...@@ -112,7 +112,7 @@ define(function(require, exports, module) {
this.stack(children, oppsite[axis]); this.stack(children, oppsite[axis]);
var bbox = this.getBranchBox(children); var bbox = this.getBranchBox(children);
var xAdjust, yAdjust; var xAdjust = 0, yAdjust = 0;
if (axis == 'x') { if (axis == 'x') {
xAdjust = pbox[name]; xAdjust = pbox[name];
......
...@@ -29,7 +29,7 @@ define(function(require, exports, module) { ...@@ -29,7 +29,7 @@ define(function(require, exports, module) {
function sendToClipboard(nodes) { function sendToClipboard(nodes) {
if (!nodes.length) return; if (!nodes.length) return;
nodes.sort(function(a, b) { nodes.sort(function(a, b) {
return b.getIndex() - a.getIndex(); return a.getIndex() - b.getIndex();
}); });
_clipboardNodes = nodes.map(function(node) { _clipboardNodes = nodes.map(function(node) {
return node.clone(); return node.clone();
......
...@@ -77,7 +77,8 @@ define(function(require, exports, module) { ...@@ -77,7 +77,8 @@ define(function(require, exports, module) {
}, },
queryState: function(km) { 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) { ...@@ -100,6 +101,31 @@ define(function(require, exports, module) {
enableReadOnly: true 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', { var Expander = kity.createClass('Expander', {
base: kity.Group, base: kity.Group,
...@@ -175,10 +201,12 @@ define(function(require, exports, module) { ...@@ -175,10 +201,12 @@ define(function(require, exports, module) {
this.expander.setTranslate(position); this.expander.setTranslate(position);
} }
}); });
return { return {
commands: { commands: {
'expand': ExpandCommand, 'expand': ExpandCommand,
'expandtolevel': ExpandToLevelCommand 'expandtolevel': ExpandToLevelCommand,
'collapse': CollapseCommand
}, },
events: { events: {
'layoutapply': function(e) { 'layoutapply': function(e) {
......
define(function(require, exports, module) {
var kity = require('../core/kity');
var utils = require('../core/utils');
var Minder = require('../core/minder');
var MinderNode = require('../core/node');
var Command = require('../core/command');
var Module = require('../core/module');
function compareObject(source, target) {
var tmp;
if (isEmptyObject(source) !== isEmptyObject(target)) {
return false;
}
if (getObjectLength(source) != getObjectLength(target)) {
return false;
}
for (var p in source) {
if (source.hasOwnProperty(p)) {
tmp = source[p];
if (target[p] === undefined) {
return false;
}
if (utils.isObject(tmp) || utils.isArray(tmp)) {
if (utils.isObject(target[p]) !== utils.isObject(tmp)) {
return false;
}
if (utils.isArray(tmp) !== utils.isArray(target[p])) {
return false;
}
if (compareObject(tmp, target[p]) === false) {
return false;
}
} else {
if (tmp != target[p]) {
return false;
}
}
}
}
return true;
}
function getObjectLength(obj) {
if (utils.isArray(obj) || utils.isString(obj)) return obj.length;
var count = 0;
for (var key in obj)
if (obj.hasOwnProperty(key)) count++;
return count;
}
function isEmptyObject(obj) {
if (obj === null || obj === undefined) return true;
if (utils.isArray(obj) || utils.isString(obj)) return obj.length === 0;
for (var key in obj)
if (obj.hasOwnProperty(key)) return false;
return true;
}
function getValueByIndex(data, index) {
var initIndex = 0,
result = 0;
data.forEach(function(arr, i) {
if (initIndex + arr.length >= index) {
if (index - initIndex == arr.length) {
if (arr.length == 1 && arr[0].width === 0) {
initIndex++;
return;
}
result = {
x: arr[arr.length - 1].x + arr[arr.length - 1].width,
y: arr[arr.length - 1].y
};
} else {
result = arr[index - initIndex];
}
return false;
} else {
initIndex += arr.length + (arr.length == 1 && arr[0].width === 0 ? 0 : 1);
}
});
return result;
}
function getNodeIndex(node, ignoreTextNode) {
var preNode = node,
i = 0;
while (preNode = preNode.previousSibling) {
if (ignoreTextNode && preNode.nodeType == 3) {
if (preNode.nodeType != preNode.nextSibling.nodeType) {
i++;
}
continue;
}
i++;
}
return i;
}
var km = this;
var Scene = kity.createClass('Scene', {
constructor: function(root, inputStatus) {
this.data = root.clone();
this.inputStatus = inputStatus;
},
getData: function() {
return this.data;
},
cloneData: function() {
return this.getData().clone();
},
equals: function(scene) {
return this.getData().compareTo(scene.getData());
},
isInputStatus: function() {
return this.inputStatus;
},
setInputStatus: function(status) {
this.inputStatus = status;
}
});
var HistoryManager = kity.createClass('HistoryManager', {
constructor: function(km) {
this.list = [];
this.index = 0;
this.hasUndo = false;
this.hasRedo = false;
this.km = km;
},
undo: function() {
if (this.hasUndo) {
var currentScene = this.list[this.index];
//如果是输入文字时的保存,直接回复当前场景
if (currentScene && currentScene.isInputStatus()) {
this.saveScene();
this.restore(--this.index);
currentScene.setInputStatus(false);
return;
}
if (this.list.length == 1) {
this.restore(0);
return;
}
if (!this.list[this.index - 1] && this.list.length == 1) {
this.reset();
return;
}
while (this.list[this.index].equals(this.list[this.index - 1])) {
this.index--;
if (this.index === 0) {
return this.restore(0);
}
}
this.restore(--this.index);
}
},
redo: function() {
if (this.hasRedo) {
while (this.list[this.index].equals(this.list[this.index + 1])) {
this.index++;
if (this.index == this.list.length - 1) {
return this.restore(this.index);
}
}
this.restore(++this.index);
}
},
partialRenewal: function(target) {
var selectedNodes = [];
function compareNode(source, target) {
if (source.getText() != target.getText()) {
return false;
}
if (compareObject(source.getData(), target.getData()) === false) {
return false;
}
return true;
}
function appendChildNode(parent, child) {
if (child.isSelected()) {
selectedNodes.push(child);
}
km.appendNode(child, parent);
child.render();
var children = child.children.slice();
for (var i = 0, ci; ci = children[i++];) {
appendChildNode(child, ci);
}
}
function traverseNode(srcNode, tagNode) {
if (compareNode(srcNode, tagNode) === false) {
srcNode.setValue(tagNode);
}
//todo,这里有性能问题,变成全部render了
srcNode.render();
if (srcNode.isSelected()) {
selectedNodes.push(srcNode);
}
for (var i = 0, j = 0, si, tj;
(si = srcNode.children[i], tj = tagNode.children[j], si || tj); i++, j++) {
if (si && !tj) {
i--;
km.removeNode(si);
} else if (!si && tj) {
j--;
appendChildNode(srcNode, tj);
} else {
traverseNode(si, tj);
}
}
}
var km = this.km;
traverseNode(km.getRoot(), target);
km.layout(200);
km.select(selectedNodes, true);
selectedNodes = [];
},
restore: function(index) {
index = index === undefined ? this.index : index;
var scene = this.list[index];
this.partialRenewal(scene.cloneData());
this.update();
this.km.fire('restoreScene');
this.km.fire('contentChange');
},
getScene: function(inputStatus) {
return new Scene(this.km.getRoot(), inputStatus);
},
saveScene: function(inputStatus) {
var currentScene = this.getScene(inputStatus);
var lastScene = this.list[this.index];
if (lastScene && lastScene.equals(currentScene)) {
if (inputStatus) {
lastScene.setInputStatus(true);
this.update();
}
return;
}
this.list = this.list.slice(0, this.index + 1);
this.list.push(currentScene);
//如果大于最大数量了,就把最前的剔除
if (this.list.length > this.km.getOption('maxUndoCount')) {
this.list.shift();
}
this.index = this.list.length - 1;
//跟新undo/redo状态
this.update();
},
update: function() {
this.hasRedo = !!this.list[this.index + 1];
this.hasUndo = !!this.list[this.index - 1];
var currentScene = this.list[this.index];
if (currentScene && currentScene.isInputStatus()) {
this.hasUndo = true;
}
},
reset: function() {
this.list = [];
this.index = 0;
this.hasUndo = false;
this.hasRedo = false;
}
});
Module.register('HistoryModule', function() {
//为km实例添加history管理
this.historyManager = new HistoryManager(this);
return {
defaultOptions: {
maxUndoCount: 20,
maxInputCount: 20
},
'commands': {
/**
* @command Undo
* @description 回退上一步操作
* @state
* 0: 当前有可回退的内容
* -1: 当前没有可回退的内容
*/
'undo': kity.createClass('UndoCommand', {
base: Command,
execute: function(km) {
km.historyManager.undo();
},
queryState: function(km) {
return km.historyManager.hasUndo ? 0 : -1;
},
isNeedUndo: function() {
return false;
}
}),
/**
* @command Redo
* @description 重做下一步已回退的操作
* @state
* 0: 当前有可重做的内容
* -1: 当前没有可重做的内容
*/
'redo': kity.createClass('RedoCommand', {
base: Command,
execute: function(km) {
km.historyManager.redo();
},
queryState: function(km) {
return km.historyManager.hasRedo ? 0 : -1;
},
isNeedUndo: function() {
return false;
}
})
},
commandShortcutKeys: {
'undo': 'ctrl+z', //undo
'redo': 'ctrl+y' //redo
},
'events': {
'saveScene': function(e) {
this.historyManager.saveScene(e.inputStatus);
},
'import': function() {
this.historyManager.reset();
}
}
};
});
});
\ No newline at end of file
...@@ -31,8 +31,7 @@ define(function(require, exports, module) { ...@@ -31,8 +31,7 @@ define(function(require, exports, module) {
bottom: p.y + p.height, bottom: p.y + p.height,
width: p.width, width: p.width,
height: p.height, height: p.height,
node: node, node: node
text: node.getText()
}); });
} }
}); });
...@@ -57,10 +56,19 @@ define(function(require, exports, module) { ...@@ -57,10 +56,19 @@ define(function(require, exports, module) {
else if (yDist < 0) dist = xDist; else if (yDist < 0) dist = xDist;
else dist = sqrt(xDist * xDist + yDist * yDist); else dist = sqrt(xDist * xDist + yDist * yDist);
return { var node1 = box1.node;
cx: dist, var node2 = box2.node;
cy: dist
}; // sibling
if (node1.parent == node2.parent) {
dist /= 10;
}
// parent
if (node2.parent == node1) {
dist /= 5;
}
return dist;
} }
function findClosestPointsFor(pointIndexes, iFind) { function findClosestPointsFor(pointIndexes, iFind) {
...@@ -78,9 +86,9 @@ define(function(require, exports, module) { ...@@ -78,9 +86,9 @@ define(function(require, exports, module) {
// left check // left check
if (current.right < find.left) { if (current.right < find.left) {
if (!most.left || dist.cx < most.left.dist) { if (!most.left || dist < most.left.dist) {
most.left = { most.left = {
dist: dist.cx, dist: dist,
node: current.node node: current.node
}; };
} }
...@@ -88,9 +96,9 @@ define(function(require, exports, module) { ...@@ -88,9 +96,9 @@ define(function(require, exports, module) {
// right check // right check
if (current.left > find.right) { if (current.left > find.right) {
if (!most.right || dist.cx < most.right.dist) { if (!most.right || dist < most.right.dist) {
most.right = { most.right = {
dist: dist.cx, dist: dist,
node: current.node node: current.node
}; };
} }
...@@ -98,9 +106,9 @@ define(function(require, exports, module) { ...@@ -98,9 +106,9 @@ define(function(require, exports, module) {
// top check // top check
if (current.bottom < find.top) { if (current.bottom < find.top) {
if (!most.top || dist.cy < most.top.dist) { if (!most.top || dist < most.top.dist) {
most.top = { most.top = {
dist: dist.cy, dist: dist,
node: current.node node: current.node
}; };
} }
...@@ -108,9 +116,9 @@ define(function(require, exports, module) { ...@@ -108,9 +116,9 @@ define(function(require, exports, module) {
// bottom check // bottom check
if (current.top > find.bottom) { if (current.top > find.bottom) {
if (!most.down || dist.cy < most.down.dist) { if (!most.down || dist < most.down.dist) {
most.down = { most.down = {
dist: dist.cy, dist: dist,
node: current.node node: current.node
}; };
} }
...@@ -153,6 +161,7 @@ define(function(require, exports, module) { ...@@ -153,6 +161,7 @@ define(function(require, exports, module) {
['left', 'right', 'up', 'down'].forEach(function(key) { ['left', 'right', 'up', 'down'].forEach(function(key) {
if (e.isShortcutKey(key)) { if (e.isShortcutKey(key)) {
navigateTo(minder, key == 'up' ? 'top' : key); navigateTo(minder, key == 'up' ? 'top' : key);
e.preventDefault();
} }
}); });
} }
......
...@@ -23,10 +23,15 @@ define(function(require, exports, module) { ...@@ -23,10 +23,15 @@ define(function(require, exports, module) {
if (!parent) { if (!parent) {
return null; return null;
} }
parent.expand();
var node = km.createNode(text, parent); var node = km.createNode(text, parent);
km.select(node, true); km.select(node, true);
node.render(); if (parent.isExpanded()) {
node.render();
}
else {
parent.expand();
parent.renderTree();
}
km.layout(600); km.layout(600);
}, },
queryState: function(km) { queryState: function(km) {
...@@ -90,7 +95,7 @@ define(function(require, exports, module) { ...@@ -90,7 +95,7 @@ define(function(require, exports, module) {
}, },
queryState: function(km) { queryState: function(km) {
var selectedNode = km.getSelectedNode(); var selectedNode = km.getSelectedNode();
return selectedNode ? 0 : -1; return selectedNode && !selectedNode.isRoot() ? 0 : -1;
} }
}); });
...@@ -98,7 +103,7 @@ define(function(require, exports, module) { ...@@ -98,7 +103,7 @@ define(function(require, exports, module) {
base: Command, base: Command,
execute: function(km, text) { execute: function(km, text) {
var nodes = km.getSelectedNodes(); var nodes = km.getSelectedNodes();
nodes.sort(function(a, b) { nodes.sort(function(a, b) {
return a.getIndex() - b.getIndex(); return a.getIndex() - b.getIndex();
}); });
...@@ -115,7 +120,7 @@ define(function(require, exports, module) { ...@@ -115,7 +120,7 @@ define(function(require, exports, module) {
}, },
queryState: function(km) { queryState: function(km) {
var nodes = km.getSelectedNodes(); var nodes = km.getSelectedNodes();
if (!nodes.length) return; if (!nodes.length) return -1;
var parent = nodes[0].parent; var parent = nodes[0].parent;
if (!parent) return -1; if (!parent) return -1;
for (var i = 1; i < nodes.length; i++) { for (var i = 1; i < nodes.length; i++) {
......
...@@ -117,7 +117,7 @@ define(function(require, exports, module) { ...@@ -117,7 +117,7 @@ define(function(require, exports, module) {
}); });
return { return {
'commands': { 'commands': {
'priority': PriorityCommand, 'priority': PriorityCommand
}, },
'renderers': { 'renderers': {
left: kity.createClass('PriorityRenderer', { left: kity.createClass('PriorityRenderer', {
......
...@@ -132,7 +132,7 @@ define(function(require, exports, module) { ...@@ -132,7 +132,7 @@ define(function(require, exports, module) {
// 点中了节点,并且按了 shift 键: // 点中了节点,并且按了 shift 键:
// 被点中的节点切换选中状态 // 被点中的节点切换选中状态
else if (e.originEvent.shiftKey) { else if (e.isShortcutKey('Ctrl')) {
this.toggleSelect(downNode); 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