/* 
4/25/2007 EAA

New js classes created for implementation of photo rotator changes in 12-Photos
*/
var foo = "bar";

//
// New class to represent a photo
//
function Photo(imageURL, imageWidth, imageHeight, description) {
     
    var image;
    var src, width,height;
    var that=this;
    var isloaded;
     
    src = imageURL;
    width = imageWidth;
    height = imageHeight;
    this.alt = description; //public
	

	//
	// get the URL of this photo (loaded or not!)
	this.getURL = function() {
	    return src;	
	}
	
	//
	// request the image from the server
	this.requestImage = function() {
	    if (!isloaded) {
	        image = new Image();
	        image.src = src;
	        isloaded = true;
	    }
	}
	
	//
	// has this photo been loaded yet?
	this.isLoaded = function() {
	    return isloaded;
	}
	
	// set an image element to this photo
	this.setPhoto = function(imgElement) {
	    if (!isloaded) {this.requestImage();}
	    imgElement.src = src;
	    imgElement.width = width;
	    imgElement.height = height;
	    imgElement.alt = this.alt;
	}
	
	
	 
}


 


//
// base delegate object
//
function Delegate(target, fn) {
    this.target = target;
    this.fn = fn;
}

//invoking a delegate here requires passing the sending object (a BasePhotoController)
// and the new index.
//
//some refactor would have to be done to create a flexible delegate functionality. But that's not needed, not yet.
//
Delegate.prototype.invoke = function(sender, newIndex) {
    this.fn.call(this.target, sender, newIndex);
}
 



//
// base class for both rotator and controller
// allows common set of "events" to be used
//

function BasePhotoController(keyID) {
    this.changeListenerDelegates = new Array();
    this.keyID = keyID;
    this.currentIndex = 0;
}


//
// Must override this in derivatives.
//
BasePhotoController.prototype.getCount = function (){
    return 0;
}

//
// Must overrides this in derivatives
//
BasePhotoController.prototype.updateDisplay = function() {
    //do nothing in base
}

//
// Notify any listeners by calling the delegate functions they supplied
// the rotator will supply itself and the current image index to the delegate
//
BasePhotoController.prototype.notifyChangeListeners = function() {
    for (var listenerindex in this.changeListenerDelegates) { //for a non-assoc array, this is the index
        var listener = this.changeListenerDelegates[listenerindex];
        if (listener && (typeof(listener) == "object") && (listener.constructor == Delegate)) {
            listener.invoke(this, this.currentIndex); //call the "delegate" function supplied
        } 
    }
}

//
// adds a new listener delegate to the collection
//
BasePhotoController.prototype.addChangeListenerDelegate = function(target,delegatefn) {
    if ((typeof(delegatefn) != "function") || (typeof(target) != "object")) {
        return 0;
    } else {
        this.changeListenerDelegates.push(new Delegate(target,delegatefn));
    }
}

//
// attaches as PhotoGrid to a Rotator, so they can control each other
//
BasePhotoController.prototype.attachPhotoController = function(controller) {
    if ((typeof(controller) != "object") || (controller.constructor != BasePhotoController)) {return 0;}
    
    //okay, they are the right kind of objects
    controller.addChangeListenerDelegate(this,this.externalControllerDelegate);
    this.addChangeListenerDelegate(controller,controller.externalControllerDelegate);
    
    this.notifyChangeListeners();
    
}

//
// All photo controllers must be able to move to a specified photo
//
BasePhotoController.prototype.moveTo = function(index) {
    
    if (index < 0 || index > this.getCount()-1) {return 0;} //invalid index - do nothing
    
    if (index != this.currentIndex) { //only do this if it changes, or else we might get an infinite event cascade!
        this.currentIndex=index;
        this.updateDisplay();
        this.notifyChangeListeners();
    }
}


//
// New PHOTOROTATOR class
//  Represents a single photo rotator, and it's capabilities, acting on an image
//
function PhotoRotator(keyID, imageElement, backElement, forwardElement, counterElement, descriptionElement, preloadAllPhotos){ //constructor
    
    //user can pass the object or it's ID, we'll figger it out here
    imageElement = typeof(imageElement) == "object" ? imageElement : document.getElementById(imageElement)
    backElement = typeof(backElement) == "object" ? backElement : document.getElementById(backElement)
    forwardElement = typeof(forwardElement) == "object" ? forwardElement : document.getElementById(forwardElement)
    counterElement = typeof(counterElement) == "object" ? counterElement : document.getElementById(counterElement)
    descriptionElement = typeof(descriptionElement) == "object" ? descriptionElement : document.getElementById(descriptionElement)
    
    this.changeListenerDelegates = new Array();
    this.photos = new Array();
    this.keyID = keyID;
    this.preloadAll = preloadAllPhotos ? true : false ;
    this.suppressprefetch = false;
     
        
    //image is the <image> element that shows the photos
    this.imageElement = imageElement;
    
    //backElement and forwardElement are clickable elements that trigger movement thru the photos
    this.backElement = backElement;
    if (this.backElement) {
        this.backElement.rotator = this;
        this.backElement.onclick = function() {this.rotator.movePrevious();}
    }
    
    this.forwardElement = forwardElement;
    if (this.forwardElement) {
        this.forwardElement.rotator = this;
        this.forwardElement.onclick = function() {this.rotator.moveNext();}
    }
    
    //counterElement will display the current position, if it exists.
    this.counterElement = counterElement;
    
    //descriptionElement will display the photo label
    this.descriptionElement = descriptionElement;
    
}


//inherits from BasePhotoController
PhotoRotator.prototype = new BasePhotoController(0);


//
// OVerride get Count
//
PhotoRotator.prototype.getCount = function () {
    return this.photos.length;
}

// Move to Next Photo
PhotoRotator.prototype.moveNext = function() {
    //check to see we have more than one photo. If not, exit.
	if (this.photos.length < 2) { return 0; }
	
    this.currentIndex = this.getNextIndex(); //move to next index, automatically resetting to 0 when it reaches the "end"
    this.updateDisplay();  
    this.notifyChangeListeners();  
    
   //now, preload the next image!
   if (!this.suppressprefetch) {
        this.photos[this.getNextIndex()].requestImage();
   }
    
}

//Move to Previous Photo
PhotoRotator.prototype.movePrevious = function() {
    //check to see we have more than one photo. If not, exit.
	if (this.photos.length < 2) { return 0; }
	
    this.currentIndex = this.getPreviousIndex(); //move to prev index, automatically resetting to count-1 when it reaches the "beginning"
    this.updateDisplay();
    this.notifyChangeListeners();
    
    
    //now, preload the previous image!
    if (!this.suppressprefetch) {
        this.photos[this.getPreviousIndex()].requestImage();  
    }
    
}

//
// Overrides base controller behavior
//
PhotoRotator.prototype.moveTo = function(index) {
    
    if (index < 0 || index > this.getCount()-1) {return 0;} //invalid index - do nothing
    
    if (index != this.currentIndex) { //only do this if it changes, or else we might get an infinite event cascade!
        this.currentIndex=index;
        this.updateDisplay();
        this.notifyChangeListeners();
    }
    
    if (!this.suppressprefetch) {
        //now, preload the next image!
        this.photos[this.getNextIndex()].requestImage();  
        //now, preload the previous image!
        this.photos[this.getPreviousIndex()].requestImage(); 
    }
}


//make these generic?
PhotoRotator.prototype.getNextIndex = function() {
    return (this.currentIndex + 1) % this.photos.length;
}

PhotoRotator.prototype.getPreviousIndex = function() {
    return ((this.currentIndex -1) + this.photos.length) % this.photos.length;
}
 


// get the photo count
PhotoRotator.prototype.getPhotoCount = function () {
    return this.photos.length;
}


// add a photo to the controller
PhotoRotator.prototype.addPhoto = function(photo) {
    if ((typeof(photo) == "object") && (photo.constructor == Photo)) {
        this.photos.push(photo);
        
        if (this.preloadAll) {photo.requestImage();}
    
        if (this.photos.length >= this.currentIndex+1) {
            this.updateDisplay();
        } //put it in sync
    }
}
//
//Update the display (image,counter and descriptions)
//overrides base function
//
PhotoRotator.prototype.updateDisplay = function() {
    //show the current photo
    var currentPhoto = this.photos[this.currentIndex];
    if (currentPhoto) {
        if (this.imageElement) {
            currentPhoto.setPhoto(this.imageElement);
        }
    	
	    //set description tag
	    if (this.counterElement) {this.counterElement.innerHTML = (this.currentIndex + 1) + ' of ' + this.getPhotoCount();}
	    if (this.descriptionElement) {this.descriptionElement.innerHTML = currentPhoto.alt;}
	}
	
	
}


// reset the control
PhotoRotator.prototype.reset = function () {
    this.currentIndex=0;
    this.updateDisplay();
    this.notifyChangeListeners();
}


PhotoRotator.prototype.externalControllerDelegate = function(sender,index) {

    this.moveTo(index);
    
}





//-------------------------------------------------------------------------------------------------


/*

New PhotoThumbnailControl class
Represents the photogrid functionality

*/
function PhotoThumbnailControl(keyid,selectedimagestyleclass, unselectedimagestyleclass){
    this.selectedstyle = selectedimagestyleclass;
    this.unselectedstyle = unselectedimagestyleclass;    
    this.thumbnails = new Array();
    this.changeListenerDelegates = new Array();
    this.currentTN = null;
    this.keyID = keyid;
}


//inherits from BasePhotoController
PhotoThumbnailControl.prototype = new BasePhotoController(0);
 
 
//
// OVerride get Count
//
PhotoThumbnailControl.prototype.getCount = function () {
    return this.thumbnails.length;
}
 
//add a thumbnail to the controller
PhotoThumbnailControl.prototype.addThumbnail = function(thumbnail) {
    var tn;
    tn = typeof(thumbnail) == "object" ? thumbnail : document.getElementById(thumbnail);
    
    if (typeof tn == "object") {
        this.thumbnails.push(tn);
        tn.photothumbnailcontrol = this;
        tn.photothumbnailindex = this.thumbnails.length-1;
        tn.onclick = function() {this.photothumbnailcontrol.moveTo(this.photothumbnailindex);} // 
        if (tn.photothumbnailindex == this.selectedIndex) {
            this.currentTN = tn;
        }
    }
}

//
//overrides base function
//
PhotoThumbnailControl.prototype.updateDisplay = function() {
    //no need to change anything here. Always in sync when moveTo is called.
}

PhotoThumbnailControl.prototype.moveTo = function(index) {

    //if out of range, just return
    if ((index < 0) || (index > this.thumbnails.length-1)) {return 0;}
    
    if (index == this.currentIndex) {return 0;}
    
    
    var oldtn = this.currentTN;
    this.currentIndex = index;
    this.currentTN = this.thumbnails[this.currentIndex];
    
    if (oldtn) {oldtn.className = this.unselectedstyle;}
    if (this.currentTN) {this.currentTN.className = this.selectedstyle; }
     
    this.notifyChangeListeners();
    
}

PhotoThumbnailControl.prototype.externalControllerDelegate = function(sender,index) {

    
    this.moveTo(index);
   

}




//-------------------------------------------------------------------------------------------------

//
//New PHOTOROTATORMASTER class
// maintains state info across pages
// this is a SINGLETON 


var PhotoControllerMaster = new function() {
  
    //Private variables!
    var state = new Object();
    var cookiename = "PhotoControllerMasterState";
    var preservedState = null;
    var startState = null;
    var controllers = new Object();
    //
    //This is used to make the object available to the private methods.
    //This is a workaround for an error in the ECMAScript Language Specification which causes 'this' to be set incorrectly for inner functions.
    var that = this; 

    //public variables/properties
    //
    // overrideChangesFromChildren controls whether or not changes flow "backward". They always carry forward (next screen, next child document)
     
    this.ignoreChangesFromChildren = true;
    
    //hook the document UNLOAD event
    window.onunload = function() {PhotoControllerMaster.setStateForUnload()};
    window.onblur = function() {PhotoControllerMaster.setStateForUnload()};
     
    
    //unload function
    this.setStateForUnload = function() {
        if (that.ignoreChangesFromChildren) {
            saveState(); //overwrites anything the children put there with this object's state, effectively ignoring what they did
            applyState(state, true); //just make sure the controllers are up to date.
        }
    }
    
    
    
    //privileged function 
    //reset
    //clears state and resets all controllers to first position
    this.reset = function() {
        state = new Object();    
        for (var controllerid in controllers) {
            if (controllers.hasOwnProperty(controllerid)) {
                var controller = controllers[controllerid];
                if ((controller.moveTo)) {
                    controllers[controllerid].moveTo(0); //all basecontrollers have moveTo(), this sets everyone to the first photo
                }
            }
        }
        saveState();
    }
    
    //
    // function to preserve state for later recall
    //
    this.preserveState = function() {
        preservedState = state;
    }
    
    //
    // function to recall preserved state 
    //
    this.recallState = function() {
        state = preservedState;
        saveState();
        //update any controllers in the master already
        applyState(state, false);
    }
    
    
     
    //
    // privileged function 
    // Add a controller to be monitored by the MASTER
    //
    this.addController = function(controller) {
         
        if ((typeof(controller) != "object") || (controller.constructor != BasePhotoController)) {return 0;}
        
        controllers[controller.keyID] = controller; //add the rotator the the list of rotators being watched
        controller.addChangeListenerDelegate(that,that.controllerChangedDelegate);
        
        //if this rotator's ID is already in stored state, then move the rotator to that image
        if (state.hasOwnProperty(controller.keyID)) {
            controller.moveTo(state[rotator.keyID]);
        } else {
            controller.moveTo(0);
        }

    }

    //
    // privileged function
    // the rotator will call this when it changes. think of this as a delegate function or callback.
    //
    this.controllerChangedDelegate = function(controller,index) {
        
        if ((typeof(controller) != "object") || (controller.constructor != BasePhotoController)) {return 0;}
        state[controller.keyID] = index;
        saveState();
    }
    
    
       
    //
    // private function
    // save state  
    //
    function saveState() {
        //build the JSON value
         var cookiepath = "/";
        if (Object.prototype.toJSONString) { //are the JSON functions defined?
            var JSONstate = state.toJSONString(); 
            document.cookie = cookiename + '=' + escape(JSONstate) + '; path=' + cookiepath;
        }
    }

        
    //
    // Restore state that may have been saved on last screen
    // Private function. No one else can call.
    //
    function restoreState() {
        //get the cookie
        var allcookies = document.cookie;
         
        var pos = allcookies.indexOf(cookiename + "=");
        var stateJSON;
        
        if (Object.prototype.toJSONString) {
        
            if (pos != -1) {
                var start=pos + cookiename.length + 1;
                var end = allcookies.indexOf(";",start);
                if (end == -1) end = allcookies.length;
                var tmp = allcookies.substring(start,end);
                stateJSON = unescape(tmp);
                state=stateJSON.parseJSON();
            }
        }
        
        startState = state; //remember what it was
           
        applyState(state, false);
    } 
    
    function applyState(s, unloading) {
        //update any rotators in the master already
        
        for (var controller in controllers) {
            if (controllers.hasOwnProperty(controller)) {
                controllers[controller].suppressprefetch = unloading;
                if (s.hasOwnProperty(controller)) {
                    controllers[controller].moveTo(s[controller]);
                } else {
                    controllers[controller].moveTo(0);
                }
                controllers[controller].suppressprefetch = false;
            }
        }
        
//        for (var controller in s) {
//            if (that.hasOwnProperty(controller)) {
//                controllers[controller].moveTo(s[controller]);
//            } else {
//                that
//            }
//        }    
    }

 
 
    //init code
    restoreState();  
    
      
}
   
   
 
 




 








