jekyll-theme-apcsp 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (183) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/_includes/alert.md +0 -0
  4. data/_includes/footer.md +0 -0
  5. data/_includes/head.html +0 -0
  6. data/_includes/header.md +1 -0
  7. data/_includes/nav.md +0 -0
  8. data/_layouts/page.html +308 -0
  9. data/_layouts/redirect.html +12 -0
  10. data/_sass/bootstrap/LICENSE +22 -0
  11. data/_sass/bootstrap/scss/_accordion.scss +146 -0
  12. data/_sass/bootstrap/scss/_alert.scss +71 -0
  13. data/_sass/bootstrap/scss/_badge.scss +40 -0
  14. data/_sass/bootstrap/scss/_breadcrumb.scss +42 -0
  15. data/_sass/bootstrap/scss/_button-group.scss +142 -0
  16. data/_sass/bootstrap/scss/_buttons.scss +158 -0
  17. data/_sass/bootstrap/scss/_card.scss +234 -0
  18. data/_sass/bootstrap/scss/_carousel.scss +229 -0
  19. data/_sass/bootstrap/scss/_close.scss +40 -0
  20. data/_sass/bootstrap/scss/_code.scss +48 -0
  21. data/_sass/bootstrap/scss/_containers.scss +41 -0
  22. data/_sass/bootstrap/scss/_custom-forms.scss +526 -0
  23. data/_sass/bootstrap/scss/_dropdown.scss +248 -0
  24. data/_sass/bootstrap/scss/_forms.scss +9 -0
  25. data/_sass/bootstrap/scss/_functions.scss +302 -0
  26. data/_sass/bootstrap/scss/_grid.scss +33 -0
  27. data/_sass/bootstrap/scss/_helpers.scss +10 -0
  28. data/_sass/bootstrap/scss/_images.scss +42 -0
  29. data/_sass/bootstrap/scss/_input-group.scss +208 -0
  30. data/_sass/bootstrap/scss/_jumbotron.scss +17 -0
  31. data/_sass/bootstrap/scss/_list-group.scss +191 -0
  32. data/_sass/bootstrap/scss/_maps.scss +54 -0
  33. data/_sass/bootstrap/scss/_media.scss +8 -0
  34. data/_sass/bootstrap/scss/_mixins.scss +43 -0
  35. data/_sass/bootstrap/scss/_modal.scss +237 -0
  36. data/_sass/bootstrap/scss/_nav.scss +172 -0
  37. data/_sass/bootstrap/scss/_navbar.scss +274 -0
  38. data/_sass/bootstrap/scss/_offcanvas.scss +143 -0
  39. data/_sass/bootstrap/scss/_pagination.scss +111 -0
  40. data/_sass/bootstrap/scss/_placeholders.scss +51 -0
  41. data/_sass/bootstrap/scss/_popover.scss +198 -0
  42. data/_sass/bootstrap/scss/_print.scss +141 -0
  43. data/_sass/bootstrap/scss/_progress.scss +59 -0
  44. data/_sass/bootstrap/scss/_reboot.scss +610 -0
  45. data/_sass/bootstrap/scss/_root.scss +76 -0
  46. data/_sass/bootstrap/scss/_spinners.scss +85 -0
  47. data/_sass/bootstrap/scss/_tables.scss +164 -0
  48. data/_sass/bootstrap/scss/_toasts.scss +70 -0
  49. data/_sass/bootstrap/scss/_tooltip.scss +122 -0
  50. data/_sass/bootstrap/scss/_transitions.scss +27 -0
  51. data/_sass/bootstrap/scss/_type.scss +104 -0
  52. data/_sass/bootstrap/scss/_utilities.scss +647 -0
  53. data/_sass/bootstrap/scss/_variables.scss +1631 -0
  54. data/_sass/bootstrap/scss/bootstrap-grid.scss +68 -0
  55. data/_sass/bootstrap/scss/bootstrap-reboot.scss +14 -0
  56. data/_sass/bootstrap/scss/bootstrap-utilities.scss +19 -0
  57. data/_sass/bootstrap/scss/bootstrap.scss +54 -0
  58. data/_sass/bootstrap/scss/forms/_floating-labels.scss +70 -0
  59. data/_sass/bootstrap/scss/forms/_form-check.scss +175 -0
  60. data/_sass/bootstrap/scss/forms/_form-control.scss +189 -0
  61. data/_sass/bootstrap/scss/forms/_form-range.scss +91 -0
  62. data/_sass/bootstrap/scss/forms/_form-select.scss +71 -0
  63. data/_sass/bootstrap/scss/forms/_form-text.scss +11 -0
  64. data/_sass/bootstrap/scss/forms/_input-group.scss +121 -0
  65. data/_sass/bootstrap/scss/forms/_labels.scss +36 -0
  66. data/_sass/bootstrap/scss/forms/_validation.scss +12 -0
  67. data/_sass/bootstrap/scss/helpers/_clearfix.scss +3 -0
  68. data/_sass/bootstrap/scss/helpers/_color-bg.scss +10 -0
  69. data/_sass/bootstrap/scss/helpers/_colored-links.scss +12 -0
  70. data/_sass/bootstrap/scss/helpers/_position.scss +36 -0
  71. data/_sass/bootstrap/scss/helpers/_ratio.scss +26 -0
  72. data/_sass/bootstrap/scss/helpers/_stacks.scss +15 -0
  73. data/_sass/bootstrap/scss/helpers/_stretched-link.scss +15 -0
  74. data/_sass/bootstrap/scss/helpers/_text-truncation.scss +7 -0
  75. data/_sass/bootstrap/scss/helpers/_visually-hidden.scss +8 -0
  76. data/_sass/bootstrap/scss/helpers/_vr.scss +8 -0
  77. data/_sass/bootstrap/scss/mixins/_alert.scss +15 -0
  78. data/_sass/bootstrap/scss/mixins/_backdrop.scss +14 -0
  79. data/_sass/bootstrap/scss/mixins/_background-variant.scss +23 -0
  80. data/_sass/bootstrap/scss/mixins/_badge.scss +17 -0
  81. data/_sass/bootstrap/scss/mixins/_border-radius.scss +78 -0
  82. data/_sass/bootstrap/scss/mixins/_box-shadow.scss +18 -0
  83. data/_sass/bootstrap/scss/mixins/_breakpoints.scss +127 -0
  84. data/_sass/bootstrap/scss/mixins/_buttons.scss +71 -0
  85. data/_sass/bootstrap/scss/mixins/_caret.scss +64 -0
  86. data/_sass/bootstrap/scss/mixins/_clearfix.scss +9 -0
  87. data/_sass/bootstrap/scss/mixins/_color-scheme.scss +7 -0
  88. data/_sass/bootstrap/scss/mixins/_container.scss +11 -0
  89. data/_sass/bootstrap/scss/mixins/_deprecate.scss +10 -0
  90. data/_sass/bootstrap/scss/mixins/_float.scss +14 -0
  91. data/_sass/bootstrap/scss/mixins/_forms.scss +152 -0
  92. data/_sass/bootstrap/scss/mixins/_gradients.scss +47 -0
  93. data/_sass/bootstrap/scss/mixins/_grid-framework.scss +80 -0
  94. data/_sass/bootstrap/scss/mixins/_grid.scss +151 -0
  95. data/_sass/bootstrap/scss/mixins/_hover.scss +37 -0
  96. data/_sass/bootstrap/scss/mixins/_image.scss +16 -0
  97. data/_sass/bootstrap/scss/mixins/_list-group.scss +24 -0
  98. data/_sass/bootstrap/scss/mixins/_lists.scss +7 -0
  99. data/_sass/bootstrap/scss/mixins/_nav-divider.scss +11 -0
  100. data/_sass/bootstrap/scss/mixins/_pagination.scss +10 -0
  101. data/_sass/bootstrap/scss/mixins/_reset-text.scss +17 -0
  102. data/_sass/bootstrap/scss/mixins/_resize.scss +6 -0
  103. data/_sass/bootstrap/scss/mixins/_screen-reader.scss +34 -0
  104. data/_sass/bootstrap/scss/mixins/_size.scss +7 -0
  105. data/_sass/bootstrap/scss/mixins/_table-row.scss +39 -0
  106. data/_sass/bootstrap/scss/mixins/_table-variants.scss +24 -0
  107. data/_sass/bootstrap/scss/mixins/_text-emphasis.scss +17 -0
  108. data/_sass/bootstrap/scss/mixins/_text-hide.scss +11 -0
  109. data/_sass/bootstrap/scss/mixins/_text-truncate.scss +8 -0
  110. data/_sass/bootstrap/scss/mixins/_transition.scss +26 -0
  111. data/_sass/bootstrap/scss/mixins/_utilities.scss +92 -0
  112. data/_sass/bootstrap/scss/mixins/_visibility.scss +8 -0
  113. data/_sass/bootstrap/scss/mixins/_visually-hidden.scss +29 -0
  114. data/_sass/bootstrap/scss/utilities/_align.scss +8 -0
  115. data/_sass/bootstrap/scss/utilities/_api.scss +47 -0
  116. data/_sass/bootstrap/scss/utilities/_background.scss +19 -0
  117. data/_sass/bootstrap/scss/utilities/_borders.scss +75 -0
  118. data/_sass/bootstrap/scss/utilities/_clearfix.scss +3 -0
  119. data/_sass/bootstrap/scss/utilities/_display.scss +26 -0
  120. data/_sass/bootstrap/scss/utilities/_embed.scss +39 -0
  121. data/_sass/bootstrap/scss/utilities/_flex.scss +51 -0
  122. data/_sass/bootstrap/scss/utilities/_float.scss +11 -0
  123. data/_sass/bootstrap/scss/utilities/_interactions.scss +5 -0
  124. data/_sass/bootstrap/scss/utilities/_overflow.scss +5 -0
  125. data/_sass/bootstrap/scss/utilities/_position.scss +32 -0
  126. data/_sass/bootstrap/scss/utilities/_screenreaders.scss +11 -0
  127. data/_sass/bootstrap/scss/utilities/_shadows.scss +6 -0
  128. data/_sass/bootstrap/scss/utilities/_sizing.scss +20 -0
  129. data/_sass/bootstrap/scss/utilities/_spacing.scss +73 -0
  130. data/_sass/bootstrap/scss/utilities/_stretched-link.scss +19 -0
  131. data/_sass/bootstrap/scss/utilities/_text.scss +72 -0
  132. data/_sass/bootstrap/scss/utilities/_visibility.scss +13 -0
  133. data/_sass/bootstrap/scss/vendor/_rfs.scss +354 -0
  134. data/_sass/jekyll-theme-cs50.scss +298 -0
  135. data/_sass/page.scss +228 -0
  136. data/_sass/rouge.scss +35 -0
  137. data/assets/@fortawesome/fontawesome-free/css/all.min.css +5 -0
  138. data/assets/@fortawesome/fontawesome-free/webfonts/fa-brands-400.eot +0 -0
  139. data/assets/@fortawesome/fontawesome-free/webfonts/fa-brands-400.svg +3717 -0
  140. data/assets/@fortawesome/fontawesome-free/webfonts/fa-brands-400.ttf +0 -0
  141. data/assets/@fortawesome/fontawesome-free/webfonts/fa-brands-400.woff +0 -0
  142. data/assets/@fortawesome/fontawesome-free/webfonts/fa-brands-400.woff2 +0 -0
  143. data/assets/@fortawesome/fontawesome-free/webfonts/fa-regular-400.eot +0 -0
  144. data/assets/@fortawesome/fontawesome-free/webfonts/fa-regular-400.svg +801 -0
  145. data/assets/@fortawesome/fontawesome-free/webfonts/fa-regular-400.ttf +0 -0
  146. data/assets/@fortawesome/fontawesome-free/webfonts/fa-regular-400.woff +0 -0
  147. data/assets/@fortawesome/fontawesome-free/webfonts/fa-regular-400.woff2 +0 -0
  148. data/assets/@fortawesome/fontawesome-free/webfonts/fa-solid-900.eot +0 -0
  149. data/assets/@fortawesome/fontawesome-free/webfonts/fa-solid-900.svg +5034 -0
  150. data/assets/@fortawesome/fontawesome-free/webfonts/fa-solid-900.ttf +0 -0
  151. data/assets/@fortawesome/fontawesome-free/webfonts/fa-solid-900.woff +0 -0
  152. data/assets/@fortawesome/fontawesome-free/webfonts/fa-solid-900.woff2 +0 -0
  153. data/assets/algolia-min.css +1 -0
  154. data/assets/algoliasearch/dist/algoliasearch-lite.umd.js +2 -0
  155. data/assets/algoliasearchLite.min.js +3 -0
  156. data/assets/bootstrap/LICENSE +22 -0
  157. data/assets/bootstrap/dist/js/bootstrap.bundle.min.js +7 -0
  158. data/assets/bootstrap/dist/js/bootstrap.bundle.min.js.map +1 -0
  159. data/assets/bootstrap/dist/js/bootstrap.min.js +7 -0
  160. data/assets/bootstrap-table/LICENSE +21 -0
  161. data/assets/bootstrap-table/dist/bootstrap-table.min.css +10 -0
  162. data/assets/bootstrap-table/dist/bootstrap-table.min.js +10 -0
  163. data/assets/bootstrap-table/dist/extensions/mobile/bootstrap-table-mobile.min.js +10 -0
  164. data/assets/instantsearch.js/LICENSE +21 -0
  165. data/assets/instantsearch.js/dist/instantsearch.production.min.js +3 -0
  166. data/assets/instantsearch.js/dist/instantsearch.production.min.js.map +1 -0
  167. data/assets/instantsearch.production.min.js +3 -0
  168. data/assets/jekyll-theme-cs50.js +620 -0
  169. data/assets/jquery/LICENSE.txt +20 -0
  170. data/assets/jquery/dist/jquery.min.js +2 -0
  171. data/assets/luxon.min.js +1 -0
  172. data/assets/moment-timezone-with-data.min.js +1 -0
  173. data/assets/moment.min.js +1 -0
  174. data/assets/page.js +4 -0
  175. data/assets/page.scss +4 -0
  176. data/assets/popper.js/dist/umd/popper.min.js +5 -0
  177. data/assets/reset-min.css +1 -0
  178. data/assets/scratchblocks.min.js +11 -0
  179. data/assets/scratchblocks.min.js.map +1 -0
  180. data/lib/jekyll-theme-cs50/constants.rb +59 -0
  181. data/lib/jekyll-theme-cs50.rb +606 -0
  182. data/lib/liquid-tag-parser.rb +1 -0
  183. 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.