DiffView.coffee 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. define [
  2. "ace/ace"
  3. "ace/mode/latex"
  4. "ace/range"
  5. "moment"
  6. "libs/backbone"
  7. ], (Ace, LatexMode, Range, moment)->
  8. DiffView = Backbone.View.extend
  9. initialize: () ->
  10. @model.on "change:diff", () => @render()
  11. @render()
  12. render: ->
  13. diff = @model.get("diff")
  14. return unless diff?
  15. @createAceEditor()
  16. @aceEditor.setValue(@getPlainDiffContent())
  17. @aceEditor.clearSelection()
  18. session = @aceEditor.getSession()
  19. session.setMode(new LatexMode.Mode())
  20. session.setUseWrapMode(true)
  21. @insertMarkers()
  22. @insertNameTag()
  23. return @
  24. createAceEditor: () ->
  25. @$el.empty()
  26. $editor = $("<div/>")
  27. @$el.append($editor)
  28. @aceEditor = Ace.edit($editor[0])
  29. @aceEditor.setTheme("ace/theme/#{window.userSettings.theme}")
  30. @aceEditor.setReadOnly true
  31. @aceEditor.setShowPrintMargin(false)
  32. @aceEditor.on "mousemove", (e) =>
  33. position = @aceEditor.renderer.screenToTextCoordinates(e.clientX, e.clientY)
  34. e.position = position
  35. @updateVisibleNames(e)
  36. getPlainDiffContent: () ->
  37. content = ""
  38. for entry in @model.get("diff") or []
  39. content += entry.u or entry.i or entry.d or ""
  40. return content
  41. insertMarkers: () ->
  42. row = 0
  43. column = 0
  44. @entries = []
  45. for entry, i in @model.get("diff") or []
  46. content = entry.u or entry.i or entry.d
  47. content ||= ""
  48. lines = content.split("\n")
  49. startRow = row
  50. startColumn = column
  51. if lines.length > 1
  52. endRow = startRow + lines.length - 1
  53. endColumn = lines[lines.length - 1].length
  54. else
  55. endRow = startRow
  56. endColumn = startColumn + lines[0].length
  57. row = endRow
  58. column = endColumn
  59. range = new Range.Range(
  60. startRow, startColumn, endRow, endColumn
  61. )
  62. entry.range = range
  63. @addMarker(range, entry)
  64. if entry.i? or entry.d?
  65. @entries.push entry
  66. addMarker: (range, entry) ->
  67. session = @aceEditor.getSession()
  68. markerBackLayer = @aceEditor.renderer.$markerBack
  69. markerFrontLayer = @aceEditor.renderer.$markerFront
  70. lineHeight = @aceEditor.renderer.lineHeight
  71. if entry.i? or entry.d?
  72. hue = entry.meta.user.hue()
  73. if entry.i?
  74. @_addMarkerWithCustomStyle session, markerBackLayer, range, "inserted-change-background", false, """
  75. background-color : hsl(#{hue}, 70%, 85%);
  76. """
  77. if entry.d?
  78. @_addMarkerWithCustomStyle session, markerBackLayer, range, "deleted-change-background", false, """
  79. background-color : hsl(#{hue}, 70%, 95%);
  80. """
  81. @_addMarkerWithCustomStyle session, markerBackLayer, range, "deleted-change-foreground", true, """
  82. height: #{Math.round(lineHeight/2) - 1}px;
  83. border-bottom: 2px solid hsl(#{hue}, 70%, 40%);
  84. """
  85. _addMarkerWithCustomStyle: (session, markerLayer, range, klass, foreground, style) ->
  86. session.addMarker range, klass, (html, range, left, top, config) ->
  87. if range.isMultiLine()
  88. markerLayer.drawTextMarker(html, range, klass, config, style)
  89. else
  90. markerLayer.drawSingleLineMarker(html, range, "#{klass} ace_start", config, 0, style)
  91. , foreground
  92. insertNameTag: () ->
  93. @$ace = $(@aceEditor.renderer.container).find(".ace_scroller")
  94. @$nameTagEl = $("<div class='change-name-marker'></div>")
  95. @$nameTagEl.css({
  96. position: "absolute"
  97. })
  98. @$nameTagEl.hide()
  99. @$ace.append(@$nameTagEl)
  100. _drawNameTag: (entry, position) ->
  101. @$nameTagEl.show()
  102. if entry.i?
  103. text = "Added by #{entry.meta.user.name()}"
  104. else if entry.d?
  105. text = "Deleted by #{entry.meta.user.name()}"
  106. date = moment(parseInt(entry.meta.end_ts, 10)).format("Do MMM YYYY, h:mm a")
  107. text += " on #{date}"
  108. @$nameTagEl.text(text)
  109. position = @aceEditor.renderer.textToScreenCoordinates(position.row, position.column)
  110. offset = @$ace.offset()
  111. position.pageX = position.pageX - offset.left
  112. position.pageY = position.pageY - offset.top
  113. height = @$ace.height()
  114. hue = entry.meta.user.hue()
  115. css = {
  116. "background-color" : "hsl(#{hue}, 70%, 90%)";
  117. }
  118. if position.pageX + @$nameTagEl.width() < @$ace.width()
  119. css["left"] = position.pageX
  120. css["right"] = "auto"
  121. else
  122. css["right"] = 0
  123. css["left"] = "auto"
  124. if position.pageY > 2 * @$nameTagEl.height()
  125. css["bottom"] = height - position.pageY
  126. css["top"] = "auto"
  127. else
  128. css["top"] = position.pageY + @aceEditor.renderer.lineHeight
  129. css["bottom"] = "auto"
  130. @$nameTagEl.css css
  131. _hideNameTag: () ->
  132. @$nameTagEl.hide()
  133. updateVisibleNames: (e) ->
  134. visibleName = false
  135. for entry in @entries or []
  136. if entry.range.contains(e.position.row, e.position.column)
  137. @_drawNameTag(entry, e.position)
  138. visibleName = true
  139. break
  140. if !visibleName
  141. @_hideNameTag()
  142. return DiffView