学习 kityminder 笔记(五)

接着学习 kityminder, 前面其实没看完 kity, 大致了解之后, 我们先看看 minder 部分, 两者结合起来学更好些.

下载 kityminder-core (按文档说只含核心部分), 从 git-hub, 建立起开发/构造环境, 打开网页 dev.html 看
果然能出来脑图了, 真不错! 于是从 dev.html 网页开始, 其先加载 kity.js, 然后通过 seajs 加载 kityminder
各个 js 模块. 前述我们分别简单了解过 kity, seajs, 这里就略过, 直接去 kityminder src 目录找.

于是找到 src/kityminder.js, 代码大致是:
   define( ...   // seajs 要求的格式, CMD 规范.
       kityminder = {
           许多许多 module = require('...xxx.js')   
       }
注解说顺序是有讲究的 (有依赖关系的), 所以我们也大致按照顺序(大致)了解下各个 module.

 

== core/utils.js ==

提供一组函数, 常见的如 extend, each, trim, clone 等就略了.
另有 uuid(group): 负责为指定 group 生成下一个可用 id. 估计为每种 svg 元素生成 id.
guid(): 生成一个类似于(像) guid 的长的随机字符串?
   我们实验一下, 在浏览器 console 输入:
      var utils = seajs.require('core/utils.js')
      utils.guid() --> 打印出 `azzsqpc8jm68' (随机的), 再来一个: `azzsqq169tz4', 前几个字符总是 azzsq, 不太满意吧...

comparePlainObject(a,b): 比较两个 plain 对象是否等价? 方法是用将两个对象用 JSON 字符串化, 然后
   比较两个字符串... 我试验了一下, 这两个 plain-object 不等价:
      var a = {x:1, y:2}, b = {y:2, x:1};
      utils.comparePlainObject(a, b) --> false! 所以不要弄太刁钻的对象......

剩下的看着也不是很重要, 暂时略...

 

== kityminder.Minder (core/minder.js) ==

注释说暴露在 window 上的唯一变量. 应该就是 window.km 对象.

定义为 Minder = kity.createClass(..., ctor)

其中这里 ctor 不是很复杂, 也没有别的方法, 估计会在别的模块 extend 方法到 minder 类.

在 ctor 中使用了 initHooks[] 机制, 对每个注册的 init-hook 用 this 产生调用.

似乎还有 event 机制, 在 ctor 最后使用 fire() 触发 'finishInitHook' 事件.

但是我没看到 Minder 类从其它类派生(这里没看到), 以及混入别的mixin, 那么哪里来的 event 机制?
   也许后续的模块 mixin 的...?

 

== Command (core/command.js) ==

按照架构文档说明, abstract Command 表示一条在 KityMinder 上执行的命令.

var Command = createClass(..., {
   execute: 子类必须实现.
   (optional) revert: 子类可选实现, 估计用于实现 undo.
   set/get ContentChanged: 设置/获取(内容变更)状态
   set/get SelectionChanged: 类似...
   以及其它几个可选重载的函数.
});

可以认为这是一个接口定义: ICommand.

然后向 Minder 类扩展了几个方法:
   _getCommand(name): 获取维护在 _commands{} 中的命令. 里面有一堆各种命令(对象).
   _queryCommand(n, t, a): 看起来像先获得 cmd, 然后调用其(可选的) query+t 方法 ...?
      我找了一个 move 方法看, 里面有 queryState, queryValue 方法... 通过这种方法调用?
   queryCommandState(): 相当于调用 cmd.queryState() ...?
   queryCommandValue(): 相当于 cmd.queryValue() ... 用 _queryCommand() 方法实现的.
   execCommand(name): 执行指定命令.
       特色是有防止重入(?), 调用命令前产生事件 `beforeExecCommand', `preExecCommand',
       执行之后产生事件 `execCommand', 'contentchange'? 等.

 

 

== Node (core/node.js) ==

Node/MinderNode 表示一个脑图节点. (这里节点的概念是什么呢? 一个文本框就是一个节点吧?)

var MinderNode = createClass(..., {
    ctor: 创建一个游离的脑图节点. 看到里面设置的数据有:
    parent: 估计是父节点. 根节点的 parent=null.
    children: 顾名思义, 此节点的子节点, 是一个数组.
    root: 可能是此图的根节点, 如果自己是 root, 则就指向自己.
    data: 某些内部数据? ctor 中看到有 guid, created(time). 但是在 DOM 中未看到? 也许被后续覆盖了?
    initContainer(): 设置一个 rc, 是一个 Group 对象. 后面看 Group 时再理解.
       这里 rc 似乎是 RenderContainer 的缩写, 不是 rect...
    isRoot(), getRoot(), isLeaf(), getParent(), getSiblings() 等比较简单, 就略了.
        有些函数的实现看着有点那个...
    getData(), setData(): 获取/设置 this.data 中的值.
    getText(), setText(): 获取/设置节点文本.
    preTraverse(): 先序(前序?)遍历当前节点树. 含递归.
    postTraverse(): 后序遍历, 递归的. 这里后序遍历指先遍历所有子元素, 再访问自己.
    traverse(): 即后序遍历.
    大量树有关获取信息, 维护的函数先略了, 总之和 DOM 树有点像就是了.
    clone(): 克隆一个节点(含所有子节点).
    compareTo(other): 比较 this vs. other, 用前面的 comparePlainObject()...
    getMinder(): 每个 node 都属于且只属于一个 minder. 新建的估计为 null?
});

实验: 在 console 中输入 var node = km.getRoot() 估计得到根节点, 类型就是 MinderNode.

向 Minder 类扩展了不少方法: {
    get/setRoot(): 获取/设置根 node.
    getAllNode(), getNodeById(), create/remove/appendNode(), ... 看名字就差不多了, 略.
}

 

== core/option.js: 提供脑图选项支持 ==

注册一个 init-hook: 为 minder 添加一个属性 this._defaultOption={}

向 Minder 类扩展了方法: {
   get/set[Default]Option() ... 看名字也就差不多了.
}

这个类这么小, 是否有必要单独弄一个...?

 

== core/animate.js 动画控制 ==

注册 init-hook: 根据选项 使能/禁用 animate. 这里依赖先有 option.js 部分.

扩展 Minder 类方法 enable/disable Animation(). 简单略.

这个类叫动画控制, 不如叫动画选项...

 

== MinderEvent(core/event.js): 表示一个脑图中发生的事件 ==

定义类 MinderEvent = createClass(..., {
   ctor(): 构造, 复杂参数先略, 可能有些事件是从 kity Event 派生的.
   getPosition(): 获取事件发生的坐标?
   getTargetNode(), stopPropagation(), preventDefault() ... 看起来像 DOM 事件模式.
});

使用注册 init-hook 的方法初始化 _initEvents().

向 Minder 类扩展: {
   _initEvents(), _resetEvents(): 略.
   _bindEvents(): 绑定很多事件, 有 click,dblclick, mousedown, contextmenu, mouseup, mousemove
      mouseover, mousewheel, DOMMousScroll (各种鼠标事件), touchxxx(触摸屏事件), dragxxx(拖放事件)
      以及整个窗口的 resize 事件. 通过一个属性 _firePharse bind(), 稍后了解.
   dispatchKeyEvent(): 派发键盘事件. 注意上面的绑定不含键盘事件, 也许绑定到整个页面?
   _firePharse(e): 创建 MinderEvent() 包装原生 DOM 事件 e;
       产生 before+e, pre+e, e, after+e 事件调用序列. 看到要创建大量事件对象, 我担心是否有必要...
   _interactChange(e): 似乎是异步触发一个 'interactchange' 事件. 估计是当前不方便立刻执行, 需要
       在下一个空闲(timeout) 时间执行.
   _listen(): 注册指定类型的(脑图)事件的回调(侦听).
   _fire(): 根据脑图事件类型, 找到注册的回调函数(数组), 产生回调. (有些 status, propagation 部分暂略).
   on(), off(), fire(): 事件相关别名/包装.
}

这里也回答了前面的问题, 即 Minder 的 event 从哪里来的问题.

 

== core/compatibility.js ==

处理脑图数据从低版本到当前版本升级.

 

== core/keymap.js ==

键的名字(如 Escape) 到值(如=27) 的映射表的建立.

 

== core/shortcut.js 快捷键支持 ==

向 MinderEvent 扩展 isShortcutKey() 方法: 其根据 metakey? 判断是否是快捷键.

注册 init-hook: 初始化 shortcut-key 支持.

向 Minder 类扩展方法: {
   _initShortcutKey(), _bindShortcutKey(): 绑定 'keydown' 事件, 事件发生时查找绑定于该键的处理函数,
       并调用它. 实际看了一下, 大约有 backspace, del, enter, tab 等20个键盘快捷键.
   addShortcut(keys, fn): 添加对 keys(可空格分隔多个) 快捷键的回调函数 fn. 用 '::' 分隔表示某种东西暂略.
   get/addCommandShortcutKeys(cmd, keys): 可能是快捷键 keys 和命令 cmd 绑定起来.
       查看了一下, 如有 bold='ctrl+b', appendchildnode='normal::Insert|Tab' etc.
   supportClipboardEvent(): 判断是否支持原生 clipboard.

 

== core/status.js 状态切换控制 ==

向 Minder 类扩展: {
   _initStatus(): 此为 init-hook, 初始化两个成员变量值.
   get/setStatus(): 获取/设置状态. 状态的变化引发 'statuschange' 事件.
   rollbackStatus(): 设置状态为 _rollbackStatus, 略. 
}

似乎意味着状态(status)能切换到其它值, 并支持 rollback 状态. 但只能 rollback 一次, 是什么意图呢?

 

== paper.js: 初始化渲染容器 ==

向 Minder 类扩展方法: {
   _initPaper(): 此为 init-hook 方法. 创建 this._paper = new kity.Paper() 并设置一些东西,
      创建根节点, 创建 render-container 等.
   get/_addRenderContainer(): 创建 kity.Group() 应该是 <g> 元素 作为 render-container.
      经过查看, 在 svg 元素下面有 g#kity_g_8 元素下面有 g#minder1 元素, 估计是此处创建的 rc.
   renderTo(): 估计是将 minder 渲染(放置)到指定 div 元素(容器), 产生事件 paperrender.
   getPaper(), getRenderTarget(): 略.
}

这里把 minder 和 paper 关联起来.

 

== select.js 选区管理 ==

向 Minder 类扩展: {
   _initSelection(): 此为 init-hook 方法.
   renderChangedSelection(): 如果选择的元素集合发生了变化则产生事件 'selectionchange'.
      并对每个改变了的元素(节点)调用 render() 方法.
   getSelectedNodes(): 得到当前被选中的节点(数组引用).
   getSelectedNode(): 单数的, 得到第一个被选中的节点, 没有则为 null.
   removeAllSelectedNodes(): 清空被选中, 产生事件 'selectionclear'.
   removeSelectedNodes(): 清除部分被选中..
   select(): 选中指定节点s. selectById() 相似.
   toggleSelect(): 略. (有注解)
   getSelectedAncestors(): 似乎是找被选中节点的祖先节点?
}

向 MinderNode 扩展方法 isSelected(): 判断是否被选中.
   我是设置一个标志到 node 中的...

 

== core/focus.js ==

注册 init-hook: 绑定事件 'beforemousedown', 'paperrender'.

扩展 Minder 类: {
   focus(): 设置焦点给此 minder, 产生 'focus' 事件. 
   blur(): 取消焦点, 产生 'blur' 事件.
   isFocused(): 判定. 方法是将 'focus' css 类名添加/删除自 renderTarget(g 元素)
}

 

(更多待学习...)

 

你可能感兴趣的:(学习 kityminder 笔记(五))