model-layer
Version:
509 lines • 16.5 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Collection = void 0;
const events_1 = require("events");
const Model_1 = require("./Model");
const EqualStack_1 = __importDefault(require("./EqualStack"));
const utils_1 = require("./utils");
const errors_1 = require("./errors");
class Collection extends events_1.EventEmitter {
constructor(rows) {
super();
if (!this.constructor.prototype.hasOwnProperty("ModelConstructor")) {
this.constructor.prototype.ModelConstructor = this.Model();
// prepare model structure without calling constructor
const model = Object.create(this.ModelConstructor.prototype);
model.prepareStructure();
}
this.models = [];
if (rows instanceof Array) {
rows.forEach((row) => {
const model = this.prepareRow(row);
this.models.push(model);
});
this.length = rows.length;
}
else {
this.length = 0;
}
}
at(index, rowOrModel) {
// set
if (rowOrModel) {
const removedModel = this.models[index];
const model = this.prepareRow(rowOrModel);
this.models[index] = model;
this.length = this.models.length;
if (removedModel) {
const removeEvent = {
type: "remove",
collection: this,
model: removedModel
};
this.emit("remove", removeEvent);
}
const addEvent = {
type: "add",
model,
collection: this
};
this.emit("add", addEvent);
}
// get
else {
return this.models[index];
}
}
prepareRow(row) {
let model;
if (row instanceof this.ModelConstructor) {
model = row;
return model;
}
if (row instanceof Model_1.Model) {
throw new errors_1.WrongModelConstructorError({
invalid: row.constructor.name,
expected: this.ModelConstructor.name,
collection: this.constructor.name
});
}
if (utils_1.isPlainObject(row)) {
model = new this.ModelConstructor(row);
}
else {
throw new errors_1.InvalidModelRowError({
model: this.ModelConstructor.name,
invalidValue: utils_1.invalidValuesAsString(row)
});
}
return model;
}
push(...models) {
if (!models.length) {
return;
}
const addedModels = [];
for (let i = 0, n = models.length; i < n; i++) {
const inputModel = models[i];
const model = this.prepareRow(inputModel);
addedModels.push(model);
this.models.push(model);
}
this.length = this.models.length;
for (let i = 0, n = addedModels.length; i < n; i++) {
const model = addedModels[i];
const addEvent = {
type: "add",
model,
collection: this
};
this.emit("add", addEvent);
}
}
add(...models) {
if (!models.length) {
return;
}
let inputModels = [];
for (let i = 0, n = models.length; i < n; i++) {
const modelOrArr = models[i];
if (Array.isArray(modelOrArr)) {
const arr = modelOrArr;
inputModels = inputModels.concat(arr);
}
else {
const model = modelOrArr;
inputModels.push(model);
}
}
const addedModels = [];
for (let i = 0, n = inputModels.length; i < n; i++) {
let model = inputModels[i];
model = this.prepareRow(model);
addedModels.push(model);
this.models.push(model);
}
this.length = this.models.length;
for (let i = 0, n = addedModels.length; i < n; i++) {
const model = addedModels[i];
const addEvent = {
type: "add",
model,
collection: this
};
this.emit("add", addEvent);
}
}
forEach(iteration, context) {
this.models.forEach(iteration, context || this);
}
each(iteration, context) {
this.models.forEach(iteration, context || this);
}
find(iteration, context) {
return this.models.find(iteration, context || this);
}
findIndex(iteration, context) {
return this.models.findIndex(iteration, context || this);
}
filter(iteration, context) {
return this.models.filter(iteration, context || this);
}
map(iteration, context) {
return this.models.map(iteration, context || this);
}
flatMap(iteration, context) {
const result = this.models.map(iteration, context || this);
let output = [];
for (let i = 0, n = result.length; i < n; i++) {
const elem = result[i];
if (Array.isArray(elem)) {
output = output.concat(elem);
}
else {
output.push(elem);
}
}
return output;
}
reduce(iteration, initialValue) {
const reduced = this.models.reduce(iteration, initialValue);
return reduced;
}
reduceRight(iteration, initialValue) {
const reduced = this.models.reduceRight(iteration, initialValue);
return reduced;
}
every(iteration, context) {
return this.models.every(iteration, context || this);
}
some(iteration, context) {
return this.models.some(iteration, context || this);
}
slice(begin, end) {
return this.models.slice(begin, end);
}
flat() {
return this.models.slice();
}
indexOf(searchElement, fromIndex) {
return this.models.indexOf(searchElement, fromIndex);
}
lastIndexOf(searchElement, fromIndex) {
if (arguments.length === 2) {
return this.models.lastIndexOf(searchElement, fromIndex);
}
else {
return this.models.lastIndexOf(searchElement);
}
}
includes(searchElement, fromIndex) {
return this.models.includes(searchElement, fromIndex);
}
pop() {
const model = this.models.pop();
this.length = this.models.length;
if (model) {
const removeEvent = {
type: "remove",
collection: this,
model
};
this.emit("remove", removeEvent);
}
return model;
}
shift() {
const model = this.models.shift();
this.length = this.models.length;
if (model) {
const removeEvent = {
type: "remove",
collection: this,
model
};
this.emit("remove", removeEvent);
}
return model;
}
unshift(...models) {
if (!models.length) {
return;
}
const preparedModels = [];
for (let i = 0, n = models.length; i < n; i++) {
const row = models[i];
const model = this.prepareRow(row);
preparedModels.push(model);
}
this.models.unshift.apply(this.models, preparedModels);
this.length = this.models.length;
for (let i = 0, n = preparedModels.length; i < n; i++) {
const model = preparedModels[i];
const addEvent = {
type: "add",
model,
collection: this
};
this.emit("add", addEvent);
}
}
sort(compareFunctionOrKey, ...otherKeys) {
if (typeof compareFunctionOrKey === "string") {
const firstKey = compareFunctionOrKey;
// order by key asc
if (!otherKeys.length) {
const key = firstKey;
this.models.sort((modelA, modelB) => {
const valueA = modelA.get(key);
const valueB = modelB.get(key);
return valueA > valueB ? 1 : -1;
});
}
// order by key1 asc, key2 asc, ...
else {
const keys = [firstKey].concat(otherKeys);
this.models.sort((modelA, modelB) => {
for (let i = 0, n = keys.length; i < n; i++) {
const key = keys[i];
const valueA = modelA.get(key);
const valueB = modelB.get(key);
if (valueA > valueB) {
return 1;
}
if (valueA < valueB) {
return -1;
}
}
return 0;
});
}
}
// sort by compareFunction( (modelA, modelB) => ... )
else if (typeof compareFunctionOrKey === "function") {
const compareFunction = compareFunctionOrKey;
this.models.sort(compareFunction);
}
else {
const invalidValue = utils_1.invalidValuesAsString(compareFunctionOrKey);
throw new errors_1.InvalidSortParamsError({
invalidValue
});
}
}
reverse() {
this.models.reverse();
return this;
}
concat(...values) {
const CustomCollection = this.constructor;
let outputModels = this.models;
for (let i = 0, n = values.length; i < n; i++) {
const rowsOrCollection = values[i];
if (rowsOrCollection instanceof Collection) {
const collection = rowsOrCollection;
outputModels = outputModels.concat(collection.models);
}
else {
const rows = rowsOrCollection;
const models = rows.map((row) => this.prepareRow(row));
outputModels = outputModels.concat(models);
}
}
return new CustomCollection(outputModels);
}
join(separator = ",") {
return this.models.join(separator);
}
// https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/fill
fill(row, start, end) {
// Step 3-5.
// tslint:disable-next-line: no-bitwise
const len = this.length >>> 0;
// Step 6-7.
start = arguments[1];
// tslint:disable-next-line: no-bitwise
const relativeStart = start >> 0;
// Step 8.
let k = relativeStart < 0 ?
Math.max(len + relativeStart, 0) :
Math.min(relativeStart, len);
// Step 9-10.
end = arguments[2];
const relativeEnd = end === undefined ?
// tslint:disable-next-line: no-bitwise
len : end >> 0;
// Step 11.
const final = relativeEnd < 0 ?
Math.max(len + relativeEnd, 0) :
Math.min(relativeEnd, len);
// Step 12.
const addedModels = [];
while (k < final) {
const model = this.prepareRow(row);
addedModels.push(model);
this.models[k] = model;
k++;
}
for (let i = 0, n = addedModels.length; i < n; i++) {
const model = addedModels[i];
const addEvent = {
type: "add",
model,
collection: this
};
this.emit("add", addEvent);
}
// Step 13.
return this;
}
splice(start, deleteCount, ...inputItems) {
let items;
if (inputItems && inputItems.length) {
items = inputItems.map((row) => this.prepareRow(row));
}
else {
items = [];
}
const removedModels = this.models.slice(start, start + deleteCount);
this.models.splice(start, deleteCount, ...items);
this.length = this.models.length;
for (let i = 0, n = removedModels.length; i < n; i++) {
const model = removedModels[i];
const removeEvent = {
type: "remove",
collection: this,
model
};
this.emit("remove", removeEvent);
}
for (let i = 0, n = items.length; i < n; i++) {
const model = items[i];
const addEvent = {
type: "add",
model,
collection: this
};
this.emit("add", addEvent);
}
}
reset() {
const removedModels = this.models.slice();
this.models = [];
this.length = 0;
for (let i = 0, n = removedModels.length; i < n; i++) {
const model = removedModels[i];
const removeEvent = {
type: "remove",
collection: this,
model
};
this.emit("remove", removeEvent);
}
}
first() {
return this.models[0];
}
last() {
return this.models[this.models.length - 1];
}
create(row) {
const model = this.prepareRow(row);
this.models.push(model);
this.length = this.models.length;
const addEvent = {
type: "add",
model,
collection: this
};
this.emit("add", addEvent);
return model;
}
toJSON(stack = []) {
return this.models.map((model) => model.toJSON(stack));
}
clone(stack) {
stack = stack || new EqualStack_1.default();
const existsClone = stack.get(this);
if (existsClone) {
return existsClone;
}
const clone = Object.create(this.constructor.prototype);
stack.add(this, clone);
const models = this.models.map((model) => model.clone(stack));
clone.models = models;
clone.length = models.length;
return clone;
}
remove(idOrModel) {
let index = -1;
let removedModel;
if (idOrModel instanceof Model_1.Model) {
const model = idOrModel;
index = this.models.indexOf(model);
removedModel = model;
}
else {
const id = idOrModel;
index = this.models.findIndex((model) => model.primaryValue === id);
removedModel = this.models[index];
}
if (index !== -1) {
this.models.splice(index, 1);
this.length = this.models.length;
}
if (removedModel) {
const removeEvent = {
type: "remove",
collection: this,
model: removedModel
};
this.emit("remove", removeEvent);
}
}
get(id) {
return this.find((model) => model.primaryValue === id);
}
equal(otherCollection, stack) {
if (!(otherCollection instanceof Collection) &&
!Array.isArray(otherCollection)) {
return false;
}
if (this.length !== otherCollection.length) {
return false;
}
stack = stack || new EqualStack_1.default();
// stop circular recursion
const stacked = stack.get(this);
if (stacked) {
return stacked === otherCollection;
}
stack.add(this, otherCollection);
for (let i = 0, n = this.length; i < n; i++) {
const selfModel = this.at(i);
const otherModel = otherCollection instanceof Collection ?
otherCollection.at(i) :
otherCollection[i];
const isEqual = selfModel.equal(otherModel, stack);
if (!isEqual) {
return false;
}
}
return true;
}
on(eventName, handler) {
super.on(eventName, handler);
}
}
exports.Collection = Collection;
// for js
Collection.prototype.Model = function () {
throw new errors_1.CollectionShouldHaveModelError({
className: this.constructor.name
});
};
exports.default = Collection;
//# sourceMappingURL=Collection.js.map