jupyterlab-emrys
Version:
A computational environment for Jupyter. Powered by Emrys
301 lines (300 loc) • 9.84 kB
JavaScript
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
"use strict";
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var phosphor_signaling_1 = require('phosphor-signaling');
var observablelist_1 = require('../../common/observablelist');
/**
* An observable list that supports undo/redo.
*/
var ObservableUndoableList = (function (_super) {
__extends(ObservableUndoableList, _super);
/**
* Construct a new undoable observable list.
*/
function ObservableUndoableList(factory) {
_super.call(this);
this._inCompound = false;
this._isUndoable = true;
this._madeCompoundChange = false;
this._index = -1;
this._stack = [];
this._factory = null;
this._factory = factory;
this.changed.connect(this._onListChanged, this);
}
Object.defineProperty(ObservableUndoableList.prototype, "canRedo", {
/**
* Whether the object can redo changes.
*
* #### Notes
* This is a read-only property.
*/
get: function () {
return this._index < this._stack.length - 1;
},
enumerable: true,
configurable: true
});
Object.defineProperty(ObservableUndoableList.prototype, "canUndo", {
/**
* Whether the object can undo changes.
*
* #### Notes
* This is a read-only property.
*/
get: function () {
return this._index >= 0;
},
enumerable: true,
configurable: true
});
Object.defineProperty(ObservableUndoableList.prototype, "isDisposed", {
/**
* Get whether the object is disposed.
*
* #### Notes
* This is a read-only property.
*/
get: function () {
return this._stack === null;
},
enumerable: true,
configurable: true
});
/**
* Dispose of the resources held by the model.
*/
ObservableUndoableList.prototype.dispose = function () {
// Do nothing if already disposed.
if (this.isDisposed) {
return;
}
phosphor_signaling_1.clearSignalData(this);
this._factory = null;
this._stack = null;
};
/**
* Begin a compound operation.
*/
ObservableUndoableList.prototype.beginCompoundOperation = function (isUndoAble) {
this._inCompound = true;
this._isUndoable = (isUndoAble !== false);
this._madeCompoundChange = false;
};
/**
* End a compound operation.
*/
ObservableUndoableList.prototype.endCompoundOperation = function () {
this._inCompound = false;
this._isUndoable = true;
if (this._madeCompoundChange) {
this._index++;
}
};
/**
* Undo an operation.
*/
ObservableUndoableList.prototype.undo = function () {
if (!this.canUndo) {
return;
}
var changes = this._stack[this._index];
this._isUndoable = false;
for (var _i = 0, _a = changes.reverse(); _i < _a.length; _i++) {
var change = _a[_i];
this._undoChange(change);
}
this._isUndoable = true;
this._index--;
};
/**
* Redo an operation.
*/
ObservableUndoableList.prototype.redo = function () {
if (!this.canRedo) {
return;
}
this._index++;
var changes = this._stack[this._index];
this._isUndoable = false;
for (var _i = 0, changes_1 = changes; _i < changes_1.length; _i++) {
var change = changes_1[_i];
this._redoChange(change);
}
this._isUndoable = true;
};
/**
* Clear the change stack.
*/
ObservableUndoableList.prototype.clearUndo = function () {
this._index = -1;
this._stack = [];
};
/**
* Handle a change in the list.
*/
ObservableUndoableList.prototype._onListChanged = function (list, change) {
if (!this._isUndoable) {
return;
}
// Clear everything after this position if necessary.
if (!this._inCompound || !this._madeCompoundChange) {
this._stack = this._stack.slice(0, this._index + 1);
}
// Copy the change.
var evt = this._copyChange(change);
// Put the change in the stack.
if (this._stack[this._index + 1]) {
this._stack[this._index + 1].push(evt);
}
else {
this._stack.push([evt]);
}
// If not in a compound operation, increase index.
if (!this._inCompound) {
this._index++;
}
else {
this._madeCompoundChange = true;
}
};
/**
* Undo a change event.
*/
ObservableUndoableList.prototype._undoChange = function (change) {
var value;
switch (change.type) {
case observablelist_1.ListChangeType.Add:
this.removeAt(change.newIndex);
break;
case observablelist_1.ListChangeType.Set:
value = this._createValue(change.oldValue);
this.set(change.oldIndex, value);
break;
case observablelist_1.ListChangeType.Remove:
value = this._createValue(change.oldValue);
this.insert(change.oldIndex, value);
break;
case observablelist_1.ListChangeType.Move:
this.move(change.newIndex, change.oldIndex);
break;
case observablelist_1.ListChangeType.Replace:
var len = change.newValue.length;
var values = this._createValues(change.oldValue);
this.replace(change.oldIndex, len, values);
break;
default:
return;
}
};
/**
* Redo a change event.
*/
ObservableUndoableList.prototype._redoChange = function (change) {
var value;
switch (change.type) {
case observablelist_1.ListChangeType.Add:
value = this._createValue(change.newValue);
this.insert(change.newIndex, value);
break;
case observablelist_1.ListChangeType.Set:
value = this._createValue(change.newValue);
this.set(change.newIndex, value);
break;
case observablelist_1.ListChangeType.Remove:
this.removeAt(change.oldIndex);
break;
case observablelist_1.ListChangeType.Move:
this.move(change.oldIndex, change.newIndex);
break;
case observablelist_1.ListChangeType.Replace:
var len = change.oldValue.length;
var cells = this._createValues(change.newValue);
this.replace(change.oldIndex, len, cells);
break;
default:
return;
}
};
/**
* Create a value from JSON.
*/
ObservableUndoableList.prototype._createValue = function (data) {
var factory = this._factory;
return factory(data);
};
/**
* Create a list of cell models from JSON.
*/
ObservableUndoableList.prototype._createValues = function (bundles) {
var values = [];
for (var _i = 0, bundles_1 = bundles; _i < bundles_1.length; _i++) {
var bundle = bundles_1[_i];
values.push(this._createValue(bundle));
}
return values;
};
/**
* Copy a change as JSON.
*/
ObservableUndoableList.prototype._copyChange = function (change) {
if (change.type === observablelist_1.ListChangeType.Replace) {
return this._copyReplace(change);
}
var oldValue = null;
var newValue = null;
switch (change.type) {
case observablelist_1.ListChangeType.Add:
case observablelist_1.ListChangeType.Set:
case observablelist_1.ListChangeType.Remove:
if (change.oldValue) {
oldValue = change.oldValue.toJSON();
}
if (change.newValue) {
newValue = change.newValue.toJSON();
}
break;
case observablelist_1.ListChangeType.Move:
// Only need the indices.
break;
default:
return;
}
return {
type: change.type,
oldIndex: change.oldIndex,
newIndex: change.newIndex,
oldValue: oldValue,
newValue: newValue
};
};
/**
* Copy a replace change as JSON.
*/
ObservableUndoableList.prototype._copyReplace = function (change) {
var oldValue = [];
for (var _i = 0, _a = change.oldValue; _i < _a.length; _i++) {
var value = _a[_i];
oldValue.push(value.toJSON());
}
var newValue = [];
for (var _b = 0, _c = change.newValue; _b < _c.length; _b++) {
var value = _c[_b];
newValue.push(value.toJSON());
}
return {
type: observablelist_1.ListChangeType.Replace,
oldIndex: change.oldIndex,
newIndex: change.newIndex,
oldValue: oldValue,
newValue: newValue
};
};
return ObservableUndoableList;
}(observablelist_1.ObservableList));
exports.ObservableUndoableList = ObservableUndoableList;