require "diff-lcs"
require "hyp_diff"
module Scrivito
class Diff; class << self
def for(mode, field_type, old, new)
case field_type
when "widgetlist"
ListComparison.for(mode, old || [], new || [])
when "html"
for_html(mode, old, new)
when "string"
for_string(mode, old, new)
end
end
private
def for_string(mode, old, new)
for_html(mode, ERB::Util.h(old), ERB::Util.h(new))
end
def for_html(mode, old, new)
return if !old
return if !new
return unless %w[added deleted diff].include?(mode)
diff = HypDiff.compare(old, new, hyp_diff_options(mode))
[StringTagging.tag_as_html(diff)]
end
def hyp_diff_options(mode)
render_insertion_proc = proc { |html| "#{html}" }
render_deletion_proc = proc { |html| "#{html}" }
render_nothing_proc = proc {}
case mode
when 'added'
{
render_insertion: render_insertion_proc,
render_deletion: render_nothing_proc,
markup_from: 'after'
}
when 'deleted'
{
render_insertion: render_nothing_proc,
render_deletion: render_deletion_proc,
markup_from: 'before'
}
when 'diff'
{
render_insertion: render_insertion_proc,
render_deletion: render_deletion_proc
}
end
end
class ListComparison
def self.for(mode, old, new)
new(mode).compare(old, new).result
end
def initialize(mode)
@mode = mode
@list = []
@modifications = []
end
def compare(old, new)
::Diff::LCS.sdiff(old, new).map do |change|
case change.action
when "-" then
note_deletion(change)
when "+" then
note_insertion(change)
when "=" then
note_equal(change)
when "!" then
note_deletion(change)
note_insertion(change)
else
raise "unexpected change.action #{change.action}"
end
end
self
end
def result
[@list, @modifications]
end
private
def note_equal(change)
@modifications << nil
if @mode == "deleted"
@list << change.old_element
else
@list << change.new_element
end
end
def note_deletion(change)
unless @mode == "added"
@modifications << "deleted"
@list << change.old_element
end
end
def note_insertion(change)
unless @mode == "deleted"
@modifications << "new"
@list << change.new_element
end
end
end
end; end
end