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