Revize 4bc99591
Přidáno uživatelem Lukáš Vlček před téměř 3 roky(ů)
webapp/api/api.ts | ||
---|---|---|
76 | 76 |
*/ |
77 | 77 |
'tagInstances'?: Array<TagInstanceInfo> | null; |
78 | 78 |
} |
79 |
/** |
|
80 |
* |
|
81 |
* @export |
|
82 |
* @interface AnnotationInstanceAddRequest |
|
83 |
*/ |
|
84 |
export interface AnnotationInstanceAddRequest { |
|
85 |
/** |
|
86 |
* |
|
87 |
* @type {number} |
|
88 |
* @memberof AnnotationInstanceAddRequest |
|
89 |
*/ |
|
90 |
'position'?: number; |
|
91 |
/** |
|
92 |
* |
|
93 |
* @type {number} |
|
94 |
* @memberof AnnotationInstanceAddRequest |
|
95 |
*/ |
|
96 |
'length'?: number; |
|
97 |
/** |
|
98 |
* |
|
99 |
* @type {ETagType} |
|
100 |
* @memberof AnnotationInstanceAddRequest |
|
101 |
*/ |
|
102 |
'type'?: ETagType; |
|
103 |
/** |
|
104 |
* |
|
105 |
* @type {string} |
|
106 |
* @memberof AnnotationInstanceAddRequest |
|
107 |
*/ |
|
108 |
'id'?: string; |
|
109 |
/** |
|
110 |
* |
|
111 |
* @type {string} |
|
112 |
* @memberof AnnotationInstanceAddRequest |
|
113 |
*/ |
|
114 |
'instanceId'?: string | null; |
|
115 |
} |
|
79 | 116 |
/** |
80 | 117 |
* |
81 | 118 |
* @export |
... | ... | |
321 | 358 |
export type EState = typeof EState[keyof typeof EState]; |
322 | 359 |
|
323 | 360 |
|
361 |
/** |
|
362 |
* |
|
363 |
* @export |
|
364 |
* @enum {string} |
|
365 |
*/ |
|
366 |
|
|
367 |
export const ETagType = { |
|
368 |
Tag: 'TAG', |
|
369 |
Subtag: 'SUBTAG' |
|
370 |
} as const; |
|
371 |
|
|
372 |
export type ETagType = typeof ETagType[keyof typeof ETagType]; |
|
373 |
|
|
374 |
|
|
324 | 375 |
/** |
325 | 376 |
* |
326 | 377 |
* @export |
... | ... | |
553 | 604 |
'subTagId'?: string | null; |
554 | 605 |
/** |
555 | 606 |
* |
556 |
* @type {number}
|
|
607 |
* @type {string}
|
|
557 | 608 |
* @memberof TagInstanceInfo |
558 | 609 |
*/ |
559 |
'instance'?: number;
|
|
610 |
'instance'?: string;
|
|
560 | 611 |
/** |
561 | 612 |
* |
562 | 613 |
* @type {number} |
... | ... | |
710 | 761 |
options: localVarRequestOptions, |
711 | 762 |
}; |
712 | 763 |
}, |
764 |
/** |
|
765 |
* |
|
766 |
* @param {string} annotationId |
|
767 |
* @param {AnnotationInstanceAddRequest} [annotationInstanceAddRequest] |
|
768 |
* @param {*} [options] Override http request option. |
|
769 |
* @throws {RequiredError} |
|
770 |
*/ |
|
771 |
annotationAnnotationIdPost: async (annotationId: string, annotationInstanceAddRequest?: AnnotationInstanceAddRequest, options: AxiosRequestConfig = {}): Promise<RequestArgs> => { |
|
772 |
// verify required parameter 'annotationId' is not null or undefined |
|
773 |
assertParamExists('annotationAnnotationIdPost', 'annotationId', annotationId) |
|
774 |
const localVarPath = `/annotation/{annotationId}` |
|
775 |
.replace(`{${"annotationId"}}`, encodeURIComponent(String(annotationId))); |
|
776 |
// use dummy base URL string because the URL constructor only accepts absolute URLs. |
|
777 |
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); |
|
778 |
let baseOptions; |
|
779 |
if (configuration) { |
|
780 |
baseOptions = configuration.baseOptions; |
|
781 |
} |
|
782 |
|
|
783 |
const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; |
|
784 |
const localVarHeaderParameter = {} as any; |
|
785 |
const localVarQueryParameter = {} as any; |
|
786 |
|
|
787 |
|
|
788 |
|
|
789 |
localVarHeaderParameter['Content-Type'] = 'application/json'; |
|
790 |
|
|
791 |
setSearchParams(localVarUrlObj, localVarQueryParameter); |
|
792 |
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; |
|
793 |
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; |
|
794 |
localVarRequestOptions.data = serializeDataIfNeeded(annotationInstanceAddRequest, localVarRequestOptions, configuration) |
|
795 |
|
|
796 |
return { |
|
797 |
url: toPathString(localVarUrlObj), |
|
798 |
options: localVarRequestOptions, |
|
799 |
}; |
|
800 |
}, |
|
713 | 801 |
/** |
714 | 802 |
* |
715 | 803 |
* @param {AnnotationsAddRequest} [annotationsAddRequest] |
... | ... | |
763 | 851 |
const localVarAxiosArgs = await localVarAxiosParamCreator.annotationAnnotationIdGet(annotationId, options); |
764 | 852 |
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); |
765 | 853 |
}, |
854 |
/** |
|
855 |
* |
|
856 |
* @param {string} annotationId |
|
857 |
* @param {AnnotationInstanceAddRequest} [annotationInstanceAddRequest] |
|
858 |
* @param {*} [options] Override http request option. |
|
859 |
* @throws {RequiredError} |
|
860 |
*/ |
|
861 |
async annotationAnnotationIdPost(annotationId: string, annotationInstanceAddRequest?: AnnotationInstanceAddRequest, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> { |
|
862 |
const localVarAxiosArgs = await localVarAxiosParamCreator.annotationAnnotationIdPost(annotationId, annotationInstanceAddRequest, options); |
|
863 |
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); |
|
864 |
}, |
|
766 | 865 |
/** |
767 | 866 |
* |
768 | 867 |
* @param {AnnotationsAddRequest} [annotationsAddRequest] |
... | ... | |
792 | 891 |
annotationAnnotationIdGet(annotationId: string, options?: any): AxiosPromise<AnnotationInfo> { |
793 | 892 |
return localVarFp.annotationAnnotationIdGet(annotationId, options).then((request) => request(axios, basePath)); |
794 | 893 |
}, |
894 |
/** |
|
895 |
* |
|
896 |
* @param {string} annotationId |
|
897 |
* @param {AnnotationInstanceAddRequest} [annotationInstanceAddRequest] |
|
898 |
* @param {*} [options] Override http request option. |
|
899 |
* @throws {RequiredError} |
|
900 |
*/ |
|
901 |
annotationAnnotationIdPost(annotationId: string, annotationInstanceAddRequest?: AnnotationInstanceAddRequest, options?: any): AxiosPromise<void> { |
|
902 |
return localVarFp.annotationAnnotationIdPost(annotationId, annotationInstanceAddRequest, options).then((request) => request(axios, basePath)); |
|
903 |
}, |
|
795 | 904 |
/** |
796 | 905 |
* |
797 | 906 |
* @param {AnnotationsAddRequest} [annotationsAddRequest] |
... | ... | |
822 | 931 |
return AnnotationApiFp(this.configuration).annotationAnnotationIdGet(annotationId, options).then((request) => request(this.axios, this.basePath)); |
823 | 932 |
} |
824 | 933 |
|
934 |
/** |
|
935 |
* |
|
936 |
* @param {string} annotationId |
|
937 |
* @param {AnnotationInstanceAddRequest} [annotationInstanceAddRequest] |
|
938 |
* @param {*} [options] Override http request option. |
|
939 |
* @throws {RequiredError} |
|
940 |
* @memberof AnnotationApi |
|
941 |
*/ |
|
942 |
public annotationAnnotationIdPost(annotationId: string, annotationInstanceAddRequest?: AnnotationInstanceAddRequest, options?: AxiosRequestConfig) { |
|
943 |
return AnnotationApiFp(this.configuration).annotationAnnotationIdPost(annotationId, annotationInstanceAddRequest, options).then((request) => request(this.axios, this.basePath)); |
|
944 |
} |
|
945 |
|
|
825 | 946 |
/** |
826 | 947 |
* |
827 | 948 |
* @param {AnnotationsAddRequest} [annotationsAddRequest] |
webapp/components/annotation/AnnotationItem.tsx | ||
---|---|---|
19 | 19 |
* @returns The item that represents an annotation. |
20 | 20 |
*/ |
21 | 21 |
export function AnnotationItem(props: { tag: Tag }) { |
22 |
const { markSelectedText } = useContext(AnnotationContext); |
|
23 |
|
|
22 | 24 |
/** |
23 | 25 |
* Should properties of this annotation be visible? |
24 | 26 |
*/ |
... | ... | |
88 | 90 |
<Col sm="auto" className="d-flex align-items-center"> |
89 | 91 |
<TagOutlined /> |
90 | 92 |
</Col> |
91 |
<Col className="d-flex align-items-center">{props.tag.name}</Col> |
|
93 |
<Col className="d-flex align-items-center"> |
|
94 |
{props.tag.tagName} |
|
95 |
{props.tag.subtagName ? ' (' + props.tag.subtagName + ')' : ''} |
|
96 |
</Col> |
|
92 | 97 |
<Col sm="auto"> |
93 | 98 |
<Button |
94 | 99 |
type="text" |
webapp/components/annotation/DocumentAnnotationView.tsx | ||
---|---|---|
15 | 15 |
|
16 | 16 |
return ( |
17 | 17 |
<div> |
18 |
<Button |
|
19 |
onClick={() => { |
|
20 |
const selection = window.getSelection(); |
|
21 |
if (!selection) { |
|
22 |
return; |
|
23 |
} |
|
24 |
if (!annotation?.tagStartPositions || !annotation.tagLengths) { |
|
25 |
console.log('start or lengths not found'); |
|
26 |
return; |
|
27 |
} |
|
28 |
|
|
29 |
let startTag = selection.anchorNode; |
|
30 |
let endTag = selection.focusNode; |
|
31 |
|
|
32 |
if (!startTag || !endTag) { |
|
33 |
console.log('Selection not found'); |
|
34 |
return; |
|
35 |
} |
|
36 |
|
|
37 |
if (startTag.nodeName.includes('#')) { |
|
38 |
startTag = startTag.parentNode; |
|
39 |
} |
|
40 |
if (endTag.nodeName.includes('#')) { |
|
41 |
endTag = endTag.parentNode; |
|
42 |
} |
|
43 |
if (!startTag || !endTag) { |
|
44 |
console.log('Selection element not found'); |
|
45 |
return; |
|
46 |
} |
|
47 |
|
|
48 |
if (!(startTag instanceof Element)) { |
|
49 |
console.log('StartTag is not instance of Element'); |
|
50 |
return; |
|
51 |
} |
|
52 |
if (!(endTag instanceof Element)) { |
|
53 |
console.log('EndTag is not instance of Element'); |
|
54 |
return; |
|
55 |
} |
|
56 |
|
|
57 |
const startElement = startTag as Element; |
|
58 |
const endElement = endTag as Element; |
|
59 |
|
|
60 |
const startId: number = |
|
61 |
Number(startElement.getAttribute('aswi-tag-id')) ?? -1; |
|
62 |
const endId: number = |
|
63 |
Number(endElement.getAttribute('aswi-tag-id')) ?? -1; |
|
64 |
|
|
65 |
const startPosition = |
|
66 |
annotation.tagStartPositions[startId] + |
|
67 |
annotation.tagLengths[startId] + |
|
68 |
selection.anchorOffset; |
|
69 |
const endPosition = |
|
70 |
annotation.tagStartPositions[endId] + |
|
71 |
annotation.tagLengths[endId] + |
|
72 |
selection.focusOffset - |
|
73 |
1; |
|
74 |
}} |
|
75 |
> |
|
76 |
Test |
|
77 |
</Button> |
|
78 | 18 |
<div |
79 | 19 |
dangerouslySetInnerHTML={{ __html: annotation.documentToRender ?? '' }} |
80 | 20 |
/> |
webapp/components/types/tag.tsx | ||
---|---|---|
1 |
import { TagInstanceInfo } from '../../api'; |
|
1 |
import { SubTagInfo, TagInfo, TagInstanceInfo } from '../../api';
|
|
2 | 2 |
|
3 | 3 |
/** |
4 | 4 |
* Special tag used in annotation panel. |
5 | 5 |
*/ |
6 | 6 |
export type Tag = { |
7 |
name: string; |
|
7 |
tagName: string; |
|
8 |
subtagName: string | null; |
|
9 |
|
|
8 | 10 |
category: string; |
9 | 11 |
visible: boolean; |
10 | 12 |
occurrences: TagInstanceInfo[]; |
13 |
|
|
14 |
tagId: string; |
|
15 |
subtagId: string | null; |
|
16 |
instanceId: string; |
|
11 | 17 |
}; |
webapp/contexts/AnnotationContext.tsx | ||
---|---|---|
1 | 1 |
import React, { createContext, useEffect, useState } from 'react'; |
2 |
import { AnnotationInfo, TagInstanceInfo } from '../api'; |
|
2 |
import { AnnotationInfo, ETagType, SubTagInfo, TagInfo, TagInstanceInfo } from '../api';
|
|
3 | 3 |
import { Tag } from '../components/types/tag'; |
4 | 4 |
import { annotationController } from '../controllers'; |
5 |
import { GetSelectionInfo } from '../utils/selectionUtils'; |
|
5 | 6 |
|
6 | 7 |
/** |
7 | 8 |
* Interface of an annotation context provider. |
... | ... | |
53 | 54 |
annotation: AnnotationInfo | null; |
54 | 55 |
mappedTags: Tag[] | null; |
55 | 56 |
refreshAnnotation: () => void; |
57 |
|
|
58 |
markSelectedText: ( |
|
59 |
tagId: string, |
|
60 |
subtagId: string | null, |
|
61 |
instanceID: string | null |
|
62 |
) => void; |
|
56 | 63 |
} |
57 | 64 |
|
58 | 65 |
/** |
... | ... | |
119 | 126 |
refreshAnnotation: () => { |
120 | 127 |
return; |
121 | 128 |
}, |
129 |
|
|
130 |
markSelectedText: () => { |
|
131 |
return; |
|
132 |
}, |
|
122 | 133 |
}); |
123 | 134 |
|
124 | 135 |
/** |
... | ... | |
139 | 150 |
|
140 | 151 |
const [mappedTags, setMappedTags] = useState<Tag[] | null>(null); |
141 | 152 |
|
153 |
async function markSelectedText( |
|
154 |
tagId: string, |
|
155 |
subtagId: string | null, |
|
156 |
instanceId: string | null |
|
157 |
) { |
|
158 |
if (!annotation) { |
|
159 |
console.log('annotation not found'); |
|
160 |
return; |
|
161 |
} |
|
162 |
|
|
163 |
const selectionInfo = GetSelectionInfo(annotation); |
|
164 |
if (!selectionInfo) { |
|
165 |
console.log( |
|
166 |
'not able to continue, selection processing not completed successfully' |
|
167 |
); |
|
168 |
return; |
|
169 |
} |
|
170 |
|
|
171 |
const id = subtagId ?? tagId; |
|
172 |
const type: ETagType = subtagId == null ? ETagType.Tag : ETagType.Subtag; |
|
173 |
|
|
174 |
const res = await annotationController.annotationAnnotationIdPost( |
|
175 |
props.annotationId, |
|
176 |
{ |
|
177 |
id: id, |
|
178 |
instanceId, |
|
179 |
type, |
|
180 |
position: selectionInfo.startPositionOriginalDocument, |
|
181 |
length: selectionInfo.selectionLengthOriginalDocument, |
|
182 |
} |
|
183 |
); |
|
184 |
console.log('res'); |
|
185 |
console.log(res); |
|
186 |
|
|
187 |
await refreshAnnotation(); |
|
188 |
} |
|
189 |
|
|
142 | 190 |
/** |
143 | 191 |
* Default implementation of addOccurrence method. |
144 | 192 |
* @param tag The tag with new occurrence. |
145 | 193 |
*/ |
146 |
const addOccurrence = (tag: Tag) => { |
|
147 |
//TODO: Implement method (should use objects from server API)
|
|
194 |
const addOccurrence = async (tag: Tag) => {
|
|
195 |
await markSelectedText(tag.tagId, tag.subtagId ?? null, tag.instanceId);
|
|
148 | 196 |
}; |
149 | 197 |
|
150 | 198 |
/** |
... | ... | |
182 | 230 |
}; |
183 | 231 |
|
184 | 232 |
const remapAnnotations = (data: AnnotationInfo) => { |
185 |
let map = new Map<number, Tag>();
|
|
233 |
let map = new Map<string, Tag>();
|
|
186 | 234 |
data.tagInstances?.forEach((tagInstance) => { |
187 |
if (map.has(tagInstance.instance ?? 0)) {
|
|
188 |
let tag = map.get(tagInstance.instance ?? 0);
|
|
235 |
if (map.has(tagInstance.instance ?? '-')) {
|
|
236 |
let tag = map.get(tagInstance.instance ?? '-');
|
|
189 | 237 |
tag!.occurrences = [...tag!.occurrences, tagInstance]; |
190 | 238 |
} else { |
191 |
map.set(tagInstance.position ?? 0, { |
|
192 |
name: tagInstance.tagName ?? '', |
|
239 |
map.set(tagInstance.instance ?? '-', { |
|
240 |
tagName: tagInstance.tagName ?? '', |
|
241 |
subtagName: tagInstance.subTagName ?? null, |
|
193 | 242 |
category: tagInstance.tagCategoryName ?? '', |
194 | 243 |
visible: true, |
195 | 244 |
occurrences: [tagInstance], |
245 |
tagId: tagInstance.tagId ?? '', |
|
246 |
instanceId: tagInstance.instance ?? '', |
|
247 |
subtagId: tagInstance.subTagId ?? null, |
|
196 | 248 |
}); |
197 | 249 |
} |
198 | 250 |
}); |
... | ... | |
229 | 281 |
refreshAnnotation, |
230 | 282 |
annotation, |
231 | 283 |
mappedTags, |
284 |
markSelectedText, |
|
232 | 285 |
}} |
233 | 286 |
> |
234 | 287 |
{props.children} |
webapp/contexts/TagCategoryContext.tsx | ||
---|---|---|
1 |
import React, { createContext, useEffect, useState } from 'react'; |
|
1 |
import React, { createContext, useContext, useEffect, useState } from 'react';
|
|
2 | 2 |
import { SubTagInfo, TagCategoryInfo, TagInfo } from '../api'; |
3 | 3 |
import { tagController } from '../controllers'; |
4 |
import { LoggedUserContext } from './LoggedUserContext'; |
|
5 |
import Annotation from '../pages/annotation/[id]'; |
|
6 |
import { AnnotationContext } from './AnnotationContext'; |
|
4 | 7 |
|
5 | 8 |
/** |
6 | 9 |
* Interface of a tag context provider. |
... | ... | |
78 | 81 |
* @returns Prepared html of the provider. |
79 | 82 |
*/ |
80 | 83 |
const TagCategoryProvider = (props: { children: React.ReactNode }) => { |
84 |
const { markSelectedText } = useContext(AnnotationContext); |
|
85 |
|
|
81 | 86 |
/** |
82 | 87 |
* Tags managed by the context. |
83 | 88 |
*/ |
... | ... | |
99 | 104 |
const selectTag = (tag: TagInfo, subTag: SubTagInfo | null) => { |
100 | 105 |
setSelectedTag(tag); |
101 | 106 |
setSelectedSubTag(subTag); |
107 |
|
|
108 |
if (!tag.id) { |
|
109 |
console.log('invalid selection'); |
|
110 |
return; |
|
111 |
} |
|
112 |
|
|
113 |
markSelectedText(tag.id, subTag?.id ?? null, null); |
|
102 | 114 |
}; |
103 | 115 |
|
104 | 116 |
/** |
webapp/pages/annotation/[id].tsx | ||
---|---|---|
5 | 5 |
import 'antd/dist/antd.css'; |
6 | 6 |
import styles from '/styles/Annotation.module.scss'; |
7 | 7 |
import AnnotationProvider from '../../contexts/AnnotationContext'; |
8 |
import TagCategoryProvider from '../../contexts/TagCategoryContext'; |
|
9 |
|
|
8 | 10 |
import { useRouter } from 'next/router'; |
9 | 11 |
|
10 | 12 |
/** |
... | ... | |
27 | 29 |
|
28 | 30 |
return ( |
29 | 31 |
<AnnotationProvider annotationId={annotationId}> |
30 |
<MainLayout> |
|
31 |
<div className={styles.layoutWrapper}> |
|
32 |
<div className={styles.tags}> |
|
33 |
<TagPanel /> |
|
34 |
</div> |
|
35 |
<div className={styles.document}> |
|
36 |
<DocumentAnnotationView /> |
|
37 |
</div> |
|
38 |
<div className={styles.annotations}> |
|
39 |
<AnnotationPanel /> |
|
32 |
<TagCategoryProvider> |
|
33 |
<MainLayout> |
|
34 |
<div className={styles.layoutWrapper}> |
|
35 |
<div className={styles.tags}> |
|
36 |
<TagPanel /> |
|
37 |
</div> |
|
38 |
<div className={styles.document}> |
|
39 |
<DocumentAnnotationView /> |
|
40 |
</div> |
|
41 |
<div className={styles.annotations}> |
|
42 |
<AnnotationPanel /> |
|
43 |
</div> |
|
40 | 44 |
</div> |
41 |
</div>
|
|
42 |
</MainLayout>
|
|
45 |
</MainLayout>
|
|
46 |
</TagCategoryProvider>
|
|
43 | 47 |
</AnnotationProvider> |
44 | 48 |
); |
45 | 49 |
} |
webapp/utils/selectionUtils.ts | ||
---|---|---|
1 |
import { AnnotationInfo } from '../api'; |
|
2 |
|
|
3 |
export interface SelectionInfo { |
|
4 |
startElementId: number; |
|
5 |
endElementId: number; |
|
6 |
startPositionOriginalDocument: number; |
|
7 |
endPositionOriginalDocument: number; |
|
8 |
selectionLengthOriginalDocument: number; |
|
9 |
} |
|
10 |
export function GetSelectionInfo(annotation: AnnotationInfo): SelectionInfo | null { |
|
11 |
const selection = window.getSelection(); |
|
12 |
if (!selection) { |
|
13 |
return null; |
|
14 |
} |
|
15 |
if (!annotation?.tagStartPositions || !annotation.tagLengths) { |
|
16 |
console.log('start or lengths not found'); |
|
17 |
return null; |
|
18 |
} |
|
19 |
|
|
20 |
let startTag = selection.anchorNode; |
|
21 |
let endTag = selection.focusNode; |
|
22 |
|
|
23 |
if (!startTag || !endTag) { |
|
24 |
console.log('Selection not found'); |
|
25 |
return null; |
|
26 |
} |
|
27 |
|
|
28 |
if (startTag.nodeName.includes('#')) { |
|
29 |
startTag = startTag.parentNode; |
|
30 |
} |
|
31 |
if (endTag.nodeName.includes('#')) { |
|
32 |
endTag = endTag.parentNode; |
|
33 |
} |
|
34 |
if (!startTag || !endTag) { |
|
35 |
console.log('Selection element not found'); |
|
36 |
return null; |
|
37 |
} |
|
38 |
|
|
39 |
if (!(startTag instanceof Element)) { |
|
40 |
console.log('StartTag is not instance of Element'); |
|
41 |
return null; |
|
42 |
} |
|
43 |
if (!(endTag instanceof Element)) { |
|
44 |
console.log('EndTag is not instance of Element'); |
|
45 |
return null; |
|
46 |
} |
|
47 |
|
|
48 |
let startElement = startTag as Element; |
|
49 |
let endElement = endTag as Element; |
|
50 |
|
|
51 |
let startId: number = Number(startElement.getAttribute('aswi-tag-id')) ?? -1; |
|
52 |
let endId: number = Number(endElement.getAttribute('aswi-tag-id')) ?? -1; |
|
53 |
|
|
54 |
let startPosition = |
|
55 |
annotation.tagStartPositions[startId] + |
|
56 |
annotation.tagLengths[startId] + |
|
57 |
selection.anchorOffset; |
|
58 |
let endPosition = |
|
59 |
annotation.tagStartPositions[endId] + |
|
60 |
annotation.tagLengths[endId] + |
|
61 |
selection.focusOffset - |
|
62 |
1; |
|
63 |
|
|
64 |
// need to switch start and end elements (selection was in the opposite way then expected) |
|
65 |
if (startPosition > endPosition) { |
|
66 |
let temp = startPosition; |
|
67 |
startPosition = endPosition; |
|
68 |
endPosition = temp; |
|
69 |
|
|
70 |
temp = startId; |
|
71 |
startId = endId; |
|
72 |
endId = temp; |
|
73 |
|
|
74 |
const tempElement = startElement; |
|
75 |
startElement = endElement; |
|
76 |
endElement = tempElement; |
|
77 |
} |
|
78 |
|
|
79 |
const length = endPosition - startPosition + 1; |
|
80 |
|
|
81 |
return { |
|
82 |
endElementId: endId, |
|
83 |
startElementId: startId, |
|
84 |
startPositionOriginalDocument: startPosition, |
|
85 |
endPositionOriginalDocument: endPosition, |
|
86 |
selectionLengthOriginalDocument: length, |
|
87 |
}; |
|
88 |
} |
Také k dispozici: Unified diff
Adding tag/subtag to selected text