Repository: echosoar/medit Branch: master Commit: 25af04b5140d Files: 8 Total size: 65.0 KB Directory structure: gitextract_t_m94g0a/ ├── .gitignore ├── README.md ├── package.json ├── src/ │ ├── medit.css │ └── medit.js ├── test/ │ ├── ajaxImageUpload.html │ └── contentEditableFocus.html └── webpack.config.dev.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ node_modules demo ================================================ FILE: README.md ================================================ ![logo](./images/logo.png) # Medit v2.0.0 *** medit新版本正在daily/3.0.0分支上进行开发,如有问题或者3.0版本开发建议可以提交issue *** A creative WYSIWYG rich text editor for mobile device by javascript. 一个创新型的移动端所见即所得富文本编辑器。 Website Address : [https://medit.js.org](https://medit.js.org) Demo: [Medit Demo](https://medit.js.org/demo.html) ##### 为了更专注做一个更具价值和体验的移动端富文本编辑器,所以Medit目前不支持Pc端使用,仅支持移动端。 *** ### Medit2.0.0 较上一版本更新内容 1. 更易用的内容选择方式,目前已支持选取内容块后手势左右滑动选择、通过手机原生自带长按选择进行编辑操作。 2. 优化编辑器样式,把原有的图标、弹层和编辑器内部标识都进行了优化。 3. 开放功能扩展接口Medit.extend,可以通过这个接口来扩展更加丰富的内容。 4. 开放内置功能配置接口Medit.nativeSetting,开放内置弹层调用接口Medit.settingPage。 5. 工具条目前不在限制于页面顶部,用户可以对工具条进行自定义配置。 *** ### 如何使用: + 第一步,引入medit.js文件,如果不下载使用icon包的话可能会导致部分功能性icon无法显示,icon存放于 __github/medit/build/images__ 下 ```html ``` > 在第一步和第二步之间可以选择性的引入medit插件,也可以自己来书写medit插件,medit提供了两个方法,一个是 __medit.extend__ 方法来配置扩展插件,另外还有一个 __medit.nativeSetting__ 方法来配置内部功能,详情请看下面的 medit类方法。 + 第二步,创建medit实例 ```html ``` + 经过上面两步之后一个medit富文本编辑器就可以使用了。 *** ### Medit实例方法 通过上面创建好的medit实例meditObj可以调用medit的方法来实现你想要的功能。 + meditObj.getContent() 获取medit编辑器中所编辑的内容结果。 + meditObj.clear() 清除medit编辑器的内容和自动保存在客户端浏览器中的内容。 + meditObj.autoSave(name, callBack(content, time)) 配置medit自动保存,需要传入两个参数: + name:为了保证在同一页面引入两个编辑器后自动保存的效果,所以需要手动传入一个自动保存的字段名称,需要在页面中保持唯一性。 + callBack(content, time):这是自动保存的回调函数,每次medit自动保存后都会调用这个回调函数,并传入当前保存的编辑器内容content和当前时间戳time。 + meditObj.image(option) || meditObj.imageUpload(option) medit图片上传配置,option是配置参数 ```javascript { // 默认图片上传设置 path:'https://sm.ms/api/upload', // 图片上传路径 name:'smfile', // 图片上传文件参数 size:0, // 大小限制,0为不限制大小 timeout:0, // 上传超时时间,0为不限制 ext:["jpg","jpeg","png","gif","bmp"], // 上传文件格式限制 success:function(){}, // 上传成功回调 error:function(){} // 上传失败回调 } ``` *** ### medit类方法 目前有三个medit类方法,所谓类方法就是直接通过medit类进行调用而不是通过medit实例进行调用。 + medit.extend(config) 功能扩展方法,可以通过这个方法实现medit插件和功能扩展。 config是一个对象,其中必须包括 __图标: icon__ 、 __其它类型模块转换为此类型模块时动作: doWhat__ 、 __模块得到焦点时动作: focus__ 、__模块失去焦点时动作: blur__ 和 __模块名称: name__ 这五个属性。其中icon可以是远程url,也可以是dataURL;name必须保持唯一,不能与内置功能名称产生冲突。 下面是一个功能模块的完整配置属性: ```javscript /* icon: [String] 类型选择icon url name: [String] 类型名称 isMerge: [Bollean] 是否开启相同内容自动合并 notDisplay: [Bollean] 在选择模式的时候不显示, emptyNotDelete: [Bollean] 如果当前块只存在一个子节点并且这个子节点要删除的时候是否开启递归删除 doWhat: [Function] 转换到此类型时会自动做哪些转换 focus:[Function] 点击或此模块获取焦点时自动触发的函数 blur:[Function] 此模块失去焦点时自动触发的函数 empty: [Function] 什么时候当前模块为空 selecting:[Function] 选择当前模块并且手指在屏幕上移动时触发的操作 selected:[Function] 手指移动结束执行的操作 setting: [Array(Object)] 当前模块可以进行哪些操作 -- name: [String] 操作名称 -- icon: [String] 操作按钮icon url -- doWhat: [Function/Array] 点击此操作按钮执行什么,或者是存在更深层次操作 */ ``` + medit.nativeSetting(callBack(config, name)) 内部功能配置方法,会循环调用callBack,然后把内置功能的配置和名称传入,返回值应该是一个修改后的config,然后medit就会应用这个config,如果没有返回值那么medit也就不会做任何改动。 + medit.settingPage(title, contentHTML, callBack) 打开medit内置弹层,title是配置弹层顶部Title,contentHTML可以传入一段html文本作为弹层的内容,callBack是在弹层的ok按钮点击之后触发。 下面是一段应用medit类方法的实例: ```html ``` *** © MIT License ================================================ FILE: package.json ================================================ { "name": "medit", "version": "1.0.0", "description": "A creative WYSIWYG rich text editor for mobile device.", "main": "medit.js", "scripts": { "dev": "webpack --config webpack.config.dev.js --watch", "build": "webpack --config webpack.config.min.js" }, "repository": { "type": "git", "url": "https://github.com/echosoar/medit.git" }, "keywords": [ "WYSIWYG", "mobile", "rich", "text", "editor" ], "author": "echosoar", "license": "ISC", "bugs": { "url": "https://github.com/echosoar/medit/issues" }, "homepage": "https://medit.js.org", "devDependencies": { "babel-plugin-transform-es2015-classes": "^6.18.0", "copy-webpack-plugin": "^4.0.1", "eslint": "~3.4.0", "eslint-plugin-babel": "^3.2.0", "eslint-plugin-react": "~4.2.3", "optimist": "^0.6.1", "resolve-url-loader": "^1.6.1", "url-loader": "~0.5.7", "webpack": "~1.12.14" }, "dependencies": { "autoprefixer": "~6.3.4", "babel-core": "^6.14.0", "babel-eslint": "^6.1.2", "babel-loader": "~6.2.4", "babel-plugin-transform-object-rest-spread": "~6.6.5", "babel-polyfill": "^6.3.14", "babel-preset-es2015": "^6.14.0", "css-loader": "~0.23.1", "file-loader": "~0.8.5", "less": "~2.6.1", "less-loader": "~2.2.2", "loader-utils": "~0.2.12", "md5": "~2.0.0", "node-sass": "^3.6.0", "postcss-loader": "~0.8.2", "precss": "~1.4.0", "sass-loader": "^3.2.0", "style-loader": "~0.13.0" } } ================================================ FILE: src/medit.css ================================================ #medit { font-family:微软雅黑,黑体,Helvetica,华文黑体; font-size: 14px; word-break:break-all; } #medit * { outline:none; } #medit span { position: relative; outline:none; } .medit-editing{ outline:none; padding: 0 20px; background: #eeddff; } #medit-tool { position:fixed; display: none; z-index:100; width:100%; left: 0; top: 0; height:40px; white-space: nowrap; overflow:auto; background:#428bca; text-align: left; font-family:微软雅黑,黑体,Helvetica,华文黑体; } #medit-tool.medit-tool-inner { position:relative; } #medit [data-meditMode=br] { height: 14px; width: 20px; margin:10px 0; border-radius: 3px; background:url("./images/icon-br.png") no-repeat center; background-size: 14px; } .medit-tool-button { width:40px; display:inline-block; height:40px; line-height: 40px; text-align:center; font-size:13px; cursor: pointer; color:#fff; font-weight:bold; } .medit-tool-addLeft { background:url("./images/add-left.png") no-repeat center center; background-size: 24px; } .medit-tool-addRight { background:url("./images/add-right.png") no-repeat center center; background-size: 24px; } .medit-tool-delete { background:url("./images/close.png") no-repeat center center; background-size: 24px; } .medit-tool-mode { background:url("./images/mode.png") no-repeat center center; background-size: 24px; } .medit-tool-ok { background:url("./images/ok.png") no-repeat center center; background-size: 24px; } .medit-tool-return { background:url("./images/return.png") no-repeat center center; background-size: 24px; } .medit-tool-button{ border-right: 1px solid #357ebd; background-color: #428bca; } /* text */ .medit-text-select { position:relative; background:#9cf; } /* Link */ .medit-link { background: #def; border-bottom:1px dashed #709; } #medit a { position: relative; outline:none; padding: 0 20px; } #medit a:before { content: " "; position: absolute; left: 0; top: 0; width: 20px; height:20px; background:url("./images/link/link-left.png") no-repeat center center; background-size: 20px; } #medit a:after { content: " "; position: absolute; bottom: 0; width: 20px; height:12px; background:url("./images/link/link-right.png") no-repeat center center; background-size: 20px; } /* setting page */ #medit-settingPage{ display: none; position:fixed; z-index:101; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,.6); font-size:14px; font-family:微软雅黑,黑体,Helvetica,华文黑体; } #medit-settingPage input { font-family:微软雅黑,黑体,Helvetica,华文黑体; } #medit-settingPage-main{ position:absolute; top:50%; left:10%; transform:translateY(-50%); width:80%; background:#fff; border-radius:3px; border:1px solid #666; } #medit-settingPage-title { padding:0 10px; font-size:12px; color:#000; height:36px; line-height:36px; border-bottom:1px solid #ddd; } #medit-settingPage-content { border-radius: 3px; } #medit-settingPage-content .medit-list-mode-style-innerTitle{ padding: 0 5px; font-size:12px; margin:10px; border-left: 3px solid #f96; } #medit-settingPage-content input[type=text] { width:100%; box-sizing:border-box; padding:0 10px; height:40px; line-height:40px; margin-top:15px; border:1px solid #eee; border-left:0; border-right:0; outline:none; margin-bottom:15px; font-size:12px; } #medit-settingPage-content .medit-settingPage-image-dataInfo { } #medit-settingPage-content .medit-settingPage-image-dataInfo span { display:block; font-size:12px; padding:15px; } #medit-settingPage-content .medit-settingPage-image-dataInfo input[type=text] { display:block; margin:0; } #medit-settingPage-content label{ display:block; margin-left:10px; } #medit-settingPage-content input[type=radio], #medit-settingPage-content input[type=checkbox] { display:none; } #medit-settingPage-content input[type=radio] + span, #medit-settingPage-content input[type=checkbox] + span { position:relative; display:inline-block; height:24px; line-height:24px; padding-left:30px; font-size:12px; } #medit-settingPage-content input[type=radio] + span:before, #medit-settingPage-content input[type=checkbox] + span:before{ content:' '; position:absolute; left:0; top:4px; width:16px; height:16px; background:#fff; border-radius:100%; border:2px solid #9cf; } #medit-settingPage-content input[type=radio]:checked + span:before, #medit-settingPage-content input[type=checkbox]:checked + span:before { background:#39f url("./images/ok.png") no-repeat center center; background-size: 16px; border:2px solid #39f; } #medit-settingPage-button{ height:36px; margin-top:15px; } #medit-settingPage-button-ok { display: block; font-style:normal; height:36px; line-height:36px; text-align:center; color:#fff; overflow:hidden; background:#69f; } #medit-settingPage-button-cancel { background:#69f url("./images/close2.png") no-repeat center center; background-size: 24px; position:absolute; top:-15px; right:-15px; width:30px; height:30px; border-radius:50%; } .medit-image{ position:relative; } img:after { content:" "; position:absolute; width:200px; height:200px; right:0; bottom:0; color:#fff; background:#333; } #medit [data-meditMode=image] { background-color:#e5e5e5; } #medit-image-upload-select-btn { width:70%; overflow:hidden; white-space:nowrap; padding: 0 5%; margin:20px auto; height:32px; line-height:32px; color:#fff; background:#3c9; border-radius:3px; text-align:center; } #medit-image-upload-file { position:absolute; left:-500px; width:200px; } #medit-settingPage-content-img-uploading{ padding: 20px 10px; line-height:24px; } #medit-settingPage-content-img-uploading-progress { display:block; height:4px; background:#c33; border-radius:2px; margin:10px 0; } #medit ul, #medit ol { position:relative; padding: 5px 0; margin:5px 0; border-left:20px solid #eee; min-height:30px; } #medit ul:before, #medit ol:before { content: "Li st"; position:absolute; height:12px; line-height:12px; left:-17px; color:#333; width:15px; font-size: 10px; top:2px; } .medit-list { background:#ddd; } #medit li { position:relative; margin:0; padding:0; list-style:none; padding:5px; min-height:20px; margin:3px; border-left:20px solid #eee; } #medit li:before{ content: "li"; position:absolute; height:20px; line-height:20px; left:-20px; color:#333; text-align:center; width:20px; font-size: 10px; top:0px; } ================================================ FILE: src/medit.js ================================================ require("./medit.css"); var obj = window; var meditToolImage = obj.meditToolImage || "./";// 工具条图片位置 var meditId = null; var container = []; var regNodeId = /medit\-(\d+)\-(\d+)$/; var regContent = /\s(class|id|contenteditable)(=".*?")?/g; // 获得内容时去除id,class和可编辑状态 var regIsNotContentEmpty = /^<.*?>$/; // 获得内容时检测是否是纯文本 var regNormalStyle = /(font\-style\s*:\s*normal\s*;)|(font\-weight\s*:\s*normal\s*;)|(color:\s*rgb\(0,\s*0,\s*0\);)|(\s*)/ig; // 正常的样式需要剔除 var selectTextReg = /(.*?)<\/span>/i; var isToolMove = false; var toolBarCatch = null; var isContainMove = false; var mainTouchPoint = {}; var nowNode = null; // 当前选择的可编辑结点 var nodeFocusTimeout = null; // nodeFocus延时 var globalSelectionContent = null; // 缓存长按选择区域 var globalSelectionHandle = null; var commonImageType = { "jpeg":"image/jpeg", "jpg":"image/jpeg", "gif":"image/gif", "png":"image/png", "bmp":"image/bmp" } var getNodeById = function(id){ return document.getElementById(id); } var fun_deep_clone = function (parent, child) { child = child || {}; for(var i in parent) { if(parent.hasOwnProperty(i)) { if(typeof parent[i] === "object") { child[i] = (Object.prototype.toString.call(parent[i]) === "[object Array]") ? [] : {}; fun_deep_clone(parent[i], child[i]); } else { child[i] = parent[i]; } } } return child; } var returnButtonHtml = function(from){ return ' '; } var toArray = function(obj){ return [].slice.call(obj); } var getXhr = function(){ if(window.XMLHttpRequest){ var xhr=new XMLHttpRequest(); }else{ try{ var xhr=new ActiveXObject("Msxml2.XMLHTTP"); }catch(e){ try{ var xhr=new ActiveXObject("Microsoft.XMLHTTP"); }catch(e){ throw new TypeError('Unsupport XMLHttpRequest'); } } } return xhr; } var mainButton = ["addLeft","delete","setting","mode","ok","addRight"]; var nowMode = "text"; /* mode 属性: icon: [String] 类型选择icon url isMerge: [Bollean] 是否开启相同内容自动合并 notDisplay: [Bollean] 在选择模式的时候不显示, emptyNotDelete: [Bollean] 如果当前块只存在一个子节点并且这个子节点要删除的时候是否开启递归删除 doWhat: [Function] 转换到此类型时会自动做哪些转换 focus:[Function] 点击或此模块获取焦点时自动触发的函数 blur:[Function] 此模块失去焦点时自动触发的函数 empty: [Function] 什么时候当前模块为空 selecting:[Function] 选择当前模块并且手指在屏幕上移动时触发的操作 selected:[Function] 手指移动结束执行的操作 setting: [Array(Object)] 当前模块可以进行哪些操作 -- name: [String] 操作名称 -- icon: [String] 操作按钮icon url -- doWhat: [Function/Array] 点击此操作按钮执行什么,或者是存在更深层次操作 */ var nativeMode = ["text", "br", "link", "image", "list", "li"]; var mode = { "text": { icon: meditToolImage + 'images/mode/text.png', doWhat: function(node){ mode[node.getAttribute("data-meditmode")].blur(node); var temNode = document.createElement("span"); temNode.setAttribute("data-medit", "true"); temNode.setAttribute("data-meditmode", "text"); node.parentNode.insertBefore(temNode,node); node.parentNode.removeChild(node); mode["text"].focus(temNode); nodeFocus(temNode); nowNode = temNode; toolBarModeSetting("text",mode["text"].setting); container[meditId].updateId(); nowMode = "text"; }, isMerge: true, focus:function(node) { node.setAttribute("contentEditable","true"); node.setAttribute("class","medit-editing"); }, blur:function(node) { node.innerHTML = node.innerHTML.replace(selectTextReg,"$1"); node.removeAttribute("contentEditable"); node.removeAttribute("class"); }, empty:function(node) { return node.innerHTML == ""; }, setting:[ { name:"bold", icon: meditToolImage + "images/text/bold.png", doWhat:function(node){ var style = node.getAttribute("style"); var reg = /font\-weight\s*:\s*(.*?)\s*;/i; if(reg.test(style)){ var regRes = reg.exec(style); if(regRes[1] == "bold"){ node.style.fontWeight = "normal"; return; } } node.style.fontWeight = "bold"; } }, { name:"italic", icon: meditToolImage + "images/text/italic.png", doWhat:function(node){ var style = node.getAttribute("style"); var reg = /font\-style\s*:\s*(.*?)\s*;/i; if(reg.test(style)){ var regRes = reg.exec(style); if(regRes[1] == "italic"){ node.style.fontStyle = "normal"; return; } } node.style.fontStyle = "italic"; } }, { name:"underline", icon: meditToolImage + "images/text/underline.png", doWhat:function(node){ var style = node.getAttribute("style"); var reg = /text\-decoration\s*:\s*(.*?)\s*;/i; if(reg.test(style)){ var regRes = reg.exec(style); if(regRes[1] == "underline"){ node.style.textDecoration = "none"; return; } } node.style.textDecoration = "underline"; } }, { name:"size", icon: meditToolImage + "images/text/size.png", doWhat:[ { name: "fontSizeBig", icon: meditToolImage + "images/text/sizeBigger.png", doWhat: function(node) { var style = node.getAttribute("style"); var displaySize = getNodeById("medit-tool-button-text-setting-3-doWhat-1"); var reg = /font\-size\s*:\s*(\d*)\s*(?:.*?)\s*;/i; var size = 15; if(reg.test(style)){ var regRes = reg.exec(style); size = ++regRes[1]; } if(displaySize){ displaySize.innerHTML = size; } node.style.fontSize = size + "px"; } }, { name: "fontSizeValue", icon: "", defaultValue: "size" }, { name: "fontSizeSmall", icon: meditToolImage + "images/text/sizeSmaller.png", doWhat: function(node) { var style = node.getAttribute("style"); var displaySize = getNodeById("medit-tool-button-text-setting-3-doWhat-1"); var reg = /font\-size\s*:\s*(\d*)\s*(?:.*?)\s*;/i; var size = 13; if(reg.test(style)){ var regRes = reg.exec(style); size = --regRes[1]; if(size<12)size=12; } if(displaySize){ displaySize.innerHTML = size; } node.style.fontSize = size + "px"; } } ] }, { name: "color", icon: meditToolImage + "images/text/color.png", doWhat:[ { name: "black", icon: meditToolImage + "images/text/colorBlack.png", doWhat:function(node){ node.style.color = "#000000"; } }, { name: "red", icon: meditToolImage + "images/text/colorRed.png", doWhat:function(node){ node.style.color = "#ff0000"; } }, { name: "green", icon: meditToolImage + "images/text/colorGreen.png", doWhat:function(node){ node.style.color = "#00ff00"; } }, { name: "blue", icon: meditToolImage + "images/text/colorBlue.png", doWhat:function(node){ node.style.color = "#0000ff"; } }, { name: "yellow", icon: meditToolImage + "images/text/colorYellow.png", doWhat:function(node){ node.style.color = "#ffff00"; } }, { name: "pink", icon: meditToolImage + "images/text/colorPink.png", doWhat:function(node){ node.style.color = "#ff00ff"; } } ] } ], selecting : function(node, isAdd){ var selectReg = selectTextReg; if(selectReg.test(node.innerHTML)){ var regRes = node.innerHTML.split(selectReg); if(isAdd){ if(regRes[1].length>1){ if(/^ |^<(.*?)\s(.*?)>(.*?)<\/(.*?)>/.test(regRes[1])){ var innerTagReg = /^ |^<(.*?)\s(.*?)>(.*?)<\/(.*?)>/.exec(regRes[1]); var replaceHTML = innerTagReg[0]; regRes[0] += replaceHTML; regRes[1] = regRes[1].replace(/^ |^<(.*?)\s(.*?)>(.*?)<\/(.*?)>/, ""); }else{ regRes[0] = regRes[0] + regRes[1].slice(0, 1); regRes[1] = regRes[1].slice(1); } } }else{ if(regRes[1].length>1){ if(/<(.*?)\s(.*?)>(.*?)<\/(.*?)>$| $/.test(regRes[1])){ var innerTagReg = /<(.*?)\s(.*?)>(.*?)<\/(.*?)>$| $/.exec(regRes[1]); var replaceHTML = innerTagReg[0]; regRes[2] = replaceHTML + regRes[2]; regRes[1] = regRes[1].replace(/<(.*?)\s(.*?)>(.*?)<\/(.*?)>$| $/, ""); }else{ regRes[2] = regRes[1].slice(-1) + regRes[2]; regRes[1] = regRes[1].slice(0, -1); } } } node.innerHTML = regRes[0] + '' + regRes[1] + '' + regRes[2]; }else{ node.innerHTML = '' + node.innerHTML + ''; } }, selected: function(thisNode){ var selectReg = selectTextReg; var newNode; var contain = container[meditId]; if(selectReg.test(thisNode.innerHTML)) { var thisId = regNodeId.exec(thisNode.getAttribute("id"))[1]; var regRes = thisNode.innerHTML.split(selectReg); var style = thisNode.getAttribute("style"); if(regRes[0]!==''){ var spanPre =document.createElement("span"); spanPre.setAttribute("data-medit","true"); spanPre.setAttribute("data-meditMode","text"); spanPre.setAttribute("id","medit-" + thisId + "-" + meditId ); spanPre.setAttribute("style",style); spanPre.innerHTML = regRes[0]; thisNode.parentNode.insertBefore(spanPre, thisNode); thisId++; } if(regRes[2]!==''){ thisNode.innerHTML = regRes[2]; var span =document.createElement("span"); span.setAttribute("data-medit","true"); span.setAttribute("data-meditMode","text"); span.setAttribute("id","medit-" + thisId + "-" + meditId ); span.setAttribute("contentEditable","true"); span.setAttribute("class","medit-editing"); span.setAttribute("style",style); span.innerHTML = regRes[1]; this.blur(thisNode); thisNode.parentNode.insertBefore(span, thisNode); newNode = thisNode.previousSibling; }else{ thisNode.innerHTML = regRes[1]; newNode = thisNode; } contain.updateId(); contain.nowNodeId = thisId; nodeFocus(newNode); }else{ newNode = thisNode; } return newNode; } }, "br":{ icon: meditToolImage + 'images/mode/br.png', doWhat: function(node) { mode[node.getAttribute("data-meditmode")].blur(node); var temNode = document.createElement("span"); temNode.style.display = "block"; temNode.innerHTML = " "; temNode.setAttribute("data-medit", "true"); temNode.setAttribute("data-meditmode", "br"); node.parentNode.insertBefore(temNode,node); node.parentNode.removeChild(node); mode["br"].focus(temNode); nodeFocus(temNode); nowNode = temNode; toolBarModeSetting("br",[]); container[meditId].updateId(); nowMode = "br"; }, focus:function(node) { node.style.backgroundColor = "#e5e5e5"; }, blur:function(node) { node.style.backgroundColor = ""; } }, "link":{ icon: meditToolImage + 'images/mode/link.png', doWhat: function(node) { var parent = node; while(parent.parentNode.getAttribute("data-medit")){ parent = parent.parentNode; } if(parent.nodeName.toLowerCase() == "a"){ return; } var linkNode = document.createElement("a"); linkNode.setAttribute("data-medit","true"); linkNode.setAttribute("data-meditmode","link"); mode[node.getAttribute("data-meditmode")].blur(node); node.parentNode.insertBefore(linkNode,node); node.parentNode.removeChild(node); linkNode.appendChild(node); toolBarModeSetting("link",mode['link'].setting); mode["link"].focus(linkNode); container[meditId].updateId(); container[meditId].nowNodeId = regNodeId.exec(linkNode.getAttribute("id"))[1]; nowMode = "link"; clearTimeout(nodeFocusTimeout); nowNode = linkNode; }, setting: [ { name: "setting", icon: meditToolImage + "images/link/setting.png", doWhat: function(node){ var href = node.getAttribute("data-meditHref"); var hrefHtml = ''; if(href){ hrefHtml = ' value="'+href+'"'; } var target = node.getAttribute("target"); if(target && target != "_blank"){ target = ""; }else{ target = " checked"; } var html = ''; settingPageDisplay('超链接设置 Link Setting',html,function(){ var href = getNodeById("medit-settingPage-input-link"); if(href){ node.setAttribute("data-meditHref", href.value); } var checkbox = getNodeById("medit-settingPage-check-link"); if(checkbox.checked){ node.setAttribute("target", "_blank"); }else{ node.removeAttribute("target"); } settingPage.style.display = "none"; }); } }, { name: "cancellink", icon: meditToolImage + "images/link/cancel-link.png", doWhat: function(node){ var childs = toArray(node.children); var temNode = childs[0]; var temMode = temNode.getAttribute("data-meditmode"); childs.forEach(function(child){ node.parentNode.insertBefore(child, node); }); node.parentNode.removeChild(node); container[meditId].updateId(); toolBarModeSetting(temMode,mode[temMode].setting); mode[temMode].focus(temNode); nowNode = temNode; nodeFocus(temNode); } } ], focus: function(node){ getNodeById("medit-tool-button-mode").style.display = "none"; node.setAttribute("class","medit-link"); }, blur:function(node){ node.removeAttribute("class"); } }, "image":{ icon: meditToolImage + "images/mode/image.png", doWhat:function(node){ mode[node.getAttribute("data-meditmode")].blur(node); var temNode = document.createElement("img"); temNode.setAttribute("data-medit", "true"); temNode.setAttribute("data-meditmode", "image"); temNode.setAttribute("src", mode["image"].icon); temNode.setAttribute("width","32"); temNode.setAttribute("height","32"); node.parentNode.insertBefore(temNode,node); node.parentNode.removeChild(node); mode["image"].focus(temNode); nodeFocus(temNode); nowNode = temNode; toolBarModeSetting("image",mode["image"].setting); container[meditId].updateId(); nowMode = "image"; }, focus: function(node){ node.setAttribute("class","medit-image"); }, blur:function(node){ node.removeAttribute("class"); }, setting:[ { name:"setting", icon: meditToolImage + "images/image/setting.png", doWhat: function(node) { var width = node.getAttribute("width"); var height = node.getAttribute("height"); var address = node.getAttribute("src"); var html = '
宽度 Width:高度 Height:图像地址 Address:
'; settingPageDisplay('图像设置 Image Setting',html,function(){ var width = getNodeById("medit-settingPage-image-width").value; if(width && width>0){ node.setAttribute("width",width); } var height = getNodeById("medit-settingPage-image-height").value; if(height && height>0){ node.setAttribute("height",height); } var newAddress = getNodeById("medit-settingPage-image-address").value; if(address != newAddress){ // 传入网络图片需要进行宽高转换 getNodeById("medit-settingPage-button").style.display = "none"; getNodeById("medit-settingPage-content").innerHTML = "Loading Image..."; var newImg = new Image(); newImg.src = newAddress; newImg.onload = function(){ var scale = newImg.width/ newImg.height; if(newImg.width>100){ newImg.width = 100; newImg.height = 100/scale; } settingPage.style.display = "none"; node.setAttribute("src",newAddress); node.setAttribute("width",newImg.width); node.setAttribute("height",newImg.height); } }else{ settingPage.style.display = "none"; } }); } }, { name: "biger", icon: meditToolImage + "images/image/biger.png", doWhat: function(node){ var width = node.getAttribute("width"); var height = node.getAttribute("height"); node.setAttribute("width",Math.ceil(width*1.1)); node.setAttribute("height",Math.ceil(height*1.1)); } }, { name: "smaller", icon: meditToolImage + "images/image/smaller.png", doWhat: function(node){ var width = node.getAttribute("width"); var height = node.getAttribute("height"); node.setAttribute("width",Math.ceil(width/1.1)); node.setAttribute("height",Math.ceil(height/1.1)); } }, { name: "upload", icon: meditToolImage + "images/image/upload.png", doWhat: function(node){ var config = container[meditId].imageUpload; var html = '
选择图片 Select Image
'; settingPageDisplay('图像上传 Image upload',html,function(){ var files = getNodeById("medit-image-upload-file"); if(files.files.length <=0){ return; } var file = files.files[0]; var size = file.size; var name = file.name; var type = file.type; var config = container[meditId].imageUpload; var ext = {}; config.ext && config.ext.forEach(function(v){ ext[commonImageType[v]] = true; }); if(config.size == 0 || config.size>=size){ if(!config.ext || ext[type]){ var http = getXhr(); var form = new FormData(getNodeById("medit-image-upload-form")); getNodeById("medit-settingPage-button").style.display = "none"; getNodeById("medit-settingPage-content").innerHTML = '
图片上传中 Image uploading...
'; http.upload.onprogress = function(v){ var progress = Math.floor(100*v.loaded/v.total) + "%"; var success = ""; if(progress == "100%") success ="上传成功,请稍后...
upload success,please waiting..."; getNodeById("medit-settingPage-content").innerHTML = '
图片上传中 Image uploading...'+ progress +'
'+success+'
'; } Object.keys(config.params).map(function(key) { form.append(key, config.params[key]); }); http.open("POST",config.path); http.send(form); http.onreadystatechange = function(){ if(http.readyState === 4){ if(http.status === 200 || http.status>200 && http.status<400){ var res = JSON.parse(http.responseText); if(res.code == "success"){ config.success(res); var scale = res.data.width/ res.data.height; if(res.data.width>100){ res.data.width = 100; res.data.height = 100/scale; } settingPage.style.display = "none"; node.setAttribute("src",res.data.url); node.setAttribute("width",res.data.width); node.setAttribute("height",res.data.height); } }else{ config.error("http status " + http.status); } } } }else{ config.error("image type limit "+config.ext.join(",")); } }else{ config.error("image size limit "+config.size); } }); getNodeById("medit-settingPage-button-ok").innerHTML = "上传 Upload"; var btn = getNodeById("medit-image-upload-select-btn"); var fileInput = getNodeById("medit-image-upload-file"); btn.onclick = function(){ fileInput.click(); } fileInput.onchange = function(e){ e = e || window.event; var files=e.target.files||e.srcElement.files; var file = files[0]; var size = file.size; var name = file.name; var type = file.type; var ext = {}; config.ext && config.ext.forEach(function(v){ ext[commonImageType[v]] = true; }); if (config.size == 0 || config.size>=size){ if (!config.ext || ext[type]){ btn.innerHTML = name; } else { config.error("image type limit "+config.ext.join(",")); } } else { config.error("image size limit "+config.size); } } } }, { name: 'verticalAlign', icon: meditToolImage + 'images/image/vertical-align.png', doWhat:[ { name: 'top', icon: meditToolImage + 'images/image/top-align.png', doWhat:function(node){ node.style.verticalAlign = "top"; } }, { name: 'middle', icon: meditToolImage + 'images/image/middle-align.png', doWhat:function(node){ node.style.verticalAlign = "middle"; } }, { name: 'bottom', icon: meditToolImage + 'images/image/bottom-align.png', doWhat:function(node){ node.style.verticalAlign = "bottom"; } } ] } ] }, "list":{ emptyNotDelete: true, icon: meditToolImage + "images/mode/list.png", focus:function(node){ node.setAttribute("class","medit-list"); getNodeById("medit-tool-button-mode").style.display = "none"; }, blur:function(node){ node.removeAttribute("class"); }, doWhat:function(node){ mode[node.getAttribute("data-meditmode")].blur(node); var temNode = document.createElement("ul"); temNode.setAttribute("data-medit", "true"); temNode.setAttribute("data-meditmode", "list"); temNode.setAttribute("type", "disc"); node.parentNode.insertBefore(temNode,node); node.parentNode.removeChild(node); nodeFocus(temNode); nowNode = temNode; toolBarModeSetting("list",mode["list"].setting); mode["list"].focus(temNode); container[meditId].updateId(); nowMode = "list"; }, setting:[ { name:"setting", icon: meditToolImage + "images/list/setting.png", doWhat:function(node){ var type = node.getAttribute("type").toLowerCase(); var nodeType = node.nodeName.toLowerCase(); var html = '
无序列表 Unorder List:
'+ ''+ ''+ ''+ '
有序列表 Order List:
'+ ''+ ''+ ''+ ''+ ''; settingPageDisplay('列表类型 List Mode',html,function(){ var radio = getNodeById("medit-settingPage-content").getElementsByTagName("input"); var input = null; for(var i = 0; i'+title+'
':''); html.push(''); html.push('
'+content+'
'); html.push('
确定 Ok
'); getNodeById("medit-settingPage-main").innerHTML = html.join(""); settingPageOk = okCallBack; settingPage.style.display = "block"; } var mainDo = function(degree, type, target) { var contain = container[meditId]; var thisNode = getNodeById("medit-" + contain.nowNodeId + "-" + meditId); // 这里有个已经修复的bug,比如在超链接中,是在当前结点外部包了一层,那么thisNode需要更新到外层结点 nowMode = thisNode.getAttribute("data-meditMode"); if( mode[nowMode].selected ){ if(globalSelectionContent && globalSelectionContent.node == thisNode && globalSelectionContent.end!=globalSelectionContent.start ){ // 原生自带文本选择处理 var html = globalSelectionContent.node.innerHTML.split(""); html.splice(globalSelectionContent.end,0,''); html.splice(globalSelectionContent.start,0,''); globalSelectionContent.node.innerHTML = html.join(""); } } if(degree == 1) { switch(type){ case 'delete': if(mode[nowMode].blur){ mode[nowMode].blur(thisNode); } while(thisNode.parentNode.getAttribute("data-medit") && thisNode.parentNode.children.length === 1 && !mode[thisNode.parentNode.getAttribute("data-meditMode")].emptyNotDelete){ thisNode = thisNode.parentNode; } thisNode.parentNode.removeChild(thisNode); contain.updateId(); toolBarHidden(); break; case 'ok': if(mode[nowMode].blur){ mode[nowMode].blur(thisNode); } if(mode[nowMode].empty && mode[nowMode].empty(thisNode)) thisNode.parentNode.removeChild(thisNode); toolBarHidden(); contain.nodeCount++; thisNode = mergeSimilarNextNode(thisNode); mergeSimilarPreNode(thisNode); break; case 'addLeft': if(mode[nowMode].empty && mode[nowMode].empty(thisNode)){ return; } if(mode[nowMode].blur){ mode[nowMode].blur(thisNode); } contain.createSpan(contain.nowNodeId, thisNode, false, true); break; case 'addRight': if(mode[nowMode].empty && mode[nowMode].empty(thisNode)){ return; } if(mode[nowMode].blur){ mode[nowMode].blur(thisNode); } contain.createSpan(contain.nowNodeId, thisNode, true, true); break; case 'mode': var toolBarRes = []; toolBarRes.push(returnButtonHtml(nowMode + "-setting-1")); for(var modeType in mode){ if(mode.hasOwnProperty(modeType) && modeType != nowMode && !mode[modeType].notDisplay){ var style = mode[modeType].icon?' style="background:#428bca url('+ mode[modeType].icon+') no-repeat center center;background-size: 24px;"':''; toolBarRes.push(' '); } } toolBar.innerHTML = toolBarRes.join(""); break; case 'return': var path = target.getAttribute("id").replace("medit-tool-button-",""); var nodePath = path.split("-"); var doWhat = mode; var pathMode = null; nodePath.pop(); path = path.replace(/\-[a-zA-Z]*\-\d*$/,''); while(pathMode = nodePath.shift()){ doWhat = doWhat[pathMode]; } toolBarModeSetting(path, doWhat); break; } }else{ if( mode[nowMode].selected ){ thisNode = mode[nowMode].selected(thisNode); } var pathRes = target.getAttribute("id").replace("medit-tool-button-",""); var pathArr = pathRes.split("-"); var pathMode = null; var doWhat = mode; while(pathMode = pathArr.shift()){ doWhat = doWhat[pathMode]; } doWhat = doWhat.doWhat; if(isType(doWhat, "array")){ toolBarModeSetting(pathRes, doWhat); }else{ doWhat(thisNode); } } if(!contain.node.children.length) contain.node.innerHTML = contain.preHTML || "Medit"; } var toolBarModeSetting = function(path, list){ var pathRes = path.split("-"); var toolBarRes = []; if(pathRes.length === 1){ mainButton.forEach(function(v,index){ if(v === "setting"){ if(list){ list.forEach(function(listv, listIndex){ var defaultValue = listv.defaultValue || " "; var style = listv.icon?' style="background:#428bca url('+listv.icon+') no-repeat center center;background-size: 24px;"':''; toolBarRes.push(''+defaultValue+''); }); } }else{ toolBarRes.push(' '); } }); }else{ toolBarRes.push(returnButtonHtml(path)); if(!!list.length){ list.forEach(function(listv, listIndex){ var defaultValue = listv.defaultValue || " "; var style = listv.icon?' style="background:#428bca url('+listv.icon+') no-repeat center center;background-size: 24px;"':' style="background:#428bca;"'; toolBarRes.push(''+defaultValue+''); }); } } toolBar.innerHTML = toolBarRes.join(""); } var toolBarDisplay = function() { if(toolBarCatch) toolBar.innerHTML = toolBarCatch; toolBar.style.display = "block"; } var toolBarHidden = function() { nowNode = null; toolBar.style.display = "none"; } var toBr = function(node){ node.setAttribute("style","display:block"); node.setAttribute("data-meditMode","br"); node.setAttribute("contentEditable","false"); node.setAttribute("class",""); node.innerHTML = " "; } var nodeFocus = function(node){ // 使模块自动获取焦点 使用了很多方法,最后发现这个方法是在移动端最好的 nodeFocusTimeout = setTimeout(function() { node.focus(); container[meditId].nowNodeId = regNodeId.exec(node.getAttribute("id"))[1]; }, 10); } var selectModeContent = function(isAdd){ if(nowNode){ var nodeMode = nowNode.getAttribute("data-meditmode"); var nodeModeObj = mode[nodeMode]; if(nodeModeObj.selecting){ nodeModeObj.selecting(nowNode, isAdd); } } } var medit = function(node, toolBarContainer) { if(!(this instanceof medit)) return new medit(node, toolBarContainer); if(!node || node.nodeType != 1)return false; if(toolBarContainer && toolBarContainer.nodeType == 1) { toolBar.parentNode.removeChild(toolBar); toolBar.setAttribute("class", "medit-tool-inner"); toolBarContainer.appendChild(toolBar); } this.node = node; this.nodeCount = 0; // 容器所有子元素数目 this.nowNodeId = 0; // 容器当前子元素ID this.getContent = medit.prototype.getContent; this.node.setAttribute("data-meditId",container.length); this.imageUpload = { // 默认图片上传设置,由于是文件上传,所以在跨域方法仅支持CORS path:'https://sm.ms/api/upload', params: {}, name:'smfile', size:0, timeout:0, ext:["jpg","jpeg","png","gif","bmp"], success:function(){}, error:function(){} } container.push(this); gevent(this.node, ["touchstart"], function(e){ mainTouchPoint = e.targetTouches[0]; if(window.getSelection){ // 原生手势长按选择 if(globalSelectionHandle)clearTimeout(globalSelectionHandle); var selectionHandle = window.getSelection(); var selectionCheckTimeout = function(){ if(selectionHandle && selectionHandle.anchorNode && selectionHandle.anchorNode == selectionHandle.focusNode && selectionHandle.anchorNode.parentNode==nowNode){ globalSelectionContent = { handle: selectionHandle, node: selectionHandle.anchorNode.parentNode, start: selectionHandle.anchorOffsetselectionHandle.focusOffset?selectionHandle.anchorOffset:selectionHandle.focusOffset } globalSelectionHandle = setTimeout(selectionCheckTimeout, 100); }else{ if(globalSelectionContent){ globalSelectionContent.handle.removeAllRanges(); globalSelectionContent = null; } if(globalSelectionHandle)clearTimeout(globalSelectionHandle); } } selectionCheckTimeout(); } }); gevent(this.node, ["touchmove"], function(e){ e = e || window.event; var distance = e.targetTouches[0].clientX - mainTouchPoint.clientX; if(Math.abs(distance) > 50){ var isAdd = distance > 0? true: false; selectModeContent(isAdd); mainTouchPoint = e.targetTouches[0]; } isContainMove = true; }); gevent(this.node, ["touchend"], this.editContainFocus); gevent(this.node, ["keydown"], function(e){ e = e || window.event; if(e.keyCode == 13){ e.preventDefault(); var contain = container[meditId]; var thisNode = getNodeById("medit-" + contain.nowNodeId + "-" + meditId); nowMode = thisNode.getAttribute("data-meditMode"); if(mode[nowMode].blur){ var newNode = mode[nowMode].blur(thisNode); // 如果当前按下回车按钮的块需要对内容进行处理,或者是结点进行改变了 if(newNode){ if(newNode.exit) return; // 不需要继续创建新的结点了 thisNode = newNode; } } contain.createSpan(contain.nowNodeId, thisNode, true, true); var brNode = getNodeById("medit-" + contain.nowNodeId + "-" + meditId); toBr(brNode); contain.createSpan(contain.nowNodeId, brNode, true, true); } }); } var returnNextNodeId = function(node){ var child = node.children; if(child.length) return returnNextNodeId(child[child.length-1]); return Number(regNodeId.exec(node.getAttribute("id"))[1])+1; } medit.prototype.createSpan = function(nodeId, fromNode, isAfter, isAutoFocus){ // 因为在内部创建span的时候不会自动focus,需要调用一下focus方法 var span =document.createElement("span"); span.setAttribute("data-medit","true"); span.setAttribute("data-meditMode","text"); span.setAttribute("id","medit-" + nodeId + "-" + meditId ); span.setAttribute("contentEditable","true"); span.setAttribute("class","medit-editing"); if(fromNode){ if(!isAfter){ this.nowNodeId = nodeId; fromNode.parentNode.insertBefore(span, fromNode); }else{ this.nowNodeId = returnNextNodeId(fromNode); if(!fromNode.nextSibling){ fromNode.parentNode.appendChild(span); }else{ fromNode.parentNode.insertBefore(span, fromNode.nextSibling); } } this.updateId(); }else{ this.node.appendChild(span); } var editor = getNodeById("medit-" + this.nowNodeId + "-" + meditId); if(nowNode){ // 当创建新的标签的时候把之前/之后的相似结点进行合并 if(!isAfter){ mergeSimilarNextNode(nowNode, true); }else{ mergeSimilarPreNode(nowNode, true); } } this.nowNodeId = Number(regNodeId.exec(editor.id)[1]); // 因为合并结点之后要重新更新id, 所以需要重新获取当前结点ID nowNode = editor; toolBarModeSetting("text", mode["text"].setting); if(isAutoFocus){ nodeFocus(editor); } } medit.prototype.updateId = function(nodeId, list) { var child = list || toArray(this.node.children); var index = nodeId || 0; child.forEach(function(v){ if(v.getAttribute("href")){ // 防止超链接在内部触发 v.setAttribute("data-meditHref",v.getAttribute("href")); v.removeAttribute("href"); } v.setAttribute("id","medit-" + (index++) + "-" + meditId ); var secondChild = toArray(v.children); if(secondChild.length){ index = container[meditId].updateId(index, secondChild); } }); return index; } medit.prototype.getContent = function(isEdit){ isEdit = isEdit || false; if(!isEdit && toolBar.style.display == "block"){ mainDo(1, "ok"); } var html = this.node.innerHTML; if(regIsNotContentEmpty.test(html)){ html = html.replace(/\sdata\-meditHref="(.*?)"/ig," href=\"$1\""); html = html.replace(selectTextReg,"$1"); return html.replace(regContent, " "); } return ""; } medit.prototype.autoSave = function(appId, callBack){// 自动保存 callBack(data, timeStamp),自动恢复已保存数据 if(window.localStorage){ this.appId = appId; var oldData = localStorage.getItem("meditAutosave"+appId); // repair bug: id is need not exits '-' var temData = this.getContent(true); if(!regIsNotContentEmpty.test(temData) && oldData){ meditId = this.node.getAttribute("data-meditid"); this.node.innerHTML = oldData; this.updateId(); } clearInterval(this.autoSaveInterval); var _this = this; this.autoSaveInterval = setInterval(function(){ var nowData = _this.getContent(true); localStorage.setItem("meditAutosave"+appId,nowData); callBack(nowData, (new Date())-0); },1000); } } medit.prototype.image = medit.prototype.imageUpload = function(option){ // 图片上传设置 var meditId = this.node.getAttribute("data-meditid") - 0; var contain = container[meditId]; for(var item in option){ if(contain.imageUpload[item] != null){ contain.imageUpload[item] = option[item]; } } } medit.prototype.clear = function(data){ data = data||""; clearInterval(this.autoSaveInterval); this.node.innerHTML = data; if(this.appId!=null){ localStorage.removeItem("meditAutosave"+this.appId); } } medit.prototype.editContainFocus = function(e) { e = e || window.event; var target = e.target || e.srcElement; if(isContainMove){ e.preventDefault(); e.stopPropagation(); isContainMove = false; return; } toolBarDisplay(); if(meditId != null) { // 在已经选择某个区块的时候选择其它的,会先调用这个区块的blur var temObj = container[meditId]; var temNode = getNodeById("medit-" + temObj.nowNodeId + "-" + meditId); if(temNode){ var temNodeMode = temNode.getAttribute("data-meditMode"); if(mode[temNodeMode].empty && mode[temNodeMode].empty(temNode) && temNode != target){ temNode.parentNode.removeChild(temNode); temObj.updateId(); }else{ if(mode[temNodeMode].blur){ mode[temNodeMode].blur(temNode); } } } } var type = target.getAttribute("data-medit"); if(!type && target.getAttribute("data-meditId")){ // target is container meditId = target.getAttribute("data-meditId"); // 全局存贮当前medit容器ID var meditObj = container[ meditId]; var child = target.children; if(!child.length){ // 如果点击了容器发现没有结点,那么就保存原有内容,并且创建新的span meditObj.preHTML = target.innerHTML; target.innerHTML = ""; meditObj.createSpan(0); target = false; }else{ var temTarget = child[child.length-1]; var temTargetMode = temTarget.getAttribute("data-meditMode"); if(!mode[temTargetMode].empty || !mode[temTargetMode].empty(temTarget)){ meditObj.createSpan(child.length-1,temTarget, true); target = false; }else{ target = temTarget; nowNode = target; } } }else{ // target is 内部包含结点 while(!target.getAttribute("data-medit")){ target = target.parentNode; } nowNode = target; } if(target){ if(!target.id) { var parentNode = target.parentNode; while(!parentNode.getAttribute("data-meditid")){ parentNode = parentNode.parentNode; } meditId = parentNode.getAttribute("data-meditid"); container[meditId].updateId(); } var idExecRes = regNodeId.exec(target.id); meditId = Number(idExecRes[2]); var meditObj = container[ meditId]; meditObj.nowNodeId = Number(idExecRes[1]); var meditNodeMode = target.getAttribute("data-meditMode"); toolBarModeSetting(meditNodeMode, mode[meditNodeMode].setting); if(mode[meditNodeMode].focus){ mode[meditNodeMode].focus(target); } } } medit.settingPage = function(title, content, callBack) { settingPageDisplay(title, content, function(){ callBack(); settingPage.style.display = "none"; }) } medit.extend = function(obj) { // 扩展方法 会向doWhat方法中传入当前结点,然后需要返回一个新的结点 if(obj && obj.icon && obj.doWhat && obj.focus && obj.blur && obj.name){ if(!mode[obj.name]){ mode[obj.name] = fun_deep_clone(obj); mode[obj.name].doWhat = function(node){ mode[node.getAttribute("data-meditmode")].blur(node); var newNode = obj.doWhat(node); if(newNode) { newNode.setAttribute("data-meditmode", obj.name); newNode.setAttribute("data-medit", "true"); mode[obj.name].focus(newNode); nodeFocus(newNode); nowNode = newNode; toolBarModeSetting(obj.name,obj.setting||[]); container[meditId].updateId(); nowMode = obj.name; } } }else{ throw new Error(obj.name + ' has already exist!'); } } } medit.nativeSetting = function(execFun){ // 原生自带功能配置接口 for(var i =0; i ================================================ FILE: test/contentEditableFocus.html ================================================ Focus ================================================ FILE: webpack.config.dev.js ================================================ var path = require('path') var webpack = require('webpack') var autoprefixer = require('autoprefixer') var precss = require('precss') var CopyWebpackPlugin = require('copy-webpack-plugin'); module.exports = { entry: { "medit.min": './src/medit.js' }, output: { path: path.join(__dirname, 'build'), filename: '[name].js' }, plugins: [], resolve: { alias: { medit: path.resolve(__dirname, 'src') }, extensions: ['', '.js', '.jsx'] }, module: { loaders: [ { test: /\.jsx?$/, loader: 'babel', include: [ path.resolve(__dirname, 'src') ] }, { test : /\.css$/, loaders: ['style-loader', 'css-loader', 'resolve-url-loader']}, { test: /\.scss$/, loaders: ['style-loader', 'css-loader?modules&localIdentName=[local]-[hash:base64:5]', 'postcss-loader', 'sass-loader'] }, { test: /\.(ttf|eot|woff|woff2|otf|svg)/, loader: 'file-loader?name=./font/[name].[ext]' }, { test: /\.json$/, loader: 'file-loader?name=./json/[name].json' }, { test: /\.(png|jpg|jpeg|gif)$/, loader: 'url-loader?limit=100000&name=./images/[name].[ext]' } ] }, postcss: function () { return [autoprefixer({ browsers: ['> 1%', 'IE 9'] }), precss] }, plugins: [ new CopyWebpackPlugin([{context:'./src/images/', from:'**/*', to:'images'}]) ] }