file-upload.coffee 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. Spine = require('spine/core')
  2. $ = Spine.$
  3. base64 = require('base64')
  4. class FileUploadUI extends Spine.Controller
  5. tag: 'div'
  6. className: 'ui-file-upload'
  7. fieldName: 'file_upload'
  8. selectedFieldName: 'photo'
  9. dropzoneText: 'drop or click'
  10. attachments: {}
  11. docId: null
  12. selectedFile: null
  13. changeCallback: null
  14. events:
  15. 'click ul.files-list > li': 'itemClick'
  16. 'dblclick ul.files-list > li': 'itemDblClick'
  17. constructor: ->
  18. super
  19. @render()
  20. @bind 'change', @changeCallback
  21. render: ->
  22. @dropzone = $("<div class=\"dropzone\">#{@dropzoneText}</div>")
  23. @fileInput = $("<input type=\"file\" name=\"#{@fieldName}\" style=\"display: none;\">")
  24. @fileSelectedInput = $("<input type=\"hidden\" name=\"#{@selectedFieldName}\">")
  25. @fileName = $("<div class=\"filename\"/>")
  26. @filesList = $("<ul class=\"files-list\"/>")
  27. @el.append @dropzone, @fileInput, @fileSelectedInput, @fileName, @filesList
  28. @setupList()
  29. @setupZoneEvents()
  30. setupList: ->
  31. names = (prop for prop of @attachments)
  32. for name in names
  33. file = @attachments[name]
  34. @addToList(name, file.content_type)
  35. if @selectedFile
  36. item = @filesList.find("> li[data-filename='#{@selectedFile}']")
  37. @selectItem(item) if item
  38. addToList: (name, type, dataURL = null) =>
  39. $item = $('<li/>')
  40. url = if @docId then "/file/#{@docId}/#{name}" else ''
  41. $item.attr('data-filename', name)
  42. .attr('data-url', url)
  43. if type.match(/image.*/)
  44. $img = $('<img>')
  45. if dataURL
  46. $img.attr('src', dataURL)
  47. else if @docId
  48. $img.attr('src', url)
  49. $item.append $img
  50. else
  51. $item.append $("<div><em>#{type}</em><br>#{name}</div>")
  52. @filesList.append $item
  53. # Also select the item if it is the only one
  54. @selectItem($item) if @attachments.length is 1
  55. # Trigger change event
  56. @trigger 'change'
  57. itemClick: (e) ->
  58. # Selecting the main photo
  59. e.preventDefault()
  60. e.stopPropagation()
  61. item = e.currentTarget
  62. if $(item).hasClass('selected')
  63. @resetSelection('Nothing is selected')
  64. else
  65. @selectItem(item)
  66. # Trigger change event
  67. @trigger 'change'
  68. itemDblClick: (e) ->
  69. # Removing an attachment
  70. e.preventDefault()
  71. e.stopPropagation()
  72. @resetSelection()
  73. @removeItem(e.currentTarget)
  74. selectItem: (item) =>
  75. @resetSelection()
  76. if item
  77. name = $(item).attr('data-filename')
  78. $(item).addClass('selected')
  79. @fileName.html "Selected: #{name}"
  80. @fileSelectedInput.val(name)
  81. resetSelection: (msg = '') ->
  82. @filesList.children().removeClass('selected')
  83. @fileName.html msg
  84. @fileSelectedInput.val('')
  85. removeItem: (item) =>
  86. if item
  87. name = $(item).attr('data-filename')
  88. if confirm "Remove this attachment?\n#{name}"
  89. delete @attachments[name]
  90. $(item).remove()
  91. @fileSelectedInput.val('') if @fileSelectedInput.val() is name
  92. @fileName.html "#{name} was removed"
  93. # Trigger change event
  94. @trigger 'change'
  95. setupZoneEvents: ->
  96. @dropzone.on 'dragenter dragover', (e) ->
  97. e.originalEvent.preventDefault()
  98. e.originalEvent.stopPropagation()
  99. e.originalEvent.dataTransfer.dropEffect = 'copy'
  100. @dropzone.on 'drop', (e) =>
  101. e.originalEvent.preventDefault()
  102. e.originalEvent.stopPropagation()
  103. @addFiles e.originalEvent.dataTransfer.files
  104. @dropzone.on 'click', (e) =>
  105. e.preventDefault()
  106. @fileInput.click()
  107. @fileInput.on 'change', (e) =>
  108. @addFiles e.target.files
  109. addFiles: (files) =>
  110. if files.length
  111. file = files[0]
  112. name = file.name.replace(/[\ \'\"]/g,'-')
  113. name = encodeURIComponent(name)
  114. type = file.type
  115. reader = new FileReader
  116. reader.addEventListener 'load', (e) =>
  117. dataURL = e.target.result
  118. # Since the result is a data URL and already base64 encoded
  119. # then just remove the first meta info and we get the file
  120. # data the we need to save into _attachments in CouchDB
  121. result = dataURL.replace(/^data:.*;base64,/, '')
  122. @attachments[name] =
  123. content_type: type
  124. data: result
  125. @addToList(name, type, dataURL)
  126. @fileName.html name
  127. reader.addEventListener 'loadstart', (e) =>
  128. @fileName.html "In progress"
  129. reader.addEventListener 'progress', (e) =>
  130. @fileName.html @fileName.html() + "."
  131. reader.addEventListener 'error', (e) =>
  132. @fileName.html "Couldn't load the file."
  133. reader.readAsDataURL file
  134. module.exports = FileUploadUI