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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
|
import { observable, action } from "mobx";
import { StrCast, Cast } from "../../new_fields/Types";
import { Doc, DocListCast } from "../../new_fields/Doc";
import { listSpec } from "../../new_fields/Schema";
import { List } from "../../new_fields/List";
import { Id } from "../../new_fields/FieldSymbols";
/*
* link doc:
* - anchor1: doc
* - anchor1page: number
* - anchor1groups: list of group docs representing the groups anchor1 categorizes this link/anchor2 in
* - anchor2: doc
* - anchor2page: number
* - anchor2groups: list of group docs representing the groups anchor2 categorizes this link/anchor1 in
*
* group doc:
* - type: string representing the group type/name/category
* - metadata: doc representing the metadata kvps
*
* metadata doc:
* - user defined kvps
*/
export class LinkManager {
private static _instance: LinkManager;
public static get Instance(): LinkManager {
return this._instance || (this._instance = new this());
}
private constructor() {
}
@observable public allLinks: Array<Doc> = []; // list of link docs
@observable public groupMetadataKeys: Map<string, Array<string>> = new Map();
// map of group type to list of its metadata keys; serves as a dictionary of groups to what kind of metadata it hodls
@observable public linkProxies: Array<Doc> = []; // list of linkbutton docs - used to visualize link when an anchors are not in the same context
// finds all links that contain the given anchor
public findAllRelatedLinks(anchor: Doc): Array<Doc> {
return LinkManager.Instance.allLinks.filter(link => {
let protomatch1 = Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, new Doc));
let protomatch2 = Doc.AreProtosEqual(anchor, Cast(link.anchor2, Doc, new Doc));
let idmatch1 = StrCast(anchor[Id]) === StrCast(Cast(link.anchor1, Doc, new Doc)[Id]);
let idmatch2 = StrCast(anchor[Id]) === StrCast(Cast(link.anchor2, Doc, new Doc)[Id]);
return protomatch1 || protomatch2 || idmatch1 || idmatch2;
});
}
// returns map of group type to anchor's links in that group type
public findRelatedGroupedLinks(anchor: Doc): Map<string, Array<Doc>> {
let related = this.findAllRelatedLinks(anchor);
let anchorGroups = new Map<string, Array<Doc>>();
related.forEach(link => {
let groups = LinkManager.Instance.getAnchorGroups(link, anchor);
if (groups.length > 0) {
groups.forEach(groupDoc => {
let groupType = StrCast(groupDoc.type);
let group = anchorGroups.get(groupType);
if (group) group.push(link);
else group = [link];
anchorGroups.set(groupType, group);
});
} else {
// if link is in no groups then put it in default group
let group = anchorGroups.get("*");
if (group) group.push(link);
else group = [link];
anchorGroups.set("*", group);
}
});
return anchorGroups;
}
// returns a list of all metadata docs associated with the given group type
public findAllMetadataDocsInGroup(groupType: string): Array<Doc> {
let md: Doc[] = [];
let allLinks = LinkManager.Instance.allLinks;
allLinks.forEach(linkDoc => {
let anchor1Groups = LinkManager.Instance.getAnchorGroups(linkDoc, Cast(linkDoc.anchor1, Doc, new Doc));
let anchor2Groups = LinkManager.Instance.getAnchorGroups(linkDoc, Cast(linkDoc.anchor2, Doc, new Doc));
anchor1Groups.forEach(groupDoc => { if (StrCast(groupDoc.type).toUpperCase() === groupType.toUpperCase()) md.push(Cast(groupDoc.metadata, Doc, new Doc)); });
anchor2Groups.forEach(groupDoc => { if (StrCast(groupDoc.type).toUpperCase() === groupType.toUpperCase()) md.push(Cast(groupDoc.metadata, Doc, new Doc)); });
});
return md;
}
// removes all group docs from all links with the given group type
public deleteGroup(groupType: string): void {
let deleted = LinkManager.Instance.groupMetadataKeys.delete(groupType);
if (deleted) {
LinkManager.Instance.allLinks.forEach(linkDoc => {
LinkManager.Instance.removeGroupFromAnchor(linkDoc, Cast(linkDoc.anchor1, Doc, new Doc), groupType);
LinkManager.Instance.removeGroupFromAnchor(linkDoc, Cast(linkDoc.anchor2, Doc, new Doc), groupType);
});
}
}
// removes group doc of given group type only from given anchor on given link
public removeGroupFromAnchor(linkDoc: Doc, anchor: Doc, groupType: string) {
let groups = LinkManager.Instance.getAnchorGroups(linkDoc, anchor);
let newGroups = groups.filter(groupDoc => StrCast(groupDoc.type).toUpperCase() !== groupType.toUpperCase());
LinkManager.Instance.setAnchorGroups(linkDoc, anchor, newGroups);
}
// checks if a link with the given anchors exists
public doesLinkExist(anchor1: Doc, anchor2: Doc) {
let allLinks = LinkManager.Instance.allLinks;
let index = allLinks.findIndex(linkDoc => {
return (Doc.AreProtosEqual(Cast(linkDoc.anchor1, Doc, new Doc), anchor1) && Doc.AreProtosEqual(Cast(linkDoc.anchor2, Doc, new Doc), anchor2)) ||
(Doc.AreProtosEqual(Cast(linkDoc.anchor1, Doc, new Doc), anchor2) && Doc.AreProtosEqual(Cast(linkDoc.anchor2, Doc, new Doc), anchor1));
});
return index !== -1;
}
// finds the opposite anchor of a given anchor in a link
public findOppositeAnchor(linkDoc: Doc, anchor: Doc): Doc {
if (Doc.AreProtosEqual(anchor, Cast(linkDoc.anchor1, Doc, new Doc))) {
return Cast(linkDoc.anchor2, Doc, new Doc);
} else {
return Cast(linkDoc.anchor1, Doc, new Doc);
}
}
// gets the groups associates with an anchor in a link
public getAnchorGroups(linkDoc: Doc, anchor: Doc): Array<Doc> {
if (Doc.AreProtosEqual(anchor, Cast(linkDoc.anchor1, Doc, new Doc))) {
return DocListCast(linkDoc.anchor1Groups);
} else {
return DocListCast(linkDoc.anchor2Groups);
}
}
// sets the groups of the given anchor in the given link
public setAnchorGroups(linkDoc: Doc, anchor: Doc, groups: Doc[]) {
if (Doc.AreProtosEqual(anchor, Cast(linkDoc.anchor1, Doc, new Doc))) {
linkDoc.anchor1Groups = new List<Doc>(groups);
} else {
linkDoc.anchor2Groups = new List<Doc>(groups);
}
}
@action
public addLinkProxy(proxy: Doc) {
LinkManager.Instance.linkProxies.push(proxy);
}
public findLinkProxy(sourceViewId: string, targetViewId: string): Doc | undefined {
let index = LinkManager.Instance.linkProxies.findIndex(p => {
return StrCast(p.sourceViewId) === sourceViewId && StrCast(p.targetViewId) === targetViewId;
});
return index > -1 ? LinkManager.Instance.linkProxies[index] : undefined;
}
}
|