|
1 | 1 | /**
|
2 |
| - * Automatically creates all the text regions containing all instances of the selected text. |
| 2 | + * Automatically creates text regions for all instances of the selected text and deletes existing regions |
| 3 | + * when the shift key is pressed. |
3 | 4 | */
|
4 | 5 |
|
5 |
| -// It will be triggered when a text selection happens |
6 |
| -LSI.on("entityCreate", (region) => { |
| 6 | +// Track the state of the shift key |
| 7 | +let isShiftKeyPressed = false; |
| 8 | + |
| 9 | +window.addEventListener("keydown", (e) => { |
| 10 | + if (e.key === "Shift") { |
| 11 | + isShiftKeyPressed = true; |
| 12 | + } |
| 13 | +}); |
| 14 | + |
| 15 | +window.addEventListener("keyup", (e) => { |
| 16 | + if (e.key === "Shift") { |
| 17 | + isShiftKeyPressed = false; |
| 18 | + } |
| 19 | +}); |
| 20 | + |
| 21 | +LSI.on("entityDelete", (region) => { |
| 22 | + if (!isShiftKeyPressed) return; // Only proceed if the shift key is pressed |
| 23 | + |
7 | 24 | if (window.BULK_REGIONS) return;
|
| 25 | + window.BULK_REGIONS = true; |
| 26 | + setTimeout(() => { |
| 27 | + window.BULK_REGIONS = false; |
| 28 | + }, 1000); |
| 29 | + |
| 30 | + const existingEntities = Htx.annotationStore.selected.regions; |
| 31 | + const regionsToDelete = existingEntities.filter((entity) => { |
| 32 | + const deletedText = region.text.toLowerCase().replace("\\\\n", " "); |
| 33 | + const otherText = entity.text.toLowerCase().replace("\\\\n", " "); |
| 34 | + return deletedText === otherText && region.labels[0] === entity.labels[0]; |
| 35 | + }); |
8 | 36 |
|
| 37 | + for (const region of regionsToDelete) { |
| 38 | + Htx.annotationStore.selected.deleteRegion(region); |
| 39 | + } |
| 40 | + |
| 41 | + Htx.annotationStore.selected.updateObjects(); |
| 42 | +}); |
| 43 | + |
| 44 | +LSI.on("entityCreate", (region) => { |
| 45 | + if (!isShiftKeyPressed) return; |
| 46 | + |
| 47 | + if (window.BULK_REGIONS) return; |
9 | 48 | window.BULK_REGIONS = true;
|
10 | 49 | setTimeout(() => {
|
11 | 50 | window.BULK_REGIONS = false;
|
12 | 51 | }, 1000);
|
13 | 52 |
|
| 53 | + const existingEntities = Htx.annotationStore.selected.regions; |
| 54 | + |
14 | 55 | setTimeout(() => {
|
15 |
| - // Find all the text regions matching the selection |
16 |
| - const matches = Array.from( |
17 |
| - region.object._value.matchAll(new RegExp(region.text, "gi")), |
| 56 | + // Prevent tagging a single character |
| 57 | + if (region.text.length < 2) return; |
| 58 | + regexp = new RegExp( |
| 59 | + region.text.replace("\\\\n", "\\\\s+").replace(" ", "\\\\s+"), |
| 60 | + "gi", |
18 | 61 | );
|
19 |
| - for (const m of matches) { |
20 |
| - if (m.index === region.startOffset) continue; |
21 |
| - |
22 |
| - // Include them in the results as new selections |
23 |
| - Htx.annotationStore.selected.createResult( |
24 |
| - { |
25 |
| - text: region.text, |
26 |
| - start: "/span[1]/text()[1]", |
27 |
| - startOffset: m.index, |
28 |
| - end: "/span[1]/text()[1]", |
29 |
| - endOffset: m.index + region.text.length, |
30 |
| - }, |
31 |
| - { labels: [...region.labeling.value.labels] }, |
32 |
| - region.labeling.from_name, |
33 |
| - region.object, |
34 |
| - ); |
| 62 | + const matches = Array.from(region.object._value.matchAll(regexp)); |
| 63 | + for (const match of matches) { |
| 64 | + if (match.index === region.startOffset) continue; |
| 65 | + |
| 66 | + const startOffset = match.index; |
| 67 | + const endOffset = match.index + region.text.length; |
| 68 | + |
| 69 | + // Check for existing entities with overlapping start and end offset |
| 70 | + let isDuplicate = false; |
| 71 | + for (const entity of existingEntities) { |
| 72 | + if ( |
| 73 | + startOffset <= entity.globalOffsets.end && |
| 74 | + entity.globalOffsets.start <= endOffset |
| 75 | + ) { |
| 76 | + isDuplicate = true; |
| 77 | + break; |
| 78 | + } |
| 79 | + } |
| 80 | + |
| 81 | + if (!isDuplicate) { |
| 82 | + Htx.annotationStore.selected.createResult( |
| 83 | + { |
| 84 | + text: region.text, |
| 85 | + start: "/span[1]/text()[1]", |
| 86 | + startOffset: startOffset, |
| 87 | + end: "/span[1]/text()[1]", |
| 88 | + endOffset: endOffset, |
| 89 | + }, |
| 90 | + { |
| 91 | + labels: [...region.labeling.value.labels], |
| 92 | + }, |
| 93 | + region.labeling.from_name, |
| 94 | + region.object, |
| 95 | + ); |
| 96 | + } |
35 | 97 | }
|
| 98 | + |
36 | 99 | Htx.annotationStore.selected.updateObjects();
|
37 | 100 | }, 100);
|
38 | 101 | });
|
0 commit comments