CKEditor5——Position源码分析(三)

CKEditor5——Position源码分析(三)

上一节我们学习了比较两个Position之间的关系,它们可能属于不同的根节点,它们可能完全相同,它们也可能存在前后关系。

今天我们继续学习这个类的另一个关键方法:那就是

getTransformedByOperation( operation ) → Position

getTransformedByOperation( operation ) {
	let result;

	switch ( operation.type ) {
		case 'insert':
			result = this._getTransformedByInsertOperation( operation );
			break;
		case 'move':
		case 'remove':
		case 'reinsert':
			result = this._getTransformedByMoveOperation( operation );
			break;
		case 'split':
			result = this._getTransformedBySplitOperation( operation );
			break;
		case 'merge':
			result = this._getTransformedByMergeOperation( operation );
			break;
		default:
			result = Position._createAt( this );
			break;
	}
	return result;
}

首先说明一下这个方法:

返回由给定操作转换的此位置的副本。新位置的参数会根据操作的效果进行相应更新。例如,如果在位置之前插入n个节点,则返回的位置偏移量将增加n。如果该位置在合并元素中,它将相应地移动到新元素等。这种方法可以安全地用于不存在的位置(例如在操作转换期间)。

从以上说明,我们可以看出,这个方法是当前位置应用一个给定的操作后返回一个新的位置,具体的操作有六类:insert,move,remove,reinsert,split,merge,而move,remove,reinsert又是同一种情况,我们先看看insert的情况:this._getTransformedByInsertOperation( operation );

_getTransformedByInsertOperation( operation ) {
	return this._getTransformedByInsertion( operation.position, operation.howMany );
}
_getTransformedByInsertion( insertPosition, howMany ) {
	const transformed = Position._createAt( this );

	// This position can't be affected if insertion was in a different root.
	if ( this.root != insertPosition.root ) {
		return transformed;
	}

	if ( compareArrays( insertPosition.getParentPath(), this.getParentPath() ) == 'same' ) {
		// If nodes are inserted in the node that is pointed by this position...
		if ( insertPosition.offset < this.offset || ( insertPosition.offset == this.offset && this.stickiness != 'toPrevious' ) ) {
			// And are inserted before an offset of that position...
			// "Push" this positions offset.
			transformed.offset += howMany;
		}
	} else if ( compareArrays( insertPosition.getParentPath(), this.getParentPath() ) == 'prefix' ) {
		// If nodes are inserted in a node that is on a path to this position...
		const i = insertPosition.path.length - 1;

		if ( insertPosition.offset <= this.path[ i ] ) {
			// And are inserted before next node of that path...
			// "Push" the index on that path.
			transformed.path[ i ] += howMany;
		}
	}

	return transformed;
}

我们看到insert的情况实际上分为三类,

1、如果当前位置的根节点与操作对应的位置的根节点不一致,那么直接返回当前位置,实际上可能啥都没有做,因为操作对应的位置没有啥效果

2、如果当前位置的parentPath与插入操作的位置的parentPath一致,那么这里分两种情况:

//第一种情况
// should increment offset if insertion is in the same parent and the same offset
const position = new Position( root, [ 1, 2, 3 ] );
position.stickiness = 'toNext';
const transformed = position._getTransformedByInsertion( new Position( root, [ 1, 2, 3 ] ), 2 );

expect( transformed.offset ).to.equal( 5 );
//第二种情况
//should increment offset if insertion is in the same parent and closer offset
const position = new Position( root, [ 1, 2, 3 ] );
const transformed = position._getTransformedByInsertion( new Position( root, [ 1, 2, 2 ] ), 2 );
expect( transformed.offset ).to.equal( 5 );

3、如果插入位置的parentPath在当前位置的parentPath之前,那么这种情况如下:

//should update path if insertion position parent is a node from that path and offset is before next node on that path
const position = new Position( root, [ 1, 2, 3 ] );
const transformed = position._getTransformedByInsertion( new Position( root, [ 1, 2 ] ), 2 );

expect( transformed.path ).to.deep.equal( [ 1, 4, 3 ] );

另外的其他情况的话,返回的位置都不会发生变化,因此,以上就是插入操作的情况下,可能发生新位置偏移的情况。另外move的情况比较复杂,我们以后分析。

 

 

版权声明:著作权归作者所有。

相关推荐

Android dex文件反编译为Java源码

工具准备 dex2jar:国人写的一个dex反编译为java的工具jd-gui:查看java源码的gui工具反编译步骤步骤一把test_apk-debug.apk里的classes.dex转换为test_apk-debug_dex2jar.jard2j-dex2jar.sh -f -o output_jar.jar apk_to_decompile.apk

JavaScript使用for...in迭代数组分析

for...in与其说是迭代,更恰当的说法应该是枚举。其目的是用来枚举object对象的属性,包括对象所继承的属性。有部分人会使用for...in来迭代数组,这是一种误用。以下对使用for...in迭代数组分析。问题一var a = ['a','b']; a[5] = 'e'; for (var x in a

Android安装错误App not installed原因分析

App not installed可能原因:App与Android设备或版本不兼容使用了不同的证书签名不同版本的apk,这会导致重新安装失败。app的签名不正确,检查下是否与选择的Signature Version相关v2为Android 7新增的签名方式。可以参考APK signature scheme v2

使用awk分析nginx访问日志access.log的ip

access.log为nginx的访问日志,默认路径在/var/log/nginx/access.log 分析access.log的ip命令如下:awk '{print $1}' access.log |sort|uniq -c|sort -n 命令里使用awk过滤出访问的ip使用sort对ip排序对排序后的ip进行统计,统计每一个ip访

Java源码分析:产生随机数Random与ThreadLocalRandom的区别

Java用于产生随机数的方法主要有两种:java.util.Random和java.util.concurrent.ThreadLocalRandom。Random从Jdk 1.0开始就有了,而ThreadLocalRandom是Jdk1.7才新增的。简单从命名和类所在的包上看,两者的区别在于对并发的支持。RandomRandom是一个伪随机数生成器,它内置了一个种子数seed。获取随机

Rxjs expand的用法分析

Rxjs的expand()函数声明:public expand(project: function(value: T, index: number), concurrent: number, scheduler: Scheduler): Observable expand()会递归调用project函数,project函数把源值映射为一个Observable,每次递归

(转)Android 5.1.1 源码目录结构

转自:http://blog.csdn.net/tfslovexizi/article/details/51888458最近公司培训新同事,我负责整理一点关于android的基础知识,遥想当年,刚接触android,也是一头雾水,啥都不懂,就是靠看文档和视频,对android有一个初步了解,然后就通过查看源码,才有更深入的了解。android有成千上万,说太少了,是成百万上亿的代码,当然要全部都了

JIT的分层编译和逃逸分析

JIT到底在Java的运行中发挥了什么作用呢?根据查阅到的资料,一个作用是做分层编译,一个是做对象的逃逸分析。对于循环体中的代码,循环到一定的程度的时候,就会被再次被编译,编程执行速度更加迅速的代码。对于新建的对象,讲过逃逸分析,如果数据不会逃逸,则将数据放在栈上,不再在heap上新建这个对象。这样的好处是:避免了在堆上新建的锁堆导致的资源损耗不需要GC

CKEditor5——模型理解(三:Element Text)

在上一节,我们学习了CK5中模型节点Node的API,今天我们学习另一个常用的API:Element。元素节点说明element表示模型的元素节点类型,它包含一个拥有名称和子节点的节点类型,继承自Node类。元素属性说明1、name,元素的名称举个例子哈,段落的名称是paragraph,代码块的名称是codeBlock等等。2、childCount, 子元素的数目这里指的是此元素节点包含的子元素的

CKEditor5——模型理解(五:Position, Range, Selection)

今天我们继续学习CK5中模型的一些知识,主要包括:Position, Range, Selection首先,我们需要知道:position表示模型树中的一个位置。模型的位置有两部分组成:root,path。即位置由其根和该根中的路径表示。位置基于偏移量,而不是索引。这意味着两个文本节点 foo 和 bar 之间的位置偏移为 3,而不是 1。由于模型中的位置由位置根和位置路径表示,因此可以创建不存在