diff .cms/lib/codemirror/src/edit/drop_events.js @ 0:78edf6b517a0 draft

24.10
author Coffee CMS <info@coffee-cms.ru>
date Fri, 11 Oct 2024 22:40:23 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.cms/lib/codemirror/src/edit/drop_events.js	Fri Oct 11 22:40:23 2024 +0000
@@ -0,0 +1,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
+  }
+}