remark-slides

personal distribution of remarkjs
git clone https://tongong.net/git/remark-slides.git
Log | Files | Refs | README

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(/^[ ]*-[ ]+&rarr;/, (a) => a.replace(
    166                             " &rarr;",
    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("->", "&rarr;");
    197 var slideshow = remark.create(
    198     { preprocessor: (s) => leftmenu(replaceArrow(s)), source },
    199     (() => {imagelistcallback(); arrowcallback(); })
    200 );