KDViewController     = require './../../core/viewcontroller.coffee'
KDScrollView         = require './../scrollview/scrollview.coffee'
KDListViewController = require './../list/listviewcontroller.coffee'
module.exports = class JTreeViewController extends KDViewController

  keyMap = ->
    37 : 'left'
    38 : 'up'
    39 : 'right'
    40 : 'down'
    8  : 'backspace'
    9  : 'tab'
    13 : 'enter'
    27 : 'escape'

  dragHelper = null

  # we're doing this because if we create this image on dragstart
  # it waits to load the image and you don't see the image on first
  # drag, this way we preload it and see it on first drag too.
  cacheDragHelper = do ->

    dragHelper       = document.createElement 'img'
    dragHelper.src   = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAG4AAAAYCAMAAAAs/jgVAAAA0lBMVEX///+It9YAAAD///8AAACIt9aIt9aIt9aIt9aIt9aIt9YAAAD///+It9aIt9aIt9aIt9aIt9aIt9aIt9bT09OIt9aIt9aIt9b///+It9bv9fr+/v79/f2QvNn4+PioyuHA2enP4u/09PS41Obf7PTn8ff6+vr29vb3+vygxd78/Pz19fX7+/vs7OzY2NjR0dGwz+Tv7+/T09Pz8/PX19fQ0NCYwdvx8fHLy8vq6urZ2dnX5/H5+fny8vLOzs739/fPz8/W1tbu7u7w8PDH3ezd3d0P0SzzAAAAGHRSTlMAACZqGJkG2/k2rkZV4bG0V9gDaYBabJYxtX/TAAACLElEQVR4Xu3V127bMBQG4NT1SOw4s0kP59Ce3iN7tu//Sj2i0iiKE8AB7Juiv0SIFAR9OAQJ7mwy/9NsvElz61yDA4cEgGWQZA0Evm0xyAFAYkVMr/nCtU8PjsnGcnxw2n7lGMsSnmSMAX9slNxRh2w4naMXjrGEByzgCWPsqtdE7qT7ARDrOuCqL4LdE8tlyCCHWMZmjw3kWuSDuJ5V4K9iKl7BWl7LcgHnXOLFecAHV8jtkU841wqrHGbhreHtWS6QN5nMbiR2BjPkfhaAoh7QEQVfKFqMkNMAoEAp3wNwC05FQGNbXQwAeuEX7z2DXVP0NIAv6uUhl8gslFKGmUzkbIBcx3L4/XUUC+q+crYg5NDSkTBaRIpo33J2qj1NYhCeL2IwRFMBC6Lqy8VWJ8MwxSuUQTgpuN2SI0Tbf6xyKETKaAVFKs4OlWc/Kt4ZX5NadgvuPrxPp+E0xWc4mX2Bi4FgKi6ytVQcESZafMAtnWWKDdnhBLlOnYshJteW+2Vqk0ko8qOSG1FCDJKjihMuIXS0Mpnj6e341sE2HTuWa9U5YgCM5eJyqUQeYCHIxRRAl5ygoIUP4FVcOdSUeAbv16WSPz/l+WWePz3nl/PhpxthjV22zkZo9i4e7i7u5tgeLn4P7TbfDme3+f4PJ0wdZ+o4aegs5wXX7m6D67aRe18ecpizPtlw+mf2RHhfnuWwwPPDDYr9w/PyAFo9z7d8vGJ5399kf+cfyh+807YxJJdmLQAAAABJRU5ErkJggg=='
    dragHelper.width = 110

  constructor:(options = {}, data = [])->

    o = options
    o.view                      or= (new KDScrollView cssClass : "jtreeview-wrapper")
    o.listViewControllerClass   or= KDListViewController
    o.treeItemClass             or= JTreeItemView
    o.listViewClass             or= JTreeView
    o.itemChildClass            or= null
    o.itemChildOptions          or= {}
    o.nodeIdPath                or= 'id'
    o.nodeParentIdPath          or= 'parentId'
    o.contextMenu                ?= no
    o.multipleSelection          ?= no
    o.addListsCollapsed          ?= no
    o.sortable                   ?= no
    o.putDepthInfo               ?= yes
    o.addOrphansToRoot           ?= yes
    o.dragdrop                   ?= no

    super o, data

    @listData                     = {}
    @listControllers              = {}
    @nodes                        = {}
    @indexedNodes                 = []
    @selectedNodes                = []


  loadView:(treeView)->

    @initTree @getData()
    @setKeyView()
    @setMainListeners()

    @registerBoundaries()


  registerBoundaries:->
    @boundaries =
      top     : @getView().getY()
      left    : @getView().getX()
      width   : @getView().getWidth()
      height  : @getView().getHeight()

  ###
  HELPERS
  ###

  initTree:(nodes)->

    @removeAllNodes()
    @addNodes nodes

  logTreeStructure:->

    o = @getOptions()
    for own index, node of @indexedNodes
      log index, @getNodeId(node), @getNodePId(node), node.depth

  getNodeId:(nodeData)->

    return nodeData[@getOptions().nodeIdPath]

  getNodePId:(nodeData)->

    return nodeData[@getOptions().nodeParentIdPath]

  getPathIndex:(targetPath)->
    for node, index in @indexedNodes
      return index if @getNodeId(node) is targetPath
    return -1

  repairIds:(nodeData)->

    options = @getOptions()
    idPath  = options.nodeIdPath
    pIdPath = options.nodeParentIdPath

    nodeData[idPath] or= @utils.getUniqueId()
    nodeData[idPath]   = "#{@getNodeId nodeData}"
    nodeData[pIdPath]  = if @getNodePId nodeData then "#{@getNodePId nodeData}" else "0"

    if options.putDepthInfo
      if @nodes[nodeData[pIdPath]]?.getData
        nodeData.depth = @nodes[nodeData[pIdPath]].getData().depth + 1
      else
        nodeData.depth = 0

    if nodeData[pIdPath] isnt "0" and not @nodes[nodeData[pIdPath]]
      if options.addOrphansToRoot then nodeData[pIdPath] = "0" else nodeData = no

    if nodeData
      @nodes[@getNodeId nodeData] = {}

    return nodeData

  isNodeVisible:(nodeView)->

    nodeData = nodeView.getData()
    parentNode = @nodes[@getNodePId nodeData]
    if parentNode
      if parentNode.expanded
        @isNodeVisible parentNode
      else
        return no
    else
      return yes


  areSibling:(node1, node2)->

    node1PId = @getNodePId node1.getData()
    node2PId = @getNodePId node2.getData()

    return node1PId is node2PId

  ###
  DECORATORS
  ###

  setFocusState:->

    view = @getView()
    KD.getSingleton("windowController").addLayer view
    view.unsetClass "dim"

  setBlurState:->

    view = @getView()
    KD.getSingleton("windowController").removeLayer view
    view.setClass "dim"

  ###
  CRUD OPERATIONS FOR NODES
  ###

  # Following Code is partially broken
  # we need to rewrite it at some point ~ GG
  # We're not using it right now but when we decided to use it
  # we need to use it to guess index of node for indexedNodes
  #
  # guessIndex:(nodeData, parentId, index)->
  #   parentIndex = @getPathIndex(parentId)
  #   treeIndex   = parentIndex + index

  #   prevItem  = @indexedNodes[treeIndex - 1]
  #   currItem  = @indexedNodes[treeIndex]
  #   nextItem  = @indexedNodes[treeIndex + 1]

  #   return treeIndex unless nextItem

  #   return treeIndex if index          is 0                           or \
  #                       prevItem.depth >= nodeData.depth              or \
  #                       treeIndex      is parentIndex                 or \
  #                      (treeIndex - 1  is parentIndex and index > 1)  or \
  #                       nextItem.depth <= nodeData.depth

  #   for i in [0..@indexedNodes.length]
  #     nextIndex = treeIndex + 1 + i
  #     nextItem  = @indexedNodes[nextIndex]
  #     return nextIndex - 1 unless nextItem
  #     return nextIndex - 1 if nextItem.depth is nodeData.depth

  #   return 0

  addNode:(nodeData, index)->

    # This methods index option is not usable for now ~ FIXME GG

    return if @nodes[@getNodeId nodeData]
    nodeData = @repairIds nodeData
    return unless nodeData

    @getData().push nodeData unless nodeData in @getData()

    @registerListData nodeData
    parentId = @getNodePId nodeData

    if @listControllers[parentId]?
      list = @listControllers[parentId].getListView()
    else
      list = @createList(parentId).getListView()
      @addSubList @nodes[parentId], parentId

    node = list.addItem nodeData, index
    @emit "NodeWasAdded", node

    # Enable this to make indexedNodes work correctly with indexes ~ GG
    #
    # if index >= 0
    #   KD.time "Starting to guess"
    #   log "INDEX", index = @guessIndex nodeData, parentId, index
    #   KD.timeEnd "Starting to guess"

    @addIndexedNode nodeData
    return node

  addNodes:(nodes)->
    @addNode node for node in nodes

  removeNode:(id)->

    nodeIndexToRemove = null
    for nodeData, index in @getData()
      if @getNodeId(nodeData) is id
        @removeIndexedNode nodeData
        nodeIndexToRemove = index

    if nodeIndexToRemove?
      nodeToRemove = @getData().splice(nodeIndexToRemove, 1)[0]
      @removeChildNodes id
      parentId = @getNodePId nodeToRemove
      # self remove
      @listControllers[parentId].getListView().removeItem @nodes[id]
      # remove reference
      delete @nodes[id]

  removeNodeView:(nodeView)->

    @removeNode @getNodeId nodeView.getData()

  removeAllNodes:->

    for own id, listController of @listControllers
      listController.getListItems().forEach @bound 'removeNodeView'
      listController?.getView().destroy()
      delete @listControllers[id]
      delete @listData[id]

    @nodes           = {}
    @listData        = {}
    @indexedNodes    = []
    @selectedNodes   = []
    @listControllers = {}

  removeChildNodes:(id)->

    childNodeIdsToRemove = []
    for nodeData, index in @getData()
      if @getNodePId(nodeData) is id
        childNodeIdsToRemove.push @getNodeId(nodeData)

    for childNodeId in childNodeIdsToRemove
      @removeNode childNodeId

    @listControllers[id]?.getView().destroy()
    delete @listControllers[id]
    delete @listData[id]

  nodeWasAdded:(nodeView)->

    nodeData = nodeView.getData()
    nodeView.$().attr "draggable","true" if @getOptions().dragdrop
    {id, parentId} = nodeData
    @nodes[@getNodeId nodeData] = nodeView
    if @nodes[@getNodePId nodeData]
      @expand @nodes[@getNodePId nodeData] unless @getOptions().addListsCollapsed
      # todo: make decoration with events
      @nodes[@getNodePId nodeData].decorateSubItemsState()
    return unless @listControllers[id]
    @addSubList nodeView, id

  getChildNodes :(aParentNode)->
    children = []
    @indexedNodes.forEach (node, index)=>
      if @getNodePId(node) is @getNodeId(aParentNode)
        children.push {node, index}
    if children.length then children else no

  getPreviousNeighbor: (aParentNode)->
    neighbor = aParentNode
    children = @getChildNodes aParentNode
    if children
      lastChild = children.last
      neighbor = @getPreviousNeighbor lastChild.node
    return neighbor

  addIndexedNode:(nodeData, index)->

    if index >= 0
      @indexedNodes.splice index + 1, 0, nodeData
      return

    # if node parent is present
    parentNodeView = @nodes[@getNodePId nodeData]
    if parentNodeView
      prevNeighbor  = @getPreviousNeighbor parentNodeView.getData()
      neighborIndex = @indexedNodes.indexOf prevNeighbor
      @indexedNodes.splice neighborIndex + 1, 0, nodeData
    else
      @indexedNodes.push nodeData

  removeIndexedNode:(nodeData)->

    if nodeData in @indexedNodes
      index = @indexedNodes.indexOf nodeData
      # Disable this for now, useless for most cases, FIXME GG
      # @selectNode @nodes[@getNodeId @indexedNodes[index-1]] if index-1 >= 0
      @indexedNodes.splice index, 1
      # todo: make decoration with events
      if @nodes[@getNodePId nodeData] and not \
        @getChildNodes(@nodes[@getNodePId nodeData].getData())
          @nodes[@getNodePId nodeData].decorateSubItemsState(no)


  ###
  CREATING LISTS
  ###

  registerListData:(node)->

    parentId = @getNodePId(node)
    @listData[parentId] or= []
    @listData[parentId].push node

  createList:(listId, listItems)->

    options = @getOptions()
    @listControllers[listId] = new options.listViewControllerClass
      id                 : "#{@getId()}_#{listId}"
      wrapper            : no
      scrollView         : no
      selection          : options.selection ? no
      multipleSelection  : options.multipleSelection ? no
      view               : new options.listViewClass
        tagName          : "ul"
        type             : options.type
        itemClass        : options.treeItemClass
        itemChildClass   : options.itemChildClass
        itemChildOptions : options.itemChildOptions
    , items : listItems

    @setListenersForList listId
    return @listControllers[listId]

  addSubList:(nodeView, id)->

    o = @getOptions()
    listToBeAdded = @listControllers[id].getView()
    if nodeView
      nodeView.$().after listToBeAdded.$()
      listToBeAdded.parentIsInDom = yes
      listToBeAdded.emit 'viewAppended'
      if o.addListsCollapsed
        @collapse nodeView
      else
        @expand nodeView
    else
      @getView().addSubView listToBeAdded


  ###
  REGISTERING LISTENERS
  ###

  setMainListeners:->

    KD.getSingleton("windowController").on "ReceivedMouseUpElsewhere", (event)=> @mouseUp event

    @getView().on "ReceivedClickElsewhere", => @setBlurState()

  setListenersForList:(listId)->

    @listControllers[listId].getView().on 'ItemWasAdded', (view, index)=>
      @setItemListeners view, index

    @listControllers[listId].on "ItemSelectionPerformed", (listController, {event, items})=>
      @organizeSelectedNodes listController, items, event

    @listControllers[listId].on "ItemDeselectionPerformed", (listController, {event, items})=>
      @deselectNodes listController, items, event


    @listControllers[listId].getListView().on 'KeyDownOnTreeView', (event)=> @keyEventHappened event

  setItemListeners:(view, index)->

    view.on "viewAppended", @nodeWasAdded.bind @, view

    mouseEvents = ["dblclick", "click", "mousedown", "mouseup", "mouseenter", "mousemove"]

    if @getOptions().contextMenu
      mouseEvents.push "contextmenu"

    if @getOptions().dragdrop
      mouseEvents = mouseEvents.concat ["dragstart", "dragenter", "dragleave", "dragend", "dragover", "drop"]

    view.on mouseEvents, (event)=> @mouseEventHappened view, event


  ###
  NODE SELECTION
  ###


  organizeSelectedNodes:(listController, nodes, event = {})->

    unless (event.metaKey or event.ctrlKey or event.shiftKey) and @getOptions().multipleSelection
      @deselectAllNodes(listController)

    for node in nodes
      unless node in @selectedNodes
        @selectedNodes.push node

  deselectNodes:(listController, nodes, event)->

    for node in nodes
      if node in @selectedNodes
        @selectedNodes.splice @selectedNodes.indexOf(node), 1

  deselectAllNodes:(exceptThisController)->

    for own id, listController of @listControllers
      if listController isnt exceptThisController
        listController.deselectAllItems()
    @selectedNodes = []

  selectNode:(nodeView, event, setFocus = yes)->

    return unless nodeView
    if setFocus then @setFocusState()

    # There is an issue with NFinderTreeController, we sometimes missing
    # the list of controllers here and calling selectNode for non-existent
    # listController, FIXME later ~ GG
    controller = @listControllers[@getNodePId nodeView.getData()]
    controller.selectItem nodeView, event  if controller

  deselectNode:(nodeView, event)->

    @listControllers[@getNodePId nodeView.getData()].deselectSingleItem nodeView, event

  selectFirstNode:->

    @selectNode @nodes[@getNodeId @indexedNodes[0]]

  selectNodesByRange:(node1, node2)->

    indicesToBeSliced = [@indexedNodes.indexOf(node1.getData()), @indexedNodes.indexOf(node2.getData())]
    indicesToBeSliced.sort (a, b)-> a - b
    itemsToBeSelected = @indexedNodes.slice indicesToBeSliced[0], indicesToBeSliced[1] + 1
    for node in itemsToBeSelected
      @selectNode @nodes[@getNodeId node], shiftKey : yes

  ###
  COLLAPSE / EXPAND
  ###

  toggle:(nodeView)->

    if nodeView.expanded then @collapse nodeView else @expand nodeView

  expand:(nodeView)->

    nodeData = nodeView.getData()
    nodeView.expand()
    @listControllers[@getNodeId nodeData]?.getView().expand()

  collapse:(nodeView)->

    nodeData = nodeView.getData()
    @listControllers[@getNodeId nodeData]?.getView().collapse =>
      nodeView.collapse()


  ###
  DND UI FEEDBACKS
  ###

  # THESE 3 METHODS BELOW SHOULD BE REFACTORRED MAKES THE UI HORRIBLY SLUGGISH ON DND - Sinan 07/2012

  showDragOverFeedback: do ->
    KD.utils.throttle (nodeView, event)->

      # log "show", nodeView.getData().name
      nodeData = nodeView.getData()
      if nodeData.type isnt "file"
        nodeView.setClass "drop-target"
      else
        @nodes[nodeData.parentPath]?.setClass "drop-target"
        @listControllers[nodeData.parentPath]?.getListView().setClass "drop-target"

      nodeView.setClass "items-hovering"

    , 100

  clearDragOverFeedback: do ->
    KD.utils.throttle (nodeView, event)->

      # log "clear", nodeView.getData().name
      nodeData = nodeView.getData()
      if nodeData.type isnt "file"
        nodeView.unsetClass "drop-target"
      else
        @nodes[nodeData.parentPath]?.unsetClass "drop-target"
        @listControllers[nodeData.parentPath]?.getListView().unsetClass "drop-target"

      nodeView.unsetClass "items-hovering"

    , 100

  clearAllDragFeedback: ->

    @utils.wait 101, =>
      @getView().$('.drop-target').removeClass "drop-target"
      @getView().$('.items-hovering').removeClass "items-hovering"
      listController.getListView().unsetClass "drop-target" for own path, listController of @listControllers
      nodeView.unsetClass "items-hovering drop-target" for own path, nodeView of @nodes

  ###
  HANDLING MOUSE EVENTS
  ###


  mouseEventHappened:(nodeView, event)->

    switch event.type
      when "mouseenter"  then @mouseEnter nodeView, event
      when "dblclick"    then @dblClick nodeView, event
      when "click"       then @click nodeView, event
      when "mousedown"   then @mouseDown nodeView, event
      when "mouseup"     then @mouseUp nodeView, event
      when "mousemove"   then @mouseMove nodeView, event
      when "contextmenu" then @contextMenu nodeView, event
      when "dragstart"   then @dragStart nodeView, event
      when "dragenter"   then @dragEnter nodeView, event
      when "dragleave"   then @dragLeave nodeView, event
      when "dragover"    then @dragOver nodeView, event
      when "dragend"     then @dragEnd nodeView, event
      when "drop"        then @drop nodeView, event

  dblClick:(nodeView, event)->

    @toggle nodeView

  click:(nodeView, event)->

    if /arrow/.test event.target.className
      @toggle nodeView
      return @selectedItems

    @lastEvent = event
    @deselectAllNodes() unless (event.metaKey or event.ctrlKey or event.shiftKey) and @getOptions().multipleSelection

    if nodeView?
      if event.shiftKey and @selectedNodes.length > 0 and @getOptions().multipleSelection
        @selectNodesByRange @selectedNodes[0], nodeView
      else
        @selectNode nodeView, event

    return @selectedItems

  contextMenu:(nodeView, event)->

  mouseDown:(nodeView, event)->

    @lastEvent = event
    unless nodeView in @selectedNodes
      @mouseIsDown = yes
      @cancelDrag = yes
      @mouseDownTempItem = nodeView
      @mouseDownTimer = setTimeout =>
        @mouseIsDown = no
        @cancelDrag = no
        @mouseDownTempItem = null
        @selectNode nodeView, event
      , 1000

    else
      @mouseIsDown = no
      @mouseDownTempItem = null

  mouseUp:(event)->

    clearTimeout @mouseDownTimer
    @mouseIsDown = no
    @cancelDrag = no
    @mouseDownTempItem = null

  mouseEnter:(nodeView, event)->

    clearTimeout @mouseDownTimer
    if @mouseIsDown and @getOptions().multipleSelection
      @cancelDrag = yes
      @deselectAllNodes() unless (event.metaKey or event.ctrlKey or event.shiftKey) and @getOptions().multipleSelection
      @selectNodesByRange @mouseDownTempItem, nodeView

  ###
  HANDLING DND
  ###

  dragStart: (nodeView, event)->

    if @cancelDrag
      event.preventDefault()
      event.stopPropagation()
      return no

    @dragIsActive = yes
    e = event.originalEvent
    e.dataTransfer.effectAllowed = 'copyMove' # only dropEffect='copy' will be dropable

    # We need to look it at later FIXME GG
    # transferredData = (JSON.stringify node.getData() for node in @selectedNodes)
    transferredData = (@getNodeId node.getData() for node in @selectedNodes)

    e.dataTransfer.setData('Text', transferredData.join()) # required otherwise doesn't work

    if @selectedNodes.length > 1
      e.dataTransfer.setDragImage dragHelper, -10, 0

    nodeView.setClass "drag-started"

  dragEnter: (nodeView, event)->
    @emit "dragEnter", nodeView, event

  dragLeave: (nodeView, event)->
    @clearAllDragFeedback()
    @emit "dragLeave", nodeView, event

  dragOver: (nodeView, event)->
    @emit "dragOver", nodeView, event

  dragEnd: (nodeView, event)->

    @dragIsActive = no
    nodeView.unsetClass "drag-started"
    @clearAllDragFeedback()
    @emit "dragEnd", nodeView, event

  drop: (nodeView, event)->

    @dragIsActive = no
    event.preventDefault()
    event.stopPropagation()
    @emit "drop", nodeView, event
    no

  ###
  HANDLING KEY EVENTS
  ###

  setKeyView:->

    if @listControllers[0]
      KD.getSingleton("windowController").setKeyView @listControllers[0].getListView()

  keyEventHappened:(event)->

    key = keyMap()[event.which]
    [nodeView] = @selectedNodes

    @emit "keyEventPerformedOnTreeView", event

    return unless nodeView

    switch key
      when "down","up"
        event.preventDefault()
        nextNode = @["perform#{key.capitalize()}Key"] nodeView, event
        @getView().scrollToSubView?(nextNode) if nextNode
      when "left"      then @performLeftKey nodeView, event
      when "right"     then @performRightKey nodeView, event
      when "backspace" then @performBackspaceKey nodeView, event
      when "enter"     then @performEnterKey nodeView, event
      when "escape"    then @performEscapeKey nodeView, event
      when "tab"       then return no

  performDownKey:(nodeView, event)->

    if @selectedNodes.length > 1
      nodeView = @selectedNodes[@selectedNodes.length-1]
      unless (event.metaKey or event.ctrlKey or event.shiftKey) and @getOptions().multipleSelection
        @deselectAllNodes()
        @selectNode nodeView

    nodeData = nodeView.getData()

    nextIndex = @indexedNodes.indexOf(nodeData)+1
    if @indexedNodes[nextIndex]
      nextNode = @nodes[@getNodeId @indexedNodes[nextIndex]]
      if @isNodeVisible nextNode
        if nextNode in @selectedNodes
          @deselectNode @nodes[@getNodeId nodeData]
        else
          @selectNode nextNode, event
          return nextNode
      else
        @performDownKey nextNode, event

  performUpKey:(nodeView, event)->

    if @selectedNodes.length > 1
      nodeView = @selectedNodes[@selectedNodes.length-1]
      unless (event.metaKey or event.ctrlKey or event.shiftKey) and @getOptions().multipleSelection
        @deselectAllNodes()
        @selectNode nodeView

    nodeData = nodeView.getData()

    nextIndex = @indexedNodes.indexOf(nodeData)-1
    if @indexedNodes[nextIndex]
      nextNode = @nodes[@getNodeId @indexedNodes[nextIndex]]
      if @isNodeVisible nextNode
        if nextNode in @selectedNodes
          @deselectNode @nodes[@getNodeId nodeData]
        else
          @selectNode nextNode, event
      else
        @performUpKey nextNode, event
    return nextNode

  performRightKey:(nodeView, event)->

    @expand nodeView
    # o = @getOptions()
    # @addNode
    #   title     : "some title"
    #   parentId  : @getNodeId nodeData

  performLeftKey:(nodeView, event)->

    nodeData = nodeView.getData()
    if @nodes[@getNodePId nodeData]
      parentNode = @nodes[@getNodePId nodeData]
      @selectNode parentNode
    return parentNode

  performBackspaceKey:(nodeView, event)->

    # nodeData = nodeView.getData()
    # @removeNode @getNodeId nodeData

  performEnterKey:(nodeView, event)->

    # nodeData = nodeView.getData()
    # nodeView.toggle()
    # @listControllers[@getNodeId nodeData]?.getView().toggle()

  performEscapeKey:(nodeView, event)->

