mirror of https://github.com/fantasticit/think.git
Merge pull request #48 from fantasticit/feat/kity-mind
This commit is contained in:
commit
955b45ba15
|
@ -1,6 +1,6 @@
|
|||
interface Window {
|
||||
// 思维导图
|
||||
MindElixir: any;
|
||||
// drawio 绘图
|
||||
GraphViewer: any;
|
||||
// 百度脑图
|
||||
kityminder: any;
|
||||
}
|
||||
|
|
|
@ -62,6 +62,7 @@
|
|||
"dompurify": "^2.3.5",
|
||||
"interactjs": "^1.10.11",
|
||||
"katex": "^0.15.2",
|
||||
"kity": "^2.0.4",
|
||||
"lib0": "^0.2.47",
|
||||
"lowlight": "^2.5.0",
|
||||
"markdown-it": "^12.3.2",
|
||||
|
@ -88,7 +89,6 @@
|
|||
"react-split-pane": "^0.1.92",
|
||||
"scroll-into-view-if-needed": "^2.2.29",
|
||||
"swr": "^1.2.0",
|
||||
"tilg": "^0.1.1",
|
||||
"timeago.js": "^4.0.2",
|
||||
"tippy.js": "^6.3.7",
|
||||
"toggle-selection": "^1.0.6",
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
/* eslint-disable */
|
||||
define('expose', function (require, exports, module) {
|
||||
module.exports = window.HotBox = require('./hotbox');
|
||||
});
|
|
@ -0,0 +1,647 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
var key = require('./key');
|
||||
var KeyControl = require('./keycontrol');
|
||||
|
||||
/**** Dom Utils ****/
|
||||
function createElement(name) {
|
||||
return document.createElement(name);
|
||||
}
|
||||
|
||||
function setElementAttribute(element, name, value) {
|
||||
element.setAttribute(name, value);
|
||||
}
|
||||
|
||||
function getElementAttribute(element, name) {
|
||||
return element.getAttribute(name);
|
||||
}
|
||||
|
||||
function addElementClass(element, name) {
|
||||
element.classList.add(name);
|
||||
}
|
||||
|
||||
function removeElementClass(element, name) {
|
||||
element.classList.remove(name);
|
||||
}
|
||||
|
||||
function appendChild(parent, child) {
|
||||
parent.appendChild(child);
|
||||
}
|
||||
/*******************/
|
||||
|
||||
var IDLE = (HotBox.STATE_IDLE = 'idle');
|
||||
var div = 'div';
|
||||
|
||||
/**
|
||||
* Simple Formatter
|
||||
*/
|
||||
function format(template, args) {
|
||||
if (typeof args != 'object') {
|
||||
args = [].slice.apply(arguments, 1);
|
||||
}
|
||||
return String(template).replace(/\{(\w+)\}/g, function (match, name) {
|
||||
return args[name] || match;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Hot Box Class
|
||||
*/
|
||||
function HotBox($container) {
|
||||
if (typeof $container == 'string') {
|
||||
$container = document.querySelector($container);
|
||||
}
|
||||
if (!$container || !($container instanceof HTMLElement)) {
|
||||
throw new Error('No container or not invalid container for hot box');
|
||||
}
|
||||
|
||||
// 创建 HotBox Dom 解构
|
||||
var $hotBox = createElement(div);
|
||||
addElementClass($hotBox, 'hotbox');
|
||||
appendChild($container, $hotBox);
|
||||
|
||||
// 保存 Dom 解构和父容器
|
||||
this.$element = $hotBox;
|
||||
this.$container = $container;
|
||||
|
||||
// 标示是否是输入法状态
|
||||
this.isIME = false;
|
||||
|
||||
/**
|
||||
* @Desc: 增加一个browser用于判断浏览器类型,方便解决兼容性问题
|
||||
* @Editor: Naixor
|
||||
* @Date: 2015.09.14
|
||||
*/
|
||||
this.browser = {
|
||||
sg: /se[\s\S]+metasr/.test(navigator.userAgent.toLowerCase()),
|
||||
};
|
||||
|
||||
/*
|
||||
* added by zhangbobell
|
||||
* 2015.09.22
|
||||
* 增加父状态机,以解决在父 FSM 下状态控制的问题,最好的解决办法是增加一个函数队列
|
||||
* 将其中的函数一起执行。//TODO
|
||||
* */
|
||||
this._parentFSM = {};
|
||||
|
||||
// 记录位置
|
||||
this.position = {};
|
||||
|
||||
// 已定义的状态(string => HotBoxState)
|
||||
var _states = {};
|
||||
|
||||
// 主状态(HotBoxState)
|
||||
var _mainState = null;
|
||||
|
||||
// 当前状态(HotBoxState)
|
||||
var _currentState = IDLE;
|
||||
|
||||
// 当前状态堆栈
|
||||
var _stateStack = [];
|
||||
|
||||
// 实例引用
|
||||
var _this = this;
|
||||
var _controler;
|
||||
|
||||
/**
|
||||
* Controller: {
|
||||
* constructor(hotbox: HotBox),
|
||||
* active: () => void
|
||||
* }
|
||||
*/
|
||||
function _control(Controller) {
|
||||
if (_controler) {
|
||||
_controler.active();
|
||||
return;
|
||||
}
|
||||
|
||||
Controller = Controller || KeyControl;
|
||||
|
||||
_controler = new Controller(_this);
|
||||
_controler.active();
|
||||
|
||||
$hotBox.onmousedown = function (e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
return _this;
|
||||
}
|
||||
|
||||
function _dispatchKey(e) {
|
||||
var type = e.type.toLowerCase();
|
||||
e.keyHash = key.hash(e);
|
||||
e.isKey = function (keyExpression) {
|
||||
if (!keyExpression) return false;
|
||||
var expressions = keyExpression.split(/\s*\|\s*/);
|
||||
while (expressions.length) {
|
||||
if (e.keyHash == key.hash(expressions.shift())) return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
e[type] = true;
|
||||
// Boot: keyup and activeKey pressed on IDLE, active main state.
|
||||
if (e.keyup && _this.activeKey && e.isKey(_this.activeKey) && _currentState == IDLE && _mainState) {
|
||||
_activeState('main', {
|
||||
x: $container.clientWidth / 2,
|
||||
y: $container.clientHeight / 2,
|
||||
});
|
||||
return;
|
||||
}
|
||||
var handleState = _currentState == IDLE ? _mainState : _currentState;
|
||||
if (handleState) {
|
||||
var handleResult = handleState.handleKeyEvent(e);
|
||||
if (typeof _this.onkeyevent == 'function') {
|
||||
e.handleResult = handleResult;
|
||||
_this.onkeyevent(e, handleResult);
|
||||
}
|
||||
return handleResult;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function _addState(name) {
|
||||
if (!name) return _currentState;
|
||||
if (name == IDLE) {
|
||||
throw new Error('Can not define or use the `idle` state.');
|
||||
}
|
||||
_states[name] = _states[name] || new HotBoxState(this, name);
|
||||
if (name == 'main') {
|
||||
_mainState = _states[name];
|
||||
}
|
||||
return _states[name];
|
||||
}
|
||||
|
||||
function _activeState(name, position) {
|
||||
_this.position = position;
|
||||
|
||||
// 回到 IDLE
|
||||
if (name == IDLE) {
|
||||
if (_currentState != IDLE) {
|
||||
_stateStack.shift().deactive();
|
||||
_stateStack = [];
|
||||
}
|
||||
_currentState = IDLE;
|
||||
}
|
||||
// 回退一个状态
|
||||
else if (name == 'back') {
|
||||
if (_currentState != IDLE) {
|
||||
_currentState.deactive();
|
||||
_stateStack.shift();
|
||||
_currentState = _stateStack[0];
|
||||
if (_currentState) {
|
||||
_currentState.active();
|
||||
} else {
|
||||
_currentState = 'idle';
|
||||
}
|
||||
}
|
||||
}
|
||||
// 切换到具体状态
|
||||
else {
|
||||
if (_currentState != IDLE) {
|
||||
_currentState.deactive();
|
||||
}
|
||||
var newState = _states[name];
|
||||
_stateStack.unshift(newState);
|
||||
if (typeof _this.position == 'function') {
|
||||
position = _this.position(position);
|
||||
}
|
||||
newState.active(position);
|
||||
_currentState = newState;
|
||||
}
|
||||
}
|
||||
|
||||
function setParentFSM(fsm) {
|
||||
_this._parentFSM = fsm;
|
||||
}
|
||||
|
||||
function getParentFSM() {
|
||||
return _this._parentFSM;
|
||||
}
|
||||
|
||||
this.control = _control;
|
||||
this.state = _addState;
|
||||
this.active = _activeState;
|
||||
this.dispatch = _dispatchKey;
|
||||
this.setParentFSM = setParentFSM;
|
||||
this.getParentFSM = getParentFSM;
|
||||
this.activeKey = 'space';
|
||||
this.actionKey = 'space';
|
||||
}
|
||||
|
||||
/**
|
||||
* 表示热盒某个状态,包含这些状态需要的 Dom 对象
|
||||
*/
|
||||
function HotBoxState(hotBox, stateName) {
|
||||
var BUTTON_SELECTED_CLASS = 'selected';
|
||||
var BUTTON_PRESSED_CLASS = 'pressed';
|
||||
var STATE_ACTIVE_CLASS = 'active';
|
||||
|
||||
// 状态容器
|
||||
var $state = createElement(div);
|
||||
|
||||
// 四种可见的按钮容器
|
||||
var $center = createElement(div);
|
||||
var $ring = createElement(div);
|
||||
var $ringShape = createElement('div');
|
||||
var $top = createElement(div);
|
||||
var $bottom = createElement(div);
|
||||
|
||||
// 添加 CSS 类
|
||||
addElementClass($state, 'state');
|
||||
addElementClass($state, stateName);
|
||||
addElementClass($center, 'center');
|
||||
addElementClass($ring, 'ring');
|
||||
addElementClass($ringShape, 'ring-shape');
|
||||
addElementClass($top, 'top');
|
||||
addElementClass($bottom, 'bottom');
|
||||
|
||||
// 摆放容器
|
||||
appendChild(hotBox.$element, $state);
|
||||
appendChild($state, $ringShape);
|
||||
appendChild($state, $center);
|
||||
appendChild($state, $ring);
|
||||
appendChild($state, $top);
|
||||
appendChild($state, $bottom);
|
||||
|
||||
// 记住状态名称
|
||||
this.name = stateName;
|
||||
|
||||
// 五种按钮:中心,圆环,上栏,下栏,幕后
|
||||
var buttons = {
|
||||
center: null,
|
||||
ring: [],
|
||||
top: [],
|
||||
bottom: [],
|
||||
behind: [],
|
||||
};
|
||||
var allButtons = [];
|
||||
var selectedButton = null;
|
||||
var pressedButton = null;
|
||||
|
||||
var stateActived = false;
|
||||
// 布局,添加按钮后,标记需要布局
|
||||
var needLayout = true;
|
||||
|
||||
function layout() {
|
||||
var radius = buttons.ring.length * 15;
|
||||
layoutRing(radius);
|
||||
layoutTop(radius);
|
||||
layoutBottom(radius);
|
||||
indexPosition();
|
||||
needLayout = false;
|
||||
|
||||
function layoutRing(radius) {
|
||||
var ring = buttons.ring;
|
||||
var step = (2 * Math.PI) / ring.length;
|
||||
|
||||
if (buttons.center) {
|
||||
buttons.center.indexedPosition = [0, 0];
|
||||
}
|
||||
|
||||
$ringShape.style.marginLeft = $ringShape.style.marginTop = -radius + 'px';
|
||||
$ringShape.style.width = $ringShape.style.height = radius + radius + 'px';
|
||||
|
||||
var $button, angle, x, y;
|
||||
for (var i = 0; i < ring.length; i++) {
|
||||
$button = ring[i].$button;
|
||||
angle = step * i - Math.PI / 2;
|
||||
x = radius * Math.cos(angle);
|
||||
y = radius * Math.sin(angle);
|
||||
ring[i].indexedPosition = [x, y];
|
||||
$button.style.left = x + 'px';
|
||||
$button.style.top = y + 'px';
|
||||
}
|
||||
}
|
||||
|
||||
function layoutTop(radius) {
|
||||
var xOffset = -$top.clientWidth / 2;
|
||||
var yOffset = -radius * 2 - $top.clientHeight / 2;
|
||||
$top.style.marginLeft = xOffset + 'px';
|
||||
$top.style.marginTop = yOffset + 'px';
|
||||
buttons.top.forEach(function (topButton) {
|
||||
var $button = topButton.$button;
|
||||
topButton.indexedPosition = [xOffset + $button.offsetLeft + $button.clientWidth / 2, yOffset];
|
||||
});
|
||||
}
|
||||
function layoutBottom(radius) {
|
||||
var xOffset = -$bottom.clientWidth / 2;
|
||||
var yOffset = radius * 2 - $bottom.clientHeight / 2;
|
||||
$bottom.style.marginLeft = xOffset + 'px';
|
||||
$bottom.style.marginTop = yOffset + 'px';
|
||||
buttons.bottom.forEach(function (bottomButton) {
|
||||
var $button = bottomButton.$button;
|
||||
bottomButton.indexedPosition = [xOffset + $button.offsetLeft + $button.clientWidth / 2, yOffset];
|
||||
});
|
||||
}
|
||||
function indexPosition() {
|
||||
var positionedButtons = allButtons.filter(function (button) {
|
||||
return button.indexedPosition;
|
||||
});
|
||||
|
||||
positionedButtons.forEach(findNeightbour);
|
||||
|
||||
function findNeightbour(button) {
|
||||
var neighbor = {};
|
||||
var coef = 0;
|
||||
var minCoef = {};
|
||||
var homePosition = button.indexedPosition;
|
||||
var candidatePosition, dx, dy, ds;
|
||||
var possible, dir;
|
||||
var abs = Math.abs;
|
||||
|
||||
positionedButtons.forEach(function (candidate) {
|
||||
if (button == candidate) return;
|
||||
|
||||
candidatePosition = candidate.indexedPosition;
|
||||
|
||||
possible = [];
|
||||
|
||||
dx = candidatePosition[0] - homePosition[0];
|
||||
dy = candidatePosition[1] - homePosition[1];
|
||||
ds = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
if (abs(dx) > 2) {
|
||||
possible.push(dx > 0 ? 'right' : 'left');
|
||||
possible.push(ds + abs(dy)); // coef for right/left neighbor
|
||||
}
|
||||
if (abs(dy) > 2) {
|
||||
possible.push(dy > 0 ? 'down' : 'up');
|
||||
possible.push(ds + abs(dx)); // coef for up/down neighbor
|
||||
}
|
||||
|
||||
while (possible.length) {
|
||||
dir = possible.shift();
|
||||
coef = possible.shift();
|
||||
if (!neighbor[dir] || coef < minCoef[dir]) {
|
||||
neighbor[dir] = candidate;
|
||||
minCoef[dir] = coef;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
button.neighbor = neighbor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function alwaysEnable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 为状态创建按钮
|
||||
function createButton(option) {
|
||||
var $button = createElement(div);
|
||||
addElementClass($button, 'button');
|
||||
var render = option.render || defaultButtonRender;
|
||||
$button.innerHTML = render(format, option);
|
||||
|
||||
switch (option.position) {
|
||||
case 'center':
|
||||
appendChild($center, $button);
|
||||
break;
|
||||
case 'ring':
|
||||
appendChild($ring, $button);
|
||||
break;
|
||||
case 'top':
|
||||
appendChild($top, $button);
|
||||
break;
|
||||
case 'bottom':
|
||||
appendChild($bottom, $button);
|
||||
break;
|
||||
}
|
||||
|
||||
return {
|
||||
action: option.action,
|
||||
enable: option.enable || alwaysEnable,
|
||||
beforeShow: option.beforeShow,
|
||||
key: option.key,
|
||||
next: option.next,
|
||||
label: option.label,
|
||||
data: option.data || null,
|
||||
$button: $button,
|
||||
};
|
||||
}
|
||||
|
||||
// 默认按钮渲染
|
||||
function defaultButtonRender(format, option) {
|
||||
return format('<span class="label">{label}</span><span class="key">{key}</span>', {
|
||||
label: option.label,
|
||||
key: option.key && option.key.split('|')[0],
|
||||
});
|
||||
}
|
||||
|
||||
// 为当前状态添加按钮
|
||||
this.button = function (option) {
|
||||
var button = createButton(option);
|
||||
if (option.position == 'center') {
|
||||
buttons.center = button;
|
||||
} else if (buttons[option.position]) {
|
||||
buttons[option.position].push(button);
|
||||
}
|
||||
allButtons.push(button);
|
||||
needLayout = true;
|
||||
};
|
||||
|
||||
function activeState(position) {
|
||||
position = position || {
|
||||
x: hotBox.$container.clientWidth / 2,
|
||||
y: hotBox.$container.clientHeight / 2,
|
||||
};
|
||||
if (position) {
|
||||
$state.style.left = position.x + 'px';
|
||||
$state.style.top = position.y + 'px';
|
||||
}
|
||||
allButtons.forEach(function (button) {
|
||||
var $button = button.$button;
|
||||
if ($button) {
|
||||
$button.classList[button.enable() ? 'add' : 'remove']('enabled');
|
||||
}
|
||||
|
||||
if (button.beforeShow) {
|
||||
button.beforeShow();
|
||||
}
|
||||
});
|
||||
addElementClass($state, STATE_ACTIVE_CLASS);
|
||||
if (needLayout) {
|
||||
layout();
|
||||
}
|
||||
if (!selectedButton) {
|
||||
select(buttons.center || buttons.ring[0] || buttons.top[0] || buttons.bottom[0]);
|
||||
}
|
||||
stateActived = true;
|
||||
}
|
||||
|
||||
function deactiveState() {
|
||||
removeElementClass($state, STATE_ACTIVE_CLASS);
|
||||
select(null);
|
||||
stateActived = false;
|
||||
}
|
||||
|
||||
// 激活当前状态
|
||||
this.active = activeState;
|
||||
|
||||
// 反激活当前状态
|
||||
this.deactive = deactiveState;
|
||||
|
||||
function press(button) {
|
||||
if (pressedButton && pressedButton.$button) {
|
||||
removeElementClass(pressedButton.$button, BUTTON_PRESSED_CLASS);
|
||||
}
|
||||
pressedButton = button;
|
||||
if (pressedButton && pressedButton.$button) {
|
||||
addElementClass(pressedButton.$button, BUTTON_PRESSED_CLASS);
|
||||
}
|
||||
}
|
||||
|
||||
function select(button) {
|
||||
if (selectedButton && selectedButton.$button) {
|
||||
if (selectedButton.$button) {
|
||||
removeElementClass(selectedButton.$button, BUTTON_SELECTED_CLASS);
|
||||
}
|
||||
}
|
||||
selectedButton = button;
|
||||
if (selectedButton && selectedButton.$button) {
|
||||
addElementClass(selectedButton.$button, BUTTON_SELECTED_CLASS);
|
||||
}
|
||||
}
|
||||
|
||||
$state.onmouseup = function (e) {
|
||||
if (e.button) return;
|
||||
var target = e.target;
|
||||
while (target && target != $state) {
|
||||
if (target.classList.contains('button')) {
|
||||
allButtons.forEach(function (button) {
|
||||
if (button.$button == target) {
|
||||
execute(button);
|
||||
}
|
||||
});
|
||||
}
|
||||
target = target.parentNode;
|
||||
}
|
||||
};
|
||||
|
||||
this.handleKeyEvent = function (e) {
|
||||
var handleResult = null;
|
||||
/**
|
||||
* @Desc: 搜狗浏览器下esc只触发keyup,因此做兼容性处理
|
||||
* @Editor: Naixor
|
||||
* @Date: 2015.09.14
|
||||
*/
|
||||
if (hotBox.browser.sg) {
|
||||
if (e.isKey('esc')) {
|
||||
if (pressedButton) {
|
||||
// 若存在已经按下的按钮,则取消操作
|
||||
if (!e.isKey(pressedButton.key)) {
|
||||
// the button is not esc
|
||||
press(null);
|
||||
}
|
||||
} else {
|
||||
hotBox.active('back', hotBox.position);
|
||||
}
|
||||
return 'back';
|
||||
}
|
||||
}
|
||||
if (e.keydown || (hotBox.isIME && e.keyup)) {
|
||||
allButtons.forEach(function (button) {
|
||||
if (button.enable() && e.isKey(button.key)) {
|
||||
if (stateActived || hotBox.hintDeactiveMainState) {
|
||||
select(button);
|
||||
press(button);
|
||||
handleResult = 'buttonpress';
|
||||
|
||||
// 如果是 keyup 事件触发的,因为没有后续的按键事件,所以就直接执行
|
||||
if (e.keyup) {
|
||||
execute(button);
|
||||
handleResult = 'execute';
|
||||
return handleResult;
|
||||
}
|
||||
} else {
|
||||
execute(button);
|
||||
handleResult = 'execute';
|
||||
}
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (!stateActived && hotBox.hintDeactiveMainState) {
|
||||
hotBox.active(stateName, hotBox.position);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (stateActived) {
|
||||
if (e.isKey('esc')) {
|
||||
if (pressedButton) {
|
||||
// 若存在已经按下的按钮,则取消操作
|
||||
if (!e.isKey(pressedButton.key)) {
|
||||
// the button is not esc
|
||||
press(null);
|
||||
}
|
||||
} else {
|
||||
hotBox.active('back', hotBox.position);
|
||||
}
|
||||
return 'back';
|
||||
}
|
||||
['up', 'down', 'left', 'right'].forEach(function (dir) {
|
||||
if (!e.isKey(dir)) return;
|
||||
if (!selectedButton) {
|
||||
select(buttons.center || buttons.ring[0] || buttons.top[0] || buttons.bottom[0]);
|
||||
return;
|
||||
}
|
||||
var neighbor = selectedButton.neighbor[dir];
|
||||
while (neighbor && !neighbor.enable()) {
|
||||
neighbor = neighbor.neighbor[dir];
|
||||
}
|
||||
if (neighbor) {
|
||||
select(neighbor);
|
||||
}
|
||||
handleResult = 'navigate';
|
||||
});
|
||||
|
||||
// 若是由 keyup 触发的,则直接执行选中的按钮
|
||||
if (e.isKey('space') && e.keyup) {
|
||||
execute(selectedButton);
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
handleResult = 'execute';
|
||||
} else if (e.isKey('space') && selectedButton) {
|
||||
press(selectedButton);
|
||||
handleResult = 'buttonpress';
|
||||
} else if (pressedButton && pressedButton != selectedButton) {
|
||||
press(null);
|
||||
handleResult = 'selectcancel';
|
||||
}
|
||||
}
|
||||
} else if (e.keyup && (stateActived || !hotBox.hintDeactiveMainState)) {
|
||||
if (pressedButton) {
|
||||
if ((e.isKey('space') && selectedButton == pressedButton) || e.isKey(pressedButton.key)) {
|
||||
execute(pressedButton);
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
handleResult = 'execute';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Add by zhangbobell 2015.09.06
|
||||
* 增加了下面这一个判断因为 safari 下开启输入法后,所有的 keydown 的 keycode 都为 229,
|
||||
* 只能以 keyup 的 keycode 进行判断
|
||||
* */
|
||||
hotBox.isIME = e.keyCode == 229 && e.keydown;
|
||||
|
||||
return handleResult;
|
||||
};
|
||||
|
||||
function execute(button) {
|
||||
if (button) {
|
||||
if (!button.enable || button.enable()) {
|
||||
if (button.action) button.action(button);
|
||||
hotBox.active(button.next || IDLE, hotBox.position);
|
||||
}
|
||||
press(null);
|
||||
select(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = HotBox;
|
||||
});
|
|
@ -0,0 +1,62 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
var keymap = require('./keymap');
|
||||
|
||||
var CTRL_MASK = 0x1000;
|
||||
var ALT_MASK = 0x2000;
|
||||
var SHIFT_MASK = 0x4000;
|
||||
|
||||
function hash(unknown) {
|
||||
if (typeof unknown == 'string') {
|
||||
return hashKeyExpression(unknown);
|
||||
}
|
||||
return hashKeyEvent(unknown);
|
||||
}
|
||||
function is(a, b) {
|
||||
return a && b && hash(a) == hash(b);
|
||||
}
|
||||
exports.hash = hash;
|
||||
exports.is = is;
|
||||
|
||||
function hashKeyEvent(keyEvent) {
|
||||
var hashCode = 0;
|
||||
if (keyEvent.ctrlKey || keyEvent.metaKey) {
|
||||
hashCode |= CTRL_MASK;
|
||||
}
|
||||
if (keyEvent.altKey) {
|
||||
hashCode |= ALT_MASK;
|
||||
}
|
||||
if (keyEvent.shiftKey) {
|
||||
hashCode |= SHIFT_MASK;
|
||||
}
|
||||
// Shift, Control, Alt KeyCode ignored.
|
||||
if ([16, 17, 18, 91].indexOf(keyEvent.keyCode) == -1) {
|
||||
hashCode |= keyEvent.keyCode;
|
||||
}
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
function hashKeyExpression(keyExpression) {
|
||||
var hashCode = 0;
|
||||
keyExpression
|
||||
.toLowerCase()
|
||||
.split(/\s*\+\s*/)
|
||||
.forEach(function (name) {
|
||||
switch (name) {
|
||||
case 'ctrl':
|
||||
case 'cmd':
|
||||
hashCode |= CTRL_MASK;
|
||||
break;
|
||||
case 'alt':
|
||||
hashCode |= ALT_MASK;
|
||||
break;
|
||||
case 'shift':
|
||||
hashCode |= SHIFT_MASK;
|
||||
break;
|
||||
default:
|
||||
hashCode |= keymap[name];
|
||||
}
|
||||
});
|
||||
return hashCode;
|
||||
}
|
||||
});
|
|
@ -0,0 +1,70 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
var key = require('./key');
|
||||
var FOCUS_CLASS = 'hotbox-focus';
|
||||
var RECEIVER_CLASS = 'hotbox-key-receiver';
|
||||
|
||||
function KeyControl(hotbox) {
|
||||
var _this = this;
|
||||
var _receiver;
|
||||
var _actived = true;
|
||||
var _receiverIsSelfCreated = false;
|
||||
var $container = hotbox.$container;
|
||||
|
||||
_createReceiver();
|
||||
_bindReceiver();
|
||||
_bindContainer();
|
||||
_active();
|
||||
|
||||
function _createReceiver() {
|
||||
_receiver = document.createElement('input');
|
||||
_receiver.classList.add(RECEIVER_CLASS);
|
||||
$container.appendChild(_receiver);
|
||||
_receiverIsSelfCreated = true;
|
||||
}
|
||||
|
||||
function _bindReceiver() {
|
||||
_receiver.onkeyup = _handle;
|
||||
_receiver.onkeypress = _handle;
|
||||
_receiver.onkeydown = _handle;
|
||||
_receiver.onfocus = _active;
|
||||
_receiver.onblur = _deactive;
|
||||
if (_receiverIsSelfCreated) {
|
||||
_receiver.oninput = function (e) {
|
||||
_receiver.value = null;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function _bindContainer() {
|
||||
$container.onmousedown = function (e) {
|
||||
_active();
|
||||
e.preventDefault();
|
||||
};
|
||||
}
|
||||
|
||||
function _handle(keyEvent) {
|
||||
if (!_actived) return;
|
||||
hotbox.dispatch(keyEvent);
|
||||
}
|
||||
|
||||
function _active() {
|
||||
_receiver.select();
|
||||
_receiver.focus();
|
||||
_actived = true;
|
||||
$container.classList.add(FOCUS_CLASS);
|
||||
}
|
||||
|
||||
function _deactive() {
|
||||
_receiver.blur();
|
||||
_actived = false;
|
||||
$container.classList.remove(FOCUS_CLASS);
|
||||
}
|
||||
|
||||
this.handle = _handle;
|
||||
this.active = _active;
|
||||
this.deactive = _deactive;
|
||||
}
|
||||
|
||||
module.exports = KeyControl;
|
||||
});
|
|
@ -0,0 +1,82 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
var keymap = {
|
||||
'Shift': 16,
|
||||
'Control': 17,
|
||||
'Alt': 18,
|
||||
'CapsLock': 20,
|
||||
|
||||
'BackSpace': 8,
|
||||
'Tab': 9,
|
||||
'Enter': 13,
|
||||
'Esc': 27,
|
||||
'Space': 32,
|
||||
|
||||
'PageUp': 33,
|
||||
'PageDown': 34,
|
||||
'End': 35,
|
||||
'Home': 36,
|
||||
|
||||
'Insert': 45,
|
||||
|
||||
'Left': 37,
|
||||
'Up': 38,
|
||||
'Right': 39,
|
||||
'Down': 40,
|
||||
|
||||
'Direction': {
|
||||
37: 1,
|
||||
38: 1,
|
||||
39: 1,
|
||||
40: 1,
|
||||
},
|
||||
|
||||
'Delete': 46,
|
||||
|
||||
'NumLock': 144,
|
||||
|
||||
'Cmd': 91,
|
||||
'CmdFF': 224,
|
||||
'F1': 112,
|
||||
'F2': 113,
|
||||
'F3': 114,
|
||||
'F4': 115,
|
||||
'F5': 116,
|
||||
'F6': 117,
|
||||
'F7': 118,
|
||||
'F8': 119,
|
||||
'F9': 120,
|
||||
'F10': 121,
|
||||
'F11': 122,
|
||||
'F12': 123,
|
||||
|
||||
'`': 192,
|
||||
'=': 187,
|
||||
'-': 189,
|
||||
|
||||
'/': 191,
|
||||
'.': 190,
|
||||
};
|
||||
|
||||
// 小写适配
|
||||
for (var key in keymap) {
|
||||
if (keymap.hasOwnProperty(key)) {
|
||||
keymap[key.toLowerCase()] = keymap[key];
|
||||
}
|
||||
}
|
||||
var aKeyCode = 65;
|
||||
var aCharCode = 'a'.charCodeAt(0);
|
||||
|
||||
// letters
|
||||
'abcdefghijklmnopqrstuvwxyz'.split('').forEach(function (letter) {
|
||||
keymap[letter] = aKeyCode + (letter.charCodeAt(0) - aCharCode);
|
||||
});
|
||||
|
||||
// numbers
|
||||
var n = 9;
|
||||
do {
|
||||
keymap[n.toString()] = n + 48;
|
||||
} while (n--);
|
||||
|
||||
module.exports = keymap;
|
||||
});
|
|
@ -0,0 +1,39 @@
|
|||
export const load = async (): Promise<void> => {
|
||||
try {
|
||||
if (typeof window !== 'undefined') {
|
||||
if (window.kityminder) {
|
||||
if (window.kityminder.Editor) return;
|
||||
}
|
||||
}
|
||||
|
||||
await import('kity');
|
||||
await import('./kity-core/kityminder');
|
||||
await import('./kity-editor/expose-editor');
|
||||
} catch (e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
};
|
||||
|
||||
type Options = {
|
||||
container: HTMLElement;
|
||||
isEditable: boolean;
|
||||
data?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export function renderMind(options: Options) {
|
||||
const Editor = window.kityminder.Editor;
|
||||
const editor = new Editor(options.container);
|
||||
const mind = editor.minder;
|
||||
|
||||
options.data && mind.importJson(options.data);
|
||||
|
||||
if (!options.isEditable) {
|
||||
mind.disable();
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
mind.execCommand('camera');
|
||||
}, 0);
|
||||
|
||||
return mind;
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/* eslint-disable */
|
||||
/**
|
||||
* @fileOverview
|
||||
*
|
||||
* 圆弧连线
|
||||
*
|
||||
* @author: techird
|
||||
* @copyright: Baidu FEX, 2014
|
||||
*/
|
||||
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('../core/kity');
|
||||
var connect = require('../core/connect');
|
||||
|
||||
var connectMarker = new kity.Marker().pipe(function () {
|
||||
var r = 7;
|
||||
var dot = new kity.Circle(r - 1);
|
||||
this.addShape(dot);
|
||||
this.setRef(r - 1, 0)
|
||||
.setViewBox(-r, -r, r + r, r + r)
|
||||
.setWidth(r)
|
||||
.setHeight(r);
|
||||
this.dot = dot;
|
||||
this.node.setAttribute('markerUnits', 'userSpaceOnUse');
|
||||
});
|
||||
|
||||
connect.register('arc', function (node, parent, connection, width, color) {
|
||||
var box = node.getLayoutBox(),
|
||||
pBox = parent.getLayoutBox();
|
||||
|
||||
var start, end, vector;
|
||||
var abs = Math.abs;
|
||||
var pathData = [];
|
||||
var side = box.x > pBox.x ? 'right' : 'left';
|
||||
|
||||
node.getMinder().getPaper().addResource(connectMarker);
|
||||
|
||||
start = new kity.Point(pBox.cx, pBox.cy);
|
||||
end = side == 'left' ? new kity.Point(box.right + 2, box.cy) : new kity.Point(box.left - 2, box.cy);
|
||||
|
||||
vector = kity.Vector.fromPoints(start, end);
|
||||
pathData.push('M', start);
|
||||
pathData.push('A', abs(vector.x), abs(vector.y), 0, 0, vector.x * vector.y > 0 ? 0 : 1, end);
|
||||
|
||||
connection.setMarker(connectMarker);
|
||||
connectMarker.dot.fill(color);
|
||||
|
||||
connection.setPathData(pathData);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,81 @@
|
|||
/* eslint-disable */
|
||||
/**
|
||||
*
|
||||
* 圆弧连线
|
||||
*
|
||||
* @author: along
|
||||
* @copyright: bpd729@163.com , 2015
|
||||
*/
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('../core/kity');
|
||||
var connect = require('../core/connect');
|
||||
|
||||
var connectMarker = new kity.Marker().pipe(function () {
|
||||
var r = 7;
|
||||
var dot = new kity.Circle(r - 1);
|
||||
this.addShape(dot);
|
||||
this.setRef(r - 1, 0)
|
||||
.setViewBox(-r, -r, r + r, r + r)
|
||||
.setWidth(r)
|
||||
.setHeight(r);
|
||||
this.dot = dot;
|
||||
this.node.setAttribute('markerUnits', 'userSpaceOnUse');
|
||||
});
|
||||
|
||||
/**
|
||||
* 天盘图连线除了连接当前节点和前一个节点外, 还需要渲染当前节点和后一个节点的连接, 防止样式上的断线
|
||||
* 这是天盘图与其余的模板不同的地方
|
||||
*/
|
||||
connect.register('arc_tp', function (node, parent, connection, width, color) {
|
||||
var end_box = node.getLayoutBox(),
|
||||
start_box = parent.getLayoutBox();
|
||||
|
||||
var index = node.getIndex();
|
||||
var nextNode = parent.getChildren()[index + 1];
|
||||
|
||||
if (node.getIndex() > 0) {
|
||||
start_box = parent.getChildren()[index - 1].getLayoutBox();
|
||||
}
|
||||
|
||||
var start, end, vector;
|
||||
var abs = Math.abs;
|
||||
var pathData = [];
|
||||
var side = end_box.x > start_box.x ? 'right' : 'left';
|
||||
|
||||
node.getMinder().getPaper().addResource(connectMarker);
|
||||
|
||||
start = new kity.Point(start_box.cx, start_box.cy);
|
||||
end = new kity.Point(end_box.cx, end_box.cy);
|
||||
|
||||
var jl = Math.sqrt(Math.pow(start.x - end.x, 2) + Math.pow(start.y - end.y, 2)); //两圆中心点距离
|
||||
|
||||
jl = node.getIndex() == 0 ? jl * 0.4 : jl;
|
||||
|
||||
vector = kity.Vector.fromPoints(start, end);
|
||||
pathData.push('M', start);
|
||||
pathData.push('A', jl, jl, 0, 0, 1, end);
|
||||
|
||||
connection.setMarker(connectMarker);
|
||||
connectMarker.dot.fill(color);
|
||||
connection.setPathData(pathData);
|
||||
|
||||
// 设置下一个的节点的连接线
|
||||
if (nextNode && nextNode.getConnection()) {
|
||||
var nextConnection = nextNode.getConnection();
|
||||
var next_end_box = nextNode.getLayoutBox();
|
||||
var next_end = new kity.Point(next_end_box.cx, next_end_box.cy);
|
||||
|
||||
var jl2 = Math.sqrt(Math.pow(end.x - next_end.x, 2) + Math.pow(end.y - next_end.y, 2)); //两圆中心点距离
|
||||
|
||||
pathData = [];
|
||||
|
||||
pathData.push('M', end);
|
||||
pathData.push('A', jl2, jl2, 0, 0, 1, next_end);
|
||||
|
||||
nextConnection.setMarker(connectMarker);
|
||||
connectMarker.dot.fill(color);
|
||||
|
||||
nextConnection.setPathData(pathData);
|
||||
}
|
||||
});
|
||||
});
|
|
@ -0,0 +1,42 @@
|
|||
/* eslint-disable */
|
||||
/**
|
||||
* @fileOverview
|
||||
*
|
||||
* 提供折线相连的方法
|
||||
*
|
||||
* @author: techird
|
||||
* @copyright: Baidu FEX, 2014
|
||||
*/
|
||||
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('../core/kity');
|
||||
var connect = require('../core/connect');
|
||||
|
||||
connect.register('bezier', function (node, parent, connection) {
|
||||
// 连线起点和终点
|
||||
var po = parent.getLayoutVertexOut(),
|
||||
pi = node.getLayoutVertexIn();
|
||||
|
||||
// 连线矢量和方向
|
||||
var v = parent.getLayoutVectorOut().normalize();
|
||||
|
||||
var r = Math.round;
|
||||
var abs = Math.abs;
|
||||
|
||||
var pathData = [];
|
||||
pathData.push('M', r(po.x), r(po.y));
|
||||
|
||||
if (abs(v.x) > abs(v.y)) {
|
||||
// x - direction
|
||||
var hx = (pi.x + po.x) / 2;
|
||||
pathData.push('C', hx, po.y, hx, pi.y, pi.x, pi.y);
|
||||
} else {
|
||||
// y - direction
|
||||
var hy = (pi.y + po.y) / 2;
|
||||
pathData.push('C', po.x, hy, pi.x, hy, pi.x, pi.y);
|
||||
}
|
||||
|
||||
connection.setMarker(null);
|
||||
connection.setPathData(pathData);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,33 @@
|
|||
/* eslint-disable */
|
||||
/**
|
||||
* @fileOverview
|
||||
*
|
||||
* 鱼骨头主干连线
|
||||
*
|
||||
* @author: techird
|
||||
* @copyright: Baidu FEX, 2014
|
||||
*/
|
||||
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('../core/kity');
|
||||
var connect = require('../core/connect');
|
||||
|
||||
connect.register('fish-bone-master', function (node, parent, connection) {
|
||||
var pout = parent.getLayoutVertexOut(),
|
||||
pin = node.getLayoutVertexIn();
|
||||
|
||||
var abs = Math.abs;
|
||||
|
||||
var dy = abs(pout.y - pin.y),
|
||||
dx = abs(pout.x - pin.x);
|
||||
|
||||
var pathData = [];
|
||||
|
||||
pathData.push('M', pout.x, pout.y);
|
||||
pathData.push('h', dx - dy);
|
||||
pathData.push('L', pin.x, pin.y);
|
||||
|
||||
connection.setMarker(null);
|
||||
connection.setPathData(pathData);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,34 @@
|
|||
/* eslint-disable */
|
||||
/**
|
||||
* @fileOverview
|
||||
*
|
||||
* "L" 连线
|
||||
*
|
||||
* @author: techird
|
||||
* @copyright: Baidu FEX, 2014
|
||||
*/
|
||||
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('../core/kity');
|
||||
var connect = require('../core/connect');
|
||||
|
||||
connect.register('l', function (node, parent, connection) {
|
||||
var po = parent.getLayoutVertexOut();
|
||||
var pi = node.getLayoutVertexIn();
|
||||
var vo = parent.getLayoutVectorOut();
|
||||
|
||||
var pathData = [];
|
||||
var r = Math.round,
|
||||
abs = Math.abs;
|
||||
|
||||
pathData.push('M', po.round());
|
||||
if (abs(vo.x) > abs(vo.y)) {
|
||||
pathData.push('H', r(pi.x));
|
||||
} else {
|
||||
pathData.push('V', pi.y);
|
||||
}
|
||||
pathData.push('L', pi);
|
||||
|
||||
connection.setPathData(pathData);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,62 @@
|
|||
/* eslint-disable */
|
||||
/**
|
||||
* @fileOverview
|
||||
*
|
||||
* 提供折线相连的方法
|
||||
*
|
||||
* @author: techird
|
||||
* @copyright: Baidu FEX, 2014
|
||||
*/
|
||||
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('../core/kity');
|
||||
var connect = require('../core/connect');
|
||||
|
||||
connect.register('poly', function (node, parent, connection, width) {
|
||||
// 连线起点和终点
|
||||
var po = parent.getLayoutVertexOut(),
|
||||
pi = node.getLayoutVertexIn();
|
||||
|
||||
// 连线矢量和方向
|
||||
var v = parent.getLayoutVectorOut().normalize();
|
||||
|
||||
var r = Math.round;
|
||||
var abs = Math.abs;
|
||||
|
||||
var pathData = [];
|
||||
pathData.push('M', r(po.x), r(po.y));
|
||||
|
||||
switch (true) {
|
||||
case abs(v.x) > abs(v.y) && v.x < 0:
|
||||
// left
|
||||
pathData.push('h', -parent.getStyle('margin-left'));
|
||||
pathData.push('v', pi.y - po.y);
|
||||
pathData.push('H', pi.x);
|
||||
break;
|
||||
|
||||
case abs(v.x) > abs(v.y) && v.x >= 0:
|
||||
// right
|
||||
pathData.push('h', parent.getStyle('margin-right'));
|
||||
pathData.push('v', pi.y - po.y);
|
||||
pathData.push('H', pi.x);
|
||||
break;
|
||||
|
||||
case abs(v.x) <= abs(v.y) && v.y < 0:
|
||||
// top
|
||||
pathData.push('v', -parent.getStyle('margin-top'));
|
||||
pathData.push('h', pi.x - po.x);
|
||||
pathData.push('V', pi.y);
|
||||
break;
|
||||
|
||||
case abs(v.x) <= abs(v.y) && v.y >= 0:
|
||||
// bottom
|
||||
pathData.push('v', parent.getStyle('margin-bottom'));
|
||||
pathData.push('h', pi.x - po.x);
|
||||
pathData.push('V', pi.y);
|
||||
break;
|
||||
}
|
||||
|
||||
connection.setMarker(null);
|
||||
connection.setPathData(pathData);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,49 @@
|
|||
/* eslint-disable */
|
||||
/**
|
||||
* @fileOverview
|
||||
*
|
||||
* 下划线连线
|
||||
*
|
||||
* @author: techird
|
||||
* @copyright: Baidu FEX, 2014
|
||||
*/
|
||||
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('../core/kity');
|
||||
var connect = require('../core/connect');
|
||||
|
||||
connect.register('under', function (node, parent, connection, width, color) {
|
||||
var box = node.getLayoutBox(),
|
||||
pBox = parent.getLayoutBox();
|
||||
|
||||
var start, end, vector;
|
||||
var abs = Math.abs;
|
||||
var pathData = [];
|
||||
var side = box.x > pBox.x ? 'right' : 'left';
|
||||
|
||||
var radius = node.getStyle('connect-radius');
|
||||
var underY = box.bottom + 3;
|
||||
var startY = parent.getType() == 'sub' ? pBox.bottom + 3 : pBox.cy;
|
||||
var p1, p2, p3, mx;
|
||||
|
||||
if (side == 'right') {
|
||||
p1 = new kity.Point(pBox.right, startY);
|
||||
p2 = new kity.Point(box.left - 10, underY);
|
||||
p3 = new kity.Point(box.right, underY);
|
||||
} else {
|
||||
p1 = new kity.Point(pBox.left, startY);
|
||||
p2 = new kity.Point(box.right + 10, underY);
|
||||
p3 = new kity.Point(box.left, underY);
|
||||
}
|
||||
|
||||
mx = (p1.x + p2.x) / 2;
|
||||
|
||||
pathData.push('M', p1);
|
||||
pathData.push('C', mx, p1.y, mx, p2.y, p2);
|
||||
pathData.push('L', p3);
|
||||
|
||||
connection.setMarker(null);
|
||||
|
||||
connection.setPathData(pathData);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,34 @@
|
|||
/* eslint-disable */
|
||||
/**
|
||||
* @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);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
|
@ -0,0 +1,44 @@
|
|||
/* eslint-disable */
|
||||
/**
|
||||
* @fileOverview
|
||||
*
|
||||
* 动画控制
|
||||
*
|
||||
* @author: techird
|
||||
* @copyright: Baidu FEX, 2014
|
||||
*/
|
||||
define(function (require, exports, module) {
|
||||
var Minder = require('./minder');
|
||||
|
||||
var animateDefaultOptions = {
|
||||
enableAnimation: true,
|
||||
layoutAnimationDuration: 300,
|
||||
viewAnimationDuration: 100,
|
||||
zoomAnimationDuration: 300,
|
||||
};
|
||||
var resoredAnimationOptions = {};
|
||||
|
||||
Minder.registerInitHook(function () {
|
||||
this.setDefaultOptions(animateDefaultOptions);
|
||||
if (!this.getOption('enableAnimation')) {
|
||||
this.disableAnimation();
|
||||
}
|
||||
});
|
||||
|
||||
Minder.prototype.enableAnimation = function () {
|
||||
for (var name in animateDefaultOptions) {
|
||||
if (animateDefaultOptions.hasOwnProperty(name)) {
|
||||
this.setOption(resoredAnimationOptions[name]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Minder.prototype.disableAnimation = function () {
|
||||
for (var name in animateDefaultOptions) {
|
||||
if (animateDefaultOptions.hasOwnProperty(name)) {
|
||||
resoredAnimationOptions[name] = this.getOption(name);
|
||||
this.setOption(name, 0);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
|
@ -0,0 +1,176 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('./kity');
|
||||
var utils = require('./utils');
|
||||
var Minder = require('./minder');
|
||||
var MinderNode = require('./node');
|
||||
var MinderEvent = require('./event');
|
||||
|
||||
var COMMAND_STATE_NORMAL = 0;
|
||||
var COMMAND_STATE_DISABLED = -1;
|
||||
var COMMAND_STATE_ACTIVED = 1;
|
||||
|
||||
/**
|
||||
* 表示一个命令,包含命令的查询及执行
|
||||
*/
|
||||
var Command = kity.createClass('Command', {
|
||||
constructor: function () {
|
||||
this._isContentChange = true;
|
||||
this._isSelectionChange = false;
|
||||
},
|
||||
|
||||
execute: function (minder, args) {
|
||||
throw new Error('Not Implement: Command.execute()');
|
||||
},
|
||||
|
||||
setContentChanged: function (val) {
|
||||
this._isContentChange = !!val;
|
||||
},
|
||||
|
||||
isContentChanged: function () {
|
||||
return this._isContentChange;
|
||||
},
|
||||
|
||||
setSelectionChanged: function (val) {
|
||||
this._isSelectionChange = !!val;
|
||||
},
|
||||
|
||||
isSelectionChanged: function () {
|
||||
return this._isContentChange;
|
||||
},
|
||||
|
||||
queryState: function (km) {
|
||||
return COMMAND_STATE_NORMAL;
|
||||
},
|
||||
|
||||
queryValue: function (km) {
|
||||
return 0;
|
||||
},
|
||||
|
||||
isNeedUndo: function () {
|
||||
return true;
|
||||
},
|
||||
});
|
||||
|
||||
Command.STATE_NORMAL = COMMAND_STATE_NORMAL;
|
||||
Command.STATE_ACTIVE = COMMAND_STATE_ACTIVED;
|
||||
Command.STATE_DISABLED = COMMAND_STATE_DISABLED;
|
||||
|
||||
kity.extendClass(Minder, {
|
||||
_getCommand: function (name) {
|
||||
return this._commands[name.toLowerCase()];
|
||||
},
|
||||
|
||||
_queryCommand: function (name, type, args) {
|
||||
var cmd = this._getCommand(name);
|
||||
if (cmd) {
|
||||
var queryCmd = cmd['query' + type];
|
||||
if (queryCmd) return queryCmd.apply(cmd, [this].concat(args));
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* @method queryCommandState()
|
||||
* @for Minder
|
||||
* @description 查询指定命令的状态
|
||||
*
|
||||
* @grammar queryCommandName(name) => {number}
|
||||
*
|
||||
* @param {string} name 要查询的命令名称
|
||||
*
|
||||
* @return {number}
|
||||
* -1: 命令不存在或命令当前不可用
|
||||
* 0: 命令可用
|
||||
* 1: 命令当前可用并且已经执行过
|
||||
*/
|
||||
queryCommandState: function (name) {
|
||||
return this._queryCommand(name, 'State', [].slice.call(arguments, 1));
|
||||
},
|
||||
|
||||
/**
|
||||
* @method queryCommandValue()
|
||||
* @for Minder
|
||||
* @description 查询指定命令当前的执行值
|
||||
*
|
||||
* @grammar queryCommandValue(name) => {any}
|
||||
*
|
||||
* @param {string} name 要查询的命令名称
|
||||
*
|
||||
* @return {any}
|
||||
* 如果命令不存在,返回 undefined
|
||||
* 不同命令具有不同返回值,具体请查看 [Command](command) 章节
|
||||
*/
|
||||
queryCommandValue: function (name) {
|
||||
return this._queryCommand(name, 'Value', [].slice.call(arguments, 1));
|
||||
},
|
||||
|
||||
/**
|
||||
* @method execCommand()
|
||||
* @for Minder
|
||||
* @description 执行指定的命令。
|
||||
*
|
||||
* @grammar execCommand(name, args...)
|
||||
*
|
||||
* @param {string} name 要执行的命令名称
|
||||
* @param {argument} args 要传递给命令的其它参数
|
||||
*/
|
||||
execCommand: function (name) {
|
||||
if (!name) return null;
|
||||
|
||||
name = name.toLowerCase();
|
||||
|
||||
var cmdArgs = [].slice.call(arguments, 1),
|
||||
cmd,
|
||||
stoped,
|
||||
result,
|
||||
eventParams;
|
||||
var me = this;
|
||||
cmd = this._getCommand(name);
|
||||
|
||||
eventParams = {
|
||||
command: cmd,
|
||||
commandName: name.toLowerCase(),
|
||||
commandArgs: cmdArgs,
|
||||
};
|
||||
|
||||
// 放行 zoom
|
||||
if (name !== 'zoom') {
|
||||
if (!cmd || !~this.queryCommandState(name)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this._hasEnterExecCommand) {
|
||||
this._hasEnterExecCommand = true;
|
||||
|
||||
stoped = this._fire(new MinderEvent('beforeExecCommand', eventParams, true));
|
||||
|
||||
if (!stoped) {
|
||||
this._fire(new MinderEvent('preExecCommand', eventParams, false));
|
||||
|
||||
result = cmd.execute.apply(cmd, [me].concat(cmdArgs));
|
||||
|
||||
this._fire(new MinderEvent('execCommand', eventParams, false));
|
||||
|
||||
if (cmd.isContentChanged()) {
|
||||
this._firePharse(new MinderEvent('contentchange'));
|
||||
}
|
||||
|
||||
this._interactChange();
|
||||
}
|
||||
this._hasEnterExecCommand = false;
|
||||
} else {
|
||||
result = cmd.execute.apply(cmd, [me].concat(cmdArgs));
|
||||
|
||||
if (!this._hasEnterExecCommand) {
|
||||
this._interactChange();
|
||||
}
|
||||
}
|
||||
|
||||
return result === undefined ? null : result;
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = Command;
|
||||
});
|
|
@ -0,0 +1,93 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
var utils = require('./utils');
|
||||
|
||||
function compatibility(json) {
|
||||
var version = json.version || (json.root ? '1.4.0' : '1.1.3');
|
||||
|
||||
switch (version) {
|
||||
case '1.1.3':
|
||||
c_113_120(json);
|
||||
/* falls through */
|
||||
case '1.2.0':
|
||||
case '1.2.1':
|
||||
c_120_130(json);
|
||||
/* falls through */
|
||||
case '1.3.0':
|
||||
case '1.3.1':
|
||||
case '1.3.2':
|
||||
case '1.3.3':
|
||||
case '1.3.4':
|
||||
case '1.3.5':
|
||||
/* falls through */
|
||||
c_130_140(json);
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
function traverse(node, fn) {
|
||||
fn(node);
|
||||
if (node.children)
|
||||
node.children.forEach(function (child) {
|
||||
traverse(child, fn);
|
||||
});
|
||||
}
|
||||
|
||||
/* 脑图数据升级 */
|
||||
function c_120_130(json) {
|
||||
traverse(json, function (node) {
|
||||
var data = node.data;
|
||||
delete data.layout_bottom_offset;
|
||||
delete data.layout_default_offset;
|
||||
delete data.layout_filetree_offset;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 脑图数据升级
|
||||
* v1.1.3 => v1.2.0
|
||||
* */
|
||||
function c_113_120(json) {
|
||||
// 原本的布局风格
|
||||
var ocs = json.data.currentstyle;
|
||||
delete json.data.currentstyle;
|
||||
|
||||
// 为 1.2 选择模板,同时保留老版本文件的皮肤
|
||||
if (ocs == 'bottom') {
|
||||
json.template = 'structure';
|
||||
json.theme = 'snow';
|
||||
} else if (ocs == 'default') {
|
||||
json.template = 'default';
|
||||
json.theme = 'classic';
|
||||
}
|
||||
|
||||
traverse(json, function (node) {
|
||||
var data = node.data;
|
||||
|
||||
// 升级优先级、进度图标
|
||||
if ('PriorityIcon' in data) {
|
||||
data.priority = data.PriorityIcon;
|
||||
delete data.PriorityIcon;
|
||||
}
|
||||
if ('ProgressIcon' in data) {
|
||||
data.progress = 1 + ((data.ProgressIcon - 1) << 1);
|
||||
delete data.ProgressIcon;
|
||||
}
|
||||
|
||||
// 删除过时属性
|
||||
delete data.point;
|
||||
delete data.layout;
|
||||
});
|
||||
}
|
||||
|
||||
function c_130_140(json) {
|
||||
json.root = {
|
||||
data: json.data,
|
||||
children: json.children,
|
||||
};
|
||||
delete json.data;
|
||||
delete json.children;
|
||||
}
|
||||
|
||||
return compatibility;
|
||||
});
|
|
@ -0,0 +1,122 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('./kity');
|
||||
var utils = require('./utils');
|
||||
var Module = require('./module');
|
||||
var Minder = require('./minder');
|
||||
var MinderNode = require('./node');
|
||||
|
||||
// 连线提供方
|
||||
var _connectProviders = {};
|
||||
|
||||
function register(name, provider) {
|
||||
_connectProviders[name] = provider;
|
||||
}
|
||||
|
||||
register('default', function (node, parent, connection) {
|
||||
connection.setPathData(['M', parent.getLayoutVertexOut(), 'L', node.getLayoutVertexIn()]);
|
||||
});
|
||||
|
||||
kity.extendClass(MinderNode, {
|
||||
/**
|
||||
* @private
|
||||
* @method getConnect()
|
||||
* @for MinderNode
|
||||
* @description 获取当前节点的连线类型
|
||||
*
|
||||
* @grammar getConnect() => {string}
|
||||
*/
|
||||
getConnect: function () {
|
||||
return this.data.connect || 'default';
|
||||
},
|
||||
|
||||
getConnectProvider: function () {
|
||||
return _connectProviders[this.getConnect()] || _connectProviders['default'];
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @method getConnection()
|
||||
* @for MinderNode
|
||||
* @description 获取当前节点的连线对象
|
||||
*
|
||||
* @grammar getConnection() => {kity.Path}
|
||||
*/
|
||||
getConnection: function () {
|
||||
return this._connection || null;
|
||||
},
|
||||
});
|
||||
|
||||
kity.extendClass(Minder, {
|
||||
getConnectContainer: function () {
|
||||
return this._connectContainer;
|
||||
},
|
||||
|
||||
createConnect: function (node) {
|
||||
if (node.isRoot()) return;
|
||||
|
||||
var connection = new kity.Path();
|
||||
|
||||
node._connection = connection;
|
||||
|
||||
this._connectContainer.addShape(connection);
|
||||
this.updateConnect(node);
|
||||
},
|
||||
|
||||
removeConnect: function (node) {
|
||||
var me = this;
|
||||
node.traverse(function (node) {
|
||||
me._connectContainer.removeShape(node._connection);
|
||||
node._connection = null;
|
||||
});
|
||||
},
|
||||
|
||||
updateConnect: function (node) {
|
||||
var connection = node._connection;
|
||||
var parent = node.parent;
|
||||
|
||||
if (!parent || !connection) return;
|
||||
|
||||
if (parent.isCollapsed()) {
|
||||
connection.setVisible(false);
|
||||
return;
|
||||
}
|
||||
connection.setVisible(true);
|
||||
|
||||
var provider = node.getConnectProvider();
|
||||
|
||||
var strokeColor = node.getStyle('connect-color') || 'white',
|
||||
strokeWidth = node.getStyle('connect-width') || 2;
|
||||
|
||||
connection.stroke(strokeColor, strokeWidth);
|
||||
|
||||
provider(node, parent, connection, strokeWidth, strokeColor);
|
||||
|
||||
if (strokeWidth % 2 === 0) {
|
||||
connection.setTranslate(0.5, 0.5);
|
||||
} else {
|
||||
connection.setTranslate(0, 0);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Module.register('Connect', {
|
||||
init: function () {
|
||||
this._connectContainer = new kity.Group().setId(utils.uuid('minder_connect_group'));
|
||||
this.getRenderContainer().prependShape(this._connectContainer);
|
||||
},
|
||||
events: {
|
||||
'nodeattach': function (e) {
|
||||
this.createConnect(e.node);
|
||||
},
|
||||
'nodedetach': function (e) {
|
||||
this.removeConnect(e.node);
|
||||
},
|
||||
'layoutapply layoutfinish noderender': function (e) {
|
||||
this.updateConnect(e.node);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
exports.register = register;
|
||||
});
|
|
@ -0,0 +1,366 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('./kity');
|
||||
var utils = require('./utils');
|
||||
var Minder = require('./minder');
|
||||
var MinderNode = require('./node');
|
||||
var MinderEvent = require('./event');
|
||||
var compatibility = require('./compatibility');
|
||||
var Promise = require('./promise');
|
||||
|
||||
var protocols = {};
|
||||
|
||||
function registerProtocol(name, protocol) {
|
||||
protocols[name] = protocol;
|
||||
|
||||
for (var pname in protocols) {
|
||||
if (protocols.hasOwnProperty(pname)) {
|
||||
protocols[pname] = protocols[pname];
|
||||
protocols[pname].name = pname;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getRegisterProtocol(name) {
|
||||
return name === undefined ? protocols : protocols[name] || null;
|
||||
}
|
||||
|
||||
exports.registerProtocol = registerProtocol;
|
||||
exports.getRegisterProtocol = getRegisterProtocol;
|
||||
|
||||
// 导入导出
|
||||
kity.extendClass(Minder, {
|
||||
// 自动导入
|
||||
setup: function (target) {
|
||||
if (typeof target == 'string') {
|
||||
target = document.querySelector(target);
|
||||
}
|
||||
if (!target) return;
|
||||
var protocol = target.getAttribute('minder-data-type');
|
||||
if (protocol in protocols) {
|
||||
var data = target.textContent;
|
||||
target.textContent = null;
|
||||
this.renderTo(target);
|
||||
this.importData(protocol, data);
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* @method exportJson()
|
||||
* @for Minder
|
||||
* @description
|
||||
* 导出当前脑图数据为 JSON 对象,导出的数据格式请参考 [Data](data) 章节。
|
||||
* @grammar exportJson() => {plain}
|
||||
*/
|
||||
exportJson: function () {
|
||||
/* 导出 node 上整棵树的数据为 JSON */
|
||||
function exportNode(node) {
|
||||
var exported = {};
|
||||
exported.data = node.getData();
|
||||
var childNodes = node.getChildren();
|
||||
exported.children = [];
|
||||
for (var i = 0; i < childNodes.length; i++) {
|
||||
exported.children.push(exportNode(childNodes[i]));
|
||||
}
|
||||
return exported;
|
||||
}
|
||||
|
||||
var json = {
|
||||
root: exportNode(this.getRoot()),
|
||||
};
|
||||
|
||||
json.template = this.getTemplate();
|
||||
json.theme = this.getTheme();
|
||||
json.version = Minder.version;
|
||||
|
||||
return JSON.parse(JSON.stringify(json));
|
||||
},
|
||||
|
||||
/**
|
||||
* function Text2Children(MinderNode, String)
|
||||
* @param {MinderNode} node 要导入数据的节点
|
||||
* @param {String} text 导入的text数据
|
||||
* @Desc: 用于批量插入子节点,并不会修改被插入的父节点
|
||||
* @Editor: Naixor
|
||||
* @Date: 2015.9.21
|
||||
* @example: 用于批量导入如下类型的节点
|
||||
* 234
|
||||
* 3456346 asadf
|
||||
* 12312414
|
||||
* wereww
|
||||
* 12314
|
||||
* 1231412
|
||||
* 13123
|
||||
*/
|
||||
Text2Children: function (node, text) {
|
||||
if (!(node instanceof kityminder.Node)) {
|
||||
return;
|
||||
// throw new Error('Json2Children::node is not a kityminder.Node type!');
|
||||
}
|
||||
var children = [],
|
||||
jsonMap = {},
|
||||
level = 0;
|
||||
|
||||
var LINE_SPLITTER = /\r|\n|\r\n/,
|
||||
TAB_REGEXP = /^(\t|\x20{4})/;
|
||||
|
||||
var lines = text.split(LINE_SPLITTER),
|
||||
line = '',
|
||||
jsonNode,
|
||||
i = 0;
|
||||
var minder = this;
|
||||
|
||||
function isEmpty(line) {
|
||||
return line === '' && !/\S/.test(line);
|
||||
}
|
||||
|
||||
function getNode(line) {
|
||||
return {
|
||||
data: {
|
||||
text: line.replace(/^(\t|\x20{4})+/, '').replace(/(\t|\x20{4})+$/, ''),
|
||||
},
|
||||
children: [],
|
||||
};
|
||||
}
|
||||
|
||||
function getLevel(text) {
|
||||
var level = 0;
|
||||
while (TAB_REGEXP.test(text)) {
|
||||
text = text.replace(TAB_REGEXP, '');
|
||||
level++;
|
||||
}
|
||||
return level;
|
||||
}
|
||||
|
||||
function addChild(parent, node) {
|
||||
parent.children.push(node);
|
||||
}
|
||||
|
||||
function importChildren(node, children) {
|
||||
for (var i = 0, l = children.length; i < l; i++) {
|
||||
var childNode = minder.createNode(null, node);
|
||||
childNode.setData('text', children[i].data.text || '');
|
||||
importChildren(childNode, children[i].children);
|
||||
}
|
||||
}
|
||||
|
||||
while ((line = lines[i++]) !== undefined) {
|
||||
line = line.replace(/ /g, '');
|
||||
if (isEmpty(line)) continue;
|
||||
|
||||
level = getLevel(line);
|
||||
jsonNode = getNode(line);
|
||||
if (level === 0) {
|
||||
jsonMap = {};
|
||||
children.push(jsonNode);
|
||||
jsonMap[0] = children[children.length - 1];
|
||||
} else {
|
||||
if (!jsonMap[level - 1]) {
|
||||
throw new Error('Invalid local format');
|
||||
}
|
||||
addChild(jsonMap[level - 1], jsonNode);
|
||||
jsonMap[level] = jsonNode;
|
||||
}
|
||||
}
|
||||
|
||||
importChildren(node, children);
|
||||
minder.refresh();
|
||||
},
|
||||
|
||||
/**
|
||||
* @method exportNode(MinderNode)
|
||||
* @param {MinderNode} node 当前要被导出的节点
|
||||
* @return {Object} 返回只含有data和children的Object
|
||||
* @Editor: Naixor
|
||||
* @Date: 2015.9.22
|
||||
*/
|
||||
exportNode: function (node) {
|
||||
var exported = {};
|
||||
exported.data = node.getData();
|
||||
var childNodes = node.getChildren();
|
||||
exported.children = [];
|
||||
for (var i = 0; i < childNodes.length; i++) {
|
||||
exported.children.push(this.exportNode(childNodes[i]));
|
||||
}
|
||||
return exported;
|
||||
},
|
||||
/**
|
||||
* @method importNode()
|
||||
* @description 根据纯json {data, children}数据转换成为脑图节点
|
||||
* @Editor: Naixor
|
||||
* @Date: 2015.9.20
|
||||
*/
|
||||
importNode: function (node, json) {
|
||||
var data = json.data;
|
||||
node.data = {};
|
||||
|
||||
for (var field in data) {
|
||||
node.setData(field, data[field]);
|
||||
}
|
||||
|
||||
var childrenTreeData = json.children || [];
|
||||
for (var i = 0; i < childrenTreeData.length; i++) {
|
||||
var childNode = this.createNode(null, node);
|
||||
this.importNode(childNode, childrenTreeData[i]);
|
||||
}
|
||||
return node;
|
||||
},
|
||||
|
||||
/**
|
||||
* @method importJson()
|
||||
* @for Minder
|
||||
* @description 导入脑图数据,数据为 JSON 对象,具体的数据字段形式请参考 [Data](data) 章节。
|
||||
*
|
||||
* @grammar importJson(json) => {this}
|
||||
*
|
||||
* @param {plain} json 要导入的数据
|
||||
*/
|
||||
importJson: function (json) {
|
||||
if (!json) return;
|
||||
|
||||
/**
|
||||
* @event preimport
|
||||
* @for Minder
|
||||
* @when 导入数据之前
|
||||
*/
|
||||
this._fire(new MinderEvent('preimport', null, false));
|
||||
|
||||
// 删除当前所有节点
|
||||
while (this._root.getChildren().length) {
|
||||
this.removeNode(this._root.getChildren()[0]);
|
||||
}
|
||||
|
||||
json = compatibility(json);
|
||||
|
||||
this.importNode(this._root, json.root);
|
||||
|
||||
this.setTemplate(json.template || 'default');
|
||||
this.setTheme(json.theme || null);
|
||||
this.refresh();
|
||||
|
||||
/**
|
||||
* @event import,contentchange,interactchange
|
||||
* @for Minder
|
||||
* @when 导入数据之后
|
||||
*/
|
||||
this.fire('import');
|
||||
|
||||
this._firePharse({
|
||||
type: 'contentchange',
|
||||
});
|
||||
|
||||
this._interactChange();
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* @method exportData()
|
||||
* @for Minder
|
||||
* @description 使用指定使用的数据协议,导入脑图数据
|
||||
*
|
||||
* @grammar exportData(protocol) => Promise<data>
|
||||
*
|
||||
* @param {string} protocol 指定的数据协议(默认内置五种数据协议 `json`、`text`、`markdown`、`svg` 和 `png`)
|
||||
*/
|
||||
exportData: function (protocolName, option) {
|
||||
var json, protocol;
|
||||
|
||||
json = this.exportJson();
|
||||
|
||||
// 指定了协议进行导出,需要检测协议是否支持
|
||||
if (protocolName) {
|
||||
protocol = protocols[protocolName];
|
||||
|
||||
if (!protocol || !protocol.encode) {
|
||||
return Promise.reject(new Error('Not supported protocol:' + protocolName));
|
||||
}
|
||||
}
|
||||
|
||||
// 导出前抛个事件
|
||||
this._fire(
|
||||
new MinderEvent('beforeexport', {
|
||||
json: json,
|
||||
protocolName: protocolName,
|
||||
protocol: protocol,
|
||||
})
|
||||
);
|
||||
|
||||
return Promise.resolve(protocol.encode(json, this, option));
|
||||
},
|
||||
|
||||
/**
|
||||
* @method importData()
|
||||
* @for Minder
|
||||
* @description 使用指定的数据协议,导入脑图数据,覆盖当前实例的脑图
|
||||
*
|
||||
* @grammar importData(protocol, callback) => Promise<json>
|
||||
*
|
||||
* @param {string} protocol 指定的用于解析数据的数据协议(默认内置三种数据协议 `json`、`text` 和 `markdown` 的支持)
|
||||
* @param {any} data 要导入的数据
|
||||
*/
|
||||
importData: function (protocolName, data, option) {
|
||||
var json, protocol;
|
||||
var minder = this;
|
||||
|
||||
// 指定了协议进行导入,需要检测协议是否支持
|
||||
if (protocolName) {
|
||||
protocol = protocols[protocolName];
|
||||
|
||||
if (!protocol || !protocol.decode) {
|
||||
return Promise.reject(new Error('Not supported protocol:' + protocolName));
|
||||
}
|
||||
}
|
||||
|
||||
var params = {
|
||||
local: data,
|
||||
protocolName: protocolName,
|
||||
protocol: protocol,
|
||||
};
|
||||
|
||||
// 导入前抛事件
|
||||
this._fire(new MinderEvent('beforeimport', params));
|
||||
|
||||
return Promise.resolve(protocol.decode(data, this, option)).then(function (json) {
|
||||
minder.importJson(json);
|
||||
return json;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @method decodeData()
|
||||
* @for Minder
|
||||
* @description 使用指定的数据协议,解析为脑图数据,与 importData 的区别在于:不覆盖当前实例的脑图
|
||||
*
|
||||
* @grammar decodeData(protocol, callback) => Promise<json>
|
||||
*
|
||||
* @param {string} protocol 指定的用于解析数据的数据协议(默认内置三种数据协议 `json`、`text` 和 `markdown` 的支持)
|
||||
* @param {any} data 要导入的数据
|
||||
*/
|
||||
decodeData: function (protocolName, data, option) {
|
||||
var json, protocol;
|
||||
var minder = this;
|
||||
|
||||
// 指定了协议进行导入,需要检测协议是否支持
|
||||
if (protocolName) {
|
||||
protocol = protocols[protocolName];
|
||||
|
||||
if (!protocol || !protocol.decode) {
|
||||
return Promise.reject(new Error('Not supported protocol:' + protocolName));
|
||||
}
|
||||
}
|
||||
|
||||
var params = {
|
||||
local: data,
|
||||
protocolName: protocolName,
|
||||
protocol: protocol,
|
||||
};
|
||||
|
||||
// 导入前抛事件
|
||||
this._fire(new MinderEvent('beforeimport', params));
|
||||
|
||||
return Promise.resolve(protocol.decode(data, this, option));
|
||||
},
|
||||
});
|
||||
});
|
|
@ -0,0 +1,264 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('./kity');
|
||||
var utils = require('./utils');
|
||||
var Minder = require('./minder');
|
||||
|
||||
/**
|
||||
* @class MinderEvent
|
||||
* @description 表示一个脑图中发生的事件
|
||||
*/
|
||||
var MinderEvent = kity.createClass('MindEvent', {
|
||||
constructor: function (type, params, canstop) {
|
||||
params = params || {};
|
||||
if (params.getType && params.getType() == 'ShapeEvent') {
|
||||
/**
|
||||
* @property kityEvent
|
||||
* @for MinderEvent
|
||||
* @description 如果事件是从一个 kity 的事件派生的,会有 kityEvent 属性指向原来的 kity 事件
|
||||
* @type {KityEvent}
|
||||
*/
|
||||
this.kityEvent = params;
|
||||
|
||||
/**
|
||||
* @property originEvent
|
||||
* @for MinderEvent
|
||||
* @description 如果事件是从原声 Dom 事件派生的(如 click、mousemove 等),会有 originEvent 指向原来的 Dom 事件
|
||||
* @type {DomEvent}
|
||||
*/
|
||||
this.originEvent = params.originEvent;
|
||||
} else if (params.target && params.preventDefault) {
|
||||
this.originEvent = params;
|
||||
} else {
|
||||
kity.Utils.extend(this, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @property type
|
||||
* @for MinderEvent
|
||||
* @description 事件的类型,如 `click`、`contentchange` 等
|
||||
* @type {string}
|
||||
*/
|
||||
this.type = type;
|
||||
this._canstop = canstop || false;
|
||||
},
|
||||
|
||||
/**
|
||||
* @method getPosition()
|
||||
* @for MinderEvent
|
||||
* @description 如果事件是从一个 kity 事件派生的,会有 `getPosition()` 获取事件发生的坐标
|
||||
*
|
||||
* @grammar getPosition(refer) => {kity.Point}
|
||||
*
|
||||
* @param {string|kity.Shape} refer
|
||||
* 参照的坐标系,
|
||||
* `"screen"` - 以浏览器屏幕为参照坐标系
|
||||
* `"minder"` - (默认)以脑图画布为参照坐标系
|
||||
* `{kity.Shape}` - 指定以某个 kity 图形为参照坐标系
|
||||
*/
|
||||
getPosition: function (refer) {
|
||||
if (!this.kityEvent) return;
|
||||
if (!refer || refer == 'minder') {
|
||||
return this.kityEvent.getPosition(this.minder.getRenderContainer());
|
||||
}
|
||||
return this.kityEvent.getPosition.call(this.kityEvent, refer);
|
||||
},
|
||||
|
||||
/**
|
||||
* @method getTargetNode()
|
||||
* @for MinderEvent
|
||||
* @description 当发生的事件是鼠标事件时,获取事件位置命中的脑图节点
|
||||
*
|
||||
* @grammar getTargetNode() => {MinderNode}
|
||||
*/
|
||||
getTargetNode: function () {
|
||||
var findShape = this.kityEvent && this.kityEvent.targetShape;
|
||||
if (!findShape) return null;
|
||||
while (!findShape.minderNode && findShape.container) {
|
||||
findShape = findShape.container;
|
||||
}
|
||||
var node = findShape.minderNode;
|
||||
if (node && findShape.getOpacity() < 1) return null;
|
||||
return node || null;
|
||||
},
|
||||
|
||||
/**
|
||||
* @method stopPropagation()
|
||||
* @for MinderEvent
|
||||
* @description 当发生的事件是鼠标事件时,获取事件位置命中的脑图节点
|
||||
*
|
||||
* @grammar getTargetNode() => {MinderNode}
|
||||
*/
|
||||
stopPropagation: function () {
|
||||
this._stoped = true;
|
||||
},
|
||||
|
||||
stopPropagationImmediately: function () {
|
||||
this._immediatelyStoped = true;
|
||||
this._stoped = true;
|
||||
},
|
||||
|
||||
shouldStopPropagation: function () {
|
||||
return this._canstop && this._stoped;
|
||||
},
|
||||
|
||||
shouldStopPropagationImmediately: function () {
|
||||
return this._canstop && this._immediatelyStoped;
|
||||
},
|
||||
preventDefault: function () {
|
||||
this.originEvent.preventDefault();
|
||||
},
|
||||
isRightMB: function () {
|
||||
var isRightMB = false;
|
||||
if (!this.originEvent) {
|
||||
return false;
|
||||
}
|
||||
if ('which' in this.originEvent) isRightMB = this.originEvent.which == 3;
|
||||
else if ('button' in this.originEvent) isRightMB = this.originEvent.button == 2;
|
||||
return isRightMB;
|
||||
},
|
||||
getKeyCode: function () {
|
||||
var evt = this.originEvent;
|
||||
return evt.keyCode || evt.which;
|
||||
},
|
||||
});
|
||||
|
||||
Minder.registerInitHook(function (option) {
|
||||
this._initEvents();
|
||||
});
|
||||
|
||||
kity.extendClass(Minder, {
|
||||
_initEvents: function () {
|
||||
this._eventCallbacks = {};
|
||||
},
|
||||
|
||||
_resetEvents: function () {
|
||||
this._initEvents();
|
||||
this._bindEvents();
|
||||
},
|
||||
|
||||
_bindEvents: function () {
|
||||
/* jscs:disable maximumLineLength */
|
||||
this._paper.on(
|
||||
'click dblclick mousedown contextmenu mouseup mousemove mouseover mousewheel DOMMouseScroll touchstart touchmove touchend dragenter dragleave drop',
|
||||
this._firePharse.bind(this)
|
||||
);
|
||||
if (window) {
|
||||
window.addEventListener('resize', this._firePharse.bind(this));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @method dispatchKeyEvent
|
||||
* @description 派发键盘(相关)事件到脑图实例上,让实例的模块处理
|
||||
* @grammar dispatchKeyEvent(e) => {this}
|
||||
* @param {Event} e 原生的 Dom 事件对象
|
||||
*/
|
||||
dispatchKeyEvent: function (e) {
|
||||
this._firePharse(e);
|
||||
},
|
||||
|
||||
_firePharse: function (e) {
|
||||
var beforeEvent, preEvent, executeEvent;
|
||||
|
||||
if (e.type == 'DOMMouseScroll') {
|
||||
e.type = 'mousewheel';
|
||||
e.wheelDelta = e.originEvent.wheelDelta = e.originEvent.detail * -10;
|
||||
e.wheelDeltaX = e.originEvent.mozMovementX;
|
||||
e.wheelDeltaY = e.originEvent.mozMovementY;
|
||||
}
|
||||
|
||||
beforeEvent = new MinderEvent('before' + e.type, e, true);
|
||||
if (this._fire(beforeEvent)) {
|
||||
return;
|
||||
}
|
||||
preEvent = new MinderEvent('pre' + e.type, e, true);
|
||||
executeEvent = new MinderEvent(e.type, e, true);
|
||||
|
||||
if (this._fire(preEvent) || this._fire(executeEvent)) this._fire(new MinderEvent('after' + e.type, e, false));
|
||||
},
|
||||
|
||||
_interactChange: function (e) {
|
||||
var me = this;
|
||||
if (me._interactScheduled) return;
|
||||
setTimeout(function () {
|
||||
me._fire(new MinderEvent('interactchange'));
|
||||
me._interactScheduled = false;
|
||||
}, 100);
|
||||
me._interactScheduled = true;
|
||||
},
|
||||
|
||||
_listen: function (type, callback) {
|
||||
var callbacks = this._eventCallbacks[type] || (this._eventCallbacks[type] = []);
|
||||
callbacks.push(callback);
|
||||
},
|
||||
|
||||
_fire: function (e) {
|
||||
/**
|
||||
* @property minder
|
||||
* @description 产生事件的 Minder 对象
|
||||
* @for MinderShape
|
||||
* @type {Minder}
|
||||
*/
|
||||
e.minder = this;
|
||||
|
||||
var status = this.getStatus();
|
||||
var callbacks = this._eventCallbacks[e.type.toLowerCase()] || [];
|
||||
|
||||
if (status) {
|
||||
callbacks = callbacks.concat(this._eventCallbacks[status + '.' + e.type.toLowerCase()] || []);
|
||||
}
|
||||
|
||||
if (callbacks.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var lastStatus = this.getStatus();
|
||||
for (var i = 0; i < callbacks.length; i++) {
|
||||
callbacks[i].call(this, e);
|
||||
|
||||
/* this.getStatus() != lastStatus ||*/
|
||||
if (e.shouldStopPropagationImmediately()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return e.shouldStopPropagation();
|
||||
},
|
||||
|
||||
on: function (name, callback) {
|
||||
var km = this;
|
||||
name.split(/\s+/).forEach(function (n) {
|
||||
km._listen(n.toLowerCase(), callback);
|
||||
});
|
||||
return this;
|
||||
},
|
||||
|
||||
off: function (name, callback) {
|
||||
var types = name.split(/\s+/);
|
||||
var i, j, callbacks, removeIndex;
|
||||
for (i = 0; i < types.length; i++) {
|
||||
callbacks = this._eventCallbacks[types[i].toLowerCase()];
|
||||
if (callbacks) {
|
||||
removeIndex = null;
|
||||
for (j = 0; j < callbacks.length; j++) {
|
||||
if (callbacks[j] == callback) {
|
||||
removeIndex = j;
|
||||
}
|
||||
}
|
||||
if (removeIndex !== null) {
|
||||
callbacks.splice(removeIndex, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
fire: function (type, params) {
|
||||
var e = new MinderEvent(type, params);
|
||||
this._fire(e);
|
||||
return this;
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = MinderEvent;
|
||||
});
|
|
@ -0,0 +1,44 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('./kity');
|
||||
var Minder = require('./minder');
|
||||
|
||||
Minder.registerInitHook(function () {
|
||||
this.on('beforemousedown', function (e) {
|
||||
this.focus();
|
||||
// FIXME:如果遇到事件触发问题,需要检查这里
|
||||
if (e.kityEvent.targetShape.__KityClassName === 'Paper') return;
|
||||
e.preventDefault();
|
||||
});
|
||||
this.on('paperrender', function () {
|
||||
this.focus();
|
||||
});
|
||||
});
|
||||
|
||||
kity.extendClass(Minder, {
|
||||
focus: function () {
|
||||
if (!this.isFocused()) {
|
||||
var renderTarget = this._renderTarget;
|
||||
renderTarget.classList.add('focus');
|
||||
this.renderNodeBatch(this.getSelectedNodes());
|
||||
}
|
||||
this.fire('focus');
|
||||
return this;
|
||||
},
|
||||
|
||||
blur: function () {
|
||||
if (this.isFocused()) {
|
||||
var renderTarget = this._renderTarget;
|
||||
renderTarget.classList.remove('focus');
|
||||
this.renderNodeBatch(this.getSelectedNodes());
|
||||
}
|
||||
this.fire('blur');
|
||||
return this;
|
||||
},
|
||||
|
||||
isFocused: function () {
|
||||
var renderTarget = this._renderTarget;
|
||||
return renderTarget && renderTarget.classList.contains('focus');
|
||||
},
|
||||
});
|
||||
});
|
|
@ -0,0 +1,129 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
var keymap = {
|
||||
'Backspace': 8,
|
||||
'Tab': 9,
|
||||
'Enter': 13,
|
||||
|
||||
'Shift': 16,
|
||||
'Control': 17,
|
||||
'Alt': 18,
|
||||
'CapsLock': 20,
|
||||
|
||||
'Esc': 27,
|
||||
|
||||
'Spacebar': 32,
|
||||
|
||||
'PageUp': 33,
|
||||
'PageDown': 34,
|
||||
'End': 35,
|
||||
'Home': 36,
|
||||
|
||||
'Insert': 45,
|
||||
|
||||
'Left': 37,
|
||||
'Up': 38,
|
||||
'Right': 39,
|
||||
'Down': 40,
|
||||
|
||||
'direction': {
|
||||
37: 1,
|
||||
38: 1,
|
||||
39: 1,
|
||||
40: 1,
|
||||
},
|
||||
|
||||
'Del': 46,
|
||||
|
||||
'NumLock': 144,
|
||||
|
||||
'Cmd': 91,
|
||||
'CmdFF': 224,
|
||||
'F1': 112,
|
||||
'F2': 113,
|
||||
'F3': 114,
|
||||
'F4': 115,
|
||||
'F5': 116,
|
||||
'F6': 117,
|
||||
'F7': 118,
|
||||
'F8': 119,
|
||||
'F9': 120,
|
||||
'F10': 121,
|
||||
'F11': 122,
|
||||
'F12': 123,
|
||||
|
||||
'`': 192,
|
||||
'=': 187,
|
||||
'-': 189,
|
||||
|
||||
'/': 191,
|
||||
'.': 190,
|
||||
'controlKeys': {
|
||||
16: 1,
|
||||
17: 1,
|
||||
18: 1,
|
||||
20: 1,
|
||||
91: 1,
|
||||
224: 1,
|
||||
},
|
||||
'notContentChange': {
|
||||
13: 1,
|
||||
9: 1,
|
||||
|
||||
33: 1,
|
||||
34: 1,
|
||||
35: 1,
|
||||
36: 1,
|
||||
|
||||
16: 1,
|
||||
17: 1,
|
||||
18: 1,
|
||||
20: 1,
|
||||
91: 1,
|
||||
|
||||
//上下左右
|
||||
37: 1,
|
||||
38: 1,
|
||||
39: 1,
|
||||
40: 1,
|
||||
|
||||
113: 1,
|
||||
114: 1,
|
||||
115: 1,
|
||||
144: 1,
|
||||
27: 1,
|
||||
},
|
||||
|
||||
'isSelectedNodeKey': {
|
||||
//上下左右
|
||||
37: 1,
|
||||
38: 1,
|
||||
39: 1,
|
||||
40: 1,
|
||||
13: 1,
|
||||
9: 1,
|
||||
},
|
||||
};
|
||||
|
||||
// 小写适配
|
||||
for (var key in keymap) {
|
||||
if (keymap.hasOwnProperty(key)) {
|
||||
keymap[key.toLowerCase()] = keymap[key];
|
||||
}
|
||||
}
|
||||
var aKeyCode = 65;
|
||||
var aCharCode = 'a'.charCodeAt(0);
|
||||
|
||||
// letters
|
||||
'abcdefghijklmnopqrstuvwxyz'.split('').forEach(function (letter) {
|
||||
keymap[letter] = aKeyCode + (letter.charCodeAt(0) - aCharCode);
|
||||
});
|
||||
|
||||
// numbers
|
||||
var n = 9;
|
||||
do {
|
||||
keymap[n.toString()] = n + 48;
|
||||
} while (--n);
|
||||
|
||||
module.exports = keymap;
|
||||
});
|
|
@ -0,0 +1,66 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('./kity');
|
||||
var utils = require('./utils');
|
||||
var Minder = require('./minder');
|
||||
|
||||
function listen(element, type, handler) {
|
||||
type.split(' ').forEach(function (name) {
|
||||
element.addEventListener(name, handler, false);
|
||||
});
|
||||
}
|
||||
|
||||
Minder.registerInitHook(function (option) {
|
||||
this.setDefaultOptions({
|
||||
enableKeyReceiver: true,
|
||||
});
|
||||
if (this.getOption('enableKeyReceiver')) {
|
||||
this.on('paperrender', function () {
|
||||
this._initKeyReceiver();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
kity.extendClass(Minder, {
|
||||
_initKeyReceiver: function () {
|
||||
if (this._keyReceiver) return;
|
||||
|
||||
var receiver = (this._keyReceiver = document.createElement('input'));
|
||||
receiver.classList.add('km-receiver');
|
||||
|
||||
var renderTarget = this._renderTarget;
|
||||
renderTarget.appendChild(receiver);
|
||||
|
||||
var minder = this;
|
||||
|
||||
listen(receiver, 'keydown keyup keypress copy paste blur focus input', function (e) {
|
||||
switch (e.type) {
|
||||
case 'blur':
|
||||
minder.blur();
|
||||
break;
|
||||
case 'focus':
|
||||
minder.focus();
|
||||
break;
|
||||
case 'input':
|
||||
receiver.value = null;
|
||||
break;
|
||||
}
|
||||
minder._firePharse(e);
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
this.on('focus', function () {
|
||||
receiver.select();
|
||||
receiver.focus();
|
||||
});
|
||||
this.on('blur', function () {
|
||||
receiver.blur();
|
||||
});
|
||||
|
||||
if (this.isFocused()) {
|
||||
receiver.select();
|
||||
receiver.focus();
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
|
@ -0,0 +1,12 @@
|
|||
/* eslint-disable */
|
||||
/**
|
||||
* @fileOverview
|
||||
*
|
||||
* Kity 引入
|
||||
*
|
||||
* @author: techird
|
||||
* @copyright: Baidu FEX, 2014
|
||||
*/
|
||||
define(function (require, exports, module) {
|
||||
module.exports = window.kity;
|
||||
});
|
|
@ -0,0 +1,529 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('./kity');
|
||||
var utils = require('./utils');
|
||||
var Minder = require('./minder');
|
||||
var MinderNode = require('./node');
|
||||
var MinderEvent = require('./event');
|
||||
var Command = require('./command');
|
||||
|
||||
var _layouts = {};
|
||||
var _defaultLayout;
|
||||
|
||||
function register(name, layout) {
|
||||
_layouts[name] = layout;
|
||||
_defaultLayout = _defaultLayout || name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @class Layout 布局基类,具体布局需要从该类派生
|
||||
*/
|
||||
var Layout = kity.createClass('Layout', {
|
||||
/**
|
||||
* @abstract
|
||||
*
|
||||
* 子类需要实现的布局算法,该算法输入一个节点,排布该节点的子节点(相对父节点的变换)
|
||||
*
|
||||
* @param {MinderNode} node 需要布局的节点
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* doLayout: function(node) {
|
||||
* var children = node.getChildren();
|
||||
* // layout calculation
|
||||
* children[i].setLayoutTransform(new kity.Matrix().translate(x, y));
|
||||
* }
|
||||
*/
|
||||
doLayout: function (parent, children) {
|
||||
throw new Error('Not Implement: Layout.doLayout()');
|
||||
},
|
||||
|
||||
/**
|
||||
* 对齐指定的节点
|
||||
*
|
||||
* @param {Array<MinderNode>} nodes 要对齐的节点
|
||||
* @param {string} border 对齐边界,允许取值 left, right, top, bottom
|
||||
*
|
||||
*/
|
||||
align: function (nodes, border, offset) {
|
||||
var me = this;
|
||||
offset = offset || 0;
|
||||
nodes.forEach(function (node) {
|
||||
var tbox = me.getTreeBox([node]);
|
||||
var matrix = node.getLayoutTransform();
|
||||
switch (border) {
|
||||
case 'left':
|
||||
return matrix.translate(offset - tbox.left, 0);
|
||||
case 'right':
|
||||
return matrix.translate(offset - tbox.right, 0);
|
||||
case 'top':
|
||||
return matrix.translate(0, offset - tbox.top);
|
||||
case 'bottom':
|
||||
return matrix.translate(0, offset - tbox.bottom);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
stack: function (nodes, axis, distance) {
|
||||
var me = this;
|
||||
|
||||
var position = 0;
|
||||
|
||||
distance =
|
||||
distance ||
|
||||
function (node, next, axis) {
|
||||
return (
|
||||
node.getStyle(
|
||||
{
|
||||
x: 'margin-right',
|
||||
y: 'margin-bottom',
|
||||
}[axis]
|
||||
) +
|
||||
next.getStyle(
|
||||
{
|
||||
x: 'margin-left',
|
||||
y: 'margin-top',
|
||||
}[axis]
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
nodes.forEach(function (node, index, nodes) {
|
||||
var tbox = me.getTreeBox([node]);
|
||||
|
||||
var size = {
|
||||
x: tbox.width,
|
||||
y: tbox.height,
|
||||
}[axis];
|
||||
var offset = {
|
||||
x: tbox.left,
|
||||
y: tbox.top,
|
||||
}[axis];
|
||||
|
||||
var matrix = node.getLayoutTransform();
|
||||
|
||||
if (axis == 'x') {
|
||||
matrix.translate(position - offset, 0);
|
||||
} else {
|
||||
matrix.translate(0, position - offset);
|
||||
}
|
||||
position += size;
|
||||
if (nodes[index + 1]) position += distance(node, nodes[index + 1], axis);
|
||||
});
|
||||
return position;
|
||||
},
|
||||
|
||||
move: function (nodes, dx, dy) {
|
||||
nodes.forEach(function (node) {
|
||||
node.getLayoutTransform().translate(dx, dy);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 工具方法:获取给点的节点所占的布局区域
|
||||
*
|
||||
* @param {MinderNode[]} nodes 需要计算的节点
|
||||
*
|
||||
* @return {Box} 计算结果
|
||||
*/
|
||||
getBranchBox: function (nodes) {
|
||||
var box = new kity.Box();
|
||||
var i, node, matrix, contentBox;
|
||||
for (i = 0; i < nodes.length; i++) {
|
||||
node = nodes[i];
|
||||
matrix = node.getLayoutTransform();
|
||||
contentBox = node.getContentBox();
|
||||
box = box.merge(matrix.transformBox(contentBox));
|
||||
}
|
||||
|
||||
return box;
|
||||
},
|
||||
|
||||
/**
|
||||
* 工具方法:计算给定的节点的子树所占的布局区域
|
||||
*
|
||||
* @param {MinderNode} nodes 需要计算的节点
|
||||
*
|
||||
* @return {Box} 计算的结果
|
||||
*/
|
||||
getTreeBox: function (nodes) {
|
||||
var i, node, matrix, treeBox;
|
||||
|
||||
var box = new kity.Box();
|
||||
|
||||
if (!(nodes instanceof Array)) nodes = [nodes];
|
||||
|
||||
for (i = 0; i < nodes.length; i++) {
|
||||
node = nodes[i];
|
||||
matrix = node.getLayoutTransform();
|
||||
|
||||
treeBox = node.getContentBox();
|
||||
|
||||
if (node.isExpanded() && node.children.length) {
|
||||
treeBox = treeBox.merge(this.getTreeBox(node.children));
|
||||
}
|
||||
|
||||
box = box.merge(matrix.transformBox(treeBox));
|
||||
}
|
||||
|
||||
return box;
|
||||
},
|
||||
|
||||
getOrderHint: function (node) {
|
||||
return [];
|
||||
},
|
||||
});
|
||||
|
||||
Layout.register = register;
|
||||
|
||||
Minder.registerInitHook(function (options) {
|
||||
this.refresh();
|
||||
});
|
||||
|
||||
/**
|
||||
* 布局支持池子管理
|
||||
*/
|
||||
utils.extend(Minder, {
|
||||
getLayoutList: function () {
|
||||
return _layouts;
|
||||
},
|
||||
|
||||
getLayoutInstance: function (name) {
|
||||
var LayoutClass = _layouts[name];
|
||||
if (!LayoutClass) throw new Error('Missing Layout: ' + name);
|
||||
var layout = new LayoutClass();
|
||||
return layout;
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* MinderNode 上的布局支持
|
||||
*/
|
||||
kity.extendClass(MinderNode, {
|
||||
/**
|
||||
* 获得当前节点的布局名称
|
||||
*
|
||||
* @return {String}
|
||||
*/
|
||||
getLayout: function () {
|
||||
var layout = this.getData('layout');
|
||||
|
||||
layout = layout || (this.isRoot() ? _defaultLayout : this.parent.getLayout());
|
||||
|
||||
return layout;
|
||||
},
|
||||
|
||||
setLayout: function (name) {
|
||||
if (name) {
|
||||
if (name == 'inherit') {
|
||||
this.setData('layout');
|
||||
} else {
|
||||
this.setData('layout', name);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
layout: function (name) {
|
||||
this.setLayout(name).getMinder().layout();
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
getLayoutInstance: function () {
|
||||
return Minder.getLayoutInstance(this.getLayout());
|
||||
},
|
||||
|
||||
getOrderHint: function (refer) {
|
||||
return this.parent.getLayoutInstance().getOrderHint(this);
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取当前节点相对于父节点的布局变换
|
||||
*/
|
||||
getLayoutTransform: function () {
|
||||
return this._layoutTransform || new kity.Matrix();
|
||||
},
|
||||
|
||||
/**
|
||||
* 第一轮布局计算后,获得的全局布局位置
|
||||
*
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
getGlobalLayoutTransformPreview: function () {
|
||||
var pMatrix = this.parent ? this.parent.getLayoutTransform() : new kity.Matrix();
|
||||
var matrix = this.getLayoutTransform();
|
||||
var offset = this.getLayoutOffset();
|
||||
if (offset) {
|
||||
matrix = matrix.clone().translate(offset.x, offset.y);
|
||||
}
|
||||
return pMatrix.merge(matrix);
|
||||
},
|
||||
|
||||
getLayoutPointPreview: function () {
|
||||
return this.getGlobalLayoutTransformPreview().transformPoint(new kity.Point());
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取节点相对于全局的布局变换
|
||||
*/
|
||||
getGlobalLayoutTransform: function () {
|
||||
if (this._globalLayoutTransform) {
|
||||
return this._globalLayoutTransform;
|
||||
} else if (this.parent) {
|
||||
return this.parent.getGlobalLayoutTransform();
|
||||
} else {
|
||||
return new kity.Matrix();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 设置当前节点相对于父节点的布局变换
|
||||
*/
|
||||
setLayoutTransform: function (matrix) {
|
||||
this._layoutTransform = matrix;
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* 设置当前节点相对于全局的布局变换(冗余优化)
|
||||
*/
|
||||
setGlobalLayoutTransform: function (matrix) {
|
||||
this.getRenderContainer().setMatrix((this._globalLayoutTransform = matrix));
|
||||
return this;
|
||||
},
|
||||
|
||||
setVertexIn: function (p) {
|
||||
this._vertexIn = p;
|
||||
},
|
||||
|
||||
setVertexOut: function (p) {
|
||||
this._vertexOut = p;
|
||||
},
|
||||
|
||||
getVertexIn: function () {
|
||||
return this._vertexIn || new kity.Point();
|
||||
},
|
||||
|
||||
getVertexOut: function () {
|
||||
return this._vertexOut || new kity.Point();
|
||||
},
|
||||
|
||||
getLayoutVertexIn: function () {
|
||||
return this.getGlobalLayoutTransform().transformPoint(this.getVertexIn());
|
||||
},
|
||||
|
||||
getLayoutVertexOut: function () {
|
||||
return this.getGlobalLayoutTransform().transformPoint(this.getVertexOut());
|
||||
},
|
||||
|
||||
setLayoutVectorIn: function (v) {
|
||||
this._layoutVectorIn = v;
|
||||
return this;
|
||||
},
|
||||
|
||||
setLayoutVectorOut: function (v) {
|
||||
this._layoutVectorOut = v;
|
||||
return this;
|
||||
},
|
||||
|
||||
getLayoutVectorIn: function () {
|
||||
return this._layoutVectorIn || new kity.Vector();
|
||||
},
|
||||
|
||||
getLayoutVectorOut: function () {
|
||||
return this._layoutVectorOut || new kity.Vector();
|
||||
},
|
||||
|
||||
getLayoutBox: function () {
|
||||
var matrix = this.getGlobalLayoutTransform();
|
||||
return matrix.transformBox(this.getContentBox());
|
||||
},
|
||||
|
||||
getLayoutPoint: function () {
|
||||
var matrix = this.getGlobalLayoutTransform();
|
||||
return matrix.transformPoint(new kity.Point());
|
||||
},
|
||||
|
||||
getLayoutOffset: function () {
|
||||
if (!this.parent) return new kity.Point();
|
||||
|
||||
// 影响当前节点位置的是父节点的布局
|
||||
var data = this.getData('layout_' + this.parent.getLayout() + '_offset');
|
||||
|
||||
if (data) return new kity.Point(data.x, data.y);
|
||||
|
||||
return new kity.Point();
|
||||
},
|
||||
|
||||
setLayoutOffset: function (p) {
|
||||
if (!this.parent) return this;
|
||||
|
||||
this.setData(
|
||||
'layout_' + this.parent.getLayout() + '_offset',
|
||||
p
|
||||
? {
|
||||
x: p.x,
|
||||
y: p.y,
|
||||
}
|
||||
: undefined
|
||||
);
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
hasLayoutOffset: function () {
|
||||
return !!this.getData('layout_' + this.parent.getLayout() + '_offset');
|
||||
},
|
||||
|
||||
resetLayoutOffset: function () {
|
||||
return this.setLayoutOffset(null);
|
||||
},
|
||||
|
||||
getLayoutRoot: function () {
|
||||
if (this.isLayoutRoot()) {
|
||||
return this;
|
||||
}
|
||||
return this.parent.getLayoutRoot();
|
||||
},
|
||||
|
||||
isLayoutRoot: function () {
|
||||
return this.getData('layout') || this.isRoot();
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Minder 上的布局支持
|
||||
*/
|
||||
kity.extendClass(Minder, {
|
||||
layout: function () {
|
||||
var duration = this.getOption('layoutAnimationDuration');
|
||||
|
||||
this.getRoot().traverse(function (node) {
|
||||
// clear last results
|
||||
node.setLayoutTransform(null);
|
||||
});
|
||||
|
||||
function layoutNode(node, round) {
|
||||
// layout all children first
|
||||
// 剪枝:收起的节点无需计算
|
||||
if (node.isExpanded() || true) {
|
||||
node.children.forEach(function (child) {
|
||||
layoutNode(child, round);
|
||||
});
|
||||
}
|
||||
|
||||
var layout = node.getLayoutInstance();
|
||||
// var childrenInFlow = node.getChildren().filter(function(child) {
|
||||
// return !child.hasLayoutOffset();
|
||||
// });
|
||||
layout.doLayout(node, node.getChildren(), round);
|
||||
}
|
||||
|
||||
// 第一轮布局
|
||||
layoutNode(this.getRoot(), 1);
|
||||
|
||||
// 第二轮布局
|
||||
layoutNode(this.getRoot(), 2);
|
||||
|
||||
var minder = this;
|
||||
this.applyLayoutResult(this.getRoot(), duration, function () {
|
||||
/**
|
||||
* 当节点>200, 不使用动画时, 此处逻辑变为同步逻辑, 外部minder.on事件无法
|
||||
* 被提前录入, 因此增加setTimeout
|
||||
* @author Naixor
|
||||
*/
|
||||
setTimeout(function () {
|
||||
minder.fire('layoutallfinish');
|
||||
}, 0);
|
||||
});
|
||||
|
||||
return this.fire('layout');
|
||||
},
|
||||
|
||||
refresh: function () {
|
||||
this.getRoot().renderTree();
|
||||
this.layout().fire('contentchange')._interactChange();
|
||||
return this;
|
||||
},
|
||||
|
||||
applyLayoutResult: function (root, duration, callback) {
|
||||
root = root || this.getRoot();
|
||||
var me = this;
|
||||
|
||||
var complex = root.getComplex();
|
||||
|
||||
function consume() {
|
||||
if (!--complex) {
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 节点复杂度大于 100,关闭动画
|
||||
if (complex > 200) duration = 0;
|
||||
|
||||
function applyMatrix(node, matrix) {
|
||||
node.setGlobalLayoutTransform(matrix);
|
||||
|
||||
me.fire('layoutapply', {
|
||||
node: node,
|
||||
matrix: matrix,
|
||||
});
|
||||
}
|
||||
|
||||
function apply(node, pMatrix) {
|
||||
var matrix = node.getLayoutTransform().merge(pMatrix.clone());
|
||||
var lastMatrix = node.getGlobalLayoutTransform() || new kity.Matrix();
|
||||
|
||||
var offset = node.getLayoutOffset();
|
||||
matrix.translate(offset.x, offset.y);
|
||||
|
||||
matrix.m.e = Math.round(matrix.m.e);
|
||||
matrix.m.f = Math.round(matrix.m.f);
|
||||
|
||||
// 如果当前有动画,停止动画
|
||||
if (node._layoutTimeline) {
|
||||
node._layoutTimeline.stop();
|
||||
node._layoutTimeline = null;
|
||||
}
|
||||
|
||||
// 如果要求以动画形式来更新,创建动画
|
||||
if (duration) {
|
||||
node._layoutTimeline = new kity.Animator(lastMatrix, matrix, applyMatrix)
|
||||
.start(node, duration, 'ease')
|
||||
.on('finish', function () {
|
||||
//可能性能低的时候会丢帧,手动添加一帧
|
||||
setTimeout(function () {
|
||||
applyMatrix(node, matrix);
|
||||
me.fire('layoutfinish', {
|
||||
node: node,
|
||||
matrix: matrix,
|
||||
});
|
||||
consume();
|
||||
}, 150);
|
||||
});
|
||||
}
|
||||
|
||||
// 否则直接更新
|
||||
else {
|
||||
applyMatrix(node, matrix);
|
||||
me.fire('layoutfinish', {
|
||||
node: node,
|
||||
matrix: matrix,
|
||||
});
|
||||
consume();
|
||||
}
|
||||
|
||||
for (var i = 0; i < node.children.length; i++) {
|
||||
apply(node.children[i], matrix);
|
||||
}
|
||||
}
|
||||
apply(root, root.parent ? root.parent.getGlobalLayoutTransform() : new kity.Matrix());
|
||||
return this;
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = Layout;
|
||||
});
|
|
@ -0,0 +1,41 @@
|
|||
/* eslint-disable */
|
||||
/**
|
||||
* @fileOverview
|
||||
*
|
||||
* KityMinder 类,暴露在 window 上的唯一变量
|
||||
*
|
||||
* @author: techird
|
||||
* @copyright: Baidu FEX, 2014
|
||||
*/
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('./kity');
|
||||
var utils = require('./utils');
|
||||
|
||||
var _initHooks = [];
|
||||
|
||||
var Minder = kity.createClass('Minder', {
|
||||
constructor: function (options) {
|
||||
this._options = utils.extend({}, options);
|
||||
|
||||
var initHooks = _initHooks.slice();
|
||||
|
||||
var initHook;
|
||||
while (initHooks.length) {
|
||||
initHook = initHooks.shift();
|
||||
if (typeof initHook == 'function') {
|
||||
initHook.call(this, this._options);
|
||||
}
|
||||
}
|
||||
|
||||
this.fire('finishInitHook');
|
||||
},
|
||||
});
|
||||
|
||||
Minder.version = '1.4.43';
|
||||
|
||||
Minder.registerInitHook = function (hook) {
|
||||
_initHooks.push(hook);
|
||||
};
|
||||
|
||||
module.exports = Minder;
|
||||
});
|
|
@ -0,0 +1,150 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('./kity');
|
||||
var utils = require('./utils');
|
||||
var Minder = require('./minder');
|
||||
|
||||
/* 已注册的模块 */
|
||||
var _modules = {};
|
||||
|
||||
exports.register = function (name, module) {
|
||||
_modules[name] = module;
|
||||
};
|
||||
|
||||
/* 模块初始化 */
|
||||
Minder.registerInitHook(function () {
|
||||
this._initModules();
|
||||
});
|
||||
|
||||
// 模块声明周期维护
|
||||
kity.extendClass(Minder, {
|
||||
_initModules: function () {
|
||||
var modulesPool = _modules;
|
||||
var modulesToLoad = this._options.modules || utils.keys(modulesPool);
|
||||
|
||||
this._commands = {};
|
||||
this._query = {};
|
||||
this._modules = {};
|
||||
this._rendererClasses = {};
|
||||
|
||||
var i, name, type, module, moduleDeals, dealCommands, dealEvents, dealRenderers;
|
||||
|
||||
var me = this;
|
||||
for (i = 0; i < modulesToLoad.length; i++) {
|
||||
name = modulesToLoad[i];
|
||||
|
||||
if (!modulesPool[name]) continue;
|
||||
|
||||
// 执行模块初始化,抛出后续处理对象
|
||||
|
||||
if (typeof modulesPool[name] == 'function') {
|
||||
moduleDeals = modulesPool[name].call(me);
|
||||
} else {
|
||||
moduleDeals = modulesPool[name];
|
||||
}
|
||||
this._modules[name] = moduleDeals;
|
||||
|
||||
if (!moduleDeals) continue;
|
||||
|
||||
if (moduleDeals.defaultOptions) {
|
||||
me.setDefaultOptions(moduleDeals.defaultOptions);
|
||||
}
|
||||
|
||||
if (moduleDeals.init) {
|
||||
moduleDeals.init.call(me, this._options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Desc: 判断是否支持原生clipboard事件,如果支持,则对pager添加其监听
|
||||
* @Editor: Naixor
|
||||
* @Date: 2015.9.20
|
||||
*/
|
||||
/**
|
||||
* 由于当前脑图解构问题,clipboard暂时全权交由玩不托管
|
||||
* @Editor: Naixor
|
||||
* @Date: 2015.9.24
|
||||
*/
|
||||
// if (name === 'ClipboardModule' && this.supportClipboardEvent && !kity.Browser.gecko) {
|
||||
// var on = function () {
|
||||
// var clipBoardReceiver = this.clipBoardReceiver || document;
|
||||
|
||||
// if (document.addEventListener) {
|
||||
// clipBoardReceiver.addEventListener.apply(this, arguments);
|
||||
// } else {
|
||||
// arguments[0] = 'on' + arguments[0];
|
||||
// clipBoardReceiver.attachEvent.apply(this, arguments);
|
||||
// }
|
||||
// }
|
||||
// for (var command in moduleDeals.clipBoardEvents) {
|
||||
// on(command, moduleDeals.clipBoardEvents[command]);
|
||||
// }
|
||||
// };
|
||||
|
||||
// command加入命令池子
|
||||
dealCommands = moduleDeals.commands;
|
||||
for (name in dealCommands) {
|
||||
this._commands[name.toLowerCase()] = new dealCommands[name]();
|
||||
}
|
||||
|
||||
// 绑定事件
|
||||
dealEvents = moduleDeals.events;
|
||||
if (dealEvents) {
|
||||
for (type in dealEvents) {
|
||||
me.on(type, dealEvents[type]);
|
||||
}
|
||||
}
|
||||
|
||||
// 渲染器
|
||||
dealRenderers = moduleDeals.renderers;
|
||||
|
||||
if (dealRenderers) {
|
||||
for (type in dealRenderers) {
|
||||
this._rendererClasses[type] = this._rendererClasses[type] || [];
|
||||
|
||||
if (utils.isArray(dealRenderers[type])) {
|
||||
this._rendererClasses[type] = this._rendererClasses[type].concat(dealRenderers[type]);
|
||||
} else {
|
||||
this._rendererClasses[type].push(dealRenderers[type]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//添加模块的快捷键
|
||||
if (moduleDeals.commandShortcutKeys) {
|
||||
this.addCommandShortcutKeys(moduleDeals.commandShortcutKeys);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_garbage: function () {
|
||||
// this.clearSelect();
|
||||
|
||||
while (this._root.getChildren().length) {
|
||||
this._root.removeChild(0);
|
||||
}
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
var modules = this._modules;
|
||||
|
||||
this._resetEvents();
|
||||
this._garbage();
|
||||
|
||||
for (var key in modules) {
|
||||
if (!modules[key].destroy) continue;
|
||||
modules[key].destroy.call(this);
|
||||
}
|
||||
},
|
||||
|
||||
reset: function () {
|
||||
var modules = this._modules;
|
||||
|
||||
this._garbage();
|
||||
|
||||
for (var key in modules) {
|
||||
if (!modules[key].reset) continue;
|
||||
modules[key].reset.call(this);
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
|
@ -0,0 +1,404 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('./kity');
|
||||
var utils = require('./utils');
|
||||
var Minder = require('./minder');
|
||||
|
||||
/**
|
||||
* @class MinderNode
|
||||
*
|
||||
* 表示一个脑图节点
|
||||
*/
|
||||
var MinderNode = kity.createClass('MinderNode', {
|
||||
/**
|
||||
* 创建一个游离的脑图节点
|
||||
*
|
||||
* @param {String|Object} textOrData
|
||||
* 节点的初始数据或文本
|
||||
*/
|
||||
constructor: function (textOrData) {
|
||||
// 指针
|
||||
this.parent = null;
|
||||
this.root = this;
|
||||
this.children = [];
|
||||
|
||||
// 数据
|
||||
this.data = {
|
||||
id: utils.guid(),
|
||||
created: +new Date(),
|
||||
};
|
||||
|
||||
// 绘图容器
|
||||
this.initContainers();
|
||||
|
||||
if (utils.isString(textOrData)) {
|
||||
this.setText(textOrData);
|
||||
} else if (utils.isObject(textOrData)) {
|
||||
utils.extend(this.data, textOrData);
|
||||
}
|
||||
},
|
||||
|
||||
initContainers: function () {
|
||||
this.rc = new kity.Group().setId(utils.uuid('minder_node'));
|
||||
this.rc.minderNode = this;
|
||||
},
|
||||
|
||||
/**
|
||||
* 判断节点是否根节点
|
||||
*/
|
||||
isRoot: function () {
|
||||
return this.root === this;
|
||||
},
|
||||
|
||||
/**
|
||||
* 判断节点是否叶子
|
||||
*/
|
||||
isLeaf: function () {
|
||||
return this.children.length === 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取节点的根节点
|
||||
*/
|
||||
getRoot: function () {
|
||||
return this.root || this;
|
||||
},
|
||||
|
||||
/**
|
||||
* 获得节点的父节点
|
||||
*/
|
||||
getParent: function () {
|
||||
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;
|
||||
},
|
||||
|
||||
/**
|
||||
* 获得节点的深度
|
||||
*/
|
||||
getLevel: function () {
|
||||
var level = 0,
|
||||
ancestor = this.parent;
|
||||
while (ancestor) {
|
||||
level++;
|
||||
ancestor = ancestor.parent;
|
||||
}
|
||||
return level;
|
||||
},
|
||||
|
||||
/**
|
||||
* 获得节点的复杂度(即子树中节点的数量)
|
||||
*/
|
||||
getComplex: function () {
|
||||
var complex = 0;
|
||||
this.traverse(function () {
|
||||
complex++;
|
||||
});
|
||||
return complex;
|
||||
},
|
||||
|
||||
/**
|
||||
* 获得节点的类型(root|main|sub)
|
||||
*/
|
||||
getType: function (type) {
|
||||
this.type = ['root', 'main', 'sub'][Math.min(this.getLevel(), 2)];
|
||||
return this.type;
|
||||
},
|
||||
|
||||
/**
|
||||
* 判断当前节点是否被测试节点的祖先
|
||||
* @param {MinderNode} test 被测试的节点
|
||||
*/
|
||||
isAncestorOf: function (test) {
|
||||
var ancestor = test.parent;
|
||||
while (ancestor) {
|
||||
if (ancestor == this) return true;
|
||||
ancestor = ancestor.parent;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
getData: function (key) {
|
||||
return key ? this.data[key] : this.data;
|
||||
},
|
||||
|
||||
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;
|
||||
},
|
||||
|
||||
/**
|
||||
* 设置节点的文本数据
|
||||
* @param {String} text 文本数据
|
||||
*/
|
||||
setText: function (text) {
|
||||
return (this.data.text = text);
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取节点的文本数据
|
||||
* @return {String}
|
||||
*/
|
||||
getText: function () {
|
||||
return this.data.text || null;
|
||||
},
|
||||
|
||||
/**
|
||||
* 先序遍历当前节点树
|
||||
* @param {Function} fn 遍历函数
|
||||
*/
|
||||
preTraverse: function (fn, excludeThis) {
|
||||
var children = this.getChildren();
|
||||
if (!excludeThis) fn(this);
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
children[i].preTraverse(fn);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 后序遍历当前节点树
|
||||
* @param {Function} fn 遍历函数
|
||||
*/
|
||||
postTraverse: function (fn, excludeThis) {
|
||||
var children = this.getChildren();
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
children[i].postTraverse(fn);
|
||||
}
|
||||
if (!excludeThis) fn(this);
|
||||
},
|
||||
|
||||
traverse: function (fn, excludeThis) {
|
||||
return this.postTraverse(fn, excludeThis);
|
||||
},
|
||||
|
||||
getChildren: function () {
|
||||
return this.children;
|
||||
},
|
||||
|
||||
getIndex: function () {
|
||||
return this.parent ? this.parent.children.indexOf(this) : -1;
|
||||
},
|
||||
|
||||
insertChild: function (node, index) {
|
||||
if (index === undefined) {
|
||||
index = this.children.length;
|
||||
}
|
||||
if (node.parent) {
|
||||
node.parent.removeChild(node);
|
||||
}
|
||||
node.parent = this;
|
||||
node.root = this.root;
|
||||
|
||||
this.children.splice(index, 0, node);
|
||||
},
|
||||
|
||||
appendChild: function (node) {
|
||||
return this.insertChild(node);
|
||||
},
|
||||
|
||||
prependChild: function (node) {
|
||||
return this.insertChild(node, 0);
|
||||
},
|
||||
|
||||
removeChild: function (elem) {
|
||||
var index = elem,
|
||||
removed;
|
||||
if (elem instanceof MinderNode) {
|
||||
index = this.children.indexOf(elem);
|
||||
}
|
||||
if (index >= 0) {
|
||||
removed = this.children.splice(index, 1)[0];
|
||||
removed.parent = null;
|
||||
removed.root = removed;
|
||||
}
|
||||
},
|
||||
|
||||
clearChildren: function () {
|
||||
this.children = [];
|
||||
},
|
||||
|
||||
getChild: function (index) {
|
||||
return this.children[index];
|
||||
},
|
||||
|
||||
getRenderContainer: function () {
|
||||
return this.rc;
|
||||
},
|
||||
|
||||
getCommonAncestor: function (node) {
|
||||
return MinderNode.getCommonAncestor(this, node);
|
||||
},
|
||||
|
||||
contains: function (node) {
|
||||
return this == node || this.isAncestorOf(node);
|
||||
},
|
||||
|
||||
clone: function () {
|
||||
var cloned = new MinderNode();
|
||||
|
||||
cloned.data = utils.clone(this.data);
|
||||
|
||||
this.children.forEach(function (child) {
|
||||
cloned.appendChild(child.clone());
|
||||
});
|
||||
|
||||
return cloned;
|
||||
},
|
||||
|
||||
compareTo: function (node) {
|
||||
if (!utils.comparePlainObject(this.data, node.data)) return false;
|
||||
if (!utils.comparePlainObject(this.temp, node.temp)) return false;
|
||||
if (this.children.length != node.children.length) return false;
|
||||
|
||||
var i = 0;
|
||||
while (this.children[i]) {
|
||||
if (!this.children[i].compareTo(node.children[i])) return false;
|
||||
i++;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
getMinder: function () {
|
||||
return this.getRoot().minder;
|
||||
},
|
||||
});
|
||||
|
||||
MinderNode.getCommonAncestor = function (nodeA, nodeB) {
|
||||
if (nodeA instanceof Array) {
|
||||
return MinderNode.getCommonAncestor.apply(this, nodeA);
|
||||
}
|
||||
switch (arguments.length) {
|
||||
case 1:
|
||||
return nodeA.parent || nodeA;
|
||||
|
||||
case 2:
|
||||
if (nodeA.isAncestorOf(nodeB)) {
|
||||
return nodeA;
|
||||
}
|
||||
if (nodeB.isAncestorOf(nodeA)) {
|
||||
return nodeB;
|
||||
}
|
||||
var ancestor = nodeA.parent;
|
||||
while (ancestor && !ancestor.isAncestorOf(nodeB)) {
|
||||
ancestor = ancestor.parent;
|
||||
}
|
||||
return ancestor;
|
||||
|
||||
default:
|
||||
return Array.prototype.reduce.call(
|
||||
arguments,
|
||||
function (prev, current) {
|
||||
return MinderNode.getCommonAncestor(prev, current);
|
||||
},
|
||||
nodeA
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
kity.extendClass(Minder, {
|
||||
getRoot: function () {
|
||||
return this._root;
|
||||
},
|
||||
|
||||
setRoot: function (root) {
|
||||
this._root = root;
|
||||
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', {
|
||||
node: node,
|
||||
parent: parent,
|
||||
index: index,
|
||||
});
|
||||
this.appendNode(node, parent, index);
|
||||
return node;
|
||||
},
|
||||
|
||||
appendNode: function (node, parent, index) {
|
||||
if (parent) parent.insertChild(node, index);
|
||||
this.attachNode(node);
|
||||
return this;
|
||||
},
|
||||
|
||||
removeNode: function (node) {
|
||||
if (node.parent) {
|
||||
node.parent.removeChild(node);
|
||||
this.detachNode(node);
|
||||
this.fire('noderemove', {
|
||||
node: node,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
attachNode: function (node) {
|
||||
var rc = this.getRenderContainer();
|
||||
node.traverse(function (current) {
|
||||
current.attached = true;
|
||||
rc.addShape(current.getRenderContainer());
|
||||
});
|
||||
rc.addShape(node.getRenderContainer());
|
||||
this.fire('nodeattach', {
|
||||
node: node,
|
||||
});
|
||||
},
|
||||
|
||||
detachNode: function (node) {
|
||||
var rc = this.getRenderContainer();
|
||||
node.traverse(function (current) {
|
||||
current.attached = false;
|
||||
rc.removeShape(current.getRenderContainer());
|
||||
});
|
||||
this.fire('nodedetach', {
|
||||
node: node,
|
||||
});
|
||||
},
|
||||
|
||||
getMinderTitle: function () {
|
||||
return this.getRoot().getText();
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = MinderNode;
|
||||
});
|
|
@ -0,0 +1,35 @@
|
|||
/* eslint-disable */
|
||||
/**
|
||||
* @fileOverview
|
||||
*
|
||||
* 提供脑图选项支持
|
||||
*
|
||||
* @author: techird
|
||||
* @copyright: Baidu FEX, 2014
|
||||
*/
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('./kity');
|
||||
var utils = require('./utils');
|
||||
var Minder = require('./minder');
|
||||
|
||||
Minder.registerInitHook(function (options) {
|
||||
this._defaultOptions = {};
|
||||
});
|
||||
|
||||
kity.extendClass(Minder, {
|
||||
setDefaultOptions: function (options) {
|
||||
utils.extend(this._defaultOptions, options);
|
||||
return this;
|
||||
},
|
||||
getOption: function (key) {
|
||||
if (key) {
|
||||
return key in this._options ? this._options[key] : this._defaultOptions[key];
|
||||
} else {
|
||||
return utils.extend({}, this._defaultOptions, this._options);
|
||||
}
|
||||
},
|
||||
setOption: function (key, value) {
|
||||
this._options[key] = value;
|
||||
},
|
||||
});
|
||||
});
|
|
@ -0,0 +1,75 @@
|
|||
/* eslint-disable */
|
||||
/**
|
||||
* @fileOverview
|
||||
*
|
||||
* 初始化渲染容器
|
||||
*
|
||||
* @author: techird
|
||||
* @copyright: Baidu FEX, 2014
|
||||
*/
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('./kity');
|
||||
var utils = require('./utils');
|
||||
var Minder = require('./minder');
|
||||
|
||||
Minder.registerInitHook(function () {
|
||||
this._initPaper();
|
||||
});
|
||||
|
||||
kity.extendClass(Minder, {
|
||||
_initPaper: function () {
|
||||
this._paper = new kity.Paper();
|
||||
this._paper._minder = this;
|
||||
this._paper.getNode().ondragstart = function (e) {
|
||||
e.preventDefault();
|
||||
};
|
||||
this._paper.shapeNode.setAttribute('transform', 'translate(0.5, 0.5)');
|
||||
|
||||
this._addRenderContainer();
|
||||
|
||||
this.setRoot(this.createNode());
|
||||
|
||||
if (this._options.renderTo) {
|
||||
this.renderTo(this._options.renderTo);
|
||||
}
|
||||
},
|
||||
|
||||
_addRenderContainer: function () {
|
||||
this._rc = new kity.Group().setId(utils.uuid('minder'));
|
||||
this._paper.addShape(this._rc);
|
||||
},
|
||||
|
||||
renderTo: function (target) {
|
||||
if (typeof target == 'string') {
|
||||
target = document.querySelector(target);
|
||||
}
|
||||
if (target) {
|
||||
if (target.tagName.toLowerCase() == 'script') {
|
||||
var newTarget = document.createElement('div');
|
||||
newTarget.id = target.id;
|
||||
newTarget.class = target.class;
|
||||
target.parentNode.insertBefore(newTarget, target);
|
||||
target.parentNode.removeChild(target);
|
||||
target = newTarget;
|
||||
}
|
||||
target.classList.add('km-view');
|
||||
this._paper.renderTo((this._renderTarget = target));
|
||||
this._bindEvents();
|
||||
this.fire('paperrender');
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
getRenderContainer: function () {
|
||||
return this._rc;
|
||||
},
|
||||
|
||||
getPaper: function () {
|
||||
return this._paper;
|
||||
},
|
||||
|
||||
getRenderTarget: function () {
|
||||
return this._renderTarget;
|
||||
},
|
||||
});
|
||||
});
|
|
@ -0,0 +1,109 @@
|
|||
/* eslint-disable */
|
||||
/**
|
||||
* @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;
|
||||
},
|
||||
});
|
||||
});
|
|
@ -0,0 +1,207 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
/*!
|
||||
** Thenable -- Embeddable Minimum Strictly-Compliant Promises/A+ 1.1.1 Thenable
|
||||
** Copyright (c) 2013-2014 Ralf S. Engelschall <http://engelschall.com>
|
||||
** Licensed under The MIT License <http://opensource.org/licenses/MIT>
|
||||
** Source-Code distributed on <http://github.com/rse/thenable>
|
||||
*/
|
||||
|
||||
/* promise states [Promises/A+ 2.1] */
|
||||
var STATE_PENDING = 0; /* [Promises/A+ 2.1.1] */
|
||||
var STATE_FULFILLED = 1; /* [Promises/A+ 2.1.2] */
|
||||
var STATE_REJECTED = 2; /* [Promises/A+ 2.1.3] */
|
||||
|
||||
/* promise object constructor */
|
||||
var Promise = function (executor) {
|
||||
/* optionally support non-constructor/plain-function call */
|
||||
if (!(this instanceof Promise)) return new Promise(executor);
|
||||
|
||||
/* initialize object */
|
||||
this.id = 'Thenable/1.0.7';
|
||||
this.state = STATE_PENDING; /* initial state */
|
||||
this.fulfillValue = undefined; /* initial value */ /* [Promises/A+ 1.3, 2.1.2.2] */
|
||||
this.rejectReason = undefined; /* initial reason */ /* [Promises/A+ 1.5, 2.1.3.2] */
|
||||
this.onFulfilled = []; /* initial handlers */
|
||||
this.onRejected = []; /* initial handlers */
|
||||
|
||||
/* support optional executor function */
|
||||
if (typeof executor === 'function') executor.call(this, this.fulfill.bind(this), this.reject.bind(this));
|
||||
};
|
||||
|
||||
/* Promise API methods */
|
||||
Promise.prototype = {
|
||||
/* promise resolving methods */
|
||||
fulfill: function (value) {
|
||||
return deliver(this, STATE_FULFILLED, 'fulfillValue', value);
|
||||
},
|
||||
reject: function (value) {
|
||||
return deliver(this, STATE_REJECTED, 'rejectReason', value);
|
||||
},
|
||||
|
||||
/* 'The then Method' [Promises/A+ 1.1, 1.2, 2.2] */
|
||||
then: function (onFulfilled, onRejected) {
|
||||
var curr = this;
|
||||
var next = new Promise(); /* [Promises/A+ 2.2.7] */
|
||||
curr.onFulfilled.push(resolver(onFulfilled, next, 'fulfill')); /* [Promises/A+ 2.2.2/2.2.6] */
|
||||
curr.onRejected.push(resolver(onRejected, next, 'reject')); /* [Promises/A+ 2.2.3/2.2.6] */
|
||||
execute(curr);
|
||||
return next; /* [Promises/A+ 2.2.7, 3.3] */
|
||||
},
|
||||
};
|
||||
|
||||
Promise.all = function (arr) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
var len = arr.length,
|
||||
i = 0,
|
||||
res = 0,
|
||||
results = [];
|
||||
|
||||
if (len === 0) {
|
||||
resolve(results);
|
||||
}
|
||||
|
||||
while (i < len) {
|
||||
arr[i].then(
|
||||
function (result) {
|
||||
results.push(result);
|
||||
if (++res === len) {
|
||||
resolve(results);
|
||||
}
|
||||
},
|
||||
function (val) {
|
||||
reject(val);
|
||||
}
|
||||
);
|
||||
i++;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/* deliver an action */
|
||||
var deliver = function (curr, state, name, value) {
|
||||
if (curr.state === STATE_PENDING) {
|
||||
curr.state = state; /* [Promises/A+ 2.1.2.1, 2.1.3.1] */
|
||||
curr[name] = value; /* [Promises/A+ 2.1.2.2, 2.1.3.2] */
|
||||
execute(curr);
|
||||
}
|
||||
return curr;
|
||||
};
|
||||
|
||||
/* execute all handlers */
|
||||
var execute = function (curr) {
|
||||
if (curr.state === STATE_FULFILLED) execute_handlers(curr, 'onFulfilled', curr.fulfillValue);
|
||||
else if (curr.state === STATE_REJECTED) execute_handlers(curr, 'onRejected', curr.rejectReason);
|
||||
};
|
||||
|
||||
/* execute particular set of handlers */
|
||||
var execute_handlers = function (curr, name, value) {
|
||||
/* global process: true */
|
||||
/* global setImmediate: true */
|
||||
/* global setTimeout: true */
|
||||
|
||||
/* short-circuit processing */
|
||||
if (curr[name].length === 0) return;
|
||||
|
||||
/* iterate over all handlers, exactly once */
|
||||
var handlers = curr[name];
|
||||
curr[name] = []; /* [Promises/A+ 2.2.2.3, 2.2.3.3] */
|
||||
var func = function () {
|
||||
for (var i = 0; i < handlers.length; i++) handlers[i](value); /* [Promises/A+ 2.2.5] */
|
||||
};
|
||||
|
||||
/* execute procedure asynchronously */ /* [Promises/A+ 2.2.4, 3.1] */
|
||||
if (typeof process === 'object' && typeof process.nextTick === 'function') process.nextTick(func);
|
||||
else if (typeof setImmediate === 'function') setImmediate(func);
|
||||
else setTimeout(func, 0);
|
||||
};
|
||||
|
||||
/* generate a resolver function */
|
||||
var resolver = function (cb, next, method) {
|
||||
return function (value) {
|
||||
if (typeof cb !== 'function') /* [Promises/A+ 2.2.1, 2.2.7.3, 2.2.7.4] */ next[method].call(next, value);
|
||||
/* [Promises/A+ 2.2.7.3, 2.2.7.4] */ else {
|
||||
var result;
|
||||
try {
|
||||
if (value instanceof Promise) {
|
||||
result = value.then(cb);
|
||||
} else result = cb(value);
|
||||
} catch (e) {
|
||||
/* [Promises/A+ 2.2.2.1, 2.2.3.1, 2.2.5, 3.2] */
|
||||
next.reject(e); /* [Promises/A+ 2.2.7.2] */
|
||||
return;
|
||||
}
|
||||
resolve(next, result); /* [Promises/A+ 2.2.7.1] */
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/* 'Promise Resolution Procedure' */ /* [Promises/A+ 2.3] */
|
||||
var resolve = function (promise, x) {
|
||||
/* sanity check arguments */ /* [Promises/A+ 2.3.1] */
|
||||
if (promise === x) {
|
||||
promise.reject(new TypeError('cannot resolve promise with itself'));
|
||||
return;
|
||||
}
|
||||
|
||||
/* surgically check for a 'then' method
|
||||
(mainly to just call the 'getter' of 'then' only once) */
|
||||
var then;
|
||||
if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
|
||||
try {
|
||||
then = x.then;
|
||||
} catch (e) {
|
||||
/* [Promises/A+ 2.3.3.1, 3.5] */
|
||||
promise.reject(e); /* [Promises/A+ 2.3.3.2] */
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* handle own Thenables [Promises/A+ 2.3.2]
|
||||
and similar 'thenables' [Promises/A+ 2.3.3] */
|
||||
if (typeof then === 'function') {
|
||||
var resolved = false;
|
||||
try {
|
||||
/* call retrieved 'then' method */ /* [Promises/A+ 2.3.3.3] */
|
||||
then.call(
|
||||
x,
|
||||
/* resolvePromise */ /* [Promises/A+ 2.3.3.3.1] */
|
||||
function (y) {
|
||||
if (resolved) return;
|
||||
resolved = true; /* [Promises/A+ 2.3.3.3.3] */
|
||||
if (y === x) /* [Promises/A+ 3.6] */ promise.reject(new TypeError('circular thenable chain'));
|
||||
else resolve(promise, y);
|
||||
},
|
||||
|
||||
/* rejectPromise */ /* [Promises/A+ 2.3.3.3.2] */
|
||||
function (r) {
|
||||
if (resolved) return;
|
||||
resolved = true; /* [Promises/A+ 2.3.3.3.3] */
|
||||
promise.reject(r);
|
||||
}
|
||||
);
|
||||
} catch (e) {
|
||||
if (!resolved) /* [Promises/A+ 2.3.3.3.3] */ promise.reject(e); /* [Promises/A+ 2.3.3.3.4] */
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* handle other values */
|
||||
promise.fulfill(x); /* [Promises/A+ 2.3.4, 2.3.3.4] */
|
||||
};
|
||||
|
||||
Promise.resolve = function (value) {
|
||||
return new Promise(function (resolve) {
|
||||
resolve(value);
|
||||
});
|
||||
};
|
||||
|
||||
Promise.reject = function (reason) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
reject(reason);
|
||||
});
|
||||
};
|
||||
|
||||
/* export API */
|
||||
module.exports = Promise;
|
||||
});
|
|
@ -0,0 +1,63 @@
|
|||
/* eslint-disable */
|
||||
/**
|
||||
* @fileOverview
|
||||
*
|
||||
* 只读模式支持
|
||||
*
|
||||
* @author: techird
|
||||
* @copyright: Baidu FEX, 2014
|
||||
*/
|
||||
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('./kity');
|
||||
var Minder = require('./minder');
|
||||
var MinderEvent = require('./event');
|
||||
|
||||
Minder.registerInitHook(function (options) {
|
||||
if (options.readOnly) {
|
||||
this.setDisabled();
|
||||
}
|
||||
});
|
||||
|
||||
kity.extendClass(Minder, {
|
||||
disable: function () {
|
||||
var me = this;
|
||||
//禁用命令
|
||||
me.bkqueryCommandState = me.queryCommandState;
|
||||
me.bkqueryCommandValue = me.queryCommandValue;
|
||||
me.queryCommandState = function (type) {
|
||||
var cmd = this._getCommand(type);
|
||||
if (cmd && cmd.enableReadOnly) {
|
||||
return me.bkqueryCommandState.apply(me, arguments);
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
me.queryCommandValue = function (type) {
|
||||
var cmd = this._getCommand(type);
|
||||
if (cmd && cmd.enableReadOnly) {
|
||||
return me.bkqueryCommandValue.apply(me, arguments);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
this.setStatus('readonly');
|
||||
me._interactChange();
|
||||
},
|
||||
|
||||
enable: function () {
|
||||
var me = this;
|
||||
|
||||
if (me.bkqueryCommandState) {
|
||||
me.queryCommandState = me.bkqueryCommandState;
|
||||
delete me.bkqueryCommandState;
|
||||
}
|
||||
if (me.bkqueryCommandValue) {
|
||||
me.queryCommandValue = me.bkqueryCommandValue;
|
||||
delete me.bkqueryCommandValue;
|
||||
}
|
||||
|
||||
this.setStatus('normal');
|
||||
|
||||
me._interactChange();
|
||||
},
|
||||
});
|
||||
});
|
|
@ -0,0 +1,255 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('./kity');
|
||||
var Minder = require('./minder');
|
||||
var MinderNode = require('./node');
|
||||
|
||||
var Renderer = kity.createClass('Renderer', {
|
||||
constructor: function (node) {
|
||||
this.node = node;
|
||||
},
|
||||
|
||||
create: function (node) {
|
||||
throw new Error('Not implement: Renderer.create()');
|
||||
},
|
||||
|
||||
shouldRender: function (node) {
|
||||
return true;
|
||||
},
|
||||
|
||||
watchChange: function (data) {
|
||||
var changed;
|
||||
|
||||
if (this.watchingData === undefined) {
|
||||
changed = true;
|
||||
} else if (this.watchingData != data) {
|
||||
changed = true;
|
||||
} else {
|
||||
changed = false;
|
||||
}
|
||||
|
||||
this.watchingData = data;
|
||||
},
|
||||
|
||||
shouldDraw: function (node) {
|
||||
return true;
|
||||
},
|
||||
|
||||
update: function (shape, node, box) {
|
||||
if (this.shouldDraw()) this.draw(shape, node);
|
||||
return this.place(shape, node, box);
|
||||
},
|
||||
|
||||
draw: function (shape, node) {
|
||||
throw new Error('Not implement: Renderer.draw()');
|
||||
},
|
||||
|
||||
place: function (shape, node, box) {
|
||||
throw new Error('Not implement: Renderer.place()');
|
||||
},
|
||||
|
||||
getRenderShape: function () {
|
||||
return this._renderShape || null;
|
||||
},
|
||||
|
||||
setRenderShape: function (shape) {
|
||||
this._renderShape = shape;
|
||||
},
|
||||
});
|
||||
|
||||
function createMinderExtension() {
|
||||
function createRendererForNode(node, registered) {
|
||||
var renderers = [];
|
||||
|
||||
['center', 'left', 'right', 'top', 'bottom', 'outline', 'outside'].forEach(function (section) {
|
||||
var before = 'before' + section;
|
||||
var after = 'after' + section;
|
||||
|
||||
if (registered[before]) {
|
||||
renderers = renderers.concat(registered[before]);
|
||||
}
|
||||
if (registered[section]) {
|
||||
renderers = renderers.concat(registered[section]);
|
||||
}
|
||||
if (registered[after]) {
|
||||
renderers = renderers.concat(registered[after]);
|
||||
}
|
||||
});
|
||||
|
||||
node._renderers = renderers.map(function (Renderer) {
|
||||
return new Renderer(node);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
renderNodeBatch: function (nodes) {
|
||||
var rendererClasses = this._rendererClasses;
|
||||
var lastBoxes = [];
|
||||
var rendererCount = 0;
|
||||
var i, j, renderer, node;
|
||||
|
||||
if (!nodes.length) return;
|
||||
|
||||
for (j = 0; j < nodes.length; j++) {
|
||||
node = nodes[j];
|
||||
if (!node._renderers) {
|
||||
createRendererForNode(node, rendererClasses);
|
||||
}
|
||||
node._contentBox = new kity.Box();
|
||||
this.fire('beforerender', {
|
||||
node: node,
|
||||
});
|
||||
}
|
||||
|
||||
// 所有节点渲染器数量是一致的
|
||||
rendererCount = nodes[0]._renderers.length;
|
||||
|
||||
for (i = 0; i < rendererCount; i++) {
|
||||
// 获取延迟盒子数据
|
||||
for (j = 0; j < nodes.length; j++) {
|
||||
if (typeof lastBoxes[j] == 'function') {
|
||||
lastBoxes[j] = lastBoxes[j]();
|
||||
}
|
||||
if (!(lastBoxes[j] instanceof kity.Box)) {
|
||||
lastBoxes[j] = new kity.Box(lastBoxes[j]);
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < nodes.length; j++) {
|
||||
node = nodes[j];
|
||||
renderer = node._renderers[i];
|
||||
|
||||
// 合并盒子
|
||||
if (lastBoxes[j]) {
|
||||
node._contentBox = node._contentBox.merge(lastBoxes[j]);
|
||||
renderer.contentBox = lastBoxes[j];
|
||||
}
|
||||
|
||||
// 判断当前上下文是否应该渲染
|
||||
if (renderer.shouldRender(node)) {
|
||||
// 应该渲染,但是渲染图形没创建过,需要创建
|
||||
if (!renderer.getRenderShape()) {
|
||||
renderer.setRenderShape(renderer.create(node));
|
||||
if (renderer.bringToBack) {
|
||||
node.getRenderContainer().prependShape(renderer.getRenderShape());
|
||||
} else {
|
||||
node.getRenderContainer().appendShape(renderer.getRenderShape());
|
||||
}
|
||||
}
|
||||
|
||||
// 强制让渲染图形显示
|
||||
renderer.getRenderShape().setVisible(true);
|
||||
|
||||
// 更新渲染图形
|
||||
lastBoxes[j] = renderer.update(renderer.getRenderShape(), node, node._contentBox);
|
||||
}
|
||||
|
||||
// 如果不应该渲染,但是渲染图形创建过了,需要隐藏起来
|
||||
else if (renderer.getRenderShape()) {
|
||||
renderer.getRenderShape().setVisible(false);
|
||||
lastBoxes[j] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < nodes.length; j++) {
|
||||
this.fire('noderender', {
|
||||
node: nodes[j],
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
renderNode: function (node) {
|
||||
var rendererClasses = this._rendererClasses;
|
||||
var i, latestBox, renderer;
|
||||
|
||||
if (!node._renderers) {
|
||||
createRendererForNode(node, rendererClasses);
|
||||
}
|
||||
|
||||
this.fire('beforerender', {
|
||||
node: node,
|
||||
});
|
||||
|
||||
node._contentBox = new kity.Box();
|
||||
|
||||
node._renderers.forEach(function (renderer) {
|
||||
// 判断当前上下文是否应该渲染
|
||||
if (renderer.shouldRender(node)) {
|
||||
// 应该渲染,但是渲染图形没创建过,需要创建
|
||||
if (!renderer.getRenderShape()) {
|
||||
renderer.setRenderShape(renderer.create(node));
|
||||
if (renderer.bringToBack) {
|
||||
node.getRenderContainer().prependShape(renderer.getRenderShape());
|
||||
} else {
|
||||
node.getRenderContainer().appendShape(renderer.getRenderShape());
|
||||
}
|
||||
}
|
||||
|
||||
// 强制让渲染图形显示
|
||||
renderer.getRenderShape().setVisible(true);
|
||||
|
||||
// 更新渲染图形
|
||||
latestBox = renderer.update(renderer.getRenderShape(), node, node._contentBox);
|
||||
|
||||
if (typeof latestBox == 'function') latestBox = latestBox();
|
||||
|
||||
// 合并渲染区域
|
||||
if (latestBox) {
|
||||
node._contentBox = node._contentBox.merge(latestBox);
|
||||
renderer.contentBox = latestBox;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果不应该渲染,但是渲染图形创建过了,需要隐藏起来
|
||||
else if (renderer.getRenderShape()) {
|
||||
renderer.getRenderShape().setVisible(false);
|
||||
}
|
||||
});
|
||||
|
||||
this.fire('noderender', {
|
||||
node: node,
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
kity.extendClass(Minder, createMinderExtension());
|
||||
|
||||
kity.extendClass(MinderNode, {
|
||||
render: function () {
|
||||
if (!this.attached) return;
|
||||
this.getMinder().renderNode(this);
|
||||
return this;
|
||||
},
|
||||
renderTree: function () {
|
||||
if (!this.attached) return;
|
||||
var list = [];
|
||||
this.traverse(function (node) {
|
||||
list.push(node);
|
||||
});
|
||||
this.getMinder().renderNodeBatch(list);
|
||||
return this;
|
||||
},
|
||||
getRenderer: function (type) {
|
||||
var rs = this._renderers;
|
||||
if (!rs) return null;
|
||||
for (var i = 0; i < rs.length; i++) {
|
||||
if (rs[i].getType() == type) return rs[i];
|
||||
}
|
||||
return null;
|
||||
},
|
||||
getContentBox: function () {
|
||||
//if (!this._contentBox) this.render();
|
||||
return this.parent && this.parent.isCollapsed() ? new kity.Box() : this._contentBox || new kity.Box();
|
||||
},
|
||||
getRenderBox: function (rendererType, refer) {
|
||||
var renderer = rendererType && this.getRenderer(rendererType);
|
||||
var contentBox = renderer ? renderer.contentBox : this.getContentBox();
|
||||
var ctm = kity.Matrix.getCTM(this.getRenderContainer(), refer || 'paper');
|
||||
return ctm.transformBox(contentBox);
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = Renderer;
|
||||
});
|
|
@ -0,0 +1,146 @@
|
|||
/* eslint-disable */
|
||||
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('./kity');
|
||||
var utils = require('./utils');
|
||||
var Minder = require('./minder');
|
||||
var MinderNode = require('./node');
|
||||
|
||||
Minder.registerInitHook(function () {
|
||||
this._initSelection();
|
||||
});
|
||||
|
||||
// 选区管理
|
||||
kity.extendClass(Minder, {
|
||||
_initSelection: function () {
|
||||
this._selectedNodes = [];
|
||||
},
|
||||
renderChangedSelection: function (last) {
|
||||
var current = this.getSelectedNodes();
|
||||
var changed = [];
|
||||
|
||||
current.forEach(function (node) {
|
||||
if (last.indexOf(node) == -1) {
|
||||
changed.push(node);
|
||||
}
|
||||
});
|
||||
|
||||
last.forEach(function (node) {
|
||||
if (current.indexOf(node) == -1) {
|
||||
changed.push(node);
|
||||
}
|
||||
});
|
||||
|
||||
if (changed.length) {
|
||||
this._interactChange();
|
||||
this.fire('selectionchange');
|
||||
}
|
||||
while (changed.length) {
|
||||
changed.shift().render();
|
||||
}
|
||||
},
|
||||
getSelectedNodes: function () {
|
||||
//不能克隆返回,会对当前选区操作,从而影响querycommand
|
||||
return this._selectedNodes;
|
||||
},
|
||||
getSelectedNode: function () {
|
||||
return this.getSelectedNodes()[0] || null;
|
||||
},
|
||||
removeAllSelectedNodes: function () {
|
||||
var me = this;
|
||||
var last = this._selectedNodes.splice(0);
|
||||
this._selectedNodes = [];
|
||||
this.renderChangedSelection(last);
|
||||
return this.fire('selectionclear');
|
||||
},
|
||||
removeSelectedNodes: function (nodes) {
|
||||
var me = this;
|
||||
var last = this._selectedNodes.slice(0);
|
||||
nodes = utils.isArray(nodes) ? nodes : [nodes];
|
||||
|
||||
nodes.forEach(function (node) {
|
||||
var index;
|
||||
if ((index = me._selectedNodes.indexOf(node)) === -1) return;
|
||||
me._selectedNodes.splice(index, 1);
|
||||
});
|
||||
|
||||
this.renderChangedSelection(last);
|
||||
return this;
|
||||
},
|
||||
select: function (nodes, isSingleSelect) {
|
||||
var lastSelect = this.getSelectedNodes().slice(0);
|
||||
if (isSingleSelect) {
|
||||
this._selectedNodes = [];
|
||||
}
|
||||
var me = this;
|
||||
nodes = utils.isArray(nodes) ? nodes : [nodes];
|
||||
nodes.forEach(function (node) {
|
||||
if (me._selectedNodes.indexOf(node) !== -1) return;
|
||||
me._selectedNodes.unshift(node);
|
||||
});
|
||||
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) {
|
||||
if (utils.isArray(node)) {
|
||||
node.forEach(this.toggleSelect.bind(this));
|
||||
} else {
|
||||
if (node.isSelected()) this.removeSelectedNodes(node);
|
||||
else this.select(node);
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
isSingleSelect: function () {
|
||||
return this._selectedNodes.length == 1;
|
||||
},
|
||||
|
||||
getSelectedAncestors: function (includeRoot) {
|
||||
var nodes = this.getSelectedNodes().slice(0),
|
||||
ancestors = [],
|
||||
judge;
|
||||
|
||||
// 根节点不参与计算
|
||||
var rootIndex = nodes.indexOf(this.getRoot());
|
||||
if (~rootIndex && !includeRoot) {
|
||||
nodes.splice(rootIndex, 1);
|
||||
}
|
||||
|
||||
// 判断 nodes 列表中是否存在 judge 的祖先
|
||||
function hasAncestor(nodes, judge) {
|
||||
for (var i = nodes.length - 1; i >= 0; --i) {
|
||||
if (nodes[i].isAncestorOf(judge)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 按照拓扑排序
|
||||
nodes.sort(function (node1, node2) {
|
||||
return node1.getLevel() - node2.getLevel();
|
||||
});
|
||||
|
||||
// 因为是拓扑有序的,所以只需往上查找
|
||||
while ((judge = nodes.pop())) {
|
||||
if (!hasAncestor(nodes, judge)) {
|
||||
ancestors.push(judge);
|
||||
}
|
||||
}
|
||||
|
||||
return ancestors;
|
||||
},
|
||||
});
|
||||
|
||||
kity.extendClass(MinderNode, {
|
||||
isSelected: function () {
|
||||
var minder = this.getMinder();
|
||||
return minder && minder.getSelectedNodes().indexOf(this) != -1;
|
||||
},
|
||||
});
|
||||
});
|
|
@ -0,0 +1,156 @@
|
|||
/* eslint-disable */
|
||||
/**
|
||||
* @fileOverview
|
||||
*
|
||||
* 添加快捷键支持
|
||||
*
|
||||
* @author: techird
|
||||
* @copyright: Baidu FEX, 2014
|
||||
*/
|
||||
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('./kity');
|
||||
var utils = require('./utils');
|
||||
var keymap = require('./keymap');
|
||||
var Minder = require('./minder');
|
||||
var MinderEvent = require('./event');
|
||||
|
||||
/**
|
||||
* 计算包含 meta 键的 keycode
|
||||
*
|
||||
* @param {String|KeyEvent} unknown
|
||||
*/
|
||||
function getMetaKeyCode(unknown) {
|
||||
var CTRL_MASK = 0x1000;
|
||||
var ALT_MASK = 0x2000;
|
||||
var SHIFT_MASK = 0x4000;
|
||||
var metaKeyCode = 0;
|
||||
|
||||
if (typeof unknown == 'string') {
|
||||
// unknown as string
|
||||
unknown
|
||||
.toLowerCase()
|
||||
.split(/\+\s*/)
|
||||
.forEach(function (name) {
|
||||
switch (name) {
|
||||
case 'ctrl':
|
||||
case 'cmd':
|
||||
metaKeyCode |= CTRL_MASK;
|
||||
break;
|
||||
case 'alt':
|
||||
metaKeyCode |= ALT_MASK;
|
||||
break;
|
||||
case 'shift':
|
||||
metaKeyCode |= SHIFT_MASK;
|
||||
break;
|
||||
default:
|
||||
metaKeyCode |= keymap[name];
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// unknown as key event
|
||||
if (unknown.ctrlKey || unknown.metaKey) {
|
||||
metaKeyCode |= CTRL_MASK;
|
||||
}
|
||||
if (unknown.altKey) {
|
||||
metaKeyCode |= ALT_MASK;
|
||||
}
|
||||
if (unknown.shiftKey) {
|
||||
metaKeyCode |= SHIFT_MASK;
|
||||
}
|
||||
metaKeyCode |= unknown.keyCode;
|
||||
}
|
||||
|
||||
return metaKeyCode;
|
||||
}
|
||||
kity.extendClass(MinderEvent, {
|
||||
isShortcutKey: function (keyCombine) {
|
||||
var keyEvent = this.originEvent;
|
||||
if (!keyEvent) return false;
|
||||
|
||||
return getMetaKeyCode(keyCombine) == getMetaKeyCode(keyEvent);
|
||||
},
|
||||
});
|
||||
|
||||
Minder.registerInitHook(function () {
|
||||
this._initShortcutKey();
|
||||
});
|
||||
|
||||
kity.extendClass(Minder, {
|
||||
_initShortcutKey: function () {
|
||||
this._bindShortcutKeys();
|
||||
},
|
||||
|
||||
_bindShortcutKeys: function () {
|
||||
var map = (this._shortcutKeys = {});
|
||||
var has = 'hasOwnProperty';
|
||||
this.on('keydown', function (e) {
|
||||
for (var keys in map) {
|
||||
if (!map[has](keys)) continue;
|
||||
if (e.isShortcutKey(keys)) {
|
||||
var fn = map[keys];
|
||||
if (fn.__statusCondition && fn.__statusCondition != this.getStatus()) return;
|
||||
fn();
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
addShortcut: function (keys, fn) {
|
||||
var binds = this._shortcutKeys;
|
||||
keys.split(/\|\s*/).forEach(function (combine) {
|
||||
var parts = combine.split('::');
|
||||
var status;
|
||||
if (parts.length > 1) {
|
||||
combine = parts[1];
|
||||
status = parts[0];
|
||||
fn.__statusCondition = status;
|
||||
}
|
||||
binds[combine] = fn;
|
||||
});
|
||||
},
|
||||
|
||||
addCommandShortcutKeys: function (cmd, keys) {
|
||||
var binds = this._commandShortcutKeys || (this._commandShortcutKeys = {});
|
||||
var obj = {},
|
||||
km = this;
|
||||
if (keys) {
|
||||
obj[cmd] = keys;
|
||||
} else {
|
||||
obj = cmd;
|
||||
}
|
||||
|
||||
var minder = this;
|
||||
|
||||
utils.each(obj, function (keys, command) {
|
||||
binds[command] = keys;
|
||||
|
||||
minder.addShortcut(keys, function execCommandByShortcut() {
|
||||
/**
|
||||
* 之前判断有问题,由 === 0 改为 !== -1
|
||||
* @editor Naixor
|
||||
* @Date 2015-12-2
|
||||
*/
|
||||
if (minder.queryCommandState(command) !== -1) {
|
||||
minder.execCommand(command);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
getCommandShortcutKey: function (cmd) {
|
||||
var binds = this._commandShortcutKeys;
|
||||
return (binds && binds[cmd]) || null;
|
||||
},
|
||||
|
||||
/**
|
||||
* @Desc: 添加一个判断是否支持原生Clipboard的变量,用于对ctrl + v和ctrl + c的处理
|
||||
* @Editor: Naixor
|
||||
* @Date: 2015.9.20
|
||||
*/
|
||||
supportClipboardEvent: (function (window) {
|
||||
return !!window.ClipboardEvent;
|
||||
})(window),
|
||||
});
|
||||
});
|
|
@ -0,0 +1,59 @@
|
|||
/* eslint-disable */
|
||||
/**
|
||||
* @fileOverview
|
||||
*
|
||||
* 状态切换控制
|
||||
*
|
||||
* @author: techird
|
||||
* @copyright: Baidu FEX, 2014
|
||||
*/
|
||||
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('./kity');
|
||||
var Minder = require('./minder');
|
||||
|
||||
var sf = ~window.location.href.indexOf('status');
|
||||
var tf = ~window.location.href.indexOf('trace');
|
||||
|
||||
Minder.registerInitHook(function () {
|
||||
this._initStatus();
|
||||
});
|
||||
|
||||
kity.extendClass(Minder, {
|
||||
_initStatus: function () {
|
||||
this._status = 'normal';
|
||||
this._rollbackStatus = 'normal';
|
||||
},
|
||||
|
||||
setStatus: function (status, force) {
|
||||
// 在 readonly 模式下,只有 force 为 true 才能切换回来
|
||||
if (this._status == 'readonly' && !force) return this;
|
||||
if (status != this._status) {
|
||||
this._rollbackStatus = this._status;
|
||||
this._status = status;
|
||||
this.fire('statuschange', {
|
||||
lastStatus: this._rollbackStatus,
|
||||
currentStatus: this._status,
|
||||
});
|
||||
if (sf) {
|
||||
/* global console: true */
|
||||
console.log(window.event.type, this._rollbackStatus, '->', this._status);
|
||||
if (tf) {
|
||||
console.trace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
rollbackStatus: function () {
|
||||
this.setStatus(this._rollbackStatus);
|
||||
},
|
||||
getRollbackStatus: function () {
|
||||
return this._rollbackStatus;
|
||||
},
|
||||
getStatus: function () {
|
||||
return this._status;
|
||||
},
|
||||
});
|
||||
});
|
|
@ -0,0 +1,102 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('./kity');
|
||||
var utils = require('./utils');
|
||||
var Minder = require('./minder');
|
||||
var Command = require('./command');
|
||||
var MinderNode = require('./node');
|
||||
var Module = require('./module');
|
||||
|
||||
var _templates = {};
|
||||
|
||||
function register(name, supports) {
|
||||
_templates[name] = supports;
|
||||
}
|
||||
exports.register = register;
|
||||
|
||||
utils.extend(Minder, {
|
||||
getTemplateList: function () {
|
||||
return _templates;
|
||||
},
|
||||
});
|
||||
|
||||
kity.extendClass(
|
||||
Minder,
|
||||
(function () {
|
||||
var originGetTheme = Minder.prototype.getTheme;
|
||||
return {
|
||||
useTemplate: function (name, duration) {
|
||||
this.setTemplate(name);
|
||||
this.refresh(duration || 500);
|
||||
},
|
||||
|
||||
getTemplate: function () {
|
||||
return this._template || 'default';
|
||||
},
|
||||
|
||||
setTemplate: function (name) {
|
||||
this._template = name || null;
|
||||
},
|
||||
|
||||
getTemplateSupport: function (method) {
|
||||
var supports = _templates[this.getTemplate()];
|
||||
return supports && supports[method];
|
||||
},
|
||||
|
||||
getTheme: function (node) {
|
||||
var support = this.getTemplateSupport('getTheme') || originGetTheme;
|
||||
return support.call(this, node);
|
||||
},
|
||||
};
|
||||
})()
|
||||
);
|
||||
|
||||
kity.extendClass(
|
||||
MinderNode,
|
||||
(function () {
|
||||
var originGetLayout = MinderNode.prototype.getLayout;
|
||||
var originGetConnect = MinderNode.prototype.getConnect;
|
||||
return {
|
||||
getLayout: function () {
|
||||
var support = this.getMinder().getTemplateSupport('getLayout') || originGetLayout;
|
||||
return support.call(this, this);
|
||||
},
|
||||
|
||||
getConnect: function () {
|
||||
var support = this.getMinder().getTemplateSupport('getConnect') || originGetConnect;
|
||||
return support.call(this, this);
|
||||
},
|
||||
};
|
||||
})()
|
||||
);
|
||||
let timer = null;
|
||||
|
||||
Module.register('TemplateModule', {
|
||||
/**
|
||||
* @command Template
|
||||
* @description 设置当前脑图的模板
|
||||
* @param {string} name 模板名称
|
||||
* 允许使用的模板可以使用 `kityminder.Minder.getTemplateList()` 查询
|
||||
* @state
|
||||
* 0: 始终可用
|
||||
* @return 返回当前的模板名称
|
||||
*/
|
||||
commands: {
|
||||
template: kity.createClass('TemplateCommand', {
|
||||
base: Command,
|
||||
|
||||
execute: function (minder, name) {
|
||||
minder.useTemplate(name);
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(() => {
|
||||
minder.execCommand('camera');
|
||||
}, 550);
|
||||
},
|
||||
|
||||
queryValue: function (minder) {
|
||||
return minder.getTemplate() || 'default';
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
});
|
|
@ -0,0 +1,171 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('./kity');
|
||||
var utils = require('./utils');
|
||||
var Minder = require('./minder');
|
||||
var MinderNode = require('./node');
|
||||
var Module = require('./module');
|
||||
var Command = require('./command');
|
||||
|
||||
var cssLikeValueMatcher = {
|
||||
left: function (value) {
|
||||
return (3 in value && value[3]) || (1 in value && value[1]) || value[0];
|
||||
},
|
||||
right: function (value) {
|
||||
return (1 in value && value[1]) || value[0];
|
||||
},
|
||||
top: function (value) {
|
||||
return value[0];
|
||||
},
|
||||
bottom: function (value) {
|
||||
return (2 in value && value[2]) || value[0];
|
||||
},
|
||||
};
|
||||
|
||||
var _themes = {};
|
||||
|
||||
/**
|
||||
* 注册一个主题
|
||||
*
|
||||
* @param {String} name 主题的名称
|
||||
* @param {Plain} theme 主题的样式描述
|
||||
*
|
||||
* @example
|
||||
* Minder.registerTheme('default', {
|
||||
* 'root-color': 'red',
|
||||
* 'root-stroke': 'none',
|
||||
* 'root-padding': [10, 20]
|
||||
* });
|
||||
*/
|
||||
function register(name, theme) {
|
||||
_themes[name] = theme;
|
||||
}
|
||||
exports.register = register;
|
||||
|
||||
utils.extend(Minder, {
|
||||
getThemeList: function () {
|
||||
return _themes;
|
||||
},
|
||||
});
|
||||
|
||||
kity.extendClass(Minder, {
|
||||
/**
|
||||
* 切换脑图实例上的主题
|
||||
* @param {String} name 要使用的主题的名称
|
||||
*/
|
||||
useTheme: function (name) {
|
||||
this.setTheme(name);
|
||||
this.refresh(800);
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
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();
|
||||
if (container) {
|
||||
container.classList.remove('km-theme-' + lastTheme);
|
||||
if (name) {
|
||||
container.classList.add('km-theme-' + name);
|
||||
}
|
||||
container.style.background = this.getStyle('background');
|
||||
}
|
||||
this.fire('themechange', {
|
||||
theme: name,
|
||||
});
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取脑图实例上的当前主题
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
getTheme: function (node) {
|
||||
return this._theme || this.getOption('defaultTheme') || 'fresh-blue';
|
||||
},
|
||||
|
||||
getThemeItems: function (node) {
|
||||
var theme = this.getTheme(node);
|
||||
return _themes[this.getTheme(node)];
|
||||
},
|
||||
|
||||
/**
|
||||
* 获得脑图实例上的样式
|
||||
* @param {String} item 样式名称
|
||||
*/
|
||||
getStyle: function (item, node) {
|
||||
var items = this.getThemeItems(node);
|
||||
var segment, dir, selector, value, matcher;
|
||||
|
||||
if (item in items) return items[item];
|
||||
|
||||
// 尝试匹配 CSS 数组形式的值
|
||||
// 比如 item 为 'pading-left'
|
||||
// theme 里有 {'padding': [10, 20]} 的定义,则可以返回 20
|
||||
segment = item.split('-');
|
||||
if (segment.length < 2) return null;
|
||||
|
||||
dir = segment.pop();
|
||||
item = segment.join('-');
|
||||
|
||||
if (item in items) {
|
||||
value = items[item];
|
||||
if (utils.isArray(value) && (matcher = cssLikeValueMatcher[dir])) {
|
||||
return matcher(value);
|
||||
}
|
||||
if (!isNaN(value)) return value;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取指定节点的样式
|
||||
* @param {String} name 样式名称,可以不加节点类型的前缀
|
||||
*/
|
||||
getNodeStyle: function (node, name) {
|
||||
var value = this.getStyle(node.getType() + '-' + name, node);
|
||||
return value !== null ? value : this.getStyle(name, node);
|
||||
},
|
||||
});
|
||||
|
||||
kity.extendClass(MinderNode, {
|
||||
getStyle: function (name) {
|
||||
return this.getMinder().getNodeStyle(this, name);
|
||||
},
|
||||
});
|
||||
|
||||
Module.register('Theme', {
|
||||
defaultOptions: {
|
||||
defaultTheme: 'fresh-blue',
|
||||
},
|
||||
commands: {
|
||||
/**
|
||||
* @command Theme
|
||||
* @description 设置当前脑图的主题
|
||||
* @param {string} name 主题名称
|
||||
* 允许使用的主题可以使用 `kityminder.Minder.getThemeList()` 查询
|
||||
* @state
|
||||
* 0: 始终可用
|
||||
* @return 返回当前的主题名称
|
||||
*/
|
||||
theme: kity.createClass('ThemeCommand', {
|
||||
base: Command,
|
||||
|
||||
execute: function (km, name) {
|
||||
return km.useTheme(name);
|
||||
},
|
||||
|
||||
queryValue: function (km) {
|
||||
return km.getTheme() || 'default';
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
Minder.registerInitHook(function () {
|
||||
this.setTheme();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,68 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports) {
|
||||
var kity = require('./kity');
|
||||
var uuidMap = {};
|
||||
|
||||
exports.extend = kity.Utils.extend.bind(kity.Utils);
|
||||
exports.each = kity.Utils.each.bind(kity.Utils);
|
||||
|
||||
exports.uuid = function (group) {
|
||||
uuidMap[group] = uuidMap[group] ? uuidMap[group] + 1 : 1;
|
||||
return group + uuidMap[group];
|
||||
};
|
||||
|
||||
exports.guid = function () {
|
||||
return (+new Date() * 1e6 + Math.floor(Math.random() * 1e6)).toString(36);
|
||||
};
|
||||
|
||||
exports.trim = function (str) {
|
||||
return str.replace(/(^[ \t\n\r]+)|([ \t\n\r]+$)/g, '');
|
||||
};
|
||||
|
||||
exports.keys = function (plain) {
|
||||
var keys = [];
|
||||
for (var key in plain) {
|
||||
if (plain.hasOwnProperty(key)) {
|
||||
keys.push(key);
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
};
|
||||
|
||||
exports.clone = function (source) {
|
||||
return JSON.parse(JSON.stringify(source));
|
||||
};
|
||||
|
||||
exports.comparePlainObject = function (a, b) {
|
||||
return JSON.stringify(a) == JSON.stringify(b);
|
||||
};
|
||||
|
||||
exports.encodeHtml = function (str, reg) {
|
||||
return str
|
||||
? str.replace(reg || /[&<">'](?:(amp|lt|quot|gt|#39|nbsp);)?/g, function (a, b) {
|
||||
if (b) {
|
||||
return a;
|
||||
} else {
|
||||
return {
|
||||
'<': '<',
|
||||
'&': '&',
|
||||
'"': '"',
|
||||
'>': '>',
|
||||
"'": ''',
|
||||
}[a];
|
||||
}
|
||||
})
|
||||
: '';
|
||||
};
|
||||
|
||||
exports.clearWhiteSpace = function (str) {
|
||||
return str.replace(/[\u200b\t\r\n]/g, '');
|
||||
};
|
||||
|
||||
exports.each(['String', 'Function', 'Array', 'Number', 'RegExp', 'Object'], function (v) {
|
||||
var toString = Object.prototype.toString;
|
||||
exports['is' + v] = function (obj) {
|
||||
return toString.apply(obj) == '[object ' + v + ']';
|
||||
};
|
||||
});
|
||||
});
|
|
@ -0,0 +1,4 @@
|
|||
/* eslint-disable */
|
||||
define('expose-kityminder', function (require, exports, module) {
|
||||
module.exports = window.kityminder = require('./kityminder');
|
||||
});
|
|
@ -0,0 +1,107 @@
|
|||
/* eslint-disable */
|
||||
/**
|
||||
* @fileOverview
|
||||
*
|
||||
* 默认导出(全部模块)
|
||||
*
|
||||
* @author: techird
|
||||
* @copyright: Baidu FEX, 2014
|
||||
*/
|
||||
|
||||
define(function (require, exports, module) {
|
||||
var kityminder = {
|
||||
version: require('./core/minder').version,
|
||||
};
|
||||
|
||||
// 核心导出,大写的部分导出类,小写的部分简单 require 一下
|
||||
// 这里顺序是有讲究的,调整前先弄清楚依赖关系。
|
||||
require('./core/utils');
|
||||
kityminder.Minder = require('./core/minder');
|
||||
kityminder.Command = require('./core/command');
|
||||
kityminder.Node = require('./core/node');
|
||||
require('./core/option');
|
||||
require('./core/animate');
|
||||
kityminder.Event = require('./core/event');
|
||||
kityminder.data = require('./core/data');
|
||||
require('./core/compatibility');
|
||||
kityminder.KeyMap = require('./core/keymap');
|
||||
require('./core/shortcut');
|
||||
require('./core/status');
|
||||
require('./core/paper');
|
||||
require('./core/select');
|
||||
require('./core/focus');
|
||||
require('./core/keyreceiver');
|
||||
kityminder.Module = require('./core/module');
|
||||
require('./core/readonly');
|
||||
kityminder.Render = require('./core/render');
|
||||
kityminder.Connect = require('./core/connect');
|
||||
kityminder.Layout = require('./core/layout');
|
||||
kityminder.Theme = require('./core/theme');
|
||||
kityminder.Template = require('./core/template');
|
||||
kityminder.Promise = require('./core/promise');
|
||||
require('./core/_boxv');
|
||||
require('./core/patch');
|
||||
|
||||
// 模块依赖
|
||||
require('./module/arrange');
|
||||
require('./module/basestyle');
|
||||
require('./module/clipboard');
|
||||
require('./module/dragtree');
|
||||
require('./module/expand');
|
||||
require('./module/font');
|
||||
require('./module/hyperlink');
|
||||
require('./module/image');
|
||||
require('./module/image-viewer');
|
||||
require('./module/keynav');
|
||||
require('./module/layout');
|
||||
require('./module/node');
|
||||
require('./module/note');
|
||||
require('./module/outline');
|
||||
require('./module/priority');
|
||||
require('./module/progress');
|
||||
require('./module/resource');
|
||||
require('./module/select');
|
||||
require('./module/style');
|
||||
require('./module/text');
|
||||
require('./module/view');
|
||||
require('./module/zoom');
|
||||
|
||||
require('./protocol/json');
|
||||
require('./protocol/text');
|
||||
require('./protocol/markdown');
|
||||
require('./protocol/svg');
|
||||
require('./protocol/png');
|
||||
|
||||
require('./layout/mind');
|
||||
require('./layout/btree');
|
||||
require('./layout/filetree');
|
||||
require('./layout/fish-bone-master');
|
||||
require('./layout/fish-bone-slave');
|
||||
require('./layout/tianpan');
|
||||
|
||||
require('./theme/default');
|
||||
require('./theme/snow');
|
||||
require('./theme/fresh');
|
||||
require('./theme/fish');
|
||||
require('./theme/snow');
|
||||
require('./theme/wire');
|
||||
require('./theme/tianpan');
|
||||
|
||||
require('./connect/arc');
|
||||
require('./connect/arc_tp');
|
||||
require('./connect/bezier');
|
||||
require('./connect/fish-bone-master');
|
||||
require('./connect/l');
|
||||
require('./connect/poly');
|
||||
require('./connect/under');
|
||||
|
||||
require('./template/default');
|
||||
require('./template/structure');
|
||||
require('./template/filetree');
|
||||
require('./template/right');
|
||||
require('./template/fish-bone');
|
||||
require('./template/tianpan');
|
||||
|
||||
window.kityminder = kityminder;
|
||||
module.exports = kityminder;
|
||||
});
|
|
@ -0,0 +1,145 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('../core/kity');
|
||||
var Layout = require('../core/layout');
|
||||
|
||||
['left', 'right', 'top', 'bottom'].forEach(registerLayoutForDirection);
|
||||
|
||||
function registerLayoutForDirection(name) {
|
||||
var axis = name == 'left' || name == 'right' ? 'x' : 'y';
|
||||
var dir = name == 'left' || name == 'top' ? -1 : 1;
|
||||
|
||||
var oppsite = {
|
||||
left: 'right',
|
||||
right: 'left',
|
||||
top: 'bottom',
|
||||
bottom: 'top',
|
||||
x: 'y',
|
||||
y: 'x',
|
||||
};
|
||||
|
||||
function getOrderHint(node) {
|
||||
var hint = [];
|
||||
var box = node.getLayoutBox();
|
||||
var offset = 5;
|
||||
|
||||
if (axis == 'x') {
|
||||
hint.push({
|
||||
type: 'up',
|
||||
node: node,
|
||||
area: new kity.Box({
|
||||
x: box.x,
|
||||
y: box.top - node.getStyle('margin-top') - offset,
|
||||
width: box.width,
|
||||
height: node.getStyle('margin-top'),
|
||||
}),
|
||||
path: ['M', box.x, box.top - offset, 'L', box.right, box.top - offset],
|
||||
});
|
||||
|
||||
hint.push({
|
||||
type: 'down',
|
||||
node: node,
|
||||
area: new kity.Box({
|
||||
x: box.x,
|
||||
y: box.bottom + offset,
|
||||
width: box.width,
|
||||
height: node.getStyle('margin-bottom'),
|
||||
}),
|
||||
path: ['M', box.x, box.bottom + offset, 'L', box.right, box.bottom + offset],
|
||||
});
|
||||
} else {
|
||||
hint.push({
|
||||
type: 'up',
|
||||
node: node,
|
||||
area: new kity.Box({
|
||||
x: box.left - node.getStyle('margin-left') - offset,
|
||||
y: box.top,
|
||||
width: node.getStyle('margin-left'),
|
||||
height: box.height,
|
||||
}),
|
||||
path: ['M', box.left - offset, box.top, 'L', box.left - offset, box.bottom],
|
||||
});
|
||||
|
||||
hint.push({
|
||||
type: 'down',
|
||||
node: node,
|
||||
area: new kity.Box({
|
||||
x: box.right + offset,
|
||||
y: box.top,
|
||||
width: node.getStyle('margin-right'),
|
||||
height: box.height,
|
||||
}),
|
||||
path: ['M', box.right + offset, box.top, 'L', box.right + offset, box.bottom],
|
||||
});
|
||||
}
|
||||
return hint;
|
||||
}
|
||||
|
||||
Layout.register(
|
||||
name,
|
||||
kity.createClass({
|
||||
base: Layout,
|
||||
|
||||
doLayout: function (parent, children) {
|
||||
var pbox = parent.getContentBox();
|
||||
|
||||
if (axis == 'x') {
|
||||
parent.setVertexOut(new kity.Point(pbox[name], pbox.cy));
|
||||
parent.setLayoutVectorOut(new kity.Vector(dir, 0));
|
||||
} else {
|
||||
parent.setVertexOut(new kity.Point(pbox.cx, pbox[name]));
|
||||
parent.setLayoutVectorOut(new kity.Vector(0, dir));
|
||||
}
|
||||
|
||||
if (!children.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
children.forEach(function (child) {
|
||||
var cbox = child.getContentBox();
|
||||
child.setLayoutTransform(new kity.Matrix());
|
||||
|
||||
if (axis == 'x') {
|
||||
child.setVertexIn(new kity.Point(cbox[oppsite[name]], cbox.cy));
|
||||
child.setLayoutVectorIn(new kity.Vector(dir, 0));
|
||||
} else {
|
||||
child.setVertexIn(new kity.Point(cbox.cx, cbox[oppsite[name]]));
|
||||
child.setLayoutVectorIn(new kity.Vector(0, dir));
|
||||
}
|
||||
});
|
||||
|
||||
this.align(children, oppsite[name]);
|
||||
this.stack(children, oppsite[axis]);
|
||||
|
||||
var bbox = this.getBranchBox(children);
|
||||
var xAdjust = 0,
|
||||
yAdjust = 0;
|
||||
|
||||
if (axis == 'x') {
|
||||
xAdjust = pbox[name];
|
||||
xAdjust += dir * parent.getStyle('margin-' + name);
|
||||
xAdjust += dir * children[0].getStyle('margin-' + oppsite[name]);
|
||||
|
||||
yAdjust = pbox.bottom;
|
||||
yAdjust -= pbox.height / 2;
|
||||
yAdjust -= bbox.height / 2;
|
||||
yAdjust -= bbox.y;
|
||||
} else {
|
||||
xAdjust = pbox.right;
|
||||
xAdjust -= pbox.width / 2;
|
||||
xAdjust -= bbox.width / 2;
|
||||
xAdjust -= bbox.x;
|
||||
|
||||
yAdjust = pbox[name];
|
||||
yAdjust += dir * parent.getStyle('margin-' + name);
|
||||
yAdjust += dir * children[0].getStyle('margin-' + oppsite[name]);
|
||||
}
|
||||
|
||||
this.move(children, xAdjust, yAdjust);
|
||||
},
|
||||
|
||||
getOrderHint: getOrderHint,
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,90 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('../core/kity');
|
||||
var Layout = require('../core/layout');
|
||||
|
||||
[-1, 1].forEach(registerLayoutForDir);
|
||||
|
||||
function registerLayoutForDir(dir) {
|
||||
var name = 'filetree-' + (dir > 0 ? 'down' : 'up');
|
||||
|
||||
Layout.register(
|
||||
name,
|
||||
kity.createClass({
|
||||
base: Layout,
|
||||
|
||||
doLayout: function (parent, children, round) {
|
||||
var pBox = parent.getContentBox();
|
||||
var indent = 20;
|
||||
|
||||
parent.setVertexOut(new kity.Point(pBox.left + indent, dir > 0 ? pBox.bottom : pBox.top));
|
||||
parent.setLayoutVectorOut(new kity.Vector(0, dir));
|
||||
|
||||
if (!children.length) return;
|
||||
|
||||
children.forEach(function (child) {
|
||||
var cbox = child.getContentBox();
|
||||
child.setLayoutTransform(new kity.Matrix());
|
||||
|
||||
child.setVertexIn(new kity.Point(cbox.left, cbox.cy));
|
||||
child.setLayoutVectorIn(new kity.Vector(1, 0));
|
||||
});
|
||||
|
||||
this.align(children, 'left');
|
||||
this.stack(children, 'y');
|
||||
|
||||
var xAdjust = 0;
|
||||
xAdjust += pBox.left;
|
||||
xAdjust += indent;
|
||||
xAdjust += children[0].getStyle('margin-left');
|
||||
|
||||
var yAdjust = 0;
|
||||
|
||||
if (dir > 0) {
|
||||
yAdjust += pBox.bottom;
|
||||
yAdjust += parent.getStyle('margin-bottom');
|
||||
yAdjust += children[0].getStyle('margin-top');
|
||||
} else {
|
||||
yAdjust -= this.getTreeBox(children).bottom;
|
||||
yAdjust += pBox.top;
|
||||
yAdjust -= parent.getStyle('margin-top');
|
||||
yAdjust -= children[0].getStyle('margin-bottom');
|
||||
}
|
||||
|
||||
this.move(children, xAdjust, yAdjust);
|
||||
},
|
||||
|
||||
getOrderHint: function (node) {
|
||||
var hint = [];
|
||||
var box = node.getLayoutBox();
|
||||
var offset = node.getLevel() > 1 ? 3 : 5;
|
||||
|
||||
hint.push({
|
||||
type: 'up',
|
||||
node: node,
|
||||
area: new kity.Box({
|
||||
x: box.x,
|
||||
y: box.top - node.getStyle('margin-top') - offset,
|
||||
width: box.width,
|
||||
height: node.getStyle('margin-top'),
|
||||
}),
|
||||
path: ['M', box.x, box.top - offset, 'L', box.right, box.top - offset],
|
||||
});
|
||||
|
||||
hint.push({
|
||||
type: 'down',
|
||||
node: node,
|
||||
area: new kity.Box({
|
||||
x: box.x,
|
||||
y: box.bottom + offset,
|
||||
width: box.width,
|
||||
height: node.getStyle('margin-bottom'),
|
||||
}),
|
||||
path: ['M', box.x, box.bottom + offset, 'L', box.right, box.bottom + offset],
|
||||
});
|
||||
return hint;
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,68 @@
|
|||
/* eslint-disable */
|
||||
/**
|
||||
* @fileOverview
|
||||
*
|
||||
* 鱼骨图主骨架布局
|
||||
*
|
||||
* @author: techird
|
||||
* @copyright: Baidu FEX, 2014
|
||||
*/
|
||||
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('../core/kity');
|
||||
var Layout = require('../core/layout');
|
||||
|
||||
Layout.register(
|
||||
'fish-bone-master',
|
||||
kity.createClass('FishBoneMasterLayout', {
|
||||
base: Layout,
|
||||
|
||||
doLayout: function (parent, children, round) {
|
||||
var upPart = [],
|
||||
downPart = [];
|
||||
|
||||
var child = children[0];
|
||||
var pBox = parent.getContentBox();
|
||||
|
||||
parent.setVertexOut(new kity.Point(pBox.right, pBox.cy));
|
||||
parent.setLayoutVectorOut(new kity.Vector(1, 0));
|
||||
|
||||
if (!child) return;
|
||||
|
||||
var cBox = child.getContentBox();
|
||||
var pMarginRight = parent.getStyle('margin-right');
|
||||
var cMarginLeft = child.getStyle('margin-left');
|
||||
var cMarginTop = child.getStyle('margin-top');
|
||||
var cMarginBottom = child.getStyle('margin-bottom');
|
||||
|
||||
children.forEach(function (child, index) {
|
||||
child.setLayoutTransform(new kity.Matrix());
|
||||
var cBox = child.getContentBox();
|
||||
|
||||
if (index % 2) {
|
||||
downPart.push(child);
|
||||
child.setVertexIn(new kity.Point(cBox.left, cBox.top));
|
||||
child.setLayoutVectorIn(new kity.Vector(1, 1));
|
||||
} else {
|
||||
upPart.push(child);
|
||||
child.setVertexIn(new kity.Point(cBox.left, cBox.bottom));
|
||||
child.setLayoutVectorIn(new kity.Vector(1, -1));
|
||||
}
|
||||
});
|
||||
|
||||
this.stack(upPart, 'x');
|
||||
this.stack(downPart, 'x');
|
||||
|
||||
this.align(upPart, 'bottom');
|
||||
this.align(downPart, 'top');
|
||||
|
||||
var xAdjust = pBox.right + pMarginRight + cMarginLeft;
|
||||
var yAdjustUp = pBox.cy - cMarginBottom - parent.getStyle('margin-top');
|
||||
var yAdjustDown = pBox.cy + cMarginTop + parent.getStyle('margin-bottom');
|
||||
|
||||
this.move(upPart, xAdjust, yAdjustUp);
|
||||
this.move(downPart, xAdjust + cMarginLeft, yAdjustDown);
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
|
@ -0,0 +1,76 @@
|
|||
/* eslint-disable */
|
||||
/**
|
||||
* @fileOverview
|
||||
*
|
||||
*
|
||||
*
|
||||
* @author: techird
|
||||
* @copyright: Baidu FEX, 2014
|
||||
*/
|
||||
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('../core/kity');
|
||||
var Layout = require('../core/layout');
|
||||
|
||||
Layout.register(
|
||||
'fish-bone-slave',
|
||||
kity.createClass('FishBoneSlaveLayout', {
|
||||
base: Layout,
|
||||
|
||||
doLayout: function (parent, children, round) {
|
||||
var layout = this;
|
||||
var abs = Math.abs;
|
||||
var GOLD_CUT = 1 - 0.618;
|
||||
|
||||
var pBox = parent.getContentBox();
|
||||
var vi = parent.getLayoutVectorIn();
|
||||
|
||||
parent.setLayoutVectorOut(vi);
|
||||
|
||||
var goldX = pBox.left + pBox.width * GOLD_CUT;
|
||||
var pout = new kity.Point(goldX, vi.y > 0 ? pBox.bottom : pBox.top);
|
||||
parent.setVertexOut(pout);
|
||||
|
||||
var child = children[0];
|
||||
if (!child) return;
|
||||
|
||||
var cBox = child.getContentBox();
|
||||
|
||||
children.forEach(function (child, index) {
|
||||
child.setLayoutTransform(new kity.Matrix());
|
||||
child.setLayoutVectorIn(new kity.Vector(1, 0));
|
||||
child.setVertexIn(new kity.Point(cBox.left, cBox.cy));
|
||||
});
|
||||
|
||||
this.stack(children, 'y');
|
||||
this.align(children, 'left');
|
||||
|
||||
var xAdjust = 0,
|
||||
yAdjust = 0;
|
||||
xAdjust += pout.x;
|
||||
|
||||
if (parent.getLayoutVectorOut().y < 0) {
|
||||
yAdjust -= this.getTreeBox(children).bottom;
|
||||
yAdjust += parent.getContentBox().top;
|
||||
yAdjust -= parent.getStyle('margin-top');
|
||||
yAdjust -= child.getStyle('margin-bottom');
|
||||
} else {
|
||||
yAdjust += parent.getContentBox().bottom;
|
||||
yAdjust += parent.getStyle('margin-bottom');
|
||||
yAdjust += child.getStyle('margin-top');
|
||||
}
|
||||
|
||||
this.move(children, xAdjust, yAdjust);
|
||||
|
||||
if (round == 2) {
|
||||
children.forEach(function (child) {
|
||||
var m = child.getLayoutTransform();
|
||||
var cbox = child.getContentBox();
|
||||
var pin = m.transformPoint(new kity.Point(cbox.left, 0));
|
||||
layout.move([child], abs(pin.y - pout.y), 0);
|
||||
});
|
||||
}
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
|
@ -0,0 +1,66 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('../core/kity');
|
||||
var Layout = require('../core/layout');
|
||||
var Minder = require('../core/minder');
|
||||
|
||||
Layout.register(
|
||||
'mind',
|
||||
kity.createClass({
|
||||
base: Layout,
|
||||
|
||||
doLayout: function (node, children) {
|
||||
var layout = this;
|
||||
var half = Math.ceil(node.children.length / 2);
|
||||
var right = [];
|
||||
var left = [];
|
||||
|
||||
children.forEach(function (child) {
|
||||
if (child.getIndex() < half) right.push(child);
|
||||
else left.push(child);
|
||||
});
|
||||
|
||||
var leftLayout = Minder.getLayoutInstance('left');
|
||||
var rightLayout = Minder.getLayoutInstance('right');
|
||||
|
||||
leftLayout.doLayout(node, left);
|
||||
rightLayout.doLayout(node, right);
|
||||
|
||||
var box = node.getContentBox();
|
||||
node.setVertexOut(new kity.Point(box.cx, box.cy));
|
||||
node.setLayoutVectorOut(new kity.Vector(0, 0));
|
||||
},
|
||||
|
||||
getOrderHint: function (node) {
|
||||
var hint = [];
|
||||
var box = node.getLayoutBox();
|
||||
var offset = 5;
|
||||
|
||||
hint.push({
|
||||
type: 'up',
|
||||
node: node,
|
||||
area: new kity.Box({
|
||||
x: box.x,
|
||||
y: box.top - node.getStyle('margin-top') - offset,
|
||||
width: box.width,
|
||||
height: node.getStyle('margin-top'),
|
||||
}),
|
||||
path: ['M', box.x, box.top - offset, 'L', box.right, box.top - offset],
|
||||
});
|
||||
|
||||
hint.push({
|
||||
type: 'down',
|
||||
node: node,
|
||||
area: new kity.Box({
|
||||
x: box.x,
|
||||
y: box.bottom + offset,
|
||||
width: box.width,
|
||||
height: node.getStyle('margin-bottom'),
|
||||
}),
|
||||
path: ['M', box.x, box.bottom + offset, 'L', box.right, box.bottom + offset],
|
||||
});
|
||||
return hint;
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
|
@ -0,0 +1,80 @@
|
|||
/* eslint-disable */
|
||||
/**
|
||||
* @fileOverview
|
||||
*
|
||||
* 天盘模板
|
||||
*
|
||||
* @author: along
|
||||
* @copyright: bpd729@163.com, 2015
|
||||
*/
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('../core/kity');
|
||||
var Layout = require('../core/layout');
|
||||
var Minder = require('../core/minder');
|
||||
|
||||
Layout.register(
|
||||
'tianpan',
|
||||
kity.createClass({
|
||||
base: Layout,
|
||||
|
||||
doLayout: function (parent, children) {
|
||||
if (children.length == 0) return;
|
||||
|
||||
var layout = this;
|
||||
var pbox = parent.getContentBox();
|
||||
|
||||
var x, y, box;
|
||||
var _theta = 5;
|
||||
var _r = Math.max(pbox.width, 50);
|
||||
children.forEach(function (child, index) {
|
||||
child.setLayoutTransform(new kity.Matrix());
|
||||
box = layout.getTreeBox(child);
|
||||
_r = Math.max(Math.max(box.width, box.height), _r);
|
||||
});
|
||||
_r = _r / 1.5 / Math.PI;
|
||||
|
||||
children.forEach(function (child, index) {
|
||||
x = _r * (Math.cos(_theta) + Math.sin(_theta) * _theta);
|
||||
y = _r * (Math.sin(_theta) - Math.cos(_theta) * _theta);
|
||||
|
||||
_theta += 0.9 - index * 0.02;
|
||||
child.setLayoutVectorIn(new kity.Vector(1, 0));
|
||||
child.setVertexIn(new kity.Point(pbox.cx, pbox.cy));
|
||||
child.setLayoutTransform(new kity.Matrix());
|
||||
layout.move([child], x, y);
|
||||
});
|
||||
},
|
||||
|
||||
getOrderHint: function (node) {
|
||||
var hint = [];
|
||||
var box = node.getLayoutBox();
|
||||
var offset = 5;
|
||||
|
||||
hint.push({
|
||||
type: 'up',
|
||||
node: node,
|
||||
area: {
|
||||
x: box.x,
|
||||
y: box.top - node.getStyle('margin-top') - offset,
|
||||
width: box.width,
|
||||
height: node.getStyle('margin-top'),
|
||||
},
|
||||
path: ['M', box.x, box.top - offset, 'L', box.right, box.top - offset],
|
||||
});
|
||||
|
||||
hint.push({
|
||||
type: 'down',
|
||||
node: node,
|
||||
area: {
|
||||
x: box.x,
|
||||
y: box.bottom + offset,
|
||||
width: box.width,
|
||||
height: node.getStyle('margin-bottom'),
|
||||
},
|
||||
path: ['M', box.x, box.bottom + offset, 'L', box.right, box.bottom + offset],
|
||||
});
|
||||
return hint;
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
|
@ -0,0 +1,166 @@
|
|||
/* eslint-disable */
|
||||
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('../core/kity');
|
||||
var MinderNode = require('../core/node');
|
||||
var Command = require('../core/command');
|
||||
var Module = require('../core/module');
|
||||
|
||||
kity.extendClass(MinderNode, {
|
||||
arrange: function (index) {
|
||||
var parent = this.parent;
|
||||
if (!parent) return;
|
||||
var sibling = parent.children;
|
||||
|
||||
if (index < 0 || index >= sibling.length) return;
|
||||
sibling.splice(this.getIndex(), 1);
|
||||
sibling.splice(index, 0, this);
|
||||
return this;
|
||||
},
|
||||
});
|
||||
|
||||
function asc(nodeA, nodeB) {
|
||||
return nodeA.getIndex() - nodeB.getIndex();
|
||||
}
|
||||
function desc(nodeA, nodeB) {
|
||||
return -asc(nodeA, nodeB);
|
||||
}
|
||||
|
||||
function canArrange(km) {
|
||||
var selected = km.getSelectedNode();
|
||||
return selected && selected.parent && selected.parent.children.length > 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @command ArrangeUp
|
||||
* @description 向上调整选中节点的位置
|
||||
* @shortcut Alt + Up
|
||||
* @state
|
||||
* 0: 当前选中了具有相同父亲的节点
|
||||
* -1: 其它情况
|
||||
*/
|
||||
var ArrangeUpCommand = kity.createClass('ArrangeUpCommand', {
|
||||
base: Command,
|
||||
|
||||
execute: function (km) {
|
||||
var nodes = km.getSelectedNodes();
|
||||
nodes.sort(asc);
|
||||
var lastIndexes = nodes.map(function (node) {
|
||||
return node.getIndex();
|
||||
});
|
||||
nodes.forEach(function (node, index) {
|
||||
node.arrange(lastIndexes[index] - 1);
|
||||
});
|
||||
km.layout(300);
|
||||
},
|
||||
|
||||
queryState: function (km) {
|
||||
var selected = km.getSelectedNode();
|
||||
return selected ? 0 : -1;
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* @command ArrangeDown
|
||||
* @description 向下调整选中节点的位置
|
||||
* @shortcut Alt + Down
|
||||
* @state
|
||||
* 0: 当前选中了具有相同父亲的节点
|
||||
* -1: 其它情况
|
||||
*/
|
||||
var ArrangeDownCommand = kity.createClass('ArrangeUpCommand', {
|
||||
base: Command,
|
||||
|
||||
execute: function (km) {
|
||||
var nodes = km.getSelectedNodes();
|
||||
nodes.sort(desc);
|
||||
var lastIndexes = nodes.map(function (node) {
|
||||
return node.getIndex();
|
||||
});
|
||||
nodes.forEach(function (node, index) {
|
||||
node.arrange(lastIndexes[index] + 1);
|
||||
});
|
||||
km.layout(300);
|
||||
},
|
||||
|
||||
queryState: function (km) {
|
||||
var selected = km.getSelectedNode();
|
||||
return selected ? 0 : -1;
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* @command Arrange
|
||||
* @description 调整选中节点的位置
|
||||
* @param {number} index 调整后节点的新位置
|
||||
* @state
|
||||
* 0: 当前选中了具有相同父亲的节点
|
||||
* -1: 其它情况
|
||||
*/
|
||||
var ArrangeCommand = kity.createClass('ArrangeCommand', {
|
||||
base: Command,
|
||||
|
||||
execute: function (km, index) {
|
||||
var nodes = km.getSelectedNodes().slice();
|
||||
|
||||
if (!nodes.length) return;
|
||||
|
||||
var ancestor = MinderNode.getCommonAncestor(nodes);
|
||||
|
||||
if (ancestor != nodes[0].parent) return;
|
||||
|
||||
var indexed = nodes.map(function (node) {
|
||||
return {
|
||||
index: node.getIndex(),
|
||||
node: node,
|
||||
};
|
||||
});
|
||||
|
||||
var asc =
|
||||
Math.min.apply(
|
||||
Math,
|
||||
indexed.map(function (one) {
|
||||
return one.index;
|
||||
})
|
||||
) >= index;
|
||||
|
||||
indexed.sort(function (a, b) {
|
||||
return asc ? b.index - a.index : a.index - b.index;
|
||||
});
|
||||
|
||||
indexed.forEach(function (one) {
|
||||
one.node.arrange(index);
|
||||
});
|
||||
|
||||
km.layout(300);
|
||||
},
|
||||
|
||||
queryState: function (km) {
|
||||
var selected = km.getSelectedNode();
|
||||
return selected ? 0 : -1;
|
||||
},
|
||||
});
|
||||
|
||||
Module.register('ArrangeModule', {
|
||||
commands: {
|
||||
arrangeup: ArrangeUpCommand,
|
||||
arrangedown: ArrangeDownCommand,
|
||||
arrange: ArrangeCommand,
|
||||
},
|
||||
contextmenu: [
|
||||
{
|
||||
command: 'arrangeup',
|
||||
},
|
||||
{
|
||||
command: 'arrangedown',
|
||||
},
|
||||
{
|
||||
divider: true,
|
||||
},
|
||||
],
|
||||
commandShortcutKeys: {
|
||||
arrangeup: 'normal::alt+Up',
|
||||
arrangedown: 'normal::alt+Down',
|
||||
},
|
||||
});
|
||||
});
|
|
@ -0,0 +1,123 @@
|
|||
/* eslint-disable */
|
||||
|
||||
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');
|
||||
|
||||
var TextRenderer = require('./text');
|
||||
|
||||
Module.register('basestylemodule', function () {
|
||||
var km = this;
|
||||
|
||||
function getNodeDataOrStyle(node, name) {
|
||||
return node.getData(name) || node.getStyle(name);
|
||||
}
|
||||
|
||||
TextRenderer.registerStyleHook(function (node, textGroup) {
|
||||
var fontWeight = getNodeDataOrStyle(node, 'font-weight');
|
||||
var fontStyle = getNodeDataOrStyle(node, 'font-style');
|
||||
var styleHash = [fontWeight, fontStyle].join('/');
|
||||
|
||||
textGroup.eachItem(function (index, item) {
|
||||
item.setFont({
|
||||
weight: fontWeight,
|
||||
style: fontStyle,
|
||||
});
|
||||
});
|
||||
});
|
||||
return {
|
||||
commands: {
|
||||
/**
|
||||
* @command Bold
|
||||
* @description 加粗选中的节点
|
||||
* @shortcut Ctrl + B
|
||||
* @state
|
||||
* 0: 当前有选中的节点
|
||||
* -1: 当前没有选中的节点
|
||||
* 1: 当前已选中的节点已加粗
|
||||
*/
|
||||
bold: kity.createClass('boldCommand', {
|
||||
base: Command,
|
||||
|
||||
execute: function (km) {
|
||||
var nodes = km.getSelectedNodes();
|
||||
if (this.queryState('bold') == 1) {
|
||||
nodes.forEach(function (n) {
|
||||
n.setData('font-weight').render();
|
||||
});
|
||||
} else {
|
||||
nodes.forEach(function (n) {
|
||||
n.setData('font-weight', 'bold').render();
|
||||
});
|
||||
}
|
||||
km.layout();
|
||||
},
|
||||
queryState: function () {
|
||||
var nodes = km.getSelectedNodes(),
|
||||
result = 0;
|
||||
if (nodes.length === 0) {
|
||||
return -1;
|
||||
}
|
||||
nodes.forEach(function (n) {
|
||||
if (n && n.getData('font-weight')) {
|
||||
result = 1;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
},
|
||||
}),
|
||||
/**
|
||||
* @command Italic
|
||||
* @description 加斜选中的节点
|
||||
* @shortcut Ctrl + I
|
||||
* @state
|
||||
* 0: 当前有选中的节点
|
||||
* -1: 当前没有选中的节点
|
||||
* 1: 当前已选中的节点已加斜
|
||||
*/
|
||||
italic: kity.createClass('italicCommand', {
|
||||
base: Command,
|
||||
|
||||
execute: function (km) {
|
||||
var nodes = km.getSelectedNodes();
|
||||
if (this.queryState('italic') == 1) {
|
||||
nodes.forEach(function (n) {
|
||||
n.setData('font-style').render();
|
||||
});
|
||||
} else {
|
||||
nodes.forEach(function (n) {
|
||||
n.setData('font-style', 'italic').render();
|
||||
});
|
||||
}
|
||||
|
||||
km.layout();
|
||||
},
|
||||
queryState: function () {
|
||||
var nodes = km.getSelectedNodes(),
|
||||
result = 0;
|
||||
if (nodes.length === 0) {
|
||||
return -1;
|
||||
}
|
||||
nodes.forEach(function (n) {
|
||||
if (n && n.getData('font-style')) {
|
||||
result = 1;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
},
|
||||
}),
|
||||
},
|
||||
commandShortcutKeys: {
|
||||
bold: 'ctrl+b', //bold
|
||||
italic: 'ctrl+i', //italic
|
||||
},
|
||||
};
|
||||
});
|
||||
});
|
|
@ -0,0 +1,172 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('../core/kity');
|
||||
var utils = require('../core/utils');
|
||||
var MinderNode = require('../core/node');
|
||||
var Command = require('../core/command');
|
||||
var Module = require('../core/module');
|
||||
|
||||
Module.register('ClipboardModule', function () {
|
||||
var km = this,
|
||||
_clipboardNodes = [],
|
||||
_selectedNodes = [];
|
||||
|
||||
function appendChildNode(parent, child) {
|
||||
_selectedNodes.push(child);
|
||||
km.appendNode(child, parent);
|
||||
child.render();
|
||||
child.setLayoutOffset(null);
|
||||
var children = child.children.map(function (node) {
|
||||
return node.clone();
|
||||
});
|
||||
|
||||
/*
|
||||
* fixed bug: Modified on 2015.08.05
|
||||
* 原因:粘贴递归 append 时没有清空原来父节点的子节点,而父节点被复制的时候,是连同子节点一起复制过来的
|
||||
* 解决办法:增加了下面这一行代码
|
||||
* by: @zhangbobell zhangbobell@163.com
|
||||
*/
|
||||
child.clearChildren();
|
||||
|
||||
for (var i = 0, ci; (ci = children[i]); i++) {
|
||||
appendChildNode(child, ci);
|
||||
}
|
||||
}
|
||||
|
||||
function sendToClipboard(nodes) {
|
||||
if (!nodes.length) return;
|
||||
nodes.sort(function (a, b) {
|
||||
return a.getIndex() - b.getIndex();
|
||||
});
|
||||
_clipboardNodes = nodes.map(function (node) {
|
||||
return node.clone();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @command Copy
|
||||
* @description 复制当前选中的节点
|
||||
* @shortcut Ctrl + C
|
||||
* @state
|
||||
* 0: 当前有选中的节点
|
||||
* -1: 当前没有选中的节点
|
||||
*/
|
||||
var CopyCommand = kity.createClass('CopyCommand', {
|
||||
base: Command,
|
||||
|
||||
execute: function (km) {
|
||||
sendToClipboard(km.getSelectedAncestors(true));
|
||||
this.setContentChanged(false);
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* @command Cut
|
||||
* @description 剪切当前选中的节点
|
||||
* @shortcut Ctrl + X
|
||||
* @state
|
||||
* 0: 当前有选中的节点
|
||||
* -1: 当前没有选中的节点
|
||||
*/
|
||||
var CutCommand = kity.createClass('CutCommand', {
|
||||
base: Command,
|
||||
|
||||
execute: function (km) {
|
||||
var ancestors = km.getSelectedAncestors();
|
||||
|
||||
if (ancestors.length === 0) return;
|
||||
|
||||
sendToClipboard(ancestors);
|
||||
|
||||
km.select(MinderNode.getCommonAncestor(ancestors), true);
|
||||
|
||||
ancestors.slice().forEach(function (node) {
|
||||
km.removeNode(node);
|
||||
});
|
||||
|
||||
km.layout(300);
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* @command Paste
|
||||
* @description 粘贴已复制的节点到每一个当前选中的节点上
|
||||
* @shortcut Ctrl + V
|
||||
* @state
|
||||
* 0: 当前有选中的节点
|
||||
* -1: 当前没有选中的节点
|
||||
*/
|
||||
var PasteCommand = kity.createClass('PasteCommand', {
|
||||
base: Command,
|
||||
|
||||
execute: function (km) {
|
||||
if (_clipboardNodes.length) {
|
||||
var nodes = km.getSelectedNodes();
|
||||
if (!nodes.length) return;
|
||||
|
||||
for (var i = 0, ni; (ni = _clipboardNodes[i]); i++) {
|
||||
for (var j = 0, node; (node = nodes[j]); j++) {
|
||||
appendChildNode(node, ni.clone());
|
||||
}
|
||||
}
|
||||
|
||||
km.select(_selectedNodes, true);
|
||||
_selectedNodes = [];
|
||||
|
||||
km.layout(300);
|
||||
}
|
||||
},
|
||||
|
||||
queryState: function (km) {
|
||||
return km.getSelectedNode() ? 0 : -1;
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* @Desc: 若支持原生clipboadr事件则基于原生扩展,否则使用km的基础事件只处理节点的粘贴复制
|
||||
* @Editor: Naixor
|
||||
* @Date: 2015.9.20
|
||||
*/
|
||||
if (km.supportClipboardEvent && !kity.Browser.gecko) {
|
||||
var Copy = function (e) {
|
||||
this.fire('beforeCopy', e);
|
||||
};
|
||||
|
||||
var Cut = function (e) {
|
||||
this.fire('beforeCut', e);
|
||||
};
|
||||
|
||||
var Paste = function (e) {
|
||||
this.fire('beforePaste', e);
|
||||
};
|
||||
|
||||
return {
|
||||
commands: {
|
||||
copy: CopyCommand,
|
||||
cut: CutCommand,
|
||||
paste: PasteCommand,
|
||||
},
|
||||
clipBoardEvents: {
|
||||
copy: Copy.bind(km),
|
||||
cut: Cut.bind(km),
|
||||
paste: Paste.bind(km),
|
||||
},
|
||||
sendToClipboard: sendToClipboard,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
commands: {
|
||||
copy: CopyCommand,
|
||||
cut: CutCommand,
|
||||
paste: PasteCommand,
|
||||
},
|
||||
commandShortcutKeys: {
|
||||
copy: 'normal::ctrl+c|',
|
||||
cut: 'normal::ctrl+x',
|
||||
paste: 'normal::ctrl+v',
|
||||
},
|
||||
sendToClipboard: sendToClipboard,
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
|
@ -0,0 +1,403 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('../core/kity');
|
||||
var utils = require('../core/utils');
|
||||
var MinderNode = require('../core/node');
|
||||
var Command = require('../core/command');
|
||||
var Module = require('../core/module');
|
||||
|
||||
// 矩形的变形动画定义
|
||||
var MoveToParentCommand = kity.createClass('MoveToParentCommand', {
|
||||
base: Command,
|
||||
execute: function (minder, nodes, parent) {
|
||||
var node;
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
node = nodes[i];
|
||||
if (node.parent) {
|
||||
node.parent.removeChild(node);
|
||||
parent.appendChild(node);
|
||||
node.render();
|
||||
}
|
||||
}
|
||||
parent.expand();
|
||||
minder.select(nodes, true);
|
||||
},
|
||||
});
|
||||
|
||||
var DropHinter = kity.createClass('DropHinter', {
|
||||
base: kity.Group,
|
||||
|
||||
constructor: function () {
|
||||
this.callBase();
|
||||
this.rect = new kity.Rect();
|
||||
this.addShape(this.rect);
|
||||
},
|
||||
|
||||
render: function (target) {
|
||||
this.setVisible(!!target);
|
||||
if (target) {
|
||||
this.rect
|
||||
.setBox(target.getLayoutBox())
|
||||
.setRadius(target.getStyle('radius') || 0)
|
||||
.stroke(target.getStyle('drop-hint-color') || 'yellow', target.getStyle('drop-hint-width') || 2);
|
||||
this.bringTop();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
var OrderHinter = kity.createClass('OrderHinter', {
|
||||
base: kity.Group,
|
||||
|
||||
constructor: function () {
|
||||
this.callBase();
|
||||
this.area = new kity.Rect();
|
||||
this.path = new kity.Path();
|
||||
this.addShapes([this.area, this.path]);
|
||||
},
|
||||
|
||||
render: function (hint) {
|
||||
this.setVisible(!!hint);
|
||||
if (hint) {
|
||||
this.area.setBox(hint.area);
|
||||
this.area.fill(hint.node.getStyle('order-hint-area-color') || 'rgba(0, 255, 0, .5)');
|
||||
this.path.setPathData(hint.path);
|
||||
this.path.stroke(
|
||||
hint.node.getStyle('order-hint-path-color') || '#0f0',
|
||||
hint.node.getStyle('order-hint-path-width') || 1
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// 对拖动对象的一个替代盒子,控制整个拖放的逻辑,包括:
|
||||
// 1. 从节点列表计算出拖动部分
|
||||
// 2. 计算可以 drop 的节点,产生 drop 交互提示
|
||||
var TreeDragger = kity.createClass('TreeDragger', {
|
||||
constructor: function (minder) {
|
||||
this._minder = minder;
|
||||
this._dropHinter = new DropHinter();
|
||||
this._orderHinter = new OrderHinter();
|
||||
minder.getRenderContainer().addShapes([this._dropHinter, this._orderHinter]);
|
||||
},
|
||||
|
||||
dragStart: function (position) {
|
||||
// 只记录开始位置,不马上开启拖放模式
|
||||
// 这个位置同时是拖放范围收缩时的焦点位置(中心)
|
||||
this._startPosition = position;
|
||||
},
|
||||
|
||||
dragMove: function (position) {
|
||||
// 启动拖放模式需要最小的移动距离
|
||||
var DRAG_MOVE_THRESHOLD = 10;
|
||||
|
||||
if (!this._startPosition) return;
|
||||
|
||||
var movement = kity.Vector.fromPoints(this._dragPosition || this._startPosition, position);
|
||||
var minder = this._minder;
|
||||
|
||||
this._dragPosition = position;
|
||||
|
||||
if (!this._dragMode) {
|
||||
// 判断拖放模式是否该启动
|
||||
if (kity.Vector.fromPoints(this._dragPosition, this._startPosition).length() < DRAG_MOVE_THRESHOLD) {
|
||||
return;
|
||||
}
|
||||
if (!this._enterDragMode()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < this._dragSources.length; i++) {
|
||||
this._dragSources[i].setLayoutOffset(this._dragSources[i].getLayoutOffset().offset(movement));
|
||||
minder.applyLayoutResult(this._dragSources[i]);
|
||||
}
|
||||
|
||||
if (!this._dropTest()) {
|
||||
this._orderTest();
|
||||
} else {
|
||||
this._renderOrderHint((this._orderSucceedHint = null));
|
||||
}
|
||||
},
|
||||
|
||||
dragEnd: function () {
|
||||
this._startPosition = null;
|
||||
this._dragPosition = null;
|
||||
|
||||
if (!this._dragMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._fadeDragSources(1);
|
||||
|
||||
if (this._dropSucceedTarget) {
|
||||
this._dragSources.forEach(function (source) {
|
||||
source.setLayoutOffset(null);
|
||||
});
|
||||
|
||||
this._minder.layout(-1);
|
||||
|
||||
this._minder.execCommand('movetoparent', this._dragSources, this._dropSucceedTarget);
|
||||
} else if (this._orderSucceedHint) {
|
||||
var hint = this._orderSucceedHint;
|
||||
var index = hint.node.getIndex();
|
||||
|
||||
var sourceIndexes = this._dragSources.map(function (source) {
|
||||
// 顺便干掉布局偏移
|
||||
source.setLayoutOffset(null);
|
||||
return source.getIndex();
|
||||
});
|
||||
|
||||
var maxIndex = Math.max.apply(Math, sourceIndexes);
|
||||
var minIndex = Math.min.apply(Math, sourceIndexes);
|
||||
|
||||
if (index < minIndex && hint.type == 'down') index++;
|
||||
if (index > maxIndex && hint.type == 'up') index--;
|
||||
|
||||
hint.node.setLayoutOffset(null);
|
||||
|
||||
this._minder.execCommand('arrange', index);
|
||||
this._renderOrderHint(null);
|
||||
} else {
|
||||
this._minder.fire('savescene');
|
||||
}
|
||||
this._minder.layout(300);
|
||||
this._leaveDragMode();
|
||||
this._minder.fire('contentchange');
|
||||
},
|
||||
|
||||
// 进入拖放模式:
|
||||
// 1. 计算拖放源和允许的拖放目标
|
||||
// 2. 标记已启动
|
||||
_enterDragMode: function () {
|
||||
this._calcDragSources();
|
||||
if (!this._dragSources.length) {
|
||||
this._startPosition = null;
|
||||
return false;
|
||||
}
|
||||
this._fadeDragSources(0.5);
|
||||
this._calcDropTargets();
|
||||
this._calcOrderHints();
|
||||
this._dragMode = true;
|
||||
this._minder.setStatus('dragtree');
|
||||
return true;
|
||||
},
|
||||
|
||||
// 从选中的节点计算拖放源
|
||||
// 并不是所有选中的节点都作为拖放源,如果选中节点中存在 A 和 B,
|
||||
// 并且 A 是 B 的祖先,则 B 不作为拖放源
|
||||
//
|
||||
// 计算过程:
|
||||
// 1. 将节点按照树高排序,排序后只可能是前面节点是后面节点的祖先
|
||||
// 2. 从后往前枚举排序的结果,如果发现枚举目标之前存在其祖先,
|
||||
// 则排除枚举目标作为拖放源,否则加入拖放源
|
||||
_calcDragSources: function () {
|
||||
this._dragSources = this._minder.getSelectedAncestors();
|
||||
},
|
||||
|
||||
_fadeDragSources: function (opacity) {
|
||||
var minder = this._minder;
|
||||
this._dragSources.forEach(function (source) {
|
||||
source.getRenderContainer().setOpacity(opacity, 200);
|
||||
source.traverse(function (node) {
|
||||
if (opacity < 1) {
|
||||
minder.detachNode(node);
|
||||
} else {
|
||||
minder.attachNode(node);
|
||||
}
|
||||
}, true);
|
||||
});
|
||||
},
|
||||
|
||||
// 计算拖放目标可以释放的节点列表(释放意味着成为其子树),存在这条限制规则:
|
||||
// - 不能拖放到拖放目标的子树上(允许拖放到自身,因为多选的情况下可以把其它节点加入)
|
||||
//
|
||||
// 1. 加入当前节点(初始为根节点)到允许列表
|
||||
// 2. 对于当前节点的每一个子节点:
|
||||
// (1) 如果是拖放目标的其中一个节点,忽略(整棵子树被剪枝)
|
||||
// (2) 如果不是拖放目标之一,以当前子节点为当前节点,回到 1 计算
|
||||
// 3. 返回允许列表
|
||||
//
|
||||
_calcDropTargets: function () {
|
||||
function findAvailableParents(nodes, root) {
|
||||
var availables = [],
|
||||
i;
|
||||
availables.push(root);
|
||||
root.getChildren().forEach(function (test) {
|
||||
for (i = 0; i < nodes.length; i++) {
|
||||
if (nodes[i] == test) return;
|
||||
}
|
||||
availables = availables.concat(findAvailableParents(nodes, test));
|
||||
});
|
||||
return availables;
|
||||
}
|
||||
|
||||
this._dropTargets = findAvailableParents(this._dragSources, this._minder.getRoot());
|
||||
this._dropTargetBoxes = this._dropTargets.map(function (source) {
|
||||
return source.getLayoutBox();
|
||||
});
|
||||
},
|
||||
|
||||
_calcOrderHints: function () {
|
||||
var sources = this._dragSources;
|
||||
var ancestor = MinderNode.getCommonAncestor(sources);
|
||||
|
||||
// 只有一个元素选中,公共祖先是其父
|
||||
if (ancestor == sources[0]) ancestor = sources[0].parent;
|
||||
|
||||
if (sources.length === 0 || ancestor != sources[0].parent) {
|
||||
this._orderHints = [];
|
||||
return;
|
||||
}
|
||||
|
||||
var siblings = ancestor.children;
|
||||
|
||||
this._orderHints = siblings.reduce(function (hint, sibling) {
|
||||
if (sources.indexOf(sibling) == -1) {
|
||||
hint = hint.concat(sibling.getOrderHint());
|
||||
}
|
||||
return hint;
|
||||
}, []);
|
||||
},
|
||||
|
||||
_leaveDragMode: function () {
|
||||
this._dragMode = false;
|
||||
this._dropSucceedTarget = null;
|
||||
this._orderSucceedHint = null;
|
||||
this._renderDropHint(null);
|
||||
this._renderOrderHint(null);
|
||||
this._minder.rollbackStatus();
|
||||
},
|
||||
|
||||
_drawForDragMode: function () {
|
||||
this._text.setContent(this._dragSources.length + ' items');
|
||||
this._text.setPosition(this._startPosition.x, this._startPosition.y + 5);
|
||||
this._minder.getRenderContainer().addShape(this);
|
||||
},
|
||||
|
||||
/**
|
||||
* 通过 judge 函数判断 targetBox 和 sourceBox 的位置交叉关系
|
||||
* @param targets -- 目标节点
|
||||
* @param targetBoxMapper -- 目标节点与对应 Box 的映射关系
|
||||
* @param judge -- 判断函数
|
||||
* @returns {*}
|
||||
* @private
|
||||
*/
|
||||
_boxTest: function (targets, targetBoxMapper, judge) {
|
||||
var sourceBoxes = this._dragSources.map(function (source) {
|
||||
return source.getLayoutBox();
|
||||
});
|
||||
|
||||
var i, j, target, sourceBox, targetBox;
|
||||
|
||||
judge =
|
||||
judge ||
|
||||
function (intersectBox, sourceBox, targetBox) {
|
||||
return intersectBox && !intersectBox.isEmpty();
|
||||
};
|
||||
|
||||
for (i = 0; i < targets.length; i++) {
|
||||
target = targets[i];
|
||||
targetBox = targetBoxMapper.call(this, target, i);
|
||||
|
||||
for (j = 0; j < sourceBoxes.length; j++) {
|
||||
sourceBox = sourceBoxes[j];
|
||||
|
||||
var intersectBox = sourceBox.intersect(targetBox);
|
||||
if (judge(intersectBox, sourceBox, targetBox)) {
|
||||
return target;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
_dropTest: function () {
|
||||
this._dropSucceedTarget = this._boxTest(
|
||||
this._dropTargets,
|
||||
function (target, i) {
|
||||
return this._dropTargetBoxes[i];
|
||||
},
|
||||
function (intersectBox, sourceBox, targetBox) {
|
||||
function area(box) {
|
||||
return box.width * box.height;
|
||||
}
|
||||
if (!intersectBox) return false;
|
||||
/*
|
||||
* Added by zhangbobell, 2015.9.8
|
||||
*
|
||||
* 增加了下面一行判断,修复了循环比较中 targetBox 为折叠节点时,intersetBox 面积为 0,
|
||||
* 而 targetBox 的 width 和 height 均为 0
|
||||
* 此时造成了满足以下的第二个条件而返回 true
|
||||
* */
|
||||
if (!area(intersectBox)) return false;
|
||||
// 面积判断,交叉面积大于其中的一半
|
||||
if (area(intersectBox) > 0.5 * Math.min(area(sourceBox), area(targetBox))) return true;
|
||||
// 有一个边完全重合的情况,也认为两个是交叉的
|
||||
if (intersectBox.width + 1 >= Math.min(sourceBox.width, targetBox.width)) return true;
|
||||
if (intersectBox.height + 1 >= Math.min(sourceBox.height, targetBox.height)) return true;
|
||||
return false;
|
||||
}
|
||||
);
|
||||
this._renderDropHint(this._dropSucceedTarget);
|
||||
return !!this._dropSucceedTarget;
|
||||
},
|
||||
|
||||
_orderTest: function () {
|
||||
this._orderSucceedHint = this._boxTest(this._orderHints, function (hint) {
|
||||
return hint.area;
|
||||
});
|
||||
this._renderOrderHint(this._orderSucceedHint);
|
||||
return !!this._orderSucceedHint;
|
||||
},
|
||||
|
||||
_renderDropHint: function (target) {
|
||||
this._dropHinter.render(target);
|
||||
},
|
||||
|
||||
_renderOrderHint: function (hint) {
|
||||
this._orderHinter.render(hint);
|
||||
},
|
||||
preventDragMove: function () {
|
||||
this._startPosition = null;
|
||||
},
|
||||
});
|
||||
|
||||
Module.register('DragTree', function () {
|
||||
var dragger;
|
||||
|
||||
return {
|
||||
init: function () {
|
||||
dragger = new TreeDragger(this);
|
||||
window.addEventListener('mouseup', function () {
|
||||
dragger.dragEnd();
|
||||
});
|
||||
},
|
||||
events: {
|
||||
'normal.mousedown inputready.mousedown': function (e) {
|
||||
// 单选中根节点也不触发拖拽
|
||||
if (e.originEvent.button) return;
|
||||
if (e.getTargetNode() && e.getTargetNode() != this.getRoot()) {
|
||||
dragger.dragStart(e.getPosition());
|
||||
}
|
||||
},
|
||||
'normal.mousemove dragtree.mousemove': function (e) {
|
||||
dragger.dragMove(e.getPosition());
|
||||
},
|
||||
'normal.mouseup dragtree.beforemouseup': function (e) {
|
||||
dragger.dragEnd();
|
||||
//e.stopPropagation();
|
||||
e.preventDefault();
|
||||
},
|
||||
'statuschange': function (e) {
|
||||
if (e.lastStatus == 'textedit' && e.currentStatus == 'normal') {
|
||||
dragger.preventDragMove();
|
||||
}
|
||||
},
|
||||
},
|
||||
commands: {
|
||||
movetoparent: MoveToParentCommand,
|
||||
},
|
||||
};
|
||||
});
|
||||
});
|
|
@ -0,0 +1,299 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('../core/kity');
|
||||
var utils = require('../core/utils');
|
||||
var keymap = require('../core/keymap');
|
||||
var MinderNode = require('../core/node');
|
||||
var Command = require('../core/command');
|
||||
var Module = require('../core/module');
|
||||
var Renderer = require('../core/render');
|
||||
|
||||
Module.register('Expand', function () {
|
||||
var minder = this;
|
||||
var EXPAND_STATE_DATA = 'expandState',
|
||||
STATE_EXPAND = 'expand',
|
||||
STATE_COLLAPSE = 'collapse';
|
||||
|
||||
// 将展开的操作和状态读取接口拓展到 MinderNode 上
|
||||
kity.extendClass(MinderNode, {
|
||||
/**
|
||||
* 展开节点
|
||||
* @param {Policy} policy 展开的策略,默认为 KEEP_STATE
|
||||
*/
|
||||
expand: function () {
|
||||
this.setData(EXPAND_STATE_DATA, STATE_EXPAND);
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* 收起节点
|
||||
*/
|
||||
collapse: function () {
|
||||
this.setData(EXPAND_STATE_DATA, STATE_COLLAPSE);
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* 判断节点当前的状态是否为展开
|
||||
*/
|
||||
isExpanded: function () {
|
||||
var expanded = this.getData(EXPAND_STATE_DATA) !== STATE_COLLAPSE;
|
||||
return expanded && (this.isRoot() || this.parent.isExpanded());
|
||||
},
|
||||
|
||||
/**
|
||||
* 判断节点当前的状态是否为收起
|
||||
*/
|
||||
isCollapsed: function () {
|
||||
return !this.isExpanded();
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* @command Expand
|
||||
* @description 展开当前选中的节点,保证其可见
|
||||
* @param {bool} justParents 是否只展开到父亲
|
||||
* * `false` - (默认)保证选中的节点以及其子树可见
|
||||
* * `true` - 只保证选中的节点可见,不展开其子树
|
||||
* @state
|
||||
* 0: 当前有选中的节点
|
||||
* -1: 当前没有选中的节点
|
||||
*/
|
||||
var ExpandCommand = kity.createClass('ExpandCommand', {
|
||||
base: Command,
|
||||
|
||||
execute: function (km, justParents) {
|
||||
var node = km.getSelectedNode();
|
||||
if (!node) return;
|
||||
if (justParents) {
|
||||
node = node.parent;
|
||||
}
|
||||
while (node.parent) {
|
||||
node.expand();
|
||||
node = node.parent;
|
||||
}
|
||||
node.renderTree();
|
||||
km.layout(100);
|
||||
},
|
||||
|
||||
queryState: function (km) {
|
||||
var node = km.getSelectedNode();
|
||||
return node && !node.isRoot() && !node.isExpanded() ? 0 : -1;
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* @command ExpandToLevel
|
||||
* @description 展开脑图到指定的层级
|
||||
* @param {number} level 指定展开到的层级,最少值为 1。
|
||||
* @state
|
||||
* 0: 一直可用
|
||||
*/
|
||||
var ExpandToLevelCommand = kity.createClass('ExpandToLevelCommand', {
|
||||
base: Command,
|
||||
execute: function (km, level) {
|
||||
km.getRoot().traverse(function (node) {
|
||||
if (node.getLevel() < level) node.expand();
|
||||
if (node.getLevel() == level && !node.isLeaf()) node.collapse();
|
||||
});
|
||||
km.refresh(100);
|
||||
},
|
||||
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,
|
||||
|
||||
constructor: function (node) {
|
||||
this.callBase();
|
||||
this.radius = 6;
|
||||
this.outline = new kity.Circle(this.radius).stroke('gray').fill('white');
|
||||
this.sign = new kity.Path().stroke('gray');
|
||||
this.addShapes([this.outline, this.sign]);
|
||||
this.initEvent(node);
|
||||
this.setId(utils.uuid('node_expander'));
|
||||
this.setStyle('cursor', 'pointer');
|
||||
},
|
||||
|
||||
initEvent: function (node) {
|
||||
this.on('mousedown', function (e) {
|
||||
minder.select([node], true);
|
||||
if (node.isExpanded()) {
|
||||
node.collapse();
|
||||
} else {
|
||||
node.expand();
|
||||
}
|
||||
node.renderTree().getMinder().layout(100);
|
||||
node.getMinder().fire('contentchange');
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
});
|
||||
this.on('dblclick click mouseup', function (e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
});
|
||||
},
|
||||
|
||||
setState: function (state) {
|
||||
if (state == 'hide') {
|
||||
this.setVisible(false);
|
||||
return;
|
||||
}
|
||||
this.setVisible(true);
|
||||
var pathData = ['M', 1.5 - this.radius, 0, 'L', this.radius - 1.5, 0];
|
||||
if (state == STATE_COLLAPSE) {
|
||||
pathData.push(['M', 0, 1.5 - this.radius, 'L', 0, this.radius - 1.5]);
|
||||
}
|
||||
this.sign.setPathData(pathData);
|
||||
},
|
||||
});
|
||||
|
||||
var ExpanderRenderer = kity.createClass('ExpanderRenderer', {
|
||||
base: Renderer,
|
||||
|
||||
create: function (node) {
|
||||
if (node.isRoot()) return;
|
||||
this.expander = new Expander(node);
|
||||
node.getRenderContainer().prependShape(this.expander);
|
||||
node.expanderRenderer = this;
|
||||
this.node = node;
|
||||
return this.expander;
|
||||
},
|
||||
|
||||
shouldRender: function (node) {
|
||||
return !node.isRoot();
|
||||
},
|
||||
|
||||
update: function (expander, node, box) {
|
||||
if (!node.parent) return;
|
||||
|
||||
var visible = node.parent.isExpanded();
|
||||
|
||||
expander.setState(visible && node.children.length ? node.getData(EXPAND_STATE_DATA) : 'hide');
|
||||
|
||||
var vector = node.getLayoutVectorIn().normalize(expander.radius + node.getStyle('stroke-width'));
|
||||
var position = node.getVertexIn().offset(vector.reverse());
|
||||
|
||||
this.expander.setTranslate(position);
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
commands: {
|
||||
expand: ExpandCommand,
|
||||
expandtolevel: ExpandToLevelCommand,
|
||||
collapse: CollapseCommand,
|
||||
},
|
||||
events: {
|
||||
'layoutapply': function (e) {
|
||||
var r = e.node.getRenderer('ExpanderRenderer');
|
||||
if (r.getRenderShape()) {
|
||||
r.update(r.getRenderShape(), e.node);
|
||||
}
|
||||
},
|
||||
'beforerender': function (e) {
|
||||
var node = e.node;
|
||||
var visible = !node.parent || node.parent.isExpanded();
|
||||
var minder = this;
|
||||
|
||||
node.getRenderContainer().setVisible(visible);
|
||||
if (!visible) e.stopPropagation();
|
||||
},
|
||||
'normal.keydown': function (e) {
|
||||
if (this.getStatus() == 'textedit') return;
|
||||
if (e.originEvent.keyCode == keymap['/']) {
|
||||
var node = this.getSelectedNode();
|
||||
if (!node || node == this.getRoot()) return;
|
||||
var expanded = node.isExpanded();
|
||||
this.getSelectedNodes().forEach(function (node) {
|
||||
if (expanded) node.collapse();
|
||||
else node.expand();
|
||||
node.renderTree();
|
||||
});
|
||||
this.layout(100);
|
||||
this.fire('contentchange');
|
||||
e.preventDefault();
|
||||
e.stopPropagationImmediately();
|
||||
}
|
||||
if (e.isShortcutKey('Alt+`')) {
|
||||
this.execCommand('expandtolevel', 9999);
|
||||
}
|
||||
for (var i = 1; i < 6; i++) {
|
||||
if (e.isShortcutKey('Alt+' + i)) {
|
||||
this.execCommand('expandtolevel', i);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
renderers: {
|
||||
outside: ExpanderRenderer,
|
||||
},
|
||||
contextmenu: [
|
||||
{
|
||||
command: 'expandtoleaf',
|
||||
query: function () {
|
||||
return !minder.getSelectedNode();
|
||||
},
|
||||
fn: function (minder) {
|
||||
minder.execCommand('expandtolevel', 9999);
|
||||
},
|
||||
},
|
||||
{
|
||||
command: 'expandtolevel1',
|
||||
query: function () {
|
||||
return !minder.getSelectedNode();
|
||||
},
|
||||
fn: function (minder) {
|
||||
minder.execCommand('expandtolevel', 1);
|
||||
},
|
||||
},
|
||||
{
|
||||
command: 'expandtolevel2',
|
||||
query: function () {
|
||||
return !minder.getSelectedNode();
|
||||
},
|
||||
fn: function (minder) {
|
||||
minder.execCommand('expandtolevel', 2);
|
||||
},
|
||||
},
|
||||
{
|
||||
command: 'expandtolevel3',
|
||||
query: function () {
|
||||
return !minder.getSelectedNode();
|
||||
},
|
||||
fn: function (minder) {
|
||||
minder.execCommand('expandtolevel', 3);
|
||||
},
|
||||
},
|
||||
{
|
||||
divider: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
});
|
|
@ -0,0 +1,158 @@
|
|||
/* eslint-disable */
|
||||
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');
|
||||
|
||||
var TextRenderer = require('./text');
|
||||
|
||||
function getNodeDataOrStyle(node, name) {
|
||||
return node.getData(name) || node.getStyle(name);
|
||||
}
|
||||
|
||||
TextRenderer.registerStyleHook(function (node, textGroup) {
|
||||
var dataColor = node.getData('color');
|
||||
var selectedColor = node.getStyle('selected-color');
|
||||
var styleColor = node.getStyle('color');
|
||||
|
||||
var foreColor = dataColor || (node.isSelected() && selectedColor ? selectedColor : styleColor);
|
||||
var fontFamily = getNodeDataOrStyle(node, 'font-family');
|
||||
var fontSize = getNodeDataOrStyle(node, 'font-size');
|
||||
|
||||
textGroup.fill(foreColor);
|
||||
|
||||
textGroup.eachItem(function (index, item) {
|
||||
item.setFont({
|
||||
family: fontFamily,
|
||||
size: fontSize,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Module.register('fontmodule', {
|
||||
commands: {
|
||||
/**
|
||||
* @command ForeColor
|
||||
* @description 设置选中节点的字体颜色
|
||||
* @param {string} color 表示颜色的字符串
|
||||
* @state
|
||||
* 0: 当前有选中的节点
|
||||
* -1: 当前没有选中的节点
|
||||
* @return 如果只有一个节点选中,返回已选中节点的字体颜色;否则返回 'mixed'。
|
||||
*/
|
||||
forecolor: kity.createClass('fontcolorCommand', {
|
||||
base: Command,
|
||||
execute: function (km, color) {
|
||||
var nodes = km.getSelectedNodes();
|
||||
nodes.forEach(function (n) {
|
||||
n.setData('color', color);
|
||||
n.render();
|
||||
});
|
||||
},
|
||||
queryState: function (km) {
|
||||
return km.getSelectedNodes().length === 0 ? -1 : 0;
|
||||
},
|
||||
queryValue: function (km) {
|
||||
if (km.getSelectedNodes().length == 1) {
|
||||
return km.getSelectedNodes()[0].getData('color');
|
||||
}
|
||||
return 'mixed';
|
||||
},
|
||||
}),
|
||||
|
||||
/**
|
||||
* @command Background
|
||||
* @description 设置选中节点的背景颜色
|
||||
* @param {string} color 表示颜色的字符串
|
||||
* @state
|
||||
* 0: 当前有选中的节点
|
||||
* -1: 当前没有选中的节点
|
||||
* @return 如果只有一个节点选中,返回已选中节点的背景颜色;否则返回 'mixed'。
|
||||
*/
|
||||
background: kity.createClass('backgroudCommand', {
|
||||
base: Command,
|
||||
|
||||
execute: function (km, color) {
|
||||
var nodes = km.getSelectedNodes();
|
||||
nodes.forEach(function (n) {
|
||||
n.setData('background', color);
|
||||
n.render();
|
||||
});
|
||||
},
|
||||
queryState: function (km) {
|
||||
return km.getSelectedNodes().length === 0 ? -1 : 0;
|
||||
},
|
||||
queryValue: function (km) {
|
||||
if (km.getSelectedNodes().length == 1) {
|
||||
return km.getSelectedNodes()[0].getData('background');
|
||||
}
|
||||
return 'mixed';
|
||||
},
|
||||
}),
|
||||
|
||||
/**
|
||||
* @command FontFamily
|
||||
* @description 设置选中节点的字体
|
||||
* @param {string} family 表示字体的字符串
|
||||
* @state
|
||||
* 0: 当前有选中的节点
|
||||
* -1: 当前没有选中的节点
|
||||
* @return 返回首个选中节点的字体
|
||||
*/
|
||||
fontfamily: kity.createClass('fontfamilyCommand', {
|
||||
base: Command,
|
||||
|
||||
execute: function (km, family) {
|
||||
var nodes = km.getSelectedNodes();
|
||||
nodes.forEach(function (n) {
|
||||
n.setData('font-family', family);
|
||||
n.render();
|
||||
km.layout();
|
||||
});
|
||||
},
|
||||
queryState: function (km) {
|
||||
return km.getSelectedNodes().length === 0 ? -1 : 0;
|
||||
},
|
||||
queryValue: function (km) {
|
||||
var node = km.getSelectedNode();
|
||||
if (node) return node.getData('font-family');
|
||||
return null;
|
||||
},
|
||||
}),
|
||||
|
||||
/**
|
||||
* @command FontSize
|
||||
* @description 设置选中节点的字体大小
|
||||
* @param {number} size 字体大小(px)
|
||||
* @state
|
||||
* 0: 当前有选中的节点
|
||||
* -1: 当前没有选中的节点
|
||||
* @return 返回首个选中节点的字体大小
|
||||
*/
|
||||
fontsize: kity.createClass('fontsizeCommand', {
|
||||
base: Command,
|
||||
|
||||
execute: function (km, size) {
|
||||
var nodes = km.getSelectedNodes();
|
||||
nodes.forEach(function (n) {
|
||||
n.setData('font-size', size);
|
||||
n.render();
|
||||
km.layout(300);
|
||||
});
|
||||
},
|
||||
queryState: function (km) {
|
||||
return km.getSelectedNodes().length === 0 ? -1 : 0;
|
||||
},
|
||||
queryValue: function (km) {
|
||||
var node = km.getSelectedNode();
|
||||
if (node) return node.getData('font-size');
|
||||
return null;
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
});
|
|
@ -0,0 +1,127 @@
|
|||
/* eslint-disable */
|
||||
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');
|
||||
var Renderer = require('../core/render');
|
||||
|
||||
// jscs:disable maximumLineLength
|
||||
var linkShapePath =
|
||||
'M16.614,10.224h-1.278c-1.668,0-3.07-1.07-3.599-2.556h4.877c0.707,0,1.278-0.571,1.278-1.278V3.834 c0-0.707-0.571-1.278-1.278-1.278h-4.877C12.266,1.071,13.668,0,15.336,0h1.278c2.116,0,3.834,1.716,3.834,3.834V6.39 C20.448,8.508,18.73,10.224,16.614,10.224z M5.112,5.112c0-0.707,0.573-1.278,1.278-1.278h7.668c0.707,0,1.278,0.571,1.278,1.278 S14.765,6.39,14.058,6.39H6.39C5.685,6.39,5.112,5.819,5.112,5.112z M2.556,3.834V6.39c0,0.707,0.573,1.278,1.278,1.278h4.877 c-0.528,1.486-1.932,2.556-3.599,2.556H3.834C1.716,10.224,0,8.508,0,6.39V3.834C0,1.716,1.716,0,3.834,0h1.278 c1.667,0,3.071,1.071,3.599,2.556H3.834C3.129,2.556,2.556,3.127,2.556,3.834z';
|
||||
|
||||
Module.register('hyperlink', {
|
||||
commands: {
|
||||
/**
|
||||
* @command HyperLink
|
||||
* @description 为选中的节点添加超链接
|
||||
* @param {string} url 超链接的 URL,设置为 null 移除
|
||||
* @param {string} title 超链接的说明
|
||||
* @state
|
||||
* 0: 当前有选中的节点
|
||||
* -1: 当前没有选中的节点
|
||||
* @return 返回首个选中节点的超链接信息,JSON 对象: `{url: url, title: title}`
|
||||
*/
|
||||
hyperlink: kity.createClass('hyperlink', {
|
||||
base: Command,
|
||||
|
||||
execute: function (km, url, title) {
|
||||
var nodes = km.getSelectedNodes();
|
||||
nodes.forEach(function (n) {
|
||||
n.setData('hyperlink', url);
|
||||
n.setData('hyperlinkTitle', url && title);
|
||||
n.render();
|
||||
});
|
||||
km.layout();
|
||||
},
|
||||
queryState: function (km) {
|
||||
var nodes = km.getSelectedNodes(),
|
||||
result = 0;
|
||||
if (nodes.length === 0) {
|
||||
return -1;
|
||||
}
|
||||
nodes.forEach(function (n) {
|
||||
if (n && n.getData('hyperlink')) {
|
||||
result = 0;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
},
|
||||
queryValue: function (km) {
|
||||
var node = km.getSelectedNode();
|
||||
return {
|
||||
url: node.getData('hyperlink'),
|
||||
title: node.getData('hyperlinkTitle'),
|
||||
};
|
||||
},
|
||||
}),
|
||||
},
|
||||
renderers: {
|
||||
right: kity.createClass('hyperlinkrender', {
|
||||
base: Renderer,
|
||||
|
||||
create: function () {
|
||||
var link = new kity.HyperLink();
|
||||
var linkshape = new kity.Path();
|
||||
var outline = new kity.Rect(24, 22, -2, -6, 4).fill('rgba(255, 255, 255, 0)');
|
||||
|
||||
linkshape.setPathData(linkShapePath).fill('#666');
|
||||
link.addShape(outline);
|
||||
link.addShape(linkshape);
|
||||
link.setTarget('_blank');
|
||||
link.setStyle('cursor', 'pointer');
|
||||
|
||||
link
|
||||
.on('mouseover', function () {
|
||||
outline.fill('rgba(255, 255, 200, .8)');
|
||||
})
|
||||
.on('mouseout', function () {
|
||||
outline.fill('rgba(255, 255, 255, 0)');
|
||||
});
|
||||
return link;
|
||||
},
|
||||
|
||||
shouldRender: function (node) {
|
||||
return node.getData('hyperlink');
|
||||
},
|
||||
|
||||
update: function (link, node, box) {
|
||||
var href = node.getData('hyperlink');
|
||||
link.setHref('#');
|
||||
|
||||
var allowed = ['^http:', '^https:', '^ftp:', '^mailto:'];
|
||||
for (var i = 0; i < allowed.length; i++) {
|
||||
var regex = new RegExp(allowed[i]);
|
||||
if (regex.test(href)) {
|
||||
link.setHref(href);
|
||||
break;
|
||||
}
|
||||
}
|
||||
var title = node.getData('hyperlinkTitle');
|
||||
|
||||
if (title) {
|
||||
title = [title, '(', href, ')'].join('');
|
||||
} else {
|
||||
title = href;
|
||||
}
|
||||
|
||||
link.node.setAttributeNS('http://www.w3.org/1999/xlink', 'title', title);
|
||||
|
||||
var spaceRight = node.getStyle('space-right');
|
||||
|
||||
link.setTranslate(box.right + spaceRight + 2, -5);
|
||||
return new kity.Box({
|
||||
x: box.right + spaceRight,
|
||||
y: -11,
|
||||
width: 24,
|
||||
height: 22,
|
||||
});
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
});
|
|
@ -0,0 +1,113 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('../core/kity');
|
||||
var keymap = require('../core/keymap');
|
||||
|
||||
var Module = require('../core/module');
|
||||
var Command = require('../core/command');
|
||||
|
||||
Module.register('ImageViewer', function () {
|
||||
function createEl(name, classNames, children) {
|
||||
var el = document.createElement(name);
|
||||
addClass(el, classNames);
|
||||
children &&
|
||||
children.length &&
|
||||
children.forEach(function (child) {
|
||||
el.appendChild(child);
|
||||
});
|
||||
return el;
|
||||
}
|
||||
|
||||
function on(el, event, handler) {
|
||||
el.addEventListener(event, handler);
|
||||
}
|
||||
|
||||
function addClass(el, classNames) {
|
||||
classNames &&
|
||||
classNames.split(' ').forEach(function (className) {
|
||||
el.classList.add(className);
|
||||
});
|
||||
}
|
||||
|
||||
function removeClass(el, classNames) {
|
||||
classNames &&
|
||||
classNames.split(' ').forEach(function (className) {
|
||||
el.classList.remove(className);
|
||||
});
|
||||
}
|
||||
|
||||
var ImageViewer = kity.createClass('ImageViewer', {
|
||||
constructor: function () {
|
||||
var btnClose = createEl('button', 'km-image-viewer-btn km-image-viewer-close');
|
||||
var btnSource = createEl('button', 'km-image-viewer-btn km-image-viewer-source');
|
||||
var image = (this.image = createEl('img'));
|
||||
var toolbar = (this.toolbar = createEl('div', 'km-image-viewer-toolbar', [btnSource, btnClose]));
|
||||
var container = createEl('div', 'km-image-viewer-container', [image]);
|
||||
var viewer = (this.viewer = createEl('div', 'km-image-viewer', [toolbar, container]));
|
||||
this.hotkeyHandler = this.hotkeyHandler.bind(this);
|
||||
on(btnClose, 'click', this.close.bind(this));
|
||||
on(btnSource, 'click', this.viewSource.bind(this));
|
||||
on(image, 'click', this.zoomImage.bind(this));
|
||||
on(viewer, 'contextmenu', this.toggleToolbar.bind(this));
|
||||
on(document, 'keydown', this.hotkeyHandler);
|
||||
},
|
||||
dispose: function () {
|
||||
this.close();
|
||||
document.removeEventListener('remove', this.hotkeyHandler);
|
||||
},
|
||||
hotkeyHandler: function (e) {
|
||||
if (!this.actived) {
|
||||
return;
|
||||
}
|
||||
if (e.keyCode === keymap['esc']) {
|
||||
this.close();
|
||||
}
|
||||
},
|
||||
toggleToolbar: function (e) {
|
||||
e && e.preventDefault();
|
||||
this.toolbar.classList.toggle('hidden');
|
||||
},
|
||||
zoomImage: function (restore) {
|
||||
var image = this.image;
|
||||
if (typeof restore === 'boolean') {
|
||||
restore && addClass(image, 'limited');
|
||||
} else {
|
||||
image.classList.toggle('limited');
|
||||
}
|
||||
},
|
||||
viewSource: function (src) {
|
||||
window.open(this.image.src);
|
||||
},
|
||||
open: function (src) {
|
||||
var input = document.querySelector('input');
|
||||
if (input) {
|
||||
input.focus();
|
||||
input.blur();
|
||||
}
|
||||
this.image.src = src;
|
||||
this.zoomImage(true);
|
||||
document.body.appendChild(this.viewer);
|
||||
this.actived = true;
|
||||
},
|
||||
close: function () {
|
||||
this.image.src = '';
|
||||
document.body.removeChild(this.viewer);
|
||||
this.actived = false;
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
init: function () {
|
||||
this.viewer = new ImageViewer();
|
||||
},
|
||||
events: {
|
||||
'normal.dblclick': function (e) {
|
||||
var shape = e.kityEvent.targetShape;
|
||||
if (shape.__KityClassName === 'Image' && shape.url) {
|
||||
this.viewer.open(shape.url);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
});
|
|
@ -0,0 +1,144 @@
|
|||
/* eslint-disable */
|
||||
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');
|
||||
var Renderer = require('../core/render');
|
||||
|
||||
Module.register('image', function () {
|
||||
function loadImageSize(url, callback) {
|
||||
var img = document.createElement('img');
|
||||
img.onload = function () {
|
||||
callback(img.width, img.height);
|
||||
};
|
||||
img.onerror = function () {
|
||||
callback(null);
|
||||
};
|
||||
img.src = url;
|
||||
}
|
||||
|
||||
function fitImageSize(width, height, maxWidth, maxHeight) {
|
||||
var ratio = width / height,
|
||||
fitRatio = maxWidth / maxHeight;
|
||||
|
||||
// 宽高比大于最大尺寸的宽高比,以宽度为标准适应
|
||||
if (width > maxWidth && ratio > fitRatio) {
|
||||
width = maxWidth;
|
||||
height = width / ratio;
|
||||
} else if (height > maxHeight) {
|
||||
height = maxHeight;
|
||||
width = height * ratio;
|
||||
}
|
||||
|
||||
return {
|
||||
width: width | 0,
|
||||
height: height | 0,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @command Image
|
||||
* @description 为选中的节点添加图片
|
||||
* @param {string} url 图片的 URL,设置为 null 移除
|
||||
* @param {string} title 图片的说明
|
||||
* @state
|
||||
* 0: 当前有选中的节点
|
||||
* -1: 当前没有选中的节点
|
||||
* @return 返回首个选中节点的图片信息,JSON 对象: `{url: url, title: title}`
|
||||
*/
|
||||
var ImageCommand = kity.createClass('ImageCommand', {
|
||||
base: Command,
|
||||
|
||||
execute: function (km, url, title) {
|
||||
var nodes = km.getSelectedNodes();
|
||||
|
||||
loadImageSize(url, function (width, height) {
|
||||
nodes.forEach(function (n) {
|
||||
var size = fitImageSize(width, height, km.getOption('maxImageWidth'), km.getOption('maxImageHeight'));
|
||||
n.setData('image', url);
|
||||
n.setData('imageTitle', url && title);
|
||||
n.setData('imageSize', url && size);
|
||||
n.render();
|
||||
});
|
||||
km.fire('saveScene');
|
||||
km.layout(300);
|
||||
});
|
||||
},
|
||||
queryState: function (km) {
|
||||
var nodes = km.getSelectedNodes(),
|
||||
result = 0;
|
||||
if (nodes.length === 0) {
|
||||
return -1;
|
||||
}
|
||||
nodes.forEach(function (n) {
|
||||
if (n && n.getData('image')) {
|
||||
result = 0;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
},
|
||||
queryValue: function (km) {
|
||||
var node = km.getSelectedNode();
|
||||
return {
|
||||
url: node.getData('image'),
|
||||
title: node.getData('imageTitle'),
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
var ImageRenderer = kity.createClass('ImageRenderer', {
|
||||
base: Renderer,
|
||||
|
||||
create: function (node) {
|
||||
return new kity.Image(node.getData('image'));
|
||||
},
|
||||
|
||||
shouldRender: function (node) {
|
||||
return node.getData('image');
|
||||
},
|
||||
|
||||
update: function (image, node, box) {
|
||||
var url = node.getData('image');
|
||||
var title = node.getData('imageTitle');
|
||||
var size = node.getData('imageSize');
|
||||
var spaceTop = node.getStyle('space-top');
|
||||
|
||||
if (!size) return;
|
||||
|
||||
if (title) {
|
||||
image.node.setAttributeNS('http://www.w3.org/1999/xlink', 'title', title);
|
||||
}
|
||||
|
||||
var x = box.cx - size.width / 2;
|
||||
var y = box.y - size.height - spaceTop;
|
||||
|
||||
image
|
||||
.setUrl(url)
|
||||
.setX(x | 0)
|
||||
.setY(y | 0)
|
||||
.setWidth(size.width | 0)
|
||||
.setHeight(size.height | 0);
|
||||
|
||||
return new kity.Box(x | 0, y | 0, size.width | 0, size.height | 0);
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
defaultOptions: {
|
||||
maxImageWidth: 200,
|
||||
maxImageHeight: 200,
|
||||
},
|
||||
commands: {
|
||||
image: ImageCommand,
|
||||
},
|
||||
renderers: {
|
||||
top: ImageRenderer,
|
||||
},
|
||||
};
|
||||
});
|
||||
});
|
|
@ -0,0 +1,171 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('../core/kity');
|
||||
var utils = require('../core/utils');
|
||||
var keymap = require('../core/keymap');
|
||||
|
||||
var Minder = require('../core/minder');
|
||||
var MinderNode = require('../core/node');
|
||||
var Command = require('../core/command');
|
||||
var Module = require('../core/module');
|
||||
var Renderer = require('../core/render');
|
||||
|
||||
Module.register('KeyboardModule', function () {
|
||||
var min = Math.min,
|
||||
max = Math.max,
|
||||
abs = Math.abs,
|
||||
sqrt = Math.sqrt,
|
||||
exp = Math.exp;
|
||||
|
||||
function buildPositionNetwork(root) {
|
||||
var pointIndexes = [],
|
||||
p;
|
||||
root.traverse(function (node) {
|
||||
p = node.getLayoutBox();
|
||||
|
||||
// bugfix: 不应导航到收起的节点(判断其尺寸是否存在)
|
||||
if (p.width && p.height) {
|
||||
pointIndexes.push({
|
||||
left: p.x,
|
||||
top: p.y,
|
||||
right: p.x + p.width,
|
||||
bottom: p.y + p.height,
|
||||
width: p.width,
|
||||
height: p.height,
|
||||
node: node,
|
||||
});
|
||||
}
|
||||
});
|
||||
for (var i = 0; i < pointIndexes.length; i++) {
|
||||
findClosestPointsFor(pointIndexes, i);
|
||||
}
|
||||
}
|
||||
|
||||
// 这是金泉的点子,赞!
|
||||
// 求两个不相交矩形的最近距离
|
||||
function getCoefedDistance(box1, box2) {
|
||||
var xMin, xMax, yMin, yMax, xDist, yDist, dist, cx, cy;
|
||||
xMin = min(box1.left, box2.left);
|
||||
xMax = max(box1.right, box2.right);
|
||||
yMin = min(box1.top, box2.top);
|
||||
yMax = max(box1.bottom, box2.bottom);
|
||||
|
||||
xDist = xMax - xMin - box1.width - box2.width;
|
||||
yDist = yMax - yMin - box1.height - box2.height;
|
||||
|
||||
if (xDist < 0) dist = yDist;
|
||||
else if (yDist < 0) dist = xDist;
|
||||
else dist = sqrt(xDist * xDist + yDist * yDist);
|
||||
|
||||
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) {
|
||||
var find = pointIndexes[iFind];
|
||||
var most = {},
|
||||
quad;
|
||||
var current, dist;
|
||||
|
||||
for (var i = 0; i < pointIndexes.length; i++) {
|
||||
if (i == iFind) continue;
|
||||
current = pointIndexes[i];
|
||||
|
||||
dist = getCoefedDistance(current, find);
|
||||
|
||||
// left check
|
||||
if (current.right < find.left) {
|
||||
if (!most.left || dist < most.left.dist) {
|
||||
most.left = {
|
||||
dist: dist,
|
||||
node: current.node,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// right check
|
||||
if (current.left > find.right) {
|
||||
if (!most.right || dist < most.right.dist) {
|
||||
most.right = {
|
||||
dist: dist,
|
||||
node: current.node,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// top check
|
||||
if (current.bottom < find.top) {
|
||||
if (!most.top || dist < most.top.dist) {
|
||||
most.top = {
|
||||
dist: dist,
|
||||
node: current.node,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// bottom check
|
||||
if (current.top > find.bottom) {
|
||||
if (!most.down || dist < most.down.dist) {
|
||||
most.down = {
|
||||
dist: dist,
|
||||
node: current.node,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
find.node._nearestNodes = {
|
||||
right: (most.right && most.right.node) || null,
|
||||
top: (most.top && most.top.node) || null,
|
||||
left: (most.left && most.left.node) || null,
|
||||
down: (most.down && most.down.node) || null,
|
||||
};
|
||||
}
|
||||
|
||||
function navigateTo(km, direction) {
|
||||
var referNode = km.getSelectedNode();
|
||||
if (!referNode) {
|
||||
km.select(km.getRoot());
|
||||
buildPositionNetwork(km.getRoot());
|
||||
return;
|
||||
}
|
||||
if (!referNode._nearestNodes) {
|
||||
buildPositionNetwork(km.getRoot());
|
||||
}
|
||||
var nextNode = referNode._nearestNodes[direction];
|
||||
if (nextNode) {
|
||||
km.select(nextNode, true);
|
||||
}
|
||||
}
|
||||
|
||||
// 稀释用
|
||||
var lastFrame;
|
||||
return {
|
||||
events: {
|
||||
'layoutallfinish': function () {
|
||||
var root = this.getRoot();
|
||||
buildPositionNetwork(root);
|
||||
},
|
||||
'normal.keydown readonly.keydown': function (e) {
|
||||
var minder = this;
|
||||
['left', 'right', 'up', 'down'].forEach(function (key) {
|
||||
if (e.isShortcutKey(key)) {
|
||||
navigateTo(minder, key == 'up' ? 'top' : key);
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
});
|
|
@ -0,0 +1,95 @@
|
|||
/* eslint-disable */
|
||||
/**
|
||||
* @fileOverview
|
||||
*
|
||||
* 布局模块
|
||||
*
|
||||
* @author: techird
|
||||
* @copyright: Baidu FEX, 2014
|
||||
*/
|
||||
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('../core/kity');
|
||||
var Command = require('../core/command');
|
||||
var Module = require('../core/module');
|
||||
|
||||
/**
|
||||
* @command Layout
|
||||
* @description 设置选中节点的布局
|
||||
* 允许使用的布局可以使用 `kityminder.Minder.getLayoutList()` 查询
|
||||
* @param {string} name 布局的名称,设置为 null 则使用继承或默认的布局
|
||||
* @state
|
||||
* 0: 当前有选中的节点
|
||||
* -1: 当前没有选中的节点
|
||||
* @return 返回首个选中节点的布局名称
|
||||
*/
|
||||
var LayoutCommand = kity.createClass('LayoutCommand', {
|
||||
base: Command,
|
||||
|
||||
execute: function (minder, name) {
|
||||
var nodes = minder.getSelectedNodes();
|
||||
nodes.forEach(function (node) {
|
||||
node.layout(name);
|
||||
});
|
||||
},
|
||||
|
||||
queryValue: function (minder) {
|
||||
var node = minder.getSelectedNode();
|
||||
if (node) {
|
||||
return node.getData('layout');
|
||||
}
|
||||
},
|
||||
|
||||
queryState: function (minder) {
|
||||
return minder.getSelectedNode() ? 0 : -1;
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* @command ResetLayout
|
||||
* @description 重设选中节点的布局,如果当前没有选中的节点,重设整个脑图的布局
|
||||
* @state
|
||||
* 0: 始终可用
|
||||
* @return 返回首个选中节点的布局名称
|
||||
*/
|
||||
var ResetLayoutCommand = kity.createClass('ResetLayoutCommand', {
|
||||
base: Command,
|
||||
|
||||
execute: function (minder) {
|
||||
var nodes = minder.getSelectedNodes();
|
||||
|
||||
if (!nodes.length) nodes = [minder.getRoot()];
|
||||
|
||||
nodes.forEach(function (node) {
|
||||
node.traverse(function (child) {
|
||||
child.resetLayoutOffset();
|
||||
if (!child.isRoot()) {
|
||||
child.setData('layout', null);
|
||||
}
|
||||
});
|
||||
});
|
||||
minder.layout(300);
|
||||
},
|
||||
|
||||
enableReadOnly: true,
|
||||
});
|
||||
|
||||
Module.register('LayoutModule', {
|
||||
commands: {
|
||||
layout: LayoutCommand,
|
||||
resetlayout: ResetLayoutCommand,
|
||||
},
|
||||
contextmenu: [
|
||||
{
|
||||
command: 'resetlayout',
|
||||
},
|
||||
{
|
||||
divider: true,
|
||||
},
|
||||
],
|
||||
|
||||
commandShortcutKeys: {
|
||||
resetlayout: 'Ctrl+Shift+L',
|
||||
},
|
||||
});
|
||||
});
|
|
@ -0,0 +1,150 @@
|
|||
/* eslint-disable */
|
||||
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');
|
||||
var Renderer = require('../core/render');
|
||||
|
||||
/**
|
||||
* @command AppendChildNode
|
||||
* @description 添加子节点到选中的节点中
|
||||
* @param {string|object} textOrData 要插入的节点的文本或数据
|
||||
* @state
|
||||
* 0: 当前有选中的节点
|
||||
* -1: 当前没有选中的节点
|
||||
*/
|
||||
var AppendChildCommand = kity.createClass('AppendChildCommand', {
|
||||
base: Command,
|
||||
execute: function (km, text) {
|
||||
var parent = km.getSelectedNode();
|
||||
if (!parent) {
|
||||
return null;
|
||||
}
|
||||
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) {
|
||||
var selectedNode = km.getSelectedNode();
|
||||
return selectedNode ? 0 : -1;
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* @command AppendSiblingNode
|
||||
* @description 添加选中的节点的兄弟节点
|
||||
* @param {string|object} textOrData 要添加的节点的文本或数据
|
||||
* @state
|
||||
* 0: 当前有选中的节点
|
||||
* -1: 当前没有选中的节点
|
||||
*/
|
||||
var AppendSiblingCommand = kity.createClass('AppendSiblingCommand', {
|
||||
base: Command,
|
||||
execute: function (km, text) {
|
||||
var sibling = km.getSelectedNode();
|
||||
var parent = sibling.parent;
|
||||
if (!parent) {
|
||||
return km.execCommand('AppendChildNode', text);
|
||||
}
|
||||
var node = km.createNode(text, parent, sibling.getIndex() + 1);
|
||||
node.setGlobalLayoutTransform(sibling.getGlobalLayoutTransform());
|
||||
km.select(node, true);
|
||||
node.render();
|
||||
km.layout(600);
|
||||
},
|
||||
queryState: function (km) {
|
||||
var selectedNode = km.getSelectedNode();
|
||||
return selectedNode ? 0 : -1;
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* @command RemoveNode
|
||||
* @description 移除选中的节点
|
||||
* @state
|
||||
* 0: 当前有选中的节点
|
||||
* -1: 当前没有选中的节点
|
||||
*/
|
||||
var RemoveNodeCommand = kity.createClass('RemoverNodeCommand', {
|
||||
base: Command,
|
||||
execute: function (km) {
|
||||
var nodes = km.getSelectedNodes();
|
||||
var ancestor = MinderNode.getCommonAncestor.apply(null, nodes);
|
||||
var index = nodes[0].getIndex();
|
||||
|
||||
nodes.forEach(function (node) {
|
||||
if (!node.isRoot()) km.removeNode(node);
|
||||
});
|
||||
if (nodes.length == 1) {
|
||||
var selectBack = ancestor.children[index - 1] || ancestor.children[index];
|
||||
km.select(selectBack || ancestor || km.getRoot(), true);
|
||||
} else {
|
||||
km.select(ancestor || km.getRoot(), true);
|
||||
}
|
||||
km.layout(600);
|
||||
},
|
||||
queryState: function (km) {
|
||||
var selectedNode = km.getSelectedNode();
|
||||
return selectedNode && !selectedNode.isRoot() ? 0 : -1;
|
||||
},
|
||||
});
|
||||
|
||||
var AppendParentCommand = kity.createClass('AppendParentCommand', {
|
||||
base: Command,
|
||||
execute: function (km, text) {
|
||||
var nodes = km.getSelectedNodes();
|
||||
|
||||
nodes.sort(function (a, b) {
|
||||
return a.getIndex() - b.getIndex();
|
||||
});
|
||||
var parent = nodes[0].parent;
|
||||
|
||||
var newParent = km.createNode(text, parent, nodes[0].getIndex());
|
||||
nodes.forEach(function (node) {
|
||||
newParent.appendChild(node);
|
||||
});
|
||||
newParent.setGlobalLayoutTransform(nodes[nodes.length >> 1].getGlobalLayoutTransform());
|
||||
|
||||
km.select(newParent, true);
|
||||
km.layout(600);
|
||||
},
|
||||
queryState: function (km) {
|
||||
var nodes = km.getSelectedNodes();
|
||||
if (!nodes.length) return -1;
|
||||
var parent = nodes[0].parent;
|
||||
if (!parent) return -1;
|
||||
for (var i = 1; i < nodes.length; i++) {
|
||||
if (nodes[i].parent != parent) return -1;
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
});
|
||||
|
||||
Module.register('NodeModule', function () {
|
||||
return {
|
||||
commands: {
|
||||
AppendChildNode: AppendChildCommand,
|
||||
AppendSiblingNode: AppendSiblingCommand,
|
||||
RemoveNode: RemoveNodeCommand,
|
||||
AppendParentNode: AppendParentCommand,
|
||||
},
|
||||
|
||||
commandShortcutKeys: {
|
||||
appendsiblingnode: 'normal::Enter',
|
||||
appendchildnode: 'normal::Insert|Tab',
|
||||
appendparentnode: 'normal::Shift+Tab|normal::Shift+Insert',
|
||||
removenode: 'normal::Del|Backspace',
|
||||
},
|
||||
};
|
||||
});
|
||||
});
|
|
@ -0,0 +1,114 @@
|
|||
/* eslint-disable */
|
||||
/**
|
||||
* @fileOverview
|
||||
*
|
||||
* 支持节点详细信息(HTML)格式
|
||||
*
|
||||
* @author: techird
|
||||
* @copyright: Baidu FEX, 2014
|
||||
*/
|
||||
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');
|
||||
var Renderer = require('../core/render');
|
||||
|
||||
Module.register('NoteModule', function () {
|
||||
var NOTE_PATH = 'M9,9H3V8h6L9,9L9,9z M9,7H3V6h6V7z M9,5H3V4h6V5z M8.5,11H2V2h8v7.5 M9,12l2-2V1H1v11';
|
||||
|
||||
/**
|
||||
* @command Note
|
||||
* @description 设置节点的备注信息
|
||||
* @param {string} note 要设置的备注信息,设置为 null 则移除备注信息
|
||||
* @state
|
||||
* 0: 当前有选中的节点
|
||||
* -1: 当前没有选中的节点
|
||||
*/
|
||||
var NoteCommand = kity.createClass('NoteCommand', {
|
||||
base: Command,
|
||||
|
||||
execute: function (minder, note) {
|
||||
var node = minder.getSelectedNode();
|
||||
node.setData('note', note);
|
||||
node.render();
|
||||
node.getMinder().layout(300);
|
||||
},
|
||||
|
||||
queryState: function (minder) {
|
||||
return minder.getSelectedNodes().length === 1 ? 0 : -1;
|
||||
},
|
||||
|
||||
queryValue: function (minder) {
|
||||
var node = minder.getSelectedNode();
|
||||
return node && node.getData('note');
|
||||
},
|
||||
});
|
||||
|
||||
var NoteIcon = kity.createClass('NoteIcon', {
|
||||
base: kity.Group,
|
||||
|
||||
constructor: function () {
|
||||
this.callBase();
|
||||
this.width = 16;
|
||||
this.height = 17;
|
||||
this.rect = new kity.Rect(16, 17, 0.5, -8.5, 2).fill('transparent');
|
||||
this.path = new kity.Path().setPathData(NOTE_PATH).setTranslate(2.5, -6.5);
|
||||
this.addShapes([this.rect, this.path]);
|
||||
|
||||
this.on('mouseover', function () {
|
||||
this.rect.fill('rgba(255, 255, 200, .8)');
|
||||
}).on('mouseout', function () {
|
||||
this.rect.fill('transparent');
|
||||
});
|
||||
|
||||
this.setStyle('cursor', 'pointer');
|
||||
},
|
||||
});
|
||||
|
||||
var NoteIconRenderer = kity.createClass('NoteIconRenderer', {
|
||||
base: Renderer,
|
||||
|
||||
create: function (node) {
|
||||
var icon = new NoteIcon();
|
||||
icon.on('mousedown', function (e) {
|
||||
e.preventDefault();
|
||||
node.getMinder().fire('editnoterequest');
|
||||
});
|
||||
icon.on('mouseover', function () {
|
||||
node.getMinder().fire('shownoterequest', { node: node, icon: icon });
|
||||
});
|
||||
icon.on('mouseout', function () {
|
||||
node.getMinder().fire('hidenoterequest', { node: node, icon: icon });
|
||||
});
|
||||
return icon;
|
||||
},
|
||||
|
||||
shouldRender: function (node) {
|
||||
return node.getData('note');
|
||||
},
|
||||
|
||||
update: function (icon, node, box) {
|
||||
var x = box.right + node.getStyle('space-left');
|
||||
var y = box.cy;
|
||||
|
||||
icon.path.fill(node.getStyle('color'));
|
||||
icon.setTranslate(x, y);
|
||||
|
||||
return new kity.Box(x, Math.round(y - icon.height / 2), icon.width, icon.height);
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
renderers: {
|
||||
right: NoteIconRenderer,
|
||||
},
|
||||
commands: {
|
||||
note: NoteCommand,
|
||||
},
|
||||
};
|
||||
});
|
||||
});
|
|
@ -0,0 +1,154 @@
|
|||
/* eslint-disable */
|
||||
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');
|
||||
var Renderer = require('../core/render');
|
||||
|
||||
var OutlineRenderer = kity.createClass('OutlineRenderer', {
|
||||
base: Renderer,
|
||||
|
||||
create: function (node) {
|
||||
var outline = new kity.Rect().setId(utils.uuid('node_outline'));
|
||||
|
||||
this.bringToBack = true;
|
||||
|
||||
return outline;
|
||||
},
|
||||
|
||||
update: function (outline, node, box) {
|
||||
var shape = node.getStyle('shape');
|
||||
|
||||
var paddingLeft = node.getStyle('padding-left'),
|
||||
paddingRight = node.getStyle('padding-right'),
|
||||
paddingTop = node.getStyle('padding-top'),
|
||||
paddingBottom = node.getStyle('padding-bottom');
|
||||
|
||||
var outlineBox = {
|
||||
x: box.x - paddingLeft,
|
||||
y: box.y - paddingTop,
|
||||
width: box.width + paddingLeft + paddingRight,
|
||||
height: box.height + paddingTop + paddingBottom,
|
||||
};
|
||||
|
||||
var radius = node.getStyle('radius');
|
||||
// 天盘图圆形的情况
|
||||
if (shape && shape == 'circle') {
|
||||
var p = Math.pow;
|
||||
var r = Math.round;
|
||||
|
||||
radius = r(Math.sqrt(p(outlineBox.width, 2) + p(outlineBox.height, 2)) / 2);
|
||||
|
||||
outlineBox.x = box.cx - radius;
|
||||
outlineBox.y = box.cy - radius;
|
||||
outlineBox.width = 2 * radius;
|
||||
outlineBox.height = 2 * radius;
|
||||
}
|
||||
|
||||
var prefix = node.isSelected() ? (node.getMinder().isFocused() ? 'selected-' : 'blur-selected-') : '';
|
||||
outline
|
||||
.setPosition(outlineBox.x, outlineBox.y)
|
||||
.setSize(outlineBox.width, outlineBox.height)
|
||||
.setRadius(radius)
|
||||
.fill(node.getData('background') || node.getStyle(prefix + 'background') || node.getStyle('background'))
|
||||
.stroke(node.getStyle(prefix + 'stroke' || node.getStyle('stroke')), node.getStyle(prefix + 'stroke-width'));
|
||||
|
||||
return new kity.Box(outlineBox);
|
||||
},
|
||||
});
|
||||
|
||||
var ShadowRenderer = kity.createClass('ShadowRenderer', {
|
||||
base: Renderer,
|
||||
|
||||
create: function (node) {
|
||||
this.bringToBack = true;
|
||||
return new kity.Rect();
|
||||
},
|
||||
|
||||
shouldRender: function (node) {
|
||||
return node.getStyle('shadow');
|
||||
},
|
||||
|
||||
update: function (shadow, node, box) {
|
||||
shadow.setPosition(box.x + 4, box.y + 5).fill(node.getStyle('shadow'));
|
||||
|
||||
var shape = node.getStyle('shape');
|
||||
if (!shape) {
|
||||
shadow.setSize(box.width, box.height);
|
||||
shadow.setRadius(node.getStyle('radius'));
|
||||
} else if (shape == 'circle') {
|
||||
var width = Math.max(box.width, box.height);
|
||||
shadow.setSize(width, width);
|
||||
shadow.setRadius(width / 2);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
var marker = new kity.Marker();
|
||||
|
||||
marker.setWidth(10);
|
||||
marker.setHeight(12);
|
||||
marker.setRef(0, 0);
|
||||
marker.setViewBox(-6, -4, 8, 10);
|
||||
|
||||
marker.addShape(new kity.Path().setPathData('M-5-3l5,3,-5,3').stroke('#33ffff'));
|
||||
|
||||
var wireframeOption = /wire/.test(window.location.href);
|
||||
var WireframeRenderer = kity.createClass('WireframeRenderer', {
|
||||
base: Renderer,
|
||||
|
||||
create: function () {
|
||||
var wireframe = new kity.Group();
|
||||
var oxy = (this.oxy = new kity.Path().stroke('#f6f').setPathData('M0,-50L0,50M-50,0L50,0'));
|
||||
|
||||
var box = (this.wireframe = new kity.Rect().stroke('lightgreen'));
|
||||
|
||||
var vectorIn = (this.vectorIn = new kity.Path().stroke('#66ffff'));
|
||||
var vectorOut = (this.vectorOut = new kity.Path().stroke('#66ffff'));
|
||||
|
||||
vectorIn.setMarker(marker, 'end');
|
||||
vectorOut.setMarker(marker, 'end');
|
||||
|
||||
return wireframe.addShapes([oxy, box, vectorIn, vectorOut]);
|
||||
},
|
||||
|
||||
shouldRender: function () {
|
||||
return wireframeOption;
|
||||
},
|
||||
|
||||
update: function (created, node, box) {
|
||||
this.wireframe.setPosition(box.x, box.y).setSize(box.width, box.height);
|
||||
var pin = node.getVertexIn();
|
||||
var pout = node.getVertexOut();
|
||||
var vin = node.getLayoutVectorIn().normalize(30);
|
||||
var vout = node.getLayoutVectorOut().normalize(30);
|
||||
this.vectorIn.setPathData(['M', pin.offset(vin.reverse()), 'L', pin]);
|
||||
this.vectorOut.setPathData(['M', pout, 'l', vout]);
|
||||
},
|
||||
});
|
||||
|
||||
Module.register('OutlineModule', function () {
|
||||
return {
|
||||
events: !wireframeOption
|
||||
? null
|
||||
: {
|
||||
ready: function () {
|
||||
this.getPaper().addResource(marker);
|
||||
},
|
||||
layoutallfinish: function () {
|
||||
this.getRoot().traverse(function (node) {
|
||||
node.getRenderer('WireframeRenderer').update(null, node, node.getContentBox());
|
||||
});
|
||||
},
|
||||
},
|
||||
renderers: {
|
||||
outline: OutlineRenderer,
|
||||
outside: [ShadowRenderer, WireframeRenderer],
|
||||
},
|
||||
};
|
||||
});
|
||||
});
|
|
@ -0,0 +1,161 @@
|
|||
/* eslint-disable */
|
||||
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');
|
||||
var Renderer = require('../core/render');
|
||||
|
||||
Module.register('PriorityModule', function () {
|
||||
var minder = this;
|
||||
|
||||
// Designed by Akikonata
|
||||
// [MASK, BACK]
|
||||
var PRIORITY_COLORS = [
|
||||
null,
|
||||
['#FF1200', '#840023'], // 1 - red
|
||||
['#0074FF', '#01467F'], // 2 - blue
|
||||
['#00AF00', '#006300'], // 3 - green
|
||||
['#FF962E', '#B25000'], // 4 - orange
|
||||
['#A464FF', '#4720C4'], // 5 - purple
|
||||
['#A3A3A3', '#515151'], // 6,7,8,9 - gray
|
||||
['#A3A3A3', '#515151'],
|
||||
['#A3A3A3', '#515151'],
|
||||
['#A3A3A3', '#515151'],
|
||||
]; // hue from 1 to 5
|
||||
|
||||
// jscs:disable maximumLineLength
|
||||
var BACK_PATH = 'M0,13c0,3.866,3.134,7,7,7h6c3.866,0,7-3.134,7-7V7H0V13z';
|
||||
var MASK_PATH = 'M20,10c0,3.866-3.134,7-7,7H7c-3.866,0-7-3.134-7-7V7c0-3.866,3.134-7,7-7h6c3.866,0,7,3.134,7,7V10z';
|
||||
|
||||
var PRIORITY_DATA = 'priority';
|
||||
|
||||
// 优先级图标的图形
|
||||
var PriorityIcon = kity.createClass('PriorityIcon', {
|
||||
base: kity.Group,
|
||||
|
||||
constructor: function () {
|
||||
this.callBase();
|
||||
this.setSize(20);
|
||||
this.create();
|
||||
this.setId(utils.uuid('node_priority'));
|
||||
},
|
||||
|
||||
setSize: function (size) {
|
||||
this.width = this.height = size;
|
||||
},
|
||||
|
||||
create: function () {
|
||||
var white, back, mask, number; // 4 layer
|
||||
|
||||
white = new kity.Path().setPathData(MASK_PATH).fill('white');
|
||||
back = new kity.Path().setPathData(BACK_PATH).setTranslate(0.5, 0.5);
|
||||
mask = new kity.Path().setPathData(MASK_PATH).setOpacity(0.8).setTranslate(0.5, 0.5);
|
||||
|
||||
number = new kity.Text()
|
||||
.setX(this.width / 2 - 0.5)
|
||||
.setY(this.height / 2)
|
||||
.setTextAnchor('middle')
|
||||
.setVerticalAlign('middle')
|
||||
.setFontItalic(true)
|
||||
.setFontSize(12)
|
||||
.fill('white');
|
||||
|
||||
this.addShapes([back, mask, number]);
|
||||
this.mask = mask;
|
||||
this.back = back;
|
||||
this.number = number;
|
||||
},
|
||||
|
||||
setValue: function (value) {
|
||||
var back = this.back,
|
||||
mask = this.mask,
|
||||
number = this.number;
|
||||
|
||||
var color = PRIORITY_COLORS[value];
|
||||
|
||||
if (color) {
|
||||
back.fill(color[1]);
|
||||
mask.fill(color[0]);
|
||||
}
|
||||
|
||||
number.setContent(value);
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* @command Priority
|
||||
* @description 设置节点的优先级信息
|
||||
* @param {number} value 要设置的优先级(添加一个优先级小图标)
|
||||
* 取值为 0 移除优先级信息;
|
||||
* 取值为 1 - 9 设置优先级,超过 9 的优先级不渲染
|
||||
* @state
|
||||
* 0: 当前有选中的节点
|
||||
* -1: 当前没有选中的节点
|
||||
*/
|
||||
var PriorityCommand = kity.createClass('SetPriorityCommand', {
|
||||
base: Command,
|
||||
execute: function (km, value) {
|
||||
var nodes = km.getSelectedNodes();
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
nodes[i].setData(PRIORITY_DATA, value || null).render();
|
||||
}
|
||||
km.layout();
|
||||
},
|
||||
queryValue: function (km) {
|
||||
var nodes = km.getSelectedNodes();
|
||||
var val;
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
val = nodes[i].getData(PRIORITY_DATA);
|
||||
if (val) break;
|
||||
}
|
||||
return val || null;
|
||||
},
|
||||
|
||||
queryState: function (km) {
|
||||
return km.getSelectedNodes().length ? 0 : -1;
|
||||
},
|
||||
});
|
||||
return {
|
||||
commands: {
|
||||
priority: PriorityCommand,
|
||||
},
|
||||
renderers: {
|
||||
left: kity.createClass('PriorityRenderer', {
|
||||
base: Renderer,
|
||||
|
||||
create: function (node) {
|
||||
return new PriorityIcon();
|
||||
},
|
||||
|
||||
shouldRender: function (node) {
|
||||
return node.getData(PRIORITY_DATA);
|
||||
},
|
||||
|
||||
update: function (icon, node, box) {
|
||||
var data = node.getData(PRIORITY_DATA);
|
||||
var spaceLeft = node.getStyle('space-left'),
|
||||
x,
|
||||
y;
|
||||
|
||||
icon.setValue(data);
|
||||
x = box.left - icon.width - spaceLeft;
|
||||
y = -icon.height / 2;
|
||||
|
||||
icon.setTranslate(x, y);
|
||||
|
||||
return new kity.Box({
|
||||
x: x,
|
||||
y: y,
|
||||
width: icon.width,
|
||||
height: icon.height,
|
||||
});
|
||||
},
|
||||
}),
|
||||
},
|
||||
};
|
||||
});
|
||||
});
|
|
@ -0,0 +1,150 @@
|
|||
/* eslint-disable */
|
||||
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');
|
||||
var Renderer = require('../core/render');
|
||||
|
||||
Module.register('ProgressModule', function () {
|
||||
var minder = this;
|
||||
|
||||
var PROGRESS_DATA = 'progress';
|
||||
|
||||
// Designed by Akikonata
|
||||
var BG_COLOR = '#FFED83';
|
||||
var PIE_COLOR = '#43BC00';
|
||||
var SHADOW_PATH = 'M10,3c4.418,0,8,3.582,8,8h1c0-5.523-3.477-10-9-10S1,5.477,1,11h1C2,6.582,5.582,3,10,3z';
|
||||
var SHADOW_COLOR = '#8E8E8E';
|
||||
|
||||
// jscs:disable maximumLineLength
|
||||
var FRAME_PATH =
|
||||
'M10,0C4.477,0,0,4.477,0,10c0,5.523,4.477,10,10,10s10-4.477,10-10C20,4.477,15.523,0,10,0zM10,18c-4.418,0-8-3.582-8-8s3.582-8,8-8s8,3.582,8,8S14.418,18,10,18z';
|
||||
|
||||
var FRAME_GRAD = new kity.LinearGradient().pipe(function (g) {
|
||||
g.setStartPosition(0, 0);
|
||||
g.setEndPosition(0, 1);
|
||||
g.addStop(0, '#fff');
|
||||
g.addStop(1, '#ccc');
|
||||
});
|
||||
var CHECK_PATH = 'M15.812,7.896l-6.75,6.75l-4.5-4.5L6.25,8.459l2.812,2.803l5.062-5.053L15.812,7.896z';
|
||||
var CHECK_COLOR = '#EEE';
|
||||
|
||||
minder.getPaper().addResource(FRAME_GRAD);
|
||||
|
||||
// 进度图标的图形
|
||||
var ProgressIcon = kity.createClass('ProgressIcon', {
|
||||
base: kity.Group,
|
||||
|
||||
constructor: function (value) {
|
||||
this.callBase();
|
||||
this.setSize(20);
|
||||
this.create();
|
||||
this.setValue(value);
|
||||
this.setId(utils.uuid('node_progress'));
|
||||
this.translate(0.5, 0.5);
|
||||
},
|
||||
|
||||
setSize: function (size) {
|
||||
this.width = this.height = size;
|
||||
},
|
||||
|
||||
create: function () {
|
||||
var bg, pie, shadow, frame, check;
|
||||
|
||||
bg = new kity.Circle(9).fill(BG_COLOR);
|
||||
|
||||
pie = new kity.Pie(9, 0).fill(PIE_COLOR);
|
||||
|
||||
shadow = new kity.Path().setPathData(SHADOW_PATH).setTranslate(-10, -10).fill(SHADOW_COLOR);
|
||||
|
||||
frame = new kity.Path().setTranslate(-10, -10).setPathData(FRAME_PATH).fill(FRAME_GRAD);
|
||||
|
||||
check = new kity.Path().setTranslate(-10, -10).setPathData(CHECK_PATH).fill(CHECK_COLOR);
|
||||
|
||||
this.addShapes([bg, pie, shadow, check, frame]);
|
||||
this.pie = pie;
|
||||
this.check = check;
|
||||
},
|
||||
|
||||
setValue: function (value) {
|
||||
this.pie.setAngle((-360 * (value - 1)) / 8);
|
||||
this.check.setVisible(value == 9);
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* @command Progress
|
||||
* @description 设置节点的进度信息(添加一个进度小图标)
|
||||
* @param {number} value 要设置的进度
|
||||
* 取值为 0 移除进度信息;
|
||||
* 取值为 1 表示未开始;
|
||||
* 取值为 2 表示完成 1/8;
|
||||
* 取值为 3 表示完成 2/8;
|
||||
* 取值为 4 表示完成 3/8;
|
||||
* 其余类推,取值为 9 表示全部完成
|
||||
* @state
|
||||
* 0: 当前有选中的节点
|
||||
* -1: 当前没有选中的节点
|
||||
*/
|
||||
var ProgressCommand = kity.createClass('ProgressCommand', {
|
||||
base: Command,
|
||||
execute: function (km, value) {
|
||||
var nodes = km.getSelectedNodes();
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
nodes[i].setData(PROGRESS_DATA, value || null).render();
|
||||
}
|
||||
km.layout();
|
||||
},
|
||||
queryValue: function (km) {
|
||||
var nodes = km.getSelectedNodes();
|
||||
var val;
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
val = nodes[i].getData(PROGRESS_DATA);
|
||||
if (val) break;
|
||||
}
|
||||
return val || null;
|
||||
},
|
||||
|
||||
queryState: function (km) {
|
||||
return km.getSelectedNodes().length ? 0 : -1;
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
commands: {
|
||||
progress: ProgressCommand,
|
||||
},
|
||||
renderers: {
|
||||
left: kity.createClass('ProgressRenderer', {
|
||||
base: Renderer,
|
||||
|
||||
create: function (node) {
|
||||
return new ProgressIcon();
|
||||
},
|
||||
|
||||
shouldRender: function (node) {
|
||||
return node.getData(PROGRESS_DATA);
|
||||
},
|
||||
|
||||
update: function (icon, node, box) {
|
||||
var data = node.getData(PROGRESS_DATA);
|
||||
var spaceLeft = node.getStyle('space-left');
|
||||
var x, y;
|
||||
|
||||
icon.setValue(data);
|
||||
|
||||
x = box.left - icon.width - spaceLeft;
|
||||
y = -icon.height / 2;
|
||||
icon.setTranslate(x + icon.width / 2, y + icon.height / 2);
|
||||
|
||||
return new kity.Box(x, y, icon.width, icon.height);
|
||||
},
|
||||
}),
|
||||
},
|
||||
};
|
||||
});
|
||||
});
|
|
@ -0,0 +1,404 @@
|
|||
/* eslint-disable */
|
||||
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');
|
||||
var Renderer = require('../core/render');
|
||||
|
||||
Module.register('Resource', function () {
|
||||
// String Hash
|
||||
// https://github.com/drostie/sha3-js/edit/master/blake32.min.js
|
||||
var blake32 = (function () {
|
||||
var k, g, r, l, m, o, p, q, t, w, x;
|
||||
x = 4 * (1 << 30);
|
||||
k = [0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19];
|
||||
m = [
|
||||
0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, 0x452821e6,
|
||||
0x38d01377, 0xbe5466cf, 0x34e90c6c, 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
|
||||
];
|
||||
w = function (i) {
|
||||
if (i < 0) {
|
||||
i += x;
|
||||
}
|
||||
return ('00000000' + i.toString(16)).slice(-8);
|
||||
};
|
||||
o = [
|
||||
[16, 50, 84, 118, 152, 186, 220, 254],
|
||||
[174, 132, 249, 109, 193, 32, 123, 53],
|
||||
[139, 12, 37, 223, 234, 99, 23, 73],
|
||||
[151, 19, 205, 235, 98, 165, 4, 143],
|
||||
[9, 117, 66, 250, 30, 203, 134, 211],
|
||||
[194, 166, 176, 56, 212, 87, 239, 145],
|
||||
[92, 241, 222, 164, 112, 54, 41, 184],
|
||||
[189, 231, 28, 147, 5, 79, 104, 162],
|
||||
[246, 158, 59, 128, 44, 125, 65, 90],
|
||||
[42, 72, 103, 81, 191, 233, 195, 13],
|
||||
];
|
||||
p = function (a, b, n) {
|
||||
var s = q[a] ^ q[b];
|
||||
q[a] = (s >>> n) | (s << (32 - n));
|
||||
};
|
||||
g = function (i, a, b, c, d) {
|
||||
var u = l + (o[r][i] % 16),
|
||||
v = l + (o[r][i] >> 4);
|
||||
a %= 4;
|
||||
b = 4 + (b % 4);
|
||||
c = 8 + (c % 4);
|
||||
d = 12 + (d % 4);
|
||||
q[a] += q[b] + (t[u] ^ m[v % 16]);
|
||||
p(d, a, 16);
|
||||
q[c] += q[d];
|
||||
p(b, c, 12);
|
||||
q[a] += q[b] + (t[v] ^ m[u % 16]);
|
||||
p(d, a, 8);
|
||||
q[c] += q[d];
|
||||
p(b, c, 7);
|
||||
};
|
||||
return function (a, b) {
|
||||
if (!(b instanceof Array && b.length === 4)) {
|
||||
b = [0, 0, 0, 0];
|
||||
}
|
||||
var c, d, e, L, f, h, j, i;
|
||||
d = k.slice(0);
|
||||
c = m.slice(0, 8);
|
||||
for (r = 0; r < 4; r += 1) {
|
||||
c[r] ^= b[r];
|
||||
}
|
||||
e = a.length * 16;
|
||||
f = e % 512 > 446 || e % 512 === 0 ? 0 : e;
|
||||
if (e % 512 === 432) {
|
||||
a += '\u8001';
|
||||
} else {
|
||||
a += '\u8000';
|
||||
while (a.length % 32 !== 27) {
|
||||
a += '\u0000';
|
||||
}
|
||||
a += '\u0001';
|
||||
}
|
||||
t = [];
|
||||
for (i = 0; i < a.length; i += 2) {
|
||||
t.push(a.charCodeAt(i) * 65536 + a.charCodeAt(i + 1));
|
||||
}
|
||||
t.push(0);
|
||||
t.push(e);
|
||||
h = t.length - 16;
|
||||
j = 0;
|
||||
for (l = 0; l < t.length; l += 16) {
|
||||
j += 512;
|
||||
L = l === h ? f : Math.min(e, j);
|
||||
q = d.concat(c);
|
||||
q[12] ^= L;
|
||||
q[13] ^= L;
|
||||
for (r = 0; r < 10; r += 1) {
|
||||
for (i = 0; i < 8; i += 1) {
|
||||
if (i < 4) {
|
||||
g(i, i, i, i, i);
|
||||
} else {
|
||||
g(i, i, i + 1, i + 2, i + 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i = 0; i < 8; i += 1) {
|
||||
d[i] ^= b[i % 4] ^ q[i] ^ q[i + 8];
|
||||
}
|
||||
}
|
||||
return d.map(w).join('');
|
||||
};
|
||||
})();
|
||||
|
||||
/**
|
||||
* 自动使用的颜色序列
|
||||
*/
|
||||
var RESOURCE_COLOR_SERIES = [51, 303, 75, 200, 157, 0, 26, 254].map(function (h) {
|
||||
return kity.Color.createHSL(h, 100, 85);
|
||||
});
|
||||
|
||||
/**
|
||||
* 在 Minder 上拓展一些关于资源的支持接口
|
||||
*/
|
||||
kity.extendClass(Minder, {
|
||||
/**
|
||||
* 获取字符串的哈希值
|
||||
*
|
||||
* @param {String} str
|
||||
* @return {Number} hashCode
|
||||
*/
|
||||
getHashCode: function (str) {
|
||||
str = blake32(str);
|
||||
var hash = 1315423911,
|
||||
i,
|
||||
ch;
|
||||
for (i = str.length - 1; i >= 0; i--) {
|
||||
ch = str.charCodeAt(i);
|
||||
hash ^= (hash << 5) + ch + (hash >> 2);
|
||||
}
|
||||
return hash & 0x7fffffff;
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取脑图中某个资源对应的颜色
|
||||
*
|
||||
* 如果存在同名资源,则返回已经分配给该资源的颜色,否则分配给该资源一个颜色,并且返回
|
||||
*
|
||||
* 如果资源数超过颜色序列数量,返回哈希颜色
|
||||
*
|
||||
* @param {String} resource 资源名称
|
||||
* @return {Color}
|
||||
*/
|
||||
getResourceColor: function (resource) {
|
||||
var colorMapping = this._getResourceColorIndexMapping();
|
||||
var nextIndex;
|
||||
|
||||
if (!Object.prototype.hasOwnProperty.call(colorMapping, resource)) {
|
||||
// 找不到找下个可用索引
|
||||
nextIndex = this._getNextResourceColorIndex();
|
||||
colorMapping[resource] = nextIndex;
|
||||
}
|
||||
|
||||
// 资源过多,找不到可用索引颜色,统一返回哈希函数得到的颜色
|
||||
return (
|
||||
RESOURCE_COLOR_SERIES[colorMapping[resource]] ||
|
||||
kity.Color.createHSL(Math.floor((this.getHashCode(resource) / 0x7fffffff) * 359), 100, 85)
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* 获得已使用的资源的列表
|
||||
*
|
||||
* @return {Array}
|
||||
*/
|
||||
getUsedResource: function () {
|
||||
var mapping = this._getResourceColorIndexMapping();
|
||||
var used = [],
|
||||
resource;
|
||||
|
||||
for (resource in mapping) {
|
||||
if (Object.prototype.hasOwnProperty.call(mapping, resource)) {
|
||||
used.push(resource);
|
||||
}
|
||||
}
|
||||
|
||||
return used;
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取脑图下一个可用的资源颜色索引
|
||||
*
|
||||
* @return {int}
|
||||
*/
|
||||
_getNextResourceColorIndex: function () {
|
||||
// 获取现有颜色映射
|
||||
// resource => color_index
|
||||
var colorMapping = this._getResourceColorIndexMapping();
|
||||
|
||||
var resource, used, i;
|
||||
|
||||
used = [];
|
||||
|
||||
// 抽取已经使用的值到 used 数组
|
||||
for (resource in colorMapping) {
|
||||
if (Object.prototype.hasOwnProperty.call(colorMapping, resource)) {
|
||||
used.push(colorMapping[resource]);
|
||||
}
|
||||
}
|
||||
|
||||
// 枚举所有的可用值,如果还没被使用,返回
|
||||
for (i = 0; i < RESOURCE_COLOR_SERIES.length; i++) {
|
||||
if (!~used.indexOf(i)) return i;
|
||||
}
|
||||
|
||||
// 没有可用的颜色了
|
||||
return -1;
|
||||
},
|
||||
|
||||
// 获取现有颜色映射
|
||||
// resource => color_index
|
||||
_getResourceColorIndexMapping: function () {
|
||||
return this._resourceColorMapping || (this._resourceColorMapping = {});
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* @class 设置资源的命令
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* // 设置选中节点资源为 "张三"
|
||||
* minder.execCommand('resource', ['张三']);
|
||||
*
|
||||
* // 添加资源 "李四" 到选中节点
|
||||
* var resource = minder.queryCommandValue();
|
||||
* resource.push('李四');
|
||||
* minder.execCommand('resource', resource);
|
||||
*
|
||||
* // 清除选中节点的资源
|
||||
* minder.execCommand('resource', null);
|
||||
*/
|
||||
var ResourceCommand = kity.createClass('ResourceCommand', {
|
||||
base: Command,
|
||||
|
||||
execute: function (minder, resource) {
|
||||
var nodes = minder.getSelectedNodes();
|
||||
|
||||
if (typeof resource == 'string') {
|
||||
resource = [resource];
|
||||
}
|
||||
|
||||
nodes.forEach(function (node) {
|
||||
node.setData('resource', resource).render();
|
||||
});
|
||||
|
||||
minder.layout(200);
|
||||
},
|
||||
|
||||
queryValue: function (minder) {
|
||||
var nodes = minder.getSelectedNodes();
|
||||
var resource = [];
|
||||
|
||||
nodes.forEach(function (node) {
|
||||
var nodeResource = node.getData('resource');
|
||||
|
||||
if (!nodeResource) return;
|
||||
|
||||
nodeResource.forEach(function (name) {
|
||||
if (!~resource.indexOf(name)) {
|
||||
resource.push(name);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return resource;
|
||||
},
|
||||
|
||||
queryState: function (km) {
|
||||
return km.getSelectedNode() ? 0 : -1;
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* @class 资源的覆盖图形
|
||||
*
|
||||
* 该类为一个资源以指定的颜色渲染一个动态的覆盖图形
|
||||
*/
|
||||
var ResourceOverlay = kity.createClass('ResourceOverlay', {
|
||||
base: kity.Group,
|
||||
|
||||
constructor: function () {
|
||||
this.callBase();
|
||||
|
||||
var text, rect;
|
||||
|
||||
rect = this.rect = new kity.Rect().setRadius(4);
|
||||
|
||||
text = this.text = new kity.Text().setFontSize(12).setVerticalAlign('middle');
|
||||
|
||||
this.addShapes([rect, text]);
|
||||
},
|
||||
|
||||
setValue: function (resourceName, color) {
|
||||
var paddingX = 8,
|
||||
paddingY = 4,
|
||||
borderRadius = 4;
|
||||
var text, box, rect;
|
||||
|
||||
text = this.text;
|
||||
|
||||
if (resourceName == this.lastResourceName) {
|
||||
box = this.lastBox;
|
||||
} else {
|
||||
text.setContent(resourceName);
|
||||
|
||||
box = text.getBoundaryBox();
|
||||
this.lastResourceName = resourceName;
|
||||
this.lastBox = box;
|
||||
}
|
||||
|
||||
text.setX(paddingX).fill(color.dec('l', 70));
|
||||
|
||||
rect = this.rect;
|
||||
rect.setPosition(0, box.y - paddingY);
|
||||
this.width = Math.round(box.width + paddingX * 2);
|
||||
this.height = Math.round(box.height + paddingY * 2);
|
||||
rect.setSize(this.width, this.height);
|
||||
rect.fill(color);
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* @class 资源渲染器
|
||||
*/
|
||||
var ResourceRenderer = kity.createClass('ResourceRenderer', {
|
||||
base: Renderer,
|
||||
|
||||
create: function (node) {
|
||||
this.overlays = [];
|
||||
return new kity.Group();
|
||||
},
|
||||
|
||||
shouldRender: function (node) {
|
||||
return node.getData('resource') && node.getData('resource').length;
|
||||
},
|
||||
|
||||
update: function (container, node, box) {
|
||||
var spaceRight = node.getStyle('space-right');
|
||||
|
||||
var overlays = this.overlays;
|
||||
|
||||
/* 修复 resource 数组中出现 null 的 bug
|
||||
* @Author zhangbobell
|
||||
* @date 2016-01-15
|
||||
*/
|
||||
var resource = node.getData('resource').filter(function (ele) {
|
||||
return ele !== null;
|
||||
});
|
||||
if (resource.length === 0) {
|
||||
return;
|
||||
}
|
||||
var minder = node.getMinder();
|
||||
var i, overlay, x;
|
||||
|
||||
x = 0;
|
||||
for (i = 0; i < resource.length; i++) {
|
||||
x += spaceRight;
|
||||
|
||||
overlay = overlays[i];
|
||||
if (!overlay) {
|
||||
overlay = new ResourceOverlay();
|
||||
overlays.push(overlay);
|
||||
container.addShape(overlay);
|
||||
}
|
||||
overlay.setVisible(true);
|
||||
overlay.setValue(resource[i], minder.getResourceColor(resource[i]));
|
||||
overlay.setTranslate(x, -1);
|
||||
|
||||
x += overlay.width;
|
||||
}
|
||||
|
||||
while ((overlay = overlays[i++])) overlay.setVisible(false);
|
||||
|
||||
container.setTranslate(box.right, 0);
|
||||
|
||||
return new kity.Box({
|
||||
x: box.right,
|
||||
y: Math.round(-overlays[0].height / 2),
|
||||
width: x,
|
||||
height: overlays[0].height,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
commands: {
|
||||
resource: ResourceCommand,
|
||||
},
|
||||
|
||||
renderers: {
|
||||
right: ResourceRenderer,
|
||||
},
|
||||
};
|
||||
});
|
||||
});
|
|
@ -0,0 +1,185 @@
|
|||
/* eslint-disable */
|
||||
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');
|
||||
var Renderer = require('../core/render');
|
||||
|
||||
Module.register('Select', function () {
|
||||
var minder = this;
|
||||
var rc = minder.getRenderContainer();
|
||||
|
||||
// 在实例上渲染框选矩形、计算框选范围的对象
|
||||
var marqueeActivator = (function () {
|
||||
// 记录选区的开始位置(mousedown的位置)
|
||||
var startPosition = null;
|
||||
|
||||
// 选区的图形
|
||||
var marqueeShape = new kity.Path();
|
||||
|
||||
// 标记是否已经启动框选状态
|
||||
// 并不是 mousedown 发生之后就启动框选状态,而是检测到移动了一定的距离(MARQUEE_MODE_THRESHOLD)之后
|
||||
var marqueeMode = false;
|
||||
var MARQUEE_MODE_THRESHOLD = 10;
|
||||
|
||||
return {
|
||||
selectStart: function (e) {
|
||||
// 只接受左键
|
||||
if (e.originEvent.button || e.originEvent.altKey) return;
|
||||
|
||||
// 清理不正确状态
|
||||
if (startPosition) {
|
||||
return this.selectEnd();
|
||||
}
|
||||
|
||||
startPosition = e.getPosition(rc).round();
|
||||
},
|
||||
selectMove: function (e) {
|
||||
if (minder.getStatus() == 'textedit') {
|
||||
return;
|
||||
}
|
||||
if (!startPosition) return;
|
||||
|
||||
var p1 = startPosition,
|
||||
p2 = e.getPosition(rc);
|
||||
|
||||
// 检测是否要进入选区模式
|
||||
if (!marqueeMode) {
|
||||
// 距离没达到阈值,退出
|
||||
if (kity.Vector.fromPoints(p1, p2).length() < MARQUEE_MODE_THRESHOLD) {
|
||||
return;
|
||||
}
|
||||
// 已经达到阈值,记录下来并且重置选区形状
|
||||
marqueeMode = true;
|
||||
rc.addShape(marqueeShape);
|
||||
marqueeShape
|
||||
.fill(minder.getStyle('marquee-background'))
|
||||
.stroke(minder.getStyle('marquee-stroke'))
|
||||
.setOpacity(0.8)
|
||||
.getDrawer()
|
||||
.clear();
|
||||
}
|
||||
|
||||
var marquee = new kity.Box(p1.x, p1.y, p2.x - p1.x, p2.y - p1.y),
|
||||
selectedNodes = [];
|
||||
|
||||
// 使其犀利
|
||||
marquee.left = Math.round(marquee.left);
|
||||
marquee.top = Math.round(marquee.top);
|
||||
marquee.right = Math.round(marquee.right);
|
||||
marquee.bottom = Math.round(marquee.bottom);
|
||||
|
||||
// 选区形状更新
|
||||
marqueeShape.getDrawer().pipe(function () {
|
||||
this.clear();
|
||||
this.moveTo(marquee.left, marquee.top);
|
||||
this.lineTo(marquee.right, marquee.top);
|
||||
this.lineTo(marquee.right, marquee.bottom);
|
||||
this.lineTo(marquee.left, marquee.bottom);
|
||||
this.close();
|
||||
});
|
||||
|
||||
// 计算选中范围
|
||||
minder.getRoot().traverse(function (node) {
|
||||
var renderBox = node.getLayoutBox();
|
||||
if (!renderBox.intersect(marquee).isEmpty()) {
|
||||
selectedNodes.push(node);
|
||||
}
|
||||
});
|
||||
|
||||
// 应用选中范围
|
||||
minder.select(selectedNodes, true);
|
||||
|
||||
// 清除多余的东西
|
||||
window.getSelection().removeAllRanges();
|
||||
},
|
||||
selectEnd: function (e) {
|
||||
if (startPosition) {
|
||||
startPosition = null;
|
||||
}
|
||||
if (marqueeMode) {
|
||||
marqueeShape.fadeOut(200, 'ease', 0, function () {
|
||||
if (marqueeShape.remove) marqueeShape.remove();
|
||||
});
|
||||
marqueeMode = false;
|
||||
}
|
||||
},
|
||||
};
|
||||
})();
|
||||
|
||||
var lastDownNode = null,
|
||||
lastDownPosition = null;
|
||||
return {
|
||||
init: function () {
|
||||
window.addEventListener('mouseup', function () {
|
||||
marqueeActivator.selectEnd();
|
||||
});
|
||||
},
|
||||
events: {
|
||||
'mousedown': function (e) {
|
||||
var downNode = e.getTargetNode();
|
||||
|
||||
// 没有点中节点:
|
||||
// 清除选中状态,并且标记选区开始位置
|
||||
if (!downNode) {
|
||||
this.removeAllSelectedNodes();
|
||||
marqueeActivator.selectStart(e);
|
||||
|
||||
this.setStatus('normal');
|
||||
}
|
||||
|
||||
// 点中了节点,并且按了 shift 键:
|
||||
// 被点中的节点切换选中状态
|
||||
else if (e.isShortcutKey('Ctrl')) {
|
||||
this.toggleSelect(downNode);
|
||||
}
|
||||
|
||||
// 点中的节点没有被选择:
|
||||
// 单选点中的节点
|
||||
else if (!downNode.isSelected()) {
|
||||
this.select(downNode, true);
|
||||
}
|
||||
|
||||
// 点中的节点被选中了,并且不是单选:
|
||||
// 完成整个点击之后需要使其变为单选。
|
||||
// 不能马上变为单选,因为可能是需要拖动选中的多个节点
|
||||
else if (!this.isSingleSelect()) {
|
||||
lastDownNode = downNode;
|
||||
lastDownPosition = e.getPosition();
|
||||
}
|
||||
},
|
||||
'mousemove': marqueeActivator.selectMove,
|
||||
'mouseup': function (e) {
|
||||
var upNode = e.getTargetNode();
|
||||
|
||||
// 如果 mouseup 发生在 lastDownNode 外,是无需理会的
|
||||
if (upNode && upNode == lastDownNode) {
|
||||
var upPosition = e.getPosition();
|
||||
var movement = kity.Vector.fromPoints(lastDownPosition, upPosition);
|
||||
if (movement.length() < 1) this.select(lastDownNode, true);
|
||||
lastDownNode = null;
|
||||
}
|
||||
|
||||
// 清理一下选择状态
|
||||
marqueeActivator.selectEnd(e);
|
||||
},
|
||||
//全选操作
|
||||
'normal.keydown': function (e) {
|
||||
if (e.isShortcutKey('ctrl+a')) {
|
||||
var selectedNodes = [];
|
||||
|
||||
this.getRoot().traverse(function (node) {
|
||||
selectedNodes.push(node);
|
||||
});
|
||||
this.select(selectedNodes, true);
|
||||
e.preventDefault();
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
});
|
|
@ -0,0 +1,114 @@
|
|||
/* eslint-disable */
|
||||
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');
|
||||
var Renderer = require('../core/render');
|
||||
|
||||
Module.register('StyleModule', function () {
|
||||
var styleNames = ['font-size', 'font-family', 'font-weight', 'font-style', 'background', 'color'];
|
||||
var styleClipBoard = null;
|
||||
|
||||
function hasStyle(node) {
|
||||
var data = node.getData();
|
||||
for (var i = 0; i < styleNames.length; i++) {
|
||||
if (styleNames[i] in data) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
commands: {
|
||||
/**
|
||||
* @command CopyStyle
|
||||
* @description 拷贝选中节点的当前样式,包括字体、字号、粗体、斜体、背景色、字体色
|
||||
* @state
|
||||
* 0: 当前有选中的节点
|
||||
* -1: 当前没有选中的节点
|
||||
*/
|
||||
copystyle: kity.createClass('CopyStyleCommand', {
|
||||
base: Command,
|
||||
|
||||
execute: function (minder) {
|
||||
var node = minder.getSelectedNode();
|
||||
var nodeData = node.getData();
|
||||
styleClipBoard = {};
|
||||
styleNames.forEach(function (name) {
|
||||
if (name in nodeData) styleClipBoard[name] = nodeData[name];
|
||||
else {
|
||||
styleClipBoard[name] = null;
|
||||
delete styleClipBoard[name];
|
||||
}
|
||||
});
|
||||
return styleClipBoard;
|
||||
},
|
||||
|
||||
queryState: function (minder) {
|
||||
var nodes = minder.getSelectedNodes();
|
||||
if (nodes.length !== 1) return -1;
|
||||
return hasStyle(nodes[0]) ? 0 : -1;
|
||||
},
|
||||
}),
|
||||
|
||||
/**
|
||||
* @command PasteStyle
|
||||
* @description 粘贴已拷贝的样式到选中的节点上,包括字体、字号、粗体、斜体、背景色、字体色
|
||||
* @state
|
||||
* 0: 当前有选中的节点,并且已经有复制的样式
|
||||
* -1: 当前没有选中的节点,或者没有复制的样式
|
||||
*/
|
||||
pastestyle: kity.createClass('PastStyleCommand', {
|
||||
base: Command,
|
||||
|
||||
execute: function (minder) {
|
||||
minder.getSelectedNodes().forEach(function (node) {
|
||||
for (var name in styleClipBoard) {
|
||||
if (styleClipBoard.hasOwnProperty(name)) node.setData(name, styleClipBoard[name]);
|
||||
}
|
||||
});
|
||||
minder.renderNodeBatch(minder.getSelectedNodes());
|
||||
minder.layout(300);
|
||||
return styleClipBoard;
|
||||
},
|
||||
|
||||
queryState: function (minder) {
|
||||
return styleClipBoard && minder.getSelectedNodes().length ? 0 : -1;
|
||||
},
|
||||
}),
|
||||
|
||||
/**
|
||||
* @command ClearStyle
|
||||
* @description 移除选中节点的样式,包括字体、字号、粗体、斜体、背景色、字体色
|
||||
* @state
|
||||
* 0: 当前有选中的节点,并且至少有一个设置了至少一种样式
|
||||
* -1: 其它情况
|
||||
*/
|
||||
clearstyle: kity.createClass('ClearStyleCommand', {
|
||||
base: Command,
|
||||
execute: function (minder) {
|
||||
minder.getSelectedNodes().forEach(function (node) {
|
||||
styleNames.forEach(function (name) {
|
||||
node.setData(name);
|
||||
});
|
||||
});
|
||||
minder.renderNodeBatch(minder.getSelectedNodes());
|
||||
minder.layout(300);
|
||||
return styleClipBoard;
|
||||
},
|
||||
|
||||
queryState: function (minder) {
|
||||
var nodes = minder.getSelectedNodes();
|
||||
if (!nodes.length) return -1;
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
if (hasStyle(nodes[i])) return 0;
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
}),
|
||||
},
|
||||
};
|
||||
});
|
||||
});
|
|
@ -0,0 +1,285 @@
|
|||
/* eslint-disable */
|
||||
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');
|
||||
var Renderer = require('../core/render');
|
||||
/**
|
||||
* 针对不同系统、不同浏览器、不同字体做居中兼容性处理
|
||||
* 暂时未增加Linux的处理
|
||||
*/
|
||||
var FONT_ADJUST = {
|
||||
safari: {
|
||||
'微软雅黑,Microsoft YaHei': -0.17,
|
||||
'楷体,楷体_GB2312,SimKai': -0.1,
|
||||
'隶书, SimLi': -0.1,
|
||||
'comic sans ms': -0.23,
|
||||
'impact,chicago': -0.15,
|
||||
'times new roman': -0.1,
|
||||
'arial black,avant garde': -0.17,
|
||||
'default': 0,
|
||||
},
|
||||
ie: {
|
||||
10: {
|
||||
'微软雅黑,Microsoft YaHei': -0.17,
|
||||
'comic sans ms': -0.17,
|
||||
'impact,chicago': -0.08,
|
||||
'times new roman': 0.04,
|
||||
'arial black,avant garde': -0.17,
|
||||
'default': -0.15,
|
||||
},
|
||||
11: {
|
||||
'微软雅黑,Microsoft YaHei': -0.17,
|
||||
'arial,helvetica,sans-serif': -0.17,
|
||||
'comic sans ms': -0.17,
|
||||
'impact,chicago': -0.08,
|
||||
'times new roman': 0.04,
|
||||
'sans-serif': -0.16,
|
||||
'arial black,avant garde': -0.17,
|
||||
'default': -0.15,
|
||||
},
|
||||
},
|
||||
edge: {
|
||||
'微软雅黑,Microsoft YaHei': -0.15,
|
||||
'arial,helvetica,sans-serif': -0.17,
|
||||
'comic sans ms': -0.17,
|
||||
'impact,chicago': -0.08,
|
||||
'sans-serif': -0.16,
|
||||
'arial black,avant garde': -0.17,
|
||||
'default': -0.15,
|
||||
},
|
||||
sg: {
|
||||
'微软雅黑,Microsoft YaHei': -0.15,
|
||||
'arial,helvetica,sans-serif': -0.05,
|
||||
'comic sans ms': -0.22,
|
||||
'impact,chicago': -0.16,
|
||||
'times new roman': -0.03,
|
||||
'arial black,avant garde': -0.22,
|
||||
'default': -0.15,
|
||||
},
|
||||
chrome: {
|
||||
Mac: {
|
||||
'andale mono': -0.05,
|
||||
'comic sans ms': -0.3,
|
||||
'impact,chicago': -0.13,
|
||||
'times new roman': -0.1,
|
||||
'arial black,avant garde': -0.17,
|
||||
'default': 0,
|
||||
},
|
||||
Win: {
|
||||
'微软雅黑,Microsoft YaHei': -0.15,
|
||||
'arial,helvetica,sans-serif': -0.02,
|
||||
'arial black,avant garde': -0.2,
|
||||
'comic sans ms': -0.2,
|
||||
'impact,chicago': -0.12,
|
||||
'times new roman': -0.02,
|
||||
'default': -0.15,
|
||||
},
|
||||
Lux: {
|
||||
'andale mono': -0.05,
|
||||
'comic sans ms': -0.3,
|
||||
'impact,chicago': -0.13,
|
||||
'times new roman': -0.1,
|
||||
'arial black,avant garde': -0.17,
|
||||
'default': 0,
|
||||
},
|
||||
},
|
||||
firefox: {
|
||||
Mac: {
|
||||
'微软雅黑,Microsoft YaHei': -0.2,
|
||||
'宋体,SimSun': 0.05,
|
||||
'comic sans ms': -0.2,
|
||||
'impact,chicago': -0.15,
|
||||
'arial black,avant garde': -0.17,
|
||||
'times new roman': -0.1,
|
||||
'default': 0.05,
|
||||
},
|
||||
Win: {
|
||||
'微软雅黑,Microsoft YaHei': -0.16,
|
||||
'andale mono': -0.17,
|
||||
'arial,helvetica,sans-serif': -0.17,
|
||||
'comic sans ms': -0.22,
|
||||
'impact,chicago': -0.23,
|
||||
'times new roman': -0.22,
|
||||
'sans-serif': -0.22,
|
||||
'arial black,avant garde': -0.17,
|
||||
'default': -0.16,
|
||||
},
|
||||
Lux: {
|
||||
'宋体,SimSun': -0.2,
|
||||
'微软雅黑,Microsoft YaHei': -0.2,
|
||||
'黑体, SimHei': -0.2,
|
||||
'隶书, SimLi': -0.2,
|
||||
'楷体,楷体_GB2312,SimKai': -0.2,
|
||||
'andale mono': -0.2,
|
||||
'arial,helvetica,sans-serif': -0.2,
|
||||
'comic sans ms': -0.2,
|
||||
'impact,chicago': -0.2,
|
||||
'times new roman': -0.2,
|
||||
'sans-serif': -0.2,
|
||||
'arial black,avant garde': -0.2,
|
||||
'default': -0.16,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
var TextRenderer = kity.createClass('TextRenderer', {
|
||||
base: Renderer,
|
||||
|
||||
create: function () {
|
||||
return new kity.Group().setId(utils.uuid('node_text'));
|
||||
},
|
||||
|
||||
update: function (textGroup, node) {
|
||||
function getDataOrStyle(name) {
|
||||
return node.getData(name) || node.getStyle(name);
|
||||
}
|
||||
|
||||
var nodeText = node.getText();
|
||||
var textArr = nodeText ? nodeText.split('\n') : [' '];
|
||||
|
||||
var lineHeight = node.getStyle('line-height');
|
||||
|
||||
var fontSize = getDataOrStyle('font-size');
|
||||
var fontFamily = getDataOrStyle('font-family') || 'default';
|
||||
|
||||
var height = lineHeight * fontSize * textArr.length - (lineHeight - 1) * fontSize;
|
||||
var yStart = -height / 2;
|
||||
var Browser = kity.Browser;
|
||||
var adjust;
|
||||
|
||||
if (Browser.chrome || Browser.opera || Browser.bd || Browser.lb === 'chrome') {
|
||||
adjust = FONT_ADJUST['chrome'][Browser.platform][fontFamily];
|
||||
} else if (Browser.gecko) {
|
||||
adjust = FONT_ADJUST['firefox'][Browser.platform][fontFamily];
|
||||
} else if (Browser.sg) {
|
||||
adjust = FONT_ADJUST['sg'][fontFamily];
|
||||
} else if (Browser.safari) {
|
||||
adjust = FONT_ADJUST['safari'][fontFamily];
|
||||
} else if (Browser.ie) {
|
||||
adjust = FONT_ADJUST['ie'][Browser.version][fontFamily];
|
||||
} else if (Browser.edge) {
|
||||
adjust = FONT_ADJUST['edge'][fontFamily];
|
||||
} else if (Browser.lb) {
|
||||
// 猎豹浏览器的ie内核兼容性模式下
|
||||
adjust = 0.9;
|
||||
}
|
||||
|
||||
textGroup.setTranslate(0, (adjust || 0) * fontSize);
|
||||
|
||||
var rBox = new kity.Box(),
|
||||
r = Math.round;
|
||||
|
||||
this.setTextStyle(node, textGroup);
|
||||
|
||||
var textLength = textArr.length;
|
||||
|
||||
var textGroupLength = textGroup.getItems().length;
|
||||
|
||||
var i, ci, textShape, text;
|
||||
|
||||
if (textLength < textGroupLength) {
|
||||
for (i = textLength, ci; (ci = textGroup.getItem(i)); ) {
|
||||
textGroup.removeItem(i);
|
||||
}
|
||||
} else if (textLength > textGroupLength) {
|
||||
var growth = textLength - textGroupLength;
|
||||
while (growth--) {
|
||||
textShape = new kity.Text().setAttr('text-rendering', 'inherit');
|
||||
if (kity.Browser.ie || kity.Browser.edge) {
|
||||
textShape.setVerticalAlign('top');
|
||||
} else {
|
||||
textShape.setAttr('dominant-baseline', 'text-before-edge');
|
||||
}
|
||||
textGroup.addItem(textShape);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0, text, textShape; (text = textArr[i]), (textShape = textGroup.getItem(i)); i++) {
|
||||
textShape.setContent(text);
|
||||
if (kity.Browser.ie || kity.Browser.edge) {
|
||||
textShape.fixPosition();
|
||||
}
|
||||
}
|
||||
|
||||
this.setTextStyle(node, textGroup);
|
||||
|
||||
var textHash =
|
||||
node.getText() + ['font-size', 'font-name', 'font-weight', 'font-style'].map(getDataOrStyle).join('/');
|
||||
|
||||
if (node._currentTextHash == textHash && node._currentTextGroupBox) return node._currentTextGroupBox;
|
||||
|
||||
node._currentTextHash = textHash;
|
||||
|
||||
return function () {
|
||||
textGroup.eachItem(function (i, textShape) {
|
||||
var y = yStart + i * fontSize * lineHeight;
|
||||
|
||||
textShape.setY(y);
|
||||
var bbox = textShape.getBoundaryBox();
|
||||
rBox = rBox.merge(new kity.Box(0, y, (bbox.height && bbox.width) || 1, fontSize));
|
||||
});
|
||||
|
||||
var nBox = new kity.Box(r(rBox.x), r(rBox.y), r(rBox.width), r(rBox.height));
|
||||
|
||||
node._currentTextGroupBox = nBox;
|
||||
return nBox;
|
||||
};
|
||||
},
|
||||
|
||||
setTextStyle: function (node, text) {
|
||||
var hooks = TextRenderer._styleHooks;
|
||||
hooks.forEach(function (hook) {
|
||||
hook(node, text);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
var TextCommand = kity.createClass({
|
||||
base: Command,
|
||||
execute: function (minder, text) {
|
||||
var node = minder.getSelectedNode();
|
||||
if (node) {
|
||||
node.setText(text);
|
||||
node.render();
|
||||
minder.layout();
|
||||
}
|
||||
},
|
||||
queryState: function (minder) {
|
||||
return minder.getSelectedNodes().length == 1 ? 0 : -1;
|
||||
},
|
||||
queryValue: function (minder) {
|
||||
var node = minder.getSelectedNode();
|
||||
return node ? node.getText() : null;
|
||||
},
|
||||
});
|
||||
|
||||
utils.extend(TextRenderer, {
|
||||
_styleHooks: [],
|
||||
|
||||
registerStyleHook: function (fn) {
|
||||
TextRenderer._styleHooks.push(fn);
|
||||
},
|
||||
});
|
||||
|
||||
kity.extendClass(MinderNode, {
|
||||
getTextGroup: function () {
|
||||
return this.getRenderer('TextRenderer').getRenderShape();
|
||||
},
|
||||
});
|
||||
|
||||
Module.register('text', {
|
||||
commands: {
|
||||
text: TextCommand,
|
||||
},
|
||||
renderers: {
|
||||
center: TextRenderer,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = TextRenderer;
|
||||
});
|
|
@ -0,0 +1,404 @@
|
|||
/* eslint-disable */
|
||||
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');
|
||||
var Renderer = require('../core/render');
|
||||
|
||||
var ViewDragger = kity.createClass('ViewDragger', {
|
||||
constructor: function (minder) {
|
||||
this._minder = minder;
|
||||
this._enabled = false;
|
||||
this._bind();
|
||||
var me = this;
|
||||
this._minder.getViewDragger = function () {
|
||||
return me;
|
||||
};
|
||||
this.setEnabled(false);
|
||||
},
|
||||
|
||||
isEnabled: function () {
|
||||
return this._enabled;
|
||||
},
|
||||
|
||||
setEnabled: function (value) {
|
||||
var paper = this._minder.getPaper();
|
||||
paper.setStyle('cursor', value ? 'pointer' : 'default');
|
||||
paper.setStyle('cursor', value ? '-webkit-grab' : 'default');
|
||||
this._enabled = value;
|
||||
},
|
||||
timeline: function () {
|
||||
return this._moveTimeline;
|
||||
},
|
||||
|
||||
move: function (offset, duration) {
|
||||
var minder = this._minder;
|
||||
|
||||
var targetPosition = this.getMovement().offset(offset);
|
||||
|
||||
this.moveTo(targetPosition, duration);
|
||||
},
|
||||
|
||||
moveTo: function (position, duration) {
|
||||
if (duration) {
|
||||
var dragger = this;
|
||||
|
||||
if (this._moveTimeline) this._moveTimeline.stop();
|
||||
|
||||
this._moveTimeline = this._minder
|
||||
.getRenderContainer()
|
||||
.animate(
|
||||
new kity.Animator(this.getMovement(), position, function (target, value) {
|
||||
dragger.moveTo(value);
|
||||
}),
|
||||
duration,
|
||||
'easeOutCubic'
|
||||
)
|
||||
.timeline();
|
||||
|
||||
this._moveTimeline.on('finish', function () {
|
||||
dragger._moveTimeline = null;
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
this._minder.getRenderContainer().setTranslate(position.round());
|
||||
this._minder.fire('viewchange');
|
||||
},
|
||||
|
||||
getMovement: function () {
|
||||
var translate = this._minder.getRenderContainer().transform.translate;
|
||||
return translate ? translate[0] : new kity.Point();
|
||||
},
|
||||
|
||||
getView: function () {
|
||||
var minder = this._minder;
|
||||
var c = minder._lastClientSize || {
|
||||
width: minder.getRenderTarget().clientWidth,
|
||||
height: minder.getRenderTarget().clientHeight,
|
||||
};
|
||||
var m = this.getMovement();
|
||||
var box = new kity.Box(0, 0, c.width, c.height);
|
||||
var viewMatrix = minder.getPaper().getViewPortMatrix();
|
||||
return viewMatrix.inverse().translate(-m.x, -m.y).transformBox(box);
|
||||
},
|
||||
|
||||
_bind: function () {
|
||||
var dragger = this,
|
||||
isTempDrag = false,
|
||||
lastPosition = null,
|
||||
currentPosition = null;
|
||||
|
||||
function dragEnd(e) {
|
||||
if (!lastPosition) return;
|
||||
|
||||
lastPosition = null;
|
||||
|
||||
e.stopPropagation();
|
||||
|
||||
// 临时拖动需要还原状态
|
||||
if (isTempDrag) {
|
||||
dragger.setEnabled(false);
|
||||
isTempDrag = false;
|
||||
if (dragger._minder.getStatus() == 'hand') dragger._minder.rollbackStatus();
|
||||
}
|
||||
var paper = dragger._minder.getPaper();
|
||||
paper.setStyle('cursor', dragger._minder.getStatus() == 'hand' ? '-webkit-grab' : 'default');
|
||||
|
||||
dragger._minder.fire('viewchanged');
|
||||
}
|
||||
|
||||
this._minder
|
||||
.on(
|
||||
'normal.mousedown normal.touchstart ' +
|
||||
'inputready.mousedown inputready.touchstart ' +
|
||||
'readonly.mousedown readonly.touchstart',
|
||||
function (e) {
|
||||
if (e.originEvent.button == 2) {
|
||||
e.originEvent.preventDefault(); // 阻止中键拉动
|
||||
}
|
||||
// 点击未选中的根节点临时开启
|
||||
if (e.getTargetNode() == this.getRoot() || e.originEvent.button == 2 || e.originEvent.altKey) {
|
||||
lastPosition = e.getPosition('view');
|
||||
isTempDrag = true;
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
.on(
|
||||
'normal.mousemove normal.touchmove ' +
|
||||
'readonly.mousemove readonly.touchmove ' +
|
||||
'inputready.mousemove inputready.touchmove',
|
||||
function (e) {
|
||||
if (e.type == 'touchmove') {
|
||||
e.preventDefault(); // 阻止浏览器的后退事件
|
||||
}
|
||||
if (!isTempDrag) return;
|
||||
var offset = kity.Vector.fromPoints(lastPosition, e.getPosition('view'));
|
||||
if (offset.length() > 10) {
|
||||
this.setStatus('hand', true);
|
||||
var paper = dragger._minder.getPaper();
|
||||
paper.setStyle('cursor', '-webkit-grabbing');
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
.on('hand.beforemousedown hand.beforetouchstart', function (e) {
|
||||
// 已经被用户打开拖放模式
|
||||
if (dragger.isEnabled()) {
|
||||
lastPosition = e.getPosition('view');
|
||||
e.stopPropagation();
|
||||
var paper = dragger._minder.getPaper();
|
||||
paper.setStyle('cursor', '-webkit-grabbing');
|
||||
}
|
||||
})
|
||||
|
||||
.on('hand.beforemousemove hand.beforetouchmove', function (e) {
|
||||
if (lastPosition) {
|
||||
currentPosition = e.getPosition('view');
|
||||
|
||||
// 当前偏移加上历史偏移
|
||||
var offset = kity.Vector.fromPoints(lastPosition, currentPosition);
|
||||
dragger.move(offset);
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
e.originEvent.preventDefault();
|
||||
lastPosition = currentPosition;
|
||||
}
|
||||
})
|
||||
|
||||
.on('mouseup touchend', dragEnd);
|
||||
|
||||
window.addEventListener('mouseup', dragEnd);
|
||||
this._minder.on('contextmenu', function (e) {
|
||||
e.preventDefault();
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
Module.register('View', function () {
|
||||
var km = this;
|
||||
|
||||
/**
|
||||
* @command Hand
|
||||
* @description 切换抓手状态,抓手状态下,鼠标拖动将拖动视野,而不是创建选区
|
||||
* @state
|
||||
* 0: 当前不是抓手状态
|
||||
* 1: 当前是抓手状态
|
||||
*/
|
||||
var ToggleHandCommand = kity.createClass('ToggleHandCommand', {
|
||||
base: Command,
|
||||
execute: function (minder) {
|
||||
if (minder.getStatus() != 'hand') {
|
||||
minder.setStatus('hand', true);
|
||||
} else {
|
||||
minder.rollbackStatus();
|
||||
}
|
||||
this.setContentChanged(false);
|
||||
},
|
||||
queryState: function (minder) {
|
||||
return minder.getStatus() == 'hand' ? 1 : 0;
|
||||
},
|
||||
enableReadOnly: true,
|
||||
});
|
||||
|
||||
/**
|
||||
* @command Camera
|
||||
* @description 设置当前视野的中心位置到某个节点上
|
||||
* @param {kityminder.MinderNode} focusNode 要定位的节点
|
||||
* @param {number} duration 设置视野移动的动画时长(单位 ms),设置为 0 不使用动画
|
||||
* @state
|
||||
* 0: 始终可用
|
||||
*/
|
||||
var CameraCommand = kity.createClass('CameraCommand', {
|
||||
base: Command,
|
||||
execute: function (km, focusNode) {
|
||||
const dragger = km._viewDragger;
|
||||
const duration = km.getOption('viewAnimationDuration');
|
||||
let dx = 0;
|
||||
let dy = 0;
|
||||
|
||||
if (!focusNode || focusNode.type === 'root') {
|
||||
// 默认居中
|
||||
const parentNode = km.getPaper().node;
|
||||
const shapeNode = km.getRoot().rc.container.node;
|
||||
const { width: pw, height: ph, x: px, y: py } = parentNode.getBoundingClientRect();
|
||||
const { width: sw, height: sh, x, y } = shapeNode.getBBox();
|
||||
dx = pw / 2 - x - sw / 2;
|
||||
dy = ph / 2 - y - sh / 2;
|
||||
dragger.moveTo(new kity.Point(dx, dy), duration);
|
||||
} else {
|
||||
var viewport = km.getPaper().getViewPort();
|
||||
var offset = focusNode.getRenderContainer().getRenderBox('view');
|
||||
dx = viewport.center.x - offset.x - offset.width / 2;
|
||||
dy = viewport.center.y - offset.y;
|
||||
dragger.move(new kity.Point(dx, dy), duration);
|
||||
}
|
||||
|
||||
this.setContentChanged(false);
|
||||
},
|
||||
enableReadOnly: true,
|
||||
});
|
||||
|
||||
/**
|
||||
* @command Move
|
||||
* @description 指定方向移动当前视野
|
||||
* @param {string} dir 移动方向
|
||||
* 取值为 'left',视野向左移动一半
|
||||
* 取值为 'right',视野向右移动一半
|
||||
* 取值为 'up',视野向上移动一半
|
||||
* 取值为 'down',视野向下移动一半
|
||||
* @param {number} duration 视野移动的动画时长(单位 ms),设置为 0 不使用动画
|
||||
* @state
|
||||
* 0: 始终可用
|
||||
*/
|
||||
var MoveCommand = kity.createClass('MoveCommand', {
|
||||
base: Command,
|
||||
|
||||
execute: function (km, dir) {
|
||||
var dragger = km._viewDragger;
|
||||
var size = km._lastClientSize;
|
||||
var duration = km.getOption('viewAnimationDuration');
|
||||
switch (dir) {
|
||||
case 'up':
|
||||
dragger.move(new kity.Point(0, size.height / 2), duration);
|
||||
break;
|
||||
case 'down':
|
||||
dragger.move(new kity.Point(0, -size.height / 2), duration);
|
||||
break;
|
||||
case 'left':
|
||||
dragger.move(new kity.Point(size.width / 2, 0), duration);
|
||||
break;
|
||||
case 'right':
|
||||
dragger.move(new kity.Point(-size.width / 2, 0), duration);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
enableReadOnly: true,
|
||||
});
|
||||
|
||||
return {
|
||||
init: function () {
|
||||
this._viewDragger = new ViewDragger(this);
|
||||
},
|
||||
commands: {
|
||||
hand: ToggleHandCommand,
|
||||
camera: CameraCommand,
|
||||
move: MoveCommand,
|
||||
},
|
||||
events: {
|
||||
'statuschange': function (e) {
|
||||
this._viewDragger.setEnabled(e.currentStatus == 'hand');
|
||||
},
|
||||
'mousewheel': function (e) {
|
||||
var dx, dy;
|
||||
e = e.originEvent;
|
||||
if (e.ctrlKey || e.shiftKey) return;
|
||||
if ('wheelDeltaX' in e) {
|
||||
dx = e.wheelDeltaX || 0;
|
||||
dy = e.wheelDeltaY || 0;
|
||||
} else {
|
||||
dx = 0;
|
||||
dy = e.wheelDelta;
|
||||
}
|
||||
|
||||
this._viewDragger.move({
|
||||
x: dx / 2.5,
|
||||
y: dy / 2.5,
|
||||
});
|
||||
|
||||
var me = this;
|
||||
clearTimeout(this._mousewheeltimer);
|
||||
this._mousewheeltimer = setTimeout(function () {
|
||||
me.fire('viewchanged');
|
||||
}, 100);
|
||||
|
||||
e.preventDefault();
|
||||
},
|
||||
'normal.dblclick readonly.dblclick': function (e) {
|
||||
if (e.kityEvent.targetShape instanceof kity.Paper) {
|
||||
this.execCommand('camera', this.getRoot(), 800);
|
||||
}
|
||||
},
|
||||
'paperrender finishInitHook': function () {
|
||||
if (!this.getRenderTarget()) {
|
||||
return;
|
||||
}
|
||||
this.execCommand('camera', null, 0);
|
||||
this._lastClientSize = {
|
||||
width: this.getRenderTarget().clientWidth,
|
||||
height: this.getRenderTarget().clientHeight,
|
||||
};
|
||||
},
|
||||
'resize': function (e) {
|
||||
var a = {
|
||||
width: this.getRenderTarget().clientWidth,
|
||||
height: this.getRenderTarget().clientHeight,
|
||||
},
|
||||
b = this._lastClientSize;
|
||||
this._viewDragger.move(new kity.Point(((a.width - b.width) / 2) | 0, ((a.height - b.height) / 2) | 0));
|
||||
this._lastClientSize = a;
|
||||
},
|
||||
'selectionchange layoutallfinish': function (e) {
|
||||
var selected = this.getSelectedNode();
|
||||
var minder = this;
|
||||
|
||||
/*
|
||||
* Added by zhangbobell 2015.9.9
|
||||
* windows 10 的 edge 浏览器在全部动画停止后,优先级图标不显示 text,
|
||||
* 因此再次触发一次 render 事件,让浏览器重绘
|
||||
* */
|
||||
if (kity.Browser.edge) {
|
||||
this.fire('paperrender');
|
||||
}
|
||||
if (!selected) return;
|
||||
|
||||
var dragger = this._viewDragger;
|
||||
var timeline = dragger.timeline();
|
||||
|
||||
/*
|
||||
* Added by zhangbobell 2015.09.25
|
||||
* 如果之前有动画,那么就先暂时返回,等之前动画结束之后再次执行本函数
|
||||
* 以防止 view 动画变动了位置,导致本函数执行的时候位置计算不对
|
||||
*
|
||||
* fixed bug : 初始化的时候中心节点位置不固定(有的时候在左上角,有的时候在中心)
|
||||
* */
|
||||
if (timeline) {
|
||||
timeline.on('finish', function () {
|
||||
minder.fire('selectionchange');
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var view = dragger.getView();
|
||||
var focus = selected.getLayoutBox();
|
||||
var space = 50;
|
||||
var dx = 0,
|
||||
dy = 0;
|
||||
|
||||
if (focus.right > view.right) {
|
||||
dx += view.right - focus.right - space;
|
||||
} else if (focus.left < view.left) {
|
||||
dx += view.left - focus.left + space;
|
||||
}
|
||||
|
||||
if (focus.bottom > view.bottom) {
|
||||
dy += view.bottom - focus.bottom - space;
|
||||
}
|
||||
if (focus.top < view.top) {
|
||||
dy += view.top - focus.top + space;
|
||||
}
|
||||
|
||||
if (dx || dy) dragger.move(new kity.Point(dx, dy), 100);
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
});
|
|
@ -0,0 +1,201 @@
|
|||
/* eslint-disable */
|
||||
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');
|
||||
var Renderer = require('../core/render');
|
||||
|
||||
Module.register('Zoom', function () {
|
||||
var me = this;
|
||||
|
||||
var timeline;
|
||||
|
||||
function setTextRendering() {
|
||||
var value = me._zoomValue >= 100 ? 'optimize-speed' : 'geometricPrecision';
|
||||
me.getRenderContainer().setAttr('text-rendering', value);
|
||||
}
|
||||
|
||||
function fixPaperCTM(paper) {
|
||||
var node = paper.shapeNode;
|
||||
var ctm = node.getCTM();
|
||||
var matrix = new kity.Matrix(ctm.a, ctm.b, ctm.c, ctm.d, (ctm.e | 0) + 0.5, (ctm.f | 0) + 0.5);
|
||||
node.setAttribute('transform', 'matrix(' + matrix.toString() + ')');
|
||||
}
|
||||
|
||||
kity.extendClass(Minder, {
|
||||
zoom: function (value) {
|
||||
var paper = this.getPaper();
|
||||
var viewport = paper.getViewPort();
|
||||
viewport.zoom = value / 100;
|
||||
viewport.center = {
|
||||
x: viewport.center.x,
|
||||
y: viewport.center.y,
|
||||
};
|
||||
paper.setViewPort(viewport);
|
||||
if (value == 100) fixPaperCTM(paper);
|
||||
},
|
||||
getZoomValue: function () {
|
||||
return this._zoomValue;
|
||||
},
|
||||
});
|
||||
|
||||
function zoomMinder(minder, value) {
|
||||
var paper = minder.getPaper();
|
||||
var viewport = paper.getViewPort();
|
||||
|
||||
if (!value) return;
|
||||
|
||||
setTextRendering();
|
||||
|
||||
var duration = minder.getOption('zoomAnimationDuration');
|
||||
if (minder.getRoot().getComplex() > 200 || !duration) {
|
||||
minder._zoomValue = value;
|
||||
minder.zoom(value);
|
||||
minder.fire('viewchange');
|
||||
} else {
|
||||
var animator = new kity.Animator({
|
||||
beginValue: minder._zoomValue,
|
||||
finishValue: value,
|
||||
setter: function (target, value) {
|
||||
target.zoom(value);
|
||||
},
|
||||
});
|
||||
minder._zoomValue = value;
|
||||
if (timeline) {
|
||||
timeline.pause();
|
||||
}
|
||||
timeline = animator.start(minder, duration, 'easeInOutSine');
|
||||
timeline.on('finish', function () {
|
||||
minder.fire('viewchange');
|
||||
});
|
||||
}
|
||||
minder.fire('zoom', {
|
||||
zoom: value,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @command Zoom
|
||||
* @description 缩放当前的视野到一定的比例(百分比)
|
||||
* @param {number} value 设置的比例,取值 100 则为原尺寸
|
||||
* @state
|
||||
* 0: 始终可用
|
||||
*/
|
||||
var ZoomCommand = kity.createClass('Zoom', {
|
||||
base: Command,
|
||||
execute: zoomMinder,
|
||||
queryValue: function (minder) {
|
||||
return minder._zoomValue;
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* @command ZoomIn
|
||||
* @description 放大当前的视野到下一个比例等级(百分比)
|
||||
* @shortcut =
|
||||
* @state
|
||||
* 0: 如果当前脑图的配置中还有下一个比例等级
|
||||
* -1: 其它情况
|
||||
*/
|
||||
var ZoomInCommand = kity.createClass('ZoomInCommand', {
|
||||
base: Command,
|
||||
execute: function (minder) {
|
||||
zoomMinder(minder, this.nextValue(minder));
|
||||
},
|
||||
queryState: function (minder) {
|
||||
return +!this.nextValue(minder);
|
||||
},
|
||||
nextValue: function (minder) {
|
||||
var stack = minder.getOption('zoom'),
|
||||
i;
|
||||
for (i = 0; i < stack.length; i++) {
|
||||
if (stack[i] > minder._zoomValue) return stack[i];
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
enableReadOnly: true,
|
||||
});
|
||||
|
||||
/**
|
||||
* @command ZoomOut
|
||||
* @description 缩小当前的视野到上一个比例等级(百分比)
|
||||
* @shortcut -
|
||||
* @state
|
||||
* 0: 如果当前脑图的配置中还有上一个比例等级
|
||||
* -1: 其它情况
|
||||
*/
|
||||
var ZoomOutCommand = kity.createClass('ZoomOutCommand', {
|
||||
base: Command,
|
||||
execute: function (minder) {
|
||||
zoomMinder(minder, this.nextValue(minder));
|
||||
},
|
||||
queryState: function (minder) {
|
||||
return +!this.nextValue(minder);
|
||||
},
|
||||
nextValue: function (minder) {
|
||||
var stack = minder.getOption('zoom'),
|
||||
i;
|
||||
for (i = stack.length - 1; i >= 0; i--) {
|
||||
if (stack[i] < minder._zoomValue) return stack[i];
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
enableReadOnly: true,
|
||||
});
|
||||
|
||||
return {
|
||||
init: function () {
|
||||
this._zoomValue = 100;
|
||||
this.setDefaultOptions({
|
||||
zoom: [10, 20, 50, 100, 200],
|
||||
});
|
||||
setTextRendering();
|
||||
},
|
||||
commands: {
|
||||
zoomin: ZoomInCommand,
|
||||
zoomout: ZoomOutCommand,
|
||||
zoom: ZoomCommand,
|
||||
},
|
||||
events: {
|
||||
'normal.mousewheel readonly.mousewheel': function (e) {
|
||||
if (!e.originEvent.ctrlKey && !e.originEvent.metaKey) return;
|
||||
|
||||
var delta = e.originEvent.wheelDelta;
|
||||
var me = this;
|
||||
|
||||
if (!kity.Browser.mac) {
|
||||
delta = -delta;
|
||||
}
|
||||
|
||||
// 稀释
|
||||
if (Math.abs(delta) > 100) {
|
||||
clearTimeout(this._wheelZoomTimeout);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
this._wheelZoomTimeout = setTimeout(function () {
|
||||
var value;
|
||||
var lastValue = me.getPaper()._zoom || 1;
|
||||
if (delta < 0) {
|
||||
me.execCommand('zoomin');
|
||||
} else if (delta > 0) {
|
||||
me.execCommand('zoomout');
|
||||
}
|
||||
}, 100);
|
||||
|
||||
e.originEvent.preventDefault();
|
||||
},
|
||||
},
|
||||
|
||||
commandShortcutKeys: {
|
||||
zoomin: 'ctrl+=',
|
||||
zoomout: 'ctrl+-',
|
||||
},
|
||||
};
|
||||
});
|
||||
});
|
|
@ -0,0 +1,22 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
var data = require('../core/data');
|
||||
|
||||
data.registerProtocol(
|
||||
'json',
|
||||
(module.exports = {
|
||||
fileDescription: 'KityMinder 格式',
|
||||
fileExtension: '.km',
|
||||
dataType: 'text',
|
||||
mineType: 'application/json',
|
||||
|
||||
encode: function (json) {
|
||||
return JSON.stringify(json);
|
||||
},
|
||||
|
||||
decode: function (local) {
|
||||
return JSON.parse(local);
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
|
@ -0,0 +1,168 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
var data = require('../core/data');
|
||||
var LINE_ENDING_SPLITER = /\r\n|\r|\n/;
|
||||
var EMPTY_LINE = '';
|
||||
var NOTE_MARK_START = '<!--Note-->';
|
||||
var NOTE_MARK_CLOSE = '<!--/Note-->';
|
||||
|
||||
function encode(json) {
|
||||
return _build(json, 1).join('\n');
|
||||
}
|
||||
|
||||
function _build(node, level) {
|
||||
var lines = [];
|
||||
|
||||
level = level || 1;
|
||||
|
||||
var sharps = _generateHeaderSharp(level);
|
||||
lines.push(sharps + ' ' + node.data.text);
|
||||
lines.push(EMPTY_LINE);
|
||||
|
||||
var note = node.data.note;
|
||||
if (note) {
|
||||
var hasSharp = /^#/.test(note);
|
||||
if (hasSharp) {
|
||||
lines.push(NOTE_MARK_START);
|
||||
note = note.replace(/^#+/gm, function ($0) {
|
||||
return sharps + $0;
|
||||
});
|
||||
}
|
||||
lines.push(note);
|
||||
if (hasSharp) {
|
||||
lines.push(NOTE_MARK_CLOSE);
|
||||
}
|
||||
lines.push(EMPTY_LINE);
|
||||
}
|
||||
|
||||
if (node.children)
|
||||
node.children.forEach(function (child) {
|
||||
lines = lines.concat(_build(child, level + 1));
|
||||
});
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
function _generateHeaderSharp(level) {
|
||||
var sharps = '';
|
||||
while (level--) sharps += '#';
|
||||
return sharps;
|
||||
}
|
||||
|
||||
function decode(markdown) {
|
||||
var json,
|
||||
parentMap = {},
|
||||
lines,
|
||||
line,
|
||||
lineInfo,
|
||||
level,
|
||||
node,
|
||||
parent,
|
||||
noteProgress,
|
||||
codeBlock;
|
||||
|
||||
// 一级标题转换 `{title}\n===` => `# {title}`
|
||||
markdown = markdown.replace(/^(.+)\n={3,}/, function ($0, $1) {
|
||||
return '# ' + $1;
|
||||
});
|
||||
|
||||
lines = markdown.split(LINE_ENDING_SPLITER);
|
||||
|
||||
// 按行分析
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
line = lines[i];
|
||||
|
||||
lineInfo = _resolveLine(line);
|
||||
|
||||
// 备注标记处理
|
||||
if (lineInfo.noteClose) {
|
||||
noteProgress = false;
|
||||
continue;
|
||||
} else if (lineInfo.noteStart) {
|
||||
noteProgress = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 代码块处理
|
||||
codeBlock = lineInfo.codeBlock ? !codeBlock : codeBlock;
|
||||
|
||||
// 备注条件:备注标签中,非标题定义,或标题越位
|
||||
if (noteProgress || codeBlock || !lineInfo.level || lineInfo.level > level + 1) {
|
||||
if (node) _pushNote(node, line);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 标题处理
|
||||
level = lineInfo.level;
|
||||
node = _initNode(lineInfo.content, parentMap[level - 1]);
|
||||
parentMap[level] = node;
|
||||
}
|
||||
|
||||
_cleanUp(parentMap[1]);
|
||||
return parentMap[1];
|
||||
}
|
||||
|
||||
function _initNode(text, parent) {
|
||||
var node = {
|
||||
data: {
|
||||
text: text,
|
||||
note: '',
|
||||
},
|
||||
};
|
||||
if (parent) {
|
||||
if (parent.children) parent.children.push(node);
|
||||
else parent.children = [node];
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
function _pushNote(node, line) {
|
||||
node.data.note += line + '\n';
|
||||
}
|
||||
|
||||
function _isEmpty(line) {
|
||||
return !/\S/.test(line);
|
||||
}
|
||||
|
||||
function _resolveLine(line) {
|
||||
var match = /^(#+)?\s*(.*)$/.exec(line);
|
||||
return {
|
||||
level: (match[1] && match[1].length) || null,
|
||||
content: match[2],
|
||||
noteStart: line == NOTE_MARK_START,
|
||||
noteClose: line == NOTE_MARK_CLOSE,
|
||||
codeBlock: /^\s*```/.test(line),
|
||||
};
|
||||
}
|
||||
|
||||
function _cleanUp(node) {
|
||||
if (!/\S/.test(node.data.note)) {
|
||||
node.data.note = null;
|
||||
delete node.data.note;
|
||||
} else {
|
||||
var notes = node.data.note.split('\n');
|
||||
while (notes.length && !/\S/.test(notes[0])) notes.shift();
|
||||
while (notes.length && !/\S/.test(notes[notes.length - 1])) notes.pop();
|
||||
node.data.note = notes.join('\n');
|
||||
}
|
||||
if (node.children) node.children.forEach(_cleanUp);
|
||||
}
|
||||
|
||||
data.registerProtocol(
|
||||
'markdown',
|
||||
(module.exports = {
|
||||
fileDescription: 'Markdown/GFM 格式',
|
||||
fileExtension: '.md',
|
||||
mineType: 'text/markdown',
|
||||
dataType: 'text',
|
||||
|
||||
encode: function (json) {
|
||||
return encode(json.root);
|
||||
},
|
||||
|
||||
decode: function (markdown) {
|
||||
return decode(markdown);
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
|
@ -0,0 +1,290 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('../core/kity');
|
||||
var data = require('../core/data');
|
||||
var Promise = require('../core/promise');
|
||||
|
||||
var DomURL = window.URL || window.webkitURL || window;
|
||||
|
||||
function loadImage(info, callback) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
var image = document.createElement('img');
|
||||
image.onload = function () {
|
||||
resolve({
|
||||
element: this,
|
||||
x: info.x,
|
||||
y: info.y,
|
||||
width: info.width,
|
||||
height: info.height,
|
||||
});
|
||||
};
|
||||
image.onerror = function (err) {
|
||||
reject(err);
|
||||
};
|
||||
|
||||
image.crossOrigin = 'anonymous';
|
||||
image.src = info.url;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* xhrLoadImage: 通过 xhr 加载保存在 BOS 上的图片
|
||||
* @note: BOS 上的 CORS 策略是取 headers 里面的 Origin 字段进行判断
|
||||
* 而通过 image 的 src 的方式是无法传递 origin 的,因此需要通过 xhr 进行
|
||||
*/
|
||||
function xhrLoadImage(info, callback) {
|
||||
return Promise(function (resolve, reject) {
|
||||
var xmlHttp = new XMLHttpRequest();
|
||||
|
||||
xmlHttp.open('GET', info.url + '?_=' + Date.now(), true);
|
||||
xmlHttp.responseType = 'blob';
|
||||
xmlHttp.onreadystatechange = function () {
|
||||
if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
|
||||
var blob = xmlHttp.response;
|
||||
|
||||
var image = document.createElement('img');
|
||||
|
||||
image.src = DomURL.createObjectURL(blob);
|
||||
image.onload = function () {
|
||||
DomURL.revokeObjectURL(image.src);
|
||||
resolve({
|
||||
element: image,
|
||||
x: info.x,
|
||||
y: info.y,
|
||||
width: info.width,
|
||||
height: info.height,
|
||||
});
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
xmlHttp.send();
|
||||
});
|
||||
}
|
||||
|
||||
function getSVGInfo(minder) {
|
||||
var paper = minder.getPaper(),
|
||||
paperTransform,
|
||||
domContainer = paper.container,
|
||||
svgXml,
|
||||
svgContainer,
|
||||
svgDom,
|
||||
renderContainer = minder.getRenderContainer(),
|
||||
renderBox = renderContainer.getRenderBox(),
|
||||
width = renderBox.width + 1,
|
||||
height = renderBox.height + 1,
|
||||
blob,
|
||||
svgUrl,
|
||||
img;
|
||||
|
||||
// 保存原始变换,并且移动到合适的位置
|
||||
paperTransform = paper.shapeNode.getAttribute('transform');
|
||||
paper.shapeNode.setAttribute('transform', 'translate(0.5, 0.5)');
|
||||
renderContainer.translate(-renderBox.x, -renderBox.y);
|
||||
|
||||
// 获取当前的 XML 代码
|
||||
svgXml = paper.container.innerHTML;
|
||||
|
||||
// 回复原始变换及位置
|
||||
renderContainer.translate(renderBox.x, renderBox.y);
|
||||
paper.shapeNode.setAttribute('transform', paperTransform);
|
||||
|
||||
// 过滤内容
|
||||
svgContainer = document.createElement('div');
|
||||
svgContainer.innerHTML = svgXml;
|
||||
svgDom = svgContainer.querySelector('svg');
|
||||
svgDom.setAttribute('width', renderBox.width + 1);
|
||||
svgDom.setAttribute('height', renderBox.height + 1);
|
||||
svgDom.setAttribute('style', 'font-family: Arial, "Microsoft Yahei","Heiti SC";');
|
||||
|
||||
svgContainer = document.createElement('div');
|
||||
svgContainer.appendChild(svgDom);
|
||||
|
||||
svgXml = svgContainer.innerHTML;
|
||||
|
||||
// Dummy IE
|
||||
svgXml = svgXml.replace(
|
||||
' xmlns="http://www.w3.org/2000/svg" ' +
|
||||
'xmlns:NS1="" NS1:ns1:xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:NS2="" NS2:xmlns:ns1=""',
|
||||
''
|
||||
);
|
||||
|
||||
// svg 含有 符号导出报错 Entity 'nbsp' not defined ,含有控制字符触发Load Image 会触发报错
|
||||
svgXml = svgXml.replace(/ |[\x00-\x1F\x7F-\x9F]/g, '');
|
||||
|
||||
// fix title issue in safari
|
||||
// @ http://stackoverflow.com/questions/30273775/namespace-prefix-ns1-for-href-on-tagelement-is-not-defined-setattributens
|
||||
svgXml = svgXml.replace(/NS\d+:title/gi, 'xlink:title');
|
||||
|
||||
blob = new Blob([svgXml], {
|
||||
type: 'image/svg+xml',
|
||||
});
|
||||
|
||||
svgUrl = DomURL.createObjectURL(blob);
|
||||
|
||||
//svgUrl = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svgXml);
|
||||
|
||||
var imagesInfo = [];
|
||||
|
||||
// 遍历取出图片信息
|
||||
traverse(minder.getRoot());
|
||||
|
||||
function traverse(node) {
|
||||
var nodeData = node.data;
|
||||
|
||||
if (nodeData.image) {
|
||||
minder.renderNode(node);
|
||||
var nodeData = node.data;
|
||||
var imageUrl = nodeData.image;
|
||||
var imageSize = nodeData.imageSize;
|
||||
var imageRenderBox = node.getRenderBox('ImageRenderer', minder.getRenderContainer());
|
||||
var imageInfo = {
|
||||
url: imageUrl,
|
||||
width: imageSize.width,
|
||||
height: imageSize.height,
|
||||
x: -renderContainer.getBoundaryBox().x + imageRenderBox.x,
|
||||
y: -renderContainer.getBoundaryBox().y + imageRenderBox.y,
|
||||
};
|
||||
|
||||
imagesInfo.push(imageInfo);
|
||||
}
|
||||
|
||||
// 若节点折叠,则直接返回
|
||||
if (nodeData.expandState === 'collapse') {
|
||||
return;
|
||||
}
|
||||
|
||||
var children = node.getChildren();
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
traverse(children[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
width: width,
|
||||
height: height,
|
||||
dataUrl: svgUrl,
|
||||
xml: svgXml,
|
||||
imagesInfo: imagesInfo,
|
||||
};
|
||||
}
|
||||
|
||||
function encode(json, minder, option) {
|
||||
var resultCallback;
|
||||
|
||||
/* 绘制 PNG 的画布及上下文 */
|
||||
var canvas = document.createElement('canvas');
|
||||
var ctx = canvas.getContext('2d');
|
||||
|
||||
/* 尝试获取背景图片 URL 或背景颜色 */
|
||||
var bgDeclare = minder.getStyle('background').toString();
|
||||
var bgUrl = /url\(\"(.+)\"\)/.exec(bgDeclare);
|
||||
var bgColor = kity.Color.parse(bgDeclare);
|
||||
|
||||
/* 获取 SVG 文件内容 */
|
||||
var svgInfo = getSVGInfo(minder);
|
||||
var width = option && option.width && option.width > svgInfo.width ? option.width : svgInfo.width;
|
||||
var height = option && option.height && option.height > svgInfo.height ? option.height : svgInfo.height;
|
||||
var offsetX = option && option.width && option.width > svgInfo.width ? (option.width - svgInfo.width) / 2 : 0;
|
||||
var offsetY = option && option.height && option.height > svgInfo.height ? (option.height - svgInfo.height) / 2 : 0;
|
||||
var svgDataUrl = svgInfo.dataUrl;
|
||||
var imagesInfo = svgInfo.imagesInfo;
|
||||
|
||||
/* 画布的填充大小 */
|
||||
var padding = 20;
|
||||
|
||||
canvas.width = width + padding * 2;
|
||||
canvas.height = height + padding * 2;
|
||||
|
||||
function fillBackground(ctx, style) {
|
||||
ctx.save();
|
||||
ctx.fillStyle = style;
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
function drawImage(ctx, image, x, y, width, height) {
|
||||
if (width && height) {
|
||||
ctx.drawImage(image, x + padding, y + padding, width, height);
|
||||
} else {
|
||||
ctx.drawImage(image, x + padding, y + padding);
|
||||
}
|
||||
}
|
||||
|
||||
function generateDataUrl(canvas) {
|
||||
return canvas.toDataURL('image/png');
|
||||
}
|
||||
|
||||
// 加载节点上的图片
|
||||
function loadImages(imagesInfo) {
|
||||
var imagePromises = imagesInfo.map(function (imageInfo) {
|
||||
return xhrLoadImage(imageInfo);
|
||||
});
|
||||
|
||||
return Promise.all(imagePromises);
|
||||
}
|
||||
|
||||
function drawSVG() {
|
||||
var svgData = { url: svgDataUrl };
|
||||
|
||||
return loadImage(svgData)
|
||||
.then(function ($image) {
|
||||
drawImage(ctx, $image.element, offsetX, offsetY, $image.width, $image.height);
|
||||
return loadImages(imagesInfo);
|
||||
})
|
||||
.then(
|
||||
function ($images) {
|
||||
for (var i = 0; i < $images.length; i++) {
|
||||
drawImage(
|
||||
ctx,
|
||||
$images[i].element,
|
||||
$images[i].x + offsetX,
|
||||
$images[i].y + offsetY,
|
||||
$images[i].width,
|
||||
$images[i].height
|
||||
);
|
||||
}
|
||||
|
||||
DomURL.revokeObjectURL(svgDataUrl);
|
||||
document.body.appendChild(canvas);
|
||||
var pngBase64 = generateDataUrl(canvas);
|
||||
|
||||
document.body.removeChild(canvas);
|
||||
return pngBase64;
|
||||
},
|
||||
function (err) {
|
||||
// 这里处理 reject,出错基本上是因为跨域,
|
||||
// 出错后依然导出,只不过没有图片。
|
||||
alert('脑图的节点中包含跨域图片,导出的 png 中节点图片不显示,你可以替换掉这些跨域的图片并重试。');
|
||||
DomURL.revokeObjectURL(svgDataUrl);
|
||||
document.body.appendChild(canvas);
|
||||
|
||||
var pngBase64 = generateDataUrl(canvas);
|
||||
document.body.removeChild(canvas);
|
||||
return pngBase64;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (bgUrl) {
|
||||
var bgInfo = { url: bgUrl[1] };
|
||||
return loadImage(bgInfo).then(function ($image) {
|
||||
fillBackground(ctx, ctx.createPattern($image.element, 'repeat'));
|
||||
return drawSVG();
|
||||
});
|
||||
} else {
|
||||
fillBackground(ctx, bgColor.toString());
|
||||
return drawSVG();
|
||||
}
|
||||
}
|
||||
data.registerProtocol(
|
||||
'png',
|
||||
(module.exports = {
|
||||
fileDescription: 'PNG 图片',
|
||||
fileExtension: '.png',
|
||||
mineType: 'image/png',
|
||||
dataType: 'base64',
|
||||
encode: encode,
|
||||
})
|
||||
);
|
||||
});
|
|
@ -0,0 +1,260 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
var data = require('../core/data');
|
||||
|
||||
/**
|
||||
* 导出svg时删除全部svg元素中的transform
|
||||
* @auth Naixor
|
||||
* @method removeTransform
|
||||
* @param {[type]} svgDom [description]
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
function cleanSVG(svgDom, x, y) {
|
||||
function getTransformToElement(target, source) {
|
||||
var matrix;
|
||||
try {
|
||||
matrix = source.getScreenCTM().inverse();
|
||||
} catch (e) {
|
||||
throw new Error("Can not inverse source element' ctm.");
|
||||
}
|
||||
return matrix.multiply(target.getScreenCTM());
|
||||
}
|
||||
function dealWithPath(d, dealWithPattern) {
|
||||
if (!(dealWithPattern instanceof Function)) {
|
||||
dealWithPattern = function () {};
|
||||
}
|
||||
var strArr = [],
|
||||
pattern = [],
|
||||
cache = [];
|
||||
for (var i = 0, l = d.length; i < l; i++) {
|
||||
switch (d[i]) {
|
||||
case 'M':
|
||||
case 'L':
|
||||
case 'T':
|
||||
case 'S':
|
||||
case 'A':
|
||||
case 'C':
|
||||
case 'H':
|
||||
case 'V':
|
||||
case 'Q': {
|
||||
if (cache.length) {
|
||||
pattern.push(cache.join(''));
|
||||
cache = [];
|
||||
}
|
||||
// 脑图的path格式真奇怪...偶尔就给我蹦出来一个"..V123 C..", 那空格几个意思 - -
|
||||
if (pattern[pattern.length - 1] === ',') {
|
||||
pattern.pop();
|
||||
}
|
||||
if (pattern.length) {
|
||||
dealWithPattern(pattern);
|
||||
strArr.push(pattern.join(''));
|
||||
pattern = [];
|
||||
}
|
||||
pattern.push(d[i]);
|
||||
break;
|
||||
}
|
||||
case 'Z':
|
||||
case 'z': {
|
||||
pattern.push(cache.join(''), d[i]);
|
||||
dealWithPattern(pattern);
|
||||
strArr.push(pattern.join(''));
|
||||
cache = [];
|
||||
pattern = [];
|
||||
break;
|
||||
}
|
||||
case '.':
|
||||
case 'e': {
|
||||
cache.push(d[i]);
|
||||
break;
|
||||
}
|
||||
case '-': {
|
||||
if (d[i - 1] !== 'e') {
|
||||
if (cache.length) {
|
||||
pattern.push(cache.join(''), ',');
|
||||
}
|
||||
cache = [];
|
||||
}
|
||||
cache.push('-');
|
||||
break;
|
||||
}
|
||||
case ' ':
|
||||
case ',': {
|
||||
if (cache.length) {
|
||||
pattern.push(cache.join(''), ',');
|
||||
cache = [];
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (/\d/.test(d[i])) {
|
||||
cache.push(d[i]);
|
||||
} else {
|
||||
// m a c s q h v l t z情况
|
||||
if (cache.length) {
|
||||
pattern.push(cache.join(''), d[i]);
|
||||
cache = [];
|
||||
} else {
|
||||
// 脑图的path格式真奇怪...偶尔就给我蹦出来一个"..V123 c..", 那空格几个意思 - -
|
||||
if (pattern[pattern.length - 1] === ',') {
|
||||
pattern.pop();
|
||||
}
|
||||
pattern.push(d[i]);
|
||||
}
|
||||
}
|
||||
if (i + 1 === l) {
|
||||
if (cache.length) {
|
||||
pattern.push(cache.join(''));
|
||||
}
|
||||
dealWithPattern(pattern);
|
||||
strArr.push(pattern.join(''));
|
||||
cache = null;
|
||||
pattern = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return strArr.join('');
|
||||
}
|
||||
|
||||
function replaceWithNode(svgNode, parentX, parentY) {
|
||||
if (!svgNode) {
|
||||
return;
|
||||
}
|
||||
if (svgNode.tagName === 'defs') {
|
||||
return;
|
||||
}
|
||||
if (svgNode.getAttribute('fill') === 'transparent') {
|
||||
svgNode.setAttribute('fill', 'none');
|
||||
}
|
||||
if (svgNode.getAttribute('marker-end')) {
|
||||
svgNode.removeAttribute('marker-end');
|
||||
}
|
||||
parentX = parentX || 0;
|
||||
parentY = parentY || 0;
|
||||
if (svgNode.getAttribute('transform')) {
|
||||
var ctm = getTransformToElement(svgNode, svgNode.parentElement);
|
||||
parentX -= ctm.e;
|
||||
parentY -= ctm.f;
|
||||
svgNode.removeAttribute('transform');
|
||||
}
|
||||
switch (svgNode.tagName.toLowerCase()) {
|
||||
case 'g':
|
||||
break;
|
||||
case 'path': {
|
||||
var d = svgNode.getAttribute('d');
|
||||
if (d) {
|
||||
d = dealWithPath(d, function (pattern) {
|
||||
switch (pattern[0]) {
|
||||
case 'V': {
|
||||
pattern[1] = +pattern[1] - parentY;
|
||||
break;
|
||||
}
|
||||
case 'H': {
|
||||
pattern[1] = +pattern[1] - parentX;
|
||||
break;
|
||||
}
|
||||
case 'M':
|
||||
case 'L':
|
||||
case 'T': {
|
||||
pattern[1] = +pattern[1] - parentX;
|
||||
pattern[3] = +pattern[3] - parentY;
|
||||
break;
|
||||
}
|
||||
case 'Q':
|
||||
case 'S': {
|
||||
pattern[1] = +pattern[1] - parentX;
|
||||
pattern[3] = +pattern[3] - parentY;
|
||||
pattern[5] = +pattern[5] - parentX;
|
||||
pattern[7] = +pattern[7] - parentY;
|
||||
break;
|
||||
}
|
||||
case 'A': {
|
||||
pattern[11] = +pattern[11] - parentX;
|
||||
pattern[13] = +pattern[13] - parentY;
|
||||
break;
|
||||
}
|
||||
case 'C': {
|
||||
pattern[1] = +pattern[1] - parentX;
|
||||
pattern[3] = +pattern[3] - parentY;
|
||||
pattern[5] = +pattern[5] - parentX;
|
||||
pattern[7] = +pattern[7] - parentY;
|
||||
pattern[9] = +pattern[9] - parentX;
|
||||
pattern[11] = +pattern[11] - parentY;
|
||||
}
|
||||
}
|
||||
});
|
||||
svgNode.setAttribute('d', d);
|
||||
svgNode.removeAttribute('transform');
|
||||
}
|
||||
return;
|
||||
}
|
||||
case 'image':
|
||||
case 'text': {
|
||||
if (parentX && parentY) {
|
||||
var x = +svgNode.getAttribute('x') || 0,
|
||||
y = +svgNode.getAttribute('y') || 0;
|
||||
svgNode.setAttribute('x', x - parentX);
|
||||
svgNode.setAttribute('y', y - parentY);
|
||||
}
|
||||
if (svgNode.getAttribute('dominant-baseline')) {
|
||||
svgNode.removeAttribute('dominant-baseline');
|
||||
svgNode.setAttribute('dy', '.8em');
|
||||
}
|
||||
svgNode.removeAttribute('transform');
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (svgNode.children) {
|
||||
for (var i = 0, l = svgNode.children.length; i < l; i++) {
|
||||
replaceWithNode(svgNode.children[i], parentX, parentY);
|
||||
}
|
||||
}
|
||||
}
|
||||
svgDom.style.visibility = 'hidden';
|
||||
replaceWithNode(svgDom, x || 0, y || 0);
|
||||
svgDom.style.visibility = 'visible';
|
||||
}
|
||||
data.registerProtocol(
|
||||
'svg',
|
||||
(module.exports = {
|
||||
fileDescription: 'SVG 矢量图',
|
||||
fileExtension: '.svg',
|
||||
mineType: 'image/svg+xml',
|
||||
dataType: 'text',
|
||||
encode: function (json, minder) {
|
||||
var paper = minder.getPaper(),
|
||||
paperTransform = paper.shapeNode.getAttribute('transform'),
|
||||
svgXml,
|
||||
svgContainer,
|
||||
svgDom,
|
||||
renderContainer = minder.getRenderContainer(),
|
||||
renderBox = renderContainer.getRenderBox(),
|
||||
transform = renderContainer.getTransform(),
|
||||
width = renderBox.width,
|
||||
height = renderBox.height,
|
||||
padding = 20;
|
||||
paper.shapeNode.setAttribute('transform', 'translate(0.5, 0.5)');
|
||||
svgXml = paper.container.innerHTML;
|
||||
paper.shapeNode.setAttribute('transform', paperTransform);
|
||||
svgContainer = document.createElement('div');
|
||||
document.body.appendChild(svgContainer);
|
||||
svgContainer.innerHTML = svgXml;
|
||||
svgDom = svgContainer.querySelector('svg');
|
||||
svgDom.setAttribute('width', (width + padding * 2) | 0);
|
||||
svgDom.setAttribute('height', (height + padding * 2) | 0);
|
||||
svgDom.setAttribute('style', 'background: ' + minder.getStyle('background')); //"font-family: Arial, Microsoft Yahei, Heiti SC; " +
|
||||
svgDom.setAttribute('viewBox', [0, 0, (width + padding * 2) | 0, (height + padding * 2) | 0].join(' '));
|
||||
tempSvgContainer = document.createElement('div');
|
||||
cleanSVG(svgDom, (renderBox.x - padding) | 0, (renderBox.y - padding) | 0);
|
||||
document.body.removeChild(svgContainer);
|
||||
tempSvgContainer.appendChild(svgDom);
|
||||
// need a xml with width and height
|
||||
svgXml = tempSvgContainer.innerHTML;
|
||||
// svg 含有 符号导出报错 Entity 'nbsp' not defined
|
||||
svgXml = svgXml.replace(/ /g, ' ');
|
||||
// svg 含有 符号导出报错 Entity 'nbsp' not defined
|
||||
return svgXml;
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
|
@ -0,0 +1,243 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
var data = require('../core/data');
|
||||
var Browser = require('../core/kity').Browser;
|
||||
|
||||
/**
|
||||
* @Desc: 增加对不容浏览器下节点中文本\t匹配的处理,不同浏览器下\t无法正确匹配,导致无法使用TAB来批量导入节点
|
||||
* @Editor: Naixor
|
||||
* @Date: 2015.9.17
|
||||
*/
|
||||
var LINE_ENDING = '\r',
|
||||
LINE_ENDING_SPLITER = /\r\n|\r|\n/,
|
||||
TAB_CHAR = (function (Browser) {
|
||||
if (Browser.gecko) {
|
||||
return {
|
||||
REGEXP: new RegExp('^(\t|' + String.fromCharCode(160, 160, 32, 160) + ')'),
|
||||
DELETE: new RegExp('^(\t|' + String.fromCharCode(160, 160, 32, 160) + ')+'),
|
||||
};
|
||||
} else if (Browser.ie || Browser.edge) {
|
||||
// ie系列和edge比较特别,\t在div中会被直接转义成SPACE故只好使用SPACE来做处理
|
||||
return {
|
||||
REGEXP: new RegExp('^(' + String.fromCharCode(32) + '|' + String.fromCharCode(160) + ')'),
|
||||
DELETE: new RegExp('^(' + String.fromCharCode(32) + '|' + String.fromCharCode(160) + ')+'),
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
REGEXP: /^(\t|\x20{4})/,
|
||||
DELETE: /^(\t|\x20{4})+/,
|
||||
};
|
||||
}
|
||||
})(Browser);
|
||||
|
||||
function repeat(s, n) {
|
||||
var result = '';
|
||||
while (n--) result += s;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对节点text中的换行符进行处理
|
||||
* @method encodeWrap
|
||||
* @param {String} nodeText MinderNode.data.text
|
||||
* @return {String} \n -> '\n'; \\n -> '\\n'
|
||||
*/
|
||||
function encodeWrap(nodeText) {
|
||||
if (!nodeText) {
|
||||
return '';
|
||||
}
|
||||
var textArr = [],
|
||||
WRAP_TEXT = ['\\', 'n'];
|
||||
for (var i = 0, j = 0, l = nodeText.length; i < l; i++) {
|
||||
if (nodeText[i] === '\n' || nodeText[i] === '\r') {
|
||||
textArr.push('\\n');
|
||||
j = 0;
|
||||
continue;
|
||||
}
|
||||
if (nodeText[i] === WRAP_TEXT[j]) {
|
||||
j++;
|
||||
if (j === 2) {
|
||||
j = 0;
|
||||
textArr.push('\\\\n');
|
||||
}
|
||||
continue;
|
||||
}
|
||||
switch (j) {
|
||||
case 0: {
|
||||
textArr.push(nodeText[i]);
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
textArr.push(nodeText[i - 1], nodeText[i]);
|
||||
}
|
||||
}
|
||||
j = 0;
|
||||
}
|
||||
return textArr.join('');
|
||||
}
|
||||
|
||||
/**
|
||||
* 将文本内容中的'\n'和'\\n'分别转换成\n和\\n
|
||||
* @method decodeWrap
|
||||
* @param {[type]} text [description]
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
function decodeWrap(text) {
|
||||
if (!text) {
|
||||
return '';
|
||||
}
|
||||
var textArr = [],
|
||||
WRAP_TEXT = ['\\', '\\', 'n'];
|
||||
for (var i = 0, j = 0, l = text.length; i < l; i++) {
|
||||
if (text[i] === WRAP_TEXT[j]) {
|
||||
j++;
|
||||
if (j === 3) {
|
||||
j = 0;
|
||||
textArr.push('\\n');
|
||||
}
|
||||
continue;
|
||||
}
|
||||
switch (j) {
|
||||
case 0: {
|
||||
textArr.push(text[i]);
|
||||
j = 0;
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
if (text[i] === 'n') {
|
||||
textArr.push('\n');
|
||||
} else {
|
||||
textArr.push(text[i - 1], text[i]);
|
||||
}
|
||||
j = 0;
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
textArr.push(text[i - 2]);
|
||||
if (text[i] !== '\\') {
|
||||
j = 0;
|
||||
textArr.push(text[i - 1], text[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return textArr.join('');
|
||||
}
|
||||
|
||||
function encode(json, level) {
|
||||
var local = '';
|
||||
level = level || 0;
|
||||
local += repeat('\t', level);
|
||||
local += encodeWrap(json.data.text) + LINE_ENDING;
|
||||
if (json.children) {
|
||||
json.children.forEach(function (child) {
|
||||
local += encode(child, level + 1);
|
||||
});
|
||||
}
|
||||
return local;
|
||||
}
|
||||
|
||||
function isEmpty(line) {
|
||||
return !/\S/.test(line);
|
||||
}
|
||||
|
||||
function getLevel(line) {
|
||||
var level = 0;
|
||||
while (TAB_CHAR.REGEXP.test(line)) {
|
||||
line = line.replace(TAB_CHAR.REGEXP, '');
|
||||
level++;
|
||||
}
|
||||
|
||||
return level;
|
||||
}
|
||||
|
||||
function getNode(line) {
|
||||
return {
|
||||
data: {
|
||||
text: decodeWrap(line.replace(TAB_CHAR.DELETE, '')),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function decode(local) {
|
||||
var json,
|
||||
parentMap = {},
|
||||
lines = local.split(LINE_ENDING_SPLITER),
|
||||
line,
|
||||
level,
|
||||
node;
|
||||
|
||||
function addChild(parent, child) {
|
||||
var children = parent.children || (parent.children = []);
|
||||
children.push(child);
|
||||
}
|
||||
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
line = lines[i];
|
||||
if (isEmpty(line)) continue;
|
||||
|
||||
level = getLevel(line);
|
||||
node = getNode(line);
|
||||
|
||||
if (level === 0) {
|
||||
if (json) {
|
||||
throw new Error('Invalid local format');
|
||||
}
|
||||
json = node;
|
||||
} else {
|
||||
if (!parentMap[level - 1]) {
|
||||
throw new Error('Invalid local format');
|
||||
}
|
||||
addChild(parentMap[level - 1], node);
|
||||
}
|
||||
parentMap[level] = node;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Desc: 增加一个将当前选中节点转换成text的方法
|
||||
* @Editor: Naixor
|
||||
* @Date: 2015.9.21
|
||||
*/
|
||||
function Node2Text(node) {
|
||||
function exportNode(node) {
|
||||
var exported = {};
|
||||
exported.data = node.getData();
|
||||
var childNodes = node.getChildren();
|
||||
exported.children = [];
|
||||
for (var i = 0; i < childNodes.length; i++) {
|
||||
exported.children.push(exportNode(childNodes[i]));
|
||||
}
|
||||
return exported;
|
||||
}
|
||||
if (!node) return;
|
||||
if (/^\s*$/.test(node.data.text)) {
|
||||
node.data.text = '分支主题';
|
||||
}
|
||||
return encode(exportNode(node));
|
||||
}
|
||||
|
||||
data.registerProtocol(
|
||||
'text',
|
||||
(module.exports = {
|
||||
fileDescription: '大纲文本',
|
||||
fileExtension: '.txt',
|
||||
dataType: 'text',
|
||||
mineType: 'text/plain',
|
||||
|
||||
encode: function (json) {
|
||||
return encode(json.root, 0);
|
||||
},
|
||||
|
||||
decode: function (local) {
|
||||
return decode(local);
|
||||
},
|
||||
|
||||
Node2Text: function (node) {
|
||||
return Node2Text(node);
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
|
@ -0,0 +1,37 @@
|
|||
/* eslint-disable */
|
||||
/**
|
||||
* @fileOverview
|
||||
*
|
||||
* 默认模板 - 脑图模板
|
||||
*
|
||||
* @author: techird
|
||||
* @copyright: Baidu FEX, 2014
|
||||
*/
|
||||
define(function (require, exports, module) {
|
||||
var template = require('../core/template');
|
||||
|
||||
template.register('default', {
|
||||
getLayout: function (node) {
|
||||
if (node.getData('layout')) return node.getData('layout');
|
||||
|
||||
var level = node.getLevel();
|
||||
|
||||
// 根节点
|
||||
if (level === 0) {
|
||||
return 'mind';
|
||||
}
|
||||
|
||||
// 一级节点
|
||||
if (level === 1) {
|
||||
return node.getLayoutPointPreview().x > 0 ? 'right' : 'left';
|
||||
}
|
||||
|
||||
return node.parent.getLayout();
|
||||
},
|
||||
|
||||
getConnect: function (node) {
|
||||
if (node.getLevel() == 1) return 'arc';
|
||||
return 'under';
|
||||
},
|
||||
});
|
||||
});
|
|
@ -0,0 +1,28 @@
|
|||
/* eslint-disable */
|
||||
/**
|
||||
* @fileOverview
|
||||
*
|
||||
* 文件夹模板
|
||||
*
|
||||
* @author: techird
|
||||
* @copyright: Baidu FEX, 2014
|
||||
*/
|
||||
define(function (require, exports, module) {
|
||||
var template = require('../core/template');
|
||||
|
||||
template.register('filetree', {
|
||||
getLayout: function (node) {
|
||||
if (node.getData('layout')) return node.getData('layout');
|
||||
if (node.isRoot()) return 'bottom';
|
||||
|
||||
return 'filetree-down';
|
||||
},
|
||||
|
||||
getConnect: function (node) {
|
||||
if (node.getLevel() == 1) {
|
||||
return 'poly';
|
||||
}
|
||||
return 'l';
|
||||
},
|
||||
});
|
||||
});
|
|
@ -0,0 +1,43 @@
|
|||
/* eslint-disable */
|
||||
/**
|
||||
* @fileOverview
|
||||
*
|
||||
* 默认模板 - 鱼骨头模板
|
||||
*
|
||||
* @author: techird
|
||||
* @copyright: Baidu FEX, 2014
|
||||
*/
|
||||
define(function (require, exports, module) {
|
||||
var template = require('../core/template');
|
||||
|
||||
template.register('fish-bone', {
|
||||
getLayout: function (node) {
|
||||
if (node.getData('layout')) return node.getData('layout');
|
||||
|
||||
var level = node.getLevel();
|
||||
|
||||
// 根节点
|
||||
if (level === 0) {
|
||||
return 'fish-bone-master';
|
||||
}
|
||||
|
||||
// 一级节点
|
||||
if (level === 1) {
|
||||
return 'fish-bone-slave';
|
||||
}
|
||||
|
||||
return node.getLayoutPointPreview().y > 0 ? 'filetree-up' : 'filetree-down';
|
||||
},
|
||||
|
||||
getConnect: function (node) {
|
||||
switch (node.getLevel()) {
|
||||
case 1:
|
||||
return 'fish-bone-master';
|
||||
case 2:
|
||||
return 'line';
|
||||
default:
|
||||
return 'l';
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
|
@ -0,0 +1,23 @@
|
|||
/* eslint-disable */
|
||||
/**
|
||||
* @fileOverview
|
||||
*
|
||||
* 往右布局结构模板
|
||||
*
|
||||
* @author: techird
|
||||
* @copyright: Baidu FEX, 2014
|
||||
*/
|
||||
define(function (require, exports, module) {
|
||||
var template = require('../core/template');
|
||||
|
||||
template.register('right', {
|
||||
getLayout: function (node) {
|
||||
return node.getData('layout') || 'right';
|
||||
},
|
||||
|
||||
getConnect: function (node) {
|
||||
if (node.getLevel() == 1) return 'arc';
|
||||
return 'bezier';
|
||||
},
|
||||
});
|
||||
});
|
|
@ -0,0 +1,22 @@
|
|||
/* eslint-disable */
|
||||
/**
|
||||
* @fileOverview
|
||||
*
|
||||
* 组织结构图模板
|
||||
*
|
||||
* @author: techird
|
||||
* @copyright: Baidu FEX, 2014
|
||||
*/
|
||||
define(function (require, exports, module) {
|
||||
var template = require('../core/template');
|
||||
|
||||
template.register('structure', {
|
||||
getLayout: function (node) {
|
||||
return node.getData('layout') || 'bottom';
|
||||
},
|
||||
|
||||
getConnect: function (node) {
|
||||
return 'poly';
|
||||
},
|
||||
});
|
||||
});
|
|
@ -0,0 +1,30 @@
|
|||
/* eslint-disable */
|
||||
/**
|
||||
* @fileOverview
|
||||
*
|
||||
* 天盘模板
|
||||
*
|
||||
* @author: along
|
||||
* @copyright: bpd729@163.com, 2015
|
||||
*/
|
||||
define(function (require, exports, module) {
|
||||
var template = require('../core/template');
|
||||
|
||||
template.register('tianpan', {
|
||||
getLayout: function (node) {
|
||||
if (node.getData('layout')) return node.getData('layout');
|
||||
var level = node.getLevel();
|
||||
|
||||
// 根节点
|
||||
if (level === 0) {
|
||||
return 'tianpan';
|
||||
}
|
||||
|
||||
return node.parent.getLayout();
|
||||
},
|
||||
|
||||
getConnect: function (node) {
|
||||
return 'arc_tp';
|
||||
},
|
||||
});
|
||||
});
|
|
@ -0,0 +1,68 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
var theme = require('../core/theme');
|
||||
|
||||
['classic', 'classic-compact'].forEach(function (name) {
|
||||
var compact = name == 'classic-compact';
|
||||
|
||||
/* jscs:disable maximumLineLength */
|
||||
theme.register(name, {
|
||||
'background':
|
||||
'#3A4144 url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAIAAAACDbGyAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDowQzg5QTQ0NDhENzgxMUUzOENGREE4QTg0RDgzRTZDNyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDowQzg5QTQ0NThENzgxMUUzOENGREE4QTg0RDgzRTZDNyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkMwOEQ1NDRGOEQ3NzExRTM4Q0ZEQThBODREODNFNkM3IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkMwOEQ1NDUwOEQ3NzExRTM4Q0ZEQThBODREODNFNkM3Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+e9P33AAAACVJREFUeNpisXJ0YUACTAyoAMr/+eM7EGGRZ4FQ7BycEAZAgAEAHbEGtkoQm/wAAAAASUVORK5CYII=") repeat',
|
||||
|
||||
'root-color': '#430',
|
||||
'root-background': '#e9df98',
|
||||
'root-stroke': '#e9df98',
|
||||
'root-font-size': 24,
|
||||
'root-padding': compact ? [10, 25] : [15, 25],
|
||||
'root-margin': compact ? [15, 25] : [30, 100],
|
||||
'root-radius': 30,
|
||||
'root-space': 10,
|
||||
'root-shadow': 'rgba(0, 0, 0, .25)',
|
||||
|
||||
'main-color': '#333',
|
||||
'main-background': '#a4c5c0',
|
||||
'main-stroke': '#a4c5c0',
|
||||
'main-font-size': 16,
|
||||
'main-padding': compact ? [5, 15] : [6, 20],
|
||||
'main-margin': compact ? [5, 10] : 20,
|
||||
'main-radius': 10,
|
||||
'main-space': 5,
|
||||
'main-shadow': 'rgba(0, 0, 0, .25)',
|
||||
|
||||
'sub-color': 'white',
|
||||
'sub-background': 'transparent',
|
||||
'sub-stroke': 'none',
|
||||
'sub-font-size': 12,
|
||||
'sub-padding': [5, 10],
|
||||
'sub-margin': compact ? [5, 10] : [15, 20],
|
||||
'sub-tree-margin': 30,
|
||||
'sub-radius': 5,
|
||||
'sub-space': 5,
|
||||
|
||||
'connect-color': 'white',
|
||||
'connect-width': 2,
|
||||
'main-connect-width': 3,
|
||||
'connect-radius': 5,
|
||||
|
||||
'selected-background': 'rgb(254, 219, 0)',
|
||||
'selected-stroke': 'rgb(254, 219, 0)',
|
||||
'selected-color': 'black',
|
||||
|
||||
'marquee-background': 'rgba(255,255,255,.3)',
|
||||
'marquee-stroke': 'white',
|
||||
|
||||
'drop-hint-color': 'yellow',
|
||||
'sub-drop-hint-width': 2,
|
||||
'main-drop-hint-width': 4,
|
||||
'root-drop-hint-width': 4,
|
||||
|
||||
'order-hint-area-color': 'rgba(0, 255, 0, .5)',
|
||||
'order-hint-path-color': '#0f0',
|
||||
'order-hint-path-width': 1,
|
||||
|
||||
'text-selection-color': 'rgb(27,171,255)',
|
||||
'line-height': 1.5,
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,59 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
var theme = require('../core/theme');
|
||||
|
||||
theme.register('fish', {
|
||||
'background':
|
||||
'#3A4144 url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAIAAAACDbGyAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDowQzg5QTQ0NDhENzgxMUUzOENGREE4QTg0RDgzRTZDNyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDowQzg5QTQ0NThENzgxMUUzOENGREE4QTg0RDgzRTZDNyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkMwOEQ1NDRGOEQ3NzExRTM4Q0ZEQThBODREODNFNkM3IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkMwOEQ1NDUwOEQ3NzExRTM4Q0ZEQThBODREODNFNkM3Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+e9P33AAAACVJREFUeNpisXJ0YUACTAyoAMr/+eM7EGGRZ4FQ7BycEAZAgAEAHbEGtkoQm/wAAAAASUVORK5CYII=") repeat',
|
||||
|
||||
'root-color': '#430',
|
||||
'root-background': '#e9df98',
|
||||
'root-stroke': '#e9df98',
|
||||
'root-font-size': 24,
|
||||
'root-padding': [35, 35],
|
||||
'root-margin': 30,
|
||||
'root-radius': 100,
|
||||
'root-space': 10,
|
||||
'root-shadow': 'rgba(0, 0, 0, .25)',
|
||||
|
||||
'main-color': '#333',
|
||||
'main-background': '#a4c5c0',
|
||||
'main-stroke': '#a4c5c0',
|
||||
'main-font-size': 16,
|
||||
'main-padding': [6, 20],
|
||||
'main-margin': [20, 20],
|
||||
'main-radius': 5,
|
||||
'main-space': 5,
|
||||
'main-shadow': 'rgba(0, 0, 0, .25)',
|
||||
|
||||
'sub-color': 'black',
|
||||
'sub-background': 'white',
|
||||
'sub-stroke': 'white',
|
||||
'sub-font-size': 12,
|
||||
'sub-padding': [5, 10],
|
||||
'sub-margin': [10],
|
||||
'sub-radius': 5,
|
||||
'sub-space': 5,
|
||||
|
||||
'connect-color': 'white',
|
||||
'connect-width': 3,
|
||||
'main-connect-width': 3,
|
||||
'connect-radius': 5,
|
||||
|
||||
'selected-background': 'rgb(254, 219, 0)',
|
||||
'selected-stroke': 'rgb(254, 219, 0)',
|
||||
|
||||
'marquee-background': 'rgba(255,255,255,.3)',
|
||||
'marquee-stroke': 'white',
|
||||
|
||||
'drop-hint-color': 'yellow',
|
||||
'drop-hint-width': 4,
|
||||
|
||||
'order-hint-area-color': 'rgba(0, 255, 0, .5)',
|
||||
'order-hint-path-color': '#0f0',
|
||||
'order-hint-path-width': 1,
|
||||
|
||||
'text-selection-color': 'rgb(27,171,255)',
|
||||
'line-height': 1.5,
|
||||
});
|
||||
});
|
|
@ -0,0 +1,78 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
var kity = require('../core/kity');
|
||||
var theme = require('../core/theme');
|
||||
|
||||
function hsl(h, s, l) {
|
||||
return kity.Color.createHSL(h, s, l);
|
||||
}
|
||||
|
||||
function generate(h, compat) {
|
||||
return {
|
||||
'background': '#fbfbfb',
|
||||
|
||||
'root-color': 'white',
|
||||
'root-background': hsl(h, 37, 60),
|
||||
'root-stroke': hsl(h, 37, 60),
|
||||
'root-font-size': 16,
|
||||
'root-padding': compat ? [6, 12] : [12, 24],
|
||||
'root-margin': compat ? 10 : [30, 100],
|
||||
'root-radius': 5,
|
||||
'root-space': 10,
|
||||
|
||||
'main-color': 'black',
|
||||
'main-background': hsl(h, 33, 95),
|
||||
'main-stroke': hsl(h, 37, 60),
|
||||
'main-stroke-width': 1,
|
||||
'main-font-size': 14,
|
||||
'main-padding': [6, 20],
|
||||
'main-margin': compat ? 8 : 20,
|
||||
'main-radius': 3,
|
||||
'main-space': 5,
|
||||
|
||||
'sub-color': 'black',
|
||||
'sub-background': 'transparent',
|
||||
'sub-stroke': 'none',
|
||||
'sub-font-size': 12,
|
||||
'sub-padding': compat ? [3, 5] : [5, 10],
|
||||
'sub-margin': compat ? [4, 8] : [15, 20],
|
||||
'sub-radius': 5,
|
||||
'sub-space': 5,
|
||||
|
||||
'connect-color': hsl(h, 37, 60),
|
||||
'connect-width': 1,
|
||||
'connect-radius': 5,
|
||||
|
||||
'selected-stroke': hsl(h, 26, 30),
|
||||
'selected-stroke-width': '3',
|
||||
'blur-selected-stroke': hsl(h, 10, 60),
|
||||
|
||||
'marquee-background': hsl(h, 100, 80).set('a', 0.1),
|
||||
'marquee-stroke': hsl(h, 37, 60),
|
||||
|
||||
'drop-hint-color': hsl(h, 26, 35),
|
||||
'drop-hint-width': 5,
|
||||
|
||||
'order-hint-area-color': hsl(h, 100, 30).set('a', 0.5),
|
||||
'order-hint-path-color': hsl(h, 100, 25),
|
||||
'order-hint-path-width': 1,
|
||||
|
||||
'text-selection-color': hsl(h, 100, 20),
|
||||
'line-height': 1.5,
|
||||
};
|
||||
}
|
||||
|
||||
var plans = {
|
||||
red: 0,
|
||||
soil: 25,
|
||||
green: 122,
|
||||
blue: 204,
|
||||
purple: 246,
|
||||
pink: 334,
|
||||
};
|
||||
var name;
|
||||
for (name in plans) {
|
||||
theme.register('fresh-' + name, generate(plans[name]));
|
||||
theme.register('fresh-' + name + '-compat', generate(plans[name], true));
|
||||
}
|
||||
});
|
|
@ -0,0 +1,64 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
var theme = require('../core/theme');
|
||||
|
||||
['snow', 'snow-compact'].forEach(function (name) {
|
||||
var compact = name == 'snow-compact';
|
||||
|
||||
/* jscs:disable maximumLineLength */
|
||||
theme.register(name, {
|
||||
'background':
|
||||
'#3A4144 url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAIAAAACDbGyAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDowQzg5QTQ0NDhENzgxMUUzOENGREE4QTg0RDgzRTZDNyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDowQzg5QTQ0NThENzgxMUUzOENGREE4QTg0RDgzRTZDNyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkMwOEQ1NDRGOEQ3NzExRTM4Q0ZEQThBODREODNFNkM3IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkMwOEQ1NDUwOEQ3NzExRTM4Q0ZEQThBODREODNFNkM3Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+e9P33AAAACVJREFUeNpisXJ0YUACTAyoAMr/+eM7EGGRZ4FQ7BycEAZAgAEAHbEGtkoQm/wAAAAASUVORK5CYII=") repeat',
|
||||
|
||||
'root-color': '#430',
|
||||
'root-background': '#e9df98',
|
||||
'root-stroke': '#e9df98',
|
||||
'root-font-size': 24,
|
||||
'root-padding': compact ? [5, 10] : [15, 25],
|
||||
'root-margin': compact ? 15 : 30,
|
||||
'root-radius': 5,
|
||||
'root-space': 10,
|
||||
'root-shadow': 'rgba(0, 0, 0, .25)',
|
||||
|
||||
'main-color': '#333',
|
||||
'main-background': '#a4c5c0',
|
||||
'main-stroke': '#a4c5c0',
|
||||
'main-font-size': 16,
|
||||
'main-padding': compact ? [4, 10] : [6, 20],
|
||||
'main-margin': compact ? [5, 10] : [20, 40],
|
||||
'main-radius': 5,
|
||||
'main-space': 5,
|
||||
'main-shadow': 'rgba(0, 0, 0, .25)',
|
||||
|
||||
'sub-color': 'black',
|
||||
'sub-background': 'white',
|
||||
'sub-stroke': 'white',
|
||||
'sub-font-size': 12,
|
||||
'sub-padding': [5, 10],
|
||||
'sub-margin': compact ? [5, 10] : [10, 20],
|
||||
'sub-radius': 5,
|
||||
'sub-space': 5,
|
||||
|
||||
'connect-color': 'white',
|
||||
'connect-width': 2,
|
||||
'main-connect-width': 3,
|
||||
'connect-radius': 5,
|
||||
|
||||
'selected-background': 'rgb(254, 219, 0)',
|
||||
'selected-stroke': 'rgb(254, 219, 0)',
|
||||
|
||||
'marquee-background': 'rgba(255,255,255,.3)',
|
||||
'marquee-stroke': 'white',
|
||||
|
||||
'drop-hint-color': 'yellow',
|
||||
'drop-hint-width': 4,
|
||||
|
||||
'order-hint-area-color': 'rgba(0, 255, 0, .5)',
|
||||
'order-hint-path-color': '#0f0',
|
||||
'order-hint-path-width': 1,
|
||||
|
||||
'text-selection-color': 'rgb(27,171,255)',
|
||||
'line-height': 1.5,
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,71 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
var theme = require('../core/theme');
|
||||
|
||||
['tianpan', 'tianpan-compact'].forEach(function (name) {
|
||||
var compact = name == 'tianpan-compact';
|
||||
|
||||
theme.register(name, {
|
||||
'background':
|
||||
'#3A4144 url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAIAAAACDbGyAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDowQzg5QTQ0NDhENzgxMUUzOENGREE4QTg0RDgzRTZDNyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDowQzg5QTQ0NThENzgxMUUzOENGREE4QTg0RDgzRTZDNyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkMwOEQ1NDRGOEQ3NzExRTM4Q0ZEQThBODREODNFNkM3IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkMwOEQ1NDUwOEQ3NzExRTM4Q0ZEQThBODREODNFNkM3Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+e9P33AAAACVJREFUeNpisXJ0YUACTAyoAMr/+eM7EGGRZ4FQ7BycEAZAgAEAHbEGtkoQm/wAAAAASUVORK5CYII=") repeat',
|
||||
|
||||
'root-color': '#430',
|
||||
'root-background': '#e9df98',
|
||||
'root-stroke': '#e9df98',
|
||||
'root-font-size': 25,
|
||||
'root-padding': compact ? 15 : 20,
|
||||
'root-margin': compact ? [15, 25] : 100,
|
||||
'root-radius': 30,
|
||||
'root-space': 10,
|
||||
'root-shadow': 'rgba(0, 0, 0, .25)',
|
||||
'root-shape': 'circle',
|
||||
|
||||
'main-color': '#333',
|
||||
'main-background': '#a4c5c0',
|
||||
'main-stroke': '#a4c5c0',
|
||||
'main-font-size': 15,
|
||||
'main-padding': compact ? 10 : 12,
|
||||
'main-margin': compact ? 10 : 12,
|
||||
'main-radius': 10,
|
||||
'main-space': 5,
|
||||
'main-shadow': 'rgba(0, 0, 0, .25)',
|
||||
'main-shape': 'circle',
|
||||
|
||||
'sub-color': '#333',
|
||||
'sub-background': '#99ca6a',
|
||||
'sub-stroke': '#a4c5c0',
|
||||
'sub-font-size': 13,
|
||||
'sub-padding': 5,
|
||||
'sub-margin': compact ? 6 : 10,
|
||||
'sub-tree-margin': 30,
|
||||
'sub-radius': 5,
|
||||
'sub-space': 5,
|
||||
'sub-shadow': 'rgba(0, 0, 0, .25)',
|
||||
'sub-shape': 'circle',
|
||||
|
||||
'connect-color': 'white',
|
||||
'connect-width': 2,
|
||||
'main-connect-width': 3,
|
||||
'connect-radius': 5,
|
||||
|
||||
'selected-background': 'rgb(254, 219, 0)',
|
||||
'selected-stroke': 'rgb(254, 219, 0)',
|
||||
'selected-color': 'black',
|
||||
|
||||
'marquee-background': 'rgba(255,255,255,.3)',
|
||||
'marquee-stroke': 'white',
|
||||
|
||||
'drop-hint-color': 'yellow',
|
||||
'sub-drop-hint-width': 2,
|
||||
'main-drop-hint-width': 4,
|
||||
'root-drop-hint-width': 4,
|
||||
|
||||
'order-hint-area-color': 'rgba(0, 255, 0, .5)',
|
||||
'order-hint-path-color': '#0f0',
|
||||
'order-hint-path-width': 1,
|
||||
|
||||
'text-selection-color': 'rgb(27,171,255)',
|
||||
'line-height': 1.4,
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,35 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
var theme = require('../core/theme');
|
||||
|
||||
theme.register('wire', {
|
||||
'background': 'black',
|
||||
|
||||
'color': '#999',
|
||||
'stroke': 'none',
|
||||
'padding': 10,
|
||||
'margin': 20,
|
||||
'font-size': 14,
|
||||
|
||||
'connect-color': '#999',
|
||||
'connect-width': 1,
|
||||
|
||||
'selected-background': '#999',
|
||||
'selected-color': 'black',
|
||||
|
||||
'marquee-background': 'rgba(255,255,255,.3)',
|
||||
'marquee-stroke': 'white',
|
||||
|
||||
'drop-hint-color': 'yellow',
|
||||
'sub-drop-hint-width': 2,
|
||||
'main-drop-hint-width': 4,
|
||||
'root-drop-hint-width': 4,
|
||||
|
||||
'order-hint-area-color': 'rgba(0, 255, 0, .5)',
|
||||
'order-hint-path-color': '#0f0',
|
||||
'order-hint-path-width': 1,
|
||||
|
||||
'text-selection-color': 'rgb(27,171,255)',
|
||||
'line-height': 1.5,
|
||||
});
|
||||
});
|
|
@ -0,0 +1,39 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
/**
|
||||
* 运行时
|
||||
*/
|
||||
var runtimes = [];
|
||||
|
||||
function assemble(runtime) {
|
||||
runtimes.push(runtime);
|
||||
}
|
||||
|
||||
function KMEditor(selector, blackList) {
|
||||
this.selector = selector;
|
||||
for (var i = 0; i < runtimes.length; i++) {
|
||||
if (typeof runtimes[i] == 'function') {
|
||||
runtimes[i].call(this, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KMEditor.assemble = assemble;
|
||||
|
||||
assemble(require('./runtime/container'));
|
||||
assemble(require('./runtime/fsm'));
|
||||
assemble(require('./runtime/minder'));
|
||||
assemble(require('./runtime/receiver'));
|
||||
assemble(require('./runtime/hotbox'));
|
||||
assemble(require('./runtime/input'));
|
||||
assemble(require('./runtime/clipboard-mimetype'));
|
||||
assemble(require('./runtime/clipboard'));
|
||||
assemble(require('./runtime/drag'));
|
||||
assemble(require('./runtime/node'));
|
||||
assemble(require('./runtime/history'));
|
||||
assemble(require('./runtime/jumping'));
|
||||
assemble(require('./runtime/priority'));
|
||||
assemble(require('./runtime/progress'));
|
||||
|
||||
return (module.exports = KMEditor);
|
||||
});
|
|
@ -0,0 +1,12 @@
|
|||
/* eslint-disable */
|
||||
/**
|
||||
* @fileOverview
|
||||
*
|
||||
* 打包暴露
|
||||
*
|
||||
* @author: techird
|
||||
* @copyright: Baidu FEX, 2014
|
||||
*/
|
||||
define('expose-editor', function (require, exports, module) {
|
||||
return (module.exports = window.kityminder.Editor = require('./editor'));
|
||||
});
|
|
@ -0,0 +1,6 @@
|
|||
/* eslint-disable */
|
||||
import Hotbox from '../hotbox/hotbox';
|
||||
|
||||
define(function (require, exports, module) {
|
||||
return (module.exports = Hotbox);
|
||||
});
|
|
@ -0,0 +1,2 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {});
|
|
@ -0,0 +1,4 @@
|
|||
/* eslint-disable */
|
||||
define(function (require, exports, module) {
|
||||
return (module.exports = window.kityminder.Minder);
|
||||
});
|
|
@ -0,0 +1,124 @@
|
|||
/* eslint-disable */
|
||||
/**
|
||||
* @Desc: 新增一个用于处理系统ctrl+c ctrl+v等方式导入导出节点的MIMETYPE处理,如系统不支持clipboardEvent或者是FF则不初始化改class
|
||||
* @Editor: Naixor
|
||||
* @Date: 2015.9.21
|
||||
*/
|
||||
define(function (require, exports, module) {
|
||||
function MimeType() {
|
||||
/**
|
||||
* 私有变量
|
||||
*/
|
||||
var SPLITOR = '\uFEFF';
|
||||
var MIMETYPE = {
|
||||
'application/km': '\uFFFF',
|
||||
};
|
||||
var SIGN = {
|
||||
'\uFEFF': 'SPLITOR',
|
||||
'\uFFFF': 'application/km',
|
||||
};
|
||||
|
||||
/**
|
||||
* 用于将一段纯文本封装成符合其数据格式的文本
|
||||
* @method process private
|
||||
* @param {MIMETYPE} mimetype 数据格式
|
||||
* @param {String} text 原始文本
|
||||
* @return {String} 符合该数据格式下的文本
|
||||
* @example
|
||||
* var str = "123";
|
||||
* str = process('application/km', str); // 返回的内容再经过MimeType判断会读取出其数据格式为application/km
|
||||
* process('text/plain', str); // 若接受到一个非纯文本信息,则会将其转换为新的数据格式
|
||||
*/
|
||||
function process(mimetype, text) {
|
||||
if (!this.isPureText(text)) {
|
||||
var _mimetype = this.whichMimeType(text);
|
||||
if (!_mimetype) {
|
||||
throw new Error('unknow mimetype!');
|
||||
}
|
||||
text = this.getPureText(text);
|
||||
}
|
||||
if (mimetype === false) {
|
||||
return text;
|
||||
}
|
||||
return mimetype + SPLITOR + text;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册数据类型的标识
|
||||
* @method registMimeTypeProtocol public
|
||||
* @param {String} type 数据类型
|
||||
* @param {String} sign 标识
|
||||
*/
|
||||
this.registMimeTypeProtocol = function (type, sign) {
|
||||
if (sign && SIGN[sign]) {
|
||||
throw new Error('sing has registed!');
|
||||
}
|
||||
if (type && !!MIMETYPE[type]) {
|
||||
throw new Error('mimetype has registed!');
|
||||
}
|
||||
SIGN[sign] = type;
|
||||
MIMETYPE[type] = sign;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取已注册数据类型的协议
|
||||
* @method getMimeTypeProtocol public
|
||||
* @param {String} type 数据类型
|
||||
* @param {String} text|undefiend 文本内容或不传入
|
||||
* @return {String|Function}
|
||||
* @example
|
||||
* text若不传入则直接返回对应数据格式的处理(process)方法
|
||||
* 若传入文本则直接调用对应的process方法进行处理,此时返回处理后的内容
|
||||
* var m = new MimeType();
|
||||
* var kmprocess = m.getMimeTypeProtocol('application/km');
|
||||
* kmprocess("123") === m.getMimeTypeProtocol('application/km', "123");
|
||||
*
|
||||
*/
|
||||
this.getMimeTypeProtocol = function (type, text) {
|
||||
var mimetype = MIMETYPE[type] || false;
|
||||
|
||||
if (text === undefined) {
|
||||
return process.bind(this, mimetype);
|
||||
}
|
||||
|
||||
return process(mimetype, text);
|
||||
};
|
||||
|
||||
this.getSpitor = function () {
|
||||
return SPLITOR;
|
||||
};
|
||||
|
||||
this.getMimeType = function (sign) {
|
||||
if (sign !== undefined) {
|
||||
return SIGN[sign] || null;
|
||||
}
|
||||
return MIMETYPE;
|
||||
};
|
||||
}
|
||||
|
||||
MimeType.prototype.isPureText = function (text) {
|
||||
return !~text.indexOf(this.getSpitor());
|
||||
};
|
||||
|
||||
MimeType.prototype.getPureText = function (text) {
|
||||
if (this.isPureText(text)) {
|
||||
return text;
|
||||
}
|
||||
return text.split(this.getSpitor())[1];
|
||||
};
|
||||
|
||||
MimeType.prototype.whichMimeType = function (text) {
|
||||
if (this.isPureText(text)) {
|
||||
return null;
|
||||
}
|
||||
return this.getMimeType(text.split(this.getSpitor())[0]);
|
||||
};
|
||||
|
||||
function MimeTypeRuntime() {
|
||||
if (this.minder.supportClipboardEvent && !kity.Browser.gecko) {
|
||||
this.MimeType = new MimeType();
|
||||
}
|
||||
}
|
||||
|
||||
return (module.exports = MimeTypeRuntime);
|
||||
});
|
|
@ -0,0 +1,192 @@
|
|||
/* eslint-disable */
|
||||
/**
|
||||
* @Desc: 处理editor的clipboard事件,只在支持ClipboardEvent并且不是FF的情况下工作
|
||||
* @Editor: Naixor
|
||||
* @Date: 2015.9.21
|
||||
*/
|
||||
define(function (require, exports, module) {
|
||||
function ClipboardRuntime() {
|
||||
var minder = this.minder;
|
||||
var Data = window.kityminder.data;
|
||||
|
||||
if (!minder.supportClipboardEvent || kity.Browser.gecko) {
|
||||
return;
|
||||
}
|
||||
|
||||
var fsm = this.fsm;
|
||||
var receiver = this.receiver;
|
||||
var MimeType = this.MimeType;
|
||||
|
||||
var kmencode = MimeType.getMimeTypeProtocol('application/km'),
|
||||
decode = Data.getRegisterProtocol('json').decode;
|
||||
var _selectedNodes = [];
|
||||
|
||||
/*
|
||||
* 增加对多节点赋值粘贴的处理
|
||||
*/
|
||||
function encode(nodes) {
|
||||
var _nodes = [];
|
||||
for (var i = 0, l = nodes.length; i < l; i++) {
|
||||
_nodes.push(minder.exportNode(nodes[i]));
|
||||
}
|
||||
return kmencode(Data.getRegisterProtocol('json').encode(_nodes));
|
||||
}
|
||||
|
||||
var beforeCopy = function (e) {
|
||||
if (document.activeElement == receiver.element) {
|
||||
var clipBoardEvent = e;
|
||||
var state = fsm.state();
|
||||
|
||||
switch (state) {
|
||||
case 'input': {
|
||||
break;
|
||||
}
|
||||
case 'normal': {
|
||||
var nodes = [].concat(minder.getSelectedNodes());
|
||||
if (nodes.length) {
|
||||
// 这里由于被粘贴复制的节点的id信息也都一样,故做此算法
|
||||
// 这里有个疑问,使用node.getParent()或者node.parent会离奇导致出现非选中节点被渲染成选中节点,因此使用isAncestorOf,而没有使用自行回溯的方式
|
||||
if (nodes.length > 1) {
|
||||
var targetLevel;
|
||||
nodes.sort(function (a, b) {
|
||||
return a.getLevel() - b.getLevel();
|
||||
});
|
||||
targetLevel = nodes[0].getLevel();
|
||||
if (targetLevel !== nodes[nodes.length - 1].getLevel()) {
|
||||
var plevel,
|
||||
pnode,
|
||||
idx = 0,
|
||||
l = nodes.length,
|
||||
pidx = l - 1;
|
||||
|
||||
pnode = nodes[pidx];
|
||||
|
||||
while (pnode.getLevel() !== targetLevel) {
|
||||
idx = 0;
|
||||
while (idx < l && nodes[idx].getLevel() === targetLevel) {
|
||||
if (nodes[idx].isAncestorOf(pnode)) {
|
||||
nodes.splice(pidx, 1);
|
||||
break;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
pidx--;
|
||||
pnode = nodes[pidx];
|
||||
}
|
||||
}
|
||||
}
|
||||
var str = encode(nodes);
|
||||
clipBoardEvent.clipboardData.setData('text/plain', str);
|
||||
}
|
||||
e.preventDefault();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var beforeCut = function (e) {
|
||||
if (document.activeElement == receiver.element) {
|
||||
if (minder.getStatus() !== 'normal') {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
var clipBoardEvent = e;
|
||||
var state = fsm.state();
|
||||
|
||||
switch (state) {
|
||||
case 'input': {
|
||||
break;
|
||||
}
|
||||
case 'normal': {
|
||||
var nodes = minder.getSelectedNodes();
|
||||
if (nodes.length) {
|
||||
clipBoardEvent.clipboardData.setData('text/plain', encode(nodes));
|
||||
minder.execCommand('removenode');
|
||||
}
|
||||
e.preventDefault();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var beforePaste = function (e) {
|
||||
if (document.activeElement == receiver.element) {
|
||||
if (minder.getStatus() !== 'normal') {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
var clipBoardEvent = e;
|
||||
var state = fsm.state();
|
||||
var textData = clipBoardEvent.clipboardData.getData('text/plain');
|
||||
|
||||
switch (state) {
|
||||
case 'input': {
|
||||
// input状态下如果格式为application/km则不进行paste操作
|
||||
if (!MimeType.isPureText(textData)) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'normal': {
|
||||
/*
|
||||
* 针对normal状态下通过对选中节点粘贴导入子节点文本进行单独处理
|
||||
*/
|
||||
var sNodes = minder.getSelectedNodes();
|
||||
|
||||
if (MimeType.whichMimeType(textData) === 'application/km') {
|
||||
var nodes = decode(MimeType.getPureText(textData));
|
||||
var _node;
|
||||
sNodes.forEach(function (node) {
|
||||
// 由于粘贴逻辑中为了排除子节点重新排序导致逆序,因此复制的时候倒过来
|
||||
for (var i = nodes.length - 1; i >= 0; i--) {
|
||||
_node = minder.createNode(null, node);
|
||||
minder.importNode(_node, nodes[i]);
|
||||
_selectedNodes.push(_node);
|
||||
node.appendChild(_node);
|
||||
}
|
||||
});
|
||||
minder.select(_selectedNodes, true);
|
||||
_selectedNodes = [];
|
||||
|
||||
minder.refresh();
|
||||
} else if (
|
||||
clipBoardEvent.clipboardData &&
|
||||
clipBoardEvent.clipboardData.items[0].type.indexOf('image') > -1
|
||||
) {
|
||||
var imageFile = clipBoardEvent.clipboardData.items[0].getAsFile();
|
||||
var serverService = angular.element(document.body).injector().get('server');
|
||||
|
||||
return serverService.uploadImage(imageFile).then(function (json) {
|
||||
var resp = json.data;
|
||||
if (resp.errno === 0) {
|
||||
minder.execCommand('image', resp.data.url);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
sNodes.forEach(function (node) {
|
||||
minder.Text2Children(node, textData);
|
||||
});
|
||||
}
|
||||
e.preventDefault();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
/**
|
||||
* 由editor的receiver统一处理全部事件,包括clipboard事件
|
||||
* @Editor: Naixor
|
||||
* @Date: 2015.9.24
|
||||
*/
|
||||
document.addEventListener('copy', beforeCopy);
|
||||
document.addEventListener('cut', beforeCut);
|
||||
document.addEventListener('paste', beforePaste);
|
||||
}
|
||||
|
||||
return (module.exports = ClipboardRuntime);
|
||||
});
|
|
@ -0,0 +1,35 @@
|
|||
/* eslint-disable */
|
||||
/**
|
||||
* @fileOverview
|
||||
*
|
||||
* 初始化编辑器的容器
|
||||
*
|
||||
* @author: techird
|
||||
* @copyright: Baidu FEX, 2014
|
||||
*/
|
||||
define(function (require, exports, module) {
|
||||
/**
|
||||
* 最先执行的 Runtime,初始化编辑器容器
|
||||
*/
|
||||
function ContainerRuntime() {
|
||||
var container;
|
||||
|
||||
if (typeof this.selector == 'string') {
|
||||
container = document.querySelector(this.selector);
|
||||
} else {
|
||||
container = this.selector;
|
||||
}
|
||||
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 这个类名用于给编辑器添加样式
|
||||
container.classList.add('km-editor');
|
||||
|
||||
// 暴露容器给其他运行时使用
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
return (module.exports = ContainerRuntime);
|
||||
});
|
|
@ -0,0 +1,149 @@
|
|||
/* eslint-disable */
|
||||
/**
|
||||
* @fileOverview
|
||||
*
|
||||
* 用于拖拽节点时屏蔽键盘事件
|
||||
*
|
||||
* @author: techird
|
||||
* @copyright: Baidu FEX, 2014
|
||||
*/
|
||||
define(function (require, exports, module) {
|
||||
var Hotbox = require('../hotbox');
|
||||
var Debug = require('../tool/debug');
|
||||
var debug = new Debug('drag');
|
||||
|
||||
function DragRuntime() {
|
||||
var fsm = this.fsm;
|
||||
var minder = this.minder;
|
||||
var hotbox = this.hotbox;
|
||||
var receiver = this.receiver;
|
||||
var receiverElement = receiver.element;
|
||||
|
||||
// setup everything to go
|
||||
setupFsm();
|
||||
|
||||
// listen the fsm changes, make action.
|
||||
function setupFsm() {
|
||||
// when jumped to drag mode, enter
|
||||
fsm.when('* -> drag', function () {
|
||||
// now is drag mode
|
||||
});
|
||||
|
||||
fsm.when('drag -> *', function (exit, enter, reason) {
|
||||
if (reason == 'drag-finish') {
|
||||
// now exit drag mode
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var downX, downY;
|
||||
var MOUSE_HAS_DOWN = 0;
|
||||
var MOUSE_HAS_UP = 1;
|
||||
var BOUND_CHECK = 20;
|
||||
var flag = MOUSE_HAS_UP;
|
||||
var maxX, maxY, osx, osy, containerY;
|
||||
var freeHorizen = false,
|
||||
freeVirtical = false;
|
||||
var frame;
|
||||
|
||||
function move(direction, speed) {
|
||||
if (!direction) {
|
||||
freeHorizen = freeVirtical = false;
|
||||
frame && kity.releaseFrame(frame);
|
||||
frame = null;
|
||||
return;
|
||||
}
|
||||
if (!frame) {
|
||||
frame = kity.requestFrame(
|
||||
(function (direction, speed, minder) {
|
||||
return function (frame) {
|
||||
switch (direction) {
|
||||
case 'left':
|
||||
minder._viewDragger.move({ x: -speed, y: 0 }, 0);
|
||||
break;
|
||||
case 'top':
|
||||
minder._viewDragger.move({ x: 0, y: -speed }, 0);
|
||||
break;
|
||||
case 'right':
|
||||
minder._viewDragger.move({ x: speed, y: 0 }, 0);
|
||||
break;
|
||||
case 'bottom':
|
||||
minder._viewDragger.move({ x: 0, y: speed }, 0);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
frame.next();
|
||||
};
|
||||
})(direction, speed, minder)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
minder.on('mousedown', function (e) {
|
||||
flag = MOUSE_HAS_DOWN;
|
||||
var rect = minder.getPaper().container.getBoundingClientRect();
|
||||
downX = e.originEvent.clientX;
|
||||
downY = e.originEvent.clientY;
|
||||
containerY = rect.top;
|
||||
maxX = rect.width;
|
||||
maxY = rect.height;
|
||||
});
|
||||
|
||||
minder.on('mousemove', function (e) {
|
||||
if (
|
||||
fsm.state() === 'drag' &&
|
||||
flag == MOUSE_HAS_DOWN &&
|
||||
minder.getSelectedNode() &&
|
||||
(Math.abs(downX - e.originEvent.clientX) > BOUND_CHECK || Math.abs(downY - e.originEvent.clientY) > BOUND_CHECK)
|
||||
) {
|
||||
osx = e.originEvent.clientX;
|
||||
osy = e.originEvent.clientY - containerY;
|
||||
|
||||
if (osx < BOUND_CHECK) {
|
||||
move('right', BOUND_CHECK - osx);
|
||||
} else if (osx > maxX - BOUND_CHECK) {
|
||||
move('left', BOUND_CHECK + osx - maxX);
|
||||
} else {
|
||||
freeHorizen = true;
|
||||
}
|
||||
if (osy < BOUND_CHECK) {
|
||||
move('bottom', osy);
|
||||
} else if (osy > maxY - BOUND_CHECK) {
|
||||
move('top', BOUND_CHECK + osy - maxY);
|
||||
} else {
|
||||
freeVirtical = true;
|
||||
}
|
||||
if (freeHorizen && freeVirtical) {
|
||||
move(false);
|
||||
}
|
||||
}
|
||||
if (
|
||||
fsm.state() !== 'drag' &&
|
||||
flag === MOUSE_HAS_DOWN &&
|
||||
minder.getSelectedNode() &&
|
||||
(Math.abs(downX - e.originEvent.clientX) > BOUND_CHECK || Math.abs(downY - e.originEvent.clientY) > BOUND_CHECK)
|
||||
) {
|
||||
if (fsm.state() === 'hotbox') {
|
||||
hotbox.active(Hotbox.STATE_IDLE);
|
||||
}
|
||||
|
||||
return fsm.jump('drag', 'user-drag');
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener(
|
||||
'mouseup',
|
||||
function () {
|
||||
flag = MOUSE_HAS_UP;
|
||||
if (fsm.state() === 'drag') {
|
||||
move(false);
|
||||
return fsm.jump('normal', 'drag-finish');
|
||||
}
|
||||
},
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
return (module.exports = DragRuntime);
|
||||
});
|
|
@ -0,0 +1,121 @@
|
|||
/* eslint-disable */
|
||||
/**
|
||||
* @fileOverview
|
||||
*
|
||||
* 编辑器状态机
|
||||
*
|
||||
* @author: techird
|
||||
* @copyright: Baidu FEX, 2014
|
||||
*/
|
||||
define(function (require, exports, module) {
|
||||
var Debug = require('../tool/debug');
|
||||
var debug = new Debug('fsm');
|
||||
|
||||
function handlerConditionMatch(condition, when, exit, enter) {
|
||||
if (condition.when != when) return false;
|
||||
if (condition.enter != '*' && condition.enter != enter) return false;
|
||||
if (condition.exit != '*' && condition.exit != exit) return;
|
||||
return true;
|
||||
}
|
||||
|
||||
function FSM(defaultState) {
|
||||
var currentState = defaultState;
|
||||
var BEFORE_ARROW = ' - ';
|
||||
var AFTER_ARROW = ' -> ';
|
||||
var handlers = [];
|
||||
|
||||
/**
|
||||
* 状态跳转
|
||||
*
|
||||
* 会通知所有的状态跳转监视器
|
||||
*
|
||||
* @param {string} newState 新状态名称
|
||||
* @param {any} reason 跳转的原因,可以作为参数传递给跳转监视器
|
||||
*/
|
||||
this.jump = function (newState, reason) {
|
||||
if (!reason) throw new Error('Please tell fsm the reason to jump');
|
||||
|
||||
var oldState = currentState;
|
||||
var notify = [oldState, newState].concat([].slice.call(arguments, 1));
|
||||
var i, handler;
|
||||
|
||||
// 跳转前
|
||||
for (i = 0; i < handlers.length; i++) {
|
||||
handler = handlers[i];
|
||||
if (handlerConditionMatch(handler.condition, 'before', oldState, newState)) {
|
||||
if (handler.apply(null, notify)) return;
|
||||
}
|
||||
}
|
||||
|
||||
currentState = newState;
|
||||
debug.log('[{0}] {1} -> {2}', reason, oldState, newState);
|
||||
|
||||
// 跳转后
|
||||
for (i = 0; i < handlers.length; i++) {
|
||||
handler = handlers[i];
|
||||
if (handlerConditionMatch(handler.condition, 'after', oldState, newState)) {
|
||||
handler.apply(null, notify);
|
||||
}
|
||||
}
|
||||
return currentState;
|
||||
};
|
||||
|
||||
/**
|
||||
* 返回当前状态
|
||||
* @return {string}
|
||||
*/
|
||||
this.state = function () {
|
||||
return currentState;
|
||||
};
|
||||
|
||||
/**
|
||||
* 添加状态跳转监视器
|
||||
*
|
||||
* @param {string} condition
|
||||
* 监视的时机
|
||||
* "* => *" (默认)
|
||||
*
|
||||
* @param {Function} handler
|
||||
* 监视函数,当状态跳转的时候,会接收三个参数
|
||||
* * from - 跳转前的状态
|
||||
* * to - 跳转后的状态
|
||||
* * reason - 跳转的原因
|
||||
*/
|
||||
this.when = function (condition, handler) {
|
||||
if (arguments.length == 1) {
|
||||
handler = condition;
|
||||
condition = '* -> *';
|
||||
}
|
||||
|
||||
var when, resolved, exit, enter;
|
||||
|
||||
resolved = condition.split(BEFORE_ARROW);
|
||||
if (resolved.length == 2) {
|
||||
when = 'before';
|
||||
} else {
|
||||
resolved = condition.split(AFTER_ARROW);
|
||||
if (resolved.length == 2) {
|
||||
when = 'after';
|
||||
}
|
||||
}
|
||||
if (!when) throw new Error('Illegal fsm condition: ' + condition);
|
||||
|
||||
exit = resolved[0];
|
||||
enter = resolved[1];
|
||||
|
||||
handler.condition = {
|
||||
when: when,
|
||||
exit: exit,
|
||||
enter: enter,
|
||||
};
|
||||
|
||||
handlers.push(handler);
|
||||
};
|
||||
}
|
||||
|
||||
function FSMRumtime() {
|
||||
this.fsm = new FSM('normal');
|
||||
}
|
||||
|
||||
return (module.exports = FSMRumtime);
|
||||
});
|
|
@ -0,0 +1,133 @@
|
|||
/* eslint-disable */
|
||||
/**
|
||||
* @fileOverview
|
||||
*
|
||||
* 历史管理
|
||||
*
|
||||
* @author: techird
|
||||
* @copyright: Baidu FEX, 2014
|
||||
*/
|
||||
|
||||
define(function (require, exports, module) {
|
||||
var jsonDiff = require('../tool/jsondiff');
|
||||
|
||||
function HistoryRuntime() {
|
||||
var minder = this.minder;
|
||||
var hotbox = this.hotbox;
|
||||
|
||||
var MAX_HISTORY = 100;
|
||||
|
||||
var lastSnap;
|
||||
var patchLock;
|
||||
var undoDiffs;
|
||||
var redoDiffs;
|
||||
|
||||
function reset() {
|
||||
undoDiffs = [];
|
||||
redoDiffs = [];
|
||||
lastSnap = minder.exportJson();
|
||||
}
|
||||
|
||||
function makeUndoDiff() {
|
||||
var headSnap = minder.exportJson();
|
||||
var diff = jsonDiff(headSnap, lastSnap);
|
||||
if (diff.length) {
|
||||
undoDiffs.push(diff);
|
||||
while (undoDiffs.length > MAX_HISTORY) {
|
||||
undoDiffs.shift();
|
||||
}
|
||||
lastSnap = headSnap;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function makeRedoDiff() {
|
||||
var revertSnap = minder.exportJson();
|
||||
redoDiffs.push(jsonDiff(revertSnap, lastSnap));
|
||||
lastSnap = revertSnap;
|
||||
}
|
||||
|
||||
function undo() {
|
||||
patchLock = true;
|
||||
var undoDiff = undoDiffs.pop();
|
||||
if (undoDiff) {
|
||||
minder.applyPatches(undoDiff);
|
||||
makeRedoDiff();
|
||||
}
|
||||
patchLock = false;
|
||||
}
|
||||
|
||||
function redo() {
|
||||
patchLock = true;
|
||||
var redoDiff = redoDiffs.pop();
|
||||
if (redoDiff) {
|
||||
minder.applyPatches(redoDiff);
|
||||
makeUndoDiff();
|
||||
}
|
||||
patchLock = false;
|
||||
}
|
||||
|
||||
function changed() {
|
||||
if (patchLock) return;
|
||||
if (makeUndoDiff()) redoDiffs = [];
|
||||
}
|
||||
|
||||
function hasUndo() {
|
||||
return !!undoDiffs.length;
|
||||
}
|
||||
|
||||
function hasRedo() {
|
||||
return !!redoDiffs.length;
|
||||
}
|
||||
|
||||
function updateSelection(e) {
|
||||
if (!patchLock) return;
|
||||
var patch = e.patch;
|
||||
switch (patch.express) {
|
||||
case 'node.add':
|
||||
minder.select(patch.node.getChild(patch.index), true);
|
||||
break;
|
||||
case 'node.remove':
|
||||
case 'data.replace':
|
||||
case 'data.remove':
|
||||
case 'data.add':
|
||||
minder.select(patch.node, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.history = {
|
||||
reset: reset,
|
||||
undo: undo,
|
||||
redo: redo,
|
||||
hasUndo: hasUndo,
|
||||
hasRedo: hasRedo,
|
||||
};
|
||||
reset();
|
||||
minder.on('contentchange', changed);
|
||||
minder.on('import', reset);
|
||||
minder.on('patch', updateSelection);
|
||||
|
||||
var main = hotbox.state('main');
|
||||
main.button({
|
||||
position: 'top',
|
||||
label: '撤销',
|
||||
key: 'Ctrl + Z',
|
||||
enable: hasUndo,
|
||||
action: undo,
|
||||
next: 'idle',
|
||||
});
|
||||
main.button({
|
||||
position: 'top',
|
||||
label: '重做',
|
||||
key: 'Ctrl + Y',
|
||||
enable: hasRedo,
|
||||
action: redo,
|
||||
next: 'idle',
|
||||
});
|
||||
}
|
||||
|
||||
window.diff = jsonDiff;
|
||||
|
||||
return (module.exports = HistoryRuntime);
|
||||
});
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue