var JSGallery2 = new Class({

  Implements: Options,

  options: {

    'borderWidthDeselected': 0,  //the width of the border of an deselected image (should be the same as in css)

    'borderWidthSelected': 0,  //border width of the selected image

    'borderColor': '#fff',    //the color of the borders

    'loadingMask': '#000',    //color of the mask overlay during loading of the big images

    'loadingOpacity': 0.6,    //opacity of the border div during loading (including the mask)

    'loadingImage': null,    //you may specify a loading image which is displayed while the big images are being loaded

    'selectSpeed': 200,      //the duration of the select effect in ms (high values will make it look ugly)

    'fadeSpeed': 350,      //the duration of the image fading effect in ms

    'pageSpeed': 1000,      //the duration of the change page effect in ms

    'prevHandle': null,      //if you pass a previous page handle in here, it will be hidden if it's not needed

    'nextHandle': null,      //like above, but for next page

    'initialIndex': -1,      //which thumb to select after init. you could create deep links with it.

    'maxOpacity': 0.8      //maximum opacity before cursor reaches prev/next control, then it will be set to 1 instantly.

  },

  /**

   *  Constructor. Starts up the whole thing :-)

   *

   *  This script is free to use. It has been created by http://www.aplusmedia.de and

   *  can be downloaded on http://www.esteak.net.

   *  License: GNU GPL 2.0: http://creativecommons.org/licenses/GPL/2.0/

   *  Example on: http://blog.aplusmedia.de/moo-gallery2

   *  Known issues:

   *  - preloading does not care about initialIndex param

   *  - hovering to a control over the border of the big image will make the other one flickering

   *  - if you enter and leave the control area very quickly, the control flickers sometimes

   *  - does not work in IE6

   *

   *   @param {Array} thumbs, An array of HTML elements

   *  @param {HTMLelement} bigImageContainer, the full size image

   *  @param {HTMLelement} pageContainer, If you have several pages, put them in this container

   *  @param {Object} options, You have to pass imagesPerPage if you have more than one!

   */

  initialize: function(thumbs, bigImageContainer, pageContainer, bigTextcontainer, options) {

    this.currentPageNumber = 0;

    this.loadedImages = 0;

    this.blockKeys = false;

    this.imagesPerPage = pageContainer.getFirst().getChildren().length;

    this.setOptions(options);

    this.thumbs = thumbs;

    this.bigImage = bigImageContainer;

    this.bigText = bigTextcontainer;

    this.pageContainer = pageContainer;

    this.convertThumbs();

    if(this.options.initialIndex != -1) {

      this.selectByIndex(this.options.initialIndex);

    } else {

      this.gotoPage(0);

    }

    this.createControls();

    if($defined(this.options.loadingImage)) {

      new Asset.image('loading.gif');

    }

    this.loadNextImage();

  },

  /**

   *  Creates the previous and next links over the big image.

   */

  createControls: function() {

    this.prevLink = new Element('a', {

      events: {

        'click': this.prevImage.bindWithEvent(this),

        'mouseleave': this.mouseLeaveHandler.bindWithEvent(this)

      },

      styles: {

        'width': '46px',

        'display': 'block',

        'position': 'absolute',

        'top': 0,

        'height' : '98%',

        'background': 'url(prev.gif) no-repeat 0 50%',

        'outline': 'none'

      },

      'href': '#'

    }).injectInside(this.bigImage.getParent());

    this.prevLink.addEvent('mouseover', this.focusControl.bindWithEvent(this, this.prevLink));

    this.nextLink = this.prevLink.clone().injectAfter(this.prevLink).set({

      'events': {

        'click': this.nextImage.bindWithEvent(this),

        'mouseleave': this.mouseLeaveHandler.bindWithEvent(this)

      },

      'styles': {

        'right': 1,

        'background-image': 'url(next.gif)'

      }

    });

    this.prevLink.setStyle('left', 1);

    this.nextLink.addEvent('mouseover', this.focusControl.bindWithEvent(this, this.nextLink));

    this.bigImage.addEvents({

      'mousemove': this.mouseOverHandler.bindWithEvent(this),

      'mouseleave': this.mouseLeaveHandler.bindWithEvent(this)

    });

    document.addEvent('keydown', this.keyboardHandler.bindWithEvent(this));

    this.mouseLeaveHandler();

  },

  /**

   * Focuses one control

   *

   * @param {Event} event

   * @param {HTMLElement} control

   */

  focusControl: function(event, control) {

    control.set('opacity', 1);

  },

  /**

   *  Handles mouse movement over the big image.

   * @param {Event} event

   */

  mouseOverHandler: function(event) {

    var currentIndex = this.thumbs.indexOf(this.selectedContainer);

    //this makes the control on the other side fade out in just the moment when you reach one

    var activeRange = this.bigImage.getSize().x;

    var op = 0;

    if(currentIndex < this.thumbs.length - 1) {

      op = this.options.maxOpacity - this.getDistanceToMouse(this.nextLink, event) / activeRange;

    }

    this.nextLink.set('opacity', 0);

    op = 0;

    if(currentIndex > 0) {

      op = this.options.maxOpacity - this.getDistanceToMouse(this.prevLink, event) / activeRange;

    }

    this.prevLink.set('opacity', 0);

  },

  /**

   * Hides the controls.

   */

  mouseLeaveHandler: function() {

    this.nextLink.set('opacity', 0);

    this.prevLink.set('opacity', 0);

  },

  /**

   * Handles keyboard interactions.

   * @param {Event} event

   */

  keyboardHandler: function(event){

    if(!this.blockKeys) {

      if(event.code >= 49 && event.code <= 57) {

        this.gotoPage(event.key - 1);

      } else if (event.key == "left") {

        this.prevImage(event);

      } else if (event.key == "right") {

        this.nextImage(event);

      }

    }

  },

  /**

   *  Returns the distance to the mouse from the middle of a given element.

   *  @param {HTMLelement} element

   *  @param {Event} event

   *   @return integer

   */

  getDistanceToMouse: function(element, event) {

    var s = element.getSize();

    var xDiff = Math.abs(event.client.x - (element.getLeft() + s.x / 2));

    var yDiff = Math.abs(event.client.y - (element.getTop() + s.y / 2));

    return Math.sqrt( Math.pow(xDiff, 2) + Math.pow(yDiff, 2) );

  },

  /**

   *   Adds the border to the thumbs and so on. (conversion of static thumbs)

   */

  convertThumbs: function() {

    this.thumbs.each(function(thumbContainer, count) {

      this.convertThumb(thumbContainer, count);

    }, this);

  },

  /**

   * Converts one single thumb.

   * @param {HTMLelement} thumbContainer

   * @param {Integer} count

   */

  convertThumb: function(thumbContainer, count) {

    if(!$defined(thumbContainer)) {

      return;

    }

    thumbContainer.addEvent('click', this.select.bind(this, thumbContainer)).setStyle('position', 'relative').setProperty('counter', count);

    var bigImage = thumbContainer.getFirst().setProperty('href', 'javascript: void(0);').getProperty('rel');

    var border = new Element('div', {

      styles: {

        'border': this.options.borderWidthDeselected + 'px solid ' + this.options.borderColor,

        'width': thumbContainer.getSize().x,

        'height': thumbContainer.getSize().y,

        'position': 'absolute',

        'background-color': this.options.loadingMask,

        'top': 0,

        'left': 0

      },

      'rel': bigImage

    }).injectTop(thumbContainer).set('opacity', this.options.loadingOpacity);

  },

  /**

   *   Removes key blocking.

   */

  unBlockKeys: function() {

    this.blockKeys = false;

  },

  /**

   *  Selects a certain image. (You have to pass the outer container of the image)

   *  @param {HTMLelement} container

   */

  select: function(container) {

    if(this.blockKeys || !$defined(container)) {

      return false;

    }

    this.blockKeys = true;

    if($defined(this.selectedContainer)) {

      //this prevents an ugly effect if you click on the currently selected item

      if(container == this.selectedContainer) {

        this.unBlockKeys();

        return false;

      }

      this.deselect(this.selectedContainer);

    }

    //if target image is not on current page, we have to go there first

    var targetPage = Math.floor(container.getProperty('counter') / this.imagesPerPage);

    if(this.currentPageNumber != targetPage) {

      this.gotoPage(targetPage, container);

    }

    this.selectedContainer = container;

    //make calculations a bit more handy

    var s = container.getSize();

    var des = this.options.borderWidthDeselected;

    var sel = this.options.borderWidthSelected;

    new Fx.Morph(container.getFirst(), {

      duration: this.options.selectSpeed,

      transition: Fx.Transitions.Quad.easeOut

    }).start({

      'border-width': [des, sel + 'px'],

      'width': [s.x, s.x - 2 * (sel - des) ],

      'height': [s.y, s.y - 2 * (sel - des)]

    });

    this.setImage(container.getFirst().getProperty('rel'));

  },

  /**

   * Preloads one big image

   */

  loadNextImage: function() {

    var thumbContainer = this.thumbs[this.loadedImages].getFirst();

    if($defined(this.options.loadingImage)) {

      new Element('img', {'src': 'loading.gif'}).injectTop(thumbContainer);;

    }

    var imageToLoad = thumbContainer.getProperty('rel');

    new Asset.image(imageToLoad, {

      onload: this.imageLoaded.bind(this, this.thumbs[this.loadedImages])

    });

  },

  /**

   * Callback after an image has been successfully preloaded.

   * Removes the loading effects from the border div.

   * @param {HTMLElement} thumbContainer the thumb wrapper div

   */

  imageLoaded: function(thumbContainer) {

    this.loadedImages++;

    if($defined(this.options.loadingImage)) {

      //remove loading gif

      thumbContainer.getFirst().getFirst().destroy();

    }

    //remove loading styles

    thumbContainer.getFirst().setStyle('background-color', 'transparent').setOpacity(1);

    if(this.loadedImages < this.thumbs.length) {

      this.loadNextImage();

    }

  },

  /**

   * Selects an image by its thumbnail index.

   * @param {integer} index of the thumbnail, starting with 0

   */

  selectByIndex: function(index) {

    this.mouseLeaveHandler();

    if(index < 0 || this.thumbs.length <= index) {

      index = 0;

    }

    this.select(this.thumbs[index]);

  },

  /**

   *  Opposite to method above.

   *  @param {HTMLelement} container

   */

  deselect: function(container) {

    new Fx.Morph(container.getFirst(), {duration: this.options.selectSpeed, transition: Fx.Transitions.Quad.easeOut}).start({

      'border-width': this.options.borderWidthDeselected + 'px',

      'width': container.getSize().x,

      'height': container.getSize().y

    });

  },

  /**

   *  Changes the full size image to given one.               this.bigText.innerhtml = ("testttt");

   *  @param {String} newSrc, new target of the full size image

   */

  setImage: function(newSrc) {

    var effect = new Fx.Tween(this.bigImage, {duration: this.options.fadeSpeed, transition: Fx.Transitions.Quad.easeOut});

    effect.start('opacity', 0).chain(function(){

      this.bigImage.setProperty('src', newSrc);

       this.mouseLeaveHandler();

      effect.start('opacity', 1).chain(this.unBlockKeys.bind(this));

    }.bind(this));

  },

  /**

   *  Navigates to previous page.

   */

  prevPage: function() {

    this.gotoPage(this.currentPageNumber - 1);

  },

  /**

   *  Navigates to next page.

   */

  nextPage: function() {

    this.gotoPage(this.currentPageNumber + 1);

  },

  /**

   *  Selects the previous image.

   */

  prevImage: function(e) {

    e = new Event(e).stop();

    this.selectByIndex(this.thumbs.indexOf(this.selectedContainer) - 1);

  },

  /**

   *  Selects the next image.

   */

  nextImage: function(e) {

    e = new Event(e).stop();

    this.selectByIndex(this.thumbs.indexOf(this.selectedContainer) + 1);

  },

  /**

   *  Navigates to given page and selects the first image of it.

   *  Also hides the handles (if set).

   *  @param {Integer} pageNumber, index of the target page (0-n)

   *  @param {HTMLElement} selectImage, optionally receives a particular image to select

   */

  gotoPage: function(pageNumber, selectImage) {

    //if we like to select another image on that page than the first one

    selectImage = $pick(selectImage, this.thumbs[pageNumber * this.imagesPerPage]);

    var lastPage = Math.ceil(this.thumbs.length / this.imagesPerPage);

    if(pageNumber >= 0 && pageNumber < lastPage) {

      this.pageContainer.set('tween', {

        duration: this.options.pageSpeed,

        transition: Fx.Transitions.Quint.easeInOut

      });

      this.pageContainer.tween('margin-left', this.pageContainer.getFirst().getSize().x * pageNumber * -1);

      this.currentPageNumber = pageNumber;

      this.select(selectImage);



      //update handles

      if($defined(this.options.prevHandle)) {

        new Fx.Tween(this.options.prevHandle, {duration:this.options.fadeSpeed * 2}).start('opacity', pageNumber == 0 ? 0 : 1);

      }

      if($defined(this.options.nextHandle)) {

        new Fx.Tween(this.options.nextHandle, {duration:this.options.fadeSpeed * 2}).start('opacity', pageNumber == lastPage - 1 ? 0 : 1);

      }

    }

  }

});
