drop_events.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. import { drawSelectionCursor } from "../display/selection.js"
  2. import { operation } from "../display/operations.js"
  3. import { clipPos } from "../line/pos.js"
  4. import { posFromMouse } from "../measurement/position_measurement.js"
  5. import { eventInWidget } from "../measurement/widgets.js"
  6. import { makeChange, replaceRange } from "../model/changes.js"
  7. import { changeEnd } from "../model/change_measurement.js"
  8. import { simpleSelection } from "../model/selection.js"
  9. import { setSelectionNoUndo, setSelectionReplaceHistory } from "../model/selection_updates.js"
  10. import { ie, presto, safari } from "../util/browser.js"
  11. import { elt, removeChildrenAndAdd } from "../util/dom.js"
  12. import { e_preventDefault, e_stop, signalDOMEvent } from "../util/event.js"
  13. import { indexOf } from "../util/misc.js"
  14. // Kludge to work around strange IE behavior where it'll sometimes
  15. // re-fire a series of drag-related events right after the drop (#1551)
  16. let lastDrop = 0
  17. export function onDrop(e) {
  18. let cm = this
  19. clearDragCursor(cm)
  20. if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
  21. return
  22. e_preventDefault(e)
  23. if (ie) lastDrop = +new Date
  24. let pos = posFromMouse(cm, e, true), files = e.dataTransfer.files
  25. if (!pos || cm.isReadOnly()) return
  26. // Might be a file drop, in which case we simply extract the text
  27. // and insert it.
  28. if (files && files.length && window.FileReader && window.File) {
  29. let n = files.length, text = Array(n), read = 0
  30. let loadFile = (file, i) => {
  31. if (cm.options.allowDropFileTypes &&
  32. indexOf(cm.options.allowDropFileTypes, file.type) == -1)
  33. return
  34. let reader = new FileReader
  35. reader.onload = operation(cm, () => {
  36. let content = reader.result
  37. if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) content = ""
  38. text[i] = content
  39. if (++read == n) {
  40. pos = clipPos(cm.doc, pos)
  41. let change = {from: pos, to: pos,
  42. text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())),
  43. origin: "paste"}
  44. makeChange(cm.doc, change)
  45. setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)))
  46. }
  47. })
  48. reader.readAsText(file)
  49. }
  50. for (let i = 0; i < n; ++i) loadFile(files[i], i)
  51. } else { // Normal drop
  52. // Don't do a replace if the drop happened inside of the selected text.
  53. if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
  54. cm.state.draggingText(e)
  55. // Ensure the editor is re-focused
  56. setTimeout(() => cm.display.input.focus(), 20)
  57. return
  58. }
  59. try {
  60. let text = e.dataTransfer.getData("Text")
  61. if (text) {
  62. let selected
  63. if (cm.state.draggingText && !cm.state.draggingText.copy)
  64. selected = cm.listSelections()
  65. setSelectionNoUndo(cm.doc, simpleSelection(pos, pos))
  66. if (selected) for (let i = 0; i < selected.length; ++i)
  67. replaceRange(cm.doc, "", selected[i].anchor, selected[i].head, "drag")
  68. cm.replaceSelection(text, "around", "paste")
  69. cm.display.input.focus()
  70. }
  71. }
  72. catch(e){}
  73. }
  74. }
  75. export function onDragStart(cm, e) {
  76. if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return }
  77. if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return
  78. e.dataTransfer.setData("Text", cm.getSelection())
  79. e.dataTransfer.effectAllowed = "copyMove"
  80. // Use dummy image instead of default browsers image.
  81. // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
  82. if (e.dataTransfer.setDragImage && !safari) {
  83. let img = elt("img", null, null, "position: fixed; left: 0; top: 0;")
  84. img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
  85. if (presto) {
  86. img.width = img.height = 1
  87. cm.display.wrapper.appendChild(img)
  88. // Force a relayout, or Opera won't use our image for some obscure reason
  89. img._top = img.offsetTop
  90. }
  91. e.dataTransfer.setDragImage(img, 0, 0)
  92. if (presto) img.parentNode.removeChild(img)
  93. }
  94. }
  95. export function onDragOver(cm, e) {
  96. let pos = posFromMouse(cm, e)
  97. if (!pos) return
  98. let frag = document.createDocumentFragment()
  99. drawSelectionCursor(cm, pos, frag)
  100. if (!cm.display.dragCursor) {
  101. cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors")
  102. cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv)
  103. }
  104. removeChildrenAndAdd(cm.display.dragCursor, frag)
  105. }
  106. export function clearDragCursor(cm) {
  107. if (cm.display.dragCursor) {
  108. cm.display.lineSpace.removeChild(cm.display.dragCursor)
  109. cm.display.dragCursor = null
  110. }
  111. }