﻿/* Creates a new ContentManager.
 * @param id identifies the ContentManager and the html elements it renders
 * @param width the width of the div rendered by the ContentManager
 * @param height the height of the div rendered by the ContentManager
 */
function ContentManager(id, width, height) {
    this.id = id;
    this.width = width;
    this.height = height;
    ContentManager.instances[id] = this;
    this.curIndex = 0;
    this.prevIndex = 0;
    this.objects = [];
    this.objectGroups = [];
    this.objectGroups[0] = this.objects;
}

/* Transition behavior is controlled by the fadeMode property.
 * FADE_NONE -> The next image is displayed immediately.
 * FADE_OUT -> The current image fades out and then the next image fades in.
 * FADE_OVER -> The next image fades in over the current image.
 */
ContentManager.FADE_NONE = 0;
ContentManager.FADE_OUT = 1;
ContentManager.FADE_OVER = 2;

/* Maps an ID to a ContentManager instance. */
ContentManager.instances = {};

/* The URL for the impression tracking handler. */
ContentManager.prototype.impressionHandler = "/ad_tracking/impression.ashx";

/* The URL for the link redirection handler. */
ContentManager.prototype.linkHandler = "/ad_tracking/link.ashx";

/* The fade mode used to transition between images. */
ContentManager.prototype.fadeMode = ContentManager.FADE_OVER;

/* The number of milliseconds for the fade interval. */
ContentManager.prototype.fadeDelay = 100;

/* The default number of milliseconds an image is visible
 * before the next transition is started. */
ContentManager.prototype.transitionDelay = 1000;

/* Returns the DOM element with the given ID.
 * @param elementID the unique ID of an element
 * @return the DOM element with the given ID
 */
ContentManager.getElement = function(elementID) {
    var result = null;
    if(document.getElementById) {
        result = document.getElementById(elementID);
    }
    else if(document.all) {
        result = document.all[elementID];
    }
    return result;
}

/* Creates and returns an instance of XMLHttpRequest.
 * @return the instance of XMLHttpRequest
 */
ContentManager.createXMLHttpRequest = function() {
    var result = null;
    if(window.XMLHttpRequest) {
        result = new XMLHttpRequest();
    }
    else {
        try {
            result = new ActiveXObject("Msxml2.XMLHTTP");
        }
        catch (ex1) {
            try {
                result = new ActiveXObject("Microsoft.XMLHTTP");
            }
            catch (ex2) {
                result = null;
            }
        }
    }
    return result;
}

/* Generates a unique URL for the path and id by adding 
 * a random number and the time to the URL query string.
 * @param path the URL without a query string
 * @param id the value of the id query string parameter
 * @return the generated URL
 */
ContentManager.generateURL = function(path, id) {
    var r = Math.random();
    var t = new Date().getTime();
    return path + '?id=' + id + '&r=' + r + '&t=' + t;
}

/* Generates the html used to embed a flash file.
 * @param width the width of the flash file
 * @param height the height of the flash file
 * @param source the URL for the flash file
 */
ContentManager.embedSWF = function(width, height, source) {
    var dimensions = ' width="' + width + '" height="' + height + '"';
    var html = '<object' + dimensions;
    html += ' classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"';
    html += ' codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,28,0"';
    html += '>';
    html += '<param name="movie" value="' + source + '" />';
    html += '<param name="quality" value="high" />';
    html += '<embed' + dimensions;
    html += ' src="' + source +'"';
    html += ' quality="high"';
    html += ' pluginspage="http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash"';
    html += ' type="application/x-shockwave-flash"';
    html += '></embed></object>';
    document.write(html);
}

/* Calls the impression handler to generate an impression. */
ContentManager.prototype.generateImpression = function() {
    var o = this.objects[this.curIndex];
    if(!o.impression) {
        o.impression = true;
        var url = ContentManager.generateURL(this.impressionHandler, o.objID);
        var request = ContentManager.createXMLHttpRequest();
        if(request) {
            request.open("GET", url, true);
            request.send(null);
        }
        else {
            new Image().src = url;
        }
    }
}

/* Updates the opacity of an image.
 * @param the index of the image to update
 */
ContentManager.prototype.updateOpacity = function(index) {
    var img = this.objects[index].img;
    if(img.filters) {
        img.filters.item("DXImageTransform.Microsoft.alpha").opacity = this.percentOpacity;
    }
    else if(img.style.opacity) {
        img.style.opacity = this.percentOpacity / 100;
    }
    else if(img.style.MozOpacity) {
        img.style.MozOpacity = this.percentOpacity / 100;
    }
    else if(img.style.KhtmlOpacity) {
        img.style.KhtmlOpacity = this.percentOpacity / 100;
    }
}

/* Updates the style property of a div.
 * @param index the index of the div
 * @param zIndex the new z index for the div
 * @param visiblity the new visiblity of the div
 */ 
ContentManager.prototype.updateDivStyle = function(index, zIndex, visibility) {
    var style = this.objects[index].div.style;
    style.zIndex = zIndex;
    style.visibility = visibility;
}

/* Fades the current image in by increasing the opacity of the image.
 * Once the current image is completely opaque, 
 * the div style of the previous image is updated if necessary
 * and a timeout is set to call startTransition.
 */
ContentManager.prototype.fadeIn = function() {
    if(this.percentOpacity < 100) {
        this.percentOpacity += 10;
        this.updateOpacity(this.curIndex);
    }
    else {
        clearInterval(this.fadeInterval);
        if(this.fadeMode === ContentManager.FADE_OVER && this.curIndex != this.prevIndex) {
            this.updateDivStyle(this.prevIndex, 0, 'hidden');
        }
        setTimeout(this.callStartTransition, this.objects[this.curIndex].delay)
    }
}

/* Fades the previous image out by decreasing the opacity of the image.
 * Once the previous image is transparent, 
 * the div style is updated as necessary
 * and an interval is set to call fadeIn.
 */
ContentManager.prototype.fadeOut = function() {
    if(this.percentOpacity > 10) {
        this.percentOpacity -= 10;
        this.updateOpacity(this.prevIndex);
    }
    else {
        clearInterval(this.fadeInterval);
        this.updateDivStyle(this.prevIndex, 0, 'hidden');
        this.updateDivStyle(this.curIndex, 2, 'visible');
        this.fadeInterval = setInterval(this.callFadeIn, this.fadeDelay);
    }
}

/* Displays an image for the first time. */
ContentManager.prototype.displayImage = function() {
    var o = this.objects[this.curIndex];
    var self = this;
    var imageStyle = 'border:0px;';
    if(this.fadeMode) {
        imageStyle += 'filter:progid:DXImageTransform.Microsoft.alpha(opacity=10);opacity:0.1;-moz-opacity:0.1;-khtml-opacity:0.1;';
    }
    
    var anchorHREF = ContentManager.generateURL(this.linkHandler, o.objID);
    var html = '<a id="' + o.id + '_a" href="' + anchorHREF + '" target="' + o.target +'">';
    html += '<img id="' + o.id + '_img" src="' + o.src + '" style="' + imageStyle + '" />';
    html += '</a>';
    o.div.innerHTML = html;
    o.div.style.zIndex = 1;
    o.img = o.div.childNodes[0].childNodes[0];
    o.img.onload = function() {
        self.objects[self.curIndex].img.onload = null;
        self.generateImpression();
        if(self.curIndex != self.prevIndex) {
            /* An image has already been displayed, so transition images. */
            if(self.fadeMode === ContentManager.FADE_OUT) {
                self.updateDivStyle(self.curIndex, 0, 'hidden');
            }
            self.transitionImage();
        }
        else if(self.fadeMode) {
            /* This is the first image being displayed and   */
            /* fadeMode != FADE_NONE, so fade the image in.  */
            self.percentOpacity = 10;
            self.updateOpacity(self.curIndex);
            self.updateDivStyle(self.curIndex, 2, 'visible');
            self.fadeInterval = setInterval(self.callFadeIn, self.fadeDelay);
        }
        else {
            /* This is the first image being displayed and       */
            /* fadeMode == FADE_NONE, so just display the image. */
            self.updateDivStyle(self.curIndex, 2, 'visible');
            setTimeout(self.callStartTransition, self.objects[self.curIndex].delay)
        }
    }
}

/* Performs the transition between images. */
ContentManager.prototype.transitionImage = function() {
    if(this.fadeMode === ContentManager.FADE_OVER) {
        this.updateDivStyle(this.prevIndex, 1, 'visible');
        this.percentOpacity = 10;
        this.updateOpacity(this.curIndex);
        this.updateDivStyle(this.curIndex, 2, 'visible');
        this.fadeInterval = setInterval(this.callFadeIn, this.fadeDelay);
    }
    else if(this.fadeMode === ContentManager.FADE_OUT) {
        this.fadeInterval = setInterval(this.callFadeOut, this.fadeDelay);
    }
    else {
        this.updateDivStyle(this.prevIndex, 0, 'hidden');
        this.updateDivStyle(this.curIndex, 2, 'visible');
        setTimeout(this.callStartTransition, this.objects[this.curIndex].delay)
    }
}

/* Performs the transition between images 
 * if the mouse is not over the ContentManager div.
 */
ContentManager.prototype.startTransition = function() {
    if(this.isMouseOver) {
        setTimeout(this.callStartTransition, 100);
    }
    else {
        this.prevIndex = this.curIndex;
        this.curIndex = (this.curIndex + 1) % this.objects.length;
        if(this.objects[this.curIndex].img) {
            this.transitionImage();
        }
        else {
            this.displayImage();
        }
    }
}

/* Initializes the ContentManager instance and 
 * writes the html for the ContentManager to the document.
 */
ContentManager.prototype.init = function() {
    document.write('<div id="cm_' + this.id  + '" style="position:relative;overflow:hidden;width:' + this.width + 'px;height:' + this.height + 'px">');
    if(this.objects.length == 1 && 'swf' === this.objects[0].type) {
        var o = this.objects[0];
        ContentManager.embedSWF(this.width, this.height, o.src + '?id=' + o.objID);
        document.write('</div>');
        this.generateImpression();
    }
    else {
        var i = 0;
        var self = this;
        
        for(i = 0; i < this.objects.length; i++) {
            this.objects[i].id = 'cm_' + this.id + '_ad_' + i;
            document.write('<div id="' + this.objects[i].id +'_div" style="position:absolute;top:0px;left:0px;z-index:0;"></div>');
        }
        document.write('</div>');
        
        var divElement = ContentManager.getElement('cm_' + this.id);
        var elements = divElement.childNodes;
        for(i = 0; i < elements.length; i++) {
            this.objects[i].div = elements[i];
        }
    
        divElement.onmouseout = function() {
            self.isMouseOver = false;
        };
        
        divElement.onmouseover = function() {
            self.isMouseOver = true;
        };
        
        this.callFadeIn = function() {
            self.fadeIn();
        };
        
        this.callFadeOut = function() {
            self.fadeOut();
        };
        
        if(this.objects.length > 1) {
            this.callStartTransition = function() {
                self.startTransition();
            };
        }
        else {
            this.callStartTransition = function() {
            };
        }
    
        this.displayImage();
    }
}

/* Adds an object to the current object group.
 * @param src the URL for the object
 * @param objID the ID for the object
 * @param order the object's order or 0 if the object's order should be random
 * @param delay the number of milliseconds the object is visible before a transition is started
 * @param target the target for the object's link
 */
ContentManager.prototype.addObject = function(src, objID, order, delay, target) {
    var type = src.match(/\.(\w+)$/);
    type = type ? type[1].toLowerCase() : 'unknown';
    this.objects[this.objects.length] = {
        "type"   : type,
        "src"    : src,
        "objID"  : objID,
        "order"  : (order ? order : -1),
        "delay"  : (delay ? delay : this.transitionDelay),
        "target" : (target ? target : this.target)
    }
}

/* Creates a new object group if there is at least one object in the current group. */
ContentManager.prototype.addGroup = function() {
    if(this.objects.length > 0) {
        this.objects = new Array();
        this.objectGroups[this.objectGroups.length] = this.objects;
    }
}

/* Randomly determines which object group is displayed
 * and the order in which the images in a group are displayed.
 * Should be called after all objects have been added to the
 * ContentManager but before the init function is called.
 */
ContentManager.prototype.randomizeObjects = function() {
    /* If there is more than one group of objects, randomly pick a group. */
    if(this.objectGroups.length > 1) {
        this.objects = this.objectGroups[Math.floor(Math.random() * this.objectGroups.length)];
    }
    
    /* If there is more than one object in the array,                */
    /* randomize the objects by sorting the array using Math.random. */
    if(this.objects.length > 1) {
        this.objects.sort(function(a, b) {
            if(a.order == -1 && b.order == -1) {
                return Math.random() - 0.5;
            }
            else if(a.order == -1) {
                return 1;
            }
            else if(b.order == -1) {
                return -1;
            }
            else {
                return a.order - b.order;
            }
        });
    }
}