Changesets can be listed by changeset number.
The Git repository is here.
- Revision:
- 344
- Log:
Massive changeset which brings the old, ROOL customised Instiki
version up to date, but without any ROOL customisations in this
latest checked-in version (which is 0.19.1). This is deliberate,
so that it's easy to see the changes made for the ROOL version
in a subsequent changeset. The 'app/views/shared' directory is not
part of Instiki but is kept to maintain the change history with
updated ROOL customisations, some of which involve the same files
in that same directory.
- Author:
- rool
- Date:
- Sat Mar 19 19:52:13 +0000 2011
- Size:
- 4240 Bytes
1 | #!/usr/bin/env ruby |
2 | # Author: Aredridel <aredridel@nbtsc.org> |
3 | # Website: http://theinternetco.net/projects/ruby/xhtmldiff.html |
4 | # Licence: same as Ruby |
5 | # Version: 1.2.2 |
6 | # |
7 | # Tweaks by Jacques Distler <distler@golem.ph.utexas.edu> |
8 | # -- add classnames to <del> and <ins> elements added by XHTMLDiff, |
9 | # for better CSS styling |
10 | # -- detect change in element name, without change in content |
11 | |
12 | require 'diff/lcs' |
13 | require 'rexml/document' |
14 | require 'delegate' |
15 | |
16 | def Math.max(a, b) |
17 | a > b ? a : b |
18 | end |
19 | |
20 | module REXML |
21 | |
22 | class Text |
23 | def deep_clone |
24 | clone |
25 | end |
26 | end |
27 | |
28 | class HashableElementDelegator < DelegateClass(Element) |
29 | def initialize(sub) |
30 | super sub |
31 | end |
32 | def == other |
33 | res = other.to_s.strip == self.to_s.strip |
34 | res |
35 | end |
36 | |
37 | def eql? other |
38 | self == other |
39 | end |
40 | |
41 | def[](k) |
42 | r = super |
43 | if r.kind_of? __getobj__.class |
44 | self.class.new(r) |
45 | else |
46 | r |
47 | end |
48 | end |
49 | |
50 | def hash |
51 | r = __getobj__.to_s.hash |
52 | r |
53 | end |
54 | end |
55 | |
56 | end |
57 | |
58 | class XHTMLDiff |
59 | include REXML |
60 | attr_accessor :output |
61 | |
62 | class << self |
63 | BLOCK_CONTAINERS = ['div', 'ul', 'li'] |
64 | def diff(a, b) |
65 | if a == b |
66 | return a.deep_clone |
67 | end |
68 | if REXML::HashableElementDelegator === a and REXML::HashableElementDelegator === b and a.name == b.name |
69 | o = REXML::Element.new(a.name) |
70 | o.add_attributes a.attributes |
71 | hd = self.new(o) |
72 | Diff::LCS.traverse_balanced(a, b, hd) |
73 | o |
74 | elsif REXML::Text === a and REXML::Text === b |
75 | o = REXML::Element.new('span') |
76 | aa = a.value.split(/\s/) |
77 | ba = b.value.split(/\s/) |
78 | hd = XHTMLTextDiff.new(o) |
79 | Diff::LCS.traverse_balanced(aa, ba, hd) |
80 | o |
81 | else |
82 | raise ArgumentError.new("both arguments must be equal or both be elements. a is #{a.class.name} and b is #{b.class.name}") |
83 | end |
84 | end |
85 | end |
86 | |
87 | def diff(a, b) |
88 | self.class.diff(a,b) |
89 | end |
90 | |
91 | def initialize(output) |
92 | @output = output |
93 | end |
94 | |
95 | # This will be called with both elements are the same |
96 | def match(event) |
97 | @output << event.old_element.deep_clone if event.old_element |
98 | end |
99 | |
100 | # This will be called when there is an element in A that isn't in B |
101 | def discard_a(event) |
102 | @output << wrap(event.old_element, 'del', 'diffdel') |
103 | end |
104 | |
105 | def change(event) |
106 | begin |
107 | sd = diff(event.old_element, event.new_element) |
108 | rescue ArgumentError |
109 | sd = nil |
110 | end |
111 | if sd and (ratio = (Float(rs = sd.to_s.gsub(%r{<(ins|del)>.*</\1>}, '').size) / bs = Math.max(event.old_element.to_s.size, event.new_element.to_s.size))) > 0.5 |
112 | @output << sd |
113 | else |
114 | @output << wrap(event.old_element, 'del', 'diffmod') |
115 | @output << wrap(event.new_element, 'ins', 'diffmod') |
116 | end |
117 | end |
118 | |
119 | # This will be called when there is an element in B that isn't in A |
120 | def discard_b(event) |
121 | @output << wrap(event.new_element, 'ins', 'diffins') |
122 | end |
123 | |
124 | def choose_event(event, element, tag) |
125 | end |
126 | |
127 | def wrap(element, tag = nil, class_name = nil) |
128 | if tag |
129 | el = Element.new tag |
130 | el << element.deep_clone |
131 | else |
132 | el = element.deep_clone |
133 | end |
134 | if class_name |
135 | el.add_attribute('class', class_name) |
136 | end |
137 | el |
138 | end |
139 | |
140 | class XHTMLTextDiff < XHTMLDiff |
141 | def change(event) |
142 | @output << wrap(event.old_element, 'del', 'diffmod') |
143 | @output << wrap(event.new_element, 'ins', 'diffmod') |
144 | end |
145 | |
146 | # This will be called with both elements are the same |
147 | def match(event) |
148 | @output << wrap(event.old_element, nil, nil) if event.old_element |
149 | end |
150 | |
151 | # This will be called when there is an element in A that isn't in B |
152 | def discard_a(event) |
153 | @output << wrap(event.old_element, 'del', 'diffdel') |
154 | end |
155 | |
156 | # This will be called when there is an element in B that isn't in A |
157 | def discard_b(event) |
158 | @output << wrap(event.new_element, 'ins', 'diffins') |
159 | end |
160 | |
161 | def wrap(element, tag = nil, class_name = nil) |
162 | element = REXML::Text.new(" " << element) if String === element |
163 | return element unless tag |
164 | wrapper_element = REXML::Element.new(tag) |
165 | wrapper_element.add_text element |
166 | if class_name |
167 | wrapper_element.add_attribute('class', class_name) |
168 | end |
169 | wrapper_element |
170 | end |
171 | end |
172 | |
173 | end |
174 | |
175 | if $0 == __FILE__ |
176 | |
177 | $stderr.puts "No tests available yet" |
178 | exit(1) |
179 | |
180 | end |