-
Notifications
You must be signed in to change notification settings - Fork 15
/
xform.js
133 lines (107 loc) · 4.32 KB
/
xform.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// This module defines the `xform` function which is at the heart of OT.
/*jslint onevar: true, undef: true, eqeqeq: true, bitwise: true,
newcap: true, immed: true, nomen: false, white: false, plusplus: false,
laxbreak: true */
/*global define */
define(["./operations"], function (ops) {
// Pattern match on two edits by looking up their transforming function in
// the `xformTable`. Each function in the table should take arguments like
// the following:
//
// xformer(editA, editB, indexA, indexB, continuation)
//
// and should return the results by calling the continuation
//
// return continuation(editAPrime || null, editBPrime || null, newIndexA, newIndexB);
var xformTable = {};
function join (a, b) {
return a + "," + b;
}
// Define a transformation function for when we are comparing two edits of
// typeA and typeB.
function defXformer (typeA, typeB, xformer) {
xformTable[join(typeA, typeB)] = xformer;
}
// Assumptions currently made by all of the xformer functions: that all of
// the individual edits only deal with one character at a time.
defXformer("retain", "retain", function (editA, editB, indexA, indexB, k) {
k(editA, editB, indexA+1, indexB+1);
});
defXformer("delete", "delete", function (editA, editB, indexA, indexB, k) {
if ( ops.val(editA) === ops.val(editB) ) {
k(null, null, indexA+1, indexB+1);
} else {
throw new TypeError("Document state mismatch: delete("
+ ops.val(editA) + ") !== delete(" + ops.val(editB) + ")");
}
});
defXformer("insert", "insert", function (editA, editB, indexA, indexB, k) {
if ( ops.val(editA) === ops.val(editB) ) {
k(ops.retain(1), ops.retain(1), indexA+1, indexB+1);
} else {
k(editA, ops.retain(1), indexA+1, indexB);
}
});
defXformer("retain", "delete", function (editA, editB, indexA, indexB, k) {
k(null, editB, indexA+1, indexB+1);
});
defXformer("delete", "retain", function (editA, editB, indexA, indexB, k) {
k(editA, null, indexA+1, indexB+1);
});
defXformer("insert", "retain", function (editA, editB, indexA, indexB, k) {
k(editA, editB, indexA+1, indexB);
});
defXformer("retain", "insert", function (editA, editB, indexA, indexB, k) {
k(editA, editB, indexA, indexB+1);
});
defXformer("insert", "delete", function (editA, editB, indexA, indexB, k) {
k(editA, ops.retain(1), indexA+1, indexB);
});
defXformer("delete", "insert", function (editA, editB, indexA, indexB, k) {
k(ops.retain(1), editB, indexA, indexB+1);
});
return function (operationA, operationB, k) {
var operationAPrime = [],
operationBPrime = [],
lenA = operationA.length,
lenB = operationB.length,
indexA = 0,
indexB = 0,
editA,
editB,
xformer;
// Continuation for the xformer.
function kk (aPrime, bPrime, newIndexA, newIndexB) {
indexA = newIndexA;
indexB = newIndexB;
if ( aPrime ) {
operationAPrime.push(aPrime);
}
if ( bPrime ) {
operationBPrime.push(bPrime);
}
}
while ( indexA < lenA && indexB < lenB ) {
editA = operationA[indexA];
editB = operationB[indexB];
xformer = xformTable[join(ops.type(editA), ops.type(editB))];
if ( xformer ) {
xformer(editA, editB, indexA, indexB, kk);
} else {
throw new TypeError("Unknown combination to transform: "
+ join(ops.type(editA), ops.type(editB)));
}
}
// If either operation contains more edits than the other, we just
// pass them on to the prime version.
for ( ; indexA < lenA; indexA++ ) {
operationAPrime.push(operationA[indexA]);
operationBPrime.push(ops.retain(1));
}
for ( ; indexB < lenB; indexB++ ) {
operationBPrime.push(operationB[indexB]);
operationAPrime.push(ops.retain(1));
}
return k(operationAPrime, operationBPrime);
};
});