├── hotbox ├── .jscsrc ├── .gitignore ├── snap.png ├── src │ ├── expose.js │ ├── keymap.js │ ├── key.js │ └── keycontrol.js ├── .jshintrc ├── bower.json ├── test │ ├── spec │ │ └── .jshintrc │ └── SpecRunner.html ├── package.json ├── hotbox.css.map ├── README.md └── demo.html ├── kityminder-core ├── .gitmodules ├── icon.ico ├── .gitignore ├── CHANGELOG.md ├── src │ ├── expose-kityminder.js │ ├── core │ │ ├── kity.js │ │ ├── _boxv.js │ │ ├── option.js │ │ ├── minder.js │ │ ├── animate.js │ │ ├── focus.js │ │ ├── status.js │ │ ├── utils.js │ │ ├── keyreceiver.js │ │ ├── paper.js │ │ ├── compatibility.js │ │ ├── keymap.js │ │ ├── template.js │ │ └── readonly.js │ ├── template │ │ ├── structure.js │ │ ├── right.js │ │ ├── filetree.js │ │ ├── tianpan.js │ │ ├── default.js │ │ └── fish-bone.js │ ├── protocol │ │ └── json.js │ ├── connect │ │ ├── fish-bone-master.js │ │ ├── l.js │ │ ├── bezier.js │ │ ├── under.js │ │ ├── arc.js │ │ ├── poly.js │ │ └── arc_tp.js │ ├── theme │ │ ├── wire.js │ │ ├── fresh.js │ │ ├── fish.js │ │ ├── snow.js │ │ └── default.js │ ├── layout │ │ ├── mind.js │ │ ├── fish-bone-master.js │ │ ├── fish-bone-slave.js │ │ ├── tianpan.js │ │ └── filetree.js │ └── module │ │ └── layout.js ├── .jshintrc ├── .jsbeautifyrc ├── LICENSE ├── package.json ├── README.md ├── example.html ├── .jscsrc ├── dev.html └── import.js ├── babel.config.js ├── packages └── VueTestcaseMinderEditor │ ├── src │ ├── script │ │ ├── lang.js │ │ ├── hotbox.js │ │ ├── minder.js │ │ ├── expose-editor.js │ │ ├── tool │ │ │ ├── format.js │ │ │ ├── innertext.js │ │ │ ├── debug.js │ │ │ ├── keymap.js │ │ │ └── key.js │ │ ├── protocol │ │ │ ├── json.js │ │ │ ├── svg.js │ │ │ ├── xmind.js │ │ │ ├── freemind.js │ │ │ └── plain.js │ │ ├── runtime │ │ │ ├── container.js │ │ │ ├── minder.js │ │ │ ├── result.js │ │ │ ├── progress.js │ │ │ ├── hotbox.js │ │ │ ├── priority.js │ │ │ ├── exports.js │ │ │ ├── clipboard-mimetype.js │ │ │ ├── node.js │ │ │ ├── receiver.js │ │ │ └── fsm.js │ │ └── editor.js │ ├── assets │ │ └── logo.png │ ├── static │ │ ├── caseMind │ │ │ ├── ban.png │ │ │ ├── bug.png │ │ │ ├── icons.png │ │ │ ├── jump.png │ │ │ ├── mold.png │ │ │ ├── noPass.png │ │ │ ├── note.png │ │ │ ├── pass.png │ │ │ ├── plan.png │ │ │ ├── calendar.png │ │ │ ├── location.png │ │ │ ├── icon-undo.png │ │ │ ├── iconpriority.png │ │ │ └── iconprogress.png │ │ └── minder │ │ │ ├── icons.png │ │ │ ├── logo.png │ │ │ ├── mold.png │ │ │ ├── iconpriority.png │ │ │ └── iconprogress.png │ ├── components │ │ ├── footer.vue │ │ ├── menu │ │ │ ├── view │ │ │ │ ├── viewMenu.vue │ │ │ │ ├── arrange.vue │ │ │ │ ├── styleOperation.vue │ │ │ │ └── mold.vue │ │ │ └── edit │ │ │ │ ├── moveBox.vue │ │ │ │ ├── search │ │ │ │ ├── search.vue │ │ │ │ └── searchLabelBox.vue │ │ │ │ ├── editDel.vue │ │ │ │ ├── expand.vue │ │ │ │ ├── progressBox.vue │ │ │ │ ├── sequenceBox.vue │ │ │ │ ├── insertBox.vue │ │ │ │ ├── editMenu.vue │ │ │ │ ├── backAndRight.vue │ │ │ │ └── noteBox.vue │ │ ├── main │ │ │ └── mainEditor.vue │ │ └── header.vue │ ├── store │ │ ├── index.js │ │ ├── state.js │ │ ├── getters.js │ │ ├── mutations.js │ │ └── actions.js │ └── style │ │ ├── mixin.scss │ │ ├── dropdown-list.scss │ │ ├── navigator.scss │ │ └── editor.scss │ ├── index.js │ ├── main.js │ └── App.vue ├── docs └── preview.png ├── public ├── favicon.ico └── index.html ├── .browserlistrc ├── .editorconfig ├── .gitignore ├── examples ├── main.js └── App.vue ├── vue.config.js └── package.json /hotbox/.jscsrc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /kityminder-core/.gitmodules: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /hotbox/.gitignore: -------------------------------------------------------------------------------- 1 | bower_components/ 2 | dist/ 3 | node_modules/ 4 | .idea/ 5 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /hotbox/snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenhengjie123/vue-testcase-minder-editor/HEAD/hotbox/snap.png -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/script/lang.js: -------------------------------------------------------------------------------- 1 | define(function(require, exports, module) { 2 | 3 | }); -------------------------------------------------------------------------------- /docs/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenhengjie123/vue-testcase-minder-editor/HEAD/docs/preview.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenhengjie123/vue-testcase-minder-editor/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /kityminder-core/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenhengjie123/vue-testcase-minder-editor/HEAD/kityminder-core/icon.ico -------------------------------------------------------------------------------- /.browserlistrc: -------------------------------------------------------------------------------- 1 | { 2 | "browserslist": [ 3 | "> 1%", 4 | "last 2 versions", 5 | "not dead", 6 | "not ie <= 8" 7 | ] 8 | } -------------------------------------------------------------------------------- /hotbox/src/expose.js: -------------------------------------------------------------------------------- 1 | define('expose', function(require, exports, module) { 2 | module.exports = window.HotBox = require('./hotbox'); 3 | }); -------------------------------------------------------------------------------- /kityminder-core/.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .DS_Store 3 | *.sublime-project 4 | *.sublime-workspace 5 | node_modules/ 6 | _drafts/ 7 | bower_components -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/script/hotbox.js: -------------------------------------------------------------------------------- 1 | define(function(require, exports, module) { 2 | return module.exports = window.HotBox; 3 | }); -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{js,jsx,ts,tsx,vue}] 2 | indent_style = space 3 | indent_size = 2 4 | trim_trailing_whitespace = true 5 | insert_final_newline = true 6 | -------------------------------------------------------------------------------- /kityminder-core/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | # KityMinder-Core 更新日志 3 | 4 | ## v1.4.0 5 | 6 | 完成从[原仓库](https://github.com/fex-team/kityminder)的代码迁移。处于兼容性考虑,版本号直接从 1.4.0 开始。 7 | -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/script/minder.js: -------------------------------------------------------------------------------- 1 | define(function(require, exports, module) { 2 | return module.exports = window.kityminder.Minder; 3 | }); 4 | -------------------------------------------------------------------------------- /kityminder-core/src/expose-kityminder.js: -------------------------------------------------------------------------------- 1 | define('expose-kityminder', function(require, exports, module) { 2 | module.exports = window.kityminder = require('./kityminder'); 3 | }); -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenhengjie123/vue-testcase-minder-editor/HEAD/packages/VueTestcaseMinderEditor/src/assets/logo.png -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/static/caseMind/ban.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenhengjie123/vue-testcase-minder-editor/HEAD/packages/VueTestcaseMinderEditor/src/static/caseMind/ban.png -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/static/caseMind/bug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenhengjie123/vue-testcase-minder-editor/HEAD/packages/VueTestcaseMinderEditor/src/static/caseMind/bug.png -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/static/minder/icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenhengjie123/vue-testcase-minder-editor/HEAD/packages/VueTestcaseMinderEditor/src/static/minder/icons.png -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/static/minder/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenhengjie123/vue-testcase-minder-editor/HEAD/packages/VueTestcaseMinderEditor/src/static/minder/logo.png -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/static/minder/mold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenhengjie123/vue-testcase-minder-editor/HEAD/packages/VueTestcaseMinderEditor/src/static/minder/mold.png -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/static/caseMind/icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenhengjie123/vue-testcase-minder-editor/HEAD/packages/VueTestcaseMinderEditor/src/static/caseMind/icons.png -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/static/caseMind/jump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenhengjie123/vue-testcase-minder-editor/HEAD/packages/VueTestcaseMinderEditor/src/static/caseMind/jump.png -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/static/caseMind/mold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenhengjie123/vue-testcase-minder-editor/HEAD/packages/VueTestcaseMinderEditor/src/static/caseMind/mold.png -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/static/caseMind/noPass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenhengjie123/vue-testcase-minder-editor/HEAD/packages/VueTestcaseMinderEditor/src/static/caseMind/noPass.png -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/static/caseMind/note.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenhengjie123/vue-testcase-minder-editor/HEAD/packages/VueTestcaseMinderEditor/src/static/caseMind/note.png -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/static/caseMind/pass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenhengjie123/vue-testcase-minder-editor/HEAD/packages/VueTestcaseMinderEditor/src/static/caseMind/pass.png -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/static/caseMind/plan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenhengjie123/vue-testcase-minder-editor/HEAD/packages/VueTestcaseMinderEditor/src/static/caseMind/plan.png -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/components/footer.vue: -------------------------------------------------------------------------------- 1 | 4 | 9 | -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/script/expose-editor.js: -------------------------------------------------------------------------------- 1 | define('expose-editor', function(require, exports, module) { 2 | return module.exports = kityminder.Editor = require('./editor'); 3 | }); 4 | -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/static/caseMind/calendar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenhengjie123/vue-testcase-minder-editor/HEAD/packages/VueTestcaseMinderEditor/src/static/caseMind/calendar.png -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/static/caseMind/location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenhengjie123/vue-testcase-minder-editor/HEAD/packages/VueTestcaseMinderEditor/src/static/caseMind/location.png -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/static/caseMind/icon-undo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenhengjie123/vue-testcase-minder-editor/HEAD/packages/VueTestcaseMinderEditor/src/static/caseMind/icon-undo.png -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/static/minder/iconpriority.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenhengjie123/vue-testcase-minder-editor/HEAD/packages/VueTestcaseMinderEditor/src/static/minder/iconpriority.png -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/static/minder/iconprogress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenhengjie123/vue-testcase-minder-editor/HEAD/packages/VueTestcaseMinderEditor/src/static/minder/iconprogress.png -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/static/caseMind/iconpriority.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenhengjie123/vue-testcase-minder-editor/HEAD/packages/VueTestcaseMinderEditor/src/static/caseMind/iconpriority.png -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/static/caseMind/iconprogress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenhengjie123/vue-testcase-minder-editor/HEAD/packages/VueTestcaseMinderEditor/src/static/caseMind/iconprogress.png -------------------------------------------------------------------------------- /kityminder-core/src/core/kity.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview 3 | * 4 | * Kity 引入 5 | * 6 | * @author: techird 7 | * @copyright: Baidu FEX, 2014 8 | */ 9 | define(function(require, exports, module) { 10 | module.exports = window.kity; 11 | }); -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/index.js: -------------------------------------------------------------------------------- 1 | import VueTestcaseMinderEditor from './src/VueTestcaseMinderEditor.vue' 2 | 3 | VueTestcaseMinderEditor.install = function (Vue) { 4 | Vue.component(VueTestcaseMinderEditor.name, VueTestcaseMinderEditor) 5 | } 6 | 7 | export default VueTestcaseMinderEditor 8 | -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/script/tool/format.js: -------------------------------------------------------------------------------- 1 | function format(template, args) { 2 | if (typeof(args) != 'object') { 3 | args = [].slice.call(arguments, 1); 4 | } 5 | return String(template).replace(/\{(\w+)\}/ig, function (match, $key) { 6 | return args[$key] || $key; 7 | }); 8 | } 9 | export {format} 10 | -------------------------------------------------------------------------------- /kityminder-core/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "undef" : true, 3 | "unused" : false, 4 | "strict" : false, 5 | "curly" : false, 6 | "newcap" : true, 7 | "trailing" : true, 8 | "white": false, 9 | "quotmark": false, 10 | "browser": true, 11 | "boss": true, 12 | "predef" : [ 13 | "define" 14 | ] 15 | } -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/store/index.js: -------------------------------------------------------------------------------- 1 | 2 | import * as getters from './getters' 3 | import { 4 | actions 5 | } from './actions' 6 | import { 7 | mutations 8 | } from './mutations' 9 | import { 10 | state 11 | } from './state' 12 | 13 | export default { 14 | namespaced: true, 15 | state, 16 | getters, 17 | actions, 18 | mutations 19 | } 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | /lib 5 | 6 | # npm pack file 7 | lizhife-vue-testcase-minder-editor-*.tgz 8 | 9 | 10 | # local env files 11 | .env.local 12 | .env.*.local 13 | 14 | # Log files 15 | npm-debug.log* 16 | yarn-debug.log* 17 | yarn-error.log* 18 | pnpm-debug.log* 19 | 20 | # Editor directories and files 21 | .idea 22 | .vscode 23 | *.suo 24 | *.ntvs* 25 | *.njsproj 26 | *.sln 27 | *.sw? 28 | -------------------------------------------------------------------------------- /hotbox/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "undef" : true, 3 | "unused" : false, 4 | "strict" : false, 5 | "curly" : false, 6 | "newcap" : true, 7 | "trailing" : true, 8 | "white": false, 9 | "quotmark": false, 10 | "browser": true, 11 | "boss": true, 12 | "loopfunc": true, 13 | "predef" : [ 14 | "module", 15 | "require", 16 | "console", 17 | "define" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /hotbox/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hotbox", 3 | "version": "1.0.15", 4 | "main": [ 5 | "hotbox.js", 6 | "hotbox.css" 7 | ], 8 | "keywords": [ 9 | "hotbox", 10 | "html5", 11 | "javascript", 12 | "ui" 13 | ], 14 | "ignore": [ 15 | ".gitignore", 16 | ".jscsrc", 17 | ".jshintrc", 18 | "Gruntfile.js", 19 | "package.json" 20 | ], 21 | "dependencies": { 22 | "seajs": "~2.3.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /hotbox/test/spec/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "undef" : true, 3 | "unused" : false, 4 | "strict" : false, 5 | "curly" : false, 6 | "newcap" : true, 7 | "trailing" : true, 8 | "white": false, 9 | "quotmark": false, 10 | "browser": true, 11 | "boss": true, 12 | "loopfunc": true, 13 | "predef" : [ 14 | "module", 15 | "require", 16 | "define", 17 | "describe", 18 | "it", 19 | "expect" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/style/mixin.scss: -------------------------------------------------------------------------------- 1 | $btn-hover-color: #eee; 2 | .vue-testcase-minder-editor-container { 3 | user-select: none; 4 | height: 360px; 5 | } 6 | 7 | *[disabled] { 8 | opacity: 0.5; 9 | } 10 | 11 | @mixin block { 12 | width: 100%; 13 | height: 100%; 14 | } 15 | 16 | @mixin button { 17 | background: transparent; 18 | border: none; 19 | outline: none; 20 | } 21 | 22 | @mixin flexcenter { 23 | display: flex; 24 | justify-content: center; 25 | align-items: center; 26 | } 27 | -------------------------------------------------------------------------------- /kityminder-core/src/template/structure.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview 3 | * 4 | * 组织结构图模板 5 | * 6 | * @author: techird 7 | * @copyright: Baidu FEX, 2014 8 | */ 9 | define(function(require, exports, module) { 10 | var template = require('../core/template'); 11 | 12 | template.register('structure', { 13 | 14 | getLayout: function(node) { 15 | return node.getData('layout') || 'bottom'; 16 | }, 17 | 18 | getConnect: function(node) { 19 | return 'poly'; 20 | } 21 | }); 22 | }); -------------------------------------------------------------------------------- /kityminder-core/src/protocol/json.js: -------------------------------------------------------------------------------- 1 | define(function(require, exports, module) { 2 | var data = require('../core/data'); 3 | 4 | data.registerProtocol('json', module.exports = { 5 | fileDescription: 'KityMinder 格式', 6 | fileExtension: '.km', 7 | dataType: 'text', 8 | mineType: 'application/json', 9 | 10 | encode: function(json) { 11 | return JSON.stringify(json); 12 | }, 13 | 14 | decode: function(local) { 15 | return JSON.parse(local); 16 | } 17 | }); 18 | }); -------------------------------------------------------------------------------- /kityminder-core/.jsbeautifyrc: -------------------------------------------------------------------------------- 1 | { 2 | "indent_size": 4, 3 | "indent_char": " ", 4 | "indent_level": 0, 5 | "indent_with_tabs": false, 6 | "preserve_newlines": true, 7 | "max_preserve_newlines": 10, 8 | "jslint_happy": false, 9 | "space_in_paren": false, 10 | "brace_style": "collapse", 11 | "keep_array_indentation": false, 12 | "keep_function_indentation": false, 13 | "space_before_conditional": false, 14 | "break_chained_methods": false, 15 | "eval_code": false, 16 | "unescape_strings": false, 17 | "wrap_line_length": 0 18 | } -------------------------------------------------------------------------------- /kityminder-core/src/template/right.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview 3 | * 4 | * 往右布局结构模板 5 | * 6 | * @author: techird 7 | * @copyright: Baidu FEX, 2014 8 | */ 9 | define(function(require, exports, module) { 10 | var template = require('../core/template'); 11 | 12 | template.register('right', { 13 | 14 | getLayout: function(node) { 15 | return node.getData('layout') || 'right'; 16 | }, 17 | 18 | getConnect: function(node) { 19 | if (node.getLevel() == 1) return 'arc'; 20 | return 'bezier'; 21 | } 22 | }); 23 | }); -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/script/protocol/json.js: -------------------------------------------------------------------------------- 1 | function exportJson(minder) { 2 | var minds = minder.exportJson(); 3 | try { 4 | const link = document.createElement('a'); 5 | const blob = new Blob(["\ufeff" + JSON.stringify(minds)], { 6 | type: 'text/json' 7 | }); 8 | link.href = window.URL.createObjectURL(blob); 9 | link.download = `${minds.root.data.text}.json`; 10 | document.body.appendChild(link); 11 | link.click(); 12 | document.body.removeChild(link); 13 | } catch (err) { 14 | alert(err); 15 | } 16 | } 17 | 18 | export { 19 | exportJson 20 | } 21 | -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/script/runtime/container.js: -------------------------------------------------------------------------------- 1 | define(function (require, exports, module) { 2 | function ContainerRuntime() { 3 | var container; 4 | 5 | if (typeof (this.selector) == 'string') { 6 | container = document.querySelector(this.selector); 7 | } else { 8 | container = this.selector; 9 | } 10 | 11 | if (!container) throw new Error('Invalid selector: ' + this.selector); 12 | 13 | // 这个类名用于给编辑器添加样式 14 | container.classList.add('km-editor'); 15 | 16 | // 暴露容器给其他运行时使用 17 | this.container = container; 18 | } 19 | 20 | return module.exports = ContainerRuntime; 21 | }); 22 | -------------------------------------------------------------------------------- /examples/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | import App from './App' 5 | 6 | import '../lib/VueTestcaseMinderEditor.css' 7 | import VueTestcaseMinderEditor from '../lib/VueTestcaseMinderEditor.umd.min.js' 8 | 9 | Vue.use(VueTestcaseMinderEditor) 10 | console.log('using VueTestcaseMinderEditor', VueTestcaseMinderEditor) 11 | 12 | Vue.use(Vuex) 13 | const store = new Vuex.Store({ 14 | modules: { 15 | caseEditorStore: VueTestcaseMinderEditor.caseEditorStore 16 | } 17 | }) 18 | 19 | new Vue({ 20 | el: '#app', 21 | store, 22 | render: h => h(App), 23 | components: { 24 | App 25 | } 26 | }).$mount('#app') 27 | -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | import App from './App' 5 | 6 | // import '../lib/VueTestcaseMinderEditor.css' 7 | import VueTestcaseMinderEditor from './src/VueTestcaseMinderEditor.vue' 8 | 9 | // Vue.use(VueTestcaseMinderEditor) 10 | // console.log('using VueTestcaseMinderEditor', VueTestcaseMinderEditor) 11 | 12 | Vue.use(Vuex) 13 | const store = new Vuex.Store({ 14 | modules: { 15 | caseEditorStore: VueTestcaseMinderEditor.caseEditorStore 16 | } 17 | }) 18 | 19 | new Vue({ 20 | el: '#app', 21 | store, 22 | render: h => h(App), 23 | components: { 24 | App 25 | } 26 | }).$mount('#app') 27 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/components/menu/view/viewMenu.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 31 | -------------------------------------------------------------------------------- /kityminder-core/src/template/filetree.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview 3 | * 4 | * 文件夹模板 5 | * 6 | * @author: techird 7 | * @copyright: Baidu FEX, 2014 8 | */ 9 | define(function(require, exports, module) { 10 | var template = require('../core/template'); 11 | 12 | template.register('filetree', { 13 | 14 | getLayout: function(node) { 15 | if (node.getData('layout')) return node.getData('layout'); 16 | if (node.isRoot()) return 'bottom'; 17 | 18 | return 'filetree-down'; 19 | }, 20 | 21 | getConnect: function(node) { 22 | if (node.getLevel() == 1) { 23 | return 'poly'; 24 | } 25 | return 'l'; 26 | } 27 | }); 28 | }); -------------------------------------------------------------------------------- /kityminder-core/src/template/tianpan.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview 3 | * 4 | * 天盘模板 5 | * 6 | * @author: along 7 | * @copyright: bpd729@163.com, 2015 8 | */ 9 | define(function(require, exports, module) { 10 | var template = require('../core/template'); 11 | 12 | template.register('tianpan', { 13 | getLayout: function (node) { 14 | if (node.getData('layout')) return node.getData('layout'); 15 | var level = node.getLevel(); 16 | 17 | // 根节点 18 | if (level === 0) { 19 | return 'tianpan'; 20 | } 21 | 22 | return node.parent.getLayout(); 23 | }, 24 | 25 | getConnect: function (node) { 26 | return 'arc_tp'; 27 | } 28 | }); 29 | }); -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/script/runtime/minder.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview 3 | * 4 | * 脑图示例运行时 5 | * 6 | * @author: techird 7 | * @copyright: Baidu FEX, 2014 8 | */ 9 | define(function (require, exports, module) { 10 | var Minder = require('../minder'); 11 | 12 | function MinderRuntime() { 13 | 14 | // 不使用 kityminder 的按键处理,由 ReceiverRuntime 统一处理 15 | var minder = new Minder({ 16 | enableKeyReceiver: false, 17 | enableAnimation: true 18 | }); 19 | 20 | // 渲染,初始化 21 | minder.renderTo(this.selector); 22 | minder.setTheme(null); 23 | minder.select(minder.getRoot(), true); 24 | minder.execCommand('text', '中心主题'); 25 | 26 | // 导出给其它 Runtime 使用 27 | this.minder = minder; 28 | } 29 | 30 | return module.exports = MinderRuntime; 31 | }); 32 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | publicPath: './', 3 | pages: { 4 | index: { 5 | entry: 'examples/main.js', // 走示例项目,需要 npm run lib 后才能使用 6 | // entry: 'packages/VueTestcaseMinderEditor/main.js', // 走主工程项目,可直接 npm run serve 使用。方便调试时查看源码堆栈 7 | template: 'public/index.html', 8 | filename: 'index.html' 9 | } 10 | }, 11 | chainWebpack: config => { 12 | config.module 13 | .rule('js') 14 | .include 15 | .add(require('path').join(__dirname, '..', 'packages')) 16 | .end() 17 | .use('babel') 18 | .loader('babel-loader') 19 | .tap(options => { 20 | return options 21 | }) 22 | }, 23 | lintOnSave: false, 24 | configureWebpack: { 25 | // provide the app's title in webpack's name field, so that 26 | // it can be accessed in index.html to inject the correct title. 27 | devtool: 'source-map' 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /kityminder-core/src/connect/fish-bone-master.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview 3 | * 4 | * 鱼骨头主干连线 5 | * 6 | * @author: techird 7 | * @copyright: Baidu FEX, 2014 8 | */ 9 | 10 | define(function(require, exports, module) { 11 | var kity = require('../core/kity'); 12 | var connect = require('../core/connect'); 13 | 14 | connect.register('fish-bone-master', function(node, parent, connection) { 15 | 16 | var pout = parent.getLayoutVertexOut(), 17 | pin = node.getLayoutVertexIn(); 18 | 19 | var abs = Math.abs; 20 | 21 | var dy = abs(pout.y - pin.y), 22 | dx = abs(pout.x - pin.x); 23 | 24 | var pathData = []; 25 | 26 | pathData.push('M', pout.x, pout.y); 27 | pathData.push('h', dx - dy); 28 | pathData.push('L', pin.x, pin.y); 29 | 30 | connection.setMarker(null); 31 | connection.setPathData(pathData); 32 | }); 33 | }); -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/components/menu/view/arrange.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 31 | -------------------------------------------------------------------------------- /kityminder-core/src/connect/l.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview 3 | * 4 | * "L" 连线 5 | * 6 | * @author: techird 7 | * @copyright: Baidu FEX, 2014 8 | */ 9 | 10 | define(function(require, exports, module) { 11 | var kity = require('../core/kity'); 12 | var connect = require('../core/connect'); 13 | 14 | connect.register('l', function(node, parent, connection) { 15 | 16 | var po = parent.getLayoutVertexOut(); 17 | var pi = node.getLayoutVertexIn(); 18 | var vo = parent.getLayoutVectorOut(); 19 | 20 | var pathData = []; 21 | var r = Math.round, 22 | abs = Math.abs; 23 | 24 | pathData.push('M', po.round()); 25 | if (abs(vo.x) > abs(vo.y)) { 26 | pathData.push('H', r(pi.x)); 27 | } else { 28 | pathData.push('V', pi.y); 29 | } 30 | pathData.push('L', pi); 31 | 32 | connection.setPathData(pathData); 33 | }); 34 | }); -------------------------------------------------------------------------------- /kityminder-core/src/core/_boxv.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview 3 | * 4 | * 调试工具:为 kity.Box 提供一个可视化的渲染 5 | * 6 | * @author: techird 7 | * @copyright: Baidu FEX, 2014 8 | */ 9 | 10 | define(function(require, exports, module) { 11 | var kity = require('./kity'); 12 | var Minder = require('./minder'); 13 | 14 | if (location.href.indexOf('boxv') != -1) { 15 | 16 | var vrect; 17 | 18 | Object.defineProperty(kity.Box.prototype, 'visualization', { 19 | get: function() { 20 | if (!vrect) return null; 21 | return vrect.setBox(this); 22 | } 23 | }); 24 | 25 | Minder.registerInitHook(function() { 26 | this.on('paperrender', function() { 27 | vrect = new kity.Rect(); 28 | vrect.fill('rgba(200, 200, 200, .5)'); 29 | vrect.stroke('orange'); 30 | this.getRenderContainer().addShape(vrect); 31 | }); 32 | }); 33 | } 34 | }); -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/script/tool/innertext.js: -------------------------------------------------------------------------------- 1 | define(function (require, exports, module) { 2 | if ((!('innerText' in document.createElement('a'))) && ('getSelection' in window)) { 3 | HTMLElement.prototype.__defineGetter__('innerText', function () { 4 | var selection = window.getSelection(), 5 | ranges = [], 6 | str, i; 7 | 8 | for (i = 0; i < selection.rangeCount; i++) { 9 | ranges[i] = selection.getRangeAt(i); 10 | } 11 | 12 | selection.removeAllRanges(); 13 | selection.selectAllChildren(this); 14 | str = selection.toString(); 15 | selection.removeAllRanges(); 16 | for (i = 0; i < ranges.length; i++) { 17 | selection.addRange(ranges[i]); 18 | } 19 | return str; 20 | }); 21 | HTMLElement.prototype.__defineSetter__('innerText', function (text) { 22 | this.innerHTML = (text || '').replace(//g, '>').replace(/\n/g, '
'); 23 | }); 24 | } 25 | }); 26 | -------------------------------------------------------------------------------- /kityminder-core/src/template/default.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview 3 | * 4 | * 默认模板 - 脑图模板 5 | * 6 | * @author: techird 7 | * @copyright: Baidu FEX, 2014 8 | */ 9 | define(function(require, exports, module) { 10 | var template = require('../core/template'); 11 | 12 | template.register('default', { 13 | 14 | getLayout: function(node) { 15 | 16 | if (node.getData('layout')) return node.getData('layout'); 17 | 18 | var level = node.getLevel(); 19 | 20 | // 根节点 21 | if (level === 0) { 22 | return 'mind'; 23 | } 24 | 25 | // 一级节点 26 | if (level === 1) { 27 | return node.getLayoutPointPreview().x > 0 ? 'right': 'left'; 28 | } 29 | 30 | return node.parent.getLayout(); 31 | }, 32 | 33 | getConnect: function(node) { 34 | if (node.getLevel() == 1) return 'arc'; 35 | return 'under'; 36 | } 37 | }); 38 | }); -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/store/state.js: -------------------------------------------------------------------------------- 1 | export const state = { 2 | count: 2, 3 | minder: {}, 4 | editor: {}, 5 | working: { 6 | editing: false, 7 | saving: false, 8 | draging: false 9 | }, 10 | callbackQueue: [], 11 | config: { 12 | // 右侧面板最小宽度 13 | ctrlPanelMin: 250, 14 | 15 | // 右侧面板宽度 16 | ctrlPanelWidth: parseInt(window.localStorage.__dev_minder_ctrlPanelWidth) || 250, 17 | 18 | // 分割线宽度 19 | dividerWidth: 3, 20 | 21 | // 默认语言 22 | defaultLang: 'zh-cn', 23 | 24 | // 放大缩小比例 25 | zoom: [ 26 | 10, 27 | 20, 28 | 30, 29 | 50, 30 | 80, 31 | 100, 32 | 120, 33 | 150, 34 | 200 35 | ], 36 | 37 | // 允许编辑优先级 38 | allowEditPriority: true, 39 | 40 | // 允许编辑标签 41 | allowEditLabel: true, 42 | 43 | // 允许编辑测试结果 44 | allowEditResult: true, 45 | 46 | // 允许编辑节点 47 | allowEditNode: true, 48 | 49 | // 是否全屏 50 | isFullScreen: false 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /kityminder-core/src/theme/wire.js: -------------------------------------------------------------------------------- 1 | define(function(require, exports, module) { 2 | var theme = require('../core/theme'); 3 | 4 | theme.register('wire', { 5 | 'background': 'black', 6 | 7 | 'color': '#999', 8 | 'stroke': 'none', 9 | 'padding': 10, 10 | 'margin': 20, 11 | 'font-size': 14, 12 | 13 | 'connect-color': '#999', 14 | 'connect-width': 1, 15 | 16 | 'selected-background': '#999', 17 | 'selected-color': 'black', 18 | 19 | 'marquee-background': 'rgba(255,255,255,.3)', 20 | 'marquee-stroke': 'white', 21 | 22 | 'drop-hint-color': 'yellow', 23 | 'sub-drop-hint-width': 2, 24 | 'main-drop-hint-width': 4, 25 | 'root-drop-hint-width': 4, 26 | 27 | 'order-hint-area-color': 'rgba(0, 255, 0, .5)', 28 | 'order-hint-path-color': '#0f0', 29 | 'order-hint-path-width': 1, 30 | 31 | 'text-selection-color': 'rgb(27,171,255)', 32 | 'line-height':1.5 33 | }); 34 | }); -------------------------------------------------------------------------------- /kityminder-core/src/core/option.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview 3 | * 4 | * 提供脑图选项支持 5 | * 6 | * @author: techird 7 | * @copyright: Baidu FEX, 2014 8 | */ 9 | define(function(require, exports, module) { 10 | var kity = require('./kity'); 11 | var utils = require('./utils'); 12 | var Minder = require('./minder'); 13 | 14 | Minder.registerInitHook(function(options) { 15 | this._defaultOptions = {}; 16 | }); 17 | 18 | kity.extendClass(Minder, { 19 | setDefaultOptions: function(options) { 20 | utils.extend(this._defaultOptions, options); 21 | return this; 22 | }, 23 | getOption: function(key) { 24 | if (key) { 25 | return key in this._options ? this._options[key] : this._defaultOptions[key]; 26 | } else { 27 | return utils.extend({}, this._defaultOptions, this._options); 28 | } 29 | }, 30 | setOption: function(key, value) { 31 | this._options[key] = value; 32 | } 33 | }); 34 | }); -------------------------------------------------------------------------------- /kityminder-core/src/core/minder.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview 3 | * 4 | * KityMinder 类,暴露在 window 上的唯一变量 5 | * 6 | * @author: techird 7 | * @copyright: Baidu FEX, 2014 8 | */ 9 | define(function(require, exports, module) { 10 | var kity = require('./kity'); 11 | var utils = require('./utils'); 12 | 13 | var _initHooks = []; 14 | 15 | var Minder = kity.createClass('Minder', { 16 | constructor: function(options) { 17 | this._options = utils.extend({}, options); 18 | 19 | var initHooks = _initHooks.slice(); 20 | 21 | var initHook; 22 | while (initHooks.length) { 23 | initHook = initHooks.shift(); 24 | if (typeof(initHook) == 'function') { 25 | initHook.call(this, this._options); 26 | } 27 | } 28 | 29 | this.fire('finishInitHook'); 30 | } 31 | }); 32 | 33 | Minder.version = '1.4.43'; 34 | 35 | Minder.registerInitHook = function(hook) { 36 | _initHooks.push(hook); 37 | }; 38 | 39 | module.exports = Minder; 40 | }); 41 | -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/store/getters.js: -------------------------------------------------------------------------------- 1 | export const count = state => { 2 | return state.count; 3 | } 4 | 5 | export const working = state => { 6 | return { 7 | saving: state.working.saving, 8 | draging: state.working.draging, 9 | editing: state.working.editing 10 | } 11 | } 12 | 13 | export const config = state => { 14 | return { 15 | ctrlPanelMin: state.config.ctrlPanelMin, 16 | ctrlPanelWidth: state.config.ctrlPanelWidth, 17 | dividerWidth: state.config.dividerWidth, 18 | defaultLang: state.config.defaultLang, 19 | zoom: state.config.zoom, 20 | allowEditPriority: state.config.allowEditPriority, 21 | allowEditLabel: state.config.allowEditLabel, 22 | allowEditResult: state.config.allowEditResult, 23 | allowEditNode: state.config.allowEditNode, 24 | isFullScreen: state.config.isFullScreen 25 | } 26 | } 27 | 28 | export const getMinder = state => { 29 | return state.minder 30 | } 31 | 32 | export const getEditor = state => { 33 | return state.editor 34 | } 35 | 36 | export const getAllowEditResult = state => { 37 | return state.allowEditResult 38 | } 39 | -------------------------------------------------------------------------------- /kityminder-core/src/template/fish-bone.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview 3 | * 4 | * 默认模板 - 鱼骨头模板 5 | * 6 | * @author: techird 7 | * @copyright: Baidu FEX, 2014 8 | */ 9 | define(function(require, exports, module) { 10 | var template = require('../core/template'); 11 | 12 | template.register('fish-bone', { 13 | 14 | getLayout: function(node) { 15 | 16 | if (node.getData('layout')) return node.getData('layout'); 17 | 18 | var level = node.getLevel(); 19 | 20 | // 根节点 21 | if (level === 0) { 22 | return 'fish-bone-master'; 23 | } 24 | 25 | // 一级节点 26 | if (level === 1) { 27 | return 'fish-bone-slave'; 28 | } 29 | 30 | return node.getLayoutPointPreview().y > 0 ? 'filetree-up': 'filetree-down'; 31 | }, 32 | 33 | getConnect: function(node) { 34 | switch (node.getLevel()) { 35 | case 1: return 'fish-bone-master'; 36 | case 2: return 'line'; 37 | default: return 'l'; 38 | } 39 | } 40 | }); 41 | }); -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/store/mutations.js: -------------------------------------------------------------------------------- 1 | export const mutations = { 2 | 3 | changeDrag(state, bool) { 4 | state.working.draging = bool; 5 | }, 6 | 7 | setMinder(state, data) { 8 | state.minder = data 9 | }, 10 | 11 | setEditor(state, data) { 12 | state.editor = data 13 | }, 14 | 15 | changeSave(state, bool) { 16 | state.working.saving = bool; 17 | }, 18 | 19 | changeCount(state) { 20 | state.count++; 21 | }, 22 | 23 | increment(state) { 24 | state.count++ 25 | }, 26 | 27 | decrement(state) { 28 | state.count-- 29 | }, 30 | 31 | registerEvent(state, callback) { 32 | state.callbackQueue.push(callback); 33 | }, 34 | 35 | setConfig(state, data) { 36 | var supported = Object.keys(state.config); 37 | 38 | for (var key in data) { 39 | if (data.hasOwnProperty(key) && supported.indexOf(key) !== -1) { 40 | state.config[key] = data[key]; 41 | } else { 42 | console.error('Unsupported config key: ', key, ', please choose in :', supported.join(', ')); 43 | return false; 44 | } 45 | } 46 | return true; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /hotbox/test/SpecRunner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Jasmine Spec Runner v2.1.3 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 21 | 22 | 36 | 37 | 38 | 39 |
40 | 41 | 42 | -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/script/tool/debug.js: -------------------------------------------------------------------------------- 1 | define(function (require, exports, module) { 2 | var format = require('./format'); 3 | 4 | function noop() {} 5 | 6 | function stringHash(str) { 7 | var hash = 0; 8 | for (var i = 0; i < str.length; i++) { 9 | hash += str.charCodeAt(i); 10 | } 11 | return hash; 12 | } 13 | 14 | function Debug(flag) { 15 | var debugMode = this.flaged = window.location.search.indexOf(flag) != -1; 16 | 17 | if (debugMode) { 18 | var h = stringHash(flag) % 360; 19 | 20 | var flagStyle = format( 21 | 'background: hsl({0}, 50%, 80%); ' + 22 | 'color: hsl({0}, 100%, 30%); ' + 23 | 'padding: 2px 3px; ' + 24 | 'margin: 1px 3px 0 0;' + 25 | 'border-radius: 2px;', h); 26 | 27 | var textStyle = 'background: none; color: black;'; 28 | this.log = function () { 29 | var output = format.apply(null, arguments); 30 | console.log(format('%c{0}%c{1}', flag, output), flagStyle, textStyle); 31 | }; 32 | } else { 33 | this.log = noop; 34 | } 35 | } 36 | 37 | return module.exports = Debug; 38 | }); 39 | -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/script/runtime/result.js: -------------------------------------------------------------------------------- 1 | define(function (require, exports, module) { 2 | 3 | function ResultRuntime() { 4 | var minder = this.minder; 5 | var hotbox = this.hotbox; 6 | var main = hotbox.state("main"); 7 | var resultButtonMapper = { 8 | '0': { 9 | label: '移除结果', 10 | key: 'Alt + D' 11 | }, 12 | '9': { 13 | label: '成功', 14 | key: 'Alt + G' 15 | }, 16 | '1': { 17 | label: '失败', 18 | key: 'Alt + F' 19 | }, 20 | '5': { 21 | label: '阻塞', 22 | key: 'Alt + B' 23 | }, 24 | '4': { 25 | label: '不执行', 26 | key: 'Alt + S' 27 | } 28 | } 29 | 30 | // 直接加到 main 中,会导致无法显示,但可以保证快捷键可以直接在界面使用 31 | "09154".replace(/./g, function(p) { 32 | main.button({ 33 | position: "buttom", 34 | label: resultButtonMapper[p].label, 35 | key: resultButtonMapper[p].key, 36 | action: function() { 37 | minder.execCommand("result", Number(p)); 38 | } 39 | }); 40 | }); 41 | } 42 | 43 | return module.exports = ResultRuntime; 44 | }); 45 | -------------------------------------------------------------------------------- /kityminder-core/src/connect/bezier.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview 3 | * 4 | * 提供折线相连的方法 5 | * 6 | * @author: techird 7 | * @copyright: Baidu FEX, 2014 8 | */ 9 | 10 | define(function(require, exports, module) { 11 | var kity = require('../core/kity'); 12 | var connect = require('../core/connect'); 13 | 14 | connect.register('bezier', function(node, parent, connection) { 15 | 16 | // 连线起点和终点 17 | var po = parent.getLayoutVertexOut(), 18 | pi = node.getLayoutVertexIn(); 19 | 20 | // 连线矢量和方向 21 | var v = parent.getLayoutVectorOut().normalize(); 22 | 23 | var r = Math.round; 24 | var abs = Math.abs; 25 | 26 | var pathData = []; 27 | pathData.push('M', r(po.x), r(po.y)); 28 | 29 | if (abs(v.x) > abs(v.y)) { 30 | // x - direction 31 | var hx = (pi.x + po.x) / 2; 32 | pathData.push('C', hx, po.y, hx, pi.y, pi.x, pi.y); 33 | } else { 34 | // y - direction 35 | var hy = (pi.y + po.y) / 2; 36 | pathData.push('C', po.x, hy, pi.x, hy, pi.x, pi.y); 37 | } 38 | 39 | connection.setMarker(null); 40 | connection.setPathData(pathData); 41 | }); 42 | }); -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/script/runtime/progress.js: -------------------------------------------------------------------------------- 1 | define(function (require, exports, module) { 2 | 3 | function ProgressRuntime() { 4 | var minder = this.minder; 5 | var hotbox = this.hotbox; 6 | 7 | var main = hotbox.state('main'); 8 | 9 | main.button({ 10 | position: 'top', 11 | label: '进度', 12 | key: 'G', 13 | next: 'progress', 14 | enable: function () { 15 | return minder.queryCommandState('progress') != -1; 16 | } 17 | }); 18 | 19 | var progress = hotbox.state('progress'); 20 | '012345678'.replace(/./g, function (p) { 21 | progress.button({ 22 | position: 'ring', 23 | label: 'G' + p, 24 | key: p, 25 | action: function () { 26 | minder.execCommand('Progress', parseInt(p) + 1); 27 | } 28 | }); 29 | }); 30 | 31 | progress.button({ 32 | position: 'center', 33 | label: '移除', 34 | key: 'Del', 35 | action: function () { 36 | minder.execCommand('Progress', 0); 37 | } 38 | }); 39 | 40 | progress.button({ 41 | position: 'top', 42 | label: '返回', 43 | key: 'esc', 44 | next: 'back' 45 | }); 46 | } 47 | 48 | return module.exports = ProgressRuntime; 49 | 50 | }); 51 | -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/components/menu/edit/moveBox.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 45 | -------------------------------------------------------------------------------- /kityminder-core/src/core/animate.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview 3 | * 4 | * 动画控制 5 | * 6 | * @author: techird 7 | * @copyright: Baidu FEX, 2014 8 | */ 9 | define(function(require, exports, module) { 10 | var Minder = require('./minder'); 11 | 12 | var animateDefaultOptions = { 13 | enableAnimation: true, 14 | layoutAnimationDuration: 300, 15 | viewAnimationDuration: 100, 16 | zoomAnimationDuration: 300 17 | }; 18 | var resoredAnimationOptions = {}; 19 | 20 | Minder.registerInitHook(function() { 21 | this.setDefaultOptions(animateDefaultOptions); 22 | if (!this.getOption('enableAnimation')) { 23 | this.disableAnimation(); 24 | } 25 | }); 26 | 27 | Minder.prototype.enableAnimation = function() { 28 | for (var name in animateDefaultOptions) { 29 | if (animateDefaultOptions.hasOwnProperty(name)) { 30 | this.setOption(resoredAnimationOptions[name]); 31 | } 32 | } 33 | }; 34 | 35 | Minder.prototype.disableAnimation = function() { 36 | for (var name in animateDefaultOptions) { 37 | if (animateDefaultOptions.hasOwnProperty(name)) { 38 | resoredAnimationOptions[name] = this.getOption(name); 39 | this.setOption(name, 0); 40 | } 41 | } 42 | }; 43 | }); -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/script/editor.js: -------------------------------------------------------------------------------- 1 | define(function (require, exports, module) { 2 | var runtimes = []; 3 | 4 | function assemble(runtime) { 5 | runtimes.push(runtime); 6 | } 7 | 8 | function KMEditor(selector) { 9 | this.selector = selector; 10 | for (var i = 0; i < runtimes.length; i++) { 11 | if (typeof runtimes[i] == 'function') { 12 | runtimes[i].call(this, this); 13 | } 14 | } 15 | } 16 | 17 | KMEditor.assemble = assemble; 18 | 19 | assemble(require('./runtime/container')); 20 | assemble(require('./runtime/fsm')); 21 | assemble(require('./runtime/minder')); 22 | assemble(require('./runtime/receiver')); 23 | assemble(require('./runtime/hotbox')); 24 | assemble(require('./runtime/input')); 25 | assemble(require('./runtime/clipboard-mimetype')); 26 | assemble(require('./runtime/clipboard')); 27 | assemble(require('./runtime/drag')); 28 | assemble(require('./runtime/node')); 29 | assemble(require('./runtime/history')); 30 | assemble(require('./runtime/jumping')); 31 | assemble(require('./runtime/priority')); 32 | // 为兼容原有滴滴用例,用例结果复用了 progress 这个 key 所以不能出原版 progress 的 hotbox 按钮 33 | // assemble(require('./runtime/progress')); 34 | // 禁用导出功能 35 | // assemble(require('./runtime/exports')); 36 | assemble(require('./runtime/result')); 37 | 38 | return module.exports = KMEditor; 39 | }); 40 | -------------------------------------------------------------------------------- /hotbox/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hotbox", 3 | "title": "Hot Box UI", 4 | "description": "Efficiency And Flexible Editor UI", 5 | "version": "1.0.15", 6 | "homepage": "https://github.com/fex-team/hotbox", 7 | "author": { 8 | "name": "Baidu FEX", 9 | "url": "https://github.com/fex-team" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/fex-team/hotbox.git" 14 | }, 15 | "keywords": [ 16 | "ui", 17 | "hotbox", 18 | "efficiency", 19 | "flexible", 20 | "javascript", 21 | "library" 22 | ], 23 | "bugs": { 24 | "url": "https://github.com/fex-team/hotbox/issues" 25 | }, 26 | "main": "src/mog.js", 27 | "licenses": [ 28 | { 29 | "type": "BSD", 30 | "url": "https://github.com/fex-team/hotbox/blob/dev/LICENSE" 31 | } 32 | ], 33 | "dependencies": {}, 34 | "devDependencies": { 35 | "grunt": "^0.4.5", 36 | "grunt-autoprefixer": "^2.0.0", 37 | "grunt-contrib-concat": "^0.5.0", 38 | "grunt-contrib-less": "^0.12.0", 39 | "grunt-contrib-uglify": "~0.4.0", 40 | "grunt-contrib-watch": "^0.6.1", 41 | "grunt-module-dependence": "~0.2.0", 42 | "grunt-replace": "^0.8.0", 43 | "less": "~2.1.1", 44 | "watch": "~0.13.0" 45 | }, 46 | "scripts": { 47 | "watch-less": "watch \"lessc --source-map less/hotbox.less hotbox.css\" \"less\"" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /kityminder-core/src/core/focus.js: -------------------------------------------------------------------------------- 1 | define(function(require, exports, module) { 2 | var kity = require('./kity'); 3 | var Minder = require('./minder'); 4 | 5 | Minder.registerInitHook(function() { 6 | this.on('beforemousedown', function(e) { 7 | this.focus(); 8 | e.preventDefault(); 9 | }); 10 | this.on('paperrender', function() { 11 | this.focus(); 12 | }); 13 | }); 14 | 15 | kity.extendClass(Minder, { 16 | focus: function() { 17 | if (!this.isFocused()) { 18 | var renderTarget = this._renderTarget; 19 | renderTarget.classList.add('focus'); 20 | this.renderNodeBatch(this.getSelectedNodes()); 21 | } 22 | this.fire('focus'); 23 | return this; 24 | }, 25 | 26 | blur: function() { 27 | if (this.isFocused()) { 28 | var renderTarget = this._renderTarget; 29 | renderTarget.classList.remove('focus'); 30 | this.renderNodeBatch(this.getSelectedNodes()); 31 | } 32 | this.fire('blur'); 33 | return this; 34 | }, 35 | 36 | isFocused: function() { 37 | var renderTarget = this._renderTarget; 38 | return renderTarget && renderTarget.classList.contains('focus'); 39 | } 40 | }); 41 | }); -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/script/runtime/hotbox.js: -------------------------------------------------------------------------------- 1 | define(function (require, exports, module) { 2 | var Hotbox = require('../hotbox'); 3 | 4 | function HotboxRuntime() { 5 | var fsm = this.fsm; 6 | var minder = this.minder; 7 | var receiver = this.receiver; 8 | var container = this.container; 9 | 10 | var hotbox = new Hotbox(container); 11 | 12 | hotbox.setParentFSM(fsm); 13 | 14 | fsm.when('normal -> hotbox', function (exit, enter, reason) { 15 | var node = minder.getSelectedNode(); 16 | var position; 17 | if (node) { 18 | var box = node.getRenderBox(); 19 | position = { 20 | x: box.cx, 21 | y: box.cy 22 | }; 23 | } 24 | hotbox.active('main', position); 25 | }); 26 | 27 | fsm.when('normal -> normal', function (exit, enter, reason, e) { 28 | if (reason == 'shortcut-handle') { 29 | var handleResult = hotbox.dispatch(e); 30 | if (handleResult) { 31 | e.preventDefault(); 32 | } else { 33 | minder.dispatchKeyEvent(e); 34 | } 35 | } 36 | }); 37 | 38 | fsm.when('modal -> normal', function (exit, enter, reason, e) { 39 | if (reason == 'import-text-finish') { 40 | receiver.element.focus(); 41 | } 42 | }); 43 | 44 | this.hotbox = hotbox; 45 | } 46 | 47 | return module.exports = HotboxRuntime; 48 | }); 49 | -------------------------------------------------------------------------------- /kityminder-core/src/connect/under.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview 3 | * 4 | * 下划线连线 5 | * 6 | * @author: techird 7 | * @copyright: Baidu FEX, 2014 8 | */ 9 | 10 | define(function(require, exports, module) { 11 | var kity = require('../core/kity'); 12 | var connect = require('../core/connect'); 13 | 14 | connect.register('under', function(node, parent, connection, width, color) { 15 | 16 | var box = node.getLayoutBox(), 17 | pBox = parent.getLayoutBox(); 18 | 19 | var start, end, vector; 20 | var abs = Math.abs; 21 | var pathData = []; 22 | var side = box.x > pBox.x ? 'right' : 'left'; 23 | 24 | 25 | var radius = node.getStyle('connect-radius'); 26 | var underY = box.bottom + 3; 27 | var startY = parent.getType() == 'sub' ? pBox.bottom + 3 : pBox.cy; 28 | var p1, p2, p3, mx; 29 | 30 | if (side == 'right') { 31 | p1 = new kity.Point(pBox.right, startY); 32 | p2 = new kity.Point(box.left - 10, underY); 33 | p3 = new kity.Point(box.right, underY); 34 | } else { 35 | p1 = new kity.Point(pBox.left, startY); 36 | p2 = new kity.Point(box.right + 10, underY); 37 | p3 = new kity.Point(box.left, underY); 38 | } 39 | 40 | mx = (p1.x + p2.x) / 2; 41 | 42 | pathData.push('M', p1); 43 | pathData.push('C', mx, p1.y, mx, p2.y, p2); 44 | pathData.push('L', p3); 45 | 46 | connection.setMarker(null); 47 | 48 | connection.setPathData(pathData); 49 | }); 50 | }); -------------------------------------------------------------------------------- /kityminder-core/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, FEX, Baidu. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the KityMinder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/components/menu/edit/search/search.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 57 | 58 | 60 | -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/script/runtime/priority.js: -------------------------------------------------------------------------------- 1 | define(function (require, exports, module) { 2 | 3 | function PriorityRuntime() { 4 | var minder = this.minder; 5 | var hotbox = this.hotbox; 6 | 7 | var main = hotbox.state('main'); 8 | 9 | main.button({ 10 | position: 'top', 11 | label: '用例级别', 12 | key: 'P', 13 | next: 'priority', 14 | enable: function () { 15 | return minder.queryCommandState('priority') != -1; 16 | } 17 | }); 18 | 19 | /* var priority = hotbox.state('priority'); 20 | '123456789'.replace(/./g, function (p) { 21 | priority.button({ 22 | position: 'ring', 23 | label: 'P' + p, 24 | key: p, 25 | action: function () { 26 | minder.execCommand('Priority', p); 27 | } 28 | }); 29 | });*/ 30 | var priority = hotbox.state('priority'); 31 | '012'.replace(/./g, function (p) { 32 | priority.button({ 33 | position: 'ring', 34 | label: 'P' + p, 35 | key: p, 36 | action: function () { 37 | minder.execCommand('Priority', Number(p) + 1); 38 | } 39 | }); 40 | }); 41 | 42 | priority.button({ 43 | position: 'top', 44 | label: '移除', 45 | key: 'Del', 46 | action: function () { 47 | minder.execCommand('Priority', 0); 48 | } 49 | }); 50 | 51 | priority.button({ 52 | position: 'top', 53 | label: '返回', 54 | key: 'esc', 55 | next: 'back' 56 | }); 57 | } 58 | return module.exports = PriorityRuntime; 59 | }); 60 | -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/components/menu/edit/editDel.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 52 | -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/script/tool/keymap.js: -------------------------------------------------------------------------------- 1 | define(function (require, exports, module) { 2 | var keymap = { 3 | 4 | 'Shift': 16, 5 | 'Control': 17, 6 | 'Alt': 18, 7 | 'CapsLock': 20, 8 | 9 | 'BackSpace': 8, 10 | 'Tab': 9, 11 | 'Enter': 13, 12 | 'Esc': 27, 13 | 'Space': 32, 14 | 15 | 'PageUp': 33, 16 | 'PageDown': 34, 17 | 'End': 35, 18 | 'Home': 36, 19 | 20 | 'Insert': 45, 21 | 22 | 'Left': 37, 23 | 'Up': 38, 24 | 'Right': 39, 25 | 'Down': 40, 26 | 27 | 'Direction': { 28 | 37: 1, 29 | 38: 1, 30 | 39: 1, 31 | 40: 1 32 | }, 33 | 34 | 'Del': 46, 35 | 36 | 'NumLock': 144, 37 | 38 | 'Cmd': 91, 39 | 'CmdFF': 224, 40 | 'F1': 112, 41 | 'F2': 113, 42 | 'F3': 114, 43 | 'F4': 115, 44 | 'F5': 116, 45 | 'F6': 117, 46 | 'F7': 118, 47 | 'F8': 119, 48 | 'F9': 120, 49 | 'F10': 121, 50 | 'F11': 122, 51 | 'F12': 123, 52 | 53 | '`': 192, 54 | '=': 187, 55 | '-': 189, 56 | 57 | '/': 191, 58 | '.': 190 59 | }; 60 | 61 | for (var key in keymap) { 62 | if (keymap.hasOwnProperty(key)) { 63 | keymap[key.toLowerCase()] = keymap[key]; 64 | } 65 | } 66 | var aKeyCode = 65; 67 | var aCharCode = 'a'.charCodeAt(0); 68 | 69 | 'abcdefghijklmnopqrstuvwxyz'.split('').forEach(function (letter) { 70 | keymap[letter] = aKeyCode + (letter.charCodeAt(0) - aCharCode); 71 | }); 72 | 73 | var n = 9; 74 | do { 75 | keymap[n.toString()] = n + 48; 76 | } while (--n); 77 | 78 | module.exports = keymap; 79 | }); 80 | -------------------------------------------------------------------------------- /kityminder-core/src/connect/arc.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview 3 | * 4 | * 圆弧连线 5 | * 6 | * @author: techird 7 | * @copyright: Baidu FEX, 2014 8 | */ 9 | 10 | define(function(require, exports, module) { 11 | var kity = require('../core/kity'); 12 | var connect = require('../core/connect'); 13 | 14 | var connectMarker = new kity.Marker().pipe(function() { 15 | var r = 7; 16 | var dot = new kity.Circle(r - 1); 17 | this.addShape(dot); 18 | this.setRef(r - 1, 0).setViewBox(-r, -r, r + r, r + r).setWidth(r).setHeight(r); 19 | this.dot = dot; 20 | this.node.setAttribute('markerUnits', 'userSpaceOnUse'); 21 | }); 22 | 23 | connect.register('arc', function(node, parent, connection, width, color) { 24 | 25 | var box = node.getLayoutBox(), 26 | pBox = parent.getLayoutBox(); 27 | 28 | var start, end, vector; 29 | var abs = Math.abs; 30 | var pathData = []; 31 | var side = box.x > pBox.x ? 'right' : 'left'; 32 | 33 | node.getMinder().getPaper().addResource(connectMarker); 34 | 35 | start = new kity.Point(pBox.cx, pBox.cy); 36 | end = side == 'left' ? 37 | new kity.Point(box.right + 2, box.cy) : 38 | new kity.Point(box.left - 2, box.cy); 39 | 40 | vector = kity.Vector.fromPoints(start, end); 41 | pathData.push('M', start); 42 | pathData.push('A', abs(vector.x), abs(vector.y), 0, 0, (vector.x * vector.y > 0 ? 0 : 1), end); 43 | 44 | connection.setMarker(connectMarker); 45 | connectMarker.dot.fill(color); 46 | 47 | connection.setPathData(pathData); 48 | }); 49 | }); -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/style/dropdown-list.scss: -------------------------------------------------------------------------------- 1 | .link-dropdown-list, 2 | .img-dropdown-list, 3 | .remark-dropdown-list, 4 | .selection-dropdown-list, 5 | .expand-dropdown-list { 6 | font-size: 12px; 7 | } 8 | 9 | .mold-dropdown-list { 10 | width: 126px; 11 | //height: 170px; 12 | height: auto; 13 | font-size: 12px; 14 | .mold-icons { 15 | background-image: url("../static/minder/mold.png"); 16 | background-repeat: no-repeat; 17 | } 18 | .dropdown-item { 19 | display: inline-block; 20 | width: 50px; 21 | //width: 30px; 22 | height: 40px; 23 | padding: 0; 24 | margin: 5px; 25 | } 26 | @for $i from 1 through 6 { 27 | .mold-#{$i} { 28 | background-position: (1-$i) * 50px 0; 29 | } 30 | } 31 | } 32 | 33 | .theme-dropdown-list { 34 | width: 120px; 35 | font-size: 12px; 36 | .mold-icons { 37 | background-repeat: no-repeat; 38 | } 39 | .dropdown-item { 40 | display: inline-block; 41 | width: 100px; 42 | height: 30px; 43 | padding: 0; 44 | margin: 5px; 45 | } 46 | } 47 | 48 | .expand-dropdown-list { 49 | .dropdown-item { 50 | line-height: 25px; 51 | } 52 | } 53 | 54 | .selection-dropdown-list { 55 | .dropdown-item { 56 | line-height: 25px; 57 | } 58 | } 59 | 60 | .theme-group { 61 | background-color: pink; 62 | padding: 0 10px; 63 | } 64 | 65 | //.el-dropdown-menu { 66 | // padding: 10px !important; 67 | // margin: 10px !important; 68 | // //left: 40px !important; 69 | //} 70 | // 71 | //.el-dropdown-menu__item { 72 | // padding: 5px 0 5px 20px !important; 73 | // height: 35px !important; 74 | // line-height: 35px !important; 75 | // margin: 0 !important; 76 | //} 77 | -------------------------------------------------------------------------------- /kityminder-core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kityminder-core", 3 | "title": "Kity Minder Core", 4 | "description": "KityMinder Core Implement", 5 | "version": "1.4.50", 6 | "homepage": "https://github.com/fex-team/kityminder-core", 7 | "author": { 8 | "name": "Baidu FEX", 9 | "url": "http://fex.baidu.com" 10 | }, 11 | "main": "dist/kityminder.core.js", 12 | "license": "BSD-3-Clause", 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/fex-team/kityminder-core.git" 16 | }, 17 | "keywords": [ 18 | "kityminder", 19 | "kity", 20 | "minder", 21 | "svg", 22 | "graphic", 23 | "javascript" 24 | ], 25 | "scripts": { 26 | "dev": "grunt dev", 27 | "build": "grunt build" 28 | }, 29 | "bugs": { 30 | "url": "https://github.com/fex-team/kityminder-core/issues" 31 | }, 32 | "licenses": [ 33 | { 34 | "type": "BSD-3-Clause", 35 | "url": "https://github.com/fex-team/kityminder-core/blob/dev/LICENSE" 36 | } 37 | ], 38 | "dependencies": { 39 | "json-diff": "^0.5.2", 40 | "kity": "^2.0.4", 41 | "seajs": "^2.3.0" 42 | }, 43 | "devDependencies": { 44 | "cz-conventional-changelog": "^1.1.5", 45 | "grunt": "^0.4.5", 46 | "grunt-browser-sync": "^2.2.0", 47 | "grunt-contrib-clean": "~0.5.0", 48 | "grunt-contrib-concat": "~0.5.0", 49 | "grunt-contrib-copy": "~0.8.2", 50 | "grunt-contrib-uglify": "~0.4.0", 51 | "grunt-contrib-watch": "^1.0.0", 52 | "grunt-module-dependence": "~0.2.1", 53 | "grunt-replace": "~0.8.0", 54 | "uglify-js": "^2.8.29" 55 | }, 56 | "config": { 57 | "commitizen": { 58 | "path": "./node_modules/cz-conventional-changelog" 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/script/tool/key.js: -------------------------------------------------------------------------------- 1 | define(function (require, exports, module) { 2 | var keymap = require('./keymap'); 3 | 4 | var CTRL_MASK = 0x1000; 5 | var ALT_MASK = 0x2000; 6 | var SHIFT_MASK = 0x4000; 7 | 8 | function hash(unknown) { 9 | if (typeof (unknown) == 'string') { 10 | return hashKeyExpression(unknown); 11 | } 12 | return hashKeyEvent(unknown); 13 | } 14 | 15 | function is(a, b) { 16 | return a && b && hash(a) == hash(b); 17 | } 18 | exports.hash = hash; 19 | exports.is = is; 20 | 21 | function hashKeyEvent(keyEvent) { 22 | var hashCode = 0; 23 | if (keyEvent.ctrlKey || keyEvent.metaKey) { 24 | hashCode |= CTRL_MASK; 25 | } 26 | if (keyEvent.altKey) { 27 | hashCode |= ALT_MASK; 28 | } 29 | if (keyEvent.shiftKey) { 30 | hashCode |= SHIFT_MASK; 31 | } 32 | if ([16, 17, 18, 91].indexOf(keyEvent.keyCode) === -1) { 33 | if (keyEvent.keyCode === 229 && keyEvent.keyIdentifier) { 34 | return hashCode |= parseInt(keyEvent.keyIdentifier.substr(2), 16); 35 | } 36 | hashCode |= keyEvent.keyCode; 37 | } 38 | return hashCode; 39 | } 40 | 41 | function hashKeyExpression(keyExpression) { 42 | var hashCode = 0; 43 | keyExpression.toLowerCase().split(/\s*\+\s*/).forEach(function (name) { 44 | switch (name) { 45 | case 'ctrl': 46 | case 'cmd': 47 | hashCode |= CTRL_MASK; 48 | break; 49 | case 'alt': 50 | hashCode |= ALT_MASK; 51 | break; 52 | case 'shift': 53 | hashCode |= SHIFT_MASK; 54 | break; 55 | default: 56 | hashCode |= keymap[name]; 57 | } 58 | }); 59 | return hashCode; 60 | } 61 | }); 62 | -------------------------------------------------------------------------------- /kityminder-core/README.md: -------------------------------------------------------------------------------- 1 | KityMinder Core 2 | ========== 3 | 4 | ## 简介 5 | 6 | KityMinder 是一款强大的脑图可视化/编辑工具,由百度 FEX 团队开发并维护。 7 | 8 | 本仓库是 KityMinder 的核心实现部分: 9 | 10 | * 包括脑图数据的可视化展示(Json 格式) 11 | * 包括简单的编辑功能(节点创建、编辑、删除)。更加强大编辑功能的 KityMinder 编辑器请移步 [kityminder-editor](https://github.com/fex-team/kityminder-editor) 12 | * 不包含第三方格式(FreeMind、XMind、MindManager)的支持,可以加载 [kityminder-protocol](https://github.com/fex-team/kityminder-third-party-protocol) 来扩展第三方格式支持。 13 | * 不包含文件存储的支持,需要自行实现存储。可参照[百度脑图](https://naotu.baidu.com)中的开源的 fio + 百度网盘方案进行实现。 14 | 15 | ## 使用 16 | 17 | 可以参考 [example.html](example.html) 进行使用。 18 | 19 | ```js 20 |
21 | 22 | 27 | ``` 28 | 29 | 更多详细的开发资料可以参考 [wiki](https://github.com/fex-team/kityminder-core/wiki) 30 | 31 | ## 兼容性 32 | 33 | KityMinder 基于 SVG 技术实现,支持绝大多数的 HTML5 浏览器,包括: 34 | 35 | 1. Chrome 36 | 2. Firefox 37 | 3. Safari 38 | 4. Internet Explorer 10 或以上 39 | 40 | ## 使用说明 41 | 42 | kityminder-core 依赖于 [kity](https://github.com/fex-team/kity),开发中用到 seajs 进行异步加载。 43 | 例子中 dev.html 使用 seajs 进行包加载,example.html 使用同步加载的方式。 44 | 使用步骤如下: 45 | 46 | 1. 安装 [bower](http://bower.io/#install-bower) 47 | 2. 切换到 kityminder-core 目录下,运行: 48 | 49 | ```bash 50 | bower install 51 | ``` 52 | 53 | ## 开发说明 54 | 55 | 1. 安装 [bower](http://bower.io/#install-bower) 56 | 2. 安装 [npm](https://www.npmjs.com/get-npm) 57 | 58 | ```bash 59 | bower install 60 | npm install 61 | npm run dev 62 | ``` 63 | 64 | ## 联系我们 65 | 66 | 问题和建议反馈:[Github Issues](https://github.com/fex-team/kityminder-core/issues) 67 | 邮件组: kity@baidu.com 68 | QQ 讨论群: 374918234 69 | -------------------------------------------------------------------------------- /hotbox/src/keymap.js: -------------------------------------------------------------------------------- 1 | define(function(require, exports, module) { 2 | var keymap = { 3 | 4 | 'Shift': 16, 5 | 'Control': 17, 6 | 'Alt': 18, 7 | 'CapsLock': 20, 8 | 9 | 'BackSpace': 8, 10 | 'Tab': 9, 11 | 'Enter': 13, 12 | 'Esc': 27, 13 | 'Space': 32, 14 | 15 | 'PageUp': 33, 16 | 'PageDown': 34, 17 | 'End': 35, 18 | 'Home': 36, 19 | 20 | 'Insert': 45, 21 | 22 | 'Left': 37, 23 | 'Up': 38, 24 | 'Right': 39, 25 | 'Down': 40, 26 | 27 | 'Direction': { 28 | 37: 1, 29 | 38: 1, 30 | 39: 1, 31 | 40: 1 32 | }, 33 | 34 | 'Delete': 46, 35 | 36 | 'NumLock': 144, 37 | 38 | 'Cmd': 91, 39 | 'CmdFF': 224, 40 | 'F1': 112, 41 | 'F2': 113, 42 | 'F3': 114, 43 | 'F4': 115, 44 | 'F5': 116, 45 | 'F6': 117, 46 | 'F7': 118, 47 | 'F8': 119, 48 | 'F9': 120, 49 | 'F10': 121, 50 | 'F11': 122, 51 | 'F12': 123, 52 | 53 | '`': 192, 54 | '=': 187, 55 | '-': 189, 56 | 57 | '/': 191, 58 | '.': 190 59 | }; 60 | 61 | // 小写适配 62 | for (var key in keymap) { 63 | if (keymap.hasOwnProperty(key)) { 64 | keymap[key.toLowerCase()] = keymap[key]; 65 | } 66 | } 67 | var aKeyCode = 65; 68 | var aCharCode = 'a'.charCodeAt(0); 69 | 70 | // letters 71 | 'abcdefghijklmnopqrstuvwxyz'.split('').forEach(function(letter) { 72 | keymap[letter] = aKeyCode + (letter.charCodeAt(0) - aCharCode); 73 | }); 74 | 75 | // numbers 76 | var n = 9; 77 | do { 78 | keymap[n.toString()] = n + 48; 79 | } while (n--); 80 | 81 | module.exports = keymap; 82 | }); -------------------------------------------------------------------------------- /hotbox/src/key.js: -------------------------------------------------------------------------------- 1 | define(function(require, exports, module) { 2 | var keymap = require('./keymap'); 3 | 4 | var CTRL_MASK = 0x1000; 5 | var ALT_MASK = 0x2000; 6 | var SHIFT_MASK = 0x4000; 7 | 8 | function hash(unknown) { 9 | if (typeof(unknown) == 'string') { 10 | return hashKeyExpression(unknown); 11 | } 12 | return hashKeyEvent(unknown); 13 | } 14 | function is(a, b) { 15 | return a && b && hash(a) == hash(b); 16 | } 17 | exports.hash = hash; 18 | exports.is = is; 19 | 20 | 21 | function hashKeyEvent(keyEvent) { 22 | var hashCode = 0; 23 | if (keyEvent.ctrlKey || keyEvent.metaKey) { 24 | hashCode |= CTRL_MASK; 25 | } 26 | if (keyEvent.altKey) { 27 | hashCode |= ALT_MASK; 28 | } 29 | if (keyEvent.shiftKey) { 30 | hashCode |= SHIFT_MASK; 31 | } 32 | // Shift, Control, Alt KeyCode ignored. 33 | if ([16, 17, 18, 91].indexOf(keyEvent.keyCode) == -1) { 34 | hashCode |= keyEvent.keyCode; 35 | } 36 | return hashCode; 37 | } 38 | 39 | function hashKeyExpression(keyExpression) { 40 | var hashCode = 0; 41 | keyExpression.toLowerCase().split(/\s*\+\s*/).forEach(function(name) { 42 | switch(name) { 43 | case 'ctrl': 44 | case 'cmd': 45 | hashCode |= CTRL_MASK; 46 | break; 47 | case 'alt': 48 | hashCode |= ALT_MASK; 49 | break; 50 | case 'shift': 51 | hashCode |= SHIFT_MASK; 52 | break; 53 | default: 54 | hashCode |= keymap[name]; 55 | } 56 | }); 57 | return hashCode; 58 | } 59 | }); -------------------------------------------------------------------------------- /kityminder-core/src/core/status.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview 3 | * 4 | * 状态切换控制 5 | * 6 | * @author: techird 7 | * @copyright: Baidu FEX, 2014 8 | */ 9 | 10 | define(function(require, exports, module) { 11 | var kity = require('./kity'); 12 | var Minder = require('./minder'); 13 | 14 | var sf = ~window.location.href.indexOf('status'); 15 | var tf = ~window.location.href.indexOf('trace'); 16 | 17 | Minder.registerInitHook(function() { 18 | this._initStatus(); 19 | }); 20 | 21 | kity.extendClass(Minder, { 22 | 23 | _initStatus: function() { 24 | this._status = 'normal'; 25 | this._rollbackStatus = 'normal'; 26 | }, 27 | 28 | setStatus: function(status, force) { 29 | // 在 readonly 模式下,只有 force 为 true 才能切换回来 30 | if (this._status == 'readonly' && !force) return this; 31 | if (status != this._status) { 32 | this._rollbackStatus = this._status; 33 | this._status = status; 34 | this.fire('statuschange', { 35 | lastStatus: this._rollbackStatus, 36 | currentStatus: this._status 37 | }); 38 | if (sf) { 39 | /* global console: true */ 40 | console.log(window.event.type, this._rollbackStatus, '->', this._status); 41 | if (tf) { 42 | console.trace(); 43 | } 44 | } 45 | } 46 | return this; 47 | }, 48 | 49 | rollbackStatus: function() { 50 | this.setStatus(this._rollbackStatus); 51 | }, 52 | getRollbackStatus:function(){ 53 | return this._rollbackStatus; 54 | }, 55 | getStatus: function() { 56 | return this._status; 57 | } 58 | }); 59 | 60 | }); -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/components/menu/edit/expand.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 50 | -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/script/protocol/svg.js: -------------------------------------------------------------------------------- 1 | function exportSVG(minder) { 2 | 3 | var paper = minder.getPaper(); 4 | var paperTransform = paper.shapeNode.getAttribute('transform'); 5 | var svgXml; 6 | var $svg; 7 | 8 | var renderContainer = minder.getRenderContainer(); 9 | var renderBox = renderContainer.getRenderBox(); 10 | var transform = renderContainer.getTransform(); 11 | var width = renderBox.width; 12 | var height = renderBox.height; 13 | var padding = 20; 14 | 15 | paper.shapeNode.setAttribute('transform', 'translate(0.5, 0.5)'); 16 | svgXml = paper.container.innerHTML; 17 | console.log(svgXml); 18 | paper.shapeNode.setAttribute('transform', paperTransform); 19 | 20 | $svg = $(svgXml).filter('svg'); 21 | $svg.attr({ 22 | width: width + padding * 2 | 0, 23 | height: height + padding * 2 | 0, 24 | style: 'font-family: Arial, "Microsoft Yahei", "Heiti SC"; background: ' + minder.getStyle('background') 25 | }); 26 | $svg[0].setAttribute('viewBox', [renderBox.x - padding | 0, 27 | renderBox.y - padding | 0, 28 | width + padding * 2 | 0, 29 | height + padding * 2 | 0 30 | ].join(' ')); 31 | 32 | svgXml = $('
').append($svg).html(); 33 | svgXml = $('
').append($svg).html(); 34 | svgXml = svgXml.replace(/ /g, ' '); 35 | 36 | var blob = new Blob([svgXml], { 37 | type: 'image/svg+xml' 38 | }); 39 | 40 | var DOMURL = window.URL || window.webkitURL || window; 41 | var svgUrl = DOMURL.createObjectURL(blob); 42 | 43 | var mind = editor.minder.exportJson(); 44 | downloadSVG(svgUrl, mind.root.data.text); 45 | } 46 | 47 | function downloadSVG(fileURI, fileName) { 48 | try { 49 | const link = document.createElement('a'); 50 | link.href = fileURI; 51 | link.download = `${fileName}.svg`; 52 | document.body.appendChild(link); 53 | link.click(); 54 | document.body.removeChild(link); 55 | } catch (err) { 56 | alert(err); 57 | } 58 | } 59 | 60 | export { 61 | exportSVG 62 | } 63 | -------------------------------------------------------------------------------- /kityminder-core/src/connect/poly.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview 3 | * 4 | * 提供折线相连的方法 5 | * 6 | * @author: techird 7 | * @copyright: Baidu FEX, 2014 8 | */ 9 | 10 | define(function(require, exports, module) { 11 | var kity = require('../core/kity'); 12 | var connect = require('../core/connect'); 13 | 14 | connect.register('poly', function(node, parent, connection, width) { 15 | 16 | // 连线起点和终点 17 | var po = parent.getLayoutVertexOut(), 18 | pi = node.getLayoutVertexIn(); 19 | 20 | // 连线矢量和方向 21 | var v = parent.getLayoutVectorOut().normalize(); 22 | 23 | var r = Math.round; 24 | var abs = Math.abs; 25 | 26 | var pathData = []; 27 | pathData.push('M', r(po.x), r(po.y)); 28 | 29 | switch (true) { 30 | case abs(v.x) > abs(v.y) && v.x < 0: 31 | // left 32 | pathData.push('h', -parent.getStyle('margin-left')); 33 | pathData.push('v', pi.y - po.y); 34 | pathData.push('H', pi.x); 35 | break; 36 | 37 | case abs(v.x) > abs(v.y) && v.x >= 0: 38 | // right 39 | pathData.push('h', parent.getStyle('margin-right')); 40 | pathData.push('v', pi.y - po.y); 41 | pathData.push('H', pi.x); 42 | break; 43 | 44 | case abs(v.x) <= abs(v.y) && v.y < 0: 45 | // top 46 | pathData.push('v', -parent.getStyle('margin-top')); 47 | pathData.push('h', pi.x - po.x); 48 | pathData.push('V', pi.y); 49 | break; 50 | 51 | case abs(v.x) <= abs(v.y) && v.y >= 0: 52 | // bottom 53 | pathData.push('v', parent.getStyle('margin-bottom')); 54 | pathData.push('h', pi.x - po.x); 55 | pathData.push('V', pi.y); 56 | break; 57 | 58 | } 59 | 60 | connection.setMarker(null); 61 | connection.setPathData(pathData); 62 | }); 63 | }); -------------------------------------------------------------------------------- /hotbox/hotbox.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["less/hotbox.less"],"names":[],"mappings":"AACA;EACI,oBAAoB,oBAAoB,mBAAmB,iCAA3D;EACA,kBAAA;EACA,OAAA;EACA,MAAA;EACA,iBAAA;;AALJ,OAMI;EACI,kBAAA;EACA,iBAAA;EACA,aAAA;;AATR,OAMI,OAII,QACI;AAXZ,OAMI,OAIa,MACL;EACI,kBAAA;EACA,WAAA;EACA,YAAA;EACA,kBAAA;EACA,iBAAA;EACA,mBAAA;EACA,uCAAA;;AAlBhB,OAMI,OAII,QAUI;AApBZ,OAMI,OAIa,MAUL;AApBZ,OAMI,OAII,QAUY;AApBpB,OAMI,OAIa,MAUG;EACJ,cAAA;EACA,kBAAA;EACA,kBAAA;EACA,sBAAA;;AAxBhB,OAMI,OAII,QAgBI;AA1BZ,OAMI,OAIa,MAgBL;EACI,eAAA;EACA,gBAAA;EACA,YAAA;EACA,mBAAA;EACA,gBAAA;;AA/BhB,OAMI,OAII,QAuBI;AAjCZ,OAMI,OAIa,MAuBL;EACI,eAAA;EACA,WAAA;;AAnChB,OAMI,OAgCI;EACI,kBAAA;EACA,WAAA;EACA,UAAA;EACA,qCAAA;EACA,mBAAA;EACA,uBAAA;;AA5CZ,OAMI,OAwCI;AA9CR,OAMI,OAwCU;EACF,kBAAA;EACA,mBAAA;;AAhDZ,OAMI,OAwCI,KAGI;AAjDZ,OAMI,OAwCU,QAGF;EACI,qBAAA;EACA,iBAAA;EACA,cAAA;EACA,mBAAA;EACA,uCAAA;EACA,kBAAA;;AAvDhB,OAMI,OAwCI,KAGI,QAQI;AAzDhB,OAMI,OAwCU,QAGF,QAQI;EACI,eAAA;EACA,iBAAA;EACA,sBAAA;EACA,YAAA;EACA,gBAAA;;AA9DpB,OAMI,OAwCI,KAGI,QAeI;AAhEhB,OAMI,OAwCU,QAGF,QAeI;EACI,eAAA;EACA,iBAAA;EACA,sBAAA;EACA,WAAA;EACA,gBAAA;;AACA,OAhEhB,OAwCI,KAGI,QAeI,KAMK;AAAD,OAhEhB,OAwCU,QAGF,QAeI,KAMK;EACG,SAAS,GAAT;;AAEJ,OAnEhB,OAwCI,KAGI,QAeI,KASK;AAAD,OAnEhB,OAwCU,QAGF,QAeI,KASK;EACG,SAAS,GAAT;;AA1ExB,OAMI,OAyEI;EACI,mBAAA;EACA,gBAAA;EACA,eAAA;;AAlFZ,OAMI,OAyEI,QAKI;AApFZ,OAMI,OAyEI,QAKU;EACF,YAAA;;AArFhB,OAMI,OAkFI,QAAO;EACH,iBAAA;;AAzFZ,OAMI,OAkFI,QAAO,QAGH;AA3FZ,OAMI,OAkFI,QAAO,QAGG;EACF,UAAA;;AAGJ,OAzFR,OAkFI,QAAO,QAOF;EACG,mBAAA;;AADJ,OAzFR,OAkFI,QAAO,QAOF,MAEG;EACI,YAAA;;AAHR,OAzFR,OAkFI,QAAO,QAOF,MAKG;EACI,cAAA;;AAGR,OAlGR,OAkFI,QAAO,QAgBF;EACG,oCAAA;EACA,mBAAA;;AAFJ,OAlGR,OAkFI,QAAO,QAgBF,SAGG;EACI,YAAA;;AAJR,OAlGR,OAkFI,QAAO,QAgBF,SAMG;EACI,cAAA;;AAGR,OA5GR,OAkFI,QAAO,QA0BF;AAAU,OA5GnB,OAkFI,QAAO,QA0BS;EACR,mBAAA;;AADJ,OA5GR,OAkFI,QAAO,QA0BF,QAEG;AAFO,OA5GnB,OAkFI,QAAO,QA0BS,OAER;EACI,YAAA;;AAHR,OA5GR,OAkFI,QAAO,QA0BF,QAKG;AALO,OA5GnB,OAkFI,QAAO,QA0BS,OAKR;EACI,cAAA;;AAxHpB,OA6HI,OAAM;EACF,cAAA;;AAIR;EACI;IAAK,WAAW,QAAX;;EACL;IAAM,WAAW,UAAX;;EACN;IAAO,WAAW,QAAX;;;AAGX;EACI,kBAAA;EACA,eAAA;EACA,cAAA;EACA,WAAA;EACA,YAAA;EACA,aAAA;EACA,SAAA","file":"hotbox.css"} -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/script/runtime/exports.js: -------------------------------------------------------------------------------- 1 | define(function (require, exports, module) { 2 | var png = require("../protocol/png"); 3 | var svg = require("../protocol/svg"); 4 | var json = require("../protocol/json"); 5 | var plain = require("../protocol/plain"); 6 | var md = require("../protocol/markdown"); 7 | var mm = require("../protocol/freemind"); 8 | 9 | function ExportRuntime() { 10 | var minder = this.minder; 11 | var hotbox = this.hotbox; 12 | var exps = [ 13 | {label: '.json', key: 'j', cmd: exportJson}, 14 | {label: '.png', key: 'p', cmd: exportImage}, 15 | {label: '.svg', key: 's', cmd: exportSVG}, 16 | {label: '.txt', key: 't', cmd: exportTextTree}, 17 | {label: '.md', key: 'm', cmd: exportMarkdown}, 18 | {label: '.mm', key: 'f', cmd: exportFreeMind} 19 | ]; 20 | 21 | var main = hotbox.state('main'); 22 | main.button({ 23 | position: 'top', 24 | label: '导出', 25 | key: 'E', 26 | enable: canExp, 27 | next: 'exp' 28 | }); 29 | 30 | var exp = hotbox.state('exp'); 31 | exps.forEach(item => { 32 | exp.button({ 33 | position: 'ring', 34 | label: item.label, 35 | key: null, 36 | action: item.cmd 37 | }); 38 | }); 39 | 40 | exp.button({ 41 | position: 'center', 42 | label: '取消', 43 | key: 'esc', 44 | next: 'back' 45 | }); 46 | 47 | function canExp() { 48 | return true; 49 | } 50 | 51 | function exportJson(){ 52 | json.exportJson(minder); 53 | } 54 | 55 | function exportImage (){ 56 | png.exportPNGImage(minder); 57 | } 58 | 59 | function exportSVG (){ 60 | svg.exportSVG(minder); 61 | } 62 | 63 | function exportTextTree (){ 64 | plain.exportTextTree(minder); 65 | } 66 | 67 | function exportMarkdown (){ 68 | md.exportMarkdown(minder); 69 | } 70 | 71 | function exportFreeMind (){ 72 | mm.exportFreeMind(minder); 73 | } 74 | } 75 | 76 | return module.exports = ExportRuntime; 77 | }); 78 | -------------------------------------------------------------------------------- /kityminder-core/src/core/utils.js: -------------------------------------------------------------------------------- 1 | define(function(require, exports) { 2 | var kity = require('./kity'); 3 | var uuidMap = {}; 4 | 5 | exports.extend = kity.Utils.extend.bind(kity.Utils); 6 | exports.each = kity.Utils.each.bind(kity.Utils); 7 | 8 | exports.uuid = function(group) { 9 | uuidMap[group] = uuidMap[group] ? uuidMap[group] + 1 : 1; 10 | return group + uuidMap[group]; 11 | }; 12 | 13 | exports.guid = function() { 14 | return (+new Date() * 1e6 + Math.floor(Math.random() * 1e6)).toString(36); 15 | }; 16 | 17 | exports.trim = function(str) { 18 | return str.replace(/(^[ \t\n\r]+)|([ \t\n\r]+$)/g, ''); 19 | }; 20 | 21 | exports.keys = function(plain) { 22 | var keys = []; 23 | for (var key in plain) { 24 | if (plain.hasOwnProperty(key)) { 25 | keys.push(key); 26 | } 27 | } 28 | return keys; 29 | }; 30 | 31 | exports.clone = function(source) { 32 | return JSON.parse(JSON.stringify(source)); 33 | }; 34 | 35 | exports.comparePlainObject = function(a, b) { 36 | return JSON.stringify(a) == JSON.stringify(b); 37 | }; 38 | 39 | exports.encodeHtml = function(str, reg) { 40 | return str ? str.replace(reg || /[&<">'](?:(amp|lt|quot|gt|#39|nbsp);)?/g, function(a, b) { 41 | if (b) { 42 | return a; 43 | } else { 44 | return { 45 | '<': '<', 46 | '&': '&', 47 | '"': '"', 48 | '>': '>', 49 | '\'': ''' 50 | }[a]; 51 | } 52 | }) : ''; 53 | }; 54 | 55 | exports.clearWhiteSpace = function(str) { 56 | return str.replace(/[\u200b\t\r\n]/g, ''); 57 | }; 58 | 59 | exports.each(['String', 'Function', 'Array', 'Number', 'RegExp', 'Object'], function(v) { 60 | var toString = Object.prototype.toString; 61 | exports['is' + v] = function(obj) { 62 | return toString.apply(obj) == '[object ' + v + ']'; 63 | }; 64 | }); 65 | }); 66 | -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/components/menu/edit/progressBox.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 76 | -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/components/menu/edit/sequenceBox.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 82 | -------------------------------------------------------------------------------- /hotbox/src/keycontrol.js: -------------------------------------------------------------------------------- 1 | define(function(require, exports, module) { 2 | 3 | var key = require('./key'); 4 | var FOCUS_CLASS = 'hotbox-focus'; 5 | var RECEIVER_CLASS = 'hotbox-key-receiver'; 6 | 7 | function KeyControl(hotbox) { 8 | var _this = this; 9 | var _receiver; 10 | var _actived = true; 11 | var _receiverIsSelfCreated = false; 12 | var $container = hotbox.$container; 13 | 14 | _createReceiver(); 15 | _bindReceiver(); 16 | _bindContainer(); 17 | _active(); 18 | 19 | function _createReceiver() { 20 | _receiver = document.createElement('input'); 21 | _receiver.classList.add(RECEIVER_CLASS); 22 | $container.appendChild(_receiver); 23 | _receiverIsSelfCreated = true; 24 | } 25 | 26 | function _bindReceiver() { 27 | _receiver.onkeyup = _handle; 28 | _receiver.onkeypress = _handle; 29 | _receiver.onkeydown = _handle; 30 | _receiver.onfocus = _active; 31 | _receiver.onblur = _deactive; 32 | if (_receiverIsSelfCreated) { 33 | _receiver.oninput = function(e) { _receiver.value = null; }; 34 | } 35 | } 36 | 37 | function _bindContainer() { 38 | $container.onmousedown = function(e) { 39 | _active(); 40 | e.preventDefault(); 41 | }; 42 | } 43 | 44 | function _handle(keyEvent) { 45 | if (!_actived) return; 46 | hotbox.dispatch(keyEvent); 47 | } 48 | 49 | function _active() { 50 | _receiver.select(); 51 | _receiver.focus(); 52 | _actived = true; 53 | $container.classList.add(FOCUS_CLASS); 54 | } 55 | 56 | function _deactive() { 57 | _receiver.blur(); 58 | _actived = false; 59 | $container.classList.remove(FOCUS_CLASS); 60 | } 61 | 62 | this.handle = _handle; 63 | this.active = _active; 64 | this.deactive = _deactive; 65 | } 66 | 67 | module.exports = KeyControl; 68 | }); -------------------------------------------------------------------------------- /kityminder-core/src/core/keyreceiver.js: -------------------------------------------------------------------------------- 1 | define(function(require, exports, module) { 2 | var kity = require('./kity'); 3 | var utils = require('./utils'); 4 | var Minder = require('./minder'); 5 | 6 | function listen(element, type, handler) { 7 | type.split(' ').forEach(function(name) { 8 | element.addEventListener(name, handler, false); 9 | }); 10 | } 11 | 12 | Minder.registerInitHook(function(option) { 13 | this.setDefaultOptions({ 14 | enableKeyReceiver: true 15 | }); 16 | if (this.getOption('enableKeyReceiver')) { 17 | this.on('paperrender', function() { 18 | this._initKeyReceiver(); 19 | }); 20 | } 21 | }); 22 | 23 | kity.extendClass(Minder, { 24 | _initKeyReceiver: function() { 25 | 26 | if (this._keyReceiver) return; 27 | 28 | var receiver = this._keyReceiver = document.createElement('input'); 29 | receiver.classList.add('km-receiver'); 30 | 31 | var renderTarget = this._renderTarget; 32 | renderTarget.appendChild(receiver); 33 | 34 | var minder = this; 35 | 36 | listen(receiver, 'keydown keyup keypress copy paste blur focus input', function(e) { 37 | switch (e.type) { 38 | case 'blur': 39 | minder.blur(); 40 | break; 41 | case 'focus': 42 | minder.focus(); 43 | break; 44 | case 'input': 45 | receiver.value = null; 46 | break; 47 | } 48 | minder._firePharse(e); 49 | e.preventDefault(); 50 | }); 51 | 52 | this.on('focus', function() { 53 | receiver.select(); 54 | receiver.focus(); 55 | }); 56 | this.on('blur', function() { 57 | receiver.blur(); 58 | }); 59 | 60 | if (this.isFocused()) { 61 | receiver.select(); 62 | receiver.focus(); 63 | } 64 | } 65 | }); 66 | }); -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/components/menu/view/styleOperation.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 65 | -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/script/protocol/xmind.js: -------------------------------------------------------------------------------- 1 | var priorities = [ 2 | {jp: 1, mp: 'full-1'}, 3 | {jp: 2, mp: 'full-2'}, 4 | {jp: 3, mp: 'full-3'}, 5 | {jp: 4, mp: 'full-4'}, 6 | {jp: 5, mp: 'full-5'}, 7 | {jp: 6, mp: 'full-6'}, 8 | {jp: 7, mp: 'full-7'}, 9 | {jp: 8, mp: 'full-8'} 10 | ]; 11 | var mmVersion = '\n'; 12 | var iconTextPrefix = '\n'; 14 | var nodeCreated = '\n'; 18 | var entityNode = '\n'; 19 | var entityMap = ''; 20 | 21 | function exportXMind(minder) { 22 | var minds = minder.exportJson(); 23 | var mmContent = mmVersion + traverseJson(minds.root) + entityNode + entityMap; 24 | try { 25 | const link = document.createElement('a'); 26 | const blob = new Blob(["\ufeff" + mmContent], { 27 | type: 'text/xml' 28 | }); 29 | link.href = window.URL.createObjectURL(blob); 30 | link.download = `${minds.root.data.text}.mm`; 31 | document.body.appendChild(link); 32 | link.click(); 33 | document.body.removeChild(link); 34 | } catch (err) { 35 | alert(err); 36 | } 37 | } 38 | 39 | function traverseJson(node){ 40 | var result = ""; 41 | if (!node) { 42 | return; 43 | } 44 | result += concatNodes(node); 45 | if (node.children && node.children.length > 0) { 46 | for (var i = 0; i < node.children.length; i++) { 47 | result += traverseJson(node.children[i]); 48 | result += entityNode; 49 | } 50 | } 51 | return result; 52 | } 53 | 54 | function concatNodes(node) { 55 | var result = ""; 56 | var datas = node.data; 57 | result += nodeCreated + datas.created + nodeId + datas.id + nodeText + datas.text + nodeSuffix; 58 | if (datas.priority) { 59 | var mapped = priorities.find(d => { 60 | return d.jp == datas.priority 61 | }); 62 | if (mapped) { 63 | result += iconTextPrefix + mapped.mp + iconTextSuffix; 64 | } 65 | } 66 | return result; 67 | } 68 | 69 | export { 70 | exportXMind 71 | } 72 | -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/script/protocol/freemind.js: -------------------------------------------------------------------------------- 1 | var priorities = [ 2 | {jp: 1, mp: 'full-1'}, 3 | {jp: 2, mp: 'full-2'}, 4 | {jp: 3, mp: 'full-3'}, 5 | {jp: 4, mp: 'full-4'}, 6 | {jp: 5, mp: 'full-5'}, 7 | {jp: 6, mp: 'full-6'}, 8 | {jp: 7, mp: 'full-7'}, 9 | {jp: 8, mp: 'full-8'} 10 | ]; 11 | var mmVersion = '\n'; 12 | var iconTextPrefix = '\n'; 14 | var nodeCreated = '\n'; 18 | var entityNode = '\n'; 19 | var entityMap = ''; 20 | 21 | function exportFreeMind(minder) { 22 | var minds = minder.exportJson(); 23 | var mmContent = mmVersion + traverseJson(minds.root) + entityNode + entityMap; 24 | try { 25 | const link = document.createElement('a'); 26 | const blob = new Blob(["\ufeff" + mmContent], { 27 | type: 'text/xml' 28 | }); 29 | link.href = window.URL.createObjectURL(blob); 30 | link.download = `${minds.root.data.text}.mm`; 31 | document.body.appendChild(link); 32 | link.click(); 33 | document.body.removeChild(link); 34 | } catch (err) { 35 | alert(err); 36 | } 37 | } 38 | 39 | function traverseJson(node){ 40 | var result = ""; 41 | if (!node) { 42 | return; 43 | } 44 | result += concatNodes(node); 45 | if (node.children && node.children.length > 0) { 46 | for (var i = 0; i < node.children.length; i++) { 47 | result += traverseJson(node.children[i]); 48 | result += entityNode; 49 | } 50 | } 51 | return result; 52 | } 53 | 54 | function concatNodes(node) { 55 | var result = ""; 56 | var datas = node.data; 57 | result += nodeCreated + datas.created + nodeId + datas.id + nodeText + datas.text + nodeSuffix; 58 | if (datas.priority) { 59 | var mapped = priorities.find(d => { 60 | return d.jp == datas.priority 61 | }); 62 | if (mapped) { 63 | result += iconTextPrefix + mapped.mp + iconTextSuffix; 64 | } 65 | } 66 | return result; 67 | } 68 | 69 | export { 70 | exportFreeMind 71 | } 72 | -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/components/main/mainEditor.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 74 | 75 | 78 | -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/components/menu/edit/insertBox.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 74 | -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/components/menu/edit/editMenu.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 83 | -------------------------------------------------------------------------------- /kityminder-core/src/layout/mind.js: -------------------------------------------------------------------------------- 1 | define(function(require, exports, module) { 2 | var kity = require('../core/kity'); 3 | var Layout = require('../core/layout'); 4 | var Minder = require('../core/minder'); 5 | 6 | Layout.register('mind', kity.createClass({ 7 | base: Layout, 8 | 9 | doLayout: function(node, children) { 10 | var layout = this; 11 | var half = Math.ceil(node.children.length / 2); 12 | var right = []; 13 | var left = []; 14 | 15 | children.forEach(function(child) { 16 | if (child.getIndex() < half) right.push(child); 17 | else left.push(child); 18 | }); 19 | 20 | var leftLayout = Minder.getLayoutInstance('left'); 21 | var rightLayout = Minder.getLayoutInstance('right'); 22 | 23 | leftLayout.doLayout(node, left); 24 | rightLayout.doLayout(node, right); 25 | 26 | var box = node.getContentBox(); 27 | node.setVertexOut(new kity.Point(box.cx, box.cy)); 28 | node.setLayoutVectorOut(new kity.Vector(0, 0)); 29 | }, 30 | 31 | getOrderHint: function(node) { 32 | var hint = []; 33 | var box = node.getLayoutBox(); 34 | var offset = 5; 35 | 36 | hint.push({ 37 | type: 'up', 38 | node: node, 39 | area: new kity.Box({ 40 | x: box.x, 41 | y: box.top - node.getStyle('margin-top') - offset, 42 | width: box.width, 43 | height: node.getStyle('margin-top') 44 | }), 45 | path: ['M', box.x, box.top - offset, 'L', box.right, box.top - offset] 46 | }); 47 | 48 | hint.push({ 49 | type: 'down', 50 | node: node, 51 | area: new kity.Box({ 52 | x: box.x, 53 | y: box.bottom + offset, 54 | width: box.width, 55 | height: node.getStyle('margin-bottom') 56 | }), 57 | path: ['M', box.x, box.bottom + offset, 'L', box.right, box.bottom + offset] 58 | }); 59 | return hint; 60 | } 61 | })); 62 | }); -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/components/menu/edit/backAndRight.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 69 | 70 | 72 | -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/components/menu/view/mold.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 56 | -------------------------------------------------------------------------------- /kityminder-core/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | KityMinder Example 6 | 7 | 8 | 9 | 10 | 26 | 27 | 28 | 29 | 30 | 53 | 54 | 55 | 56 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /hotbox/README.md: -------------------------------------------------------------------------------- 1 | 热盒 UI 2 | ==== 3 | 4 | 热盒 UI 是一种高效的上下文交互方式,在最大化编辑区域的同时(再也不需要臃肿的工具栏了)允许全键盘操作。 5 | 6 | ![热盒 UI 截图](snap.png) 7 | 8 | ## 使用示例 9 | 10 | ```js 11 | var hotbox = new HotBox('#editor'); 12 | 13 | var main = hotbox.state('main'); 14 | main.button({ 15 | position: 'center', 16 | action: function() { 17 | // 编辑动作 18 | }, 19 | label: '编辑', 20 | key: 'F2', 21 | next: 'idle' 22 | }); 23 | 24 | hotbox.active('main', { x: 300, y: 300 }); 25 | ``` 26 | 27 | ## HotBox 类 28 | 29 | 用于构建热盒 UI。 30 | 31 | ### 构造函数 `HotBox.constructor` 32 | 33 | 直接使用 `new` 关键字创建 HotBox 实例。 34 | 35 | ```js 36 | var hotbox = new HotBox(selector); 37 | ``` 38 | 39 | 构造函数接受一个**「必须」**的参数 `selector`,表示热盒渲染的位置。热盒创建之后的状态是 `idle`,这个状态下热盒是不可见的。 40 | 41 | ### 定义和获取状态 `state()` 方法 42 | 43 | 热盒在某个时刻有且只有一个状态(`state`),默认状态为 `idle`,此时热盒空闲,不会渲染,并且监听着按键,准备着进行定义的动作。用户需要通过 `state()` 方法来定义状态。 44 | 45 | ```js 46 | var main = hotbox.state('main'); 47 | ``` 48 | 49 | 该方法需要指定一个状态名称,返回指定名称的 `HotBoxState` 对象,该对象可以用于进一步定义状态。 50 | 51 | 热盒会自动把 `main` 状态作为主要状态,会在 `idle` 状态下监听 `main` 状态定义的按键。建议用户定义并使用 `main` 状态。 52 | 53 | ### 设置热盒当前状态 `active()` 方法 54 | 55 | 热盒默认在 `idle` 状态上,使用 `active` 方法,使热盒进入指定的状态。 56 | 57 | ```js 58 | hotbox.active('main', {x: 400, y: 400}); 59 | ``` 60 | 61 | 方法的完整签名为 `active(name, position)`。表示让热盒在 `position` 焦点位置渲染指定的状态。`position` 需要有 `x` 和 `y` 属性。 62 | 63 | ## HotBoxState 类 64 | 65 | `HotBoxState` 类用户无法创建,调用 `HotBox` 的 `.state()` 方法时返回。 66 | 67 | ### 为状态添加按钮 `button()` 68 | 69 | 使用 `button()` 方法为状态添加一个按钮。 70 | 71 | ```js 72 | var main = hotbox.state('main'); 73 | main.button({ 74 | label: '编辑', 75 | key: 'F2', 76 | action: function() { /* 执行的动作 */ } 77 | next: 'idle' 78 | }); 79 | ``` 80 | 81 | 下面是 `option` 对象支持的字段: 82 | 83 | 配置 | 类型 | 说明 84 | --- | --- | --- 85 | option.position | `String` | 按钮的位置。允许在以下位置:
`center` - 按钮在圆心处,只能定义一个
`ring` - 按钮在圆环处,能定义多个
`top` - 按钮在上栏,能定义多个
`bottom` - 按钮在下栏,能定义多个 86 | option.label | `String` | 按钮的标签文本 87 | option.key | `String` | 按钮的快捷键 88 | option.render | `Function` | 按钮的渲染器,如果指定,将使用指定的渲染器。如果不指定,将渲染标签。
渲染器需要返回按钮的 HTML 代码。 89 | option.enable | `Function` | 按钮是否可用的查询函数,如果不指定,则按钮始终可用。函数需要返回一个 `bool` 类型的值,来决定按钮是否可用。 90 | option.action | `Function` | 按钮执行的操作 91 | option.next | `string` | 操作执行之后热盒到达的状态。如不指定,默认到达 `idle`。可使用该参数来跳转到多级热盒。可以取值为 `back` 92 | -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/script/runtime/clipboard-mimetype.js: -------------------------------------------------------------------------------- 1 | define(function (require, exports, module) { 2 | function MimeType() { 3 | var SPLITOR = '\uFEFF'; 4 | var MIMETYPE = { 5 | 'application/km': '\uFFFF' 6 | }; 7 | var SIGN = { 8 | '\uFEFF': 'SPLITOR', 9 | '\uFFFF': 'application/km' 10 | }; 11 | 12 | function process(mimetype, text) { 13 | if (!this.isPureText(text)) { 14 | var _mimetype = this.whichMimeType(text); 15 | if (!_mimetype) { 16 | throw new Error('unknow mimetype!'); 17 | }; 18 | text = this.getPureText(text); 19 | }; 20 | if (mimetype === false) { 21 | return text; 22 | }; 23 | return mimetype + SPLITOR + text; 24 | } 25 | 26 | this.registMimeTypeProtocol = function (type, sign) { 27 | if (sign && SIGN[sign]) { 28 | throw new Error('sing has registed!'); 29 | } 30 | if (type && !!MIMETYPE[type]) { 31 | throw new Error('mimetype has registed!'); 32 | }; 33 | SIGN[sign] = type; 34 | MIMETYPE[type] = sign; 35 | } 36 | 37 | this.getMimeTypeProtocol = function (type, text) { 38 | var mimetype = MIMETYPE[type] || false; 39 | 40 | if (text === undefined) { 41 | return process.bind(this, mimetype); 42 | }; 43 | 44 | return process(mimetype, text); 45 | } 46 | 47 | this.getSpitor = function () { 48 | return SPLITOR; 49 | } 50 | 51 | this.getMimeType = function (sign) { 52 | if (sign !== undefined) { 53 | return SIGN[sign] || null; 54 | }; 55 | return MIMETYPE; 56 | } 57 | } 58 | 59 | MimeType.prototype.isPureText = function (text) { 60 | return !(~text.indexOf(this.getSpitor())); 61 | } 62 | 63 | MimeType.prototype.getPureText = function (text) { 64 | if (this.isPureText(text)) { 65 | return text; 66 | }; 67 | return text.split(this.getSpitor())[1]; 68 | } 69 | 70 | MimeType.prototype.whichMimeType = function (text) { 71 | if (this.isPureText(text)) { 72 | return null; 73 | }; 74 | return this.getMimeType(text.split(this.getSpitor())[0]); 75 | } 76 | 77 | function MimeTypeRuntime() { 78 | if (this.minder.supportClipboardEvent && !kity.Browser.gecko) { 79 | this.MimeType = new MimeType(); 80 | }; 81 | } 82 | 83 | return module.exports = MimeTypeRuntime; 84 | }); 85 | -------------------------------------------------------------------------------- /kityminder-core/.jscsrc: -------------------------------------------------------------------------------- 1 | /** 2 | * FEX Style Guide (Javascript) 3 | * 4 | * TODO: 5 | * 6 | * 1. 找不到选项:每行只允许一个语句 7 | * 2. 找不到选项:块状代码需要用大括号括起来 8 | */ 9 | { 10 | // 缩进「MUST」使用 4 个空格 11 | "validateIndentation": 4, 12 | 13 | // 大括号(块状代码)前「MUST」使用空格 14 | "requireSpaceBeforeBlockStatements": true, 15 | 16 | // 下列关键字「MUST」使用空格 17 | "requireSpaceAfterKeywords": ["if", "else", "for", "while", 18 | "do", "try", "catch", "finally" 19 | ], 20 | 21 | // `,` 和 `;` 前面不允许「MUST NOT」使用空格。 22 | "requireLeftStickedOperators": [",", ";"], 23 | 24 | // 二元运算符前后「MUST」使用空格 25 | "requireSpaceBeforeBinaryOperators": [ 26 | "+", 27 | "-", 28 | "*", 29 | "/", 30 | "=", 31 | "==", 32 | "===", 33 | "!=", 34 | "!==", 35 | "|", 36 | "||", 37 | "&", 38 | "&&" 39 | ], 40 | "requireSpaceAfterBinaryOperators": [ 41 | "+", 42 | "-", 43 | "*", 44 | "/", 45 | "=", 46 | "==", 47 | "===", 48 | "!=", 49 | "!==", 50 | "|", 51 | "||", 52 | "&", 53 | "&&", 54 | ":" 55 | ], 56 | 57 | // 一元运算符与操作对象间「MUST NOT」使用空格 58 | "disallowSpaceAfterPrefixUnaryOperators": ["++", "--", "+", "-", "~", "!"], 59 | 60 | // 函数参数小括号前「MUST NOT」使用空格 61 | "disallowSpacesInFunctionExpression": { 62 | "beforeOpeningRoundBrace": true 63 | }, 64 | 65 | // 小括号里面「MUST NOT」使用空格 66 | "disallowSpacesInsideParentheses": true, 67 | 68 | // 行尾「MUST NOT」使用空格 69 | "disallowTrailingWhitespace": true, 70 | 71 | // 每行「MUST NOT」超过 120 个字符 72 | "maximumLineLength": 120, 73 | 74 | // 一下操作符「MUST NOT」放在一行的最前面,需要放在上一行的后面 75 | "requireOperatorBeforeLineBreak": [ 76 | "?", 77 | "+", 78 | "-", 79 | "/", 80 | "*", 81 | "=", 82 | "==", 83 | "===", 84 | "!=", 85 | "!==", 86 | ">", 87 | ">=", 88 | "<", 89 | "<=", 90 | ",", 91 | ";", 92 | "&&", 93 | "&", 94 | "||", 95 | "|" 96 | ], 97 | 98 | // 字符串统一「MUST」使用单引号 99 | "validateQuoteMarks": "'", 100 | 101 | // 「MUST NOT」使用多行字符串 102 | "disallowMultipleLineStrings": true 103 | } -------------------------------------------------------------------------------- /kityminder-core/src/core/paper.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview 3 | * 4 | * 初始化渲染容器 5 | * 6 | * @author: techird 7 | * @copyright: Baidu FEX, 2014 8 | */ 9 | define(function(require, exports, module) { 10 | var kity = require('./kity'); 11 | var utils = require('./utils'); 12 | var Minder = require('./minder'); 13 | 14 | Minder.registerInitHook(function() { 15 | this._initPaper(); 16 | }); 17 | 18 | kity.extendClass(Minder, { 19 | 20 | _initPaper: function() { 21 | 22 | this._paper = new kity.Paper(); 23 | this._paper._minder = this; 24 | this._paper.getNode().ondragstart = function(e) { 25 | e.preventDefault(); 26 | }; 27 | this._paper.shapeNode.setAttribute('transform', 'translate(0.5, 0.5)'); 28 | 29 | this._addRenderContainer(); 30 | 31 | this.setRoot(this.createNode()); 32 | 33 | if (this._options.renderTo) { 34 | this.renderTo(this._options.renderTo); 35 | } 36 | }, 37 | 38 | _addRenderContainer: function() { 39 | this._rc = new kity.Group().setId(utils.uuid('minder')); 40 | this._paper.addShape(this._rc); 41 | }, 42 | 43 | renderTo: function(target) { 44 | if (typeof(target) == 'string') { 45 | target = document.querySelector(target); 46 | } 47 | if (target) { 48 | if (target.tagName.toLowerCase() == 'script') { 49 | var newTarget = document.createElement('div'); 50 | newTarget.id = target.id; 51 | newTarget.class = target.class; 52 | target.parentNode.insertBefore(newTarget, target); 53 | target.parentNode.removeChild(target); 54 | target = newTarget; 55 | } 56 | target.classList.add('km-view'); 57 | this._paper.renderTo(this._renderTarget = target); 58 | this._bindEvents(); 59 | this.fire('paperrender'); 60 | } 61 | return this; 62 | }, 63 | 64 | getRenderContainer: function() { 65 | return this._rc; 66 | }, 67 | 68 | getPaper: function() { 69 | return this._paper; 70 | }, 71 | 72 | getRenderTarget: function() { 73 | return this._renderTarget; 74 | }, 75 | }); 76 | }); -------------------------------------------------------------------------------- /kityminder-core/dev.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | KityMinder Example 6 | 7 | 8 | 9 | 10 | 26 | 27 | 28 | 29 | 30 | 53 | 54 | 55 | 56 | 57 | 71 | -------------------------------------------------------------------------------- /kityminder-core/src/layout/fish-bone-master.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview 3 | * 4 | * 鱼骨图主骨架布局 5 | * 6 | * @author: techird 7 | * @copyright: Baidu FEX, 2014 8 | */ 9 | 10 | define(function(require, exports, module) { 11 | var kity = require('../core/kity'); 12 | var Layout = require('../core/layout'); 13 | 14 | Layout.register('fish-bone-master', kity.createClass('FishBoneMasterLayout', { 15 | base: Layout, 16 | 17 | doLayout: function(parent, children, round) { 18 | 19 | var upPart = [], 20 | downPart = []; 21 | 22 | var child = children[0]; 23 | var pBox = parent.getContentBox(); 24 | 25 | parent.setVertexOut(new kity.Point(pBox.right, pBox.cy)); 26 | parent.setLayoutVectorOut(new kity.Vector(1, 0)); 27 | 28 | if (!child) return; 29 | 30 | var cBox = child.getContentBox(); 31 | var pMarginRight = parent.getStyle('margin-right'); 32 | var cMarginLeft = child.getStyle('margin-left'); 33 | var cMarginTop = child.getStyle('margin-top'); 34 | var cMarginBottom = child.getStyle('margin-bottom'); 35 | 36 | children.forEach(function(child, index) { 37 | child.setLayoutTransform(new kity.Matrix()); 38 | var cBox = child.getContentBox(); 39 | 40 | if (index % 2) { 41 | downPart.push(child); 42 | child.setVertexIn(new kity.Point(cBox.left, cBox.top)); 43 | child.setLayoutVectorIn(new kity.Vector(1, 1)); 44 | } 45 | else { 46 | upPart.push(child); 47 | child.setVertexIn(new kity.Point(cBox.left, cBox.bottom)); 48 | child.setLayoutVectorIn(new kity.Vector(1, -1)); 49 | } 50 | 51 | }); 52 | 53 | this.stack(upPart, 'x'); 54 | this.stack(downPart, 'x'); 55 | 56 | this.align(upPart, 'bottom'); 57 | this.align(downPart, 'top'); 58 | 59 | var xAdjust = pBox.right + pMarginRight + cMarginLeft; 60 | var yAdjustUp = pBox.cy - cMarginBottom - parent.getStyle('margin-top'); 61 | var yAdjustDown = pBox.cy + cMarginTop + parent.getStyle('margin-bottom'); 62 | 63 | this.move(upPart, xAdjust, yAdjustUp); 64 | this.move(downPart, xAdjust + cMarginLeft, yAdjustDown); 65 | } 66 | })); 67 | 68 | }); -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/script/runtime/node.js: -------------------------------------------------------------------------------- 1 | define(function (require, exports, module) { 2 | function NodeRuntime() { 3 | var runtime = this; 4 | var minder = this.minder; 5 | var hotbox = this.hotbox; 6 | var fsm = this.fsm; 7 | 8 | var main = hotbox.state('main'); 9 | 10 | var buttons = [ 11 | '前移:Alt+Up:ArrangeUp', 12 | '下级:Tab|Insert:AppendChildNode', 13 | '同级:Enter:AppendSiblingNode', 14 | '后移:Alt+Down:ArrangeDown', 15 | '删除:Delete|Backspace:RemoveNode', 16 | '上级:Shift+Tab|Shift+Insert:AppendParentNode' 17 | ]; 18 | 19 | var AppendLock = 0; 20 | 21 | buttons.forEach(function (button) { 22 | var parts = button.split(':'); 23 | var label = parts.shift(); 24 | var key = parts.shift(); 25 | var command = parts.shift(); 26 | main.button({ 27 | position: 'ring', 28 | label: label, 29 | key: key, 30 | action: function () { 31 | if (command.indexOf('Append') === 0) { 32 | AppendLock++; 33 | minder.execCommand(command, '分支主题'); 34 | 35 | function afterAppend() { 36 | if (!--AppendLock) { 37 | runtime.editText(); 38 | } 39 | minder.off('layoutallfinish', afterAppend); 40 | } 41 | minder.on('layoutallfinish', afterAppend); 42 | } else { 43 | minder.execCommand(command); 44 | fsm.jump('normal', 'command-executed'); 45 | } 46 | }, 47 | enable: function () { 48 | return minder.queryCommandState(command) != -1; 49 | } 50 | }); 51 | }); 52 | 53 | main.button({ 54 | position: 'ring', 55 | key: '/', 56 | action: function () { 57 | if (!minder.queryCommandState('expand')) { 58 | minder.execCommand('expand'); 59 | } else if (!minder.queryCommandState('collapse')) { 60 | minder.execCommand('collapse'); 61 | } 62 | }, 63 | enable: function () { 64 | return minder.queryCommandState('expand') != -1 || minder.queryCommandState('collapse') != -1; 65 | }, 66 | beforeShow: function () { 67 | if (!minder.queryCommandState('expand')) { 68 | this.$button.children[0].innerHTML = '展开'; 69 | } else { 70 | this.$button.children[0].innerHTML = '收起'; 71 | } 72 | } 73 | }) 74 | } 75 | 76 | return module.exports = NodeRuntime; 77 | }); 78 | -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/components/menu/edit/noteBox.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 91 | 92 | 94 | -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/store/actions.js: -------------------------------------------------------------------------------- 1 | export const actions = { 2 | changeCount: ({ 3 | commit 4 | }) => commit('changeCount'), 5 | 6 | increment: ({ 7 | commit 8 | }) => commit('increment'), 9 | 10 | decrement: ({ 11 | commit 12 | }) => commit('decrement'), 13 | 14 | incrementIfOdd({ 15 | commit, 16 | state 17 | }) { 18 | if ((state.count + 1) % 2 === 0) { 19 | commit('increment') 20 | } 21 | }, 22 | 23 | incrementAsync({ 24 | commit 25 | }) { 26 | return new Promise((resolve, reject) => { 27 | setTimeout(() => { 28 | commit('increment') 29 | resolve() 30 | }, 1000) 31 | }) 32 | }, 33 | 34 | setConfig: ({ 35 | commit 36 | }) => commit('setConfig'), 37 | 38 | registerEvent: ({ 39 | commit 40 | }) => commit('registerEvent', callback), 41 | 42 | executeCallback({ 43 | commit, 44 | state 45 | }) { 46 | state.callbackQueue.forEach(function (ele) { 47 | ele.apply(this, arguments); 48 | }) 49 | }, 50 | 51 | isQuotaExceeded(e) { 52 | var quotaExceeded = false; 53 | if (e) { 54 | if (e.code) { 55 | switch (e.code) { 56 | case 22: 57 | quotaExceeded = true; 58 | break; 59 | case 1014: 60 | // Firefox 61 | if (e.name === 'NS_ERROR_DOM_QUOTA_REACHED') { 62 | quotaExceeded = true; 63 | } 64 | break; 65 | } 66 | } else if (e.number === -2147024882) { 67 | // Internet Explorer 8 68 | quotaExceeded = true; 69 | } 70 | } 71 | return quotaExceeded; 72 | index 73 | }, 74 | 75 | getMemory({ 76 | commit, 77 | state 78 | }, key) { 79 | var value = window.localStorage.getItem(key); 80 | var result = null || JSON.parse(value) 81 | console.log('action:' + result); 82 | return result; 83 | }, 84 | 85 | setMemory({ 86 | commit, 87 | state 88 | }, data) { 89 | try { 90 | window.localStorage.setItem(data.key, JSON.stringify(data.value)); 91 | return true; 92 | } catch (e) { 93 | if (this.isQuotaExceeded(e)) { 94 | return false; 95 | } 96 | } 97 | }, 98 | 99 | removeMemory(key) { 100 | var value = window.localStorage.getItem(key); 101 | window.localStorage.removeItem(key); 102 | return value; 103 | }, 104 | 105 | clearMemory() { 106 | window.localStorage.clear(); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /kityminder-core/src/layout/fish-bone-slave.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview 3 | * 4 | * 5 | * 6 | * @author: techird 7 | * @copyright: Baidu FEX, 2014 8 | */ 9 | 10 | define(function(require, exports, module) { 11 | var kity = require('../core/kity'); 12 | var Layout = require('../core/layout'); 13 | 14 | Layout.register('fish-bone-slave', kity.createClass('FishBoneSlaveLayout', { 15 | base: Layout, 16 | 17 | doLayout: function (parent, children, round) { 18 | 19 | var layout = this; 20 | var abs = Math.abs; 21 | var GOLD_CUT = 1 - 0.618; 22 | 23 | var pBox = parent.getContentBox(); 24 | var vi = parent.getLayoutVectorIn(); 25 | 26 | parent.setLayoutVectorOut(vi); 27 | 28 | var goldX = pBox.left + pBox.width * GOLD_CUT; 29 | var pout = new kity.Point(goldX, vi.y > 0 ? pBox.bottom : pBox.top); 30 | parent.setVertexOut(pout); 31 | 32 | var child = children[0]; 33 | if (!child) return; 34 | 35 | var cBox = child.getContentBox(); 36 | 37 | children.forEach(function(child, index) { 38 | child.setLayoutTransform(new kity.Matrix()); 39 | child.setLayoutVectorIn(new kity.Vector(1, 0)); 40 | child.setVertexIn(new kity.Point(cBox.left, cBox.cy)); 41 | }); 42 | 43 | this.stack(children, 'y'); 44 | this.align(children, 'left'); 45 | 46 | var xAdjust = 0, yAdjust = 0; 47 | xAdjust += pout.x; 48 | 49 | if (parent.getLayoutVectorOut().y < 0) { 50 | yAdjust -= this.getTreeBox(children).bottom; 51 | yAdjust += parent.getContentBox().top; 52 | yAdjust -= parent.getStyle('margin-top'); 53 | yAdjust -= child.getStyle('margin-bottom'); 54 | } else { 55 | yAdjust += parent.getContentBox().bottom; 56 | yAdjust += parent.getStyle('margin-bottom'); 57 | yAdjust += child.getStyle('margin-top'); 58 | } 59 | 60 | this.move(children, xAdjust, yAdjust); 61 | 62 | if (round == 2) { 63 | children.forEach(function(child) { 64 | var m = child.getLayoutTransform(); 65 | var cbox = child.getContentBox(); 66 | var pin = m.transformPoint(new kity.Point(cbox.left, 0)); 67 | layout.move([child], abs(pin.y - pout.y), 0); 68 | }); 69 | } 70 | } 71 | })); 72 | }); -------------------------------------------------------------------------------- /kityminder-core/src/module/layout.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview 3 | * 4 | * 布局模块 5 | * 6 | * @author: techird 7 | * @copyright: Baidu FEX, 2014 8 | */ 9 | 10 | define(function(require, exports, module) { 11 | var kity = require('../core/kity'); 12 | var Command = require('../core/command'); 13 | var Module = require('../core/module'); 14 | 15 | /** 16 | * @command Layout 17 | * @description 设置选中节点的布局 18 | * 允许使用的布局可以使用 `kityminder.Minder.getLayoutList()` 查询 19 | * @param {string} name 布局的名称,设置为 null 则使用继承或默认的布局 20 | * @state 21 | * 0: 当前有选中的节点 22 | * -1: 当前没有选中的节点 23 | * @return 返回首个选中节点的布局名称 24 | */ 25 | var LayoutCommand = kity.createClass('LayoutCommand', { 26 | base: Command, 27 | 28 | execute: function(minder, name) { 29 | var nodes = minder.getSelectedNodes(); 30 | nodes.forEach(function(node) { 31 | node.layout(name); 32 | }); 33 | }, 34 | 35 | queryValue: function(minder) { 36 | var node = minder.getSelectedNode(); 37 | if (node) { 38 | return node.getData('layout'); 39 | } 40 | }, 41 | 42 | queryState: function(minder) { 43 | return minder.getSelectedNode() ? 0 : -1; 44 | } 45 | }); 46 | 47 | /** 48 | * @command ResetLayout 49 | * @description 重设选中节点的布局,如果当前没有选中的节点,重设整个脑图的布局 50 | * @state 51 | * 0: 始终可用 52 | * @return 返回首个选中节点的布局名称 53 | */ 54 | var ResetLayoutCommand = kity.createClass('ResetLayoutCommand', { 55 | base: Command, 56 | 57 | execute: function(minder) { 58 | var nodes = minder.getSelectedNodes(); 59 | 60 | if (!nodes.length) nodes = [minder.getRoot()]; 61 | 62 | nodes.forEach(function(node) { 63 | node.traverse(function(child) { 64 | child.resetLayoutOffset(); 65 | if (!child.isRoot()) { 66 | child.setData('layout', null); 67 | } 68 | }); 69 | }); 70 | minder.layout(300); 71 | }, 72 | 73 | enableReadOnly: true 74 | }); 75 | 76 | Module.register('LayoutModule', { 77 | commands: { 78 | 'layout': LayoutCommand, 79 | 'resetlayout': ResetLayoutCommand 80 | }, 81 | contextmenu: [{ 82 | command: 'resetlayout' 83 | }, { 84 | divider: true 85 | }], 86 | 87 | commandShortcutKeys: { 88 | 'resetlayout': 'Ctrl+Shift+L' 89 | } 90 | }); 91 | 92 | }); -------------------------------------------------------------------------------- /kityminder-core/src/theme/fresh.js: -------------------------------------------------------------------------------- 1 | define(function(require, exports, module) { 2 | var kity = require('../core/kity'); 3 | var theme = require('../core/theme'); 4 | 5 | function hsl(h, s, l) { 6 | return kity.Color.createHSL(h, s, l); 7 | } 8 | 9 | function generate(h, compat) { 10 | return { 11 | 'background': '#fbfbfb', 12 | 13 | 'root-color': 'white', 14 | 'root-background': hsl(h, 37, 60), 15 | 'root-stroke': hsl(h, 37, 60), 16 | 'root-font-size': 16, 17 | 'root-padding': compat ? [6, 12] : [12, 24], 18 | 'root-margin': compat ? 10 : [30, 100], 19 | 'root-radius': 5, 20 | 'root-space': 10, 21 | 22 | 'main-color': 'black', 23 | 'main-background': hsl(h, 33, 95), 24 | 'main-stroke': hsl(h, 37, 60), 25 | 'main-stroke-width': 1, 26 | 'main-font-size': 14, 27 | 'main-padding': [6, 20], 28 | 'main-margin': compat ? 8 : 20, 29 | 'main-radius': 3, 30 | 'main-space': 5, 31 | 32 | 'sub-color': 'black', 33 | 'sub-background': 'transparent', 34 | 'sub-stroke': 'none', 35 | 'sub-font-size': 12, 36 | 'sub-padding': compat ? [3, 5] : [5, 10], 37 | 'sub-margin': compat ? [4, 8] : [15, 20], 38 | 'sub-radius': 5, 39 | 'sub-space': 5, 40 | 41 | 'connect-color': hsl(h, 37, 60), 42 | 'connect-width': 1, 43 | 'connect-radius': 5, 44 | 45 | 'selected-stroke': hsl(h, 26, 30), 46 | 'selected-stroke-width': '3', 47 | 'blur-selected-stroke': hsl(h, 10, 60), 48 | 49 | 'marquee-background': hsl(h, 100, 80).set('a', 0.1), 50 | 'marquee-stroke': hsl(h, 37, 60), 51 | 52 | 'drop-hint-color': hsl(h, 26, 35), 53 | 'drop-hint-width': 5, 54 | 55 | 'order-hint-area-color': hsl(h, 100, 30).set('a', 0.5), 56 | 'order-hint-path-color': hsl(h, 100, 25), 57 | 'order-hint-path-width': 1, 58 | 59 | 'text-selection-color': hsl(h, 100, 20), 60 | 'line-height':1.5 61 | }; 62 | } 63 | 64 | var plans = { 65 | red: 0, 66 | soil: 25, 67 | green: 122, 68 | blue: 204, 69 | purple: 246, 70 | pink: 334 71 | }; 72 | var name; 73 | for (name in plans) { 74 | theme.register('fresh-' + name, generate(plans[name])); 75 | theme.register('fresh-' + name + '-compat', generate(plans[name], true)); 76 | } 77 | 78 | }); -------------------------------------------------------------------------------- /kityminder-core/src/core/compatibility.js: -------------------------------------------------------------------------------- 1 | define(function(require, exports, module) { 2 | var utils = require('./utils'); 3 | 4 | function compatibility(json) { 5 | 6 | var version = json.version || (json.root ? '1.4.0' : '1.1.3'); 7 | 8 | switch (version) { 9 | case '1.1.3': 10 | c_113_120(json); 11 | /* falls through */ 12 | case '1.2.0': 13 | case '1.2.1': 14 | c_120_130(json); 15 | /* falls through */ 16 | case '1.3.0': 17 | case '1.3.1': 18 | case '1.3.2': 19 | case '1.3.3': 20 | case '1.3.4': 21 | case '1.3.5': 22 | /* falls through */ 23 | c_130_140(json); 24 | } 25 | return json; 26 | } 27 | 28 | function traverse(node, fn) { 29 | fn(node); 30 | if (node.children) node.children.forEach(function(child) { 31 | traverse(child, fn); 32 | }); 33 | } 34 | 35 | /* 脑图数据升级 */ 36 | function c_120_130(json) { 37 | traverse(json, function(node) { 38 | var data = node.data; 39 | delete data.layout_bottom_offset; 40 | delete data.layout_default_offset; 41 | delete data.layout_filetree_offset; 42 | }); 43 | } 44 | 45 | /** 46 | * 脑图数据升级 47 | * v1.1.3 => v1.2.0 48 | * */ 49 | function c_113_120(json) { 50 | // 原本的布局风格 51 | var ocs = json.data.currentstyle; 52 | delete json.data.currentstyle; 53 | 54 | // 为 1.2 选择模板,同时保留老版本文件的皮肤 55 | if (ocs == 'bottom') { 56 | json.template = 'structure'; 57 | json.theme = 'snow'; 58 | } else if (ocs == 'default') { 59 | json.template = 'default'; 60 | json.theme = 'classic'; 61 | } 62 | 63 | traverse(json, function(node) { 64 | var data = node.data; 65 | 66 | // 升级优先级、进度图标 67 | if ('PriorityIcon' in data) { 68 | data.priority = data.PriorityIcon; 69 | delete data.PriorityIcon; 70 | } 71 | if ('ProgressIcon' in data) { 72 | data.progress = 1 + ((data.ProgressIcon - 1) << 1); 73 | delete data.ProgressIcon; 74 | } 75 | 76 | // 删除过时属性 77 | delete data.point; 78 | delete data.layout; 79 | }); 80 | } 81 | 82 | function c_130_140(json) { 83 | json.root = { 84 | data: json.data, 85 | children: json.children 86 | }; 87 | delete json.data; 88 | delete json.children; 89 | } 90 | 91 | return compatibility; 92 | }); -------------------------------------------------------------------------------- /kityminder-core/src/layout/tianpan.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview 3 | * 4 | * 天盘模板 5 | * 6 | * @author: along 7 | * @copyright: bpd729@163.com, 2015 8 | */ 9 | define(function(require, exports, module) { 10 | var kity = require('../core/kity'); 11 | var Layout = require('../core/layout'); 12 | var Minder = require('../core/minder'); 13 | 14 | Layout.register('tianpan', kity.createClass({ 15 | base: Layout, 16 | 17 | doLayout: function (parent, children) { 18 | if (children.length == 0) return; 19 | 20 | var layout = this; 21 | var pbox = parent.getContentBox(); 22 | 23 | var x, y,box; 24 | var _theta = 5; 25 | var _r = Math.max(pbox.width, 50); 26 | children.forEach(function (child, index) { 27 | child.setLayoutTransform(new kity.Matrix()); 28 | box = layout.getTreeBox(child); 29 | _r = Math.max(Math.max(box.width, box.height), _r); 30 | }) 31 | _r = _r / 1.5 / Math.PI; 32 | 33 | children.forEach(function (child, index) { 34 | x = _r * (Math.cos(_theta) + Math.sin(_theta) * _theta); 35 | y = _r * (Math.sin(_theta) - Math.cos(_theta) * _theta); 36 | 37 | _theta += (0.9 - index * 0.02); 38 | child.setLayoutVectorIn(new kity.Vector(1, 0)); 39 | child.setVertexIn(new kity.Point(pbox.cx, pbox.cy)); 40 | child.setLayoutTransform(new kity.Matrix()); 41 | layout.move([child], x, y); 42 | }); 43 | }, 44 | 45 | getOrderHint: function (node) { 46 | var hint = []; 47 | var box = node.getLayoutBox(); 48 | var offset = 5; 49 | 50 | hint.push({ 51 | type: 'up', 52 | node: node, 53 | area: { 54 | x: box.x, 55 | y: box.top - node.getStyle('margin-top') - offset, 56 | width: box.width, 57 | height: node.getStyle('margin-top') 58 | }, 59 | path: ['M', box.x, box.top - offset, 'L', box.right, box.top - offset] 60 | }); 61 | 62 | hint.push({ 63 | type: 'down', 64 | node: node, 65 | area: { 66 | x: box.x, 67 | y: box.bottom + offset, 68 | width: box.width, 69 | height: node.getStyle('margin-bottom') 70 | }, 71 | path: ['M', box.x, box.bottom + offset, 'L', box.right, box.bottom + offset] 72 | }); 73 | return hint; 74 | } 75 | })); 76 | }); -------------------------------------------------------------------------------- /kityminder-core/src/connect/arc_tp.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * 圆弧连线 4 | * 5 | * @author: along 6 | * @copyright: bpd729@163.com , 2015 7 | */ 8 | define(function(require, exports, module) { 9 | var kity = require('../core/kity'); 10 | var connect = require('../core/connect'); 11 | 12 | var connectMarker = new kity.Marker().pipe(function () { 13 | var r = 7; 14 | var dot = new kity.Circle(r - 1); 15 | this.addShape(dot); 16 | this.setRef(r - 1, 0).setViewBox(-r, -r, r + r, r + r).setWidth(r).setHeight(r); 17 | this.dot = dot; 18 | this.node.setAttribute('markerUnits', 'userSpaceOnUse'); 19 | }); 20 | 21 | /** 22 | * 天盘图连线除了连接当前节点和前一个节点外, 还需要渲染当前节点和后一个节点的连接, 防止样式上的断线 23 | * 这是天盘图与其余的模板不同的地方 24 | */ 25 | connect.register('arc_tp', function (node, parent, connection, width, color) { 26 | var end_box = node.getLayoutBox(), 27 | start_box = parent.getLayoutBox(); 28 | 29 | var index = node.getIndex(); 30 | var nextNode = parent.getChildren()[index + 1]; 31 | 32 | 33 | if (node.getIndex() > 0) { 34 | start_box = parent.getChildren()[index - 1].getLayoutBox(); 35 | } 36 | 37 | 38 | var start, end, vector; 39 | var abs = Math.abs; 40 | var pathData = []; 41 | var side = end_box.x > start_box.x ? 'right' : 'left'; 42 | 43 | node.getMinder().getPaper().addResource(connectMarker); 44 | 45 | 46 | start = new kity.Point(start_box.cx, start_box.cy); 47 | end = new kity.Point(end_box.cx, end_box.cy); 48 | 49 | var jl = Math.sqrt(Math.pow((start.x - end.x), 2) + Math.pow((start.y - end.y), 2)); //两圆中心点距离 50 | 51 | jl = node.getIndex() == 0 ? jl * 0.4 : jl; 52 | 53 | 54 | vector = kity.Vector.fromPoints(start, end); 55 | pathData.push('M', start); 56 | pathData.push('A', jl, jl, 0, 0, 1, end); 57 | 58 | 59 | connection.setMarker(connectMarker); 60 | connectMarker.dot.fill(color); 61 | connection.setPathData(pathData); 62 | 63 | 64 | // 设置下一个的节点的连接线 65 | if (nextNode && nextNode.getConnection()) { 66 | var nextConnection = nextNode.getConnection(); 67 | var next_end_box = nextNode.getLayoutBox(); 68 | var next_end = new kity.Point(next_end_box.cx, next_end_box.cy); 69 | 70 | var jl2 = Math.sqrt(Math.pow((end.x - next_end.x), 2) + Math.pow((end.y - next_end.y), 2)); //两圆中心点距离 71 | 72 | pathData = []; 73 | 74 | pathData.push('M', end); 75 | pathData.push('A', jl2, jl2, 0, 0, 1, next_end); 76 | 77 | nextConnection.setMarker(connectMarker); 78 | connectMarker.dot.fill(color); 79 | 80 | nextConnection.setPathData(pathData); 81 | 82 | } 83 | 84 | }); 85 | }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-testcase-minder-editor", 3 | "version": "0.3.14", 4 | "private": false, 5 | "author": "chenhengjie", 6 | "main": "lib/VueTestcaseMinderEditor.umd.min.js", 7 | "files": [ 8 | "lib/VueTestcaseMinderEditor.umd.min.js", 9 | "lib/VueTestcaseMinderEditor.css", 10 | "lib/fonts/*", 11 | "lib/img/*" 12 | ], 13 | "scripts": { 14 | "serve": "vue-cli-service serve", 15 | "build": "vue-cli-service build", 16 | "lint": "vue-cli-service lint", 17 | "lib": "vue-cli-service build --target lib --name VueTestcaseMinderEditor --dest lib packages/VueTestcaseMinderEditor/index.js && mv lib/VueTestcaseMinderEditor.umd.min.js lib/VueTestcaseMinderEditor.umd.min.js.bak && babel --plugins transform-object-rest-spread lib/VueTestcaseMinderEditor.umd.min.js.bak > lib/VueTestcaseMinderEditor.umd.min.js", 18 | "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s" 19 | }, 20 | "dependencies": { 21 | "codemirror": "^5.24.2", 22 | "color-picker": "0.0.1", 23 | "core-js": "^3.6.5", 24 | "element-ui": "^2.12.0", 25 | "jquery": "^3.2.0", 26 | "json-diff": "^0.5.4", 27 | "kity": "^2.0.4", 28 | "marked": "^0.3.6", 29 | "screenfull": "^5.0.2", 30 | "vue": "^2.6.11", 31 | "vue-markdown": "^2.2.4", 32 | "vue-router": "^3.2.0", 33 | "vuex": "^3.1.0" 34 | }, 35 | "devDependencies": { 36 | "@vue/cli-plugin-babel": "~4.5.0", 37 | "@vue/cli-plugin-eslint": "~4.5.0", 38 | "@vue/cli-plugin-router": "~4.5.0", 39 | "@vue/cli-service": "~4.5.0", 40 | "@vue/eslint-config-standard": "^5.1.2", 41 | "babel-cli": "^6.26.0", 42 | "babel-eslint": "^10.1.0", 43 | "babel-helper-vue-jsx-merge-props": "^2.0.3", 44 | "babel-plugin-syntax-dynamic-import": "^6.18.0", 45 | "babel-plugin-syntax-jsx": "^6.18.0", 46 | "babel-plugin-transform-imports": "1.5.0", 47 | "babel-plugin-transform-object-rest-spread": "^6.26.0", 48 | "babel-plugin-transform-vue-jsx": "^3.7.0", 49 | "babel-polyfill": "^6.26.0", 50 | "babel-preset-env": "^1.3.2", 51 | "babel-preset-es2015": "^6.24.1", 52 | "babel-preset-stage-2": "^6.24.1", 53 | "babel-register": "^6.26.0", 54 | "conventional-changelog-cli": "^2.1.1", 55 | "eslint": "^6.7.2", 56 | "eslint-plugin-import": "^2.20.2", 57 | "eslint-plugin-node": "^11.1.0", 58 | "eslint-plugin-promise": "^4.2.1", 59 | "eslint-plugin-standard": "^4.0.0", 60 | "eslint-plugin-vue": "^6.2.2", 61 | "less": "^3.12.2", 62 | "less-loader": "^7.1.0", 63 | "node-sass": "^4.9.0", 64 | "sass-loader": "^7.0.1", 65 | "vue-template-compiler": "^2.6.11" 66 | }, 67 | "eslintConfig": { 68 | "root": true, 69 | "env": { 70 | "node": true 71 | }, 72 | "extends": [ 73 | "plugin:vue/essential", 74 | "@vue/standard" 75 | ], 76 | "parserOptions": { 77 | "parser": "babel-eslint" 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /kityminder-core/src/core/keymap.js: -------------------------------------------------------------------------------- 1 | define(function(require, exports, module) { 2 | var keymap = { 3 | 'Backspace': 8, 4 | 'Tab': 9, 5 | 'Enter': 13, 6 | 7 | 'Shift': 16, 8 | 'Control': 17, 9 | 'Alt': 18, 10 | 'CapsLock': 20, 11 | 12 | 'Esc': 27, 13 | 14 | 'Spacebar': 32, 15 | 16 | 'PageUp': 33, 17 | 'PageDown': 34, 18 | 'End': 35, 19 | 'Home': 36, 20 | 21 | 'Insert': 45, 22 | 23 | 'Left': 37, 24 | 'Up': 38, 25 | 'Right': 39, 26 | 'Down': 40, 27 | 28 | 'direction': { 29 | 37: 1, 30 | 38: 1, 31 | 39: 1, 32 | 40: 1 33 | }, 34 | 35 | 'Del': 46, 36 | 37 | 'NumLock': 144, 38 | 39 | 'Cmd': 91, 40 | 'CmdFF': 224, 41 | 'F1': 112, 42 | 'F2': 113, 43 | 'F3': 114, 44 | 'F4': 115, 45 | 'F5': 116, 46 | 'F6': 117, 47 | 'F7': 118, 48 | 'F8': 119, 49 | 'F9': 120, 50 | 'F10': 121, 51 | 'F11': 122, 52 | 'F12': 123, 53 | 54 | '`': 192, 55 | '=': 187, 56 | '-': 189, 57 | 58 | '/': 191, 59 | '.': 190, 60 | controlKeys: { 61 | 16: 1, 62 | 17: 1, 63 | 18: 1, 64 | 20: 1, 65 | 91: 1, 66 | 224: 1 67 | }, 68 | 'notContentChange': { 69 | 13: 1, 70 | 9: 1, 71 | 72 | 33: 1, 73 | 34: 1, 74 | 35: 1, 75 | 36: 1, 76 | 77 | 16: 1, 78 | 17: 1, 79 | 18: 1, 80 | 20: 1, 81 | 91: 1, 82 | 83 | //上下左右 84 | 37: 1, 85 | 38: 1, 86 | 39: 1, 87 | 40: 1, 88 | 89 | 113: 1, 90 | 114: 1, 91 | 115: 1, 92 | 144: 1, 93 | 27: 1 94 | }, 95 | 96 | 'isSelectedNodeKey': { 97 | //上下左右 98 | 37: 1, 99 | 38: 1, 100 | 39: 1, 101 | 40: 1, 102 | 13: 1, 103 | 9: 1 104 | } 105 | }; 106 | 107 | // 小写适配 108 | for (var key in keymap) { 109 | if (keymap.hasOwnProperty(key)) { 110 | keymap[key.toLowerCase()] = keymap[key]; 111 | } 112 | } 113 | var aKeyCode = 65; 114 | var aCharCode = 'a'.charCodeAt(0); 115 | 116 | // letters 117 | 'abcdefghijklmnopqrstuvwxyz'.split('').forEach(function(letter) { 118 | keymap[letter] = aKeyCode + (letter.charCodeAt(0) - aCharCode); 119 | }); 120 | 121 | // numbers 122 | var n = 9; 123 | do { 124 | keymap[n.toString()] = n + 48; 125 | } while (--n); 126 | 127 | module.exports = keymap; 128 | }); -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/style/navigator.scss: -------------------------------------------------------------------------------- 1 | .nav-bar { 2 | position: absolute; 3 | width: 35px; 4 | height: 230px; 5 | padding: 5px 0; 6 | left: 10px; 7 | bottom: 10px; 8 | background: #fc8383; 9 | color: #fff; 10 | border-radius: 4px; 11 | z-index: 10; 12 | box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.2); 13 | transition: -webkit-transform 0.7s 0.1s ease; 14 | transition: transform 0.7s 0.1s ease; 15 | 16 | .nav-btn { 17 | width: 35px; 18 | height: 24px; 19 | line-height: 24px; 20 | text-align: center; 21 | 22 | .icon { 23 | background: url("../../static/minder/icons.png"); 24 | width: 20px; 25 | height: 20px; 26 | margin: 2px auto; 27 | display: block; 28 | } 29 | 30 | &.active { 31 | background-color: #5a6378; 32 | } 33 | } 34 | 35 | .zoom-in .icon { 36 | background-position: 0 -730px; 37 | } 38 | 39 | .zoom-out .icon { 40 | background-position: 0 -750px; 41 | } 42 | 43 | .hand .icon { 44 | background-position: 0 -770px; 45 | width: 25px; 46 | height: 25px; 47 | margin: 0 auto; 48 | } 49 | 50 | .camera .icon { 51 | background-position: 0 -870px; 52 | width: 25px; 53 | height: 25px; 54 | margin: 0 auto; 55 | } 56 | 57 | .nav-trigger .icon { 58 | background-position: 0 -845px; 59 | width: 25px; 60 | height: 25px; 61 | margin: 0 auto; 62 | } 63 | 64 | .zoom-pan { 65 | width: 2px; 66 | height: 70px; 67 | box-shadow: 0 1px #e50000; 68 | position: relative; 69 | background: white; 70 | margin: 3px auto; 71 | overflow: visible; 72 | 73 | .origin { 74 | position: absolute; 75 | width: 20px; 76 | height: 8px; 77 | left: -9px; 78 | margin-top: -4px; 79 | background: transparent; 80 | 81 | &:after { 82 | content: " "; 83 | display: block; 84 | width: 6px; 85 | height: 2px; 86 | background: white; 87 | left: 7px; 88 | top: 3px; 89 | position: absolute; 90 | } 91 | } 92 | 93 | .indicator { 94 | position: absolute; 95 | width: 8px; 96 | height: 8px; 97 | left: -3px; 98 | background: white; 99 | border-radius: 100%; 100 | margin-top: -4px; 101 | } 102 | } 103 | } 104 | 105 | .nav-previewer { 106 | background: #fff; 107 | width: 140px; 108 | height: 120px; 109 | position: absolute; 110 | left: 45px; 111 | bottom: 30px; 112 | box-shadow: 0 0 8px rgba(0, 0, 0, 0.2); 113 | border-radius: 0 2px 2px 0; 114 | padding: 1px; 115 | z-index: 9; 116 | cursor: crosshair; 117 | transition: -webkit-transform 0.7s 0.1s ease; 118 | transition: transform 0.7s 0.1s ease; 119 | 120 | &.grab { 121 | cursor: move; 122 | cursor: -webkit-grabbing; 123 | cursor: -moz-grabbing; 124 | cursor: grabbing; 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/script/protocol/plain.js: -------------------------------------------------------------------------------- 1 | var LINE_ENDING = '\r'; 2 | var LINE_ENDING_SPLITER = /\r\n|\r|\n/; 3 | var TAB_CHAR = '\t'; 4 | 5 | function exportTextTree(minder) { 6 | var minds = minder.exportJson(); 7 | try { 8 | const link = document.createElement('a'); 9 | const blob = new Blob(["\ufeff" + encode(minds.root, 0)], { 10 | type: 'text/plain' 11 | }); 12 | link.href = window.URL.createObjectURL(blob); 13 | link.download = `${minds.root.data.text}.txt`; 14 | document.body.appendChild(link); 15 | link.click(); 16 | document.body.removeChild(link); 17 | } catch (err) { 18 | alert(err); 19 | } 20 | } 21 | 22 | function repeat(s, n) { 23 | var result = ''; 24 | while (n--) result += s; 25 | return result; 26 | } 27 | 28 | function encode(json, level) { 29 | var local = ''; 30 | level = level || 0; 31 | local += repeat(TAB_CHAR, level); 32 | local += json.data.text + LINE_ENDING; 33 | if (json.children) { 34 | json.children.forEach(function (child) { 35 | local += encode(child, level + 1); 36 | }); 37 | } 38 | return local; 39 | } 40 | 41 | function isEmpty(line) { 42 | return !/\S/.test(line); 43 | } 44 | 45 | function getLevel(line) { 46 | var level = 0; 47 | while (line.charAt(level) === TAB_CHAR) level++; 48 | return level; 49 | } 50 | 51 | function getNode(line) { 52 | return { 53 | data: { 54 | text: line.replace(new RegExp('^' + TAB_CHAR + '*'), '') 55 | } 56 | }; 57 | } 58 | 59 | /** 60 | * 文本解码 61 | * 62 | * @param {string} local 文本内容 63 | * @param {=boolean} root 自动根节点 64 | * @return {Object} 返回解析后节点 65 | */ 66 | function decode(local, root) { 67 | var json, 68 | offset, 69 | parentMap = {}, 70 | lines = local.split(LINE_ENDING_SPLITER), 71 | line, level, node; 72 | 73 | function addChild(parent, child) { 74 | var children = parent.children || (parent.children = []); 75 | children.push(child); 76 | } 77 | if (root) { 78 | parentMap[0] = json = getNode('root'); 79 | offset = 1; 80 | } else { 81 | offset = 0; 82 | } 83 | 84 | for (var i = 0; i < lines.length; i++) { 85 | line = lines[i]; 86 | if (isEmpty(line)) continue; 87 | 88 | level = getLevel(line) + offset; 89 | node = getNode(line); 90 | 91 | if (level === 0) { 92 | if (json) { 93 | throw new Error('Invalid local format'); 94 | } 95 | json = node; 96 | } else { 97 | if (!parentMap[level - 1]) { 98 | throw new Error('Invalid local format'); 99 | } 100 | addChild(parentMap[level - 1], node); 101 | } 102 | parentMap[level] = node; 103 | } 104 | return json; 105 | } 106 | 107 | export { 108 | exportTextTree 109 | } 110 | -------------------------------------------------------------------------------- /kityminder-core/src/theme/fish.js: -------------------------------------------------------------------------------- 1 | define(function(require, exports, module) { 2 | var theme = require('../core/theme'); 3 | 4 | theme.register('fish', { 5 | 'background': '#3A4144 url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAIAAAACDbGyAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDowQzg5QTQ0NDhENzgxMUUzOENGREE4QTg0RDgzRTZDNyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDowQzg5QTQ0NThENzgxMUUzOENGREE4QTg0RDgzRTZDNyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkMwOEQ1NDRGOEQ3NzExRTM4Q0ZEQThBODREODNFNkM3IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkMwOEQ1NDUwOEQ3NzExRTM4Q0ZEQThBODREODNFNkM3Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+e9P33AAAACVJREFUeNpisXJ0YUACTAyoAMr/+eM7EGGRZ4FQ7BycEAZAgAEAHbEGtkoQm/wAAAAASUVORK5CYII=") repeat', 6 | 7 | 'root-color': '#430', 8 | 'root-background': '#e9df98', 9 | 'root-stroke': '#e9df98', 10 | 'root-font-size': 24, 11 | 'root-padding': [35, 35], 12 | 'root-margin': 30, 13 | 'root-radius': 100, 14 | 'root-space': 10, 15 | 'root-shadow': 'rgba(0, 0, 0, .25)', 16 | 17 | 'main-color': '#333', 18 | 'main-background': '#a4c5c0', 19 | 'main-stroke': '#a4c5c0', 20 | 'main-font-size': 16, 21 | 'main-padding': [6, 20], 22 | 'main-margin': [20, 20], 23 | 'main-radius': 5, 24 | 'main-space': 5, 25 | 'main-shadow': 'rgba(0, 0, 0, .25)', 26 | 27 | 'sub-color': 'black', 28 | 'sub-background': 'white', 29 | 'sub-stroke': 'white', 30 | 'sub-font-size': 12, 31 | 'sub-padding': [5, 10], 32 | 'sub-margin': [10], 33 | 'sub-radius': 5, 34 | 'sub-space': 5, 35 | 36 | 'connect-color': 'white', 37 | 'connect-width': 3, 38 | 'main-connect-width': 3, 39 | 'connect-radius': 5, 40 | 41 | 'selected-background': 'rgb(254, 219, 0)', 42 | 'selected-stroke': 'rgb(254, 219, 0)', 43 | 44 | 'marquee-background': 'rgba(255,255,255,.3)', 45 | 'marquee-stroke': 'white', 46 | 47 | 'drop-hint-color': 'yellow', 48 | 'drop-hint-width': 4, 49 | 50 | 'order-hint-area-color': 'rgba(0, 255, 0, .5)', 51 | 'order-hint-path-color': '#0f0', 52 | 'order-hint-path-width': 1, 53 | 54 | 'text-selection-color': 'rgb(27,171,255)', 55 | 'line-height':1.5 56 | }); 57 | }); -------------------------------------------------------------------------------- /kityminder-core/src/core/template.js: -------------------------------------------------------------------------------- 1 | define(function(require, exports, module) { 2 | var kity = require('./kity'); 3 | var utils = require('./utils'); 4 | var Minder = require('./minder'); 5 | var Command = require('./command'); 6 | var MinderNode = require('./node'); 7 | var Module = require('./module'); 8 | 9 | var _templates = {}; 10 | 11 | function register(name, supports) { 12 | _templates[name] = supports; 13 | } 14 | exports.register = register; 15 | 16 | utils.extend(Minder, { 17 | getTemplateList: function() { 18 | return _templates; 19 | } 20 | }); 21 | 22 | kity.extendClass(Minder, (function() { 23 | var originGetTheme = Minder.prototype.getTheme; 24 | return { 25 | useTemplate: function(name, duration) { 26 | this.setTemplate(name); 27 | this.refresh(duration || 800); 28 | }, 29 | 30 | getTemplate: function() { 31 | return this._template || 'default'; 32 | }, 33 | 34 | setTemplate: function(name) { 35 | this._template = name || null; 36 | }, 37 | 38 | getTemplateSupport: function(method) { 39 | var supports = _templates[this.getTemplate()]; 40 | return supports && supports[method]; 41 | }, 42 | 43 | getTheme: function(node) { 44 | var support = this.getTemplateSupport('getTheme') || originGetTheme; 45 | return support.call(this, node); 46 | } 47 | }; 48 | })()); 49 | 50 | 51 | kity.extendClass(MinderNode, (function() { 52 | var originGetLayout = MinderNode.prototype.getLayout; 53 | var originGetConnect = MinderNode.prototype.getConnect; 54 | return { 55 | getLayout: function() { 56 | var support = this.getMinder().getTemplateSupport('getLayout') || originGetLayout; 57 | return support.call(this, this); 58 | }, 59 | 60 | getConnect: function() { 61 | var support = this.getMinder().getTemplateSupport('getConnect') || originGetConnect; 62 | return support.call(this, this); 63 | } 64 | }; 65 | })()); 66 | 67 | Module.register('TemplateModule', { 68 | /** 69 | * @command Template 70 | * @description 设置当前脑图的模板 71 | * @param {string} name 模板名称 72 | * 允许使用的模板可以使用 `kityminder.Minder.getTemplateList()` 查询 73 | * @state 74 | * 0: 始终可用 75 | * @return 返回当前的模板名称 76 | */ 77 | commands: { 78 | 'template': kity.createClass('TemplateCommand', { 79 | base: Command, 80 | 81 | execute: function(minder, name) { 82 | minder.useTemplate(name); 83 | minder.execCommand('camera'); 84 | }, 85 | 86 | queryValue: function(minder) { 87 | return minder.getTemplate() || 'default'; 88 | } 89 | }) 90 | } 91 | }); 92 | }); -------------------------------------------------------------------------------- /kityminder-core/import.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | /** 4 | * 开发版本的文件导入 5 | */ 6 | (function() { 7 | /* 可能的文件路径,已按照依赖关系排序 */ 8 | var pathInfo = [ 9 | /* 核心代码 */ 10 | 'src/core/kityminder.js', 11 | 'src/core/utils.js', 12 | 13 | 'src/core/command.js', 14 | 'src/core/node.js', 15 | 16 | 'src/core/options.js', 17 | 'src/core/event.js', 18 | 'src/core/status.js', 19 | 'src/core/paper.js', 20 | 'src/core/select.js', 21 | 'src/core/module.js', 22 | 'src/core/data.js', 23 | 'src/core/readonly.js', 24 | 'src/core/layout.js', 25 | 'src/core/theme.js', 26 | 27 | 'src/core/compatibility.js', 28 | 'src/core/render.js', 29 | 'src/core/connect.js', 30 | 'src/core/template.js', 31 | 'src/core/keymap.js', 32 | 33 | /* 布局 */ 34 | 'src/layout/mind.js', 35 | 'src/layout/filetree.js', 36 | 'src/layout/btree.js', 37 | 'src/layout/fish-bone-master.js', 38 | 'src/layout/fish-bone-slave.js', 39 | 40 | /* 连线 */ 41 | 'src/connect/bezier.js', 42 | 'src/connect/poly.js', 43 | 'src/connect/arc.js', 44 | 'src/connect/under.js', 45 | 'src/connect/l.js', 46 | 'src/connect/fish-bone-master.js', 47 | 48 | /* 皮肤 */ 49 | 'src/theme/default.js', 50 | 'src/theme/snow.js', 51 | 'src/theme/fresh.js', 52 | 'src/theme/fish.js', 53 | 'src/theme/wire.js', 54 | 55 | /* 模板 */ 56 | 'src/template/default.js', 57 | 'src/template/structure.js', 58 | 'src/template/filetree.js', 59 | 'src/template/right.js', 60 | 'src/template/fish-bone.js', 61 | 62 | /* 模块 */ 63 | 'src/module/node.js', 64 | 'src/module/text.js', 65 | 'src/module/expand.js', 66 | 'src/module/outline.js', 67 | 'src/module/history.js', 68 | 'src/module/progress.js', 69 | 'src/module/priority.js', 70 | 'src/module/image.js', 71 | 'src/module/resource.js', 72 | 'src/module/view.js', 73 | 'src/module/dragtree.js', 74 | 'src/module/keynav.js', 75 | 'src/module/select.js', 76 | 'src/module/history.js', 77 | 'src/module/editor.js', 78 | 'src/module/editor.keyboard.js', 79 | 'src/module/editor.range.js', 80 | 'src/module/editor.receiver.js', 81 | 'src/module/editor.selection.js', 82 | 'src/module/basestyle.js', 83 | 'src/module/font.js', 84 | 'src/module/zoom.js', 85 | 'src/module/hyperlink.js', 86 | 'src/module/arrange.js', 87 | 'src/module/clipboard.js', 88 | 'src/module/style.js' 89 | ]; 90 | 91 | if (typeof(module) === 'object' && module.exports) { 92 | module.exports = pathInfo; 93 | } 94 | 95 | else if (document) { 96 | while (pathInfo.length) { 97 | var path = pathInfo.shift(); 98 | window.document.write(''); 99 | } 100 | } 101 | })(); -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/script/runtime/receiver.js: -------------------------------------------------------------------------------- 1 | define(function (require, exports, module) { 2 | var key = require('../tool/key'); 3 | var hotbox = require('./hotbox'); 4 | 5 | function ReceiverRuntime() { 6 | var fsm = this.fsm; 7 | var minder = this.minder; 8 | var me = this; 9 | 10 | // 接收事件的 div 11 | var element = document.createElement('div'); 12 | element.contentEditable = true; 13 | element.setAttribute("tabindex", -1); 14 | element.classList.add('receiver'); 15 | element.onkeydown = element.onkeypress = element.onkeyup = dispatchKeyEvent; 16 | this.container.appendChild(element); 17 | 18 | // receiver 对象 19 | var receiver = { 20 | element: element, 21 | selectAll: function () { 22 | // 保证有被选中的 23 | if (!element.innerHTML) element.innerHTML = ' '; 24 | var range = document.createRange(); 25 | var selection = window.getSelection(); 26 | range.selectNodeContents(element); 27 | selection.removeAllRanges(); 28 | selection.addRange(range); 29 | element.focus(); 30 | }, 31 | enable: function () { 32 | element.setAttribute("contenteditable", true); 33 | }, 34 | disable: function () { 35 | element.setAttribute("contenteditable", false); 36 | }, 37 | fixFFCaretDisappeared: function () { 38 | element.removeAttribute("contenteditable"); 39 | element.setAttribute("contenteditable", "true"); 40 | element.blur(); 41 | element.focus(); 42 | }, 43 | onblur: function (handler) { 44 | element.onblur = handler; 45 | } 46 | }; 47 | receiver.selectAll(); 48 | minder.on('beforemousedown', receiver.selectAll); 49 | minder.on('receiverfocus', receiver.selectAll); 50 | minder.on('readonly', function () { 51 | // 屏蔽minder的事件接受,删除receiver和hotbox 52 | minder.disable(); 53 | editor.receiver.element.parentElement.removeChild(editor.receiver.element); 54 | editor.hotbox.$container.removeChild(editor.hotbox.$element); 55 | }); 56 | 57 | // 侦听器,接收到的事件会派发给所有侦听器 58 | var listeners = []; 59 | 60 | // 侦听指定状态下的事件,如果不传 state,侦听所有状态 61 | receiver.listen = function (state, listener) { 62 | if (arguments.length == 1) { 63 | listener = state; 64 | state = '*'; 65 | } 66 | listener.notifyState = state; 67 | listeners.push(listener); 68 | }; 69 | 70 | function dispatchKeyEvent(e) { 71 | e.is = function (keyExpression) { 72 | var subs = keyExpression.split('|'); 73 | for (var i = 0; i < subs.length; i++) { 74 | if (key.is(this, subs[i])) return true; 75 | } 76 | return false; 77 | }; 78 | var listener, jumpState; 79 | for (var i = 0; i < listeners.length; i++) { 80 | 81 | listener = listeners[i]; 82 | // 忽略不在侦听状态的侦听器 83 | if (listener.notifyState != '*' && listener.notifyState != fsm.state()) { 84 | continue; 85 | } 86 | 87 | if (listener.call(null, e)) { 88 | return; 89 | } 90 | } 91 | } 92 | 93 | this.receiver = receiver; 94 | } 95 | 96 | return module.exports = ReceiverRuntime; 97 | }); 98 | -------------------------------------------------------------------------------- /kityminder-core/src/core/readonly.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview 3 | * 4 | * 只读模式支持 5 | * 6 | * @author: techird 7 | * @copyright: Baidu FEX, 2014 8 | */ 9 | 10 | define(function(require, exports, module) { 11 | var kity = require('./kity'); 12 | var Minder = require('./minder'); 13 | var MinderEvent = require('./event'); 14 | 15 | // 初始化时根据的传入的 options 决定是否要 disable 16 | Minder.registerInitHook(function(options) { 17 | if (options.readOnly) { 18 | this.disable(); 19 | } 20 | }); 21 | 22 | kity.extendClass(Minder, { 23 | 24 | disable: function() { 25 | var me = this; 26 | //禁用命令 27 | me.bkqueryCommandState = me.queryCommandState; 28 | me.bkqueryCommandValue = me.queryCommandValue; 29 | me.queryCommandState = function(type) { 30 | var cmd = this._getCommand(type); 31 | if (cmd && cmd.enableReadOnly) { 32 | return me.bkqueryCommandState.apply(me, arguments); 33 | } 34 | return -1; 35 | }; 36 | me.queryCommandValue = function(type) { 37 | var cmd = this._getCommand(type); 38 | if (cmd && cmd.enableReadOnly) { 39 | return me.bkqueryCommandValue.apply(me, arguments); 40 | } 41 | return null; 42 | }; 43 | this.setStatus('readonly'); 44 | me._interactChange(); 45 | }, 46 | 47 | /** 48 | * @description 【二次开发新增】关闭指定模块命令 49 | * @param {Array} value 要关闭的模块名称。 50 | * 具体名称可从 minder 对象的 _commands 中获取 51 | */ 52 | disableSpecificModule: function(moduleNames) { 53 | var me = this; 54 | //仅启用部分模块 55 | me.bkqueryCommandState = me.queryCommandState; 56 | me.bkqueryCommandValue = me.queryCommandValue; 57 | me.queryCommandState = function(type) { 58 | var cmd = this._getCommand(type); 59 | if (cmd && moduleNames.indexOf(cmd.__KityClassName) === -1) { 60 | return me.bkqueryCommandState.apply(me, arguments); 61 | } 62 | return -1; 63 | }; 64 | me.queryCommandValue = function(type) { 65 | var cmd = this._getCommand(type); 66 | if (cmd && moduleNames.indexOf(cmd.__KityClassName) === -1) { 67 | return me.bkqueryCommandValue.apply(me, arguments); 68 | } 69 | return null; 70 | }; 71 | this.setStatus('partEnable'); 72 | me._interactChange(); 73 | }, 74 | 75 | enable: function() { 76 | var me = this; 77 | 78 | if (me.bkqueryCommandState) { 79 | me.queryCommandState = me.bkqueryCommandState; 80 | delete me.bkqueryCommandState; 81 | } 82 | if (me.bkqueryCommandValue) { 83 | me.queryCommandValue = me.bkqueryCommandValue; 84 | delete me.bkqueryCommandValue; 85 | } 86 | 87 | this.setStatus('normal'); 88 | 89 | me._interactChange(); 90 | } 91 | }); 92 | }); -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/components/menu/edit/search/searchLabelBox.vue: -------------------------------------------------------------------------------- 1 | 51 | 52 | 112 | 113 | 114 | 116 | -------------------------------------------------------------------------------- /examples/App.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 97 | -------------------------------------------------------------------------------- /hotbox/demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 26 | 27 | 28 |
编辑区域获得焦点时,按空格呼出热盒。主菜单的快捷键可以直接执行
29 |
30 | 31 | 107 | -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/style/editor.scss: -------------------------------------------------------------------------------- 1 | @import "~codemirror/lib/codemirror.css"; 2 | //@import "~kityminder-core/dist/kityminder.core.css"; 3 | @import "~@/../kityminder-core/dist/kityminder.core.css"; 4 | @import "navigator.scss"; 5 | @import "hotbox.scss"; 6 | 7 | // * { 8 | // box-sizing: border-box; 9 | // } 10 | 11 | .mind-editor { 12 | position: absolute; 13 | top: 91px; 14 | left: 0; 15 | right: 0; 16 | bottom: 0; 17 | } 18 | 19 | .km-editor { 20 | overflow: hidden; 21 | z-index: 2; 22 | } 23 | 24 | .km-editor > .mask { 25 | display: block; 26 | position: absolute; 27 | left: 0; 28 | right: 0; 29 | top: 0; 30 | bottom: 0; 31 | background-color: transparent; 32 | } 33 | 34 | .km-editor > .receiver { 35 | position: absolute; 36 | background: white; 37 | outline: none; 38 | box-shadow: 0 0 20px fadeout(black, 50%); 39 | left: 0; 40 | top: 0; 41 | padding: 3px 5px; 42 | margin-left: -3px; 43 | margin-top: -5px; 44 | max-width: 300px; 45 | width: auto; 46 | overflow: hidden; 47 | font-size: 14px; 48 | line-height: 1.4em; 49 | min-height: 1.4em; 50 | box-sizing: border-box; 51 | overflow: hidden; 52 | word-break: break-all; 53 | word-wrap: break-word; 54 | border: none; 55 | -webkit-user-select: text; 56 | pointer-events: none; 57 | opacity: 0; 58 | z-index: -1000; 59 | 60 | &.debug { 61 | opacity: 1; 62 | outline: 1px solid green; 63 | background: none; 64 | z-index: 0; 65 | } 66 | 67 | &.input { 68 | pointer-events: all; 69 | opacity: 1; 70 | z-index: 999; 71 | background: white; 72 | outline: none; 73 | } 74 | } 75 | 76 | div.minder-editor-container { 77 | position: absolute; 78 | top: 40px; 79 | bottom: 0; 80 | left: 0; 81 | right: 0; 82 | font-family: Arial, "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif; 83 | } 84 | 85 | .minder-editor { 86 | position: absolute; 87 | top: 92px; 88 | left: 0; 89 | right: 0; 90 | bottom: 0; 91 | } 92 | 93 | .minder-viewer { 94 | position: absolute; 95 | top: 0; 96 | left: 0; 97 | right: 0; 98 | bottom: 0; 99 | } 100 | 101 | .control-panel { 102 | position: absolute; 103 | top: 0; 104 | right: 0; 105 | width: 250px; 106 | bottom: 0; 107 | border-left: 1px solid #ccc; 108 | } 109 | 110 | .minder-divider { 111 | position: absolute; 112 | top: 0; 113 | right: 250px; 114 | bottom: 0; 115 | width: 2px; 116 | background-color: rgb(251, 251, 251); 117 | cursor: ew-resize; 118 | } 119 | 120 | .hotbox .state .button.enabled.selected .key, 121 | .hotbox .state .ring .key { 122 | margin-top: 5px; 123 | font-size: 13px; 124 | } 125 | 126 | .hotbox .state .bottom .button .label, 127 | .hotbox .state .top .button .label { 128 | font-weight: 600; 129 | } 130 | 131 | .hotbox .exp .ring .button .label { 132 | margin-top: 28px; 133 | margin-left: -2px; 134 | } 135 | 136 | .hotbox .exp .ring .button .key { 137 | display: none; 138 | } 139 | 140 | .note-box { 141 | text-align: left; 142 | width: 400px; 143 | height: 200px; 144 | background: #feffdd; 145 | border-radius: 15px; 146 | box-shadow: 2px 2px 20px #000; 147 | overflow: auto; 148 | padding: 15px; 149 | position: fixed; 150 | z-index: 999; 151 | user-select: text; 152 | } 153 | -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/components/header.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 97 | 98 | 101 | -------------------------------------------------------------------------------- /kityminder-core/src/layout/filetree.js: -------------------------------------------------------------------------------- 1 | define(function(require, exports, module) { 2 | var kity = require('../core/kity'); 3 | var Layout = require('../core/layout'); 4 | 5 | [-1, 1].forEach(registerLayoutForDir); 6 | 7 | function registerLayoutForDir(dir) { 8 | var name = 'filetree-' + (dir > 0 ? 'down' : 'up'); 9 | 10 | Layout.register(name, kity.createClass({ 11 | base: Layout, 12 | 13 | doLayout: function(parent, children, round) { 14 | var pBox = parent.getContentBox(); 15 | var indent = 20; 16 | 17 | parent.setVertexOut(new kity.Point(pBox.left + indent, dir > 0 ? pBox.bottom : pBox.top)); 18 | parent.setLayoutVectorOut(new kity.Vector(0, dir)); 19 | 20 | if (!children.length) return; 21 | 22 | children.forEach(function(child) { 23 | var cbox = child.getContentBox(); 24 | child.setLayoutTransform(new kity.Matrix()); 25 | 26 | child.setVertexIn(new kity.Point(cbox.left, cbox.cy)); 27 | child.setLayoutVectorIn(new kity.Vector(1, 0)); 28 | }); 29 | 30 | this.align(children, 'left'); 31 | this.stack(children, 'y'); 32 | 33 | var xAdjust = 0; 34 | xAdjust += pBox.left; 35 | xAdjust += indent; 36 | xAdjust += children[0].getStyle('margin-left'); 37 | 38 | var yAdjust = 0; 39 | 40 | if (dir > 0) { 41 | yAdjust += pBox.bottom; 42 | yAdjust += parent.getStyle('margin-bottom'); 43 | yAdjust += children[0].getStyle('margin-top'); 44 | } else { 45 | yAdjust -= this.getTreeBox(children).bottom; 46 | yAdjust += pBox.top; 47 | yAdjust -= parent.getStyle('margin-top'); 48 | yAdjust -= children[0].getStyle('margin-bottom'); 49 | } 50 | 51 | this.move(children, xAdjust, yAdjust); 52 | 53 | }, 54 | 55 | getOrderHint: function(node) { 56 | var hint = []; 57 | var box = node.getLayoutBox(); 58 | var offset = node.getLevel() > 1 ? 3 : 5; 59 | 60 | hint.push({ 61 | type: 'up', 62 | node: node, 63 | area: new kity.Box({ 64 | x: box.x, 65 | y: box.top - node.getStyle('margin-top') - offset, 66 | width: box.width, 67 | height: node.getStyle('margin-top') 68 | }), 69 | path: ['M', box.x, box.top - offset, 'L', box.right, box.top - offset] 70 | }); 71 | 72 | hint.push({ 73 | type: 'down', 74 | node: node, 75 | area: new kity.Box({ 76 | x: box.x, 77 | y: box.bottom + offset, 78 | width: box.width, 79 | height: node.getStyle('margin-bottom') 80 | }), 81 | path: ['M', box.x, box.bottom + offset, 'L', box.right, box.bottom + offset] 82 | }); 83 | return hint; 84 | } 85 | })); 86 | 87 | } 88 | 89 | }); -------------------------------------------------------------------------------- /kityminder-core/src/theme/snow.js: -------------------------------------------------------------------------------- 1 | define(function(require, exports, module) { 2 | var theme = require('../core/theme'); 3 | 4 | ['snow', 'snow-compact'].forEach(function(name) { 5 | var compact = name == 'snow-compact'; 6 | 7 | /* jscs:disable maximumLineLength */ 8 | theme.register(name, { 9 | 'background': '#3A4144 url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAIAAAACDbGyAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDowQzg5QTQ0NDhENzgxMUUzOENGREE4QTg0RDgzRTZDNyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDowQzg5QTQ0NThENzgxMUUzOENGREE4QTg0RDgzRTZDNyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkMwOEQ1NDRGOEQ3NzExRTM4Q0ZEQThBODREODNFNkM3IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkMwOEQ1NDUwOEQ3NzExRTM4Q0ZEQThBODREODNFNkM3Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+e9P33AAAACVJREFUeNpisXJ0YUACTAyoAMr/+eM7EGGRZ4FQ7BycEAZAgAEAHbEGtkoQm/wAAAAASUVORK5CYII=") repeat', 10 | 11 | 'root-color': '#430', 12 | 'root-background': '#e9df98', 13 | 'root-stroke': '#e9df98', 14 | 'root-font-size': 24, 15 | 'root-padding': compact ? [5, 10] : [15, 25], 16 | 'root-margin': compact ? 15 : 30, 17 | 'root-radius': 5, 18 | 'root-space': 10, 19 | 'root-shadow': 'rgba(0, 0, 0, .25)', 20 | 21 | 'main-color': '#333', 22 | 'main-background': '#a4c5c0', 23 | 'main-stroke': '#a4c5c0', 24 | 'main-font-size': 16, 25 | 'main-padding': compact ? [4, 10] : [6, 20], 26 | 'main-margin': compact ? [5, 10] : [20, 40], 27 | 'main-radius': 5, 28 | 'main-space': 5, 29 | 'main-shadow': 'rgba(0, 0, 0, .25)', 30 | 31 | 'sub-color': 'black', 32 | 'sub-background': 'white', 33 | 'sub-stroke': 'white', 34 | 'sub-font-size': 12, 35 | 'sub-padding': [5, 10], 36 | 'sub-margin': compact ? [5, 10] : [10, 20], 37 | 'sub-radius': 5, 38 | 'sub-space': 5, 39 | 40 | 'connect-color': 'white', 41 | 'connect-width': 2, 42 | 'main-connect-width': 3, 43 | 'connect-radius': 5, 44 | 45 | 'selected-background': 'rgb(254, 219, 0)', 46 | 'selected-stroke': 'rgb(254, 219, 0)', 47 | 48 | 'marquee-background': 'rgba(255,255,255,.3)', 49 | 'marquee-stroke': 'white', 50 | 51 | 'drop-hint-color': 'yellow', 52 | 'drop-hint-width': 4, 53 | 54 | 'order-hint-area-color': 'rgba(0, 255, 0, .5)', 55 | 'order-hint-path-color': '#0f0', 56 | 'order-hint-path-width': 1, 57 | 58 | 'text-selection-color': 'rgb(27,171,255)', 59 | 'line-height':1.5 60 | }); 61 | }); 62 | }); -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/src/script/runtime/fsm.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview 3 | * 4 | * 编辑器状态机 5 | * 6 | * @author: techird 7 | * @copyright: Baidu FEX, 2014 8 | */ 9 | define(function (require, exports, module) { 10 | 11 | var Debug = require('../tool/debug'); 12 | var debug = new Debug('fsm'); 13 | 14 | function handlerConditionMatch(condition, when, exit, enter) { 15 | if (condition.when != when) return false; 16 | if (condition.enter != '*' && condition.enter != enter) return false; 17 | if (condition.exit != '*' && condition.exit != exit) return; 18 | return true; 19 | } 20 | 21 | function FSM(defaultState) { 22 | var currentState = defaultState; 23 | var BEFORE_ARROW = ' - '; 24 | var AFTER_ARROW = ' -> '; 25 | var handlers = []; 26 | 27 | /** 28 | * 状态跳转 29 | * 30 | * 会通知所有的状态跳转监视器 31 | * 32 | * @param {string} newState 新状态名称 33 | * @param {any} reason 跳转的原因,可以作为参数传递给跳转监视器 34 | */ 35 | this.jump = function (newState, reason) { 36 | if (!reason) throw new Error('Please tell fsm the reason to jump'); 37 | 38 | var oldState = currentState; 39 | var notify = [oldState, newState].concat([].slice.call(arguments, 1)); 40 | var i, handler; 41 | 42 | // 跳转前 43 | for (i = 0; i < handlers.length; i++) { 44 | handler = handlers[i]; 45 | if (handlerConditionMatch(handler.condition, 'before', oldState, newState)) { 46 | if (handler.apply(null, notify)) return; 47 | } 48 | } 49 | 50 | currentState = newState; 51 | debug.log('[{0}] {1} -> {2}', reason, oldState, newState); 52 | 53 | // 跳转后 54 | for (i = 0; i < handlers.length; i++) { 55 | handler = handlers[i]; 56 | if (handlerConditionMatch(handler.condition, 'after', oldState, newState)) { 57 | handler.apply(null, notify); 58 | } 59 | } 60 | return currentState; 61 | }; 62 | 63 | /** 64 | * 返回当前状态 65 | * @return {string} 66 | */ 67 | this.state = function () { 68 | return currentState; 69 | }; 70 | 71 | /** 72 | * 添加状态跳转监视器 73 | * 74 | * @param {string} condition 75 | * 监视的时机 76 | * "* => *" (默认) 77 | * 78 | * @param {Function} handler 79 | * 监视函数,当状态跳转的时候,会接收三个参数 80 | * * from - 跳转前的状态 81 | * * to - 跳转后的状态 82 | * * reason - 跳转的原因 83 | */ 84 | this.when = function (condition, handler) { 85 | if (arguments.length == 1) { 86 | handler = condition; 87 | condition = '* -> *'; 88 | } 89 | 90 | var when, resolved, exit, enter; 91 | 92 | resolved = condition.split(BEFORE_ARROW); 93 | if (resolved.length == 2) { 94 | when = 'before'; 95 | } else { 96 | resolved = condition.split(AFTER_ARROW); 97 | if (resolved.length == 2) { 98 | when = 'after'; 99 | } 100 | } 101 | if (!when) throw new Error('Illegal fsm condition: ' + condition); 102 | 103 | exit = resolved[0]; 104 | enter = resolved[1]; 105 | 106 | handler.condition = { 107 | when: when, 108 | exit: exit, 109 | enter: enter 110 | }; 111 | 112 | handlers.push(handler); 113 | }; 114 | } 115 | 116 | function FSMRumtime() { 117 | this.fsm = new FSM('normal'); 118 | } 119 | 120 | return module.exports = FSMRumtime; 121 | }); 122 | -------------------------------------------------------------------------------- /packages/VueTestcaseMinderEditor/App.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 102 | -------------------------------------------------------------------------------- /kityminder-core/src/theme/default.js: -------------------------------------------------------------------------------- 1 | define(function(require, exports, module) { 2 | var theme = require('../core/theme'); 3 | 4 | ['classic', 'classic-compact'].forEach(function(name) { 5 | var compact = name == 'classic-compact'; 6 | 7 | /* jscs:disable maximumLineLength */ 8 | theme.register(name, { 9 | 'background': '#3A4144 url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAIAAAACDbGyAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDowQzg5QTQ0NDhENzgxMUUzOENGREE4QTg0RDgzRTZDNyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDowQzg5QTQ0NThENzgxMUUzOENGREE4QTg0RDgzRTZDNyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkMwOEQ1NDRGOEQ3NzExRTM4Q0ZEQThBODREODNFNkM3IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkMwOEQ1NDUwOEQ3NzExRTM4Q0ZEQThBODREODNFNkM3Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+e9P33AAAACVJREFUeNpisXJ0YUACTAyoAMr/+eM7EGGRZ4FQ7BycEAZAgAEAHbEGtkoQm/wAAAAASUVORK5CYII=") repeat', 10 | 11 | 'root-color': '#430', 12 | 'root-background': '#e9df98', 13 | 'root-stroke': '#e9df98', 14 | 'root-font-size': 24, 15 | 'root-padding': compact ? [10, 25] : [15, 25], 16 | 'root-margin': compact ? [15, 25] : [30, 100], 17 | 'root-radius': 30, 18 | 'root-space': 10, 19 | 'root-shadow': 'rgba(0, 0, 0, .25)', 20 | 21 | 'main-color': '#333', 22 | 'main-background': '#a4c5c0', 23 | 'main-stroke': '#a4c5c0', 24 | 'main-font-size': 16, 25 | 'main-padding': compact ? [5, 15] : [6, 20], 26 | 'main-margin': compact ? [5, 10] : 20, 27 | 'main-radius': 10, 28 | 'main-space': 5, 29 | 'main-shadow': 'rgba(0, 0, 0, .25)', 30 | 31 | 'sub-color': 'white', 32 | 'sub-background': 'transparent', 33 | 'sub-stroke': 'none', 34 | 'sub-font-size': 12, 35 | 'sub-padding': [5, 10], 36 | 'sub-margin': compact ? [5, 10] : [15, 20], 37 | 'sub-tree-margin': 30, 38 | 'sub-radius': 5, 39 | 'sub-space': 5, 40 | 41 | 'connect-color': 'white', 42 | 'connect-width': 2, 43 | 'main-connect-width': 3, 44 | 'connect-radius': 5, 45 | 46 | 'selected-background': 'rgb(254, 219, 0)', 47 | 'selected-stroke': 'rgb(254, 219, 0)', 48 | 'selected-color': 'black', 49 | 50 | 'marquee-background': 'rgba(255,255,255,.3)', 51 | 'marquee-stroke': 'white', 52 | 53 | 'drop-hint-color': 'yellow', 54 | 'sub-drop-hint-width': 2, 55 | 'main-drop-hint-width': 4, 56 | 'root-drop-hint-width': 4, 57 | 58 | 'order-hint-area-color': 'rgba(0, 255, 0, .5)', 59 | 'order-hint-path-color': '#0f0', 60 | 'order-hint-path-width': 1, 61 | 62 | 'text-selection-color': 'rgb(27,171,255)', 63 | 'line-height':1.5 64 | }); 65 | }); 66 | }); --------------------------------------------------------------------------------