class DiffRenderer class ParagraphBlock def self.new(context_change) case context_change.action when "=" then PlainParagraph when "-" then RemovedParagraph when "+" then AddedParagraph when "!" then ChangedParagraph end.new(context_change) end class HtmlParagraph def initialize(content, class_string = "") @content = content @class_string = class_string end def to_s str = "<p#{@class_string}>\n" str << " " << @content str << "\n</p>\n" end end class BaseParagraph def initialize(context_change) @context_change = context_change end def to_s HtmlParagraph.new(content, html_class_string).to_s end end class PlainParagraph < BaseParagraph def html_class_string "" end def content @context_change.old_element end end class ClassfulParagraph < BaseParagraph def html_class_string %Q{ class="#{classes.join(" ")}"} end end class DeletedParagraph < ClassfulParagraph def content @context_change.old_element end end class RemovedParagraph < DeletedParagraph def classes %w[ removed ] end end class ReplacedParagraph < DeletedParagraph def classes %w[ removed replaced ] end end class AddedParagraph < ClassfulParagraph def classes %w[ added ] end def content @context_change.new_element end end class ChangedParagraph include WordRunDiffLCS class HtmlText class HtmlSpan def initialize(content, class_string) @content = content @class_string = class_string end def to_s %Q{<span class="#@class_string">#@content</span>} end end def initialize(context_change) @context_change = context_change end def to_s case @context_change.action when "=" then old_text when "-" then removed_span when "+" then added_span when "!" then "#{removed_span} #{added_span}" end.to_s end private def old_text @context_change.old_element end def removed_span HtmlSpan.new(@context_change.old_element, "removed") end def added_span HtmlSpan.new(@context_change.new_element, "added") end end def initialize(context_change) @context_change = context_change end def to_s if significant_change? && !all_additions_or_removals? removed_paragraph = ReplacedParagraph.new(@context_change) added_paragraph = AddedParagraph.new(@context_change) removed_paragraph.to_s + added_paragraph.to_s else words = word_run_level_sdiff.map { |context_change| HtmlText.new(context_change) } HtmlParagraph.new(words.join(" ")).to_s end end private WORD_CHANGE_THRESHOLD = 0.25 def significant_change? count = word_level_sdiff.count { |context_change| context_change.action != "=" } count.to_f / word_level_sdiff.length > WORD_CHANGE_THRESHOLD end def all_additions_or_removals? word_level_sdiff.all? { |context_change| context_change.action =~ /^[+=]$/ } || word_level_sdiff.all? { |context_change| context_change.action =~ /^[-=]$/ } end def word_run_level_sdiff word_run_sdiff = WordRunContextChangeSequence.new word_level_sdiff.each do |context_change| word_run_sdiff << context_change end word_run_sdiff end def word_level_sdiff Diff::LCS.sdiff(@context_change.old_element.split, @context_change.new_element.split) end end end end