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