├── .babelrc ├── .editorconfig ├── .gitattributes ├── .gitignore ├── .vscode └── settings.json ├── README.md ├── README.zh-CN.md ├── example.png ├── index.html ├── package.json ├── src ├── App.vue ├── assets │ └── logo.png ├── components │ └── JsMind │ │ ├── JsMind.vue │ │ ├── index.js │ │ ├── jsmind.css │ │ ├── jsmind.draggable.js │ │ ├── jsmind.js │ │ └── jsmind.screenshot.js └── main.js ├── webpack.config.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "env", 5 | { 6 | "modules": false 7 | } 8 | ] 9 | ] 10 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.js linguist-language=vue 2 | *.css linguist-language=vue 3 | *.html linguist-language=vue -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | dist/ 4 | npm-debug.log 5 | yarn-error.log 6 | 7 | # Editor directories and files 8 | .idea 9 | *.suo 10 | *.ntvs* 11 | *.njsproj 12 | *.sln 13 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-jsmind 2 | 3 | > A Vue.js Component ( base on [jsmind](https://github.com/hizzgdev/jsmind ) ) 4 | 5 | English | [简体中文](README.zh-CN.md) 6 | # Install 7 | ```bash 8 | yarn add vue-jsmind #or npm install vue-jsmind 9 | ``` 10 | 11 | # Quick Start 12 | ```js 13 | import Vue from 'vue' 14 | import jm from 'vue-jsmind' 15 | 16 | Vue.use(jm) 17 | ``` 18 | 19 | # Use 20 | ```html 21 | 22 | ``` 23 | 24 | ## Props 25 | - [values](https://github.com/hizzgdev/jsmind/blob/master/docs/en/1.usage.md#12-data-format) 26 | - [options](https://github.com/hizzgdev/jsmind/blob/master/docs/zh/2.options.md) 27 | - [ref](https://github.com/hizzgdev/jsmind/blob/master/docs/zh/3.operation.md#jsmind-%E5%AF%B9%E8%B1%A1) is mounted [jsmind API](https://github.com/hizzgdev/jsmind/blob/master/docs/zh/3.operation.md#jsmind-%E5%AF%B9%E8%B1%A1) 28 | 29 | 30 | # Use Example 31 | ```bash 32 | git clone git@github.com:chentoday/vue-jsmind.git 33 | yarn #or npm install 34 | yarn run dev #or npm run dev 35 | ``` 36 | ## Result 37 | ![example](example.png) 38 | -------------------------------------------------------------------------------- /README.zh-CN.md: -------------------------------------------------------------------------------- 1 | # vue-jsmind 2 | 3 | > 一个 Vue 组件 ( 基于 [jsmind](https://github.com/hizzgdev/jsmind ) ) 4 | 5 | [English](README.md) | 简体中文 6 | # 安装 7 | ```bash 8 | yarn add vue-jsmind #or npm install vue-jsmind 9 | ``` 10 | 11 | # 快速开始 12 | ```js 13 | import Vue from 'vue' 14 | import jm from 'vue-jsmind' 15 | 16 | Vue.use(jm) 17 | ``` 18 | 19 | # 使用 20 | ```html 21 | 22 | ``` 23 | 24 | ## 参数 25 | - [values](https://github.com/hizzgdev/jsmind/blob/master/docs/en/1.usage.md#12-data-format) 26 | - [options](https://github.com/hizzgdev/jsmind/blob/master/docs/zh/2.options.md) 27 | - [ref](https://github.com/hizzgdev/jsmind/blob/master/docs/zh/3.operation.md#jsmind-%E5%AF%B9%E8%B1%A1) is mounted [jsmind API](https://github.com/hizzgdev/jsmind/blob/master/docs/zh/3.operation.md#jsmind-%E5%AF%B9%E8%B1%A1) 28 | 29 | 30 | # 使用例子 31 | ```bash 32 | git clone git@github.com:chentoday/vue-jsmind.git 33 | yarn #or npm install 34 | yarn run dev #or npm run dev 35 | ``` 36 | ## 结果 37 | ![example](example.png) 38 | -------------------------------------------------------------------------------- /example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chentoday/vue-jsmind/e663180081ad60c1bbd920e24dea867f4f68bf5c/example.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | vue-jsmind 6 | 7 | 8 |
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-jsmind", 3 | "description": "A Vue.js project", 4 | "version": "1.5.0", 5 | "author": "chentoday <906106844@qq.com>", 6 | "license": "MIT", 7 | "private": false, 8 | "main": "dist/build", 9 | "scripts": { 10 | "dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot", 11 | "build": "cross-env NODE_ENV=production webpack --progress --hide-modules" 12 | }, 13 | "dependencies": { 14 | "vue": "^2.5.11" 15 | }, 16 | "browserslist": [ 17 | "> 1%", 18 | "last 2 versions", 19 | "not ie <= 8" 20 | ], 21 | "devDependencies": { 22 | "babel-core": "^6.26.0", 23 | "babel-loader": "^7.1.2", 24 | "babel-plugin-component": "^1.1.1", 25 | "babel-preset-env": "^1.6.0", 26 | "babel-preset-stage-3": "^6.24.1", 27 | "cross-env": "^5.0.5", 28 | "css-loader": "^0.28.7", 29 | "file-loader": "^6.0.0", 30 | "style-loader": "^1.1.3", 31 | "url-loader": "^4.0.0", 32 | "vue-loader": "^13.0.5", 33 | "vue-template-compiler": "^2.4.4", 34 | "webpack": "^3.6.0", 35 | "webpack-cli": "^3.3.11", 36 | "webpack-dev-server": "2.7.1" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 2 | 59 | 60 | -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chentoday/vue-jsmind/e663180081ad60c1bbd920e24dea867f4f68bf5c/src/assets/logo.png -------------------------------------------------------------------------------- /src/components/JsMind/JsMind.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 104 | 109 | -------------------------------------------------------------------------------- /src/components/JsMind/index.js: -------------------------------------------------------------------------------- 1 | import jsMind from './JsMind.vue' 2 | jsMind.install = Vue => { 3 | Vue.component(jsMind.name, jsMind) 4 | } 5 | 6 | if (typeof window !== 'undefined' && window.Vue) { 7 | window.Vue.component(jsMind.name, jsMind) 8 | } 9 | 10 | export default jsMind; 11 | -------------------------------------------------------------------------------- /src/components/JsMind/jsmind.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Released under BSD License 3 | * Copyright (c) 2014-2015 hizzgdev@163.com 4 | * 5 | * Project Home: 6 | * https://github.com/hizzgdev/jsmind/ 7 | */ 8 | 9 | /* important section */ 10 | .jsmind-inner{position:relative;overflow:auto;width:100%;height:100%;}/*box-shadow:0 0 2px #000;*/ 11 | .jsmind-inner{ 12 | moz-user-select:-moz-none; 13 | -moz-user-select:none; 14 | -o-user-select:none; 15 | -khtml-user-select:none; 16 | -webkit-user-select:none; 17 | -ms-user-select:none; 18 | user-select:none; 19 | } 20 | 21 | /* z-index:1 */ 22 | canvas{position:absolute;z-index:1;} 23 | 24 | /* z-index:2 */ 25 | jmnodes{position:absolute;z-index:2;background-color:rgba(0,0,0,0);}/*background color is necessary*/ 26 | jmnode{position:absolute;cursor:default;max-width:400px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;} 27 | jmexpander{position:absolute;width:11px;height:11px;display:block;overflow:hidden;line-height:12px;font-size:12px;text-align:center;border-radius:6px;border-width:1px;border-style:solid;cursor:pointer;} 28 | 29 | /* default theme */ 30 | jmnode{padding:10px;background-color:#fff;color:#333;border-radius:5px;box-shadow:1px 1px 1px #666;font:16px/1.125 Verdana,Arial,Helvetica,sans-serif;} 31 | jmnode:hover{box-shadow:2px 2px 8px #000;background-color:#ebebeb;color:#333;} 32 | jmnode.selected{background-color:#11f;color:#fff;box-shadow:2px 2px 8px #000;} 33 | jmnode.root{font-size:24px;} 34 | jmexpander{border-color:gray;} 35 | jmexpander:hover{border-color:#000;} 36 | 37 | @media screen and (max-device-width: 1024px) { 38 | jmnode{padding:5px;border-radius:3px;font-size:14px;} 39 | jmnode.root{font-size:21px;} 40 | } 41 | /* primary theme */ 42 | jmnodes.theme-primary jmnode{background-color:#428bca;color:#fff;border-color:#357ebd;} 43 | jmnodes.theme-primary jmnode:hover{background-color:#3276b1;border-color:#285e8e;} 44 | jmnodes.theme-primary jmnode.selected{background-color:#f1c40f;color:#fff;} 45 | jmnodes.theme-primary jmnode.root{} 46 | jmnodes.theme-primary jmexpander{} 47 | jmnodes.theme-primary jmexpander:hover{} 48 | 49 | /* warning theme */ 50 | jmnodes.theme-warning jmnode{background-color:#f0ad4e;border-color:#eea236;color:#fff;} 51 | jmnodes.theme-warning jmnode:hover{background-color:#ed9c28;border-color:#d58512;} 52 | jmnodes.theme-warning jmnode.selected{background-color:#11f;color:#fff;} 53 | jmnodes.theme-warning jmnode.root{} 54 | jmnodes.theme-warning jmexpander{} 55 | jmnodes.theme-warning jmexpander:hover{} 56 | 57 | /* danger theme */ 58 | jmnodes.theme-danger jmnode{background-color:#d9534f;border-color:#d43f3a;color:#fff;} 59 | jmnodes.theme-danger jmnode:hover{background-color:#d2322d;border-color:#ac2925;} 60 | jmnodes.theme-danger jmnode.selected{background-color:#11f;color:#fff;} 61 | jmnodes.theme-danger jmnode.root{} 62 | jmnodes.theme-danger jmexpander{} 63 | jmnodes.theme-danger jmexpander:hover{} 64 | 65 | /* success theme */ 66 | jmnodes.theme-success jmnode{background-color:#5cb85c;border-color:#4cae4c;color:#fff;} 67 | jmnodes.theme-success jmnode:hover{background-color:#47a447;border-color:#398439;} 68 | jmnodes.theme-success jmnode.selected{background-color:#11f;color:#fff;} 69 | jmnodes.theme-success jmnode.root{} 70 | jmnodes.theme-success jmexpander{} 71 | jmnodes.theme-success jmexpander:hover{} 72 | 73 | /* info theme */ 74 | jmnodes.theme-info jmnode{background-color:#5dc0de;border-color:#46b8da;;color:#fff;} 75 | jmnodes.theme-info jmnode:hover{background-color:#39b3d7;border-color:#269abc;} 76 | jmnodes.theme-info jmnode.selected{background-color:#11f;color:#fff;} 77 | jmnodes.theme-info jmnode.root{} 78 | jmnodes.theme-info jmexpander{} 79 | jmnodes.theme-info jmexpander:hover{} 80 | 81 | /* greensea theme */ 82 | jmnodes.theme-greensea jmnode{background-color:#1abc9c;color:#fff;} 83 | jmnodes.theme-greensea jmnode:hover{background-color:#16a085;} 84 | jmnodes.theme-greensea jmnode.selected{background-color:#11f;color:#fff;} 85 | jmnodes.theme-greensea jmnode.root{} 86 | jmnodes.theme-greensea jmexpander{} 87 | jmnodes.theme-greensea jmexpander:hover{} 88 | 89 | /* nephrite theme */ 90 | jmnodes.theme-nephrite jmnode{background-color:#2ecc71;color:#fff;} 91 | jmnodes.theme-nephrite jmnode:hover{background-color:#27ae60;} 92 | jmnodes.theme-nephrite jmnode.selected{background-color:#11f;color:#fff;} 93 | jmnodes.theme-nephrite jmnode.root{} 94 | jmnodes.theme-nephrite jmexpander{} 95 | jmnodes.theme-nephrite jmexpander:hover{} 96 | 97 | /* belizehole theme */ 98 | jmnodes.theme-belizehole jmnode{background-color:#3498db;color:#fff;} 99 | jmnodes.theme-belizehole jmnode:hover{background-color:#2980b9;} 100 | jmnodes.theme-belizehole jmnode.selected{background-color:#11f;color:#fff;} 101 | jmnodes.theme-belizehole jmnode.root{} 102 | jmnodes.theme-belizehole jmexpander{} 103 | jmnodes.theme-belizehole jmexpander:hover{} 104 | 105 | /* wisteria theme */ 106 | jmnodes.theme-wisteria jmnode{background-color:#9b59b6;color:#fff;} 107 | jmnodes.theme-wisteria jmnode:hover{background-color:#8e44ad;} 108 | jmnodes.theme-wisteria jmnode.selected{background-color:#11f;color:#fff;} 109 | jmnodes.theme-wisteria jmnode.root{} 110 | jmnodes.theme-wisteria jmexpander{} 111 | jmnodes.theme-wisteria jmexpander:hover{} 112 | 113 | /* asphalt theme */ 114 | jmnodes.theme-asphalt jmnode{background-color:#34495e;color:#fff;} 115 | jmnodes.theme-asphalt jmnode:hover{background-color:#2c3e50;} 116 | jmnodes.theme-asphalt jmnode.selected{background-color:#11f;color:#fff;} 117 | jmnodes.theme-asphalt jmnode.root{} 118 | jmnodes.theme-asphalt jmexpander{} 119 | jmnodes.theme-asphalt jmexpander:hover{} 120 | 121 | /* orange theme */ 122 | jmnodes.theme-orange jmnode{background-color:#f1c40f;color:#fff;} 123 | jmnodes.theme-orange jmnode:hover{background-color:#f39c12;} 124 | jmnodes.theme-orange jmnode.selected{background-color:#11f;color:#fff;} 125 | jmnodes.theme-orange jmnode.root{} 126 | jmnodes.theme-orange jmexpander{} 127 | jmnodes.theme-orange jmexpander:hover{} 128 | 129 | /* pumpkin theme */ 130 | jmnodes.theme-pumpkin jmnode{background-color:#e67e22;color:#fff;} 131 | jmnodes.theme-pumpkin jmnode:hover{background-color:#d35400;} 132 | jmnodes.theme-pumpkin jmnode.selected{background-color:#11f;color:#fff;} 133 | jmnodes.theme-pumpkin jmnode.root{} 134 | jmnodes.theme-pumpkin jmexpander{} 135 | jmnodes.theme-pumpkin jmexpander:hover{} 136 | 137 | /* pomegranate theme */ 138 | jmnodes.theme-pomegranate jmnode{background-color:#e74c3c;color:#fff;} 139 | jmnodes.theme-pomegranate jmnode:hover{background-color:#c0392b;} 140 | jmnodes.theme-pomegranate jmnode.selected{background-color:#11f;color:#fff;} 141 | jmnodes.theme-pomegranate jmnode.root{} 142 | jmnodes.theme-pomegranate jmexpander{} 143 | jmnodes.theme-pomegranate jmexpander:hover{} 144 | 145 | /* clouds theme */ 146 | jmnodes.theme-clouds jmnode{background-color:#ecf0f1;color:#333;} 147 | jmnodes.theme-clouds jmnode:hover{background-color:#bdc3c7;} 148 | jmnodes.theme-clouds jmnode.selected{background-color:#11f;color:#fff;} 149 | jmnodes.theme-clouds jmnode.root{} 150 | jmnodes.theme-clouds jmexpander{} 151 | jmnodes.theme-clouds jmexpander:hover{} 152 | 153 | /* asbestos theme */ 154 | jmnodes.theme-asbestos jmnode{background-color:#95a5a6;color:#fff;} 155 | jmnodes.theme-asbestos jmnode:hover{background-color:#7f8c8d;} 156 | jmnodes.theme-asbestos jmnode.selected{background-color:#11f;color:#fff;} 157 | jmnodes.theme-asbestos jmnode.root{} 158 | jmnodes.theme-asbestos jmexpander{} 159 | jmnodes.theme-asbestos jmexpander:hover{} 160 | -------------------------------------------------------------------------------- /src/components/JsMind/jsmind.draggable.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Released under BSD License 3 | * Copyright (c) 2014-2015 hizzgdev@163.com 4 | * 5 | * Project Home: 6 | * https://github.com/hizzgdev/jsmind/ 7 | */ 8 | 9 | (function ($w) { 10 | 'use strict'; 11 | var $d = $w.document; 12 | var __name__ = 'jsMind'; 13 | var jsMind = $w[__name__]; 14 | if (!jsMind) { return; } 15 | if (typeof jsMind.draggable != 'undefined') { return; } 16 | 17 | var jdom = jsMind.util.dom; 18 | var clear_selection = 'getSelection' in $w ? function () { 19 | $w.getSelection().removeAllRanges(); 20 | } : function () { 21 | $d.selection.empty(); 22 | }; 23 | 24 | var options = { 25 | line_width: 5, 26 | lookup_delay: 500, 27 | lookup_interval: 80 28 | }; 29 | 30 | jsMind.draggable = function (jm) { 31 | this.jm = jm; 32 | this.e_canvas = null; 33 | this.canvas_ctx = null; 34 | this.shadow = null; 35 | this.shadow_w = 0; 36 | this.shadow_h = 0; 37 | this.active_node = null; 38 | this.target_node = null; 39 | this.target_direct = null; 40 | this.client_w = 0; 41 | this.client_h = 0; 42 | this.offset_x = 0; 43 | this.offset_y = 0; 44 | this.hlookup_delay = 0; 45 | this.hlookup_timer = 0; 46 | this.capture = false; 47 | this.moved = false; 48 | }; 49 | 50 | jsMind.draggable.prototype = { 51 | init: function () { 52 | this._create_canvas(); 53 | this._create_shadow(); 54 | this._event_bind(); 55 | }, 56 | 57 | resize: function () { 58 | this.jm.view.e_nodes.appendChild(this.shadow); 59 | this.e_canvas.width = this.jm.view.size.w; 60 | this.e_canvas.height = this.jm.view.size.h; 61 | }, 62 | 63 | _create_canvas: function () { 64 | var c = $d.createElement('canvas'); 65 | this.jm.view.e_panel.appendChild(c); 66 | var ctx = c.getContext('2d'); 67 | this.e_canvas = c; 68 | this.canvas_ctx = ctx; 69 | }, 70 | 71 | _create_shadow: function () { 72 | var s = $d.createElement('jmnode'); 73 | s.style.visibility = 'hidden'; 74 | s.style.zIndex = '3'; 75 | s.style.cursor = 'move'; 76 | s.style.opacity = '0.7'; 77 | this.shadow = s; 78 | }, 79 | 80 | reset_shadow: function (el) { 81 | var s = this.shadow.style; 82 | this.shadow.innerHTML = el.innerHTML; 83 | s.left = el.style.left; 84 | s.top = el.style.top; 85 | s.width = el.style.width; 86 | s.height = el.style.height; 87 | s.backgroundImage = el.style.backgroundImage; 88 | s.backgroundSize = el.style.backgroundSize; 89 | s.transform = el.style.transform; 90 | this.shadow_w = this.shadow.clientWidth; 91 | this.shadow_h = this.shadow.clientHeight; 92 | 93 | }, 94 | 95 | show_shadow: function () { 96 | if (!this.moved) { 97 | this.shadow.style.visibility = 'visible'; 98 | } 99 | }, 100 | 101 | hide_shadow: function () { 102 | this.shadow.style.visibility = 'hidden'; 103 | }, 104 | 105 | _magnet_shadow: function (node) { 106 | if (!!node) { 107 | this.canvas_ctx.lineWidth = options.line_width; 108 | this.canvas_ctx.strokeStyle = 'rgba(0,0,0,0.3)'; 109 | this.canvas_ctx.lineCap = 'round'; 110 | this._clear_lines(); 111 | this._canvas_lineto(node.sp.x, node.sp.y, node.np.x, node.np.y); 112 | } 113 | }, 114 | 115 | _clear_lines: function () { 116 | this.canvas_ctx.clearRect(0, 0, this.jm.view.size.w, this.jm.view.size.h); 117 | }, 118 | 119 | _canvas_lineto: function (x1, y1, x2, y2) { 120 | this.canvas_ctx.beginPath(); 121 | this.canvas_ctx.moveTo(x1, y1); 122 | this.canvas_ctx.lineTo(x2, y2); 123 | this.canvas_ctx.stroke(); 124 | }, 125 | 126 | _lookup_close_node: function () { 127 | var root = this.jm.get_root(); 128 | var root_location = root.get_location(); 129 | var root_size = root.get_size(); 130 | var root_x = root_location.x + root_size.w / 2; 131 | 132 | var sw = this.shadow_w; 133 | var sh = this.shadow_h; 134 | var sx = this.shadow.offsetLeft; 135 | var sy = this.shadow.offsetTop; 136 | 137 | var ns, nl; 138 | 139 | var direct = (sx + sw / 2) >= root_x ? 140 | jsMind.direction.right : jsMind.direction.left; 141 | var nodes = this.jm.mind.nodes; 142 | var node = null; 143 | var min_distance = Number.MAX_VALUE; 144 | var distance = 0; 145 | var closest_node = null; 146 | var closest_p = null; 147 | var shadow_p = null; 148 | for (var nodeid in nodes) { 149 | var np, sp; 150 | node = nodes[nodeid]; 151 | if (node.isroot || node.direction == direct) { 152 | if (node.id == this.active_node.id) { 153 | continue; 154 | } 155 | ns = node.get_size(); 156 | nl = node.get_location(); 157 | if (direct == jsMind.direction.right) { 158 | if (sx - nl.x - ns.w <= 0) { continue; } 159 | distance = Math.abs(sx - nl.x - ns.w) + Math.abs(sy + sh / 2 - nl.y - ns.h / 2); 160 | np = { x: nl.x + ns.w - options.line_width, y: nl.y + ns.h / 2 }; 161 | sp = { x: sx + options.line_width, y: sy + sh / 2 }; 162 | } else { 163 | if (nl.x - sx - sw <= 0) { continue; } 164 | distance = Math.abs(sx + sw - nl.x) + Math.abs(sy + sh / 2 - nl.y - ns.h / 2); 165 | np = { x: nl.x + options.line_width, y: nl.y + ns.h / 2 }; 166 | sp = { x: sx + sw - options.line_width, y: sy + sh / 2 }; 167 | } 168 | if (distance < min_distance) { 169 | closest_node = node; 170 | closest_p = np; 171 | shadow_p = sp; 172 | min_distance = distance; 173 | } 174 | } 175 | } 176 | var result_node = null; 177 | if (!!closest_node) { 178 | result_node = { 179 | node: closest_node, 180 | direction: direct, 181 | sp: shadow_p, 182 | np: closest_p 183 | }; 184 | } 185 | return result_node; 186 | }, 187 | 188 | lookup_close_node: function () { 189 | var node_data = this._lookup_close_node(); 190 | if (!!node_data) { 191 | this._magnet_shadow(node_data); 192 | this.target_node = node_data.node; 193 | this.target_direct = node_data.direction; 194 | } 195 | }, 196 | 197 | _event_bind: function () { 198 | var jd = this; 199 | var container = this.jm.view.container; 200 | jdom.add_event(container, 'mousedown', function (e) { 201 | var evt = e || event; 202 | jd.dragstart.call(jd, evt); 203 | }); 204 | jdom.add_event(container, 'mousemove', function (e) { 205 | var evt = e || event; 206 | jd.drag.call(jd, evt); 207 | }); 208 | jdom.add_event(container, 'mouseup', function (e) { 209 | var evt = e || event; 210 | jd.dragend.call(jd, evt); 211 | }); 212 | jdom.add_event(container, 'touchstart', function (e) { 213 | var evt = e || event; 214 | jd.dragstart.call(jd, evt); 215 | }); 216 | jdom.add_event(container, 'touchmove', function (e) { 217 | var evt = e || event; 218 | jd.drag.call(jd, evt); 219 | }); 220 | jdom.add_event(container, 'touchend', function (e) { 221 | var evt = e || event; 222 | jd.dragend.call(jd, evt); 223 | }); 224 | }, 225 | 226 | dragstart: function (e) { 227 | if (!this.jm.get_editable()) { return; } 228 | if (this.capture) { return; } 229 | this.active_node = null; 230 | 231 | var jview = this.jm.view; 232 | var el = e.target || event.srcElement; 233 | if (el.tagName.toLowerCase() != 'jmnode') { return; } 234 | var nodeid = jview.get_binded_nodeid(el); 235 | if (!!nodeid) { 236 | var node = this.jm.get_node(nodeid); 237 | if (!node.isroot) { 238 | this.reset_shadow(el); 239 | this.active_node = node; 240 | this.offset_x = (e.clientX || e.touches[0].clientX) - el.offsetLeft; 241 | this.offset_y = (e.clientY || e.touches[0].clientY) - el.offsetTop; 242 | this.client_hw = Math.floor(el.clientWidth / 2); 243 | this.client_hh = Math.floor(el.clientHeight / 2); 244 | if (this.hlookup_delay != 0) { 245 | $w.clearTimeout(this.hlookup_delay); 246 | } 247 | if (this.hlookup_timer != 0) { 248 | $w.clearInterval(this.hlookup_timer); 249 | } 250 | var jd = this; 251 | this.hlookup_delay = $w.setTimeout(function () { 252 | jd.hlookup_delay = 0; 253 | jd.hlookup_timer = $w.setInterval(function () { 254 | jd.lookup_close_node.call(jd); 255 | }, options.lookup_interval); 256 | }, options.lookup_delay); 257 | this.capture = true; 258 | } 259 | } 260 | }, 261 | 262 | drag: function (e) { 263 | if (!this.jm.get_editable()) { return; } 264 | if (this.capture) { 265 | e.preventDefault(); 266 | this.show_shadow(); 267 | this.moved = true; 268 | clear_selection(); 269 | var px = (e.clientX || e.touches[0].clientX) - this.offset_x; 270 | var py = (e.clientY || e.touches[0].clientY) - this.offset_y; 271 | var cx = px + this.client_hw; 272 | var cy = py + this.client_hh; 273 | this.shadow.style.left = px + 'px'; 274 | this.shadow.style.top = py + 'px'; 275 | clear_selection(); 276 | } 277 | }, 278 | 279 | dragend: function (e) { 280 | if (!this.jm.get_editable()) { return; } 281 | if (this.capture) { 282 | if (this.hlookup_delay != 0) { 283 | $w.clearTimeout(this.hlookup_delay); 284 | this.hlookup_delay = 0; 285 | this._clear_lines(); 286 | } 287 | if (this.hlookup_timer != 0) { 288 | $w.clearInterval(this.hlookup_timer); 289 | this.hlookup_timer = 0; 290 | this._clear_lines(); 291 | } 292 | if (this.moved) { 293 | var src_node = this.active_node; 294 | var target_node = this.target_node; 295 | var target_direct = this.target_direct; 296 | this.move_node(src_node, target_node, target_direct); 297 | } 298 | this.hide_shadow(); 299 | } 300 | this.moved = false; 301 | this.capture = false; 302 | }, 303 | 304 | move_node: function (src_node, target_node, target_direct) { 305 | var shadow_h = this.shadow.offsetTop; 306 | if (!!target_node && !!src_node && !jsMind.node.inherited(src_node, target_node)) { 307 | // lookup before_node 308 | var sibling_nodes = target_node.children; 309 | var sc = sibling_nodes.length; 310 | var node = null; 311 | var delta_y = Number.MAX_VALUE; 312 | var node_before = null; 313 | var beforeid = '_last_'; 314 | while (sc--) { 315 | node = sibling_nodes[sc]; 316 | if (node.direction == target_direct && node.id != src_node.id) { 317 | var dy = node.get_location().y - shadow_h; 318 | if (dy > 0 && dy < delta_y) { 319 | delta_y = dy; 320 | node_before = node; 321 | beforeid = '_first_'; 322 | } 323 | } 324 | } 325 | if (!!node_before) { beforeid = node_before.id; } 326 | this.jm.move_node(src_node.id, beforeid, target_node.id, target_direct); 327 | } 328 | this.active_node = null; 329 | this.target_node = null; 330 | this.target_direct = null; 331 | }, 332 | 333 | jm_event_handle: function (type, data) { 334 | if (type === jsMind.event_type.resize) { 335 | this.resize(); 336 | } 337 | } 338 | }; 339 | 340 | var draggable_plugin = new jsMind.plugin('draggable', function (jm) { 341 | var jd = new jsMind.draggable(jm); 342 | jd.init(); 343 | jm.add_event_listener(function (type, data) { 344 | jd.jm_event_handle.call(jd, type, data); 345 | }); 346 | }); 347 | 348 | jsMind.register_plugin(draggable_plugin); 349 | 350 | })(window); 351 | -------------------------------------------------------------------------------- /src/components/JsMind/jsmind.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Released under BSD License 3 | * Copyright (c) 2014-2016 hizzgdev@163.com 4 | * 5 | * Project Home: 6 | * https://github.com/hizzgdev/jsmind/ 7 | */ 8 | 9 | ; (function ($w) { 10 | 'use strict'; 11 | // set 'jsMind' as the library name. 12 | // __name__ should be a const value, Never try to change it easily. 13 | var __name__ = 'jsMind'; 14 | // library version 15 | var __version__ = '0.4.6'; 16 | // author 17 | var __author__ = 'hizzgdev@163.com'; 18 | 19 | // an noop function define 20 | var _noop = function () { }; 21 | var logger = (typeof console === 'undefined') ? { 22 | log: _noop, debug: _noop, error: _noop, warn: _noop, info: _noop 23 | } : console; 24 | 25 | // check global variables 26 | if (typeof module === 'undefined' || !module.exports) { 27 | if (typeof $w[__name__] != 'undefined') { 28 | logger.log(__name__ + ' has been already exist.'); 29 | return; 30 | } 31 | } 32 | 33 | // shortcut of methods in dom 34 | var $d = $w.document; 35 | var $g = function (id) { return $d.getElementById(id); }; 36 | var $c = function (tag) { return $d.createElement(tag); }; 37 | var $t = function (n, t) { if (n.hasChildNodes()) { n.firstChild.nodeValue = t; } else { n.appendChild($d.createTextNode(t)); } }; 38 | 39 | var $h = function (n, t) { 40 | if (t instanceof HTMLElement) { 41 | n.innerHTML = ''; 42 | n.appendChild(t) 43 | } else { 44 | n.innerHTML = t; 45 | } 46 | }; 47 | // detect isElement 48 | var $i = function (el) { return !!el && (typeof el === 'object') && (el.nodeType === 1) && (typeof el.style === 'object') && (typeof el.ownerDocument === 'object'); }; 49 | if (typeof String.prototype.startsWith != 'function') { String.prototype.startsWith = function (p) { return this.slice(0, p.length) === p; }; } 50 | 51 | var DEFAULT_OPTIONS = { 52 | container: '', // id of the container 53 | editable: false, // you can change it in your options 54 | theme: null, 55 | mode: 'full', // full or side 56 | support_html: true, 57 | 58 | view: { 59 | engine: 'canvas', 60 | hmargin: 100, 61 | vmargin: 50, 62 | line_width: 2, 63 | line_color: '#555' 64 | }, 65 | layout: { 66 | hspace: 30, 67 | vspace: 20, 68 | pspace: 13 69 | }, 70 | default_event_handle: { 71 | enable_mousedown_handle: true, 72 | enable_click_handle: true, 73 | enable_dblclick_handle: true 74 | }, 75 | shortcut: { 76 | enable: true, 77 | handles: { 78 | }, 79 | mapping: { 80 | addchild: 45, // Insert 81 | addbrother: 13, // Enter 82 | editnode: 113,// F2 83 | delnode: 46, // Delete 84 | toggle: 32, // Space 85 | left: 37, // Left 86 | up: 38, // Up 87 | right: 39, // Right 88 | down: 40, // Down 89 | } 90 | }, 91 | }; 92 | 93 | // core object 94 | var jm = function (options) { 95 | jm.current = this; 96 | 97 | this.version = __version__; 98 | var opts = {}; 99 | jm.util.json.merge(opts, DEFAULT_OPTIONS); 100 | jm.util.json.merge(opts, options); 101 | 102 | if (!opts.container) { 103 | logger.error('the options.container should not be null or empty.'); 104 | return; 105 | } 106 | this.options = opts; 107 | this.inited = false; 108 | this.mind = null; 109 | this.event_handles = []; 110 | this.init(); 111 | }; 112 | 113 | // ============= static object ============================================= 114 | jm.direction = { left: -1, center: 0, right: 1 }; 115 | jm.event_type = { show: 1, resize: 2, edit: 3, select: 4 }; 116 | jm.key = { meta: 1 << 13, ctrl: 1 << 12, alt: 1 << 11, shift: 1 << 10 }; 117 | 118 | jm.node = function (sId, iIndex, sTopic, oData, bIsRoot, oParent, eDirection, bExpanded) { 119 | if (!sId) { logger.error('invalid nodeid'); return; } 120 | if (typeof iIndex != 'number') { logger.error('invalid node index'); return; } 121 | if (typeof bExpanded === 'undefined') { bExpanded = true; } 122 | this.id = sId; 123 | this.index = iIndex; 124 | this.topic = sTopic; 125 | this.data = oData || {}; 126 | this.isroot = bIsRoot; 127 | this.parent = oParent; 128 | this.direction = eDirection; 129 | this.expanded = !!bExpanded; 130 | this.children = []; 131 | this._data = {}; 132 | }; 133 | 134 | jm.node.compare = function (node1, node2) { 135 | // '-1' is alwary the last 136 | var r = 0; 137 | var i1 = node1.index; 138 | var i2 = node2.index; 139 | if (i1 >= 0 && i2 >= 0) { 140 | r = i1 - i2; 141 | } else if (i1 == -1 && i2 == -1) { 142 | r = 0; 143 | } else if (i1 == -1) { 144 | r = 1; 145 | } else if (i2 == -1) { 146 | r = -1; 147 | } else { 148 | r = 0; 149 | } 150 | //logger.debug(i1+' <> '+i2+' = '+r); 151 | return r; 152 | }; 153 | 154 | jm.node.inherited = function (pnode, node) { 155 | if (!!pnode && !!node) { 156 | if (pnode.id === node.id) { 157 | return true; 158 | } 159 | if (pnode.isroot) { 160 | return true; 161 | } 162 | var pid = pnode.id; 163 | var p = node; 164 | while (!p.isroot) { 165 | p = p.parent; 166 | if (p.id === pid) { 167 | return true; 168 | } 169 | } 170 | } 171 | return false; 172 | }; 173 | 174 | jm.node.prototype = { 175 | get_location: function () { 176 | var vd = this._data.view; 177 | return { 178 | x: vd.abs_x, 179 | y: vd.abs_y 180 | }; 181 | }, 182 | get_size: function () { 183 | var vd = this._data.view; 184 | return { 185 | w: vd.width, 186 | h: vd.height 187 | } 188 | } 189 | }; 190 | 191 | 192 | jm.mind = function () { 193 | this.name = null; 194 | this.author = null; 195 | this.version = null; 196 | this.root = null; 197 | this.selected = null; 198 | this.nodes = {}; 199 | }; 200 | 201 | jm.mind.prototype = { 202 | get_node: function (nodeid) { 203 | if (nodeid in this.nodes) { 204 | return this.nodes[nodeid]; 205 | } else { 206 | logger.warn('the node[id=' + nodeid + '] can not be found'); 207 | return null; 208 | } 209 | }, 210 | 211 | set_root: function (nodeid, topic, data) { 212 | if (this.root == null) { 213 | this.root = new jm.node(nodeid, 0, topic, data, true); 214 | this._put_node(this.root); 215 | } else { 216 | logger.error('root node is already exist'); 217 | } 218 | }, 219 | 220 | add_node: function (parent_node, nodeid, topic, data, idx, direction, expanded) { 221 | if (!jm.util.is_node(parent_node)) { 222 | var the_parent_node = this.get_node(parent_node); 223 | if (!the_parent_node) { 224 | logger.error('the parent_node[id=' + parent_node + '] can not be found.'); 225 | return null; 226 | } else { 227 | return this.add_node(the_parent_node, nodeid, topic, data, idx, direction, expanded); 228 | } 229 | } 230 | var nodeindex = idx || -1; 231 | var node = null; 232 | if (parent_node.isroot) { 233 | var d = jm.direction.right; 234 | if (isNaN(direction)) { 235 | var children = parent_node.children; 236 | var children_len = children.length; 237 | var r = 0; 238 | for (var i = 0; i < children_len; i++) { if (children[i].direction === jm.direction.left) { r--; } else { r++; } } 239 | d = (children_len > 1 && r > 0) ? jm.direction.left : jm.direction.right 240 | } else { 241 | d = (direction != jm.direction.left) ? jm.direction.right : jm.direction.left; 242 | } 243 | node = new jm.node(nodeid, nodeindex, topic, data, false, parent_node, d, expanded); 244 | } else { 245 | node = new jm.node(nodeid, nodeindex, topic, data, false, parent_node, parent_node.direction, expanded); 246 | } 247 | if (this._put_node(node)) { 248 | parent_node.children.push(node); 249 | this._reindex(parent_node); 250 | } else { 251 | logger.error('fail, the nodeid \'' + node.id + '\' has been already exist.'); 252 | node = null; 253 | } 254 | return node; 255 | }, 256 | 257 | insert_node_before: function (node_before, nodeid, topic, data) { 258 | if (!jm.util.is_node(node_before)) { 259 | var the_node_before = this.get_node(node_before); 260 | if (!the_node_before) { 261 | logger.error('the node_before[id=' + node_before + '] can not be found.'); 262 | return null; 263 | } else { 264 | return this.insert_node_before(the_node_before, nodeid, topic, data); 265 | } 266 | } 267 | var node_index = node_before.index - 0.5; 268 | return this.add_node(node_before.parent, nodeid, topic, data, node_index); 269 | }, 270 | 271 | get_node_before: function (node) { 272 | if (!jm.util.is_node(node)) { 273 | var the_node = this.get_node(node); 274 | if (!the_node) { 275 | logger.error('the node[id=' + node + '] can not be found.'); 276 | return null; 277 | } else { 278 | return this.get_node_before(the_node); 279 | } 280 | } 281 | if (node.isroot) { return null; } 282 | var idx = node.index - 2; 283 | if (idx >= 0) { 284 | return node.parent.children[idx]; 285 | } else { 286 | return null; 287 | } 288 | }, 289 | 290 | insert_node_after: function (node_after, nodeid, topic, data) { 291 | if (!jm.util.is_node(node_after)) { 292 | var the_node_after = this.get_node(node_before); 293 | if (!the_node_after) { 294 | logger.error('the node_after[id=' + node_after + '] can not be found.'); 295 | return null; 296 | } else { 297 | return this.insert_node_after(the_node_after, nodeid, topic, data); 298 | } 299 | } 300 | var node_index = node_after.index + 0.5; 301 | return this.add_node(node_after.parent, nodeid, topic, data, node_index); 302 | }, 303 | 304 | get_node_after: function (node) { 305 | if (!jm.util.is_node(node)) { 306 | var the_node = this.get_node(node); 307 | if (!the_node) { 308 | logger.error('the node[id=' + node + '] can not be found.'); 309 | return null; 310 | } else { 311 | return this.get_node_after(the_node); 312 | } 313 | } 314 | if (node.isroot) { return null; } 315 | var idx = node.index; 316 | var brothers = node.parent.children; 317 | if (brothers.length >= idx) { 318 | return node.parent.children[idx]; 319 | } else { 320 | return null; 321 | } 322 | }, 323 | 324 | move_node: function (node, beforeid, parentid, direction) { 325 | if (!jm.util.is_node(node)) { 326 | var the_node = this.get_node(node); 327 | if (!the_node) { 328 | logger.error('the node[id=' + node + '] can not be found.'); 329 | return null; 330 | } else { 331 | return this.move_node(the_node, beforeid, parentid, direction); 332 | } 333 | } 334 | if (!parentid) { 335 | parentid = node.parent.id; 336 | } 337 | return this._move_node(node, beforeid, parentid, direction); 338 | }, 339 | 340 | _flow_node_direction: function (node, direction) { 341 | if (typeof direction === 'undefined') { 342 | direction = node.direction; 343 | } else { 344 | node.direction = direction; 345 | } 346 | var len = node.children.length; 347 | while (len--) { 348 | this._flow_node_direction(node.children[len], direction); 349 | } 350 | }, 351 | 352 | _move_node_internal: function (node, beforeid) { 353 | if (!!node && !!beforeid) { 354 | if (beforeid == '_last_') { 355 | node.index = -1; 356 | this._reindex(node.parent); 357 | } else if (beforeid == '_first_') { 358 | node.index = 0; 359 | this._reindex(node.parent); 360 | } else { 361 | var node_before = (!!beforeid) ? this.get_node(beforeid) : null; 362 | if (node_before != null && node_before.parent != null && node_before.parent.id == node.parent.id) { 363 | node.index = node_before.index - 0.5; 364 | this._reindex(node.parent); 365 | } 366 | } 367 | } 368 | return node; 369 | }, 370 | 371 | _move_node: function (node, beforeid, parentid, direction) { 372 | if (!!node && !!parentid) { 373 | if (node.parent.id != parentid) { 374 | // remove from parent's children 375 | var sibling = node.parent.children; 376 | var si = sibling.length; 377 | while (si--) { 378 | if (sibling[si].id == node.id) { 379 | sibling.splice(si, 1); 380 | break; 381 | } 382 | } 383 | node.parent = this.get_node(parentid); 384 | node.parent.children.push(node); 385 | } 386 | 387 | if (node.parent.isroot) { 388 | if (direction == jm.direction.left) { 389 | node.direction = direction; 390 | } else { 391 | node.direction = jm.direction.right; 392 | } 393 | } else { 394 | node.direction = node.parent.direction; 395 | } 396 | this._move_node_internal(node, beforeid); 397 | this._flow_node_direction(node); 398 | } 399 | return node; 400 | }, 401 | 402 | remove_node: function (node) { 403 | if (!jm.util.is_node(node)) { 404 | var the_node = this.get_node(node); 405 | if (!the_node) { 406 | logger.error('the node[id=' + node + '] can not be found.'); 407 | return false; 408 | } else { 409 | return this.remove_node(the_node); 410 | } 411 | } 412 | if (!node) { 413 | logger.error('fail, the node can not be found'); 414 | return false; 415 | } 416 | if (node.isroot) { 417 | logger.error('fail, can not remove root node'); 418 | return false; 419 | } 420 | if (this.selected != null && this.selected.id == node.id) { 421 | this.selected = null; 422 | } 423 | // clean all subordinate nodes 424 | var children = node.children; 425 | var ci = children.length; 426 | while (ci--) { 427 | this.remove_node(children[ci]); 428 | } 429 | // clean all children 430 | children.length = 0; 431 | // remove from parent's children 432 | var sibling = node.parent.children; 433 | var si = sibling.length; 434 | while (si--) { 435 | if (sibling[si].id == node.id) { 436 | sibling.splice(si, 1); 437 | break; 438 | } 439 | } 440 | // remove from global nodes 441 | delete this.nodes[node.id]; 442 | // clean all properties 443 | for (var k in node) { 444 | delete node[k]; 445 | } 446 | // remove it's self 447 | node = null; 448 | //delete node; 449 | return true; 450 | }, 451 | 452 | _put_node: function (node) { 453 | if (node.id in this.nodes) { 454 | logger.warn('the nodeid \'' + node.id + '\' has been already exist.'); 455 | return false; 456 | } else { 457 | this.nodes[node.id] = node; 458 | return true; 459 | } 460 | }, 461 | 462 | _reindex: function (node) { 463 | if (node instanceof jm.node) { 464 | node.children.sort(jm.node.compare); 465 | for (var i = 0; i < node.children.length; i++) { 466 | node.children[i].index = i + 1; 467 | } 468 | } 469 | }, 470 | }; 471 | 472 | jm.format = { 473 | node_tree: { 474 | example: { 475 | "meta": { 476 | "name": __name__, 477 | "author": __author__, 478 | "version": __version__ 479 | }, 480 | "format": "node_tree", 481 | "data": { "id": "root", "topic": "jsMind Example" } 482 | }, 483 | get_mind: function (source) { 484 | var df = jm.format.node_tree; 485 | var mind = new jm.mind(); 486 | mind.name = source.meta.name; 487 | mind.author = source.meta.author; 488 | mind.version = source.meta.version; 489 | df._parse(mind, source.data); 490 | return mind; 491 | }, 492 | get_data: function (mind) { 493 | var df = jm.format.node_tree; 494 | var json = {}; 495 | json.meta = { 496 | name: mind.name, 497 | author: mind.author, 498 | version: mind.version 499 | }; 500 | json.format = 'node_tree'; 501 | json.data = df._buildnode(mind.root); 502 | return json; 503 | }, 504 | 505 | _parse: function (mind, node_root) { 506 | var df = jm.format.node_tree; 507 | var data = df._extract_data(node_root); 508 | mind.set_root(node_root.id, node_root.topic, data); 509 | if ('children' in node_root) { 510 | var children = node_root.children; 511 | for (var i = 0; i < children.length; i++) { 512 | df._extract_subnode(mind, mind.root, children[i]); 513 | } 514 | } 515 | }, 516 | 517 | _extract_data: function (node_json) { 518 | var data = {}; 519 | for (var k in node_json) { 520 | if (k == 'id' || k == 'topic' || k == 'children' || k == 'direction' || k == 'expanded') { 521 | continue; 522 | } 523 | data[k] = node_json[k]; 524 | } 525 | return data; 526 | }, 527 | 528 | _extract_subnode: function (mind, node_parent, node_json) { 529 | var df = jm.format.node_tree; 530 | var data = df._extract_data(node_json); 531 | var d = null; 532 | if (node_parent.isroot) { 533 | d = node_json.direction == 'left' ? jm.direction.left : jm.direction.right; 534 | } 535 | var node = mind.add_node(node_parent, node_json.id, node_json.topic, data, null, d, node_json.expanded); 536 | if ('children' in node_json) { 537 | var children = node_json.children; 538 | for (var i = 0; i < children.length; i++) { 539 | df._extract_subnode(mind, node, children[i]); 540 | } 541 | } 542 | }, 543 | 544 | _buildnode: function (node) { 545 | var df = jm.format.node_tree; 546 | if (!(node instanceof jm.node)) { return; } 547 | var o = { 548 | id: node.id, 549 | topic: node.topic, 550 | expanded: node.expanded 551 | }; 552 | if (!!node.parent && node.parent.isroot) { 553 | o.direction = node.direction == jm.direction.left ? 'left' : 'right'; 554 | } 555 | if (node.data != null) { 556 | var node_data = node.data; 557 | for (var k in node_data) { 558 | o[k] = node_data[k]; 559 | } 560 | } 561 | var children = node.children; 562 | if (children.length > 0) { 563 | o.children = []; 564 | for (var i = 0; i < children.length; i++) { 565 | o.children.push(df._buildnode(children[i])); 566 | } 567 | } 568 | return o; 569 | } 570 | }, 571 | 572 | node_array: { 573 | example: { 574 | "meta": { 575 | "name": __name__, 576 | "author": __author__, 577 | "version": __version__ 578 | }, 579 | "format": "node_array", 580 | "data": [ 581 | { "id": "root", "topic": "jsMind Example", "isroot": true } 582 | ] 583 | }, 584 | 585 | get_mind: function (source) { 586 | var df = jm.format.node_array; 587 | var mind = new jm.mind(); 588 | mind.name = source.meta.name; 589 | mind.author = source.meta.author; 590 | mind.version = source.meta.version; 591 | df._parse(mind, source.data); 592 | return mind; 593 | }, 594 | 595 | get_data: function (mind) { 596 | var df = jm.format.node_array; 597 | var json = {}; 598 | json.meta = { 599 | name: mind.name, 600 | author: mind.author, 601 | version: mind.version 602 | }; 603 | json.format = 'node_array'; 604 | json.data = []; 605 | df._array(mind, json.data); 606 | return json; 607 | }, 608 | 609 | _parse: function (mind, node_array) { 610 | var df = jm.format.node_array; 611 | var narray = node_array.slice(0); 612 | // reverse array for improving looping performance 613 | narray.reverse(); 614 | var root_id = df._extract_root(mind, narray); 615 | if (!!root_id) { 616 | df._extract_subnode(mind, root_id, narray); 617 | } else { 618 | logger.error('root node can not be found'); 619 | } 620 | }, 621 | 622 | _extract_root: function (mind, node_array) { 623 | var df = jm.format.node_array; 624 | var i = node_array.length; 625 | while (i--) { 626 | if ('isroot' in node_array[i] && node_array[i].isroot) { 627 | var root_json = node_array[i]; 628 | var data = df._extract_data(root_json); 629 | mind.set_root(root_json.id, root_json.topic, data); 630 | node_array.splice(i, 1); 631 | return root_json.id; 632 | } 633 | } 634 | return null; 635 | }, 636 | 637 | _extract_subnode: function (mind, parentid, node_array) { 638 | var df = jm.format.node_array; 639 | var i = node_array.length; 640 | var node_json = null; 641 | var data = null; 642 | var extract_count = 0; 643 | while (i--) { 644 | node_json = node_array[i]; 645 | if (node_json.parentid == parentid) { 646 | data = df._extract_data(node_json); 647 | var d = null; 648 | var node_direction = node_json.direction; 649 | if (!!node_direction) { 650 | d = node_direction == 'left' ? jm.direction.left : jm.direction.right; 651 | } 652 | mind.add_node(parentid, node_json.id, node_json.topic, data, null, d, node_json.expanded); 653 | node_array.splice(i, 1); 654 | extract_count++; 655 | var sub_extract_count = df._extract_subnode(mind, node_json.id, node_array); 656 | if (sub_extract_count > 0) { 657 | // reset loop index after extract subordinate node 658 | i = node_array.length; 659 | extract_count += sub_extract_count; 660 | } 661 | } 662 | } 663 | return extract_count; 664 | }, 665 | 666 | _extract_data: function (node_json) { 667 | var data = {}; 668 | for (var k in node_json) { 669 | if (k == 'id' || k == 'topic' || k == 'parentid' || k == 'isroot' || k == 'direction' || k == 'expanded') { 670 | continue; 671 | } 672 | data[k] = node_json[k]; 673 | } 674 | return data; 675 | }, 676 | 677 | _array: function (mind, node_array) { 678 | var df = jm.format.node_array; 679 | df._array_node(mind.root, node_array); 680 | }, 681 | 682 | _array_node: function (node, node_array) { 683 | var df = jm.format.node_array; 684 | if (!(node instanceof jm.node)) { return; } 685 | var o = { 686 | id: node.id, 687 | topic: node.topic, 688 | expanded: node.expanded 689 | }; 690 | if (!!node.parent) { 691 | o.parentid = node.parent.id; 692 | } 693 | if (node.isroot) { 694 | o.isroot = true; 695 | } 696 | if (!!node.parent && node.parent.isroot) { 697 | o.direction = node.direction == jm.direction.left ? 'left' : 'right'; 698 | } 699 | if (node.data != null) { 700 | var node_data = node.data; 701 | for (var k in node_data) { 702 | o[k] = node_data[k]; 703 | } 704 | } 705 | node_array.push(o); 706 | var ci = node.children.length; 707 | for (var i = 0; i < ci; i++) { 708 | df._array_node(node.children[i], node_array); 709 | } 710 | }, 711 | }, 712 | 713 | freemind: { 714 | example: { 715 | "meta": { 716 | "name": __name__, 717 | "author": __author__, 718 | "version": __version__ 719 | }, 720 | "format": "freemind", 721 | "data": "" 722 | }, 723 | get_mind: function (source) { 724 | var df = jm.format.freemind; 725 | var mind = new jm.mind(); 726 | mind.name = source.meta.name; 727 | mind.author = source.meta.author; 728 | mind.version = source.meta.version; 729 | var xml = source.data; 730 | var xml_doc = df._parse_xml(xml); 731 | var xml_root = df._find_root(xml_doc); 732 | df._load_node(mind, null, xml_root); 733 | return mind; 734 | }, 735 | 736 | get_data: function (mind) { 737 | var df = jm.format.freemind; 738 | var json = {}; 739 | json.meta = { 740 | name: mind.name, 741 | author: mind.author, 742 | version: mind.version 743 | }; 744 | json.format = 'freemind'; 745 | var xmllines = []; 746 | xmllines.push(''); 747 | df._buildmap(mind.root, xmllines); 748 | xmllines.push(''); 749 | json.data = xmllines.join(' '); 750 | return json; 751 | }, 752 | 753 | _parse_xml: function (xml) { 754 | var xml_doc = null; 755 | if (window.DOMParser) { 756 | var parser = new DOMParser(); 757 | xml_doc = parser.parseFromString(xml, 'text/xml'); 758 | } else { // Internet Explorer 759 | xml_doc = new ActiveXObject('Microsoft.XMLDOM'); 760 | xml_doc.async = false; 761 | xml_doc.loadXML(xml); 762 | } 763 | return xml_doc; 764 | }, 765 | 766 | _find_root: function (xml_doc) { 767 | var nodes = xml_doc.childNodes; 768 | var node = null; 769 | var root = null; 770 | var n = null; 771 | for (var i = 0; i < nodes.length; i++) { 772 | n = nodes[i]; 773 | if (n.nodeType == 1 && n.tagName == 'map') { 774 | node = n; 775 | break; 776 | } 777 | } 778 | if (!!node) { 779 | var ns = node.childNodes; 780 | node = null; 781 | for (var i = 0; i < ns.length; i++) { 782 | n = ns[i]; 783 | if (n.nodeType == 1 && n.tagName == 'node') { 784 | node = n; 785 | break; 786 | } 787 | } 788 | } 789 | return node; 790 | }, 791 | 792 | _load_node: function (mind, parent_id, xml_node) { 793 | var df = jm.format.freemind; 794 | var node_id = xml_node.getAttribute('ID'); 795 | var node_topic = xml_node.getAttribute('TEXT'); 796 | // look for richcontent 797 | if (node_topic == null) { 798 | var topic_children = xml_node.childNodes; 799 | var topic_child = null; 800 | for (var i = 0; i < topic_children.length; i++) { 801 | topic_child = topic_children[i]; 802 | //logger.debug(topic_child.tagName); 803 | if (topic_child.nodeType == 1 && topic_child.tagName === 'richcontent') { 804 | node_topic = topic_child.textContent; 805 | break; 806 | } 807 | } 808 | } 809 | var node_data = df._load_attributes(xml_node); 810 | var node_expanded = ('expanded' in node_data) ? (node_data.expanded == 'true') : true; 811 | delete node_data.expanded; 812 | 813 | var node_position = xml_node.getAttribute('POSITION'); 814 | var node_direction = null; 815 | if (!!node_position) { 816 | node_direction = node_position == 'left' ? jm.direction.left : jm.direction.right; 817 | } 818 | //logger.debug(node_position +':'+ node_direction); 819 | if (!!parent_id) { 820 | mind.add_node(parent_id, node_id, node_topic, node_data, null, node_direction, node_expanded); 821 | } else { 822 | mind.set_root(node_id, node_topic, node_data); 823 | } 824 | var children = xml_node.childNodes; 825 | var child = null; 826 | for (var i = 0; i < children.length; i++) { 827 | child = children[i]; 828 | if (child.nodeType == 1 && child.tagName == 'node') { 829 | df._load_node(mind, node_id, child); 830 | } 831 | } 832 | }, 833 | 834 | _load_attributes: function (xml_node) { 835 | var children = xml_node.childNodes; 836 | var attr = null; 837 | var attr_data = {}; 838 | for (var i = 0; i < children.length; i++) { 839 | attr = children[i]; 840 | if (attr.nodeType == 1 && attr.tagName === 'attribute') { 841 | attr_data[attr.getAttribute('NAME')] = attr.getAttribute('VALUE'); 842 | } 843 | } 844 | return attr_data; 845 | }, 846 | 847 | _buildmap: function (node, xmllines) { 848 | var df = jm.format.freemind; 849 | var pos = null; 850 | if (!!node.parent && node.parent.isroot) { 851 | pos = node.direction === jm.direction.left ? 'left' : 'right'; 852 | } 853 | xmllines.push(''); 859 | 860 | // store expanded status as an attribute 861 | xmllines.push(''); 862 | 863 | // for attributes 864 | var node_data = node.data; 865 | if (node_data != null) { 866 | for (var k in node_data) { 867 | xmllines.push(''); 868 | } 869 | } 870 | 871 | // for children 872 | var children = node.children; 873 | for (var i = 0; i < children.length; i++) { 874 | df._buildmap(children[i], xmllines); 875 | } 876 | 877 | xmllines.push(''); 878 | }, 879 | }, 880 | }; 881 | 882 | // ============= utility object ============================================= 883 | 884 | jm.util = { 885 | is_node: function (node) { 886 | return !!node && node instanceof jm.node; 887 | }, 888 | ajax: { 889 | _xhr: function () { 890 | var xhr = null; 891 | if (window.XMLHttpRequest) { 892 | xhr = new XMLHttpRequest(); 893 | } else { 894 | try { 895 | xhr = new ActiveXObject('Microsoft.XMLHTTP'); 896 | } catch (e) { } 897 | } 898 | return xhr; 899 | }, 900 | _eurl: function (url) { 901 | return encodeURIComponent(url); 902 | }, 903 | request: function (url, param, method, callback, fail_callback) { 904 | var a = jm.util.ajax; 905 | var p = null; 906 | var tmp_param = []; 907 | for (var k in param) { 908 | tmp_param.push(a._eurl(k) + '=' + a._eurl(param[k])); 909 | } 910 | if (tmp_param.length > 0) { 911 | p = tmp_param.join('&'); 912 | } 913 | var xhr = a._xhr(); 914 | if (!xhr) { return; } 915 | xhr.onreadystatechange = function () { 916 | if (xhr.readyState == 4) { 917 | if (xhr.status == 200 || xhr.status == 0) { 918 | if (typeof callback === 'function') { 919 | var data = jm.util.json.string2json(xhr.responseText); 920 | if (data != null) { 921 | callback(data); 922 | } else { 923 | callback(xhr.responseText); 924 | } 925 | } 926 | } else { 927 | if (typeof fail_callback === 'function') { 928 | fail_callback(xhr); 929 | } else { 930 | logger.error('xhr request failed.', xhr); 931 | } 932 | } 933 | } 934 | } 935 | method = method || 'GET'; 936 | xhr.open(method, url, true); 937 | xhr.setRequestHeader('If-Modified-Since', '0'); 938 | if (method == 'POST') { 939 | xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded;charset=utf-8'); 940 | xhr.send(p); 941 | } else { 942 | xhr.send(); 943 | } 944 | }, 945 | get: function (url, callback) { 946 | return jm.util.ajax.request(url, {}, 'GET', callback); 947 | }, 948 | post: function (url, param, callback) { 949 | return jm.util.ajax.request(url, param, 'POST', callback); 950 | } 951 | }, 952 | 953 | dom: { 954 | //target,eventType,handler 955 | add_event: function (t, e, h) { 956 | if (!!t.addEventListener) { 957 | t.addEventListener(e, h, false); 958 | } else { 959 | t.attachEvent('on' + e, h); 960 | } 961 | } 962 | }, 963 | 964 | file: { 965 | read: function (file_data, fn_callback) { 966 | var reader = new FileReader(); 967 | reader.onload = function () { 968 | if (typeof fn_callback === 'function') { 969 | fn_callback(this.result, file_data.name); 970 | } 971 | }; 972 | reader.readAsText(file_data); 973 | }, 974 | 975 | save: function (file_data, type, name) { 976 | var blob; 977 | if (typeof $w.Blob === 'function') { 978 | blob = new Blob([file_data], { type: type }); 979 | } else { 980 | var BlobBuilder = $w.BlobBuilder || $w.MozBlobBuilder || $w.WebKitBlobBuilder || $w.MSBlobBuilder; 981 | var bb = new BlobBuilder(); 982 | bb.append(file_data); 983 | blob = bb.getBlob(type); 984 | } 985 | if (navigator.msSaveBlob) { 986 | navigator.msSaveBlob(blob, name); 987 | } else { 988 | var URL = $w.URL || $w.webkitURL; 989 | var bloburl = URL.createObjectURL(blob); 990 | var anchor = $c('a'); 991 | if ('download' in anchor) { 992 | anchor.style.visibility = 'hidden'; 993 | anchor.href = bloburl; 994 | anchor.download = name; 995 | $d.body.appendChild(anchor); 996 | var evt = $d.createEvent('MouseEvents'); 997 | evt.initEvent('click', true, true); 998 | anchor.dispatchEvent(evt); 999 | $d.body.removeChild(anchor); 1000 | } else { 1001 | location.href = bloburl; 1002 | } 1003 | } 1004 | } 1005 | }, 1006 | 1007 | json: { 1008 | json2string: function (json) { 1009 | if (!!JSON) { 1010 | try { 1011 | var json_str = JSON.stringify(json); 1012 | return json_str; 1013 | } catch (e) { 1014 | logger.warn(e); 1015 | logger.warn('can not convert to string'); 1016 | return null; 1017 | } 1018 | } 1019 | }, 1020 | string2json: function (json_str) { 1021 | if (!!JSON) { 1022 | try { 1023 | var json = JSON.parse(json_str); 1024 | return json; 1025 | } catch (e) { 1026 | logger.warn(e); 1027 | logger.warn('can not parse to json'); 1028 | return null; 1029 | } 1030 | } 1031 | }, 1032 | merge: function (b, a) { 1033 | for (var o in a) { 1034 | if (o in b) { 1035 | if (typeof b[o] === 'object' && 1036 | Object.prototype.toString.call(b[o]).toLowerCase() == '[object object]' && 1037 | !b[o].length) { 1038 | jm.util.json.merge(b[o], a[o]); 1039 | } else { 1040 | b[o] = a[o]; 1041 | } 1042 | } else { 1043 | b[o] = a[o]; 1044 | } 1045 | } 1046 | return b; 1047 | } 1048 | }, 1049 | 1050 | uuid: { 1051 | newid: function () { 1052 | return (new Date().getTime().toString(16) + Math.random().toString(16).substr(2)).substr(2, 16); 1053 | } 1054 | }, 1055 | 1056 | text: { 1057 | is_empty: function (s) { 1058 | if (!s) { return true; } 1059 | return s.replace(/\s*/, '').length == 0; 1060 | } 1061 | } 1062 | }; 1063 | 1064 | jm.prototype = { 1065 | init: function () { 1066 | if (this.inited) { return; } 1067 | this.inited = true; 1068 | 1069 | var opts = this.options; 1070 | 1071 | var opts_layout = { 1072 | mode: opts.mode, 1073 | hspace: opts.layout.hspace, 1074 | vspace: opts.layout.vspace, 1075 | pspace: opts.layout.pspace 1076 | } 1077 | var opts_view = { 1078 | container: opts.container, 1079 | support_html: opts.support_html, 1080 | engine: opts.view.engine, 1081 | hmargin: opts.view.hmargin, 1082 | vmargin: opts.view.vmargin, 1083 | line_width: opts.view.line_width, 1084 | line_color: opts.view.line_color 1085 | }; 1086 | // create instance of function provider 1087 | this.data = new jm.data_provider(this); 1088 | this.layout = new jm.layout_provider(this, opts_layout); 1089 | this.view = new jm.view_provider(this, opts_view); 1090 | this.shortcut = new jm.shortcut_provider(this, opts.shortcut); 1091 | 1092 | this.data.init(); 1093 | this.layout.init(); 1094 | this.view.init(); 1095 | this.shortcut.init(); 1096 | 1097 | this._event_bind(); 1098 | 1099 | jm.init_plugins(this); 1100 | }, 1101 | 1102 | enable_edit: function () { 1103 | this.options.editable = true; 1104 | }, 1105 | 1106 | disable_edit: function () { 1107 | this.options.editable = false; 1108 | }, 1109 | 1110 | // call enable_event_handle('dblclick') 1111 | // options are 'mousedown', 'click', 'dblclick' 1112 | enable_event_handle: function (event_handle) { 1113 | this.options.default_event_handle['enable_' + event_handle + '_handle'] = true; 1114 | }, 1115 | 1116 | // call disable_event_handle('dblclick') 1117 | // options are 'mousedown', 'click', 'dblclick' 1118 | disable_event_handle: function (event_handle) { 1119 | this.options.default_event_handle['enable_' + event_handle + '_handle'] = false; 1120 | }, 1121 | 1122 | get_editable: function () { 1123 | return this.options.editable; 1124 | }, 1125 | 1126 | set_theme: function (theme) { 1127 | var theme_old = this.options.theme; 1128 | this.options.theme = (!!theme) ? theme : null; 1129 | if (theme_old != this.options.theme) { 1130 | this.view.reset_theme(); 1131 | this.view.reset_custom_style(); 1132 | } 1133 | }, 1134 | _event_bind: function () { 1135 | this.view.add_event(this, 'mousedown', this.mousedown_handle); 1136 | this.view.add_event(this, 'click', this.click_handle); 1137 | this.view.add_event(this, 'dblclick', this.dblclick_handle); 1138 | }, 1139 | 1140 | mousedown_handle: function (e) { 1141 | if (!this.options.default_event_handle['enable_mousedown_handle']) { 1142 | return; 1143 | } 1144 | var element = e.target || event.srcElement; 1145 | var nodeid = this.view.get_binded_nodeid(element); 1146 | if (!!nodeid) { 1147 | if (element.tagName.toLowerCase() == 'jmnode') { 1148 | this.select_node(nodeid); 1149 | } 1150 | } else { 1151 | this.select_clear(); 1152 | } 1153 | }, 1154 | 1155 | click_handle: function (e) { 1156 | if (!this.options.default_event_handle['enable_click_handle']) { 1157 | return; 1158 | } 1159 | var element = e.target || event.srcElement; 1160 | var isexpander = this.view.is_expander(element); 1161 | if (isexpander) { 1162 | var nodeid = this.view.get_binded_nodeid(element); 1163 | if (!!nodeid) { 1164 | this.toggle_node(nodeid); 1165 | } 1166 | } 1167 | }, 1168 | 1169 | dblclick_handle: function (e) { 1170 | if (!this.options.default_event_handle['enable_dblclick_handle']) { 1171 | return; 1172 | } 1173 | if (this.get_editable()) { 1174 | var element = e.target || event.srcElement; 1175 | var nodeid = this.view.get_binded_nodeid(element); 1176 | if (!!nodeid) { 1177 | this.begin_edit(nodeid); 1178 | } 1179 | } 1180 | }, 1181 | 1182 | begin_edit: function (node) { 1183 | if (!jm.util.is_node(node)) { 1184 | var the_node = this.get_node(node); 1185 | if (!the_node) { 1186 | logger.error('the node[id=' + node + '] can not be found.'); 1187 | return false; 1188 | } else { 1189 | return this.begin_edit(the_node); 1190 | } 1191 | } 1192 | if (this.get_editable()) { 1193 | this.view.edit_node_begin(node); 1194 | } else { 1195 | logger.error('fail, this mind map is not editable.'); 1196 | return; 1197 | } 1198 | }, 1199 | 1200 | end_edit: function () { 1201 | this.view.edit_node_end(); 1202 | }, 1203 | 1204 | toggle_node: function (node) { 1205 | if (!jm.util.is_node(node)) { 1206 | var the_node = this.get_node(node); 1207 | if (!the_node) { 1208 | logger.error('the node[id=' + node + '] can not be found.'); 1209 | return; 1210 | } else { 1211 | return this.toggle_node(the_node); 1212 | } 1213 | } 1214 | if (node.isroot) { return; } 1215 | this.view.save_location(node); 1216 | this.layout.toggle_node(node); 1217 | this.view.relayout(); 1218 | this.view.restore_location(node); 1219 | }, 1220 | 1221 | expand_node: function (node) { 1222 | if (!jm.util.is_node(node)) { 1223 | var the_node = this.get_node(node); 1224 | if (!the_node) { 1225 | logger.error('the node[id=' + node + '] can not be found.'); 1226 | return; 1227 | } else { 1228 | return this.expand_node(the_node); 1229 | } 1230 | } 1231 | if (node.isroot) { return; } 1232 | this.view.save_location(node); 1233 | this.layout.expand_node(node); 1234 | this.view.relayout(); 1235 | this.view.restore_location(node); 1236 | }, 1237 | 1238 | collapse_node: function (node) { 1239 | if (!jm.util.is_node(node)) { 1240 | var the_node = this.get_node(node); 1241 | if (!the_node) { 1242 | logger.error('the node[id=' + node + '] can not be found.'); 1243 | return; 1244 | } else { 1245 | return this.collapse_node(the_node); 1246 | } 1247 | } 1248 | if (node.isroot) { return; } 1249 | this.view.save_location(node); 1250 | this.layout.collapse_node(node); 1251 | this.view.relayout(); 1252 | this.view.restore_location(node); 1253 | }, 1254 | 1255 | expand_all: function () { 1256 | this.layout.expand_all(); 1257 | this.view.relayout(); 1258 | }, 1259 | 1260 | collapse_all: function () { 1261 | this.layout.collapse_all(); 1262 | this.view.relayout(); 1263 | }, 1264 | 1265 | expand_to_depth: function (depth) { 1266 | this.layout.expand_to_depth(depth); 1267 | this.view.relayout(); 1268 | }, 1269 | 1270 | _reset: function () { 1271 | this.view.reset(); 1272 | this.layout.reset(); 1273 | this.data.reset(); 1274 | }, 1275 | 1276 | _show: function (mind) { 1277 | var m = mind || jm.format.node_array.example; 1278 | 1279 | this.mind = this.data.load(m); 1280 | if (!this.mind) { 1281 | logger.error('data.load error'); 1282 | return; 1283 | } else { 1284 | logger.debug('data.load ok'); 1285 | } 1286 | 1287 | this.view.load(); 1288 | logger.debug('view.load ok'); 1289 | 1290 | this.layout.layout(); 1291 | logger.debug('layout.layout ok'); 1292 | 1293 | this.view.show(true); 1294 | logger.debug('view.show ok'); 1295 | 1296 | this.invoke_event_handle(jm.event_type.show, { data: [mind] }); 1297 | }, 1298 | 1299 | show: function (mind) { 1300 | this._reset(); 1301 | this._show(mind); 1302 | }, 1303 | 1304 | get_meta: function () { 1305 | return { 1306 | name: this.mind.name, 1307 | author: this.mind.author, 1308 | version: this.mind.version 1309 | }; 1310 | }, 1311 | 1312 | get_data: function (data_format) { 1313 | var df = data_format || 'node_tree'; 1314 | return this.data.get_data(df); 1315 | }, 1316 | 1317 | get_root: function () { 1318 | return this.mind.root; 1319 | }, 1320 | 1321 | get_node: function (nodeid) { 1322 | return this.mind.get_node(nodeid); 1323 | }, 1324 | 1325 | add_node: function (parent_node, nodeid, topic, data) { 1326 | if (this.get_editable()) { 1327 | var node = this.mind.add_node(parent_node, nodeid, topic, data); 1328 | if (!!node) { 1329 | this.view.add_node(node); 1330 | this.layout.layout(); 1331 | this.view.show(false); 1332 | this.view.reset_node_custom_style(node); 1333 | this.expand_node(parent_node); 1334 | this.invoke_event_handle(jm.event_type.edit, { evt: 'add_node', data: [parent_node.id, nodeid, topic, data], node: nodeid }); 1335 | } 1336 | return node; 1337 | } else { 1338 | logger.error('fail, this mind map is not editable'); 1339 | return null; 1340 | } 1341 | }, 1342 | 1343 | insert_node_before: function (node_before, nodeid, topic, data) { 1344 | if (this.get_editable()) { 1345 | var beforeid = jm.util.is_node(node_before) ? node_before.id : node_before; 1346 | var node = this.mind.insert_node_before(node_before, nodeid, topic, data); 1347 | if (!!node) { 1348 | this.view.add_node(node); 1349 | this.layout.layout(); 1350 | this.view.show(false); 1351 | this.invoke_event_handle(jm.event_type.edit, { evt: 'insert_node_before', data: [beforeid, nodeid, topic, data], node: nodeid }); 1352 | } 1353 | return node; 1354 | } else { 1355 | logger.error('fail, this mind map is not editable'); 1356 | return null; 1357 | } 1358 | }, 1359 | 1360 | insert_node_after: function (node_after, nodeid, topic, data) { 1361 | if (this.get_editable()) { 1362 | var afterid = jm.util.is_node(node_after) ? node_after.id : node_after; 1363 | var node = this.mind.insert_node_after(node_after, nodeid, topic, data); 1364 | if (!!node) { 1365 | this.view.add_node(node); 1366 | this.layout.layout(); 1367 | this.view.show(false); 1368 | this.invoke_event_handle(jm.event_type.edit, { evt: 'insert_node_after', data: [afterid, nodeid, topic, data], node: nodeid }); 1369 | } 1370 | return node; 1371 | } else { 1372 | logger.error('fail, this mind map is not editable'); 1373 | return null; 1374 | } 1375 | }, 1376 | 1377 | remove_node: function (node) { 1378 | if (!jm.util.is_node(node)) { 1379 | var the_node = this.get_node(node); 1380 | if (!the_node) { 1381 | logger.error('the node[id=' + node + '] can not be found.'); 1382 | return false; 1383 | } else { 1384 | return this.remove_node(the_node); 1385 | } 1386 | } 1387 | if (this.get_editable()) { 1388 | if (node.isroot) { 1389 | logger.error('fail, can not remove root node'); 1390 | return false; 1391 | } 1392 | var nodeid = node.id; 1393 | var parentid = node.parent.id; 1394 | var parent_node = this.get_node(parentid); 1395 | this.view.save_location(parent_node); 1396 | this.view.remove_node(node); 1397 | this.mind.remove_node(node); 1398 | this.layout.layout(); 1399 | this.view.show(false); 1400 | this.view.restore_location(parent_node); 1401 | this.invoke_event_handle(jm.event_type.edit, { evt: 'remove_node', data: [nodeid], node: parentid }); 1402 | return true; 1403 | } else { 1404 | logger.error('fail, this mind map is not editable'); 1405 | return false; 1406 | } 1407 | }, 1408 | 1409 | update_node: function (nodeid, topic) { 1410 | if (this.get_editable()) { 1411 | if (jm.util.text.is_empty(topic)) { 1412 | logger.warn('fail, topic can not be empty'); 1413 | return; 1414 | } 1415 | var node = this.get_node(nodeid); 1416 | if (!!node) { 1417 | if (node.topic === topic) { 1418 | logger.info('nothing changed'); 1419 | this.view.update_node(node); 1420 | return; 1421 | } 1422 | node.topic = topic; 1423 | this.view.update_node(node); 1424 | this.layout.layout(); 1425 | this.view.show(false); 1426 | this.invoke_event_handle(jm.event_type.edit, { evt: 'update_node', data: [nodeid, topic], node: nodeid }); 1427 | } 1428 | } else { 1429 | logger.error('fail, this mind map is not editable'); 1430 | return; 1431 | } 1432 | }, 1433 | 1434 | move_node: function (nodeid, beforeid, parentid, direction) { 1435 | if (this.get_editable()) { 1436 | var node = this.mind.move_node(nodeid, beforeid, parentid, direction); 1437 | if (!!node) { 1438 | this.view.update_node(node); 1439 | this.layout.layout(); 1440 | this.view.show(false); 1441 | this.invoke_event_handle(jm.event_type.edit, { evt: 'move_node', data: [nodeid, beforeid, parentid, direction], node: nodeid }); 1442 | } 1443 | } else { 1444 | logger.error('fail, this mind map is not editable'); 1445 | return; 1446 | } 1447 | }, 1448 | 1449 | select_node: function (node) { 1450 | if (!jm.util.is_node(node)) { 1451 | var the_node = this.get_node(node); 1452 | if (!the_node) { 1453 | logger.error('the node[id=' + node + '] can not be found.'); 1454 | return; 1455 | } else { 1456 | return this.select_node(the_node); 1457 | } 1458 | } 1459 | if (!this.layout.is_visible(node)) { 1460 | return; 1461 | } 1462 | this.mind.selected = node; 1463 | this.view.select_node(node); 1464 | }, 1465 | 1466 | get_selected_node: function () { 1467 | if (!!this.mind) { 1468 | return this.mind.selected; 1469 | } else { 1470 | return null; 1471 | } 1472 | }, 1473 | 1474 | select_clear: function () { 1475 | if (!!this.mind) { 1476 | this.mind.selected = null; 1477 | this.view.select_clear(); 1478 | } 1479 | }, 1480 | 1481 | is_node_visible: function (node) { 1482 | return this.layout.is_visible(node); 1483 | }, 1484 | 1485 | find_node_before: function (node) { 1486 | if (!jm.util.is_node(node)) { 1487 | var the_node = this.get_node(node); 1488 | if (!the_node) { 1489 | logger.error('the node[id=' + node + '] can not be found.'); 1490 | return; 1491 | } else { 1492 | return this.find_node_before(the_node); 1493 | } 1494 | } 1495 | if (node.isroot) { return null; } 1496 | var n = null; 1497 | if (node.parent.isroot) { 1498 | var c = node.parent.children; 1499 | var prev = null; 1500 | var ni = null; 1501 | for (var i = 0; i < c.length; i++) { 1502 | ni = c[i]; 1503 | if (node.direction === ni.direction) { 1504 | if (node.id === ni.id) { 1505 | n = prev; 1506 | } 1507 | prev = ni; 1508 | } 1509 | } 1510 | } else { 1511 | n = this.mind.get_node_before(node); 1512 | } 1513 | return n; 1514 | }, 1515 | 1516 | find_node_after: function (node) { 1517 | if (!jm.util.is_node(node)) { 1518 | var the_node = this.get_node(node); 1519 | if (!the_node) { 1520 | logger.error('the node[id=' + node + '] can not be found.'); 1521 | return; 1522 | } else { 1523 | return this.find_node_after(the_node); 1524 | } 1525 | } 1526 | if (node.isroot) { return null; } 1527 | var n = null; 1528 | if (node.parent.isroot) { 1529 | var c = node.parent.children; 1530 | var getthis = false; 1531 | var ni = null; 1532 | for (var i = 0; i < c.length; i++) { 1533 | ni = c[i]; 1534 | if (node.direction === ni.direction) { 1535 | if (getthis) { 1536 | n = ni; 1537 | break; 1538 | } 1539 | if (node.id === ni.id) { 1540 | getthis = true; 1541 | } 1542 | } 1543 | } 1544 | } else { 1545 | n = this.mind.get_node_after(node); 1546 | } 1547 | return n; 1548 | }, 1549 | 1550 | set_node_color: function (nodeid, bgcolor, fgcolor) { 1551 | if (this.get_editable()) { 1552 | var node = this.mind.get_node(nodeid); 1553 | if (!!node) { 1554 | if (!!bgcolor) { 1555 | node.data['background-color'] = bgcolor; 1556 | } 1557 | if (!!fgcolor) { 1558 | node.data['foreground-color'] = fgcolor; 1559 | } 1560 | this.view.reset_node_custom_style(node); 1561 | } 1562 | } else { 1563 | logger.error('fail, this mind map is not editable'); 1564 | return null; 1565 | } 1566 | }, 1567 | 1568 | set_node_font_style: function (nodeid, size, weight, style) { 1569 | if (this.get_editable()) { 1570 | var node = this.mind.get_node(nodeid); 1571 | if (!!node) { 1572 | if (!!size) { 1573 | node.data['font-size'] = size; 1574 | } 1575 | if (!!weight) { 1576 | node.data['font-weight'] = weight; 1577 | } 1578 | if (!!style) { 1579 | node.data['font-style'] = style; 1580 | } 1581 | this.view.reset_node_custom_style(node); 1582 | this.view.update_node(node); 1583 | this.layout.layout(); 1584 | this.view.show(false); 1585 | } 1586 | } else { 1587 | logger.error('fail, this mind map is not editable'); 1588 | return null; 1589 | } 1590 | }, 1591 | 1592 | set_node_background_image: function (nodeid, image, width, height, rotation) { 1593 | if (this.get_editable()) { 1594 | var node = this.mind.get_node(nodeid); 1595 | if (!!node) { 1596 | if (!!image) { 1597 | node.data['background-image'] = image; 1598 | } 1599 | if (!!width) { 1600 | node.data['width'] = width; 1601 | } 1602 | if (!!height) { 1603 | node.data['height'] = height; 1604 | } 1605 | if (!!rotation) { 1606 | node.data['background-rotation'] = rotation; 1607 | } 1608 | this.view.reset_node_custom_style(node); 1609 | this.view.update_node(node); 1610 | this.layout.layout(); 1611 | this.view.show(false); 1612 | } 1613 | } else { 1614 | logger.error('fail, this mind map is not editable'); 1615 | return null; 1616 | } 1617 | }, 1618 | 1619 | set_node_background_rotation: function (nodeid, rotation) { 1620 | if (this.get_editable()) { 1621 | var node = this.mind.get_node(nodeid); 1622 | if (!!node) { 1623 | if (!node.data['background-image']) { 1624 | logger.error('fail, only can change rotation angle of node with background image'); 1625 | return null; 1626 | } 1627 | node.data['background-rotation'] = rotation; 1628 | this.view.reset_node_custom_style(node); 1629 | this.view.update_node(node); 1630 | this.layout.layout(); 1631 | this.view.show(false); 1632 | } 1633 | } else { 1634 | logger.error('fail, this mind map is not editable'); 1635 | return null; 1636 | } 1637 | }, 1638 | 1639 | resize: function () { 1640 | this.view.resize(); 1641 | }, 1642 | 1643 | // callback(type ,data) 1644 | add_event_listener: function (callback) { 1645 | if (typeof callback === 'function') { 1646 | this.event_handles.push(callback); 1647 | } 1648 | }, 1649 | 1650 | invoke_event_handle: function (type, data) { 1651 | var j = this; 1652 | $w.setTimeout(function () { 1653 | j._invoke_event_handle(type, data); 1654 | }, 0); 1655 | }, 1656 | 1657 | _invoke_event_handle: function (type, data) { 1658 | var l = this.event_handles.length; 1659 | for (var i = 0; i < l; i++) { 1660 | this.event_handles[i](type, data); 1661 | } 1662 | } 1663 | 1664 | }; 1665 | 1666 | // ============= data provider ============================================= 1667 | 1668 | jm.data_provider = function (jm) { 1669 | this.jm = jm; 1670 | }; 1671 | 1672 | jm.data_provider.prototype = { 1673 | init: function () { 1674 | logger.debug('data.init'); 1675 | }, 1676 | 1677 | reset: function () { 1678 | logger.debug('data.reset'); 1679 | }, 1680 | 1681 | load: function (mind_data) { 1682 | var df = null; 1683 | var mind = null; 1684 | if (typeof mind_data === 'object') { 1685 | if (!!mind_data.format) { 1686 | df = mind_data.format; 1687 | } else { 1688 | df = 'node_tree'; 1689 | } 1690 | } else { 1691 | df = 'freemind'; 1692 | } 1693 | 1694 | if (df == 'node_array') { 1695 | mind = jm.format.node_array.get_mind(mind_data); 1696 | } else if (df == 'node_tree') { 1697 | mind = jm.format.node_tree.get_mind(mind_data); 1698 | } else if (df == 'freemind') { 1699 | mind = jm.format.freemind.get_mind(mind_data); 1700 | } else { 1701 | logger.warn('unsupported format'); 1702 | } 1703 | return mind; 1704 | }, 1705 | 1706 | get_data: function (data_format) { 1707 | var data = null; 1708 | if (data_format == 'node_array') { 1709 | data = jm.format.node_array.get_data(this.jm.mind); 1710 | } else if (data_format == 'node_tree') { 1711 | data = jm.format.node_tree.get_data(this.jm.mind); 1712 | } else if (data_format == 'freemind') { 1713 | data = jm.format.freemind.get_data(this.jm.mind); 1714 | } else { 1715 | logger.error('unsupported ' + data_format + ' format'); 1716 | } 1717 | return data; 1718 | }, 1719 | }; 1720 | 1721 | // ============= layout provider =========================================== 1722 | 1723 | jm.layout_provider = function (jm, options) { 1724 | this.opts = options; 1725 | this.jm = jm; 1726 | this.isside = (this.opts.mode == 'side'); 1727 | this.bounds = null; 1728 | 1729 | this.cache_valid = false; 1730 | }; 1731 | 1732 | jm.layout_provider.prototype = { 1733 | init: function () { 1734 | logger.debug('layout.init'); 1735 | }, 1736 | reset: function () { 1737 | logger.debug('layout.reset'); 1738 | this.bounds = { n: 0, s: 0, w: 0, e: 0 }; 1739 | }, 1740 | layout: function () { 1741 | logger.debug('layout.layout'); 1742 | this.layout_direction(); 1743 | this.layout_offset(); 1744 | }, 1745 | 1746 | layout_direction: function () { 1747 | this._layout_direction_root(); 1748 | }, 1749 | 1750 | _layout_direction_root: function () { 1751 | var node = this.jm.mind.root; 1752 | // logger.debug(node); 1753 | var layout_data = null; 1754 | if ('layout' in node._data) { 1755 | layout_data = node._data.layout; 1756 | } else { 1757 | layout_data = {}; 1758 | node._data.layout = layout_data; 1759 | } 1760 | var children = node.children; 1761 | var children_count = children.length; 1762 | layout_data.direction = jm.direction.center; 1763 | layout_data.side_index = 0; 1764 | if (this.isside) { 1765 | var i = children_count; 1766 | while (i--) { 1767 | this._layout_direction_side(children[i], jm.direction.right, i); 1768 | } 1769 | } else { 1770 | var i = children_count; 1771 | var subnode = null; 1772 | while (i--) { 1773 | subnode = children[i]; 1774 | if (subnode.direction == jm.direction.left) { 1775 | this._layout_direction_side(subnode, jm.direction.left, i); 1776 | } else { 1777 | this._layout_direction_side(subnode, jm.direction.right, i); 1778 | } 1779 | } 1780 | /* 1781 | var boundary = Math.ceil(children_count/2); 1782 | var i = children_count; 1783 | while(i--){ 1784 | if(i>=boundary){ 1785 | this._layout_direction_side(children[i],jm.direction.left, children_count-i-1); 1786 | }else{ 1787 | this._layout_direction_side(children[i],jm.direction.right, i); 1788 | } 1789 | }*/ 1790 | 1791 | } 1792 | }, 1793 | 1794 | _layout_direction_side: function (node, direction, side_index) { 1795 | var layout_data = null; 1796 | if ('layout' in node._data) { 1797 | layout_data = node._data.layout; 1798 | } else { 1799 | layout_data = {}; 1800 | node._data.layout = layout_data; 1801 | } 1802 | var children = node.children; 1803 | var children_count = children.length; 1804 | 1805 | layout_data.direction = direction; 1806 | layout_data.side_index = side_index; 1807 | var i = children_count; 1808 | while (i--) { 1809 | this._layout_direction_side(children[i], direction, i); 1810 | } 1811 | }, 1812 | 1813 | layout_offset: function () { 1814 | var node = this.jm.mind.root; 1815 | var layout_data = node._data.layout; 1816 | layout_data.offset_x = 0; 1817 | layout_data.offset_y = 0; 1818 | layout_data.outer_height = 0; 1819 | var children = node.children; 1820 | var i = children.length; 1821 | var left_nodes = []; 1822 | var right_nodes = []; 1823 | var subnode = null; 1824 | while (i--) { 1825 | subnode = children[i]; 1826 | if (subnode._data.layout.direction == jm.direction.right) { 1827 | right_nodes.unshift(subnode); 1828 | } else { 1829 | left_nodes.unshift(subnode); 1830 | } 1831 | } 1832 | layout_data.left_nodes = left_nodes; 1833 | layout_data.right_nodes = right_nodes; 1834 | layout_data.outer_height_left = this._layout_offset_subnodes(left_nodes); 1835 | layout_data.outer_height_right = this._layout_offset_subnodes(right_nodes); 1836 | this.bounds.e = node._data.view.width / 2; 1837 | this.bounds.w = 0 - this.bounds.e; 1838 | //logger.debug(this.bounds.w); 1839 | this.bounds.n = 0; 1840 | this.bounds.s = Math.max(layout_data.outer_height_left, layout_data.outer_height_right); 1841 | }, 1842 | 1843 | // layout both the x and y axis 1844 | _layout_offset_subnodes: function (nodes) { 1845 | var total_height = 0; 1846 | var nodes_count = nodes.length; 1847 | var i = nodes_count; 1848 | var node = null; 1849 | var node_outer_height = 0; 1850 | var layout_data = null; 1851 | var base_y = 0; 1852 | var pd = null; // parent._data 1853 | while (i--) { 1854 | node = nodes[i]; 1855 | layout_data = node._data.layout; 1856 | if (pd == null) { 1857 | pd = node.parent._data; 1858 | } 1859 | 1860 | node_outer_height = this._layout_offset_subnodes(node.children); 1861 | if (!node.expanded) { 1862 | node_outer_height = 0; 1863 | this.set_visible(node.children, false); 1864 | } 1865 | node_outer_height = Math.max(node._data.view.height, node_outer_height); 1866 | 1867 | layout_data.outer_height = node_outer_height; 1868 | layout_data.offset_y = base_y - node_outer_height / 2; 1869 | layout_data.offset_x = this.opts.hspace * layout_data.direction + pd.view.width * (pd.layout.direction + layout_data.direction) / 2; 1870 | if (!node.parent.isroot) { 1871 | layout_data.offset_x += this.opts.pspace * layout_data.direction; 1872 | } 1873 | 1874 | base_y = base_y - node_outer_height - this.opts.vspace; 1875 | total_height += node_outer_height; 1876 | } 1877 | if (nodes_count > 1) { 1878 | total_height += this.opts.vspace * (nodes_count - 1); 1879 | } 1880 | i = nodes_count; 1881 | var middle_height = total_height / 2; 1882 | while (i--) { 1883 | node = nodes[i]; 1884 | node._data.layout.offset_y += middle_height; 1885 | } 1886 | return total_height; 1887 | }, 1888 | 1889 | // layout the y axis only, for collapse/expand a node 1890 | _layout_offset_subnodes_height: function (nodes) { 1891 | var total_height = 0; 1892 | var nodes_count = nodes.length; 1893 | var i = nodes_count; 1894 | var node = null; 1895 | var node_outer_height = 0; 1896 | var layout_data = null; 1897 | var base_y = 0; 1898 | var pd = null; // parent._data 1899 | while (i--) { 1900 | node = nodes[i]; 1901 | layout_data = node._data.layout; 1902 | if (pd == null) { 1903 | pd = node.parent._data; 1904 | } 1905 | 1906 | node_outer_height = this._layout_offset_subnodes_height(node.children); 1907 | if (!node.expanded) { 1908 | node_outer_height = 0; 1909 | } 1910 | node_outer_height = Math.max(node._data.view.height, node_outer_height); 1911 | 1912 | layout_data.outer_height = node_outer_height; 1913 | layout_data.offset_y = base_y - node_outer_height / 2; 1914 | base_y = base_y - node_outer_height - this.opts.vspace; 1915 | total_height += node_outer_height; 1916 | } 1917 | if (nodes_count > 1) { 1918 | total_height += this.opts.vspace * (nodes_count - 1); 1919 | } 1920 | i = nodes_count; 1921 | var middle_height = total_height / 2; 1922 | while (i--) { 1923 | node = nodes[i]; 1924 | node._data.layout.offset_y += middle_height; 1925 | //logger.debug(node.topic); 1926 | //logger.debug(node._data.layout.offset_y); 1927 | } 1928 | return total_height; 1929 | }, 1930 | 1931 | get_node_offset: function (node) { 1932 | var layout_data = node._data.layout; 1933 | var offset_cache = null; 1934 | if (('_offset_' in layout_data) && this.cache_valid) { 1935 | offset_cache = layout_data._offset_; 1936 | } else { 1937 | offset_cache = { x: -1, y: -1 }; 1938 | layout_data._offset_ = offset_cache; 1939 | } 1940 | if (offset_cache.x == -1 || offset_cache.y == -1) { 1941 | var x = layout_data.offset_x; 1942 | var y = layout_data.offset_y; 1943 | if (!node.isroot) { 1944 | var offset_p = this.get_node_offset(node.parent); 1945 | x += offset_p.x; 1946 | y += offset_p.y; 1947 | } 1948 | offset_cache.x = x; 1949 | offset_cache.y = y; 1950 | } 1951 | return offset_cache; 1952 | }, 1953 | 1954 | get_node_point: function (node) { 1955 | var view_data = node._data.view; 1956 | var offset_p = this.get_node_offset(node); 1957 | //logger.debug(offset_p); 1958 | var p = {}; 1959 | p.x = offset_p.x + view_data.width * (node._data.layout.direction - 1) / 2; 1960 | p.y = offset_p.y - view_data.height / 2; 1961 | //logger.debug(p); 1962 | return p; 1963 | }, 1964 | 1965 | get_node_point_in: function (node) { 1966 | var p = this.get_node_offset(node); 1967 | return p; 1968 | }, 1969 | 1970 | get_node_point_out: function (node) { 1971 | var layout_data = node._data.layout; 1972 | var pout_cache = null; 1973 | if (('_pout_' in layout_data) && this.cache_valid) { 1974 | pout_cache = layout_data._pout_; 1975 | } else { 1976 | pout_cache = { x: -1, y: -1 }; 1977 | layout_data._pout_ = pout_cache; 1978 | } 1979 | if (pout_cache.x == -1 || pout_cache.y == -1) { 1980 | if (node.isroot) { 1981 | pout_cache.x = 0; 1982 | pout_cache.y = 0; 1983 | } else { 1984 | var view_data = node._data.view; 1985 | var offset_p = this.get_node_offset(node); 1986 | pout_cache.x = offset_p.x + (view_data.width + this.opts.pspace) * node._data.layout.direction; 1987 | pout_cache.y = offset_p.y; 1988 | //logger.debug('pout'); 1989 | //logger.debug(pout_cache); 1990 | } 1991 | } 1992 | return pout_cache; 1993 | }, 1994 | 1995 | get_expander_point: function (node) { 1996 | var p = this.get_node_point_out(node); 1997 | var ex_p = {}; 1998 | if (node._data.layout.direction == jm.direction.right) { 1999 | ex_p.x = p.x - this.opts.pspace; 2000 | } else { 2001 | ex_p.x = p.x; 2002 | } 2003 | ex_p.y = p.y - Math.ceil(this.opts.pspace / 2); 2004 | return ex_p; 2005 | }, 2006 | 2007 | get_min_size: function () { 2008 | var nodes = this.jm.mind.nodes; 2009 | var node = null; 2010 | var pout = null; 2011 | for (var nodeid in nodes) { 2012 | node = nodes[nodeid]; 2013 | pout = this.get_node_point_out(node); 2014 | if (pout.x > this.bounds.e) { this.bounds.e = pout.x; } 2015 | if (pout.x < this.bounds.w) { this.bounds.w = pout.x; } 2016 | } 2017 | return { 2018 | w: this.bounds.e - this.bounds.w, 2019 | h: this.bounds.s - this.bounds.n 2020 | } 2021 | }, 2022 | 2023 | toggle_node: function (node) { 2024 | if (node.isroot) { 2025 | return; 2026 | } 2027 | if (node.expanded) { 2028 | this.collapse_node(node); 2029 | } else { 2030 | this.expand_node(node); 2031 | } 2032 | }, 2033 | 2034 | expand_node: function (node) { 2035 | node.expanded = true; 2036 | this.part_layout(node); 2037 | this.set_visible(node.children, true); 2038 | }, 2039 | 2040 | collapse_node: function (node) { 2041 | node.expanded = false; 2042 | this.part_layout(node); 2043 | this.set_visible(node.children, false); 2044 | }, 2045 | 2046 | expand_all: function () { 2047 | var nodes = this.jm.mind.nodes; 2048 | var c = 0; 2049 | var node; 2050 | for (var nodeid in nodes) { 2051 | node = nodes[nodeid]; 2052 | if (!node.expanded) { 2053 | node.expanded = true; 2054 | c++; 2055 | } 2056 | } 2057 | if (c > 0) { 2058 | var root = this.jm.mind.root; 2059 | this.part_layout(root); 2060 | this.set_visible(root.children, true); 2061 | } 2062 | }, 2063 | 2064 | collapse_all: function () { 2065 | var nodes = this.jm.mind.nodes; 2066 | var c = 0; 2067 | var node; 2068 | for (var nodeid in nodes) { 2069 | node = nodes[nodeid]; 2070 | if (node.expanded && !node.isroot) { 2071 | node.expanded = false 2072 | c++; 2073 | } 2074 | } 2075 | if (c > 0) { 2076 | var root = this.jm.mind.root; 2077 | this.part_layout(root); 2078 | this.set_visible(root.children, true); 2079 | } 2080 | }, 2081 | 2082 | expand_to_depth: function (target_depth, curr_nodes, curr_depth) { 2083 | if (target_depth < 1) { return; } 2084 | var nodes = curr_nodes || this.jm.mind.root.children; 2085 | var depth = curr_depth || 1; 2086 | var i = nodes.length; 2087 | var node = null; 2088 | while (i--) { 2089 | node = nodes[i]; 2090 | if (depth < target_depth) { 2091 | if (!node.expanded) { 2092 | this.expand_node(node); 2093 | } 2094 | this.expand_to_depth(target_depth, node.children, depth + 1); 2095 | } 2096 | if (depth == target_depth) { 2097 | if (node.expanded) { 2098 | this.collapse_node(node); 2099 | } 2100 | } 2101 | } 2102 | }, 2103 | 2104 | part_layout: function (node) { 2105 | var root = this.jm.mind.root; 2106 | if (!!root) { 2107 | var root_layout_data = root._data.layout; 2108 | if (node.isroot) { 2109 | root_layout_data.outer_height_right = this._layout_offset_subnodes_height(root_layout_data.right_nodes); 2110 | root_layout_data.outer_height_left = this._layout_offset_subnodes_height(root_layout_data.left_nodes); 2111 | } else { 2112 | if (node._data.layout.direction == jm.direction.right) { 2113 | root_layout_data.outer_height_right = this._layout_offset_subnodes_height(root_layout_data.right_nodes); 2114 | } else { 2115 | root_layout_data.outer_height_left = this._layout_offset_subnodes_height(root_layout_data.left_nodes); 2116 | } 2117 | } 2118 | this.bounds.s = Math.max(root_layout_data.outer_height_left, root_layout_data.outer_height_right); 2119 | this.cache_valid = false; 2120 | } else { 2121 | logger.warn('can not found root node'); 2122 | } 2123 | }, 2124 | 2125 | set_visible: function (nodes, visible) { 2126 | var i = nodes.length; 2127 | var node = null; 2128 | var layout_data = null; 2129 | while (i--) { 2130 | node = nodes[i]; 2131 | layout_data = node._data.layout; 2132 | if (node.expanded) { 2133 | this.set_visible(node.children, visible); 2134 | } else { 2135 | this.set_visible(node.children, false); 2136 | } 2137 | if (!node.isroot) { 2138 | node._data.layout.visible = visible; 2139 | } 2140 | } 2141 | }, 2142 | 2143 | is_expand: function (node) { 2144 | return node.expanded; 2145 | }, 2146 | 2147 | is_visible: function (node) { 2148 | var layout_data = node._data.layout; 2149 | if (('visible' in layout_data) && !layout_data.visible) { 2150 | return false; 2151 | } else { 2152 | return true; 2153 | } 2154 | } 2155 | }; 2156 | 2157 | jm.graph_canvas = function (view) { 2158 | this.opts = view.opts; 2159 | this.e_canvas = $c('canvas'); 2160 | this.canvas_ctx = this.e_canvas.getContext('2d'); 2161 | this.size = { w: 0, h: 0 }; 2162 | }; 2163 | 2164 | jm.graph_canvas.prototype = { 2165 | element: function () { 2166 | return this.e_canvas; 2167 | }, 2168 | 2169 | set_size: function (w, h) { 2170 | this.size.w = w; 2171 | this.size.h = h; 2172 | this.e_canvas.width = w; 2173 | this.e_canvas.height = h; 2174 | }, 2175 | 2176 | clear: function () { 2177 | this.canvas_ctx.clearRect(0, 0, this.size.w, this.size.h); 2178 | }, 2179 | 2180 | draw_line: function (pout, pin, offset) { 2181 | var ctx = this.canvas_ctx; 2182 | ctx.strokeStyle = this.opts.line_color; 2183 | ctx.lineWidth = this.opts.line_width; 2184 | ctx.lineCap = 'round'; 2185 | 2186 | this._bezier_to(ctx, 2187 | pin.x + offset.x, 2188 | pin.y + offset.y, 2189 | pout.x + offset.x, 2190 | pout.y + offset.y); 2191 | }, 2192 | 2193 | copy_to: function (dest_canvas_ctx, callback) { 2194 | dest_canvas_ctx.drawImage(this.e_canvas, 0, 0); 2195 | !!callback && callback(); 2196 | }, 2197 | 2198 | _bezier_to: function (ctx, x1, y1, x2, y2) { 2199 | ctx.beginPath(); 2200 | ctx.moveTo(x1, y1); 2201 | ctx.bezierCurveTo(x1 + (x2 - x1) * 2 / 3, y1, x1, y2, x2, y2); 2202 | ctx.stroke(); 2203 | }, 2204 | 2205 | _line_to: function (ctx, x1, y1, x2, y2) { 2206 | ctx.beginPath(); 2207 | ctx.moveTo(x1, y1); 2208 | ctx.lineTo(x2, y2); 2209 | ctx.stroke(); 2210 | } 2211 | }; 2212 | 2213 | jm.graph_svg = function (view) { 2214 | this.view = view; 2215 | this.opts = view.opts; 2216 | this.e_svg = jm.graph_svg.c('svg'); 2217 | this.size = { w: 0, h: 0 }; 2218 | this.lines = []; 2219 | }; 2220 | 2221 | jm.graph_svg.c = function (tag) { 2222 | return $d.createElementNS('http://www.w3.org/2000/svg', tag); 2223 | }; 2224 | 2225 | jm.graph_svg.prototype = { 2226 | element: function () { 2227 | return this.e_svg; 2228 | }, 2229 | 2230 | set_size: function (w, h) { 2231 | this.size.w = w; 2232 | this.size.h = h; 2233 | this.e_svg.setAttribute('width', w); 2234 | this.e_svg.setAttribute('height', h); 2235 | }, 2236 | 2237 | clear: function () { 2238 | var len = this.lines.length; 2239 | while (len--) { 2240 | this.e_svg.removeChild(this.lines[len]); 2241 | } 2242 | this.lines.length = 0; 2243 | }, 2244 | 2245 | draw_line: function (pout, pin, offset) { 2246 | var line = jm.graph_svg.c('path'); 2247 | line.setAttribute('stroke', this.opts.line_color); 2248 | line.setAttribute('stroke-width', this.opts.line_width); 2249 | line.setAttribute('fill', 'transparent'); 2250 | this.lines.push(line); 2251 | this.e_svg.appendChild(line); 2252 | this._bezier_to(line, pin.x + offset.x, pin.y + offset.y, pout.x + offset.x, pout.y + offset.y); 2253 | }, 2254 | 2255 | copy_to: function (dest_canvas_ctx, callback) { 2256 | var img = new Image(); 2257 | img.onload = function () { 2258 | dest_canvas_ctx.drawImage(img, 0, 0); 2259 | !!callback && callback(); 2260 | } 2261 | img.src = 'data:image/svg+xml;base64,' + btoa(new XMLSerializer().serializeToString(this.e_svg)); 2262 | }, 2263 | 2264 | _bezier_to: function (path, x1, y1, x2, y2) { 2265 | path.setAttribute('d', 'M' + x1 + ' ' + y1 + ' C ' + (x1 + (x2 - x1) * 2 / 3) + ' ' + y1 + ', ' + x1 + ' ' + y2 + ', ' + x2 + ' ' + y2); 2266 | }, 2267 | 2268 | _line_to: function (path, x1, y1, x2, y2) { 2269 | path.setAttribute('d', 'M ' + x1 + ' ' + y1 + ' L ' + x2 + ' ' + y2); 2270 | } 2271 | }; 2272 | 2273 | // view provider 2274 | jm.view_provider = function (jm, options) { 2275 | this.opts = options; 2276 | this.jm = jm; 2277 | this.layout = jm.layout; 2278 | 2279 | this.container = null; 2280 | this.e_panel = null; 2281 | this.e_nodes = null; 2282 | 2283 | this.size = { w: 0, h: 0 }; 2284 | 2285 | this.selected_node = null; 2286 | this.editing_node = null; 2287 | 2288 | this.graph = null; 2289 | }; 2290 | 2291 | jm.view_provider.prototype = { 2292 | init: function () { 2293 | logger.debug('view.init'); 2294 | 2295 | this.container = $i(this.opts.container) ? this.opts.container : $g(this.opts.container); 2296 | if (!this.container) { 2297 | logger.error('the options.view.container was not be found in dom'); 2298 | return; 2299 | } 2300 | this.e_panel = $c('div'); 2301 | this.e_nodes = $c('jmnodes'); 2302 | this.e_editor = $c('input'); 2303 | 2304 | this.graph = this.opts.engine.toLowerCase() === 'svg' ? new jm.graph_svg(this) : new jm.graph_canvas(this); 2305 | 2306 | this.e_panel.className = 'jsmind-inner'; 2307 | this.e_panel.appendChild(this.graph.element()); 2308 | this.e_panel.appendChild(this.e_nodes); 2309 | 2310 | this.e_editor.className = 'jsmind-editor'; 2311 | this.e_editor.type = 'text'; 2312 | 2313 | this.actualZoom = 1; 2314 | this.zoomStep = 0.1; 2315 | this.minZoom = 0.5; 2316 | this.maxZoom = 2; 2317 | 2318 | var v = this; 2319 | jm.util.dom.add_event(this.e_editor, 'keydown', function (e) { 2320 | var evt = e || event; 2321 | if (evt.keyCode == 13) { v.edit_node_end(); evt.stopPropagation(); } 2322 | }); 2323 | jm.util.dom.add_event(this.e_editor, 'blur', function (e) { 2324 | v.edit_node_end(); 2325 | }); 2326 | 2327 | this.container.appendChild(this.e_panel); 2328 | }, 2329 | 2330 | add_event: function (obj, event_name, event_handle) { 2331 | jm.util.dom.add_event(this.e_nodes, event_name, function (e) { 2332 | var evt = e || event; 2333 | event_handle.call(obj, evt); 2334 | }); 2335 | }, 2336 | 2337 | get_binded_nodeid: function (element) { 2338 | if (element == null) { 2339 | return null; 2340 | } 2341 | var tagName = element.tagName.toLowerCase(); 2342 | if (tagName == 'jmnodes' || tagName == 'body' || tagName == 'html') { 2343 | return null; 2344 | } 2345 | if (tagName == 'jmnode' || tagName == 'jmexpander') { 2346 | return element.getAttribute('nodeid'); 2347 | } else { 2348 | return this.get_binded_nodeid(element.parentElement); 2349 | } 2350 | }, 2351 | 2352 | is_expander: function (element) { 2353 | return (element.tagName.toLowerCase() == 'jmexpander'); 2354 | }, 2355 | 2356 | reset: function () { 2357 | logger.debug('view.reset'); 2358 | this.selected_node = null; 2359 | this.clear_lines(); 2360 | this.clear_nodes(); 2361 | this.reset_theme(); 2362 | }, 2363 | 2364 | reset_theme: function () { 2365 | var theme_name = this.jm.options.theme; 2366 | if (!!theme_name) { 2367 | this.e_nodes.className = 'theme-' + theme_name; 2368 | } else { 2369 | this.e_nodes.className = ''; 2370 | } 2371 | }, 2372 | 2373 | reset_custom_style: function () { 2374 | var nodes = this.jm.mind.nodes; 2375 | for (var nodeid in nodes) { 2376 | this.reset_node_custom_style(nodes[nodeid]); 2377 | } 2378 | }, 2379 | 2380 | load: function () { 2381 | logger.debug('view.load'); 2382 | this.init_nodes(); 2383 | }, 2384 | 2385 | expand_size: function () { 2386 | var min_size = this.layout.get_min_size(); 2387 | var min_width = min_size.w + this.opts.hmargin * 2; 2388 | var min_height = min_size.h + this.opts.vmargin * 2; 2389 | var client_w = this.e_panel.clientWidth; 2390 | var client_h = this.e_panel.clientHeight; 2391 | if (client_w < min_width) { client_w = min_width; } 2392 | if (client_h < min_height) { client_h = min_height; } 2393 | this.size.w = client_w; 2394 | this.size.h = client_h; 2395 | }, 2396 | 2397 | init_nodes_size: function (node) { 2398 | var view_data = node._data.view; 2399 | view_data.width = view_data.element.clientWidth; 2400 | view_data.height = view_data.element.clientHeight; 2401 | }, 2402 | 2403 | init_nodes: function () { 2404 | var nodes = this.jm.mind.nodes; 2405 | var doc_frag = $d.createDocumentFragment(); 2406 | for (var nodeid in nodes) { 2407 | this.create_node_element(nodes[nodeid], doc_frag); 2408 | } 2409 | this.e_nodes.appendChild(doc_frag); 2410 | for (var nodeid in nodes) { 2411 | this.init_nodes_size(nodes[nodeid]); 2412 | } 2413 | }, 2414 | 2415 | add_node: function (node) { 2416 | this.create_node_element(node, this.e_nodes); 2417 | this.init_nodes_size(node); 2418 | }, 2419 | 2420 | create_node_element: function (node, parent_node) { 2421 | var view_data = null; 2422 | if ('view' in node._data) { 2423 | view_data = node._data.view; 2424 | } else { 2425 | view_data = {}; 2426 | node._data.view = view_data; 2427 | } 2428 | 2429 | var d = $c('jmnode'); 2430 | if (node.isroot) { 2431 | d.className = 'root'; 2432 | } else { 2433 | var d_e = $c('jmexpander'); 2434 | $t(d_e, '-'); 2435 | d_e.setAttribute('nodeid', node.id); 2436 | d_e.style.visibility = 'hidden'; 2437 | parent_node.appendChild(d_e); 2438 | view_data.expander = d_e; 2439 | } 2440 | if (!!node.topic) { 2441 | if (this.opts.support_html) { 2442 | $h(d, node.topic); 2443 | } else { 2444 | $t(d, node.topic); 2445 | } 2446 | } 2447 | d.setAttribute('nodeid', node.id); 2448 | d.style.visibility = 'hidden'; 2449 | this._reset_node_custom_style(d, node.data); 2450 | 2451 | parent_node.appendChild(d); 2452 | view_data.element = d; 2453 | }, 2454 | 2455 | remove_node: function (node) { 2456 | if (this.selected_node != null && this.selected_node.id == node.id) { 2457 | this.selected_node = null; 2458 | } 2459 | if (this.editing_node != null && this.editing_node.id == node.id) { 2460 | node._data.view.element.removeChild(this.e_editor); 2461 | this.editing_node = null; 2462 | } 2463 | var children = node.children; 2464 | var i = children.length; 2465 | while (i--) { 2466 | this.remove_node(children[i]); 2467 | } 2468 | if (node._data.view) { 2469 | var element = node._data.view.element; 2470 | var expander = node._data.view.expander; 2471 | this.e_nodes.removeChild(element); 2472 | this.e_nodes.removeChild(expander); 2473 | node._data.view.element = null; 2474 | node._data.view.expander = null; 2475 | } 2476 | }, 2477 | 2478 | update_node: function (node) { 2479 | var view_data = node._data.view; 2480 | var element = view_data.element; 2481 | if (!!node.topic) { 2482 | if (this.opts.support_html) { 2483 | $h(element, node.topic); 2484 | } else { 2485 | $t(element, node.topic); 2486 | } 2487 | } 2488 | view_data.width = element.clientWidth; 2489 | view_data.height = element.clientHeight; 2490 | }, 2491 | 2492 | select_node: function (node) { 2493 | if (!!this.selected_node) { 2494 | this.selected_node._data.view.element.className = 2495 | this.selected_node._data.view.element.className.replace(/\s*selected\b/i, ''); 2496 | this.reset_node_custom_style(this.selected_node); 2497 | } 2498 | if (!!node) { 2499 | this.selected_node = node; 2500 | node._data.view.element.className += ' selected'; 2501 | this.clear_node_custom_style(node); 2502 | } 2503 | }, 2504 | 2505 | select_clear: function () { 2506 | this.select_node(null); 2507 | }, 2508 | 2509 | get_editing_node: function () { 2510 | return this.editing_node; 2511 | }, 2512 | 2513 | is_editing: function () { 2514 | return (!!this.editing_node); 2515 | }, 2516 | 2517 | edit_node_begin: function (node) { 2518 | if (!node.topic) { 2519 | logger.warn("don't edit image nodes"); 2520 | return; 2521 | } 2522 | if (this.editing_node != null) { 2523 | this.edit_node_end(); 2524 | } 2525 | this.editing_node = node; 2526 | var view_data = node._data.view; 2527 | var element = view_data.element; 2528 | var topic = node.topic; 2529 | var ncs = getComputedStyle(element); 2530 | this.e_editor.value = topic; 2531 | this.e_editor.style.width = (element.clientWidth - parseInt(ncs.getPropertyValue('padding-left')) - parseInt(ncs.getPropertyValue('padding-right'))) + 'px'; 2532 | element.innerHTML = ''; 2533 | element.appendChild(this.e_editor); 2534 | element.style.zIndex = 5; 2535 | this.e_editor.focus(); 2536 | this.e_editor.select(); 2537 | }, 2538 | 2539 | edit_node_end: function () { 2540 | if (this.editing_node != null) { 2541 | var node = this.editing_node; 2542 | this.editing_node = null; 2543 | var view_data = node._data.view; 2544 | var element = view_data.element; 2545 | var topic = this.e_editor.value; 2546 | element.style.zIndex = 'auto'; 2547 | element.removeChild(this.e_editor); 2548 | if (jm.util.text.is_empty(topic) || node.topic === topic) { 2549 | if (this.opts.support_html) { 2550 | $h(element, node.topic); 2551 | } else { 2552 | $t(element, node.topic); 2553 | } 2554 | } else { 2555 | this.jm.update_node(node.id, topic); 2556 | } 2557 | } 2558 | }, 2559 | 2560 | get_view_offset: function () { 2561 | var bounds = this.layout.bounds; 2562 | var _x = (this.size.w - bounds.e - bounds.w) / 2; 2563 | var _y = this.size.h / 2; 2564 | return { x: _x, y: _y }; 2565 | }, 2566 | 2567 | resize: function () { 2568 | this.graph.set_size(1, 1); 2569 | this.e_nodes.style.width = '1px'; 2570 | this.e_nodes.style.height = '1px'; 2571 | 2572 | this.expand_size(); 2573 | this._show(); 2574 | }, 2575 | 2576 | _show: function () { 2577 | this.graph.set_size(this.size.w, this.size.h); 2578 | this.e_nodes.style.width = this.size.w + 'px'; 2579 | this.e_nodes.style.height = this.size.h + 'px'; 2580 | this.show_nodes(); 2581 | this.show_lines(); 2582 | //this.layout.cache_valid = true; 2583 | this.jm.invoke_event_handle(jm.event_type.resize, { data: [] }); 2584 | }, 2585 | 2586 | zoomIn: function () { 2587 | return this.setZoom(this.actualZoom + this.zoomStep); 2588 | }, 2589 | 2590 | zoomOut: function () { 2591 | return this.setZoom(this.actualZoom - this.zoomStep); 2592 | }, 2593 | 2594 | setZoom: function (zoom) { 2595 | if ((zoom < this.minZoom) || (zoom > this.maxZoom)) { 2596 | return false; 2597 | } 2598 | this.actualZoom = zoom; 2599 | for (var i = 0; i < this.e_panel.children.length; i++) { 2600 | this.e_panel.children[i].style.transform = 'scale(' + zoom + ')'; 2601 | }; 2602 | this.show(true); 2603 | return true; 2604 | 2605 | }, 2606 | 2607 | _center_root: function () { 2608 | // center root node 2609 | var outer_w = this.e_panel.clientWidth; 2610 | var outer_h = this.e_panel.clientHeight; 2611 | if (this.size.w > outer_w) { 2612 | var _offset = this.get_view_offset(); 2613 | this.e_panel.scrollLeft = _offset.x - outer_w / 2; 2614 | } 2615 | if (this.size.h > outer_h) { 2616 | this.e_panel.scrollTop = (this.size.h - outer_h) / 2; 2617 | } 2618 | }, 2619 | 2620 | show: function (keep_center) { 2621 | logger.debug('view.show'); 2622 | this.expand_size(); 2623 | this._show(); 2624 | if (!!keep_center) { 2625 | this._center_root(); 2626 | } 2627 | }, 2628 | 2629 | relayout: function () { 2630 | this.expand_size(); 2631 | this._show(); 2632 | }, 2633 | 2634 | save_location: function (node) { 2635 | var vd = node._data.view; 2636 | vd._saved_location = { 2637 | x: parseInt(vd.element.style.left) - this.e_panel.scrollLeft, 2638 | y: parseInt(vd.element.style.top) - this.e_panel.scrollTop, 2639 | }; 2640 | }, 2641 | 2642 | restore_location: function (node) { 2643 | var vd = node._data.view; 2644 | this.e_panel.scrollLeft = parseInt(vd.element.style.left) - vd._saved_location.x; 2645 | this.e_panel.scrollTop = parseInt(vd.element.style.top) - vd._saved_location.y; 2646 | }, 2647 | 2648 | clear_nodes: function () { 2649 | var mind = this.jm.mind; 2650 | if (mind == null) { 2651 | return; 2652 | } 2653 | var nodes = mind.nodes; 2654 | var node = null; 2655 | for (var nodeid in nodes) { 2656 | node = nodes[nodeid]; 2657 | node._data.view.element = null; 2658 | node._data.view.expander = null; 2659 | } 2660 | this.e_nodes.innerHTML = ''; 2661 | }, 2662 | 2663 | show_nodes: function () { 2664 | var nodes = this.jm.mind.nodes; 2665 | var node = null; 2666 | var node_element = null; 2667 | var expander = null; 2668 | var p = null; 2669 | var p_expander = null; 2670 | var expander_text = '-'; 2671 | var view_data = null; 2672 | var _offset = this.get_view_offset(); 2673 | for (var nodeid in nodes) { 2674 | node = nodes[nodeid]; 2675 | view_data = node._data.view; 2676 | node_element = view_data.element; 2677 | expander = view_data.expander; 2678 | if (!this.layout.is_visible(node)) { 2679 | node_element.style.display = 'none'; 2680 | expander.style.display = 'none'; 2681 | continue; 2682 | } 2683 | this.reset_node_custom_style(node); 2684 | p = this.layout.get_node_point(node); 2685 | view_data.abs_x = _offset.x + p.x; 2686 | view_data.abs_y = _offset.y + p.y; 2687 | node_element.style.left = (_offset.x + p.x) + 'px'; 2688 | node_element.style.top = (_offset.y + p.y) + 'px'; 2689 | node_element.style.display = ''; 2690 | node_element.style.visibility = 'visible'; 2691 | if (!node.isroot && node.children.length > 0) { 2692 | expander_text = node.expanded ? '-' : '+'; 2693 | p_expander = this.layout.get_expander_point(node); 2694 | expander.style.left = (_offset.x + p_expander.x) + 'px'; 2695 | expander.style.top = (_offset.y + p_expander.y) + 'px'; 2696 | expander.style.display = ''; 2697 | expander.style.visibility = 'visible'; 2698 | $t(expander, expander_text); 2699 | } 2700 | // hide expander while all children have been removed 2701 | if (!node.isroot && node.children.length == 0) { 2702 | expander.style.display = 'none'; 2703 | expander.style.visibility = 'hidden'; 2704 | } 2705 | } 2706 | }, 2707 | 2708 | reset_node_custom_style: function (node) { 2709 | this._reset_node_custom_style(node._data.view.element, node.data); 2710 | }, 2711 | 2712 | _reset_node_custom_style: function (node_element, node_data) { 2713 | if ('background-color' in node_data) { 2714 | node_element.style.backgroundColor = node_data['background-color']; 2715 | } 2716 | if ('foreground-color' in node_data) { 2717 | node_element.style.color = node_data['foreground-color']; 2718 | } 2719 | if ('width' in node_data) { 2720 | node_element.style.width = node_data['width'] + 'px'; 2721 | } 2722 | if ('height' in node_data) { 2723 | node_element.style.height = node_data['height'] + 'px'; 2724 | } 2725 | if ('font-size' in node_data) { 2726 | node_element.style.fontSize = node_data['font-size'] + 'px'; 2727 | } 2728 | if ('font-weight' in node_data) { 2729 | node_element.style.fontWeight = node_data['font-weight']; 2730 | } 2731 | if ('font-style' in node_data) { 2732 | node_element.style.fontStyle = node_data['font-style']; 2733 | } 2734 | if ('background-image' in node_data) { 2735 | var backgroundImage = node_data['background-image']; 2736 | if (backgroundImage.startsWith('data') && node_data['width'] && node_data['height']) { 2737 | var img = new Image(); 2738 | 2739 | img.onload = function () { 2740 | var c = $c('canvas'); 2741 | c.width = node_element.clientWidth; 2742 | c.height = node_element.clientHeight; 2743 | var img = this; 2744 | if (c.getContext) { 2745 | var ctx = c.getContext('2d'); 2746 | ctx.drawImage(img, 2, 2, node_element.clientWidth, node_element.clientHeight); 2747 | var scaledImageData = c.toDataURL(); 2748 | node_element.style.backgroundImage = 'url(' + scaledImageData + ')'; 2749 | } 2750 | }; 2751 | img.src = backgroundImage; 2752 | 2753 | } else { 2754 | node_element.style.backgroundImage = 'url(' + backgroundImage + ')'; 2755 | } 2756 | node_element.style.backgroundSize = '99%'; 2757 | 2758 | if ('background-rotation' in node_data) { 2759 | node_element.style.transform = 'rotate(' + node_data['background-rotation'] + 'deg)'; 2760 | } 2761 | } 2762 | }, 2763 | 2764 | clear_node_custom_style: function (node) { 2765 | var node_element = node._data.view.element; 2766 | node_element.style.backgroundColor = ""; 2767 | node_element.style.color = ""; 2768 | }, 2769 | 2770 | clear_lines: function () { 2771 | this.graph.clear(); 2772 | }, 2773 | 2774 | show_lines: function () { 2775 | this.clear_lines(); 2776 | var nodes = this.jm.mind.nodes; 2777 | var node = null; 2778 | var pin = null; 2779 | var pout = null; 2780 | var _offset = this.get_view_offset(); 2781 | for (var nodeid in nodes) { 2782 | node = nodes[nodeid]; 2783 | if (!!node.isroot) { continue; } 2784 | if (('visible' in node._data.layout) && !node._data.layout.visible) { continue; } 2785 | pin = this.layout.get_node_point_in(node); 2786 | pout = this.layout.get_node_point_out(node.parent); 2787 | this.graph.draw_line(pout, pin, _offset); 2788 | } 2789 | }, 2790 | }; 2791 | 2792 | // shortcut provider 2793 | jm.shortcut_provider = function (jm, options) { 2794 | this.jm = jm; 2795 | this.opts = options; 2796 | this.mapping = options.mapping; 2797 | this.handles = options.handles; 2798 | this._mapping = {}; 2799 | }; 2800 | 2801 | jm.shortcut_provider.prototype = { 2802 | init: function () { 2803 | jm.util.dom.add_event($d, 'keydown', this.handler.bind(this)); 2804 | 2805 | this.handles['addchild'] = this.handle_addchild; 2806 | this.handles['addbrother'] = this.handle_addbrother; 2807 | this.handles['editnode'] = this.handle_editnode; 2808 | this.handles['delnode'] = this.handle_delnode; 2809 | this.handles['toggle'] = this.handle_toggle; 2810 | this.handles['up'] = this.handle_up; 2811 | this.handles['down'] = this.handle_down; 2812 | this.handles['left'] = this.handle_left; 2813 | this.handles['right'] = this.handle_right; 2814 | 2815 | for (var handle in this.mapping) { 2816 | if (!!this.mapping[handle] && (handle in this.handles)) { 2817 | this._mapping[this.mapping[handle]] = this.handles[handle]; 2818 | } 2819 | } 2820 | }, 2821 | 2822 | enable_shortcut: function () { 2823 | this.opts.enable = true; 2824 | }, 2825 | 2826 | disable_shortcut: function () { 2827 | this.opts.enable = false; 2828 | }, 2829 | 2830 | handler: function (e) { 2831 | if (this.jm.view.is_editing()) { return; } 2832 | var evt = e || event; 2833 | if (!this.opts.enable) { return true; } 2834 | var kc = evt.keyCode + (evt.metaKey << 13) + (evt.ctrlKey << 12) + (evt.altKey << 11) + (evt.shiftKey << 10); 2835 | if (kc in this._mapping) { 2836 | this._mapping[kc].call(this, this.jm, e); 2837 | } 2838 | }, 2839 | 2840 | handle_addchild: function (_jm, e) { 2841 | var selected_node = _jm.get_selected_node(); 2842 | if (!!selected_node) { 2843 | var nodeid = jm.util.uuid.newid(); 2844 | var node = _jm.add_node(selected_node, nodeid, 'New Node'); 2845 | if (!!node) { 2846 | _jm.select_node(nodeid); 2847 | _jm.begin_edit(nodeid); 2848 | } 2849 | } 2850 | }, 2851 | handle_addbrother: function (_jm, e) { 2852 | var selected_node = _jm.get_selected_node(); 2853 | if (!!selected_node && !selected_node.isroot) { 2854 | var nodeid = jm.util.uuid.newid(); 2855 | var node = _jm.insert_node_after(selected_node, nodeid, 'New Node'); 2856 | if (!!node) { 2857 | _jm.select_node(nodeid); 2858 | _jm.begin_edit(nodeid); 2859 | } 2860 | } 2861 | }, 2862 | handle_editnode: function (_jm, e) { 2863 | var selected_node = _jm.get_selected_node(); 2864 | if (!!selected_node) { 2865 | _jm.begin_edit(selected_node); 2866 | } 2867 | }, 2868 | handle_delnode: function (_jm, e) { 2869 | var selected_node = _jm.get_selected_node(); 2870 | if (!!selected_node && !selected_node.isroot) { 2871 | _jm.select_node(selected_node.parent); 2872 | _jm.remove_node(selected_node); 2873 | } 2874 | }, 2875 | handle_toggle: function (_jm, e) { 2876 | var evt = e || event; 2877 | var selected_node = _jm.get_selected_node(); 2878 | if (!!selected_node) { 2879 | _jm.toggle_node(selected_node.id); 2880 | evt.stopPropagation(); 2881 | evt.preventDefault(); 2882 | } 2883 | }, 2884 | handle_up: function (_jm, e) { 2885 | var evt = e || event; 2886 | var selected_node = _jm.get_selected_node(); 2887 | if (!!selected_node) { 2888 | var up_node = _jm.find_node_before(selected_node); 2889 | if (!up_node) { 2890 | var np = _jm.find_node_before(selected_node.parent); 2891 | if (!!np && np.children.length > 0) { 2892 | up_node = np.children[np.children.length - 1]; 2893 | } 2894 | } 2895 | if (!!up_node) { 2896 | _jm.select_node(up_node); 2897 | } 2898 | evt.stopPropagation(); 2899 | evt.preventDefault(); 2900 | } 2901 | }, 2902 | 2903 | handle_down: function (_jm, e) { 2904 | var evt = e || event; 2905 | var selected_node = _jm.get_selected_node(); 2906 | if (!!selected_node) { 2907 | var down_node = _jm.find_node_after(selected_node); 2908 | if (!down_node) { 2909 | var np = _jm.find_node_after(selected_node.parent); 2910 | if (!!np && np.children.length > 0) { 2911 | down_node = np.children[0]; 2912 | } 2913 | } 2914 | if (!!down_node) { 2915 | _jm.select_node(down_node); 2916 | } 2917 | evt.stopPropagation(); 2918 | evt.preventDefault(); 2919 | } 2920 | }, 2921 | 2922 | handle_left: function (_jm, e) { 2923 | this._handle_direction(_jm, e, jm.direction.left); 2924 | }, 2925 | handle_right: function (_jm, e) { 2926 | this._handle_direction(_jm, e, jm.direction.right); 2927 | }, 2928 | _handle_direction: function (_jm, e, d) { 2929 | var evt = e || event; 2930 | var selected_node = _jm.get_selected_node(); 2931 | var node = null; 2932 | if (!!selected_node) { 2933 | if (selected_node.isroot) { 2934 | var c = selected_node.children; 2935 | var children = []; 2936 | for (var i = 0; i < c.length; i++) { 2937 | if (c[i].direction === d) { 2938 | children.push(i) 2939 | } 2940 | } 2941 | node = c[children[Math.floor((children.length - 1) / 2)]]; 2942 | } 2943 | else if (selected_node.direction === d) { 2944 | var children = selected_node.children; 2945 | var childrencount = children.length; 2946 | if (childrencount > 0) { 2947 | node = children[Math.floor((childrencount - 1) / 2)] 2948 | } 2949 | } else { 2950 | node = selected_node.parent; 2951 | } 2952 | if (!!node) { 2953 | _jm.select_node(node); 2954 | } 2955 | evt.stopPropagation(); 2956 | evt.preventDefault(); 2957 | } 2958 | }, 2959 | }; 2960 | 2961 | 2962 | // plugin 2963 | jm.plugin = function (name, init) { 2964 | this.name = name; 2965 | this.init = init; 2966 | }; 2967 | 2968 | jm.plugins = []; 2969 | 2970 | jm.register_plugin = function (plugin) { 2971 | if (plugin instanceof jm.plugin) { 2972 | jm.plugins.push(plugin); 2973 | } 2974 | }; 2975 | 2976 | jm.init_plugins = function (sender) { 2977 | $w.setTimeout(function () { 2978 | jm._init_plugins(sender); 2979 | }, 0); 2980 | }; 2981 | 2982 | jm._init_plugins = function (sender) { 2983 | var l = jm.plugins.length; 2984 | var fn_init = null; 2985 | for (var i = 0; i < l; i++) { 2986 | fn_init = jm.plugins[i].init; 2987 | if (typeof fn_init === 'function') { 2988 | fn_init(sender); 2989 | } 2990 | } 2991 | }; 2992 | 2993 | // quick way 2994 | jm.show = function (options, mind) { 2995 | var _jm = new jm(options); 2996 | _jm.show(mind); 2997 | return _jm; 2998 | }; 2999 | 3000 | // export jsmind 3001 | if (typeof module !== 'undefined' && typeof exports === 'object') { 3002 | module.exports = jm; 3003 | $w[__name__] = jm; 3004 | } else if (typeof define === 'function' && (define.amd || define.cmd)) { 3005 | define(function () { return jm; }); 3006 | } else { 3007 | $w[__name__] = jm; 3008 | } 3009 | })(typeof window !== 'undefined' ? window : global); 3010 | 3011 | -------------------------------------------------------------------------------- /src/components/JsMind/jsmind.screenshot.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Released under BSD License 3 | * Copyright (c) 2014-2015 hizzgdev@163.com 4 | * 5 | * Project Home: 6 | * https://github.com/hizzgdev/jsmind/ 7 | */ 8 | 9 | (function ($w) { 10 | 'use strict'; 11 | 12 | var __name__ = 'jsMind'; 13 | var jsMind = $w[__name__]; 14 | if (!jsMind) { return; } 15 | if (typeof jsMind.screenshot != 'undefined') { return; } 16 | var $d = $w.document; 17 | var $c = function (tag) { return $d.createElement(tag); }; 18 | 19 | var css = function (cstyle, property_name) { 20 | return cstyle.getPropertyValue(property_name); 21 | }; 22 | var is_visible = function (cstyle) { 23 | var visibility = css(cstyle, 'visibility'); 24 | var display = css(cstyle, 'display'); 25 | return (visibility !== 'hidden' && display !== 'none'); 26 | }; 27 | var jcanvas = {}; 28 | jcanvas.rect = function (ctx, x, y, w, h, r) { 29 | if (w < 2 * r) r = w / 2; 30 | if (h < 2 * r) r = h / 2; 31 | ctx.moveTo(x + r, y); 32 | ctx.arcTo(x + w, y, x + w, y + h, r); 33 | ctx.arcTo(x + w, y + h, x, y + h, r); 34 | ctx.arcTo(x, y + h, x, y, r); 35 | ctx.arcTo(x, y, x + w, y, r); 36 | }; 37 | 38 | jcanvas.text_multiline = function (ctx, text, x, y, w, h, lineheight) { 39 | var line = ''; 40 | var text_len = text.length; 41 | var chars = text.split(''); 42 | var test_line = null; 43 | ctx.textAlign = 'left'; 44 | ctx.textBaseline = 'top'; 45 | for (var i = 0; i < text_len; i++) { 46 | test_line = line + chars[i]; 47 | if (ctx.measureText(test_line).width > w && i > 0) { 48 | ctx.fillText(line, x, y); 49 | line = chars[i]; 50 | y += lineheight; 51 | } else { 52 | line = test_line; 53 | } 54 | } 55 | ctx.fillText(line, x, y); 56 | }; 57 | 58 | jcanvas.text_ellipsis = function (ctx, text, x, y, w, h) { 59 | var center_y = y + h / 2; 60 | var text = jcanvas.fittingString(ctx, text, w); 61 | ctx.textAlign = 'left'; 62 | ctx.textBaseline = 'middle'; 63 | ctx.fillText(text, x, center_y, w); 64 | }; 65 | 66 | jcanvas.fittingString = function (ctx, text, max_width) { 67 | var width = ctx.measureText(text).width; 68 | var ellipsis = '…' 69 | var ellipsis_width = ctx.measureText(ellipsis).width; 70 | if (width <= max_width || width <= ellipsis_width) { 71 | return text; 72 | } else { 73 | var len = text.length; 74 | while (width >= max_width - ellipsis_width && len-- > 0) { 75 | text = text.substring(0, len); 76 | width = ctx.measureText(text).width; 77 | } 78 | return text + ellipsis; 79 | } 80 | }; 81 | 82 | jcanvas.image = function (ctx, url, x, y, w, h, r, rotation, callback) { 83 | var img = new Image(); 84 | img.onload = function () { 85 | ctx.save(); 86 | ctx.translate(x, y); 87 | ctx.save(); 88 | ctx.beginPath(); 89 | jcanvas.rect(ctx, 0, 0, w, h, r); 90 | ctx.closePath(); 91 | ctx.clip(); 92 | ctx.translate(w / 2, h / 2); 93 | ctx.rotate(rotation * Math.PI / 180); 94 | ctx.drawImage(img, -w / 2, -h / 2); 95 | ctx.restore(); 96 | ctx.restore(); 97 | !!callback && callback(); 98 | } 99 | img.src = url; 100 | }; 101 | 102 | jsMind.screenshot = function (jm) { 103 | this.jm = jm; 104 | this.canvas_elem = null; 105 | this.canvas_ctx = null; 106 | this._inited = false; 107 | }; 108 | 109 | jsMind.screenshot.prototype = { 110 | init: function () { 111 | if (this._inited) { return; } 112 | console.log('init'); 113 | var c = $c('canvas'); 114 | var ctx = c.getContext('2d'); 115 | 116 | this.canvas_elem = c; 117 | this.canvas_ctx = ctx; 118 | this.jm.view.e_panel.appendChild(c); 119 | this._inited = true; 120 | this.resize(); 121 | }, 122 | 123 | shoot: function (callback) { 124 | this.init(); 125 | this._draw(function () { 126 | !!callback && callback(); 127 | this.clean(); 128 | }.bind(this)); 129 | this._watermark(); 130 | }, 131 | 132 | shootDownload: function () { 133 | this.shoot(function () { 134 | this._download(); 135 | }.bind(this)); 136 | }, 137 | 138 | shootAsDataURL: function (callback) { 139 | this.shoot(function () { 140 | !!callback && callback(this.canvas_elem.toDataURL()); 141 | }.bind(this)); 142 | }, 143 | 144 | resize: function () { 145 | if (this._inited) { 146 | this.canvas_elem.width = this.jm.view.size.w; 147 | this.canvas_elem.height = this.jm.view.size.h; 148 | } 149 | }, 150 | 151 | clean: function () { 152 | var c = this.canvas_elem; 153 | this.canvas_ctx.clearRect(0, 0, c.width, c.height); 154 | }, 155 | 156 | _draw: function (callback) { 157 | var ctx = this.canvas_ctx; 158 | ctx.textAlign = 'left'; 159 | ctx.textBaseline = 'top'; 160 | this._draw_lines(function () { 161 | this._draw_nodes(callback); 162 | }.bind(this)); 163 | }, 164 | 165 | _watermark: function () { 166 | var c = this.canvas_elem; 167 | var ctx = this.canvas_ctx; 168 | ctx.textAlign = 'right'; 169 | ctx.textBaseline = 'bottom'; 170 | ctx.fillStyle = '#000'; 171 | ctx.font = '11px Verdana,Arial,Helvetica,sans-serif'; 172 | ctx.fillText('hizzgdev.github.io/jsmind', c.width - 5.5, c.height - 2.5); 173 | ctx.textAlign = 'left'; 174 | ctx.fillText($w.location, 5.5, c.height - 2.5); 175 | }, 176 | 177 | _draw_lines: function (callback) { 178 | this.jm.view.graph.copy_to(this.canvas_ctx, callback); 179 | }, 180 | 181 | _draw_nodes: function (callback) { 182 | var nodes = this.jm.mind.nodes; 183 | var node; 184 | for (var nodeid in nodes) { 185 | node = nodes[nodeid]; 186 | this._draw_node(node); 187 | } 188 | 189 | function check_nodes_ready() { 190 | console.log('check_node_ready' + new Date()); 191 | var allOk = true; 192 | for (var nodeid in nodes) { 193 | node = nodes[nodeid]; 194 | allOk = allOk & node.ready; 195 | } 196 | 197 | if (!allOk) { 198 | $w.setTimeout(check_nodes_ready, 200); 199 | } else { 200 | $w.setTimeout(callback, 200); 201 | } 202 | } 203 | check_nodes_ready(); 204 | }, 205 | 206 | _draw_node: function (node) { 207 | var ctx = this.canvas_ctx; 208 | var view_data = node._data.view; 209 | var node_element = view_data.element; 210 | var ncs = getComputedStyle(node_element); 211 | if (!is_visible(ncs)) { 212 | node.ready = true; 213 | return; 214 | } 215 | 216 | var bgcolor = css(ncs, 'background-color'); 217 | var round_radius = parseInt(css(ncs, 'border-top-left-radius')); 218 | var color = css(ncs, 'color'); 219 | var padding_left = parseInt(css(ncs, 'padding-left')); 220 | var padding_right = parseInt(css(ncs, 'padding-right')); 221 | var padding_top = parseInt(css(ncs, 'padding-top')); 222 | var padding_bottom = parseInt(css(ncs, 'padding-bottom')); 223 | var text_overflow = css(ncs, 'text-overflow'); 224 | var font = css(ncs, 'font-style') + ' ' + 225 | css(ncs, 'font-variant') + ' ' + 226 | css(ncs, 'font-weight') + ' ' + 227 | css(ncs, 'font-size') + '/' + css(ncs, 'line-height') + ' ' + 228 | css(ncs, 'font-family'); 229 | 230 | var rb = { 231 | x: view_data.abs_x, 232 | y: view_data.abs_y, 233 | w: view_data.width + 1, 234 | h: view_data.height + 1 235 | }; 236 | var tb = { 237 | x: rb.x + padding_left, 238 | y: rb.y + padding_top, 239 | w: rb.w - padding_left - padding_right, 240 | h: rb.h - padding_top - padding_bottom 241 | }; 242 | 243 | ctx.font = font; 244 | ctx.fillStyle = bgcolor; 245 | ctx.beginPath(); 246 | jcanvas.rect(ctx, rb.x, rb.y, rb.w, rb.h, round_radius); 247 | ctx.closePath(); 248 | ctx.fill(); 249 | 250 | ctx.fillStyle = color; 251 | if ('background-image' in node.data) { 252 | var backgroundUrl = css(ncs, 'background-image').slice(5, -2); 253 | node.ready = false; 254 | var rotation = 0; 255 | if ('background-rotation' in node.data) { 256 | rotation = node.data['background-rotation']; 257 | } 258 | jcanvas.image(ctx, backgroundUrl, rb.x, rb.y, rb.w, rb.h, round_radius, rotation, 259 | function () { 260 | node.ready = true; 261 | }); 262 | } 263 | if (!!node.topic) { 264 | if (text_overflow === 'ellipsis') { 265 | jcanvas.text_ellipsis(ctx, node.topic, tb.x, tb.y, tb.w, tb.h); 266 | } else { 267 | var line_height = parseInt(css(ncs, 'line-height')); 268 | jcanvas.text_multiline(ctx, node.topic, tb.x, tb.y, tb.w, tb.h, line_height); 269 | } 270 | } 271 | if (!!view_data.expander) { 272 | this._draw_expander(view_data.expander); 273 | } 274 | if (!('background-image' in node.data)) { 275 | node.ready = true; 276 | } 277 | }, 278 | 279 | _draw_expander: function (expander) { 280 | var ctx = this.canvas_ctx; 281 | var ncs = getComputedStyle(expander); 282 | if (!is_visible(ncs)) { return; } 283 | 284 | var style_left = css(ncs, 'left'); 285 | var style_top = css(ncs, 'top'); 286 | var font = css(ncs, 'font'); 287 | var left = parseInt(style_left); 288 | var top = parseInt(style_top); 289 | var is_plus = expander.innerHTML === '+'; 290 | 291 | ctx.lineWidth = 1; 292 | 293 | ctx.beginPath(); 294 | ctx.arc(left + 7, top + 7, 5, 0, Math.PI * 2, true); 295 | ctx.moveTo(left + 10, top + 7); 296 | ctx.lineTo(left + 4, top + 7); 297 | if (is_plus) { 298 | ctx.moveTo(left + 7, top + 4); 299 | ctx.lineTo(left + 7, top + 10); 300 | } 301 | ctx.closePath(); 302 | ctx.stroke(); 303 | }, 304 | 305 | _download: function () { 306 | var c = this.canvas_elem; 307 | var name = this.jm.mind.name + '.png'; 308 | 309 | if (navigator.msSaveBlob && (!!c.msToBlob)) { 310 | var blob = c.msToBlob(); 311 | navigator.msSaveBlob(blob, name); 312 | } else { 313 | var bloburl = this.canvas_elem.toDataURL(); 314 | var anchor = $c('a'); 315 | if ('download' in anchor) { 316 | anchor.style.visibility = 'hidden'; 317 | anchor.href = bloburl; 318 | anchor.download = name; 319 | $d.body.appendChild(anchor); 320 | var evt = $d.createEvent('MouseEvents'); 321 | evt.initEvent('click', true, true); 322 | anchor.dispatchEvent(evt); 323 | $d.body.removeChild(anchor); 324 | } else { 325 | location.href = bloburl; 326 | } 327 | } 328 | }, 329 | 330 | jm_event_handle: function (type, data) { 331 | if (type === jsMind.event_type.resize) { 332 | this.resize(); 333 | } 334 | } 335 | }; 336 | 337 | var screenshot_plugin = new jsMind.plugin('screenshot', function (jm) { 338 | var jss = new jsMind.screenshot(jm); 339 | jm.screenshot = jss; 340 | jm.shoot = function () { 341 | jss.shoot(); 342 | }; 343 | jm.add_event_listener(function (type, data) { 344 | jss.jm_event_handle.call(jss, type, data); 345 | }); 346 | }); 347 | 348 | jsMind.register_plugin(screenshot_plugin); 349 | 350 | })(window); 351 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | 4 | import JsMind from '../dist/build' 5 | // import JsMind from './components/JsMind/index' 6 | Vue.use(JsMind) 7 | 8 | new Vue({ 9 | el: '#app', 10 | render: h => h(App) 11 | }) 12 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var webpack = require('webpack') 3 | 4 | module.exports = { 5 | entry: process.env.NODE_ENV == 'development' ? './src/main.js' : './src/components/JsMind/index.js', 6 | output: { 7 | path: path.resolve(__dirname, './dist'), 8 | publicPath: '/dist/', 9 | filename: 'build.js', 10 | library: 'vue-jsMind', 11 | libraryTarget: 'umd', 12 | umdNamedDefine: true 13 | }, 14 | module: { 15 | rules: [ 16 | { 17 | test: /\.css$/, 18 | use: [ 19 | 'style-loader', 20 | 'css-loader' 21 | ], 22 | }, { 23 | test: /\.vue$/, 24 | loader: 'vue-loader', 25 | options: { 26 | loaders: { 27 | } 28 | // other vue-loader options go here 29 | } 30 | }, 31 | { 32 | test: /\.js$/, 33 | loader: 'babel-loader', 34 | exclude: /node_modules/ 35 | }, 36 | { 37 | test: /\.(png|jpg|gif|svg)$/, 38 | loader: 'url-loader', 39 | options: { 40 | name: '[name].[ext]?[hash]' 41 | } 42 | }, 43 | { 44 | test: /\.(woff|ttf)$/, 45 | loader: 'url-loader' 46 | } 47 | ] 48 | }, 49 | resolve: { 50 | alias: { 51 | 'vue$': 'vue/dist/vue.esm.js' 52 | }, 53 | extensions: ['*', '.js', '.vue', '.json'] 54 | }, 55 | devServer: { 56 | historyApiFallback: true, 57 | noInfo: true, 58 | overlay: true 59 | }, 60 | performance: { 61 | hints: false 62 | }, 63 | devtool: '#eval-source-map' 64 | } 65 | 66 | if (process.env.NODE_ENV === 'production') { 67 | module.exports.devtool = '#source-map' 68 | // http://vue-loader.vuejs.org/en/workflow/production.html 69 | module.exports.plugins = (module.exports.plugins || []).concat([ 70 | new webpack.DefinePlugin({ 71 | 'process.env': { 72 | NODE_ENV: '"production"' 73 | } 74 | }), 75 | new webpack.optimize.UglifyJsPlugin({ 76 | sourceMap: true, 77 | compress: { 78 | warnings: false 79 | } 80 | }), 81 | new webpack.LoaderOptionsPlugin({ 82 | minimize: true 83 | }) 84 | ]) 85 | } 86 | --------------------------------------------------------------------------------