// Album object

// Laurent Fortin

// Xprima 2009


/**************************************************

var album = new Album(nodeId, {config object});

mandatory config options:


optional config options:


***************************************************/

// constructor
function Album(nodeId, config) {

  // initialize
  var scope = this;
  
  scope.version = '1.0';
  scope.nodeId = nodeId;
  scope.config = config;
  
  // some config init
  scope.config.selfName = scope.config.selfName || 'myAlbum';
  scope.config.duration = scope.config.duration || 0.2;
  //scope.config.dimRatio = scope.config.dimRatio || (4 / 3);
  // fixed 4:3 ratio for now ...
  scope.config.dimRatio = 4 / 3;
  
  scope.info = {
    createTime: new Date().getTime()
  };
  scope.cache = {
    cursor: 0,
    boxes: [],
    images: []
  };
  
  if(typeof(YUI) == 'undefined') {
    alert('YUI 3.x needed for Xportal object to work!');
    return;
  };
  
  YUI().use('node', 'io', 'anim', 'json-parse', function(Y) {
  
    scope.Y = Y;
    
    scope.validateConfig(config) && scope.build();
  
  });
  //console.debug(this);
};

// validate params
Album.prototype.validateConfig = function(config) {

  var scope = this;
  
  if(!scope.nodeId) {
    alert('Album: nodeId needed');
    return(false);
  }
  if(typeof(config) != 'object') {
    alert('Album: invalid config object: #' + scope.nodeId);
    return(false);
  }
  if(!config.images) {
    alert('Album: images parameter needed: #' + scope.nodeId);
    return(false);
  }
  
  return(true);
};

// build initial stuff
Album.prototype.build = function() {

  var scope = this;
  
  // get container node
  var container = scope.Y.Node.get('#' + scope.nodeId);
  if(container) {
    // set navigation divs
    container.set('innerHTML', '');
  
    var middleContentNode = scope.Y.Node.create('<div></div>')
      .setStyles({
        float: 'left',
        width: '100%',
        height: '100%',
        overflow: 'hidden',
        position: 'relative'
      }).set("className", "album-photos");
    container.appendChild(middleContentNode);
    
    // create n image containers
    var containers = [];
    var n = 10 + (scope.config.images.length % 10);
    
    for(var i = 1; i <= n; i++) {
      var imgContainer = scope.Y.Node.create('<div></div>')
        .setStyles({
          display: 'none',
          overflow: 'hidden',
          position: 'absolute',
          padding: '2px',
          border: 'solid 1px #dddddd',
          backgroundColor: '#fff'
        }).set("className", "album-photo");
      middleContentNode.appendChild(imgContainer);
      containers.push(imgContainer);
    };
    
    // insert image containers into cache
    for(var i = 0; i < scope.config.images.length; i++) {
      var containerToInsert = containers[i % n];
      scope.cache.boxes.push(containerToInsert);
    };
    
    // build image list in cache
    scope.Y.each(scope.config.images, function(i) {
      var img;
      if(typeof(i) == 'object') {
        scope.cache.images.push(i);
      } else {
        scope.cache.images.push({src: i, href: ''});
      }
      //var img = scope.Y.Node.create('<img src="' + src + '" style="display: none; position: absolute;">');
    });
    
    
    // keep many stuff in cache
    scope
      .setCache('contentWidth', middleContentNode.getComputedStyle('width'))
        .setCache('contentHeight', middleContentNode.getComputedStyle('height'));
    
    // here, init images position!!
    var styles = scope.getStyles(scope.getCache('contentWidth'), scope.getCache('contentHeight'));
    scope.setCache('styles', styles);
    
    scope.cache.boxes[scope.getCarouselIndex(scope.cache.boxes.length, 0, -2)]
      .setStyles({'display': 'block'})
        .setStyles(styles.left2);
    scope.cache.boxes[scope.getCarouselIndex(scope.cache.boxes.length, 0, -1)]
      .setStyles({'display': 'block'})
        .setStyles(styles.left1);
    scope.cache.boxes[0]
      .setStyles({'display': 'block'})
        .setStyles(styles.middle);
    scope.cache.boxes[scope.getCarouselIndex(scope.cache.boxes.length, 0, 1)]
      .setStyles({'display': 'block'})
        .setStyles(styles.right1);
    scope.cache.boxes[scope.getCarouselIndex(scope.cache.boxes.length, 0, 2)]
      .setStyles({'display': 'block'})
        .setStyles(styles.right2);
    
    // place navigation arrows
    var leftArrow = scope.Y.Node.create('<img src="/site/img/left-arrow.gif">');
    var rightArrow = scope.Y.Node.create('<img src="/site/img/right-arrow.gif">')
    middleContentNode.appendChild(leftArrow);
    middleContentNode.appendChild(rightArrow);
    leftArrow
      .setStyles({'display': 'block', 'position': 'absolute'})
        .setStyles(styles.leftarrow)
          .on('click', function() {
            scope.navigate('left');
          });
    rightArrow
      .setStyles({'display': 'block', 'position': 'absolute'})
        .setStyles(styles.rightarrow)
          .on('click', function() {
            scope.navigate('right');
          });
    
    // load images
    scope.loadImage(scope.cache.boxes[scope.getCarouselIndex(scope.cache.boxes.length, 0, -2)], scope.cache.images[scope.getCarouselIndex(scope.cache.images.length, 0, -2)].src, "JavaScript:(function(){" + scope.config.selfName + ".navigate('left')})()");
    scope.loadImage(scope.cache.boxes[scope.getCarouselIndex(scope.cache.boxes.length, 0, -1)], scope.cache.images[scope.getCarouselIndex(scope.cache.images.length, 0, -1)].src, "JavaScript:(function(){" + scope.config.selfName + ".navigate('left')})()");
    scope.loadImage(scope.cache.boxes[0], scope.cache.images[0].src, scope.cache.images[0].href);
    scope.loadImage(scope.cache.boxes[scope.getCarouselIndex(scope.cache.boxes.length, 0, 1)], scope.cache.images[scope.getCarouselIndex(scope.cache.images.length, 0, 1)].src, "JavaScript:(function(){" + scope.config.selfName + ".navigate('right')})()");
    scope.loadImage(scope.cache.boxes[scope.getCarouselIndex(scope.cache.boxes.length, 0, 2)], scope.cache.images[scope.getCarouselIndex(scope.cache.images.length, 0, 2)].src, "JavaScript:(function(){" + scope.config.selfName + ".navigate('right')})()");
    
    // return self
    return(this);
  }
};

// get image styles
Album.prototype.getStyles = function(width, height) {
  
  var scope = this;
  
  width = scope.numerizeDim(width);
  height = scope.numerizeDim(height);
  
  var frameRatio = width / height;
  var carousselRatio = 2.496454;
  
  var styles = { };
  
  // we use width or height, when we compare container ration and caroussel
  if(frameRatio < carousselRatio) {
    // we use frame width as reference
  
	  styles['left2'] = {
	    'width': (width * 0.2499) + 'px',
	    'height': (width * 0.2499 * (1 / scope.config.dimRatio)) + 'px',
	    'left': (width * 0) + 'px',
	    'top': (height - (width * 0.2499 * (1 / scope.config.dimRatio))) / 2 + 'px',
	    'opacity': 0.2,
	    'zIndex': 0,
	    'cursor': 'pointer'
	  };
	  styles['left1'] = {
	    'width': (width * 0.3864) + 'px',
	    'height': (width * 0.3864 * (1 / scope.config.dimRatio)) + 'px',
	    'left': (width * 0.0909) + 'px',
	    'top': (height - (width * 0.3864 * (1 / scope.config.dimRatio))) / 2 + 'px',
	    'opacity': 0.5,
	    'zIndex': 10,
	    'cursor': 'pointer'
	  };
	  styles['middle'] = {
	    'width': (width * 0.534) + 'px',
	    'height': (width * 0.534 * (1 / scope.config.dimRatio)) + 'px',
	    'left': (width * 0.2386) + 'px',
	    'top': (height - (width * 0.534 * (1 / scope.config.dimRatio))) / 2 + 'px',
	    'opacity': 1,
	    'zIndex': 20,
	    'cursor': 'pointer'
	  };
	  styles['right1'] = {
	    'width': (width * 0.3864) + 'px',
	    'height': (width * 0.3864 * (1 / scope.config.dimRatio)) + 'px',
	    'left': (width * 0.5227) + 'px',
	    'top': (height - (width * 0.3864 * (1 / scope.config.dimRatio))) / 2 + 'px',
	    'opacity': 0.5,
	    'zIndex': 10,
	    'cursor': 'pointer'
	  };
	  styles['right2'] = {
	    'width': (width * 0.2499) + 'px',
	    'height': (width * 0.2499 * (1 / scope.config.dimRatio)) + 'px',
	    'left': (width * 0.736) + 'px',
	    'top': (height - (width * 0.2499 * (1 / scope.config.dimRatio))) / 2 + 'px',
	    'opacity': 0.2,
	    'zIndex': 0,
	    'cursor': 'pointer'
	  };
	  styles['leftarrow'] = {
	    'left': ((width * 0.03977) - 9) + 'px',
	    'top': ((height / 2) - 16) + 'px',
	    'opacity': 0.5,
	    'zIndex': 20,
	    'cursor': 'pointer'
	  };
	  styles['rightarrow'] = {
	    'left': ((width * 0.9616) - 9) + 'px',
	    'top': ((height / 2) - 16) + 'px',
	    'opacity': 0.5,
	    'zIndex': 20,
	    'cursor': 'pointer'
	  };
	  
  } else {
    // we use frame height as reference
    
    	  var middleWidth = height * 0.98 * scope.config.dimRatio;
    	  var middleHeight = height * 0.98;
    	  var middleLeft = (width / 2 - (height * 0.98 * scope.config.dimRatio) / 2);
    	  
    	  styles['middle'] = {
    	    'width': middleWidth + 'px',
    	    'height': middleHeight + 'px',
    	    'left': middleLeft + 'px',
    	    'top': '0px',
    	    'opacity': 1,
    	    'zIndex': 20,
    	    'cursor': 'pointer'
	  };
	  
	  styles['left2'] = {
	    'width': (middleWidth * 0.4679) + 'px',
	    'height': (middleHeight * 0.4679) + 'px',
	    'left': (middleLeft - (middleWidth * 0.4468)) + 'px',
	    'top': (middleHeight * 0.266) + 'px',
	    'opacity': 0.2,
	    'zIndex': 0,
	    'cursor': 'pointer'
	  };
	  styles['left1'] = {
	    'width': (middleWidth * 0.7236) + 'px',
	    'height': (middleHeight * 0.7236) + 'px',
	    'left': (middleLeft - (middleWidth * 0.2766)) + 'px',
	    'top': (middleHeight * 0.1382) + 'px',
	    'opacity': 0.5,
	    'zIndex': 10,
	    'cursor': 'pointer'
	  };
	  
	  styles['right1'] = {
	    'width': (middleWidth * 0.7236) + 'px',
	    'height': (middleHeight * 0.7236) + 'px',
	    'left': (middleLeft + (middleWidth * 0.56)) + 'px',
	    'top': (middleHeight * 0.1382) + 'px',
	    'opacity': 0.5,
	    'zIndex': 10,
	    'cursor': 'pointer'
	  };
	  styles['right2'] = {
	    'width': (middleWidth * 0.4679) + 'px',
	    'height': (middleHeight * 0.4679) + 'px',
	    'left': (middleLeft + (middleWidth * 0.98)) + 'px',
	    'top': (middleHeight * 0.266) + 'px',
	    'opacity': 0.2,
	    'zIndex': 0,
	    'cursor': 'pointer'
	  };
	  styles['leftarrow'] = {
	    'left': ((width * 0.03977) - 9) + 'px',
	    'top': ((height / 2) - 16) + 'px',
	    'opacity': 0.5,
	    'zIndex': 20,
	    'cursor': 'pointer'
	  };
	  styles['rightarrow'] = {
	    'left': ((width * 0.9616) - 9) + 'px',
	    'top': ((height / 2) - 16) + 'px',
	    'opacity': 0.5,
	    'zIndex': 20,
	    'cursor': 'pointer'
	  };
	  
  };
  
  return(styles);
};

// load image into box
Album.prototype.loadImage = function(container, imageUrl, url) {

  var scope = this;
  
  // set please wait animation
  //container.set('innerHTML', 'LOADING..');
  container.addClass('wait');
  
  // load image
  var tmpId = 'img' + scope.getRandom();
  
  var newImage = scope.Y.Node.create('<img id="' + tmpId + '" style="display: none;">');
  scope.Y.get('body').appendChild(newImage);
  
  newImage.on('load', scope.Y.bind(function(event) {
    
    // move to container and show
    this.container
      .removeClass('wait')
        .set('innerHTML', '');
        
    // set an anchor if applicable
    var url = this.url || 'JavaScript:;';
    var alink = scope.Y.Node.create('<a></a>')
      .set('href', url);
    if(url.search(/JavaScript/i) == -1) {
      alink.set('target', '_blank');
    }
    alink.appendChild(scope.Y.get('#' + this.tmpId));
    
    this.container.appendChild(alink);
    
    event.target
      .setStyle('opacity', '0')
        .setStyle('display', 'block');
    
    // set 100% width or height
    var imgWidth = scope.numerizeDim(event.target.getComputedStyle('width'));
    var imgHeight = scope.numerizeDim(event.target.getComputedStyle('height'));
    
    if((imgWidth / imgHeight) > scope.config.dimRatio) {
      event.target.setStyle('width', '100%');
    } else {
      event.target.setStyle('height', '100%');
    }
    
    new scope.Y.Anim({
      node: event.target,
      from: {'opacity': 0},
      to: {'opacity': 1},
      duration: 0.3
    }).run();
    //event.target.setStyle('visibility', 'visible');
    
  }, {'container': container, 'tmpId': tmpId, 'url': url}));
  
  newImage.set('src', imageUrl);
  
  return(this);
};

// set url
Album.prototype.setImageURL = function(box, url) {
  
  var scope = this;
  
  var alink = box.query('a');
  if(!alink) return(false);
  
  var url = url || 'JavaScript:;';
  
  alink.set('href', url);
  if(url.search(/JavaScript/i) == -1) {
    alink.set('target', '_blank');
  } else {
    alink.set('target', '');
  }
  
  return(this);
};

// transform width and height in numeric values
Album.prototype.numerizeDim = function(dim) {
  return Number(dim.replace(/px/, ''));
};

/**************
Effects
***************/

// wrapper for position switch
Album.prototype.navigate = function(direction) {

  var scope = this;
  
  // get info
  var len = scope.getCache('boxes').length;
  var cursor = scope.getCache('cursor');
  var position = scope.getCarouselIndex(len, cursor, (direction === 'left' ? -1 : 1));
  
  // if lock is released
  if(! scope.getCache('lock')) {
  
    // update cursor
    scope.setCache('cursor', position);
    
    scope.setCache('lock', true);
    scope.switchImage(position, direction, scope.config.duration, scope.Y.bind(function() {
    
      var styles = scope.getCache('styles');
      
      // show next image ( and load img )
      if(this.direction === 'left') {
        var box = scope.cache.boxes[scope.getCarouselIndex(scope.cache.boxes.length, this.position, -2)];
        box
          .setStyles({'display': 'block'})
            .setStyles(styles.left2);
        scope.loadImage(
          box,
          scope.cache.images[scope.getCarouselIndex(scope.cache.images.length, this.position, -2)].src,
          "JavaScript:(function(){" + scope.config.selfName + ".navigate('left')})()"
        );
      } else {
        var box = scope.cache.boxes[scope.getCarouselIndex(scope.cache.boxes.length, this.position, 2)];
        box
          .setStyles({'display': 'block'})
            .setStyles(styles.right2);
        scope.loadImage(
          box,
          scope.cache.images[scope.getCarouselIndex(scope.cache.images.length, this.position, 2)].src,
          "JavaScript:(function(){" + scope.config.selfName + ".navigate('right')})()"
        );
      }
      
      scope.setCache('lock', false);
    }, {position: position, direction: direction}));
  }
  
  // return self
  return(this);
};


// image switcher
Album.prototype.switchImage = function(toIndex, direction, duration, callback) {

  var scope = this;
  
  var middleContentNode = scope.getCache('middleContentNode');
  var styles = scope.getCache('styles');
  var anims = { };
  
  if(direction === 'left') {
  
    // hide image
    scope.cache.boxes[scope.getCarouselIndex(scope.cache.boxes.length, toIndex, 3)]
      .setStyles({'display': 'none'});
    
    anims.anim1 = new scope.Y.Anim({
      node: scope.cache.boxes[scope.getCarouselIndex(scope.cache.boxes.length, toIndex, -1)],
      from: styles.left2,
      to: styles.left1,
      duration: Number(duration),
      easing: scope.Y.Easing.easeOut
    });
    anims.anim2 = new scope.Y.Anim({
      node: scope.cache.boxes[scope.getCarouselIndex(scope.cache.boxes.length, toIndex, 0)],
      from: styles.left1,
      to: styles.middle,
      duration: Number(duration),
      easing: scope.Y.Easing.easeOut
    });
    anims.anim3 = new scope.Y.Anim({
      node: scope.cache.boxes[scope.getCarouselIndex(scope.cache.boxes.length, toIndex, 1)],
      from: styles.middle,
      to: styles.right1,
      duration: Number(duration),
      easing: scope.Y.Easing.easeOut
    });
    anims.anim4 = new scope.Y.Anim({
      node: scope.cache.boxes[scope.getCarouselIndex(scope.cache.boxes.length, toIndex, 2)],
      from: styles.right1,
      to: styles.right2,
      duration: Number(duration),
      easing: scope.Y.Easing.easeOut
    });
    
    anims.anim1.on('end', callback || function(){});
    
  } else {
    
    // hide image
    scope.cache.boxes[scope.getCarouselIndex(scope.cache.boxes.length, toIndex, -3)]
      .setStyles({'display': 'none'});
    
    anims.anim1 = new scope.Y.Anim({
      node: scope.cache.boxes[scope.getCarouselIndex(scope.cache.boxes.length, toIndex, -2)],
      from: styles.left1,
      to: styles.left2,
      duration: Number(duration),
      easing: scope.Y.Easing.easeOut
    });
    anims.anim2 = new scope.Y.Anim({
      node: scope.cache.boxes[scope.getCarouselIndex(scope.cache.boxes.length, toIndex, -1)],
      from: styles.middle,
      to: styles.left1,
      duration: Number(duration),
      easing: scope.Y.Easing.easeOut
    });
    anims.anim3 = new scope.Y.Anim({
      node: scope.cache.boxes[scope.getCarouselIndex(scope.cache.boxes.length, toIndex, 0)],
      from: styles.right1,
      to: styles.middle,
      duration: Number(duration),
      easing: scope.Y.Easing.easeOut
    });
    anims.anim4 = new scope.Y.Anim({
      node: scope.cache.boxes[scope.getCarouselIndex(scope.cache.boxes.length, toIndex, 1)],
      from: styles.right2,
      to: styles.right1,
      duration: Number(duration),
      easing: scope.Y.Easing.easeOut
    });
    
    anims.anim1.on('end', callback || function(){});
    
  }
  
  // run all animations
  scope.Y.each(anims, function(a, k) {
    a.run();
  });
  
  // change urls
  scope.setImageURL(
    scope.cache.boxes[scope.getCarouselIndex(scope.cache.boxes.length, toIndex, -2)],
    "JavaScript:(function(){" + scope.config.selfName + ".navigate('left')})()"
  );
  scope.setImageURL(
    scope.cache.boxes[scope.getCarouselIndex(scope.cache.boxes.length, toIndex, -1)],
    "JavaScript:(function(){" + scope.config.selfName + ".navigate('left')})()"
  );
  scope.setImageURL(
    scope.cache.boxes[scope.getCarouselIndex(scope.cache.boxes.length, toIndex, 0)],
    scope.getCache('images')[toIndex].href
  );
  scope.setImageURL(
    scope.cache.boxes[scope.getCarouselIndex(scope.cache.boxes.length, toIndex, 1)],
    "JavaScript:(function(){" + scope.config.selfName + ".navigate('right')})()"
  );
  scope.setImageURL(
    scope.cache.boxes[scope.getCarouselIndex(scope.cache.boxes.length, toIndex, 2)],
    "JavaScript:(function(){" + scope.config.selfName + ".navigate('right')})()"
  );
  
  // return self
  return(this);
};



// carousel index
Album.prototype.getCarouselIndex = function(len, pos, offset) {

  if((pos + offset) >= len) {
    return (pos + offset) % len;
  }
  if(pos + offset < 0) {
    var tmp = ((pos + offset) % len) + len;
    return (tmp == len ? 0 : tmp);
  }
  return pos + offset;
};



// evaluate template
Album.prototype.evaluateTemplate = function(template, obj) {

  var scope = this;
  
  var content = template;
  
  // format: #{label}
  
  for(key in obj) {
    // content = content.replace(/\#\{label\}/, obj['label']);
    eval("content = content.replace(/\#\{" + key +"\}/g, '" + obj[key] + "');");
  }
  
  // remove undefined tags from template
  content = content.replace(/\#\{[a-zA-Z0-9_]*\}/g, '');
  
  return(content);
};

// get cache
Album.prototype.getCache = function(label) {

  return(this.cache[label]);
};

// set cache
Album.prototype.setCache = function(label, value) {

  this.cache[label] = value;
  
  // return self
  return(this);
};

// clear cache
Album.prototype.clearCache = function(label) {

  delete(this.cache[label]);
  
  // return self
  return(this);
};

// generate random number
Album.prototype.getRandom = function() {

  return(parseInt(Math.random() * 1000000000));
};

// identity function, which returns its argument untouched
Album.prototype.identityFunction = function(x) {
  return(x);
};

/**
  Flash Player Detection from Adobe
  http://www.adobe.com/support/flash/how/shock/javaplugs/javaplugs05.html
**/
Album.prototype.isFlashEnabled = function() {

  var flashEnabled = (navigator.appName == "Microsoft Internet Explorer" && navigator.appVersion.indexOf("Mac") == -1 && navigator.appVersion.indexOf("3.1") == -1)
    || (navigator.plugins && navigator.plugins["Shockwave Flash"])
    || navigator.plugins["Shockwave Flash 2.0"];
  
  return(flashEnabled ? true : false);
};

