New version of the lecture
BIN
Lecture_files/figure-revealjs/unnamed-chunk-18-1.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
Lecture_files/figure-revealjs/unnamed-chunk-19-1.png
Normal file
|
After Width: | Height: | Size: 59 KiB |
BIN
Lecture_files/figure-revealjs/unnamed-chunk-24-1.png
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
Lecture_files/figure-revealjs/unnamed-chunk-27-1.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
Lecture_files/figure-revealjs/unnamed-chunk-48-1.png
Normal file
|
After Width: | Height: | Size: 117 KiB |
BIN
Lecture_files/figure-revealjs/unnamed-chunk-55-1.png
Normal file
|
After Width: | Height: | Size: 99 KiB |
BIN
Lecture_files/figure-revealjs/unnamed-chunk-57-1.png
Normal file
|
After Width: | Height: | Size: 85 KiB |
BIN
Lecture_files/figure-revealjs/unnamed-chunk-59-1.png
Normal file
|
After Width: | Height: | Size: 102 KiB |
BIN
Lecture_files/figure-revealjs/unnamed-chunk-60-1.png
Normal file
|
After Width: | Height: | Size: 103 KiB |
BIN
Lecture_files/figure-revealjs/unnamed-chunk-62-1.png
Normal file
|
After Width: | Height: | Size: 109 KiB |
BIN
Lecture_files/figure-revealjs/unnamed-chunk-65-1.png
Normal file
|
After Width: | Height: | Size: 86 KiB |
BIN
Lecture_files/figure-revealjs/unnamed-chunk-67-1.png
Normal file
|
After Width: | Height: | Size: 113 KiB |
BIN
Lecture_files/figure-revealjs/unnamed-chunk-72-1.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
Lecture_files/figure-revealjs/unnamed-chunk-74-1.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
Lecture_files/figure-revealjs/unnamed-chunk-76-1.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
Lecture_files/figure-revealjs/unnamed-chunk-83-1.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
Lecture_files/figure-revealjs/unnamed-chunk-86-1.png
Normal file
|
After Width: | Height: | Size: 180 KiB |
BIN
Lecture_files/figure-revealjs/unnamed-chunk-88-1.png
Normal file
|
After Width: | Height: | Size: 115 KiB |
BIN
Lecture_files/figure-revealjs/unnamed-chunk-90-1.png
Normal file
|
After Width: | Height: | Size: 156 KiB |
BIN
Lecture_files/figure-revealjs/unnamed-chunk-92-1.png
Normal file
|
After Width: | Height: | Size: 158 KiB |
7
Lecture_files/libs/clipboard/clipboard.min.js
vendored
Normal file
8
Lecture_files/libs/kePrint-0.0.1/kePrint.js
Normal file
@@ -0,0 +1,8 @@
|
||||
$(document).ready(function(){
|
||||
if (typeof $('[data-toggle="tooltip"]').tooltip === 'function') {
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
}
|
||||
if ($('[data-toggle="popover"]').popover === 'function') {
|
||||
$('[data-toggle="popover"]').popover();
|
||||
}
|
||||
});
|
||||
272
Lecture_files/libs/lightable-0.0.1/lightable.css
Normal file
@@ -0,0 +1,272 @@
|
||||
/*!
|
||||
* lightable v0.0.1
|
||||
* Copyright 2020 Hao Zhu
|
||||
* Licensed under MIT (https://github.com/haozhu233/kableExtra/blob/master/LICENSE)
|
||||
*/
|
||||
|
||||
.lightable-minimal {
|
||||
border-collapse: separate;
|
||||
border-spacing: 16px 1px;
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.lightable-minimal td {
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.lightable-minimal th {
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.lightable-minimal thead tr:last-child th {
|
||||
border-bottom: 2px solid #00000050;
|
||||
empty-cells: hide;
|
||||
|
||||
}
|
||||
|
||||
.lightable-minimal tbody tr:first-child td {
|
||||
padding-top: 0.5em;
|
||||
}
|
||||
|
||||
.lightable-minimal.lightable-hover tbody tr:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.lightable-minimal.lightable-striped tbody tr:nth-child(even) {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.lightable-classic {
|
||||
border-top: 0.16em solid #111111;
|
||||
border-bottom: 0.16em solid #111111;
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
margin: 10px 5px;
|
||||
}
|
||||
|
||||
.lightable-classic tfoot tr td {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.lightable-classic tfoot tr:first-child td {
|
||||
border-top: 0.14em solid #111111;
|
||||
}
|
||||
|
||||
.lightable-classic caption {
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
.lightable-classic td {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
.lightable-classic th {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
font-weight: normal;
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
.lightable-classic thead tr:last-child th {
|
||||
border-bottom: 0.10em solid #111111;
|
||||
}
|
||||
|
||||
.lightable-classic.lightable-hover tbody tr:hover {
|
||||
background-color: #F9EEC1;
|
||||
}
|
||||
|
||||
.lightable-classic.lightable-striped tbody tr:nth-child(even) {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.lightable-classic-2 {
|
||||
border-top: 3px double #111111;
|
||||
border-bottom: 3px double #111111;
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.lightable-classic-2 tfoot tr td {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.lightable-classic-2 tfoot tr:first-child td {
|
||||
border-top: 3px double #111111;
|
||||
}
|
||||
|
||||
.lightable-classic-2 caption {
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
.lightable-classic-2 td {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
.lightable-classic-2 th {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
font-weight: normal;
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
.lightable-classic-2 tbody tr:last-child td {
|
||||
border-bottom: 3px double #111111;
|
||||
}
|
||||
|
||||
.lightable-classic-2 thead tr:last-child th {
|
||||
border-bottom: 1px solid #111111;
|
||||
}
|
||||
|
||||
.lightable-classic-2.lightable-hover tbody tr:hover {
|
||||
background-color: #F9EEC1;
|
||||
}
|
||||
|
||||
.lightable-classic-2.lightable-striped tbody tr:nth-child(even) {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.lightable-material {
|
||||
min-width: 100%;
|
||||
white-space: nowrap;
|
||||
table-layout: fixed;
|
||||
font-family: Roboto, sans-serif;
|
||||
border: 1px solid #EEE;
|
||||
border-collapse: collapse;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.lightable-material tfoot tr td {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.lightable-material tfoot tr:first-child td {
|
||||
border-top: 1px solid #EEE;
|
||||
}
|
||||
|
||||
.lightable-material th {
|
||||
height: 56px;
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
}
|
||||
|
||||
.lightable-material td {
|
||||
height: 52px;
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
border-top: 1px solid #eeeeee;
|
||||
}
|
||||
|
||||
.lightable-material.lightable-hover tbody tr:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.lightable-material.lightable-striped tbody tr:nth-child(even) {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.lightable-material.lightable-striped tbody td {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.lightable-material.lightable-striped thead tr:last-child th {
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.lightable-material-dark {
|
||||
min-width: 100%;
|
||||
white-space: nowrap;
|
||||
table-layout: fixed;
|
||||
font-family: Roboto, sans-serif;
|
||||
border: 1px solid #FFFFFF12;
|
||||
border-collapse: collapse;
|
||||
margin-bottom: 10px;
|
||||
background-color: #363640;
|
||||
}
|
||||
|
||||
.lightable-material-dark tfoot tr td {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.lightable-material-dark tfoot tr:first-child td {
|
||||
border-top: 1px solid #FFFFFF12;
|
||||
}
|
||||
|
||||
.lightable-material-dark th {
|
||||
height: 56px;
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
color: #FFFFFF60;
|
||||
}
|
||||
|
||||
.lightable-material-dark td {
|
||||
height: 52px;
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
color: #FFFFFF;
|
||||
border-top: 1px solid #FFFFFF12;
|
||||
}
|
||||
|
||||
.lightable-material-dark.lightable-hover tbody tr:hover {
|
||||
background-color: #FFFFFF12;
|
||||
}
|
||||
|
||||
.lightable-material-dark.lightable-striped tbody tr:nth-child(even) {
|
||||
background-color: #FFFFFF12;
|
||||
}
|
||||
|
||||
.lightable-material-dark.lightable-striped tbody td {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.lightable-material-dark.lightable-striped thead tr:last-child th {
|
||||
border-bottom: 1px solid #FFFFFF12;
|
||||
}
|
||||
|
||||
.lightable-paper {
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.lightable-paper tfoot tr td {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.lightable-paper tfoot tr:first-child td {
|
||||
border-top: 1px solid #00000020;
|
||||
}
|
||||
|
||||
.lightable-paper thead tr:last-child th {
|
||||
color: #666;
|
||||
vertical-align: bottom;
|
||||
border-bottom: 1px solid #00000020;
|
||||
line-height: 1.15em;
|
||||
padding: 10px 5px;
|
||||
}
|
||||
|
||||
.lightable-paper td {
|
||||
vertical-align: middle;
|
||||
border-bottom: 1px solid #00000010;
|
||||
line-height: 1.15em;
|
||||
padding: 7px 5px;
|
||||
}
|
||||
|
||||
.lightable-paper.lightable-hover tbody tr:hover {
|
||||
background-color: #F9EEC1;
|
||||
}
|
||||
|
||||
.lightable-paper.lightable-striped tbody tr:nth-child(even) {
|
||||
background-color: #00000008;
|
||||
}
|
||||
|
||||
.lightable-paper.lightable-striped tbody td {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
6
Lecture_files/libs/quarto-html/popper.min.js
vendored
Normal file
1
Lecture_files/libs/quarto-html/quarto-html.min.css
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/*# sourceMappingURL=0a6b880beb84f9b6f36107a76f82c5b1.css.map */
|
||||
171
Lecture_files/libs/quarto-html/quarto-syntax-highlighting.css
Normal file
@@ -0,0 +1,171 @@
|
||||
/* quarto syntax highlight colors */
|
||||
:root {
|
||||
--quarto-hl-ot-color: #003B4F;
|
||||
--quarto-hl-at-color: #657422;
|
||||
--quarto-hl-ss-color: #20794D;
|
||||
--quarto-hl-an-color: #5E5E5E;
|
||||
--quarto-hl-fu-color: #4758AB;
|
||||
--quarto-hl-st-color: #20794D;
|
||||
--quarto-hl-cf-color: #003B4F;
|
||||
--quarto-hl-op-color: #5E5E5E;
|
||||
--quarto-hl-er-color: #AD0000;
|
||||
--quarto-hl-bn-color: #AD0000;
|
||||
--quarto-hl-al-color: #AD0000;
|
||||
--quarto-hl-va-color: #111111;
|
||||
--quarto-hl-bu-color: inherit;
|
||||
--quarto-hl-ex-color: inherit;
|
||||
--quarto-hl-pp-color: #AD0000;
|
||||
--quarto-hl-in-color: #5E5E5E;
|
||||
--quarto-hl-vs-color: #20794D;
|
||||
--quarto-hl-wa-color: #5E5E5E;
|
||||
--quarto-hl-do-color: #5E5E5E;
|
||||
--quarto-hl-im-color: #00769E;
|
||||
--quarto-hl-ch-color: #20794D;
|
||||
--quarto-hl-dt-color: #AD0000;
|
||||
--quarto-hl-fl-color: #AD0000;
|
||||
--quarto-hl-co-color: #5E5E5E;
|
||||
--quarto-hl-cv-color: #5E5E5E;
|
||||
--quarto-hl-cn-color: #8f5902;
|
||||
--quarto-hl-sc-color: #5E5E5E;
|
||||
--quarto-hl-dv-color: #AD0000;
|
||||
--quarto-hl-kw-color: #003B4F;
|
||||
}
|
||||
|
||||
/* other quarto variables */
|
||||
:root {
|
||||
--quarto-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
}
|
||||
|
||||
pre > code.sourceCode > span {
|
||||
color: #003B4F;
|
||||
}
|
||||
|
||||
code span {
|
||||
color: #003B4F;
|
||||
}
|
||||
|
||||
code.sourceCode > span {
|
||||
color: #003B4F;
|
||||
}
|
||||
|
||||
div.sourceCode,
|
||||
div.sourceCode pre.sourceCode {
|
||||
color: #003B4F;
|
||||
}
|
||||
|
||||
code span.ot {
|
||||
color: #003B4F;
|
||||
}
|
||||
|
||||
code span.at {
|
||||
color: #657422;
|
||||
}
|
||||
|
||||
code span.ss {
|
||||
color: #20794D;
|
||||
}
|
||||
|
||||
code span.an {
|
||||
color: #5E5E5E;
|
||||
}
|
||||
|
||||
code span.fu {
|
||||
color: #4758AB;
|
||||
}
|
||||
|
||||
code span.st {
|
||||
color: #20794D;
|
||||
}
|
||||
|
||||
code span.cf {
|
||||
color: #003B4F;
|
||||
}
|
||||
|
||||
code span.op {
|
||||
color: #5E5E5E;
|
||||
}
|
||||
|
||||
code span.er {
|
||||
color: #AD0000;
|
||||
}
|
||||
|
||||
code span.bn {
|
||||
color: #AD0000;
|
||||
}
|
||||
|
||||
code span.al {
|
||||
color: #AD0000;
|
||||
}
|
||||
|
||||
code span.va {
|
||||
color: #111111;
|
||||
}
|
||||
|
||||
code span.pp {
|
||||
color: #AD0000;
|
||||
}
|
||||
|
||||
code span.in {
|
||||
color: #5E5E5E;
|
||||
}
|
||||
|
||||
code span.vs {
|
||||
color: #20794D;
|
||||
}
|
||||
|
||||
code span.wa {
|
||||
color: #5E5E5E;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
code span.do {
|
||||
color: #5E5E5E;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
code span.im {
|
||||
color: #00769E;
|
||||
}
|
||||
|
||||
code span.ch {
|
||||
color: #20794D;
|
||||
}
|
||||
|
||||
code span.dt {
|
||||
color: #AD0000;
|
||||
}
|
||||
|
||||
code span.fl {
|
||||
color: #AD0000;
|
||||
}
|
||||
|
||||
code span.co {
|
||||
color: #5E5E5E;
|
||||
}
|
||||
|
||||
code span.cv {
|
||||
color: #5E5E5E;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
code span.cn {
|
||||
color: #8f5902;
|
||||
}
|
||||
|
||||
code span.sc {
|
||||
color: #5E5E5E;
|
||||
}
|
||||
|
||||
code span.dv {
|
||||
color: #AD0000;
|
||||
}
|
||||
|
||||
code span.kw {
|
||||
color: #003B4F;
|
||||
}
|
||||
|
||||
.prevent-inlining {
|
||||
content: "</";
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=debc5d5d77c3f9108843748ff7464032.css.map */
|
||||
418
Lecture_files/libs/quarto-html/tabby.min.js
vendored
Normal file
@@ -0,0 +1,418 @@
|
||||
(function (root, factory) {
|
||||
if (typeof define === "function" && define.amd) {
|
||||
define([], function () {
|
||||
return factory(root);
|
||||
});
|
||||
} else if (typeof exports === "object") {
|
||||
module.exports = factory(root);
|
||||
} else {
|
||||
root.Tabby = factory(root);
|
||||
}
|
||||
})(
|
||||
typeof global !== "undefined"
|
||||
? global
|
||||
: typeof window !== "undefined"
|
||||
? window
|
||||
: this,
|
||||
function (window) {
|
||||
"use strict";
|
||||
|
||||
//
|
||||
// Variables
|
||||
//
|
||||
|
||||
var defaults = {
|
||||
idPrefix: "tabby-toggle_",
|
||||
default: "[data-tabby-default]",
|
||||
};
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
/**
|
||||
* Merge two or more objects together.
|
||||
* @param {Object} objects The objects to merge together
|
||||
* @returns {Object} Merged values of defaults and options
|
||||
*/
|
||||
var extend = function () {
|
||||
var merged = {};
|
||||
Array.prototype.forEach.call(arguments, function (obj) {
|
||||
for (var key in obj) {
|
||||
if (!obj.hasOwnProperty(key)) return;
|
||||
merged[key] = obj[key];
|
||||
}
|
||||
});
|
||||
return merged;
|
||||
};
|
||||
|
||||
/**
|
||||
* Emit a custom event
|
||||
* @param {String} type The event type
|
||||
* @param {Node} tab The tab to attach the event to
|
||||
* @param {Node} details Details about the event
|
||||
*/
|
||||
var emitEvent = function (tab, details) {
|
||||
// Create a new event
|
||||
var event;
|
||||
if (typeof window.CustomEvent === "function") {
|
||||
event = new CustomEvent("tabby", {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
detail: details,
|
||||
});
|
||||
} else {
|
||||
event = document.createEvent("CustomEvent");
|
||||
event.initCustomEvent("tabby", true, true, details);
|
||||
}
|
||||
|
||||
// Dispatch the event
|
||||
tab.dispatchEvent(event);
|
||||
};
|
||||
|
||||
var focusHandler = function (event) {
|
||||
toggle(event.target);
|
||||
};
|
||||
|
||||
var getKeyboardFocusableElements = function (element) {
|
||||
return [
|
||||
...element.querySelectorAll(
|
||||
'a[href], button, input, textarea, select, details,[tabindex]:not([tabindex="-1"])'
|
||||
),
|
||||
].filter(
|
||||
(el) => !el.hasAttribute("disabled") && !el.getAttribute("aria-hidden")
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove roles and attributes from a tab and its content
|
||||
* @param {Node} tab The tab
|
||||
* @param {Node} content The tab content
|
||||
* @param {Object} settings User settings and options
|
||||
*/
|
||||
var destroyTab = function (tab, content, settings) {
|
||||
// Remove the generated ID
|
||||
if (tab.id.slice(0, settings.idPrefix.length) === settings.idPrefix) {
|
||||
tab.id = "";
|
||||
}
|
||||
|
||||
// remove event listener
|
||||
tab.removeEventListener("focus", focusHandler, true);
|
||||
|
||||
// Remove roles
|
||||
tab.removeAttribute("role");
|
||||
tab.removeAttribute("aria-controls");
|
||||
tab.removeAttribute("aria-selected");
|
||||
tab.removeAttribute("tabindex");
|
||||
tab.closest("li").removeAttribute("role");
|
||||
content.removeAttribute("role");
|
||||
content.removeAttribute("aria-labelledby");
|
||||
content.removeAttribute("hidden");
|
||||
};
|
||||
|
||||
/**
|
||||
* Add the required roles and attributes to a tab and its content
|
||||
* @param {Node} tab The tab
|
||||
* @param {Node} content The tab content
|
||||
* @param {Object} settings User settings and options
|
||||
*/
|
||||
var setupTab = function (tab, content, settings) {
|
||||
// Give tab an ID if it doesn't already have one
|
||||
if (!tab.id) {
|
||||
tab.id = settings.idPrefix + content.id;
|
||||
}
|
||||
|
||||
// Add roles
|
||||
tab.setAttribute("role", "tab");
|
||||
tab.setAttribute("aria-controls", content.id);
|
||||
tab.closest("li").setAttribute("role", "presentation");
|
||||
content.setAttribute("role", "tabpanel");
|
||||
content.setAttribute("aria-labelledby", tab.id);
|
||||
|
||||
// Add selected state
|
||||
if (tab.matches(settings.default)) {
|
||||
tab.setAttribute("aria-selected", "true");
|
||||
} else {
|
||||
tab.setAttribute("aria-selected", "false");
|
||||
content.setAttribute("hidden", "hidden");
|
||||
}
|
||||
|
||||
// add focus event listender
|
||||
tab.addEventListener("focus", focusHandler);
|
||||
};
|
||||
|
||||
/**
|
||||
* Hide a tab and its content
|
||||
* @param {Node} newTab The new tab that's replacing it
|
||||
*/
|
||||
var hide = function (newTab) {
|
||||
// Variables
|
||||
var tabGroup = newTab.closest('[role="tablist"]');
|
||||
if (!tabGroup) return {};
|
||||
var tab = tabGroup.querySelector('[role="tab"][aria-selected="true"]');
|
||||
if (!tab) return {};
|
||||
var content = document.querySelector(tab.hash);
|
||||
|
||||
// Hide the tab
|
||||
tab.setAttribute("aria-selected", "false");
|
||||
|
||||
// Hide the content
|
||||
if (!content) return { previousTab: tab };
|
||||
content.setAttribute("hidden", "hidden");
|
||||
|
||||
// Return the hidden tab and content
|
||||
return {
|
||||
previousTab: tab,
|
||||
previousContent: content,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Show a tab and its content
|
||||
* @param {Node} tab The tab
|
||||
* @param {Node} content The tab content
|
||||
*/
|
||||
var show = function (tab, content) {
|
||||
tab.setAttribute("aria-selected", "true");
|
||||
content.removeAttribute("hidden");
|
||||
tab.focus();
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggle a new tab
|
||||
* @param {Node} tab The tab to show
|
||||
*/
|
||||
var toggle = function (tab) {
|
||||
// Make sure there's a tab to toggle and it's not already active
|
||||
if (!tab || tab.getAttribute("aria-selected") == "true") return;
|
||||
|
||||
// Variables
|
||||
var content = document.querySelector(tab.hash);
|
||||
if (!content) return;
|
||||
|
||||
// Hide active tab and content
|
||||
var details = hide(tab);
|
||||
|
||||
// Show new tab and content
|
||||
show(tab, content);
|
||||
|
||||
// Add event details
|
||||
details.tab = tab;
|
||||
details.content = content;
|
||||
|
||||
// Emit a custom event
|
||||
emitEvent(tab, details);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all of the tabs in a tablist
|
||||
* @param {Node} tab A tab from the list
|
||||
* @return {Object} The tabs and the index of the currently active one
|
||||
*/
|
||||
var getTabsMap = function (tab) {
|
||||
var tabGroup = tab.closest('[role="tablist"]');
|
||||
var tabs = tabGroup ? tabGroup.querySelectorAll('[role="tab"]') : null;
|
||||
if (!tabs) return;
|
||||
return {
|
||||
tabs: tabs,
|
||||
index: Array.prototype.indexOf.call(tabs, tab),
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Switch the active tab based on keyboard activity
|
||||
* @param {Node} tab The currently active tab
|
||||
* @param {Key} key The key that was pressed
|
||||
*/
|
||||
var switchTabs = function (tab, key) {
|
||||
// Get a map of tabs
|
||||
var map = getTabsMap(tab);
|
||||
if (!map) return;
|
||||
var length = map.tabs.length - 1;
|
||||
var index;
|
||||
|
||||
// Go to previous tab
|
||||
if (["ArrowUp", "ArrowLeft", "Up", "Left"].indexOf(key) > -1) {
|
||||
index = map.index < 1 ? length : map.index - 1;
|
||||
}
|
||||
|
||||
// Go to next tab
|
||||
else if (["ArrowDown", "ArrowRight", "Down", "Right"].indexOf(key) > -1) {
|
||||
index = map.index === length ? 0 : map.index + 1;
|
||||
}
|
||||
|
||||
// Go to home
|
||||
else if (key === "Home") {
|
||||
index = 0;
|
||||
}
|
||||
|
||||
// Go to end
|
||||
else if (key === "End") {
|
||||
index = length;
|
||||
}
|
||||
|
||||
// Toggle the tab
|
||||
toggle(map.tabs[index]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create the Constructor object
|
||||
*/
|
||||
var Constructor = function (selector, options) {
|
||||
//
|
||||
// Variables
|
||||
//
|
||||
|
||||
var publicAPIs = {};
|
||||
var settings, tabWrapper;
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
publicAPIs.destroy = function () {
|
||||
// Get all tabs
|
||||
var tabs = tabWrapper.querySelectorAll("a");
|
||||
|
||||
// Add roles to tabs
|
||||
Array.prototype.forEach.call(tabs, function (tab) {
|
||||
// Get the tab content
|
||||
var content = document.querySelector(tab.hash);
|
||||
if (!content) return;
|
||||
|
||||
// Setup the tab
|
||||
destroyTab(tab, content, settings);
|
||||
});
|
||||
|
||||
// Remove role from wrapper
|
||||
tabWrapper.removeAttribute("role");
|
||||
|
||||
// Remove event listeners
|
||||
document.documentElement.removeEventListener(
|
||||
"click",
|
||||
clickHandler,
|
||||
true
|
||||
);
|
||||
tabWrapper.removeEventListener("keydown", keyHandler, true);
|
||||
|
||||
// Reset variables
|
||||
settings = null;
|
||||
tabWrapper = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Setup the DOM with the proper attributes
|
||||
*/
|
||||
publicAPIs.setup = function () {
|
||||
// Variables
|
||||
tabWrapper = document.querySelector(selector);
|
||||
if (!tabWrapper) return;
|
||||
var tabs = tabWrapper.querySelectorAll("a");
|
||||
|
||||
// Add role to wrapper
|
||||
tabWrapper.setAttribute("role", "tablist");
|
||||
|
||||
// Add roles to tabs. provide dynanmic tab indexes if we are within reveal
|
||||
var contentTabindexes =
|
||||
window.document.body.classList.contains("reveal-viewport");
|
||||
var nextTabindex = 1;
|
||||
Array.prototype.forEach.call(tabs, function (tab) {
|
||||
if (contentTabindexes) {
|
||||
tab.setAttribute("tabindex", "" + nextTabindex++);
|
||||
} else {
|
||||
tab.setAttribute("tabindex", "0");
|
||||
}
|
||||
|
||||
// Get the tab content
|
||||
var content = document.querySelector(tab.hash);
|
||||
if (!content) return;
|
||||
|
||||
// set tab indexes for content
|
||||
if (contentTabindexes) {
|
||||
getKeyboardFocusableElements(content).forEach(function (el) {
|
||||
el.setAttribute("tabindex", "" + nextTabindex++);
|
||||
});
|
||||
}
|
||||
|
||||
// Setup the tab
|
||||
setupTab(tab, content, settings);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggle a tab based on an ID
|
||||
* @param {String|Node} id The tab to toggle
|
||||
*/
|
||||
publicAPIs.toggle = function (id) {
|
||||
// Get the tab
|
||||
var tab = id;
|
||||
if (typeof id === "string") {
|
||||
tab = document.querySelector(
|
||||
selector + ' [role="tab"][href*="' + id + '"]'
|
||||
);
|
||||
}
|
||||
|
||||
// Toggle the tab
|
||||
toggle(tab);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle click events
|
||||
*/
|
||||
var clickHandler = function (event) {
|
||||
// Only run on toggles
|
||||
var tab = event.target.closest(selector + ' [role="tab"]');
|
||||
if (!tab) return;
|
||||
|
||||
// Prevent link behavior
|
||||
event.preventDefault();
|
||||
|
||||
// Toggle the tab
|
||||
toggle(tab);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle keydown events
|
||||
*/
|
||||
var keyHandler = function (event) {
|
||||
// Only run if a tab is in focus
|
||||
var tab = document.activeElement;
|
||||
if (!tab.matches(selector + ' [role="tab"]')) return;
|
||||
|
||||
// Only run for specific keys
|
||||
if (["Home", "End"].indexOf(event.key) < 0) return;
|
||||
|
||||
// Switch tabs
|
||||
switchTabs(tab, event.key);
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the instance
|
||||
*/
|
||||
var init = function () {
|
||||
// Merge user options with defaults
|
||||
settings = extend(defaults, options || {});
|
||||
|
||||
// Setup the DOM
|
||||
publicAPIs.setup();
|
||||
|
||||
// Add event listeners
|
||||
document.documentElement.addEventListener("click", clickHandler, true);
|
||||
tabWrapper.addEventListener("keydown", keyHandler, true);
|
||||
};
|
||||
|
||||
//
|
||||
// Initialize and return the Public APIs
|
||||
//
|
||||
|
||||
init();
|
||||
return publicAPIs;
|
||||
};
|
||||
|
||||
//
|
||||
// Return the Constructor
|
||||
//
|
||||
|
||||
return Constructor;
|
||||
}
|
||||
);
|
||||
1
Lecture_files/libs/quarto-html/tippy.css
Normal file
@@ -0,0 +1 @@
|
||||
.tippy-box[data-animation=fade][data-state=hidden]{opacity:0}[data-tippy-root]{max-width:calc(100vw - 10px)}.tippy-box{position:relative;background-color:#333;color:#fff;border-radius:4px;font-size:14px;line-height:1.4;white-space:normal;outline:0;transition-property:transform,visibility,opacity}.tippy-box[data-placement^=top]>.tippy-arrow{bottom:0}.tippy-box[data-placement^=top]>.tippy-arrow:before{bottom:-7px;left:0;border-width:8px 8px 0;border-top-color:initial;transform-origin:center top}.tippy-box[data-placement^=bottom]>.tippy-arrow{top:0}.tippy-box[data-placement^=bottom]>.tippy-arrow:before{top:-7px;left:0;border-width:0 8px 8px;border-bottom-color:initial;transform-origin:center bottom}.tippy-box[data-placement^=left]>.tippy-arrow{right:0}.tippy-box[data-placement^=left]>.tippy-arrow:before{border-width:8px 0 8px 8px;border-left-color:initial;right:-7px;transform-origin:center left}.tippy-box[data-placement^=right]>.tippy-arrow{left:0}.tippy-box[data-placement^=right]>.tippy-arrow:before{left:-7px;border-width:8px 8px 8px 0;border-right-color:initial;transform-origin:center right}.tippy-box[data-inertia][data-state=visible]{transition-timing-function:cubic-bezier(.54,1.5,.38,1.11)}.tippy-arrow{width:16px;height:16px;color:#333}.tippy-arrow:before{content:"";position:absolute;border-color:transparent;border-style:solid}.tippy-content{position:relative;padding:5px 9px;z-index:1}
|
||||
2
Lecture_files/libs/quarto-html/tippy.umd.min.js
vendored
Normal file
30
Lecture_files/libs/revealjs/dist/reset.css
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
/* http://meyerweb.com/eric/tools/css/reset/
|
||||
v4.0 | 20180602
|
||||
License: none (public domain)
|
||||
*/
|
||||
|
||||
html, body, div, span, applet, object, iframe,
|
||||
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
||||
a, abbr, acronym, address, big, cite, code,
|
||||
del, dfn, em, img, ins, kbd, q, s, samp,
|
||||
small, strike, strong, sub, sup, tt, var,
|
||||
b, u, i, center,
|
||||
dl, dt, dd, ol, ul, li,
|
||||
fieldset, form, label, legend,
|
||||
table, caption, tbody, tfoot, thead, tr, th, td,
|
||||
article, aside, canvas, details, embed,
|
||||
figure, figcaption, footer, header, hgroup,
|
||||
main, menu, nav, output, ruby, section, summary,
|
||||
time, mark, audio, video {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
font-size: 100%;
|
||||
font: inherit;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
/* HTML5 display-role reset for older browsers */
|
||||
article, aside, details, figcaption, figure,
|
||||
footer, header, hgroup, main, menu, nav, section {
|
||||
display: block;
|
||||
}
|
||||
8
Lecture_files/libs/revealjs/dist/reveal.css
vendored
Normal file
9
Lecture_files/libs/revealjs/dist/reveal.esm.js
vendored
Normal file
1
Lecture_files/libs/revealjs/dist/reveal.esm.js.map
vendored
Normal file
9
Lecture_files/libs/revealjs/dist/reveal.js
vendored
Normal file
1
Lecture_files/libs/revealjs/dist/reveal.js.map
vendored
Normal file
2
Lecture_files/libs/revealjs/dist/theme/fonts/league-gothic/LICENSE
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
SIL Open Font License (OFL)
|
||||
http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL
|
||||
10
Lecture_files/libs/revealjs/dist/theme/fonts/league-gothic/league-gothic.css
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
@font-face {
|
||||
font-family: 'League Gothic';
|
||||
src: url('./league-gothic.eot');
|
||||
src: url('./league-gothic.eot?#iefix') format('embedded-opentype'),
|
||||
url('./league-gothic.woff') format('woff'),
|
||||
url('./league-gothic.ttf') format('truetype');
|
||||
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
BIN
Lecture_files/libs/revealjs/dist/theme/fonts/league-gothic/league-gothic.eot
vendored
Executable file
BIN
Lecture_files/libs/revealjs/dist/theme/fonts/league-gothic/league-gothic.ttf
vendored
Executable file
BIN
Lecture_files/libs/revealjs/dist/theme/fonts/league-gothic/league-gothic.woff
vendored
Executable file
45
Lecture_files/libs/revealjs/dist/theme/fonts/source-sans-pro/LICENSE
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
SIL Open Font License
|
||||
|
||||
Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name ‘Source’. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries.
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
|
||||
|
||||
—————————————————————————————-
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
—————————————————————————————-
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
“Font Software” refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation.
|
||||
|
||||
“Reserved Font Name” refers to any names specified as such after the copyright statement(s).
|
||||
|
||||
“Original Version” refers to the collection of Font Software components as distributed by the Copyright Holder(s).
|
||||
|
||||
“Modified Version” refers to any derivative made by adding to, deleting, or substituting—in part or in whole—any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment.
|
||||
|
||||
“Author” refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
BIN
Lecture_files/libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-italic.eot
vendored
Executable file
BIN
Lecture_files/libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-italic.ttf
vendored
Executable file
BIN
Lecture_files/libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-italic.woff
vendored
Executable file
BIN
Lecture_files/libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-regular.eot
vendored
Executable file
BIN
Lecture_files/libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-regular.ttf
vendored
Executable file
BIN
Lecture_files/libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-regular.woff
vendored
Executable file
BIN
Lecture_files/libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-semibold.eot
vendored
Executable file
BIN
Lecture_files/libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-semibold.ttf
vendored
Executable file
BIN
Lecture_files/libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-semibold.woff
vendored
Executable file
BIN
Lecture_files/libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-semibolditalic.eot
vendored
Executable file
BIN
Lecture_files/libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-semibolditalic.ttf
vendored
Executable file
BIN
Lecture_files/libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-semibolditalic.woff
vendored
Executable file
39
Lecture_files/libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro.css
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
@font-face {
|
||||
font-family: 'Source Sans Pro';
|
||||
src: url('./source-sans-pro-regular.eot');
|
||||
src: url('./source-sans-pro-regular.eot?#iefix') format('embedded-opentype'),
|
||||
url('./source-sans-pro-regular.woff') format('woff'),
|
||||
url('./source-sans-pro-regular.ttf') format('truetype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Source Sans Pro';
|
||||
src: url('./source-sans-pro-italic.eot');
|
||||
src: url('./source-sans-pro-italic.eot?#iefix') format('embedded-opentype'),
|
||||
url('./source-sans-pro-italic.woff') format('woff'),
|
||||
url('./source-sans-pro-italic.ttf') format('truetype');
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Source Sans Pro';
|
||||
src: url('./source-sans-pro-semibold.eot');
|
||||
src: url('./source-sans-pro-semibold.eot?#iefix') format('embedded-opentype'),
|
||||
url('./source-sans-pro-semibold.woff') format('woff'),
|
||||
url('./source-sans-pro-semibold.ttf') format('truetype');
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Source Sans Pro';
|
||||
src: url('./source-sans-pro-semibolditalic.eot');
|
||||
src: url('./source-sans-pro-semibolditalic.eot?#iefix') format('embedded-opentype'),
|
||||
url('./source-sans-pro-semibolditalic.woff') format('woff'),
|
||||
url('./source-sans-pro-semibolditalic.ttf') format('truetype');
|
||||
font-weight: 600;
|
||||
font-style: italic;
|
||||
}
|
||||
5
Lecture_files/libs/revealjs/dist/theme/quarto.css
vendored
Normal file
71
Lecture_files/libs/revealjs/plugin/highlight/monokai.css
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
Monokai style - ported by Luigi Maselli - http://grigio.org
|
||||
*/
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding: 0.5em;
|
||||
background: #272822;
|
||||
color: #ddd;
|
||||
}
|
||||
|
||||
.hljs-tag,
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-literal,
|
||||
.hljs-strong,
|
||||
.hljs-name {
|
||||
color: #f92672;
|
||||
}
|
||||
|
||||
.hljs-code {
|
||||
color: #66d9ef;
|
||||
}
|
||||
|
||||
.hljs-class .hljs-title {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.hljs-attribute,
|
||||
.hljs-symbol,
|
||||
.hljs-regexp,
|
||||
.hljs-link {
|
||||
color: #bf79db;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-bullet,
|
||||
.hljs-subst,
|
||||
.hljs-title,
|
||||
.hljs-section,
|
||||
.hljs-emphasis,
|
||||
.hljs-type,
|
||||
.hljs-built_in,
|
||||
.hljs-builtin-name,
|
||||
.hljs-selector-attr,
|
||||
.hljs-selector-pseudo,
|
||||
.hljs-addition,
|
||||
.hljs-variable,
|
||||
.hljs-template-tag,
|
||||
.hljs-template-variable {
|
||||
color: #a6e22e;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-quote,
|
||||
.hljs-deletion,
|
||||
.hljs-meta {
|
||||
color: #75715e;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-literal,
|
||||
.hljs-doctag,
|
||||
.hljs-title,
|
||||
.hljs-section,
|
||||
.hljs-type,
|
||||
.hljs-selector-id {
|
||||
font-weight: bold;
|
||||
}
|
||||
439
Lecture_files/libs/revealjs/plugin/highlight/plugin.js
Normal file
@@ -0,0 +1,439 @@
|
||||
import hljs from 'highlight.js';
|
||||
|
||||
/* highlightjs-line-numbers.js 2.8.0 | (C) 2018 Yauheni Pakala | MIT License | github.com/wcoder/highlightjs-line-numbers.js */
|
||||
!function(r,o){"use strict";var e,i="hljs-ln",l="hljs-ln-line",h="hljs-ln-code",s="hljs-ln-numbers",c="hljs-ln-n",m="data-line-number",a=/\r\n|\r|\n/g;function u(e){for(var n=e.toString(),t=e.anchorNode;"TD"!==t.nodeName;)t=t.parentNode;for(var r=e.focusNode;"TD"!==r.nodeName;)r=r.parentNode;var o=parseInt(t.dataset.lineNumber),a=parseInt(r.dataset.lineNumber);if(o==a)return n;var i,l=t.textContent,s=r.textContent;for(a<o&&(i=o,o=a,a=i,i=l,l=s,s=i);0!==n.indexOf(l);)l=l.slice(1);for(;-1===n.lastIndexOf(s);)s=s.slice(0,-1);for(var c=l,u=function(e){for(var n=e;"TABLE"!==n.nodeName;)n=n.parentNode;return n}(t),d=o+1;d<a;++d){var f=p('.{0}[{1}="{2}"]',[h,m,d]);c+="\n"+u.querySelector(f).textContent}return c+="\n"+s}function n(e){try{var n=o.querySelectorAll("code.hljs,code.nohighlight");for(var t in n)n.hasOwnProperty(t)&&(n[t].classList.contains("nohljsln")||d(n[t],e))}catch(e){r.console.error("LineNumbers error: ",e)}}function d(e,n){if("object"==typeof e)e.innerHTML=f(e,n)}function f(e,n){var t,r,o=(t=e,{singleLine:function(e){return!!e.singleLine&&e.singleLine}(r=(r=n)||{}),startFrom:function(e,n){var t=1;isFinite(n.startFrom)&&(t=n.startFrom);var r=function(e,n){return e.hasAttribute(n)?e.getAttribute(n):null}(e,"data-ln-start-from");return null!==r&&(t=function(e,n){if(!e)return n;var t=Number(e);return isFinite(t)?t:n}(r,1)),t}(t,r)});return function e(n){var t=n.childNodes;for(var r in t){var o;t.hasOwnProperty(r)&&(o=t[r],0<(o.textContent.trim().match(a)||[]).length&&(0<o.childNodes.length?e(o):v(o.parentNode)))}}(e),function(e,n){var t=g(e);""===t[t.length-1].trim()&&t.pop();if(1<t.length||n.singleLine){for(var r="",o=0,a=t.length;o<a;o++)r+=p('<tr><td class="{0} {1}" {3}="{5}"><div class="{2}" {3}="{5}"></div></td><td class="{0} {4}" {3}="{5}">{6}</td></tr>',[l,s,c,m,h,o+n.startFrom,0<t[o].length?t[o]:" "]);return p('<table class="{0}">{1}</table>',[i,r])}return e}(e.innerHTML,o)}function v(e){var n=e.className;if(/hljs-/.test(n)){for(var t=g(e.innerHTML),r=0,o="";r<t.length;r++){o+=p('<span class="{0}">{1}</span>\n',[n,0<t[r].length?t[r]:" "])}e.innerHTML=o.trim()}}function g(e){return 0===e.length?[]:e.split(a)}function p(e,t){return e.replace(/\{(\d+)\}/g,function(e,n){return void 0!==t[n]?t[n]:e})}hljs?(hljs.initLineNumbersOnLoad=function(e){"interactive"===o.readyState||"complete"===o.readyState?n(e):r.addEventListener("DOMContentLoaded",function(){n(e)})},hljs.lineNumbersBlock=d,hljs.lineNumbersValue=function(e,n){if("string"!=typeof e)return;var t=document.createElement("code");return t.innerHTML=e,f(t,n)},(e=o.createElement("style")).type="text/css",e.innerHTML=p(".{0}{border-collapse:collapse}.{0} td{padding:0}.{1}:before{content:attr({2})}",[i,c,m]),o.getElementsByTagName("head")[0].appendChild(e)):r.console.error("highlight.js not detected!"),document.addEventListener("copy",function(e){var n,t=window.getSelection();!function(e){for(var n=e;n;){if(n.className&&-1!==n.className.indexOf("hljs-ln-code"))return 1;n=n.parentNode}}(t.anchorNode)||(n=-1!==window.navigator.userAgent.indexOf("Edge")?u(t):t.toString(),e.clipboardData.setData("text/plain",n),e.preventDefault())})}(window,document);
|
||||
|
||||
|
||||
/*!
|
||||
* reveal.js plugin that adds syntax highlight support.
|
||||
*/
|
||||
|
||||
const Plugin = {
|
||||
|
||||
id: 'highlight',
|
||||
|
||||
HIGHLIGHT_STEP_DELIMITER: '|',
|
||||
HIGHLIGHT_LINE_DELIMITER: ',',
|
||||
HIGHLIGHT_LINE_RANGE_DELIMITER: '-',
|
||||
|
||||
hljs,
|
||||
|
||||
/**
|
||||
* Highlights code blocks withing the given deck.
|
||||
*
|
||||
* Note that this can be called multiple times if
|
||||
* there are multiple presentations on one page.
|
||||
*
|
||||
* @param {Reveal} reveal the reveal.js instance
|
||||
*/
|
||||
init: function( reveal ) {
|
||||
|
||||
// Read the plugin config options and provide fallbacks
|
||||
let config = reveal.getConfig().highlight || {};
|
||||
|
||||
config.highlightOnLoad = typeof config.highlightOnLoad === 'boolean' ? config.highlightOnLoad : true;
|
||||
config.escapeHTML = typeof config.escapeHTML === 'boolean' ? config.escapeHTML : true;
|
||||
|
||||
Array.from( reveal.getRevealElement().querySelectorAll( 'pre code' ) ).forEach( block => {
|
||||
|
||||
block.parentNode.classList.add('code-wrapper');
|
||||
|
||||
// Code can optionally be wrapped in script template to avoid
|
||||
// HTML being parsed by the browser (i.e. when you need to
|
||||
// include <, > or & in your code).
|
||||
let substitute = block.querySelector( 'script[type="text/template"]' );
|
||||
if( substitute ) {
|
||||
// textContent handles the HTML entity escapes for us
|
||||
block.textContent = substitute.innerHTML;
|
||||
}
|
||||
|
||||
// Trim whitespace if the "data-trim" attribute is present
|
||||
if( block.hasAttribute( 'data-trim' ) && typeof block.innerHTML.trim === 'function' ) {
|
||||
block.innerHTML = betterTrim( block );
|
||||
}
|
||||
|
||||
// Escape HTML tags unless the "data-noescape" attrbute is present
|
||||
if( config.escapeHTML && !block.hasAttribute( 'data-noescape' )) {
|
||||
block.innerHTML = block.innerHTML.replace( /</g,"<").replace(/>/g, '>' );
|
||||
}
|
||||
|
||||
// Re-highlight when focus is lost (for contenteditable code)
|
||||
block.addEventListener( 'focusout', function( event ) {
|
||||
hljs.highlightElement( event.currentTarget );
|
||||
}, false );
|
||||
|
||||
} );
|
||||
|
||||
// Triggers a callback function before we trigger highlighting
|
||||
if( typeof config.beforeHighlight === 'function' ) {
|
||||
config.beforeHighlight( hljs );
|
||||
}
|
||||
|
||||
// Run initial highlighting for all code
|
||||
if( config.highlightOnLoad ) {
|
||||
Array.from( reveal.getRevealElement().querySelectorAll( 'pre code' ) ).forEach( block => {
|
||||
Plugin.highlightBlock( block );
|
||||
} );
|
||||
}
|
||||
|
||||
// If we're printing to PDF, scroll the code highlights of
|
||||
// all blocks in the deck into view at once
|
||||
reveal.on( 'pdf-ready', function() {
|
||||
[].slice.call( reveal.getRevealElement().querySelectorAll( 'pre code[data-line-numbers].current-fragment' ) ).forEach( function( block ) {
|
||||
Plugin.scrollHighlightedLineIntoView( block, {}, true );
|
||||
} );
|
||||
} );
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Highlights a code block. If the <code> node has the
|
||||
* 'data-line-numbers' attribute we also generate slide
|
||||
* numbers.
|
||||
*
|
||||
* If the block contains multiple line highlight steps,
|
||||
* we clone the block and create a fragment for each step.
|
||||
*/
|
||||
highlightBlock: function( block ) {
|
||||
|
||||
hljs.highlightElement( block );
|
||||
|
||||
// Don't generate line numbers for empty code blocks
|
||||
if( block.innerHTML.trim().length === 0 ) return;
|
||||
|
||||
if( block.hasAttribute( 'data-line-numbers' ) ) {
|
||||
hljs.lineNumbersBlock( block, { singleLine: true } );
|
||||
|
||||
var scrollState = { currentBlock: block };
|
||||
|
||||
// If there is more than one highlight step, generate
|
||||
// fragments
|
||||
var highlightSteps = Plugin.deserializeHighlightSteps( block.getAttribute( 'data-line-numbers' ) );
|
||||
if( highlightSteps.length > 1 ) {
|
||||
|
||||
// If the original code block has a fragment-index,
|
||||
// each clone should follow in an incremental sequence
|
||||
var fragmentIndex = parseInt( block.getAttribute( 'data-fragment-index' ), 10 );
|
||||
|
||||
if( typeof fragmentIndex !== 'number' || isNaN( fragmentIndex ) ) {
|
||||
fragmentIndex = null;
|
||||
}
|
||||
|
||||
// Generate fragments for all steps except the original block
|
||||
highlightSteps.slice(1).forEach( function( highlight ) {
|
||||
|
||||
var fragmentBlock = block.cloneNode( true );
|
||||
fragmentBlock.setAttribute( 'data-line-numbers', Plugin.serializeHighlightSteps( [ highlight ] ) );
|
||||
fragmentBlock.classList.add( 'fragment' );
|
||||
block.parentNode.appendChild( fragmentBlock );
|
||||
Plugin.highlightLines( fragmentBlock );
|
||||
|
||||
if( typeof fragmentIndex === 'number' ) {
|
||||
fragmentBlock.setAttribute( 'data-fragment-index', fragmentIndex );
|
||||
fragmentIndex += 1;
|
||||
}
|
||||
else {
|
||||
fragmentBlock.removeAttribute( 'data-fragment-index' );
|
||||
}
|
||||
|
||||
// Scroll highlights into view as we step through them
|
||||
fragmentBlock.addEventListener( 'visible', Plugin.scrollHighlightedLineIntoView.bind( Plugin, fragmentBlock, scrollState ) );
|
||||
fragmentBlock.addEventListener( 'hidden', Plugin.scrollHighlightedLineIntoView.bind( Plugin, fragmentBlock.previousSibling, scrollState ) );
|
||||
|
||||
} );
|
||||
|
||||
block.removeAttribute( 'data-fragment-index' );
|
||||
block.setAttribute( 'data-line-numbers', Plugin.serializeHighlightSteps( [ highlightSteps[0] ] ) );
|
||||
|
||||
}
|
||||
|
||||
// Scroll the first highlight into view when the slide
|
||||
// becomes visible. Note supported in IE11 since it lacks
|
||||
// support for Element.closest.
|
||||
var slide = typeof block.closest === 'function' ? block.closest( 'section:not(.stack)' ) : null;
|
||||
if( slide ) {
|
||||
var scrollFirstHighlightIntoView = function() {
|
||||
Plugin.scrollHighlightedLineIntoView( block, scrollState, true );
|
||||
slide.removeEventListener( 'visible', scrollFirstHighlightIntoView );
|
||||
}
|
||||
slide.addEventListener( 'visible', scrollFirstHighlightIntoView );
|
||||
}
|
||||
|
||||
Plugin.highlightLines( block );
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Animates scrolling to the first highlighted line
|
||||
* in the given code block.
|
||||
*/
|
||||
scrollHighlightedLineIntoView: function( block, scrollState, skipAnimation ) {
|
||||
|
||||
cancelAnimationFrame( scrollState.animationFrameID );
|
||||
|
||||
// Match the scroll position of the currently visible
|
||||
// code block
|
||||
if( scrollState.currentBlock ) {
|
||||
block.scrollTop = scrollState.currentBlock.scrollTop;
|
||||
}
|
||||
|
||||
// Remember the current code block so that we can match
|
||||
// its scroll position when showing/hiding fragments
|
||||
scrollState.currentBlock = block;
|
||||
|
||||
var highlightBounds = this.getHighlightedLineBounds( block )
|
||||
var viewportHeight = block.offsetHeight;
|
||||
|
||||
// Subtract padding from the viewport height
|
||||
var blockStyles = getComputedStyle( block );
|
||||
viewportHeight -= parseInt( blockStyles.paddingTop ) + parseInt( blockStyles.paddingBottom );
|
||||
|
||||
// Scroll position which centers all highlights
|
||||
var startTop = block.scrollTop;
|
||||
var targetTop = highlightBounds.top + ( Math.min( highlightBounds.bottom - highlightBounds.top, viewportHeight ) - viewportHeight ) / 2;
|
||||
|
||||
// Account for offsets in position applied to the
|
||||
// <table> that holds our lines of code
|
||||
var lineTable = block.querySelector( '.hljs-ln' );
|
||||
if( lineTable ) targetTop += lineTable.offsetTop - parseInt( blockStyles.paddingTop );
|
||||
|
||||
// Make sure the scroll target is within bounds
|
||||
targetTop = Math.max( Math.min( targetTop, block.scrollHeight - viewportHeight ), 0 );
|
||||
|
||||
if( skipAnimation === true || startTop === targetTop ) {
|
||||
block.scrollTop = targetTop;
|
||||
}
|
||||
else {
|
||||
|
||||
// Don't attempt to scroll if there is no overflow
|
||||
if( block.scrollHeight <= viewportHeight ) return;
|
||||
|
||||
var time = 0;
|
||||
var animate = function() {
|
||||
time = Math.min( time + 0.02, 1 );
|
||||
|
||||
// Update our eased scroll position
|
||||
block.scrollTop = startTop + ( targetTop - startTop ) * Plugin.easeInOutQuart( time );
|
||||
|
||||
// Keep animating unless we've reached the end
|
||||
if( time < 1 ) {
|
||||
scrollState.animationFrameID = requestAnimationFrame( animate );
|
||||
}
|
||||
};
|
||||
|
||||
animate();
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* The easing function used when scrolling.
|
||||
*/
|
||||
easeInOutQuart: function( t ) {
|
||||
|
||||
// easeInOutQuart
|
||||
return t<.5 ? 8*t*t*t*t : 1-8*(--t)*t*t*t;
|
||||
|
||||
},
|
||||
|
||||
getHighlightedLineBounds: function( block ) {
|
||||
|
||||
var highlightedLines = block.querySelectorAll( '.highlight-line' );
|
||||
if( highlightedLines.length === 0 ) {
|
||||
return { top: 0, bottom: 0 };
|
||||
}
|
||||
else {
|
||||
var firstHighlight = highlightedLines[0];
|
||||
var lastHighlight = highlightedLines[ highlightedLines.length -1 ];
|
||||
|
||||
return {
|
||||
top: firstHighlight.offsetTop,
|
||||
bottom: lastHighlight.offsetTop + lastHighlight.offsetHeight
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Visually emphasize specific lines within a code block.
|
||||
* This only works on blocks with line numbering turned on.
|
||||
*
|
||||
* @param {HTMLElement} block a <code> block
|
||||
* @param {String} [linesToHighlight] The lines that should be
|
||||
* highlighted in this format:
|
||||
* "1" = highlights line 1
|
||||
* "2,5" = highlights lines 2 & 5
|
||||
* "2,5-7" = highlights lines 2, 5, 6 & 7
|
||||
*/
|
||||
highlightLines: function( block, linesToHighlight ) {
|
||||
|
||||
var highlightSteps = Plugin.deserializeHighlightSteps( linesToHighlight || block.getAttribute( 'data-line-numbers' ) );
|
||||
|
||||
if( highlightSteps.length ) {
|
||||
|
||||
highlightSteps[0].forEach( function( highlight ) {
|
||||
|
||||
var elementsToHighlight = [];
|
||||
|
||||
// Highlight a range
|
||||
if( typeof highlight.end === 'number' ) {
|
||||
elementsToHighlight = [].slice.call( block.querySelectorAll( 'table tr:nth-child(n+'+highlight.start+'):nth-child(-n+'+highlight.end+')' ) );
|
||||
}
|
||||
// Highlight a single line
|
||||
else if( typeof highlight.start === 'number' ) {
|
||||
elementsToHighlight = [].slice.call( block.querySelectorAll( 'table tr:nth-child('+highlight.start+')' ) );
|
||||
}
|
||||
|
||||
if( elementsToHighlight.length ) {
|
||||
elementsToHighlight.forEach( function( lineElement ) {
|
||||
lineElement.classList.add( 'highlight-line' );
|
||||
} );
|
||||
|
||||
block.classList.add( 'has-highlights' );
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Parses and formats a user-defined string of line
|
||||
* numbers to highlight.
|
||||
*
|
||||
* @example
|
||||
* Plugin.deserializeHighlightSteps( '1,2|3,5-10' )
|
||||
* // [
|
||||
* // [ { start: 1 }, { start: 2 } ],
|
||||
* // [ { start: 3 }, { start: 5, end: 10 } ]
|
||||
* // ]
|
||||
*/
|
||||
deserializeHighlightSteps: function( highlightSteps ) {
|
||||
|
||||
// Remove whitespace
|
||||
highlightSteps = highlightSteps.replace( /\s/g, '' );
|
||||
|
||||
// Divide up our line number groups
|
||||
highlightSteps = highlightSteps.split( Plugin.HIGHLIGHT_STEP_DELIMITER );
|
||||
|
||||
return highlightSteps.map( function( highlights ) {
|
||||
|
||||
return highlights.split( Plugin.HIGHLIGHT_LINE_DELIMITER ).map( function( highlight ) {
|
||||
|
||||
// Parse valid line numbers
|
||||
if( /^[\d-]+$/.test( highlight ) ) {
|
||||
|
||||
highlight = highlight.split( Plugin.HIGHLIGHT_LINE_RANGE_DELIMITER );
|
||||
|
||||
var lineStart = parseInt( highlight[0], 10 ),
|
||||
lineEnd = parseInt( highlight[1], 10 );
|
||||
|
||||
if( isNaN( lineEnd ) ) {
|
||||
return {
|
||||
start: lineStart
|
||||
};
|
||||
}
|
||||
else {
|
||||
return {
|
||||
start: lineStart,
|
||||
end: lineEnd
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
// If no line numbers are provided, no code will be highlighted
|
||||
else {
|
||||
|
||||
return {};
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Serializes parsed line number data into a string so
|
||||
* that we can store it in the DOM.
|
||||
*/
|
||||
serializeHighlightSteps: function( highlightSteps ) {
|
||||
|
||||
return highlightSteps.map( function( highlights ) {
|
||||
|
||||
return highlights.map( function( highlight ) {
|
||||
|
||||
// Line range
|
||||
if( typeof highlight.end === 'number' ) {
|
||||
return highlight.start + Plugin.HIGHLIGHT_LINE_RANGE_DELIMITER + highlight.end;
|
||||
}
|
||||
// Single line
|
||||
else if( typeof highlight.start === 'number' ) {
|
||||
return highlight.start;
|
||||
}
|
||||
// All lines
|
||||
else {
|
||||
return '';
|
||||
}
|
||||
|
||||
} ).join( Plugin.HIGHLIGHT_LINE_DELIMITER );
|
||||
|
||||
} ).join( Plugin.HIGHLIGHT_STEP_DELIMITER );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Function to perform a better "data-trim" on code snippets
|
||||
// Will slice an indentation amount on each line of the snippet (amount based on the line having the lowest indentation length)
|
||||
function betterTrim(snippetEl) {
|
||||
// Helper functions
|
||||
function trimLeft(val) {
|
||||
// Adapted from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/Trim#Polyfill
|
||||
return val.replace(/^[\s\uFEFF\xA0]+/g, '');
|
||||
}
|
||||
function trimLineBreaks(input) {
|
||||
var lines = input.split('\n');
|
||||
|
||||
// Trim line-breaks from the beginning
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
if (lines[i].trim() === '') {
|
||||
lines.splice(i--, 1);
|
||||
} else break;
|
||||
}
|
||||
|
||||
// Trim line-breaks from the end
|
||||
for (var i = lines.length-1; i >= 0; i--) {
|
||||
if (lines[i].trim() === '') {
|
||||
lines.splice(i, 1);
|
||||
} else break;
|
||||
}
|
||||
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
// Main function for betterTrim()
|
||||
return (function(snippetEl) {
|
||||
var content = trimLineBreaks(snippetEl.innerHTML);
|
||||
var lines = content.split('\n');
|
||||
// Calculate the minimum amount to remove on each line start of the snippet (can be 0)
|
||||
var pad = lines.reduce(function(acc, line) {
|
||||
if (line.length > 0 && trimLeft(line).length > 0 && acc > line.length - trimLeft(line).length) {
|
||||
return line.length - trimLeft(line).length;
|
||||
}
|
||||
return acc;
|
||||
}, Number.POSITIVE_INFINITY);
|
||||
// Slice each line with this amount
|
||||
return lines.map(function(line, index) {
|
||||
return line.slice(pad);
|
||||
})
|
||||
.join('\n');
|
||||
})(snippetEl);
|
||||
}
|
||||
|
||||
export default () => Plugin;
|
||||
80
Lecture_files/libs/revealjs/plugin/highlight/zenburn.css
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
|
||||
Zenburn style from voldmar.ru (c) Vladimir Epifanov <voldmar@voldmar.ru>
|
||||
based on dark.css by Ivan Sagalaev
|
||||
|
||||
*/
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding: 0.5em;
|
||||
background: #3f3f3f;
|
||||
color: #dcdcdc;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-tag {
|
||||
color: #e3ceab;
|
||||
}
|
||||
|
||||
.hljs-template-tag {
|
||||
color: #dcdcdc;
|
||||
}
|
||||
|
||||
.hljs-number {
|
||||
color: #8cd0d3;
|
||||
}
|
||||
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-attribute {
|
||||
color: #efdcbc;
|
||||
}
|
||||
|
||||
.hljs-literal {
|
||||
color: #efefaf;
|
||||
}
|
||||
|
||||
.hljs-subst {
|
||||
color: #8f8f8f;
|
||||
}
|
||||
|
||||
.hljs-title,
|
||||
.hljs-name,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class,
|
||||
.hljs-section,
|
||||
.hljs-type {
|
||||
color: #efef8f;
|
||||
}
|
||||
|
||||
.hljs-symbol,
|
||||
.hljs-bullet,
|
||||
.hljs-link {
|
||||
color: #dca3a3;
|
||||
}
|
||||
|
||||
.hljs-deletion,
|
||||
.hljs-string,
|
||||
.hljs-built_in,
|
||||
.hljs-builtin-name {
|
||||
color: #cc9393;
|
||||
}
|
||||
|
||||
.hljs-addition,
|
||||
.hljs-comment,
|
||||
.hljs-quote,
|
||||
.hljs-meta {
|
||||
color: #7f9f7f;
|
||||
}
|
||||
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
1
Lecture_files/libs/revealjs/plugin/markdown/markdown.js
Normal file
475
Lecture_files/libs/revealjs/plugin/markdown/plugin.js
Executable file
@@ -0,0 +1,475 @@
|
||||
/*!
|
||||
* The reveal.js markdown plugin. Handles parsing of
|
||||
* markdown inside of presentations as well as loading
|
||||
* of external markdown documents.
|
||||
*/
|
||||
|
||||
import { marked } from 'marked';
|
||||
|
||||
const DEFAULT_SLIDE_SEPARATOR = '\r?\n---\r?\n',
|
||||
DEFAULT_NOTES_SEPARATOR = 'notes?:',
|
||||
DEFAULT_ELEMENT_ATTRIBUTES_SEPARATOR = '\\\.element\\\s*?(.+?)$',
|
||||
DEFAULT_SLIDE_ATTRIBUTES_SEPARATOR = '\\\.slide:\\\s*?(\\\S.+?)$';
|
||||
|
||||
const SCRIPT_END_PLACEHOLDER = '__SCRIPT_END__';
|
||||
|
||||
const CODE_LINE_NUMBER_REGEX = /\[([\s\d,|-]*)\]/;
|
||||
|
||||
const HTML_ESCAPE_MAP = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
"'": '''
|
||||
};
|
||||
|
||||
const Plugin = () => {
|
||||
|
||||
// The reveal.js instance this plugin is attached to
|
||||
let deck;
|
||||
|
||||
/**
|
||||
* Retrieves the markdown contents of a slide section
|
||||
* element. Normalizes leading tabs/whitespace.
|
||||
*/
|
||||
function getMarkdownFromSlide( section ) {
|
||||
|
||||
// look for a <script> or <textarea data-template> wrapper
|
||||
var template = section.querySelector( '[data-template]' ) || section.querySelector( 'script' );
|
||||
|
||||
// strip leading whitespace so it isn't evaluated as code
|
||||
var text = ( template || section ).textContent;
|
||||
|
||||
// restore script end tags
|
||||
text = text.replace( new RegExp( SCRIPT_END_PLACEHOLDER, 'g' ), '</script>' );
|
||||
|
||||
var leadingWs = text.match( /^\n?(\s*)/ )[1].length,
|
||||
leadingTabs = text.match( /^\n?(\t*)/ )[1].length;
|
||||
|
||||
if( leadingTabs > 0 ) {
|
||||
text = text.replace( new RegExp('\\n?\\t{' + leadingTabs + '}','g'), '\n' );
|
||||
}
|
||||
else if( leadingWs > 1 ) {
|
||||
text = text.replace( new RegExp('\\n? {' + leadingWs + '}', 'g'), '\n' );
|
||||
}
|
||||
|
||||
return text;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a markdown slide section element, this will
|
||||
* return all arguments that aren't related to markdown
|
||||
* parsing. Used to forward any other user-defined arguments
|
||||
* to the output markdown slide.
|
||||
*/
|
||||
function getForwardedAttributes( section ) {
|
||||
|
||||
var attributes = section.attributes;
|
||||
var result = [];
|
||||
|
||||
for( var i = 0, len = attributes.length; i < len; i++ ) {
|
||||
var name = attributes[i].name,
|
||||
value = attributes[i].value;
|
||||
|
||||
// disregard attributes that are used for markdown loading/parsing
|
||||
if( /data\-(markdown|separator|vertical|notes)/gi.test( name ) ) continue;
|
||||
|
||||
if( value ) {
|
||||
result.push( name + '="' + value + '"' );
|
||||
}
|
||||
else {
|
||||
result.push( name );
|
||||
}
|
||||
}
|
||||
|
||||
return result.join( ' ' );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Inspects the given options and fills out default
|
||||
* values for what's not defined.
|
||||
*/
|
||||
function getSlidifyOptions( options ) {
|
||||
|
||||
options = options || {};
|
||||
options.separator = options.separator || DEFAULT_SLIDE_SEPARATOR;
|
||||
options.notesSeparator = options.notesSeparator || DEFAULT_NOTES_SEPARATOR;
|
||||
options.attributes = options.attributes || '';
|
||||
|
||||
return options;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for constructing a markdown slide.
|
||||
*/
|
||||
function createMarkdownSlide( content, options ) {
|
||||
|
||||
options = getSlidifyOptions( options );
|
||||
|
||||
var notesMatch = content.split( new RegExp( options.notesSeparator, 'mgi' ) );
|
||||
|
||||
if( notesMatch.length === 2 ) {
|
||||
content = notesMatch[0] + '<aside class="notes">' + marked(notesMatch[1].trim()) + '</aside>';
|
||||
}
|
||||
|
||||
// prevent script end tags in the content from interfering
|
||||
// with parsing
|
||||
content = content.replace( /<\/script>/g, SCRIPT_END_PLACEHOLDER );
|
||||
|
||||
return '<script type="text/template">' + content + '</script>';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a data string into multiple slides based
|
||||
* on the passed in separator arguments.
|
||||
*/
|
||||
function slidify( markdown, options ) {
|
||||
|
||||
options = getSlidifyOptions( options );
|
||||
|
||||
var separatorRegex = new RegExp( options.separator + ( options.verticalSeparator ? '|' + options.verticalSeparator : '' ), 'mg' ),
|
||||
horizontalSeparatorRegex = new RegExp( options.separator );
|
||||
|
||||
var matches,
|
||||
lastIndex = 0,
|
||||
isHorizontal,
|
||||
wasHorizontal = true,
|
||||
content,
|
||||
sectionStack = [];
|
||||
|
||||
// iterate until all blocks between separators are stacked up
|
||||
while( matches = separatorRegex.exec( markdown ) ) {
|
||||
var notes = null;
|
||||
|
||||
// determine direction (horizontal by default)
|
||||
isHorizontal = horizontalSeparatorRegex.test( matches[0] );
|
||||
|
||||
if( !isHorizontal && wasHorizontal ) {
|
||||
// create vertical stack
|
||||
sectionStack.push( [] );
|
||||
}
|
||||
|
||||
// pluck slide content from markdown input
|
||||
content = markdown.substring( lastIndex, matches.index );
|
||||
|
||||
if( isHorizontal && wasHorizontal ) {
|
||||
// add to horizontal stack
|
||||
sectionStack.push( content );
|
||||
}
|
||||
else {
|
||||
// add to vertical stack
|
||||
sectionStack[sectionStack.length-1].push( content );
|
||||
}
|
||||
|
||||
lastIndex = separatorRegex.lastIndex;
|
||||
wasHorizontal = isHorizontal;
|
||||
}
|
||||
|
||||
// add the remaining slide
|
||||
( wasHorizontal ? sectionStack : sectionStack[sectionStack.length-1] ).push( markdown.substring( lastIndex ) );
|
||||
|
||||
var markdownSections = '';
|
||||
|
||||
// flatten the hierarchical stack, and insert <section data-markdown> tags
|
||||
for( var i = 0, len = sectionStack.length; i < len; i++ ) {
|
||||
// vertical
|
||||
if( sectionStack[i] instanceof Array ) {
|
||||
markdownSections += '<section '+ options.attributes +'>';
|
||||
|
||||
sectionStack[i].forEach( function( child ) {
|
||||
markdownSections += '<section data-markdown>' + createMarkdownSlide( child, options ) + '</section>';
|
||||
} );
|
||||
|
||||
markdownSections += '</section>';
|
||||
}
|
||||
else {
|
||||
markdownSections += '<section '+ options.attributes +' data-markdown>' + createMarkdownSlide( sectionStack[i], options ) + '</section>';
|
||||
}
|
||||
}
|
||||
|
||||
return markdownSections;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses any current data-markdown slides, splits
|
||||
* multi-slide markdown into separate sections and
|
||||
* handles loading of external markdown.
|
||||
*/
|
||||
function processSlides( scope ) {
|
||||
|
||||
return new Promise( function( resolve ) {
|
||||
|
||||
var externalPromises = [];
|
||||
|
||||
[].slice.call( scope.querySelectorAll( 'section[data-markdown]:not([data-markdown-parsed])') ).forEach( function( section, i ) {
|
||||
|
||||
if( section.getAttribute( 'data-markdown' ).length ) {
|
||||
|
||||
externalPromises.push( loadExternalMarkdown( section ).then(
|
||||
|
||||
// Finished loading external file
|
||||
function( xhr, url ) {
|
||||
section.outerHTML = slidify( xhr.responseText, {
|
||||
separator: section.getAttribute( 'data-separator' ),
|
||||
verticalSeparator: section.getAttribute( 'data-separator-vertical' ),
|
||||
notesSeparator: section.getAttribute( 'data-separator-notes' ),
|
||||
attributes: getForwardedAttributes( section )
|
||||
});
|
||||
},
|
||||
|
||||
// Failed to load markdown
|
||||
function( xhr, url ) {
|
||||
section.outerHTML = '<section data-state="alert">' +
|
||||
'ERROR: The attempt to fetch ' + url + ' failed with HTTP status ' + xhr.status + '.' +
|
||||
'Check your browser\'s JavaScript console for more details.' +
|
||||
'<p>Remember that you need to serve the presentation HTML from a HTTP server.</p>' +
|
||||
'</section>';
|
||||
}
|
||||
|
||||
) );
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
section.outerHTML = slidify( getMarkdownFromSlide( section ), {
|
||||
separator: section.getAttribute( 'data-separator' ),
|
||||
verticalSeparator: section.getAttribute( 'data-separator-vertical' ),
|
||||
notesSeparator: section.getAttribute( 'data-separator-notes' ),
|
||||
attributes: getForwardedAttributes( section )
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Promise.all( externalPromises ).then( resolve );
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
function loadExternalMarkdown( section ) {
|
||||
|
||||
return new Promise( function( resolve, reject ) {
|
||||
|
||||
var xhr = new XMLHttpRequest(),
|
||||
url = section.getAttribute( 'data-markdown' );
|
||||
|
||||
var datacharset = section.getAttribute( 'data-charset' );
|
||||
|
||||
// see https://developer.mozilla.org/en-US/docs/Web/API/element.getAttribute#Notes
|
||||
if( datacharset != null && datacharset != '' ) {
|
||||
xhr.overrideMimeType( 'text/html; charset=' + datacharset );
|
||||
}
|
||||
|
||||
xhr.onreadystatechange = function( section, xhr ) {
|
||||
if( xhr.readyState === 4 ) {
|
||||
// file protocol yields status code 0 (useful for local debug, mobile applications etc.)
|
||||
if ( ( xhr.status >= 200 && xhr.status < 300 ) || xhr.status === 0 ) {
|
||||
|
||||
resolve( xhr, url );
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
reject( xhr, url );
|
||||
|
||||
}
|
||||
}
|
||||
}.bind( this, section, xhr );
|
||||
|
||||
xhr.open( 'GET', url, true );
|
||||
|
||||
try {
|
||||
xhr.send();
|
||||
}
|
||||
catch ( e ) {
|
||||
console.warn( 'Failed to get the Markdown file ' + url + '. Make sure that the presentation and the file are served by a HTTP server and the file can be found there. ' + e );
|
||||
resolve( xhr, url );
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a node value has the attributes pattern.
|
||||
* If yes, extract it and add that value as one or several attributes
|
||||
* to the target element.
|
||||
*
|
||||
* You need Cache Killer on Chrome to see the effect on any FOM transformation
|
||||
* directly on refresh (F5)
|
||||
* http://stackoverflow.com/questions/5690269/disabling-chrome-cache-for-website-development/7000899#answer-11786277
|
||||
*/
|
||||
function addAttributeInElement( node, elementTarget, separator ) {
|
||||
|
||||
var mardownClassesInElementsRegex = new RegExp( separator, 'mg' );
|
||||
var mardownClassRegex = new RegExp( "([^\"= ]+?)=\"([^\"]+?)\"|(data-[^\"= ]+?)(?=[\" ])", 'mg' );
|
||||
var nodeValue = node.nodeValue;
|
||||
var matches,
|
||||
matchesClass;
|
||||
if( matches = mardownClassesInElementsRegex.exec( nodeValue ) ) {
|
||||
|
||||
var classes = matches[1];
|
||||
nodeValue = nodeValue.substring( 0, matches.index ) + nodeValue.substring( mardownClassesInElementsRegex.lastIndex );
|
||||
node.nodeValue = nodeValue;
|
||||
while( matchesClass = mardownClassRegex.exec( classes ) ) {
|
||||
if( matchesClass[2] ) {
|
||||
elementTarget.setAttribute( matchesClass[1], matchesClass[2] );
|
||||
} else {
|
||||
elementTarget.setAttribute( matchesClass[3], "" );
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add attributes to the parent element of a text node,
|
||||
* or the element of an attribute node.
|
||||
*/
|
||||
function addAttributes( section, element, previousElement, separatorElementAttributes, separatorSectionAttributes ) {
|
||||
|
||||
if ( element != null && element.childNodes != undefined && element.childNodes.length > 0 ) {
|
||||
var previousParentElement = element;
|
||||
for( var i = 0; i < element.childNodes.length; i++ ) {
|
||||
var childElement = element.childNodes[i];
|
||||
if ( i > 0 ) {
|
||||
var j = i - 1;
|
||||
while ( j >= 0 ) {
|
||||
var aPreviousChildElement = element.childNodes[j];
|
||||
if ( typeof aPreviousChildElement.setAttribute == 'function' && aPreviousChildElement.tagName != "BR" ) {
|
||||
previousParentElement = aPreviousChildElement;
|
||||
break;
|
||||
}
|
||||
j = j - 1;
|
||||
}
|
||||
}
|
||||
var parentSection = section;
|
||||
if( childElement.nodeName == "section" ) {
|
||||
parentSection = childElement ;
|
||||
previousParentElement = childElement ;
|
||||
}
|
||||
if ( typeof childElement.setAttribute == 'function' || childElement.nodeType == Node.COMMENT_NODE ) {
|
||||
addAttributes( parentSection, childElement, previousParentElement, separatorElementAttributes, separatorSectionAttributes );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( element.nodeType == Node.COMMENT_NODE ) {
|
||||
if ( addAttributeInElement( element, previousElement, separatorElementAttributes ) == false ) {
|
||||
addAttributeInElement( element, section, separatorSectionAttributes );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts any current data-markdown slides in the
|
||||
* DOM to HTML.
|
||||
*/
|
||||
function convertSlides() {
|
||||
|
||||
var sections = deck.getRevealElement().querySelectorAll( '[data-markdown]:not([data-markdown-parsed])');
|
||||
|
||||
[].slice.call( sections ).forEach( function( section ) {
|
||||
|
||||
section.setAttribute( 'data-markdown-parsed', true )
|
||||
|
||||
var notes = section.querySelector( 'aside.notes' );
|
||||
var markdown = getMarkdownFromSlide( section );
|
||||
|
||||
section.innerHTML = marked( markdown );
|
||||
addAttributes( section, section, null, section.getAttribute( 'data-element-attributes' ) ||
|
||||
section.parentNode.getAttribute( 'data-element-attributes' ) ||
|
||||
DEFAULT_ELEMENT_ATTRIBUTES_SEPARATOR,
|
||||
section.getAttribute( 'data-attributes' ) ||
|
||||
section.parentNode.getAttribute( 'data-attributes' ) ||
|
||||
DEFAULT_SLIDE_ATTRIBUTES_SEPARATOR);
|
||||
|
||||
// If there were notes, we need to re-add them after
|
||||
// having overwritten the section's HTML
|
||||
if( notes ) {
|
||||
section.appendChild( notes );
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
return Promise.resolve();
|
||||
|
||||
}
|
||||
|
||||
function escapeForHTML( input ) {
|
||||
|
||||
return input.replace( /([&<>'"])/g, char => HTML_ESCAPE_MAP[char] );
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
id: 'markdown',
|
||||
|
||||
/**
|
||||
* Starts processing and converting Markdown within the
|
||||
* current reveal.js deck.
|
||||
*/
|
||||
init: function( reveal ) {
|
||||
|
||||
deck = reveal;
|
||||
|
||||
let { renderer, animateLists, ...markedOptions } = deck.getConfig().markdown || {};
|
||||
|
||||
if( !renderer ) {
|
||||
renderer = new marked.Renderer();
|
||||
|
||||
renderer.code = ( code, language ) => {
|
||||
|
||||
// Off by default
|
||||
let lineNumbers = '';
|
||||
|
||||
// Users can opt in to show line numbers and highlight
|
||||
// specific lines.
|
||||
// ```javascript [] show line numbers
|
||||
// ```javascript [1,4-8] highlights lines 1 and 4-8
|
||||
if( CODE_LINE_NUMBER_REGEX.test( language ) ) {
|
||||
lineNumbers = language.match( CODE_LINE_NUMBER_REGEX )[1].trim();
|
||||
lineNumbers = `data-line-numbers="${lineNumbers}"`;
|
||||
language = language.replace( CODE_LINE_NUMBER_REGEX, '' ).trim();
|
||||
}
|
||||
|
||||
// Escape before this gets injected into the DOM to
|
||||
// avoid having the HTML parser alter our code before
|
||||
// highlight.js is able to read it
|
||||
code = escapeForHTML( code );
|
||||
|
||||
return `<pre><code ${lineNumbers} class="${language}">${code}</code></pre>`;
|
||||
};
|
||||
}
|
||||
|
||||
if( animateLists === true ) {
|
||||
renderer.listitem = text => `<li class="fragment">${text}</li>`;
|
||||
}
|
||||
|
||||
marked.setOptions( {
|
||||
renderer,
|
||||
...markedOptions
|
||||
} );
|
||||
|
||||
return processSlides( deck.getRevealElement() ).then( convertSlides );
|
||||
|
||||
},
|
||||
|
||||
// TODO: Do these belong in the API?
|
||||
processSlides: processSlides,
|
||||
convertSlides: convertSlides,
|
||||
slidify: slidify,
|
||||
marked: marked
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
export default Plugin;
|
||||
96
Lecture_files/libs/revealjs/plugin/math/katex.js
Executable file
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* A plugin which enables rendering of math equations inside
|
||||
* of reveal.js slides. Essentially a thin wrapper for KaTeX.
|
||||
*
|
||||
* @author Hakim El Hattab
|
||||
* @author Gerhard Burger
|
||||
*/
|
||||
export const KaTeX = () => {
|
||||
let deck;
|
||||
|
||||
let defaultOptions = {
|
||||
version: 'latest',
|
||||
delimiters: [
|
||||
{left: '$$', right: '$$', display: true}, // Note: $$ has to come before $
|
||||
{left: '$', right: '$', display: false},
|
||||
{left: '\\(', right: '\\)', display: false},
|
||||
{left: '\\[', right: '\\]', display: true}
|
||||
],
|
||||
ignoredTags: ['script', 'noscript', 'style', 'textarea', 'pre']
|
||||
}
|
||||
|
||||
const loadCss = src => {
|
||||
let link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.href = src;
|
||||
document.head.appendChild(link);
|
||||
};
|
||||
|
||||
/**
|
||||
* Loads a JavaScript file and returns a Promise for when it is loaded
|
||||
* Credits: https://aaronsmith.online/easily-load-an-external-script-using-javascript/
|
||||
*/
|
||||
const loadScript = src => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const script = document.createElement('script')
|
||||
script.type = 'text/javascript'
|
||||
script.onload = resolve
|
||||
script.onerror = reject
|
||||
script.src = src
|
||||
document.head.append(script)
|
||||
})
|
||||
};
|
||||
|
||||
async function loadScripts(urls) {
|
||||
for(const url of urls) {
|
||||
await loadScript(url);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
id: 'katex',
|
||||
|
||||
init: function (reveal) {
|
||||
|
||||
deck = reveal;
|
||||
|
||||
let revealOptions = deck.getConfig().katex || {};
|
||||
|
||||
let options = {...defaultOptions, ...revealOptions};
|
||||
const {local, version, extensions, ...katexOptions} = options;
|
||||
|
||||
let baseUrl = options.local || 'https://cdn.jsdelivr.net/npm/katex';
|
||||
let versionString = options.local ? '' : '@' + options.version;
|
||||
|
||||
let cssUrl = baseUrl + versionString + '/dist/katex.min.css';
|
||||
let katexUrl = baseUrl + versionString + '/dist/katex.min.js';
|
||||
let mhchemUrl = baseUrl + versionString + '/dist/contrib/mhchem.min.js'
|
||||
let karUrl = baseUrl + versionString + '/dist/contrib/auto-render.min.js';
|
||||
|
||||
let katexScripts = [katexUrl];
|
||||
if(options.extensions && options.extensions.includes("mhchem")) {
|
||||
katexScripts.push(mhchemUrl);
|
||||
}
|
||||
katexScripts.push(karUrl);
|
||||
|
||||
const renderMath = () => {
|
||||
renderMathInElement(reveal.getSlidesElement(), katexOptions);
|
||||
deck.layout();
|
||||
}
|
||||
|
||||
loadCss(cssUrl);
|
||||
|
||||
// For some reason dynamically loading with defer attribute doesn't result in the expected behavior, the below code does
|
||||
loadScripts(katexScripts).then(() => {
|
||||
if( deck.isReady() ) {
|
||||
renderMath();
|
||||
}
|
||||
else {
|
||||
deck.on( 'ready', renderMath.bind( this ) );
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
1
Lecture_files/libs/revealjs/plugin/math/math.esm.js
Normal file
1
Lecture_files/libs/revealjs/plugin/math/math.js
Normal file
89
Lecture_files/libs/revealjs/plugin/math/mathjax2.js
Normal file
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* A plugin which enables rendering of math equations inside
|
||||
* of reveal.js slides. Essentially a thin wrapper for MathJax.
|
||||
*
|
||||
* @author Hakim El Hattab
|
||||
*/
|
||||
export const MathJax2 = () => {
|
||||
|
||||
// The reveal.js instance this plugin is attached to
|
||||
let deck;
|
||||
|
||||
let defaultOptions = {
|
||||
messageStyle: 'none',
|
||||
tex2jax: {
|
||||
inlineMath: [ [ '$', '$' ], [ '\\(', '\\)' ] ],
|
||||
skipTags: [ 'script', 'noscript', 'style', 'textarea', 'pre' ]
|
||||
},
|
||||
skipStartupTypeset: true
|
||||
};
|
||||
|
||||
function loadScript( url, callback ) {
|
||||
|
||||
let head = document.querySelector( 'head' );
|
||||
let script = document.createElement( 'script' );
|
||||
script.type = 'text/javascript';
|
||||
script.src = url;
|
||||
|
||||
// Wrapper for callback to make sure it only fires once
|
||||
let finish = () => {
|
||||
if( typeof callback === 'function' ) {
|
||||
callback.call();
|
||||
callback = null;
|
||||
}
|
||||
}
|
||||
|
||||
script.onload = finish;
|
||||
|
||||
// IE
|
||||
script.onreadystatechange = () => {
|
||||
if ( this.readyState === 'loaded' ) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
// Normal browsers
|
||||
head.appendChild( script );
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
id: 'mathjax2',
|
||||
|
||||
init: function( reveal ) {
|
||||
|
||||
deck = reveal;
|
||||
|
||||
let revealOptions = deck.getConfig().mathjax2 || deck.getConfig().math || {};
|
||||
|
||||
let options = { ...defaultOptions, ...revealOptions };
|
||||
let mathjax = options.mathjax || 'https://cdn.jsdelivr.net/npm/mathjax@2/MathJax.js';
|
||||
let config = options.config || 'TeX-AMS_HTML-full';
|
||||
let url = mathjax + '?config=' + config;
|
||||
|
||||
options.tex2jax = { ...defaultOptions.tex2jax, ...revealOptions.tex2jax };
|
||||
|
||||
options.mathjax = options.config = null;
|
||||
|
||||
loadScript( url, function() {
|
||||
|
||||
MathJax.Hub.Config( options );
|
||||
|
||||
// Typeset followed by an immediate reveal.js layout since
|
||||
// the typesetting process could affect slide height
|
||||
MathJax.Hub.Queue( [ 'Typeset', MathJax.Hub, deck.getRevealElement() ] );
|
||||
MathJax.Hub.Queue( deck.layout );
|
||||
|
||||
// Reprocess equations in slides when they turn visible
|
||||
deck.on( 'slidechanged', function( event ) {
|
||||
|
||||
MathJax.Hub.Queue( [ 'Typeset', MathJax.Hub, event.currentSlide ] );
|
||||
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
77
Lecture_files/libs/revealjs/plugin/math/mathjax3.js
Normal file
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* A plugin which enables rendering of math equations inside
|
||||
* of reveal.js slides. Essentially a thin wrapper for MathJax 3
|
||||
*
|
||||
* @author Hakim El Hattab
|
||||
* @author Gerhard Burger
|
||||
*/
|
||||
export const MathJax3 = () => {
|
||||
|
||||
// The reveal.js instance this plugin is attached to
|
||||
let deck;
|
||||
|
||||
let defaultOptions = {
|
||||
tex: {
|
||||
inlineMath: [ [ '$', '$' ], [ '\\(', '\\)' ] ]
|
||||
},
|
||||
options: {
|
||||
skipHtmlTags: [ 'script', 'noscript', 'style', 'textarea', 'pre' ]
|
||||
},
|
||||
startup: {
|
||||
ready: () => {
|
||||
MathJax.startup.defaultReady();
|
||||
MathJax.startup.promise.then(() => {
|
||||
Reveal.layout();
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function loadScript( url, callback ) {
|
||||
|
||||
let script = document.createElement( 'script' );
|
||||
script.type = "text/javascript"
|
||||
script.id = "MathJax-script"
|
||||
script.src = url;
|
||||
script.async = true
|
||||
|
||||
// Wrapper for callback to make sure it only fires once
|
||||
script.onload = () => {
|
||||
if (typeof callback === 'function') {
|
||||
callback.call();
|
||||
callback = null;
|
||||
}
|
||||
};
|
||||
|
||||
document.head.appendChild( script );
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
id: 'mathjax3',
|
||||
init: function(reveal) {
|
||||
|
||||
deck = reveal;
|
||||
|
||||
let revealOptions = deck.getConfig().mathjax3 || {};
|
||||
let options = {...defaultOptions, ...revealOptions};
|
||||
options.tex = {...defaultOptions.tex, ...revealOptions.tex}
|
||||
options.options = {...defaultOptions.options, ...revealOptions.options}
|
||||
options.startup = {...defaultOptions.startup, ...revealOptions.startup}
|
||||
|
||||
let url = options.mathjax || 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js';
|
||||
options.mathjax = null;
|
||||
|
||||
window.MathJax = options;
|
||||
|
||||
loadScript( url, function() {
|
||||
// Reprocess equations in slides when they turn visible
|
||||
Reveal.addEventListener( 'slidechanged', function( event ) {
|
||||
MathJax.typeset();
|
||||
} );
|
||||
} );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
15
Lecture_files/libs/revealjs/plugin/math/plugin.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import {KaTeX} from "./katex";
|
||||
import {MathJax2} from "./mathjax2";
|
||||
import {MathJax3} from "./mathjax3";
|
||||
|
||||
const defaultTypesetter = MathJax2;
|
||||
|
||||
/*!
|
||||
* This plugin is a wrapper for the MathJax2,
|
||||
* MathJax3 and KaTeX typesetter plugins.
|
||||
*/
|
||||
export default Plugin = Object.assign( defaultTypesetter(), {
|
||||
KaTeX,
|
||||
MathJax2,
|
||||
MathJax3
|
||||
} );
|
||||
1
Lecture_files/libs/revealjs/plugin/notes/notes.esm.js
Normal file
1
Lecture_files/libs/revealjs/plugin/notes/notes.js
Normal file
236
Lecture_files/libs/revealjs/plugin/notes/plugin.js
Normal file
@@ -0,0 +1,236 @@
|
||||
import speakerViewHTML from './speaker-view.html';
|
||||
|
||||
import { marked } from 'marked';
|
||||
|
||||
/**
|
||||
* Handles opening of and synchronization with the reveal.js
|
||||
* notes window.
|
||||
*
|
||||
* Handshake process:
|
||||
* 1. This window posts 'connect' to notes window
|
||||
* - Includes URL of presentation to show
|
||||
* 2. Notes window responds with 'connected' when it is available
|
||||
* 3. This window proceeds to send the current presentation state
|
||||
* to the notes window
|
||||
*/
|
||||
const Plugin = () => {
|
||||
|
||||
let connectInterval;
|
||||
let speakerWindow = null;
|
||||
let deck;
|
||||
|
||||
/**
|
||||
* Opens a new speaker view window.
|
||||
*/
|
||||
function openSpeakerWindow() {
|
||||
|
||||
// If a window is already open, focus it
|
||||
if( speakerWindow && !speakerWindow.closed ) {
|
||||
speakerWindow.focus();
|
||||
}
|
||||
else {
|
||||
speakerWindow = window.open( 'about:blank', 'reveal.js - Notes', 'width=1100,height=700' );
|
||||
speakerWindow.marked = marked;
|
||||
speakerWindow.document.write( speakerViewHTML );
|
||||
|
||||
if( !speakerWindow ) {
|
||||
alert( 'Speaker view popup failed to open. Please make sure popups are allowed and reopen the speaker view.' );
|
||||
return;
|
||||
}
|
||||
|
||||
connect();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconnect with an existing speaker view window.
|
||||
*/
|
||||
function reconnectSpeakerWindow( reconnectWindow ) {
|
||||
|
||||
if( speakerWindow && !speakerWindow.closed ) {
|
||||
speakerWindow.focus();
|
||||
}
|
||||
else {
|
||||
speakerWindow = reconnectWindow;
|
||||
window.addEventListener( 'message', onPostMessage );
|
||||
onConnected();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the notes window through a postmessage handshake.
|
||||
* Using postmessage enables us to work in situations where the
|
||||
* origins differ, such as a presentation being opened from the
|
||||
* file system.
|
||||
*/
|
||||
function connect() {
|
||||
|
||||
const presentationURL = deck.getConfig().url;
|
||||
|
||||
const url = typeof presentationURL === 'string' ? presentationURL :
|
||||
window.location.protocol + '//' + window.location.host + window.location.pathname + window.location.search;
|
||||
|
||||
// Keep trying to connect until we get a 'connected' message back
|
||||
connectInterval = setInterval( function() {
|
||||
speakerWindow.postMessage( JSON.stringify( {
|
||||
namespace: 'reveal-notes',
|
||||
type: 'connect',
|
||||
state: deck.getState(),
|
||||
url
|
||||
} ), '*' );
|
||||
}, 500 );
|
||||
|
||||
window.addEventListener( 'message', onPostMessage );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the specified Reveal.js method with the provided argument
|
||||
* and then pushes the result to the notes frame.
|
||||
*/
|
||||
function callRevealApi( methodName, methodArguments, callId ) {
|
||||
|
||||
let result = deck[methodName].apply( deck, methodArguments );
|
||||
speakerWindow.postMessage( JSON.stringify( {
|
||||
namespace: 'reveal-notes',
|
||||
type: 'return',
|
||||
result,
|
||||
callId
|
||||
} ), '*' );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Posts the current slide data to the notes window.
|
||||
*/
|
||||
function post( event ) {
|
||||
|
||||
let slideElement = deck.getCurrentSlide(),
|
||||
notesElement = slideElement.querySelector( 'aside.notes' ),
|
||||
fragmentElement = slideElement.querySelector( '.current-fragment' );
|
||||
|
||||
let messageData = {
|
||||
namespace: 'reveal-notes',
|
||||
type: 'state',
|
||||
notes: '',
|
||||
markdown: false,
|
||||
whitespace: 'normal',
|
||||
state: deck.getState()
|
||||
};
|
||||
|
||||
// Look for notes defined in a slide attribute
|
||||
if( slideElement.hasAttribute( 'data-notes' ) ) {
|
||||
messageData.notes = slideElement.getAttribute( 'data-notes' );
|
||||
messageData.whitespace = 'pre-wrap';
|
||||
}
|
||||
|
||||
// Look for notes defined in a fragment
|
||||
if( fragmentElement ) {
|
||||
let fragmentNotes = fragmentElement.querySelector( 'aside.notes' );
|
||||
if( fragmentNotes ) {
|
||||
notesElement = fragmentNotes;
|
||||
}
|
||||
else if( fragmentElement.hasAttribute( 'data-notes' ) ) {
|
||||
messageData.notes = fragmentElement.getAttribute( 'data-notes' );
|
||||
messageData.whitespace = 'pre-wrap';
|
||||
|
||||
// In case there are slide notes
|
||||
notesElement = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Look for notes defined in an aside element
|
||||
if( notesElement ) {
|
||||
messageData.notes = notesElement.innerHTML;
|
||||
messageData.markdown = typeof notesElement.getAttribute( 'data-markdown' ) === 'string';
|
||||
}
|
||||
|
||||
speakerWindow.postMessage( JSON.stringify( messageData ), '*' );
|
||||
|
||||
}
|
||||
|
||||
function onPostMessage( event ) {
|
||||
|
||||
let data = JSON.parse( event.data );
|
||||
if( data && data.namespace === 'reveal-notes' && data.type === 'connected' ) {
|
||||
clearInterval( connectInterval );
|
||||
onConnected();
|
||||
}
|
||||
else if( data && data.namespace === 'reveal-notes' && data.type === 'call' ) {
|
||||
callRevealApi( data.methodName, data.arguments, data.callId );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once we have established a connection to the notes
|
||||
* window.
|
||||
*/
|
||||
function onConnected() {
|
||||
|
||||
// Monitor events that trigger a change in state
|
||||
deck.on( 'slidechanged', post );
|
||||
deck.on( 'fragmentshown', post );
|
||||
deck.on( 'fragmenthidden', post );
|
||||
deck.on( 'overviewhidden', post );
|
||||
deck.on( 'overviewshown', post );
|
||||
deck.on( 'paused', post );
|
||||
deck.on( 'resumed', post );
|
||||
|
||||
// Post the initial state
|
||||
post();
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
id: 'notes',
|
||||
|
||||
init: function( reveal ) {
|
||||
|
||||
deck = reveal;
|
||||
|
||||
if( !/receiver/i.test( window.location.search ) ) {
|
||||
|
||||
// If the there's a 'notes' query set, open directly
|
||||
if( window.location.search.match( /(\?|\&)notes/gi ) !== null ) {
|
||||
openSpeakerWindow();
|
||||
}
|
||||
else {
|
||||
// Keep listening for speaker view hearbeats. If we receive a
|
||||
// heartbeat from an orphaned window, reconnect it. This ensures
|
||||
// that we remain connected to the notes even if the presentation
|
||||
// is reloaded.
|
||||
window.addEventListener( 'message', event => {
|
||||
|
||||
if( !speakerWindow && typeof event.data === 'string' ) {
|
||||
let data;
|
||||
|
||||
try {
|
||||
data = JSON.parse( event.data );
|
||||
}
|
||||
catch( error ) {}
|
||||
|
||||
if( data && data.namespace === 'reveal-notes' && data.type === 'heartbeat' ) {
|
||||
reconnectSpeakerWindow( event.source );
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Open the notes when the 's' key is hit
|
||||
deck.addKeyBinding({keyCode: 83, key: 'S', description: 'Speaker notes view'}, function() {
|
||||
openSpeakerWindow();
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
open: openSpeakerWindow
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
export default Plugin;
|
||||
884
Lecture_files/libs/revealjs/plugin/notes/speaker-view.html
Normal file
@@ -0,0 +1,884 @@
|
||||
<!--
|
||||
NOTE: You need to build the notes plugin after making changes to this file.
|
||||
-->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<title>reveal.js - Speaker View</title>
|
||||
|
||||
<style>
|
||||
body {
|
||||
font-family: Helvetica;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
#current-slide,
|
||||
#upcoming-slide,
|
||||
#speaker-controls {
|
||||
padding: 6px;
|
||||
box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
}
|
||||
|
||||
#current-slide iframe,
|
||||
#upcoming-slide iframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
#current-slide .label,
|
||||
#upcoming-slide .label {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
#connection-status {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 20;
|
||||
padding: 30% 20% 20% 20%;
|
||||
font-size: 18px;
|
||||
color: #222;
|
||||
background: #fff;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.overlay-element {
|
||||
height: 34px;
|
||||
line-height: 34px;
|
||||
padding: 0 10px;
|
||||
text-shadow: none;
|
||||
background: rgba( 220, 220, 220, 0.8 );
|
||||
color: #222;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.overlay-element.interactive:hover {
|
||||
background: rgba( 220, 220, 220, 1 );
|
||||
}
|
||||
|
||||
#current-slide {
|
||||
position: absolute;
|
||||
width: 60%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
#upcoming-slide {
|
||||
position: absolute;
|
||||
width: 40%;
|
||||
height: 40%;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
/* Speaker controls */
|
||||
#speaker-controls {
|
||||
position: absolute;
|
||||
top: 40%;
|
||||
right: 0;
|
||||
width: 40%;
|
||||
height: 60%;
|
||||
overflow: auto;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.speaker-controls-time.hidden,
|
||||
.speaker-controls-notes.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.speaker-controls-time .label,
|
||||
.speaker-controls-pace .label,
|
||||
.speaker-controls-notes .label {
|
||||
text-transform: uppercase;
|
||||
font-weight: normal;
|
||||
font-size: 0.66em;
|
||||
color: #666;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.speaker-controls-time, .speaker-controls-pace {
|
||||
border-bottom: 1px solid rgba( 200, 200, 200, 0.5 );
|
||||
margin-bottom: 10px;
|
||||
padding: 10px 16px;
|
||||
padding-bottom: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.speaker-controls-time .reset-button {
|
||||
opacity: 0;
|
||||
float: right;
|
||||
color: #666;
|
||||
text-decoration: none;
|
||||
}
|
||||
.speaker-controls-time:hover .reset-button {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.speaker-controls-time .timer,
|
||||
.speaker-controls-time .clock {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.speaker-controls-time .timer,
|
||||
.speaker-controls-time .clock,
|
||||
.speaker-controls-time .pacing .hours-value,
|
||||
.speaker-controls-time .pacing .minutes-value,
|
||||
.speaker-controls-time .pacing .seconds-value {
|
||||
font-size: 1.9em;
|
||||
}
|
||||
|
||||
.speaker-controls-time .timer {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.speaker-controls-time .clock {
|
||||
float: right;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.speaker-controls-time span.mute {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.speaker-controls-time .pacing-title {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.speaker-controls-time .pacing.ahead {
|
||||
color: blue;
|
||||
}
|
||||
|
||||
.speaker-controls-time .pacing.on-track {
|
||||
color: green;
|
||||
}
|
||||
|
||||
.speaker-controls-time .pacing.behind {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.speaker-controls-notes {
|
||||
padding: 10px 16px;
|
||||
}
|
||||
|
||||
.speaker-controls-notes .value {
|
||||
margin-top: 5px;
|
||||
line-height: 1.4;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
/* Layout selector */
|
||||
#speaker-layout {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
color: #222;
|
||||
z-index: 10;
|
||||
}
|
||||
#speaker-layout select {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
border: 0;
|
||||
box-shadow: 0;
|
||||
cursor: pointer;
|
||||
opacity: 0;
|
||||
|
||||
font-size: 1em;
|
||||
background-color: transparent;
|
||||
|
||||
-moz-appearance: none;
|
||||
-webkit-appearance: none;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
#speaker-layout select:focus {
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.clear {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
/* Speaker layout: Wide */
|
||||
body[data-speaker-layout="wide"] #current-slide,
|
||||
body[data-speaker-layout="wide"] #upcoming-slide {
|
||||
width: 50%;
|
||||
height: 45%;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
body[data-speaker-layout="wide"] #current-slide {
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
body[data-speaker-layout="wide"] #upcoming-slide {
|
||||
top: 0;
|
||||
left: 50%;
|
||||
}
|
||||
|
||||
body[data-speaker-layout="wide"] #speaker-controls {
|
||||
top: 45%;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 50%;
|
||||
font-size: 1.25em;
|
||||
}
|
||||
|
||||
/* Speaker layout: Tall */
|
||||
body[data-speaker-layout="tall"] #current-slide,
|
||||
body[data-speaker-layout="tall"] #upcoming-slide {
|
||||
width: 45%;
|
||||
height: 50%;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
body[data-speaker-layout="tall"] #current-slide {
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
body[data-speaker-layout="tall"] #upcoming-slide {
|
||||
top: 50%;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
body[data-speaker-layout="tall"] #speaker-controls {
|
||||
padding-top: 40px;
|
||||
top: 0;
|
||||
left: 45%;
|
||||
width: 55%;
|
||||
height: 100%;
|
||||
font-size: 1.25em;
|
||||
}
|
||||
|
||||
/* Speaker layout: Notes only */
|
||||
body[data-speaker-layout="notes-only"] #current-slide,
|
||||
body[data-speaker-layout="notes-only"] #upcoming-slide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body[data-speaker-layout="notes-only"] #speaker-controls {
|
||||
padding-top: 40px;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 1.25em;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1080px) {
|
||||
body[data-speaker-layout="default"] #speaker-controls {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 900px) {
|
||||
body[data-speaker-layout="default"] #speaker-controls {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
body[data-speaker-layout="default"] #speaker-controls {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="connection-status">Loading speaker view...</div>
|
||||
|
||||
<div id="current-slide"></div>
|
||||
<div id="upcoming-slide"><span class="overlay-element label">Upcoming</span></div>
|
||||
<div id="speaker-controls">
|
||||
<div class="speaker-controls-time">
|
||||
<h4 class="label">Time <span class="reset-button">Click to Reset</span></h4>
|
||||
<div class="clock">
|
||||
<span class="clock-value">0:00 AM</span>
|
||||
</div>
|
||||
<div class="timer">
|
||||
<span class="hours-value">00</span><span class="minutes-value">:00</span><span class="seconds-value">:00</span>
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
|
||||
<h4 class="label pacing-title" style="display: none">Pacing – Time to finish current slide</h4>
|
||||
<div class="pacing" style="display: none">
|
||||
<span class="hours-value">00</span><span class="minutes-value">:00</span><span class="seconds-value">:00</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="speaker-controls-notes hidden">
|
||||
<h4 class="label">Notes</h4>
|
||||
<div class="value"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="speaker-layout" class="overlay-element interactive">
|
||||
<span class="speaker-layout-label"></span>
|
||||
<select class="speaker-layout-dropdown"></select>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
(function() {
|
||||
|
||||
var notes,
|
||||
notesValue,
|
||||
currentState,
|
||||
currentSlide,
|
||||
upcomingSlide,
|
||||
layoutLabel,
|
||||
layoutDropdown,
|
||||
pendingCalls = {},
|
||||
lastRevealApiCallId = 0,
|
||||
connected = false,
|
||||
whitelistedWindows = [window.opener];
|
||||
|
||||
var SPEAKER_LAYOUTS = {
|
||||
'default': 'Default',
|
||||
'wide': 'Wide',
|
||||
'tall': 'Tall',
|
||||
'notes-only': 'Notes only'
|
||||
};
|
||||
|
||||
setupLayout();
|
||||
|
||||
var connectionStatus = document.querySelector( '#connection-status' );
|
||||
var connectionTimeout = setTimeout( function() {
|
||||
connectionStatus.innerHTML = 'Error connecting to main window.<br>Please try closing and reopening the speaker view.';
|
||||
}, 5000 );
|
||||
;
|
||||
window.addEventListener( 'message', function( event ) {
|
||||
|
||||
// Validate the origin of this message to prevent XSS
|
||||
if( window.location.origin !== event.origin && whitelistedWindows.indexOf( event.source ) === -1 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
clearTimeout( connectionTimeout );
|
||||
connectionStatus.style.display = 'none';
|
||||
|
||||
var data = JSON.parse( event.data );
|
||||
|
||||
// The overview mode is only useful to the reveal.js instance
|
||||
// where navigation occurs so we don't sync it
|
||||
if( data.state ) delete data.state.overview;
|
||||
|
||||
// Messages sent by the notes plugin inside of the main window
|
||||
if( data && data.namespace === 'reveal-notes' ) {
|
||||
if( data.type === 'connect' ) {
|
||||
handleConnectMessage( data );
|
||||
}
|
||||
else if( data.type === 'state' ) {
|
||||
handleStateMessage( data );
|
||||
}
|
||||
else if( data.type === 'return' ) {
|
||||
pendingCalls[data.callId](data.result);
|
||||
delete pendingCalls[data.callId];
|
||||
}
|
||||
}
|
||||
// Messages sent by the reveal.js inside of the current slide preview
|
||||
else if( data && data.namespace === 'reveal' ) {
|
||||
if( /ready/.test( data.eventName ) ) {
|
||||
// Send a message back to notify that the handshake is complete
|
||||
window.opener.postMessage( JSON.stringify({ namespace: 'reveal-notes', type: 'connected'} ), '*' );
|
||||
}
|
||||
else if( /slidechanged|fragmentshown|fragmenthidden|paused|resumed/.test( data.eventName ) && currentState !== JSON.stringify( data.state ) ) {
|
||||
|
||||
dispatchStateToMainWindow( data.state );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
/**
|
||||
* Updates the presentation in the main window to match the state
|
||||
* of the presentation in the notes window.
|
||||
*/
|
||||
const dispatchStateToMainWindow = debounce(( state ) => {
|
||||
window.opener.postMessage( JSON.stringify({ method: 'setState', args: [ state ]} ), '*' );
|
||||
}, 500);
|
||||
|
||||
/**
|
||||
* Asynchronously calls the Reveal.js API of the main frame.
|
||||
*/
|
||||
function callRevealApi( methodName, methodArguments, callback ) {
|
||||
|
||||
var callId = ++lastRevealApiCallId;
|
||||
pendingCalls[callId] = callback;
|
||||
window.opener.postMessage( JSON.stringify( {
|
||||
namespace: 'reveal-notes',
|
||||
type: 'call',
|
||||
callId: callId,
|
||||
methodName: methodName,
|
||||
arguments: methodArguments
|
||||
} ), '*' );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the main window is trying to establish a
|
||||
* connection.
|
||||
*/
|
||||
function handleConnectMessage( data ) {
|
||||
|
||||
if( connected === false ) {
|
||||
connected = true;
|
||||
|
||||
setupIframes( data );
|
||||
setupKeyboard();
|
||||
setupNotes();
|
||||
setupTimer();
|
||||
setupHeartbeat();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the main window sends an updated state.
|
||||
*/
|
||||
function handleStateMessage( data ) {
|
||||
|
||||
// Store the most recently set state to avoid circular loops
|
||||
// applying the same state
|
||||
currentState = JSON.stringify( data.state );
|
||||
|
||||
// No need for updating the notes in case of fragment changes
|
||||
if ( data.notes ) {
|
||||
notes.classList.remove( 'hidden' );
|
||||
notesValue.style.whiteSpace = data.whitespace;
|
||||
if( data.markdown ) {
|
||||
notesValue.innerHTML = marked( data.notes );
|
||||
}
|
||||
else {
|
||||
notesValue.innerHTML = data.notes;
|
||||
}
|
||||
}
|
||||
else {
|
||||
notes.classList.add( 'hidden' );
|
||||
}
|
||||
|
||||
// Update the note slides
|
||||
currentSlide.contentWindow.postMessage( JSON.stringify({ method: 'setState', args: [ data.state ] }), '*' );
|
||||
upcomingSlide.contentWindow.postMessage( JSON.stringify({ method: 'setState', args: [ data.state ] }), '*' );
|
||||
upcomingSlide.contentWindow.postMessage( JSON.stringify({ method: 'next' }), '*' );
|
||||
|
||||
}
|
||||
|
||||
// Limit to max one state update per X ms
|
||||
handleStateMessage = debounce( handleStateMessage, 200 );
|
||||
|
||||
/**
|
||||
* Forward keyboard events to the current slide window.
|
||||
* This enables keyboard events to work even if focus
|
||||
* isn't set on the current slide iframe.
|
||||
*
|
||||
* Block F5 default handling, it reloads and disconnects
|
||||
* the speaker notes window.
|
||||
*/
|
||||
function setupKeyboard() {
|
||||
|
||||
document.addEventListener( 'keydown', function( event ) {
|
||||
if( event.keyCode === 116 || ( event.metaKey && event.keyCode === 82 ) ) {
|
||||
event.preventDefault();
|
||||
return false;
|
||||
}
|
||||
currentSlide.contentWindow.postMessage( JSON.stringify({ method: 'triggerKey', args: [ event.keyCode ] }), '*' );
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the preview iframes.
|
||||
*/
|
||||
function setupIframes( data ) {
|
||||
|
||||
var params = [
|
||||
'receiver',
|
||||
'progress=false',
|
||||
'history=false',
|
||||
'transition=none',
|
||||
'autoSlide=0',
|
||||
'backgroundTransition=none'
|
||||
].join( '&' );
|
||||
|
||||
var urlSeparator = /\?/.test(data.url) ? '&' : '?';
|
||||
var hash = '#/' + data.state.indexh + '/' + data.state.indexv;
|
||||
var currentURL = data.url + urlSeparator + params + '&postMessageEvents=true' + hash;
|
||||
var upcomingURL = data.url + urlSeparator + params + '&controls=false' + hash;
|
||||
|
||||
currentSlide = document.createElement( 'iframe' );
|
||||
currentSlide.setAttribute( 'width', 1280 );
|
||||
currentSlide.setAttribute( 'height', 1024 );
|
||||
currentSlide.setAttribute( 'src', currentURL );
|
||||
document.querySelector( '#current-slide' ).appendChild( currentSlide );
|
||||
|
||||
upcomingSlide = document.createElement( 'iframe' );
|
||||
upcomingSlide.setAttribute( 'width', 640 );
|
||||
upcomingSlide.setAttribute( 'height', 512 );
|
||||
upcomingSlide.setAttribute( 'src', upcomingURL );
|
||||
document.querySelector( '#upcoming-slide' ).appendChild( upcomingSlide );
|
||||
|
||||
whitelistedWindows.push( currentSlide.contentWindow, upcomingSlide.contentWindow );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup the notes UI.
|
||||
*/
|
||||
function setupNotes() {
|
||||
|
||||
notes = document.querySelector( '.speaker-controls-notes' );
|
||||
notesValue = document.querySelector( '.speaker-controls-notes .value' );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* We send out a heartbeat at all times to ensure we can
|
||||
* reconnect with the main presentation window after reloads.
|
||||
*/
|
||||
function setupHeartbeat() {
|
||||
|
||||
setInterval( () => {
|
||||
window.opener.postMessage( JSON.stringify({ namespace: 'reveal-notes', type: 'heartbeat'} ), '*' );
|
||||
}, 1000 );
|
||||
|
||||
}
|
||||
|
||||
function getTimings( callback ) {
|
||||
|
||||
callRevealApi( 'getSlidesAttributes', [], function ( slideAttributes ) {
|
||||
callRevealApi( 'getConfig', [], function ( config ) {
|
||||
var totalTime = config.totalTime;
|
||||
var minTimePerSlide = config.minimumTimePerSlide || 0;
|
||||
var defaultTiming = config.defaultTiming;
|
||||
if ((defaultTiming == null) && (totalTime == null)) {
|
||||
callback(null);
|
||||
return;
|
||||
}
|
||||
// Setting totalTime overrides defaultTiming
|
||||
if (totalTime) {
|
||||
defaultTiming = 0;
|
||||
}
|
||||
var timings = [];
|
||||
for ( var i in slideAttributes ) {
|
||||
var slide = slideAttributes[ i ];
|
||||
var timing = defaultTiming;
|
||||
if( slide.hasOwnProperty( 'data-timing' )) {
|
||||
var t = slide[ 'data-timing' ];
|
||||
timing = parseInt(t);
|
||||
if( isNaN(timing) ) {
|
||||
console.warn("Could not parse timing '" + t + "' of slide " + i + "; using default of " + defaultTiming);
|
||||
timing = defaultTiming;
|
||||
}
|
||||
}
|
||||
timings.push(timing);
|
||||
}
|
||||
if ( totalTime ) {
|
||||
// After we've allocated time to individual slides, we summarize it and
|
||||
// subtract it from the total time
|
||||
var remainingTime = totalTime - timings.reduce( function(a, b) { return a + b; }, 0 );
|
||||
// The remaining time is divided by the number of slides that have 0 seconds
|
||||
// allocated at the moment, giving the average time-per-slide on the remaining slides
|
||||
var remainingSlides = (timings.filter( function(x) { return x == 0 }) ).length
|
||||
var timePerSlide = Math.round( remainingTime / remainingSlides, 0 )
|
||||
// And now we replace every zero-value timing with that average
|
||||
timings = timings.map( function(x) { return (x==0 ? timePerSlide : x) } );
|
||||
}
|
||||
var slidesUnderMinimum = timings.filter( function(x) { return (x < minTimePerSlide) } ).length
|
||||
if ( slidesUnderMinimum ) {
|
||||
message = "The pacing time for " + slidesUnderMinimum + " slide(s) is under the configured minimum of " + minTimePerSlide + " seconds. Check the data-timing attribute on individual slides, or consider increasing the totalTime or minimumTimePerSlide configuration options (or removing some slides).";
|
||||
alert(message);
|
||||
}
|
||||
callback( timings );
|
||||
} );
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of seconds allocated for presenting
|
||||
* all slides up to and including this one.
|
||||
*/
|
||||
function getTimeAllocated( timings, callback ) {
|
||||
|
||||
callRevealApi( 'getSlidePastCount', [], function ( currentSlide ) {
|
||||
var allocated = 0;
|
||||
for (var i in timings.slice(0, currentSlide + 1)) {
|
||||
allocated += timings[i];
|
||||
}
|
||||
callback( allocated );
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the timer and clock and start updating them
|
||||
* at an interval.
|
||||
*/
|
||||
function setupTimer() {
|
||||
|
||||
var start = new Date(),
|
||||
timeEl = document.querySelector( '.speaker-controls-time' ),
|
||||
clockEl = timeEl.querySelector( '.clock-value' ),
|
||||
hoursEl = timeEl.querySelector( '.hours-value' ),
|
||||
minutesEl = timeEl.querySelector( '.minutes-value' ),
|
||||
secondsEl = timeEl.querySelector( '.seconds-value' ),
|
||||
pacingTitleEl = timeEl.querySelector( '.pacing-title' ),
|
||||
pacingEl = timeEl.querySelector( '.pacing' ),
|
||||
pacingHoursEl = pacingEl.querySelector( '.hours-value' ),
|
||||
pacingMinutesEl = pacingEl.querySelector( '.minutes-value' ),
|
||||
pacingSecondsEl = pacingEl.querySelector( '.seconds-value' );
|
||||
|
||||
var timings = null;
|
||||
getTimings( function ( _timings ) {
|
||||
|
||||
timings = _timings;
|
||||
if (_timings !== null) {
|
||||
pacingTitleEl.style.removeProperty('display');
|
||||
pacingEl.style.removeProperty('display');
|
||||
}
|
||||
|
||||
// Update once directly
|
||||
_updateTimer();
|
||||
|
||||
// Then update every second
|
||||
setInterval( _updateTimer, 1000 );
|
||||
|
||||
} );
|
||||
|
||||
|
||||
function _resetTimer() {
|
||||
|
||||
if (timings == null) {
|
||||
start = new Date();
|
||||
_updateTimer();
|
||||
}
|
||||
else {
|
||||
// Reset timer to beginning of current slide
|
||||
getTimeAllocated( timings, function ( slideEndTimingSeconds ) {
|
||||
var slideEndTiming = slideEndTimingSeconds * 1000;
|
||||
callRevealApi( 'getSlidePastCount', [], function ( currentSlide ) {
|
||||
var currentSlideTiming = timings[currentSlide] * 1000;
|
||||
var previousSlidesTiming = slideEndTiming - currentSlideTiming;
|
||||
var now = new Date();
|
||||
start = new Date(now.getTime() - previousSlidesTiming);
|
||||
_updateTimer();
|
||||
} );
|
||||
} );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
timeEl.addEventListener( 'click', function() {
|
||||
_resetTimer();
|
||||
return false;
|
||||
} );
|
||||
|
||||
function _displayTime( hrEl, minEl, secEl, time) {
|
||||
|
||||
var sign = Math.sign(time) == -1 ? "-" : "";
|
||||
time = Math.abs(Math.round(time / 1000));
|
||||
var seconds = time % 60;
|
||||
var minutes = Math.floor( time / 60 ) % 60 ;
|
||||
var hours = Math.floor( time / ( 60 * 60 )) ;
|
||||
hrEl.innerHTML = sign + zeroPadInteger( hours );
|
||||
if (hours == 0) {
|
||||
hrEl.classList.add( 'mute' );
|
||||
}
|
||||
else {
|
||||
hrEl.classList.remove( 'mute' );
|
||||
}
|
||||
minEl.innerHTML = ':' + zeroPadInteger( minutes );
|
||||
if (hours == 0 && minutes == 0) {
|
||||
minEl.classList.add( 'mute' );
|
||||
}
|
||||
else {
|
||||
minEl.classList.remove( 'mute' );
|
||||
}
|
||||
secEl.innerHTML = ':' + zeroPadInteger( seconds );
|
||||
}
|
||||
|
||||
function _updateTimer() {
|
||||
|
||||
var diff, hours, minutes, seconds,
|
||||
now = new Date();
|
||||
|
||||
diff = now.getTime() - start.getTime();
|
||||
|
||||
clockEl.innerHTML = now.toLocaleTimeString( 'en-US', { hour12: true, hour: '2-digit', minute:'2-digit' } );
|
||||
_displayTime( hoursEl, minutesEl, secondsEl, diff );
|
||||
if (timings !== null) {
|
||||
_updatePacing(diff);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function _updatePacing(diff) {
|
||||
|
||||
getTimeAllocated( timings, function ( slideEndTimingSeconds ) {
|
||||
var slideEndTiming = slideEndTimingSeconds * 1000;
|
||||
|
||||
callRevealApi( 'getSlidePastCount', [], function ( currentSlide ) {
|
||||
var currentSlideTiming = timings[currentSlide] * 1000;
|
||||
var timeLeftCurrentSlide = slideEndTiming - diff;
|
||||
if (timeLeftCurrentSlide < 0) {
|
||||
pacingEl.className = 'pacing behind';
|
||||
}
|
||||
else if (timeLeftCurrentSlide < currentSlideTiming) {
|
||||
pacingEl.className = 'pacing on-track';
|
||||
}
|
||||
else {
|
||||
pacingEl.className = 'pacing ahead';
|
||||
}
|
||||
_displayTime( pacingHoursEl, pacingMinutesEl, pacingSecondsEl, timeLeftCurrentSlide );
|
||||
} );
|
||||
} );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the speaker view layout and layout selector.
|
||||
*/
|
||||
function setupLayout() {
|
||||
|
||||
layoutDropdown = document.querySelector( '.speaker-layout-dropdown' );
|
||||
layoutLabel = document.querySelector( '.speaker-layout-label' );
|
||||
|
||||
// Render the list of available layouts
|
||||
for( var id in SPEAKER_LAYOUTS ) {
|
||||
var option = document.createElement( 'option' );
|
||||
option.setAttribute( 'value', id );
|
||||
option.textContent = SPEAKER_LAYOUTS[ id ];
|
||||
layoutDropdown.appendChild( option );
|
||||
}
|
||||
|
||||
// Monitor the dropdown for changes
|
||||
layoutDropdown.addEventListener( 'change', function( event ) {
|
||||
|
||||
setLayout( layoutDropdown.value );
|
||||
|
||||
}, false );
|
||||
|
||||
// Restore any currently persisted layout
|
||||
setLayout( getLayout() );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new speaker view layout. The layout is persisted
|
||||
* in local storage.
|
||||
*/
|
||||
function setLayout( value ) {
|
||||
|
||||
var title = SPEAKER_LAYOUTS[ value ];
|
||||
|
||||
layoutLabel.innerHTML = 'Layout' + ( title ? ( ': ' + title ) : '' );
|
||||
layoutDropdown.value = value;
|
||||
|
||||
document.body.setAttribute( 'data-speaker-layout', value );
|
||||
|
||||
// Persist locally
|
||||
if( supportsLocalStorage() ) {
|
||||
window.localStorage.setItem( 'reveal-speaker-layout', value );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ID of the most recently set speaker layout
|
||||
* or our default layout if none has been set.
|
||||
*/
|
||||
function getLayout() {
|
||||
|
||||
if( supportsLocalStorage() ) {
|
||||
var layout = window.localStorage.getItem( 'reveal-speaker-layout' );
|
||||
if( layout ) {
|
||||
return layout;
|
||||
}
|
||||
}
|
||||
|
||||
// Default to the first record in the layouts hash
|
||||
for( var id in SPEAKER_LAYOUTS ) {
|
||||
return id;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function supportsLocalStorage() {
|
||||
|
||||
try {
|
||||
localStorage.setItem('test', 'test');
|
||||
localStorage.removeItem('test');
|
||||
return true;
|
||||
}
|
||||
catch( e ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function zeroPadInteger( num ) {
|
||||
|
||||
var str = '00' + parseInt( num );
|
||||
return str.substring( str.length - 2 );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Limits the frequency at which a function can be called.
|
||||
*/
|
||||
function debounce( fn, ms ) {
|
||||
|
||||
var lastTime = 0,
|
||||
timeout;
|
||||
|
||||
return function() {
|
||||
|
||||
var args = arguments;
|
||||
var context = this;
|
||||
|
||||
clearTimeout( timeout );
|
||||
|
||||
var timeSinceLastCall = Date.now() - lastTime;
|
||||
if( timeSinceLastCall > ms ) {
|
||||
fn.apply( context, args );
|
||||
lastTime = Date.now();
|
||||
}
|
||||
else {
|
||||
timeout = setTimeout( function() {
|
||||
fn.apply( context, args );
|
||||
lastTime = Date.now();
|
||||
}, ms - timeSinceLastCall );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
108
Lecture_files/libs/revealjs/plugin/pdf-export/pdfexport.js
Normal file
@@ -0,0 +1,108 @@
|
||||
var PdfExport = ( function( _Reveal ){
|
||||
|
||||
var Reveal = _Reveal;
|
||||
var setStylesheet = null;
|
||||
var installAltKeyBindings = null;
|
||||
|
||||
function getRevealJsPath(){
|
||||
var regex = /\b[^/]+\/reveal.css$/i;
|
||||
var script = Array.from( document.querySelectorAll( 'link' ) ).find( function( e ){
|
||||
return e.attributes.href && e.attributes.href.value.search( regex ) >= 0;
|
||||
});
|
||||
if( !script ){
|
||||
console.error( 'reveal.css could not be found in included <link> elements. Did you rename this file?' );
|
||||
return '';
|
||||
}
|
||||
return script.attributes.href.value.replace( regex, '' );
|
||||
}
|
||||
|
||||
function setStylesheet3( pdfExport ){
|
||||
var link = document.querySelector( '#print' );
|
||||
if( !link ){
|
||||
link = document.createElement( 'link' );
|
||||
link.rel = 'stylesheet';
|
||||
link.id = 'print';
|
||||
document.querySelector( 'head' ).appendChild( link );
|
||||
}
|
||||
var style = 'paper';
|
||||
if( pdfExport ){
|
||||
style = 'pdf';
|
||||
}
|
||||
link.href = getRevealJsPath() + 'css/print/' + style + '.css';
|
||||
}
|
||||
|
||||
function setStylesheet4( pdfExport ){
|
||||
}
|
||||
|
||||
function installAltKeyBindings3(){
|
||||
}
|
||||
|
||||
function installAltKeyBindings4(){
|
||||
if( isPrintingPDF() ){
|
||||
var config = Reveal.getConfig();
|
||||
var shortcut = config.pdfExportShortcut || 'E';
|
||||
window.addEventListener( 'keydown', function( e ){
|
||||
if( e.target.nodeName.toUpperCase() == 'BODY'
|
||||
&& ( e.key.toUpperCase() == shortcut.toUpperCase() || e.keyCode == shortcut.toUpperCase().charCodeAt( 0 ) ) ){
|
||||
e.preventDefault();
|
||||
togglePdfExport();
|
||||
return false;
|
||||
}
|
||||
}, true );
|
||||
}
|
||||
}
|
||||
|
||||
function isPrintingPDF(){
|
||||
return ( /print-pdf/gi ).test( window.location.search );
|
||||
}
|
||||
|
||||
function togglePdfExport(){
|
||||
var url_doc = new URL( document.URL );
|
||||
var query_doc = new URLSearchParams( url_doc.searchParams );
|
||||
if( isPrintingPDF() ){
|
||||
query_doc.delete( 'print-pdf' );
|
||||
}else{
|
||||
query_doc.set( 'print-pdf', '' );
|
||||
}
|
||||
url_doc.search = ( query_doc.toString() ? '?' + query_doc.toString() : '' );
|
||||
window.location.href = url_doc.toString();
|
||||
}
|
||||
|
||||
function installKeyBindings(){
|
||||
var config = Reveal.getConfig();
|
||||
var shortcut = config.pdfExportShortcut || 'E';
|
||||
Reveal.addKeyBinding({
|
||||
keyCode: shortcut.toUpperCase().charCodeAt( 0 ),
|
||||
key: shortcut.toUpperCase(),
|
||||
description: 'PDF export mode'
|
||||
}, togglePdfExport );
|
||||
installAltKeyBindings();
|
||||
}
|
||||
|
||||
function install(){
|
||||
installKeyBindings();
|
||||
setStylesheet( isPrintingPDF() );
|
||||
}
|
||||
|
||||
var Plugin = {
|
||||
}
|
||||
|
||||
if( Reveal && Reveal.VERSION && Reveal.VERSION.length && Reveal.VERSION[ 0 ] == '3' ){
|
||||
// reveal 3.x
|
||||
setStylesheet = setStylesheet3;
|
||||
installAltKeyBindings = installAltKeyBindings3;
|
||||
install();
|
||||
}else{
|
||||
// must be reveal 4.x
|
||||
setStylesheet = setStylesheet4;
|
||||
installAltKeyBindings = installAltKeyBindings4;
|
||||
Plugin.id = 'pdf-export';
|
||||
Plugin.init = function( _Reveal ){
|
||||
Reveal = _Reveal;
|
||||
install();
|
||||
};
|
||||
}
|
||||
|
||||
return Plugin;
|
||||
|
||||
})( Reveal );
|
||||
2
Lecture_files/libs/revealjs/plugin/pdf-export/plugin.yml
Normal file
@@ -0,0 +1,2 @@
|
||||
name: PdfExport
|
||||
script: pdfexport.js
|
||||
@@ -0,0 +1,31 @@
|
||||
.reveal
|
||||
div.sourceCode
|
||||
pre
|
||||
code.has-line-highlights
|
||||
> span:not(.highlight-line) {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.reveal pre.numberSource {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.reveal pre.numberSource code > span {
|
||||
left: -2.1em;
|
||||
}
|
||||
|
||||
pre.numberSource code > span > a:first-child::before {
|
||||
left: -0.7em;
|
||||
}
|
||||
|
||||
.reveal pre > code:not(:first-child).fragment {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.reveal div.sourceCode pre code {
|
||||
min-height: 100%;
|
||||
}
|
||||
@@ -0,0 +1,351 @@
|
||||
window.QuartoLineHighlight = function () {
|
||||
function isPrintView() {
|
||||
return /print-pdf/gi.test(window.location.search);
|
||||
}
|
||||
|
||||
const delimiters = {
|
||||
step: "|",
|
||||
line: ",",
|
||||
lineRange: "-",
|
||||
};
|
||||
|
||||
const regex = new RegExp(
|
||||
"^[\\d" + Object.values(delimiters).join("") + "]+$"
|
||||
);
|
||||
|
||||
function handleLinesSelector(deck, attr) {
|
||||
// if we are in printview with pdfSeparateFragments: false
|
||||
// then we'll also want to supress
|
||||
if (regex.test(attr)) {
|
||||
if (isPrintView() && deck.getConfig().pdfSeparateFragments !== true) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const kCodeLineNumbersAttr = "data-code-line-numbers";
|
||||
const kFragmentIndex = "data-fragment-index";
|
||||
|
||||
function initQuartoLineHighlight(deck) {
|
||||
const divSourceCode = deck
|
||||
.getRevealElement()
|
||||
.querySelectorAll("div.sourceCode");
|
||||
// Process each div created by Pandoc highlighting - numbered line are already included.
|
||||
divSourceCode.forEach((el) => {
|
||||
if (el.hasAttribute(kCodeLineNumbersAttr)) {
|
||||
const codeLineAttr = el.getAttribute(kCodeLineNumbersAttr);
|
||||
el.removeAttribute("data-code-line-numbers");
|
||||
if (handleLinesSelector(deck, codeLineAttr)) {
|
||||
// Only process if attr is a string to select lines to highlights
|
||||
// e.g "1|3,6|8-11"
|
||||
const codeBlock = el.querySelectorAll("pre code");
|
||||
codeBlock.forEach((code) => {
|
||||
// move attributes on code block
|
||||
code.setAttribute(kCodeLineNumbersAttr, codeLineAttr);
|
||||
|
||||
const scrollState = { currentBlock: code };
|
||||
|
||||
// Check if there are steps and duplicate code block accordingly
|
||||
const highlightSteps = splitLineNumbers(codeLineAttr);
|
||||
if (highlightSteps.length > 1) {
|
||||
// If the original code block has a fragment-index,
|
||||
// each clone should follow in an incremental sequence
|
||||
let fragmentIndex = parseInt(
|
||||
code.getAttribute(kFragmentIndex),
|
||||
10
|
||||
);
|
||||
fragmentIndex =
|
||||
typeof fragmentIndex !== "number" || isNaN(fragmentIndex)
|
||||
? null
|
||||
: fragmentIndex;
|
||||
|
||||
let stepN = 1;
|
||||
highlightSteps.slice(1).forEach(
|
||||
// Generate fragments for all steps except the original block
|
||||
(step) => {
|
||||
var fragmentBlock = code.cloneNode(true);
|
||||
fragmentBlock.setAttribute(
|
||||
"data-code-line-numbers",
|
||||
joinLineNumbers([step])
|
||||
);
|
||||
fragmentBlock.classList.add("fragment");
|
||||
|
||||
// Pandoc sets id on spans we need to keep unique
|
||||
fragmentBlock
|
||||
.querySelectorAll(":scope > span")
|
||||
.forEach((span) => {
|
||||
if (span.hasAttribute("id")) {
|
||||
span.setAttribute(
|
||||
"id",
|
||||
span.getAttribute("id").concat("-" + stepN)
|
||||
);
|
||||
}
|
||||
});
|
||||
stepN = ++stepN;
|
||||
|
||||
// Add duplicated <code> element after existing one
|
||||
code.parentNode.appendChild(fragmentBlock);
|
||||
|
||||
// Each new <code> element is highlighted based on the new attributes value
|
||||
highlightCodeBlock(fragmentBlock);
|
||||
|
||||
if (typeof fragmentIndex === "number") {
|
||||
fragmentBlock.setAttribute(kFragmentIndex, fragmentIndex);
|
||||
fragmentIndex += 1;
|
||||
} else {
|
||||
fragmentBlock.removeAttribute(kFragmentIndex);
|
||||
}
|
||||
|
||||
// Scroll highlights into view as we step through them
|
||||
fragmentBlock.addEventListener(
|
||||
"visible",
|
||||
scrollHighlightedLineIntoView.bind(
|
||||
this,
|
||||
fragmentBlock,
|
||||
scrollState
|
||||
)
|
||||
);
|
||||
fragmentBlock.addEventListener(
|
||||
"hidden",
|
||||
scrollHighlightedLineIntoView.bind(
|
||||
this,
|
||||
fragmentBlock.previousSibling,
|
||||
scrollState
|
||||
)
|
||||
);
|
||||
}
|
||||
);
|
||||
code.removeAttribute(kFragmentIndex);
|
||||
code.setAttribute(
|
||||
kCodeLineNumbersAttr,
|
||||
joinLineNumbers([highlightSteps[0]])
|
||||
);
|
||||
}
|
||||
|
||||
// Scroll the first highlight into view when the slide becomes visible.
|
||||
const slide =
|
||||
typeof code.closest === "function"
|
||||
? code.closest("section:not(.stack)")
|
||||
: null;
|
||||
if (slide) {
|
||||
const scrollFirstHighlightIntoView = function () {
|
||||
scrollHighlightedLineIntoView(code, scrollState, true);
|
||||
slide.removeEventListener(
|
||||
"visible",
|
||||
scrollFirstHighlightIntoView
|
||||
);
|
||||
};
|
||||
slide.addEventListener("visible", scrollFirstHighlightIntoView);
|
||||
}
|
||||
|
||||
highlightCodeBlock(code);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function highlightCodeBlock(codeBlock) {
|
||||
const highlightSteps = splitLineNumbers(
|
||||
codeBlock.getAttribute(kCodeLineNumbersAttr)
|
||||
);
|
||||
|
||||
if (highlightSteps.length) {
|
||||
// If we have at least one step, we generate fragments
|
||||
highlightSteps[0].forEach((highlight) => {
|
||||
// Add expected class on <pre> for reveal CSS
|
||||
codeBlock.parentNode.classList.add("code-wrapper");
|
||||
|
||||
// Select lines to highlight
|
||||
spanToHighlight = [];
|
||||
if (typeof highlight.last === "number") {
|
||||
spanToHighlight = [].slice.call(
|
||||
codeBlock.querySelectorAll(
|
||||
":scope > span:nth-child(n+" +
|
||||
highlight.first +
|
||||
"):nth-child(-n+" +
|
||||
highlight.last +
|
||||
")"
|
||||
)
|
||||
);
|
||||
} else if (typeof highlight.first === "number") {
|
||||
spanToHighlight = [].slice.call(
|
||||
codeBlock.querySelectorAll(
|
||||
":scope > span:nth-child(" + highlight.first + ")"
|
||||
)
|
||||
);
|
||||
}
|
||||
if (spanToHighlight.length) {
|
||||
// Add a class on <code> and <span> to select line to highlight
|
||||
spanToHighlight.forEach((span) =>
|
||||
span.classList.add("highlight-line")
|
||||
);
|
||||
codeBlock.classList.add("has-line-highlights");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Animates scrolling to the first highlighted line
|
||||
* in the given code block.
|
||||
*/
|
||||
function scrollHighlightedLineIntoView(block, scrollState, skipAnimation) {
|
||||
window.cancelAnimationFrame(scrollState.animationFrameID);
|
||||
|
||||
// Match the scroll position of the currently visible
|
||||
// code block
|
||||
if (scrollState.currentBlock) {
|
||||
block.scrollTop = scrollState.currentBlock.scrollTop;
|
||||
}
|
||||
|
||||
// Remember the current code block so that we can match
|
||||
// its scroll position when showing/hiding fragments
|
||||
scrollState.currentBlock = block;
|
||||
|
||||
const highlightBounds = getHighlightedLineBounds(block);
|
||||
let viewportHeight = block.offsetHeight;
|
||||
|
||||
// Subtract padding from the viewport height
|
||||
const blockStyles = window.getComputedStyle(block);
|
||||
viewportHeight -=
|
||||
parseInt(blockStyles.paddingTop) + parseInt(blockStyles.paddingBottom);
|
||||
|
||||
// Scroll position which centers all highlights
|
||||
const startTop = block.scrollTop;
|
||||
let targetTop =
|
||||
highlightBounds.top +
|
||||
(Math.min(highlightBounds.bottom - highlightBounds.top, viewportHeight) -
|
||||
viewportHeight) /
|
||||
2;
|
||||
|
||||
// Make sure the scroll target is within bounds
|
||||
targetTop = Math.max(
|
||||
Math.min(targetTop, block.scrollHeight - viewportHeight),
|
||||
0
|
||||
);
|
||||
|
||||
if (skipAnimation === true || startTop === targetTop) {
|
||||
block.scrollTop = targetTop;
|
||||
} else {
|
||||
// Don't attempt to scroll if there is no overflow
|
||||
if (block.scrollHeight <= viewportHeight) return;
|
||||
|
||||
let time = 0;
|
||||
|
||||
const animate = function () {
|
||||
time = Math.min(time + 0.02, 1);
|
||||
|
||||
// Update our eased scroll position
|
||||
block.scrollTop =
|
||||
startTop + (targetTop - startTop) * easeInOutQuart(time);
|
||||
|
||||
// Keep animating unless we've reached the end
|
||||
if (time < 1) {
|
||||
scrollState.animationFrameID = requestAnimationFrame(animate);
|
||||
}
|
||||
};
|
||||
|
||||
animate();
|
||||
}
|
||||
}
|
||||
|
||||
function getHighlightedLineBounds(block) {
|
||||
const highlightedLines = block.querySelectorAll(".highlight-line");
|
||||
if (highlightedLines.length === 0) {
|
||||
return { top: 0, bottom: 0 };
|
||||
} else {
|
||||
const firstHighlight = highlightedLines[0];
|
||||
const lastHighlight = highlightedLines[highlightedLines.length - 1];
|
||||
|
||||
return {
|
||||
top: firstHighlight.offsetTop,
|
||||
bottom: lastHighlight.offsetTop + lastHighlight.offsetHeight,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The easing function used when scrolling.
|
||||
*/
|
||||
function easeInOutQuart(t) {
|
||||
// easeInOutQuart
|
||||
return t < 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t;
|
||||
}
|
||||
|
||||
function splitLineNumbers(lineNumbersAttr) {
|
||||
// remove space
|
||||
lineNumbersAttr = lineNumbersAttr.replace("/s/g", "");
|
||||
// seperate steps (for fragment)
|
||||
lineNumbersAttr = lineNumbersAttr.split(delimiters.step);
|
||||
|
||||
// for each step, calculate first and last line, if any
|
||||
return lineNumbersAttr.map((highlights) => {
|
||||
// detect lines
|
||||
const lines = highlights.split(delimiters.line);
|
||||
return lines.map((range) => {
|
||||
if (/^[\d-]+$/.test(range)) {
|
||||
range = range.split(delimiters.lineRange);
|
||||
const firstLine = parseInt(range[0], 10);
|
||||
const lastLine = range[1] ? parseInt(range[1], 10) : undefined;
|
||||
return {
|
||||
first: firstLine,
|
||||
last: lastLine,
|
||||
};
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function joinLineNumbers(splittedLineNumbers) {
|
||||
return splittedLineNumbers
|
||||
.map(function (highlights) {
|
||||
return highlights
|
||||
.map(function (highlight) {
|
||||
// Line range
|
||||
if (typeof highlight.last === "number") {
|
||||
return highlight.first + delimiters.lineRange + highlight.last;
|
||||
}
|
||||
// Single line
|
||||
else if (typeof highlight.first === "number") {
|
||||
return highlight.first;
|
||||
}
|
||||
// All lines
|
||||
else {
|
||||
return "";
|
||||
}
|
||||
})
|
||||
.join(delimiters.line);
|
||||
})
|
||||
.join(delimiters.step);
|
||||
}
|
||||
|
||||
return {
|
||||
id: "quarto-line-highlight",
|
||||
init: function (deck) {
|
||||
initQuartoLineHighlight(deck);
|
||||
|
||||
// If we're printing to PDF, scroll the code highlights of
|
||||
// all blocks in the deck into view at once
|
||||
deck.on("pdf-ready", function () {
|
||||
[].slice
|
||||
.call(
|
||||
deck
|
||||
.getRevealElement()
|
||||
.querySelectorAll(
|
||||
"pre code[data-code-line-numbers].current-fragment"
|
||||
)
|
||||
)
|
||||
.forEach(function (block) {
|
||||
scrollHighlightedLineIntoView(block, {}, true);
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
# adapted from https://github.com/hakimel/reveal.js/tree/master/plugin/highlight
|
||||
name: QuartoLineHighlight
|
||||
script: line-highlight.js
|
||||
stylesheet: line-highlight.css
|
||||
109
Lecture_files/libs/revealjs/plugin/quarto-support/footer.css
Normal file
@@ -0,0 +1,109 @@
|
||||
.reveal .slide-logo {
|
||||
display: block;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
right: 12px;
|
||||
max-height: 2.2rem;
|
||||
height: 100%;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.reveal .footer {
|
||||
display: block;
|
||||
position: fixed;
|
||||
bottom: 18px;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.reveal .footer > * {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.reveal .slide .footer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.reveal .slide-number {
|
||||
bottom: 10px;
|
||||
right: 10px;
|
||||
font-size: 16px;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.reveal.has-logo .slide-number {
|
||||
bottom: initial;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
}
|
||||
|
||||
.reveal .slide-number .slide-number-delimiter {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.reveal .slide-menu-button {
|
||||
left: 8px;
|
||||
bottom: 8px;
|
||||
}
|
||||
|
||||
.reveal .slide-chalkboard-buttons {
|
||||
position: fixed;
|
||||
left: 12px;
|
||||
bottom: 8px;
|
||||
z-index: 30;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.reveal .slide-chalkboard-buttons.slide-menu-offset {
|
||||
left: 54px;
|
||||
}
|
||||
|
||||
.reveal .slide-chalkboard-buttons > span {
|
||||
margin-right: 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
.reveal .slide-logo {
|
||||
max-height: 1.1rem;
|
||||
bottom: -2px;
|
||||
right: 10px;
|
||||
}
|
||||
.reveal .footer {
|
||||
font-size: 14px;
|
||||
bottom: 12px;
|
||||
}
|
||||
.reveal .slide-number {
|
||||
font-size: 12px;
|
||||
bottom: 7px;
|
||||
}
|
||||
.reveal .slide-menu-button .fas::before {
|
||||
height: 1.3rem;
|
||||
width: 1.3rem;
|
||||
vertical-align: -0.125em;
|
||||
background-size: 1.3rem 1.3rem;
|
||||
}
|
||||
|
||||
.reveal .slide-chalkboard-buttons .fas::before {
|
||||
height: 0.95rem;
|
||||
width: 0.95rem;
|
||||
background-size: 0.95rem 0.95rem;
|
||||
vertical-align: -0em;
|
||||
}
|
||||
|
||||
.reveal .slide-chalkboard-buttons.slide-menu-offset {
|
||||
left: 36px;
|
||||
}
|
||||
.reveal .slide-chalkboard-buttons > span {
|
||||
margin-right: 9px;
|
||||
}
|
||||
}
|
||||
|
||||
html.print-pdf .reveal .slide-menu-button,
|
||||
html.print-pdf .reveal .slide-chalkboard-buttons {
|
||||
display: none;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
name: QuartoSupport
|
||||
script: support.js
|
||||
stylesheet: footer.css
|
||||
config:
|
||||
smaller: false
|
||||
290
Lecture_files/libs/revealjs/plugin/quarto-support/support.js
Normal file
@@ -0,0 +1,290 @@
|
||||
// catch all plugin for various quarto features
|
||||
window.QuartoSupport = function () {
|
||||
function isPrintView() {
|
||||
return /print-pdf/gi.test(window.location.search);
|
||||
}
|
||||
|
||||
// implement controlsAudo
|
||||
function controlsAuto(deck) {
|
||||
const config = deck.getConfig();
|
||||
if (config.controlsAuto === true) {
|
||||
const iframe = window.location !== window.parent.location;
|
||||
const localhost =
|
||||
window.location.hostname === "localhost" ||
|
||||
window.location.hostname === "127.0.0.1";
|
||||
deck.configure({
|
||||
controls:
|
||||
(iframe && !localhost) ||
|
||||
(deck.hasVerticalSlides() && config.navigationMode !== "linear"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// helper to provide event handlers for all links in a container
|
||||
function handleLinkClickEvents(deck, container) {
|
||||
Array.from(container.querySelectorAll("a")).forEach((el) => {
|
||||
const url = el.getAttribute("href");
|
||||
if (/^(http|www)/gi.test(url)) {
|
||||
el.addEventListener(
|
||||
"click",
|
||||
(ev) => {
|
||||
const fullscreen = !!window.document.fullscreen;
|
||||
const dataPreviewLink = el.getAttribute("data-preview-link");
|
||||
|
||||
// if there is a local specifcation then use that
|
||||
if (dataPreviewLink) {
|
||||
if (
|
||||
dataPreviewLink === "true" ||
|
||||
(dataPreviewLink === "auto" && fullscreen)
|
||||
) {
|
||||
ev.preventDefault();
|
||||
deck.showPreview(url);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
const previewLinks = !!deck.getConfig().previewLinks;
|
||||
const previewLinksAuto =
|
||||
deck.getConfig().previewLinksAuto === true;
|
||||
if (previewLinks == true || (previewLinksAuto && fullscreen)) {
|
||||
ev.preventDefault();
|
||||
deck.showPreview(url);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// if the deck is in an iframe we want to open it externally
|
||||
// (don't do this when in vscode though as it has its own
|
||||
// handler for opening links externally that will be play)
|
||||
const iframe = window.location !== window.parent.location;
|
||||
if (
|
||||
iframe &&
|
||||
!window.location.search.includes("quartoPreviewReqId=")
|
||||
) {
|
||||
ev.preventDefault();
|
||||
ev.stopImmediatePropagation();
|
||||
window.open(url, "_blank");
|
||||
return false;
|
||||
}
|
||||
|
||||
// if the user has set data-preview-link to "auto" we need to handle the event
|
||||
// (because reveal will interpret "auto" as true)
|
||||
if (dataPreviewLink === "auto") {
|
||||
ev.preventDefault();
|
||||
ev.stopImmediatePropagation();
|
||||
const target =
|
||||
el.getAttribute("target") ||
|
||||
(ev.ctrlKey || ev.metaKey ? "_blank" : "");
|
||||
if (target) {
|
||||
window.open(url, target);
|
||||
} else {
|
||||
window.location.href = url;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
},
|
||||
false
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// implement previewLinksAuto
|
||||
function previewLinksAuto(deck) {
|
||||
handleLinkClickEvents(deck, deck.getRevealElement());
|
||||
}
|
||||
|
||||
// apply styles
|
||||
function applyGlobalStyles(deck) {
|
||||
if (deck.getConfig()["smaller"] === true) {
|
||||
const revealParent = deck.getRevealElement();
|
||||
revealParent.classList.add("smaller");
|
||||
}
|
||||
}
|
||||
|
||||
// add logo image
|
||||
function addLogoImage(deck) {
|
||||
const revealParent = deck.getRevealElement();
|
||||
const logoImg = document.querySelector(".slide-logo");
|
||||
if (logoImg) {
|
||||
revealParent.appendChild(logoImg);
|
||||
revealParent.classList.add("has-logo");
|
||||
}
|
||||
}
|
||||
|
||||
// add footer text
|
||||
function addFooter(deck) {
|
||||
const revealParent = deck.getRevealElement();
|
||||
const defaultFooterDiv = document.querySelector(".footer-default");
|
||||
if (defaultFooterDiv) {
|
||||
revealParent.appendChild(defaultFooterDiv);
|
||||
handleLinkClickEvents(deck, defaultFooterDiv);
|
||||
if (!isPrintView()) {
|
||||
deck.on("slidechanged", function (ev) {
|
||||
const prevSlideFooter = document.querySelector(
|
||||
".reveal > .footer:not(.footer-default)"
|
||||
);
|
||||
if (prevSlideFooter) {
|
||||
prevSlideFooter.remove();
|
||||
}
|
||||
const currentSlideFooter = ev.currentSlide.querySelector(".footer");
|
||||
if (currentSlideFooter) {
|
||||
defaultFooterDiv.style.display = "none";
|
||||
const slideFooter = currentSlideFooter.cloneNode(true);
|
||||
handleLinkClickEvents(deck, slideFooter);
|
||||
deck.getRevealElement().appendChild(slideFooter);
|
||||
} else {
|
||||
defaultFooterDiv.style.display = "block";
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add chalkboard buttons
|
||||
function addChalkboardButtons(deck) {
|
||||
const chalkboard = deck.getPlugin("RevealChalkboard");
|
||||
if (chalkboard && !isPrintView()) {
|
||||
const revealParent = deck.getRevealElement();
|
||||
const chalkboardDiv = document.createElement("div");
|
||||
chalkboardDiv.classList.add("slide-chalkboard-buttons");
|
||||
if (document.querySelector(".slide-menu-button")) {
|
||||
chalkboardDiv.classList.add("slide-menu-offset");
|
||||
}
|
||||
// add buttons
|
||||
const buttons = [
|
||||
{
|
||||
icon: "easel2",
|
||||
title: "Toggle Chalkboard (b)",
|
||||
onclick: chalkboard.toggleChalkboard,
|
||||
},
|
||||
{
|
||||
icon: "brush",
|
||||
title: "Toggle Notes Canvas (c)",
|
||||
onclick: chalkboard.toggleNotesCanvas,
|
||||
},
|
||||
];
|
||||
buttons.forEach(function (button) {
|
||||
const span = document.createElement("span");
|
||||
span.title = button.title;
|
||||
const icon = document.createElement("i");
|
||||
icon.classList.add("fas");
|
||||
icon.classList.add("fa-" + button.icon);
|
||||
span.appendChild(icon);
|
||||
span.onclick = function (event) {
|
||||
event.preventDefault();
|
||||
button.onclick();
|
||||
};
|
||||
chalkboardDiv.appendChild(span);
|
||||
});
|
||||
revealParent.appendChild(chalkboardDiv);
|
||||
const config = deck.getConfig();
|
||||
if (!config.chalkboard.buttons) {
|
||||
chalkboardDiv.classList.add("hidden");
|
||||
}
|
||||
|
||||
// show and hide chalkboard buttons on slidechange
|
||||
deck.on("slidechanged", function (ev) {
|
||||
const config = deck.getConfig();
|
||||
let buttons = !!config.chalkboard.buttons;
|
||||
const slideButtons = ev.currentSlide.getAttribute(
|
||||
"data-chalkboard-buttons"
|
||||
);
|
||||
if (slideButtons) {
|
||||
if (slideButtons === "true" || slideButtons === "1") {
|
||||
buttons = true;
|
||||
} else if (slideButtons === "false" || slideButtons === "0") {
|
||||
buttons = false;
|
||||
}
|
||||
}
|
||||
if (buttons) {
|
||||
chalkboardDiv.classList.remove("hidden");
|
||||
} else {
|
||||
chalkboardDiv.classList.add("hidden");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function handleTabbyClicks() {
|
||||
const tabs = document.querySelectorAll(".panel-tabset-tabby > li > a");
|
||||
for (let i = 0; i < tabs.length; i++) {
|
||||
const tab = tabs[i];
|
||||
tab.onclick = function (ev) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
return false;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function fixupForPrint(deck) {
|
||||
if (isPrintView()) {
|
||||
const slides = deck.getSlides();
|
||||
slides.forEach(function (slide) {
|
||||
slide.removeAttribute("data-auto-animate");
|
||||
});
|
||||
window.document.querySelectorAll(".hljs").forEach(function (el) {
|
||||
el.classList.remove("hljs");
|
||||
});
|
||||
window.document.querySelectorAll(".hljs-ln-code").forEach(function (el) {
|
||||
el.classList.remove("hljs-ln-code");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function handleSlideChanges(deck) {
|
||||
// dispatch for htmlwidgets
|
||||
const fireSlideEnter = () => {
|
||||
const event = window.document.createEvent("Event");
|
||||
event.initEvent("slideenter", true, true);
|
||||
window.document.dispatchEvent(event);
|
||||
};
|
||||
|
||||
const fireSlideChanged = (previousSlide, currentSlide) => {
|
||||
fireSlideEnter();
|
||||
|
||||
// dispatch for shiny
|
||||
if (window.jQuery) {
|
||||
if (previousSlide) {
|
||||
window.jQuery(previousSlide).trigger("hidden");
|
||||
}
|
||||
if (currentSlide) {
|
||||
window.jQuery(currentSlide).trigger("shown");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// fire slideEnter for tabby tab activations (for htmlwidget resize behavior)
|
||||
document.addEventListener("tabby", fireSlideEnter, false);
|
||||
|
||||
deck.on("slidechanged", function (event) {
|
||||
fireSlideChanged(event.previousSlide, event.currentSlide);
|
||||
});
|
||||
}
|
||||
|
||||
function workaroundMermaidDistance(deck) {
|
||||
if (window.document.querySelector("pre.mermaid-js")) {
|
||||
const slideCount = deck.getTotalSlides();
|
||||
deck.configure({
|
||||
mobileViewDistance: slideCount,
|
||||
viewDistance: slideCount,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
id: "quarto-support",
|
||||
init: function (deck) {
|
||||
controlsAuto(deck);
|
||||
previewLinksAuto(deck);
|
||||
fixupForPrint(deck);
|
||||
applyGlobalStyles(deck);
|
||||
addLogoImage(deck);
|
||||
addFooter(deck);
|
||||
addChalkboardButtons(deck);
|
||||
handleTabbyClicks();
|
||||
handleSlideChanges(deck);
|
||||
workaroundMermaidDistance(deck);
|
||||
},
|
||||
};
|
||||
};
|
||||
346
Lecture_files/libs/revealjs/plugin/reveal-menu/menu.css
Normal file
@@ -0,0 +1,346 @@
|
||||
.slide-menu-wrapper {
|
||||
font-family: 'Source Sans Pro', Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-menu {
|
||||
background-color: #333;
|
||||
z-index: 200;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 300px;
|
||||
height: 100%;
|
||||
/*overflow-y: scroll;*/
|
||||
transition: transform 0.3s;
|
||||
font-size: 16px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-menu.slide-menu--wide {
|
||||
width: 500px;
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-menu.slide-menu--third {
|
||||
width: 33%;
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-menu.slide-menu--half {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-menu.slide-menu--full {
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
/*
|
||||
* Slides menu
|
||||
*/
|
||||
|
||||
.slide-menu-wrapper .slide-menu-items {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
border-bottom: solid 1px #555;
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-menu-item,
|
||||
.slide-menu-wrapper .slide-menu-item-vertical {
|
||||
display: block;
|
||||
text-align: left;
|
||||
padding: 10px 18px;
|
||||
color: #aaa;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-menu-item-vertical {
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-menu--wide .slide-menu-item-vertical,
|
||||
.slide-menu-wrapper .slide-menu--third .slide-menu-item-vertical,
|
||||
.slide-menu-wrapper .slide-menu--half .slide-menu-item-vertical,
|
||||
.slide-menu-wrapper .slide-menu--full .slide-menu-item-vertical,
|
||||
.slide-menu-wrapper .slide-menu--custom .slide-menu-item-vertical {
|
||||
padding-left: 50px;
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-menu-item {
|
||||
border-top: solid 1px #555;
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .active-menu-panel li.selected {
|
||||
background-color: #222;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .active-menu-panel li.active {
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-menu-item.no-title .slide-menu-item-title,
|
||||
.slide-menu-wrapper .slide-menu-item-vertical.no-title .slide-menu-item-title {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-menu-item-number {
|
||||
color: #999;
|
||||
padding-right: 6px;
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-menu-item i.far,
|
||||
.slide-menu-wrapper .slide-menu-item i.fas,
|
||||
.slide-menu-wrapper .slide-menu-item-vertical i.far,
|
||||
.slide-menu-wrapper .slide-menu-item-vertical i.fas,
|
||||
.slide-menu-wrapper .slide-menu-item svg.svg-inline--fa,
|
||||
.slide-menu-wrapper .slide-menu-item-vertical svg.svg-inline--fa {
|
||||
padding-right: 12px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-menu-item.past i.fas.past,
|
||||
.slide-menu-wrapper .slide-menu-item-vertical.past i.fas.past,
|
||||
.slide-menu-wrapper .slide-menu-item.active i.fas.active,
|
||||
.slide-menu-wrapper .slide-menu-item-vertical.active i.fas.active,
|
||||
.slide-menu-wrapper .slide-menu-item.future i.far.future,
|
||||
.slide-menu-wrapper .slide-menu-item-vertical.future i.far.future,
|
||||
.slide-menu-wrapper .slide-menu-item.past svg.svg-inline--fa.past,
|
||||
.slide-menu-wrapper .slide-menu-item-vertical.past svg.svg-inline--fa.past,
|
||||
.slide-menu-wrapper .slide-menu-item.active svg.svg-inline--fa.active,
|
||||
.slide-menu-wrapper .slide-menu-item-vertical.active svg.svg-inline--fa.active,
|
||||
.slide-menu-wrapper .slide-menu-item.future svg.svg-inline--fa.future,
|
||||
.slide-menu-wrapper .slide-menu-item-vertical.future svg.svg-inline--fa.future {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-menu-item.past i.fas.past,
|
||||
.slide-menu-wrapper .slide-menu-item-vertical.past i.fas.past,
|
||||
.slide-menu-wrapper .slide-menu-item.future i.far.future,
|
||||
.slide-menu-wrapper .slide-menu-item-vertical.future i.far.future,
|
||||
.slide-menu-wrapper .slide-menu-item.past svg.svg-inline--fa.past,
|
||||
.slide-menu-wrapper .slide-menu-item-vertical.past svg.svg-inline--fa.past,
|
||||
.slide-menu-wrapper .slide-menu-item.future svg.svg-inline--fa.future,
|
||||
.slide-menu-wrapper .slide-menu-item-vertical.future svg.svg-inline--fa.future {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-menu-item.active i.fas.active,
|
||||
.slide-menu-wrapper .slide-menu-item-vertical.active i.fas.active,
|
||||
.slide-menu-wrapper .slide-menu-item.active svg.svg-inline--fa.active,
|
||||
.slide-menu-wrapper .slide-menu-item-vertical.active svg.svg-inline--fa.active {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-menu--left {
|
||||
left: 0;
|
||||
-webkit-transform: translateX(-100%);
|
||||
-ms-transform: translateX(-100%);
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-menu--left.active {
|
||||
-webkit-transform: translateX(0);
|
||||
-ms-transform: translateX(0);
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-menu--right {
|
||||
right: 0;
|
||||
-webkit-transform: translateX(100%);
|
||||
-ms-transform: translateX(100%);
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-menu--right.active {
|
||||
-webkit-transform: translateX(0);
|
||||
-ms-transform: translateX(0);
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.slide-menu-wrapper {
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
|
||||
/*
|
||||
* Toolbar
|
||||
*/
|
||||
.slide-menu-wrapper .slide-menu-toolbar {
|
||||
height: 60px;
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
display: table;
|
||||
table-layout: fixed; /* ensures equal width */
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border-bottom: solid 2px #666;
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-menu-toolbar > li {
|
||||
display: table-cell;
|
||||
line-height: 150%;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
color: #aaa;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-menu-toolbar > li.toolbar-panel-button i,
|
||||
.slide-menu-wrapper
|
||||
.slide-menu-toolbar
|
||||
> li.toolbar-panel-button
|
||||
svg.svg-inline--fa {
|
||||
font-size: 1.7em;
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-menu-toolbar > li.active-toolbar-button {
|
||||
color: white;
|
||||
text-shadow: 0 1px black;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.slide-menu-toolbar > li.toolbar-panel-button:hover {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.slide-menu-toolbar
|
||||
> li.toolbar-panel-button:hover
|
||||
span.slide-menu-toolbar-label,
|
||||
.slide-menu-wrapper
|
||||
.slide-menu-toolbar
|
||||
> li.active-toolbar-button
|
||||
span.slide-menu-toolbar-label {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
/*
|
||||
* Panels
|
||||
*/
|
||||
.slide-menu-wrapper .slide-menu-panel {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
visibility: hidden;
|
||||
height: calc(100% - 60px);
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-menu-panel.active-menu-panel {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-menu-panel h1,
|
||||
.slide-menu-wrapper .slide-menu-panel h2,
|
||||
.slide-menu-wrapper .slide-menu-panel h3,
|
||||
.slide-menu-wrapper .slide-menu-panel h4,
|
||||
.slide-menu-wrapper .slide-menu-panel h5,
|
||||
.slide-menu-wrapper .slide-menu-panel h6 {
|
||||
margin: 20px 0 10px 0;
|
||||
color: #fff;
|
||||
line-height: 1.2;
|
||||
letter-spacing: normal;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-menu-panel h1 {
|
||||
font-size: 1.6em;
|
||||
}
|
||||
.slide-menu-wrapper .slide-menu-panel h2 {
|
||||
font-size: 1.4em;
|
||||
}
|
||||
.slide-menu-wrapper .slide-menu-panel h3 {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
.slide-menu-wrapper .slide-menu-panel h4 {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
.slide-menu-wrapper .slide-menu-panel h5 {
|
||||
font-size: 1em;
|
||||
}
|
||||
.slide-menu-wrapper .slide-menu-panel h6 {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-menu-panel p {
|
||||
margin: 10px 0 5px 0;
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-menu-panel a {
|
||||
color: #ccc;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-menu-panel a:hover {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-menu-item a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-menu-custom-panel {
|
||||
width: calc(100% - 20px);
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-menu-custom-panel .slide-menu-items {
|
||||
width: calc(100% + 20px);
|
||||
margin-left: -10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
/*
|
||||
* Theme and Transitions buttons
|
||||
*/
|
||||
|
||||
.slide-menu-wrapper div[data-panel='Themes'] li,
|
||||
.slide-menu-wrapper div[data-panel='Transitions'] li {
|
||||
display: block;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
color: #848484;
|
||||
}
|
||||
|
||||
/*
|
||||
* Menu controls
|
||||
*/
|
||||
.reveal .slide-menu-button {
|
||||
position: fixed;
|
||||
left: 30px;
|
||||
bottom: 30px;
|
||||
z-index: 30;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
/*
|
||||
* Menu overlay
|
||||
*/
|
||||
|
||||
.slide-menu-wrapper .slide-menu-overlay {
|
||||
position: fixed;
|
||||
z-index: 199;
|
||||
top: 0;
|
||||
left: 0;
|
||||
overflow: hidden;
|
||||
width: 0;
|
||||
height: 0;
|
||||
background-color: #000;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s, width 0s 0.3s, height 0s 0.3s;
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-menu-overlay.active {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0.7;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
/*
|
||||
* Hide menu for pdf printing
|
||||
*/
|
||||
body.print-pdf .slide-menu-wrapper .slide-menu,
|
||||
body.print-pdf .reveal .slide-menu-button,
|
||||
body.print-pdf .slide-menu-wrapper .slide-menu-overlay {
|
||||
display: none;
|
||||
}
|
||||
1
Lecture_files/libs/revealjs/plugin/reveal-menu/menu.js
Normal file
@@ -0,0 +1,9 @@
|
||||
name: RevealMenu
|
||||
script: [menu.js, quarto-menu.js]
|
||||
stylesheet: [menu.css, quarto-menu.css]
|
||||
config:
|
||||
menu:
|
||||
side: "left"
|
||||
useTextContentForMissingTitles: true
|
||||
markers: false
|
||||
loadIcons: false
|
||||
@@ -0,0 +1,68 @@
|
||||
.slide-menu-wrapper .slide-tool-item {
|
||||
display: block;
|
||||
text-align: left;
|
||||
padding: 10px 18px;
|
||||
color: #aaa;
|
||||
cursor: pointer;
|
||||
border-top: solid 1px #555;
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-tool-item a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-tool-item kbd {
|
||||
font-family: monospace;
|
||||
margin-right: 10px;
|
||||
padding: 3px 8px;
|
||||
color: inherit;
|
||||
border: 1px solid;
|
||||
border-radius: 5px;
|
||||
border-color: #555;
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-menu-toolbar > li.active-toolbar-button {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.reveal .slide-menu-button {
|
||||
left: 8px;
|
||||
bottom: 8px;
|
||||
}
|
||||
|
||||
.reveal .slide-menu-button .fas::before,
|
||||
.reveal .slide-chalkboard-buttons .fas::before,
|
||||
.slide-menu-wrapper .slide-menu-toolbar .fas::before {
|
||||
display: inline-block;
|
||||
height: 2.2rem;
|
||||
width: 2.2rem;
|
||||
content: "";
|
||||
vertical-align: -0.125em;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 2.2rem 2.2rem;
|
||||
}
|
||||
|
||||
.reveal .slide-chalkboard-buttons .fas::before {
|
||||
height: 1.45rem;
|
||||
width: 1.45rem;
|
||||
background-size: 1.45rem 1.45rem;
|
||||
vertical-align: 0.1em;
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-menu-toolbar .fas::before {
|
||||
height: 1.8rem;
|
||||
width: 1.8rem;
|
||||
background-size: 1.8rem 1.8rem;
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-menu-toolbar .fa-images::before {
|
||||
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="white" class="bi bi-images" viewBox="0 0 16 16"><path d="M4.502 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z"/><path d="M14.002 13a2 2 0 0 1-2 2h-10a2 2 0 0 1-2-2V5A2 2 0 0 1 2 3a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v8a2 2 0 0 1-1.998 2zM14 2H4a1 1 0 0 0-1 1h9.002a2 2 0 0 1 2 2v7A1 1 0 0 0 15 11V3a1 1 0 0 0-1-1zM2.002 4a1 1 0 0 0-1 1v8l2.646-2.354a.5.5 0 0 1 .63-.062l2.66 1.773 3.71-3.71a.5.5 0 0 1 .577-.094l1.777 1.947V5a1 1 0 0 0-1-1h-10z"/></svg>');
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-menu-toolbar .fa-gear::before {
|
||||
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="white" class="bi bi-gear" viewBox="0 0 16 16"><path d="M8 4.754a3.246 3.246 0 1 0 0 6.492 3.246 3.246 0 0 0 0-6.492zM5.754 8a2.246 2.246 0 1 1 4.492 0 2.246 2.246 0 0 1-4.492 0z"/><path d="M9.796 1.343c-.527-1.79-3.065-1.79-3.592 0l-.094.319a.873.873 0 0 1-1.255.52l-.292-.16c-1.64-.892-3.433.902-2.54 2.541l.159.292a.873.873 0 0 1-.52 1.255l-.319.094c-1.79.527-1.79 3.065 0 3.592l.319.094a.873.873 0 0 1 .52 1.255l-.16.292c-.892 1.64.901 3.434 2.541 2.54l.292-.159a.873.873 0 0 1 1.255.52l.094.319c.527 1.79 3.065 1.79 3.592 0l.094-.319a.873.873 0 0 1 1.255-.52l.292.16c1.64.893 3.434-.902 2.54-2.541l-.159-.292a.873.873 0 0 1 .52-1.255l.319-.094c1.79-.527 1.79-3.065 0-3.592l-.319-.094a.873.873 0 0 1-.52-1.255l.16-.292c.893-1.64-.902-3.433-2.541-2.54l-.292.159a.873.873 0 0 1-1.255-.52l-.094-.319zm-2.633.283c.246-.835 1.428-.835 1.674 0l.094.319a1.873 1.873 0 0 0 2.693 1.115l.291-.16c.764-.415 1.6.42 1.184 1.185l-.159.292a1.873 1.873 0 0 0 1.116 2.692l.318.094c.835.246.835 1.428 0 1.674l-.319.094a1.873 1.873 0 0 0-1.115 2.693l.16.291c.415.764-.42 1.6-1.185 1.184l-.291-.159a1.873 1.873 0 0 0-2.693 1.116l-.094.318c-.246.835-1.428.835-1.674 0l-.094-.319a1.873 1.873 0 0 0-2.692-1.115l-.292.16c-.764.415-1.6-.42-1.184-1.185l.159-.291A1.873 1.873 0 0 0 1.945 8.93l-.319-.094c-.835-.246-.835-1.428 0-1.674l.319-.094A1.873 1.873 0 0 0 3.06 4.377l-.16-.292c-.415-.764.42-1.6 1.185-1.184l.292.159a1.873 1.873 0 0 0 2.692-1.115l.094-.319z"/></svg>');
|
||||
}
|
||||
|
||||
.slide-menu-wrapper .slide-menu-toolbar .fa-times::before {
|
||||
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="white" class="bi bi-x" viewBox="0 0 16 16"><path d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z"/></svg>');
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
window.revealMenuToolHandler = function (handler) {
|
||||
return function (event) {
|
||||
event.preventDefault();
|
||||
handler();
|
||||
Reveal.getPlugin("menu").closeMenu();
|
||||
};
|
||||
};
|
||||
|
||||
window.RevealMenuToolHandlers = {
|
||||
fullscreen: revealMenuToolHandler(function () {
|
||||
const element = document.documentElement;
|
||||
const requestMethod =
|
||||
element.requestFullscreen ||
|
||||
element.webkitRequestFullscreen ||
|
||||
element.webkitRequestFullScreen ||
|
||||
element.mozRequestFullScreen ||
|
||||
element.msRequestFullscreen;
|
||||
if (requestMethod) {
|
||||
requestMethod.apply(element);
|
||||
}
|
||||
}),
|
||||
speakerMode: revealMenuToolHandler(function () {
|
||||
Reveal.getPlugin("notes").open();
|
||||
}),
|
||||
keyboardHelp: revealMenuToolHandler(function () {
|
||||
Reveal.toggleHelp(true);
|
||||
}),
|
||||
overview: revealMenuToolHandler(function () {
|
||||
Reveal.toggleOverview(true);
|
||||
}),
|
||||
toggleChalkboard: revealMenuToolHandler(function () {
|
||||
RevealChalkboard.toggleChalkboard();
|
||||
}),
|
||||
toggleNotesCanvas: revealMenuToolHandler(function () {
|
||||
RevealChalkboard.toggleNotesCanvas();
|
||||
}),
|
||||
downloadDrawings: revealMenuToolHandler(function () {
|
||||
RevealChalkboard.download();
|
||||
}),
|
||||
};
|
||||
243
Lecture_files/libs/revealjs/plugin/search/plugin.js
Normal file
@@ -0,0 +1,243 @@
|
||||
/*!
|
||||
* Handles finding a text string anywhere in the slides and showing the next occurrence to the user
|
||||
* by navigatating to that slide and highlighting it.
|
||||
*
|
||||
* @author Jon Snyder <snyder.jon@gmail.com>, February 2013
|
||||
*/
|
||||
|
||||
const Plugin = () => {
|
||||
|
||||
// The reveal.js instance this plugin is attached to
|
||||
let deck;
|
||||
|
||||
let searchElement;
|
||||
let searchButton;
|
||||
let searchInput;
|
||||
|
||||
let matchedSlides;
|
||||
let currentMatchedIndex;
|
||||
let searchboxDirty;
|
||||
let hilitor;
|
||||
|
||||
function render() {
|
||||
|
||||
searchElement = document.createElement( 'div' );
|
||||
searchElement.classList.add( 'searchbox' );
|
||||
searchElement.style.position = 'absolute';
|
||||
searchElement.style.top = '10px';
|
||||
searchElement.style.right = '10px';
|
||||
searchElement.style.zIndex = 10;
|
||||
|
||||
//embedded base64 search icon Designed by Sketchdock - http://www.sketchdock.com/:
|
||||
searchElement.innerHTML = `<input type="search" class="searchinput" placeholder="Search..." style="vertical-align: top;"/>
|
||||
</span>`;
|
||||
|
||||
searchInput = searchElement.querySelector( '.searchinput' );
|
||||
searchInput.style.width = '240px';
|
||||
searchInput.style.fontSize = '14px';
|
||||
searchInput.style.padding = '4px 6px';
|
||||
searchInput.style.color = '#000';
|
||||
searchInput.style.background = '#fff';
|
||||
searchInput.style.borderRadius = '2px';
|
||||
searchInput.style.border = '0';
|
||||
searchInput.style.outline = '0';
|
||||
searchInput.style.boxShadow = '0 2px 18px rgba(0, 0, 0, 0.2)';
|
||||
searchInput.style['-webkit-appearance'] = 'none';
|
||||
|
||||
deck.getRevealElement().appendChild( searchElement );
|
||||
|
||||
// searchButton.addEventListener( 'click', function(event) {
|
||||
// doSearch();
|
||||
// }, false );
|
||||
|
||||
searchInput.addEventListener( 'keyup', function( event ) {
|
||||
switch (event.keyCode) {
|
||||
case 13:
|
||||
event.preventDefault();
|
||||
doSearch();
|
||||
searchboxDirty = false;
|
||||
break;
|
||||
default:
|
||||
searchboxDirty = true;
|
||||
}
|
||||
}, false );
|
||||
|
||||
closeSearch();
|
||||
|
||||
}
|
||||
|
||||
function openSearch() {
|
||||
if( !searchElement ) render();
|
||||
|
||||
searchElement.style.display = 'inline';
|
||||
searchInput.focus();
|
||||
searchInput.select();
|
||||
}
|
||||
|
||||
function closeSearch() {
|
||||
if( !searchElement ) render();
|
||||
|
||||
searchElement.style.display = 'none';
|
||||
if(hilitor) hilitor.remove();
|
||||
}
|
||||
|
||||
function toggleSearch() {
|
||||
if( !searchElement ) render();
|
||||
|
||||
if (searchElement.style.display !== 'inline') {
|
||||
openSearch();
|
||||
}
|
||||
else {
|
||||
closeSearch();
|
||||
}
|
||||
}
|
||||
|
||||
function doSearch() {
|
||||
//if there's been a change in the search term, perform a new search:
|
||||
if (searchboxDirty) {
|
||||
var searchstring = searchInput.value;
|
||||
|
||||
if (searchstring === '') {
|
||||
if(hilitor) hilitor.remove();
|
||||
matchedSlides = null;
|
||||
}
|
||||
else {
|
||||
//find the keyword amongst the slides
|
||||
hilitor = new Hilitor("slidecontent");
|
||||
matchedSlides = hilitor.apply(searchstring);
|
||||
currentMatchedIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (matchedSlides) {
|
||||
//navigate to the next slide that has the keyword, wrapping to the first if necessary
|
||||
if (matchedSlides.length && (matchedSlides.length <= currentMatchedIndex)) {
|
||||
currentMatchedIndex = 0;
|
||||
}
|
||||
if (matchedSlides.length > currentMatchedIndex) {
|
||||
deck.slide(matchedSlides[currentMatchedIndex].h, matchedSlides[currentMatchedIndex].v);
|
||||
currentMatchedIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Original JavaScript code by Chirp Internet: www.chirp.com.au
|
||||
// Please acknowledge use of this code by including this header.
|
||||
// 2/2013 jon: modified regex to display any match, not restricted to word boundaries.
|
||||
function Hilitor(id, tag) {
|
||||
|
||||
var targetNode = document.getElementById(id) || document.body;
|
||||
var hiliteTag = tag || "EM";
|
||||
var skipTags = new RegExp("^(?:" + hiliteTag + "|SCRIPT|FORM)$");
|
||||
var colors = ["#ff6", "#a0ffff", "#9f9", "#f99", "#f6f"];
|
||||
var wordColor = [];
|
||||
var colorIdx = 0;
|
||||
var matchRegex = "";
|
||||
var matchingSlides = [];
|
||||
|
||||
this.setRegex = function(input)
|
||||
{
|
||||
input = input.replace(/^[^\w]+|[^\w]+$/g, "").replace(/[^\w'-]+/g, "|");
|
||||
matchRegex = new RegExp("(" + input + ")","i");
|
||||
}
|
||||
|
||||
this.getRegex = function()
|
||||
{
|
||||
return matchRegex.toString().replace(/^\/\\b\(|\)\\b\/i$/g, "").replace(/\|/g, " ");
|
||||
}
|
||||
|
||||
// recursively apply word highlighting
|
||||
this.hiliteWords = function(node)
|
||||
{
|
||||
if(node == undefined || !node) return;
|
||||
if(!matchRegex) return;
|
||||
if(skipTags.test(node.nodeName)) return;
|
||||
|
||||
if(node.hasChildNodes()) {
|
||||
for(var i=0; i < node.childNodes.length; i++)
|
||||
this.hiliteWords(node.childNodes[i]);
|
||||
}
|
||||
if(node.nodeType == 3) { // NODE_TEXT
|
||||
var nv, regs;
|
||||
if((nv = node.nodeValue) && (regs = matchRegex.exec(nv))) {
|
||||
//find the slide's section element and save it in our list of matching slides
|
||||
var secnode = node;
|
||||
while (secnode != null && secnode.nodeName != 'SECTION') {
|
||||
secnode = secnode.parentNode;
|
||||
}
|
||||
|
||||
var slideIndex = deck.getIndices(secnode);
|
||||
var slidelen = matchingSlides.length;
|
||||
var alreadyAdded = false;
|
||||
for (var i=0; i < slidelen; i++) {
|
||||
if ( (matchingSlides[i].h === slideIndex.h) && (matchingSlides[i].v === slideIndex.v) ) {
|
||||
alreadyAdded = true;
|
||||
}
|
||||
}
|
||||
if (! alreadyAdded) {
|
||||
matchingSlides.push(slideIndex);
|
||||
}
|
||||
|
||||
if(!wordColor[regs[0].toLowerCase()]) {
|
||||
wordColor[regs[0].toLowerCase()] = colors[colorIdx++ % colors.length];
|
||||
}
|
||||
|
||||
var match = document.createElement(hiliteTag);
|
||||
match.appendChild(document.createTextNode(regs[0]));
|
||||
match.style.backgroundColor = wordColor[regs[0].toLowerCase()];
|
||||
match.style.fontStyle = "inherit";
|
||||
match.style.color = "#000";
|
||||
|
||||
var after = node.splitText(regs.index);
|
||||
after.nodeValue = after.nodeValue.substring(regs[0].length);
|
||||
node.parentNode.insertBefore(match, after);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// remove highlighting
|
||||
this.remove = function()
|
||||
{
|
||||
var arr = document.getElementsByTagName(hiliteTag);
|
||||
var el;
|
||||
while(arr.length && (el = arr[0])) {
|
||||
el.parentNode.replaceChild(el.firstChild, el);
|
||||
}
|
||||
};
|
||||
|
||||
// start highlighting at target node
|
||||
this.apply = function(input)
|
||||
{
|
||||
if(input == undefined || !input) return;
|
||||
this.remove();
|
||||
this.setRegex(input);
|
||||
this.hiliteWords(targetNode);
|
||||
return matchingSlides;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
id: 'search',
|
||||
|
||||
init: reveal => {
|
||||
|
||||
deck = reveal;
|
||||
deck.registerKeyboardShortcut( 'CTRL + Shift + F', 'Search' );
|
||||
|
||||
document.addEventListener( 'keydown', function( event ) {
|
||||
if( event.key == "F" && (event.ctrlKey || event.metaKey) ) { //Control+Shift+f
|
||||
event.preventDefault();
|
||||
toggleSearch();
|
||||
}
|
||||
}, false );
|
||||
|
||||
},
|
||||
|
||||
open: openSearch
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
export default Plugin;
|
||||
7
Lecture_files/libs/revealjs/plugin/search/search.esm.js
Normal file
7
Lecture_files/libs/revealjs/plugin/search/search.js
Normal file
264
Lecture_files/libs/revealjs/plugin/zoom/plugin.js
Normal file
@@ -0,0 +1,264 @@
|
||||
/*!
|
||||
* reveal.js Zoom plugin
|
||||
*/
|
||||
const Plugin = {
|
||||
|
||||
id: 'zoom',
|
||||
|
||||
init: function( reveal ) {
|
||||
|
||||
reveal.getRevealElement().addEventListener( 'mousedown', function( event ) {
|
||||
var defaultModifier = /Linux/.test( window.navigator.platform ) ? 'ctrl' : 'alt';
|
||||
|
||||
var modifier = ( reveal.getConfig().zoomKey ? reveal.getConfig().zoomKey : defaultModifier ) + 'Key';
|
||||
var zoomLevel = ( reveal.getConfig().zoomLevel ? reveal.getConfig().zoomLevel : 2 );
|
||||
|
||||
if( event[ modifier ] && !reveal.isOverview() ) {
|
||||
event.preventDefault();
|
||||
|
||||
zoom.to({
|
||||
x: event.clientX,
|
||||
y: event.clientY,
|
||||
scale: zoomLevel,
|
||||
pan: false
|
||||
});
|
||||
}
|
||||
} );
|
||||
|
||||
},
|
||||
|
||||
destroy: () => {
|
||||
|
||||
zoom.reset();
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
export default () => Plugin;
|
||||
|
||||
/*!
|
||||
* zoom.js 0.3 (modified for use with reveal.js)
|
||||
* http://lab.hakim.se/zoom-js
|
||||
* MIT licensed
|
||||
*
|
||||
* Copyright (C) 2011-2014 Hakim El Hattab, http://hakim.se
|
||||
*/
|
||||
var zoom = (function(){
|
||||
|
||||
// The current zoom level (scale)
|
||||
var level = 1;
|
||||
|
||||
// The current mouse position, used for panning
|
||||
var mouseX = 0,
|
||||
mouseY = 0;
|
||||
|
||||
// Timeout before pan is activated
|
||||
var panEngageTimeout = -1,
|
||||
panUpdateInterval = -1;
|
||||
|
||||
// Check for transform support so that we can fallback otherwise
|
||||
var supportsTransforms = 'transform' in document.body.style;
|
||||
|
||||
if( supportsTransforms ) {
|
||||
// The easing that will be applied when we zoom in/out
|
||||
document.body.style.transition = 'transform 0.8s ease';
|
||||
}
|
||||
|
||||
// Zoom out if the user hits escape
|
||||
document.addEventListener( 'keyup', function( event ) {
|
||||
if( level !== 1 && event.keyCode === 27 ) {
|
||||
zoom.out();
|
||||
}
|
||||
} );
|
||||
|
||||
// Monitor mouse movement for panning
|
||||
document.addEventListener( 'mousemove', function( event ) {
|
||||
if( level !== 1 ) {
|
||||
mouseX = event.clientX;
|
||||
mouseY = event.clientY;
|
||||
}
|
||||
} );
|
||||
|
||||
/**
|
||||
* Applies the CSS required to zoom in, prefers the use of CSS3
|
||||
* transforms but falls back on zoom for IE.
|
||||
*
|
||||
* @param {Object} rect
|
||||
* @param {Number} scale
|
||||
*/
|
||||
function magnify( rect, scale ) {
|
||||
|
||||
var scrollOffset = getScrollOffset();
|
||||
|
||||
// Ensure a width/height is set
|
||||
rect.width = rect.width || 1;
|
||||
rect.height = rect.height || 1;
|
||||
|
||||
// Center the rect within the zoomed viewport
|
||||
rect.x -= ( window.innerWidth - ( rect.width * scale ) ) / 2;
|
||||
rect.y -= ( window.innerHeight - ( rect.height * scale ) ) / 2;
|
||||
|
||||
if( supportsTransforms ) {
|
||||
// Reset
|
||||
if( scale === 1 ) {
|
||||
document.body.style.transform = '';
|
||||
}
|
||||
// Scale
|
||||
else {
|
||||
var origin = scrollOffset.x +'px '+ scrollOffset.y +'px',
|
||||
transform = 'translate('+ -rect.x +'px,'+ -rect.y +'px) scale('+ scale +')';
|
||||
|
||||
document.body.style.transformOrigin = origin;
|
||||
document.body.style.transform = transform;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Reset
|
||||
if( scale === 1 ) {
|
||||
document.body.style.position = '';
|
||||
document.body.style.left = '';
|
||||
document.body.style.top = '';
|
||||
document.body.style.width = '';
|
||||
document.body.style.height = '';
|
||||
document.body.style.zoom = '';
|
||||
}
|
||||
// Scale
|
||||
else {
|
||||
document.body.style.position = 'relative';
|
||||
document.body.style.left = ( - ( scrollOffset.x + rect.x ) / scale ) + 'px';
|
||||
document.body.style.top = ( - ( scrollOffset.y + rect.y ) / scale ) + 'px';
|
||||
document.body.style.width = ( scale * 100 ) + '%';
|
||||
document.body.style.height = ( scale * 100 ) + '%';
|
||||
document.body.style.zoom = scale;
|
||||
}
|
||||
}
|
||||
|
||||
level = scale;
|
||||
|
||||
if( document.documentElement.classList ) {
|
||||
if( level !== 1 ) {
|
||||
document.documentElement.classList.add( 'zoomed' );
|
||||
}
|
||||
else {
|
||||
document.documentElement.classList.remove( 'zoomed' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pan the document when the mosue cursor approaches the edges
|
||||
* of the window.
|
||||
*/
|
||||
function pan() {
|
||||
var range = 0.12,
|
||||
rangeX = window.innerWidth * range,
|
||||
rangeY = window.innerHeight * range,
|
||||
scrollOffset = getScrollOffset();
|
||||
|
||||
// Up
|
||||
if( mouseY < rangeY ) {
|
||||
window.scroll( scrollOffset.x, scrollOffset.y - ( 1 - ( mouseY / rangeY ) ) * ( 14 / level ) );
|
||||
}
|
||||
// Down
|
||||
else if( mouseY > window.innerHeight - rangeY ) {
|
||||
window.scroll( scrollOffset.x, scrollOffset.y + ( 1 - ( window.innerHeight - mouseY ) / rangeY ) * ( 14 / level ) );
|
||||
}
|
||||
|
||||
// Left
|
||||
if( mouseX < rangeX ) {
|
||||
window.scroll( scrollOffset.x - ( 1 - ( mouseX / rangeX ) ) * ( 14 / level ), scrollOffset.y );
|
||||
}
|
||||
// Right
|
||||
else if( mouseX > window.innerWidth - rangeX ) {
|
||||
window.scroll( scrollOffset.x + ( 1 - ( window.innerWidth - mouseX ) / rangeX ) * ( 14 / level ), scrollOffset.y );
|
||||
}
|
||||
}
|
||||
|
||||
function getScrollOffset() {
|
||||
return {
|
||||
x: window.scrollX !== undefined ? window.scrollX : window.pageXOffset,
|
||||
y: window.scrollY !== undefined ? window.scrollY : window.pageYOffset
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
/**
|
||||
* Zooms in on either a rectangle or HTML element.
|
||||
*
|
||||
* @param {Object} options
|
||||
* - element: HTML element to zoom in on
|
||||
* OR
|
||||
* - x/y: coordinates in non-transformed space to zoom in on
|
||||
* - width/height: the portion of the screen to zoom in on
|
||||
* - scale: can be used instead of width/height to explicitly set scale
|
||||
*/
|
||||
to: function( options ) {
|
||||
|
||||
// Due to an implementation limitation we can't zoom in
|
||||
// to another element without zooming out first
|
||||
if( level !== 1 ) {
|
||||
zoom.out();
|
||||
}
|
||||
else {
|
||||
options.x = options.x || 0;
|
||||
options.y = options.y || 0;
|
||||
|
||||
// If an element is set, that takes precedence
|
||||
if( !!options.element ) {
|
||||
// Space around the zoomed in element to leave on screen
|
||||
var padding = 20;
|
||||
var bounds = options.element.getBoundingClientRect();
|
||||
|
||||
options.x = bounds.left - padding;
|
||||
options.y = bounds.top - padding;
|
||||
options.width = bounds.width + ( padding * 2 );
|
||||
options.height = bounds.height + ( padding * 2 );
|
||||
}
|
||||
|
||||
// If width/height values are set, calculate scale from those values
|
||||
if( options.width !== undefined && options.height !== undefined ) {
|
||||
options.scale = Math.max( Math.min( window.innerWidth / options.width, window.innerHeight / options.height ), 1 );
|
||||
}
|
||||
|
||||
if( options.scale > 1 ) {
|
||||
options.x *= options.scale;
|
||||
options.y *= options.scale;
|
||||
|
||||
magnify( options, options.scale );
|
||||
|
||||
if( options.pan !== false ) {
|
||||
|
||||
// Wait with engaging panning as it may conflict with the
|
||||
// zoom transition
|
||||
panEngageTimeout = setTimeout( function() {
|
||||
panUpdateInterval = setInterval( pan, 1000 / 60 );
|
||||
}, 800 );
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Resets the document zoom state to its default.
|
||||
*/
|
||||
out: function() {
|
||||
clearTimeout( panEngageTimeout );
|
||||
clearInterval( panUpdateInterval );
|
||||
|
||||
magnify( { x: 0, y: 0 }, 1 );
|
||||
|
||||
level = 1;
|
||||
},
|
||||
|
||||
// Alias
|
||||
magnify: function( options ) { this.to( options ) },
|
||||
reset: function() { this.out() },
|
||||
|
||||
zoomLevel: function() {
|
||||
return level;
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
||||
4
Lecture_files/libs/revealjs/plugin/zoom/zoom.esm.js
Normal file
@@ -0,0 +1,4 @@
|
||||
/*!
|
||||
* reveal.js Zoom plugin
|
||||
*/
|
||||
var e={id:"zoom",init:function(e){e.getRevealElement().addEventListener("mousedown",(function(n){var o=/Linux/.test(window.navigator.platform)?"ctrl":"alt",i=(e.getConfig().zoomKey?e.getConfig().zoomKey:o)+"Key",d=e.getConfig().zoomLevel?e.getConfig().zoomLevel:2;n[i]&&!e.isOverview()&&(n.preventDefault(),t.to({x:n.clientX,y:n.clientY,scale:d,pan:!1}))}))},destroy:function(){t.reset()}},t=function(){var e=1,n=0,o=0,i=-1,d=-1,l="transform"in document.body.style;function s(t,n){var o=r();if(t.width=t.width||1,t.height=t.height||1,t.x-=(window.innerWidth-t.width*n)/2,t.y-=(window.innerHeight-t.height*n)/2,l)if(1===n)document.body.style.transform="";else{var i=o.x+"px "+o.y+"px",d="translate("+-t.x+"px,"+-t.y+"px) scale("+n+")";document.body.style.transformOrigin=i,document.body.style.transform=d}else 1===n?(document.body.style.position="",document.body.style.left="",document.body.style.top="",document.body.style.width="",document.body.style.height="",document.body.style.zoom=""):(document.body.style.position="relative",document.body.style.left=-(o.x+t.x)/n+"px",document.body.style.top=-(o.y+t.y)/n+"px",document.body.style.width=100*n+"%",document.body.style.height=100*n+"%",document.body.style.zoom=n);e=n,document.documentElement.classList&&(1!==e?document.documentElement.classList.add("zoomed"):document.documentElement.classList.remove("zoomed"))}function c(){var t=.12*window.innerWidth,i=.12*window.innerHeight,d=r();o<i?window.scroll(d.x,d.y-14/e*(1-o/i)):o>window.innerHeight-i&&window.scroll(d.x,d.y+(1-(window.innerHeight-o)/i)*(14/e)),n<t?window.scroll(d.x-14/e*(1-n/t),d.y):n>window.innerWidth-t&&window.scroll(d.x+(1-(window.innerWidth-n)/t)*(14/e),d.y)}function r(){return{x:void 0!==window.scrollX?window.scrollX:window.pageXOffset,y:void 0!==window.scrollY?window.scrollY:window.pageYOffset}}return l&&(document.body.style.transition="transform 0.8s ease"),document.addEventListener("keyup",(function(n){1!==e&&27===n.keyCode&&t.out()})),document.addEventListener("mousemove",(function(t){1!==e&&(n=t.clientX,o=t.clientY)})),{to:function(n){if(1!==e)t.out();else{if(n.x=n.x||0,n.y=n.y||0,n.element){var o=n.element.getBoundingClientRect();n.x=o.left-20,n.y=o.top-20,n.width=o.width+40,n.height=o.height+40}void 0!==n.width&&void 0!==n.height&&(n.scale=Math.max(Math.min(window.innerWidth/n.width,window.innerHeight/n.height),1)),n.scale>1&&(n.x*=n.scale,n.y*=n.scale,s(n,n.scale),!1!==n.pan&&(i=setTimeout((function(){d=setInterval(c,1e3/60)}),800)))}},out:function(){clearTimeout(i),clearInterval(d),s({x:0,y:0},1),e=1},magnify:function(e){this.to(e)},reset:function(){this.out()},zoomLevel:function(){return e}}}();export default function(){return e}
|
||||
4
Lecture_files/libs/revealjs/plugin/zoom/zoom.js
Normal file
@@ -0,0 +1,4 @@
|
||||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).RevealZoom=t()}(this,(function(){"use strict";
|
||||
/*!
|
||||
* reveal.js Zoom plugin
|
||||
*/var e={id:"zoom",init:function(e){e.getRevealElement().addEventListener("mousedown",(function(o){var n=/Linux/.test(window.navigator.platform)?"ctrl":"alt",i=(e.getConfig().zoomKey?e.getConfig().zoomKey:n)+"Key",d=e.getConfig().zoomLevel?e.getConfig().zoomLevel:2;o[i]&&!e.isOverview()&&(o.preventDefault(),t.to({x:o.clientX,y:o.clientY,scale:d,pan:!1}))}))},destroy:function(){t.reset()}},t=function(){var e=1,o=0,n=0,i=-1,d=-1,l="transform"in document.body.style;function s(t,o){var n=r();if(t.width=t.width||1,t.height=t.height||1,t.x-=(window.innerWidth-t.width*o)/2,t.y-=(window.innerHeight-t.height*o)/2,l)if(1===o)document.body.style.transform="";else{var i=n.x+"px "+n.y+"px",d="translate("+-t.x+"px,"+-t.y+"px) scale("+o+")";document.body.style.transformOrigin=i,document.body.style.transform=d}else 1===o?(document.body.style.position="",document.body.style.left="",document.body.style.top="",document.body.style.width="",document.body.style.height="",document.body.style.zoom=""):(document.body.style.position="relative",document.body.style.left=-(n.x+t.x)/o+"px",document.body.style.top=-(n.y+t.y)/o+"px",document.body.style.width=100*o+"%",document.body.style.height=100*o+"%",document.body.style.zoom=o);e=o,document.documentElement.classList&&(1!==e?document.documentElement.classList.add("zoomed"):document.documentElement.classList.remove("zoomed"))}function c(){var t=.12*window.innerWidth,i=.12*window.innerHeight,d=r();n<i?window.scroll(d.x,d.y-14/e*(1-n/i)):n>window.innerHeight-i&&window.scroll(d.x,d.y+(1-(window.innerHeight-n)/i)*(14/e)),o<t?window.scroll(d.x-14/e*(1-o/t),d.y):o>window.innerWidth-t&&window.scroll(d.x+(1-(window.innerWidth-o)/t)*(14/e),d.y)}function r(){return{x:void 0!==window.scrollX?window.scrollX:window.pageXOffset,y:void 0!==window.scrollY?window.scrollY:window.pageYOffset}}return l&&(document.body.style.transition="transform 0.8s ease"),document.addEventListener("keyup",(function(o){1!==e&&27===o.keyCode&&t.out()})),document.addEventListener("mousemove",(function(t){1!==e&&(o=t.clientX,n=t.clientY)})),{to:function(o){if(1!==e)t.out();else{if(o.x=o.x||0,o.y=o.y||0,o.element){var n=o.element.getBoundingClientRect();o.x=n.left-20,o.y=n.top-20,o.width=n.width+40,o.height=n.height+40}void 0!==o.width&&void 0!==o.height&&(o.scale=Math.max(Math.min(window.innerWidth/o.width,window.innerHeight/o.height),1)),o.scale>1&&(o.x*=o.scale,o.y*=o.scale,s(o,o.scale),!1!==o.pan&&(i=setTimeout((function(){d=setInterval(c,1e3/60)}),800)))}},out:function(){clearTimeout(i),clearInterval(d),s({x:0,y:0},1),e=1},magnify:function(e){this.to(e)},reset:function(){this.out()},zoomLevel:function(){return e}}}();return function(){return e}}));
|
||||