first commit

This commit is contained in:
2026-06-24 09:48:54 +02:00
commit 41e62ddcad
33739 changed files with 4266226 additions and 0 deletions
+243
View File
@@ -0,0 +1,243 @@
var diff = require('fast-diff');
var is = require('./is');
var op = require('./op');
var NULL_CHARACTER = String.fromCharCode(0); // Placeholder char for embed in diff()
var Delta = function (ops) {
// Assume we are given a well formed ops
if (is.array(ops)) {
this.ops = ops;
} else if (is.object(ops) && is.array(ops.ops)) {
this.ops = ops.ops;
} else {
this.ops = [];
}
};
Delta.prototype.insert = function (text, attributes) {
var newOp = {};
if (is.string(text)) {
if (text.length === 0) return this;
newOp.insert = text;
} else if (is.number(text)) {
newOp.insert = text;
}
if (is.object(attributes) && Object.keys(attributes).length > 0) newOp.attributes = attributes;
return this.push(newOp);
};
Delta.prototype['delete'] = function (length) {
if (length <= 0) return this;
return this.push({ 'delete': length });
};
Delta.prototype.retain = function (length, attributes) {
if (length <= 0) return this;
var newOp = { retain: length };
if (is.object(attributes) && Object.keys(attributes).length > 0) newOp.attributes = attributes;
return this.push(newOp);
};
Delta.prototype.push = function (newOp) {
var index = this.ops.length;
var lastOp = this.ops[index - 1];
newOp = op.clone(newOp);
if (is.object(lastOp)) {
if (is.number(newOp['delete']) && is.number(lastOp['delete'])) {
this.ops[index - 1] = { 'delete': lastOp['delete'] + newOp['delete'] };
return this;
}
// Since it does not matter if we insert before or after deleting at the same index,
// always prefer to insert first
if (is.number(lastOp['delete']) && (is.string(newOp.insert) || is.number(newOp.insert))) {
index -= 1;
lastOp = this.ops[index - 1];
if (!is.object(lastOp)) {
this.ops.unshift(newOp);
return this;
}
}
if (is.equal(newOp.attributes, lastOp.attributes)) {
if (is.string(newOp.insert) && is.string(lastOp.insert)) {
this.ops[index - 1] = { insert: lastOp.insert + newOp.insert };
if (is.object(newOp.attributes)) this.ops[index - 1].attributes = newOp.attributes
return this;
} else if (is.number(newOp.retain) && is.number(lastOp.retain)) {
this.ops[index - 1] = { retain: lastOp.retain + newOp.retain };
if (is.object(newOp.attributes)) this.ops[index - 1].attributes = newOp.attributes
return this;
}
}
}
this.ops.splice(index, 0, newOp);
return this;
};
Delta.prototype.chop = function () {
var lastOp = this.ops[this.ops.length - 1];
if (lastOp && lastOp.retain && !lastOp.attributes) {
this.ops.pop();
}
return this;
};
Delta.prototype.length = function () {
return this.ops.reduce(function (length, elem) {
return length + op.length(elem);
}, 0);
};
Delta.prototype.slice = function (start, end) {
start = start || 0;
if (!is.number(end)) end = Infinity;
var delta = new Delta();
var iter = op.iterator(this.ops);
var index = 0;
while (index < end && iter.hasNext()) {
var nextOp;
if (index < start) {
nextOp = iter.next(start - index);
} else {
nextOp = iter.next(end - index);
delta.push(nextOp);
}
index += op.length(nextOp);
}
return delta;
};
Delta.prototype.compose = function (other) {
var thisIter = op.iterator(this.ops);
var otherIter = op.iterator(other.ops);
this.ops = [];
while (thisIter.hasNext() || otherIter.hasNext()) {
if (otherIter.peekType() === 'insert') {
this.push(otherIter.next());
} else if (thisIter.peekType() === 'delete') {
this.push(thisIter.next());
} else {
var length = Math.min(thisIter.peekLength(), otherIter.peekLength());
var thisOp = thisIter.next(length);
var otherOp = otherIter.next(length);
if (is.number(otherOp.retain)) {
var newOp = {};
if (is.number(thisOp.retain)) {
newOp.retain = length;
} else {
newOp.insert = thisOp.insert;
}
// Preserve null when composing with a retain, otherwise remove it for inserts
var attributes = op.attributes.compose(thisOp.attributes, otherOp.attributes, is.number(thisOp.retain));
if (attributes) newOp.attributes = attributes;
this.push(newOp);
// Other op should be delete, we could be an insert or retain
// Insert + delete cancels out
} else if (is.number(otherOp['delete']) && is.number(thisOp.retain)) {
this.push(otherOp);
}
}
}
return this.chop();
};
Delta.prototype.diff = function (other) {
var strings = [this.ops, other.ops].map(function (ops) {
return ops.map(function (op) {
if (is.string(op.insert)) return op.insert;
if (is.number(op.insert)) return NULL_CHARACTER;
var prep = ops === other.ops ? 'on' : 'with';
throw new Error('diff() called ' + prep + ' non-document');
}).join('');
});
var diffResult = diff(strings[0], strings[1]);
var thisIter = op.iterator(this.ops);
var otherIter = op.iterator(other.ops);
var delta = new Delta();
diffResult.forEach(function (component) {
var length = component[1].length;
while (length > 0) {
var opLength = 0;
switch (component[0]) {
case diff.INSERT:
opLength = Math.min(otherIter.peekLength(), length);
delta.push(otherIter.next(opLength));
break;
case diff.DELETE:
opLength = Math.min(length, thisIter.peekLength());
thisIter.next(opLength);
delta['delete'](opLength);
break;
case diff.EQUAL:
opLength = Math.min(thisIter.peekLength(), otherIter.peekLength(), length);
var thisOp = thisIter.next(opLength);
var otherOp = otherIter.next(opLength);
if (thisOp.insert === otherOp.insert) {
delta.retain(opLength, op.attributes.diff(thisOp.attributes, otherOp.attributes));
} else {
delta.push(otherOp)['delete'](opLength);
}
break;
}
length -= opLength;
}
});
return delta.chop();
};
Delta.prototype.transform = function (other, priority) {
priority = !!priority;
if (is.number(other)) {
return this.transformPosition(other, priority);
}
var thisIter = op.iterator(this.ops);
var otherIter = op.iterator(other.ops);
var delta = new Delta();
while (thisIter.hasNext() || otherIter.hasNext()) {
if (thisIter.peekType() === 'insert' && (priority || otherIter.peekType() !== 'insert')) {
delta.retain(op.length(thisIter.next()));
} else if (otherIter.peekType() === 'insert') {
delta.push(otherIter.next());
} else {
var length = Math.min(thisIter.peekLength(), otherIter.peekLength());
var thisOp = thisIter.next(length);
var otherOp = otherIter.next(length);
if (thisOp['delete']) {
// Our delete either makes their delete redundant or removes their retain
continue;
} else if (otherOp['delete']) {
delta.push(otherOp);
} else {
// We retain either their retain or insert
delta.retain(length, op.attributes.transform(thisOp.attributes, otherOp.attributes, priority));
}
}
}
return delta.chop();
};
Delta.prototype.transformPosition = function (index, priority) {
priority = !!priority;
var thisIter = op.iterator(this.ops);
var offset = 0;
while (thisIter.hasNext() && offset <= index) {
var length = thisIter.peekLength();
var nextType = thisIter.peekType();
thisIter.next();
if (nextType === 'delete') {
index -= Math.min(length, index - offset);
continue;
} else if (nextType === 'insert' && (offset < index || !priority)) {
index += length;
}
offset += length;
}
return index;
};
module.exports = Delta;
+34
View File
@@ -0,0 +1,34 @@
module.exports = {
equal: function (a, b) {
if (a === b) return true;
if (a == null && b == null) return true;
if (a == null || b == null) return false;
if (Object.keys(a).length != Object.keys(b).length) return false;
for(var key in a) {
// Only compare one level deep
if (a[key] !== b[key]) return false;
}
return true;
},
array: function (value) {
return Array.isArray(value);
},
number: function (value) {
if (typeof value === 'number') return true;
if (typeof value === 'object' && Object.prototype.toString.call(value) === '[object Number]') return true;
return false;
},
object: function (value) {
if (!value) return false;
return (typeof value === 'function' || typeof value === 'object');
},
string: function (value) {
if (typeof value === 'string') return true;
if (typeof value === 'object' && Object.prototype.toString.call(value) === '[object String]') return true;
return false;
}
};
+144
View File
@@ -0,0 +1,144 @@
var is = require('./is');
var lib = {
attributes: {
clone: function (attributes, keepNull) {
if (!is.object(attributes)) return {};
return Object.keys(attributes).reduce(function (memo, key) {
if (attributes[key] !== undefined && (attributes[key] !== null || keepNull)) {
memo[key] = attributes[key];
}
return memo;
}, {});
},
compose: function (a, b, keepNull) {
if (!is.object(a)) a = {};
if (!is.object(b)) b = {};
var attributes = this.clone(b, keepNull);
for (var key in a) {
if (a[key] !== undefined && b[key] === undefined) {
attributes[key] = a[key];
}
}
return Object.keys(attributes).length > 0 ? attributes : undefined;
},
diff: function(a, b) {
if (!is.object(a)) a = {};
if (!is.object(b)) b = {};
var attributes = Object.keys(a).concat(Object.keys(b)).reduce(function (attributes, key) {
if (a[key] !== b[key]) {
attributes[key] = b[key] === undefined ? null : b[key];
}
return attributes;
}, {});
return Object.keys(attributes).length > 0 ? attributes : undefined;
},
transform: function (a, b, priority) {
if (!is.object(a)) return b;
if (!is.object(b)) return undefined;
if (!priority) return b; // b simply overwrites us without priority
var attributes = Object.keys(b).reduce(function (attributes, key) {
if (a[key] === undefined) attributes[key] = b[key]; // null is a valid value
return attributes;
}, {});
return Object.keys(attributes).length > 0 ? attributes : undefined;
}
},
clone: function (op) {
var newOp = this.attributes.clone(op);
if (is.object(newOp.attributes)) {
newOp.attributes = this.attributes.clone(newOp.attributes, true);
}
return newOp;
},
iterator: function (ops) {
return new Iterator(ops);
},
length: function (op) {
if (is.number(op['delete'])) {
return op['delete'];
} else if (is.number(op.retain)) {
return op.retain;
} else {
return is.string(op.insert) ? op.insert.length : 1;
}
}
};
function Iterator(ops) {
this.ops = ops;
this.index = 0;
this.offset = 0;
};
Iterator.prototype.hasNext = function () {
return this.peekLength() < Infinity;
};
Iterator.prototype.next = function (length) {
if (!length) length = Infinity;
var nextOp = this.ops[this.index];
if (nextOp) {
var offset = this.offset;
var opLength = lib.length(nextOp)
if (length >= opLength - offset) {
length = opLength - offset;
this.index += 1;
this.offset = 0;
} else {
this.offset += length;
}
if (is.number(nextOp['delete'])) {
return { 'delete': length };
} else {
var retOp = {};
if (nextOp.attributes) {
retOp.attributes = nextOp.attributes;
}
if (is.number(nextOp.retain)) {
retOp.retain = length;
} else if (is.string(nextOp.insert)) {
retOp.insert = nextOp.insert.substr(offset, length);
} else {
// offset should === 0, length should === 1
retOp.insert = nextOp.insert;
}
return retOp;
}
} else {
return { retain: Infinity };
}
};
Iterator.prototype.peekLength = function () {
if (this.ops[this.index]) {
// Should never return 0 if our index is being managed correctly
return lib.length(this.ops[this.index]) - this.offset;
} else {
return Infinity;
}
};
Iterator.prototype.peekType = function () {
if (this.ops[this.index]) {
if (is.number(this.ops[this.index]['delete'])) {
return 'delete';
} else if (is.number(this.ops[this.index].retain)) {
return 'retain';
} else {
return 'insert';
}
}
return 'retain';
};
module.exports = lib;
+38
View File
@@ -0,0 +1,38 @@
var Delta = require('./delta');
var pkg = require('../package.json');
module.exports = {
Delta: Delta,
name: 'rich-text',
uri: 'http://sharejs.org/types/rich-text/v1',
create: function (initial) {
return new Delta(initial);
},
apply: function (snapshot, delta) {
snapshot = new Delta(snapshot);
delta = new Delta(delta);
return snapshot.compose(delta);
},
compose: function (delta1, delta2) {
delta1 = new Delta(delta1);
delta2 = new Delta(delta2);
return delta1.compose(delta2);
},
diff: function (delta1, delta2) {
delta1 = new Delta(delta1);
delta2 = new Delta(delta2);
return delta1.diff(delta2);
},
transform: function (delta1, delta2, side) {
delta1 = new Delta(delta1);
delta2 = new Delta(delta2);
// Fuzzer specs is in opposite order of delta interface
return delta2.transform(delta1, side === 'left');
}
};