codecombat/app/views/editor/DeltaView.coffee

127 lines
4.3 KiB
CoffeeScript

CocoView = require 'views/core/CocoView'
template = require 'templates/editor/delta'
deltasLib = require 'core/deltas'
require 'vendor/diffview'
require 'vendor/difflib'
require 'vendor/treema'
TEXTDIFF_OPTIONS =
baseTextName: "Old"
newTextName: "New"
contextSize: 5
viewType: 1
module.exports = class DeltaView extends CocoView
###
Takes a CocoModel instance (model) and displays changes since the
last save (attributes vs _revertAttributes).
* If headModel is included, will look for and display conflicts with the changes in model.
* If comparisonModel is included, will show deltas between model and comparisonModel instead
of changes within model itself.
###
@deltaCounter: 0
className: 'delta-view'
template: template
constructor: (options) ->
super(options)
@expandedDeltas = []
@skipPaths = options.skipPaths
for modelName in ['model', 'headModel', 'comparisonModel']
@[modelName] = options[modelName]
continue unless @[modelName] and options.loadModels
if not @[modelName].isLoaded
@[modelName] = @supermodel.loadModel(@[modelName], 'document').model
@buildDeltas() if @supermodel.finished()
onLoaded: ->
@buildDeltas()
super()
buildDeltas: ->
if @comparisonModel
@expandedDeltas = @model.getExpandedDeltaWith(@comparisonModel)
else
@expandedDeltas = @model.getExpandedDelta()
[@expandedDeltas, @skippedDeltas] = @filterDeltas(@expandedDeltas)
if @headModel
@headDeltas = @headModel.getExpandedDelta()
@headDeltas = @filterDeltas(@headDeltas)[0]
@conflicts = deltasLib.getConflicts(@headDeltas, @expandedDeltas)
filterDeltas: (deltas) ->
return [deltas, []] unless @skipPaths
for path, i in @skipPaths
@skipPaths[i] = [path] if _.isString(path)
newDeltas = []
skippedDeltas = []
for delta in deltas
skip = false
for skipPath in @skipPaths
if _.isEqual _.first(delta.dataPath, skipPath.length), skipPath
skip = true
break
if skip then skippedDeltas.push delta else newDeltas.push delta
[newDeltas, skippedDeltas]
getRenderData: ->
c = super()
c.deltas = @expandedDeltas
c.counter = DeltaView.deltaCounter
DeltaView.deltaCounter += @expandedDeltas.length
c
afterRender: ->
deltas = @$el.find('.details')
for delta, i in deltas
deltaEl = $(delta)
deltaData = @expandedDeltas[i]
@expandDetails(deltaEl, deltaData)
conflictDeltas = @$el.find('.conflict-details')
conflicts = (delta.conflict for delta in @expandedDeltas when delta.conflict)
for delta, i in conflictDeltas
deltaEl = $(delta)
deltaData = conflicts[i]
@expandDetails(deltaEl, deltaData)
expandDetails: (deltaEl, deltaData) ->
treemaOptions = { schema: deltaData.schema or {}, readOnly: true }
if _.isObject(deltaData.left) and leftEl = deltaEl.find('.old-value')
options = _.defaults {data: _.merge({}, deltaData.left)}, treemaOptions
try
TreemaNode.make(leftEl, options).build()
catch error
console.error "Couldn't show left details Treema for", deltaData.left, treemaOptions
if _.isObject(deltaData.right) and rightEl = deltaEl.find('.new-value')
options = _.defaults {data: _.merge({}, deltaData.right)}, treemaOptions
try
TreemaNode.make(rightEl, options).build()
catch error
console.error "Couldn't show right details Treema for", deltaData.right, treemaOptions
if deltaData.action is 'text-diff'
return console.error "Couldn't show diff for left: #{deltaData.left}, right: #{deltaData.right} of delta:", deltaData unless deltaData.left? and deltaData.right?
left = difflib.stringAsLines deltaData.left
right = difflib.stringAsLines deltaData.right
sm = new difflib.SequenceMatcher(left, right)
opcodes = sm.get_opcodes()
el = deltaEl.find('.text-diff')
options = {baseTextLines: left, newTextLines: right, opcodes: opcodes}
args = _.defaults options, TEXTDIFF_OPTIONS
el.append(diffview.buildView(args))
getApplicableDelta: ->
delta = @model.getDelta()
delta = deltasLib.pruneConflictsFromDelta delta, @conflicts if @conflicts
delta = deltasLib.pruneExpandedDeltasFromDelta delta, @skippedDeltas if @skippedDeltas
delta