diff --git a/.stylelintrc.js b/.stylelintrc.js
index 0749dce..d640c2f 100644
--- a/.stylelintrc.js
+++ b/.stylelintrc.js
@@ -16,7 +16,7 @@ module.exports = {
'at-rule-name-case': 'lower', // 指定@规则名的大小写
'length-zero-no-unit': true, // 禁止零长度的单位(可自动修复)
'shorthand-property-no-redundant-values': true, // 简写属性
- 'number-leading-zero': 'never', // 小数不带0
+ 'number-leading-zero': 'always', // 小数不带0
'declaration-block-no-duplicate-properties': true, // 禁止声明快重复属性
'no-descending-specificity': true, // 禁止在具有较高优先级的选择器后出现被其覆盖的较低优先级的选择器。
'selector-max-id': 3, // 限制一个选择器中 ID 选择器的数量
diff --git a/packages/client/package.json b/packages/client/package.json
index bd84a91..36114bc 100644
--- a/packages/client/package.json
+++ b/packages/client/package.json
@@ -61,6 +61,7 @@
"dompurify": "^2.3.5",
"interactjs": "^1.10.11",
"katex": "^0.15.2",
+ "kity": "^2.0.4",
"lib0": "^0.2.47",
"lowlight": "^2.5.0",
"markdown-it": "^12.3.2",
diff --git a/packages/client/src/components/template/card/index.module.scss b/packages/client/src/components/template/card/index.module.scss
index b774633..1cc5e7d 100644
--- a/packages/client/src/components/template/card/index.module.scss
+++ b/packages/client/src/components/template/card/index.module.scss
@@ -36,7 +36,7 @@
border-radius: 0 0 var(--border-radius) var(--border-radius);
opacity: 0;
justify-content: space-around;
- transition: all ease-in-out .2s;
+ transition: all ease-in-out 0.2s;
button {
width: 40%;
diff --git a/packages/client/src/components/wiki/tocs/index.module.scss b/packages/client/src/components/wiki/tocs/index.module.scss
index 8fdd25a..952e7b6 100644
--- a/packages/client/src/components/wiki/tocs/index.module.scss
+++ b/packages/client/src/components/wiki/tocs/index.module.scss
@@ -103,5 +103,5 @@
}
.docListTitle {
- margin: 12px .5rem;
+ margin: 12px 0.5rem;
}
diff --git a/packages/client/src/pages/wiki/[wikiId]/index.module.scss b/packages/client/src/pages/wiki/[wikiId]/index.module.scss
index 39b43d5..e619ac7 100644
--- a/packages/client/src/pages/wiki/[wikiId]/index.module.scss
+++ b/packages/client/src/pages/wiki/[wikiId]/index.module.scss
@@ -1,5 +1,5 @@
.navItemWrap {
- padding: 0 .5rem;
+ padding: 0 0.5rem;
.navItem {
display: flex;
diff --git a/packages/client/src/styles/globals.scss b/packages/client/src/styles/globals.scss
index 5d10e4d..b602f75 100644
--- a/packages/client/src/styles/globals.scss
+++ b/packages/client/src/styles/globals.scss
@@ -1,6 +1,8 @@
@import '~@douyinfe/semi-ui/dist/css/semi.min.css';
@import './var.scss';
@import './reset.scss';
+@import './kityminder.editor.css';
+@import './hotbox.css';
.container {
margin-right: auto;
@@ -27,7 +29,7 @@
.Resizer {
z-index: 1;
- opacity: .2;
+ opacity: 0.2;
box-sizing: border-box;
background-clip: padding;
}
diff --git a/packages/client/src/styles/hotbox.css b/packages/client/src/styles/hotbox.css
new file mode 100644
index 0000000..20c927c
--- /dev/null
+++ b/packages/client/src/styles/hotbox.css
@@ -0,0 +1,191 @@
+/* stylelint-disable */
+.hotbox {
+ position: absolute;
+ top: 0;
+ left: 0;
+ overflow: visible;
+ font-family: Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 'WenQuanYi Micro Hei', sans-serif;
+}
+
+.hotbox .state {
+ position: absolute;
+ display: none;
+ overflow: visible;
+}
+
+.hotbox .state .center .button,
+.hotbox .state .ring .button {
+ position: absolute;
+ width: 70px;
+ height: 70px;
+ margin-top: -35px;
+ margin-left: -35px;
+ border-radius: 100%;
+ box-shadow: 0 0 30px rgb(0 0 0 / 30%);
+}
+
+.hotbox .state .center .label,
+.hotbox .state .ring .label,
+.hotbox .state .center .key,
+.hotbox .state .ring .key {
+ display: block;
+ line-height: 1.4em;
+ text-align: center;
+ vertical-align: middle;
+}
+
+.hotbox .state .center .label,
+.hotbox .state .ring .label {
+ margin-top: 17px;
+ font-size: 16px;
+ font-weight: normal;
+ line-height: 1em;
+ color: black;
+}
+
+.hotbox .state .center .key,
+.hotbox .state .ring .key {
+ font-size: 12px;
+ color: #999;
+}
+
+.hotbox .state .ring-shape {
+ position: absolute;
+ top: -25px;
+ left: -25px;
+ border: 25px solid rgb(0 0 0 / 30%);
+ border-radius: 100%;
+ box-sizing: content-box;
+}
+
+.hotbox .state .top,
+.hotbox .state .bottom {
+ position: absolute;
+ white-space: nowrap;
+}
+
+.hotbox .state .top .button,
+.hotbox .state .bottom .button {
+ position: relative;
+ display: inline-block;
+ padding: 8px 15px;
+ margin: 0 10px;
+ border-radius: 15px;
+ box-shadow: 0 0 30px rgb(0 0 0 / 30%);
+}
+
+.hotbox .state .top .button .label,
+.hotbox .state .bottom .button .label {
+ font-size: 14px;
+ line-height: 14px;
+ line-height: 1em;
+ color: black;
+ vertical-align: middle;
+}
+
+.hotbox .state .top .button .key,
+.hotbox .state .bottom .button .key {
+ margin-left: 3px;
+ font-size: 12px;
+ line-height: 12px;
+ color: #999;
+ vertical-align: middle;
+}
+
+.hotbox .state .top .button .key::before,
+.hotbox .state .bottom .button .key::before {
+ content: '(';
+}
+
+.hotbox .state .top .button .key::after,
+.hotbox .state .bottom .button .key::after {
+ content: ')';
+}
+
+.hotbox .state .button {
+ overflow: hidden;
+ cursor: default;
+ background: #f9f9f9;
+}
+
+.hotbox .state .button .key,
+.hotbox .state .button .label {
+ opacity: 0.3;
+}
+
+.hotbox .state .button.enabled {
+ background: white;
+}
+
+.hotbox .state .button.enabled .key,
+.hotbox .state .button.enabled .label {
+ opacity: 1;
+}
+
+.hotbox .state .button.enabled:hover {
+ background: #e87372;
+}
+
+.hotbox .state .button.enabled:hover .label {
+ color: white;
+}
+
+.hotbox .state .button.enabled:hover .key {
+ color: #fadfdf;
+}
+
+.hotbox .state .button.enabled.selected {
+ animation: selected 0.1s ease;
+ background: #e45d5c;
+}
+
+.hotbox .state .button.enabled.selected .label {
+ color: white;
+}
+
+.hotbox .state .button.enabled.selected .key {
+ color: #fadfdf;
+}
+
+.hotbox .state .button.enabled.pressed,
+.hotbox .state .button.enabled:active {
+ background: #ff974d;
+}
+
+.hotbox .state .button.enabled.pressed .label,
+.hotbox .state .button.enabled:active .label {
+ color: white;
+}
+
+.hotbox .state .button.enabled.pressed .key,
+.hotbox .state .button.enabled:active .key {
+ color: #fff0e6;
+}
+
+.hotbox .state.active {
+ display: block;
+}
+
+@keyframes selected {
+ 0% {
+ transform: scale(1);
+ }
+
+ 50% {
+ transform: scale(1.1);
+ }
+
+ 100% {
+ transform: scale(1);
+ }
+}
+
+.hotbox-key-receiver {
+ position: absolute;
+ top: -999999px;
+ left: -999999px;
+ width: 20px;
+ height: 20px;
+ margin: 0;
+ outline: none;
+}
diff --git a/packages/client/src/styles/kityminder.editor.css b/packages/client/src/styles/kityminder.editor.css
new file mode 100644
index 0000000..315aa7d
--- /dev/null
+++ b/packages/client/src/styles/kityminder.editor.css
@@ -0,0 +1,1855 @@
+/* stylelint-disable */
+.km-editor {
+ position: relative;
+ z-index: 2;
+ overflow: hidden;
+}
+
+.km-editor > .mask {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ display: block;
+ background-color: transparent;
+}
+
+.km-editor > .receiver {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: -1000;
+ width: auto;
+ max-width: 300px;
+ min-height: 1.4em;
+ padding: 3px 5px;
+ margin-top: -5px;
+ margin-left: -3px;
+ overflow: hidden;
+ font-size: 14px;
+ line-height: 1.4em;
+ word-break: break-all;
+ word-wrap: break-word;
+ pointer-events: none;
+ background: white;
+ border: none;
+ outline: none;
+ opacity: 0;
+ box-shadow: 0 0 20px rgb(0 0 0 / 50%);
+ box-sizing: border-box;
+ user-select: text;
+}
+
+.km-editor > .receiver.debug {
+ z-index: 0;
+ background: none;
+ outline: 1px solid green;
+ opacity: 1;
+}
+
+.km-editor > .receiver.input {
+ z-index: 999;
+ pointer-events: all;
+ background: white;
+ outline: none;
+ opacity: 1;
+}
+
+div.minder-editor-container {
+ position: absolute;
+ top: 40px;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ font-family: Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 'WenQuanYi Micro Hei', sans-serif;
+}
+
+.minder-editor {
+ position: absolute;
+ top: 92px;
+ right: 0;
+ bottom: 0;
+ left: 0;
+}
+
+.minder-viewer {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+}
+
+.control-panel {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ width: 250px;
+ border-left: 1px solid #ccc;
+}
+
+.minder-divider {
+ position: absolute;
+ top: 0;
+ right: 250px;
+ bottom: 0;
+ width: 2px;
+ cursor: ew-resize;
+ background-color: #fbfbfb;
+}
+
+.panel-body {
+ padding: 10px;
+}
+
+.upload-image {
+ position: absolute;
+ z-index: -1;
+ width: 0.1px;
+ height: 0.1px;
+ overflow: hidden;
+ opacity: 0;
+}
+
+.top-tab .nav-tabs {
+ height: 32px;
+ background-color: #e1e1e1;
+ border: 0;
+}
+
+.top-tab .nav-tabs li {
+ margin: 0;
+}
+
+.top-tab .nav-tabs li a {
+ padding: 6px 15px;
+ margin: 0;
+ vertical-align: middle;
+ border: 0;
+ border-radius: 0;
+}
+
+.top-tab .nav-tabs li a:hover,
+.top-tab .nav-tabs li a:focus {
+ background: inherit;
+ border: 0;
+}
+
+.top-tab .nav-tabs li.active a {
+ background-color: #fff;
+ border: 0;
+}
+
+.top-tab .nav-tabs li.active a:hover,
+.top-tab .nav-tabs li.active a:focus {
+ border: 0;
+}
+
+.top-tab .tab-content {
+ height: 60px;
+ background-color: #fff;
+ border-bottom: 1px solid #dbdbdb;
+}
+
+.top-tab .tab-pane {
+ font-size: 0;
+}
+
+.km-btn-group {
+ display: inline-block;
+ padding: 0 5px;
+ margin: 5px 0;
+ vertical-align: middle;
+ border-right: 1px dashed #eee;
+}
+
+.km-btn-item {
+ display: inline-block;
+ margin: 0 3px;
+ font-size: 0;
+ cursor: default;
+}
+
+.km-btn-item[disabled] {
+ opacity: 0.5;
+}
+
+.km-btn-item[disabled]:hover,
+.km-btn-item[disabled]:active {
+ background-color: #fff;
+}
+
+.km-btn-item .km-btn-icon {
+ display: inline-block;
+
+ /* background: url(images/icons.png) no-repeat; */
+ background-position: 0 20px;
+ width: 20px;
+ height: 20px;
+ padding: 2px;
+ margin: 1px;
+ vertical-align: middle;
+}
+
+.km-btn-item .km-btn-caption {
+ display: inline-block;
+ font-size: 12px;
+ vertical-align: middle;
+}
+
+.km-btn-item:hover {
+ background-color: #eff3fa;
+}
+
+.km-btn-item:active {
+ background-color: #c4d0ee;
+}
+
+.do-group {
+ width: 38px;
+}
+
+.undo .km-btn-icon {
+ background-position: 0 -1240px;
+}
+
+.redo .km-btn-icon {
+ background-position: 0 -1220px;
+}
+
+.append-group {
+ width: 212px;
+}
+
+.append-child-node .km-btn-icon {
+ background-position: 0 0;
+}
+
+.append-sibling-node .km-btn-icon {
+ background-position: 0 -20px;
+}
+
+.append-parent-node .km-btn-icon {
+ background-position: 0 -40px;
+}
+
+.arrange-group {
+ width: 64px;
+}
+
+.arrange-up .km-btn-icon {
+ background-position: 0 -280px;
+}
+
+.arrange-down .km-btn-icon {
+ background-position: 0 -300px;
+}
+
+.operation-group {
+ width: 64px;
+}
+
+.edit-node .km-btn-icon {
+ background-position: 0 -60px;
+}
+
+.remove-node .km-btn-icon {
+ background-position: 0 -80px;
+}
+
+.btn-group-vertical {
+ margin: 5px;
+ vertical-align: middle;
+}
+
+.btn-group-vertical .hyperlink,
+.btn-group-vertical .hyperlink-caption {
+ width: 40px;
+ padding: 0;
+ margin: 0;
+ border: none !important;
+ border-radius: 0 !important;
+}
+
+.btn-group-vertical .hyperlink:hover,
+.btn-group-vertical .hyperlink-caption:hover {
+ background-color: #eff3fa;
+}
+
+.btn-group-vertical .hyperlink:active,
+.btn-group-vertical .hyperlink-caption:active {
+ background-color: #c4d0ee;
+}
+
+.btn-group-vertical .hyperlink.active,
+.btn-group-vertical .hyperlink-caption.active {
+ box-shadow: none;
+ background-color: #eff3fa;
+}
+
+.btn-group-vertical .hyperlink {
+ height: 25px;
+
+ /* background: url(images/icons.png) no-repeat center -100px; */
+}
+
+.btn-group-vertical .hyperlink-caption {
+ height: 20px;
+}
+
+.btn-group-vertical .hyperlink-caption .caption {
+ font-size: 12px;
+}
+
+.open > .dropdown-toggle.btn-default {
+ background-color: #eff3fa;
+}
+
+.btn-group-vertical .image-btn,
+.btn-group-vertical .image-btn-caption {
+ width: 40px;
+ padding: 0;
+ margin: 0;
+ border: none !important;
+ border-radius: 0 !important;
+}
+
+.btn-group-vertical .image-btn:hover,
+.btn-group-vertical .image-btn-caption:hover {
+ background-color: #eff3fa;
+}
+
+.btn-group-vertical .image-btn:active,
+.btn-group-vertical .image-btn-caption:active {
+ background-color: #c4d0ee;
+}
+
+.btn-group-vertical .image-btn.active,
+.btn-group-vertical .image-btn-caption.active {
+ box-shadow: none;
+ background-color: #eff3fa;
+}
+
+.btn-group-vertical .image-btn {
+ height: 25px;
+
+ /* background: url(images/icons.png) no-repeat center -125px; */
+}
+
+.btn-group-vertical .image-btn-caption {
+ height: 20px;
+}
+
+.btn-group-vertical .image-btn-caption .caption {
+ font-size: 12px;
+}
+
+.image-preview {
+ display: block;
+ max-width: 50%;
+}
+
+.modal-body .tab-pane {
+ padding-top: 15px;
+ font-size: inherit;
+}
+
+.search-result {
+ height: 370px;
+ margin-top: 15px;
+ overflow: hidden;
+}
+
+.search-result ul {
+ height: 100%;
+ padding: 0;
+ margin: 0;
+ overflow-x: hidden;
+ overflow-y: auto;
+ clear: both;
+ list-style: none;
+}
+
+.search-result ul li {
+ position: relative;
+ display: block;
+ float: left;
+ width: 130px;
+ height: 130px;
+ padding: 0;
+ margin: 6px;
+ overflow: hidden;
+ font-size: 12px;
+ line-height: 130px;
+ text-align: center;
+ vertical-align: top;
+ list-style: none;
+ cursor: pointer;
+ border: 2px solid #fcfcfc;
+}
+
+.search-result ul li.selected {
+ border: 2px solid #fc8383;
+}
+
+.search-result ul li img {
+ max-width: 126px;
+ max-height: 130px;
+ vertical-align: middle;
+}
+
+.search-result ul li span {
+ position: absolute;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ display: block;
+ height: 20px;
+ overflow: hidden;
+ line-height: 20px;
+ color: white;
+ text-overflow: ellipsis;
+ word-break: break-all;
+ white-space: nowrap;
+ background: rgb(0 0 0 / 50%);
+ opacity: 0;
+ transform: translate(0, 20px);
+ transform: translate(0, 20px);
+ transform: translate(0, 20px);
+ transition: all 0.2s ease;
+ transition: all 0.2s ease;
+}
+
+.search-result ul li:hover span {
+ opacity: 1;
+ transform: translate(0, 0);
+ transform: translate(0, 0);
+ transform: translate(0, 0);
+}
+@media (min-width: 768px) {
+ .form-inline .form-control {
+ width: 422px;
+ }
+}
+
+.btn-group-vertical {
+ margin: 5px;
+ vertical-align: top;
+}
+
+.btn-group-vertical.note-btn-group {
+ border-right: 1px dashed #eee;
+ padding-right: 5px;
+}
+
+.btn-group-vertical .note-btn,
+.btn-group-vertical .note-btn-caption {
+ width: 40px;
+ padding: 0;
+ margin: 0;
+ border: none !important;
+ border-radius: 0 !important;
+}
+
+.btn-group-vertical .note-btn:hover,
+.btn-group-vertical .note-btn-caption:hover {
+ background-color: #eff3fa;
+}
+
+.btn-group-vertical .note-btn:active,
+.btn-group-vertical .note-btn-caption:active {
+ background-color: #c4d0ee;
+}
+
+.btn-group-vertical .note-btn.active,
+.btn-group-vertical .note-btn-caption.active {
+ box-shadow: none;
+ background-color: #eff3fa;
+}
+
+.btn-group-vertical .note-btn {
+ height: 25px;
+
+ /* background: url(images/icons.png) no-repeat center -1150px; */
+}
+
+.btn-group-vertical .note-btn-caption {
+ height: 20px;
+}
+
+.btn-group-vertical .note-btn-caption .caption {
+ font-size: 12px;
+}
+
+.open > .dropdown-toggle.btn-default {
+ background-color: #eff3fa;
+}
+
+.gfm-render {
+ font-size: 12px;
+ line-height: 1.8em;
+ color: #333;
+ user-select: text;
+}
+
+.gfm-render blockquote,
+.gfm-render ul,
+.gfm-render table,
+.gfm-render p,
+.gfm-render pre,
+.gfm-render hr {
+ margin: 1em 0;
+ cursor: text;
+}
+
+.gfm-render blockquote:first-child:last-child,
+.gfm-render ul:first-child:last-child,
+.gfm-render table:first-child:last-child,
+.gfm-render p:first-child:last-child,
+.gfm-render pre:first-child:last-child,
+.gfm-render hr:first-child:last-child {
+ margin: 0;
+}
+
+.gfm-render img {
+ max-width: 100%;
+}
+
+.gfm-render a {
+ color: blue;
+}
+
+.gfm-render a:hover {
+ color: red;
+}
+
+.gfm-render blockquote {
+ display: block;
+ padding-left: 10px;
+ margin-left: 2em;
+ font-style: italic;
+ color: #da8e68;
+ border-left: 4px solid #e4ad91;
+}
+
+.gfm-render ul,
+.gfm-render ol {
+ padding-left: 3em;
+}
+
+.gfm-render table {
+ width: 100%;
+ border-collapse: collapse;
+ margin: 1em 0;
+}
+
+.gfm-render table th,
+.gfm-render table td {
+ padding: 2px 4px;
+ border: 1px solid #666;
+}
+
+.gfm-render table th {
+ background: rgb(45 141 234 / 20%);
+}
+
+.gfm-render table tr:nth-child(even) td {
+ background: rgb(45 141 234 / 3%);
+}
+
+.gfm-render em {
+ color: red;
+}
+
+.gfm-render del {
+ color: #999;
+}
+
+.gfm-render pre {
+ padding: 5px;
+ word-break: break-all;
+ word-wrap: break-word;
+ background: rgb(45 141 234 / 10%);
+ border-radius: 5px;
+}
+
+.gfm-render code {
+ /* display: inline-block; */
+ padding: 0 5px;
+ background: rgb(45 141 234 / 10%);
+ border-radius: 3px;
+}
+
+.gfm-render pre code {
+ background: none;
+}
+
+.gfm-render hr {
+ border: none;
+ border-top: 1px solid #ccc;
+}
+
+.gfm-render .highlight {
+ color: red;
+ background: yellow;
+}
+
+.km-note {
+ position: absolute;
+ top: 92px;
+ right: 0;
+ bottom: 0;
+ left: auto;
+ z-index: 3;
+ width: 300px;
+ padding: 5px 10px;
+ background: white;
+ border-left: 1px solid #babfcd;
+}
+
+.km-note.panel {
+ padding: 0;
+ margin: 0;
+}
+
+.km-note.panel .panel-heading h3 {
+ display: inline-block;
+}
+
+.km-note.panel .panel-heading .close-note-editor {
+ display: inline-block;
+ float: right;
+ width: 15px;
+ height: 15px;
+}
+
+.km-note.panel .panel-heading .close-note-editor:hover {
+ cursor: pointer;
+}
+
+.km-note.panel .panel-body {
+ padding: 0;
+}
+
+.km-note .CodeMirror {
+ position: absolute;
+ top: 41px;
+ bottom: 0;
+ height: auto;
+ font-family: consolas;
+ font-size: 14px;
+ line-height: 1.3em;
+ cursor: text;
+}
+
+.km-note-tips {
+ padding: 3px 8px;
+ color: #ccc;
+}
+
+#previewer-content {
+ position: absolute;
+ z-index: 10;
+ max-width: 400px;
+ max-height: 200px;
+ padding: 5px 15px;
+ overflow: auto;
+ font-size: 12px;
+ line-height: 1.8em;
+ color: #333;
+ word-break: break-all;
+ background: #ffd;
+ border-radius: 5px;
+ box-shadow: 0 0 15px rgb(0 0 0 / 50%);
+ user-select: text;
+}
+
+#previewer-content blockquote,
+#previewer-content ul,
+#previewer-content table,
+#previewer-content p,
+#previewer-content pre,
+#previewer-content hr {
+ margin: 1em 0;
+ cursor: text;
+}
+
+#previewer-content blockquote:first-child:last-child,
+#previewer-content ul:first-child:last-child,
+#previewer-content table:first-child:last-child,
+#previewer-content p:first-child:last-child,
+#previewer-content pre:first-child:last-child,
+#previewer-content hr:first-child:last-child {
+ margin: 0;
+}
+
+#previewer-content img {
+ max-width: 100%;
+}
+
+#previewer-content a {
+ color: blue;
+}
+
+#previewer-content a:hover {
+ color: red;
+}
+
+#previewer-content blockquote {
+ display: block;
+ padding-left: 10px;
+ margin-left: 2em;
+ font-style: italic;
+ color: #da8e68;
+ border-left: 4px solid #e4ad91;
+}
+
+#previewer-content ul,
+#previewer-content ol {
+ padding-left: 3em;
+}
+
+#previewer-content table {
+ width: 100%;
+ border-collapse: collapse;
+ margin: 1em 0;
+}
+
+#previewer-content table th,
+#previewer-content table td {
+ padding: 2px 4px;
+ border: 1px solid #666;
+}
+
+#previewer-content table th {
+ background: rgb(45 141 234 / 20%);
+}
+
+#previewer-content table tr:nth-child(even) td {
+ background: rgb(45 141 234 / 3%);
+}
+
+#previewer-content em {
+ color: red;
+}
+
+#previewer-content del {
+ color: #999;
+}
+
+#previewer-content pre {
+ padding: 5px;
+ word-break: break-all;
+ word-wrap: break-word;
+ background: rgb(45 141 234 / 10%);
+ border-radius: 5px;
+}
+
+#previewer-content code {
+ /* display: inline-block; */
+ padding: 0 5px;
+ background: rgb(45 141 234 / 10%);
+ border-radius: 3px;
+}
+
+#previewer-content pre code {
+ background: none;
+}
+
+#previewer-content hr {
+ border: none;
+ border-top: 1px solid #ccc;
+}
+
+#previewer-content .highlight {
+ color: red;
+ background: yellow;
+}
+
+#previewer-content.ng-hide {
+ top: -99999px !important;
+ left: -99999px !important;
+ display: block !important;
+}
+
+.panel-body {
+ padding: 10px;
+}
+
+.tab-content .km-priority {
+ display: inline-block;
+ width: 140px;
+ margin: 5px;
+ font-size: inherit;
+ vertical-align: middle;
+ border-right: 1px dashed #eee;
+}
+
+.tab-content .km-priority .km-priority-item {
+ padding: 1px;
+ margin: 0 1px;
+}
+
+.tab-content .km-priority .km-priority-item .km-priority-icon {
+ /* background: url(images/iconpriority.png) repeat-y; */
+ background-color: transparent;
+}
+
+.tab-content .km-priority .km-priority-item .km-priority-icon.priority-0 {
+ background-position: 0 20px;
+}
+
+.tab-content .km-priority .km-priority-item .km-priority-icon.priority-1 {
+ background-position: 0 0;
+}
+
+.tab-content .km-priority .km-priority-item .km-priority-icon.priority-2 {
+ background-position: 0 -20px;
+}
+
+.tab-content .km-priority .km-priority-item .km-priority-icon.priority-3 {
+ background-position: 0 -40px;
+}
+
+.tab-content .km-priority .km-priority-item .km-priority-icon.priority-4 {
+ background-position: 0 -60px;
+}
+
+.tab-content .km-priority .km-priority-item .km-priority-icon.priority-5 {
+ background-position: 0 -80px;
+}
+
+.tab-content .km-priority .km-priority-item .km-priority-icon.priority-6 {
+ background-position: 0 -100px;
+}
+
+.tab-content .km-priority .km-priority-item .km-priority-icon.priority-7 {
+ background-position: 0 -120px;
+}
+
+.tab-content .km-priority .km-priority-item .km-priority-icon.priority-8 {
+ background-position: 0 -140px;
+}
+
+.tab-content .km-priority .km-priority-item .km-priority-icon.priority-9 {
+ background-position: 0 -160px;
+}
+
+.tab-content .km-progress {
+ display: inline-block;
+ width: 140px;
+ margin: 5px;
+ font-size: inherit;
+ vertical-align: middle;
+ border-right: 1px dashed #eee;
+}
+
+.tab-content .km-progress .km-progress-item {
+ padding: 1px;
+ margin: 0 1px;
+}
+
+.tab-content .km-progress .km-progress-item .km-progress-icon {
+ /* background: url(images/iconprogress.png) repeat-y; */
+ background-color: transparent;
+}
+
+.tab-content .km-progress .km-progress-item .km-progress-icon.progress-0 {
+ background-position: 0 20px;
+}
+
+.tab-content .km-progress .km-progress-item .km-progress-icon.progress-1 {
+ background-position: 0 0;
+}
+
+.tab-content .km-progress .km-progress-item .km-progress-icon.progress-2 {
+ background-position: 0 -20px;
+}
+
+.tab-content .km-progress .km-progress-item .km-progress-icon.progress-3 {
+ background-position: 0 -40px;
+}
+
+.tab-content .km-progress .km-progress-item .km-progress-icon.progress-4 {
+ background-position: 0 -60px;
+}
+
+.tab-content .km-progress .km-progress-item .km-progress-icon.progress-5 {
+ background-position: 0 -80px;
+}
+
+.tab-content .km-progress .km-progress-item .km-progress-icon.progress-6 {
+ background-position: 0 -100px;
+}
+
+.tab-content .km-progress .km-progress-item .km-progress-icon.progress-7 {
+ background-position: 0 -120px;
+}
+
+.tab-content .km-progress .km-progress-item .km-progress-icon.progress-8 {
+ background-position: 0 -140px;
+}
+
+.tab-content .km-progress .km-progress-item .km-progress-icon.progress-9 {
+ background-position: 0 -160px;
+}
+
+.resource-editor {
+ display: inline-block;
+ margin: 5px;
+ vertical-align: middle;
+}
+
+.resource-editor .input-group,
+.resource-editor .km-resource {
+ font-size: 12px;
+}
+
+.resource-editor .input-group {
+ width: 168px;
+ height: 20px;
+}
+
+.resource-editor .resource-dropdown {
+ position: relative;
+ width: 168px;
+ margin-top: -1px;
+ border: 1px solid #ccc;
+ border-bottom-right-radius: 4px;
+ border-bottom-left-radius: 4px;
+}
+
+.resource-editor .resource-dropdown .km-resource {
+ position: absolute;
+ width: 154px;
+ max-height: 500px;
+ padding: 0;
+ margin-bottom: 3px;
+ overflow: scroll;
+ list-style-type: none;
+}
+
+.resource-editor .resource-dropdown .km-resource.open {
+ z-index: 3;
+ background-color: #fff;
+}
+
+.resource-editor .resource-dropdown .km-resource li {
+ display: inline-block;
+ padding: 1px 2px;
+ margin: 2px 3px;
+ border-radius: 4px;
+}
+
+.resource-editor .resource-dropdown .km-resource li[disabled] {
+ opacity: 0.5;
+}
+
+.resource-editor .resource-dropdown .resource-caret {
+ display: block;
+ float: right;
+ width: 12px;
+ height: 24px;
+ padding: 8px 1px;
+ vertical-align: middle;
+}
+
+.resource-editor .resource-dropdown .resource-caret:hover {
+ background-color: #eff3fa;
+}
+
+.resource-editor .resource-dropdown .resource-caret:active {
+ background-color: #c4d0ee;
+}
+
+.resource-editor input.form-control,
+.resource-editor .btn {
+ font-size: 12px;
+}
+
+.resource-editor input.form-control {
+ height: 24px;
+ padding: 2px 4px;
+ border-bottom-left-radius: 0;
+}
+
+.resource-editor .input-group-btn {
+ line-height: 24px;
+}
+
+.resource-editor .input-group-btn .btn {
+ height: 24px;
+ padding: 2px 4px;
+ border-bottom-right-radius: 0;
+}
+
+.temp-panel {
+ display: inline-block;
+ margin: 5px 5px 5px 10px;
+ vertical-align: middle;
+ border-right: 1px dashed #eee;
+}
+
+.temp-list {
+ min-width: 124px;
+}
+
+.temp-item-wrap {
+ display: inline-block;
+ width: 50px;
+ height: 40px;
+ padding: 0 2px;
+ margin: 5px;
+}
+
+.temp-item {
+ display: inline-block;
+ width: 50px;
+ height: 40px;
+
+ /* background-image: url(images/template.png); */
+ background-repeat: no-repeat;
+}
+
+.temp-item.default {
+ background-position: 0 0;
+}
+
+.temp-item.structure {
+ background-position: -50px 0;
+}
+
+.temp-item.filetree {
+ background-position: -100px 0;
+}
+
+.temp-item.right {
+ background-position: -150px 0;
+}
+
+.temp-item.fish-bone {
+ background-position: -200px 0;
+}
+
+.temp-item.tianpan {
+ background-position: -250px 0;
+}
+
+.current-temp-item {
+ width: 74px;
+ padding: 0 0 0 5px;
+ border: 1px solid #fff;
+}
+
+.current-temp-item:hover {
+ background-color: #eff3fa;
+}
+
+.current-temp-item[disabled] {
+ opacity: 0.5;
+}
+
+.current-temp-item .caret {
+ margin-left: 5px;
+}
+
+.temp-item-selected {
+ background-color: #87a9da;
+}
+
+.theme-panel {
+ display: inline-block;
+ height: 42px;
+ padding: 0 5px 0 0;
+ margin: 5px;
+ vertical-align: middle;
+ border-right: 1px dashed #eee;
+}
+
+.theme-list {
+ min-width: 162px;
+}
+
+div a.theme-item {
+ display: inline-block;
+ width: 70px;
+ height: 30px;
+ padding: 0 5px;
+ font-size: 12px;
+ line-height: 30px;
+ color: #000;
+ text-align: center;
+ text-decoration: none;
+ cursor: pointer;
+}
+
+.theme-item-selected {
+ width: 100px;
+ padding: 6px 7px;
+ border: 1px solid #fff;
+}
+
+.theme-item-selected:hover {
+ background-color: #eff3fa;
+}
+
+.theme-item-selected .caret {
+ margin-left: 5px;
+}
+
+.theme-item-selected[disabled] {
+ opacity: 0.5;
+}
+
+.theme-item-wrap {
+ display: inline-block;
+ width: 80px;
+ height: 40px;
+ padding: 5px;
+}
+
+.theme-item-wrap:hover {
+ background-color: #eff3fa;
+}
+
+.readjust-layout {
+ display: inline-block;
+ padding: 0 10px 0 5px;
+ vertical-align: middle;
+ border-right: 1px dashed #eee;
+}
+
+.btn-icon {
+ display: block;
+ width: 25px;
+ height: 25px;
+ margin-left: 12px;
+}
+
+.btn-label {
+ font-size: 12px;
+}
+
+.btn-wrap {
+ display: inline-block;
+ width: 50px;
+ height: 42px;
+ text-decoration: none;
+ cursor: pointer;
+}
+
+.btn-wrap[disabled] span {
+ opacity: 0.5;
+}
+
+.btn-wrap[disabled] {
+ cursor: default;
+}
+
+.btn-wrap[disabled]:hover {
+ background-color: transparent;
+}
+
+.btn-wrap[disabled]:active {
+ background-color: transparent;
+}
+
+.btn-wrap:link {
+ text-decoration: none;
+}
+
+.btn-wrap:visited {
+ text-decoration: none;
+}
+
+.btn-wrap:hover {
+ text-decoration: none;
+ background-color: #eff3fa;
+}
+
+.btn-wrap:active {
+ background-color: #c4d0ee;
+}
+
+.reset-layout-icon {
+ /* background: url(images/icons.png) no-repeat; */
+ background-position: 0 -150px;
+}
+
+.style-operator {
+ display: inline-block;
+ padding: 0 5px;
+ vertical-align: middle;
+ border-right: 1px dashed #eee;
+}
+
+.style-operator .clear-style {
+ vertical-align: middle;
+}
+
+.clear-style-icon {
+ /* background: url(images/icons.png) no-repeat; */
+ background-position: 0 -175px;
+}
+
+.s-btn-group-vertical {
+ display: inline-block;
+ vertical-align: middle;
+}
+
+.s-btn-icon {
+ display: inline-block;
+ width: 20px;
+ height: 20px;
+ margin-right: 3px;
+ vertical-align: middle;
+}
+
+.s-btn-label {
+ display: inline-block;
+ font-size: 12px;
+ vertical-align: middle;
+}
+
+.s-btn-wrap {
+ display: inline-block;
+ padding: 0 5px 0 3px;
+ font-size: 0;
+ text-decoration: none;
+}
+
+.s-btn-wrap[disabled] span {
+ opacity: 0.5;
+}
+
+.s-btn-wrap[disabled] {
+ cursor: default;
+}
+
+.s-btn-wrap[disabled]:hover {
+ background-color: transparent;
+}
+
+.s-btn-wrap[disabled]:active {
+ background-color: transparent;
+}
+
+.s-btn-wrap:hover {
+ text-decoration: none;
+ background-color: #eff3fa;
+}
+
+.s-btn-wrap:active {
+ background-color: #c4d0ee;
+}
+
+.copy-style-icon {
+ /* background: url(images/icons.png) no-repeat; */
+ background-position: 0 -200px;
+}
+
+.paste-style-wrap {
+ display: block;
+}
+
+.paste-style-icon {
+ /* background: url(images/icons.png) no-repeat; */
+ background-position: 0 -220px;
+}
+
+.font-operator {
+ display: inline-block;
+ width: 170px;
+ padding: 0 5px;
+ font-size: 12px;
+ vertical-align: middle;
+}
+
+.font-operator .font-size-list {
+ display: inline-block;
+ padding: 2px 4px;
+ border: 1px solid #eee;
+}
+
+.font-operator .font-family-list {
+ display: inline-block;
+ padding: 2px 4px;
+ border: 1px solid #eee;
+}
+
+.current-font-item a {
+ display: inline-block;
+ text-decoration: none;
+}
+
+.current-font-family {
+ width: 75px;
+ height: 18px;
+ overflow: hidden;
+ vertical-align: bottom;
+}
+
+.current-font-size {
+ width: 32px;
+ height: 18px;
+ overflow: hidden;
+ vertical-align: bottom;
+}
+
+.current-font-item[disabled] {
+ opacity: 0.5;
+}
+
+.font-item {
+ line-height: 1em;
+ text-align: left;
+}
+
+.font-item-selected {
+ background-color: #87a9da;
+}
+
+.font-bold,
+.font-italics {
+ display: inline-block;
+ margin: 0 3px;
+
+ /* background: url(images/icons.png) no-repeat; */
+ cursor: pointer;
+}
+
+.font-bold:hover,
+.font-italics:hover {
+ background-color: #eff3fa;
+}
+
+.font-bold:active,
+.font-italics:active {
+ background-color: #c4d0ee;
+}
+
+.font-bold[disabled],
+.font-italics[disabled] {
+ opacity: 0.5;
+}
+
+.font-bold {
+ background-position: 0 -240px;
+}
+
+.font-italics {
+ background-position: 0 -260px;
+}
+
+.font-bold-selected,
+.font-italics-selected {
+ background-color: #87a9da;
+}
+
+.font-color-wrap {
+ display: inline-block;
+ width: 30px;
+ height: 22px;
+ margin: 3px 3px 0 0;
+ font-size: 0;
+ vertical-align: middle;
+ border: 1px #efefef solid;
+ user-select: none;
+ user-select: none;
+ user-select: none;
+ user-select: none;
+}
+
+.font-color-wrap[disabled] {
+ opacity: 0.5;
+}
+
+.font-color-wrap .quick-font-color {
+ display: inline-block;
+ width: 20px;
+ height: 16px;
+ font-size: 14px;
+ line-height: 16px;
+ color: #000;
+ text-align: center;
+ vertical-align: top;
+ cursor: default;
+}
+
+.font-color-wrap .quick-font-color:hover {
+ background-color: #eff3fa;
+}
+
+.font-color-wrap .quick-font-color:active {
+ background-color: #c4d0ee;
+}
+
+.font-color-wrap .quick-font-color[disabled] {
+ opacity: 0.5;
+}
+
+.font-color-wrap .font-color-preview {
+ display: inline-block;
+ width: 12px;
+ height: 2px;
+ margin: 0 4px;
+ background-color: #000;
+}
+
+.font-color-wrap .font-color-preview[disabled] {
+ opacity: 0.5;
+}
+
+.font-color {
+ display: inline-block;
+ width: 8px;
+ height: 16px;
+}
+
+.font-color:hover {
+ background-color: #eff3fa;
+}
+
+.font-color:active {
+ background-color: #c4d0ee;
+}
+
+.font-color[disabled] {
+ opacity: 0.5;
+}
+
+.font-color .caret {
+ margin-top: 7px;
+ margin-left: -2px;
+}
+
+.bg-color-wrap {
+ display: inline-block;
+ width: 30px;
+ height: 22px;
+ margin: 3px 3px 0 0;
+ font-size: 0;
+ vertical-align: middle;
+ border: 1px #efefef solid;
+ user-select: none;
+ user-select: none;
+ user-select: none;
+ user-select: none;
+}
+
+.bg-color-wrap[disabled] {
+ opacity: 0.5;
+}
+
+.bg-color-wrap .quick-bg-color {
+ display: inline-block;
+ width: 20px;
+ height: 16px;
+ font-size: 14px;
+ line-height: 16px;
+ color: #000;
+ text-align: center;
+ vertical-align: top;
+ cursor: default;
+
+ /* background: url(images/icons.png) no-repeat center -1260px; */
+}
+
+.bg-color-wrap .quick-bg-color:hover {
+ background-color: #eff3fa;
+}
+
+.bg-color-wrap .quick-bg-color:active {
+ background-color: #c4d0ee;
+}
+
+.bg-color-wrap .quick-bg-color[disabled] {
+ opacity: 0.5;
+}
+
+.bg-color-wrap .bg-color-preview {
+ display: inline-block;
+ width: 12px;
+ height: 2px;
+ margin: 0 4px;
+ background-color: #fff;
+}
+
+.bg-color-wrap .bg-color-preview[disabled] {
+ opacity: 0.5;
+}
+
+.bg-color {
+ display: inline-block;
+ width: 8px;
+ height: 16px;
+}
+
+.bg-color:hover {
+ background-color: #eff3fa;
+}
+
+.bg-color:active {
+ background-color: #c4d0ee;
+}
+
+.bg-color[disabled] {
+ opacity: 0.5;
+}
+
+.bg-color .caret {
+ margin-top: 7px;
+ margin-left: -2px;
+}
+
+.btn-group-vertical {
+ margin: 5px;
+ vertical-align: middle;
+}
+
+.btn-group-vertical .expand,
+.btn-group-vertical .expand-caption {
+ width: 40px;
+ padding: 0;
+ margin: 0;
+ border: none !important;
+ border-radius: 0 !important;
+}
+
+.btn-group-vertical .expand:hover,
+.btn-group-vertical .expand-caption:hover {
+ background-color: #eff3fa;
+}
+
+.btn-group-vertical .expand:active,
+.btn-group-vertical .expand-caption:active {
+ background-color: #c4d0ee;
+}
+
+.btn-group-vertical .expand.active,
+.btn-group-vertical .expand-caption.active {
+ box-shadow: none;
+ background-color: #eff3fa;
+}
+
+.btn-group-vertical .expand {
+ height: 25px;
+
+ /* background: url(images/icons.png) no-repeat 0 -995px; */
+ background-position-x: 50%;
+}
+
+.btn-group-vertical .expand-caption {
+ height: 20px;
+}
+
+.btn-group-vertical .expand-caption .caption {
+ font-size: 12px;
+}
+
+.btn-group-vertical {
+ margin: 5px;
+ vertical-align: middle;
+}
+
+.btn-group-vertical .select,
+.btn-group-vertical .select-caption {
+ width: 40px;
+ padding: 0;
+ margin: 0;
+ border: none !important;
+ border-radius: 0 !important;
+}
+
+.btn-group-vertical .select:hover,
+.btn-group-vertical .select-caption:hover {
+ background-color: #eff3fa;
+}
+
+.btn-group-vertical .select:active,
+.btn-group-vertical .select-caption:active {
+ background-color: #c4d0ee;
+}
+
+.btn-group-vertical .select.active,
+.btn-group-vertical .select-caption.active {
+ box-shadow: none;
+ background-color: #eff3fa;
+}
+
+.btn-group-vertical .select {
+ height: 25px;
+
+ /* background: url(images/icons.png) no-repeat 7px -1175px; */
+}
+
+.btn-group-vertical .select-caption {
+ height: 20px;
+}
+
+.btn-group-vertical .select-caption .caption {
+ font-size: 12px;
+}
+
+.btn-group-vertical {
+ margin: 5px;
+ vertical-align: middle;
+}
+
+.btn-group-vertical .search,
+.btn-group-vertical .search-caption {
+ width: 40px;
+ padding: 0;
+ margin: 0;
+ border: none !important;
+ border-radius: 0 !important;
+}
+
+.btn-group-vertical .search:hover,
+.btn-group-vertical .search-caption:hover {
+ background-color: #eff3fa;
+}
+
+.btn-group-vertical .search:active,
+.btn-group-vertical .search-caption:active {
+ background-color: #c4d0ee;
+}
+
+.btn-group-vertical .search.active,
+.btn-group-vertical .search-caption.active {
+ box-shadow: none;
+ background-color: #eff3fa;
+}
+
+.btn-group-vertical .search {
+ height: 25px;
+
+ /* background: url(images/icons.png) no-repeat 0 -345px; */
+ background-position-x: 50%;
+}
+
+.btn-group-vertical .search-caption {
+ height: 20px;
+}
+
+.btn-group-vertical .search-caption .caption {
+ font-size: 12px;
+}
+
+.search-box {
+ position: relative;
+ top: 0;
+ z-index: 3;
+ float: right;
+ width: 360px;
+ height: 40px;
+ padding: 3px 6px;
+ background-color: #fff;
+ border: 1px solid #dbdbdb;
+ opacity: 1;
+}
+
+.search-box .search-input-wrap,
+.search-box .prev-and-next-btn {
+ float: left;
+}
+
+.search-box .close-search {
+ float: right;
+ width: 16px;
+ height: 16px;
+ padding: 1px;
+ margin-top: 6px;
+ margin-right: 10px;
+ border-radius: 100%;
+}
+
+.search-box .close-search .glyphicon {
+ top: -1px;
+}
+
+.search-box .close-search:hover {
+ background-color: #efefef;
+}
+
+.search-box .close-search:active {
+ background-color: #999;
+}
+
+.search-box .search-input-wrap {
+ width: 240px;
+}
+
+.search-box .prev-and-next-btn {
+ margin-left: 5px;
+}
+
+.search-box .prev-and-next-btn .btn:focus {
+ outline: none;
+}
+
+.search-box .search-addon {
+ background-color: #fff;
+}
+
+.tool-group {
+ padding: 0;
+}
+
+.tool-group[disabled] {
+ opacity: 0.5;
+}
+
+.tool-group .tool-group-item {
+ display: inline-block;
+ border-radius: 4px;
+}
+
+.tool-group .tool-group-item .tool-group-icon {
+ width: 20px;
+ height: 20px;
+ padding: 2px;
+ margin: 1px;
+}
+
+.tool-group .tool-group-item:hover {
+ background-color: #eff3fa;
+}
+
+.tool-group .tool-group-item:active {
+ background-color: #c4d0ee;
+}
+
+.tool-group .tool-group-item.active {
+ background-color: #c4d0ee;
+}
+
+.nav-bar {
+ position: absolute;
+ bottom: 10px;
+ left: 10px;
+ z-index: 10;
+ width: 35px;
+ height: 240px;
+ padding: 5px 0;
+ color: #fff;
+ background: #fc8383;
+ border-radius: 4px;
+ box-shadow: 3px 3px 10px rgb(0 0 0 / 20%);
+ transition: -webkit-transform 0.7s 0.1s ease;
+ transition: transform 0.7s 0.1s ease;
+}
+
+.nav-bar .nav-btn {
+ width: 35px;
+ height: 24px;
+ line-height: 24px;
+ text-align: center;
+}
+
+.nav-bar .nav-btn .icon {
+ display: block;
+
+ /* background: url(images/icons.png); */
+ width: 20px;
+ height: 20px;
+ margin: 2px auto;
+}
+
+.nav-bar .nav-btn.active {
+ background-color: #5a6378;
+}
+
+.nav-bar .zoom-in .icon {
+ background-position: 0 -730px;
+}
+
+.nav-bar .zoom-out .icon {
+ background-position: 0 -750px;
+}
+
+.nav-bar .hand .icon {
+ background-position: 0 -770px;
+ width: 25px;
+ height: 25px;
+ margin: 0 auto;
+}
+
+.nav-bar .camera .icon {
+ background-position: 0 -870px;
+ width: 25px;
+ height: 25px;
+ margin: 0 auto;
+}
+
+.nav-bar .nav-trigger .icon {
+ background-position: 0 -845px;
+ width: 25px;
+ height: 25px;
+ margin: 0 auto;
+}
+
+.nav-bar .zoom-pan {
+ position: relative;
+ width: 2px;
+ height: 70px;
+ margin: 3px auto;
+ overflow: visible;
+ background: white;
+ box-shadow: 0 1px #e50000;
+}
+
+.nav-bar .zoom-pan .origin {
+ position: absolute;
+ left: -9px;
+ width: 20px;
+ height: 8px;
+ margin-top: -4px;
+ background: transparent;
+}
+
+.nav-bar .zoom-pan .origin::after {
+ position: absolute;
+ top: 3px;
+ left: 7px;
+ display: block;
+ width: 6px;
+ height: 2px;
+ background: white;
+ content: ' ';
+}
+
+.nav-bar .zoom-pan .indicator {
+ position: absolute;
+ left: -3px;
+ width: 8px;
+ height: 8px;
+ margin-top: -4px;
+ background: white;
+ border-radius: 100%;
+}
+
+.nav-previewer {
+ position: absolute;
+ bottom: 30px;
+ left: 45px;
+ z-index: 9;
+ width: 140px;
+ height: 120px;
+ padding: 1px;
+ cursor: crosshair;
+ background: #fff;
+ border-radius: 0 2px 2px 0;
+ box-shadow: 0 0 8px rgb(0 0 0 / 20%);
+ transition: -webkit-transform 0.7s 0.1s ease;
+ transition: transform 0.7s 0.1s ease;
+}
+
+.nav-previewer.grab {
+ cursor: move;
+ cursor: grabbing;
+ cursor: grabbing;
+ cursor: grabbing;
+}
diff --git a/packages/client/src/styles/reset.scss b/packages/client/src/styles/reset.scss
index acd494a..5692b88 100644
--- a/packages/client/src/styles/reset.scss
+++ b/packages/client/src/styles/reset.scss
@@ -81,8 +81,8 @@ select {
*,
*::before,
*::after {
- transition-duration: .01ms !important;
- animation-duration: .01ms !important;
+ transition-duration: 0.01ms !important;
+ animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
scroll-behavior: auto !important;
}
@@ -95,8 +95,8 @@ select {
::-webkit-scrollbar-thumb {
background-color: transparent;
- border-radius: .5rem;
- transition: all .2s;
+ border-radius: 0.5rem;
+ transition: all 0.2s;
}
*:hover {
diff --git a/packages/client/src/tiptap/extensions/mind.ts b/packages/client/src/tiptap/extensions/mind.ts
index f5a94a8..ed85aeb 100644
--- a/packages/client/src/tiptap/extensions/mind.ts
+++ b/packages/client/src/tiptap/extensions/mind.ts
@@ -4,13 +4,10 @@ import { MindWrapper } from '../wrappers/mind';
import { getDatasetAttribute } from '../utils/dataset';
const DEFAULT_MIND_DATA = {
- meta: {
- name: 'jsMind',
- author: 'think',
- version: '0.2',
- },
- format: 'node_tree',
- data: { id: 'root', topic: '中心节点', children: [] },
+ root: { data: { text: '中心节点' }, children: [] },
+ template: 'default',
+ theme: 'fresh-blue',
+ version: '1.4.43',
};
declare module '@tiptap/core' {
@@ -23,11 +20,10 @@ declare module '@tiptap/core' {
export const Mind = Node.create({
name: 'mind',
- content: '',
- marks: '',
group: 'block',
selectable: true,
atom: true,
+ inline: false,
addAttributes() {
return {
diff --git a/packages/client/src/tiptap/menubar.tsx b/packages/client/src/tiptap/menubar.tsx
index eb19240..64db893 100644
--- a/packages/client/src/tiptap/menubar.tsx
+++ b/packages/client/src/tiptap/menubar.tsx
@@ -38,6 +38,7 @@ import { DocumentReference } from './menus/document-reference';
import { Image } from './menus/image';
import { Iframe } from './menus/iframe';
import { Table } from './menus/table';
+import { Mind } from './menus/mind';
export const MenuBar: React.FC<{ editor: any }> = ({ editor }) => {
if (!editor) {
@@ -94,6 +95,7 @@ export const MenuBar: React.FC<{ editor: any }> = ({ editor }) => {
+
);
diff --git a/packages/client/src/tiptap/menus/document-reference/bubble.tsx b/packages/client/src/tiptap/menus/document-reference/bubble.tsx
index fee80b6..f021f99 100644
--- a/packages/client/src/tiptap/menus/document-reference/bubble.tsx
+++ b/packages/client/src/tiptap/menus/document-reference/bubble.tsx
@@ -38,7 +38,7 @@ export const DocumentReferenceBubbleMenu = ({ editor }) => {
editor.isActive(DocumentReference.name)}
tippyOptions={{ maxWidth: 'calc(100vw - 100px)' }}
>
diff --git a/packages/client/src/tiptap/menus/mind/bubble.tsx b/packages/client/src/tiptap/menus/mind/bubble.tsx
new file mode 100644
index 0000000..241c432
--- /dev/null
+++ b/packages/client/src/tiptap/menus/mind/bubble.tsx
@@ -0,0 +1,34 @@
+import { useCallback } from 'react';
+import { useRouter } from 'next/router';
+import { Space, Button, List, Popover, Typography } from '@douyinfe/semi-ui';
+import { IconEdit, IconDelete } from '@douyinfe/semi-icons';
+import { Tooltip } from 'components/tooltip';
+import { DataRender } from 'components/data-render';
+import { IconDocument } from 'components/icons';
+import { useWikiTocs } from 'data/wiki';
+import { BubbleMenu } from '../../views/bubble-menu';
+import { Mind } from '../../extensions/mind';
+import { Divider } from '../../divider';
+
+const { Text } = Typography;
+
+export const MindBubbleMenu = ({ editor }) => {
+ const deleteNode = useCallback(() => editor.chain().deleteSelection().run(), [editor]);
+
+ return (
+ editor.isActive(Mind.name)}
+ tippyOptions={{ maxWidth: 'calc(100vw - 100px)' }}
+ >
+
+
+
+ } type="tertiary" theme="borderless" size="small" />
+
+
+
+ );
+};
diff --git a/packages/client/src/tiptap/menus/mind/index.tsx b/packages/client/src/tiptap/menus/mind/index.tsx
new file mode 100644
index 0000000..18b558b
--- /dev/null
+++ b/packages/client/src/tiptap/menus/mind/index.tsx
@@ -0,0 +1,15 @@
+import React from 'react';
+import { Editor } from '@tiptap/core';
+import { MindBubbleMenu } from './bubble';
+
+export const Mind: React.FC<{ editor: Editor }> = ({ editor }) => {
+ if (!editor) {
+ return null;
+ }
+
+ return (
+ <>
+
+ >
+ );
+};
diff --git a/packages/client/src/tiptap/styles/base.scss b/packages/client/src/tiptap/styles/base.scss
index 29e4c3d..4205352 100644
--- a/packages/client/src/tiptap/styles/base.scss
+++ b/packages/client/src/tiptap/styles/base.scss
@@ -12,7 +12,7 @@
Arial,
sans-serif;
line-height: 1.74;
- letter-spacing: .008em;
+ letter-spacing: 0.008em;
color: var(--semi-color-text-0);
word-break: break-word;
word-wrap: break-word;
@@ -34,19 +34,19 @@
}
p {
- margin-top: .75rem;
+ margin-top: 0.75rem;
margin-bottom: 0;
font-size: 1em;
font-weight: normal;
line-height: 1.714;
- letter-spacing: -.005em;
+ letter-spacing: -0.005em;
}
blockquote {
box-sizing: border-box;
padding-left: 10px;
border-left: 2px solid var(--semi-color-fill-2);
- margin: .75rem 0;
+ margin: 0.75rem 0;
}
hr {
@@ -84,7 +84,7 @@
body[theme-mode='dark'] {
.ProseMirror {
img {
- opacity: .75;
+ opacity: 0.75;
}
}
}
diff --git a/packages/client/src/tiptap/styles/code.scss b/packages/client/src/tiptap/styles/code.scss
index 4388f9f..830ba53 100644
--- a/packages/client/src/tiptap/styles/code.scss
+++ b/packages/client/src/tiptap/styles/code.scss
@@ -2,7 +2,7 @@
code {
padding: 4px;
font-family: Consolas, Menlo, Courier, monospace;
- font-size: .875rem;
+ font-size: 0.875rem;
line-height: 1.3;
cursor: text;
background-color: var(--semi-color-fill-1);
@@ -25,7 +25,7 @@
padding: 0;
margin: 8px;
overflow: auto;
- font-size: .875rem;
+ font-size: 0.875rem;
line-height: 1.5rem;
color: inherit;
white-space: pre;
diff --git a/packages/client/src/tiptap/styles/collaboration.scss b/packages/client/src/tiptap/styles/collaboration.scss
index 8958070..0f51711 100644
--- a/packages/client/src/tiptap/styles/collaboration.scss
+++ b/packages/client/src/tiptap/styles/collaboration.scss
@@ -12,7 +12,7 @@
position: absolute;
top: -1.4em;
left: -1px;
- padding: .1rem .3rem;
+ padding: 0.1rem 0.3rem;
font-size: 12px;
font-style: normal;
font-weight: 600;
diff --git a/packages/client/src/tiptap/styles/color.scss b/packages/client/src/tiptap/styles/color.scss
index 8b1a607..9b72f96 100644
--- a/packages/client/src/tiptap/styles/color.scss
+++ b/packages/client/src/tiptap/styles/color.scss
@@ -5,8 +5,8 @@
display: inline-block;
width: 1em;
height: 1em;
- margin-right: .1em;
- margin-bottom: .15em;
+ margin-right: 0.1em;
+ margin-bottom: 0.15em;
vertical-align: middle;
background-color: var(--color);
border: 1px solid rgb(128 128 128 / 30%);
diff --git a/packages/client/src/tiptap/styles/index.scss b/packages/client/src/tiptap/styles/index.scss
index 6f07ccb..3c3afa7 100644
--- a/packages/client/src/tiptap/styles/index.scss
+++ b/packages/client/src/tiptap/styles/index.scss
@@ -8,10 +8,10 @@
@import './list.scss';
@import './mention.scss';
@import './menu.scss';
-@import './mind.scss';
@import './node.scss';
@import './placeholder.scss';
@import './search.scss';
@import './selection.scss';
@import './table.scss';
@import './title.scss';
+@import './kityminder.scss';
diff --git a/packages/client/src/tiptap/styles/kityminder.scss b/packages/client/src/tiptap/styles/kityminder.scss
new file mode 100644
index 0000000..9589ab6
--- /dev/null
+++ b/packages/client/src/tiptap/styles/kityminder.scss
@@ -0,0 +1,2140 @@
+/* stylelint-disable */
+.km-view {
+ position: relative;
+ font-family: STHeitiSC-Light, STHeiti, Hei, 'Heiti SC', 'Microsoft Yahei', Arial, sans-serif;
+ user-select: none;
+ user-select: none;
+}
+
+.km-view .km-receiver {
+ position: absolute;
+ top: -99999px;
+ left: -99999px;
+ width: 20px;
+ height: 20px;
+ margin: 0;
+ outline: none;
+}
+
+.km-view image {
+ cursor: zoom-in;
+}
+
+.km-image-viewer {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 99999;
+ background: rgb(0 0 0 / 75%);
+}
+
+.km-image-viewer .km-image-viewer-container {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ overflow: auto;
+ text-align: center;
+ white-space: nowrap;
+}
+
+.km-image-viewer .km-image-viewer-container::before {
+ display: inline-block;
+ width: 0;
+ height: 100%;
+ font-size: 0;
+ vertical-align: middle;
+ content: '';
+}
+
+.km-image-viewer .km-image-viewer-container img {
+ vertical-align: middle;
+ cursor: zoom-out;
+}
+
+.km-image-viewer .km-image-viewer-container img.limited {
+ max-width: 100%;
+ max-height: 100%;
+ cursor: zoom-in;
+}
+
+.km-image-viewer .km-image-viewer-toolbar {
+ z-index: 1;
+ text-align: right;
+ background: rgb(0 0 0 / 75%);
+ transition: all 0.25s;
+}
+
+.km-image-viewer .km-image-viewer-toolbar.hidden {
+ opacity: 0;
+ transform: translate(0, -100%);
+}
+
+.km-image-viewer .km-image-viewer-btn {
+ width: 44px;
+ height: 44px;
+ cursor: pointer;
+ background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjY0IiBoZWlnaHQ9Ijg4IiB2aWV3Qm94PSIwIDAgMjY0IDg4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjx0aXRsZT5kZWZhdWx0LXNraW4gMjwvdGl0bGU+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48Zz48cGF0aCBkPSJNNjcuMDAyIDU5LjV2My43NjhjLTYuMzA3Ljg0LTkuMTg0IDUuNzUtMTAuMDAyIDkuNzMyIDIuMjItMi44MyA1LjU2NC01LjA5OCAxMC4wMDItNS4wOThWNzEuNUw3MyA2NS41ODUgNjcuMDAyIDU5LjV6IiBpZD0iU2hhcGUiIGZpbGw9IiNmZmYiLz48ZyBmaWxsPSIjZmZmIj48cGF0aCBkPSJNMTMgMjl2LTVoMnYzaDN2MmgtNXpNMTMgMTVoNXYyaC0zdjNoLTJ2LTV6TTMxIDE1djVoLTJ2LTNoLTN2LTJoNXpNMzEgMjloLTV2LTJoM3YtM2gydjV6IiBpZD0iU2hhcGUiLz48L2c+PGcgZmlsbD0iI2ZmZiI+PHBhdGggZD0iTTYyIDI0djVoLTJ2LTNoLTN2LTJoNXpNNjIgMjBoLTV2LTJoM3YtM2gydjV6TTcwIDIwdi01aDJ2M2gzdjJoLTV6TTcwIDI0aDV2MmgtM3YzaC0ydi01eiIvPjwvZz48cGF0aCBkPSJNMjAuNTg2IDY2bC01LjY1Ni01LjY1NiAxLjQxNC0xLjQxNEwyMiA2NC41ODZsNS42NTYtNS42NTYgMS40MTQgMS40MTRMMjMuNDE0IDY2bDUuNjU2IDUuNjU2LTEuNDE0IDEuNDE0TDIyIDY3LjQxNGwtNS42NTYgNS42NTYtMS40MTQtMS40MTRMMjAuNTg2IDY2eiIgZmlsbD0iI2ZmZiIvPjxwYXRoIGQ9Ik0xMTEuNzg1IDY1LjAzTDExMCA2My41bDMtMy41aC0xMHYtMmgxMGwtMy0zLjUgMS43ODUtMS40NjhMMTE3IDU5bC01LjIxNSA2LjAzeiIgZmlsbD0iI2ZmZiIvPjxwYXRoIGQ9Ik0xNTIuMjE1IDY1LjAzTDE1NCA2My41bC0zLTMuNWgxMHYtMmgtMTBsMy0zLjUtMS43ODUtMS40NjhMMTQ3IDU5bDUuMjE1IDYuMDN6IiBmaWxsPSIjZmZmIi8+PGc+PHBhdGggaWQ9IlJlY3RhbmdsZS0xMSIgZmlsbD0iI2ZmZiIgZD0iTTE2MC45NTcgMjguNTQzbC0zLjI1LTMuMjUtMS40MTMgMS40MTQgMy4yNSAzLjI1eiIvPjxwYXRoIGQ9Ik0xNTIuNSAyN2MzLjAzOCAwIDUuNS0yLjQ2MiA1LjUtNS41cy0yLjQ2Mi01LjUtNS41LTUuNS01LjUgMi40NjItNS41IDUuNSAyLjQ2MiA1LjUgNS41IDUuNXoiIGlkPSJPdmFsLTEiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxLjUiLz48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMTUwIDIxaDV2MWgtNXoiLz48L2c+PGc+PHBhdGggZD0iTTExNi45NTcgMjguNTQzbC0xLjQxNCAxLjQxNC0zLjI1LTMuMjUgMS40MTQtMS40MTQgMy4yNSAzLjI1eiIgZmlsbD0iI2ZmZiIvPjxwYXRoIGQ9Ik0xMDguNSAyN2MzLjAzOCAwIDUuNS0yLjQ2MiA1LjUtNS41cy0yLjQ2Mi01LjUtNS41LTUuNS01LjUgMi40NjItNS41IDUuNSAyLjQ2MiA1LjUgNS41IDUuNXoiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxLjUiLz48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMTA2IDIxaDV2MWgtNXoiLz48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMTA5LjA0MyAxOS4wMDhsLS4wODUgNS0xLS4wMTcuMDg1LTV6Ii8+PC9nPjwvZz48L2c+PC9zdmc+);
+ border: 0;
+ outline: 0;
+}
+
+.km-image-viewer .km-image-viewer-toolbar {
+ position: absolute;
+ top: 0;
+ right: 0;
+ left: 0;
+}
+
+.km-image-viewer .km-image-viewer-close {
+ background-position: 0 -44px;
+}
+
+.km-editor {
+ position: relative;
+ z-index: 2;
+ overflow: hidden;
+}
+
+.km-editor > .mask {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ display: block;
+ background-color: transparent;
+}
+
+.km-editor > .receiver {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: -1000;
+ width: auto;
+ max-width: 300px;
+ min-height: 1.4em;
+ padding: 3px 5px;
+ margin-top: -5px;
+ margin-left: -3px;
+ overflow: hidden;
+ font-size: 14px;
+ line-height: 1.4em;
+ word-break: break-all;
+ word-wrap: break-word;
+ pointer-events: none;
+ background: white;
+ border: none;
+ outline: none;
+ opacity: 0;
+ box-shadow: 0 0 20px rgb(0 0 0 / 50%);
+ box-sizing: border-box;
+ user-select: text;
+}
+
+.km-editor > .receiver.debug {
+ z-index: 0;
+ background: none;
+ outline: 1px solid green;
+ opacity: 1;
+}
+
+.km-editor > .receiver.input {
+ z-index: 999;
+ pointer-events: all;
+ background: white;
+ outline: none;
+ opacity: 1;
+}
+
+div.minder-editor-container {
+ position: absolute;
+ top: 40px;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ font-family: Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 'WenQuanYi Micro Hei', sans-serif;
+}
+
+.minder-editor {
+ position: absolute;
+ top: 92px;
+ right: 0;
+ bottom: 0;
+ left: 0;
+}
+
+.minder-viewer {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+}
+
+.control-panel {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ width: 250px;
+ border-left: 1px solid #ccc;
+}
+
+.minder-divider {
+ position: absolute;
+ top: 0;
+ right: 250px;
+ bottom: 0;
+ width: 2px;
+ cursor: ew-resize;
+ background-color: #fbfbfb;
+}
+
+.panel-body {
+ padding: 10px;
+}
+
+.upload-image {
+ position: absolute;
+ z-index: -1;
+ width: 0.1px;
+ height: 0.1px;
+ overflow: hidden;
+ opacity: 0;
+}
+
+.top-tab .nav-tabs {
+ height: 32px;
+ background-color: #e1e1e1;
+ border: 0;
+}
+
+.top-tab .nav-tabs li {
+ margin: 0;
+}
+
+.top-tab .nav-tabs li a {
+ padding: 6px 15px;
+ margin: 0;
+ vertical-align: middle;
+ border: 0;
+ border-radius: 0;
+}
+
+.top-tab .nav-tabs li a:hover,
+.top-tab .nav-tabs li a:focus {
+ background: inherit;
+ border: 0;
+}
+
+.top-tab .nav-tabs li.active a {
+ background-color: #fff;
+ border: 0;
+}
+
+.top-tab .nav-tabs li.active a:hover,
+.top-tab .nav-tabs li.active a:focus {
+ border: 0;
+}
+
+.top-tab .tab-content {
+ height: 60px;
+ background-color: #fff;
+ border-bottom: 1px solid #dbdbdb;
+}
+
+.top-tab .tab-pane {
+ font-size: 0;
+}
+
+.km-btn-group {
+ display: inline-block;
+ padding: 0 5px;
+ margin: 5px 0;
+ vertical-align: middle;
+ border-right: 1px dashed #eee;
+}
+
+.km-btn-item {
+ display: inline-block;
+ margin: 0 3px;
+ font-size: 0;
+ cursor: default;
+}
+
+.km-btn-item[disabled] {
+ opacity: 0.5;
+}
+
+.km-btn-item[disabled]:hover,
+.km-btn-item[disabled]:active {
+ background-color: #fff;
+}
+
+.km-btn-item .km-btn-icon {
+ display: inline-block;
+
+ /* background: url(images/icons.png) no-repeat; */
+ background-position: 0 20px;
+ width: 20px;
+ height: 20px;
+ padding: 2px;
+ margin: 1px;
+ vertical-align: middle;
+}
+
+.km-btn-item .km-btn-caption {
+ display: inline-block;
+ font-size: 12px;
+ vertical-align: middle;
+}
+
+.km-btn-item:hover {
+ background-color: #eff3fa;
+}
+
+.km-btn-item:active {
+ background-color: #c4d0ee;
+}
+
+.do-group {
+ width: 38px;
+}
+
+.undo .km-btn-icon {
+ background-position: 0 -1240px;
+}
+
+.redo .km-btn-icon {
+ background-position: 0 -1220px;
+}
+
+.append-group {
+ width: 212px;
+}
+
+.append-child-node .km-btn-icon {
+ background-position: 0 0;
+}
+
+.append-sibling-node .km-btn-icon {
+ background-position: 0 -20px;
+}
+
+.append-parent-node .km-btn-icon {
+ background-position: 0 -40px;
+}
+
+.arrange-group {
+ width: 64px;
+}
+
+.arrange-up .km-btn-icon {
+ background-position: 0 -280px;
+}
+
+.arrange-down .km-btn-icon {
+ background-position: 0 -300px;
+}
+
+.operation-group {
+ width: 64px;
+}
+
+.edit-node .km-btn-icon {
+ background-position: 0 -60px;
+}
+
+.remove-node .km-btn-icon {
+ background-position: 0 -80px;
+}
+
+.btn-group-vertical {
+ margin: 5px;
+ vertical-align: middle;
+}
+
+.btn-group-vertical .hyperlink,
+.btn-group-vertical .hyperlink-caption {
+ width: 40px;
+ padding: 0;
+ margin: 0;
+ border: none !important;
+ border-radius: 0 !important;
+}
+
+.btn-group-vertical .hyperlink:hover,
+.btn-group-vertical .hyperlink-caption:hover {
+ background-color: #eff3fa;
+}
+
+.btn-group-vertical .hyperlink:active,
+.btn-group-vertical .hyperlink-caption:active {
+ background-color: #c4d0ee;
+}
+
+.btn-group-vertical .hyperlink.active,
+.btn-group-vertical .hyperlink-caption.active {
+ box-shadow: none;
+ background-color: #eff3fa;
+}
+
+.btn-group-vertical .hyperlink {
+ height: 25px;
+
+ /* background: url(images/icons.png) no-repeat center -100px; */
+}
+
+.btn-group-vertical .hyperlink-caption {
+ height: 20px;
+}
+
+.btn-group-vertical .hyperlink-caption .caption {
+ font-size: 12px;
+}
+
+.open > .dropdown-toggle.btn-default {
+ background-color: #eff3fa;
+}
+
+.btn-group-vertical .image-btn,
+.btn-group-vertical .image-btn-caption {
+ width: 40px;
+ padding: 0;
+ margin: 0;
+ border: none !important;
+ border-radius: 0 !important;
+}
+
+.btn-group-vertical .image-btn:hover,
+.btn-group-vertical .image-btn-caption:hover {
+ background-color: #eff3fa;
+}
+
+.btn-group-vertical .image-btn:active,
+.btn-group-vertical .image-btn-caption:active {
+ background-color: #c4d0ee;
+}
+
+.btn-group-vertical .image-btn.active,
+.btn-group-vertical .image-btn-caption.active {
+ box-shadow: none;
+ background-color: #eff3fa;
+}
+
+.btn-group-vertical .image-btn {
+ height: 25px;
+
+ /* background: url(images/icons.png) no-repeat center -125px; */
+}
+
+.btn-group-vertical .image-btn-caption {
+ height: 20px;
+}
+
+.btn-group-vertical .image-btn-caption .caption {
+ font-size: 12px;
+}
+
+.image-preview {
+ display: block;
+ max-width: 50%;
+}
+
+.modal-body .tab-pane {
+ padding-top: 15px;
+ font-size: inherit;
+}
+
+.search-result {
+ height: 370px;
+ margin-top: 15px;
+ overflow: hidden;
+}
+
+.search-result ul {
+ height: 100%;
+ padding: 0;
+ margin: 0;
+ overflow-x: hidden;
+ overflow-y: auto;
+ clear: both;
+ list-style: none;
+}
+
+.search-result ul li {
+ position: relative;
+ display: block;
+ float: left;
+ width: 130px;
+ height: 130px;
+ padding: 0;
+ margin: 6px;
+ overflow: hidden;
+ font-size: 12px;
+ line-height: 130px;
+ text-align: center;
+ vertical-align: top;
+ list-style: none;
+ cursor: pointer;
+ border: 2px solid #fcfcfc;
+}
+
+.search-result ul li.selected {
+ border: 2px solid #fc8383;
+}
+
+.search-result ul li img {
+ max-width: 126px;
+ max-height: 130px;
+ vertical-align: middle;
+}
+
+.search-result ul li span {
+ position: absolute;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ display: block;
+ height: 20px;
+ overflow: hidden;
+ line-height: 20px;
+ color: white;
+ text-overflow: ellipsis;
+ word-break: break-all;
+ white-space: nowrap;
+ background: rgb(0 0 0 / 50%);
+ opacity: 0;
+ transform: translate(0, 20px);
+ transform: translate(0, 20px);
+ transform: translate(0, 20px);
+ transition: all 0.2s ease;
+ transition: all 0.2s ease;
+}
+
+.search-result ul li:hover span {
+ opacity: 1;
+ transform: translate(0, 0);
+ transform: translate(0, 0);
+ transform: translate(0, 0);
+}
+@media (min-width: 768px) {
+ .form-inline .form-control {
+ width: 422px;
+ }
+}
+
+.btn-group-vertical {
+ margin: 5px;
+ vertical-align: top;
+}
+
+.btn-group-vertical.note-btn-group {
+ border-right: 1px dashed #eee;
+ padding-right: 5px;
+}
+
+.btn-group-vertical .note-btn,
+.btn-group-vertical .note-btn-caption {
+ width: 40px;
+ padding: 0;
+ margin: 0;
+ border: none !important;
+ border-radius: 0 !important;
+}
+
+.btn-group-vertical .note-btn:hover,
+.btn-group-vertical .note-btn-caption:hover {
+ background-color: #eff3fa;
+}
+
+.btn-group-vertical .note-btn:active,
+.btn-group-vertical .note-btn-caption:active {
+ background-color: #c4d0ee;
+}
+
+.btn-group-vertical .note-btn.active,
+.btn-group-vertical .note-btn-caption.active {
+ box-shadow: none;
+ background-color: #eff3fa;
+}
+
+.btn-group-vertical .note-btn {
+ height: 25px;
+
+ /* background: url(images/icons.png) no-repeat center -1150px; */
+}
+
+.btn-group-vertical .note-btn-caption {
+ height: 20px;
+}
+
+.btn-group-vertical .note-btn-caption .caption {
+ font-size: 12px;
+}
+
+.open > .dropdown-toggle.btn-default {
+ background-color: #eff3fa;
+}
+
+.gfm-render {
+ font-size: 12px;
+ line-height: 1.8em;
+ color: #333;
+ user-select: text;
+}
+
+.gfm-render blockquote,
+.gfm-render ul,
+.gfm-render table,
+.gfm-render p,
+.gfm-render pre,
+.gfm-render hr {
+ margin: 1em 0;
+ cursor: text;
+}
+
+.gfm-render blockquote:first-child:last-child,
+.gfm-render ul:first-child:last-child,
+.gfm-render table:first-child:last-child,
+.gfm-render p:first-child:last-child,
+.gfm-render pre:first-child:last-child,
+.gfm-render hr:first-child:last-child {
+ margin: 0;
+}
+
+.gfm-render img {
+ max-width: 100%;
+}
+
+.gfm-render a {
+ color: blue;
+}
+
+.gfm-render a:hover {
+ color: red;
+}
+
+.gfm-render blockquote {
+ display: block;
+ padding-left: 10px;
+ margin-left: 2em;
+ font-style: italic;
+ color: #da8e68;
+ border-left: 4px solid #e4ad91;
+}
+
+.gfm-render ul,
+.gfm-render ol {
+ padding-left: 3em;
+}
+
+.gfm-render table {
+ width: 100%;
+ border-collapse: collapse;
+ margin: 1em 0;
+}
+
+.gfm-render table th,
+.gfm-render table td {
+ padding: 2px 4px;
+ border: 1px solid #666;
+}
+
+.gfm-render table th {
+ background: rgb(45 141 234 / 20%);
+}
+
+.gfm-render table tr:nth-child(even) td {
+ background: rgb(45 141 234 / 3%);
+}
+
+.gfm-render em {
+ color: red;
+}
+
+.gfm-render del {
+ color: #999;
+}
+
+.gfm-render pre {
+ padding: 5px;
+ word-break: break-all;
+ word-wrap: break-word;
+ background: rgb(45 141 234 / 10%);
+ border-radius: 5px;
+}
+
+.gfm-render code {
+ /* display: inline-block; */
+ padding: 0 5px;
+ background: rgb(45 141 234 / 10%);
+ border-radius: 3px;
+}
+
+.gfm-render pre code {
+ background: none;
+}
+
+.gfm-render hr {
+ border: none;
+ border-top: 1px solid #ccc;
+}
+
+.gfm-render .highlight {
+ color: red;
+ background: yellow;
+}
+
+.km-note {
+ position: absolute;
+ top: 92px;
+ right: 0;
+ bottom: 0;
+ left: auto;
+ z-index: 3;
+ width: 300px;
+ padding: 5px 10px;
+ background: white;
+ border-left: 1px solid #babfcd;
+}
+
+.km-note.panel {
+ padding: 0;
+ margin: 0;
+}
+
+.km-note.panel .panel-heading h3 {
+ display: inline-block;
+}
+
+.km-note.panel .panel-heading .close-note-editor {
+ display: inline-block;
+ float: right;
+ width: 15px;
+ height: 15px;
+}
+
+.km-note.panel .panel-heading .close-note-editor:hover {
+ cursor: pointer;
+}
+
+.km-note.panel .panel-body {
+ padding: 0;
+}
+
+.km-note .CodeMirror {
+ position: absolute;
+ top: 41px;
+ bottom: 0;
+ height: auto;
+ font-family: consolas;
+ font-size: 14px;
+ line-height: 1.3em;
+ cursor: text;
+}
+
+.km-note-tips {
+ padding: 3px 8px;
+ color: #ccc;
+}
+
+#previewer-content {
+ position: absolute;
+ z-index: 10;
+ max-width: 400px;
+ max-height: 200px;
+ padding: 5px 15px;
+ overflow: auto;
+ font-size: 12px;
+ line-height: 1.8em;
+ color: #333;
+ word-break: break-all;
+ background: #ffd;
+ border-radius: 5px;
+ box-shadow: 0 0 15px rgb(0 0 0 / 50%);
+ user-select: text;
+}
+
+#previewer-content blockquote,
+#previewer-content ul,
+#previewer-content table,
+#previewer-content p,
+#previewer-content pre,
+#previewer-content hr {
+ margin: 1em 0;
+ cursor: text;
+}
+
+#previewer-content blockquote:first-child:last-child,
+#previewer-content ul:first-child:last-child,
+#previewer-content table:first-child:last-child,
+#previewer-content p:first-child:last-child,
+#previewer-content pre:first-child:last-child,
+#previewer-content hr:first-child:last-child {
+ margin: 0;
+}
+
+#previewer-content img {
+ max-width: 100%;
+}
+
+#previewer-content a {
+ color: blue;
+}
+
+#previewer-content a:hover {
+ color: red;
+}
+
+#previewer-content blockquote {
+ display: block;
+ padding-left: 10px;
+ margin-left: 2em;
+ font-style: italic;
+ color: #da8e68;
+ border-left: 4px solid #e4ad91;
+}
+
+#previewer-content ul,
+#previewer-content ol {
+ padding-left: 3em;
+}
+
+#previewer-content table {
+ width: 100%;
+ border-collapse: collapse;
+ margin: 1em 0;
+}
+
+#previewer-content table th,
+#previewer-content table td {
+ padding: 2px 4px;
+ border: 1px solid #666;
+}
+
+#previewer-content table th {
+ background: rgb(45 141 234 / 20%);
+}
+
+#previewer-content table tr:nth-child(even) td {
+ background: rgb(45 141 234 / 3%);
+}
+
+#previewer-content em {
+ color: red;
+}
+
+#previewer-content del {
+ color: #999;
+}
+
+#previewer-content pre {
+ padding: 5px;
+ word-break: break-all;
+ word-wrap: break-word;
+ background: rgb(45 141 234 / 10%);
+ border-radius: 5px;
+}
+
+#previewer-content code {
+ /* display: inline-block; */
+ padding: 0 5px;
+ background: rgb(45 141 234 / 10%);
+ border-radius: 3px;
+}
+
+#previewer-content pre code {
+ background: none;
+}
+
+#previewer-content hr {
+ border: none;
+ border-top: 1px solid #ccc;
+}
+
+#previewer-content .highlight {
+ color: red;
+ background: yellow;
+}
+
+#previewer-content.ng-hide {
+ top: -99999px !important;
+ left: -99999px !important;
+ display: block !important;
+}
+
+.panel-body {
+ padding: 10px;
+}
+
+.tab-content .km-priority {
+ display: inline-block;
+ width: 140px;
+ margin: 5px;
+ font-size: inherit;
+ vertical-align: middle;
+ border-right: 1px dashed #eee;
+}
+
+.tab-content .km-priority .km-priority-item {
+ padding: 1px;
+ margin: 0 1px;
+}
+
+.tab-content .km-priority .km-priority-item .km-priority-icon {
+ /* background: url(images/iconpriority.png) repeat-y; */
+ background-color: transparent;
+}
+
+.tab-content .km-priority .km-priority-item .km-priority-icon.priority-0 {
+ background-position: 0 20px;
+}
+
+.tab-content .km-priority .km-priority-item .km-priority-icon.priority-1 {
+ background-position: 0 0;
+}
+
+.tab-content .km-priority .km-priority-item .km-priority-icon.priority-2 {
+ background-position: 0 -20px;
+}
+
+.tab-content .km-priority .km-priority-item .km-priority-icon.priority-3 {
+ background-position: 0 -40px;
+}
+
+.tab-content .km-priority .km-priority-item .km-priority-icon.priority-4 {
+ background-position: 0 -60px;
+}
+
+.tab-content .km-priority .km-priority-item .km-priority-icon.priority-5 {
+ background-position: 0 -80px;
+}
+
+.tab-content .km-priority .km-priority-item .km-priority-icon.priority-6 {
+ background-position: 0 -100px;
+}
+
+.tab-content .km-priority .km-priority-item .km-priority-icon.priority-7 {
+ background-position: 0 -120px;
+}
+
+.tab-content .km-priority .km-priority-item .km-priority-icon.priority-8 {
+ background-position: 0 -140px;
+}
+
+.tab-content .km-priority .km-priority-item .km-priority-icon.priority-9 {
+ background-position: 0 -160px;
+}
+
+.tab-content .km-progress {
+ display: inline-block;
+ width: 140px;
+ margin: 5px;
+ font-size: inherit;
+ vertical-align: middle;
+ border-right: 1px dashed #eee;
+}
+
+.tab-content .km-progress .km-progress-item {
+ padding: 1px;
+ margin: 0 1px;
+}
+
+.tab-content .km-progress .km-progress-item .km-progress-icon {
+ /* background: url(images/iconprogress.png) repeat-y; */
+ background-color: transparent;
+}
+
+.tab-content .km-progress .km-progress-item .km-progress-icon.progress-0 {
+ background-position: 0 20px;
+}
+
+.tab-content .km-progress .km-progress-item .km-progress-icon.progress-1 {
+ background-position: 0 0;
+}
+
+.tab-content .km-progress .km-progress-item .km-progress-icon.progress-2 {
+ background-position: 0 -20px;
+}
+
+.tab-content .km-progress .km-progress-item .km-progress-icon.progress-3 {
+ background-position: 0 -40px;
+}
+
+.tab-content .km-progress .km-progress-item .km-progress-icon.progress-4 {
+ background-position: 0 -60px;
+}
+
+.tab-content .km-progress .km-progress-item .km-progress-icon.progress-5 {
+ background-position: 0 -80px;
+}
+
+.tab-content .km-progress .km-progress-item .km-progress-icon.progress-6 {
+ background-position: 0 -100px;
+}
+
+.tab-content .km-progress .km-progress-item .km-progress-icon.progress-7 {
+ background-position: 0 -120px;
+}
+
+.tab-content .km-progress .km-progress-item .km-progress-icon.progress-8 {
+ background-position: 0 -140px;
+}
+
+.tab-content .km-progress .km-progress-item .km-progress-icon.progress-9 {
+ background-position: 0 -160px;
+}
+
+.resource-editor {
+ display: inline-block;
+ margin: 5px;
+ vertical-align: middle;
+}
+
+.resource-editor .input-group,
+.resource-editor .km-resource {
+ font-size: 12px;
+}
+
+.resource-editor .input-group {
+ width: 168px;
+ height: 20px;
+}
+
+.resource-editor .resource-dropdown {
+ position: relative;
+ width: 168px;
+ margin-top: -1px;
+ border: 1px solid #ccc;
+ border-bottom-right-radius: 4px;
+ border-bottom-left-radius: 4px;
+}
+
+.resource-editor .resource-dropdown .km-resource {
+ position: absolute;
+ width: 154px;
+ max-height: 500px;
+ padding: 0;
+ margin-bottom: 3px;
+ overflow: scroll;
+ list-style-type: none;
+}
+
+.resource-editor .resource-dropdown .km-resource.open {
+ z-index: 3;
+ background-color: #fff;
+}
+
+.resource-editor .resource-dropdown .km-resource li {
+ display: inline-block;
+ padding: 1px 2px;
+ margin: 2px 3px;
+ border-radius: 4px;
+}
+
+.resource-editor .resource-dropdown .km-resource li[disabled] {
+ opacity: 0.5;
+}
+
+.resource-editor .resource-dropdown .resource-caret {
+ display: block;
+ float: right;
+ width: 12px;
+ height: 24px;
+ padding: 8px 1px;
+ vertical-align: middle;
+}
+
+.resource-editor .resource-dropdown .resource-caret:hover {
+ background-color: #eff3fa;
+}
+
+.resource-editor .resource-dropdown .resource-caret:active {
+ background-color: #c4d0ee;
+}
+
+.resource-editor input.form-control,
+.resource-editor .btn {
+ font-size: 12px;
+}
+
+.resource-editor input.form-control {
+ height: 24px;
+ padding: 2px 4px;
+ border-bottom-left-radius: 0;
+}
+
+.resource-editor .input-group-btn {
+ line-height: 24px;
+}
+
+.resource-editor .input-group-btn .btn {
+ height: 24px;
+ padding: 2px 4px;
+ border-bottom-right-radius: 0;
+}
+
+.temp-panel {
+ display: inline-block;
+ margin: 5px 5px 5px 10px;
+ vertical-align: middle;
+ border-right: 1px dashed #eee;
+}
+
+.temp-list {
+ min-width: 124px;
+}
+
+.temp-item-wrap {
+ display: inline-block;
+ width: 50px;
+ height: 40px;
+ padding: 0 2px;
+ margin: 5px;
+}
+
+.temp-item {
+ display: inline-block;
+ width: 50px;
+ height: 40px;
+
+ /* background-image: url(images/template.png); */
+ background-repeat: no-repeat;
+}
+
+.temp-item.default {
+ background-position: 0 0;
+}
+
+.temp-item.structure {
+ background-position: -50px 0;
+}
+
+.temp-item.filetree {
+ background-position: -100px 0;
+}
+
+.temp-item.right {
+ background-position: -150px 0;
+}
+
+.temp-item.fish-bone {
+ background-position: -200px 0;
+}
+
+.temp-item.tianpan {
+ background-position: -250px 0;
+}
+
+.current-temp-item {
+ width: 74px;
+ padding: 0 0 0 5px;
+ border: 1px solid #fff;
+}
+
+.current-temp-item:hover {
+ background-color: #eff3fa;
+}
+
+.current-temp-item[disabled] {
+ opacity: 0.5;
+}
+
+.current-temp-item .caret {
+ margin-left: 5px;
+}
+
+.temp-item-selected {
+ background-color: #87a9da;
+}
+
+.theme-panel {
+ display: inline-block;
+ height: 42px;
+ padding: 0 5px 0 0;
+ margin: 5px;
+ vertical-align: middle;
+ border-right: 1px dashed #eee;
+}
+
+.theme-list {
+ min-width: 162px;
+}
+
+div a.theme-item {
+ display: inline-block;
+ width: 70px;
+ height: 30px;
+ padding: 0 5px;
+ font-size: 12px;
+ line-height: 30px;
+ color: #000;
+ text-align: center;
+ text-decoration: none;
+ cursor: pointer;
+}
+
+.theme-item-selected {
+ width: 100px;
+ padding: 6px 7px;
+ border: 1px solid #fff;
+}
+
+.theme-item-selected:hover {
+ background-color: #eff3fa;
+}
+
+.theme-item-selected .caret {
+ margin-left: 5px;
+}
+
+.theme-item-selected[disabled] {
+ opacity: 0.5;
+}
+
+.theme-item-wrap {
+ display: inline-block;
+ width: 80px;
+ height: 40px;
+ padding: 5px;
+}
+
+.theme-item-wrap:hover {
+ background-color: #eff3fa;
+}
+
+.readjust-layout {
+ display: inline-block;
+ padding: 0 10px 0 5px;
+ vertical-align: middle;
+ border-right: 1px dashed #eee;
+}
+
+.btn-icon {
+ display: block;
+ width: 25px;
+ height: 25px;
+ margin-left: 12px;
+}
+
+.btn-label {
+ font-size: 12px;
+}
+
+.btn-wrap {
+ display: inline-block;
+ width: 50px;
+ height: 42px;
+ text-decoration: none;
+ cursor: pointer;
+}
+
+.btn-wrap[disabled] span {
+ opacity: 0.5;
+}
+
+.btn-wrap[disabled] {
+ cursor: default;
+}
+
+.btn-wrap[disabled]:hover {
+ background-color: transparent;
+}
+
+.btn-wrap[disabled]:active {
+ background-color: transparent;
+}
+
+.btn-wrap:link {
+ text-decoration: none;
+}
+
+.btn-wrap:visited {
+ text-decoration: none;
+}
+
+.btn-wrap:hover {
+ text-decoration: none;
+ background-color: #eff3fa;
+}
+
+.btn-wrap:active {
+ background-color: #c4d0ee;
+}
+
+.reset-layout-icon {
+ /* background: url(images/icons.png) no-repeat; */
+ background-position: 0 -150px;
+}
+
+.style-operator {
+ display: inline-block;
+ padding: 0 5px;
+ vertical-align: middle;
+ border-right: 1px dashed #eee;
+}
+
+.style-operator .clear-style {
+ vertical-align: middle;
+}
+
+.clear-style-icon {
+ /* background: url(images/icons.png) no-repeat; */
+ background-position: 0 -175px;
+}
+
+.s-btn-group-vertical {
+ display: inline-block;
+ vertical-align: middle;
+}
+
+.s-btn-icon {
+ display: inline-block;
+ width: 20px;
+ height: 20px;
+ margin-right: 3px;
+ vertical-align: middle;
+}
+
+.s-btn-label {
+ display: inline-block;
+ font-size: 12px;
+ vertical-align: middle;
+}
+
+.s-btn-wrap {
+ display: inline-block;
+ padding: 0 5px 0 3px;
+ font-size: 0;
+ text-decoration: none;
+}
+
+.s-btn-wrap[disabled] span {
+ opacity: 0.5;
+}
+
+.s-btn-wrap[disabled] {
+ cursor: default;
+}
+
+.s-btn-wrap[disabled]:hover {
+ background-color: transparent;
+}
+
+.s-btn-wrap[disabled]:active {
+ background-color: transparent;
+}
+
+.s-btn-wrap:hover {
+ text-decoration: none;
+ background-color: #eff3fa;
+}
+
+.s-btn-wrap:active {
+ background-color: #c4d0ee;
+}
+
+.copy-style-icon {
+ /* background: url(images/icons.png) no-repeat; */
+ background-position: 0 -200px;
+}
+
+.paste-style-wrap {
+ display: block;
+}
+
+.paste-style-icon {
+ /* background: url(images/icons.png) no-repeat; */
+ background-position: 0 -220px;
+}
+
+.font-operator {
+ display: inline-block;
+ width: 170px;
+ padding: 0 5px;
+ font-size: 12px;
+ vertical-align: middle;
+}
+
+.font-operator .font-size-list {
+ display: inline-block;
+ padding: 2px 4px;
+ border: 1px solid #eee;
+}
+
+.font-operator .font-family-list {
+ display: inline-block;
+ padding: 2px 4px;
+ border: 1px solid #eee;
+}
+
+.current-font-item a {
+ display: inline-block;
+ text-decoration: none;
+}
+
+.current-font-family {
+ width: 75px;
+ height: 18px;
+ overflow: hidden;
+ vertical-align: bottom;
+}
+
+.current-font-size {
+ width: 32px;
+ height: 18px;
+ overflow: hidden;
+ vertical-align: bottom;
+}
+
+.current-font-item[disabled] {
+ opacity: 0.5;
+}
+
+.font-item {
+ line-height: 1em;
+ text-align: left;
+}
+
+.font-item-selected {
+ background-color: #87a9da;
+}
+
+.font-bold,
+.font-italics {
+ display: inline-block;
+ margin: 0 3px;
+
+ /* background: url(images/icons.png) no-repeat; */
+ cursor: pointer;
+}
+
+.font-bold:hover,
+.font-italics:hover {
+ background-color: #eff3fa;
+}
+
+.font-bold:active,
+.font-italics:active {
+ background-color: #c4d0ee;
+}
+
+.font-bold[disabled],
+.font-italics[disabled] {
+ opacity: 0.5;
+}
+
+.font-bold {
+ background-position: 0 -240px;
+}
+
+.font-italics {
+ background-position: 0 -260px;
+}
+
+.font-bold-selected,
+.font-italics-selected {
+ background-color: #87a9da;
+}
+
+.font-color-wrap {
+ display: inline-block;
+ width: 30px;
+ height: 22px;
+ margin: 3px 3px 0 0;
+ font-size: 0;
+ vertical-align: middle;
+ border: 1px #efefef solid;
+ user-select: none;
+ user-select: none;
+ user-select: none;
+ user-select: none;
+}
+
+.font-color-wrap[disabled] {
+ opacity: 0.5;
+}
+
+.font-color-wrap .quick-font-color {
+ display: inline-block;
+ width: 20px;
+ height: 16px;
+ font-size: 14px;
+ line-height: 16px;
+ color: #000;
+ text-align: center;
+ vertical-align: top;
+ cursor: default;
+}
+
+.font-color-wrap .quick-font-color:hover {
+ background-color: #eff3fa;
+}
+
+.font-color-wrap .quick-font-color:active {
+ background-color: #c4d0ee;
+}
+
+.font-color-wrap .quick-font-color[disabled] {
+ opacity: 0.5;
+}
+
+.font-color-wrap .font-color-preview {
+ display: inline-block;
+ width: 12px;
+ height: 2px;
+ margin: 0 4px;
+ background-color: #000;
+}
+
+.font-color-wrap .font-color-preview[disabled] {
+ opacity: 0.5;
+}
+
+.font-color {
+ display: inline-block;
+ width: 8px;
+ height: 16px;
+}
+
+.font-color:hover {
+ background-color: #eff3fa;
+}
+
+.font-color:active {
+ background-color: #c4d0ee;
+}
+
+.font-color[disabled] {
+ opacity: 0.5;
+}
+
+.font-color .caret {
+ margin-top: 7px;
+ margin-left: -2px;
+}
+
+.bg-color-wrap {
+ display: inline-block;
+ width: 30px;
+ height: 22px;
+ margin: 3px 3px 0 0;
+ font-size: 0;
+ vertical-align: middle;
+ border: 1px #efefef solid;
+ user-select: none;
+ user-select: none;
+ user-select: none;
+ user-select: none;
+}
+
+.bg-color-wrap[disabled] {
+ opacity: 0.5;
+}
+
+.bg-color-wrap .quick-bg-color {
+ display: inline-block;
+ width: 20px;
+ height: 16px;
+ font-size: 14px;
+ line-height: 16px;
+ color: #000;
+ text-align: center;
+ vertical-align: top;
+ cursor: default;
+
+ /* background: url(images/icons.png) no-repeat center -1260px; */
+}
+
+.bg-color-wrap .quick-bg-color:hover {
+ background-color: #eff3fa;
+}
+
+.bg-color-wrap .quick-bg-color:active {
+ background-color: #c4d0ee;
+}
+
+.bg-color-wrap .quick-bg-color[disabled] {
+ opacity: 0.5;
+}
+
+.bg-color-wrap .bg-color-preview {
+ display: inline-block;
+ width: 12px;
+ height: 2px;
+ margin: 0 4px;
+ background-color: #fff;
+}
+
+.bg-color-wrap .bg-color-preview[disabled] {
+ opacity: 0.5;
+}
+
+.bg-color {
+ display: inline-block;
+ width: 8px;
+ height: 16px;
+}
+
+.bg-color:hover {
+ background-color: #eff3fa;
+}
+
+.bg-color:active {
+ background-color: #c4d0ee;
+}
+
+.bg-color[disabled] {
+ opacity: 0.5;
+}
+
+.bg-color .caret {
+ margin-top: 7px;
+ margin-left: -2px;
+}
+
+.btn-group-vertical {
+ margin: 5px;
+ vertical-align: middle;
+}
+
+.btn-group-vertical .expand,
+.btn-group-vertical .expand-caption {
+ width: 40px;
+ padding: 0;
+ margin: 0;
+ border: none !important;
+ border-radius: 0 !important;
+}
+
+.btn-group-vertical .expand:hover,
+.btn-group-vertical .expand-caption:hover {
+ background-color: #eff3fa;
+}
+
+.btn-group-vertical .expand:active,
+.btn-group-vertical .expand-caption:active {
+ background-color: #c4d0ee;
+}
+
+.btn-group-vertical .expand.active,
+.btn-group-vertical .expand-caption.active {
+ box-shadow: none;
+ background-color: #eff3fa;
+}
+
+.btn-group-vertical .expand {
+ height: 25px;
+
+ /* background: url(images/icons.png) no-repeat 0 -995px; */
+ background-position-x: 50%;
+}
+
+.btn-group-vertical .expand-caption {
+ height: 20px;
+}
+
+.btn-group-vertical .expand-caption .caption {
+ font-size: 12px;
+}
+
+.btn-group-vertical {
+ margin: 5px;
+ vertical-align: middle;
+}
+
+.btn-group-vertical .select,
+.btn-group-vertical .select-caption {
+ width: 40px;
+ padding: 0;
+ margin: 0;
+ border: none !important;
+ border-radius: 0 !important;
+}
+
+.btn-group-vertical .select:hover,
+.btn-group-vertical .select-caption:hover {
+ background-color: #eff3fa;
+}
+
+.btn-group-vertical .select:active,
+.btn-group-vertical .select-caption:active {
+ background-color: #c4d0ee;
+}
+
+.btn-group-vertical .select.active,
+.btn-group-vertical .select-caption.active {
+ box-shadow: none;
+ background-color: #eff3fa;
+}
+
+.btn-group-vertical .select {
+ height: 25px;
+
+ /* background: url(images/icons.png) no-repeat 7px -1175px; */
+}
+
+.btn-group-vertical .select-caption {
+ height: 20px;
+}
+
+.btn-group-vertical .select-caption .caption {
+ font-size: 12px;
+}
+
+.btn-group-vertical {
+ margin: 5px;
+ vertical-align: middle;
+}
+
+.btn-group-vertical .search,
+.btn-group-vertical .search-caption {
+ width: 40px;
+ padding: 0;
+ margin: 0;
+ border: none !important;
+ border-radius: 0 !important;
+}
+
+.btn-group-vertical .search:hover,
+.btn-group-vertical .search-caption:hover {
+ background-color: #eff3fa;
+}
+
+.btn-group-vertical .search:active,
+.btn-group-vertical .search-caption:active {
+ background-color: #c4d0ee;
+}
+
+.btn-group-vertical .search.active,
+.btn-group-vertical .search-caption.active {
+ box-shadow: none;
+ background-color: #eff3fa;
+}
+
+.btn-group-vertical .search {
+ height: 25px;
+
+ /* background: url(images/icons.png) no-repeat 0 -345px; */
+ background-position-x: 50%;
+}
+
+.btn-group-vertical .search-caption {
+ height: 20px;
+}
+
+.btn-group-vertical .search-caption .caption {
+ font-size: 12px;
+}
+
+.search-box {
+ position: relative;
+ top: 0;
+ z-index: 3;
+ float: right;
+ width: 360px;
+ height: 40px;
+ padding: 3px 6px;
+ background-color: #fff;
+ border: 1px solid #dbdbdb;
+ opacity: 1;
+}
+
+.search-box .search-input-wrap,
+.search-box .prev-and-next-btn {
+ float: left;
+}
+
+.search-box .close-search {
+ float: right;
+ width: 16px;
+ height: 16px;
+ padding: 1px;
+ margin-top: 6px;
+ margin-right: 10px;
+ border-radius: 100%;
+}
+
+.search-box .close-search .glyphicon {
+ top: -1px;
+}
+
+.search-box .close-search:hover {
+ background-color: #efefef;
+}
+
+.search-box .close-search:active {
+ background-color: #999;
+}
+
+.search-box .search-input-wrap {
+ width: 240px;
+}
+
+.search-box .prev-and-next-btn {
+ margin-left: 5px;
+}
+
+.search-box .prev-and-next-btn .btn:focus {
+ outline: none;
+}
+
+.search-box .search-addon {
+ background-color: #fff;
+}
+
+.tool-group {
+ padding: 0;
+}
+
+.tool-group[disabled] {
+ opacity: 0.5;
+}
+
+.tool-group .tool-group-item {
+ display: inline-block;
+ border-radius: 4px;
+}
+
+.tool-group .tool-group-item .tool-group-icon {
+ width: 20px;
+ height: 20px;
+ padding: 2px;
+ margin: 1px;
+}
+
+.tool-group .tool-group-item:hover {
+ background-color: #eff3fa;
+}
+
+.tool-group .tool-group-item:active {
+ background-color: #c4d0ee;
+}
+
+.tool-group .tool-group-item.active {
+ background-color: #c4d0ee;
+}
+
+.nav-bar {
+ position: absolute;
+ bottom: 10px;
+ left: 10px;
+ z-index: 10;
+ width: 35px;
+ height: 240px;
+ padding: 5px 0;
+ color: #fff;
+ background: #fc8383;
+ border-radius: 4px;
+ box-shadow: 3px 3px 10px rgb(0 0 0 / 20%);
+ transition: -webkit-transform 0.7s 0.1s ease;
+ transition: transform 0.7s 0.1s ease;
+}
+
+.nav-bar .nav-btn {
+ width: 35px;
+ height: 24px;
+ line-height: 24px;
+ text-align: center;
+}
+
+.nav-bar .nav-btn .icon {
+ display: block;
+
+ /* background: url(images/icons.png); */
+ width: 20px;
+ height: 20px;
+ margin: 2px auto;
+}
+
+.nav-bar .nav-btn.active {
+ background-color: #5a6378;
+}
+
+.nav-bar .zoom-in .icon {
+ background-position: 0 -730px;
+}
+
+.nav-bar .zoom-out .icon {
+ background-position: 0 -750px;
+}
+
+.nav-bar .hand .icon {
+ background-position: 0 -770px;
+ width: 25px;
+ height: 25px;
+ margin: 0 auto;
+}
+
+.nav-bar .camera .icon {
+ background-position: 0 -870px;
+ width: 25px;
+ height: 25px;
+ margin: 0 auto;
+}
+
+.nav-bar .nav-trigger .icon {
+ background-position: 0 -845px;
+ width: 25px;
+ height: 25px;
+ margin: 0 auto;
+}
+
+.nav-bar .zoom-pan {
+ position: relative;
+ width: 2px;
+ height: 70px;
+ margin: 3px auto;
+ overflow: visible;
+ background: white;
+ box-shadow: 0 1px #e50000;
+}
+
+.nav-bar .zoom-pan .origin {
+ position: absolute;
+ left: -9px;
+ width: 20px;
+ height: 8px;
+ margin-top: -4px;
+ background: transparent;
+}
+
+.nav-bar .zoom-pan .origin::after {
+ position: absolute;
+ top: 3px;
+ left: 7px;
+ display: block;
+ width: 6px;
+ height: 2px;
+ background: white;
+ content: ' ';
+}
+
+.nav-bar .zoom-pan .indicator {
+ position: absolute;
+ left: -3px;
+ width: 8px;
+ height: 8px;
+ margin-top: -4px;
+ background: white;
+ border-radius: 100%;
+}
+
+.nav-previewer {
+ position: absolute;
+ bottom: 30px;
+ left: 45px;
+ z-index: 9;
+ width: 140px;
+ height: 120px;
+ padding: 1px;
+ cursor: crosshair;
+ background: #fff;
+ border-radius: 0 2px 2px 0;
+ box-shadow: 0 0 8px rgb(0 0 0 / 20%);
+ transition: -webkit-transform 0.7s 0.1s ease;
+ transition: transform 0.7s 0.1s ease;
+}
+
+.nav-previewer.grab {
+ cursor: move;
+ cursor: grabbing;
+ cursor: grabbing;
+ cursor: grabbing;
+}
+
+.hotbox {
+ position: absolute;
+ top: 0;
+ left: 0;
+ overflow: visible;
+ font-family: Arial, 'Hiragino Sans GB', 'Microsoft YaHei', 'WenQuanYi Micro Hei', sans-serif;
+}
+
+.hotbox .state {
+ position: absolute;
+ display: none;
+ overflow: visible;
+}
+
+.hotbox .state .center .button,
+.hotbox .state .ring .button {
+ position: absolute;
+ width: 70px;
+ height: 70px;
+ margin-top: -35px;
+ margin-left: -35px;
+ border-radius: 100%;
+ box-shadow: 0 0 30px rgb(0 0 0 / 30%);
+}
+
+.hotbox .state .center .label,
+.hotbox .state .ring .label,
+.hotbox .state .center .key,
+.hotbox .state .ring .key {
+ display: block;
+ line-height: 1.4em;
+ text-align: center;
+ vertical-align: middle;
+}
+
+.hotbox .state .center .label,
+.hotbox .state .ring .label {
+ margin-top: 17px;
+ font-size: 16px;
+ font-weight: normal;
+ line-height: 1em;
+ color: black;
+}
+
+.hotbox .state .center .key,
+.hotbox .state .ring .key {
+ font-size: 12px;
+ color: #999;
+}
+
+.hotbox .state .ring-shape {
+ position: absolute;
+ top: -25px;
+ left: -25px;
+ border: 25px solid rgb(0 0 0 / 30%);
+ border-radius: 100%;
+ box-sizing: content-box;
+}
+
+.hotbox .state .top,
+.hotbox .state .bottom {
+ position: absolute;
+ white-space: nowrap;
+}
+
+.hotbox .state .top .button,
+.hotbox .state .bottom .button {
+ position: relative;
+ display: inline-block;
+ padding: 8px 15px;
+ margin: 0 10px;
+ border-radius: 15px;
+ box-shadow: 0 0 30px rgb(0 0 0 / 30%);
+}
+
+.hotbox .state .top .button .label,
+.hotbox .state .bottom .button .label {
+ font-size: 14px;
+ line-height: 14px;
+ line-height: 1em;
+ color: black;
+ vertical-align: middle;
+}
+
+.hotbox .state .top .button .key,
+.hotbox .state .bottom .button .key {
+ margin-left: 3px;
+ font-size: 12px;
+ line-height: 12px;
+ color: #999;
+ vertical-align: middle;
+}
+
+.hotbox .state .top .button .key::before,
+.hotbox .state .bottom .button .key::before {
+ content: '(';
+}
+
+.hotbox .state .top .button .key::after,
+.hotbox .state .bottom .button .key::after {
+ content: ')';
+}
+
+.hotbox .state .button {
+ overflow: hidden;
+ cursor: default;
+ background: #f9f9f9;
+}
+
+.hotbox .state .button .key,
+.hotbox .state .button .label {
+ opacity: 0.3;
+}
+
+.hotbox .state .button.enabled {
+ background: white;
+}
+
+.hotbox .state .button.enabled .key,
+.hotbox .state .button.enabled .label {
+ opacity: 1;
+}
+
+.hotbox .state .button.enabled:hover {
+ background: #e87372;
+}
+
+.hotbox .state .button.enabled:hover .label {
+ color: white;
+}
+
+.hotbox .state .button.enabled:hover .key {
+ color: #fadfdf;
+}
+
+.hotbox .state .button.enabled.selected {
+ animation: selected 0.1s ease;
+ background: #e45d5c;
+}
+
+.hotbox .state .button.enabled.selected .label {
+ color: white;
+}
+
+.hotbox .state .button.enabled.selected .key {
+ color: #fadfdf;
+}
+
+.hotbox .state .button.enabled.pressed,
+.hotbox .state .button.enabled:active {
+ background: #ff974d;
+}
+
+.hotbox .state .button.enabled.pressed .label,
+.hotbox .state .button.enabled:active .label {
+ color: white;
+}
+
+.hotbox .state .button.enabled.pressed .key,
+.hotbox .state .button.enabled:active .key {
+ color: #fff0e6;
+}
+
+.hotbox .state.active {
+ display: block;
+}
+
+@keyframes selected {
+ 0% {
+ transform: scale(1);
+ }
+
+ 50% {
+ transform: scale(1.1);
+ }
+
+ 100% {
+ transform: scale(1);
+ }
+}
+
+.hotbox-key-receiver {
+ position: absolute;
+ top: -999999px;
+ left: -999999px;
+ width: 20px;
+ height: 20px;
+ margin: 0;
+ outline: none;
+}
diff --git a/packages/client/src/tiptap/styles/mind.scss b/packages/client/src/tiptap/styles/mind.scss
deleted file mode 100644
index d21c7db..0000000
--- a/packages/client/src/tiptap/styles/mind.scss
+++ /dev/null
@@ -1,334 +0,0 @@
-/* stylelint-disable */
-
-/* important section */
-.jsmind-inner {
- position: relative;
- width: 100%;
- height: 100%;
- overflow: auto;
-} /* box-shadow:0 0 2px #000; */
-.jsmind-inner {
- user-select: none;
- user-select: none;
- user-select: none;
- user-select: none;
- user-select: none;
- user-select: none;
-}
-
-/* z-index:1 */
-canvas {
- position: absolute;
- z-index: 1;
-}
-
-/* z-index:2 */
-jmnodes {
- position: absolute;
- z-index: 2;
- background-color: rgb(0 0 0 / 0%);
-} /* background color is necessary */
-jmnode {
- position: absolute;
- max-width: 400px;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- cursor: default;
-}
-
-jmexpander {
- position: absolute;
- display: block;
- width: 11px;
- height: 11px;
- overflow: hidden;
- font-size: 12px;
- line-height: 12px;
- text-align: center;
- cursor: pointer;
- border-radius: 6px;
- border-width: 1px;
- border-style: solid;
-}
-
-/* default theme */
-jmnode {
- padding: 10px;
- font: 16px/1.125 Verdana, Arial, Helvetica, sans-serif;
- color: #333;
- background-color: #fff;
- border-radius: 5px;
- box-shadow: 1px 1px 1px #666;
-}
-
-jmnode:hover {
- color: #333;
- background-color: #ebebeb;
- box-shadow: 2px 2px 8px #000;
-}
-
-jmnode.selected {
- color: #fff;
- background-color: #11f;
- box-shadow: 2px 2px 8px #000;
-}
-
-jmnode.root {
- font-size: 24px;
-}
-
-jmexpander {
- border-color: gray;
-}
-
-jmexpander:hover {
- border-color: #000;
-}
-
-@media screen and (max-device-width: 1024px) {
- jmnode {
- padding: 5px;
- font-size: 14px;
- border-radius: 3px;
- }
-
- jmnode.root {
- font-size: 21px;
- }
-}
-
-/* primary theme */
-jmnodes.theme-primary jmnode {
- border-color: #357ebd;
- color: #fff;
- background-color: #428bca;
-}
-
-jmnodes.theme-primary jmnode:hover {
- border-color: #285e8e;
- background-color: #3276b1;
-}
-
-jmnodes.theme-primary jmnode.selected {
- color: #fff;
- background-color: #f1c40f;
-}
-
-/* warning theme */
-jmnodes.theme-warning jmnode {
- border-color: #eea236;
- color: #fff;
- background-color: #f0ad4e;
-}
-
-jmnodes.theme-warning jmnode:hover {
- border-color: #d58512;
- background-color: #ed9c28;
-}
-
-jmnodes.theme-warning jmnode.selected {
- color: #fff;
- background-color: #11f;
-}
-
-/* danger theme */
-jmnodes.theme-danger jmnode {
- border-color: #d43f3a;
- color: #fff;
- background-color: #d9534f;
-}
-
-jmnodes.theme-danger jmnode:hover {
- border-color: #ac2925;
- background-color: #d2322d;
-}
-
-jmnodes.theme-danger jmnode.selected {
- color: #fff;
- background-color: #11f;
-}
-
-/* success theme */
-jmnodes.theme-success jmnode {
- border-color: #4cae4c;
- color: #fff;
- background-color: #5cb85c;
-}
-
-jmnodes.theme-success jmnode:hover {
- border-color: #398439;
- background-color: #47a447;
-}
-
-jmnodes.theme-success jmnode.selected {
- color: #fff;
- background-color: #11f;
-}
-
-/* info theme */
-jmnodes.theme-info jmnode {
- border-color: #46b8da;
- color: #fff;
- background-color: #5dc0de;
-}
-
-jmnodes.theme-info jmnode:hover {
- border-color: #269abc;
- background-color: #39b3d7;
-}
-
-jmnodes.theme-info jmnode.selected {
- color: #fff;
- background-color: #11f;
-}
-
-/* greensea theme */
-jmnodes.theme-greensea jmnode {
- color: #fff;
- background-color: #1abc9c;
-}
-
-jmnodes.theme-greensea jmnode:hover {
- background-color: #16a085;
-}
-
-jmnodes.theme-greensea jmnode.selected {
- color: #fff;
- background-color: #11f;
-}
-
-/* nephrite theme */
-jmnodes.theme-nephrite jmnode {
- color: #fff;
- background-color: #2ecc71;
-}
-
-jmnodes.theme-nephrite jmnode:hover {
- background-color: #27ae60;
-}
-
-jmnodes.theme-nephrite jmnode.selected {
- color: #fff;
- background-color: #11f;
-}
-
-/* belizehole theme */
-jmnodes.theme-belizehole jmnode {
- color: #fff;
- background-color: #3498db;
-}
-
-jmnodes.theme-belizehole jmnode:hover {
- background-color: #2980b9;
-}
-
-jmnodes.theme-belizehole jmnode.selected {
- color: #fff;
- background-color: #11f;
-}
-
-/* wisteria theme */
-jmnodes.theme-wisteria jmnode {
- color: #fff;
- background-color: #9b59b6;
-}
-
-jmnodes.theme-wisteria jmnode:hover {
- background-color: #8e44ad;
-}
-
-jmnodes.theme-wisteria jmnode.selected {
- color: #fff;
- background-color: #11f;
-}
-
-/* asphalt theme */
-jmnodes.theme-asphalt jmnode {
- color: #fff;
- background-color: #34495e;
-}
-
-jmnodes.theme-asphalt jmnode:hover {
- background-color: #2c3e50;
-}
-
-jmnodes.theme-asphalt jmnode.selected {
- color: #fff;
- background-color: #11f;
-}
-
-/* orange theme */
-jmnodes.theme-orange jmnode {
- color: #fff;
- background-color: #f1c40f;
-}
-
-jmnodes.theme-orange jmnode:hover {
- background-color: #f39c12;
-}
-
-jmnodes.theme-orange jmnode.selected {
- color: #fff;
- background-color: #11f;
-}
-
-/* pumpkin theme */
-jmnodes.theme-pumpkin jmnode {
- color: #fff;
- background-color: #e67e22;
-}
-
-jmnodes.theme-pumpkin jmnode:hover {
- background-color: #d35400;
-}
-
-jmnodes.theme-pumpkin jmnode.selected {
- color: #fff;
- background-color: #11f;
-}
-
-/* pomegranate theme */
-jmnodes.theme-pomegranate jmnode {
- color: #fff;
- background-color: #e74c3c;
-}
-
-jmnodes.theme-pomegranate jmnode:hover {
- background-color: #c0392b;
-}
-
-jmnodes.theme-pomegranate jmnode.selected {
- color: #fff;
- background-color: #11f;
-}
-
-/* clouds theme */
-jmnodes.theme-clouds jmnode {
- color: #333;
- background-color: #ecf0f1;
-}
-
-jmnodes.theme-clouds jmnode:hover {
- background-color: #bdc3c7;
-}
-
-jmnodes.theme-clouds jmnode.selected {
- color: #fff;
- background-color: #11f;
-}
-
-/* asbestos theme */
-jmnodes.theme-asbestos jmnode {
- color: #fff;
- background-color: #95a5a6;
-}
-
-jmnodes.theme-asbestos jmnode:hover {
- background-color: #7f8c8d;
-}
-
-jmnodes.theme-asbestos jmnode.selected {
- color: #fff;
- background-color: #11f;
-}
diff --git a/packages/client/src/tiptap/styles/table.scss b/packages/client/src/tiptap/styles/table.scss
index 4ed0017..b28b326 100644
--- a/packages/client/src/tiptap/styles/table.scss
+++ b/packages/client/src/tiptap/styles/table.scss
@@ -6,7 +6,7 @@
table {
width: 100%;
- margin: .75em 0 0;
+ margin: 0.75em 0 0;
overflow: hidden;
border-collapse: collapse;
table-layout: fixed;
diff --git a/packages/client/src/tiptap/wrappers/callout/index.module.scss b/packages/client/src/tiptap/wrappers/callout/index.module.scss
index 0d92f9e..ab03a14 100644
--- a/packages/client/src/tiptap/wrappers/callout/index.module.scss
+++ b/packages/client/src/tiptap/wrappers/callout/index.module.scss
@@ -1,5 +1,5 @@
.wrap {
- margin-top: .75em;
+ margin-top: 0.75em;
line-height: 0;
.innerWrap {
@@ -29,7 +29,7 @@
}
p {
- margin-top: .25em;
+ margin-top: 0.25em;
}
p:first-child {
diff --git a/packages/client/src/tiptap/wrappers/code-block/index.module.scss b/packages/client/src/tiptap/wrappers/code-block/index.module.scss
index 0adb894..e29551c 100644
--- a/packages/client/src/tiptap/wrappers/code-block/index.module.scss
+++ b/packages/client/src/tiptap/wrappers/code-block/index.module.scss
@@ -1,6 +1,6 @@
.wrap {
position: relative;
- margin-top: .75em;
+ margin-top: 0.75em;
.handleWrap {
display: flex;
diff --git a/packages/client/src/tiptap/wrappers/document-children/index.module.scss b/packages/client/src/tiptap/wrappers/document-children/index.module.scss
index 0c2e9ec..23de4dd 100644
--- a/packages/client/src/tiptap/wrappers/document-children/index.module.scss
+++ b/packages/client/src/tiptap/wrappers/document-children/index.module.scss
@@ -1,6 +1,6 @@
.wrap {
padding: 12px;
- margin-top: .75em;
+ margin-top: 0.75em;
border: 1px solid var(--node-border-color);
border-radius: var(--border-radius);
diff --git a/packages/client/src/tiptap/wrappers/document-reference/index.module.scss b/packages/client/src/tiptap/wrappers/document-reference/index.module.scss
index 90f594f..4ff8f5d 100644
--- a/packages/client/src/tiptap/wrappers/document-reference/index.module.scss
+++ b/packages/client/src/tiptap/wrappers/document-reference/index.module.scss
@@ -1,5 +1,5 @@
.wrap {
- margin-top: .75em;
+ margin-top: 0.75em;
border-radius: var(--border-radius);
.itemWrap {
diff --git a/packages/client/src/tiptap/wrappers/mind/index.module.scss b/packages/client/src/tiptap/wrappers/mind/index.module.scss
index b071342..6fab04e 100644
--- a/packages/client/src/tiptap/wrappers/mind/index.module.scss
+++ b/packages/client/src/tiptap/wrappers/mind/index.module.scss
@@ -5,113 +5,9 @@
line-height: 0;
outline: none;
- :global {
- /* stylelint-disable-next-line selector-type-no-unknown */
- jmnodes {
- /* stylelint-disable-next-line selector-type-no-unknown */
- jmnode {
- color: var(--semi-color-text-1);
- background-color: var(--semi-color-fill-2);
- outline: 2px solid transparent;
- box-shadow: none;
-
- &:first-of-type {
- color: #fff;
- background-color: var(--semi-color-link);
- }
-
- &.selected {
- outline: 2px solid var(--semi-color-link);
- }
-
- input {
- width: 100px !important;
- height: 100%;
- background-color: #fff;
- border: 0;
- outline: none;
- }
- }
-
- /* stylelint-disable-next-line selector-type-no-unknown */
- jmexpander {
- display: flex;
- justify-content: center;
- align-items: center;
- width: 12px;
- height: 12px;
- border-color: var(--node-border-color);
- }
- }
- }
-
- .jsmindWrap {
- position: absolute;
- top: 10px;
- left: 50%;
- z-index: 200;
- max-width: 100%;
- padding-bottom: 10px;
- opacity: 0;
- transform: translate(-50%, 0);
-
- .jsmindInnerWrap {
- padding: 6px;
- background-color: var(--semi-color-bg-2);
- border: 1px solid var(--node-border-color);
- border-radius: var(--border-radius);
- box-shadow: var(--box-shadow);
- }
- }
-
.renderWrap {
- position: relative;
- min-height: 50px;
- overflow: hidden;
- border: 1px solid var(--node-border-color);
- border-radius: var(--border-radius);
- outline: none;
-
- > input {
- position: absolute;
- top: 0;
- left: 0;
- z-index: 10000;
- width: 100%;
- height: 100%;
- background-color: transparent;
+ &::after {
+ background-color: transparent !important;
}
-
- .mindHandlerWrap {
- position: absolute;
- right: 20px;
- bottom: 20px;
- z-index: 1000;
- padding: 4px 8px;
- background-color: var(--semi-color-bg-2);
- border: 1px solid var(--node-border-color);
- border-radius: var(--border-radius);
- opacity: 0;
- box-shadow: var(--box-shadow);
- }
- }
-
- &.isActive {
- .jsmindWrap {
- opacity: 1;
- }
-
- .renderWrap {
- .mindHandlerWrap {
- opacity: 1;
- }
- }
- }
-
- .handlerWrap {
- display: flex;
- justify-content: center;
- padding: 10px;
- border-top: 1px solid var(--node-border-color);
}
}
diff --git a/packages/client/src/tiptap/wrappers/mind/index.tsx b/packages/client/src/tiptap/wrappers/mind/index.tsx
index dd8b14a..5d85c97 100644
--- a/packages/client/src/tiptap/wrappers/mind/index.tsx
+++ b/packages/client/src/tiptap/wrappers/mind/index.tsx
@@ -1,13 +1,13 @@
-import { NodeViewWrapper, NodeViewContent } from '@tiptap/react';
+import { NodeViewWrapper } from '@tiptap/react';
import cls from 'classnames';
import { useCallback, useEffect, useRef } from 'react';
import { Button } from '@douyinfe/semi-ui';
import { IconMinus, IconPlus } from '@douyinfe/semi-icons';
import { Resizeable } from 'components/resizeable';
import deepEqual from 'deep-equal';
+import { useToggle } from 'hooks/use-toggle';
import { Mind } from '../../extensions/mind';
-// @ts-ignore
-import jsMind from './jsmind.jsx';
+import { loadKityMinder } from './kityminder';
import styles from './index.module.scss';
export const MindWrapper = ({ editor, node, updateAttributes }) => {
@@ -16,87 +16,57 @@ export const MindWrapper = ({ editor, node, updateAttributes }) => {
const isMindActive = editor.isActive(Mind.name);
const isEditable = editor.isEditable;
const { data, width, height = 100 } = node.attrs;
+ const [loading, toggleLoading] = useToggle(true);
- const zoomIn = useCallback(() => {
- const jm = $mind.current;
- if (!jm) return;
+ const onResize = useCallback(
+ (size) => {
+ updateAttributes({ width: size.width, height: size.height });
+ },
+ [updateAttributes]
+ );
- jm.view.zoomIn();
- }, []);
-
- const zoomOut = useCallback(() => {
- const jm = $mind.current;
- if (!jm) return;
-
- jm.view.zoomOut();
- }, []);
-
- const syncData = useCallback(() => {
- const jm = $mind.current;
- if (!jm) return;
- const data = jm.get_data();
- try {
- updateAttributes({ data });
- } catch (e) {}
- }, []);
+ const saveData = useCallback(() => {
+ const minder = $mind.current;
+ if (!minder) return;
+ updateAttributes({ data: minder.exportJson() });
+ }, [updateAttributes]);
+ // 初始化
useEffect(() => {
- if (!data) return;
- if (!data.meta) return;
+ const onChange = () => {
+ saveData();
+ };
+ loadKityMinder().then((Editor) => {
+ toggleLoading(false);
+ const minder = new Editor($container.current).minder;
+ minder.importJson(data);
+ $mind.current = minder;
+ minder.on('contentChange', onChange);
+ // @ts-ignore
+ window.minder = minder;
+ });
- const onChange = (_, data) => {
- if (data.node) {
- syncData();
+ return () => {
+ if ($mind.current) {
+ $mind.current.off('contentChange', onChange);
}
};
+ }, [toggleLoading]);
- setTimeout(() => {
- if ($mind.current) {
- const jm = $mind.current;
- const currentData = jm.get_data();
- const isEqual = deepEqual(currentData, data);
- if (!isEqual) {
- jm.show(data);
- }
- return;
- }
-
- const options = {
- container: $container.current,
- editable: isEditable,
- view: {
- hmargin: 100,
- vmargin: 50,
- line_width: window.devicePixelRatio,
- line_color: '#e5e9ef',
- },
- };
- const jm = new jsMind(options);
- jm.show(data);
- $mind.current = jm;
- jm.add_event_listener(onChange);
- }, 0);
- }, [data, isEditable]);
-
- const onResize = (size) => {
- const jm = $mind.current;
- if (!jm) return;
- updateAttributes({ width: size.width, height: size.height });
- setTimeout(() => {
- jm.view.show(true);
- jm.view.showAddHandlerDOMNode();
- }, 100);
- };
-
+ // 数据同步渲染
useEffect(() => {
- const jm = $mind.current;
- if (!jm) return;
+ const minder = $mind.current;
+ if (!minder) return;
+ const currentData = minder.exportJson();
+ const isEqual = deepEqual(currentData, data);
+ if (isEqual) return;
+ minder.importData(data);
+ }, [data]);
- if (isEditable) {
- jm.enable_edit();
- } else {
- jm.disable_edit();
- }
+ // 启用/禁用
+ useEffect(() => {
+ const minder = $mind.current;
+ if (!minder) return;
}, [isEditable]);
const content = (
@@ -105,26 +75,7 @@ export const MindWrapper = ({ editor, node, updateAttributes }) => {
className={cls(styles.renderWrap, 'render-wrapper')}
tabIndex={0}
style={{ width: '100%', height: '100%' }}
- >
- {!isEditable && (
-
- }
- onClick={zoomOut}
- />
- }
- onClick={zoomIn}
- />
-
- )}
-
+ >
);
return (
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/hotbox/expose.js b/packages/client/src/tiptap/wrappers/mind/kityminder/hotbox/expose.js
new file mode 100644
index 0000000..ddaea0e
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/hotbox/expose.js
@@ -0,0 +1,3 @@
+define('expose', function(require, exports, module) {
+ module.exports = window.HotBox = require('./hotbox');
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/hotbox/hotbox.js b/packages/client/src/tiptap/wrappers/mind/kityminder/hotbox/hotbox.js
new file mode 100644
index 0000000..62dd9b7
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/hotbox/hotbox.js
@@ -0,0 +1,636 @@
+define(function(require, exports, module) {
+ var key = require('./key');
+ var KeyControl = require('./keycontrol');
+
+ /**** Dom Utils ****/
+ function createElement(name) {
+ return document.createElement(name);
+ }
+
+ function setElementAttribute(element, name, value) {
+ element.setAttribute(name, value);
+ }
+
+ function getElementAttribute(element, name) {
+ return element.getAttribute(name);
+ }
+
+ function addElementClass(element, name) {
+ element.classList.add(name);
+ }
+
+ function removeElementClass(element, name) {
+ element.classList.remove(name);
+ }
+
+ function appendChild(parent, child) {
+ parent.appendChild(child);
+ }
+ /*******************/
+
+ var IDLE = HotBox.STATE_IDLE = 'idle';
+ var div = 'div';
+
+ /**
+ * Simple Formatter
+ */
+ function format(template, args) {
+ if (typeof(args) != 'object') {
+ args = [].slice.apply(arguments, 1);
+ }
+ return String(template).replace(/\{(\w+)\}/g, function(match, name) {
+ return args[name] || match;
+ });
+ }
+
+ /**
+ * Hot Box Class
+ */
+ function HotBox($container) {
+ if (typeof($container) == 'string') {
+ $container = document.querySelector($container);
+ }
+ if (!$container || !($container instanceof HTMLElement)) {
+ throw new Error('No container or not invalid container for hot box');
+ }
+
+ // 创建 HotBox Dom 解构
+ var $hotBox = createElement(div);
+ addElementClass($hotBox, 'hotbox');
+ appendChild($container, $hotBox);
+
+ // 保存 Dom 解构和父容器
+ this.$element = $hotBox;
+ this.$container = $container;
+
+ // 标示是否是输入法状态
+ this.isIME = false;
+
+ /**
+ * @Desc: 增加一个browser用于判断浏览器类型,方便解决兼容性问题
+ * @Editor: Naixor
+ * @Date: 2015.09.14
+ */
+ this.browser = {
+ sg: /se[\s\S]+metasr/.test(navigator.userAgent.toLowerCase())
+ };
+
+ /*
+ * added by zhangbobell
+ * 2015.09.22
+ * 增加父状态机,以解决在父 FSM 下状态控制的问题,最好的解决办法是增加一个函数队列
+ * 将其中的函数一起执行。//TODO
+ * */
+ this._parentFSM = {};
+
+ // 记录位置
+ this.position = {};
+
+ // 已定义的状态(string => HotBoxState)
+ var _states = {};
+
+ // 主状态(HotBoxState)
+ var _mainState = null;
+
+ // 当前状态(HotBoxState)
+ var _currentState = IDLE;
+
+ // 当前状态堆栈
+ var _stateStack = [];
+
+ // 实例引用
+ var _this = this;
+ var _controler;
+
+ /**
+ * Controller: {
+ * constructor(hotbox: HotBox),
+ * active: () => void
+ * }
+ */
+ function _control(Controller) {
+ if (_controler) {
+ _controler.active();
+ return;
+ }
+
+ Controller = Controller || KeyControl;
+
+ _controler = new Controller(_this);
+ _controler.active();
+
+ $hotBox.onmousedown = function(e) {
+ e.stopPropagation();
+ e.preventDefault();
+ };
+
+ return _this;
+ }
+
+ function _dispatchKey(e) {
+ var type = e.type.toLowerCase();
+ e.keyHash = key.hash(e);
+ e.isKey = function(keyExpression) {
+ if (!keyExpression) return false;
+ var expressions = keyExpression.split(/\s*\|\s*/);
+ while(expressions.length) {
+ if (e.keyHash == key.hash(expressions.shift())) return true;
+ }
+ return false;
+ };
+ e[type] = true;
+ // Boot: keyup and activeKey pressed on IDLE, active main state.
+ if (e.keyup && _this.activeKey && e.isKey(_this.activeKey) && _currentState == IDLE && _mainState) {
+ _activeState('main', {
+ x: $container.clientWidth / 2,
+ y: $container.clientHeight / 2
+ });
+ return;
+ }
+ var handleState = _currentState == IDLE ? _mainState : _currentState;
+ if (handleState) {
+ var handleResult = handleState.handleKeyEvent(e);
+ if (typeof(_this.onkeyevent) == 'function') {
+ e.handleResult = handleResult;
+ _this.onkeyevent(e, handleResult);
+ }
+ return handleResult;
+ }
+ return null;
+ }
+
+ function _addState(name) {
+ if (!name) return _currentState;
+ if (name == IDLE) {
+ throw new Error('Can not define or use the `idle` state.');
+ }
+ _states[name] = _states[name] || new HotBoxState(this, name);
+ if (name == 'main') {
+ _mainState = _states[name];
+ }
+ return _states[name];
+ }
+
+ function _activeState(name, position) {
+ _this.position = position;
+
+ // 回到 IDLE
+ if (name == IDLE) {
+ if (_currentState != IDLE) {
+ _stateStack.shift().deactive();
+ _stateStack = [];
+ }
+ _currentState = IDLE;
+ }
+ // 回退一个状态
+ else if (name == 'back') {
+ if (_currentState != IDLE) {
+ _currentState.deactive();
+ _stateStack.shift();
+ _currentState = _stateStack[0];
+ if (_currentState) {
+ _currentState.active();
+ } else {
+ _currentState = 'idle';
+ }
+ }
+ }
+ // 切换到具体状态
+ else {
+ if (_currentState != IDLE) {
+ _currentState.deactive();
+ }
+ var newState = _states[name];
+ _stateStack.unshift(newState);
+ if (typeof(_this.position) == 'function') {
+ position = _this.position(position);
+ }
+ newState.active(position);
+ _currentState = newState;
+ }
+ }
+
+ function setParentFSM(fsm) {
+ _this._parentFSM = fsm;
+ }
+
+ function getParentFSM() {
+ return _this._parentFSM;
+ }
+
+ this.control = _control;
+ this.state = _addState;
+ this.active = _activeState;
+ this.dispatch = _dispatchKey;
+ this.setParentFSM = setParentFSM;
+ this.getParentFSM = getParentFSM;
+ this.activeKey = 'space';
+ this.actionKey = 'space';
+ }
+
+ /**
+ * 表示热盒某个状态,包含这些状态需要的 Dom 对象
+ */
+ function HotBoxState(hotBox, stateName) {
+
+ var BUTTON_SELECTED_CLASS = 'selected';
+ var BUTTON_PRESSED_CLASS = 'pressed';
+ var STATE_ACTIVE_CLASS = 'active';
+
+ // 状态容器
+ var $state = createElement(div);
+
+ // 四种可见的按钮容器
+ var $center = createElement(div);
+ var $ring = createElement(div);
+ var $ringShape = createElement('div');
+ var $top = createElement(div);
+ var $bottom = createElement(div);
+
+ // 添加 CSS 类
+ addElementClass($state, 'state');
+ addElementClass($state, stateName);
+ addElementClass($center, 'center');
+ addElementClass($ring, 'ring');
+ addElementClass($ringShape, 'ring-shape');
+ addElementClass($top, 'top');
+ addElementClass($bottom, 'bottom');
+
+ // 摆放容器
+ appendChild(hotBox.$element, $state);
+ appendChild($state, $ringShape);
+ appendChild($state, $center);
+ appendChild($state, $ring);
+ appendChild($state, $top);
+ appendChild($state, $bottom);
+
+ // 记住状态名称
+ this.name = stateName;
+
+ // 五种按钮:中心,圆环,上栏,下栏,幕后
+ var buttons = {
+ center: null,
+ ring: [],
+ top: [],
+ bottom: [],
+ behind: []
+ };
+ var allButtons = [];
+ var selectedButton = null;
+ var pressedButton = null;
+
+ var stateActived = false;
+ // 布局,添加按钮后,标记需要布局
+ var needLayout = true;
+
+ function layout() {
+ var radius = buttons.ring.length * 15;
+ layoutRing(radius);
+ layoutTop(radius);
+ layoutBottom(radius);
+ indexPosition();
+ needLayout = false;
+
+ function layoutRing(radius) {
+ var ring = buttons.ring;
+ var step = 2 * Math.PI / ring.length;
+
+ if (buttons.center) {
+ buttons.center.indexedPosition = [0, 0];
+ }
+
+ $ringShape.style.marginLeft = $ringShape.style.marginTop = -radius + 'px';
+ $ringShape.style.width = $ringShape.style.height = (radius + radius) + 'px';
+
+ var $button, angle, x, y;
+ for (var i = 0; i < ring.length; i++) {
+ $button = ring[i].$button;
+ angle = step * i - Math.PI / 2;
+ x = radius * Math.cos(angle);
+ y = radius * Math.sin(angle);
+ ring[i].indexedPosition = [x, y];
+ $button.style.left = x + 'px';
+ $button.style.top = y + 'px';
+ }
+ }
+
+ function layoutTop(radius) {
+ var xOffset = -$top.clientWidth / 2;
+ var yOffset = -radius * 2 - $top.clientHeight / 2;
+ $top.style.marginLeft = xOffset + 'px';
+ $top.style.marginTop = yOffset + 'px';
+ buttons.top.forEach(function(topButton) {
+ var $button = topButton.$button;
+ topButton.indexedPosition = [xOffset + $button.offsetLeft + $button.clientWidth / 2, yOffset];
+ });
+ }
+ function layoutBottom(radius) {
+ var xOffset = -$bottom.clientWidth / 2;
+ var yOffset = radius * 2 - $bottom.clientHeight / 2;
+ $bottom.style.marginLeft = xOffset + 'px';
+ $bottom.style.marginTop = yOffset + 'px';
+ buttons.bottom.forEach(function(bottomButton) {
+ var $button = bottomButton.$button;
+ bottomButton.indexedPosition = [xOffset + $button.offsetLeft + $button.clientWidth / 2, yOffset];
+ });
+ }
+ function indexPosition() {
+ var positionedButtons = allButtons.filter(function(button) {
+ return button.indexedPosition;
+ });
+
+ positionedButtons.forEach(findNeightbour);
+
+ function findNeightbour(button) {
+ var neighbor = {};
+ var coef = 0;
+ var minCoef = {};
+ var homePosition = button.indexedPosition;
+ var candidatePosition, dx, dy, ds;
+ var possible, dir;
+ var abs = Math.abs;
+
+ positionedButtons.forEach(function(candidate) {
+ if (button == candidate) return;
+
+ candidatePosition = candidate.indexedPosition;
+
+ possible = [];
+
+ dx = candidatePosition[0] - homePosition[0];
+ dy = candidatePosition[1] - homePosition[1];
+ ds = Math.sqrt(dx * dx + dy * dy);
+
+ if (abs(dx) > 2) {
+ possible.push(dx > 0 ? 'right' : 'left');
+ possible.push(ds + abs(dy)); // coef for right/left neighbor
+ }
+ if (abs(dy) > 2) {
+ possible.push(dy > 0 ? 'down' : 'up');
+ possible.push(ds + abs(dx)); // coef for up/down neighbor
+ }
+
+ while (possible.length) {
+ dir = possible.shift();
+ coef = possible.shift();
+ if (!neighbor[dir] || coef < minCoef[dir]) {
+ neighbor[dir] = candidate;
+ minCoef[dir] = coef;
+ }
+ }
+ });
+
+ button.neighbor = neighbor;
+ }
+ }
+ }
+
+ function alwaysEnable() {
+ return true;
+ }
+
+ // 为状态创建按钮
+ function createButton(option) {
+ var $button = createElement(div);
+ addElementClass($button, 'button');
+ var render = option.render || defaultButtonRender;
+ $button.innerHTML = render(format, option);
+
+ switch (option.position) {
+ case 'center': appendChild($center, $button); break;
+ case 'ring': appendChild($ring, $button); break;
+ case 'top': appendChild($top, $button); break;
+ case 'bottom': appendChild($bottom, $button); break;
+ }
+
+ return {
+ action: option.action,
+ enable: option.enable || alwaysEnable,
+ beforeShow: option.beforeShow,
+ key: option.key,
+ next: option.next,
+ label: option.label,
+ data: option.data || null,
+ $button: $button
+ };
+ }
+
+ // 默认按钮渲染
+ function defaultButtonRender(format, option) {
+ return format('{label}{key}', {
+ label: option.label,
+ key: option.key && option.key.split('|')[0]
+ });
+ }
+
+ // 为当前状态添加按钮
+ this.button = function(option) {
+ var button = createButton(option);
+ if (option.position == 'center') {
+ buttons.center = button;
+ } else if (buttons[option.position]) {
+ buttons[option.position].push(button);
+ }
+ allButtons.push(button);
+ needLayout = true;
+ };
+
+ function activeState(position) {
+ position = position || {
+ x: hotBox.$container.clientWidth / 2,
+ y: hotBox.$container.clientHeight / 2
+ };
+ if (position) {
+ $state.style.left = position.x + 'px';
+ $state.style.top = position.y + 'px';
+ }
+ allButtons.forEach(function(button) {
+ var $button = button.$button;
+ if ($button) {
+ $button.classList[button.enable() ? 'add' : 'remove']('enabled');
+ }
+
+ if (button.beforeShow) {
+ button.beforeShow();
+ }
+ });
+ addElementClass($state, STATE_ACTIVE_CLASS);
+ if (needLayout) {
+ layout();
+ }
+ if (!selectedButton) {
+ select(buttons.center || buttons.ring[0] || buttons.top[0] || buttons.bottom[0]);
+ }
+ stateActived = true;
+ }
+
+ function deactiveState() {
+ removeElementClass($state, STATE_ACTIVE_CLASS);
+ select(null);
+ stateActived = false;
+ }
+
+ // 激活当前状态
+ this.active = activeState;
+
+ // 反激活当前状态
+ this.deactive = deactiveState;
+
+ function press(button) {
+ if (pressedButton && pressedButton.$button) {
+ removeElementClass(pressedButton.$button, BUTTON_PRESSED_CLASS);
+ }
+ pressedButton = button;
+ if (pressedButton && pressedButton.$button) {
+ addElementClass(pressedButton.$button, BUTTON_PRESSED_CLASS);
+ }
+ }
+
+ function select(button) {
+ if (selectedButton && selectedButton.$button) {
+ if (selectedButton.$button) {
+ removeElementClass(selectedButton.$button, BUTTON_SELECTED_CLASS);
+ }
+ }
+ selectedButton = button;
+ if (selectedButton && selectedButton.$button) {
+ addElementClass(selectedButton.$button, BUTTON_SELECTED_CLASS);
+ }
+ }
+
+ $state.onmouseup = function(e) {
+ if (e.button) return;
+ var target = e.target;
+ while (target && target != $state) {
+ if (target.classList.contains('button')) {
+ allButtons.forEach(function(button) {
+ if (button.$button == target) {
+ execute(button);
+ }
+ });
+ }
+ target = target.parentNode;
+ }
+ };
+
+ this.handleKeyEvent = function(e) {
+ var handleResult = null;
+ /**
+ * @Desc: 搜狗浏览器下esc只触发keyup,因此做兼容性处理
+ * @Editor: Naixor
+ * @Date: 2015.09.14
+ */
+ if (hotBox.browser.sg) {
+ if (e.isKey('esc')) {
+ if (pressedButton) { // 若存在已经按下的按钮,则取消操作
+ if (!e.isKey(pressedButton.key)) { // the button is not esc
+ press(null);
+ }
+ } else {
+ hotBox.active('back', hotBox.position);
+ }
+ return 'back';
+ };
+ };
+ if (e.keydown || (hotBox.isIME && e.keyup)) {
+ allButtons.forEach(function(button) {
+ if (button.enable() && e.isKey(button.key)) {
+ if (stateActived || hotBox.hintDeactiveMainState) {
+ select(button);
+ press(button);
+ handleResult = 'buttonpress';
+
+ // 如果是 keyup 事件触发的,因为没有后续的按键事件,所以就直接执行
+ if(e.keyup) {
+ execute(button);
+ handleResult = 'execute';
+ return handleResult;
+ }
+ } else {
+ execute(button);
+ handleResult = 'execute';
+ }
+ e.preventDefault();
+ e.stopPropagation();
+ if (!stateActived && hotBox.hintDeactiveMainState) {
+ hotBox.active(stateName, hotBox.position);
+ }
+ }
+ });
+ if (stateActived) {
+ if (e.isKey('esc')) {
+ if (pressedButton) { // 若存在已经按下的按钮,则取消操作
+ if (!e.isKey(pressedButton.key)) { // the button is not esc
+ press(null);
+ }
+ } else {
+ hotBox.active('back', hotBox.position);
+ }
+ return 'back';
+ }
+ ['up', 'down', 'left', 'right'].forEach(function(dir) {
+ if (!e.isKey(dir)) return;
+ if (!selectedButton) {
+ select(buttons.center || buttons.ring[0] || buttons.top[0] || buttons.bottom[0]);
+ return;
+ }
+ var neighbor = selectedButton.neighbor[dir];
+ while (neighbor && !neighbor.enable()) {
+ neighbor = neighbor.neighbor[dir];
+ }
+ if (neighbor) {
+ select(neighbor);
+ }
+ handleResult = 'navigate';
+ });
+
+ // 若是由 keyup 触发的,则直接执行选中的按钮
+ if (e.isKey('space') && e.keyup) {
+ execute(selectedButton);
+ e.preventDefault();
+ e.stopPropagation();
+ handleResult = 'execute';
+ } else if (e.isKey('space') && selectedButton) {
+ press(selectedButton);
+ handleResult = 'buttonpress';
+ } else if (pressedButton && pressedButton != selectedButton) {
+ press(null);
+ handleResult = 'selectcancel';
+ }
+ }
+ }
+ else if (e.keyup && (stateActived || !hotBox.hintDeactiveMainState)) {
+ if (pressedButton) {
+ if (e.isKey('space') && selectedButton == pressedButton || e.isKey(pressedButton.key)) {
+ execute(pressedButton);
+ e.preventDefault();
+ e.stopPropagation();
+ handleResult = 'execute';
+ }
+ }
+ }
+
+ /*
+ * Add by zhangbobell 2015.09.06
+ * 增加了下面这一个判断因为 safari 下开启输入法后,所有的 keydown 的 keycode 都为 229,
+ * 只能以 keyup 的 keycode 进行判断
+ * */
+ hotBox.isIME = (e.keyCode == 229 && e.keydown);
+
+ return handleResult;
+ };
+
+ function execute(button) {
+ if (button) {
+ if (!button.enable || button.enable()) {
+ if (button.action) button.action(button);
+ hotBox.active(button.next || IDLE, hotBox.position);
+ }
+ press(null);
+ select(null);
+ }
+ }
+ }
+
+ module.exports = HotBox;
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/hotbox/key.js b/packages/client/src/tiptap/wrappers/mind/kityminder/hotbox/key.js
new file mode 100644
index 0000000..023dd5c
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/hotbox/key.js
@@ -0,0 +1,59 @@
+define(function(require, exports, module) {
+ var keymap = require('./keymap');
+
+ var CTRL_MASK = 0x1000;
+ var ALT_MASK = 0x2000;
+ var SHIFT_MASK = 0x4000;
+
+ function hash(unknown) {
+ if (typeof(unknown) == 'string') {
+ return hashKeyExpression(unknown);
+ }
+ return hashKeyEvent(unknown);
+ }
+ function is(a, b) {
+ return a && b && hash(a) == hash(b);
+ }
+ exports.hash = hash;
+ exports.is = is;
+
+
+ function hashKeyEvent(keyEvent) {
+ var hashCode = 0;
+ if (keyEvent.ctrlKey || keyEvent.metaKey) {
+ hashCode |= CTRL_MASK;
+ }
+ if (keyEvent.altKey) {
+ hashCode |= ALT_MASK;
+ }
+ if (keyEvent.shiftKey) {
+ hashCode |= SHIFT_MASK;
+ }
+ // Shift, Control, Alt KeyCode ignored.
+ if ([16, 17, 18, 91].indexOf(keyEvent.keyCode) == -1) {
+ hashCode |= keyEvent.keyCode;
+ }
+ return hashCode;
+ }
+
+ function hashKeyExpression(keyExpression) {
+ var hashCode = 0;
+ keyExpression.toLowerCase().split(/\s*\+\s*/).forEach(function(name) {
+ switch(name) {
+ case 'ctrl':
+ case 'cmd':
+ hashCode |= CTRL_MASK;
+ break;
+ case 'alt':
+ hashCode |= ALT_MASK;
+ break;
+ case 'shift':
+ hashCode |= SHIFT_MASK;
+ break;
+ default:
+ hashCode |= keymap[name];
+ }
+ });
+ return hashCode;
+ }
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/hotbox/keycontrol.js b/packages/client/src/tiptap/wrappers/mind/kityminder/hotbox/keycontrol.js
new file mode 100644
index 0000000..d6ab316
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/hotbox/keycontrol.js
@@ -0,0 +1,68 @@
+define(function(require, exports, module) {
+
+ var key = require('./key');
+ var FOCUS_CLASS = 'hotbox-focus';
+ var RECEIVER_CLASS = 'hotbox-key-receiver';
+
+ function KeyControl(hotbox) {
+ var _this = this;
+ var _receiver;
+ var _actived = true;
+ var _receiverIsSelfCreated = false;
+ var $container = hotbox.$container;
+
+ _createReceiver();
+ _bindReceiver();
+ _bindContainer();
+ _active();
+
+ function _createReceiver() {
+ _receiver = document.createElement('input');
+ _receiver.classList.add(RECEIVER_CLASS);
+ $container.appendChild(_receiver);
+ _receiverIsSelfCreated = true;
+ }
+
+ function _bindReceiver() {
+ _receiver.onkeyup = _handle;
+ _receiver.onkeypress = _handle;
+ _receiver.onkeydown = _handle;
+ _receiver.onfocus = _active;
+ _receiver.onblur = _deactive;
+ if (_receiverIsSelfCreated) {
+ _receiver.oninput = function(e) { _receiver.value = null; };
+ }
+ }
+
+ function _bindContainer() {
+ $container.onmousedown = function(e) {
+ _active();
+ e.preventDefault();
+ };
+ }
+
+ function _handle(keyEvent) {
+ if (!_actived) return;
+ hotbox.dispatch(keyEvent);
+ }
+
+ function _active() {
+ _receiver.select();
+ _receiver.focus();
+ _actived = true;
+ $container.classList.add(FOCUS_CLASS);
+ }
+
+ function _deactive() {
+ _receiver.blur();
+ _actived = false;
+ $container.classList.remove(FOCUS_CLASS);
+ }
+
+ this.handle = _handle;
+ this.active = _active;
+ this.deactive = _deactive;
+ }
+
+ module.exports = KeyControl;
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/hotbox/keymap.js b/packages/client/src/tiptap/wrappers/mind/kityminder/hotbox/keymap.js
new file mode 100644
index 0000000..3ed6433
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/hotbox/keymap.js
@@ -0,0 +1,82 @@
+define(function(require, exports, module) {
+ var keymap = {
+
+ 'Shift': 16,
+ 'Control': 17,
+ 'Alt': 18,
+ 'CapsLock': 20,
+
+ 'BackSpace': 8,
+ 'Tab': 9,
+ 'Enter': 13,
+ 'Esc': 27,
+ 'Space': 32,
+
+ 'PageUp': 33,
+ 'PageDown': 34,
+ 'End': 35,
+ 'Home': 36,
+
+ 'Insert': 45,
+
+ 'Left': 37,
+ 'Up': 38,
+ 'Right': 39,
+ 'Down': 40,
+
+ 'Direction': {
+ 37: 1,
+ 38: 1,
+ 39: 1,
+ 40: 1
+ },
+
+ 'Delete': 46,
+
+ 'NumLock': 144,
+
+ 'Cmd': 91,
+ 'CmdFF': 224,
+ 'F1': 112,
+ 'F2': 113,
+ 'F3': 114,
+ 'F4': 115,
+ 'F5': 116,
+ 'F6': 117,
+ 'F7': 118,
+ 'F8': 119,
+ 'F9': 120,
+ 'F10': 121,
+ 'F11': 122,
+ 'F12': 123,
+
+ '`': 192,
+ '=': 187,
+ '-': 189,
+
+ '/': 191,
+ '.': 190
+ };
+
+ // 小写适配
+ for (var key in keymap) {
+ if (keymap.hasOwnProperty(key)) {
+ keymap[key.toLowerCase()] = keymap[key];
+ }
+ }
+ var aKeyCode = 65;
+ var aCharCode = 'a'.charCodeAt(0);
+
+ // letters
+ 'abcdefghijklmnopqrstuvwxyz'.split('').forEach(function(letter) {
+ keymap[letter] = aKeyCode + (letter.charCodeAt(0) - aCharCode);
+ });
+
+ // numbers
+ var n = 9;
+ do {
+ keymap[n.toString()] = n + 48;
+ } while (n--);
+
+ module.exports = keymap;
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/index.ts b/packages/client/src/tiptap/wrappers/mind/kityminder/index.ts
new file mode 100644
index 0000000..e8361d3
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/index.ts
@@ -0,0 +1,9 @@
+export const loadKityMinder = async (): Promise => {
+ await import('kity');
+ await import('./kity-core/kityminder');
+ await import('./kity-editor/expose-editor');
+
+ const Editor = (window as any).kityminder.Editor;
+
+ return Editor;
+};
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/connect/arc.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/connect/arc.js
new file mode 100644
index 0000000..3720b05
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/connect/arc.js
@@ -0,0 +1,49 @@
+/**
+ * @fileOverview
+ *
+ * 圆弧连线
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+
+define(function(require, exports, module) {
+ var kity = require('../core/kity');
+ var connect = require('../core/connect');
+
+ var connectMarker = new kity.Marker().pipe(function() {
+ var r = 7;
+ var dot = new kity.Circle(r - 1);
+ this.addShape(dot);
+ this.setRef(r - 1, 0).setViewBox(-r, -r, r + r, r + r).setWidth(r).setHeight(r);
+ this.dot = dot;
+ this.node.setAttribute('markerUnits', 'userSpaceOnUse');
+ });
+
+ connect.register('arc', function(node, parent, connection, width, color) {
+
+ var box = node.getLayoutBox(),
+ pBox = parent.getLayoutBox();
+
+ var start, end, vector;
+ var abs = Math.abs;
+ var pathData = [];
+ var side = box.x > pBox.x ? 'right' : 'left';
+
+ node.getMinder().getPaper().addResource(connectMarker);
+
+ start = new kity.Point(pBox.cx, pBox.cy);
+ end = side == 'left' ?
+ new kity.Point(box.right + 2, box.cy) :
+ new kity.Point(box.left - 2, box.cy);
+
+ vector = kity.Vector.fromPoints(start, end);
+ pathData.push('M', start);
+ pathData.push('A', abs(vector.x), abs(vector.y), 0, 0, (vector.x * vector.y > 0 ? 0 : 1), end);
+
+ connection.setMarker(connectMarker);
+ connectMarker.dot.fill(color);
+
+ connection.setPathData(pathData);
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/connect/arc_tp.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/connect/arc_tp.js
new file mode 100644
index 0000000..e68bedd
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/connect/arc_tp.js
@@ -0,0 +1,85 @@
+/**
+ *
+ * 圆弧连线
+ *
+ * @author: along
+ * @copyright: bpd729@163.com , 2015
+ */
+define(function(require, exports, module) {
+ var kity = require('../core/kity');
+ var connect = require('../core/connect');
+
+ var connectMarker = new kity.Marker().pipe(function () {
+ var r = 7;
+ var dot = new kity.Circle(r - 1);
+ this.addShape(dot);
+ this.setRef(r - 1, 0).setViewBox(-r, -r, r + r, r + r).setWidth(r).setHeight(r);
+ this.dot = dot;
+ this.node.setAttribute('markerUnits', 'userSpaceOnUse');
+ });
+
+ /**
+ * 天盘图连线除了连接当前节点和前一个节点外, 还需要渲染当前节点和后一个节点的连接, 防止样式上的断线
+ * 这是天盘图与其余的模板不同的地方
+ */
+ connect.register('arc_tp', function (node, parent, connection, width, color) {
+ var end_box = node.getLayoutBox(),
+ start_box = parent.getLayoutBox();
+
+ var index = node.getIndex();
+ var nextNode = parent.getChildren()[index + 1];
+
+
+ if (node.getIndex() > 0) {
+ start_box = parent.getChildren()[index - 1].getLayoutBox();
+ }
+
+
+ var start, end, vector;
+ var abs = Math.abs;
+ var pathData = [];
+ var side = end_box.x > start_box.x ? 'right' : 'left';
+
+ node.getMinder().getPaper().addResource(connectMarker);
+
+
+ start = new kity.Point(start_box.cx, start_box.cy);
+ end = new kity.Point(end_box.cx, end_box.cy);
+
+ var jl = Math.sqrt(Math.pow((start.x - end.x), 2) + Math.pow((start.y - end.y), 2)); //两圆中心点距离
+
+ jl = node.getIndex() == 0 ? jl * 0.4 : jl;
+
+
+ vector = kity.Vector.fromPoints(start, end);
+ pathData.push('M', start);
+ pathData.push('A', jl, jl, 0, 0, 1, end);
+
+
+ connection.setMarker(connectMarker);
+ connectMarker.dot.fill(color);
+ connection.setPathData(pathData);
+
+
+ // 设置下一个的节点的连接线
+ if (nextNode && nextNode.getConnection()) {
+ var nextConnection = nextNode.getConnection();
+ var next_end_box = nextNode.getLayoutBox();
+ var next_end = new kity.Point(next_end_box.cx, next_end_box.cy);
+
+ var jl2 = Math.sqrt(Math.pow((end.x - next_end.x), 2) + Math.pow((end.y - next_end.y), 2)); //两圆中心点距离
+
+ pathData = [];
+
+ pathData.push('M', end);
+ pathData.push('A', jl2, jl2, 0, 0, 1, next_end);
+
+ nextConnection.setMarker(connectMarker);
+ connectMarker.dot.fill(color);
+
+ nextConnection.setPathData(pathData);
+
+ }
+
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/connect/bezier.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/connect/bezier.js
new file mode 100644
index 0000000..f1cc3d3
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/connect/bezier.js
@@ -0,0 +1,42 @@
+/**
+ * @fileOverview
+ *
+ * 提供折线相连的方法
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+
+define(function(require, exports, module) {
+ var kity = require('../core/kity');
+ var connect = require('../core/connect');
+
+ connect.register('bezier', function(node, parent, connection) {
+
+ // 连线起点和终点
+ var po = parent.getLayoutVertexOut(),
+ pi = node.getLayoutVertexIn();
+
+ // 连线矢量和方向
+ var v = parent.getLayoutVectorOut().normalize();
+
+ var r = Math.round;
+ var abs = Math.abs;
+
+ var pathData = [];
+ pathData.push('M', r(po.x), r(po.y));
+
+ if (abs(v.x) > abs(v.y)) {
+ // x - direction
+ var hx = (pi.x + po.x) / 2;
+ pathData.push('C', hx, po.y, hx, pi.y, pi.x, pi.y);
+ } else {
+ // y - direction
+ var hy = (pi.y + po.y) / 2;
+ pathData.push('C', po.x, hy, pi.x, hy, pi.x, pi.y);
+ }
+
+ connection.setMarker(null);
+ connection.setPathData(pathData);
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/connect/fish-bone-master.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/connect/fish-bone-master.js
new file mode 100644
index 0000000..6da3c2e
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/connect/fish-bone-master.js
@@ -0,0 +1,33 @@
+/**
+ * @fileOverview
+ *
+ * 鱼骨头主干连线
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+
+define(function(require, exports, module) {
+ var kity = require('../core/kity');
+ var connect = require('../core/connect');
+
+ connect.register('fish-bone-master', function(node, parent, connection) {
+
+ var pout = parent.getLayoutVertexOut(),
+ pin = node.getLayoutVertexIn();
+
+ var abs = Math.abs;
+
+ var dy = abs(pout.y - pin.y),
+ dx = abs(pout.x - pin.x);
+
+ var pathData = [];
+
+ pathData.push('M', pout.x, pout.y);
+ pathData.push('h', dx - dy);
+ pathData.push('L', pin.x, pin.y);
+
+ connection.setMarker(null);
+ connection.setPathData(pathData);
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/connect/l.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/connect/l.js
new file mode 100644
index 0000000..c678f66
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/connect/l.js
@@ -0,0 +1,34 @@
+/**
+ * @fileOverview
+ *
+ * "L" 连线
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+
+define(function(require, exports, module) {
+ var kity = require('../core/kity');
+ var connect = require('../core/connect');
+
+ connect.register('l', function(node, parent, connection) {
+
+ var po = parent.getLayoutVertexOut();
+ var pi = node.getLayoutVertexIn();
+ var vo = parent.getLayoutVectorOut();
+
+ var pathData = [];
+ var r = Math.round,
+ abs = Math.abs;
+
+ pathData.push('M', po.round());
+ if (abs(vo.x) > abs(vo.y)) {
+ pathData.push('H', r(pi.x));
+ } else {
+ pathData.push('V', pi.y);
+ }
+ pathData.push('L', pi);
+
+ connection.setPathData(pathData);
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/connect/poly.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/connect/poly.js
new file mode 100644
index 0000000..27612fd
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/connect/poly.js
@@ -0,0 +1,63 @@
+/**
+ * @fileOverview
+ *
+ * 提供折线相连的方法
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+
+define(function(require, exports, module) {
+ var kity = require('../core/kity');
+ var connect = require('../core/connect');
+
+ connect.register('poly', function(node, parent, connection, width) {
+
+ // 连线起点和终点
+ var po = parent.getLayoutVertexOut(),
+ pi = node.getLayoutVertexIn();
+
+ // 连线矢量和方向
+ var v = parent.getLayoutVectorOut().normalize();
+
+ var r = Math.round;
+ var abs = Math.abs;
+
+ var pathData = [];
+ pathData.push('M', r(po.x), r(po.y));
+
+ switch (true) {
+ case abs(v.x) > abs(v.y) && v.x < 0:
+ // left
+ pathData.push('h', -parent.getStyle('margin-left'));
+ pathData.push('v', pi.y - po.y);
+ pathData.push('H', pi.x);
+ break;
+
+ case abs(v.x) > abs(v.y) && v.x >= 0:
+ // right
+ pathData.push('h', parent.getStyle('margin-right'));
+ pathData.push('v', pi.y - po.y);
+ pathData.push('H', pi.x);
+ break;
+
+ case abs(v.x) <= abs(v.y) && v.y < 0:
+ // top
+ pathData.push('v', -parent.getStyle('margin-top'));
+ pathData.push('h', pi.x - po.x);
+ pathData.push('V', pi.y);
+ break;
+
+ case abs(v.x) <= abs(v.y) && v.y >= 0:
+ // bottom
+ pathData.push('v', parent.getStyle('margin-bottom'));
+ pathData.push('h', pi.x - po.x);
+ pathData.push('V', pi.y);
+ break;
+
+ }
+
+ connection.setMarker(null);
+ connection.setPathData(pathData);
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/connect/under.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/connect/under.js
new file mode 100644
index 0000000..d7f21aa
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/connect/under.js
@@ -0,0 +1,50 @@
+/**
+ * @fileOverview
+ *
+ * 下划线连线
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+
+define(function(require, exports, module) {
+ var kity = require('../core/kity');
+ var connect = require('../core/connect');
+
+ connect.register('under', function(node, parent, connection, width, color) {
+
+ var box = node.getLayoutBox(),
+ pBox = parent.getLayoutBox();
+
+ var start, end, vector;
+ var abs = Math.abs;
+ var pathData = [];
+ var side = box.x > pBox.x ? 'right' : 'left';
+
+
+ var radius = node.getStyle('connect-radius');
+ var underY = box.bottom + 3;
+ var startY = parent.getType() == 'sub' ? pBox.bottom + 3 : pBox.cy;
+ var p1, p2, p3, mx;
+
+ if (side == 'right') {
+ p1 = new kity.Point(pBox.right, startY);
+ p2 = new kity.Point(box.left - 10, underY);
+ p3 = new kity.Point(box.right, underY);
+ } else {
+ p1 = new kity.Point(pBox.left, startY);
+ p2 = new kity.Point(box.right + 10, underY);
+ p3 = new kity.Point(box.left, underY);
+ }
+
+ mx = (p1.x + p2.x) / 2;
+
+ pathData.push('M', p1);
+ pathData.push('C', mx, p1.y, mx, p2.y, p2);
+ pathData.push('L', p3);
+
+ connection.setMarker(null);
+
+ connection.setPathData(pathData);
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/_boxv.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/_boxv.js
new file mode 100644
index 0000000..7ebc6b9
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/_boxv.js
@@ -0,0 +1,34 @@
+/**
+ * @fileOverview
+ *
+ * 调试工具:为 kity.Box 提供一个可视化的渲染
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+
+define(function(require, exports, module) {
+ var kity = require('./kity');
+ var Minder = require('./minder');
+
+ if (location.href.indexOf('boxv') != -1) {
+
+ var vrect;
+
+ Object.defineProperty(kity.Box.prototype, 'visualization', {
+ get: function() {
+ if (!vrect) return null;
+ return vrect.setBox(this);
+ }
+ });
+
+ Minder.registerInitHook(function() {
+ this.on('paperrender', function() {
+ vrect = new kity.Rect();
+ vrect.fill('rgba(200, 200, 200, .5)');
+ vrect.stroke('orange');
+ this.getRenderContainer().addShape(vrect);
+ });
+ });
+ }
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/animate.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/animate.js
new file mode 100644
index 0000000..75d266f
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/animate.js
@@ -0,0 +1,43 @@
+/**
+ * @fileOverview
+ *
+ * 动画控制
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+define(function(require, exports, module) {
+ var Minder = require('./minder');
+
+ var animateDefaultOptions = {
+ enableAnimation: true,
+ layoutAnimationDuration: 300,
+ viewAnimationDuration: 100,
+ zoomAnimationDuration: 300
+ };
+ var resoredAnimationOptions = {};
+
+ Minder.registerInitHook(function() {
+ this.setDefaultOptions(animateDefaultOptions);
+ if (!this.getOption('enableAnimation')) {
+ this.disableAnimation();
+ }
+ });
+
+ Minder.prototype.enableAnimation = function() {
+ for (var name in animateDefaultOptions) {
+ if (animateDefaultOptions.hasOwnProperty(name)) {
+ this.setOption(resoredAnimationOptions[name]);
+ }
+ }
+ };
+
+ Minder.prototype.disableAnimation = function() {
+ for (var name in animateDefaultOptions) {
+ if (animateDefaultOptions.hasOwnProperty(name)) {
+ resoredAnimationOptions[name] = this.getOption(name);
+ this.setOption(name, 0);
+ }
+ }
+ };
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/command.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/command.js
new file mode 100644
index 0000000..f36a3da
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/command.js
@@ -0,0 +1,169 @@
+define(function(require, exports, module) {
+ var kity = require('./kity');
+ var utils = require('./utils');
+ var Minder = require('./minder');
+ var MinderNode = require('./node');
+ var MinderEvent = require('./event');
+
+ var COMMAND_STATE_NORMAL = 0;
+ var COMMAND_STATE_DISABLED = -1;
+ var COMMAND_STATE_ACTIVED = 1;
+
+ /**
+ * 表示一个命令,包含命令的查询及执行
+ */
+ var Command = kity.createClass('Command', {
+ constructor: function() {
+ this._isContentChange = true;
+ this._isSelectionChange = false;
+ },
+
+ execute: function(minder, args) {
+ throw new Error('Not Implement: Command.execute()');
+ },
+
+ setContentChanged: function(val) {
+ this._isContentChange = !!val;
+ },
+
+ isContentChanged: function() {
+ return this._isContentChange;
+ },
+
+ setSelectionChanged: function(val) {
+ this._isSelectionChange = !!val;
+ },
+
+ isSelectionChanged: function() {
+ return this._isContentChange;
+ },
+
+ queryState: function(km) {
+ return COMMAND_STATE_NORMAL;
+ },
+
+ queryValue: function(km) {
+ return 0;
+ },
+
+ isNeedUndo: function() {
+ return true;
+ }
+ });
+
+ Command.STATE_NORMAL = COMMAND_STATE_NORMAL;
+ Command.STATE_ACTIVE = COMMAND_STATE_ACTIVED;
+ Command.STATE_DISABLED = COMMAND_STATE_DISABLED;
+
+ kity.extendClass(Minder, {
+ _getCommand: function(name) {
+ return this._commands[name.toLowerCase()];
+ },
+
+ _queryCommand: function(name, type, args) {
+ var cmd = this._getCommand(name);
+ if (cmd) {
+ var queryCmd = cmd['query' + type];
+ if (queryCmd)
+ return queryCmd.apply(cmd, [this].concat(args));
+ }
+ return 0;
+ },
+
+ /**
+ * @method queryCommandState()
+ * @for Minder
+ * @description 查询指定命令的状态
+ *
+ * @grammar queryCommandName(name) => {number}
+ *
+ * @param {string} name 要查询的命令名称
+ *
+ * @return {number}
+ * -1: 命令不存在或命令当前不可用
+ * 0: 命令可用
+ * 1: 命令当前可用并且已经执行过
+ */
+ queryCommandState: function(name) {
+ return this._queryCommand(name, 'State', [].slice.call(arguments, 1));
+ },
+
+ /**
+ * @method queryCommandValue()
+ * @for Minder
+ * @description 查询指定命令当前的执行值
+ *
+ * @grammar queryCommandValue(name) => {any}
+ *
+ * @param {string} name 要查询的命令名称
+ *
+ * @return {any}
+ * 如果命令不存在,返回 undefined
+ * 不同命令具有不同返回值,具体请查看 [Command](command) 章节
+ */
+ queryCommandValue: function(name) {
+ return this._queryCommand(name, 'Value', [].slice.call(arguments, 1));
+ },
+
+ /**
+ * @method execCommand()
+ * @for Minder
+ * @description 执行指定的命令。
+ *
+ * @grammar execCommand(name, args...)
+ *
+ * @param {string} name 要执行的命令名称
+ * @param {argument} args 要传递给命令的其它参数
+ */
+ execCommand: function(name) {
+ if (!name) return null;
+
+ name = name.toLowerCase();
+
+ var cmdArgs = [].slice.call(arguments, 1),
+ cmd, stoped, result, eventParams;
+ var me = this;
+ cmd = this._getCommand(name);
+
+ eventParams = {
+ command: cmd,
+ commandName: name.toLowerCase(),
+ commandArgs: cmdArgs
+ };
+ if (!cmd || !~this.queryCommandState(name)) {
+ return false;
+ }
+
+ if (!this._hasEnterExecCommand) {
+ this._hasEnterExecCommand = true;
+
+ stoped = this._fire(new MinderEvent('beforeExecCommand', eventParams, true));
+
+ if (!stoped) {
+ this._fire(new MinderEvent('preExecCommand', eventParams, false));
+
+ result = cmd.execute.apply(cmd, [me].concat(cmdArgs));
+
+ this._fire(new MinderEvent('execCommand', eventParams, false));
+
+ if (cmd.isContentChanged()) {
+ this._firePharse(new MinderEvent('contentchange'));
+ }
+
+ this._interactChange();
+ }
+ this._hasEnterExecCommand = false;
+ } else {
+ result = cmd.execute.apply(cmd, [me].concat(cmdArgs));
+
+ if (!this._hasEnterExecCommand) {
+ this._interactChange();
+ }
+ }
+
+ return result === undefined ? null : result;
+ }
+ });
+
+ module.exports = Command;
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/compatibility.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/compatibility.js
new file mode 100644
index 0000000..e836b64
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/compatibility.js
@@ -0,0 +1,92 @@
+define(function(require, exports, module) {
+ var utils = require('./utils');
+
+ function compatibility(json) {
+
+ var version = json.version || (json.root ? '1.4.0' : '1.1.3');
+
+ switch (version) {
+ case '1.1.3':
+ c_113_120(json);
+ /* falls through */
+ case '1.2.0':
+ case '1.2.1':
+ c_120_130(json);
+ /* falls through */
+ case '1.3.0':
+ case '1.3.1':
+ case '1.3.2':
+ case '1.3.3':
+ case '1.3.4':
+ case '1.3.5':
+ /* falls through */
+ c_130_140(json);
+ }
+ return json;
+ }
+
+ function traverse(node, fn) {
+ fn(node);
+ if (node.children) node.children.forEach(function(child) {
+ traverse(child, fn);
+ });
+ }
+
+ /* 脑图数据升级 */
+ function c_120_130(json) {
+ traverse(json, function(node) {
+ var data = node.data;
+ delete data.layout_bottom_offset;
+ delete data.layout_default_offset;
+ delete data.layout_filetree_offset;
+ });
+ }
+
+ /**
+ * 脑图数据升级
+ * v1.1.3 => v1.2.0
+ * */
+ function c_113_120(json) {
+ // 原本的布局风格
+ var ocs = json.data.currentstyle;
+ delete json.data.currentstyle;
+
+ // 为 1.2 选择模板,同时保留老版本文件的皮肤
+ if (ocs == 'bottom') {
+ json.template = 'structure';
+ json.theme = 'snow';
+ } else if (ocs == 'default') {
+ json.template = 'default';
+ json.theme = 'classic';
+ }
+
+ traverse(json, function(node) {
+ var data = node.data;
+
+ // 升级优先级、进度图标
+ if ('PriorityIcon' in data) {
+ data.priority = data.PriorityIcon;
+ delete data.PriorityIcon;
+ }
+ if ('ProgressIcon' in data) {
+ data.progress = 1 + ((data.ProgressIcon - 1) << 1);
+ delete data.ProgressIcon;
+ }
+
+ // 删除过时属性
+ delete data.point;
+ delete data.layout;
+ });
+ }
+
+ function c_130_140(json) {
+ json.root = {
+ data: json.data,
+ children: json.children
+ };
+ delete json.data;
+ delete json.children;
+ }
+
+ return compatibility;
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/connect.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/connect.js
new file mode 100644
index 0000000..451feb7
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/connect.js
@@ -0,0 +1,126 @@
+define(function(require, exports, module) {
+ var kity = require('./kity');
+ var utils = require('./utils');
+ var Module = require('./module');
+ var Minder = require('./minder');
+ var MinderNode = require('./node');
+
+ // 连线提供方
+ var _connectProviders = {};
+
+ function register(name, provider) {
+ _connectProviders[name] = provider;
+ }
+
+ register('default', function(node, parent, connection) {
+ connection.setPathData([
+ 'M', parent.getLayoutVertexOut(),
+ 'L', node.getLayoutVertexIn()
+ ]);
+ });
+
+ kity.extendClass(MinderNode, {
+ /**
+ * @private
+ * @method getConnect()
+ * @for MinderNode
+ * @description 获取当前节点的连线类型
+ *
+ * @grammar getConnect() => {string}
+ */
+ getConnect: function() {
+ return this.data.connect || 'default';
+ },
+
+ getConnectProvider: function() {
+ return _connectProviders[this.getConnect()] || _connectProviders['default'];
+ },
+
+ /**
+ * @private
+ * @method getConnection()
+ * @for MinderNode
+ * @description 获取当前节点的连线对象
+ *
+ * @grammar getConnection() => {kity.Path}
+ */
+ getConnection: function() {
+ return this._connection || null;
+ }
+ });
+
+ kity.extendClass(Minder, {
+
+ getConnectContainer: function() {
+ return this._connectContainer;
+ },
+
+ createConnect: function(node) {
+ if (node.isRoot()) return;
+
+ var connection = new kity.Path();
+
+ node._connection = connection;
+
+ this._connectContainer.addShape(connection);
+ this.updateConnect(node);
+ },
+
+ removeConnect: function(node) {
+ var me = this;
+ node.traverse(function(node) {
+ me._connectContainer.removeShape(node._connection);
+ node._connection = null;
+ });
+ },
+
+ updateConnect: function(node) {
+
+ var connection = node._connection;
+ var parent = node.parent;
+
+ if (!parent || !connection) return;
+
+ if (parent.isCollapsed()) {
+ connection.setVisible(false);
+ return;
+ }
+ connection.setVisible(true);
+
+ var provider = node.getConnectProvider();
+
+ var strokeColor = node.getStyle('connect-color') || 'white',
+ strokeWidth = node.getStyle('connect-width') || 2;
+
+ connection.stroke(strokeColor, strokeWidth);
+
+ provider(node, parent, connection, strokeWidth, strokeColor);
+
+ if (strokeWidth % 2 === 0) {
+ connection.setTranslate(0.5, 0.5);
+ } else {
+ connection.setTranslate(0, 0);
+ }
+ }
+ });
+
+ Module.register('Connect', {
+ init: function() {
+ this._connectContainer = new kity.Group().setId(utils.uuid('minder_connect_group'));
+ this.getRenderContainer().prependShape(this._connectContainer);
+ },
+ events: {
+ 'nodeattach': function(e) {
+ this.createConnect(e.node);
+ },
+ 'nodedetach': function(e) {
+ this.removeConnect(e.node);
+ },
+ 'layoutapply layoutfinish noderender': function(e) {
+ this.updateConnect(e.node);
+ }
+ }
+ });
+
+ exports.register = register;
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/data.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/data.js
new file mode 100644
index 0000000..a851000
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/data.js
@@ -0,0 +1,362 @@
+define(function(require, exports, module) {
+ var kity = require('./kity');
+ var utils = require('./utils');
+ var Minder = require('./minder');
+ var MinderNode = require('./node');
+ var MinderEvent = require('./event');
+ var compatibility = require('./compatibility');
+ var Promise = require('./promise');
+
+ var protocols = {};
+
+ function registerProtocol(name, protocol) {
+ protocols[name] = protocol;
+
+ for (var pname in protocols) {
+ if (protocols.hasOwnProperty(pname)) {
+ protocols[pname] = protocols[pname];
+ protocols[pname].name = pname;
+ }
+ }
+ }
+
+ function getRegisterProtocol(name) {
+ return name === undefined ? protocols : (protocols[name] || null);
+ }
+
+ exports.registerProtocol = registerProtocol;
+ exports.getRegisterProtocol = getRegisterProtocol;
+
+ // 导入导出
+ kity.extendClass(Minder, {
+
+ // 自动导入
+ setup: function(target) {
+ if (typeof target == 'string') {
+ target = document.querySelector(target);
+ }
+ if (!target) return;
+ var protocol = target.getAttribute('minder-data-type');
+ if (protocol in protocols) {
+ var data = target.textContent;
+ target.textContent = null;
+ this.renderTo(target);
+ this.importData(protocol, data);
+ }
+ return this;
+ },
+
+ /**
+ * @method exportJson()
+ * @for Minder
+ * @description
+ * 导出当前脑图数据为 JSON 对象,导出的数据格式请参考 [Data](data) 章节。
+ * @grammar exportJson() => {plain}
+ */
+ exportJson: function() {
+ /* 导出 node 上整棵树的数据为 JSON */
+ function exportNode(node) {
+ var exported = {};
+ exported.data = node.getData();
+ var childNodes = node.getChildren();
+ exported.children = [];
+ for (var i = 0; i < childNodes.length; i++) {
+ exported.children.push(exportNode(childNodes[i]));
+ }
+ return exported;
+ }
+
+ var json = {
+ root: exportNode(this.getRoot())
+ };
+
+ json.template = this.getTemplate();
+ json.theme = this.getTheme();
+ json.version = Minder.version;
+
+ return JSON.parse(JSON.stringify(json));
+ },
+
+ /**
+ * function Text2Children(MinderNode, String)
+ * @param {MinderNode} node 要导入数据的节点
+ * @param {String} text 导入的text数据
+ * @Desc: 用于批量插入子节点,并不会修改被插入的父节点
+ * @Editor: Naixor
+ * @Date: 2015.9.21
+ * @example: 用于批量导入如下类型的节点
+ * 234
+ * 3456346 asadf
+ * 12312414
+ * wereww
+ * 12314
+ * 1231412
+ * 13123
+ */
+ Text2Children: function (node, text) {
+ if (!(node instanceof kityminder.Node)) {
+ return;
+ // throw new Error('Json2Children::node is not a kityminder.Node type!');
+ };
+ var children = [],
+ jsonMap = {},
+ level = 0;
+
+ var LINE_SPLITTER = /\r|\n|\r\n/,
+ TAB_REGEXP = /^(\t|\x20{4})/;
+
+ var lines = text.split(LINE_SPLITTER),
+ line = '', jsonNode, i = 0;
+ var minder = this;
+
+ function isEmpty(line) {
+ return line === "" && !/\S/.test(line);
+ }
+
+ function getNode(line) {
+ return {
+ data: {
+ text: line.replace(/^(\t|\x20{4})+/, '').replace(/(\t|\x20{4})+$/, '')
+ },
+ children: []
+ }
+ }
+
+ function getLevel(text) {
+ var level = 0;
+ while (TAB_REGEXP.test(text)) {
+ text = text.replace(TAB_REGEXP, '');
+ level++;
+ }
+ return level;
+ }
+
+ function addChild(parent, node) {
+ parent.children.push(node);
+ }
+
+ function importChildren(node, children) {
+ for (var i = 0, l = children.length; i < l; i++) {
+ var childNode = minder.createNode(null, node);
+ childNode.setData('text', children[i].data.text || '');
+ importChildren(childNode, children[i].children);
+ }
+ }
+
+ while ((line = lines[i++]) !== undefined) {
+ line = line.replace(/ /g, '');
+ if (isEmpty(line)) continue;
+
+ level = getLevel(line);
+ jsonNode = getNode(line);
+ if (level === 0) {
+ jsonMap = {};
+ children.push(jsonNode);
+ jsonMap[0] = children[children.length-1];
+ } else {
+ if (!jsonMap[level-1]) {
+ throw new Error('Invalid local format');
+ };
+ addChild(jsonMap[level-1], jsonNode);
+ jsonMap[level] = jsonNode;
+ }
+ }
+
+ importChildren(node, children);
+ minder.refresh();
+ },
+
+ /**
+ * @method exportNode(MinderNode)
+ * @param {MinderNode} node 当前要被导出的节点
+ * @return {Object} 返回只含有data和children的Object
+ * @Editor: Naixor
+ * @Date: 2015.9.22
+ */
+ exportNode: function (node) {
+ var exported = {};
+ exported.data = node.getData();
+ var childNodes = node.getChildren();
+ exported.children = [];
+ for (var i = 0; i < childNodes.length; i++) {
+ exported.children.push(this.exportNode(childNodes[i]));
+ }
+ return exported;
+ },
+ /**
+ * @method importNode()
+ * @description 根据纯json {data, children}数据转换成为脑图节点
+ * @Editor: Naixor
+ * @Date: 2015.9.20
+ */
+ importNode: function(node, json) {
+ var data = json.data;
+ node.data = {};
+
+ for (var field in data) {
+ node.setData(field, data[field]);
+ }
+
+ var childrenTreeData = json.children || [];
+ for (var i = 0; i < childrenTreeData.length; i++) {
+ var childNode = this.createNode(null, node);
+ this.importNode(childNode, childrenTreeData[i]);
+ }
+ return node;
+ },
+
+ /**
+ * @method importJson()
+ * @for Minder
+ * @description 导入脑图数据,数据为 JSON 对象,具体的数据字段形式请参考 [Data](data) 章节。
+ *
+ * @grammar importJson(json) => {this}
+ *
+ * @param {plain} json 要导入的数据
+ */
+ importJson: function(json) {
+ if (!json) return;
+
+ /**
+ * @event preimport
+ * @for Minder
+ * @when 导入数据之前
+ */
+ this._fire(new MinderEvent('preimport', null, false));
+
+ // 删除当前所有节点
+ while (this._root.getChildren().length) {
+ this.removeNode(this._root.getChildren()[0]);
+ }
+
+ json = compatibility(json);
+
+ this.importNode(this._root, json.root);
+
+ this.setTemplate(json.template || 'default');
+ this.setTheme(json.theme || null);
+ this.refresh();
+
+ /**
+ * @event import,contentchange,interactchange
+ * @for Minder
+ * @when 导入数据之后
+ */
+ this.fire('import');
+
+ this._firePharse({
+ type: 'contentchange'
+ });
+
+ this._interactChange();
+
+ return this;
+ },
+
+ /**
+ * @method exportData()
+ * @for Minder
+ * @description 使用指定使用的数据协议,导入脑图数据
+ *
+ * @grammar exportData(protocol) => Promise
+ *
+ * @param {string} protocol 指定的数据协议(默认内置五种数据协议 `json`、`text`、`markdown`、`svg` 和 `png`)
+ */
+ exportData: function(protocolName, option) {
+ var json, protocol;
+
+ json = this.exportJson();
+
+ // 指定了协议进行导出,需要检测协议是否支持
+ if (protocolName) {
+ protocol = protocols[protocolName];
+
+ if (!protocol || !protocol.encode) {
+ return Promise.reject(new Error('Not supported protocol:' + protocolName));
+ }
+ }
+
+ // 导出前抛个事件
+ this._fire(new MinderEvent('beforeexport', {
+ json: json,
+ protocolName: protocolName,
+ protocol: protocol
+ }));
+
+ return Promise.resolve(protocol.encode(json, this, option));
+ },
+
+ /**
+ * @method importData()
+ * @for Minder
+ * @description 使用指定的数据协议,导入脑图数据,覆盖当前实例的脑图
+ *
+ * @grammar importData(protocol, callback) => Promise
+ *
+ * @param {string} protocol 指定的用于解析数据的数据协议(默认内置三种数据协议 `json`、`text` 和 `markdown` 的支持)
+ * @param {any} data 要导入的数据
+ */
+ importData: function(protocolName, data, option) {
+ var json, protocol;
+ var minder = this;
+
+ // 指定了协议进行导入,需要检测协议是否支持
+ if (protocolName) {
+ protocol = protocols[protocolName];
+
+ if (!protocol || !protocol.decode) {
+ return Promise.reject(new Error('Not supported protocol:' + protocolName));
+ }
+ }
+
+ var params = {
+ local: data,
+ protocolName: protocolName,
+ protocol: protocol
+ };
+
+ // 导入前抛事件
+ this._fire(new MinderEvent('beforeimport', params));
+
+ return Promise.resolve(protocol.decode(data, this, option)).then(function(json) {
+ minder.importJson(json);
+ return json;
+ });
+ },
+
+ /**
+ * @method decodeData()
+ * @for Minder
+ * @description 使用指定的数据协议,解析为脑图数据,与 importData 的区别在于:不覆盖当前实例的脑图
+ *
+ * @grammar decodeData(protocol, callback) => Promise
+ *
+ * @param {string} protocol 指定的用于解析数据的数据协议(默认内置三种数据协议 `json`、`text` 和 `markdown` 的支持)
+ * @param {any} data 要导入的数据
+ */
+ decodeData: function(protocolName, data, option) {
+ var json, protocol;
+ var minder = this;
+
+ // 指定了协议进行导入,需要检测协议是否支持
+ if (protocolName) {
+ protocol = protocols[protocolName];
+
+ if (!protocol || !protocol.decode) {
+ return Promise.reject(new Error('Not supported protocol:' + protocolName));
+ }
+ }
+
+ var params = {
+ local: data,
+ protocolName: protocolName,
+ protocol: protocol
+ };
+
+ // 导入前抛事件
+ this._fire(new MinderEvent('beforeimport', params));
+
+ return Promise.resolve(protocol.decode(data, this, option))
+ }
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/event.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/event.js
new file mode 100644
index 0000000..049bcd7
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/event.js
@@ -0,0 +1,263 @@
+define(function (require, exports, module) {
+ var kity = require('./kity');
+ var utils = require('./utils');
+ var Minder = require('./minder');
+
+ /**
+ * @class MinderEvent
+ * @description 表示一个脑图中发生的事件
+ */
+ var MinderEvent = kity.createClass('MindEvent', {
+ constructor: function (type, params, canstop) {
+ params = params || {};
+ if (params.getType && params.getType() == 'ShapeEvent') {
+ /**
+ * @property kityEvent
+ * @for MinderEvent
+ * @description 如果事件是从一个 kity 的事件派生的,会有 kityEvent 属性指向原来的 kity 事件
+ * @type {KityEvent}
+ */
+ this.kityEvent = params;
+
+ /**
+ * @property originEvent
+ * @for MinderEvent
+ * @description 如果事件是从原声 Dom 事件派生的(如 click、mousemove 等),会有 originEvent 指向原来的 Dom 事件
+ * @type {DomEvent}
+ */
+ this.originEvent = params.originEvent;
+ } else if (params.target && params.preventDefault) {
+ this.originEvent = params;
+ } else {
+ kity.Utils.extend(this, params);
+ }
+
+ /**
+ * @property type
+ * @for MinderEvent
+ * @description 事件的类型,如 `click`、`contentchange` 等
+ * @type {string}
+ */
+ this.type = type;
+ this._canstop = canstop || false;
+ },
+
+ /**
+ * @method getPosition()
+ * @for MinderEvent
+ * @description 如果事件是从一个 kity 事件派生的,会有 `getPosition()` 获取事件发生的坐标
+ *
+ * @grammar getPosition(refer) => {kity.Point}
+ *
+ * @param {string|kity.Shape} refer
+ * 参照的坐标系,
+ * `"screen"` - 以浏览器屏幕为参照坐标系
+ * `"minder"` - (默认)以脑图画布为参照坐标系
+ * `{kity.Shape}` - 指定以某个 kity 图形为参照坐标系
+ */
+ getPosition: function (refer) {
+ if (!this.kityEvent) return;
+ if (!refer || refer == 'minder') {
+ return this.kityEvent.getPosition(this.minder.getRenderContainer());
+ }
+ return this.kityEvent.getPosition.call(this.kityEvent, refer);
+ },
+
+ /**
+ * @method getTargetNode()
+ * @for MinderEvent
+ * @description 当发生的事件是鼠标事件时,获取事件位置命中的脑图节点
+ *
+ * @grammar getTargetNode() => {MinderNode}
+ */
+ getTargetNode: function () {
+ var findShape = this.kityEvent && this.kityEvent.targetShape;
+ if (!findShape) return null;
+ while (!findShape.minderNode && findShape.container) {
+ findShape = findShape.container;
+ }
+ var node = findShape.minderNode;
+ if (node && findShape.getOpacity() < 1) return null;
+ return node || null;
+ },
+
+ /**
+ * @method stopPropagation()
+ * @for MinderEvent
+ * @description 当发生的事件是鼠标事件时,获取事件位置命中的脑图节点
+ *
+ * @grammar getTargetNode() => {MinderNode}
+ */
+ stopPropagation: function () {
+ this._stoped = true;
+ },
+
+ stopPropagationImmediately: function () {
+ this._immediatelyStoped = true;
+ this._stoped = true;
+ },
+
+ shouldStopPropagation: function () {
+ return this._canstop && this._stoped;
+ },
+
+ shouldStopPropagationImmediately: function () {
+ return this._canstop && this._immediatelyStoped;
+ },
+ preventDefault: function () {
+ this.originEvent.preventDefault();
+ },
+ isRightMB: function () {
+ var isRightMB = false;
+ if (!this.originEvent) {
+ return false;
+ }
+ if ('which' in this.originEvent) isRightMB = this.originEvent.which == 3;
+ else if ('button' in this.originEvent) isRightMB = this.originEvent.button == 2;
+ return isRightMB;
+ },
+ getKeyCode: function () {
+ var evt = this.originEvent;
+ return evt.keyCode || evt.which;
+ },
+ });
+
+ Minder.registerInitHook(function (option) {
+ this._initEvents();
+ });
+
+ kity.extendClass(Minder, {
+ _initEvents: function () {
+ this._eventCallbacks = {};
+ },
+
+ _resetEvents: function () {
+ this._initEvents();
+ this._bindEvents();
+ },
+
+ _bindEvents: function () {
+ /* jscs:disable maximumLineLength */
+ this._paper.on(
+ 'click dblclick mousedown contextmenu mouseup mousemove mouseover mousewheel DOMMouseScroll touchstart touchmove touchend dragenter dragleave drop',
+ this._firePharse.bind(this)
+ );
+ if (window) {
+ window.addEventListener('resize', this._firePharse.bind(this));
+ }
+ },
+
+ /**
+ * @method dispatchKeyEvent
+ * @description 派发键盘(相关)事件到脑图实例上,让实例的模块处理
+ * @grammar dispatchKeyEvent(e) => {this}
+ * @param {Event} e 原生的 Dom 事件对象
+ */
+ dispatchKeyEvent: function (e) {
+ this._firePharse(e);
+ },
+
+ _firePharse: function (e) {
+ var beforeEvent, preEvent, executeEvent;
+
+ if (e.type == 'DOMMouseScroll') {
+ e.type = 'mousewheel';
+ e.wheelDelta = e.originEvent.wheelDelta = e.originEvent.detail * -10;
+ e.wheelDeltaX = e.originEvent.mozMovementX;
+ e.wheelDeltaY = e.originEvent.mozMovementY;
+ }
+
+ beforeEvent = new MinderEvent('before' + e.type, e, true);
+ if (this._fire(beforeEvent)) {
+ return;
+ }
+ preEvent = new MinderEvent('pre' + e.type, e, true);
+ executeEvent = new MinderEvent(e.type, e, true);
+
+ if (this._fire(preEvent) || this._fire(executeEvent)) this._fire(new MinderEvent('after' + e.type, e, false));
+ },
+
+ _interactChange: function (e) {
+ var me = this;
+ if (me._interactScheduled) return;
+ setTimeout(function () {
+ me._fire(new MinderEvent('interactchange'));
+ me._interactScheduled = false;
+ }, 100);
+ me._interactScheduled = true;
+ },
+
+ _listen: function (type, callback) {
+ var callbacks = this._eventCallbacks[type] || (this._eventCallbacks[type] = []);
+ callbacks.push(callback);
+ },
+
+ _fire: function (e) {
+ /**
+ * @property minder
+ * @description 产生事件的 Minder 对象
+ * @for MinderShape
+ * @type {Minder}
+ */
+ e.minder = this;
+
+ var status = this.getStatus();
+ var callbacks = this._eventCallbacks[e.type.toLowerCase()] || [];
+
+ if (status) {
+ callbacks = callbacks.concat(this._eventCallbacks[status + '.' + e.type.toLowerCase()] || []);
+ }
+
+ if (callbacks.length === 0) {
+ return;
+ }
+
+ var lastStatus = this.getStatus();
+ for (var i = 0; i < callbacks.length; i++) {
+ callbacks[i].call(this, e);
+
+ /* this.getStatus() != lastStatus ||*/
+ if (e.shouldStopPropagationImmediately()) {
+ break;
+ }
+ }
+
+ return e.shouldStopPropagation();
+ },
+
+ on: function (name, callback) {
+ var km = this;
+ name.split(/\s+/).forEach(function (n) {
+ km._listen(n.toLowerCase(), callback);
+ });
+ return this;
+ },
+
+ off: function (name, callback) {
+ var types = name.split(/\s+/);
+ var i, j, callbacks, removeIndex;
+ for (i = 0; i < types.length; i++) {
+ callbacks = this._eventCallbacks[types[i].toLowerCase()];
+ if (callbacks) {
+ removeIndex = null;
+ for (j = 0; j < callbacks.length; j++) {
+ if (callbacks[j] == callback) {
+ removeIndex = j;
+ }
+ }
+ if (removeIndex !== null) {
+ callbacks.splice(removeIndex, 1);
+ }
+ }
+ }
+ },
+
+ fire: function (type, params) {
+ var e = new MinderEvent(type, params);
+ this._fire(e);
+ return this;
+ },
+ });
+
+ module.exports = MinderEvent;
+});
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/focus.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/focus.js
new file mode 100644
index 0000000..9f3eae2
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/focus.js
@@ -0,0 +1,41 @@
+define(function (require, exports, module) {
+ var kity = require('./kity');
+ var Minder = require('./minder');
+
+ Minder.registerInitHook(function () {
+ this.on('beforemousedown', function (e) {
+ this.focus();
+ // e.preventDefault();
+ });
+ this.on('paperrender', function () {
+ this.focus();
+ });
+ });
+
+ kity.extendClass(Minder, {
+ focus: function () {
+ if (!this.isFocused()) {
+ var renderTarget = this._renderTarget;
+ renderTarget.classList.add('focus');
+ this.renderNodeBatch(this.getSelectedNodes());
+ }
+ this.fire('focus');
+ return this;
+ },
+
+ blur: function () {
+ if (this.isFocused()) {
+ var renderTarget = this._renderTarget;
+ renderTarget.classList.remove('focus');
+ this.renderNodeBatch(this.getSelectedNodes());
+ }
+ this.fire('blur');
+ return this;
+ },
+
+ isFocused: function () {
+ var renderTarget = this._renderTarget;
+ return renderTarget && renderTarget.classList.contains('focus');
+ },
+ });
+});
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/keymap.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/keymap.js
new file mode 100644
index 0000000..99e5e88
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/keymap.js
@@ -0,0 +1,128 @@
+define(function(require, exports, module) {
+ var keymap = {
+ 'Backspace': 8,
+ 'Tab': 9,
+ 'Enter': 13,
+
+ 'Shift': 16,
+ 'Control': 17,
+ 'Alt': 18,
+ 'CapsLock': 20,
+
+ 'Esc': 27,
+
+ 'Spacebar': 32,
+
+ 'PageUp': 33,
+ 'PageDown': 34,
+ 'End': 35,
+ 'Home': 36,
+
+ 'Insert': 45,
+
+ 'Left': 37,
+ 'Up': 38,
+ 'Right': 39,
+ 'Down': 40,
+
+ 'direction': {
+ 37: 1,
+ 38: 1,
+ 39: 1,
+ 40: 1
+ },
+
+ 'Del': 46,
+
+ 'NumLock': 144,
+
+ 'Cmd': 91,
+ 'CmdFF': 224,
+ 'F1': 112,
+ 'F2': 113,
+ 'F3': 114,
+ 'F4': 115,
+ 'F5': 116,
+ 'F6': 117,
+ 'F7': 118,
+ 'F8': 119,
+ 'F9': 120,
+ 'F10': 121,
+ 'F11': 122,
+ 'F12': 123,
+
+ '`': 192,
+ '=': 187,
+ '-': 189,
+
+ '/': 191,
+ '.': 190,
+ controlKeys: {
+ 16: 1,
+ 17: 1,
+ 18: 1,
+ 20: 1,
+ 91: 1,
+ 224: 1
+ },
+ 'notContentChange': {
+ 13: 1,
+ 9: 1,
+
+ 33: 1,
+ 34: 1,
+ 35: 1,
+ 36: 1,
+
+ 16: 1,
+ 17: 1,
+ 18: 1,
+ 20: 1,
+ 91: 1,
+
+ //上下左右
+ 37: 1,
+ 38: 1,
+ 39: 1,
+ 40: 1,
+
+ 113: 1,
+ 114: 1,
+ 115: 1,
+ 144: 1,
+ 27: 1
+ },
+
+ 'isSelectedNodeKey': {
+ //上下左右
+ 37: 1,
+ 38: 1,
+ 39: 1,
+ 40: 1,
+ 13: 1,
+ 9: 1
+ }
+ };
+
+ // 小写适配
+ for (var key in keymap) {
+ if (keymap.hasOwnProperty(key)) {
+ keymap[key.toLowerCase()] = keymap[key];
+ }
+ }
+ var aKeyCode = 65;
+ var aCharCode = 'a'.charCodeAt(0);
+
+ // letters
+ 'abcdefghijklmnopqrstuvwxyz'.split('').forEach(function(letter) {
+ keymap[letter] = aKeyCode + (letter.charCodeAt(0) - aCharCode);
+ });
+
+ // numbers
+ var n = 9;
+ do {
+ keymap[n.toString()] = n + 48;
+ } while (--n);
+
+ module.exports = keymap;
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/keyreceiver.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/keyreceiver.js
new file mode 100644
index 0000000..1206e49
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/keyreceiver.js
@@ -0,0 +1,66 @@
+define(function(require, exports, module) {
+ var kity = require('./kity');
+ var utils = require('./utils');
+ var Minder = require('./minder');
+
+ function listen(element, type, handler) {
+ type.split(' ').forEach(function(name) {
+ element.addEventListener(name, handler, false);
+ });
+ }
+
+ Minder.registerInitHook(function(option) {
+ this.setDefaultOptions({
+ enableKeyReceiver: true
+ });
+ if (this.getOption('enableKeyReceiver')) {
+ this.on('paperrender', function() {
+ this._initKeyReceiver();
+ });
+ }
+ });
+
+ kity.extendClass(Minder, {
+ _initKeyReceiver: function() {
+
+ if (this._keyReceiver) return;
+
+ var receiver = this._keyReceiver = document.createElement('input');
+ receiver.classList.add('km-receiver');
+
+ var renderTarget = this._renderTarget;
+ renderTarget.appendChild(receiver);
+
+ var minder = this;
+
+ listen(receiver, 'keydown keyup keypress copy paste blur focus input', function(e) {
+ switch (e.type) {
+ case 'blur':
+ minder.blur();
+ break;
+ case 'focus':
+ minder.focus();
+ break;
+ case 'input':
+ receiver.value = null;
+ break;
+ }
+ minder._firePharse(e);
+ e.preventDefault();
+ });
+
+ this.on('focus', function() {
+ receiver.select();
+ receiver.focus();
+ });
+ this.on('blur', function() {
+ receiver.blur();
+ });
+
+ if (this.isFocused()) {
+ receiver.select();
+ receiver.focus();
+ }
+ }
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/kity.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/kity.js
new file mode 100644
index 0000000..c95bf84
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/kity.js
@@ -0,0 +1,11 @@
+/**
+ * @fileOverview
+ *
+ * Kity 引入
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+define(function(require, exports, module) {
+ module.exports = window.kity;
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/layout.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/layout.js
new file mode 100644
index 0000000..666deae
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/layout.js
@@ -0,0 +1,523 @@
+define(function(require, exports, module) {
+ var kity = require('./kity');
+ var utils = require('./utils');
+ var Minder = require('./minder');
+ var MinderNode = require('./node');
+ var MinderEvent = require('./event');
+ var Command = require('./command');
+
+ var _layouts = {};
+ var _defaultLayout;
+
+ function register(name, layout) {
+ _layouts[name] = layout;
+ _defaultLayout = _defaultLayout || name;
+ }
+
+ /**
+ * @class Layout 布局基类,具体布局需要从该类派生
+ */
+ var Layout = kity.createClass('Layout', {
+
+ /**
+ * @abstract
+ *
+ * 子类需要实现的布局算法,该算法输入一个节点,排布该节点的子节点(相对父节点的变换)
+ *
+ * @param {MinderNode} node 需要布局的节点
+ *
+ * @example
+ *
+ * doLayout: function(node) {
+ * var children = node.getChildren();
+ * // layout calculation
+ * children[i].setLayoutTransform(new kity.Matrix().translate(x, y));
+ * }
+ */
+ doLayout: function(parent, children) {
+ throw new Error('Not Implement: Layout.doLayout()');
+ },
+
+ /**
+ * 对齐指定的节点
+ *
+ * @param {Array} nodes 要对齐的节点
+ * @param {string} border 对齐边界,允许取值 left, right, top, bottom
+ *
+ */
+ align: function(nodes, border, offset) {
+ var me = this;
+ offset = offset || 0;
+ nodes.forEach(function(node) {
+ var tbox = me.getTreeBox([node]);
+ var matrix = node.getLayoutTransform();
+ switch (border) {
+ case 'left':
+ return matrix.translate(offset - tbox.left, 0);
+ case 'right':
+ return matrix.translate(offset - tbox.right, 0);
+ case 'top':
+ return matrix.translate(0, offset - tbox.top);
+ case 'bottom':
+ return matrix.translate(0, offset - tbox.bottom);
+ }
+ });
+ },
+
+ stack: function(nodes, axis, distance) {
+ var me = this;
+
+ var position = 0;
+
+ distance = distance || function(node, next, axis) {
+ return node.getStyle({
+ x: 'margin-right',
+ y: 'margin-bottom'
+ }[axis]) + next.getStyle({
+ x: 'margin-left',
+ y: 'margin-top'
+ }[axis]);
+ };
+
+ nodes.forEach(function(node, index, nodes) {
+ var tbox = me.getTreeBox([node]);
+
+ var size = {
+ x: tbox.width,
+ y: tbox.height
+ }[axis];
+ var offset = {
+ x: tbox.left,
+ y: tbox.top
+ }[axis];
+
+ var matrix = node.getLayoutTransform();
+
+ if (axis == 'x') {
+ matrix.translate(position - offset, 0);
+ } else {
+ matrix.translate(0, position - offset);
+ }
+ position += size;
+ if (nodes[index + 1])
+ position += distance(node, nodes[index + 1], axis);
+ });
+ return position;
+ },
+
+ move: function(nodes, dx, dy) {
+ nodes.forEach(function(node) {
+ node.getLayoutTransform().translate(dx, dy);
+ });
+ },
+
+ /**
+ * 工具方法:获取给点的节点所占的布局区域
+ *
+ * @param {MinderNode[]} nodes 需要计算的节点
+ *
+ * @return {Box} 计算结果
+ */
+ getBranchBox: function(nodes) {
+ var box = new kity.Box();
+ var i, node, matrix, contentBox;
+ for (i = 0; i < nodes.length; i++) {
+ node = nodes[i];
+ matrix = node.getLayoutTransform();
+ contentBox = node.getContentBox();
+ box = box.merge(matrix.transformBox(contentBox));
+ }
+
+ return box;
+ },
+
+ /**
+ * 工具方法:计算给定的节点的子树所占的布局区域
+ *
+ * @param {MinderNode} nodes 需要计算的节点
+ *
+ * @return {Box} 计算的结果
+ */
+ getTreeBox: function(nodes) {
+
+ var i, node, matrix, treeBox;
+
+ var box = new kity.Box();
+
+ if (!(nodes instanceof Array)) nodes = [nodes];
+
+ for (i = 0; i < nodes.length; i++) {
+ node = nodes[i];
+ matrix = node.getLayoutTransform();
+
+ treeBox = node.getContentBox();
+
+ if (node.isExpanded() && node.children.length) {
+ treeBox = treeBox.merge(this.getTreeBox(node.children));
+ }
+
+ box = box.merge(matrix.transformBox(treeBox));
+ }
+
+ return box;
+ },
+
+ getOrderHint: function(node) {
+ return [];
+ }
+ });
+
+ Layout.register = register;
+
+ Minder.registerInitHook(function(options) {
+ this.refresh();
+ });
+
+ /**
+ * 布局支持池子管理
+ */
+ utils.extend(Minder, {
+
+ getLayoutList: function() {
+ return _layouts;
+ },
+
+ getLayoutInstance: function(name) {
+ var LayoutClass = _layouts[name];
+ if (!LayoutClass) throw new Error('Missing Layout: ' + name);
+ var layout = new LayoutClass();
+ return layout;
+ }
+ });
+
+ /**
+ * MinderNode 上的布局支持
+ */
+ kity.extendClass(MinderNode, {
+
+ /**
+ * 获得当前节点的布局名称
+ *
+ * @return {String}
+ */
+ getLayout: function() {
+ var layout = this.getData('layout');
+
+ layout = layout || (this.isRoot() ? _defaultLayout : this.parent.getLayout());
+
+ return layout;
+ },
+
+ setLayout: function(name) {
+ if (name) {
+ if (name == 'inherit') {
+ this.setData('layout');
+ } else {
+ this.setData('layout', name);
+ }
+ }
+ return this;
+ },
+
+ layout: function(name) {
+
+ this.setLayout(name).getMinder().layout();
+
+ return this;
+ },
+
+ getLayoutInstance: function() {
+ return Minder.getLayoutInstance(this.getLayout());
+ },
+
+ getOrderHint: function(refer) {
+ return this.parent.getLayoutInstance().getOrderHint(this);
+ },
+
+ /**
+ * 获取当前节点相对于父节点的布局变换
+ */
+ getLayoutTransform: function() {
+ return this._layoutTransform || new kity.Matrix();
+ },
+
+ /**
+ * 第一轮布局计算后,获得的全局布局位置
+ *
+ * @return {[type]} [description]
+ */
+ getGlobalLayoutTransformPreview: function() {
+ var pMatrix = this.parent ? this.parent.getLayoutTransform() : new kity.Matrix();
+ var matrix = this.getLayoutTransform();
+ var offset = this.getLayoutOffset();
+ if (offset) {
+ matrix = matrix.clone().translate(offset.x, offset.y);
+ }
+ return pMatrix.merge(matrix);
+ },
+
+ getLayoutPointPreview: function() {
+ return this.getGlobalLayoutTransformPreview().transformPoint(new kity.Point());
+ },
+
+ /**
+ * 获取节点相对于全局的布局变换
+ */
+ getGlobalLayoutTransform: function() {
+ if (this._globalLayoutTransform) {
+ return this._globalLayoutTransform;
+ } else if (this.parent) {
+ return this.parent.getGlobalLayoutTransform();
+ } else {
+ return new kity.Matrix();
+ }
+ },
+
+ /**
+ * 设置当前节点相对于父节点的布局变换
+ */
+ setLayoutTransform: function(matrix) {
+ this._layoutTransform = matrix;
+ return this;
+ },
+
+ /**
+ * 设置当前节点相对于全局的布局变换(冗余优化)
+ */
+ setGlobalLayoutTransform: function(matrix) {
+ this.getRenderContainer().setMatrix(this._globalLayoutTransform = matrix);
+ return this;
+ },
+
+ setVertexIn: function(p) {
+ this._vertexIn = p;
+ },
+
+ setVertexOut: function(p) {
+ this._vertexOut = p;
+ },
+
+ getVertexIn: function() {
+ return this._vertexIn || new kity.Point();
+ },
+
+ getVertexOut: function() {
+ return this._vertexOut || new kity.Point();
+ },
+
+ getLayoutVertexIn: function() {
+ return this.getGlobalLayoutTransform().transformPoint(this.getVertexIn());
+ },
+
+ getLayoutVertexOut: function() {
+ return this.getGlobalLayoutTransform().transformPoint(this.getVertexOut());
+ },
+
+ setLayoutVectorIn: function(v) {
+ this._layoutVectorIn = v;
+ return this;
+ },
+
+ setLayoutVectorOut: function(v) {
+ this._layoutVectorOut = v;
+ return this;
+ },
+
+ getLayoutVectorIn: function() {
+ return this._layoutVectorIn || new kity.Vector();
+ },
+
+ getLayoutVectorOut: function() {
+ return this._layoutVectorOut || new kity.Vector();
+ },
+
+ getLayoutBox: function() {
+ var matrix = this.getGlobalLayoutTransform();
+ return matrix.transformBox(this.getContentBox());
+ },
+
+ getLayoutPoint: function() {
+ var matrix = this.getGlobalLayoutTransform();
+ return matrix.transformPoint(new kity.Point());
+ },
+
+ getLayoutOffset: function() {
+ if (!this.parent) return new kity.Point();
+
+ // 影响当前节点位置的是父节点的布局
+ var data = this.getData('layout_' + this.parent.getLayout() + '_offset');
+
+ if (data) return new kity.Point(data.x, data.y);
+
+ return new kity.Point();
+ },
+
+ setLayoutOffset: function(p) {
+ if (!this.parent) return this;
+
+ this.setData('layout_' + this.parent.getLayout() + '_offset', p ? {
+ x: p.x,
+ y: p.y
+ } : undefined);
+
+ return this;
+ },
+
+ hasLayoutOffset: function() {
+ return !!this.getData('layout_' + this.parent.getLayout() + '_offset');
+ },
+
+ resetLayoutOffset: function() {
+ return this.setLayoutOffset(null);
+ },
+
+ getLayoutRoot: function() {
+ if (this.isLayoutRoot()) {
+ return this;
+ }
+ return this.parent.getLayoutRoot();
+ },
+
+ isLayoutRoot: function() {
+ return this.getData('layout') || this.isRoot();
+ }
+ });
+
+ /**
+ * Minder 上的布局支持
+ */
+ kity.extendClass(Minder, {
+
+ layout: function() {
+
+ var duration = this.getOption('layoutAnimationDuration');
+
+ this.getRoot().traverse(function(node) {
+ // clear last results
+ node.setLayoutTransform(null);
+ });
+
+ function layoutNode(node, round) {
+
+ // layout all children first
+ // 剪枝:收起的节点无需计算
+ if (node.isExpanded() || true) {
+ node.children.forEach(function(child) {
+ layoutNode(child, round);
+ });
+ }
+
+ var layout = node.getLayoutInstance();
+ // var childrenInFlow = node.getChildren().filter(function(child) {
+ // return !child.hasLayoutOffset();
+ // });
+ layout.doLayout(node, node.getChildren(), round);
+ }
+
+ // 第一轮布局
+ layoutNode(this.getRoot(), 1);
+
+ // 第二轮布局
+ layoutNode(this.getRoot(), 2);
+
+ var minder = this;
+ this.applyLayoutResult(this.getRoot(), duration, function() {
+ /**
+ * 当节点>200, 不使用动画时, 此处逻辑变为同步逻辑, 外部minder.on事件无法
+ * 被提前录入, 因此增加setTimeout
+ * @author Naixor
+ */
+ setTimeout(function () {
+ minder.fire('layoutallfinish');
+ }, 0);
+ });
+
+ return this.fire('layout');
+ },
+
+ refresh: function() {
+ this.getRoot().renderTree();
+ this.layout().fire('contentchange')._interactChange();
+ return this;
+ },
+
+ applyLayoutResult: function(root, duration, callback) {
+ root = root || this.getRoot();
+ var me = this;
+
+ var complex = root.getComplex();
+
+ function consume() {
+ if (!--complex) {
+ if (callback) {
+ callback();
+ }
+ }
+ }
+
+ // 节点复杂度大于 100,关闭动画
+ if (complex > 200) duration = 0;
+
+ function applyMatrix(node, matrix) {
+ node.setGlobalLayoutTransform(matrix);
+
+ me.fire('layoutapply', {
+ node: node,
+ matrix: matrix
+ });
+ }
+
+ function apply(node, pMatrix) {
+ var matrix = node.getLayoutTransform().merge(pMatrix.clone());
+ var lastMatrix = node.getGlobalLayoutTransform() || new kity.Matrix();
+
+ var offset = node.getLayoutOffset();
+ matrix.translate(offset.x, offset.y);
+
+ matrix.m.e = Math.round(matrix.m.e);
+ matrix.m.f = Math.round(matrix.m.f);
+
+ // 如果当前有动画,停止动画
+ if (node._layoutTimeline) {
+ node._layoutTimeline.stop();
+ node._layoutTimeline = null;
+ }
+
+ // 如果要求以动画形式来更新,创建动画
+ if (duration) {
+ node._layoutTimeline = new kity.Animator(lastMatrix, matrix, applyMatrix)
+ .start(node, duration, 'ease')
+ .on('finish', function() {
+ //可能性能低的时候会丢帧,手动添加一帧
+ setTimeout(function() {
+ applyMatrix(node, matrix);
+ me.fire('layoutfinish', {
+ node: node,
+ matrix: matrix
+ });
+ consume();
+ }, 150);
+ });
+ }
+
+ // 否则直接更新
+ else {
+ applyMatrix(node, matrix);
+ me.fire('layoutfinish', {
+ node: node,
+ matrix: matrix
+ });
+ consume();
+ }
+
+ for (var i = 0; i < node.children.length; i++) {
+ apply(node.children[i], matrix);
+ }
+ }
+ apply(root, root.parent ? root.parent.getGlobalLayoutTransform() : new kity.Matrix());
+ return this;
+ }
+ });
+
+ module.exports = Layout;
+});
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/minder.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/minder.js
new file mode 100644
index 0000000..469fb6f
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/minder.js
@@ -0,0 +1,40 @@
+/**
+ * @fileOverview
+ *
+ * KityMinder 类,暴露在 window 上的唯一变量
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+define(function(require, exports, module) {
+ var kity = require('./kity');
+ var utils = require('./utils');
+
+ var _initHooks = [];
+
+ var Minder = kity.createClass('Minder', {
+ constructor: function(options) {
+ this._options = utils.extend({}, options);
+
+ var initHooks = _initHooks.slice();
+
+ var initHook;
+ while (initHooks.length) {
+ initHook = initHooks.shift();
+ if (typeof(initHook) == 'function') {
+ initHook.call(this, this._options);
+ }
+ }
+
+ this.fire('finishInitHook');
+ }
+ });
+
+ Minder.version = '1.4.43';
+
+ Minder.registerInitHook = function(hook) {
+ _initHooks.push(hook);
+ };
+
+ module.exports = Minder;
+});
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/module.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/module.js
new file mode 100644
index 0000000..362d02e
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/module.js
@@ -0,0 +1,151 @@
+define(function(require, exports, module) {
+ var kity = require('./kity');
+ var utils = require('./utils');
+ var Minder = require('./minder');
+
+ /* 已注册的模块 */
+ var _modules = {};
+
+ exports.register = function(name, module) {
+ _modules[name] = module;
+ };
+
+ /* 模块初始化 */
+ Minder.registerInitHook(function() {
+ this._initModules();
+ });
+
+ // 模块声明周期维护
+ kity.extendClass(Minder, {
+ _initModules: function() {
+ var modulesPool = _modules;
+ var modulesToLoad = this._options.modules || utils.keys(modulesPool);
+
+ this._commands = {};
+ this._query = {};
+ this._modules = {};
+ this._rendererClasses = {};
+
+ var i, name, type, module, moduleDeals,
+ dealCommands, dealEvents, dealRenderers;
+
+ var me = this;
+ for (i = 0; i < modulesToLoad.length; i++) {
+ name = modulesToLoad[i];
+
+ if (!modulesPool[name]) continue;
+
+ // 执行模块初始化,抛出后续处理对象
+
+ if (typeof(modulesPool[name]) == 'function') {
+ moduleDeals = modulesPool[name].call(me);
+ } else {
+ moduleDeals = modulesPool[name];
+ }
+ this._modules[name] = moduleDeals;
+
+ if (!moduleDeals) continue;
+
+ if (moduleDeals.defaultOptions) {
+ me.setDefaultOptions(moduleDeals.defaultOptions);
+ }
+
+ if (moduleDeals.init) {
+ moduleDeals.init.call(me, this._options);
+ }
+
+ /**
+ * @Desc: 判断是否支持原生clipboard事件,如果支持,则对pager添加其监听
+ * @Editor: Naixor
+ * @Date: 2015.9.20
+ */
+ /**
+ * 由于当前脑图解构问题,clipboard暂时全权交由玩不托管
+ * @Editor: Naixor
+ * @Date: 2015.9.24
+ */
+ // if (name === 'ClipboardModule' && this.supportClipboardEvent && !kity.Browser.gecko) {
+ // var on = function () {
+ // var clipBoardReceiver = this.clipBoardReceiver || document;
+
+ // if (document.addEventListener) {
+ // clipBoardReceiver.addEventListener.apply(this, arguments);
+ // } else {
+ // arguments[0] = 'on' + arguments[0];
+ // clipBoardReceiver.attachEvent.apply(this, arguments);
+ // }
+ // }
+ // for (var command in moduleDeals.clipBoardEvents) {
+ // on(command, moduleDeals.clipBoardEvents[command]);
+ // }
+ // };
+
+ // command加入命令池子
+ dealCommands = moduleDeals.commands;
+ for (name in dealCommands) {
+ this._commands[name.toLowerCase()] = new dealCommands[name]();
+ }
+
+ // 绑定事件
+ dealEvents = moduleDeals.events;
+ if (dealEvents) {
+ for (type in dealEvents) {
+ me.on(type, dealEvents[type]);
+ }
+ }
+
+ // 渲染器
+ dealRenderers = moduleDeals.renderers;
+
+ if (dealRenderers) {
+
+ for (type in dealRenderers) {
+ this._rendererClasses[type] = this._rendererClasses[type] || [];
+
+ if (utils.isArray(dealRenderers[type])) {
+ this._rendererClasses[type] = this._rendererClasses[type].concat(dealRenderers[type]);
+ } else {
+ this._rendererClasses[type].push(dealRenderers[type]);
+ }
+ }
+ }
+
+ //添加模块的快捷键
+ if (moduleDeals.commandShortcutKeys) {
+ this.addCommandShortcutKeys(moduleDeals.commandShortcutKeys);
+ }
+ }
+ },
+
+ _garbage: function() {
+ this.clearSelect();
+
+ while (this._root.getChildren().length) {
+ this._root.removeChild(0);
+ }
+ },
+
+ destroy: function() {
+ var modules = this._modules;
+
+ this._resetEvents();
+ this._garbage();
+
+ for (var key in modules) {
+ if (!modules[key].destroy) continue;
+ modules[key].destroy.call(this);
+ }
+ },
+
+ reset: function() {
+ var modules = this._modules;
+
+ this._garbage();
+
+ for (var key in modules) {
+ if (!modules[key].reset) continue;
+ modules[key].reset.call(this);
+ }
+ }
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/node.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/node.js
new file mode 100644
index 0000000..b4ad8a3
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/node.js
@@ -0,0 +1,407 @@
+define(function(require, exports, module) {
+ var kity = require('./kity');
+ var utils = require('./utils');
+ var Minder = require('./minder');
+
+ /**
+ * @class MinderNode
+ *
+ * 表示一个脑图节点
+ */
+ var MinderNode = kity.createClass('MinderNode', {
+
+ /**
+ * 创建一个游离的脑图节点
+ *
+ * @param {String|Object} textOrData
+ * 节点的初始数据或文本
+ */
+ constructor: function(textOrData) {
+
+ // 指针
+ this.parent = null;
+ this.root = this;
+ this.children = [];
+
+ // 数据
+ this.data = {
+ id: utils.guid(),
+ created: +new Date()
+ };
+
+ // 绘图容器
+ this.initContainers();
+
+ if (utils.isString(textOrData)) {
+ this.setText(textOrData);
+ } else if (utils.isObject(textOrData)) {
+ utils.extend(this.data, textOrData);
+ }
+ },
+
+ initContainers: function() {
+ this.rc = new kity.Group().setId(utils.uuid('minder_node'));
+ this.rc.minderNode = this;
+ },
+
+ /**
+ * 判断节点是否根节点
+ */
+ isRoot: function() {
+ return this.root === this;
+ },
+
+ /**
+ * 判断节点是否叶子
+ */
+ isLeaf: function() {
+ return this.children.length === 0;
+ },
+
+ /**
+ * 获取节点的根节点
+ */
+ getRoot: function() {
+ return this.root || this;
+ },
+
+ /**
+ * 获得节点的父节点
+ */
+ getParent: function() {
+ return this.parent;
+ },
+
+ getSiblings: function() {
+ var children = this.parent.children;
+ var siblings = [];
+ var self = this;
+ children.forEach(function(child) {
+ if (child != self) siblings.push(child);
+ });
+ return siblings;
+ },
+
+ /**
+ * 获得节点的深度
+ */
+ getLevel: function() {
+ var level = 0,
+ ancestor = this.parent;
+ while (ancestor) {
+ level++;
+ ancestor = ancestor.parent;
+ }
+ return level;
+ },
+
+ /**
+ * 获得节点的复杂度(即子树中节点的数量)
+ */
+ getComplex: function() {
+ var complex = 0;
+ this.traverse(function() {
+ complex++;
+ });
+ return complex;
+ },
+
+ /**
+ * 获得节点的类型(root|main|sub)
+ */
+ getType: function(type) {
+ this.type = ['root', 'main', 'sub'][Math.min(this.getLevel(), 2)];
+ return this.type;
+ },
+
+ /**
+ * 判断当前节点是否被测试节点的祖先
+ * @param {MinderNode} test 被测试的节点
+ */
+ isAncestorOf: function(test) {
+ var ancestor = test.parent;
+ while (ancestor) {
+ if (ancestor == this) return true;
+ ancestor = ancestor.parent;
+ }
+ return false;
+ },
+
+ getData: function(key) {
+ return key ? this.data[key] : this.data;
+ },
+
+ setData: function(key, value) {
+ if (typeof key == 'object') {
+ var data = key;
+ for (key in data) if (data.hasOwnProperty(key)) {
+ this.data[key] = data[key];
+ }
+ }
+ else {
+ this.data[key] = value;
+ }
+ return this;
+ },
+
+ /**
+ * 设置节点的文本数据
+ * @param {String} text 文本数据
+ */
+ setText: function(text) {
+ return this.data.text = text;
+ },
+
+ /**
+ * 获取节点的文本数据
+ * @return {String}
+ */
+ getText: function() {
+ return this.data.text || null;
+ },
+
+ /**
+ * 先序遍历当前节点树
+ * @param {Function} fn 遍历函数
+ */
+ preTraverse: function(fn, excludeThis) {
+ var children = this.getChildren();
+ if (!excludeThis) fn(this);
+ for (var i = 0; i < children.length; i++) {
+ children[i].preTraverse(fn);
+ }
+ },
+
+ /**
+ * 后序遍历当前节点树
+ * @param {Function} fn 遍历函数
+ */
+ postTraverse: function(fn, excludeThis) {
+ var children = this.getChildren();
+ for (var i = 0; i < children.length; i++) {
+ children[i].postTraverse(fn);
+ }
+ if (!excludeThis) fn(this);
+ },
+
+ traverse: function(fn, excludeThis) {
+ return this.postTraverse(fn, excludeThis);
+ },
+
+ getChildren: function() {
+ return this.children;
+ },
+
+ getIndex: function() {
+ return this.parent ? this.parent.children.indexOf(this) : -1;
+ },
+
+ insertChild: function(node, index) {
+ if (index === undefined) {
+ index = this.children.length;
+ }
+ if (node.parent) {
+ node.parent.removeChild(node);
+ }
+ node.parent = this;
+ node.root = this.root;
+
+ this.children.splice(index, 0, node);
+ },
+
+ appendChild: function(node) {
+ return this.insertChild(node);
+ },
+
+ prependChild: function(node) {
+ return this.insertChild(node, 0);
+ },
+
+ removeChild: function(elem) {
+ var index = elem,
+ removed;
+ if (elem instanceof MinderNode) {
+ index = this.children.indexOf(elem);
+ }
+ if (index >= 0) {
+ removed = this.children.splice(index, 1)[0];
+ removed.parent = null;
+ removed.root = removed;
+ }
+ },
+
+ clearChildren: function() {
+ this.children = [];
+ },
+
+ getChild: function(index) {
+ return this.children[index];
+ },
+
+ getRenderContainer: function() {
+ return this.rc;
+ },
+
+ getCommonAncestor: function(node) {
+ return MinderNode.getCommonAncestor(this, node);
+ },
+
+ contains: function(node) {
+ return this == node || this.isAncestorOf(node);
+ },
+
+ clone: function() {
+ var cloned = new MinderNode();
+
+ cloned.data = utils.clone(this.data);
+
+ this.children.forEach(function(child) {
+ cloned.appendChild(child.clone());
+ });
+
+ return cloned;
+ },
+
+ compareTo: function(node) {
+
+ if (!utils.comparePlainObject(this.data, node.data)) return false;
+ if (!utils.comparePlainObject(this.temp, node.temp)) return false;
+ if (this.children.length != node.children.length) return false;
+
+ var i = 0;
+ while (this.children[i]) {
+ if (!this.children[i].compareTo(node.children[i])) return false;
+ i++;
+ }
+
+ return true;
+ },
+
+ getMinder: function() {
+ return this.getRoot().minder;
+ }
+ });
+
+ MinderNode.getCommonAncestor = function(nodeA, nodeB) {
+ if (nodeA instanceof Array) {
+ return MinderNode.getCommonAncestor.apply(this, nodeA);
+ }
+ switch (arguments.length) {
+ case 1:
+ return nodeA.parent || nodeA;
+
+ case 2:
+ if (nodeA.isAncestorOf(nodeB)) {
+ return nodeA;
+ }
+ if (nodeB.isAncestorOf(nodeA)) {
+ return nodeB;
+ }
+ var ancestor = nodeA.parent;
+ while (ancestor && !ancestor.isAncestorOf(nodeB)) {
+ ancestor = ancestor.parent;
+ }
+ return ancestor;
+
+ default:
+ return Array.prototype.reduce.call(arguments,
+ function(prev, current) {
+ return MinderNode.getCommonAncestor(prev, current);
+ },
+ nodeA
+ );
+ }
+ };
+
+ kity.extendClass(Minder, {
+
+ getRoot: function() {
+ return this._root;
+ },
+
+ setRoot: function(root) {
+ this._root = root;
+ root.minder = this;
+ },
+
+ getAllNode: function() {
+ var nodes = [];
+ this.getRoot().traverse(function(node) {
+ nodes.push(node);
+ });
+ return nodes;
+ },
+
+ getNodeById: function(id) {
+ return this.getNodesById([id])[0];
+ },
+
+ getNodesById: function(ids) {
+ var nodes = this.getAllNode();
+ var result = [];
+ nodes.forEach(function(node) {
+ if (ids.indexOf(node.getData('id')) != -1) {
+ result.push(node);
+ }
+ });
+ return result;
+ },
+
+ createNode: function(textOrData, parent, index) {
+ var node = new MinderNode(textOrData);
+ this.fire('nodecreate', {
+ node: node,
+ parent: parent,
+ index: index
+ });
+ this.appendNode(node, parent, index);
+ return node;
+ },
+
+ appendNode: function(node, parent, index) {
+ if (parent) parent.insertChild(node, index);
+ this.attachNode(node);
+ return this;
+ },
+
+ removeNode: function(node) {
+ if (node.parent) {
+ node.parent.removeChild(node);
+ this.detachNode(node);
+ this.fire('noderemove', {
+ node: node
+ });
+ }
+ },
+
+ attachNode: function(node) {
+ var rc = this.getRenderContainer();
+ node.traverse(function(current) {
+ current.attached = true;
+ rc.addShape(current.getRenderContainer());
+ });
+ rc.addShape(node.getRenderContainer());
+ this.fire('nodeattach', {
+ node: node
+ });
+ },
+
+ detachNode: function(node) {
+ var rc = this.getRenderContainer();
+ node.traverse(function(current) {
+ current.attached = false;
+ rc.removeShape(current.getRenderContainer());
+ });
+ this.fire('nodedetach', {
+ node: node
+ });
+ },
+
+ getMinderTitle: function() {
+ return this.getRoot().getText();
+ }
+
+ });
+
+ module.exports = MinderNode;
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/option.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/option.js
new file mode 100644
index 0000000..c7c055f
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/option.js
@@ -0,0 +1,34 @@
+/**
+ * @fileOverview
+ *
+ * 提供脑图选项支持
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+define(function(require, exports, module) {
+ var kity = require('./kity');
+ var utils = require('./utils');
+ var Minder = require('./minder');
+
+ Minder.registerInitHook(function(options) {
+ this._defaultOptions = {};
+ });
+
+ kity.extendClass(Minder, {
+ setDefaultOptions: function(options) {
+ utils.extend(this._defaultOptions, options);
+ return this;
+ },
+ getOption: function(key) {
+ if (key) {
+ return key in this._options ? this._options[key] : this._defaultOptions[key];
+ } else {
+ return utils.extend({}, this._defaultOptions, this._options);
+ }
+ },
+ setOption: function(key, value) {
+ this._options[key] = value;
+ }
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/paper.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/paper.js
new file mode 100644
index 0000000..9cae4ff
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/paper.js
@@ -0,0 +1,76 @@
+/**
+ * @fileOverview
+ *
+ * 初始化渲染容器
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+define(function(require, exports, module) {
+ var kity = require('./kity');
+ var utils = require('./utils');
+ var Minder = require('./minder');
+
+ Minder.registerInitHook(function() {
+ this._initPaper();
+ });
+
+ kity.extendClass(Minder, {
+
+ _initPaper: function() {
+
+ this._paper = new kity.Paper();
+ this._paper._minder = this;
+ this._paper.getNode().ondragstart = function(e) {
+ e.preventDefault();
+ };
+ this._paper.shapeNode.setAttribute('transform', 'translate(0.5, 0.5)');
+
+ this._addRenderContainer();
+
+ this.setRoot(this.createNode());
+
+ if (this._options.renderTo) {
+ this.renderTo(this._options.renderTo);
+ }
+ },
+
+ _addRenderContainer: function() {
+ this._rc = new kity.Group().setId(utils.uuid('minder'));
+ this._paper.addShape(this._rc);
+ },
+
+ renderTo: function(target) {
+ if (typeof(target) == 'string') {
+ target = document.querySelector(target);
+ }
+ if (target) {
+ if (target.tagName.toLowerCase() == 'script') {
+ var newTarget = document.createElement('div');
+ newTarget.id = target.id;
+ newTarget.class = target.class;
+ target.parentNode.insertBefore(newTarget, target);
+ target.parentNode.removeChild(target);
+ target = newTarget;
+ }
+ target.classList.add('km-view');
+ this._paper.renderTo(this._renderTarget = target);
+ this._bindEvents();
+ this.fire('paperrender');
+ }
+ return this;
+ },
+
+ getRenderContainer: function() {
+ return this._rc;
+ },
+
+ getPaper: function() {
+ return this._paper;
+ },
+
+ getRenderTarget: function() {
+ return this._renderTarget;
+ },
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/patch.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/patch.js
new file mode 100644
index 0000000..0ef99c1
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/patch.js
@@ -0,0 +1,110 @@
+/**
+ * @fileOverview
+ *
+ * 打补丁
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+define(function(require, exports, module) {
+ var kity = require('./kity');
+ var Minder = require('./minder');
+
+ function insertNode(minder, info, parent, index) {
+ parent = minder.createNode(info.data, parent, index);
+ info.children.forEach(function(childInfo, index) {
+ insertNode(minder, childInfo, parent, index);
+ });
+ return parent;
+ }
+
+ function applyPatch(minder, patch) {
+ // patch.op - 操作,包括 remove, add, replace
+ // patch.path - 路径,如 '/root/children/1/data'
+ // patch.value - 数据,如 { text: "思路" }
+ var path = patch.path.split('/');
+ path.shift();
+
+ var changed = path.shift();
+ var node;
+
+ if (changed == 'root') {
+
+ var dataIndex = path.indexOf('data');
+ if (dataIndex > -1) {
+ changed = 'data';
+ var dataPath = path.splice(dataIndex + 1);
+ patch.dataPath = dataPath;
+ } else {
+ changed = 'node';
+ }
+
+ node = minder.getRoot();
+ var segment, index;
+ while (segment = path.shift()) {
+ if (segment == 'children') continue;
+ if (typeof index != 'undefined') node = node.getChild(index);
+ index = +segment;
+ }
+ patch.index = index;
+ patch.node = node;
+ }
+
+ var express = patch.express = [changed, patch.op].join('.');
+
+ switch (express) {
+ case 'theme.replace':
+ minder.useTheme(patch.value);
+ break;
+ case 'template.replace':
+ minder.useTemplate(patch.value);
+ break;
+ case 'node.add':
+ insertNode(minder, patch.value, patch.node, patch.index).renderTree();
+ minder.layout();
+ break;
+ case 'node.remove':
+ minder.removeNode(patch.node.getChild(patch.index));
+ minder.layout();
+ break;
+ case 'data.add':
+ case 'data.replace':
+ case 'data.remove':
+ var data = patch.node.data;
+ var field;
+ path = patch.dataPath.slice();
+ while (data && path.length > 1) {
+ field = path.shift();
+ if (field in data) {
+ data = data[field];
+ } else if (patch.op != 'remove') {
+ data = data[field] = {};
+ }
+ }
+ if (data) {
+ field = path.shift();
+ data[field] = patch.value;
+ }
+ if (field == 'expandState') {
+ node.renderTree();
+ } else {
+ node.render();
+ }
+ minder.layout();
+ }
+
+ minder.fire('patch', { 'patch' : patch } );
+ }
+
+ kity.extendClass(Minder, {
+ applyPatches: function(patches) {
+ for (var i = 0; i < patches.length; i++) {
+ applyPatch(this, patches[i]);
+ }
+
+ this.fire('contentchange');
+ return this;
+ }
+ });
+
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/promise.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/promise.js
new file mode 100644
index 0000000..5e39ad8
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/promise.js
@@ -0,0 +1,214 @@
+define(function(require, exports, module) {
+ /*!
+ ** Thenable -- Embeddable Minimum Strictly-Compliant Promises/A+ 1.1.1 Thenable
+ ** Copyright (c) 2013-2014 Ralf S. Engelschall
+ ** Licensed under The MIT License
+ ** Source-Code distributed on
+ */
+
+ /* promise states [Promises/A+ 2.1] */
+ var STATE_PENDING = 0; /* [Promises/A+ 2.1.1] */
+ var STATE_FULFILLED = 1; /* [Promises/A+ 2.1.2] */
+ var STATE_REJECTED = 2; /* [Promises/A+ 2.1.3] */
+
+ /* promise object constructor */
+ var Promise = function(executor) {
+ /* optionally support non-constructor/plain-function call */
+ if (!(this instanceof Promise))
+ return new Promise(executor);
+
+ /* initialize object */
+ this.id = 'Thenable/1.0.7';
+ this.state = STATE_PENDING; /* initial state */
+ this.fulfillValue = undefined; /* initial value */ /* [Promises/A+ 1.3, 2.1.2.2] */
+ this.rejectReason = undefined; /* initial reason */ /* [Promises/A+ 1.5, 2.1.3.2] */
+ this.onFulfilled = []; /* initial handlers */
+ this.onRejected = []; /* initial handlers */
+
+ /* support optional executor function */
+ if (typeof executor === 'function')
+ executor.call(this, this.fulfill.bind(this), this.reject.bind(this));
+ };
+
+ /* Promise API methods */
+ Promise.prototype = {
+ /* promise resolving methods */
+ fulfill: function(value) { return deliver(this, STATE_FULFILLED, 'fulfillValue', value); },
+ reject: function(value) { return deliver(this, STATE_REJECTED, 'rejectReason', value); },
+
+ /* 'The then Method' [Promises/A+ 1.1, 1.2, 2.2] */
+ then: function(onFulfilled, onRejected) {
+ var curr = this;
+ var next = new Promise(); /* [Promises/A+ 2.2.7] */
+ curr.onFulfilled.push(
+ resolver(onFulfilled, next, 'fulfill')); /* [Promises/A+ 2.2.2/2.2.6] */
+ curr.onRejected.push(
+ resolver(onRejected, next, 'reject')); /* [Promises/A+ 2.2.3/2.2.6] */
+ execute(curr);
+ return next; /* [Promises/A+ 2.2.7, 3.3] */
+ }
+ };
+
+ Promise.all = function (arr) {
+ return new Promise(function(resolve, reject) {
+ var len = arr.length,
+ i = 0,
+ res = 0,
+ results = [];
+
+ if (len === 0) {
+ resolve(results);
+ }
+
+ while (i < len) {
+ arr[i].then(
+ function (result) {
+ results.push(result);
+ if (++res === len) {
+ resolve(results);
+ }
+ },
+ function (val) {
+ reject(val);
+ }
+ );
+ i++;
+ }
+ });
+ };
+
+ /* deliver an action */
+ var deliver = function(curr, state, name, value) {
+ if (curr.state === STATE_PENDING) {
+ curr.state = state; /* [Promises/A+ 2.1.2.1, 2.1.3.1] */
+ curr[name] = value; /* [Promises/A+ 2.1.2.2, 2.1.3.2] */
+ execute(curr);
+ }
+ return curr;
+ };
+
+ /* execute all handlers */
+ var execute = function(curr) {
+ if (curr.state === STATE_FULFILLED)
+ execute_handlers(curr, 'onFulfilled', curr.fulfillValue);
+ else if (curr.state === STATE_REJECTED)
+ execute_handlers(curr, 'onRejected', curr.rejectReason);
+ };
+
+ /* execute particular set of handlers */
+ var execute_handlers = function(curr, name, value) {
+ /* global process: true */
+ /* global setImmediate: true */
+ /* global setTimeout: true */
+
+ /* short-circuit processing */
+ if (curr[name].length === 0)
+ return;
+
+ /* iterate over all handlers, exactly once */
+ var handlers = curr[name];
+ curr[name] = []; /* [Promises/A+ 2.2.2.3, 2.2.3.3] */
+ var func = function() {
+ for (var i = 0; i < handlers.length; i++)
+ handlers[i](value); /* [Promises/A+ 2.2.5] */
+ };
+
+ /* execute procedure asynchronously */ /* [Promises/A+ 2.2.4, 3.1] */
+ if (typeof process === 'object' && typeof process.nextTick === 'function')
+ process.nextTick(func);
+ else if (typeof setImmediate === 'function')
+ setImmediate(func);
+ else
+ setTimeout(func, 0);
+ };
+
+ /* generate a resolver function */
+ var resolver = function(cb, next, method) {
+ return function(value) {
+ if (typeof cb !== 'function') /* [Promises/A+ 2.2.1, 2.2.7.3, 2.2.7.4] */
+ next[method].call(next, value); /* [Promises/A+ 2.2.7.3, 2.2.7.4] */
+ else {
+ var result;
+ try {
+ if (value instanceof Promise) {
+ result = value.then(cb);
+ }
+ else result = cb(value);
+ } /* [Promises/A+ 2.2.2.1, 2.2.3.1, 2.2.5, 3.2] */
+ catch (e) {
+ next.reject(e); /* [Promises/A+ 2.2.7.2] */
+ return;
+ }
+ resolve(next, result); /* [Promises/A+ 2.2.7.1] */
+ }
+ };
+ };
+
+ /* 'Promise Resolution Procedure' */ /* [Promises/A+ 2.3] */
+ var resolve = function(promise, x) {
+ /* sanity check arguments */ /* [Promises/A+ 2.3.1] */
+ if (promise === x) {
+ promise.reject(new TypeError('cannot resolve promise with itself'));
+ return;
+ }
+
+ /* surgically check for a 'then' method
+ (mainly to just call the 'getter' of 'then' only once) */
+ var then;
+ if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
+ try { then = x.then; } /* [Promises/A+ 2.3.3.1, 3.5] */
+ catch (e) {
+ promise.reject(e); /* [Promises/A+ 2.3.3.2] */
+ return;
+ }
+ }
+
+ /* handle own Thenables [Promises/A+ 2.3.2]
+ and similar 'thenables' [Promises/A+ 2.3.3] */
+ if (typeof then === 'function') {
+ var resolved = false;
+ try {
+ /* call retrieved 'then' method */ /* [Promises/A+ 2.3.3.3] */
+ then.call(x,
+ /* resolvePromise */ /* [Promises/A+ 2.3.3.3.1] */
+ function(y) {
+ if (resolved) return; resolved = true; /* [Promises/A+ 2.3.3.3.3] */
+ if (y === x) /* [Promises/A+ 3.6] */
+ promise.reject(new TypeError('circular thenable chain'));
+ else
+ resolve(promise, y);
+ },
+
+ /* rejectPromise */ /* [Promises/A+ 2.3.3.3.2] */
+ function(r) {
+ if (resolved) return; resolved = true; /* [Promises/A+ 2.3.3.3.3] */
+ promise.reject(r);
+ }
+ );
+ }
+ catch (e) {
+ if (!resolved) /* [Promises/A+ 2.3.3.3.3] */
+ promise.reject(e); /* [Promises/A+ 2.3.3.3.4] */
+ }
+ return;
+ }
+
+ /* handle other values */
+ promise.fulfill(x); /* [Promises/A+ 2.3.4, 2.3.3.4] */
+ };
+
+ Promise.resolve = function(value) {
+ return new Promise(function(resolve) {
+ resolve(value);
+ });
+ };
+
+ Promise.reject = function(reason) {
+ return new Promise(function(resolve, reject) {
+ reject(reason);
+ });
+ };
+
+ /* export API */
+ module.exports = Promise;
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/readonly.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/readonly.js
new file mode 100644
index 0000000..ddacc11
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/readonly.js
@@ -0,0 +1,63 @@
+/**
+ * @fileOverview
+ *
+ * 只读模式支持
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+
+define(function(require, exports, module) {
+ var kity = require('./kity');
+ var Minder = require('./minder');
+ var MinderEvent = require('./event');
+
+ Minder.registerInitHook(function(options) {
+ if (options.readOnly) {
+ this.setDisabled();
+ }
+ });
+
+ kity.extendClass(Minder, {
+
+ disable: function() {
+ var me = this;
+ //禁用命令
+ me.bkqueryCommandState = me.queryCommandState;
+ me.bkqueryCommandValue = me.queryCommandValue;
+ me.queryCommandState = function(type) {
+ var cmd = this._getCommand(type);
+ if (cmd && cmd.enableReadOnly) {
+ return me.bkqueryCommandState.apply(me, arguments);
+ }
+ return -1;
+ };
+ me.queryCommandValue = function(type) {
+ var cmd = this._getCommand(type);
+ if (cmd && cmd.enableReadOnly) {
+ return me.bkqueryCommandValue.apply(me, arguments);
+ }
+ return null;
+ };
+ this.setStatus('readonly');
+ me._interactChange();
+ },
+
+ enable: function() {
+ var me = this;
+
+ if (me.bkqueryCommandState) {
+ me.queryCommandState = me.bkqueryCommandState;
+ delete me.bkqueryCommandState;
+ }
+ if (me.bkqueryCommandValue) {
+ me.queryCommandValue = me.bkqueryCommandValue;
+ delete me.bkqueryCommandValue;
+ }
+
+ this.setStatus('normal');
+
+ me._interactChange();
+ }
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/render.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/render.js
new file mode 100644
index 0000000..ae4a5e0
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/render.js
@@ -0,0 +1,261 @@
+define(function(require, exports, module) {
+
+ var kity = require('./kity');
+ var Minder = require('./minder');
+ var MinderNode = require('./node');
+
+ var Renderer = kity.createClass('Renderer', {
+ constructor: function(node) {
+ this.node = node;
+ },
+
+ create: function(node) {
+ throw new Error('Not implement: Renderer.create()');
+ },
+
+ shouldRender: function(node) {
+ return true;
+ },
+
+ watchChange: function(data) {
+ var changed;
+
+ if (this.watchingData === undefined) {
+ changed = true;
+ } else if (this.watchingData != data) {
+ changed = true;
+ } else {
+ changed = false;
+ }
+
+ this.watchingData = data;
+ },
+
+ shouldDraw: function(node) {
+ return true;
+ },
+
+ update: function(shape, node, box) {
+ if (this.shouldDraw()) this.draw(shape, node);
+ return this.place(shape, node, box);
+ },
+
+ draw: function(shape, node) {
+ throw new Error('Not implement: Renderer.draw()');
+ },
+
+ place: function(shape, node, box) {
+ throw new Error('Not implement: Renderer.place()');
+ },
+
+ getRenderShape: function() {
+ return this._renderShape || null;
+ },
+
+ setRenderShape: function(shape) {
+ this._renderShape = shape;
+ }
+ });
+
+ function createMinderExtension() {
+
+ function createRendererForNode(node, registered) {
+ var renderers = [];
+
+ ['center', 'left', 'right', 'top', 'bottom', 'outline', 'outside'].forEach(function(section) {
+ var before = 'before' + section;
+ var after = 'after' + section;
+
+ if (registered[before]) {
+ renderers = renderers.concat(registered[before]);
+ }
+ if (registered[section]) {
+ renderers = renderers.concat(registered[section]);
+ }
+ if (registered[after]) {
+ renderers = renderers.concat(registered[after]);
+ }
+ });
+
+ node._renderers = renderers.map(function(Renderer) {
+ return new Renderer(node);
+ });
+ }
+
+ return {
+ renderNodeBatch: function(nodes) {
+ var rendererClasses = this._rendererClasses;
+ var lastBoxes = [];
+ var rendererCount = 0;
+ var i, j, renderer, node;
+
+ if (!nodes.length) return;
+
+ for (j = 0; j < nodes.length; j++) {
+ node = nodes[j];
+ if (!node._renderers) {
+ createRendererForNode(node, rendererClasses);
+ }
+ node._contentBox = new kity.Box();
+ this.fire('beforerender', {
+ node: node
+ });
+ }
+
+ // 所有节点渲染器数量是一致的
+ rendererCount = nodes[0]._renderers.length;
+
+ for (i = 0; i < rendererCount; i++) {
+
+ // 获取延迟盒子数据
+ for (j = 0; j < nodes.length; j++) {
+ if (typeof(lastBoxes[j]) == 'function') {
+ lastBoxes[j] = lastBoxes[j]();
+ }
+ if (!(lastBoxes[j] instanceof kity.Box)) {
+ lastBoxes[j] = new kity.Box(lastBoxes[j]);
+ }
+ }
+
+ for (j = 0; j < nodes.length; j++) {
+ node = nodes[j];
+ renderer = node._renderers[i];
+
+ // 合并盒子
+ if (lastBoxes[j]) {
+ node._contentBox = node._contentBox.merge(lastBoxes[j]);
+ renderer.contentBox = lastBoxes[j];
+ }
+
+ // 判断当前上下文是否应该渲染
+ if (renderer.shouldRender(node)) {
+
+ // 应该渲染,但是渲染图形没创建过,需要创建
+ if (!renderer.getRenderShape()) {
+ renderer.setRenderShape(renderer.create(node));
+ if (renderer.bringToBack) {
+ node.getRenderContainer().prependShape(renderer.getRenderShape());
+ } else {
+ node.getRenderContainer().appendShape(renderer.getRenderShape());
+ }
+ }
+
+ // 强制让渲染图形显示
+ renderer.getRenderShape().setVisible(true);
+
+ // 更新渲染图形
+ lastBoxes[j] = renderer.update(renderer.getRenderShape(), node, node._contentBox);
+ }
+
+ // 如果不应该渲染,但是渲染图形创建过了,需要隐藏起来
+ else if (renderer.getRenderShape()) {
+ renderer.getRenderShape().setVisible(false);
+ lastBoxes[j] = null;
+ }
+ }
+ }
+
+ for (j = 0; j < nodes.length; j++) {
+ this.fire('noderender', {
+ node: nodes[j]
+ });
+ }
+ },
+
+ renderNode: function(node) {
+ var rendererClasses = this._rendererClasses;
+ var i, latestBox, renderer;
+
+ if (!node._renderers) {
+ createRendererForNode(node, rendererClasses);
+ }
+
+ this.fire('beforerender', {
+ node: node
+ });
+
+ node._contentBox = new kity.Box();
+
+ node._renderers.forEach(function(renderer) {
+
+ // 判断当前上下文是否应该渲染
+ if (renderer.shouldRender(node)) {
+
+ // 应该渲染,但是渲染图形没创建过,需要创建
+ if (!renderer.getRenderShape()) {
+ renderer.setRenderShape(renderer.create(node));
+ if (renderer.bringToBack) {
+ node.getRenderContainer().prependShape(renderer.getRenderShape());
+ } else {
+ node.getRenderContainer().appendShape(renderer.getRenderShape());
+ }
+ }
+
+ // 强制让渲染图形显示
+ renderer.getRenderShape().setVisible(true);
+
+ // 更新渲染图形
+ latestBox = renderer.update(renderer.getRenderShape(), node, node._contentBox);
+
+ if (typeof(latestBox) == 'function') latestBox = latestBox();
+
+ // 合并渲染区域
+ if (latestBox) {
+ node._contentBox = node._contentBox.merge(latestBox);
+ renderer.contentBox = latestBox;
+ }
+ }
+
+ // 如果不应该渲染,但是渲染图形创建过了,需要隐藏起来
+ else if (renderer.getRenderShape()) {
+ renderer.getRenderShape().setVisible(false);
+ }
+
+ });
+
+ this.fire('noderender', {
+ node: node
+ });
+ }
+ };
+ }
+
+ kity.extendClass(Minder, createMinderExtension());
+
+ kity.extendClass(MinderNode, {
+ render: function() {
+ if (!this.attached) return;
+ this.getMinder().renderNode(this);
+ return this;
+ },
+ renderTree: function() {
+ if (!this.attached) return;
+ var list = [];
+ this.traverse(function(node) {
+ list.push(node);
+ });
+ this.getMinder().renderNodeBatch(list);
+ return this;
+ },
+ getRenderer: function(type) {
+ var rs = this._renderers;
+ if (!rs) return null;
+ for (var i = 0; i < rs.length; i++) {
+ if (rs[i].getType() == type) return rs[i];
+ }
+ return null;
+ },
+ getContentBox: function() {
+ //if (!this._contentBox) this.render();
+ return this.parent && this.parent.isCollapsed() ? new kity.Box() : (this._contentBox || new kity.Box());
+ },
+ getRenderBox: function(rendererType, refer) {
+ var renderer = rendererType && this.getRenderer(rendererType);
+ var contentBox = renderer ? renderer.contentBox : this.getContentBox();
+ var ctm = kity.Matrix.getCTM(this.getRenderContainer(), refer || 'paper');
+ return ctm.transformBox(contentBox);
+ }
+ });
+
+ module.exports = Renderer;
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/select.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/select.js
new file mode 100644
index 0000000..4366b22
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/select.js
@@ -0,0 +1,146 @@
+
+define(function(require, exports, module) {
+ var kity = require('./kity');
+ var utils = require('./utils');
+ var Minder = require('./minder');
+ var MinderNode = require('./node');
+
+ Minder.registerInitHook(function() {
+ this._initSelection();
+ });
+
+ // 选区管理
+ kity.extendClass(Minder, {
+ _initSelection: function() {
+ this._selectedNodes = [];
+ },
+ renderChangedSelection: function(last) {
+ var current = this.getSelectedNodes();
+ var changed = [];
+
+ current.forEach(function(node) {
+ if (last.indexOf(node) == -1) {
+ changed.push(node);
+ }
+ });
+
+ last.forEach(function(node) {
+ if (current.indexOf(node) == -1) {
+ changed.push(node);
+ }
+ });
+
+ if (changed.length) {
+ this._interactChange();
+ this.fire('selectionchange');
+ }
+ while (changed.length) {
+ changed.shift().render();
+ }
+ },
+ getSelectedNodes: function() {
+ //不能克隆返回,会对当前选区操作,从而影响querycommand
+ return this._selectedNodes;
+ },
+ getSelectedNode: function() {
+ return this.getSelectedNodes()[0] || null;
+ },
+ removeAllSelectedNodes: function() {
+ var me = this;
+ var last = this._selectedNodes.splice(0);
+ this._selectedNodes = [];
+ this.renderChangedSelection(last);
+ return this.fire('selectionclear');
+ },
+ removeSelectedNodes: function(nodes) {
+ var me = this;
+ var last = this._selectedNodes.slice(0);
+ nodes = utils.isArray(nodes) ? nodes : [nodes];
+
+ nodes.forEach(function(node) {
+ var index;
+ if ((index = me._selectedNodes.indexOf(node)) === -1) return;
+ me._selectedNodes.splice(index, 1);
+ });
+
+ this.renderChangedSelection(last);
+ return this;
+ },
+ select: function(nodes, isSingleSelect) {
+ var lastSelect = this.getSelectedNodes().slice(0);
+ if (isSingleSelect) {
+ this._selectedNodes = [];
+ }
+ var me = this;
+ nodes = utils.isArray(nodes) ? nodes : [nodes];
+ nodes.forEach(function(node) {
+ if (me._selectedNodes.indexOf(node) !== -1) return;
+ me._selectedNodes.unshift(node);
+ });
+ this.renderChangedSelection(lastSelect);
+ return this;
+ },
+ selectById: function(ids, isSingleSelect) {
+ ids = utils.isArray(ids) ? ids : [ids];
+ var nodes = this.getNodesById(ids);
+ return this.select(nodes, isSingleSelect);
+ },
+ //当前选区中的节点在给定的节点范围内的保留选中状态,
+ //没在给定范围的取消选中,给定范围中的但没在当前选中范围的也做选中效果
+ toggleSelect: function(node) {
+ if (utils.isArray(node)) {
+ node.forEach(this.toggleSelect.bind(this));
+ } else {
+ if (node.isSelected()) this.removeSelectedNodes(node);
+ else this.select(node);
+ }
+ return this;
+ },
+
+ isSingleSelect: function() {
+ return this._selectedNodes.length == 1;
+ },
+
+ getSelectedAncestors: function(includeRoot) {
+ var nodes = this.getSelectedNodes().slice(0),
+ ancestors = [],
+ judge;
+
+ // 根节点不参与计算
+ var rootIndex = nodes.indexOf(this.getRoot());
+ if (~rootIndex && !includeRoot) {
+ nodes.splice(rootIndex, 1);
+ }
+
+ // 判断 nodes 列表中是否存在 judge 的祖先
+ function hasAncestor(nodes, judge) {
+ for (var i = nodes.length - 1; i >= 0; --i) {
+ if (nodes[i].isAncestorOf(judge)) return true;
+ }
+ return false;
+ }
+
+ // 按照拓扑排序
+ nodes.sort(function(node1, node2) {
+ return node1.getLevel() - node2.getLevel();
+ });
+
+ // 因为是拓扑有序的,所以只需往上查找
+ while ((judge = nodes.pop())) {
+ if (!hasAncestor(nodes, judge)) {
+ ancestors.push(judge);
+ }
+ }
+
+ return ancestors;
+ }
+ });
+
+ kity.extendClass(MinderNode, {
+ isSelected: function() {
+ var minder = this.getMinder();
+ return minder && minder.getSelectedNodes().indexOf(this) != -1;
+ }
+ });
+
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/shortcut.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/shortcut.js
new file mode 100644
index 0000000..57682e8
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/shortcut.js
@@ -0,0 +1,154 @@
+/**
+ * @fileOverview
+ *
+ * 添加快捷键支持
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+
+define(function(require, exports, module) {
+ var kity = require('./kity');
+ var utils = require('./utils');
+ var keymap = require('./keymap');
+ var Minder = require('./minder');
+ var MinderEvent = require('./event');
+
+ /**
+ * 计算包含 meta 键的 keycode
+ *
+ * @param {String|KeyEvent} unknown
+ */
+ function getMetaKeyCode(unknown) {
+ var CTRL_MASK = 0x1000;
+ var ALT_MASK = 0x2000;
+ var SHIFT_MASK = 0x4000;
+ var metaKeyCode = 0;
+
+ if (typeof(unknown) == 'string') {
+ // unknown as string
+ unknown.toLowerCase().split(/\+\s*/).forEach(function(name) {
+ switch(name) {
+ case 'ctrl':
+ case 'cmd':
+ metaKeyCode |= CTRL_MASK;
+ break;
+ case 'alt':
+ metaKeyCode |= ALT_MASK;
+ break;
+ case 'shift':
+ metaKeyCode |= SHIFT_MASK;
+ break;
+ default:
+ metaKeyCode |= keymap[name];
+ }
+ });
+ } else {
+ // unknown as key event
+ if (unknown.ctrlKey || unknown.metaKey) {
+ metaKeyCode |= CTRL_MASK;
+ }
+ if (unknown.altKey) {
+ metaKeyCode |= ALT_MASK;
+ }
+ if (unknown.shiftKey) {
+ metaKeyCode |= SHIFT_MASK;
+ }
+ metaKeyCode |= unknown.keyCode;
+ }
+
+ return metaKeyCode;
+ }
+ kity.extendClass(MinderEvent, {
+ isShortcutKey: function(keyCombine) {
+ var keyEvent = this.originEvent;
+ if (!keyEvent) return false;
+
+ return getMetaKeyCode(keyCombine) == getMetaKeyCode(keyEvent);
+ }
+ });
+
+ Minder.registerInitHook(function() {
+ this._initShortcutKey();
+ });
+
+ kity.extendClass(Minder, {
+
+ _initShortcutKey: function() {
+ this._bindShortcutKeys();
+ },
+
+ _bindShortcutKeys: function() {
+ var map = this._shortcutKeys = {};
+ var has = 'hasOwnProperty';
+ this.on('keydown', function(e) {
+ for (var keys in map) {
+ if (!map[has](keys)) continue;
+ if (e.isShortcutKey(keys)) {
+ var fn = map[keys];
+ if (fn.__statusCondition && fn.__statusCondition != this.getStatus()) return;
+ fn();
+ e.preventDefault();
+ }
+ }
+ });
+ },
+
+ addShortcut: function(keys, fn) {
+ var binds = this._shortcutKeys;
+ keys.split(/\|\s*/).forEach(function(combine) {
+ var parts = combine.split('::');
+ var status;
+ if (parts.length > 1) {
+ combine = parts[1];
+ status = parts[0];
+ fn.__statusCondition = status;
+ }
+ binds[combine] = fn;
+ });
+ },
+
+ addCommandShortcutKeys: function(cmd, keys) {
+ var binds = this._commandShortcutKeys || (this._commandShortcutKeys = {});
+ var obj = {},
+ km = this;
+ if (keys) {
+ obj[cmd] = keys;
+ } else {
+ obj = cmd;
+ }
+
+ var minder = this;
+
+ utils.each(obj, function(keys, command) {
+
+ binds[command] = keys;
+
+ minder.addShortcut(keys, function execCommandByShortcut() {
+ /**
+ * 之前判断有问题,由 === 0 改为 !== -1
+ * @editor Naixor
+ * @Date 2015-12-2
+ */
+ if (minder.queryCommandState(command) !== -1) {
+ minder.execCommand(command);
+ }
+ });
+ });
+ },
+
+ getCommandShortcutKey: function(cmd) {
+ var binds = this._commandShortcutKeys;
+ return binds && binds[cmd] || null;
+ },
+
+ /**
+ * @Desc: 添加一个判断是否支持原生Clipboard的变量,用于对ctrl + v和ctrl + c的处理
+ * @Editor: Naixor
+ * @Date: 2015.9.20
+ */
+ supportClipboardEvent: (function(window) {
+ return !!window.ClipboardEvent;
+ })(window)
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/status.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/status.js
new file mode 100644
index 0000000..a3ae2e1
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/status.js
@@ -0,0 +1,60 @@
+/**
+ * @fileOverview
+ *
+ * 状态切换控制
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+
+define(function(require, exports, module) {
+ var kity = require('./kity');
+ var Minder = require('./minder');
+
+ var sf = ~window.location.href.indexOf('status');
+ var tf = ~window.location.href.indexOf('trace');
+
+ Minder.registerInitHook(function() {
+ this._initStatus();
+ });
+
+ kity.extendClass(Minder, {
+
+ _initStatus: function() {
+ this._status = 'normal';
+ this._rollbackStatus = 'normal';
+ },
+
+ setStatus: function(status, force) {
+ // 在 readonly 模式下,只有 force 为 true 才能切换回来
+ if (this._status == 'readonly' && !force) return this;
+ if (status != this._status) {
+ this._rollbackStatus = this._status;
+ this._status = status;
+ this.fire('statuschange', {
+ lastStatus: this._rollbackStatus,
+ currentStatus: this._status
+ });
+ if (sf) {
+ /* global console: true */
+ console.log(window.event.type, this._rollbackStatus, '->', this._status);
+ if (tf) {
+ console.trace();
+ }
+ }
+ }
+ return this;
+ },
+
+ rollbackStatus: function() {
+ this.setStatus(this._rollbackStatus);
+ },
+ getRollbackStatus:function(){
+ return this._rollbackStatus;
+ },
+ getStatus: function() {
+ return this._status;
+ }
+ });
+
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/template.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/template.js
new file mode 100644
index 0000000..94df4bb
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/template.js
@@ -0,0 +1,92 @@
+define(function(require, exports, module) {
+ var kity = require('./kity');
+ var utils = require('./utils');
+ var Minder = require('./minder');
+ var Command = require('./command');
+ var MinderNode = require('./node');
+ var Module = require('./module');
+
+ var _templates = {};
+
+ function register(name, supports) {
+ _templates[name] = supports;
+ }
+ exports.register = register;
+
+ utils.extend(Minder, {
+ getTemplateList: function() {
+ return _templates;
+ }
+ });
+
+ kity.extendClass(Minder, (function() {
+ var originGetTheme = Minder.prototype.getTheme;
+ return {
+ useTemplate: function(name, duration) {
+ this.setTemplate(name);
+ this.refresh(duration || 800);
+ },
+
+ getTemplate: function() {
+ return this._template || 'default';
+ },
+
+ setTemplate: function(name) {
+ this._template = name || null;
+ },
+
+ getTemplateSupport: function(method) {
+ var supports = _templates[this.getTemplate()];
+ return supports && supports[method];
+ },
+
+ getTheme: function(node) {
+ var support = this.getTemplateSupport('getTheme') || originGetTheme;
+ return support.call(this, node);
+ }
+ };
+ })());
+
+
+ kity.extendClass(MinderNode, (function() {
+ var originGetLayout = MinderNode.prototype.getLayout;
+ var originGetConnect = MinderNode.prototype.getConnect;
+ return {
+ getLayout: function() {
+ var support = this.getMinder().getTemplateSupport('getLayout') || originGetLayout;
+ return support.call(this, this);
+ },
+
+ getConnect: function() {
+ var support = this.getMinder().getTemplateSupport('getConnect') || originGetConnect;
+ return support.call(this, this);
+ }
+ };
+ })());
+
+ Module.register('TemplateModule', {
+ /**
+ * @command Template
+ * @description 设置当前脑图的模板
+ * @param {string} name 模板名称
+ * 允许使用的模板可以使用 `kityminder.Minder.getTemplateList()` 查询
+ * @state
+ * 0: 始终可用
+ * @return 返回当前的模板名称
+ */
+ commands: {
+ 'template': kity.createClass('TemplateCommand', {
+ base: Command,
+
+ execute: function(minder, name) {
+ minder.useTemplate(name);
+ minder.execCommand('camera');
+ },
+
+ queryValue: function(minder) {
+ return minder.getTemplate() || 'default';
+ }
+ })
+ }
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/theme.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/theme.js
new file mode 100644
index 0000000..20a11d4
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/theme.js
@@ -0,0 +1,175 @@
+define(function(require, exports, module) {
+ var kity = require('./kity');
+ var utils = require('./utils');
+ var Minder = require('./minder');
+ var MinderNode = require('./node');
+ var Module = require('./module');
+ var Command = require('./command');
+
+ var cssLikeValueMatcher = {
+ left: function(value) {
+ return 3 in value && value[3] ||
+ 1 in value && value[1] ||
+ value[0];
+ },
+ right: function(value) {
+ return 1 in value && value[1] || value[0];
+ },
+ top: function(value) {
+ return value[0];
+ },
+ bottom: function(value) {
+ return 2 in value && value[2] || value[0];
+ }
+ };
+
+ var _themes = {};
+
+ /**
+ * 注册一个主题
+ *
+ * @param {String} name 主题的名称
+ * @param {Plain} theme 主题的样式描述
+ *
+ * @example
+ * Minder.registerTheme('default', {
+ * 'root-color': 'red',
+ * 'root-stroke': 'none',
+ * 'root-padding': [10, 20]
+ * });
+ */
+ function register(name, theme) {
+ _themes[name] = theme;
+ }
+ exports.register = register;
+
+ utils.extend(Minder, {
+ getThemeList: function() {
+ return _themes;
+ }
+ });
+
+ kity.extendClass(Minder, {
+
+ /**
+ * 切换脑图实例上的主题
+ * @param {String} name 要使用的主题的名称
+ */
+ useTheme: function(name) {
+
+ this.setTheme(name);
+ this.refresh(800);
+
+ return true;
+ },
+
+ setTheme: function(name) {
+ if (name && !_themes[name]) throw new Error('Theme ' + name + ' not exists!');
+ var lastTheme = this._theme;
+ this._theme = name || null;
+ var container = this.getRenderTarget();
+ if (container) {
+ container.classList.remove('km-theme-' + lastTheme);
+ if (name) {
+ container.classList.add('km-theme-' + name);
+ }
+ container.style.background = this.getStyle('background');
+ }
+ this.fire('themechange', {
+ theme: name
+ });
+ return this;
+ },
+
+ /**
+ * 获取脑图实例上的当前主题
+ * @return {[type]} [description]
+ */
+ getTheme: function(node) {
+ return this._theme || this.getOption('defaultTheme') || 'fresh-blue';
+ },
+
+ getThemeItems: function(node) {
+ var theme = this.getTheme(node);
+ return _themes[this.getTheme(node)];
+ },
+
+ /**
+ * 获得脑图实例上的样式
+ * @param {String} item 样式名称
+ */
+ getStyle: function(item, node) {
+ var items = this.getThemeItems(node);
+ var segment, dir, selector, value, matcher;
+
+ if (item in items) return items[item];
+
+ // 尝试匹配 CSS 数组形式的值
+ // 比如 item 为 'pading-left'
+ // theme 里有 {'padding': [10, 20]} 的定义,则可以返回 20
+ segment = item.split('-');
+ if (segment.length < 2) return null;
+
+ dir = segment.pop();
+ item = segment.join('-');
+
+ if (item in items) {
+ value = items[item];
+ if (utils.isArray(value) && (matcher = cssLikeValueMatcher[dir])) {
+ return matcher(value);
+ }
+ if (!isNaN(value)) return value;
+ }
+
+ return null;
+ },
+
+ /**
+ * 获取指定节点的样式
+ * @param {String} name 样式名称,可以不加节点类型的前缀
+ */
+ getNodeStyle: function(node, name) {
+ var value = this.getStyle(node.getType() + '-' + name, node);
+ return value !== null ? value : this.getStyle(name, node);
+ }
+ });
+
+ kity.extendClass(MinderNode, {
+ getStyle: function(name) {
+ return this.getMinder().getNodeStyle(this, name);
+ }
+ });
+
+ Module.register('Theme', {
+ defaultOptions: {
+ defaultTheme: 'fresh-blue'
+ },
+ commands: {
+ /**
+ * @command Theme
+ * @description 设置当前脑图的主题
+ * @param {string} name 主题名称
+ * 允许使用的主题可以使用 `kityminder.Minder.getThemeList()` 查询
+ * @state
+ * 0: 始终可用
+ * @return 返回当前的主题名称
+ */
+ 'theme': kity.createClass('ThemeCommand', {
+ base: Command,
+
+ execute: function(km, name) {
+ return km.useTheme(name);
+ },
+
+ queryValue: function(km) {
+ return km.getTheme() || 'default';
+ }
+ })
+ }
+ });
+
+ Minder.registerInitHook(function() {
+ this.setTheme();
+ });
+
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/utils.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/utils.js
new file mode 100644
index 0000000..955b4e7
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/core/utils.js
@@ -0,0 +1,65 @@
+define(function(require, exports) {
+ var kity = require('./kity');
+ var uuidMap = {};
+
+ exports.extend = kity.Utils.extend.bind(kity.Utils);
+ exports.each = kity.Utils.each.bind(kity.Utils);
+
+ exports.uuid = function(group) {
+ uuidMap[group] = uuidMap[group] ? uuidMap[group] + 1 : 1;
+ return group + uuidMap[group];
+ };
+
+ exports.guid = function() {
+ return (+new Date() * 1e6 + Math.floor(Math.random() * 1e6)).toString(36);
+ };
+
+ exports.trim = function(str) {
+ return str.replace(/(^[ \t\n\r]+)|([ \t\n\r]+$)/g, '');
+ };
+
+ exports.keys = function(plain) {
+ var keys = [];
+ for (var key in plain) {
+ if (plain.hasOwnProperty(key)) {
+ keys.push(key);
+ }
+ }
+ return keys;
+ };
+
+ exports.clone = function(source) {
+ return JSON.parse(JSON.stringify(source));
+ };
+
+ exports.comparePlainObject = function(a, b) {
+ return JSON.stringify(a) == JSON.stringify(b);
+ };
+
+ exports.encodeHtml = function(str, reg) {
+ return str ? str.replace(reg || /[&<">'](?:(amp|lt|quot|gt|#39|nbsp);)?/g, function(a, b) {
+ if (b) {
+ return a;
+ } else {
+ return {
+ '<': '<',
+ '&': '&',
+ '"': '"',
+ '>': '>',
+ '\'': '''
+ }[a];
+ }
+ }) : '';
+ };
+
+ exports.clearWhiteSpace = function(str) {
+ return str.replace(/[\u200b\t\r\n]/g, '');
+ };
+
+ exports.each(['String', 'Function', 'Array', 'Number', 'RegExp', 'Object'], function(v) {
+ var toString = Object.prototype.toString;
+ exports['is' + v] = function(obj) {
+ return toString.apply(obj) == '[object ' + v + ']';
+ };
+ });
+});
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/expose-kityminder.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/expose-kityminder.js
new file mode 100644
index 0000000..32e5e87
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/expose-kityminder.js
@@ -0,0 +1,3 @@
+define('expose-kityminder', function(require, exports, module) {
+ module.exports = window.kityminder = require('./kityminder');
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/kityminder.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/kityminder.js
new file mode 100644
index 0000000..cfcc2cd
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/kityminder.js
@@ -0,0 +1,106 @@
+/**
+ * @fileOverview
+ *
+ * 默认导出(全部模块)
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+
+define(function (require, exports, module) {
+ var kityminder = {
+ version: require('./core/minder').version,
+ };
+
+ // 核心导出,大写的部分导出类,小写的部分简单 require 一下
+ // 这里顺序是有讲究的,调整前先弄清楚依赖关系。
+ require('./core/utils');
+ kityminder.Minder = require('./core/minder');
+ kityminder.Command = require('./core/command');
+ kityminder.Node = require('./core/node');
+ require('./core/option');
+ require('./core/animate');
+ kityminder.Event = require('./core/event');
+ kityminder.data = require('./core/data');
+ require('./core/compatibility');
+ kityminder.KeyMap = require('./core/keymap');
+ require('./core/shortcut');
+ require('./core/status');
+ require('./core/paper');
+ require('./core/select');
+ require('./core/focus');
+ require('./core/keyreceiver');
+ kityminder.Module = require('./core/module');
+ require('./core/readonly');
+ kityminder.Render = require('./core/render');
+ kityminder.Connect = require('./core/connect');
+ kityminder.Layout = require('./core/layout');
+ kityminder.Theme = require('./core/theme');
+ kityminder.Template = require('./core/template');
+ kityminder.Promise = require('./core/promise');
+ require('./core/_boxv');
+ require('./core/patch');
+
+ // 模块依赖
+ require('./module/arrange');
+ require('./module/basestyle');
+ require('./module/clipboard');
+ require('./module/dragtree');
+ require('./module/expand');
+ require('./module/font');
+ require('./module/hyperlink');
+ require('./module/image');
+ require('./module/image-viewer');
+ require('./module/keynav');
+ require('./module/layout');
+ require('./module/node');
+ require('./module/note');
+ require('./module/outline');
+ require('./module/priority');
+ require('./module/progress');
+ require('./module/resource');
+ require('./module/select');
+ require('./module/style');
+ require('./module/text');
+ require('./module/view');
+ require('./module/zoom');
+
+ require('./protocol/json');
+ require('./protocol/text');
+ require('./protocol/markdown');
+ require('./protocol/svg');
+ require('./protocol/png');
+
+ require('./layout/mind');
+ require('./layout/btree');
+ require('./layout/filetree');
+ require('./layout/fish-bone-master');
+ require('./layout/fish-bone-slave');
+ require('./layout/tianpan');
+
+ require('./theme/default');
+ require('./theme/snow');
+ require('./theme/fresh');
+ require('./theme/fish');
+ require('./theme/snow');
+ require('./theme/wire');
+ require('./theme/tianpan');
+
+ require('./connect/arc');
+ require('./connect/arc_tp');
+ require('./connect/bezier');
+ require('./connect/fish-bone-master');
+ require('./connect/l');
+ require('./connect/poly');
+ require('./connect/under');
+
+ require('./template/default');
+ require('./template/structure');
+ require('./template/filetree');
+ require('./template/right');
+ require('./template/fish-bone');
+ require('./template/tianpan');
+
+ window.kityminder = kityminder;
+ module.exports = kityminder;
+});
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/layout/btree.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/layout/btree.js
new file mode 100644
index 0000000..1f7f9c6
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/layout/btree.js
@@ -0,0 +1,143 @@
+define(function(require, exports, module) {
+ var kity = require('../core/kity');
+ var Layout = require('../core/layout');
+
+ ['left', 'right', 'top', 'bottom'].forEach(registerLayoutForDirection);
+
+ function registerLayoutForDirection(name) {
+
+ var axis = (name == 'left' || name == 'right') ? 'x' : 'y';
+ var dir = (name == 'left' || name == 'top') ? -1 : 1;
+
+ var oppsite = {
+ 'left': 'right',
+ 'right': 'left',
+ 'top': 'bottom',
+ 'bottom': 'top',
+ 'x': 'y',
+ 'y': 'x'
+ };
+
+ function getOrderHint(node) {
+ var hint = [];
+ var box = node.getLayoutBox();
+ var offset = 5;
+
+ if (axis == 'x') {
+ hint.push({
+ type: 'up',
+ node: node,
+ area: new kity.Box({
+ x: box.x,
+ y: box.top - node.getStyle('margin-top') - offset,
+ width: box.width,
+ height: node.getStyle('margin-top')
+ }),
+ path: ['M', box.x, box.top - offset, 'L', box.right, box.top - offset]
+ });
+
+ hint.push({
+ type: 'down',
+ node: node,
+ area: new kity.Box({
+ x: box.x,
+ y: box.bottom + offset,
+ width: box.width,
+ height: node.getStyle('margin-bottom')
+ }),
+ path: ['M', box.x, box.bottom + offset, 'L', box.right, box.bottom + offset]
+ });
+ } else {
+ hint.push({
+ type: 'up',
+ node: node,
+ area: new kity.Box({
+ x: box.left - node.getStyle('margin-left') - offset,
+ y: box.top,
+ width: node.getStyle('margin-left'),
+ height: box.height
+ }),
+ path: ['M', box.left - offset, box.top, 'L', box.left - offset, box.bottom]
+ });
+
+ hint.push({
+ type: 'down',
+ node: node,
+ area: new kity.Box({
+ x: box.right + offset,
+ y: box.top,
+ width: node.getStyle('margin-right'),
+ height: box.height
+ }),
+ path: ['M', box.right + offset, box.top, 'L', box.right + offset, box.bottom]
+ });
+ }
+ return hint;
+ }
+
+ Layout.register(name, kity.createClass({
+
+ base: Layout,
+
+ doLayout: function(parent, children) {
+
+ var pbox = parent.getContentBox();
+
+ if (axis == 'x') {
+ parent.setVertexOut(new kity.Point(pbox[name], pbox.cy));
+ parent.setLayoutVectorOut(new kity.Vector(dir, 0));
+ } else {
+ parent.setVertexOut(new kity.Point(pbox.cx, pbox[name]));
+ parent.setLayoutVectorOut(new kity.Vector(0, dir));
+ }
+
+ if (!children.length) {
+ return false;
+ }
+
+ children.forEach(function(child) {
+ var cbox = child.getContentBox();
+ child.setLayoutTransform(new kity.Matrix());
+
+ if (axis == 'x') {
+ child.setVertexIn(new kity.Point(cbox[oppsite[name]], cbox.cy));
+ child.setLayoutVectorIn(new kity.Vector(dir, 0));
+ } else {
+ child.setVertexIn(new kity.Point(cbox.cx, cbox[oppsite[name]]));
+ child.setLayoutVectorIn(new kity.Vector(0, dir));
+ }
+ });
+
+ this.align(children, oppsite[name]);
+ this.stack(children, oppsite[axis]);
+
+ var bbox = this.getBranchBox(children);
+ var xAdjust = 0, yAdjust = 0;
+
+ if (axis == 'x') {
+ xAdjust = pbox[name];
+ xAdjust += dir * parent.getStyle('margin-' + name);
+ xAdjust += dir * children[0].getStyle('margin-' + oppsite[name]);
+
+ yAdjust = pbox.bottom;
+ yAdjust -= pbox.height / 2;
+ yAdjust -= bbox.height / 2;
+ yAdjust -= bbox.y;
+ } else {
+ xAdjust = pbox.right;
+ xAdjust -= pbox.width / 2;
+ xAdjust -= bbox.width / 2;
+ xAdjust -= bbox.x;
+
+ yAdjust = pbox[name];
+ yAdjust += dir * parent.getStyle('margin-' + name);
+ yAdjust += dir * children[0].getStyle('margin-' + oppsite[name]);
+ }
+
+ this.move(children, xAdjust, yAdjust);
+ },
+
+ getOrderHint: getOrderHint
+ }));
+ }
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/layout/filetree.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/layout/filetree.js
new file mode 100644
index 0000000..4f1e715
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/layout/filetree.js
@@ -0,0 +1,89 @@
+define(function(require, exports, module) {
+ var kity = require('../core/kity');
+ var Layout = require('../core/layout');
+
+ [-1, 1].forEach(registerLayoutForDir);
+
+ function registerLayoutForDir(dir) {
+ var name = 'filetree-' + (dir > 0 ? 'down' : 'up');
+
+ Layout.register(name, kity.createClass({
+ base: Layout,
+
+ doLayout: function(parent, children, round) {
+ var pBox = parent.getContentBox();
+ var indent = 20;
+
+ parent.setVertexOut(new kity.Point(pBox.left + indent, dir > 0 ? pBox.bottom : pBox.top));
+ parent.setLayoutVectorOut(new kity.Vector(0, dir));
+
+ if (!children.length) return;
+
+ children.forEach(function(child) {
+ var cbox = child.getContentBox();
+ child.setLayoutTransform(new kity.Matrix());
+
+ child.setVertexIn(new kity.Point(cbox.left, cbox.cy));
+ child.setLayoutVectorIn(new kity.Vector(1, 0));
+ });
+
+ this.align(children, 'left');
+ this.stack(children, 'y');
+
+ var xAdjust = 0;
+ xAdjust += pBox.left;
+ xAdjust += indent;
+ xAdjust += children[0].getStyle('margin-left');
+
+ var yAdjust = 0;
+
+ if (dir > 0) {
+ yAdjust += pBox.bottom;
+ yAdjust += parent.getStyle('margin-bottom');
+ yAdjust += children[0].getStyle('margin-top');
+ } else {
+ yAdjust -= this.getTreeBox(children).bottom;
+ yAdjust += pBox.top;
+ yAdjust -= parent.getStyle('margin-top');
+ yAdjust -= children[0].getStyle('margin-bottom');
+ }
+
+ this.move(children, xAdjust, yAdjust);
+
+ },
+
+ getOrderHint: function(node) {
+ var hint = [];
+ var box = node.getLayoutBox();
+ var offset = node.getLevel() > 1 ? 3 : 5;
+
+ hint.push({
+ type: 'up',
+ node: node,
+ area: new kity.Box({
+ x: box.x,
+ y: box.top - node.getStyle('margin-top') - offset,
+ width: box.width,
+ height: node.getStyle('margin-top')
+ }),
+ path: ['M', box.x, box.top - offset, 'L', box.right, box.top - offset]
+ });
+
+ hint.push({
+ type: 'down',
+ node: node,
+ area: new kity.Box({
+ x: box.x,
+ y: box.bottom + offset,
+ width: box.width,
+ height: node.getStyle('margin-bottom')
+ }),
+ path: ['M', box.x, box.bottom + offset, 'L', box.right, box.bottom + offset]
+ });
+ return hint;
+ }
+ }));
+
+ }
+
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/layout/fish-bone-master.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/layout/fish-bone-master.js
new file mode 100644
index 0000000..9948cd3
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/layout/fish-bone-master.js
@@ -0,0 +1,68 @@
+/**
+ * @fileOverview
+ *
+ * 鱼骨图主骨架布局
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+
+define(function(require, exports, module) {
+ var kity = require('../core/kity');
+ var Layout = require('../core/layout');
+
+ Layout.register('fish-bone-master', kity.createClass('FishBoneMasterLayout', {
+ base: Layout,
+
+ doLayout: function(parent, children, round) {
+
+ var upPart = [],
+ downPart = [];
+
+ var child = children[0];
+ var pBox = parent.getContentBox();
+
+ parent.setVertexOut(new kity.Point(pBox.right, pBox.cy));
+ parent.setLayoutVectorOut(new kity.Vector(1, 0));
+
+ if (!child) return;
+
+ var cBox = child.getContentBox();
+ var pMarginRight = parent.getStyle('margin-right');
+ var cMarginLeft = child.getStyle('margin-left');
+ var cMarginTop = child.getStyle('margin-top');
+ var cMarginBottom = child.getStyle('margin-bottom');
+
+ children.forEach(function(child, index) {
+ child.setLayoutTransform(new kity.Matrix());
+ var cBox = child.getContentBox();
+
+ if (index % 2) {
+ downPart.push(child);
+ child.setVertexIn(new kity.Point(cBox.left, cBox.top));
+ child.setLayoutVectorIn(new kity.Vector(1, 1));
+ }
+ else {
+ upPart.push(child);
+ child.setVertexIn(new kity.Point(cBox.left, cBox.bottom));
+ child.setLayoutVectorIn(new kity.Vector(1, -1));
+ }
+
+ });
+
+ this.stack(upPart, 'x');
+ this.stack(downPart, 'x');
+
+ this.align(upPart, 'bottom');
+ this.align(downPart, 'top');
+
+ var xAdjust = pBox.right + pMarginRight + cMarginLeft;
+ var yAdjustUp = pBox.cy - cMarginBottom - parent.getStyle('margin-top');
+ var yAdjustDown = pBox.cy + cMarginTop + parent.getStyle('margin-bottom');
+
+ this.move(upPart, xAdjust, yAdjustUp);
+ this.move(downPart, xAdjust + cMarginLeft, yAdjustDown);
+ }
+ }));
+
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/layout/fish-bone-slave.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/layout/fish-bone-slave.js
new file mode 100644
index 0000000..4234be5
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/layout/fish-bone-slave.js
@@ -0,0 +1,72 @@
+/**
+ * @fileOverview
+ *
+ *
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+
+define(function(require, exports, module) {
+ var kity = require('../core/kity');
+ var Layout = require('../core/layout');
+
+ Layout.register('fish-bone-slave', kity.createClass('FishBoneSlaveLayout', {
+ base: Layout,
+
+ doLayout: function (parent, children, round) {
+
+ var layout = this;
+ var abs = Math.abs;
+ var GOLD_CUT = 1 - 0.618;
+
+ var pBox = parent.getContentBox();
+ var vi = parent.getLayoutVectorIn();
+
+ parent.setLayoutVectorOut(vi);
+
+ var goldX = pBox.left + pBox.width * GOLD_CUT;
+ var pout = new kity.Point(goldX, vi.y > 0 ? pBox.bottom : pBox.top);
+ parent.setVertexOut(pout);
+
+ var child = children[0];
+ if (!child) return;
+
+ var cBox = child.getContentBox();
+
+ children.forEach(function(child, index) {
+ child.setLayoutTransform(new kity.Matrix());
+ child.setLayoutVectorIn(new kity.Vector(1, 0));
+ child.setVertexIn(new kity.Point(cBox.left, cBox.cy));
+ });
+
+ this.stack(children, 'y');
+ this.align(children, 'left');
+
+ var xAdjust = 0, yAdjust = 0;
+ xAdjust += pout.x;
+
+ if (parent.getLayoutVectorOut().y < 0) {
+ yAdjust -= this.getTreeBox(children).bottom;
+ yAdjust += parent.getContentBox().top;
+ yAdjust -= parent.getStyle('margin-top');
+ yAdjust -= child.getStyle('margin-bottom');
+ } else {
+ yAdjust += parent.getContentBox().bottom;
+ yAdjust += parent.getStyle('margin-bottom');
+ yAdjust += child.getStyle('margin-top');
+ }
+
+ this.move(children, xAdjust, yAdjust);
+
+ if (round == 2) {
+ children.forEach(function(child) {
+ var m = child.getLayoutTransform();
+ var cbox = child.getContentBox();
+ var pin = m.transformPoint(new kity.Point(cbox.left, 0));
+ layout.move([child], abs(pin.y - pout.y), 0);
+ });
+ }
+ }
+ }));
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/layout/mind.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/layout/mind.js
new file mode 100644
index 0000000..86ba477
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/layout/mind.js
@@ -0,0 +1,62 @@
+define(function(require, exports, module) {
+ var kity = require('../core/kity');
+ var Layout = require('../core/layout');
+ var Minder = require('../core/minder');
+
+ Layout.register('mind', kity.createClass({
+ base: Layout,
+
+ doLayout: function(node, children) {
+ var layout = this;
+ var half = Math.ceil(node.children.length / 2);
+ var right = [];
+ var left = [];
+
+ children.forEach(function(child) {
+ if (child.getIndex() < half) right.push(child);
+ else left.push(child);
+ });
+
+ var leftLayout = Minder.getLayoutInstance('left');
+ var rightLayout = Minder.getLayoutInstance('right');
+
+ leftLayout.doLayout(node, left);
+ rightLayout.doLayout(node, right);
+
+ var box = node.getContentBox();
+ node.setVertexOut(new kity.Point(box.cx, box.cy));
+ node.setLayoutVectorOut(new kity.Vector(0, 0));
+ },
+
+ getOrderHint: function(node) {
+ var hint = [];
+ var box = node.getLayoutBox();
+ var offset = 5;
+
+ hint.push({
+ type: 'up',
+ node: node,
+ area: new kity.Box({
+ x: box.x,
+ y: box.top - node.getStyle('margin-top') - offset,
+ width: box.width,
+ height: node.getStyle('margin-top')
+ }),
+ path: ['M', box.x, box.top - offset, 'L', box.right, box.top - offset]
+ });
+
+ hint.push({
+ type: 'down',
+ node: node,
+ area: new kity.Box({
+ x: box.x,
+ y: box.bottom + offset,
+ width: box.width,
+ height: node.getStyle('margin-bottom')
+ }),
+ path: ['M', box.x, box.bottom + offset, 'L', box.right, box.bottom + offset]
+ });
+ return hint;
+ }
+ }));
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/layout/tianpan.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/layout/tianpan.js
new file mode 100644
index 0000000..9ce27ac
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/layout/tianpan.js
@@ -0,0 +1,76 @@
+/**
+ * @fileOverview
+ *
+ * 天盘模板
+ *
+ * @author: along
+ * @copyright: bpd729@163.com, 2015
+ */
+define(function(require, exports, module) {
+ var kity = require('../core/kity');
+ var Layout = require('../core/layout');
+ var Minder = require('../core/minder');
+
+ Layout.register('tianpan', kity.createClass({
+ base: Layout,
+
+ doLayout: function (parent, children) {
+ if (children.length == 0) return;
+
+ var layout = this;
+ var pbox = parent.getContentBox();
+
+ var x, y,box;
+ var _theta = 5;
+ var _r = Math.max(pbox.width, 50);
+ children.forEach(function (child, index) {
+ child.setLayoutTransform(new kity.Matrix());
+ box = layout.getTreeBox(child);
+ _r = Math.max(Math.max(box.width, box.height), _r);
+ })
+ _r = _r / 1.5 / Math.PI;
+
+ children.forEach(function (child, index) {
+ x = _r * (Math.cos(_theta) + Math.sin(_theta) * _theta);
+ y = _r * (Math.sin(_theta) - Math.cos(_theta) * _theta);
+
+ _theta += (0.9 - index * 0.02);
+ child.setLayoutVectorIn(new kity.Vector(1, 0));
+ child.setVertexIn(new kity.Point(pbox.cx, pbox.cy));
+ child.setLayoutTransform(new kity.Matrix());
+ layout.move([child], x, y);
+ });
+ },
+
+ getOrderHint: function (node) {
+ var hint = [];
+ var box = node.getLayoutBox();
+ var offset = 5;
+
+ hint.push({
+ type: 'up',
+ node: node,
+ area: {
+ x: box.x,
+ y: box.top - node.getStyle('margin-top') - offset,
+ width: box.width,
+ height: node.getStyle('margin-top')
+ },
+ path: ['M', box.x, box.top - offset, 'L', box.right, box.top - offset]
+ });
+
+ hint.push({
+ type: 'down',
+ node: node,
+ area: {
+ x: box.x,
+ y: box.bottom + offset,
+ width: box.width,
+ height: node.getStyle('margin-bottom')
+ },
+ path: ['M', box.x, box.bottom + offset, 'L', box.right, box.bottom + offset]
+ });
+ return hint;
+ }
+ }));
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/arrange.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/arrange.js
new file mode 100644
index 0000000..5365e6c
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/arrange.js
@@ -0,0 +1,156 @@
+
+define(function(require, exports, module) {
+ var kity = require('../core/kity');
+ var MinderNode = require('../core/node');
+ var Command = require('../core/command');
+ var Module = require('../core/module');
+
+ kity.extendClass(MinderNode, {
+ arrange: function(index) {
+ var parent = this.parent;
+ if (!parent) return;
+ var sibling = parent.children;
+
+ if (index < 0 || index >= sibling.length) return;
+ sibling.splice(this.getIndex(), 1);
+ sibling.splice(index, 0, this);
+ return this;
+ }
+ });
+
+ function asc(nodeA, nodeB) {
+ return nodeA.getIndex() - nodeB.getIndex();
+ }
+ function desc(nodeA, nodeB) {
+ return -asc(nodeA, nodeB);
+ }
+
+ function canArrange(km) {
+ var selected = km.getSelectedNode();
+ return selected && selected.parent && selected.parent.children.length > 1;
+ }
+
+
+ /**
+ * @command ArrangeUp
+ * @description 向上调整选中节点的位置
+ * @shortcut Alt + Up
+ * @state
+ * 0: 当前选中了具有相同父亲的节点
+ * -1: 其它情况
+ */
+ var ArrangeUpCommand = kity.createClass('ArrangeUpCommand', {
+ base: Command,
+
+ execute: function(km) {
+ var nodes = km.getSelectedNodes();
+ nodes.sort(asc);
+ var lastIndexes = nodes.map(function(node) {
+ return node.getIndex();
+ });
+ nodes.forEach(function(node, index) {
+ node.arrange(lastIndexes[index] - 1);
+ });
+ km.layout(300);
+ },
+
+ queryState: function(km) {
+ var selected = km.getSelectedNode();
+ return selected ? 0 : -1;
+ }
+ });
+
+ /**
+ * @command ArrangeDown
+ * @description 向下调整选中节点的位置
+ * @shortcut Alt + Down
+ * @state
+ * 0: 当前选中了具有相同父亲的节点
+ * -1: 其它情况
+ */
+ var ArrangeDownCommand = kity.createClass('ArrangeUpCommand', {
+ base: Command,
+
+ execute: function(km) {
+ var nodes = km.getSelectedNodes();
+ nodes.sort(desc);
+ var lastIndexes = nodes.map(function(node) {
+ return node.getIndex();
+ });
+ nodes.forEach(function(node, index) {
+ node.arrange(lastIndexes[index] + 1);
+ });
+ km.layout(300);
+ },
+
+ queryState: function(km) {
+ var selected = km.getSelectedNode();
+ return selected ? 0 : -1;
+ }
+ });
+
+ /**
+ * @command Arrange
+ * @description 调整选中节点的位置
+ * @param {number} index 调整后节点的新位置
+ * @state
+ * 0: 当前选中了具有相同父亲的节点
+ * -1: 其它情况
+ */
+ var ArrangeCommand = kity.createClass('ArrangeCommand', {
+ base: Command,
+
+ execute: function(km, index) {
+ var nodes = km.getSelectedNodes().slice();
+
+ if (!nodes.length) return;
+
+ var ancestor = MinderNode.getCommonAncestor(nodes);
+
+ if (ancestor != nodes[0].parent) return;
+
+ var indexed = nodes.map(function(node) {
+ return {
+ index: node.getIndex(),
+ node: node
+ };
+ });
+
+ var asc = Math.min.apply(Math, indexed.map(function(one) { return one.index; })) >= index;
+
+ indexed.sort(function(a, b) {
+ return asc ? (b.index - a.index) : (a.index - b.index);
+ });
+
+ indexed.forEach(function(one) {
+ one.node.arrange(index);
+ });
+
+ km.layout(300);
+ },
+
+ queryState: function(km) {
+ var selected = km.getSelectedNode();
+ return selected ? 0 : -1;
+ }
+ });
+
+ Module.register('ArrangeModule', {
+ commands: {
+ 'arrangeup': ArrangeUpCommand,
+ 'arrangedown': ArrangeDownCommand,
+ 'arrange': ArrangeCommand
+ },
+ contextmenu: [{
+ command: 'arrangeup'
+ }, {
+ command: 'arrangedown'
+ }, {
+ divider: true
+ }],
+ commandShortcutKeys: {
+ 'arrangeup': 'normal::alt+Up',
+ 'arrangedown': 'normal::alt+Down'
+ }
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/basestyle.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/basestyle.js
new file mode 100644
index 0000000..e607ebe
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/basestyle.js
@@ -0,0 +1,126 @@
+
+define(function(require, exports, module) {
+ var kity = require('../core/kity');
+ var utils = require('../core/utils');
+
+ var Minder = require('../core/minder');
+ var MinderNode = require('../core/node');
+ var Command = require('../core/command');
+ var Module = require('../core/module');
+
+ var TextRenderer = require('./text');
+
+ Module.register('basestylemodule', function() {
+ var km = this;
+
+ function getNodeDataOrStyle(node, name) {
+ return node.getData(name) || node.getStyle(name);
+ }
+
+ TextRenderer.registerStyleHook(function(node, textGroup) {
+
+ var fontWeight = getNodeDataOrStyle(node,'font-weight');
+ var fontStyle = getNodeDataOrStyle(node, 'font-style');
+ var styleHash = [fontWeight, fontStyle].join('/');
+
+ textGroup.eachItem(function(index,item) {
+ item.setFont({
+ 'weight': fontWeight,
+ 'style': fontStyle
+ });
+ });
+
+ });
+ return {
+ 'commands': {
+ /**
+ * @command Bold
+ * @description 加粗选中的节点
+ * @shortcut Ctrl + B
+ * @state
+ * 0: 当前有选中的节点
+ * -1: 当前没有选中的节点
+ * 1: 当前已选中的节点已加粗
+ */
+ 'bold': kity.createClass('boldCommand', {
+ base: Command,
+
+ execute: function(km) {
+
+ var nodes = km.getSelectedNodes();
+ if (this.queryState('bold') == 1) {
+ nodes.forEach(function(n) {
+ n.setData('font-weight').render();
+ });
+ } else {
+ nodes.forEach(function(n) {
+ n.setData('font-weight', 'bold').render();
+ });
+ }
+ km.layout();
+ },
+ queryState: function() {
+ var nodes = km.getSelectedNodes(),
+ result = 0;
+ if (nodes.length === 0) {
+ return -1;
+ }
+ nodes.forEach(function(n) {
+ if (n && n.getData('font-weight')) {
+ result = 1;
+ return false;
+ }
+ });
+ return result;
+ }
+ }),
+ /**
+ * @command Italic
+ * @description 加斜选中的节点
+ * @shortcut Ctrl + I
+ * @state
+ * 0: 当前有选中的节点
+ * -1: 当前没有选中的节点
+ * 1: 当前已选中的节点已加斜
+ */
+ 'italic': kity.createClass('italicCommand', {
+ base: Command,
+
+ execute: function(km) {
+
+ var nodes = km.getSelectedNodes();
+ if (this.queryState('italic') == 1) {
+ nodes.forEach(function(n) {
+ n.setData('font-style').render();
+ });
+ } else {
+ nodes.forEach(function(n) {
+ n.setData('font-style', 'italic').render();
+ });
+ }
+
+ km.layout();
+ },
+ queryState: function() {
+ var nodes = km.getSelectedNodes(),
+ result = 0;
+ if (nodes.length === 0) {
+ return -1;
+ }
+ nodes.forEach(function(n) {
+ if (n && n.getData('font-style')) {
+ result = 1;
+ return false;
+ }
+ });
+ return result;
+ }
+ })
+ },
+ commandShortcutKeys: {
+ 'bold': 'ctrl+b', //bold
+ 'italic': 'ctrl+i' //italic
+ }
+ };
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/clipboard.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/clipboard.js
new file mode 100644
index 0000000..a702621
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/clipboard.js
@@ -0,0 +1,172 @@
+define(function(require, exports, module) {
+ var kity = require('../core/kity');
+ var utils = require('../core/utils');
+ var MinderNode = require('../core/node');
+ var Command = require('../core/command');
+ var Module = require('../core/module');
+
+ Module.register('ClipboardModule', function() {
+ var km = this,
+ _clipboardNodes = [],
+ _selectedNodes = [];
+
+ function appendChildNode(parent, child) {
+ _selectedNodes.push(child);
+ km.appendNode(child, parent);
+ child.render();
+ child.setLayoutOffset(null);
+ var children = child.children.map(function(node) {
+ return node.clone();
+ });
+
+ /*
+ * fixed bug: Modified on 2015.08.05
+ * 原因:粘贴递归 append 时没有清空原来父节点的子节点,而父节点被复制的时候,是连同子节点一起复制过来的
+ * 解决办法:增加了下面这一行代码
+ * by: @zhangbobell zhangbobell@163.com
+ */
+ child.clearChildren();
+
+ for (var i = 0, ci;
+ (ci = children[i]); i++) {
+ appendChildNode(child, ci);
+ }
+ }
+
+ function sendToClipboard(nodes) {
+ if (!nodes.length) return;
+ nodes.sort(function(a, b) {
+ return a.getIndex() - b.getIndex();
+ });
+ _clipboardNodes = nodes.map(function(node) {
+ return node.clone();
+ });
+ }
+
+ /**
+ * @command Copy
+ * @description 复制当前选中的节点
+ * @shortcut Ctrl + C
+ * @state
+ * 0: 当前有选中的节点
+ * -1: 当前没有选中的节点
+ */
+ var CopyCommand = kity.createClass('CopyCommand', {
+ base: Command,
+
+ execute: function(km) {
+ sendToClipboard(km.getSelectedAncestors(true));
+ this.setContentChanged(false);
+ }
+ });
+
+ /**
+ * @command Cut
+ * @description 剪切当前选中的节点
+ * @shortcut Ctrl + X
+ * @state
+ * 0: 当前有选中的节点
+ * -1: 当前没有选中的节点
+ */
+ var CutCommand = kity.createClass('CutCommand', {
+ base: Command,
+
+ execute: function(km) {
+ var ancestors = km.getSelectedAncestors();
+
+ if (ancestors.length === 0) return;
+
+ sendToClipboard(ancestors);
+
+ km.select(MinderNode.getCommonAncestor(ancestors), true);
+
+ ancestors.slice().forEach(function(node) {
+ km.removeNode(node);
+ });
+
+ km.layout(300);
+ }
+ });
+
+ /**
+ * @command Paste
+ * @description 粘贴已复制的节点到每一个当前选中的节点上
+ * @shortcut Ctrl + V
+ * @state
+ * 0: 当前有选中的节点
+ * -1: 当前没有选中的节点
+ */
+ var PasteCommand = kity.createClass('PasteCommand', {
+ base: Command,
+
+ execute: function(km) {
+ if (_clipboardNodes.length) {
+ var nodes = km.getSelectedNodes();
+ if (!nodes.length) return;
+
+ for (var i = 0, ni; ni = _clipboardNodes[i]; i++) {
+ for (var j = 0, node; node = nodes[j]; j++) {
+ appendChildNode(node, ni.clone());
+ }
+ }
+
+ km.select(_selectedNodes, true);
+ _selectedNodes = [];
+
+ km.layout(300);
+ }
+ },
+
+ queryState: function(km) {
+ return km.getSelectedNode() ? 0 : -1;
+ }
+ });
+
+ /**
+ * @Desc: 若支持原生clipboadr事件则基于原生扩展,否则使用km的基础事件只处理节点的粘贴复制
+ * @Editor: Naixor
+ * @Date: 2015.9.20
+ */
+ if (km.supportClipboardEvent && !kity.Browser.gecko) {
+ var Copy = function (e) {
+ this.fire('beforeCopy', e);
+ }
+
+ var Cut = function (e) {
+ this.fire('beforeCut', e);
+ }
+
+ var Paste = function (e) {
+ this.fire('beforePaste', e);
+ }
+
+ return {
+ 'commands': {
+ 'copy': CopyCommand,
+ 'cut': CutCommand,
+ 'paste': PasteCommand
+ },
+ 'clipBoardEvents': {
+ 'copy': Copy.bind(km),
+ 'cut': Cut.bind(km),
+ 'paste': Paste.bind(km)
+ },
+ sendToClipboard: sendToClipboard
+ };
+ } else {
+ return {
+ 'commands': {
+ 'copy': CopyCommand,
+ 'cut': CutCommand,
+ 'paste': PasteCommand
+ },
+ 'commandShortcutKeys': {
+ 'copy': 'normal::ctrl+c|',
+ 'cut': 'normal::ctrl+x',
+ 'paste': 'normal::ctrl+v'
+ },
+ sendToClipboard: sendToClipboard
+ };
+ }
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/dragtree.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/dragtree.js
new file mode 100644
index 0000000..dba04be
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/dragtree.js
@@ -0,0 +1,405 @@
+define(function(require, exports, module) {
+ var kity = require('../core/kity');
+ var utils = require('../core/utils');
+ var MinderNode = require('../core/node');
+ var Command = require('../core/command');
+ var Module = require('../core/module');
+
+ // 矩形的变形动画定义
+ var MoveToParentCommand = kity.createClass('MoveToParentCommand', {
+ base: Command,
+ execute: function(minder, nodes, parent) {
+ var node;
+ for (var i = 0; i < nodes.length; i++) {
+ node = nodes[i];
+ if (node.parent) {
+ node.parent.removeChild(node);
+ parent.appendChild(node);
+ node.render();
+ }
+ }
+ parent.expand();
+ minder.select(nodes, true);
+ }
+ });
+
+ var DropHinter = kity.createClass('DropHinter', {
+ base: kity.Group,
+
+ constructor: function() {
+ this.callBase();
+ this.rect = new kity.Rect();
+ this.addShape(this.rect);
+ },
+
+ render: function(target) {
+ this.setVisible(!!target);
+ if (target) {
+ this.rect
+ .setBox(target.getLayoutBox())
+ .setRadius(target.getStyle('radius') || 0)
+ .stroke(
+ target.getStyle('drop-hint-color') || 'yellow',
+ target.getStyle('drop-hint-width') || 2
+ );
+ this.bringTop();
+ }
+ }
+ });
+
+ var OrderHinter = kity.createClass('OrderHinter', {
+ base: kity.Group,
+
+ constructor: function() {
+ this.callBase();
+ this.area = new kity.Rect();
+ this.path = new kity.Path();
+ this.addShapes([this.area, this.path]);
+ },
+
+ render: function(hint) {
+ this.setVisible(!!hint);
+ if (hint) {
+ this.area.setBox(hint.area);
+ this.area.fill(hint.node.getStyle('order-hint-area-color') || 'rgba(0, 255, 0, .5)');
+ this.path.setPathData(hint.path);
+ this.path.stroke(
+ hint.node.getStyle('order-hint-path-color') || '#0f0',
+ hint.node.getStyle('order-hint-path-width') || 1);
+ }
+ }
+ });
+
+ // 对拖动对象的一个替代盒子,控制整个拖放的逻辑,包括:
+ // 1. 从节点列表计算出拖动部分
+ // 2. 计算可以 drop 的节点,产生 drop 交互提示
+ var TreeDragger = kity.createClass('TreeDragger', {
+
+ constructor: function(minder) {
+ this._minder = minder;
+ this._dropHinter = new DropHinter();
+ this._orderHinter = new OrderHinter();
+ minder.getRenderContainer().addShapes([this._dropHinter, this._orderHinter]);
+ },
+
+ dragStart: function(position) {
+ // 只记录开始位置,不马上开启拖放模式
+ // 这个位置同时是拖放范围收缩时的焦点位置(中心)
+ this._startPosition = position;
+ },
+
+ dragMove: function(position) {
+ // 启动拖放模式需要最小的移动距离
+ var DRAG_MOVE_THRESHOLD = 10;
+
+ if (!this._startPosition) return;
+
+ var movement = kity.Vector.fromPoints(this._dragPosition || this._startPosition, position);
+ var minder = this._minder;
+
+ this._dragPosition = position;
+
+ if (!this._dragMode) {
+ // 判断拖放模式是否该启动
+ if (kity.Vector.fromPoints(this._dragPosition, this._startPosition).length() < DRAG_MOVE_THRESHOLD) {
+ return;
+ }
+ if (!this._enterDragMode()) {
+ return;
+ }
+ }
+
+ for (var i = 0; i < this._dragSources.length; i++) {
+ this._dragSources[i].setLayoutOffset(this._dragSources[i].getLayoutOffset().offset(movement));
+ minder.applyLayoutResult(this._dragSources[i]);
+ }
+
+ if (!this._dropTest()) {
+ this._orderTest();
+ } else {
+ this._renderOrderHint(this._orderSucceedHint = null);
+ }
+ },
+
+ dragEnd: function() {
+ this._startPosition = null;
+ this._dragPosition = null;
+
+ if (!this._dragMode) {
+ return;
+ }
+
+ this._fadeDragSources(1);
+
+ if (this._dropSucceedTarget) {
+
+ this._dragSources.forEach(function(source) {
+ source.setLayoutOffset(null);
+ });
+
+ this._minder.layout(-1);
+
+ this._minder.execCommand('movetoparent', this._dragSources, this._dropSucceedTarget);
+
+ } else if (this._orderSucceedHint) {
+
+ var hint = this._orderSucceedHint;
+ var index = hint.node.getIndex();
+
+ var sourceIndexes = this._dragSources.map(function(source) {
+ // 顺便干掉布局偏移
+ source.setLayoutOffset(null);
+ return source.getIndex();
+ });
+
+ var maxIndex = Math.max.apply(Math, sourceIndexes);
+ var minIndex = Math.min.apply(Math, sourceIndexes);
+
+ if (index < minIndex && hint.type == 'down') index++;
+ if (index > maxIndex && hint.type == 'up') index--;
+
+ hint.node.setLayoutOffset(null);
+
+ this._minder.execCommand('arrange', index);
+ this._renderOrderHint(null);
+ } else {
+ this._minder.fire('savescene');
+ }
+ this._minder.layout(300);
+ this._leaveDragMode();
+ this._minder.fire('contentchange');
+ },
+
+ // 进入拖放模式:
+ // 1. 计算拖放源和允许的拖放目标
+ // 2. 标记已启动
+ _enterDragMode: function() {
+ this._calcDragSources();
+ if (!this._dragSources.length) {
+ this._startPosition = null;
+ return false;
+ }
+ this._fadeDragSources(0.5);
+ this._calcDropTargets();
+ this._calcOrderHints();
+ this._dragMode = true;
+ this._minder.setStatus('dragtree');
+ return true;
+ },
+
+ // 从选中的节点计算拖放源
+ // 并不是所有选中的节点都作为拖放源,如果选中节点中存在 A 和 B,
+ // 并且 A 是 B 的祖先,则 B 不作为拖放源
+ //
+ // 计算过程:
+ // 1. 将节点按照树高排序,排序后只可能是前面节点是后面节点的祖先
+ // 2. 从后往前枚举排序的结果,如果发现枚举目标之前存在其祖先,
+ // 则排除枚举目标作为拖放源,否则加入拖放源
+ _calcDragSources: function() {
+ this._dragSources = this._minder.getSelectedAncestors();
+ },
+
+ _fadeDragSources: function(opacity) {
+ var minder = this._minder;
+ this._dragSources.forEach(function(source) {
+ source.getRenderContainer().setOpacity(opacity, 200);
+ source.traverse(function(node) {
+ if (opacity < 1) {
+ minder.detachNode(node);
+ } else {
+ minder.attachNode(node);
+ }
+ }, true);
+ });
+ },
+
+
+ // 计算拖放目标可以释放的节点列表(释放意味着成为其子树),存在这条限制规则:
+ // - 不能拖放到拖放目标的子树上(允许拖放到自身,因为多选的情况下可以把其它节点加入)
+ //
+ // 1. 加入当前节点(初始为根节点)到允许列表
+ // 2. 对于当前节点的每一个子节点:
+ // (1) 如果是拖放目标的其中一个节点,忽略(整棵子树被剪枝)
+ // (2) 如果不是拖放目标之一,以当前子节点为当前节点,回到 1 计算
+ // 3. 返回允许列表
+ //
+ _calcDropTargets: function() {
+
+ function findAvailableParents(nodes, root) {
+ var availables = [],
+ i;
+ availables.push(root);
+ root.getChildren().forEach(function(test) {
+ for (i = 0; i < nodes.length; i++) {
+ if (nodes[i] == test) return;
+ }
+ availables = availables.concat(findAvailableParents(nodes, test));
+ });
+ return availables;
+ }
+
+ this._dropTargets = findAvailableParents(this._dragSources, this._minder.getRoot());
+ this._dropTargetBoxes = this._dropTargets.map(function(source) {
+ return source.getLayoutBox();
+ });
+ },
+
+ _calcOrderHints: function() {
+ var sources = this._dragSources;
+ var ancestor = MinderNode.getCommonAncestor(sources);
+
+ // 只有一个元素选中,公共祖先是其父
+ if (ancestor == sources[0]) ancestor = sources[0].parent;
+
+ if (sources.length === 0 || ancestor != sources[0].parent) {
+ this._orderHints = [];
+ return;
+ }
+
+ var siblings = ancestor.children;
+
+ this._orderHints = siblings.reduce(function(hint, sibling) {
+ if (sources.indexOf(sibling) == -1) {
+ hint = hint.concat(sibling.getOrderHint());
+ }
+ return hint;
+ }, []);
+ },
+
+ _leaveDragMode: function() {
+ this._dragMode = false;
+ this._dropSucceedTarget = null;
+ this._orderSucceedHint = null;
+ this._renderDropHint(null);
+ this._renderOrderHint(null);
+ this._minder.rollbackStatus();
+ },
+
+ _drawForDragMode: function() {
+ this._text.setContent(this._dragSources.length + ' items');
+ this._text.setPosition(this._startPosition.x, this._startPosition.y + 5);
+ this._minder.getRenderContainer().addShape(this);
+ },
+
+ /**
+ * 通过 judge 函数判断 targetBox 和 sourceBox 的位置交叉关系
+ * @param targets -- 目标节点
+ * @param targetBoxMapper -- 目标节点与对应 Box 的映射关系
+ * @param judge -- 判断函数
+ * @returns {*}
+ * @private
+ */
+ _boxTest: function(targets, targetBoxMapper, judge) {
+ var sourceBoxes = this._dragSources.map(function(source) {
+ return source.getLayoutBox();
+ });
+
+ var i, j, target, sourceBox, targetBox;
+
+ judge = judge || function(intersectBox, sourceBox, targetBox) {
+ return intersectBox && !intersectBox.isEmpty();
+ };
+
+ for (i = 0; i < targets.length; i++) {
+
+ target = targets[i];
+ targetBox = targetBoxMapper.call(this, target, i);
+
+ for (j = 0; j < sourceBoxes.length; j++) {
+ sourceBox = sourceBoxes[j];
+
+ var intersectBox = sourceBox.intersect(targetBox);
+ if (judge(intersectBox, sourceBox, targetBox)) {
+ return target;
+ }
+ }
+ }
+
+ return null;
+ },
+
+ _dropTest: function() {
+ this._dropSucceedTarget = this._boxTest(this._dropTargets, function(target, i) {
+ return this._dropTargetBoxes[i];
+ }, function(intersectBox, sourceBox, targetBox) {
+ function area(box) {
+ return box.width * box.height;
+ }
+ if (!intersectBox) return false;
+ /*
+ * Added by zhangbobell, 2015.9.8
+ *
+ * 增加了下面一行判断,修复了循环比较中 targetBox 为折叠节点时,intersetBox 面积为 0,
+ * 而 targetBox 的 width 和 height 均为 0
+ * 此时造成了满足以下的第二个条件而返回 true
+ * */
+ if (!area(intersectBox)) return false;
+ // 面积判断,交叉面积大于其中的一半
+ if (area(intersectBox) > 0.5 * Math.min(area(sourceBox), area(targetBox))) return true;
+ // 有一个边完全重合的情况,也认为两个是交叉的
+ if (intersectBox.width + 1 >= Math.min(sourceBox.width, targetBox.width)) return true;
+ if (intersectBox.height + 1 >= Math.min(sourceBox.height, targetBox.height)) return true;
+ return false;
+ });
+ this._renderDropHint(this._dropSucceedTarget);
+ return !!this._dropSucceedTarget;
+ },
+
+ _orderTest: function() {
+ this._orderSucceedHint = this._boxTest(this._orderHints, function(hint) {
+ return hint.area;
+ });
+ this._renderOrderHint(this._orderSucceedHint);
+ return !!this._orderSucceedHint;
+ },
+
+ _renderDropHint: function(target) {
+ this._dropHinter.render(target);
+ },
+
+ _renderOrderHint: function(hint) {
+ this._orderHinter.render(hint);
+ },
+ preventDragMove: function() {
+ this._startPosition = null;
+ }
+ });
+
+ Module.register('DragTree', function() {
+ var dragger;
+
+ return {
+ init: function() {
+ dragger = new TreeDragger(this);
+ window.addEventListener('mouseup', function() {
+ dragger.dragEnd();
+ });
+ },
+ events: {
+ 'normal.mousedown inputready.mousedown': function(e) {
+ // 单选中根节点也不触发拖拽
+ if (e.originEvent.button) return;
+ if (e.getTargetNode() && e.getTargetNode() != this.getRoot()) {
+ dragger.dragStart(e.getPosition());
+ }
+ },
+ 'normal.mousemove dragtree.mousemove': function(e) {
+ dragger.dragMove(e.getPosition());
+ },
+ 'normal.mouseup dragtree.beforemouseup': function(e) {
+ dragger.dragEnd();
+ //e.stopPropagation();
+ e.preventDefault();
+ },
+ 'statuschange': function(e) {
+ if (e.lastStatus == 'textedit' && e.currentStatus == 'normal') {
+ dragger.preventDragMove();
+ }
+ }
+ },
+ commands: {
+ 'movetoparent': MoveToParentCommand
+ }
+ };
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/expand.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/expand.js
new file mode 100644
index 0000000..9b2f838
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/expand.js
@@ -0,0 +1,293 @@
+define(function(require, exports, module) {
+ var kity = require('../core/kity');
+ var utils = require('../core/utils');
+ var keymap = require('../core/keymap');
+ var MinderNode = require('../core/node');
+ var Command = require('../core/command');
+ var Module = require('../core/module');
+ var Renderer = require('../core/render');
+
+ Module.register('Expand', function() {
+ var minder = this;
+ var EXPAND_STATE_DATA = 'expandState',
+ STATE_EXPAND = 'expand',
+ STATE_COLLAPSE = 'collapse';
+
+ // 将展开的操作和状态读取接口拓展到 MinderNode 上
+ kity.extendClass(MinderNode, {
+
+ /**
+ * 展开节点
+ * @param {Policy} policy 展开的策略,默认为 KEEP_STATE
+ */
+ expand: function() {
+ this.setData(EXPAND_STATE_DATA, STATE_EXPAND);
+ return this;
+ },
+
+ /**
+ * 收起节点
+ */
+ collapse: function() {
+ this.setData(EXPAND_STATE_DATA, STATE_COLLAPSE);
+ return this;
+ },
+
+ /**
+ * 判断节点当前的状态是否为展开
+ */
+ isExpanded: function() {
+ var expanded = this.getData(EXPAND_STATE_DATA) !== STATE_COLLAPSE;
+ return expanded && (this.isRoot() || this.parent.isExpanded());
+ },
+
+ /**
+ * 判断节点当前的状态是否为收起
+ */
+ isCollapsed: function() {
+ return !this.isExpanded();
+ }
+ });
+
+ /**
+ * @command Expand
+ * @description 展开当前选中的节点,保证其可见
+ * @param {bool} justParents 是否只展开到父亲
+ * * `false` - (默认)保证选中的节点以及其子树可见
+ * * `true` - 只保证选中的节点可见,不展开其子树
+ * @state
+ * 0: 当前有选中的节点
+ * -1: 当前没有选中的节点
+ */
+ var ExpandCommand = kity.createClass('ExpandCommand', {
+ base: Command,
+
+ execute: function(km, justParents) {
+ var node = km.getSelectedNode();
+ if (!node) return;
+ if (justParents) {
+ node = node.parent;
+ }
+ while (node.parent) {
+ node.expand();
+ node = node.parent;
+ }
+ node.renderTree();
+ km.layout(100);
+ },
+
+ queryState: function(km) {
+ var node = km.getSelectedNode();
+ return node && !node.isRoot() && !node.isExpanded() ? 0 : -1;
+ }
+ });
+
+ /**
+ * @command ExpandToLevel
+ * @description 展开脑图到指定的层级
+ * @param {number} level 指定展开到的层级,最少值为 1。
+ * @state
+ * 0: 一直可用
+ */
+ var ExpandToLevelCommand = kity.createClass('ExpandToLevelCommand', {
+ base: Command,
+ execute: function(km, level) {
+ km.getRoot().traverse(function(node) {
+ if (node.getLevel() < level) node.expand();
+ if (node.getLevel() == level && !node.isLeaf()) node.collapse();
+ });
+ km.refresh(100);
+ },
+ enableReadOnly: true
+ });
+
+ /**
+ * @command Collapse
+ * @description 收起当前节点的子树
+ * @state
+ * 0: 当前有选中的节点
+ * -1: 当前没有选中的节点
+ */
+ var CollapseCommand = kity.createClass('CollapseCommand', {
+ base: Command,
+
+ execute: function(km) {
+ var node = km.getSelectedNode();
+ if (!node) return;
+
+ node.collapse();
+ node.renderTree();
+ km.layout();
+ },
+
+ queryState: function(km) {
+ var node = km.getSelectedNode();
+ return node && !node.isRoot() && node.isExpanded() ? 0 : -1;
+ }
+ });
+
+ var Expander = kity.createClass('Expander', {
+ base: kity.Group,
+
+ constructor: function(node) {
+ this.callBase();
+ this.radius = 6;
+ this.outline = new kity.Circle(this.radius).stroke('gray').fill('white');
+ this.sign = new kity.Path().stroke('gray');
+ this.addShapes([this.outline, this.sign]);
+ this.initEvent(node);
+ this.setId(utils.uuid('node_expander'));
+ this.setStyle('cursor', 'pointer');
+ },
+
+ initEvent: function(node) {
+ this.on('mousedown', function(e) {
+ minder.select([node], true);
+ if (node.isExpanded()) {
+ node.collapse();
+ } else {
+ node.expand();
+ }
+ node.renderTree().getMinder().layout(100);
+ node.getMinder().fire('contentchange');
+ e.stopPropagation();
+ e.preventDefault();
+ });
+ this.on('dblclick click mouseup', function(e) {
+ e.stopPropagation();
+ e.preventDefault();
+ });
+ },
+
+ setState: function(state) {
+ if (state == 'hide') {
+ this.setVisible(false);
+ return;
+ }
+ this.setVisible(true);
+ var pathData = ['M', 1.5 - this.radius, 0, 'L', this.radius - 1.5, 0];
+ if (state == STATE_COLLAPSE) {
+ pathData.push(['M', 0, 1.5 - this.radius, 'L', 0, this.radius - 1.5]);
+ }
+ this.sign.setPathData(pathData);
+ }
+ });
+
+ var ExpanderRenderer = kity.createClass('ExpanderRenderer', {
+ base: Renderer,
+
+ create: function(node) {
+ if (node.isRoot()) return;
+ this.expander = new Expander(node);
+ node.getRenderContainer().prependShape(this.expander);
+ node.expanderRenderer = this;
+ this.node = node;
+ return this.expander;
+ },
+
+ shouldRender: function(node) {
+ return !node.isRoot();
+ },
+
+ update: function(expander, node, box) {
+ if (!node.parent) return;
+
+ var visible = node.parent.isExpanded();
+
+ expander.setState(visible && node.children.length ? node.getData(EXPAND_STATE_DATA) : 'hide');
+
+ var vector = node.getLayoutVectorIn().normalize(expander.radius + node.getStyle('stroke-width'));
+ var position = node.getVertexIn().offset(vector.reverse());
+
+ this.expander.setTranslate(position);
+ }
+ });
+
+ return {
+ commands: {
+ 'expand': ExpandCommand,
+ 'expandtolevel': ExpandToLevelCommand,
+ 'collapse': CollapseCommand
+ },
+ events: {
+ 'layoutapply': function(e) {
+ var r = e.node.getRenderer('ExpanderRenderer');
+ if (r.getRenderShape()) {
+ r.update(r.getRenderShape(), e.node);
+ }
+ },
+ 'beforerender': function(e) {
+ var node = e.node;
+ var visible = !node.parent || node.parent.isExpanded();
+ var minder = this;
+
+ node.getRenderContainer().setVisible(visible);
+ if (!visible) e.stopPropagation();
+ },
+ 'normal.keydown': function(e) {
+ if (this.getStatus() == 'textedit') return;
+ if (e.originEvent.keyCode == keymap['/']) {
+ var node = this.getSelectedNode();
+ if (!node || node == this.getRoot()) return;
+ var expanded = node.isExpanded();
+ this.getSelectedNodes().forEach(function(node) {
+ if (expanded) node.collapse();
+ else node.expand();
+ node.renderTree();
+ });
+ this.layout(100);
+ this.fire('contentchange');
+ e.preventDefault();
+ e.stopPropagationImmediately();
+ }
+ if (e.isShortcutKey('Alt+`')) {
+ this.execCommand('expandtolevel', 9999);
+ }
+ for (var i = 1; i < 6; i++) {
+ if (e.isShortcutKey('Alt+' + i)) {
+ this.execCommand('expandtolevel', i);
+ }
+ }
+ }
+ },
+ renderers: {
+ outside: ExpanderRenderer
+ },
+ contextmenu: [{
+ command: 'expandtoleaf',
+ query: function() {
+ return !minder.getSelectedNode();
+ },
+ fn: function(minder) {
+ minder.execCommand('expandtolevel', 9999);
+ }
+ }, {
+ command: 'expandtolevel1',
+ query: function() {
+ return !minder.getSelectedNode();
+ },
+ fn: function(minder) {
+ minder.execCommand('expandtolevel', 1);
+ }
+ }, {
+ command: 'expandtolevel2',
+ query: function() {
+ return !minder.getSelectedNode();
+ },
+ fn: function(minder) {
+ minder.execCommand('expandtolevel', 2);
+ }
+ },{
+ command: 'expandtolevel3',
+ query: function() {
+ return !minder.getSelectedNode();
+ },
+ fn: function(minder) {
+ minder.execCommand('expandtolevel', 3);
+ }
+ }, {
+ divider: true
+ }]
+ };
+ });
+});
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/font.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/font.js
new file mode 100644
index 0000000..11ffd7b
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/font.js
@@ -0,0 +1,159 @@
+define(function(require, exports, module) {
+ var kity = require('../core/kity');
+ var utils = require('../core/utils');
+
+ var Minder = require('../core/minder');
+ var MinderNode = require('../core/node');
+ var Command = require('../core/command');
+ var Module = require('../core/module');
+
+ var TextRenderer = require('./text');
+
+ function getNodeDataOrStyle(node, name) {
+ return node.getData(name) || node.getStyle(name);
+ }
+
+ TextRenderer.registerStyleHook(function(node, textGroup) {
+ var dataColor = node.getData('color');
+ var selectedColor = node.getStyle('selected-color');
+ var styleColor = node.getStyle('color');
+
+ var foreColor = dataColor || (node.isSelected() && selectedColor ? selectedColor : styleColor);
+ var fontFamily = getNodeDataOrStyle(node, 'font-family');
+ var fontSize = getNodeDataOrStyle(node, 'font-size');
+
+ textGroup.fill(foreColor);
+
+ textGroup.eachItem(function(index, item) {
+ item.setFont({
+ 'family': fontFamily,
+ 'size': fontSize
+ });
+ });
+ });
+
+
+ Module.register('fontmodule', {
+ 'commands': {
+ /**
+ * @command ForeColor
+ * @description 设置选中节点的字体颜色
+ * @param {string} color 表示颜色的字符串
+ * @state
+ * 0: 当前有选中的节点
+ * -1: 当前没有选中的节点
+ * @return 如果只有一个节点选中,返回已选中节点的字体颜色;否则返回 'mixed'。
+ */
+ 'forecolor': kity.createClass('fontcolorCommand', {
+ base: Command,
+ execute: function(km, color) {
+ var nodes = km.getSelectedNodes();
+ nodes.forEach(function(n) {
+ n.setData('color', color);
+ n.render();
+ });
+ },
+ queryState: function(km) {
+ return km.getSelectedNodes().length === 0 ? -1 : 0;
+ },
+ queryValue: function(km) {
+ if (km.getSelectedNodes().length == 1) {
+ return km.getSelectedNodes()[0].getData('color');
+ }
+ return 'mixed';
+ }
+ }),
+
+ /**
+ * @command Background
+ * @description 设置选中节点的背景颜色
+ * @param {string} color 表示颜色的字符串
+ * @state
+ * 0: 当前有选中的节点
+ * -1: 当前没有选中的节点
+ * @return 如果只有一个节点选中,返回已选中节点的背景颜色;否则返回 'mixed'。
+ */
+ 'background': kity.createClass('backgroudCommand', {
+ base: Command,
+
+ execute: function(km, color) {
+ var nodes = km.getSelectedNodes();
+ nodes.forEach(function(n) {
+ n.setData('background', color);
+ n.render();
+ });
+ },
+ queryState: function(km) {
+ return km.getSelectedNodes().length === 0 ? -1 : 0;
+ },
+ queryValue: function(km) {
+ if (km.getSelectedNodes().length == 1) {
+ return km.getSelectedNodes()[0].getData('background');
+ }
+ return 'mixed';
+ }
+ }),
+
+
+ /**
+ * @command FontFamily
+ * @description 设置选中节点的字体
+ * @param {string} family 表示字体的字符串
+ * @state
+ * 0: 当前有选中的节点
+ * -1: 当前没有选中的节点
+ * @return 返回首个选中节点的字体
+ */
+ 'fontfamily': kity.createClass('fontfamilyCommand', {
+ base: Command,
+
+ execute: function(km, family) {
+ var nodes = km.getSelectedNodes();
+ nodes.forEach(function(n) {
+ n.setData('font-family', family);
+ n.render();
+ km.layout();
+ });
+ },
+ queryState: function(km) {
+ return km.getSelectedNodes().length === 0 ? -1 : 0;
+ },
+ queryValue: function(km) {
+ var node = km.getSelectedNode();
+ if (node) return node.getData('font-family');
+ return null;
+ }
+ }),
+
+ /**
+ * @command FontSize
+ * @description 设置选中节点的字体大小
+ * @param {number} size 字体大小(px)
+ * @state
+ * 0: 当前有选中的节点
+ * -1: 当前没有选中的节点
+ * @return 返回首个选中节点的字体大小
+ */
+ 'fontsize': kity.createClass('fontsizeCommand', {
+ base: Command,
+
+ execute: function(km, size) {
+ var nodes = km.getSelectedNodes();
+ nodes.forEach(function(n) {
+ n.setData('font-size', size);
+ n.render();
+ km.layout(300);
+ });
+ },
+ queryState: function(km) {
+ return km.getSelectedNodes().length === 0 ? -1 : 0;
+ },
+ queryValue: function(km) {
+ var node = km.getSelectedNode();
+ if (node) return node.getData('font-size');
+ return null;
+ }
+ })
+ }
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/hyperlink.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/hyperlink.js
new file mode 100644
index 0000000..a26c5a5
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/hyperlink.js
@@ -0,0 +1,127 @@
+define(function(require, exports, module) {
+ var kity = require('../core/kity');
+ var utils = require('../core/utils');
+
+ var Minder = require('../core/minder');
+ var MinderNode = require('../core/node');
+ var Command = require('../core/command');
+ var Module = require('../core/module');
+ var Renderer = require('../core/render');
+
+ // jscs:disable maximumLineLength
+ var linkShapePath = 'M16.614,10.224h-1.278c-1.668,0-3.07-1.07-3.599-2.556h4.877c0.707,0,1.278-0.571,1.278-1.278V3.834 c0-0.707-0.571-1.278-1.278-1.278h-4.877C12.266,1.071,13.668,0,15.336,0h1.278c2.116,0,3.834,1.716,3.834,3.834V6.39 C20.448,8.508,18.73,10.224,16.614,10.224z M5.112,5.112c0-0.707,0.573-1.278,1.278-1.278h7.668c0.707,0,1.278,0.571,1.278,1.278 S14.765,6.39,14.058,6.39H6.39C5.685,6.39,5.112,5.819,5.112,5.112z M2.556,3.834V6.39c0,0.707,0.573,1.278,1.278,1.278h4.877 c-0.528,1.486-1.932,2.556-3.599,2.556H3.834C1.716,10.224,0,8.508,0,6.39V3.834C0,1.716,1.716,0,3.834,0h1.278 c1.667,0,3.071,1.071,3.599,2.556H3.834C3.129,2.556,2.556,3.127,2.556,3.834z';
+
+ Module.register('hyperlink',{
+ 'commands': {
+
+ /**
+ * @command HyperLink
+ * @description 为选中的节点添加超链接
+ * @param {string} url 超链接的 URL,设置为 null 移除
+ * @param {string} title 超链接的说明
+ * @state
+ * 0: 当前有选中的节点
+ * -1: 当前没有选中的节点
+ * @return 返回首个选中节点的超链接信息,JSON 对象: `{url: url, title: title}`
+ */
+ 'hyperlink': kity.createClass('hyperlink', {
+ base: Command,
+
+ execute: function(km, url, title) {
+ var nodes = km.getSelectedNodes();
+ nodes.forEach(function(n) {
+ n.setData('hyperlink', url);
+ n.setData('hyperlinkTitle', url && title);
+ n.render();
+ });
+ km.layout();
+ },
+ queryState: function(km) {
+ var nodes = km.getSelectedNodes(),
+ result = 0;
+ if (nodes.length === 0) {
+ return -1;
+ }
+ nodes.forEach(function(n) {
+ if (n && n.getData('hyperlink')) {
+ result = 0;
+ return false;
+ }
+ });
+ return result;
+ },
+ queryValue: function(km) {
+ var node = km.getSelectedNode();
+ return {
+ url: node.getData('hyperlink'),
+ title: node.getData('hyperlinkTitle')
+ };
+ }
+ })
+ },
+ 'renderers': {
+ right: kity.createClass('hyperlinkrender', {
+ base: Renderer,
+
+ create: function() {
+
+ var link = new kity.HyperLink();
+ var linkshape = new kity.Path();
+ var outline = new kity.Rect(24, 22, -2, -6, 4).fill('rgba(255, 255, 255, 0)');
+
+
+ linkshape.setPathData(linkShapePath).fill('#666');
+ link.addShape(outline);
+ link.addShape(linkshape);
+ link.setTarget('_blank');
+ link.setStyle('cursor', 'pointer');
+
+ link.on('mouseover', function() {
+ outline.fill('rgba(255, 255, 200, .8)');
+ }).on('mouseout', function() {
+ outline.fill('rgba(255, 255, 255, 0)');
+ });
+ return link;
+ },
+
+ shouldRender: function(node) {
+ return node.getData('hyperlink');
+ },
+
+ update: function(link, node, box) {
+
+ var href = node.getData('hyperlink');
+ link.setHref('#');
+
+ var allowed = ['^http:', '^https:', '^ftp:', '^mailto:'];
+ for (var i = 0; i < allowed.length; i++) {
+ var regex = new RegExp(allowed[i]);
+ if (regex.test(href)) {
+ link.setHref(href);
+ break;
+ }
+ }
+ var title = node.getData('hyperlinkTitle');
+
+ if (title) {
+ title = [title, '(', href, ')'].join('');
+ } else {
+ title = href;
+ }
+
+ link.node.setAttributeNS('http://www.w3.org/1999/xlink', 'title', title);
+
+ var spaceRight = node.getStyle('space-right');
+
+ link.setTranslate(box.right + spaceRight + 2, -5);
+ return new kity.Box({
+ x: box.right + spaceRight,
+ y: -11,
+ width: 24,
+ height: 22
+ });
+ }
+ })
+ }
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/image-viewer.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/image-viewer.js
new file mode 100644
index 0000000..5ff3a6d
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/image-viewer.js
@@ -0,0 +1,111 @@
+define(function(require, exports, module) {
+
+ var kity = require('../core/kity');
+ var keymap = require('../core/keymap');
+
+ var Module = require('../core/module');
+ var Command = require('../core/command');
+
+ Module.register('ImageViewer', function() {
+
+ function createEl(name, classNames, children) {
+ var el = document.createElement(name);
+ addClass(el, classNames);
+ children && children.length && children.forEach(function (child) {
+ el.appendChild(child);
+ });
+ return el;
+ }
+
+ function on(el, event, handler) {
+ el.addEventListener(event, handler);
+ }
+
+ function addClass(el, classNames) {
+ classNames && classNames.split(' ').forEach(function (className) {
+ el.classList.add(className);
+ });
+ }
+
+ function removeClass(el, classNames) {
+ classNames && classNames.split(' ').forEach(function (className) {
+ el.classList.remove(className);
+ });
+ }
+
+ var ImageViewer = kity.createClass('ImageViewer', {
+ constructor: function () {
+ var btnClose = createEl('button', 'km-image-viewer-btn km-image-viewer-close');
+ var btnSource = createEl('button', 'km-image-viewer-btn km-image-viewer-source');
+ var image = this.image = createEl('img');
+ var toolbar = this.toolbar = createEl('div', 'km-image-viewer-toolbar', [btnSource, btnClose]);
+ var container = createEl('div', 'km-image-viewer-container', [image]);
+ var viewer = this.viewer = createEl('div', 'km-image-viewer', [toolbar, container]);
+ this.hotkeyHandler = this.hotkeyHandler.bind(this)
+ on(btnClose, 'click', this.close.bind(this));
+ on(btnSource, 'click', this.viewSource.bind(this));
+ on(image, 'click', this.zoomImage.bind(this));
+ on(viewer, 'contextmenu', this.toggleToolbar.bind(this));
+ on(document, 'keydown', this.hotkeyHandler);
+ },
+ dispose: function () {
+ this.close();
+ document.removeEventListener('remove', this.hotkeyHandler);
+ },
+ hotkeyHandler: function (e) {
+ if (!this.actived) {
+ return;
+ }
+ if (e.keyCode === keymap['esc']) {
+ this.close();
+ }
+ },
+ toggleToolbar: function (e) {
+ e && e.preventDefault();
+ this.toolbar.classList.toggle('hidden');
+ },
+ zoomImage: function (restore) {
+ var image = this.image;
+ if (typeof restore === 'boolean') {
+ restore && addClass(image, 'limited');
+ }
+ else {
+ image.classList.toggle('limited');
+ }
+ },
+ viewSource: function (src) {
+ window.open(this.image.src);
+ },
+ open: function (src) {
+ var input = document.querySelector('input');
+ if (input) {
+ input.focus();
+ input.blur();
+ }
+ this.image.src = src;
+ this.zoomImage(true);
+ document.body.appendChild(this.viewer);
+ this.actived = true;
+ },
+ close: function () {
+ this.image.src = '';
+ document.body.removeChild(this.viewer);
+ this.actived = false;
+ }
+ });
+
+ return {
+ init: function() {
+ this.viewer = new ImageViewer();
+ },
+ events: {
+ 'normal.dblclick': function(e) {
+ var shape = e.kityEvent.targetShape
+ if (shape.__KityClassName === 'Image' && shape.url) {
+ this.viewer.open(shape.url);
+ }
+ }
+ }
+ };
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/image.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/image.js
new file mode 100644
index 0000000..8eb1d80
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/image.js
@@ -0,0 +1,147 @@
+define(function(require, exports, module) {
+ var kity = require('../core/kity');
+ var utils = require('../core/utils');
+
+ var Minder = require('../core/minder');
+ var MinderNode = require('../core/node');
+ var Command = require('../core/command');
+ var Module = require('../core/module');
+ var Renderer = require('../core/render');
+
+ Module.register('image', function() {
+ function loadImageSize(url, callback) {
+ var img = document.createElement('img');
+ img.onload = function() {
+ callback(img.width, img.height);
+ };
+ img.onerror = function() {
+ callback(null);
+ };
+ img.src = url;
+ }
+
+ function fitImageSize(width, height, maxWidth, maxHeight) {
+ var ratio = width / height,
+ fitRatio = maxWidth / maxHeight;
+
+ // 宽高比大于最大尺寸的宽高比,以宽度为标准适应
+ if (width > maxWidth && ratio > fitRatio) {
+ width = maxWidth;
+ height = width / ratio;
+ } else if (height > maxHeight) {
+ height = maxHeight;
+ width = height * ratio;
+ }
+
+ return {
+ width: width | 0,
+ height: height | 0
+ };
+ }
+
+ /**
+ * @command Image
+ * @description 为选中的节点添加图片
+ * @param {string} url 图片的 URL,设置为 null 移除
+ * @param {string} title 图片的说明
+ * @state
+ * 0: 当前有选中的节点
+ * -1: 当前没有选中的节点
+ * @return 返回首个选中节点的图片信息,JSON 对象: `{url: url, title: title}`
+ */
+ var ImageCommand = kity.createClass('ImageCommand', {
+ base: Command,
+
+ execute: function(km, url, title) {
+ var nodes = km.getSelectedNodes();
+
+ loadImageSize(url, function(width, height) {
+ nodes.forEach(function(n) {
+ var size = fitImageSize(
+ width, height,
+ km.getOption('maxImageWidth'),
+ km.getOption('maxImageHeight'));
+ n.setData('image', url);
+ n.setData('imageTitle', url && title);
+ n.setData('imageSize', url && size);
+ n.render();
+ });
+ km.fire('saveScene');
+ km.layout(300);
+ });
+
+ },
+ queryState: function(km) {
+ var nodes = km.getSelectedNodes(),
+ result = 0;
+ if (nodes.length === 0) {
+ return -1;
+ }
+ nodes.forEach(function(n) {
+ if (n && n.getData('image')) {
+ result = 0;
+ return false;
+ }
+ });
+ return result;
+ },
+ queryValue: function(km) {
+ var node = km.getSelectedNode();
+ return {
+ url: node.getData('image'),
+ title: node.getData('imageTitle')
+ };
+ }
+ });
+
+ var ImageRenderer = kity.createClass('ImageRenderer', {
+ base: Renderer,
+
+ create: function(node) {
+ return new kity.Image(node.getData('image'));
+ },
+
+ shouldRender: function(node) {
+ return node.getData('image');
+ },
+
+ update: function(image, node, box) {
+ var url = node.getData('image');
+ var title = node.getData('imageTitle');
+ var size = node.getData('imageSize');
+ var spaceTop = node.getStyle('space-top');
+
+ if (!size) return;
+
+ if (title) {
+ image.node.setAttributeNS('http://www.w3.org/1999/xlink', 'title', title);
+ }
+
+ var x = box.cx - size.width / 2;
+ var y = box.y - size.height - spaceTop;
+
+ image
+ .setUrl(url)
+ .setX(x | 0)
+ .setY(y | 0)
+ .setWidth(size.width | 0)
+ .setHeight(size.height | 0);
+
+ return new kity.Box(x | 0, y | 0, size.width | 0, size.height | 0);
+ }
+ });
+
+ return {
+ 'defaultOptions': {
+ 'maxImageWidth': 200,
+ 'maxImageHeight': 200
+ },
+ 'commands': {
+ 'image': ImageCommand
+ },
+ 'renderers': {
+ 'top': ImageRenderer
+ }
+ };
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/keynav.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/keynav.js
new file mode 100644
index 0000000..4b90289
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/keynav.js
@@ -0,0 +1,171 @@
+define(function(require, exports, module) {
+ var kity = require('../core/kity');
+ var utils = require('../core/utils');
+ var keymap = require('../core/keymap');
+
+ var Minder = require('../core/minder');
+ var MinderNode = require('../core/node');
+ var Command = require('../core/command');
+ var Module = require('../core/module');
+ var Renderer = require('../core/render');
+
+ Module.register('KeyboardModule', function() {
+ var min = Math.min,
+ max = Math.max,
+ abs = Math.abs,
+ sqrt = Math.sqrt,
+ exp = Math.exp;
+
+ function buildPositionNetwork(root) {
+ var pointIndexes = [],
+ p;
+ root.traverse(function(node) {
+ p = node.getLayoutBox();
+
+ // bugfix: 不应导航到收起的节点(判断其尺寸是否存在)
+ if (p.width && p.height) {
+ pointIndexes.push({
+ left: p.x,
+ top: p.y,
+ right: p.x + p.width,
+ bottom: p.y + p.height,
+ width: p.width,
+ height: p.height,
+ node: node
+ });
+ }
+ });
+ for (var i = 0; i < pointIndexes.length; i++) {
+ findClosestPointsFor(pointIndexes, i);
+ }
+ }
+
+ // 这是金泉的点子,赞!
+ // 求两个不相交矩形的最近距离
+ function getCoefedDistance(box1, box2) {
+ var xMin, xMax, yMin, yMax, xDist, yDist, dist, cx, cy;
+ xMin = min(box1.left, box2.left);
+ xMax = max(box1.right, box2.right);
+ yMin = min(box1.top, box2.top);
+ yMax = max(box1.bottom, box2.bottom);
+
+ xDist = xMax - xMin - box1.width - box2.width;
+ yDist = yMax - yMin - box1.height - box2.height;
+
+ if (xDist < 0) dist = yDist;
+ else if (yDist < 0) dist = xDist;
+ else dist = sqrt(xDist * xDist + yDist * yDist);
+
+ var node1 = box1.node;
+ var node2 = box2.node;
+
+ // sibling
+ if (node1.parent == node2.parent) {
+ dist /= 10;
+ }
+ // parent
+ if (node2.parent == node1) {
+ dist /= 5;
+ }
+
+ return dist;
+ }
+
+ function findClosestPointsFor(pointIndexes, iFind) {
+ var find = pointIndexes[iFind];
+ var most = {},
+ quad;
+ var current, dist;
+
+ for (var i = 0; i < pointIndexes.length; i++) {
+
+ if (i == iFind) continue;
+ current = pointIndexes[i];
+
+ dist = getCoefedDistance(current, find);
+
+ // left check
+ if (current.right < find.left) {
+ if (!most.left || dist < most.left.dist) {
+ most.left = {
+ dist: dist,
+ node: current.node
+ };
+ }
+ }
+
+ // right check
+ if (current.left > find.right) {
+ if (!most.right || dist < most.right.dist) {
+ most.right = {
+ dist: dist,
+ node: current.node
+ };
+ }
+ }
+
+ // top check
+ if (current.bottom < find.top) {
+ if (!most.top || dist < most.top.dist) {
+ most.top = {
+ dist: dist,
+ node: current.node
+ };
+ }
+ }
+
+ // bottom check
+ if (current.top > find.bottom) {
+ if (!most.down || dist < most.down.dist) {
+ most.down = {
+ dist: dist,
+ node: current.node
+ };
+ }
+ }
+ }
+ find.node._nearestNodes = {
+ right: most.right && most.right.node || null,
+ top: most.top && most.top.node || null,
+ left: most.left && most.left.node || null,
+ down: most.down && most.down.node || null
+ };
+ }
+
+ function navigateTo(km, direction) {
+ var referNode = km.getSelectedNode();
+ if (!referNode) {
+ km.select(km.getRoot());
+ buildPositionNetwork(km.getRoot());
+ return;
+ }
+ if (!referNode._nearestNodes) {
+ buildPositionNetwork(km.getRoot());
+ }
+ var nextNode = referNode._nearestNodes[direction];
+ if (nextNode) {
+ km.select(nextNode, true);
+ }
+ }
+
+ // 稀释用
+ var lastFrame;
+ return {
+ 'events': {
+ 'layoutallfinish': function() {
+ var root = this.getRoot();
+ buildPositionNetwork(root);
+ },
+ 'normal.keydown readonly.keydown': function(e) {
+ var minder = this;
+ ['left', 'right', 'up', 'down'].forEach(function(key) {
+ if (e.isShortcutKey(key)) {
+ navigateTo(minder, key == 'up' ? 'top' : key);
+ e.preventDefault();
+ }
+ });
+ }
+ }
+ };
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/layout.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/layout.js
new file mode 100644
index 0000000..4bcedf6
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/layout.js
@@ -0,0 +1,92 @@
+/**
+ * @fileOverview
+ *
+ * 布局模块
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+
+define(function(require, exports, module) {
+ var kity = require('../core/kity');
+ var Command = require('../core/command');
+ var Module = require('../core/module');
+
+ /**
+ * @command Layout
+ * @description 设置选中节点的布局
+ * 允许使用的布局可以使用 `kityminder.Minder.getLayoutList()` 查询
+ * @param {string} name 布局的名称,设置为 null 则使用继承或默认的布局
+ * @state
+ * 0: 当前有选中的节点
+ * -1: 当前没有选中的节点
+ * @return 返回首个选中节点的布局名称
+ */
+ var LayoutCommand = kity.createClass('LayoutCommand', {
+ base: Command,
+
+ execute: function(minder, name) {
+ var nodes = minder.getSelectedNodes();
+ nodes.forEach(function(node) {
+ node.layout(name);
+ });
+ },
+
+ queryValue: function(minder) {
+ var node = minder.getSelectedNode();
+ if (node) {
+ return node.getData('layout');
+ }
+ },
+
+ queryState: function(minder) {
+ return minder.getSelectedNode() ? 0 : -1;
+ }
+ });
+
+ /**
+ * @command ResetLayout
+ * @description 重设选中节点的布局,如果当前没有选中的节点,重设整个脑图的布局
+ * @state
+ * 0: 始终可用
+ * @return 返回首个选中节点的布局名称
+ */
+ var ResetLayoutCommand = kity.createClass('ResetLayoutCommand', {
+ base: Command,
+
+ execute: function(minder) {
+ var nodes = minder.getSelectedNodes();
+
+ if (!nodes.length) nodes = [minder.getRoot()];
+
+ nodes.forEach(function(node) {
+ node.traverse(function(child) {
+ child.resetLayoutOffset();
+ if (!child.isRoot()) {
+ child.setData('layout', null);
+ }
+ });
+ });
+ minder.layout(300);
+ },
+
+ enableReadOnly: true
+ });
+
+ Module.register('LayoutModule', {
+ commands: {
+ 'layout': LayoutCommand,
+ 'resetlayout': ResetLayoutCommand
+ },
+ contextmenu: [{
+ command: 'resetlayout'
+ }, {
+ divider: true
+ }],
+
+ commandShortcutKeys: {
+ 'resetlayout': 'Ctrl+Shift+L'
+ }
+ });
+
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/node.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/node.js
new file mode 100644
index 0000000..28a8fb9
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/node.js
@@ -0,0 +1,150 @@
+define(function(require, exports, module) {
+ var kity = require('../core/kity');
+ var utils = require('../core/utils');
+
+ var Minder = require('../core/minder');
+ var MinderNode = require('../core/node');
+ var Command = require('../core/command');
+ var Module = require('../core/module');
+ var Renderer = require('../core/render');
+
+ /**
+ * @command AppendChildNode
+ * @description 添加子节点到选中的节点中
+ * @param {string|object} textOrData 要插入的节点的文本或数据
+ * @state
+ * 0: 当前有选中的节点
+ * -1: 当前没有选中的节点
+ */
+ var AppendChildCommand = kity.createClass('AppendChildCommand', {
+ base: Command,
+ execute: function(km, text) {
+ var parent = km.getSelectedNode();
+ if (!parent) {
+ return null;
+ }
+ var node = km.createNode(text, parent);
+ km.select(node, true);
+ if (parent.isExpanded()) {
+ node.render();
+ }
+ else {
+ parent.expand();
+ parent.renderTree();
+ }
+ km.layout(600);
+ },
+ queryState: function(km) {
+ var selectedNode = km.getSelectedNode();
+ return selectedNode ? 0 : -1;
+ }
+ });
+
+ /**
+ * @command AppendSiblingNode
+ * @description 添加选中的节点的兄弟节点
+ * @param {string|object} textOrData 要添加的节点的文本或数据
+ * @state
+ * 0: 当前有选中的节点
+ * -1: 当前没有选中的节点
+ */
+ var AppendSiblingCommand = kity.createClass('AppendSiblingCommand', {
+ base: Command,
+ execute: function(km, text) {
+ var sibling = km.getSelectedNode();
+ var parent = sibling.parent;
+ if (!parent) {
+ return km.execCommand('AppendChildNode', text);
+ }
+ var node = km.createNode(text, parent, sibling.getIndex() + 1);
+ node.setGlobalLayoutTransform(sibling.getGlobalLayoutTransform());
+ km.select(node, true);
+ node.render();
+ km.layout(600);
+ },
+ queryState: function(km) {
+ var selectedNode = km.getSelectedNode();
+ return selectedNode ? 0 : -1;
+ }
+ });
+
+ /**
+ * @command RemoveNode
+ * @description 移除选中的节点
+ * @state
+ * 0: 当前有选中的节点
+ * -1: 当前没有选中的节点
+ */
+ var RemoveNodeCommand = kity.createClass('RemoverNodeCommand', {
+ base: Command,
+ execute: function(km) {
+ var nodes = km.getSelectedNodes();
+ var ancestor = MinderNode.getCommonAncestor.apply(null, nodes);
+ var index = nodes[0].getIndex();
+
+ nodes.forEach(function(node) {
+ if (!node.isRoot()) km.removeNode(node);
+ });
+ if (nodes.length == 1) {
+ var selectBack = ancestor.children[index - 1] || ancestor.children[index];
+ km.select(selectBack || ancestor || km.getRoot(), true);
+ } else {
+ km.select(ancestor || km.getRoot(), true);
+ }
+ km.layout(600);
+ },
+ queryState: function(km) {
+ var selectedNode = km.getSelectedNode();
+ return selectedNode && !selectedNode.isRoot() ? 0 : -1;
+ }
+ });
+
+ var AppendParentCommand = kity.createClass('AppendParentCommand', {
+ base: Command,
+ execute: function(km, text) {
+ var nodes = km.getSelectedNodes();
+
+ nodes.sort(function(a, b) {
+ return a.getIndex() - b.getIndex();
+ });
+ var parent = nodes[0].parent;
+
+ var newParent = km.createNode(text, parent, nodes[0].getIndex());
+ nodes.forEach(function(node) {
+ newParent.appendChild(node);
+ });
+ newParent.setGlobalLayoutTransform(nodes[nodes.length >> 1].getGlobalLayoutTransform());
+
+ km.select(newParent, true);
+ km.layout(600);
+ },
+ queryState: function(km) {
+ var nodes = km.getSelectedNodes();
+ if (!nodes.length) return -1;
+ var parent = nodes[0].parent;
+ if (!parent) return -1;
+ for (var i = 1; i < nodes.length; i++) {
+ if (nodes[i].parent != parent) return -1;
+ }
+ return 0;
+ }
+ });
+
+ Module.register('NodeModule', function() {
+ return {
+ commands: {
+ 'AppendChildNode': AppendChildCommand,
+ 'AppendSiblingNode': AppendSiblingCommand,
+ 'RemoveNode': RemoveNodeCommand,
+ 'AppendParentNode': AppendParentCommand
+ },
+
+ 'commandShortcutKeys': {
+ 'appendsiblingnode': 'normal::Enter',
+ 'appendchildnode': 'normal::Insert|Tab',
+ 'appendparentnode': 'normal::Shift+Tab|normal::Shift+Insert',
+ 'removenode': 'normal::Del|Backspace'
+ }
+ };
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/note.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/note.js
new file mode 100644
index 0000000..e5159de
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/note.js
@@ -0,0 +1,116 @@
+/**
+ * @fileOverview
+ *
+ * 支持节点详细信息(HTML)格式
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+define(function(require, exports, module) {
+ var kity = require('../core/kity');
+ var utils = require('../core/utils');
+
+ var Minder = require('../core/minder');
+ var MinderNode = require('../core/node');
+ var Command = require('../core/command');
+ var Module = require('../core/module');
+ var Renderer = require('../core/render');
+
+ Module.register('NoteModule', function() {
+
+ var NOTE_PATH = 'M9,9H3V8h6L9,9L9,9z M9,7H3V6h6V7z M9,5H3V4h6V5z M8.5,11H2V2h8v7.5 M9,12l2-2V1H1v11';
+
+
+ /**
+ * @command Note
+ * @description 设置节点的备注信息
+ * @param {string} note 要设置的备注信息,设置为 null 则移除备注信息
+ * @state
+ * 0: 当前有选中的节点
+ * -1: 当前没有选中的节点
+ */
+ var NoteCommand = kity.createClass('NoteCommand', {
+ base: Command,
+
+ execute: function(minder, note) {
+ var node = minder.getSelectedNode();
+ node.setData('note', note);
+ node.render();
+ node.getMinder().layout(300);
+ },
+
+ queryState: function(minder) {
+ return minder.getSelectedNodes().length === 1 ? 0 : -1;
+ },
+
+ queryValue: function(minder) {
+ var node = minder.getSelectedNode();
+ return node && node.getData('note');
+ }
+ });
+
+ var NoteIcon = kity.createClass('NoteIcon', {
+ base: kity.Group,
+
+ constructor: function() {
+ this.callBase();
+ this.width = 16;
+ this.height = 17;
+ this.rect = new kity.Rect(16, 17, 0.5, -8.5, 2).fill('transparent');
+ this.path = new kity.Path().setPathData(NOTE_PATH).setTranslate(2.5, -6.5);
+ this.addShapes([this.rect, this.path]);
+
+ this.on('mouseover', function() {
+ this.rect.fill('rgba(255, 255, 200, .8)');
+ }).on('mouseout', function() {
+ this.rect.fill('transparent');
+ });
+
+ this.setStyle('cursor', 'pointer');
+ }
+ });
+
+ var NoteIconRenderer = kity.createClass('NoteIconRenderer', {
+ base: Renderer,
+
+ create: function(node) {
+ var icon = new NoteIcon();
+ icon.on('mousedown', function(e) {
+ e.preventDefault();
+ node.getMinder().fire('editnoterequest');
+ });
+ icon.on('mouseover', function() {
+ node.getMinder().fire('shownoterequest', {node: node, icon: icon});
+ });
+ icon.on('mouseout', function() {
+ node.getMinder().fire('hidenoterequest', {node: node, icon: icon});
+ });
+ return icon;
+ },
+
+ shouldRender: function(node) {
+ return node.getData('note');
+ },
+
+ update: function(icon, node, box) {
+ var x = box.right + node.getStyle('space-left');
+ var y = box.cy;
+
+ icon.path.fill(node.getStyle('color'));
+ icon.setTranslate(x, y);
+
+ return new kity.Box(x, Math.round(y - icon.height / 2), icon.width, icon.height);
+ }
+
+ });
+
+ return {
+ renderers: {
+ right: NoteIconRenderer
+ },
+ commands: {
+ 'note': NoteCommand
+ }
+ };
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/outline.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/outline.js
new file mode 100644
index 0000000..499bf42
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/outline.js
@@ -0,0 +1,165 @@
+define(function(require, exports, module) {
+ var kity = require('../core/kity');
+ var utils = require('../core/utils');
+
+ var Minder = require('../core/minder');
+ var MinderNode = require('../core/node');
+ var Command = require('../core/command');
+ var Module = require('../core/module');
+ var Renderer = require('../core/render');
+
+ var OutlineRenderer = kity.createClass('OutlineRenderer', {
+ base: Renderer,
+
+ create: function(node) {
+
+ var outline = new kity.Rect()
+ .setId(utils.uuid('node_outline'));
+
+ this.bringToBack = true;
+
+ return outline;
+ },
+
+ update: function(outline, node, box) {
+
+ var shape = node.getStyle('shape');
+
+ var paddingLeft = node.getStyle('padding-left'),
+ paddingRight = node.getStyle('padding-right'),
+ paddingTop = node.getStyle('padding-top'),
+ paddingBottom = node.getStyle('padding-bottom');
+
+ var outlineBox = {
+ x: box.x - paddingLeft,
+ y: box.y - paddingTop,
+ width: box.width + paddingLeft + paddingRight,
+ height: box.height + paddingTop + paddingBottom
+ };
+
+
+ var radius = node.getStyle('radius');
+ // 天盘图圆形的情况
+ if (shape && shape == 'circle') {
+ var p = Math.pow;
+ var r = Math.round;
+
+ radius = r(Math.sqrt(p(outlineBox.width, 2) + p(outlineBox.height, 2)) / 2);
+
+ outlineBox.x = box.cx - radius;
+ outlineBox.y = box.cy - radius;
+ outlineBox.width = 2 * radius;
+ outlineBox.height = 2 * radius;
+ }
+
+ var prefix = node.isSelected() ? (node.getMinder().isFocused() ? 'selected-' : 'blur-selected-') : '';
+ outline
+ .setPosition(outlineBox.x, outlineBox.y)
+ .setSize(outlineBox.width, outlineBox.height)
+ .setRadius(radius)
+ .fill(node.getData('background') || node.getStyle(prefix + 'background') || node.getStyle('background'))
+ .stroke(node.getStyle(prefix + 'stroke' || node.getStyle('stroke')),
+ node.getStyle(prefix + 'stroke-width'));
+
+ return new kity.Box(outlineBox);
+ }
+ });
+
+ var ShadowRenderer = kity.createClass('ShadowRenderer', {
+ base: Renderer,
+
+ create: function(node) {
+ this.bringToBack = true;
+ return new kity.Rect();
+ },
+
+ shouldRender: function(node) {
+ return node.getStyle('shadow');
+ },
+
+ update: function(shadow, node, box) {
+ shadow.setPosition(box.x + 4, box.y + 5)
+ .fill(node.getStyle('shadow'));
+
+ var shape = node.getStyle('shape');
+ if(!shape){
+ shadow.setSize(box.width, box.height);
+ shadow.setRadius(node.getStyle('radius'));
+
+ }else if(shape=='circle'){
+ var width= Math.max(box.width,box.height);
+ shadow.setSize(width, width);
+ shadow.setRadius(width/2);
+ }
+ }
+ });
+
+ var marker = new kity.Marker();
+
+ marker.setWidth(10);
+ marker.setHeight(12);
+ marker.setRef(0, 0);
+ marker.setViewBox(-6, -4, 8, 10);
+
+ marker.addShape(new kity.Path().setPathData('M-5-3l5,3,-5,3').stroke('#33ffff'));
+
+ var wireframeOption = /wire/.test(window.location.href);
+ var WireframeRenderer = kity.createClass('WireframeRenderer', {
+ base: Renderer,
+
+ create: function() {
+ var wireframe = new kity.Group();
+ var oxy = this.oxy = new kity.Path()
+ .stroke('#f6f')
+ .setPathData('M0,-50L0,50M-50,0L50,0');
+
+ var box = this.wireframe = new kity.Rect()
+ .stroke('lightgreen');
+
+ var vectorIn = this.vectorIn = new kity.Path()
+ .stroke('#66ffff');
+ var vectorOut = this.vectorOut = new kity.Path()
+ .stroke('#66ffff');
+
+ vectorIn.setMarker(marker, 'end');
+ vectorOut.setMarker(marker, 'end');
+
+ return wireframe.addShapes([oxy, box, vectorIn, vectorOut]);
+ },
+
+ shouldRender: function() {
+ return wireframeOption;
+ },
+
+ update: function(created, node, box) {
+ this.wireframe
+ .setPosition(box.x, box.y)
+ .setSize(box.width, box.height);
+ var pin = node.getVertexIn();
+ var pout = node.getVertexOut();
+ var vin = node.getLayoutVectorIn().normalize(30);
+ var vout = node.getLayoutVectorOut().normalize(30);
+ this.vectorIn.setPathData(['M', pin.offset(vin.reverse()), 'L', pin]);
+ this.vectorOut.setPathData(['M', pout, 'l', vout]);
+ }
+ });
+
+ Module.register('OutlineModule', function() {
+ return {
+ events: (!wireframeOption ? null : {
+ 'ready': function() {
+ this.getPaper().addResource(marker);
+ },
+ 'layoutallfinish': function() {
+ this.getRoot().traverse(function(node) {
+ node.getRenderer('WireframeRenderer').update(null, node, node.getContentBox());
+ });
+ }
+ }),
+ renderers: {
+ outline: OutlineRenderer,
+ outside: [ShadowRenderer, WireframeRenderer]
+ }
+ };
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/priority.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/priority.js
new file mode 100644
index 0000000..a7b7452
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/priority.js
@@ -0,0 +1,156 @@
+define(function(require, exports, module) {
+ var kity = require('../core/kity');
+ var utils = require('../core/utils');
+
+ var Minder = require('../core/minder');
+ var MinderNode = require('../core/node');
+ var Command = require('../core/command');
+ var Module = require('../core/module');
+ var Renderer = require('../core/render');
+
+ Module.register('PriorityModule', function() {
+ var minder = this;
+
+ // Designed by Akikonata
+ // [MASK, BACK]
+ var PRIORITY_COLORS = [null, ['#FF1200', '#840023'], // 1 - red
+ ['#0074FF', '#01467F'], // 2 - blue
+ ['#00AF00', '#006300'], // 3 - green
+ ['#FF962E', '#B25000'], // 4 - orange
+ ['#A464FF', '#4720C4'], // 5 - purple
+ ['#A3A3A3', '#515151'], // 6,7,8,9 - gray
+ ['#A3A3A3', '#515151'],
+ ['#A3A3A3', '#515151'],
+ ['#A3A3A3', '#515151'],
+ ]; // hue from 1 to 5
+
+ // jscs:disable maximumLineLength
+ var BACK_PATH = 'M0,13c0,3.866,3.134,7,7,7h6c3.866,0,7-3.134,7-7V7H0V13z';
+ var MASK_PATH = 'M20,10c0,3.866-3.134,7-7,7H7c-3.866,0-7-3.134-7-7V7c0-3.866,3.134-7,7-7h6c3.866,0,7,3.134,7,7V10z';
+
+ var PRIORITY_DATA = 'priority';
+
+ // 优先级图标的图形
+ var PriorityIcon = kity.createClass('PriorityIcon', {
+ base: kity.Group,
+
+ constructor: function() {
+ this.callBase();
+ this.setSize(20);
+ this.create();
+ this.setId(utils.uuid('node_priority'));
+ },
+
+ setSize: function(size) {
+ this.width = this.height = size;
+ },
+
+ create: function() {
+ var white, back, mask, number; // 4 layer
+
+ white = new kity.Path().setPathData(MASK_PATH).fill('white');
+ back = new kity.Path().setPathData(BACK_PATH).setTranslate(0.5, 0.5);
+ mask = new kity.Path().setPathData(MASK_PATH).setOpacity(0.8).setTranslate(0.5, 0.5);
+
+ number = new kity.Text()
+ .setX(this.width / 2 - 0.5).setY(this.height / 2)
+ .setTextAnchor('middle')
+ .setVerticalAlign('middle')
+ .setFontItalic(true)
+ .setFontSize(12)
+ .fill('white');
+
+ this.addShapes([back, mask, number]);
+ this.mask = mask;
+ this.back = back;
+ this.number = number;
+ },
+
+ setValue: function(value) {
+ var back = this.back,
+ mask = this.mask,
+ number = this.number;
+
+ var color = PRIORITY_COLORS[value];
+
+ if (color) {
+ back.fill(color[1]);
+ mask.fill(color[0]);
+ }
+
+ number.setContent(value);
+ }
+ });
+
+ /**
+ * @command Priority
+ * @description 设置节点的优先级信息
+ * @param {number} value 要设置的优先级(添加一个优先级小图标)
+ * 取值为 0 移除优先级信息;
+ * 取值为 1 - 9 设置优先级,超过 9 的优先级不渲染
+ * @state
+ * 0: 当前有选中的节点
+ * -1: 当前没有选中的节点
+ */
+ var PriorityCommand = kity.createClass('SetPriorityCommand', {
+ base: Command,
+ execute: function(km, value) {
+ var nodes = km.getSelectedNodes();
+ for (var i = 0; i < nodes.length; i++) {
+ nodes[i].setData(PRIORITY_DATA, value || null).render();
+ }
+ km.layout();
+ },
+ queryValue: function(km) {
+ var nodes = km.getSelectedNodes();
+ var val;
+ for (var i = 0; i < nodes.length; i++) {
+ val = nodes[i].getData(PRIORITY_DATA);
+ if (val) break;
+ }
+ return val || null;
+ },
+
+ queryState: function(km) {
+ return km.getSelectedNodes().length ? 0 : -1;
+ }
+ });
+ return {
+ 'commands': {
+ 'priority': PriorityCommand
+ },
+ 'renderers': {
+ left: kity.createClass('PriorityRenderer', {
+ base: Renderer,
+
+ create: function(node) {
+ return new PriorityIcon();
+ },
+
+ shouldRender: function(node) {
+ return node.getData(PRIORITY_DATA);
+ },
+
+ update: function(icon, node, box) {
+ var data = node.getData(PRIORITY_DATA);
+ var spaceLeft = node.getStyle('space-left'),
+ x, y;
+
+ icon.setValue(data);
+ x = box.left - icon.width - spaceLeft;
+ y = -icon.height / 2;
+
+ icon.setTranslate(x, y);
+
+ return new kity.Box({
+ x: x,
+ y: y,
+ width: icon.width,
+ height: icon.height
+ });
+ }
+ })
+ }
+ };
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/progress.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/progress.js
new file mode 100644
index 0000000..a7fa451
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/progress.js
@@ -0,0 +1,160 @@
+define(function(require, exports, module) {
+ var kity = require('../core/kity');
+ var utils = require('../core/utils');
+
+ var Minder = require('../core/minder');
+ var MinderNode = require('../core/node');
+ var Command = require('../core/command');
+ var Module = require('../core/module');
+ var Renderer = require('../core/render');
+
+ Module.register('ProgressModule', function() {
+ var minder = this;
+
+ var PROGRESS_DATA = 'progress';
+
+ // Designed by Akikonata
+ var BG_COLOR = '#FFED83';
+ var PIE_COLOR = '#43BC00';
+ var SHADOW_PATH = 'M10,3c4.418,0,8,3.582,8,8h1c0-5.523-3.477-10-9-10S1,5.477,1,11h1C2,6.582,5.582,3,10,3z';
+ var SHADOW_COLOR = '#8E8E8E';
+
+ // jscs:disable maximumLineLength
+ var FRAME_PATH = 'M10,0C4.477,0,0,4.477,0,10c0,5.523,4.477,10,10,10s10-4.477,10-10C20,4.477,15.523,0,10,0zM10,18c-4.418,0-8-3.582-8-8s3.582-8,8-8s8,3.582,8,8S14.418,18,10,18z';
+
+ var FRAME_GRAD = new kity.LinearGradient().pipe(function(g) {
+ g.setStartPosition(0, 0);
+ g.setEndPosition(0, 1);
+ g.addStop(0, '#fff');
+ g.addStop(1, '#ccc');
+ });
+ var CHECK_PATH = 'M15.812,7.896l-6.75,6.75l-4.5-4.5L6.25,8.459l2.812,2.803l5.062-5.053L15.812,7.896z';
+ var CHECK_COLOR = '#EEE';
+
+ minder.getPaper().addResource(FRAME_GRAD);
+
+ // 进度图标的图形
+ var ProgressIcon = kity.createClass('ProgressIcon', {
+ base: kity.Group,
+
+ constructor: function(value) {
+ this.callBase();
+ this.setSize(20);
+ this.create();
+ this.setValue(value);
+ this.setId(utils.uuid('node_progress'));
+ this.translate(0.5, 0.5);
+ },
+
+ setSize: function(size) {
+ this.width = this.height = size;
+ },
+
+ create: function() {
+
+ var bg, pie, shadow, frame, check;
+
+ bg = new kity.Circle(9)
+ .fill(BG_COLOR);
+
+ pie = new kity.Pie(9, 0)
+ .fill(PIE_COLOR);
+
+ shadow = new kity.Path()
+ .setPathData(SHADOW_PATH)
+ .setTranslate(-10, -10)
+ .fill(SHADOW_COLOR);
+
+ frame = new kity.Path()
+ .setTranslate(-10, -10)
+ .setPathData(FRAME_PATH)
+ .fill(FRAME_GRAD);
+
+ check = new kity.Path()
+ .setTranslate(-10, -10)
+ .setPathData(CHECK_PATH)
+ .fill(CHECK_COLOR);
+
+ this.addShapes([bg, pie, shadow, check, frame]);
+ this.pie = pie;
+ this.check = check;
+ },
+
+ setValue: function(value) {
+ this.pie.setAngle(-360 * (value - 1) / 8);
+ this.check.setVisible(value == 9);
+ }
+ });
+
+ /**
+ * @command Progress
+ * @description 设置节点的进度信息(添加一个进度小图标)
+ * @param {number} value 要设置的进度
+ * 取值为 0 移除进度信息;
+ * 取值为 1 表示未开始;
+ * 取值为 2 表示完成 1/8;
+ * 取值为 3 表示完成 2/8;
+ * 取值为 4 表示完成 3/8;
+ * 其余类推,取值为 9 表示全部完成
+ * @state
+ * 0: 当前有选中的节点
+ * -1: 当前没有选中的节点
+ */
+ var ProgressCommand = kity.createClass('ProgressCommand', {
+ base: Command,
+ execute: function(km, value) {
+ var nodes = km.getSelectedNodes();
+ for (var i = 0; i < nodes.length; i++) {
+ nodes[i].setData(PROGRESS_DATA, value || null).render();
+ }
+ km.layout();
+ },
+ queryValue: function(km) {
+ var nodes = km.getSelectedNodes();
+ var val;
+ for (var i = 0; i < nodes.length; i++) {
+ val = nodes[i].getData(PROGRESS_DATA);
+ if (val) break;
+ }
+ return val || null;
+ },
+
+ queryState: function(km) {
+ return km.getSelectedNodes().length ? 0 : -1;
+ }
+ });
+
+ return {
+ 'commands': {
+ 'progress': ProgressCommand
+ },
+ 'renderers': {
+ left: kity.createClass('ProgressRenderer', {
+ base: Renderer,
+
+ create: function(node) {
+ return new ProgressIcon();
+ },
+
+ shouldRender: function(node) {
+ return node.getData(PROGRESS_DATA);
+ },
+
+ update: function(icon, node, box) {
+ var data = node.getData(PROGRESS_DATA);
+ var spaceLeft = node.getStyle('space-left');
+ var x, y;
+
+ icon.setValue(data);
+
+ x = box.left - icon.width - spaceLeft;
+ y = -icon.height / 2;
+ icon.setTranslate(x + icon.width / 2, y + icon.height / 2);
+
+ return new kity.Box(x, y, icon.width, icon.height);
+ }
+ })
+ }
+ };
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/resource.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/resource.js
new file mode 100644
index 0000000..d672f6c
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/resource.js
@@ -0,0 +1,314 @@
+define(function(require, exports, module) {
+ var kity = require('../core/kity');
+ var utils = require('../core/utils');
+
+ var Minder = require('../core/minder');
+ var MinderNode = require('../core/node');
+ var Command = require('../core/command');
+ var Module = require('../core/module');
+ var Renderer = require('../core/render');
+
+ Module.register('Resource', function() {
+
+ // String Hash
+ // https://github.com/drostie/sha3-js/edit/master/blake32.min.js
+ var blake32=(function(){var k,g,r,l,m,o,p,q,t,w,x;x=4*(1<<30);k=[0x6a09e667,0xbb67ae85,0x3c6ef372,0xa54ff53a,0x510e527f,0x9b05688c,0x1f83d9ab,0x5be0cd19];m=[0x243F6A88,0x85A308D3,0x13198A2E,0x03707344,0xA4093822,0x299F31D0,0x082EFA98,0xEC4E6C89,0x452821E6,0x38D01377,0xBE5466CF,0x34E90C6C,0xC0AC29B7,0xC97C50DD,0x3F84D5B5,0xB5470917];w=function(i){if(i<0){i+=x}return("00000000"+i.toString(16)).slice(-8)};o=[[16,50,84,118,152,186,220,254],[174,132,249,109,193,32,123,53],[139,12,37,223,234,99,23,73],[151,19,205,235,98,165,4,143],[9,117,66,250,30,203,134,211],[194,166,176,56,212,87,239,145],[92,241,222,164,112,54,41,184],[189,231,28,147,5,79,104,162],[246,158,59,128,44,125,65,90],[42,72,103,81,191,233,195,13]];p=function(a,b,n){var s=q[a]^q[b];q[a]=(s>>>n)|(s<<(32-n))};g=function(i,a,b,c,d){var u=l+o[r][i]%16,v=l+(o[r][i]>>4);a%=4;b=4+b%4;c=8+c%4;d=12+d%4;q[a]+=q[b]+(t[u]^m[v%16]);p(d,a,16);q[c]+=q[d];p(b,c,12);q[a]+=q[b]+(t[v]^m[u%16]);p(d,a,8);q[c]+=q[d];p(b,c,7)};return function(a,b){if(!(b instanceof Array&&b.length===4)){b=[0,0,0,0]}var c,d,e,L,f,h,j,i;d=k.slice(0);c=m.slice(0,8);for(r=0;r<4;r+=1){c[r]^=b[r]}e=a.length*16;f=(e%512>446||e%512===0)?0:e;if(e%512===432){a+="\u8001"}else{a+="\u8000";while(a.length%32!==27){a+="\u0000"}a+="\u0001"}t=[];for(i=0;i= 0; i--) {
+ ch = str.charCodeAt(i);
+ hash ^= ((hash << 5) + ch + (hash >> 2));
+ }
+ return (hash & 0x7FFFFFFF);
+ },
+
+ /**
+ * 获取脑图中某个资源对应的颜色
+ *
+ * 如果存在同名资源,则返回已经分配给该资源的颜色,否则分配给该资源一个颜色,并且返回
+ *
+ * 如果资源数超过颜色序列数量,返回哈希颜色
+ *
+ * @param {String} resource 资源名称
+ * @return {Color}
+ */
+ getResourceColor: function(resource) {
+ var colorMapping = this._getResourceColorIndexMapping();
+ var nextIndex;
+
+ if (!Object.prototype.hasOwnProperty.call(colorMapping, resource)) {
+ // 找不到找下个可用索引
+ nextIndex = this._getNextResourceColorIndex();
+ colorMapping[resource] = nextIndex;
+ }
+
+ // 资源过多,找不到可用索引颜色,统一返回哈希函数得到的颜色
+ return RESOURCE_COLOR_SERIES[colorMapping[resource]] || kity.Color.createHSL(Math.floor(this.getHashCode(resource) / 0x7FFFFFFF * 359), 100, 85);
+ },
+
+ /**
+ * 获得已使用的资源的列表
+ *
+ * @return {Array}
+ */
+ getUsedResource: function() {
+ var mapping = this._getResourceColorIndexMapping();
+ var used = [],
+ resource;
+
+ for (resource in mapping) {
+ if (Object.prototype.hasOwnProperty.call(mapping, resource)) {
+ used.push(resource);
+ }
+ }
+
+ return used;
+ },
+
+ /**
+ * 获取脑图下一个可用的资源颜色索引
+ *
+ * @return {int}
+ */
+ _getNextResourceColorIndex: function() {
+ // 获取现有颜色映射
+ // resource => color_index
+ var colorMapping = this._getResourceColorIndexMapping();
+
+ var resource, used, i;
+
+ used = [];
+
+ // 抽取已经使用的值到 used 数组
+ for (resource in colorMapping) {
+ if (Object.prototype.hasOwnProperty.call(colorMapping, resource)) {
+ used.push(colorMapping[resource]);
+ }
+ }
+
+ // 枚举所有的可用值,如果还没被使用,返回
+ for (i = 0; i < RESOURCE_COLOR_SERIES.length; i++) {
+ if (!~used.indexOf(i)) return i;
+ }
+
+ // 没有可用的颜色了
+ return -1;
+ },
+
+ // 获取现有颜色映射
+ // resource => color_index
+ _getResourceColorIndexMapping: function() {
+ return this._resourceColorMapping || (this._resourceColorMapping = {});
+ }
+
+ });
+
+
+ /**
+ * @class 设置资源的命令
+ *
+ * @example
+ *
+ * // 设置选中节点资源为 "张三"
+ * minder.execCommand('resource', ['张三']);
+ *
+ * // 添加资源 "李四" 到选中节点
+ * var resource = minder.queryCommandValue();
+ * resource.push('李四');
+ * minder.execCommand('resource', resource);
+ *
+ * // 清除选中节点的资源
+ * minder.execCommand('resource', null);
+ */
+ var ResourceCommand = kity.createClass('ResourceCommand', {
+
+ base: Command,
+
+ execute: function(minder, resource) {
+ var nodes = minder.getSelectedNodes();
+
+ if (typeof(resource) == 'string') {
+ resource = [resource];
+ }
+
+ nodes.forEach(function(node) {
+ node.setData('resource', resource).render();
+ });
+
+ minder.layout(200);
+ },
+
+ queryValue: function(minder) {
+ var nodes = minder.getSelectedNodes();
+ var resource = [];
+
+ nodes.forEach(function(node) {
+ var nodeResource = node.getData('resource');
+
+ if (!nodeResource) return;
+
+ nodeResource.forEach(function(name) {
+ if (!~resource.indexOf(name)) {
+ resource.push(name);
+ }
+ });
+ });
+
+ return resource;
+ },
+
+ queryState: function(km) {
+ return km.getSelectedNode() ? 0 : -1;
+ }
+ });
+
+ /**
+ * @class 资源的覆盖图形
+ *
+ * 该类为一个资源以指定的颜色渲染一个动态的覆盖图形
+ */
+ var ResourceOverlay = kity.createClass('ResourceOverlay', {
+ base: kity.Group,
+
+ constructor: function() {
+ this.callBase();
+
+ var text, rect;
+
+ rect = this.rect = new kity.Rect().setRadius(4);
+
+ text = this.text = new kity.Text()
+ .setFontSize(12)
+ .setVerticalAlign('middle');
+
+ this.addShapes([rect, text]);
+ },
+
+ setValue: function(resourceName, color) {
+ var paddingX = 8,
+ paddingY = 4,
+ borderRadius = 4;
+ var text, box, rect;
+
+ text = this.text;
+
+ if (resourceName == this.lastResourceName) {
+
+ box = this.lastBox;
+
+ } else {
+
+ text.setContent(resourceName);
+
+ box = text.getBoundaryBox();
+ this.lastResourceName = resourceName;
+ this.lastBox = box;
+
+ }
+
+ text.setX(paddingX).fill(color.dec('l', 70));
+
+ rect = this.rect;
+ rect.setPosition(0, box.y - paddingY);
+ this.width = Math.round(box.width + paddingX * 2);
+ this.height = Math.round(box.height + paddingY * 2);
+ rect.setSize(this.width, this.height);
+ rect.fill(color);
+ }
+ });
+
+ /**
+ * @class 资源渲染器
+ */
+ var ResourceRenderer = kity.createClass('ResourceRenderer', {
+ base: Renderer,
+
+ create: function(node) {
+ this.overlays = [];
+ return new kity.Group();
+ },
+
+ shouldRender: function(node) {
+ return node.getData('resource') && node.getData('resource').length;
+ },
+
+ update: function(container, node, box) {
+ var spaceRight = node.getStyle('space-right');
+
+ var overlays = this.overlays;
+
+ /* 修复 resource 数组中出现 null 的 bug
+ * @Author zhangbobell
+ * @date 2016-01-15
+ */
+ var resource = node.getData("resource").filter(function(ele) {
+ return ele !== null;
+ });
+ if (resource.length === 0) {
+ return;
+ }
+ var minder = node.getMinder();
+ var i, overlay, x;
+
+ x = 0;
+ for (i = 0; i < resource.length; i++) {
+
+ x += spaceRight;
+
+ overlay = overlays[i];
+ if (!overlay) {
+ overlay = new ResourceOverlay();
+ overlays.push(overlay);
+ container.addShape(overlay);
+ }
+ overlay.setVisible(true);
+ overlay.setValue(resource[i], minder.getResourceColor(resource[i]));
+ overlay.setTranslate(x, -1);
+
+ x += overlay.width;
+ }
+
+ while ((overlay = overlays[i++])) overlay.setVisible(false);
+
+ container.setTranslate(box.right, 0);
+
+ return new kity.Box({
+ x: box.right,
+ y: Math.round(-overlays[0].height / 2),
+ width: x,
+ height: overlays[0].height
+ });
+ }
+ });
+
+ return {
+ commands: {
+ 'resource': ResourceCommand
+ },
+
+ renderers: {
+ right: ResourceRenderer
+ }
+ };
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/select.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/select.js
new file mode 100644
index 0000000..28c6a9f
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/select.js
@@ -0,0 +1,184 @@
+define(function(require, exports, module) {
+ var kity = require('../core/kity');
+ var utils = require('../core/utils');
+
+ var Minder = require('../core/minder');
+ var MinderNode = require('../core/node');
+ var Command = require('../core/command');
+ var Module = require('../core/module');
+ var Renderer = require('../core/render');
+
+ Module.register('Select', function() {
+ var minder = this;
+ var rc = minder.getRenderContainer();
+
+ // 在实例上渲染框选矩形、计算框选范围的对象
+ var marqueeActivator = (function() {
+
+ // 记录选区的开始位置(mousedown的位置)
+ var startPosition = null;
+
+ // 选区的图形
+ var marqueeShape = new kity.Path();
+
+ // 标记是否已经启动框选状态
+ // 并不是 mousedown 发生之后就启动框选状态,而是检测到移动了一定的距离(MARQUEE_MODE_THRESHOLD)之后
+ var marqueeMode = false;
+ var MARQUEE_MODE_THRESHOLD = 10;
+
+ return {
+ selectStart: function(e) {
+ // 只接受左键
+ if (e.originEvent.button || e.originEvent.altKey) return;
+
+ // 清理不正确状态
+ if (startPosition) {
+ return this.selectEnd();
+ }
+
+ startPosition = e.getPosition(rc).round();
+ },
+ selectMove: function(e) {
+ if (minder.getStatus() == 'textedit') {
+ return;
+ }
+ if (!startPosition) return;
+
+ var p1 = startPosition,
+ p2 = e.getPosition(rc);
+
+ // 检测是否要进入选区模式
+ if (!marqueeMode) {
+ // 距离没达到阈值,退出
+ if (kity.Vector.fromPoints(p1, p2).length() < MARQUEE_MODE_THRESHOLD) {
+ return;
+ }
+ // 已经达到阈值,记录下来并且重置选区形状
+ marqueeMode = true;
+ rc.addShape(marqueeShape);
+ marqueeShape
+ .fill(minder.getStyle('marquee-background'))
+ .stroke(minder.getStyle('marquee-stroke')).setOpacity(0.8).getDrawer().clear();
+ }
+
+ var marquee = new kity.Box(p1.x, p1.y, p2.x - p1.x, p2.y - p1.y),
+ selectedNodes = [];
+
+ // 使其犀利
+ marquee.left = Math.round(marquee.left);
+ marquee.top = Math.round(marquee.top);
+ marquee.right = Math.round(marquee.right);
+ marquee.bottom = Math.round(marquee.bottom);
+
+ // 选区形状更新
+ marqueeShape.getDrawer().pipe(function() {
+ this.clear();
+ this.moveTo(marquee.left, marquee.top);
+ this.lineTo(marquee.right, marquee.top);
+ this.lineTo(marquee.right, marquee.bottom);
+ this.lineTo(marquee.left, marquee.bottom);
+ this.close();
+ });
+
+ // 计算选中范围
+ minder.getRoot().traverse(function(node) {
+ var renderBox = node.getLayoutBox();
+ if (!renderBox.intersect(marquee).isEmpty()) {
+ selectedNodes.push(node);
+ }
+ });
+
+ // 应用选中范围
+ minder.select(selectedNodes, true);
+
+ // 清除多余的东西
+ window.getSelection().removeAllRanges();
+ },
+ selectEnd: function(e) {
+ if (startPosition) {
+ startPosition = null;
+ }
+ if (marqueeMode) {
+ marqueeShape.fadeOut(200, 'ease', 0, function() {
+ if (marqueeShape.remove) marqueeShape.remove();
+ });
+ marqueeMode = false;
+ }
+ }
+ };
+ })();
+
+ var lastDownNode = null,
+ lastDownPosition = null;
+ return {
+ 'init': function() {
+ window.addEventListener('mouseup', function() {
+ marqueeActivator.selectEnd();
+ });
+ },
+ 'events': {
+ 'mousedown': function(e) {
+
+ var downNode = e.getTargetNode();
+
+ // 没有点中节点:
+ // 清除选中状态,并且标记选区开始位置
+ if (!downNode) {
+ this.removeAllSelectedNodes();
+ marqueeActivator.selectStart(e);
+
+ this.setStatus('normal');
+ }
+
+ // 点中了节点,并且按了 shift 键:
+ // 被点中的节点切换选中状态
+ else if (e.isShortcutKey('Ctrl')) {
+ this.toggleSelect(downNode);
+ }
+
+ // 点中的节点没有被选择:
+ // 单选点中的节点
+ else if (!downNode.isSelected()) {
+ this.select(downNode, true);
+ }
+
+ // 点中的节点被选中了,并且不是单选:
+ // 完成整个点击之后需要使其变为单选。
+ // 不能马上变为单选,因为可能是需要拖动选中的多个节点
+ else if (!this.isSingleSelect()) {
+ lastDownNode = downNode;
+ lastDownPosition = e.getPosition();
+ }
+ },
+ 'mousemove': marqueeActivator.selectMove,
+ 'mouseup': function(e) {
+ var upNode = e.getTargetNode();
+
+ // 如果 mouseup 发生在 lastDownNode 外,是无需理会的
+ if (upNode && upNode == lastDownNode) {
+ var upPosition = e.getPosition();
+ var movement = kity.Vector.fromPoints(lastDownPosition, upPosition);
+ if (movement.length() < 1) this.select(lastDownNode, true);
+ lastDownNode = null;
+ }
+
+ // 清理一下选择状态
+ marqueeActivator.selectEnd(e);
+ },
+ //全选操作
+ 'normal.keydown': function(e) {
+
+ if (e.isShortcutKey('ctrl+a')) {
+ var selectedNodes = [];
+
+ this.getRoot().traverse(function(node) {
+ selectedNodes.push(node);
+ });
+ this.select(selectedNodes, true);
+ e.preventDefault();
+ }
+ }
+ }
+ };
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/style.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/style.js
new file mode 100644
index 0000000..02e21eb
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/style.js
@@ -0,0 +1,114 @@
+define(function(require, exports, module) {
+ var kity = require('../core/kity');
+ var utils = require('../core/utils');
+
+ var Minder = require('../core/minder');
+ var MinderNode = require('../core/node');
+ var Command = require('../core/command');
+ var Module = require('../core/module');
+ var Renderer = require('../core/render');
+
+ Module.register('StyleModule', function() {
+ var styleNames = ['font-size', 'font-family', 'font-weight', 'font-style', 'background', 'color'];
+ var styleClipBoard = null;
+
+ function hasStyle(node) {
+ var data = node.getData();
+ for (var i = 0; i < styleNames.length; i++) {
+ if (styleNames[i] in data) return true;
+ }
+ }
+
+ return {
+ 'commands': {
+ /**
+ * @command CopyStyle
+ * @description 拷贝选中节点的当前样式,包括字体、字号、粗体、斜体、背景色、字体色
+ * @state
+ * 0: 当前有选中的节点
+ * -1: 当前没有选中的节点
+ */
+ 'copystyle': kity.createClass('CopyStyleCommand', {
+ base: Command,
+
+ execute: function(minder) {
+ var node = minder.getSelectedNode();
+ var nodeData = node.getData();
+ styleClipBoard = {};
+ styleNames.forEach(function(name) {
+ if (name in nodeData) styleClipBoard[name] = nodeData[name];
+ else {
+ styleClipBoard[name] = null;
+ delete styleClipBoard[name];
+ }
+ });
+ return styleClipBoard;
+ },
+
+ queryState: function(minder) {
+ var nodes = minder.getSelectedNodes();
+ if (nodes.length !== 1) return -1;
+ return hasStyle(nodes[0]) ? 0 : -1;
+ }
+ }),
+
+ /**
+ * @command PasteStyle
+ * @description 粘贴已拷贝的样式到选中的节点上,包括字体、字号、粗体、斜体、背景色、字体色
+ * @state
+ * 0: 当前有选中的节点,并且已经有复制的样式
+ * -1: 当前没有选中的节点,或者没有复制的样式
+ */
+ 'pastestyle': kity.createClass('PastStyleCommand', {
+ base: Command,
+
+ execute: function(minder) {
+ minder.getSelectedNodes().forEach(function(node) {
+ for (var name in styleClipBoard) {
+ if (styleClipBoard.hasOwnProperty(name))
+ node.setData(name, styleClipBoard[name]);
+ }
+ });
+ minder.renderNodeBatch(minder.getSelectedNodes());
+ minder.layout(300);
+ return styleClipBoard;
+ },
+
+ queryState: function(minder) {
+ return (styleClipBoard && minder.getSelectedNodes().length) ? 0 : -1;
+ }
+ }),
+
+ /**
+ * @command ClearStyle
+ * @description 移除选中节点的样式,包括字体、字号、粗体、斜体、背景色、字体色
+ * @state
+ * 0: 当前有选中的节点,并且至少有一个设置了至少一种样式
+ * -1: 其它情况
+ */
+ 'clearstyle': kity.createClass('ClearStyleCommand', {
+ base: Command,
+ execute: function(minder) {
+ minder.getSelectedNodes().forEach(function(node) {
+ styleNames.forEach(function(name) {
+ node.setData(name);
+ });
+ });
+ minder.renderNodeBatch(minder.getSelectedNodes());
+ minder.layout(300);
+ return styleClipBoard;
+ },
+
+ queryState: function(minder) {
+ var nodes = minder.getSelectedNodes();
+ if (!nodes.length) return -1;
+ for (var i = 0; i < nodes.length; i++) {
+ if (hasStyle(nodes[i])) return 0;
+ }
+ return -1;
+ }
+ })
+ }
+ };
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/text.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/text.js
new file mode 100644
index 0000000..3a0f1cf
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/text.js
@@ -0,0 +1,288 @@
+define(function(require, exports, module) {
+ var kity = require('../core/kity');
+ var utils = require('../core/utils');
+
+ var Minder = require('../core/minder');
+ var MinderNode = require('../core/node');
+ var Command = require('../core/command');
+ var Module = require('../core/module');
+ var Renderer = require('../core/render');
+ /**
+ * 针对不同系统、不同浏览器、不同字体做居中兼容性处理
+ * 暂时未增加Linux的处理
+ */
+ var FONT_ADJUST = {
+ 'safari': {
+ '微软雅黑,Microsoft YaHei': -0.17,
+ '楷体,楷体_GB2312,SimKai': -0.1,
+ '隶书, SimLi': -0.1,
+ 'comic sans ms': -0.23,
+ 'impact,chicago': -0.15,
+ 'times new roman': -0.1,
+ 'arial black,avant garde': -0.17,
+ 'default': 0
+ },
+ 'ie': {
+ 10: {
+ '微软雅黑,Microsoft YaHei': -0.17,
+ 'comic sans ms': -0.17,
+ 'impact,chicago': -0.08,
+ 'times new roman': 0.04,
+ 'arial black,avant garde': -0.17,
+ 'default': -0.15
+ },
+ 11: {
+ '微软雅黑,Microsoft YaHei': -0.17,
+ 'arial,helvetica,sans-serif': -0.17,
+ 'comic sans ms': -0.17,
+ 'impact,chicago': -0.08,
+ 'times new roman': 0.04,
+ 'sans-serif': -0.16,
+ 'arial black,avant garde': -0.17,
+ 'default': -0.15
+ }
+ },
+ 'edge': {
+ '微软雅黑,Microsoft YaHei': -0.15,
+ 'arial,helvetica,sans-serif': -0.17,
+ 'comic sans ms': -0.17,
+ 'impact,chicago': -0.08,
+ 'sans-serif': -0.16,
+ 'arial black,avant garde': -0.17,
+ 'default': -0.15
+ },
+ 'sg': {
+ '微软雅黑,Microsoft YaHei': -0.15,
+ 'arial,helvetica,sans-serif': -0.05,
+ 'comic sans ms': -0.22,
+ 'impact,chicago': -0.16,
+ 'times new roman': -0.03,
+ 'arial black,avant garde': -0.22,
+ 'default': -0.15
+ },
+ 'chrome': {
+ 'Mac': {
+ 'andale mono': -0.05,
+ 'comic sans ms': -0.3,
+ 'impact,chicago': -0.13,
+ 'times new roman': -0.1,
+ 'arial black,avant garde': -0.17,
+ 'default': 0
+ },
+ 'Win': {
+ '微软雅黑,Microsoft YaHei': -0.15,
+ 'arial,helvetica,sans-serif': -0.02,
+ 'arial black,avant garde': -0.2,
+ 'comic sans ms': -0.2,
+ 'impact,chicago': -0.12,
+ 'times new roman': -0.02,
+ 'default': -0.15
+ },
+ 'Lux': {
+ 'andale mono': -0.05,
+ 'comic sans ms': -0.3,
+ 'impact,chicago': -0.13,
+ 'times new roman': -0.1,
+ 'arial black,avant garde': -0.17,
+ 'default': 0
+ }
+ },
+ 'firefox': {
+ 'Mac': {
+ '微软雅黑,Microsoft YaHei': -0.2,
+ '宋体,SimSun': 0.05,
+ 'comic sans ms': -0.2,
+ 'impact,chicago': -0.15,
+ 'arial black,avant garde': -0.17,
+ 'times new roman': -0.1,
+ 'default': 0.05
+ },
+ 'Win': {
+ '微软雅黑,Microsoft YaHei': -0.16,
+ 'andale mono': -0.17,
+ 'arial,helvetica,sans-serif': -0.17,
+ 'comic sans ms': -0.22,
+ 'impact,chicago': -0.23,
+ 'times new roman': -0.22,
+ 'sans-serif': -0.22,
+ 'arial black,avant garde': -0.17,
+ 'default': -0.16
+ },
+ 'Lux': {
+ "宋体,SimSun": -0.2,
+ "微软雅黑,Microsoft YaHei": -0.2,
+ "黑体, SimHei": -0.2,
+ "隶书, SimLi": -0.2,
+ "楷体,楷体_GB2312,SimKai": -0.2,
+ "andale mono": -0.2,
+ "arial,helvetica,sans-serif": -0.2,
+ "comic sans ms": -0.2,
+ "impact,chicago": -0.2,
+ "times new roman": -0.2,
+ "sans-serif": -0.2,
+ "arial black,avant garde": -0.2,
+ "default": -0.16
+ }
+ },
+ };
+
+ var TextRenderer = kity.createClass('TextRenderer', {
+ base: Renderer,
+
+ create: function() {
+ return new kity.Group().setId(utils.uuid('node_text'));
+ },
+
+ update: function(textGroup, node) {
+
+ function getDataOrStyle(name) {
+ return node.getData(name) || node.getStyle(name);
+ }
+
+ var nodeText = node.getText();
+ var textArr = nodeText ? nodeText.split('\n') : [' '];
+
+ var lineHeight = node.getStyle('line-height');
+
+ var fontSize = getDataOrStyle('font-size');
+ var fontFamily = getDataOrStyle('font-family') || 'default';
+
+ var height = (lineHeight * fontSize) * textArr.length - (lineHeight - 1) * fontSize;
+ var yStart = -height / 2;
+ var Browser = kity.Browser;
+ var adjust;
+
+ if (Browser.chrome || Browser.opera || Browser.bd ||Browser.lb === "chrome") {
+ adjust = FONT_ADJUST['chrome'][Browser.platform][fontFamily];
+ } else if (Browser.gecko) {
+ adjust = FONT_ADJUST['firefox'][Browser.platform][fontFamily];
+ } else if (Browser.sg) {
+ adjust = FONT_ADJUST['sg'][fontFamily];
+ } else if (Browser.safari) {
+ adjust = FONT_ADJUST['safari'][fontFamily];
+ } else if (Browser.ie) {
+ adjust = FONT_ADJUST['ie'][Browser.version][fontFamily];
+ } else if (Browser.edge) {
+ adjust = FONT_ADJUST['edge'][fontFamily];
+ } else if (Browser.lb) {
+ // 猎豹浏览器的ie内核兼容性模式下
+ adjust = 0.9;
+ }
+
+ textGroup.setTranslate(0, (adjust || 0) * fontSize);
+
+ var rBox = new kity.Box(),
+ r = Math.round;
+
+ this.setTextStyle(node, textGroup);
+
+ var textLength = textArr.length;
+
+ var textGroupLength = textGroup.getItems().length;
+
+ var i, ci, textShape, text;
+
+ if (textLength < textGroupLength) {
+ for (i = textLength, ci; ci = textGroup.getItem(i);) {
+ textGroup.removeItem(i);
+ }
+ } else if (textLength > textGroupLength) {
+ var growth = textLength - textGroupLength;
+ while (growth--) {
+ textShape = new kity.Text()
+ .setAttr('text-rendering', 'inherit');
+ if (kity.Browser.ie || kity.Browser.edge) {
+ textShape.setVerticalAlign('top');
+ } else {
+ textShape.setAttr('dominant-baseline', 'text-before-edge');
+ }
+ textGroup.addItem(textShape);
+ }
+ }
+
+ for (i = 0, text, textShape;
+ (text = textArr[i], textShape = textGroup.getItem(i)); i++) {
+ textShape.setContent(text);
+ if (kity.Browser.ie || kity.Browser.edge) {
+ textShape.fixPosition();
+ }
+ }
+
+ this.setTextStyle(node, textGroup);
+
+ var textHash = node.getText() +
+ ['font-size', 'font-name', 'font-weight', 'font-style'].map(getDataOrStyle).join('/');
+
+ if (node._currentTextHash == textHash && node._currentTextGroupBox) return node._currentTextGroupBox;
+
+ node._currentTextHash = textHash;
+
+ return function() {
+ textGroup.eachItem(function(i, textShape) {
+ var y = yStart + i * fontSize * lineHeight;
+
+ textShape.setY(y);
+ var bbox = textShape.getBoundaryBox();
+ rBox = rBox.merge(new kity.Box(0, y, bbox.height && bbox.width || 1, fontSize));
+ });
+
+ var nBox = new kity.Box(r(rBox.x), r(rBox.y), r(rBox.width), r(rBox.height));
+
+ node._currentTextGroupBox = nBox;
+ return nBox;
+ };
+
+ },
+
+ setTextStyle: function(node, text) {
+ var hooks = TextRenderer._styleHooks;
+ hooks.forEach(function(hook) {
+ hook(node, text);
+ });
+ }
+ });
+
+ var TextCommand = kity.createClass({
+ base: Command,
+ execute: function(minder, text) {
+ var node = minder.getSelectedNode();
+ if (node) {
+ node.setText(text);
+ node.render();
+ minder.layout();
+ }
+ },
+ queryState: function(minder) {
+ return minder.getSelectedNodes().length == 1 ? 0 : -1;
+ },
+ queryValue: function(minder) {
+ var node = minder.getSelectedNode();
+ return node ? node.getText() : null;
+ }
+ });
+
+ utils.extend(TextRenderer, {
+ _styleHooks: [],
+
+ registerStyleHook: function(fn) {
+ TextRenderer._styleHooks.push(fn);
+ }
+ });
+
+ kity.extendClass(MinderNode, {
+ getTextGroup: function() {
+ return this.getRenderer('TextRenderer').getRenderShape();
+ }
+ });
+
+ Module.register('text', {
+ 'commands': {
+ 'text': TextCommand
+ },
+ 'renderers': {
+ center: TextRenderer
+ }
+ });
+
+ module.exports = TextRenderer;
+});
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/view.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/view.js
new file mode 100644
index 0000000..c522172
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/view.js
@@ -0,0 +1,397 @@
+define(function(require, exports, module) {
+ var kity = require('../core/kity');
+ var utils = require('../core/utils');
+
+ var Minder = require('../core/minder');
+ var MinderNode = require('../core/node');
+ var Command = require('../core/command');
+ var Module = require('../core/module');
+ var Renderer = require('../core/render');
+
+ var ViewDragger = kity.createClass('ViewDragger', {
+ constructor: function(minder) {
+ this._minder = minder;
+ this._enabled = false;
+ this._bind();
+ var me = this;
+ this._minder.getViewDragger = function() {
+ return me;
+ };
+ this.setEnabled(false);
+ },
+
+ isEnabled: function() {
+ return this._enabled;
+ },
+
+ setEnabled: function(value) {
+ var paper = this._minder.getPaper();
+ paper.setStyle('cursor', value ? 'pointer' : 'default');
+ paper.setStyle('cursor', value ? '-webkit-grab' : 'default');
+ this._enabled = value;
+ },
+ timeline: function() {
+ return this._moveTimeline;
+ },
+
+ move: function(offset, duration) {
+ var minder = this._minder;
+
+ var targetPosition = this.getMovement().offset(offset);
+
+ this.moveTo(targetPosition, duration);
+ },
+
+ moveTo: function(position, duration) {
+
+ if (duration) {
+ var dragger = this;
+
+ if (this._moveTimeline) this._moveTimeline.stop();
+
+ this._moveTimeline = this._minder.getRenderContainer().animate(new kity.Animator(
+ this.getMovement(),
+ position,
+ function(target, value) {
+ dragger.moveTo(value);
+ }
+ ), duration, 'easeOutCubic').timeline();
+
+ this._moveTimeline.on('finish', function() {
+ dragger._moveTimeline = null;
+ });
+
+ return this;
+ }
+
+ this._minder.getRenderContainer().setTranslate(position.round());
+ this._minder.fire('viewchange');
+ },
+
+ getMovement: function() {
+ var translate = this._minder.getRenderContainer().transform.translate;
+ return translate ? translate[0] : new kity.Point();
+ },
+
+ getView: function() {
+ var minder = this._minder;
+ var c = minder._lastClientSize || {
+ width: minder.getRenderTarget().clientWidth,
+ height: minder.getRenderTarget().clientHeight
+ };
+ var m = this.getMovement();
+ var box = new kity.Box(0, 0, c.width, c.height);
+ var viewMatrix = minder.getPaper().getViewPortMatrix();
+ return viewMatrix.inverse().translate(-m.x, -m.y).transformBox(box);
+ },
+
+ _bind: function() {
+ var dragger = this,
+ isTempDrag = false,
+ lastPosition = null,
+ currentPosition = null;
+
+ function dragEnd(e) {
+ if (!lastPosition) return;
+
+
+ lastPosition = null;
+
+ e.stopPropagation();
+
+ // 临时拖动需要还原状态
+ if (isTempDrag) {
+ dragger.setEnabled(false);
+ isTempDrag = false;
+ if (dragger._minder.getStatus() == 'hand')
+ dragger._minder.rollbackStatus();
+ }
+ var paper = dragger._minder.getPaper();
+ paper.setStyle('cursor', dragger._minder.getStatus() == 'hand' ? '-webkit-grab' : 'default');
+
+ dragger._minder.fire('viewchanged');
+ }
+
+ this._minder.on('normal.mousedown normal.touchstart ' +
+ 'inputready.mousedown inputready.touchstart ' +
+ 'readonly.mousedown readonly.touchstart',
+ function(e) {
+ if (e.originEvent.button == 2) {
+ e.originEvent.preventDefault(); // 阻止中键拉动
+ }
+ // 点击未选中的根节点临时开启
+ if (e.getTargetNode() == this.getRoot() || e.originEvent.button == 2 || e.originEvent.altKey) {
+ lastPosition = e.getPosition('view');
+ isTempDrag = true;
+ }
+ })
+
+ .on('normal.mousemove normal.touchmove ' +
+ 'readonly.mousemove readonly.touchmove ' +
+ 'inputready.mousemove inputready.touchmove',
+ function(e) {
+ if (e.type == 'touchmove') {
+ e.preventDefault(); // 阻止浏览器的后退事件
+ }
+ if (!isTempDrag) return;
+ var offset = kity.Vector.fromPoints(lastPosition, e.getPosition('view'));
+ if (offset.length() > 10) {
+ this.setStatus('hand', true);
+ var paper = dragger._minder.getPaper();
+ paper.setStyle('cursor', '-webkit-grabbing');
+ }
+ })
+
+ .on('hand.beforemousedown hand.beforetouchstart', function(e) {
+ // 已经被用户打开拖放模式
+ if (dragger.isEnabled()) {
+ lastPosition = e.getPosition('view');
+ e.stopPropagation();
+ var paper = dragger._minder.getPaper();
+ paper.setStyle('cursor', '-webkit-grabbing');
+ }
+ })
+
+ .on('hand.beforemousemove hand.beforetouchmove', function(e) {
+ if (lastPosition) {
+ currentPosition = e.getPosition('view');
+
+ // 当前偏移加上历史偏移
+ var offset = kity.Vector.fromPoints(lastPosition, currentPosition);
+ dragger.move(offset);
+ e.stopPropagation();
+ e.preventDefault();
+ e.originEvent.preventDefault();
+ lastPosition = currentPosition;
+ }
+ })
+
+ .on('mouseup touchend', dragEnd);
+
+ window.addEventListener('mouseup', dragEnd);
+ this._minder.on('contextmenu', function(e) {
+ e.preventDefault();
+ });
+ }
+ });
+
+ Module.register('View', function() {
+
+ var km = this;
+
+ /**
+ * @command Hand
+ * @description 切换抓手状态,抓手状态下,鼠标拖动将拖动视野,而不是创建选区
+ * @state
+ * 0: 当前不是抓手状态
+ * 1: 当前是抓手状态
+ */
+ var ToggleHandCommand = kity.createClass('ToggleHandCommand', {
+ base: Command,
+ execute: function(minder) {
+
+ if (minder.getStatus() != 'hand') {
+ minder.setStatus('hand', true);
+ } else {
+ minder.rollbackStatus();
+ }
+ this.setContentChanged(false);
+
+ },
+ queryState: function(minder) {
+ return minder.getStatus() == 'hand' ? 1 : 0;
+ },
+ enableReadOnly: true
+ });
+
+ /**
+ * @command Camera
+ * @description 设置当前视野的中心位置到某个节点上
+ * @param {kityminder.MinderNode} focusNode 要定位的节点
+ * @param {number} duration 设置视野移动的动画时长(单位 ms),设置为 0 不使用动画
+ * @state
+ * 0: 始终可用
+ */
+ var CameraCommand = kity.createClass('CameraCommand', {
+ base: Command,
+ execute: function(km, focusNode) {
+
+ focusNode = focusNode || km.getRoot();
+ var viewport = km.getPaper().getViewPort();
+ var offset = focusNode.getRenderContainer().getRenderBox('view');
+ var dx = viewport.center.x - offset.x - offset.width / 2,
+ dy = viewport.center.y - offset.y;
+ var dragger = km._viewDragger;
+
+ var duration = km.getOption('viewAnimationDuration');
+ dragger.move(new kity.Point(dx, dy), duration);
+ this.setContentChanged(false);
+ },
+ enableReadOnly: true
+ });
+
+ /**
+ * @command Move
+ * @description 指定方向移动当前视野
+ * @param {string} dir 移动方向
+ * 取值为 'left',视野向左移动一半
+ * 取值为 'right',视野向右移动一半
+ * 取值为 'up',视野向上移动一半
+ * 取值为 'down',视野向下移动一半
+ * @param {number} duration 视野移动的动画时长(单位 ms),设置为 0 不使用动画
+ * @state
+ * 0: 始终可用
+ */
+ var MoveCommand = kity.createClass('MoveCommand', {
+ base: Command,
+
+ execute: function(km, dir) {
+ var dragger = km._viewDragger;
+ var size = km._lastClientSize;
+ var duration = km.getOption('viewAnimationDuration');
+ switch (dir) {
+ case 'up':
+ dragger.move(new kity.Point(0, size.height / 2), duration);
+ break;
+ case 'down':
+ dragger.move(new kity.Point(0, -size.height / 2), duration);
+ break;
+ case 'left':
+ dragger.move(new kity.Point(size.width / 2, 0), duration);
+ break;
+ case 'right':
+ dragger.move(new kity.Point(-size.width / 2, 0), duration);
+ break;
+ }
+ },
+
+ enableReadOnly: true
+ });
+
+ return {
+ init: function() {
+ this._viewDragger = new ViewDragger(this);
+ },
+ commands: {
+ 'hand': ToggleHandCommand,
+ 'camera': CameraCommand,
+ 'move': MoveCommand
+ },
+ events: {
+ statuschange: function(e) {
+ this._viewDragger.setEnabled(e.currentStatus == 'hand');
+ },
+ mousewheel: function(e) {
+ var dx, dy;
+ e = e.originEvent;
+ if (e.ctrlKey || e.shiftKey) return;
+ if ('wheelDeltaX' in e) {
+
+ dx = e.wheelDeltaX || 0;
+ dy = e.wheelDeltaY || 0;
+
+ } else {
+
+ dx = 0;
+ dy = e.wheelDelta;
+
+ }
+
+ this._viewDragger.move({
+ x: dx / 2.5,
+ y: dy / 2.5
+ });
+
+ var me = this;
+ clearTimeout(this._mousewheeltimer);
+ this._mousewheeltimer = setTimeout(function() {
+ me.fire('viewchanged');
+ }, 100);
+
+ e.preventDefault();
+ },
+ 'normal.dblclick readonly.dblclick': function(e) {
+ if (e.kityEvent.targetShape instanceof kity.Paper) {
+ this.execCommand('camera', this.getRoot(), 800);
+ }
+ },
+ 'paperrender finishInitHook': function() {
+ if (!this.getRenderTarget()) {
+ return;
+ }
+ this.execCommand('camera', null, 0);
+ this._lastClientSize = {
+ width: this.getRenderTarget().clientWidth,
+ height: this.getRenderTarget().clientHeight
+ };
+ },
+ resize: function(e) {
+ var a = {
+ width: this.getRenderTarget().clientWidth,
+ height: this.getRenderTarget().clientHeight
+ },
+ b = this._lastClientSize;
+ this._viewDragger.move(
+ new kity.Point((a.width - b.width) / 2 | 0, (a.height - b.height) / 2 | 0));
+ this._lastClientSize = a;
+ },
+ 'selectionchange layoutallfinish': function(e) {
+ var selected = this.getSelectedNode();
+ var minder = this;
+
+ /*
+ * Added by zhangbobell 2015.9.9
+ * windows 10 的 edge 浏览器在全部动画停止后,优先级图标不显示 text,
+ * 因此再次触发一次 render 事件,让浏览器重绘
+ * */
+ if (kity.Browser.edge) {
+ this.fire('paperrender');
+ }
+ if (!selected) return;
+
+ var dragger = this._viewDragger;
+ var timeline = dragger.timeline();
+
+ /*
+ * Added by zhangbobell 2015.09.25
+ * 如果之前有动画,那么就先暂时返回,等之前动画结束之后再次执行本函数
+ * 以防止 view 动画变动了位置,导致本函数执行的时候位置计算不对
+ *
+ * fixed bug : 初始化的时候中心节点位置不固定(有的时候在左上角,有的时候在中心)
+ * */
+ if (timeline){
+ timeline.on('finish', function() {
+ minder.fire('selectionchange');
+ });
+
+ return;
+ }
+
+
+ var view = dragger.getView();
+ var focus = selected.getLayoutBox();
+ var space = 50;
+ var dx = 0, dy = 0;
+
+ if (focus.right > view.right) {
+ dx += view.right - focus.right - space;
+ }
+ else if (focus.left < view.left) {
+ dx += view.left - focus.left + space;
+ }
+
+ if (focus.bottom > view.bottom) {
+ dy += view.bottom - focus.bottom - space;
+ }
+ if (focus.top < view.top) {
+ dy += view.top - focus.top + space;
+ }
+
+ if (dx || dy) dragger.move(new kity.Point(dx, dy), 100);
+
+
+ }
+ }
+ };
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/zoom.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/zoom.js
new file mode 100644
index 0000000..9a784f9
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/module/zoom.js
@@ -0,0 +1,200 @@
+define(function(require, exports, module) {
+ var kity = require('../core/kity');
+ var utils = require('../core/utils');
+
+ var Minder = require('../core/minder');
+ var MinderNode = require('../core/node');
+ var Command = require('../core/command');
+ var Module = require('../core/module');
+ var Renderer = require('../core/render');
+
+ Module.register('Zoom', function() {
+ var me = this;
+
+ var timeline;
+
+ function setTextRendering() {
+ var value = me._zoomValue >= 100 ? 'optimize-speed' : 'geometricPrecision';
+ me.getRenderContainer().setAttr('text-rendering', value);
+ }
+
+ function fixPaperCTM(paper) {
+ var node = paper.shapeNode;
+ var ctm = node.getCTM();
+ var matrix = new kity.Matrix(ctm.a, ctm.b, ctm.c, ctm.d, (ctm.e | 0) + 0.5, (ctm.f | 0) + 0.5);
+ node.setAttribute('transform', 'matrix(' + matrix.toString() + ')');
+ }
+
+ kity.extendClass(Minder, {
+ zoom: function(value) {
+ var paper = this.getPaper();
+ var viewport = paper.getViewPort();
+ viewport.zoom = value / 100;
+ viewport.center = {
+ x: viewport.center.x,
+ y: viewport.center.y
+ };
+ paper.setViewPort(viewport);
+ if (value == 100) fixPaperCTM(paper);
+ },
+ getZoomValue: function() {
+ return this._zoomValue;
+ }
+ });
+
+ function zoomMinder(minder, value) {
+ var paper = minder.getPaper();
+ var viewport = paper.getViewPort();
+
+ if (!value) return;
+
+ setTextRendering();
+
+ var duration = minder.getOption('zoomAnimationDuration');
+ if (minder.getRoot().getComplex() > 200 || !duration) {
+ minder._zoomValue = value;
+ minder.zoom(value);
+ minder.fire('viewchange');
+ } else {
+ var animator = new kity.Animator({
+ beginValue: minder._zoomValue,
+ finishValue: value,
+ setter: function(target, value) {
+ target.zoom(value);
+ }
+ });
+ minder._zoomValue = value;
+ if (timeline) {
+ timeline.pause();
+ }
+ timeline = animator.start(minder, duration, 'easeInOutSine');
+ timeline.on('finish', function() {
+ minder.fire('viewchange');
+ });
+ }
+ minder.fire('zoom', {
+ zoom: value
+ });
+ }
+
+ /**
+ * @command Zoom
+ * @description 缩放当前的视野到一定的比例(百分比)
+ * @param {number} value 设置的比例,取值 100 则为原尺寸
+ * @state
+ * 0: 始终可用
+ */
+ var ZoomCommand = kity.createClass('Zoom', {
+ base: Command,
+ execute: zoomMinder,
+ queryValue: function(minder) {
+ return minder._zoomValue;
+ }
+ });
+
+ /**
+ * @command ZoomIn
+ * @description 放大当前的视野到下一个比例等级(百分比)
+ * @shortcut =
+ * @state
+ * 0: 如果当前脑图的配置中还有下一个比例等级
+ * -1: 其它情况
+ */
+ var ZoomInCommand = kity.createClass('ZoomInCommand', {
+ base: Command,
+ execute: function(minder) {
+ zoomMinder(minder, this.nextValue(minder));
+ },
+ queryState: function(minder) {
+ return +!this.nextValue(minder);
+ },
+ nextValue: function(minder) {
+ var stack = minder.getOption('zoom'),
+ i;
+ for (i = 0; i < stack.length; i++) {
+ if (stack[i] > minder._zoomValue) return stack[i];
+ }
+ return 0;
+ },
+ enableReadOnly: true
+ });
+
+ /**
+ * @command ZoomOut
+ * @description 缩小当前的视野到上一个比例等级(百分比)
+ * @shortcut -
+ * @state
+ * 0: 如果当前脑图的配置中还有上一个比例等级
+ * -1: 其它情况
+ */
+ var ZoomOutCommand = kity.createClass('ZoomOutCommand', {
+ base: Command,
+ execute: function(minder) {
+ zoomMinder(minder, this.nextValue(minder));
+ },
+ queryState: function(minder) {
+ return +!this.nextValue(minder);
+ },
+ nextValue: function(minder) {
+ var stack = minder.getOption('zoom'),
+ i;
+ for (i = stack.length - 1; i >= 0; i--) {
+ if (stack[i] < minder._zoomValue) return stack[i];
+ }
+ return 0;
+ },
+ enableReadOnly: true
+ });
+
+ return {
+ init: function() {
+ this._zoomValue = 100;
+ this.setDefaultOptions({
+ zoom: [10, 20, 50, 100, 200]
+ });
+ setTextRendering();
+ },
+ commands: {
+ 'zoomin': ZoomInCommand,
+ 'zoomout': ZoomOutCommand,
+ 'zoom': ZoomCommand
+ },
+ events: {
+ 'normal.mousewheel readonly.mousewheel': function(e) {
+ if (!e.originEvent.ctrlKey && !e.originEvent.metaKey) return;
+
+ var delta = e.originEvent.wheelDelta;
+ var me = this;
+
+ if (!kity.Browser.mac) {
+ delta = -delta;
+ }
+
+ // 稀释
+ if (Math.abs(delta) > 100) {
+ clearTimeout(this._wheelZoomTimeout);
+ } else {
+ return;
+ }
+
+ this._wheelZoomTimeout = setTimeout(function() {
+ var value;
+ var lastValue = me.getPaper()._zoom || 1;
+ if (delta < 0) {
+ me.execCommand('zoomin');
+ } else if (delta > 0) {
+ me.execCommand('zoomout');
+ }
+ }, 100);
+
+ e.originEvent.preventDefault();
+ }
+ },
+
+ commandShortcutKeys: {
+ 'zoomin': 'ctrl+=',
+ 'zoomout': 'ctrl+-'
+ }
+ };
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/protocol/json.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/protocol/json.js
new file mode 100644
index 0000000..2f9657d
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/protocol/json.js
@@ -0,0 +1,18 @@
+define(function(require, exports, module) {
+ var data = require('../core/data');
+
+ data.registerProtocol('json', module.exports = {
+ fileDescription: 'KityMinder 格式',
+ fileExtension: '.km',
+ dataType: 'text',
+ mineType: 'application/json',
+
+ encode: function(json) {
+ return JSON.stringify(json);
+ },
+
+ decode: function(local) {
+ return JSON.parse(local);
+ }
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/protocol/markdown.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/protocol/markdown.js
new file mode 100644
index 0000000..3029f29
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/protocol/markdown.js
@@ -0,0 +1,158 @@
+define(function(require, exports, module) {
+ var data = require('../core/data');
+ var LINE_ENDING_SPLITER = /\r\n|\r|\n/;
+ var EMPTY_LINE = '';
+ var NOTE_MARK_START = '';
+ var NOTE_MARK_CLOSE = '';
+
+ function encode(json) {
+
+ return _build(json, 1).join('\n');
+ }
+
+ function _build(node, level) {
+ var lines = [];
+
+ level = level || 1;
+
+ var sharps = _generateHeaderSharp(level);
+ lines.push(sharps + ' ' + node.data.text);
+ lines.push(EMPTY_LINE);
+
+ var note = node.data.note;
+ if (note) {
+ var hasSharp = /^#/.test(note);
+ if (hasSharp) {
+ lines.push(NOTE_MARK_START);
+ note = note.replace(/^#+/gm, function($0) {
+ return sharps + $0;
+ });
+ }
+ lines.push(note);
+ if (hasSharp) {
+ lines.push(NOTE_MARK_CLOSE);
+ }
+ lines.push(EMPTY_LINE);
+ }
+
+ if (node.children) node.children.forEach(function(child) {
+ lines = lines.concat(_build(child, level + 1));
+ });
+
+ return lines;
+ }
+
+ function _generateHeaderSharp(level) {
+ var sharps = '';
+ while (level--) sharps += '#';
+ return sharps;
+ }
+
+ function decode(markdown) {
+
+ var json,
+ parentMap = {},
+ lines, line, lineInfo, level, node, parent, noteProgress, codeBlock;
+
+ // 一级标题转换 `{title}\n===` => `# {title}`
+ markdown = markdown.replace(/^(.+)\n={3,}/, function($0, $1) {
+ return '# ' + $1;
+ });
+
+ lines = markdown.split(LINE_ENDING_SPLITER);
+
+ // 按行分析
+ for (var i = 0; i < lines.length; i++) {
+ line = lines[i];
+
+ lineInfo = _resolveLine(line);
+
+ // 备注标记处理
+ if (lineInfo.noteClose) {
+ noteProgress = false;
+ continue;
+ } else if (lineInfo.noteStart) {
+ noteProgress = true;
+ continue;
+ }
+
+ // 代码块处理
+ codeBlock = lineInfo.codeBlock ? !codeBlock : codeBlock;
+
+ // 备注条件:备注标签中,非标题定义,或标题越位
+ if (noteProgress || codeBlock || !lineInfo.level || lineInfo.level > level + 1) {
+ if (node) _pushNote(node, line);
+ continue;
+ }
+
+ // 标题处理
+ level = lineInfo.level;
+ node = _initNode(lineInfo.content, parentMap[level - 1]);
+ parentMap[level] = node;
+ }
+
+ _cleanUp(parentMap[1]);
+ return parentMap[1];
+ }
+
+ function _initNode(text, parent) {
+ var node = {
+ data: {
+ text: text,
+ note: ''
+ }
+ };
+ if (parent) {
+ if (parent.children) parent.children.push(node);
+ else parent.children = [node];
+ }
+ return node;
+ }
+
+ function _pushNote(node, line) {
+ node.data.note += line + '\n';
+ }
+
+ function _isEmpty(line) {
+ return !/\S/.test(line);
+ }
+
+ function _resolveLine(line) {
+ var match = /^(#+)?\s*(.*)$/.exec(line);
+ return {
+ level: match[1] && match[1].length || null,
+ content: match[2],
+ noteStart: line == NOTE_MARK_START,
+ noteClose: line == NOTE_MARK_CLOSE,
+ codeBlock: /^\s*```/.test(line)
+ };
+ }
+
+ function _cleanUp(node) {
+ if (!/\S/.test(node.data.note)) {
+ node.data.note = null;
+ delete node.data.note;
+ } else {
+ var notes = node.data.note.split('\n');
+ while (notes.length && !/\S/.test(notes[0])) notes.shift();
+ while (notes.length && !/\S/.test(notes[notes.length - 1])) notes.pop();
+ node.data.note = notes.join('\n');
+ }
+ if (node.children) node.children.forEach(_cleanUp);
+ }
+
+ data.registerProtocol('markdown', module.exports = {
+ fileDescription: 'Markdown/GFM 格式',
+ fileExtension: '.md',
+ mineType: 'text/markdown',
+ dataType: 'text',
+
+ encode: function(json) {
+ return encode(json.root);
+ },
+
+ decode: function(markdown) {
+ return decode(markdown);
+ }
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/protocol/png.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/protocol/png.js
new file mode 100644
index 0000000..22096c8
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/protocol/png.js
@@ -0,0 +1,273 @@
+define(function(require, exports, module) {
+ var kity = require('../core/kity');
+ var data = require('../core/data');
+ var Promise = require('../core/promise');
+
+ var DomURL = window.URL || window.webkitURL || window;
+
+ function loadImage(info, callback) {
+ return new Promise(function(resolve, reject) {
+ var image = document.createElement("img");
+ image.onload = function() {
+ resolve({
+ element: this,
+ x: info.x,
+ y: info.y,
+ width: info.width,
+ height: info.height
+ });
+ };
+ image.onerror = function(err) {
+ reject(err);
+ };
+
+ image.crossOrigin = 'anonymous';
+ image.src = info.url;
+ });
+ }
+
+ /**
+ * xhrLoadImage: 通过 xhr 加载保存在 BOS 上的图片
+ * @note: BOS 上的 CORS 策略是取 headers 里面的 Origin 字段进行判断
+ * 而通过 image 的 src 的方式是无法传递 origin 的,因此需要通过 xhr 进行
+ */
+ function xhrLoadImage(info, callback) {
+ return Promise(function (resolve, reject) {
+ var xmlHttp = new XMLHttpRequest();
+
+ xmlHttp.open('GET', info.url + '?_=' + Date.now(), true);
+ xmlHttp.responseType = 'blob';
+ xmlHttp.onreadystatechange = function () {
+ if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
+ var blob = xmlHttp.response;
+
+ var image = document.createElement('img');
+
+ image.src = DomURL.createObjectURL(blob);
+ image.onload = function () {
+ DomURL.revokeObjectURL(image.src);
+ resolve({
+ element: image,
+ x: info.x,
+ y: info.y,
+ width: info.width,
+ height: info.height
+ });
+ };
+ }
+ };
+
+ xmlHttp.send();
+ });
+ }
+
+ function getSVGInfo(minder) {
+ var paper = minder.getPaper(),
+ paperTransform,
+ domContainer = paper.container,
+ svgXml,
+ svgContainer,
+ svgDom,
+
+ renderContainer = minder.getRenderContainer(),
+ renderBox = renderContainer.getRenderBox(),
+ width = renderBox.width + 1,
+ height = renderBox.height + 1,
+
+ blob, svgUrl, img;
+
+ // 保存原始变换,并且移动到合适的位置
+ paperTransform = paper.shapeNode.getAttribute('transform');
+ paper.shapeNode.setAttribute('transform', 'translate(0.5, 0.5)');
+ renderContainer.translate(-renderBox.x, -renderBox.y);
+
+ // 获取当前的 XML 代码
+ svgXml = paper.container.innerHTML;
+
+ // 回复原始变换及位置
+ renderContainer.translate(renderBox.x, renderBox.y);
+ paper.shapeNode.setAttribute('transform', paperTransform);
+
+ // 过滤内容
+ svgContainer = document.createElement('div');
+ svgContainer.innerHTML = svgXml;
+ svgDom = svgContainer.querySelector('svg');
+ svgDom.setAttribute('width', renderBox.width + 1);
+ svgDom.setAttribute('height', renderBox.height + 1);
+ svgDom.setAttribute('style', 'font-family: Arial, "Microsoft Yahei","Heiti SC";');
+
+ svgContainer = document.createElement('div');
+ svgContainer.appendChild(svgDom);
+
+ svgXml = svgContainer.innerHTML;
+
+ // Dummy IE
+ svgXml = svgXml.replace(' xmlns="http://www.w3.org/2000/svg" ' +
+ 'xmlns:NS1="" NS1:ns1:xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:NS2="" NS2:xmlns:ns1=""', '');
+
+ // svg 含有 符号导出报错 Entity 'nbsp' not defined ,含有控制字符触发Load Image 会触发报错
+ svgXml = svgXml.replace(/ |[\x00-\x1F\x7F-\x9F]/g, "");
+
+ // fix title issue in safari
+ // @ http://stackoverflow.com/questions/30273775/namespace-prefix-ns1-for-href-on-tagelement-is-not-defined-setattributens
+ svgXml = svgXml.replace(/NS\d+:title/gi, 'xlink:title');
+
+ blob = new Blob([svgXml], {
+ type: 'image/svg+xml'
+ });
+
+ svgUrl = DomURL.createObjectURL(blob);
+
+ //svgUrl = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svgXml);
+
+ var imagesInfo = [];
+
+ // 遍历取出图片信息
+ traverse(minder.getRoot());
+
+ function traverse(node) {
+ var nodeData = node.data;
+
+ if (nodeData.image) {
+ minder.renderNode(node);
+ var nodeData = node.data;
+ var imageUrl = nodeData.image;
+ var imageSize = nodeData.imageSize;
+ var imageRenderBox = node.getRenderBox("ImageRenderer", minder.getRenderContainer());
+ var imageInfo = {
+ url: imageUrl,
+ width: imageSize.width,
+ height: imageSize.height,
+ x: -renderContainer.getBoundaryBox().x + imageRenderBox.x,
+ y: -renderContainer.getBoundaryBox().y + imageRenderBox.y
+ };
+
+ imagesInfo.push(imageInfo);
+ }
+
+ // 若节点折叠,则直接返回
+ if (nodeData.expandState === 'collapse') {
+ return;
+ }
+
+ var children = node.getChildren();
+ for (var i = 0; i < children.length; i++) {
+ traverse(children[i]);
+ }
+ }
+
+ return {
+ width: width,
+ height: height,
+ dataUrl: svgUrl,
+ xml: svgXml,
+ imagesInfo: imagesInfo
+ };
+ }
+
+
+ function encode(json, minder, option) {
+
+ var resultCallback;
+
+ /* 绘制 PNG 的画布及上下文 */
+ var canvas = document.createElement('canvas');
+ var ctx = canvas.getContext('2d');
+
+ /* 尝试获取背景图片 URL 或背景颜色 */
+ var bgDeclare = minder.getStyle('background').toString();
+ var bgUrl = /url\(\"(.+)\"\)/.exec(bgDeclare);
+ var bgColor = kity.Color.parse(bgDeclare);
+
+ /* 获取 SVG 文件内容 */
+ var svgInfo = getSVGInfo(minder);
+ var width = option && option.width && option.width > svgInfo.width ? option.width : svgInfo.width;
+ var height = option && option.height && option.height > svgInfo.height ? option.height : svgInfo.height;
+ var offsetX = option && option.width && option.width > svgInfo.width ? (option.width - svgInfo.width)/2 : 0;
+ var offsetY = option && option.height && option.height > svgInfo.height ? (option.height - svgInfo.height)/2 : 0;
+ var svgDataUrl = svgInfo.dataUrl;
+ var imagesInfo = svgInfo.imagesInfo;
+
+ /* 画布的填充大小 */
+ var padding = 20;
+
+ canvas.width = width + padding * 2;
+ canvas.height = height + padding * 2;
+
+ function fillBackground(ctx, style) {
+ ctx.save();
+ ctx.fillStyle = style;
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+ ctx.restore();
+ }
+
+ function drawImage(ctx, image, x, y, width, height) {
+ if (width && height) {
+ ctx.drawImage(image, x + padding, y + padding, width, height);
+ } else {
+ ctx.drawImage(image, x + padding, y + padding);
+ }
+ }
+
+ function generateDataUrl(canvas) {
+ return canvas.toDataURL('image/png');
+ }
+
+ // 加载节点上的图片
+ function loadImages(imagesInfo) {
+ var imagePromises = imagesInfo.map(function(imageInfo) {
+ return xhrLoadImage(imageInfo);
+ });
+
+ return Promise.all(imagePromises);
+ }
+
+ function drawSVG() {
+ var svgData = {url: svgDataUrl};
+
+ return loadImage(svgData).then(function($image) {
+ drawImage(ctx, $image.element, offsetX, offsetY, $image.width, $image.height);
+ return loadImages(imagesInfo);
+ }).then(function($images) {
+ for(var i = 0; i < $images.length; i++) {
+ drawImage(ctx, $images[i].element, $images[i].x + offsetX, $images[i].y + offsetY, $images[i].width, $images[i].height);
+ }
+
+ DomURL.revokeObjectURL(svgDataUrl);
+ document.body.appendChild(canvas);
+ var pngBase64 = generateDataUrl(canvas);
+
+ document.body.removeChild(canvas);
+ return pngBase64;
+ }, function(err) {
+ // 这里处理 reject,出错基本上是因为跨域,
+ // 出错后依然导出,只不过没有图片。
+ alert('脑图的节点中包含跨域图片,导出的 png 中节点图片不显示,你可以替换掉这些跨域的图片并重试。');
+ DomURL.revokeObjectURL(svgDataUrl);
+ document.body.appendChild(canvas);
+
+ var pngBase64 = generateDataUrl(canvas);
+ document.body.removeChild(canvas);
+ return pngBase64;
+ });
+ }
+
+ if (bgUrl) {
+ var bgInfo = {url: bgUrl[1]};
+ return loadImage(bgInfo).then(function($image) {
+ fillBackground(ctx, ctx.createPattern($image.element, "repeat"));
+ return drawSVG();
+ });
+ } else {
+ fillBackground(ctx, bgColor.toString());
+ return drawSVG();
+ }
+ }
+ data.registerProtocol("png", module.exports = {
+ fileDescription: "PNG 图片",
+ fileExtension: ".png",
+ mineType: "image/png",
+ dataType: "base64",
+ encode: encode
+ });
+});
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/protocol/svg.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/protocol/svg.js
new file mode 100644
index 0000000..96a25e0
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/protocol/svg.js
@@ -0,0 +1,243 @@
+define(function(require, exports, module) {
+ var data = require('../core/data');
+
+ /**
+ * 导出svg时删除全部svg元素中的transform
+ * @auth Naixor
+ * @method removeTransform
+ * @param {[type]} svgDom [description]
+ * @return {[type]} [description]
+ */
+ function cleanSVG(svgDom, x, y) {
+ function getTransformToElement(target, source) {
+ var matrix;
+ try {
+ matrix = source.getScreenCTM().inverse();
+ } catch (e) {
+ throw new Error("Can not inverse source element' ctm.");
+ }
+ return matrix.multiply(target.getScreenCTM());
+ }
+ function dealWithPath(d, dealWithPattern) {
+ if (!(dealWithPattern instanceof Function)) {
+ dealWithPattern = function() {};
+ }
+ var strArr = [], pattern = [], cache = [];
+ for (var i = 0, l = d.length; i < l; i++) {
+ switch (d[i]) {
+ case "M":
+ case "L":
+ case "T":
+ case "S":
+ case "A":
+ case "C":
+ case "H":
+ case "V":
+ case "Q": {
+ if (cache.length) {
+ pattern.push(cache.join(""));
+ cache = [];
+ }
+ // 脑图的path格式真奇怪...偶尔就给我蹦出来一个"..V123 C..", 那空格几个意思 - -
+ if (pattern[pattern.length-1] === ",") {
+ pattern.pop();
+ }
+ if (pattern.length) {
+ dealWithPattern(pattern);
+ strArr.push(pattern.join(""));
+ pattern = [];
+ }
+ pattern.push(d[i]);
+ break;
+ }
+ case "Z":
+ case "z": {
+ pattern.push(cache.join(""), d[i]);
+ dealWithPattern(pattern);
+ strArr.push(pattern.join(""));
+ cache = [];
+ pattern = [];
+ break;
+ }
+ case ".":
+ case "e": {
+ cache.push(d[i]);
+ break;
+ }
+ case "-": {
+ if (d[i-1] !== "e") {
+ if (cache.length) {
+ pattern.push(cache.join(""), ",");
+ }
+ cache = [];
+ }
+ cache.push("-");
+ break;
+ }
+ case " ":
+ case ",": {
+ if (cache.length) {
+ pattern.push(cache.join(""), ",");
+ cache = [];
+ }
+ break;
+ }
+ default: {
+ if (/\d/.test(d[i])) {
+ cache.push(d[i]);
+ } else {
+ // m a c s q h v l t z情况
+ if (cache.length) {
+ pattern.push(cache.join(""), d[i]);
+ cache = [];
+ } else {
+ // 脑图的path格式真奇怪...偶尔就给我蹦出来一个"..V123 c..", 那空格几个意思 - -
+ if (pattern[pattern.length-1] === ",") {
+ pattern.pop();
+ }
+ pattern.push(d[i]);
+ }
+ }
+ if (i + 1 === l) {
+ if (cache.length) {
+ pattern.push(cache.join(""));
+ }
+ dealWithPattern(pattern);
+ strArr.push(pattern.join(""));
+ cache = null;
+ pattern = null;
+ }
+ }
+ }
+ }
+ return strArr.join("");
+ }
+
+ function replaceWithNode(svgNode, parentX, parentY) {
+ if (!svgNode) {
+ return;
+ }
+ if (svgNode.tagName === "defs") {
+ return;
+ }
+ if (svgNode.getAttribute('fill') === 'transparent') {
+ svgNode.setAttribute('fill', 'none');
+ }
+ if (svgNode.getAttribute('marker-end')) {
+ svgNode.removeAttribute('marker-end');
+ }
+ parentX = parentX || 0;
+ parentY = parentY || 0;
+ if (svgNode.getAttribute("transform")) {
+ var ctm = getTransformToElement(svgNode, svgNode.parentElement);
+ parentX -= ctm.e;
+ parentY -= ctm.f;
+ svgNode.removeAttribute("transform");
+ }
+ switch (svgNode.tagName.toLowerCase()) {
+ case "g":
+ break;
+ case "path": {
+ var d = svgNode.getAttribute("d");
+ if (d) {
+ d = dealWithPath(d, function(pattern) {
+ switch (pattern[0]) {
+ case "V": {
+ pattern[1] = +pattern[1] - parentY;
+ break;
+ }
+ case "H": {
+ pattern[1] = +pattern[1] - parentX;
+ break;
+ }
+ case "M":
+ case "L":
+ case "T": {
+ pattern[1] = +pattern[1] - parentX;
+ pattern[3] = +pattern[3] - parentY;
+ break;
+ }
+ case "Q":
+ case "S": {
+ pattern[1] = +pattern[1] - parentX;
+ pattern[3] = +pattern[3] - parentY;
+ pattern[5] = +pattern[5] - parentX;
+ pattern[7] = +pattern[7] - parentY;
+ break;
+ }
+ case "A": {
+ pattern[11] = +pattern[11] - parentX;
+ pattern[13] = +pattern[13] - parentY;
+ break;
+ }
+ case "C": {
+ pattern[1] = +pattern[1] - parentX;
+ pattern[3] = +pattern[3] - parentY;
+ pattern[5] = +pattern[5] - parentX;
+ pattern[7] = +pattern[7] - parentY;
+ pattern[9] = +pattern[9] - parentX;
+ pattern[11] = +pattern[11] - parentY;
+ }
+ }
+ });
+ svgNode.setAttribute("d", d);
+ svgNode.removeAttribute("transform");
+ }
+ return;
+ }
+ case "image":
+ case "text": {
+ if (parentX && parentY) {
+ var x = +svgNode.getAttribute("x") || 0, y = +svgNode.getAttribute("y") || 0;
+ svgNode.setAttribute("x", x - parentX);
+ svgNode.setAttribute("y", y - parentY);
+ }
+ if (svgNode.getAttribute("dominant-baseline")) {
+ svgNode.removeAttribute("dominant-baseline");
+ svgNode.setAttribute("dy", ".8em");
+ }
+ svgNode.removeAttribute("transform");
+ return;
+ }
+ }
+ if (svgNode.children) {
+ for (var i = 0, l = svgNode.children.length; i < l; i++) {
+ replaceWithNode(svgNode.children[i], parentX, parentY);
+ }
+ }
+ }
+ svgDom.style.visibility = "hidden";
+ replaceWithNode(svgDom, x || 0, y || 0);
+ svgDom.style.visibility = "visible";
+ }
+ data.registerProtocol("svg", module.exports = {
+ fileDescription: "SVG 矢量图",
+ fileExtension: ".svg",
+ mineType: "image/svg+xml",
+ dataType: "text",
+ encode: function(json, minder) {
+ var paper = minder.getPaper(), paperTransform = paper.shapeNode.getAttribute("transform"), svgXml, svgContainer, svgDom, renderContainer = minder.getRenderContainer(), renderBox = renderContainer.getRenderBox(), transform = renderContainer.getTransform(), width = renderBox.width, height = renderBox.height, padding = 20;
+ paper.shapeNode.setAttribute("transform", "translate(0.5, 0.5)");
+ svgXml = paper.container.innerHTML;
+ paper.shapeNode.setAttribute("transform", paperTransform);
+ svgContainer = document.createElement("div");
+ document.body.appendChild(svgContainer);
+ svgContainer.innerHTML = svgXml;
+ svgDom = svgContainer.querySelector("svg");
+ svgDom.setAttribute("width", width + padding * 2 | 0);
+ svgDom.setAttribute("height", height + padding * 2 | 0);
+ svgDom.setAttribute("style", "background: " + minder.getStyle("background"));//"font-family: Arial, Microsoft Yahei, Heiti SC; " +
+ svgDom.setAttribute("viewBox", [ 0, 0, width + padding * 2 | 0, height + padding * 2 | 0 ].join(" "));
+ tempSvgContainer = document.createElement("div");
+ cleanSVG(svgDom, renderBox.x - padding | 0, renderBox.y - padding | 0);
+ document.body.removeChild(svgContainer);
+ tempSvgContainer.appendChild(svgDom);
+ // need a xml with width and height
+ svgXml = tempSvgContainer.innerHTML;
+ // svg 含有 符号导出报错 Entity 'nbsp' not defined
+ svgXml = svgXml.replace(/ /g, " ");
+ // svg 含有 符号导出报错 Entity 'nbsp' not defined
+ return svgXml;
+ }
+ });
+});
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/protocol/text.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/protocol/text.js
new file mode 100644
index 0000000..ad740af
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/protocol/text.js
@@ -0,0 +1,237 @@
+define(function(require, exports, module) {
+ var data = require('../core/data');
+ var Browser = require('../core/kity').Browser;
+
+ /**
+ * @Desc: 增加对不容浏览器下节点中文本\t匹配的处理,不同浏览器下\t无法正确匹配,导致无法使用TAB来批量导入节点
+ * @Editor: Naixor
+ * @Date: 2015.9.17
+ */
+ var LINE_ENDING = '\r',
+ LINE_ENDING_SPLITER = /\r\n|\r|\n/,
+ TAB_CHAR = (function(Browser) {
+ if (Browser.gecko) {
+ return {
+ REGEXP: new RegExp('^(\t|'+ String.fromCharCode(160,160,32,160) +')'),
+ DELETE: new RegExp('^(\t|'+ String.fromCharCode(160,160,32,160) +')+')
+ }
+ } else if (Browser.ie || Browser.edge) {
+ // ie系列和edge比较特别,\t在div中会被直接转义成SPACE故只好使用SPACE来做处理
+ return {
+ REGEXP: new RegExp('^('+ String.fromCharCode(32) +'|'+ String.fromCharCode(160) +')'),
+ DELETE: new RegExp('^('+ String.fromCharCode(32) +'|'+ String.fromCharCode(160) +')+')
+ }
+ } else {
+ return {
+ REGEXP: /^(\t|\x20{4})/,
+ DELETE: /^(\t|\x20{4})+/
+ }
+ }
+ })(Browser);
+
+ function repeat(s, n) {
+ var result = '';
+ while (n--) result += s;
+ return result;
+ }
+
+ /**
+ * 对节点text中的换行符进行处理
+ * @method encodeWrap
+ * @param {String} nodeText MinderNode.data.text
+ * @return {String} \n -> '\n'; \\n -> '\\n'
+ */
+ function encodeWrap(nodeText) {
+ if (!nodeText) {
+ return '';
+ }
+ var textArr = [],
+ WRAP_TEXT = ['\\', 'n'];
+ for (var i = 0, j = 0, l = nodeText.length; i < l; i++) {
+ if (nodeText[i] === '\n' || nodeText[i] === '\r') {
+ textArr.push('\\n');
+ j = 0;
+ continue;
+ }
+ if (nodeText[i] === WRAP_TEXT[j]) {
+ j++;
+ if (j === 2) {
+ j = 0;
+ textArr.push('\\\\n');
+ }
+ continue;
+ }
+ switch (j) {
+ case 0: {
+ textArr.push(nodeText[i]);
+ break;
+ }
+ case 1: {
+ textArr.push(nodeText[i-1], nodeText[i]);
+ }
+ }
+ j = 0;
+ }
+ return textArr.join('');
+ }
+
+ /**
+ * 将文本内容中的'\n'和'\\n'分别转换成\n和\\n
+ * @method decodeWrap
+ * @param {[type]} text [description]
+ * @return {[type]} [description]
+ */
+ function decodeWrap(text) {
+ if (!text) {
+ return '';
+ }
+ var textArr = [],
+ WRAP_TEXT = ['\\', '\\', 'n'];
+ for (var i = 0, j = 0, l = text.length; i < l; i++) {
+ if (text[i] === WRAP_TEXT[j]) {
+ j++;
+ if (j === 3) {
+ j = 0;
+ textArr.push('\\n');
+ }
+ continue;
+ }
+ switch (j) {
+ case 0: {
+ textArr.push(text[i]);
+ j = 0;
+ break;
+ }
+ case 1: {
+ if (text[i] === 'n') {
+ textArr.push('\n');
+ } else {
+ textArr.push(text[i-1], text[i]);
+ }
+ j = 0;
+ break;
+ }
+ case 2: {
+ textArr.push(text[i-2]);
+ if (text[i] !== '\\') {
+ j = 0;
+ textArr.push(text[i-1], text[i]);
+ }
+ break;
+ }
+ }
+ }
+ return textArr.join('');
+ }
+
+ function encode(json, level) {
+ var local = '';
+ level = level || 0;
+ local += repeat('\t', level);
+ local += encodeWrap(json.data.text) + LINE_ENDING;
+ if (json.children) {
+ json.children.forEach(function(child) {
+ local += encode(child, level + 1);
+ });
+ }
+ return local;
+ }
+
+ function isEmpty(line) {
+ return !/\S/.test(line);
+ }
+
+ function getLevel(line) {
+ var level = 0;
+ while (TAB_CHAR.REGEXP.test(line)) {
+ line = line.replace(TAB_CHAR.REGEXP, '');
+ level++;
+ }
+
+ return level;
+ }
+
+ function getNode(line) {
+ return {
+ data: {
+ text: decodeWrap(line.replace(TAB_CHAR.DELETE, ""))
+ }
+ };
+ }
+
+ function decode(local) {
+ var json,
+ parentMap = {},
+ lines = local.split(LINE_ENDING_SPLITER),
+ line, level, node;
+
+ function addChild(parent, child) {
+ var children = parent.children || (parent.children = []);
+ children.push(child);
+ }
+
+ for (var i = 0; i < lines.length; i++) {
+ line = lines[i];
+ if (isEmpty(line)) continue;
+
+ level = getLevel(line);
+ node = getNode(line);
+
+ if (level === 0) {
+ if (json) {
+ throw new Error('Invalid local format');
+ }
+ json = node;
+ } else {
+ if (!parentMap[level - 1]) {
+ throw new Error('Invalid local format');
+ }
+ addChild(parentMap[level - 1], node);
+ }
+ parentMap[level] = node;
+ }
+ return json;
+ }
+
+ /**
+ * @Desc: 增加一个将当前选中节点转换成text的方法
+ * @Editor: Naixor
+ * @Date: 2015.9.21
+ */
+ function Node2Text(node) {
+ function exportNode(node) {
+ var exported = {};
+ exported.data = node.getData();
+ var childNodes = node.getChildren();
+ exported.children = [];
+ for (var i = 0; i < childNodes.length; i++) {
+ exported.children.push(exportNode(childNodes[i]));
+ }
+ return exported;
+ }
+ if (!node) return;
+ if (/^\s*$/.test(node.data.text)) {
+ node.data.text = "分支主题";
+ }
+ return encode(exportNode(node));
+ }
+
+ data.registerProtocol('text', module.exports = {
+ fileDescription: '大纲文本',
+ fileExtension: '.txt',
+ dataType: 'text',
+ mineType: 'text/plain',
+
+ encode: function(json) {
+ return encode(json.root, 0);
+ },
+
+ decode: function(local) {
+ return decode(local);
+ },
+
+ Node2Text: function(node) {
+ return Node2Text(node);
+ }
+ });
+});
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/template/default.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/template/default.js
new file mode 100644
index 0000000..0e20fea
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/template/default.js
@@ -0,0 +1,38 @@
+/**
+ * @fileOverview
+ *
+ * 默认模板 - 脑图模板
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+define(function(require, exports, module) {
+ var template = require('../core/template');
+
+ template.register('default', {
+
+ getLayout: function(node) {
+
+ if (node.getData('layout')) return node.getData('layout');
+
+ var level = node.getLevel();
+
+ // 根节点
+ if (level === 0) {
+ return 'mind';
+ }
+
+ // 一级节点
+ if (level === 1) {
+ return node.getLayoutPointPreview().x > 0 ? 'right': 'left';
+ }
+
+ return node.parent.getLayout();
+ },
+
+ getConnect: function(node) {
+ if (node.getLevel() == 1) return 'arc';
+ return 'under';
+ }
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/template/filetree.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/template/filetree.js
new file mode 100644
index 0000000..dd68b12
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/template/filetree.js
@@ -0,0 +1,28 @@
+/**
+ * @fileOverview
+ *
+ * 文件夹模板
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+define(function(require, exports, module) {
+ var template = require('../core/template');
+
+ template.register('filetree', {
+
+ getLayout: function(node) {
+ if (node.getData('layout')) return node.getData('layout');
+ if (node.isRoot()) return 'bottom';
+
+ return 'filetree-down';
+ },
+
+ getConnect: function(node) {
+ if (node.getLevel() == 1) {
+ return 'poly';
+ }
+ return 'l';
+ }
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/template/fish-bone.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/template/fish-bone.js
new file mode 100644
index 0000000..71e8076
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/template/fish-bone.js
@@ -0,0 +1,41 @@
+/**
+ * @fileOverview
+ *
+ * 默认模板 - 鱼骨头模板
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+define(function(require, exports, module) {
+ var template = require('../core/template');
+
+ template.register('fish-bone', {
+
+ getLayout: function(node) {
+
+ if (node.getData('layout')) return node.getData('layout');
+
+ var level = node.getLevel();
+
+ // 根节点
+ if (level === 0) {
+ return 'fish-bone-master';
+ }
+
+ // 一级节点
+ if (level === 1) {
+ return 'fish-bone-slave';
+ }
+
+ return node.getLayoutPointPreview().y > 0 ? 'filetree-up': 'filetree-down';
+ },
+
+ getConnect: function(node) {
+ switch (node.getLevel()) {
+ case 1: return 'fish-bone-master';
+ case 2: return 'line';
+ default: return 'l';
+ }
+ }
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/template/right.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/template/right.js
new file mode 100644
index 0000000..6d1c206
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/template/right.js
@@ -0,0 +1,23 @@
+/**
+ * @fileOverview
+ *
+ * 往右布局结构模板
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+define(function(require, exports, module) {
+ var template = require('../core/template');
+
+ template.register('right', {
+
+ getLayout: function(node) {
+ return node.getData('layout') || 'right';
+ },
+
+ getConnect: function(node) {
+ if (node.getLevel() == 1) return 'arc';
+ return 'bezier';
+ }
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/template/structure.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/template/structure.js
new file mode 100644
index 0000000..4af7eab
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/template/structure.js
@@ -0,0 +1,22 @@
+/**
+ * @fileOverview
+ *
+ * 组织结构图模板
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+define(function(require, exports, module) {
+ var template = require('../core/template');
+
+ template.register('structure', {
+
+ getLayout: function(node) {
+ return node.getData('layout') || 'bottom';
+ },
+
+ getConnect: function(node) {
+ return 'poly';
+ }
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/template/tianpan.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/template/tianpan.js
new file mode 100644
index 0000000..cc92ada
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/template/tianpan.js
@@ -0,0 +1,29 @@
+/**
+ * @fileOverview
+ *
+ * 天盘模板
+ *
+ * @author: along
+ * @copyright: bpd729@163.com, 2015
+ */
+define(function(require, exports, module) {
+ var template = require('../core/template');
+
+ template.register('tianpan', {
+ getLayout: function (node) {
+ if (node.getData('layout')) return node.getData('layout');
+ var level = node.getLevel();
+
+ // 根节点
+ if (level === 0) {
+ return 'tianpan';
+ }
+
+ return node.parent.getLayout();
+ },
+
+ getConnect: function (node) {
+ return 'arc_tp';
+ }
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/theme/default.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/theme/default.js
new file mode 100644
index 0000000..54c1b86
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/theme/default.js
@@ -0,0 +1,66 @@
+define(function(require, exports, module) {
+ var theme = require('../core/theme');
+
+ ['classic', 'classic-compact'].forEach(function(name) {
+ var compact = name == 'classic-compact';
+
+ /* jscs:disable maximumLineLength */
+ theme.register(name, {
+ 'background': '#3A4144 url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAIAAAACDbGyAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDowQzg5QTQ0NDhENzgxMUUzOENGREE4QTg0RDgzRTZDNyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDowQzg5QTQ0NThENzgxMUUzOENGREE4QTg0RDgzRTZDNyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkMwOEQ1NDRGOEQ3NzExRTM4Q0ZEQThBODREODNFNkM3IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkMwOEQ1NDUwOEQ3NzExRTM4Q0ZEQThBODREODNFNkM3Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+e9P33AAAACVJREFUeNpisXJ0YUACTAyoAMr/+eM7EGGRZ4FQ7BycEAZAgAEAHbEGtkoQm/wAAAAASUVORK5CYII=") repeat',
+
+ 'root-color': '#430',
+ 'root-background': '#e9df98',
+ 'root-stroke': '#e9df98',
+ 'root-font-size': 24,
+ 'root-padding': compact ? [10, 25] : [15, 25],
+ 'root-margin': compact ? [15, 25] : [30, 100],
+ 'root-radius': 30,
+ 'root-space': 10,
+ 'root-shadow': 'rgba(0, 0, 0, .25)',
+
+ 'main-color': '#333',
+ 'main-background': '#a4c5c0',
+ 'main-stroke': '#a4c5c0',
+ 'main-font-size': 16,
+ 'main-padding': compact ? [5, 15] : [6, 20],
+ 'main-margin': compact ? [5, 10] : 20,
+ 'main-radius': 10,
+ 'main-space': 5,
+ 'main-shadow': 'rgba(0, 0, 0, .25)',
+
+ 'sub-color': 'white',
+ 'sub-background': 'transparent',
+ 'sub-stroke': 'none',
+ 'sub-font-size': 12,
+ 'sub-padding': [5, 10],
+ 'sub-margin': compact ? [5, 10] : [15, 20],
+ 'sub-tree-margin': 30,
+ 'sub-radius': 5,
+ 'sub-space': 5,
+
+ 'connect-color': 'white',
+ 'connect-width': 2,
+ 'main-connect-width': 3,
+ 'connect-radius': 5,
+
+ 'selected-background': 'rgb(254, 219, 0)',
+ 'selected-stroke': 'rgb(254, 219, 0)',
+ 'selected-color': 'black',
+
+ 'marquee-background': 'rgba(255,255,255,.3)',
+ 'marquee-stroke': 'white',
+
+ 'drop-hint-color': 'yellow',
+ 'sub-drop-hint-width': 2,
+ 'main-drop-hint-width': 4,
+ 'root-drop-hint-width': 4,
+
+ 'order-hint-area-color': 'rgba(0, 255, 0, .5)',
+ 'order-hint-path-color': '#0f0',
+ 'order-hint-path-width': 1,
+
+ 'text-selection-color': 'rgb(27,171,255)',
+ 'line-height':1.5
+ });
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/theme/fish.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/theme/fish.js
new file mode 100644
index 0000000..71d88aa
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/theme/fish.js
@@ -0,0 +1,57 @@
+define(function(require, exports, module) {
+ var theme = require('../core/theme');
+
+ theme.register('fish', {
+ 'background': '#3A4144 url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAIAAAACDbGyAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDowQzg5QTQ0NDhENzgxMUUzOENGREE4QTg0RDgzRTZDNyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDowQzg5QTQ0NThENzgxMUUzOENGREE4QTg0RDgzRTZDNyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkMwOEQ1NDRGOEQ3NzExRTM4Q0ZEQThBODREODNFNkM3IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkMwOEQ1NDUwOEQ3NzExRTM4Q0ZEQThBODREODNFNkM3Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+e9P33AAAACVJREFUeNpisXJ0YUACTAyoAMr/+eM7EGGRZ4FQ7BycEAZAgAEAHbEGtkoQm/wAAAAASUVORK5CYII=") repeat',
+
+ 'root-color': '#430',
+ 'root-background': '#e9df98',
+ 'root-stroke': '#e9df98',
+ 'root-font-size': 24,
+ 'root-padding': [35, 35],
+ 'root-margin': 30,
+ 'root-radius': 100,
+ 'root-space': 10,
+ 'root-shadow': 'rgba(0, 0, 0, .25)',
+
+ 'main-color': '#333',
+ 'main-background': '#a4c5c0',
+ 'main-stroke': '#a4c5c0',
+ 'main-font-size': 16,
+ 'main-padding': [6, 20],
+ 'main-margin': [20, 20],
+ 'main-radius': 5,
+ 'main-space': 5,
+ 'main-shadow': 'rgba(0, 0, 0, .25)',
+
+ 'sub-color': 'black',
+ 'sub-background': 'white',
+ 'sub-stroke': 'white',
+ 'sub-font-size': 12,
+ 'sub-padding': [5, 10],
+ 'sub-margin': [10],
+ 'sub-radius': 5,
+ 'sub-space': 5,
+
+ 'connect-color': 'white',
+ 'connect-width': 3,
+ 'main-connect-width': 3,
+ 'connect-radius': 5,
+
+ 'selected-background': 'rgb(254, 219, 0)',
+ 'selected-stroke': 'rgb(254, 219, 0)',
+
+ 'marquee-background': 'rgba(255,255,255,.3)',
+ 'marquee-stroke': 'white',
+
+ 'drop-hint-color': 'yellow',
+ 'drop-hint-width': 4,
+
+ 'order-hint-area-color': 'rgba(0, 255, 0, .5)',
+ 'order-hint-path-color': '#0f0',
+ 'order-hint-path-width': 1,
+
+ 'text-selection-color': 'rgb(27,171,255)',
+ 'line-height':1.5
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/theme/fresh.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/theme/fresh.js
new file mode 100644
index 0000000..49ef3e1
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/theme/fresh.js
@@ -0,0 +1,78 @@
+define(function(require, exports, module) {
+ var kity = require('../core/kity');
+ var theme = require('../core/theme');
+
+ function hsl(h, s, l) {
+ return kity.Color.createHSL(h, s, l);
+ }
+
+ function generate(h, compat) {
+ return {
+ 'background': '#fbfbfb',
+
+ 'root-color': 'white',
+ 'root-background': hsl(h, 37, 60),
+ 'root-stroke': hsl(h, 37, 60),
+ 'root-font-size': 16,
+ 'root-padding': compat ? [6, 12] : [12, 24],
+ 'root-margin': compat ? 10 : [30, 100],
+ 'root-radius': 5,
+ 'root-space': 10,
+
+ 'main-color': 'black',
+ 'main-background': hsl(h, 33, 95),
+ 'main-stroke': hsl(h, 37, 60),
+ 'main-stroke-width': 1,
+ 'main-font-size': 14,
+ 'main-padding': [6, 20],
+ 'main-margin': compat ? 8 : 20,
+ 'main-radius': 3,
+ 'main-space': 5,
+
+ 'sub-color': 'black',
+ 'sub-background': 'transparent',
+ 'sub-stroke': 'none',
+ 'sub-font-size': 12,
+ 'sub-padding': compat ? [3, 5] : [5, 10],
+ 'sub-margin': compat ? [4, 8] : [15, 20],
+ 'sub-radius': 5,
+ 'sub-space': 5,
+
+ 'connect-color': hsl(h, 37, 60),
+ 'connect-width': 1,
+ 'connect-radius': 5,
+
+ 'selected-stroke': hsl(h, 26, 30),
+ 'selected-stroke-width': '3',
+ 'blur-selected-stroke': hsl(h, 10, 60),
+
+ 'marquee-background': hsl(h, 100, 80).set('a', 0.1),
+ 'marquee-stroke': hsl(h, 37, 60),
+
+ 'drop-hint-color': hsl(h, 26, 35),
+ 'drop-hint-width': 5,
+
+ 'order-hint-area-color': hsl(h, 100, 30).set('a', 0.5),
+ 'order-hint-path-color': hsl(h, 100, 25),
+ 'order-hint-path-width': 1,
+
+ 'text-selection-color': hsl(h, 100, 20),
+ 'line-height':1.5
+ };
+ }
+
+ var plans = {
+ red: 0,
+ soil: 25,
+ green: 122,
+ blue: 204,
+ purple: 246,
+ pink: 334
+ };
+ var name;
+ for (name in plans) {
+ theme.register('fresh-' + name, generate(plans[name]));
+ theme.register('fresh-' + name + '-compat', generate(plans[name], true));
+ }
+
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/theme/snow.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/theme/snow.js
new file mode 100644
index 0000000..08ba928
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/theme/snow.js
@@ -0,0 +1,62 @@
+define(function(require, exports, module) {
+ var theme = require('../core/theme');
+
+ ['snow', 'snow-compact'].forEach(function(name) {
+ var compact = name == 'snow-compact';
+
+ /* jscs:disable maximumLineLength */
+ theme.register(name, {
+ 'background': '#3A4144 url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAIAAAACDbGyAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDowQzg5QTQ0NDhENzgxMUUzOENGREE4QTg0RDgzRTZDNyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDowQzg5QTQ0NThENzgxMUUzOENGREE4QTg0RDgzRTZDNyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkMwOEQ1NDRGOEQ3NzExRTM4Q0ZEQThBODREODNFNkM3IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkMwOEQ1NDUwOEQ3NzExRTM4Q0ZEQThBODREODNFNkM3Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+e9P33AAAACVJREFUeNpisXJ0YUACTAyoAMr/+eM7EGGRZ4FQ7BycEAZAgAEAHbEGtkoQm/wAAAAASUVORK5CYII=") repeat',
+
+ 'root-color': '#430',
+ 'root-background': '#e9df98',
+ 'root-stroke': '#e9df98',
+ 'root-font-size': 24,
+ 'root-padding': compact ? [5, 10] : [15, 25],
+ 'root-margin': compact ? 15 : 30,
+ 'root-radius': 5,
+ 'root-space': 10,
+ 'root-shadow': 'rgba(0, 0, 0, .25)',
+
+ 'main-color': '#333',
+ 'main-background': '#a4c5c0',
+ 'main-stroke': '#a4c5c0',
+ 'main-font-size': 16,
+ 'main-padding': compact ? [4, 10] : [6, 20],
+ 'main-margin': compact ? [5, 10] : [20, 40],
+ 'main-radius': 5,
+ 'main-space': 5,
+ 'main-shadow': 'rgba(0, 0, 0, .25)',
+
+ 'sub-color': 'black',
+ 'sub-background': 'white',
+ 'sub-stroke': 'white',
+ 'sub-font-size': 12,
+ 'sub-padding': [5, 10],
+ 'sub-margin': compact ? [5, 10] : [10, 20],
+ 'sub-radius': 5,
+ 'sub-space': 5,
+
+ 'connect-color': 'white',
+ 'connect-width': 2,
+ 'main-connect-width': 3,
+ 'connect-radius': 5,
+
+ 'selected-background': 'rgb(254, 219, 0)',
+ 'selected-stroke': 'rgb(254, 219, 0)',
+
+ 'marquee-background': 'rgba(255,255,255,.3)',
+ 'marquee-stroke': 'white',
+
+ 'drop-hint-color': 'yellow',
+ 'drop-hint-width': 4,
+
+ 'order-hint-area-color': 'rgba(0, 255, 0, .5)',
+ 'order-hint-path-color': '#0f0',
+ 'order-hint-path-width': 1,
+
+ 'text-selection-color': 'rgb(27,171,255)',
+ 'line-height':1.5
+ });
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/theme/tianpan.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/theme/tianpan.js
new file mode 100644
index 0000000..333a677
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/theme/tianpan.js
@@ -0,0 +1,74 @@
+define(function(require, exports, module) {
+ var theme = require('../core/theme');
+
+ ['tianpan', 'tianpan-compact'].forEach(function (name) {
+ var compact = name == 'tianpan-compact';
+
+ theme.register(name, {
+ 'background': '#3A4144 url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAIAAAACDbGyAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDowQzg5QTQ0NDhENzgxMUUzOENGREE4QTg0RDgzRTZDNyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDowQzg5QTQ0NThENzgxMUUzOENGREE4QTg0RDgzRTZDNyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkMwOEQ1NDRGOEQ3NzExRTM4Q0ZEQThBODREODNFNkM3IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkMwOEQ1NDUwOEQ3NzExRTM4Q0ZEQThBODREODNFNkM3Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+e9P33AAAACVJREFUeNpisXJ0YUACTAyoAMr/+eM7EGGRZ4FQ7BycEAZAgAEAHbEGtkoQm/wAAAAASUVORK5CYII=") repeat',
+
+ 'root-color': '#430',
+ 'root-background': '#e9df98',
+ 'root-stroke': '#e9df98',
+ 'root-font-size': 25,
+ 'root-padding': compact ? 15 : 20,
+ 'root-margin': compact ? [15, 25] : 100,
+ 'root-radius': 30,
+ 'root-space': 10,
+ 'root-shadow': 'rgba(0, 0, 0, .25)',
+ 'root-shape': 'circle',
+
+ 'main-color': '#333',
+ 'main-background': '#a4c5c0',
+ 'main-stroke': '#a4c5c0',
+ 'main-font-size': 15,
+ 'main-padding': compact ? 10 : 12,
+ 'main-margin': compact ? 10 : 12,
+ 'main-radius': 10,
+ 'main-space': 5,
+ 'main-shadow': 'rgba(0, 0, 0, .25)',
+ 'main-shape': 'circle',
+
+ 'sub-color': '#333',
+ 'sub-background': '#99ca6a',
+ 'sub-stroke': '#a4c5c0',
+ 'sub-font-size': 13,
+ 'sub-padding': 5,
+ 'sub-margin': compact ? 6 : 10,
+ 'sub-tree-margin': 30,
+ 'sub-radius': 5,
+ 'sub-space': 5,
+ 'sub-shadow': 'rgba(0, 0, 0, .25)',
+ 'sub-shape': 'circle',
+
+ 'connect-color': 'white',
+ 'connect-width': 2,
+ 'main-connect-width': 3,
+ 'connect-radius': 5,
+
+ 'selected-background': 'rgb(254, 219, 0)',
+ 'selected-stroke': 'rgb(254, 219, 0)',
+ 'selected-color': 'black',
+
+ 'marquee-background': 'rgba(255,255,255,.3)',
+ 'marquee-stroke': 'white',
+
+ 'drop-hint-color': 'yellow',
+ 'sub-drop-hint-width': 2,
+ 'main-drop-hint-width': 4,
+ 'root-drop-hint-width': 4,
+
+ 'order-hint-area-color': 'rgba(0, 255, 0, .5)',
+ 'order-hint-path-color': '#0f0',
+ 'order-hint-path-width': 1,
+
+ 'text-selection-color': 'rgb(27,171,255)',
+ 'line-height': 1.4
+ });
+ });
+});
+
+
+
+
+
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/theme/wire.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/theme/wire.js
new file mode 100644
index 0000000..a0eaacd
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-core/theme/wire.js
@@ -0,0 +1,34 @@
+define(function(require, exports, module) {
+ var theme = require('../core/theme');
+
+ theme.register('wire', {
+ 'background': 'black',
+
+ 'color': '#999',
+ 'stroke': 'none',
+ 'padding': 10,
+ 'margin': 20,
+ 'font-size': 14,
+
+ 'connect-color': '#999',
+ 'connect-width': 1,
+
+ 'selected-background': '#999',
+ 'selected-color': 'black',
+
+ 'marquee-background': 'rgba(255,255,255,.3)',
+ 'marquee-stroke': 'white',
+
+ 'drop-hint-color': 'yellow',
+ 'sub-drop-hint-width': 2,
+ 'main-drop-hint-width': 4,
+ 'root-drop-hint-width': 4,
+
+ 'order-hint-area-color': 'rgba(0, 255, 0, .5)',
+ 'order-hint-path-color': '#0f0',
+ 'order-hint-path-width': 1,
+
+ 'text-selection-color': 'rgb(27,171,255)',
+ 'line-height':1.5
+ });
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/editor.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/editor.js
new file mode 100644
index 0000000..4c62d08
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/editor.js
@@ -0,0 +1,38 @@
+define(function (require, exports, module) {
+ /**
+ * 运行时
+ */
+ var runtimes = [];
+
+ function assemble(runtime) {
+ runtimes.push(runtime);
+ }
+
+ function KMEditor(selector, blackList) {
+ this.selector = selector;
+ for (var i = 0; i < runtimes.length; i++) {
+ if (typeof runtimes[i] == 'function') {
+ runtimes[i].call(this, this);
+ }
+ }
+ }
+
+ KMEditor.assemble = assemble;
+
+ assemble(require('./runtime/container'));
+ assemble(require('./runtime/fsm'));
+ assemble(require('./runtime/minder'));
+ assemble(require('./runtime/receiver'));
+ assemble(require('./runtime/hotbox'));
+ assemble(require('./runtime/input'));
+ assemble(require('./runtime/clipboard-mimetype'));
+ assemble(require('./runtime/clipboard'));
+ assemble(require('./runtime/drag'));
+ assemble(require('./runtime/node'));
+ assemble(require('./runtime/history'));
+ assemble(require('./runtime/jumping'));
+ assemble(require('./runtime/priority'));
+ assemble(require('./runtime/progress'));
+
+ return (module.exports = KMEditor);
+});
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/expose-editor.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/expose-editor.js
new file mode 100644
index 0000000..823926f
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/expose-editor.js
@@ -0,0 +1,11 @@
+/**
+ * @fileOverview
+ *
+ * 打包暴露
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+define('expose-editor', function (require, exports, module) {
+ return (module.exports = window.kityminder.Editor = require('./editor'));
+});
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/hotbox.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/hotbox.js
new file mode 100644
index 0000000..08c95dd
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/hotbox.js
@@ -0,0 +1,5 @@
+import Hotbox from '../hotbox/hotbox';
+
+define(function (require, exports, module) {
+ return (module.exports = Hotbox);
+});
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/lang.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/lang.js
new file mode 100644
index 0000000..c7d8611
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/lang.js
@@ -0,0 +1,3 @@
+define(function(require, exports, module) {
+
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/minder.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/minder.js
new file mode 100644
index 0000000..34d0c41
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/minder.js
@@ -0,0 +1,3 @@
+define(function (require, exports, module) {
+ return (module.exports = window.kityminder.Minder);
+});
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/clipboard-mimetype.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/clipboard-mimetype.js
new file mode 100644
index 0000000..33ef036
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/clipboard-mimetype.js
@@ -0,0 +1,123 @@
+/**
+ * @Desc: 新增一个用于处理系统ctrl+c ctrl+v等方式导入导出节点的MIMETYPE处理,如系统不支持clipboardEvent或者是FF则不初始化改class
+ * @Editor: Naixor
+ * @Date: 2015.9.21
+ */
+define(function(require, exports, module) {
+ function MimeType() {
+ /**
+ * 私有变量
+ */
+ var SPLITOR = '\uFEFF';
+ var MIMETYPE = {
+ 'application/km': '\uFFFF'
+ };
+ var SIGN = {
+ '\uFEFF': 'SPLITOR',
+ '\uFFFF': 'application/km'
+ };
+
+ /**
+ * 用于将一段纯文本封装成符合其数据格式的文本
+ * @method process private
+ * @param {MIMETYPE} mimetype 数据格式
+ * @param {String} text 原始文本
+ * @return {String} 符合该数据格式下的文本
+ * @example
+ * var str = "123";
+ * str = process('application/km', str); // 返回的内容再经过MimeType判断会读取出其数据格式为application/km
+ * process('text/plain', str); // 若接受到一个非纯文本信息,则会将其转换为新的数据格式
+ */
+ function process(mimetype, text) {
+ if (!this.isPureText(text)) {
+ var _mimetype = this.whichMimeType(text);
+ if (!_mimetype) {
+ throw new Error('unknow mimetype!');
+ };
+ text = this.getPureText(text);
+ };
+ if (mimetype === false) {
+ return text;
+ };
+ return mimetype + SPLITOR + text;
+ }
+
+ /**
+ * 注册数据类型的标识
+ * @method registMimeTypeProtocol public
+ * @param {String} type 数据类型
+ * @param {String} sign 标识
+ */
+ this.registMimeTypeProtocol = function(type, sign) {
+ if (sign && SIGN[sign]) {
+ throw new Error('sing has registed!');
+ }
+ if (type && !!MIMETYPE[type]) {
+ throw new Error('mimetype has registed!');
+ };
+ SIGN[sign] = type;
+ MIMETYPE[type] = sign;
+ }
+
+ /**
+ * 获取已注册数据类型的协议
+ * @method getMimeTypeProtocol public
+ * @param {String} type 数据类型
+ * @param {String} text|undefiend 文本内容或不传入
+ * @return {String|Function}
+ * @example
+ * text若不传入则直接返回对应数据格式的处理(process)方法
+ * 若传入文本则直接调用对应的process方法进行处理,此时返回处理后的内容
+ * var m = new MimeType();
+ * var kmprocess = m.getMimeTypeProtocol('application/km');
+ * kmprocess("123") === m.getMimeTypeProtocol('application/km', "123");
+ *
+ */
+ this.getMimeTypeProtocol = function(type, text) {
+ var mimetype = MIMETYPE[type] || false;
+
+ if (text === undefined) {
+ return process.bind(this, mimetype);
+ };
+
+ return process(mimetype, text);
+ }
+
+ this.getSpitor = function() {
+ return SPLITOR;
+ }
+
+ this.getMimeType = function(sign) {
+ if (sign !== undefined) {
+ return SIGN[sign] || null;
+ };
+ return MIMETYPE;
+ }
+ }
+
+ MimeType.prototype.isPureText = function(text) {
+ return !(~text.indexOf(this.getSpitor()));
+ }
+
+ MimeType.prototype.getPureText = function(text) {
+ if (this.isPureText(text)) {
+ return text;
+ };
+ return text.split(this.getSpitor())[1];
+ }
+
+ MimeType.prototype.whichMimeType = function(text) {
+ if (this.isPureText(text)) {
+ return null;
+ };
+ return this.getMimeType(text.split(this.getSpitor())[0]);
+ }
+
+ function MimeTypeRuntime() {
+ if (this.minder.supportClipboardEvent && !kity.Browser.gecko) {
+ this.MimeType = new MimeType();
+ };
+ }
+
+ return module.exports = MimeTypeRuntime;
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/clipboard.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/clipboard.js
new file mode 100644
index 0000000..e01e354
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/clipboard.js
@@ -0,0 +1,188 @@
+/**
+ * @Desc: 处理editor的clipboard事件,只在支持ClipboardEvent并且不是FF的情况下工作
+ * @Editor: Naixor
+ * @Date: 2015.9.21
+ */
+define(function(require, exports, module) {
+
+ function ClipboardRuntime () {
+ var minder = this.minder;
+ var Data = window.kityminder.data;
+
+ if (!minder.supportClipboardEvent || kity.Browser.gecko) {
+ return;
+ };
+
+ var fsm = this.fsm;
+ var receiver = this.receiver;
+ var MimeType = this.MimeType;
+
+ var kmencode = MimeType.getMimeTypeProtocol('application/km'),
+ decode = Data.getRegisterProtocol('json').decode;
+ var _selectedNodes = [];
+
+ /*
+ * 增加对多节点赋值粘贴的处理
+ */
+ function encode (nodes) {
+ var _nodes = [];
+ for (var i = 0, l = nodes.length; i < l; i++) {
+ _nodes.push(minder.exportNode(nodes[i]));
+ }
+ return kmencode(Data.getRegisterProtocol('json').encode(_nodes));
+ }
+
+ var beforeCopy = function (e) {
+ if (document.activeElement == receiver.element) {
+ var clipBoardEvent = e;
+ var state = fsm.state();
+
+ switch (state) {
+ case 'input': {
+ break;
+ }
+ case 'normal': {
+ var nodes = [].concat(minder.getSelectedNodes());
+ if (nodes.length) {
+ // 这里由于被粘贴复制的节点的id信息也都一样,故做此算法
+ // 这里有个疑问,使用node.getParent()或者node.parent会离奇导致出现非选中节点被渲染成选中节点,因此使用isAncestorOf,而没有使用自行回溯的方式
+ if (nodes.length > 1) {
+ var targetLevel;
+ nodes.sort(function(a, b) {
+ return a.getLevel() - b.getLevel();
+ });
+ targetLevel = nodes[0].getLevel();
+ if (targetLevel !== nodes[nodes.length-1].getLevel()) {
+ var plevel, pnode,
+ idx = 0, l = nodes.length, pidx = l-1;
+
+ pnode = nodes[pidx];
+
+ while (pnode.getLevel() !== targetLevel) {
+ idx = 0;
+ while (idx < l && nodes[idx].getLevel() === targetLevel) {
+ if (nodes[idx].isAncestorOf(pnode)) {
+ nodes.splice(pidx, 1);
+ break;
+ }
+ idx++;
+ }
+ pidx--;
+ pnode = nodes[pidx];
+ }
+ };
+ };
+ var str = encode(nodes);
+ clipBoardEvent.clipboardData.setData('text/plain', str);
+ }
+ e.preventDefault();
+ break;
+ }
+ }
+ }
+ }
+
+ var beforeCut = function (e) {
+ if (document.activeElement == receiver.element) {
+ if (minder.getStatus() !== 'normal') {
+ e.preventDefault();
+ return;
+ };
+
+ var clipBoardEvent = e;
+ var state = fsm.state();
+
+ switch (state) {
+ case 'input': {
+ break;
+ }
+ case 'normal': {
+ var nodes = minder.getSelectedNodes();
+ if (nodes.length) {
+ clipBoardEvent.clipboardData.setData('text/plain', encode(nodes));
+ minder.execCommand('removenode');
+ }
+ e.preventDefault();
+ break;
+ }
+ }
+ };
+ }
+
+ var beforePaste = function(e) {
+ if (document.activeElement == receiver.element) {
+ if (minder.getStatus() !== 'normal') {
+ e.preventDefault();
+ return;
+ };
+
+ var clipBoardEvent = e;
+ var state = fsm.state();
+ var textData = clipBoardEvent.clipboardData.getData('text/plain');
+
+ switch (state) {
+ case 'input': {
+ // input状态下如果格式为application/km则不进行paste操作
+ if (!MimeType.isPureText(textData)) {
+ e.preventDefault();
+ return;
+ };
+ break;
+ }
+ case 'normal': {
+ /*
+ * 针对normal状态下通过对选中节点粘贴导入子节点文本进行单独处理
+ */
+ var sNodes = minder.getSelectedNodes();
+
+ if (MimeType.whichMimeType(textData) === 'application/km') {
+ var nodes = decode(MimeType.getPureText(textData));
+ var _node;
+ sNodes.forEach(function(node) {
+ // 由于粘贴逻辑中为了排除子节点重新排序导致逆序,因此复制的时候倒过来
+ for (var i = nodes.length-1; i >= 0; i--) {
+ _node = minder.createNode(null, node);
+ minder.importNode(_node, nodes[i]);
+ _selectedNodes.push(_node);
+ node.appendChild(_node);
+ }
+ });
+ minder.select(_selectedNodes, true);
+ _selectedNodes = [];
+
+ minder.refresh();
+ }
+ else if (clipBoardEvent.clipboardData && clipBoardEvent.clipboardData.items[0].type.indexOf('image') > -1) {
+ var imageFile = clipBoardEvent.clipboardData.items[0].getAsFile();
+ var serverService = angular.element(document.body).injector().get('server');
+
+ return serverService.uploadImage(imageFile).then(function (json) {
+ var resp = json.data;
+ if (resp.errno === 0) {
+ minder.execCommand('image', resp.data.url);
+ }
+ });
+ }
+ else {
+ sNodes.forEach(function(node) {
+ minder.Text2Children(node, textData);
+ });
+ }
+ e.preventDefault();
+ break;
+ }
+ }
+ }
+ }
+ /**
+ * 由editor的receiver统一处理全部事件,包括clipboard事件
+ * @Editor: Naixor
+ * @Date: 2015.9.24
+ */
+ document.addEventListener('copy', beforeCopy);
+ document.addEventListener('cut', beforeCut);
+ document.addEventListener('paste', beforePaste);
+ }
+
+ return module.exports = ClipboardRuntime;
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/container.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/container.js
new file mode 100644
index 0000000..258ac1a
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/container.js
@@ -0,0 +1,33 @@
+/**
+ * @fileOverview
+ *
+ * 初始化编辑器的容器
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+define(function(require, exports, module) {
+
+ /**
+ * 最先执行的 Runtime,初始化编辑器容器
+ */
+ function ContainerRuntime() {
+ var container;
+
+ if (typeof(this.selector) == 'string') {
+ container = document.querySelector(this.selector);
+ } else {
+ container = this.selector;
+ }
+
+ if (!container) throw new Error('Invalid selector: ' + this.selector);
+
+ // 这个类名用于给编辑器添加样式
+ container.classList.add('km-editor');
+
+ // 暴露容器给其他运行时使用
+ this.container = container;
+ }
+
+ return module.exports = ContainerRuntime;
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/drag.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/drag.js
new file mode 100644
index 0000000..3fc185f
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/drag.js
@@ -0,0 +1,140 @@
+/**
+ * @fileOverview
+ *
+ * 用于拖拽节点时屏蔽键盘事件
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+define(function(require, exports, module) {
+
+ var Hotbox = require('../hotbox');
+ var Debug = require('../tool/debug');
+ var debug = new Debug('drag');
+
+ function DragRuntime() {
+ var fsm = this.fsm;
+ var minder = this.minder;
+ var hotbox = this.hotbox;
+ var receiver = this.receiver;
+ var receiverElement = receiver.element;
+
+ // setup everything to go
+ setupFsm();
+
+ // listen the fsm changes, make action.
+ function setupFsm() {
+
+ // when jumped to drag mode, enter
+ fsm.when('* -> drag', function() {
+ // now is drag mode
+ });
+
+ fsm.when('drag -> *', function(exit, enter, reason) {
+ if (reason == 'drag-finish') {
+ // now exit drag mode
+ }
+ });
+ }
+
+ var downX, downY;
+ var MOUSE_HAS_DOWN = 0;
+ var MOUSE_HAS_UP = 1;
+ var BOUND_CHECK = 20;
+ var flag = MOUSE_HAS_UP;
+ var maxX, maxY, osx, osy, containerY;
+ var freeHorizen = false, freeVirtical = false;
+ var frame;
+
+ function move(direction, speed) {
+ if (!direction) {
+ freeHorizen = freeVirtical = false;
+ frame && kity.releaseFrame(frame);
+ frame = null;
+ return;
+ }
+ if (!frame) {
+ frame = kity.requestFrame((function (direction, speed, minder) {
+ return function (frame) {
+ switch (direction) {
+ case 'left':
+ minder._viewDragger.move({x: -speed, y: 0}, 0);
+ break;
+ case 'top':
+ minder._viewDragger.move({x: 0, y: -speed}, 0);
+ break;
+ case 'right':
+ minder._viewDragger.move({x: speed, y: 0}, 0);
+ break;
+ case 'bottom':
+ minder._viewDragger.move({x: 0, y: speed}, 0);
+ break;
+ default:
+ return;
+ }
+ frame.next();
+ };
+ })(direction, speed, minder));
+ }
+ }
+
+ minder.on('mousedown', function(e) {
+ flag = MOUSE_HAS_DOWN;
+ var rect = minder.getPaper().container.getBoundingClientRect();
+ downX = e.originEvent.clientX;
+ downY = e.originEvent.clientY;
+ containerY = rect.top;
+ maxX = rect.width;
+ maxY = rect.height;
+ });
+
+ minder.on('mousemove', function(e) {
+ if (fsm.state() === 'drag' && flag == MOUSE_HAS_DOWN && minder.getSelectedNode()
+ && (Math.abs(downX - e.originEvent.clientX) > BOUND_CHECK
+ || Math.abs(downY - e.originEvent.clientY) > BOUND_CHECK)) {
+ osx = e.originEvent.clientX;
+ osy = e.originEvent.clientY - containerY;
+
+ if (osx < BOUND_CHECK) {
+ move('right', BOUND_CHECK - osx);
+ } else if (osx > maxX - BOUND_CHECK) {
+ move('left', BOUND_CHECK + osx - maxX);
+ } else {
+ freeHorizen = true;
+ }
+ if (osy < BOUND_CHECK) {
+ move('bottom', osy);
+ } else if (osy > maxY - BOUND_CHECK) {
+ move('top', BOUND_CHECK + osy - maxY);
+ } else {
+ freeVirtical = true;
+ }
+ if (freeHorizen && freeVirtical) {
+ move(false);
+ }
+ }
+ if (fsm.state() !== 'drag'
+ && flag === MOUSE_HAS_DOWN
+ && minder.getSelectedNode()
+ && (Math.abs(downX - e.originEvent.clientX) > BOUND_CHECK
+ || Math.abs(downY - e.originEvent.clientY) > BOUND_CHECK)) {
+
+ if (fsm.state() === 'hotbox') {
+ hotbox.active(Hotbox.STATE_IDLE);
+ }
+
+ return fsm.jump('drag', 'user-drag');
+ }
+ });
+
+ window.addEventListener('mouseup', function () {
+ flag = MOUSE_HAS_UP;
+ if (fsm.state() === 'drag') {
+ move(false);
+ return fsm.jump('normal', 'drag-finish');
+ }
+ }, false);
+ }
+
+ return module.exports = DragRuntime;
+});
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/fsm.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/fsm.js
new file mode 100644
index 0000000..c9ed5cb
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/fsm.js
@@ -0,0 +1,121 @@
+/**
+ * @fileOverview
+ *
+ * 编辑器状态机
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+define(function(require, exports, module) {
+
+ var Debug = require('../tool/debug');
+ var debug = new Debug('fsm');
+
+ function handlerConditionMatch(condition, when, exit, enter) {
+ if (condition.when != when) return false;
+ if (condition.enter != '*' && condition.enter != enter) return false;
+ if (condition.exit != '*' && condition.exit != exit) return;
+ return true;
+ }
+
+ function FSM(defaultState) {
+ var currentState = defaultState;
+ var BEFORE_ARROW = ' - ';
+ var AFTER_ARROW = ' -> ';
+ var handlers = [];
+
+ /**
+ * 状态跳转
+ *
+ * 会通知所有的状态跳转监视器
+ *
+ * @param {string} newState 新状态名称
+ * @param {any} reason 跳转的原因,可以作为参数传递给跳转监视器
+ */
+ this.jump = function(newState, reason) {
+ if (!reason) throw new Error('Please tell fsm the reason to jump');
+
+ var oldState = currentState;
+ var notify = [oldState, newState].concat([].slice.call(arguments, 1));
+ var i, handler;
+
+ // 跳转前
+ for (i = 0; i < handlers.length; i++) {
+ handler = handlers[i];
+ if (handlerConditionMatch(handler.condition, 'before', oldState, newState)) {
+ if (handler.apply(null, notify)) return;
+ }
+ }
+
+ currentState = newState;
+ debug.log('[{0}] {1} -> {2}', reason, oldState, newState);
+
+ // 跳转后
+ for (i = 0; i < handlers.length; i++) {
+ handler = handlers[i];
+ if (handlerConditionMatch(handler.condition, 'after', oldState, newState)) {
+ handler.apply(null, notify);
+ }
+ }
+ return currentState;
+ };
+
+ /**
+ * 返回当前状态
+ * @return {string}
+ */
+ this.state = function() {
+ return currentState;
+ };
+
+ /**
+ * 添加状态跳转监视器
+ *
+ * @param {string} condition
+ * 监视的时机
+ * "* => *" (默认)
+ *
+ * @param {Function} handler
+ * 监视函数,当状态跳转的时候,会接收三个参数
+ * * from - 跳转前的状态
+ * * to - 跳转后的状态
+ * * reason - 跳转的原因
+ */
+ this.when = function(condition, handler) {
+ if (arguments.length == 1) {
+ handler = condition;
+ condition = '* -> *';
+ }
+
+ var when, resolved, exit, enter;
+
+ resolved = condition.split(BEFORE_ARROW);
+ if (resolved.length == 2) {
+ when = 'before';
+ } else {
+ resolved = condition.split(AFTER_ARROW);
+ if (resolved.length == 2) {
+ when = 'after';
+ }
+ }
+ if (!when) throw new Error('Illegal fsm condition: ' + condition);
+
+ exit = resolved[0];
+ enter = resolved[1];
+
+ handler.condition = {
+ when: when,
+ exit: exit,
+ enter: enter
+ };
+
+ handlers.push(handler);
+ };
+ }
+
+ function FSMRumtime() {
+ this.fsm = new FSM('normal');
+ }
+
+ return module.exports = FSMRumtime;
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/history.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/history.js
new file mode 100644
index 0000000..e95c161
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/history.js
@@ -0,0 +1,133 @@
+/**
+ * @fileOverview
+ *
+ * 历史管理
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+
+
+define(function(require, exports, module) {
+ var jsonDiff = require('../tool/jsondiff');
+
+ function HistoryRuntime() {
+ var minder = this.minder;
+ var hotbox = this.hotbox;
+
+ var MAX_HISTORY = 100;
+
+ var lastSnap;
+ var patchLock;
+ var undoDiffs;
+ var redoDiffs;
+
+ function reset() {
+ undoDiffs = [];
+ redoDiffs = [];
+ lastSnap = minder.exportJson();
+ }
+
+ function makeUndoDiff() {
+ var headSnap = minder.exportJson();
+ var diff = jsonDiff(headSnap, lastSnap);
+ if (diff.length) {
+ undoDiffs.push(diff);
+ while (undoDiffs.length > MAX_HISTORY) {
+ undoDiffs.shift();
+ }
+ lastSnap = headSnap;
+ return true;
+ }
+ }
+
+ function makeRedoDiff() {
+ var revertSnap = minder.exportJson();
+ redoDiffs.push(jsonDiff(revertSnap, lastSnap));
+ lastSnap = revertSnap;
+ }
+
+ function undo() {
+ patchLock = true;
+ var undoDiff = undoDiffs.pop();
+ if (undoDiff) {
+ minder.applyPatches(undoDiff);
+ makeRedoDiff();
+ }
+ patchLock = false;
+ }
+
+ function redo() {
+ patchLock = true;
+ var redoDiff = redoDiffs.pop();
+ if (redoDiff) {
+ minder.applyPatches(redoDiff);
+ makeUndoDiff();
+ }
+ patchLock = false;
+ }
+
+ function changed() {
+ if (patchLock) return;
+ if (makeUndoDiff()) redoDiffs = [];
+ }
+
+ function hasUndo() {
+ return !!undoDiffs.length;
+ }
+
+ function hasRedo() {
+ return !!redoDiffs.length;
+ }
+
+ function updateSelection(e) {
+ if (!patchLock) return;
+ var patch = e.patch;
+ switch (patch.express) {
+ case 'node.add':
+ minder.select(patch.node.getChild(patch.index), true);
+ break;
+ case 'node.remove':
+ case 'data.replace':
+ case 'data.remove':
+ case 'data.add':
+ minder.select(patch.node, true);
+ break;
+ }
+ }
+
+ this.history = {
+ reset: reset,
+ undo: undo,
+ redo: redo,
+ hasUndo: hasUndo,
+ hasRedo: hasRedo
+ };
+ reset();
+ minder.on('contentchange', changed);
+ minder.on('import', reset);
+ minder.on('patch', updateSelection);
+
+ var main = hotbox.state('main');
+ main.button({
+ position: 'top',
+ label: '撤销',
+ key: 'Ctrl + Z',
+ enable: hasUndo,
+ action: undo,
+ next: 'idle'
+ });
+ main.button({
+ position: 'top',
+ label: '重做',
+ key: 'Ctrl + Y',
+ enable: hasRedo,
+ action: redo,
+ next: 'idle'
+ });
+ }
+
+ window.diff = jsonDiff;
+
+ return module.exports = HistoryRuntime;
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/hotbox.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/hotbox.js
new file mode 100644
index 0000000..a9a1e00
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/hotbox.js
@@ -0,0 +1,50 @@
+/**
+ * @fileOverview
+ *
+ * 热盒 Runtime
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+define(function (require, exports, module) {
+ var Hotbox = require('../hotbox');
+
+ function HotboxRuntime() {
+ var fsm = this.fsm;
+ var minder = this.minder;
+ var receiver = this.receiver;
+ var container = this.container;
+ var hotbox = new Hotbox(container);
+ hotbox.setParentFSM(fsm);
+ fsm.when('normal -> hotbox', function (exit, enter, reason) {
+ var node = minder.getSelectedNode();
+ var position;
+ if (node) {
+ var box = node.getRenderBox();
+ position = {
+ x: box.cx,
+ y: box.cy,
+ };
+ }
+ hotbox.active('main', position);
+ });
+ fsm.when('normal -> normal', function (exit, enter, reason, e) {
+ if (reason == 'shortcut-handle') {
+ var handleResult = hotbox.dispatch(e);
+ if (handleResult) {
+ e.preventDefault();
+ } else {
+ minder.dispatchKeyEvent(e);
+ }
+ }
+ });
+ fsm.when('modal -> normal', function (exit, enter, reason, e) {
+ if (reason == 'import-text-finish') {
+ receiver.element.focus();
+ }
+ });
+ this.hotbox = hotbox;
+ }
+
+ return (module.exports = HotboxRuntime);
+});
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/input.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/input.js
new file mode 100644
index 0000000..dcd5181
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/input.js
@@ -0,0 +1,360 @@
+/**
+ * @fileOverview
+ *
+ * 文本输入支持
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+define(function (require, exports, module) {
+ require('../tool/innertext');
+
+ var Debug = require('../tool/debug');
+ var debug = new Debug('input');
+
+ function InputRuntime() {
+ var fsm = this.fsm;
+ var minder = this.minder;
+ var hotbox = this.hotbox;
+ var receiver = this.receiver;
+ var receiverElement = receiver.element;
+ var isGecko = window.kity.Browser.gecko;
+ // setup everything to go
+ setupReciverElement();
+ setupFsm();
+ setupHotbox();
+ // expose editText()
+ this.editText = editText;
+ // listen the fsm changes, make action.
+ function setupFsm() {
+ // when jumped to input mode, enter
+ fsm.when('* -> input', enterInputMode);
+ // when exited, commit or exit depends on the exit reason
+ fsm.when('input -> *', function (exit, enter, reason) {
+ switch (reason) {
+ case 'input-cancel':
+ return exitInputMode();
+
+ case 'input-commit':
+ default:
+ return commitInputResult();
+ }
+ });
+ // lost focus to commit
+ receiver.onblur(function (e) {
+ if (fsm.state() == 'input') {
+ fsm.jump('normal', 'input-commit');
+ }
+ });
+ minder.on('beforemousedown', function () {
+ if (fsm.state() == 'input') {
+ fsm.jump('normal', 'input-commit');
+ }
+ });
+ minder.on('dblclick', function () {
+ if (minder.getSelectedNode() && minder._status !== 'readonly') {
+ editText();
+ }
+ });
+ }
+ // let the receiver follow the current selected node position
+ function setupReciverElement() {
+ if (debug.flaged) {
+ receiverElement.classList.add('debug');
+ }
+ receiverElement.onmousedown = function (e) {
+ // e.stopPropagation();
+ };
+ minder.on('layoutallfinish viewchange viewchanged selectionchange', function (e) {
+ // viewchange event is too frequenced, lazy it
+ if (e.type == 'viewchange' && fsm.state() != 'input') return;
+ updatePosition();
+ });
+ updatePosition();
+ }
+ // edit entrance in hotbox
+ function setupHotbox() {
+ hotbox.state('main').button({
+ position: 'center',
+ label: '编辑',
+ key: 'F2',
+ enable: function () {
+ return minder.queryCommandState('text') != -1;
+ },
+ action: editText,
+ });
+ }
+ /**
+ * 增加对字体的鉴别,以保证用户在编辑状态ctrl/cmd + b/i所触发的加粗斜体与显示一致
+ * @editor Naixor
+ * @Date 2015-12-2
+ */
+ // edit for the selected node
+ function editText() {
+ var node = minder.getSelectedNode();
+ if (!node) {
+ return;
+ }
+ var textContainer = receiverElement;
+ receiverElement.innerText = '';
+ if (node.getData('font-weight') === 'bold') {
+ var b = document.createElement('b');
+ textContainer.appendChild(b);
+ textContainer = b;
+ }
+ if (node.getData('font-style') === 'italic') {
+ var i = document.createElement('i');
+ textContainer.appendChild(i);
+ textContainer = i;
+ }
+ textContainer.innerText = minder.queryCommandValue('text');
+ if (isGecko) {
+ receiver.fixFFCaretDisappeared();
+ }
+ fsm.jump('input', 'input-request');
+ receiver.selectAll();
+ }
+ /**
+ * 增加对字体的鉴别,以保证用户在编辑状态ctrl/cmd + b/i所触发的加粗斜体与显示一致
+ * @editor Naixor
+ * @Date 2015-12-2
+ */
+ function enterInputMode() {
+ var node = minder.getSelectedNode();
+ if (node) {
+ var fontSize = node.getData('font-size') || node.getStyle('font-size');
+ receiverElement.style.fontSize = fontSize + 'px';
+ receiverElement.style.minWidth = 0;
+ receiverElement.style.minWidth = receiverElement.clientWidth + 'px';
+ receiverElement.style.fontWeight = node.getData('font-weight') || '';
+ receiverElement.style.fontStyle = node.getData('font-style') || '';
+ receiverElement.classList.add('input');
+ receiverElement.focus();
+ }
+ }
+ /**
+ * 按照文本提交操作处理
+ * @Desc: 从其他节点复制文字到另一个节点时部分浏览器(chrome)会自动包裹一个span标签,这样试用一下逻辑出来的就不是text节点二是span节点因此导致undefined的情况发生
+ * @Warning: 下方代码使用[].slice.call来将HTMLDomCollection处理成为Array,ie8及以下会有问题
+ * @Editor: Naixor
+ * @Date: 2015.9.16
+ */
+ function commitInputText(textNodes) {
+ var text = '';
+ var TAB_CHAR = '\t',
+ ENTER_CHAR = '\n',
+ STR_CHECK = /\S/,
+ SPACE_CHAR = ' ', // 针对FF,SG,BD,LB,IE等浏览器下SPACE的charCode存在为32和160的情况做处理
+ SPACE_CHAR_REGEXP = new RegExp('( |' + String.fromCharCode(160) + ')'),
+ BR = document.createElement('br');
+ var isBold = false,
+ isItalic = false;
+ for (var str, _divChildNodes, space_l, space_num, tab_num, i = 0, l = textNodes.length; i < l; i++) {
+ str = textNodes[i];
+ switch (Object.prototype.toString.call(str)) {
+ // 正常情况处理
+ case '[object HTMLBRElement]': {
+ text += ENTER_CHAR;
+ break;
+ }
+
+ case '[object Text]': {
+ // SG下会莫名其妙的加上 影响后续判断,干掉!
+ /**
+ * FF下的wholeText会导致如下问题:
+ * |123| -> 在一个节点中输入一段字符,此时TextNode为[#Text 123]
+ * 提交并重新编辑,在后面追加几个字符
+ * |123abc| -> 此时123为一个TextNode为[#Text 123, #Text abc],但是对这两个任意取值wholeText均为全部内容123abc
+ * 上述BUG仅存在在FF中,故将wholeText更改为textContent
+ */
+ str = str.textContent.replace(' ', ' ');
+ if (!STR_CHECK.test(str)) {
+ space_l = str.length;
+ while (space_l--) {
+ if (SPACE_CHAR_REGEXP.test(str[space_l])) {
+ text += SPACE_CHAR;
+ } else if (str[space_l] === TAB_CHAR) {
+ text += TAB_CHAR;
+ }
+ }
+ } else {
+ text += str;
+ }
+ break;
+ }
+
+ // ctrl + b/i 会给字体加上/标签来实现黑体和斜体
+ case '[object HTMLElement]': {
+ switch (str.nodeName) {
+ case 'B': {
+ isBold = true;
+ break;
+ }
+
+ case 'I': {
+ isItalic = true;
+ break;
+ }
+
+ default: {
+ }
+ }
+ [].splice.apply(textNodes, [i, 1].concat([].slice.call(str.childNodes)));
+ l = textNodes.length;
+ i--;
+ break;
+ }
+
+ // 被增加span标签的情况会被处理成正常情况并会推交给上面处理
+ case '[object HTMLSpanElement]': {
+ [].splice.apply(textNodes, [i, 1].concat([].slice.call(str.childNodes)));
+ l = textNodes.length;
+ i--;
+ break;
+ }
+
+ // 若标签为image标签,则判断是否为合法url,是将其加载进来
+ case '[object HTMLImageElement]': {
+ if (str.src) {
+ if (/http(|s):\/\//.test(str.src)) {
+ minder.execCommand('Image', str.src, str.alt);
+ } else {
+ }
+ }
+ break;
+ }
+
+ // 被增加div标签的情况会被处理成正常情况并会推交给上面处理
+ case '[object HTMLDivElement]': {
+ _divChildNodes = [];
+ for (var di = 0, l = str.childNodes.length; di < l; di++) {
+ _divChildNodes.push(str.childNodes[di]);
+ }
+ _divChildNodes.push(BR);
+ [].splice.apply(textNodes, [i, 1].concat(_divChildNodes));
+ l = textNodes.length;
+ i--;
+ break;
+ }
+
+ default: {
+ if (str && str.childNodes.length) {
+ _divChildNodes = [];
+ for (var di = 0, l = str.childNodes.length; di < l; di++) {
+ _divChildNodes.push(str.childNodes[di]);
+ }
+ _divChildNodes.push(BR);
+ [].splice.apply(textNodes, [i, 1].concat(_divChildNodes));
+ l = textNodes.length;
+ i--;
+ } else {
+ if (str && str.textContent !== undefined) {
+ text += str.textContent;
+ } else {
+ text += '';
+ }
+ }
+ }
+ }
+ }
+ text = text.replace(/^\n*|\n*$/g, '');
+ text = text.replace(new RegExp('(\n|\r|\n\r)( |' + String.fromCharCode(160) + '){4}', 'g'), '$1\t');
+ minder.getSelectedNode().setText(text);
+ if (isBold) {
+ minder.queryCommandState('bold') || minder.execCommand('bold');
+ } else {
+ minder.queryCommandState('bold') && minder.execCommand('bold');
+ }
+ if (isItalic) {
+ minder.queryCommandState('italic') || minder.execCommand('italic');
+ } else {
+ minder.queryCommandState('italic') && minder.execCommand('italic');
+ }
+ exitInputMode();
+ return text;
+ }
+ /**
+ * 判断节点的文本信息是否是
+ * @Desc: 从其他节点复制文字到另一个节点时部分浏览器(chrome)会自动包裹一个span标签,这样使用以下逻辑出来的就不是text节点二是span节点因此导致undefined的情况发生
+ * @Notice: 此处逻辑应该拆分到 kityminder-core/core/data中去,单独增加一个对某个节点importJson的事件
+ * @Editor: Naixor
+ * @Date: 2015.9.16
+ */
+ function commitInputNode(node, text) {
+ try {
+ minder.decodeData('text', text).then(function (json) {
+ function importText(node, json, minder) {
+ var data = json.data;
+ node.setText(data.text || '');
+ var childrenTreeData = json.children || [];
+ for (var i = 0; i < childrenTreeData.length; i++) {
+ var childNode = minder.createNode(null, node);
+ importText(childNode, childrenTreeData[i], minder);
+ }
+ return node;
+ }
+ importText(node, json, minder);
+ minder.fire('contentchange');
+ minder.getRoot().renderTree();
+ minder.layout(300);
+ });
+ } catch (e) {
+ minder.fire('contentchange');
+ minder.getRoot().renderTree();
+ // 无法被转换成脑图节点则不处理
+ if (e.toString() !== 'Error: Invalid local format') {
+ throw e;
+ }
+ }
+ }
+ function commitInputResult() {
+ /**
+ * @Desc: 进行如下处理:
+ * 根据用户的输入判断是否生成新的节点
+ * fix #83 https://github.com/fex-team/kityminder-editor/issues/83
+ * @Editor: Naixor
+ * @Date: 2015.9.16
+ */
+ var textNodes = [].slice.call(receiverElement.childNodes);
+ /**
+ * @Desc: 增加setTimeout的原因:ie下receiverElement.innerHTML=""会导致后
+ * 面commitInputText中使用textContent报错,不要问我什么原因!
+ * @Editor: Naixor
+ * @Date: 2015.12.14
+ */
+ setTimeout(function () {
+ // 解决过大内容导致SVG窜位问题
+ receiverElement.innerHTML = '';
+ }, 0);
+ var node = minder.getSelectedNode();
+ textNodes = commitInputText(textNodes);
+ commitInputNode(node, textNodes);
+ if (node.type == 'root') {
+ var rootText = minder.getRoot().getText();
+ minder.fire('initChangeRoot', {
+ text: rootText,
+ });
+ }
+ }
+ function exitInputMode() {
+ receiverElement.classList.remove('input');
+ receiver.selectAll();
+ }
+ function updatePosition() {
+ var planed = updatePosition;
+ var focusNode = minder.getSelectedNode();
+ if (!focusNode) return;
+ if (!planed.timer) {
+ planed.timer = setTimeout(function () {
+ var box = focusNode.getRenderBox('TextRenderer');
+ receiverElement.style.left = Math.round(box.x) + 'px';
+ receiverElement.style.top = (debug.flaged ? Math.round(box.bottom + 30) : Math.round(box.y)) + 'px';
+ //receiverElement.focus();
+ planed.timer = 0;
+ });
+ }
+ }
+ }
+
+ return (module.exports = InputRuntime);
+});
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/jumping.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/jumping.js
new file mode 100644
index 0000000..3a48446
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/jumping.js
@@ -0,0 +1,193 @@
+/**
+ * @fileOverview
+ *
+ * 根据按键控制状态机的跳转
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+define(function (require, exports, module) {
+ var Hotbox = require('../hotbox');
+
+ // Nice: http://unixpapa.com/js/key.html
+ function isIntendToInput(e) {
+ if (e.ctrlKey || e.metaKey || e.altKey) return false;
+
+ // a-zA-Z
+ if (e.keyCode >= 65 && e.keyCode <= 90) return true;
+
+ // 0-9 以及其上面的符号
+ if (e.keyCode >= 48 && e.keyCode <= 57) return true;
+
+ // 小键盘区域 (除回车外)
+ if (e.keyCode != 108 && e.keyCode >= 96 && e.keyCode <= 111) return true;
+
+ // 小键盘区域 (除回车外)
+ // @yinheli from pull request
+ if (e.keyCode != 108 && e.keyCode >= 96 && e.keyCode <= 111) return true;
+
+ // 输入法
+ if (e.keyCode == 229 || e.keyCode === 0) return true;
+
+ return false;
+ }
+ /**
+ * @Desc: 下方使用receiver.enable()和receiver.disable()通过
+ * 修改div contenteditable属性的hack来解决开启热核后依然无法屏蔽浏览器输入的bug;
+ * 特别: win下FF对于此种情况必须要先blur在focus才能解决,但是由于这样做会导致用户
+ * 输入法状态丢失,因此对FF暂不做处理
+ * @Editor: Naixor
+ * @Date: 2015.09.14
+ */
+ function JumpingRuntime() {
+ var fsm = this.fsm;
+ var minder = this.minder;
+ var receiver = this.receiver;
+ var container = this.container;
+ var receiverElement = receiver.element;
+ var hotbox = this.hotbox;
+ var compositionLock = false;
+
+ // normal -> *
+ receiver.listen('normal', function (e) {
+ // 为了防止处理进入edit模式而丢失处理的首字母,此时receiver必须为enable
+ receiver.enable();
+ // normal -> hotbox
+ if (e.is('Space')) {
+ e.preventDefault();
+ // safari下Space触发hotbox,然而这时Space已在receiver上留下作案痕迹,因此抹掉
+ if (kity.Browser.safari) {
+ receiverElement.innerHTML = '';
+ }
+ return fsm.jump('hotbox', 'space-trigger');
+ }
+
+ /**
+ * check
+ * @editor Naixor
+ * @Date 2015-12-2
+ */
+ switch (e.type) {
+ case 'keydown': {
+ if (minder.getSelectedNode()) {
+ if (isIntendToInput(e)) {
+ return fsm.jump('input', 'user-input');
+ }
+ } else {
+ receiverElement.innerHTML = '';
+ }
+ // normal -> normal shortcut
+ fsm.jump('normal', 'shortcut-handle', e);
+ break;
+ }
+ case 'keyup': {
+ break;
+ }
+ default: {
+ }
+ }
+ });
+
+ // hotbox -> normal
+ receiver.listen('hotbox', function (e) {
+ receiver.disable();
+ e.preventDefault();
+ var handleResult = hotbox.dispatch(e);
+ if (hotbox.state() == Hotbox.STATE_IDLE && fsm.state() == 'hotbox') {
+ return fsm.jump('normal', 'hotbox-idle');
+ }
+ });
+
+ // input => normal
+ receiver.listen('input', function (e) {
+ receiver.enable();
+ if (e.type == 'keydown') {
+ if (e.is('Enter')) {
+ e.preventDefault();
+ return fsm.jump('normal', 'input-commit');
+ }
+ if (e.is('Esc')) {
+ e.preventDefault();
+ return fsm.jump('normal', 'input-cancel');
+ }
+ if (e.is('Tab') || e.is('Shift + Tab')) {
+ e.preventDefault();
+ }
+ } else if (e.type == 'keyup' && e.is('Esc')) {
+ e.preventDefault();
+ if (!compositionLock) {
+ return fsm.jump('normal', 'input-cancel');
+ }
+ } else if (e.type == 'compositionstart') {
+ compositionLock = true;
+ } else if (e.type == 'compositionend') {
+ setTimeout(function () {
+ compositionLock = false;
+ });
+ }
+ });
+
+ //////////////////////////////////////////////
+ /// 右键呼出热盒
+ /// 判断的标准是:按下的位置和结束的位置一致
+ //////////////////////////////////////////////
+ var downX, downY;
+ var MOUSE_RB = 2; // 右键
+
+ container.addEventListener(
+ 'mousedown',
+ function (e) {
+ if (e.button == MOUSE_RB) {
+ e.preventDefault();
+ }
+ if (fsm.state() == 'hotbox') {
+ hotbox.active(Hotbox.STATE_IDLE);
+ fsm.jump('normal', 'blur');
+ } else if (fsm.state() == 'normal' && e.button == MOUSE_RB) {
+ downX = e.clientX;
+ downY = e.clientY;
+ }
+ },
+ false
+ );
+
+ container.addEventListener(
+ 'mousewheel',
+ function (e) {
+ if (fsm.state() == 'hotbox') {
+ hotbox.active(Hotbox.STATE_IDLE);
+ fsm.jump('normal', 'mousemove-blur');
+ }
+ },
+ false
+ );
+
+ container.addEventListener('contextmenu', function (e) {
+ e.preventDefault();
+ });
+
+ container.addEventListener(
+ 'mouseup',
+ function (e) {
+ if (fsm.state() != 'normal') {
+ return;
+ }
+ if (e.button != MOUSE_RB || e.clientX != downX || e.clientY != downY) {
+ return;
+ }
+ if (!minder.getSelectedNode()) {
+ return;
+ }
+ fsm.jump('hotbox', 'content-menu');
+ },
+ false
+ );
+
+ // 阻止热盒事件冒泡,在热盒正确执行前导致热盒关闭
+ hotbox.$element.addEventListener('mousedown', function (e) {
+ e.stopPropagation();
+ });
+ }
+
+ return (module.exports = JumpingRuntime);
+});
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/minder.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/minder.js
new file mode 100644
index 0000000..7a0d85a
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/minder.js
@@ -0,0 +1,31 @@
+/**
+ * @fileOverview
+ *
+ * 脑图示例运行时
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+define(function(require, exports, module) {
+ var Minder = require('../minder');
+
+ function MinderRuntime() {
+
+ // 不使用 kityminder 的按键处理,由 ReceiverRuntime 统一处理
+ var minder = new Minder({
+ enableKeyReceiver: false,
+ enableAnimation: true
+ });
+
+ // 渲染,初始化
+ minder.renderTo(this.selector);
+ minder.setTheme(null);
+ minder.select(minder.getRoot(), true);
+ minder.execCommand('name', '中心主题');
+
+ // 导出给其它 Runtime 使用
+ this.minder = minder;
+ }
+
+ return module.exports = MinderRuntime;
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/node.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/node.js
new file mode 100644
index 0000000..b8bf9a6
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/node.js
@@ -0,0 +1,112 @@
+define(function(require, exports, module) {
+
+ function NodeRuntime() {
+ var runtime = this;
+ var minder = this.minder;
+ var hotbox = this.hotbox;
+ var fsm = this.fsm;
+
+ var main = hotbox.state('main');
+
+ var buttons = [
+ '前移:Alt+Up:ArrangeUp',
+ '下级:Tab|Insert:AppendChildNode',
+ '同级:Enter:AppendSiblingNode',
+ '后移:Alt+Down:ArrangeDown',
+ '删除:Delete|Backspace:RemoveNode',
+ '上级:Shift+Tab|Shift+Insert:AppendParentNode'
+ //'全选:Ctrl+A:SelectAll'
+ ];
+
+ var AppendLock = 0;
+
+ buttons.forEach(function(button) {
+ var parts = button.split(':');
+ var label = parts.shift();
+ var key = parts.shift();
+ var command = parts.shift();
+ main.button({
+ position: 'ring',
+ label: label,
+ key: key,
+ action: function() {
+ if (command.indexOf('Append') === 0) {
+ AppendLock++;
+ minder.execCommand(command, '分支主题');
+
+ // provide in input runtime
+ function afterAppend () {
+ if (!--AppendLock) {
+ runtime.editText();
+ }
+ minder.off('layoutallfinish', afterAppend);
+ }
+ minder.on('layoutallfinish', afterAppend);
+ } else {
+ minder.execCommand(command);
+ fsm.jump('normal', 'command-executed');
+ }
+ },
+ enable: function() {
+ return minder.queryCommandState(command) != -1;
+ }
+ });
+ });
+
+ main.button({
+ position: 'bottom',
+ label: '导入节点',
+ key: 'Alt + V',
+ enable: function() {
+ var selectedNodes = minder.getSelectedNodes();
+ return selectedNodes.length == 1;
+ },
+ action: importNodeData,
+ next: 'idle'
+ });
+
+ main.button({
+ position: 'bottom',
+ label: '导出节点',
+ key: 'Alt + C',
+ enable: function() {
+ var selectedNodes = minder.getSelectedNodes();
+ return selectedNodes.length == 1;
+ },
+ action: exportNodeData,
+ next: 'idle'
+ });
+
+ function importNodeData() {
+ minder.fire('importNodeData');
+ }
+
+ function exportNodeData() {
+ minder.fire('exportNodeData');
+ }
+
+ //main.button({
+ // position: 'ring',
+ // key: '/',
+ // action: function(){
+ // if (!minder.queryCommandState('expand')) {
+ // minder.execCommand('expand');
+ // } else if (!minder.queryCommandState('collapse')) {
+ // minder.execCommand('collapse');
+ // }
+ // },
+ // enable: function() {
+ // return minder.queryCommandState('expand') != -1 || minder.queryCommandState('collapse') != -1;
+ // },
+ // beforeShow: function() {
+ // if (!minder.queryCommandState('expand')) {
+ // this.$button.children[0].innerHTML = '展开';
+ // } else {
+ // this.$button.children[0].innerHTML = '收起';
+ // }
+ // }
+ //})
+ }
+
+ return module.exports = NodeRuntime;
+});
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/priority.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/priority.js
new file mode 100644
index 0000000..3b91dcc
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/priority.js
@@ -0,0 +1,51 @@
+define(function(require, exports, module){
+
+ function PriorityRuntime() {
+ var minder = this.minder;
+ var hotbox = this.hotbox;
+
+ var main = hotbox.state('main');
+
+ main.button({
+ position: 'top',
+ label: '优先级',
+ key: 'P',
+ next: 'priority',
+ enable: function() {
+ return minder.queryCommandState('priority') != -1;
+ }
+ });
+
+ var priority = hotbox.state('priority');
+ '123456789'.replace(/./g, function(p) {
+ priority.button({
+ position: 'ring',
+ label: 'P' + p,
+ key: p,
+ action: function() {
+ minder.execCommand('Priority', p);
+ }
+ });
+ });
+
+ priority.button({
+ position: 'center',
+ label: '移除',
+ key: 'Del',
+ action: function() {
+ minder.execCommand('Priority', 0);
+ }
+ });
+
+ priority.button({
+ position: 'top',
+ label: '返回',
+ key: 'esc',
+ next: 'back'
+ });
+
+ }
+
+ return module.exports = PriorityRuntime;
+
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/progress.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/progress.js
new file mode 100644
index 0000000..3ce7d30
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/progress.js
@@ -0,0 +1,52 @@
+define(function(require, exports, module){
+
+ function ProgressRuntime() {
+ var minder = this.minder;
+ var hotbox = this.hotbox;
+
+ var main = hotbox.state('main');
+
+ main.button({
+ position: 'top',
+ label: '进度',
+ key: 'G',
+ next: 'progress',
+ enable: function() {
+ return minder.queryCommandState('progress') != -1;
+ }
+ });
+
+ var progress = hotbox.state('progress');
+ '012345678'.replace(/./g, function(p) {
+ progress.button({
+ position: 'ring',
+ label: 'G' + p,
+ key: p,
+ action: function() {
+ minder.execCommand('Progress', parseInt(p) + 1);
+ }
+ });
+ });
+
+ progress.button({
+ position: 'center',
+ label: '移除',
+ key: 'Del',
+ action: function() {
+ minder.execCommand('Progress', 0);
+ }
+ });
+
+ progress.button({
+ position: 'top',
+ label: '返回',
+ key: 'esc',
+ next: 'back'
+ });
+
+
+ }
+
+ return module.exports = ProgressRuntime;
+
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/receiver.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/receiver.js
new file mode 100644
index 0000000..ba34c3d
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/runtime/receiver.js
@@ -0,0 +1,145 @@
+/**
+ * @fileOverview
+ *
+ * 键盘事件接收/分发器
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+
+define(function(require, exports, module) {
+ var key = require('../tool/key');
+
+ function ReceiverRuntime() {
+ var fsm = this.fsm;
+ var minder = this.minder;
+ var me = this;
+
+ // 接收事件的 div
+ var element = document.createElement('div');
+ element.contentEditable = true;
+ /**
+ * @Desc: 增加tabindex属性使得element的contenteditable不管是trur还是false都能有focus和blur事件
+ * @Editor: Naixor
+ * @Date: 2015.09.14
+ */
+ element.setAttribute('tabindex', -1);
+ element.classList.add('receiver');
+ element.onkeydown = element.onkeypress = element.onkeyup = dispatchKeyEvent;
+ element.addEventListener('compositionstart', dispatchKeyEvent);
+ // element.addEventListener('compositionend', dispatchKeyEvent);
+ this.container.appendChild(element);
+
+ // receiver 对象
+ var receiver = {
+ element: element,
+ selectAll: function() {
+ // 保证有被选中的
+ if (!element.innerHTML) element.innerHTML = ' ';
+ var range = document.createRange();
+ var selection = window.getSelection();
+ range.selectNodeContents(element);
+ selection.removeAllRanges();
+ selection.addRange(range);
+ element.focus();
+ },
+ /**
+ * @Desc: 增加enable和disable方法用于解决热核态的输入法屏蔽问题
+ * @Editor: Naixor
+ * @Date: 2015.09.14
+ */
+ enable: function() {
+ element.setAttribute("contenteditable", true);
+ },
+ disable: function() {
+ element.setAttribute("contenteditable", false);
+ },
+ /**
+ * @Desc: hack FF下div contenteditable的光标丢失BUG
+ * @Editor: Naixor
+ * @Date: 2015.10.15
+ */
+ fixFFCaretDisappeared: function() {
+ element.removeAttribute("contenteditable");
+ element.setAttribute("contenteditable", "true");
+ element.blur();
+ element.focus();
+ },
+ /**
+ * 以此事件代替通过mouse事件来判断receiver丢失焦点的事件
+ * @editor Naixor
+ * @Date 2015-12-2
+ */
+ onblur: function (handler) {
+ element.onblur = handler;
+ }
+ };
+ receiver.selectAll();
+
+ minder.on('beforemousedown', receiver.selectAll);
+ minder.on('receiverfocus', receiver.selectAll);
+ minder.on('readonly', function() {
+ // 屏蔽minder的事件接受,删除receiver和hotbox
+ minder.disable();
+ editor.receiver.element.parentElement.removeChild(editor.receiver.element);
+ editor.hotbox.$container.removeChild(editor.hotbox.$element);
+ });
+
+ // 侦听器,接收到的事件会派发给所有侦听器
+ var listeners = [];
+
+ // 侦听指定状态下的事件,如果不传 state,侦听所有状态
+ receiver.listen = function(state, listener) {
+ if (arguments.length == 1) {
+ listener = state;
+ state = '*';
+ }
+ listener.notifyState = state;
+ listeners.push(listener);
+ };
+
+ function dispatchKeyEvent(e) {
+ e.is = function(keyExpression) {
+ var subs = keyExpression.split('|');
+ for (var i = 0; i < subs.length; i++) {
+ if (key.is(this, subs[i])) return true;
+ }
+ return false;
+ };
+ var listener, jumpState;
+ for (var i = 0; i < listeners.length; i++) {
+
+ listener = listeners[i];
+ // 忽略不在侦听状态的侦听器
+ if (listener.notifyState != '*' && listener.notifyState != fsm.state()) {
+ continue;
+ }
+
+ /**
+ *
+ * 对于所有的侦听器,只允许一种处理方式:跳转状态。
+ * 如果侦听器确定要跳转,则返回要跳转的状态。
+ * 每个事件只允许一个侦听器进行状态跳转
+ * 跳转动作由侦听器自行完成(因为可能需要在跳转时传递 reason),返回跳转结果即可。
+ * 比如:
+ *
+ * ```js
+ * receiver.listen('normal', function(e) {
+ * if (isSomeReasonForJumpState(e)) {
+ * return fsm.jump('newstate', e);
+ * }
+ * });
+ * ```
+ */
+ if (listener.call(null, e)) {
+ return;
+ }
+ }
+ }
+
+ this.receiver = receiver;
+ }
+
+ return module.exports = ReceiverRuntime;
+
+});
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/tool/debug.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/tool/debug.js
new file mode 100644
index 0000000..53a4773
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/tool/debug.js
@@ -0,0 +1,51 @@
+/**
+ * @fileOverview
+ *
+ * 支持各种调试后门
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+define(function (require, exports, module) {
+ var format = require('./format');
+
+ function noop() {}
+
+ function stringHash(str) {
+ var hash = 0;
+ for (var i = 0; i < str.length; i++) {
+ hash += str.charCodeAt(i);
+ }
+ return hash;
+ }
+
+ /* global console */
+ function Debug(flag) {
+ if (typeof window === 'undefined') return;
+
+ var debugMode = (this.flaged = window.location.search.indexOf(flag) != -1);
+
+ if (debugMode) {
+ var h = stringHash(flag) % 360;
+
+ var flagStyle = format(
+ 'background: hsl({0}, 50%, 80%); ' +
+ 'color: hsl({0}, 100%, 30%); ' +
+ 'padding: 2px 3px; ' +
+ 'margin: 1px 3px 0 0;' +
+ 'border-radius: 2px;',
+ h
+ );
+
+ var textStyle = 'background: none; color: black;';
+ this.log = function () {
+ var output = format.apply(null, arguments);
+ console.log(format('%c{0}%c{1}', flag, output), flagStyle, textStyle);
+ };
+ } else {
+ this.log = noop;
+ }
+ }
+
+ return (module.exports = Debug);
+});
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/tool/format.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/tool/format.js
new file mode 100644
index 0000000..2e168dc
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/tool/format.js
@@ -0,0 +1,11 @@
+define(function(require, exports, module) {
+ function format(template, args) {
+ if (typeof(args) != 'object') {
+ args = [].slice.call(arguments, 1);
+ }
+ return String(template).replace(/\{(\w+)\}/ig, function(match, $key) {
+ return args[$key] || $key;
+ });
+ }
+ return module.exports = format;
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/tool/innertext.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/tool/innertext.js
new file mode 100644
index 0000000..912fdb9
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/tool/innertext.js
@@ -0,0 +1,54 @@
+/**
+ * @fileOverview
+ *
+ * innerText polyfill
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+
+
+define(function(require, exports, module) {
+ if ((!('innerText' in document.createElement('a'))) && ('getSelection' in window)) {
+ HTMLElement.prototype.__defineGetter__('innerText', function() {
+ var selection = window.getSelection(),
+ ranges = [],
+ str, i;
+
+ // Save existing selections.
+ for (i = 0; i < selection.rangeCount; i++) {
+ ranges[i] = selection.getRangeAt(i);
+ }
+
+ // Deselect everything.
+ selection.removeAllRanges();
+
+ // Select `el` and all child nodes.
+ // 'this' is the element .innerText got called on
+ selection.selectAllChildren(this);
+
+ // Get the string representation of the selected nodes.
+ str = selection.toString();
+
+ // Deselect everything. Again.
+ selection.removeAllRanges();
+
+ // Restore all formerly existing selections.
+ for (i = 0; i < ranges.length; i++) {
+ selection.addRange(ranges[i]);
+ }
+
+ // Oh look, this is what we wanted.
+ // String representation of the element, close to as rendered.
+ return str;
+ });
+ HTMLElement.prototype.__defineSetter__('innerText', function(text) {
+ /**
+ * @Desc: 解决FireFox节点内容删除后text为null,出现报错的问题
+ * @Editor: Naixor
+ * @Date: 2015.9.16
+ */
+ this.innerHTML = (text || '').replace(//g, '>').replace(/\n/g, '
');
+ });
+ }
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/tool/jsondiff.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/tool/jsondiff.js
new file mode 100644
index 0000000..dac36e1
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/tool/jsondiff.js
@@ -0,0 +1,91 @@
+/**
+ * @fileOverview
+ *
+ *
+ *
+ * @author: techird
+ * @copyright: Baidu FEX, 2014
+ */
+
+
+define(function(require, exports, module) {
+ /*!
+ * https://github.com/Starcounter-Jack/Fast-JSON-Patch
+ * json-patch-duplex.js 0.5.0
+ * (c) 2013 Joachim Wester
+ * MIT license
+ */
+
+ var _objectKeys = (function () {
+ if (Object.keys)
+ return Object.keys;
+
+ return function (o) {
+ var keys = [];
+ for (var i in o) {
+ if (o.hasOwnProperty(i)) {
+ keys.push(i);
+ }
+ }
+ return keys;
+ };
+ })();
+ function escapePathComponent(str) {
+ if (str.indexOf('/') === -1 && str.indexOf('~') === -1)
+ return str;
+ return str.replace(/~/g, '~0').replace(/\//g, '~1');
+ }
+ function deepClone(obj) {
+ if (typeof obj === "object") {
+ return JSON.parse(JSON.stringify(obj));
+ } else {
+ return obj;
+ }
+ }
+
+ // Dirty check if obj is different from mirror, generate patches and update mirror
+ function _generate(mirror, obj, patches, path) {
+ var newKeys = _objectKeys(obj);
+ var oldKeys = _objectKeys(mirror);
+ var changed = false;
+ var deleted = false;
+
+ for (var t = oldKeys.length - 1; t >= 0; t--) {
+ var key = oldKeys[t];
+ var oldVal = mirror[key];
+ if (obj.hasOwnProperty(key)) {
+ var newVal = obj[key];
+ if (typeof oldVal == "object" && oldVal != null && typeof newVal == "object" && newVal != null) {
+ _generate(oldVal, newVal, patches, path + "/" + escapePathComponent(key));
+ } else {
+ if (oldVal != newVal) {
+ changed = true;
+ patches.push({ op: "replace", path: path + "/" + escapePathComponent(key), value: deepClone(newVal) });
+ }
+ }
+ } else {
+ patches.push({ op: "remove", path: path + "/" + escapePathComponent(key) });
+ deleted = true; // property has been deleted
+ }
+ }
+
+ if (!deleted && newKeys.length == oldKeys.length) {
+ return;
+ }
+
+ for (var t = 0; t < newKeys.length; t++) {
+ var key = newKeys[t];
+ if (!mirror.hasOwnProperty(key)) {
+ patches.push({ op: "add", path: path + "/" + escapePathComponent(key), value: deepClone(obj[key]) });
+ }
+ }
+ }
+
+ function compare(tree1, tree2) {
+ var patches = [];
+ _generate(tree1, tree2, patches, '');
+ return patches;
+ }
+
+ return module.exports = compare;
+});
\ No newline at end of file
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/tool/key.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/tool/key.js
new file mode 100644
index 0000000..5a403b9
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/tool/key.js
@@ -0,0 +1,69 @@
+define(function(require, exports, module) {
+ var keymap = require('./keymap');
+
+ var CTRL_MASK = 0x1000;
+ var ALT_MASK = 0x2000;
+ var SHIFT_MASK = 0x4000;
+
+ function hash(unknown) {
+ if (typeof(unknown) == 'string') {
+ return hashKeyExpression(unknown);
+ }
+ return hashKeyEvent(unknown);
+ }
+ function is(a, b) {
+ return a && b && hash(a) == hash(b);
+ }
+ exports.hash = hash;
+ exports.is = is;
+
+
+ function hashKeyEvent(keyEvent) {
+ var hashCode = 0;
+ if (keyEvent.ctrlKey || keyEvent.metaKey) {
+ hashCode |= CTRL_MASK;
+ }
+ if (keyEvent.altKey) {
+ hashCode |= ALT_MASK;
+ }
+ if (keyEvent.shiftKey) {
+ hashCode |= SHIFT_MASK;
+ }
+ // Shift, Control, Alt KeyCode ignored.
+ if ([16, 17, 18, 91].indexOf(keyEvent.keyCode) === -1) {
+ /**
+ * 解决浏览器输入法状态下对keyDown的keyCode判断不准确的问题,使用keyIdentifier,
+ * 可以解决chrome和safari下的各种问题,其他浏览器依旧有问题,然而那并不影响我们对特
+ * 需判断的按键进行判断(比如Space在safari输入法态下就是229,其他的就不是)
+ * @editor Naixor
+ * @Date 2015-12-2
+ */
+ if (keyEvent.keyCode === 229 && keyEvent.keyIdentifier) {
+ return hashCode |= parseInt(keyEvent.keyIdentifier.substr(2), 16);
+ }
+ hashCode |= keyEvent.keyCode;
+ }
+ return hashCode;
+ }
+
+ function hashKeyExpression(keyExpression) {
+ var hashCode = 0;
+ keyExpression.toLowerCase().split(/\s*\+\s*/).forEach(function(name) {
+ switch(name) {
+ case 'ctrl':
+ case 'cmd':
+ hashCode |= CTRL_MASK;
+ break;
+ case 'alt':
+ hashCode |= ALT_MASK;
+ break;
+ case 'shift':
+ hashCode |= SHIFT_MASK;
+ break;
+ default:
+ hashCode |= keymap[name];
+ }
+ });
+ return hashCode;
+ }
+});
diff --git a/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/tool/keymap.js b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/tool/keymap.js
new file mode 100644
index 0000000..0e587da
--- /dev/null
+++ b/packages/client/src/tiptap/wrappers/mind/kityminder/kity-editor/tool/keymap.js
@@ -0,0 +1,82 @@
+define(function(require, exports, module) {
+ var keymap = {
+
+ 'Shift': 16,
+ 'Control': 17,
+ 'Alt': 18,
+ 'CapsLock': 20,
+
+ 'BackSpace': 8,
+ 'Tab': 9,
+ 'Enter': 13,
+ 'Esc': 27,
+ 'Space': 32,
+
+ 'PageUp': 33,
+ 'PageDown': 34,
+ 'End': 35,
+ 'Home': 36,
+
+ 'Insert': 45,
+
+ 'Left': 37,
+ 'Up': 38,
+ 'Right': 39,
+ 'Down': 40,
+
+ 'Direction': {
+ 37: 1,
+ 38: 1,
+ 39: 1,
+ 40: 1
+ },
+
+ 'Del': 46,
+
+ 'NumLock': 144,
+
+ 'Cmd': 91,
+ 'CmdFF': 224,
+ 'F1': 112,
+ 'F2': 113,
+ 'F3': 114,
+ 'F4': 115,
+ 'F5': 116,
+ 'F6': 117,
+ 'F7': 118,
+ 'F8': 119,
+ 'F9': 120,
+ 'F10': 121,
+ 'F11': 122,
+ 'F12': 123,
+
+ '`': 192,
+ '=': 187,
+ '-': 189,
+
+ '/': 191,
+ '.': 190
+ };
+
+ // 小写适配
+ for (var key in keymap) {
+ if (keymap.hasOwnProperty(key)) {
+ keymap[key.toLowerCase()] = keymap[key];
+ }
+ }
+ var aKeyCode = 65;
+ var aCharCode = 'a'.charCodeAt(0);
+
+ // letters
+ 'abcdefghijklmnopqrstuvwxyz'.split('').forEach(function(letter) {
+ keymap[letter] = aKeyCode + (letter.charCodeAt(0) - aCharCode);
+ });
+
+ // numbers
+ var n = 9;
+ do {
+ keymap[n.toString()] = n + 48;
+ } while (--n);
+
+ module.exports = keymap;
+});
\ No newline at end of file
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 62054ed..f45e86d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -91,6 +91,7 @@ importers:
dompurify: ^2.3.5
interactjs: ^1.10.11
katex: ^0.15.2
+ kity: ^2.0.4
lib0: ^0.2.47
lowlight: ^2.5.0
markdown-it: ^12.3.2
@@ -173,6 +174,7 @@ importers:
dompurify: 2.3.5
interactjs: 1.10.11
katex: 0.15.2
+ kity: 2.0.4
lib0: 0.2.47
lowlight: 2.5.0
markdown-it: 12.3.2
@@ -5426,6 +5428,10 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
+ /kity/2.0.4:
+ resolution: {integrity: sha512-u6jPit9N44gPTMzB7EiX3szurI7JB6HPswJESQiS0UnrNiceOeLxGRAm0mgGRuRnkGXwmA3Pzn7hZ1rauINpOA==}
+ dev: false
+
/kleur/3.0.3:
resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
engines: {node: '>=6'}