UNPKG

exarcheia-vue-silentbox

Version:

A simple lightbox inspired Vue.js component.

982 lines 61.8 kB
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();
      });
    }
  };
};

export default VueSilentbox;