forked from mystiq/hydrogen-web
Merge pull request #606 from vector-im/bwindels/typescript-observable-2
Typescript conversion of yet more observables
This commit is contained in:
commit
08314bd4b5
13 changed files with 181 additions and 168 deletions
|
@ -188,7 +188,7 @@ import {NullLogItem, NullLogger} from "../../../../logging/NullLogger";
|
||||||
import {HomeServer as MockHomeServer} from "../../../../mocks/HomeServer.js";
|
import {HomeServer as MockHomeServer} from "../../../../mocks/HomeServer.js";
|
||||||
// other imports
|
// other imports
|
||||||
import {BaseMessageTile} from "./tiles/BaseMessageTile.js";
|
import {BaseMessageTile} from "./tiles/BaseMessageTile.js";
|
||||||
import {MappedList} from "../../../../observable/list/MappedList.js";
|
import {MappedList} from "../../../../observable/list/MappedList";
|
||||||
import {ObservableValue} from "../../../../observable/ObservableValue";
|
import {ObservableValue} from "../../../../observable/ObservableValue";
|
||||||
import {PowerLevels} from "../../../../matrix/room/PowerLevels.js";
|
import {PowerLevels} from "../../../../matrix/room/PowerLevels.js";
|
||||||
|
|
||||||
|
|
|
@ -253,7 +253,7 @@ export class TilesCollection extends BaseObservableList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
import {ObservableArray} from "../../../../observable/list/ObservableArray.js";
|
import {ObservableArray} from "../../../../observable/list/ObservableArray";
|
||||||
import {UpdateAction} from "./UpdateAction.js";
|
import {UpdateAction} from "./UpdateAction.js";
|
||||||
|
|
||||||
export function tests() {
|
export function tests() {
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {SortedArray} from "../../../observable/list/SortedArray.js";
|
import {SortedArray} from "../../../observable/list/SortedArray";
|
||||||
import {ConnectionError} from "../../error.js";
|
import {ConnectionError} from "../../error.js";
|
||||||
import {PendingEvent, SendStatus} from "./PendingEvent.js";
|
import {PendingEvent, SendStatus} from "./PendingEvent.js";
|
||||||
import {makeTxnId, isTxnId} from "../../common.js";
|
import {makeTxnId, isTxnId} from "../../common.js";
|
||||||
|
|
|
@ -20,11 +20,11 @@ import {MappedMap} from "./map/MappedMap.js";
|
||||||
import {JoinedMap} from "./map/JoinedMap.js";
|
import {JoinedMap} from "./map/JoinedMap.js";
|
||||||
import {BaseObservableMap} from "./map/BaseObservableMap.js";
|
import {BaseObservableMap} from "./map/BaseObservableMap.js";
|
||||||
// re-export "root" (of chain) collections
|
// re-export "root" (of chain) collections
|
||||||
export { ObservableArray } from "./list/ObservableArray.js";
|
export { ObservableArray } from "./list/ObservableArray";
|
||||||
export { SortedArray } from "./list/SortedArray.js";
|
export { SortedArray } from "./list/SortedArray";
|
||||||
export { MappedList } from "./list/MappedList.js";
|
export { MappedList } from "./list/MappedList";
|
||||||
export { AsyncMappedList } from "./list/AsyncMappedList.js";
|
export { AsyncMappedList } from "./list/AsyncMappedList";
|
||||||
export { ConcatList } from "./list/ConcatList.js";
|
export { ConcatList } from "./list/ConcatList";
|
||||||
export { ObservableMap } from "./map/ObservableMap.js";
|
export { ObservableMap } from "./map/ObservableMap.js";
|
||||||
|
|
||||||
// avoid circular dependency between these classes
|
// avoid circular dependency between these classes
|
||||||
|
|
|
@ -15,15 +15,14 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {BaseMappedList, runAdd, runUpdate, runRemove, runMove, runReset} from "./BaseMappedList";
|
import {IListObserver} from "./BaseObservableList";
|
||||||
|
import {BaseMappedList, Mapper, Updater, runAdd, runUpdate, runRemove, runMove, runReset} from "./BaseMappedList";
|
||||||
|
|
||||||
export class AsyncMappedList extends BaseMappedList {
|
export class AsyncMappedList<F,T> extends BaseMappedList<F,T,Promise<T>> implements IListObserver<F> {
|
||||||
constructor(sourceList, mapper, updater, removeCallback) {
|
private _eventQueue: AsyncEvent<F>[] | null = null;
|
||||||
super(sourceList, mapper, updater, removeCallback);
|
private _flushing: boolean = false;
|
||||||
this._eventQueue = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
onSubscribeFirst() {
|
onSubscribeFirst(): void {
|
||||||
this._sourceUnsubscribe = this._sourceList.subscribe(this);
|
this._sourceUnsubscribe = this._sourceList.subscribe(this);
|
||||||
this._eventQueue = [];
|
this._eventQueue = [];
|
||||||
this._mappedValues = [];
|
this._mappedValues = [];
|
||||||
|
@ -35,122 +34,112 @@ export class AsyncMappedList extends BaseMappedList {
|
||||||
this._flush();
|
this._flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
async _flush() {
|
async _flush(): Promise<void> {
|
||||||
if (this._flushing) {
|
if (this._flushing) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._flushing = true;
|
this._flushing = true;
|
||||||
try {
|
try {
|
||||||
while (this._eventQueue.length) {
|
while (this._eventQueue!.length) {
|
||||||
const event = this._eventQueue.shift();
|
const event = this._eventQueue!.shift();
|
||||||
await event.run(this);
|
await event!.run(this);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
this._flushing = false;
|
this._flushing = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onReset() {
|
onReset(): void {
|
||||||
if (this._eventQueue) {
|
if (this._eventQueue) {
|
||||||
this._eventQueue.push(new ResetEvent());
|
this._eventQueue.push(new ResetEvent());
|
||||||
this._flush();
|
this._flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onAdd(index, value) {
|
onAdd(index: number, value: F): void {
|
||||||
if (this._eventQueue) {
|
if (this._eventQueue) {
|
||||||
this._eventQueue.push(new AddEvent(index, value));
|
this._eventQueue.push(new AddEvent(index, value));
|
||||||
this._flush();
|
this._flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onUpdate(index, value, params) {
|
onUpdate(index: number, value: F, params: any): void {
|
||||||
if (this._eventQueue) {
|
if (this._eventQueue) {
|
||||||
this._eventQueue.push(new UpdateEvent(index, value, params));
|
this._eventQueue.push(new UpdateEvent(index, value, params));
|
||||||
this._flush();
|
this._flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onRemove(index) {
|
onRemove(index: number): void {
|
||||||
if (this._eventQueue) {
|
if (this._eventQueue) {
|
||||||
this._eventQueue.push(new RemoveEvent(index));
|
this._eventQueue.push(new RemoveEvent(index));
|
||||||
this._flush();
|
this._flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMove(fromIdx, toIdx) {
|
onMove(fromIdx: number, toIdx: number): void {
|
||||||
if (this._eventQueue) {
|
if (this._eventQueue) {
|
||||||
this._eventQueue.push(new MoveEvent(fromIdx, toIdx));
|
this._eventQueue.push(new MoveEvent(fromIdx, toIdx));
|
||||||
this._flush();
|
this._flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onUnsubscribeLast() {
|
onUnsubscribeLast(): void {
|
||||||
this._sourceUnsubscribe();
|
this._sourceUnsubscribe!();
|
||||||
this._eventQueue = null;
|
this._eventQueue = null;
|
||||||
this._mappedValues = null;
|
this._mappedValues = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AddEvent {
|
type AsyncEvent<F> = AddEvent<F> | UpdateEvent<F> | RemoveEvent<F> | MoveEvent<F> | ResetEvent<F>
|
||||||
constructor(index, value) {
|
|
||||||
this.index = index;
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
async run(list) {
|
class AddEvent<F> {
|
||||||
|
constructor(public index: number, public value: F) {}
|
||||||
|
|
||||||
|
async run<T>(list: AsyncMappedList<F,T>): Promise<void> {
|
||||||
const mappedValue = await list._mapper(this.value);
|
const mappedValue = await list._mapper(this.value);
|
||||||
runAdd(list, this.index, mappedValue);
|
runAdd(list, this.index, mappedValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class UpdateEvent {
|
class UpdateEvent<F> {
|
||||||
constructor(index, value, params) {
|
constructor(public index: number, public value: F, public params: any) {}
|
||||||
this.index = index;
|
|
||||||
this.value = value;
|
|
||||||
this.params = params;
|
|
||||||
}
|
|
||||||
|
|
||||||
async run(list) {
|
async run<T>(list: AsyncMappedList<F,T>): Promise<void> {
|
||||||
runUpdate(list, this.index, this.value, this.params);
|
runUpdate(list, this.index, this.value, this.params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class RemoveEvent {
|
class RemoveEvent<F> {
|
||||||
constructor(index) {
|
constructor(public index: number) {}
|
||||||
this.index = index;
|
|
||||||
}
|
|
||||||
|
|
||||||
async run(list) {
|
async run<T>(list: AsyncMappedList<F,T>): Promise<void> {
|
||||||
runRemove(list, this.index);
|
runRemove(list, this.index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MoveEvent {
|
class MoveEvent<F> {
|
||||||
constructor(fromIdx, toIdx) {
|
constructor(public fromIdx: number, public toIdx: number) {}
|
||||||
this.fromIdx = fromIdx;
|
|
||||||
this.toIdx = toIdx;
|
|
||||||
}
|
|
||||||
|
|
||||||
async run(list) {
|
async run<T>(list: AsyncMappedList<F,T>): Promise<void> {
|
||||||
runMove(list, this.fromIdx, this.toIdx);
|
runMove(list, this.fromIdx, this.toIdx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ResetEvent {
|
class ResetEvent<F> {
|
||||||
async run(list) {
|
async run<T>(list: AsyncMappedList<F,T>): Promise<void> {
|
||||||
runReset(list);
|
runReset(list);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
import {ObservableArray} from "./ObservableArray.js";
|
import {ObservableArray} from "./ObservableArray";
|
||||||
import {ListObserver} from "../../mocks/ListObserver.js";
|
import {ListObserver} from "../../mocks/ListObserver.js";
|
||||||
|
|
||||||
export function tests() {
|
export function tests() {
|
||||||
return {
|
return {
|
||||||
"events are emitted in order": async assert => {
|
"events are emitted in order": async assert => {
|
||||||
const double = n => n * n;
|
const double = n => n * n;
|
||||||
const source = new ObservableArray();
|
const source = new ObservableArray<number>();
|
||||||
const mapper = new AsyncMappedList(source, async n => {
|
const mapper = new AsyncMappedList(source, async n => {
|
||||||
await new Promise(r => setTimeout(r, n));
|
await new Promise(r => setTimeout(r, n));
|
||||||
return {n: double(n)};
|
return {n: double(n)};
|
|
@ -21,15 +21,15 @@ import {findAndUpdateInArray} from "./common";
|
||||||
export type Mapper<F,T> = (value: F) => T
|
export type Mapper<F,T> = (value: F) => T
|
||||||
export type Updater<F,T> = (mappedValue: T, params: any, value: F) => void;
|
export type Updater<F,T> = (mappedValue: T, params: any, value: F) => void;
|
||||||
|
|
||||||
export class BaseMappedList<F,T> extends BaseObservableList<T> {
|
export class BaseMappedList<F,T,R = T> extends BaseObservableList<T> {
|
||||||
protected _sourceList: BaseObservableList<F>;
|
protected _sourceList: BaseObservableList<F>;
|
||||||
protected _sourceUnsubscribe: (() => void) | null = null;
|
protected _sourceUnsubscribe: (() => void) | null = null;
|
||||||
_mapper: Mapper<F,T>;
|
_mapper: Mapper<F,R>;
|
||||||
_updater: Updater<F,T>;
|
_updater?: Updater<F,T>;
|
||||||
_removeCallback?: (value: T) => void;
|
_removeCallback?: (value: T) => void;
|
||||||
_mappedValues: T[] | null = null;
|
_mappedValues: T[] | null = null;
|
||||||
|
|
||||||
constructor(sourceList: BaseObservableList<F>, mapper: Mapper<F,T>, updater: Updater<F,T>, removeCallback?: (value: T) => void) {
|
constructor(sourceList: BaseObservableList<F>, mapper: Mapper<F,R>, updater?: Updater<F,T>, removeCallback?: (value: T) => void) {
|
||||||
super();
|
super();
|
||||||
this._sourceList = sourceList;
|
this._sourceList = sourceList;
|
||||||
this._mapper = mapper;
|
this._mapper = mapper;
|
||||||
|
@ -50,12 +50,12 @@ export class BaseMappedList<F,T> extends BaseObservableList<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function runAdd<F,T>(list: BaseMappedList<F,T>, index: number, mappedValue: T): void {
|
export function runAdd<F,T,R>(list: BaseMappedList<F,T,R>, index: number, mappedValue: T): void {
|
||||||
list._mappedValues!.splice(index, 0, mappedValue);
|
list._mappedValues!.splice(index, 0, mappedValue);
|
||||||
list.emitAdd(index, mappedValue);
|
list.emitAdd(index, mappedValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function runUpdate<F,T>(list: BaseMappedList<F,T>, index: number, value: F, params: any): void {
|
export function runUpdate<F,T,R>(list: BaseMappedList<F,T,R>, index: number, value: F, params: any): void {
|
||||||
const mappedValue = list._mappedValues![index];
|
const mappedValue = list._mappedValues![index];
|
||||||
if (list._updater) {
|
if (list._updater) {
|
||||||
list._updater(mappedValue, params, value);
|
list._updater(mappedValue, params, value);
|
||||||
|
@ -63,7 +63,7 @@ export function runUpdate<F,T>(list: BaseMappedList<F,T>, index: number, value:
|
||||||
list.emitUpdate(index, mappedValue, params);
|
list.emitUpdate(index, mappedValue, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function runRemove<F,T>(list: BaseMappedList<F,T>, index: number): void {
|
export function runRemove<F,T,R>(list: BaseMappedList<F,T,R>, index: number): void {
|
||||||
const mappedValue = list._mappedValues![index];
|
const mappedValue = list._mappedValues![index];
|
||||||
list._mappedValues!.splice(index, 1);
|
list._mappedValues!.splice(index, 1);
|
||||||
if (list._removeCallback) {
|
if (list._removeCallback) {
|
||||||
|
@ -72,14 +72,14 @@ export function runRemove<F,T>(list: BaseMappedList<F,T>, index: number): void {
|
||||||
list.emitRemove(index, mappedValue);
|
list.emitRemove(index, mappedValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function runMove<F,T>(list: BaseMappedList<F,T>, fromIdx: number, toIdx: number): void {
|
export function runMove<F,T,R>(list: BaseMappedList<F,T,R>, fromIdx: number, toIdx: number): void {
|
||||||
const mappedValue = list._mappedValues![fromIdx];
|
const mappedValue = list._mappedValues![fromIdx];
|
||||||
list._mappedValues!.splice(fromIdx, 1);
|
list._mappedValues!.splice(fromIdx, 1);
|
||||||
list._mappedValues!.splice(toIdx, 0, mappedValue);
|
list._mappedValues!.splice(toIdx, 0, mappedValue);
|
||||||
list.emitMove(fromIdx, toIdx, mappedValue);
|
list.emitMove(fromIdx, toIdx, mappedValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function runReset<F,T>(list: BaseMappedList<F,T>): void {
|
export function runReset<F,T,R>(list: BaseMappedList<F,T,R>): void {
|
||||||
list._mappedValues = [];
|
list._mappedValues = [];
|
||||||
list.emitReset();
|
list.emitReset();
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,18 @@ export interface IListObserver<T> {
|
||||||
onMove(from: number, to: number, value: T, list: BaseObservableList<T>): void
|
onMove(from: number, to: number, value: T, list: BaseObservableList<T>): void
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class BaseObservableList<T> extends BaseObservable<IListObserver<T>> {
|
export function defaultObserverWith<T>(overrides: { [key in keyof IListObserver<T>]?: IListObserver<T>[key] }): IListObserver<T> {
|
||||||
|
const defaults = {
|
||||||
|
onReset(){},
|
||||||
|
onAdd(){},
|
||||||
|
onUpdate(){},
|
||||||
|
onRemove(){},
|
||||||
|
onMove(){},
|
||||||
|
}
|
||||||
|
return Object.assign(defaults, overrides);
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class BaseObservableList<T> extends BaseObservable<IListObserver<T>> implements Iterable<T> {
|
||||||
emitReset() {
|
emitReset() {
|
||||||
for(let h of this._handlers) {
|
for(let h of this._handlers) {
|
||||||
h.onReset(this);
|
h.onReset(this);
|
||||||
|
@ -38,7 +49,7 @@ export abstract class BaseObservableList<T> extends BaseObservable<IListObserver
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emitUpdate(index: number, value: T, params: any): void {
|
emitUpdate(index: number, value: T, params?: any): void {
|
||||||
for(let h of this._handlers) {
|
for(let h of this._handlers) {
|
||||||
h.onUpdate(index, value, params, this);
|
h.onUpdate(index, value, params, this);
|
||||||
}
|
}
|
||||||
|
@ -58,6 +69,6 @@ export abstract class BaseObservableList<T> extends BaseObservable<IListObserver
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract [Symbol.iterator](): IterableIterator<T>;
|
abstract [Symbol.iterator](): Iterator<T>;
|
||||||
abstract get length(): number;
|
abstract get length(): number;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,16 +14,18 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {BaseObservableList} from "./BaseObservableList";
|
import {BaseObservableList, IListObserver} from "./BaseObservableList";
|
||||||
|
|
||||||
export class ConcatList extends BaseObservableList {
|
export class ConcatList<T> extends BaseObservableList<T> implements IListObserver<T> {
|
||||||
constructor(...sourceLists) {
|
protected _sourceLists: BaseObservableList<T>[];
|
||||||
|
protected _sourceUnsubscribes: (() => void)[] | null = null;
|
||||||
|
|
||||||
|
constructor(...sourceLists: BaseObservableList<T>[]) {
|
||||||
super();
|
super();
|
||||||
this._sourceLists = sourceLists;
|
this._sourceLists = sourceLists;
|
||||||
this._sourceUnsubscribes = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_offsetForSource(sourceList) {
|
_offsetForSource(sourceList: BaseObservableList<T>): number {
|
||||||
const listIdx = this._sourceLists.indexOf(sourceList);
|
const listIdx = this._sourceLists.indexOf(sourceList);
|
||||||
let offset = 0;
|
let offset = 0;
|
||||||
for (let i = 0; i < listIdx; ++i) {
|
for (let i = 0; i < listIdx; ++i) {
|
||||||
|
@ -32,17 +34,17 @@ export class ConcatList extends BaseObservableList {
|
||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
onSubscribeFirst() {
|
onSubscribeFirst(): void {
|
||||||
this._sourceUnsubscribes = this._sourceLists.map(sourceList => sourceList.subscribe(this));
|
this._sourceUnsubscribes = this._sourceLists.map(sourceList => sourceList.subscribe(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
onUnsubscribeLast() {
|
onUnsubscribeLast(): void {
|
||||||
for (const sourceUnsubscribe of this._sourceUnsubscribes) {
|
for (const sourceUnsubscribe of this._sourceUnsubscribes!) {
|
||||||
sourceUnsubscribe();
|
sourceUnsubscribe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onReset() {
|
onReset(): void {
|
||||||
// TODO: not ideal if other source lists are large
|
// TODO: not ideal if other source lists are large
|
||||||
// but working impl for now
|
// but working impl for now
|
||||||
// reset, and
|
// reset, and
|
||||||
|
@ -54,11 +56,11 @@ export class ConcatList extends BaseObservableList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onAdd(index, value, sourceList) {
|
onAdd(index: number, value: T, sourceList: BaseObservableList<T>): void {
|
||||||
this.emitAdd(this._offsetForSource(sourceList) + index, value);
|
this.emitAdd(this._offsetForSource(sourceList) + index, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
onUpdate(index, value, params, sourceList) {
|
onUpdate(index: number, value: T, params: any, sourceList: BaseObservableList<T>): void {
|
||||||
// if an update is emitted while calling source.subscribe() from onSubscribeFirst, ignore it
|
// if an update is emitted while calling source.subscribe() from onSubscribeFirst, ignore it
|
||||||
// as we are not supposed to call `length` on any uninitialized list
|
// as we are not supposed to call `length` on any uninitialized list
|
||||||
if (!this._sourceUnsubscribes) {
|
if (!this._sourceUnsubscribes) {
|
||||||
|
@ -67,16 +69,16 @@ export class ConcatList extends BaseObservableList {
|
||||||
this.emitUpdate(this._offsetForSource(sourceList) + index, value, params);
|
this.emitUpdate(this._offsetForSource(sourceList) + index, value, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
onRemove(index, value, sourceList) {
|
onRemove(index: number, value: T, sourceList: BaseObservableList<T>): void {
|
||||||
this.emitRemove(this._offsetForSource(sourceList) + index, value);
|
this.emitRemove(this._offsetForSource(sourceList) + index, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
onMove(fromIdx, toIdx, value, sourceList) {
|
onMove(fromIdx: number, toIdx: number, value: T, sourceList: BaseObservableList<T>): void {
|
||||||
const offset = this._offsetForSource(sourceList);
|
const offset = this._offsetForSource(sourceList);
|
||||||
this.emitMove(offset + fromIdx, offset + toIdx, value);
|
this.emitMove(offset + fromIdx, offset + toIdx, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
get length() {
|
get length(): number {
|
||||||
let len = 0;
|
let len = 0;
|
||||||
for (let i = 0; i < this._sourceLists.length; ++i) {
|
for (let i = 0; i < this._sourceLists.length; ++i) {
|
||||||
len += this._sourceLists[i].length;
|
len += this._sourceLists[i].length;
|
||||||
|
@ -104,7 +106,8 @@ export class ConcatList extends BaseObservableList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
import {ObservableArray} from "./ObservableArray.js";
|
import {ObservableArray} from "./ObservableArray";
|
||||||
|
import {defaultObserverWith} from "./BaseObservableList";
|
||||||
export async function tests() {
|
export async function tests() {
|
||||||
return {
|
return {
|
||||||
test_length(assert) {
|
test_length(assert) {
|
||||||
|
@ -133,13 +136,13 @@ export async function tests() {
|
||||||
const list2 = new ObservableArray([11, 12, 13]);
|
const list2 = new ObservableArray([11, 12, 13]);
|
||||||
const all = new ConcatList(list1, list2);
|
const all = new ConcatList(list1, list2);
|
||||||
let fired = false;
|
let fired = false;
|
||||||
all.subscribe({
|
all.subscribe(defaultObserverWith({
|
||||||
onAdd(index, value) {
|
onAdd(index, value) {
|
||||||
fired = true;
|
fired = true;
|
||||||
assert.equal(index, 4);
|
assert.equal(index, 4);
|
||||||
assert.equal(value, 11.5);
|
assert.equal(value, 11.5);
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
list2.insert(1, 11.5);
|
list2.insert(1, 11.5);
|
||||||
assert(fired);
|
assert(fired);
|
||||||
},
|
},
|
||||||
|
@ -148,13 +151,13 @@ export async function tests() {
|
||||||
const list2 = new ObservableArray([11, 12, 13]);
|
const list2 = new ObservableArray([11, 12, 13]);
|
||||||
const all = new ConcatList(list1, list2);
|
const all = new ConcatList(list1, list2);
|
||||||
let fired = false;
|
let fired = false;
|
||||||
all.subscribe({
|
all.subscribe(defaultObserverWith({
|
||||||
onUpdate(index, value) {
|
onUpdate(index, value) {
|
||||||
fired = true;
|
fired = true;
|
||||||
assert.equal(index, 4);
|
assert.equal(index, 4);
|
||||||
assert.equal(value, 10);
|
assert.equal(value, 10);
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
list2.emitUpdate(1, 10);
|
list2.emitUpdate(1, 10);
|
||||||
assert(fired);
|
assert(fired);
|
||||||
},
|
},
|
|
@ -15,9 +15,10 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {IListObserver} from "./BaseObservableList";
|
||||||
import {BaseMappedList, runAdd, runUpdate, runRemove, runMove, runReset} from "./BaseMappedList";
|
import {BaseMappedList, runAdd, runUpdate, runRemove, runMove, runReset} from "./BaseMappedList";
|
||||||
|
|
||||||
export class MappedList extends BaseMappedList {
|
export class MappedList<F,T> extends BaseMappedList<F,T> implements IListObserver<F> {
|
||||||
onSubscribeFirst() {
|
onSubscribeFirst() {
|
||||||
this._sourceUnsubscribe = this._sourceList.subscribe(this);
|
this._sourceUnsubscribe = this._sourceList.subscribe(this);
|
||||||
this._mappedValues = [];
|
this._mappedValues = [];
|
||||||
|
@ -26,16 +27,16 @@ export class MappedList extends BaseMappedList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onReset() {
|
onReset(): void {
|
||||||
runReset(this);
|
runReset(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
onAdd(index, value) {
|
onAdd(index: number, value: F): void {
|
||||||
const mappedValue = this._mapper(value);
|
const mappedValue = this._mapper(value);
|
||||||
runAdd(this, index, mappedValue);
|
runAdd(this, index, mappedValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
onUpdate(index, value, params) {
|
onUpdate(index: number, value: F, params: any): void {
|
||||||
// if an update is emitted while calling source.subscribe() from onSubscribeFirst, ignore it
|
// if an update is emitted while calling source.subscribe() from onSubscribeFirst, ignore it
|
||||||
if (!this._mappedValues) {
|
if (!this._mappedValues) {
|
||||||
return;
|
return;
|
||||||
|
@ -43,24 +44,25 @@ export class MappedList extends BaseMappedList {
|
||||||
runUpdate(this, index, value, params);
|
runUpdate(this, index, value, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
onRemove(index) {
|
onRemove(index: number): void {
|
||||||
runRemove(this, index);
|
runRemove(this, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
onMove(fromIdx, toIdx) {
|
onMove(fromIdx: number, toIdx: number): void {
|
||||||
runMove(this, fromIdx, toIdx);
|
runMove(this, fromIdx, toIdx);
|
||||||
}
|
}
|
||||||
|
|
||||||
onUnsubscribeLast() {
|
onUnsubscribeLast(): void {
|
||||||
this._sourceUnsubscribe();
|
this._sourceUnsubscribe!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
import {ObservableArray} from "./ObservableArray.js";
|
import {ObservableArray} from "./ObservableArray";
|
||||||
import {BaseObservableList} from "./BaseObservableList";
|
import {BaseObservableList} from "./BaseObservableList";
|
||||||
|
import {defaultObserverWith} from "./BaseObservableList";
|
||||||
|
|
||||||
export async function tests() {
|
export async function tests() {
|
||||||
class MockList extends BaseObservableList {
|
class MockList extends BaseObservableList<number> {
|
||||||
get length() {
|
get length() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -74,26 +76,26 @@ export async function tests() {
|
||||||
const source = new MockList();
|
const source = new MockList();
|
||||||
const mapped = new MappedList(source, n => {return {n: n*n};});
|
const mapped = new MappedList(source, n => {return {n: n*n};});
|
||||||
let fired = false;
|
let fired = false;
|
||||||
const unsubscribe = mapped.subscribe({
|
const unsubscribe = mapped.subscribe(defaultObserverWith({
|
||||||
onAdd(idx, value) {
|
onAdd(idx, value) {
|
||||||
fired = true;
|
fired = true;
|
||||||
assert.equal(idx, 0);
|
assert.equal(idx, 0);
|
||||||
assert.equal(value.n, 36);
|
assert.equal(value.n, 36);
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
source.emitAdd(0, 6);
|
source.emitAdd(0, 6);
|
||||||
assert(fired);
|
assert(fired);
|
||||||
unsubscribe();
|
unsubscribe();
|
||||||
},
|
},
|
||||||
test_update(assert) {
|
test_update(assert) {
|
||||||
const source = new MockList();
|
const source = new MockList();
|
||||||
const mapped = new MappedList(
|
const mapped = new MappedList<number, { n: number, m?: number }>(
|
||||||
source,
|
source,
|
||||||
n => {return {n: n*n};},
|
n => {return {n: n*n};},
|
||||||
(o, p, n) => o.m = n*n
|
(o, p, n) => o.m = n*n
|
||||||
);
|
);
|
||||||
let fired = false;
|
let fired = false;
|
||||||
const unsubscribe = mapped.subscribe({
|
const unsubscribe = mapped.subscribe(defaultObserverWith({
|
||||||
onAdd() {},
|
onAdd() {},
|
||||||
onUpdate(idx, value) {
|
onUpdate(idx, value) {
|
||||||
fired = true;
|
fired = true;
|
||||||
|
@ -101,7 +103,7 @@ export async function tests() {
|
||||||
assert.equal(value.n, 36);
|
assert.equal(value.n, 36);
|
||||||
assert.equal(value.m, 49);
|
assert.equal(value.m, 49);
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
source.emitAdd(0, 6);
|
source.emitAdd(0, 6);
|
||||||
source.emitUpdate(0, 7);
|
source.emitUpdate(0, 7);
|
||||||
assert(fired);
|
assert(fired);
|
||||||
|
@ -113,9 +115,9 @@ export async function tests() {
|
||||||
source,
|
source,
|
||||||
n => {return n*n;}
|
n => {return n*n;}
|
||||||
);
|
);
|
||||||
mapped.subscribe({
|
mapped.subscribe(defaultObserverWith({
|
||||||
onUpdate() { assert.fail(); }
|
onUpdate() { assert.fail(); }
|
||||||
});
|
}));
|
||||||
assert.equal(mapped.findAndUpdate(
|
assert.equal(mapped.findAndUpdate(
|
||||||
n => n === 100,
|
n => n === 100,
|
||||||
() => assert.fail()
|
() => assert.fail()
|
||||||
|
@ -127,9 +129,9 @@ export async function tests() {
|
||||||
source,
|
source,
|
||||||
n => {return n*n;}
|
n => {return n*n;}
|
||||||
);
|
);
|
||||||
mapped.subscribe({
|
mapped.subscribe(defaultObserverWith({
|
||||||
onUpdate() { assert.fail(); }
|
onUpdate() { assert.fail(); }
|
||||||
});
|
}));
|
||||||
let fired = false;
|
let fired = false;
|
||||||
assert.equal(mapped.findAndUpdate(
|
assert.equal(mapped.findAndUpdate(
|
||||||
n => n === 9,
|
n => n === 9,
|
||||||
|
@ -148,14 +150,14 @@ export async function tests() {
|
||||||
n => {return n*n;}
|
n => {return n*n;}
|
||||||
);
|
);
|
||||||
let fired = false;
|
let fired = false;
|
||||||
mapped.subscribe({
|
mapped.subscribe(defaultObserverWith({
|
||||||
onUpdate(idx, n, params) {
|
onUpdate(idx, n, params) {
|
||||||
assert.equal(idx, 1);
|
assert.equal(idx, 1);
|
||||||
assert.equal(n, 9);
|
assert.equal(n, 9);
|
||||||
assert.equal(params, "param");
|
assert.equal(params, "param");
|
||||||
fired = true;
|
fired = true;
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
assert.equal(mapped.findAndUpdate(n => n === 9, () => "param"), true);
|
assert.equal(mapped.findAndUpdate(n => n === 9, () => "param"), true);
|
||||||
assert.equal(fired, true);
|
assert.equal(fired, true);
|
||||||
},
|
},
|
|
@ -16,35 +16,37 @@ limitations under the License.
|
||||||
|
|
||||||
import {BaseObservableList} from "./BaseObservableList";
|
import {BaseObservableList} from "./BaseObservableList";
|
||||||
|
|
||||||
export class ObservableArray extends BaseObservableList {
|
export class ObservableArray<T> extends BaseObservableList<T> {
|
||||||
constructor(initialValues = []) {
|
private _items: T[];
|
||||||
|
|
||||||
|
constructor(initialValues: T[] = []) {
|
||||||
super();
|
super();
|
||||||
this._items = initialValues;
|
this._items = initialValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
append(item) {
|
append(item: T): void {
|
||||||
this._items.push(item);
|
this._items.push(item);
|
||||||
this.emitAdd(this._items.length - 1, item);
|
this.emitAdd(this._items.length - 1, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
remove(idx) {
|
remove(idx: number): void {
|
||||||
const [item] = this._items.splice(idx, 1);
|
const [item] = this._items.splice(idx, 1);
|
||||||
this.emitRemove(idx, item);
|
this.emitRemove(idx, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
insertMany(idx, items) {
|
insertMany(idx: number, items: T[]): void {
|
||||||
for(let item of items) {
|
for(let item of items) {
|
||||||
this.insert(idx, item);
|
this.insert(idx, item);
|
||||||
idx += 1;
|
idx += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
insert(idx, item) {
|
insert(idx: number, item: T): void {
|
||||||
this._items.splice(idx, 0, item);
|
this._items.splice(idx, 0, item);
|
||||||
this.emitAdd(idx, item);
|
this.emitAdd(idx, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
move(fromIdx, toIdx) {
|
move(fromIdx: number, toIdx: number): void {
|
||||||
if (fromIdx < this._items.length && toIdx < this._items.length) {
|
if (fromIdx < this._items.length && toIdx < this._items.length) {
|
||||||
const [item] = this._items.splice(fromIdx, 1);
|
const [item] = this._items.splice(fromIdx, 1);
|
||||||
this._items.splice(toIdx, 0, item);
|
this._items.splice(toIdx, 0, item);
|
||||||
|
@ -52,24 +54,24 @@ export class ObservableArray extends BaseObservableList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
update(idx, item, params = null) {
|
update(idx: number, item: T, params: any = null): void {
|
||||||
if (idx < this._items.length) {
|
if (idx < this._items.length) {
|
||||||
this._items[idx] = item;
|
this._items[idx] = item;
|
||||||
this.emitUpdate(idx, item, params);
|
this.emitUpdate(idx, item, params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get array() {
|
get array(): Readonly<T[]> {
|
||||||
return this._items;
|
return this._items;
|
||||||
}
|
}
|
||||||
|
|
||||||
at(idx) {
|
at(idx: number): T | undefined {
|
||||||
if (this._items && idx >= 0 && idx < this._items.length) {
|
if (this._items && idx >= 0 && idx < this._items.length) {
|
||||||
return this._items[idx];
|
return this._items[idx];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get length() {
|
get length(): number {
|
||||||
return this._items.length;
|
return this._items.length;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,18 +18,20 @@ import {BaseObservableList} from "./BaseObservableList";
|
||||||
import {sortedIndex} from "../../utils/sortedIndex";
|
import {sortedIndex} from "../../utils/sortedIndex";
|
||||||
import {findAndUpdateInArray} from "./common";
|
import {findAndUpdateInArray} from "./common";
|
||||||
|
|
||||||
export class SortedArray extends BaseObservableList {
|
export class SortedArray<T> extends BaseObservableList<T> {
|
||||||
constructor(comparator) {
|
private _comparator: (left: T, right: T) => number;
|
||||||
|
private _items: T[] = [];
|
||||||
|
|
||||||
|
constructor(comparator: (left: T, right: T) => number) {
|
||||||
super();
|
super();
|
||||||
this._comparator = comparator;
|
this._comparator = comparator;
|
||||||
this._items = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setManyUnsorted(items) {
|
setManyUnsorted(items: T[]): void {
|
||||||
this.setManySorted(items);
|
this.setManySorted(items);
|
||||||
}
|
}
|
||||||
|
|
||||||
setManySorted(items) {
|
setManySorted(items: T[]): void {
|
||||||
// TODO: we can make this way faster by only looking up the first and last key,
|
// TODO: we can make this way faster by only looking up the first and last key,
|
||||||
// and merging whatever is inbetween with items
|
// and merging whatever is inbetween with items
|
||||||
// if items is not sorted, 💩🌀 will follow!
|
// if items is not sorted, 💩🌀 will follow!
|
||||||
|
@ -42,11 +44,11 @@ export class SortedArray extends BaseObservableList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
findAndUpdate(predicate, updater) {
|
findAndUpdate(predicate: (value: T) => boolean, updater: (value: T) => any | false): boolean {
|
||||||
return findAndUpdateInArray(predicate, this._items, this, updater);
|
return findAndUpdateInArray(predicate, this._items, this, updater);
|
||||||
}
|
}
|
||||||
|
|
||||||
getAndUpdate(item, updater, updateParams = null) {
|
getAndUpdate(item: T, updater: (existing: T, item: T) => any, updateParams: any = null): void {
|
||||||
const idx = this.indexOf(item);
|
const idx = this.indexOf(item);
|
||||||
if (idx !== -1) {
|
if (idx !== -1) {
|
||||||
const existingItem = this._items[idx];
|
const existingItem = this._items[idx];
|
||||||
|
@ -56,7 +58,7 @@ export class SortedArray extends BaseObservableList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
update(item, updateParams = null) {
|
update(item: T, updateParams: any = null): void {
|
||||||
const idx = this.indexOf(item);
|
const idx = this.indexOf(item);
|
||||||
if (idx !== -1) {
|
if (idx !== -1) {
|
||||||
this._items[idx] = item;
|
this._items[idx] = item;
|
||||||
|
@ -64,7 +66,7 @@ export class SortedArray extends BaseObservableList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
indexOf(item) {
|
indexOf(item: T): number {
|
||||||
const idx = sortedIndex(this._items, item, this._comparator);
|
const idx = sortedIndex(this._items, item, this._comparator);
|
||||||
if (idx < this._items.length && this._comparator(this._items[idx], item) === 0) {
|
if (idx < this._items.length && this._comparator(this._items[idx], item) === 0) {
|
||||||
return idx;
|
return idx;
|
||||||
|
@ -73,7 +75,7 @@ export class SortedArray extends BaseObservableList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_getNext(item) {
|
_getNext(item: T): T | undefined {
|
||||||
let idx = sortedIndex(this._items, item, this._comparator);
|
let idx = sortedIndex(this._items, item, this._comparator);
|
||||||
while(idx < this._items.length && this._comparator(this._items[idx], item) <= 0) {
|
while(idx < this._items.length && this._comparator(this._items[idx], item) <= 0) {
|
||||||
idx += 1;
|
idx += 1;
|
||||||
|
@ -81,7 +83,7 @@ export class SortedArray extends BaseObservableList {
|
||||||
return this.get(idx);
|
return this.get(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
set(item, updateParams = null) {
|
set(item: T, updateParams: any = null): void {
|
||||||
const idx = sortedIndex(this._items, item, this._comparator);
|
const idx = sortedIndex(this._items, item, this._comparator);
|
||||||
if (idx >= this._items.length || this._comparator(this._items[idx], item) !== 0) {
|
if (idx >= this._items.length || this._comparator(this._items[idx], item) !== 0) {
|
||||||
this._items.splice(idx, 0, item);
|
this._items.splice(idx, 0, item);
|
||||||
|
@ -92,21 +94,21 @@ export class SortedArray extends BaseObservableList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get(idx) {
|
get(idx: number): T | undefined {
|
||||||
return this._items[idx];
|
return this._items[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
remove(idx) {
|
remove(idx: number): void {
|
||||||
const item = this._items[idx];
|
const item = this._items[idx];
|
||||||
this._items.splice(idx, 1);
|
this._items.splice(idx, 1);
|
||||||
this.emitRemove(idx, item);
|
this.emitRemove(idx, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
get array() {
|
get array(): T[] {
|
||||||
return this._items;
|
return this._items;
|
||||||
}
|
}
|
||||||
|
|
||||||
get length() {
|
get length(): number {
|
||||||
return this._items.length;
|
return this._items.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,8 +118,11 @@ export class SortedArray extends BaseObservableList {
|
||||||
}
|
}
|
||||||
|
|
||||||
// iterator that works even if the current value is removed while iterating
|
// iterator that works even if the current value is removed while iterating
|
||||||
class Iterator {
|
class Iterator<T> {
|
||||||
constructor(sortedArray) {
|
private _sortedArray: SortedArray<T> | null
|
||||||
|
private _current: T | null | undefined
|
||||||
|
|
||||||
|
constructor(sortedArray: SortedArray<T>) {
|
||||||
this._sortedArray = sortedArray;
|
this._sortedArray = sortedArray;
|
||||||
this._current = null;
|
this._current = null;
|
||||||
}
|
}
|
||||||
|
@ -145,7 +150,7 @@ class Iterator {
|
||||||
export function tests() {
|
export function tests() {
|
||||||
return {
|
return {
|
||||||
"setManyUnsorted": assert => {
|
"setManyUnsorted": assert => {
|
||||||
const sa = new SortedArray((a, b) => a.localeCompare(b));
|
const sa = new SortedArray<string>((a, b) => a.localeCompare(b));
|
||||||
sa.setManyUnsorted(["b", "a", "c"]);
|
sa.setManyUnsorted(["b", "a", "c"]);
|
||||||
assert.equal(sa.length, 3);
|
assert.equal(sa.length, 3);
|
||||||
assert.equal(sa.get(0), "a");
|
assert.equal(sa.get(0), "a");
|
||||||
|
@ -153,7 +158,7 @@ export function tests() {
|
||||||
assert.equal(sa.get(2), "c");
|
assert.equal(sa.get(2), "c");
|
||||||
},
|
},
|
||||||
"_getNext": assert => {
|
"_getNext": assert => {
|
||||||
const sa = new SortedArray((a, b) => a.localeCompare(b));
|
const sa = new SortedArray<string>((a, b) => a.localeCompare(b));
|
||||||
sa.setManyUnsorted(["b", "a", "f"]);
|
sa.setManyUnsorted(["b", "a", "f"]);
|
||||||
assert.equal(sa._getNext("a"), "b");
|
assert.equal(sa._getNext("a"), "b");
|
||||||
assert.equal(sa._getNext("b"), "f");
|
assert.equal(sa._getNext("b"), "f");
|
||||||
|
@ -162,7 +167,7 @@ export function tests() {
|
||||||
assert.equal(sa._getNext("f"), undefined);
|
assert.equal(sa._getNext("f"), undefined);
|
||||||
},
|
},
|
||||||
"iterator with removals": assert => {
|
"iterator with removals": assert => {
|
||||||
const queue = new SortedArray((a, b) => a.idx - b.idx);
|
const queue = new SortedArray<{idx: number}>((a, b) => a.idx - b.idx);
|
||||||
queue.setManyUnsorted([{idx: 5}, {idx: 3}, {idx: 1}, {idx: 4}, {idx: 2}]);
|
queue.setManyUnsorted([{idx: 5}, {idx: 3}, {idx: 1}, {idx: 4}, {idx: 2}]);
|
||||||
const it = queue[Symbol.iterator]();
|
const it = queue[Symbol.iterator]();
|
||||||
assert.equal(it.next().value.idx, 1);
|
assert.equal(it.next().value.idx, 1);
|
|
@ -15,6 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Range, RangeZone} from "./Range";
|
import {Range, RangeZone} from "./Range";
|
||||||
|
import {defaultObserverWith} from "../../../../observable/list/BaseObservableList";
|
||||||
|
|
||||||
function skipOnIterator<T>(it: Iterator<T>, pos: number): boolean {
|
function skipOnIterator<T>(it: Iterator<T>, pos: number): boolean {
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
@ -268,7 +269,7 @@ export function tests() {
|
||||||
const list = new ObservableArray(["b", "c", "d", "e"]);
|
const list = new ObservableArray(["b", "c", "d", "e"]);
|
||||||
const range = new ListRange(1, 3, list.length);
|
const range = new ListRange(1, 3, list.length);
|
||||||
let added = false;
|
let added = false;
|
||||||
list.subscribe({
|
list.subscribe(defaultObserverWith({
|
||||||
onAdd(idx, value) {
|
onAdd(idx, value) {
|
||||||
added = true;
|
added = true;
|
||||||
const result = range.queryAdd(idx, value, list);
|
const result = range.queryAdd(idx, value, list);
|
||||||
|
@ -280,7 +281,7 @@ export function tests() {
|
||||||
newRange: new ListRange(1, 3, 5)
|
newRange: new ListRange(1, 3, 5)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
list.insert(0, "a");
|
list.insert(0, "a");
|
||||||
assert(added);
|
assert(added);
|
||||||
},
|
},
|
||||||
|
@ -288,7 +289,7 @@ export function tests() {
|
||||||
const list = new ObservableArray(["a", "b", "d", "e"]);
|
const list = new ObservableArray(["a", "b", "d", "e"]);
|
||||||
const range = new ListRange(1, 3, list.length);
|
const range = new ListRange(1, 3, list.length);
|
||||||
let added = false;
|
let added = false;
|
||||||
list.subscribe({
|
list.subscribe(defaultObserverWith({
|
||||||
onAdd(idx, value) {
|
onAdd(idx, value) {
|
||||||
added = true;
|
added = true;
|
||||||
const result = range.queryAdd(idx, value, list);
|
const result = range.queryAdd(idx, value, list);
|
||||||
|
@ -300,7 +301,7 @@ export function tests() {
|
||||||
newRange: new ListRange(1, 3, 5)
|
newRange: new ListRange(1, 3, 5)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
list.insert(2, "c");
|
list.insert(2, "c");
|
||||||
assert(added);
|
assert(added);
|
||||||
},
|
},
|
||||||
|
@ -308,7 +309,7 @@ export function tests() {
|
||||||
const list = new ObservableArray(["a", "b", "c", "d"]);
|
const list = new ObservableArray(["a", "b", "c", "d"]);
|
||||||
const range = new ListRange(1, 3, list.length);
|
const range = new ListRange(1, 3, list.length);
|
||||||
let added = false;
|
let added = false;
|
||||||
list.subscribe({
|
list.subscribe(defaultObserverWith({
|
||||||
onAdd(idx, value) {
|
onAdd(idx, value) {
|
||||||
added = true;
|
added = true;
|
||||||
const result = range.queryAdd(idx, value, list);
|
const result = range.queryAdd(idx, value, list);
|
||||||
|
@ -317,7 +318,7 @@ export function tests() {
|
||||||
newRange: new ListRange(1, 3, 5)
|
newRange: new ListRange(1, 3, 5)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
list.insert(4, "e");
|
list.insert(4, "e");
|
||||||
assert(added);
|
assert(added);
|
||||||
},
|
},
|
||||||
|
@ -326,7 +327,7 @@ export function tests() {
|
||||||
const viewportItemCount = 4;
|
const viewportItemCount = 4;
|
||||||
const range = new ListRange(0, 3, list.length, viewportItemCount);
|
const range = new ListRange(0, 3, list.length, viewportItemCount);
|
||||||
let added = false;
|
let added = false;
|
||||||
list.subscribe({
|
list.subscribe(defaultObserverWith({
|
||||||
onAdd(idx, value) {
|
onAdd(idx, value) {
|
||||||
added = true;
|
added = true;
|
||||||
const result = range.queryAdd(idx, value, list);
|
const result = range.queryAdd(idx, value, list);
|
||||||
|
@ -337,7 +338,7 @@ export function tests() {
|
||||||
value: "c"
|
value: "c"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
list.insert(2, "c");
|
list.insert(2, "c");
|
||||||
assert(added);
|
assert(added);
|
||||||
},
|
},
|
||||||
|
@ -345,7 +346,7 @@ export function tests() {
|
||||||
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
|
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
|
||||||
const range = new ListRange(1, 3, list.length);
|
const range = new ListRange(1, 3, list.length);
|
||||||
let removed = false;
|
let removed = false;
|
||||||
list.subscribe({
|
list.subscribe(defaultObserverWith({
|
||||||
onRemove(idx) {
|
onRemove(idx) {
|
||||||
removed = true;
|
removed = true;
|
||||||
const result = range.queryRemove(idx, list);
|
const result = range.queryRemove(idx, list);
|
||||||
|
@ -357,7 +358,7 @@ export function tests() {
|
||||||
newRange: new ListRange(1, 3, 4)
|
newRange: new ListRange(1, 3, 4)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
list.remove(0);
|
list.remove(0);
|
||||||
assert(removed);
|
assert(removed);
|
||||||
},
|
},
|
||||||
|
@ -365,7 +366,7 @@ export function tests() {
|
||||||
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
|
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
|
||||||
const range = new ListRange(1, 3, list.length);
|
const range = new ListRange(1, 3, list.length);
|
||||||
let removed = false;
|
let removed = false;
|
||||||
list.subscribe({
|
list.subscribe(defaultObserverWith({
|
||||||
onRemove(idx) {
|
onRemove(idx) {
|
||||||
removed = true;
|
removed = true;
|
||||||
const result = range.queryRemove(idx, list);
|
const result = range.queryRemove(idx, list);
|
||||||
|
@ -378,7 +379,7 @@ export function tests() {
|
||||||
});
|
});
|
||||||
assert.equal(list.length, 4);
|
assert.equal(list.length, 4);
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
list.remove(2);
|
list.remove(2);
|
||||||
assert(removed);
|
assert(removed);
|
||||||
},
|
},
|
||||||
|
@ -386,7 +387,7 @@ export function tests() {
|
||||||
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
|
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
|
||||||
const range = new ListRange(1, 3, list.length);
|
const range = new ListRange(1, 3, list.length);
|
||||||
let removed = false;
|
let removed = false;
|
||||||
list.subscribe({
|
list.subscribe(defaultObserverWith({
|
||||||
onRemove(idx) {
|
onRemove(idx) {
|
||||||
removed = true;
|
removed = true;
|
||||||
const result = range.queryRemove(idx, list);
|
const result = range.queryRemove(idx, list);
|
||||||
|
@ -395,7 +396,7 @@ export function tests() {
|
||||||
newRange: new ListRange(1, 3, 4)
|
newRange: new ListRange(1, 3, 4)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
list.remove(3);
|
list.remove(3);
|
||||||
assert(removed);
|
assert(removed);
|
||||||
},
|
},
|
||||||
|
@ -403,7 +404,7 @@ export function tests() {
|
||||||
const list = new ObservableArray(["a", "b", "c"]);
|
const list = new ObservableArray(["a", "b", "c"]);
|
||||||
const range = new ListRange(1, 3, list.length);
|
const range = new ListRange(1, 3, list.length);
|
||||||
let removed = false;
|
let removed = false;
|
||||||
list.subscribe({
|
list.subscribe(defaultObserverWith({
|
||||||
onRemove(idx) {
|
onRemove(idx) {
|
||||||
removed = true;
|
removed = true;
|
||||||
const result = range.queryRemove(idx, list);
|
const result = range.queryRemove(idx, list);
|
||||||
|
@ -415,7 +416,7 @@ export function tests() {
|
||||||
value: "a"
|
value: "a"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
list.remove(2);
|
list.remove(2);
|
||||||
assert(removed);
|
assert(removed);
|
||||||
},
|
},
|
||||||
|
@ -423,7 +424,7 @@ export function tests() {
|
||||||
const list = new ObservableArray(["a", "b", "c"]);
|
const list = new ObservableArray(["a", "b", "c"]);
|
||||||
const range = new ListRange(0, 3, list.length);
|
const range = new ListRange(0, 3, list.length);
|
||||||
let removed = false;
|
let removed = false;
|
||||||
list.subscribe({
|
list.subscribe(defaultObserverWith({
|
||||||
onRemove(idx) {
|
onRemove(idx) {
|
||||||
removed = true;
|
removed = true;
|
||||||
const result = range.queryRemove(idx, list);
|
const result = range.queryRemove(idx, list);
|
||||||
|
@ -433,7 +434,7 @@ export function tests() {
|
||||||
removeIdx: 2,
|
removeIdx: 2,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
list.remove(2);
|
list.remove(2);
|
||||||
assert(removed);
|
assert(removed);
|
||||||
},
|
},
|
||||||
|
@ -441,7 +442,7 @@ export function tests() {
|
||||||
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
|
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
|
||||||
const range = new ListRange(1, 4, list.length);
|
const range = new ListRange(1, 4, list.length);
|
||||||
let moved = false;
|
let moved = false;
|
||||||
list.subscribe({
|
list.subscribe(defaultObserverWith({
|
||||||
onMove(fromIdx, toIdx, value) {
|
onMove(fromIdx, toIdx, value) {
|
||||||
moved = true;
|
moved = true;
|
||||||
const result = range.queryMove(fromIdx, toIdx, value, list);
|
const result = range.queryMove(fromIdx, toIdx, value, list);
|
||||||
|
@ -451,7 +452,7 @@ export function tests() {
|
||||||
toIdx: 3
|
toIdx: 3
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
list.move(2, 3);
|
list.move(2, 3);
|
||||||
assert(moved);
|
assert(moved);
|
||||||
},
|
},
|
||||||
|
@ -459,7 +460,7 @@ export function tests() {
|
||||||
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
|
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
|
||||||
const range = new ListRange(2, 5, list.length);
|
const range = new ListRange(2, 5, list.length);
|
||||||
let moved = false;
|
let moved = false;
|
||||||
list.subscribe({
|
list.subscribe(defaultObserverWith({
|
||||||
onMove(fromIdx, toIdx, value) {
|
onMove(fromIdx, toIdx, value) {
|
||||||
moved = true;
|
moved = true;
|
||||||
const result = range.queryMove(fromIdx, toIdx, value, list);
|
const result = range.queryMove(fromIdx, toIdx, value, list);
|
||||||
|
@ -470,7 +471,7 @@ export function tests() {
|
||||||
value: "a"
|
value: "a"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
list.move(0, 3); // move "a" to after "d"
|
list.move(0, 3); // move "a" to after "d"
|
||||||
assert(moved);
|
assert(moved);
|
||||||
},
|
},
|
||||||
|
@ -478,7 +479,7 @@ export function tests() {
|
||||||
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
|
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
|
||||||
const range = new ListRange(0, 3, list.length);
|
const range = new ListRange(0, 3, list.length);
|
||||||
let moved = false;
|
let moved = false;
|
||||||
list.subscribe({
|
list.subscribe(defaultObserverWith({
|
||||||
onMove(fromIdx, toIdx, value) {
|
onMove(fromIdx, toIdx, value) {
|
||||||
moved = true;
|
moved = true;
|
||||||
const result = range.queryMove(fromIdx, toIdx, value, list);
|
const result = range.queryMove(fromIdx, toIdx, value, list);
|
||||||
|
@ -489,7 +490,7 @@ export function tests() {
|
||||||
value: "e"
|
value: "e"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
list.move(4, 1); // move "e" to before "b"
|
list.move(4, 1); // move "e" to before "b"
|
||||||
assert(moved);
|
assert(moved);
|
||||||
},
|
},
|
||||||
|
@ -497,7 +498,7 @@ export function tests() {
|
||||||
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
|
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
|
||||||
const range = new ListRange(0, 3, list.length);
|
const range = new ListRange(0, 3, list.length);
|
||||||
let moved = false;
|
let moved = false;
|
||||||
list.subscribe({
|
list.subscribe(defaultObserverWith({
|
||||||
onMove(fromIdx, toIdx, value) {
|
onMove(fromIdx, toIdx, value) {
|
||||||
moved = true;
|
moved = true;
|
||||||
const result = range.queryMove(fromIdx, toIdx, value, list);
|
const result = range.queryMove(fromIdx, toIdx, value, list);
|
||||||
|
@ -508,7 +509,7 @@ export function tests() {
|
||||||
value: "d"
|
value: "d"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
list.move(1, 3); // move "b" to after "d"
|
list.move(1, 3); // move "b" to after "d"
|
||||||
assert(moved);
|
assert(moved);
|
||||||
},
|
},
|
||||||
|
@ -516,7 +517,7 @@ export function tests() {
|
||||||
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
|
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
|
||||||
const range = new ListRange(2, 5, list.length);
|
const range = new ListRange(2, 5, list.length);
|
||||||
let moved = false;
|
let moved = false;
|
||||||
list.subscribe({
|
list.subscribe(defaultObserverWith({
|
||||||
onMove(fromIdx, toIdx, value) {
|
onMove(fromIdx, toIdx, value) {
|
||||||
moved = true;
|
moved = true;
|
||||||
const result = range.queryMove(fromIdx, toIdx, value, list);
|
const result = range.queryMove(fromIdx, toIdx, value, list);
|
||||||
|
@ -527,7 +528,7 @@ export function tests() {
|
||||||
value: "b"
|
value: "b"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
list.move(3, 0); // move "d" to before "a"
|
list.move(3, 0); // move "d" to before "a"
|
||||||
assert(moved);
|
assert(moved);
|
||||||
},
|
},
|
||||||
|
@ -535,7 +536,7 @@ export function tests() {
|
||||||
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
|
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
|
||||||
const range = new ListRange(1, 4, list.length);
|
const range = new ListRange(1, 4, list.length);
|
||||||
let moved = false;
|
let moved = false;
|
||||||
list.subscribe({
|
list.subscribe(defaultObserverWith({
|
||||||
onMove(fromIdx, toIdx, value) {
|
onMove(fromIdx, toIdx, value) {
|
||||||
moved = true;
|
moved = true;
|
||||||
const result = range.queryMove(fromIdx, toIdx, value, list);
|
const result = range.queryMove(fromIdx, toIdx, value, list);
|
||||||
|
@ -546,7 +547,7 @@ export function tests() {
|
||||||
value: "e"
|
value: "e"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
list.move(0, 4); // move "a" to after "e"
|
list.move(0, 4); // move "a" to after "e"
|
||||||
assert(moved);
|
assert(moved);
|
||||||
},
|
},
|
||||||
|
|
|
@ -43,7 +43,7 @@ export class Range {
|
||||||
return range.start < this.end && this.start < range.end;
|
return range.start < this.end && this.start < range.end;
|
||||||
}
|
}
|
||||||
|
|
||||||
forEachInIterator<T>(it: IterableIterator<T>, callback: ((T, i: number) => void)) {
|
forEachInIterator<T>(it: Iterator<T>, callback: ((T, i: number) => void)) {
|
||||||
let i = 0;
|
let i = 0;
|
||||||
for (i = 0; i < this.start; i += 1) {
|
for (i = 0; i < this.start; i += 1) {
|
||||||
it.next();
|
it.next();
|
||||||
|
|
Loading…
Reference in a new issue