essays.coffee 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. Spine = require('spine/core')
  2. $ = Spine.$
  3. templates = require('duality/templates')
  4. utils = require('lib/utils')
  5. MultiSelectUI = require('controllers/ui/multi-select')
  6. FileUploadUI = require('controllers/ui/file-upload')
  7. PreviewUI = require('controllers/ui/preview')
  8. Essay = require('models/essay')
  9. Author = require('models/author')
  10. Collection = require('models/collection')
  11. Sponsor = require('models/sponsor')
  12. Site = require('models/site')
  13. class EssayForm extends Spine.Controller
  14. className: 'essay form panel'
  15. elements:
  16. '.item-title': 'itemTitle'
  17. '.error-message': 'errorMessage'
  18. 'form': 'form'
  19. 'select[name=site]': 'formSite'
  20. 'select[name=author_id]': 'formAuthorId'
  21. 'select[name=sponsor_id]': 'formSponsorId'
  22. 'input[name=title]': 'formTitle'
  23. 'input[name=published]': 'formPublished'
  24. 'textarea[name=intro]': 'formIntro'
  25. 'textarea[name=body]': 'formBody'
  26. '.collections-list': 'collectionsList'
  27. '.upload-ui': 'fileUploadContainer'
  28. '.save-button': 'saveButton'
  29. '.cancel-button': 'cancelButton'
  30. 'button.fullscreen-button': 'fullscreenButton'
  31. events:
  32. 'submit form': 'preventSubmit'
  33. 'change *[name]': 'markAsDirty'
  34. 'keyup *[name]': 'markAsDirty'
  35. 'click .save-button': 'save'
  36. 'click .cancel-button': 'cancel'
  37. 'click .delete-button': 'destroy'
  38. 'change select[name=site]': 'siteChange'
  39. 'blur input[name=slug]': 'updateSlug'
  40. 'click .fullscreen-button': 'fullscreen'
  41. constructor: ->
  42. super
  43. @active @render
  44. render: (params) ->
  45. @dirtyForm = false
  46. @editing = params.id?
  47. if @editing
  48. @copying = params.id.split('-')[0] is 'copy'
  49. if @copying
  50. @title = 'Copy Essay'
  51. @item = Essay.find(params.id.split('-')[1]).dup()
  52. # Important to indicate that we are creating a new record
  53. @editing = false
  54. else
  55. @item = Essay.find(params.id)
  56. @title = @item.name
  57. # Fetch missing data if need be
  58. if not @item.body?
  59. @item.ajax().reload {},
  60. success: =>
  61. @formBody.val(@item.body)
  62. @formIntro.val(@item.intro)
  63. else
  64. @title = 'New Essay'
  65. @item = {}
  66. @item.collections ?= []
  67. @item._attachments ?= {}
  68. @item.sites = Site.all().sort(Site.alphaSort)
  69. @item.sponsors = Sponsor.all().sort(Sponsor.alphaSort)
  70. @html templates.render('essay-form.html', {}, @item)
  71. @itemTitle.html @title
  72. # Set few initial form values
  73. if @editing or @copying
  74. @formSite.val(@item.site)
  75. @formSponsorId.val(@item.sponsor_id)
  76. @formPublished.prop('checked', @item.published)
  77. else
  78. @formSite.val(@stack.stack.filterBox.siteId)
  79. # @formPublished.prop('checked', true)
  80. @siteChange()
  81. # Files upload area
  82. @fileUploadUI = new FileUploadUI
  83. docId: @item.id
  84. selectedFile: @item.photo
  85. attachments: @item._attachments
  86. changeCallback: @markAsDirty
  87. @fileUploadContainer.html @fileUploadUI.el
  88. return @
  89. siteChange: ->
  90. $siteSelected = @formSite.parents('.field').find('.site-selected')
  91. site = Site.exists(@formSite.val())
  92. if site
  93. $siteSelected.html "<div class=\"site-name theme-#{site.theme}\">#{site.name_html}</div>"
  94. @makeAuthorsList(site)
  95. @makeCollectionsList(site)
  96. else
  97. $siteSelected.html ""
  98. makeAuthorsList: (site) ->
  99. authors = Author.findAllByAttribute('site', site.id).sort(Author.alphaSort)
  100. @formAuthorId.empty()
  101. .append "<option value=\"\">Select an author...</option>"
  102. for author in authors
  103. @formAuthorId.append "<option value=\"#{author.id}\">#{author.name}</option>"
  104. @formAuthorId.val(@item.author_id)
  105. makeCollectionsList: (site) ->
  106. collections = Collection.findAllByAttribute('site', site.id).sort(Collection.alphaSort)
  107. @collectionSelectUI = new MultiSelectUI
  108. items: collections
  109. selectedItems: (c.id for c in @item.collections)
  110. valueFields: ['id','slug']
  111. changeCallback: @markAsDirty
  112. @collectionsList.html @collectionSelectUI.el
  113. updateSlug: (e) =>
  114. slug = $(e.currentTarget)
  115. unless slug.val()
  116. slug.val utils.cleanSlug(@formTitle.val())
  117. fullscreen: (e) =>
  118. e?.preventDefault()
  119. @fullscreenButtonText ?= @fullscreenButton.html()
  120. if @form.hasClass('fullscreen')
  121. @form.removeClass('fullscreen')
  122. @fullscreenButton.html @fullscreenButtonText
  123. @previewUI?.close()
  124. else
  125. @form.addClass('fullscreen')
  126. @fullscreenButton.html "Exit #{@fullscreenButtonText}"
  127. @previewUI = new PreviewUI field: @formBody
  128. save: (e) ->
  129. e.preventDefault()
  130. if not navigator.onLine
  131. alert "Can not save. You are OFFLINE."
  132. return
  133. if @editing
  134. @item.fromForm(@form)
  135. else
  136. @item = new Essay().fromForm(@form)
  137. @item.collections = @collectionSelectUI.selected()
  138. @item._attachments = @fileUploadUI.attachments
  139. # Take care of some boolean checkboxes
  140. @item.published = @formPublished.is(':checked')
  141. # Save the item and make sure it validates
  142. if @item.save()
  143. @back()
  144. else
  145. msg = @item.validate()
  146. @showError msg
  147. return @
  148. showError: (msg) ->
  149. @errorMessage.html(msg).show()
  150. @el.scrollTop(0)
  151. destroy: (e) ->
  152. e.preventDefault()
  153. if @item and confirm "Are you sure you want to delete this item?"
  154. @item.destroy()
  155. @back()
  156. markAsDirty: =>
  157. @dirtyForm = true
  158. @saveButton.addClass('glow')
  159. cancel: (e) ->
  160. e.preventDefault()
  161. if @dirtyForm
  162. if confirm "You may have some unsaved changes.\nAre you sure you want to proceed?"
  163. @back()
  164. else
  165. @back()
  166. back: ->
  167. @navigate('/essays/list')
  168. preventSubmit: (e) ->
  169. e.preventDefault()
  170. return false
  171. deactivate: ->
  172. @el.scrollTop(0)
  173. super
  174. class EssayList extends Spine.Controller
  175. className: 'essay list panel fixed-header'
  176. events:
  177. 'click h1 .count': 'reload'
  178. constructor: ->
  179. super
  180. # @active @render
  181. Essay.bind 'change refresh', @render
  182. Spine.bind 'filterbox:change', @filter
  183. render: =>
  184. sortFunc = if @filterObj?.sortBy then Essay[@filterObj.sortBy] else Essay.dateSort
  185. context =
  186. essays: Essay.filter(@filterObj).sort(sortFunc)
  187. @html templates.render('essays.html', {}, context)
  188. filter: (@filterObj) =>
  189. @render()
  190. @el.scrollTop(0)
  191. reload: ->
  192. Essay.fetch()
  193. class Essays extends Spine.Stack
  194. className: 'essays panel'
  195. controllers:
  196. list: EssayList
  197. form: EssayForm
  198. default: 'list'
  199. routes:
  200. '/essays/list': 'list'
  201. '/essay/new': 'form'
  202. '/essay/:id': 'form'
  203. constructor: ->
  204. super
  205. for k, v of @controllers
  206. @[k].active => @active()
  207. module.exports = Essays