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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
|
import { action, runInAction } from 'mobx';
import { simulateMouseClick } from '../../ClientUtils';
import { Doc, Opt } from '../../fields/Doc';
import { Cast, StrCast } from '../../fields/Types';
import { WebField } from '../../fields/URLField';
import { Docs } from '../documents/Documents';
import { DocumentLinksButton } from '../views/nodes/DocumentLinksButton';
import { DocumentView } from '../views/nodes/DocumentView';
import { DocumentManager } from './DocumentManager';
import { SelectionManager } from './SelectionManager';
export namespace Hypothesis {
/**
* Retrieve a WebDocument with the given url, prioritizing results that are on screen.
* If none exist, create and return a new WebDocument.
*/
export const getSourceWebDoc = async (uri: string) => {
const result = await findWebDoc(uri);
console.log(result ? 'existing doc found' : 'existing doc NOT found');
return result || Docs.Create.WebDocument(uri, { title: uri, _nativeWidth: 850, _height: 512, _width: 400, data_useCors: true }); // create and return a new Web doc with given uri if no matching docs are found
};
/**
* Search for a WebDocument whose url field matches the given uri, return undefined if not found
*/
export const findWebDoc = async (uri: string) => {
const currentDoc = SelectionManager.Docs.lastElement();
if (currentDoc && Cast(currentDoc.data, WebField)?.url.href === uri) return currentDoc; // always check first whether the currently selected doc is the annotation's source, only use Search otherwise
const results: Doc[] = [];
// await SearchUtil.Search('web', true).then(
// action(async (res: SearchUtil.DocSearchResult) => {
// const docs = res.docs;
// const filteredDocs = docs.filter(doc => doc.author === ClientUtils.CurrentUserEmail() && doc.type === DocumentType.WEB && doc.data);
// filteredDocs.forEach(doc => {
// uri === Cast(doc.data, WebField)?.url.href && results.push(doc); // TODO check visited sites history?
// });
// })
// );
const onScreenResults = results.filter(doc => DocumentManager.Instance.getFirstDocumentView(doc));
return onScreenResults.length ? onScreenResults[0] : results.length ? results[0] : undefined; // prioritize results that are currently on the screen
};
/**
* listen for event from Hypothes.is plugin to link an annotation to Dash
*/
export const linkListener = async (e: any) => {
const annotationId: string = e.detail.id;
const annotationUri: string = StrCast(e.detail.uri).split('#annotations:')[0]; // clean hypothes.is URLs that reference a specific annotation
const sourceDoc: Doc = await getSourceWebDoc(annotationUri);
if (!DocumentLinksButton.StartLink || sourceDoc === DocumentLinksButton.StartLink) {
// start new link if there were none already started, or if the old startLink came from the same web document (prevent links to itself)
runInAction(() => {
DocumentLinksButton.AnnotationId = annotationId;
DocumentLinksButton.AnnotationUri = annotationUri;
DocumentLinksButton.StartLink = sourceDoc;
DocumentLinksButton.StartLinkView = undefined;
});
} else {
// if a link has already been started, complete the link to sourceDoc
runInAction(() => {
DocumentLinksButton.AnnotationId = annotationId;
DocumentLinksButton.AnnotationUri = annotationUri;
});
const endLinkView = DocumentManager.Instance.getFirstDocumentView(sourceDoc);
const rect = document.body.getBoundingClientRect();
const x = rect.x + rect.width / 2;
const y = 250;
DocumentLinksButton.finishLinkClick(x, y, DocumentLinksButton.StartLink, sourceDoc, false, endLinkView);
}
};
/**
* Send message to Hypothes.is client to edit an annotation to add a Dash hyperlink
*/
export const makeLink = async (title: string, url: string, annotationId: string, annotationSourceDoc: Doc) => {
// if the annotation's source webpage isn't currently loaded in Dash, we're not able to access and edit the annotation from the client
// so we're loading the webpage and its annotations invisibly in a WebBox in MainView.tsx, until the editing is done
//!DocumentManager.Instance.getFirstDocumentView(annotationSourceDoc) && runInAction(() => DocumentLinksButton.invisibleWebDoc = annotationSourceDoc);
var success = false;
const onSuccess = action(() => {
console.log('Edit success!!');
success = true;
clearTimeout(interval);
//DocumentLinksButton.invisibleWebDoc = undefined;
document.removeEventListener('editSuccess', onSuccess);
});
const newHyperlink = `[${title}\n](${url})`;
const interval = setInterval(
() =>
// keep trying to edit until annotations have loaded and editing is successful
!success &&
document.dispatchEvent(
new CustomEvent<{ newHyperlink: string; id: string }>('addLink', {
detail: { newHyperlink: newHyperlink, id: annotationId },
bubbles: true,
})
),
300
);
setTimeout(
action(() => {
if (!success) {
clearInterval(interval);
//DocumentLinksButton.invisibleWebDoc = undefined;
}
}),
10000
); // give up if no success after 10s
document.addEventListener('editSuccess', onSuccess);
};
/**
* Send message Hypothes.is client request to edit an annotation to find and delete the target Dash hyperlink
*/
export const deleteLink = async (linkDoc: Doc, sourceDoc: Doc, destinationDoc: Doc) => {
if (Cast(destinationDoc.data, WebField)?.url.href !== StrCast(linkDoc.annotationUri)) return; // check that the destinationDoc is a WebDocument containing the target annotation
//!DocumentManager.Instance.getFirstDocumentView(destinationDoc) && runInAction(() => DocumentLinksButton.invisibleWebDoc = destinationDoc); // see note in makeLink
var success = false;
const onSuccess = action(() => {
console.log('Edit success!');
success = true;
clearTimeout(interval);
// DocumentLinksButton.invisibleWebDoc = undefined;
document.removeEventListener('editSuccess', onSuccess);
});
const annotationId = StrCast(linkDoc.annotationId);
const linkUrl = Doc.globalServerPath(sourceDoc);
const interval = setInterval(() => {
// keep trying to edit until annotations have loaded and editing is successful
!success &&
document.dispatchEvent(
new CustomEvent<{ targetUrl: string; id: string }>('deleteLink', {
detail: { targetUrl: linkUrl, id: annotationId },
bubbles: true,
})
);
}, 300);
setTimeout(
action(() => {
if (!success) {
clearInterval(interval);
//DocumentLinksButton.invisibleWebDoc = undefined;
}
}),
10000
); // give up if no success after 10s
document.addEventListener('editSuccess', onSuccess);
};
/**
* Send message to Hypothes.is client to scroll to an annotation when it loads
*/
export const scrollToAnnotation = (annotationId: string, target: Doc) => {
var success = false;
const onSuccess = () => {
console.log('Scroll success!!');
document.removeEventListener('scrollSuccess', onSuccess);
clearInterval(interval);
success = true;
};
const interval = setInterval(() => {
// keep trying to scroll every 250ms until annotations have loaded and scrolling is successful
document.dispatchEvent(
new CustomEvent('scrollToAnnotation', {
detail: annotationId,
bubbles: true,
})
);
const targetView: Opt<DocumentView> = DocumentManager.Instance.getFirstDocumentView(target);
const position = targetView?.screenToViewTransform().inverse().transformPoint(0, 0);
targetView && position && simulateMouseClick(targetView.ContentDiv!, position[0], position[1], position[0], position[1], false);
}, 300);
document.addEventListener('scrollSuccess', onSuccess); // listen for success message from client
setTimeout(() => !success && clearInterval(interval), 10000); // give up if no success after 10s
};
}
|