zoukankan      html  css  js  c++  java
  • 简易富文本编辑器bootstrap-wysiwyg源码注释

    好久没写随笔了,因为最近比较忙,小公司基本都是一个前端干所有属于和部分不属于前端的事情,所以就没空弄了,即使想分享,也因为没有时间和精力就搁置了。

    这周周六日休息,正好时间比较充裕(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 /* 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));
  • 相关阅读:
    有向图中的环DAG
    pyltp安装闭坑指南
    pip安装包到不同的python解释器
    WARNING: Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None)) after connec
    词向量
    基于规则的关系抽取
    NLP(十三)中文分词工具的使用尝试
    NLP(十二)依存句法分析的可视化及图分析
    NLP入门(十一)从文本中提取时间
    NLP入门(十)使用LSTM进行文本情感分析
  • 原文地址:https://www.cnblogs.com/woleicom/p/5468874.html
Copyright © 2011-2022 走看看