diff --git a/examples/index.html b/examples/index.html
index 63c730e..f1f153e 100644
--- a/examples/index.html
+++ b/examples/index.html
@@ -1,61 +1,72 @@
-
+
-
-
- paste-markdown demo
-
-
-
- Test by selecting the elements and then either copy/paste or drag them into the textarea.
-
-
-
- | name |
- origin |
-
-
-
-
- | hubot |
- github |
-
-
- | bender |
- futurama |
-
-
-
+
+
+ paste-markdown demo
+
+
+
+ Test by selecting the elements and then either copy/paste or drag them into the textarea.
+
+
+
+ | name |
+ origin |
+
+
+
+
+ | hubot |
+ github |
+
+
+ | bender |
+ futurama |
+
+
+
-
-
-
- | name |
- origin |
-
-
-
-
- | this table will not be |
- converted to markdown |
-
-
-
+
+
+
+ | name |
+ origin |
+
+
+
+
+ | this table will not be |
+ converted to markdown |
+
+
+
-
+
- Test by copying this page's URL and then selecting here in the textarea and pasting the URL.
+ Test by copying this page's URL and then selecting here in the textarea and pasting the URL.
- Or copy and paste a link and another link and maybe one more into the textarea.
+
+ Or copy and paste a link and
+ another link and maybe
+ one more into the textarea.
+
-
+
-
-
+
+
diff --git a/src/index.ts b/src/index.ts
index c3c37fa..2f84aee 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,6 +1,10 @@
import {install as installHTML, uninstall as uninstallHTML} from './paste-markdown-html'
import {install as installImageLink, uninstall as uninstallImageLink} from './paste-markdown-image-link'
import {install as installLink, uninstall as uninstallLink} from './paste-markdown-link'
+import {
+ installAround as installSkipFormatting,
+ uninstall as uninstallSkipFormatting
+} from './paste-keyboard-shortcut-helper'
import {install as installTable, uninstall as uninstallTable} from './paste-markdown-table'
import {install as installText, uninstall as uninstallText} from './paste-markdown-text'
@@ -9,14 +13,11 @@ interface Subscription {
}
function subscribe(el: HTMLElement): Subscription {
- installTable(el)
- installImageLink(el)
- installLink(el)
- installText(el)
- installHTML(el)
+ installSkipFormatting(el, installTable, installImageLink, installLink, installText, installHTML)
return {
unsubscribe: () => {
+ uninstallSkipFormatting(el)
uninstallTable(el)
uninstallHTML(el)
uninstallImageLink(el)
diff --git a/src/paste-keyboard-shortcut-helper.ts b/src/paste-keyboard-shortcut-helper.ts
new file mode 100644
index 0000000..f2a5261
--- /dev/null
+++ b/src/paste-keyboard-shortcut-helper.ts
@@ -0,0 +1,37 @@
+const skipFormattingMap = new WeakMap()
+
+function setSkipFormattingFlag(event: KeyboardEvent): void {
+ const {currentTarget: el} = event
+ const isSkipFormattingKeys = event.code === 'KeyV' && (event.ctrlKey || event.metaKey) && event.shiftKey
+
+ // Supports Cmd+Shift+V (Chrome) / Cmd+Shift+Opt+V (Safari, Firefox and Edge) to mimic paste and match style shortcut on MacOS.
+ if (isSkipFormattingKeys || (isSkipFormattingKeys && event.altKey)) {
+ skipFormattingMap.set(el as HTMLElement, true)
+ }
+}
+
+function unsetSkipFormattedFlag(event: ClipboardEvent): void {
+ const {currentTarget: el} = event
+ skipFormattingMap.delete(el as HTMLElement)
+}
+
+export function shouldSkipFormatting(el: HTMLElement): boolean {
+ const shouldSkipFormattingState = skipFormattingMap.get(el) ?? false
+
+ return shouldSkipFormattingState
+}
+
+export function installAround(el: HTMLElement, ...installCallbacks: Array<(el: HTMLElement) => void>): void {
+ el.addEventListener('keydown', setSkipFormattingFlag)
+
+ for (const installCallback of installCallbacks) {
+ installCallback(el)
+ }
+
+ el.addEventListener('paste', unsetSkipFormattedFlag)
+}
+
+export function uninstall(el: HTMLElement): void {
+ el.removeEventListener('keydown', setSkipFormattingFlag)
+ el.removeEventListener('paste', unsetSkipFormattedFlag)
+}
diff --git a/src/paste-markdown-html.ts b/src/paste-markdown-html.ts
index 3283f84..8b1ae17 100644
--- a/src/paste-markdown-html.ts
+++ b/src/paste-markdown-html.ts
@@ -1,4 +1,5 @@
import {insertText} from './text'
+import {shouldSkipFormatting} from './paste-keyboard-shortcut-helper'
export function install(el: HTMLElement): void {
el.addEventListener('paste', onPaste)
@@ -10,6 +11,8 @@ export function uninstall(el: HTMLElement): void {
function onPaste(event: ClipboardEvent) {
const transfer = event.clipboardData
+ const {currentTarget: el} = event
+ if (shouldSkipFormatting(el as HTMLElement)) return
// if there is no clipboard data, or
// if there is no html content in the clipboard, return
if (!transfer || !hasHTML(transfer)) return
diff --git a/src/paste-markdown-image-link.ts b/src/paste-markdown-image-link.ts
index defdcb2..b3db644 100644
--- a/src/paste-markdown-image-link.ts
+++ b/src/paste-markdown-image-link.ts
@@ -1,6 +1,6 @@
/* @flow strict */
-
import {insertText} from './text'
+import {shouldSkipFormatting} from './paste-keyboard-shortcut-helper'
export function install(el: HTMLElement): void {
el.addEventListener('dragover', onDragover)
@@ -38,6 +38,9 @@ function onDragover(event: DragEvent) {
}
function onPaste(event: ClipboardEvent) {
+ const {currentTarget: el} = event
+ if (shouldSkipFormatting(el as HTMLElement)) return
+
const transfer = event.clipboardData
if (!transfer || !hasLink(transfer)) return
diff --git a/src/paste-markdown-link.ts b/src/paste-markdown-link.ts
index 1bbf17c..1c9674d 100644
--- a/src/paste-markdown-link.ts
+++ b/src/paste-markdown-link.ts
@@ -1,4 +1,5 @@
import {insertText} from './text'
+import {shouldSkipFormatting} from './paste-keyboard-shortcut-helper'
export function install(el: HTMLElement): void {
el.addEventListener('paste', onPaste)
@@ -9,6 +10,9 @@ export function uninstall(el: HTMLElement): void {
}
function onPaste(event: ClipboardEvent) {
+ const {currentTarget: el} = event
+ if (shouldSkipFormatting(el as HTMLElement)) return
+
const transfer = event.clipboardData
if (!transfer || !hasPlainText(transfer)) return
diff --git a/src/paste-markdown-table.ts b/src/paste-markdown-table.ts
index e2784b7..afa5294 100644
--- a/src/paste-markdown-table.ts
+++ b/src/paste-markdown-table.ts
@@ -1,4 +1,5 @@
import {insertText} from './text'
+import {shouldSkipFormatting} from './paste-keyboard-shortcut-helper'
export function install(el: HTMLElement): void {
el.addEventListener('dragover', onDragover)
@@ -35,6 +36,9 @@ function onDragover(event: DragEvent) {
}
function onPaste(event: ClipboardEvent) {
+ const {currentTarget: el} = event
+ if (shouldSkipFormatting(el as HTMLElement)) return
+
if (!event.clipboardData) return
const textToPaste = generateText(event.clipboardData)
diff --git a/src/paste-markdown-text.ts b/src/paste-markdown-text.ts
index b02fa6d..f041402 100644
--- a/src/paste-markdown-text.ts
+++ b/src/paste-markdown-text.ts
@@ -1,4 +1,5 @@
import {insertText} from './text'
+import {shouldSkipFormatting} from './paste-keyboard-shortcut-helper'
export function install(el: HTMLElement): void {
el.addEventListener('paste', onPaste)
@@ -9,6 +10,9 @@ export function uninstall(el: HTMLElement): void {
}
function onPaste(event: ClipboardEvent) {
+ const {currentTarget: el} = event
+ if (shouldSkipFormatting(el as HTMLElement)) return
+
const transfer = event.clipboardData
if (!transfer || !hasMarkdown(transfer)) return
diff --git a/test/test.js b/test/test.js
index 526f88a..8020f6e 100644
--- a/test/test.js
+++ b/test/test.js
@@ -1,5 +1,17 @@
import {subscribe} from '../dist/index.esm.js'
+const tableHtml = `
+
+ | name | origin |
+
+ | hubot | github |
+ | bender | futurama |
+
+
+`
+
+const tableMarkdown = 'name | origin\n-- | --\nhubot | github\nbender | futurama'
+
describe('paste-markdown', function () {
describe('installed on textarea', function () {
let subscription
@@ -43,18 +55,10 @@ describe('paste-markdown', function () {
it('turns html tables into markdown', function () {
const data = {
- 'text/html': `
-
- | name | origin |
-
- | hubot | github |
- | bender | futurama |
-
-
- `
+ 'text/html': tableHtml
}
paste(textarea, data)
- assert.include(textarea.value, 'name | origin\n-- | --\nhubot | github\nbender | futurama')
+ assert.include(textarea.value, tableMarkdown)
})
it("doesn't execute JavaScript", async function () {
@@ -76,13 +80,7 @@ describe('paste-markdown', function () {
const data = {
'text/html': `
Here is a cool table
-
- | name | origin |
-
- | hubot | github |
- | bender | futurama |
-
-
+ ${tableHtml}
Very cool
`
}
@@ -91,7 +89,7 @@ describe('paste-markdown', function () {
assert.equal(
textarea.value.trim(),
// eslint-disable-next-line github/unescaped-html-literal
- 'Here is a cool table
\n \n\nname | origin\n-- | --\nhubot | github\nbender | futurama\n\n\n Very cool
'
+ `Here is a cool table
\n \n \n\n${tableMarkdown}\n\n\n\n Very cool
`
)
})
@@ -111,7 +109,7 @@ describe('paste-markdown', function () {
// Synthetic paste events don't manipulate the DOM. A empty textarea
// means that the event handler didn't fire and normal paste happened.
- assert.equal(textarea.value, '')
+ assertUnformattedPaste(textarea)
})
it('accepts x-gfm', function () {
@@ -213,9 +211,45 @@ describe('paste-markdown', function () {
paste(textarea, {'text/html': sentence, 'text/plain': plaintextSentence})
assert.equal(textarea.value, markdownSentence)
})
+
+ it('skip markdown formatting with (Ctrl+Shift+v)', function () {
+ const data = {
+ 'text/html': tableHtml
+ }
+
+ dispatchSkipFormattingKeyEvent(textarea)
+ paste(textarea, data)
+ assertUnformattedPaste(textarea)
+
+ textarea.value = ''
+ paste(textarea, data)
+ assert.include(textarea.value, tableMarkdown)
+ })
})
})
+/**
+ * Note: It's possible to construct and dispatch a synthetic paste event,
+ * but this will not affect the document's contents in tests to assert it.
+ * https://developer.mozilla.org/en-US/docs/Web/API/Element/paste_event
+ * So for that reason assert result on keydown (Ctrl+Shift+v) will be empty '' here.
+ */
+function assertUnformattedPaste(textarea) {
+ return assert.equal(textarea.value, '')
+}
+
+function dispatchSkipFormattingKeyEvent(textarea) {
+ textarea.dispatchEvent(
+ new KeyboardEvent('keydown', {
+ key: 'v',
+ code: 'KeyV',
+ shiftKey: true,
+ ctrlKey: true,
+ metaKey: true
+ })
+ )
+}
+
function paste(textarea, data) {
const dataTransfer = new DataTransfer()
for (const key in data) {