/*! * bespoke-overview v1.0.5 * * Copyright 2018, Dan Allen * This content is released under the MIT license */ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g=(g.bespoke||(g.bespoke = {}));g=(g.plugins||(g.plugins = {}));g.overview = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= 0 && targetIndex < deck.slides.length) deck.slide(targetIndex, { preview: true }); return false; }, onActivate = function(e) { if (e.scrollIntoView !== false) scrollSlideIntoView(e.slide, e.index, getZoomFactor(e.slide)); }, updateLocation = function(state) { var s = location.search.replace(RE_MODE, '').replace(/^[^?]/, '?$&'); if (state) { history.replaceState(null, null, location.pathname + (s.length > 0 ? s + '&' : '?') + 'overview' + location.hash); } else { history.replaceState(null, null, location.pathname + s + location.hash); } }, scrollSlideIntoView = function(slide, index, zoomFactor) { deck.parent.scrollTop = index < columns ? 0 : deck.parent.scrollTop + slide.getBoundingClientRect().top * (zoomFactor || 1); }, removeAfterTransition = function(direction, parentClasses, slide, slideAlt) { slide.removeEventListener(TRANSITIONEND, afterTransition, false); if (slideAlt && slideAlt !== slide) slideAlt.removeEventListener(TRANSITIONEND, afterTransition, false); afterTransition = undefined; parentClasses.remove('bespoke-overview-' + direction); }, getOrCreateTitle = function(parent) { var first = parent.firstElementChild; if (first.classList.contains('bespoke-title')) { first.style.width = ''; return first; } var header = document.createElement('header'); header.className = 'bespoke-title'; header.style[getStyleProperty(header, 'transformOrigin')] = '0 0'; var h1 = document.createElement('h1'); h1.appendChild(document.createTextNode(parent.getAttribute('data-title') || document.title)); header.appendChild(h1); flushStyle(parent.insertBefore(header, first)); return header; }, openOverview = function() { var slides = deck.slides, parent = deck.parent, parentClasses = parent.classList, lastSlideIndex = slides.length - 1, activeSlideIndex = deck.slide(), sampleSlide = activeSlideIndex > 0 ? slides[0] : slides[lastSlideIndex], transformProp = getStyleProperty(sampleSlide, 'transform'), scaleParent = parent.querySelector('.bespoke-scale-parent'), baseScale = 1, zoomFactor, title, numTransitions = 0, resize = overviewActive, isWebKit = 'webkitAppearance' in parent.style; if (scaleParent) { baseScale = getTransformScaleFactor(scaleParent, transformProp); } else if ((zoomFactor = getZoomFactor(sampleSlide))) { baseScale = zoomFactor; } if (afterTransition) removeAfterTransition('from', parentClasses, slides[0], slides[lastSlideIndex]); if (!!opts.title) title = getOrCreateTitle(parent); if (!resize) { deck.slide(activeSlideIndex, { preview: true }); parentClasses.add('bespoke-overview'); addEventListener('resize', openOverview, false); overviewActive = [deck.on('activate', onActivate), deck.on('prev', onNavigate.bind(null, -1)), deck.on('next', onNavigate.bind(null, 1))]; if (!!opts.counter) parentClasses.add('bespoke-overview-counter'); if (!!opts.location) updateLocation(true); parentClasses.add('bespoke-overview-to'); numTransitions = lastSlideIndex > 0 ? getTransitionProperties(sampleSlide).length : (getTransitionProperties(sampleSlide).join(' ').indexOf('transform') < 0 ? 0 : 1); parent.style.overflowY = 'scroll'; // gives us fine-grained control parent.style.scrollBehavior = 'smooth'; // not supported by all browsers if (isWebKit) slides.forEach(function(slide) { flushStyle(slide, 'marginBottom', '0%', ''); }); } var deckWidth = parent.clientWidth / baseScale, deckHeight = parent.clientHeight / baseScale, scrollbarWidth = (scaleParent || parent).offsetWidth - parent.clientWidth, scrollbarOffset = scaleParent ? scrollbarWidth / 2 / baseScale : 0, slideWidth = sampleSlide.offsetWidth, slideHeight = sampleSlide.offsetHeight, scale = deckWidth / (columns * slideWidth + (columns + 1) * margin), totalScale = baseScale * scale, scaledSlideWidth = slideWidth * scale, scaledSlideHeight = slideHeight * scale, // NOTE x & y offset calculation based on transform origin at center of slide slideX = (deckWidth - scaledSlideWidth) / 2, slideY = (deckHeight - scaledSlideHeight) / 2, scaledMargin = margin * scale, scaledTitleHeight = 0, row = 0, col = 0; if (title) { if (opts.scaleTitle !== false) { title.style[zoomFactor ? 'zoom' : transformProp] = zoomFactor ? totalScale : 'scale(' + totalScale + ')'; title.style.width = (parent.clientWidth / totalScale) + 'px'; scaledTitleHeight = title.offsetHeight * scale; } else { if (scrollbarWidth > 0) title.style.width = parent.clientWidth + 'px'; scaledTitleHeight = title.offsetHeight / baseScale; } } slides.forEach(function(slide) { var x = col * scaledSlideWidth + (col + 1) * scaledMargin - scrollbarOffset - slideX, y = row * scaledSlideHeight + (row + 1) * scaledMargin + scaledTitleHeight - slideY; // NOTE drop scientific notation for numbers near 0 as it confuses WebKit slide.style[transformProp] = 'translate(' + (x.toString().indexOf('e-') < 0 ? x : 0) + 'px, ' + (y.toString().indexOf('e-') < 0 ? y : 0) + 'px) scale(' + scale + ')'; // NOTE add margin to last slide to leave gap below last row; only honored by WebKit if (row * columns + col === lastSlideIndex) slide.style.marginBottom = margin + 'px'; slide.addEventListener('click', onSlideClick, false); if (col === (columns - 1)) { row += 1; col = 0; } else { col += 1; } }); if (resize) { scrollSlideIntoView(slides[activeSlideIndex], activeSlideIndex, zoomFactor); } else if (numTransitions > 0) { sampleSlide.addEventListener(TRANSITIONEND, (afterTransition = function(e) { if (e.target === this && (numTransitions -= 1) === 0) { removeAfterTransition('to', parentClasses, this); if (isWebKit && parent.scrollHeight > parent.clientHeight) { flushStyle(parent, 'overflowY', 'auto', 'scroll'); // awakens scrollbar from zombie state } scrollSlideIntoView(slides[activeSlideIndex], activeSlideIndex, zoomFactor); } }), false); } else { slides.forEach(function(slide) { flushStyle(slide); }); // bypass transition, if any parentClasses.remove('bespoke-overview-to'); scrollSlideIntoView(slides[activeSlideIndex], activeSlideIndex, zoomFactor); } }, // NOTE the order of operation in this method is critical; heavily impacts behavior & transition smoothness closeOverview = function(selection) { // IMPORTANT intentionally reselect active slide to reactivate behavior deck.slide(typeof selection === 'number' ? selection : deck.slide(), { scrollIntoView: false }); var slides = deck.slides, parent = deck.parent, parentClasses = parent.classList, lastSlideIndex = slides.length - 1, sampleSlide = deck.slide() > 0 ? slides[0] : slides[lastSlideIndex], transformProp = getStyleProperty(sampleSlide, 'transform'), transitionProp = getStyleProperty(sampleSlide, 'transition'), scaleParent = parent.querySelector('.bespoke-scale-parent'), baseScale, isWebKit = 'webkitAppearance' in parent.style; if (scaleParent) { baseScale = getTransformScaleFactor(scaleParent, transformProp); } else if (!(baseScale = getZoomFactor(sampleSlide))) { baseScale = 1; } if (afterTransition) removeAfterTransition('to', parentClasses, slides[0], slides[lastSlideIndex]); var yShift = parent.scrollTop / baseScale, // xShift accounts for horizontal shift when scrollbar is removed xShift = (parent.offsetWidth - (scaleParent || parent).clientWidth) / 2 / baseScale; parent.style.scrollBehavior = parent.style.overflowY = ''; slides.forEach(function(slide) { if (isWebKit) flushStyle(slide, 'marginBottom', '0%', ''); slide.removeEventListener('click', onSlideClick, false); }); if (yShift || xShift) { slides.forEach(function(slide) { var m = slide.style[transformProp].match(RE_TRANS); slide.style[transformProp] = 'translate(' + (parseFloat(m[1]) - xShift) + 'px, ' + (parseFloat(m[2]) - yShift) + 'px) scale(' + m[3] + ')'; flushStyle(slide, transitionProp, 'none', ''); // bypass transition, if any }); } parent.scrollTop = 0; parentClasses.remove('bespoke-overview'); removeEventListener('resize', openOverview, false); (overviewActive || []).forEach(function(unbindEvent) { unbindEvent(); }); overviewActive = null; if (!!opts.counter) parentClasses.remove('bespoke-overview-counter'); if (!!opts.location) updateLocation(false); parentClasses.add('bespoke-overview-from'); var numTransitions = lastSlideIndex > 0 ? getTransitionProperties(sampleSlide).length : (getTransitionProperties(sampleSlide).join(' ').indexOf('transform') < 0 ? 0 : 1); slides.forEach(function(slide) { slide.style[transformProp] = ''; }); if (numTransitions > 0) { sampleSlide.addEventListener(TRANSITIONEND, (afterTransition = function(e) { if (e.target === this && (numTransitions -= 1) === 0) removeAfterTransition('from', parentClasses, this); }), false); } else { slides.forEach(function(slide) { flushStyle(slide); }); // bypass transition, if any parentClasses.remove('bespoke-overview-from'); } }, toggleOverview = function() { (overviewActive ? closeOverview : openOverview)(); }, onKeydown = function(e) { if (e.which === KEY_O) { if (!e.altKey && !e.ctrlKey && !e.metaKey && !e.shiftKey) toggleOverview(); } else if (overviewActive) { switch (e.which) { case KEY_ENT: if (!e.altKey && !e.ctrlKey && !e.metaKey && !e.shiftKey) closeOverview(); break; case KEY_UP: return onNavigate(-columns, { index: deck.slide() }); case KEY_DN: return onNavigate(columns, { index: deck.slide() }); } } }; deck.on('activate', onReady); deck.on('destroy', function() { removeEventListener('resize', openOverview, false); document.removeEventListener('keydown', onKeydown, false); }); deck.on('overview', toggleOverview); document.addEventListener('keydown', onKeydown, false); }; }; },{}]},{},[1])(1) });