diff --git a/.bowerrc b/.bowerrc new file mode 100644 index 00000000..cabe7fcb --- /dev/null +++ b/.bowerrc @@ -0,0 +1,3 @@ +{ + "directory": "lib/bower" +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 84659b06..5889460e 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ _drafts/ native-support/archive/src/tmp/ native-support/archive/upload/ ui/theme/default/css/*.css -ui/theme/default/css/*.css.map \ No newline at end of file +ui/theme/default/css/*.css.map +lib/bower/ \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index d07e504a..b2aaabd9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,9 @@ [submodule "lib/fio"] path = lib/fio url = https://github.com/fex-team/fio +[submodule "lib/umeditor"] + path = lib/umeditor + url = https://github.com/fex-team/umeditor +[submodule "lib/marked"] + path = lib/marked + url = https://github.com/chjj/marked.git diff --git a/CHANGELOG.md b/CHANGELOG.md index b429caab..19c0b9ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,14 @@ # KityMinder 更新日志 +## v1.3.4 + +### 功能更新 + +1. 支持节点备注(GFM 格式) +2. 支持更多快捷选择 +3. 支持展开到更多层次的快捷操作 + ## v1.3.3 ### 功能更新 diff --git a/Gruntfile.js b/Gruntfile.js index a1d5ef36..1a7be5f9 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -131,12 +131,21 @@ module.exports = function(grunt) { }] }, - noCache: { + pageNoCache: { src: distPages, overwrite: true, replacements: [{ - from: /src=\"(.+?)\.js\"/ig, - to: 'src="$1.js?_=' + (+new Date()) + '"' + from: /(src|href)=\"(.+?)\.(js|css)\"/ig, + to: '$1="$2.$3?_=' + (+new Date()) + '"' + }] + }, + + imageNoCache: { + src: 'dist/ui/theme/default/css/default.all.css', + overwrite: true, + replacements: [{ + from: /\.png/ig, + to: '.png?_=' + (+new Date()) }] } }, diff --git a/bower.json b/bower.json new file mode 100644 index 00000000..570c9915 --- /dev/null +++ b/bower.json @@ -0,0 +1,6 @@ +{ + "name": "kityminder", + "dependencies": { + "codemirror": "4.7.0" + } +} \ No newline at end of file diff --git a/edit.html b/edit.html index 311551c9..f95583d2 100644 --- a/edit.html +++ b/edit.html @@ -26,53 +26,6 @@
- -
- - - - - - - - - - KityMinder - - - under - BSD License - . - Powered by f-cube, - FEX - | - Source - - Bug - | - Contact Us - -
diff --git a/import.js b/import.js index 0eb87e00..f16517a5 100755 --- a/import.js +++ b/import.js @@ -21,6 +21,15 @@ { path: 'lib/fio/src/fio.js', pack: 'index|edit' }, { path: 'lib/fio/provider/netdisk/oauth.js', pack: 'index|edit' }, { path: 'lib/fio/provider/netdisk/netdisk.js', pack: 'edit' }, + { path: 'lib/marked/lib/marked.js', pack: 'edit|share' }, + { path: 'lib/bower/codemirror/lib/codemirror.js', pack: 'edit' }, + { path: 'lib/bower/codemirror/mode/xml/xml.js', pack: 'edit' }, + { path: 'lib/bower/codemirror/mode/javascript/javascript.js', pack: 'edit' }, + { path: 'lib/bower/codemirror/mode/css/css.js', pack: 'edit' }, + { path: 'lib/bower/codemirror/mode/htmlmixed/htmlmixed.js', pack: 'edit' }, + { path: 'lib/bower/codemirror/mode/markdown/markdown.js', pack: 'edit' }, + { path: 'lib/bower/codemirror/addon/mode/overlay.js', pack: 'edit' }, + { path: 'lib/bower/codemirror/mode/gfm/gfm.js', pack: 'edit' }, /* Kity 依赖库 */ { path: 'lib/kity/dist/kity.js', pack: 'edit|share|m-share' }, @@ -96,6 +105,7 @@ { path: 'src/module/priority.js', pack: 'edit|share|m-share' }, { path: 'src/module/image.js', pack: 'edit|share|m-share' }, { path: 'src/module/resource.js', pack: 'edit|share|m-share' }, + { path: 'src/module/note.js', pack: 'edit|share|m-share' }, { path: 'src/module/view.js', pack: 'edit|share|m-share' }, { path: 'src/module/dragtree.js', pack: 'edit|share|m-share' }, { path: 'src/module/keynav.js', pack: 'edit|share|m-share' }, @@ -200,6 +210,7 @@ { path: 'ui/ribbon/idea/attachment.js', pack: 'edit' }, { path: 'ui/ribbon/idea/link.js', pack: 'edit' }, { path: 'ui/ribbon/idea/image.js', pack: 'edit' }, + { path: 'ui/ribbon/idea/note.js', pack: 'edit' }, { path: 'ui/ribbon/idea/priority.js', pack: 'edit' }, { path: 'ui/ribbon/idea/progress.js', pack: 'edit' }, { path: 'ui/ribbon/idea/resource.js', pack: 'edit' }, @@ -214,7 +225,8 @@ /* UI Ribbon「视图」面板 */ { path: 'ui/ribbon/view/fullscreen.js', pack: 'edit' }, - { path: 'ui/ribbon/view/level.js', pack: 'edit' } + { path: 'ui/ribbon/view/level.js', pack: 'edit' }, + { path: 'ui/ribbon/view/select.js', pack: 'edit' } ]; if (typeof(module) === 'object' && module.exports) { diff --git a/import.mobile.share.js b/import.mobile.share.js deleted file mode 100644 index a14211be..00000000 --- a/import.mobile.share.js +++ /dev/null @@ -1,180 +0,0 @@ -/** - * Created by zhangbo21 on 14-9-10. - */ -/** - * 开发版本的文件导入 - */ -(function() { - var paths = [ - - /* 依赖库 */ - 'lib/jquery-2.1.1.js', - 'lib/jquery.transit.min.js', - 'lib/promise-1.0.0.js', - 'lib/fui/dev-lib/jhtmls.min.js', - 'lib/fui/dist/fui.all.js', - - /* Kity 依赖库 */ - 'lib/kity/dist/kity.js', - - /* 核心代码 */ - 'src/core/kityminder.js', - 'src/core/utils.js', - 'src/core/browser.js', - 'src/core/minder.js', - 'src/core/option.js', - 'src/core/event.js', - 'src/core/status.js', - 'src/core/paper.js', - 'src/core/readonly.js', - 'src/core/command.js', - 'src/core/node.js', - 'src/core/module.js', - 'src/core/data.js', - 'src/core/compatibility.js', - 'src/core/render.js', - 'src/core/connect.js', - 'src/core/theme.js', - 'src/core/layout.js', - 'src/core/template.js', - 'src/core/select.js', - 'src/core/lang.js', - 'src/core/defaultoptions.js', - 'src/core/preference.js', - 'src/core/keymap.js', - 'src/core/key.js', - 'src/core/contextmenu.js', - - /* 布局 */ - 'src/layout/mind.js', - 'src/layout/filetree.js', - 'src/layout/btree.js', - - /* 连线 */ - 'src/connect/bezier.js', - 'src/connect/poly.js', - 'src/connect/arc.js', - 'src/connect/under.js', - 'src/connect/l.js', - - /* 皮肤 */ - 'src/theme/default.js', - 'src/theme/snow.js', - 'src/theme/fresh.js', - - /* 模板 */ - 'src/template/default.js', - 'src/template/structure.js', - 'src/template/filetree.js', - 'src/template/right.js', - - /* 模块 */ - 'src/module/node.js', - 'src/module/text.js', - 'src/module/expand.js', - 'src/module/outline.js', - 'src/module/geometry.js', - 'src/module/progress.js', - 'src/module/priority.js', - 'src/module/image.js', - 'src/module/resource.js', - 'src/module/view.js', - 'src/module/dragtree.js', - 'src/module/keyboard.js', - 'src/module/select.js', - 'src/module/basestyle.js', - 'src/module/font.js', - 'src/module/zoom.js', - 'src/module/hyperlink.js', - 'src/module/arrange.js', - 'src/module/paste.js', - 'src/module/style.js', - - /* 格式支持 */ - 'src/protocol/plain.js', - 'src/protocol/json.js', - 'src/protocol/png.js', - 'src/protocol/svg.js', - - /* UI 基础 */ - 'ui/ui.js', - 'ui/eve.js', - 'ui/fuix.js', - // 'ui/fiox.js', - 'ui/doc.js', - - /* UI 组件 */ - // 'ui/widget/commandbutton.js', - // 'ui/widget/commandbuttonset.js', - // 'ui/widget/commandinputmenu.js', - // 'ui/widget/friendlytimespan.js', - // 'ui/widget/locallist.js', - // 'ui/widget/netdiskfinder.js', - 'ui/widget/menutab.js', - - /* UI 菜单 */ -// 'ui/menu/menu.js', -// 'ui/menu/header.js', - - /* UI 菜单 - 新建 */ - // 'ui/menu/new/new.js', - - /* UI 菜单 - 打开 */ - // 'ui/menu/open/open.js', - // 'ui/menu/open/recent.js', - // 'ui/menu/open/netdisk.js', - // 'ui/menu/open/local.js', - // 'ui/menu/open/draft.js', - - /* UI 菜单 - 保存 */ -// 'ui/menu/save/save.js', - // 'ui/menu/save/netdisk.js', -// 'ui/menu/save/download.js', - - /* UI 菜单 - 分享 */ - // 'ui/menu/share/share.js', - 'ui/menu/share/m-share.js', - - /* UI Top Bar */ - // 'ui/topbar/history.js', - // 'ui/topbar/user.js', -// 'ui/topbar/search.js', - 'ui/topbar/m-logo.js', - 'ui/topbar/switch-view.js', - 'ui/topbar/title.js' - - /* UI Ribbon */ - // 'ui/ribbon/tabs.js', - - /* UI Ribbon「思路」面板 */ - // 'ui/ribbon/idea/insert.js', - // 'ui/ribbon/idea/arrange.js', - // 'ui/ribbon/idea/operation.js', - // 'ui/ribbon/idea/attachment.js', - // 'ui/ribbon/idea/link.js', - // 'ui/ribbon/idea/image.js', - // 'ui/ribbon/idea/priority.js', - // 'ui/ribbon/idea/progress.js', - // 'ui/ribbon/idea/resource.js', - - /* UI Ribbon「展示」面板 */ - // 'ui/ribbon/appearence/template.js', - // 'ui/ribbon/appearence/theme.js', - // 'ui/ribbon/appearence/layout.js', - // 'ui/ribbon/appearence/style.js', - // 'ui/ribbon/appearence/font.js', - // 'ui/ribbon/appearence/color.js', - - /* UI Ribbon「视图」面板 */ - // 'ui/ribbon/view/level.js' - ]; - - if (typeof(module) === 'object' && module.exports) { - module.exports = paths; - } else if (document) { - while (paths.length) { - /* jshint browser:true */ - window.document.write(''); - } - } -})(); \ No newline at end of file diff --git a/import.share.js b/import.share.js deleted file mode 100644 index 134d3301..00000000 --- a/import.share.js +++ /dev/null @@ -1,168 +0,0 @@ -/** - * 开发版本的文件导入 - */ -(function() { - - /* 可能的文件路径,已按照依赖关系排序 */ - var paths = [ - - /* 依赖库 */ - 'lib/jquery-2.1.1.js', - 'lib/jquery.transit.min.js', - 'lib/promise-1.0.0.js', - 'lib/fui/dev-lib/jhtmls.min.js', - 'lib/fui/dist/fui.all.js', - - /* Kity 依赖库 */ - 'lib/kity/dist/kity.js', - - /* 核心代码 */ - 'src/core/kityminder.js', - 'src/core/utils.js', - 'src/core/browser.js', - 'src/core/minder.js', - 'src/core/option.js', - 'src/core/event.js', - 'src/core/status.js', - 'src/core/paper.js', - 'src/core/readonly.js', - 'src/core/command.js', - 'src/core/node.js', - 'src/core/module.js', - 'src/core/data.js', - 'src/core/compatibility.js', - 'src/core/render.js', - 'src/core/connect.js', - 'src/core/theme.js', - 'src/core/layout.js', - 'src/core/template.js', - 'src/core/select.js', - 'src/core/lang.js', - 'src/core/defaultoptions.js', - 'src/core/preference.js', - 'src/core/keymap.js', - 'src/core/key.js', - 'src/core/contextmenu.js', - - /* 布局 */ - 'src/layout/default.js', - 'src/layout/default.connect.js', - 'src/layout/bottom.js', - 'src/layout/filetree.js', - - /* 皮肤 */ - 'src/theme/default.js', - 'src/theme/snow.js', - 'src/theme/fresh.js', - - /* 模板 */ - 'src/template/structure.js', - - /* 模块 */ - 'src/module/node.js', - 'src/module/text.js', - 'src/module/expand.js', - 'src/module/outline.js', - 'src/module/geometry.js', - 'src/module/progress.js', - 'src/module/priority.js', - 'src/module/image.js', - 'src/module/resource.js', - 'src/module/view.js', - 'src/module/dragtree.js', - 'src/module/keyboard.js', - 'src/module/select.js', - 'src/module/basestyle.js', - 'src/module/font.js', - 'src/module/zoom.js', - 'src/module/hyperlink.js', - 'src/module/arrange.js', - 'src/module/paste.js', - 'src/module/style.js', - - /* 格式支持 */ - 'src/protocol/plain.js', - 'src/protocol/json.js', - 'src/protocol/png.js', - 'src/protocol/svg.js', - - /* UI 基础 */ - 'ui/ui.js', - 'ui/eve.js', - 'ui/fuix.js', - // 'ui/fiox.js', - 'ui/doc.js', - - /* UI 组件 */ - // 'ui/widget/commandbutton.js', - // 'ui/widget/commandbuttonset.js', - // 'ui/widget/commandinputmenu.js', - // 'ui/widget/friendlytimespan.js', - // 'ui/widget/locallist.js', - // 'ui/widget/netdiskfinder.js', - 'ui/widget/menutab.js', - - /* UI 菜单 */ - 'ui/menu/menu.js', - 'ui/menu/header.js', - - /* UI 菜单 - 新建 */ - // 'ui/menu/new/new.js', - - /* UI 菜单 - 打开 */ - // 'ui/menu/open/open.js', - // 'ui/menu/open/recent.js', - // 'ui/menu/open/netdisk.js', - // 'ui/menu/open/local.js', - // 'ui/menu/open/draft.js', - - /* UI 菜单 - 保存 */ - 'ui/menu/save/save.js', - // 'ui/menu/save/netdisk.js', - 'ui/menu/save/download.js', - - /* UI 菜单 - 分享 */ - // 'ui/menu/share/share.js', - 'ui/menu/share/view.js', - - /* UI Top Bar */ - // 'ui/topbar/history.js', - // 'ui/topbar/user.js', - 'ui/topbar/search.js', - 'ui/topbar/title.js', - - /* UI Ribbon */ - // 'ui/ribbon/tabs.js', - - /* UI Ribbon「思路」面板 */ - // 'ui/ribbon/idea/insert.js', - // 'ui/ribbon/idea/arrange.js', - // 'ui/ribbon/idea/operation.js', - // 'ui/ribbon/idea/attachment.js', - // 'ui/ribbon/idea/link.js', - // 'ui/ribbon/idea/image.js', - // 'ui/ribbon/idea/priority.js', - // 'ui/ribbon/idea/progress.js', - // 'ui/ribbon/idea/resource.js', - - /* UI Ribbon「展示」面板 */ - // 'ui/ribbon/appearence/template.js', - // 'ui/ribbon/appearence/theme.js', - // 'ui/ribbon/appearence/layout.js', - // 'ui/ribbon/appearence/style.js', - // 'ui/ribbon/appearence/font.js', - // 'ui/ribbon/appearence/color.js', - - /* UI Ribbon「视图」面板 */ - // 'ui/ribbon/view/level.js' - ]; - - if (typeof(module) === 'object' && module.exports) { - module.exports = paths; - } else if (document) { - while (paths.length) { - /* jshint browser:true */ - window.document.write(''); - } - } -})(); \ No newline at end of file diff --git a/lang/zh-cn/zh-cn.js b/lang/zh-cn/zh-cn.js index ffde7890..d1e41d40 100755 --- a/lang/zh-cn/zh-cn.js +++ b/lang/zh-cn/zh-cn.js @@ -156,11 +156,18 @@ KityMinder.LANG['zh-cn'] = { 'arrangedown': '下移', 'resetlayout': '整理布局', 'expandtoleaf': '展开全部节点', - 'collapsetolevel1': '收起到一级节点', + 'expandtolevel1': '展开到一级节点', + 'expandtolevel2': '展开到二级节点', + 'expandtolevel3': '展开到三级节点', + 'expandtolevel4': '展开到四级节点', + 'expandtolevel5': '展开到五级节点', + 'expandtolevel6': '展开到六级节点', 'fullscreen': '全屏', 'outline': '大纲' }, + 'expandtoleaf': '展开', + 'back': '返回', 'undo': '撤销 (Ctrl + Z)', @@ -308,8 +315,10 @@ KityMinder.LANG['zh-cn'] = { }, 'link': '链接', 'image': '图片', + 'note': '备注', 'removelink': '移除已有连接', 'removeimage': '移除已有图片', + 'removenote': '移除已有备注', 'resetlayout': '整理', 'justnow': '刚刚', @@ -335,7 +344,14 @@ KityMinder.LANG['zh-cn'] = { 'load_success': '{0} 加载成功', 'save_success': '{0} 已保存于 {1}', - 'autosave_success': '{0} 已自动保存于 {1}' + 'autosave_success': '{0} 已自动保存于 {1}', + + 'selectall': '全选', + 'selectrevert': '反选', + 'selectsiblings': '选择兄弟节点', + 'selectlevel': '选择同级节点', + 'selectpath': '选择路径', + 'selecttree': '选择子树' }, 'popupcolor': { 'clearColor': '清空颜色', diff --git a/lib/marked b/lib/marked new file mode 160000 index 00000000..3e02a699 --- /dev/null +++ b/lib/marked @@ -0,0 +1 @@ +Subproject commit 3e02a69921b9b4009d0b17aa1fe0ae2546f96de2 diff --git a/package.json b/package.json index 41c5bade..3416bde2 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "kityminder", "title": "kityminder", "description": "Kity Minder", - "version": "1.3.3", + "version": "1.3.4", "homepage": "https://github.com/fex-team/kityminder", "author": { "name": "f-cube @ FEX", diff --git a/src/core/kityminder.js b/src/core/kityminder.js index f65953a6..53373f8c 100644 --- a/src/core/kityminder.js +++ b/src/core/kityminder.js @@ -3,7 +3,7 @@ var KityMinder = window.KM = window.KityMinder = function() { instanceId = 0, uuidMap = {}; return { - version: '1.3.3', + version: '1.3.4', uuid: function(name) { name = name || 'unknown'; uuidMap[name] = uuidMap[name] || 0; diff --git a/src/core/node.js b/src/core/node.js index 1bb7dcb3..8ec9a4b3 100644 --- a/src/core/node.js +++ b/src/core/node.js @@ -121,6 +121,8 @@ var MinderNode = KityMinder.MinderNode = kity.createClass('MinderNode', { getText: function(str2arr) { var text = this.getData('text') || ''; + text = text.replace('&', '&').replace('<', '<').replace('>', '>'); + if(str2arr){ text = text.split('\n'); } diff --git a/src/module/expand.js b/src/module/expand.js index eb1110b7..c48faa4e 100644 --- a/src/module/expand.js +++ b/src/module/expand.js @@ -6,123 +6,23 @@ KityMinder.registerModule('Expand', function() { STATE_EXPAND = 'expand', STATE_COLLAPSE = 'collapse'; - /** - * 该函数返回一个策略,表示递归到节点指定的层数 - * - * 返回的策略表示把操作(展开/收起)进行到指定的层数 - * 也可以给出一个策略指定超过层数的节点如何操作,默认不进行任何操作 - * - * @param {int} deep_level 指定的层数 - * @param {Function} policy_after_level 超过的层数执行的策略 - */ - function generateDeepPolicy(deep_level, policy_after_level) { - - return function(node, state, policy, level) { - var children, child, i; - - node.setData(EXPAND_STATE_DATA, state); - level = level || 1; - - children = node.getChildren(); - - for (i = 0; i < children.length; i++) { - child = children[i]; - - if (level <= deep_level) { - policy(child, state, policy, level + 1); - } else if (policy_after_level) { - policy_after_level(child, state, policy, level + 1); - } - } - - }; - } - - /** - * 节点展开和收缩的策略常量 - * - * 策略是一个处理函数,处理函数接受 3 个参数: - * - * @param {MinderNode} node 要处理的节点 - * @param {Enum} state 取值为 "expand" | "collapse",表示要对节点进行的操作是展开还是收缩 - * @param {Function} policy 提供当前策略的函数,方便递归调用 - */ - var EXPAND_POLICY = MinderNode.EXPAND_POLICY = { - - /** - * 策略 1:只修改当前节点的状态,不递归子节点的状态 - */ - KEEP_STATE: function(node, state, policy) { - node.setData(EXPAND_STATE_DATA, state); - }, - - generateDeepPolicy: generateDeepPolicy, - - /** - * 策略 2:把操作进行到儿子 - */ - DEEP_TO_CHILD: generateDeepPolicy(2), - - /** - * 策略 3:把操作进行到叶子 - */ - DEEP_TO_LEAF: generateDeepPolicy(Number.MAX_VALUE) - }; - - function setExpandState(node, state, policy) { - var changed = node.isExpanded() ? (state == STATE_COLLAPSE) : (state == STATE_EXPAND); - policy = policy || EXPAND_POLICY.KEEP_STATE; - policy(node, state, policy); - - node.renderTree().getMinder().layout(100); - node.getMinder().fire('contentchange'); - - /* 如何加展开效果: - - var vo = node.getVertexOut(); - - if (state == STATE_EXPAND) { - - var m = node.getGlobalLayoutTransform().clone().translate(vo.x, vo.y); - node.traverse(function(child) { - child.setGlobalLayoutTransform(m); - child.getRenderContainer().setOpacity(0).fadeIn(); - }, true); - node.renderTree().getMinder().layout(300); - - } else { - - node.traverse(function(child) { - child.setLayoutTransform(child.parent == node ? new kity.Matrix().translate(vo.x, vo.y) : null); - child.getRenderContainer().fadeOut(100); - }, true); - - node.getMinder().applyLayoutResult(node, 150).then(function() { - node.renderTree().getMinder().layout(150); - }); - - } - */ - } - // 将展开的操作和状态读取接口拓展到 MinderNode 上 kity.extendClass(MinderNode, { /** - * 使用指定的策略展开节点 + * 展开节点 * @param {Policy} policy 展开的策略,默认为 KEEP_STATE */ - expand: function(policy) { - setExpandState(this, STATE_EXPAND, policy); + expand: function() { + this.setData(EXPAND_STATE_DATA, STATE_EXPAND); return this; }, /** - * 使用指定的策略收起节点 - * @param {Policy} policy 展开的策略,默认为 KEEP_STATE + * 收起节点 */ - collapse: function(policy) { - setExpandState(this, STATE_COLLAPSE, policy); + collapse: function() { + this.setData(EXPAND_STATE_DATA, STATE_COLLAPSE); return this; }, @@ -141,30 +41,40 @@ KityMinder.registerModule('Expand', function() { return !this.isExpanded(); } }); - var ExpandNodeCommand = kity.createClass('ExpandNodeCommand', { + + var ExpandCommand = kity.createClass('ExpandCommand', { base: Command, - execute: function(km) { - var nodes = km.getRoot().getChildren(); - nodes.forEach(function(node) { - node.expand(EXPAND_POLICY.DEEP_TO_LEAF); - }); + + 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) { - return !km.getSelectedNode() ? 0 : -1; + return km.getSelectedNode() ? 0 : -1; } }); - var CollapseNodeCommand = kity.createClass('CollapseNodeCommand', { + + var ExpandToLevelCommand = kity.createClass('ExpandToLevelCommand', { base: Command, - execute: function(km) { - var nodes = km.getRoot().getChildren(); - nodes.forEach(function(node) { - node.collapse(); + execute: function(km, level) { + km.getRoot().traverse(function(node) { + if (node.getLevel() < level) node.expand(); + if (node.getLevel() == level) node.collapse(); }); - }, - queryState: function(km) { - return !km.getSelectedNode() ? 0 : -1; + km.refresh(100); } }); + var Expander = kity.createClass('Expander', { base: kity.Group, @@ -186,6 +96,8 @@ KityMinder.registerModule('Expand', function() { } else { node.expand(); } + node.renderTree().getMinder().layout(100); + node.getMinder().fire('contentchange'); e.stopPropagation(); e.preventDefault(); }); @@ -240,8 +152,8 @@ KityMinder.registerModule('Expand', function() { }); return { commands: { - 'expandtoleaf': ExpandNodeCommand, - 'collapsetolevel1': CollapseNodeCommand + 'expand': ExpandCommand, + 'expandtolevel': ExpandToLevelCommand }, events: { 'layoutapply': function(e) { @@ -267,25 +179,60 @@ KityMinder.registerModule('Expand', function() { 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' + 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: 'collapsetolevel1' + 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 - }], - commandShortcutKeys: { - 'expandtoleaf': 'Alt+`', - 'collapsetolevel1': 'Alt+1' - } + }] }; }); \ No newline at end of file diff --git a/src/module/note.js b/src/module/note.js new file mode 100644 index 00000000..5bd3b460 --- /dev/null +++ b/src/module/note.js @@ -0,0 +1,96 @@ +/** + * @fileOverview + * + * 支持节点详细信息(HTML)格式 + * + * @author: techird + * @copyright: Baidu FEX, 2014 + */ +KityMinder.registerModule('NoteModule', function() { + + var NOTE_PATH = 'M9,9H3V8h6L9,9L9,9z M9,7H3V6h6V7z M9,5H3V4h6V5z M8.5,11H2V2h8v7.5 M9,12l2-2V1H1v11'; + + 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: KityMinder.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 + } + }; +}); \ No newline at end of file diff --git a/src/protocol/png.js b/src/protocol/png.js index b03077b7..71f32a1e 100644 --- a/src/protocol/png.js +++ b/src/protocol/png.js @@ -111,8 +111,12 @@ if (!kity.Browser.ie) { } function generateDataUrl(canvas) { - var url = canvas.toDataURL('png'); - return url; + try { + var url = canvas.toDataURL('png'); + return url; + } catch (e) { + throw new Error('当前浏览器版本不支持导出 PNG 功能,请尝试升级到最新版本!'); + } } function drawSVG() { diff --git a/ui/contextmenu.js b/ui/contextmenu.js index 240ff78a..775f7671 100644 --- a/ui/contextmenu.js +++ b/ui/contextmenu.js @@ -29,8 +29,11 @@ KityMinder.registerUI('contextmenu', function(minder) { $menu.delegate('li', 'mousedown', function(e, info) { var item = $(e.target).closest('li').data('menu'); + if (item.fn) { + return item.fn.call(minder, minder); + } if (item.command) { - minder.execCommand(item.command); + return minder.execCommand(item.command); } }); @@ -63,6 +66,7 @@ KityMinder.registerUI('contextmenu', function(minder) { var lastDivider = true; ctxmenu.forEach(function(item) { + if (item.query && !item.query()) return; if (item.command && minder.queryCommandState(item.command) === 0) { var label = minder.getLang('ui.command.' + item.command); diff --git a/ui/ribbon/idea/image.js b/ui/ribbon/idea/image.js index 9a37e7e2..1c857da2 100644 --- a/ui/ribbon/idea/image.js +++ b/ui/ribbon/idea/image.js @@ -7,7 +7,7 @@ * @copyright: Baidu FEX, 2014 */ -KityMinder.registerUI('image', function(minder) { +KityMinder.registerUI('ribbon/idea/image', function(minder) { var $attachment = minder.getUI('ribbon/idea/attachment'); diff --git a/ui/ribbon/idea/note.js b/ui/ribbon/idea/note.js new file mode 100644 index 00000000..2dfcfbca --- /dev/null +++ b/ui/ribbon/idea/note.js @@ -0,0 +1,193 @@ +/** + * @fileOverview + * + * 节点笔记支持 + * + * @author: techird + * @copyright: Baidu FEX, 2014 + */ +/* global marked: true */ +KityMinder.registerUI('ribbon/idea/note', function(minder) { + + marked.setOptions({ + breaks: true + }); + + var $attachment = minder.getUI('ribbon/idea/attachment'); + + var $noteButtonMenu = new FUI.ButtonMenu({ + id: 'note-button-menu', + text: minder.getLang('ui.note'), + layout: 'bottom', + buttons: [{}, { + label: minder.getLang('ui.note') + }], + menu: { + items: [minder.getLang('ui.removenote')] + } + }).appendTo($attachment); + + $noteButtonMenu.on('select', function() { + minder.execCommand('note', null); + }); + + var $notePanel = $('
'); + var $title = $('

节点备注

').appendTo($notePanel); + var $close = $('').appendTo($notePanel).click(hide); + + var $tab = $('
' + + '编辑' + + '预览' + + '支持 GFM 语法书写' + + '
').appendTo($notePanel); + + var $editTab = $tab.find('.edit-tab'); + var $previewTab = $tab.find('.preview-tab'); + + var $editor = $('
').appendTo($notePanel); + var $preview = $('
').appendTo($notePanel); + + var $previewer = $('
').appendTo('#content-wrapper'); + + var noteVisible = false; + + $editor.on('keydown keyup keypress mouedown mouseup click contextmenu', function(e) { + e.stopPropagation(); + }); + + var editor = new window.CodeMirror($editor[0], { + mode: 'gfm', + lineWrapping: true, + dragDrop: false + }); + + minder.on('uiready', function() { + editor.setSize('100%', '100%'); + }); + + var visible = false; + var selectedNode = null; + + function axss(value) { + var div = document.createElement('div'); + div.innerHTML = value; + $(div).find('script').remove(); + for (var name in div) { + if (name.indexOf('on') === 0) { + div.removeAttribute(name); + } + } + return div.innerHTML; + } + + function updateEditorView() { + if (noteVisible && selectedNode != minder.getSelectedNode()) { + selectedNode = minder.getSelectedNode(); + var note = minder.queryCommandValue('note') || ''; + editor.setValue(note); + + if (selectedNode) { + $notePanel.removeAttr('disabled'); + $title.text('备注 - ' + selectedNode.getText()); + } else { + $notePanel.attr('disabled', 'disabled'); + $title.text('选择节点添加备注'); + } + + if ($previewTab.hasClass(activeTabClass)) { + $preview.html(marked(note)); + } + } + } + + function updateNodeData() { + if (selectedNode && minder.queryCommandState('note') != -1) { + minder.execCommand('note', editor.getValue()); + } + } + + minder.on('interactchange', updateEditorView); + editor.on('change', updateNodeData); + $noteButtonMenu.bindCommandState(minder, 'note'); + $noteButtonMenu.on('buttonclick', show); + minder.on('editnoterequest', show); + minder.on('shownoterequest', preview); + + $('#kityminder').after($notePanel); + + hide(); + + var previewLive = false; + $('#kityminder').on('mousedown mousewheel DOMMouseScroll', function() { + if (!previewLive) return; + $previewer.fadeOut(); + previewLive = false; + }); + + $previewer.hide(); + function preview(e) { + var icon = e.icon; + var node = e.node; + var b = icon.getRenderBox('screen'); + var note = node.getData('note'); + + $previewer.html(marked(axss(note))); + + var cw = $('#content-wrapper').width(); + var ch = $('#content-wrapper').height(); + var pw = $previewer.outerWidth(); + var ph = $previewer.outerHeight(); + + var x = b.cx - pw / 2; + var y = b.bottom + 10; + + if (x < 0) x = 10; + if (x + pw > cw) x = cw - pw - 10; + if (y + ph > ch) y = b.top - ph - 10; + + $previewer.css({ + left: Math.round(x), + top: Math.round(y) + }); + $previewer.show(); + previewLive = true; + } + + var activeTabClass = 'active-tab'; + + function editMode() { + if ($editTab.hasClass(activeTabClass)) return; + $preview.hide(); + $previewTab.removeClass(activeTabClass); + + $editor.show().addClass(activeTabClass); + $editTab.addClass(activeTabClass); + } + + function previewMode() { + if ($previewTab.hasClass(activeTabClass)) return; + + $editor.hide(); + $editTab.removeClass(activeTabClass); + $preview.html(marked(axss(editor.getValue()))).show(); + $previewTab.addClass(activeTabClass); + } + + $editTab.click(editMode); + $previewTab.click(previewMode); + + function show() { + noteVisible = true; + $notePanel.show(); + editMode(); + updateEditorView(); + $('#content-wrapper').addClass('note-panel-visible'); + } + + function hide() { + noteVisible = false; + $notePanel.hide(); + $('#content-wrapper').removeClass('note-panel-visible'); + } + +}); \ No newline at end of file diff --git a/ui/ribbon/view/level.js b/ui/ribbon/view/level.js index 40d603dd..33e01ddf 100644 --- a/ui/ribbon/view/level.js +++ b/ui/ribbon/view/level.js @@ -16,7 +16,28 @@ KityMinder.registerUI('ribbon/view/level', function(minder) { column: true }).appendTo($tabs.view); - ['expandtoleaf', 'collapsetolevel1'].forEach(function(cmd) { - $commandbutton.generate(cmd).appendTo($levelPanel); + var $levelButtonMenu = new FUI.ButtonMenu({ + id: 'level-button-menu', + text: minder.getLang('ui.level'), + layout: 'bottom', + buttons: [{}, { + label: minder.getLang('ui.expandtoleaf') + }], + menu: { + items: [1, 2, 3, 4, 5, 6].map(function(level) { + return { + label: minder.getLang('ui.command.expandtolevel' + level), + value: level + }; + }) + } + }).appendTo($levelPanel); + + $levelButtonMenu.on('buttonclick', function() { + minder.execCommand('expandtolevel', 9999); + }); + + $levelButtonMenu.on('select', function(e, info) { + minder.execCommand('expandtolevel', info.value); }); }); diff --git a/ui/ribbon/view/select.js b/ui/ribbon/view/select.js new file mode 100644 index 00000000..bd65cc9c --- /dev/null +++ b/ui/ribbon/view/select.js @@ -0,0 +1,104 @@ +/** + * @fileOverview + * + * 节点选择功能 + * + * @author: techird + * @copyright: Baidu FEX, 2014 + */ + +KityMinder.registerUI('ribbon/view/select', function(minder) { + + var $tabs = minder.getUI('ribbon/tabs'); + + var $selectPanel = new FUI.LabelPanel({ + label: minder.getLang('panels.level'), + column: true + }).appendTo($tabs.view); + + var $selectButtonMenu = new FUI.ButtonMenu({ + id: 'select-button-menu', + text: minder.getLang('ui.select'), + layout: 'bottom', + buttons: [{}, { + label: minder.getLang('ui.selectall') + }], + menu: { + items: ['revert', 'siblings', 'level', 'path', 'tree'].map(function(mode) { + return { + label: minder.getLang('ui.select' + mode), + value: mode + }; + }) + } + }).appendTo($selectPanel); + + var select = { + all: function() { + var selection = []; + minder.getRoot().traverse(function(node) { + selection.push(node); + }); + minder.select(selection, true); + }, + revert: function() { + var selected = minder.getSelectedNodes(); + var selection = []; + minder.getRoot().traverse(function(node) { + if (selected.indexOf(node) == -1) { + selection.push(node); + } + }); + minder.select(selection, true); + }, + siblings: function() { + var selected = minder.getSelectedNodes(); + var selection = []; + selected.forEach(function(node) { + if (!node.parent) return; + node.parent.children.forEach(function(sibling) { + if (selection.indexOf(sibling) == -1) selection.push(sibling); + }); + }); + minder.select(selection, true); + }, + level: function() { + var selectedLevel = minder.getSelectedNodes().map(function(node) { + return node.getLevel(); + }); + var selection = []; + minder.getRoot().traverse(function(node) { + if (selectedLevel.indexOf(node.getLevel()) != -1) { + selection.push(node); + } + }); + minder.select(selection, true); + }, + path: function() { + var selected = minder.getSelectedNodes(); + var selection = []; + selected.forEach(function(node) { + while(node && selection.indexOf(node) == -1) { + selection.push(node); + node = node.parent; + } + }); + minder.select(selection, true); + }, + tree: function() { + var selected = minder.getSelectedNodes(); + var selection = []; + selected.forEach(function(parent) { + parent.traverse(function(node) { + if (selection.indexOf(node) == -1) selection.push(node); + }); + }); + minder.select(selection, true); + } + }; + + $selectButtonMenu.on('buttonclick', select.all); + $selectButtonMenu.on('select', function(e, info) { + select[info.value](); + }); +}); \ No newline at end of file diff --git a/ui/theme/default/css/_icons.less b/ui/theme/default/css/_icons.less index c148eee5..788af1b4 100644 --- a/ui/theme/default/css/_icons.less +++ b/ui/theme/default/css/_icons.less @@ -24,4 +24,21 @@ .expandtoleaf& { background-position: 0 -995px; } .collapsetolevel1& { background-position: 0 -1015px;} .fullscreen& { background-position: 0 -1035px;} +} + +#select-button-menu .fui-button:first-child { + background-image: url(../images/icons.png); + background-position: 7px -1175px; + background-repeat: no-repeat; +} + +#level-button-menu .fui-button:first-child .fui-icon { + width: 20px; + height: 20px; + display: block; + background-image: url(../images/icons.png); + background-position: 0 -995px; + background-repeat: no-repeat; + margin-left: 10px; + margin-top: 2px; } \ No newline at end of file diff --git a/ui/theme/default/css/_kityminder.less b/ui/theme/default/css/_kityminder.less index 5d3678b7..094a110f 100644 --- a/ui/theme/default/css/_kityminder.less +++ b/ui/theme/default/css/_kityminder.less @@ -5,7 +5,6 @@ html, body, div { margin: 0; padding: 0; - overflow: hidden; } body, svg { diff --git a/ui/theme/default/css/_note.less b/ui/theme/default/css/_note.less new file mode 100644 index 00000000..8ce2695a --- /dev/null +++ b/ui/theme/default/css/_note.less @@ -0,0 +1,170 @@ +.gfm-render { + blockquote, ul, table, p, pre, hr { + margin: 1em 0; + &:first-child:last-child { + margin: 0; + } + } + + blockquote { + display: block; + border-left: 4px solid #E4AD91; + color: darken(#E4AD91, 10%); + padding-left: 10px; + font-style: italic; + margin-left: 2em; + } + + ul, ol { + padding-left: 3em; + } + + table { + width: 100%; + border-collapse: collapse; + th, td { + border: 1px solid #666; + padding: 2px 4px; + } + th { + background: rgba(45, 141, 234, 0.2); + } + tr:nth-child(even) td { + background: rgba(45, 141, 234, 0.03); + } + margin: 1em 0; + } + + em { + color: red; + } + + del { + color: #999; + } + + pre { + background: rgba(45, 141, 234, 0.1); + padding: 5px; + border-radius: 5px; + word-break: break-all; + word-wrap: break-word; + } + + hr { + border: none; + border-top: 1px solid #CCC; + } +} + +#note-panel { + width: 300px; + border-left: 1px solid lighten(@ui-color, 50%); + background: white; + .dock(100px, 0, 0, auto); + + .tab { + height: 30px; + background: #EEE; + font-size: 12px; + line-height: 30px; + padding: 0 10px; + position: relative; + a.edit-tab, a.preview-tab { + display: inline-block; + padding: 0 10px; + height: 24px; + line-height: 24px; + vertical-align: bottom; + margin-right: 5px; + border-radius: 3px 3px 0 0; + &:hover { + background: rgba(255, 255, 255, 0.5); + } + &.active-tab { + background: white; + } + } + a.help { + position: absolute; + right: 10px; + height: 30px; + line-height: 30px; + top: 0; + } + } + + .note-editor { + .dock(60px, 0, 0); + padding: 5px; + -webkit-user-select: text; + + .CodeMirror { + cursor: text; + font-size: 14px; + line-height: 1.3em; + font-family: consolas; + } + } + + .note-preview { + .dock(60px, 0, 0); + padding: 10px; + -webkit-user-select: text; + overflow-x: hidden; + overflow-y: auto; + .gfm-render; + } + + .close { + .close-button; + position: absolute; + right: 5px; + top: 6px; + } + + &[disabled] { + &:after { + content: ' '; + display: block; + .dock(20px, 0, 0); + background: rgba(100, 100, 100, .1); + } + } + + & > h2 { + font-size: 12px; + margin: 0; + font-weight: normal; + background: lighten(@ui-color, 30%); + color: @ui-fore; + padding: 5px 10px; + height: 20px; + line-height: 20px; + } +} + +#note-previewer { + position: absolute; + background: #FFD; + padding: 5px 15px; + border-radius: 5px; + max-width: 400px; + max-height: 200px; + overflow: auto; + font-size: 12px; + color: #333; + line-height: 1.5em; + z-index: 10; + box-shadow: 0 0 15px rgba(0, 0, 0, .5); + .gfm-render; +} + + +#content-wrapper.note-panel-visible #kityminder { + right: 300px; +} + +#tab-container.collapsed + #kityminder + #note-panel { + top: 40px; +} \ No newline at end of file diff --git a/ui/theme/default/css/_resource_panel.less b/ui/theme/default/css/_resource_panel.less index d76ccd39..6cae1e9d 100644 --- a/ui/theme/default/css/_resource_panel.less +++ b/ui/theme/default/css/_resource_panel.less @@ -36,6 +36,12 @@ background-repeat: no-repeat; } +#note-button-menu .fui-button:first-child { + background-image: url(../images/icons.png); + background-position: center -1150px; + background-repeat: no-repeat; +} + ul.resource-list { margin: 0; padding: 0; diff --git a/ui/theme/default/css/_tab.less b/ui/theme/default/css/_tab.less index e0ad7d70..7f893bbd 100644 --- a/ui/theme/default/css/_tab.less +++ b/ui/theme/default/css/_tab.less @@ -111,9 +111,6 @@ } } } - - transition: transform 0.7s ease, - opacity,height .5s ease; &.collapsed { opacity: 0; diff --git a/ui/theme/default/css/_widgets.less b/ui/theme/default/css/_widgets.less index b4147e81..8464cb35 100644 --- a/ui/theme/default/css/_widgets.less +++ b/ui/theme/default/css/_widgets.less @@ -14,6 +14,7 @@ textarea { user-select: text; } + overflow: hidden; } input::-ms-clear { @@ -208,7 +209,33 @@ input[type=search]::-ms-clear { } } } - +.close-button { + display: block; + width: 20px; + height: 20px; + cursor: pointer; + border-radius: 100%; + &:hover { + background-color: hsl(0, 73%, 64%); + } + &:active { + background-color: hsl(0, 73%, 60%); + } + &:after { + content: 'x'; + display: block; + width: 16px; + height: 16px; + font-family: inherit; + color: white; + font-size: 14px; + position: absolute; + left: 2px; + top: 1px; + line-height: 16px; + text-align: center; + } +} .fui-dialog { & > .fui-panel-content { padding: 0; @@ -233,31 +260,8 @@ input[type=search]::-ms-clear { right: 6px; top: 7px; left: auto; - display: block; - width: 20px; - height: 20px; - cursor: pointer; - border-radius: 100%; - &:hover { - background-color: hsl(0, 73%, 64%); - } - &:active { - background-color: hsl(0, 73%, 60%); - } - &:after { - content: 'x'; - display: block; - width: 16px; - height: 16px; - font-family: inherit; - color: white; - font-size: 14px; - position: absolute; - left: 2px; - top: 1px; - line-height: 16px; - text-align: center; - } + + } } .fui-dialog-body { @@ -406,6 +410,20 @@ input[type=search]::-ms-clear { overflow: hidden; transition: none; + + &.fui-item-selected { + background-color: white; + position: relative; + +/* .fui-icon { + width: 20px; + height: 20px; + position: absolute; + left: 3px; + top: 5px; + background: url(../images/icons.png) 0 -1200px; + } */ + } &:hover, :focus { background: #0099f2; /* Old browsers */ diff --git a/ui/theme/default/css/import.less b/ui/theme/default/css/import.less index e48c05f4..6a1b09ee 100644 --- a/ui/theme/default/css/import.less +++ b/ui/theme/default/css/import.less @@ -1,4 +1,5 @@ @import (less) "../../../../lib/fui/theme/default/fui.all.css"; +@import (less) "../../../../lib/bower/codemirror/lib/codemirror.css"; @import "_vars"; @import "_public"; @@ -20,6 +21,7 @@ @import "_draft"; @import "_download"; @import "_help"; +@import "_note"; @import "_priority_panel"; @import "_progress_panel"; @@ -37,3 +39,4 @@ @import "_share"; @import "_icons"; @import "_nav"; + diff --git a/ui/theme/default/images/icons.png b/ui/theme/default/images/icons.png index 5dd8eea8..26dd8a4a 100755 Binary files a/ui/theme/default/images/icons.png and b/ui/theme/default/images/icons.png differ diff --git a/ui/topbar/search.js b/ui/topbar/search.js index 398ad70c..3e428b43 100644 --- a/ui/topbar/search.js +++ b/ui/topbar/search.js @@ -66,6 +66,7 @@ KityMinder.registerUI('topbar/search', function(minder) { minder.execCommand('camera', node, 50); setTimeout(function() { minder.select(node, true); + if (!node.isExpanded()) minder.execCommand('expand', true); }, 60); } }