1// INFO: the non-esm plugins need reveal JS in scope so it can't be imported 2// here 3// import Reveal from "revealjs"; 4import RevealHighlight from "reveal-highlight"; 5import RevealMath from "reveal-math"; 6import RevealNotes from "reveal-notes"; 7import RevealSearch from "reveal-search"; 8import RevealZoom from "reveal-zoom"; 9import RevealECharts from "reveal-echarts"; 10import RevealMermaid from "reveal-mermaid"; 11// import RevealApexchart from "reveal-apexchart"; 12// import RevealChartjs from "reveal-chartjs"; 13// INFO: non-esm therefore they can't be properly imported: 14// import * as PdfExport from "reveal-pdfexport"; 15// import * as RevealChalkboard from "reveal-chalkboard"; 16// import * as RevealCustomControls from "reveal-customcontrols"; 17import SlidesDown from "slidesdown"; 18 19// computeURL builds a URL to a raw markdown file from a short human-typable 20// string, e.g. it turns github.com/slidesdown/slidesdown into 21// https://raw.githubusercontent.com/slidsedown/slidesdown/main/SLIDES.md 22function computeURL(defaults, url) { 23 if (!(defaults.branch && defaults.resource)) { 24 console.error("Default branch and/or resource unset"); 25 return; 26 } 27 const decodedURL = decodeURI(url); 28 let match = ""; 29 const githubRegExp = new RegExp( 30 /^(?:https:\/\/)?github\.com\/(?<owner>[a-zA-Z0-9_-]+)\/(?<repo>[a-zA-Z0-9_-]+)(?:(?:\/((?<blob>blob)|(?<tree>tree)))?(?:\/(?<dir_or_branch>[^/]+?))?(?:\/(?<resource>.*)))?$/, 31 ); 32 // resource is not considered for gists, because there's no safe way to 33 // determine the resource's name from the provided anchor tag 34 const gistRegExp = new RegExp( 35 /^(?:https:\/\/)?gist\.github\.com\/(?<owner>[a-zA-Z0-9_-]+)\/(?<repo>[a-zA-Z0-9_-]+)\/?/, 36 ); 37 // gistRegExp.exec("gist.github.com/jceb/4bfcfdcddd2020e5b7e521b9e1044f3b") 38 // gistRegExp.exec("https://gist.github.com/jceb/4bfcfdcddd2020e5b7e521b9e1044f3b") 39 // gistRegExp.exec("https://gist.github.com/jceb/4bfcfdcddd2020e5b7e521b9e1044f3b#file-230911_dif_wg_id_presentation-md") 40 // gistRegExp.exec("https://gist.github.com/jceb/4bfcfdcddd2020e5b7e521b9e1044f3b/#file-230911_dif_wg_id_presentation-md") 41 // gistRegExp.exec("https://gist.githubusercontent.com/jceb/4bfcfdcddd2020e5b7e521b9e1044f3b/raw/dd6e852ccb04c1690a7e96eb77008240e0fbf69f/SLIDES.md") 42 const srhtRegExp = new RegExp( 43 /^(?:https:\/\/)?(git\.)?sr\.ht\/(?<owner>~[a-zA-Z0-9_-]+)\/(?<repo>[a-zA-Z0-9_-]+)(?:(?:\/((?<blob>blob)|(?<tree>tree)))?(?:\/(?<dir_or_branch>[^/]+?))?(?:\/(item\/)?(?<resource>.*)))?$/, 44 ); 45 // srhtRegExp.exec("https://git.sr.ht/~jceb/test") 46 // srhtRegExp.exec("https://git.sr.ht/~jceb/test/SLIDES.md") 47 // srhtRegExp.exec("https://git.sr.ht/~jceb/test/tree") 48 // srhtRegExp.exec("https://git.sr.ht/~jceb/test/tree/SLIDES.md") 49 // srhtRegExp.exec("https://git.sr.ht/~jceb/test/tree/main/item/SLIDES.md") 50 // srhtRegExp.exec("sr.ht/~jceb/test/tree/main/item/SLIDES.md") 51 // srhtRegExp.exec("sr.ht/~jceb/test/SLIDES.md") 52 if ((match = githubRegExp.exec(decodedURL)) !== null) { 53 let resource = `${defaults.branch}/${defaults.resource}`; 54 // if tree is present, then the default resouce name must be appended 55 if ((match.groups.blob | match.groups.tree) && match.groups.dir_or_branch) { 56 // if tree or blob are present, then dir_or_branch is the branch - perfect, I can build the URL with confidence 57 resource = `${match.groups.dir_or_branch}/${ 58 match.groups.resource ? match.groups.resource : defaults.resource 59 }`; 60 } else if (match.groups.blob && match.groups.dir_or_branch) { 61 // if tree or blob are not present, then dir_or_branch must be a dir but the branch name can't be determined 62 resource = `${match.groups.dir_or_branch}/${ 63 match.groups.resource ? match.groups.resource : defaults.resource 64 }`; 65 } else { 66 if (match.groups.dir_or_branch) { 67 resource = `${defaults.branch}/${match.groups.dir_or_branch}/${ 68 match.groups.resource ? match.groups.resource : defaults.resource 69 }`; 70 } else { 71 resource = `${defaults.branch}/${ 72 match.groups.resource ? match.groups.resource : defaults.resource 73 }`; 74 } 75 } 76 return `https://raw.githubusercontent.com/${match.groups.owner}/${match.groups.repo}/${resource}`; 77 } else if ((match = gistRegExp.exec(decodedURL)) !== null) { 78 return `https://gist.githubusercontent.com/${match.groups.owner}/${match.groups.repo}/raw/SLIDES.md`; 79 } else if ((match = srhtRegExp.exec(decodedURL)) !== null) { 80 // INFO: support for sr.ht is currently broken due to missing CORS headers on
80sr.ht's side 81 let resource = `${defaults.branch}/${defaults.resource}`; 82 // if tree is present, then the default resouce name must be appended 83 if ((match.groups.blob | match.groups.tree) && match.groups.dir_or_branch) { 84 // if tree or blob are present, then dir_or_branch is the branch - perfect, I can build the URL with confidence 85 resource = `${match.groups.dir_or_branch}/${ 86 match.groups.resource ? match.groups.resource : defaults.resource 87 }`; 88 } else if (match.groups.blob && match.groups.dir_or_branch) { 89 // if tree or blob are not present, then dir_or_branch must be a dir but the branch name can't be determined 90 resource = `${match.groups.dir_or_branch}/${ 91 match.groups.resource ? match.groups.resource : defaults.resource 92 }`; 93 } else { 94 if (match.groups.dir_or_branch) { 95 resource = `${defaults.branch}/${match.groups.dir_or_branch}/${ 96 match.groups.resource ? match.groups.resource : defaults.resource 97 }`; 98 } else { 99 resource = `${defaults.branch}/${ 100 match.groups.resource ? match.groups.resource : defaults.resource 101 }`; 102 } 103 } 104 return `https://git.sr.ht/${match.groups.owner}/${match.groups.repo}/blob/${resource}`; 105 } 106 return decodedURL; 107} 108 109async function main(defaults) { 110 if (!(defaults.branch && defaults.resource && defaults.markdownElementId)) { 111 console.error( 112 "Default branch, resource and/or markdownElementId are not set", 113 ); 114 return; 115 } 116 const mdSection = document.getElementById(defaults.markdownElementId); 117 if (!mdSection) { 118 console.error( 119 `Couldn't find markdown element with id: ${defaults.markdownElementId}`, 120 ); 121 return; 122 } 123 const customSlidesBase64Gzip = new URLSearchParams( 124 new URL(document.URL).search, 125 ).get("slides64"); 126 if (customSlidesBase64Gzip) { 127 async function decompressBlob(blob) { 128 const decompressedStream = blob.pipeThrough( 129 new DecompressionStream("gzip"), 130 ); 131 return await new Response(decompressedStream).blob(); 132 } 133 const slidesGz = Uint8Array.from( 134 atob( 135 decodeURI( 136 customSlidesBase64Gzip.replaceAll("-", "+").replaceAll("_", "/"), 137 ), 138 ), 139 (c) => c.charCodeAt(0), 140 ); 141 // function buf2hex(buffer) { // buffer is an ArrayBuffer 142 // return [...new Uint8Array(buffer)] 143 // .map(x => x.toString(16).padStart(2, '0')) 144 // .join(''); 145 // } 146 const blob = await decompressBlob( 147 new Blob([slidesGz], { type: "application/octet-stream" }).stream(), 148 ); 149 const text = await blob.text(); 150 mdSection.setAttribute("data-markdown", "<<load-plain-markdown>>"); 151 mdSection.setAttribute("data-markdown-plain", text); 152 } else { 153 const customSlidesURL = new URLSearchParams(new URL(document.URL).search) 154 .get("slides"); 155 let slidesURL = customSlidesURL 156 ? customSlidesURL 157 : `github.com/slidesdown/slidesdown/blob/${defaults.branch}/${defaults.resource}`; 158 159 slidesURL = computeURL(defaults, slidesURL); 160 161 if (!slidesURL) { 162 console.error("Couldn't compute slides URL"); 163 return; 164 } 165 mdSection.setAttribute("data-markdown", slidesURL); 166 } 167 const multiplexBase64 = new URLSearchParams(new URL(document.URL).search).get( 168 "multiplex64", 169 ); 170 let multiplex = {}; 171 let dependencies = []; 172 if (multiplexBase64) { 173 multiplex = JSON.parse(atob( 174 decodeURI( 175 multiplexBase64.replaceAll("-", "+").replaceAll("_", "/"), 176 ), 177 )); 178 multiplex = { 179 secret: multiplex?.secret || null, 180 id: multiplex?.id || null, 181 url: "/", 182 // url: "https://reveal-multiplex.glitch.me" 183 }; 184 const minium_secret_length = 20; 185 if ( 186 typeof multiplex.id != "string" && 187 multiplex.id.length < minium_secret_length 188 ) { 189 multiplex = {}; 190 } 191 if (multiplex?.id) { 192 dependencies.push({ src: "/socket.io/socket.io.js", async: true }); 193 // dependencies.push({ src: 'https://reveal-multiplex.glitch.me/socket.io/socket.io.js', async: true }) 194 } 195 if (multiplex?.secret) { 196 dependencies.push({ src: "/vendor/multiplex/master.js", async: true }); 197 // dependencies.push({ src: 'https://reveal-multiplex.glitch.me/master.js', async: true }) 198 } else { 199 dependencies.push({ src: "/vendor/multiplex/client.js", async: true }); 200 // dependencies.push({ src: 'https://reveal-multiplex.glitch.me/client.js', async: true }) 201 } 202 } 203 // initialize presentation 204 Reveal.slidesdownLoader = () => { 205 window.location.href = "/loader.html"; 206 }; 207 Reveal.initialize({ 208 multiplex, 209 dependencies, 210 hash: true, 211 // mathjax2: { 212 // mathjax: "/vendor/mathjax/MathJax.js" 213 // }, 214 mathjax3: { 215 mathjax: "/vendor/mathjax/tex-mml-chtml.js", 216 }, 217 plugins: [ 218 SlidesDown, 219 RevealHighlight, 220 RevealMath.MathJax3(), 221 RevealNotes, 222 RevealSearch, 223 RevealZoom, 224 // Source: https://github.com/McShelby/reveal-pdfexport 225 PdfExport, 226 // Source: https://github.com/rajgoel/reveal.js-plugins/tree/master/chalkboard 227 RevealChalkboard, 228 // Source: https://github.com/rajgoel/reveal.js-plugins/tree/master/cu
228stomcontrols 229 RevealCustomControls, 230 // Source: https://github.com/rajgoel/reveal.js-plugins/tree/master/anything 231 // RevealAnything, 232 // RevealChartjs, 233 // RevealApexchart, 234 RevealECharts, 235 RevealMermaid, 236 ], 237 customcontrols: { 238 collapseIcon: '<div class="i-fa6-solid-chevron-down"></div>', 239 expandIcon: '<div class="i-fa6-solid-chevron-up"></div>', 240 controls: [ 241 { 242 icon: '<div class="i-fa6-solid-folder-open"></div>', 243 title: "Open another presentation", 244 action: "Reveal.slidesdownLoader();", 245 }, 246 { 247 icon: '<div class="i-fa6-solid-table-cells"></div>', 248 id: "toggle-overview", 249 title: "Toggle overview (O)", 250 action: "Reveal.toggleOverview();", 251 }, 252 { 253 icon: '<div class="i-fa6-solid-square-pen"></div>', 254 title: "Toggle chalkboard (B)", 255 action: "RevealChalkboard.toggleChalkboard();", 256 }, 257 { 258 icon: '<div class="i-fa6-solid-pen"></div>', 259 title: "Toggle notes canvas (C)", 260 action: "RevealChalkboard.toggleNotesCanvas();", 261 }, 262 { 263 icon: '<div class="i-fa6-solid-print"></div>', 264 title: "Toggle print view (E)", 265 action: "PdfExport.togglePdfExport();", 266 }, 267 ], 268 }, 269 }).then(() => { 270 Reveal.sync(); // workaround to make the first slide center properly after adding slides 271 console.debug("initialization finished"); 272 }); 273} 274 275main({ 276 branch: "main", 277 resource: "SLIDES.md", 278 markdownElementId: "markdown", 279});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.