sitediff 0.0.6 → 1.2.0
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 +5 -5
- data/.eslintignore +1 -0
- data/.eslintrc.json +28 -0
- data/.project +11 -0
- data/.rubocop.yml +179 -0
- data/.rubocop_todo.yml +51 -0
- data/CHANGELOG.md +28 -0
- data/Dockerfile +33 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +85 -0
- data/INSTALLATION.md +146 -0
- data/LICENSE +339 -0
- data/README.md +810 -0
- data/Rakefile +12 -0
- data/Thorfile +135 -0
- data/bin/sitediff +9 -2
- data/config/.gitkeep +0 -0
- data/config/sanitize_domains.example.yaml +8 -0
- data/config/sitediff.example.yaml +81 -0
- data/docker-compose.test.yml +3 -0
- data/lib/sitediff/api.rb +276 -0
- data/lib/sitediff/cache.rb +57 -8
- data/lib/sitediff/cli.rb +156 -176
- data/lib/sitediff/config/creator.rb +61 -77
- data/lib/sitediff/config/preset.rb +75 -0
- data/lib/sitediff/config.rb +436 -31
- data/lib/sitediff/crawler.rb +27 -21
- data/lib/sitediff/diff.rb +32 -9
- data/lib/sitediff/fetch.rb +10 -3
- data/lib/sitediff/files/diff.html.erb +20 -2
- data/lib/sitediff/files/jquery.min.js +2 -0
- data/lib/sitediff/files/normalize.css +349 -0
- data/lib/sitediff/files/report.html.erb +171 -0
- data/lib/sitediff/files/sidebyside.html.erb +5 -2
- data/lib/sitediff/files/sitediff.css +303 -30
- data/lib/sitediff/files/sitediff.js +367 -0
- data/lib/sitediff/presets/drupal.yaml +63 -0
- data/lib/sitediff/report.rb +254 -0
- data/lib/sitediff/result.rb +50 -20
- data/lib/sitediff/sanitize/dom_transform.rb +47 -8
- data/lib/sitediff/sanitize/regexp.rb +24 -3
- data/lib/sitediff/sanitize.rb +81 -12
- data/lib/sitediff/uriwrapper.rb +65 -23
- data/lib/sitediff/webserver/resultserver.rb +30 -33
- data/lib/sitediff/webserver.rb +15 -3
- data/lib/sitediff.rb +130 -83
- data/misc/sitediff - overview report.png +0 -0
- data/misc/sitediff - page report.png +0 -0
- data/package-lock.json +878 -0
- data/package.json +25 -0
- data/sitediff.gemspec +51 -0
- metadata +91 -29
- data/lib/sitediff/files/html_report.html.erb +0 -66
- data/lib/sitediff/files/rules/drupal.yaml +0 -63
- data/lib/sitediff/rules.rb +0 -65
@@ -1,53 +1,326 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
font-size: 1.2em;
|
1
|
+
* {
|
2
|
+
box-sizing: border-box;
|
4
3
|
}
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
|
5
|
+
a {
|
6
|
+
color: #6E73B7;
|
7
|
+
text-decoration: none;
|
8
|
+
}
|
9
|
+
|
10
|
+
body {
|
11
|
+
font-family: sans-serif;
|
12
|
+
font-size: 16px;
|
13
|
+
padding: 3em 0;
|
14
|
+
}
|
15
|
+
|
16
|
+
.container {
|
17
|
+
overflow: hidden;
|
18
|
+
margin: 0 auto;
|
19
|
+
padding-left: 1em;
|
20
|
+
padding-right: 1em;
|
21
|
+
max-width: 1280px;
|
22
|
+
}
|
23
|
+
|
24
|
+
#footer {
|
25
|
+
font-size: 90%;
|
26
|
+
margin-top: 2em;
|
8
27
|
text-align: center;
|
9
28
|
}
|
10
|
-
|
11
|
-
|
12
|
-
|
29
|
+
|
30
|
+
.site-info {
|
31
|
+
display: flex;
|
32
|
+
flex-direction: row;
|
33
|
+
align-items: center;
|
34
|
+
justify-content: space-between;
|
35
|
+
}
|
36
|
+
|
37
|
+
.site-info .site {
|
38
|
+
background-color: #F9F9FB;
|
39
|
+
flex-basis: 49%;
|
40
|
+
flex-grow: 0;
|
41
|
+
flex-shrink: 0;
|
42
|
+
padding: 1em;
|
43
|
+
}
|
44
|
+
|
45
|
+
.site__tag {
|
46
|
+
margin-top: 0;
|
47
|
+
margin-bottom: .5em;
|
48
|
+
}
|
49
|
+
|
50
|
+
/* Statistical info */
|
51
|
+
.statistical-info {
|
52
|
+
display: flex;
|
53
|
+
align-items: center;
|
54
|
+
justify-content: space-between;
|
55
|
+
flex-direction: row;
|
56
|
+
margin-bottom: 1em;
|
57
|
+
}
|
58
|
+
|
59
|
+
.statistical-info > div {
|
60
|
+
flex-basis: 49%;
|
61
|
+
}
|
62
|
+
|
63
|
+
.statistical-info h3 {
|
64
|
+
display: inline-block;
|
65
|
+
margin-right: 1em;
|
13
66
|
}
|
14
|
-
|
67
|
+
|
68
|
+
.statistical-info .count {
|
69
|
+
background-color: #eeeeee;
|
70
|
+
border-radius: 50%;
|
71
|
+
display: inline-block;
|
72
|
+
font-weight: bold;
|
73
|
+
line-height: 2em;
|
74
|
+
padding: .1em .25em 0;
|
75
|
+
height: 2em;
|
76
|
+
width: 2em;
|
15
77
|
text-align: center;
|
16
78
|
}
|
17
|
-
|
79
|
+
|
80
|
+
/* Buttons */
|
81
|
+
.button {
|
82
|
+
background-color: #EAECF3;
|
83
|
+
cursor: pointer;
|
84
|
+
display: inline-block;
|
85
|
+
font-size: .9em;
|
86
|
+
padding: .25em .5em .3em;
|
87
|
+
text-decoration: none;
|
88
|
+
}
|
89
|
+
|
90
|
+
/**
|
91
|
+
* Forms.
|
92
|
+
*/
|
93
|
+
label[for] {
|
94
|
+
cursor: pointer;
|
95
|
+
}
|
96
|
+
|
97
|
+
/* Checkboxes */
|
98
|
+
.form-checkboxes .form-checkbox {
|
99
|
+
display: inline-block;
|
100
|
+
margin-right: .5em;
|
101
|
+
}
|
102
|
+
|
103
|
+
.form-checkbox input {
|
104
|
+
height: 1em;
|
105
|
+
width: 1em;
|
106
|
+
}
|
107
|
+
|
108
|
+
/* Search */
|
109
|
+
.form-search label {
|
110
|
+
display: none;
|
111
|
+
}
|
112
|
+
|
113
|
+
.form-search input {
|
114
|
+
font-size: .9em;
|
115
|
+
padding-left: .5em;
|
116
|
+
padding-right: .5em;
|
117
|
+
width: 200px;
|
118
|
+
}
|
119
|
+
|
120
|
+
/* SiteDiff Toolbar */
|
121
|
+
.sitediff-toolbar {
|
122
|
+
margin-top: 1em;
|
123
|
+
margin-bottom: 1em;
|
124
|
+
overflow: hidden;
|
125
|
+
padding-top: 5px;
|
126
|
+
padding-bottom: 5px;
|
127
|
+
}
|
128
|
+
|
129
|
+
.sitediff-toolbar .form-item {
|
130
|
+
display: inline-block;
|
131
|
+
margin-right: 1em;
|
132
|
+
}
|
133
|
+
|
134
|
+
.sitediff-toolbar .form-item:last-child {
|
135
|
+
margin-right: 0;
|
136
|
+
}
|
137
|
+
|
138
|
+
.sitediff-toolbar .toolbar__left {
|
139
|
+
float: left;
|
140
|
+
}
|
141
|
+
|
142
|
+
.sitediff-toolbar .toolbar__right {
|
143
|
+
float: right;
|
144
|
+
}
|
145
|
+
|
146
|
+
/* Table */
|
147
|
+
table {
|
148
|
+
width: 100%;
|
149
|
+
}
|
150
|
+
|
151
|
+
table thead {
|
152
|
+
background: #F9F9FB;
|
153
|
+
color: #999999;
|
154
|
+
}
|
155
|
+
|
156
|
+
td,
|
157
|
+
th {
|
18
158
|
text-align: left;
|
19
|
-
padding
|
159
|
+
padding: .4em .5em;
|
20
160
|
}
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
161
|
+
|
162
|
+
th {
|
163
|
+
border-bottom: 2px solid #EAECF3;
|
164
|
+
padding-top: .5em;
|
165
|
+
padding-bottom: .5em;
|
26
166
|
}
|
27
|
-
|
28
|
-
|
29
|
-
|
167
|
+
|
168
|
+
table tbody td {
|
169
|
+
border-bottom: 2px solid #F9F9FB;
|
30
170
|
}
|
31
|
-
|
32
|
-
|
33
|
-
background-color:
|
171
|
+
|
172
|
+
table tbody tr:hover {
|
173
|
+
background-color: #F9F9FB;
|
34
174
|
}
|
35
|
-
|
36
|
-
.
|
37
|
-
|
38
|
-
.sitediff .diff-stat-col {
|
39
|
-
width: 10%;
|
175
|
+
|
176
|
+
table .path {
|
177
|
+
margin-right: 1em;
|
40
178
|
}
|
41
|
-
|
42
|
-
|
179
|
+
|
180
|
+
table td.description .buttons {
|
181
|
+
float: right;
|
182
|
+
}
|
183
|
+
|
184
|
+
table td.description a {
|
185
|
+
display: none;
|
186
|
+
margin-left: .4em;
|
187
|
+
}
|
188
|
+
|
189
|
+
table td.description:hover a {
|
190
|
+
display: inline-block;
|
191
|
+
}
|
192
|
+
|
193
|
+
table td.description .button-diff {
|
194
|
+
display: block;
|
195
|
+
}
|
196
|
+
|
197
|
+
table .icon-col {
|
198
|
+
width: 25px;
|
43
199
|
}
|
44
200
|
|
201
|
+
table .status-col {
|
202
|
+
width: 100px;
|
203
|
+
}
|
204
|
+
|
205
|
+
/* Icons */
|
206
|
+
.icon {
|
207
|
+
display: inline-block;
|
208
|
+
height: 1em;
|
209
|
+
width: 1em;
|
210
|
+
}
|
211
|
+
|
212
|
+
.icon-result-changed {
|
213
|
+
background-color: lightcoral;
|
214
|
+
border-radius: 50%;
|
215
|
+
}
|
216
|
+
|
217
|
+
.icon-result-unchanged {
|
218
|
+
background-color: lightgreen;
|
219
|
+
border-radius: 50%;
|
220
|
+
}
|
221
|
+
|
222
|
+
.icon-result-error {
|
223
|
+
background: khaki;
|
224
|
+
border-radius: 50%;
|
225
|
+
}
|
226
|
+
|
227
|
+
/* Side-by-side view */
|
45
228
|
#sidebyside {
|
46
229
|
margin: 0;
|
47
230
|
}
|
231
|
+
|
48
232
|
#sidebyside iframe {
|
49
233
|
float: left;
|
50
234
|
height: 100%;
|
51
235
|
width: 50%;
|
52
236
|
border: 0;
|
53
237
|
}
|
238
|
+
|
239
|
+
/* Page: Diff */
|
240
|
+
#diff-navigator {
|
241
|
+
background-color: #fefefe;
|
242
|
+
box-shadow: 0 0 .5em rgba(0, 0, 0, .25);
|
243
|
+
padding: .25em;
|
244
|
+
margin-top: 0;
|
245
|
+
position: fixed;
|
246
|
+
top: 0;
|
247
|
+
width: 100%;
|
248
|
+
}
|
249
|
+
|
250
|
+
/*** Overlay */
|
251
|
+
.overlay {
|
252
|
+
background-color: rgba(0, 0, 0, 0.25);
|
253
|
+
height: 100vh;
|
254
|
+
left: 0;
|
255
|
+
padding: 2em;
|
256
|
+
position: fixed;
|
257
|
+
top: 0;
|
258
|
+
width: 100vw;
|
259
|
+
}
|
260
|
+
|
261
|
+
.overlay__inner {
|
262
|
+
background-color: #fff;
|
263
|
+
height: 100%;
|
264
|
+
margin: 0;
|
265
|
+
position: relative;
|
266
|
+
width: 100%;
|
267
|
+
}
|
268
|
+
|
269
|
+
.overlay header {
|
270
|
+
background-color: #f9f9fb;
|
271
|
+
border-bottom: 2px solid #eaecf3;
|
272
|
+
display: flex;
|
273
|
+
min-height: 3em;
|
274
|
+
left: 0;
|
275
|
+
position: absolute;
|
276
|
+
top: 0;
|
277
|
+
width: 100%;
|
278
|
+
}
|
279
|
+
|
280
|
+
.overlay header .prev,
|
281
|
+
.overlay header .next,
|
282
|
+
.overlay header .exit {
|
283
|
+
padding: 0.5em 0;
|
284
|
+
margin: 0.5em;
|
285
|
+
}
|
286
|
+
|
287
|
+
.overlay header .prev a,
|
288
|
+
.overlay header .next a,
|
289
|
+
.overlay header .exit a {
|
290
|
+
background-color: #eaecf3;
|
291
|
+
font-size: .9em;
|
292
|
+
padding: 0.5em;
|
293
|
+
text-decoration: none;
|
294
|
+
}
|
295
|
+
|
296
|
+
.overlay header .prev a:hover,
|
297
|
+
.overlay header .next a:hover,
|
298
|
+
.overlay header .exit a:hover {
|
299
|
+
background-color: #d2d7ef;
|
300
|
+
}
|
301
|
+
|
302
|
+
.overlay header .prev.disabled a,
|
303
|
+
.overlay header .next.disabled a,
|
304
|
+
.overlay header .exit.disabled a,
|
305
|
+
.overlay header .prev.disabled a:hover,
|
306
|
+
.overlay header .next.disabled a:hover,
|
307
|
+
.overlay header .exit.disabled a:hover {
|
308
|
+
background-color: eaecf3;
|
309
|
+
color: #9b9db8;
|
310
|
+
cursor: not-allowed;
|
311
|
+
}
|
312
|
+
.overlay header .path {
|
313
|
+
flex-grow: 1;
|
314
|
+
padding: 1em;
|
315
|
+
}
|
316
|
+
|
317
|
+
.overlay article {
|
318
|
+
background-color: #fff;
|
319
|
+
height: 100%;
|
320
|
+
width: 100%;
|
321
|
+
}
|
322
|
+
|
323
|
+
.overlay article iframe {
|
324
|
+
height: 100%;
|
325
|
+
width: 100%;
|
326
|
+
}
|
@@ -0,0 +1,367 @@
|
|
1
|
+
/**
|
2
|
+
* @file
|
3
|
+
* SiteDiff report behaviors.
|
4
|
+
*/
|
5
|
+
|
6
|
+
'use strict';
|
7
|
+
|
8
|
+
/* global $ */
|
9
|
+
/**
|
10
|
+
* SiteDiff namespace.
|
11
|
+
*/
|
12
|
+
var SiteDiff = SiteDiff || {};
|
13
|
+
|
14
|
+
/**
|
15
|
+
* SiteDiff global map of diffs.
|
16
|
+
*/
|
17
|
+
SiteDiff.diffs = SiteDiff.diffs || {};
|
18
|
+
|
19
|
+
SiteDiff.currentDiff = -1;
|
20
|
+
|
21
|
+
/**
|
22
|
+
* Scrolls the document to the said position.
|
23
|
+
*
|
24
|
+
* @param options
|
25
|
+
* Object specifying various options.
|
26
|
+
*
|
27
|
+
* x: X position.
|
28
|
+
* y: Y position.
|
29
|
+
* animate: Whether to animate.
|
30
|
+
* callback: A function to call after scrolling.
|
31
|
+
*/
|
32
|
+
SiteDiff.scrollToPosition = function (options) {
|
33
|
+
// Compute vertical and horizontal adjustments, if any.
|
34
|
+
// Example: Fixed elements, etc.
|
35
|
+
var xFix = 0;
|
36
|
+
var yFix = 0 - 100;
|
37
|
+
|
38
|
+
// Determine final x and y offsets.
|
39
|
+
var x = parseInt(options.x) + xFix;
|
40
|
+
x = Math.max(x, 0);
|
41
|
+
var y = parseInt(options.y) + yFix;
|
42
|
+
y = Math.max(y, 0);
|
43
|
+
|
44
|
+
// Perform the scroll with or without animation.
|
45
|
+
window.scrollTo(x, y);
|
46
|
+
|
47
|
+
// Trigger a callback, if any.
|
48
|
+
if (options.callback) {
|
49
|
+
options.callback();
|
50
|
+
}
|
51
|
+
};
|
52
|
+
|
53
|
+
/**
|
54
|
+
* Scrolls to a DOM element on the page.
|
55
|
+
*
|
56
|
+
* @param el
|
57
|
+
* The DOM element.
|
58
|
+
*
|
59
|
+
* @param options
|
60
|
+
* Object specifying various options.
|
61
|
+
*
|
62
|
+
* "callback" to trigger after scrolling.
|
63
|
+
*/
|
64
|
+
SiteDiff.scrollToElement = function (el, options) {
|
65
|
+
options = options || {};
|
66
|
+
var callback = options.callback || function () {};
|
67
|
+
|
68
|
+
// See if the element exists.
|
69
|
+
var $el = $(el).first();
|
70
|
+
if ($el.length == 1) {
|
71
|
+
// Inject callback to focus on the element we scroll to.
|
72
|
+
options.x = 0;
|
73
|
+
options.y = $el.offset().top;
|
74
|
+
options.callback = function () {
|
75
|
+
$el.focus();
|
76
|
+
callback.call(el);
|
77
|
+
};
|
78
|
+
SiteDiff.scrollToPosition(options);
|
79
|
+
}
|
80
|
+
};
|
81
|
+
|
82
|
+
/**
|
83
|
+
* Initialize behaviors.
|
84
|
+
*/
|
85
|
+
SiteDiff.init = function () {
|
86
|
+
// On the overview page.
|
87
|
+
switch ($(document.body).data('page')) {
|
88
|
+
case 'overview':
|
89
|
+
SiteDiff.initFilterForm();
|
90
|
+
break;
|
91
|
+
|
92
|
+
case 'diff':
|
93
|
+
SiteDiff.jumpToFirstDiff();
|
94
|
+
break;
|
95
|
+
}
|
96
|
+
};
|
97
|
+
|
98
|
+
/**
|
99
|
+
* Initializes report filters and overlay.
|
100
|
+
*/
|
101
|
+
SiteDiff.initFilterForm = function () {
|
102
|
+
SiteDiff.initDiffArray();
|
103
|
+
SiteDiff.initStatusFilter();
|
104
|
+
SiteDiff.initSearchFilter();
|
105
|
+
SiteDiff.initOverlay();
|
106
|
+
SiteDiff.initClickHandlers();
|
107
|
+
};
|
108
|
+
|
109
|
+
/**
|
110
|
+
* Initialize global diff array
|
111
|
+
*
|
112
|
+
*/
|
113
|
+
SiteDiff.initDiffArray = function() {
|
114
|
+
SiteDiff.diffs = $('.button-diff').map(function (i, element) {
|
115
|
+
var $el = $(element);
|
116
|
+
$el.data('diffindex', i);
|
117
|
+
return {
|
118
|
+
diff: $el.attr('href'),
|
119
|
+
element: $el,
|
120
|
+
index: i,
|
121
|
+
path: $el.parents('.description').find('.path').text()
|
122
|
+
};
|
123
|
+
});
|
124
|
+
};
|
125
|
+
|
126
|
+
/**
|
127
|
+
* Initializes the "status" filter.
|
128
|
+
*/
|
129
|
+
SiteDiff.initStatusFilter = function () {
|
130
|
+
$('.form-item--status input')
|
131
|
+
.on('change', function () {
|
132
|
+
// Get a list of applied filters.
|
133
|
+
var appliedFilters = $('.form-item--status input:checked')
|
134
|
+
.map(function () {
|
135
|
+
return this.getAttribute('value');
|
136
|
+
// applied.push(this.getAttribute('value'));
|
137
|
+
});
|
138
|
+
// Show only matching results, hide the rest.
|
139
|
+
$('#sitediff-report')
|
140
|
+
.find('.sitediff-result')
|
141
|
+
.each(function () {
|
142
|
+
var $row = $(this);
|
143
|
+
var status = $row.data('status');
|
144
|
+
if (
|
145
|
+
// Row matches applied filters.
|
146
|
+
$.inArray(status, appliedFilters) > -1 ||
|
147
|
+
// No filters are applied.
|
148
|
+
appliedFilters.length === 0
|
149
|
+
) {
|
150
|
+
$row.removeAttr('hidden');
|
151
|
+
}
|
152
|
+
else {
|
153
|
+
$row.attr('hidden', 'hidden');
|
154
|
+
}
|
155
|
+
});
|
156
|
+
});
|
157
|
+
};
|
158
|
+
|
159
|
+
/**
|
160
|
+
* Initializes the "search" filter.
|
161
|
+
*/
|
162
|
+
SiteDiff.initSearchFilter = function () {
|
163
|
+
$('#input-search')
|
164
|
+
.on('change keyup', function () {
|
165
|
+
var keyword = $(this).val().toLowerCase();
|
166
|
+
|
167
|
+
// Filter the records.
|
168
|
+
// TODO: Trigger one event per 250ms.
|
169
|
+
$('#sitediff-report')
|
170
|
+
.find('.sitediff-result')
|
171
|
+
.each(function () {
|
172
|
+
var $row = $(this);
|
173
|
+
var path = $row.find('.path').text();
|
174
|
+
|
175
|
+
// If keyword matches, keep the row visible.
|
176
|
+
if (path.toLowerCase().indexOf(keyword) > -1) {
|
177
|
+
$row.attr('hidden', null);
|
178
|
+
}
|
179
|
+
else {
|
180
|
+
$row.attr('hidden', 'hidden');
|
181
|
+
}
|
182
|
+
});
|
183
|
+
});
|
184
|
+
};
|
185
|
+
|
186
|
+
/**
|
187
|
+
* Set up the diff overlay to be displayed.
|
188
|
+
*/
|
189
|
+
SiteDiff.initOverlay = function () {
|
190
|
+
if (SiteDiff.diffs.length <= 0) return;
|
191
|
+
|
192
|
+
// add overlay
|
193
|
+
$('body').append($(
|
194
|
+
'<div class="overlay" style="display: none;"><div class="overlay__inner"><header>' +
|
195
|
+
'<div class="path"></div>' +
|
196
|
+
'<div class="prev"><a href="#" title="Previous diff (left arrow)">< Prev</a></div>' +
|
197
|
+
'<div class="next"><a href="#" title="Next diff (right arrow)">Next ></a></div>' +
|
198
|
+
'<div class="exit"><a href="#" title="Close diff display (Esc)">Close</a></div>' +
|
199
|
+
'</header><article></article></div></div>'));
|
200
|
+
// add header click handlers
|
201
|
+
$('.overlay header .exit').click(function (event) {
|
202
|
+
event.preventDefault();
|
203
|
+
SiteDiff.destroyOverlay();
|
204
|
+
});
|
205
|
+
$('.overlay header .prev').click(function (event) {
|
206
|
+
event.preventDefault();
|
207
|
+
SiteDiff.prevDiff();
|
208
|
+
});
|
209
|
+
$('.overlay header .next').click(function (event) {
|
210
|
+
event.preventDefault();
|
211
|
+
SiteDiff.nextDiff();
|
212
|
+
});
|
213
|
+
|
214
|
+
};
|
215
|
+
|
216
|
+
/**
|
217
|
+
* Set up click handlers for all diff buttons
|
218
|
+
*/
|
219
|
+
SiteDiff.initClickHandlers = function () {
|
220
|
+
SiteDiff.diffs.each( function (i, diff) {
|
221
|
+
diff.element.click({index: i}, function (event) {
|
222
|
+
event.preventDefault();
|
223
|
+
SiteDiff.openOverlay(event.data.index);
|
224
|
+
});
|
225
|
+
});
|
226
|
+
};
|
227
|
+
|
228
|
+
/**
|
229
|
+
* Set up key handlers for overlay.
|
230
|
+
*/
|
231
|
+
SiteDiff.initKeyHandlers = function () {
|
232
|
+
$(document).keyup(function (event) {
|
233
|
+
switch (event.which) {
|
234
|
+
case 37:
|
235
|
+
SiteDiff.prevDiff();
|
236
|
+
break;
|
237
|
+
case 39:
|
238
|
+
SiteDiff.nextDiff();
|
239
|
+
break;
|
240
|
+
case 27:
|
241
|
+
SiteDiff.destroyOverlay();
|
242
|
+
break;
|
243
|
+
}
|
244
|
+
});
|
245
|
+
};
|
246
|
+
|
247
|
+
/**
|
248
|
+
* Remove overlay key handlers.
|
249
|
+
*/
|
250
|
+
SiteDiff.removeKeyHandlers = function () {
|
251
|
+
$(document).off('keyup');
|
252
|
+
};
|
253
|
+
|
254
|
+
/**
|
255
|
+
* Open overlay for the diff identified by the `index`.
|
256
|
+
*
|
257
|
+
* @param integer index
|
258
|
+
* The index of the diff to be viewed.
|
259
|
+
*/
|
260
|
+
SiteDiff.openOverlay = function (index) {
|
261
|
+
SiteDiff.currentDiff = index;
|
262
|
+
SiteDiff.showDiff();
|
263
|
+
SiteDiff.initKeyHandlers();
|
264
|
+
$('.overlay').fadeIn(300);
|
265
|
+
};
|
266
|
+
|
267
|
+
/**
|
268
|
+
* Create the iframe to display the current diff.
|
269
|
+
*/
|
270
|
+
SiteDiff.showDiff = function () {
|
271
|
+
var diff = SiteDiff.diffs[SiteDiff.currentDiff];
|
272
|
+
var iframe = '<iframe src="' + diff.diff + '"></iframe>';
|
273
|
+
$('.overlay header .path').text(diff.path);
|
274
|
+
SiteDiff.setPrevNext();
|
275
|
+
$('.overlay article').html(iframe);
|
276
|
+
};
|
277
|
+
|
278
|
+
/**
|
279
|
+
* Hide the overlay and clean up.
|
280
|
+
*/
|
281
|
+
SiteDiff.destroyOverlay = function () {
|
282
|
+
$('.overlay article').empty();
|
283
|
+
SiteDiff.removeKeyHandlers();
|
284
|
+
$('.overlay').fadeOut(300, SiteDiff.scrollToButton);
|
285
|
+
};
|
286
|
+
|
287
|
+
/**
|
288
|
+
* Display the previous diff.
|
289
|
+
*/
|
290
|
+
SiteDiff.prevDiff = function () {
|
291
|
+
if (SiteDiff.currentDiff > 0) {
|
292
|
+
SiteDiff.currentDiff--;
|
293
|
+
SiteDiff.showDiff();
|
294
|
+
}
|
295
|
+
};
|
296
|
+
|
297
|
+
/**
|
298
|
+
* Display the next diff.
|
299
|
+
*/
|
300
|
+
SiteDiff.nextDiff = function () {
|
301
|
+
if (SiteDiff.currentDiff < SiteDiff.diffs.length - 1) {
|
302
|
+
SiteDiff.currentDiff++;
|
303
|
+
SiteDiff.showDiff();
|
304
|
+
}
|
305
|
+
};
|
306
|
+
|
307
|
+
/**
|
308
|
+
* Enable or disable prev and next buttons based on current diff.
|
309
|
+
*/
|
310
|
+
SiteDiff.setPrevNext = function () {
|
311
|
+
if (SiteDiff.currentDiff <= 0) {
|
312
|
+
// set prev disabled
|
313
|
+
$('.overlay header .prev').addClass('disabled');
|
314
|
+
}
|
315
|
+
else {
|
316
|
+
$('.overlay header .prev.disabled').removeClass('disabled');
|
317
|
+
}
|
318
|
+
if (SiteDiff.currentDiff >= SiteDiff.diffs.length - 1) {
|
319
|
+
// set next disabled
|
320
|
+
$('.overlay header .next').addClass('disabled');
|
321
|
+
}
|
322
|
+
else {
|
323
|
+
$('.overlay header .next.disabled').removeClass('disabled');
|
324
|
+
}
|
325
|
+
};
|
326
|
+
|
327
|
+
/**
|
328
|
+
* Scroll to the button associated with the current diff.
|
329
|
+
*/
|
330
|
+
SiteDiff.scrollToButton = function () {
|
331
|
+
var $diffButton = SiteDiff.diffs[SiteDiff.currentDiff].element;
|
332
|
+
if (! SiteDiff.isElementVisible($diffButton)) {
|
333
|
+
SiteDiff.scrollToElement($diffButton);
|
334
|
+
}
|
335
|
+
};
|
336
|
+
|
337
|
+
/**
|
338
|
+
* Check if an element is at least partly visible.
|
339
|
+
* @param element
|
340
|
+
*/
|
341
|
+
SiteDiff.isElementVisible = function (element) {
|
342
|
+
var topVisible = $(window).scrollTop();
|
343
|
+
var bottomVisible = topVisible + $(window).height();
|
344
|
+
var elemTop = $(element).offset().top;
|
345
|
+
var elemBottom = elemTop + $(element).height();
|
346
|
+
return ((elemBottom <= bottomVisible) && (elemTop >= topVisible));
|
347
|
+
};
|
348
|
+
|
349
|
+
/**
|
350
|
+
* Jumps to the first diff on the page.
|
351
|
+
*/
|
352
|
+
SiteDiff.jumpToFirstDiff = function () {
|
353
|
+
// Get the first diff hunk.
|
354
|
+
var $diff = $('#diff-container')
|
355
|
+
.find('.del, .ins')
|
356
|
+
.first();
|
357
|
+
if ($diff.length === 0) {
|
358
|
+
return;
|
359
|
+
}
|
360
|
+
|
361
|
+
// Scroll the window to it!
|
362
|
+
setTimeout(function () {
|
363
|
+
SiteDiff.scrollToElement($diff[0]);
|
364
|
+
}, 250);
|
365
|
+
};
|
366
|
+
|
367
|
+
$(document).ready(SiteDiff.init);
|