module HypDiff

@api public

Constants

VERSION

Public Class Methods

compare(before, after, options = {}) click to toggle source

Compare two html snippets. @param before [String] the first html snippet @param after [String] the second html snippet @option options [Proc] :render_insertion provide a callback to render insertions. The callback will receive the inserted text as a html snippet. It should return a new html snippet that will be used in the output. If no callback is given, `<ins>`-Tags will be used to highlight insertions. @option options [Proc] :render_deletion provide a callback to render deletions. The callback will receive the deleted text as a html snippet. It should return a new html snippet that will be used in the output. If no callback is given, `<del>`-Tags will be used to highlight deletions. @option options [String] :markup_from specify if the markup from `before` or `after` should be used as the basis for the output. Possible values: “before” and “after”. Default: “after” @return [String] a new html snippet that highlights changes between `before` and `after` @api public

# File lib/hyp_diff.rb, line 17
def compare(before, after, options = {})
  parsed_after = parse(after)
  parsed_before = parse(before)

  text_changes = Diff::LCS.sdiff(extract_text(parsed_before), extract_text(parsed_after))

  markup_from_before = options[:markup_from] == "before"

  change_node_tuples = text_changes.map do |change|
    text_from_node = markup_from_before ? change.old_element : change.new_element
    [change, text_from_node && text_from_node.node]
  end

  render_deletion = options[:render_deletion] || proc { |html| "<del>#{html}</del>" }
  render_insertion = options[:render_insertion] || proc { |html| "<ins>#{html}</ins>" }

  NodeMap.for(change_node_tuples).each do |node, changes|
    node.replace(ChangeRenderer.render(changes, render_deletion, render_insertion))
  end

  modified_fragment = markup_from_before ? parsed_before : parsed_after
  modified_fragment.to_html
end

Private Class Methods

extract_text(node) click to toggle source
# File lib/hyp_diff.rb, line 165
def extract_text(node)
  filter_whitespace(text_fragments(node))
end
filter_whitespace(node_list) click to toggle source
# File lib/hyp_diff.rb, line 177
def filter_whitespace(node_list)
  result = []
  last_node_whitespace = false
  node_list.each do |node|
    node_whitespace = node.whitespace?
    result << node unless last_node_whitespace && node_whitespace

    last_node_whitespace = node_whitespace
  end

  result
end
parse(text) click to toggle source
# File lib/hyp_diff.rb, line 161
def parse(text)
  Nokogiri::HTML.fragment(text)
end
text_fragments(node) click to toggle source
# File lib/hyp_diff.rb, line 169
def text_fragments(node)
  if node.is_a?(Nokogiri::XML::Text)
    node.text.split(/(?=[.!,<> ])|\b/).map { |token| TextFromNode.new(token, node) }
  else
    node.children.map { |c| text_fragments(c) }.flatten
  end
end