Compare commits
18 Commits
v13.0.0-50
...
v13.0.0-55
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
29760cfe53 | ||
|
|
941a22b257 | ||
|
|
4aa41b98a9 | ||
|
|
079de07eff | ||
|
|
54453e87fa | ||
|
|
1b0e3659c3 | ||
|
|
641f426339 | ||
|
|
fcbca65d8f | ||
|
|
5f8ae0dd43 | ||
|
|
de14fe0f3e | ||
|
|
5e4b071693 | ||
|
|
937de2c59f | ||
|
|
f1f1bff901 | ||
|
|
4855b2d590 | ||
|
|
908ce31e2f | ||
|
|
e4d4c23f0b | ||
|
|
fc500a8247 | ||
|
|
acb0affa33 |
13
README.md
13
README.md
@@ -64,6 +64,19 @@ missing modules.
|
||||
<script src="./bower_components/yjs/y.js"></script>
|
||||
```
|
||||
|
||||
### CDN
|
||||
```
|
||||
<script src="https://cdn.jsdelivr.net/npm/yjs@12/src/y.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/y-array@10/dist/y-array.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/y-websockets-client@8/dist/y-websockets-client.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/y-memory@8/dist/y-memory.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/y-array@10/dist/y-array.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/y-map@10/dist/y-map.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/y-text@9/dist/y-text.js"></script>
|
||||
// ..
|
||||
// do the same for all modules you want to use
|
||||
```
|
||||
|
||||
### Npm
|
||||
```
|
||||
npm install --save yjs % add all y-* modules you want to use
|
||||
|
||||
@@ -18,7 +18,7 @@ window.undoManager = new Y.utils.UndoManager(window.yXmlType, {
|
||||
})
|
||||
|
||||
document.onkeydown = function interceptUndoRedo (e) {
|
||||
if (e.keyCode === 90 && e.metaKey) {
|
||||
if (e.keyCode === 90 && (e.metaKey || e.ctrlKey)) {
|
||||
if (!e.shiftKey) {
|
||||
window.undoManager.undo()
|
||||
} else {
|
||||
|
||||
2
package-lock.json
generated
2
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "yjs",
|
||||
"version": "13.0.0-50",
|
||||
"version": "13.0.0-55",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "yjs",
|
||||
"version": "13.0.0-50",
|
||||
"version": "13.0.0-55",
|
||||
"description": "A framework for real-time p2p shared editing on any data",
|
||||
"main": "./y.node.js",
|
||||
"browser": "./y.js",
|
||||
|
||||
@@ -4,8 +4,10 @@ import { readDeleteSet, writeDeleteSet } from './deleteSet.js'
|
||||
import BinaryEncoder from '../Binary/Encoder.js'
|
||||
|
||||
export function fromBinary (y, decoder) {
|
||||
integrateRemoteStructs(y, decoder)
|
||||
readDeleteSet(y, decoder)
|
||||
y.transact(function () {
|
||||
integrateRemoteStructs(y, decoder)
|
||||
readDeleteSet(y, decoder)
|
||||
})
|
||||
}
|
||||
|
||||
export function toBinary (y) {
|
||||
|
||||
@@ -51,22 +51,57 @@ export default class Item {
|
||||
this._parent = null
|
||||
this._parentSub = null
|
||||
this._deleted = false
|
||||
this._redone = null
|
||||
}
|
||||
/**
|
||||
* Copy the effect of struct
|
||||
* Create a operation with the same effect (without position effect)
|
||||
*/
|
||||
_copy (undeleteChildren, copyPosition) {
|
||||
let struct = new this.constructor()
|
||||
if (copyPosition) {
|
||||
struct._origin = this._left
|
||||
struct._left = this._left
|
||||
struct._right = this
|
||||
struct._right_origin = this
|
||||
struct._parent = this._parent
|
||||
struct._parentSub = this._parentSub
|
||||
_copy () {
|
||||
return new this.constructor()
|
||||
}
|
||||
/**
|
||||
* Redo the effect of this operation.
|
||||
*/
|
||||
_redo (y) {
|
||||
if (this._redone !== null) {
|
||||
return this._redone
|
||||
}
|
||||
let struct = this._copy()
|
||||
let left = this._left
|
||||
let right = this
|
||||
let parent = this._parent
|
||||
// make sure that parent is redone
|
||||
if (parent._deleted === true && parent._redone === null) {
|
||||
parent._redo(y)
|
||||
}
|
||||
if (parent._redone !== null) {
|
||||
parent = parent._redone
|
||||
// find next cloned items
|
||||
while (left !== null) {
|
||||
if (left._redone !== null && left._redone._parent === parent) {
|
||||
left = left._redone
|
||||
break
|
||||
}
|
||||
left = left._left
|
||||
}
|
||||
while (right !== null) {
|
||||
if (right._redone !== null && right._redone._parent === parent) {
|
||||
right = right._redone
|
||||
}
|
||||
right = right._right
|
||||
}
|
||||
}
|
||||
struct._origin = left
|
||||
struct._left = left
|
||||
struct._right = right
|
||||
struct._right_origin = right
|
||||
struct._parent = parent
|
||||
struct._parentSub = this._parentSub
|
||||
struct._integrate(y)
|
||||
this._redone = struct
|
||||
return struct
|
||||
}
|
||||
|
||||
get _lastId () {
|
||||
return new ID(this._id.user, this._id.clock + this._length - 1)
|
||||
}
|
||||
@@ -92,11 +127,15 @@ export default class Item {
|
||||
if (!this._deleted) {
|
||||
this._deleted = true
|
||||
y.ds.markDeleted(this._id, this._length)
|
||||
let del = new Delete()
|
||||
del._targetID = this._id
|
||||
del._length = this._length
|
||||
if (createDelete) {
|
||||
let del = new Delete()
|
||||
del._targetID = this._id
|
||||
del._length = this._length
|
||||
// broadcast and persists Delete
|
||||
del._integrate(y, true)
|
||||
} else if (y.persistence !== null) {
|
||||
// only persist Delete
|
||||
y.persistence.saveStruct(y, del)
|
||||
}
|
||||
transactionTypeChanged(y, this._parent, this._parentSub)
|
||||
y._transaction.deletedStructs.add(this)
|
||||
|
||||
@@ -6,8 +6,8 @@ export default class ItemJSON extends Item {
|
||||
super()
|
||||
this._content = null
|
||||
}
|
||||
_copy (undeleteChildren, copyPosition) {
|
||||
let struct = super._copy(undeleteChildren, copyPosition)
|
||||
_copy () {
|
||||
let struct = super._copy()
|
||||
struct._content = this._content
|
||||
return struct
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ export default class ItemString extends Item {
|
||||
super()
|
||||
this._content = null
|
||||
}
|
||||
_copy (undeleteChildren, copyPosition) {
|
||||
let struct = super._copy(undeleteChildren, copyPosition)
|
||||
_copy () {
|
||||
let struct = super._copy()
|
||||
struct._content = this._content
|
||||
return struct
|
||||
}
|
||||
|
||||
@@ -79,40 +79,6 @@ export default class Type extends Item {
|
||||
type = type._parent
|
||||
}
|
||||
}
|
||||
_copy (undeleteChildren, copyPosition) {
|
||||
let copy = super._copy(undeleteChildren, copyPosition)
|
||||
let map = new Map()
|
||||
copy._map = map
|
||||
for (let [key, value] of this._map) {
|
||||
if (undeleteChildren.has(value) || !value.deleted) {
|
||||
let _item = value._copy(undeleteChildren, false)
|
||||
_item._parent = copy
|
||||
_item._parentSub = key
|
||||
map.set(key, _item)
|
||||
}
|
||||
}
|
||||
let prevUndeleted = null
|
||||
copy._start = null
|
||||
let item = this._start
|
||||
while (item !== null) {
|
||||
if (undeleteChildren.has(item) || !item.deleted) {
|
||||
let _item = item._copy(undeleteChildren, false)
|
||||
_item._left = prevUndeleted
|
||||
_item._origin = prevUndeleted
|
||||
_item._right = null
|
||||
_item._right_origin = null
|
||||
_item._parent = copy
|
||||
if (prevUndeleted === null) {
|
||||
copy._start = _item
|
||||
} else {
|
||||
prevUndeleted._right = _item
|
||||
}
|
||||
prevUndeleted = _item
|
||||
}
|
||||
item = item._right
|
||||
}
|
||||
return copy
|
||||
}
|
||||
_transact (f) {
|
||||
const y = this._y
|
||||
if (y !== null) {
|
||||
|
||||
@@ -8,6 +8,9 @@ export default class YXmlElement extends YXmlFragment {
|
||||
super()
|
||||
this.nodeName = null
|
||||
this._scrollElement = null
|
||||
if (typeof arg2 === 'function') {
|
||||
this._domFilter = arg2
|
||||
}
|
||||
if (typeof arg1 === 'string') {
|
||||
this.nodeName = arg1.toUpperCase()
|
||||
} else if (arg1 != null && arg1.nodeType != null && arg1.nodeType === arg1.ELEMENT_NODE) {
|
||||
@@ -16,12 +19,9 @@ export default class YXmlElement extends YXmlFragment {
|
||||
} else {
|
||||
this.nodeName = 'UNDEFINED'
|
||||
}
|
||||
if (typeof arg2 === 'function') {
|
||||
this._domFilter = arg2
|
||||
}
|
||||
}
|
||||
_copy (undeleteChildren, copyPosition) {
|
||||
let struct = super._copy(undeleteChildren, copyPosition)
|
||||
_copy () {
|
||||
let struct = super._copy()
|
||||
struct.nodeName = this.nodeName
|
||||
return struct
|
||||
}
|
||||
@@ -36,9 +36,10 @@ export default class YXmlElement extends YXmlFragment {
|
||||
let attributes = new Map()
|
||||
for (let i = 0; i < dom.attributes.length; i++) {
|
||||
let attr = dom.attributes[i]
|
||||
attributes.set(attr.name, attr.value)
|
||||
// get attribute via getAttribute for custom element support (some write something different in attr.value)
|
||||
attributes.set(attr.name, dom.getAttribute(attr.name))
|
||||
}
|
||||
attributes = this._domFilter(dom, attributes)
|
||||
attributes = this._domFilter(dom.nodeName, attributes)
|
||||
attributes.forEach((value, name) => {
|
||||
this.setAttribute(name, value)
|
||||
})
|
||||
|
||||
@@ -14,8 +14,8 @@ export default class YXmlHook extends YMap {
|
||||
getHook(hookName).fillType(dom, this)
|
||||
}
|
||||
}
|
||||
_copy (undeleteChildren, copyPosition) {
|
||||
const struct = super._copy(undeleteChildren, copyPosition)
|
||||
_copy () {
|
||||
const struct = super._copy()
|
||||
struct.hookName = this.hookName
|
||||
return struct
|
||||
}
|
||||
|
||||
@@ -4,11 +4,12 @@ class ReverseOperation {
|
||||
constructor (y, transaction) {
|
||||
this.created = new Date()
|
||||
const beforeState = transaction.beforeState
|
||||
this.toState = new ID(y.userID, y.ss.getState(y.userID) - 1)
|
||||
if (beforeState.has(y.userID)) {
|
||||
this.toState = new ID(y.userID, y.ss.getState(y.userID) - 1)
|
||||
this.fromState = new ID(y.userID, beforeState.get(y.userID))
|
||||
} else {
|
||||
this.fromState = this.toState
|
||||
this.toState = null
|
||||
this.fromState = null
|
||||
}
|
||||
this.deletedStructs = transaction.deletedStructs
|
||||
}
|
||||
@@ -30,28 +31,32 @@ function applyReverseOperation (y, scope, reverseBuffer) {
|
||||
while (!performedUndo && reverseBuffer.length > 0) {
|
||||
let undoOp = reverseBuffer.pop()
|
||||
// make sure that it is possible to iterate {from}-{to}
|
||||
y.os.getItemCleanStart(undoOp.fromState)
|
||||
y.os.getItemCleanEnd(undoOp.toState)
|
||||
y.os.iterate(undoOp.fromState, undoOp.toState, op => {
|
||||
if (!op._deleted && isStructInScope(y, op, scope)) {
|
||||
performedUndo = true
|
||||
op._delete(y)
|
||||
}
|
||||
})
|
||||
if (undoOp.fromState !== null) {
|
||||
y.os.getItemCleanStart(undoOp.fromState)
|
||||
y.os.getItemCleanEnd(undoOp.toState)
|
||||
y.os.iterate(undoOp.fromState, undoOp.toState, op => {
|
||||
while (op._deleted && op._redone !== null) {
|
||||
op = op._redone
|
||||
}
|
||||
if (op._deleted === false && isStructInScope(y, op, scope)) {
|
||||
performedUndo = true
|
||||
op._delete(y)
|
||||
}
|
||||
})
|
||||
}
|
||||
for (let op of undoOp.deletedStructs) {
|
||||
if (
|
||||
isStructInScope(y, op, scope) &&
|
||||
op._parent !== y &&
|
||||
!op._parent._deleted &&
|
||||
(
|
||||
op._parent._id.user !== y.userID ||
|
||||
op._parent._id.clock < undoOp.fromState.clock ||
|
||||
op._parent._id.clock > undoOp.fromState.clock
|
||||
op._id.user !== y.userID ||
|
||||
undoOp.fromState === null ||
|
||||
op._id.clock < undoOp.fromState.clock ||
|
||||
op._id.clock > undoOp.toState.clock
|
||||
)
|
||||
) {
|
||||
performedUndo = true
|
||||
op = op._copy(undoOp.deletedStructs, true)
|
||||
op._integrate(y)
|
||||
op._redo(y)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,6 +73,7 @@ export default class UndoManager {
|
||||
this._scope = scope
|
||||
this._undoing = false
|
||||
this._redoing = false
|
||||
this._lastTransactionWasUndo = false
|
||||
const y = scope._y
|
||||
this.y = y
|
||||
y.on('afterTransaction', (y, transaction, remote) => {
|
||||
@@ -75,17 +81,29 @@ export default class UndoManager {
|
||||
let reverseOperation = new ReverseOperation(y, transaction)
|
||||
if (!this._undoing) {
|
||||
let lastUndoOp = this._undoBuffer.length > 0 ? this._undoBuffer[this._undoBuffer.length - 1] : null
|
||||
if (lastUndoOp !== null && reverseOperation.created - lastUndoOp.created <= options.captureTimeout) {
|
||||
if (
|
||||
this._redoing === false &&
|
||||
this._lastTransactionWasUndo === false &&
|
||||
lastUndoOp !== null &&
|
||||
reverseOperation.created - lastUndoOp.created <= options.captureTimeout
|
||||
) {
|
||||
lastUndoOp.created = reverseOperation.created
|
||||
lastUndoOp.toState = reverseOperation.toState
|
||||
if (reverseOperation.toState !== null) {
|
||||
lastUndoOp.toState = reverseOperation.toState
|
||||
if (lastUndoOp.fromState === null) {
|
||||
lastUndoOp.fromState = reverseOperation.fromState
|
||||
}
|
||||
}
|
||||
reverseOperation.deletedStructs.forEach(lastUndoOp.deletedStructs.add, lastUndoOp.deletedStructs)
|
||||
} else {
|
||||
this._lastTransactionWasUndo = false
|
||||
this._undoBuffer.push(reverseOperation)
|
||||
}
|
||||
if (!this._redoing) {
|
||||
this._redoBuffer = []
|
||||
}
|
||||
} else {
|
||||
this._lastTransactionWasUndo = true
|
||||
this._redoBuffer.push(reverseOperation)
|
||||
}
|
||||
}
|
||||
|
||||
188
y.node.js
188
y.node.js
@@ -1,7 +1,7 @@
|
||||
|
||||
/**
|
||||
* yjs - A framework for real-time p2p shared editing on any data
|
||||
* @version v13.0.0-50
|
||||
* @version v13.0.0-55
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
@@ -1340,22 +1340,57 @@ class Item {
|
||||
this._parent = null;
|
||||
this._parentSub = null;
|
||||
this._deleted = false;
|
||||
this._redone = null;
|
||||
}
|
||||
/**
|
||||
* Copy the effect of struct
|
||||
* Create a operation with the same effect (without position effect)
|
||||
*/
|
||||
_copy (undeleteChildren, copyPosition) {
|
||||
let struct = new this.constructor();
|
||||
if (copyPosition) {
|
||||
struct._origin = this._left;
|
||||
struct._left = this._left;
|
||||
struct._right = this;
|
||||
struct._right_origin = this;
|
||||
struct._parent = this._parent;
|
||||
struct._parentSub = this._parentSub;
|
||||
_copy () {
|
||||
return new this.constructor()
|
||||
}
|
||||
/**
|
||||
* Redo the effect of this operation.
|
||||
*/
|
||||
_redo (y) {
|
||||
if (this._redone !== null) {
|
||||
return this._redone
|
||||
}
|
||||
let struct = this._copy();
|
||||
let left = this._left;
|
||||
let right = this;
|
||||
let parent = this._parent;
|
||||
// make sure that parent is redone
|
||||
if (parent._deleted === true && parent._redone === null) {
|
||||
parent._redo(y);
|
||||
}
|
||||
if (parent._redone !== null) {
|
||||
parent = parent._redone;
|
||||
// find next cloned items
|
||||
while (left !== null) {
|
||||
if (left._redone !== null && left._redone._parent === parent) {
|
||||
left = left._redone;
|
||||
break
|
||||
}
|
||||
left = left._left;
|
||||
}
|
||||
while (right !== null) {
|
||||
if (right._redone !== null && right._redone._parent === parent) {
|
||||
right = right._redone;
|
||||
}
|
||||
right = right._right;
|
||||
}
|
||||
}
|
||||
struct._origin = left;
|
||||
struct._left = left;
|
||||
struct._right = right;
|
||||
struct._right_origin = right;
|
||||
struct._parent = parent;
|
||||
struct._parentSub = this._parentSub;
|
||||
struct._integrate(y);
|
||||
this._redone = struct;
|
||||
return struct
|
||||
}
|
||||
|
||||
get _lastId () {
|
||||
return new ID(this._id.user, this._id.clock + this._length - 1)
|
||||
}
|
||||
@@ -1381,11 +1416,15 @@ class Item {
|
||||
if (!this._deleted) {
|
||||
this._deleted = true;
|
||||
y.ds.markDeleted(this._id, this._length);
|
||||
let del = new Delete();
|
||||
del._targetID = this._id;
|
||||
del._length = this._length;
|
||||
if (createDelete) {
|
||||
let del = new Delete();
|
||||
del._targetID = this._id;
|
||||
del._length = this._length;
|
||||
// broadcast and persists Delete
|
||||
del._integrate(y, true);
|
||||
} else if (y.persistence !== null) {
|
||||
// only persist Delete
|
||||
y.persistence.saveStruct(y, del);
|
||||
}
|
||||
transactionTypeChanged(y, this._parent, this._parentSub);
|
||||
y._transaction.deletedStructs.add(this);
|
||||
@@ -1720,40 +1759,6 @@ class Type extends Item {
|
||||
type = type._parent;
|
||||
}
|
||||
}
|
||||
_copy (undeleteChildren, copyPosition) {
|
||||
let copy = super._copy(undeleteChildren, copyPosition);
|
||||
let map = new Map();
|
||||
copy._map = map;
|
||||
for (let [key, value] of this._map) {
|
||||
if (undeleteChildren.has(value) || !value.deleted) {
|
||||
let _item = value._copy(undeleteChildren, false);
|
||||
_item._parent = copy;
|
||||
_item._parentSub = key;
|
||||
map.set(key, _item);
|
||||
}
|
||||
}
|
||||
let prevUndeleted = null;
|
||||
copy._start = null;
|
||||
let item = this._start;
|
||||
while (item !== null) {
|
||||
if (undeleteChildren.has(item) || !item.deleted) {
|
||||
let _item = item._copy(undeleteChildren, false);
|
||||
_item._left = prevUndeleted;
|
||||
_item._origin = prevUndeleted;
|
||||
_item._right = null;
|
||||
_item._right_origin = null;
|
||||
_item._parent = copy;
|
||||
if (prevUndeleted === null) {
|
||||
copy._start = _item;
|
||||
} else {
|
||||
prevUndeleted._right = _item;
|
||||
}
|
||||
prevUndeleted = _item;
|
||||
}
|
||||
item = item._right;
|
||||
}
|
||||
return copy
|
||||
}
|
||||
_transact (f) {
|
||||
const y = this._y;
|
||||
if (y !== null) {
|
||||
@@ -1817,8 +1822,8 @@ class ItemJSON extends Item {
|
||||
super();
|
||||
this._content = null;
|
||||
}
|
||||
_copy (undeleteChildren, copyPosition) {
|
||||
let struct = super._copy(undeleteChildren, copyPosition);
|
||||
_copy () {
|
||||
let struct = super._copy();
|
||||
struct._content = this._content;
|
||||
return struct
|
||||
}
|
||||
@@ -1879,8 +1884,8 @@ class ItemString extends Item {
|
||||
super();
|
||||
this._content = null;
|
||||
}
|
||||
_copy (undeleteChildren, copyPosition) {
|
||||
let struct = super._copy(undeleteChildren, copyPosition);
|
||||
_copy () {
|
||||
let struct = super._copy();
|
||||
struct._content = this._content;
|
||||
return struct
|
||||
}
|
||||
@@ -3121,6 +3126,9 @@ class YXmlElement extends YXmlFragment {
|
||||
super();
|
||||
this.nodeName = null;
|
||||
this._scrollElement = null;
|
||||
if (typeof arg2 === 'function') {
|
||||
this._domFilter = arg2;
|
||||
}
|
||||
if (typeof arg1 === 'string') {
|
||||
this.nodeName = arg1.toUpperCase();
|
||||
} else if (arg1 != null && arg1.nodeType != null && arg1.nodeType === arg1.ELEMENT_NODE) {
|
||||
@@ -3129,12 +3137,9 @@ class YXmlElement extends YXmlFragment {
|
||||
} else {
|
||||
this.nodeName = 'UNDEFINED';
|
||||
}
|
||||
if (typeof arg2 === 'function') {
|
||||
this._domFilter = arg2;
|
||||
}
|
||||
}
|
||||
_copy (undeleteChildren, copyPosition) {
|
||||
let struct = super._copy(undeleteChildren, copyPosition);
|
||||
_copy () {
|
||||
let struct = super._copy();
|
||||
struct.nodeName = this.nodeName;
|
||||
return struct
|
||||
}
|
||||
@@ -3149,9 +3154,10 @@ class YXmlElement extends YXmlFragment {
|
||||
let attributes = new Map();
|
||||
for (let i = 0; i < dom.attributes.length; i++) {
|
||||
let attr = dom.attributes[i];
|
||||
attributes.set(attr.name, attr.value);
|
||||
// get attribute via getAttribute for custom element support (some write something different in attr.value)
|
||||
attributes.set(attr.name, dom.getAttribute(attr.name));
|
||||
}
|
||||
attributes = this._domFilter(dom, attributes);
|
||||
attributes = this._domFilter(dom.nodeName, attributes);
|
||||
attributes.forEach((value, name) => {
|
||||
this.setAttribute(name, value);
|
||||
});
|
||||
@@ -3272,8 +3278,8 @@ class YXmlHook extends YMap {
|
||||
getHook(hookName).fillType(dom, this);
|
||||
}
|
||||
}
|
||||
_copy (undeleteChildren, copyPosition) {
|
||||
const struct = super._copy(undeleteChildren, copyPosition);
|
||||
_copy () {
|
||||
const struct = super._copy();
|
||||
struct.hookName = this.hookName;
|
||||
return struct
|
||||
}
|
||||
@@ -3671,11 +3677,12 @@ class ReverseOperation {
|
||||
constructor (y, transaction) {
|
||||
this.created = new Date();
|
||||
const beforeState = transaction.beforeState;
|
||||
this.toState = new ID(y.userID, y.ss.getState(y.userID) - 1);
|
||||
if (beforeState.has(y.userID)) {
|
||||
this.toState = new ID(y.userID, y.ss.getState(y.userID) - 1);
|
||||
this.fromState = new ID(y.userID, beforeState.get(y.userID));
|
||||
} else {
|
||||
this.fromState = this.toState;
|
||||
this.toState = null;
|
||||
this.fromState = null;
|
||||
}
|
||||
this.deletedStructs = transaction.deletedStructs;
|
||||
}
|
||||
@@ -3697,28 +3704,32 @@ function applyReverseOperation (y, scope, reverseBuffer) {
|
||||
while (!performedUndo && reverseBuffer.length > 0) {
|
||||
let undoOp = reverseBuffer.pop();
|
||||
// make sure that it is possible to iterate {from}-{to}
|
||||
y.os.getItemCleanStart(undoOp.fromState);
|
||||
y.os.getItemCleanEnd(undoOp.toState);
|
||||
y.os.iterate(undoOp.fromState, undoOp.toState, op => {
|
||||
if (!op._deleted && isStructInScope(y, op, scope)) {
|
||||
performedUndo = true;
|
||||
op._delete(y);
|
||||
}
|
||||
});
|
||||
if (undoOp.fromState !== null) {
|
||||
y.os.getItemCleanStart(undoOp.fromState);
|
||||
y.os.getItemCleanEnd(undoOp.toState);
|
||||
y.os.iterate(undoOp.fromState, undoOp.toState, op => {
|
||||
while (op._deleted && op._redone !== null) {
|
||||
op = op._redone;
|
||||
}
|
||||
if (op._deleted === false && isStructInScope(y, op, scope)) {
|
||||
performedUndo = true;
|
||||
op._delete(y);
|
||||
}
|
||||
});
|
||||
}
|
||||
for (let op of undoOp.deletedStructs) {
|
||||
if (
|
||||
isStructInScope(y, op, scope) &&
|
||||
op._parent !== y &&
|
||||
!op._parent._deleted &&
|
||||
(
|
||||
op._parent._id.user !== y.userID ||
|
||||
op._parent._id.clock < undoOp.fromState.clock ||
|
||||
op._parent._id.clock > undoOp.fromState.clock
|
||||
op._id.user !== y.userID ||
|
||||
undoOp.fromState === null ||
|
||||
op._id.clock < undoOp.fromState.clock ||
|
||||
op._id.clock > undoOp.toState.clock
|
||||
)
|
||||
) {
|
||||
performedUndo = true;
|
||||
op = op._copy(undoOp.deletedStructs, true);
|
||||
op._integrate(y);
|
||||
op._redo(y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3735,6 +3746,7 @@ class UndoManager {
|
||||
this._scope = scope;
|
||||
this._undoing = false;
|
||||
this._redoing = false;
|
||||
this._lastTransactionWasUndo = false;
|
||||
const y = scope._y;
|
||||
this.y = y;
|
||||
y.on('afterTransaction', (y, transaction, remote) => {
|
||||
@@ -3742,17 +3754,29 @@ class UndoManager {
|
||||
let reverseOperation = new ReverseOperation(y, transaction);
|
||||
if (!this._undoing) {
|
||||
let lastUndoOp = this._undoBuffer.length > 0 ? this._undoBuffer[this._undoBuffer.length - 1] : null;
|
||||
if (lastUndoOp !== null && reverseOperation.created - lastUndoOp.created <= options.captureTimeout) {
|
||||
if (
|
||||
this._redoing === false &&
|
||||
this._lastTransactionWasUndo === false &&
|
||||
lastUndoOp !== null &&
|
||||
reverseOperation.created - lastUndoOp.created <= options.captureTimeout
|
||||
) {
|
||||
lastUndoOp.created = reverseOperation.created;
|
||||
lastUndoOp.toState = reverseOperation.toState;
|
||||
if (reverseOperation.toState !== null) {
|
||||
lastUndoOp.toState = reverseOperation.toState;
|
||||
if (lastUndoOp.fromState === null) {
|
||||
lastUndoOp.fromState = reverseOperation.fromState;
|
||||
}
|
||||
}
|
||||
reverseOperation.deletedStructs.forEach(lastUndoOp.deletedStructs.add, lastUndoOp.deletedStructs);
|
||||
} else {
|
||||
this._lastTransactionWasUndo = false;
|
||||
this._undoBuffer.push(reverseOperation);
|
||||
}
|
||||
if (!this._redoing) {
|
||||
this._redoBuffer = [];
|
||||
}
|
||||
} else {
|
||||
this._lastTransactionWasUndo = true;
|
||||
this._redoBuffer.push(reverseOperation);
|
||||
}
|
||||
}
|
||||
@@ -4626,8 +4650,10 @@ class AbstractConnector {
|
||||
}
|
||||
|
||||
function fromBinary (y, decoder) {
|
||||
integrateRemoteStructs(y, decoder);
|
||||
readDeleteSet(y, decoder);
|
||||
y.transact(function () {
|
||||
integrateRemoteStructs(y, decoder);
|
||||
readDeleteSet(y, decoder);
|
||||
});
|
||||
}
|
||||
|
||||
function toBinary (y) {
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user