ChangeListView.coffee 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. define [
  2. "moment"
  3. "libs/mustache"
  4. "libs/backbone"
  5. ], (moment)->
  6. moment.lang "en", calendar:
  7. lastDay : '[Yesterday]'
  8. sameDay : '[Today]'
  9. nextDay : '[Tomorrow]'
  10. lastWeek : "ddd, Do MMM YY"
  11. nextWeek : "ddd, Do MMM YY"
  12. sameElse : 'ddd, Do MMM YY'
  13. ChangeListView = Backbone.View.extend
  14. template: $("#changeListTemplate").html()
  15. events:
  16. "scroll" : () -> @loadUntilFull()
  17. initialize: () ->
  18. @itemViews = []
  19. @atEndOfCollection = false
  20. self = this
  21. @collection.on "add", (model) ->
  22. self.addItem model
  23. @collection.on "reset", (collection) ->
  24. self.addItem model for model in collection.models
  25. @selectedFromIndex = 0
  26. @selectedToIndex = 0
  27. @render()
  28. @hideLoading()
  29. render: ->
  30. @$el.html Mustache.to_html @template
  31. @$el.css
  32. overflow: "scroll"
  33. this
  34. remove: () ->
  35. @undelegateEvents()
  36. addItem: (model) ->
  37. index = @collection.indexOf(model)
  38. previousModel = @collection.models[index - 1]
  39. view = new ChangeListItemView(model : model, previousModel: previousModel)
  40. @itemViews.push view
  41. elementAtIndex = @$(".change-list").children()[index]
  42. view.$el.insertBefore(elementAtIndex)
  43. view.on "click", (e, v) =>
  44. if e.shiftKey
  45. @selectRangeTo(index)
  46. else
  47. @setSelectionRange(index, index)
  48. view.on "selected:to", (e, v) =>
  49. @setSelectionRange(@selectedFromIndex, index)
  50. view.on "selected:from", (e, v) =>
  51. @setSelectionRange(index, @selectedToIndex)
  52. view.on "mouseenter:to", (e) =>
  53. @hoverToIndex = index
  54. @resetHoverStates()
  55. view.on "mouseleave:to", (e) =>
  56. delete @hoverToIndex
  57. @resetHoverStates()
  58. view.on "mouseenter:from", (e) =>
  59. @hoverFromIndex = index
  60. @resetHoverStates()
  61. view.on "mouseleave:from", (e) =>
  62. delete @hoverFromIndex
  63. @resetHoverStates()
  64. view.resetSelector(index, @selectedFromIndex, @selectedToIndex)
  65. setSelectionRange: (fromIndex, toIndex) ->
  66. @selectedFromIndex = fromIndex
  67. @selectedToIndex = toIndex
  68. @resetAllSelectors()
  69. @triggerChangeDiff()
  70. selectRangeTo: (index) ->
  71. return unless @selectedFromIndex? and @selectedToIndex?
  72. if index < @selectedToIndex
  73. @setSelectionRange(@selectedFromIndex, index)
  74. else
  75. @setSelectionRange(index, @selectedToIndex)
  76. resetAllSelectors: () ->
  77. for view, i in @itemViews
  78. view.resetSelector(i, @selectedFromIndex, @selectedToIndex)
  79. resetHoverStates: () ->
  80. if @hoverToIndex? and @hoverToIndex != @selectedToIndex
  81. @$("ul").addClass("hover-state")
  82. for view, i in @itemViews
  83. view.resetHoverState(i, @selectedFromIndex, @hoverToIndex)
  84. else if @hoverFromIndex? and @hoverFromIndex != @selectedFromIndex
  85. @$("ul").addClass("hover-state")
  86. for view, i in @itemViews
  87. view.resetHoverState(i, @hoverFromIndex, @selectedToIndex)
  88. else
  89. @$("ul").removeClass("hover-state")
  90. for view, i in @itemViews
  91. view.setHoverUnselected()
  92. triggerChangeDiff: () ->
  93. @trigger "change_diff", @selectedFromIndex, @selectedToIndex
  94. listShorterThanContainer: ->
  95. @$el.height() > @$(".change-list").height()
  96. atEndOfListView: ->
  97. @$el.scrollTop() + @$el.height() >= @$(".change-list").height() - 30
  98. loadUntilFull: (callback = (error) ->) ->
  99. if (@listShorterThanContainer() or @atEndOfListView()) and not @atEndOfCollection and not @loading
  100. @showLoading()
  101. @hideEmptyMessage()
  102. @collection.fetchNextBatch
  103. error: (error) =>
  104. @hideLoading()
  105. @showEmptyMessageIfCollectionEmpty()
  106. callback(error)
  107. success: (collection, response) =>
  108. @hideLoading()
  109. if @collection.isAtEnd()
  110. @atEndOfCollection = true
  111. @showEmptyMessageIfCollectionEmpty()
  112. callback()
  113. else
  114. @loadUntilFull(callback)
  115. else
  116. callback() if callback?
  117. showEmptyMessageIfCollectionEmpty: ()->
  118. if @collection.isEmpty()
  119. @$(".empty-message").show()
  120. else
  121. @$(".empty-message").hide()
  122. hideEmptyMessage: () ->
  123. @$(".empty-message").hide()
  124. showLoading: ->
  125. @loading = true
  126. @$(".loading-changes").show()
  127. hideLoading: ->
  128. @loading = false
  129. @$(".loading-changes").hide()
  130. ChangeListItemView = Backbone.View.extend
  131. tagName: "li"
  132. events:
  133. "click .change-description" : "onClick"
  134. "click .change-selector-from" : "onFromSelectorClick"
  135. "click .change-selector-to" : "onToSelectorClick"
  136. "mouseenter .change-selector-to": (args...) ->
  137. @trigger "mouseenter:to", args...
  138. "mouseleave .change-selector-to": (args...) ->
  139. @trigger "mouseleave:to", args...
  140. "mouseenter .change-selector-from": (args...) ->
  141. @trigger "mouseenter:from", args...
  142. "mouseleave .change-selector-from": (args...) ->
  143. @trigger "mouseleave:from", args...
  144. templates:
  145. item: $("#changeListItemTemplate").html()
  146. user: $("#changeListItemUserTemplate").html()
  147. initialize: ->
  148. @render()
  149. render: ->
  150. userHtml = for user in @model.get("users")
  151. Mustache.to_html @templates.user, {
  152. hue: user.hue()
  153. name: user.name()
  154. }
  155. docNames = []
  156. for doc in @model.get("docs")
  157. if doc.entity?
  158. docNames.push doc.entity.get("name")
  159. else
  160. docNames.push "deleted"
  161. data = {
  162. day: moment(parseInt(@model.get("end_ts"), 10)).calendar()
  163. time: moment(parseInt(@model.get("end_ts"), 10)).format("h:mm a")
  164. users: userHtml.join("")
  165. docs: docNames.join(", ")
  166. }
  167. @$el.html Mustache.to_html(@templates.item, data)
  168. if @options.previousModel?
  169. prevDate = @options.previousModel.get("end_ts")
  170. date = @model.get("end_ts")
  171. if not moment(prevDate).isSame(date, "day")
  172. @$el.addClass("first-in-day")
  173. else
  174. @$el.addClass("first-in-day")
  175. @$(".change-selector-from").tooltip({
  176. title: "Show back to this change",
  177. placement: "left",
  178. animation: false
  179. })
  180. @$(".change-selector-to").tooltip({
  181. title: "Show up to this change",
  182. placement: "left",
  183. animation: false
  184. })
  185. return this
  186. onClick: (e) ->
  187. e.preventDefault()
  188. @trigger "click", e, @
  189. onToSelectorClick: (e) ->
  190. @trigger "selected:to", e, @
  191. onFromSelectorClick: (e) ->
  192. @trigger "selected:from", e, @
  193. isSelectedFrom: () ->
  194. @$(".change-selector-from").is(":checked")
  195. isSelectedTo: () ->
  196. @$(".change-selector-to").is(":checked")
  197. hideFromSelector: () ->
  198. @$(".change-selector-from").hide()
  199. showFromSelector: () ->
  200. @$(".change-selector-from").show()
  201. hideToSelector: () ->
  202. @$(".change-selector-to").hide()
  203. showToSelector: () ->
  204. @$(".change-selector-to").show()
  205. setFromChecked: (checked) ->
  206. @$(".change-selector-from").prop("checked", checked)
  207. setToChecked: (checked) ->
  208. @$(".change-selector-to").prop("checked", checked)
  209. setSelected: (first, last) ->
  210. @$el.addClass("selected-change")
  211. if first
  212. @$el.addClass("selected-change-to")
  213. else
  214. @$el.removeClass("selected-change-to")
  215. if last
  216. @$el.addClass("selected-change-from")
  217. else
  218. @$el.removeClass("selected-change-from")
  219. setUnselected: () ->
  220. @$el.removeClass("selected-change-to")
  221. @$el.removeClass("selected-change-from")
  222. @$el.removeClass("selected-change")
  223. setHoverSelected: (first, last) ->
  224. @$el.addClass("hover-selected")
  225. if first
  226. @$el.addClass("hover-selected-to")
  227. else
  228. @$el.removeClass("hover-selected-to")
  229. if last
  230. @$el.addClass("hover-selected-from")
  231. else
  232. @$el.removeClass("hover-selected-from")
  233. setHoverUnselected: () ->
  234. @$el.removeClass("hover-selected-to")
  235. @$el.removeClass("hover-selected-from")
  236. @$el.removeClass("hover-selected")
  237. resetSelector: (myIndex, selectedFromIndex, selectedToIndex) ->
  238. if myIndex >= selectedToIndex
  239. @showFromSelector()
  240. else
  241. @hideFromSelector()
  242. if myIndex <= selectedFromIndex
  243. @showToSelector()
  244. else
  245. @hideToSelector()
  246. if selectedToIndex <= myIndex <= selectedFromIndex
  247. @setSelected(selectedToIndex == myIndex, selectedFromIndex == myIndex)
  248. else
  249. @setUnselected()
  250. @setFromChecked(myIndex == selectedFromIndex)
  251. @setToChecked(myIndex == selectedToIndex)
  252. resetHoverState: (myIndex, hoverFromIndex, hoverToIndex) ->
  253. if hoverToIndex <= myIndex <= hoverFromIndex
  254. @setHoverSelected(hoverToIndex == myIndex, hoverFromIndex == myIndex)
  255. else
  256. @setHoverUnselected()
  257. return ChangeListView