${outputResult}
`;
128 | })
129 | .join("\n");
130 |
131 |
132 | // Clean the state
133 | // We're now able to process both graphics and pager events.
134 | // As a result, we cannot maintain a true 1-to-1 output order
135 | // without individually feeding each line
136 | const msgs = await mainWebR.flush();
137 |
138 | // Output each image event stored
139 | msgs.forEach((msg) => {
140 | // Determine if old canvas can be used or a new canvas is required.
141 | if (msg.type === 'canvas'){
142 | // Add image to the current canvas
143 | if (msg.data.event === 'canvasImage') {
144 | canvas.getContext('2d').drawImage(msg.data.image, 0, 0);
145 | } else if (msg.data.event === 'canvasNewPage') {
146 |
147 | // Generate a new canvas element
148 | canvas = document.createElement("canvas");
149 | canvas.setAttribute("width", 2 * fig_width);
150 | canvas.setAttribute("height", 2 * fig_height);
151 | canvas.style.width = options["out-width"] ? options["out-width"] : `${fig_width}px`;
152 | if (options["out-height"]) {
153 | canvas.style.height = options["out-height"];
154 | }
155 | canvas.style.display = "block";
156 | canvas.style.margin = "auto";
157 | }
158 | }
159 | });
160 |
161 | // Use `map` to process the filtered "pager" events asynchronously
162 | const pager = await Promise.all(
163 | msgs.filter(msg => msg.type === 'pager').map(
164 | async (msg) => {
165 | return await qwebrParseTypePager(msg);
166 | }
167 | )
168 | );
169 |
170 | // Nullify the output area of content
171 | elements.outputCodeDiv.innerHTML = "";
172 | elements.outputGraphDiv.innerHTML = "";
173 |
174 | // Design an output object for messages
175 | const pre = document.createElement("pre");
176 | if (/\S/.test(out)) {
177 | // Display results as HTML elements to retain output styling
178 | const div = document.createElement("div");
179 | div.innerHTML = out;
180 | pre.appendChild(div);
181 | } else {
182 | // If nothing is present, hide the element.
183 | pre.style.visibility = "hidden";
184 | }
185 |
186 | elements.outputCodeDiv.appendChild(pre);
187 |
188 | // Place the graphics on the canvas
189 | if (canvas) {
190 | // Create figure element
191 | const figureElement = document.createElement('figure');
192 |
193 | // Append canvas to figure
194 | figureElement.appendChild(canvas);
195 |
196 | if (options['fig-cap']) {
197 | // Create figcaption element
198 | const figcaptionElement = document.createElement('figcaption');
199 | figcaptionElement.innerText = options['fig-cap'];
200 | // Append figcaption to figure
201 | figureElement.appendChild(figcaptionElement);
202 | }
203 |
204 | elements.outputGraphDiv.appendChild(figureElement);
205 | }
206 |
207 | // Display the pager data
208 | if (pager) {
209 | // Use the `pre` element to preserve whitespace.
210 | pager.forEach((paged_data, index) => {
211 | let pre_pager = document.createElement("pre");
212 | pre_pager.innerText = paged_data;
213 | pre_pager.classList.add("qwebr-output-code-pager");
214 | pre_pager.setAttribute("id", `qwebr-output-code-pager-editor-${elements.id}-result-${index + 1}`);
215 | elements.outputCodeDiv.appendChild(pre_pager);
216 | });
217 | }
218 | } finally {
219 | // Clean up the remaining code
220 | mainWebRCodeShelter.purge();
221 | }
222 | }
223 |
224 | // Function to execute the code (accepts code as an argument)
225 | globalThis.qwebrExecuteCode = async function (
226 | codeToRun,
227 | id,
228 | options = {}) {
229 |
230 | // If options are not passed, we fall back on the bare minimum to handle the computation
231 | if (qwebrIsObjectEmpty(options)) {
232 | options = {
233 | "context": "interactive",
234 | "fig-width": 7, "fig-height": 5,
235 | "out-width": "700px", "out-height": "",
236 | "dpi": 72,
237 | "results": "markup",
238 | "warning": "true", "message": "true",
239 | };
240 | }
241 |
242 | // Next, we access the compute areas values
243 | const elements = {
244 | runButton: document.getElementById(`qwebr-button-run-${id}`),
245 | outputCodeDiv: document.getElementById(`qwebr-output-code-area-${id}`),
246 | outputGraphDiv: document.getElementById(`qwebr-output-graph-area-${id}`),
247 | id: id,
248 | }
249 |
250 | // Disallowing execution of other code cells
251 | document.querySelectorAll(".qwebr-button-run").forEach((btn) => {
252 | btn.disabled = true;
253 | });
254 |
255 | if (options.context == EvalTypes.Interactive) {
256 | // Emphasize the active code cell
257 | elements.runButton.innerHTML = ' Run Code';
258 | }
259 |
260 | // Evaluate the code and parse the output into the document
261 | await qwebrComputeEngine(codeToRun, elements, options);
262 |
263 | // Switch to allowing execution of code
264 | document.querySelectorAll(".qwebr-button-run").forEach((btn) => {
265 | btn.disabled = false;
266 | });
267 |
268 | if (options.context == EvalTypes.Interactive) {
269 | // Revert to the initial code cell state
270 | elements.runButton.innerHTML = ' Run Code';
271 | }
272 | }
273 |
--------------------------------------------------------------------------------
/_extensions/coatless/webr/qwebr-document-engine-initialization.js:
--------------------------------------------------------------------------------
1 | // Function to install a single package
2 | async function qwebrInstallRPackage(packageName) {
3 | await mainWebR.evalRVoid(`webr::install('${packageName}');`);
4 | }
5 |
6 | // Function to load a single package
7 | async function qwebrLoadRPackage(packageName) {
8 | await mainWebR.evalRVoid(`require('${packageName}', quietly = TRUE)`);
9 | }
10 |
11 | // Generic function to process R packages
12 | async function qwebrProcessRPackagesWithStatus(packages, processType, displayStatusMessageUpdate = true) {
13 | // Switch between contexts
14 | const messagePrefix = processType === 'install' ? 'Installing' : 'Loading';
15 |
16 | // Modify button state
17 | qwebrSetInteractiveButtonState(`🟡 ${messagePrefix} package ...`, false);
18 |
19 | // Iterate over packages
20 | for (let i = 0; i < packages.length; i++) {
21 | const activePackage = packages[i];
22 | const formattedMessage = `${messagePrefix} package ${i + 1} out of ${packages.length}: ${activePackage}`;
23 |
24 | // Display the update in header
25 | if (displayStatusMessageUpdate) {
26 | qwebrUpdateStatusHeader(formattedMessage);
27 | }
28 |
29 | // Display the update in non-active areas
30 | qwebrUpdateStatusMessage(formattedMessage);
31 |
32 | // Run package installation
33 | if (processType === 'install') {
34 | await qwebrInstallRPackage(activePackage);
35 | } else {
36 | await qwebrLoadRPackage(activePackage);
37 | }
38 | }
39 |
40 | // Clean slate
41 | if (processType === 'load') {
42 | await mainWebR.flush();
43 | }
44 | }
45 |
46 | // Start a timer
47 | const initializeWebRTimerStart = performance.now();
48 |
49 | // Encase with a dynamic import statement
50 | globalThis.qwebrInstance = import(qwebrCustomizedWebROptions.baseURL + "webr.mjs").then(
51 | async ({ WebR, ChannelType }) => {
52 | // Populate WebR options with defaults or new values based on `webr` meta
53 | globalThis.mainWebR = new WebR(qwebrCustomizedWebROptions);
54 |
55 | // Initialization WebR
56 | await mainWebR.init();
57 |
58 | // Setup a shelter
59 | globalThis.mainWebRCodeShelter = await new mainWebR.Shelter();
60 |
61 | // Setup a pager to allow processing help documentation
62 | await mainWebR.evalRVoid('webr::pager_install()');
63 |
64 | // Override the existing install.packages() to use webr::install()
65 | await mainWebR.evalRVoid('webr::shim_install()');
66 |
67 | // Specify the repositories to pull from
68 | // Note: webR does not use the `repos` option, but instead uses `webr_pkg_repos`
69 | // inside of `install()`. However, other R functions still pull from `repos`.
70 | await mainWebR.evalRVoid(`
71 | options(
72 | webr_pkg_repos = c(${qwebrPackageRepoURLS.map(repoURL => `'${repoURL}'`).join(',')}),
73 | repos = c(${qwebrPackageRepoURLS.map(repoURL => `'${repoURL}'`).join(',')})
74 | )
75 | `);
76 |
77 | // Check to see if any packages need to be installed
78 | if (qwebrSetupRPackages) {
79 | // Obtain only a unique list of packages
80 | const uniqueRPackageList = Array.from(new Set(qwebrInstallRPackagesList));
81 |
82 | // Install R packages one at a time (either silently or with a status update)
83 | await qwebrProcessRPackagesWithStatus(uniqueRPackageList, 'install', qwebrShowStartupMessage);
84 |
85 | if (qwebrAutoloadRPackages) {
86 | // Load R packages one at a time (either silently or with a status update)
87 | await qwebrProcessRPackagesWithStatus(uniqueRPackageList, 'load', qwebrShowStartupMessage);
88 | }
89 | }
90 | }
91 | );
92 |
93 | // Stop timer
94 | const initializeWebRTimerEnd = performance.now();
95 |
--------------------------------------------------------------------------------
/_extensions/coatless/webr/qwebr-document-settings.js:
--------------------------------------------------------------------------------
1 | // Document level settings ----
2 |
3 | // Determine if we need to install R packages
4 | globalThis.qwebrInstallRPackagesList = [{{INSTALLRPACKAGESLIST}}];
5 |
6 | // Specify possible locations to search for the repository
7 | globalThis.qwebrPackageRepoURLS = [{{RPACKAGEREPOURLS}}];
8 |
9 | // Check to see if we have an empty array, if we do set to skip the installation.
10 | globalThis.qwebrSetupRPackages = !(qwebrInstallRPackagesList.indexOf("") !== -1);
11 | globalThis.qwebrAutoloadRPackages = {{AUTOLOADRPACKAGES}};
12 |
13 | // Display a startup message?
14 | globalThis.qwebrShowStartupMessage = {{SHOWSTARTUPMESSAGE}};
15 | globalThis.qwebrShowHeaderMessage = {{SHOWHEADERMESSAGE}};
16 |
17 | // Describe the webR settings that should be used
18 | globalThis.qwebrCustomizedWebROptions = {
19 | "baseURL": "{{BASEURL}}",
20 | "serviceWorkerUrl": "{{SERVICEWORKERURL}}",
21 | "homedir": "{{HOMEDIR}}",
22 | "channelType": "{{CHANNELTYPE}}"
23 | };
24 |
25 | // Store cell data
26 | globalThis.qwebrCellDetails = {{QWEBRCELLDETAILS}};
27 |
--------------------------------------------------------------------------------
/_extensions/coatless/webr/qwebr-document-status.js:
--------------------------------------------------------------------------------
1 | // Declare startupMessageQWebR globally
2 | globalThis.qwebrStartupMessage = document.createElement("p");
3 |
4 |
5 | // Function to set the button text
6 | globalThis.qwebrSetInteractiveButtonState = function(buttonText, enableCodeButton = true) {
7 | document.querySelectorAll(".qwebr-button-run").forEach((btn) => {
8 | btn.innerHTML = buttonText;
9 | btn.disabled = !enableCodeButton;
10 | });
11 | }
12 |
13 | // Function to update the status message in non-interactive cells
14 | globalThis.qwebrUpdateStatusMessage = function(message) {
15 | document.querySelectorAll(".qwebr-status-text.qwebr-cell-needs-evaluation").forEach((elem) => {
16 | elem.innerText = message;
17 | });
18 | }
19 |
20 | // Function to update the status message
21 | globalThis.qwebrUpdateStatusHeader = function(message) {
22 | qwebrStartupMessage.innerHTML = `
23 |
24 | ${message}`;
25 | }
26 |
27 | // Function that attaches the document status message and diagnostics
28 | function displayStartupMessage(showStartupMessage, showHeaderMessage) {
29 | if (!showStartupMessage) {
30 | return;
31 | }
32 |
33 | // Get references to header elements
34 | const headerHTML = document.getElementById("title-block-header");
35 | const headerRevealJS = document.getElementById("title-slide");
36 |
37 | // Create the outermost div element for metadata
38 | const quartoTitleMeta = document.createElement("div");
39 | quartoTitleMeta.classList.add("quarto-title-meta");
40 |
41 | // Create the first inner div element
42 | const firstInnerDiv = document.createElement("div");
43 | firstInnerDiv.setAttribute("id", "qwebr-status-message-area");
44 |
45 | // Create the second inner div element for "WebR Status" heading and contents
46 | const secondInnerDiv = document.createElement("div");
47 | secondInnerDiv.setAttribute("id", "qwebr-status-message-title");
48 | secondInnerDiv.classList.add("quarto-title-meta-heading");
49 | secondInnerDiv.innerText = "WebR Status";
50 |
51 | // Create another inner div for contents
52 | const secondInnerDivContents = document.createElement("div");
53 | secondInnerDivContents.setAttribute("id", "qwebr-status-message-body");
54 | secondInnerDivContents.classList.add("quarto-title-meta-contents");
55 |
56 | // Describe the WebR state
57 | qwebrStartupMessage.innerText = "🟡 Loading...";
58 | qwebrStartupMessage.setAttribute("id", "qwebr-status-message-text");
59 | // Add `aria-live` to auto-announce the startup status to screen readers
60 | qwebrStartupMessage.setAttribute("aria-live", "assertive");
61 |
62 | // Append the startup message to the contents
63 | secondInnerDivContents.appendChild(qwebrStartupMessage);
64 |
65 | // Add a status indicator for COOP and COEP Headers if needed
66 | if (showHeaderMessage) {
67 | const crossOriginMessage = document.createElement("p");
68 | crossOriginMessage.innerText = `${crossOriginIsolated ? '🟢' : '🟡'} COOP & COEP Headers`;
69 | crossOriginMessage.setAttribute("id", "qwebr-coop-coep-header");
70 | secondInnerDivContents.appendChild(crossOriginMessage);
71 | }
72 |
73 | // Combine the inner divs and contents
74 | firstInnerDiv.appendChild(secondInnerDiv);
75 | firstInnerDiv.appendChild(secondInnerDivContents);
76 | quartoTitleMeta.appendChild(firstInnerDiv);
77 |
78 | // Determine where to insert the quartoTitleMeta element
79 | if (headerHTML || headerRevealJS) {
80 | // Append to the existing "title-block-header" element or "title-slide" div
81 | (headerHTML || headerRevealJS).appendChild(quartoTitleMeta);
82 | } else {
83 | // If neither headerHTML nor headerRevealJS is found, insert after "webr-monaco-editor-init" script
84 | const monacoScript = document.getElementById("qwebr-monaco-editor-init");
85 | const header = document.createElement("header");
86 | header.setAttribute("id", "title-block-header");
87 | header.appendChild(quartoTitleMeta);
88 | monacoScript.after(header);
89 | }
90 | }
91 |
92 | displayStartupMessage(qwebrShowStartupMessage, qwebrShowHeaderMessage);
--------------------------------------------------------------------------------
/_extensions/coatless/webr/qwebr-monaco-editor-element.js:
--------------------------------------------------------------------------------
1 | // Global dictionary to store Monaco Editor instances
2 | globalThis.qwebrEditorInstances = {};
3 |
4 | // Function that builds and registers a Monaco Editor instance
5 | globalThis.qwebrCreateMonacoEditorInstance = function (cellData) {
6 |
7 | const initialCode = cellData.code;
8 | const qwebrCounter = cellData.id;
9 | const qwebrOptions = cellData.options;
10 |
11 | // Retrieve the previously created document elements
12 | let runButton = document.getElementById(`qwebr-button-run-${qwebrCounter}`);
13 | let resetButton = document.getElementById(`qwebr-button-reset-${qwebrCounter}`);
14 | let copyButton = document.getElementById(`qwebr-button-copy-${qwebrCounter}`);
15 | let editorDiv = document.getElementById(`qwebr-editor-${qwebrCounter}`);
16 |
17 | // Load the Monaco Editor and create an instance
18 | let editor;
19 | require(['vs/editor/editor.main'], function () {
20 | editor = monaco.editor.create(editorDiv, {
21 | value: initialCode,
22 | language: 'r',
23 | theme: 'vs-light',
24 | automaticLayout: true, // Works wonderfully with RevealJS
25 | scrollBeyondLastLine: false,
26 | minimap: {
27 | enabled: false
28 | },
29 | fontSize: '17.5pt', // Bootstrap is 1 rem
30 | renderLineHighlight: "none", // Disable current line highlighting
31 | hideCursorInOverviewRuler: true, // Remove cursor indictor in right hand side scroll bar
32 | readOnly: qwebrOptions['read-only'] ?? false
33 | });
34 |
35 | // Store the official counter ID to be used in keyboard shortcuts
36 | editor.__qwebrCounter = qwebrCounter;
37 |
38 | // Store the official div container ID
39 | editor.__qwebrEditorId = `qwebr-editor-${qwebrCounter}`;
40 |
41 | // Store the initial code value and options
42 | editor.__qwebrinitialCode = initialCode;
43 | editor.__qwebrOptions = qwebrOptions;
44 |
45 | // Set at the model level the preferred end of line (EOL) character to LF.
46 | // This prevent `\r\n` from being given to the webR engine if the user is on Windows.
47 | // See details in: https://github.com/coatless/quarto-webr/issues/94
48 | // Associated error text:
49 | // Error: Remember that you need to serve the presentation HTML from a HTTP server.
' + 231 | '${code}
`;
450 | };
451 | }
452 |
453 | if( animateLists === true ) {
454 | renderer.listitem = text => ` element after existing one
91 | code.parentNode.appendChild(fragmentBlock);
92 |
93 | // Each new element is highlighted based on the new attributes value
94 | highlightCodeBlock(fragmentBlock);
95 |
96 | if (typeof fragmentIndex === "number") {
97 | fragmentBlock.setAttribute(kFragmentIndex, fragmentIndex);
98 | fragmentIndex += 1;
99 | } else {
100 | fragmentBlock.removeAttribute(kFragmentIndex);
101 | }
102 |
103 | // Scroll highlights into view as we step through them
104 | fragmentBlock.addEventListener(
105 | "visible",
106 | scrollHighlightedLineIntoView.bind(
107 | this,
108 | fragmentBlock,
109 | scrollState
110 | )
111 | );
112 | fragmentBlock.addEventListener(
113 | "hidden",
114 | scrollHighlightedLineIntoView.bind(
115 | this,
116 | fragmentBlock.previousSibling,
117 | scrollState
118 | )
119 | );
120 | }
121 | );
122 | code.removeAttribute(kFragmentIndex);
123 | code.setAttribute(
124 | kCodeLineNumbersAttr,
125 | joinLineNumbers([highlightSteps[0]])
126 | );
127 | }
128 |
129 | // Scroll the first highlight into view when the slide becomes visible.
130 | const slide =
131 | typeof code.closest === "function"
132 | ? code.closest("section:not(.stack)")
133 | : null;
134 | if (slide) {
135 | const scrollFirstHighlightIntoView = function () {
136 | scrollHighlightedLineIntoView(code, scrollState, true);
137 | slide.removeEventListener(
138 | "visible",
139 | scrollFirstHighlightIntoView
140 | );
141 | };
142 | slide.addEventListener("visible", scrollFirstHighlightIntoView);
143 | }
144 |
145 | highlightCodeBlock(code);
146 | });
147 | }
148 | }
149 | });
150 | }
151 |
152 | function highlightCodeBlock(codeBlock) {
153 | const highlightSteps = splitLineNumbers(
154 | codeBlock.getAttribute(kCodeLineNumbersAttr)
155 | );
156 |
157 | if (highlightSteps.length) {
158 | // If we have at least one step, we generate fragments
159 | highlightSteps[0].forEach((highlight) => {
160 | // Add expected class on for reveal CSS
161 | codeBlock.parentNode.classList.add("code-wrapper");
162 |
163 | // Select lines to highlight
164 | spanToHighlight = [];
165 | if (typeof highlight.last === "number") {
166 | spanToHighlight = [].slice.call(
167 | codeBlock.querySelectorAll(
168 | ":scope > span:nth-of-type(n+" +
169 | highlight.first +
170 | "):nth-of-type(-n+" +
171 | highlight.last +
172 | ")"
173 | )
174 | );
175 | } else if (typeof highlight.first === "number") {
176 | spanToHighlight = [].slice.call(
177 | codeBlock.querySelectorAll(
178 | ":scope > span:nth-of-type(" + highlight.first + ")"
179 | )
180 | );
181 | }
182 | if (spanToHighlight.length) {
183 | // Add a class on and to select line to highlight
184 | spanToHighlight.forEach((span) =>
185 | span.classList.add("highlight-line")
186 | );
187 | codeBlock.classList.add("has-line-highlights");
188 | }
189 | });
190 | }
191 | }
192 |
193 | /**
194 | * Animates scrolling to the first highlighted line
195 | * in the given code block.
196 | */
197 | function scrollHighlightedLineIntoView(block, scrollState, skipAnimation) {
198 | window.cancelAnimationFrame(scrollState.animationFrameID);
199 |
200 | // Match the scroll position of the currently visible
201 | // code block
202 | if (scrollState.currentBlock) {
203 | block.scrollTop = scrollState.currentBlock.scrollTop;
204 | }
205 |
206 | // Remember the current code block so that we can match
207 | // its scroll position when showing/hiding fragments
208 | scrollState.currentBlock = block;
209 |
210 | const highlightBounds = getHighlightedLineBounds(block);
211 | let viewportHeight = block.offsetHeight;
212 |
213 | // Subtract padding from the viewport height
214 | const blockStyles = window.getComputedStyle(block);
215 | viewportHeight -=
216 | parseInt(blockStyles.paddingTop) + parseInt(blockStyles.paddingBottom);
217 |
218 | // Scroll position which centers all highlights
219 | const startTop = block.scrollTop;
220 | let targetTop =
221 | highlightBounds.top +
222 | (Math.min(highlightBounds.bottom - highlightBounds.top, viewportHeight) -
223 | viewportHeight) /
224 | 2;
225 |
226 | // Make sure the scroll target is within bounds
227 | targetTop = Math.max(
228 | Math.min(targetTop, block.scrollHeight - viewportHeight),
229 | 0
230 | );
231 |
232 | if (skipAnimation === true || startTop === targetTop) {
233 | block.scrollTop = targetTop;
234 | } else {
235 | // Don't attempt to scroll if there is no overflow
236 | if (block.scrollHeight <= viewportHeight) return;
237 |
238 | let time = 0;
239 |
240 | const animate = function () {
241 | time = Math.min(time + 0.02, 1);
242 |
243 | // Update our eased scroll position
244 | block.scrollTop =
245 | startTop + (targetTop - startTop) * easeInOutQuart(time);
246 |
247 | // Keep animating unless we've reached the end
248 | if (time < 1) {
249 | scrollState.animationFrameID = requestAnimationFrame(animate);
250 | }
251 | };
252 |
253 | animate();
254 | }
255 | }
256 |
257 | function getHighlightedLineBounds(block) {
258 | const highlightedLines = block.querySelectorAll(".highlight-line");
259 | if (highlightedLines.length === 0) {
260 | return { top: 0, bottom: 0 };
261 | } else {
262 | const firstHighlight = highlightedLines[0];
263 | const lastHighlight = highlightedLines[highlightedLines.length - 1];
264 |
265 | return {
266 | top: firstHighlight.offsetTop,
267 | bottom: lastHighlight.offsetTop + lastHighlight.offsetHeight,
268 | };
269 | }
270 | }
271 |
272 | /**
273 | * The easing function used when scrolling.
274 | */
275 | function easeInOutQuart(t) {
276 | // easeInOutQuart
277 | return t < 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t;
278 | }
279 |
280 | function splitLineNumbers(lineNumbersAttr) {
281 | // remove space
282 | lineNumbersAttr = lineNumbersAttr.replace("/s/g", "");
283 | // seperate steps (for fragment)
284 | lineNumbersAttr = lineNumbersAttr.split(delimiters.step);
285 |
286 | // for each step, calculate first and last line, if any
287 | return lineNumbersAttr.map((highlights) => {
288 | // detect lines
289 | const lines = highlights.split(delimiters.line);
290 | return lines.map((range) => {
291 | if (/^[\d-]+$/.test(range)) {
292 | range = range.split(delimiters.lineRange);
293 | const firstLine = parseInt(range[0], 10);
294 | const lastLine = range[1] ? parseInt(range[1], 10) : undefined;
295 | return {
296 | first: firstLine,
297 | last: lastLine,
298 | };
299 | } else {
300 | return {};
301 | }
302 | });
303 | });
304 | }
305 |
306 | function joinLineNumbers(splittedLineNumbers) {
307 | return splittedLineNumbers
308 | .map(function (highlights) {
309 | return highlights
310 | .map(function (highlight) {
311 | // Line range
312 | if (typeof highlight.last === "number") {
313 | return highlight.first + delimiters.lineRange + highlight.last;
314 | }
315 | // Single line
316 | else if (typeof highlight.first === "number") {
317 | return highlight.first;
318 | }
319 | // All lines
320 | else {
321 | return "";
322 | }
323 | })
324 | .join(delimiters.line);
325 | })
326 | .join(delimiters.step);
327 | }
328 |
329 | return {
330 | id: "quarto-line-highlight",
331 | init: function (deck) {
332 | initQuartoLineHighlight(deck);
333 |
334 | // If we're printing to PDF, scroll the code highlights of
335 | // all blocks in the deck into view at once
336 | deck.on("pdf-ready", function () {
337 | [].slice
338 | .call(
339 | deck
340 | .getRevealElement()
341 | .querySelectorAll(
342 | "pre code[data-code-line-numbers].current-fragment"
343 | )
344 | )
345 | .forEach(function (block) {
346 | scrollHighlightedLineIntoView(block, {}, true);
347 | });
348 | });
349 | },
350 | };
351 | };
352 |
--------------------------------------------------------------------------------
/index_files/libs/revealjs/plugin/quarto-line-highlight/plugin.yml:
--------------------------------------------------------------------------------
1 | # adapted from https://github.com/hakimel/reveal.js/tree/master/plugin/highlight
2 | name: QuartoLineHighlight
3 | script: line-highlight.js
4 | stylesheet: line-highlight.css
5 |
--------------------------------------------------------------------------------
/index_files/libs/revealjs/plugin/quarto-support/footer.css:
--------------------------------------------------------------------------------
1 | .reveal .slide-logo {
2 | display: block;
3 | position: fixed;
4 | bottom: 0;
5 | right: 12px;
6 | max-height: 2.2rem;
7 | height: 100%;
8 | width: auto;
9 | z-index: 2;
10 | }
11 |
12 | .reveal .footer {
13 | display: block;
14 | position: fixed;
15 | bottom: 18px;
16 | width: 100%;
17 | margin: 0 auto;
18 | text-align: center;
19 | font-size: 18px;
20 | z-index: 2;
21 | }
22 |
23 | .reveal .footer > * {
24 | margin-top: 0;
25 | margin-bottom: 0;
26 | }
27 |
28 | .reveal .slide .footer {
29 | display: none;
30 | }
31 |
32 | .reveal .slide-number {
33 | bottom: 10px;
34 | right: 10px;
35 | font-size: 16px;
36 | background-color: transparent;
37 | }
38 |
39 | .reveal.has-logo .slide-number {
40 | bottom: initial;
41 | top: 8px;
42 | right: 8px;
43 | }
44 |
45 | .reveal .slide-number .slide-number-delimiter {
46 | margin: 0;
47 | }
48 |
49 | .reveal .slide-menu-button {
50 | left: 8px;
51 | bottom: 8px;
52 | }
53 |
54 | .reveal .slide-chalkboard-buttons {
55 | position: fixed;
56 | left: 12px;
57 | bottom: 8px;
58 | z-index: 30;
59 | font-size: 24px;
60 | }
61 |
62 | .reveal .slide-chalkboard-buttons.slide-menu-offset {
63 | left: 54px;
64 | }
65 |
66 | .reveal .slide-chalkboard-buttons > span {
67 | margin-right: 14px;
68 | cursor: pointer;
69 | }
70 |
71 | @media screen and (max-width: 800px) {
72 | .reveal .slide-logo {
73 | max-height: 1.1rem;
74 | bottom: -2px;
75 | right: 10px;
76 | }
77 | .reveal .footer {
78 | font-size: 14px;
79 | bottom: 12px;
80 | }
81 | .reveal .slide-number {
82 | font-size: 12px;
83 | bottom: 7px;
84 | }
85 | .reveal .slide-menu-button .fas::before {
86 | height: 1.3rem;
87 | width: 1.3rem;
88 | vertical-align: -0.125em;
89 | background-size: 1.3rem 1.3rem;
90 | }
91 |
92 | .reveal .slide-chalkboard-buttons .fas::before {
93 | height: 0.95rem;
94 | width: 0.95rem;
95 | background-size: 0.95rem 0.95rem;
96 | vertical-align: -0em;
97 | }
98 |
99 | .reveal .slide-chalkboard-buttons.slide-menu-offset {
100 | left: 36px;
101 | }
102 | .reveal .slide-chalkboard-buttons > span {
103 | margin-right: 9px;
104 | }
105 | }
106 |
107 | html.print-pdf .reveal .slide-menu-button,
108 | html.print-pdf .reveal .slide-chalkboard-buttons {
109 | display: none;
110 | }
111 |
--------------------------------------------------------------------------------
/index_files/libs/revealjs/plugin/quarto-support/plugin.yml:
--------------------------------------------------------------------------------
1 | name: QuartoSupport
2 | script: support.js
3 | stylesheet: footer.css
4 | config:
5 | smaller: false
6 |
--------------------------------------------------------------------------------
/index_files/libs/revealjs/plugin/quarto-support/support.js:
--------------------------------------------------------------------------------
1 | // catch all plugin for various quarto features
2 | window.QuartoSupport = function () {
3 | function isPrintView() {
4 | return /print-pdf/gi.test(window.location.search);
5 | }
6 |
7 | // helper for theme toggling
8 | function toggleBackgroundTheme(el, onDarkBackground, onLightBackground) {
9 | if (onDarkBackground) {
10 | el.classList.add('has-dark-background')
11 | } else {
12 | el.classList.remove('has-dark-background')
13 | }
14 | if (onLightBackground) {
15 | el.classList.add('has-light-background')
16 | } else {
17 | el.classList.remove('has-light-background')
18 | }
19 | }
20 |
21 | // implement controlsAudo
22 | function controlsAuto(deck) {
23 | const config = deck.getConfig();
24 | if (config.controlsAuto === true) {
25 | const iframe = window.location !== window.parent.location;
26 | const localhost =
27 | window.location.hostname === "localhost" ||
28 | window.location.hostname === "127.0.0.1";
29 | deck.configure({
30 | controls:
31 | (iframe && !localhost) ||
32 | (deck.hasVerticalSlides() && config.navigationMode !== "linear"),
33 | });
34 | }
35 | }
36 |
37 | // helper to provide event handlers for all links in a container
38 | function handleLinkClickEvents(deck, container) {
39 | Array.from(container.querySelectorAll("a")).forEach((el) => {
40 | const url = el.getAttribute("href");
41 | if (/^(http|www)/gi.test(url)) {
42 | el.addEventListener(
43 | "click",
44 | (ev) => {
45 | const fullscreen = !!window.document.fullscreen;
46 | const dataPreviewLink = el.getAttribute("data-preview-link");
47 |
48 | // if there is a local specifcation then use that
49 | if (dataPreviewLink) {
50 | if (
51 | dataPreviewLink === "true" ||
52 | (dataPreviewLink === "auto" && fullscreen)
53 | ) {
54 | ev.preventDefault();
55 | deck.showPreview(url);
56 | return false;
57 | }
58 | } else {
59 | const previewLinks = !!deck.getConfig().previewLinks;
60 | const previewLinksAuto =
61 | deck.getConfig().previewLinksAuto === true;
62 | if (previewLinks == true || (previewLinksAuto && fullscreen)) {
63 | ev.preventDefault();
64 | deck.showPreview(url);
65 | return false;
66 | }
67 | }
68 |
69 | // if the deck is in an iframe we want to open it externally
70 | // (don't do this when in vscode though as it has its own
71 | // handler for opening links externally that will be play)
72 | const iframe = window.location !== window.parent.location;
73 | if (
74 | iframe &&
75 | !window.location.search.includes("quartoPreviewReqId=")
76 | ) {
77 | ev.preventDefault();
78 | ev.stopImmediatePropagation();
79 | window.open(url, "_blank");
80 | return false;
81 | }
82 |
83 | // if the user has set data-preview-link to "auto" we need to handle the event
84 | // (because reveal will interpret "auto" as true)
85 | if (dataPreviewLink === "auto") {
86 | ev.preventDefault();
87 | ev.stopImmediatePropagation();
88 | const target =
89 | el.getAttribute("target") ||
90 | (ev.ctrlKey || ev.metaKey ? "_blank" : "");
91 | if (target) {
92 | window.open(url, target);
93 | } else {
94 | window.location.href = url;
95 | }
96 | return false;
97 | }
98 | },
99 | false
100 | );
101 | }
102 | });
103 | }
104 |
105 | // implement previewLinksAuto
106 | function previewLinksAuto(deck) {
107 | handleLinkClickEvents(deck, deck.getRevealElement());
108 | }
109 |
110 | // apply styles
111 | function applyGlobalStyles(deck) {
112 | if (deck.getConfig()["smaller"] === true) {
113 | const revealParent = deck.getRevealElement();
114 | revealParent.classList.add("smaller");
115 | }
116 | }
117 |
118 | // add logo image
119 | function addLogoImage(deck) {
120 | const revealParent = deck.getRevealElement();
121 | const logoImg = document.querySelector(".slide-logo");
122 | if (logoImg) {
123 | revealParent.appendChild(logoImg);
124 | revealParent.classList.add("has-logo");
125 | }
126 | }
127 |
128 | // tweak slide-number element
129 | function tweakSlideNumber(deck) {
130 | deck.on("slidechanged", function (ev) {
131 | const revealParent = deck.getRevealElement();
132 | const slideNumberEl = revealParent.querySelector(".slide-number");
133 | const onDarkBackground = Reveal.getSlideBackground(ev.indexh, ev.indexv).classList.contains('has-dark-background');
134 | const onLightBackground = Reveal.getSlideBackground(ev.indexh, ev.indexv).classList.contains('has-light-background');
135 | toggleBackgroundTheme(slideNumberEl, onDarkBackground, onLightBackground);
136 | })
137 | }
138 |
139 | // add footer text
140 | function addFooter(deck) {
141 | const revealParent = deck.getRevealElement();
142 | const defaultFooterDiv = document.querySelector(".footer-default");
143 | if (defaultFooterDiv) {
144 | revealParent.appendChild(defaultFooterDiv);
145 | handleLinkClickEvents(deck, defaultFooterDiv);
146 | if (!isPrintView()) {
147 | deck.on("slidechanged", function (ev) {
148 | const prevSlideFooter = document.querySelector(
149 | ".reveal > .footer:not(.footer-default)"
150 | );
151 | if (prevSlideFooter) {
152 | prevSlideFooter.remove();
153 | }
154 | const currentSlideFooter = ev.currentSlide.querySelector(".footer");
155 | const onDarkBackground = Reveal.getSlideBackground(ev.indexh, ev.indexv).classList.contains('has-dark-background')
156 | const onLightBackground = Reveal.getSlideBackground(ev.indexh, ev.indexv).classList.contains('has-light-background')
157 | if (currentSlideFooter) {
158 | defaultFooterDiv.style.display = "none";
159 | const slideFooter = currentSlideFooter.cloneNode(true);
160 | handleLinkClickEvents(deck, slideFooter);
161 | deck.getRevealElement().appendChild(slideFooter);
162 | toggleBackgroundTheme(slideFooter, onDarkBackground, onLightBackground)
163 | } else {
164 | defaultFooterDiv.style.display = "block";
165 | toggleBackgroundTheme(defaultFooterDiv, onDarkBackground, onLightBackground)
166 | }
167 | });
168 | }
169 | }
170 | }
171 |
172 | // add chalkboard buttons
173 | function addChalkboardButtons(deck) {
174 | const chalkboard = deck.getPlugin("RevealChalkboard");
175 | if (chalkboard && !isPrintView()) {
176 | const revealParent = deck.getRevealElement();
177 | const chalkboardDiv = document.createElement("div");
178 | chalkboardDiv.classList.add("slide-chalkboard-buttons");
179 | if (document.querySelector(".slide-menu-button")) {
180 | chalkboardDiv.classList.add("slide-menu-offset");
181 | }
182 | // add buttons
183 | const buttons = [
184 | {
185 | icon: "easel2",
186 | title: "Toggle Chalkboard (b)",
187 | onclick: chalkboard.toggleChalkboard,
188 | },
189 | {
190 | icon: "brush",
191 | title: "Toggle Notes Canvas (c)",
192 | onclick: chalkboard.toggleNotesCanvas,
193 | },
194 | ];
195 | buttons.forEach(function (button) {
196 | const span = document.createElement("span");
197 | span.title = button.title;
198 | const icon = document.createElement("i");
199 | icon.classList.add("fas");
200 | icon.classList.add("fa-" + button.icon);
201 | span.appendChild(icon);
202 | span.onclick = function (event) {
203 | event.preventDefault();
204 | button.onclick();
205 | };
206 | chalkboardDiv.appendChild(span);
207 | });
208 | revealParent.appendChild(chalkboardDiv);
209 | const config = deck.getConfig();
210 | if (!config.chalkboard.buttons) {
211 | chalkboardDiv.classList.add("hidden");
212 | }
213 |
214 | // show and hide chalkboard buttons on slidechange
215 | deck.on("slidechanged", function (ev) {
216 | const config = deck.getConfig();
217 | let buttons = !!config.chalkboard.buttons;
218 | const slideButtons = ev.currentSlide.getAttribute(
219 | "data-chalkboard-buttons"
220 | );
221 | if (slideButtons) {
222 | if (slideButtons === "true" || slideButtons === "1") {
223 | buttons = true;
224 | } else if (slideButtons === "false" || slideButtons === "0") {
225 | buttons = false;
226 | }
227 | }
228 | if (buttons) {
229 | chalkboardDiv.classList.remove("hidden");
230 | } else {
231 | chalkboardDiv.classList.add("hidden");
232 | }
233 | });
234 | }
235 | }
236 |
237 | function handleTabbyClicks() {
238 | const tabs = document.querySelectorAll(".panel-tabset-tabby > li > a");
239 | for (let i = 0; i < tabs.length; i++) {
240 | const tab = tabs[i];
241 | tab.onclick = function (ev) {
242 | ev.preventDefault();
243 | ev.stopPropagation();
244 | return false;
245 | };
246 | }
247 | }
248 |
249 | function fixupForPrint(deck) {
250 | if (isPrintView()) {
251 | const slides = deck.getSlides();
252 | slides.forEach(function (slide) {
253 | slide.removeAttribute("data-auto-animate");
254 | });
255 | window.document.querySelectorAll(".hljs").forEach(function (el) {
256 | el.classList.remove("hljs");
257 | });
258 | window.document.querySelectorAll(".hljs-ln-code").forEach(function (el) {
259 | el.classList.remove("hljs-ln-code");
260 | });
261 | }
262 | }
263 |
264 | function handleSlideChanges(deck) {
265 | // dispatch for htmlwidgets
266 | const fireSlideEnter = () => {
267 | const event = window.document.createEvent("Event");
268 | event.initEvent("slideenter", true, true);
269 | window.document.dispatchEvent(event);
270 | };
271 |
272 | const fireSlideChanged = (previousSlide, currentSlide) => {
273 | fireSlideEnter();
274 |
275 | // dispatch for shiny
276 | if (window.jQuery) {
277 | if (previousSlide) {
278 | window.jQuery(previousSlide).trigger("hidden");
279 | }
280 | if (currentSlide) {
281 | window.jQuery(currentSlide).trigger("shown");
282 | }
283 | }
284 | };
285 |
286 | // fire slideEnter for tabby tab activations (for htmlwidget resize behavior)
287 | document.addEventListener("tabby", fireSlideEnter, false);
288 |
289 | deck.on("slidechanged", function (event) {
290 | fireSlideChanged(event.previousSlide, event.currentSlide);
291 | });
292 | }
293 |
294 | function workaroundMermaidDistance(deck) {
295 | if (window.document.querySelector("pre.mermaid-js")) {
296 | const slideCount = deck.getTotalSlides();
297 | deck.configure({
298 | mobileViewDistance: slideCount,
299 | viewDistance: slideCount,
300 | });
301 | }
302 | }
303 |
304 | return {
305 | id: "quarto-support",
306 | init: function (deck) {
307 | controlsAuto(deck);
308 | previewLinksAuto(deck);
309 | fixupForPrint(deck);
310 | applyGlobalStyles(deck);
311 | addLogoImage(deck);
312 | tweakSlideNumber(deck);
313 | addFooter(deck);
314 | addChalkboardButtons(deck);
315 | handleTabbyClicks();
316 | handleSlideChanges(deck);
317 | workaroundMermaidDistance(deck);
318 | },
319 | };
320 | };
321 |
--------------------------------------------------------------------------------
/index_files/libs/revealjs/plugin/reveal-menu/menu.css:
--------------------------------------------------------------------------------
1 | .slide-menu-wrapper {
2 | font-family: 'Source Sans Pro', Helvetica, sans-serif;
3 | }
4 |
5 | .slide-menu-wrapper .slide-menu {
6 | background-color: #333;
7 | z-index: 200;
8 | position: fixed;
9 | top: 0;
10 | width: 300px;
11 | height: 100%;
12 | /*overflow-y: scroll;*/
13 | transition: transform 0.3s;
14 | font-size: 16px;
15 | font-weight: normal;
16 | }
17 |
18 | .slide-menu-wrapper .slide-menu.slide-menu--wide {
19 | width: 500px;
20 | }
21 |
22 | .slide-menu-wrapper .slide-menu.slide-menu--third {
23 | width: 33%;
24 | }
25 |
26 | .slide-menu-wrapper .slide-menu.slide-menu--half {
27 | width: 50%;
28 | }
29 |
30 | .slide-menu-wrapper .slide-menu.slide-menu--full {
31 | width: 95%;
32 | }
33 |
34 | /*
35 | * Slides menu
36 | */
37 |
38 | .slide-menu-wrapper .slide-menu-items {
39 | margin: 0;
40 | padding: 0;
41 | width: 100%;
42 | border-bottom: solid 1px #555;
43 | }
44 |
45 | .slide-menu-wrapper .slide-menu-item,
46 | .slide-menu-wrapper .slide-menu-item-vertical {
47 | display: block;
48 | text-align: left;
49 | padding: 10px 18px;
50 | color: #aaa;
51 | cursor: pointer;
52 | }
53 |
54 | .slide-menu-wrapper .slide-menu-item-vertical {
55 | padding-left: 30px;
56 | }
57 |
58 | .slide-menu-wrapper .slide-menu--wide .slide-menu-item-vertical,
59 | .slide-menu-wrapper .slide-menu--third .slide-menu-item-vertical,
60 | .slide-menu-wrapper .slide-menu--half .slide-menu-item-vertical,
61 | .slide-menu-wrapper .slide-menu--full .slide-menu-item-vertical,
62 | .slide-menu-wrapper .slide-menu--custom .slide-menu-item-vertical {
63 | padding-left: 50px;
64 | }
65 |
66 | .slide-menu-wrapper .slide-menu-item {
67 | border-top: solid 1px #555;
68 | }
69 |
70 | .slide-menu-wrapper .active-menu-panel li.selected {
71 | background-color: #222;
72 | color: white;
73 | }
74 |
75 | .slide-menu-wrapper .active-menu-panel li.active {
76 | color: #eee;
77 | }
78 |
79 | .slide-menu-wrapper .slide-menu-item.no-title .slide-menu-item-title,
80 | .slide-menu-wrapper .slide-menu-item-vertical.no-title .slide-menu-item-title {
81 | font-style: italic;
82 | }
83 |
84 | .slide-menu-wrapper .slide-menu-item-number {
85 | color: #999;
86 | padding-right: 6px;
87 | }
88 |
89 | .slide-menu-wrapper .slide-menu-item i.far,
90 | .slide-menu-wrapper .slide-menu-item i.fas,
91 | .slide-menu-wrapper .slide-menu-item-vertical i.far,
92 | .slide-menu-wrapper .slide-menu-item-vertical i.fas,
93 | .slide-menu-wrapper .slide-menu-item svg.svg-inline--fa,
94 | .slide-menu-wrapper .slide-menu-item-vertical svg.svg-inline--fa {
95 | padding-right: 12px;
96 | display: none;
97 | }
98 |
99 | .slide-menu-wrapper .slide-menu-item.past i.fas.past,
100 | .slide-menu-wrapper .slide-menu-item-vertical.past i.fas.past,
101 | .slide-menu-wrapper .slide-menu-item.active i.fas.active,
102 | .slide-menu-wrapper .slide-menu-item-vertical.active i.fas.active,
103 | .slide-menu-wrapper .slide-menu-item.future i.far.future,
104 | .slide-menu-wrapper .slide-menu-item-vertical.future i.far.future,
105 | .slide-menu-wrapper .slide-menu-item.past svg.svg-inline--fa.past,
106 | .slide-menu-wrapper .slide-menu-item-vertical.past svg.svg-inline--fa.past,
107 | .slide-menu-wrapper .slide-menu-item.active svg.svg-inline--fa.active,
108 | .slide-menu-wrapper .slide-menu-item-vertical.active svg.svg-inline--fa.active,
109 | .slide-menu-wrapper .slide-menu-item.future svg.svg-inline--fa.future,
110 | .slide-menu-wrapper .slide-menu-item-vertical.future svg.svg-inline--fa.future {
111 | display: inline-block;
112 | }
113 |
114 | .slide-menu-wrapper .slide-menu-item.past i.fas.past,
115 | .slide-menu-wrapper .slide-menu-item-vertical.past i.fas.past,
116 | .slide-menu-wrapper .slide-menu-item.future i.far.future,
117 | .slide-menu-wrapper .slide-menu-item-vertical.future i.far.future,
118 | .slide-menu-wrapper .slide-menu-item.past svg.svg-inline--fa.past,
119 | .slide-menu-wrapper .slide-menu-item-vertical.past svg.svg-inline--fa.past,
120 | .slide-menu-wrapper .slide-menu-item.future svg.svg-inline--fa.future,
121 | .slide-menu-wrapper .slide-menu-item-vertical.future svg.svg-inline--fa.future {
122 | opacity: 0.4;
123 | }
124 |
125 | .slide-menu-wrapper .slide-menu-item.active i.fas.active,
126 | .slide-menu-wrapper .slide-menu-item-vertical.active i.fas.active,
127 | .slide-menu-wrapper .slide-menu-item.active svg.svg-inline--fa.active,
128 | .slide-menu-wrapper .slide-menu-item-vertical.active svg.svg-inline--fa.active {
129 | opacity: 0.8;
130 | }
131 |
132 | .slide-menu-wrapper .slide-menu--left {
133 | left: 0;
134 | -webkit-transform: translateX(-100%);
135 | -ms-transform: translateX(-100%);
136 | transform: translateX(-100%);
137 | }
138 |
139 | .slide-menu-wrapper .slide-menu--left.active {
140 | -webkit-transform: translateX(0);
141 | -ms-transform: translateX(0);
142 | transform: translateX(0);
143 | }
144 |
145 | .slide-menu-wrapper .slide-menu--right {
146 | right: 0;
147 | -webkit-transform: translateX(100%);
148 | -ms-transform: translateX(100%);
149 | transform: translateX(100%);
150 | }
151 |
152 | .slide-menu-wrapper .slide-menu--right.active {
153 | -webkit-transform: translateX(0);
154 | -ms-transform: translateX(0);
155 | transform: translateX(0);
156 | }
157 |
158 | .slide-menu-wrapper {
159 | transition: transform 0.3s;
160 | }
161 |
162 | /*
163 | * Toolbar
164 | */
165 | .slide-menu-wrapper .slide-menu-toolbar {
166 | height: 60px;
167 | width: 100%;
168 | font-size: 12px;
169 | display: table;
170 | table-layout: fixed; /* ensures equal width */
171 | margin: 0;
172 | padding: 0;
173 | border-bottom: solid 2px #666;
174 | }
175 |
176 | .slide-menu-wrapper .slide-menu-toolbar > li {
177 | display: table-cell;
178 | line-height: 150%;
179 | text-align: center;
180 | vertical-align: middle;
181 | cursor: pointer;
182 | color: #aaa;
183 | border-radius: 3px;
184 | }
185 |
186 | .slide-menu-wrapper .slide-menu-toolbar > li.toolbar-panel-button i,
187 | .slide-menu-wrapper
188 | .slide-menu-toolbar
189 | > li.toolbar-panel-button
190 | svg.svg-inline--fa {
191 | font-size: 1.7em;
192 | }
193 |
194 | .slide-menu-wrapper .slide-menu-toolbar > li.active-toolbar-button {
195 | color: white;
196 | text-shadow: 0 1px black;
197 | text-decoration: underline;
198 | }
199 |
200 | .slide-menu-toolbar > li.toolbar-panel-button:hover {
201 | color: white;
202 | }
203 |
204 | .slide-menu-toolbar
205 | > li.toolbar-panel-button:hover
206 | span.slide-menu-toolbar-label,
207 | .slide-menu-wrapper
208 | .slide-menu-toolbar
209 | > li.active-toolbar-button
210 | span.slide-menu-toolbar-label {
211 | visibility: visible;
212 | }
213 |
214 | /*
215 | * Panels
216 | */
217 | .slide-menu-wrapper .slide-menu-panel {
218 | position: absolute;
219 | width: 100%;
220 | visibility: hidden;
221 | height: calc(100% - 60px);
222 | overflow-x: hidden;
223 | overflow-y: auto;
224 | color: #aaa;
225 | }
226 |
227 | .slide-menu-wrapper .slide-menu-panel.active-menu-panel {
228 | visibility: visible;
229 | }
230 |
231 | .slide-menu-wrapper .slide-menu-panel h1,
232 | .slide-menu-wrapper .slide-menu-panel h2,
233 | .slide-menu-wrapper .slide-menu-panel h3,
234 | .slide-menu-wrapper .slide-menu-panel h4,
235 | .slide-menu-wrapper .slide-menu-panel h5,
236 | .slide-menu-wrapper .slide-menu-panel h6 {
237 | margin: 20px 0 10px 0;
238 | color: #fff;
239 | line-height: 1.2;
240 | letter-spacing: normal;
241 | text-shadow: none;
242 | }
243 |
244 | .slide-menu-wrapper .slide-menu-panel h1 {
245 | font-size: 1.6em;
246 | }
247 | .slide-menu-wrapper .slide-menu-panel h2 {
248 | font-size: 1.4em;
249 | }
250 | .slide-menu-wrapper .slide-menu-panel h3 {
251 | font-size: 1.3em;
252 | }
253 | .slide-menu-wrapper .slide-menu-panel h4 {
254 | font-size: 1.1em;
255 | }
256 | .slide-menu-wrapper .slide-menu-panel h5 {
257 | font-size: 1em;
258 | }
259 | .slide-menu-wrapper .slide-menu-panel h6 {
260 | font-size: 0.9em;
261 | }
262 |
263 | .slide-menu-wrapper .slide-menu-panel p {
264 | margin: 10px 0 5px 0;
265 | }
266 |
267 | .slide-menu-wrapper .slide-menu-panel a {
268 | color: #ccc;
269 | text-decoration: underline;
270 | }
271 |
272 | .slide-menu-wrapper .slide-menu-panel a:hover {
273 | color: white;
274 | }
275 |
276 | .slide-menu-wrapper .slide-menu-item a {
277 | text-decoration: none;
278 | }
279 |
280 | .slide-menu-wrapper .slide-menu-custom-panel {
281 | width: calc(100% - 20px);
282 | padding-left: 10px;
283 | padding-right: 10px;
284 | }
285 |
286 | .slide-menu-wrapper .slide-menu-custom-panel .slide-menu-items {
287 | width: calc(100% + 20px);
288 | margin-left: -10px;
289 | margin-right: 10px;
290 | }
291 |
292 | /*
293 | * Theme and Transitions buttons
294 | */
295 |
296 | .slide-menu-wrapper div[data-panel='Themes'] li,
297 | .slide-menu-wrapper div[data-panel='Transitions'] li {
298 | display: block;
299 | text-align: left;
300 | cursor: pointer;
301 | color: #848484;
302 | }
303 |
304 | /*
305 | * Menu controls
306 | */
307 | .reveal .slide-menu-button {
308 | position: fixed;
309 | left: 30px;
310 | bottom: 30px;
311 | z-index: 30;
312 | font-size: 24px;
313 | }
314 |
315 | /*
316 | * Menu overlay
317 | */
318 |
319 | .slide-menu-wrapper .slide-menu-overlay {
320 | position: fixed;
321 | z-index: 199;
322 | top: 0;
323 | left: 0;
324 | overflow: hidden;
325 | width: 0;
326 | height: 0;
327 | background-color: #000;
328 | opacity: 0;
329 | transition: opacity 0.3s, width 0s 0.3s, height 0s 0.3s;
330 | }
331 |
332 | .slide-menu-wrapper .slide-menu-overlay.active {
333 | width: 100%;
334 | height: 100%;
335 | opacity: 0.7;
336 | transition: opacity 0.3s;
337 | }
338 |
339 | /*
340 | * Hide menu for pdf printing
341 | */
342 | body.print-pdf .slide-menu-wrapper .slide-menu,
343 | body.print-pdf .reveal .slide-menu-button,
344 | body.print-pdf .slide-menu-wrapper .slide-menu-overlay {
345 | display: none;
346 | }
347 |
--------------------------------------------------------------------------------
/index_files/libs/revealjs/plugin/reveal-menu/plugin.yml:
--------------------------------------------------------------------------------
1 | name: RevealMenu
2 | script: [menu.js, quarto-menu.js]
3 | stylesheet: [menu.css, quarto-menu.css]
4 | config:
5 | menu:
6 | side: "left"
7 | useTextContentForMissingTitles: true
8 | markers: false
9 | loadIcons: false
10 |
--------------------------------------------------------------------------------
/index_files/libs/revealjs/plugin/reveal-menu/quarto-menu.css:
--------------------------------------------------------------------------------
1 | .slide-menu-wrapper .slide-tool-item {
2 | display: block;
3 | text-align: left;
4 | padding: 10px 18px;
5 | color: #aaa;
6 | cursor: pointer;
7 | border-top: solid 1px #555;
8 | }
9 |
10 | .slide-menu-wrapper .slide-tool-item a {
11 | text-decoration: none;
12 | }
13 |
14 | .slide-menu-wrapper .slide-tool-item kbd {
15 | font-family: monospace;
16 | margin-right: 10px;
17 | padding: 3px 8px;
18 | color: inherit;
19 | border: 1px solid;
20 | border-radius: 5px;
21 | border-color: #555;
22 | }
23 |
24 | .slide-menu-wrapper .slide-menu-toolbar > li.active-toolbar-button {
25 | text-decoration: none;
26 | }
27 |
28 | .reveal .slide-menu-button {
29 | left: 8px;
30 | bottom: 8px;
31 | }
32 |
33 | .reveal .slide-menu-button .fas::before,
34 | .reveal .slide-chalkboard-buttons .fas::before,
35 | .slide-menu-wrapper .slide-menu-toolbar .fas::before {
36 | display: inline-block;
37 | height: 2.2rem;
38 | width: 2.2rem;
39 | content: "";
40 | vertical-align: -0.125em;
41 | background-repeat: no-repeat;
42 | background-size: 2.2rem 2.2rem;
43 | }
44 |
45 | .reveal .slide-chalkboard-buttons .fas::before {
46 | height: 1.45rem;
47 | width: 1.45rem;
48 | background-size: 1.45rem 1.45rem;
49 | vertical-align: 0.1em;
50 | }
51 |
52 | .slide-menu-wrapper .slide-menu-toolbar .fas::before {
53 | height: 1.8rem;
54 | width: 1.8rem;
55 | background-size: 1.8rem 1.8rem;
56 | }
57 |
58 | .slide-menu-wrapper .slide-menu-toolbar .fa-images::before {
59 | background-image: url('data:image/svg+xml,');
60 | }
61 |
62 | .slide-menu-wrapper .slide-menu-toolbar .fa-gear::before {
63 | background-image: url('data:image/svg+xml,');
64 | }
65 |
66 | .slide-menu-wrapper .slide-menu-toolbar .fa-times::before {
67 | background-image: url('data:image/svg+xml,');
68 | }
69 |
--------------------------------------------------------------------------------
/index_files/libs/revealjs/plugin/reveal-menu/quarto-menu.js:
--------------------------------------------------------------------------------
1 | window.revealMenuToolHandler = function (handler) {
2 | return function (event) {
3 | event.preventDefault();
4 | handler();
5 | Reveal.getPlugin("menu").closeMenu();
6 | };
7 | };
8 |
9 | window.RevealMenuToolHandlers = {
10 | fullscreen: revealMenuToolHandler(function () {
11 | const element = document.documentElement;
12 | const requestMethod =
13 | element.requestFullscreen ||
14 | element.webkitRequestFullscreen ||
15 | element.webkitRequestFullScreen ||
16 | element.mozRequestFullScreen ||
17 | element.msRequestFullscreen;
18 | if (requestMethod) {
19 | requestMethod.apply(element);
20 | }
21 | }),
22 | speakerMode: revealMenuToolHandler(function () {
23 | Reveal.getPlugin("notes").open();
24 | }),
25 | keyboardHelp: revealMenuToolHandler(function () {
26 | Reveal.toggleHelp(true);
27 | }),
28 | overview: revealMenuToolHandler(function () {
29 | Reveal.toggleOverview(true);
30 | }),
31 | toggleChalkboard: revealMenuToolHandler(function () {
32 | RevealChalkboard.toggleChalkboard();
33 | }),
34 | toggleNotesCanvas: revealMenuToolHandler(function () {
35 | RevealChalkboard.toggleNotesCanvas();
36 | }),
37 | downloadDrawings: revealMenuToolHandler(function () {
38 | RevealChalkboard.download();
39 | }),
40 | togglePdfExport: revealMenuToolHandler(function () {
41 | PdfExport.togglePdfExport();
42 | }),
43 | };
44 |
--------------------------------------------------------------------------------
/index_files/libs/revealjs/plugin/search/plugin.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Handles finding a text string anywhere in the slides and showing the next occurrence to the user
3 | * by navigatating to that slide and highlighting it.
4 | *
5 | * @author Jon Snyder , February 2013
6 | */
7 |
8 | const Plugin = () => {
9 |
10 | // The reveal.js instance this plugin is attached to
11 | let deck;
12 |
13 | let searchElement;
14 | let searchButton;
15 | let searchInput;
16 |
17 | let matchedSlides;
18 | let currentMatchedIndex;
19 | let searchboxDirty;
20 | let hilitor;
21 |
22 | function render() {
23 |
24 | searchElement = document.createElement( 'div' );
25 | searchElement.classList.add( 'searchbox' );
26 | searchElement.style.position = 'absolute';
27 | searchElement.style.top = '10px';
28 | searchElement.style.right = '10px';
29 | searchElement.style.zIndex = 10;
30 |
31 | //embedded base64 search icon Designed by Sketchdock - http://www.sketchdock.com/:
32 | searchElement.innerHTML = `
33 | `;
34 |
35 | searchInput = searchElement.querySelector( '.searchinput' );
36 | searchInput.style.width = '240px';
37 | searchInput.style.fontSize = '14px';
38 | searchInput.style.padding = '4px 6px';
39 | searchInput.style.color = '#000';
40 | searchInput.style.background = '#fff';
41 | searchInput.style.borderRadius = '2px';
42 | searchInput.style.border = '0';
43 | searchInput.style.outline = '0';
44 | searchInput.style.boxShadow = '0 2px 18px rgba(0, 0, 0, 0.2)';
45 | searchInput.style['-webkit-appearance'] = 'none';
46 |
47 | deck.getRevealElement().appendChild( searchElement );
48 |
49 | // searchButton.addEventListener( 'click', function(event) {
50 | // doSearch();
51 | // }, false );
52 |
53 | searchInput.addEventListener( 'keyup', function( event ) {
54 | switch (event.keyCode) {
55 | case 13:
56 | event.preventDefault();
57 | doSearch();
58 | searchboxDirty = false;
59 | break;
60 | default:
61 | searchboxDirty = true;
62 | }
63 | }, false );
64 |
65 | closeSearch();
66 |
67 | }
68 |
69 | function openSearch() {
70 | if( !searchElement ) render();
71 |
72 | searchElement.style.display = 'inline';
73 | searchInput.focus();
74 | searchInput.select();
75 | }
76 |
77 | function closeSearch() {
78 | if( !searchElement ) render();
79 |
80 | searchElement.style.display = 'none';
81 | if(hilitor) hilitor.remove();
82 | }
83 |
84 | function toggleSearch() {
85 | if( !searchElement ) render();
86 |
87 | if (searchElement.style.display !== 'inline') {
88 | openSearch();
89 | }
90 | else {
91 | closeSearch();
92 | }
93 | }
94 |
95 | function doSearch() {
96 | //if there's been a change in the search term, perform a new search:
97 | if (searchboxDirty) {
98 | var searchstring = searchInput.value;
99 |
100 | if (searchstring === '') {
101 | if(hilitor) hilitor.remove();
102 | matchedSlides = null;
103 | }
104 | else {
105 | //find the keyword amongst the slides
106 | hilitor = new Hilitor("slidecontent");
107 | matchedSlides = hilitor.apply(searchstring);
108 | currentMatchedIndex = 0;
109 | }
110 | }
111 |
112 | if (matchedSlides) {
113 | //navigate to the next slide that has the keyword, wrapping to the first if necessary
114 | if (matchedSlides.length && (matchedSlides.length <= currentMatchedIndex)) {
115 | currentMatchedIndex = 0;
116 | }
117 | if (matchedSlides.length > currentMatchedIndex) {
118 | deck.slide(matchedSlides[currentMatchedIndex].h, matchedSlides[currentMatchedIndex].v);
119 | currentMatchedIndex++;
120 | }
121 | }
122 | }
123 |
124 | // Original JavaScript code by Chirp Internet: www.chirp.com.au
125 | // Please acknowledge use of this code by including this header.
126 | // 2/2013 jon: modified regex to display any match, not restricted to word boundaries.
127 | function Hilitor(id, tag) {
128 |
129 | var targetNode = document.getElementById(id) || document.body;
130 | var hiliteTag = tag || "EM";
131 | var skipTags = new RegExp("^(?:" + hiliteTag + "|SCRIPT|FORM)$");
132 | var colors = ["#ff6", "#a0ffff", "#9f9", "#f99", "#f6f"];
133 | var wordColor = [];
134 | var colorIdx = 0;
135 | var matchRegex = "";
136 | var matchingSlides = [];
137 |
138 | this.setRegex = function(input)
139 | {
140 | input = input.replace(/^[^\w]+|[^\w]+$/g, "").replace(/[^\w'-]+/g, "|");
141 | matchRegex = new RegExp("(" + input + ")","i");
142 | }
143 |
144 | this.getRegex = function()
145 | {
146 | return matchRegex.toString().replace(/^\/\\b\(|\)\\b\/i$/g, "").replace(/\|/g, " ");
147 | }
148 |
149 | // recursively apply word highlighting
150 | this.hiliteWords = function(node)
151 | {
152 | if(node == undefined || !node) return;
153 | if(!matchRegex) return;
154 | if(skipTags.test(node.nodeName)) return;
155 |
156 | if(node.hasChildNodes()) {
157 | for(var i=0; i < node.childNodes.length; i++)
158 | this.hiliteWords(node.childNodes[i]);
159 | }
160 | if(node.nodeType == 3) { // NODE_TEXT
161 | var nv, regs;
162 | if((nv = node.nodeValue) && (regs = matchRegex.exec(nv))) {
163 | //find the slide's section element and save it in our list of matching slides
164 | var secnode = node;
165 | while (secnode != null && secnode.nodeName != 'SECTION') {
166 | secnode = secnode.parentNode;
167 | }
168 |
169 | var slideIndex = deck.getIndices(secnode);
170 | var slidelen = matchingSlides.length;
171 | var alreadyAdded = false;
172 | for (var i=0; i < slidelen; i++) {
173 | if ( (matchingSlides[i].h === slideIndex.h) && (matchingSlides[i].v === slideIndex.v) ) {
174 | alreadyAdded = true;
175 | }
176 | }
177 | if (! alreadyAdded) {
178 | matchingSlides.push(slideIndex);
179 | }
180 |
181 | if(!wordColor[regs[0].toLowerCase()]) {
182 | wordColor[regs[0].toLowerCase()] = colors[colorIdx++ % colors.length];
183 | }
184 |
185 | var match = document.createElement(hiliteTag);
186 | match.appendChild(document.createTextNode(regs[0]));
187 | match.style.backgroundColor = wordColor[regs[0].toLowerCase()];
188 | match.style.fontStyle = "inherit";
189 | match.style.color = "#000";
190 |
191 | var after = node.splitText(regs.index);
192 | after.nodeValue = after.nodeValue.substring(regs[0].length);
193 | node.parentNode.insertBefore(match, after);
194 | }
195 | }
196 | };
197 |
198 | // remove highlighting
199 | this.remove = function()
200 | {
201 | var arr = document.getElementsByTagName(hiliteTag);
202 | var el;
203 | while(arr.length && (el = arr[0])) {
204 | el.parentNode.replaceChild(el.firstChild, el);
205 | }
206 | };
207 |
208 | // start highlighting at target node
209 | this.apply = function(input)
210 | {
211 | if(input == undefined || !input) return;
212 | this.remove();
213 | this.setRegex(input);
214 | this.hiliteWords(targetNode);
215 | return matchingSlides;
216 | };
217 |
218 | }
219 |
220 | return {
221 |
222 | id: 'search',
223 |
224 | init: reveal => {
225 |
226 | deck = reveal;
227 | deck.registerKeyboardShortcut( 'CTRL + Shift + F', 'Search' );
228 |
229 | document.addEventListener( 'keydown', function( event ) {
230 | if( event.key == "F" && (event.ctrlKey || event.metaKey) ) { //Control+Shift+f
231 | event.preventDefault();
232 | toggleSearch();
233 | }
234 | }, false );
235 |
236 | },
237 |
238 | open: openSearch
239 |
240 | }
241 | };
242 |
243 | export default Plugin;
--------------------------------------------------------------------------------
/index_files/libs/revealjs/plugin/zoom/plugin.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * reveal.js Zoom plugin
3 | */
4 | const Plugin = {
5 |
6 | id: 'zoom',
7 |
8 | init: function( reveal ) {
9 |
10 | reveal.getRevealElement().addEventListener( 'mousedown', function( event ) {
11 | var defaultModifier = /Linux/.test( window.navigator.platform ) ? 'ctrl' : 'alt';
12 |
13 | var modifier = ( reveal.getConfig().zoomKey ? reveal.getConfig().zoomKey : defaultModifier ) + 'Key';
14 | var zoomLevel = ( reveal.getConfig().zoomLevel ? reveal.getConfig().zoomLevel : 2 );
15 |
16 | if( event[ modifier ] && !reveal.isOverview() ) {
17 | event.preventDefault();
18 |
19 | zoom.to({
20 | x: event.clientX,
21 | y: event.clientY,
22 | scale: zoomLevel,
23 | pan: false
24 | });
25 | }
26 | } );
27 |
28 | },
29 |
30 | destroy: () => {
31 |
32 | zoom.reset();
33 |
34 | }
35 |
36 | };
37 |
38 | export default () => Plugin;
39 |
40 | /*!
41 | * zoom.js 0.3 (modified for use with reveal.js)
42 | * http://lab.hakim.se/zoom-js
43 | * MIT licensed
44 | *
45 | * Copyright (C) 2011-2014 Hakim El Hattab, http://hakim.se
46 | */
47 | var zoom = (function(){
48 |
49 | // The current zoom level (scale)
50 | var level = 1;
51 |
52 | // The current mouse position, used for panning
53 | var mouseX = 0,
54 | mouseY = 0;
55 |
56 | // Timeout before pan is activated
57 | var panEngageTimeout = -1,
58 | panUpdateInterval = -1;
59 |
60 | // Check for transform support so that we can fallback otherwise
61 | var supportsTransforms = 'transform' in document.body.style;
62 |
63 | if( supportsTransforms ) {
64 | // The easing that will be applied when we zoom in/out
65 | document.body.style.transition = 'transform 0.8s ease';
66 | }
67 |
68 | // Zoom out if the user hits escape
69 | document.addEventListener( 'keyup', function( event ) {
70 | if( level !== 1 && event.keyCode === 27 ) {
71 | zoom.out();
72 | }
73 | } );
74 |
75 | // Monitor mouse movement for panning
76 | document.addEventListener( 'mousemove', function( event ) {
77 | if( level !== 1 ) {
78 | mouseX = event.clientX;
79 | mouseY = event.clientY;
80 | }
81 | } );
82 |
83 | /**
84 | * Applies the CSS required to zoom in, prefers the use of CSS3
85 | * transforms but falls back on zoom for IE.
86 | *
87 | * @param {Object} rect
88 | * @param {Number} scale
89 | */
90 | function magnify( rect, scale ) {
91 |
92 | var scrollOffset = getScrollOffset();
93 |
94 | // Ensure a width/height is set
95 | rect.width = rect.width || 1;
96 | rect.height = rect.height || 1;
97 |
98 | // Center the rect within the zoomed viewport
99 | rect.x -= ( window.innerWidth - ( rect.width * scale ) ) / 2;
100 | rect.y -= ( window.innerHeight - ( rect.height * scale ) ) / 2;
101 |
102 | if( supportsTransforms ) {
103 | // Reset
104 | if( scale === 1 ) {
105 | document.body.style.transform = '';
106 | }
107 | // Scale
108 | else {
109 | var origin = scrollOffset.x +'px '+ scrollOffset.y +'px',
110 | transform = 'translate('+ -rect.x +'px,'+ -rect.y +'px) scale('+ scale +')';
111 |
112 | document.body.style.transformOrigin = origin;
113 | document.body.style.transform = transform;
114 | }
115 | }
116 | else {
117 | // Reset
118 | if( scale === 1 ) {
119 | document.body.style.position = '';
120 | document.body.style.left = '';
121 | document.body.style.top = '';
122 | document.body.style.width = '';
123 | document.body.style.height = '';
124 | document.body.style.zoom = '';
125 | }
126 | // Scale
127 | else {
128 | document.body.style.position = 'relative';
129 | document.body.style.left = ( - ( scrollOffset.x + rect.x ) / scale ) + 'px';
130 | document.body.style.top = ( - ( scrollOffset.y + rect.y ) / scale ) + 'px';
131 | document.body.style.width = ( scale * 100 ) + '%';
132 | document.body.style.height = ( scale * 100 ) + '%';
133 | document.body.style.zoom = scale;
134 | }
135 | }
136 |
137 | level = scale;
138 |
139 | if( document.documentElement.classList ) {
140 | if( level !== 1 ) {
141 | document.documentElement.classList.add( 'zoomed' );
142 | }
143 | else {
144 | document.documentElement.classList.remove( 'zoomed' );
145 | }
146 | }
147 | }
148 |
149 | /**
150 | * Pan the document when the mosue cursor approaches the edges
151 | * of the window.
152 | */
153 | function pan() {
154 | var range = 0.12,
155 | rangeX = window.innerWidth * range,
156 | rangeY = window.innerHeight * range,
157 | scrollOffset = getScrollOffset();
158 |
159 | // Up
160 | if( mouseY < rangeY ) {
161 | window.scroll( scrollOffset.x, scrollOffset.y - ( 1 - ( mouseY / rangeY ) ) * ( 14 / level ) );
162 | }
163 | // Down
164 | else if( mouseY > window.innerHeight - rangeY ) {
165 | window.scroll( scrollOffset.x, scrollOffset.y + ( 1 - ( window.innerHeight - mouseY ) / rangeY ) * ( 14 / level ) );
166 | }
167 |
168 | // Left
169 | if( mouseX < rangeX ) {
170 | window.scroll( scrollOffset.x - ( 1 - ( mouseX / rangeX ) ) * ( 14 / level ), scrollOffset.y );
171 | }
172 | // Right
173 | else if( mouseX > window.innerWidth - rangeX ) {
174 | window.scroll( scrollOffset.x + ( 1 - ( window.innerWidth - mouseX ) / rangeX ) * ( 14 / level ), scrollOffset.y );
175 | }
176 | }
177 |
178 | function getScrollOffset() {
179 | return {
180 | x: window.scrollX !== undefined ? window.scrollX : window.pageXOffset,
181 | y: window.scrollY !== undefined ? window.scrollY : window.pageYOffset
182 | }
183 | }
184 |
185 | return {
186 | /**
187 | * Zooms in on either a rectangle or HTML element.
188 | *
189 | * @param {Object} options
190 | * - element: HTML element to zoom in on
191 | * OR
192 | * - x/y: coordinates in non-transformed space to zoom in on
193 | * - width/height: the portion of the screen to zoom in on
194 | * - scale: can be used instead of width/height to explicitly set scale
195 | */
196 | to: function( options ) {
197 |
198 | // Due to an implementation limitation we can't zoom in
199 | // to another element without zooming out first
200 | if( level !== 1 ) {
201 | zoom.out();
202 | }
203 | else {
204 | options.x = options.x || 0;
205 | options.y = options.y || 0;
206 |
207 | // If an element is set, that takes precedence
208 | if( !!options.element ) {
209 | // Space around the zoomed in element to leave on screen
210 | var padding = 20;
211 | var bounds = options.element.getBoundingClientRect();
212 |
213 | options.x = bounds.left - padding;
214 | options.y = bounds.top - padding;
215 | options.width = bounds.width + ( padding * 2 );
216 | options.height = bounds.height + ( padding * 2 );
217 | }
218 |
219 | // If width/height values are set, calculate scale from those values
220 | if( options.width !== undefined && options.height !== undefined ) {
221 | options.scale = Math.max( Math.min( window.innerWidth / options.width, window.innerHeight / options.height ), 1 );
222 | }
223 |
224 | if( options.scale > 1 ) {
225 | options.x *= options.scale;
226 | options.y *= options.scale;
227 |
228 | magnify( options, options.scale );
229 |
230 | if( options.pan !== false ) {
231 |
232 | // Wait with engaging panning as it may conflict with the
233 | // zoom transition
234 | panEngageTimeout = setTimeout( function() {
235 | panUpdateInterval = setInterval( pan, 1000 / 60 );
236 | }, 800 );
237 |
238 | }
239 | }
240 | }
241 | },
242 |
243 | /**
244 | * Resets the document zoom state to its default.
245 | */
246 | out: function() {
247 | clearTimeout( panEngageTimeout );
248 | clearInterval( panUpdateInterval );
249 |
250 | magnify( { x: 0, y: 0 }, 1 );
251 |
252 | level = 1;
253 | },
254 |
255 | // Alias
256 | magnify: function( options ) { this.to( options ) },
257 | reset: function() { this.out() },
258 |
259 | zoomLevel: function() {
260 | return level;
261 | }
262 | }
263 |
264 | })();
265 |
--------------------------------------------------------------------------------
/index_files/libs/revealjs/plugin/zoom/zoom.esm.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * reveal.js Zoom plugin
3 | */
4 | 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();owindow.innerHeight-i&&window.scroll(d.x,d.y+(1-(window.innerHeight-o)/i)*(14/e)),nwindow.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}
5 |
--------------------------------------------------------------------------------
/index_files/libs/revealjs/plugin/zoom/zoom.js:
--------------------------------------------------------------------------------
1 | !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";
2 | /*!
3 | * reveal.js Zoom plugin
4 | */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();nwindow.innerHeight-i&&window.scroll(d.x,d.y+(1-(window.innerHeight-n)/i)*(14/e)),owindow.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}}));
5 |
--------------------------------------------------------------------------------
/learning-statistics-with-webR.Rproj:
--------------------------------------------------------------------------------
1 | Version: 1.0
2 |
3 | RestoreWorkspace: Default
4 | SaveWorkspace: Default
5 | AlwaysSaveHistory: Default
6 |
7 | EnableCodeIndexing: Yes
8 | UseSpacesForTab: Yes
9 | NumSpacesForTab: 2
10 | Encoding: UTF-8
11 |
12 | RnwWeave: Sweave
13 | LaTeX: pdfLaTeX
14 |
--------------------------------------------------------------------------------
/webr-serviceworker.js:
--------------------------------------------------------------------------------
1 | importScripts('https://webr.r-wasm.org/v0.2.2/webr-serviceworker.js');
2 |
--------------------------------------------------------------------------------
/webr-worker.js:
--------------------------------------------------------------------------------
1 | importScripts('https://webr.r-wasm.org/v0.2.2/webr-worker.js');
2 |
--------------------------------------------------------------------------------