require "diff-lcs" require "hyp_diff" module Scrivito class Diff; class << self def for(mode, field_type, old, new) case field_type when "widget" ListComparison.for(mode, old || [], new || []) when "html" for_html(mode, old, new) when "text" for_string(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 nil if !old return nil if !new hyp_diff_options = case mode when "added" {render_deletion: proc {}, markup_from: "after"} when "deleted" {render_insertion: proc {}, markup_from: "before"} when "diff" {} else return nil end diff = HypDiff.compare(old, new, hyp_diff_options) [StringTagging.tag_as_html(diff)] 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