@@ -5,103 +5,105 @@ import {getWindow} from '../misc/getWindow'
5
5
6
6
// Clipboard API is only fully available in secure context or for browser extensions.
7
7
8
- const Window = Symbol ( 'Window reference' )
9
-
10
8
type ItemData = Record < string , Blob | string | Promise < Blob | string > >
11
9
12
- class ClipboardItemStub implements ClipboardItem {
13
- private data : ItemData
14
- constructor ( data : ItemData ) {
15
- this . data = data
16
- }
10
+ // MDN lists string|Blob|Promise<Blob|string> as possible types in ClipboardItemData
11
+ // lib.dom.d.ts lists only Promise<Blob|string>
12
+ // https://developer.mozilla.org/en-US/docs/Web/API/ClipboardItem/ClipboardItem#syntax
13
+ export function createClipboardItem (
14
+ window : Window & typeof globalThis ,
15
+ ...blobs : Array < Blob | string >
16
+ ) {
17
+ const dataMap = Object . fromEntries (
18
+ blobs . map ( b => [
19
+ typeof b === 'string' ? 'text/plain' : b . type ,
20
+ Promise . resolve ( b ) ,
21
+ ] ) ,
22
+ )
17
23
18
- get types ( ) {
19
- return Array . from ( Object . keys ( this . data ) )
24
+ // use real ClipboardItem if available
25
+ /* istanbul ignore if */
26
+ if ( typeof window . ClipboardItem !== 'undefined' ) {
27
+ return new window . ClipboardItem ( dataMap )
20
28
}
21
29
22
- async getType ( type : string ) {
23
- const data = await this . data [ type ]
30
+ return new ( class ClipboardItem implements ClipboardItem {
31
+ private data : ItemData
32
+ constructor ( d : ItemData ) {
33
+ this . data = d
34
+ }
24
35
25
- if ( ! data ) {
26
- throw new Error (
27
- `${ type } is not one of the available MIME types on this item.` ,
28
- )
36
+ get types ( ) {
37
+ return Array . from ( Object . keys ( this . data ) )
29
38
}
30
39
31
- return data instanceof this [ Window ] . Blob
32
- ? data
33
- : new this [ Window ] . Blob ( [ data ] , { type} )
34
- }
40
+ async getType ( type : string ) {
41
+ const value = await this . data [ type ]
35
42
36
- [ Window ] = window
43
+ if ( ! value ) {
44
+ throw new Error (
45
+ `${ type } is not one of the available MIME types on this item.` ,
46
+ )
47
+ }
48
+
49
+ return value instanceof window . Blob
50
+ ? value
51
+ : new window . Blob ( [ value ] , { type} )
52
+ }
53
+ } ) ( dataMap )
37
54
}
38
55
39
56
const ClipboardStubControl = Symbol ( 'Manage ClipboardSub' )
40
57
41
- class ClipboardStub extends window . EventTarget implements Clipboard {
42
- private items : ClipboardItem [ ] = [ ]
58
+ function createClipboardStub ( window : Window & typeof globalThis ) {
59
+ return new ( class Clipboard extends window . EventTarget {
60
+ private items : ClipboardItem [ ] = [ ]
43
61
44
- async read ( ) {
45
- return Array . from ( this . items )
46
- }
62
+ async read ( ) {
63
+ return Array . from ( this . items )
64
+ }
47
65
48
- async readText ( ) {
49
- let text = ''
50
- for ( const item of this . items ) {
51
- const type = item . types . includes ( 'text/plain' )
52
- ? 'text/plain'
53
- : item . types . find ( t => t . startsWith ( 'text/' ) )
54
- if ( type ) {
55
- text += await item
56
- . getType ( type )
57
- . then ( b => readBlobText ( b , this [ Window ] . FileReader ) )
66
+ async readText ( ) {
67
+ let text = ''
68
+ for ( const item of this . items ) {
69
+ const type = item . types . includes ( 'text/plain' )
70
+ ? 'text/plain'
71
+ : item . types . find ( t => t . startsWith ( 'text/' ) )
72
+ if ( type ) {
73
+ text += await item
74
+ . getType ( type )
75
+ . then ( b => readBlobText ( b , window . FileReader ) )
76
+ }
58
77
}
78
+ return text
59
79
}
60
- return text
61
- }
62
80
63
- async write ( data : ClipboardItem [ ] ) {
64
- this . items = data
65
- }
81
+ async write ( data : ClipboardItem [ ] ) {
82
+ this . items = data
83
+ }
66
84
67
- async writeText ( text : string ) {
68
- this . items = [ createClipboardItem ( this [ Window ] , text ) ]
69
- }
85
+ async writeText ( text : string ) {
86
+ this . items = [ createClipboardItem ( window , text ) ]
87
+ }
70
88
71
- [ Window ] = window ;
72
- [ ClipboardStubControl ] : {
73
- resetClipboardStub : ( ) => void
74
- detachClipboardStub : ( ) => void
75
- }
89
+ [ ClipboardStubControl ] : {
90
+ resetClipboardStub : ( ) => void
91
+ detachClipboardStub : ( ) => void
92
+ }
93
+ } ) ( )
76
94
}
77
-
78
- // MDN lists string|Blob|Promise<Blob|string> as possible types in ClipboardItemData
79
- // lib.dom.d.ts lists only Promise<Blob|string>
80
- // https://developer.mozilla.org/en-US/docs/Web/API/ClipboardItem/ClipboardItem#syntax
81
- export function createClipboardItem (
82
- window : Window & typeof globalThis ,
83
- ...blobs : Array < Blob | string >
84
- ) : ClipboardItem {
85
- const data = Object . fromEntries (
86
- blobs . map ( b => [
87
- typeof b === 'string' ? 'text/plain' : b . type ,
88
- Promise . resolve ( b ) ,
89
- ] ) ,
90
- )
91
-
92
- // use real ClipboardItem if available
93
- /* istanbul ignore else */
94
- if ( typeof window . ClipboardItem === 'undefined' ) {
95
- const item = new ClipboardItemStub ( data )
96
- item [ Window ] = window
97
- return item
98
- } else {
99
- return new window . ClipboardItem ( data )
100
- }
95
+ type ClipboardStub = ReturnType < typeof createClipboardStub >
96
+
97
+ function isClipboardStub (
98
+ clipboard : Clipboard | ClipboardStub | undefined ,
99
+ ) : clipboard is ClipboardStub {
100
+ return ! ! ( clipboard as { [ ClipboardStubControl ] ?: object } | undefined ) ?. [
101
+ ClipboardStubControl
102
+ ]
101
103
}
102
104
103
105
export function attachClipboardStubToView ( window : Window & typeof globalThis ) {
104
- if ( window . navigator . clipboard instanceof ClipboardStub ) {
106
+ if ( isClipboardStub ( window . navigator . clipboard ) ) {
105
107
return window . navigator . clipboard [ ClipboardStubControl ]
106
108
}
107
109
@@ -110,12 +112,10 @@ export function attachClipboardStubToView(window: Window & typeof globalThis) {
110
112
'clipboard' ,
111
113
)
112
114
113
- let stub = new ClipboardStub ( )
114
- stub [ Window ] = window
115
+ let stub = createClipboardStub ( window )
115
116
const control = {
116
117
resetClipboardStub : ( ) => {
117
- stub = new ClipboardStub ( )
118
- stub [ Window ] = window
118
+ stub = createClipboardStub ( window )
119
119
stub [ ClipboardStubControl ] = control
120
120
} ,
121
121
detachClipboardStub : ( ) => {
@@ -141,15 +141,15 @@ export function attachClipboardStubToView(window: Window & typeof globalThis) {
141
141
}
142
142
143
143
export function resetClipboardStubOnView ( window : Window & typeof globalThis ) {
144
- if ( window . navigator . clipboard instanceof ClipboardStub ) {
144
+ if ( isClipboardStub ( window . navigator . clipboard ) ) {
145
145
window . navigator . clipboard [ ClipboardStubControl ] . resetClipboardStub ( )
146
146
}
147
147
}
148
148
149
149
export function detachClipboardStubFromView (
150
150
window : Window & typeof globalThis ,
151
151
) {
152
- if ( window . navigator . clipboard instanceof ClipboardStub ) {
152
+ if ( isClipboardStub ( window . navigator . clipboard ) ) {
153
153
window . navigator . clipboard [ ClipboardStubControl ] . detachClipboardStub ( )
154
154
}
155
155
}
@@ -204,11 +204,11 @@ export async function writeDataTransferToClipboard(
204
204
}
205
205
206
206
/* istanbul ignore else */
207
- if ( typeof afterEach === 'function' ) {
208
- afterEach ( ( ) => resetClipboardStubOnView ( window ) )
207
+ if ( typeof globalThis . afterEach === 'function' ) {
208
+ globalThis . afterEach ( ( ) => resetClipboardStubOnView ( globalThis . window ) )
209
209
}
210
210
211
211
/* istanbul ignore else */
212
- if ( typeof afterAll === 'function' ) {
213
- afterAll ( ( ) => detachClipboardStubFromView ( window ) )
212
+ if ( typeof globalThis . afterAll === 'function' ) {
213
+ globalThis . afterAll ( ( ) => detachClipboardStubFromView ( globalThis . window ) )
214
214
}
0 commit comments