//-----------------------------------------------------------------------
// Module name   : LmooImgMorph
// Author        : Paul Battersby
// Creation Date : 02/14/08
// Description   :
//  This class is responsible for performing an image morph. See runChain()
//  for the events that will be performed.
//
//  This class can be used in 1 of 3 ways.
//   - a user can click a thumbnail image an have the old image morph away,
//     and a new image morph into it's place
//   - a user can click some "next" or some "previous" button to see the next
//     image in a list
//
//  $Log:$
//------------------------------------------------------------------------

//---------------------------- INCLUDE FILES -----------------------------


//----------------------------- CONSTANTS --------------------------------


//----------------------------- VARIABLES --------------------------------


//----------------------------- FUNCTIONS --------------------------------

var LmooImgMorph = new Class({
  options : {
    titleId : null,
    thumbSuffix : "_thumb",

    onComplete : Class.empty

  },

  //************************************************************************
  // Name   : loadImg
  //  This loads the imgNum image from the image array given during initialization
  //  and after the image has been loaded, continues the chain of effects
  //
  //  (fx object) fxObj - the opacity object that is running the effects chain
  //  (string) imgName  - the name of the image to be loaded
  //
  // Pre : to be called from runChain()
  // Returns : (nothing)
  //*************************************************************************
  loadImg : function(fxObj,imgName) {
    this.img = new Asset.image(imgName,{
      onload : function() {
        this.callChain();
      }.bind(fxObj)
    });
  },

  //************************************************************************
  // Name   : getImgObj
  //
  // Returns :
  //*************************************************************************
  getThumbObj : function()
  {
    return this.selectedThumb;
  },

  //************************************************************************
  // Name   : runChain
  //  Will chain the following events:
  //   - fade out the current image,
  //   - load another image
  //   - morph the size of the parent container of the image
  //   - fade in the new image
  //
  //  (int) imgNum the index of the image from the image array to be loaded
  //
  // Returns : (nothing)
  //*************************************************************************
  runChain : function(imgNum) {
    this.imgNum = imgNum;
    this.opacityFx.start(1,0).chain( // fade out

      function() {
        // read the old height,width from the current image
        var imgElement = $(this.imgParent).getFirst();
        this.oldHeight = imgElement.getStyle("height");
        this.oldWidth = imgElement.getStyle("width");
        // load a new image
        this.loadImg(this.opacityFx,this.imgArray[this.imgNum].src);
      }.bind(this),

      function () {
        // set the image parent to the old height (because initially it was
        // probably set to "auto" and we don't want it immediately resizing
        // after we delete the old image)
        $(this.imgParent).setStyles({
          "height" :this.oldHeight,
          "width"  :this.oldWidth
        });

        // remove the old image
        $(this.imgParent).empty();

        // if a title is to be displayed, display it
        if (this.options.titleId) {
          // read the alt tag from the thumbnail image
          $(this.options.titleId).innerHTML = this.imgArray[this.imgNum].alt;
        } // endif

        // morph to the new height
        var newHeight = $(this.img).height;
        var newWidth = $(this.img).width;
        this.sizeFx.start({
          "height" : newHeight,
          "width" : newWidth
        });

      }.bind(this),

      function () {
        // insert the new image
        this.img.injectInside(this.imgParent);

        // continue the chain effect
        this.opacityFx.callChain();
      }.bind(this),

      function() {
        // fade in the new image
        this.opacityFx.start(0,1);
        // continue the chain effect
        this.opacityFx.callChain();
      }.bind(this),

      function() {
        // trigger onComplete event if it exists, pass the src field
        //  of the thing that was clicked
        this.fireEvent("onComplete",this.imgArray[this.imgNum].src);
      }.bind(this)
    );
  },

  //************************************************************************
  // Name   : nextImg
  //  This runs the effect chain for the next image in the image array
  //
  // Returns : (nothing)
  //*************************************************************************
  nextImg : function() {
    this.imgNum++;
    if ( this.imgNum == this.numImgs ) {
      this.imgNum = 0;
    } // endif
    imgCycle.runChain(this.imgNum);
  },

  //************************************************************************
  // Name   : prevImg
  //  This runs the effect chain for the previous image in the image array
  //
  // Returns : (nothing)
  //*************************************************************************
  nextImg : function() {
    this.imgNum--;
    if ( this.imgNum < 0 ) {
      this.imgNum = this.numImgs-1;
    } // endif
    imgCycle.runChain(this.imgNum);
  },

  //************************************************************************
  // Name   : randomImg
  //  This runs the effect chain a randomly chosen image from the image array
  //
  // Returns : (nothing)
  //*************************************************************************
  randomImg : function() {
    this.imgNum = $random(0,this.numImgs-1)
    imgCycle.runChain(this.imgNum);
  },

  //************************************************************************
  // Name   : addThumbEvent
  //  This adds an event to all the thumbnail images matching the
  //  given eventString ("click" for example)
  //
  // (string) thumbClass - a CSS class (".slideshow" for example) common to
  //          all the thumbnails that are to be able to trigger the image morph
  //
  // (string) eventString - (optional) a string (default to "click")
  //                         that will trigger the image morph
  //
  // Returns : (nothing)
  //*************************************************************************
  addThumbEvent : function(thumbClass,eventString) {

    // if missing, set the eventString to a default
    if ( eventString == "" || eventString === null) {
      eventString = "click";
    } // endif

    // get the thumbClass name without the "." and create a pseudo
    // property to attach to the thumb image
    this.pseudoProperty = "LmooImgMorph" + thumbClass.slice(1);

    // store the event that will trigger the image Cycle
    this.thumbEvent = eventString;

    // for each img of the given CSS class
    $$(thumbClass).each(function(item,index) {

      // add the pseudo property to the thumbnail image and set to the
      // image number
      item.setProperty(this.pseudoProperty,index);

      // add an event to each thumnail image
      item.addEvent(this.thumbEvent,function(event) {
        this.selectedThumb = event.target;

        // run the effects chain
        this.runChain(event.target.getProperty(this.pseudoProperty));
      }.bind(this))
    }.bind(this));

  },

  //************************************************************************
  // Name   : _setImgArray (private)
  //  Determines or simply sets the array of images to be avaialble for morphing
  //
  //  (mixed) classOrArray - a CSS class or a 2 dimensional array.
  //
  //            If it's a CSS:
  //              This class will be used to read the src field of each
  //              thumb image and convert that to the name of the large image
  //              to be displayed. It will also read the "alt" tag incase it
  //              is to be used as a title for the image
  //
  //            If it's a 2 dimensional array:
  //              The first dimension is the name of the large image to be
  //              displayed. The second dimension is a title or html or some
  //              other such thing that is optionally to be displayed with
  //              the image
  //
  // Post : updates this.imgArray
  // Returns : (nothing)
  //*************************************************************************
  _setImgArray : function(classOrArray) {

    // if we were given an image class
    if ( $type(classOrArray == "string") ) {
      // for each thumbnail image of the given class
      $$(classOrArray).each(function(item,index) {
        // get the name of the thumb image
        var src = $(item).getProperty("src");

        // remove the thumb suffix to get the name of the full sized image
        src = src.replace(this.options.thumbSuffix,"");

        // store the path to the large image and the alt tag value of the thumbnail
        // (may later be used as an image title)
        this.imgArray[index] = {};
        this.imgArray[index].src = src;
        this.imgArray[index].alt = $(item).getProperty("alt");
      }.bind(this));

    // we were given an array of images
    } else {
      this.imgArray = classOrArray;
    }

    // record the number of images
    return this.imgArray.length;
  },

  //************************************************************************
  // Name   : initialize
  //  (string) imgParent - id of parent that contains the image.
  //                       it is the parent that will be faded in and out
  //
  //  (mixed) classOrArray - a CSS class or a 2 dimensional array. See _setImgArray()
  //  (array) imgArray - array of the names of the full sized images
  //                     that are to be part of the image view
  //  (int) imgNum     - index into imgArray of the image that is currently displayed
  //                     as the first image
  //  (object) options - (optional) configuration options. See options declaration
  //                     above for the available options
  //
  // Pre : This must be called AFTER the first image has been loaded
  //       It should be part of a call like this:
  //
  //          window.addEvent('load',myFile_loadInit);
  //
  // Returns : (nothing)
  //*************************************************************************
  initialize : function(imgParent,classOrArray,imgNum,options) {
    var imgElement;
    this.imgParent = imgParent;
    this.imgNum = imgNum;
    this.img = null;
    this.oldHeight = 0;
    this.oldWidth = 0;
    this.setOptions(options);
    this.imgArray = [];
    this.pseudoProperty = null;
    this.thumbEvent = null;

    // get first child of parent element which of course is the large image
    imgElement = $(imgParent).getFirst();

    // set the parent to the size of the initial image
    $(this.imgParent).setStyles({
      "width" : imgElement.width,
      "height" : imgElement.height
    });

    // setup the image array and record the number of images
    this.numImgs = this._setImgArray(classOrArray);

    // create the opacity effect for image parent
    this.opacityFx = new Fx.Tween(this.imgParent, 'opacity');

    // set size morph for the parent
    this.sizeFx = new Fx.Morph(this.imgParent,{
      onComplete : function() {
        this.opacityFx.callChain();
      }.bind(this)
    });

  }

});

LmooImgMorph.implement(new Events,new Options);

