
/**
* Slideshow, as used by Big Outdoors. This is fairly simple to use,
* just add images to a div, or other element, and create a new
* boSlideshow object. Example:
* 
* <div id="myPath">
*   <img src="img1.jpg"><img src="img2.jpg"><img src="img3.jpg">
* </div>
*
* <script type="text/javascript" src="/path/to/this.js"></script>
* <script type="text/javascript">
*   var slideshow = new boSlideshow({
*       path: '#myPath'
*   });
*
*   slideshow.init();
* </script>
*
* @author       Cameron Eure <cleure@websprockets.com>
* @copyright    ©2011 WebSprockets LLC. All rights reserved.
**/

function boSlideshow(params) {
    this.params = {
        interval: 4000,
        speed: 1000
    };
    
    this.images = [];
    this.path = false;
    this.current = 0;
    
    this.bgImage = false;
    this.fgImage = false;
    
    var i;
    for (i in params) {
        if (params.hasOwnProperty(i)) {
            this.setVar(i, params[i]);
        }
    }
    
    if (this.params.hasOwnProperty('path')) {
        this.setPath(this.params.path);
    }
}

/**
* Everything below is wrapped in a function, so functions and
* variables won't be accessible outside it's scope.
**/
(function () {

/**
* Reference to prototype. This is a trick to reduce file size, and
* the amount of code required to define functions / variables on
* the prototype. Because it's wrapped in a function, it won't be
* accessible outside this scope.
**/
var b = boSlideshow.prototype;

/**
* Helper method to get proper instance of this. Useful when scope
* changes and this may be a reference to something else (DOM, etc).
*
* @param    this
* @param    self
**/
function getSelf(thiss, self) {
    if (self instanceof boSlideshow) {
        return self;
    }
    
    if (thiss instanceof boSlideshow) {
        return thiss;
    }
    
    return undefined;
}

/**
* Get variable
*
* @param    k
* @param    self
* @return   value or undefined
**/
b.getVar = function(k, self) {
    self = getSelf(this, self);
    if (self.params.hasOwnProperty(k)) {
        return self.params[k];
    }
    
    return undefined;
};

/**
* Set variable
*
* @param    k
* @param    v
* @param    self
* @return   void
**/
b.setVar = function(k, v, self) {
    self = getSelf(this, self);
    self.params[k] = v;
};

/**
* Set path to element containing images.
* 
* @param    path
* @param    self
* @return   bool
**/
b.setPath = function(path, self) {
    self = getSelf(this, self);
    if (typeof(path) === 'string') {
        path = $(path);
    }
    
    if (typeof(path) !== 'object') {
        return false;
    }
    
    self.path = path;
    return true;
};

/**
* Init.
*
* @param    void
* @return   bool
**/
b.init = function(self) {
    self = getSelf(this, self);
    if (typeof(self.path) !== 'object') {
        return false;
    }
    
    // Copy image attributes
    $('img', self.path).each(function() {
        // Attributes to copy... IE will list ALL attributes, so they
        // have to be filtered
        var attrs = [];
        var i;
        var fetchAttrs = {
            width: true,
            height: true,
            src: true,
            alt: true};
        
        for (i = 0; i < this.attributes.length; i += 1) {
            // Make sure it's a copy
            var attr = {
                name: this.attributes[i].name,
                value: this.attributes[i].value};
            
            if (fetchAttrs.hasOwnProperty(attr.name)
            && fetchAttrs[attr.name] === true) {
                attrs.push(attr);
            }
        }
        
        self.images.push(attrs);
    });
    
    $(self.path).html('');
    
    self.setFgImage(0);
    self.setBgImage(1);
    self.loop();
    
    return true;
};

/**
* Set the foreground image
*
* @param    num
* @param    self
* @return   bool
**/
b.setFgImage = function(num, self) {
    self = getSelf(this, self);
    if (typeof(self.images[num]) === undefined) {
        return false;
    }
    
    if (typeof(self.fgImage) !== 'object') {
        self.fgImage = $('<img>');
        $(self.fgImage).css('position', 'absolute');
        $(self.fgImage).css('z-index', '100');
        $(self.path).append(self.fgImage);
    }
    
    var i;
    var attr;
    for (i = 0; i < self.images[num].length; i += 1) {
        attr = self.images[num][i];
        $(self.fgImage).attr(attr.name, attr.value);
    }
    
    return true;
};

/**
* Set the background image
*
* @param    num
* @param    self
* @return   bool
**/
b.setBgImage = function(num, self) {
    self = getSelf(this, self);
    if (typeof(self.images[num]) === undefined) {
        return false;
    }
    
    if (typeof(self.bgImage) !== 'object') {
        self.bgImage = $('<img>');
        $(self.path).append(self.bgImage);
    }
    
    var i;
    var attr;
    for (i = 0; i < self.images[num].length; i += 1) {
        attr = self.images[num][i];
        $(self.bgImage).attr(attr.name, attr.value);
    }
    
    return true;
};

/**
* Transition to next image.
*
* @param    self
* @return   void
**/
b.next = function(self) {
    self = getSelf(this, self);
    
    var next = (self.current + 1);
    if (next >= self.images.length) {
        next = 0;
    }
    
    self.setBgImage(next, self);
    $(self.fgImage).fadeOut(self.params.speed, function() {
        // Alt text flickers in Chrome. The timeout below
        // prevents this
        self.setFgImage(next, self);
        setTimeout(function() {
            $(self.fgImage).fadeTo(0, 1.0);
        }, 200);
    });
    
    self.current = next;
};

/**
* Loop method. Uses setTimeout at regular intervals. See also
* self.params.interval
*
* @param    self
* @return   void
**/
b.loop = function(self, fromLoop) {
    self = getSelf(this, self);
    if (self.images.length <= 1) {
        return;
    }
    
    setTimeout(function() {
        self.loop(self, true);
    }, self.params.interval);
    
    if (typeof(fromLoop) !== 'boolean' || fromLoop === false) {
        return;
    }
    
    self.next(self);
};

}());

