diff options
author | Marvin Borner | 2020-07-09 22:33:43 +0200 |
---|---|---|
committer | Marvin Borner | 2020-07-09 22:33:43 +0200 |
commit | 1edfdf5f3a316a36108a0a853b0a2553d116d6fc (patch) | |
tree | 4b825dc642cb6eb9a060e54bf8d69288fbee4904 /src/main/resources/js/imagePreview.js | |
parent | 18edde9bd3603f3f867cebce100e7b22be9012cd (diff) |
Rewriiiiiiiiiite!
Okay, okay. I know, rewriting projects all the time is dumb.
Buuut, we really don't want to work with our old and ugly code anymore.
Furthermore we'll be using Deno now!
Diffstat (limited to 'src/main/resources/js/imagePreview.js')
-rw-r--r-- | src/main/resources/js/imagePreview.js | 663 |
1 files changed, 0 insertions, 663 deletions
diff --git a/src/main/resources/js/imagePreview.js b/src/main/resources/js/imagePreview.js deleted file mode 100644 index 0574e73..0000000 --- a/src/main/resources/js/imagePreview.js +++ /dev/null @@ -1,663 +0,0 @@ -// BigPicture.js | license MIT | henrygd.me/bigpicture -(function () { - const // assign window object to variable - global = window; - let // trigger element used to open popup - el, - // set to true after first interaction - initialized, - // container element holding html needed for script - container, - // currently active display element (image, video, youtube / vimeo iframe container) - displayElement, - // popup image element - displayImage, - // popup video element - displayVideo, - // popup audio element - displayAudio, - // container element to hold youtube / vimeo iframe - iframeContainer, - // iframe to hold youtube / vimeo player - iframeSiteVid, - // store requested image source - imgSrc, - // button that closes the container - closeButton, - // youtube / vimeo video id - siteVidID, - // keeps track of loading icon display state - isLoading, - // timeout to check video status while loading - checkMediaTimeout, - // loading icon element - loadingIcon, - // caption element - caption, - // caption content element - captionText, - // store caption content - captionContent, - // hide caption button element - captionHideButton, - // open state for container element - isOpen, - // gallery open state - galleryOpen, - // used during close animation to avoid triggering timeout twice - isClosing; - const // array of prev viewed image urls to check if cached before showing loading icon - imgCache = []; - let // store whether image requested is remote or local - remoteImage, - // store animation opening callbacks - animationStart, - animationEnd, - // gallery left / right icons - rightArrowBtn, - leftArrowBtn, - // position of gallery - galleryPosition, - // hold active gallery els / image src - galleryEls, - // counter element - galleryCounter, - // store images in gallery that are being loaded - preloadedImages = {}, - // whether device supports touch events - supportsTouch, - // options object - opts; - const // Save bytes in the minified version - doc = document, - appendEl = 'appendChild', - createEl = 'createElement', - removeEl = 'removeChild', - htmlInner = 'innerHTML', - pointerEventsAuto = 'pointer-events:auto', - cHeight = 'clientHeight', - cWidth = 'clientWidth', - listenFor = 'addEventListener', - timeout = global.setTimeout, - clearTimeout = global.clearTimeout; - - global.BigPicture = function (options) { - // initialize called on initial open to create elements / style / event handlers - initialized || initialize(); - - // clear currently loading stuff - if (isLoading) { - clearTimeout(checkMediaTimeout); - removeContainer() - } - - opts = options; - - // store video id if youtube / vimeo video is requested - siteVidID = options.ytSrc || options.vimeoSrc; - - // store optional callbacks - animationStart = options.animationStart; - animationEnd = options.animationEnd; - - // set trigger element - el = options.el; - - // wipe existing remoteImage state - remoteImage = false; - - // set caption if provided - captionContent = el.getAttribute('data-caption'); - - if (options.gallery) { - makeGallery(options.gallery) - } else if (siteVidID || options.iframeSrc) { - // if vimeo, youtube, or iframe video - toggleLoadingIcon(true); - displayElement = iframeContainer; - createIframe(); - } else if (options.imgSrc) { - // if remote image - remoteImage = true; - imgSrc = options.imgSrc; - !~imgCache.indexOf(imgSrc) && toggleLoadingIcon(true); - displayElement = displayImage; - displayElement.src = imgSrc - } else if (options.audio) { - // if direct video link - toggleLoadingIcon(true); - displayElement = displayAudio; - displayElement.src = options.audio; - checkMedia('audio file') - } else if (options.vidSrc) { - // if direct video link - toggleLoadingIcon(true); - makeVidSrc(options.vidSrc); - checkMedia('video') - } else { - // local image / background image already loaded on page - displayElement = displayImage; - // get img source or element background image - displayElement.src = - el.tagName === 'IMG' - ? el.src - : global - .getComputedStyle(el) - .backgroundImage.replace(/^url|[(|)|'|"]/g, '') - } - - // add container to page - container[appendEl](displayElement); - doc.body[appendEl](container) - }; - - // create all needed methods / store dom elements on first use - function initialize() { - let startX; - - // return close button elements - function createCloseButton(className) { - const el = doc[createEl]('button'); - el.className = className; - el[htmlInner] = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48"><path d="M28 24L47 5a3 3 0 1 0-4-4L24 20 5 1a3 3 0 1 0-4 4l19 19L1 43a3 3 0 1 0 4 4l19-19 19 19a3 3 0 0 0 4 0v-4L28 24z"/></svg>'; - return el - } - - function createArrowSymbol(direction, style) { - const el = doc[createEl]('button'); - el.className = 'bp-lr'; - el[htmlInner] = - '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 129 129" height="70" fill="#fff"><path d="M88.6 121.3c.8.8 1.8 1.2 2.9 1.2s2.1-.4 2.9-1.2a4.1 4.1 0 0 0 0-5.8l-51-51 51-51a4.1 4.1 0 0 0-5.8-5.8l-54 53.9a4.1 4.1 0 0 0 0 5.8l54 53.9z"/></svg>'; - changeCSS(el, style); - el.onclick = function (e) { - e.stopPropagation(); - updateGallery(direction) - }; - return el - } - - // add style - if you want to tweak, run through beautifier - const style = doc[createEl]('STYLE'); - style[htmlInner] = - '#bp_caption,#bp_container{bottom:0;left:0;right:0;position:fixed;opacity:0}#bp_container>*,#bp_loader{position:absolute;right:0;z-index:10}#bp_container,#bp_caption,#bp_container svg{pointer-events:none}#bp_container{top:0;z-index:9999;background:rgba(0,0,0,.7);opacity:0;transition:opacity .35s}#bp_loader{top:0;left:0;bottom:0;display:flex;margin:0;cursor:wait;z-index:9;background:0 0}#bp_loader svg{width:50%;max-width:300px;max-height:50%;margin:auto;animation:bpturn 1s infinite linear}#bp_aud,#bp_container img,#bp_sv,#bp_vid{user-select:none;max-height:96%;max-width:96%;top:0;bottom:0;left:0;margin:auto;box-shadow:0 0 3em rgba(0,0,0,.4);z-index:-1}#bp_sv{height:0;padding-bottom:54%;background-color:#000;width:96%}#bp_caption{font-size:.9em;padding:1.3em;background:rgba(15,15,15,.94);color:#fff;text-align:center;transition:opacity .3s}#bp_aud{width:650px;top:calc(50% - 20px);bottom:auto;box-shadow:none}#bp_count{left:0;right:auto;padding:14px;color:rgba(255,255,255,.7);font-size:22px;cursor:default}#bp_container button{position:absolute;border:0;outline:0;background:0 0;cursor:pointer;transition:all .1s}#bp_container>.bp-x{height:41px;width:41px;border-radius:100%;top:8px;right:14px;opacity:.8}#bp_container>.bp-x:focus,#bp_container>.bp-x:hover{background:rgba(255,255,255,.2)}.bp-x svg,.bp-xc svg{height:21px;width:20px;fill:#fff;vertical-align:top;}.bp-xc svg{width:16px}#bp_container .bp-xc{left:2%;bottom:100%;padding:9px 20px 7px;background:#d04444;border-radius:2px 2px 0 0;opacity:.85}#bp_container .bp-xc:focus,#bp_container .bp-xc:hover{opacity:1}.bp-lr{top:50%;top:calc(50% - 130px);padding:99px 0;width:6%;background:0 0;border:0;opacity:.4;transition:opacity .1s}.bp-lr:focus,.bp-lr:hover{opacity:.8}@keyframes bpf{50%{transform:translatex(15px)}100%{transform:none}}@keyframes bpl{50%{transform:translatex(-15px)}100%{transform:none}}@keyframes bpfl{0%{opacity:0;transform:translatex(70px)}100%{opacity:1;transform:none}}@keyframes bpfr{0%{opacity:0;transform:translatex(-70px)}100%{opacity:1;transform:none}}@keyframes bpfol{0%{opacity:1;transform:none}100%{opacity:0;transform:translatex(-70px)}}@keyframes bpfor{0%{opacity:1;transform:none}100%{opacity:0;transform:translatex(70px)}}@keyframes bpturn{0%{transform:none}100%{transform:rotate(360deg)}}@media (max-width:600px){.bp-lr{font-size:15vw}}@media (min-aspect-ratio:9/5){#bp_sv{height:98%;width:170.6vh;padding:0}}'; - doc.head[appendEl](style); - - // create container element - container = doc[createEl]('DIV'); - container.id = 'bp_container'; - container.onclick = close; - closeButton = createCloseButton('bp-x'); - container[appendEl](closeButton); - // gallery swipe listeners - if ('ontouchstart' in global) { - supportsTouch = true; - container.ontouchstart = function (e) { - startX = e.changedTouches[0].pageX - }; - container.ontouchmove = function (e) { - e.preventDefault() - }; - container.ontouchend = function (e) { - if (!galleryOpen) { - return - } - const touchobj = e.changedTouches[0]; - const distX = touchobj.pageX - startX; - // swipe right - distX < -30 && updateGallery(1); - // swipe left - distX > 30 && updateGallery(-1) - } - } - - // create display image element - displayImage = doc[createEl]('IMG'); - - // create display video element - displayVideo = doc[createEl]('VIDEO'); - displayVideo.id = 'bp_vid'; - displayVideo.setAttribute('playsinline', true); - displayVideo.controls = true; - displayVideo.loop = true; - - // create audio element - displayAudio = doc[createEl]("audio"); - displayAudio.id = "bp_aud"; - displayAudio.controls = true; - displayAudio.loop = true; - - // create gallery counter - galleryCounter = doc[createEl]('span'); - galleryCounter.id = 'bp_count'; - - // create caption elements - caption = doc[createEl]('DIV'); - caption.id = 'bp_caption'; - captionHideButton = createCloseButton('bp-xc'); - captionHideButton.onclick = toggleCaption.bind(null, false); - caption[appendEl](captionHideButton); - captionText = doc[createEl]('SPAN'); - caption[appendEl](captionText); - container[appendEl](caption); - - // left / right arrow icons - rightArrowBtn = createArrowSymbol(1, 'transform:scalex(-1)'); - leftArrowBtn = createArrowSymbol(-1, 'left:0;right:auto'); - - // create loading icon element - loadingIcon = doc[createEl]('DIV'); - loadingIcon.id = 'bp_loader'; - loadingIcon[htmlInner] = - '<svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 32 32" fill="#fff" opacity=".8"><path d="M16 0a16 16 0 0 0 0 32 16 16 0 0 0 0-32m0 4a12 12 0 0 1 0 24 12 12 0 0 1 0-24" fill="#000" opacity=".5"/><path d="M16 0a16 16 0 0 1 16 16h-4A12 12 0 0 0 16 4z"/></svg>'; - // create youtube / vimeo container - iframeContainer = doc[createEl]('DIV'); - iframeContainer.id = 'bp_sv'; - - // create iframe to hold youtube / vimeo player - iframeSiteVid = doc[createEl]('IFRAME'); - iframeSiteVid.setAttribute('allowfullscreen', true); - iframeSiteVid.allow = 'autoplay; fullscreen'; - iframeSiteVid.onload = open; - changeCSS(iframeSiteVid, 'border:0;position:absolute;height:100%;width:100%;left:0;top:0'); - iframeContainer[appendEl](iframeSiteVid); - - // display image bindings for image load and error - displayImage.onload = open; - displayImage.onerror = open.bind(null, 'image'); - - // adjust loader position on window resize - global[listenFor]('resize', function () { - galleryOpen || (isLoading && toggleLoadingIcon(true)) - }); - - // close container on escape key press and arrow buttons for gallery - doc[listenFor]('keyup', function (e) { - const key = e.keyCode; - key === 27 && isOpen && close(container); - if (galleryOpen) { - key === 39 && updateGallery(1); - key === 37 && updateGallery(-1); - key === 38 && updateGallery(10); - key === 40 && updateGallery(-10) - } - }); - // prevent scrolling with arrow keys if gallery open - doc[listenFor]('keydown', function (e) { - const usedKeys = [37, 38, 39, 40]; - if (galleryOpen && ~usedKeys.indexOf(e.keyCode)) { - e.preventDefault() - } - }); - - // trap focus within container while open - doc[listenFor]( - 'focus', - function (e) { - if (isOpen && !container.contains(e.target)) { - e.stopPropagation(); - closeButton.focus() - } - }, - true - ); - - // all done - initialized = true - } - - // return transform style to make full size display el match trigger el size - function getRect() { - const rect = el.getBoundingClientRect(); - const leftOffset = rect.left - (container[cWidth] - rect.width) / 2; - const centerTop = rect.top - (container[cHeight] - rect.height) / 2; - const scaleWidth = el[cWidth] / displayElement[cWidth]; - const scaleHeight = el[cHeight] / displayElement[cHeight]; - return 'transform:translate3D(' + - leftOffset + - 'px, ' + - centerTop + - 'px, 0) scale3D(' + - scaleWidth + - ', ' + - scaleHeight + - ', 0)' - } - - function makeVidSrc(source) { - if (Array.isArray(source)) { - displayElement = displayVideo.cloneNode(); - source.forEach(function (src) { - const source = doc[createEl]('SOURCE'); - source.src = src; - source.type = 'video/' + src.match(/.(\w+)$/)[1]; - displayElement[appendEl](source) - }) - } else { - displayElement = displayVideo; - displayElement.src = source - } - } - - function makeGallery(gallery) { - if (Array.isArray(gallery)) { - // is array of images - galleryPosition = 0; - galleryEls = gallery; - captionContent = gallery[0].caption - } else { - // is element selector or nodelist - galleryEls = [].slice.call(typeof gallery === 'string' ? doc.querySelectorAll(gallery + ' [data-bp]') : gallery); - // find initial gallery position - const elIndex = galleryEls.indexOf(el); - galleryPosition = elIndex !== -1 ? elIndex : 0; - // make gallery object w/ els / src / caption - galleryEls = galleryEls.map(function (el) { - return { - el: el, - src: el.getAttribute('data-bp'), - caption: el.getAttribute('data-caption') - } - }) - } - // show loading icon if needed - remoteImage = true; - // set initial src to imgSrc so it will be cached in open func - imgSrc = galleryEls[galleryPosition].src; - !~imgCache.indexOf(imgSrc) && toggleLoadingIcon(true); - if (galleryEls.length > 1) { - // if length is greater than one, add gallery stuff - container[appendEl](galleryCounter); - galleryCounter[htmlInner] = galleryPosition + 1 + '/' + galleryEls.length; - if (!supportsTouch) { - // add arrows if device doesn't support touch - container[appendEl](rightArrowBtn); - container[appendEl](leftArrowBtn) - } - } else { - // gallery is one, just show without clutter - galleryEls = false - } - displayElement = displayImage; - // set initial image src - displayElement.src = imgSrc - } - - function updateGallery(movement) { - const galleryLength = galleryEls.length - 1; - let isEnd; - - // only allow one change at a time - if (isLoading) { - return - } - - // return if requesting out of range image - if (movement > 0) { - if (galleryPosition === galleryLength) { - isEnd = true - } - } else if (galleryPosition === 0) { - isEnd = true - } - if (isEnd) { - // if beginning or end of gallery, run end animation - changeCSS(displayImage, ''); - timeout(changeCSS, 9, displayImage, 'animation:' + (movement > 0 ? 'bpl' : 'bpf') + ' .3s;transition:transform .35s'); - return - } - - // normalize position - galleryPosition = Math.max( - 0, - Math.min(galleryPosition + movement, galleryLength) - ) - - // load images before and after for quicker scrolling through pictures - ;[galleryPosition - 1, galleryPosition, galleryPosition + 1].forEach( - function (position) { - // normalize position - position = Math.max(0, Math.min(position, galleryLength)); - // cancel if image has already been preloaded - if (preloadedImages[position]) return; - const src = galleryEls[position].src; - // create image for preloadedImages - const img = doc[createEl]('IMG'); - img[listenFor]('load', addToImgCache.bind(null, src)); - img.src = src; - preloadedImages[position] = img - } - ); - // if image is loaded, show it - if (preloadedImages[galleryPosition].complete) { - return changeGalleryImage(movement) - } - // if not, show loading icon and change when loaded - isLoading = true; - changeCSS(loadingIcon, 'opacity:.4;'); - container[appendEl](loadingIcon); - preloadedImages[galleryPosition].onload = function () { - galleryOpen && changeGalleryImage(movement) - }; - // if error, store error object in el array - preloadedImages[galleryPosition].onerror = function () { - galleryEls[galleryPosition] = { - error: 'Error loading image' - }; - galleryOpen && changeGalleryImage(movement) - } - } - - function changeGalleryImage(movement) { - if (isLoading) { - container[removeEl](loadingIcon); - isLoading = false - } - const activeEl = galleryEls[galleryPosition]; - if (activeEl.error) { - // show alert if error - alert(activeEl.error) - } else { - // add new image, animate images in and out w/ css animation - const oldimg = container.querySelector('img:last-of-type'); - displayImage = displayElement = preloadedImages[galleryPosition]; - changeCSS(displayImage, 'animation:' + (movement > 0 ? 'bpfl' : 'bpfr') + ' .35s;transition:transform .35s'); - changeCSS(oldimg, 'animation:' + (movement > 0 ? 'bpfol' : 'bpfor') + ' .35s both'); - container[appendEl](displayImage); - // update el for closing animation - if (activeEl.el) { - el = activeEl.el - } - } - // update counter - galleryCounter[htmlInner] = galleryPosition + 1 + '/' + galleryEls.length; - // show / hide caption - toggleCaption(galleryEls[galleryPosition].caption) - } - - // create video iframe - function createIframe() { - let url; - const prefix = 'https://'; - const suffix = 'autoplay=1'; - - // create appropriate url - if (opts.ytSrc) { - url = prefix + 'www.youtube.com/embed/' + siteVidID + '?html5=1&rel=0&playsinline=1&' + suffix; - } else if (opts.vimeoSrc) { - url = prefix + 'player.vimeo.com/video/' + siteVidID + '?' + suffix; - } else if (opts.iframeSrc) { - url = opts.iframeSrc; - } - - // set iframe src to url - iframeSiteVid.src = url; - } - - // timeout to check video status while loading - function checkMedia(errMsg) { - if (~[1, 4].indexOf(displayElement.readyState)) { - open(); - // short timeout to to make sure controls show in safari 11 - timeout(function () { - displayElement.play() - }, 99) - } else if (displayElement.error) open(errMsg); - else checkMediaTimeout = timeout(checkMedia, 35, errMsg) - } - - // hide / show loading icon - function toggleLoadingIcon(bool) { - // don't show loading icon if noLoader is specified - if (opts.noLoader) return; - // bool is true if we want to show icon, false if we want to remove - // change style to match trigger element dimensions if we want to show - bool && - changeCSS( - loadingIcon, - 'top:' + - el.offsetTop + - 'px;left:' + - el.offsetLeft + - 'px;height:' + - el[cHeight] + - 'px;width:' + - el[cWidth] + - 'px' - ); - // add or remove loader from DOM - el.parentElement[bool ? appendEl : removeEl](loadingIcon); - isLoading = bool - } - - // hide & show caption - function toggleCaption(captionContent) { - if (captionContent) { - captionText[htmlInner] = captionContent - } - changeCSS( - caption, - 'opacity:' + (captionContent ? '1;' + pointerEventsAuto : '0') - ) - } - - function addToImgCache(url) { - !~imgCache.indexOf(url) && imgCache.push(url) - } - - // animate open of image / video; display caption if needed - function open(err) { - // hide loading spinner - isLoading && toggleLoadingIcon(); - - // execute animationStart callback - animationStart && animationStart(); - - // check if we have an error string instead of normal event - if (typeof err === 'string') { - removeContainer(); - return opts.onError ? opts.onError() : alert('Error: The requested ' + err + ' could not be loaded.') - } - - // if remote image is loaded, add url to imgCache array - remoteImage && addToImgCache(imgSrc); - - // transform displayEl to match trigger el - changeCSS(displayElement, getRect()); - - // fade in container - changeCSS(container, 'opacity:1;' + pointerEventsAuto); - - // set animationEnd callback to run after animation ends (cleared if container closed) - animationEnd = timeout(animationEnd, 410); - - isOpen = true; - - galleryOpen = !!galleryEls; - - // enlarge displayEl, fade in caption if hasCaption - timeout(function () { - changeCSS(displayElement, 'transition:transform .35s;transform:none'); - captionContent && timeout(toggleCaption, 250, captionContent) - }, 60) - } - - // close active display element - function close(e) { - const target = e.target; - const clickEls = [ - caption, - captionHideButton, - displayVideo, - displayAudio, - captionText, - leftArrowBtn, - rightArrowBtn, - loadingIcon - ]; - - // blur to hide close button focus style - target && target.blur(); - - // don't close if one of the clickEls was clicked or container is already closing - if (isClosing || ~clickEls.indexOf(target)) { - return - } - - // animate closing - displayElement.style.cssText += getRect(); - changeCSS(container, pointerEventsAuto); - - // timeout to remove els from dom; use variable to avoid calling more than once - timeout(removeContainer, 350); - - // clear animationEnd timeout - clearTimeout(animationEnd); - - isOpen = false; - isClosing = true - } - - // remove container / display element from the DOM - function removeContainer() { - // remove container from DOM & clear inline style - doc.body[removeEl](container); - container[removeEl](displayElement); - changeCSS(container, '') - - // clear src of displayElement (or iframe if display el is iframe container) - ;(displayElement === iframeContainer - ? iframeSiteVid - : displayElement - ).removeAttribute('src'); - - // remove caption - toggleCaption(false); - - if (galleryOpen) { - // remove all gallery stuff - const images = container.querySelectorAll('img'); - for (let i = 0; i < images.length; i++) { - container[removeEl](images[i]) - } - isLoading && container[removeEl](loadingIcon); - container[removeEl](galleryCounter); - galleryOpen = galleryEls = false; - preloadedImages = {}; - supportsTouch || container[removeEl](rightArrowBtn); - supportsTouch || container[removeEl](leftArrowBtn); - // in case displayimage changed, we need to update event listeners - displayImage.onload = open; - displayImage.onerror = open.bind(null, 'image') - } - - // run close callback - opts.onClose && opts.onClose(); - - isClosing = isLoading = false - } - - // style helper functions - function changeCSS(element, newStyle) { - element.style.cssText = newStyle - } -})(); |