mirror of https://codeberg.org/Sonoj/osamc.de
1098 lines
37 KiB
HTML
1098 lines
37 KiB
HTML
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, minimal-ui"><title>mod-host vs Pisound button</title><link rel="stylesheet" href="node_modules/reveal.js/dist/reset.css"><link rel="stylesheet" href="node_modules/reveal.js/dist/reveal.css"><link rel="stylesheet" href="node_modules/reveal.js/dist/theme/black.css" id="theme"><!--This CSS is generated by the Asciidoctor reveal.js converter to further integrate AsciiDoc's existing semantic with reveal.js--><style type="text/css">.reveal div.right {
|
||
|
float: right
|
||
|
}
|
||
|
|
||
|
/* source blocks */
|
||
|
.reveal .listingblock.stretch > .content {
|
||
|
height: 100%
|
||
|
}
|
||
|
|
||
|
.reveal .listingblock.stretch > .content > pre {
|
||
|
height: 100%
|
||
|
}
|
||
|
|
||
|
.reveal .listingblock.stretch > .content > pre > code {
|
||
|
height: 100%;
|
||
|
max-height: 100%
|
||
|
}
|
||
|
|
||
|
/* auto-animate feature */
|
||
|
/* hide the scrollbar when auto-animating source blocks */
|
||
|
.reveal pre[data-auto-animate-target] {
|
||
|
overflow: hidden;
|
||
|
}
|
||
|
|
||
|
.reveal pre[data-auto-animate-target] code {
|
||
|
overflow: hidden;
|
||
|
}
|
||
|
|
||
|
/* add a min width to avoid horizontal shift on line numbers */
|
||
|
code.hljs .hljs-ln-line.hljs-ln-n {
|
||
|
min-width: 1.25em;
|
||
|
}
|
||
|
|
||
|
/* tables */
|
||
|
table {
|
||
|
border-collapse: collapse;
|
||
|
border-spacing: 0
|
||
|
}
|
||
|
|
||
|
table {
|
||
|
margin-bottom: 1.25em;
|
||
|
border: solid 1px #dedede
|
||
|
}
|
||
|
|
||
|
table thead tr th, table thead tr td, table tfoot tr th, table tfoot tr td {
|
||
|
padding: .5em .625em .625em;
|
||
|
font-size: inherit;
|
||
|
text-align: left
|
||
|
}
|
||
|
|
||
|
table tr th, table tr td {
|
||
|
padding: .5625em .625em;
|
||
|
font-size: inherit
|
||
|
}
|
||
|
|
||
|
table thead tr th, table tfoot tr th, table tbody tr td, table tr td, table tfoot tr td {
|
||
|
display: table-cell;
|
||
|
line-height: 1.6
|
||
|
}
|
||
|
|
||
|
td.tableblock > .content {
|
||
|
margin-bottom: 1.25em
|
||
|
}
|
||
|
|
||
|
td.tableblock > .content > :last-child {
|
||
|
margin-bottom: -1.25em
|
||
|
}
|
||
|
|
||
|
table.tableblock, th.tableblock, td.tableblock {
|
||
|
border: 0 solid #dedede
|
||
|
}
|
||
|
|
||
|
table.grid-all > thead > tr > .tableblock, table.grid-all > tbody > tr > .tableblock {
|
||
|
border-width: 0 1px 1px 0
|
||
|
}
|
||
|
|
||
|
table.grid-all > tfoot > tr > .tableblock {
|
||
|
border-width: 1px 1px 0 0
|
||
|
}
|
||
|
|
||
|
table.grid-cols > * > tr > .tableblock {
|
||
|
border-width: 0 1px 0 0
|
||
|
}
|
||
|
|
||
|
table.grid-rows > thead > tr > .tableblock, table.grid-rows > tbody > tr > .tableblock {
|
||
|
border-width: 0 0 1px
|
||
|
}
|
||
|
|
||
|
table.grid-rows > tfoot > tr > .tableblock {
|
||
|
border-width: 1px 0 0
|
||
|
}
|
||
|
|
||
|
table.grid-all > * > tr > .tableblock:last-child, table.grid-cols > * > tr > .tableblock:last-child {
|
||
|
border-right-width: 0
|
||
|
}
|
||
|
|
||
|
table.grid-all > tbody > tr:last-child > .tableblock, table.grid-all > thead:last-child > tr > .tableblock, table.grid-rows > tbody > tr:last-child > .tableblock, table.grid-rows > thead:last-child > tr > .tableblock {
|
||
|
border-bottom-width: 0
|
||
|
}
|
||
|
|
||
|
table.frame-all {
|
||
|
border-width: 1px
|
||
|
}
|
||
|
|
||
|
table.frame-sides {
|
||
|
border-width: 0 1px
|
||
|
}
|
||
|
|
||
|
table.frame-topbot, table.frame-ends {
|
||
|
border-width: 1px 0
|
||
|
}
|
||
|
|
||
|
.reveal table th.halign-left, .reveal table td.halign-left {
|
||
|
text-align: left
|
||
|
}
|
||
|
|
||
|
.reveal table th.halign-right, .reveal table td.halign-right {
|
||
|
text-align: right
|
||
|
}
|
||
|
|
||
|
.reveal table th.halign-center, .reveal table td.halign-center {
|
||
|
text-align: center
|
||
|
}
|
||
|
|
||
|
.reveal table th.valign-top, .reveal table td.valign-top {
|
||
|
vertical-align: top
|
||
|
}
|
||
|
|
||
|
.reveal table th.valign-bottom, .reveal table td.valign-bottom {
|
||
|
vertical-align: bottom
|
||
|
}
|
||
|
|
||
|
.reveal table th.valign-middle, .reveal table td.valign-middle {
|
||
|
vertical-align: middle
|
||
|
}
|
||
|
|
||
|
table thead th, table tfoot th {
|
||
|
font-weight: bold
|
||
|
}
|
||
|
|
||
|
tbody tr th {
|
||
|
display: table-cell;
|
||
|
line-height: 1.6
|
||
|
}
|
||
|
|
||
|
tbody tr th, tbody tr th p, tfoot tr th, tfoot tr th p {
|
||
|
font-weight: bold
|
||
|
}
|
||
|
|
||
|
thead {
|
||
|
display: table-header-group
|
||
|
}
|
||
|
|
||
|
.reveal table.grid-none th, .reveal table.grid-none td {
|
||
|
border-bottom: 0 !important
|
||
|
}
|
||
|
|
||
|
/* kbd macro */
|
||
|
kbd {
|
||
|
font-family: "Droid Sans Mono", "DejaVu Sans Mono", monospace;
|
||
|
display: inline-block;
|
||
|
color: rgba(0, 0, 0, .8);
|
||
|
font-size: .65em;
|
||
|
line-height: 1.45;
|
||
|
background: #f7f7f7;
|
||
|
border: 1px solid #ccc;
|
||
|
-webkit-border-radius: 3px;
|
||
|
border-radius: 3px;
|
||
|
-webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, .2), 0 0 0 .1em white inset;
|
||
|
box-shadow: 0 1px 0 rgba(0, 0, 0, .2), 0 0 0 .1em #fff inset;
|
||
|
margin: 0 .15em;
|
||
|
padding: .2em .5em;
|
||
|
vertical-align: middle;
|
||
|
position: relative;
|
||
|
top: -.1em;
|
||
|
white-space: nowrap
|
||
|
}
|
||
|
|
||
|
.keyseq kbd:first-child {
|
||
|
margin-left: 0
|
||
|
}
|
||
|
|
||
|
.keyseq kbd:last-child {
|
||
|
margin-right: 0
|
||
|
}
|
||
|
|
||
|
/* callouts */
|
||
|
.conum[data-value] {
|
||
|
display: inline-block;
|
||
|
color: #fff !important;
|
||
|
background: rgba(0, 0, 0, .8);
|
||
|
-webkit-border-radius: 50%;
|
||
|
border-radius: 50%;
|
||
|
text-align: center;
|
||
|
font-size: .75em;
|
||
|
width: 1.67em;
|
||
|
height: 1.67em;
|
||
|
line-height: 1.67em;
|
||
|
font-family: "Open Sans", "DejaVu Sans", sans-serif;
|
||
|
font-style: normal;
|
||
|
font-weight: bold
|
||
|
}
|
||
|
|
||
|
.conum[data-value] * {
|
||
|
color: #fff !important
|
||
|
}
|
||
|
|
||
|
.conum[data-value] + b {
|
||
|
display: none
|
||
|
}
|
||
|
|
||
|
.conum[data-value]:after {
|
||
|
content: attr(data-value)
|
||
|
}
|
||
|
|
||
|
pre .conum[data-value] {
|
||
|
position: relative;
|
||
|
top: -.125em
|
||
|
}
|
||
|
|
||
|
b.conum * {
|
||
|
color: inherit !important
|
||
|
}
|
||
|
|
||
|
.conum:not([data-value]):empty {
|
||
|
display: none
|
||
|
}
|
||
|
|
||
|
/* Callout list */
|
||
|
.hdlist > table, .colist > table {
|
||
|
border: 0;
|
||
|
background: none
|
||
|
}
|
||
|
|
||
|
.hdlist > table > tbody > tr, .colist > table > tbody > tr {
|
||
|
background: none
|
||
|
}
|
||
|
|
||
|
td.hdlist1, td.hdlist2 {
|
||
|
vertical-align: top;
|
||
|
padding: 0 .625em
|
||
|
}
|
||
|
|
||
|
td.hdlist1 {
|
||
|
font-weight: bold;
|
||
|
padding-bottom: 1.25em
|
||
|
}
|
||
|
|
||
|
/* Disabled from Asciidoctor CSS because it caused callout list to go under the
|
||
|
* source listing when .stretch is applied (see #335)
|
||
|
* .literalblock+.colist,.listingblock+.colist{margin-top:-.5em} */
|
||
|
.colist td:not([class]):first-child {
|
||
|
padding: .4em .75em 0;
|
||
|
line-height: 1;
|
||
|
vertical-align: top
|
||
|
}
|
||
|
|
||
|
.colist td:not([class]):first-child img {
|
||
|
max-width: none
|
||
|
}
|
||
|
|
||
|
.colist td:not([class]):last-child {
|
||
|
padding: .25em 0
|
||
|
}
|
||
|
|
||
|
/* Override Asciidoctor CSS that causes issues with reveal.js features */
|
||
|
.reveal .hljs table {
|
||
|
border: 0
|
||
|
}
|
||
|
|
||
|
/* Callout list rows would have a bottom border with some reveal.js themes (see #335) */
|
||
|
.reveal .colist > table th, .reveal .colist > table td {
|
||
|
border-bottom: 0
|
||
|
}
|
||
|
|
||
|
/* Fixes line height with Highlight.js source listing when linenums enabled (see #331) */
|
||
|
.reveal .hljs table thead tr th, .reveal .hljs table tfoot tr th, .reveal .hljs table tbody tr td, .reveal .hljs table tr td, .reveal .hljs table tfoot tr td {
|
||
|
line-height: inherit
|
||
|
}
|
||
|
|
||
|
/* Columns layout */
|
||
|
.columns .slide-content {
|
||
|
display: flex;
|
||
|
}
|
||
|
|
||
|
.columns.wrap .slide-content {
|
||
|
flex-wrap: wrap;
|
||
|
}
|
||
|
|
||
|
.columns.is-vcentered .slide-content {
|
||
|
align-items: center;
|
||
|
}
|
||
|
|
||
|
.columns .slide-content > .column {
|
||
|
display: block;
|
||
|
flex-basis: 0;
|
||
|
flex-grow: 1;
|
||
|
flex-shrink: 1;
|
||
|
}
|
||
|
|
||
|
.columns .slide-content > .column > * {
|
||
|
padding: .75rem;
|
||
|
}
|
||
|
|
||
|
/* See #353 */
|
||
|
.columns.wrap .slide-content > .column {
|
||
|
flex-basis: auto;
|
||
|
}
|
||
|
|
||
|
.columns .slide-content > .column.is-full {
|
||
|
flex: none;
|
||
|
width: 100%;
|
||
|
}
|
||
|
|
||
|
.columns .slide-content > .column.is-four-fifths {
|
||
|
flex: none;
|
||
|
width: 80%;
|
||
|
}
|
||
|
|
||
|
.columns .slide-content > .column.is-three-quarters {
|
||
|
flex: none;
|
||
|
width: 75%;
|
||
|
}
|
||
|
|
||
|
.columns .slide-content > .column.is-two-thirds {
|
||
|
flex: none;
|
||
|
width: 66.6666%;
|
||
|
}
|
||
|
|
||
|
.columns .slide-content > .column.is-three-fifths {
|
||
|
flex: none;
|
||
|
width: 60%;
|
||
|
}
|
||
|
|
||
|
.columns .slide-content > .column.is-half {
|
||
|
flex: none;
|
||
|
width: 50%;
|
||
|
}
|
||
|
|
||
|
.columns .slide-content > .column.is-two-fifths {
|
||
|
flex: none;
|
||
|
width: 40%;
|
||
|
}
|
||
|
|
||
|
.columns .slide-content > .column.is-one-third {
|
||
|
flex: none;
|
||
|
width: 33.3333%;
|
||
|
}
|
||
|
|
||
|
.columns .slide-content > .column.is-one-quarter {
|
||
|
flex: none;
|
||
|
width: 25%;
|
||
|
}
|
||
|
|
||
|
.columns .slide-content > .column.is-one-fifth {
|
||
|
flex: none;
|
||
|
width: 20%;
|
||
|
}
|
||
|
|
||
|
.columns .slide-content > .column.has-text-left {
|
||
|
text-align: left;
|
||
|
}
|
||
|
|
||
|
.columns .slide-content > .column.has-text-justified {
|
||
|
text-align: justify;
|
||
|
}
|
||
|
|
||
|
.columns .slide-content > .column.has-text-right {
|
||
|
text-align: right;
|
||
|
}
|
||
|
|
||
|
.columns .slide-content > .column.has-text-left {
|
||
|
text-align: left;
|
||
|
}
|
||
|
|
||
|
.columns .slide-content > .column.has-text-justified {
|
||
|
text-align: justify;
|
||
|
}
|
||
|
|
||
|
.columns .slide-content > .column.has-text-right {
|
||
|
text-align: right;
|
||
|
}
|
||
|
|
||
|
.text-left {
|
||
|
text-align: left !important
|
||
|
}
|
||
|
|
||
|
.text-right {
|
||
|
text-align: right !important
|
||
|
}
|
||
|
|
||
|
.text-center {
|
||
|
text-align: center !important
|
||
|
}
|
||
|
|
||
|
.text-justify {
|
||
|
text-align: justify !important
|
||
|
}
|
||
|
|
||
|
.footnotes {
|
||
|
border-top: 1px solid rgba(0, 0, 0, 0.2);
|
||
|
padding: 0.5em 0 0 0;
|
||
|
font-size: 0.65em;
|
||
|
margin-top: 4em;
|
||
|
}
|
||
|
|
||
|
.byline {
|
||
|
font-size:.8em
|
||
|
}
|
||
|
ul.byline {
|
||
|
list-style-type: none;
|
||
|
}
|
||
|
ul.byline li + li {
|
||
|
margin-top: 0.25em;
|
||
|
}
|
||
|
</style><link rel="stylesheet" href="presentation.css"></head><body><div class="reveal"><div class="slides"><section class="title" data-state="title"><h1>mod-host vs Pisound button</h1><div class="preamble"><div class="paragraph"><p><a href="https://https://sonoj.org/" target="_blank">Sonoj Convention</a></p></div>
|
||
|
<div class="paragraph"><p><a href="https://github.com/danielappelt" target="_blank">Daniel Appelt</a> / 2023-09-30</p></div></div></section>
|
||
|
<section id="_pisound_the_button"><h2>Pisound & the button</h2><div class="slide-content"><div class="ulist"><ul><li class="fragment"><p><a href="https://blokas.io/pisound/">Pisound</a> is a sound card and MIDI interface for the Raspberry Pi</p></li><li class="fragment"><p>It comes with one <a href="https://github.com/BlokasLabs/pisound/blob/master/pisound-btn/pisound.conf">configurable button</a></p></li><li class="fragment"><p>Bash scripts are used for button action (<a href="scripts/pisound-btn/toggle_wifi_hotspot.sh">example</a>)</p></li></ul></div></div></section>
|
||
|
<section id="_mod_host"><h2>mod-host</h2><div class="slide-content"><div class="ulist"><ul><li class="fragment"><p><a href="https://github.com/moddevices/mod-host">mod-host</a> is an LV2 host for JACK, controllable via socket or command line</p></li><li class="fragment"><p><a href="https://github.com/moddevices/mod-ui">mod-ui</a> is started together with mod-host in MODEP</p></li><li class="fragment"><p>mod-ui uses file socket to communicate with mod-host</p></li></ul></div></div></section>
|
||
|
<section id="_problem"><h2>Problem</h2><div class="slide-content"><div class="ulist"><ul><li class="fragment"><p>Syncing mod-host as secondary via MIDI clock was unreliable</p></li><li class="fragment"><p>How to start/stop transport in mod-host as MIDI clock primary not using mod-ui?</p></li></ul></div></div></section>
|
||
|
<section id="_solution"><h2>Solution</h2><div class="slide-content"><div class="ulist"><ul><li class="fragment"><p>Let Pisound button start/stop mod-host via web socket</p></li><li class="fragment"><p><a href="https://github.com/vi/websocat">websocat</a> is like netcat, curl and socat for WebSockets</p></li><li class="fragment"><p>Example script to start/stop transport in mod-host</p></li></ul></div></div></section>
|
||
|
<section id="_code"><h2>Code</h2><div class="slide-content"><div class="listingblock"><div class="content"><pre class="highlightjs highlight"><code class="language-bash hljs" data-noescape="true" data-lang="bash">#!/bin/sh
|
||
|
|
||
|
. /usr/local/pisound/scripts/common/common.sh
|
||
|
|
||
|
# Use websocket interface to mod-ui as the regular socket is exclusively used for
|
||
|
# communication between mod-ui and mod-host.
|
||
|
|
||
|
if [ -z $(echo -n '' | /usr/local/bin/websocat --text ws://127.0.0.1:80/websocket/ | grep 'transport 1') ]; then
|
||
|
# Start transport and blinking
|
||
|
echo 'transport-rolling 1' | /usr/local/bin/websocat ws://127.0.0.1:80/websocket/
|
||
|
else
|
||
|
# Stop transport and blinking
|
||
|
echo 'transport-rolling 0' | /usr/local/bin/websocat ws://127.0.0.1:80/websocket/
|
||
|
fi</code></pre></div></div></div></section></div></div><script src="node_modules/reveal.js/dist/reveal.js"></script><script>Array.prototype.slice.call(document.querySelectorAll('.slides section')).forEach(function(slide) {
|
||
|
if (slide.getAttribute('data-background-color')) return;
|
||
|
// user needs to explicitly say he wants CSS color to override otherwise we might break custom css or theme (#226)
|
||
|
if (!(slide.classList.contains('canvas') || slide.classList.contains('background'))) return;
|
||
|
var bgColor = getComputedStyle(slide).backgroundColor;
|
||
|
if (bgColor !== 'rgba(0, 0, 0, 0)' && bgColor !== 'transparent') {
|
||
|
slide.setAttribute('data-background-color', bgColor);
|
||
|
slide.style.backgroundColor = 'transparent';
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// More info about config & dependencies:
|
||
|
// - https://github.com/hakimel/reveal.js#configuration
|
||
|
// - https://github.com/hakimel/reveal.js#dependencies
|
||
|
Reveal.initialize({
|
||
|
// Display presentation control arrows
|
||
|
controls: true,
|
||
|
// Help the user learn the controls by providing hints, for example by
|
||
|
// bouncing the down arrow when they first encounter a vertical slide
|
||
|
controlsTutorial: true,
|
||
|
// Determines where controls appear, "edges" or "bottom-right"
|
||
|
controlsLayout: 'bottom-right',
|
||
|
// Visibility rule for backwards navigation arrows; "faded", "hidden"
|
||
|
// or "visible"
|
||
|
controlsBackArrows: 'faded',
|
||
|
// Display a presentation progress bar
|
||
|
progress: true,
|
||
|
// Display the page number of the current slide
|
||
|
slideNumber: false,
|
||
|
// Control which views the slide number displays on
|
||
|
showSlideNumber: 'all',
|
||
|
// Add the current slide number to the URL hash so that reloading the
|
||
|
// page/copying the URL will return you to the same slide
|
||
|
hash: false,
|
||
|
// Push each slide change to the browser history. Implies `hash: true`
|
||
|
history: false,
|
||
|
// Enable keyboard shortcuts for navigation
|
||
|
keyboard: true,
|
||
|
// Enable the slide overview mode
|
||
|
overview: true,
|
||
|
// Disables the default reveal.js slide layout so that you can use custom CSS layout
|
||
|
disableLayout: false,
|
||
|
// Vertical centering of slides
|
||
|
center: true,
|
||
|
// Enables touch navigation on devices with touch input
|
||
|
touch: true,
|
||
|
// Loop the presentation
|
||
|
loop: false,
|
||
|
// Change the presentation direction to be RTL
|
||
|
rtl: false,
|
||
|
// See https://github.com/hakimel/reveal.js/#navigation-mode
|
||
|
navigationMode: 'default',
|
||
|
// Randomizes the order of slides each time the presentation loads
|
||
|
shuffle: false,
|
||
|
// Turns fragments on and off globally
|
||
|
fragments: true,
|
||
|
// Flags whether to include the current fragment in the URL,
|
||
|
// so that reloading brings you to the same fragment position
|
||
|
fragmentInURL: false,
|
||
|
// Flags if the presentation is running in an embedded mode,
|
||
|
// i.e. contained within a limited portion of the screen
|
||
|
embedded: false,
|
||
|
// Flags if we should show a help overlay when the questionmark
|
||
|
// key is pressed
|
||
|
help: true,
|
||
|
// Flags if speaker notes should be visible to all viewers
|
||
|
showNotes: false,
|
||
|
// Global override for autolaying embedded media (video/audio/iframe)
|
||
|
// - null: Media will only autoplay if data-autoplay is present
|
||
|
// - true: All media will autoplay, regardless of individual setting
|
||
|
// - false: No media will autoplay, regardless of individual setting
|
||
|
autoPlayMedia: null,
|
||
|
// Global override for preloading lazy-loaded iframes
|
||
|
// - null: Iframes with data-src AND data-preload will be loaded when within
|
||
|
// the viewDistance, iframes with only data-src will be loaded when visible
|
||
|
// - true: All iframes with data-src will be loaded when within the viewDistance
|
||
|
// - false: All iframes with data-src will be loaded only when visible
|
||
|
preloadIframes: null,
|
||
|
// Number of milliseconds between automatically proceeding to the
|
||
|
// next slide, disabled when set to 0, this value can be overwritten
|
||
|
// by using a data-autoslide attribute on your slides
|
||
|
autoSlide: 0,
|
||
|
// Stop auto-sliding after user input
|
||
|
autoSlideStoppable: true,
|
||
|
// Use this method for navigation when auto-sliding
|
||
|
autoSlideMethod: Reveal.navigateNext,
|
||
|
// Specify the average time in seconds that you think you will spend
|
||
|
// presenting each slide. This is used to show a pacing timer in the
|
||
|
// speaker view
|
||
|
defaultTiming: 120,
|
||
|
// Specify the total time in seconds that is available to
|
||
|
// present. If this is set to a nonzero value, the pacing
|
||
|
// timer will work out the time available for each slide,
|
||
|
// instead of using the defaultTiming value
|
||
|
totalTime: 0,
|
||
|
// Specify the minimum amount of time you want to allot to
|
||
|
// each slide, if using the totalTime calculation method. If
|
||
|
// the automated time allocation causes slide pacing to fall
|
||
|
// below this threshold, then you will see an alert in the
|
||
|
// speaker notes window
|
||
|
minimumTimePerSlide: 0,
|
||
|
// Enable slide navigation via mouse wheel
|
||
|
mouseWheel: false,
|
||
|
// Hide cursor if inactive
|
||
|
hideInactiveCursor: true,
|
||
|
// Time before the cursor is hidden (in ms)
|
||
|
hideCursorTime: 5000,
|
||
|
// Hides the address bar on mobile devices
|
||
|
hideAddressBar: true,
|
||
|
// Opens links in an iframe preview overlay
|
||
|
// Add `data-preview-link` and `data-preview-link="false"` to customise each link
|
||
|
// individually
|
||
|
previewLinks: false,
|
||
|
// Transition style (e.g., none, fade, slide, convex, concave, zoom)
|
||
|
transition: 'slide',
|
||
|
// Transition speed (e.g., default, fast, slow)
|
||
|
transitionSpeed: 'default',
|
||
|
// Transition style for full page slide backgrounds (e.g., none, fade, slide, convex, concave, zoom)
|
||
|
backgroundTransition: 'fade',
|
||
|
// Number of slides away from the current that are visible
|
||
|
viewDistance: 3,
|
||
|
// Number of slides away from the current that are visible on mobile
|
||
|
// devices. It is advisable to set this to a lower number than
|
||
|
// viewDistance in order to save resources.
|
||
|
mobileViewDistance: 3,
|
||
|
// Parallax background image (e.g., "'https://s3.amazonaws.com/hakim-static/reveal-js/reveal-parallax-1.jpg'")
|
||
|
parallaxBackgroundImage: '',
|
||
|
// Parallax background size in CSS syntax (e.g., "2100px 900px")
|
||
|
parallaxBackgroundSize: '',
|
||
|
// Number of pixels to move the parallax background per slide
|
||
|
// - Calculated automatically unless specified
|
||
|
// - Set to 0 to disable movement along an axis
|
||
|
parallaxBackgroundHorizontal: null,
|
||
|
parallaxBackgroundVertical: null,
|
||
|
// The display mode that will be used to show slides
|
||
|
display: 'block',
|
||
|
|
||
|
// The "normal" size of the presentation, aspect ratio will be preserved
|
||
|
// when the presentation is scaled to fit different resolutions. Can be
|
||
|
// specified using percentage units.
|
||
|
width: 960,
|
||
|
height: 700,
|
||
|
|
||
|
// Factor of the display size that should remain empty around the content
|
||
|
margin: 0.1,
|
||
|
|
||
|
// Bounds for smallest/largest possible scale to apply to content
|
||
|
minScale: 0.2,
|
||
|
maxScale: 1.5,
|
||
|
|
||
|
// PDF Export Options
|
||
|
// Put each fragment on a separate page
|
||
|
pdfSeparateFragments: true,
|
||
|
// For slides that do not fit on a page, max number of pages
|
||
|
pdfMaxPagesPerSlide: 1,
|
||
|
|
||
|
// Optional libraries used to extend on reveal.js
|
||
|
dependencies: [
|
||
|
{ src: 'node_modules/reveal.js/plugin/zoom/zoom.js', async: true, callback: function () { Reveal.registerPlugin(RevealZoom) } },
|
||
|
{ src: 'node_modules/reveal.js/plugin/notes/notes.js', async: true, callback: function () { Reveal.registerPlugin(RevealNotes) } }
|
||
|
],
|
||
|
});</script><script>var dom = {};
|
||
|
dom.slides = document.querySelector('.reveal .slides');
|
||
|
|
||
|
function getRemainingHeight(element, slideElement, height) {
|
||
|
height = height || 0;
|
||
|
if (element) {
|
||
|
var newHeight, oldHeight = element.style.height;
|
||
|
// Change the .stretch element height to 0 in order find the height of all
|
||
|
// the other elements
|
||
|
element.style.height = '0px';
|
||
|
// In Overview mode, the parent (.slide) height is set of 700px.
|
||
|
// Restore it temporarily to its natural height.
|
||
|
slideElement.style.height = 'auto';
|
||
|
newHeight = height - slideElement.offsetHeight;
|
||
|
// Restore the old height, just in case
|
||
|
element.style.height = oldHeight + 'px';
|
||
|
// Clear the parent (.slide) height. .removeProperty works in IE9+
|
||
|
slideElement.style.removeProperty('height');
|
||
|
return newHeight;
|
||
|
}
|
||
|
return height;
|
||
|
}
|
||
|
|
||
|
function layoutSlideContents(width, height) {
|
||
|
// Handle sizing of elements with the 'stretch' class
|
||
|
toArray(dom.slides.querySelectorAll('section .stretch')).forEach(function (element) {
|
||
|
// Determine how much vertical space we can use
|
||
|
var limit = 5; // hard limit
|
||
|
var parent = element.parentNode;
|
||
|
while (parent.nodeName !== 'SECTION' && limit > 0) {
|
||
|
parent = parent.parentNode;
|
||
|
limit--;
|
||
|
}
|
||
|
if (limit === 0) {
|
||
|
// unable to find parent, aborting!
|
||
|
return;
|
||
|
}
|
||
|
var remainingHeight = getRemainingHeight(element, parent, height);
|
||
|
// Consider the aspect ratio of media elements
|
||
|
if (/(img|video)/gi.test(element.nodeName)) {
|
||
|
var nw = element.naturalWidth || element.videoWidth, nh = element.naturalHeight || element.videoHeight;
|
||
|
var es = Math.min(width / nw, remainingHeight / nh);
|
||
|
element.style.width = (nw * es) + 'px';
|
||
|
element.style.height = (nh * es) + 'px';
|
||
|
} else {
|
||
|
element.style.width = width + 'px';
|
||
|
element.style.height = remainingHeight + 'px';
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function toArray(o) {
|
||
|
return Array.prototype.slice.call(o);
|
||
|
}
|
||
|
|
||
|
Reveal.addEventListener('slidechanged', function () {
|
||
|
layoutSlideContents(960, 700)
|
||
|
});
|
||
|
Reveal.addEventListener('ready', function () {
|
||
|
layoutSlideContents(960, 700)
|
||
|
});
|
||
|
Reveal.addEventListener('resize', function () {
|
||
|
layoutSlideContents(960, 700)
|
||
|
});</script><link rel="stylesheet" href="node_modules/reveal.js/plugin/highlight/monokai.css"/>
|
||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.7.3/highlight.min.js"></script>
|
||
|
|
||
|
<script>
|
||
|
|
||
|
/* highlightjs-line-numbers.js 2.6.0 | (C) 2018 Yauheni Pakala | MIT License | github.com/wcoder/highlightjs-line-numbers.js */
|
||
|
/* Edited by Hakim for reveal.js; removed async timeout */
|
||
|
!function(n,e){"use strict";function t(){var n=e.createElement("style");n.type="text/css",n.innerHTML=g(".{0}{border-collapse:collapse}.{0} td{padding:0}.{1}:before{content:attr({2})}",[v,L,b]),e.getElementsByTagName("head")[0].appendChild(n)}function r(t){"interactive"===e.readyState||"complete"===e.readyState?i(t):n.addEventListener("DOMContentLoaded",function(){i(t)})}function i(t){try{var r=e.querySelectorAll("code.hljs,code.nohighlight");for(var i in r)r.hasOwnProperty(i)&&l(r[i],t)}catch(o){n.console.error("LineNumbers error: ",o)}}function l(n,e){"object"==typeof n&&f(function(){n.innerHTML=s(n,e)})}function o(n,e){if("string"==typeof n){var t=document.createElement("code");return t.innerHTML=n,s(t,e)}}function s(n,e){e=e||{singleLine:!1};var t=e.singleLine?0:1;return c(n),a(n.innerHTML,t)}function a(n,e){var t=u(n);if(""===t[t.length-1].trim()&&t.pop(),t.length>e){for(var r="",i=0,l=t.length;i<l;i++)r+=g('<tr><td class="{0}"><div class="{1} {2}" {3}="{5}"></div></td><td class="{4}"><div class="{1}">{6}</div></td></tr>',[j,m,L,b,p,i+1,t[i].length>0?t[i]:" "]);return g('<table class="{0}">{1}</table>',[v,r])}return n}function c(n){var e=n.childNodes;for(var t in e)if(e.hasOwnProperty(t)){var r=e[t];h(r.textContent)>0&&(r.childNodes.length>0?c(r):d(r.parentNode))}}function d(n){var e=n.className;if(/hljs-/.test(e)){for(var t=u(n.innerHTML),r=0,i="";r<t.length;r++){var l=t[r].length>0?t[r]:" ";i+=g('<span class="{0}">{1}</span>\n',[e,l])}n.innerHTML=i.trim()}}function u(n){return 0===n.length?[]:n.split(y)}function h(n){return(n.trim().match(y)||[]).length}function f(e){e()}function g(n,e){return n.replace(/{(\d+)}/g,function(n,t){return e[t]?e[t]:n})}var v="hljs-ln",m="hljs-ln-line",p="hljs-ln-code",j="hljs-ln-numbers",L="hljs-ln-n",b="data-line-number",y=/\r\n|\r|\n/g;n.hljs?(n.hljs.initLineNumbersOnLoad=r,n.hljs.lineNumbersBlock=l,n.hljs.lineNumbersValue=o,t()):n.console.error("highlight.js not detected!")}(window,document);
|
||
|
|
||
|
/**
|
||
|
* This reveal.js plugin is wrapper around the highlight.js
|
||
|
* syntax highlighting library.
|
||
|
*/
|
||
|
(function( root, factory ) {
|
||
|
if (typeof define === 'function' && define.amd) {
|
||
|
root.RevealHighlight = factory();
|
||
|
} else if( typeof exports === 'object' ) {
|
||
|
module.exports = factory();
|
||
|
} else {
|
||
|
// Browser globals (root is window)
|
||
|
root.RevealHighlight = factory();
|
||
|
}
|
||
|
}( this, function() {
|
||
|
|
||
|
// 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);
|
||
|
}
|
||
|
|
||
|
var RevealHighlight = {
|
||
|
|
||
|
HIGHLIGHT_STEP_DELIMITER: '|',
|
||
|
HIGHLIGHT_LINE_DELIMITER: ',',
|
||
|
HIGHLIGHT_LINE_RANGE_DELIMITER: '-',
|
||
|
|
||
|
init: function( reveal ) {
|
||
|
|
||
|
// Read the plugin config options and provide fallbacks
|
||
|
var config = Reveal.getConfig().highlight || {};
|
||
|
config.highlightOnLoad = typeof config.highlightOnLoad === 'boolean' ? config.highlightOnLoad : true;
|
||
|
config.escapeHTML = typeof config.escapeHTML === 'boolean' ? config.escapeHTML : true;
|
||
|
|
||
|
[].slice.call( reveal.getRevealElement().querySelectorAll( 'pre code' ) ).forEach( function( block ) {
|
||
|
|
||
|
block.parentNode.className = '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 );
|
||
|
|
||
|
if( config.highlightOnLoad ) {
|
||
|
RevealHighlight.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 ) {
|
||
|
RevealHighlight.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 at least one highlight step, generate
|
||
|
// fragments
|
||
|
var highlightSteps = RevealHighlight.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', RevealHighlight.serializeHighlightSteps( [ highlight ] ) );
|
||
|
fragmentBlock.classList.add( 'fragment' );
|
||
|
block.parentNode.appendChild( fragmentBlock );
|
||
|
RevealHighlight.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', RevealHighlight.scrollHighlightedLineIntoView.bind( Plugin, fragmentBlock, scrollState ) );
|
||
|
fragmentBlock.addEventListener( 'hidden', RevealHighlight.scrollHighlightedLineIntoView.bind( Plugin, fragmentBlock.previousSibling, scrollState ) );
|
||
|
|
||
|
} );
|
||
|
|
||
|
block.removeAttribute( 'data-fragment-index' )
|
||
|
block.setAttribute( 'data-line-numbers', RevealHighlight.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() {
|
||
|
RevealHighlight.scrollHighlightedLineIntoView( block, scrollState, true );
|
||
|
slide.removeEventListener( 'visible', scrollFirstHighlightIntoView );
|
||
|
}
|
||
|
slide.addEventListener( 'visible', scrollFirstHighlightIntoView );
|
||
|
}
|
||
|
|
||
|
RevealHighlight.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 = RevealHighlight.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 ) * RevealHighlight.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 = RevealHighlight.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
|
||
|
* RevealHighlight.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( RevealHighlight.HIGHLIGHT_STEP_DELIMITER );
|
||
|
|
||
|
return highlightSteps.map( function( highlights ) {
|
||
|
|
||
|
return highlights.split( RevealHighlight.HIGHLIGHT_LINE_DELIMITER ).map( function( highlight ) {
|
||
|
|
||
|
// Parse valid line numbers
|
||
|
if( /^[\d-]+$/.test( highlight ) ) {
|
||
|
|
||
|
highlight = highlight.split( RevealHighlight.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 + RevealHighlight.HIGHLIGHT_LINE_RANGE_DELIMITER + highlight.end;
|
||
|
}
|
||
|
// Single line
|
||
|
else if( typeof highlight.start === 'number' ) {
|
||
|
return highlight.start;
|
||
|
}
|
||
|
// All lines
|
||
|
else {
|
||
|
return '';
|
||
|
}
|
||
|
|
||
|
} ).join( RevealHighlight.HIGHLIGHT_LINE_DELIMITER );
|
||
|
|
||
|
} ).join( RevealHighlight.HIGHLIGHT_STEP_DELIMITER );
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
Reveal.registerPlugin( 'highlight', RevealHighlight );
|
||
|
|
||
|
return RevealHighlight;
|
||
|
|
||
|
}));
|
||
|
|
||
|
hljs.configure({
|
||
|
ignoreUnescapedHTML: true,
|
||
|
});
|
||
|
hljs.highlightAll();
|
||
|
</script></body></html>
|