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