<commit_before>KDObject      = require './object.coffee'

module.exports = class KDView extends KDObject

# #
# CLASS LEVEL STUFF
# #

  {defineProperty} = Object

  deprecated = (methodName)-> warn "#{methodName} is deprecated from KDView if you need it override in your subclass"

  eventNames =
    ///
    ^(
    (dbl)?click|
    key(up|down|press)|
    mouse(up|down|over|enter|leave|move)|
    drag(start|end|enter|leave|over)|
    blur|change|focus|
    drop|
    contextmenu|
    scroll|
    paste|
    error|
    load
    )$
    ///

  eventToMethodMap = ->
    dblclick      : "dblClick"
    keyup         : "keyUp"
    keydown       : "keyDown"
    keypress      : "keyPress"
    mouseup       : "mouseUp"
    mousedown     : "mouseDown"
    mouseenter    : "mouseEnter"
    mouseleave    : "mouseLeave"
    mousemove     : "mouseMove"
    mousewheel    : "mouseWheel"
    wheel         : "mouseWheel"
    mouseover     : "mouseOver"
    contextmenu   : "contextMenu"
    dragstart     : "dragStart"
    dragenter     : "dragEnter"
    dragleave     : "dragLeave"
    dragover      : "dragOver"
    paste         : "paste"
    transitionend : "transitionEnd"


  overrideAndMergeObjects = (objects)->
    for own title,item of objects.overridden
      continue if objects.overrider[title]
      objects.overrider[title] = item
    objects.overrider

  appendToDomBody: ->
    @parentIsInDom = yes
    unless @lazy
      $("body").append @$()
      @utils.defer => @emit "viewAppended"

  @appendToDOMBody = (view) ->
    console.warn "KDView.appendToDOMBody is deprecated; use #appendToDomBody instead"
    view.appendToDomBody()

# #
# INSTANCE LEVEL
# #

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

    options.tagName           or= "div"     # a String of a HTML tag
    options.domId             or= null      # a String
    options.cssClass          or= ""        # a String
    options.parent            or= null      # a KDView Instance
    options.partial           or= null      # a String of HTML or text
    options.pistachio         or= null      # a String of Pistachio
    options.delegate          or= null      # a KDView Instance
    options.bind              or= ""        # a String of space separated javascript dom events to be listened
    options.draggable         or= null      # an Object holding draggable options and/or events !!! NOT HTML5 !!!
    options.size              or= null      # an Object holding width and height properties
    options.position          or= null      # an Object holding top/right/bottom/left properties (would force view to be positioned absolutely)
    options.attributes        or= null      # an Object holding attribute key/value pairs e.g. {href:'#',title:'my picture'}
    options.prefix            or= ""        # a String
    options.suffix            or= ""        # a String
    options.tooltip           or= null      # an Object of kdtooltip options
    options.lazyLoadThreshold  ?= no
    # TO BE IMPLEMENTED
    options.droppable         or= null      # TBDL
    options.resizable         or= null      # TBDL

    super options, data

    @getData()?.on? 'update', @bound 'render'

    @defaultInit()
    @devHacks()

  defaultInit:->

    options           = @getOptions()
    {@domId, @parent} = options
    @subViews         = []

    { cssClass, attributes, size, position
      partial, draggable, pistachio, pistachioParams
      lazyLoadThreshold, tooltip, draggable, tagName
    } = options

    @setDomElement cssClass
    @setDataId()
    @setDomId @domId          if @domId
    @setAttributes attributes if attributes
    @setPosition position     if position
    @updatePartial partial    if partial
    @setClass 'kddraggable'   if draggable

    @addEventHandlers options

    # TODO: I commented this out because it was firing before viewAppended
    #       I implemented the equivalent feature in JView, and I'm hoping
    #       sometime soon we can merge JView into KDView.  C.T.
    #
    # if options.pistachio
    #   @setTemplate options.pistachio, options.pistachioParams
    #   @template.update()

    @setLazyLoader lazyLoadThreshold  if lazyLoadThreshold
    @setTooltip tooltip               if tooltip
    @setDraggable draggable           if draggable

    @bindEvents()

    @on 'childAppended', @childAppended.bind this

    @on 'viewAppended', =>
      @setViewReady()
      @viewAppended()
      @childAppended this
      @parentIsInDom = yes

      fireViewAppended = (child)->
        unless child.parentIsInDom
          child.parentIsInDom = yes
          child.emit 'viewAppended'  unless child.lazy

      # temp fix for KDTreeView
      # subviews are stored in an object not in an array
      # hmm not really sth weirder going on...
      subViews = @getSubViews()
      if Array.isArray subViews
        fireViewAppended child for child in subViews
      else if subViews? and 'object' is typeof subViews
        fireViewAppended child for own key, child of subViews


  getDomId:-> @domElement.attr "id"


# #
# DOM ELEMENT CREATION
# #


  setDomElement:(cssClass='')->
    {domId, tagName} = @getOptions()

    if domId
      el = document.getElementById domId

    @lazy = unless el?
      el    = document.createElement tagName
      el.id = domId  if domId
      no
    else yes

    for klass in "kdview #{cssClass}".split ' ' when klass.length
      el.classList.add klass

    @domElement = $ el

    if @lazy
      # warn "lazyElement found with id #{domId}"
      @utils.defer => @emit 'viewAppended'

  setDomId:(id)->
    @domElement.attr "id",id

  setData: (data) ->
    @data?.off? 'update', @bound 'render'
    super data
    @data?.on? 'update', @bound 'render'
    @render()  if @parentIsInDom

  setDataId:->
    @domElement.data "data-id",@getId()

  getAttribute:(attr)->
    @getElement().getAttribute attr

  setAttribute:(attr, val)->
    @getElement().setAttribute attr, val

  setAttributes:(attributes)->
    @setAttribute attr, val for own attr, val of attributes

  isInDom:do ->
    findUltimateAncestor =(el)->
      ancestor = el
      while ancestor.parentNode
        ancestor = ancestor.parentNode
      ancestor
    -> findUltimateAncestor(@$()[0]).body?

# #
# TRAVERSE DOM ELEMENT
# #

  getDomElement:-> @domElement

  getElement:-> @getDomElement()[0]

  getTagName:-> @options.tagName || 'div'

  # shortcut method for @getDomElement()
  $:(selector)->
    if selector
    then @getDomElement().find(selector)
    else @getDomElement()

# #
# MANIPULATE DOM ELEMENT
# #

  # TODO: DRY these out.
  append:(child, selector)->
    @$(selector).append child.$()
    if @parentIsInDom
      child.emit 'viewAppended'
    this

  appendTo:(parent, selector)->
    @$().appendTo parent.$(selector)
    if @parentIsInDom
      @emit 'viewAppended'
    this

  appendToSelector:(selector)->
    $(selector).append @$()
    @emit 'viewAppended'

  prepend:(child, selector)->
    @$(selector).prepend child.$()
    if @parentIsInDom
      child.emit 'viewAppended'
    this

  prependTo:(parent, selector)->
    @$().prependTo parent.$(selector)
    if @parentIsInDom
      @emit 'viewAppended'
    this

  prependToSelector:(selector)->
    $(selector).prepend @$()
    @emit 'viewAppended'

  setPartial:(partial,selector)->
    @$(selector).append partial
    this

  updatePartial: (partial, selector) ->
    @$(selector).html partial

  clear:-> @getElement().innerHTML = ''

  # UPDATE PARTIAL EXPERIMENT TO NOT TO ORPHAN SUBVIEWS

  # updatePartial: (partial, selector) ->
  #   subViews = @getSubViews()
  #   subViewSelectors = for subView in subViews
  #     subView.$().parent().attr "class"
  #
  #   @$(selector).html partial
  #
  #   for subView,i in subViews
  #     @$(subViewSelectors[i]).append subView.$()


# #
# CSS METHODS
# #

  @setElementClass = (el, addOrRemove, cssClass)->
    el.classList[addOrRemove] cl for cl in cssClass.split(' ') when cl isnt ''

  setCss:(property, value)->

    @$().css property, value

  setStyle:(properties)->

    @$().css property, value for own property, value of properties

  setClass:(cssClass)->

    return unless cssClass
    KDView.setElementClass @getElement(), "add", cssClass
    return this

  unsetClass:(cssClass)->

    return unless cssClass
    KDView.setElementClass @getElement(), "remove", cssClass
    return this

  toggleClass:(cssClass)->
    @$().toggleClass cssClass
    return this

  hasClass:(cssClass)->
    @getElement().classList.contains cssClass

  getBounds:->
    #return false unless @viewDidAppend
    bounds =
      x : @getX()
      y : @getY()
      w : @getWidth()
      h : @getHeight()
      n : @constructor.name

  setRandomBG:->@getDomElement().css "background-color", KD.utils.getRandomRGB()

  hide:(duration)->
    @setClass 'hidden'
    # @$().hide duration
    #@getDomElement()[0].style.display = "none"

  show:(duration)->
    @unsetClass 'hidden'
    # @$().show duration
    #@getDomElement()[0].style.display = "block"

  setPosition:->
    positionOptions = @getOptions().position
    positionOptions.position = "absolute"
    @$().css positionOptions

  getWidth:-> @$().outerWidth no

  setWidth:(w, unit = "px")->
    @getElement().style.width = "#{w}#{unit}"
    @emit "ViewResized", {newWidth : w, unit}

  getHeight:->
    @getDomElement().outerHeight no

  setHeight:(h, unit = "px")->
    @getElement().style.height = "#{h}#{unit}"
    @emit "ViewResized", {newHeight : h, unit}

  setX:(x)-> @$().css left : x
  setY:(y)-> @$().css top : y
  getX:-> @$().offset().left
  getY:-> @$().offset().top
  getRelativeX:-> @$().position().left
  getRelativeY:-> @$().position().top

  destroyChild: (prop) ->
    if @[prop]?
      @[prop].destroy?()
      delete @[prop]
      yes
    else no

# #
# ADD/DESTROY VIEW INSTANCES
# #
  detach: ->
    @parent.getElement().removeChild @getElement()

  attach:(view) ->
    @getElement().appendChild view.getElement()

  detach: ->
    @parent?.getElement().removeChild @getElement()

  destroy: ->

    # good idea but needs some refactoring see KDObject::destroy
    # return if @isDestroyed

    # instance destroys own subviews<commit_msg>KDView: Probably a merge conflict fix<commit_after>KDObject      = require './object.coffee'

module.exports = class KDView extends KDObject

# #
# CLASS LEVEL STUFF
# #

  {defineProperty} = Object

  deprecated = (methodName)-> warn "#{methodName} is deprecated from KDView if you need it override in your subclass"

  eventNames =
    ///
    ^(
    (dbl)?click|
    key(up|down|press)|
    mouse(up|down|over|enter|leave|move)|
    drag(start|end|enter|leave|over)|
    blur|change|focus|
    drop|
    contextmenu|
    scroll|
    paste|
    error|
    load
    )$
    ///

  eventToMethodMap = ->
    dblclick      : "dblClick"
    keyup         : "keyUp"
    keydown       : "keyDown"
    keypress      : "keyPress"
    mouseup       : "mouseUp"
    mousedown     : "mouseDown"
    mouseenter    : "mouseEnter"
    mouseleave    : "mouseLeave"
    mousemove     : "mouseMove"
    mousewheel    : "mouseWheel"
    wheel         : "mouseWheel"
    mouseover     : "mouseOver"
    contextmenu   : "contextMenu"
    dragstart     : "dragStart"
    dragenter     : "dragEnter"
    dragleave     : "dragLeave"
    dragover      : "dragOver"
    paste         : "paste"
    transitionend : "transitionEnd"


  overrideAndMergeObjects = (objects)->
    for own title,item of objects.overridden
      continue if objects.overrider[title]
      objects.overrider[title] = item
    objects.overrider

  appendToDomBody: ->
    @parentIsInDom = yes
    unless @lazy
      $("body").append @$()
      @utils.defer => @emit "viewAppended"

  @appendToDOMBody = (view) ->
    console.warn "KDView.appendToDOMBody is deprecated; use #appendToDomBody instead"
    view.appendToDomBody()

# #
# INSTANCE LEVEL
# #

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

    options.tagName           or= "div"     # a String of a HTML tag
    options.domId             or= null      # a String
    options.cssClass          or= ""        # a String
    options.parent            or= null      # a KDView Instance
    options.partial           or= null      # a String of HTML or text
    options.pistachio         or= null      # a String of Pistachio
    options.delegate          or= null      # a KDView Instance
    options.bind              or= ""        # a String of space separated javascript dom events to be listened
    options.draggable         or= null      # an Object holding draggable options and/or events !!! NOT HTML5 !!!
    options.size              or= null      # an Object holding width and height properties
    options.position          or= null      # an Object holding top/right/bottom/left properties (would force view to be positioned absolutely)
    options.attributes        or= null      # an Object holding attribute key/value pairs e.g. {href:'#',title:'my picture'}
    options.prefix            or= ""        # a String
    options.suffix            or= ""        # a String
    options.tooltip           or= null      # an Object of kdtooltip options
    options.lazyLoadThreshold  ?= no
    # TO BE IMPLEMENTED
    options.droppable         or= null      # TBDL
    options.resizable         or= null      # TBDL

    super options, data

    @getData()?.on? 'update', @bound 'render'

    @defaultInit()
    @devHacks()

  defaultInit:->

    options           = @getOptions()
    {@domId, @parent} = options
    @subViews         = []

    { cssClass, attributes, size, position
      partial, draggable, pistachio, pistachioParams
      lazyLoadThreshold, tooltip, draggable, tagName
    } = options

    @setDomElement cssClass
    @setDataId()
    @setDomId @domId          if @domId
    @setAttributes attributes if attributes
    @setPosition position     if position
    @updatePartial partial    if partial
    @setClass 'kddraggable'   if draggable

    @addEventHandlers options

    # TODO: I commented this out because it was firing before viewAppended
    #       I implemented the equivalent feature in JView, and I'm hoping
    #       sometime soon we can merge JView into KDView.  C.T.
    #
    # if options.pistachio
    #   @setTemplate options.pistachio, options.pistachioParams
    #   @template.update()

    @setLazyLoader lazyLoadThreshold  if lazyLoadThreshold
    @setTooltip tooltip               if tooltip
    @setDraggable draggable           if draggable

    @bindEvents()

    @on 'childAppended', @childAppended.bind this

    @on 'viewAppended', =>
      @setViewReady()
      @viewAppended()
      @childAppended this
      @parentIsInDom = yes

      fireViewAppended = (child)->
        unless child.parentIsInDom
          child.parentIsInDom = yes
          child.emit 'viewAppended'  unless child.lazy

      # temp fix for KDTreeView
      # subviews are stored in an object not in an array
      # hmm not really sth weirder going on...
      subViews = @getSubViews()
      if Array.isArray subViews
        fireViewAppended child for child in subViews
      else if subViews? and 'object' is typeof subViews
        fireViewAppended child for own key, child of subViews


  getDomId:-> @domElement.attr "id"


# #
# DOM ELEMENT CREATION
# #


  setDomElement:(cssClass='')->
    {domId, tagName} = @getOptions()

    if domId
      el = document.getElementById domId

    @lazy = unless el?
      el    = document.createElement tagName
      el.id = domId  if domId
      no
    else yes

    for klass in "kdview #{cssClass}".split ' ' when klass.length
      el.classList.add klass

    @domElement = $ el

    if @lazy
      # warn "lazyElement found with id #{domId}"
      @utils.defer => @emit 'viewAppended'

  setDomId:(id)->
    @domElement.attr "id",id

  setData: (data) ->
    @data?.off? 'update', @bound 'render'
    super data
    @data?.on? 'update', @bound 'render'
    @render()  if @parentIsInDom

  setDataId:->
    @domElement.data "data-id",@getId()

  getAttribute:(attr)->
    @getElement().getAttribute attr

  setAttribute:(attr, val)->
    @getElement().setAttribute attr, val

  setAttributes:(attributes)->
    @setAttribute attr, val for own attr, val of attributes

  isInDom:do ->
    findUltimateAncestor =(el)->
      ancestor = el
      while ancestor.parentNode
        ancestor = ancestor.parentNode
      ancestor
    -> findUltimateAncestor(@$()[0]).body?

# #
# TRAVERSE DOM ELEMENT
# #

  getDomElement:-> @domElement

  getElement:-> @getDomElement()[0]

  getTagName:-> @options.tagName || 'div'

  # shortcut method for @getDomElement()
  $:(selector)->
    if selector
    then @getDomElement().find(selector)
    else @getDomElement()

# #
# MANIPULATE DOM ELEMENT
# #

  # TODO: DRY these out.
  append:(child, selector)->
    @$(selector).append child.$()
    if @parentIsInDom
      child.emit 'viewAppended'
    this

  appendTo:(parent, selector)->
    @$().appendTo parent.$(selector)
    if @parentIsInDom
      @emit 'viewAppended'
    this

  appendToSelector:(selector)->
    $(selector).append @$()
    @emit 'viewAppended'

  prepend:(child, selector)->
    @$(selector).prepend child.$()
    if @parentIsInDom
      child.emit 'viewAppended'
    this

  prependTo:(parent, selector)->
    @$().prependTo parent.$(selector)
    if @parentIsInDom
      @emit 'viewAppended'
    this

  prependToSelector:(selector)->
    $(selector).prepend @$()
    @emit 'viewAppended'

  setPartial:(partial,selector)->
    @$(selector).append partial
    this

  updatePartial: (partial, selector) ->
    @$(selector).html partial

  clear:-> @getElement().innerHTML = ''

  # UPDATE PARTIAL EXPERIMENT TO NOT TO ORPHAN SUBVIEWS

  # updatePartial: (partial, selector) ->
  #   subViews = @getSubViews()
  #   subViewSelectors = for subView in subViews
  #     subView.$().parent().attr "class"
  #
  #   @$(selector).html partial
  #
  #   for subView,i in subViews
  #     @$(subViewSelectors[i]).append subView.$()


# #
# CSS METHODS
# #

  @setElementClass = (el, addOrRemove, cssClass)->
    el.classList[addOrRemove] cl for cl in cssClass.split(' ') when cl isnt ''

  setCss:(property, value)->

    @$().css property, value

  setStyle:(properties)->

    @$().css property, value for own property, value of properties

  setClass:(cssClass)->

    return unless cssClass
    KDView.setElementClass @getElement(), "add", cssClass
    return this

  unsetClass:(cssClass)->

    return unless cssClass
    KDView.setElementClass @getElement(), "remove", cssClass
    return this

  toggleClass:(cssClass)->
    @$().toggleClass cssClass
    return this

  hasClass:(cssClass)->
    @getElement().classList.contains cssClass

  getBounds:->
    #return false unless @viewDidAppend
    bounds =
      x : @getX()
      y : @getY()
      w : @getWidth()
      h : @getHeight()
      n : @constructor.name

  setRandomBG:->@getDomElement().css "background-color", KD.utils.getRandomRGB()

  hide:(duration)->
    @setClass 'hidden'
    # @$().hide duration
    #@getDomElement()[0].style.display = "none"

  show:(duration)->
    @unsetClass 'hidden'
    # @$().show duration
    #@getDomElement()[0].style.display = "block"

  setPosition:->
    positionOptions = @getOptions().position
    positionOptions.position = "absolute"
    @$().css positionOptions

  getWidth:-> @$().outerWidth no

  setWidth:(w, unit = "px")->
    @getElement().style.width = "#{w}#{unit}"
    @emit "ViewResized", {newWidth : w, unit}

  getHeight:->
    @getDomElement().outerHeight no

  setHeight:(h, unit = "px")->
    @getElement().style.height = "#{h}#{unit}"
    @emit "ViewResized", {newHeight : h, unit}

  setX:(x)-> @$().css left : x
  setY:(y)-> @$().css top : y
  getX:-> @$().offset().left
  getY:-> @$().offset().top
  getRelativeX:-> @$().position().left
  getRelativeY:-> @$().position().top

  destroyChild: (prop) ->
    if @[prop]?
      @[prop].destroy?()
      delete @[prop]
      yes
    else no

# #
# ADD/DESTROY VIEW INSTANCES
# #

  detach: ->
    @parent?.getElement().removeChild @getElement()

  destroy: ->

    # good idea but needs some refactoring see KDObject::destroy
    # return if @isDestroyed

    # instance destroys own subviews
{'fraction_non_alphanumeric': 0.12224310157254475, 'fraction_numerical': 0.000494510928691524, 'mean_word_length': 2.568554790894653, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 127, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6846891', 'n_tokens_mistral': 6446, 'n_tokens_neox': 6190, 'n_words': 1628}