diff options
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 - } -})(); |