openrat-cms

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

keymap.js (6389B)


      1 import { flipCtrlCmd, mac, presto } from "../util/browser.js"
      2 import { map } from "../util/misc.js"
      3 
      4 import { keyNames } from "./keynames.js"
      5 
      6 export let keyMap = {}
      7 
      8 keyMap.basic = {
      9   "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
     10   "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
     11   "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
     12   "Tab": "defaultTab", "Shift-Tab": "indentAuto",
     13   "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
     14   "Esc": "singleSelection"
     15 }
     16 // Note that the save and find-related commands aren't defined by
     17 // default. User code or addons can define them. Unknown commands
     18 // are simply ignored.
     19 keyMap.pcDefault = {
     20   "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
     21   "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
     22   "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
     23   "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
     24   "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
     25   "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
     26   "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
     27   fallthrough: "basic"
     28 }
     29 // Very basic readline/emacs-style bindings, which are standard on Mac.
     30 keyMap.emacsy = {
     31   "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
     32   "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
     33   "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
     34   "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars",
     35   "Ctrl-O": "openLine"
     36 }
     37 keyMap.macDefault = {
     38   "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
     39   "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
     40   "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
     41   "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
     42   "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
     43   "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
     44   "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
     45   fallthrough: ["basic", "emacsy"]
     46 }
     47 keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault
     48 
     49 // KEYMAP DISPATCH
     50 
     51 function normalizeKeyName(name) {
     52   let parts = name.split(/-(?!$)/)
     53   name = parts[parts.length - 1]
     54   let alt, ctrl, shift, cmd
     55   for (let i = 0; i < parts.length - 1; i++) {
     56     let mod = parts[i]
     57     if (/^(cmd|meta|m)$/i.test(mod)) cmd = true
     58     else if (/^a(lt)?$/i.test(mod)) alt = true
     59     else if (/^(c|ctrl|control)$/i.test(mod)) ctrl = true
     60     else if (/^s(hift)?$/i.test(mod)) shift = true
     61     else throw new Error("Unrecognized modifier name: " + mod)
     62   }
     63   if (alt) name = "Alt-" + name
     64   if (ctrl) name = "Ctrl-" + name
     65   if (cmd) name = "Cmd-" + name
     66   if (shift) name = "Shift-" + name
     67   return name
     68 }
     69 
     70 // This is a kludge to keep keymaps mostly working as raw objects
     71 // (backwards compatibility) while at the same time support features
     72 // like normalization and multi-stroke key bindings. It compiles a
     73 // new normalized keymap, and then updates the old object to reflect
     74 // this.
     75 export function normalizeKeyMap(keymap) {
     76   let copy = {}
     77   for (let keyname in keymap) if (keymap.hasOwnProperty(keyname)) {
     78     let value = keymap[keyname]
     79     if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) continue
     80     if (value == "...") { delete keymap[keyname]; continue }
     81 
     82     let keys = map(keyname.split(" "), normalizeKeyName)
     83     for (let i = 0; i < keys.length; i++) {
     84       let val, name
     85       if (i == keys.length - 1) {
     86         name = keys.join(" ")
     87         val = value
     88       } else {
     89         name = keys.slice(0, i + 1).join(" ")
     90         val = "..."
     91       }
     92       let prev = copy[name]
     93       if (!prev) copy[name] = val
     94       else if (prev != val) throw new Error("Inconsistent bindings for " + name)
     95     }
     96     delete keymap[keyname]
     97   }
     98   for (let prop in copy) keymap[prop] = copy[prop]
     99   return keymap
    100 }
    101 
    102 export function lookupKey(key, map, handle, context) {
    103   map = getKeyMap(map)
    104   let found = map.call ? map.call(key, context) : map[key]
    105   if (found === false) return "nothing"
    106   if (found === "...") return "multi"
    107   if (found != null && handle(found)) return "handled"
    108 
    109   if (map.fallthrough) {
    110     if (Object.prototype.toString.call(map.fallthrough) != "[object Array]")
    111       return lookupKey(key, map.fallthrough, handle, context)
    112     for (let i = 0; i < map.fallthrough.length; i++) {
    113       let result = lookupKey(key, map.fallthrough[i], handle, context)
    114       if (result) return result
    115     }
    116   }
    117 }
    118 
    119 // Modifier key presses don't count as 'real' key presses for the
    120 // purpose of keymap fallthrough.
    121 export function isModifierKey(value) {
    122   let name = typeof value == "string" ? value : keyNames[value.keyCode]
    123   return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"
    124 }
    125 
    126 export function addModifierNames(name, event, noShift) {
    127   let base = name
    128   if (event.altKey && base != "Alt") name = "Alt-" + name
    129   if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") name = "Ctrl-" + name
    130   if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") name = "Cmd-" + name
    131   if (!noShift && event.shiftKey && base != "Shift") name = "Shift-" + name
    132   return name
    133 }
    134 
    135 // Look up the name of a key as indicated by an event object.
    136 export function keyName(event, noShift) {
    137   if (presto && event.keyCode == 34 && event["char"]) return false
    138   let name = keyNames[event.keyCode]
    139   if (name == null || event.altGraphKey) return false
    140   return addModifierNames(name, event, noShift)
    141 }
    142 
    143 export function getKeyMap(val) {
    144   return typeof val == "string" ? keyMap[val] : val
    145 }