$ ->
  # This file integrates contenteditable for string attributes.

  onKey = (event) ->
    cmsField = $(event.currentTarget)
    key = event.keyCode || event.which

    switch key
      when 13 # Enter
        if !isNewlineAllowed(cmsField)
          event.preventDefault()
          cmsField.blur()
      when 27 # Esc
        event.stopPropagation()
        cmsField.blur()

  onInput = (event) ->
    cmsField = $(event.currentTarget)
    save(cmsField)

  onFocus = (event) ->
    cmsField = $(event.currentTarget)
    prepareForEditing(cmsField)

  onBlur = (event) ->
    cmsField = $(event.currentTarget)

    save(cmsField).done ->
      cmsField.trigger('scrivito_editors:blur')

    finishEditing(cmsField)

  save = (cmsField) ->
    content = getCurrentContent(cmsField)

    # Save only if the content has changed.
    if content != cmsField.scrivito('content')
      cmsField.scrivito('save', content).done ->
        cmsField.trigger('save.scrivito_editors')
    else
      $.Deferred().resolve()

  getCurrentContent = (cmsField) ->
    cleanUp(cmsField)

    clone = cmsFieldAndPastedContent(cmsField).clone()
    # Starting with empty element, entering 3 lines, per browser:
    # Chrome: 1<div>2</div><div>3</div>
    # IE: <p>1</p><p>2</p><p>3</p>
    # Firefox: 1<br>2<br>3<br>
    clone.find('div:not(:has(br)),p:not(:first)').before('\n')
    clone.find('br').replaceWith('\n')
    content = clone.text()
    clone.remove()
    content

  cleanUp = (cmsField) ->
    siblings = cmsFieldAndPastedContent(cmsField)
    pasted = siblings.not(cmsField)
    if pasted.length > 0
      pasted.remove()
      cmsField.text(siblings.text())

  cmsFieldAndPastedContent = (cmsField) ->
    siblingsBefore = cmsField.data('scrivito_editors_siblings_before_edit')
    siblings = cmsField.siblings()
    needsReset = !siblingsBefore || siblings.filter(siblingsBefore).length < siblingsBefore.length
    if needsReset
      cmsField.data('scrivito_editors_siblings_before_edit', siblings)
    else
      cmsField.siblings().addBack().not(siblingsBefore)

  _deprecationLogged = false

  isNewlineAllowed = (cmsField) ->
    if cmsField.data('editor') == 'text'
      if !_deprecationLogged
        console.warn 'Scrivito editors: data-editor="text" is deprecated, please use data-editor="string" data-newlines="true" instead.'
        _deprecationLogged = true
      return true

    return true if cmsField.data('newlines') == true
    return false if cmsField.data('newlines') == false
    cmsField.css('white-space').match /pre/

  prepareForEditing = (cmsField) ->
    if isNewlineAllowed(cmsField) && !cmsField.data('scrivito_editors_prepared_for_editing')
      cmsField.data('scrivito_editors_prepared_for_editing', true)
      html = cmsField.html()
      htmlNl2Br = html.replace(/\n/g, '<br />')
      if html != htmlNl2Br
        cmsField.html(htmlNl2Br)

  finishEditing = (cmsField) ->
    cmsField.data('scrivito_editors_prepared_for_editing', false)
    cmsField.text(cmsField.scrivito('content'))

  DOUBLE_CLICK_MS = 300

  editSelector = '''
    [data-scrivito-field-type="string"]:not([data-editor]),
    [data-editor~="string"],
    [data-editor~="text"]
  '''

  initialize = ->
    $(':root').on 'mouseenter', editSelector, (event) ->
      cmsField = $(@)

      unless cmsField.attr('contenteditable')?
        cmsField
          .attr('contenteditable', true)
          .blur(onBlur)
          .focus(onFocus)
          .keypress(onKey)
          .keyup(onKey)

        if cmsField.attr('data-autosave') != 'false'
          cmsField.on('cut input keypress keyup paste', onInput)

      prepareForEditing(cmsField)

    $(':root').on 'click', editSelector, (event) ->
      cmsField = $(@)

      cmsField.attr('contenteditable', true)
      cleanUp(cmsField)

      # Prevent editable link strings from being opened on click, but allow an enforcing double click
      unless event.timeStamp - cmsField.data('scrivito_editors_last_click') < DOUBLE_CLICK_MS
        event.preventDefault()
      cmsField.data('scrivito_editors_last_click', event.timeStamp)

  scrivito.on 'load', ->
    if scrivito.in_editable_view()
      initialize()