UNPKG

exarcheia-vue-silentbox

Version:

A simple lightbox inspired Vue.js component.

989 lines 63.7 kB
var VueSilentbox = (function (exports) {
  'use strict';

  var VideoUrlDecoderMixin = {
    methods: {
      getYoutubeVideoId(url) {
        const regExp = /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#&?]*).*/;
        const match = url.match(regExp);
        return match !== undefined && match[7] !== undefined ? match[7] : false;
      },

      getVimeoVideoId(url) {
        return /(vimeo(pro)?\.com)\/(?:[^\d]+)?(\d+)\??(.*)?$/.exec(url)[3];
      }

    }
  };

  var itemMixin = {
    mixins: [VideoUrlDecoderMixin],
    data: () => ({
      sizeVideoYoutube: 'maxresdefault',
      sizesYoutube: ['maxresdefault', 'sddefault', 'hqdefault'],
      youtubeDomain: '//img.youtube.com/vi',
      vimeoDomain: '//vimeo.com/api/v2/video'
    }),
    methods: {
      isEmbedVideo(itemSrc) {
        const supportedVideoServices = ['youtube.com', 'youtu.be', 'vimeo.com'];
        return supportedVideoServices.some(service => {
          return itemSrc.includes(service);
        });
      },

      isLocalVideo(itemSrc) {
        const supportedVideoServices = ['.mp4', '.ogg', '.webm', '.mov', '.flv', '.wmv', '.mkv'];
        return supportedVideoServices.some(service => {
          return itemSrc.includes(service);
        });
      },

      async getYoutubeThumbnail(videoId) {
        for (const size of this.sizesYoutube) {
          const url = await new Promise(resolve => {
            try {
              const image = new Image();
              image.src = `${location.protocol}${this.youtubeDomain}/${videoId}/${size}.jpg`;

              image.onload = ({
                target
              }) => {
                resolve(target.width !== 120 ? target.src : false);
              };

              image.onerror = () => resolve(false);
            } catch (e) {
              return false;
            }
          });

          if (url) {
            return url;
          }
        }
      },

      async getThumbnail(src) {
        if (src.includes('youtube.com') || src.includes('youtu.be')) {
          return await this.getYoutubeThumbnail(this.getYoutubeVideoId(src));
        } else if (src.includes('vimeo.com')) {
          const videoId = this.getVimeoVideoId(src);
          const videoDetails = await this.httpGet(`${location.protocol}${this.vimeoDomain}/${videoId}.json`);
          return videoDetails[0].thumbnail_large;
        } else {
          return src;
        }
      },

      async httpGet(url) {
        const result = await fetch(url);
        return await result.json();
      }

    }
  };

  //
  var script = {
    name: 'SilentboxOverlay',
    mixins: [itemMixin],
    props: {
      overlayItem: {
        type: Object,
        default: () => ({
          src: '',
          srcSet: '',
          sources: [],
          description: ''
        })
      },
      visible: {
        type: Boolean,
        default: false
      },
      totalItems: {
        type: Number,
        default: 1
      }
    },

    data() {
      return {
        touchHandling: {
          posX: 0,
          posY: 0
        },
        animationName: 'silentbox-animation__swipe-left'
      };
    },

    computed: {
      /**
       * Check if there are other sources of the image
       */
      isSources() {
        return Array.isArray(this.overlayItem.sources) && this.overlayItem.sources.length;
      },

      /**
       * Get other image sources
       */
      getSources() {
        return this.overlayItem.sources;
      }

    },

    created() {
      // Listen to key events.
      window.addEventListener('keyup', event => {
        // Escape: 27
        if (event.which === 27) {
          this.closeSilentboxOverlay();
        } // Right arrow: 39


        if (event.which === 39) {
          this.moveToNextItem();
        } // Left arrow: 37


        if (event.which === 37) {
          this.moveToPreviousItem();
        }
      }); // Disable browser scrolling.

      this.enableScrollLock();
    },

    methods: {
      /**
       * Registers the finger position on website so we can later calculate users
       * swipe direction.
       */
      touchStart(event) {
        const {
          clientX: x,
          clientY: y
        } = event.touches[0];
        this.touchHandling.posX = x;
        this.touchHandling.posY = y;
      },

      /**
       * Handles touch movement events, at the moment only swipe left and right
       * are supported, but later could be extended with up and down swipes.
       * It should be good to implement some kind of minimal swipe lenght support.
       */
      touchMove(event) {
        const {
          clientX: x,
          clientY: y
        } = event.touches[0];
        const {
          posX,
          posY
        } = this.touchHandling;

        if (posX === 0 || posY === 0) {
          return;
        }

        const xDiff = posX - x;
        const yDiff = posY - y;

        if (Math.abs(xDiff) > Math.abs(yDiff)) {
          if (xDiff > 0) {
            // left
            this.moveToNextItem();
          } else {
            // right
            this.moveToPreviousItem();
          }
        } // reset


        this.touchHandling.posX = 0;
        this.touchHandling.posY = 0;
      },

      /**
       * This method enables browser scrolling lock which prevent from horizontal
       * and vertical scrolling. This makes touch navigation less confusing.
       */
      enableScrollLock() {
        if (!document.body.classList.contains('silentbox-is-opened')) {
          return document.body.classList.add('silentbox-is-opened');
        }
      },

      /**
       * This method removes browser scrolling lock.
       */
      removeScrollLock() {
        if (document.body.classList.contains('silentbox-is-opened')) {
          return document.body.classList.remove('silentbox-is-opened');
        }
      },

      /**
       * Move to next item.
       */
      moveToNextItem() {
        this.animationName = 'silentbox-animation__swipe-left';
        this.$emit('requestNextSilentBoxItem');
      },

      /**
       * Move to previous item.
       */
      moveToPreviousItem() {
        this.animationName = 'silentbox-animation__swipe-right';
        this.$emit('requestPreviousSilentBoxItem');
      },

      /**
       * Hide silentbox overlay.
       */
      closeSilentboxOverlay() {
        this.removeScrollLock();
        this.$emit('closeSilentboxOverlay');
      },

      /**
       * Search for known video services URLs and return their players if recognized.
       * Unrecognized URLs are handled as images.
       *
       * @param  {string} url
       * @return {string}
       */
      handleUrl(url) {
        if (url.includes('youtube.com') || url.includes('youtu.be')) {
          return this.parseYoutubeVideo(url);
        } else if (url.includes('vimeo')) {
          return this.parseVimeoVideo(url);
        }

        return url;
      },

      /**
       * Get embed URL for youtube.com
       *
       * @param  {string} url
       * @return {string}
       */
      parseYoutubeVideo(url) {
        let videoUrl = '';
        const videoId = this.getYoutubeVideoId(url);

        if (videoId) {
          videoUrl = `${location.protocol}//www.youtube.com/embed/${videoId}?rel=0`;

          if (this.overlayItem.autoplay) {
            videoUrl += '&autoplay=1';
          }

          if (!this.overlayItem.controls) {
            videoUrl += '&controls=0';
          }
        }

        return videoUrl;
      },

      /**
       * Get embed URL for vimeo.com
       *
       * @param  {string} url
       * @return {string}
       */
      parseVimeoVideo(url) {
        let videoUrl = '';
        const vimoId = /(vimeo(pro)?\.com)\/(?:[^\d]+)?(\d+)\??(.*)?$/.exec(url)[3];

        if (vimoId !== undefined) {
          videoUrl = `${location.protocol}//player.vimeo.com/video/${vimoId}?rel=0`;

          if (this.overlayItem.autoplay === 'autoplay') {
            videoUrl += '&autoplay=1';
          }
        }

        return videoUrl;
      }

    }
  };

  function normalizeComponent(template, style, script, scopeId, isFunctionalTemplate, moduleIdentifier
  /* server only */
  , shadowMode, createInjector, createInjectorSSR, createInjectorShadow) {
    if (typeof shadowMode !== 'boolean') {
      createInjectorSSR = createInjector;
      createInjector = shadowMode;
      shadowMode = false;
    } // Vue.extend constructor export interop.


    const options = typeof script === 'function' ? script.options : script; // render functions

    if (template && template.render) {
      options.render = template.render;
      options.staticRenderFns = template.staticRenderFns;
      options._compiled = true; // functional template

      if (isFunctionalTemplate) {
        options.functional = true;
      }
    } // scopedId


    if (scopeId) {
      options._scopeId = scopeId;
    }

    let hook;

    if (moduleIdentifier) {
      // server build
      hook = function (context) {
        // 2.3 injection
        context = context || // cached call
        this.$vnode && this.$vnode.ssrContext || // stateful
        this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext; // functional
        // 2.2 with runInNewContext: true

        if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') {
          context = __VUE_SSR_CONTEXT__;
        } // inject component styles


        if (style) {
          style.call(this, createInjectorSSR(context));
        } // register component module identifier for async chunk inference


        if (context && context._registeredComponents) {
          context._registeredComponents.add(moduleIdentifier);
        }
      }; // used by ssr in case component is cached and beforeCreate
      // never gets called


      options._ssrRegister = hook;
    } else if (style) {
      hook = shadowMode ? function (context) {
        style.call(this, createInjectorShadow(context, this.$root.$options.shadowRoot));
      } : function (context) {
        style.call(this, createInjector(context));
      };
    }

    if (hook) {
      if (options.functional) {
        // register for functional component in vue file
        const originalRender = options.render;

        options.render = function renderWithStyleInjection(h, context) {
          hook.call(context);
          return originalRender(h, context);
        };
      } else {
        // inject component registration as beforeCreate hook
        const existing = options.beforeCreate;
        options.beforeCreate = existing ? [].concat(existing, hook) : [hook];
      }
    }

    return script;
  }

  const isOldIE = typeof navigator !== 'undefined' && /msie [6-9]\\b/.test(navigator.userAgent.toLowerCase());

  function createInjector(context) {
    return (id, style) => addStyle(id, style);
  }

  let HEAD;
  const styles = {};

  function addStyle(id, css) {
    const group = isOldIE ? css.media || 'default' : id;
    const style = styles[group] || (styles[group] = {
      ids: new Set(),
      styles: []
    });

    if (!style.ids.has(id)) {
      style.ids.add(id);
      let code = css.source;

      if (css.map) {
        // https://developer.chrome.com/devtools/docs/javascript-debugging
        // this makes source maps inside style tags work properly in Chrome
        code += '\n/*# sourceURL=' + css.map.sources[0] + ' */'; // http://stackoverflow.com/a/26603875

        code += '\n/*# sourceMappingURL=data:application/json;base64,' + btoa(unescape(encodeURIComponent(JSON.stringify(css.map)))) + ' */';
      }

      if (!style.element) {
        style.element = document.createElement('style');
        style.element.type = 'text/css';
        if (css.media) style.element.setAttribute('media', css.media);

        if (HEAD === undefined) {
          HEAD = document.head || document.getElementsByTagName('head')[0];
        }

        HEAD.appendChild(style.element);
      }

      if ('styleSheet' in style.element) {
        style.styles.push(code);
        style.element.styleSheet.cssText = style.styles.filter(Boolean).join('\n');
      } else {
        const index = style.ids.size - 1;
        const textNode = document.createTextNode(code);
        const nodes = style.element.childNodes;
        if (nodes[index]) style.element.removeChild(nodes[index]);
        if (nodes.length) style.element.insertBefore(textNode, nodes[index]);else style.element.appendChild(textNode);
      }
    }
  }

  /* script */
  const __vue_script__ = script;

  /* template */
  var __vue_render__ = function() {
    var _vm = this;
    var _h = _vm.$createElement;
    var _c = _vm._self._c || _h;
    return _vm.visible
      ? _c(
          "div",
          {
            staticClass: "silentbox-overlay",
            on: { touchstart: _vm.touchStart, touchmove: _vm.touchMove }
          },
          [
            _c(
              "button",
              {
                staticClass: "silentbox-overlay__close",
                on: {
                  click: function($event) {
                    $event.stopPropagation();
                    return _vm.closeSilentboxOverlay($event)
                  }
                }
              },
              [_c("i", { staticClass: "icon" })]
            ),
            _vm._v(" "),
            _c("div", { staticClass: "silentbox-overlay__background" }),
            _vm._v(" "),
            _c(
              "transition",
              { attrs: { name: _vm.animationName, mode: "out-in" } },
              [
                _c(
                  "div",
                  {
                    key: _vm.overlayItem.src,
                    staticClass: "silentbox-overlay__content",
                    on: {
                      click: function($event) {
                        $event.stopPropagation();
                        return _vm.closeSilentboxOverlay($event)
                      }
                    }
                  },
                  [
                    _c("div", { staticClass: "silentbox-overlay__embed" }, [
                      _c("div", { staticClass: "silentbox-overlay__container" }, [
                        _vm.isEmbedVideo(_vm.overlayItem.src)
                          ? _c("iframe", {
                              attrs: {
                                allow:
                                  "accelerometer; " +
                                  (!!_vm.overlayItem.autoplay && "autoplay;") +
                                  " encrypted-media; gyroscope; picture-in-picture",
                                src: _vm.handleUrl(_vm.overlayItem.src),
                                frameborder: "0",
                                width: "100%",
                                height: "100%",
                                allowfullscreen: ""
                              }
                            })
                          : _vm.isLocalVideo(_vm.overlayItem.src)
                          ? _c(
                              "div",
                              { staticClass: "silentbox-overlay__frame" },
                              [
                                _c("video", {
                                  staticClass: "silentbox-overlay__embed",
                                  attrs: {
                                    src: _vm.overlayItem.src,
                                    autoplay: _vm.overlayItem.autoplay,
                                    controls: ""
                                  }
                                })
                              ]
                            )
                          : _c(
                              "picture",
                              {
                                staticClass: "silentbox-overlay__picture",
                                on: {
                                  click: function($event) {
                                    $event.stopPropagation();
                                    return _vm.moveToNextItem($event)
                                  }
                                }
                              },
                              [
                                _vm.isSources
                                  ? _vm._l(_vm.getSources, function(source) {
                                      return _c("source", {
                                        key: source.src,
                                        attrs: {
                                          srcset: source.srcSet,
                                          media: source.media,
                                          src: source.src,
                                          type: source.type
                                        }
                                      })
                                    })
                                  : _vm._e(),
                                _vm._v(" "),
                                _c("img", {
                                  attrs: {
                                    srcset: _vm.overlayItem.srcSet
                                      ? _vm.overlayItem.srcSet
                                      : _vm.overlayItem.src,
                                    src: _vm.overlayItem.src,
                                    alt: _vm.overlayItem.alt,
                                    width: "auto",
                                    height: "auto"
                                  }
                                })
                              ],
                              2
                            )
                      ]),
                      _vm._v(" "),
                      _vm.overlayItem.description
                        ? _c(
                            "p",
                            { staticClass: "silentbox-overlay__description" },
                            [
                              _vm._v(
                                "\n          " +
                                  _vm._s(_vm.overlayItem.description) +
                                  "\n        "
                              )
                            ]
                          )
                        : _vm._e()
                    ])
                  ]
                )
              ]
            ),
            _vm._v(" "),
            _vm.totalItems > 1
              ? _c("div", { staticClass: "silentbox-overlay__buttons" }, [
                  _c(
                    "button",
                    {
                      staticClass:
                        "silentbox-overlay__buttons-arrow silentbox-overlay__buttons-arrow--previous",
                      on: {
                        click: function($event) {
                          $event.stopPropagation();
                          return _vm.moveToPreviousItem($event)
                        }
                      }
                    },
                    [_c("i", { staticClass: "arrow-icon" })]
                  ),
                  _vm._v(" "),
                  _c(
                    "button",
                    {
                      staticClass:
                        "silentbox-overlay__buttons-arrow silentbox-overlay__buttons-arrow--next",
                      on: {
                        click: function($event) {
                          $event.stopPropagation();
                          return _vm.moveToNextItem($event)
                        }
                      }
                    },
                    [_c("i", { staticClass: "arrow-icon" })]
                  )
                ])
              : _vm._e()
          ],
          1
        )
      : _vm._e()
  };
  var __vue_staticRenderFns__ = [];
  __vue_render__._withStripped = true;

    /* style */
    const __vue_inject_styles__ = function (inject) {
      if (!inject) return
      inject("data-v-575b43a3_0", { source: ".silentbox-is-opened {\n  overflow: hidden;\n}\n.silentbox-overlay {\n  position: fixed;\n  left: 0;\n  top: 0;\n  z-index: 999;\n  display: block;\n  width: 100vw;\n  height: 100vh;\n}\n.silentbox-overlay__picture {\n  cursor: pointer;\n}\n.silentbox-overlay__frame {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  width: 100%;\n  height: 100%;\n}\n.silentbox-overlay__background {\n  position: absolute;\n  left: 0;\n  top: 0;\n  display: block;\n  width: 100%;\n  height: 100%;\n  background: rgba(0, 0, 0, 0.75);\n  backdrop-filter: blur(20px);\n}\n.silentbox-overlay__content {\n  position: relative;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  width: 100%;\n  height: 100%;\n}\n.silentbox-overlay__embed {\n  width: 75%;\n  height: 80%;\n}\n.silentbox-overlay__embed img,\n.silentbox-overlay__embed iframe {\n  position: absolute;\n  left: 0;\n  right: 0;\n  top: 0;\n  bottom: 0;\n  display: block;\n  margin: auto;\n  max-width: 100%;\n  max-height: 100%;\n  box-shadow: 0 0 1.5rem rgba(0, 0, 0, 0.45);\n  background-color: #000;\n}\n.silentbox-overlay__embed:active, .silentbox-overlay__embed:focus, .silentbox-overlay__embed:hover {\n  outline: none;\n}\n.silentbox-overlay__container {\n  position: relative;\n  margin: 0;\n  width: 100%;\n  height: 100%;\n  text-align: center;\n}\n.silentbox-overlay__description {\n  display: block;\n  padding-top: 1rem;\n  text-align: center;\n  color: #fff;\n}\n.silentbox-overlay__buttons-arrow {\n  position: absolute;\n  top: 0;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  width: 6%;\n  height: 100%;\n  transition: 0.3s;\n  cursor: pointer;\n}\n.silentbox-overlay__buttons-arrow .arrow-icon {\n  border-top: 2px solid #fff;\n  border-left: 2px solid #fff;\n  width: 1.5rem;\n  height: 1.5rem;\n}\n.silentbox-overlay__buttons-arrow:hover {\n  background: rgba(0, 0, 0, 0.2);\n}\n.silentbox-overlay__buttons-arrow--previous {\n  left: 0;\n}\n.silentbox-overlay__buttons-arrow--previous .arrow-icon {\n  transform: rotate(-45deg);\n}\n.silentbox-overlay__buttons-arrow--previous:hover .arrow-icon, .silentbox-overlay__buttons-arrow--previous:focus .arrow-icon {\n  opacity: 0.7;\n  animation-name: pulsingPrevious;\n  animation-duration: 1s;\n  animation-iteration-count: infinite;\n}\n.silentbox-overlay__buttons-arrow--next {\n  right: 0;\n}\n.silentbox-overlay__buttons-arrow--next .arrow-icon {\n  transform: rotate(135deg);\n}\n.silentbox-overlay__buttons-arrow--next:hover .arrow-icon, .silentbox-overlay__buttons-arrow--next:focus .arrow-icon {\n  opacity: 0.7;\n  animation-name: pulsingNext;\n  animation-duration: 1s;\n  animation-iteration-count: infinite;\n}\n@media (max-width: 1199px) {\n.silentbox-overlay__buttons-arrow {\n    width: 12%;\n}\n}\n.silentbox-overlay__close {\n  position: absolute;\n  right: 7%;\n  top: 16px;\n  z-index: 2000;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  width: 2.5em;\n  height: 2.5em;\n  color: #fff;\n  cursor: pointer;\n}\n.silentbox-overlay__close .icon {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n.silentbox-overlay__close .icon::before {\n  transform: rotate(-45deg);\n}\n.silentbox-overlay__close .icon::before, .silentbox-overlay__close .icon::after {\n  content: \"\";\n  position: absolute;\n  width: 100%;\n  height: 2.5px;\n  background: #fff;\n}\n.silentbox-overlay__close .icon::after {\n  transform: rotate(45deg);\n}\n.silentbox-overlay__close:hover {\n  opacity: 0.75;\n}\n@media (max-width: 1199px) {\n.silentbox-overlay__close {\n    width: 1.8em;\n    height: 1.8em;\n}\n}\n@media (max-width: 1199px) {\n.silentbox-overlay__close {\n    right: 13%;\n}\n}\n.silentbox-overlay .silentbox-animation__swipe-left-enter-active {\n  opacity: 0;\n  transform: translateX(25vw);\n  transition: all 0.3s ease;\n}\n.silentbox-overlay .silentbox-animation__swipe-left-leave-active {\n  transition: all 0.3s ease;\n}\n.silentbox-overlay .silentbox-animation__swipe-left-enter-to {\n  opacity: 1;\n  transform: translateX(0);\n  transition: all 0.3s ease;\n}\n.silentbox-overlay .silentbox-animation__swipe-left-leave-to {\n  opacity: 0;\n  transform: translateX(-25vw);\n  transition: all 0.3s ease;\n}\n.silentbox-overlay .silentbox-animation__swipe-right-enter-active {\n  opacity: 0;\n  transform: translateX(-25vw);\n  transition: all 0.3s ease;\n}\n.silentbox-overlay .silentbox-animation__swipe-right-leave-active {\n  transition: all 0.3s ease;\n}\n.silentbox-overlay .silentbox-animation__swipe-right-enter-to {\n  opacity: 1;\n  transform: translateX(0);\n  transition: all 0.3s ease;\n}\n.silentbox-overlay .silentbox-animation__swipe-right-leave-to {\n  opacity: 0;\n  transform: translateX(25vw);\n  transition: all 0.3s ease;\n}\n@keyframes pulsingNext {\n0% {\n    margin-right: 0;\n    animation-timing-function: ease-in;\n}\n25% {\n    margin-right: 0.25rem;\n    animation-timing-function: ease-in;\n}\n50% {\n    margin-right: 0.5rem;\n    animation-timing-function: ease-in;\n}\n75% {\n    margin-right: 0.25rem;\n    animation-timing-function: ease-in;\n}\n100% {\n    margin-right: 0;\n    animation-timing-function: ease-in;\n}\n}\n@keyframes pulsingPrevious {\n0% {\n    margin-left: 0;\n    animation-timing-function: ease-in;\n}\n25% {\n    margin-left: 0.25rem;\n    animation-timing-function: ease-in;\n}\n50% {\n    margin-left: 0.5rem;\n    animation-timing-function: ease-in;\n}\n75% {\n    margin-left: 0.25rem;\n    animation-timing-function: ease-in;\n}\n100% {\n    margin-left: 0;\n    animation-timing-function: ease-in;\n}\n}\n\n/*# sourceMappingURL=overlay.vue.map */", map: {"version":3,"sources":["N:\\Projects\\silentbox\\src\\components\\overlay.vue","overlay.vue"],"names":[],"mappings":"AAqUA;EACA,gBAAA;ACpUA;ADuUA;EACA,eAAA;EACA,OAAA;EACA,MAAA;EACA,YAAA;EACA,cAAA;EACA,YAAA;EACA,aAAA;ACpUA;ADsUA;EACA,eAAA;ACpUA;ADuUA;EACA,aAAA;EACA,mBAAA;EACA,uBAAA;EACA,WAAA;EACA,YAAA;ACrUA;ADwUA;EACA,kBAAA;EACA,OAAA;EACA,MAAA;EACA,cAAA;EACA,WAAA;EACA,YAAA;EACA,+BAAA;EACA,2BAAA;ACtUA;ADyUA;EACA,kBAAA;EACA,aAAA;EACA,mBAAA;EACA,uBAAA;EACA,WAAA;EACA,YAAA;ACvUA;AD0UA;EACA,UAAA;EACA,WAAA;ACxUA;AD0UA;;EAEA,kBAAA;EACA,OAAA;EACA,QAAA;EACA,MAAA;EACA,SAAA;EACA,cAAA;EACA,YAAA;EACA,eAAA;EACA,gBAAA;EACA,0CAAA;EACA,sBA/DA;ACzQA;AD2UA;EAGA,aAAA;AC3UA;AD+UA;EACA,kBAAA;EACA,SAAA;EACA,WAAA;EACA,YAAA;EACA,kBAAA;AC7UA;ADgVA;EACA,cAAA;EACA,iBAAA;EACA,kBAAA;EACA,WAvFA;ACvPA;ADkVA;EACA,kBAAA;EACA,MAAA;EACA,aAAA;EACA,mBAAA;EACA,uBAAA;EACA,SAAA;EACA,YAAA;EACA,gBAAA;EACA,eAAA;AChVA;ADkVA;EACA,0BAAA;EACA,2BAAA;EACA,aAAA;EACA,cAAA;AChVA;ADmVA;EACA,8BAAA;ACjVA;ADoVA;EACA,OAAA;AClVA;ADoVA;EACA,yBAAA;AClVA;ADqVA;EAEA,YAAA;EACA,+BAAA;EACA,sBAAA;EACA,mCAAA;ACpVA;ADwVA;EACA,QAAA;ACtVA;ADwVA;EACA,yBAAA;ACtVA;ADyVA;EAEA,YAAA;EACA,2BAAA;EACA,sBAAA;EACA,mCAAA;ACxVA;AD4VA;AAtDA;IAuDA,UAAA;ACzVE;AACF;AD6VA;EACA,kBAAA;EACA,SAAA;EACA,SAAA;EACA,aAAA;EACA,aAAA;EACA,mBAAA;EACA,uBAAA;EACA,YAAA;EACA,aAAA;EACA,WAjKA;EAkKA,eAAA;AC3VA;AD6VA;EACA,aAAA;EACA,mBAAA;EACA,uBAAA;AC3VA;AD6VA;EACA,yBAAA;AC3VA;AD8VA;EAEA,WAAA;EACA,kBAAA;EACA,WAAA;EACA,aAAA;EACA,gBAnLA;AC1KA;ADgWA;EACA,wBAAA;AC9VA;ADkWA;EACA,aAAA;AChWA;ADmWA;AAxCA;IAyCA,YAAA;IACA,aAAA;AChWE;AACF;ADkWA;AA7CA;IA8CA,UAAA;AC/VE;AACF;ADmWA;EACA,UAAA;EACA,2BAAA;EACA,yBAAA;ACjWA;ADoWA;EACA,yBAAA;AClWA;ADqWA;EACA,UAAA;EACA,wBAAA;EACA,yBAAA;ACnWA;ADsWA;EACA,UAAA;EACA,4BAAA;EACA,yBAAA;ACpWA;ADuWA;EACA,UAAA;EACA,4BAAA;EACA,yBAAA;ACrWA;ADwWA;EACA,yBAAA;ACtWA;ADyWA;EACA,UAAA;EACA,wBAAA;EACA,yBAAA;ACvWA;AD0WA;EACA,UAAA;EACA,2BAAA;EACA,yBAAA;ACxWA;AD4WA;AACA;IACA,eAAA;IACA,kCAAA;AC1WE;AD6WF;IACA,qBAAA;IACA,kCAAA;AC3WE;AD8WF;IACA,oBAAA;IACA,kCAAA;AC5WE;AD+WF;IACA,qBAAA;IACA,kCAAA;AC7WE;ADgXF;IACA,eAAA;IACA,kCAAA;AC9WE;AACF;ADiXA;AACA;IACA,cAAA;IACA,kCAAA;AC/WE;ADkXF;IACA,oBAAA;IACA,kCAAA;AChXE;ADmXF;IACA,mBAAA;IACA,kCAAA;ACjXE;ADoXF;IACA,oBAAA;IACA,kCAAA;AClXE;ADqXF;IACA,cAAA;IACA,kCAAA;ACnXE;AACF;;AAEA,sCAAsC","file":"overlay.vue","sourcesContent":["<template>\n  <div\n    class=\"silentbox-overlay\"\n    v-if=\"visible\"\n    @touchstart=\"touchStart\"\n    @touchmove=\"touchMove\"\n  >\n    <button\n      class=\"silentbox-overlay__close\"\n      @click.stop=\"closeSilentboxOverlay\"\n    >\n      <i class=\"icon\" />\n    </button>\n    <div class=\"silentbox-overlay__background\" />\n    <transition :name=\"animationName\" mode=\"out-in\">\n      <div\n        class=\"silentbox-overlay__content\"\n        :key=\"overlayItem.src\"\n        @click.stop=\"closeSilentboxOverlay\"\n      >\n        <div class=\"silentbox-overlay__embed\">\n          <div class=\"silentbox-overlay__container\">\n            <!-- embed video rendering -->\n            <iframe\n              v-if=\"isEmbedVideo(overlayItem.src)\"\n              :allow=\"`accelerometer; ${\n                !!overlayItem.autoplay && 'autoplay;'\n              } encrypted-media; gyroscope; picture-in-picture`\"\n              :src=\"handleUrl(overlayItem.src)\"\n              frameborder=\"0\"\n              width=\"100%\"\n              height=\"100%\"\n              allowfullscreen\n            />\n            <!-- local video rendering -->\n            <div\n              v-else-if=\"isLocalVideo(overlayItem.src)\"\n              class=\"silentbox-overlay__frame\"\n            >\n              <video\n                :src=\"overlayItem.src\"\n                :autoplay=\"overlayItem.autoplay\"\n                controls\n                class=\"silentbox-overlay__embed\"\n              />\n            </div>\n            <!-- local/embed image rendering -->\n            <picture\n              v-else\n              @click.stop=\"moveToNextItem\"\n              class=\"silentbox-overlay__picture\"\n            >\n              <template v-if=\"isSources\">\n                <source\n                  v-for=\"source in getSources\"\n                  :key=\"source.src\"\n                  :srcset=\"source.srcSet\"\n                  :media=\"source.media\"\n                  :src=\"source.src\"\n                  :type=\"source.type\"\n                />\n              </template>\n              <img\n                :srcset=\"\n                  overlayItem.srcSet ? overlayItem.srcSet : overlayItem.src\n                \"\n                :src=\"overlayItem.src\"\n                :alt=\"overlayItem.alt\"\n                width=\"auto\"\n                height=\"auto\"\n              />\n            </picture>\n          </div>\n          <p\n            class=\"silentbox-overlay__description\"\n            v-if=\"overlayItem.description\"\n          >\n            {{ overlayItem.description }}\n          </p>\n        </div>\n      </div>\n    </transition>\n\n    <div class=\"silentbox-overlay__buttons\" v-if=\"totalItems > 1\">\n      <button\n        class=\"silentbox-overlay__buttons-arrow silentbox-overlay__buttons-arrow--previous\"\n        @click.stop=\"moveToPreviousItem\"\n      >\n        <i class=\"arrow-icon\" />\n      </button>\n      <button\n        class=\"silentbox-overlay__buttons-arrow silentbox-overlay__buttons-arrow--next\"\n        @click.stop=\"moveToNextItem\"\n      >\n        <i class=\"arrow-icon\" />\n      </button>\n    </div>\n  </div>\n</template>\n\n<script>\nimport itemMixim from './../mixins/item'\n\nexport default {\n  name: 'SilentboxOverlay',\n  mixins: [itemMixim],\n  props: {\n    overlayItem: {\n      type: Object,\n      default: () => ({\n        src: '',\n        srcSet: '',\n        sources: [],\n        description: '',\n      }),\n    },\n    visible: {\n      type: Boolean,\n      default: false,\n    },\n    totalItems: {\n      type: Number,\n      default: 1,\n    },\n  },\n  data() {\n    return {\n      touchHandling: {\n        posX: 0,\n        posY: 0,\n      },\n      animationName: 'silentbox-animation__swipe-left',\n    }\n  },\n  computed: {\n    /**\n     * Check if there are other sources of the image\n     */\n    isSources() {\n      return (\n        Array.isArray(this.overlayItem.sources) &&\n        this.overlayItem.sources.length\n      )\n    },\n    /**\n     * Get other image sources\n     */\n    getSources() {\n      return this.overlayItem.sources\n    },\n  },\n  created() {\n    // Listen to key events.\n    window.addEventListener('keyup', (event) => {\n      // Escape: 27\n      if (event.which === 27) {\n        this.closeSilentboxOverlay()\n      }\n      // Right arrow: 39\n      if (event.which === 39) {\n        this.moveToNextItem()\n      }\n      // Left arrow: 37\n      if (event.which === 37) {\n        this.moveToPreviousItem()\n      }\n    })\n\n    // Disable browser scrolling.\n    this.enableScrollLock()\n  },\n  methods: {\n    /**\n     * Registers the finger position on website so we can later calculate users\n     * swipe direction.\n     */\n    touchStart(event) {\n      const { clientX: x, clientY: y } = event.touches[0]\n      this.touchHandling.posX = x\n      this.touchHandling.posY = y\n    },\n    /**\n     * Handles touch movement events, at the moment only swipe left and right\n     * are supported, but later could be extended with up and down swipes.\n     * It should be good to implement some kind of minimal swipe lenght support.\n     */\n    touchMove(event) {\n      const { clientX: x, clientY: y } = event.touches[0]\n      const { posX, posY } = this.touchHandling\n\n      if (posX === 0 || posY === 0) {\n        return\n      }\n\n      const xDiff = posX - x\n      const yDiff = posY - y\n\n      if (Math.abs(xDiff) > Math.abs(yDiff)) {\n        if (xDiff > 0) {\n          // left\n          this.moveToNextItem()\n        } else {\n          // right\n          this.moveToPreviousItem()\n        }\n      } else {\n        if (yDiff > 0) {\n          // up\n        } else {\n          // down\n          // this.closeSilentboxOverlay()\n        }\n      }\n\n      // reset\n      this.touchHandling.posX = 0\n      this.touchHandling.posY = 0\n    },\n    /**\n     * This method enables browser scrolling lock which prevent from horizontal\n     * and vertical scrolling. This makes touch navigation less confusing.\n     */\n    enableScrollLock() {\n      if (!document.body.classList.contains('silentbox-is-opened')) {\n        return document.body.classList.add('silentbox-is-opened')\n      }\n    },\n    /**\n     * This method removes browser scrolling lock.\n     */\n    removeScrollLock() {\n      if (document.body.classList.contains('silentbox-is-opened')) {\n        return document.body.classList.remove('silentbox-is-opened')\n      }\n    },\n    /**\n     * Move to next item.\n     */\n    moveToNextItem() {\n      this.animationName = 'silentbox-animation__swipe-left'\n      this.$emit('requestNextSilentBoxItem')\n    },\n    /**\n     * Move to previous item.\n     */\n    moveToPreviousItem() {\n      this.animationName = 'silentbox-animation__swipe-right'\n      this.$emit('requestPreviousSilentBoxItem')\n    },\n    /**\n     * Hide silentbox overlay.\n     */\n    closeSilentboxOverlay() {\n      this.removeScrollLock()\n      this.$emit('closeSilentboxOverlay')\n    },\n    /**\n     * Search for known video services URLs and return their players if recognized.\n     * Unrecognized URLs are handled as images.\n     *\n     * @param  {string} url\n     * @return {string}\n     */\n    handleUrl(url) {\n      if (url.includes('youtube.com') || url.includes('youtu.be')) {\n        return this.parseYoutubeVideo(url)\n      } else if (url.includes('vimeo')) {\n        return this.parseVimeoVideo(url)\n      }\n      return url\n    },\n    /**\n     * Get embed URL for youtube.com\n     *\n     * @param  {string} url\n     * @return {string}\n     */\n    parseYoutubeVideo(url) {\n      let videoUrl = ''\n      const videoId = this.getYoutubeVideoId(url)\n\n      if (videoId) {\n        videoUrl = `${location.protocol}//www.youtube.com/embed/${videoId}?rel=0`\n\n        if (this.overlayItem.autoplay) {\n          videoUrl += '&autoplay=1'\n        }\n        if (!this.overlayItem.controls) {\n          videoUrl += '&controls=0'\n        }\n      }\n\n      return videoUrl\n    },\n    /**\n     * Get embed URL for vimeo.com\n     *\n     * @param  {string} url\n     * @return {string}\n     */\n    parseVimeoVideo(url) {\n      let videoUrl = ''\n      const vimoId = /(vimeo(pro)?\\.com)\\/(?:[^\\d]+)?(\\d+)\\??(.*)?$/.exec(\n        url\n      )[3]\n\n      if (vimoId !== undefined) {\n        videoUrl = `${location.protocol}//player.vimeo.com/video/${vimoId}?rel=0`\n        if (this.overlayItem.autoplay === 'autoplay') {\n          videoUrl += '&autoplay=1'\n        }\n      }\n\n      return videoUrl\n    },\n  },\n}\n</script>\n\n<style lang=\"scss\">\n// Colours used in silentbox\n$main: #fff;\n$accent: #58e8d2;\n$bg: #000;\n\n.silentbox-is-opened {\n  overflow: hidden;\n}\n\n.silentbox-overlay {\n  position: fixed;\n  left: 0;\n  top: 0;\n  z-index: 999;\n  display: block;\n  width: 100vw;\n  height: 100vh;\n\n  &__picture {\n    cursor: pointer;\n  }\n\n  &__frame {\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    width: 100%;\n    height: 100%;\n  }\n\n  &__background {\n    position: absolute;\n    left: 0;\n    top: 0;\n    display: block;\n    width: 100%;\n    height: 100%;\n    background: rgba($bg, 0.75);\n    backdrop-filter: blur(20px);\n  }\n\n  &__content {\n    position: relative;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    width: 100%;\n    height: 100%;\n  }\n\n  &__embed {\n    width: 75%;\n    height: 80%;\n\n    img,\n    iframe {\n      position: absolute;\n      left: 0;\n      right: 0;\n      top: 0;\n      bottom: 0;\n      display: block;\n      margin: auto;\n      max-width: 100%;\n      max-height: 100%;\n      box-shadow: 0 0 1.5rem rgba($bg, 0.45);\n      background-color: $bg;\n    }\n\n    &:active,\n    &:focus,\n    &:hover {\n      outline: none;\n    }\n  }\n\n  &__container {\n    position: relative;\n    margin: 0;\n    width: 100%;\n    height: 100%;\n    text-align: center;\n  }\n\n  &__description {\n    display: block;\n    padding-top: 1rem;\n    text-align: center;\n    color: $main;\n  }\n\n  &__buttons {\n    &-arrow {\n      position: absolute;\n      top: 0;\n      display: flex;\n      align-items: center;\n      justify-content: center;\n      width: 6%;\n      height: 100%;\n      transition: 0.3s;\n      cursor: pointer;\n\n      .arrow-icon {\n        border-top: 2px solid $main;\n        border-left: 2px solid $main;\n        width: 1.5rem;\n        height: 1.5rem;\n      }\n\n      &:hover {\n        background: rgba($bg, 0.2);\n      }\n\n      &--previous {\n        left: 0;\n\n        .arrow-icon {\n          transform: rotate(-45deg);\n        }\n\n        &:hover .arrow-icon,\n        &:focus .arrow-icon {\n          opacity: 0.7;\n          animation-name: pulsingPrevious;\n          animation-duration: 1s;\n          animation-iteration-count: infinite;\n        }\n      }\n\n      &--next {\n        right: 0;\n\n        .arrow-icon {\n          transform: rotate(135deg);\n        }\n\n        &:hover .arrow-icon,\n        &:focus .arrow-icon {\n          opacity: 0.7;\n          animation-name: pulsingNext;\n          animation-duration: 1s;\n          animation-iteration-count: infinite;\n        }\n      }\n\n      @media (max-width: 1199px) {\n        width: 12%;\n      }\n    }\n  }\n\n  &__close {\n    position: absolute;\n    right: 7%;\n    top: 16px;\n    z-index: 2000;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    width: 2.5em;\n    height: 2.5em;\n    color: $main;\n    cursor: pointer;\n\n    .icon {\n      display: flex;\n      align-items: center;\n      justify-content: center;\n\n      &::before {\n        transform: rotate(-45deg);\n      }\n\n      &::before,\n      &::after {\n        content: '';\n        position: absolute;\n        width: 100%;\n        height: 2.5px;\n        background: $main;\n      }\n\n      &::after {\n        transform: rotate(45deg);\n      }\n    }\n\n    &:hover {\n      opacity: 0.75;\n    }\n\n    @media (max-width: 1199px) {\n      width: 1.8em;\n      height: 1.8em;\n    }\n\n    @media (max-width: 1199px) {\n      right: 13%;\n    }\n  }\n\n  // Transitions\n  .silentbox-animation__swipe-left-enter-active {\n    opacity: 0;\n    transform: translateX(25vw);\n    transition: all 0.3s ease;\n  }\n\n  .silentbox-animation__swipe-left-leave-active {\n    transition: all 0.3s ease;\n  }\n\n  .silentbox-animation__swipe-left-enter-to {\n    opacity: 1;\n    transform: translateX(0);\n    transition: all 0.3s ease;\n  }\n\n  .silentbox-animation__swipe-left-leave-to {\n    opacity: 0;\n    transform: translateX(-25vw);\n    transition: all 0.3s ease;\n  }\n\n  .silentbox-animation__swipe-right-enter-active {\n    opacity: 0;\n    transform: translateX(-25vw);\n    transition: all 0.3s ease;\n  }\n\n  .silentbox-animation__swipe-right-leave-active {\n    transition: all 0.3s ease;\n  }\n\n  .silentbox-animation__swipe-right-enter-to {\n    opacity: 1;\n    transform: translateX(0);\n    transition: all 0.3s ease;\n  }\n\n  .silentbox-animation__swipe-right-leave-to {\n    opacity: 0;\n    transform: translateX(25vw);\n    transition: all 0.3s ease;\n  }\n\n  // Animations\n  @keyframes pulsingNext {\n    0% {\n      margin-right: 0;\n      animation-timing-function: ease-in;\n    }\n\n    25% {\n      margin-right: 0.25rem;\n      animation-timing-function: ease-in;\n    }\n\n    50% {\n      margin-right: 0.5rem;\n      animation-timing-function: ease-in;\n    }\n\n    75% {\n      margin-right: 0.25rem;\n      animation-timing-function: ease-in;\n    }\n\n    100% {\n      margin-right: 0;\n      animation-timing-function: ease-in;\n    }\n  }\n\n  @keyframes pulsingPrevious {\n    0% {\n      margin-left: 0;\n      animation-timing-function: ease-in;\n    }\n\n    25% {\n      margin-left: 0.25rem;\n      animation-timing-function: ease-in;\n    }\n\n    50% {\n      margin-left: 0.5rem;\n      animation-timing-function: ease-in;\n    }\n\n    75% {\n      margin-left: 0.25rem;\n      animation-timing-function: ease-in;\n    }\n\n    100% {\n      margin-left: 0;\n      animation-timing-function: ease-in;\n    }\n  }\n}\n</style>\n",".silentbox-is-opened {\n  overflow: hidden;\n}\n\n.silentbox-overlay {\n  position: fixed;\n  left: 0;\n  top: 0;\n  z-index: 999;\n  display: block;\n  width: 100vw;\n  height: 100vh;\n}\n.silentbox-overlay__picture {\n  cursor: pointer;\n}\n.silentbox-overlay__frame {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  width: 100%;\n  height: 100%;\n}\n.silentbox-overlay__background {\n  position: absolute;\n  left: 0;\n  top: 0;\n  display: block;\n  width: 100%;\n  height: 100%;\n  background: rgba(0, 0, 0, 0.75);\n  backdrop-filter: blur(20px);\n}\n.silentbox-overlay__content {\n  position: relative;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  width: 100%;\n  height: 100%;\n}\n.silentbox-overlay__embed {\n  width: 75%;\n  height: 80%;\n}\n.silentbox-overlay__embed img,\n.silentbox-overlay__embed iframe {\n  position: absolute;\n  left: 0;\n  right: 0;\n  top: 0;\n  bottom: 0;\n  display: block;\n  margin: auto;\n  max-width: 100%;\n  max-height: 100%;\n  box-shadow: 0 0 1.5rem rgba(0, 0, 0, 0.45);\n  background-color: #000;\n}\n.silentbox-overlay__embed:active, .silentbox-overlay__embed:focus, .silentbox-overlay__embed:hover {\n  outline: none;\n}\n.silentbox-overlay__container {\n  position: relative;\n  margin: 0;\n  width: 100%;\n  height: 100%;\n  text-align: center;\n}\n.silentbox-overlay__description {\n  display: block;\n  padding-top: 1rem;\n  text-align: center;\n  color: #fff;\n}\n.silentbox-overlay__buttons-arrow {\n  position: absolute;\n  top: 0;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  width: 6%;\n  height: 100%;\n  transition: 0.3s;\n  cursor: pointer;\n}\n.silentbox-overlay__buttons-arrow .arrow-icon {\n  border-top: 2px solid #fff;\n  border-left: 2px solid #fff;\n  width: 1.5rem;\n  height: 1.5rem;\n}\n.silentbox-overlay__buttons-arrow:hover {\n  background: rgba(0, 0, 0, 0.2);\n}\n.silentbox-overlay__buttons-arrow--previous {\n  left: 0;\n}\n.silentbox-overlay__buttons-arrow--previous .arrow-icon {\n  transform: rotate(-45deg);\n}\n.silentbox-overlay__buttons-arrow--previous:hover .arrow-icon, .silentbox-overlay__buttons-arrow--previous:focus .arrow-icon {\n  opacity: 0.7;\n  animation-name: pulsingPrevious;\n  animation-duration: 1s;\n  animation-iteration-count: infinite;\n}\n.silentbox-overlay__buttons-arrow--next {\n  right: 0;\n}\n.silentbox-overlay__buttons-arrow--next .arrow-icon {\n  transform: rotate(135deg);\n}\n.silentbox-overlay__buttons-arrow--next:hover .arrow-icon, .silentbox-overlay__buttons-arrow--next:focus .arrow-icon {\n  opacity: 0.7;\n  animation-name: pulsingNext;\n  animation-duration: 1s;\n  animation-iteration-count: infinite;\n}\n@media (max-width: 1199px) {\n  .silentbox-overlay__buttons-arrow {\n    width: 12%;\n  }\n}\n.silentbox-overlay__close {\n  position: absolute;\n  right: 7%;\n  top: 16px;\n  z-index: 2000;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  width: 2.5em;\n  height: 2.5em;\n  color: #fff;\n  cursor: pointer;\n}\n.silentbox-overlay__close .icon {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n.silentbox-overlay__close .icon::before {\n  transform: rotate(-45deg);\n}\n.silentbox-overlay__close .icon::before, .silentbox-overlay__close .icon::after {\n  content: \"\";\n  position: absolute;\n  width: 100%;\n  height: 2.5px;\n  background: #fff;\n}\n.silentbox-overlay__close .icon::after {\n  transform: rotate(45deg);\n}\n.silentbox-overlay__close:hover {\n  opacity: 0.75;\n}\n@media (max-width: 1199px) {\n  .silentbox-overlay__close {\n    width: 1.8em;\n    height: 1.8em;\n  }\n}\n@media (max-width: 1199px) {\n  .silentbox-overlay__close {\n    right: 13%;\n  }\n}\n.silentbox-overlay .silentbox-animation__swipe-left-enter-active {\n  opacity: 0;\n  transform: translateX(25vw);\n  transition: all 0.3s ease;\n}\n.silentbox-overlay .silentbox-animation__swipe-left-leave-active {\n  transition: all 0.3s ease;\n}\n.silentbox-overlay .silentbox-animation__swipe-left-enter-to {\n  opacity: 1;\n  transform: translateX(0);\n  transition: all 0.3s ease;\n}\n.silentbox-overlay .silentbox-animation__swipe-left-leave-to {\n  opacity: 0;\n  transform: translateX(-25vw);\n  transition: all 0.3s ease;\n}\n.silentbox-overlay .silentbox-animation__swipe-right-enter-active {\n  opacity: 0;\n  transform: translateX(-25vw);\n  transition: all 0.3s ease;\n}\n.silentbox-overlay .silentbox-animation__swipe-right-leave-active {\n  transition: all 0.3s ease;\n}\n.silentbox-overlay .silentbox-animation__swipe-right-enter-to {\n  opacity: 1;\n  transform: translateX(0);\n  transition: all 0.3s ease;\n}\n.silentbox-overlay .silentbox-animation__swipe-right-leave-to {\n  opacity: 0;\n  transform: translateX(25vw);\n  transition: all 0.3s ease;\n}\n@keyframes pulsingNext {\n  0% {\n    margin-right: 0;\n    animation-timing-function: ease-in;\n  }\n  25% {\n    margin-right: 0.25rem;\n    animation-timing-function: ease-in;\n  }\n  50% {\n    margin-right: 0.5rem;\n    animation-timing-function: ease-in;\n  }\n  75% {\n    margin-right: 0.25rem;\n    animation-timing-function: ease-in;\n  }\n  100% {\n    margin-right: 0;\n    animation-timing-function: ease-in;\n  }\n}\n@keyframes pulsingPrevious {\n  0% {\n    margin-left: 0;\n    animation-timing-function: ease-in;\n  }\n  25% {\n    margin-left: 0.25rem;\n    animation-timing-function: ease-in;\n  }\n  50% {\n    margin-left: 0.5rem;\n    animation-timing-function: ease-in;\n  }\n  75% {\n    margin-left: 0.25rem;\n    animation-timing-function: ease-in;\n  }\n  100% {\n    margin-left: 0;\n    animation-timing-function: ease-in;\n  }\n}\n\n/*# sourceMappingURL=overlay.vue.map */"]}, media: undefined });

    };
    /* scoped */
    const __vue_scope_id__ = undefined;
    /* module identifier */
    const __vue_module_identifier__ = undefined;
    /* functional template */
    const __vue_is_functional_template__ = false;
    /* style inject SSR */
    
    /* style inject shadow dom */
    

    
    const __vue_component__ = /*#__PURE__*/normalizeComponent(
      { render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ },
      __vue_inject_styles__,
      __vue_script__,
      __vue_scope_id__,
      __vue_is_functional_template__,
      __vue_module_identifier__,
      false,
      createInjector,
      undefined,
      undefined
    );

  //
  var script$1 = {
    name: 'silentboxGallery',
    mixins: [itemMixin],
    props: {
      lazyLoading: {
        type: Boolean,
        default: true
      },
      previewCount: {
        type: Number,
        default: null
      },
      gallery: {
        type: Array,
        default: () => []
      },
      image: {
        type: Object,
        default: () => ({
          src: '',
          alt: '',
          sources: [],
          thumbnailWidth: 'auto',
          thumbnailHeight: 'auto',
          thumbnail: '',
          thumbnailSources: [],
          autoplay: false,
          controls: true,
          description: ''
        })
      }
    },
    components: {
      'silentbox-overlay': __vue_component__
    },
    data: () => ({
      overlay: {
        item: {
          src: '',
          alt: '',
          sources: [],
          thumbnailWidth: 'auto',
          thumbnailHeight: 'auto',
          thumbnail: '',
          thumbnailSources: [],
          autoplay: false,
          controls: true,
          description: ''
        },
        visible: false,
        currentItem: 0
      }
    }),
    computed: {
      totalItems() {
        return this.gallery.length || 1;
      },

      previewGallery() {
        if (Number.isInteger(this.previewCount)) {
          return this.gallery.slice(0, this.previewCount).map(item => {
            return { ...this.overlay.item,
              ...item,
              thumbnail: this.setThumbnail(item),
              autoplay: this.setAutoplay(item)
            };
          });
        }

        return this.galleryItems;
      },

      galleryItems() {
        if (this.gallery.length > 0) {
          return this.gallery.map(item => {
            return { ...this.overlay.item,
              ...item,
              thumbnail: this.setThumbnail(item),
              autoplay: this.setAutoplay(item)
            };
          });
        }

        return [{ ...this.overlay.item,
          ...this.image,
          thumbnail: this.setThumbnail(this.image)
        }];
      }

    },
    methods: {
      isSources(image) {
        return this.isNotEmptyThumbnailSources(image) || this.isNotEmptySources(image);
      },

      isNotEmptyThumbnailSources(image) {
        return Array.isArray(image.thumbnailSources) && image.thumbnailSources.length;
      },

      isNotEmptySources(image) {
        return Array.isArray(image.sources) && image.sources.length;
      },

      getSources(image) {
        return this.isNotEmptyThumbnailSources(image) ? image.thumbnailSources : image.sources;
      },

      openOverlay(image, index = 0) {
        this.overlay.visible = true;
        this.overlay.item = image;
        this.overlay.currentItem = index;
        this.$emit('silentbox-overlay-opened', {
          item: image
        });
      },

      hideOverlay() {
        this.overlay.visible = false;
        this.$emit('silentbox-overlay-hidden', {
          item: this.overlay.item
        });
      },

      showNextItem() {
        let newItemIndex = this.overlay.currentItem + 1;
        newItemIndex = newItemIndex <= this.galleryItems.length - 1 ? newItemIndex : 0;
        this.overlay.item = this.galleryItems[newItemIndex];
        this.overlay.currentItem = newItemIndex;
        this.$emit('silentbox-overlay-next-item-displayed', {
          item: this.overlay.item
        });
      },

      showPreviousItem() {
        let newItemIndex = this.overlay.currentItem - 1;
        newItemIndex = newItemIndex > -1 ? newItemIndex : this.galleryItems.length - 1;
        this.overlay.item = this.galleryItems[newItemIndex];
        this.overlay.currentItem = newItemIndex;
        this.$emit('silentbox-overlay-previous-item-displayed', {
          item: this.overlay.item
        });
      },

      setAutoplay(item) {
        return item.autoplay ? 'autoplay' : '';
      },

      async setThumbnail(item) {
        if (this.isEmbedVideo(item.src) && item.thumbnail === undefined) {
          return await this.getThumbnail(item.src);
        }

        return item.thumbnail || item.src;
      }

    }
  };

  /* script */
  const __vue_script__$1 = script$1;

  /* template */
  var __vue_render__$1 = function() {
    var _vm = this;
    var _h = _vm.$createElement;
    var _c = _vm._self._c || _h;
    return _c(
      "section",
      { attrs: { id: "silentbox-gallery" } },
      [
        _vm._t("default"),
        _vm._v(" "),
        _vm._l(_vm.previewGallery, function(image, index) {
          return _c(
            "div",
            {
              key: image.src,
              staticClass: "silentbox-item",
              on: {
                click: function($event) {
                  return _vm.openOverlay(image, index)
                }
              }
            },
            [
              _vm._t(
                "silentbox-item",
                [
                  _c(
                    "picture",
                    [
                      _vm.isSources(image)
                        ? _vm._l(_vm.getSources(image), function(source) {
                            return _c("source", {
                              key: source.src,
                              attrs: {
                                srcset: source.srcSet,
                                media: source.media,
                                src: source.src,
                                type: source.type
                              }
                            })
                          })
                        : _vm._e(),
                      _vm._v(" "),
                      _c("img", {
                        attrs: {
                          loading: _vm.lazyLoading ? "lazy" : "eager",
                          src: image.thumbnail,
                          alt: image.alt,
                          width: image.thumbnailWidth,
                          height: image.thumbnailHeight
                        }
                      })
                    ],
                    2
                  )
                ],
                { silentboxItem: image }
              )
            ],
            2
          )
        }),
        _vm._v(" "),
        _vm.overlay.visible
          ? _c("silentbox-overlay", {
              attrs: {
                "overlay-item": _vm.overlay.item,
                visible: _vm.overlay.visible,
                "total-items": _vm.totalItems
              },
              on: {
                closeSilentboxOverlay: _vm.hideOverlay,
                requestNextSilentBoxItem: _vm.showNextItem,
                requestPreviousSilentBoxItem: _vm.showPreviousItem
              }
            })
          : _vm._e()
      ],
      2
    )
  };
  var __vue_staticRenderFns__$1 = [];
  __vue_render__$1._withStripped = true;

    /* style */
    const __vue_inject_styles__$1 = function (inject) {
      if (!inject) return
      inject("data-v-f1fdbc9a_0", { source: ".silentbox-item {\n  display: inline-block;\n  text-decoration: underline;\n  cursor: pointer;\n}\n\n/*# sourceMappingURL=gallery.vue.map */", map: {"version":3,"sources":["N:\\Projects\\silentbox\\src\\components\\gallery.vue","gallery.vue"],"names":[],"mappings":"AA2MA;EACA,qBAAA;EACA,0BAAA;EACA,eAAA;AC1MA;;AAEA,sCAAsC","file":"gallery.vue","sourcesContent":["<template>\n  <section id=\"silentbox-gallery\">\n    <slot />\n    <div\n      v-for=\"(image, index) in previewGallery\"\n      :key=\"image.src\"\n      @click=\"openOverlay(image, index)\"\n      class=\"silentbox-item\"\n    >\n      <slot name=\"silentbox-item\" v-bind:silentboxItem=\"image\">\n        <picture>\n          <template v-if=\"isSources(image)\">\n            <source\n              v-for=\"source in getSources(image)\"\n              :key=\"source.src\"\n              :srcset=\"source.srcSet\"\n              :media=\"source.media\"\n              :src=\"source.src\"\n              :type=\"source.type\"\n            />\n          </template>\n          <img\n            :loading=\"lazyLoading ? 'lazy' : 'eager'\"\n            :src=\"image.thumbnail\"\n            :alt=\"image.alt\"\n            :width=\"image.thumbnailWidth\"\n            :height=\"image.thumbnailHeight\"\n          />\n        </picture>\n      </slot>\n    </div>\n    <silentbox-overlay\n      v-if=\"overlay.visible\"\n      :overlay-item=\"overlay.item\"\n      :visible=\"overlay.visible\"\n      :total-items=\"totalItems\"\n      @closeSilentboxOverlay=\"hideOverlay\"\n      @requestNextSilentBoxItem=\"showNextItem\"\n      @requestPreviousSilentBoxItem=\"showPreviousItem\"\n    />\n  </section>\n</template>\n\n<script>\nimport overlay from './overlay.vue'\nimport itemMixin from './../mixins/item'\n\nexport default {\n  name: 'silentboxGallery',\n  mixins: [itemMixin],\n  props: {\n    lazyLoading: {\n      type: Boolean,\n      default: true,\n    },\n    previewCount: {\n      type: Number,\n      default: null,\n    },\n    gallery: {\n      type: Array,\n      default: () => [],\n    },\n    image: {\n      type: Object,\n      default: () => ({\n        src: '',\n        alt: '',\n        sources: [],\n        thumbnailWidth: 'auto',\n        thumbnailHeight: 'auto',\n        thumbnail: '',\n        thumbnailSources: [],\n        autoplay: false,\n        controls: true,\n        description: '',\n      }),\n    },\n  },\n  components: {\n    'silentbox-overlay': overlay,\n  },\n  data: () => ({\n    overlay: {\n      item: {\n        src: '',\n        alt: '',\n        sources: [],\n        thumbnailWidth: 'auto',\n        thumbnailHeight: 'auto',\n        thumbnail: '',\n        thumbnailSources: [],\n        autoplay: false,\n        controls: true,\n        description: '',\n      },\n      visible: false,\n      currentItem: 0,\n    },\n  }),\n  computed: {\n    totalItems() {\n      return this.gallery.length || 1\n    },\n    previewGallery() {\n      if (Number.isInteger(this.previewCount)) {\n        return this.gallery.slice(0, this.previewCount).map((item) => {\n          return {\n            ...this.overlay.item,\n            ...item,\n            thumbnail: this.setThumbnail(item),\n            autoplay: this.setAutoplay(item),\n          }\n        })\n      }\n      return this.galleryItems\n    },\n    galleryItems() {\n      if (this.gallery.length > 0) {\n        return this.gallery.map((item) => {\n          return {\n            ...this.overlay.item,\n            ...item,\n            thumbnail: this.setThumbnail(item),\n            autoplay: this.setAutoplay(item),\n          }\n        })\n      }\n      return [\n        {\n          ...this.overlay.item,\n          ...this.image,\n          thumbnail: this.setThumbnail(this.image),\n        },\n      ]\n    },\n  },\n  methods: {\n    isSources(image) {\n      return (\n        this.isNotEmptyThumbnailSources(image) || this.isNotEmptySources(image)\n      )\n    },\n    isNotEmptyThumbnailSources(image) {\n      return (\n        Array.isArray(image.thumbnailSources) && image.thumbnailSources.length\n      )\n    },\n    isNotEmptySources(image) {\n      return Array.isArray(image.sources) && image.sources.length\n    },\n    getSources(image) {\n      return this.isNotEmptyThumbnailSources(image)\n        ? image.thumbnailSources\n        : image.sources\n    },\n    openOverlay(image, index = 0) {\n      this.overlay.visible = true\n      this.overlay.item = image\n      this.overlay.currentItem = index\n      this.$emit('silentbox-overlay-opened', { item: image })\n    },\n    hideOverlay() {\n      this.overlay.visible = false\n      this.$emit('silentbox-overlay-hidden', { item: this.overlay.item })\n    },\n    showNextItem() {\n      let newItemIndex = this.overlay.currentItem + 1\n      newItemIndex =\n        newItemIndex <= this.galleryItems.length - 1 ? newItemIndex : 0\n\n      this.overlay.item = this.galleryItems[newItemIndex]\n      this.overlay.currentItem = newItemIndex\n      this.$emit('silentbox-overlay-next-item-displayed', {\n        item: this.overlay.item,\n      })\n    },\n    showPreviousItem() {\n      let newItemIndex = this.overlay.currentItem - 1\n      newItemIndex =\n        newItemIndex > -1 ? newItemIndex : this.galleryItems.length - 1\n\n      this.overlay.item = this.galleryItems[newItemIndex]\n      this.overlay.currentItem = newItemIndex\n      this.$emit('silentbox-overlay-previous-item-displayed', {\n        item: this.overlay.item,\n      })\n    },\n    setAutoplay(item) {\n      return item.autoplay ? 'autoplay' : ''\n    },\n    async setThumbnail(item) {\n      if (this.isEmbedVideo(item.src) && item.thumbnail === undefined) {\n        return await this.getThumbnail(item.src)\n      }\n\n      return item.thumbnail || item.src\n    },\n  },\n}\n</script>\n\n<style lang=\"scss\">\n.silentbox-item {\n  display: inline-block;\n  text-decoration: underline;\n  cursor: pointer;\n}\n</style>\n",".silentbox-item {\n  display: inline-block;\n  text-decoration: underline;\n  cursor: pointer;\n}\n\n/*# sourceMappingURL=gallery.vue.map */"]}, media: undefined });

    };
    /* scoped */
    const __vue_scope_id__$1 = undefined;
    /* module identifier */
    const __vue_module_identifier__$1 = undefined;
    /* functional template */
    const __vue_is_functional_template__$1 = false;
    /* style inject SSR */
    
    /* style inject shadow dom */
    

    
    const __vue_component__$1 = /*#__PURE__*/normalizeComponent(
      { render: __vue_render__$1, staticRenderFns: __vue_staticRenderFns__$1 },
      __vue_inject_styles__$1,
      __vue_script__$1,
      __vue_scope_id__$1,
      __vue_is_functional_template__$1,
      __vue_module_identifier__$1,
      false,
      createInjector,
      undefined,
      undefined
    );

  const VueSilentbox = {};

  VueSilentbox.install = function (Vue) {
    Vue.mixin({
      components: {
        'silent-box': __vue_component__$1
      }
    });
    Vue.prototype.$silentbox = {
      /**
       * Programatically open SilentBox overlay.
       *
       * @param {Object} image
       */
      open: image => {
        const OverlayClass = Vue.extend(__vue_component__);
        const instance = new OverlayClass().$mount();
        instance.$set(instance, 'visible', true);
        instance.$set(instance, 'overlayItem', image); // Get the website body, so we can append false silentbox root to it later

        const bodyRoot = document.getElementsByTagName('body')[0]; // Create false silentbox root

        const silentboxRoot = document.createElement('div'); // Set ID to false silentbox root, so we can target it in some cases

        silentboxRoot.setAttribute('id', 'silentbox--false-root'); // Register the elements

        silentboxRoot.appendChild(instance.$el);
        bodyRoot.appendChild(silentboxRoot);
        instance.$on('closeSilentboxOverlay', () => {
          // Remove and destroy the instance after closing.
          silentboxRoot.remove();
          instance.$destroy();
        });
      }
    };
  };

  exports.default = VueSilentbox;

  return exports;

}({}));