123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355 |
- // 用于拖拽节点时屏蔽键盘事件
- MWF.xApplication.MinderEditor.Drag = new Class({
- initialize : function( editor ){
- this.editor = editor;
- this.fsm = editor.fsm;
- this.minder = editor.minder;
- this.popmenu = editor.popmenu;
- this.receiver = editor.receiver;
- this.receiverElement = this.receiver.element;
- this.setupFsm();
- 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 = this.freeHorizen = false;
- var freeVirtical = this.freeVirtical = false;
- this.frame = null;
- this.minder.on('mousedown', function(e) {
- flag = MOUSE_HAS_DOWN;
- var rect = this.minder.getPaper().container.getBoundingClientRect();
- downX = e.originEvent.clientX;
- downY = e.originEvent.clientY;
- containerY = rect.top;
- maxX = rect.width;
- maxY = rect.height;
- }.bind(this));
- this.minder.on('mousemove', function(e) {
- if ( this.fsm.state() === 'drag' && flag == MOUSE_HAS_DOWN && this.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) {
- this.move('right', BOUND_CHECK - osx);
- } else if (osx > maxX - BOUND_CHECK) {
- this.move('left', BOUND_CHECK + osx - maxX);
- } else {
- freeHorizen = true;
- }
- if (osy < BOUND_CHECK) {
- this.move('bottom', osy);
- } else if (osy > maxY - BOUND_CHECK) {
- this.move('top', BOUND_CHECK + osy - maxY);
- } else {
- freeVirtical = true;
- }
- if (freeHorizen && freeVirtical) {
- this.move(false);
- }
- }
- if (this.fsm.state() !== 'drag'
- && flag === MOUSE_HAS_DOWN
- && this.minder.getSelectedNode()
- && (Math.abs(downX - e.originEvent.clientX) > BOUND_CHECK
- || Math.abs(downY - e.originEvent.clientY) > BOUND_CHECK)) {
- if (this.fsm.state() === 'popmenu') {
- popmenu.active(Popmenu.STATE_IDLE);
- }
- return this.fsm.jump('drag', 'user-drag');
- }
- }.bind(this));
- window.addEventListener('mouseup', function () {
- flag = MOUSE_HAS_UP;
- if (this.fsm.state() === 'drag') {
- this.move(false);
- return this.fsm.jump('normal', 'drag-finish');
- }
- }.bind(this), false);
- },
- setupFsm: function(){
- // when jumped to drag mode, enter
- this.fsm.when('* -> drag', function() {
- // now is drag mode
- });
- this.fsm.when('drag -> *', function(exit, enter, reason) {
- if (reason == 'drag-finish') {
- // now exit drag mode
- }
- });
- },
- move: function(direction, speed) {
- if (!direction) {
- this.freeHorizen = this.freeVirtical = false;
- this.frame && kity.releaseFrame(this.frame);
- this.frame = null;
- return;
- }
- if (!this.frame) {
- this.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, this.minder));
- }
- }
- });
- MWF.xApplication.MinderEditor.FSM = new Class({
- initialize: function( defaultState ){
- this.currentState = defaultState;
- this.BEFORE_ARROW = ' - ';
- this.AFTER_ARROW = ' -> ';
- this.handlers = [];
- this.debug = new MWF.xApplication.MinderEditor.Debug('fsm');
- },
- /**
- * 状态跳转
- *
- * 会通知所有的状态跳转监视器
- *
- * @param {string} newState 新状态名称
- * @param {any} reason 跳转的原因,可以作为参数传递给跳转监视器
- */
- jump: function(newState, reason) {
- if (!reason) throw new Error('Please tell fsm the reason to jump');
- var oldState = this.currentState;
- var notify = [oldState, newState].concat([].slice.call(arguments, 1));
- var i, handler;
- // 跳转前
- for (i = 0; i < this.handlers.length; i++) {
- handler = this.handlers[i];
- if (this.handlerConditionMatch(handler.condition, 'before', oldState, newState)) {
- if (handler.apply(null, notify)) return;
- }
- }
- this.currentState = newState;
- this.debug.log('[{0}] {1} -> {2}', reason, oldState, newState);
- // 跳转后
- for (i = 0; i < this.handlers.length; i++) {
- handler = this.handlers[i];
- if (this.handlerConditionMatch(handler.condition, 'after', oldState, newState)) {
- handler.apply(null, notify);
- }
- }
- return this.currentState;
- },
- /**
- * 返回当前状态
- * @return {string}
- */
- state : function() {
- return this.currentState;
- },
- /**
- * 添加状态跳转监视器
- *
- * @param {string} condition
- * 监视的时机
- * "* => *" (默认)
- *
- * @param {Function} handler
- * 监视函数,当状态跳转的时候,会接收三个参数
- * * from - 跳转前的状态
- * * to - 跳转后的状态
- * * reason - 跳转的原因
- */
- when : function(condition, handler) {
- this.debug.log('[{0}] {1} ', condition, handler );
- if (arguments.length == 1) {
- handler = condition;
- condition = '* -> *';
- }
- var when, resolved, exit, enter;
- resolved = condition.split(this.BEFORE_ARROW);
- if (resolved.length == 2) {
- when = 'before';
- } else {
- resolved = condition.split(this.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
- };
- this.handlers.push(handler);
- },
- handlerConditionMatch: function (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;
- }
- });
- //键盘事件接收/分发器
- MWF.xApplication.MinderEditor.Receiver = new Class({
- initialize: function( editor ){
- this.editor = editor;
- this.minder = editor.minder;
- this.fsm = editor.fsm;
- this.key = editor.key;
- // 接收事件的 div
- var element = this.element = document.createElement('div');
- element.contentEditable = true;
- /**
- * @Desc: 增加tabindex属性使得element的contenteditable不管是trur还是false都能有focus和blur事件
- * @Editor: Naixor
- * @Date: 2015.09.14
- */
- element.setAttribute("tabindex", -1);
- element.classList.add('receiver');
- element.onkeydown = element.onkeypress = element.onkeyup = this.dispatchKeyEvent.bind(this);
- this.editor.contentNode.appendChild(element);
- this.selectAll();
- this.minder.on('beforemousedown', this.selectAll.bind(this));
- this.minder.on('receiverfocus', this.selectAll.bind(this));
- this.minder.on('readonly', function() {
- // 屏蔽minder的事件接受,删除receiver和popmenu
- this.minder.disable();
- this.element.parentElement.removeChild(this.element);
- //this.editor.popmenu.$container.removeChild(editor.popmenu.$element);
- }.bind(this));
- // 侦听器,接收到的事件会派发给所有侦听器
- this.listeners = [];
- },
- selectAll: function() {
- // 保证有被选中的
- if (!this.element.innerHTML) this.element.innerHTML = ' ';
- var range = document.createRange();
- var selection = window.getSelection();
- range.selectNodeContents(this.element);
- selection.removeAllRanges();
- selection.addRange(range);
- this.element.focus();
- },
- /**
- * @Desc: 增加enable和disable方法用于解决热核态的输入法屏蔽问题
- * @Editor: Naixor
- * @Date: 2015.09.14
- */
- enable: function() {
- this.element.setAttribute("contenteditable", true);
- },
- disable: function() {
- this.element.setAttribute("contenteditable", false);
- },
- /**
- * @Desc: hack FF下div contenteditable的光标丢失BUG
- * @Editor: Naixor
- * @Date: 2015.10.15
- */
- fixFFCaretDisappeared: function() {
- this.element.removeAttribute("contenteditable");
- this.element.setAttribute("contenteditable", "true");
- this.element.blur();
- this.element.focus();
- },
- /**
- * 以此事件代替通过mouse事件来判断receiver丢失焦点的事件
- * @editor Naixor
- * @Date 2015-12-2
- */
- onblur: function (handler) {
- this.element.onblur = handler;
- },
- // 侦听指定状态下的事件,如果不传 state,侦听所有状态
- listen : function(state, listener) {
- if (arguments.length == 1) {
- listener = state;
- state = '*';
- }
- listener.notifyState = state;
- this.listeners.push(listener);
- },
- dispatchKeyEvent: function (e) {
- var _self = this;
- e.is = function(keyExpression) {
- var subs = keyExpression.split('|');
- for (var i = 0; i < subs.length; i++) {
- if (_self.key.is(this, subs[i])) return true;
- }
- return false;
- };
- var listener, jumpState;
- for (var i = 0; i < this.listeners.length; i++) {
- listener = this.listeners[i];
- // 忽略不在侦听状态的侦听器
- if (listener.notifyState != '*' && listener.notifyState != this.fsm.state()) {
- continue;
- }
- /**
- *
- * 对于所有的侦听器,只允许一种处理方式:跳转状态。
- * 如果侦听器确定要跳转,则返回要跳转的状态。
- * 每个事件只允许一个侦听器进行状态跳转
- * 跳转动作由侦听器自行完成(因为可能需要在跳转时传递 reason),返回跳转结果即可。
- * 比如:
- *
- * ```js
- * receiver.listen('normal', function(e) {
- * if (isSomeReasonForJumpState(e)) {
- * return fsm.jump('newstate', e);
- * }
- * });
- * ```
- */
- if ( listener.call(null, e)) {
- return;
- }
- }
- }
- });
|