onKeyDown (event) {
    const keynum = event.which || event.keyCode
    const target = event.target || event.srcElement
    const ctrlKey = event.ctrlKey
    const shiftKey = event.shiftKey
    const altKey = event.altKey
    let handled = false
    let prevNode, nextNode, nextDom, nextDom2
    const editable = this.editor.options.mode === 'tree'
    let oldSelection
    let oldNextNode
    let oldParent
    let oldIndexRedo
    let newIndexRedo
    let oldParentPathRedo
    let newParentPathRedo
    let nodes
    let multiselection
    const selectedNodes = this.editor.multiselection.nodes.length > 0
      ? this.editor.multiselection.nodes
      : [this]
    const firstNode = selectedNodes[0]
    const lastNode = selectedNodes[selectedNodes.length - 1]

    // console.log(ctrlKey, keynum, event.charCode); // TODO: cleanup
    if (keynum === 13) { // Enter
      if (target === this.dom.value) {
        if (!this.editable.value || event.ctrlKey) {
          if (isUrl(this.value)) {
            window.open(this.value, '_blank', 'noreferrer')
            handled = true
          }
        }
      } else if (target === this.dom.expand) {
        const expandable = this._hasChilds()
        if (expandable) {
          const recurse = event.ctrlKey // with ctrl-key, expand/collapse all
          this._onExpand(recurse)
          target.focus()
          handled = true
        }
      }
    } else if (keynum === 68) { // D
      if (ctrlKey && editable) { // Ctrl+D
        Node.onDuplicate(selectedNodes)
        handled = true
      }
    } else if (keynum === 69) { // E
      if (ctrlKey) { // Ctrl+E and Ctrl+Shift+E
        this._onExpand(shiftKey) // recurse = shiftKey
        target.focus() // TODO: should restore focus in case of recursing expand (which takes DOM offline)
        handled = true
      }
    } else if (keynum === 77 && editable) { // M
      if (ctrlKey) { // Ctrl+M
        this.showContextMenu(target)
        handled = true
      }
    } else if (keynum === 46 && editable) { // Del
      if (ctrlKey) { // Ctrl+Del
        Node.onRemove(selectedNodes)
        handled = true
      }
    } else if (keynum === 45 && editable) { // Ins
      if (ctrlKey && !shiftKey) { // Ctrl+Ins
        this._onInsertBefore()
        handled = true
      } else if (ctrlKey && shiftKey) { // Ctrl+Shift+Ins
        this._onInsertAfter()
        handled = true
      }
    } else if (keynum === 35) { // End
      if (altKey) { // Alt+End
        // find the last node
        const endNode = this._lastNode()
        if (endNode) {
          endNode.focus(Node.focusElement || this._getElementName(target))
        }
        handled = true
      }
    } else if (keynum === 36) { // Home
      if (altKey) { // Alt+Home
        // find the first node
        const homeNode = this._firstNode()
        if (homeNode) {
          homeNode.focus(Node.focusElement || this._getElementName(target))
        }
        handled = true
      }
    } else if (keynum === 37) { // Arrow Left
      if (altKey && !shiftKey) { // Alt + Arrow Left
        // move to left element
        const prevElement = this._previousElement(target)
        if (prevElement) {
          this.focus(this._getElementName(prevElement))
        }
        handled = true
      } else if (altKey && shiftKey && editable) { // Alt + Shift + Arrow left
        if (lastNode.expanded) {
          const appendDom = lastNode.getAppendDom()
          nextDom = appendDom ? appendDom.nextSibling : undefined
        } else {
          const dom = lastNode.getDom()
          nextDom = dom.nextSibling
        }
        if (nextDom) {
          nextNode = Node.getNodeFromTarget(nextDom)
          nextDom2 = nextDom.nextSibling
          const nextNode2 = Node.getNodeFromTarget(nextDom2)
          if (nextNode && nextNode instanceof AppendNode &&
              !(lastNode.parent.childs.length === 1) &&
              nextNode2 && nextNode2.parent) {
            oldSelection = this.editor.getDomSelection()
            oldParent = firstNode.parent
            oldNextNode = oldParent.childs[lastNode.getIndex() + 1] || oldParent.append
            oldIndexRedo = firstNode.getIndex()
            newIndexRedo = nextNode2.getIndex()
            oldParentPathRedo = oldParent.getInternalPath()
            newParentPathRedo = nextNode2.parent.getInternalPath()

            selectedNodes.forEach(node => {
              nextNode2.parent.moveBefore(node, nextNode2)
            })
            this.focus(Node.focusElement || this._getElementName(target))

            this.editor._onAction('moveNodes', {
              count: selectedNodes.length,
              fieldNames: selectedNodes.map(getField),

              oldParentPath: oldParent.getInternalPath(),
              newParentPath: firstNode.parent.getInternalPath(),
              oldIndex: oldNextNode.getIndex(),
              newIndex: firstNode.getIndex(),

              oldIndexRedo,
              newIndexRedo,
              oldParentPathRedo,
              newParentPathRedo,

              oldSelection,
              newSelection: this.editor.getDomSelection()
            })
          }
        }
      }
    } else if (keynum === 38) { // Arrow Up
      if (altKey && !shiftKey) { // Alt + Arrow Up
        // find the previous node
        prevNode = this._previousNode()
        if (prevNode) {
          this.editor.deselect(true)
          prevNode.focus(Node.focusElement || this._getElementName(target))
        }
        handled = true
      } else if (!altKey && ctrlKey && shiftKey && editable) { // Ctrl + Shift + Arrow Up
        // select multiple nodes
        prevNode = this._previousNode()
        if (prevNode) {
          multiselection = this.editor.multiselection
          multiselection.start = multiselection.start || this
          multiselection.end = prevNode
          nodes = this.editor._findTopLevelNodes(multiselection.start, multiselection.end)

          this.editor.select(nodes)
          prevNode.focus('field') // select field as we know this always exists
        }
        handled = true
      } else if (altKey && shiftKey && editable) { // Alt + Shift + Arrow Up
        // find the previous node
        prevNode = firstNode._previousNode()
        if (prevNode && prevNode.parent) {
          oldSelection = this.editor.getDomSelection()
          oldParent = firstNode.parent
          oldNextNode = oldParent.childs[lastNode.getIndex() + 1] || oldParent.append
          oldIndexRedo = firstNode.getIndex()
          newIndexRedo = prevNode.getIndex()
          oldParentPathRedo = oldParent.getInternalPath()
          newParentPathRedo = prevNode.parent.getInternalPath()

          selectedNodes.forEach(node => {
            prevNode.parent.moveBefore(node, prevNode)
          })
          this.focus(Node.focusElement || this._getElementName(target))

          this.editor._onAction('moveNodes', {
            count: selectedNodes.length,
            fieldNames: selectedNodes.map(getField),

            oldParentPath: oldParent.getInternalPath(),
            newParentPath: firstNode.parent.getInternalPath(),
            oldIndex: oldNextNode.getIndex(),
            newIndex: firstNode.getIndex(),

            oldIndexRedo,
            newIndexRedo,
            oldParentPathRedo,
            newParentPathRedo,

            oldSelection,
            newSelection: this.editor.getDomSelection()
          })
        }
        handled = true
      }
    } else if (keynum === 39) { // Arrow Right
      if (altKey && !shiftKey) { // Alt + Arrow Right
        // move to right element
        const nextElement = this._nextElement(target)
        if (nextElement) {
          this.focus(this._getElementName(nextElement))
        }
        handled = true
      } else if (altKey && shiftKey && editable) { // Alt + Shift + Arrow Right
        const dom = firstNode.getDom()
        const prevDom = dom.previousSibling
        if (prevDom) {
          prevNode = Node.getNodeFromTarget(prevDom)
          if (prevNode && prevNode.parent && !prevNode.isVisible()) {
            oldSelection = this.editor.getDomSelection()
            oldParent = firstNode.parent
            oldNextNode = oldParent.childs[lastNode.getIndex() + 1] || oldParent.append
            oldIndexRedo = firstNode.getIndex()
            newIndexRedo = prevNode.getIndex()
            oldParentPathRedo = oldParent.getInternalPath()
            newParentPathRedo = prevNode.parent.getInternalPath()

            selectedNodes.forEach(node => {
              prevNode.parent.moveBefore(node, prevNode)
            })
            this.focus(Node.focusElement || this._getElementName(target))

            this.editor._onAction('moveNodes', {
              count: selectedNodes.length,
              fieldNames: selectedNodes.map(getField),

              oldParentPath: oldParent.getInternalPath(),
              newParentPath: firstNode.parent.getInternalPath(),
              oldIndex: oldNextNode.getIndex(),
              newIndex: firstNode.getIndex(),

              oldIndexRedo,
              newIndexRedo,
              oldParentPathRedo,
              newParentPathRedo,

              oldSelection,
              newSelection: this.editor.getDomSelection()
            })
          }
        }
      }
    } else if (keynum === 40) { // Arrow Down
      if (altKey && !shiftKey) { // Alt + Arrow Down
        // find the next node
        nextNode = this._nextNode()
        if (nextNode) {
          this.editor.deselect(true)
          nextNode.focus(Node.focusElement || this._getElementName(target))
        }
        handled = true
      } else if (!altKey && ctrlKey && shiftKey && editable) { // Ctrl + Shift + Arrow Down
        // select multiple nodes
        nextNode = this._nextNode()
        if (nextNode) {
          multiselection = this.editor.multiselection
          multiselection.start = multiselection.start || this
          multiselection.end = nextNode
          nodes = this.editor._findTopLevelNodes(multiselection.start, multiselection.end)

          this.editor.select(nodes)
          nextNode.focus('field') // select field as we know this always exists
        }
        handled = true
      } else if (altKey && shiftKey && editable) { // Alt + Shift + Arrow Down
        // find the 2nd next node and move before that one
        if (lastNode.expanded) {
          nextNode = lastNode.append ? lastNode.append._nextNode() : undefined
        } else {
          nextNode = lastNode._nextNode()
        }

        // when the next node is not visible, we've reached the "showMore" buttons
        if (nextNode && !nextNode.isVisible()) {
          nextNode = nextNode.parent.showMore
        }

        if (nextNode && nextNode instanceof AppendNode) {
          nextNode = lastNode
        }

        const nextNode2 = nextNode && (nextNode._nextNode() || nextNode.parent.append)
        if (nextNode2 && nextNode2.parent) {
          oldSelection = this.editor.getDomSelection()
          oldParent = firstNode.parent
          oldNextNode = oldParent.childs[lastNode.getIndex() + 1] || oldParent.append
          oldIndexRedo = firstNode.getIndex()
          newIndexRedo = nextNode2.getIndex()
          oldParentPathRedo = oldParent.getInternalPath()
          newParentPathRedo = nextNode2.parent.getInternalPath()

          selectedNodes.forEach(node => {
            nextNode2.parent.moveBefore(node, nextNode2)
          })
          this.focus(Node.focusElement || this._getElementName(target))

          this.editor._onAction('moveNodes', {
            count: selectedNodes.length,
            fieldNames: selectedNodes.map(getField),
            oldParentPath: oldParent.getInternalPath(),
            newParentPath: firstNode.parent.getInternalPath(),
            oldParentPathRedo,
            newParentPathRedo,
            oldIndexRedo,
            newIndexRedo,
            oldIndex: oldNextNode.getIndex(),
            newIndex: firstNode.getIndex(),
            oldSelection,
            newSelection: this.editor.getDomSelection()
          })
        }
        handled = true
      }
    }

    if (handled) {
      event.preventDefault()
      event.stopPropagation()
    }
  }
Key down event handler
@param {Event} event