app/assets/javascripts/components/uploader.js.coffee

Summary

Maintainability
Test Coverage
#= require jquery-fileupload/basic

uploaderTemplate = """
  <div class="uploader">
    <span class="upload-button button-primary"><%= buttonLabel %></span>
  </div>
"""

App.components.uploader = ->
  buttonLabel: I18n.uploader.select_image

  method: 'PATCH'

  attributes: ->
    _uploader = $ _.template(uploaderTemplate)(buttonLabel: @buttonLabel)
    {
      field:    @container.closest('.field')
      uploader: _uploader
      button:   _uploader.find('.upload-button')
    }

  initialize: ->
    @render()
    @bindEvents()
    @afterInitialize?()
    @startPlugin()

  bindEvents: ->
    @on @attr.button, 'click', (evt) => @container.trigger 'click'

  render: ->
    @attr.field.hide()
    @attr.field.after @attr.uploader

  startPlugin: ->
    @container.fileupload
      url:      @attr.url
      type:     @method
      dataType: 'json'
      add:      @onAdd.bind(this)
      done:     @onDone.bind(this)
      fail:     @onFail.bind(this)

  onAdd: (evt, data) ->
    @attr.uploader.find('.error').remove()
    @attr.button.text I18n.uploader.uploading
    @attr.button.append "<i class=\"fa fa-spinner fa-spin\"></i>"
    data.submit()

  onDone: (e, data) ->
    setTimeout( =>
      @reloadImage(data.result)
      App.utils.flashMessage(data.result.flash)
      @reset()
    , 200)

  onFail: (evt, data) ->
    @attr.button.after "<span class='error'>#{ @getErrorMsg(data) }</span>"
    @reset()

  reloadImage: (res) ->
    img = $(".avatar img")
    img.fadeOut ->
      img.attr("src", res.avatar + '?' + (new Date()).getTime()).fadeIn()

  reset: ->
    @attr.button.find('i').remove()
    @attr.button.html @buttonLabel
    @startPlugin() # restart plugin (kludge for retrying uploads)

  getErrorMsg: (data) ->
    errors = data.jqXHR.responseJSON.errors
    err_msg = (errors[ @fieldName() ] || ['Error'])[0]

  # Extract the field name.
  # E.g: <input name="user[avatar]" />  => "avatar"
  fieldName: ->
    match = @container.attr('name').match(/\[(.*?)\]/)
    if match.length > 1 then match[1] else null