/* ========================================================================
 * Avvo UI - collapse.js
 * ========================================================================
 * Forked from Bootstrap collapse.js v3.3.1
 * http://getbootstrap.com/javascript/#collapse
 * ========================================================================
 * Copyright 2011-2014 Twitter, Inc.
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 * ======================================================================== */

const $ = global.jQuery

// COLLAPSE PUBLIC CLASS DEFINITION
// ================================

const Collapse = function (element, options) {
  this.$element      = $(element)
  this.options       = $.extend({}, Collapse.DEFAULTS, options)
  this.$toggler      = $(this.options.toggler).filter(`[href="#${element.id}"], [data-target="#${element.id}"]`)
  this.transitioning = null
  this.togglerContentCollapsed = null
  this.togglerContentExpanded  = null

  this.setTogglerStates()

  if (this.options.parent) {
    this.$parent = this.getParent()
  } else {
    this.addAriaAndCollapsedClass(this.$element, this.$toggler)
  }

  if (this.options.toggle) {
    this.toggle()
  }
}

Collapse.VERSION  = '4.0.0'

Collapse.TRANSITION_DURATION = 350

Collapse.DEFAULTS = {
  parent:      null,
  toggle:      true,
  toggler:     '[data-toggle="collapse"]',
  toggledText: null,
}

function getTargetFromToggler($toggler) {
  const target = $toggler.attr('data-target') || $toggler.attr('href')

  return $(target)
}

function isExpanded($element) {
  return $element.hasClass('in')
}

function forceRedraw(el) {
  // Reading "offsetHeight" forces the browser to redraw the element.
  return el.offsetHeight
}

Collapse.prototype.dimension = function () {
  const hasWidth = this.$element.hasClass('width')
  return hasWidth ? 'width' : 'height'
}

Collapse.prototype.show = function () {
  if (this.transitioning || isExpanded(this.$element)) {
    return
  }

  let activesData
  const $actives = this.$parent && this.$parent.find('.in, .collapsing')

  if ($actives && $actives.length) {
    activesData = $actives.data('ui.collapse')
    if (activesData && activesData.transitioning) {
      return
    }
  }

  const startEvent = $.Event('show.ui.collapse')
  this.$element.trigger(startEvent)
  if (startEvent.isDefaultPrevented()) {
    return
  }

  if ($actives && $actives.length) {
    Plugin.call($actives, 'hide')
    if (!activesData) {
      $actives.data('ui.collapse', null)
    }
  }

  const dimension = this.dimension()

  this.$element
    .removeClass('collapse')
    .addClass('collapsing')
    [dimension](0)
    .attr('aria-expanded', true)

  this.$toggler
    .html(this.togglerContentExpanded)
    .removeClass('collapsed')
    .attr('aria-expanded', true)

  this.transitioning = 1

  const complete = function () {
    this.$element
      .removeClass('collapsing')
      .addClass('collapse in')
      [dimension]('')
    this.transitioning = 0
    this.$element.trigger('shown.ui.collapse')
  }

  if (!$.support.transition) {
    return complete.call(this)
  }

  const scrollSize = $.camelCase(['scroll', dimension].join('-'))

  this.$element
    .one('uiTransitionEnd', $.proxy(complete, this))
    .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize])
}

Collapse.prototype.hide = function () {
  if (this.transitioning || !isExpanded(this.$element)) {
    return
  }

  const startEvent = $.Event('hide.ui.collapse')
  this.$element.trigger(startEvent)
  if (startEvent.isDefaultPrevented()) {
    return
  }

  const dimension = this.dimension()

  // Hardcoding the element's dimension ensures there's a value to animate
  this.$element[dimension](this.$element[dimension]())
  forceRedraw(this.$element[0])

  this.$element
    .addClass('collapsing')
    .removeClass('collapse in')
    .attr('aria-expanded', false)

  this.$toggler
    .html(this.togglerContentCollapsed)
    .addClass('collapsed')
    .attr('aria-expanded', false)

  this.transitioning = 1

  const complete = function () {
    this.transitioning = 0
    this.$element
      .removeClass('collapsing')
      .addClass('collapse')
      .trigger('hidden.ui.collapse')
  }

  if (!$.support.transition) {
    return complete.call(this)
  }

  this.$element[dimension](0)
    .one('uiTransitionEnd', $.proxy(complete, this))
    .emulateTransitionEnd(Collapse.TRANSITION_DURATION)
}

Collapse.prototype.toggle = function () {
  const action = isExpanded(this.$element) ? 'hide' : 'show'
  this[action]()
}

Collapse.prototype.getParent = function () {
  return $(this.options.parent)
    .find(`[data-toggle="collapse"][data-parent="${this.options.parent}"]`)
    .each($.proxy(function (i, element) {
      const $element = $(element)
      this.addAriaAndCollapsedClass(getTargetFromToggler($element), $element)
    }, this))
    .end()
}

Collapse.prototype.addAriaAndCollapsedClass = function ($element, $toggler) {
  const isOpen = isExpanded($element)

  $element.attr('aria-expanded', isOpen)
  $toggler
    .toggleClass('collapsed', !isOpen)
    .attr('aria-expanded', isOpen)
}

Collapse.prototype.setTogglerStates = function () {
  const isCollapsed = !isExpanded(this.$element)
  const current = isCollapsed ? 'togglerContentCollapsed' : 'togglerContentExpanded'
  const toggled = isCollapsed ? 'togglerContentExpanded' : 'togglerContentCollapsed'

  this[current] = this.$toggler.html()
  this[toggled] = this.getToggledTogglerContent()
}

Collapse.prototype.getToggledTogglerContent = function () {
  if (!this.$toggler.html()) return ''
  if (this.options.toggledText) return this.options.toggledText

  if (!isExpanded(this.$element)) {
    return this.$toggler.html()
      .replace('More', 'Less')
      .replace('more', 'less')
      .replace('chevron-down', 'chevron-up')
  }

  return this.$toggler.html()
    .replace('Less', 'More')
    .replace('less', 'more')
    .replace('chevron-up', 'chevron-down')
}

// COLLAPSE PLUGIN DEFINITION
// ==========================

function Plugin(option) {
  return this.each(function () {
    const $this   = $(this)
    let data    = $this.data('ui.collapse')
    const options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option === 'object' && option)

    if (!data && options.toggle && option === 'show') {
      options.toggle = false
    }
    if (!data) {
      data = new Collapse(this, options)
      $this.data('ui.collapse', data)
    }
    if (typeof option === 'string') {
      data[option]()
    }
  })
}

export function init() {
  const old = $.fn.collapse

  $.fn.collapse             = Plugin
  $.fn.collapse.Constructor = Collapse

  // COLLAPSE NO CONFLICT
  // ====================

  $.fn.collapse.noConflict = function () {
    $.fn.collapse = old
    return this
  }


  // COLLAPSE DATA-API
  // =================

  $(document).on('click.ui.collapse.data-api', '[data-toggle="collapse"]', function (event) {
    const $this   = $(this)

    if (!$this.attr('data-target')) {
      event.preventDefault()
    }

    const $target = getTargetFromToggler($this)
    const data    = $target.data('ui.collapse')
    const option  = data ? 'toggle' : $.extend({}, $this.data(), { toggler: this })

    Plugin.call($target, option)
  })
}
