jekyll-theme-apcsp 1.1.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 +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.
|