openrat-cms

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

input.js (5379B)


      1 import { runInOp } from "../display/operations.js"
      2 import { ensureCursorVisible } from "../display/scrolling.js"
      3 import { Pos } from "../line/pos.js"
      4 import { getLine } from "../line/utils_line.js"
      5 import { makeChange } from "../model/changes.js"
      6 import { ios, webkit } from "../util/browser.js"
      7 import { elt } from "../util/dom.js"
      8 import { lst, map } from "../util/misc.js"
      9 import { signalLater } from "../util/operation_group.js"
     10 import { splitLinesAuto } from "../util/feature_detection.js"
     11 
     12 import { indentLine } from "./indent.js"
     13 
     14 // This will be set to a {lineWise: bool, text: [string]} object, so
     15 // that, when pasting, we know what kind of selections the copied
     16 // text was made out of.
     17 export let lastCopied = null
     18 
     19 export function setLastCopied(newLastCopied) {
     20   lastCopied = newLastCopied
     21 }
     22 
     23 export function applyTextInput(cm, inserted, deleted, sel, origin) {
     24   let doc = cm.doc
     25   cm.display.shift = false
     26   if (!sel) sel = doc.sel
     27 
     28   let paste = cm.state.pasteIncoming || origin == "paste"
     29   let textLines = splitLinesAuto(inserted), multiPaste = null
     30   // When pasing N lines into N selections, insert one line per selection
     31   if (paste && sel.ranges.length > 1) {
     32     if (lastCopied && lastCopied.text.join("\n") == inserted) {
     33       if (sel.ranges.length % lastCopied.text.length == 0) {
     34         multiPaste = []
     35         for (let i = 0; i < lastCopied.text.length; i++)
     36           multiPaste.push(doc.splitLines(lastCopied.text[i]))
     37       }
     38     } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) {
     39       multiPaste = map(textLines, l => [l])
     40     }
     41   }
     42 
     43   let updateInput
     44   // Normal behavior is to insert the new text into every selection
     45   for (let i = sel.ranges.length - 1; i >= 0; i--) {
     46     let range = sel.ranges[i]
     47     let from = range.from(), to = range.to()
     48     if (range.empty()) {
     49       if (deleted && deleted > 0) // Handle deletion
     50         from = Pos(from.line, from.ch - deleted)
     51       else if (cm.state.overwrite && !paste) // Handle overwrite
     52         to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length))
     53       else if (lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted)
     54         from = to = Pos(from.line, 0)
     55     }
     56     updateInput = cm.curOp.updateInput
     57     let changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % multiPaste.length] : textLines,
     58                        origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")}
     59     makeChange(cm.doc, changeEvent)
     60     signalLater(cm, "inputRead", cm, changeEvent)
     61   }
     62   if (inserted && !paste)
     63     triggerElectric(cm, inserted)
     64 
     65   ensureCursorVisible(cm)
     66   cm.curOp.updateInput = updateInput
     67   cm.curOp.typing = true
     68   cm.state.pasteIncoming = cm.state.cutIncoming = false
     69 }
     70 
     71 export function handlePaste(e, cm) {
     72   let pasted = e.clipboardData && e.clipboardData.getData("Text")
     73   if (pasted) {
     74     e.preventDefault()
     75     if (!cm.isReadOnly() && !cm.options.disableInput)
     76       runInOp(cm, () => applyTextInput(cm, pasted, 0, null, "paste"))
     77     return true
     78   }
     79 }
     80 
     81 export function triggerElectric(cm, inserted) {
     82   // When an 'electric' character is inserted, immediately trigger a reindent
     83   if (!cm.options.electricChars || !cm.options.smartIndent) return
     84   let sel = cm.doc.sel
     85 
     86   for (let i = sel.ranges.length - 1; i >= 0; i--) {
     87     let range = sel.ranges[i]
     88     if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) continue
     89     let mode = cm.getModeAt(range.head)
     90     let indented = false
     91     if (mode.electricChars) {
     92       for (let j = 0; j < mode.electricChars.length; j++)
     93         if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
     94           indented = indentLine(cm, range.head.line, "smart")
     95           break
     96         }
     97     } else if (mode.electricInput) {
     98       if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch)))
     99         indented = indentLine(cm, range.head.line, "smart")
    100     }
    101     if (indented) signalLater(cm, "electricInput", cm, range.head.line)
    102   }
    103 }
    104 
    105 export function copyableRanges(cm) {
    106   let text = [], ranges = []
    107   for (let i = 0; i < cm.doc.sel.ranges.length; i++) {
    108     let line = cm.doc.sel.ranges[i].head.line
    109     let lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)}
    110     ranges.push(lineRange)
    111     text.push(cm.getRange(lineRange.anchor, lineRange.head))
    112   }
    113   return {text: text, ranges: ranges}
    114 }
    115 
    116 export function disableBrowserMagic(field, spellcheck) {
    117   field.setAttribute("autocorrect", "off")
    118   field.setAttribute("autocapitalize", "off")
    119   field.setAttribute("spellcheck", !!spellcheck)
    120 }
    121 
    122 export function hiddenTextarea() {
    123   let te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none")
    124   let div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;")
    125   // The textarea is kept positioned near the cursor to prevent the
    126   // fact that it'll be scrolled into view on input from scrolling
    127   // our fake cursor out of view. On webkit, when wrap=off, paste is
    128   // very slow. So make the area wide instead.
    129   if (webkit) te.style.width = "1000px"
    130   else te.setAttribute("wrap", "off")
    131   // If border: 0; -- iOS fails to open keyboard (issue #1287)
    132   if (ios) te.style.border = "1px solid black"
    133   disableBrowserMagic(te)
    134   return div
    135 }