jupyterlab-emrys
Version:
A computational environment for Jupyter. Powered by Emrys
419 lines (418 loc) • 14 kB
JavaScript
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
/*-----------------------------------------------------------------------------
| Copyright (c) 2014-2015, PhosphorJS Contributors
|
| Distributed under the terms of the BSD 3-Clause License.
|
| The full license is in the file LICENSE, distributed with this software.
|----------------------------------------------------------------------------*/
'use strict';
var arrays = require('phosphor-arrays');
var phosphor_signaling_1 = require('phosphor-signaling');
/**
* An enum of the change types which occur on an observable list.
*/
(function (ListChangeType) {
/**
* An item was added to the list.
*/
ListChangeType[ListChangeType["Add"] = 0] = "Add";
/**
* An item was moved in the list.
*/
ListChangeType[ListChangeType["Move"] = 1] = "Move";
/**
* An item was removed from the list.
*/
ListChangeType[ListChangeType["Remove"] = 2] = "Remove";
/**
* Items were replaced in the list.
*/
ListChangeType[ListChangeType["Replace"] = 3] = "Replace";
/**
* An item was set in the list.
*/
ListChangeType[ListChangeType["Set"] = 4] = "Set";
})(exports.ListChangeType || (exports.ListChangeType = {}));
var ListChangeType = exports.ListChangeType;
/**
* A concrete implementation of [[IObservableList]].
*/
var ObservableList = (function () {
/**
* Construct a new observable list.
*
* @param items - The initial items for the list.
*/
function ObservableList(items) {
this.internal = items ? items.slice() : [];
}
Object.defineProperty(ObservableList.prototype, "changed", {
/**
* A signal emitted when the list has changed.
*
* #### Notes
* This is a pure delegate to the [[changedSignal]].
*/
get: function () {
return ObservableList.changedSignal.bind(this);
},
enumerable: true,
configurable: true
});
Object.defineProperty(ObservableList.prototype, "length", {
/**
* The number of items in the list.
*
* #### Notes
* This is a read-only property.
*/
get: function () {
return this.internal.length;
},
enumerable: true,
configurable: true
});
/**
* Get the item at a specific index in the list.
*
* @param index - The index of the item of interest. If this is
* negative, it is offset from the end of the list.
*
* @returns The item at the specified index, or `undefined` if the
* index is out of range.
*/
ObservableList.prototype.get = function (index) {
return this.internal[this._norm(index)];
};
/**
* Test whether the list contains a specific item.
*
* @param item - The item of interest.
*
* @returns `true` if the list contains the item, `false` otherwise.
*/
ObservableList.prototype.contains = function (item) {
return this.internal.indexOf(item) !== -1;
};
/**
* Get the index of the first occurence of an item in the list.
*
* @param item - The item of interest.
*
* @returns The index of the specified item or `-1` if the item is
* not contained in the list.
*/
ObservableList.prototype.indexOf = function (item) {
return this.internal.indexOf(item);
};
/**
* Get a shallow copy of a portion of the list.
*
* @param start - The start index of the slice, inclusive. If this is
* negative, it is offset from the end of the list. If this is not
* provided, it defaults to `0`. In all cases, it is clamped to the
* bounds of the list.
*
* @param end - The end index of the slice, exclusive. If this is
* negative, it is offset from the end of the list. If this is not
* provided, it defaults to `length`. In all cases, it is clamped
* to the bounds of the list.
*
* @returns A new array containing the specified range of items.
*/
ObservableList.prototype.slice = function (start, end) {
return this.internal.slice(start, end);
};
/**
* Set the item at a specific index.
*
* @param index - The index of interest. If this is negative, it is
* offset from the end of the list.
*
* @param item - The item to set at the index.
*
* @returns The item which occupied the index, or `undefined` if the
* index is out of range.
*/
ObservableList.prototype.set = function (index, item) {
var i = this._norm(index);
if (!this._check(i))
return void 0;
return this.setItem(i, item);
};
/**
* Replace the contents of the list with the specified items.
*
* @param items - The items to assign to the list.
*
* @returns An array of the previous list items.
*
* #### Notes
* This is equivalent to `list.replace(0, list.length, items)`.
*/
ObservableList.prototype.assign = function (items) {
return this.replaceItems(0, this.internal.length, items);
};
/**
* Add an item to the end of the list.
*
* @param item - The item to add to the list.
*
* @returns The index at which the item was added.
*/
ObservableList.prototype.add = function (item) {
return this.addItem(this.internal.length, item);
};
/**
* Insert an item into the list at a specific index.
*
* @param index - The index at which to insert the item. If this is
* negative, it is offset from the end of the list. In all cases,
* it is clamped to the bounds of the list.
*
* @param item - The item to insert into the list.
*
* @returns The index at which the item was inserted.
*/
ObservableList.prototype.insert = function (index, item) {
return this.addItem(this._clamp(index), item);
};
/**
* Move an item from one index to another.
*
* @param fromIndex - The index of the item of interest. If this is
* negative, it is offset from the end of the list.
*
* @param toIndex - The desired index for the item. If this is
* negative, it is offset from the end of the list.
*
* @returns `true` if the item was moved, `false` otherwise.
*/
ObservableList.prototype.move = function (fromIndex, toIndex) {
var i = this._norm(fromIndex);
if (!this._check(i))
return false;
var j = this._norm(toIndex);
if (!this._check(j))
return false;
return this.moveItem(i, j);
};
/**
* Remove the first occurrence of a specific item from the list.
*
* @param item - The item to remove from the list.
*
* @return The index occupied by the item, or `-1` if the item is
* not contained in the list.
*/
ObservableList.prototype.remove = function (item) {
var i = this.internal.indexOf(item);
if (i !== -1)
this.removeItem(i);
return i;
};
/**
* Remove the item at a specific index.
*
* @param index - The index of the item of interest. If this is
* negative, it is offset from the end of the list.
*
* @returns The item at the specified index, or `undefined` if the
* index is out of range.
*/
ObservableList.prototype.removeAt = function (index) {
var i = this._norm(index);
if (!this._check(i))
return void 0;
return this.removeItem(i);
};
/**
* Replace items at a specific location in the list.
*
* @param index - The index at which to modify the list. If this is
* negative, it is offset from the end of the list. In all cases,
* it is clamped to the bounds of the list.
*
* @param count - The number of items to remove at the given index.
* This is clamped to the length of the list.
*
* @param items - The items to insert at the specified index.
*
* @returns An array of the items removed from the list.
*/
ObservableList.prototype.replace = function (index, count, items) {
return this.replaceItems(this._norm(index), this._limit(count), items);
};
/**
* Remove all items from the list.
*
* @returns An array of the items removed from the list.
*
* #### Notes
* This is equivalent to `list.replace(0, list.length, [])`.
*/
ObservableList.prototype.clear = function () {
return this.replaceItems(0, this.internal.length, []);
};
/**
* Add an item to the list at the specified index.
*
* @param index - The index at which to add the item. This must be
* an integer in the range `[0, internal.length]`.
*
* @param item - The item to add at the specified index.
*
* @returns The index at which the item was added.
*
* #### Notes
* This may be reimplemented by subclasses to customize the behavior.
*/
ObservableList.prototype.addItem = function (index, item) {
var i = arrays.insert(this.internal, index, item);
this.changed.emit({
type: ListChangeType.Add,
newIndex: i,
newValue: item,
oldIndex: -1,
oldValue: void 0,
});
return i;
};
/**
* Move an item in the list from one index to another.
*
* @param fromIndex - The initial index of the item. This must be
* an integer in the range `[0, internal.length)`.
*
* @param toIndex - The desired index for the item. This must be
* an integer in the range `[0, internal.length)`.
*
* @returns `true` if the item was moved, `false` otherwise.
*
* #### Notes
* This may be reimplemented by subclasses to customize the behavior.
*/
ObservableList.prototype.moveItem = function (fromIndex, toIndex) {
if (!arrays.move(this.internal, fromIndex, toIndex)) {
return false;
}
var item = this.internal[toIndex];
this.changed.emit({
type: ListChangeType.Move,
newIndex: toIndex,
newValue: item,
oldIndex: fromIndex,
oldValue: item,
});
return true;
};
/**
* Remove the item from the list at the specified index.
*
* @param index - The index of the item to remove. This must be
* an integer in the range `[0, internal.length)`.
*
* @returns The item removed from the list.
*
* #### Notes
* This may be reimplemented by subclasses to customize the behavior.
*/
ObservableList.prototype.removeItem = function (index) {
var item = arrays.removeAt(this.internal, index);
this.changed.emit({
type: ListChangeType.Remove,
newIndex: -1,
newValue: void 0,
oldIndex: index,
oldValue: item,
});
return item;
};
/**
* Replace items at a specific location in the list.
*
* @param index - The index at which to modify the list. This must
* be an integer in the range `[0, internal.length]`.
*
* @param count - The number of items to remove from the list. This
* must be an integer in the range `[0, internal.length]`.
*
* @param items - The items to insert at the specified index.
*
* @returns An array of the items removed from the list.
*
* #### Notes
* This may be reimplemented by subclasses to customize the behavior.
*/
ObservableList.prototype.replaceItems = function (index, count, items) {
var old = (_a = this.internal).splice.apply(_a, [index, count].concat(items));
this.changed.emit({
type: ListChangeType.Replace,
newIndex: index,
newValue: items,
oldIndex: index,
oldValue: old,
});
return old;
var _a;
};
/**
* Set the item at a specific index in the list.
*
* @param index - The index of interest. This must be an integer in
* the range `[0, internal.length)`.
*
* @param item - The item to set at the index.
*
* @returns The item which previously occupied the specified index.
*
* #### Notes
* This may be reimplemented by subclasses to customize the behavior.
*/
ObservableList.prototype.setItem = function (index, item) {
var old = this.internal[index];
this.internal[index] = item;
this.changed.emit({
type: ListChangeType.Set,
newIndex: index,
newValue: item,
oldIndex: index,
oldValue: old,
});
return old;
};
/**
* Normalize an index and offset negative values from the list end.
*/
ObservableList.prototype._norm = function (i) {
return i < 0 ? Math.floor(i) + this.internal.length : Math.floor(i);
};
/**
* Check whether a normalized index is in range.
*/
ObservableList.prototype._check = function (i) {
return i >= 0 && i < this.internal.length;
};
/**
* Normalize and clamp an index to the list bounds.
*/
ObservableList.prototype._clamp = function (i) {
return Math.max(0, Math.min(this._norm(i), this.internal.length));
};
/**
* Normalize and limit a count to the length of the list.
*/
ObservableList.prototype._limit = function (c) {
return Math.max(0, Math.min(Math.floor(c), this.internal.length));
};
/**
* A signal emitted when the list has changed.
*
* **See also:** [[changed]]
*/
ObservableList.changedSignal = new phosphor_signaling_1.Signal();
return ObservableList;
}());
exports.ObservableList = ObservableList;