├── .babelrc
├── .editorconfig
├── .eslintrc.json
├── .gitattributes
├── .github
└── ISSUE_TEMPLATE.md
├── .gitignore
├── .npmignore
├── .travis.yml
├── LICENSE
├── README.md
├── __test__
└── index.html
├── assets
└── .gitkeep
├── build
├── build-style.js
├── webpack.dev.config.js
├── webpack.dist.prod.config.js
└── webpack.docs.config.js
├── docs
├── css
│ └── main.css
├── index.html
├── js
│ ├── 1.chunk.js
│ └── main.js
├── styles
│ ├── images
│ │ ├── iconpriority.png
│ │ ├── iconprogress.png
│ │ ├── icons.png
│ │ └── template.png
│ └── minder.css
└── vendors.js
├── examples
├── app.vue
├── docs.html
├── index.html
├── main.js
├── routers
│ └── editor.vue
└── styles
│ ├── images
│ ├── iconpriority.png
│ ├── iconprogress.png
│ ├── icons.png
│ └── template.png
│ └── minder.css
├── package.json
└── src
├── components
├── .gitkeep
├── breadcrumb
│ └── index.vue
├── editor
│ └── index.vue
├── icon
│ ├── icons.js
│ └── index.vue
├── navigator
│ └── index.vue
├── search
│ └── index.vue
├── template-list
│ └── index.vue
└── theme-list
│ └── index.vue
├── directives
├── clickoutside.js
└── visible.js
├── editor.js
├── expose-editor.js
├── filter
└── lang.js
├── hotbox.js
├── index.js
├── lang.js
├── locale
├── format.js
├── index.js
└── lang
│ ├── en-US.js
│ ├── minder
│ └── zh-CN.js
│ ├── zh-CN.js
│ └── zh-TW.js
├── minder.js
├── mixins
└── locale.js
├── module
└── imageicon.js
├── runtime
├── clipboard-mimetype.js
├── clipboard.js
├── container.js
├── drag.js
├── fsm.js
├── history.js
├── hotbox.js
├── input.js
├── jumping.js
├── minder.js
├── node.js
├── priority.js
├── progress.js
└── receiver.js
├── services
├── config.js
└── memory.js
├── styles
├── README.md
├── _navigator.less
├── _tool_group.less
├── _vars.less
├── editor.less
├── imageDialog.less
├── images
│ ├── iconpriority.png
│ ├── iconprogress.png
│ ├── icons.png
│ └── template.png
└── topTab
│ ├── appearance
│ ├── colorPanel.less
│ ├── fontOperator.less
│ ├── layout.less
│ ├── styleOperator.less
│ ├── templatePanel.less
│ └── themePanel.less
│ ├── idea
│ ├── appendNode.less
│ ├── arrange.less
│ ├── hyperlink.less
│ ├── image.less
│ ├── note.less
│ ├── noteEditor.less
│ ├── operation.less
│ ├── priority.less
│ ├── progress.less
│ ├── resource.less
│ └── undoRedo.less
│ ├── searchBox.less
│ ├── topTab.less
│ └── view
│ ├── expand.less
│ ├── search.less
│ └── select.less
├── tool
├── debug.js
├── format.js
├── innertext.js
├── jsondiff.js
├── key.js
└── keymap.js
└── utils
├── assist.js
├── calcTextareaHeight.js
├── csv.js
└── date.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["env", {
4 | "modules": false,
5 | "targets": {
6 | "browsers": [
7 | "> 1%",
8 | "last 2 versions",
9 | "not ie <= 20",
10 | "not ie_mob <= 100",
11 | "not ff <= 100",
12 | "not and_ff <= 100",
13 | "not Edge <= 100",
14 | "Android >= 4.0"
15 | ]
16 | }
17 | }],
18 | "stage-2"
19 | ],
20 | "plugins": ["transform-runtime", "add-module-exports", "transform-es2015-modules-umd"],
21 | "comments": false
22 | }
23 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # https://github.com/jiangtao/mydotfiles/blob/master/.config/.editorconfig
2 | root = true
3 |
4 | [*]
5 |
6 | # We recommend you to keep these unchanged
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = false
10 | insert_final_newline = true
11 | indent_style = space
12 | indent_size = 2
13 | [*.{html}]
14 | indent_size = 4
15 |
16 |
17 | [*.md]
18 | insert_final_newline = false
19 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "parserOptions": {
4 | "ecmaVersion": 6,
5 | "sourceType": "module"
6 | },
7 | "env": {
8 | "browser": true
9 | },
10 | "extends": "eslint:recommended",
11 | "plugins": [ "html" ],
12 | "rules": {
13 | "no-undef":"warn",
14 | "no-mixed-spaces-and-tabs": "warn"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | src/styles/**/* linguist-vendored=false
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
4 |
7 |
8 |
9 | ### 操作系统/浏览器 版本号
10 |
11 |
12 | ### Vue 版本号
13 |
14 |
15 | ### 能够复现问题的在线示例
16 |
17 |
18 | ### 复现步骤
19 |
20 | ### 问题现象,以及你期望的结果是怎样的?
21 |
22 | ### 你估计可能的原因是什么(选填)?
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .idea
3 | .ipr
4 | .iws
5 | *.diff
6 | *.patch
7 | *.bak
8 | .DS_Store
9 | node_modules/
10 | node_modules2/
11 | .project
12 | .settings
13 | npm-debug.log
14 | .*proj
15 | .svn/
16 | *.swp
17 | *.swo
18 | *.log
19 | test/dist/
20 | dist/
21 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .*
2 | *.md
3 | *.yml
4 | build/
5 | node_modules/
6 | test/
7 | gulpfile.js
8 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "4"
4 | script:
5 | - npm run test
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Vue Minder
2 |
3 | 本组件主要内聚百度脑图的能力,使脑图可以便于在Vue生态下进行扩展。
4 | [DEMO地址](https://www.imjiangtao.com/vue-minder/#!/editor)
5 |
6 | ---
7 |
8 | Vue Minder 基于百度脑图核心代码[kityminder-core](https://github.com/fex-team/kityminder-core),主要满足在Vue项目中开发和使用。感谢[FEX Team](https://github.com/fex-team)
9 |
10 | ## 安装
11 |
12 | ```
13 | yarn add vue-minder
14 | npm install vue-minder
15 | ```
16 |
17 | ## 使用
18 |
19 |
20 |
21 | ```vue
22 |
30 |
31 | ```
32 |
33 | ### props说明
34 |
35 | - show-search-box 是否显示搜索框
36 | - show-template 是否显示模板
37 | - show-theme 是否显示主题
38 | - show-navigator 是否显示导航器
39 | - enable 禁用或编辑模式
40 | - import-data 导入的数据
41 |
42 | [具体使用可参考](./examples)
43 |
44 | ## 与百度脑图不同之处
45 |
46 | - 数据中的字段 text 改为 name
47 |
48 | ## 开发和贡献
49 |
50 | 目前主要用于vue1.0项目中,感兴趣可升级为2.0版本,甚至以后3.0版本
51 |
52 | ## 版本规范
53 |
54 | ### 分支规范
55 |
56 | - master 为 vue1.0版本
57 | - 2.x 为 vue2.x版本
58 | - 3.x 为 vue3.x版本
59 |
60 | ### npm包规范
61 |
62 | - 2.x 为vue2.x版本
63 |
64 | ## 文件说明
65 |
66 | ```
67 | ├── __test__ 测试dist包运行
68 | ├── assets 资源文件
69 | ├── build build脚本
70 | ├── dist 生产环境目录
71 | ├── docs 预览demo目录
72 | ├── examples 预览demo源码,便于本地开发
73 | ├── src 核心源代码
74 | ```
75 | ## TODO
76 | - [ ] Support Vue2.7, Vue2.7作为过渡版本,相对来说较稳定,考虑支持
77 |
--------------------------------------------------------------------------------
/__test__/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiangtao/vue-minder/b4cbaec72624f0fe761f7cc5286cec193cb3a571/assets/.gitkeep
--------------------------------------------------------------------------------
/build/build-style.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp');
2 | var cleanCSS = require('gulp-clean-css');
3 | var less = require('gulp-less');
4 | var rename = require('gulp-rename');
5 | var autoprefixer = require('gulp-autoprefixer');
6 |
7 | // 编译less
8 | gulp.task('css', function () {
9 | gulp.src('../src/styles/editor.less')
10 | .pipe(less())
11 | .pipe(autoprefixer({
12 | browsers: ['last 2 versions', 'ie > 8']
13 | }))
14 | .pipe(cleanCSS())
15 | .pipe(rename('minder.css'))
16 | .pipe(gulp.dest('../dist/styles'));
17 | });
18 |
19 | // 拷贝图片
20 | gulp.task('images', function () {
21 | gulp.src('../src/styles/images/*.*')
22 | .pipe(gulp.dest('../dist/styles/images'));
23 | });
24 |
25 | gulp.task('default', ['css', 'images']);
26 |
--------------------------------------------------------------------------------
/build/webpack.dev.config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 本地预览
3 | */
4 |
5 | var path = require('path');
6 | var webpack = require('webpack');
7 | var ExtractTextPlugin = require('extract-text-webpack-plugin');
8 | var HtmlWebpackIncludeAssetsPlugin = require('html-webpack-include-assets-plugin');
9 |
10 |
11 | module.exports = {
12 | // 入口
13 | entry: {
14 | main: './examples/main',
15 | vendors: ['vue', 'vue-router']
16 | },
17 | // 输出
18 | output: {
19 | path: path.join(__dirname, '../examples/docs'),
20 | publicPath: '/examples/docs',
21 | filename: '[name].js',
22 | chunkFilename: '[name].chunk.js'
23 | },
24 | // 加载器
25 | module: {
26 | loaders: [
27 | {test: /\.vue$/, loader: 'vue'},
28 | {test: /\.js$/, loader: 'babel', exclude: /node_modules/},
29 | {test: /\.js$/, loader: 'babel', include: /vue/},
30 | {test: /\.css$/, loader: 'style!css!autoprefixer'},
31 | {test: /\.less$/, loader: 'style!css!less'},
32 | {test: /\.scss$/, loader: 'style!css!sass?sourceMap'},
33 | {test: /\.(gif|jpg|png|woff|svg|eot|ttf)\??.*$/, loader: 'url-loader?limit=8192'},
34 | {test: /\.(html|tpl)$/, loader: 'html-loader'}
35 | ]
36 | },
37 | vue: {
38 | loaders: {
39 | css: ExtractTextPlugin.extract(
40 | 'style-loader',
41 | 'css-loader?sourceMap',
42 | {
43 | publicPath: '/examples/docs'
44 | }
45 | ),
46 | less: ExtractTextPlugin.extract(
47 | 'vue-style-loader',
48 | 'css-loader!less-loader',
49 | {
50 | publicPath: '/examples/docs'
51 | }
52 | ),
53 | js: 'babel'
54 | }
55 | },
56 | resolve: {
57 | // require时省略的扩展名,如:require('module') 不需要module.js
58 | extensions: ['', '.js', '.vue'],
59 | alias: {
60 | minder: '../../src/index'
61 | }
62 | },
63 | plugins: [
64 | new ExtractTextPlugin('[name].css', {allChunks: true, resolve: ['modules']}), // 提取CSS
65 | new webpack.optimize.CommonsChunkPlugin('vendors', 'vendors.js')
66 | ],
67 | devServer: {
68 | }
69 | };
70 |
--------------------------------------------------------------------------------
/build/webpack.dist.prod.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | entry: {
6 | main: './src/index.js'
7 | },
8 | output: {
9 | path: path.resolve(__dirname, '../dist'),
10 | publicPath: '/dist/',
11 | filename: 'minder.min.js',
12 | library: 'minder',
13 | libraryTarget: 'umd',
14 | umdNamedDefine: true
15 | },
16 | externals: {
17 | vue: {
18 | root: 'Vue',
19 | commonjs: 'vue',
20 | commonjs2: 'vue',
21 | amd: 'vue'
22 | }
23 | },
24 | resolve: {
25 | extensions: ['', '.js', '.vue']
26 | },
27 | module: {
28 | loaders: [{
29 | test: /\.vue$/,
30 | loader: 'vue'
31 | }, {
32 | test: /\.js$/,
33 | loader: 'babel',
34 | exclude: /node_modules/
35 | }, {
36 | test: /\.css$/,
37 | loader: 'style!css!autoprefixer'
38 | }, {
39 | test: /\.less$/,
40 | loader: 'style!css!less'
41 | }, {
42 | test: /\.(gif|jpg|png|woff|svg|eot|ttf)\??.*$/,
43 | loader: 'url?limit=8192'
44 | }, {
45 | test: /\.(html|tpl)$/,
46 | loader: 'vue-html'
47 | }]
48 | },
49 | plugins: [
50 | new webpack.DefinePlugin({
51 | 'process.env': {
52 | NODE_ENV: '"production"'
53 | }
54 | }),
55 | new webpack.optimize.UglifyJsPlugin({
56 | compress: {
57 | warnings: false
58 | }
59 | }),
60 | new webpack.optimize.OccurenceOrderPlugin()
61 | ]
62 | }
63 |
--------------------------------------------------------------------------------
/build/webpack.docs.config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 本地预览
3 | */
4 |
5 | var path = require('path');
6 | var webpack = require('webpack');
7 | var ExtractTextPlugin = require('extract-text-webpack-plugin');
8 | var HtmlWebpackPlugin = require('html-webpack-plugin');
9 |
10 | module.exports = {
11 | // 入口
12 | entry: {
13 | main: './examples/main',
14 | vendors: ['vue', 'vue-router']
15 | },
16 | // 输出
17 | output: {
18 | path: path.join(__dirname, '../docs'),
19 | publicPath: '',
20 | filename: 'js/[name].js',
21 | chunkFilename: 'js/[name].chunk.js'
22 | },
23 | // 加载器
24 | module: {
25 | loaders: [
26 | {test: /\.vue$/, loader: 'vue'},
27 | {test: /\.js$/, loader: 'babel', exclude: /node_modules/},
28 | {test: /\.js$/, loader: 'babel', include: /vue/},
29 | {test: /\.css$/, loader: 'style!css!autoprefixer'},
30 | {test: /\.less$/, loader: 'style!css!less'},
31 | {test: /\.scss$/, loader: 'style!css!sass?sourceMap'},
32 | {test: /\.(gif|jpg|png|woff|svg|eot|ttf)\??.*$/, loader: 'url-loader?limit=8192'},
33 | {test: /\.(html|tpl)$/, loader: 'html-loader'}
34 | ]
35 | },
36 | vue: {
37 | loaders: {
38 | css: ExtractTextPlugin.extract(
39 | 'style-loader',
40 | 'css-loader?sourceMap',
41 | {
42 | publicPath: '/examples/docs'
43 | }
44 | ),
45 | less: ExtractTextPlugin.extract(
46 | 'vue-style-loader',
47 | 'css-loader!less-loader',
48 | {
49 | publicPath: '/examples/docs'
50 | }
51 | ),
52 | js: 'babel'
53 | }
54 | },
55 | resolve: {
56 | // require时省略的扩展名,如:require('module') 不需要module.js
57 | extensions: ['', '.js', '.vue'],
58 | alias: {
59 | minder: '../../src/index'
60 | }
61 | },
62 | plugins: [
63 | new ExtractTextPlugin('css/[name].css', {allChunks: true, resolve: ['modules']}), // 提取CSS
64 | new webpack.optimize.CommonsChunkPlugin('vendors', 'vendors.js'),
65 | new webpack.DefinePlugin({
66 | 'process.env': {
67 | NODE_ENV: '"production"'
68 | }
69 | }),
70 | new webpack.optimize.UglifyJsPlugin({
71 | compress: {
72 | warnings: false
73 | }
74 | }),
75 | new webpack.optimize.OccurenceOrderPlugin(),
76 | new HtmlWebpackPlugin({
77 | title: 'Vue Minder based on kityminder-core',
78 | prod: true,
79 | template: 'examples/docs.html'
80 | }),
81 | ]
82 | };
83 |
84 |
85 | // var path = require('path');
86 | // var webpack = require('webpack');
87 | // var HtmlWebpackPlugin = require('html-webpack-plugin');
88 | // var HtmlWebpackIncludeAssetsPlugin = require('html-webpack-include-assets-plugin');
89 | //
90 | // module.exports = {
91 | // entry: {
92 | // main: './examples/main',
93 | // vendors: ['vue', 'vue-router']
94 | // },
95 | // output: {
96 | // path: path.join(__dirname, '../docs'),
97 | // publicPath: '',
98 | // filename: 'js/[name].js?v=[hash:7]',
99 | // chunkFilename: 'js/[name].chunk.js'
100 | // },
101 | // externals: {
102 | // vue: {
103 | // root: 'Vue',
104 | // commonjs: 'vue',
105 | // commonjs2: 'vue',
106 | // amd: 'vue'
107 | // }
108 | // },
109 | // resolve: {
110 | // // require时省略的扩展名,如:require('module') 不需要module.js
111 | // extensions: ['', '.js', '.vue'],
112 | // alias: {
113 | // minder: path.join(__dirname, '../src/index.js')
114 | // }
115 | // },
116 | // module: {
117 | // loaders: [{
118 | // test: /\.vue$/,
119 | // loader: 'vue'
120 | // }, {
121 | // test: /\.js$/,
122 | // loader: 'babel',
123 | // exclude: /node_modules/
124 | // }, {
125 | // test: /\.css$/,
126 | // loader: 'style!css!autoprefixer'
127 | // }, {
128 | // test: /\.less$/,
129 | // loader: 'style!css!less'
130 | // }, {
131 | // test: /\.(gif|jpg|png|woff|svg|eot|ttf)\??.*$/,
132 | // loader: 'url?limit=8192'
133 | // }, {
134 | // test: /\.(html|tpl)$/,
135 | // loader: 'vue-html'
136 | // }]
137 | // },
138 | // plugins: [
139 | // new webpack.DefinePlugin({
140 | // 'process.env': {
141 | // NODE_ENV: '"production"'
142 | // }
143 | // }),
144 | // new webpack.optimize.OccurenceOrderPlugin(),
145 | // new HtmlWebpackPlugin({
146 | // title: 'Vue Minder based on kityminder-core',
147 | // prod: true
148 | // }),
149 | // new HtmlWebpackIncludeAssetsPlugin({
150 | // assets: [
151 | // 'https://unpkg.com/vue@1.0.28/dist/vue.min.js'
152 | // ],
153 | // append: false
154 | // })// 提取第三方库
155 | // ]
156 | // };
157 | // if(process.env.DEV_ENV !== 'local') {
158 | // module.exports.plugins.unshift(new webpack.optimize.UglifyJsPlugin({
159 | // compress: {
160 | // warnings: false
161 | // }
162 | // }));
163 | // }
164 |
--------------------------------------------------------------------------------
/docs/css/main.css:
--------------------------------------------------------------------------------
1 | .hotbox{font-family:Arial,Hiragino Sans GB,Microsoft YaHei,WenQuanYi Micro Hei,sans-serif;left:0;top:0}.hotbox,.hotbox .state{position:absolute;overflow:visible}.hotbox .state{display:none}.hotbox .state .center .button,.hotbox .state .ring .button{position:absolute;width:70px;height:70px;margin-left:-35px;margin-top:-35px;border-radius:100%;box-shadow:0 0 30px rgba(0,0,0,.3)}.hotbox .state .center .key,.hotbox .state .center .label,.hotbox .state .ring .key,.hotbox .state .ring .label{display:block;text-align:center;line-height:1.4em;vertical-align:middle}.hotbox .state .center .label,.hotbox .state .ring .label{font-size:16px;margin-top:17px;color:#000;font-weight:400;line-height:1em}.hotbox .state .center .key,.hotbox .state .ring .key{font-size:12px;color:#999}.hotbox .state .ring-shape{position:absolute;left:-25px;top:-25px;border:25px solid rgba(0,0,0,.3);border-radius:100%;box-sizing:content-box}.hotbox .state .bottom,.hotbox .state .top{position:absolute;white-space:nowrap}.hotbox .state .bottom .button,.hotbox .state .top .button{display:inline-block;padding:8px 15px;margin:0 10px;border-radius:15px;box-shadow:0 0 30px rgba(0,0,0,.3);position:relative}.hotbox .state .bottom .button .label,.hotbox .state .top .button .label{font-size:14px;line-height:14px;vertical-align:middle;color:#000;line-height:1em}.hotbox .state .bottom .button .key,.hotbox .state .top .button .key{font-size:12px;line-height:12px;vertical-align:middle;color:#999;margin-left:3px}.hotbox .state .bottom .button .key:before,.hotbox .state .top .button .key:before{content:"("}.hotbox .state .bottom .button .key:after,.hotbox .state .top .button .key:after{content:")"}.hotbox .state .button{background:#f9f9f9;overflow:hidden;cursor:default}.hotbox .state .button .key,.hotbox .state .button .label{opacity:.3}.hotbox .state .button.enabled{background:#fff}.hotbox .state .button.enabled .key,.hotbox .state .button.enabled .label{opacity:1}.hotbox .state .button.enabled:hover{background:#e87372}.hotbox .state .button.enabled:hover .label{color:#fff}.hotbox .state .button.enabled:hover .key{color:#fadfdf}.hotbox .state .button.enabled.selected{-webkit-animation:selected .1s ease;background:#e45d5c}.hotbox .state .button.enabled.selected .label{color:#fff}.hotbox .state .button.enabled.selected .key{color:#fadfdf}.hotbox .state .button.enabled.pressed,.hotbox .state .button.enabled:active{background:#ff974d}.hotbox .state .button.enabled.pressed .label,.hotbox .state .button.enabled:active .label{color:#fff}.hotbox .state .button.enabled.pressed .key,.hotbox .state .button.enabled:active .key{color:#fff0e6}.hotbox .state.active{display:block}.hotbox-key-receiver{position:absolute;left:-999999px;top:-999999px;width:20px;height:20px;outline:none;margin:0}nav[_v-af8a0ebe]{margin-bottom:40px}li[_v-af8a0ebe]{display:inline-block}li+li[_v-af8a0ebe]{border-left:1px solid #bbb;padding-left:5px;margin-left:5px}.v-link-active[_v-af8a0ebe]{color:#bbb}.template__list,.theme__panel{float:left}.minder__top-tab{position:relative;z-index:3}.nav-bar{background-color:#fff!important}.nav-btn{vertical-align:middle}.icon{width:20px!important;height:20px!important}.zoom--pan .text{color:#222;display:inline-block;text-align:center;width:30px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.zoom-in .icon{font-size:24px}.template__list .dropdown-menu{display:block}.theme__panel .dropdown-menu{display:block}.theme__panel .theme-item-selected .caret{margin-left:0}.breadcrumb__list{display:-ms-flexbox;display:flex;color:#777;line-height:30px;font-size:16px;padding:5px 0;margin-left:10px;float:left}.breadcrumb__list li{list-style:none;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.breadcrumb__list .item{cursor:pointer;display:block;max-width:100px;border-radius:3px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.breadcrumb__list .arrow{float:left;margin:0 6px;font-size:13px}
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 | Vue Minder Based kityminder
--------------------------------------------------------------------------------
/docs/js/1.chunk.js:
--------------------------------------------------------------------------------
1 | webpackJsonp([1],{79:function(e,n,t){var o,a,d;!function(r,i){a=[n,t(113)],o=i,d="function"==typeof o?o.apply(n,a):o,!(void 0!==d&&(e.exports=d))}(this,function(n,t){"use strict";function o(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(n,"__esModule",{value:!0});o(t);n.default={data:function(){return{defaultStyle:{width:"1000px",height:"600px"},enable:!1}},computed:{},methods:{generateIndex:function(e){return"_"+(e.data.id||e.data._id)},setColor:function(e,n){e.setData(n),e.render()}},beforeDestroy:function(){this.$refs.minder.setMemory()},ready:function(){var e=this,n={root:{data:{name:"中心主题",hexType:"project",id:1},children:[{data:{name:"111",expandState:"collapse",hexType:"project",id:2},children:[{data:{name:"1111111",hexType:"dir",id:3},children:[]},{data:{name:"1111111",hexType:"dir",id:4},children:[]},{data:{name:"1111111",hexType:"dir",id:5},children:[{data:{name:"哈哈",hexType:"page",id:6},children:[]},{data:{name:"哈哈1",hexType:"page",id:7},children:[]},{data:{name:"哈哈2",hexType:"page",id:8},children:[]}]},{data:{name:"1111111",hexType:"dir",id:9},children:[]},{data:{name:"11在",hexType:"page",id:10},children:[]}]},{data:{name:"222",id:11,hexType:"page"},children:[]},{data:{name:"哈哈哈",hexType:"page",id:12},children:[]},{data:{name:"333",id:13,hexType:"page"},children:[]},{data:{name:"444",hexType:"app",id:14},children:[]},{data:{name:"555",id:15,hexType:"page"},children:[]},{data:{name:"666",id:16,hexType:"page"},children:[]}]},template:"default",theme:"fresh-blue",version:"1.4.43"};this.$nextTick(function(){var t=this,o=this.$refs.minder.minder;setTimeout(function(){o.importJson(t.$refs.minder.getMemory(n))},1e3),window.onbeforeunload=function(e){t.$refs.minder.setMemory()},o.on("editText",function(e,n){e.minder.getSelectedNode()}),o.on("selectionchange",function(n){e.minder=n.minder;var t=n.minder.getSelectedNode();e.currentNode=t,t&&t.parent&&setTimeout(function(){t.parent.layout()},0),e.lock&&t,e.lock=!0}),o.on("beforeExecCommand",function(e){var n=e.minder.getSelectedNode();/arrange/i.test(e.commandName)?console.log("arrange",n,e):/append/i.test(e.commandName)?console.log("add",n):/remove/i.test(e.commandName)&&console.log("remove",n)}),o.on("beforeExecCommand",function(e){var n=e.minder.getSelectedNode();console.log(n),/append/i.test(e.commandName)?console.log("add",n):/remove/i.test(e.commandName)&&console.log("remove",n)}),o.on("AfterExecCommand",function(e){var n=e.minder.getSelectedNode();console.log("after",n),/append/i.test(e.commandName)?console.log("after add",n):/remove/i.test(e.commandName)&&console.log("after remove",n)})})}},e.exports=n.default})},113:function(e,n,t){e.exports={default:t(124),__esModule:!0}},124:function(e,n,t){t(32),t(31),e.exports=t(162)},162:function(e,n,t){var o=t(8),a=t(75);e.exports=t(1).getIterator=function(e){var n=a(e);if("function"!=typeof n)throw TypeError(e+" is not iterable!");return o(n.call(e))}},183:function(e,n){},201:function(e,n){e.exports=" "},210:function(e,n,t){var o,a,d={};t(183),o=t(79),a=t(201),e.exports=o||{},e.exports.__esModule&&(e.exports=e.exports.default);var r="function"==typeof e.exports?e.exports.options||(e.exports.options={}):e.exports;a&&(r.template=a),r.computed||(r.computed={}),Object.keys(d).forEach(function(e){var n=d[e];r.computed[e]=function(){return n}})}});
--------------------------------------------------------------------------------
/docs/styles/images/iconpriority.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiangtao/vue-minder/b4cbaec72624f0fe761f7cc5286cec193cb3a571/docs/styles/images/iconpriority.png
--------------------------------------------------------------------------------
/docs/styles/images/iconprogress.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiangtao/vue-minder/b4cbaec72624f0fe761f7cc5286cec193cb3a571/docs/styles/images/iconprogress.png
--------------------------------------------------------------------------------
/docs/styles/images/icons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiangtao/vue-minder/b4cbaec72624f0fe761f7cc5286cec193cb3a571/docs/styles/images/icons.png
--------------------------------------------------------------------------------
/docs/styles/images/template.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiangtao/vue-minder/b4cbaec72624f0fe761f7cc5286cec193cb3a571/docs/styles/images/template.png
--------------------------------------------------------------------------------
/examples/app.vue:
--------------------------------------------------------------------------------
1 |
4 |
23 |
24 |
25 |
26 |
40 |
--------------------------------------------------------------------------------
/examples/docs.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Vue Minder Based kityminder
6 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/examples/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | <%= htmlWebpackPlugin.options.title %>
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/examples/main.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by aresn on 16/6/20.
3 | */
4 | import Vue from 'vue'
5 | import VueRouter from 'vue-router';
6 | import App from './app.vue';
7 | import minder from '../src/index';
8 |
9 | Vue.use(VueRouter);
10 | Vue.use(minder);
11 | // 开启debug模式
12 | Vue.config.debug = true;
13 |
14 | // 路由配置
15 | var router = new VueRouter();
16 |
17 | router.map({
18 | '/editor': {
19 | component: function (resolve) {
20 | require(['./routers/editor.vue'], resolve);
21 | }
22 | }
23 | });
24 |
25 | router.beforeEach(function () {
26 | window.scrollTo(0, 0);
27 | });
28 |
29 | router.afterEach(function (transition) {
30 |
31 | });
32 |
33 | router.redirect({
34 | '*': "/editor"
35 | });
36 | router.start(App, '#app');
37 |
--------------------------------------------------------------------------------
/examples/routers/editor.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
279 |
281 |
--------------------------------------------------------------------------------
/examples/styles/images/iconpriority.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiangtao/vue-minder/b4cbaec72624f0fe761f7cc5286cec193cb3a571/examples/styles/images/iconpriority.png
--------------------------------------------------------------------------------
/examples/styles/images/iconprogress.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiangtao/vue-minder/b4cbaec72624f0fe761f7cc5286cec193cb3a571/examples/styles/images/iconprogress.png
--------------------------------------------------------------------------------
/examples/styles/images/icons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiangtao/vue-minder/b4cbaec72624f0fe761f7cc5286cec193cb3a571/examples/styles/images/icons.png
--------------------------------------------------------------------------------
/examples/styles/images/template.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiangtao/vue-minder/b4cbaec72624f0fe761f7cc5286cec193cb3a571/examples/styles/images/template.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-minder",
3 | "version": "1.0.0",
4 | "title": "Vue kityminder",
5 | "description": "Vue Kityminder",
6 | "homepage": "https://github.com/hexyun/vue-minder",
7 | "keywords": [
8 | "kityminder",
9 | "vue",
10 | "vue.js",
11 | "component",
12 | "components",
13 | "ui",
14 | "framework",
15 | "baidu naotu"
16 | ],
17 | "main": "dist/minder.min.js",
18 | "files": [
19 | "dist",
20 | "src"
21 | ],
22 | "scripts": {
23 | "dev": "npm run build:style && npm run copy:example && webpack-dev-server --content-base examples/ --open --inline --hot --compress --history-api-fallback --port 10000 --config build/webpack.dev.config.js",
24 | "build:style": "gulp --gulpfile build/build-style.js",
25 | "docs": "webpack --config build/webpack.docs.config.js && npm run copy:docs",
26 | "build:prod": "npm run build:style && webpack --config build/webpack.dist.prod.config.js",
27 | "build": "rm -fr docs dist && npm run svgo && npm run build:prod && npm run docs",
28 | "svgo": "svgo --enable=removeXMLNS src/components/icon/icons/*.svg",
29 | "copy:docs": "cp -r dist/styles docs",
30 | "copy:example": "cp -r dist/styles examples",
31 | "copy": "npm run copy:docs && npm run copy:example",
32 | "lint": "eslint --fix --ext .js,.vue src",
33 | "test": "npm run build && npm run lint",
34 | "prepublish": "npm run build"
35 | },
36 | "repository": {
37 | "type": "git",
38 | "url": "https://github.com/vue-minder"
39 | },
40 | "author": "jiangtao",
41 | "license": "MIT",
42 | "bugs": {
43 | "url": "https://github.com/vue-minder/issues"
44 | },
45 | "dependencies": {
46 | "bootstrap": "^3.4.1",
47 | "core-js": "^2.4.1",
48 | "hotbox": "fex-team/hotbox",
49 | "kity": "^2.0.4",
50 | "kityminder-core": "github:jiangtao/kityminder-core",
51 | "vue": "1.0.28"
52 | },
53 | "devDependencies": {
54 | "autoprefixer-loader": "^2.0.0",
55 | "babel": "^6.3.13",
56 | "babel-core": "^6.11.4",
57 | "babel-loader": "^6.2.4",
58 | "babel-plugin-add-module-exports": "^1.0.0",
59 | "babel-plugin-transform-runtime": "^6.23.0",
60 | "babel-preset-env": "^1.7.0",
61 | "babel-preset-es2015": "^6.24.1",
62 | "babel-preset-stage-2": "^6.24.1",
63 | "css-loader": "^0.23.1",
64 | "ejs-html-loader": "^4.0.1",
65 | "eslint": "^3.12.2",
66 | "eslint-plugin-html": "^1.7.0",
67 | "extract-text-webpack-plugin": "^1.0.1",
68 | "file-loader": "^0.8.5",
69 | "gulp": "^3.9.1",
70 | "gulp-autoprefixer": "^3.1.1",
71 | "gulp-clean-css": "^2.0.13",
72 | "gulp-less": "^3.1.0",
73 | "gulp-rename": "^1.2.2",
74 | "html-loader": "^0.3.0",
75 | "html-webpack-include-assets-plugin": "^1.0.7",
76 | "html-webpack-plugin": "^3.2.0",
77 | "less": "^2.7.1",
78 | "less-loader": "^2.2.3",
79 | "style-loader": "^0.13.1",
80 | "url-loader": "^0.5.7",
81 | "vue-hot-reload-api": "^1.3.3",
82 | "vue-html-loader": "^1.2.3",
83 | "vue-loader": "^8.5.3",
84 | "vue-router": "^0.7.13",
85 | "vue-style-loader": "^1.0.0",
86 | "webpack": "^1.13.1",
87 | "webpack-dev-server": "^1.16.1"
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/components/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiangtao/vue-minder/b4cbaec72624f0fe761f7cc5286cec193cb3a571/src/components/.gitkeep
--------------------------------------------------------------------------------
/src/components/breadcrumb/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 | {{item.data.name}}
5 | >
6 |
7 |
8 |
9 |
42 |
128 |
--------------------------------------------------------------------------------
/src/components/editor/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
9 |
10 |
11 |
15 |
16 |
17 |
18 |
29 |
252 |
--------------------------------------------------------------------------------
/src/components/icon/icons.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'expand': {
3 | name: '缩放',
4 | svg: require('./icons/expand.svg')
5 | },
6 | 'zoom_out': {
7 | name: '缩放',
8 | svg: require('./icons/zoom_out.svg')
9 | },
10 | 'camera': {
11 | name: '聚焦',
12 | svg: require('./icons/camera.svg')
13 | },
14 | 'zoom_out': {
15 | name: '缩放',
16 | svg: require('./icons/zoom_out.svg')
17 | },
18 | 'zoom_out': {
19 | name: '缩放',
20 | svg: require('./icons/zoom_out.svg')
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/components/icon/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
34 |
--------------------------------------------------------------------------------
/src/components/search/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 | {{'第 ' + curIndex + ' 条,共 ' + resultNum + ' 条'}}
13 |
14 |
15 |
18 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
172 |
--------------------------------------------------------------------------------
/src/components/template-list/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
21 |
87 |
--------------------------------------------------------------------------------
/src/components/theme-list/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
27 |
118 |
--------------------------------------------------------------------------------
/src/directives/clickoutside.js:
--------------------------------------------------------------------------------
1 | export default {
2 | bind () {
3 | this.documentHandler = (e) => {
4 | if (this.el.contains(e.target)) {
5 | return false;
6 | }
7 | if (this.expression) {
8 | this.vm[this.expression]();
9 | }
10 | };
11 | document.addEventListener('click', this.documentHandler);
12 | },
13 | update () {
14 |
15 | },
16 | unbind () {
17 | document.removeEventListener('click', this.documentHandler);
18 | }
19 | };
20 |
--------------------------------------------------------------------------------
/src/directives/visible.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @Date 2019-06-04
3 | * @author jiangtao
4 | * 目的是支持子组件中的method可以被父调用
5 | * 处理vue1.0中 v-if v-show false情况下, refs为空的问题
6 | *
7 | */
8 | export default {
9 | bind() {
10 | this.documentHandler = null;
11 | this._i = 0;
12 | },
13 | update() {
14 | let flag = true;
15 | this._i = 0;
16 | if(this.expression) {
17 | let splits = this.expression.split(/\&\&/g), key;
18 | while(flag && this._i < splits.length) {
19 | key = splits[this._i++];
20 | if(key && (key = key.trim())) {
21 | flag &= this.vm[key];
22 | }
23 | }
24 | }
25 | this.documentHandler = (e) => {
26 | this.el.style.visibility = !flag ? 'hidden' : 'visible';
27 | };
28 | window.requestAnimationFrame(this.documentHandler);
29 | },
30 | unbind() {
31 | if(this.documentHandler) window.cancelAnimationFrame(this.documentHandler);
32 | this._i = 0;
33 | this.documentHandler = null;
34 | }
35 | };
36 |
--------------------------------------------------------------------------------
/src/editor.js:
--------------------------------------------------------------------------------
1 | define(function(require, exports, module) {
2 |
3 | /**
4 | * 运行时
5 | */
6 | var runtimes = [];
7 |
8 | function assemble(runtime) {
9 | runtimes.push(runtime);
10 | }
11 |
12 | function KMEditor(selector, blackList) {
13 | this.selector = selector;
14 | for (var i = 0; i < runtimes.length; i++) {
15 | if (typeof runtimes[i] == 'function') {
16 | runtimes[i].call(this, this);
17 | }
18 | }
19 | }
20 |
21 | KMEditor.assemble = assemble;
22 |
23 | assemble(require('./runtime/container'));
24 | assemble(require('./runtime/fsm'));
25 | assemble(require('./runtime/minder'));
26 | assemble(require('./runtime/receiver'));
27 | assemble(require('./runtime/hotbox'));
28 | assemble(require('./runtime/input'));
29 | assemble(require('./runtime/clipboard-mimetype'));
30 | assemble(require('./runtime/clipboard'));
31 | assemble(require('./runtime/drag'));
32 | assemble(require('./runtime/node'));
33 | assemble(require('./runtime/history'));
34 | assemble(require('./runtime/jumping'));
35 | assemble(require('./runtime/priority'));
36 | assemble(require('./runtime/progress'));
37 |
38 |
39 | return module.exports = KMEditor;
40 | });
41 |
--------------------------------------------------------------------------------
/src/expose-editor.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileOverview
3 | *
4 | * 打包暴露
5 | *
6 | * @author: techird
7 | * @copyright: Baidu FEX, 2014
8 | */
9 | define('expose-editor', function(require, exports, module) {
10 | return module.exports = kityminder.Editor = require('./editor');
11 | });
--------------------------------------------------------------------------------
/src/filter/lang.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | // minder lang
3 | Vue.filter('ml', function(value, key, lang = 'zh-CN') {
4 | const conf = require(`../locale/lang/minder/${lang}.js`)
5 | return conf[key][value]
6 | })
7 |
--------------------------------------------------------------------------------
/src/hotbox.js:
--------------------------------------------------------------------------------
1 | import Hotbox from 'hotbox/src/hotbox'
2 | define(function(require, exports, module) {
3 | return module.exports = Hotbox;
4 | });
5 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import kity from 'kity';
2 | import kityminder from 'kityminder-core';
3 | import Editor from './components/editor';
4 | import './module/imageicon'
5 |
6 | const MindEditor = {
7 | Minder: Editor
8 | };
9 |
10 | const install = function(Vue, opts = {}) {
11 | Object.keys(MindEditor).forEach((key) => {
12 | Vue.component(key, MindEditor[key]);
13 | });
14 | };
15 |
16 | // auto install
17 | if(typeof window !== 'undefined' && window.Vue) {
18 | install(window.Vue);
19 | }
20 |
21 | module.exports = Object.assign(MindEditor, {install}); // eslint-disable-line no-undef
22 |
--------------------------------------------------------------------------------
/src/lang.js:
--------------------------------------------------------------------------------
1 | define(function(require, exports, module) {
2 |
3 | });
--------------------------------------------------------------------------------
/src/locale/format.js:
--------------------------------------------------------------------------------
1 | /**
2 | * String format template
3 | * - Inspired:
4 | * https://github.com/Matt-Esch/string-template/index.js
5 | */
6 |
7 | const RE_NARGS = /(%|)\{([0-9a-zA-Z_]+)\}/g;
8 |
9 | export default function(Vue) {
10 | const { hasOwn } = Vue.util;
11 |
12 | /**
13 | * template
14 | *
15 | * @param {String} string
16 | * @param {Array} ...args
17 | * @return {String}
18 | */
19 |
20 | function template(string, ...args) {
21 | if (args.length === 1 && typeof args[0] === 'object') {
22 | args = args[0];
23 | }
24 |
25 | if (!args || !args.hasOwnProperty) {
26 | args = {};
27 | }
28 |
29 | return string.replace(RE_NARGS, (match, prefix, i, index) => {
30 | let result;
31 |
32 | if (string[index - 1] === '{' &&
33 | string[index + match.length] === '}') {
34 | return i;
35 | } else {
36 | result = hasOwn(args, i) ? args[i] : null;
37 | if (result === null || result === undefined) {
38 | return '';
39 | }
40 |
41 | return result;
42 | }
43 | });
44 | }
45 |
46 | return template;
47 | }
48 |
--------------------------------------------------------------------------------
/src/locale/index.js:
--------------------------------------------------------------------------------
1 | // https://github.com/ElemeFE/element/blob/dev/src/locale/index.js
2 |
3 | import defaultLang from './lang/zh-CN';
4 | import Vue from 'vue';
5 | import deepmerge from 'deepmerge';
6 | import Format from './format';
7 |
8 | const format = Format(Vue);
9 | let lang = defaultLang;
10 | let merged = false;
11 | let i18nHandler = function() {
12 | const vuei18n = Object.getPrototypeOf(this || Vue).$t;
13 | if (typeof vuei18n === 'function') {
14 | if (!merged) {
15 | merged = true;
16 | Vue.locale(
17 | Vue.config.lang,
18 | deepmerge(lang, Vue.locale(Vue.config.lang) || {}, { clone: true })
19 | );
20 | }
21 | return vuei18n.apply(this, arguments);
22 | }
23 | };
24 |
25 | export const t = function(path, options) {
26 | let value = i18nHandler.apply(this, arguments);
27 | if (value !== null && value !== undefined) return value;
28 |
29 | const array = path.split('.');
30 | let current = lang;
31 |
32 | for (let i = 0, j = array.length; i < j; i++) {
33 | const property = array[i];
34 | value = current[property];
35 | if (i === j - 1) return format(value, options);
36 | if (!value) return '';
37 | current = value;
38 | }
39 | return '';
40 | };
41 |
42 | export const use = function(l) {
43 | lang = l || lang;
44 | };
45 |
46 | export const i18n = function(fn) {
47 | i18nHandler = fn || i18nHandler;
48 | };
49 |
50 | export default { use, t, i18n };
--------------------------------------------------------------------------------
/src/locale/lang/en-US.js:
--------------------------------------------------------------------------------
1 | export default {
2 | i: {
3 | select: {
4 | placeholder: 'Select',
5 | noMatch: 'No matching data'
6 | },
7 | table: {
8 | noDataText: 'No Data',
9 | noFilteredDataText: 'No filter data',
10 | confirmFilter: 'Confirm',
11 | resetFilter: 'Reset',
12 | clearFilter: 'All'
13 | },
14 | datepicker: {
15 | selectDate: 'Select date',
16 | selectTime: 'Select time',
17 | startTime: 'Start Time',
18 | endTime: 'End Time',
19 | clear: 'Clear',
20 | ok: 'OK',
21 | month: '',
22 | month1: 'January',
23 | month2: 'February',
24 | month3: 'March',
25 | month4: 'April',
26 | month5: 'May',
27 | month6: 'June',
28 | month7: 'July',
29 | month8: 'August',
30 | month9: 'September',
31 | month10: 'October',
32 | month11: 'November',
33 | month12: 'December',
34 | year: '',
35 | weeks: {
36 | sun: 'Sun',
37 | mon: 'Mon',
38 | tue: 'Tue',
39 | wed: 'Wed',
40 | thu: 'Thu',
41 | fri: 'Fri',
42 | sat: 'Sat'
43 | },
44 | months: {
45 | m1: 'Jan',
46 | m2: 'Feb',
47 | m3: 'Mar',
48 | m4: 'Apr',
49 | m5: 'May',
50 | m6: 'Jun',
51 | m7: 'Jul',
52 | m8: 'Aug',
53 | m9: 'Sep',
54 | m10: 'Oct',
55 | m11: 'Nov',
56 | m12: 'Dec'
57 | }
58 | },
59 | transfer: {
60 | titles: {
61 | source: 'Source',
62 | target: 'Target'
63 | },
64 | filterPlaceholder: 'Search here',
65 | notFoundText: 'Not Found'
66 | },
67 | modal: {
68 | okText: 'OK',
69 | cancelText: 'Cancel'
70 | },
71 | poptip: {
72 | okText: 'OK',
73 | cancelText: 'Cancel'
74 | },
75 | page: {
76 | prev: 'Previous Page',
77 | next: 'Next Page',
78 | total: 'Total',
79 | item: 'item',
80 | items: 'items',
81 | prev5: 'Previous 5 Pages',
82 | next5: 'Next 5 Pages',
83 | page: '/page',
84 | goto: 'Goto',
85 | p: ''
86 | },
87 | rate: {
88 | star: 'Star',
89 | stars: 'Stars'
90 | },
91 | tree: {
92 | emptyText: 'No Data'
93 | }
94 | }
95 | };
--------------------------------------------------------------------------------
/src/locale/lang/minder/zh-CN.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | 'template': {
3 | 'default': '思维导图',
4 | 'tianpan': '天盘图',
5 | 'structure': '组织结构图',
6 | 'filetree': '目录组织图',
7 | 'right': '逻辑结构图',
8 | 'fish-bone': '鱼骨头图'
9 | },
10 | 'theme': {
11 | 'classic': '脑图经典',
12 | 'classic-compact': '紧凑经典',
13 | 'snow': '温柔冷光',
14 | 'snow-compact': '紧凑冷光',
15 | 'fish': '鱼骨图',
16 | 'wire': '线框',
17 | 'fresh-red': '清新红',
18 | 'fresh-soil': '泥土黄',
19 | 'fresh-green': '文艺绿',
20 | 'fresh-blue': '天空蓝',
21 | 'fresh-purple': '浪漫紫',
22 | 'fresh-pink': '脑残粉',
23 | 'fresh-red-compat': '紧凑红',
24 | 'fresh-soil-compat': '紧凑黄',
25 | 'fresh-green-compat': '紧凑绿',
26 | 'fresh-blue-compat': '紧凑蓝',
27 | 'fresh-purple-compat': '紧凑紫',
28 | 'fresh-pink-compat': '紧凑粉',
29 | 'tianpan': '经典天盘',
30 | 'tianpan-compact': '紧凑天盘'
31 | },
32 | 'maintopic': '中心主题',
33 | 'topic': '分支主题',
34 | 'panels': {
35 | 'history': '历史',
36 | 'template': '模板',
37 | 'theme': '皮肤',
38 | 'layout': '布局',
39 | 'style': '样式',
40 | 'font': '文字',
41 | 'color': '颜色',
42 | 'background': '背景',
43 | 'insert': '插入',
44 | 'arrange': '调整',
45 | 'nodeop': '当前',
46 | 'priority': '优先级',
47 | 'progress': '进度',
48 | 'resource': '资源',
49 | 'note': '备注',
50 | 'attachment': '附件',
51 | 'word': '文字'
52 | },
53 | 'error_message': {
54 | 'title': '哎呀,脑图出错了',
55 |
56 | 'err_load': '加载脑图失败',
57 | 'err_save': '保存脑图失败',
58 | 'err_network': '网络错误',
59 | 'err_doc_resolve': '文档解析失败',
60 | 'err_unknown': '发生了奇怪的错误',
61 | 'err_localfile_read': '文件读取错误',
62 | 'err_download': '文件下载失败',
63 | 'err_remove_share': '取消分享失败',
64 | 'err_create_share': '分享失败',
65 | 'err_mkdir': '目录创建失败',
66 | 'err_ls': '读取目录失败',
67 | 'err_share_data': '加载分享内容出错',
68 | 'err_share_sync_fail': '分享内容同步失败',
69 | 'err_move_file': '文件移动失败',
70 | 'err_rename': '重命名失败',
71 |
72 | 'unknownreason': '可能是外星人篡改了代码...',
73 | 'pcs_code': {
74 | 3: '不支持此接口',
75 | 4: '没有权限执行此操作',
76 | 5: 'IP未授权',
77 | 110: '用户会话已过期,请重新登录',
78 | 31001: '数据库查询错误',
79 | 31002: '数据库连接错误',
80 | 31003: '数据库返回空结果',
81 | 31021: '网络错误',
82 | 31022: '暂时无法连接服务器',
83 | 31023: '输入参数错误',
84 | 31024: 'app id为空',
85 | 31025: '后端存储错误',
86 | 31041: '用户的cookie不是合法的百度cookie',
87 | 31042: '用户未登陆',
88 | 31043: '用户未激活',
89 | 31044: '用户未授权',
90 | 31045: '用户不存在',
91 | 31046: '用户已经存在',
92 | 31061: '文件已经存在',
93 | 31062: '文件名非法',
94 | 31063: '文件父目录不存在',
95 | 31064: '无权访问此文件',
96 | 31065: '目录已满',
97 | 31066: '文件不存在',
98 | 31067: '文件处理出错',
99 | 31068: '文件创建失败',
100 | 31069: '文件拷贝失败',
101 | 31070: '文件删除失败',
102 | 31071: '不能读取文件元信息',
103 | 31072: '文件移动失败',
104 | 31073: '文件重命名失败',
105 | 31079: '未找到文件MD5,请使用上传API上传整个文件。',
106 | 31081: 'superfile创建失败',
107 | 31082: 'superfile 块列表为空',
108 | 31083: 'superfile 更新失败',
109 | 31101: 'tag系统内部错误',
110 | 31102: 'tag参数错误',
111 | 31103: 'tag系统错误',
112 | 31110: '未授权设置此目录配额',
113 | 31111: '配额管理只支持两级目录',
114 | 31112: '超出配额',
115 | 31113: '配额不能超出目录祖先的配额',
116 | 31114: '配额不能比子目录配额小',
117 | 31141: '请求缩略图服务失败',
118 | 31201: '签名错误',
119 | 31202: '文件不存在',
120 | 31203: '设置acl失败',
121 | 31204: '请求acl验证失败',
122 | 31205: '获取acl失败',
123 | 31206: 'acl不存在',
124 | 31207: 'bucket已存在',
125 | 31208: '用户请求错误',
126 | 31209: '服务器错误',
127 | 31210: '服务器不支持',
128 | 31211: '禁止访问',
129 | 31212: '服务不可用',
130 | 31213: '重试出错',
131 | 31214: '上传文件data失败',
132 | 31215: '上传文件meta失败',
133 | 31216: '下载文件data失败',
134 | 31217: '下载文件meta失败',
135 | 31218: '容量超出限额',
136 | 31219: '请求数超出限额',
137 | 31220: '流量超出限额',
138 | 31298: '服务器返回值KEY非法',
139 | 31299: '服务器返回值KEY不存在'
140 | }
141 | },
142 | 'ui': {
143 | 'shared_file_title': '[分享的] {0} (只读)',
144 | 'load_share_for_edit': '正在加载分享的文件...',
145 | 'share_sync_success': '分享内容已同步',
146 | 'recycle_clear_confirm': '确认清空回收站么?清空后的文件无法恢复。',
147 |
148 | 'fullscreen_exit_hint': '按 Esc 或 F11 退出全屏',
149 |
150 | 'error_detail': '详细信息',
151 | 'copy_and_feedback': '复制并反馈',
152 | 'move_file_confirm': '确定把 "{0}" 移动到 "{1}" 吗?',
153 | 'rename': '重命名',
154 | 'rename_success': '{0} 重命名成功',
155 | 'move_success': '{0} 移动成功到 {1}',
156 | 'command': {
157 | 'appendsiblingnode': '插入同级主题',
158 | 'appendparentnode': '插入上级主题',
159 | 'appendchildnode': '插入下级主题',
160 | 'removenode': '删除',
161 | 'editnode': '编辑',
162 | 'arrangeup': '上移',
163 | 'arrangedown': '下移',
164 | 'resetlayout': '整理布局',
165 | 'expandtoleaf': '展开全部节点',
166 | 'expandtolevel1': '展开到一级节点',
167 | 'expandtolevel2': '展开到二级节点',
168 | 'expandtolevel3': '展开到三级节点',
169 | 'expandtolevel4': '展开到四级节点',
170 | 'expandtolevel5': '展开到五级节点',
171 | 'expandtolevel6': '展开到六级节点',
172 | 'fullscreen': '全屏',
173 | 'outline': '大纲'
174 | },
175 |
176 | 'search': '搜索',
177 |
178 | 'expandtoleaf': '展开',
179 |
180 | 'back': '返回',
181 |
182 | 'undo': '撤销 (Ctrl + Z)',
183 | 'redo': '重做 (Ctrl + Y)',
184 |
185 | 'tabs': {
186 | 'idea': '思路',
187 | 'appearence': '外观',
188 | 'view': '视图'
189 | },
190 |
191 | 'quickvisit': {
192 | 'new': '新建 (Ctrl + Alt + N)',
193 | 'save': '保存 (Ctrl + S)',
194 | 'share': '分享 (Ctrl + Alt + S)',
195 | 'feedback': '反馈问题(F1)',
196 | 'editshare': '编辑'
197 | },
198 |
199 | 'menu': {
200 |
201 | 'mainmenutext': '百度脑图', // 主菜单按钮文本
202 |
203 | 'newtab': '新建',
204 | 'opentab': '打开',
205 | 'savetab': '保存',
206 | 'sharetab': '分享',
207 | 'preferencetab': '设置',
208 | 'helptab': '帮助',
209 | 'feedbacktab': '反馈',
210 | 'recenttab': '最近使用',
211 | 'netdisktab': '百度云存储',
212 | 'localtab': '本地文件',
213 | 'drafttab': '草稿箱',
214 | 'downloadtab': '导出到本地',
215 | 'createsharetab': '当前脑图',
216 | 'managesharetab': '已分享',
217 |
218 | 'newheader': '新建脑图',
219 | 'openheader': '打开',
220 | 'saveheader': '保存到',
221 | 'draftheader': '草稿箱',
222 | 'shareheader': '分享我的脑图',
223 | 'downloadheader': '导出到指定格式',
224 | 'preferenceheader': '偏好设置',
225 | 'helpheader': '帮助',
226 | 'feedbackheader': '反馈'
227 | },
228 |
229 | 'mydocument': '我的文档',
230 | 'emptydir': '目录为空!',
231 | 'pickfile': '选择文件...',
232 | 'acceptfile': '支持的格式:{0}',
233 | 'dropfile': '或将文件拖至此处',
234 | 'unsupportedfile': '不支持的文件格式',
235 | 'untitleddoc': '未命名文档',
236 | 'overrideconfirm': '{0} 已存在,确认覆盖吗?',
237 | 'checklogin': '检查登录状态中...',
238 | 'loggingin': '正在登录...',
239 | 'recent': '最近打开',
240 | 'clearrecent': '清空',
241 | 'clearrecentconfirm': '确认清空最近文档列表?',
242 | 'cleardraft': '清空',
243 | 'cleardraftconfirm': '确认清空草稿箱?',
244 |
245 | 'none_share': '不分享',
246 | 'public_share': '公开分享',
247 | 'password_share': '私密分享',
248 | 'email_share': '邮件邀请',
249 | 'url_share': '脑图 URL 地址:',
250 | 'sns_share': '社交网络分享:',
251 | 'sns_share_text': '“{0}” - 我用百度脑图制作的思维导图,快看看吧!(地址:{1})',
252 | 'none_share_description': '不分享当前脑图',
253 | 'public_share_description': '创建任何人可见的分享',
254 | 'share_button_text': '创建',
255 | 'password_share_description': '创建需要密码才可见的分享',
256 | 'email_share_description': '创建指定人可见的分享,您还可以允许他们编辑',
257 | 'ondev': '敬请期待!',
258 | 'create_share_failed': '分享失败:{0}',
259 | 'remove_share_failed': '删除失败:{1}',
260 | 'copy': '复制',
261 | 'copied': '已复制',
262 | 'shared_tip': '当前脑图被 {0} 分享,你可以修改之后保存到自己的网盘上或再次分享',
263 | 'current_share': '当前脑图',
264 | 'manage_share': '我的分享',
265 | 'share_remove_action': '不分享该脑图',
266 | 'share_view_action': '打开分享地址',
267 | 'share_edit_action': '编辑分享的文件',
268 |
269 | 'login': '登录',
270 | 'logout': '注销',
271 | 'switchuser': '切换账户',
272 | 'userinfo': '个人信息',
273 | 'gotonetdisk': '我的网盘',
274 | 'requirelogin': '请 登录 后使用',
275 | 'saveas': '保存为',
276 | 'filename': '文件名',
277 | 'fileformat': '保存格式',
278 | 'save': '保存',
279 | 'mkdir': '新建目录',
280 | 'recycle': '回收站',
281 | 'newdir': '未命名目录',
282 |
283 | 'bold': '加粗',
284 | 'italic': '斜体',
285 | 'forecolor': '字体颜色',
286 | 'fontfamily': '字体',
287 | 'fontsize': '字号',
288 | 'layoutstyle': '主题',
289 | 'node': '节点操作',
290 | 'saveto': '另存为',
291 | 'hand': '允许拖拽',
292 | 'camera': '定位根节点',
293 | 'zoom-in': '放大(Ctrl+)',
294 | 'zoom-out': '缩小(Ctrl-)',
295 | 'markers': '标签',
296 | 'resource': '资源',
297 | 'help': '帮助',
298 | 'preference': '偏好设置',
299 | 'expandnode': '展开到叶子',
300 | 'collapsenode': '收起到一级节点',
301 | 'template': '模板',
302 | 'theme': '皮肤',
303 | 'clearstyle': '清除样式',
304 | 'copystyle': '复制样式',
305 | 'pastestyle': '粘贴样式',
306 | 'appendsiblingnode': '同级主题',
307 | 'appendchildnode': '下级主题',
308 | 'arrangeup': '前调',
309 | 'arrangedown': '后调',
310 | 'editnode': '编辑',
311 | 'removenode': '移除',
312 | 'priority': '优先级',
313 | 'progress': {
314 | 'p1': '未开始',
315 | 'p2': '完成 1/8',
316 | 'p3': '完成 1/4',
317 | 'p4': '完成 3/8',
318 | 'p5': '完成一半',
319 | 'p6': '完成 5/8',
320 | 'p7': '完成 3/4',
321 | 'p8': '完成 7/8',
322 | 'p9': '已完成',
323 | 'p0': '清除进度'
324 | },
325 | 'link': '链接',
326 | 'image': '图片',
327 | 'note': '备注',
328 | 'insertlink': '插入链接',
329 | 'insertimage': '插入图片',
330 | 'insertnote': '插入备注',
331 | 'removelink': '移除已有链接',
332 | 'removeimage': '移除已有图片',
333 | 'removenote': '移除已有备注',
334 | 'resetlayout': '整理',
335 |
336 | 'justnow': '刚刚',
337 | 'minutesago': '{0} 分钟前',
338 | 'hoursago': '{0} 小时前',
339 | 'yesterday': '昨天',
340 | 'daysago': '{0} 天前',
341 | 'longago': '很久之前',
342 |
343 | 'redirect': '您正在打开连接 {0},百度脑图不能保证连接的安全性,是否要继续?',
344 | 'navigator': '导航器',
345 | 'topbar': '头部导航',
346 |
347 | 'unsavedcontent': '当前文件还没有保存到网盘:\n\n{0}\n\n虽然未保存的数据会缓存在草稿箱,但是清除浏览器缓存会导致草稿箱清除。',
348 |
349 | 'shortcuts': '快捷键',
350 | 'contact': '联系与反馈',
351 | 'email': '邮件组',
352 | 'qq_group': 'QQ 群',
353 | 'github_issue': 'Github',
354 | 'baidu_tieba': '贴吧',
355 |
356 | 'clipboardunsupported': '您的浏览器不支持剪贴板,请使用快捷键复制',
357 |
358 | 'load_success': '{0} 加载成功',
359 | 'save_success': '{0} 已保存于 {1}',
360 | 'autosave_success': '{0} 已自动保存于 {1}',
361 |
362 | 'selectall': '全选',
363 | 'selectrevert': '反选',
364 | 'selectsiblings': '选择兄弟节点',
365 | 'selectlevel': '选择同级节点',
366 | 'selectpath': '选择路径',
367 | 'selecttree': '选择子树'
368 | },
369 | 'popupcolor': {
370 | 'clearColor': '清空颜色',
371 | 'standardColor': '标准颜色',
372 | 'themeColor': '主题颜色'
373 | },
374 | 'dialogs': {
375 | 'markers': {
376 | 'static': {
377 | 'lang_input_text': '文本内容:',
378 | 'lang_input_url': '链接地址:',
379 | 'lang_input_title': '标题:',
380 | 'lang_input_target': '是否在新窗口:'
381 | },
382 | 'priority': '优先级',
383 | 'none': '无',
384 | 'progress': {
385 | 'title': '进度',
386 | 'notdone': '未完成',
387 | 'done1': '完成 1/8',
388 | 'done2': '完成 1/4',
389 | 'done3': '完成 3/8',
390 | 'done4': '完成 1/2',
391 | 'done5': '完成 5/8',
392 | 'done6': '完成 3/4',
393 | 'done7': '完成 7/8',
394 | 'done': '已完成'
395 | }
396 | },
397 | 'help': {},
398 | 'hyperlink': {},
399 | 'image': {},
400 | 'resource': {}
401 | },
402 | 'hyperlink': {
403 | 'hyperlink': '链接...',
404 | 'unhyperlink': '移除链接'
405 | },
406 | 'image': {
407 | 'image': '图片...',
408 | 'removeimage': '移除图片'
409 | },
410 | 'marker': {
411 | 'marker': '进度/优先级...'
412 | },
413 | 'resource': {
414 | 'resource': '资源...'
415 | }
416 | };
417 |
--------------------------------------------------------------------------------
/src/locale/lang/zh-CN.js:
--------------------------------------------------------------------------------
1 | export default {
2 | i: {
3 | select: {
4 | placeholder: '请选择',
5 | noMatch: '无匹配数据'
6 | },
7 | table: {
8 | noDataText: '暂无数据',
9 | noFilteredDataText: '暂无筛选结果',
10 | confirmFilter: '筛选',
11 | resetFilter: '重置',
12 | clearFilter: '全部'
13 | },
14 | datepicker: {
15 | selectDate: '选择日期',
16 | selectTime: '选择时间',
17 | startTime: '开始时间',
18 | endTime: '结束时间',
19 | clear: '清空',
20 | ok: '确定',
21 | month: '月',
22 | month1: '1 月',
23 | month2: '2 月',
24 | month3: '3 月',
25 | month4: '4 月',
26 | month5: '5 月',
27 | month6: '6 月',
28 | month7: '7 月',
29 | month8: '8 月',
30 | month9: '9 月',
31 | month10: '10 月',
32 | month11: '11 月',
33 | month12: '12 月',
34 | year: '年',
35 | weeks: {
36 | sun: '日',
37 | mon: '一',
38 | tue: '二',
39 | wed: '三',
40 | thu: '四',
41 | fri: '五',
42 | sat: '六'
43 | },
44 | months: {
45 | m1: '1月',
46 | m2: '2月',
47 | m3: '3月',
48 | m4: '4月',
49 | m5: '5月',
50 | m6: '6月',
51 | m7: '7月',
52 | m8: '8月',
53 | m9: '9月',
54 | m10: '10月',
55 | m11: '11月',
56 | m12: '12月'
57 | }
58 | },
59 | transfer: {
60 | titles: {
61 | source: '源列表',
62 | target: '目的列表'
63 | },
64 | filterPlaceholder: '请输入搜索内容',
65 | notFoundText: '列表为空'
66 | },
67 | modal: {
68 | okText: '确定',
69 | cancelText: '取消'
70 | },
71 | poptip: {
72 | okText: '确定',
73 | cancelText: '取消'
74 | },
75 | page: {
76 | prev: '上一页',
77 | next: '下一页',
78 | total: '共',
79 | item: '条',
80 | items: '条',
81 | prev5: '向前 5 页',
82 | next5: '向后 5 页',
83 | page: '条/页',
84 | goto: '跳至',
85 | p: '页'
86 | },
87 | rate: {
88 | star: '星',
89 | stars: '星'
90 | },
91 | tree: {
92 | emptyText: '暂无数据'
93 | }
94 | }
95 | };
--------------------------------------------------------------------------------
/src/locale/lang/zh-TW.js:
--------------------------------------------------------------------------------
1 | export default {
2 | i: {
3 | select: {
4 | placeholder: '請選擇',
5 | noMatch: '無匹配數據'
6 | },
7 | table: {
8 | noDataText: '暫無數據',
9 | noFilteredDataText: '暫無篩選結果',
10 | confirmFilter: '篩選',
11 | resetFilter: '重置',
12 | clearFilter: '全部'
13 | },
14 | datepicker: {
15 | selectDate: '選擇日期',
16 | selectTime: '選擇時間',
17 | startTime: '開始時間',
18 | endTime: '結束時間',
19 | clear: '清空',
20 | ok: '確定',
21 | month: '月',
22 | month1: '1 月',
23 | month2: '2 月',
24 | month3: '3 月',
25 | month4: '4 月',
26 | month5: '5 月',
27 | month6: '6 月',
28 | month7: '7 月',
29 | month8: '8 月',
30 | month9: '9 月',
31 | month10: '10 月',
32 | month11: '11 月',
33 | month12: '12 月',
34 | year: '年',
35 | weeks: {
36 | sun: '日',
37 | mon: '一',
38 | tue: '二',
39 | wed: '三',
40 | thu: '四',
41 | fri: '五',
42 | sat: '六'
43 | },
44 | months: {
45 | m1: '1月',
46 | m2: '2月',
47 | m3: '3月',
48 | m4: '4月',
49 | m5: '5月',
50 | m6: '6月',
51 | m7: '7月',
52 | m8: '8月',
53 | m9: '9月',
54 | m10: '10月',
55 | m11: '11月',
56 | m12: '12月'
57 | }
58 | },
59 | transfer: {
60 | titles: {
61 | source: '源列表',
62 | target: '目的列表'
63 | },
64 | filterPlaceholder: '請輸入搜索內容',
65 | notFoundText: '列表爲空'
66 | },
67 | modal: {
68 | okText: '確定',
69 | cancelText: '取消'
70 | },
71 | poptip: {
72 | okText: '確定',
73 | cancelText: '取消'
74 | },
75 | page: {
76 | prev: '上壹頁',
77 | next: '下壹頁',
78 | total: '共',
79 | item: '條',
80 | items: '條',
81 | prev5: '向前 5 頁',
82 | next5: '向後 5 頁',
83 | page: '條/頁',
84 | goto: '跳至',
85 | p: '頁'
86 | },
87 | rate: {
88 | star: '星',
89 | stars: '星'
90 | },
91 | tree: {
92 | emptyText: '暫無數據'
93 | }
94 | }
95 | };
--------------------------------------------------------------------------------
/src/minder.js:
--------------------------------------------------------------------------------
1 | define(function(require, exports, module) {
2 | return module.exports = window.kityminder.Minder;
3 | });
4 |
--------------------------------------------------------------------------------
/src/mixins/locale.js:
--------------------------------------------------------------------------------
1 | import { t } from '../locale';
2 |
3 | export default {
4 | methods: {
5 | t(...args) {
6 | return t.apply(this, args);
7 | }
8 | }
9 | };
10 |
--------------------------------------------------------------------------------
/src/module/imageicon.js:
--------------------------------------------------------------------------------
1 | var kity = window.kity;
2 | var utils = require('kityminder-core/src/core/utils');
3 | var Minder = window.kityminder.Minder;
4 | var MinderNode = window.kityminder.Node;
5 | var Command = window.kityminder.Command;
6 | var Module = window.kityminder.Module;
7 | var Renderer = window.kityminder.Render;
8 |
9 | module.exports = Module.register('imageicon', function() {
10 | var self = this;
11 |
12 | function loadImageSize(url, callback) {
13 | var img = document.createElement('img');
14 | img.onload = function() {
15 | callback(img.width, img.height);
16 | };
17 | img.onerror = function() {
18 | callback(null);
19 | };
20 | img.src = url;
21 | }
22 |
23 | function fitImageSize(width, height, maxWidth, maxHeight) {
24 | var ratio = width / height,
25 | fitRatio = maxWidth / maxHeight;
26 |
27 | // 宽高比大于最大尺寸的宽高比,以宽度为标准适应
28 | if(width > maxWidth && ratio > fitRatio) {
29 | width = maxWidth;
30 | height = width / ratio;
31 | } else if(height > maxHeight) {
32 | height = maxHeight;
33 | width = height * ratio;
34 | }
35 |
36 | return {
37 | width: width | 0,
38 | height: height | 0
39 | };
40 | }
41 |
42 | /**
43 | * @command Image
44 | * @description 为选中的节点添加图片
45 | * @param {string} url 图片的 URL,设置为 null 移除
46 | * @param {string} title 图片的说明
47 | * @state
48 | * 0: 当前有选中的节点
49 | * -1: 当前没有选中的节点
50 | * @return 返回首个选中节点的图片信息,JSON 对象: `{url: url, title: title}`
51 | */
52 | var ImageIconCommand = kity.createClass('ImageIconCommand', {
53 | base: Command,
54 |
55 | execute: function(km, url, title) {
56 | var nodes = km.getSelectedNodes();
57 |
58 | loadImageSize(url, function(width, height) {
59 | nodes.forEach(function(n) {
60 | var size = fitImageSize(
61 | width, height,
62 | km.getOption('maxImageWidth'),
63 | km.getOption('maxImageHeight'));
64 | n.setData('imageicon', url);
65 | n.setData('imageTitle', url && title);
66 | n.setData('imageSize', url && size);
67 | n.render();
68 | });
69 | km.fire('saveScene');
70 | km.layout(300);
71 | });
72 |
73 | },
74 | queryState: function(km) {
75 | var nodes = km.getSelectedNodes(),
76 | result = 0;
77 | if(nodes.length === 0) {
78 | return -1;
79 | }
80 | nodes.forEach(function(n) {
81 | if(n && n.getData('image')) {
82 | result = 0;
83 | return false;
84 | }
85 | });
86 | return result;
87 | },
88 | queryValue: function(km) {
89 | var node = km.getSelectedNode();
90 | return {
91 | url: node.getData('image'),
92 | title: node.getData('imageTitle')
93 | };
94 | }
95 | });
96 |
97 | var ImageRenderer = kity.createClass('ImageRenderer', {
98 | base: Renderer,
99 |
100 | create: function(node) {
101 | return new kity.Image(node.getData('imageicon'));
102 | },
103 |
104 | shouldRender: function(node) {
105 | return node.getData('imageicon') || node.getData('hexType');
106 | },
107 |
108 | update: function(image, node, box) {
109 | var url = node.getData('imageicon');
110 | var title = node.getData('imageTitle');
111 | var size = node.getData('imageSize');
112 | var spaceTop = node.getStyle('space-top');
113 | var type = node.getData('hexType');
114 |
115 |
116 | var HEX_WIDTH = self.getOption('hex_width') || 20;
117 | var HEX_HEIGHT = self.getOption('hex_height') || 20;
118 | var urls = {
119 | app: self.getOption('app_url'),
120 | project: self.getOption('project_url'),
121 | dir: self.getOption('dir_url'),
122 | page: self.getOption('page_url')
123 | };
124 |
125 | if(type && urls[type]) {
126 | size = size || {width: HEX_WIDTH, height: HEX_HEIGHT};
127 | url = urls[type]
128 | }
129 |
130 | if(!size ) return;
131 |
132 | if(title) {
133 | image.node.setAttributeNS('http://www.w3.org/1999/xlink', 'title', title);
134 | }
135 |
136 | var x = -1 * size.width ;
137 | var y = box.y - spaceTop;
138 |
139 | image.setUrl(url).setX(x | 0).setY(y | 0).setWidth(size.width | 0).setHeight(size.height | 0);
140 |
141 | // use kity svg
142 | var items = node.rc.getItems();
143 | var group = items.find(a => a.constructor.name === 'Group');
144 | group.setTranslate(2, (size.height - group.getHeight()) / 2 - spaceTop);
145 |
146 | node.layout();
147 |
148 | return new kity.Box(x | 0, y | 0, size.width | 0, size.height | 0);
149 | }
150 | });
151 |
152 | return {
153 | 'defaultOptions': {
154 | 'hex_width': 20,
155 | 'hex_height': 20
156 | },
157 | 'commands': {
158 | 'imageicon': ImageIconCommand
159 | },
160 | 'renderers': {
161 | 'top': ImageRenderer
162 | }
163 | };
164 | });
165 |
--------------------------------------------------------------------------------
/src/runtime/clipboard-mimetype.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @Desc: 新增一个用于处理系统ctrl+c ctrl+v等方式导入导出节点的MIMETYPE处理,如系统不支持clipboardEvent或者是FF则不初始化改class
3 | * @Editor: Naixor
4 | * @Date: 2015.9.21
5 | */
6 | define(function(require, exports, module) {
7 | function MimeType() {
8 | /**
9 | * 私有变量
10 | */
11 | var SPLITOR = '\uFEFF';
12 | var MIMETYPE = {
13 | 'application/km': '\uFFFF'
14 | };
15 | var SIGN = {
16 | '\uFEFF': 'SPLITOR',
17 | '\uFFFF': 'application/km'
18 | };
19 |
20 | /**
21 | * 用于将一段纯文本封装成符合其数据格式的文本
22 | * @method process private
23 | * @param {MIMETYPE} mimetype 数据格式
24 | * @param {String} text 原始文本
25 | * @return {String} 符合该数据格式下的文本
26 | * @example
27 | * var str = "123";
28 | * str = process('application/km', str); // 返回的内容再经过MimeType判断会读取出其数据格式为application/km
29 | * process('text/plain', str); // 若接受到一个非纯文本信息,则会将其转换为新的数据格式
30 | */
31 | function process(mimetype, text) {
32 | if (!this.isPureText(text)) {
33 | var _mimetype = this.whichMimeType(text);
34 | if (!_mimetype) {
35 | throw new Error('unknow mimetype!');
36 | };
37 | text = this.getPureText(text);
38 | };
39 | if (mimetype === false) {
40 | return text;
41 | };
42 | return mimetype + SPLITOR + text;
43 | }
44 |
45 | /**
46 | * 注册数据类型的标识
47 | * @method registMimeTypeProtocol public
48 | * @param {String} type 数据类型
49 | * @param {String} sign 标识
50 | */
51 | this.registMimeTypeProtocol = function(type, sign) {
52 | if (sign && SIGN[sign]) {
53 | throw new Error('sing has registed!');
54 | }
55 | if (type && !!MIMETYPE[type]) {
56 | throw new Error('mimetype has registed!');
57 | };
58 | SIGN[sign] = type;
59 | MIMETYPE[type] = sign;
60 | }
61 |
62 | /**
63 | * 获取已注册数据类型的协议
64 | * @method getMimeTypeProtocol public
65 | * @param {String} type 数据类型
66 | * @param {String} text|undefiend 文本内容或不传入
67 | * @return {String|Function}
68 | * @example
69 | * text若不传入则直接返回对应数据格式的处理(process)方法
70 | * 若传入文本则直接调用对应的process方法进行处理,此时返回处理后的内容
71 | * var m = new MimeType();
72 | * var kmprocess = m.getMimeTypeProtocol('application/km');
73 | * kmprocess("123") === m.getMimeTypeProtocol('application/km', "123");
74 | *
75 | */
76 | this.getMimeTypeProtocol = function(type, text) {
77 | var mimetype = MIMETYPE[type] || false;
78 |
79 | if (text === undefined) {
80 | return process.bind(this, mimetype);
81 | };
82 |
83 | return process(mimetype, text);
84 | }
85 |
86 | this.getSpitor = function() {
87 | return SPLITOR;
88 | }
89 |
90 | this.getMimeType = function(sign) {
91 | if (sign !== undefined) {
92 | return SIGN[sign] || null;
93 | };
94 | return MIMETYPE;
95 | }
96 | }
97 |
98 | MimeType.prototype.isPureText = function(text) {
99 | return !(~text.indexOf(this.getSpitor()));
100 | }
101 |
102 | MimeType.prototype.getPureText = function(text) {
103 | if (this.isPureText(text)) {
104 | return text;
105 | };
106 | return text.split(this.getSpitor())[1];
107 | }
108 |
109 | MimeType.prototype.whichMimeType = function(text) {
110 | if (this.isPureText(text)) {
111 | return null;
112 | };
113 | return this.getMimeType(text.split(this.getSpitor())[0]);
114 | }
115 |
116 | function MimeTypeRuntime() {
117 | if (this.minder.supportClipboardEvent && !kity.Browser.gecko) {
118 | this.MimeType = new MimeType();
119 | };
120 | }
121 |
122 | return module.exports = MimeTypeRuntime;
123 | });
--------------------------------------------------------------------------------
/src/runtime/clipboard.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @Desc: 处理editor的clipboard事件,只在支持ClipboardEvent并且不是FF的情况下工作
3 | * @Editor: Naixor
4 | * @Date: 2015.9.21
5 | */
6 | define(function(require, exports, module) {
7 |
8 | function ClipboardRuntime () {
9 | var minder = this.minder;
10 | var Data = window.kityminder.data;
11 |
12 | if (!minder.supportClipboardEvent || kity.Browser.gecko) {
13 | return;
14 | };
15 |
16 | var fsm = this.fsm;
17 | var receiver = this.receiver;
18 | var MimeType = this.MimeType;
19 |
20 | var kmencode = MimeType.getMimeTypeProtocol('application/km'),
21 | decode = Data.getRegisterProtocol('json').decode;
22 | var _selectedNodes = [];
23 |
24 | /*
25 | * 增加对多节点赋值粘贴的处理
26 | */
27 | function encode (nodes) {
28 | var _nodes = [];
29 | for (var i = 0, l = nodes.length; i < l; i++) {
30 | _nodes.push(minder.exportNode(nodes[i]));
31 | }
32 | return kmencode(Data.getRegisterProtocol('json').encode(_nodes));
33 | }
34 |
35 | var beforeCopy = function (e) {
36 | if (document.activeElement == receiver.element) {
37 | var clipBoardEvent = e;
38 | var state = fsm.state();
39 |
40 | switch (state) {
41 | case 'input': {
42 | break;
43 | }
44 | case 'normal': {
45 | var nodes = [].concat(minder.getSelectedNodes());
46 | if (nodes.length) {
47 | // 这里由于被粘贴复制的节点的id信息也都一样,故做此算法
48 | // 这里有个疑问,使用node.getParent()或者node.parent会离奇导致出现非选中节点被渲染成选中节点,因此使用isAncestorOf,而没有使用自行回溯的方式
49 | if (nodes.length > 1) {
50 | var targetLevel;
51 | nodes.sort(function(a, b) {
52 | return a.getLevel() - b.getLevel();
53 | });
54 | targetLevel = nodes[0].getLevel();
55 | if (targetLevel !== nodes[nodes.length-1].getLevel()) {
56 | var plevel, pnode,
57 | idx = 0, l = nodes.length, pidx = l-1;
58 |
59 | pnode = nodes[pidx];
60 |
61 | while (pnode.getLevel() !== targetLevel) {
62 | idx = 0;
63 | while (idx < l && nodes[idx].getLevel() === targetLevel) {
64 | if (nodes[idx].isAncestorOf(pnode)) {
65 | nodes.splice(pidx, 1);
66 | break;
67 | }
68 | idx++;
69 | }
70 | pidx--;
71 | pnode = nodes[pidx];
72 | }
73 | };
74 | };
75 | var str = encode(nodes);
76 | clipBoardEvent.clipboardData.setData('text/plain', str);
77 | }
78 | e.preventDefault();
79 | break;
80 | }
81 | }
82 | }
83 | }
84 |
85 | var beforeCut = function (e) {
86 | if (document.activeElement == receiver.element) {
87 | if (minder.getStatus() !== 'normal') {
88 | e.preventDefault();
89 | return;
90 | };
91 |
92 | var clipBoardEvent = e;
93 | var state = fsm.state();
94 |
95 | switch (state) {
96 | case 'input': {
97 | break;
98 | }
99 | case 'normal': {
100 | var nodes = minder.getSelectedNodes();
101 | if (nodes.length) {
102 | clipBoardEvent.clipboardData.setData('text/plain', encode(nodes));
103 | minder.execCommand('removenode');
104 | }
105 | e.preventDefault();
106 | break;
107 | }
108 | }
109 | };
110 | }
111 |
112 | var beforePaste = function(e) {
113 | if (document.activeElement == receiver.element) {
114 | if (minder.getStatus() !== 'normal') {
115 | e.preventDefault();
116 | return;
117 | };
118 |
119 | var clipBoardEvent = e;
120 | var state = fsm.state();
121 | var textData = clipBoardEvent.clipboardData.getData('text/plain');
122 |
123 | switch (state) {
124 | case 'input': {
125 | // input状态下如果格式为application/km则不进行paste操作
126 | if (!MimeType.isPureText(textData)) {
127 | e.preventDefault();
128 | return;
129 | };
130 | break;
131 | }
132 | case 'normal': {
133 | /*
134 | * 针对normal状态下通过对选中节点粘贴导入子节点文本进行单独处理
135 | */
136 | var sNodes = minder.getSelectedNodes();
137 |
138 | if (MimeType.whichMimeType(textData) === 'application/km') {
139 | var nodes = decode(MimeType.getPureText(textData));
140 | var _node;
141 | sNodes.forEach(function(node) {
142 | // 由于粘贴逻辑中为了排除子节点重新排序导致逆序,因此复制的时候倒过来
143 | for (var i = nodes.length-1; i >= 0; i--) {
144 | _node = minder.createNode(null, node);
145 | minder.importNode(_node, nodes[i]);
146 | _selectedNodes.push(_node);
147 | node.appendChild(_node);
148 | }
149 | });
150 | minder.select(_selectedNodes, true);
151 | _selectedNodes = [];
152 |
153 | minder.refresh();
154 | }
155 | else if (clipBoardEvent.clipboardData && clipBoardEvent.clipboardData.items[0].type.indexOf('image') > -1) {
156 | var imageFile = clipBoardEvent.clipboardData.items[0].getAsFile();
157 | var serverService = angular.element(document.body).injector().get('server');
158 |
159 | return serverService.uploadImage(imageFile).then(function (json) {
160 | var resp = json.data;
161 | if (resp.errno === 0) {
162 | minder.execCommand('image', resp.data.url);
163 | }
164 | });
165 | }
166 | else {
167 | sNodes.forEach(function(node) {
168 | minder.Text2Children(node, textData);
169 | });
170 | }
171 | e.preventDefault();
172 | break;
173 | }
174 | }
175 | }
176 | }
177 | /**
178 | * 由editor的receiver统一处理全部事件,包括clipboard事件
179 | * @Editor: Naixor
180 | * @Date: 2015.9.24
181 | */
182 | document.addEventListener('copy', beforeCopy);
183 | document.addEventListener('cut', beforeCut);
184 | document.addEventListener('paste', beforePaste);
185 | }
186 |
187 | return module.exports = ClipboardRuntime;
188 | });
--------------------------------------------------------------------------------
/src/runtime/container.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileOverview
3 | *
4 | * 初始化编辑器的容器
5 | *
6 | * @author: techird
7 | * @copyright: Baidu FEX, 2014
8 | */
9 | define(function(require, exports, module) {
10 |
11 | /**
12 | * 最先执行的 Runtime,初始化编辑器容器
13 | */
14 | function ContainerRuntime() {
15 | var container;
16 |
17 | if (typeof(this.selector) == 'string') {
18 | container = document.querySelector(this.selector);
19 | } else {
20 | container = this.selector;
21 | }
22 |
23 | if (!container) throw new Error('Invalid selector: ' + this.selector);
24 |
25 | // 这个类名用于给编辑器添加样式
26 | container.classList.add('km-editor');
27 |
28 | // 暴露容器给其他运行时使用
29 | this.container = container;
30 | }
31 |
32 | return module.exports = ContainerRuntime;
33 | });
--------------------------------------------------------------------------------
/src/runtime/drag.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileOverview
3 | *
4 | * 用于拖拽节点时屏蔽键盘事件
5 | *
6 | * @author: techird
7 | * @copyright: Baidu FEX, 2014
8 | */
9 | define(function(require, exports, module) {
10 |
11 | var Hotbox = require('../hotbox');
12 | var Debug = require('../tool/debug');
13 | var debug = new Debug('drag');
14 |
15 | function DragRuntime() {
16 | var fsm = this.fsm;
17 | var minder = this.minder;
18 | var hotbox = this.hotbox;
19 | var receiver = this.receiver;
20 | var receiverElement = receiver.element;
21 |
22 | // setup everything to go
23 | setupFsm();
24 |
25 | // listen the fsm changes, make action.
26 | function setupFsm() {
27 |
28 | // when jumped to drag mode, enter
29 | fsm.when('* -> drag', function() {
30 | // now is drag mode
31 | });
32 |
33 | fsm.when('drag -> *', function(exit, enter, reason) {
34 | if (reason == 'drag-finish') {
35 | // now exit drag mode
36 | }
37 | });
38 | }
39 |
40 | var downX, downY;
41 | var MOUSE_HAS_DOWN = 0;
42 | var MOUSE_HAS_UP = 1;
43 | var BOUND_CHECK = 20;
44 | var flag = MOUSE_HAS_UP;
45 | var maxX, maxY, osx, osy, containerY;
46 | var freeHorizen = false, freeVirtical = false;
47 | var frame;
48 |
49 | function move(direction, speed) {
50 | if (!direction) {
51 | freeHorizen = freeVirtical = false;
52 | frame && kity.releaseFrame(frame);
53 | frame = null;
54 | return;
55 | }
56 | if (!frame) {
57 | frame = kity.requestFrame((function (direction, speed, minder) {
58 | return function (frame) {
59 | switch (direction) {
60 | case 'left':
61 | minder._viewDragger.move({x: -speed, y: 0}, 0);
62 | break;
63 | case 'top':
64 | minder._viewDragger.move({x: 0, y: -speed}, 0);
65 | break;
66 | case 'right':
67 | minder._viewDragger.move({x: speed, y: 0}, 0);
68 | break;
69 | case 'bottom':
70 | minder._viewDragger.move({x: 0, y: speed}, 0);
71 | break;
72 | default:
73 | return;
74 | }
75 | frame.next();
76 | };
77 | })(direction, speed, minder));
78 | }
79 | }
80 |
81 | minder.on('mousedown', function(e) {
82 | flag = MOUSE_HAS_DOWN;
83 | var rect = minder.getPaper().container.getBoundingClientRect();
84 | downX = e.originEvent.clientX;
85 | downY = e.originEvent.clientY;
86 | containerY = rect.top;
87 | maxX = rect.width;
88 | maxY = rect.height;
89 | });
90 |
91 | minder.on('mousemove', function(e) {
92 | if (fsm.state() === 'drag' && flag == MOUSE_HAS_DOWN && minder.getSelectedNode()
93 | && (Math.abs(downX - e.originEvent.clientX) > BOUND_CHECK
94 | || Math.abs(downY - e.originEvent.clientY) > BOUND_CHECK)) {
95 | osx = e.originEvent.clientX;
96 | osy = e.originEvent.clientY - containerY;
97 |
98 | if (osx < BOUND_CHECK) {
99 | move('right', BOUND_CHECK - osx);
100 | } else if (osx > maxX - BOUND_CHECK) {
101 | move('left', BOUND_CHECK + osx - maxX);
102 | } else {
103 | freeHorizen = true;
104 | }
105 | if (osy < BOUND_CHECK) {
106 | move('bottom', osy);
107 | } else if (osy > maxY - BOUND_CHECK) {
108 | move('top', BOUND_CHECK + osy - maxY);
109 | } else {
110 | freeVirtical = true;
111 | }
112 | if (freeHorizen && freeVirtical) {
113 | move(false);
114 | }
115 | }
116 | if (fsm.state() !== 'drag'
117 | && flag === MOUSE_HAS_DOWN
118 | && minder.getSelectedNode()
119 | && (Math.abs(downX - e.originEvent.clientX) > BOUND_CHECK
120 | || Math.abs(downY - e.originEvent.clientY) > BOUND_CHECK)) {
121 |
122 | if (fsm.state() === 'hotbox') {
123 | hotbox.active(Hotbox.STATE_IDLE);
124 | }
125 |
126 | return fsm.jump('drag', 'user-drag');
127 | }
128 | });
129 |
130 | window.addEventListener('mouseup', function () {
131 | flag = MOUSE_HAS_UP;
132 | if (fsm.state() === 'drag') {
133 | move(false);
134 | return fsm.jump('normal', 'drag-finish');
135 | }
136 | }, false);
137 | }
138 |
139 | return module.exports = DragRuntime;
140 | });
141 |
--------------------------------------------------------------------------------
/src/runtime/fsm.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileOverview
3 | *
4 | * 编辑器状态机
5 | *
6 | * @author: techird
7 | * @copyright: Baidu FEX, 2014
8 | */
9 | define(function(require, exports, module) {
10 |
11 | var Debug = require('../tool/debug');
12 | var debug = new Debug('fsm');
13 |
14 | function handlerConditionMatch(condition, when, exit, enter) {
15 | if (condition.when != when) return false;
16 | if (condition.enter != '*' && condition.enter != enter) return false;
17 | if (condition.exit != '*' && condition.exit != exit) return;
18 | return true;
19 | }
20 |
21 | function FSM(defaultState) {
22 | var currentState = defaultState;
23 | var BEFORE_ARROW = ' - ';
24 | var AFTER_ARROW = ' -> ';
25 | var handlers = [];
26 |
27 | /**
28 | * 状态跳转
29 | *
30 | * 会通知所有的状态跳转监视器
31 | *
32 | * @param {string} newState 新状态名称
33 | * @param {any} reason 跳转的原因,可以作为参数传递给跳转监视器
34 | */
35 | this.jump = function(newState, reason) {
36 | if (!reason) throw new Error('Please tell fsm the reason to jump');
37 |
38 | var oldState = currentState;
39 | var notify = [oldState, newState].concat([].slice.call(arguments, 1));
40 | var i, handler;
41 |
42 | // 跳转前
43 | for (i = 0; i < handlers.length; i++) {
44 | handler = handlers[i];
45 | if (handlerConditionMatch(handler.condition, 'before', oldState, newState)) {
46 | if (handler.apply(null, notify)) return;
47 | }
48 | }
49 |
50 | currentState = newState;
51 | debug.log('[{0}] {1} -> {2}', reason, oldState, newState);
52 |
53 | // 跳转后
54 | for (i = 0; i < handlers.length; i++) {
55 | handler = handlers[i];
56 | if (handlerConditionMatch(handler.condition, 'after', oldState, newState)) {
57 | handler.apply(null, notify);
58 | }
59 | }
60 | return currentState;
61 | };
62 |
63 | /**
64 | * 返回当前状态
65 | * @return {string}
66 | */
67 | this.state = function() {
68 | return currentState;
69 | };
70 |
71 | /**
72 | * 添加状态跳转监视器
73 | *
74 | * @param {string} condition
75 | * 监视的时机
76 | * "* => *" (默认)
77 | *
78 | * @param {Function} handler
79 | * 监视函数,当状态跳转的时候,会接收三个参数
80 | * * from - 跳转前的状态
81 | * * to - 跳转后的状态
82 | * * reason - 跳转的原因
83 | */
84 | this.when = function(condition, handler) {
85 | if (arguments.length == 1) {
86 | handler = condition;
87 | condition = '* -> *';
88 | }
89 |
90 | var when, resolved, exit, enter;
91 |
92 | resolved = condition.split(BEFORE_ARROW);
93 | if (resolved.length == 2) {
94 | when = 'before';
95 | } else {
96 | resolved = condition.split(AFTER_ARROW);
97 | if (resolved.length == 2) {
98 | when = 'after';
99 | }
100 | }
101 | if (!when) throw new Error('Illegal fsm condition: ' + condition);
102 |
103 | exit = resolved[0];
104 | enter = resolved[1];
105 |
106 | handler.condition = {
107 | when: when,
108 | exit: exit,
109 | enter: enter
110 | };
111 |
112 | handlers.push(handler);
113 | };
114 | }
115 |
116 | function FSMRumtime() {
117 | this.fsm = new FSM('normal');
118 | }
119 |
120 | return module.exports = FSMRumtime;
121 | });
--------------------------------------------------------------------------------
/src/runtime/history.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileOverview
3 | *
4 | * 历史管理
5 | *
6 | * @author: techird
7 | * @copyright: Baidu FEX, 2014
8 | */
9 |
10 |
11 | define(function(require, exports, module) {
12 | var jsonDiff = require('../tool/jsondiff');
13 |
14 | function HistoryRuntime() {
15 | var minder = this.minder;
16 | var hotbox = this.hotbox;
17 |
18 | var MAX_HISTORY = 100;
19 |
20 | var lastSnap;
21 | var patchLock;
22 | var undoDiffs;
23 | var redoDiffs;
24 |
25 | function reset() {
26 | undoDiffs = [];
27 | redoDiffs = [];
28 | lastSnap = minder.exportJson();
29 | }
30 |
31 | function makeUndoDiff() {
32 | var headSnap = minder.exportJson();
33 | var diff = jsonDiff(headSnap, lastSnap);
34 | if (diff.length) {
35 | undoDiffs.push(diff);
36 | while (undoDiffs.length > MAX_HISTORY) {
37 | undoDiffs.shift();
38 | }
39 | lastSnap = headSnap;
40 | return true;
41 | }
42 | }
43 |
44 | function makeRedoDiff() {
45 | var revertSnap = minder.exportJson();
46 | redoDiffs.push(jsonDiff(revertSnap, lastSnap));
47 | lastSnap = revertSnap;
48 | }
49 |
50 | function undo() {
51 | patchLock = true;
52 | var undoDiff = undoDiffs.pop();
53 | if (undoDiff) {
54 | minder.applyPatches(undoDiff);
55 | makeRedoDiff();
56 | }
57 | patchLock = false;
58 | }
59 |
60 | function redo() {
61 | patchLock = true;
62 | var redoDiff = redoDiffs.pop();
63 | if (redoDiff) {
64 | minder.applyPatches(redoDiff);
65 | makeUndoDiff();
66 | }
67 | patchLock = false;
68 | }
69 |
70 | function changed() {
71 | if (patchLock) return;
72 | if (makeUndoDiff()) redoDiffs = [];
73 | }
74 |
75 | function hasUndo() {
76 | return !!undoDiffs.length;
77 | }
78 |
79 | function hasRedo() {
80 | return !!redoDiffs.length;
81 | }
82 |
83 | function updateSelection(e) {
84 | if (!patchLock) return;
85 | var patch = e.patch;
86 | switch (patch.express) {
87 | case 'node.add':
88 | minder.select(patch.node.getChild(patch.index), true);
89 | break;
90 | case 'node.remove':
91 | case 'data.replace':
92 | case 'data.remove':
93 | case 'data.add':
94 | minder.select(patch.node, true);
95 | break;
96 | }
97 | }
98 |
99 | this.history = {
100 | reset: reset,
101 | undo: undo,
102 | redo: redo,
103 | hasUndo: hasUndo,
104 | hasRedo: hasRedo
105 | };
106 | reset();
107 | minder.on('contentchange', changed);
108 | minder.on('import', reset);
109 | minder.on('patch', updateSelection);
110 |
111 | var main = hotbox.state('main');
112 | main.button({
113 | position: 'top',
114 | label: '撤销',
115 | key: 'Ctrl + Z',
116 | enable: hasUndo,
117 | action: undo,
118 | next: 'idle'
119 | });
120 | main.button({
121 | position: 'top',
122 | label: '重做',
123 | key: 'Ctrl + Y',
124 | enable: hasRedo,
125 | action: redo,
126 | next: 'idle'
127 | });
128 | }
129 |
130 | window.diff = jsonDiff;
131 |
132 | return module.exports = HistoryRuntime;
133 | });
--------------------------------------------------------------------------------
/src/runtime/hotbox.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileOverview
3 | *
4 | * 热盒 Runtime
5 | *
6 | * @author: techird
7 | * @copyright: Baidu FEX, 2014
8 | */
9 | define(function(require, exports, module) {
10 | var Hotbox = require('../hotbox');
11 |
12 | function HotboxRuntime() {
13 | var fsm = this.fsm;
14 | var minder = this.minder;
15 | var receiver = this.receiver;
16 | var container = this.container;
17 |
18 | var hotbox = new Hotbox(container);
19 |
20 | hotbox.setParentFSM(fsm);
21 |
22 | fsm.when('normal -> hotbox', function(exit, enter, reason) {
23 | var node = minder.getSelectedNode();
24 | var position;
25 | if (node) {
26 | var box = node.getRenderBox();
27 | position = {
28 | x: box.cx,
29 | y: box.cy
30 | };
31 | }
32 | hotbox.active('main', position);
33 | });
34 |
35 | fsm.when('normal -> normal', function(exit, enter, reason, e) {
36 | if (reason == 'shortcut-handle') {
37 | var handleResult = hotbox.dispatch(e);
38 | if (handleResult) {
39 | e.preventDefault();
40 | } else {
41 | minder.dispatchKeyEvent(e);
42 | }
43 | }
44 | });
45 |
46 | fsm.when('modal -> normal', function(exit, enter, reason, e) {
47 | if (reason == 'import-text-finish') {
48 | receiver.element.focus();
49 | }
50 | });
51 |
52 | this.hotbox = hotbox;
53 | }
54 |
55 | return module.exports = HotboxRuntime;
56 | });
--------------------------------------------------------------------------------
/src/runtime/jumping.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileOverview
3 | *
4 | * 根据按键控制状态机的跳转
5 | *
6 | * @author: techird
7 | * @copyright: Baidu FEX, 2014
8 | */
9 | define(function(require, exports, module) {
10 |
11 | var Hotbox = require('../hotbox');
12 |
13 |
14 | // Nice: http://unixpapa.com/js/key.html
15 | function isIntendToInput(e) {
16 | if (e.ctrlKey || e.metaKey || e.altKey) return false;
17 |
18 | // a-zA-Z
19 | if (e.keyCode >= 65 && e.keyCode <= 90) return true;
20 |
21 | // 0-9 以及其上面的符号
22 | if (e.keyCode >= 48 && e.keyCode <= 57) return true;
23 |
24 | // 小键盘区域 (除回车外)
25 | if (e.keyCode != 108 && e.keyCode >= 96 && e.keyCode <= 111) return true;
26 |
27 | // 小键盘区域 (除回车外)
28 | // @yinheli from pull request
29 | if (e.keyCode != 108 && e.keyCode >= 96 && e.keyCode <= 111) return true;
30 |
31 | // 输入法
32 | if (e.keyCode == 229 || e.keyCode === 0) return true;
33 |
34 | return false;
35 | }
36 | /**
37 | * @Desc: 下方使用receiver.enable()和receiver.disable()通过
38 | * 修改div contenteditable属性的hack来解决开启热核后依然无法屏蔽浏览器输入的bug;
39 | * 特别: win下FF对于此种情况必须要先blur在focus才能解决,但是由于这样做会导致用户
40 | * 输入法状态丢失,因此对FF暂不做处理
41 | * @Editor: Naixor
42 | * @Date: 2015.09.14
43 | */
44 | function JumpingRuntime() {
45 | var fsm = this.fsm;
46 | var minder = this.minder;
47 | var receiver = this.receiver;
48 | var container = this.container;
49 | var receiverElement = receiver.element;
50 | var hotbox = this.hotbox;
51 | var compositionLock = false;
52 |
53 | // normal -> *
54 | receiver.listen('normal', function(e) {
55 | // 为了防止处理进入edit模式而丢失处理的首字母,此时receiver必须为enable
56 | receiver.enable();
57 | // normal -> hotbox
58 | if (e.is('Space')) {
59 | e.preventDefault();
60 | // safari下Space触发hotbox,然而这时Space已在receiver上留下作案痕迹,因此抹掉
61 | if (kity.Browser.safari) {
62 | receiverElement.innerHTML = '';
63 | }
64 | return fsm.jump('hotbox', 'space-trigger');
65 | }
66 |
67 | /**
68 | * check
69 | * @editor Naixor
70 | * @Date 2015-12-2
71 | */
72 | switch (e.type) {
73 | case 'keydown': {
74 | if (minder.getSelectedNode()) {
75 | if (isIntendToInput(e)) {
76 | return fsm.jump('input', 'user-input');
77 | };
78 | } else {
79 | receiverElement.innerHTML = '';
80 | }
81 | // normal -> normal shortcut
82 | fsm.jump('normal', 'shortcut-handle', e);
83 | break;
84 | }
85 | case 'keyup': {
86 | break;
87 | }
88 | default: {}
89 | }
90 | });
91 |
92 | // hotbox -> normal
93 | receiver.listen('hotbox', function(e) {
94 | receiver.disable();
95 | e.preventDefault();
96 | var handleResult = hotbox.dispatch(e);
97 | if (hotbox.state() == Hotbox.STATE_IDLE && fsm.state() == 'hotbox') {
98 | return fsm.jump('normal', 'hotbox-idle');
99 | }
100 | });
101 |
102 | // input => normal
103 | receiver.listen('input', function(e) {
104 | receiver.enable();
105 | if (e.type == 'keydown') {
106 | if (e.is('Enter')) {
107 | e.preventDefault();
108 | return fsm.jump('normal', 'input-commit');
109 | }
110 | if (e.is('Esc')) {
111 | e.preventDefault();
112 | return fsm.jump('normal', 'input-cancel');
113 | }
114 | if (e.is('Tab') || e.is('Shift + Tab')) {
115 | e.preventDefault();
116 | }
117 | } else if (e.type == 'keyup' && e.is('Esc')) {
118 | e.preventDefault();
119 | if (!compositionLock) {
120 | return fsm.jump('normal', 'input-cancel');
121 | }
122 | }
123 | else if (e.type == 'compositionstart') {
124 | compositionLock = true;
125 | }
126 | else if (e.type == 'compositionend') {
127 | setTimeout(function () {
128 | compositionLock = false;
129 | });
130 | }
131 | });
132 |
133 | //////////////////////////////////////////////
134 | /// 右键呼出热盒
135 | /// 判断的标准是:按下的位置和结束的位置一致
136 | //////////////////////////////////////////////
137 | var downX, downY;
138 | var MOUSE_RB = 2; // 右键
139 |
140 | container.addEventListener('mousedown', function(e) {
141 | if (e.button == MOUSE_RB) {
142 | e.preventDefault();
143 | }
144 | if (fsm.state() == 'hotbox') {
145 | hotbox.active(Hotbox.STATE_IDLE);
146 | fsm.jump('normal', 'blur');
147 | } else if (fsm.state() == 'normal' && e.button == MOUSE_RB) {
148 | downX = e.clientX;
149 | downY = e.clientY;
150 | }
151 | }, false);
152 |
153 | container.addEventListener('mousewheel', function(e) {
154 | if (fsm.state() == 'hotbox') {
155 | hotbox.active(Hotbox.STATE_IDLE);
156 | fsm.jump('normal', 'mousemove-blur');
157 | }
158 | }, false);
159 |
160 | container.addEventListener('contextmenu', function(e) {
161 | e.preventDefault();
162 | });
163 |
164 | container.addEventListener('mouseup', function(e) {
165 | if (fsm.state() != 'normal') {
166 | return;
167 | }
168 | if (e.button != MOUSE_RB || e.clientX != downX || e.clientY != downY) {
169 | return;
170 | }
171 | if (!minder.getSelectedNode()) {
172 | return;
173 | }
174 | fsm.jump('hotbox', 'content-menu');
175 | }, false);
176 |
177 | // 阻止热盒事件冒泡,在热盒正确执行前导致热盒关闭
178 | hotbox.$element.addEventListener('mousedown', function(e) {
179 | e.stopPropagation();
180 | });
181 | }
182 |
183 | return module.exports = JumpingRuntime;
184 | });
185 |
--------------------------------------------------------------------------------
/src/runtime/minder.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileOverview
3 | *
4 | * 脑图示例运行时
5 | *
6 | * @author: techird
7 | * @copyright: Baidu FEX, 2014
8 | */
9 | define(function(require, exports, module) {
10 | var Minder = require('../minder');
11 |
12 | function MinderRuntime() {
13 |
14 | // 不使用 kityminder 的按键处理,由 ReceiverRuntime 统一处理
15 | var minder = new Minder({
16 | enableKeyReceiver: false,
17 | enableAnimation: true
18 | });
19 |
20 | // 渲染,初始化
21 | minder.renderTo(this.selector);
22 | minder.setTheme(null);
23 | minder.select(minder.getRoot(), true);
24 | minder.execCommand('name', '中心主题');
25 |
26 | // 导出给其它 Runtime 使用
27 | this.minder = minder;
28 | }
29 |
30 | return module.exports = MinderRuntime;
31 | });
--------------------------------------------------------------------------------
/src/runtime/node.js:
--------------------------------------------------------------------------------
1 | define(function(require, exports, module) {
2 |
3 | function NodeRuntime() {
4 | var runtime = this;
5 | var minder = this.minder;
6 | var hotbox = this.hotbox;
7 | var fsm = this.fsm;
8 |
9 | var main = hotbox.state('main');
10 |
11 | var buttons = [
12 | '前移:Alt+Up:ArrangeUp',
13 | '下级:Tab|Insert:AppendChildNode',
14 | '同级:Enter:AppendSiblingNode',
15 | '后移:Alt+Down:ArrangeDown',
16 | '删除:Delete|Backspace:RemoveNode',
17 | '上级:Shift+Tab|Shift+Insert:AppendParentNode'
18 | //'全选:Ctrl+A:SelectAll'
19 | ];
20 |
21 | var AppendLock = 0;
22 |
23 | buttons.forEach(function(button) {
24 | var parts = button.split(':');
25 | var label = parts.shift();
26 | var key = parts.shift();
27 | var command = parts.shift();
28 | main.button({
29 | position: 'ring',
30 | label: label,
31 | key: key,
32 | action: function() {
33 | if (command.indexOf('Append') === 0) {
34 | AppendLock++;
35 | minder.execCommand(command, '分支主题');
36 |
37 | // provide in input runtime
38 | function afterAppend () {
39 | if (!--AppendLock) {
40 | runtime.editText();
41 | }
42 | minder.off('layoutallfinish', afterAppend);
43 | }
44 | minder.on('layoutallfinish', afterAppend);
45 | } else {
46 | minder.execCommand(command);
47 | fsm.jump('normal', 'command-executed');
48 | }
49 | },
50 | enable: function() {
51 | return minder.queryCommandState(command) != -1;
52 | }
53 | });
54 | });
55 |
56 | main.button({
57 | position: 'bottom',
58 | label: '导入节点',
59 | key: 'Alt + V',
60 | enable: function() {
61 | var selectedNodes = minder.getSelectedNodes();
62 | return selectedNodes.length == 1;
63 | },
64 | action: importNodeData,
65 | next: 'idle'
66 | });
67 |
68 | main.button({
69 | position: 'bottom',
70 | label: '导出节点',
71 | key: 'Alt + C',
72 | enable: function() {
73 | var selectedNodes = minder.getSelectedNodes();
74 | return selectedNodes.length == 1;
75 | },
76 | action: exportNodeData,
77 | next: 'idle'
78 | });
79 |
80 | function importNodeData() {
81 | minder.fire('importNodeData');
82 | }
83 |
84 | function exportNodeData() {
85 | minder.fire('exportNodeData');
86 | }
87 |
88 | //main.button({
89 | // position: 'ring',
90 | // key: '/',
91 | // action: function(){
92 | // if (!minder.queryCommandState('expand')) {
93 | // minder.execCommand('expand');
94 | // } else if (!minder.queryCommandState('collapse')) {
95 | // minder.execCommand('collapse');
96 | // }
97 | // },
98 | // enable: function() {
99 | // return minder.queryCommandState('expand') != -1 || minder.queryCommandState('collapse') != -1;
100 | // },
101 | // beforeShow: function() {
102 | // if (!minder.queryCommandState('expand')) {
103 | // this.$button.children[0].innerHTML = '展开';
104 | // } else {
105 | // this.$button.children[0].innerHTML = '收起';
106 | // }
107 | // }
108 | //})
109 | }
110 |
111 | return module.exports = NodeRuntime;
112 | });
113 |
--------------------------------------------------------------------------------
/src/runtime/priority.js:
--------------------------------------------------------------------------------
1 | define(function(require, exports, module){
2 |
3 | function PriorityRuntime() {
4 | var minder = this.minder;
5 | var hotbox = this.hotbox;
6 |
7 | var main = hotbox.state('main');
8 |
9 | main.button({
10 | position: 'top',
11 | label: '优先级',
12 | key: 'P',
13 | next: 'priority',
14 | enable: function() {
15 | return minder.queryCommandState('priority') != -1;
16 | }
17 | });
18 |
19 | var priority = hotbox.state('priority');
20 | '123456789'.replace(/./g, function(p) {
21 | priority.button({
22 | position: 'ring',
23 | label: 'P' + p,
24 | key: p,
25 | action: function() {
26 | minder.execCommand('Priority', p);
27 | }
28 | });
29 | });
30 |
31 | priority.button({
32 | position: 'center',
33 | label: '移除',
34 | key: 'Del',
35 | action: function() {
36 | minder.execCommand('Priority', 0);
37 | }
38 | });
39 |
40 | priority.button({
41 | position: 'top',
42 | label: '返回',
43 | key: 'esc',
44 | next: 'back'
45 | });
46 |
47 | }
48 |
49 | return module.exports = PriorityRuntime;
50 |
51 | });
--------------------------------------------------------------------------------
/src/runtime/progress.js:
--------------------------------------------------------------------------------
1 | define(function(require, exports, module){
2 |
3 | function ProgressRuntime() {
4 | var minder = this.minder;
5 | var hotbox = this.hotbox;
6 |
7 | var main = hotbox.state('main');
8 |
9 | main.button({
10 | position: 'top',
11 | label: '进度',
12 | key: 'G',
13 | next: 'progress',
14 | enable: function() {
15 | return minder.queryCommandState('progress') != -1;
16 | }
17 | });
18 |
19 | var progress = hotbox.state('progress');
20 | '012345678'.replace(/./g, function(p) {
21 | progress.button({
22 | position: 'ring',
23 | label: 'G' + p,
24 | key: p,
25 | action: function() {
26 | minder.execCommand('Progress', parseInt(p) + 1);
27 | }
28 | });
29 | });
30 |
31 | progress.button({
32 | position: 'center',
33 | label: '移除',
34 | key: 'Del',
35 | action: function() {
36 | minder.execCommand('Progress', 0);
37 | }
38 | });
39 |
40 | progress.button({
41 | position: 'top',
42 | label: '返回',
43 | key: 'esc',
44 | next: 'back'
45 | });
46 |
47 |
48 | }
49 |
50 | return module.exports = ProgressRuntime;
51 |
52 | });
--------------------------------------------------------------------------------
/src/runtime/receiver.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileOverview
3 | *
4 | * 键盘事件接收/分发器
5 | *
6 | * @author: techird
7 | * @copyright: Baidu FEX, 2014
8 | */
9 |
10 | define(function(require, exports, module) {
11 | var key = require('../tool/key');
12 |
13 | function ReceiverRuntime() {
14 | var fsm = this.fsm;
15 | var minder = this.minder;
16 | var me = this;
17 |
18 | // 接收事件的 div
19 | var element = document.createElement('div');
20 | element.contentEditable = true;
21 | /**
22 | * @Desc: 增加tabindex属性使得element的contenteditable不管是trur还是false都能有focus和blur事件
23 | * @Editor: Naixor
24 | * @Date: 2015.09.14
25 | */
26 | element.setAttribute('tabindex', -1);
27 | element.classList.add('receiver');
28 | element.onkeydown = element.onkeypress = element.onkeyup = dispatchKeyEvent;
29 | element.addEventListener('compositionstart', dispatchKeyEvent);
30 | // element.addEventListener('compositionend', dispatchKeyEvent);
31 | this.container.appendChild(element);
32 |
33 | // receiver 对象
34 | var receiver = {
35 | element: element,
36 | selectAll: function() {
37 | // 保证有被选中的
38 | if (!element.innerHTML) element.innerHTML = ' ';
39 | var range = document.createRange();
40 | var selection = window.getSelection();
41 | range.selectNodeContents(element);
42 | selection.removeAllRanges();
43 | selection.addRange(range);
44 | element.focus();
45 | },
46 | /**
47 | * @Desc: 增加enable和disable方法用于解决热核态的输入法屏蔽问题
48 | * @Editor: Naixor
49 | * @Date: 2015.09.14
50 | */
51 | enable: function() {
52 | element.setAttribute("contenteditable", true);
53 | },
54 | disable: function() {
55 | element.setAttribute("contenteditable", false);
56 | },
57 | /**
58 | * @Desc: hack FF下div contenteditable的光标丢失BUG
59 | * @Editor: Naixor
60 | * @Date: 2015.10.15
61 | */
62 | fixFFCaretDisappeared: function() {
63 | element.removeAttribute("contenteditable");
64 | element.setAttribute("contenteditable", "true");
65 | element.blur();
66 | element.focus();
67 | },
68 | /**
69 | * 以此事件代替通过mouse事件来判断receiver丢失焦点的事件
70 | * @editor Naixor
71 | * @Date 2015-12-2
72 | */
73 | onblur: function (handler) {
74 | element.onblur = handler;
75 | }
76 | };
77 | receiver.selectAll();
78 |
79 | minder.on('beforemousedown', receiver.selectAll);
80 | minder.on('receiverfocus', receiver.selectAll);
81 | minder.on('readonly', function() {
82 | // 屏蔽minder的事件接受,删除receiver和hotbox
83 | minder.disable();
84 | editor.receiver.element.parentElement.removeChild(editor.receiver.element);
85 | editor.hotbox.$container.removeChild(editor.hotbox.$element);
86 | });
87 |
88 | // 侦听器,接收到的事件会派发给所有侦听器
89 | var listeners = [];
90 |
91 | // 侦听指定状态下的事件,如果不传 state,侦听所有状态
92 | receiver.listen = function(state, listener) {
93 | if (arguments.length == 1) {
94 | listener = state;
95 | state = '*';
96 | }
97 | listener.notifyState = state;
98 | listeners.push(listener);
99 | };
100 |
101 | function dispatchKeyEvent(e) {
102 | e.is = function(keyExpression) {
103 | var subs = keyExpression.split('|');
104 | for (var i = 0; i < subs.length; i++) {
105 | if (key.is(this, subs[i])) return true;
106 | }
107 | return false;
108 | };
109 | var listener, jumpState;
110 | for (var i = 0; i < listeners.length; i++) {
111 |
112 | listener = listeners[i];
113 | // 忽略不在侦听状态的侦听器
114 | if (listener.notifyState != '*' && listener.notifyState != fsm.state()) {
115 | continue;
116 | }
117 |
118 | /**
119 | *
120 | * 对于所有的侦听器,只允许一种处理方式:跳转状态。
121 | * 如果侦听器确定要跳转,则返回要跳转的状态。
122 | * 每个事件只允许一个侦听器进行状态跳转
123 | * 跳转动作由侦听器自行完成(因为可能需要在跳转时传递 reason),返回跳转结果即可。
124 | * 比如:
125 | *
126 | * ```js
127 | * receiver.listen('normal', function(e) {
128 | * if (isSomeReasonForJumpState(e)) {
129 | * return fsm.jump('newstate', e);
130 | * }
131 | * });
132 | * ```
133 | */
134 | if (listener.call(null, e)) {
135 | return;
136 | }
137 | }
138 | }
139 |
140 | this.receiver = receiver;
141 | }
142 |
143 | return module.exports = ReceiverRuntime;
144 |
145 | });
146 |
--------------------------------------------------------------------------------
/src/services/config.js:
--------------------------------------------------------------------------------
1 | var conf = {};
2 |
3 | function initConf() {
4 | this.config = {
5 | // 右侧面板最小宽度
6 | ctrlPanelMin: 250,
7 |
8 | // 右侧面板宽度
9 | ctrlPanelWidth: parseInt(window.localStorage.__dev_minder_ctrlPanelWidth) || 250,
10 |
11 | // 分割线宽度
12 | dividerWidth: 3,
13 |
14 | // 默认语言
15 | defaultLang: 'zh-cn',
16 |
17 | // 放大缩小比例
18 | zoom: [10, 20, 30, 50, 80, 100, 120, 150, 200],
19 |
20 | // 图片上传接口
21 | imageUpload: 'server/imageUpload.php'
22 | };
23 |
24 | this.set = (key, value) => {
25 | var supported = Object.keys(this.config);
26 | var configObj = {};
27 |
28 | // 支持全配置
29 | if(typeof key === 'object') {
30 | configObj = key;
31 | }
32 | else {
33 | configObj[key] = value;
34 | }
35 |
36 | for(var i in configObj) {
37 | if(configObj.hasOwnProperty(i) && supported.indexOf(i) !== -1) {
38 | this.config[i] = configObj[i];
39 | }
40 | else {
41 | console.error('Unsupported config key: ', key, ', please choose in :', supported.join(', '));
42 | return false;
43 | }
44 | }
45 |
46 | return true;
47 | };
48 |
49 | this.get = key => {
50 | var me = this
51 | if(!key) {
52 | return me.config;
53 | }
54 |
55 | if(me.config.hasOwnProperty(key)) {
56 | return me.config[key];
57 | }
58 |
59 | console.warn('Missing config key pair for : ', key);
60 | return '';
61 | }
62 | return this
63 | }
64 |
65 | module.exports = initConf.call(conf)
66 |
--------------------------------------------------------------------------------
/src/services/memory.js:
--------------------------------------------------------------------------------
1 | function isQuotaExceeded(e) {
2 | var quotaExceeded = false;
3 | if(e) {
4 | if(e.code) {
5 | switch(e.code) {
6 | case 22:
7 | quotaExceeded = true;
8 | break;
9 | case 1014:
10 | // Firefox
11 | if(e.name === 'NS_ERROR_DOM_QUOTA_REACHED') {
12 | quotaExceeded = true;
13 | }
14 | break;
15 | }
16 | } else if(e.number === -2147024882) {
17 | // Internet Explorer 8
18 | quotaExceeded = true;
19 | }
20 | }
21 | return quotaExceeded;
22 | }
23 |
24 | module.exports = {
25 | get: function(key) {
26 | var value = window.localStorage.getItem(key);
27 | return null || JSON.parse(value);
28 | },
29 |
30 | set: function(key, value) {
31 | try {
32 | window.localStorage.setItem(key, JSON.stringify(value));
33 | return true;
34 | } catch(e) {
35 | if(isQuotaExceeded(e)) {
36 | return false;
37 | }
38 | }
39 | },
40 | remove: function(key) {
41 | var value = window.localStorage.getItem(key);
42 | window.localStorage.removeItem(key);
43 | return value;
44 | },
45 | clear: function() {
46 | window.localStorage.clear();
47 | }
48 | };
49 |
--------------------------------------------------------------------------------
/src/styles/README.md:
--------------------------------------------------------------------------------
1 | # 样式库说明
2 |
3 | ## 目录
4 |
5 | |-- animation (动画)
6 |
7 | |-- common (全局样式)
8 |
9 | |-- components (组件样式)
10 |
11 | |-- mixins (混入)
12 |
--------------------------------------------------------------------------------
/src/styles/_navigator.less:
--------------------------------------------------------------------------------
1 | .nav-bar {
2 | position: absolute;
3 | width: 240px;
4 | height: 35px;
5 | padding: 5px 0;
6 | display: flex;
7 | align-items: center;
8 | justify-content: space-evenly;
9 | left: 10px;
10 | bottom: 10px;
11 | background: #dc8686;
12 | color: #fff;
13 | border-radius: 4px;
14 | z-index: 10;
15 | box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.2);
16 | transition: -webkit-transform .7s 0.1s ease;
17 | transition: transform .7s 0.1s ease;
18 |
19 | .nav-btn {
20 | width: 35px;
21 | height: 35px;
22 | line-height: 35px;
23 | text-align: center;
24 | display: flex;
25 | justify-content: center;
26 | align-items: center;
27 | }
28 | .nav-btn:hover .icon,
29 | .nav-btn.active .icon{
30 | fill: #5c94f3;
31 | }
32 |
33 | .zoom-in .icon {
34 | background-position: 0 -730px;
35 | }
36 |
37 | .zoom-out .icon {
38 | background-position: 0 -750px;
39 | }
40 |
41 | .hand .icon {
42 | background-position: 0 -770px;
43 | width: 25px;
44 | height: 25px;
45 | margin: 0 auto;
46 | }
47 |
48 |
49 | .nav-trigger .icon {
50 | background-position: 0 -845px;
51 | width: 25px;
52 | height: 25px;
53 | margin: 0 auto;
54 | }
55 |
56 | .zoom-pan {
57 | width: 2px;
58 | height: 70px;
59 | box-shadow: 0 1px #E50000;
60 | position: relative;
61 | background: white;
62 | margin: 3px auto;
63 | overflow: visible;
64 |
65 | .origin {
66 | position: absolute;
67 | width: 20px;
68 | height: 8px;
69 | left: -9px;
70 | margin-top: -4px;
71 | background: transparent;
72 |
73 | &:after {
74 | content: ' ';
75 | display: block;
76 | width: 6px;
77 | height: 2px;
78 | background: white;
79 | left: 7px;
80 | top: 3px;
81 | position: absolute;
82 | }
83 | }
84 |
85 | .indicator {
86 | position: absolute;
87 | width: 8px;
88 | height: 8px;
89 | left: -3px;
90 | background: white;
91 | border-radius: 100%;
92 | margin-top: -4px;
93 | }
94 |
95 | }
96 | }
97 |
98 | .nav-previewer {
99 | background: #fff;
100 | width: 140px;
101 | height: 120px;
102 | position: absolute;
103 | left: 45px;
104 | bottom: 30px;
105 | box-shadow: 0 0 8px rgba(0, 0, 0, 0.2);
106 | border-radius: 0 2px 2px 0;
107 | padding: 1px;
108 | z-index: 9;
109 | cursor: crosshair;
110 | transition: -webkit-transform .7s 0.1s ease;
111 | transition: transform .7s 0.1s ease;
112 |
113 | &.grab {
114 | cursor: move;
115 | cursor: -webkit-grabbing;
116 | cursor: -moz-grabbing;
117 | cursor: grabbing;
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/src/styles/_tool_group.less:
--------------------------------------------------------------------------------
1 | .tool-group {
2 | padding: 0;
3 |
4 | &[disabled] {
5 | opacity: 0.5;
6 | }
7 |
8 | .tool-group-item {
9 | display: inline-block;
10 | border-radius: 4px;
11 |
12 | .tool-group-icon {
13 | width: 20px;
14 | height: 20px;
15 | padding: 2px;
16 | margin: 1px;
17 | }
18 |
19 | &:hover {background-color: @button-hover;}
20 | &:active {background-color: @button-active;}
21 |
22 | &.active {background-color: @button-active;}
23 | }
24 | }
--------------------------------------------------------------------------------
/src/styles/_vars.less:
--------------------------------------------------------------------------------
1 | @button-hover: hsl(222, 55%, 96%);
2 | @button-active: hsl(222, 55%, 85%);
3 | @button-pressed: hsl(222, 55%, 90%);
4 |
5 | @tool-hover: #eff3fa;
6 | @tool-active: #c4d0ee;
7 | @tool-selected: #87a9da;
--------------------------------------------------------------------------------
/src/styles/editor.less:
--------------------------------------------------------------------------------
1 | .km-editor {
2 | overflow: hidden;
3 | z-index: 2;
4 | }
5 |
6 | .km-editor > .mask {
7 | display: block;
8 | position: absolute;
9 | left: 0;
10 | right: 0;
11 | top: 0;
12 | bottom: 0;
13 | background-color: transparent;
14 | }
15 |
16 | .km-editor > .receiver {
17 | position: absolute;
18 | background: white;
19 | outline: none;
20 | box-shadow: 0 0 20px fadeout(black, 50%);
21 | left: 0;
22 | top: 0;
23 | padding: 3px 5px;
24 | margin-left: -3px;
25 | margin-top: -5px;
26 | max-width: 300px;
27 | width: auto;
28 | overflow: hidden;
29 | font-size: 14px;
30 | line-height: 1.4em;
31 | min-height: 1.4em;
32 | box-sizing: border-box;
33 | overflow: hidden;
34 | word-break: break-all;
35 | word-wrap: break-word;
36 | border: none;
37 | -webkit-user-select: text;
38 | pointer-events: none;
39 | opacity: 0;
40 | z-index: -1000;
41 | &.debug {
42 | opacity: 1;
43 | outline: 1px solid green;
44 | background: none;
45 | z-index: 0;
46 | }
47 |
48 | &.input {
49 | pointer-events: all;
50 | opacity: 1;
51 | z-index: 999;
52 | background: white;
53 | outline: none;
54 | }
55 | }
56 |
57 | div.minder-editor-container {
58 | position: absolute;
59 | bottom: 0;
60 | left: 0;
61 | right: 0;
62 | top: 0;
63 | font-family: Arial, "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif;
64 | }
65 |
66 |
67 | .minder-editor {
68 | position: absolute;
69 | top: 0px;
70 | left: 0;
71 | right: 0;
72 | bottom: 0;
73 | }
74 |
75 | .minder-viewer {
76 | position: absolute;
77 | top: 0;
78 | left: 0;
79 | right: 0;
80 | bottom: 0;
81 | }
82 |
83 |
84 | .control-panel {
85 | position: absolute;
86 | top: 0;
87 | right: 0;
88 | width: 250px;
89 | bottom: 0;
90 | border-left: 1px solid #CCC;
91 | }
92 | .minder-divider {
93 | position: absolute;
94 | top: 0;
95 | right: 250px;
96 | bottom: 0;
97 | width: 2px;
98 | background-color: rgb(251, 251, 251);
99 | cursor: ew-resize;
100 | }
101 |
102 | // @override bootstrap
103 | .panel-body {
104 | padding: 10px;
105 | }
106 |
107 | @import (less) "_vars.less";
108 | @import (less) "imageDialog.less";
109 | @import (less) "topTab/topTab.less";
110 | @import (less) "topTab/idea/undoRedo.less";
111 | @import (less) "topTab/idea/appendNode.less";
112 | @import (less) "topTab/idea/arrange.less";
113 | @import (less) "topTab/idea/operation.less";
114 | @import (less) "topTab/idea/hyperlink.less";
115 | @import (less) "topTab/idea/image.less";
116 | @import (less) "topTab/idea/note.less";
117 | @import (less) "topTab/idea/noteEditor.less";
118 | @import (less) "topTab/idea/priority.less";
119 | @import (less) "topTab/idea/progress.less";
120 | @import (less) "topTab/idea/resource.less";
121 | @import (less) "topTab/appearance/templatePanel.less";
122 | @import (less) "topTab/appearance/themePanel.less";
123 | @import (less) "topTab/appearance/layout.less";
124 | @import (less) "topTab/appearance/styleOperator.less";
125 | @import (less) "topTab/appearance/fontOperator.less";
126 | @import (less) "topTab/appearance/colorPanel.less";
127 | @import (less) "topTab/view/expand.less";
128 | @import (less) "topTab/view/select.less";
129 | @import (less) "topTab/view/search.less";
130 | @import (less) "topTab/searchBox.less";
131 | @import (less) "_tool_group.less";
132 | @import (less) "_navigator.less";
133 |
134 |
135 |
136 |
--------------------------------------------------------------------------------
/src/styles/imageDialog.less:
--------------------------------------------------------------------------------
1 | .upload-image {
2 | width: 0.1px;
3 | height: 0.1px;
4 | opacity: 0;
5 | overflow: hidden;
6 | position: absolute;
7 | z-index: -1;
8 | }
--------------------------------------------------------------------------------
/src/styles/images/iconpriority.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiangtao/vue-minder/b4cbaec72624f0fe761f7cc5286cec193cb3a571/src/styles/images/iconpriority.png
--------------------------------------------------------------------------------
/src/styles/images/iconprogress.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiangtao/vue-minder/b4cbaec72624f0fe761f7cc5286cec193cb3a571/src/styles/images/iconprogress.png
--------------------------------------------------------------------------------
/src/styles/images/icons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiangtao/vue-minder/b4cbaec72624f0fe761f7cc5286cec193cb3a571/src/styles/images/icons.png
--------------------------------------------------------------------------------
/src/styles/images/template.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiangtao/vue-minder/b4cbaec72624f0fe761f7cc5286cec193cb3a571/src/styles/images/template.png
--------------------------------------------------------------------------------
/src/styles/topTab/appearance/colorPanel.less:
--------------------------------------------------------------------------------
1 | .bg-color-wrap {
2 | display: inline-block;
3 | width: 30px;
4 | height: 22px;
5 | margin: 3px 3px 0 0;
6 | border: 1px #efefef solid;
7 | vertical-align: middle;
8 | font-size: 0;
9 | -webkit-user-select: none;
10 | -moz-user-select: none;
11 | -ms-user-select: none;
12 | user-select: none;
13 |
14 | &[disabled] {
15 | opacity: 0.5;
16 | }
17 |
18 | .quick-bg-color {
19 | display: inline-block;
20 | width: 20px;
21 | height: 16px;
22 | font-size: 14px;
23 | line-height: 16px;
24 | vertical-align: top;
25 | text-align: center;
26 | cursor: default;
27 | color: #000;
28 | background: url(images/icons.png) no-repeat center -1260px;
29 |
30 | &:hover {
31 | background-color: @tool-hover;
32 | }
33 |
34 | &:active {
35 | background-color: @tool-active;
36 | }
37 |
38 | &[disabled] {
39 | opacity: 0.5;
40 | }
41 | }
42 |
43 | .bg-color-preview {
44 | display: inline-block;
45 | width: 12px;
46 | height: 2px;
47 | margin: 0 4px 0;
48 | background-color: #fff;
49 |
50 | &[disabled] {
51 | opacity: 0.5;
52 | }
53 | }
54 | }
55 |
56 | .bg-color {
57 | display: inline-block;
58 | width: 8px;
59 | height: 16px;
60 |
61 | &:hover {
62 | background-color: @tool-hover;
63 | }
64 |
65 | &:active {
66 | background-color: @tool-active;
67 | }
68 |
69 | &[disabled] {
70 | opacity: 0.5;
71 | }
72 |
73 | .caret {
74 | margin-left: -2px;
75 | margin-top: 7px;
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/styles/topTab/appearance/fontOperator.less:
--------------------------------------------------------------------------------
1 | .font-operator {
2 | width: 170px;
3 | display: inline-block;
4 | vertical-align: middle;
5 | font-size: 12px;
6 | padding: 0 5px;
7 |
8 | .font-size-list {
9 | display: inline-block;
10 | border: 1px solid #eee;
11 | padding: 2px 4px;
12 | }
13 |
14 | .font-family-list {
15 | display: inline-block;
16 | border: 1px solid #eee;
17 | padding: 2px 4px;
18 | }
19 |
20 | }
21 |
22 | .current-font-item a {
23 | text-decoration: none;
24 | display: inline-block;
25 | }
26 |
27 | .current-font-family {
28 | width: 75px;
29 | height: 18px;
30 | overflow: hidden;
31 | vertical-align: bottom;
32 | }
33 | .current-font-size {
34 | width: 32px;
35 | height: 18px;
36 | overflow: hidden;
37 | vertical-align: bottom;
38 | }
39 |
40 | .current-font-item[disabled] {
41 | opacity: 0.5;
42 | }
43 |
44 | .font-item {
45 | line-height: 1em;
46 | text-align: left;
47 | }
48 |
49 | .font-item-selected {
50 | background-color: @tool-selected;
51 | }
52 |
53 | .font-bold, .font-italics {
54 | display: inline-block;
55 | background: url(images/icons.png) no-repeat;
56 | cursor: pointer;
57 | margin: 0 3px;
58 |
59 |
60 | &:hover {
61 | background-color: @tool-hover;
62 | }
63 |
64 | &:active {
65 | background-color: @tool-active;
66 | }
67 |
68 | &[disabled] {
69 | opacity: 0.5;
70 | }
71 | }
72 |
73 | .font-bold {
74 | background-position: 0 -240px;
75 | }
76 |
77 | .font-italics {
78 | background-position: 0 -260px;
79 | }
80 |
81 | .font-bold-selected, .font-italics-selected {
82 | background-color: @tool-selected;
83 | }
84 |
85 | .font-color-wrap {
86 | display: inline-block;
87 | width: 30px;
88 | height: 22px;
89 | margin: 3px 3px 0 0;
90 | border: 1px #efefef solid;
91 | vertical-align: middle;
92 | font-size: 0;
93 | -webkit-user-select: none;
94 | -moz-user-select: none;
95 | -ms-user-select: none;
96 | user-select: none;
97 |
98 | &[disabled] {
99 | opacity: 0.5;
100 | }
101 |
102 | .quick-font-color {
103 | display: inline-block;
104 | width: 20px;
105 | height: 16px;
106 | font-size: 14px;
107 | line-height: 16px;
108 | vertical-align: top;
109 | text-align: center;
110 | cursor: default;
111 | color: #000;
112 |
113 | &:hover {
114 | background-color: @tool-hover;
115 | }
116 |
117 | &:active {
118 | background-color: @tool-active;
119 | }
120 |
121 | &[disabled] {
122 | opacity: 0.5;
123 | }
124 | }
125 |
126 | .font-color-preview {
127 | display: inline-block;
128 | width: 12px;
129 | height: 2px;
130 | margin: 0 4px 0;
131 | background-color: #000;
132 |
133 | &[disabled] {
134 | opacity: 0.5;
135 | }
136 | }
137 | }
138 |
139 | .font-color {
140 | display: inline-block;
141 | width: 8px;
142 | height: 16px;
143 |
144 | &:hover {
145 | background-color: @tool-hover;
146 | }
147 |
148 | &:active {
149 | background-color: @tool-active;
150 | }
151 |
152 | &[disabled] {
153 | opacity: 0.5;
154 | }
155 |
156 | .caret {
157 | margin-left: -2px;
158 | margin-top: 7px;
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/src/styles/topTab/appearance/layout.less:
--------------------------------------------------------------------------------
1 | .readjust-layout {
2 | display: inline-block;
3 | vertical-align: middle;
4 | padding: 0 10px 0 5px;
5 | border-right: 1px dashed #eee;
6 | }
7 |
8 | .btn-icon {
9 | width: 25px;
10 | height: 25px;
11 | margin-left: 12px;
12 | display: block;
13 | }
14 |
15 | .btn-label {
16 | font-size: 12px;
17 | }
18 |
19 | .btn-wrap {
20 | width: 50px;
21 | height: 42px;
22 | cursor: pointer;
23 | display: inline-block;
24 | text-decoration: none;
25 |
26 | &[disabled] span {
27 | opacity: 0.5;
28 | }
29 |
30 | &[disabled] {
31 | cursor: default;
32 | }
33 |
34 | &[disabled]:hover {
35 | background-color: transparent;
36 | }
37 |
38 | &[disabled]:active {
39 | background-color: transparent;
40 | }
41 |
42 | &:link {
43 | text-decoration: none;
44 | }
45 |
46 | &:visited {
47 | text-decoration: none;
48 | }
49 |
50 | &:hover {
51 | background-color: @tool-hover;
52 | text-decoration: none;
53 | }
54 |
55 | &:active {
56 | background-color: @tool-active;
57 | }
58 |
59 | }
60 |
61 | .reset-layout-icon {
62 | background: url(images/icons.png) no-repeat;
63 | background-position: 0 -150px;
64 | }
65 |
66 |
--------------------------------------------------------------------------------
/src/styles/topTab/appearance/styleOperator.less:
--------------------------------------------------------------------------------
1 | .style-operator {
2 | display: inline-block;
3 | vertical-align: middle;
4 | padding: 0 5px;
5 | border-right: 1px dashed #eee;
6 |
7 | .clear-style {
8 | vertical-align: middle;
9 | }
10 |
11 | }
12 |
13 | .clear-style-icon {
14 | background: url(images/icons.png) no-repeat;
15 | background-position: 0 -175px;;
16 | }
17 |
18 | .s-btn-group-vertical {
19 | display: inline-block;
20 | vertical-align: middle;
21 | }
22 |
23 | .s-btn-icon {
24 | width: 20px;
25 | height: 20px;
26 | margin-right: 3px;
27 | display: inline-block;
28 | vertical-align: middle;
29 | }
30 |
31 | .s-btn-label {
32 | font-size: 12px;
33 | vertical-align: middle;
34 | display: inline-block;
35 | }
36 |
37 | .s-btn-wrap {
38 | // margin-bottom: 2px;
39 | padding: 0 5px 0 3px;
40 | display: inline-block;
41 | text-decoration: none;
42 | font-size: 0;
43 |
44 | &[disabled] span {
45 | opacity: 0.5;
46 | }
47 |
48 | &[disabled] {
49 | cursor: default;
50 | }
51 |
52 | &[disabled]:hover {
53 | background-color: transparent;
54 | }
55 |
56 | &[disabled]:active {
57 | background-color: transparent;
58 | }
59 |
60 | &:hover {
61 | background-color: @tool-hover;
62 | text-decoration: none;
63 | }
64 |
65 | &:active {
66 | background-color: @tool-active;
67 | }
68 |
69 | }
70 |
71 | .copy-style-icon {
72 | background: url(images/icons.png) no-repeat;
73 | background-position: 0 -200px;
74 | }
75 |
76 | .paste-style-wrap {
77 | display: block;
78 | }
79 |
80 | .paste-style-icon {
81 | background: url(../../../../assets/images/icons.png) no-repeat;
82 | background-position: 0 -220px;
83 | }
84 |
--------------------------------------------------------------------------------
/src/styles/topTab/appearance/templatePanel.less:
--------------------------------------------------------------------------------
1 | .temp-panel {
2 | margin: 5px 5px 5px 10px;
3 | border-right: 1px dashed #eee;
4 | display: inline-block;
5 | vertical-align: middle;
6 | }
7 |
8 | .temp-list {
9 | min-width: 124px;
10 | }
11 |
12 | .temp-item-wrap {
13 | width: 50px;
14 | height: 40px;
15 | padding: 0 2px;
16 | margin: 5px;
17 | display: inline-block;
18 | }
19 |
20 | .temp-item {
21 | display: inline-block;
22 | width: 50px;
23 | height: 40px;
24 | background-image: url(images/template.png);
25 | background-repeat: no-repeat;
26 |
27 | &.default {
28 | background-position: 0 0;
29 | }
30 |
31 | &.structure {
32 | background-position: -50px 0;
33 | }
34 |
35 | &.filetree {
36 | background-position: -100px 0;
37 | }
38 |
39 | &.right {
40 | background-position: -150px 0;
41 | }
42 |
43 | &.fish-bone {
44 | background-position: -200px 0;
45 | }
46 |
47 | &.tianpan {
48 | background-position: -250px 0;
49 | }
50 | }
51 |
52 | .current-temp-item {
53 | width: 74px;
54 | padding: 0 0 0 5px;
55 | border: 1px solid #fff;
56 |
57 | &:hover {
58 | background-color: @tool-hover;
59 | }
60 |
61 | &[disabled] {
62 | opacity: 0.5;
63 | }
64 |
65 | .caret {
66 | margin-left: 5px;
67 | }
68 | }
69 | .temp-item-selected {
70 | background-color: @tool-selected;
71 | }
72 |
--------------------------------------------------------------------------------
/src/styles/topTab/appearance/themePanel.less:
--------------------------------------------------------------------------------
1 | .theme-panel {
2 | height: 42px;
3 | margin: 5px;
4 | padding: 0 5px 0 0;
5 | border-right: 1px dashed #eee;
6 | display: inline-block;
7 | vertical-align: middle;
8 | }
9 |
10 | .theme-list {
11 | min-width: 162px;
12 | }
13 |
14 | div a.theme-item {
15 | display: inline-block;
16 | width: 70px;
17 | height: 30px;
18 | text-align: center;
19 | line-height: 30px;
20 | padding: 0 5px;
21 | font-size: 12px;
22 | cursor: pointer;
23 | text-decoration: none;
24 | color: #000;
25 | }
26 |
27 | .theme-item-selected {
28 | width: 100px;
29 | padding: 6px 7px;
30 | border: 1px solid #fff;
31 |
32 | &:hover {
33 | background-color: @tool-hover;
34 | }
35 |
36 | .caret {
37 | margin-left: 5px;
38 | }
39 |
40 | &[disabled] {
41 | opacity: 0.5;
42 | }
43 | }
44 |
45 | .theme-item-wrap {
46 | display: inline-block;
47 | width: 80px;
48 | height: 40px;
49 | padding: 5px;
50 | }
51 | .theme-item-wrap:hover {
52 | background-color: #eff3fa;
53 | }
--------------------------------------------------------------------------------
/src/styles/topTab/idea/appendNode.less:
--------------------------------------------------------------------------------
1 | .append-group {
2 | width: 212px;
3 | }
4 |
5 | .append-child-node {
6 | .km-btn-icon {
7 | background-position: 0 0;
8 | }
9 | }
10 |
11 | .append-sibling-node {
12 | .km-btn-icon {
13 | background-position: 0 -20px;
14 | }
15 | }
16 |
17 | .append-parent-node {
18 | .km-btn-icon {
19 | background-position: 0 -40px;
20 | }
21 | }
--------------------------------------------------------------------------------
/src/styles/topTab/idea/arrange.less:
--------------------------------------------------------------------------------
1 | .arrange-group {
2 | width: 64px;
3 | }
4 |
5 | .arrange-up {
6 | .km-btn-icon {
7 | background-position: 0 -280px;
8 | }
9 | }
10 |
11 | .arrange-down {
12 | .km-btn-icon {
13 | background-position: 0 -300px;
14 | }
15 | }
--------------------------------------------------------------------------------
/src/styles/topTab/idea/hyperlink.less:
--------------------------------------------------------------------------------
1 | .btn-group-vertical {
2 | vertical-align: middle;
3 | margin: 5px;
4 |
5 | .hyperlink, .hyperlink-caption {
6 | width: 40px;
7 | margin: 0;
8 | padding: 0;
9 | border: none!important;
10 | border-radius: 0!important;
11 |
12 | &:hover {
13 | background-color: @tool-hover;
14 | }
15 |
16 | &:active {
17 | background-color: @tool-active;
18 | }
19 |
20 | &.active {
21 | box-shadow: none;
22 | background-color: @tool-hover;
23 | }
24 | }
25 |
26 | .hyperlink {
27 | height: 25px;
28 | background: url(images/icons.png) no-repeat center -100px;
29 | }
30 |
31 | .hyperlink-caption {
32 | height: 20px;
33 |
34 | .caption {
35 | font-size: 12px;
36 | }
37 | }
38 | }
39 |
40 | //override bootstrap
41 | .open > .dropdown-toggle.btn-default {
42 | background-color: @tool-hover;
43 | }
44 |
--------------------------------------------------------------------------------
/src/styles/topTab/idea/image.less:
--------------------------------------------------------------------------------
1 | .btn-group-vertical {
2 |
3 | .image-btn, .image-btn-caption {
4 | width: 40px;
5 | margin: 0;
6 | padding: 0;
7 | border: none!important;
8 | border-radius: 0!important;
9 |
10 | &:hover {
11 | background-color: @tool-hover;
12 | }
13 |
14 | &:active {
15 | background-color: @tool-active;
16 | }
17 |
18 | &.active {
19 | box-shadow: none;
20 | background-color: @tool-hover;
21 | }
22 | }
23 |
24 | .image-btn {
25 | height: 25px;
26 | background: url(images/icons.png) no-repeat center -125px;
27 | }
28 |
29 | .image-btn-caption {
30 | height: 20px;
31 |
32 | .caption {
33 | font-size: 12px;
34 | }
35 | }
36 | }
37 |
38 | .image-preview {
39 | display: block;
40 | max-width: 50%;
41 | }
42 |
43 | .modal-body {
44 | .tab-pane {
45 | font-size: inherit;
46 | padding-top: 15px;
47 | }
48 | }
49 |
50 | .search-result {
51 | margin-top: 15px;
52 | height: 370px;
53 | overflow: hidden;
54 |
55 | ul {
56 | margin: 0;
57 | padding: 0;
58 | list-style: none;
59 | clear: both;
60 | height: 100%;
61 | overflow-x: hidden;
62 | overflow-y: auto;
63 |
64 | li {
65 | list-style: none;
66 | float: left;
67 | display: block;
68 | width: 130px;
69 | height: 130px;
70 | line-height: 130px;
71 | margin: 6px;
72 | padding: 0;
73 | font-size: 12px;
74 | position: relative;
75 | vertical-align: top;
76 | text-align: center;
77 | overflow: hidden;
78 | cursor: pointer;
79 | border: 2px solid #fcfcfc;
80 |
81 | &.selected {
82 | border: 2px solid #fc8383;
83 | }
84 |
85 |
86 | img {
87 | max-width: 126px;
88 | max-height: 130px;
89 | vertical-align: middle;
90 | }
91 |
92 | span {
93 | display: block;
94 | position: absolute;
95 | bottom: 0;
96 | height: 20px;
97 | background: rgba(0, 0, 0, 0.5);
98 | left: 0;
99 | right: 0;
100 | color: white;
101 | line-height: 20px;
102 | overflow: hidden;
103 | text-overflow: ellipsis;
104 | word-break: break-all;
105 | white-space: nowrap;
106 | opacity: 0;
107 | -webkit-transform: translate(0, 20px);
108 | -ms-transform: translate(0, 20px);
109 | transform: translate(0, 20px);
110 | -webkit-transition: all .2s ease;
111 | transition: all .2s ease;
112 | }
113 | }
114 |
115 | li:hover span {
116 | opacity: 1;
117 | -webkit-transform: translate(0, 0);
118 | -ms-transform: translate(0, 0);
119 | transform: translate(0, 0);
120 | }
121 | }
122 | }
123 |
124 | // 覆盖 bootstrap 样式
125 | @media (min-width: 768px){
126 | .form-inline .form-control {
127 | width: 422px;
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/src/styles/topTab/idea/note.less:
--------------------------------------------------------------------------------
1 | .btn-group-vertical {
2 | vertical-align: top;
3 | margin: 5px;
4 |
5 | &.note-btn-group {
6 | border-right: 1px dashed #eee;
7 | padding-right: 5px;
8 | }
9 |
10 | .note-btn, .note-btn-caption {
11 | width: 40px;
12 | margin: 0;
13 | padding: 0;
14 | border: none!important;
15 | border-radius: 0!important;
16 |
17 | &:hover {
18 | background-color: @tool-hover;
19 | }
20 |
21 | &:active {
22 | background-color: @tool-active;
23 | }
24 |
25 | &.active {
26 | box-shadow: none;
27 | background-color: @tool-hover;
28 | }
29 | }
30 |
31 | .note-btn {
32 | height: 25px;
33 | background: url(images/icons.png) no-repeat center -1150px;
34 | }
35 |
36 | .note-btn-caption {
37 | height: 20px;
38 |
39 | .caption {
40 | font-size: 12px;
41 | }
42 | }
43 | }
44 |
45 | //override bootstrap
46 | .open > .dropdown-toggle.btn-default {
47 | background-color: @tool-hover;
48 | }
49 |
--------------------------------------------------------------------------------
/src/styles/topTab/idea/noteEditor.less:
--------------------------------------------------------------------------------
1 | .gfm-render {
2 |
3 | font-size: 12px;
4 | -webkit-user-select: text;
5 | color: #333;
6 | line-height: 1.8em;
7 |
8 | blockquote, ul, table, p, pre, hr {
9 | margin: 1em 0;
10 | cursor: text;
11 | &:first-child:last-child {
12 | margin: 0;
13 | }
14 | }
15 |
16 | img {
17 | max-width: 100%;
18 | }
19 |
20 | a {
21 | color: blue;
22 | &:hover {
23 | color: red;
24 | }
25 | }
26 |
27 | blockquote {
28 | display: block;
29 | border-left: 4px solid #E4AD91;
30 | color: darken(#E4AD91, 10%);
31 | padding-left: 10px;
32 | font-style: italic;
33 | margin-left: 2em;
34 | }
35 |
36 | ul, ol {
37 | padding-left: 3em;
38 | }
39 |
40 | table {
41 | width: 100%;
42 | border-collapse: collapse;
43 | th, td {
44 | border: 1px solid #666;
45 | padding: 2px 4px;
46 | }
47 | th {
48 | background: rgba(45, 141, 234, 0.2);
49 | }
50 | tr:nth-child(even) td {
51 | background: rgba(45, 141, 234, 0.03);
52 | }
53 | margin: 1em 0;
54 | }
55 |
56 | em {
57 | color: red;
58 | }
59 |
60 | del {
61 | color: #999;
62 | }
63 |
64 | pre {
65 | background: rgba(45, 141, 234, 0.1);
66 | padding: 5px;
67 | border-radius: 5px;
68 | word-break: break-all;
69 | word-wrap: break-word;
70 | }
71 |
72 | code {
73 | background: rgba(45, 141, 234, 0.1);
74 | /* display: inline-block; */
75 | padding: 0 5px;
76 | border-radius: 3px;
77 | }
78 |
79 | pre code {
80 | background: none;
81 | }
82 |
83 | hr {
84 | border: none;
85 | border-top: 1px solid #CCC;
86 | }
87 |
88 | .highlight {
89 | background: yellow;
90 | color: red;
91 | }
92 | }
93 |
94 | .km-note {
95 | width: 300px;
96 | border-left: 1px solid #babfcd;
97 | padding: 5px 10px;
98 | background: white;
99 | position: absolute;
100 | top: 92px;
101 | right: 0;
102 | bottom: 0;
103 | left: auto;
104 | z-index: 3;
105 |
106 | &.panel {
107 | margin: 0;
108 | padding: 0;
109 |
110 | .panel-heading {
111 |
112 | h3 {
113 | display: inline-block;
114 | }
115 |
116 | .close-note-editor {
117 | width: 15px;
118 | height: 15px;
119 | display: inline-block;
120 | float: right;
121 |
122 | &:hover {
123 | cursor: pointer;
124 | }
125 | }
126 | }
127 |
128 | .panel-body {
129 | padding: 0;
130 | }
131 | }
132 |
133 | .CodeMirror {
134 | position: absolute;
135 | top: 41px;
136 | bottom: 0;
137 | height: auto;
138 | cursor: text;
139 | font-size: 14px;
140 | line-height: 1.3em;
141 | font-family: consolas;
142 | }
143 | }
144 | .km-note-tips {
145 | color: #ccc;
146 | padding: 3px 8px;
147 | }
148 | #previewer-content {
149 | position: absolute;
150 | background: #FFD;
151 | padding: 5px 15px;
152 | border-radius: 5px;
153 | max-width: 400px;
154 | max-height: 200px;
155 | overflow: auto;
156 | z-index: 10;
157 | box-shadow: 0 0 15px rgba(0, 0, 0, .5);
158 | word-break: break-all;
159 | .gfm-render;
160 | }
161 | #previewer-content.ng-hide {
162 | display: block!important;
163 | left: -99999px!important;
164 | top: -99999px!important;
165 | }
166 | .panel-body {
167 | padding: 10px;
168 | }
--------------------------------------------------------------------------------
/src/styles/topTab/idea/operation.less:
--------------------------------------------------------------------------------
1 | .operation-group {
2 | width: 64px;
3 | }
4 |
5 | .edit-node {
6 | .km-btn-icon {
7 | background-position: 0 -60px;
8 | }
9 | }
10 |
11 | .remove-node {
12 | .km-btn-icon {
13 | background-position: 0 -80px;
14 | }
15 | }
--------------------------------------------------------------------------------
/src/styles/topTab/idea/priority.less:
--------------------------------------------------------------------------------
1 | .priority-sprite(@count) when (@count >= 0) {
2 | .priority-sprite(@count - 1);
3 | &.priority-@{count} {
4 | background-position: 0 (-20px * (@count - 1));
5 | }
6 | }
7 |
8 | .tab-content .km-priority {
9 | vertical-align: middle;
10 | font-size: inherit;
11 | display: inline-block;
12 | width: 140px;
13 | margin: 5px;
14 | border-right: 1px dashed #eee;
15 |
16 | .km-priority-item {
17 | margin: 0 1px;
18 | padding: 1px;
19 |
20 | .km-priority-icon {
21 | .priority-sprite(9);
22 | background: url(images/iconpriority.png) repeat-y;
23 | background-color: transparent;
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/styles/topTab/idea/progress.less:
--------------------------------------------------------------------------------
1 | .progress-sprite(@count) when (@count >= 0) {
2 | .progress-sprite(@count - 1);
3 | &.progress-@{count} {
4 | background-position: 0 (-20px * (@count - 1));
5 | }
6 | }
7 |
8 | .tab-content .km-progress {
9 | vertical-align: middle;
10 | font-size: inherit;
11 | display: inline-block;
12 | width: 140px;
13 | margin: 5px;
14 | border-right: 1px dashed #eee;
15 |
16 | .km-progress-item {
17 | margin: 0 1px;
18 | padding: 1px;
19 |
20 | .km-progress-icon {
21 | .progress-sprite(9);
22 | background: url(images/iconprogress.png) repeat-y;
23 | background-color: transparent;
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/styles/topTab/idea/resource.less:
--------------------------------------------------------------------------------
1 | .resource-editor {
2 | vertical-align: middle;
3 | display: inline-block;
4 | margin: 5px;
5 |
6 | .input-group, .km-resource {
7 | font-size: 12px;
8 | }
9 |
10 | .input-group {
11 | height: 20px;
12 | width: 168px;
13 | }
14 |
15 | .resource-dropdown {
16 | position: relative;
17 | width: 168px;
18 | border: 1px solid #ccc;
19 | margin-top: -1px;
20 | border-bottom-right-radius: 4px;
21 | border-bottom-left-radius: 4px;
22 |
23 | .km-resource {
24 | position: absolute;
25 | width: 154px;
26 | margin-bottom: 3px;
27 | padding: 0;
28 | list-style-type: none;
29 | overflow: scroll;
30 | max-height: 500px;
31 |
32 | &.open {
33 | z-index: 3;
34 | background-color: #fff;
35 | }
36 |
37 | li {
38 | display: inline-block;
39 | padding: 1px 2px;
40 | border-radius: 4px;
41 | margin: 2px 3px;
42 |
43 | &[disabled] {
44 | opacity: 0.5;
45 | }
46 | }
47 | }
48 |
49 | .resource-caret {
50 | display: block;
51 | float: right;
52 | vertical-align: middle;
53 | width: 12px;
54 | height: 24px;
55 | padding: 8px 1px;
56 |
57 | &:hover {background-color: @button-hover;}
58 | &:active {background-color: @button-active;}
59 | }
60 | }
61 |
62 | // 覆盖 bootstrap
63 | input.form-control, .btn {
64 | font-size: 12px;
65 | }
66 |
67 | input.form-control {
68 | padding: 2px 4px;
69 | height: 24px;
70 | border-bottom-left-radius: 0;
71 | }
72 |
73 | .input-group-btn {
74 | line-height: 24px;
75 |
76 | .btn {
77 | padding: 2px 4px;
78 | height: 24px;
79 | border-bottom-right-radius: 0;
80 | }
81 | }
82 | }
83 |
84 |
--------------------------------------------------------------------------------
/src/styles/topTab/idea/undoRedo.less:
--------------------------------------------------------------------------------
1 | .do-group {
2 | width: 38px;
3 | }
4 |
5 | .undo {
6 | .km-btn-icon {
7 | background-position: 0 -1240px;
8 | }
9 | }
10 |
11 | .redo {
12 | .km-btn-icon {
13 | background-position: 0 -1220px;
14 | }
15 | }
--------------------------------------------------------------------------------
/src/styles/topTab/searchBox.less:
--------------------------------------------------------------------------------
1 | .search-box {
2 | float: right;
3 | background-color: #fff;
4 | border: 1px solid #dbdbdb;
5 | position: relative;
6 | top: 0;
7 | z-index: 3;
8 | width: 330px;
9 | height: 40px;
10 | padding: 3px 6px;
11 | opacity: 1;
12 |
13 | .search-input-wrap, .prev-and-next-btn {
14 | float: left;
15 | }
16 |
17 | .close-search {
18 | float: right;
19 | height: 16px;
20 | width: 16px;
21 | padding: 1px;
22 | border-radius: 100%;
23 | margin-top: 6px;
24 | margin-right: 10px;
25 |
26 | .glyphicon {
27 | top: -1px
28 | }
29 |
30 | &:hover {
31 | background-color: #efefef;
32 | }
33 |
34 | &:active {
35 | background-color: #999;
36 | }
37 | }
38 |
39 | .search-input-wrap {
40 | width: 240px;
41 | }
42 |
43 | .prev-and-next-btn {
44 | margin-left: 5px;
45 |
46 | .btn:focus {
47 | outline: none;
48 | }
49 | }
50 |
51 | .search-input {
52 | //border-right: none;
53 | }
54 |
55 | .search-addon {
56 | background-color: #fff;
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/src/styles/topTab/topTab.less:
--------------------------------------------------------------------------------
1 | .top-tab {
2 | .nav-tabs {
3 | background-color: #e1e1e1;
4 | border: 0;
5 | height: 32px;
6 |
7 | li {
8 | margin: 0;
9 |
10 | a {
11 | margin: 0;
12 | border: 0;
13 | padding: 6px 15px;
14 | border-radius: 0;
15 | vertical-align: middle;
16 |
17 |
18 | &:hover, &:focus {
19 | background: inherit;
20 | border: 0;
21 | }
22 | }
23 |
24 |
25 | &.active a {
26 | border: 0;
27 | background-color: #fff;
28 |
29 | &:hover, &:focus {
30 | border: 0;
31 | }
32 | }
33 | }
34 | }
35 |
36 | .tab-content {
37 | height: 60px;
38 | background-color: #fff;
39 | border-bottom: 1px solid #dbdbdb;
40 | }
41 |
42 | .tab-pane {
43 | font-size: 0;
44 | }
45 | }
46 |
47 | .km-btn-group {
48 | display: inline-block;
49 | margin: 5px 0;
50 | padding: 0 5px;
51 | vertical-align: middle;
52 | border-right: 1px dashed #eee;
53 | }
54 |
55 | .km-btn-item {
56 | display: inline-block;
57 | margin: 0 3px;
58 | font-size: 0;
59 | cursor: default;
60 |
61 | &[disabled] {
62 | opacity: 0.5;
63 |
64 | &:hover, &:active {
65 | background-color: #fff;
66 | }
67 | }
68 |
69 | .km-btn-icon {
70 | display: inline-block;
71 | background: url(images/icons.png) no-repeat;
72 | background-position: 0 20px;
73 | width: 20px;
74 | height: 20px;
75 | padding: 2px;
76 | margin: 1px;
77 | vertical-align: middle;
78 | }
79 |
80 | .km-btn-caption {
81 | display: inline-block;
82 | font-size: 12px;
83 | vertical-align: middle;
84 | }
85 |
86 | &:hover {background-color: @button-hover;}
87 | &:active {background-color: @button-active;}
88 | }
89 |
--------------------------------------------------------------------------------
/src/styles/topTab/view/expand.less:
--------------------------------------------------------------------------------
1 | .btn-group-vertical {
2 | vertical-align: middle;
3 | margin: 5px;
4 |
5 | .expand, .expand-caption {
6 | width: 40px;
7 | margin: 0;
8 | padding: 0;
9 | border: none!important;
10 | border-radius: 0!important;
11 |
12 | &:hover {
13 | background-color: @tool-hover;
14 | }
15 |
16 | &:active {
17 | background-color: @tool-active;
18 | }
19 |
20 | &.active {
21 | box-shadow: none;
22 | background-color: @tool-hover;
23 | }
24 | }
25 |
26 | .expand {
27 | height: 25px;
28 | background: url(images/icons.png) no-repeat 0 -995px;
29 | background-position-x: 50%;
30 | }
31 |
32 | .expand-caption {
33 | height: 20px;
34 |
35 | .caption {
36 | font-size: 12px;
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/styles/topTab/view/search.less:
--------------------------------------------------------------------------------
1 | .btn-group-vertical {
2 | vertical-align: middle;
3 | margin: 5px;
4 |
5 | .search, .search-caption {
6 | width: 40px;
7 | margin: 0;
8 | padding: 0;
9 | border: none!important;
10 | border-radius: 0!important;
11 |
12 | &:hover {
13 | background-color: @tool-hover;
14 | }
15 |
16 | &:active {
17 | background-color: @tool-active;
18 | }
19 |
20 | &.active {
21 | box-shadow: none;
22 | background-color: @tool-hover;
23 | }
24 | }
25 |
26 | .search {
27 | height: 25px;
28 | background: url(images/icons.png) no-repeat 0 -345px;
29 | background-position-x: 50%;
30 | }
31 |
32 | .search-caption {
33 | height: 20px;
34 |
35 | .caption {
36 | font-size: 12px;
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/styles/topTab/view/select.less:
--------------------------------------------------------------------------------
1 | .btn-group-vertical {
2 | vertical-align: middle;
3 | margin: 5px;
4 |
5 | .select, .select-caption {
6 | width: 40px;
7 | margin: 0;
8 | padding: 0;
9 | border: none!important;
10 | border-radius: 0!important;
11 |
12 | &:hover {
13 | background-color: @tool-hover;
14 | }
15 |
16 | &:active {
17 | background-color: @tool-active;
18 | }
19 |
20 | &.active {
21 | box-shadow: none;
22 | background-color: @tool-hover;
23 | }
24 | }
25 |
26 | .select {
27 | height: 25px;
28 | background: url(images/icons.png) no-repeat 7px -1175px;
29 | }
30 |
31 | .select-caption {
32 | height: 20px;
33 |
34 | .caption {
35 | font-size: 12px;
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/tool/debug.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileOverview
3 | *
4 | * 支持各种调试后门
5 | *
6 | * @author: techird
7 | * @copyright: Baidu FEX, 2014
8 | */
9 | define(function(require, exports, module) {
10 | var format = require('./format');
11 |
12 | function noop() {}
13 |
14 | function stringHash(str) {
15 | var hash = 0;
16 | for (var i = 0; i < str.length; i++) {
17 | hash += str.charCodeAt(i);
18 | }
19 | return hash;
20 | }
21 |
22 | /* global console */
23 | function Debug(flag) {
24 | var debugMode = this.flaged = window.location.search.indexOf(flag) != -1;
25 |
26 | if (debugMode) {
27 | var h = stringHash(flag) % 360;
28 |
29 | var flagStyle = format(
30 | 'background: hsl({0}, 50%, 80%); ' +
31 | 'color: hsl({0}, 100%, 30%); ' +
32 | 'padding: 2px 3px; ' +
33 | 'margin: 1px 3px 0 0;' +
34 | 'border-radius: 2px;', h);
35 |
36 | var textStyle = 'background: none; color: black;';
37 | this.log = function() {
38 | var output = format.apply(null, arguments);
39 | };
40 | } else {
41 | this.log = noop;
42 | }
43 | }
44 |
45 | return module.exports = Debug;
46 | });
47 |
--------------------------------------------------------------------------------
/src/tool/format.js:
--------------------------------------------------------------------------------
1 | define(function(require, exports, module) {
2 | function format(template, args) {
3 | if (typeof(args) != 'object') {
4 | args = [].slice.call(arguments, 1);
5 | }
6 | return String(template).replace(/\{(\w+)\}/ig, function(match, $key) {
7 | return args[$key] || $key;
8 | });
9 | }
10 | return module.exports = format;
11 | });
--------------------------------------------------------------------------------
/src/tool/innertext.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileOverview
3 | *
4 | * innerText polyfill
5 | *
6 | * @author: techird
7 | * @copyright: Baidu FEX, 2014
8 | */
9 |
10 |
11 | define(function(require, exports, module) {
12 | if ((!('innerText' in document.createElement('a'))) && ('getSelection' in window)) {
13 | HTMLElement.prototype.__defineGetter__('innerText', function() {
14 | var selection = window.getSelection(),
15 | ranges = [],
16 | str, i;
17 |
18 | // Save existing selections.
19 | for (i = 0; i < selection.rangeCount; i++) {
20 | ranges[i] = selection.getRangeAt(i);
21 | }
22 |
23 | // Deselect everything.
24 | selection.removeAllRanges();
25 |
26 | // Select `el` and all child nodes.
27 | // 'this' is the element .innerText got called on
28 | selection.selectAllChildren(this);
29 |
30 | // Get the string representation of the selected nodes.
31 | str = selection.toString();
32 |
33 | // Deselect everything. Again.
34 | selection.removeAllRanges();
35 |
36 | // Restore all formerly existing selections.
37 | for (i = 0; i < ranges.length; i++) {
38 | selection.addRange(ranges[i]);
39 | }
40 |
41 | // Oh look, this is what we wanted.
42 | // String representation of the element, close to as rendered.
43 | return str;
44 | });
45 | HTMLElement.prototype.__defineSetter__('innerText', function(text) {
46 | /**
47 | * @Desc: 解决FireFox节点内容删除后text为null,出现报错的问题
48 | * @Editor: Naixor
49 | * @Date: 2015.9.16
50 | */
51 | this.innerHTML = (text || '').replace(//g, '>').replace(/\n/g, '
');
52 | });
53 | }
54 | });
--------------------------------------------------------------------------------
/src/tool/jsondiff.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileOverview
3 | *
4 | *
5 | *
6 | * @author: techird
7 | * @copyright: Baidu FEX, 2014
8 | */
9 |
10 |
11 | define(function(require, exports, module) {
12 | /*!
13 | * https://github.com/Starcounter-Jack/Fast-JSON-Patch
14 | * json-patch-duplex.js 0.5.0
15 | * (c) 2013 Joachim Wester
16 | * MIT license
17 | */
18 |
19 | var _objectKeys = (function () {
20 | if (Object.keys)
21 | return Object.keys;
22 |
23 | return function (o) {
24 | var keys = [];
25 | for (var i in o) {
26 | if (o.hasOwnProperty(i)) {
27 | keys.push(i);
28 | }
29 | }
30 | return keys;
31 | };
32 | })();
33 | function escapePathComponent(str) {
34 | if (str.indexOf('/') === -1 && str.indexOf('~') === -1)
35 | return str;
36 | return str.replace(/~/g, '~0').replace(/\//g, '~1');
37 | }
38 | function deepClone(obj) {
39 | if (typeof obj === "object") {
40 | return JSON.parse(JSON.stringify(obj));
41 | } else {
42 | return obj;
43 | }
44 | }
45 |
46 | // Dirty check if obj is different from mirror, generate patches and update mirror
47 | function _generate(mirror, obj, patches, path) {
48 | var newKeys = _objectKeys(obj);
49 | var oldKeys = _objectKeys(mirror);
50 | var changed = false;
51 | var deleted = false;
52 |
53 | for (var t = oldKeys.length - 1; t >= 0; t--) {
54 | var key = oldKeys[t];
55 | var oldVal = mirror[key];
56 | if (obj.hasOwnProperty(key)) {
57 | var newVal = obj[key];
58 | if (typeof oldVal == "object" && oldVal != null && typeof newVal == "object" && newVal != null) {
59 | _generate(oldVal, newVal, patches, path + "/" + escapePathComponent(key));
60 | } else {
61 | if (oldVal != newVal) {
62 | changed = true;
63 | patches.push({ op: "replace", path: path + "/" + escapePathComponent(key), value: deepClone(newVal) });
64 | }
65 | }
66 | } else {
67 | patches.push({ op: "remove", path: path + "/" + escapePathComponent(key) });
68 | deleted = true; // property has been deleted
69 | }
70 | }
71 |
72 | if (!deleted && newKeys.length == oldKeys.length) {
73 | return;
74 | }
75 |
76 | for (var t = 0; t < newKeys.length; t++) {
77 | var key = newKeys[t];
78 | if (!mirror.hasOwnProperty(key)) {
79 | patches.push({ op: "add", path: path + "/" + escapePathComponent(key), value: deepClone(obj[key]) });
80 | }
81 | }
82 | }
83 |
84 | function compare(tree1, tree2) {
85 | var patches = [];
86 | _generate(tree1, tree2, patches, '');
87 | return patches;
88 | }
89 |
90 | return module.exports = compare;
91 | });
--------------------------------------------------------------------------------
/src/tool/key.js:
--------------------------------------------------------------------------------
1 | define(function(require, exports, module) {
2 | var keymap = require('./keymap');
3 |
4 | var CTRL_MASK = 0x1000;
5 | var ALT_MASK = 0x2000;
6 | var SHIFT_MASK = 0x4000;
7 |
8 | function hash(unknown) {
9 | if (typeof(unknown) == 'string') {
10 | return hashKeyExpression(unknown);
11 | }
12 | return hashKeyEvent(unknown);
13 | }
14 | function is(a, b) {
15 | return a && b && hash(a) == hash(b);
16 | }
17 | exports.hash = hash;
18 | exports.is = is;
19 |
20 |
21 | function hashKeyEvent(keyEvent) {
22 | var hashCode = 0;
23 | if (keyEvent.ctrlKey || keyEvent.metaKey) {
24 | hashCode |= CTRL_MASK;
25 | }
26 | if (keyEvent.altKey) {
27 | hashCode |= ALT_MASK;
28 | }
29 | if (keyEvent.shiftKey) {
30 | hashCode |= SHIFT_MASK;
31 | }
32 | // Shift, Control, Alt KeyCode ignored.
33 | if ([16, 17, 18, 91].indexOf(keyEvent.keyCode) === -1) {
34 | /**
35 | * 解决浏览器输入法状态下对keyDown的keyCode判断不准确的问题,使用keyIdentifier,
36 | * 可以解决chrome和safari下的各种问题,其他浏览器依旧有问题,然而那并不影响我们对特
37 | * 需判断的按键进行判断(比如Space在safari输入法态下就是229,其他的就不是)
38 | * @editor Naixor
39 | * @Date 2015-12-2
40 | */
41 | if (keyEvent.keyCode === 229 && keyEvent.keyIdentifier) {
42 | return hashCode |= parseInt(keyEvent.keyIdentifier.substr(2), 16);
43 | }
44 | hashCode |= keyEvent.keyCode;
45 | }
46 | return hashCode;
47 | }
48 |
49 | function hashKeyExpression(keyExpression) {
50 | var hashCode = 0;
51 | keyExpression.toLowerCase().split(/\s*\+\s*/).forEach(function(name) {
52 | switch(name) {
53 | case 'ctrl':
54 | case 'cmd':
55 | hashCode |= CTRL_MASK;
56 | break;
57 | case 'alt':
58 | hashCode |= ALT_MASK;
59 | break;
60 | case 'shift':
61 | hashCode |= SHIFT_MASK;
62 | break;
63 | default:
64 | hashCode |= keymap[name];
65 | }
66 | });
67 | return hashCode;
68 | }
69 | });
70 |
--------------------------------------------------------------------------------
/src/tool/keymap.js:
--------------------------------------------------------------------------------
1 | define(function(require, exports, module) {
2 | var keymap = {
3 |
4 | 'Shift': 16,
5 | 'Control': 17,
6 | 'Alt': 18,
7 | 'CapsLock': 20,
8 |
9 | 'BackSpace': 8,
10 | 'Tab': 9,
11 | 'Enter': 13,
12 | 'Esc': 27,
13 | 'Space': 32,
14 |
15 | 'PageUp': 33,
16 | 'PageDown': 34,
17 | 'End': 35,
18 | 'Home': 36,
19 |
20 | 'Insert': 45,
21 |
22 | 'Left': 37,
23 | 'Up': 38,
24 | 'Right': 39,
25 | 'Down': 40,
26 |
27 | 'Direction': {
28 | 37: 1,
29 | 38: 1,
30 | 39: 1,
31 | 40: 1
32 | },
33 |
34 | 'Del': 46,
35 |
36 | 'NumLock': 144,
37 |
38 | 'Cmd': 91,
39 | 'CmdFF': 224,
40 | 'F1': 112,
41 | 'F2': 113,
42 | 'F3': 114,
43 | 'F4': 115,
44 | 'F5': 116,
45 | 'F6': 117,
46 | 'F7': 118,
47 | 'F8': 119,
48 | 'F9': 120,
49 | 'F10': 121,
50 | 'F11': 122,
51 | 'F12': 123,
52 |
53 | '`': 192,
54 | '=': 187,
55 | '-': 189,
56 |
57 | '/': 191,
58 | '.': 190
59 | };
60 |
61 | // 小写适配
62 | for (var key in keymap) {
63 | if (keymap.hasOwnProperty(key)) {
64 | keymap[key.toLowerCase()] = keymap[key];
65 | }
66 | }
67 | var aKeyCode = 65;
68 | var aCharCode = 'a'.charCodeAt(0);
69 |
70 | // letters
71 | 'abcdefghijklmnopqrstuvwxyz'.split('').forEach(function(letter) {
72 | keymap[letter] = aKeyCode + (letter.charCodeAt(0) - aCharCode);
73 | });
74 |
75 | // numbers
76 | var n = 9;
77 | do {
78 | keymap[n.toString()] = n + 48;
79 | } while (--n);
80 |
81 | module.exports = keymap;
82 | });
--------------------------------------------------------------------------------
/src/utils/assist.js:
--------------------------------------------------------------------------------
1 | // 判断参数是否是其中之一
2 | export function oneOf (value, validList) {
3 | for (let i = 0; i < validList.length; i++) {
4 | if (value === validList[i]) {
5 | return true;
6 | }
7 | }
8 | return false;
9 | }
10 |
11 | export function camelcaseToHyphen (str) {
12 | return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
13 | }
14 |
15 | // For Modal scrollBar hidden
16 | let cached;
17 | export function getScrollBarSize (fresh) {
18 | if (fresh || cached === undefined) {
19 | const inner = document.createElement('div');
20 | inner.style.width = '100%';
21 | inner.style.height = '200px';
22 |
23 | const outer = document.createElement('div');
24 | const outerStyle = outer.style;
25 |
26 | outerStyle.position = 'absolute';
27 | outerStyle.top = 0;
28 | outerStyle.left = 0;
29 | outerStyle.pointerEvents = 'none';
30 | outerStyle.visibility = 'hidden';
31 | outerStyle.width = '200px';
32 | outerStyle.height = '150px';
33 | outerStyle.overflow = 'hidden';
34 |
35 | outer.appendChild(inner);
36 |
37 | document.body.appendChild(outer);
38 |
39 | const widthContained = inner.offsetWidth;
40 | outer.style.overflow = 'scroll';
41 | let widthScroll = inner.offsetWidth;
42 |
43 | if (widthContained === widthScroll) {
44 | widthScroll = outer.clientWidth;
45 | }
46 |
47 | document.body.removeChild(outer);
48 |
49 | cached = widthContained - widthScroll;
50 | }
51 | return cached;
52 | }
53 |
54 | // watch DOM change
55 | export const MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver || false;
56 |
57 | const SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
58 | const MOZ_HACK_REGEXP = /^moz([A-Z])/;
59 |
60 | function camelCase(name) {
61 | return name.replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
62 | return offset ? letter.toUpperCase() : letter;
63 | }).replace(MOZ_HACK_REGEXP, 'Moz$1');
64 | }
65 | // getStyle
66 | export function getStyle (element, styleName) {
67 | if (!element || !styleName) return null;
68 | styleName = camelCase(styleName);
69 | if (styleName === 'float') {
70 | styleName = 'cssFloat';
71 | }
72 | try {
73 | const computed = document.defaultView.getComputedStyle(element, '');
74 | return element.style[styleName] || computed ? computed[styleName] : null;
75 | } catch(e) {
76 | return element.style[styleName];
77 | }
78 | }
79 |
80 | // firstUpperCase
81 | function firstUpperCase(str) {
82 | return str.toString()[0].toUpperCase() + str.toString().slice(1);
83 | }
84 | export {firstUpperCase};
85 |
86 | // Warn
87 | export function warnProp(component, prop, correctType, wrongType) {
88 | correctType = firstUpperCase(correctType);
89 | wrongType = firstUpperCase(wrongType);
90 | console.error(`[iView warn]: Invalid prop: type check failed for prop ${prop}. Expected ${correctType}, got ${wrongType}. (found in component: ${component})`); // eslint-disable-line
91 | }
92 |
93 | function typeOf(obj) {
94 | const toString = Object.prototype.toString;
95 | const map = {
96 | '[object Boolean]' : 'boolean',
97 | '[object Number]' : 'number',
98 | '[object String]' : 'string',
99 | '[object Function]' : 'function',
100 | '[object Array]' : 'array',
101 | '[object Date]' : 'date',
102 | '[object RegExp]' : 'regExp',
103 | '[object Undefined]': 'undefined',
104 | '[object Null]' : 'null',
105 | '[object Object]' : 'object'
106 | };
107 | return map[toString.call(obj)];
108 | }
109 |
110 | // deepCopy
111 | function deepCopy(data) {
112 | const t = typeOf(data);
113 | let o;
114 |
115 | if (t === 'array') {
116 | o = [];
117 | } else if ( t === 'object') {
118 | o = {};
119 | } else {
120 | return data;
121 | }
122 |
123 | if (t === 'array') {
124 | for (let i = 0; i < data.length; i++) {
125 | o.push(deepCopy(data[i]));
126 | }
127 | } else if ( t === 'object') {
128 | for (let i in data) {
129 | o[i] = deepCopy(data[i]);
130 | }
131 | }
132 | return o;
133 | }
134 |
135 | export {deepCopy};
136 |
137 | // scrollTop animation
138 | export function scrollTop(el, from = 0, to, duration = 500) {
139 | if (!window.requestAnimationFrame) {
140 | window.requestAnimationFrame = (
141 | window.webkitRequestAnimationFrame ||
142 | window.mozRequestAnimationFrame ||
143 | window.msRequestAnimationFrame ||
144 | function (callback) {
145 | return window.setTimeout(callback, 1000/60);
146 | }
147 | );
148 | }
149 | const difference = Math.abs(from - to);
150 | const step = Math.ceil(difference / duration * 50);
151 |
152 | function scroll(start, end, step) {
153 | if (start === end) return;
154 |
155 | let d = (start + step > end) ? end : start + step;
156 | if (start > end) {
157 | d = (start - step < end) ? end : start - step;
158 | }
159 |
160 | if (el === window) {
161 | window.scrollTo(d, d);
162 | } else {
163 | el.scrollTop = d;
164 | }
165 | window.requestAnimationFrame(() => scroll(d, end, step));
166 | }
167 | scroll(from, to, step);
168 | }
--------------------------------------------------------------------------------
/src/utils/calcTextareaHeight.js:
--------------------------------------------------------------------------------
1 | // Thanks to
2 | // https://github.com/andreypopp/react-textarea-autosize/
3 | // https://github.com/ElemeFE/element/blob/master/packages/input/src/calcTextareaHeight.js
4 |
5 | let hiddenTextarea;
6 |
7 | const HIDDEN_STYLE = `
8 | height:0 !important;
9 | min-height:0 !important;
10 | max-height:none !important;
11 | visibility:hidden !important;
12 | overflow:hidden !important;
13 | position:absolute !important;
14 | z-index:-1000 !important;
15 | top:0 !important;
16 | right:0 !important
17 | `;
18 |
19 | const CONTEXT_STYLE = [
20 | 'letter-spacing',
21 | 'line-height',
22 | 'padding-top',
23 | 'padding-bottom',
24 | 'font-family',
25 | 'font-weight',
26 | 'font-size',
27 | 'text-rendering',
28 | 'text-transform',
29 | 'width',
30 | 'text-indent',
31 | 'padding-left',
32 | 'padding-right',
33 | 'border-width',
34 | 'box-sizing'
35 | ];
36 |
37 | function calculateNodeStyling(node) {
38 | const style = window.getComputedStyle(node);
39 |
40 | const boxSizing = style.getPropertyValue('box-sizing');
41 |
42 | const paddingSize = (
43 | parseFloat(style.getPropertyValue('padding-bottom')) +
44 | parseFloat(style.getPropertyValue('padding-top'))
45 | );
46 |
47 | const borderSize = (
48 | parseFloat(style.getPropertyValue('border-bottom-width')) +
49 | parseFloat(style.getPropertyValue('border-top-width'))
50 | );
51 |
52 | const contextStyle = CONTEXT_STYLE
53 | .map(name => `${name}:${style.getPropertyValue(name)}`)
54 | .join(';');
55 |
56 | return {contextStyle, paddingSize, borderSize, boxSizing};
57 | }
58 |
59 | export default function calcTextareaHeight(targetNode, minRows = null, maxRows = null) {
60 | if (!hiddenTextarea) {
61 | hiddenTextarea = document.createElement('textarea');
62 | document.body.appendChild(hiddenTextarea);
63 | }
64 |
65 | let {
66 | paddingSize,
67 | borderSize,
68 | boxSizing,
69 | contextStyle
70 | } = calculateNodeStyling(targetNode);
71 |
72 | hiddenTextarea.setAttribute('style', `${contextStyle};${HIDDEN_STYLE}`);
73 | hiddenTextarea.value = targetNode.value || targetNode.placeholder || '';
74 |
75 | let height = hiddenTextarea.scrollHeight;
76 | let minHeight = -Infinity;
77 | let maxHeight = Infinity;
78 |
79 | if (boxSizing === 'border-box') {
80 | height = height + borderSize;
81 | } else if (boxSizing === 'content-box') {
82 | height = height - paddingSize;
83 | }
84 |
85 | hiddenTextarea.value = '';
86 | let singleRowHeight = hiddenTextarea.scrollHeight - paddingSize;
87 |
88 | if (minRows !== null) {
89 | minHeight = singleRowHeight * minRows;
90 | if (boxSizing === 'border-box') {
91 | minHeight = minHeight + paddingSize + borderSize;
92 | }
93 | height = Math.max(minHeight, height);
94 | }
95 | if (maxRows !== null) {
96 | maxHeight = singleRowHeight * maxRows;
97 | if (boxSizing === 'border-box') {
98 | maxHeight = maxHeight + paddingSize + borderSize;
99 | }
100 | height = Math.min(maxHeight, height);
101 | }
102 |
103 | return {
104 | height: `${height}px`,
105 | minHeight: `${minHeight}px`,
106 | maxHeight: `${maxHeight}px`
107 | };
108 | }
--------------------------------------------------------------------------------
/src/utils/csv.js:
--------------------------------------------------------------------------------
1 | // https://github.com/Terminux/react-csv-downloader/blob/master/src/lib/csv.js
2 |
3 | const newLine = '\r\n';
4 |
5 | export default function csv(columns, datas, separator = ',', noHeader = false) {
6 | let columnOrder;
7 | const content = [];
8 | const column = [];
9 |
10 | if (columns) {
11 | columnOrder = columns.map(v => {
12 | if (typeof v === 'string') {
13 | return v;
14 | }
15 | if (!noHeader) {
16 | column.push((typeof v.title !== 'undefined') ? v.title : v.key);
17 | }
18 | return v.key;
19 | });
20 | if (column.length > 0) {
21 | content.push(column.join(separator));
22 | }
23 | } else {
24 | columnOrder = [];
25 | datas.forEach(v => {
26 | if (!Array.isArray(v)) {
27 | columnOrder = columnOrder.concat(Object.keys(v));
28 | }
29 | });
30 | if (columnOrder.length > 0) {
31 | columnOrder = columnOrder.filter((value, index, self) => self.indexOf(value) === index);
32 |
33 | if (!noHeader) {
34 | content.push(columnOrder.join(separator));
35 | }
36 | }
37 | }
38 |
39 | if (Array.isArray(datas)) {
40 | datas.map(v => {
41 | if (Array.isArray(v)) {
42 | return v;
43 | }
44 | return columnOrder.map(k => {
45 | if (typeof v[k] !== 'undefined') {
46 | return v[k];
47 | }
48 | return '';
49 | });
50 | }).forEach(v => {
51 | content.push(v.join(separator));
52 | });
53 | }
54 | return content.join(newLine);
55 | }
--------------------------------------------------------------------------------
/src/utils/date.js:
--------------------------------------------------------------------------------
1 | /*eslint-disable*/
2 | // 把 YYYY-MM-DD 改成了 yyyy-MM-dd
3 | (function (main) {
4 | 'use strict';
5 |
6 | /**
7 | * Parse or format dates
8 | * @class fecha
9 | */
10 | var fecha = {};
11 | var token = /d{1,4}|M{1,4}|yy(?:yy)?|S{1,3}|Do|ZZ|([HhMsDm])\1?|[aA]|"[^"]*"|'[^']*'/g;
12 | var twoDigits = /\d\d?/;
13 | var threeDigits = /\d{3}/;
14 | var fourDigits = /\d{4}/;
15 | var word = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i;
16 | var noop = function () {
17 | };
18 |
19 | function shorten(arr, sLen) {
20 | var newArr = [];
21 | for (var i = 0, len = arr.length; i < len; i++) {
22 | newArr.push(arr[i].substr(0, sLen));
23 | }
24 | return newArr;
25 | }
26 |
27 | function monthUpdate(arrName) {
28 | return function (d, v, i18n) {
29 | var index = i18n[arrName].indexOf(v.charAt(0).toUpperCase() + v.substr(1).toLowerCase());
30 | if (~index) {
31 | d.month = index;
32 | }
33 | };
34 | }
35 |
36 | function pad(val, len) {
37 | val = String(val);
38 | len = len || 2;
39 | while (val.length < len) {
40 | val = '0' + val;
41 | }
42 | return val;
43 | }
44 |
45 | var dayNames = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
46 | var monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
47 | var monthNamesShort = shorten(monthNames, 3);
48 | var dayNamesShort = shorten(dayNames, 3);
49 | fecha.i18n = {
50 | dayNamesShort: dayNamesShort,
51 | dayNames: dayNames,
52 | monthNamesShort: monthNamesShort,
53 | monthNames: monthNames,
54 | amPm: ['am', 'pm'],
55 | DoFn: function DoFn(D) {
56 | return D + ['th', 'st', 'nd', 'rd'][D % 10 > 3 ? 0 : (D - D % 10 !== 10) * D % 10];
57 | }
58 | };
59 |
60 | var formatFlags = {
61 | D: function (dateObj) {
62 | return dateObj.getDay();
63 | },
64 | DD: function (dateObj) {
65 | return pad(dateObj.getDay());
66 | },
67 | Do: function (dateObj, i18n) {
68 | return i18n.DoFn(dateObj.getDate());
69 | },
70 | d: function (dateObj) {
71 | return dateObj.getDate();
72 | },
73 | dd: function (dateObj) {
74 | return pad(dateObj.getDate());
75 | },
76 | ddd: function (dateObj, i18n) {
77 | return i18n.dayNamesShort[dateObj.getDay()];
78 | },
79 | dddd: function (dateObj, i18n) {
80 | return i18n.dayNames[dateObj.getDay()];
81 | },
82 | M: function (dateObj) {
83 | return dateObj.getMonth() + 1;
84 | },
85 | MM: function (dateObj) {
86 | return pad(dateObj.getMonth() + 1);
87 | },
88 | MMM: function (dateObj, i18n) {
89 | return i18n.monthNamesShort[dateObj.getMonth()];
90 | },
91 | MMMM: function (dateObj, i18n) {
92 | return i18n.monthNames[dateObj.getMonth()];
93 | },
94 | yy: function (dateObj) {
95 | return String(dateObj.getFullYear()).substr(2);
96 | },
97 | yyyy: function (dateObj) {
98 | return dateObj.getFullYear();
99 | },
100 | h: function (dateObj) {
101 | return dateObj.getHours() % 12 || 12;
102 | },
103 | hh: function (dateObj) {
104 | return pad(dateObj.getHours() % 12 || 12);
105 | },
106 | H: function (dateObj) {
107 | return dateObj.getHours();
108 | },
109 | HH: function (dateObj) {
110 | return pad(dateObj.getHours());
111 | },
112 | m: function (dateObj) {
113 | return dateObj.getMinutes();
114 | },
115 | mm: function (dateObj) {
116 | return pad(dateObj.getMinutes());
117 | },
118 | s: function (dateObj) {
119 | return dateObj.getSeconds();
120 | },
121 | ss: function (dateObj) {
122 | return pad(dateObj.getSeconds());
123 | },
124 | S: function (dateObj) {
125 | return Math.round(dateObj.getMilliseconds() / 100);
126 | },
127 | SS: function (dateObj) {
128 | return pad(Math.round(dateObj.getMilliseconds() / 10), 2);
129 | },
130 | SSS: function (dateObj) {
131 | return pad(dateObj.getMilliseconds(), 3);
132 | },
133 | a: function (dateObj, i18n) {
134 | return dateObj.getHours() < 12 ? i18n.amPm[0] : i18n.amPm[1];
135 | },
136 | A: function (dateObj, i18n) {
137 | return dateObj.getHours() < 12 ? i18n.amPm[0].toUpperCase() : i18n.amPm[1].toUpperCase();
138 | },
139 | ZZ: function (dateObj) {
140 | var o = dateObj.getTimezoneOffset();
141 | return (o > 0 ? '-' : '+') + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4);
142 | }
143 | };
144 |
145 | var parseFlags = {
146 | d: [twoDigits, function (d, v) {
147 | d.day = v;
148 | }],
149 | M: [twoDigits, function (d, v) {
150 | d.month = v - 1;
151 | }],
152 | yy: [twoDigits, function (d, v) {
153 | var da = new Date(), cent = +('' + da.getFullYear()).substr(0, 2);
154 | d.year = '' + (v > 68 ? cent - 1 : cent) + v;
155 | }],
156 | h: [twoDigits, function (d, v) {
157 | d.hour = v;
158 | }],
159 | m: [twoDigits, function (d, v) {
160 | d.minute = v;
161 | }],
162 | s: [twoDigits, function (d, v) {
163 | d.second = v;
164 | }],
165 | yyyy: [fourDigits, function (d, v) {
166 | d.year = v;
167 | }],
168 | S: [/\d/, function (d, v) {
169 | d.millisecond = v * 100;
170 | }],
171 | SS: [/\d{2}/, function (d, v) {
172 | d.millisecond = v * 10;
173 | }],
174 | SSS: [threeDigits, function (d, v) {
175 | d.millisecond = v;
176 | }],
177 | D: [twoDigits, noop],
178 | ddd: [word, noop],
179 | MMM: [word, monthUpdate('monthNamesShort')],
180 | MMMM: [word, monthUpdate('monthNames')],
181 | a: [word, function (d, v, i18n) {
182 | var val = v.toLowerCase();
183 | if (val === i18n.amPm[0]) {
184 | d.isPm = false;
185 | } else if (val === i18n.amPm[1]) {
186 | d.isPm = true;
187 | }
188 | }],
189 | ZZ: [/[\+\-]\d\d:?\d\d/, function (d, v) {
190 | var parts = (v + '').match(/([\+\-]|\d\d)/gi), minutes;
191 |
192 | if (parts) {
193 | minutes = +(parts[1] * 60) + parseInt(parts[2], 10);
194 | d.timezoneOffset = parts[0] === '+' ? minutes : -minutes;
195 | }
196 | }]
197 | };
198 | parseFlags.DD = parseFlags.DD;
199 | parseFlags.dddd = parseFlags.ddd;
200 | parseFlags.Do = parseFlags.dd = parseFlags.d;
201 | parseFlags.mm = parseFlags.m;
202 | parseFlags.hh = parseFlags.H = parseFlags.HH = parseFlags.h;
203 | parseFlags.MM = parseFlags.M;
204 | parseFlags.ss = parseFlags.s;
205 | parseFlags.A = parseFlags.a;
206 |
207 |
208 | // Some common format strings
209 | fecha.masks = {
210 | 'default': 'ddd MMM dd yyyy HH:mm:ss',
211 | shortDate: 'M/D/yy',
212 | mediumDate: 'MMM d, yyyy',
213 | longDate: 'MMMM d, yyyy',
214 | fullDate: 'dddd, MMMM d, yyyy',
215 | shortTime: 'HH:mm',
216 | mediumTime: 'HH:mm:ss',
217 | longTime: 'HH:mm:ss.SSS'
218 | };
219 |
220 | /***
221 | * Format a date
222 | * @method format
223 | * @param {Date|number} dateObj
224 | * @param {string} mask Format of the date, i.e. 'mm-dd-yy' or 'shortDate'
225 | */
226 | fecha.format = function (dateObj, mask, i18nSettings) {
227 | var i18n = i18nSettings || fecha.i18n;
228 |
229 | if (typeof dateObj === 'number') {
230 | dateObj = new Date(dateObj);
231 | }
232 |
233 | if (Object.prototype.toString.call(dateObj) !== '[object Date]' || isNaN(dateObj.getTime())) {
234 | throw new Error('Invalid Date in fecha.format');
235 | }
236 |
237 | mask = fecha.masks[mask] || mask || fecha.masks['default'];
238 |
239 | return mask.replace(token, function ($0) {
240 | return $0 in formatFlags ? formatFlags[$0](dateObj, i18n) : $0.slice(1, $0.length - 1);
241 | });
242 | };
243 |
244 | /**
245 | * Parse a date string into an object, changes - into /
246 | * @method parse
247 | * @param {string} dateStr Date string
248 | * @param {string} format Date parse format
249 | * @returns {Date|boolean}
250 | */
251 | fecha.parse = function (dateStr, format, i18nSettings) {
252 | var i18n = i18nSettings || fecha.i18n;
253 |
254 | if (typeof format !== 'string') {
255 | throw new Error('Invalid format in fecha.parse');
256 | }
257 |
258 | format = fecha.masks[format] || format;
259 |
260 | // Avoid regular expression denial of service, fail early for really long strings
261 | // https://www.owasp.org/index.php/Regular_expression_Denial_of_Service_-_ReDoS
262 | if (dateStr.length > 1000) {
263 | return false;
264 | }
265 |
266 | var isValid = true;
267 | var dateInfo = {};
268 | format.replace(token, function ($0) {
269 | if (parseFlags[$0]) {
270 | var info = parseFlags[$0];
271 | var index = dateStr.search(info[0]);
272 | if (!~index) {
273 | isValid = false;
274 | } else {
275 | dateStr.replace(info[0], function (result) {
276 | info[1](dateInfo, result, i18n);
277 | dateStr = dateStr.substr(index + result.length);
278 | return result;
279 | });
280 | }
281 | }
282 |
283 | return parseFlags[$0] ? '' : $0.slice(1, $0.length - 1);
284 | });
285 |
286 | if (!isValid) {
287 | return false;
288 | }
289 |
290 | var today = new Date();
291 | if (dateInfo.isPm === true && dateInfo.hour != null && +dateInfo.hour !== 12) {
292 | dateInfo.hour = +dateInfo.hour + 12;
293 | } else if (dateInfo.isPm === false && +dateInfo.hour === 12) {
294 | dateInfo.hour = 0;
295 | }
296 |
297 | var date;
298 | if (dateInfo.timezoneOffset != null) {
299 | dateInfo.minute = +(dateInfo.minute || 0) - +dateInfo.timezoneOffset;
300 | date = new Date(Date.UTC(dateInfo.year || today.getFullYear(), dateInfo.month || 0, dateInfo.day || 1,
301 | dateInfo.hour || 0, dateInfo.minute || 0, dateInfo.second || 0, dateInfo.millisecond || 0));
302 | } else {
303 | date = new Date(dateInfo.year || today.getFullYear(), dateInfo.month || 0, dateInfo.day || 1,
304 | dateInfo.hour || 0, dateInfo.minute || 0, dateInfo.second || 0, dateInfo.millisecond || 0);
305 | }
306 | return date;
307 | };
308 |
309 | /* istanbul ignore next */
310 | if (typeof module !== 'undefined' && module.exports) {
311 | module.exports = fecha;
312 | } else if (typeof define === 'function' && define.amd) {
313 | define(function () {
314 | return fecha;
315 | });
316 | } else {
317 | main.fecha = fecha;
318 | }
319 | })(this);
320 |
--------------------------------------------------------------------------------