package.Collection.js Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ol Show documentation
Show all versions of ol Show documentation
OpenLayers mapping library
The newest version!
/**
* @module ol/Collection
*/
import BaseObject from './Object.js';
import CollectionEventType from './CollectionEventType.js';
import Event from './events/Event.js';
/**
* @enum {string}
* @private
*/
const Property = {
LENGTH: 'length',
};
/**
* @classdesc
* Events emitted by {@link module:ol/Collection~Collection} instances are instances of this
* type.
* @template T
*/
export class CollectionEvent extends Event {
/**
* @param {import("./CollectionEventType.js").default} type Type.
* @param {T} element Element.
* @param {number} index The index of the added or removed element.
*/
constructor(type, element, index) {
super(type);
/**
* The element that is added to or removed from the collection.
* @type {T}
* @api
*/
this.element = element;
/**
* The index of the added or removed element.
* @type {number}
* @api
*/
this.index = index;
}
}
/***
* @template T
* @template Return
* @typedef {import("./Observable").OnSignature &
* import("./Observable").OnSignature &
* import("./Observable").OnSignature<'add'|'remove', CollectionEvent, Return> &
* import("./Observable").CombinedOnSignature} CollectionOnSignature
*/
/**
* @typedef {Object} Options
* @property {boolean} [unique=false] Disallow the same item from being added to
* the collection twice.
*/
/**
* @classdesc
* An expanded version of standard JS Array, adding convenience methods for
* manipulation. Add and remove changes to the Collection trigger a Collection
* event. Note that this does not cover changes to the objects _within_ the
* Collection; they trigger events on the appropriate object, not on the
* Collection as a whole.
*
* @fires CollectionEvent
*
* @template T
* @api
*/
class Collection extends BaseObject {
/**
* @param {Array} [array] Array.
* @param {Options} [options] Collection options.
*/
constructor(array, options) {
super();
/***
* @type {CollectionOnSignature}
*/
this.on;
/***
* @type {CollectionOnSignature}
*/
this.once;
/***
* @type {CollectionOnSignature}
*/
this.un;
options = options || {};
/**
* @private
* @type {boolean}
*/
this.unique_ = !!options.unique;
/**
* @private
* @type {!Array}
*/
this.array_ = array ? array : [];
if (this.unique_) {
for (let i = 0, ii = this.array_.length; i < ii; ++i) {
this.assertUnique_(this.array_[i], i);
}
}
this.updateLength_();
}
/**
* Remove all elements from the collection.
* @api
*/
clear() {
while (this.getLength() > 0) {
this.pop();
}
}
/**
* Add elements to the collection. This pushes each item in the provided array
* to the end of the collection.
* @param {!Array} arr Array.
* @return {Collection} This collection.
* @api
*/
extend(arr) {
for (let i = 0, ii = arr.length; i < ii; ++i) {
this.push(arr[i]);
}
return this;
}
/**
* Iterate over each element, calling the provided callback.
* @param {function(T, number, Array): *} f The function to call
* for every element. This function takes 3 arguments (the element, the
* index and the array). The return value is ignored.
* @api
*/
forEach(f) {
const array = this.array_;
for (let i = 0, ii = array.length; i < ii; ++i) {
f(array[i], i, array);
}
}
/**
* Get a reference to the underlying Array object. Warning: if the array
* is mutated, no events will be dispatched by the collection, and the
* collection's "length" property won't be in sync with the actual length
* of the array.
* @return {!Array} Array.
* @api
*/
getArray() {
return this.array_;
}
/**
* Get the element at the provided index.
* @param {number} index Index.
* @return {T} Element.
* @api
*/
item(index) {
return this.array_[index];
}
/**
* Get the length of this collection.
* @return {number} The length of the array.
* @observable
* @api
*/
getLength() {
return this.get(Property.LENGTH);
}
/**
* Insert an element at the provided index.
* @param {number} index Index.
* @param {T} elem Element.
* @api
*/
insertAt(index, elem) {
if (index < 0 || index > this.getLength()) {
throw new Error('Index out of bounds: ' + index);
}
if (this.unique_) {
this.assertUnique_(elem);
}
this.array_.splice(index, 0, elem);
this.updateLength_();
this.dispatchEvent(
new CollectionEvent(CollectionEventType.ADD, elem, index),
);
}
/**
* Remove the last element of the collection and return it.
* Return `undefined` if the collection is empty.
* @return {T|undefined} Element.
* @api
*/
pop() {
return this.removeAt(this.getLength() - 1);
}
/**
* Insert the provided element at the end of the collection.
* @param {T} elem Element.
* @return {number} New length of the collection.
* @api
*/
push(elem) {
if (this.unique_) {
this.assertUnique_(elem);
}
const n = this.getLength();
this.insertAt(n, elem);
return this.getLength();
}
/**
* Remove the first occurrence of an element from the collection.
* @param {T} elem Element.
* @return {T|undefined} The removed element or undefined if none found.
* @api
*/
remove(elem) {
const arr = this.array_;
for (let i = 0, ii = arr.length; i < ii; ++i) {
if (arr[i] === elem) {
return this.removeAt(i);
}
}
return undefined;
}
/**
* Remove the element at the provided index and return it.
* Return `undefined` if the collection does not contain this index.
* @param {number} index Index.
* @return {T|undefined} Value.
* @api
*/
removeAt(index) {
if (index < 0 || index >= this.getLength()) {
return undefined;
}
const prev = this.array_[index];
this.array_.splice(index, 1);
this.updateLength_();
this.dispatchEvent(
/** @type {CollectionEvent} */ (
new CollectionEvent(CollectionEventType.REMOVE, prev, index)
),
);
return prev;
}
/**
* Set the element at the provided index.
* @param {number} index Index.
* @param {T} elem Element.
* @api
*/
setAt(index, elem) {
const n = this.getLength();
if (index >= n) {
this.insertAt(index, elem);
return;
}
if (index < 0) {
throw new Error('Index out of bounds: ' + index);
}
if (this.unique_) {
this.assertUnique_(elem, index);
}
const prev = this.array_[index];
this.array_[index] = elem;
this.dispatchEvent(
/** @type {CollectionEvent} */ (
new CollectionEvent(CollectionEventType.REMOVE, prev, index)
),
);
this.dispatchEvent(
/** @type {CollectionEvent} */ (
new CollectionEvent(CollectionEventType.ADD, elem, index)
),
);
}
/**
* @private
*/
updateLength_() {
this.set(Property.LENGTH, this.array_.length);
}
/**
* @private
* @param {T} elem Element.
* @param {number} [except] Optional index to ignore.
*/
assertUnique_(elem, except) {
for (let i = 0, ii = this.array_.length; i < ii; ++i) {
if (this.array_[i] === elem && i !== except) {
throw new Error('Duplicate item added to a unique collection');
}
}
}
}
export default Collection;