aboutsummaryrefslogtreecommitdiff
path: root/src/fields/ListField.ts
blob: c4008bd12ee3976c0853b97ce0cabc75be84ad27 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import { action, IArrayChange, IArraySplice, IObservableArray, observe, observable, Lambda } from "mobx";
import { Server } from "../client/Server";
import { UndoManager } from "../client/util/UndoManager";
import { Types } from "../server/Message";
import { BasicField } from "./BasicField";
import { Field, FieldId } from "./Field";
import { FieldMap } from "../client/SocketStub";

export class ListField<T extends Field> extends BasicField<T[]> {
    private _proxies: string[] = []
    constructor(data: T[] = [], id?: FieldId, save: boolean = true) {
        super(data, save, id);
        this.updateProxies();
        if (save) {
            Server.UpdateField(this);
        }
        this.observeList();
    }

    private _processingServerUpdate: boolean = false;

    private observeDisposer: Lambda | undefined;
    private observeList(): void {
        if (this.observeDisposer) {
            this.observeDisposer()
        }
        this.observeDisposer = observe(this.Data as IObservableArray<T>, (change: IArrayChange<T> | IArraySplice<T>) => {
            this.updateProxies()
            if (change.type == "splice") {
                UndoManager.AddEvent({
                    undo: () => this.Data.splice(change.index, change.addedCount, ...change.removed),
                    redo: () => this.Data.splice(change.index, change.removedCount, ...change.added)
                })
            } else {
                UndoManager.AddEvent({
                    undo: () => this.Data[change.index] = change.oldValue,
                    redo: () => this.Data[change.index] = change.newValue
                })
            }
            if (!this._processingServerUpdate)
                Server.UpdateField(this);
        });
    }

    protected setData(value: T[]) {
        this.data = observable(value);
        this.updateProxies();
        this.observeList();
    }

    private updateProxies() {
        this._proxies = this.Data.map(field => field.Id);
    }

    UpdateFromServer(fields: string[]) {
        this._proxies = fields;
    }
    private arraysEqual(a: any[], b: any[]) {
        if (a === b) return true;
        if (a == null || b == null) return false;
        if (a.length != b.length) return false;

        // If you don't care about the order of the elements inside
        // the array, you should sort both arrays here.
        // Please note that calling sort on an array will modify that array.
        // you might want to clone your array first.

        for (var i = 0; i < a.length; ++i) {
            if (a[i] !== b[i]) return false;
        }
        return true;
    }

    init(callback: (field: Field) => any) {
        Server.GetFields(this._proxies, action((fields: FieldMap) => {
            if (!this.arraysEqual(this._proxies, this.data.map(field => field.Id))) {
                var dataids = this.data.map(d => d.Id);
                var proxies = this._proxies.map(p => p);
                var added = this.data.length < this._proxies.length;
                var deleted = this.data.length > this._proxies.length;
                for (let i = 0; i < dataids.length && added; i++)
                    added = proxies.indexOf(dataids[i]) != -1;
                for (let i = 0; i < this._proxies.length && deleted; i++)
                    deleted = dataids.indexOf(proxies[i]) != -1;

                this._processingServerUpdate = true;
                for (let i = 0; i < proxies.length && added; i++) {
                    if (dataids.indexOf(proxies[i]) === -1)
                        this.Data.splice(i, 0, fields[proxies[i]] as T);
                }
                for (let i = dataids.length - 1; i >= 0 && deleted; i--) {
                    if (proxies.indexOf(dataids[i]) === -1)
                        this.Data.splice(i, 1);
                }
                if (!added && !deleted) {// otherwise, just rebuild the whole list
                    this.setData(proxies.map(id => fields[id] as T));
                }
                this._processingServerUpdate = false;
            }
            callback(this);
        }))
    }

    ToScriptString(): string {
        return "new ListField([" + this.Data.map(field => field.ToScriptString()).join(", ") + "])";
    }

    Copy(): Field {
        return new ListField<T>(this.Data);
    }

    ToJson(): { type: Types, data: string[], _id: string } {
        return {
            type: Types.List,
            data: this._proxies || [],
            _id: this.Id
        }
    }

    static FromJson(id: string, ids: string[]): ListField<Field> {
        let list = new ListField([], id, false);
        list._proxies = ids;
        return list
    }
}