<commit_before>namespace 'Exo.Views', (exports) ->
  class exports.Modal extends Exo.View
    name: "modal"

    className: "modal"

    # @todo all of these
    width: null
    height: "auto"
    appendTo: document.body
    modal: true
    visible: false
    scrollable: true
    zIndex: 1024
    destroyOnHide: false

    headerSelector:  "> .modal-header"
    sidebarSelect:   "> .modal-sidebar"
    contentSelector: "> .modal-content"
    footerSelector:  "> .modal-footer"

    initialize: ->
      # @param content [String, Backbone.View] optional content to append to modal-content
      @content ||= null
      
      # @todo
      @dimensions ||= { width: null, height: null }

    positionElement: ->
      headerHeight = @$header.outerHeight()

      setHeight = (height) =>
        @dimensions.height = height
        @$el.height height

        @$content.css(position: "absolute")
        @$footer.css(position: "absolute")

        @$content.add(@$sidebar).css
          top: headerHeight
          bottom: @$footer.outerHeight()

      if @height == "auto"
        @$el.css(height: "auto")
        @$content.css(position: "relative", top: 0)
        @$footer.css(position: "static")

        elHeight = @_detectHeightForPositioning()
        maxHeight = window.innerHeight - 160

        contentHeight = @$content.height()
        if (contentHeight + @$footer.height() + headerHeight) >= maxHeight
          setHeight maxHeight
        else if @$sidebar.length || contentHeight == 0
          setHeight elHeight

      else
        setHeight @height

      if @width == "auto"
        @$el.css(width: "auto")
        @$content.css(position: "relative")
        @dimensions.width = @$el.width()
      else if @width
        @dimensions.width = @width
        @$el.width @width

      @$el.css(
        top: "80px"
        left: "50%"
        marginLeft: "-#{ @dimensions.width / 2 }px"
      )

      this

    getOverlay: ->
      @_overlay ||= $("<div class='modal-overlay'></div>").css(zIndex: @zIndex - 1).appendTo(document.body)

    scrollTo: (pos) ->
      # TODO support elements, += values, etc.
      @$("> .modal-content").scrollTop pos

    show: ->
      @render() unless @_renderCount

      @$el.appendTo(@appendTo) unless @el.parentNode == @appendTo

      @_boundPositionElement ||= _.bind(@positionElement, this)
      $(window).on "resize.modal-#{@cid}", @_boundPositionElement

      @visible = true
      @getOverlay().show() if @modal
      @$el.show()
      @positionElement()
      @trigger "show"

    hide: (e) ->
      if e
        e.preventDefault()
        e.stopPropagation()

      $(window).off "resize.modal-#{@cid}", @_boundPositionElement

      @visible = false
      @$el.hide()
      @getOverlay().hide()
      @trigger "hide"
      @destroy() if @destroyOnHide
      this

    toggle: ->
      if @visible
        @hide()
      else
        @show()

    render: ->
      super

      @$header  = @$(@headerSelector)
      @$content = @$(@contentSelector)
      @$sidebar = @$(@sidebarSelector)
      @$footer  = @$(@footerSelector)

      @$el.css(zIndex: @zIndex)
      @$el.addClass("not-scrollable") unless @scrollable

      @$el.addClass("with-footer") if @$footer.length
      @$el.addClass("with-sidebar") if @$sidebar.length

      if content = (@content?.el || @content)
        content.style.display = "block"
        @$content.append(content)

      return this

    remove: ->
      @_overlay?.remove()
      super

    # 
    # Private
    #
    
    _detectHeightForPositioning: ->
      @$el.height()

<commit_msg>Add KeyboardManager to Exo.Views.Modal, close on esc<commit_after>namespace 'Exo.Views', (exports) ->
  class exports.Modal extends Exo.View
    name: "modal"

    className: "modal"

    # @todo all of these
    width: null
    height: "auto"
    appendTo: document.body
    modal: true
    visible: false
    scrollable: true
    zIndex: 1024
    destroyOnHide: false
    escToClose: true

    headerSelector:  "> .modal-header"
    sidebarSelect:   "> .modal-sidebar"
    contentSelector: "> .modal-content"
    footerSelector:  "> .modal-footer"

    initialize: ->
      # @param content [String, Backbone.View] optional content to append to modal-content
      @content ||= null
      
      # @todo
      @dimensions ||= { width: null, height: null }

      @keyboardManager ||= new Exo.KeyboardManager(self)

    positionElement: ->
      headerHeight = @$header.outerHeight()

      setHeight = (height) =>
        @dimensions.height = height
        @$el.height height

        @$content.css(position: "absolute")
        @$footer.css(position: "absolute")

        @$content.add(@$sidebar).css
          top: headerHeight
          bottom: @$footer.outerHeight()

      if @height == "auto"
        @$el.css(height: "auto")
        @$content.css(position: "relative", top: 0)
        @$footer.css(position: "static")

        elHeight = @_detectHeightForPositioning()
        maxHeight = window.innerHeight - 160

        contentHeight = @$content.height()
        if (contentHeight + @$footer.height() + headerHeight) >= maxHeight
          setHeight maxHeight
        else if @$sidebar.length || contentHeight == 0
          setHeight elHeight

      else
        setHeight @height

      if @width == "auto"
        @$el.css(width: "auto")
        @$content.css(position: "relative")
        @dimensions.width = @$el.width()
      else if @width
        @dimensions.width = @width
        @$el.width @width

      @$el.css(
        top: "80px"
        left: "50%"
        marginLeft: "-#{ @dimensions.width / 2 }px"
      )

      this

    handleKeyUp: (key, e) ->
      switch key
        when "esc"
          @hide() if @escToClose
      false

    getOverlay: ->
      @_overlay ||= $("<div class='modal-overlay'></div>").css(zIndex: @zIndex - 1).appendTo(document.body)

    scrollTo: (pos) ->
      # TODO support elements, += values, etc.
      @$("> .modal-content").scrollTop pos

    show: ->
      @render() unless @_renderCount

      @$el.appendTo(@appendTo) unless @el.parentNode == @appendTo

      @_boundPositionElement ||= _.bind(@positionElement, this)
      $(window).on "resize.modal-#{@cid}", @_boundPositionElement

      @visible = true
      @getOverlay().show() if @modal
      @$el.show()
      @positionElement()
      @keyboardManager.nominate(this)
      @trigger "show"

    hide: (e) ->
      if e
        e.preventDefault()
        e.stopPropagation()

      $(window).off "resize.modal-#{@cid}", @_boundPositionElement

      @visible = false
      @$el.hide()
      @getOverlay().hide()
      @trigger "hide"
      @destroy() if @destroyOnHide
      this

    toggle: ->
      if @visible
        @hide()
      else
        @show()

    render: ->
      super

      @$header  = @$(@headerSelector)
      @$content = @$(@contentSelector)
      @$sidebar = @$(@sidebarSelector)
      @$footer  = @$(@footerSelector)

      @$el.css(zIndex: @zIndex)
      @$el.addClass("not-scrollable") unless @scrollable

      @$el.addClass("with-footer") if @$footer.length
      @$el.addClass("with-sidebar") if @$sidebar.length

      if content = (@content?.el || @content)
        content.style.display = "block"
        @$content.append(content)

      return this

    remove: ->
      @_overlay?.remove()
      super

    destroy: ->
      super
      @keyboardManager.revoke(this)

    # 
    # Private
    #
    
    _detectHeightForPositioning: ->
      @$el.height()


{'fraction_non_alphanumeric': 0.14701173959445038, 'fraction_numerical': 0.00400213447171825, 'mean_word_length': 2.21483704974271, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 45, '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': '3099991', 'n_tokens_mistral': 2379, 'n_tokens_neox': 2287, 'n_words': 498}