Gallery = function(config) {
    this.browser = new Browser();
    this.config = config;
    this.index = 0;
    
    if (this.config.initial == null) {
        //default to image
        this.config.initial = Gallery.TYPE_IMAGE;
    }
    this.setMediaType(this.config.initial);
    this.setShowingAlt(false);
    
    this.setupImages();
    this.setupMovies();
    this.setupFX();
}

Gallery.FX_NONE = 0;
Gallery.FX_PAN = 1;
Gallery.FX_FADE = 2;

Gallery.TYPE_IMAGE = 0;
Gallery.TYPE_MOVIE = 1;

Gallery.prototype.setupImages = function() {
    //load images
    if (this.hasImages()) {
        for (var i=0; i<this.config.images.length; i++) {
            //there may not be any thumbs
            if (this.config.images[i].thumb != null) {
                var image = new Image();
                image.src = this.config.images[i].thumb;    
                this.config.images[i].thumbImage = image;
            }
            //pre load full images
            if (this.config.images[i].full != null) {
                var image = new Image();
                image.src = this.config.images[i].full;                
                this.config.images[i].fullImage = image;                
            }
        }    
    }
}

Gallery.prototype.setupMovies = function() {
    if (this.hasMovies()) {
        //generate xml for qt, if javascript to load QT is loaded
        if (Gallery.isDefined(window, "_QTGenerate")) {
            //if we have not defined the element id for full qt then make up one
            if (this.config.movie.full == null) {
                this.config.movie.full = new Object();
            }
            if (this.config.movie.full.elementId == null) {
                this.config.movie.full.elementId = this.config.viewer.elementId + "-QT";
            }

            //setup movie data
            for (var i=0; i<this.config.movies.length; i++) {
                if (this.config.movies[i].thumb != null) {  
                    var image = new Image();
                    image.src = this.config.movies[i].thumb;    
                    this.config.movies[i].thumbImage = image;                
                }
                if (this.config.movies[i].full != null) {  
                   var movie = this.config.movies[i];
                   //search args for defined id/name
                   var movieId = Gallery.findIdName(movie.full);
                   if (movieId == null) {
                       movieId = this.config.movie.full.elementId + i;
                       //add our generated one to args list
                       movie.full.push("id");
                       movie.full.push(movieId);
                   }
                   //save for fast lookup
                   movie.elementId = movieId;
                   
                   //force javascript                   
                   movie.full.push("enablejavascript");
                   movie.full.push("true");

                   //generate the object tag
                   movie.xml = _QTGenerate("Gallery", true, movie.full);
                }
            }
        } else {
            throw "QT Javascript Not Available";
        }
    }    
}

Gallery.prototype.setupFX = function() {
    if (this.config.fade != null) {
           this.fader = new CrossFade(this.config.fade.speed, this.config.fade.increment);
       }
       if (this.config.swoosh != null) {
           this.swoosher = new Swoosh(this.config.viewer.elementId, 
                                      this.config.swoosh.speed, 
                                      this.config.swoosh.increment);
       }
}

Gallery.prototype.next = function() { 
    if (this.isShowingImage()) {   
        this._showImage(this.index + 1, Gallery.FX_PAN);    
    } else {
        this._showMovie(this.index + 1, Gallery.FX_NONE);    
    }
}

Gallery.prototype.previous = function() {    
    if (this.isShowingImage()) {   
        this._showImage(this.index - 1, Gallery.FX_PAN);    
    } else {
        this._showMovie(this.index - 1, Gallery.FX_NONE);    
    }    
}

Gallery.prototype.image = function(idx) {
    if (!this.isShowingImage()) {
        this.swapMedia();
    }
    this._showImage(idx, Gallery.FX_FADE);
}

Gallery.prototype.movie = function(idx) {
    if (!this.isShowingMovie()) {
        this.swapMedia();
    }            
    this._showMovie(idx, Gallery.FX_NONE);
}

Gallery.prototype.hasImages = function() {
    return this.config.images != null && this.config.images.length > 0;
}

Gallery.prototype.hasMovies = function() {
    return this.config.movies != null && this.config.movies.length > 0;
}

Gallery.prototype.isShowingImage = function() {
    return (this.mediaType == Gallery.TYPE_IMAGE);
}

Gallery.prototype.isShowingMovie = function() {
    return (this.mediaType == Gallery.TYPE_MOVIE);
}

Gallery.prototype.setMediaType = function(type) {
    this.mediaType = type;
}

Gallery.prototype.swapMedia = function() {
    this.stopAnimations();
    var movie = this.getMovieElement();
    var image = this.getActiveImage();
    if (this.isShowingImage()) {        
        image.style.visibility = "hidden";
        movie.innerHTML = "";
        movie.style.visibility = "visible";
        this.changeThumbImageState(Gallery.TYPE_IMAGE, this.index, -1);   
        this.setMediaType(Gallery.TYPE_MOVIE);
    } else {
        image.style.visibility = "visible";
        movie.innerHTML = "";
        movie.style.visibility = "hidden";
        this.changeThumbImageState(Gallery.TYPE_MOVIE, this.index, -1);             
        this.setMediaType(Gallery.TYPE_IMAGE);
    }
    this.index = -1;
}

Gallery.prototype._showMovie = function(idx, fx) {    
    if (idx == this.index) {
        return;
    } 
        
    if (!this.hasMovies()) {
        return;
    }
    
    var oldIndex = this.index;
    
    if (idx >= this.config.movies.length) {
        idx = 0;
    } else if (idx < 0) {
        idx = this.config.movies.length - 1;
    }
    
    this.changeThumbImageState(Gallery.TYPE_MOVIE, oldIndex, idx);
    //stop any animations that are executing
    this.stopAnimations();
    var movieDiv = this.getMovieElement(); 
    movieDiv.style.visiblity = "hidden";  
    try { 
        //kill the previous movie
        var currentMovie = null;
        if (oldIndex >= 0) {
            currentMovie = document.getElementById(this.config.movies[oldIndex].elementId);
        }
        if (currentMovie != null) {
            currentMovie.Stop();
            currentMovie.style.display = "none";
            Gallery.removeChildren(movieDiv);     
            movieDiv.innerHTML = "";
        }
    } catch (err) { 
        movieDiv.innerHTML = ""; 
    }
    movieDiv.innerHTML = this.config.movies[idx].xml;
    //ensure it is visible
    movieDiv.style.visiblity = "visible";
    
    this.index = idx;    
}

Gallery.prototype._showImage = function(idx, fx) {
    if (idx == this.index) {
        return;
    }
        
    if (!this.hasImages()) {
        return;
    }
    
    var oldIndex = this.index;
    
    //set pan direction before adjusting index for loop around
    var panRight = (idx >= oldIndex);
         
    if (idx >= this.config.images.length) {
        idx = 0;
    } else if (idx < 0) {
        idx = this.config.images.length - 1;
    }
    
    this.changeThumbImageState(Gallery.TYPE_IMAGE, this.index, idx);     
    //stop any animations that are executing
    this.stopAnimations();
    if (fx == Gallery.FX_FADE) {
        if (this.fader) {
            this.fadeImage(this.config.images[idx].fullImage.src);
        } else {
            this.getActiveImage().src = this.config.images[idx].fullImage.src;
        }
    } else if (fx == Gallery.FX_PAN) {
        if (this.swoosher) {
            this.panImage(panRight, this.config.images[idx].fullImage.src);
        } else {
            this.getActiveImage().src = this.config.images[idx].fullImage.src;
        }
    }
    
    this.index = idx;    
}

Gallery.prototype.getActiveImage = function() {
    if (this.isShowingAlt()) {
        return this.getAltFullImage();
    } else {
        return this.getFullImage();
    }
}

Gallery.prototype.stopAnimations = function() {
    if (this.fader != null) {
        this.fader.stopAnimation();
    }
    if (this.swoosher != null) {
        this.swoosher.stopAnimation();
    }
}

Gallery.prototype.isShowingAlt = function() {
    return this.showingAlt;
}

Gallery.prototype.setShowingAlt = function(val) {
    return this.showingAlt = val;
}

Gallery.prototype.fadeImage = function(newUrl) {
    var showAlt = !this.isShowingAlt();
    //setup state for next transition
    this.setShowingAlt(showAlt);
    
    var fromImage = null;
    var toImage = null;
    if (showAlt) {
        fromImage = this.getFullImage();
        toImage = this.getAltFullImage();
    } else {
        fromImage = this.getAltFullImage();
        toImage = this.getFullImage();
    }
    toImage.src = newUrl;
    this.fader.fade(fromImage, toImage);
}

Gallery.prototype.panImage = function(right, newUrl) {
    var showAlt = !this.isShowingAlt();
    //setup state for next transition
    this.setShowingAlt(showAlt);
    
    var fromImage = null;
    var toImage = null;
    if (showAlt) {
        fromImage = this.getFullImage();
        toImage = this.getAltFullImage();
    } else {
        fromImage = this.getAltFullImage();
        toImage = this.getFullImage();
    }
    toImage.src = newUrl;
    this.swoosher.pan(right, fromImage, toImage);
}

Gallery.prototype.changeThumbImageState = function(type, prevIndex, nextIndex) {
    var prevImg = this.getThumbAnchor(type, prevIndex);
    var nextImg = this.getThumbAnchor(type, nextIndex);  

    if (prevImg != null) {  
        prevImg.className = this.config.image.thumb.cssClass;
    }
    if (nextImg) {
        nextImg.className = this.config.image.thumb.cssActiveClass;
    }
}

Gallery.prototype.getFullImage = function() {
    return document.getElementById(this.config.image.full.elementId);
}

Gallery.prototype.getAltFullImage = function() {
    var altImageName = this.config.image.full.elementId + "Alt";
    var altImage = document.getElementById(altImageName);
    if (altImage == null) {
        //create alt image
        var image = this.getFullImage();
        altImage = image.cloneNode(false);
        altImage.id = altImageName;
        var viewer = this.getViewer();
        //add alt image to DOM
        viewer.appendChild(altImage);
    }
    return altImage;
}

Gallery.prototype.getMovieElement = function() {
    var qtDivId = this.config.movie.full.elementId;
    var qtDiv = document.getElementById(qtDivId);
    if (qtDiv == null) {
        //create movie div
        var qtDiv = document.createElement("div");
        qtDiv.id = qtDivId;
        qtDiv.style.position = "absolute";
        qtDiv.style.display = "block";
        qtDiv.style.left = "0px";
        qtDiv.style.top = "0px";        
        var viewer = this.getViewer();
        //add alt image to DOM
        viewer.appendChild(qtDiv);
    }
    return qtDiv;
}

Gallery.prototype.getViewer = function() {
    return document.getElementById(this.config.viewer.elementId);
}

Gallery.prototype.getThumbAnchorId = function(type, index) {
    var id = null;
    if (type == Gallery.TYPE_IMAGE) {
        id = this.config.image.thumb.elementId + index;
    } else {
        id = this.config.movie.thumb.elementId + index;
    }
    return id;
}

Gallery.prototype.getThumbAnchor = function(type, index) {
    return document.getElementById(this.getThumbAnchorId(type, index));
}

Gallery.prototype.getThumbImage = function(type, index) {
    var image = null;
    if (type == Gallery.TYPE_IMAGE) {
        image = this.config.images[index].thumbImage;
    } else {
        image = this.config.movies[index].thumbImage;
    }
    return image; 
}

Gallery.prototype.outputImageThumbs = function() {
    if (this.hasImages()) {
        var parent = null;
        parent = document.getElementById(this.config.image.thumb.elementId);
        for (var i=0; i<this.config.images.length; i++) {                        
            var wrapper = new ClickWrapper(this, i, Gallery.TYPE_IMAGE);
            this.createThumb(parent, 
                this.getThumbAnchorId(Gallery.TYPE_IMAGE, i), 
                this.config.images[i].thumbImage.src,
                false, 
                this.config.image.thumb, 
                wrapper);
        }
        
        if (this.isShowingImage()) {
            this.index = -1;
            this._showImage(0, Gallery.FX_NONE);
        }
    }
}

Gallery.prototype.outputMovieThumbs = function() {
    if (this.hasMovies()) {
        var parent = null;
        parent = document.getElementById(this.config.movie.thumb.elementId);
        for (var i=0; i<this.config.movies.length; i++) {
            var wrapper = new ClickWrapper(this, i, Gallery.TYPE_MOVIE);
            this.createThumb(parent, 
                this.getThumbAnchorId(Gallery.TYPE_MOVIE, i), 
                this.config.movies[i].thumbImage.src,
                false, 
                this.config.movie.thumb, 
                wrapper);
        }
        
        if (this.isShowingMovie()) {
            this.index = -1;            
            this._showMovie(0, Gallery.FX_NONE);
        }        
    }
}

Gallery.prototype.createThumb = function(parent, id, src, isActive, definition, clickWrapper) {    
    //generate anchor element
    var anchor = this.createAnchorNode(id, isActive, definition.cssActiveClass, definition.cssClass);    
    //setup onclick to pass through to anchorLink  
    anchor.onclick = function(){clickWrapper.onClick()};
    
    //generate image element
    var image = this.createImageNode(src, definition.width, definition.height);
    //add image
    anchor.appendChild(image);
    //add anchor
    parent.appendChild(anchor);        
}

Gallery.prototype.createImageNode = function(src, width, height) {
    /*
    Using createElement instead of new Image so
    that appendChild can be used to add the image
    to the DOM.
    */
    var image = document.createElement("img");
    image.style.height = height + "px";
    image.style.width = width + "px";
    //The thumb may not be defined, if not then don't output src. 
    //Some uses allow for thumbs to be ommitted but if they decide to use
    //this function thumbs are required and they will see broken image.
    image.src = src;
    return image;
}

Gallery.prototype.createAnchorNode = function(id, isActive, activeClass, inactiveClass) {
    var anchor = document.createElement("a");
    //set the id
    anchor.id = id;
    if (isActive) {
        if (activeClass != null) {
            anchor.className = activeClass;
        }
    } else {
        if (inactiveClass != null) {
            anchor.className = inactiveClass;
        }        
    }
    return anchor;
}

Gallery.setOpacity = function(image, val) {
    // Safari<1.2, Konqueror
    image.style.KHTMLOpacity = val;
    // Older Mozilla and Firefox 
    image.style.MozOpacity = val; 
    // Safari 1.2, newer Firefox and Mozilla, CSS3
    image.style.opacity = val; 
    // IE
    image.style.filter = "progid:DXImageTransform.Microsoft.Alpha(opacity="+Math.round(val*100)+")";
    
}

Gallery.isDefined = function(obj, name){
    var p = name.split(".");
    for(var i = 0; i < p.length; i++){
        if(!(obj[p[i]])) return false;
        obj = obj[p[i]];
    }
    return true;
}

Gallery.findIdName = function(args) {
    for ( var ndx = 0; ndx < args.length; ndx += 2) {
        attrName = args[ndx].toLowerCase();
        attrValue = args[ndx + 1];

        // "name" and "id" should have the same value, the former goes in the embed and the later goes in
        //  the object. use one array slot 
        if ( "name" == attrName || "id" == attrName ) {
            gTagAttrs["name"] = attrValue;
            return attrValue;
        }
    }
    return null;
}

Gallery.removeChildren = function(node) {
    var count = node.childNodes.length;
    while(node.hasChildNodes()) { 
        node.removeChild(node.firstChild); 
    }
    return count;
}

ClickWrapper = function(gallery, index, type) { 
    this.gallery = gallery;
    this.index = index;
    this.type = type;
}

ClickWrapper.prototype.onClick = function() {
    if (this.type == Gallery.TYPE_IMAGE) {
        this.gallery.image(this.index);
    } else {
        this.gallery.movie(this.index);
    }
}

CrossFade = function(fadeSpeed, fadeIncrement) {
    // animation speed in ms (smaller is faster)
    if (fadeSpeed == null) {
        fadeSpeed = CrossFade.DEFAULT_SPEED;
    }
    
    if (fadeIncrement == null) {
       fadeIncrement = CrossFade.DEFAULT_INCREMENT;
    }
    
    this.fadeSpeed = fadeSpeed; 
    // fade speed (steps between 0.0 --> 1.0)
    this.fadeInc = fadeIncrement; 
    this.fadeStep = 0.0;
}

CrossFade.DEFAULT_SPEED = 8;
CrossFade.DEFAULT_INCREMENT = 0.05;

CrossFade.prototype.fade = function(fromImage, toImage) {
    this.fadeStep = 0.0;

    toImage.style.visibility = "visible";    
    toImage.style.top = "0px";
    toImage.style.left = "0px";
    Gallery.setOpacity(toImage, 0.0);
    
    fromImage.style.visibility = "visible";    
    fromImage.style.top = "0px";
    fromImage.style.left = "0px";
    Gallery.setOpacity(fromImage, 1.0);
    
    this._fade(fromImage, toImage);
}

CrossFade.prototype._fade = function(oldImg, newImg) {
    if (this.timer) { 
        clearTimeout(this.timer); 
    }
    this.fadeStep = this._round(this.fadeStep + this.fadeInc);
    if (this.fadeStep < 1.0) { 
        // still working on cross-fade
        var fadeInOpac = this.fadeStep;
        Gallery.setOpacity(newImg, fadeInOpac);
        
        var fadeOutOpac = this._round(1.0 - this.fadeStep);        
        Gallery.setOpacity(oldImg, fadeOutOpac);
        
        var self = this;
        this.timer = setTimeout(function() { self._fade(oldImg, newImg) }, this.fadeSpeed);
    } else { 
        // finished the cross-fade
        this._finishFade(oldImg, newImg);
    }
}

CrossFade.prototype._finishFade = function(oldImg, newImg) {
    if (this.timer) { 
        clearTimeout(this.timer); 
    }
    oldImg.style.visibility = "hidden";   
    Gallery.setOpacity(newImg, 1.0);  
}

CrossFade.prototype._round = function(val) {
    //round to 3 decimal places
    return Math.round(val*1000)/1000
}

CrossFade.prototype.stopAnimation = function() {
    if (this.timer) {
        clearTimeout(this.timer); 
    }
}

Swoosh = function(viewerId, panSpeed, panIncrement) {
    this.viewerId = viewerId;
    if (panSpeed == null) {
        panSpeed = Swoosh.DEFAULT_PAN_SPEED;
    }
    if (panIncrement == null) {
        panIncrement = Swoosh.DEFAULT_PAN_INCREMENT;
    }
    this.panSpeed = panSpeed;
    this.panIncrement = panIncrement;
}

Swoosh.DEFAULT_PAN_SPEED = 20;
Swoosh.DEFAULT_PAN_INCREMENT = 40;

Swoosh.prototype.getViewer = function() {
    return document.getElementById(this.viewerId);
}

Swoosh.prototype.pan = function(right, oldImage, newImage) {
    var viewWidth = parseInt(this.getViewWidth());

    oldImage.style.visibility = "visible";    
    oldImage.style.top = "0px";
    oldImage.style.left = "0px";
    Gallery.setOpacity(oldImage, 1.0);  
    
    newImage.style.visibility = "visible";        
    newImage.style.top = "0px";    
    newImage.style.left = (right ? viewWidth : -viewWidth) + "px";
    Gallery.setOpacity(newImage, 1.0);  
    
    this._pan(Math.PI, viewWidth, right, oldImage, newImage);    
}

Swoosh.prototype.getViewWidth = function() {
    return this.getViewer().style.width;
}

Swoosh.prototype._pan = function(radians, width, right, oldImage, newImage) {
    if (this.timer) {
        clearTimeout(this.timer);
    }
    
    radians += Math.PI / this.panIncrement;
    if (radians <= (2 * Math.PI)) { // pan in progress
        var x = Math.floor((width/2) * (1.0 + Math.cos(radians)));
        if (right) { 
            x = -x; 
        }
        oldImage.style.left = x + "px";
        newImage.style.left = (x + (right ? width : -width)) + "px";
        var self = this;
        this.timer = setTimeout(function() { self._pan(radians, width, right, oldImage, newImage) }, 
                                this.panSpeed);
    } else {
        this._finishPan(oldImage, newImage);
    }
}

Swoosh.prototype._finishPan = function(oldImage, newImage) {
    if (this.timer) { 
        clearTimeout(this.timer); 
    }    
    oldImage.style.visibility = "hidden";     
}

Swoosh.prototype.stopAnimation = function() {
    if (this.timer) {
        clearTimeout(this.timer); 
    }
}

Browser = function() {
    this.OSName = Browser.UNKNOWN;
    this.bName = Browser.UNKNOWN;
    
    if (navigator.appVersion.indexOf(Browser.MAC) > 0) {
        this.OSname = Browser.MAC;  
    }  else if (navigator.appVersion.indexOf(Browser.WIN) > 0) {
        this.OSname = Browser.WIN;
    }

    if (navigator.appName != null) {
        if (navigator.appName.length >= 7 && navigator.appName.substring(0,8) == Browser.NETSCAPE) {
            this.bName = Browser.NETSCAPE;
        } else if (navigator.appName.length >= 8 && navigator.appName.substring(0,9) == Browser.MICROSOFT) {
            this.bName = Browser.IE;
        }
    }

}

Browser.MAC = "Mac";
Browser.WIN = "Win";
Browser.UKNOWN = "unknown";
Browser.NETSCAPE = "Netscape";
Browser.MICROSOFT = "Microsoft";
Browser.IE = "IE";
Browser.QUICKTIME = "QuickTime";

Browser.prototype.isMac = function() {
    return (Browser.MAC == this.OSName);
}

Browser.prototype.isIE = function() {
    return (Browser.IE == this.bName);
}

Browser.prototype.hasQuickTime = function() {    
    var hasQT = false;
    vbHasQT = false;

    if (navigator.plugins != null && navigator.plugins.length) {
        for (i=0; i < navigator.plugins.length; i++ ) {
            if (navigator.plugins[i].name.indexOf(Browser.QUICKTIME) >= 0) { 
                hasQT = true; 
            }
        }
    } else {
        execScript('on error resume next: vbHasQT = IsObject(CreateObject("QuickTimeCheckObject.QuickTimeCheck.1"))','VBScript');
        hasQT = vbHasQT;
    }
    return hasQT;
}
