很久沒寫隨筆了,由於最近比較忙,小公司基本都是一個前端幹全部屬於和部分不屬於前端的事情,因此就沒空弄了,即便想分享,也由於沒有時間和精力就擱置了。html
這週週六日休息,正好時間比較充裕(ps:目前處在單休中。。。),就分析瞭如下bootstrap-wysiwyg的源碼,雖然只有200多行,可是本人js水平仍是欠佳,因此大概用了一天的時間(ps:可憐我偶爾的雙休仍是變成了單休),對bootstrap-wysiwyg的源碼進行了解讀和部分的註釋,在這裏放出來,給須要的人,畢竟不是人人都喜歡百度的編輯器的(ps:我就是由於不喜歡百度編輯器的樣式和一大堆文件的緣由纔會去看bootstrap-wysiwyg的源碼的),這樣,理解了內部構造也好擴展和修改,由於最近的項目圖片不能轉碼提交,而是要上傳到淘寶的oss上才返回地址顯示在富文本編輯器中,這也是促使我對bootstrap-wysiwyg源碼的解讀,好了,廢話太多了,簡易註釋的源碼在下面:前端
ps:裏面我添加了對富文本編輯器中點擊圖片的監聽事件(這個仍是頗有用的,好比修改圖片大小),或者讀到這篇文章的也能夠去本身定義接口jquery
同時我也放在了github上,地址是:git
https://github.com/woleicom/bootstrap-wysiwyg-notesgithub
若是github被牆,國內能夠訪問oschina,地址是:web
https://git.oschina.net/woleicom/bootstrap-wysiwyg-notesbootstrap
喜歡的就給星吧,哈哈promise
1 /* http://github.com/mindmup/bootstrap-wysiwyg */ 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));