/**
 * jBox Image plugin: Adds a lightbox to your images
 *
 * Author: Stephan Wagner <stephanwagner.me@gmail.com> (https://stephanwagner.me)
 *
 * License: MIT (https://opensource.org/licenses/MIT)
 *
 * Requires: jBox (https://cdn.jsdelivr.net/gh/StephanWagner/jBox@latest/dist/jBox.min.js)
 */

function jBoxImageWrapper(jBox, jQuery) {

  new jBox.plugin('Image', {


    // Options (https://stephanwagner.me/jBox/options#options-image)

    src: 'href',                 // The attribute where jBox gets the image source from, e.g. href="/path_to_image/image.jpg"
    gallery: 'data-jbox-image',  // The attribute to set the galleries, e.g. data-jbox-image="gallery1"
    imageLabel: 'title',         // The attribute where jBox gets the image label from, e.g. title="My label"
    imageFade: 360,              // The fade duration for images in ms
    imageSize: 'contain',        // How to display the images. Use CSS background-position values, e.g. 'cover', 'contain', 'auto', 'initial', '50% 50%'
    imageCounter: false,         // Set to true to add an image counter, e.g. 4/20
    imageCounterSeparator: '/',  // HTML to separate the current image number from all image numbers, e.g. '/' or ' of '
    downloadButton: false,       // Adds a download button
    downloadButtonText: null,    // Text for the download button
    downloadButtonUrl: null,     // The attribute at the source element where to find the image to download, e.g. data-download="/path_to_image/image.jpg". If none provided, the currently active image will be downloaded
    mobileImageAttr: null,       // The attribute to look for an mobile version of the image
    mobileImageBreakpoint: null, // The upper breakpoint to load the mobile image
    preloadFirstImage: false,    // Preload the first image when page is loaded
    target: window,
    attach: '[data-jbox-image]',
    fixed: true,
    blockScroll: true,
    closeOnEsc: true,
    closeOnClick: 'button',
    closeButton: true,
    overlay: true,
    animation: 'zoomIn',
    preventDefault: true,
    width: '100%',
    height: '100%',
    adjustDistance: {
      top: 40,
      right: 0,
      bottom: 40,
      left: 0
    },


    // Triggered when jBox is initialized

    _onInit: function ()
    {
      // Initial images and z-index
      this.images = this.currentImage = {};
      this.imageZIndex = 1;

      this.initImage = function (item) {
        item = jQuery(item);

        // Abort if the item was added to a gallery already
        if (item.data('jBox-image-gallery')) {
          return;
        }

        // Get the image src
        var src = item.attr(this.options.src);

        // Update responsive image src
        if (this.options.mobileImageAttr && this.options.mobileImageBreakpoint && item.attr(this.options.mobileImageAttr)) {
          if (jQuery(window).width() <= this.options.mobileImageBreakpoint) {
            src = item.attr(this.options.mobileImageAttr);
          }
        }

        // Add item to a gallery
        var gallery = item.attr(this.options.gallery) || 'default';
        !this.images[gallery] && (this.images[gallery] = []);
        this.images[gallery].push({
          src: src,
          label: (item.attr(this.options.imageLabel) || ''),
          downloadUrl: this.options.downloadButtonUrl && item.attr(this.options.downloadButtonUrl) ? item.attr(this.options.downloadButtonUrl) : null
        });

        // Remove the title attribute so it won't show the browsers tooltip
        this.options.imageLabel == 'title' && item.removeAttr('title');

        // Store data in source element for easy access
        item.data('jBox-image-gallery', gallery);
        item.data('jBox-image-id', (this.images[gallery].length - 1));
      }.bind(this);

      // Loop through images, sort and save in global variable
      this.attachedElements && this.attachedElements.length && jQuery.each(this.attachedElements, function (index, item) {
        this.initImage(item);
      }.bind(this));

      // Helper to inject the image into content area
      var appendImage = function (gallery, id, show, instant) {
        // Abort if image was appended already
        if (jQuery('#jBox-image-' + gallery + '-' + id).length) {
          return;
        }

        // Create image container
        var image = jQuery('<div/>', {
          id: 'jBox-image-' + gallery + '-' + id,
          'class': 'jBox-image-container' + (show ? ' jBox-image-' + gallery + '-current' : '')
        }).css({
          backgroundSize: this.options.imageSize,
          opacity: (instant ? 1 : 0),
          zIndex: (show ? this.imageZIndex++ : 0)
        }).appendTo(this.content);

        // Add swipe events
        this.swipeDetector(image)
          .on("swipeLeft.sd swipeRight.sd", function (event) {
            if (event.type === "swipeLeft") {
              this.showImage('next');
            } else if (event.type === "swipeRight") {
              this.showImage('prev');
            }
          }.bind(this));

        // Create labels
        jQuery('<div/>', {
          id: 'jBox-image-label-' + gallery + '-' + id,
          'class': 'jBox-image-label' + (show ? ' active' : '')
        })
        .html(this.images[gallery][id].label)
        .on('click tap', function () {
          jQuery(this).toggleClass('expanded');
        })
        .appendTo(this.imageLabelContainer);

        // Show image
        show && image.animate({opacity: 1}, instant ? 0 : this.options.imageFade);

        return image;
      }.bind(this);

      // Function to download an image
      this.downloadImage = function (imageUrl) {
        var link = document.createElement('a');
        link.href = imageUrl;
        link.setAttribute('download', imageUrl.substring(imageUrl.lastIndexOf('/')+1));
        document.body.appendChild(link);
        link.click();
      };

      // Helper to show new image label
      var showLabel = function (gallery, id) {
        jQuery('.jBox-image-label.active').removeClass('active expanded');
        jQuery('#jBox-image-label-' + gallery + '-' + id).addClass('active');
      };

      // Helper to load image
      var loadImage = function (gallery, id, show, instant) {
        var imageContainer = appendImage(gallery, id, show, instant);
        imageContainer.addClass('jBox-image-loading');

        jQuery('<img src="' + this.images[gallery][id].src + '" />').each(function () {
          var tmpImg = new Image();
          tmpImg.onload = function () {
            imageContainer.removeClass('jBox-image-loading');
            imageContainer.css({backgroundImage: 'url("' + this.images[gallery][id].src + '")'});
          }.bind(this);

          tmpImg.onerror = function () {
            imageContainer.removeClass('jBox-image-loading');
            imageContainer.addClass('jBox-image-not-found');
          }.bind(this);

          tmpImg.src = this.images[gallery][id].src;
        }.bind(this));
      }.bind(this);

      // Show images when they are loaded or load them if not
      this.showImage = function (img) {
        // Get the gallery and the image id from the next or the previous image
        if (img != 'open') {
          var gallery = this.currentImage.gallery;
          var id = this.currentImage.id + (1 * (img == 'prev') ? -1 : 1);
          id = id > (this.images[gallery].length - 1) ? 0 : (id < 0 ? (this.images[gallery].length - 1) : id);

        // Or get image data from source element
        } else {
          // Get gallery and image id from source element
          if (this.source) {
            var gallery = this.source.data('jBox-image-gallery');
            var id = this.source.data('jBox-image-id');

          // Get gallery and image id attached elements
          } else if (this.attachedElements && this.attachedElements.length) {
            var gallery = jQuery(this.attachedElements[0]).data('jBox-image-gallery');
            var id = jQuery(this.attachedElements[0]).data('jBox-image-id');
          } else {
            return;
          }

          // Remove or show the next and prev buttons
          jQuery('.jBox-image-pointer-prev, .jBox-image-pointer-next').css({display: (this.images[gallery].length > 1 ? 'block' : 'none')});
        }

        // If there is a current image already shown, hide it
        if (jQuery('.jBox-image-' + gallery + '-current').length) {
          jQuery('.jBox-image-' + gallery + '-current').removeClass('jBox-image-' + gallery + '-current').animate({opacity: 0}, (img == 'open') ? 0 : this.options.imageFade);
        }

        // Set new current image
        this.currentImage = {gallery: gallery, id: id};

        // Show image if it already exists
        if (jQuery('#jBox-image-' + gallery + '-' + id).length) {
          jQuery('#jBox-image-' + gallery + '-' + id).addClass('jBox-image-' + gallery + '-current').css({zIndex: this.imageZIndex++, opacity: 0}).animate({opacity: 1}, (img == 'open') ? 0 : this.options.imageFade);

        // Load image
        } else {
          loadImage(gallery, id, true, (img === 'open'));
        }

        // Show label
        showLabel(gallery, id);

        // Update the image counter numbers
        if (this.imageCounter) {
          if (this.images[gallery].length > 1) {
            this.wrapper.addClass('jBox-image-has-counter');
            this.imageCounter.find('.jBox-image-counter-all').html(this.images[gallery].length);
            this.imageCounter.find('.jBox-image-counter-current').html(id + 1);
          } else {
            this.wrapper.removeClass('jBox-image-has-counter');
          }
        }

        // Preload next image
        if (this.images[gallery].length - 1) {
	        var next_id = id + 1;
	        next_id = next_id > (this.images[gallery].length - 1) ? 0 : (next_id < 0 ? (this.images[gallery].length - 1) : next_id);

	        if (!jQuery('#jBox-image-' + gallery + '-' + next_id).length) {
            loadImage(gallery, next_id, false, false);
          }
	      }
      };

      // Preload image
      if (this.options.preloadFirstImage) {
        jQuery(window).on('load', function() {
          this.showImage('open');
        }.bind(this));
      }
    },


    // Triggered when jBox attaches a new element

    _onAttach: function (item) {
      this.initImage && this.initImage(item);
    },


    // Triggered when jBox was created

    _onCreated: function ()
    {
      // Create image label and navigation buttons
      this.imageLabelWrapper = jQuery('<div class="jBox-image-label-wrapper"/>').appendTo(this.wrapper);

      this.imagePrevButton = jQuery('<div class="jBox-image-pointer-prev"/>')
        .on('click tap', function () {
          this.showImage('prev');
        }.bind(this));

      this.imageNextButton = jQuery('<div class="jBox-image-pointer-next"/>')
        .on('click tap', function () {
          this.showImage('next');
        }.bind(this));

      this.imageLabelContainer = jQuery('<div class="jBox-image-label-container"/>');

      this.imageLabelWrapper
        .append(this.imagePrevButton)
        .append(this.imageLabelContainer)
        .append(this.imageNextButton);

      // Append the download button
      if (this.options.downloadButton) {
        this.downloadButton = jQuery('<div/>', {'class': 'jBox-image-download-button-wrapper'})
          .appendTo(this.wrapper)
          .append(
            this.options.downloadButtonText ? jQuery('<div/>', {'class': 'jBox-image-download-button-text'}).html(this.options.downloadButtonText) : null
          )
          .append(
            jQuery('<div/>', {'class': 'jBox-image-download-button-icon'})
          ).on('click tap', function () {
            if (this.images[this.currentImage.gallery][this.currentImage.id].downloadUrl) {
              var currentImageUrl = this.images[this.currentImage.gallery][this.currentImage.id].downloadUrl;
            } else {
              var currentImage = this.wrapper.find('.jBox-image-' + this.currentImage.gallery + '-current');
              var currentImageStyle = currentImage[0].style.backgroundImage;
              var currentImageUrl = currentImageStyle.slice(4, -1).replace(/["']/g, '');
            }
            this.downloadImage(currentImageUrl);
          }.bind(this));
      }

      // Creating the image counter containers
      if (this.options.imageCounter) {
        this.imageCounter = jQuery('<div/>', {'class': 'jBox-image-counter-container'}).insertAfter(this.imageLabelContainer);
        this.imageCounter.append(jQuery('<span/>', {'class': 'jBox-image-counter-current'})).append(jQuery('<span/>').html(this.options.imageCounterSeparator)).append(jQuery('<span/>', {'class': 'jBox-image-counter-all'}));
      }
    },


    // Triggered when jBox opens

    _onOpen: function ()
    {
      // Add key events
      jQuery(document).on('keyup.jBox-Image-' + this.id, function (ev) {
        (ev.keyCode == 37) && this.showImage('prev');
        (ev.keyCode == 39) && this.showImage('next');
      }.bind(this));

      // Load the image from the attached element
      this.showImage('open');
    },


    // Triggered when jBox closes

    _onClose: function ()
    {
      // Remove key events
      jQuery(document).off('keyup.jBox-Image-' + this.id);
    },


    // Triggered when jBox finished closing

    _onCloseComplete: function ()
    {
      // Hide all image containers
      this.wrapper.find('.jBox-image-container').css('opacity', 0);
    }

  });

};
