2014-03-06 09:08:12 -05:00
|
|
|
define [
|
|
|
|
"ace/ace"
|
|
|
|
"ace/mode/latex"
|
|
|
|
"ace/range"
|
2014-03-12 12:46:20 -04:00
|
|
|
"moment"
|
2014-03-06 09:08:12 -05:00
|
|
|
"libs/backbone"
|
2014-03-21 07:34:16 -04:00
|
|
|
"libs/mustache"
|
2014-03-12 12:46:20 -04:00
|
|
|
], (Ace, LatexMode, Range, moment)->
|
2014-03-06 09:08:12 -05:00
|
|
|
DiffView = Backbone.View.extend
|
2014-03-21 07:34:16 -04:00
|
|
|
template: $("#trackChangesDiffTemplate").html()
|
|
|
|
|
|
|
|
events:
|
2014-06-05 11:18:25 -04:00
|
|
|
"click .restore": (e) ->
|
|
|
|
e.preventDefault()
|
2014-03-21 07:34:16 -04:00
|
|
|
@trigger "restore"
|
2014-06-05 11:18:25 -04:00
|
|
|
"click .restore-deleted": (e) ->
|
|
|
|
e.preventDefault()
|
|
|
|
@$("a.restore-deleted").attr("disabled", true)
|
|
|
|
@$("a.restore-deleted").text("Restoring...")
|
|
|
|
@trigger "restore-deleted"
|
2014-03-21 07:34:16 -04:00
|
|
|
|
2014-03-06 09:08:12 -05:00
|
|
|
initialize: () ->
|
|
|
|
@model.on "change:diff", () => @render()
|
|
|
|
|
|
|
|
render: ->
|
|
|
|
diff = @model.get("diff")
|
|
|
|
return unless diff?
|
2014-03-21 10:23:07 -04:00
|
|
|
|
|
|
|
changes = @getNumberOfChanges()
|
|
|
|
html = Mustache.to_html @template, {
|
|
|
|
changes: "#{changes} change#{if changes == 1 then "" else "s"}"
|
|
|
|
name: @model.get("doc")?.get("name")
|
|
|
|
}
|
|
|
|
@$el.html(html)
|
|
|
|
|
|
|
|
if !@model.get("from")? or !@model.get("to")? or changes == 0
|
|
|
|
@$(".restore").hide()
|
|
|
|
|
2014-06-05 11:18:25 -04:00
|
|
|
if @model.get("doc").get("deleted")
|
|
|
|
@$(".restore").hide()
|
|
|
|
@$(".deleted-info").show()
|
|
|
|
|
2014-03-06 09:08:12 -05:00
|
|
|
@createAceEditor()
|
|
|
|
@aceEditor.setValue(@getPlainDiffContent())
|
|
|
|
@aceEditor.clearSelection()
|
2014-03-13 13:32:08 -04:00
|
|
|
@$ace = $(@aceEditor.renderer.container).find(".ace_scroller")
|
2014-03-06 09:08:12 -05:00
|
|
|
@insertMarkers()
|
2014-03-12 12:46:20 -04:00
|
|
|
@insertNameTag()
|
2014-03-13 13:32:08 -04:00
|
|
|
@insertMoreChangeLabels()
|
2014-03-19 06:52:30 -04:00
|
|
|
@bindToScrollEvents()
|
2014-03-13 13:32:08 -04:00
|
|
|
@scrollToFirstChange()
|
2014-03-06 09:08:12 -05:00
|
|
|
return @
|
|
|
|
|
2014-03-21 07:34:16 -04:00
|
|
|
remove: () ->
|
2014-03-13 10:32:30 -04:00
|
|
|
@$editor?.remove()
|
2014-03-21 07:34:16 -04:00
|
|
|
@undelegateEvents()
|
2014-03-13 10:32:30 -04:00
|
|
|
|
2014-03-06 09:08:12 -05:00
|
|
|
createAceEditor: () ->
|
2014-03-21 07:34:16 -04:00
|
|
|
@$editor = @$(".track-changes-diff-editor")
|
2014-03-13 10:32:30 -04:00
|
|
|
@$el.append(@$editor)
|
|
|
|
@aceEditor = Ace.edit(@$editor[0])
|
2014-03-06 09:08:12 -05:00
|
|
|
@aceEditor.setTheme("ace/theme/#{window.userSettings.theme}")
|
|
|
|
@aceEditor.setReadOnly true
|
|
|
|
@aceEditor.setShowPrintMargin(false)
|
2014-03-13 13:32:08 -04:00
|
|
|
session = @aceEditor.getSession()
|
|
|
|
session.setMode(new LatexMode.Mode())
|
|
|
|
session.setUseWrapMode(true)
|
2014-03-06 09:08:12 -05:00
|
|
|
|
|
|
|
@aceEditor.on "mousemove", (e) =>
|
|
|
|
position = @aceEditor.renderer.screenToTextCoordinates(e.clientX, e.clientY)
|
|
|
|
e.position = position
|
|
|
|
@updateVisibleNames(e)
|
|
|
|
|
2014-03-19 06:52:30 -04:00
|
|
|
bindToScrollEvents: () ->
|
|
|
|
@aceEditor.getSession().on "changeScrollTop", (e) =>
|
2014-03-13 13:32:08 -04:00
|
|
|
@updateMoreChangeLabels()
|
|
|
|
|
2014-03-06 09:08:12 -05:00
|
|
|
getPlainDiffContent: () ->
|
|
|
|
content = ""
|
|
|
|
for entry in @model.get("diff") or []
|
|
|
|
content += entry.u or entry.i or entry.d or ""
|
|
|
|
return content
|
|
|
|
|
2014-03-21 07:34:16 -04:00
|
|
|
getNumberOfChanges: () ->
|
|
|
|
changes = 0
|
|
|
|
for entry in @model.get("diff") or []
|
|
|
|
changes += 1 if entry.i? or entry.d?
|
|
|
|
return changes
|
|
|
|
|
2014-03-06 09:08:12 -05:00
|
|
|
insertMarkers: () ->
|
|
|
|
row = 0
|
|
|
|
column = 0
|
2014-03-12 12:46:20 -04:00
|
|
|
@entries = []
|
2014-03-06 09:08:12 -05:00
|
|
|
for entry, i in @model.get("diff") or []
|
|
|
|
content = entry.u or entry.i or entry.d
|
2014-03-10 09:09:40 -04:00
|
|
|
content ||= ""
|
2014-03-06 09:08:12 -05:00
|
|
|
lines = content.split("\n")
|
|
|
|
startRow = row
|
|
|
|
startColumn = column
|
|
|
|
if lines.length > 1
|
|
|
|
endRow = startRow + lines.length - 1
|
|
|
|
endColumn = lines[lines.length - 1].length
|
|
|
|
else
|
|
|
|
endRow = startRow
|
|
|
|
endColumn = startColumn + lines[0].length
|
|
|
|
row = endRow
|
|
|
|
column = endColumn
|
|
|
|
|
|
|
|
range = new Range.Range(
|
|
|
|
startRow, startColumn, endRow, endColumn
|
|
|
|
)
|
2014-03-12 12:46:20 -04:00
|
|
|
entry.range = range
|
|
|
|
@addMarker(range, entry)
|
|
|
|
if entry.i? or entry.d?
|
|
|
|
@entries.push entry
|
2014-03-06 09:08:12 -05:00
|
|
|
|
2014-03-12 12:46:20 -04:00
|
|
|
addMarker: (range, entry) ->
|
2014-03-06 09:08:12 -05:00
|
|
|
session = @aceEditor.getSession()
|
|
|
|
markerBackLayer = @aceEditor.renderer.$markerBack
|
|
|
|
markerFrontLayer = @aceEditor.renderer.$markerFront
|
|
|
|
lineHeight = @aceEditor.renderer.lineHeight
|
2014-03-19 07:23:27 -04:00
|
|
|
|
|
|
|
dark = @_isDarkTheme()
|
|
|
|
|
2014-03-06 09:08:12 -05:00
|
|
|
if entry.i? or entry.d?
|
2014-03-07 07:00:31 -05:00
|
|
|
hue = entry.meta.user.hue()
|
2014-03-06 09:08:12 -05:00
|
|
|
if entry.i?
|
2014-03-19 07:23:27 -04:00
|
|
|
if dark
|
|
|
|
style = "background-color : hsl(#{hue}, 100%, 28%);"
|
|
|
|
else
|
|
|
|
style = "background-color : hsl(#{hue}, 70%, 85%);"
|
|
|
|
@_addMarkerWithCustomStyle session, markerBackLayer, range, "inserted-change-background", false, style
|
2014-03-06 09:08:12 -05:00
|
|
|
if entry.d?
|
2014-03-19 07:23:27 -04:00
|
|
|
if dark
|
|
|
|
bgStyle = "background-color: hsl(#{hue}, 100%, 20%);"
|
|
|
|
fgStyle = "border-bottom: 2px solid hsl(#{hue}, 100%, 60%);"
|
|
|
|
else
|
|
|
|
bgStyle = "background-color: hsl(#{hue}, 70%, 95%);"
|
|
|
|
fgStyle = "border-bottom: 2px solid hsl(#{hue}, 70%, 40%);"
|
|
|
|
fgStyle += "; height: #{Math.round(lineHeight/2) - 1}px;"
|
|
|
|
@_addMarkerWithCustomStyle session, markerBackLayer, range, "deleted-change-background", false, bgStyle
|
|
|
|
@_addMarkerWithCustomStyle session, markerBackLayer, range, "deleted-change-foreground", true, fgStyle
|
2014-03-06 09:08:12 -05:00
|
|
|
|
|
|
|
_addMarkerWithCustomStyle: (session, markerLayer, range, klass, foreground, style) ->
|
|
|
|
session.addMarker range, klass, (html, range, left, top, config) ->
|
|
|
|
if range.isMultiLine()
|
|
|
|
markerLayer.drawTextMarker(html, range, klass, config, style)
|
|
|
|
else
|
|
|
|
markerLayer.drawSingleLineMarker(html, range, "#{klass} ace_start", config, 0, style)
|
|
|
|
, foreground
|
|
|
|
|
2014-03-19 07:23:27 -04:00
|
|
|
_isDarkTheme: () ->
|
|
|
|
rgb = $(".ace_editor").css("background-color");
|
|
|
|
[m, r, g, b] = rgb.match(/rgb\(([0-9]+), ([0-9]+), ([0-9]+)\)/)
|
|
|
|
r = parseInt(r, 10)
|
|
|
|
g = parseInt(g, 10)
|
|
|
|
b = parseInt(b, 10)
|
|
|
|
return r + g + b < 3 * 128
|
|
|
|
|
2014-03-12 12:46:20 -04:00
|
|
|
insertNameTag: () ->
|
|
|
|
@$nameTagEl = $("<div class='change-name-marker'></div>")
|
|
|
|
@$nameTagEl.css({
|
|
|
|
position: "absolute"
|
|
|
|
})
|
|
|
|
@$nameTagEl.hide()
|
|
|
|
@$ace.append(@$nameTagEl)
|
|
|
|
|
2014-03-13 13:32:08 -04:00
|
|
|
insertMoreChangeLabels: () ->
|
2014-03-19 06:52:30 -04:00
|
|
|
@$changesBefore = $("<a class='changes-before' href='#'><span></span> <i class='icon-arrow-up'></a>")
|
|
|
|
@$changesAfter = $("<a class='changes-after' href='#'><span></span> <i class='icon-arrow-down'></a>")
|
2014-03-13 13:32:08 -04:00
|
|
|
@$ace.append(@$changesBefore)
|
|
|
|
@$ace.append(@$changesAfter)
|
2014-03-19 06:52:30 -04:00
|
|
|
@$changesBefore.on "click", () =>
|
|
|
|
@gotoLastHiddenChangeBefore()
|
|
|
|
@$changesAfter.on "click", () =>
|
|
|
|
@gotoFirstHiddenChangeAfter()
|
2014-03-13 13:32:08 -04:00
|
|
|
@updateMoreChangeLabels()
|
|
|
|
|
|
|
|
scrollToFirstChange: () ->
|
2014-03-19 06:52:30 -04:00
|
|
|
@aceEditor.scrollToLine(0)
|
|
|
|
setTimeout () =>
|
|
|
|
if @entries? and @entries[0]?
|
|
|
|
row = @entries[0].range.start.row
|
|
|
|
@aceEditor.scrollToLine(row, true, false)
|
|
|
|
, 10
|
2014-03-13 13:32:08 -04:00
|
|
|
|
2014-03-12 12:46:20 -04:00
|
|
|
_drawNameTag: (entry, position) ->
|
|
|
|
@$nameTagEl.show()
|
|
|
|
|
|
|
|
if entry.i?
|
|
|
|
text = "Added by #{entry.meta.user.name()}"
|
|
|
|
else if entry.d?
|
|
|
|
text = "Deleted by #{entry.meta.user.name()}"
|
|
|
|
date = moment(parseInt(entry.meta.end_ts, 10)).format("Do MMM YYYY, h:mm a")
|
|
|
|
text += " on #{date}"
|
|
|
|
@$nameTagEl.text(text)
|
|
|
|
|
|
|
|
position = @aceEditor.renderer.textToScreenCoordinates(position.row, position.column)
|
|
|
|
offset = @$ace.offset()
|
|
|
|
position.pageX = position.pageX - offset.left
|
|
|
|
position.pageY = position.pageY - offset.top
|
|
|
|
height = @$ace.height()
|
|
|
|
|
|
|
|
hue = entry.meta.user.hue()
|
2014-03-19 07:23:27 -04:00
|
|
|
if @_isDarkTheme()
|
|
|
|
css = { "background-color" : "hsl(#{hue}, 100%, 20%)"; }
|
|
|
|
else
|
|
|
|
css = { "background-color" : "hsl(#{hue}, 70%, 90%)"; }
|
2014-03-12 12:46:20 -04:00
|
|
|
|
|
|
|
if position.pageX + @$nameTagEl.width() < @$ace.width()
|
|
|
|
css["left"] = position.pageX
|
|
|
|
css["right"] = "auto"
|
|
|
|
else
|
|
|
|
css["right"] = 0
|
|
|
|
css["left"] = "auto"
|
|
|
|
|
|
|
|
if position.pageY > 2 * @$nameTagEl.height()
|
|
|
|
css["bottom"] = height - position.pageY
|
|
|
|
css["top"] = "auto"
|
|
|
|
else
|
|
|
|
css["top"] = position.pageY + @aceEditor.renderer.lineHeight
|
|
|
|
css["bottom"] = "auto"
|
|
|
|
|
|
|
|
@$nameTagEl.css css
|
|
|
|
|
|
|
|
_hideNameTag: () ->
|
2014-03-19 07:23:27 -04:00
|
|
|
@$nameTagEl?.hide()
|
2014-03-06 09:08:12 -05:00
|
|
|
|
|
|
|
updateVisibleNames: (e) ->
|
2014-03-12 12:46:20 -04:00
|
|
|
visibleName = false
|
|
|
|
for entry in @entries or []
|
|
|
|
if entry.range.contains(e.position.row, e.position.column)
|
|
|
|
@_drawNameTag(entry, e.position)
|
|
|
|
visibleName = true
|
|
|
|
break
|
|
|
|
if !visibleName
|
|
|
|
@_hideNameTag()
|
2014-03-06 09:08:12 -05:00
|
|
|
|
2014-03-13 13:32:08 -04:00
|
|
|
updateMoreChangeLabels: () ->
|
|
|
|
return if !@$changesBefore or !@$changesAfter
|
2014-03-19 06:52:30 -04:00
|
|
|
setTimeout () =>
|
|
|
|
firstRow = @aceEditor.getFirstVisibleRow()
|
|
|
|
lastRow = @aceEditor.getLastVisibleRow()
|
|
|
|
changesBefore = 0
|
|
|
|
changesAfter = 0
|
|
|
|
@lastHiddenChangeBefore = null
|
|
|
|
@firstHiddenChangeAfter = null
|
|
|
|
for entry in @entries or []
|
|
|
|
if entry.range.start.row < firstRow
|
|
|
|
changesBefore += 1
|
|
|
|
@lastHiddenChangeBefore = entry
|
|
|
|
if entry.range.end.row > lastRow
|
|
|
|
changesAfter += 1
|
|
|
|
@firstHiddenChangeAfter ||= entry
|
|
|
|
|
|
|
|
if changesBefore > 0
|
|
|
|
@$changesBefore.find("span").text("#{changesBefore} more change#{if changesBefore > 1 then "s" else ""} above")
|
|
|
|
@$changesBefore.show()
|
|
|
|
else
|
|
|
|
@$changesBefore.hide()
|
|
|
|
if changesAfter > 0
|
|
|
|
@$changesAfter.find("span").text("#{changesAfter} more change#{if changesAfter > 1 then "s" else ""} below")
|
|
|
|
@$changesAfter.show()
|
|
|
|
else
|
|
|
|
@$changesAfter.hide()
|
|
|
|
, 100
|
|
|
|
|
|
|
|
gotoLastHiddenChangeBefore: () ->
|
|
|
|
return if !@lastHiddenChangeBefore
|
|
|
|
@aceEditor.scrollToLine(@lastHiddenChangeBefore.range.start.row, true, false)
|
|
|
|
|
|
|
|
gotoFirstHiddenChangeAfter: () ->
|
|
|
|
return if !@firstHiddenChangeAfter
|
|
|
|
@aceEditor.scrollToLine(@firstHiddenChangeAfter.range.end.row, true, false)
|
2014-03-13 13:32:08 -04:00
|
|
|
|
2014-03-14 06:20:00 -04:00
|
|
|
resize: () ->
|
|
|
|
@aceEditor.resize()
|
|
|
|
|
2014-03-06 09:08:12 -05:00
|
|
|
return DiffView
|
|
|
|
|