added diff-display plugin for showing git diffs

This commit is contained in:
Timothy N. Tsvetkov 2011-03-10 14:17:32 +03:00
parent e6e44d292a
commit 02bed37d17
41 changed files with 4112 additions and 0 deletions

View File

@ -0,0 +1 @@
.DS_Store

View File

@ -0,0 +1,4 @@
== 0.0.1 2008-01-28
* 1 major enhancement:
* Initial release

21
vendor/plugins/diff-display/License.txt vendored Normal file
View File

@ -0,0 +1,21 @@
Copyright (c) 2008 Johan Sørensen
Copyright (c) 2003 Marcel Molina Jr.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,26 @@
History.txt
License.txt
Manifest.txt
README.txt
Rakefile
config/hoe.rb
config/requirements.rb
lib/diff-display.rb
lib/diff-display/version.rb
log/debug.log
script/destroy
script/generate
script/txt2html
setup.rb
spec/diff-display_spec.rb
spec/spec.opts
spec/spec_helper.rb
tasks/deployment.rake
tasks/environment.rake
tasks/rspec.rake
tasks/website.rake
website/index.html
website/index.txt
website/javascripts/rounded_corners_lite.inc.js
website/stylesheets/screen.css
website/template.rhtml

58
vendor/plugins/diff-display/README.txt vendored Normal file
View File

@ -0,0 +1,58 @@
Diff::Display
=============
Diff::Display::Unified renders unified diffs into various forms. The output is
based on a callback object that's passed into the renderer
Rewrite of an (unreleased) library by Marcel Molina Jr., who wrote this it
probably back in 2004 or so.
Usage
======
irb(main):001:0> require 'diff-display'
=> true
irb(main):002:0> diff = <<EOS
irb(main):003:0" diff --git a/History.txt b/History.txt
irb(main):004:0" index 0ed7358..622c384 100644
irb(main):005:0" --- a/History.txt
irb(main):006:0" +++ b/History.txt
irb(main):007:0" @@ -1,4 +1,5 @@
irb(main):008:0" == 0.0.1 2008-01-28
irb(main):009:0"
irb(main):010:0" -* 1 major enhancement:
irb(main):011:0" - * Initial release
irb(main):012:0" +* 2 major enhancements:
irb(main):013:0" + * The Initial release
irb(main):014:0" + * stuff added
irb(main):015:0" EOS
...
irb(main):016:0> diff_display = Diff::Display::Unified.new(diff)
=> #<Diff::Display::Unified:0x331c9c @data=...
# Be boring and render it back out as a diff
irb(main):017:0> puts diff_display.render(Diff::Renderer::Diff.new)
diff --git a/History.txt b/History.txt
index 0ed7358..622c384 100644
--- a/History.txt
+++ b/History.txt
@@ -1,4 +1,5 @@
== 0.0.1 2008-01-28
-* 1 major enhancement:
- * Initial release
+* 2 major enhancements:
+ * The Initial release
+ * stuff added
See Diff::Renderer::Base for what methods your callback needs to implement
Git Repository
===============
http://gitorious.org/projects/diff-display/
License
======
Please see License.txt

10
vendor/plugins/diff-display/Rakefile vendored Normal file
View File

@ -0,0 +1,10 @@
# encoding: utf-8
require 'rubygems'
require 'hoe'
require './lib/diff-display.rb'
Hoe.new('diff-display', Diff::Display::VERSION::STRING) do |p|
p.rubyforge_name = 'hoe_testx'
p.developer('Johan Sørensen', 'johan@johansorensen.com')
end

View File

View File

@ -0,0 +1,14 @@
$:.unshift File.dirname(__FILE__)
module Diff
module Display
end
end
require "diff/display/version"
# require "diff/display/data_structure"
require "diff/display/unified"
require "diff/display/unified/generator"
require "diff/renderer/base"
require "diff/renderer/diff"

View File

@ -0,0 +1,19 @@
module Diff
module Display
class Unified
def initialize(udiff)
@data = Diff::Display::Unified::Generator.run(udiff)
end
attr_reader :data
def stats
@stats ||= data.stats
end
def render(renderer, out="")
out << renderer.render(data)
out
end
end
end
end

View File

@ -0,0 +1,404 @@
module Diff::Display
# Processes the diff and generates a Data object which contains the
# resulting data structure.
#
# The +run+ class method is fed a diff and returns a Data object. It will
# accept as its argument a String, an Array or a File object (or anything
# that responds to #each):
#
# Diff::Display::Unified::Generator.run(diff)
#
class Data < Array
def initialize
super
@stats = {:additions => 0, :deletions => 0}
end
def stats
each do |block|
block.each do |line|
case line
when Diff::Display::AddLine
@stats[:additions] += 1
when Diff::Display::RemLine
@stats[:deletions] += 1
end
end
end
@stats
end
def to_diff
diff = ""
each do |block|
block.each do |line|
line_str = line.expand_inline_changes_with(nil, nil)
case line
when HeaderLine
diff << "#{line_str}\n"
when UnModLine
diff << " #{line_str}\n"
when SepLine
diff << "\n"
when AddLine
diff << "+#{line_str}\n"
when RemLine
diff << "-#{line_str}\n"
when NonewlineLine
diff << line_str
end
end
end
diff.chomp
end
def debug
demodularize = Proc.new {|obj| obj.class.name[/\w+$/]}
each do |diff_block|
print "-" * 40, ' ', demodularize.call(diff_block)
puts
puts diff_block.map {|line|
# "%5d" % line.old_number +
"%8s" % "[#{line.old_number || '.'} #{line.new_number || '.'}]" +
" [#{demodularize.call(line)}#{'(i)' if line.inline_changes?}]" +
line
}.join("\n")
puts "-" * 40, ' '
end
nil
end
end
class Line < String
class << self
def add(line, line_number, inline = false, offsets = [])
AddLine.new(line, line_number, inline, offsets)
end
def rem(line, line_number, inline = false, offsets = [])
RemLine.new(line, line_number, inline, offsets)
end
def unmod(line, old_number, new_number)
UnModLine.new(line, old_number, new_number)
end
def nonewline(line)
NonewlineLine.new(line)
end
def header(line)
HeaderLine.new(line)
end
end
def initialize(line, old_number = nil, new_number = nil)
super(line)
@old_number, @new_number = old_number, new_number
@inline = false
end
attr_reader :old_number, :new_number
def identifier
self.class.name[/\w+$/].gsub(/Line$/, "").downcase.to_sym
end
def inline_changes?
# Is set in the AddLine+RemLine subclasses
@inline
end
# returns the prefix, middle and postfix parts of a line with inline changes
def segments
return self.dup unless inline_changes?
prefix, changed = self.dup.split('\\0')
changed, postfix = changed.split('\\1')
[prefix, changed, postfix]
end
# Expand any inline changes with +prefix+ and +postfix+
def expand_inline_changes_with(prefix, postfix)
return self.dup unless inline_changes?
str = self.dup
str.sub!('\\0', prefix.to_s)
str.sub!('\\1', postfix.to_s)
str
end
def inspect
%Q{#<#{self.class.name} [#{old_number.inspect}-#{new_number.inspect}] "#{self}">}
end
end
class AddLine < Line
def initialize(line, line_number, inline = false, offsets = [])
super(line, nil, line_number)
@inline = inline
@offsets = offsets
end
attr_reader :offsets
end
class RemLine < Line
def initialize(line, line_number, inline = false, offsets = [])
super(line, line_number, nil)
@inline = inline
@offsets = offsets
end
attr_reader :offsets
end
class NonewlineLine < Line
def initialize(line = '\\ No newline at end of file')
super(line)
end
end
class UnModLine < Line
def initialize(line, old_number, new_number)
super(line, old_number, new_number)
end
end
class SepLine < Line
def initialize(line = '...')
super(line)
end
end
class HeaderLine < Line
def initialize(line)
super(line)
end
end
# This class is an array which contains Line objects. Just like Line
# classes, several Block classes inherit from Block. If all the lines
# in the block are added lines then it is an AddBlock. If all lines
# in the block are removed lines then it is a RemBlock. If the lines
# in the block are all unmodified then it is an UnMod block. If the
# lines in the block are a mixture of added and removed lines then
# it is a ModBlock. There are no blocks that contain a mixture of
# modified and unmodified lines.
class Block < Array
class << self
def add; AddBlock.new end
def rem; RemBlock.new end
def mod; ModBlock.new end
def unmod; UnModBlock.new end
def header; HeaderBlock.new end
def nonewline; NonewlineBlock.new end
end
end
#:stopdoc:#
class AddBlock < Block; end
class RemBlock < Block; end
class ModBlock < Block; end
class UnModBlock < Block; end
class SepBlock < Block; end
class HeaderBlock < Block; end
class NonewlineBlock < Block; end
#:startdoc:#
class Unified::Generator
# Extracts the line number info for a given diff section
LINE_NUM_RE = /^@@ [+-]([0-9]+)(?:,([0-9]+))? [+-]([0-9]+)(?:,([0-9]+))? @@/
LINE_TYPES = {'+' => :add, '-' => :rem, ' ' => :unmod, '\\' => :nonewline}
# Runs the generator on a diff and returns a Data object
def self.run(udiff)
raise ArgumentError, "Object must be enumerable" unless udiff.respond_to?(:each_line)
generator = new
udiff.each_line do |line|
begin
generator.process(line.chomp)
rescue ArgumentError => e
e.message =~ /^invalid byte sequence/ ? next : raise(e)
end
end
generator.finish
generator.data
end
def initialize
@buffer = []
@line_type = nil
@prev_line_type = nil
@offset = [0, 0]
@data = Data.new
self
end
# Finishes up with the generation and returns the Data object (could
# probably use a better name...maybe just #data?)
def data
@data
end
# This method is called once the generator is done with the unified
# diff. It is a finalizer of sorts. By the time it is called all data
# has been collected and processed.
def finish
# certain things could be set now that processing is done
#identify_block
end
def process(line)
if is_header_line?(line)
push Block.header
current_block << Line.header(line)
return
end
if line =~ LINE_NUM_RE
push Block.header
current_block << Line.header(line)
add_separator unless @offset[0].zero?
@line_type = nil
@offset = Array.new(2) { $3.to_i - 1 }
return
end
@line_type, line = LINE_TYPES[car(line)], cdr(line)
if @line_type == :add && @prev_line_type == :rem
@offset[0] -= 1
@buffer.push current_block.pop
@buffer.push line
process_block(:mod, false)
return
end
if LINE_TYPES.values.include?(@line_type)
@buffer.push(line.to_s)
process_block(@line_type, true)
end
end
protected
def is_header_line?(line)
return true if ['+++ ', '--- '].include?(line[0,4])
return true if line =~ /^(new|delete) file mode [0-9]+$/
return true if line =~ /^diff \-\-git/
return true if line =~ /^index \w+\.\.\w+( [0-9]+)?$/i
false
end
def process_block(diff_line_type, isnew = false)
@data.pop unless isnew
push Block.send(diff_line_type)
current_line = @buffer.pop
return unless current_line
# \\ No newline at end of file
if diff_line_type == :nonewline
current_block << Line.nonewline('\\ No newline at end of file')
return
end
if isnew
process_line(current_line, diff_line_type)
else
process_lines_with_differences(@buffer.shift, current_line)
raise "buffer exceeded #{@buffer.inspect}" unless @buffer.empty?
end
end
def process_line(line, type, inline = false)
case type
when :add
@offset[1] += 1
current_block << Line.send(type, line, @offset[1], inline, @offset.dup)
when :rem
@offset[0] += 1
current_block << Line.send(type, line, @offset[0], inline, @offset.dup)
# when :rmod
# @offset[0] += 1
# @offset[1] += 1 # TODO: is that really correct?
# current_block << Line.send(@prev_line_type, line, @offset[0])
when :unmod
@offset[0] += 1
@offset[1] += 1
current_block << Line.send(type, line, *@offset)
end
@prev_line_type = type
end
# TODO Needs a better name...it does process a line (two in fact) but
# its primary function is to add a Rem and an Add pair which
# potentially have inline changes
def process_lines_with_differences(oldline, newline)
start, ending = get_change_extent(oldline, newline)
if start.zero? && ending.zero?
process_line(oldline, :rem, false) # -
process_line(newline, :add, false) # +
else
# -
line = inline_diff(oldline, start, ending)
process_line(line, :rem, true)
# +
line = inline_diff(newline, start, ending)
process_line(line, :add, true)
end
end
# Inserts string formating characters around the section of a string
# that differs internally from another line so that the Line class
# can insert the desired formating
def inline_diff(line, start, ending)
if start != 0 || ending != 0
last = ending + line.length
str = line[0...start] + '\0' + line[start...last] + '\1' + line[last...line.length]
end
str || line
end
def add_separator
push SepBlock.new
current_block << SepLine.new
end
def car(line)
line[0,1]
end
def cdr(line)
line[1..-1]
end
# Returns the current Block object
def current_block
@data.last
end
# Adds a Line object onto the current Block object
def push(line)
@data.push line
end
# Determines the extent of differences between two string. Returns
# an array containing the offset at which changes start, and then
# negative offset at which the chnages end. If the two strings have
# neither a common prefix nor a common suffic, [0, 0] is returned.
def get_change_extent(str1, str2)
start = 0
limit = [str1.size, str2.size].sort.first
while start < limit and str1[start, 1] == str2[start, 1]
start += 1
end
ending = -1
limit -= start
while -ending <= limit and str1[ending, 1] == str2[ending, 1]
ending -= 1
end
return [start, ending + 1]
end
end
end

View File

@ -0,0 +1,11 @@
module Diff
module Display #:nodoc:
module VERSION #:nodoc:
MAJOR = 0
MINOR = 0
TINY = 1
STRING = [MAJOR, MINOR, TINY].join('.')
end
end
end

View File

@ -0,0 +1,92 @@
module Diff
module Renderer
class Base
def render(data)
result = []
data.each do |block|
result << send("before_" + classify(block), block)
result << block.map { |line| send(classify(line), line) }
result << send("after_" + classify(block), block)
end
result.compact.join(new_line)
end
def before_headerblock(block)
end
def before_unmodblock(block)
end
def before_modblock(block)
end
def before_remblock(block)
end
def before_addblock(block)
end
def before_sepblock(block)
end
def before_nonewlineblock(block)
end
def headerline(line)
line
end
def unmodline(line)
line
end
def remline(line)
line
end
def addline(line)
line
end
def sepline(line)
end
def nonewlineline(line)
line
end
def after_headerblock(block)
end
def after_unmodblock(block)
end
def after_modblock(block)
end
def after_remblock(block)
"</div>"
end
def after_addblock(block)
"</div>"
end
def after_sepblock(block)
end
def after_nonewlineblock(block)
end
def new_line
""
end
protected
def classify(object)
object.class.name[/\w+$/].downcase
end
end
end
end

View File

@ -0,0 +1,93 @@
module Diff
module Renderer
class Diff < Base
def before_addblock(block)
%q{<tbody class="add"><tr>}
end
def before_remblock(block)
%Q{<tbody class="rem"><tr>}
end
def before_modblock(block)
%Q{<tbody class="mod"><tr>}
end
def before_unmodblock(block)
%Q{<tbody class="unmod"><tr>}
end
def before_sepblock(block)
%Q{<tbody class="sep"><tr>}
end
# After blocks
def after_addblock(block)
"</tbody>"
end
def after_remblock(block)
"</tbody>"
end
def after_modblock(block)
"</tbody>"
end
def after_unmodblock(block)
"</tbody>"
end
def after_sepblock(block)
"</tbody>"
end
# Before lines
def addline(line)
# adds go on the right
%Q{<th class="line-numbers">#{line.old_number}</th>} +
%Q{<td class="code ins"></td>} +
%Q{<th class="line-numbers">#{line.new_number}</th>} +
%Q{<td class="code ins"><ins>#{line}</ins></td></tr>}
end
def remline(line)
# rems go on the left (hide the right side)
%Q{<th class="line-numbers">#{line.old_number}</th>} +
%Q{<td class="code del"><del>#{line}</del></td>} +
%Q{<th class="line-numbers">#{line.new_number}</th>} +
%Q{<td class="code del hidden"><del>#{line}</del></td></tr>}
end
def modline(line)
# TODO: figure how we best display these
# %Q{<th class="line-numbers">#{line.old_number}</th>} +
# %Q{<td class="code changed mod">#{render_line(line)}</td>} +
# %Q{<th class="line-numbers">#{line.new_number}</th>} +
# %Q{<td class="code changed mod">#{render_line(line)}</td></tr>}
end
def unmodline(line)
# unmods goes on both sides
%Q{<th class="line-numbers">#{line.old_number}</th>} +
%Q{<td class="code unchanged unmod">#{line}</td>} +
%Q{<th class="line-numbers">#{line.new_number}</th>} +
%Q{<td class="code unchanged unmod">#{line}</td></tr>}
end
def sepline(line)
%Q{<th class="line-numbers line-num-cut">&hellip;</th>} +
%Q{<td class="code cut-line"></td>} +
%Q{<th class="line-numbers line-num-cut">&hellip;</th>} +
%Q{<td class="code cut-line"></td></tr>}
end
def nonewlineline(line)
%Q{<th class="line-numbers line-num-cut">&hellip;</th>} +
%Q{<td class="code mod"></td>} +
%Q{<th class="line-numbers line-num-cut">&hellip;</th>} +
%Q{<td class="code mod">#{line}</td></tr>}
end
end
end
end

14
vendor/plugins/diff-display/script/destroy vendored Executable file
View File

@ -0,0 +1,14 @@
#!/usr/bin/env ruby
APP_ROOT = File.join(File.dirname(__FILE__), '..')
begin
require 'rubigen'
rescue LoadError
require 'rubygems'
require 'rubigen'
end
require 'rubigen/scripts/destroy'
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
RubiGen::Scripts::Destroy.new.run(ARGV)

14
vendor/plugins/diff-display/script/generate vendored Executable file
View File

@ -0,0 +1,14 @@
#!/usr/bin/env ruby
APP_ROOT = File.join(File.dirname(__FILE__), '..')
begin
require 'rubigen'
rescue LoadError
require 'rubygems'
require 'rubigen'
end
require 'rubigen/scripts/generate'
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
RubiGen::Scripts::Generate.new.run(ARGV)

74
vendor/plugins/diff-display/script/txt2html vendored Executable file
View File

@ -0,0 +1,74 @@
#!/usr/bin/env ruby
require 'rubygems'
begin
require 'newgem'
rescue LoadError
puts "\n\nGenerating the website requires the newgem RubyGem"
puts "Install: gem install newgem\n\n"
exit(1)
end
require 'redcloth'
require 'syntax/convertors/html'
require 'erb'
require File.dirname(__FILE__) + '/../lib/diff-display/version.rb'
version = Diff-display::VERSION::STRING
download = 'http://rubyforge.org/projects/diff-display'
class Fixnum
def ordinal
# teens
return 'th' if (10..19).include?(self % 100)
# others
case self % 10
when 1: return 'st'
when 2: return 'nd'
when 3: return 'rd'
else return 'th'
end
end
end
class Time
def pretty
return "#{mday}#{mday.ordinal} #{strftime('%B')} #{year}"
end
end
def convert_syntax(syntax, source)
return Syntax::Convertors::HTML.for_syntax(syntax).convert(source).gsub(%r!^<pre>|</pre>$!,'')
end
if ARGV.length >= 1
src, template = ARGV
template ||= File.join(File.dirname(__FILE__), '/../website/template.rhtml')
else
puts("Usage: #{File.split($0).last} source.txt [template.rhtml] > output.html")
exit!
end
template = ERB.new(File.open(template).read)
title = nil
body = nil
File.open(src) do |fsrc|
title_text = fsrc.readline
body_text = fsrc.read
syntax_items = []
body_text.gsub!(%r!<(pre|code)[^>]*?syntax=['"]([^'"]+)[^>]*>(.*?)</\1>!m){
ident = syntax_items.length
element, syntax, source = $1, $2, $3
syntax_items << "<#{element} class='syntax'>#{convert_syntax(syntax, source)}</#{element}>"
"syntax-temp-#{ident}"
}
title = RedCloth.new(title_text).to_html.gsub(%r!<.*?>!,'').strip
body = RedCloth.new(body_text).to_html
body.gsub!(%r!(?:<pre><code>)?syntax-temp-(\d+)(?:</code></pre>)?!){ syntax_items[$1.to_i] }
end
stat = File.stat(src)
created = stat.ctime
modified = stat.mtime
$stdout << template.result(binding)

1585
vendor/plugins/diff-display/setup.rb vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,34 @@
desc 'Release the website and new gem version'
task :deploy => [:check_version, :website, :release] do
puts "Remember to create SVN tag:"
puts "svn copy svn+ssh://#{rubyforge_username}@rubyforge.org/var/svn/#{PATH}/trunk " +
"svn+ssh://#{rubyforge_username}@rubyforge.org/var/svn/#{PATH}/tags/REL-#{VERS} "
puts "Suggested comment:"
puts "Tagging release #{CHANGES}"
end
desc 'Runs tasks website_generate and install_gem as a local deployment of the gem'
task :local_deploy => [:website_generate, :install_gem]
task :check_version do
unless ENV['VERSION']
puts 'Must pass a VERSION=x.y.z release version'
exit
end
unless ENV['VERSION'] == VERS
puts "Please update your version.rb to match the release version, currently #{VERS}"
exit
end
end
desc 'Install the package as a gem, without generating documentation(ri/rdoc)'
task :install_gem_no_doc => [:clean, :package] do
sh "#{'sudo ' unless Hoe::WINDOZE }gem install pkg/*.gem --no-rdoc --no-ri"
end
namespace :manifest do
desc 'Recreate Manifest.txt to include ALL files'
task :refresh do
`rake check_manifest | patch -p0 > Manifest.txt`
end
end

View File

@ -0,0 +1,7 @@
task :ruby_env do
RUBY_APP = if RUBY_PLATFORM =~ /java/
"jruby"
else
"ruby"
end unless defined? RUBY_APP
end

View File

@ -0,0 +1,17 @@
desc 'Generate website files'
task :website_generate => :ruby_env do
(Dir['website/**/*.txt'] - Dir['website/version*.txt']).each do |txt|
sh %{ #{RUBY_APP} script/txt2html #{txt} > #{txt.gsub(/txt$/,'html')} }
end
end
desc 'Upload website files to rubyforge'
task :website_upload do
host = "#{rubyforge_username}@rubyforge.org"
remote_dir = "/var/www/gforge-projects/#{PATH}/"
local_dir = 'website'
sh %{rsync -aCv #{local_dir}/ #{host}:#{remote_dir}}
end
desc 'Generate and upload website files'
task :website => [:website_generate, :website_upload, :publish_docs]

View File

@ -0,0 +1,590 @@
--- unified.rb (revision 620)
+++ unified.rb (revision 644)
@@ -1,298 +1,390 @@
module Diff
module Display
module Unified
-
- LINE_RE = /@@ [+-]([0-9]+),([0-9]+) [+-]([0-9]+),([0-9]+) @@/
- TABWIDTH = 4
- SPACE = ' ' #'&nbsp;'
- # By defaul don't wrap inline diffs in anything
- INLINE_REM_OPEN = "\e[4;33m"
- INLINE_REM_CLOSE = "\e[m"
- INLINE_ADD_OPEN = "\e[4;35m"
- INLINE_ADD_CLOSE = "\e[m"
- ESCAPE_HTML = false
-
class Line < String
- attr_reader :add_lineno, :rem_lineno
- def initialize(line, type, add_lineno, rem_lineno = add_lineno)
+ def initialize(line, line_number)
super(line)
- @type = type
- @add_lineno = add_lineno
- @rem_lineno = rem_lineno
+ @line_number = line_number
+ self
end
+ def contains_inline_change?
+ @inline
+ end
+
def number
- add_lineno ? add_lineno : rem_lineno
+ @line_number
end
- def type
- @type
+ def decorate(&block)
+ yield self
end
- class << self
- def add(line, add_lineno)
- AddLine.new(line, add_lineno)
+ def inline_add_open; '' end
+ def inline_add_close; '' end
+ def inline_rem_open; '' end
+ def inline_rem_close; '' end
+
+ protected
+
+ def escape
+ self
end
- def rem(line, rem_lineno)
- RemLine.new(line, rem_lineno)
+ def expand
+ escape.gsub("\t", ' ' * tabwidth).gsub(/ ( +)|^ /) do |match|
+ (space + ' ') * (match.size / 2) +
+ space * (match.size % 2)
+ end
end
- def unmod(line, lineno)
- UnModLine.new(line, lineno)
+ def tabwidth
+ 4
end
- def mod(line, lineno)
- ModLine.new(line, lineno)
+
+ def space
+ ' '
end
+
+ class << self
+ def add(line, line_number, inline = false)
+ AddLine.new(line, line_number, inline)
+ end
+
+ def rem(line, line_number, inline = false)
+ RemLine.new(line, line_number, inline)
+ end
+
+ def unmod(line, line_number)
+ UnModLine.new(line, line_number)
+ end
end
end
class AddLine < Line
- def initialize(line, add_lineno)
- super(line, 'add', add_lineno, nil)
+ def initialize(line, line_number, inline = false)
+ line = inline ? line % [inline_add_open, inline_add_close] : line
+ super(line, line_number)
+ @inline = inline
+ self
end
end
class RemLine < Line
- def initialize(line, rem_lineno)
- super(line, 'rem', nil, rem_lineno)
+ def initialize(line, line_number, inline = false)
+ line = inline ? line % [inline_rem_open, inline_rem_close] : line
+ super(line, line_number)
+ @inline = inline
+ self
end
end
class UnModLine < Line
- def initialize(line, lineno)
- super(line, 'unmod', lineno)
+ def initialize(line, line_number)
+ super(line, line_number)
end
end
- class ModLine < Line
- def initialize(line, lineno)
- super(line, 'mod', lineno)
+ class SepLine < Line
+ def initialize(line = '...')
+ super(line, nil)
end
end
+ # This class is an array which contains Line objects. Just like Line
+ # classes, several Block classes inherit from Block. If all the lines
+ # in the block are added lines then it is an AddBlock. If all lines
+ # in the block are removed lines then it is a RemBlock. If the lines
+ # in the block are all unmodified then it is an UnMod block. If the
+ # lines in the block are a mixture of added and removed lines then
+ # it is a ModBlock. There are no blocks that contain a mixture of
+ # modified and unmodified lines.
class Block < Array
- def initialize(type)
- super(0)
- @type = type
+ def initialize
+ super
+ @line_types = []
end
def <<(line_object)
super(line_object)
- (@line_types ||= []).push(line_object.type)
- @line_types.uniq!
+ line_class = line_object.class.name[/\w+$/]
+ @line_types.push(line_class) unless @line_types.include?(line_class)
self
end
+ def decorate(&block)
+ yield self
+ end
+
def line_types
@line_types
end
- def type
- @type
+ class << self
+ def add; AddBlock.new end
+ def rem; RemBlock.new end
+ def mod; ModBlock.new end
+ def unmod; UnModBlock.new end
end
end
- class Generator < Array
+ class AddBlock < Block; end
+ class RemBlock < Block; end
+ class ModBlock < Block; end
+ class UnModBlock < Block; end
+ class SepBlock < Block; end
+ # This data object contains the generated diff data structure. It is an
+ # array of Block objects which are themselves arrays of Line objects. The
+ # Generator class returns a Data instance object after it is done
+ # processing the diff.
+ class Data < Array
+ def initialize
+ super
+ end
+
+ def debug
+ demodularize = Proc.new {|obj| obj.class.name[/\w+$/]}
+ each do |diff_block|
+ print "*" * 40, ' ', demodularize.call(diff_block)
+ puts
+ puts diff_block.map {|line|
+ "%5d" % line.number +
+ " [#{demodularize.call(line)}]" +
+ line
+ }.join("\n")
+ puts "*" * 40, ' '
+ end
+ end
+
+ end
+
+ # Processes the diff and generates a Data object which contains the
+ # resulting data structure.
+ class Generator
+
+ # Extracts the line number info for a given diff section
+ LINE_NUM_RE = /@@ [+-]([0-9]+),([0-9]+) [+-]([0-9]+),([0-9]+) @@/
+ LINE_TYPES = {'+' => :add, '-' => :rem, ' ' => :unmod}
+
class << self
- def run(udiff, options = {})
- generator = new(options)
- udiff.split("\n").each {|line| generator.build(line) }
- generator.close
- generator
+
+ # Runs the generator on a diff and returns a Data object without
+ # instantiating a Generator object
+ def run(udiff)
+ raise ArgumentError, "Object must be enumerable" unless udiff.respond_to?(:each)
+ generator = new
+ udiff.each {|line| generator.process(line.chomp)}
+ generator.render
end
end
- def initialize(options = {})
- super(0)
- default_options = {:inline_add_open => INLINE_ADD_OPEN,
- :inline_add_close => INLINE_ADD_CLOSE,
- :inline_rem_open => INLINE_REM_OPEN,
- :inline_rem_close => INLINE_REM_CLOSE,
- :escape_html => ESCAPE_HTML,
- :tabwidth => TABWIDTH,
- :space => SPACE}
-
- @options = default_options.merge(options)
- @block = []
- @ttype = nil
- @p_block = []
- @p_type = nil
- @changeno = -1
- @blockno = 0
+ def initialize
+ @buffer = []
+ @prev_buffer = []
+ @line_type = nil
+ @prev_line_type = nil
@offset_base = 0
@offset_changed = 0
+ @data = Diff::Display::Unified::Data.new
+ self
end
- def current_block
- last
+ # Operates on a single line from the diff and passes along the
+ # collected data to the appropriate method for further processing. The
+ # cycle of processing is in general:
+ #
+ # process --> identify_block --> process_block --> process_line
+ #
+ def process(line)
+ return if ['++', '--'].include?(line[0,2])
+
+ if match = LINE_NUM_RE.match(line)
+ identify_block
+ push SepBlock.new and current_block << SepLine.new unless @offset_changed.zero?
+ @line_type = nil
+ @offset_base = match[1].to_i - 1
+ @offset_changed = match[3].to_i - 1
+ return
+ end
+
+ new_line_type, line = LINE_TYPES[car(line)], cdr(line)
+
+ # Add line to the buffer if it's the same diff line type
+ # as the previous line
+ #
+ # e.g.
+ #
+ # + This is a new line
+ # + As is this one
+ # + And yet another one...
+ #
+ if new_line_type.eql?(@line_type)
+ @buffer.push(line)
+ else
+ # Side by side inline diff
+ #
+ # e.g.
+ #
+ # - This line just had to go
+ # + This line is on the way in
+ #
+ if new_line_type.eql?(LINE_TYPES['+']) and @line_type.eql?(LINE_TYPES['-'])
+ @prev_buffer = @buffer
+ @prev_line_type = @line_type
+ else
+ identify_block
+ end
+ @buffer = [line]
+ @line_type = new_line_type
+ end
end
+ # Finishes up with the generation and returns the Data object (could
+ # probably use a better name...maybe just #data?)
def render
close
- self
+ @data
end
-
- def escape(text)
- return '' unless text
- return text unless @options[:escape_html]
- text.gsub('&', '&amp;').
- gsub('<', '&lt;' ).
- gsub('>', '&gt;' ).
- gsub('"', '&#34;')
- end
- def expand(text)
- escape(text).gsub(/ ( +)|^ /) do |match|
- (@options[:space] + ' ') * (match.size / 2) +
- @options[:space] * (match.size % 2)
- end
- end
+ protected
- def inline_diff(line, start, ending, change)
- expand(line[0, start]) +
- change +
- expand(line[ending, ending.abs])
- end
+ def identify_block
+ if @prev_line_type.eql?(LINE_TYPES['-']) and @line_type.eql?(LINE_TYPES['+'])
+ process_block(:mod, {:old => @prev_buffer, :new => @buffer})
+ else
+ if LINE_TYPES.values.include?(@line_type)
+ process_block(@line_type, {:new => @buffer})
+ end
+ end
- def write_line(oldline, newline)
- start, ending = get_change_extent(oldline, newline)
- change = ''
- if oldline.size > start - ending
- change = @options[:inline_rem_open] +
- expand(oldline[start...ending]) +
- @options[:inline_rem_close]
+ @prev_line_type = nil
end
- line = inline_diff(oldline, start, ending, change)
- current_block << Line.rem(line, @offset_base)
+ def process_block(diff_line_type, blocks = {:old => nil, :new => nil})
+ push Block.send(diff_line_type)
+ old, new = blocks[:old], blocks[:new]
- change = ''
- if newline.size > start - ending
- change = @options[:inline_add_open] +
- expand(newline[start...ending]) +
- @options[:inline_add_close]
+ # Mod block
+ if diff_line_type.eql?(:mod) and old.size & new.size == 1
+ process_line(old.first, new.first)
+ return
+ end
+
+ if old and not old.empty?
+ old.each do |line|
+ @offset_base += 1
+ current_block << Line.send(@prev_line_type, line, @offset_base)
+ end
+ end
+
+ if new and not new.empty?
+ new.each do |line|
+ @offset_changed += 1
+ current_block << Line.send(@line_type, line, @offset_changed)
+ end
+ end
end
- line = inline_diff(newline, start, ending, change)
- current_block << Line.add(line, @offset_changed)
- end
+ # TODO Needs a better name...it does process a line (two in fact) but
+ # its primary function is to add a Rem and an Add pair which
+ # potentially have inline changes
+ def process_line(oldline, newline)
+ start, ending = get_change_extent(oldline, newline)
- def write_block(dtype, old = nil, new = nil)
- push Block.new(dtype)
+ # -
+ line = inline_diff(oldline, start, ending)
+ current_block << Line.rem(line, @offset_base += 1, true)
- if dtype == 'mod' and old.size == 1 and new.size == 1
- write_line(old.first, new.first)
- return
+ # +
+ line = inline_diff(newline, start, ending)
+ current_block << Line.add(line, @offset_changed += 1, true)
end
- if old and not old.empty?
- old.each do |e|
- current_block << Line.send(dtype, expand(e), @offset_base)
- @offset_base += 1
- end
+ # Inserts string formating characters around the section of a string
+ # that differs internally from another line so that the Line class
+ # can insert the desired formating
+ def inline_diff(line, start, ending)
+ line[0, start] +
+ '%s' + extract_change(line, start, ending) + '%s' +
+ line[ending, ending.abs]
end
- if new and not new.empty?
- new.each do |e|
- current_block << Line.send(dtype, expand(e), @offset_changed)
- @offset_changed += 1
- end
+ def extract_change(line, start, ending)
+ line.size > (start - ending) ? line[start...ending] : ''
end
- end
- def print_block
- if @p_type.eql?('-') and @ttype.eql?('+')
- write_block('mod', @p_block, @block)
- else
- case @ttype
- when '+'
- write_block('add', @block)
- when '-'
- write_block('rem', @block)
- when ' '
- write_block('unmod', @block)
- end
+ def car(line)
+ line[0,1]
end
- @block = @p_block = []
- @p_type = ' '
- @blockno += 1
- end
+ def cdr(line)
+ line[1..-1]
+ end
- def build(text)
- # TODO Names of the files and their versions go here perhaps
+ # Returns the current Block object
+ def current_block
+ @data.last
+ end
- return if ['++', '--'].include?(text[0,2])
+ # Adds a Line object onto the current Block object
+ def push(line)
+ @data.push line
+ end
- if match = LINE_RE.match(text)
- print_block
- @changeno += 1
- @blockno = 0
- @offset_base = match[1].to_i - 1
- @offset_changed = match[3].to_i - 1
- return
+ # This method is called once the generator is done with the unified
+ # diff. It is a finalizer of sorts. By the time it is called all data
+ # has been collected and processed.
+ def close
+ # certain things could be set now that processing is done
+ identify_block
end
- # Set ttype to first character of line
- ttype = text[0, 1]
- text = text[1..-1]
- text = text.gsub("\t", ' ' * @options[:tabwidth]) if text
- # If it's the same type of mod as the last line push this line onto the
- # block stack
- if ttype.eql?(@ttype)
- @block.push(text)
- else
- # If we have a side by side subtraction/addition
- if ttype == '+' and @ttype == '-'
- @p_block = @block
- @p_type = @ttype
- else
- print_block
+ # Determines the extent of differences between two string. Returns
+ # an array containing the offset at which changes start, and then
+ # negative offset at which the chnages end. If the two strings have
+ # neither a common prefix nor a common suffic, [0, 0] is returned.
+ def get_change_extent(str1, str2)
+ start = 0
+ limit = [str1.size, str2.size].sort.first
+ while start < limit and str1[start, 1] == str2[start, 1]
+ start += 1
end
- @block = [text]
- @ttype = ttype
+ ending = -1
+ limit -= start
+ while -ending <= limit and str1[ending, 1] == str2[ending, 1]
+ ending -= 1
+ end
+
+ return [start, ending + 1]
end
- end
+ end
- def debug
- each do |diff_block|
- print "*" * (40 - diff_block.type.size / 2), ' ', diff_block.type
- puts
- puts diff_block.map {|line| "#{line.number}" << line << " [#{line.type}]"}.join("\n")
- print "Line types:"
- puts diff_block.line_types.join(", ")
- puts
- end
+ # Mostly a convinience class at this point that just overwrites various
+ # customization methods
+ class HTMLGenerator < Generator
+
+ # This and the space method now don't work/make sense now that those
+ # methods are part of the Line class and there certainly won't be an
+ # HTMLLine class
+ def escape(text)
+ text.gsub('&', '&amp;').
+ gsub('<', '&lt;' ).
+ gsub('>', '&gt;' ).
+ gsub('"', '&#34;')
end
- def close
- # certain things could be set now that processing is done
- print_block
+ def space
+ '&nbsp;'
end
- # Determines the extent of differences between two string. Returns
- # an array containing the offset at which changes start, and then
- # negative offset at which the chnages end. If the two strings have
- # neither a common prefix nor a common suffic, [0, 0] is returned.
- def get_change_extent(str1, str2)
- start = 0
- limit = [str1.size, str2.size].sort.first
- while start < limit and str1[start, 1] == str2[start, 1]
- start += 1
- end
- ending = -1
- limit -= start
- while -ending <= limit and str1[ending, 1] == str2[ending, 1]
- ending -= 1
- end
+ end
- return [start, ending + 1]
- end
+ # See doc string for HTMLGenerator
+ class ASCIIGenerator < Generator
end
+
end
end
end
-

View File

@ -0,0 +1,11 @@
--- a/one.txt
+++ b/one.txt
@@ -193,7 +193,21 @@ module One
pre yada yada
-
+
+ foo
+ bar
+ baz
+
post yada yada

View File

@ -0,0 +1,14 @@
diff --git a/strokedb-ruby/lib/stores/chainable_storage.rb b/strokedb-ruby/lib/stores/chainable_storage.rb
index 5326c92..1e2de8f 100644
--- a/strokedb-ruby/lib/stores/chainable_storage.rb
+++ b/strokedb-ruby/lib/stores/chainable_storage.rb
@@ -44,9 +44,7 @@ module StrokeDB
def save_with_chained_storages!(chunk,source=nil)
perform_save!(chunk)
(@chained_storages||{}).each_pair do |storage,savings|
- unless storage == chunk
- savings << chunk unless savings.include?(chunk)
- end
+ savings << chunk unless storage == chunk || savings.include?(chunk)
end
end

View File

@ -0,0 +1,4 @@
--- a.txt 2008-02-03 16:28:06.000000000 +0100
+++ b.txt 2008-02-03 16:28:14.000000000 +0100
@@ -0,0 +1 @@
+foo

View File

@ -0,0 +1,4 @@
--- b.txt 2008-02-03 16:28:14.000000000 +0100
+++ a.txt 2008-02-03 16:28:06.000000000 +0100
@@ -1 +0,0 @@
-foo

View File

@ -0,0 +1,21 @@
diff --git a/spec/fixtures/multiple_rems_then_add.diff b/spec/fixtures/multiple_rems_then_add.diff
new file mode 100644
index 0000000..f5fd3f8
--- /dev/null
+++ b/spec/fixtures/multiple_rems_then_add.diff
@@ -0,0 +1,14 @@
+diff --git a/strokedb-ruby/lib/stores/chainable_storage.rb b/strokedb-ruby/lib/stores/chainable_storage.rb
+index 5326c92..1e2de8f 100644
+--- a/strokedb-ruby/lib/stores/chainable_storage.rb
++++ b/strokedb-ruby/lib/stores/chainable_storage.rb
+@@ -44,9 +44,7 @@ module StrokeDB
+ def save_with_chained_storages!(chunk,source=nil)
+ perform_save!(chunk)
+ (@chained_storages||{}).each_pair do |storage,savings|
+- unless storage == chunk
+- savings << chunk unless savings.include?(chunk)
+- end
++ savings << chunk unless storage == chunk || savings.include?(chunk)
+ end
+ end
\ No newline at end of file

View File

@ -0,0 +1,12 @@
diff --git a/History.txt b/History.txt
index 0ed7358..622c384 100644
--- a/History.txt
+++ b/History.txt
@@ -1,4 +1,5 @@
== 0.0.1 2008-01-28
-* 1 major enhancement:
- * Initial release
+* 2 major enhancements:
+ * The Initial release
+ * stuff added

View File

@ -0,0 +1,7 @@
--- a.txt 2009-03-04 17:20:15.000000000 +0100
+++ b.txt 2009-03-04 17:20:33.000000000 +0100
@@ -1,3 +1,3 @@
foo
-bar
+baz
baz

View File

@ -0,0 +1,8 @@
diff --git a/text.txt b/text.txt
index 7b57bd2..7010a82 100644
--- a/text.txt
+++ b/text.txt
@@ -1 +1 @@
-some text
+asdasdsa
\ No newline at end of file

View File

@ -0,0 +1,12 @@
# encoding: utf-8
require File.dirname(__FILE__) + "/test_helper"
class TestApi < Test::Unit::TestCase
include DiffFixtureHelper
def test_it_has_a_simple_API
diff = Diff::Display::Unified.new(load_diff("simple"))
diff.render(Diff::Renderer::Base.new)
end
end

View File

@ -0,0 +1,101 @@
# encoding: utf-8
require File.dirname(__FILE__) + "/test_helper"
class TestBaseRenderer < Test::Unit::TestCase
include DiffFixtureHelper
def setup
@data = Diff::Display::Unified::Generator.run(load_diff("big"))
@base_renderer = Diff::Renderer::Base.new
end
def test_it_classifies_a_classname
assert_equal "remblock", @base_renderer.send(:classify, Diff::Display::RemBlock.new)
end
def test_it_calls_the_before_headerblock
@base_renderer.expects(:before_headerblock).at_least_once
@base_renderer.render(@data)
end
# def test_it_calls_the_before_sepblock
# @base_renderer.expects(:before_sepblock).at_least_once
# @base_renderer.render(@data)
# end
# def test_it_calls_the_before_modblock
# @base_renderer.expects(:before_modblock).at_least_once
# @base_renderer.render(@data)
# end
def test_calls_the_before_unmodblock
@base_renderer.expects(:before_unmodblock).at_least_once
@base_renderer.render(@data)
end
def test_should_calls_the_before_addblock
@base_renderer.expects(:before_addblock).at_least_once
@base_renderer.render(@data)
end
def test_calls_the_before_remblock
@base_renderer.expects(:before_remblock).at_least_once
@base_renderer.render(@data)
end
def test_calls_headerline
@base_renderer.expects(:headerline).at_least_once
@base_renderer.render(@data)
end
def test_calls_unmodline
@base_renderer.expects(:unmodline).at_least_once
@base_renderer.render(@data)
end
def test_calls_addline
@base_renderer.expects(:addline).at_least_once
@base_renderer.render(@data)
end
def test_calls_remline
@base_renderer.expects(:remline).at_least_once
@base_renderer.render(@data)
end
def test_calls_the_after_headerblock
@base_renderer.expects(:after_headerblock).at_least_once
@base_renderer.render(@data)
end
# def test_calls_the_after_sepblock
# @base_renderer.expects(:after_sepblock).at_least_once
# @base_renderer.render(@data)
# end
# def test_calls_the_after_modblock
# @base_renderer.expects(:after_modblock).at_least_once
# @base_renderer.render(@data)
# end
def test_calls_the_after_unmodblock
@base_renderer.expects(:after_unmodblock).at_least_once
@base_renderer.render(@data)
end
def test_calls_the_after_addblock
@base_renderer.expects(:after_addblock).at_least_once
@base_renderer.render(@data)
end
def test_calls_the_after_remblock
@base_renderer.expects(:after_remblock).at_least_once
@base_renderer.render(@data)
end
def test_renders_a_basic_datastructure
output = @base_renderer.render(@data)
assert_not_equal nil, output
end
end

View File

@ -0,0 +1,100 @@
# encoding: utf-8
require File.dirname(__FILE__) + "/test_helper"
class TestDatastructure < Test::Unit::TestCase
include DiffFixtureHelper
# Data
def test_behaves_like_an_array
data = Diff::Display::Data.new
data << "foo"
data.push "bar"
assert_equal ["foo", "bar"], data
end
# Line
def test_initializes_with_an_old_line_number
line = Diff::Display::Line.new("foo", 12)
assert_equal 12, line.old_number
end
def test_initializes_with_numbers
line = Diff::Display::Line.new("foo", 12, 13)
assert_equal 12, line.old_number
assert_equal 13, line.new_number
end
def test_has_a_class_method_for_creating_an_addline
line = Diff::Display::Line.add("foo", 7)
assert_instance_of Diff::Display::AddLine, line
end
def test_has_a_class_method_for_creating_a_remline
line = Diff::Display::Line.rem("foo", 7)
assert_instance_of Diff::Display::RemLine, line
end
def test_has_a_class_method_for_creating_a_unmodline
line = Diff::Display::Line.unmod("foo", 7, 8)
assert_instance_of Diff::Display::UnModLine, line
end
def test_has_a_class_method_for_creating_a_header_line
line = Diff::Display::Line.header("foo")
assert_instance_of Diff::Display::HeaderLine, line
end
def test_has_an_identifier
assert_equal :add, Diff::Display::Line.add("foo", 7).identifier
assert_equal :rem, Diff::Display::Line.rem("foo", 7).identifier
assert_equal :unmod, Diff::Display::Line.unmod("foo", 7, 8).identifier
assert_equal :header, Diff::Display::Line.header("foo").identifier
assert_equal :nonewline, Diff::Display::Line.nonewline("foo").identifier
end
def test_expands_inline_changes
line = Diff::Display::AddLine.new('the \\0quick \\1brown fox', 42, true)
expanded = line.expand_inline_changes_with("START", "END")
assert_equal "the STARTquick ENDbrown fox", expanded.to_s
end
def test_segments
line = Diff::Display::AddLine.new('the \\0quick \\1brown fox', 42, true)
assert_equal ["the ", "quick ", "brown fox"], line.segments
end
# Block
def test_block_behaves_like_an_array
block = Diff::Display::Block.new
block.push 1,2,3
assert_equal 3, block.size
assert_equal [1,2,3], block
end
def test_has_class_method_for_creating_an_addblock
block = Diff::Display::Block.add
assert_instance_of Diff::Display::AddBlock, block
end
def test_has_class_method_for_creating_an_remblock
block = Diff::Display::Block.rem
assert_instance_of Diff::Display::RemBlock, block
end
def test_has_class_method_for_creating_an_modblock
block = Diff::Display::Block.mod
assert_instance_of Diff::Display::ModBlock, block
end
def test_has_class_method_for_creating_an_unmodblock
block = Diff::Display::Block.unmod
assert_instance_of Diff::Display::UnModBlock, block
end
def test_has_class_method_for_creating_an_headerblock
block = Diff::Display::Block.header
assert_instance_of Diff::Display::HeaderBlock, block
end
end

View File

@ -0,0 +1,14 @@
# encoding: utf-8
require File.dirname(__FILE__) + "/test_helper"
class TestDiffRenderer < Test::Unit::TestCase
include DiffFixtureHelper
def test_it_renders_a_diff_back_to_its_original_state
data = Diff::Display::Unified::Generator.run(load_diff("simple"))
base_renderer = Diff::Renderer::Diff.new
assert_equal load_diff("simple"), base_renderer.render(data)
end
end

View File

@ -0,0 +1,148 @@
# encoding: utf-8
require File.dirname(__FILE__) + "/test_helper"
class TestGenerator < Test::Unit::TestCase
include DiffFixtureHelper
def setup
@generator = Diff::Display::Unified::Generator.new
end
def test_generator_run_raises_if_doesnt_get_a_enumerable_object
assert_raises(ArgumentError) do
Diff::Display::Unified::Generator.run(nil)
end
end
def test_generator_run_processes_each_line_in_the_diff
Diff::Display::Unified::Generator.expects(:new).returns(@generator)
@generator.expects(:process).with("foo")
@generator.expects(:process).with("bar")
Diff::Display::Unified::Generator.run("foo\nbar")
end
def test_generator_run_returns_the_data
Diff::Display::Unified::Generator.expects(:new).returns(@generator)
generated = Diff::Display::Unified::Generator.run("foo\nbar")
assert_instance_of Diff::Display::Data, generated
end
def test_the_returned_that_object_is_in_parity_with_the_diff
%w[multiple_rems_then_add only_rem simple multiple_adds_after_rem only_add pseudo_recursive simple_oneliner].each do |diff|
data = Diff::Display::Unified::Generator.run(load_diff(diff))
assert_equal load_diff(diff).chomp, data.to_diff, "failed on #{diff}"
end
end
def test_big
diff_data = load_diff("big")
data = Diff::Display::Unified::Generator.run(diff_data)
assert_equal diff_data.chomp, data.to_diff
end
def test_multiple_rems_and_an_add_is_in_parity
diff_data = load_diff("multiple_rems_then_add")
data = Diff::Display::Unified::Generator.run(diff_data)
assert_equal diff_data.chomp, data.to_diff
end
def test_doesnt_parse_linenumbers_that_isnt_part_if_the_diff
range = 1..14
expected_lines = range.to_a.map{|l| [nil, l] }
assert_equal expected_lines, line_numbers_for(:pseudo_recursive).compact
end
def test_parses_no_newline_at_end_of_file
diff_data = load_diff("pseudo_recursive")
data = Diff::Display::Unified::Generator.run(diff_data)
assert_equal diff_data.chomp, data.to_diff
assert_instance_of Diff::Display::NonewlineBlock, data.last
assert_equal 1, data.last.size
assert_instance_of Diff::Display::NonewlineLine, data.last[0]
assert_equal '\\ No newline at end of file', data.last[0]
end
def test_a_changed_string_becomes_a_modblock
diff_data = load_diff("simple_oneliner")
data = Diff::Display::Unified::Generator.run(diff_data)
assert_equal diff_data.chomp, data.to_diff
assert_equal 6, data.size
modblock = data[4]
assert_instance_of Diff::Display::ModBlock, modblock
assert_equal 2, modblock.size, modblock.inspect
rem = modblock[0]
add = modblock[1]
assert_instance_of Diff::Display::RemLine, rem
assert_instance_of Diff::Display::AddLine, add
assert add.inline_changes?
assert rem.inline_changes?
end
# line numbering
def test_numbers_correctly_for_multiple_adds_after_rem
expected = [
[193, 193],
[194, nil],
[nil, 194],
[nil, 195],
[nil, 196],
[nil, 197],
[nil, 198],
[195, 199]
]
assert_equal expected, line_numbers_for(:multiple_adds_after_rem)
end
def test_numbers_correctly_for_simple
expected = [
[1, 1],
[2, 2],
[3, nil],
[4, nil],
[nil, 3],
[nil, 4],
[nil, 5],
]
assert_equal expected, line_numbers_for(:simple)
end
def test_rewrite_line_does_not_set_inline_changes
diff_data = load_diff("simple_rewrite")
data = Diff::Display::Unified::Generator.run(diff_data)
assert_equal diff_data.chomp, data.to_diff
modblock = data[5]
assert_instance_of Diff::Display::ModBlock, modblock
assert_instance_of Diff::Display::RemLine, rem = modblock[0]
assert_instance_of Diff::Display::AddLine, add = modblock[1]
assert !rem.inline_changes?, "rem has inline changes"
assert !add.inline_changes?, "add has inline_changes"
end
def test_should_not_explode_on_invalid_byte_sequences
diff_data = "+s\x8B\xB5\x13\n"
assert_nothing_raised do
Diff::Display::Unified::Generator.run(diff_data)
end
end
protected
def line_numbers_for(diff)
diff_data = load_diff(diff)
data = Diff::Display::Unified::Generator.run(diff_data)
linenos = []
data.each do |blk|
blk.each do |line|
next if line.class == Diff::Display::HeaderLine
next if line.class == Diff::Display::NonewlineLine
linenos << [line.old_number, line.new_number]
end
end
linenos
end
end

View File

@ -0,0 +1,25 @@
# require "test/unit"
# $:.unshift File.dirname(__FILE__) + "/../lib/"
# require "diff-display"
require File.join(File.dirname(__FILE__), "/../lib/diff-display")
if RUBY_VERSION > '1.9'
gem 'test-unit', ">=0"
class Test::Unit::TestCase
PASSTHROUGH_EXCEPTIONS = [NoMemoryError, SignalException, Interrupt, SystemExit]
end
end
require 'test/unit'
require "rubygems"
gem("mocha", ">=0")
require "mocha"
begin
require "redgreen"
rescue LoadError
end
module DiffFixtureHelper
def load_diff(name)
File.read(File.dirname(__FILE__) + "/fixtures/#{name}.diff")
end
end

View File

@ -0,0 +1,22 @@
require File.dirname(__FILE__) + "/test_helper"
class TestUnified < Test::Unit::TestCase
include DiffFixtureHelper
def test_generates_its_data_structure_via_the_generator
generator_data = mock("Generator mock")
Diff::Display::Unified::Generator.expects(:run).returns(generator_data)
diff = Diff::Display::Unified.new(load_diff("simple"))
assert_equal generator_data, diff.data
end
def test_renders_a_diff_via_a_callback_and_renders_it_to_a_stringlike_object
diff = Diff::Display::Unified.new(load_diff("simple"))
callback = mock()
callback.expects(:render).returns("foo")
output = ""
diff.render(callback, output)
assert_equal "foo", output
end
end

View File

@ -0,0 +1,11 @@
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>diff-display</title>
</head>
<body id="body">
<p>This page has not yet been created for RubyGem <code>diff-display</code></p>
<p>To the developer: To generate it, update website/index.txt and run the rake task <code>website</code> to generate this <code>index.html</code> file.</p>
</body>
</html>

View File

@ -0,0 +1,39 @@
h1. diff display
h1. &#x2192; 'diff-display'
h2. What
h2. Installing
<pre syntax="ruby">sudo gem install diff-display</pre>
h2. The basics
h2. Demonstration of usage
h2. Forum
"http://groups.google.com/group/diff-display":http://groups.google.com/group/diff-display
TODO - create Google Group - diff-display
h2. How to submit patches
Read the "8 steps for fixing other people's code":http://drnicwilliams.com/2007/06/01/8-steps-for-fixing-other-peoples-code/ and for section "8b: Submit patch to Google Groups":http://drnicwilliams.com/2007/06/01/8-steps-for-fixing-other-peoples-code/#8b-google-groups, use the Google Group above.
The trunk repository is <code>svn://rubyforge.org/var/svn/diff-display/trunk</code> for anonymous access.
h2. License
This code is free to use under the terms of the MIT license.
h2. Contact
Comments are welcome. Send an email to "FIXME full name":mailto:FIXME email via the "forum":http://groups.google.com/group/diff-display

View File

@ -0,0 +1,285 @@
/****************************************************************
* *
* curvyCorners *
* ------------ *
* *
* This script generates rounded corners for your divs. *
* *
* Version 1.2.9 *
* Copyright (c) 2006 Cameron Cooke *
* By: Cameron Cooke and Tim Hutchison. *
* *
* *
* Website: http://www.curvycorners.net *
* Email: info@totalinfinity.com *
* Forum: http://www.curvycorners.net/forum/ *
* *
* *
* This library is free software; you can redistribute *
* it and/or modify it under the terms of the GNU *
* Lesser General Public License as published by the *
* Free Software Foundation; either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will *
* be useful, but WITHOUT ANY WARRANTY; without even the *
* implied warranty of MERCHANTABILITY or FITNESS FOR A *
* PARTICULAR PURPOSE. See the GNU Lesser General Public *
* License for more details. *
* *
* You should have received a copy of the GNU Lesser *
* General Public License along with this library; *
* Inc., 59 Temple Place, Suite 330, Boston, *
* MA 02111-1307 USA *
* *
****************************************************************/
var isIE = navigator.userAgent.toLowerCase().indexOf("msie") > -1; var isMoz = document.implementation && document.implementation.createDocument; var isSafari = ((navigator.userAgent.toLowerCase().indexOf('safari')!=-1)&&(navigator.userAgent.toLowerCase().indexOf('mac')!=-1))?true:false; function curvyCorners()
{ if(typeof(arguments[0]) != "object") throw newCurvyError("First parameter of curvyCorners() must be an object."); if(typeof(arguments[1]) != "object" && typeof(arguments[1]) != "string") throw newCurvyError("Second parameter of curvyCorners() must be an object or a class name."); if(typeof(arguments[1]) == "string")
{ var startIndex = 0; var boxCol = getElementsByClass(arguments[1]);}
else
{ var startIndex = 1; var boxCol = arguments;}
var curvyCornersCol = new Array(); if(arguments[0].validTags)
var validElements = arguments[0].validTags; else
var validElements = ["div"]; for(var i = startIndex, j = boxCol.length; i < j; i++)
{ var currentTag = boxCol[i].tagName.toLowerCase(); if(inArray(validElements, currentTag) !== false)
{ curvyCornersCol[curvyCornersCol.length] = new curvyObject(arguments[0], boxCol[i]);}
}
this.objects = curvyCornersCol; this.applyCornersToAll = function()
{ for(var x = 0, k = this.objects.length; x < k; x++)
{ this.objects[x].applyCorners();}
}
}
function curvyObject()
{ this.box = arguments[1]; this.settings = arguments[0]; this.topContainer = null; this.bottomContainer = null; this.masterCorners = new Array(); this.contentDIV = null; var boxHeight = get_style(this.box, "height", "height"); var boxWidth = get_style(this.box, "width", "width"); var borderWidth = get_style(this.box, "borderTopWidth", "border-top-width"); var borderColour = get_style(this.box, "borderTopColor", "border-top-color"); var boxColour = get_style(this.box, "backgroundColor", "background-color"); var backgroundImage = get_style(this.box, "backgroundImage", "background-image"); var boxPosition = get_style(this.box, "position", "position"); var boxPadding = get_style(this.box, "paddingTop", "padding-top"); this.boxHeight = parseInt(((boxHeight != "" && boxHeight != "auto" && boxHeight.indexOf("%") == -1)? boxHeight.substring(0, boxHeight.indexOf("px")) : this.box.scrollHeight)); this.boxWidth = parseInt(((boxWidth != "" && boxWidth != "auto" && boxWidth.indexOf("%") == -1)? boxWidth.substring(0, boxWidth.indexOf("px")) : this.box.scrollWidth)); this.borderWidth = parseInt(((borderWidth != "" && borderWidth.indexOf("px") !== -1)? borderWidth.slice(0, borderWidth.indexOf("px")) : 0)); this.boxColour = format_colour(boxColour); this.boxPadding = parseInt(((boxPadding != "" && boxPadding.indexOf("px") !== -1)? boxPadding.slice(0, boxPadding.indexOf("px")) : 0)); this.borderColour = format_colour(borderColour); this.borderString = this.borderWidth + "px" + " solid " + this.borderColour; this.backgroundImage = ((backgroundImage != "none")? backgroundImage : ""); this.boxContent = this.box.innerHTML; if(boxPosition != "absolute") this.box.style.position = "relative"; this.box.style.padding = "0px"; if(isIE && boxWidth == "auto" && boxHeight == "auto") this.box.style.width = "100%"; if(this.settings.autoPad == true && this.boxPadding > 0)
this.box.innerHTML = ""; this.applyCorners = function()
{ for(var t = 0; t < 2; t++)
{ switch(t)
{ case 0:
if(this.settings.tl || this.settings.tr)
{ var newMainContainer = document.createElement("DIV"); newMainContainer.style.width = "100%"; newMainContainer.style.fontSize = "1px"; newMainContainer.style.overflow = "hidden"; newMainContainer.style.position = "absolute"; newMainContainer.style.paddingLeft = this.borderWidth + "px"; newMainContainer.style.paddingRight = this.borderWidth + "px"; var topMaxRadius = Math.max(this.settings.tl ? this.settings.tl.radius : 0, this.settings.tr ? this.settings.tr.radius : 0); newMainContainer.style.height = topMaxRadius + "px"; newMainContainer.style.top = 0 - topMaxRadius + "px"; newMainContainer.style.left = 0 - this.borderWidth + "px"; this.topContainer = this.box.appendChild(newMainContainer);}
break; case 1:
if(this.settings.bl || this.settings.br)
{ var newMainContainer = document.createElement("DIV"); newMainContainer.style.width = "100%"; newMainContainer.style.fontSize = "1px"; newMainContainer.style.overflow = "hidden"; newMainContainer.style.position = "absolute"; newMainContainer.style.paddingLeft = this.borderWidth + "px"; newMainContainer.style.paddingRight = this.borderWidth + "px"; var botMaxRadius = Math.max(this.settings.bl ? this.settings.bl.radius : 0, this.settings.br ? this.settings.br.radius : 0); newMainContainer.style.height = botMaxRadius + "px"; newMainContainer.style.bottom = 0 - botMaxRadius + "px"; newMainContainer.style.left = 0 - this.borderWidth + "px"; this.bottomContainer = this.box.appendChild(newMainContainer);}
break;}
}
if(this.topContainer) this.box.style.borderTopWidth = "0px"; if(this.bottomContainer) this.box.style.borderBottomWidth = "0px"; var corners = ["tr", "tl", "br", "bl"]; for(var i in corners)
{ if(i > -1 < 4)
{ var cc = corners[i]; if(!this.settings[cc])
{ if(((cc == "tr" || cc == "tl") && this.topContainer != null) || ((cc == "br" || cc == "bl") && this.bottomContainer != null))
{ var newCorner = document.createElement("DIV"); newCorner.style.position = "relative"; newCorner.style.fontSize = "1px"; newCorner.style.overflow = "hidden"; if(this.backgroundImage == "")
newCorner.style.backgroundColor = this.boxColour; else
newCorner.style.backgroundImage = this.backgroundImage; switch(cc)
{ case "tl":
newCorner.style.height = topMaxRadius - this.borderWidth + "px"; newCorner.style.marginRight = this.settings.tr.radius - (this.borderWidth*2) + "px"; newCorner.style.borderLeft = this.borderString; newCorner.style.borderTop = this.borderString; newCorner.style.left = -this.borderWidth + "px"; break; case "tr":
newCorner.style.height = topMaxRadius - this.borderWidth + "px"; newCorner.style.marginLeft = this.settings.tl.radius - (this.borderWidth*2) + "px"; newCorner.style.borderRight = this.borderString; newCorner.style.borderTop = this.borderString; newCorner.style.backgroundPosition = "-" + (topMaxRadius + this.borderWidth) + "px 0px"; newCorner.style.left = this.borderWidth + "px"; break; case "bl":
newCorner.style.height = botMaxRadius - this.borderWidth + "px"; newCorner.style.marginRight = this.settings.br.radius - (this.borderWidth*2) + "px"; newCorner.style.borderLeft = this.borderString; newCorner.style.borderBottom = this.borderString; newCorner.style.left = -this.borderWidth + "px"; newCorner.style.backgroundPosition = "-" + (this.borderWidth) + "px -" + (this.boxHeight + (botMaxRadius + this.borderWidth)) + "px"; break; case "br":
newCorner.style.height = botMaxRadius - this.borderWidth + "px"; newCorner.style.marginLeft = this.settings.bl.radius - (this.borderWidth*2) + "px"; newCorner.style.borderRight = this.borderString; newCorner.style.borderBottom = this.borderString; newCorner.style.left = this.borderWidth + "px"
newCorner.style.backgroundPosition = "-" + (botMaxRadius + this.borderWidth) + "px -" + (this.boxHeight + (botMaxRadius + this.borderWidth)) + "px"; break;}
}
}
else
{ if(this.masterCorners[this.settings[cc].radius])
{ var newCorner = this.masterCorners[this.settings[cc].radius].cloneNode(true);}
else
{ var newCorner = document.createElement("DIV"); newCorner.style.height = this.settings[cc].radius + "px"; newCorner.style.width = this.settings[cc].radius + "px"; newCorner.style.position = "absolute"; newCorner.style.fontSize = "1px"; newCorner.style.overflow = "hidden"; var borderRadius = parseInt(this.settings[cc].radius - this.borderWidth); for(var intx = 0, j = this.settings[cc].radius; intx < j; intx++)
{ if((intx +1) >= borderRadius)
var y1 = -1; else
var y1 = (Math.floor(Math.sqrt(Math.pow(borderRadius, 2) - Math.pow((intx+1), 2))) - 1); if(borderRadius != j)
{ if((intx) >= borderRadius)
var y2 = -1; else
var y2 = Math.ceil(Math.sqrt(Math.pow(borderRadius,2) - Math.pow(intx, 2))); if((intx+1) >= j)
var y3 = -1; else
var y3 = (Math.floor(Math.sqrt(Math.pow(j ,2) - Math.pow((intx+1), 2))) - 1);}
if((intx) >= j)
var y4 = -1; else
var y4 = Math.ceil(Math.sqrt(Math.pow(j ,2) - Math.pow(intx, 2))); if(y1 > -1) this.drawPixel(intx, 0, this.boxColour, 100, (y1+1), newCorner, -1, this.settings[cc].radius); if(borderRadius != j)
{ for(var inty = (y1 + 1); inty < y2; inty++)
{ if(this.settings.antiAlias)
{ if(this.backgroundImage != "")
{ var borderFract = (pixelFraction(intx, inty, borderRadius) * 100); if(borderFract < 30)
{ this.drawPixel(intx, inty, this.borderColour, 100, 1, newCorner, 0, this.settings[cc].radius);}
else
{ this.drawPixel(intx, inty, this.borderColour, 100, 1, newCorner, -1, this.settings[cc].radius);}
}
else
{ var pixelcolour = BlendColour(this.boxColour, this.borderColour, pixelFraction(intx, inty, borderRadius)); this.drawPixel(intx, inty, pixelcolour, 100, 1, newCorner, 0, this.settings[cc].radius, cc);}
}
}
if(this.settings.antiAlias)
{ if(y3 >= y2)
{ if (y2 == -1) y2 = 0; this.drawPixel(intx, y2, this.borderColour, 100, (y3 - y2 + 1), newCorner, 0, 0);}
}
else
{ if(y3 >= y1)
{ this.drawPixel(intx, (y1 + 1), this.borderColour, 100, (y3 - y1), newCorner, 0, 0);}
}
var outsideColour = this.borderColour;}
else
{ var outsideColour = this.boxColour; var y3 = y1;}
if(this.settings.antiAlias)
{ for(var inty = (y3 + 1); inty < y4; inty++)
{ this.drawPixel(intx, inty, outsideColour, (pixelFraction(intx, inty , j) * 100), 1, newCorner, ((this.borderWidth > 0)? 0 : -1), this.settings[cc].radius);}
}
}
this.masterCorners[this.settings[cc].radius] = newCorner.cloneNode(true);}
if(cc != "br")
{ for(var t = 0, k = newCorner.childNodes.length; t < k; t++)
{ var pixelBar = newCorner.childNodes[t]; var pixelBarTop = parseInt(pixelBar.style.top.substring(0, pixelBar.style.top.indexOf("px"))); var pixelBarLeft = parseInt(pixelBar.style.left.substring(0, pixelBar.style.left.indexOf("px"))); var pixelBarHeight = parseInt(pixelBar.style.height.substring(0, pixelBar.style.height.indexOf("px"))); if(cc == "tl" || cc == "bl"){ pixelBar.style.left = this.settings[cc].radius -pixelBarLeft -1 + "px";}
if(cc == "tr" || cc == "tl"){ pixelBar.style.top = this.settings[cc].radius -pixelBarHeight -pixelBarTop + "px";}
switch(cc)
{ case "tr":
pixelBar.style.backgroundPosition = "-" + Math.abs((this.boxWidth - this.settings[cc].radius + this.borderWidth) + pixelBarLeft) + "px -" + Math.abs(this.settings[cc].radius -pixelBarHeight -pixelBarTop - this.borderWidth) + "px"; break; case "tl":
pixelBar.style.backgroundPosition = "-" + Math.abs((this.settings[cc].radius -pixelBarLeft -1) - this.borderWidth) + "px -" + Math.abs(this.settings[cc].radius -pixelBarHeight -pixelBarTop - this.borderWidth) + "px"; break; case "bl":
pixelBar.style.backgroundPosition = "-" + Math.abs((this.settings[cc].radius -pixelBarLeft -1) - this.borderWidth) + "px -" + Math.abs((this.boxHeight + this.settings[cc].radius + pixelBarTop) -this.borderWidth) + "px"; break;}
}
}
}
if(newCorner)
{ switch(cc)
{ case "tl":
if(newCorner.style.position == "absolute") newCorner.style.top = "0px"; if(newCorner.style.position == "absolute") newCorner.style.left = "0px"; if(this.topContainer) this.topContainer.appendChild(newCorner); break; case "tr":
if(newCorner.style.position == "absolute") newCorner.style.top = "0px"; if(newCorner.style.position == "absolute") newCorner.style.right = "0px"; if(this.topContainer) this.topContainer.appendChild(newCorner); break; case "bl":
if(newCorner.style.position == "absolute") newCorner.style.bottom = "0px"; if(newCorner.style.position == "absolute") newCorner.style.left = "0px"; if(this.bottomContainer) this.bottomContainer.appendChild(newCorner); break; case "br":
if(newCorner.style.position == "absolute") newCorner.style.bottom = "0px"; if(newCorner.style.position == "absolute") newCorner.style.right = "0px"; if(this.bottomContainer) this.bottomContainer.appendChild(newCorner); break;}
}
}
}
var radiusDiff = new Array(); radiusDiff["t"] = Math.abs(this.settings.tl.radius - this.settings.tr.radius)
radiusDiff["b"] = Math.abs(this.settings.bl.radius - this.settings.br.radius); for(z in radiusDiff)
{ if(z == "t" || z == "b")
{ if(radiusDiff[z])
{ var smallerCornerType = ((this.settings[z + "l"].radius < this.settings[z + "r"].radius)? z +"l" : z +"r"); var newFiller = document.createElement("DIV"); newFiller.style.height = radiusDiff[z] + "px"; newFiller.style.width = this.settings[smallerCornerType].radius+ "px"
newFiller.style.position = "absolute"; newFiller.style.fontSize = "1px"; newFiller.style.overflow = "hidden"; newFiller.style.backgroundColor = this.boxColour; switch(smallerCornerType)
{ case "tl":
newFiller.style.bottom = "0px"; newFiller.style.left = "0px"; newFiller.style.borderLeft = this.borderString; this.topContainer.appendChild(newFiller); break; case "tr":
newFiller.style.bottom = "0px"; newFiller.style.right = "0px"; newFiller.style.borderRight = this.borderString; this.topContainer.appendChild(newFiller); break; case "bl":
newFiller.style.top = "0px"; newFiller.style.left = "0px"; newFiller.style.borderLeft = this.borderString; this.bottomContainer.appendChild(newFiller); break; case "br":
newFiller.style.top = "0px"; newFiller.style.right = "0px"; newFiller.style.borderRight = this.borderString; this.bottomContainer.appendChild(newFiller); break;}
}
var newFillerBar = document.createElement("DIV"); newFillerBar.style.position = "relative"; newFillerBar.style.fontSize = "1px"; newFillerBar.style.overflow = "hidden"; newFillerBar.style.backgroundColor = this.boxColour; newFillerBar.style.backgroundImage = this.backgroundImage; switch(z)
{ case "t":
if(this.topContainer)
{ if(this.settings.tl.radius && this.settings.tr.radius)
{ newFillerBar.style.height = topMaxRadius - this.borderWidth + "px"; newFillerBar.style.marginLeft = this.settings.tl.radius - this.borderWidth + "px"; newFillerBar.style.marginRight = this.settings.tr.radius - this.borderWidth + "px"; newFillerBar.style.borderTop = this.borderString; if(this.backgroundImage != "")
newFillerBar.style.backgroundPosition = "-" + (topMaxRadius + this.borderWidth) + "px 0px"; this.topContainer.appendChild(newFillerBar);}
this.box.style.backgroundPosition = "0px -" + (topMaxRadius - this.borderWidth) + "px";}
break; case "b":
if(this.bottomContainer)
{ if(this.settings.bl.radius && this.settings.br.radius)
{ newFillerBar.style.height = botMaxRadius - this.borderWidth + "px"; newFillerBar.style.marginLeft = this.settings.bl.radius - this.borderWidth + "px"; newFillerBar.style.marginRight = this.settings.br.radius - this.borderWidth + "px"; newFillerBar.style.borderBottom = this.borderString; if(this.backgroundImage != "")
newFillerBar.style.backgroundPosition = "-" + (botMaxRadius + this.borderWidth) + "px -" + (this.boxHeight + (topMaxRadius + this.borderWidth)) + "px"; this.bottomContainer.appendChild(newFillerBar);}
}
break;}
}
}
if(this.settings.autoPad == true && this.boxPadding > 0)
{ var contentContainer = document.createElement("DIV"); contentContainer.style.position = "relative"; contentContainer.innerHTML = this.boxContent; contentContainer.className = "autoPadDiv"; var topPadding = Math.abs(topMaxRadius - this.boxPadding); var botPadding = Math.abs(botMaxRadius - this.boxPadding); if(topMaxRadius < this.boxPadding)
contentContainer.style.paddingTop = topPadding + "px"; if(botMaxRadius < this.boxPadding)
contentContainer.style.paddingBottom = botMaxRadius + "px"; contentContainer.style.paddingLeft = this.boxPadding + "px"; contentContainer.style.paddingRight = this.boxPadding + "px"; this.contentDIV = this.box.appendChild(contentContainer);}
}
this.drawPixel = function(intx, inty, colour, transAmount, height, newCorner, image, cornerRadius)
{ var pixel = document.createElement("DIV"); pixel.style.height = height + "px"; pixel.style.width = "1px"; pixel.style.position = "absolute"; pixel.style.fontSize = "1px"; pixel.style.overflow = "hidden"; var topMaxRadius = Math.max(this.settings["tr"].radius, this.settings["tl"].radius); if(image == -1 && this.backgroundImage != "")
{ pixel.style.backgroundImage = this.backgroundImage; pixel.style.backgroundPosition = "-" + (this.boxWidth - (cornerRadius - intx) + this.borderWidth) + "px -" + ((this.boxHeight + topMaxRadius + inty) -this.borderWidth) + "px";}
else
{ pixel.style.backgroundColor = colour;}
if (transAmount != 100)
setOpacity(pixel, transAmount); pixel.style.top = inty + "px"; pixel.style.left = intx + "px"; newCorner.appendChild(pixel);}
}
function insertAfter(parent, node, referenceNode)
{ parent.insertBefore(node, referenceNode.nextSibling);}
function BlendColour(Col1, Col2, Col1Fraction)
{ var red1 = parseInt(Col1.substr(1,2),16); var green1 = parseInt(Col1.substr(3,2),16); var blue1 = parseInt(Col1.substr(5,2),16); var red2 = parseInt(Col2.substr(1,2),16); var green2 = parseInt(Col2.substr(3,2),16); var blue2 = parseInt(Col2.substr(5,2),16); if(Col1Fraction > 1 || Col1Fraction < 0) Col1Fraction = 1; var endRed = Math.round((red1 * Col1Fraction) + (red2 * (1 - Col1Fraction))); if(endRed > 255) endRed = 255; if(endRed < 0) endRed = 0; var endGreen = Math.round((green1 * Col1Fraction) + (green2 * (1 - Col1Fraction))); if(endGreen > 255) endGreen = 255; if(endGreen < 0) endGreen = 0; var endBlue = Math.round((blue1 * Col1Fraction) + (blue2 * (1 - Col1Fraction))); if(endBlue > 255) endBlue = 255; if(endBlue < 0) endBlue = 0; return "#" + IntToHex(endRed)+ IntToHex(endGreen)+ IntToHex(endBlue);}
function IntToHex(strNum)
{ base = strNum / 16; rem = strNum % 16; base = base - (rem / 16); baseS = MakeHex(base); remS = MakeHex(rem); return baseS + '' + remS;}
function MakeHex(x)
{ if((x >= 0) && (x <= 9))
{ return x;}
else
{ switch(x)
{ case 10: return "A"; case 11: return "B"; case 12: return "C"; case 13: return "D"; case 14: return "E"; case 15: return "F";}
}
}
function pixelFraction(x, y, r)
{ var pixelfraction = 0; var xvalues = new Array(1); var yvalues = new Array(1); var point = 0; var whatsides = ""; var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(x,2))); if ((intersect >= y) && (intersect < (y+1)))
{ whatsides = "Left"; xvalues[point] = 0; yvalues[point] = intersect - y; point = point + 1;}
var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(y+1,2))); if ((intersect >= x) && (intersect < (x+1)))
{ whatsides = whatsides + "Top"; xvalues[point] = intersect - x; yvalues[point] = 1; point = point + 1;}
var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(x+1,2))); if ((intersect >= y) && (intersect < (y+1)))
{ whatsides = whatsides + "Right"; xvalues[point] = 1; yvalues[point] = intersect - y; point = point + 1;}
var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(y,2))); if ((intersect >= x) && (intersect < (x+1)))
{ whatsides = whatsides + "Bottom"; xvalues[point] = intersect - x; yvalues[point] = 0;}
switch (whatsides)
{ case "LeftRight":
pixelfraction = Math.min(yvalues[0],yvalues[1]) + ((Math.max(yvalues[0],yvalues[1]) - Math.min(yvalues[0],yvalues[1]))/2); break; case "TopRight":
pixelfraction = 1-(((1-xvalues[0])*(1-yvalues[1]))/2); break; case "TopBottom":
pixelfraction = Math.min(xvalues[0],xvalues[1]) + ((Math.max(xvalues[0],xvalues[1]) - Math.min(xvalues[0],xvalues[1]))/2); break; case "LeftBottom":
pixelfraction = (yvalues[0]*xvalues[1])/2; break; default:
pixelfraction = 1;}
return pixelfraction;}
function rgb2Hex(rgbColour)
{ try{ var rgbArray = rgb2Array(rgbColour); var red = parseInt(rgbArray[0]); var green = parseInt(rgbArray[1]); var blue = parseInt(rgbArray[2]); var hexColour = "#" + IntToHex(red) + IntToHex(green) + IntToHex(blue);}
catch(e){ alert("There was an error converting the RGB value to Hexadecimal in function rgb2Hex");}
return hexColour;}
function rgb2Array(rgbColour)
{ var rgbValues = rgbColour.substring(4, rgbColour.indexOf(")")); var rgbArray = rgbValues.split(", "); return rgbArray;}
function setOpacity(obj, opacity)
{ opacity = (opacity == 100)?99.999:opacity; if(isSafari && obj.tagName != "IFRAME")
{ var rgbArray = rgb2Array(obj.style.backgroundColor); var red = parseInt(rgbArray[0]); var green = parseInt(rgbArray[1]); var blue = parseInt(rgbArray[2]); obj.style.backgroundColor = "rgba(" + red + ", " + green + ", " + blue + ", " + opacity/100 + ")";}
else if(typeof(obj.style.opacity) != "undefined")
{ obj.style.opacity = opacity/100;}
else if(typeof(obj.style.MozOpacity) != "undefined")
{ obj.style.MozOpacity = opacity/100;}
else if(typeof(obj.style.filter) != "undefined")
{ obj.style.filter = "alpha(opacity:" + opacity + ")";}
else if(typeof(obj.style.KHTMLOpacity) != "undefined")
{ obj.style.KHTMLOpacity = opacity/100;}
}
function inArray(array, value)
{ for(var i = 0; i < array.length; i++){ if (array[i] === value) return i;}
return false;}
function inArrayKey(array, value)
{ for(key in array){ if(key === value) return true;}
return false;}
function addEvent(elm, evType, fn, useCapture) { if (elm.addEventListener) { elm.addEventListener(evType, fn, useCapture); return true;}
else if (elm.attachEvent) { var r = elm.attachEvent('on' + evType, fn); return r;}
else { elm['on' + evType] = fn;}
}
function removeEvent(obj, evType, fn, useCapture){ if (obj.removeEventListener){ obj.removeEventListener(evType, fn, useCapture); return true;} else if (obj.detachEvent){ var r = obj.detachEvent("on"+evType, fn); return r;} else { alert("Handler could not be removed");}
}
function format_colour(colour)
{ var returnColour = "#ffffff"; if(colour != "" && colour != "transparent")
{ if(colour.substr(0, 3) == "rgb")
{ returnColour = rgb2Hex(colour);}
else if(colour.length == 4)
{ returnColour = "#" + colour.substring(1, 2) + colour.substring(1, 2) + colour.substring(2, 3) + colour.substring(2, 3) + colour.substring(3, 4) + colour.substring(3, 4);}
else
{ returnColour = colour;}
}
return returnColour;}
function get_style(obj, property, propertyNS)
{ try
{ if(obj.currentStyle)
{ var returnVal = eval("obj.currentStyle." + property);}
else
{ if(isSafari && obj.style.display == "none")
{ obj.style.display = ""; var wasHidden = true;}
var returnVal = document.defaultView.getComputedStyle(obj, '').getPropertyValue(propertyNS); if(isSafari && wasHidden)
{ obj.style.display = "none";}
}
}
catch(e)
{ }
return returnVal;}
function getElementsByClass(searchClass, node, tag)
{ var classElements = new Array(); if(node == null)
node = document; if(tag == null)
tag = '*'; var els = node.getElementsByTagName(tag); var elsLen = els.length; var pattern = new RegExp("(^|\s)"+searchClass+"(\s|$)"); for (i = 0, j = 0; i < elsLen; i++)
{ if(pattern.test(els[i].className))
{ classElements[j] = els[i]; j++;}
}
return classElements;}
function newCurvyError(errorMessage)
{ return new Error("curvyCorners Error:\n" + errorMessage)
}

View File

@ -0,0 +1,138 @@
body {
background-color: #E1D1F1;
font-family: "Georgia", sans-serif;
font-size: 16px;
line-height: 1.6em;
padding: 1.6em 0 0 0;
color: #333;
}
h1, h2, h3, h4, h5, h6 {
color: #444;
}
h1 {
font-family: sans-serif;
font-weight: normal;
font-size: 4em;
line-height: 0.8em;
letter-spacing: -0.1ex;
margin: 5px;
}
li {
padding: 0;
margin: 0;
list-style-type: square;
}
a {
color: #5E5AFF;
background-color: #DAC;
font-weight: normal;
text-decoration: underline;
}
blockquote {
font-size: 90%;
font-style: italic;
border-left: 1px solid #111;
padding-left: 1em;
}
.caps {
font-size: 80%;
}
#main {
width: 45em;
padding: 0;
margin: 0 auto;
}
.coda {
text-align: right;
color: #77f;
font-size: smaller;
}
table {
font-size: 90%;
line-height: 1.4em;
color: #ff8;
background-color: #111;
padding: 2px 10px 2px 10px;
border-style: dashed;
}
th {
color: #fff;
}
td {
padding: 2px 10px 2px 10px;
}
.success {
color: #0CC52B;
}
.failed {
color: #E90A1B;
}
.unknown {
color: #995000;
}
pre, code {
font-family: monospace;
font-size: 90%;
line-height: 1.4em;
color: #ff8;
background-color: #111;
padding: 2px 10px 2px 10px;
}
.comment { color: #aaa; font-style: italic; }
.keyword { color: #eff; font-weight: bold; }
.punct { color: #eee; font-weight: bold; }
.symbol { color: #0bb; }
.string { color: #6b4; }
.ident { color: #ff8; }
.constant { color: #66f; }
.regex { color: #ec6; }
.number { color: #F99; }
.expr { color: #227; }
#version {
float: right;
text-align: right;
font-family: sans-serif;
font-weight: normal;
background-color: #B3ABFF;
color: #141331;
padding: 15px 20px 10px 20px;
margin: 0 auto;
margin-top: 15px;
border: 3px solid #141331;
}
#version .numbers {
display: block;
font-size: 4em;
line-height: 0.8em;
letter-spacing: -0.1ex;
margin-bottom: 15px;
}
#version p {
text-decoration: none;
color: #141331;
background-color: #B3ABFF;
margin: 0;
padding: 0;
}
#version a {
text-decoration: none;
color: #141331;
background-color: #B3ABFF;
}
.clickable {
cursor: pointer;
cursor: hand;
}

View File

@ -0,0 +1,48 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<link rel="stylesheet" href="stylesheets/screen.css" type="text/css" media="screen" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>
<%= title %>
</title>
<script src="javascripts/rounded_corners_lite.inc.js" type="text/javascript"></script>
<style>
</style>
<script type="text/javascript">
window.onload = function() {
settings = {
tl: { radius: 10 },
tr: { radius: 10 },
bl: { radius: 10 },
br: { radius: 10 },
antiAlias: true,
autoPad: true,
validTags: ["div"]
}
var versionBox = new curvyCorners(settings, document.getElementById("version"));
versionBox.applyCornersToAll();
}
</script>
</head>
<body>
<div id="main">
<h1><%= title %></h1>
<div id="version" class="clickable" onclick='document.location = "<%= download %>"; return false'>
<p>Get Version</p>
<a href="<%= download %>" class="numbers"><%= version %></a>
</div>
<%= body %>
<p class="coda">
<a href="FIXME email">FIXME full name</a>, <%= modified.pretty %><br>
Theme extended from <a href="http://rb2js.rubyforge.org/">Paul Battley</a>
</p>
</div>
<!-- insert site tracking codes here, like Google Urchin -->
</body>
</html>