Skip to content

Commit

Permalink
fix: remove implicit global references (#932)
Browse files Browse the repository at this point in the history
  • Loading branch information
ph-fritsche authored Apr 17, 2022
1 parent 31a808b commit 9913798
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 90 deletions.
15 changes: 11 additions & 4 deletions src/setup/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export function createConfig(
defaults: Required<Options> = defaultOptionsSetup,
node?: Node,
): Config {
const document = getDocument(options, node)
const document = getDocument(options, node, defaults)

const {
keyboardState = createKeyboardState(),
Expand All @@ -43,7 +43,8 @@ export function setupMain(options: Options = {}) {
const config = createConfig(options)
prepareDocument(config.document)

const view = config.document.defaultView ?? /* istanbul ignore next */ window
const view =
config.document.defaultView ?? /* istanbul ignore next */ globalThis.window
attachClipboardStubToView(view)

return doSetup(config)
Expand Down Expand Up @@ -103,6 +104,12 @@ function doSetup(config: Config): UserEvent {
}
}

function getDocument(options: Partial<Config>, node?: Node) {
return options.document ?? (node && getDocumentFromNode(node)) ?? document
function getDocument(
options: Partial<Config>,
node: Node | undefined,
defaults: Required<Options>,
) {
return (
options.document ?? (node && getDocumentFromNode(node)) ?? defaults.document
)
}
166 changes: 83 additions & 83 deletions src/utils/dataTransfer/Clipboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,103 +5,105 @@ import {getWindow} from '../misc/getWindow'

// Clipboard API is only fully available in secure context or for browser extensions.

const Window = Symbol('Window reference')

type ItemData = Record<string, Blob | string | Promise<Blob | string>>

class ClipboardItemStub implements ClipboardItem {
private data: ItemData
constructor(data: ItemData) {
this.data = data
}
// MDN lists string|Blob|Promise<Blob|string> as possible types in ClipboardItemData
// lib.dom.d.ts lists only Promise<Blob|string>
// https://developer.mozilla.org/en-US/docs/Web/API/ClipboardItem/ClipboardItem#syntax
export function createClipboardItem(
window: Window & typeof globalThis,
...blobs: Array<Blob | string>
) {
const dataMap = Object.fromEntries(
blobs.map(b => [
typeof b === 'string' ? 'text/plain' : b.type,
Promise.resolve(b),
]),
)

get types() {
return Array.from(Object.keys(this.data))
// use real ClipboardItem if available
/* istanbul ignore if */
if (typeof window.ClipboardItem !== 'undefined') {
return new window.ClipboardItem(dataMap)
}

async getType(type: string) {
const data = await this.data[type]
return new (class ClipboardItem implements ClipboardItem {
private data: ItemData
constructor(d: ItemData) {
this.data = d
}

if (!data) {
throw new Error(
`${type} is not one of the available MIME types on this item.`,
)
get types() {
return Array.from(Object.keys(this.data))
}

return data instanceof this[Window].Blob
? data
: new this[Window].Blob([data], {type})
}
async getType(type: string) {
const value = await this.data[type]

[Window] = window
if (!value) {
throw new Error(
`${type} is not one of the available MIME types on this item.`,
)
}

return value instanceof window.Blob
? value
: new window.Blob([value], {type})
}
})(dataMap)
}

const ClipboardStubControl = Symbol('Manage ClipboardSub')

class ClipboardStub extends window.EventTarget implements Clipboard {
private items: ClipboardItem[] = []
function createClipboardStub(window: Window & typeof globalThis) {
return new (class Clipboard extends window.EventTarget {
private items: ClipboardItem[] = []

async read() {
return Array.from(this.items)
}
async read() {
return Array.from(this.items)
}

async readText() {
let text = ''
for (const item of this.items) {
const type = item.types.includes('text/plain')
? 'text/plain'
: item.types.find(t => t.startsWith('text/'))
if (type) {
text += await item
.getType(type)
.then(b => readBlobText(b, this[Window].FileReader))
async readText() {
let text = ''
for (const item of this.items) {
const type = item.types.includes('text/plain')
? 'text/plain'
: item.types.find(t => t.startsWith('text/'))
if (type) {
text += await item
.getType(type)
.then(b => readBlobText(b, window.FileReader))
}
}
return text
}
return text
}

async write(data: ClipboardItem[]) {
this.items = data
}
async write(data: ClipboardItem[]) {
this.items = data
}

async writeText(text: string) {
this.items = [createClipboardItem(this[Window], text)]
}
async writeText(text: string) {
this.items = [createClipboardItem(window, text)]
}

[Window] = window;
[ClipboardStubControl]: {
resetClipboardStub: () => void
detachClipboardStub: () => void
}
[ClipboardStubControl]: {
resetClipboardStub: () => void
detachClipboardStub: () => void
}
})()
}

// MDN lists string|Blob|Promise<Blob|string> as possible types in ClipboardItemData
// lib.dom.d.ts lists only Promise<Blob|string>
// https://developer.mozilla.org/en-US/docs/Web/API/ClipboardItem/ClipboardItem#syntax
export function createClipboardItem(
window: Window & typeof globalThis,
...blobs: Array<Blob | string>
): ClipboardItem {
const data = Object.fromEntries(
blobs.map(b => [
typeof b === 'string' ? 'text/plain' : b.type,
Promise.resolve(b),
]),
)

// use real ClipboardItem if available
/* istanbul ignore else */
if (typeof window.ClipboardItem === 'undefined') {
const item = new ClipboardItemStub(data)
item[Window] = window
return item
} else {
return new window.ClipboardItem(data)
}
type ClipboardStub = ReturnType<typeof createClipboardStub>

function isClipboardStub(
clipboard: Clipboard | ClipboardStub | undefined,
): clipboard is ClipboardStub {
return !!(clipboard as {[ClipboardStubControl]?: object} | undefined)?.[
ClipboardStubControl
]
}

export function attachClipboardStubToView(window: Window & typeof globalThis) {
if (window.navigator.clipboard instanceof ClipboardStub) {
if (isClipboardStub(window.navigator.clipboard)) {
return window.navigator.clipboard[ClipboardStubControl]
}

Expand All @@ -110,12 +112,10 @@ export function attachClipboardStubToView(window: Window & typeof globalThis) {
'clipboard',
)

let stub = new ClipboardStub()
stub[Window] = window
let stub = createClipboardStub(window)
const control = {
resetClipboardStub: () => {
stub = new ClipboardStub()
stub[Window] = window
stub = createClipboardStub(window)
stub[ClipboardStubControl] = control
},
detachClipboardStub: () => {
Expand All @@ -141,15 +141,15 @@ export function attachClipboardStubToView(window: Window & typeof globalThis) {
}

export function resetClipboardStubOnView(window: Window & typeof globalThis) {
if (window.navigator.clipboard instanceof ClipboardStub) {
if (isClipboardStub(window.navigator.clipboard)) {
window.navigator.clipboard[ClipboardStubControl].resetClipboardStub()
}
}

export function detachClipboardStubFromView(
window: Window & typeof globalThis,
) {
if (window.navigator.clipboard instanceof ClipboardStub) {
if (isClipboardStub(window.navigator.clipboard)) {
window.navigator.clipboard[ClipboardStubControl].detachClipboardStub()
}
}
Expand Down Expand Up @@ -204,11 +204,11 @@ export async function writeDataTransferToClipboard(
}

/* istanbul ignore else */
if (typeof afterEach === 'function') {
afterEach(() => resetClipboardStubOnView(window))
if (typeof globalThis.afterEach === 'function') {
globalThis.afterEach(() => resetClipboardStubOnView(globalThis.window))
}

/* istanbul ignore else */
if (typeof afterAll === 'function') {
afterAll(() => detachClipboardStubFromView(window))
if (typeof globalThis.afterAll === 'function') {
globalThis.afterAll(() => detachClipboardStubFromView(globalThis.window))
}
6 changes: 4 additions & 2 deletions src/utils/edit/buildTimeValue.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const parseInt = globalThis.parseInt

export function buildTimeValue(value: string): string {
const onlyDigitsValue = value.replace(/\D/g, '')
if (onlyDigitsValue.length < 2) {
Expand Down Expand Up @@ -26,7 +28,7 @@ function build(onlyDigitsValue: string, index: number): string {
const minuteCharacters = onlyDigitsValue.slice(index)
const parsedMinutes = parseInt(minuteCharacters, 10)
const validMinutes = Math.min(parsedMinutes, 59)
return `${validHours
return `${validHours.toString().padStart(2, '0')}:${validMinutes
.toString()
.padStart(2, '0')}:${validMinutes.toString().padStart(2, '0')}`
.padStart(2, '0')}`
}
2 changes: 1 addition & 1 deletion src/utils/misc/wait.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export function wait(config: Config) {
return
}
return Promise.all([
new Promise<void>(resolve => setTimeout(() => resolve(), delay)),
new Promise<void>(resolve => globalThis.setTimeout(() => resolve(), delay)),
config.advanceTimers(delay),
])
}

0 comments on commit 9913798

Please sign in to comment.