jekyll-theme-apcsp 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/_includes/alert.md +0 -0
- data/_includes/footer.md +0 -0
- data/_includes/head.html +0 -0
- data/_includes/header.md +1 -0
- data/_includes/nav.md +0 -0
- data/_layouts/page.html +308 -0
- data/_layouts/redirect.html +12 -0
- data/_sass/bootstrap/LICENSE +22 -0
- data/_sass/bootstrap/scss/_accordion.scss +146 -0
- data/_sass/bootstrap/scss/_alert.scss +71 -0
- data/_sass/bootstrap/scss/_badge.scss +40 -0
- data/_sass/bootstrap/scss/_breadcrumb.scss +42 -0
- data/_sass/bootstrap/scss/_button-group.scss +142 -0
- data/_sass/bootstrap/scss/_buttons.scss +158 -0
- data/_sass/bootstrap/scss/_card.scss +234 -0
- data/_sass/bootstrap/scss/_carousel.scss +229 -0
- data/_sass/bootstrap/scss/_close.scss +40 -0
- data/_sass/bootstrap/scss/_code.scss +48 -0
- data/_sass/bootstrap/scss/_containers.scss +41 -0
- data/_sass/bootstrap/scss/_custom-forms.scss +526 -0
- data/_sass/bootstrap/scss/_dropdown.scss +248 -0
- data/_sass/bootstrap/scss/_forms.scss +9 -0
- data/_sass/bootstrap/scss/_functions.scss +302 -0
- data/_sass/bootstrap/scss/_grid.scss +33 -0
- data/_sass/bootstrap/scss/_helpers.scss +10 -0
- data/_sass/bootstrap/scss/_images.scss +42 -0
- data/_sass/bootstrap/scss/_input-group.scss +208 -0
- data/_sass/bootstrap/scss/_jumbotron.scss +17 -0
- data/_sass/bootstrap/scss/_list-group.scss +191 -0
- data/_sass/bootstrap/scss/_maps.scss +54 -0
- data/_sass/bootstrap/scss/_media.scss +8 -0
- data/_sass/bootstrap/scss/_mixins.scss +43 -0
- data/_sass/bootstrap/scss/_modal.scss +237 -0
- data/_sass/bootstrap/scss/_nav.scss +172 -0
- data/_sass/bootstrap/scss/_navbar.scss +274 -0
- data/_sass/bootstrap/scss/_offcanvas.scss +143 -0
- data/_sass/bootstrap/scss/_pagination.scss +111 -0
- data/_sass/bootstrap/scss/_placeholders.scss +51 -0
- data/_sass/bootstrap/scss/_popover.scss +198 -0
- data/_sass/bootstrap/scss/_print.scss +141 -0
- data/_sass/bootstrap/scss/_progress.scss +59 -0
- data/_sass/bootstrap/scss/_reboot.scss +610 -0
- data/_sass/bootstrap/scss/_root.scss +76 -0
- data/_sass/bootstrap/scss/_spinners.scss +85 -0
- data/_sass/bootstrap/scss/_tables.scss +164 -0
- data/_sass/bootstrap/scss/_toasts.scss +70 -0
- data/_sass/bootstrap/scss/_tooltip.scss +122 -0
- data/_sass/bootstrap/scss/_transitions.scss +27 -0
- data/_sass/bootstrap/scss/_type.scss +104 -0
- data/_sass/bootstrap/scss/_utilities.scss +647 -0
- data/_sass/bootstrap/scss/_variables.scss +1631 -0
- data/_sass/bootstrap/scss/bootstrap-grid.scss +68 -0
- data/_sass/bootstrap/scss/bootstrap-reboot.scss +14 -0
- data/_sass/bootstrap/scss/bootstrap-utilities.scss +19 -0
- data/_sass/bootstrap/scss/bootstrap.scss +54 -0
- data/_sass/bootstrap/scss/forms/_floating-labels.scss +70 -0
- data/_sass/bootstrap/scss/forms/_form-check.scss +175 -0
- data/_sass/bootstrap/scss/forms/_form-control.scss +189 -0
- data/_sass/bootstrap/scss/forms/_form-range.scss +91 -0
- data/_sass/bootstrap/scss/forms/_form-select.scss +71 -0
- data/_sass/bootstrap/scss/forms/_form-text.scss +11 -0
- data/_sass/bootstrap/scss/forms/_input-group.scss +121 -0
- data/_sass/bootstrap/scss/forms/_labels.scss +36 -0
- data/_sass/bootstrap/scss/forms/_validation.scss +12 -0
- data/_sass/bootstrap/scss/helpers/_clearfix.scss +3 -0
- data/_sass/bootstrap/scss/helpers/_color-bg.scss +10 -0
- data/_sass/bootstrap/scss/helpers/_colored-links.scss +12 -0
- data/_sass/bootstrap/scss/helpers/_position.scss +36 -0
- data/_sass/bootstrap/scss/helpers/_ratio.scss +26 -0
- data/_sass/bootstrap/scss/helpers/_stacks.scss +15 -0
- data/_sass/bootstrap/scss/helpers/_stretched-link.scss +15 -0
- data/_sass/bootstrap/scss/helpers/_text-truncation.scss +7 -0
- data/_sass/bootstrap/scss/helpers/_visually-hidden.scss +8 -0
- data/_sass/bootstrap/scss/helpers/_vr.scss +8 -0
- data/_sass/bootstrap/scss/mixins/_alert.scss +15 -0
- data/_sass/bootstrap/scss/mixins/_backdrop.scss +14 -0
- data/_sass/bootstrap/scss/mixins/_background-variant.scss +23 -0
- data/_sass/bootstrap/scss/mixins/_badge.scss +17 -0
- data/_sass/bootstrap/scss/mixins/_border-radius.scss +78 -0
- data/_sass/bootstrap/scss/mixins/_box-shadow.scss +18 -0
- data/_sass/bootstrap/scss/mixins/_breakpoints.scss +127 -0
- data/_sass/bootstrap/scss/mixins/_buttons.scss +71 -0
- data/_sass/bootstrap/scss/mixins/_caret.scss +64 -0
- data/_sass/bootstrap/scss/mixins/_clearfix.scss +9 -0
- data/_sass/bootstrap/scss/mixins/_color-scheme.scss +7 -0
- data/_sass/bootstrap/scss/mixins/_container.scss +11 -0
- data/_sass/bootstrap/scss/mixins/_deprecate.scss +10 -0
- data/_sass/bootstrap/scss/mixins/_float.scss +14 -0
- data/_sass/bootstrap/scss/mixins/_forms.scss +152 -0
- data/_sass/bootstrap/scss/mixins/_gradients.scss +47 -0
- data/_sass/bootstrap/scss/mixins/_grid-framework.scss +80 -0
- data/_sass/bootstrap/scss/mixins/_grid.scss +151 -0
- data/_sass/bootstrap/scss/mixins/_hover.scss +37 -0
- data/_sass/bootstrap/scss/mixins/_image.scss +16 -0
- data/_sass/bootstrap/scss/mixins/_list-group.scss +24 -0
- data/_sass/bootstrap/scss/mixins/_lists.scss +7 -0
- data/_sass/bootstrap/scss/mixins/_nav-divider.scss +11 -0
- data/_sass/bootstrap/scss/mixins/_pagination.scss +10 -0
- data/_sass/bootstrap/scss/mixins/_reset-text.scss +17 -0
- data/_sass/bootstrap/scss/mixins/_resize.scss +6 -0
- data/_sass/bootstrap/scss/mixins/_screen-reader.scss +34 -0
- data/_sass/bootstrap/scss/mixins/_size.scss +7 -0
- data/_sass/bootstrap/scss/mixins/_table-row.scss +39 -0
- data/_sass/bootstrap/scss/mixins/_table-variants.scss +24 -0
- data/_sass/bootstrap/scss/mixins/_text-emphasis.scss +17 -0
- data/_sass/bootstrap/scss/mixins/_text-hide.scss +11 -0
- data/_sass/bootstrap/scss/mixins/_text-truncate.scss +8 -0
- data/_sass/bootstrap/scss/mixins/_transition.scss +26 -0
- data/_sass/bootstrap/scss/mixins/_utilities.scss +92 -0
- data/_sass/bootstrap/scss/mixins/_visibility.scss +8 -0
- data/_sass/bootstrap/scss/mixins/_visually-hidden.scss +29 -0
- data/_sass/bootstrap/scss/utilities/_align.scss +8 -0
- data/_sass/bootstrap/scss/utilities/_api.scss +47 -0
- data/_sass/bootstrap/scss/utilities/_background.scss +19 -0
- data/_sass/bootstrap/scss/utilities/_borders.scss +75 -0
- data/_sass/bootstrap/scss/utilities/_clearfix.scss +3 -0
- data/_sass/bootstrap/scss/utilities/_display.scss +26 -0
- data/_sass/bootstrap/scss/utilities/_embed.scss +39 -0
- data/_sass/bootstrap/scss/utilities/_flex.scss +51 -0
- data/_sass/bootstrap/scss/utilities/_float.scss +11 -0
- data/_sass/bootstrap/scss/utilities/_interactions.scss +5 -0
- data/_sass/bootstrap/scss/utilities/_overflow.scss +5 -0
- data/_sass/bootstrap/scss/utilities/_position.scss +32 -0
- data/_sass/bootstrap/scss/utilities/_screenreaders.scss +11 -0
- data/_sass/bootstrap/scss/utilities/_shadows.scss +6 -0
- data/_sass/bootstrap/scss/utilities/_sizing.scss +20 -0
- data/_sass/bootstrap/scss/utilities/_spacing.scss +73 -0
- data/_sass/bootstrap/scss/utilities/_stretched-link.scss +19 -0
- data/_sass/bootstrap/scss/utilities/_text.scss +72 -0
- data/_sass/bootstrap/scss/utilities/_visibility.scss +13 -0
- data/_sass/bootstrap/scss/vendor/_rfs.scss +354 -0
- data/_sass/jekyll-theme-cs50.scss +298 -0
- data/_sass/page.scss +228 -0
- data/_sass/rouge.scss +35 -0
- data/assets/@fortawesome/fontawesome-free/css/all.min.css +5 -0
- data/assets/@fortawesome/fontawesome-free/webfonts/fa-brands-400.eot +0 -0
- data/assets/@fortawesome/fontawesome-free/webfonts/fa-brands-400.svg +3717 -0
- data/assets/@fortawesome/fontawesome-free/webfonts/fa-brands-400.ttf +0 -0
- data/assets/@fortawesome/fontawesome-free/webfonts/fa-brands-400.woff +0 -0
- data/assets/@fortawesome/fontawesome-free/webfonts/fa-brands-400.woff2 +0 -0
- data/assets/@fortawesome/fontawesome-free/webfonts/fa-regular-400.eot +0 -0
- data/assets/@fortawesome/fontawesome-free/webfonts/fa-regular-400.svg +801 -0
- data/assets/@fortawesome/fontawesome-free/webfonts/fa-regular-400.ttf +0 -0
- data/assets/@fortawesome/fontawesome-free/webfonts/fa-regular-400.woff +0 -0
- data/assets/@fortawesome/fontawesome-free/webfonts/fa-regular-400.woff2 +0 -0
- data/assets/@fortawesome/fontawesome-free/webfonts/fa-solid-900.eot +0 -0
- data/assets/@fortawesome/fontawesome-free/webfonts/fa-solid-900.svg +5034 -0
- data/assets/@fortawesome/fontawesome-free/webfonts/fa-solid-900.ttf +0 -0
- data/assets/@fortawesome/fontawesome-free/webfonts/fa-solid-900.woff +0 -0
- data/assets/@fortawesome/fontawesome-free/webfonts/fa-solid-900.woff2 +0 -0
- data/assets/algolia-min.css +1 -0
- data/assets/algoliasearch/dist/algoliasearch-lite.umd.js +2 -0
- data/assets/algoliasearchLite.min.js +3 -0
- data/assets/bootstrap/LICENSE +22 -0
- data/assets/bootstrap/dist/js/bootstrap.bundle.min.js +7 -0
- data/assets/bootstrap/dist/js/bootstrap.bundle.min.js.map +1 -0
- data/assets/bootstrap/dist/js/bootstrap.min.js +7 -0
- data/assets/bootstrap-table/LICENSE +21 -0
- data/assets/bootstrap-table/dist/bootstrap-table.min.css +10 -0
- data/assets/bootstrap-table/dist/bootstrap-table.min.js +10 -0
- data/assets/bootstrap-table/dist/extensions/mobile/bootstrap-table-mobile.min.js +10 -0
- data/assets/instantsearch.js/LICENSE +21 -0
- data/assets/instantsearch.js/dist/instantsearch.production.min.js +3 -0
- data/assets/instantsearch.js/dist/instantsearch.production.min.js.map +1 -0
- data/assets/instantsearch.production.min.js +3 -0
- data/assets/jekyll-theme-cs50.js +620 -0
- data/assets/jquery/LICENSE.txt +20 -0
- data/assets/jquery/dist/jquery.min.js +2 -0
- data/assets/luxon.min.js +1 -0
- data/assets/moment-timezone-with-data.min.js +1 -0
- data/assets/moment.min.js +1 -0
- data/assets/page.js +4 -0
- data/assets/page.scss +4 -0
- data/assets/popper.js/dist/umd/popper.min.js +5 -0
- data/assets/reset-min.css +1 -0
- data/assets/scratchblocks.min.js +11 -0
- data/assets/scratchblocks.min.js.map +1 -0
- data/lib/jekyll-theme-cs50/constants.rb +59 -0
- data/lib/jekyll-theme-cs50.rb +606 -0
- data/lib/liquid-tag-parser.rb +1 -0
- metadata +350 -0
@@ -0,0 +1,620 @@
|
|
1
|
+
// Infer baseurl from this file's (known) path
|
2
|
+
const a = document.createElement('a');
|
3
|
+
a.href = document.currentScript.src;
|
4
|
+
const matches = a.pathname.match(/^(.+)\/assets\/jekyll-theme-cs50\.js$/);
|
5
|
+
if (matches) {
|
6
|
+
window.baseurl = matches[1];
|
7
|
+
}
|
8
|
+
|
9
|
+
// https://github.com/typekit/webfontloader#get-started
|
10
|
+
WebFont.load({
|
11
|
+
google: {
|
12
|
+
families: ['PT Sans', 'PT Sans:bold', 'PT Sans:ital']
|
13
|
+
}
|
14
|
+
});
|
15
|
+
|
16
|
+
// On DOMContentLoaded
|
17
|
+
$(document).on('DOMContentLoaded', function() {
|
18
|
+
|
19
|
+
// Current timestamp
|
20
|
+
const now = luxon.DateTime.local();
|
21
|
+
|
22
|
+
// data-after, data-before
|
23
|
+
$('[data-after], [data-before]').each(function(index, element) {
|
24
|
+
|
25
|
+
// Return true iff element should be removed
|
26
|
+
const remove = function() {
|
27
|
+
if (data = $(element).attr('data-before')) {
|
28
|
+
return !(now < luxon.DateTime.fromISO($(element).attr('data-before')));
|
29
|
+
}
|
30
|
+
else if (data = $(element).attr('data-after')) {
|
31
|
+
return !(now > luxon.DateTime.fromISO($(element).attr('data-after')));
|
32
|
+
}
|
33
|
+
};
|
34
|
+
|
35
|
+
// Remember element's siblings
|
36
|
+
const $prev = $(element).prev(), $next = $(element).next();
|
37
|
+
|
38
|
+
// Siblings to be merged
|
39
|
+
const SIBLINGS = ['DL', 'OL', 'UL'];
|
40
|
+
|
41
|
+
// If element should be removed
|
42
|
+
if (remove()) {
|
43
|
+
|
44
|
+
// Remove element
|
45
|
+
$(element).remove();
|
46
|
+
|
47
|
+
// Merge siblings
|
48
|
+
if (SIBLINGS.includes($prev.prop('tagName')) && $prev.prop('tagName') === $next.prop('tagName')) {
|
49
|
+
$prev.append($next.children());
|
50
|
+
$next.remove();
|
51
|
+
}
|
52
|
+
}
|
53
|
+
else {
|
54
|
+
|
55
|
+
// Unwrap element
|
56
|
+
const $children = $(element).children().unwrap();
|
57
|
+
|
58
|
+
// If element had one child
|
59
|
+
if ($children.length === 1) {
|
60
|
+
|
61
|
+
// Merge siblings
|
62
|
+
if (SIBLINGS.includes($children.prop('tagName'))) {
|
63
|
+
if ($prev.prop('tagName') === $children.prop('tagName')) {
|
64
|
+
$children.prepend($prev.children());
|
65
|
+
$prev.remove();
|
66
|
+
}
|
67
|
+
if ($children.prop('tagName') == $next.prop('tagName')) {
|
68
|
+
$children.append($next.children());
|
69
|
+
$next.remove();
|
70
|
+
}
|
71
|
+
}
|
72
|
+
}
|
73
|
+
}
|
74
|
+
});
|
75
|
+
|
76
|
+
// Remembers that alert with hash has been dismissed by storing hash in localStorage
|
77
|
+
const dismiss = function(hash) {
|
78
|
+
let alert;
|
79
|
+
try {
|
80
|
+
alert = JSON.parse(localStorage.getItem('alert'));
|
81
|
+
if (!Array.isArray(alert)) {
|
82
|
+
throw new Error();
|
83
|
+
}
|
84
|
+
}
|
85
|
+
catch (err) {
|
86
|
+
alert = [];
|
87
|
+
}
|
88
|
+
if (!alert.includes(hash)) {
|
89
|
+
alert.push(hash);
|
90
|
+
}
|
91
|
+
localStorage.setItem('alert', JSON.stringify(alert));
|
92
|
+
};
|
93
|
+
|
94
|
+
// Returns true iff alert with hash has already been dismissed
|
95
|
+
const dismissed = function(hash) {
|
96
|
+
try {
|
97
|
+
const alert = JSON.parse(localStorage.getItem('alert'));
|
98
|
+
return Array.isArray(alert) && alert.includes(hash);
|
99
|
+
}
|
100
|
+
catch (err) {
|
101
|
+
return false;
|
102
|
+
}
|
103
|
+
};
|
104
|
+
|
105
|
+
// Listen for dismissal of fixed-top alert
|
106
|
+
$('#alert').on('closed.bs.alert', function() {
|
107
|
+
|
108
|
+
// Resize UI
|
109
|
+
$(window).trigger('resize');
|
110
|
+
|
111
|
+
// Remember that alert has been dismissed
|
112
|
+
dismiss($(this).attr('data-hash'));
|
113
|
+
});
|
114
|
+
|
115
|
+
// Remove fixed-top alert if already dismissed
|
116
|
+
if (dismissed($('#alert').attr('data-hash'))) {
|
117
|
+
$('#alert').remove();
|
118
|
+
}
|
119
|
+
|
120
|
+
// Listen for details in fixed-top alert
|
121
|
+
$('#alert details').on('toggle', function() {
|
122
|
+
|
123
|
+
// Resize UI
|
124
|
+
$(window).trigger('resize');
|
125
|
+
});
|
126
|
+
|
127
|
+
// data-alert
|
128
|
+
$('[data-alert]').each(function(index, element) {
|
129
|
+
|
130
|
+
// Split data-alert on whitespace
|
131
|
+
for (let alert of $(element).attr('data-alert').split(/ +/)) {
|
132
|
+
|
133
|
+
// If valid class
|
134
|
+
if (['primary', 'secondary', 'success', 'danger', 'warning', 'info', 'light', 'dark', 'dismissible'].includes(alert)) {
|
135
|
+
|
136
|
+
// Add it to element
|
137
|
+
$(element).addClass('alert-' + alert);
|
138
|
+
}
|
139
|
+
}
|
140
|
+
|
141
|
+
// If dismissible, reveal button
|
142
|
+
if ($(element).hasClass('alert-dismissible')) {
|
143
|
+
$(element).children('[data-bs-dismiss]').removeClass('d-none');
|
144
|
+
}
|
145
|
+
|
146
|
+
// Add .alert-link to links
|
147
|
+
$(element).find('a').addClass('alert-link');
|
148
|
+
|
149
|
+
// Add .alert-heading to headings
|
150
|
+
$(element).find('h1, h2, h3, h4, h5, h6').each(function(index, element) {
|
151
|
+
const tagName = $(element).prop('tagName');
|
152
|
+
$(element).replaceWith(function() {
|
153
|
+
return $('<p>').append($(this).contents()).addClass(tagName.toLowerCase()).addClass('alert-heading');
|
154
|
+
});
|
155
|
+
});
|
156
|
+
});
|
157
|
+
|
158
|
+
// data-calendar
|
159
|
+
$('[data-calendar]').each(function(index, element) {
|
160
|
+
|
161
|
+
// Display calendar in user's time zone
|
162
|
+
// https://stackoverflow.com/a/32511510/5156190
|
163
|
+
if ($(element).attr('data-calendar')) {
|
164
|
+
let src = $(element).attr('data-calendar');
|
165
|
+
src += '&ctz=' + luxon.DateTime.local().zoneName;
|
166
|
+
$(element).attr('src', src);
|
167
|
+
}
|
168
|
+
});
|
169
|
+
|
170
|
+
// data-local
|
171
|
+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#Syntax
|
172
|
+
// https://english.stackexchange.com/a/100754
|
173
|
+
$('[data-local]').each(function(index, element) {
|
174
|
+
|
175
|
+
// HTML to display
|
176
|
+
let html;
|
177
|
+
|
178
|
+
// Parse attribute
|
179
|
+
const local = $(element).attr('data-local');
|
180
|
+
const locals = local.split('/');
|
181
|
+
|
182
|
+
// If range
|
183
|
+
if (locals.length == 2) {
|
184
|
+
|
185
|
+
// Parse start
|
186
|
+
const start = luxon.DateTime.fromISO(locals[0]).setLocale(CS50.locale);
|
187
|
+
|
188
|
+
// Parse end
|
189
|
+
const end = luxon.DateTime.fromISO(locals[1]).setLocale(CS50.locale);
|
190
|
+
|
191
|
+
// Options for formatting start
|
192
|
+
const opts = {
|
193
|
+
day: CS50.local.day,
|
194
|
+
hour: CS50.local.hour,
|
195
|
+
minute: CS50.local.minute,
|
196
|
+
month: CS50.local.month,
|
197
|
+
weekday: CS50.local.weekday,
|
198
|
+
year: CS50.local.year
|
199
|
+
};
|
200
|
+
|
201
|
+
// If start and end on different dates or if clocks change between start and end
|
202
|
+
if (start.toLocaleString(luxon.DateTime.DATE_SHORT) !== end.toLocaleString(luxon.DateTime.DATE_SHORT) ||
|
203
|
+
start.toLocal().offsetNameLong !== end.toLocal().offsetNameLong) {
|
204
|
+
|
205
|
+
// Add time zone to start
|
206
|
+
opts.timeZoneName = CS50.local.timeZoneName;
|
207
|
+
}
|
208
|
+
|
209
|
+
// If start and end on same date (and English locale), or if end on midnight of start
|
210
|
+
if (CS50.locale === 'en' && (
|
211
|
+
start.toLocaleString(luxon.DateTime.DATE_SHORT) === end.toLocaleString(luxon.DateTime.DATE_SHORT) ||
|
212
|
+
end.toLocaleString(luxon.DateTime.TIME_24_WITH_SECONDS) === '24:00:00' &&
|
213
|
+
start.toLocaleString(luxon.DateTime.DATE_SHORT) == end.minus({days: 1}).toLocaleString(luxon.DateTime.DATE_SHORT))) {
|
214
|
+
|
215
|
+
// Format end without date
|
216
|
+
html = start.toLocaleString(opts) + ' – ' + end.toLocaleString({
|
217
|
+
hour: CS50.local.hour,
|
218
|
+
minute: CS50.local.minute,
|
219
|
+
timeZoneName: CS50.local.timeZoneName
|
220
|
+
});
|
221
|
+
}
|
222
|
+
|
223
|
+
// If start and end on different dates
|
224
|
+
else {
|
225
|
+
|
226
|
+
// Format end without date
|
227
|
+
html = start.toLocaleString(opts) + ' – ' + end.toLocaleString({
|
228
|
+
day: CS50.local.day,
|
229
|
+
hour: CS50.local.hour,
|
230
|
+
minute: CS50.local.minute,
|
231
|
+
month: CS50.local.month,
|
232
|
+
timeZoneName: CS50.local.timeZoneName,
|
233
|
+
weekday: CS50.local.weekday,
|
234
|
+
year: CS50.local.year
|
235
|
+
});
|
236
|
+
}
|
237
|
+
}
|
238
|
+
else {
|
239
|
+
|
240
|
+
// Parse start
|
241
|
+
const start = luxon.DateTime.fromISO(locals[0]).setLocale(CS50.locale);
|
242
|
+
|
243
|
+
// Format start
|
244
|
+
html = start.toLocaleString({
|
245
|
+
day: CS50.local.day,
|
246
|
+
hour: CS50.local.hour,
|
247
|
+
minute: CS50.local.minute,
|
248
|
+
month: CS50.local.month,
|
249
|
+
timeZoneName: CS50.local.timeZoneName,
|
250
|
+
weekday: CS50.local.weekday,
|
251
|
+
year: CS50.local.year
|
252
|
+
});
|
253
|
+
}
|
254
|
+
|
255
|
+
// Display HTML
|
256
|
+
$(this).html(html);
|
257
|
+
|
258
|
+
// Inject clock after date-local (and, if date-local is inside a link, outside that link)
|
259
|
+
const $clock = $('<a data-clock href="https://time.cs50.io/' + local + '"><i class="far fa-clock" title="CS50 Time Converter"></i></a>');
|
260
|
+
const $closest = $(this).closest('a');
|
261
|
+
if ($closest.length) {
|
262
|
+
$closest.after($clock);
|
263
|
+
}
|
264
|
+
else {
|
265
|
+
$(this).after($clock);
|
266
|
+
}
|
267
|
+
});
|
268
|
+
|
269
|
+
// Toggle clocks on hover
|
270
|
+
const enableClocks = function(element) {
|
271
|
+
$('[data-clock]').each(function(index, element) {
|
272
|
+
$(element).on('mouseover', function() {
|
273
|
+
$(element).find('.fa-clock').removeClass('far').addClass('fas');
|
274
|
+
});
|
275
|
+
$(element).on('click mouseout', function() {
|
276
|
+
$(element).find('.fa-clock').removeClass('fas').addClass('far');
|
277
|
+
});
|
278
|
+
});
|
279
|
+
};
|
280
|
+
enableClocks();
|
281
|
+
|
282
|
+
// Enable tooltips
|
283
|
+
const enableTooltips = function() {
|
284
|
+
$('[data-bs-toggle="tooltip"]').each(function(index, element) {
|
285
|
+
new bootstrap.Tooltip(element);
|
286
|
+
});
|
287
|
+
};
|
288
|
+
enableTooltips();
|
289
|
+
|
290
|
+
// Re-attach tooltips after tables have responded
|
291
|
+
// https://github.com/wenzhixin/bootstrap-table/issues/572#issuecomment-76503607
|
292
|
+
$('table').on('post-body.bs.table', function() {
|
293
|
+
enableClocks();
|
294
|
+
enableTooltips();
|
295
|
+
});
|
296
|
+
|
297
|
+
// Ensure tables are responsive
|
298
|
+
// https://bootstrap-table.com/docs/extensions/mobile/
|
299
|
+
$('table').each(function(index, element) {
|
300
|
+
|
301
|
+
// Workaround for https://github.com/wenzhixin/bootstrap-table/issues/5470
|
302
|
+
$(element).find('thead td, thead th').each(function(index, element) {
|
303
|
+
if ($(element).css('text-align')) {
|
304
|
+
$(element).wrapInner('<div style="text-align: ' + $(element).css('text-align') + '">');
|
305
|
+
}
|
306
|
+
});
|
307
|
+
|
308
|
+
// Enable bootstrap-table
|
309
|
+
try {
|
310
|
+
$(element).bootstrapTable({
|
311
|
+
classes: 'table table-bordered table-striped',
|
312
|
+
minWidth: 992, // https://getbootstrap.com/docs/5.0/layout/breakpoints/#available-breakpoints
|
313
|
+
mobileResponsive: true,
|
314
|
+
onPostBody: function(data) {
|
315
|
+
|
316
|
+
// Left-align cards on mobile
|
317
|
+
$(element).find('.card-view-title, .card-view-title > *, .card-view-value').css('text-align', 'left');
|
318
|
+
}
|
319
|
+
});
|
320
|
+
}
|
321
|
+
catch(err) {} // In case no thead
|
322
|
+
});
|
323
|
+
|
324
|
+
// data-marker
|
325
|
+
$('[data-marker]').each(function(index, element) {
|
326
|
+
|
327
|
+
// Add .fa-ul to parent ul
|
328
|
+
$(element).parent().addClass('fa-ul');
|
329
|
+
|
330
|
+
// Filter
|
331
|
+
const filter = function() {
|
332
|
+
return !$(this).is('ol, ul');
|
333
|
+
};
|
334
|
+
|
335
|
+
// Listener
|
336
|
+
const click = function(eventObject) {
|
337
|
+
|
338
|
+
// If it wasn't a descendent link that was clicked
|
339
|
+
if (!$(eventObject.target).is('a')) {
|
340
|
+
|
341
|
+
// Don't propgate to nested lists
|
342
|
+
eventObject.stopPropagation();
|
343
|
+
|
344
|
+
// Toggle marker
|
345
|
+
const marker = $(element).attr('data-marker');
|
346
|
+
if (marker === '+') {
|
347
|
+
$(element).attr('data-marker', '-');
|
348
|
+
$(element).find('> .fa-li > .fa-plus-square').removeClass('fa-plus-square').addClass('fa-minus-square');
|
349
|
+
}
|
350
|
+
else if (marker === '-') {
|
351
|
+
$(element).attr('data-marker', '+');
|
352
|
+
$(element).find('> .fa-li > .fa-minus-square').removeClass('fa-minus-square').addClass('fa-plus-square');
|
353
|
+
}
|
354
|
+
$(window).trigger('resize');
|
355
|
+
}
|
356
|
+
};
|
357
|
+
|
358
|
+
// Icons
|
359
|
+
const plus = $('<span class="fa-li"><i class="far fa-plus-square"></i></span>').click(click);
|
360
|
+
const minus = $('<span class="fa-li"><i class="far fa-minus-square"></i></span>').click(click);
|
361
|
+
const square = $('<span class="fa-li"><i class="fas fa-square"></i></span>');
|
362
|
+
|
363
|
+
// Wrapper
|
364
|
+
const $span = $('<span>').click(click);
|
365
|
+
|
366
|
+
// If +
|
367
|
+
if ($(element).attr('data-marker') === '+') {
|
368
|
+
$(element).contents().filter(filter).wrap($span);
|
369
|
+
$(element).prepend(plus);
|
370
|
+
}
|
371
|
+
|
372
|
+
// If -
|
373
|
+
else if ($(element).attr('data-marker') === '-') {
|
374
|
+
$(element).contents().filter(filter).wrap($span);
|
375
|
+
$(element).prepend(minus);
|
376
|
+
}
|
377
|
+
|
378
|
+
// If *
|
379
|
+
else if ($(element).attr('data-marker') === '*') {
|
380
|
+
$(element).prepend(square);
|
381
|
+
}
|
382
|
+
});
|
383
|
+
|
384
|
+
// Also add .fa-ul to TOC, if any, for consistency
|
385
|
+
$('.markdown-toc').addClass('fa-ul');
|
386
|
+
|
387
|
+
// Get next slice of elements
|
388
|
+
function next(element) {
|
389
|
+
|
390
|
+
// Next siblings
|
391
|
+
const siblings = element.nextAll();
|
392
|
+
|
393
|
+
// First sibling after this element
|
394
|
+
const start = siblings.index(element) + 1;
|
395
|
+
|
396
|
+
// Following buttons
|
397
|
+
const buttons = siblings.slice(start).find('[data-next]');
|
398
|
+
|
399
|
+
// Last sibling before next button
|
400
|
+
let end = (buttons.length > 0) ? siblings.index(buttons[0]) : siblings.length;
|
401
|
+
|
402
|
+
// Next slice
|
403
|
+
return siblings.slice(start, end);
|
404
|
+
}
|
405
|
+
|
406
|
+
// data-next
|
407
|
+
$('[data-next]').each(function(index, element) {
|
408
|
+
|
409
|
+
// Hide next elements
|
410
|
+
next($(this).parent()).addClass('next');
|
411
|
+
|
412
|
+
// Listen for clicks
|
413
|
+
$(this).click(function() {
|
414
|
+
|
415
|
+
// Show next elements
|
416
|
+
next($(this).parent()).removeClass('next');
|
417
|
+
|
418
|
+
// Update margin
|
419
|
+
$(window).trigger('resize');
|
420
|
+
|
421
|
+
// Remember p-wrapped button's offset
|
422
|
+
let top = $(this).parent().offset().top;
|
423
|
+
let bottom = top + $(this).parent().outerHeight(true);
|
424
|
+
|
425
|
+
// Scroll to next elements
|
426
|
+
$('html, body').animate({scrollTop: bottom + 1}, 500);
|
427
|
+
|
428
|
+
// Disable button
|
429
|
+
$(this).prop('disabled', true);
|
430
|
+
});
|
431
|
+
});
|
432
|
+
|
433
|
+
// Ensure iframes responsive in Safari on iOS (for, e.g., Google Calendars), per https://stackoverflow.com/a/23083463/5156190
|
434
|
+
$('iframe').each(function(index, element) {
|
435
|
+
if (!$(this).attr('scrolling')) {
|
436
|
+
$(this).attr('scrolling', 'no');
|
437
|
+
}
|
438
|
+
});
|
439
|
+
|
440
|
+
// Render Mermaid charts
|
441
|
+
$('code[class="language-mermaid"]').each(function(index, element) {
|
442
|
+
|
443
|
+
// Replace pre > code with div
|
444
|
+
const $element = $(element);
|
445
|
+
const $div = $('<div class="mermaid">').text($element.text());
|
446
|
+
$element.parent().replaceWith($div);
|
447
|
+
|
448
|
+
// Render chart
|
449
|
+
mermaid.init({}, $div.get(0));
|
450
|
+
|
451
|
+
// Left-align Mermaid, until https://github.com/mermaid-js/mermaid/issues/1983
|
452
|
+
// https://stackoverflow.com/a/6322799/5156190
|
453
|
+
$div.children('svg').attr('preserveAspectRatio', 'xMinYMin meet');
|
454
|
+
});
|
455
|
+
|
456
|
+
// Render Scratch blocks
|
457
|
+
scratchblocks.renderMatching('pre code.language-scratch', {
|
458
|
+
style: 'scratch3'
|
459
|
+
});
|
460
|
+
|
461
|
+
// Remove PRE wrapper, since not actually preformatted text
|
462
|
+
$('pre code.language-scratch').each(function(index, element) {
|
463
|
+
$(element).parent().replaceWith($(element).children());
|
464
|
+
});
|
465
|
+
|
466
|
+
// https://github.com/scratchblocks/scratchblocks/pull/301#issuecomment-829428605
|
467
|
+
$('.scratchblocks svg').each(function(index, element) {
|
468
|
+
const bbox = element.getBBox();
|
469
|
+
const height = Math.floor(bbox.height) + 2; // Prevents clipping of bottom of some blocks
|
470
|
+
$(element).attr('width', bbox.width);
|
471
|
+
$(element).attr('height', height);
|
472
|
+
$(element).parent().css('height', Math.ceil(element.getBoundingClientRect().height) + 'px');
|
473
|
+
});
|
474
|
+
|
475
|
+
// If H1 is immediately followed H2 (and no other H2 siblings),
|
476
|
+
// treat H1 as title and H2 as subtitle, relocating in DOM as customized heading
|
477
|
+
// https://getbootstrap.com/docs/4.6/content/typography/#customizing-headings
|
478
|
+
const $title = $('main h1').first();
|
479
|
+
const $subtitle = $title.next('h2');
|
480
|
+
if ($title.length && $subtitle.length && !$subtitle.nextAll('h2').length) {
|
481
|
+
$title.append(' ').append($('<small>').addClass('text-muted').append($subtitle.detach().contents()));
|
482
|
+
}
|
483
|
+
|
484
|
+
// Get headings
|
485
|
+
const headings = $([
|
486
|
+
'main h1',
|
487
|
+
'main h2',
|
488
|
+
'main h3',
|
489
|
+
'main h4',
|
490
|
+
'main h5',
|
491
|
+
'main h6'].join(','));
|
492
|
+
headings.each(function(index, element) {
|
493
|
+
|
494
|
+
// If it has an id
|
495
|
+
if ($(element).attr('id')) {
|
496
|
+
|
497
|
+
// Link heading's children to heading (unless already linked)
|
498
|
+
$(element).contents().each(function(index, node) {
|
499
|
+
if (!$(node).is('a')) {
|
500
|
+
$(node).wrapAll($('<a data-id href="#' + $(element).attr('id') + '"></a>'));
|
501
|
+
}
|
502
|
+
});
|
503
|
+
|
504
|
+
// Relocate id to an anchor (so that it can be invisibly positioned below any alert)
|
505
|
+
// https://stackoverflow.com/a/13184714
|
506
|
+
$(element).before($('<a data-id id="' + $(element).attr('id') + '"></a>'))
|
507
|
+
$(element).removeAttr('id');
|
508
|
+
}
|
509
|
+
});
|
510
|
+
|
511
|
+
// Listen for hashchange
|
512
|
+
$(window).on('hashchange', function() {
|
513
|
+
|
514
|
+
// Find heading
|
515
|
+
let heading;
|
516
|
+
try {
|
517
|
+
heading = $(window.location.hash); // In case syntactically invalid ID
|
518
|
+
}
|
519
|
+
catch (err) {
|
520
|
+
return false;
|
521
|
+
}
|
522
|
+
if (!heading.length) {
|
523
|
+
return false;
|
524
|
+
}
|
525
|
+
|
526
|
+
// Previous siblings
|
527
|
+
heading.prevAll().removeClass('next').find('[data-next]').prop('disabled', true);
|
528
|
+
|
529
|
+
// This heading
|
530
|
+
heading.removeClass('next');
|
531
|
+
|
532
|
+
// Next siblings
|
533
|
+
next(heading).removeClass('next');
|
534
|
+
});
|
535
|
+
$(window).trigger('hashchange');
|
536
|
+
|
537
|
+
// Listen for resize
|
538
|
+
$(window).on('resize', function() {
|
539
|
+
|
540
|
+
// Return true iff small device (on which aside will be above main)
|
541
|
+
const mobile = function() {
|
542
|
+
return $('aside').position().top < $('main').position().top;
|
543
|
+
};
|
544
|
+
|
545
|
+
// Get headings
|
546
|
+
const headings = $([
|
547
|
+
'main h1:not(.next)',
|
548
|
+
'main h2:not(.next)',
|
549
|
+
'main h3:not(.next)',
|
550
|
+
'main h4:not(.next)',
|
551
|
+
'main h5:not(.next)',
|
552
|
+
'main h6:not(.next)'].join(','));
|
553
|
+
|
554
|
+
// Ensure last heading, if any, can be anchored atop page
|
555
|
+
if (headings.last().offset()) {
|
556
|
+
var top = headings.last().offset().top;
|
557
|
+
}
|
558
|
+
else {
|
559
|
+
var top = 0;
|
560
|
+
}
|
561
|
+
|
562
|
+
// On small devices
|
563
|
+
if (mobile()) {
|
564
|
+
var margin = $(window).height() - ($('main').outerHeight() + $('aside').outerHeight() - top);
|
565
|
+
}
|
566
|
+
|
567
|
+
// On large devices
|
568
|
+
else {
|
569
|
+
var margin = $(window).height() - ($('main').outerHeight() - top);
|
570
|
+
}
|
571
|
+
|
572
|
+
// Update margin
|
573
|
+
$('main').css('margin-bottom', Math.max(0, Math.ceil(margin)) + 'px');
|
574
|
+
|
575
|
+
// Resize search UI
|
576
|
+
if (mobile()) {
|
577
|
+
|
578
|
+
// Shrink
|
579
|
+
$('#search .form-control').removeClass('form-control-lg');
|
580
|
+
$('#search .btn').removeClass('btn-lg');
|
581
|
+
}
|
582
|
+
else {
|
583
|
+
|
584
|
+
// Grow
|
585
|
+
$('#search .form-control').addClass('form-control-lg');
|
586
|
+
$('#search .btn').addClass('btn-lg');
|
587
|
+
}
|
588
|
+
|
589
|
+
// Calculate height of alert, if any
|
590
|
+
const height = $('#alert').outerHeight(true) || 0;
|
591
|
+
|
592
|
+
// Position aside
|
593
|
+
if (mobile()) {
|
594
|
+
$('aside').css('height', '');
|
595
|
+
$('aside').css('margin-top', height + 'px');
|
596
|
+
$('aside').css('top', '');
|
597
|
+
$('main').css('margin-top', '');
|
598
|
+
}
|
599
|
+
else {
|
600
|
+
$('aside').css('height', ($(window).height() - height) + 'px');
|
601
|
+
$('aside').css('margin-top', '');
|
602
|
+
$('aside').css('top', height + 'px');
|
603
|
+
$('main').css('margin-top', height + 'px');
|
604
|
+
}
|
605
|
+
|
606
|
+
// Position headings' anchors below alert
|
607
|
+
// https://stackoverflow.com/a/13184714
|
608
|
+
$('a[data-id][id]').css('top', '-' + height + 'px');
|
609
|
+
|
610
|
+
});
|
611
|
+
$(window).trigger('resize');
|
612
|
+
|
613
|
+
// Resize iframes dynamically
|
614
|
+
$('iframe').on('load', function() {
|
615
|
+
$(this).iFrameResize();
|
616
|
+
});
|
617
|
+
|
618
|
+
// Reveal page
|
619
|
+
$('body').removeClass('invisible');
|
620
|
+
});
|
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright OpenJS Foundation and other contributors, https://openjsf.org/
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|