11import { Origin , type User } from '@annotorious/core' ;
22import { v4 as uuidv4 } from 'uuid' ;
33import type { TextAnnotatorState } from './state' ;
4- import type { TextSelector , TextAnnotationTarget } from './model' ;
4+ import type { TextSelector , TextAnnotationTarget , TextSelectorQuote } from './model' ;
55import { trimRange } from './utils' ;
66
7- export const rangeToSelector = ( range : Range , container : HTMLElement , offsetReferenceSelector ?: string ) : TextSelector => {
8- const offsetReference : HTMLElement = offsetReferenceSelector ?
9- ( range . startContainer . parentElement as HTMLElement ) . closest ( offsetReferenceSelector ) : container ;
10-
11- // Helper range from the start of the contentNode to the start of the selection
12- const rangeBefore = document . createRange ( ) ;
13- rangeBefore . setStart ( offsetReference , 0 ) ;
14- rangeBefore . setEnd ( range . startContainer , range . startOffset ) ;
15-
7+ const rangeToQuote = ( range : Range ) : TextSelectorQuote => {
168 /**
17- * Capture the prefix and suffix of the selection to match the `Text Quote Selector` spec from the W3C Annotation Data Model
9+ * Captures the prefix and suffix snippets of the selection to match the `Text Quote Selector` spec
1810 * @see https://www.w3.org/TR/annotation-model/#text-quote-selector
1911 */
2012 const snippetLength = 10 ;
2113
2214 const rangePrefix = document . createRange ( ) ;
23- const rangePrefixStartOffset = range . startOffset < snippetLength ? 0 : range . startOffset - snippetLength ;
24- rangePrefix . setStart ( range . startContainer , rangePrefixStartOffset ) ;
15+ rangePrefix . setStart ( range . startContainer , Math . max ( range . startOffset - snippetLength , 0 ) ) ;
2516 rangePrefix . setEnd ( range . startContainer , range . startOffset ) ;
2617
2718 const rangeSuffix = document . createRange ( ) ;
28- const rangeSuffixEndOffset = range . endOffset + snippetLength > range . endContainer . textContent . length ? range . endContainer . textContent . length : range . endOffset + snippetLength ;
2919 rangeSuffix . setStart ( range . endContainer , range . endOffset ) ;
30- rangeSuffix . setEnd ( range . endContainer , rangeSuffixEndOffset ) ;
20+ rangeSuffix . setEnd ( range . endContainer , Math . min ( range . endOffset + snippetLength , range . endContainer . textContent . length ) ) ;
3121
32- const quote = {
22+ return {
3323 exact : range . toString ( ) ,
3424 prefix : rangePrefix . toString ( ) ,
3525 suffix : rangeSuffix . toString ( )
3626 }
27+ }
28+
29+ export const rangeToSelector = ( range : Range , container : HTMLElement , offsetReferenceSelector ?: string ) : TextSelector => {
30+ const offsetReference : HTMLElement = offsetReferenceSelector
31+ ? ( range . startContainer . parentElement as HTMLElement ) . closest ( offsetReferenceSelector )
32+ : container ;
33+
34+ // Helper range from the start of the contentNode to the start of the selection
35+ const rangeBefore = document . createRange ( ) ;
36+ rangeBefore . setStart ( offsetReference , 0 ) ;
37+ rangeBefore . setEnd ( range . startContainer , range . startOffset ) ;
38+
39+ const quote = rangeToQuote ( range ) ;
3740 const start = rangeBefore . toString ( ) . length ;
3841 const end = start + quote . exact . length ;
3942
@@ -43,7 +46,7 @@ export const rangeToSelector = (range: Range, container: HTMLElement, offsetRefe
4346}
4447
4548export const SelectionHandler = (
46- container : HTMLElement ,
49+ container : HTMLElement ,
4750 state : TextAnnotatorState ,
4851 // Experimental
4952 offsetReferenceSelector ?: string
@@ -66,7 +69,7 @@ export const SelectionHandler = (
6669 return ;
6770
6871 // Make sure we don't listen to selection changes that
69- // were not started on the container, or which are not supposed to
72+ // were not started on the container, or which are not supposed to
7073 // be annotatable (like the popup)
7174 const annotatable = ! ( evt . target as Node ) . parentElement . closest ( '.not-annotatable' ) ;
7275 if ( annotatable ) {
@@ -91,7 +94,7 @@ export const SelectionHandler = (
9194 debounceTimer = setTimeout ( ( ) => onSelectionChange ( ) , 50 ) ;
9295 } ) ;
9396
94- const onSelectionChange = ( ) => {
97+ const onSelectionChange = ( ) => {
9598 const sel = document . getSelection ( ) ;
9699
97100 if ( ! sel . isCollapsed && isLeftClick && currentTarget ) {
@@ -108,7 +111,7 @@ export const SelectionHandler = (
108111 ...currentTarget ,
109112 selector : rangeToSelector ( ranges [ 0 ] , container , offsetReferenceSelector )
110113 } ;
111-
114+
112115 if ( store . getAnnotation ( currentTarget . annotation ) ) {
113116 store . updateTarget ( currentTarget , Origin . LOCAL ) ;
114117 } else {
@@ -148,13 +151,13 @@ export const SelectionHandler = (
148151
149152 currentTarget = null ;
150153 lastPointerEvent = undefined ;
151- } else {
154+ } else {
152155 const { x, y } = container . getBoundingClientRect ( ) ;
153-
156+
154157 const hovered = store . getAt ( evt . clientX - x , evt . clientY - y ) ;
155158 if ( hovered ) {
156159 const { selected } = selection ;
157-
160+
158161 if ( selected . length !== 1 || selected [ 0 ] . id !== hovered . id ) {
159162 selection . clickSelect ( hovered . id , evt ) ;
160163 lastPointerEvent = undefined ;
0 commit comments