summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorerdgeist <erdgeist@erdgeist.org>2026-07-10 00:35:24 +0200
committererdgeist <erdgeist@erdgeist.org>2026-07-10 00:35:24 +0200
commita25d90335ad3738b6831288190132c2f7498465c (patch)
tree5aa7083d9f8c8701644fe60bc7d28542775694b8 /app
parentda0b79a7c1f3116e2b450ecbe50efdf31cf23414 (diff)
Retire vendored cacycle_diff.js for a diff-lcs-based diff view
Revisions#diff now computes an inline or side-by-side word diff server-side (Page#diff_against, lib/html_word_diff.rb) instead of shipping raw/escaped content to the browser for a 2008-era vendored JS differ to mangle. View mode is picked via a `view` param, settable either on the diff page itself or directly from the revisions#index sticky bar next to "Diff revisions". Also fixes a pre-existing bug in RevisionsController#diff's single- revision branch, which set params[:start]/params[:end] instead of params[:start_revision]/params[:end_revision].
Diffstat (limited to 'app')
-rw-r--r--app/controllers/revisions_controller.rb12
-rw-r--r--app/models/page.rb16
-rw-r--r--app/views/revisions/diff.html.erb78
-rw-r--r--app/views/revisions/index.html.erb8
4 files changed, 59 insertions, 55 deletions
diff --git a/app/controllers/revisions_controller.rb b/app/controllers/revisions_controller.rb
index 42d667e..9acb26f 100644
--- a/app/controllers/revisions_controller.rb
+++ b/app/controllers/revisions_controller.rb
@@ -13,16 +13,18 @@ class RevisionsController < ApplicationController
13 13
14 def diff 14 def diff
15 @node = Node.find(params[:node_id]) 15 @node = Node.find(params[:node_id])
16 16
17 if @node.pages.length > 1 17 if @node.pages.length > 1
18 params[:start_revision] ||= @node.pages.all[-2].revision 18 params[:start_revision] ||= @node.pages.all[-2].revision
19 params[:end_revision] ||= @node.pages.all[-1].revision 19 params[:end_revision] ||= @node.pages.all[-1].revision
20 else 20 else
21 params[:start], params[:end] = 1, 1 21 params[:start_revision], params[:end_revision] = 1, 1
22 end 22 end
23 23
24 @start = @node.pages.find_by_revision( params[:start_revision] ) 24 @start = @node.pages.find_by_revision( params[:start_revision] )
25 @end = @node.pages.find_by_revision( params[:end_revision] ) 25 @end = @node.pages.find_by_revision( params[:end_revision] )
26 @diff_view = params[:view] == "side_by_side" ? :side_by_side : :inline
27 @diff = @end.diff_against( @start, view: @diff_view )
26 end 28 end
27 29
28 def show 30 def show
diff --git a/app/models/page.rb b/app/models/page.rb
index 1a98e08..ea04cd6 100644
--- a/app/models/page.rb
+++ b/app/models/page.rb
@@ -175,6 +175,22 @@ class Page < ApplicationRecord
175 self.save 175 self.save
176 end 176 end
177 177
178 def diff_against other, view: :inline
179 if view == :side_by_side
180 {
181 title: HtmlWordDiff.side_by_side(other.title.to_s, title.to_s),
182 abstract: HtmlWordDiff.side_by_side(other.abstract.to_s, abstract.to_s),
183 body: HtmlWordDiff.side_by_side(other.body.to_s, body.to_s)
184 }
185 else
186 {
187 title: HtmlWordDiff.inline(other.title.to_s, title.to_s),
188 abstract: HtmlWordDiff.inline(other.abstract.to_s, abstract.to_s),
189 body: HtmlWordDiff.inline(other.body.to_s, body.to_s)
190 }
191 end
192 end
193
178 def public? 194 def public?
179 published_at.nil? ? true : published_at < Time.now 195 published_at.nil? ? true : published_at < Time.now
180 end 196 end
diff --git a/app/views/revisions/diff.html.erb b/app/views/revisions/diff.html.erb
index d8c6a47..d7bb528 100644
--- a/app/views/revisions/diff.html.erb
+++ b/app/views/revisions/diff.html.erb
@@ -7,58 +7,36 @@
7<%= form_tag diff_node_revisions_path do %> 7<%= form_tag diff_node_revisions_path do %>
8 <%= select_tag :start_revision, options_for_select(@node.pages.map{|x| x.revision}, params[:start_revision].to_i) %> 8 <%= select_tag :start_revision, options_for_select(@node.pages.map{|x| x.revision}, params[:start_revision].to_i) %>
9 <%= select_tag :end_revision, options_for_select(@node.pages.map{|x| x.revision}, params[:end_revision].to_i) %> 9 <%= select_tag :end_revision, options_for_select(@node.pages.map{|x| x.revision}, params[:end_revision].to_i) %>
10 <%= select_tag :view, options_for_select([['Inline', 'inline'], ['Side by side', 'side_by_side']], @diff_view) %>
10 <%= submit_tag 'Diff' %> 11 <%= submit_tag 'Diff' %>
11<% end %> 12<% end %>
12 13
13<div id="start_title" style="display: none;">
14 <%= h(@start.title) %>
15</div>
16
17<div id="end_title" style="display: none;">
18 <%= h(@end.title) %>
19</div>
20
21<div id="start_abstract" style="display: none;">
22 <%= h(@start.abstract) %>
23</div>
24
25<div id="end_abstract" style="display: none;">
26 <%= h(@end.abstract) %>
27</div>
28
29<pre>
30 <div id="start_body" style="display: none;">
31 <%= (@start.body) %>
32 </div>
33</pre>
34<pre>
35 <div id="end_body" style="display: none;">
36 <%= (@end.body) %>
37 </div>
38</pre>
39
40<script src="/javascripts/cacycle_diff.js"></script>
41<script type="text/javascript" charset="utf-8">
42 window.onload = function() {
43 title1 = document.getElementById('start_title').innerHTML;
44 title2 = document.getElementById('end_title').innerHTML;
45 abstract1 = document.getElementById('start_abstract').innerHTML;
46 abstract2 = document.getElementById('end_abstract').innerHTML;
47 body1 = document.getElementById('start_body').innerHTML;
48 body2 = document.getElementById('end_body').innerHTML;
49 document.getElementById('diffview_title').innerHTML = WDiffString(title1, title2);
50 document.getElementById('diffview_abstract').innerHTML = WDiffString(abstract1, abstract2);
51 document.getElementById('diffview_body').innerHTML = WDiffString(body1, body2);
52 }
53</script>
54
55<div id="diffview"> 14<div id="diffview">
56 <h3>Title</h3> 15 <% if @diff_view == :side_by_side %>
57 <p id="diffview_title"></p> 16 <div class="diff_side_by_side">
58 17 <div class="diff_column">
59 <h3>Abstract</h3> 18 <h3>Title</h3>
60 <p id="diffview_abstract"></p> 19 <p><%= raw @diff[:title][0] %></p>
61 20 <h3>Abstract</h3>
62 <h3>Body</h3> 21 <p><%= raw @diff[:abstract][0] %></p>
63 <p id="diffview_body"></p> 22 <h3>Body</h3>
23 <%= raw @diff[:body][0] %>
24 </div>
25 <div class="diff_column">
26 <h3>Title</h3>
27 <p><%= raw @diff[:title][1] %></p>
28 <h3>Abstract</h3>
29 <p><%= raw @diff[:abstract][1] %></p>
30 <h3>Body</h3>
31 <%= raw @diff[:body][1] %>
32 </div>
33 </div>
34 <% else %>
35 <h3>Title</h3>
36 <p><%= raw @diff[:title] %></p>
37 <h3>Abstract</h3>
38 <p><%= raw @diff[:abstract] %></p>
39 <h3>Body</h3>
40 <%= raw @diff[:body] %>
41 <% end %>
64</div> 42</div>
diff --git a/app/views/revisions/index.html.erb b/app/views/revisions/index.html.erb
index a6a981a..58c08b7 100644
--- a/app/views/revisions/index.html.erb
+++ b/app/views/revisions/index.html.erb
@@ -19,6 +19,8 @@
19 form: { id: 'diff_form', class: 'button_to computation' }, 19 form: { id: 'diff_form', class: 'button_to computation' },
20 disabled: true %> 20 disabled: true %>
21 <span id="diff_selection_label"></span> 21 <span id="diff_selection_label"></span>
22 <label><%= radio_button_tag :view, 'inline', true %> Inline</label>
23 <label><%= radio_button_tag :view, 'side_by_side', false %> Side by side</label>
22 </td> 24 </td>
23 </tr> 25 </tr>
24 </thead> 26 </thead>
@@ -68,6 +70,7 @@
68 document.getElementById('diff_form').addEventListener('submit', function(e) { 70 document.getElementById('diff_form').addEventListener('submit', function(e) {
69 var start = document.querySelector('input[name="start_revision"]:checked'); 71 var start = document.querySelector('input[name="start_revision"]:checked');
70 var end = document.querySelector('input[name="end_revision"]:checked'); 72 var end = document.querySelector('input[name="end_revision"]:checked');
73 var view = document.querySelector('input[name="view"]:checked');
71 if (start) { 74 if (start) {
72 var s = document.createElement('input'); 75 var s = document.createElement('input');
73 s.type = 'hidden'; s.name = 'start_revision'; s.value = start.value; 76 s.type = 'hidden'; s.name = 'start_revision'; s.value = start.value;
@@ -78,5 +81,10 @@
78 en.type = 'hidden'; en.name = 'end_revision'; en.value = end.value; 81 en.type = 'hidden'; en.name = 'end_revision'; en.value = end.value;
79 this.appendChild(en); 82 this.appendChild(en);
80 } 83 }
84 if (view) {
85 var v = document.createElement('input');
86 v.type = 'hidden'; v.name = 'view'; v.value = view.value;
87 this.appendChild(v);
88 }
81 }); 89 });
82</script> 90</script>