-
Notifications
You must be signed in to change notification settings - Fork 77
/
script.js
194 lines (159 loc) · 5.66 KB
/
script.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
{
function fixTable() {
const table = document.querySelector('table');
// Remove <hr>s
Array.from(table.querySelectorAll('hr')).forEach(({ parentNode }) => {
const row = parentNode.parentNode;
row.parentNode.removeChild(row);
});
// Make a table head.
const thead = document.createElement('thead');
const firstRow = table.querySelector('tr');
firstRow.parentNode.removeChild(firstRow);
thead.appendChild(firstRow);
table.insertBefore(thead, table.firstElementChild);
// Remove the first column and put the image in the next.
const rows = Array.from(table.querySelectorAll('tr'));
rows.forEach((row) => {
const iconColumn = row.children[0];
const fileColumn = row.children[1];
// Remove icon column.
row.removeChild(iconColumn);
const image = iconColumn.firstElementChild;
if (!image) {
return;
}
// Wrap icon in a div.img-wrap.
const div = document.createElement('div');
div.className = 'img-wrap';
div.appendChild(image);
// Insert icon before filename.
fileColumn.insertBefore(div, fileColumn.firstElementChild);
});
}
// Underscore string's titleize.
function titleize(str) {
return decodeURI(str).toLowerCase().replace(/(?:^|\s|-)\S/g, c => c.toUpperCase());
}
function addTitle() {
let path = window.location.pathname.replace(/\/$/g, '');
let titleText;
if (path) {
const parts = path.split('/');
path = parts[parts.length - 1];
titleText = titleize(path).replace(/-|_/g, ' ');
} else {
titleText = window.location.host;
}
titleText = `Index of ${titleText}`;
const container = document.createElement('div');
container.id = 'page-header';
const h1 = document.createElement('h1');
h1.appendChild(document.createTextNode(titleText));
container.appendChild(h1);
document.body.insertBefore(container, document.body.firstChild);
document.title = titleText;
}
/**
* Get the value and unit to use for RelativeTimeFormat.
* @param {number} seconds Difference in seconds between two dates.
*/
function getTimeFormatArgs(seconds) {
const absoluteSeconds = Math.abs(seconds);
if (absoluteSeconds > 60 * 60 * 24 * 365) {
return { value: seconds / (60 * 60 * 24 * 365), unit: 'year' };
}
if (absoluteSeconds > 60 * 60 * 24 * 30) {
return { value: seconds / (60 * 60 * 24 * 30), unit: 'month' };
}
if (absoluteSeconds > 60 * 60 * 24) {
return { value: seconds / (60 * 60 * 24), unit: 'day' };
}
if (absoluteSeconds > 60 * 60) {
return { value: seconds / (60 * 60), unit: 'hour' };
}
if (absoluteSeconds > 60) {
return { value: seconds / 60, unit: 'minute' };
}
return { value: seconds, unit: 'second' };
}
/**
* Convert the date output from the server to a Date instance.
* @param {string} str Date string from the server.
* @return {Date | null}
*/
function getDateFromString(str) {
if (!str) {
return null;
}
// 2014-12-09 10:43 -> 2014, 11, 09, 10, 43, 0.
const parts = str.split(' ');
const day = parts[0].split('-');
const timeOfDay = parts[1].split(':');
const year = parseInt(day[0], 10);
const month = parseInt(day[1], 10) - 1;
const _day = parseInt(day[2], 10);
const hour = parseInt(timeOfDay[0], 10);
const minutes = parseInt(timeOfDay[1], 10);
return new Date(year, month, _day, hour, minutes, 0);
}
function fixTime() {
const hasRelativeTimeFormatter = 'RelativeTimeFormat' in Intl;
if (!hasRelativeTimeFormatter) return;
const formatter = new Intl.RelativeTimeFormat();
const now = Date.now();
Array.from(document.querySelectorAll('.indexcollastmod')).forEach((date, i) => {
// Skip the first row because it's the link to the parent directory.
if (i === 0) {
return;
}
const lastModified = getDateFromString(date.textContent.trim());
if (lastModified && !Number.isNaN(lastModified)) {
const difference = Math.round((lastModified.getTime() - now) / 1000);
const relativeFormat = getTimeFormatArgs(difference);
date.textContent = formatter.format(Math.round(relativeFormat.value), relativeFormat.unit);
}
});
}
function addSearch() {
const input = document.createElement('input');
input.type = 'search';
input.id = 'search';
input.setAttribute('placeholder', 'Search');
document.getElementById('page-header').appendChild(input);
const sortColumns = Array.from(document.querySelectorAll('thead a'));
const nameColumns = Array.from(document.querySelectorAll('tbody .indexcolname'));
const rows = nameColumns.map(({ parentNode }) => parentNode);
const fileNames = nameColumns.map(({ textContent }) => textContent);
function filter(value) {
// Allow tabbing out of the search input and skipping the sort links
// when there is a search value.
sortColumns.forEach((link) => {
if (value) {
link.tabIndex = -1;
} else {
link.removeAttribute('tabIndex');
}
});
// Test the input against the file/folder name.
let even = false;
fileNames.forEach((name, i) => {
if (!value || name.toLowerCase().includes(value.toLowerCase())) {
const className = even ? 'even' : '';
rows[i].className = className;
even = !even;
} else {
rows[i].className = 'hidden';
}
});
}
document.getElementById('search').addEventListener('input', ({ target }) => {
filter(target.value);
});
filter('');
}
fixTable();
addTitle();
fixTime();
addSearch();
}