好久没写随笔了,因为最近比较忙,小公司基本都是一个前端干所有属于和部分不属于前端的事情,所以就没空弄了,即使想分享,也因为没有时间和精力就搁置了。
这周周六日休息,正好时间比较充裕(ps:目前处在单休中。。。),就分析了以下bootstrap-wysiwyg的源码,虽然只有200多行,但是本人js水平还是欠佳,所以大概用了一天的时间(ps:可怜我偶尔的双休还是变成了单休),对bootstrap-wysiwyg的源码进行了解读和部分的注释,在这里放出来,给需要的人,毕竟不是人人都喜欢百度的编辑器的(ps:我就是因为不喜欢百度编辑器的样式和一大堆文件的原因才会去看bootstrap-wysiwyg的源码的),这样,理解了内部构造也好扩展和修改,因为最近的项目图片不能转码提交,而是要上传到淘宝的oss上才返回地址显示在富文本编辑器中,这也是促使我对bootstrap-wysiwyg源码的解读,好了,废话太多了,简易注释的源码在下面:
ps:里面我添加了对富文本编辑器中点击图片的监听事件(这个还是很有用的,比如修改图片大小),或者读到这篇文章的也可以去自己定义接口
同时我也放在了github上,地址是:
https://github.com/woleicom/bootstrap-wysiwyg-notes
如果github被墙,国内可以访问oschina,地址是:
https://git.oschina.net/woleicom/bootstrap-wysiwyg-notes
喜欢的就给星吧,哈哈
1 /* */ 2 /*global jQuery, $, FileReader*/ 3 /*jslint browser:true*/ 4 (function ($) { 5 \'use strict\'; 6 /*转码图片*/ 7 var readFileIntoDataUrl = function (fileInfo) { 8 var loader = $.Deferred(), //jq延迟对象 9 fReader = new FileReader(); 10 fReader.onload = function (e) { 11 loader.resolve(e.target.result); 12 }; 13 fReader.onerror = loader.reject; //拒绝 14 fReader.onprogress = loader.notify; 15 fReader.readAsDataURL(fileInfo); //转码图片 16 return loader.promise(); //返回promise 17 }; 18 /*清空内容*/ 19 $.fn.cleanHtml = function () { 20 var html = $(this).html(); 21 return html && html.replace(/(<br>|\s|<div><br><\/div>| )*$/, \'\'); 22 }; 23 $.fn.wysiwyg = function (userOptions) { 24 var editor = this, //设置ui-jq=\'设置的插件别名的dom元素\'(此句注释可忽略,是针对我的项目结构写的) 25 selectedRange, 26 options, 27 toolbarBtnSelector, 28 //更新工具栏 29 updateToolbar = function () { 30 if (options.activeToolbarClass) { 31 $(options.toolbarSelector).find(toolbarBtnSelector).each(function () { 32 var command = $(this).data(options.commandRole); 33 //判断光标所在位置以确定命令的状态,为真则显示为激活 34 if (document.queryCommandState(command)) { 35 $(this).addClass(options.activeToolbarClass); 36 } else { 37 $(this).removeClass(options.activeToolbarClass); 38 } 39 }); 40 } 41 }, 42 //插入内容 43 execCommand = function (commandWithArgs, valueArg) { 44 var commandArr = commandWithArgs.split(\' \'), 45 command = commandArr.shift(), 46 args = commandArr.join(\' \') + (valueArg || \'\'); 47 document.execCommand(command, 0, args); 48 updateToolbar(); 49 }, 50 //用jquery.hotkeys.js插件监听键盘 51 bindHotkeys = function (hotKeys) { 52 $.each(hotKeys, function (hotkey, command) { 53 editor.keydown(hotkey, function (e) { 54 if (editor.attr(\'contenteditable\') && editor.is(\':visible\')) { 55 e.preventDefault(); 56 e.stopPropagation(); 57 execCommand(command); 58 } 59 }).keyup(hotkey, function (e) { 60 if (editor.attr(\'contenteditable\') && editor.is(\':visible\')) { 61 e.preventDefault(); 62 e.stopPropagation(); 63 } 64 }); 65 }); 66 }, 67 //获取当前range对象 68 getCurrentRange = function () { 69 var sel = window.getSelection(); 70 if (sel.getRangeAt && sel.rangeCount) { 71 return sel.getRangeAt(0); //从当前selection对象中获得一个range对象。 72 } 73 }, 74 //保存 75 saveSelection = function () { 76 selectedRange = getCurrentRange(); 77 }, 78 //恢复 79 restoreSelection = function () { 80 var selection = window.getSelection(); //获取当前既获区,selection是对当前激活选中区(即高亮文本)进行操作 81 if (selectedRange) { 82 try { 83 //移除selection中所有的range对象,执行后anchorNode、focusNode被设置为null,不存在任何被选中的内容。 84 selection.removeAllRanges(); 85 } catch (ex) { 86 document.body.createTextRange().select(); 87 document.selection.empty(); 88 } 89 //将range添加到selection当中,所以一个selection中可以有多个range。 90 //注意Chrome不允许同时存在多个range,它的处理方式和Firefox有些不同。 91 selection.addRange(selectedRange); 92 } 93 }, 94 //插入文件(这里指图片) 95 insertFiles = function (files) { 96 editor.focus(); 97 //遍历插入(应为可以多文件插入) 98 $.each(files, function (idx, fileInfo) { 99 //只可插入图片文件 100 if (/^image\//.test(fileInfo.type)) { 101 //转码图片 102 $.when(readFileIntoDataUrl(fileInfo)) 103 .done(function (dataUrl) { 104 execCommand(\'insertimage\', dataUrl); //插入图片dom及src属性值 105 }) 106 .fail(function (e) { 107 options.fileUploadError("file-reader", e); 108 }); 109 } else { 110 //非图片文件会调用config的错误函数 111 options.fileUploadError("unsupported-file-type", fileInfo.type); 112 } 113 }); 114 }, 115 //TODO 暂不了解用意 116 markSelection = function (input, color) { 117 restoreSelection(); 118 //确定命令是否被支持,返回true或false 119 if (document.queryCommandSupported(\'hiliteColor\')) { 120 document.execCommand(\'hiliteColor\', 0, color || \'transparent\'); 121 } 122 saveSelection(); 123 input.data(options.selectionMarker, color); 124 }, 125 //绑定工具栏相应工具事件 126 bindToolbar = function (toolbar, options) { 127 //给所有工具栏上的控件绑定点击事件 128 toolbar.find(toolbarBtnSelector).click(function () { 129 restoreSelection(); 130 editor.focus(); //获取焦点 131 //设置相应配置的工具execCommand 132 execCommand($(this).data(options.commandRole)); 133 //保存 134 saveSelection(); 135 }); 136 //对[data-toggle=dropdown]进行单独绑定点击事件处理 字体大小 137 toolbar.find(\'[data-toggle=dropdown]\').click(restoreSelection); 138 //对input控件进行单独处理,webkitspeechchange为语音事件 139 toolbar.find(\'input[type=text][data-\' + options.commandRole + \']\').on(\'webkitspeechchange change\', function () { 140 var newValue = this.value; //获取input 的value 141 this.value = \'\'; //清空value防止冲突 142 restoreSelection(); 143 if (newValue) { 144 editor.focus();//获取焦点 145 //设置相应配置的工具execCommand 146 execCommand($(this).data(options.commandRole), newValue); 147 } 148 saveSelection(); 149 }).on(\'focus\', function () { //获取焦点 150 var input = $(this); 151 if (!input.data(options.selectionMarker)) { 152 markSelection(input, options.selectionColor); 153 input.focus(); 154 } 155 }).on(\'blur\', function () { //失去焦点 156 var input = $(this); 157 if (input.data(options.selectionMarker)) { 158 markSelection(input, false); 159 } 160 }); 161 toolbar.find(\'input[type=file][data-\' + options.commandRole + \']\').change(function () { 162 restoreSelection(); 163 if (this.type === \'file\' && this.files && this.files.length > 0) { 164 insertFiles(this.files); 165 } 166 saveSelection(); 167 this.value = \'\'; 168 }); 169 }, 170 //初始化拖放事件 171 initFileDrops = function () { 172 editor.on(\'dragenter dragover\', false) 173 .on(\'drop\', function (e) { 174 var dataTransfer = e.originalEvent.dataTransfer; 175 e.stopPropagation(); 176 e.preventDefault(); 177 if (dataTransfer && dataTransfer.files && dataTransfer.files.length > 0) { 178 insertFiles(dataTransfer.files); 179 } 180 }); 181 }; 182 //合并传入的配置对象userOptions和默认的配置对象config 183 options = $.extend({}, $.fn.wysiwyg.defaults, userOptions); 184 //设置查找字符串:a[data-edit] button[data-edit] input[type=button][data-edit] 185 toolbarBtnSelector = \'a[data-\' + options.commandRole + \'],button[data-\' + options.commandRole + \'],input[type=button][data-\' + options.commandRole + \']\'; 186 //设置热键 容器有[data-role=editor-toolbar]属性的dom元素 187 bindHotkeys(options.hotKeys); 188 //是否允许拖放 允许则配置拖放 189 if (options.dragAndDropImages) {initFileDrops();} 190 //配置工具栏 191 bindToolbar($(options.toolbarSelector), options); 192 //设置编辑区域为可编辑状态并绑定事件mouseup keyup mouseout 193 editor.attr(\'contenteditable\', true) 194 .on(\'mouseup keyup mouseout\', function () { 195 saveSelection(); 196 updateToolbar(); 197 }); 198 //编辑区域绑定图片点击事件 199 //TODO 这是我自己添加的,因为有时要对图片进行一些操作 200 editor.on(\'mousedown\',\'img\', function (e) { 201 e.preventDefault(); 202 }).on(\'click\', \'img\', function (e) { 203 var $img = $(e.currentTarget); 204 console.log($img); 205 e.preventDefault(); 206 e.stopPropagation(); 207 }); 208 //window绑定touchend事件 209 $(window).bind(\'touchend\', function (e) { 210 var isInside = (editor.is(e.target) || editor.has(e.target).length > 0), 211 currentRange = getCurrentRange(), 212 clear = currentRange && (currentRange.startContainer === currentRange.endContainer && currentRange.startOffset === currentRange.endOffset); 213 if (!clear || isInside) { 214 saveSelection(); 215 updateToolbar(); 216 } 217 }); 218 return this; 219 }; 220 //配置参数 221 $.fn.wysiwyg.defaults = { 222 hotKeys: { //热键 应用hotkeys.js jquery插件 223 \'ctrl+b meta+b\': \'bold\', 224 \'ctrl+i meta+i\': \'italic\', 225 \'ctrl+u meta+u\': \'underline\', 226 \'ctrl+z meta+z\': \'undo\', 227 \'ctrl+y meta+y meta+shift+z\': \'redo\', 228 \'ctrl+l meta+l\': \'justifyleft\', 229 \'ctrl+r meta+r\': \'justifyright\', 230 \'ctrl+e meta+e\': \'justifycenter\', 231 \'ctrl+j meta+j\': \'justifyfull\', 232 \'shift+tab\': \'outdent\', 233 \'tab\': \'indent\' 234 }, 235 toolbarSelector: \'[data-role=editor-toolbar]\', 236 commandRole: \'edit\', 237 activeToolbarClass: \'btn-info\', 238 selectionMarker: \'edit-focus-marker\', 239 selectionColor: \'darkgrey\', 240 dragAndDropImages: true, //是否支持拖放,默认为支持 241 fileUploadError: function (reason, detail) { console.log("File upload error", reason, detail); } 242 }; 243 }(window.jQuery));