编辑器简单介绍

Squire is an HTML5 rich text editor, which provides powerful cross-browser normalisation, whilst being supremely lightweight and flexible. It is built for the present and the future, and as such does not support truly ancient browsers. It should work fine back to around Opera 12, Firefox 3.5, Safari 5, Chrome 9 and IE9.

各文件的功能

  • Editor.js:用构造函数定义了编辑器,并且在原型上增加属性,以实现更多的编辑器操作

    • function Squire ( root, config ) {}
    • var proto = Squire.prototype;
  • clean.js:定义了编辑支持的各种元素,包括tag、属性、style。

    • 对标签,属性,样式多统一化处理
    • 去除白名单外的标签,属性和样式
  • clipboard.js:定义了复制、剪切、粘贴、拖拽操作的drop。所有的编辑器定义的粘贴操作都类似,基本步骤如下:

    • 先从系统剪贴板获取数据;
    • 根据不同浏览器以及数据类型,将数据插入到对应的位置
    • 阻止默认事件
  • Constants.js:定义了编辑器的全局配置,以及一些缩写变量。通常包含以下内容:

    • 给生涩变量取别名

      var ELEMENT_NODE = 1;
      var TEXT_NODE = 3;
      var ZWS = '\u200B';
    • 缩写,比如当前文档对象所在的window对象:var win = doc.defaultView, 也就是var win = node.ownerDocument.defaultView
    • 浏览器的判定以及判断是否支持一些属性:var canObserveMutations = typeof MutationObserver !== 'undefined';
  • exports.js:定义了将要暴露的接口

    Squire.onPaste = onPaste;
    // Node.js exports
    Squire.isInline = isInline;
    Squire.isBlock = isBlock;
    Squire.isContainer = isContainer;
    Squire.getBlockWalker = getBlockWalker;
  • intro.js:代码拼凑的头部`

    ( function ( doc, undefined ) {
    "use strict";`
  • outro.js:代码拼凑的尾部。用Shell命令依次合并intro.js-->其他js文件-->outro.js

    if ( typeof exports === 'object' ) {module.exports = Squire;
    } else if ( typeof define === 'function' && define.amd ) {define( function () {return Squire;});
    } else {win.Squire = Squire;if ( top !== win &&doc.documentElement.getAttribute( 'data-squireinit' ) === 'true' ) {win.editor = new Squire( doc );if ( win.onEditorLoad ) {win.onEditorLoad( win.editor );win.onEditorLoad = null;}}
    }
    }( document ) );
  • KeyHandlers.js:重新定义了上下左右导航、删除、backspace、tab、回车,以及组合按键等操作。以获取在编辑器一致的变现,并且在某些操作中增加钩子函数,比如beforeDelete;afterDelete

    • 对于会对产生变化的操作,先需压入undolist->根据range.collapsed进行操作->更新选区、dom结构树路径
    • 去除白名单外的标签,属性和样式
    • space按键操作的代码解读:

      space: function ( self, _, range ) {var node, parent;self._recordUndoState( range );addLinks( range.startContainer, self._root, self );self._getRangeAndRemoveBookmark( range );// If the cursor is at the end of a link (<a>foo|</a>) then move it// outside of the link (<a>foo</a>|) so that the space is not part of// the link text.node = range.endContainer;parent = node.parentNode;<p><s>asdf</s>sdfas<a href='xx' ><i><b>asdf</b><i></a><p>if ( range.collapsed && range.endOffset === getLength( node ) ) {if ( node.nodeName === 'A' ) {range.setStartAfter( node );} else if ( parent.nodeName === 'A' && !node.nextSibling ) {range.setStartAfter( parent );}}// Delete the selection if not collapsedif ( !range.collapsed ) {deleteContentsOfRange( range, self._root );self._ensureBottomLine();self.setSelection( range );self._updatePath( range, true );}self.setSelection( range );
      },
  • Range.js:关于光标、选区以及范围的操作。

    • 定义一些辅助函数,以实现节点的定位

      var getNodeBefore = function ( node, offset ) {
      var children = node.childNodes;
      //
      while ( offset && node.nodeType === ELEMENT_NODE ) {node = children[ offset - 1 ];children = node.childNodes;offset = children.length;
      }
      return node;
      };
    • Range的增删改查操作

      // Returns the first block at least partially contained by the range,
      // or null if no block is contained by the range.
      var getStartBlockOfRange = function ( range, root ) {var container = range.startContainer,block;// If inline, get the containing block.if ( isInline( container ) ) {block = getPreviousBlock( container, root );} else if ( container !== root && isBlock( container ) ) {block = container;} else {block = getNodeBefore( container, range.startOffset );block = getNextBlock( block, root );}// Check the block actually intersects the rangereturn block && isNodeContainedInRange( range, block, true ) ? block : null;
      };
  • Node.js:关于节点的基础定义,以及基本操作。

    • 从各种角度,定义节点类型,如:inline&&block、叶子节点&&非叶子节点
    • 定义查找指定节点的方法以及节点间的关系:

      function getNearest ( node, root, tag, attributes ) {while ( node && node !== root ) {if ( hasTagAttributes( node, tag, attributes ) ) {return node;}node = node.parentNode;}return null;
      }
    • 定义节点以及之间的操作方法,如:split、merge等操作:

      function _mergeInlines ( node, fakeRange ) {var children = node.childNodes,l = children.length,frags = [],child, prev, len;while ( l-- ) {child = children[l];prev = l && children[ l - 1 ];if ( l && isInline( child ) && areAlike( child, prev ) &&!leafNodeNames[ child.nodeName ] ) {if ( fakeRange.startContainer === child ) {fakeRange.startContainer = prev;fakeRange.startOffset += getLength( prev );}if ( fakeRange.endContainer === child ) {fakeRange.endContainer = prev;fakeRange.endOffset += getLength( prev );}if ( fakeRange.startContainer === node ) {if ( fakeRange.startOffset > l ) {fakeRange.startOffset -= 1;}else if ( fakeRange.startOffset === l ) {fakeRange.startContainer = prev;fakeRange.startOffset = getLength( prev );}}if ( fakeRange.endContainer === node ) {if ( fakeRange.endOffset > l ) {fakeRange.endOffset -= 1;}else if ( fakeRange.endOffset === l ) {fakeRange.endContainer = prev;fakeRange.endOffset = getLength( prev );}}detach( child );if ( child.nodeType === TEXT_NODE ) {prev.appendData( child.data );}else {frags.push( empty( child ) );}}else if ( child.nodeType === ELEMENT_NODE ) {len = frags.length;while ( len-- ) {child.appendChild( frags.pop() );}_mergeInlines( child, fakeRange );}}
      }
  • TreeWakler.js:定义节点的遍历模型,定义最基本的节点查找方法。

    TreeWalker.prototype.previousNode = function () {var current = this.currentNode,root = this.root,nodeType = this.nodeType,filter = this.filter,node;while ( true ) {if ( current === root ) {return null;}node = current.previousSibling;if ( node ) {while ( current = node.lastChild ) {node = current;}} else {node = current.parentNode;}if ( !node ) {return null;}if ( ( typeToBitArray[ node.nodeType ] & nodeType ) &&filter( node ) ) {this.currentNode = node;return node;}current = node;}
    };
  • Editor.js:编辑器构造函数的模型function Squire ( root, config ) { }

    • 编辑器构造函数
    • 编辑器的参数操作函数
    • 编辑器的原型方法(操作函数、自定义事件机制、光标以及选区的方法)

      proto.getSelection = function () {var sel = getWindowSelection( this );var root = this._root;var selection, startContainer, endContainer, node;// If not focused, always rely on cached selection; another function may// have set it but the DOM is not modified until focus againif ( this._isFocused && sel && sel.rangeCount ) {selection  = sel.getRangeAt( 0 ).cloneRange();startContainer = selection.startContainer;endContainer = selection.endContainer;// FF can return the selection as being inside an <img>. WTF?if ( startContainer && isLeaf( startContainer ) ) {selection.setStartBefore( startContainer );}if ( endContainer && isLeaf( endContainer ) ) {selection.setEndBefore( endContainer );}}if ( selection &&isOrContains( root, selection.commonAncestorContainer ) ) {this._lastSelection = selection;} else {selection = this._lastSelection;node = selection.commonAncestorContainer;// Check the editor is in the live document; if not, the range has// probably been rewritten by the browser and is bogusif ( !isOrContains( node.ownerDocument, node ) ) {selection = null;}}if ( !selection ) {selection = this._createRange( root.firstChild, 0 );}return selection;
      };

Squire编辑器源码解读相关推荐

  1. php artisan code,源码解读 Laravel PHP artisan config:cache

    再来一篇源码解读系列,其实包含本篇 config:cache 源码解读在内,这三篇的源码解读都是跟线上环境部署 Laravel 项目有关,因为我们通常会使用这三个 artisan 命令来提高项目的执行 ...

  2. php artisan实现机制,源码解读:php artisan serve

    在学习 Laravel 的时候,可能很多人接触的第一个 artisan 的命令就是:php artisan serve,这样我们就可以跑起第一个 Laravel 的应用.本文来尝试解读一下这个命令行的 ...

  3. 【Unity】 Spine渲染原理解析与源码解读

    Spine渲染原理解析与源码解读 安装环境 从Spine编辑器导出 将资源导入Unity 基础概念 其他相关概念 Spine架构 Spine运行时的各个模块 有状态(Stateful) 和 无状态(S ...

  4. Bert系列(二)——源码解读之模型主体

    本篇文章主要是解读模型主体代码modeling.py.在阅读这篇文章之前希望读者们对bert的相关理论有一定的了解,尤其是transformer的结构原理,网上的资料很多,本文内容对原理部分就不做过多 ...

  5. Bert系列(三)——源码解读之Pre-train

    https://www.jianshu.com/p/22e462f01d8c pre-train是迁移学习的基础,虽然Google已经发布了各种预训练好的模型,而且因为资源消耗巨大,自己再预训练也不现 ...

  6. linux下free源码,linux命令free源码解读:Procps free.c

    linux命令free源码解读 linux命令free源码解读:Procps free.c 作者:isayme 发布时间:September 26, 2011 分类:Linux 我们讨论的是linux ...

  7. nodeJS之eventproxy源码解读

    1.源码缩影 !(function (name, definition) { var hasDefine = typeof define === 'function', //检查上下文环境是否为AMD ...

  8. PyTorch 源码解读之即时编译篇

    点击上方"AI遇见机器学习",选择"星标"公众号 重磅干货,第一时间送达 作者丨OpenMMLab 来源丨https://zhuanlan.zhihu.com/ ...

  9. Alamofire源码解读系列(九)之响应封装(Response)

    本篇主要带来Alamofire中Response的解读 前言 在每篇文章的前言部分,我都会把我认为的本篇最重要的内容提前讲一下.我更想同大家分享这些顶级框架在设计和编码层次究竟有哪些过人的地方?当然, ...

最新文章

  1. 深度学习(2)基础2 -- 分类:得分函数损失函数(损失、正则化惩罚项、梯度下降、学习率)概率
  2. Data Structure - 返回单链表的中间结点
  3. 从零开始数据科学与机器学习算法-简单感知器-05
  4. linux 用vi命令的使用以及vi编辑,Linux中vi编辑命令的使用
  5. HDU 4085 Steiner树模板称号
  6. 前端学习(1697):前端系列javascript之原型
  7. python基础代码技巧_Python 代码优化技巧(二)
  8. 江苏师范大学科文学院计算机科学与技术,2019江苏师范大学科文学院专业排名...
  9. CSS3图片跳动效果
  10. koajs 项目实战(二)
  11. oracle 11g 数据库恢复技术 --rman catalog
  12. dmg镜像如何写入u盘_最全macOS官方全家桶原版系统镜像
  13. google人体浏览器+android,google人体浏览器
  14. 万能Ghost系统制作教程(龙帝国论坛)
  15. 【labelme软件】使用指南
  16. 合理使用AutoHotKey+StrokeIt
  17. 建时间维度表(oracle)
  18. Python比较文本相似度的7种方法(详细)
  19. 经典BBS语录100条
  20. matlab—赋值具有的非单一 rhs 维度多于非单一下标数

热门文章

  1. js移除数组中指定元素
  2. 亚马逊主图视频的那些事儿
  3. Java 基础 之 Valid 验证
  4. softmax算法详解
  5. 神经网络的万能逼近定理
  6. ARM+Android还是 MeeGo+X86
  7. OpenCV_005-OpenCV 鼠标作为画笔
  8. python socketio_python-socketio
  9. 嗨 Jina,帮我画一幅高山流水图
  10. mongodb数据库使用01、基础查询和数据类型介绍