学习 kityminder 笔记(五)

接着学习 kityminder, 前面其实没看完 kity, 大体了解以后, 咱们先看看 minder 部分, 二者结合起来学更好些. css

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

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

 

== core/utils.js == git

提供一组函数, 常见的如 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) == app

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

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

其中这里 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 元素)
}

 

(更多待学习...)

相关文章
相关标签/搜索