├── .gitattributes
├── .gitignore
├── .prettierignore
├── .prettierrc.js
├── .vscode
├── extensions.json
├── launch.json
├── settings.json
└── tasks.json
├── .vscodeignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── package.json
├── resources
└── icon.png
├── src
├── constant
│ └── index.ts
├── extension.ts
├── services
│ ├── image.ts
│ ├── index.ts
│ ├── markdown.ts
│ └── xmind.ts
├── test
│ ├── extension.test.ts
│ └── index.ts
└── types
│ └── index.ts
├── tsconfig.json
├── tslint.json
├── webui
├── .babelrc
├── .bowerrc
├── .gitignore
├── .jscsrc
├── .jshintrc
├── Gruntfile.js
├── LICENSE
├── README.md
├── bower.json
├── favicon.ico
├── index.html
├── less
│ ├── _navigator.less
│ ├── _tool_group.less
│ ├── _vars.less
│ ├── editor.less
│ ├── imageDialog.less
│ └── topTab
│ │ ├── appearance
│ │ ├── colorPanel.less
│ │ ├── export.less
│ │ ├── fontOperator.less
│ │ ├── layout.less
│ │ ├── styleOperator.less
│ │ ├── templatePanel.less
│ │ └── themePanel.less
│ │ ├── idea
│ │ ├── appendNode.less
│ │ ├── arrange.less
│ │ ├── hyperlink.less
│ │ ├── image.less
│ │ ├── note.less
│ │ ├── noteEditor.less
│ │ ├── operation.less
│ │ ├── priority.less
│ │ ├── progress.less
│ │ ├── resource.less
│ │ └── undoRedo.less
│ │ ├── searchBox.less
│ │ ├── topTab.less
│ │ └── view
│ │ ├── expand.less
│ │ ├── search.less
│ │ └── select.less
├── main.js
├── mindmap.html
├── package.json
├── rollup.config.js
├── server
│ └── imageUpload.php
├── src
│ ├── editor.js
│ ├── expose-editor.js
│ ├── hotbox.js
│ ├── lang.js
│ ├── minder.js
│ ├── runtime
│ │ ├── clipboard-mimetype.js
│ │ ├── clipboard.js
│ │ ├── container.js
│ │ ├── drag.js
│ │ ├── fsm.js
│ │ ├── history.js
│ │ ├── hotbox.js
│ │ ├── input.js
│ │ ├── jumping.js
│ │ ├── minder.js
│ │ ├── node.js
│ │ ├── priority.js
│ │ ├── progress.js
│ │ └── receiver.js
│ └── tool
│ │ ├── debug.js
│ │ ├── format.js
│ │ ├── innertext.js
│ │ ├── jsondiff.js
│ │ ├── key.js
│ │ └── keymap.js
├── ui
│ ├── dialog
│ │ ├── hyperlink
│ │ │ ├── hyperlink.ctrl.js
│ │ │ └── hyperlink.tpl.html
│ │ ├── imExportNode
│ │ │ ├── imExportNode.ctrl.js
│ │ │ └── imExportNode.tpl.html
│ │ └── image
│ │ │ ├── image.ctrl.js
│ │ │ └── image.tpl.html
│ ├── directive
│ │ ├── appendNode
│ │ │ ├── appendNode.directive.js
│ │ │ └── appendNode.html
│ │ ├── arrange
│ │ │ ├── arrange.directive.js
│ │ │ └── arrange.html
│ │ ├── colorPanel
│ │ │ ├── colorPanel.directive.js
│ │ │ └── colorPanel.html
│ │ ├── expandLevel
│ │ │ ├── expandLevel.directive.js
│ │ │ └── expandLevel.html
│ │ ├── export
│ │ │ ├── export.directive.js
│ │ │ └── export.html
│ │ ├── fontOperator
│ │ │ ├── fontOperator.directive.js
│ │ │ └── fontOperator.html
│ │ ├── hyperLink
│ │ │ ├── hyperLink.directive.js
│ │ │ └── hyperLink.html
│ │ ├── imageBtn
│ │ │ ├── imageBtn.directive.js
│ │ │ └── imageBtn.html
│ │ ├── kityminderEditor
│ │ │ ├── kityminderEditor.directive.js
│ │ │ └── kityminderEditor.html
│ │ ├── kityminderViewer
│ │ │ ├── kityminderViewer.directive.js
│ │ │ └── kityminderViewer.html
│ │ ├── layout
│ │ │ ├── layout.directive.js
│ │ │ └── layout.html
│ │ ├── navigator
│ │ │ ├── navigator.directive.js
│ │ │ └── navigator.html
│ │ ├── noteBtn
│ │ │ ├── noteBtn.directive.js
│ │ │ └── noteBtn.html
│ │ ├── noteEditor
│ │ │ ├── noteEditor.directive.js
│ │ │ └── noteEditor.html
│ │ ├── notePreviewer
│ │ │ ├── notePreviewer.directive.js
│ │ │ └── notePreviewer.html
│ │ ├── operation
│ │ │ ├── operation.directive.js
│ │ │ └── operation.html
│ │ ├── priorityEditor
│ │ │ ├── priorityEditor.directive.js
│ │ │ └── priorityEditor.html
│ │ ├── progressEditor
│ │ │ ├── progressEditor.directive.js
│ │ │ └── progressEditor.html
│ │ ├── resourceEditor
│ │ │ ├── resourceEditor.directive.js
│ │ │ └── resourceEditor.html
│ │ ├── searchBox
│ │ │ ├── searchBox.directive.js
│ │ │ └── searchBox.html
│ │ ├── searchBtn
│ │ │ ├── searchBtn.directive.js
│ │ │ └── searchBtn.html
│ │ ├── selectAll
│ │ │ ├── selectAll.directive.js
│ │ │ └── selectAll.html
│ │ ├── styleOperator
│ │ │ ├── styleOperator.directive.js
│ │ │ └── styleOperator.html
│ │ ├── templateList
│ │ │ ├── templateList.directive.js
│ │ │ └── templateList.html
│ │ ├── themeList
│ │ │ ├── themeList.directive.js
│ │ │ └── themeList.html
│ │ ├── topTab
│ │ │ ├── topTab.directive.js
│ │ │ └── topTab.html
│ │ └── undoRedo
│ │ │ ├── undoRedo.directive.js
│ │ │ └── undoRedo.html
│ ├── filter
│ │ ├── command.filters.js
│ │ └── lang.filter.js
│ ├── images
│ │ ├── iconpriority.png
│ │ ├── iconprogress.png
│ │ ├── icons.png
│ │ └── template.png
│ ├── kityminder.app.js
│ └── service
│ │ ├── commandBinder.service.js
│ │ ├── config.service.js
│ │ ├── lang.en.service.js
│ │ ├── lang.zh-cn.service.js
│ │ ├── memory.service.js
│ │ ├── minder.service.js
│ │ ├── resource.service.js
│ │ ├── revokeDialog.service.js
│ │ ├── server.service.js
│ │ └── valueTransfer.service.js
└── yarn.lock
└── yarn.lock
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Set default behavior to automatically normalize line endings.
2 | * text=auto
3 |
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | out
2 | node_modules
3 | .vscode-test/
4 | *.vsix
5 | webui/node_modules
6 | webui/bower_components
7 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | webui/jscsrc
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | trailingComma: 'es5',
3 | tabWidth: 2,
4 | semi: true,
5 | singleQuote: true,
6 | };
7 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | // See http://go.microsoft.com/fwlink/?LinkId=827846
3 | // for the documentation about the extensions.json format
4 | "recommendations": [
5 | "eg2.tslint"
6 | ]
7 | }
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | // A launch configuration that compiles the extension and then opens it inside a new window
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | {
6 | "version": "0.2.0",
7 | "configurations": [
8 | {
9 | "name": "Extension",
10 | "type": "extensionHost",
11 | "request": "launch",
12 | "runtimeExecutable": "${execPath}",
13 | "args": ["--extensionDevelopmentPath=${workspaceFolder}"],
14 | "outFiles": ["${workspaceFolder}/out/**/*.js"],
15 | "preLaunchTask": "npm: watch"
16 | },
17 | {
18 | "name": "Extension Tests",
19 | "type": "extensionHost",
20 | "request": "launch",
21 | "runtimeExecutable": "${execPath}",
22 | "args": [
23 | "--extensionDevelopmentPath=${workspaceFolder}",
24 | "--extensionTestsPath=${workspaceFolder}/out/test"
25 | ],
26 | "outFiles": ["${workspaceFolder}/out/test/**/*.js"],
27 | "preLaunchTask": "npm: watch"
28 | }
29 | ]
30 | }
31 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | // Place your settings in this file to overwrite default and user settings.
2 | {
3 | "files.exclude": {
4 | "out": false // set this to true to hide the "out" folder with the compiled JS files
5 | },
6 | "search.exclude": {
7 | "out": true // set this to false to include "out" folder in search results
8 | },
9 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts
10 | "typescript.tsc.autoDetect": "off",
11 | "editor.formatOnSave": true
12 | }
13 |
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | // See https://go.microsoft.com/fwlink/?LinkId=733558
2 | // for the documentation about the tasks.json format
3 | {
4 | "version": "2.0.0",
5 | "tasks": [
6 | {
7 | "type": "npm",
8 | "script": "watch",
9 | "problemMatcher": "$tsc-watch",
10 | "isBackground": true,
11 | "presentation": {
12 | "reveal": "never"
13 | },
14 | "group": {
15 | "kind": "build",
16 | "isDefault": true
17 | }
18 | }
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/.vscodeignore:
--------------------------------------------------------------------------------
1 | .vscode/**
2 | .vscode-test/**
3 | out/test/**
4 | out/**/*.map
5 | src/**
6 | webui/node_modules/**
7 | webui/bower_components/**
8 | webui/src/**
9 | webui/less/**
10 | webui/ui/**
11 | webui/server/**
12 | webui/bower.json
13 | webui/favicon.ico
14 | webui/Gruntfile.js
15 | webui/index.html
16 | webui/LICENSE
17 | webui/main.js
18 | webui/package.json
19 | webui/README.md
20 | webui/rollup.config.js
21 | webui/yarn.lock
22 | webui/.babelrc
23 | webui/.bowerrc
24 | webui/.jscsrc
25 | !webui/node_modules/kityminder-core/dist/kityminder.core.css
26 | !webui/node_modules/kityminder-core/dist/kityminder.core.min.js
27 | !webui/bower_components/bootstrap/dist/css/bootstrap.css
28 | !webui/bower_components/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2
29 | !webui/bower_components/bootstrap/dist/fonts/glyphicons-halflings-regular.woff
30 | !webui/bower_components/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf
31 | !webui/bower_components/codemirror/lib/codemirror.css
32 | !webui/bower_components/hotbox/hotbox.css
33 | !webui/bower_components/color-picker/dist/color-picker.min.css
34 | !webui/bower_components/jquery/dist/jquery.js
35 | !webui/bower_components/bootstrap/dist/js/bootstrap.js
36 | !webui/bower_components/angular/angular.js
37 | !webui/bower_components/angular-bootstrap/ui-bootstrap-tpls.js
38 | !webui/bower_components/codemirror/lib/codemirror.js
39 | !webui/bower_components/codemirror/mode/xml/xml.js
40 | !webui/bower_components/codemirror/mode/javascript/javascript.js
41 | !webui/bower_components/codemirror/mode/css/css.js
42 | !webui/bower_components/codemirror/mode/htmlmixed/htmlmixed.js
43 | !webui/bower_components/codemirror/mode/markdown/markdown.js
44 | !webui/bower_components/codemirror/addon/mode/overlay.js
45 | !webui/bower_components/codemirror/mode/gfm/gfm.js
46 | !webui/bower_components/angular-ui-codemirror/ui-codemirror.js
47 | !webui/bower_components/marked/lib/marked.js
48 | !webui/bower_components/kity/dist/kity.min.js
49 | !webui/bower_components/hotbox/hotbox.js
50 | !webui/bower_components/json-diff/json-diff.js
51 | !webui/bower_components/color-picker/dist/color-picker.min.js
52 | .gitignore
53 | tsconfig.json
54 | vsc-extension-quickstart.md
55 | tslint.json
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | ## 0.0.2 - (2019.4.23)
4 |
5 | - first release
6 | - transpile .xmind to .km
7 | - export mindmap files to pictures(eg: png)
8 |
9 | ## 0.0.3 - (2019.4.28)
10 |
11 | - add the function of automatically opening mindmaps
12 |
13 | ## 0.0.4 - (2019.5.9)
14 |
15 | - fix the error of opening the mindmap on the markdown file and .gitignore
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
vscode-mindmap
3 |
4 |
5 | 
6 |
7 | ## Features
8 |
9 | - File Edit(eg: .km), Save, Export
10 | - Transpile .xmind to .km
11 | - Support export to image(.png)
12 |
13 | ## Installation
14 |
15 | Install through VS Code extensions. Search for "vscode-mindmap"
16 |
17 | Visual Studio Code Market Place: vscode-mindmap
18 |
19 | ## Usage
20 |
21 | open any file with extension of km or xmind after install the plugin
22 |
23 | ## Keyboard Shortcuts
24 |
25 | | Key | Command |
26 | | -------------------------------- | ------------------------------ |
27 | | cmd + m(mac) / ctrl + m(windows) | open a webview of textDocument |
28 | | cmd + s(mac) / ctrl + s(windows) | save mindmap file |
29 |
30 | ## FAQ
31 | - **File parsed incorrectly, the current webview only show initial mindmap**
32 |
33 | close the file and webview, try to reopen this file
34 |
35 | - **Extension invalid when the plugin installed**
36 |
37 | check your vscode version, please ensure the version is 1.29.0 or above
38 |
39 | ## Feedback
40 |
41 | 
42 |
43 | ##
44 |
45 | [大搜车无线开发中心](https://blog.souche.com/tag/frontend/) Present
46 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vscode-mindmap",
3 | "displayName": "vscode-mindmap",
4 | "icon": "resources/icon.png",
5 | "description": "mindmap for vscode",
6 | "version": "0.0.5",
7 | "publisher": "Souche",
8 | "repository": {
9 | "type": "git",
10 | "url": "https://github.com/souche/vscode-mindmap.git"
11 | },
12 | "engines": {
13 | "vscode": "^1.29.0"
14 | },
15 | "categories": [
16 | "Other"
17 | ],
18 | "activationEvents": [
19 | "onLanguage:xmind",
20 | "onLanguage:km",
21 | "onCommand:extension.mindmap"
22 | ],
23 | "main": "./out/extension",
24 | "contributes": {
25 | "languages": [
26 | {
27 | "id": "km",
28 | "extensions": [
29 | ".km"
30 | ],
31 | "aliases": [
32 | "KM",
33 | "km"
34 | ]
35 | },
36 | {
37 | "id": "xmind",
38 | "extensions": [
39 | ".xmind"
40 | ],
41 | "aliases": [
42 | "XMIND",
43 | "xmind"
44 | ]
45 | }
46 | ],
47 | "commands": [
48 | {
49 | "command": "extension.mindmap",
50 | "title": "mindmap"
51 | }
52 | ],
53 | "keybindings": [
54 | {
55 | "command": "extension.mindmap",
56 | "key": "ctrl+m",
57 | "mac": "cmd+m",
58 | "when": "editorTextFocus"
59 | }
60 | ]
61 | },
62 | "scripts": {
63 | "vscode:prepublish": "npm run compile",
64 | "compile": "tsc -p ./",
65 | "watch": "tsc -watch -p ./",
66 | "postinstall": "node ./node_modules/vscode/bin/install",
67 | "test": "npm run compile && node ./node_modules/vscode/bin/test"
68 | },
69 | "dependencies": {
70 | "xmind": "^0.5.0"
71 | },
72 | "devDependencies": {
73 | "typescript": "^2.6.1",
74 | "vscode": "^1.1.21",
75 | "tslint": "^5.8.0",
76 | "@types/node": "^8.10.25",
77 | "@types/mocha": "^2.2.42"
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/resources/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/souche/vscode-mindmap/94b0b5a572cd7bf9971a52a82e0aa9ffb1354725/resources/icon.png
--------------------------------------------------------------------------------
/src/constant/index.ts:
--------------------------------------------------------------------------------
1 | // resource placeholder for mindmap.html
2 | export const resourceSchema: string = 'vscode-resource';
3 |
--------------------------------------------------------------------------------
/src/extension.ts:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | import * as vscode from 'vscode';
3 | import * as path from 'path';
4 | import * as fs from 'fs';
5 | import { resourceSchema } from './constant';
6 | import { Xmind, Img } from './services';
7 | const matchableFileTypes: string[] = ['xmind', 'km'];
8 |
9 | // type ProcessService = Xmind | Markdown;
10 |
11 | export function activate(context: vscode.ExtensionContext) {
12 | const openedPanelMap = new Map();
13 | let isFirstActivate: boolean = true;
14 | let timer: any = null;
15 | let disposable = vscode.commands.registerTextEditorCommand(
16 | 'extension.mindmap',
17 | () => {
18 | const editor: vscode.TextEditor | undefined =
19 | vscode.window.activeTextEditor;
20 | const onDiskPath = vscode.Uri.file(
21 | path.join(context.extensionPath, 'webui', 'mindmap.html')
22 | );
23 | const resourcePath = vscode.Uri.file(
24 | path.join(context.extensionPath, 'webui')
25 | );
26 | const resourceRealPath = resourcePath.with({ scheme: resourceSchema });
27 | const fileContent =
28 | process.platform === 'win32'
29 | ? fs.readFileSync(onDiskPath.path.slice(1)).toString()
30 | : fs.readFileSync(onDiskPath.path).toString();
31 | const html = fileContent.replace(
32 | /\$\{vscode\}/g,
33 | resourceRealPath.toString()
34 | );
35 | const fileName = (editor).document.fileName;
36 | const basename = path.basename(fileName);
37 | const extName = path.extname(fileName);
38 | const xmindService = new Xmind(fileName);
39 | const imgService = new Img();
40 | const importData = getImportData(fileName, extName, xmindService) || '{}';
41 |
42 | if (!matchableFileTypes.includes(extName.slice(1))) {
43 | return;
44 | }
45 |
46 | const panel = createWebviewPanel(basename);
47 | panel.webview.html = html;
48 | panel.webview.onDidReceiveMessage(
49 | message => {
50 | let destFileName = '';
51 | switch (message.command) {
52 | case 'loaded':
53 | panel.webview.postMessage({
54 | command: 'import',
55 | importData,
56 | extName,
57 | });
58 | return;
59 |
60 | case 'save':
61 | try {
62 | const retData = JSON.parse(message.exportData);
63 | destFileName =
64 | extName === '.xmind'
65 | ? fileName.replace(/(\.xmind)/, '.km')
66 | : fileName;
67 |
68 | writeFileToDisk(destFileName, JSON.stringify(retData, null, 4));
69 | } catch (ex) {
70 | console.error(ex);
71 | }
72 | return;
73 |
74 | case 'exportToImage':
75 | const buffer = imgService.base64ToPng(message.exportData);
76 | destFileName = fileName.replace(/(\.xmind|\.kme|\.km)/, '.png');
77 | writeFileToDisk(destFileName, buffer);
78 | return;
79 | }
80 | },
81 | undefined,
82 | context.subscriptions
83 | );
84 |
85 | panel.onDidDispose(
86 | () => {
87 | // emit event to webview
88 | },
89 | null,
90 | context.subscriptions
91 | );
92 | }
93 | );
94 |
95 | const executeFirstCommand = (originFileName: string) => {
96 | if (isFirstActivate) {
97 | isFirstActivate = false;
98 | openedPanelMap.set(originFileName, true);
99 | timer = setTimeout(() => {
100 | vscode.commands.executeCommand('extension.mindmap');
101 | }, 300);
102 | }
103 | };
104 |
105 | context.subscriptions.push(disposable);
106 | executeFirstCommand(
107 | (vscode.window.activeTextEditor as vscode.TextEditor).document.fileName
108 | );
109 | vscode.workspace.onDidOpenTextDocument(e => {
110 | const originFileName = e.fileName.replace('.git', '');
111 | if (isFirstActivate) {
112 | executeFirstCommand(originFileName);
113 | return;
114 | }
115 |
116 | if (!openedPanelMap.get(originFileName)) {
117 | openedPanelMap.set(originFileName, true);
118 | timer = setTimeout(() => {
119 | vscode.commands.executeCommand('extension.mindmap', originFileName);
120 | }, 300);
121 | }
122 | });
123 | vscode.workspace.onDidCloseTextDocument(e => {
124 | if (e.fileName.endsWith('.git')) {
125 | return;
126 | }
127 |
128 | if (openedPanelMap.get(e.fileName)) {
129 | clearTimeout(timer);
130 | openedPanelMap.set(e.fileName, false);
131 | }
132 | });
133 | }
134 |
135 | export function deactivate() {}
136 |
137 | /**
138 | * create webview
139 | * @param fileName
140 | */
141 | function createWebviewPanel(fileName: string) {
142 | return vscode.window.createWebviewPanel(
143 | 'mindMap',
144 | `${fileName}-mindmap`,
145 | vscode.ViewColumn.One,
146 | {
147 | enableScripts: true,
148 | retainContextWhenHidden: true,
149 | }
150 | );
151 | }
152 |
153 | /**
154 | * processing source data
155 | * @param fileName
156 | * @param extName
157 | */
158 | function getImportData(fileName: string, extName: string, xmind: Xmind) {
159 | if (extName === '.xmind') {
160 | return JSON.stringify(xmind.process());
161 | }
162 |
163 | return fs.readFileSync(fileName).toString();
164 | }
165 |
166 | function writeFileToDisk(fileName: string, data: any) {
167 | fs.writeFile(fileName, data, (err: any) => {
168 | if (err) {
169 | vscode.window.showErrorMessage(`write ${fileName} failed`);
170 | console.log(err);
171 | throw err;
172 | }
173 | vscode.window.showInformationMessage(`write ${fileName} successed`);
174 | });
175 | }
176 |
--------------------------------------------------------------------------------
/src/services/image.ts:
--------------------------------------------------------------------------------
1 | export class Img {
2 | base64ToPng(base64: string) {
3 | const formattedBase64Str = base64.replace(/^data:image\/\w+;base64,/, '');
4 |
5 | return Buffer.from(formattedBase64Str, 'base64');
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/services/index.ts:
--------------------------------------------------------------------------------
1 | export * from './xmind';
2 | export * from './markdown';
3 | export * from './image';
4 |
--------------------------------------------------------------------------------
/src/services/markdown.ts:
--------------------------------------------------------------------------------
1 | export class Markdown {}
2 |
--------------------------------------------------------------------------------
/src/services/xmind.ts:
--------------------------------------------------------------------------------
1 | import { KMRootNode, KMSubNode } from '../types';
2 | const xmind = require('xmind');
3 |
4 | export class Xmind {
5 | private filename: string;
6 | constructor(filename: string) {
7 | this.filename = filename;
8 | }
9 |
10 | process() {
11 | const workbook = xmind.open(this.filename);
12 | const primarySheet = workbook.getPrimarySheet();
13 | const rootTopic = primarySheet.rootTopic;
14 | const result: KMRootNode = {
15 | root: {},
16 | template: 'right',
17 | theme: 'fresh-blue-compat',
18 | version: '1.4.43',
19 | };
20 |
21 | const walkTopic = (topic: any) => {
22 | const item: KMSubNode = {
23 | data: {
24 | id: topic.id,
25 | text: topic.getTitle(),
26 | created: Date.now(),
27 | },
28 | children: [],
29 | };
30 |
31 | if (topic.children && topic.children.length) {
32 | item.children = topic.children.map((child: any) => walkTopic(child));
33 | }
34 |
35 | return item;
36 | };
37 |
38 | result.root = walkTopic(rootTopic);
39 |
40 | return result;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/test/extension.test.ts:
--------------------------------------------------------------------------------
1 | //
2 | // Note: This example test is leveraging the Mocha test framework.
3 | // Please refer to their documentation on https://mochajs.org/ for help.
4 | //
5 |
6 | // The module 'assert' provides assertion methods from node
7 | import * as assert from 'assert';
8 |
9 | // You can import and use all API from the 'vscode' module
10 | // as well as import your extension to test it
11 | // import * as vscode from 'vscode';
12 | // import * as myExtension from '../extension';
13 |
14 | // Defines a Mocha test suite to group tests of similar kind together
15 | suite("Extension Tests", function () {
16 |
17 | // Defines a Mocha unit test
18 | test("Something 1", function() {
19 | assert.equal(-1, [1, 2, 3].indexOf(5));
20 | assert.equal(-1, [1, 2, 3].indexOf(0));
21 | });
22 | });
--------------------------------------------------------------------------------
/src/test/index.ts:
--------------------------------------------------------------------------------
1 | //
2 | // PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING
3 | //
4 | // This file is providing the test runner to use when running extension tests.
5 | // By default the test runner in use is Mocha based.
6 | //
7 | // You can provide your own test runner if you want to override it by exporting
8 | // a function run(testRoot: string, clb: (error:Error) => void) that the extension
9 | // host can call to run the tests. The test runner is expected to use console.log
10 | // to report the results back to the caller. When the tests are finished, return
11 | // a possible error to the callback or null if none.
12 |
13 | import * as testRunner from 'vscode/lib/testrunner';
14 |
15 | // You can directly control Mocha options by uncommenting the following lines
16 | // See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info
17 | testRunner.configure({
18 | ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.)
19 | useColors: true, // colored output from test results
20 | });
21 |
22 | module.exports = testRunner;
23 |
--------------------------------------------------------------------------------
/src/types/index.ts:
--------------------------------------------------------------------------------
1 | export interface KMRootNode {
2 | root: any;
3 | template: string;
4 | theme: string;
5 | version: string;
6 | }
7 |
8 | export interface KMSubNode {
9 | data: any;
10 | children: any[];
11 | }
12 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "target": "es6",
5 | "outDir": "out",
6 | "lib": ["es6", "es2017"],
7 | "sourceMap": true,
8 | "rootDir": "src",
9 | /* Strict Type-Checking Option */
10 | "strict": true /* enable all strict type-checking options */,
11 | /* Additional Checks */
12 | "noUnusedLocals": true /* Report errors on unused locals. */
13 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
14 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
15 | // "noUnusedParameters": true, /* Report errors on unused parameters. */
16 | },
17 | "exclude": ["node_modules", ".vscode-test"]
18 | }
19 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | "no-string-throw": true,
4 | "no-unused-expression": true,
5 | "no-duplicate-variable": true,
6 | "curly": true,
7 | "class-name": true,
8 | "semicolon": [true, "always"],
9 | "triple-equals": true
10 | },
11 | "defaultSeverity": "warning"
12 | }
13 |
--------------------------------------------------------------------------------
/webui/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [["@babel/preset-env"]]
3 | }
4 |
--------------------------------------------------------------------------------
/webui/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "bower_components",
3 | "allow_root": true,
4 | "registry": "https://registry.bower.io"
5 | }
6 |
--------------------------------------------------------------------------------
/webui/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | .DS_Store
3 | bower_components/
4 | node_modules/
5 | dist/
6 | ui/templates.js
7 | .tmp/
8 | upload/
--------------------------------------------------------------------------------
/webui/.jscsrc:
--------------------------------------------------------------------------------
1 | {
2 | "validateIndentation": 4,
3 |
4 | "requireSpaceBeforeBlockStatements": true,
5 |
6 | "requireSpaceAfterKeywords": [
7 | "if",
8 | "else",
9 | "for",
10 | "while",
11 | "do",
12 | "try",
13 | "catch",
14 | "finally"
15 | ],
16 |
17 | "requireLeftStickedOperators": [",", ";"],
18 |
19 | "requireSpaceBeforeBinaryOperators": [
20 | "+",
21 | "-",
22 | "*",
23 | "/",
24 | "=",
25 | "==",
26 | "===",
27 | "!=",
28 | "!==",
29 | "|",
30 | "||",
31 | "&",
32 | "&&"
33 | ],
34 | "requireSpaceAfterBinaryOperators": [
35 | "+",
36 | "-",
37 | "*",
38 | "/",
39 | "=",
40 | "==",
41 | "===",
42 | "!=",
43 | "!==",
44 | "|",
45 | "||",
46 | "&",
47 | "&&",
48 | ":"
49 | ],
50 |
51 | "disallowSpaceAfterPrefixUnaryOperators": ["++", "--", "+", "-", "~", "!"],
52 |
53 | "disallowSpacesInFunctionExpression": {
54 | "beforeOpeningRoundBrace": true
55 | },
56 |
57 | "disallowSpacesInsideParentheses": true,
58 |
59 | "disallowTrailingWhitespace": true,
60 |
61 | "maximumLineLength": 120,
62 |
63 | "requireOperatorBeforeLineBreak": [
64 | "?",
65 | "+",
66 | "-",
67 | "/",
68 | "*",
69 | "=",
70 | "==",
71 | "===",
72 | "!=",
73 | "!==",
74 | ">",
75 | ">=",
76 | "<",
77 | "<=",
78 | ",",
79 | ";",
80 | "&&",
81 | "&",
82 | "||",
83 | "|"
84 | ],
85 |
86 | "validateQuoteMarks": "'",
87 |
88 | "disallowMultipleLineStrings": true
89 | }
90 |
--------------------------------------------------------------------------------
/webui/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "undef": true,
3 | "unused": false,
4 | "strict": false,
5 | "curly": false,
6 | "newcap": true,
7 | "trailing": true,
8 | "white": false,
9 | "quotmark": false,
10 | "browser": true,
11 | "boss": true,
12 | "indent": 4,
13 | "predef": ["define"]
14 | }
15 |
--------------------------------------------------------------------------------
/webui/README.md:
--------------------------------------------------------------------------------
1 | KityMinder Editor
2 | ==========
3 |
4 | ## 简介
5 |
6 | KityMinder Editor 是一款强大、简洁、体验优秀的脑图编辑工具,适合用于编辑树/图/网等结构的数据。
7 |
8 | 编辑器由百度 [FEX](https://github.com/fex-team) 基于 [kityminder-core](https://github.com/fex-team/kityminder-core) 搭建,并且在[百度脑图](http://naotu.baidu.com)中使用。
9 |
10 | 他们的区别与联系如下:
11 |
12 | 
13 |
14 | - [kityminder-core](https://github.com/fex-team/kityminder-core) 是 kityminder 的核心部分,基于百度 [FEX](https://github.com/fex-team) 开发的矢量图形库 [kity](https://github.com/fex-team/kity)。包含了脑图数据的可视化展现,简单编辑功能等所有底层支持。
15 | - [kityminder-editor](https://github.com/fex-team/kityminder-editor) 基于 kityminder-core 搭建,依赖于 AngularJS,包含 UI 和热盒 [hotbox](https://github.com/fex-team/hotbox) 等方便用户输入的功能,简单来说,就是一款编辑器。
16 | - [百度脑图](http://naotu.baidu.com) 基于 kityminder-editor,加入了第三方格式导入导出 (FreeMind, XMind, MindManager) 、文件储存、用户认证、文件分享、历史版本等业务逻辑。
17 |
18 | ## 功能
19 |
20 | - 基本操作:文本编辑,节点折叠、插入、删除、排序、归纳、复制、剪切、粘贴等
21 | - 样式控制:字体、加粗、斜体、颜色、样式拷贝、样式粘贴等
22 | - 图标:优先级、进度等
23 | - 历史:撤销/重做
24 | - 标签:多标签贴入
25 | - 备注:支持 Markdown 格式备注
26 | - 图片:支持本地/网络/搜索图片插入
27 | - 超链接:支持 HTTP/HTTPS/MAIL/FTP 链接插入
28 | - 布局:支持多种布局切换
29 | - 主题:支持多种主题切换
30 | - 数据导入导出:支持多种格式的导入,多种格式(包括图片)的导出
31 | - 缩略图:支持缩略图查看/导航
32 |
33 | ## 开发使用
34 | 根目录下的 `index.html` 为开发环境,`dist` 目录下的 `index.html` 使用打包好的代码,适用于线上环境。
35 |
36 | 1. 安装 [nodejs](http://nodejs.org) 和 [npm](https://docs.npmjs.com/getting-started/installing-node)
37 | 2. 初始化:切到 kityminder-editor 根目录下运行 `npm run init`
38 | 3. 在 kityminder-editor 根目录下运行 `grunt dev` 即可启动项目
39 | 4. 你可以基于根目录的 `index.html` 开发,或者查看 `dist` 目录下用于生产环境的 `index.html`,Enjoy it!
40 |
41 | 另外,kityminder-editor 还提供了 bower 包,方便开发者直接使用。你可以在需要用到 kityminder-editor 的工程目录下
42 | 运行 `bower install kityminder-editor`,接着手动引入 kityminder-editor 所依赖的 css 和 js 文件,具体文件见
43 | `dist` 目录下的 `index.html`,推荐使用 npm 包 [wireDep](https://www.npmjs.com/package/wiredep) 自动进行,
44 | 可参考根目录下 `Gruntfile.js`。
45 |
46 | ## 构建
47 | 运行 `grunt build`,完成后 `dist` 目录里就是可用运行的 kityminder-editor, 双击 `index.html` 即可打开运行示例
48 |
49 | ## 初始化配置
50 | 用户可以根据需要,配置 `kityminder-editor`, 具体使用方法如下:
51 | ```
52 | angular.module('kityminderDemo', ['kityminderEditor'])
53 | .config(function (configProvider) {
54 | configProvider.set('imageUpload', 'path/to/image/upload/handler');
55 | });
56 |
57 | ```
58 |
59 | ## 数据导入导出
60 | 由于 kityminder-editor 是基于 kityminder-core 搭建的,而 kityminder-core 内置了五种常见
61 | 格式的导入或导出,在创建编辑器实例之后,可以使用四个接口进行数据的导入导出。
62 |
63 | * `editor.minder.exportJson()` - 导出脑图数据为 JSON 对象
64 | * `editor.minder.importJson(json)` - 导入 JSON 对象为当前脑图数据
65 | * `editor.minder.exportData(protocol, option)` - 导出脑图数据为指定的数据格式,返回一个 Promise,其值为导出的结果
66 | * `editor.minder.importData(protocol, data, option)` - 导入指定格式的数据为脑图数据,返回一个 Promise,其值为转换之后的脑图 Json 数据
67 |
68 | 目前支持的数据格式包括:
69 |
70 | * `json` - JSON 字符串,支持导入和导出
71 | * `text` - 纯文本格式,支持导入和导出
72 | * `markdown` - Markdown 格式,支持导入和导出
73 | * `svg` - SVG 矢量格式,仅支持导出
74 | * `png` - PNG 位图格式,仅支持导出
75 |
76 | 更多格式的支持,可以加载 [kityminder-protocol](https://github.com/fex-team/kityminder-protocol) 来扩展第三方格式支持。
77 |
78 | 数据格式的具体信息,可参考 [kityminder-core-wiki 的中的说明](https://github.com/fex-team/kityminder-core/wiki)。
79 |
80 | ## 联系我们
81 | 问题和建议反馈:
82 |
83 | [Github issues](https://github.com/fex-team/kityminder-editor/issues)
84 |
85 | 邮件组:kity@baidu.com
86 |
87 | QQ 讨论群:475962105
--------------------------------------------------------------------------------
/webui/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "kityminder-editor",
3 | "version": "1.0.61",
4 | "authors": [
5 | "fex"
6 | ],
7 | "description": "Kity Minder Editor",
8 | "main": [
9 | "dist/kityminder.editor.js",
10 | "dist/kityminder.editor.css"
11 | ],
12 | "keywords": [
13 | "kityminder",
14 | "fex",
15 | "ui",
16 | "javascript",
17 | "html5",
18 | "svg"
19 | ],
20 | "license": "BSD",
21 | "homepage": "https://github.com/fex-team/kityminder-editor",
22 | "ignore": [
23 | "**/.*",
24 | "node_modules",
25 | "bower_components",
26 | "test",
27 | "tests",
28 | "less",
29 | "ui",
30 | "src",
31 | "Gruntfile.js",
32 | "package.json"
33 | ],
34 | "devDependencies": {
35 | "seajs": "~2.3.0"
36 | },
37 | "dependencies": {
38 | "bootstrap": "~3.3.4",
39 | "angular": "~1.3.15",
40 | "angular-bootstrap": "~0.12.1",
41 | "angular-ui-codemirror": "~0.2.3",
42 | "codemirror": "~4.8.0",
43 | "marked": "git://github.com/chjj/marked.git#master",
44 | "hotbox": "~1.0.2",
45 | "color-picker": "~1.0.2",
46 | "kity": "^2.0.5",
47 | "json-diff": "*"
48 | },
49 | "overrides": {
50 | "codemirror": {
51 | "main": [
52 | "lib/codemirror.js",
53 | "lib/codemirror.css",
54 | "mode/xml/xml.js",
55 | "mode/javascript/javascript.js",
56 | "mode/css/css.js",
57 | "mode/htmlmixed/htmlmixed.js",
58 | "mode/markdown/markdown.js",
59 | "addon/mode/overlay.js",
60 | "mode/gfm/gfm.js"
61 | ]
62 | }
63 | },
64 | "resolutions": {
65 | "angular": "~1.3.8"
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/webui/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/souche/vscode-mindmap/94b0b5a572cd7bf9971a52a82e0aa9ffb1354725/webui/favicon.ico
--------------------------------------------------------------------------------
/webui/less/_navigator.less:
--------------------------------------------------------------------------------
1 | .nav-bar {
2 | position: absolute;
3 | width: 35px;
4 | height: 240px;
5 | padding: 5px 0;
6 | left: 10px;
7 | bottom: 10px;
8 | background: #fc8383;
9 | color: #fff;
10 | border-radius: 4px;
11 | z-index: 10;
12 | box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.2);
13 | transition: -webkit-transform .7s 0.1s ease;
14 | transition: transform .7s 0.1s ease;
15 |
16 | .nav-btn {
17 | width: 35px;
18 | height: 24px;
19 | line-height: 24px;
20 | text-align: center;
21 |
22 | .icon {
23 | background: url(images/icons.png);
24 | width: 20px;
25 | height: 20px;
26 | margin: 2px auto;
27 | display: block;
28 | }
29 |
30 | &.active {
31 | background-color: #5A6378;
32 | }
33 | }
34 |
35 | .zoom-in .icon {
36 | background-position: 0 -730px;
37 | }
38 |
39 | .zoom-out .icon {
40 | background-position: 0 -750px;
41 | }
42 |
43 | .hand .icon {
44 | background-position: 0 -770px;
45 | width: 25px;
46 | height: 25px;
47 | margin: 0 auto;
48 | }
49 |
50 | .camera .icon {
51 | background-position: 0 -870px;
52 | width: 25px;
53 | height: 25px;
54 | margin: 0 auto;
55 | }
56 |
57 | .nav-trigger .icon {
58 | background-position: 0 -845px;
59 | width: 25px;
60 | height: 25px;
61 | margin: 0 auto;
62 | }
63 |
64 | .zoom-pan {
65 | width: 2px;
66 | height: 70px;
67 | box-shadow: 0 1px #E50000;
68 | position: relative;
69 | background: white;
70 | margin: 3px auto;
71 | overflow: visible;
72 |
73 | .origin {
74 | position: absolute;
75 | width: 20px;
76 | height: 8px;
77 | left: -9px;
78 | margin-top: -4px;
79 | background: transparent;
80 |
81 | &:after {
82 | content: ' ';
83 | display: block;
84 | width: 6px;
85 | height: 2px;
86 | background: white;
87 | left: 7px;
88 | top: 3px;
89 | position: absolute;
90 | }
91 | }
92 |
93 | .indicator {
94 | position: absolute;
95 | width: 8px;
96 | height: 8px;
97 | left: -3px;
98 | background: white;
99 | border-radius: 100%;
100 | margin-top: -4px;
101 | }
102 |
103 | }
104 | }
105 |
106 | .nav-previewer {
107 | background: #fff;
108 | width: 140px;
109 | height: 120px;
110 | position: absolute;
111 | left: 45px;
112 | bottom: 30px;
113 | box-shadow: 0 0 8px rgba(0, 0, 0, 0.2);
114 | border-radius: 0 2px 2px 0;
115 | padding: 1px;
116 | z-index: 9;
117 | cursor: crosshair;
118 | transition: -webkit-transform .7s 0.1s ease;
119 | transition: transform .7s 0.1s ease;
120 |
121 | &.grab {
122 | cursor: move;
123 | cursor: -webkit-grabbing;
124 | cursor: -moz-grabbing;
125 | cursor: grabbing;
126 | }
127 | }
--------------------------------------------------------------------------------
/webui/less/_tool_group.less:
--------------------------------------------------------------------------------
1 | .tool-group {
2 | padding: 0;
3 |
4 | &[disabled] {
5 | opacity: 0.5;
6 | }
7 |
8 | .tool-group-item {
9 | display: inline-block;
10 | border-radius: 4px;
11 |
12 | .tool-group-icon {
13 | width: 20px;
14 | height: 20px;
15 | padding: 2px;
16 | margin: 1px;
17 | }
18 |
19 | &:hover {background-color: @button-hover;}
20 | &:active {background-color: @button-active;}
21 |
22 | &.active {background-color: @button-active;}
23 | }
24 | }
--------------------------------------------------------------------------------
/webui/less/_vars.less:
--------------------------------------------------------------------------------
1 | @button-hover: hsl(222, 55%, 96%);
2 | @button-active: hsl(222, 55%, 85%);
3 | @button-pressed: hsl(222, 55%, 90%);
4 |
5 | @tool-hover: #eff3fa;
6 | @tool-active: #c4d0ee;
7 | @tool-selected: #87a9da;
--------------------------------------------------------------------------------
/webui/less/editor.less:
--------------------------------------------------------------------------------
1 | .km-editor {
2 | overflow: hidden;
3 | z-index: 2;
4 | }
5 |
6 | .km-editor > .mask {
7 | display: block;
8 | position: absolute;
9 | left: 0;
10 | right: 0;
11 | top: 0;
12 | bottom: 0;
13 | background-color: transparent;
14 | }
15 |
16 | .km-editor > .receiver {
17 | position: absolute;
18 | background: white;
19 | outline: none;
20 | box-shadow: 0 0 20px fadeout(black, 50%);
21 | left: 0;
22 | top: 0;
23 | padding: 3px 5px;
24 | margin-left: -3px;
25 | margin-top: -5px;
26 | max-width: 300px;
27 | width: auto;
28 | overflow: hidden;
29 | font-size: 14px;
30 | line-height: 1.4em;
31 | min-height: 1.4em;
32 | box-sizing: border-box;
33 | overflow: hidden;
34 | word-break: break-all;
35 | word-wrap: break-word;
36 | border: none;
37 | -webkit-user-select: text;
38 | pointer-events: none;
39 | opacity: 0;
40 | z-index: -1000;
41 | &.debug {
42 | opacity: 1;
43 | outline: 1px solid green;
44 | background: none;
45 | z-index: 0;
46 | }
47 |
48 | &.input {
49 | pointer-events: all;
50 | opacity: 1;
51 | z-index: 999;
52 | background: white;
53 | outline: none;
54 | }
55 | }
56 |
57 | div.minder-editor-container {
58 | position: absolute;
59 | top: 0;
60 | bottom: 0;
61 | left: 0;
62 | right: 0;
63 | font-family: Arial, 'Hiragino Sans GB', 'Microsoft YaHei',
64 | 'WenQuanYi Micro Hei', sans-serif;
65 | }
66 |
67 | .minder-editor {
68 | position: absolute;
69 | top: 92px;
70 | left: 0;
71 | right: 0;
72 | bottom: 0;
73 | }
74 |
75 | .minder-viewer {
76 | position: absolute;
77 | top: 0;
78 | left: 0;
79 | right: 0;
80 | bottom: 0;
81 | }
82 |
83 | .control-panel {
84 | position: absolute;
85 | top: 0;
86 | right: 0;
87 | width: 250px;
88 | bottom: 0;
89 | border-left: 1px solid #ccc;
90 | }
91 | .minder-divider {
92 | position: absolute;
93 | top: 0;
94 | right: 250px;
95 | bottom: 0;
96 | width: 2px;
97 | background-color: rgb(251, 251, 251);
98 | cursor: ew-resize;
99 | }
100 |
101 | // @override bootstrap
102 | .panel-body {
103 | padding: 10px;
104 | }
105 |
106 | @import (less) '_vars.less';
107 | @import (less) 'imageDialog.less';
108 | @import (less) 'topTab/topTab.less';
109 | @import (less) 'topTab/idea/undoRedo.less';
110 | @import (less) 'topTab/idea/appendNode.less';
111 | @import (less) 'topTab/idea/arrange.less';
112 | @import (less) 'topTab/idea/operation.less';
113 | @import (less) 'topTab/idea/hyperlink.less';
114 | @import (less) 'topTab/idea/image.less';
115 | @import (less) 'topTab/idea/note.less';
116 | @import (less) 'topTab/idea/noteEditor.less';
117 | @import (less) 'topTab/idea/priority.less';
118 | @import (less) 'topTab/idea/progress.less';
119 | @import (less) 'topTab/idea/resource.less';
120 | @import (less) 'topTab/appearance/templatePanel.less';
121 | @import (less) 'topTab/appearance/themePanel.less';
122 | @import (less) 'topTab/appearance/layout.less';
123 | @import (less) 'topTab/appearance/styleOperator.less';
124 | @import (less) 'topTab/appearance/fontOperator.less';
125 | @import (less) 'topTab/appearance/colorPanel.less';
126 | @import (less) 'topTab/appearance/export.less';
127 | @import (less) 'topTab/view/expand.less';
128 | @import (less) 'topTab/view/select.less';
129 | @import (less) 'topTab/view/search.less';
130 | @import (less) 'topTab/searchBox.less';
131 | @import (less) '_tool_group.less';
132 | @import (less) '_navigator.less';
133 |
--------------------------------------------------------------------------------
/webui/less/imageDialog.less:
--------------------------------------------------------------------------------
1 | .upload-image {
2 | width: 0.1px;
3 | height: 0.1px;
4 | opacity: 0;
5 | overflow: hidden;
6 | position: absolute;
7 | z-index: -1;
8 | }
--------------------------------------------------------------------------------
/webui/less/topTab/appearance/colorPanel.less:
--------------------------------------------------------------------------------
1 | .bg-color-wrap {
2 | display: inline-block;
3 | width: 30px;
4 | height: 22px;
5 | margin: 3px 3px 0 0;
6 | border: 1px #efefef solid;
7 | vertical-align: middle;
8 | font-size: 0;
9 | -webkit-user-select: none;
10 | -moz-user-select: none;
11 | -ms-user-select: none;
12 | user-select: none;
13 |
14 | &[disabled] {
15 | opacity: 0.5;
16 | }
17 |
18 | .quick-bg-color {
19 | display: inline-block;
20 | width: 20px;
21 | height: 16px;
22 | font-size: 14px;
23 | line-height: 16px;
24 | vertical-align: top;
25 | text-align: center;
26 | cursor: default;
27 | color: #000;
28 | background: url(images/icons.png) no-repeat center -1260px;
29 |
30 | &:hover {
31 | background-color: @tool-hover;
32 | }
33 |
34 | &:active {
35 | background-color: @tool-active;
36 | }
37 |
38 | &[disabled] {
39 | opacity: 0.5;
40 | }
41 | }
42 |
43 | .bg-color-preview {
44 | display: inline-block;
45 | width: 12px;
46 | height: 2px;
47 | margin: 0 4px 0;
48 | background-color: #fff;
49 |
50 | &[disabled] {
51 | opacity: 0.5;
52 | }
53 | }
54 | }
55 |
56 | .bg-color {
57 | display: inline-block;
58 | width: 8px;
59 | height: 16px;
60 |
61 | &:hover {
62 | background-color: @tool-hover;
63 | }
64 |
65 | &:active {
66 | background-color: @tool-active;
67 | }
68 |
69 | &[disabled] {
70 | opacity: 0.5;
71 | }
72 |
73 | .caret {
74 | margin-left: -2px;
75 | margin-top: 7px;
76 | }
77 | }
--------------------------------------------------------------------------------
/webui/less/topTab/appearance/export.less:
--------------------------------------------------------------------------------
1 | .export {
2 | .km-btn-export {
3 | font-size: 14px;
4 | padding: 6px 10px;
5 | margin-right: 5px;
6 | border-radius: 4px;
7 | color: #fff;
8 | font-weight: 500;
9 | }
10 | .km-export-save {
11 | background: rgba(24, 144, 255, 1);
12 | border-color: rgba(24, 144, 255, 1);
13 | &:hover {
14 | background: rgba(24, 144, 255, 0.8);
15 | }
16 | }
17 | .km-export-image {
18 | background: rgba(104, 203, 131, 1);
19 | border-color: rgba(104, 203, 131, 1);
20 | &:hover {
21 | background: rgba(104, 203, 131, 0.8);
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/webui/less/topTab/appearance/fontOperator.less:
--------------------------------------------------------------------------------
1 | .font-operator {
2 | width: 220px;
3 | display: inline-block;
4 | vertical-align: middle;
5 | font-size: 12px;
6 | padding: 0 5px;
7 |
8 | .font-size-list {
9 | display: inline-block;
10 | border: 1px solid #eee;
11 | padding: 2px 4px;
12 | }
13 |
14 | .font-family-list {
15 | display: inline-block;
16 | border: 1px solid #eee;
17 | padding: 2px 4px;
18 | }
19 | }
20 |
21 | .current-font-item a {
22 | text-decoration: none;
23 | display: inline-block;
24 | }
25 |
26 | .current-font-family {
27 | width: 75px;
28 | height: 18px;
29 | overflow: hidden;
30 | vertical-align: bottom;
31 | }
32 | .current-font-size {
33 | width: 44px;
34 | height: 18px;
35 | overflow: hidden;
36 | vertical-align: bottom;
37 | }
38 |
39 | .current-font-item[disabled] {
40 | opacity: 0.5;
41 | }
42 |
43 | .font-item {
44 | line-height: 1em;
45 | text-align: left;
46 | }
47 |
48 | .font-item-selected {
49 | background-color: @tool-selected;
50 | }
51 |
52 | .font-bold,
53 | .font-italics {
54 | display: inline-block;
55 | background: url(images/icons.png) no-repeat;
56 | cursor: pointer;
57 | margin: 0 3px;
58 |
59 | &:hover {
60 | background-color: @tool-hover;
61 | }
62 |
63 | &:active {
64 | background-color: @tool-active;
65 | }
66 |
67 | &[disabled] {
68 | opacity: 0.5;
69 | }
70 | }
71 |
72 | .font-bold {
73 | background-position: 0 -240px;
74 | }
75 |
76 | .font-italics {
77 | background-position: 0 -260px;
78 | }
79 |
80 | .font-bold-selected,
81 | .font-italics-selected {
82 | background-color: @tool-selected;
83 | }
84 |
85 | .font-color-wrap {
86 | display: inline-block;
87 | width: 30px;
88 | height: 22px;
89 | margin: 3px 3px 0 0;
90 | border: 1px #efefef solid;
91 | vertical-align: middle;
92 | font-size: 0;
93 | -webkit-user-select: none;
94 | -moz-user-select: none;
95 | -ms-user-select: none;
96 | user-select: none;
97 |
98 | &[disabled] {
99 | opacity: 0.5;
100 | }
101 |
102 | .quick-font-color {
103 | display: inline-block;
104 | width: 20px;
105 | height: 16px;
106 | font-size: 14px;
107 | line-height: 16px;
108 | vertical-align: top;
109 | text-align: center;
110 | cursor: default;
111 | color: #000;
112 |
113 | &:hover {
114 | background-color: @tool-hover;
115 | }
116 |
117 | &:active {
118 | background-color: @tool-active;
119 | }
120 |
121 | &[disabled] {
122 | opacity: 0.5;
123 | }
124 | }
125 |
126 | .font-color-preview {
127 | display: inline-block;
128 | width: 12px;
129 | height: 2px;
130 | margin: 0 4px 0;
131 | background-color: #000;
132 |
133 | &[disabled] {
134 | opacity: 0.5;
135 | }
136 | }
137 | }
138 |
139 | .font-color {
140 | display: inline-block;
141 | width: 8px;
142 | height: 16px;
143 |
144 | &:hover {
145 | background-color: @tool-hover;
146 | }
147 |
148 | &:active {
149 | background-color: @tool-active;
150 | }
151 |
152 | &[disabled] {
153 | opacity: 0.5;
154 | }
155 |
156 | .caret {
157 | margin-left: -2px;
158 | margin-top: 7px;
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/webui/less/topTab/appearance/layout.less:
--------------------------------------------------------------------------------
1 | .readjust-layout {
2 | display: inline-block;
3 | vertical-align: middle;
4 | padding: 0 10px 0 5px;
5 | border-right: 1px dashed #eee;
6 | }
7 |
8 | .btn-icon {
9 | width: 25px;
10 | height: 25px;
11 | margin-left: 12px;
12 | display: block;
13 | }
14 |
15 | .btn-label {
16 | font-size: 12px;
17 | }
18 |
19 | .btn-wrap {
20 | width: 50px;
21 | height: 42px;
22 | cursor: pointer;
23 | display: inline-block;
24 | text-decoration: none;
25 |
26 | &[disabled] span {
27 | opacity: 0.5;
28 | }
29 |
30 | &[disabled] {
31 | cursor: default;
32 | }
33 |
34 | &[disabled]:hover {
35 | background-color: transparent;
36 | }
37 |
38 | &[disabled]:active {
39 | background-color: transparent;
40 | }
41 |
42 | &:link {
43 | text-decoration: none;
44 | }
45 |
46 | &:visited {
47 | text-decoration: none;
48 | }
49 |
50 | &:hover {
51 | background-color: @tool-hover;
52 | text-decoration: none;
53 | }
54 |
55 | &:active {
56 | background-color: @tool-active;
57 | }
58 |
59 | }
60 |
61 | .reset-layout-icon {
62 | background: url(images/icons.png) no-repeat;
63 | background-position: 0 -150px;
64 | }
65 |
66 |
--------------------------------------------------------------------------------
/webui/less/topTab/appearance/styleOperator.less:
--------------------------------------------------------------------------------
1 | .style-operator {
2 | display: inline-block;
3 | vertical-align: middle;
4 | padding: 0 5px;
5 | border-right: 1px dashed #eee;
6 |
7 | .clear-style {
8 | vertical-align: middle;
9 | }
10 |
11 | }
12 |
13 | .clear-style-icon {
14 | background: url(images/icons.png) no-repeat;
15 | background-position: 0 -175px;;
16 | }
17 |
18 | .s-btn-group-vertical {
19 | display: inline-block;
20 | vertical-align: middle;
21 | }
22 |
23 | .s-btn-icon {
24 | width: 20px;
25 | height: 20px;
26 | margin-right: 3px;
27 | display: inline-block;
28 | vertical-align: middle;
29 | }
30 |
31 | .s-btn-label {
32 | font-size: 12px;
33 | vertical-align: middle;
34 | display: inline-block;
35 | }
36 |
37 | .s-btn-wrap {
38 | // margin-bottom: 2px;
39 | padding: 0 5px 0 3px;
40 | display: inline-block;
41 | text-decoration: none;
42 | font-size: 0;
43 |
44 | &[disabled] span {
45 | opacity: 0.5;
46 | }
47 |
48 | &[disabled] {
49 | cursor: default;
50 | }
51 |
52 | &[disabled]:hover {
53 | background-color: transparent;
54 | }
55 |
56 | &[disabled]:active {
57 | background-color: transparent;
58 | }
59 |
60 | &:hover {
61 | background-color: @tool-hover;
62 | text-decoration: none;
63 | }
64 |
65 | &:active {
66 | background-color: @tool-active;
67 | }
68 |
69 | }
70 |
71 | .copy-style-icon {
72 | background: url(images/icons.png) no-repeat;
73 | background-position: 0 -200px;
74 | }
75 |
76 | .paste-style-wrap {
77 | display: block;
78 | }
79 |
80 | .paste-style-icon {
81 | background: url(images/icons.png) no-repeat;
82 | background-position: 0 -220px;
83 | }
--------------------------------------------------------------------------------
/webui/less/topTab/appearance/templatePanel.less:
--------------------------------------------------------------------------------
1 | .temp-panel {
2 | margin: 5px 5px 5px 10px;
3 | border-right: 1px dashed #eee;
4 | display: inline-block;
5 | vertical-align: middle;
6 | }
7 |
8 | .temp-list {
9 | min-width: 124px;
10 | }
11 |
12 | .temp-item-wrap {
13 | width: 50px;
14 | height: 40px;
15 | padding: 0 2px;
16 | margin: 5px;
17 | display: inline-block;
18 | }
19 |
20 | .temp-item {
21 | display: inline-block;
22 | width: 50px;
23 | height: 40px;
24 | background-image: url(images/template.png);
25 | background-repeat: no-repeat;
26 |
27 | &.default {
28 | background-position: 0 0;
29 | }
30 |
31 | &.structure {
32 | background-position: -50px 0;
33 | }
34 |
35 | &.filetree {
36 | background-position: -100px 0;
37 | }
38 |
39 | &.right {
40 | background-position: -150px 0;
41 | }
42 |
43 | &.fish-bone {
44 | background-position: -200px 0;
45 | }
46 |
47 | &.tianpan {
48 | background-position: -250px 0;
49 | }
50 | }
51 |
52 | .current-temp-item {
53 | width: 74px;
54 | padding: 0 0 0 5px;
55 | border: 1px solid #fff;
56 |
57 | &:hover {
58 | background-color: @tool-hover;
59 | }
60 |
61 | &[disabled] {
62 | opacity: 0.5;
63 | }
64 |
65 | .caret {
66 | margin-left: 5px;
67 | }
68 | }
69 | .temp-item-selected {
70 | background-color: @tool-selected;
71 | }
--------------------------------------------------------------------------------
/webui/less/topTab/appearance/themePanel.less:
--------------------------------------------------------------------------------
1 | .theme-panel {
2 | height: 42px;
3 | margin: 5px;
4 | padding: 0 5px 0 0;
5 | border-right: 1px dashed #eee;
6 | display: inline-block;
7 | vertical-align: middle;
8 | }
9 |
10 | .theme-list {
11 | min-width: 225px;
12 | }
13 |
14 | div a.theme-item {
15 | display: inline-block;
16 | width: 100px;
17 | height: 30px;
18 | text-align: center;
19 | line-height: 30px;
20 | padding: 0 5px;
21 | font-size: 12px;
22 | cursor: pointer;
23 | text-decoration: none;
24 | color: #000;
25 | }
26 |
27 | .theme-item-selected {
28 | width: 100px;
29 | padding: 6px 7px;
30 | border: 1px solid #fff;
31 |
32 | &:hover {
33 | background-color: @tool-hover;
34 | }
35 |
36 | .caret {
37 | margin-left: 5px;
38 | }
39 |
40 | &[disabled] {
41 | opacity: 0.5;
42 | }
43 | }
44 |
45 | .theme-item-wrap {
46 | display: inline-block;
47 | width: 110px;
48 | height: 40px;
49 | padding: 5px;
50 | }
51 | .theme-item-wrap:hover {
52 | background-color: #eff3fa;
53 | }
54 |
--------------------------------------------------------------------------------
/webui/less/topTab/idea/appendNode.less:
--------------------------------------------------------------------------------
1 | .append-group {
2 | width: 212px;
3 | }
4 |
5 | .append-child-node {
6 | .km-btn-icon {
7 | background-position: 0 0;
8 | }
9 | }
10 |
11 | .append-sibling-node {
12 | .km-btn-icon {
13 | background-position: 0 -20px;
14 | }
15 | }
16 |
17 | .append-parent-node {
18 | .km-btn-icon {
19 | background-position: 0 -40px;
20 | }
21 | }
--------------------------------------------------------------------------------
/webui/less/topTab/idea/arrange.less:
--------------------------------------------------------------------------------
1 | .arrange-group {
2 | width: 80px;
3 | }
4 |
5 | .arrange-up {
6 | .km-btn-icon {
7 | background-position: 0 -280px;
8 | }
9 | }
10 |
11 | .arrange-down {
12 | .km-btn-icon {
13 | background-position: 0 -300px;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/webui/less/topTab/idea/hyperlink.less:
--------------------------------------------------------------------------------
1 | .btn-group-vertical {
2 | vertical-align: middle;
3 | margin: 5px;
4 |
5 | .hyperlink, .hyperlink-caption {
6 | width: 40px;
7 | margin: 0;
8 | padding: 0;
9 | border: none!important;
10 | border-radius: 0!important;
11 |
12 | &:hover {
13 | background-color: @tool-hover;
14 | }
15 |
16 | &:active {
17 | background-color: @tool-active;
18 | }
19 |
20 | &.active {
21 | box-shadow: none;
22 | background-color: @tool-hover;
23 | }
24 | }
25 |
26 | .hyperlink {
27 | height: 25px;
28 | background: url(images/icons.png) no-repeat center -100px;
29 | }
30 |
31 | .hyperlink-caption {
32 | height: 20px;
33 |
34 | .caption {
35 | font-size: 12px;
36 | }
37 | }
38 | }
39 |
40 | //override bootstrap
41 | .open > .dropdown-toggle.btn-default {
42 | background-color: @tool-hover;
43 | }
44 |
--------------------------------------------------------------------------------
/webui/less/topTab/idea/image.less:
--------------------------------------------------------------------------------
1 | .btn-group-vertical {
2 |
3 | .image-btn, .image-btn-caption {
4 | width: 40px;
5 | margin: 0;
6 | padding: 0;
7 | border: none!important;
8 | border-radius: 0!important;
9 |
10 | &:hover {
11 | background-color: @tool-hover;
12 | }
13 |
14 | &:active {
15 | background-color: @tool-active;
16 | }
17 |
18 | &.active {
19 | box-shadow: none;
20 | background-color: @tool-hover;
21 | }
22 | }
23 |
24 | .image-btn {
25 | height: 25px;
26 | background: url(images/icons.png) no-repeat center -125px;
27 | }
28 |
29 | .image-btn-caption {
30 | height: 20px;
31 |
32 | .caption {
33 | font-size: 12px;
34 | }
35 | }
36 | }
37 |
38 | .image-preview {
39 | display: block;
40 | max-width: 50%;
41 | }
42 |
43 | .modal-body {
44 | .tab-pane {
45 | font-size: inherit;
46 | padding-top: 15px;
47 | }
48 | }
49 |
50 | .search-result {
51 | margin-top: 15px;
52 | height: 370px;
53 | overflow: hidden;
54 |
55 | ul {
56 | margin: 0;
57 | padding: 0;
58 | list-style: none;
59 | clear: both;
60 | height: 100%;
61 | overflow-x: hidden;
62 | overflow-y: auto;
63 |
64 | li {
65 | list-style: none;
66 | float: left;
67 | display: block;
68 | width: 130px;
69 | height: 130px;
70 | line-height: 130px;
71 | margin: 6px;
72 | padding: 0;
73 | font-size: 12px;
74 | position: relative;
75 | vertical-align: top;
76 | text-align: center;
77 | overflow: hidden;
78 | cursor: pointer;
79 | border: 2px solid #fcfcfc;
80 |
81 | &.selected {
82 | border: 2px solid #fc8383;
83 | }
84 |
85 |
86 | img {
87 | max-width: 126px;
88 | max-height: 130px;
89 | vertical-align: middle;
90 | }
91 |
92 | span {
93 | display: block;
94 | position: absolute;
95 | bottom: 0;
96 | height: 20px;
97 | background: rgba(0, 0, 0, 0.5);
98 | left: 0;
99 | right: 0;
100 | color: white;
101 | line-height: 20px;
102 | overflow: hidden;
103 | text-overflow: ellipsis;
104 | word-break: break-all;
105 | white-space: nowrap;
106 | opacity: 0;
107 | -webkit-transform: translate(0, 20px);
108 | -ms-transform: translate(0, 20px);
109 | transform: translate(0, 20px);
110 | -webkit-transition: all .2s ease;
111 | transition: all .2s ease;
112 | }
113 | }
114 |
115 | li:hover span {
116 | opacity: 1;
117 | -webkit-transform: translate(0, 0);
118 | -ms-transform: translate(0, 0);
119 | transform: translate(0, 0);
120 | }
121 | }
122 | }
123 |
124 | // 覆盖 bootstrap 样式
125 | @media (min-width: 768px){
126 | .form-inline .form-control {
127 | width: 422px;
128 | }
129 | }
--------------------------------------------------------------------------------
/webui/less/topTab/idea/note.less:
--------------------------------------------------------------------------------
1 | .btn-group-vertical {
2 | vertical-align: top;
3 | margin: 5px;
4 |
5 | &.note-btn-group {
6 | border-right: 1px dashed #eee;
7 | padding-right: 5px;
8 | }
9 |
10 | .note-btn, .note-btn-caption {
11 | width: 40px;
12 | margin: 0;
13 | padding: 0;
14 | border: none!important;
15 | border-radius: 0!important;
16 |
17 | &:hover {
18 | background-color: @tool-hover;
19 | }
20 |
21 | &:active {
22 | background-color: @tool-active;
23 | }
24 |
25 | &.active {
26 | box-shadow: none;
27 | background-color: @tool-hover;
28 | }
29 | }
30 |
31 | .note-btn {
32 | height: 25px;
33 | background: url(images/icons.png) no-repeat center -1150px;
34 | }
35 |
36 | .note-btn-caption {
37 | height: 20px;
38 |
39 | .caption {
40 | font-size: 12px;
41 | }
42 | }
43 | }
44 |
45 | //override bootstrap
46 | .open > .dropdown-toggle.btn-default {
47 | background-color: @tool-hover;
48 | }
49 |
--------------------------------------------------------------------------------
/webui/less/topTab/idea/noteEditor.less:
--------------------------------------------------------------------------------
1 | .gfm-render {
2 |
3 | font-size: 12px;
4 | -webkit-user-select: text;
5 | color: #333;
6 | line-height: 1.8em;
7 |
8 | blockquote, ul, table, p, pre, hr {
9 | margin: 1em 0;
10 | cursor: text;
11 | &:first-child:last-child {
12 | margin: 0;
13 | }
14 | }
15 |
16 | img {
17 | max-width: 100%;
18 | }
19 |
20 | a {
21 | color: blue;
22 | &:hover {
23 | color: red;
24 | }
25 | }
26 |
27 | blockquote {
28 | display: block;
29 | border-left: 4px solid #E4AD91;
30 | color: darken(#E4AD91, 10%);
31 | padding-left: 10px;
32 | font-style: italic;
33 | margin-left: 2em;
34 | }
35 |
36 | ul, ol {
37 | padding-left: 3em;
38 | }
39 |
40 | table {
41 | width: 100%;
42 | border-collapse: collapse;
43 | th, td {
44 | border: 1px solid #666;
45 | padding: 2px 4px;
46 | }
47 | th {
48 | background: rgba(45, 141, 234, 0.2);
49 | }
50 | tr:nth-child(even) td {
51 | background: rgba(45, 141, 234, 0.03);
52 | }
53 | margin: 1em 0;
54 | }
55 |
56 | em {
57 | color: red;
58 | }
59 |
60 | del {
61 | color: #999;
62 | }
63 |
64 | pre {
65 | background: rgba(45, 141, 234, 0.1);
66 | padding: 5px;
67 | border-radius: 5px;
68 | word-break: break-all;
69 | word-wrap: break-word;
70 | }
71 |
72 | code {
73 | background: rgba(45, 141, 234, 0.1);
74 | /* display: inline-block; */
75 | padding: 0 5px;
76 | border-radius: 3px;
77 | }
78 |
79 | pre code {
80 | background: none;
81 | }
82 |
83 | hr {
84 | border: none;
85 | border-top: 1px solid #CCC;
86 | }
87 |
88 | .highlight {
89 | background: yellow;
90 | color: red;
91 | }
92 | }
93 |
94 | .km-note {
95 | width: 300px;
96 | border-left: 1px solid #babfcd;
97 | padding: 5px 10px;
98 | background: white;
99 | position: absolute;
100 | top: 92px;
101 | right: 0;
102 | bottom: 0;
103 | left: auto;
104 | z-index: 3;
105 |
106 | &.panel {
107 | margin: 0;
108 | padding: 0;
109 |
110 | .panel-heading {
111 |
112 | h3 {
113 | display: inline-block;
114 | }
115 |
116 | .close-note-editor {
117 | width: 15px;
118 | height: 15px;
119 | display: inline-block;
120 | float: right;
121 |
122 | &:hover {
123 | cursor: pointer;
124 | }
125 | }
126 | }
127 |
128 | .panel-body {
129 | padding: 0;
130 | }
131 | }
132 |
133 | .CodeMirror {
134 | position: absolute;
135 | top: 41px;
136 | bottom: 0;
137 | height: auto;
138 | cursor: text;
139 | font-size: 14px;
140 | line-height: 1.3em;
141 | font-family: consolas;
142 | }
143 | }
144 | .km-note-tips {
145 | color: #ccc;
146 | padding: 3px 8px;
147 | }
148 | #previewer-content {
149 | position: absolute;
150 | background: #FFD;
151 | padding: 5px 15px;
152 | border-radius: 5px;
153 | max-width: 400px;
154 | max-height: 200px;
155 | overflow: auto;
156 | z-index: 10;
157 | box-shadow: 0 0 15px rgba(0, 0, 0, .5);
158 | word-break: break-all;
159 | .gfm-render;
160 | }
161 | #previewer-content.ng-hide {
162 | display: block!important;
163 | left: -99999px!important;
164 | top: -99999px!important;
165 | }
166 | .panel-body {
167 | padding: 10px;
168 | }
--------------------------------------------------------------------------------
/webui/less/topTab/idea/operation.less:
--------------------------------------------------------------------------------
1 | .operation-group {
2 | width: 108px;
3 | }
4 |
5 | .edit-node {
6 | .km-btn-icon {
7 | background-position: 0 -60px;
8 | }
9 | }
10 |
11 | .remove-node {
12 | .km-btn-icon {
13 | background-position: 0 -80px;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/webui/less/topTab/idea/priority.less:
--------------------------------------------------------------------------------
1 | .priority-sprite(@count) when (@count >= 0) {
2 | .priority-sprite(@count - 1);
3 | &.priority-@{count} {
4 | background-position: 0 (-20px * (@count - 1));
5 | }
6 | }
7 |
8 | .tab-content .km-priority {
9 | vertical-align: middle;
10 | font-size: inherit;
11 | display: inline-block;
12 | width: 140px;
13 | margin: 5px;
14 | border-right: 1px dashed #eee;
15 |
16 | .km-priority-item {
17 | margin: 0 1px;
18 | padding: 1px;
19 |
20 | .km-priority-icon {
21 | .priority-sprite(9);
22 | background: url(images/iconpriority.png) repeat-y;
23 | background-color: transparent;
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/webui/less/topTab/idea/progress.less:
--------------------------------------------------------------------------------
1 | .progress-sprite(@count) when (@count >= 0) {
2 | .progress-sprite(@count - 1);
3 | &.progress-@{count} {
4 | background-position: 0 (-20px * (@count - 1));
5 | }
6 | }
7 |
8 | .tab-content .km-progress {
9 | vertical-align: middle;
10 | font-size: inherit;
11 | display: inline-block;
12 | width: 140px;
13 | margin: 5px;
14 | border-right: 1px dashed #eee;
15 |
16 | .km-progress-item {
17 | margin: 0 1px;
18 | padding: 1px;
19 |
20 | .km-progress-icon {
21 | .progress-sprite(9);
22 | background: url(images/iconprogress.png) repeat-y;
23 | background-color: transparent;
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/webui/less/topTab/idea/resource.less:
--------------------------------------------------------------------------------
1 | .resource-editor {
2 | vertical-align: middle;
3 | display: inline-block;
4 | margin: 5px;
5 |
6 | .input-group, .km-resource {
7 | font-size: 12px;
8 | }
9 |
10 | .input-group {
11 | height: 20px;
12 | width: 168px;
13 | }
14 |
15 | .resource-dropdown {
16 | position: relative;
17 | width: 168px;
18 | border: 1px solid #ccc;
19 | margin-top: -1px;
20 | border-bottom-right-radius: 4px;
21 | border-bottom-left-radius: 4px;
22 |
23 | .km-resource {
24 | position: absolute;
25 | width: 154px;
26 | margin-bottom: 3px;
27 | padding: 0;
28 | list-style-type: none;
29 | overflow: scroll;
30 | max-height: 500px;
31 |
32 | &.open {
33 | z-index: 3;
34 | background-color: #fff;
35 | }
36 |
37 | li {
38 | display: inline-block;
39 | padding: 1px 2px;
40 | border-radius: 4px;
41 | margin: 2px 3px;
42 |
43 | &[disabled] {
44 | opacity: 0.5;
45 | }
46 | }
47 | }
48 |
49 | .resource-caret {
50 | display: block;
51 | float: right;
52 | vertical-align: middle;
53 | width: 12px;
54 | height: 24px;
55 | padding: 8px 1px;
56 |
57 | &:hover {background-color: @button-hover;}
58 | &:active {background-color: @button-active;}
59 | }
60 | }
61 |
62 | // 覆盖 bootstrap
63 | input.form-control, .btn {
64 | font-size: 12px;
65 | }
66 |
67 | input.form-control {
68 | padding: 2px 4px;
69 | height: 24px;
70 | border-bottom-left-radius: 0;
71 | }
72 |
73 | .input-group-btn {
74 | line-height: 24px;
75 |
76 | .btn {
77 | padding: 2px 4px;
78 | height: 24px;
79 | border-bottom-right-radius: 0;
80 | }
81 | }
82 | }
83 |
84 |
--------------------------------------------------------------------------------
/webui/less/topTab/idea/undoRedo.less:
--------------------------------------------------------------------------------
1 | .do-group {
2 | width: 38px;
3 | }
4 |
5 | .undo {
6 | .km-btn-icon {
7 | background-position: 0 -1240px;
8 | }
9 | }
10 |
11 | .redo {
12 | .km-btn-icon {
13 | background-position: 0 -1220px;
14 | }
15 | }
--------------------------------------------------------------------------------
/webui/less/topTab/searchBox.less:
--------------------------------------------------------------------------------
1 | .search-box {
2 | float: right;
3 | background-color: #fff;
4 | border: 1px solid #dbdbdb;
5 | position: relative;
6 | top: 0;
7 | z-index: 3;
8 | width: 360px;
9 | height: 40px;
10 | padding: 3px 6px;
11 | opacity: 1;
12 |
13 | .search-input-wrap, .prev-and-next-btn {
14 | float: left;
15 | }
16 |
17 | .close-search {
18 | float: right;
19 | height: 16px;
20 | width: 16px;
21 | padding: 1px;
22 | border-radius: 100%;
23 | margin-top: 6px;
24 | margin-right: 10px;
25 |
26 | .glyphicon {
27 | top: -1px
28 | }
29 |
30 | &:hover {
31 | background-color: #efefef;
32 | }
33 |
34 | &:active {
35 | background-color: #999;
36 | }
37 | }
38 |
39 | .search-input-wrap {
40 | width: 240px;
41 | }
42 |
43 | .prev-and-next-btn {
44 | margin-left: 5px;
45 |
46 | .btn:focus {
47 | outline: none;
48 | }
49 | }
50 |
51 | .search-input {
52 | //border-right: none;
53 | }
54 |
55 | .search-addon {
56 | background-color: #fff;
57 | }
58 |
59 | }
--------------------------------------------------------------------------------
/webui/less/topTab/topTab.less:
--------------------------------------------------------------------------------
1 | .top-tab {
2 | .nav-tabs {
3 | background-color: #e1e1e1;
4 | border: 0;
5 | height: 32px;
6 |
7 | li {
8 | margin: 0;
9 |
10 | a {
11 | margin: 0;
12 | border: 0;
13 | padding: 6px 15px;
14 | border-radius: 0;
15 | vertical-align: middle;
16 |
17 | &:hover,
18 | &:focus {
19 | background: inherit;
20 | border: 0;
21 | }
22 | }
23 |
24 | &.active a {
25 | border: 0;
26 | background-color: #fff;
27 |
28 | &:hover,
29 | &:focus {
30 | border: 0;
31 | }
32 | }
33 | }
34 | }
35 |
36 | .tab-content {
37 | height: 60px;
38 | background-color: #fff;
39 | border-bottom: 1px solid #dbdbdb;
40 | }
41 |
42 | .tab-pane {
43 | font-size: 0;
44 | }
45 | }
46 |
47 | .km-btn-group {
48 | display: inline-block;
49 | // margin: 5px 0;
50 | padding: 0 5px;
51 | vertical-align: middle;
52 | border-right: 1px dashed #eee;
53 | }
54 |
55 | .km-btn-item {
56 | display: inline-block;
57 | margin: 0 3px;
58 | font-size: 0;
59 | cursor: default;
60 |
61 | &[disabled] {
62 | opacity: 0.5;
63 |
64 | &:hover,
65 | &:active {
66 | background-color: #fff;
67 | }
68 | }
69 |
70 | .km-btn-icon {
71 | display: inline-block;
72 | background: url(images/icons.png) no-repeat;
73 | background-position: 0 20px;
74 | width: 20px;
75 | height: 20px;
76 | padding: 2px;
77 | margin: 1px;
78 | vertical-align: middle;
79 | }
80 |
81 | .km-btn-caption {
82 | display: inline-block;
83 | font-size: 12px;
84 | vertical-align: middle;
85 | }
86 |
87 | &:hover {
88 | background-color: @button-hover;
89 | }
90 | &:active {
91 | background-color: @button-active;
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/webui/less/topTab/view/expand.less:
--------------------------------------------------------------------------------
1 | .btn-group-vertical {
2 | vertical-align: middle;
3 | margin: 5px;
4 | width: 90px;
5 | .expand,
6 | .expand-caption {
7 | width: 40px;
8 | margin: 0;
9 | padding: 0;
10 | border: none !important;
11 | border-radius: 0 !important;
12 |
13 | &:hover {
14 | background-color: @tool-hover;
15 | }
16 |
17 | &:active {
18 | background-color: @tool-active;
19 | }
20 |
21 | &.active {
22 | box-shadow: none;
23 | background-color: @tool-hover;
24 | }
25 | }
26 |
27 | .expand {
28 | height: 25px;
29 | background: url(images/icons.png) no-repeat 0 -995px;
30 | background-position-x: 50%;
31 | }
32 |
33 | .expand-caption {
34 | height: 20px;
35 |
36 | .caption {
37 | font-size: 12px;
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/webui/less/topTab/view/search.less:
--------------------------------------------------------------------------------
1 | .btn-group-vertical {
2 | vertical-align: middle;
3 | margin: 5px;
4 |
5 | .search, .search-caption {
6 | width: 40px;
7 | margin: 0;
8 | padding: 0;
9 | border: none!important;
10 | border-radius: 0!important;
11 |
12 | &:hover {
13 | background-color: @tool-hover;
14 | }
15 |
16 | &:active {
17 | background-color: @tool-active;
18 | }
19 |
20 | &.active {
21 | box-shadow: none;
22 | background-color: @tool-hover;
23 | }
24 | }
25 |
26 | .search {
27 | height: 25px;
28 | background: url(images/icons.png) no-repeat 0 -345px;
29 | background-position-x: 50%;
30 | }
31 |
32 | .search-caption {
33 | height: 20px;
34 |
35 | .caption {
36 | font-size: 12px;
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/webui/less/topTab/view/select.less:
--------------------------------------------------------------------------------
1 | .btn-group-vertical {
2 | vertical-align: middle;
3 | margin: 5px;
4 | width: 65px;
5 | .select,
6 | .select-caption {
7 | width: 40px;
8 | margin: 0;
9 | padding: 0;
10 | border: none !important;
11 | border-radius: 0 !important;
12 |
13 | &:hover {
14 | background-color: @tool-hover;
15 | }
16 |
17 | &:active {
18 | background-color: @tool-active;
19 | }
20 |
21 | &.active {
22 | box-shadow: none;
23 | background-color: @tool-hover;
24 | }
25 | }
26 |
27 | .select {
28 | height: 25px;
29 | background: url(images/icons.png) no-repeat 7px -1175px;
30 | }
31 |
32 | .select-caption {
33 | height: 20px;
34 |
35 | .caption {
36 | font-size: 12px;
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/webui/main.js:
--------------------------------------------------------------------------------
1 | /**
2 | * initial kityminder-editor
3 | */
4 | angular
5 | .module('kityminderDemo', ['kityminderEditor'])
6 | .config(function(configProvider) {
7 | configProvider.set('imageUpload', '../server/imageUpload.php');
8 | })
9 | .controller('MainController', function($scope) {
10 | $scope.initEditor = function(editor, minder) {
11 | window.editor = editor;
12 | window.minder = minder;
13 |
14 | /**
15 | * receive message event from extension
16 | */
17 | window.addEventListener('message', function(event) {
18 | window.message = event.data;
19 | const { command } = window.message;
20 |
21 | switch (command) {
22 | case 'import':
23 | try {
24 | const importData = JSON.parse(window.message.importData);
25 | window.minder.importJson(importData);
26 | } catch (ex) {
27 | console.error(ex);
28 | }
29 | break;
30 | }
31 | });
32 |
33 | window.addEventListener('keydown', e => {
34 | const keyCode = e.keyCode || e.which || e.charCode;
35 | const ctrlKey = e.ctrlKey || e.metaKey;
36 | if (ctrlKey && keyCode === 83) {
37 | const btnSave = document.querySelector('.km-export-save');
38 | btnSave.click();
39 | }
40 | });
41 |
42 | window.vscode.postMessage({
43 | command: 'loaded',
44 | });
45 | };
46 | });
47 |
--------------------------------------------------------------------------------
/webui/mindmap.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | mindmap
6 |
7 |
11 |
15 |
19 |
23 |
27 |
28 |
29 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/webui/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "kityminder-editor",
3 | "version": "1.0.67",
4 | "description": "A powerful mind map editor",
5 | "main": "kityminder.editor.js",
6 | "scripts": {
7 | "init": "npm i -g wr && npm install -g less && npm install -g bower && bower install && npm install",
8 | "build": "grunt build",
9 | "dev": "grunt dev",
10 | "watch": "wr --exec \"lessc --source-map less/editor.less dist/kityminder.editor.css && grunt build\" less ui",
11 | "postinstall": "bower install",
12 | "build:rollup": "rollup -w --config ./rollup.config.js"
13 | },
14 | "repository": {
15 | "type": "git",
16 | "url": "https://github.com/fex-team/kityminder-editor"
17 | },
18 | "keywords": [
19 | "kityminder",
20 | "editor",
21 | "html5",
22 | "js",
23 | "mindmap"
24 | ],
25 | "author": "fex ",
26 | "license": "GPL-2.0",
27 | "bugs": {
28 | "url": "https://github.com/fex-team/kityminder-editor/issues"
29 | },
30 | "homepage": "https://github.com/fex-team/kityminder-editor",
31 | "devDependencies": {
32 | "@babel/preset-env": "^7.4.3",
33 | "cz-conventional-changelog": "^1.1.5",
34 | "grunt": "~0.4.1",
35 | "grunt-angular-templates": "~0.5.0",
36 | "grunt-browser-sync": "^2.2.0",
37 | "grunt-contrib-clean": "^0.5.0",
38 | "grunt-contrib-concat": "~0.5.0",
39 | "grunt-contrib-copy": "^0.5.0",
40 | "grunt-contrib-cssmin": "^0.12.0",
41 | "grunt-contrib-less": "^1.0.0",
42 | "grunt-contrib-uglify": "^3.3.0",
43 | "grunt-contrib-watch": "^1.0.0",
44 | "grunt-module-dependence": "~0.2.0",
45 | "grunt-ng-annotate": "^0.9.2",
46 | "grunt-replace": "~0.8.0",
47 | "grunt-wiredep": "^2.0.0",
48 | "jshint-stylish": "^1.0.0",
49 | "load-grunt-tasks": "^3.1.0",
50 | "rollup-plugin-babel": "^4.3.2",
51 | "uglify-js": "^2.8.29"
52 | },
53 | "dependencies": {
54 | "@babel/core": "^7.4.3",
55 | "kityminder-core": "^1.4.50",
56 | "rollup-plugin-uglify": "^6.0.2"
57 | },
58 | "config": {
59 | "commitizen": {
60 | "path": "./node_modules/cz-conventional-changelog"
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/webui/rollup.config.js:
--------------------------------------------------------------------------------
1 | import { uglify } from 'rollup-plugin-uglify';
2 | import babel from 'rollup-plugin-babel';
3 |
4 | module.exports = {
5 | input: './main.js',
6 | output: {
7 | file: './dist/main.min.js',
8 | format: 'esm',
9 | strict: false,
10 | },
11 | plugins: [
12 | uglify({
13 | mangle: false,
14 | }),
15 | babel(),
16 | ],
17 | };
18 |
--------------------------------------------------------------------------------
/webui/server/imageUpload.php:
--------------------------------------------------------------------------------
1 | , msg: <错误信息>, data: {url: <返回的 URL>}};
7 | * 2. 由于要兼容两种情况的上传:通过对话框选择本地文件上传和直接 Ctrl + V(多见于截图后),因此本文件分别进行了判断
8 | *
9 | *
10 | * 注意:
11 | * 1. 本文件的路径可以进行配置,详见 README.md 中「初始化配置」部分。
12 | * 2. 由于使用场景不同,请根据实际场景编写上传文件的处理。
13 | * 3. 本文件并没有做任何的安全方面的防护,请勿用于生产环境。
14 | *
15 | * @author: zhangbobell
16 | *
17 | * @date: 2016.07.06
18 | *
19 | */
20 |
21 | // 返回给前端的地址是绝对地址,这里是前缀
22 | $HTTP_PREFIX = 'http://localhost/kityminder-editor/';
23 |
24 |
25 | $errno = 0;
26 | $msg = 'ok';
27 | $url = '';
28 |
29 |
30 | if ((($_FILES["upload_file"]["type"] == "image/gif")
31 | || ($_FILES["upload_file"]["type"] == "image/jpeg")
32 | || ($_FILES["upload_file"]["type"] == "image/jpg")
33 | || ($_FILES["upload_file"]["type"] == "image/png"))
34 | && ($_FILES["upload_file"]["size"] < 1 * 1000 * 1000)) {
35 |
36 | if ($_FILES["upload_file"]["error"] > 0) {
37 | $errno = 414;
38 | $msg = $_FILES["upload_file"]["error"];
39 | } else {
40 |
41 | // 分为两种情况 `Ctrl + V` 和普通上传
42 | if ($_FILES["upload_file"]["name"] === 'blob') {
43 | $ext_name = 'png';
44 | } else {
45 | $ext_name = array_pop(explode('.', $_FILES["upload_file"]["name"]));
46 | }
47 |
48 | $sha1_name = sha1_file($_FILES["upload_file"]["tmp_name"]) . '.' . $ext_name;
49 |
50 | move_uploaded_file($_FILES["upload_file"]["tmp_name"], "upload/" . $sha1_name);
51 | $url = $HTTP_PREFIX . "server/upload/" . $sha1_name;
52 | }
53 | } else {
54 | $errno = 416;
55 | $msg = 'File is invalid';
56 | }
57 |
58 |
59 | $result = array(
60 | 'errno' => $errno,
61 | 'msg' => $msg,
62 | 'data' => array(
63 | 'url' => $url
64 | )
65 | );
66 |
67 | echo json_encode($result);
--------------------------------------------------------------------------------
/webui/src/editor.js:
--------------------------------------------------------------------------------
1 | define(function(require, exports, module) {
2 |
3 | /**
4 | * 运行时
5 | */
6 | var runtimes = [];
7 |
8 | function assemble(runtime) {
9 | runtimes.push(runtime);
10 | }
11 |
12 | function KMEditor(selector) {
13 | this.selector = selector;
14 | for (var i = 0; i < runtimes.length; i++) {
15 | if (typeof runtimes[i] == 'function') {
16 | runtimes[i].call(this, this);
17 | }
18 | }
19 | }
20 |
21 | KMEditor.assemble = assemble;
22 |
23 | assemble(require('./runtime/container'));
24 | assemble(require('./runtime/fsm'));
25 | assemble(require('./runtime/minder'));
26 | assemble(require('./runtime/receiver'));
27 | assemble(require('./runtime/hotbox'));
28 | assemble(require('./runtime/input'));
29 | assemble(require('./runtime/clipboard-mimetype'));
30 | assemble(require('./runtime/clipboard'));
31 | assemble(require('./runtime/drag'));
32 | assemble(require('./runtime/node'));
33 | assemble(require('./runtime/history'));
34 | assemble(require('./runtime/jumping'));
35 | assemble(require('./runtime/priority'));
36 | assemble(require('./runtime/progress'));
37 |
38 |
39 | return module.exports = KMEditor;
40 | });
--------------------------------------------------------------------------------
/webui/src/expose-editor.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileOverview
3 | *
4 | * 打包暴露
5 | *
6 | * @author: techird
7 | * @copyright: Baidu FEX, 2014
8 | */
9 | define('expose-editor', function(require, exports, module) {
10 | return module.exports = kityminder.Editor = require('./editor');
11 | });
--------------------------------------------------------------------------------
/webui/src/hotbox.js:
--------------------------------------------------------------------------------
1 | define(function(require, exports, module) {
2 | return module.exports = window.HotBox;
3 | });
--------------------------------------------------------------------------------
/webui/src/lang.js:
--------------------------------------------------------------------------------
1 | define(function(require, exports, module) {
2 |
3 | });
--------------------------------------------------------------------------------
/webui/src/minder.js:
--------------------------------------------------------------------------------
1 | define(function(require, exports, module) {
2 | return module.exports = window.kityminder.Minder;
3 | });
--------------------------------------------------------------------------------
/webui/src/runtime/clipboard-mimetype.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @Desc: 新增一个用于处理系统ctrl+c ctrl+v等方式导入导出节点的MIMETYPE处理,如系统不支持clipboardEvent或者是FF则不初始化改class
3 | * @Editor: Naixor
4 | * @Date: 2015.9.21
5 | */
6 | define(function(require, exports, module) {
7 | function MimeType() {
8 | /**
9 | * 私有变量
10 | */
11 | var SPLITOR = '\uFEFF';
12 | var MIMETYPE = {
13 | 'application/km': '\uFFFF'
14 | };
15 | var SIGN = {
16 | '\uFEFF': 'SPLITOR',
17 | '\uFFFF': 'application/km'
18 | };
19 |
20 | /**
21 | * 用于将一段纯文本封装成符合其数据格式的文本
22 | * @method process private
23 | * @param {MIMETYPE} mimetype 数据格式
24 | * @param {String} text 原始文本
25 | * @return {String} 符合该数据格式下的文本
26 | * @example
27 | * var str = "123";
28 | * str = process('application/km', str); // 返回的内容再经过MimeType判断会读取出其数据格式为application/km
29 | * process('text/plain', str); // 若接受到一个非纯文本信息,则会将其转换为新的数据格式
30 | */
31 | function process(mimetype, text) {
32 | if (!this.isPureText(text)) {
33 | var _mimetype = this.whichMimeType(text);
34 | if (!_mimetype) {
35 | throw new Error('unknow mimetype!');
36 | };
37 | text = this.getPureText(text);
38 | };
39 | if (mimetype === false) {
40 | return text;
41 | };
42 | return mimetype + SPLITOR + text;
43 | }
44 |
45 | /**
46 | * 注册数据类型的标识
47 | * @method registMimeTypeProtocol public
48 | * @param {String} type 数据类型
49 | * @param {String} sign 标识
50 | */
51 | this.registMimeTypeProtocol = function(type, sign) {
52 | if (sign && SIGN[sign]) {
53 | throw new Error('sing has registed!');
54 | }
55 | if (type && !!MIMETYPE[type]) {
56 | throw new Error('mimetype has registed!');
57 | };
58 | SIGN[sign] = type;
59 | MIMETYPE[type] = sign;
60 | }
61 |
62 | /**
63 | * 获取已注册数据类型的协议
64 | * @method getMimeTypeProtocol public
65 | * @param {String} type 数据类型
66 | * @param {String} text|undefiend 文本内容或不传入
67 | * @return {String|Function}
68 | * @example
69 | * text若不传入则直接返回对应数据格式的处理(process)方法
70 | * 若传入文本则直接调用对应的process方法进行处理,此时返回处理后的内容
71 | * var m = new MimeType();
72 | * var kmprocess = m.getMimeTypeProtocol('application/km');
73 | * kmprocess("123") === m.getMimeTypeProtocol('application/km', "123");
74 | *
75 | */
76 | this.getMimeTypeProtocol = function(type, text) {
77 | var mimetype = MIMETYPE[type] || false;
78 |
79 | if (text === undefined) {
80 | return process.bind(this, mimetype);
81 | };
82 |
83 | return process(mimetype, text);
84 | }
85 |
86 | this.getSpitor = function() {
87 | return SPLITOR;
88 | }
89 |
90 | this.getMimeType = function(sign) {
91 | if (sign !== undefined) {
92 | return SIGN[sign] || null;
93 | };
94 | return MIMETYPE;
95 | }
96 | }
97 |
98 | MimeType.prototype.isPureText = function(text) {
99 | return !(~text.indexOf(this.getSpitor()));
100 | }
101 |
102 | MimeType.prototype.getPureText = function(text) {
103 | if (this.isPureText(text)) {
104 | return text;
105 | };
106 | return text.split(this.getSpitor())[1];
107 | }
108 |
109 | MimeType.prototype.whichMimeType = function(text) {
110 | if (this.isPureText(text)) {
111 | return null;
112 | };
113 | return this.getMimeType(text.split(this.getSpitor())[0]);
114 | }
115 |
116 | function MimeTypeRuntime() {
117 | if (this.minder.supportClipboardEvent && !kity.Browser.gecko) {
118 | this.MimeType = new MimeType();
119 | };
120 | }
121 |
122 | return module.exports = MimeTypeRuntime;
123 | });
--------------------------------------------------------------------------------
/webui/src/runtime/container.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileOverview
3 | *
4 | * 初始化编辑器的容器
5 | *
6 | * @author: techird
7 | * @copyright: Baidu FEX, 2014
8 | */
9 | define(function(require, exports, module) {
10 |
11 | /**
12 | * 最先执行的 Runtime,初始化编辑器容器
13 | */
14 | function ContainerRuntime() {
15 | var container;
16 |
17 | if (typeof(this.selector) == 'string') {
18 | container = document.querySelector(this.selector);
19 | } else {
20 | container = this.selector;
21 | }
22 |
23 | if (!container) throw new Error('Invalid selector: ' + this.selector);
24 |
25 | // 这个类名用于给编辑器添加样式
26 | container.classList.add('km-editor');
27 |
28 | // 暴露容器给其他运行时使用
29 | this.container = container;
30 | }
31 |
32 | return module.exports = ContainerRuntime;
33 | });
--------------------------------------------------------------------------------
/webui/src/runtime/drag.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileOverview
3 | *
4 | * 用于拖拽节点时屏蔽键盘事件
5 | *
6 | * @author: techird
7 | * @copyright: Baidu FEX, 2014
8 | */
9 | define(function(require, exports, module) {
10 |
11 | var Hotbox = require('../hotbox');
12 | var Debug = require('../tool/debug');
13 | var debug = new Debug('drag');
14 |
15 | function DragRuntime() {
16 | var fsm = this.fsm;
17 | var minder = this.minder;
18 | var hotbox = this.hotbox;
19 | var receiver = this.receiver;
20 | var receiverElement = receiver.element;
21 |
22 | // setup everything to go
23 | setupFsm();
24 |
25 | // listen the fsm changes, make action.
26 | function setupFsm() {
27 |
28 | // when jumped to drag mode, enter
29 | fsm.when('* -> drag', function() {
30 | // now is drag mode
31 | });
32 |
33 | fsm.when('drag -> *', function(exit, enter, reason) {
34 | if (reason == 'drag-finish') {
35 | // now exit drag mode
36 | }
37 | });
38 | }
39 |
40 | var downX, downY;
41 | var MOUSE_HAS_DOWN = 0;
42 | var MOUSE_HAS_UP = 1;
43 | var BOUND_CHECK = 20;
44 | var flag = MOUSE_HAS_UP;
45 | var maxX, maxY, osx, osy, containerY;
46 | var freeHorizen = false, freeVirtical = false;
47 | var frame;
48 |
49 | function move(direction, speed) {
50 | if (!direction) {
51 | freeHorizen = freeVirtical = false;
52 | frame && kity.releaseFrame(frame);
53 | frame = null;
54 | return;
55 | }
56 | if (!frame) {
57 | frame = kity.requestFrame((function (direction, speed, minder) {
58 | return function (frame) {
59 | switch (direction) {
60 | case 'left':
61 | minder._viewDragger.move({x: -speed, y: 0}, 0);
62 | break;
63 | case 'top':
64 | minder._viewDragger.move({x: 0, y: -speed}, 0);
65 | break;
66 | case 'right':
67 | minder._viewDragger.move({x: speed, y: 0}, 0);
68 | break;
69 | case 'bottom':
70 | minder._viewDragger.move({x: 0, y: speed}, 0);
71 | break;
72 | default:
73 | return;
74 | }
75 | frame.next();
76 | };
77 | })(direction, speed, minder));
78 | }
79 | }
80 |
81 | minder.on('mousedown', function(e) {
82 | flag = MOUSE_HAS_DOWN;
83 | var rect = minder.getPaper().container.getBoundingClientRect();
84 | downX = e.originEvent.clientX;
85 | downY = e.originEvent.clientY;
86 | containerY = rect.top;
87 | maxX = rect.width;
88 | maxY = rect.height;
89 | });
90 |
91 | minder.on('mousemove', function(e) {
92 | if (fsm.state() === 'drag' && flag == MOUSE_HAS_DOWN && minder.getSelectedNode()
93 | && (Math.abs(downX - e.originEvent.clientX) > BOUND_CHECK
94 | || Math.abs(downY - e.originEvent.clientY) > BOUND_CHECK)) {
95 | osx = e.originEvent.clientX;
96 | osy = e.originEvent.clientY - containerY;
97 |
98 | if (osx < BOUND_CHECK) {
99 | move('right', BOUND_CHECK - osx);
100 | } else if (osx > maxX - BOUND_CHECK) {
101 | move('left', BOUND_CHECK + osx - maxX);
102 | } else {
103 | freeHorizen = true;
104 | }
105 | if (osy < BOUND_CHECK) {
106 | move('bottom', osy);
107 | } else if (osy > maxY - BOUND_CHECK) {
108 | move('top', BOUND_CHECK + osy - maxY);
109 | } else {
110 | freeVirtical = true;
111 | }
112 | if (freeHorizen && freeVirtical) {
113 | move(false);
114 | }
115 | }
116 | if (fsm.state() !== 'drag'
117 | && flag === MOUSE_HAS_DOWN
118 | && minder.getSelectedNode()
119 | && (Math.abs(downX - e.originEvent.clientX) > BOUND_CHECK
120 | || Math.abs(downY - e.originEvent.clientY) > BOUND_CHECK)) {
121 |
122 | if (fsm.state() === 'hotbox') {
123 | hotbox.active(Hotbox.STATE_IDLE);
124 | }
125 |
126 | return fsm.jump('drag', 'user-drag');
127 | }
128 | });
129 |
130 | window.addEventListener('mouseup', function () {
131 | flag = MOUSE_HAS_UP;
132 | if (fsm.state() === 'drag') {
133 | move(false);
134 | return fsm.jump('normal', 'drag-finish');
135 | }
136 | }, false);
137 | }
138 |
139 | return module.exports = DragRuntime;
140 | });
141 |
--------------------------------------------------------------------------------
/webui/src/runtime/fsm.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileOverview
3 | *
4 | * 编辑器状态机
5 | *
6 | * @author: techird
7 | * @copyright: Baidu FEX, 2014
8 | */
9 | define(function(require, exports, module) {
10 |
11 | var Debug = require('../tool/debug');
12 | var debug = new Debug('fsm');
13 |
14 | function handlerConditionMatch(condition, when, exit, enter) {
15 | if (condition.when != when) return false;
16 | if (condition.enter != '*' && condition.enter != enter) return false;
17 | if (condition.exit != '*' && condition.exit != exit) return;
18 | return true;
19 | }
20 |
21 | function FSM(defaultState) {
22 | var currentState = defaultState;
23 | var BEFORE_ARROW = ' - ';
24 | var AFTER_ARROW = ' -> ';
25 | var handlers = [];
26 |
27 | /**
28 | * 状态跳转
29 | *
30 | * 会通知所有的状态跳转监视器
31 | *
32 | * @param {string} newState 新状态名称
33 | * @param {any} reason 跳转的原因,可以作为参数传递给跳转监视器
34 | */
35 | this.jump = function(newState, reason) {
36 | if (!reason) throw new Error('Please tell fsm the reason to jump');
37 |
38 | var oldState = currentState;
39 | var notify = [oldState, newState].concat([].slice.call(arguments, 1));
40 | var i, handler;
41 |
42 | // 跳转前
43 | for (i = 0; i < handlers.length; i++) {
44 | handler = handlers[i];
45 | if (handlerConditionMatch(handler.condition, 'before', oldState, newState)) {
46 | if (handler.apply(null, notify)) return;
47 | }
48 | }
49 |
50 | currentState = newState;
51 | debug.log('[{0}] {1} -> {2}', reason, oldState, newState);
52 |
53 | // 跳转后
54 | for (i = 0; i < handlers.length; i++) {
55 | handler = handlers[i];
56 | if (handlerConditionMatch(handler.condition, 'after', oldState, newState)) {
57 | handler.apply(null, notify);
58 | }
59 | }
60 | return currentState;
61 | };
62 |
63 | /**
64 | * 返回当前状态
65 | * @return {string}
66 | */
67 | this.state = function() {
68 | return currentState;
69 | };
70 |
71 | /**
72 | * 添加状态跳转监视器
73 | *
74 | * @param {string} condition
75 | * 监视的时机
76 | * "* => *" (默认)
77 | *
78 | * @param {Function} handler
79 | * 监视函数,当状态跳转的时候,会接收三个参数
80 | * * from - 跳转前的状态
81 | * * to - 跳转后的状态
82 | * * reason - 跳转的原因
83 | */
84 | this.when = function(condition, handler) {
85 | if (arguments.length == 1) {
86 | handler = condition;
87 | condition = '* -> *';
88 | }
89 |
90 | var when, resolved, exit, enter;
91 |
92 | resolved = condition.split(BEFORE_ARROW);
93 | if (resolved.length == 2) {
94 | when = 'before';
95 | } else {
96 | resolved = condition.split(AFTER_ARROW);
97 | if (resolved.length == 2) {
98 | when = 'after';
99 | }
100 | }
101 | if (!when) throw new Error('Illegal fsm condition: ' + condition);
102 |
103 | exit = resolved[0];
104 | enter = resolved[1];
105 |
106 | handler.condition = {
107 | when: when,
108 | exit: exit,
109 | enter: enter
110 | };
111 |
112 | handlers.push(handler);
113 | };
114 | }
115 |
116 | function FSMRumtime() {
117 | this.fsm = new FSM('normal');
118 | }
119 |
120 | return module.exports = FSMRumtime;
121 | });
--------------------------------------------------------------------------------
/webui/src/runtime/history.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileOverview
3 | *
4 | * 历史管理
5 | *
6 | * @author: techird
7 | * @copyright: Baidu FEX, 2014
8 | */
9 |
10 |
11 | define(function(require, exports, module) {
12 | var jsonDiff = require('../tool/jsondiff');
13 |
14 | function HistoryRuntime() {
15 | var minder = this.minder;
16 | var hotbox = this.hotbox;
17 |
18 | var MAX_HISTORY = 100;
19 |
20 | var lastSnap;
21 | var patchLock;
22 | var undoDiffs;
23 | var redoDiffs;
24 |
25 | function reset() {
26 | undoDiffs = [];
27 | redoDiffs = [];
28 | lastSnap = minder.exportJson();
29 | }
30 |
31 | function makeUndoDiff() {
32 | var headSnap = minder.exportJson();
33 | var diff = jsonDiff(headSnap, lastSnap);
34 | if (diff.length) {
35 | undoDiffs.push(diff);
36 | while (undoDiffs.length > MAX_HISTORY) {
37 | undoDiffs.shift();
38 | }
39 | lastSnap = headSnap;
40 | return true;
41 | }
42 | }
43 |
44 | function makeRedoDiff() {
45 | var revertSnap = minder.exportJson();
46 | redoDiffs.push(jsonDiff(revertSnap, lastSnap));
47 | lastSnap = revertSnap;
48 | }
49 |
50 | function undo() {
51 | patchLock = true;
52 | var undoDiff = undoDiffs.pop();
53 | if (undoDiff) {
54 | minder.applyPatches(undoDiff);
55 | makeRedoDiff();
56 | }
57 | patchLock = false;
58 | }
59 |
60 | function redo() {
61 | patchLock = true;
62 | var redoDiff = redoDiffs.pop();
63 | if (redoDiff) {
64 | minder.applyPatches(redoDiff);
65 | makeUndoDiff();
66 | }
67 | patchLock = false;
68 | }
69 |
70 | function changed() {
71 | if (patchLock) return;
72 | if (makeUndoDiff()) redoDiffs = [];
73 | }
74 |
75 | function hasUndo() {
76 | return !!undoDiffs.length;
77 | }
78 |
79 | function hasRedo() {
80 | return !!redoDiffs.length;
81 | }
82 |
83 | function updateSelection(e) {
84 | if (!patchLock) return;
85 | var patch = e.patch;
86 | switch (patch.express) {
87 | case 'node.add':
88 | minder.select(patch.node.getChild(patch.index), true);
89 | break;
90 | case 'node.remove':
91 | case 'data.replace':
92 | case 'data.remove':
93 | case 'data.add':
94 | minder.select(patch.node, true);
95 | break;
96 | }
97 | }
98 |
99 | this.history = {
100 | reset: reset,
101 | undo: undo,
102 | redo: redo,
103 | hasUndo: hasUndo,
104 | hasRedo: hasRedo
105 | };
106 | reset();
107 | minder.on('contentchange', changed);
108 | minder.on('import', reset);
109 | minder.on('patch', updateSelection);
110 |
111 | var main = hotbox.state('main');
112 | main.button({
113 | position: 'top',
114 | label: '撤销',
115 | key: 'Ctrl + Z',
116 | enable: hasUndo,
117 | action: undo,
118 | next: 'idle'
119 | });
120 | main.button({
121 | position: 'top',
122 | label: '重做',
123 | key: 'Ctrl + Y',
124 | enable: hasRedo,
125 | action: redo,
126 | next: 'idle'
127 | });
128 | }
129 |
130 | window.diff = jsonDiff;
131 |
132 | return module.exports = HistoryRuntime;
133 | });
--------------------------------------------------------------------------------
/webui/src/runtime/hotbox.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileOverview
3 | *
4 | * 热盒 Runtime
5 | *
6 | * @author: techird
7 | * @copyright: Baidu FEX, 2014
8 | */
9 | define(function(require, exports, module) {
10 | var Hotbox = require('../hotbox');
11 |
12 | function HotboxRuntime() {
13 | var fsm = this.fsm;
14 | var minder = this.minder;
15 | var receiver = this.receiver;
16 | var container = this.container;
17 |
18 | var hotbox = new Hotbox(container);
19 |
20 | hotbox.setParentFSM(fsm);
21 |
22 | fsm.when('normal -> hotbox', function(exit, enter, reason) {
23 | var node = minder.getSelectedNode();
24 | var position;
25 | if (node) {
26 | var box = node.getRenderBox();
27 | position = {
28 | x: box.cx,
29 | y: box.cy
30 | };
31 | }
32 | hotbox.active('main', position);
33 | });
34 |
35 | fsm.when('normal -> normal', function(exit, enter, reason, e) {
36 | if (reason == 'shortcut-handle') {
37 | var handleResult = hotbox.dispatch(e);
38 | if (handleResult) {
39 | e.preventDefault();
40 | } else {
41 | minder.dispatchKeyEvent(e);
42 | }
43 | }
44 | });
45 |
46 | fsm.when('modal -> normal', function(exit, enter, reason, e) {
47 | if (reason == 'import-text-finish') {
48 | receiver.element.focus();
49 | }
50 | });
51 |
52 | this.hotbox = hotbox;
53 | }
54 |
55 | return module.exports = HotboxRuntime;
56 | });
--------------------------------------------------------------------------------
/webui/src/runtime/minder.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileOverview
3 | *
4 | * 脑图示例运行时
5 | *
6 | * @author: techird
7 | * @copyright: Baidu FEX, 2014
8 | */
9 | define(function(require, exports, module) {
10 | var Minder = require('../minder');
11 |
12 | function MinderRuntime() {
13 | // 不使用 kityminder 的按键处理,由 ReceiverRuntime 统一处理
14 | var minder = new Minder({
15 | enableKeyReceiver: false,
16 | enableAnimation: true,
17 | });
18 |
19 | // 渲染,初始化
20 | minder.renderTo(this.selector);
21 | minder.setTheme(null);
22 | minder.select(minder.getRoot(), true);
23 | minder.execCommand('text', 'MainTopic');
24 |
25 | // 导出给其它 Runtime 使用
26 | this.minder = minder;
27 | }
28 |
29 | return (module.exports = MinderRuntime);
30 | });
31 |
--------------------------------------------------------------------------------
/webui/src/runtime/node.js:
--------------------------------------------------------------------------------
1 | define(function(require, exports, module) {
2 | function NodeRuntime() {
3 | var runtime = this;
4 | var minder = this.minder;
5 | var hotbox = this.hotbox;
6 | var fsm = this.fsm;
7 |
8 | var main = hotbox.state('main');
9 |
10 | var buttons = [
11 | '前移:Alt+Up:ArrangeUp',
12 | '下级:Tab|Insert:AppendChildNode',
13 | '同级:Enter:AppendSiblingNode',
14 | '后移:Alt+Down:ArrangeDown',
15 | '删除:Delete|Backspace:RemoveNode',
16 | '上级:Shift+Tab|Shift+Insert:AppendParentNode',
17 | //'全选:Ctrl+A:SelectAll'
18 | ];
19 |
20 | var AppendLock = 0;
21 |
22 | buttons.forEach(function(button) {
23 | var parts = button.split(':');
24 | var label = parts.shift();
25 | var key = parts.shift();
26 | var command = parts.shift();
27 | main.button({
28 | position: 'ring',
29 | label: label,
30 | key: key,
31 | action: function() {
32 | if (command.indexOf('Append') === 0) {
33 | AppendLock++;
34 | minder.execCommand(command, 'topic');
35 |
36 | // provide in input runtime
37 | function afterAppend() {
38 | if (!--AppendLock) {
39 | runtime.editText();
40 | }
41 | minder.off('layoutallfinish', afterAppend);
42 | }
43 | minder.on('layoutallfinish', afterAppend);
44 | } else {
45 | minder.execCommand(command);
46 | fsm.jump('normal', 'command-executed');
47 | }
48 | },
49 | enable: function() {
50 | return minder.queryCommandState(command) != -1;
51 | },
52 | });
53 | });
54 |
55 | main.button({
56 | position: 'bottom',
57 | label: '导入节点',
58 | key: 'Alt + V',
59 | enable: function() {
60 | var selectedNodes = minder.getSelectedNodes();
61 | return selectedNodes.length == 1;
62 | },
63 | action: importNodeData,
64 | next: 'idle',
65 | });
66 |
67 | main.button({
68 | position: 'bottom',
69 | label: '导出节点',
70 | key: 'Alt + C',
71 | enable: function() {
72 | var selectedNodes = minder.getSelectedNodes();
73 | return selectedNodes.length == 1;
74 | },
75 | action: exportNodeData,
76 | next: 'idle',
77 | });
78 |
79 | function importNodeData() {
80 | minder.fire('importNodeData');
81 | }
82 |
83 | function exportNodeData() {
84 | minder.fire('exportNodeData');
85 | }
86 |
87 | //main.button({
88 | // position: 'ring',
89 | // key: '/',
90 | // action: function(){
91 | // if (!minder.queryCommandState('expand')) {
92 | // minder.execCommand('expand');
93 | // } else if (!minder.queryCommandState('collapse')) {
94 | // minder.execCommand('collapse');
95 | // }
96 | // },
97 | // enable: function() {
98 | // return minder.queryCommandState('expand') != -1 || minder.queryCommandState('collapse') != -1;
99 | // },
100 | // beforeShow: function() {
101 | // if (!minder.queryCommandState('expand')) {
102 | // this.$button.children[0].innerHTML = '展开';
103 | // } else {
104 | // this.$button.children[0].innerHTML = '收起';
105 | // }
106 | // }
107 | //})
108 | }
109 |
110 | return (module.exports = NodeRuntime);
111 | });
112 |
--------------------------------------------------------------------------------
/webui/src/runtime/priority.js:
--------------------------------------------------------------------------------
1 | define(function(require, exports, module){
2 |
3 | function PriorityRuntime() {
4 | var minder = this.minder;
5 | var hotbox = this.hotbox;
6 |
7 | var main = hotbox.state('main');
8 |
9 | main.button({
10 | position: 'top',
11 | label: '优先级',
12 | key: 'P',
13 | next: 'priority',
14 | enable: function() {
15 | return minder.queryCommandState('priority') != -1;
16 | }
17 | });
18 |
19 | var priority = hotbox.state('priority');
20 | '123456789'.replace(/./g, function(p) {
21 | priority.button({
22 | position: 'ring',
23 | label: 'P' + p,
24 | key: p,
25 | action: function() {
26 | minder.execCommand('Priority', p);
27 | }
28 | });
29 | });
30 |
31 | priority.button({
32 | position: 'center',
33 | label: '移除',
34 | key: 'Del',
35 | action: function() {
36 | minder.execCommand('Priority', 0);
37 | }
38 | });
39 |
40 | priority.button({
41 | position: 'top',
42 | label: '返回',
43 | key: 'esc',
44 | next: 'back'
45 | });
46 |
47 | }
48 |
49 | return module.exports = PriorityRuntime;
50 |
51 | });
--------------------------------------------------------------------------------
/webui/src/runtime/progress.js:
--------------------------------------------------------------------------------
1 | define(function(require, exports, module){
2 |
3 | function ProgressRuntime() {
4 | var minder = this.minder;
5 | var hotbox = this.hotbox;
6 |
7 | var main = hotbox.state('main');
8 |
9 | main.button({
10 | position: 'top',
11 | label: '进度',
12 | key: 'G',
13 | next: 'progress',
14 | enable: function() {
15 | return minder.queryCommandState('progress') != -1;
16 | }
17 | });
18 |
19 | var progress = hotbox.state('progress');
20 | '012345678'.replace(/./g, function(p) {
21 | progress.button({
22 | position: 'ring',
23 | label: 'G' + p,
24 | key: p,
25 | action: function() {
26 | minder.execCommand('Progress', parseInt(p) + 1);
27 | }
28 | });
29 | });
30 |
31 | progress.button({
32 | position: 'center',
33 | label: '移除',
34 | key: 'Del',
35 | action: function() {
36 | minder.execCommand('Progress', 0);
37 | }
38 | });
39 |
40 | progress.button({
41 | position: 'top',
42 | label: '返回',
43 | key: 'esc',
44 | next: 'back'
45 | });
46 |
47 |
48 | }
49 |
50 | return module.exports = ProgressRuntime;
51 |
52 | });
--------------------------------------------------------------------------------
/webui/src/runtime/receiver.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileOverview
3 | *
4 | * 键盘事件接收/分发器
5 | *
6 | * @author: techird
7 | * @copyright: Baidu FEX, 2014
8 | */
9 |
10 | define(function(require, exports, module) {
11 | var key = require('../tool/key');
12 | var hotbox = require('hotbox');
13 |
14 | function ReceiverRuntime() {
15 | var fsm = this.fsm;
16 | var minder = this.minder;
17 | var me = this;
18 |
19 | // 接收事件的 div
20 | var element = document.createElement('div');
21 | element.contentEditable = true;
22 | /**
23 | * @Desc: 增加tabindex属性使得element的contenteditable不管是trur还是false都能有focus和blur事件
24 | * @Editor: Naixor
25 | * @Date: 2015.09.14
26 | */
27 | element.setAttribute('tabindex', -1);
28 | element.classList.add('receiver');
29 | element.onkeydown = element.onkeypress = element.onkeyup = dispatchKeyEvent;
30 | element.addEventListener('compositionstart', dispatchKeyEvent);
31 | // element.addEventListener('compositionend', dispatchKeyEvent);
32 | this.container.appendChild(element);
33 |
34 | // receiver 对象
35 | var receiver = {
36 | element: element,
37 | selectAll: function() {
38 | // 保证有被选中的
39 | if (!element.innerHTML) element.innerHTML = ' ';
40 | var range = document.createRange();
41 | var selection = window.getSelection();
42 | range.selectNodeContents(element);
43 | selection.removeAllRanges();
44 | selection.addRange(range);
45 | element.focus();
46 | },
47 | /**
48 | * @Desc: 增加enable和disable方法用于解决热核态的输入法屏蔽问题
49 | * @Editor: Naixor
50 | * @Date: 2015.09.14
51 | */
52 | enable: function() {
53 | element.setAttribute("contenteditable", true);
54 | },
55 | disable: function() {
56 | element.setAttribute("contenteditable", false);
57 | },
58 | /**
59 | * @Desc: hack FF下div contenteditable的光标丢失BUG
60 | * @Editor: Naixor
61 | * @Date: 2015.10.15
62 | */
63 | fixFFCaretDisappeared: function() {
64 | element.removeAttribute("contenteditable");
65 | element.setAttribute("contenteditable", "true");
66 | element.blur();
67 | element.focus();
68 | },
69 | /**
70 | * 以此事件代替通过mouse事件来判断receiver丢失焦点的事件
71 | * @editor Naixor
72 | * @Date 2015-12-2
73 | */
74 | onblur: function (handler) {
75 | element.onblur = handler;
76 | }
77 | };
78 | receiver.selectAll();
79 |
80 | minder.on('beforemousedown', receiver.selectAll);
81 | minder.on('receiverfocus', receiver.selectAll);
82 | minder.on('readonly', function() {
83 | // 屏蔽minder的事件接受,删除receiver和hotbox
84 | minder.disable();
85 | editor.receiver.element.parentElement.removeChild(editor.receiver.element);
86 | editor.hotbox.$container.removeChild(editor.hotbox.$element);
87 | });
88 |
89 | // 侦听器,接收到的事件会派发给所有侦听器
90 | var listeners = [];
91 |
92 | // 侦听指定状态下的事件,如果不传 state,侦听所有状态
93 | receiver.listen = function(state, listener) {
94 | if (arguments.length == 1) {
95 | listener = state;
96 | state = '*';
97 | }
98 | listener.notifyState = state;
99 | listeners.push(listener);
100 | };
101 |
102 | function dispatchKeyEvent(e) {
103 | e.is = function(keyExpression) {
104 | var subs = keyExpression.split('|');
105 | for (var i = 0; i < subs.length; i++) {
106 | if (key.is(this, subs[i])) return true;
107 | }
108 | return false;
109 | };
110 | var listener, jumpState;
111 | for (var i = 0; i < listeners.length; i++) {
112 |
113 | listener = listeners[i];
114 | // 忽略不在侦听状态的侦听器
115 | if (listener.notifyState != '*' && listener.notifyState != fsm.state()) {
116 | continue;
117 | }
118 |
119 | /**
120 | *
121 | * 对于所有的侦听器,只允许一种处理方式:跳转状态。
122 | * 如果侦听器确定要跳转,则返回要跳转的状态。
123 | * 每个事件只允许一个侦听器进行状态跳转
124 | * 跳转动作由侦听器自行完成(因为可能需要在跳转时传递 reason),返回跳转结果即可。
125 | * 比如:
126 | *
127 | * ```js
128 | * receiver.listen('normal', function(e) {
129 | * if (isSomeReasonForJumpState(e)) {
130 | * return fsm.jump('newstate', e);
131 | * }
132 | * });
133 | * ```
134 | */
135 | if (listener.call(null, e)) {
136 | return;
137 | }
138 | }
139 | }
140 |
141 | this.receiver = receiver;
142 | }
143 |
144 | return module.exports = ReceiverRuntime;
145 |
146 | });
147 |
--------------------------------------------------------------------------------
/webui/src/tool/debug.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileOverview
3 | *
4 | * 支持各种调试后门
5 | *
6 | * @author: techird
7 | * @copyright: Baidu FEX, 2014
8 | */
9 | define(function(require, exports, module) {
10 | var format = require('./format');
11 |
12 | function noop() {}
13 |
14 | function stringHash(str) {
15 | var hash = 0;
16 | for (var i = 0; i < str.length; i++) {
17 | hash += str.charCodeAt(i);
18 | }
19 | return hash;
20 | }
21 |
22 | /* global console */
23 | function Debug(flag) {
24 | var debugMode = this.flaged = window.location.search.indexOf(flag) != -1;
25 |
26 | if (debugMode) {
27 | var h = stringHash(flag) % 360;
28 |
29 | var flagStyle = format(
30 | 'background: hsl({0}, 50%, 80%); ' +
31 | 'color: hsl({0}, 100%, 30%); ' +
32 | 'padding: 2px 3px; ' +
33 | 'margin: 1px 3px 0 0;' +
34 | 'border-radius: 2px;', h);
35 |
36 | var textStyle = 'background: none; color: black;';
37 | this.log = function() {
38 | var output = format.apply(null, arguments);
39 | console.log(format('%c{0}%c{1}', flag, output), flagStyle, textStyle);
40 | };
41 | } else {
42 | this.log = noop;
43 | }
44 | }
45 |
46 | return module.exports = Debug;
47 | });
--------------------------------------------------------------------------------
/webui/src/tool/format.js:
--------------------------------------------------------------------------------
1 | define(function(require, exports, module) {
2 | function format(template, args) {
3 | if (typeof(args) != 'object') {
4 | args = [].slice.call(arguments, 1);
5 | }
6 | return String(template).replace(/\{(\w+)\}/ig, function(match, $key) {
7 | return args[$key] || $key;
8 | });
9 | }
10 | return module.exports = format;
11 | });
--------------------------------------------------------------------------------
/webui/src/tool/innertext.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileOverview
3 | *
4 | * innerText polyfill
5 | *
6 | * @author: techird
7 | * @copyright: Baidu FEX, 2014
8 | */
9 |
10 |
11 | define(function(require, exports, module) {
12 | if ((!('innerText' in document.createElement('a'))) && ('getSelection' in window)) {
13 | HTMLElement.prototype.__defineGetter__('innerText', function() {
14 | var selection = window.getSelection(),
15 | ranges = [],
16 | str, i;
17 |
18 | // Save existing selections.
19 | for (i = 0; i < selection.rangeCount; i++) {
20 | ranges[i] = selection.getRangeAt(i);
21 | }
22 |
23 | // Deselect everything.
24 | selection.removeAllRanges();
25 |
26 | // Select `el` and all child nodes.
27 | // 'this' is the element .innerText got called on
28 | selection.selectAllChildren(this);
29 |
30 | // Get the string representation of the selected nodes.
31 | str = selection.toString();
32 |
33 | // Deselect everything. Again.
34 | selection.removeAllRanges();
35 |
36 | // Restore all formerly existing selections.
37 | for (i = 0; i < ranges.length; i++) {
38 | selection.addRange(ranges[i]);
39 | }
40 |
41 | // Oh look, this is what we wanted.
42 | // String representation of the element, close to as rendered.
43 | return str;
44 | });
45 | HTMLElement.prototype.__defineSetter__('innerText', function(text) {
46 | /**
47 | * @Desc: 解决FireFox节点内容删除后text为null,出现报错的问题
48 | * @Editor: Naixor
49 | * @Date: 2015.9.16
50 | */
51 | this.innerHTML = (text || '').replace(//g, '>').replace(/\n/g, '
');
52 | });
53 | }
54 | });
--------------------------------------------------------------------------------
/webui/src/tool/jsondiff.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileOverview
3 | *
4 | *
5 | *
6 | * @author: techird
7 | * @copyright: Baidu FEX, 2014
8 | */
9 |
10 |
11 | define(function(require, exports, module) {
12 | /*!
13 | * https://github.com/Starcounter-Jack/Fast-JSON-Patch
14 | * json-patch-duplex.js 0.5.0
15 | * (c) 2013 Joachim Wester
16 | * MIT license
17 | */
18 |
19 | var _objectKeys = (function () {
20 | if (Object.keys)
21 | return Object.keys;
22 |
23 | return function (o) {
24 | var keys = [];
25 | for (var i in o) {
26 | if (o.hasOwnProperty(i)) {
27 | keys.push(i);
28 | }
29 | }
30 | return keys;
31 | };
32 | })();
33 | function escapePathComponent(str) {
34 | if (str.indexOf('/') === -1 && str.indexOf('~') === -1)
35 | return str;
36 | return str.replace(/~/g, '~0').replace(/\//g, '~1');
37 | }
38 | function deepClone(obj) {
39 | if (typeof obj === "object") {
40 | return JSON.parse(JSON.stringify(obj));
41 | } else {
42 | return obj;
43 | }
44 | }
45 |
46 | // Dirty check if obj is different from mirror, generate patches and update mirror
47 | function _generate(mirror, obj, patches, path) {
48 | var newKeys = _objectKeys(obj);
49 | var oldKeys = _objectKeys(mirror);
50 | var changed = false;
51 | var deleted = false;
52 |
53 | for (var t = oldKeys.length - 1; t >= 0; t--) {
54 | var key = oldKeys[t];
55 | var oldVal = mirror[key];
56 | if (obj.hasOwnProperty(key)) {
57 | var newVal = obj[key];
58 | if (typeof oldVal == "object" && oldVal != null && typeof newVal == "object" && newVal != null) {
59 | _generate(oldVal, newVal, patches, path + "/" + escapePathComponent(key));
60 | } else {
61 | if (oldVal != newVal) {
62 | changed = true;
63 | patches.push({ op: "replace", path: path + "/" + escapePathComponent(key), value: deepClone(newVal) });
64 | }
65 | }
66 | } else {
67 | patches.push({ op: "remove", path: path + "/" + escapePathComponent(key) });
68 | deleted = true; // property has been deleted
69 | }
70 | }
71 |
72 | if (!deleted && newKeys.length == oldKeys.length) {
73 | return;
74 | }
75 |
76 | for (var t = 0; t < newKeys.length; t++) {
77 | var key = newKeys[t];
78 | if (!mirror.hasOwnProperty(key)) {
79 | patches.push({ op: "add", path: path + "/" + escapePathComponent(key), value: deepClone(obj[key]) });
80 | }
81 | }
82 | }
83 |
84 | function compare(tree1, tree2) {
85 | var patches = [];
86 | _generate(tree1, tree2, patches, '');
87 | return patches;
88 | }
89 |
90 | return module.exports = compare;
91 | });
--------------------------------------------------------------------------------
/webui/src/tool/key.js:
--------------------------------------------------------------------------------
1 | define(function(require, exports, module) {
2 | var keymap = require('./keymap');
3 |
4 | var CTRL_MASK = 0x1000;
5 | var ALT_MASK = 0x2000;
6 | var SHIFT_MASK = 0x4000;
7 |
8 | function hash(unknown) {
9 | if (typeof(unknown) == 'string') {
10 | return hashKeyExpression(unknown);
11 | }
12 | return hashKeyEvent(unknown);
13 | }
14 | function is(a, b) {
15 | return a && b && hash(a) == hash(b);
16 | }
17 | exports.hash = hash;
18 | exports.is = is;
19 |
20 |
21 | function hashKeyEvent(keyEvent) {
22 | var hashCode = 0;
23 | if (keyEvent.ctrlKey || keyEvent.metaKey) {
24 | hashCode |= CTRL_MASK;
25 | }
26 | if (keyEvent.altKey) {
27 | hashCode |= ALT_MASK;
28 | }
29 | if (keyEvent.shiftKey) {
30 | hashCode |= SHIFT_MASK;
31 | }
32 | // Shift, Control, Alt KeyCode ignored.
33 | if ([16, 17, 18, 91].indexOf(keyEvent.keyCode) === -1) {
34 | /**
35 | * 解决浏览器输入法状态下对keyDown的keyCode判断不准确的问题,使用keyIdentifier,
36 | * 可以解决chrome和safari下的各种问题,其他浏览器依旧有问题,然而那并不影响我们对特
37 | * 需判断的按键进行判断(比如Space在safari输入法态下就是229,其他的就不是)
38 | * @editor Naixor
39 | * @Date 2015-12-2
40 | */
41 | if (keyEvent.keyCode === 229 && keyEvent.keyIdentifier) {
42 | return hashCode |= parseInt(keyEvent.keyIdentifier.substr(2), 16);
43 | }
44 | hashCode |= keyEvent.keyCode;
45 | }
46 | return hashCode;
47 | }
48 |
49 | function hashKeyExpression(keyExpression) {
50 | var hashCode = 0;
51 | keyExpression.toLowerCase().split(/\s*\+\s*/).forEach(function(name) {
52 | switch(name) {
53 | case 'ctrl':
54 | case 'cmd':
55 | hashCode |= CTRL_MASK;
56 | break;
57 | case 'alt':
58 | hashCode |= ALT_MASK;
59 | break;
60 | case 'shift':
61 | hashCode |= SHIFT_MASK;
62 | break;
63 | default:
64 | hashCode |= keymap[name];
65 | }
66 | });
67 | return hashCode;
68 | }
69 | });
70 |
--------------------------------------------------------------------------------
/webui/src/tool/keymap.js:
--------------------------------------------------------------------------------
1 | define(function(require, exports, module) {
2 | var keymap = {
3 |
4 | 'Shift': 16,
5 | 'Control': 17,
6 | 'Alt': 18,
7 | 'CapsLock': 20,
8 |
9 | 'BackSpace': 8,
10 | 'Tab': 9,
11 | 'Enter': 13,
12 | 'Esc': 27,
13 | 'Space': 32,
14 |
15 | 'PageUp': 33,
16 | 'PageDown': 34,
17 | 'End': 35,
18 | 'Home': 36,
19 |
20 | 'Insert': 45,
21 |
22 | 'Left': 37,
23 | 'Up': 38,
24 | 'Right': 39,
25 | 'Down': 40,
26 |
27 | 'Direction': {
28 | 37: 1,
29 | 38: 1,
30 | 39: 1,
31 | 40: 1
32 | },
33 |
34 | 'Del': 46,
35 |
36 | 'NumLock': 144,
37 |
38 | 'Cmd': 91,
39 | 'CmdFF': 224,
40 | 'F1': 112,
41 | 'F2': 113,
42 | 'F3': 114,
43 | 'F4': 115,
44 | 'F5': 116,
45 | 'F6': 117,
46 | 'F7': 118,
47 | 'F8': 119,
48 | 'F9': 120,
49 | 'F10': 121,
50 | 'F11': 122,
51 | 'F12': 123,
52 |
53 | '`': 192,
54 | '=': 187,
55 | '-': 189,
56 |
57 | '/': 191,
58 | '.': 190
59 | };
60 |
61 | // 小写适配
62 | for (var key in keymap) {
63 | if (keymap.hasOwnProperty(key)) {
64 | keymap[key.toLowerCase()] = keymap[key];
65 | }
66 | }
67 | var aKeyCode = 65;
68 | var aCharCode = 'a'.charCodeAt(0);
69 |
70 | // letters
71 | 'abcdefghijklmnopqrstuvwxyz'.split('').forEach(function(letter) {
72 | keymap[letter] = aKeyCode + (letter.charCodeAt(0) - aCharCode);
73 | });
74 |
75 | // numbers
76 | var n = 9;
77 | do {
78 | keymap[n.toString()] = n + 48;
79 | } while (--n);
80 |
81 | module.exports = keymap;
82 | });
--------------------------------------------------------------------------------
/webui/ui/dialog/hyperlink/hyperlink.ctrl.js:
--------------------------------------------------------------------------------
1 | angular.module('kityminderEditor')
2 | .controller('hyperlink.ctrl', function ($scope, $modalInstance, link) {
3 |
4 | var urlRegex = '^(?!mailto:)(?:(?:http|https|ftp)://)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$';
5 | $scope.R_URL = new RegExp(urlRegex, 'i');
6 |
7 | $scope.url = link.url || '';
8 | $scope.title = link.title || '';
9 |
10 | setTimeout(function() {
11 | var $linkUrl = $('#link-url');
12 | $linkUrl.focus();
13 | $linkUrl[0].setSelectionRange(0, $scope.url.length);
14 | }, 30);
15 |
16 | $scope.shortCut = function(e) {
17 | e.stopPropagation();
18 |
19 | if (e.keyCode == 13) {
20 | $scope.ok();
21 | } else if (e.keyCode == 27) {
22 | $scope.cancel();
23 | }
24 | };
25 |
26 | $scope.ok = function () {
27 | if($scope.R_URL.test($scope.url)) {
28 | $modalInstance.close({
29 | url: $scope.url,
30 | title: $scope.title
31 | });
32 | } else {
33 | $scope.urlPassed = false;
34 |
35 | var $linkUrl = $('#link-url');
36 | $linkUrl.focus();
37 | $linkUrl[0].setSelectionRange(0, $scope.url.length);
38 | }
39 | editor.receiver.selectAll();
40 | };
41 |
42 | $scope.cancel = function () {
43 | $modalInstance.dismiss('cancel');
44 | editor.receiver.selectAll();
45 | };
46 |
47 | });
--------------------------------------------------------------------------------
/webui/ui/dialog/hyperlink/hyperlink.tpl.html:
--------------------------------------------------------------------------------
1 |
4 |
23 |
--------------------------------------------------------------------------------
/webui/ui/dialog/imExportNode/imExportNode.ctrl.js:
--------------------------------------------------------------------------------
1 | angular.module('kityminderEditor')
2 | .controller('imExportNode.ctrl', function ($scope, $modalInstance, title, defaultValue, type) {
3 |
4 | $scope.title = title;
5 |
6 | $scope.value = defaultValue;
7 |
8 | $scope.type = type;
9 |
10 | $scope.ok = function () {
11 | if ($scope.value == '') {
12 | return;
13 | }
14 | $modalInstance.close($scope.value);
15 | editor.receiver.selectAll();
16 | };
17 |
18 | $scope.cancel = function () {
19 | $modalInstance.dismiss('cancel');
20 | editor.receiver.selectAll();
21 | };
22 |
23 | setTimeout(function() {
24 | $('.single-input').focus();
25 |
26 | $('.single-input')[0].setSelectionRange(0, defaultValue.length);
27 |
28 | }, 30);
29 |
30 | $scope.shortCut = function(e) {
31 | e.stopPropagation();
32 |
33 | //if (e.keyCode == 13 && e.shiftKey == false) {
34 | // $scope.ok();
35 | //}
36 |
37 | if (e.keyCode == 27) {
38 | $scope.cancel();
39 | }
40 |
41 | // tab 键屏蔽默认事件 和 backspace 键屏蔽默认事件
42 | if (e.keyCode == 8 && type == 'export') {
43 | e.preventDefault();
44 | }
45 |
46 | if (e.keyCode == 9) {
47 | e.preventDefault();
48 | var $textarea = e.target;
49 | var pos = getCursortPosition($textarea);
50 | var str = $textarea.value;
51 | $textarea.value = str.substr(0, pos) + '\t' + str.substr(pos);
52 | setCaretPosition($textarea, pos + 1);
53 | }
54 |
55 | };
56 |
57 | /*
58 | * 获取 textarea 的光标位置
59 | * @Author: Naixor
60 | * @date: 2015.09.23
61 | * */
62 | function getCursortPosition (ctrl) {
63 | var CaretPos = 0; // IE Support
64 | if (document.selection) {
65 | ctrl.focus ();
66 | var Sel = document.selection.createRange ();
67 | Sel.moveStart ('character', -ctrl.value.length);
68 | CaretPos = Sel.text.length;
69 | }
70 | // Firefox support
71 | else if (ctrl.selectionStart || ctrl.selectionStart == '0') {
72 | CaretPos = ctrl.selectionStart;
73 | }
74 | return (CaretPos);
75 | }
76 |
77 | /*
78 | * 设置 textarea 的光标位置
79 | * @Author: Naixor
80 | * @date: 2015.09.23
81 | * */
82 | function setCaretPosition(ctrl, pos){
83 | if(ctrl.setSelectionRange) {
84 | ctrl.focus();
85 | ctrl.setSelectionRange(pos,pos);
86 | } else if (ctrl.createTextRange) {
87 | var range = ctrl.createTextRange();
88 | range.collapse(true);
89 | range.moveEnd('character', pos);
90 | range.moveStart('character', pos);
91 | range.select();
92 | }
93 | }
94 |
95 | });
--------------------------------------------------------------------------------
/webui/ui/dialog/imExportNode/imExportNode.tpl.html:
--------------------------------------------------------------------------------
1 |
4 |
5 |
12 |
13 |
--------------------------------------------------------------------------------
/webui/ui/dialog/image/image.ctrl.js:
--------------------------------------------------------------------------------
1 | angular.module('kityminderEditor')
2 | .controller('image.ctrl', ['$http', '$scope', '$modalInstance', 'image', 'server', function($http, $scope, $modalInstance, image, server) {
3 |
4 | $scope.data = {
5 | list: [],
6 | url: image.url || '',
7 | title: image.title || '',
8 | R_URL: /^https?\:\/\/\w+/
9 | };
10 |
11 | setTimeout(function() {
12 | var $imageUrl = $('#image-url');
13 | $imageUrl.focus();
14 | $imageUrl[0].setSelectionRange(0, $scope.data.url.length);
15 | }, 300);
16 |
17 |
18 | // 搜索图片按钮点击事件
19 | $scope.searchImage = function() {
20 | $scope.list = [];
21 |
22 | getImageData()
23 | .success(function(json) {
24 | if(json && json.data) {
25 | for(var i = 0; i < json.data.length; i++) {
26 | if(json.data[i].objURL) {
27 | $scope.list.push({
28 | title: json.data[i].fromPageTitleEnc,
29 | src: json.data[i].middleURL,
30 | url: json.data[i].middleURL
31 | });
32 | }
33 | }
34 | }
35 | })
36 | .error(function() {
37 |
38 | });
39 | };
40 |
41 | // 选择图片的鼠标点击事件
42 | $scope.selectImage = function($event) {
43 | var targetItem = $('#img-item'+ (this.$index));
44 | var targetImg = $('#img-'+ (this.$index));
45 |
46 | targetItem.siblings('.selected').removeClass('selected');
47 | targetItem.addClass('selected');
48 |
49 | $scope.data.url = targetImg.attr('src');
50 | $scope.data.title = targetImg.attr('alt');
51 | };
52 |
53 | // 自动上传图片,后端需要直接返回图片 URL
54 | $scope.uploadImage = function() {
55 | var fileInput = $('#upload-image');
56 | if (!fileInput.val()) {
57 | return;
58 | }
59 | if (/^.*\.(jpg|JPG|jpeg|JPEG|gif|GIF|png|PNG)$/.test(fileInput.val())) {
60 | var file = fileInput[0].files[0];
61 | return server.uploadImage(file).then(function (json) {
62 | var resp = json.data;
63 | if (resp.errno === 0) {
64 | $scope.data.url = resp.data.url;
65 | }
66 | });
67 | } else {
68 | alert("后缀只能是 jpg、gif 及 png");
69 | }
70 | };
71 |
72 | $scope.shortCut = function(e) {
73 | e.stopPropagation();
74 |
75 | if (e.keyCode == 13) {
76 | $scope.ok();
77 | } else if (e.keyCode == 27) {
78 | $scope.cancel();
79 | }
80 | };
81 |
82 | $scope.ok = function () {
83 | if($scope.data.R_URL.test($scope.data.url)) {
84 | $modalInstance.close({
85 | url: $scope.data.url,
86 | title: $scope.data.title
87 | });
88 | } else {
89 | $scope.urlPassed = false;
90 |
91 | var $imageUrl = $('#image-url');
92 | if ($imageUrl) {
93 | $imageUrl.focus();
94 | $imageUrl[0].setSelectionRange(0, $scope.data.url.length);
95 | }
96 |
97 | }
98 |
99 | editor.receiver.selectAll();
100 | };
101 |
102 | $scope.cancel = function () {
103 | $modalInstance.dismiss('cancel');
104 | editor.receiver.selectAll();
105 | };
106 |
107 | function getImageData() {
108 | var key = $scope.data.searchKeyword2;
109 | var currentTime = new Date();
110 | var url = 'http://image.baidu.com/search/acjson?tn=resultjson_com&ipn=rj&ct=201326592&fp=result&queryWord='+ key +'&cl=2&lm=-1&ie=utf-8&oe=utf-8&st=-1&ic=0&word='+ key +'&face=0&istype=2&nc=1&pn=60&rn=60&gsm=3c&'+ currentTime.getTime() +'=&callback=JSON_CALLBACK';
111 |
112 | return $http.jsonp(url);
113 | }
114 | }]);
--------------------------------------------------------------------------------
/webui/ui/dialog/image/image.tpl.html:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
14 |
15 |
16 | -
17 |
18 | {{ image.title }}
19 |
20 |
21 |
22 |
23 |
24 |
45 |
46 |
47 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/webui/ui/directive/appendNode/appendNode.directive.js:
--------------------------------------------------------------------------------
1 | angular.module('kityminderEditor').directive('appendNode', [
2 | 'commandBinder',
3 | function(commandBinder) {
4 | return {
5 | restrict: 'E',
6 | templateUrl: 'ui/directive/appendNode/appendNode.html',
7 | scope: {
8 | minder: '=',
9 | },
10 | replace: true,
11 | link: function($scope) {
12 | var minder = $scope.minder;
13 |
14 | commandBinder.bind(minder, 'appendchildnode', $scope);
15 |
16 | $scope.execCommand = function(command) {
17 | minder.execCommand(command, 'topic');
18 | editText();
19 | };
20 |
21 | function editText() {
22 | var receiverElement = editor.receiver.element;
23 | var fsm = editor.fsm;
24 | var receiver = editor.receiver;
25 |
26 | receiverElement.innerText = minder.queryCommandValue('text');
27 | fsm.jump('input', 'input-request');
28 | receiver.selectAll();
29 | }
30 | },
31 | };
32 | },
33 | ]);
34 |
--------------------------------------------------------------------------------
/webui/ui/directive/appendNode/appendNode.html:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 | {{ 'appendchildnode' | lang:'ui/command' }}
8 |
9 |
13 |
14 | {{ 'appendparentnode' | lang:'ui/command' }}
15 |
16 |
20 |
21 | {{ 'appendsiblingnode' | lang:'ui/command' }}
22 |
23 |
--------------------------------------------------------------------------------
/webui/ui/directive/arrange/arrange.directive.js:
--------------------------------------------------------------------------------
1 | angular.module('kityminderEditor')
2 | .directive('arrange', ['commandBinder', function(commandBinder) {
3 | return {
4 | restrict: 'E',
5 | templateUrl: 'ui/directive/arrange/arrange.html',
6 | scope: {
7 | minder: '='
8 | },
9 | replace: true,
10 | link: function($scope) {
11 | var minder = $scope.minder;
12 |
13 | //commandBinder.bind(minder, 'priority', $scope);
14 | }
15 | }
16 | }]);
--------------------------------------------------------------------------------
/webui/ui/directive/arrange/arrange.html:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 | {{ 'arrangeup' | lang:'ui/command' }}
8 |
9 |
13 |
14 | {{ 'arrangedown' | lang:'ui/command' }}
15 |
16 |
--------------------------------------------------------------------------------
/webui/ui/directive/colorPanel/colorPanel.directive.js:
--------------------------------------------------------------------------------
1 | angular.module('kityminderEditor')
2 | .directive('colorPanel', function() {
3 | return {
4 | restrict: 'E',
5 | templateUrl: 'ui/directive/colorPanel/colorPanel.html',
6 | scope: {
7 | minder: '='
8 | },
9 | replace: true,
10 | link: function(scope) {
11 |
12 | var minder = scope.minder;
13 | var currentTheme = minder.getThemeItems();
14 |
15 | scope.$on('colorPicked', function(event, color) {
16 | event.stopPropagation();
17 | scope.bgColor = color;
18 | minder.execCommand('background', color);
19 | });
20 |
21 | scope.setDefaultBg = function() {
22 | var currentNode = minder.getSelectedNode();
23 | var bgColor = minder.getNodeStyle(currentNode, 'background');
24 |
25 | // 有可能是 kity 的颜色类
26 | return typeof bgColor === 'object' ? bgColor.toHEX() : bgColor;
27 | };
28 |
29 | scope.bgColor = scope.setDefaultBg() || '#fff';
30 |
31 | }
32 | }
33 | });
--------------------------------------------------------------------------------
/webui/ui/directive/colorPanel/colorPanel.html:
--------------------------------------------------------------------------------
1 |
2 |
5 |
9 |
10 |
11 |
15 |
16 |
--------------------------------------------------------------------------------
/webui/ui/directive/expandLevel/expandLevel.directive.js:
--------------------------------------------------------------------------------
1 | angular.module('kityminderEditor')
2 | .directive('expandLevel', function() {
3 | return {
4 | restrict: 'E',
5 | templateUrl: 'ui/directive/expandLevel/expandLevel.html',
6 | scope: {
7 | minder: '='
8 | },
9 | replace: true,
10 | link: function($scope) {
11 |
12 | $scope.levels = [1, 2, 3, 4, 5, 6];
13 | }
14 | }
15 | });
--------------------------------------------------------------------------------
/webui/ui/directive/expandLevel/expandLevel.html:
--------------------------------------------------------------------------------
1 |
2 |
7 |
15 |
21 |
--------------------------------------------------------------------------------
/webui/ui/directive/export/export.directive.js:
--------------------------------------------------------------------------------
1 | angular.module('kityminderEditor').directive('export', [
2 | 'commandBinder',
3 | function() {
4 | return {
5 | restrict: 'E',
6 | templateUrl: 'ui/directive/export/export.html',
7 | scope: {
8 | minder: '=',
9 | },
10 | replace: true,
11 | link: function($scope) {
12 | var minder = $scope.minder;
13 |
14 | $scope.save = function() {
15 | window.vscode.postMessage({
16 | command: 'save',
17 | exportData: JSON.stringify(minder.exportJson(), null, 4),
18 | });
19 | };
20 | $scope.exportToImage = function() {
21 | minder.exportData('png').then(function(res) {
22 | window.vscode.postMessage({
23 | command: 'exportToImage',
24 | exportData: res,
25 | });
26 | });
27 | };
28 | },
29 | };
30 | },
31 | ]);
32 |
--------------------------------------------------------------------------------
/webui/ui/directive/export/export.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
--------------------------------------------------------------------------------
/webui/ui/directive/fontOperator/fontOperator.directive.js:
--------------------------------------------------------------------------------
1 | angular.module('kityminderEditor').directive('fontOperator', function() {
2 | return {
3 | restrict: 'E',
4 | templateUrl: 'ui/directive/fontOperator/fontOperator.html',
5 | scope: {
6 | minder: '=',
7 | },
8 | replace: true,
9 | link: function(scope) {
10 | var minder = scope.minder;
11 | var currentTheme = minder.getThemeItems();
12 |
13 | scope.fontSizeList = [10, 12, 16, 18, 24, 32, 48];
14 | scope.fontFamilyList = [
15 | {
16 | name: 'SimSun',
17 | val: '宋体,SimSun',
18 | },
19 | {
20 | name: 'Microsoft YaHei',
21 | val: '微软雅黑,Microsoft YaHei',
22 | },
23 | {
24 | name: 'SimKai',
25 | val: '楷体,楷体_GB2312,SimKai',
26 | },
27 | {
28 | name: 'SimHei',
29 | val: '黑体, SimHei',
30 | },
31 | {
32 | name: 'SimLi',
33 | val: '隶书, SimLi',
34 | },
35 | {
36 | name: 'Andale Mono',
37 | val: 'andale mono',
38 | },
39 | {
40 | name: 'Arial',
41 | val: 'arial,helvetica,sans-serif',
42 | },
43 | {
44 | name: 'arialBlack',
45 | val: 'arial black,avant garde',
46 | },
47 | {
48 | name: 'Comic Sans Ms',
49 | val: 'comic sans ms',
50 | },
51 | {
52 | name: 'Impact',
53 | val: 'impact,chicago',
54 | },
55 | {
56 | name: 'Times New Roman',
57 | val: 'times new roman',
58 | },
59 | {
60 | name: 'Sans-Serif',
61 | val: 'sans-serif',
62 | },
63 | ];
64 |
65 | scope.$on('colorPicked', function(event, color) {
66 | event.stopPropagation();
67 |
68 | scope.foreColor = color;
69 | minder.execCommand('forecolor', color);
70 | });
71 |
72 | scope.setDefaultColor = function() {
73 | var currentNode = minder.getSelectedNode();
74 | var fontColor = minder.getNodeStyle(currentNode, 'color');
75 |
76 | // 有可能是 kity 的颜色类
77 | return typeof fontColor === 'object' ? fontColor.toHEX() : fontColor;
78 | };
79 |
80 | scope.foreColor = scope.setDefaultColor() || '#000';
81 |
82 | scope.getFontfamilyName = function(val) {
83 | var fontName = '';
84 | scope.fontFamilyList.forEach(function(ele, idx, arr) {
85 | if (ele.val === val) {
86 | fontName = ele.name;
87 | return '';
88 | }
89 | });
90 |
91 | return fontName;
92 | };
93 | },
94 | };
95 | });
96 |
--------------------------------------------------------------------------------
/webui/ui/directive/fontOperator/fontOperator.html:
--------------------------------------------------------------------------------
1 |
2 |
31 |
54 |
60 |
66 |
67 |
68 | A
74 |
80 |
81 |
82 |
88 |
89 |
90 |
91 |
--------------------------------------------------------------------------------
/webui/ui/directive/hyperLink/hyperLink.directive.js:
--------------------------------------------------------------------------------
1 | angular.module('kityminderEditor')
2 | .directive('hyperLink', ['$modal', function($modal) {
3 | return {
4 | restrict: 'E',
5 | templateUrl: 'ui/directive/hyperLink/hyperLink.html',
6 | scope: {
7 | minder: '='
8 | },
9 | replace: true,
10 | link: function($scope) {
11 | var minder = $scope.minder;
12 |
13 | $scope.addHyperlink = function() {
14 |
15 | var link = minder.queryCommandValue('HyperLink');
16 |
17 | var hyperlinkModal = $modal.open({
18 | animation: true,
19 | templateUrl: 'ui/dialog/hyperlink/hyperlink.tpl.html',
20 | controller: 'hyperlink.ctrl',
21 | size: 'md',
22 | resolve: {
23 | link: function() {
24 | return link;
25 | }
26 | }
27 | });
28 |
29 | hyperlinkModal.result.then(function(result) {
30 | minder.execCommand('HyperLink', result.url, result.title || '');
31 | });
32 | }
33 | }
34 | }
35 | }]);
--------------------------------------------------------------------------------
/webui/ui/directive/hyperLink/hyperLink.html:
--------------------------------------------------------------------------------
1 |
2 |
8 |
17 |
27 |
--------------------------------------------------------------------------------
/webui/ui/directive/imageBtn/imageBtn.directive.js:
--------------------------------------------------------------------------------
1 | angular.module('kityminderEditor')
2 | .directive('imageBtn', ['$modal', function($modal) {
3 | return {
4 | restrict: 'E',
5 | templateUrl: 'ui/directive/imageBtn/imageBtn.html',
6 | scope: {
7 | minder: '='
8 | },
9 | replace: true,
10 | link: function($scope) {
11 | var minder = $scope.minder;
12 |
13 | $scope.addImage = function() {
14 |
15 | var image = minder.queryCommandValue('image');
16 |
17 | var imageModal = $modal.open({
18 | animation: true,
19 | templateUrl: 'ui/dialog/image/image.tpl.html',
20 | controller: 'image.ctrl',
21 | size: 'md',
22 | resolve: {
23 | image: function() {
24 | return image;
25 | }
26 | }
27 | });
28 |
29 | imageModal.result.then(function(result) {
30 | minder.execCommand('image', result.url, result.title || '');
31 | });
32 | }
33 | }
34 | }
35 | }]);
--------------------------------------------------------------------------------
/webui/ui/directive/imageBtn/imageBtn.html:
--------------------------------------------------------------------------------
1 |
2 |
8 |
17 |
27 |
--------------------------------------------------------------------------------
/webui/ui/directive/kityminderEditor/kityminderEditor.directive.js:
--------------------------------------------------------------------------------
1 | angular.module('kityminderEditor').directive('kityminderEditor', [
2 | 'config',
3 | 'minder.service',
4 | 'revokeDialog',
5 | function(config, minderService, revokeDialog) {
6 | return {
7 | restrict: 'EA',
8 | templateUrl: 'ui/directive/kityminderEditor/kityminderEditor.html',
9 | replace: true,
10 | scope: {
11 | onInit: '&',
12 | },
13 | link: function(scope, element, attributes) {
14 | var $minderEditor = element.children('.minder-editor')[0];
15 |
16 | function onInit(editor, minder) {
17 | scope.onInit({
18 | editor: editor,
19 | minder: minder,
20 | });
21 |
22 | minderService.executeCallback();
23 | }
24 |
25 | if (typeof seajs != 'undefined') {
26 | /* global seajs */
27 | seajs.config({
28 | base: './src',
29 | });
30 |
31 | define('demo', function(require) {
32 | var Editor = require('editor');
33 |
34 | var editor = (window.editor = new Editor($minderEditor));
35 |
36 | if (window.vscode.getState()['__dev_minder_content']) {
37 | editor.minder.importJson(
38 | JSON.parse(window.vscode.getState()['__dev_minder_content'])
39 | );
40 | }
41 |
42 | editor.minder.on('contentchange', function() {
43 | window.vscode.setState({
44 | __dev_minder_content: JSON.stringify(
45 | editor.minder.exportJson()
46 | ),
47 | });
48 | });
49 |
50 | window.minder = window.km = editor.minder;
51 |
52 | scope.editor = editor;
53 | scope.minder = minder;
54 | scope.config = config.get();
55 |
56 | //scope.minder.setDefaultOptions(scope.config);
57 | scope.$apply();
58 |
59 | onInit(editor, minder);
60 | });
61 |
62 | seajs.use('demo');
63 | } else if (window.kityminder && window.kityminder.Editor) {
64 | var editor = new kityminder.Editor($minderEditor);
65 |
66 | window.editor = scope.editor = editor;
67 | window.minder = scope.minder = editor.minder;
68 |
69 | scope.config = config.get();
70 |
71 | //scope.minder.setDefaultOptions(config.getConfig());
72 |
73 | onInit(editor, editor.minder);
74 | }
75 | },
76 | };
77 | },
78 | ]);
79 |
--------------------------------------------------------------------------------
/webui/ui/directive/kityminderEditor/kityminderEditor.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/webui/ui/directive/kityminderViewer/kityminderViewer.directive.js:
--------------------------------------------------------------------------------
1 | angular.module('kityminderEditor')
2 | .directive('kityminderViewer', ['config', 'minder.service', function(config, minderService) {
3 | return {
4 | restrict: 'EA',
5 | templateUrl: 'ui/directive/kityminderViewer/kityminderViewer.html',
6 | replace: true,
7 | scope: {
8 | onInit: '&'
9 | },
10 | link: function(scope, element, attributes) {
11 |
12 | var $minderEditor = element.children('.minder-viewer')[0];
13 |
14 | function onInit(editor, minder) {
15 | scope.onInit({
16 | editor: editor,
17 | minder: minder
18 | });
19 |
20 | minderService.executeCallback();
21 | }
22 |
23 | if (window.kityminder && window.kityminder.Editor) {
24 | var editor = new kityminder.Editor($minderEditor);
25 |
26 | window.editor = scope.editor = editor;
27 | window.minder = scope.minder = editor.minder;
28 |
29 | onInit(editor, editor.minder);
30 | }
31 |
32 | }
33 | }
34 | }]);
--------------------------------------------------------------------------------
/webui/ui/directive/kityminderViewer/kityminderViewer.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/webui/ui/directive/layout/layout.directive.js:
--------------------------------------------------------------------------------
1 | angular.module('kityminderEditor')
2 | .directive('layout', function() {
3 | return {
4 | restrict: 'E',
5 | templateUrl: 'ui/directive/layout/layout.html',
6 | scope: {
7 | minder: '='
8 | },
9 | replace: true,
10 | link: function(scope) {
11 |
12 | }
13 | }
14 | });
--------------------------------------------------------------------------------
/webui/ui/directive/layout/layout.html:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/webui/ui/directive/navigator/navigator.html:
--------------------------------------------------------------------------------
1 |
2 |
8 |
18 |
24 |
30 |
35 |
41 |
42 |
--------------------------------------------------------------------------------
/webui/ui/directive/noteBtn/noteBtn.directive.js:
--------------------------------------------------------------------------------
1 | angular.module('kityminderEditor')
2 | .directive('noteBtn', ['valueTransfer', function(valueTransfer) {
3 | return {
4 | restrict: 'E',
5 | templateUrl: 'ui/directive/noteBtn/noteBtn.html',
6 | scope: {
7 | minder: '='
8 | },
9 | replace: true,
10 | link: function($scope) {
11 | var minder = $scope.minder;
12 |
13 | $scope.addNote =function() {
14 | valueTransfer.noteEditorOpen = true;
15 | };
16 | }
17 | }
18 | }]);
--------------------------------------------------------------------------------
/webui/ui/directive/noteBtn/noteBtn.html:
--------------------------------------------------------------------------------
1 |
2 |
8 |
17 |
27 |
--------------------------------------------------------------------------------
/webui/ui/directive/noteEditor/noteEditor.directive.js:
--------------------------------------------------------------------------------
1 | angular.module('kityminderEditor')
2 |
3 | .directive('noteEditor', ['valueTransfer', function(valueTransfer) {
4 | return {
5 | restrict: 'A',
6 | templateUrl: 'ui/directive/noteEditor/noteEditor.html',
7 | scope: {
8 | minder: '='
9 | },
10 | replace: true,
11 | controller: function($scope) {
12 | var minder = $scope.minder;
13 | var isInteracting = false;
14 | var cmEditor;
15 |
16 | $scope.codemirrorLoaded = function(_editor) {
17 |
18 | cmEditor = $scope.cmEditor = _editor;
19 |
20 | _editor.setSize('100%', '100%');
21 | };
22 |
23 | function updateNote() {
24 | var enabled = $scope.noteEnabled = minder.queryCommandState('note') != -1;
25 | var noteValue = minder.queryCommandValue('note') || '';
26 |
27 | if (enabled) {
28 | $scope.noteContent = noteValue;
29 | }
30 |
31 | isInteracting = true;
32 | $scope.$apply();
33 | isInteracting = false;
34 | }
35 |
36 |
37 | $scope.$watch('noteContent', function(content) {
38 | var enabled = minder.queryCommandState('note') != -1;
39 |
40 | if (content && enabled && !isInteracting) {
41 | minder.execCommand('note', content);
42 | }
43 |
44 | setTimeout(function() {
45 | cmEditor.refresh();
46 | });
47 | });
48 |
49 |
50 | var noteEditorOpen = function() {
51 | return valueTransfer.noteEditorOpen;
52 | };
53 |
54 | // 监听面板状态变量的改变
55 | $scope.$watch(noteEditorOpen, function(newVal, oldVal) {
56 | if (newVal) {
57 | setTimeout(function() {
58 | cmEditor.refresh();
59 | cmEditor.focus();
60 | });
61 | }
62 | $scope.noteEditorOpen = valueTransfer.noteEditorOpen;
63 | }, true);
64 |
65 |
66 | $scope.closeNoteEditor = function() {
67 | valueTransfer.noteEditorOpen = false;
68 | editor.receiver.selectAll();
69 | };
70 |
71 |
72 |
73 | minder.on('interactchange', updateNote);
74 | }
75 | }
76 | }]);
--------------------------------------------------------------------------------
/webui/ui/directive/noteEditor/noteEditor.html:
--------------------------------------------------------------------------------
1 |
6 |
21 |
22 |
35 |
36 | Select Node to Edit Notes
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/webui/ui/directive/notePreviewer/notePreviewer.directive.js:
--------------------------------------------------------------------------------
1 | // TODO: 使用一个 div 容器作为 previewer,而不是两个
2 | angular.module('kityminderEditor')
3 |
4 | .directive('notePreviewer', ['$sce', 'valueTransfer', function($sce, valueTransfer) {
5 | return {
6 | restrict: 'A',
7 | templateUrl: 'ui/directive/notePreviewer/notePreviewer.html',
8 | link: function(scope, element) {
9 | var minder = scope.minder;
10 | var $container = element.parent();
11 | var $previewer = element.children();
12 | scope.showNotePreviewer = false;
13 |
14 | marked.setOptions({
15 | gfm: true,
16 | tables: true,
17 | breaks: true,
18 | pedantic: false,
19 | sanitize: true,
20 | smartLists: true,
21 | smartypants: false
22 | });
23 |
24 |
25 | var previewTimer;
26 | minder.on('shownoterequest', function(e) {
27 |
28 | previewTimer = setTimeout(function() {
29 | preview(e.node, e.keyword);
30 | }, 300);
31 | });
32 | minder.on('hidenoterequest', function() {
33 | clearTimeout(previewTimer);
34 |
35 | scope.showNotePreviewer = false;
36 | //scope.$apply();
37 | });
38 |
39 | var previewLive = false;
40 | $(document).on('mousedown mousewheel DOMMouseScroll', function() {
41 | if (!previewLive) return;
42 | scope.showNotePreviewer = false;
43 | scope.$apply();
44 | });
45 |
46 | element.on('mousedown mousewheel DOMMouseScroll', function(e) {
47 | e.stopPropagation();
48 | });
49 |
50 | function preview(node, keyword) {
51 | var icon = node.getRenderer('NoteIconRenderer').getRenderShape();
52 | var b = icon.getRenderBox('screen');
53 | var note = node.getData('note');
54 |
55 | $previewer[0].scrollTop = 0;
56 |
57 | var html = marked(note);
58 | if (keyword) {
59 | html = html.replace(new RegExp('(' + keyword + ')', 'ig'), '$1');
60 | }
61 | scope.noteContent = $sce.trustAsHtml(html);
62 | scope.$apply(); // 让浏览器重新渲染以获取 previewer 提示框的尺寸
63 |
64 | var cw = $($container[0]).width();
65 | var ch = $($container[0]).height();
66 | var pw = $($previewer).outerWidth();
67 | var ph = $($previewer).outerHeight();
68 |
69 | var x = b.cx - pw / 2 - $container[0].offsetLeft;
70 | var y = b.bottom + 10 - $container[0].offsetTop;
71 |
72 | if (x < 0) x = 10;
73 | if (x + pw > cw) x = b.left - pw - 10 - $container[0].offsetLeft;
74 | if (y + ph > ch) y = b.top - ph - 10 - $container[0].offsetTop;
75 |
76 |
77 | scope.previewerStyle = {
78 | 'left': Math.round(x) + 'px',
79 | 'top': Math.round(y) + 'px'
80 | };
81 |
82 | scope.showNotePreviewer = true;
83 |
84 | var view = $previewer[0].querySelector('.highlight');
85 | if (view) {
86 | view.scrollIntoView();
87 | }
88 | previewLive = true;
89 |
90 | scope.$apply();
91 | }
92 | }
93 | }
94 | }]);
--------------------------------------------------------------------------------
/webui/ui/directive/notePreviewer/notePreviewer.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/webui/ui/directive/operation/operation.directive.js:
--------------------------------------------------------------------------------
1 | angular.module('kityminderEditor')
2 | .directive('operation', function() {
3 | return {
4 | restrict: 'E',
5 | templateUrl: 'ui/directive/operation/operation.html',
6 | scope: {
7 | minder: '='
8 | },
9 | replace: true,
10 | link: function($scope) {
11 | $scope.editNode = function() {
12 |
13 | var receiverElement = editor.receiver.element;
14 | var fsm = editor.fsm;
15 | var receiver = editor.receiver;
16 |
17 | receiverElement.innerText = minder.queryCommandValue('text');
18 | fsm.jump('input', 'input-request');
19 | receiver.selectAll();
20 |
21 | }
22 |
23 | }
24 | }
25 | });
--------------------------------------------------------------------------------
/webui/ui/directive/operation/operation.html:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 | {{ 'editnode' | lang:'ui/command' }}
8 |
9 |
13 |
14 | {{ 'removenode' | lang:'ui/command' }}
15 |
16 |
17 |
--------------------------------------------------------------------------------
/webui/ui/directive/priorityEditor/priorityEditor.directive.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('kityminderEditor')
3 |
4 | .directive('priorityEditor', [
5 | 'commandBinder',
6 | function(commandBinder) {
7 | return {
8 | restrict: 'E',
9 | templateUrl: 'ui/directive/priorityEditor/priorityEditor.html',
10 | scope: {
11 | minder: '=',
12 | },
13 | replace: true,
14 | link: function($scope) {
15 | var minder = $scope.minder;
16 | var priorities = [];
17 |
18 | for (var i = 0; i < 10; i++) {
19 | priorities.push(i);
20 | }
21 |
22 | commandBinder.bind(minder, 'priority', $scope);
23 |
24 | $scope.priorities = priorities;
25 |
26 | $scope.getPriorityTitle = function(p) {
27 | switch (p) {
28 | case 0:
29 | return 'clear';
30 | default:
31 | return 'priority' + p;
32 | }
33 | };
34 | },
35 | };
36 | },
37 | ]);
38 |
--------------------------------------------------------------------------------
/webui/ui/directive/priorityEditor/priorityEditor.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/webui/ui/directive/progressEditor/progressEditor.directive.js:
--------------------------------------------------------------------------------
1 | angular.module('kityminderEditor').directive('progressEditor', [
2 | 'commandBinder',
3 | function(commandBinder) {
4 | return {
5 | restrict: 'E',
6 | templateUrl: 'ui/directive/progressEditor/progressEditor.html',
7 | scope: {
8 | minder: '=',
9 | },
10 | replace: true,
11 | link: function($scope) {
12 | var minder = $scope.minder;
13 | var progresses = [];
14 |
15 | for (var i = 0; i < 10; i++) {
16 | progresses.push(i);
17 | }
18 |
19 | commandBinder.bind(minder, 'progress', $scope);
20 |
21 | $scope.progresses = progresses;
22 |
23 | $scope.getProgressTitle = function(p) {
24 | switch (p) {
25 | case 0:
26 | return 'clear';
27 | case 1:
28 | return 'undone';
29 | case 9:
30 | return 'done';
31 | default:
32 | return 'done' + (p - 1) + '/8';
33 | }
34 | };
35 | },
36 | };
37 | },
38 | ]);
39 |
--------------------------------------------------------------------------------
/webui/ui/directive/progressEditor/progressEditor.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/webui/ui/directive/resourceEditor/resourceEditor.directive.js:
--------------------------------------------------------------------------------
1 | angular.module('kityminderEditor')
2 | .directive('resourceEditor', function () {
3 | return {
4 | restrict: 'E',
5 | templateUrl: 'ui/directive/resourceEditor/resourceEditor.html',
6 | scope: {
7 | minder: '='
8 | },
9 | replace: true,
10 | controller: function ($scope) {
11 | var minder = $scope.minder;
12 |
13 | var isInteracting = false;
14 |
15 | minder.on('interactchange', function () {
16 | var enabled = $scope.enabled = minder.queryCommandState('resource') != -1;
17 | var selected = enabled ? minder.queryCommandValue('resource') : [];
18 | var used = minder.getUsedResource().map(function (resourceName) {
19 | return {
20 | name: resourceName,
21 | selected: selected.indexOf(resourceName) > -1
22 | }
23 | });
24 | $scope.used = used;
25 |
26 | isInteracting = true;
27 | $scope.$apply();
28 | isInteracting = false;
29 | });
30 |
31 | $scope.$watch('used', function (used) {
32 | if (minder.queryCommandState('resource') != -1 && used) {
33 | var resource = used.filter(function (resource) {
34 | return resource.selected;
35 | }).map(function (resource) {
36 | return resource.name;
37 | });
38 |
39 | // 由于 interactchange 带来的改变则不用执行 resource 命令
40 | if (isInteracting) {
41 | return;
42 | }
43 | minder.execCommand('resource', resource);
44 | }
45 | }, true);
46 |
47 | $scope.resourceColor = function (resource) {
48 | return minder.getResourceColor(resource).toHEX();
49 | };
50 |
51 | $scope.addResource = function (resourceName) {
52 | var origin = minder.queryCommandValue('resource');
53 | if (!resourceName || !/\S/.test(resourceName)) return;
54 |
55 | if (origin.indexOf(resourceName) == -1) {
56 | $scope.used.push({
57 | name: resourceName,
58 | selected: true
59 | });
60 | }
61 |
62 | $scope.newResourceName = null;
63 | };
64 |
65 | }
66 | };
67 | })
68 |
69 | .directive('clickAnywhereButHere', ['$document', function ($document) {
70 | return {
71 | link: function(scope, element, attrs) {
72 | var onClick = function (event) {
73 | var isChild = $('#resource-dropdown').has(event.target).length > 0;
74 | var isSelf = $('#resource-dropdown') == event.target;
75 | var isInside = isChild || isSelf;
76 | if (!isInside) {
77 | scope.$apply(attrs.clickAnywhereButHere)
78 | }
79 | };
80 |
81 | scope.$watch(attrs.isActive, function(newValue, oldValue) {
82 | if (newValue !== oldValue && newValue == true) {
83 | $document.bind('click', onClick);
84 | }
85 | else if (newValue !== oldValue && newValue == false) {
86 | $document.unbind('click', onClick);
87 | }
88 | });
89 | }
90 | };
91 | }]);
--------------------------------------------------------------------------------
/webui/ui/directive/resourceEditor/resourceEditor.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
12 |
19 |
20 |
21 |
51 |
52 |
--------------------------------------------------------------------------------
/webui/ui/directive/searchBox/searchBox.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
12 |
13 |
14 |
17 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/webui/ui/directive/searchBtn/searchBtn.directive.js:
--------------------------------------------------------------------------------
1 | angular.module('kityminderEditor')
2 | .directive('searchBtn', function() {
3 | return {
4 | restrict: 'E',
5 | templateUrl: 'ui/directive/searchBtn/searchBtn.html',
6 | scope: {
7 | minder: '='
8 | },
9 | replace: true,
10 | link: function (scope) {
11 | scope.enterSearch = enterSearch;
12 |
13 | function enterSearch() {
14 | minder.fire('searchNode');
15 | }
16 | }
17 | }
18 | });
--------------------------------------------------------------------------------
/webui/ui/directive/searchBtn/searchBtn.html:
--------------------------------------------------------------------------------
1 |
2 |
7 |
14 |
--------------------------------------------------------------------------------
/webui/ui/directive/selectAll/selectAll.directive.js:
--------------------------------------------------------------------------------
1 | angular.module('kityminderEditor')
2 | .directive('selectAll', function() {
3 | return {
4 | restrict: 'E',
5 | templateUrl: 'ui/directive/selectAll/selectAll.html',
6 | scope: {
7 | minder: '='
8 | },
9 | replace: true,
10 | link: function($scope) {
11 | var minder = $scope.minder;
12 |
13 | $scope.items = ['revert', 'siblings', 'level', 'path', 'tree'];
14 |
15 | $scope.select = {
16 | all: function() {
17 | var selection = [];
18 | minder.getRoot().traverse(function(node) {
19 | selection.push(node);
20 | });
21 | minder.select(selection, true);
22 | minder.fire('receiverfocus');
23 | },
24 | revert: function() {
25 | var selected = minder.getSelectedNodes();
26 | var selection = [];
27 | minder.getRoot().traverse(function(node) {
28 | if (selected.indexOf(node) == -1) {
29 | selection.push(node);
30 | }
31 | });
32 | minder.select(selection, true);
33 | minder.fire('receiverfocus');
34 | },
35 | siblings: function() {
36 | var selected = minder.getSelectedNodes();
37 | var selection = [];
38 | selected.forEach(function(node) {
39 | if (!node.parent) return;
40 | node.parent.children.forEach(function(sibling) {
41 | if (selection.indexOf(sibling) == -1) selection.push(sibling);
42 | });
43 | });
44 | minder.select(selection, true);
45 | minder.fire('receiverfocus');
46 | },
47 | level: function() {
48 | var selectedLevel = minder.getSelectedNodes().map(function(node) {
49 | return node.getLevel();
50 | });
51 | var selection = [];
52 | minder.getRoot().traverse(function(node) {
53 | if (selectedLevel.indexOf(node.getLevel()) != -1) {
54 | selection.push(node);
55 | }
56 | });
57 | minder.select(selection, true);
58 | minder.fire('receiverfocus');
59 | },
60 | path: function() {
61 | var selected = minder.getSelectedNodes();
62 | var selection = [];
63 | selected.forEach(function(node) {
64 | while(node && selection.indexOf(node) == -1) {
65 | selection.push(node);
66 | node = node.parent;
67 | }
68 | });
69 | minder.select(selection, true);
70 | minder.fire('receiverfocus');
71 | },
72 | tree: function() {
73 | var selected = minder.getSelectedNodes();
74 | var selection = [];
75 | selected.forEach(function(parent) {
76 | parent.traverse(function(node) {
77 | if (selection.indexOf(node) == -1) selection.push(node);
78 | });
79 | });
80 | minder.select(selection, true);
81 | minder.fire('receiverfocus');
82 | }
83 | };
84 | }
85 | }
86 | });
--------------------------------------------------------------------------------
/webui/ui/directive/selectAll/selectAll.html:
--------------------------------------------------------------------------------
1 |
2 |
7 |
15 |
21 |
--------------------------------------------------------------------------------
/webui/ui/directive/styleOperator/styleOperator.directive.js:
--------------------------------------------------------------------------------
1 | angular.module('kityminderEditor')
2 | .directive('styleOperator', function() {
3 | return {
4 | restrict: 'E',
5 | templateUrl: 'ui/directive/styleOperator/styleOperator.html',
6 | scope: {
7 | minder: '='
8 | },
9 | replace: true
10 | }
11 | });
--------------------------------------------------------------------------------
/webui/ui/directive/styleOperator/styleOperator.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/webui/ui/directive/templateList/templateList.directive.js:
--------------------------------------------------------------------------------
1 | angular.module('kityminderEditor')
2 | .directive('templateList', function() {
3 | return {
4 | restrict: 'E',
5 | templateUrl: 'ui/directive/templateList/templateList.html',
6 | scope: {
7 | minder: '='
8 | },
9 | replace: true,
10 | link: function($scope) {
11 | $scope.templateList = kityminder.Minder.getTemplateList();
12 |
13 | }
14 | }
15 | });
--------------------------------------------------------------------------------
/webui/ui/directive/templateList/templateList.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/webui/ui/directive/themeList/themeList.directive.js:
--------------------------------------------------------------------------------
1 | angular.module('kityminderEditor')
2 | .directive('themeList', function() {
3 | return {
4 | restrict: 'E',
5 | templateUrl: 'ui/directive/themeList/themeList.html',
6 | replace: true,
7 | link: function($scope) {
8 | var themeList = kityminder.Minder.getThemeList();
9 |
10 | //$scope.themeList = themeList;
11 |
12 | $scope.getThemeThumbStyle = function (theme) {
13 | var themeObj = themeList[theme];
14 | if (!themeObj) {
15 | return;
16 | }
17 | var style = {
18 | 'color': themeObj['root-color'],
19 | 'border-radius': themeObj['root-radius'] / 2
20 | };
21 |
22 | if (themeObj['root-background']) {
23 | style['background'] = themeObj['root-background'].toString();
24 | }
25 |
26 | return style;
27 | };
28 |
29 | // 维护 theme key 列表以保证列表美观(不按字母顺序排序)
30 | $scope.themeKeyList = [
31 | 'classic',
32 | 'classic-compact',
33 | 'fresh-blue',
34 | 'fresh-blue-compat',
35 | 'fresh-green',
36 | 'fresh-green-compat',
37 | 'fresh-pink',
38 | 'fresh-pink-compat',
39 | 'fresh-purple',
40 | 'fresh-purple-compat',
41 | 'fresh-red',
42 | 'fresh-red-compat',
43 | 'fresh-soil',
44 | 'fresh-soil-compat',
45 | 'snow',
46 | 'snow-compact',
47 | 'tianpan',
48 | 'tianpan-compact',
49 | 'fish',
50 | 'wire'
51 | ];
52 | }
53 | }
54 | });
--------------------------------------------------------------------------------
/webui/ui/directive/themeList/themeList.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/webui/ui/directive/topTab/topTab.directive.js:
--------------------------------------------------------------------------------
1 | angular.module('kityminderEditor')
2 | .directive('topTab', function() {
3 | return {
4 | restrict: 'A',
5 | templateUrl: 'ui/directive/topTab/topTab.html',
6 | scope: {
7 | minder: '=topTab',
8 | editor: '='
9 | },
10 | link: function(scope) {
11 |
12 | /*
13 | *
14 | * 用户选择一个新的选项卡会执行 setCurTab 和 foldTopTab 两个函数
15 | * 用户点击原来的选项卡会执行 foldTopTop 一个函数
16 | *
17 | * 也就是每次选择新的选项卡都会执行 setCurTab,初始化的时候也会执行 setCurTab 函数
18 | * 因此用 executedCurTab 记录是否已经执行了 setCurTab 函数
19 | * 用 isInit 记录是否是初始化的状态,在任意一个函数时候 isInit 设置为 false
20 | * 用 isOpen 记录是否打开了 topTab
21 | *
22 | * 因此用到了三个 mutex
23 | * */
24 | var executedCurTab = false;
25 | var isInit = true;
26 | var isOpen = true;
27 |
28 | scope.setCurTab = function(tabName) {
29 | setTimeout(function() {
30 | //console.log('set cur tab to : ' + tabName);
31 | executedCurTab = true;
32 | //isOpen = false;
33 | if (tabName != 'idea') {
34 | isInit = false;
35 | }
36 | });
37 | };
38 |
39 | scope.toggleTopTab = function() {
40 | setTimeout(function() {
41 | if(!executedCurTab || isInit) {
42 | isInit = false;
43 |
44 | isOpen ? closeTopTab(): openTopTab();
45 | isOpen = !isOpen;
46 | }
47 |
48 | executedCurTab = false;
49 | });
50 | };
51 |
52 | function closeTopTab() {
53 | var $tabContent = $('.tab-content');
54 | var $minderEditor = $('.minder-editor');
55 |
56 | $tabContent.animate({
57 | height: 0,
58 | display: 'none'
59 | });
60 |
61 | $minderEditor.animate({
62 | top: '32px'
63 | });
64 | }
65 |
66 | function openTopTab() {
67 | var $tabContent = $('.tab-content');
68 | var $minderEditor = $('.minder-editor');
69 |
70 | $tabContent.animate({
71 | height: '60px',
72 | display: 'block'
73 | });
74 |
75 | $minderEditor.animate({
76 | top: '92px'
77 | });
78 | }
79 | }
80 | }
81 | });
--------------------------------------------------------------------------------
/webui/ui/directive/topTab/topTab.html:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/webui/ui/directive/undoRedo/undoRedo.directive.js:
--------------------------------------------------------------------------------
1 | angular.module('kityminderEditor')
2 | .directive('undoRedo', function() {
3 | return {
4 | restrict: 'E',
5 | templateUrl: 'ui/directive/undoRedo/undoRedo.html',
6 | scope: {
7 | editor: '='
8 | },
9 | replace: true,
10 | link: function($scope) {
11 |
12 | }
13 | }
14 | });
--------------------------------------------------------------------------------
/webui/ui/directive/undoRedo/undoRedo.html:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/webui/ui/filter/command.filters.js:
--------------------------------------------------------------------------------
1 | angular.module('kityminderEditor')
2 | .filter('commandState', function() {
3 | return function(minder, command) {
4 | return minder.queryCommandState(command);
5 | }
6 | })
7 | .filter('commandValue', function() {
8 | return function(minder, command) {
9 | return minder.queryCommandValue(command);
10 | }
11 | });
12 |
13 |
--------------------------------------------------------------------------------
/webui/ui/filter/lang.filter.js:
--------------------------------------------------------------------------------
1 | angular.module('kityminderEditor').filter('lang', [
2 | 'config',
3 | 'lang.en',
4 | function(config, lang) {
5 | return function(text, block) {
6 | var defaultLang = config.get('defaultLang');
7 |
8 | if (lang[defaultLang] == undefined) {
9 | return '未发现对应语言包,请检查 lang.xxx.service.js!';
10 | } else {
11 | var dict = lang[defaultLang];
12 | block.split('/').forEach(function(ele, idx) {
13 | dict = dict[ele];
14 | });
15 |
16 | return dict[text] || null;
17 | }
18 | };
19 | },
20 | ]);
21 |
--------------------------------------------------------------------------------
/webui/ui/images/iconpriority.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/souche/vscode-mindmap/94b0b5a572cd7bf9971a52a82e0aa9ffb1354725/webui/ui/images/iconpriority.png
--------------------------------------------------------------------------------
/webui/ui/images/iconprogress.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/souche/vscode-mindmap/94b0b5a572cd7bf9971a52a82e0aa9ffb1354725/webui/ui/images/iconprogress.png
--------------------------------------------------------------------------------
/webui/ui/images/icons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/souche/vscode-mindmap/94b0b5a572cd7bf9971a52a82e0aa9ffb1354725/webui/ui/images/icons.png
--------------------------------------------------------------------------------
/webui/ui/images/template.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/souche/vscode-mindmap/94b0b5a572cd7bf9971a52a82e0aa9ffb1354725/webui/ui/images/template.png
--------------------------------------------------------------------------------
/webui/ui/kityminder.app.js:
--------------------------------------------------------------------------------
1 | angular.module('kityminderEditor', [
2 | 'ui.bootstrap',
3 | 'ui.codemirror',
4 | 'ui.colorpicker'
5 | ])
6 | .config(function($sceDelegateProvider) {
7 | $sceDelegateProvider.resourceUrlWhitelist([
8 | // Allow same origin resource loads.
9 | 'self',
10 | // Allow loading from our assets domain. Notice the difference between * and **.
11 | 'http://agroup.baidu.com:8910/**',
12 | 'http://cq01-fe-rdtest01.vm.baidu.com:8910/**',
13 | 'http://agroup.baidu.com:8911/**'
14 | ]);
15 | });
--------------------------------------------------------------------------------
/webui/ui/service/commandBinder.service.js:
--------------------------------------------------------------------------------
1 | angular.module('kityminderEditor').service('commandBinder', function() {
2 | return {
3 | bind: function(minder, command, scope) {
4 |
5 | minder.on('interactchange', function() {
6 | scope.commandDisabled = minder.queryCommandState(command) === -1;
7 | scope.commandValue = minder.queryCommandValue(command);
8 | scope.$apply();
9 | });
10 | }
11 | };
12 | });
--------------------------------------------------------------------------------
/webui/ui/service/config.service.js:
--------------------------------------------------------------------------------
1 | angular.module('kityminderEditor').provider('config', function() {
2 | this.config = {
3 | // 右侧面板最小宽度
4 | ctrlPanelMin: 250,
5 |
6 | // 右侧面板宽度
7 | ctrlPanelWidth: 250,
8 |
9 | // 分割线宽度
10 | dividerWidth: 3,
11 |
12 | // 默认语言
13 | defaultLang: 'en',
14 |
15 | // 放大缩小比例
16 | zoom: [10, 20, 30, 50, 80, 100, 120, 150, 200],
17 |
18 | // 图片上传接口
19 | imageUpload: 'server/imageUpload.php',
20 | };
21 |
22 | this.set = function(key, value) {
23 | var supported = Object.keys(this.config);
24 | var configObj = {};
25 |
26 | // 支持全配置
27 | if (typeof key === 'object') {
28 | configObj = key;
29 | } else {
30 | configObj[key] = value;
31 | }
32 |
33 | for (var i in configObj) {
34 | if (configObj.hasOwnProperty(i) && supported.indexOf(i) !== -1) {
35 | this.config[i] = configObj[i];
36 | } else {
37 | console.error(
38 | 'Unsupported config key: ',
39 | key,
40 | ', please choose in :',
41 | supported.join(', ')
42 | );
43 | return false;
44 | }
45 | }
46 |
47 | return true;
48 | };
49 |
50 | this.$get = function() {
51 | var me = this;
52 |
53 | return {
54 | get: function(key) {
55 | if (arguments.length === 0) {
56 | return me.config;
57 | }
58 |
59 | if (me.config.hasOwnProperty(key)) {
60 | return me.config[key];
61 | }
62 |
63 | console.warn('Missing config key pair for : ', key);
64 | return '';
65 | },
66 | };
67 | };
68 | });
69 |
--------------------------------------------------------------------------------
/webui/ui/service/memory.service.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileOverview
3 | *
4 | * UI 状态的 LocalStorage 的存取文件,未来可能在离线编辑的时候升级
5 | *
6 | * @author: zhangbobell
7 | * @email : zhangbobell@163.com
8 | *
9 | * @copyright: Baidu FEX, 2015
10 | */
11 | angular.module('kityminderEditor').service('memory', function() {
12 | function isQuotaExceeded(e) {
13 | var quotaExceeded = false;
14 | if (e) {
15 | if (e.code) {
16 | switch (e.code) {
17 | case 22:
18 | quotaExceeded = true;
19 | break;
20 | case 1014:
21 | // Firefox
22 | if (e.name === 'NS_ERROR_DOM_QUOTA_REACHED') {
23 | quotaExceeded = true;
24 | }
25 | break;
26 | }
27 | } else if (e.number === -2147024882) {
28 | // Internet Explorer 8
29 | quotaExceeded = true;
30 | }
31 | }
32 | return quotaExceeded;
33 | }
34 |
35 | return {
36 | get: function(key) {
37 | var value = window.vscode.getState()[key];
38 | return null || JSON.parse(value);
39 | },
40 |
41 | set: function(key, value) {
42 | try {
43 | // window.vscode.setState({[key]: JSON.stringify(value) });
44 | return true;
45 | } catch (e) {
46 | if (isQuotaExceeded(e)) {
47 | return false;
48 | }
49 | }
50 | },
51 | remove: function(key) {
52 | var value = window.vscode.getState()[key];
53 | // window.vscode.setState({
54 | // [key]: null,
55 | // });
56 | return value;
57 | },
58 | clear: function() {
59 | // var prevState = window.vscode.getState();
60 | // for (var key in prevState) {
61 | // window.vscode.setState({
62 | // [key]: null,
63 | // });
64 | // }
65 | },
66 | };
67 | });
68 |
--------------------------------------------------------------------------------
/webui/ui/service/minder.service.js:
--------------------------------------------------------------------------------
1 | angular.module('kityminderEditor')
2 | .service('minder.service', function() {
3 |
4 | var callbackQueue = [];
5 |
6 | function registerEvent(callback) {
7 | callbackQueue.push(callback);
8 | }
9 |
10 | function executeCallback() {
11 | callbackQueue.forEach(function(ele) {
12 | ele.apply(this, arguments);
13 | })
14 | }
15 |
16 | return {
17 | registerEvent: registerEvent,
18 | executeCallback: executeCallback
19 | }
20 | });
--------------------------------------------------------------------------------
/webui/ui/service/resource.service.js:
--------------------------------------------------------------------------------
1 | angular.module('kityminderEditor')
2 | .service('resourceService', ['$document', function($document) {
3 | var openScope = null;
4 |
5 | this.open = function( dropdownScope ) {
6 | if ( !openScope ) {
7 | $document.bind('click', closeDropdown);
8 | $document.bind('keydown', escapeKeyBind);
9 | }
10 |
11 | if ( openScope && openScope !== dropdownScope ) {
12 | openScope.resourceListOpen = false;
13 | }
14 |
15 | openScope = dropdownScope;
16 | };
17 |
18 | this.close = function( dropdownScope ) {
19 | if ( openScope === dropdownScope ) {
20 | openScope = null;
21 | $document.unbind('click', closeDropdown);
22 | $document.unbind('keydown', escapeKeyBind);
23 | }
24 | };
25 |
26 | var closeDropdown = function( evt ) {
27 | // This method may still be called during the same mouse event that
28 | // unbound this event handler. So check openScope before proceeding.
29 | //console.log(evt, openScope);
30 | if (!openScope) { return; }
31 |
32 | var toggleElement = openScope.getToggleElement();
33 | if ( evt && toggleElement && toggleElement[0].contains(evt.target) ) {
34 | return;
35 | }
36 |
37 | openScope.$apply(function() {
38 | console.log('to close the resourcelist');
39 | openScope.resourceListOpen = false;
40 | });
41 | };
42 |
43 | var escapeKeyBind = function( evt ) {
44 | if ( evt.which === 27 ) {
45 | openScope.focusToggleElement();
46 | closeDropdown();
47 | }
48 | };
49 | }])
--------------------------------------------------------------------------------
/webui/ui/service/revokeDialog.service.js:
--------------------------------------------------------------------------------
1 | angular.module('kityminderEditor').service('revokeDialog', ['$modal', 'minder.service', function($modal, minderService) {
2 |
3 | minderService.registerEvent(function() {
4 |
5 | // 触发导入节点或导出节点对话框
6 | var minder = window.minder;
7 | var editor = window.editor;
8 | var parentFSM = editor.hotbox.getParentFSM();
9 |
10 |
11 | minder.on('importNodeData', function() {
12 | parentFSM.jump('modal', 'import-text-modal');
13 |
14 | var importModal = $modal.open({
15 | animation: true,
16 | templateUrl: 'ui/dialog/imExportNode/imExportNode.tpl.html',
17 | controller: 'imExportNode.ctrl',
18 | size: 'md',
19 | resolve: {
20 | title: function() {
21 | return '导入节点';
22 | },
23 | defaultValue: function() {
24 | return '';
25 | },
26 | type: function() {
27 | return 'import';
28 | }
29 | }
30 | });
31 |
32 | importModal.result.then(function(result) {
33 | try{
34 | minder.Text2Children(minder.getSelectedNode(), result);
35 | } catch(e) {
36 | alert(e);
37 | }
38 | parentFSM.jump('normal', 'import-text-finish');
39 | editor.receiver.selectAll();
40 | }, function() {
41 | parentFSM.jump('normal', 'import-text-finish');
42 | editor.receiver.selectAll();
43 | });
44 | });
45 |
46 | minder.on('exportNodeData', function() {
47 | parentFSM.jump('modal', 'export-text-modal');
48 |
49 | var exportModal = $modal.open({
50 | animation: true,
51 | templateUrl: 'ui/dialog/imExportNode/imExportNode.tpl.html',
52 | controller: 'imExportNode.ctrl',
53 | size: 'md',
54 | resolve: {
55 | title: function() {
56 | return '导出节点';
57 | },
58 | defaultValue: function() {
59 | var selectedNode = minder.getSelectedNode(),
60 | Node2Text = window.kityminder.data.getRegisterProtocol('text').Node2Text;
61 |
62 | return Node2Text(selectedNode);
63 | },
64 | type: function() {
65 | return 'export';
66 | }
67 | }
68 | });
69 |
70 | exportModal.result.then(function(result) {
71 | parentFSM.jump('normal', 'export-text-finish');
72 | editor.receiver.selectAll();
73 | }, function() {
74 | parentFSM.jump('normal', 'export-text-finish');
75 | editor.receiver.selectAll();
76 | });
77 | });
78 |
79 | });
80 |
81 | return {};
82 | }]);
--------------------------------------------------------------------------------
/webui/ui/service/server.service.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileOverview
3 | *
4 | * 与后端交互的服务
5 | *
6 | * @author: zhangbobell
7 | * @email : zhangbobell@163.com
8 | *
9 | * @copyright: Baidu FEX, 2015
10 | */
11 | angular.module('kityminderEditor')
12 | .service('server', ['config', '$http', function(config, $http) {
13 |
14 | return {
15 | uploadImage: function(file) {
16 | var url = config.get('imageUpload');
17 | var fd = new FormData();
18 | fd.append('upload_file', file);
19 |
20 | return $http.post(url, fd, {
21 | transformRequest: angular.identity,
22 | headers: {'Content-Type': undefined}
23 | });
24 | }
25 | }
26 | }]);
--------------------------------------------------------------------------------
/webui/ui/service/valueTransfer.service.js:
--------------------------------------------------------------------------------
1 | angular.module('kityminderEditor')
2 | .service('valueTransfer', function() {
3 | return {};
4 | });
--------------------------------------------------------------------------------