scripts.js (6955B)
1 // imagelist.js 2 // This is a helper-script for the .ilist class 3 // A line of images get scaled, so that they all have the same height and fill 4 // the entire width 5 // ~ made for modified version by @tongong 6 7 const gap = 10; // pixels between each image; should match with the css rules 8 9 // this function gets passed to the slideshow-constructor and is executed after 10 // the creation of the slideshow 11 imagelistcallback = function () { 12 document.querySelectorAll(".ilist p").forEach((list) => { 13 let images = Array.from(list.querySelectorAll("img")); 14 // Waits for all images to finish loading/detects if all images are 15 // already loaded 16 let alreadyLoaded = false; // prevent the function from executing twice 17 if (images.every((a) => imgLoaded(a))) { 18 console.log("Images are already cached."); 19 allLoaded(list); 20 } else { 21 images.forEach((img) => { 22 img.onload = function () { 23 if (images.every((a) => imgLoaded(a)) && !alreadyLoaded) { 24 alreadyLoaded = true; 25 allLoaded(list); 26 } 27 }; 28 }); 29 } 30 }); 31 }; 32 33 // Checks if an image is already loaded 34 // thanks to (aka shamelessly copied from) https://stackoverflow.com/a/34726863 35 function imgLoaded(imgElement) { 36 return imgElement.complete && imgElement.naturalHeight !== 0; 37 }; 38 39 // The actual magic of image resizing happens here 40 function allLoaded(list) { 41 // kind of redundant, but who cares? 42 let images = Array.from(list.querySelectorAll("img")); 43 // get ratio width/height of all images combined without the gaps 44 let ratio = images.map((a) => a.naturalWidth / a.naturalHeight).reduce( 45 (b, a) => a + b 46 ); 47 // build css calc() function for every image, because the final width 48 // cannot be measured since the slides are display: none; at the start of 49 // the presentation 50 // width is calculated as fraction of the width of all images and that 51 // fraction is applied to the width of the wrapper element minus the gaps 52 images.forEach((image) => { 53 image.style.width = "calc((100% - " + (images.length - 1) * gap + 54 "px) * " + image.naturalWidth / image.naturalHeight / ratio + ")"; 55 }); 56 57 // Apply margin, if set in the alt of any of the images 58 let width = 0; 59 images.forEach((i) => { 60 if (i.alt.includes(":")) { 61 width = +i.alt.split(":")[1] || 0; 62 } 63 }); 64 if (width != 0) { 65 list.parentNode.style.marginLeft = (100-width)/2 + "%"; 66 list.parentNode.style.marginRight = (100-width)/2 + "%"; 67 } 68 }; 69 70 71 72 // leftmenu.js 73 // This script generates a menu with headings at the left side 74 // headings are specified for each slide by the "left:..." option 75 // The headings list gets reset for every heading starting with "_" 76 // ~ made for modified version by @tongong 77 78 // This polyfill is needed for the pdf-export. For whatever reason the 79 // JS-version shipped with puppeteer does not support the replaceAll() function 80 // and I am to lazy to change my code lol 81 /** 82 * String.prototype.replaceAll() polyfill 83 * https://gomakethings.com/how-to-replace-a-section-of-a-string-with-another-one-with-vanilla-js/ 84 * @author Chris Ferdinandi 85 * @license MIT 86 */ 87 if (!String.prototype.replaceAll) { 88 String.prototype.replaceAll = function (str, newStr) { 89 // If a regex pattern 90 if ( Object.prototype.toString.call(str) 91 .toLowerCase() === "[object regexp]" 92 ) { 93 return this.replace(str, newStr); 94 } 95 // If a string 96 return this.replace(new RegExp(str, "g"), newStr); 97 }; 98 } 99 100 function leftmenu(slides) { 101 let curleftmenu = ""; 102 let curtemplate = ""; 103 slides.forEach((slide) => { 104 // Check if template slide 105 if (!(slide.properties || { layout: false }).layout) { 106 // get template 107 let template = curtemplate; 108 if (slide.properties.template) { 109 template = slide.properties.template; 110 } 111 112 // For left-menu template build the menu 113 if (template == "leftmenu") { 114 // variable with the new heading to the left 115 let newleft = slide.properties.left || "..."; 116 if (newleft[0] != "*") { 117 if (newleft[0] == "_") { 118 curleftmenu = "\n<h2>" 119 + newleft.substring(1) + "</h2>"; 120 } else { 121 curleftmenu += "\n<h2>" + newleft + "</h2>"; 122 } 123 } 124 125 // Generate new content object 126 let content = [ 127 "\n", 128 { 129 block: true, 130 class: "left-column", 131 content: [curleftmenu + "\n"], 132 }, 133 "\n", 134 { 135 block: true, 136 class: "right-column", 137 content: [...slide.content, "\n"], 138 }, 139 "\n", 140 ]; 141 142 // Apply new content object 143 slide.content = content; 144 } 145 } else { 146 // If template slide reload template 147 curtemplate = slide.properties.name || curtemplate; 148 } 149 }); 150 return slides; 151 } 152 153 154 155 // arrow.js 156 // if a bullet point starts with an arrow, the bullet style gets changed 157 // convertes "- -> blabla" to "-> blabla" 158 // ~ made for modified version by @tongong 159 // applies the css rule list-style-type: symbols(cyclic "→"); 160 function replaceArrow(slides) { 161 slides.forEach((slide) => { 162 slide.content = slide.content.map((p) => { 163 if (typeof p == "string") { 164 p = p.split("\n").map((l) => ( 165 l.replace(/^[ ]*-[ ]+→/, (a) => a.replace( 166 " →", 167 " <span class='tag-arrow-bullet'></span>" 168 ) 169 ) 170 )).join("\n"); 171 } 172 return p; 173 }); 174 }); 175 return slides; 176 } 177 178 function arrowcallback() { 179 document.querySelectorAll(".tag-arrow-bullet").forEach((li) => { 180 let parent; 181 if (li.parentNode.tagName == "LI") { 182 parent = li.parentNode; 183 } 184 else if (li.parentNode.parentNode.tagName == "LI") { 185 parent = li.parentNode.parentNode; 186 } 187 else return; 188 parent.style.listStyleType = 'symbols(cyclic "→")'; 189 }); 190 } 191 192 193 194 // Init remark with additional replacing of arrows 195 let source = document.getElementById("source").value 196 .replaceAll("->", "→"); 197 var slideshow = remark.create( 198 { preprocessor: (s) => leftmenu(replaceArrow(s)), source }, 199 (() => {imagelistcallback(); arrowcallback(); }) 200 );