@@ -22,6 +22,7 @@ export function install(container: Element) {
2222 installed += containers . has ( container ) ? 0 : 1
2323 containers . set ( container , 1 )
2424 document . addEventListener ( 'keydown' , quoteSelection )
25+ container . addEventListener ( 'copy' , onCopy )
2526}
2627
2728export function uninstall ( container : Element ) {
@@ -30,6 +31,29 @@ export function uninstall(container: Element) {
3031 if ( ! installed ) {
3132 document . removeEventListener ( 'keydown' , quoteSelection )
3233 }
34+ container . removeEventListener ( 'copy' , onCopy )
35+ }
36+
37+ function onCopy ( event : ClipboardEvent ) {
38+ const target = event . target
39+ if ( ! ( target instanceof HTMLElement ) ) return
40+ if ( isFormField ( target ) ) return
41+
42+ const transfer = event . clipboardData
43+ if ( ! transfer ) return
44+
45+ const selection = window . getSelection ( )
46+ let range
47+ try {
48+ range = selection . getRangeAt ( 0 )
49+ } catch ( err ) {
50+ return
51+ }
52+ const quoted = extractQuote ( selection . toString ( ) , range )
53+ if ( ! quoted ) return
54+
55+ transfer . setData ( 'text/plain' , quoted . selectionText )
56+ event . preventDefault ( )
3357}
3458
3559function eventIsNotRelevant ( event : KeyboardEvent ) : boolean {
@@ -72,17 +96,47 @@ function quoteSelection(event: KeyboardEvent): void {
7296}
7397
7498export function quote ( text : string , range : Range ) : boolean {
99+ const quoted = extractQuote ( text , range )
100+ if ( ! quoted ) return false
101+
102+ const { container, selectionText} = quoted
103+
104+ const dispatched = container . dispatchEvent (
105+ new CustomEvent ( 'quote-selection' , {
106+ bubbles : true ,
107+ cancelable : true ,
108+ detail : { range, selectionText}
109+ } )
110+ )
111+
112+ if ( ! dispatched ) {
113+ return true
114+ }
115+
116+ const field = findTextarea ( container )
117+ if ( ! field ) return false
118+
119+ insertQuote ( selectionText , field )
120+ return true
121+ }
122+
123+ type Quote = {
124+ container : Element ,
125+ selectionText : string
126+ }
127+
128+ function extractQuote ( text : string , range : Range ) : ?Quote {
75129 let selectionText = text . trim ( )
76- if ( ! selectionText ) return false
130+ if ( ! selectionText ) return
77131
78132 let focusNode = range . startContainer
79- if ( ! focusNode ) return false
133+ if ( ! focusNode ) return
80134
81135 if ( focusNode . nodeType !== Node . ELEMENT_NODE ) focusNode = focusNode . parentNode
82- if ( ! ( focusNode instanceof Element ) ) return false
136+ if ( ! ( focusNode instanceof Element ) ) return
83137
84138 const container = findContainer ( focusNode )
85- if ( ! container ) return false
139+ if ( ! container ) return
86140
87141 const markdownSelector = container . getAttribute ( 'data-quote-markdown' )
88142 if ( markdownSelector != null ) {
@@ -97,21 +151,10 @@ export function quote(text: string, range: Range): boolean {
97151 }
98152 }
99153
100- const dispatched = container . dispatchEvent (
101- new CustomEvent ( 'quote-selection' , {
102- bubbles : true ,
103- cancelable : true ,
104- detail : { range, selectionText}
105- } )
106- )
107-
108- if ( ! dispatched ) {
109- return true
110- }
111-
112- const field = findTextarea ( container )
113- if ( ! field ) return false
154+ return { selectionText, container}
155+ }
114156
157+ function insertQuote ( selectionText : string , field : HTMLTextAreaElement ) {
115158 let quotedText = `> ${ selectionText . replace ( / \n / g, '\n> ' ) } \n\n`
116159 if ( field . value ) {
117160 quotedText = `${ field . value } \n\n${ quotedText } `
@@ -120,8 +163,6 @@ export function quote(text: string, range: Range): boolean {
120163 field . focus ( )
121164 field . selectionStart = field . value . length
122165 field . scrollTop = field . scrollHeight
123-
124- return true
125166}
126167
127168function visible ( el : HTMLElement ) : boolean {
0 commit comments