/* juxtapose - v1.2.0 - 2017-12-18
 * Copyright (c) 2017 Alex Duner and Northwestern University Knight Lab
 */
(function (document, window) {

  var juxtapose = {
    sliders: [],
    OPTIMIZATION_ACCEPTED: 1,
    OPTIMIZATION_WAS_CONSTRAINED: 2
  };

  var flickr_key = "d90fc2d1f4acc584e08b8eaea5bf4d6c";
  var FLICKR_SIZE_PREFERENCES = ['Large', 'Medium'];

  function Graphic(properties, slider) {
    var self = this;
    this.image = new Image();

    this.loaded = false;
    this.image.onload = function() {
      self.loaded = true;
      slider._onLoaded();
    };

    this.image.src = properties.src;
    this.image.alt = properties.alt || '';
    this.label = properties.label || false;
    this.credit = properties.credit || false;
  }

  function FlickrGraphic(properties, slider) {
    var self = this;
    this.image = new Image();

    this.loaded = false;
    this.image.onload = function() {
      self.loaded = true;
      slider._onLoaded();
    };

    this.flickrID = this.getFlickrID(properties.src);
    this.callFlickrAPI(this.flickrID, self);

    this.label = properties.label || false;
    this.credit = properties.credit || false;
  }

  FlickrGraphic.prototype = {
    getFlickrID: function(url) {
      if (url.match(/flic.kr\/.+/i)) {
        var encoded = url.split('/').slice(-1)[0];
        return base58Decode(encoded);
      }
      var idx = url.indexOf("flickr.com/photos/");
      var pos = idx + "flickr.com/photos/".length;
      var photo_info = url.substr(pos);
      if (photo_info.indexOf('/') == -1) return null;
      if (photo_info.indexOf('/') === 0) photo_info = photo_info.substr(1);
      id = photo_info.split("/")[1];
      return id;
    },

    callFlickrAPI: function(id, self) {
      var url = 'https://api.flickr.com/services/rest/?method=flickr.photos.getSizes' +
          '&api_key=' + flickr_key +
          '&photo_id=' + id + '&format=json&nojsoncallback=1';

      var request = new XMLHttpRequest();
      request.open('GET', url, true);
      request.onload = function() {
        if (request.status >= 200 && request.status < 400){
          data = JSON.parse(request.responseText);
          var flickr_url = self.bestFlickrUrl(data.sizes.size);
          self.setFlickrImage(flickr_url);
        } else {
          console.error("There was an error getting the picture from Flickr");
        }
      };
      request.onerror = function() {
        console.error("There was an error getting the picture from Flickr");
      };
      request.send();
    },

    setFlickrImage: function(src) {
      this.image.src = src;
    },

    bestFlickrUrl: function(ary) {
      var dict = {};
      for (var i = 0; i < ary.length; i++) {
        dict[ary[i].label] = ary[i].source;
      }
      for (var j = 0; j < FLICKR_SIZE_PREFERENCES.length; j++) {
        if (FLICKR_SIZE_PREFERENCES[j] in dict) {
          return dict[FLICKR_SIZE_PREFERENCES[j]];
        }
      }
      return ary[0].source;
    }
  };

  function getNaturalDimensions(DOMelement) {
    if (DOMelement.naturalWidth && DOMelement.naturalHeight) {
      return {width: DOMelement.naturalWidth, height: DOMelement.naturalHeight};
    }
    // http://www.jacklmoore.com/notes/naturalwidth-and-naturalheight-in-ie/
    var img = new Image();
    img.src = DOMelement.src;
    return {width: img.width, height: img.height};
  }

  function getImageDimensions(img) {
    var dimensions = {
      width: getNaturalDimensions(img).width,
      height: getNaturalDimensions(img).height,
      aspect: function() { return (this.width / this.height); }
    };
    return dimensions;
  }

  function addClass(element, c) {
    if (element.classList) {
      element.classList.add(c);
    } else {
      element.className += " " + c;
    }
  }

  function removeClass(element, c) {
    element.className = element.className.replace(/(\S+)\s*/g, function (w, match) {
      if (match === c) {
        return '';
      }
      return w;
    }).replace(/^\s+/, '');
  }

  function setText(element, text) {
    if (document.body.textContent) {
      element.textContent = text;
    } else {
      element.innerText = text;
    }
  }

  function getComputedWidthAndHeight(element) {
    if (window.getComputedStyle) {
      return {
        width: parseInt(getComputedStyle(element).width, 10),
        height: parseInt(getComputedStyle(element).height, 10)
      };
    } else {
      w = element.getBoundingClientRect().right - element.getBoundingClientRect().left;
      h = element.getBoundingClientRect().bottom - element.getBoundingClientRect().top;
      return {
        width: parseInt(w, 10) || 0,
        height: parseInt(h, 10) || 0
      };
    }
  }

  function viewport() {
    var e = window, a = 'inner';
    if ( !( 'innerWidth' in window ) ) {
      a = 'client';
      e = document.documentElement || document.body;
    }
    return { width : e[ a+'Width' ] , height : e[ a+'Height' ] }
  }

  function getPageX(e) {
    var pageX;
    if (e.pageX) {
      pageX = e.pageX;
    } else if (e.touches) {
      pageX = e.touches[0].pageX;
    } else {
      pageX = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
    }
    return pageX;
  }

  function getPageY(e) {
    var pageY;
    if (e.pageY) {
      pageY = e.pageY;
    } else if (e.touches) {
      pageY = e.touches[0].pageY;
    } else {
      pageY = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
    }
    return pageY;
  }

  function checkFlickr(url) {
    if (url.match(/flic.kr\/.+/i)) {
      return true;
    }
    var idx = url.indexOf("flickr.com/photos/");
    if (idx == -1) {
      return false;
    } else {
      return true;
    }
  }

  function base58Decode(encoded) {
    var alphabet = '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ',
        base = alphabet.length;
    if (typeof encoded !== 'string') {
      throw '"base58Decode" only accepts strings.';
    }
    var decoded = 0;
    while (encoded) {
      var alphabetPosition = alphabet.indexOf(encoded[0]);
      if (alphabetPosition < 0) {
        throw '"base58Decode" can\'t find "' + encoded[0] + '" in the alphabet: "' + alphabet + '"';
      }
      var powerOf = encoded.length - 1;
      decoded += alphabetPosition * (Math.pow(base, powerOf));
      encoded = encoded.substring(1);
    }
    return decoded.toString();
  }

  function getLeftPercent(slider, input) {
    if (typeof(input) === "string" || typeof(input) === "number") {
      leftPercent = parseInt(input, 10);
    } else {
      var sliderRect = slider.getBoundingClientRect();
      var offset = {
        top: sliderRect.top + document.body.scrollTop + document.documentElement.scrollTop,
        left: sliderRect.left + document.body.scrollLeft + document.documentElement.scrollLeft
      };
      var width = slider.offsetWidth;
      var pageX = getPageX(input);
      var relativeX = pageX - offset.left;
      leftPercent = (relativeX / width) * 100;
    }
    return leftPercent;
  }

  function getTopPercent(slider, input) {
    if (typeof(input) === "string" || typeof(input) === "number") {
      topPercent = parseInt(input, 10);
    } else {
      var sliderRect = slider.getBoundingClientRect();
      var offset = {
        top: sliderRect.top + document.body.scrollTop + document.documentElement.scrollTop,
        left: sliderRect.left + document.body.scrollLeft + document.documentElement.scrollLeft
      };
      var width = slider.offsetHeight;
      var pageY = getPageY(input);
      var relativeY = pageY - offset.top;
      topPercent = (relativeY / width) * 100;
    }
    return topPercent;
  }

  // values of BOOLEAN_OPTIONS are ignored. just used for 'in' test on keys
  var BOOLEAN_OPTIONS =  {'animate': true, 'showLabels': true, 'showCredits': true, 'makeResponsive': true };
  function interpret_boolean(x) {
    if (typeof(x) != 'string') {
      return Boolean(x);
    }
    return !(x === 'false' || x === '');
  }

  function JXSlider(selector, images, options) {

    this.selector = selector;

    var i;
    this.options = { // new options must have default values set here.
      animate: true,
      showLabels: true,
      showCredits: true,
      makeResponsive: true,
      startingPosition: "50%",
      mode: 'horizontal',
      callback: null // pass a callback function if you like
    };

    for (i in this.options) {
      if(i in options) {
        if (i in BOOLEAN_OPTIONS) {
          this.options[i] = interpret_boolean(options[i]);
        } else {
          this.options[i] = options[i];
        }
      }
    }

    if (images.length == 2) {

      if(checkFlickr(images[0].src)) {
        this.imgBefore = new FlickrGraphic(images[0], this);
      } else {
        this.imgBefore = new Graphic(images[0], this);
      }

      if(checkFlickr(images[1].src)) {
        this.imgAfter = new FlickrGraphic(images[1], this);
      } else {
        this.imgAfter = new Graphic(images[1], this);
      }

    } else {
      console.warn("The images parameter takes two Image objects.");
    }

    if (this.imgBefore.credit || this.imgAfter.credit) {
      this.options.showCredits = true;
    } else {
      this.options.showCredits = false;
    }
  }

  JXSlider.prototype = {

    updateSlider: function(input, animate) {
      var leftPercent, rightPercent;

      if (this.options.mode === "vertical") {
        leftPercent = getTopPercent(this.slider, input);
      } else {
        leftPercent = getLeftPercent(this.slider, input);
      }

      leftPercent = leftPercent.toFixed(2) + "%";
      leftPercentNum = parseFloat(leftPercent);
      rightPercent = (100 - leftPercentNum) + "%";

      if (leftPercentNum > 0 && leftPercentNum < 100) {
        removeClass(this.handle, 'transition');
        removeClass(this.rightImage, 'transition');
        removeClass(this.leftImage, 'transition');

        if (this.options.animate && animate) {
          addClass(this.handle, 'transition');
          addClass(this.leftImage, 'transition');
          addClass(this.rightImage, 'transition');
        }

        if (this.options.mode === "vertical") {
          this.handle.style.top = leftPercent;
          this.leftImage.style.height = leftPercent;
          this.rightImage.style.height = rightPercent;
        } else {
          this.handle.style.left = leftPercent;
          this.leftImage.style.width = leftPercent;
          this.rightImage.style.width = rightPercent;
        }
        this.sliderPosition = leftPercent;
      }
    },

    getPosition: function() {
      return this.sliderPosition;
    },

    displayLabel: function(element, labelText) {
      label = document.createElement("div");
      label.className = 'jx-label';
      label.setAttribute('tabindex', 0); //put the controller in the natural tab order of the document

      setText(label, labelText);
      element.appendChild(label);
    },

    displayCredits: function() {
      credit = document.createElement("div");
      credit.className = "jx-credit";

      text = "<em>Photo Credits:</em>";
      if (this.imgBefore.credit) { text += " <em>Before</em> " + this.imgBefore.credit; }
      if (this.imgAfter.credit) { text += " <em>After</em> " + this.imgAfter.credit; }

      credit.innerHTML = text;

      this.wrapper.appendChild(credit);
    },

    setStartingPosition: function(s) {
      this.options.startingPosition = s;
    },

    checkImages: function() {
      if (getImageDimensions(this.imgBefore.image).aspect() ==
        getImageDimensions(this.imgAfter.image).aspect()) {
        return true;
      } else {
        return false;
      }
    },

    calculateDims: function(width, height){
      var ratio = getImageDimensions(this.imgBefore.image).aspect();
      if (width) {
        height = width / ratio;
      } else if (height) {
        width = height * ratio;
      }
      return {
        width: width,
        height: height,
        ratio: ratio
      };
    },

    responsivizeIframe: function(dims){
      //Check the slider dimensions against the iframe (window) dimensions
      if (dims.height < window.innerHeight){
        //If the aspect ratio is greater than 1, imgs are landscape, so letterbox top and bottom
        if (dims.ratio >= 1){
          this.wrapper.style.paddingTop = parseInt((window.innerHeight - dims.height) / 2) + "px";
        }
      } else if (dims.height > window.innerHeight) {
        /* If the image is too tall for the window, which happens at 100% width on large screens,
         * force dimension recalculation based on height instead of width */
        dims = this.calculateDims(0, window.innerHeight);
        this.wrapper.style.paddingLeft = parseInt((window.innerWidth - dims.width) / 2) + "px";
      }
      if (this.options.showCredits) {
        // accommodate the credits box within the iframe
        dims.height -= 13;
      }
      return dims;
    },

    setWrapperDimensions: function() {
      var wrapperWidth = getComputedWidthAndHeight(this.wrapper).width;
      var wrapperHeight = getComputedWidthAndHeight(this.wrapper).height;
      var dims = this.calculateDims(wrapperWidth, wrapperHeight);
      // if window is in iframe, make sure images don't overflow boundaries
      if (window.location !== window.parent.location && !this.options.makeResponsive) {
        dims = this.responsivizeIframe(dims);
      }

      this.wrapper.style.height = parseInt(dims.height) + "px";
      this.wrapper.style.width = parseInt(dims.width) + "px";
    },

    optimizeWrapper: function(maxWidth){
      var result = juxtapose.OPTIMIZATION_ACCEPTED;
      if ((this.imgBefore.image.naturalWidth >= maxWidth) && (this.imgAfter.image.naturalWidth >= maxWidth)) {
        this.wrapper.style.width = maxWidth + "px";
        result = juxtapose.OPTIMIZATION_WAS_CONSTRAINED;
      } else if (this.imgAfter.image.naturalWidth < maxWidth) {
        this.wrapper.style.width = this.imgAfter.image.naturalWidth + "px";
      } else {
        this.wrapper.style.width = this.imgBefore.image.naturalWidth + "px";
      }
      this.setWrapperDimensions();
      return result;
    },

    _onLoaded: function() {

      if (this.imgBefore && this.imgBefore.loaded === true &&
        this.imgAfter && this.imgAfter.loaded === true) {

        this.wrapper = document.querySelector(this.selector);
        addClass(this.wrapper, 'juxtapose');

        this.wrapper.style.width = getNaturalDimensions(this.imgBefore.image).width;
        this.setWrapperDimensions();

        this.slider = document.createElement("div");
        this.slider.className = 'jx-slider';
        this.wrapper.appendChild(this.slider);

        if (this.options.mode != "horizontal") {
          addClass(this.slider, this.options.mode);
        }

        this.handle = document.createElement("div");
        this.handle.className = 'jx-handle';

        this.rightImage = document.createElement("div");
        this.rightImage.className = 'jx-image jx-right';
        this.rightImage.appendChild(this.imgAfter.image);


        this.leftImage = document.createElement("div");
        this.leftImage.className = 'jx-image jx-left';
        this.leftImage.appendChild(this.imgBefore.image);

        this.labCredit = document.createElement("a");
        this.labCredit.setAttribute('href', 'http://juxtapose.knightlab.com');
        this.labCredit.setAttribute('target', '_blank');
        this.labLogo = document.createElement("div");
        this.labLogo.className = 'knightlab-logo';
        this.labCredit.appendChild(this.labLogo);
        this.projectName = document.createElement("span");
        this.projectName.className = 'juxtapose-name';
        setText(this.projectName, 'JuxtaposeJS');
        this.labCredit.appendChild(this.projectName);

        this.slider.appendChild(this.handle);
        this.slider.appendChild(this.leftImage);
        this.slider.appendChild(this.rightImage);
        //this.slider.appendChild(this.labCredit);

        this.leftArrow = document.createElement("div");
        this.rightArrow = document.createElement("div");
        this.control = document.createElement("div");
        this.controller = document.createElement("div");

        this.leftArrow.className = 'jx-arrow jx-left';
        this.rightArrow.className = 'jx-arrow jx-right';
        this.control.className = 'jx-control';
        this.controller.className = 'jx-controller';

        this.controller.setAttribute('tabindex', 0); //put the controller in the natural tab order of the document
        this.controller.setAttribute('role', 'slider');
        this.controller.setAttribute('aria-valuenow', 50);
        this.controller.setAttribute('aria-valuemin', 0);
        this.controller.setAttribute('aria-valuemax', 100);

        this.handle.appendChild(this.leftArrow);
        this.handle.appendChild(this.control);
        this.handle.appendChild(this.rightArrow);
        this.control.appendChild(this.controller);

        this._init();
      }
    },

    _init: function() {

      if (this.checkImages() === false) {
        console.warn(this, "Check that the two images have the same aspect ratio for the slider to work correctly.");
      }

      this.updateSlider(this.options.startingPosition, false);

      if (this.options.showLabels === true) {
        if (this.imgBefore.label) { this.displayLabel(this.leftImage, this.imgBefore.label); }
        if (this.imgAfter.label) { this.displayLabel(this.rightImage, this.imgAfter.label); }
      }

      if (this.options.showCredits === true) {
        this.displayCredits();
      }

      var self = this;
      window.addEventListener("resize", function() {
        self.setWrapperDimensions();
      });


      // Set up Javascript Events
      // On mousedown, call updateSlider then set animate to false
      // (if animate is true, adds css transition when updating).

      this.slider.addEventListener("mousedown", function(e) {
        e = e || window.event;
        e.preventDefault();
        self.updateSlider(e, true);
        animate = true;

        this.addEventListener("mousemove", function(e) {
          e = e || window.event;
          e.preventDefault();
          if (animate) { self.updateSlider(e, false); }
        });

        this.addEventListener('mouseup', function(e) {
          e = e || window.event;
          e.preventDefault();
          e.stopPropagation();
          this.removeEventListener('mouseup', arguments.callee);
          animate = false;
        });
      });

      this.slider.addEventListener("touchstart", function(e) {
        e = e || window.event;
        e.preventDefault();
        e.stopPropagation();
        self.updateSlider(e, true);

        this.addEventListener("touchmove", function(e) {
          e = e || window.event;
          e.preventDefault();
          e.stopPropagation();
          self.updateSlider(event, false);
        });

      });

      /* keyboard accessibility */

      this.handle.addEventListener("keydown", function (e) {
        e = e || window.event;
        var key = e.which || e.keyCode;
        var ariaValue = parseFloat(this.style.left);

          //move jx-controller left
          if (key == 37) {
            ariaValue = ariaValue - 1;
          var leftStart = parseFloat(this.style.left) - 1;
          self.updateSlider(leftStart, false);
          self.controller.setAttribute('aria-valuenow', ariaValue);
          }

          //move jx-controller right
          if (key == 39) {
            ariaValue = ariaValue + 1;
          var rightStart = parseFloat(this.style.left) + 1;
          self.updateSlider(rightStart, false);
          self.controller.setAttribute('aria-valuenow', ariaValue);
          }
      });

      //toggle right-hand image visibility
      this.leftImage.addEventListener("keydown", function (event) {
           var key = event.which || event.keyCode;
            if ((key == 13) || (key ==32)) {
              self.updateSlider("90%", true);
                self.controller.setAttribute('aria-valuenow', 90);
            }
      });

      //toggle left-hand image visibility
      this.rightImage.addEventListener("keydown", function (event) {
           var key = event.which || event.keyCode;
            if ((key == 13) || (key ==32)) {
            self.updateSlider("10%", true);
            self.controller.setAttribute('aria-valuenow', 10);
            }
      });

      juxtapose.sliders.push(this);

      if (this.options.callback && typeof(this.options.callback) == 'function') {
        this.options.callback(this);
      }
    }

  };

  /*
    Given an element that is configured with the proper data elements, make a slider out of it.
    Normally this will just be used by scanPage.
  */
  juxtapose.makeSlider = function (element, idx) {
    if (typeof idx == 'undefined') {
      idx = juxtapose.sliders.length; // not super threadsafe...
    }

    var w = element;

    var images = w.querySelectorAll('img');

    var options = {};
    // don't set empty string into options, that's a false false.
    if (w.getAttribute('data-animate')) {
      options.animate = w.getAttribute('data-animate');
    }
    if (w.getAttribute('data-showlabels')) {
      options.showLabels = w.getAttribute('data-showlabels');
    }
    if (w.getAttribute('data-showcredits')) {
      options.showCredits = w.getAttribute('data-showcredits');
    }
    if (w.getAttribute('data-startingposition')) {
      options.startingPosition = w.getAttribute('data-startingposition');
    }
    if (w.getAttribute('data-mode')) {
      options.mode = w.getAttribute('data-mode');
    }
    if (w.getAttribute('data-makeresponsive')) {
      options.mode = w.getAttribute('data-makeresponsive');
    }

    specificClass = 'juxtapose-' + idx;
    addClass(element, specificClass);

    selector = '.' + specificClass;

    if (w.innerHTML) {
      w.innerHTML = '';
    } else {
      w.innerText = '';
    }

    slider = new juxtapose.JXSlider(
      selector,
      [
        {
          src: images[0].src,
          label: images[0].getAttribute('data-label'),
          credit: images[0].getAttribute('data-credit'),
          alt: images[0].alt
        },
        {
          src: images[1].src,
          label: images[1].getAttribute('data-label'),
          credit: images[1].getAttribute('data-credit'),
          alt: images[1].alt
        }
      ],
      options
    );
  };

  //Enable HTML Implementation
  juxtapose.scanPage = function() {
      var elements = document.querySelectorAll('.juxtapose');
      for (var i = 0; i < elements.length; i++) {
      juxtapose.makeSlider(elements[i], i);
    }
  };

  juxtapose.JXSlider = JXSlider;
  window.juxtapose = juxtapose;

  juxtapose.scanPage();

}(document, window));
// addEventListener polyfill / jonathantneal
!window.addEventListener && (function (WindowPrototype, DocumentPrototype, ElementPrototype, addEventListener, removeEventListener, dispatchEvent, registry) {
	WindowPrototype[addEventListener] = DocumentPrototype[addEventListener] = ElementPrototype[addEventListener] = function (type, listener) {
		var target = this;

		registry.unshift([target, type, listener, function (event) {
			event.currentTarget = target;
			event.preventDefault = function () { event.returnValue = false };
			event.stopPropagation = function () { event.cancelBubble = true };
			event.target = event.srcElement || target;

			listener.call(target, event);
		}]);

		this.attachEvent("on" + type, registry[0][3]);
	};

	WindowPrototype[removeEventListener] = DocumentPrototype[removeEventListener] = ElementPrototype[removeEventListener] = function (type, listener) {
		for (var index = 0, register; register = registry[index]; ++index) {
			if (register[0] == this && register[1] == type && register[2] == listener) {
				return this.detachEvent("on" + type, registry.splice(index, 1)[0][3]);
			}
		}
	};

	WindowPrototype[dispatchEvent] = DocumentPrototype[dispatchEvent] = ElementPrototype[dispatchEvent] = function (eventObject) {
		return this.fireEvent("on" + eventObject.type, eventObject);
	};
})(Window.prototype, HTMLDocument.prototype, Element.prototype, "addEventListener", "removeEventListener", "dispatchEvent", []);
