1/* 2 * Tile View for remark.js Slides 3 * 4 * Garrick Aden-Buie 5 * 6 * Inspired and converted to Vanilla JS from 7 * https://github.com/StephenHesperus/remark-hook/ 8 * 9 * Include after remarkjs slides are initialized. 10 * 11 */ 12 13/* global slideshow */ 14(function () { 15 const ready = function (fn) { 16 /* MIT License Copyright (c) 2016 Nuclei */ 17 /* https://github.com/nuclei/readyjs */ 18 const completed = () => { 19 document.removeEventListener('DOMContentLoaded', completed) 20 window.removeEventListener('load', completed) 21 fn() 22 } 23 if (document.readyState !== 'loading') { 24 setTimeout(fn) 25 } else { 26 document.addEventListener('DOMContentLoaded', completed) 27 window.addEventListener('load', completed) 28 } 29 } 30 31 ready(function () { 32 const launchKey = 79 // keycode for O, used to enable tile view 33 34 // Slides container 35 const remarkSlideShow = document.querySelector('div.remark-slides-area') 36 37 let tileView = document.querySelector('div.remark__tile-view') 38 if (!tileView) { 39 tileView = document.createElement('div') 40 tileView.className = 'remark__tile-view' 41 } 42 43 const toggleElement = el => { 44 el.style.display = el.style.display === 'none' ? '' : 'none' 45 } 46 47 function slideshowResize () { 48 window.dispatchEvent(new Event('resize')) 49 } 50 51 const toggleTileView = function () { 52 toggleElement(tileView) 53 toggleElement(remarkSlideShow) 54 55 if (tileView.style.display === 'none') { 56 // tileView is now hidden, go to current slide 57 slideshow.gotoSlide(tileVars.currentSlideIdx + 1) 58 59 slideshow.resume() 60 slideshowResize() 61 } else { 62 // store current slide index prior to launching tile-view 63 tileVars.currentSlideIdx = slideshow.getCurrentSlideIndex() 64 65 // set class on seen and current slide and scroll into view 66 const tiles = tileView.querySelectorAll('.remark__tile-view__tile'); 67 [...tiles].forEach((tile, idx) => { 68 tile.classList.toggle( 69 'remark__tile-view__tile--seen', 70 idx < tileVars.currentSlideIdx 71 ) 72 tile.classList.toggle( 73 'remark__tile-view__tile--current', 74 idx === tileVars.currentSlideIdx 75 ) 76 }) 77 tiles[tileVars.currentSlideIdx].scrollIntoView({ 78 behavior: 'smooth', 79 block: 'center' 80 }) 81 82 slideshow.pause() 83 } 84 } 85 86 const createTileView = ({ minSize = 250, title = document.title } = {}) => { 87 // Tile view header 88 const h1 = document.createElement('h1') 89 h1.className = 'remark__tile-view__header' 90 h1.innerHTML = title 91 92 tileView.appendChild(h1) 93 const tiles = document.createElement('div') 94 tiles.className = 'remark__tile-view__tiles' 95 tileView.appendChild(tiles) 96 97 // Clone slideshow 98 const slidesArea = remarkSlideShow.cloneNode(true) 99 100 // Calculate slide scale and tile container size 101 const slideScaler = slidesArea.querySelector('.remark-slide-scaler') 102 const slideWidth = parseFloat(slideScaler.style.width.replace('px', '')) 103 const slideHeight = parseFloat( 104 slideScaler.style.height.replace('px', '') 105 ) 106 const scale = minSize / Math.min(slideWidth, slideHeight) 107 let tileWidth = Math.round(slideWidth * scale) 108 let tileHeight = Math.round(slideHeight * scale) 109 110 // convert tileWidth/Height to em relative to base 18px (set in CSS) 111 tileWidth = tileWidth / 18 112 tileHeight = tileHeight / 18 113 114 tiles.style.gridTemplateColumns = `repeat(auto-fill, minmax(${tileWidth}em, 1fr))` 115 116 const slides = slidesArea.querySelectorAll('.remark-slide-container') 117 118 slides.forEach((slide, slideIndex) => { 119 let tile = document.createElement('template') 120 tile.innerHTML = `<div class="remark__tile-view__tile"> 121 <div class="remark__tile-view__slide-container"> 122 </div></div>` 123 tile = tile.content.firstChild 124 125 const tileContainer = tile.querySelector( 126 '.remark__tile-view__slide-container' 127 ) 128 tileContainer.style.width = `${tileWidth}em` 129 tileContainer.style.height = `${tileHeight}em` 130 131 const thisSlideScaler = slide.querySelector('.remark-slide-scaler') 132 thisSlideScaler.style.top = '0px' 133 thisSlideScaler.style.left = '0px' 134 thisSlideScaler.style.transform = `scale(${scale})` 135 thisSlideScaler.parentElement.classList.add('remark-visible') 136 137 slide.addEventListener('click', () => { 138 tileVars.currentSlideIdx = slideIndex 139 toggleTileView() 140 }) 141 142 tileContainer.appendChild(slide) 143 tiles.appendChild(tile) 144 }) 145 146 document.body.appendChild(tileView) 147 } 148 149 const tileVars = {} 150 151 document.addEventListener('keydown', ev => { 152 if (ev.keyCode === launchKey) { 153 toggleTileView() 154 } 155 }) 156 157 const addTileViewHelpText = () => { 158 const helpTable = document.querySelector( 159 '.remark-help-content table.light-keys' 160 ) 161 if (!helpTable) { 162 console.error( 163 'Could not find remark help table, has remark been initialized?' 164 ) 165 return 166 } 167 const newRow = document.createElement('tr') 168 newRow.innerHTML += '<td><span class="key">o</span></td>' 169 newRow.innerHTML += '<td>Tile View: Overview of Slides</td>' 170 helpTable.append(newRow) 171 } 172 173 createTileView({ minSize: 200 }) 174 toggleElement(tileView) 175 addTileViewHelpText() 176 }) 177})()
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.