/**
 * Creates a previewing layer object which automatically positions itself based on the parent's
 * position and size. Styles are included via CSS.
 * 
 * Part of the Scout Portal Toolkit
 * Copyright 2004 Internet Scout Project
 * http://scout.wisc.edu
 *
 * @author Tim Baumgard
 */
function Previewer(parent, title) {

    /**
     * @var element Parent object initiating Previewer
     * @var element Previewer object that displays previewed content
     * @var element Pointer pointer object
     * @var element PointerLeft holds the file name of the left pointer
     * @var element PointerRight holds the file name of the right pointer
     * @var element Title element holding the title
     * @var element Content content layer
     * @var element Buttons buttons layer
     * @var boolean Effects use visual effects (REQUIRES jQUERY) 
     * @var boolean Visibility true for visible, false for invisible
     */
    var Parent;
    var Previewer;
    var Pointer;
    var PointerLeft;
    var PointerRight;
    var Title;
    var Content;
    var Buttons;
    var Effects;
    var Visibility;
    
    /**
     * --- PUBLIC METHODS ---
     */
     
    /**
     * Set a new parent. Useful for using the previewer with multiple children.
     *
     * @author Tim Baumgard
     * @param element parent new parent element
     */
    this.setParent = function(parent) {
        // If id was passed
        if ("string" === typeof parent) {
            parent = document.getElementById(parent);
        }
        
        // Make sure parent is an element
        if ("object" !== typeof parent || !parent.nodeName) {
            return;
        } else {
            reposition();
            Parent = parent;
        }
    }
    
    /**
     * Set the title.
     *
     * @author Tim Baumgard
     * @param string title new title
     */
    this.setTitle = function(title) {
        // Return if title is not a string
        if ("string" !== typeof title) {
            return;
        }
        
        // Set title, reposition if necessary
        Title.innerHTML = title;
        reposition();
    }
    
    /**
     * Set the pointer image source.
     *
     * @author Tim Baumgard
     * @param string source URL to the pointer image
     */
    this.setPointer = function(left, right) {
        // Return if source is not a string
        if ("string" !== typeof left || "string" !== typeof right) {
            return;
        }
        
        // Needs Pointer.src otherwise Safari doesn't return correct offsetWidth when positioning
        Pointer.src = PointerLeft = left;
        PointerRight = right;
    }
    
    /**
     * Adds content to the previewer.
     *
     * @param mixed content content to add to the previewer
     * @param boolean truncate true to truncate content before adding new data
     */
    this.addContent = function(content, truncate) {
        // Return if content is invalid
        if ("undefined" === typeof content) {
            return;
        }
    
        // Truncate if necessary
        if ("boolean" === typeof truncate && truncate === true) {
            Content.innerHTML = "";
        }
        
        // Add content and reposition if necessary
        Content.innerHTML += content;
        reposition();
    }
    
    /**
     * Add a button to the buttons layer.
     *
     * @author Tim Baumgard
     * @param string text text to display on the button
     * @param string action javascript to run when the button is clicked
     */
    this.addButton = function(text, action) {
        // Validate input, return if necessary
        if ("string" !== typeof text || "string" !== typeof action) {
            return;
        }
    
        // Create button and attributes
        var button = document.createElement("button");
        var buttonText = document.createTextNode(text);
        button.style.marginLeft = "5px";
        button.style.marginRight = "5px";
        button.onclick = new Function(action);
        button.appendChild(buttonText);
        
        // Add button to button div and reposition if necessary
        Buttons.appendChild(button);
        reposition();
    }
    
    /**
     * Remove all the buttons from the button layer.
     *
     * @author Tim Baumgard
     */
    this.clearButtons = function() {
        Buttons.innerHTML = "";
    }
    
    /**
     * Set whether or not to use visual effects.
     *
     * @author Tim Baumgard
     * @param boolean effects true to turn effects on, false to turn off
     */
    this.useEffects = function(effects) {
        if ("boolean" !== typeof effects) {
            Effects = true;
            return;
        }
        Effects = effects;
    }
    
    /**
     * Toggle the previewer object's visibility.
     *
     * @author Tim Baumgard
     * @param boolean visibility true to show, false to hide
     */
    this.toggleDisplay = function(visibility) {   
        if (Visibility) {
            // Hide previewer
            if (Effects) {
                $(Previewer).fadeOut(100);
                $(Pointer).fadeOut(100);
            } else {
                Previewer.style.display = "none";
                Pointer.style.display = "none";
            }
        } else {
            // Show and reposition previewer
            if (Effects) {
                $(Previewer).fadeIn(100);
                $(Pointer).fadeIn(100); 
            } else {
                Previewer.style.display = "block";
                Pointer.style.display = "block";
            }
            reposition();
        }
        
        // Toggle visibility variable
        Visibility = !(Visibility);
    }
    
    /**
     * Show the previewer object.
     *
     * @author Tim Baumgard
     */
    this.show = function() {
        // Toggle variable, make visible
        Visibility = false;
        this.toggleDisplay();
        reposition();
    }
    
    /**
     * Hide the previewer object.
     *
     * @author Tim Baumgard
     */
    this.hide = function() {
        // Toggle variable, make hidden
        Visibility = true;
        this.toggleDisplay();
    }
    
    /**
     * PRIVATE METHODS
     */
    
    /**
     * Get the (X,Y) position of the given element.
     *
     * @author Tim Baumgard
     * @param element element element or id thereof
     */
    function getPositionOf(element){
        // If an Id was passed or element doesn't exist, return (0,0) if necessary
        if ("string" === typeof element){
            element = document.getElementById(element);
        } else if ("object" !== typeof element || !element.nodeName) {
            return {"X":0, "Y":0};
        }
        
        // Get position
	    var X = Y = 0;
        if (element.offsetParent) {
            do {
                X += element.offsetLeft;
                Y += element.offsetTop;
            } while (element = element.offsetParent);
        }
        
        // Return position
        return {"X":X, "Y":Y};
    }
    
    /**
     * Get the size of the window/viewport (not the size of the page).
     *
     * @author Tim Baumgard
     */
    function getWindowSize() {
        // (Mozilla/Netscape/Opera/IE7)
        if ("undefined" !== typeof window.innerWidth) {
            return {height:window.innerHeight, width:window.innerWidth};
        } else if ("undefined" !== typeof document.documentElement
            && "undefined" !== typeof document.documentElement.clientWidth
            && document.documentElement.clientWidth != 0) { // IE6
            return {
                height:document.documentElement.clientHeight,
                width:document.documentElement.clientWidth
            };
        } else { // IE < 6
            return {
                height: document.getElementsByTagName("body")[0].clientHeight,
                width:document.getElementsByTagName("body")[0].clientWidth
            };
        }
    }
    
    /**
     * Get the scroll offsets of the window.
     *
     * @author Tim Baumgard
     */
    function getScrollOffsets() {
        if ("number" === typeof window.pageYOffset) { // Netscape
            return {"X":window.pageXOffset, "Y":window.pageYOffset};
        } else if (document.body && (document.body.scrollLeft || document.body.scrollTop)) { // DOM
            return {"X":document.body.scrollLeft, "Y":document.body.scrollTop};
        } else if (document.documentElement
            && (document.documentElement.scrollLeft || document.documentElement.scrollTop)) { //IE6
            return {
                "X":document.documentElement.scrollLeft,
                "Y":document.documentElement.scrollTop
            };
        }
        
        // If all else fails, return {0, 0}
        return {X:0, "Y":0};
    }
    
    /**
     * Reposition the previewer object based on spacing between the window and parent.
     *
     * @author Tim Baumgard
     */
    function reposition() {
        // Return if  not visible (can't reposition when invisible anyway)
        if (!Visibility) {
            return;
        }
    
        // Get sizes/positions
        var windowSize = getWindowSize();
        var windowPos = getScrollOffsets();
        var parentPos = getPositionOf(Parent);
        var spacing = 30;

        // Get offsets
        var left = parentPos.X - windowPos.X;
        var right = windowSize.width + windowPos.X - parentPos.X - Parent.offsetWidth;
        var max = parseInt(Math.max(left, right));
        var middle = parentPos.Y + Math.ceil(Parent.offsetHeight / 2);

        // Position the previewer
        if (max == right) {
            // Position previewer and pointer
            Previewer.style.top = (middle - Math.ceil(Previewer.offsetHeight / 2)) + "px";
            Previewer.style.left = (parentPos.X + Parent.offsetWidth + spacing) + "px";
            repositionPointer(0);
        } else if (max == left) {
            // Position previewer and pointer
            Previewer.style.top = (middle - Math.ceil(Previewer.offsetHeight / 2)) + "px";
            Previewer.style.left = (parentPos.X - Previewer.offsetWidth - spacing) + "px";
            repositionPointer(1);
        }
    }
   
    /**
     * Reposition the pointer.
     *
     * @author Tim Baumgard
     * @param number orient the orientation of the pointer. 0 is top and goes clockwise to 3.
     */
    function repositionPointer(orient) {
        // Check input and if pointer is set, return if necessary
        if ("number" !== typeof orient || Pointer == "" || !Visibility) {
            return;
        }

        // Offset
        var offset = 7;
            
        // Resposition and update pointer source
        var previewPosition = getPositionOf(Previewer);
        if (orient == 0) {
            Pointer.src = PointerLeft;
            Pointer.style.display = "block";
            Pointer.style.top = (previewPosition.Y + Math.ceil(Previewer.offsetHeight / 2) -
                Math.ceil(Pointer.offsetHeight / 2)) + "px";
            Pointer.style.left = (previewPosition.X - Pointer.offsetWidth + offset) + "px"; 
        } else if (orient == 1) {
            Pointer.src = PointerRight;
            Pointer.style.top = (previewPosition.Y + Math.ceil(Previewer.offsetHeight / 2) -
                Math.ceil(Pointer.offsetHeight / 2)) + "px";
            Pointer.style.left = (previewPosition.X + Previewer.offsetWidth - offset) + "px";
        }
    }
    
    /**
     * Constructor: set up the object.
     *
     * @author Tim Baumgard
     * @param element parent object initiating Previewer
     * @param string title title, if any, for the previewer
     */
     
        // If the parent is undefined, return
        if ("object" !== typeof parent || !parent.nodeName) {
            return;
        }

        // Set class variables
        Previewer = document.createElement("div");
        Visibility = false;
        
        // Set up preview layer
        Previewer.className = "PREV_Previewer";
        Previewer.style.width = "auto";
        Previewer.style.position = "absolute";
        Previewer.style.display = "none";
        Pointer = document.createElement("img");
        Pointer.className = "_PREV_Pointer";
        Pointer.style.position = "absolute";
        Pointer.style.display = "none";
        Pointer.style.zIndex = 100;
        Title = document.createElement("div");
        Title.className = "PREV_Title";
        Content = document.createElement("div");
        Content.className = "PREV_Content";
        Buttons = document.createElement("div");
        Buttons.className = "PREV_Buttons";
        Previewer.appendChild(Title);
        Previewer.appendChild(Content);
        Previewer.appendChild(Buttons);
        document.body.appendChild(Previewer);
        document.body.appendChild(Pointer);
        this.setTitle(title);
        this.setParent(parent);
                        
}
