簡易富文本編輯器bootstrap-wysiwyg源碼註釋

很久沒寫隨筆了,由於最近比較忙,小公司基本都是一個前端幹全部屬於和部分不屬於前端的事情,因此就沒空弄了,即便想分享,也由於沒有時間和精力就擱置了。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>|&nbsp;)*$/, '');
 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));
相關文章
相關標籤/搜索