├── .gitignore ├── images └── logo.png ├── src ├── images │ ├── mode.png │ ├── ok.png │ ├── close.png │ ├── close2.png │ ├── return.png │ ├── add-left.png │ ├── add-right.png │ ├── icon-br.png │ ├── mode │ │ ├── br.png │ │ ├── link.png │ │ ├── list.png │ │ ├── text.png │ │ └── image.png │ ├── text │ │ ├── bold.png │ │ ├── size.png │ │ ├── color.png │ │ ├── italic.png │ │ ├── colorBlue.png │ │ ├── colorGrey.png │ │ ├── colorPink.png │ │ ├── colorRed.png │ │ ├── underline.png │ │ ├── colorBlack.png │ │ ├── colorGreen.png │ │ ├── colorYellow.png │ │ ├── sizeBigger.png │ │ └── sizeSmaller.png │ ├── image │ │ ├── biger.png │ │ ├── setting.png │ │ ├── smaller.png │ │ ├── upload.png │ │ ├── top-align.png │ │ ├── bottom-align.png │ │ ├── middle-align.png │ │ └── vertical-align.png │ ├── list │ │ ├── newLi.png │ │ ├── add-top.png │ │ ├── setting.png │ │ └── add-bottom.png │ └── link │ │ ├── link-left.png │ │ ├── setting.png │ │ ├── cancel-link.png │ │ └── link-right.png ├── medit.css └── medit.js ├── test ├── contentEditableFocus.html └── ajaxImageUpload.html ├── webpack.config.dev.js ├── webpack.config.min.js ├── package.json ├── demo └── index.html └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | demo -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/images/logo.png -------------------------------------------------------------------------------- /src/images/mode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/mode.png -------------------------------------------------------------------------------- /src/images/ok.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/ok.png -------------------------------------------------------------------------------- /src/images/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/close.png -------------------------------------------------------------------------------- /src/images/close2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/close2.png -------------------------------------------------------------------------------- /src/images/return.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/return.png -------------------------------------------------------------------------------- /src/images/add-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/add-left.png -------------------------------------------------------------------------------- /src/images/add-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/add-right.png -------------------------------------------------------------------------------- /src/images/icon-br.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/icon-br.png -------------------------------------------------------------------------------- /src/images/mode/br.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/mode/br.png -------------------------------------------------------------------------------- /src/images/mode/link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/mode/link.png -------------------------------------------------------------------------------- /src/images/mode/list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/mode/list.png -------------------------------------------------------------------------------- /src/images/mode/text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/mode/text.png -------------------------------------------------------------------------------- /src/images/text/bold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/text/bold.png -------------------------------------------------------------------------------- /src/images/text/size.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/text/size.png -------------------------------------------------------------------------------- /src/images/image/biger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/image/biger.png -------------------------------------------------------------------------------- /src/images/list/newLi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/list/newLi.png -------------------------------------------------------------------------------- /src/images/mode/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/mode/image.png -------------------------------------------------------------------------------- /src/images/text/color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/text/color.png -------------------------------------------------------------------------------- /src/images/text/italic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/text/italic.png -------------------------------------------------------------------------------- /src/images/image/setting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/image/setting.png -------------------------------------------------------------------------------- /src/images/image/smaller.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/image/smaller.png -------------------------------------------------------------------------------- /src/images/image/upload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/image/upload.png -------------------------------------------------------------------------------- /src/images/link/link-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/link/link-left.png -------------------------------------------------------------------------------- /src/images/link/setting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/link/setting.png -------------------------------------------------------------------------------- /src/images/list/add-top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/list/add-top.png -------------------------------------------------------------------------------- /src/images/list/setting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/list/setting.png -------------------------------------------------------------------------------- /src/images/text/colorBlue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/text/colorBlue.png -------------------------------------------------------------------------------- /src/images/text/colorGrey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/text/colorGrey.png -------------------------------------------------------------------------------- /src/images/text/colorPink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/text/colorPink.png -------------------------------------------------------------------------------- /src/images/text/colorRed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/text/colorRed.png -------------------------------------------------------------------------------- /src/images/text/underline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/text/underline.png -------------------------------------------------------------------------------- /src/images/image/top-align.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/image/top-align.png -------------------------------------------------------------------------------- /src/images/link/cancel-link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/link/cancel-link.png -------------------------------------------------------------------------------- /src/images/link/link-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/link/link-right.png -------------------------------------------------------------------------------- /src/images/list/add-bottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/list/add-bottom.png -------------------------------------------------------------------------------- /src/images/text/colorBlack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/text/colorBlack.png -------------------------------------------------------------------------------- /src/images/text/colorGreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/text/colorGreen.png -------------------------------------------------------------------------------- /src/images/text/colorYellow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/text/colorYellow.png -------------------------------------------------------------------------------- /src/images/text/sizeBigger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/text/sizeBigger.png -------------------------------------------------------------------------------- /src/images/text/sizeSmaller.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/text/sizeSmaller.png -------------------------------------------------------------------------------- /src/images/image/bottom-align.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/image/bottom-align.png -------------------------------------------------------------------------------- /src/images/image/middle-align.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/image/middle-align.png -------------------------------------------------------------------------------- /src/images/image/vertical-align.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/echosoar/medit/HEAD/src/images/image/vertical-align.png -------------------------------------------------------------------------------- /test/contentEditableFocus.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | Focus 13 | -------------------------------------------------------------------------------- /test/ajaxImageUpload.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | -------------------------------------------------------------------------------- /webpack.config.dev.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var webpack = require('webpack') 3 | var autoprefixer = require('autoprefixer') 4 | var precss = require('precss') 5 | var CopyWebpackPlugin = require('copy-webpack-plugin'); 6 | 7 | module.exports = { 8 | entry: { 9 | "medit.min": './src/medit.js' 10 | }, 11 | output: { 12 | path: path.join(__dirname, 'build'), 13 | filename: '[name].js' 14 | }, 15 | plugins: [], 16 | resolve: { 17 | alias: { 18 | medit: path.resolve(__dirname, 'src') 19 | }, 20 | extensions: ['', '.js', '.jsx'] 21 | }, 22 | module: { 23 | loaders: [ 24 | { 25 | test: /\.jsx?$/, 26 | loader: 'babel', 27 | include: [ 28 | path.resolve(__dirname, 'src') 29 | ] 30 | }, 31 | { test : /\.css$/, loaders: ['style-loader', 'css-loader', 'resolve-url-loader']}, 32 | { test: /\.scss$/, loaders: ['style-loader', 'css-loader?modules&localIdentName=[local]-[hash:base64:5]', 'postcss-loader', 'sass-loader'] }, 33 | { test: /\.(ttf|eot|woff|woff2|otf|svg)/, loader: 'file-loader?name=./font/[name].[ext]' }, 34 | { test: /\.json$/, loader: 'file-loader?name=./json/[name].json' }, 35 | { test: /\.(png|jpg|jpeg|gif)$/, loader: 'url-loader?limit=100000&name=./images/[name].[ext]' } 36 | ] 37 | }, 38 | postcss: function () { 39 | return [autoprefixer({ browsers: ['> 1%', 'IE 9'] }), precss] 40 | }, 41 | plugins: [ 42 | new CopyWebpackPlugin([{context:'./src/images/', from:'**/*', to:'images'}]) 43 | ] 44 | } -------------------------------------------------------------------------------- /webpack.config.min.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var webpack = require('webpack') 3 | var autoprefixer = require('autoprefixer') 4 | var precss = require('precss') 5 | var CopyWebpackPlugin = require('copy-webpack-plugin'); 6 | 7 | module.exports = { 8 | entry: { 9 | "medit.min": './src/medit.js' 10 | }, 11 | output: { 12 | path: path.join(__dirname, 'build'), 13 | filename: '[name].js' 14 | }, 15 | plugins: [], 16 | resolve: { 17 | alias: { 18 | medit: path.resolve(__dirname, 'src') 19 | }, 20 | extensions: ['', '.js', '.jsx'] 21 | }, 22 | module: { 23 | loaders: [ 24 | { 25 | test: /\.jsx?$/, 26 | loader: 'babel', 27 | include: [ 28 | path.resolve(__dirname, 'src') 29 | ] 30 | }, 31 | { test : /\.css$/, loaders: ['style-loader', 'css-loader', 'resolve-url-loader']}, 32 | { test: /\.scss$/, loaders: ['style-loader', 'css-loader?modules&localIdentName=[local]-[hash:base64:5]', 'postcss-loader', 'sass-loader'] }, 33 | { test: /\.(ttf|eot|woff|woff2|otf|svg)/, loader: 'file-loader?name=./font/[name].[ext]' }, 34 | { test: /\.json$/, loader: 'file-loader?name=./json/[name].json' }, 35 | { test: /\.(png|jpg|jpeg|gif)$/, loader: 'url-loader?limit=100000&name=./images/[name].[ext]' } 36 | ] 37 | }, 38 | postcss: function () { 39 | return [autoprefixer({ browsers: ['> 1%', 'IE 9'] }), precss] 40 | }, 41 | plugins: [ 42 | new webpack.optimize.UglifyJsPlugin(), 43 | new CopyWebpackPlugin([{context:'./src/images/', from:'**/*', to:'images'}]) 44 | ] 45 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "medit", 3 | "version": "1.0.0", 4 | "description": "A creative WYSIWYG rich text editor for mobile device.", 5 | "main": "medit.js", 6 | "scripts": { 7 | "dev": "webpack --config webpack.config.dev.js --watch", 8 | "build": "webpack --config webpack.config.min.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/echosoar/medit.git" 13 | }, 14 | "keywords": [ 15 | "WYSIWYG", 16 | "mobile", 17 | "rich", 18 | "text", 19 | "editor" 20 | ], 21 | "author": "echosoar", 22 | "license": "ISC", 23 | "bugs": { 24 | "url": "https://github.com/echosoar/medit/issues" 25 | }, 26 | "homepage": "https://medit.js.org", 27 | "devDependencies": { 28 | "babel-plugin-transform-es2015-classes": "^6.18.0", 29 | "copy-webpack-plugin": "^4.0.1", 30 | "eslint": "~3.4.0", 31 | "eslint-plugin-babel": "^3.2.0", 32 | "eslint-plugin-react": "~4.2.3", 33 | "optimist": "^0.6.1", 34 | "resolve-url-loader": "^1.6.1", 35 | "url-loader": "~0.5.7", 36 | "webpack": "~1.12.14" 37 | }, 38 | "dependencies": { 39 | "autoprefixer": "~6.3.4", 40 | "babel-core": "^6.14.0", 41 | "babel-eslint": "^6.1.2", 42 | "babel-loader": "~6.2.4", 43 | "babel-plugin-transform-object-rest-spread": "~6.6.5", 44 | "babel-polyfill": "^6.3.14", 45 | "babel-preset-es2015": "^6.14.0", 46 | "css-loader": "~0.23.1", 47 | "file-loader": "~0.8.5", 48 | "less": "~2.6.1", 49 | "less-loader": "~2.2.2", 50 | "loader-utils": "~0.2.12", 51 | "md5": "~2.0.0", 52 | "node-sass": "^3.6.0", 53 | "postcss-loader": "~0.8.2", 54 | "precss": "~1.4.0", 55 | "sass-loader": "^3.2.0", 56 | "style-loader": "~0.13.0" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 35 |
36 |
37 |
预览 Preview
38 |
取消 Cancel
39 |
40 |
41 | 42 | 43 | 72 | 100 | 新窗口打开 Open in a new window -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![logo](./images/logo.png) 2 | # Medit v2.0.0 3 | 4 | *** 5 | 6 | medit新版本正在daily/3.0.0分支上进行开发,如有问题或者3.0版本开发建议可以提交issue 7 | 8 | *** 9 | 10 | A creative WYSIWYG rich text editor for mobile device by javascript. 11 | 12 | 一个创新型的移动端所见即所得富文本编辑器。 13 | 14 | Website Address : [https://medit.js.org](https://medit.js.org) 15 | 16 | Demo: [Medit Demo](https://medit.js.org/demo.html) 17 | 18 | ##### 为了更专注做一个更具价值和体验的移动端富文本编辑器,所以Medit目前不支持Pc端使用,仅支持移动端。 19 | 20 | *** 21 | ### Medit2.0.0 较上一版本更新内容 22 | 23 | 1. 更易用的内容选择方式,目前已支持选取内容块后手势左右滑动选择、通过手机原生自带长按选择进行编辑操作。 24 | 2. 优化编辑器样式,把原有的图标、弹层和编辑器内部标识都进行了优化。 25 | 3. 开放功能扩展接口Medit.extend,可以通过这个接口来扩展更加丰富的内容。 26 | 4. 开放内置功能配置接口Medit.nativeSetting,开放内置弹层调用接口Medit.settingPage。 27 | 5. 工具条目前不在限制于页面顶部,用户可以对工具条进行自定义配置。 28 | 29 | 30 | *** 31 | ### 如何使用: 32 | 33 | + 第一步,引入medit.js文件,如果不下载使用icon包的话可能会导致部分功能性icon无法显示,icon存放于 __github/medit/build/images__ 下 34 | ```html 35 | 36 | ``` 37 | > 在第一步和第二步之间可以选择性的引入medit插件,也可以自己来书写medit插件,medit提供了两个方法,一个是 __medit.extend__ 方法来配置扩展插件,另外还有一个 __medit.nativeSetting__ 方法来配置内部功能,详情请看下面的 medit类方法。 38 | 39 | + 第二步,创建medit实例 40 | ```html 41 | 54 | ``` 55 | + 经过上面两步之后一个medit富文本编辑器就可以使用了。 56 | 57 | *** 58 | ### Medit实例方法 59 | 60 | 通过上面创建好的medit实例meditObj可以调用medit的方法来实现你想要的功能。 61 | 62 | + meditObj.getContent() 63 | 获取medit编辑器中所编辑的内容结果。 64 | 65 | + meditObj.clear() 66 | 清除medit编辑器的内容和自动保存在客户端浏览器中的内容。 67 | 68 | + meditObj.autoSave(name, callBack(content, time)) 69 | 配置medit自动保存,需要传入两个参数: 70 | + name:为了保证在同一页面引入两个编辑器后自动保存的效果,所以需要手动传入一个自动保存的字段名称,需要在页面中保持唯一性。 71 | + callBack(content, time):这是自动保存的回调函数,每次medit自动保存后都会调用这个回调函数,并传入当前保存的编辑器内容content和当前时间戳time。 72 | 73 | + meditObj.image(option) || meditObj.imageUpload(option) 74 | medit图片上传配置,option是配置参数 75 | ```javascript 76 | { // 默认图片上传设置 77 | path:'https://sm.ms/api/upload', // 图片上传路径 78 | name:'smfile', // 图片上传文件参数 79 | size:0, // 大小限制,0为不限制大小 80 | timeout:0, // 上传超时时间,0为不限制 81 | ext:["jpg","jpeg","png","gif","bmp"], // 上传文件格式限制 82 | success:function(){}, // 上传成功回调 83 | error:function(){} // 上传失败回调 84 | } 85 | ``` 86 | 87 | *** 88 | 89 | ### medit类方法 90 | 91 | 目前有三个medit类方法,所谓类方法就是直接通过medit类进行调用而不是通过medit实例进行调用。 92 | 93 | + medit.extend(config) 94 | 功能扩展方法,可以通过这个方法实现medit插件和功能扩展。 95 | config是一个对象,其中必须包括 __图标: icon__ 、 __其它类型模块转换为此类型模块时动作: doWhat__ 、 __模块得到焦点时动作: focus__ 、__模块失去焦点时动作: blur__ 和 __模块名称: name__ 这五个属性。其中icon可以是远程url,也可以是dataURL;name必须保持唯一,不能与内置功能名称产生冲突。 96 | 97 | 下面是一个功能模块的完整配置属性: 98 | ```javscript 99 | /* 100 | icon: [String] 类型选择icon url 101 | name: [String] 类型名称 102 | isMerge: [Bollean] 是否开启相同内容自动合并 103 | notDisplay: [Bollean] 在选择模式的时候不显示, 104 | emptyNotDelete: [Bollean] 如果当前块只存在一个子节点并且这个子节点要删除的时候是否开启递归删除 105 | 106 | doWhat: [Function] 转换到此类型时会自动做哪些转换 107 | focus:[Function] 点击或此模块获取焦点时自动触发的函数 108 | blur:[Function] 此模块失去焦点时自动触发的函数 109 | empty: [Function] 什么时候当前模块为空 110 | selecting:[Function] 选择当前模块并且手指在屏幕上移动时触发的操作 111 | selected:[Function] 手指移动结束执行的操作 112 | setting: [Array(Object)] 当前模块可以进行哪些操作 113 | -- name: [String] 操作名称 114 | -- icon: [String] 操作按钮icon url 115 | -- doWhat: [Function/Array] 点击此操作按钮执行什么,或者是存在更深层次操作 116 | */ 117 | ``` 118 | + medit.nativeSetting(callBack(config, name)) 119 | 内部功能配置方法,会循环调用callBack,然后把内置功能的配置和名称传入,返回值应该是一个修改后的config,然后medit就会应用这个config,如果没有返回值那么medit也就不会做任何改动。 120 | 121 | + medit.settingPage(title, contentHTML, callBack) 122 | 打开medit内置弹层,title是配置弹层顶部Title,contentHTML可以传入一段html文本作为弹层的内容,callBack是在弹层的ok按钮点击之后触发。 123 | 124 | 下面是一段应用medit类方法的实例: 125 | ```html 126 | 153 | ``` 154 | *** 155 | 156 | © MIT License 157 | -------------------------------------------------------------------------------- /src/medit.css: -------------------------------------------------------------------------------- 1 | #medit { 2 | font-family:微软雅黑,黑体,Helvetica,华文黑体; 3 | font-size: 14px; 4 | word-break:break-all; 5 | } 6 | #medit * { 7 | outline:none; 8 | } 9 | #medit span { 10 | position: relative; 11 | outline:none; 12 | } 13 | .medit-editing{ 14 | outline:none; 15 | padding: 0 20px; 16 | background: #eeddff; 17 | 18 | } 19 | 20 | #medit-tool { 21 | position:fixed; 22 | display: none; 23 | z-index:100; 24 | width:100%; 25 | left: 0; 26 | top: 0; 27 | height:40px; 28 | white-space: nowrap; 29 | overflow:auto; 30 | background:#428bca; 31 | text-align: left; 32 | font-family:微软雅黑,黑体,Helvetica,华文黑体; 33 | } 34 | 35 | #medit-tool.medit-tool-inner { 36 | position:relative; 37 | } 38 | 39 | #medit [data-meditMode=br] { 40 | height: 14px; 41 | width: 20px; 42 | margin:10px 0; 43 | border-radius: 3px; 44 | background:url("./images/icon-br.png") no-repeat center; 45 | background-size: 14px; 46 | } 47 | 48 | .medit-tool-button { 49 | width:40px; 50 | display:inline-block; 51 | height:40px; 52 | line-height: 40px; 53 | text-align:center; 54 | font-size:13px; 55 | cursor: pointer; 56 | color:#fff; 57 | font-weight:bold; 58 | } 59 | 60 | .medit-tool-addLeft { 61 | background:url("./images/add-left.png") no-repeat center center; 62 | background-size: 24px; 63 | } 64 | .medit-tool-addRight { 65 | background:url("./images/add-right.png") no-repeat center center; 66 | background-size: 24px; 67 | } 68 | .medit-tool-delete { 69 | background:url("./images/close.png") no-repeat center center; 70 | background-size: 24px; 71 | } 72 | 73 | .medit-tool-mode { 74 | background:url("./images/mode.png") no-repeat center center; 75 | background-size: 24px; 76 | } 77 | .medit-tool-ok { 78 | background:url("./images/ok.png") no-repeat center center; 79 | background-size: 24px; 80 | } 81 | 82 | .medit-tool-return { 83 | background:url("./images/return.png") no-repeat center center; 84 | background-size: 24px; 85 | } 86 | 87 | .medit-tool-button{ 88 | border-right: 1px solid #357ebd; 89 | background-color: #428bca; 90 | } 91 | 92 | /* text */ 93 | .medit-text-select { 94 | position:relative; 95 | background:#9cf; 96 | } 97 | 98 | /* Link */ 99 | .medit-link { 100 | background: #def; 101 | border-bottom:1px dashed #709; 102 | } 103 | #medit a { 104 | position: relative; 105 | outline:none; 106 | padding: 0 20px; 107 | 108 | } 109 | #medit a:before { 110 | content: " "; 111 | position: absolute; 112 | left: 0; 113 | top: 0; 114 | width: 20px; 115 | height:20px; 116 | background:url("./images/link/link-left.png") no-repeat center center; 117 | background-size: 20px; 118 | } 119 | #medit a:after { 120 | content: " "; 121 | position: absolute; 122 | bottom: 0; 123 | width: 20px; 124 | height:12px; 125 | background:url("./images/link/link-right.png") no-repeat center center; 126 | background-size: 20px; 127 | } 128 | 129 | /* setting page */ 130 | 131 | #medit-settingPage{ 132 | display: none; 133 | position:fixed; 134 | z-index:101; 135 | top:0; 136 | left:0; 137 | width:100%; 138 | height:100%; 139 | background:rgba(0,0,0,.6); 140 | font-size:14px; 141 | font-family:微软雅黑,黑体,Helvetica,华文黑体; 142 | 143 | } 144 | 145 | #medit-settingPage input { 146 | font-family:微软雅黑,黑体,Helvetica,华文黑体; 147 | } 148 | 149 | #medit-settingPage-main{ 150 | position:absolute; 151 | top:50%; 152 | left:10%; 153 | transform:translateY(-50%); 154 | width:80%; 155 | background:#fff; 156 | border-radius:3px; 157 | border:1px solid #666; 158 | } 159 | #medit-settingPage-title { 160 | padding:0 10px; 161 | font-size:12px; 162 | color:#000; 163 | height:36px; 164 | line-height:36px; 165 | border-bottom:1px solid #ddd; 166 | } 167 | #medit-settingPage-content { 168 | border-radius: 3px; 169 | } 170 | 171 | #medit-settingPage-content .medit-list-mode-style-innerTitle{ 172 | padding: 0 5px; 173 | font-size:12px; 174 | margin:10px; 175 | border-left: 3px solid #f96; 176 | } 177 | 178 | #medit-settingPage-content input[type=text] { 179 | width:100%; 180 | box-sizing:border-box; 181 | padding:0 10px; 182 | height:40px; 183 | line-height:40px; 184 | margin-top:15px; 185 | border:1px solid #eee; 186 | border-left:0; 187 | border-right:0; 188 | outline:none; 189 | margin-bottom:15px; 190 | font-size:12px; 191 | } 192 | #medit-settingPage-content .medit-settingPage-image-dataInfo { 193 | 194 | } 195 | #medit-settingPage-content .medit-settingPage-image-dataInfo span { 196 | display:block; 197 | font-size:12px; 198 | padding:15px; 199 | } 200 | #medit-settingPage-content .medit-settingPage-image-dataInfo input[type=text] { 201 | display:block; 202 | margin:0; 203 | } 204 | 205 | #medit-settingPage-content label{ 206 | display:block; 207 | margin-left:10px; 208 | } 209 | 210 | #medit-settingPage-content input[type=radio], #medit-settingPage-content input[type=checkbox] { 211 | display:none; 212 | } 213 | #medit-settingPage-content input[type=radio] + span, #medit-settingPage-content input[type=checkbox] + span { 214 | position:relative; 215 | display:inline-block; 216 | height:24px; 217 | line-height:24px; 218 | padding-left:30px; 219 | font-size:12px; 220 | } 221 | #medit-settingPage-content input[type=radio] + span:before, #medit-settingPage-content input[type=checkbox] + span:before{ 222 | content:' '; 223 | position:absolute; 224 | left:0; 225 | top:4px; 226 | width:16px; 227 | height:16px; 228 | background:#fff; 229 | border-radius:100%; 230 | border:2px solid #9cf; 231 | } 232 | #medit-settingPage-content input[type=radio]:checked + span:before, #medit-settingPage-content input[type=checkbox]:checked + span:before { 233 | background:#39f url("./images/ok.png") no-repeat center center; 234 | background-size: 16px; 235 | border:2px solid #39f; 236 | } 237 | #medit-settingPage-button{ 238 | height:36px; 239 | margin-top:15px; 240 | } 241 | 242 | #medit-settingPage-button-ok { 243 | display: block; 244 | font-style:normal; 245 | height:36px; 246 | line-height:36px; 247 | text-align:center; 248 | color:#fff; 249 | overflow:hidden; 250 | background:#69f; 251 | 252 | } 253 | #medit-settingPage-button-cancel { 254 | background:#69f url("./images/close2.png") no-repeat center center; 255 | background-size: 24px; 256 | position:absolute; 257 | top:-15px; 258 | right:-15px; 259 | width:30px; 260 | height:30px; 261 | border-radius:50%; 262 | } 263 | .medit-image{ 264 | position:relative; 265 | } 266 | img:after { 267 | content:" "; 268 | position:absolute; 269 | width:200px; 270 | height:200px; 271 | right:0; 272 | bottom:0; 273 | color:#fff; 274 | background:#333; 275 | } 276 | 277 | 278 | #medit [data-meditMode=image] { 279 | background-color:#e5e5e5; 280 | } 281 | 282 | #medit-image-upload-select-btn { 283 | width:70%; 284 | overflow:hidden; 285 | white-space:nowrap; 286 | padding: 0 5%; 287 | margin:20px auto; 288 | height:32px; 289 | line-height:32px; 290 | color:#fff; 291 | background:#3c9; 292 | border-radius:3px; 293 | text-align:center; 294 | } 295 | 296 | #medit-image-upload-file { 297 | position:absolute; 298 | left:-500px; 299 | width:200px; 300 | 301 | } 302 | #medit-settingPage-content-img-uploading{ 303 | padding: 20px 10px; 304 | line-height:24px; 305 | } 306 | #medit-settingPage-content-img-uploading-progress { 307 | display:block; 308 | height:4px; 309 | background:#c33; 310 | border-radius:2px; 311 | margin:10px 0; 312 | } 313 | 314 | #medit ul, #medit ol { 315 | position:relative; 316 | padding: 5px 0; 317 | margin:5px 0; 318 | border-left:20px solid #eee; 319 | min-height:30px; 320 | } 321 | #medit ul:before, #medit ol:before { 322 | content: "Li st"; 323 | position:absolute; 324 | height:12px; 325 | line-height:12px; 326 | left:-17px; 327 | color:#333; 328 | width:15px; 329 | font-size: 10px; 330 | top:2px; 331 | } 332 | .medit-list { 333 | background:#ddd; 334 | } 335 | #medit li { 336 | position:relative; 337 | margin:0; 338 | padding:0; 339 | list-style:none; 340 | padding:5px; 341 | min-height:20px; 342 | margin:3px; 343 | border-left:20px solid #eee; 344 | } 345 | #medit li:before{ 346 | content: "li"; 347 | position:absolute; 348 | height:20px; 349 | line-height:20px; 350 | left:-20px; 351 | color:#333; 352 | text-align:center; 353 | width:20px; 354 | font-size: 10px; 355 | top:0px; 356 | } -------------------------------------------------------------------------------- /src/medit.js: -------------------------------------------------------------------------------- 1 | require("./medit.css"); 2 | 3 | var obj = window; 4 | 5 | var meditToolImage = obj.meditToolImage || "./";// 工具条图片位置 6 | 7 | var meditId = null; 8 | 9 | var container = []; 10 | 11 | var regNodeId = /medit\-(\d+)\-(\d+)$/; 12 | 13 | var regContent = /\s(class|id|contenteditable)(=".*?")?/g; // 获得内容时去除id,class和可编辑状态 14 | 15 | var regIsNotContentEmpty = /^<.*?>$/; // 获得内容时检测是否是纯文本 16 | 17 | var regNormalStyle = /(font\-style\s*:\s*normal\s*;)|(font\-weight\s*:\s*normal\s*;)|(color:\s*rgb\(0,\s*0,\s*0\);)|(\s*)/ig; // 正常的样式需要剔除 18 | 19 | var selectTextReg = /(.*?)<\/span>/i; 20 | 21 | var isToolMove = false; 22 | 23 | var toolBarCatch = null; 24 | 25 | var isContainMove = false; 26 | 27 | var mainTouchPoint = {}; 28 | 29 | var nowNode = null; // 当前选择的可编辑结点 30 | 31 | var nodeFocusTimeout = null; // nodeFocus延时 32 | 33 | var globalSelectionContent = null; // 缓存长按选择区域 34 | var globalSelectionHandle = null; 35 | 36 | var commonImageType = { 37 | "jpeg":"image/jpeg", 38 | "jpg":"image/jpeg", 39 | "gif":"image/gif", 40 | "png":"image/png", 41 | "bmp":"image/bmp" 42 | } 43 | 44 | var getNodeById = function(id){ 45 | return document.getElementById(id); 46 | } 47 | 48 | var fun_deep_clone = function (parent, child) { 49 | child = child || {}; 50 | for(var i in parent) { 51 | if(parent.hasOwnProperty(i)) { 52 | if(typeof parent[i] === "object") { 53 | child[i] = (Object.prototype.toString.call(parent[i]) === "[object Array]") ? [] : {}; 54 | fun_deep_clone(parent[i], child[i]); 55 | } else { 56 | child[i] = parent[i]; 57 | } 58 | } 59 | } 60 | return child; 61 | } 62 | 63 | var returnButtonHtml = function(from){ 64 | return ' '; 65 | } 66 | 67 | var toArray = function(obj){ 68 | return [].slice.call(obj); 69 | } 70 | 71 | var getXhr = function(){ 72 | if(window.XMLHttpRequest){ 73 | var xhr=new XMLHttpRequest(); 74 | }else{ 75 | try{ 76 | var xhr=new ActiveXObject("Msxml2.XMLHTTP"); 77 | }catch(e){ 78 | try{ 79 | var xhr=new ActiveXObject("Microsoft.XMLHTTP"); 80 | }catch(e){ 81 | throw new TypeError('Unsupport XMLHttpRequest'); 82 | } 83 | } 84 | } 85 | return xhr; 86 | } 87 | 88 | var mainButton = ["addLeft","delete","setting","mode","ok","addRight"]; 89 | 90 | var nowMode = "text"; 91 | /* 92 | mode 属性: 93 | 94 | icon: [String] 类型选择icon url 95 | 96 | isMerge: [Bollean] 是否开启相同内容自动合并 97 | notDisplay: [Bollean] 在选择模式的时候不显示, 98 | emptyNotDelete: [Bollean] 如果当前块只存在一个子节点并且这个子节点要删除的时候是否开启递归删除 99 | 100 | doWhat: [Function] 转换到此类型时会自动做哪些转换 101 | focus:[Function] 点击或此模块获取焦点时自动触发的函数 102 | blur:[Function] 此模块失去焦点时自动触发的函数 103 | empty: [Function] 什么时候当前模块为空 104 | selecting:[Function] 选择当前模块并且手指在屏幕上移动时触发的操作 105 | selected:[Function] 手指移动结束执行的操作 106 | setting: [Array(Object)] 当前模块可以进行哪些操作 107 | -- name: [String] 操作名称 108 | -- icon: [String] 操作按钮icon url 109 | -- doWhat: [Function/Array] 点击此操作按钮执行什么,或者是存在更深层次操作 110 | 111 | 112 | */ 113 | var nativeMode = ["text", "br", "link", "image", "list", "li"]; 114 | var mode = { 115 | "text": { 116 | icon: meditToolImage + 'images/mode/text.png', 117 | doWhat: function(node){ 118 | mode[node.getAttribute("data-meditmode")].blur(node); 119 | var temNode = document.createElement("span"); 120 | temNode.setAttribute("data-medit", "true"); 121 | temNode.setAttribute("data-meditmode", "text"); 122 | node.parentNode.insertBefore(temNode,node); 123 | node.parentNode.removeChild(node); 124 | mode["text"].focus(temNode); 125 | nodeFocus(temNode); 126 | nowNode = temNode; 127 | toolBarModeSetting("text",mode["text"].setting); 128 | container[meditId].updateId(); 129 | nowMode = "text"; 130 | }, 131 | isMerge: true, 132 | focus:function(node) { 133 | node.setAttribute("contentEditable","true"); 134 | node.setAttribute("class","medit-editing"); 135 | }, 136 | blur:function(node) { 137 | node.innerHTML = node.innerHTML.replace(selectTextReg,"$1"); 138 | node.removeAttribute("contentEditable"); 139 | node.removeAttribute("class"); 140 | }, 141 | empty:function(node) { 142 | return node.innerHTML == ""; 143 | }, 144 | setting:[ 145 | { 146 | name:"bold", 147 | icon: meditToolImage + "images/text/bold.png", 148 | doWhat:function(node){ 149 | var style = node.getAttribute("style"); 150 | var reg = /font\-weight\s*:\s*(.*?)\s*;/i; 151 | if(reg.test(style)){ 152 | var regRes = reg.exec(style); 153 | if(regRes[1] == "bold"){ 154 | node.style.fontWeight = "normal"; 155 | return; 156 | } 157 | } 158 | node.style.fontWeight = "bold"; 159 | } 160 | }, 161 | { 162 | name:"italic", 163 | icon: meditToolImage + "images/text/italic.png", 164 | doWhat:function(node){ 165 | var style = node.getAttribute("style"); 166 | var reg = /font\-style\s*:\s*(.*?)\s*;/i; 167 | if(reg.test(style)){ 168 | var regRes = reg.exec(style); 169 | if(regRes[1] == "italic"){ 170 | node.style.fontStyle = "normal"; 171 | return; 172 | } 173 | } 174 | node.style.fontStyle = "italic"; 175 | } 176 | }, 177 | { 178 | name:"underline", 179 | icon: meditToolImage + "images/text/underline.png", 180 | doWhat:function(node){ 181 | var style = node.getAttribute("style"); 182 | var reg = /text\-decoration\s*:\s*(.*?)\s*;/i; 183 | if(reg.test(style)){ 184 | var regRes = reg.exec(style); 185 | if(regRes[1] == "underline"){ 186 | node.style.textDecoration = "none"; 187 | return; 188 | } 189 | } 190 | node.style.textDecoration = "underline"; 191 | } 192 | }, 193 | { 194 | name:"size", 195 | icon: meditToolImage + "images/text/size.png", 196 | doWhat:[ 197 | { 198 | name: "fontSizeBig", 199 | icon: meditToolImage + "images/text/sizeBigger.png", 200 | doWhat: function(node) { 201 | var style = node.getAttribute("style"); 202 | var displaySize = getNodeById("medit-tool-button-text-setting-3-doWhat-1"); 203 | var reg = /font\-size\s*:\s*(\d*)\s*(?:.*?)\s*;/i; 204 | var size = 15; 205 | if(reg.test(style)){ 206 | var regRes = reg.exec(style); 207 | size = ++regRes[1]; 208 | } 209 | 210 | if(displaySize){ 211 | displaySize.innerHTML = size; 212 | } 213 | node.style.fontSize = size + "px"; 214 | } 215 | }, 216 | { 217 | name: "fontSizeValue", 218 | icon: "", 219 | defaultValue: "size" 220 | }, 221 | { 222 | name: "fontSizeSmall", 223 | icon: meditToolImage + "images/text/sizeSmaller.png", 224 | doWhat: function(node) { 225 | var style = node.getAttribute("style"); 226 | var displaySize = getNodeById("medit-tool-button-text-setting-3-doWhat-1"); 227 | var reg = /font\-size\s*:\s*(\d*)\s*(?:.*?)\s*;/i; 228 | var size = 13; 229 | if(reg.test(style)){ 230 | var regRes = reg.exec(style); 231 | size = --regRes[1]; 232 | if(size<12)size=12; 233 | } 234 | 235 | if(displaySize){ 236 | displaySize.innerHTML = size; 237 | } 238 | node.style.fontSize = size + "px"; 239 | } 240 | } 241 | ] 242 | }, 243 | { 244 | name: "color", 245 | icon: meditToolImage + "images/text/color.png", 246 | doWhat:[ 247 | { 248 | name: "black", 249 | icon: meditToolImage + "images/text/colorBlack.png", 250 | doWhat:function(node){ 251 | node.style.color = "#000000"; 252 | } 253 | }, 254 | { 255 | name: "red", 256 | icon: meditToolImage + "images/text/colorRed.png", 257 | doWhat:function(node){ 258 | node.style.color = "#ff0000"; 259 | } 260 | }, 261 | { 262 | name: "green", 263 | icon: meditToolImage + "images/text/colorGreen.png", 264 | doWhat:function(node){ 265 | node.style.color = "#00ff00"; 266 | } 267 | }, 268 | { 269 | name: "blue", 270 | icon: meditToolImage + "images/text/colorBlue.png", 271 | doWhat:function(node){ 272 | node.style.color = "#0000ff"; 273 | } 274 | }, 275 | { 276 | name: "yellow", 277 | icon: meditToolImage + "images/text/colorYellow.png", 278 | doWhat:function(node){ 279 | node.style.color = "#ffff00"; 280 | } 281 | }, 282 | { 283 | name: "pink", 284 | icon: meditToolImage + "images/text/colorPink.png", 285 | doWhat:function(node){ 286 | node.style.color = "#ff00ff"; 287 | } 288 | } 289 | ] 290 | } 291 | ], 292 | selecting : function(node, isAdd){ 293 | var selectReg = selectTextReg; 294 | if(selectReg.test(node.innerHTML)){ 295 | var regRes = node.innerHTML.split(selectReg); 296 | if(isAdd){ 297 | if(regRes[1].length>1){ 298 | if(/^ |^<(.*?)\s(.*?)>(.*?)<\/(.*?)>/.test(regRes[1])){ 299 | var innerTagReg = /^ |^<(.*?)\s(.*?)>(.*?)<\/(.*?)>/.exec(regRes[1]); 300 | var replaceHTML = innerTagReg[0]; 301 | regRes[0] += replaceHTML; 302 | regRes[1] = regRes[1].replace(/^ |^<(.*?)\s(.*?)>(.*?)<\/(.*?)>/, ""); 303 | }else{ 304 | regRes[0] = regRes[0] + regRes[1].slice(0, 1); 305 | regRes[1] = regRes[1].slice(1); 306 | } 307 | } 308 | }else{ 309 | if(regRes[1].length>1){ 310 | if(/<(.*?)\s(.*?)>(.*?)<\/(.*?)>$| $/.test(regRes[1])){ 311 | var innerTagReg = /<(.*?)\s(.*?)>(.*?)<\/(.*?)>$| $/.exec(regRes[1]); 312 | var replaceHTML = innerTagReg[0]; 313 | regRes[2] = replaceHTML + regRes[2]; 314 | regRes[1] = regRes[1].replace(/<(.*?)\s(.*?)>(.*?)<\/(.*?)>$| $/, ""); 315 | }else{ 316 | regRes[2] = regRes[1].slice(-1) + regRes[2]; 317 | regRes[1] = regRes[1].slice(0, -1); 318 | } 319 | } 320 | } 321 | 322 | node.innerHTML = regRes[0] + '' + regRes[1] + '' + regRes[2]; 323 | }else{ 324 | node.innerHTML = '' + node.innerHTML + ''; 325 | } 326 | }, 327 | selected: function(thisNode){ 328 | var selectReg = selectTextReg; 329 | var newNode; 330 | var contain = container[meditId]; 331 | if(selectReg.test(thisNode.innerHTML)) { 332 | var thisId = regNodeId.exec(thisNode.getAttribute("id"))[1]; 333 | var regRes = thisNode.innerHTML.split(selectReg); 334 | var style = thisNode.getAttribute("style"); 335 | if(regRes[0]!==''){ 336 | var spanPre =document.createElement("span"); 337 | spanPre.setAttribute("data-medit","true"); 338 | spanPre.setAttribute("data-meditMode","text"); 339 | spanPre.setAttribute("id","medit-" + thisId + "-" + meditId ); 340 | spanPre.setAttribute("style",style); 341 | spanPre.innerHTML = regRes[0]; 342 | thisNode.parentNode.insertBefore(spanPre, thisNode); 343 | thisId++; 344 | } 345 | if(regRes[2]!==''){ 346 | thisNode.innerHTML = regRes[2]; 347 | var span =document.createElement("span"); 348 | span.setAttribute("data-medit","true"); 349 | span.setAttribute("data-meditMode","text"); 350 | span.setAttribute("id","medit-" + thisId + "-" + meditId ); 351 | span.setAttribute("contentEditable","true"); 352 | span.setAttribute("class","medit-editing"); 353 | span.setAttribute("style",style); 354 | span.innerHTML = regRes[1]; 355 | this.blur(thisNode); 356 | thisNode.parentNode.insertBefore(span, thisNode); 357 | newNode = thisNode.previousSibling; 358 | }else{ 359 | thisNode.innerHTML = regRes[1]; 360 | newNode = thisNode; 361 | } 362 | contain.updateId(); 363 | contain.nowNodeId = thisId; 364 | nodeFocus(newNode); 365 | }else{ 366 | newNode = thisNode; 367 | } 368 | return newNode; 369 | } 370 | }, 371 | "br":{ 372 | icon: meditToolImage + 'images/mode/br.png', 373 | doWhat: function(node) { 374 | mode[node.getAttribute("data-meditmode")].blur(node); 375 | var temNode = document.createElement("span"); 376 | temNode.style.display = "block"; 377 | temNode.innerHTML = " "; 378 | temNode.setAttribute("data-medit", "true"); 379 | temNode.setAttribute("data-meditmode", "br"); 380 | node.parentNode.insertBefore(temNode,node); 381 | node.parentNode.removeChild(node); 382 | mode["br"].focus(temNode); 383 | nodeFocus(temNode); 384 | nowNode = temNode; 385 | toolBarModeSetting("br",[]); 386 | container[meditId].updateId(); 387 | nowMode = "br"; 388 | }, 389 | focus:function(node) { 390 | node.style.backgroundColor = "#e5e5e5"; 391 | }, 392 | blur:function(node) { 393 | node.style.backgroundColor = ""; 394 | } 395 | }, 396 | "link":{ 397 | icon: meditToolImage + 'images/mode/link.png', 398 | doWhat: function(node) { 399 | 400 | var parent = node; 401 | while(parent.parentNode.getAttribute("data-medit")){ 402 | parent = parent.parentNode; 403 | } 404 | if(parent.nodeName.toLowerCase() == "a"){ 405 | return; 406 | } 407 | 408 | var linkNode = document.createElement("a"); 409 | linkNode.setAttribute("data-medit","true"); 410 | linkNode.setAttribute("data-meditmode","link"); 411 | mode[node.getAttribute("data-meditmode")].blur(node); 412 | node.parentNode.insertBefore(linkNode,node); 413 | node.parentNode.removeChild(node); 414 | linkNode.appendChild(node); 415 | toolBarModeSetting("link",mode['link'].setting); 416 | mode["link"].focus(linkNode); 417 | container[meditId].updateId(); 418 | container[meditId].nowNodeId = regNodeId.exec(linkNode.getAttribute("id"))[1]; 419 | nowMode = "link"; 420 | clearTimeout(nodeFocusTimeout); 421 | nowNode = linkNode; 422 | }, 423 | setting: [ 424 | { 425 | name: "setting", 426 | icon: meditToolImage + "images/link/setting.png", 427 | doWhat: function(node){ 428 | var href = node.getAttribute("data-meditHref"); 429 | var hrefHtml = ''; 430 | if(href){ 431 | hrefHtml = ' value="'+href+'"'; 432 | } 433 | var target = node.getAttribute("target"); 434 | if(target && target != "_blank"){ 435 | target = ""; 436 | }else{ 437 | target = " checked"; 438 | } 439 | 440 | var html = ''; 441 | settingPageDisplay('超链接设置 Link Setting',html,function(){ 442 | var href = getNodeById("medit-settingPage-input-link"); 443 | if(href){ 444 | node.setAttribute("data-meditHref", href.value); 445 | } 446 | var checkbox = getNodeById("medit-settingPage-check-link"); 447 | if(checkbox.checked){ 448 | node.setAttribute("target", "_blank"); 449 | }else{ 450 | node.removeAttribute("target"); 451 | } 452 | settingPage.style.display = "none"; 453 | }); 454 | } 455 | }, 456 | { 457 | name: "cancellink", 458 | icon: meditToolImage + "images/link/cancel-link.png", 459 | doWhat: function(node){ 460 | var childs = toArray(node.children); 461 | var temNode = childs[0]; 462 | var temMode = temNode.getAttribute("data-meditmode"); 463 | childs.forEach(function(child){ 464 | node.parentNode.insertBefore(child, node); 465 | }); 466 | node.parentNode.removeChild(node); 467 | container[meditId].updateId(); 468 | toolBarModeSetting(temMode,mode[temMode].setting); 469 | mode[temMode].focus(temNode); 470 | nowNode = temNode; 471 | nodeFocus(temNode); 472 | } 473 | } 474 | ], 475 | focus: function(node){ 476 | getNodeById("medit-tool-button-mode").style.display = "none"; 477 | node.setAttribute("class","medit-link"); 478 | }, 479 | blur:function(node){ 480 | node.removeAttribute("class"); 481 | } 482 | }, 483 | "image":{ 484 | icon: meditToolImage + "images/mode/image.png", 485 | doWhat:function(node){ 486 | mode[node.getAttribute("data-meditmode")].blur(node); 487 | var temNode = document.createElement("img"); 488 | temNode.setAttribute("data-medit", "true"); 489 | temNode.setAttribute("data-meditmode", "image"); 490 | temNode.setAttribute("src", mode["image"].icon); 491 | temNode.setAttribute("width","32"); 492 | temNode.setAttribute("height","32"); 493 | node.parentNode.insertBefore(temNode,node); 494 | node.parentNode.removeChild(node); 495 | mode["image"].focus(temNode); 496 | nodeFocus(temNode); 497 | nowNode = temNode; 498 | toolBarModeSetting("image",mode["image"].setting); 499 | container[meditId].updateId(); 500 | nowMode = "image"; 501 | }, 502 | focus: function(node){ 503 | node.setAttribute("class","medit-image"); 504 | }, 505 | blur:function(node){ 506 | node.removeAttribute("class"); 507 | }, 508 | setting:[ 509 | { 510 | name:"setting", 511 | icon: meditToolImage + "images/image/setting.png", 512 | doWhat: function(node) { 513 | var width = node.getAttribute("width"); 514 | var height = node.getAttribute("height"); 515 | var address = node.getAttribute("src"); 516 | var html = '
宽度 Width:高度 Height:图像地址 Address:
'; 517 | settingPageDisplay('图像设置 Image Setting',html,function(){ 518 | var width = getNodeById("medit-settingPage-image-width").value; 519 | if(width && width>0){ 520 | node.setAttribute("width",width); 521 | } 522 | var height = getNodeById("medit-settingPage-image-height").value; 523 | if(height && height>0){ 524 | node.setAttribute("height",height); 525 | } 526 | var newAddress = getNodeById("medit-settingPage-image-address").value; 527 | if(address != newAddress){ // 传入网络图片需要进行宽高转换 528 | getNodeById("medit-settingPage-button").style.display = "none"; 529 | getNodeById("medit-settingPage-content").innerHTML = "Loading Image..."; 530 | var newImg = new Image(); 531 | newImg.src = newAddress; 532 | newImg.onload = function(){ 533 | var scale = newImg.width/ newImg.height; 534 | if(newImg.width>100){ 535 | newImg.width = 100; 536 | newImg.height = 100/scale; 537 | } 538 | settingPage.style.display = "none"; 539 | node.setAttribute("src",newAddress); 540 | node.setAttribute("width",newImg.width); 541 | node.setAttribute("height",newImg.height); 542 | } 543 | }else{ 544 | settingPage.style.display = "none"; 545 | } 546 | }); 547 | } 548 | }, 549 | { 550 | name: "biger", 551 | icon: meditToolImage + "images/image/biger.png", 552 | doWhat: function(node){ 553 | var width = node.getAttribute("width"); 554 | var height = node.getAttribute("height"); 555 | node.setAttribute("width",Math.ceil(width*1.1)); 556 | node.setAttribute("height",Math.ceil(height*1.1)); 557 | } 558 | }, 559 | { 560 | name: "smaller", 561 | icon: meditToolImage + "images/image/smaller.png", 562 | doWhat: function(node){ 563 | var width = node.getAttribute("width"); 564 | var height = node.getAttribute("height"); 565 | node.setAttribute("width",Math.ceil(width/1.1)); 566 | node.setAttribute("height",Math.ceil(height/1.1)); 567 | } 568 | }, 569 | { 570 | name: "upload", 571 | icon: meditToolImage + "images/image/upload.png", 572 | doWhat: function(node){ 573 | var config = container[meditId].imageUpload; 574 | var html = '
选择图片 Select Image
'; 575 | settingPageDisplay('图像上传 Image upload',html,function(){ 576 | 577 | var files = getNodeById("medit-image-upload-file"); 578 | if(files.files.length <=0){ 579 | return; 580 | } 581 | var file = files.files[0]; 582 | var size = file.size; 583 | var name = file.name; 584 | var type = file.type; 585 | var config = container[meditId].imageUpload; 586 | var ext = {}; 587 | 588 | config.ext && config.ext.forEach(function(v){ ext[commonImageType[v]] = true; }); 589 | 590 | if(config.size == 0 || config.size>=size){ 591 | if(!config.ext || ext[type]){ 592 | var http = getXhr(); 593 | var form = new FormData(getNodeById("medit-image-upload-form")); 594 | getNodeById("medit-settingPage-button").style.display = "none"; 595 | getNodeById("medit-settingPage-content").innerHTML = '
图片上传中 Image uploading...
'; 596 | http.upload.onprogress = function(v){ 597 | var progress = Math.floor(100*v.loaded/v.total) + "%"; 598 | var success = ""; 599 | if(progress == "100%") success ="上传成功,请稍后...
upload success,please waiting..."; 600 | getNodeById("medit-settingPage-content").innerHTML = '
图片上传中 Image uploading...'+ progress +'
'+success+'
'; 601 | } 602 | 603 | Object.keys(config.params).map(function(key) { 604 | form.append(key, config.params[key]); 605 | }); 606 | 607 | http.open("POST",config.path); 608 | http.send(form); 609 | 610 | http.onreadystatechange = function(){ 611 | if(http.readyState === 4){ 612 | if(http.status === 200 || http.status>200 && http.status<400){ 613 | var res = JSON.parse(http.responseText); 614 | 615 | if(res.code == "success"){ 616 | config.success(res); 617 | var scale = res.data.width/ res.data.height; 618 | if(res.data.width>100){ 619 | res.data.width = 100; 620 | res.data.height = 100/scale; 621 | } 622 | settingPage.style.display = "none"; 623 | node.setAttribute("src",res.data.url); 624 | node.setAttribute("width",res.data.width); 625 | node.setAttribute("height",res.data.height); 626 | } 627 | }else{ 628 | config.error("http status " + http.status); 629 | } 630 | } 631 | } 632 | }else{ config.error("image type limit "+config.ext.join(",")); } 633 | }else{ config.error("image size limit "+config.size); } 634 | }); 635 | getNodeById("medit-settingPage-button-ok").innerHTML = "上传 Upload"; 636 | var btn = getNodeById("medit-image-upload-select-btn"); 637 | var fileInput = getNodeById("medit-image-upload-file"); 638 | 639 | btn.onclick = function(){ 640 | fileInput.click(); 641 | } 642 | fileInput.onchange = function(e){ 643 | 644 | e = e || window.event; 645 | var files=e.target.files||e.srcElement.files; 646 | var file = files[0]; 647 | var size = file.size; 648 | var name = file.name; 649 | var type = file.type; 650 | 651 | var ext = {}; 652 | config.ext && config.ext.forEach(function(v){ ext[commonImageType[v]] = true; }); 653 | 654 | if (config.size == 0 || config.size>=size){ 655 | if (!config.ext || ext[type]){ 656 | btn.innerHTML = name; 657 | } else { 658 | config.error("image type limit "+config.ext.join(",")); 659 | } 660 | } else { 661 | config.error("image size limit "+config.size); 662 | } 663 | } 664 | } 665 | }, 666 | { 667 | name: 'verticalAlign', 668 | icon: meditToolImage + 'images/image/vertical-align.png', 669 | doWhat:[ 670 | { 671 | name: 'top', 672 | icon: meditToolImage + 'images/image/top-align.png', 673 | doWhat:function(node){ 674 | node.style.verticalAlign = "top"; 675 | } 676 | }, 677 | { 678 | name: 'middle', 679 | icon: meditToolImage + 'images/image/middle-align.png', 680 | doWhat:function(node){ 681 | node.style.verticalAlign = "middle"; 682 | } 683 | }, 684 | { 685 | name: 'bottom', 686 | icon: meditToolImage + 'images/image/bottom-align.png', 687 | doWhat:function(node){ 688 | node.style.verticalAlign = "bottom"; 689 | } 690 | } 691 | ] 692 | } 693 | ] 694 | }, 695 | "list":{ 696 | emptyNotDelete: true, 697 | icon: meditToolImage + "images/mode/list.png", 698 | focus:function(node){ 699 | node.setAttribute("class","medit-list"); 700 | getNodeById("medit-tool-button-mode").style.display = "none"; 701 | }, 702 | blur:function(node){ 703 | node.removeAttribute("class"); 704 | }, 705 | doWhat:function(node){ 706 | mode[node.getAttribute("data-meditmode")].blur(node); 707 | var temNode = document.createElement("ul"); 708 | temNode.setAttribute("data-medit", "true"); 709 | temNode.setAttribute("data-meditmode", "list"); 710 | temNode.setAttribute("type", "disc"); 711 | node.parentNode.insertBefore(temNode,node); 712 | node.parentNode.removeChild(node); 713 | nodeFocus(temNode); 714 | nowNode = temNode; 715 | toolBarModeSetting("list",mode["list"].setting); 716 | mode["list"].focus(temNode); 717 | container[meditId].updateId(); 718 | nowMode = "list"; 719 | }, 720 | setting:[ 721 | { 722 | name:"setting", 723 | icon: meditToolImage + "images/list/setting.png", 724 | doWhat:function(node){ 725 | var type = node.getAttribute("type").toLowerCase(); 726 | var nodeType = node.nodeName.toLowerCase(); 727 | var html = '
无序列表 Unorder List:
'+ 728 | ''+ 729 | ''+ 730 | ''+ 731 | '
有序列表 Order List:
'+ 732 | ''+ 733 | ''+ 734 | ''+ 735 | ''+ 736 | ''; 737 | settingPageDisplay('列表类型 List Mode',html,function(){ 738 | var radio = getNodeById("medit-settingPage-content").getElementsByTagName("input"); 739 | var input = null; 740 | for(var i = 0; i'+title+'
':''); 1051 | html.push(''); 1052 | html.push('
'+content+'
'); 1053 | html.push('
确定 Ok
'); 1054 | getNodeById("medit-settingPage-main").innerHTML = html.join(""); 1055 | settingPageOk = okCallBack; 1056 | settingPage.style.display = "block"; 1057 | } 1058 | 1059 | var mainDo = function(degree, type, target) { 1060 | 1061 | var contain = container[meditId]; 1062 | var thisNode = getNodeById("medit-" + contain.nowNodeId + "-" + meditId); // 这里有个已经修复的bug,比如在超链接中,是在当前结点外部包了一层,那么thisNode需要更新到外层结点 1063 | 1064 | nowMode = thisNode.getAttribute("data-meditMode"); 1065 | 1066 | if( mode[nowMode].selected ){ 1067 | if(globalSelectionContent && globalSelectionContent.node == thisNode && globalSelectionContent.end!=globalSelectionContent.start ){ // 原生自带文本选择处理 1068 | var html = globalSelectionContent.node.innerHTML.split(""); 1069 | html.splice(globalSelectionContent.end,0,''); 1070 | html.splice(globalSelectionContent.start,0,''); 1071 | globalSelectionContent.node.innerHTML = html.join(""); 1072 | } 1073 | } 1074 | if(degree == 1) { 1075 | switch(type){ 1076 | case 'delete': 1077 | if(mode[nowMode].blur){ 1078 | mode[nowMode].blur(thisNode); 1079 | } 1080 | 1081 | while(thisNode.parentNode.getAttribute("data-medit") && thisNode.parentNode.children.length === 1 && !mode[thisNode.parentNode.getAttribute("data-meditMode")].emptyNotDelete){ 1082 | thisNode = thisNode.parentNode; 1083 | } 1084 | thisNode.parentNode.removeChild(thisNode); 1085 | contain.updateId(); 1086 | toolBarHidden(); 1087 | break; 1088 | case 'ok': 1089 | if(mode[nowMode].blur){ 1090 | mode[nowMode].blur(thisNode); 1091 | } 1092 | 1093 | if(mode[nowMode].empty && mode[nowMode].empty(thisNode)) thisNode.parentNode.removeChild(thisNode); 1094 | toolBarHidden(); 1095 | contain.nodeCount++; 1096 | 1097 | thisNode = mergeSimilarNextNode(thisNode); 1098 | mergeSimilarPreNode(thisNode); 1099 | break; 1100 | case 'addLeft': 1101 | if(mode[nowMode].empty && mode[nowMode].empty(thisNode)){ 1102 | return; 1103 | } 1104 | if(mode[nowMode].blur){ 1105 | mode[nowMode].blur(thisNode); 1106 | } 1107 | contain.createSpan(contain.nowNodeId, thisNode, false, true); 1108 | break; 1109 | case 'addRight': 1110 | if(mode[nowMode].empty && mode[nowMode].empty(thisNode)){ 1111 | return; 1112 | } 1113 | if(mode[nowMode].blur){ 1114 | mode[nowMode].blur(thisNode); 1115 | } 1116 | contain.createSpan(contain.nowNodeId, thisNode, true, true); 1117 | 1118 | break; 1119 | case 'mode': 1120 | var toolBarRes = []; 1121 | toolBarRes.push(returnButtonHtml(nowMode + "-setting-1")); 1122 | 1123 | for(var modeType in mode){ 1124 | if(mode.hasOwnProperty(modeType) && modeType != nowMode && !mode[modeType].notDisplay){ 1125 | var style = mode[modeType].icon?' style="background:#428bca url('+ mode[modeType].icon+') no-repeat center center;background-size: 24px;"':''; 1126 | 1127 | toolBarRes.push(' '); 1128 | } 1129 | } 1130 | toolBar.innerHTML = toolBarRes.join(""); 1131 | break; 1132 | 1133 | case 'return': 1134 | var path = target.getAttribute("id").replace("medit-tool-button-",""); 1135 | var nodePath = path.split("-"); 1136 | var doWhat = mode; 1137 | var pathMode = null; 1138 | 1139 | nodePath.pop(); 1140 | path = path.replace(/\-[a-zA-Z]*\-\d*$/,''); 1141 | 1142 | while(pathMode = nodePath.shift()){ 1143 | doWhat = doWhat[pathMode]; 1144 | } 1145 | toolBarModeSetting(path, doWhat); 1146 | 1147 | break; 1148 | } 1149 | }else{ 1150 | 1151 | if( mode[nowMode].selected ){ 1152 | thisNode = mode[nowMode].selected(thisNode); 1153 | } 1154 | 1155 | var pathRes = target.getAttribute("id").replace("medit-tool-button-",""); 1156 | 1157 | var pathArr = pathRes.split("-"); 1158 | 1159 | var pathMode = null; 1160 | 1161 | var doWhat = mode; 1162 | 1163 | while(pathMode = pathArr.shift()){ 1164 | doWhat = doWhat[pathMode]; 1165 | } 1166 | 1167 | doWhat = doWhat.doWhat; 1168 | if(isType(doWhat, "array")){ 1169 | toolBarModeSetting(pathRes, doWhat); 1170 | }else{ 1171 | doWhat(thisNode); 1172 | } 1173 | } 1174 | 1175 | if(!contain.node.children.length) contain.node.innerHTML = contain.preHTML || "Medit"; 1176 | } 1177 | 1178 | var toolBarModeSetting = function(path, list){ 1179 | 1180 | var pathRes = path.split("-"); 1181 | 1182 | var toolBarRes = []; 1183 | 1184 | if(pathRes.length === 1){ 1185 | 1186 | mainButton.forEach(function(v,index){ 1187 | 1188 | if(v === "setting"){ 1189 | if(list){ 1190 | list.forEach(function(listv, listIndex){ 1191 | 1192 | var defaultValue = listv.defaultValue || " "; 1193 | 1194 | var style = listv.icon?' style="background:#428bca url('+listv.icon+') no-repeat center center;background-size: 24px;"':''; 1195 | 1196 | toolBarRes.push(''+defaultValue+''); 1197 | }); 1198 | } 1199 | }else{ 1200 | toolBarRes.push(' '); 1201 | } 1202 | }); 1203 | 1204 | }else{ 1205 | toolBarRes.push(returnButtonHtml(path)); 1206 | if(!!list.length){ 1207 | list.forEach(function(listv, listIndex){ 1208 | 1209 | var defaultValue = listv.defaultValue || " "; 1210 | 1211 | var style = listv.icon?' style="background:#428bca url('+listv.icon+') no-repeat center center;background-size: 24px;"':' style="background:#428bca;"'; 1212 | 1213 | toolBarRes.push(''+defaultValue+''); 1214 | }); 1215 | } 1216 | } 1217 | 1218 | toolBar.innerHTML = toolBarRes.join(""); 1219 | 1220 | } 1221 | 1222 | var toolBarDisplay = function() { 1223 | if(toolBarCatch) 1224 | toolBar.innerHTML = toolBarCatch; 1225 | toolBar.style.display = "block"; 1226 | } 1227 | 1228 | var toolBarHidden = function() { 1229 | nowNode = null; 1230 | toolBar.style.display = "none"; 1231 | } 1232 | 1233 | var toBr = function(node){ 1234 | node.setAttribute("style","display:block"); 1235 | node.setAttribute("data-meditMode","br"); 1236 | node.setAttribute("contentEditable","false"); 1237 | node.setAttribute("class",""); 1238 | node.innerHTML = " "; 1239 | } 1240 | 1241 | var nodeFocus = function(node){ // 使模块自动获取焦点 使用了很多方法,最后发现这个方法是在移动端最好的 1242 | nodeFocusTimeout = setTimeout(function() { 1243 | node.focus(); 1244 | container[meditId].nowNodeId = regNodeId.exec(node.getAttribute("id"))[1]; 1245 | }, 10); 1246 | } 1247 | 1248 | var selectModeContent = function(isAdd){ 1249 | if(nowNode){ 1250 | var nodeMode = nowNode.getAttribute("data-meditmode"); 1251 | var nodeModeObj = mode[nodeMode]; 1252 | if(nodeModeObj.selecting){ 1253 | nodeModeObj.selecting(nowNode, isAdd); 1254 | } 1255 | } 1256 | } 1257 | 1258 | var medit = function(node, toolBarContainer) { 1259 | if(!(this instanceof medit)) return new medit(node, toolBarContainer); 1260 | 1261 | if(!node || node.nodeType != 1)return false; 1262 | 1263 | if(toolBarContainer && toolBarContainer.nodeType == 1) { 1264 | toolBar.parentNode.removeChild(toolBar); 1265 | toolBar.setAttribute("class", "medit-tool-inner"); 1266 | toolBarContainer.appendChild(toolBar); 1267 | } 1268 | 1269 | this.node = node; 1270 | this.nodeCount = 0; // 容器所有子元素数目 1271 | this.nowNodeId = 0; // 容器当前子元素ID 1272 | 1273 | this.getContent = medit.prototype.getContent; 1274 | 1275 | this.node.setAttribute("data-meditId",container.length); 1276 | 1277 | this.imageUpload = { // 默认图片上传设置,由于是文件上传,所以在跨域方法仅支持CORS 1278 | path:'https://sm.ms/api/upload', 1279 | params: {}, 1280 | name:'smfile', 1281 | size:0, 1282 | timeout:0, 1283 | ext:["jpg","jpeg","png","gif","bmp"], 1284 | success:function(){}, 1285 | error:function(){} 1286 | } 1287 | 1288 | container.push(this); 1289 | 1290 | gevent(this.node, ["touchstart"], function(e){ 1291 | mainTouchPoint = e.targetTouches[0]; 1292 | 1293 | if(window.getSelection){ // 原生手势长按选择 1294 | if(globalSelectionHandle)clearTimeout(globalSelectionHandle); 1295 | var selectionHandle = window.getSelection(); 1296 | var selectionCheckTimeout = function(){ 1297 | if(selectionHandle && selectionHandle.anchorNode && selectionHandle.anchorNode == selectionHandle.focusNode && selectionHandle.anchorNode.parentNode==nowNode){ 1298 | globalSelectionContent = { 1299 | handle: selectionHandle, 1300 | node: selectionHandle.anchorNode.parentNode, 1301 | start: selectionHandle.anchorOffsetselectionHandle.focusOffset?selectionHandle.anchorOffset:selectionHandle.focusOffset 1303 | } 1304 | globalSelectionHandle = setTimeout(selectionCheckTimeout, 100); 1305 | }else{ 1306 | if(globalSelectionContent){ 1307 | globalSelectionContent.handle.removeAllRanges(); 1308 | globalSelectionContent = null; 1309 | } 1310 | if(globalSelectionHandle)clearTimeout(globalSelectionHandle); 1311 | } 1312 | } 1313 | selectionCheckTimeout(); 1314 | } 1315 | }); 1316 | 1317 | gevent(this.node, ["touchmove"], function(e){ 1318 | 1319 | e = e || window.event; 1320 | var distance = e.targetTouches[0].clientX - mainTouchPoint.clientX; 1321 | 1322 | if(Math.abs(distance) > 50){ 1323 | var isAdd = distance > 0? true: false; 1324 | selectModeContent(isAdd); 1325 | mainTouchPoint = e.targetTouches[0]; 1326 | } 1327 | isContainMove = true; 1328 | }); 1329 | gevent(this.node, ["touchend"], this.editContainFocus); 1330 | 1331 | gevent(this.node, ["keydown"], function(e){ 1332 | e = e || window.event; 1333 | if(e.keyCode == 13){ 1334 | e.preventDefault(); 1335 | var contain = container[meditId]; 1336 | var thisNode = getNodeById("medit-" + contain.nowNodeId + "-" + meditId); 1337 | nowMode = thisNode.getAttribute("data-meditMode"); 1338 | if(mode[nowMode].blur){ 1339 | var newNode = mode[nowMode].blur(thisNode); // 如果当前按下回车按钮的块需要对内容进行处理,或者是结点进行改变了 1340 | if(newNode){ 1341 | if(newNode.exit) return; // 不需要继续创建新的结点了 1342 | thisNode = newNode; 1343 | } 1344 | } 1345 | contain.createSpan(contain.nowNodeId, thisNode, true, true); 1346 | 1347 | var brNode = getNodeById("medit-" + contain.nowNodeId + "-" + meditId); 1348 | toBr(brNode); 1349 | 1350 | contain.createSpan(contain.nowNodeId, brNode, true, true); 1351 | } 1352 | }); 1353 | } 1354 | 1355 | var returnNextNodeId = function(node){ 1356 | var child = node.children; 1357 | if(child.length) return returnNextNodeId(child[child.length-1]); 1358 | return Number(regNodeId.exec(node.getAttribute("id"))[1])+1; 1359 | } 1360 | 1361 | medit.prototype.createSpan = function(nodeId, fromNode, isAfter, isAutoFocus){ // 因为在内部创建span的时候不会自动focus,需要调用一下focus方法 1362 | 1363 | var span =document.createElement("span"); 1364 | span.setAttribute("data-medit","true"); 1365 | span.setAttribute("data-meditMode","text"); 1366 | span.setAttribute("id","medit-" + nodeId + "-" + meditId ); 1367 | span.setAttribute("contentEditable","true"); 1368 | span.setAttribute("class","medit-editing"); 1369 | if(fromNode){ 1370 | if(!isAfter){ 1371 | this.nowNodeId = nodeId; 1372 | fromNode.parentNode.insertBefore(span, fromNode); 1373 | }else{ 1374 | this.nowNodeId = returnNextNodeId(fromNode); 1375 | if(!fromNode.nextSibling){ 1376 | fromNode.parentNode.appendChild(span); 1377 | }else{ 1378 | fromNode.parentNode.insertBefore(span, fromNode.nextSibling); 1379 | } 1380 | } 1381 | this.updateId(); 1382 | }else{ 1383 | this.node.appendChild(span); 1384 | } 1385 | var editor = getNodeById("medit-" + this.nowNodeId + "-" + meditId); 1386 | if(nowNode){ // 当创建新的标签的时候把之前/之后的相似结点进行合并 1387 | if(!isAfter){ 1388 | mergeSimilarNextNode(nowNode, true); 1389 | }else{ 1390 | mergeSimilarPreNode(nowNode, true); 1391 | } 1392 | } 1393 | 1394 | this.nowNodeId = Number(regNodeId.exec(editor.id)[1]); // 因为合并结点之后要重新更新id, 所以需要重新获取当前结点ID 1395 | nowNode = editor; 1396 | toolBarModeSetting("text", mode["text"].setting); 1397 | 1398 | if(isAutoFocus){ 1399 | nodeFocus(editor); 1400 | } 1401 | } 1402 | 1403 | medit.prototype.updateId = function(nodeId, list) { 1404 | 1405 | var child = list || toArray(this.node.children); 1406 | var index = nodeId || 0; 1407 | child.forEach(function(v){ 1408 | if(v.getAttribute("href")){ // 防止超链接在内部触发 1409 | v.setAttribute("data-meditHref",v.getAttribute("href")); 1410 | v.removeAttribute("href"); 1411 | } 1412 | v.setAttribute("id","medit-" + (index++) + "-" + meditId ); 1413 | var secondChild = toArray(v.children); 1414 | if(secondChild.length){ 1415 | index = container[meditId].updateId(index, secondChild); 1416 | } 1417 | }); 1418 | return index; 1419 | } 1420 | 1421 | medit.prototype.getContent = function(isEdit){ 1422 | isEdit = isEdit || false; 1423 | if(!isEdit && toolBar.style.display == "block"){ 1424 | mainDo(1, "ok"); 1425 | } 1426 | var html = this.node.innerHTML; 1427 | if(regIsNotContentEmpty.test(html)){ 1428 | html = html.replace(/\sdata\-meditHref="(.*?)"/ig," href=\"$1\""); 1429 | html = html.replace(selectTextReg,"$1"); 1430 | return html.replace(regContent, " "); 1431 | 1432 | } 1433 | return ""; 1434 | } 1435 | 1436 | medit.prototype.autoSave = function(appId, callBack){// 自动保存 callBack(data, timeStamp),自动恢复已保存数据 1437 | if(window.localStorage){ 1438 | this.appId = appId; 1439 | var oldData = localStorage.getItem("meditAutosave"+appId); // repair bug: id is need not exits '-' 1440 | var temData = this.getContent(true); 1441 | if(!regIsNotContentEmpty.test(temData) && oldData){ 1442 | meditId = this.node.getAttribute("data-meditid"); 1443 | this.node.innerHTML = oldData; 1444 | this.updateId(); 1445 | } 1446 | 1447 | clearInterval(this.autoSaveInterval); 1448 | var _this = this; 1449 | this.autoSaveInterval = setInterval(function(){ 1450 | var nowData = _this.getContent(true); 1451 | localStorage.setItem("meditAutosave"+appId,nowData); 1452 | callBack(nowData, (new Date())-0); 1453 | },1000); 1454 | } 1455 | } 1456 | 1457 | medit.prototype.image = medit.prototype.imageUpload = function(option){ // 图片上传设置 1458 | var meditId = this.node.getAttribute("data-meditid") - 0; 1459 | var contain = container[meditId]; 1460 | for(var item in option){ 1461 | if(contain.imageUpload[item] != null){ 1462 | contain.imageUpload[item] = option[item]; 1463 | } 1464 | } 1465 | } 1466 | 1467 | medit.prototype.clear = function(data){ 1468 | data = data||""; 1469 | clearInterval(this.autoSaveInterval); 1470 | this.node.innerHTML = data; 1471 | if(this.appId!=null){ 1472 | localStorage.removeItem("meditAutosave"+this.appId); 1473 | } 1474 | } 1475 | 1476 | medit.prototype.editContainFocus = function(e) { 1477 | 1478 | e = e || window.event; 1479 | var target = e.target || e.srcElement; 1480 | 1481 | if(isContainMove){ 1482 | e.preventDefault(); 1483 | e.stopPropagation(); 1484 | isContainMove = false; 1485 | return; 1486 | } 1487 | 1488 | toolBarDisplay(); 1489 | 1490 | if(meditId != null) { // 在已经选择某个区块的时候选择其它的,会先调用这个区块的blur 1491 | var temObj = container[meditId]; 1492 | var temNode = getNodeById("medit-" + temObj.nowNodeId + "-" + meditId); 1493 | 1494 | if(temNode){ 1495 | var temNodeMode = temNode.getAttribute("data-meditMode"); 1496 | if(mode[temNodeMode].empty && mode[temNodeMode].empty(temNode) && temNode != target){ 1497 | temNode.parentNode.removeChild(temNode); 1498 | temObj.updateId(); 1499 | }else{ 1500 | if(mode[temNodeMode].blur){ 1501 | mode[temNodeMode].blur(temNode); 1502 | } 1503 | } 1504 | } 1505 | } 1506 | 1507 | var type = target.getAttribute("data-medit"); 1508 | if(!type && target.getAttribute("data-meditId")){ // target is container 1509 | meditId = target.getAttribute("data-meditId"); // 全局存贮当前medit容器ID 1510 | var meditObj = container[ meditId]; 1511 | var child = target.children; 1512 | if(!child.length){ // 如果点击了容器发现没有结点,那么就保存原有内容,并且创建新的span 1513 | meditObj.preHTML = target.innerHTML; 1514 | target.innerHTML = ""; 1515 | 1516 | meditObj.createSpan(0); 1517 | 1518 | target = false; 1519 | }else{ 1520 | var temTarget = child[child.length-1]; 1521 | var temTargetMode = temTarget.getAttribute("data-meditMode"); 1522 | if(!mode[temTargetMode].empty || !mode[temTargetMode].empty(temTarget)){ 1523 | meditObj.createSpan(child.length-1,temTarget, true); 1524 | target = false; 1525 | }else{ 1526 | target = temTarget; 1527 | nowNode = target; 1528 | } 1529 | 1530 | } 1531 | }else{ // target is 内部包含结点 1532 | while(!target.getAttribute("data-medit")){ 1533 | target = target.parentNode; 1534 | } 1535 | nowNode = target; 1536 | } 1537 | 1538 | if(target){ 1539 | if(!target.id) { 1540 | var parentNode = target.parentNode; 1541 | while(!parentNode.getAttribute("data-meditid")){ 1542 | parentNode = parentNode.parentNode; 1543 | } 1544 | meditId = parentNode.getAttribute("data-meditid"); 1545 | container[meditId].updateId(); 1546 | } 1547 | var idExecRes = regNodeId.exec(target.id); 1548 | meditId = Number(idExecRes[2]); 1549 | var meditObj = container[ meditId]; 1550 | meditObj.nowNodeId = Number(idExecRes[1]); 1551 | 1552 | var meditNodeMode = target.getAttribute("data-meditMode"); 1553 | 1554 | toolBarModeSetting(meditNodeMode, mode[meditNodeMode].setting); 1555 | 1556 | if(mode[meditNodeMode].focus){ 1557 | mode[meditNodeMode].focus(target); 1558 | } 1559 | } 1560 | } 1561 | 1562 | medit.settingPage = function(title, content, callBack) { 1563 | settingPageDisplay(title, content, function(){ 1564 | callBack(); 1565 | settingPage.style.display = "none"; 1566 | }) 1567 | } 1568 | 1569 | medit.extend = function(obj) { // 扩展方法 会向doWhat方法中传入当前结点,然后需要返回一个新的结点 1570 | if(obj && obj.icon && obj.doWhat && obj.focus && obj.blur && obj.name){ 1571 | if(!mode[obj.name]){ 1572 | mode[obj.name] = fun_deep_clone(obj); 1573 | mode[obj.name].doWhat = function(node){ 1574 | mode[node.getAttribute("data-meditmode")].blur(node); 1575 | var newNode = obj.doWhat(node); 1576 | if(newNode) { 1577 | newNode.setAttribute("data-meditmode", obj.name); 1578 | newNode.setAttribute("data-medit", "true"); 1579 | mode[obj.name].focus(newNode); 1580 | nodeFocus(newNode); 1581 | nowNode = newNode; 1582 | toolBarModeSetting(obj.name,obj.setting||[]); 1583 | container[meditId].updateId(); 1584 | nowMode = obj.name; 1585 | } 1586 | } 1587 | }else{ 1588 | throw new Error(obj.name + ' has already exist!'); 1589 | } 1590 | } 1591 | } 1592 | 1593 | 1594 | medit.nativeSetting = function(execFun){ // 原生自带功能配置接口 1595 | for(var i =0; i