nokodiff 0.4.2 → 1.0.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +12 -11
- data/app/assets/stylesheets/nokodiff.scss +299 -29
- data/lib/nokodiff/differ.rb +93 -63
- data/lib/nokodiff/formatting_helpers.rb +8 -0
- data/lib/nokodiff/version.rb +1 -1
- metadata +8 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 98b3f977e0c9f7e35ccff54cbab81944e22bbcd3b7bb29a5a383fbd0a2668186
|
|
4
|
+
data.tar.gz: 7dbb7c2c74e1d85d245a48722e91d2ab8f89dde3508e32cfb3fe350ea30fd75b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7a8f9d05204c541ceba28f32d5d0f233dacf510d6b03391aab292fca7e649ca302b291543b14a9c27cfdc685d9fb3d250d66ebd9aa60107ffd36413c6b8c1256
|
|
7
|
+
data.tar.gz: d43cf00ad191e146e35f9a581d8a69de0076eec5618c94361e899a74cc11e2aba51deeda8bf99d16572b3da42397d79e7cc84704f16220fd22c04e3f0b2eb901
|
data/README.md
CHANGED
|
@@ -66,7 +66,7 @@ In your application.scss file include:
|
|
|
66
66
|
@import "nokodiff";
|
|
67
67
|
```
|
|
68
68
|
|
|
69
|
-
This will include the styling for
|
|
69
|
+
This will include the styling for `.diff`, `.del`, `.ins` and other classes and `<span>` tags to allow colour coding, highlighting, and underlining of changes.
|
|
70
70
|
|
|
71
71
|
### More complex diffing with `data-diff-key`
|
|
72
72
|
|
|
@@ -111,9 +111,7 @@ Output:
|
|
|
111
111
|
<p>First paragraph</p>
|
|
112
112
|
</div>
|
|
113
113
|
<div data-diff-key="zm7q">
|
|
114
|
-
|
|
115
|
-
<ins aria-label="added content"><p>Second paragraph</p></ins>
|
|
116
|
-
</div>
|
|
114
|
+
<p class="diff ins"><span class="visually-hidden">Added content </span>Second paragraph</p>
|
|
117
115
|
</div>
|
|
118
116
|
</div>
|
|
119
117
|
```
|
|
@@ -154,15 +152,18 @@ Output:
|
|
|
154
152
|
|
|
155
153
|
```html
|
|
156
154
|
<div>
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
<div class="diff">
|
|
160
|
-
<del aria-label="removed content"><p><span class="diff-marker">S</span>e<span class="diff-marker">cond</span> paragraph</p></del>
|
|
155
|
+
<div data-diff-key="ixn4">
|
|
156
|
+
<p>First paragraph</p>
|
|
161
157
|
</div>
|
|
162
|
-
<div
|
|
163
|
-
|
|
158
|
+
<div data-diff-key="zm7q">
|
|
159
|
+
<p class="diff del">
|
|
160
|
+
<span class="diff-marker"><span class="visually-hidden">Diff marker </span><span class="visually-hidden">Removed content </span>S</span>e<span class="diff-marker"><span class="visually-hidden">Diff marker </span>cond</span> paragraph
|
|
161
|
+
</p>
|
|
162
|
+
|
|
163
|
+
<p class="diff ins">
|
|
164
|
+
<span class="diff-marker"><span class="visually-hidden">Diff marker </span><span class="visually-hidden">Added content </span>N</span>e<span class="diff-marker"><span class="visually-hidden">Diff marker </span>w</span> paragraph
|
|
165
|
+
</p>
|
|
164
166
|
</div>
|
|
165
|
-
</div>
|
|
166
167
|
</div>
|
|
167
168
|
```
|
|
168
169
|
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
// stylelint-disable selector-no-qualifying-type, max-nesting-depth
|
|
2
|
-
// Diff of two editions
|
|
3
1
|
$added-color: #e6fff3;
|
|
4
2
|
$diff-marker-added-color: #99ffcf;
|
|
5
3
|
$removed-color: #fadede;
|
|
@@ -7,66 +5,338 @@ $diff-marker-removed-color: #f3aeac;
|
|
|
7
5
|
$text-colour: #0b0c0c;
|
|
8
6
|
$light-grey: #f3f2f1;
|
|
9
7
|
$black: #0b0c0c;
|
|
8
|
+
$comparison-sidebar-width: 40px;
|
|
9
|
+
$comparison-sidebar-padding: 15px;
|
|
10
|
+
$comparison-sidebar-offset: $comparison-sidebar-width + $comparison-sidebar-padding;
|
|
11
|
+
|
|
12
|
+
.visually-hidden {
|
|
13
|
+
position: absolute !important;
|
|
14
|
+
width: 1px !important;
|
|
15
|
+
height: 1px !important;
|
|
16
|
+
// If margin is set to a negative value it can cause text to be announced in
|
|
17
|
+
// the wrong order in VoiceOver for OSX
|
|
18
|
+
margin: 0 !important;
|
|
19
|
+
padding: 0 !important;
|
|
20
|
+
overflow: hidden !important;
|
|
21
|
+
|
|
22
|
+
clip: rect(0 0 0 0) !important;
|
|
23
|
+
clip-path: inset(50%) !important;
|
|
24
|
+
|
|
25
|
+
border: 0 !important;
|
|
26
|
+
|
|
27
|
+
// For long content, line feeds are not interpreted as spaces and small width
|
|
28
|
+
// causes content to wrap 1 word per line:
|
|
29
|
+
// https://medium.com/@jessebeach/beware-smushed-off-screen-accessible-text-5952a4c2cbfe
|
|
30
|
+
white-space: nowrap !important;
|
|
31
|
+
|
|
32
|
+
// Prevent users from selecting or copying visually-hidden text. This prevents
|
|
33
|
+
// a user unintentionally copying more text than they intended and needing to
|
|
34
|
+
// manually trim it down again.
|
|
35
|
+
user-select: none !important;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.visually-hidden {
|
|
39
|
+
position: absolute !important;
|
|
40
|
+
width: 1px !important;
|
|
41
|
+
height: 1px !important;
|
|
42
|
+
// If margin is set to a negative value it can cause text to be announced in
|
|
43
|
+
// the wrong order in VoiceOver for OSX
|
|
44
|
+
margin: 0 !important;
|
|
45
|
+
padding: 0 !important;
|
|
46
|
+
overflow: hidden !important;
|
|
47
|
+
|
|
48
|
+
clip: rect(0 0 0 0) !important;
|
|
49
|
+
clip-path: inset(50%) !important;
|
|
50
|
+
|
|
51
|
+
border: 0 !important;
|
|
52
|
+
|
|
53
|
+
// For long content, line feeds are not interpreted as spaces and small width
|
|
54
|
+
// causes content to wrap 1 word per line:
|
|
55
|
+
// https://medium.com/@jessebeach/beware-smushed-off-screen-accessible-text-5952a4c2cbfe
|
|
56
|
+
white-space: nowrap !important;
|
|
57
|
+
|
|
58
|
+
// Prevent users from selecting or copying visually-hidden text. This prevents
|
|
59
|
+
// a user unintentionally copying more text than they intended and needing to
|
|
60
|
+
// manually trim it down again.
|
|
61
|
+
user-select: none !important;
|
|
62
|
+
|
|
63
|
+
}
|
|
10
64
|
|
|
11
65
|
.compare-editions {
|
|
12
66
|
position: relative;
|
|
13
|
-
border-left:
|
|
14
|
-
padding:
|
|
67
|
+
border-left: $comparison-sidebar-width solid $light-grey;
|
|
68
|
+
padding: $comparison-sidebar-padding;
|
|
69
|
+
clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);
|
|
70
|
+
|
|
71
|
+
td::before {
|
|
72
|
+
// Backup to handle any inputted classes which make use of a ::before attached to a td element
|
|
73
|
+
// unsetting all styles for it will prevent it interfering with the ::before that we need to use
|
|
74
|
+
// for the diff marker
|
|
75
|
+
all: unset;
|
|
76
|
+
}
|
|
15
77
|
|
|
16
78
|
.diff {
|
|
17
79
|
position: static;
|
|
18
80
|
margin-bottom: 10px;
|
|
19
81
|
|
|
20
|
-
del, ins {
|
|
21
|
-
|
|
82
|
+
&.del, &.ins {
|
|
83
|
+
&:is(li){ display: list-item; }
|
|
84
|
+
|
|
85
|
+
position: relative;
|
|
22
86
|
display: block;
|
|
23
87
|
text-decoration: none;
|
|
24
|
-
|
|
88
|
+
background-color: transparent;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
&::after {
|
|
92
|
+
content: "";
|
|
93
|
+
position: absolute;
|
|
94
|
+
top: 0;
|
|
95
|
+
bottom: 0;
|
|
96
|
+
left: -$comparison-sidebar-padding;
|
|
97
|
+
min-height: 1.5em;
|
|
98
|
+
width: 100vw;
|
|
99
|
+
display: block;
|
|
100
|
+
z-index: -1;
|
|
25
101
|
}
|
|
26
102
|
|
|
27
|
-
del {
|
|
28
|
-
background-color: $removed-color;
|
|
29
|
-
padding-bottom: 2px;
|
|
103
|
+
&.del {
|
|
104
|
+
&::after { background-color: $removed-color; }
|
|
30
105
|
|
|
31
106
|
span.diff-marker {
|
|
32
|
-
font-weight: normal;
|
|
33
107
|
background-color: $diff-marker-removed-color;
|
|
34
108
|
border-bottom: 2px dashed $black;
|
|
35
109
|
}
|
|
36
110
|
}
|
|
37
111
|
|
|
38
|
-
ins {
|
|
39
|
-
background-color: $added-color;
|
|
40
|
-
padding-bottom: 2px;
|
|
112
|
+
&.ins {
|
|
113
|
+
&::after { background-color: $added-color; }
|
|
41
114
|
|
|
42
115
|
span.diff-marker {
|
|
43
|
-
font-weight: normal;
|
|
44
116
|
background-color: $diff-marker-added-color;
|
|
45
117
|
border-bottom: 2px dashed $black;
|
|
46
118
|
}
|
|
47
119
|
}
|
|
48
120
|
|
|
49
|
-
|
|
50
|
-
|
|
121
|
+
&::before {
|
|
122
|
+
// Some govuk styles use ::before in ways we need to override to make this work
|
|
123
|
+
border-radius: unset !important;
|
|
124
|
+
border: unset !important;
|
|
125
|
+
|
|
51
126
|
position: absolute;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
127
|
+
top: 0;
|
|
128
|
+
left: -$comparison-sidebar-offset;
|
|
129
|
+
width: $comparison-sidebar-width;
|
|
130
|
+
height: 100%;
|
|
131
|
+
min-height: 1.5em;
|
|
55
132
|
line-height: 1.5em;
|
|
56
133
|
text-align: center;
|
|
134
|
+
font-weight: normal;
|
|
57
135
|
z-index: 10;
|
|
58
136
|
}
|
|
137
|
+
|
|
138
|
+
&.del::before {
|
|
139
|
+
color: $text-colour;
|
|
140
|
+
background-color: $diff-marker-removed-color;
|
|
141
|
+
content: "−" !important;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
&.ins::before {
|
|
145
|
+
color: $text-colour;
|
|
146
|
+
background-color: $diff-marker-added-color;
|
|
147
|
+
content: "+" !important;
|
|
148
|
+
}
|
|
59
149
|
}
|
|
60
150
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
content: "−";
|
|
151
|
+
// Global govspeak-specific overrides
|
|
152
|
+
.gem-c-govspeak & {
|
|
153
|
+
&::before { min-height: 1.3157894737em; }
|
|
65
154
|
}
|
|
66
155
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
156
|
+
// Inset/callout elements overrides
|
|
157
|
+
|
|
158
|
+
.address {
|
|
159
|
+
.diff{
|
|
160
|
+
$address-offset: -16px;
|
|
161
|
+
|
|
162
|
+
&::after {
|
|
163
|
+
left: -$comparison-sidebar-padding + $address-offset;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
&::before {
|
|
167
|
+
left: -$comparison-sidebar-offset + $address-offset;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
71
170
|
}
|
|
72
|
-
|
|
171
|
+
|
|
172
|
+
.help-notice {
|
|
173
|
+
$help-notice-offset: -45px;
|
|
174
|
+
.diff {
|
|
175
|
+
&::after, &::before {
|
|
176
|
+
top: -15px;
|
|
177
|
+
min-height: calc(1.5em + 30px);
|
|
178
|
+
left: -$comparison-sidebar-padding + $help-notice-offset;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
&::before {
|
|
182
|
+
top: -15px;
|
|
183
|
+
min-height: calc(1.5em + 30px);
|
|
184
|
+
left: -$comparison-sidebar-offset + $help-notice-offset;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.example, .info-notice {
|
|
190
|
+
$callout-parent-offset: -10px;
|
|
191
|
+
$callout-child-offset: -30px;
|
|
192
|
+
|
|
193
|
+
&.diff {
|
|
194
|
+
&::after {
|
|
195
|
+
left: -$comparison-sidebar-padding + $callout-parent-offset;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
&::before {
|
|
199
|
+
left: -$comparison-sidebar-offset + $callout-parent-offset;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.diff {
|
|
204
|
+
&::after {
|
|
205
|
+
left: -$comparison-sidebar-padding + $callout-child-offset;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
&::before {
|
|
209
|
+
left: -$comparison-sidebar-offset + $callout-child-offset;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// List-specific overrides for non-govuk lists
|
|
215
|
+
$non-govuk-list-offset: -40px;
|
|
216
|
+
|
|
217
|
+
li span.diff, ul span.diff{
|
|
218
|
+
&::after {
|
|
219
|
+
left: -$comparison-sidebar-padding + $non-govuk-list-offset;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
&::before {
|
|
223
|
+
left: -$comparison-sidebar-offset + $non-govuk-list-offset;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// List-specific overrides for govuk lists as we apply a non-standard offset to lists
|
|
228
|
+
$govuk-list-offset: -20px;
|
|
229
|
+
|
|
230
|
+
.gem-c-govspeak & {
|
|
231
|
+
ul li span.diff, ul span.diff {
|
|
232
|
+
&::after {
|
|
233
|
+
left: -$comparison-sidebar-padding + $govuk-list-offset;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
&::before {
|
|
237
|
+
left: -$comparison-sidebar-offset + $govuk-list-offset;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
ol li span.diff {
|
|
242
|
+
list-style-type: decimal;
|
|
243
|
+
|
|
244
|
+
&::marker {
|
|
245
|
+
background-color: #0b0c0c;
|
|
246
|
+
border-radius: 50%;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
&::after {
|
|
250
|
+
left: $comparison-sidebar-padding;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
&::before {
|
|
254
|
+
left: $comparison-sidebar-offset;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
//govuk "steps"-style ol overrides
|
|
259
|
+
$steps-list-offset: -48px;
|
|
260
|
+
|
|
261
|
+
ol.steps {
|
|
262
|
+
li{
|
|
263
|
+
display: list-item;
|
|
264
|
+
list-style-type: decimal;
|
|
265
|
+
padding-left: 0.25em;
|
|
266
|
+
margin-left: 2.25em;
|
|
267
|
+
|
|
268
|
+
&::before {
|
|
269
|
+
display: none;
|
|
270
|
+
}
|
|
271
|
+
&::after { left: -$comparison-sidebar-padding + $steps-list-offset !important; }
|
|
272
|
+
&::marker { content: "#."; }
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
li span.diff {
|
|
276
|
+
&::marker { content: unset; }
|
|
277
|
+
&::before {
|
|
278
|
+
top: -2px;
|
|
279
|
+
left: -$comparison-sidebar-offset + $steps-list-offset;
|
|
280
|
+
height: 100%;
|
|
281
|
+
width: $comparison-sidebar-width;
|
|
282
|
+
min-height: 1.6em;
|
|
283
|
+
}
|
|
284
|
+
&::after {
|
|
285
|
+
top: -2px;
|
|
286
|
+
left: -$comparison-sidebar-padding + $steps-list-offset;
|
|
287
|
+
min-height: 1.6em;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Table-specific overrides
|
|
294
|
+
$table-row-offset: -2px;
|
|
295
|
+
|
|
296
|
+
table {
|
|
297
|
+
overflow-x: visible;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
tr.diff {
|
|
301
|
+
position: static;
|
|
302
|
+
display: table-row;
|
|
303
|
+
transform: scale(1);
|
|
304
|
+
|
|
305
|
+
&.del::after, &.ins::after {
|
|
306
|
+
left: -$comparison-sidebar-padding + $table-row-offset;
|
|
307
|
+
// Need to account for border in row element
|
|
308
|
+
height: 100%;
|
|
309
|
+
top: 1px;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Attaching a ::before psuedoelement to a <tr> creates a phantom column so we need to hide it
|
|
313
|
+
// and instead attach one to the first-child <td>
|
|
314
|
+
&::before {
|
|
315
|
+
display: none;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
td:first-child::before, th:first-child::before {
|
|
319
|
+
position: absolute;
|
|
320
|
+
left: -$comparison-sidebar-offset;
|
|
321
|
+
width: $comparison-sidebar-width;
|
|
322
|
+
height: 100%;
|
|
323
|
+
min-height: 1.5em;
|
|
324
|
+
top: 1px;
|
|
325
|
+
line-height: 1.5em;
|
|
326
|
+
text-align: center;
|
|
327
|
+
z-index: 10;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
&.del td:first-child::before, &.del th:first-child::before {
|
|
331
|
+
color: $text-colour;
|
|
332
|
+
background-color: $diff-marker-removed-color;
|
|
333
|
+
content: "−";
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
&.ins td:first-child::before, &.ins th:first-child::before {
|
|
337
|
+
color: $text-colour;
|
|
338
|
+
background-color: $diff-marker-added-color;
|
|
339
|
+
content: "+";
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
data/lib/nokodiff/differ.rb
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
module Nokodiff
|
|
2
2
|
class Differ
|
|
3
|
+
include FormattingHelpers
|
|
4
|
+
|
|
5
|
+
BOTTOM_LEVEL_ELEMENTS = %w[p li tr h1 h2 h3 h4 h5 h6].freeze
|
|
6
|
+
|
|
3
7
|
def initialize(before, after)
|
|
4
8
|
@before = before
|
|
5
9
|
@after = after
|
|
@@ -13,9 +17,9 @@ module Nokodiff
|
|
|
13
17
|
when :changed
|
|
14
18
|
changed_block(diff[:before], diff[:after])
|
|
15
19
|
when :deleted
|
|
16
|
-
diff[:before].
|
|
20
|
+
diff[:before].text? ? build_deleted_text_html(diff[:before]) : build_deleted_element_html(diff[:before])
|
|
17
21
|
when :added
|
|
18
|
-
diff[:after].
|
|
22
|
+
diff[:after].text? ? build_added_text_html(diff[:after]) : build_added_element_html(diff[:after])
|
|
19
23
|
end
|
|
20
24
|
}.join("\n")
|
|
21
25
|
end
|
|
@@ -60,21 +64,15 @@ module Nokodiff
|
|
|
60
64
|
end
|
|
61
65
|
|
|
62
66
|
def changed_block(before_node, after_node)
|
|
63
|
-
if structurally_similar?(before_node, after_node) &&
|
|
67
|
+
if structurally_similar?(before_node, after_node) && !treat_element_as_single_change?(before_node)
|
|
64
68
|
inner_diff = Differ.new(before_node, after_node).to_html
|
|
65
69
|
rebuild_element(after_node, inner_diff)
|
|
70
|
+
elsif both_text_nodes?(before_node, after_node)
|
|
71
|
+
before_node, after_node = diff_raw_text(before_node, after_node)
|
|
72
|
+
build_deleted_text_html(before_node) + build_added_text_html(after_node)
|
|
66
73
|
else
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
else
|
|
70
|
-
diff_sub_elements(before_node, after_node)
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
if before_node.name == "li"
|
|
74
|
-
deleted_li(before_diff) + added_li(after_diff)
|
|
75
|
-
else
|
|
76
|
-
deleted_block(before_diff) + added_block(after_diff)
|
|
77
|
-
end
|
|
74
|
+
before_node, after_node = diff_sub_elements(before_node, after_node)
|
|
75
|
+
build_deleted_element_html(before_node) + build_added_element_html(after_node)
|
|
78
76
|
end
|
|
79
77
|
end
|
|
80
78
|
|
|
@@ -88,13 +86,11 @@ module Nokodiff
|
|
|
88
86
|
before_node.name == after_node.name
|
|
89
87
|
end
|
|
90
88
|
|
|
91
|
-
# We want all
|
|
92
|
-
#
|
|
93
|
-
#
|
|
94
|
-
def
|
|
95
|
-
before_node.name
|
|
96
|
-
!before_node.name.match(/^h[1-6]$/) &&
|
|
97
|
-
before_node.name != "li"
|
|
89
|
+
# We want to treat all content within certain elements as single changes, even if they are structurally different,
|
|
90
|
+
# to avoid overwhelming the user with changes, and ensure any nested elements are included within the diff,
|
|
91
|
+
# rather than being treated as added or removed content on their own.
|
|
92
|
+
def treat_element_as_single_change?(before_node)
|
|
93
|
+
BOTTOM_LEVEL_ELEMENTS.include?(before_node.name)
|
|
98
94
|
end
|
|
99
95
|
|
|
100
96
|
def rebuild_element(template_node, inner_html)
|
|
@@ -118,19 +114,15 @@ module Nokodiff
|
|
|
118
114
|
end
|
|
119
115
|
|
|
120
116
|
def diff_sub_elements(before_html, after_html)
|
|
121
|
-
|
|
122
|
-
|
|
117
|
+
before_fragment = before_html.dup
|
|
118
|
+
after_fragment = after_html.dup
|
|
123
119
|
|
|
124
|
-
|
|
120
|
+
Nokodiff::TextNodeDiffs.new(before_fragment, after_fragment).call
|
|
125
121
|
|
|
126
122
|
merge_adjacent_highlighted_changes(before_fragment)
|
|
127
123
|
merge_adjacent_highlighted_changes(after_fragment)
|
|
128
124
|
|
|
129
|
-
|
|
130
|
-
[before_fragment.inner_html, after_fragment.inner_html]
|
|
131
|
-
else
|
|
132
|
-
[before_fragment.to_html, after_fragment.to_html]
|
|
133
|
-
end
|
|
125
|
+
[before_fragment, after_fragment]
|
|
134
126
|
end
|
|
135
127
|
|
|
136
128
|
def merge_adjacent_highlighted_changes(node)
|
|
@@ -159,40 +151,78 @@ module Nokodiff
|
|
|
159
151
|
node.to_html
|
|
160
152
|
end
|
|
161
153
|
|
|
162
|
-
def
|
|
163
|
-
|
|
164
|
-
<
|
|
165
|
-
<
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
</
|
|
177
|
-
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
def
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
154
|
+
def build_deleted_text_html(html)
|
|
155
|
+
<<~HTML
|
|
156
|
+
<span class="diff del">
|
|
157
|
+
<span class="visually-hidden">Removed content </span>
|
|
158
|
+
#{html}
|
|
159
|
+
</span>
|
|
160
|
+
HTML
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def build_added_text_html(html)
|
|
164
|
+
<<~HTML
|
|
165
|
+
<span class="diff ins">
|
|
166
|
+
<span class="visually-hidden">Added content </span>
|
|
167
|
+
#{html}
|
|
168
|
+
</span>
|
|
169
|
+
HTML
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def build_added_element_html(element)
|
|
173
|
+
if element.name == "tr"
|
|
174
|
+
insert_table_row_change_marker(element, "Added row")
|
|
175
|
+
class_string = "#{element['class']&.gsub('del', '')} diff ins".strip
|
|
176
|
+
|
|
177
|
+
<<~HTML
|
|
178
|
+
<#{element.name} class="#{class_string}">
|
|
179
|
+
#{element.inner_html}
|
|
180
|
+
</#{element.name}>
|
|
181
|
+
HTML
|
|
182
|
+
|
|
183
|
+
else
|
|
184
|
+
# Entirely new steps OLs require that the div also have the .steps class
|
|
185
|
+
# This is either a bug or weakness in the actual govuk CSS for that class
|
|
186
|
+
class_string = element["class"].nil? ? nil : " class= \"#{element['class']}\""
|
|
187
|
+
span_class_string = element["class"]&.include?("steps") ? "diff ins steps" : "diff ins"
|
|
188
|
+
|
|
189
|
+
<<~HTML
|
|
190
|
+
<#{element.name}#{class_string}>
|
|
191
|
+
<span class="#{span_class_string}">
|
|
192
|
+
<span class="visually-hidden">Added content </span>
|
|
193
|
+
#{element.inner_html}
|
|
194
|
+
</span>
|
|
195
|
+
</#{element.name}>
|
|
196
|
+
HTML
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def build_deleted_element_html(element)
|
|
201
|
+
if element.name == "tr"
|
|
202
|
+
insert_table_row_change_marker(element, "Removed row")
|
|
203
|
+
class_string = "#{element['class']&.gsub('ins', '')} diff del".strip
|
|
204
|
+
|
|
205
|
+
<<~HTML
|
|
206
|
+
<#{element.name} class="#{class_string}">
|
|
207
|
+
#{element.inner_html}
|
|
208
|
+
</#{element.name}>
|
|
209
|
+
HTML
|
|
210
|
+
|
|
211
|
+
else
|
|
212
|
+
# Entirely removed steps OLs require that the span also have the .steps class
|
|
213
|
+
# This is either a bug or weakness in the actual govuk CSS for that class
|
|
214
|
+
class_string = element["class"].nil? ? nil : " class= \"#{element['class']}\""
|
|
215
|
+
span_class_string = element["class"]&.include?("steps") ? "diff del steps" : "diff del"
|
|
216
|
+
|
|
217
|
+
<<~HTML
|
|
218
|
+
<#{element.name}#{class_string}>
|
|
219
|
+
<span class="#{span_class_string}">
|
|
220
|
+
<span class="visually-hidden">Removed content </span>
|
|
221
|
+
#{element.inner_html}
|
|
222
|
+
</span>
|
|
223
|
+
</#{element.name}>
|
|
224
|
+
HTML
|
|
225
|
+
end
|
|
196
226
|
end
|
|
197
227
|
end
|
|
198
228
|
end
|
|
@@ -6,5 +6,13 @@ module Nokodiff
|
|
|
6
6
|
n["class"] = "diff-marker"
|
|
7
7
|
end
|
|
8
8
|
end
|
|
9
|
+
|
|
10
|
+
def insert_table_row_change_marker(element, message)
|
|
11
|
+
marker = Nokogiri::XML::Node.new("span", element.document)
|
|
12
|
+
marker.content = "#{message} "
|
|
13
|
+
marker["class"] = "visually-hidden"
|
|
14
|
+
|
|
15
|
+
element.children.first.prepend_child(marker)
|
|
16
|
+
end
|
|
9
17
|
end
|
|
10
18
|
end
|
data/lib/nokodiff/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: nokodiff
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 1.0.0.rc1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- GOV.UK Dev
|
|
@@ -29,14 +29,14 @@ dependencies:
|
|
|
29
29
|
requirements:
|
|
30
30
|
- - '='
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: 13.
|
|
32
|
+
version: 13.4.2
|
|
33
33
|
type: :development
|
|
34
34
|
prerelease: false
|
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
37
|
- - '='
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: 13.
|
|
39
|
+
version: 13.4.2
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
41
41
|
name: rspec-html-matchers
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -71,14 +71,14 @@ dependencies:
|
|
|
71
71
|
requirements:
|
|
72
72
|
- - '='
|
|
73
73
|
- !ruby/object:Gem::Version
|
|
74
|
-
version: 5.2.
|
|
74
|
+
version: 5.2.1
|
|
75
75
|
type: :development
|
|
76
76
|
prerelease: false
|
|
77
77
|
version_requirements: !ruby/object:Gem::Requirement
|
|
78
78
|
requirements:
|
|
79
79
|
- - '='
|
|
80
80
|
- !ruby/object:Gem::Version
|
|
81
|
-
version: 5.2.
|
|
81
|
+
version: 5.2.1
|
|
82
82
|
- !ruby/object:Gem::Dependency
|
|
83
83
|
name: simplecov
|
|
84
84
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -150,7 +150,7 @@ dependencies:
|
|
|
150
150
|
version: '101.0'
|
|
151
151
|
- - "<"
|
|
152
152
|
- !ruby/object:Gem::Version
|
|
153
|
-
version: '103.
|
|
153
|
+
version: '103.4'
|
|
154
154
|
type: :runtime
|
|
155
155
|
prerelease: false
|
|
156
156
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -160,7 +160,7 @@ dependencies:
|
|
|
160
160
|
version: '101.0'
|
|
161
161
|
- - "<"
|
|
162
162
|
- !ruby/object:Gem::Version
|
|
163
|
-
version: '103.
|
|
163
|
+
version: '103.4'
|
|
164
164
|
- !ruby/object:Gem::Dependency
|
|
165
165
|
name: govspeak
|
|
166
166
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -244,7 +244,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
244
244
|
- !ruby/object:Gem::Version
|
|
245
245
|
version: '0'
|
|
246
246
|
requirements: []
|
|
247
|
-
rubygems_version: 4.0.
|
|
247
|
+
rubygems_version: 4.0.16
|
|
248
248
|
specification_version: 4
|
|
249
249
|
summary: A Ruby Gem to highlight additions, deletions and character level changes
|
|
250
250
|
while preserving original HTML
|