/**
 * Copyright (c) 2006 The Norther Organization (http://www.norther.org/).
 *
 * $Id: tammi.js,v 1.21 2010-03-13 07:05:47 cvsimp Exp $
 */
 
/**
 * Creates the global tammi package and the spray subpackage.
 */
if (typeof tammi == "undefined") 
{
    var tammi = { };
    tammi["spray"] = { };
}

/**
 * Defines a subpackage of a main package.
 *
 * @param _main the main package name.
 * @param _path the subpackage path.
 * @return the defined package.
 */
tammi.subpackage = function(_main, _path)
{
    var _package = eval(_main);
    if (_path != null)
    {
        var _items = _path.split('.');
        for (var _i = 0; _i < _items.length; _i++) 
        {
            _package[_items[_i]] = _package[_items[_i]] || { };
            _package = _package[_items[_i]];
        }
    }
    return _package;
};

/**
 * Defines a subclass of a base class.
 *
 * @param _base the base class.
 * @param _subc the subclass.
 * @return the defined class.
 */
tammi.subclass = function(_base, _subc)
{
    if ((_base != null) && (_subc != null))
    {
        var _chain = function() { };
        _chain.prototype = _base.prototype;
        _subc.prototype = new _chain();
        _subc.prototype.constructor = _subc;
        _subc.superclass = _base.prototype;
        if (_base.prototype.constructor == Object.prototype.constructor) 
        {
            _base.prototype.constructor = _base;
        }
    }
    return _subc;
};

/**
 * A static window class.
 */
tammi.spray.Window = 
{
    /**
     * An array of popups.
     */
    popups: [ ],
    
    /**
     * The latest popup.
     */
    poppedup: -1,
    
    /**
     * The document listener.
     */
    listener: null,
    
    /**
     * Makes a redirection.
     *
     * @param _link the link.
     */
    redirect: function(_link)
    {
        window.location = _link;
    },

    /**
     * Expands a tree.
     * 
     * <p>
     * Note that browsers see 'k' when PC numpad '+' is pressed.
     * </p>
     *
     * @param _event the key event.
     * @param _anchor the anchor element.
     * @param _scroll the scroll input element name.
     * @param _focus the focus input element name.
     */
    expandTree: function(_event, _anchor, _scroll, _focus)
    {
        var _key;
        if (window.event)
        {
            _key = _event.keyCode;
        }
        else if (_event.which)
        {
            _key = _event.which;
        }
        else
        {
            _key = 0;
        }
        var _char = String.fromCharCode(_key);
        if ((_char == 'e') || (_char == 'E') || (_char == '+') || (_char == 'k'))
        {
            var _position = this.getScroll();
            window.location = _anchor.href + '&' + _scroll + '=' + _position.x 
                + ',' + _position.y + '&' + _focus + '=' + _anchor.id;
        }
    },

    /**
     * Collapses a tree.
     *
     * <p>
     * Note that browsers see 'm' when PC numpad '-' is pressed.
     * </p>
     *
     * @param _event the key event.
     * @param _anchor the anchor element.
     * @param _scroll the scroll input element name.
     * @param _focus the focus input element name.
     */
    collapseTree: function(_event, _anchor, _scroll, _focus)
    {
        var _key;
        if (window.event)
        {
            _key = _event.keyCode;
        }
        else if (_event.which)
        {
            _key = _event.which;
        }
        else
        {
            _key = 0;
        }
        var _char = String.fromCharCode(_key);
        if ((_char == 'c') || (_char == 'C') || (_char == '-') || (_char == 'm'))
        {
            var _position = this.getScroll();
            window.location = _anchor.href + '&' + _scroll + '=' + _position.x 
                + ',' + _position.y + '&' + _focus + '=' + _anchor.id;
        }
    },

    /**
     * Opens a popup window.
     *
     * @param _link the link.
     * @param _name the name.
     * @param _left the x-coordinate.
     * @param _top the y-coordinate.
     * @param _width the width.
     * @param _height the height.
     */
    popup: function(_link, _name, _left, _top, _width, _height) 
    {
        var _control = "left=" + _left + ", top=" + _top 
            + ", width=" + _width + ", height=" + _height 
            + ", toolbar=no, scrollbars=yes, resizable=yes, status=no"
            + ", location=no, dependent=yes";
        var _view = window.open(_link, _name, _control, false);
        _view.focus();
    },

    /**
     * Adds a popup object.
     *
     * @param _popup the popup.
     * @return the index of the popup or -1.
     */
    addPopup: function(_popup)
    {
        var _index;
        if (_popup != null)
        {
            _index = this.popups.length;
            this.popups[_index] = _popup;
            if (_index == 0)
            {
                this.listener = document.onmouseup;
                if (this.listener != null) 
                {
                    document.onmouseup = function() 
                    { 
                        this.listener(_event); 
                        tammi.spray.Window.hidePopupsHandler();
                    };
                }
                else 
                {
                    document.onmouseup = this.hidePopupsHandler;
                }
            }
        }
        else
        {
            _index = -1;
        }
        return _index;
    },
    
    /**
     * Gets a popup object.
     *
     * @param _index the popup index.
     * @return the popup or null.
     */
    getPopup: function(_index)
    {
        this.poppedup = _index;
        return this.popups[_index];
    },
    
    /**
     * Checks whether the root element contains the specified element.
     *
     * @param _root the root element.
     * @param _element the element to check.
     * @return true if contained, false otherwise.
     */
    contains: function(_root, _element) 
    {
        if ((_root == null) || (_element == null))
        { 
            return false; 
        }
        
        if (_root.contains)
        {
            return _root.contains(_element);
        }
        else if (_root.compareDocumentPosition)
        {
            return (_root.compareDocumentPosition(_element) & 16) != 0;
        }
        else 
        {
            var _parent = _element.parentNode;
            while (_parent != null) 
            {
                if (_root == _parent)
                {
                    return true;
                }
                else if (!_parent.tagName 
                    || (_parent.tagName.toLowerCase() == 'html')) 
                {
                    return false;
                }
                _parent = _parent.parentNode;
            }
            return false;
        }
    },
    
    /**
     * Gets the region of the specified element within the document.
     *
     * @param _element the element.
     * @return a region object with x, y, w and h or null.
     */
    getRegion: function(_element) 
    {
        if ((_element == null)
            || (_element.parentNode == null) 
            || (_element.offsetParent == null) 
            || (_element.style.display == 'none'))
        {
            return null;
        }

        var _region = { };
        if (_element.getBoundingClientRect) 
        {
            // MSIE...
            var _document;
            var _box = _element.getBoundingClientRect();
            if (!this.contains(document.documentElement, _element) 
                && (parent.document != document))
            {
                // Probably a frame or iframe.
                if (!this.contains(parent.document.documentElement, _element)) 
                {
                    return null;
                }
                
                _document = parent.document;
            }
            else
            {
                _document = document;
            }
            _region.x = _box.left + 
                Math.max(_document.documentElement.scrollLeft, 
                    _document.body.scrollLeft);
            _region.y = _box.top + 
                Math.max(_document.documentElement.scrollTop, 
                    _document.body.scrollTop);
            _region.w = _box.right - _box.left;
            _region.h = _box.bottom - _box.top;
            return _region;
        }
        
        // Other browsers...
        _region.x = _element.offsetLeft;
        _region.y = _element.offsetTop;
        _region.w = _element.offsetWidth;
        _region.h = _element.offsetHeight;
        var _parent = _element.offsetParent;
        if (_parent != _element) 
        {
            while (_parent) 
            {
                _region.x += _parent.offsetLeft;
                _region.y += _parent.offsetTop;
                _parent = _parent.offsetParent;
            }
        }
        return _region;
    },
    
    /**
     * Gets the window scroll.
     *
     * @return a position object with x and y.
     */
    getScroll: function() 
    {
        var _position = { };
        if (typeof(window.pageXOffset) == 'number') 
        {
            _position.x = window.pageXOffset;
            _position.y = window.pageYOffset;
        }
        else if (document.body 
            && (document.body.scrollLeft || document.body.scrollTop)) 
        {
            _position.x = document.body.scrollLeft;
            _position.y = document.body.scrollTop;
        }
        else if (document.documentElement 
            && (document.documentElement.scrollLeft 
                || document.documentElement.scrollTop)) 
        {
            _position.x = document.documentElement.scrollLeft;
            _position.y = document.documentElement.scrollTop;
        }
        else
        {
            _position.x = 0;
            _position.y = 0;
        }
        return _position;
    },

    /**
     * Sets the window scroll.
     * 
     * @param _position the scroll left and top separated by a comma.
     */
    setScroll: function(_position) 
    {
    	var _array = _position.split(',');
    	if (_array.length >= 2)
    	{
    	    if ((_array[0] != 0) || (_array[1] != 0))
    	    {
    		    window.scroll(_array[0], _array[1]);
    	    }
    	}
    },

    /**
     * Saves the window scroll.
     * 
     * <p>
     * Note that we apply an element name instead of its id as there may appear
     * more than one scroll input element on the same page.
     * </p>
     *
     * @param _form the form element.
     * @param _scroll the scroll input element name.
     */
    saveScroll: function(_form, _scroll) 
    {
        _scroll = document.getElementsByName(_scroll);
        if (_scroll.length > 1)
        {
            for (var _i = 0; _i < _scroll.length; _i++) 
            {
                if (_scroll[_i].form == _form)
                {
                    var _position = this.getScroll();
                    _scroll[_i].value = _position.x + ',' + _position.y;
                    break;
                }
            }
        }
        else if (_scroll.length > 0)
        {
            var _position = this.getScroll();
            _scroll[0].value = _position.x + ',' + _position.y;
        }
    },

    /**
     * Links the window scroll to a href attribute as a _scroll parameter.
     *
     * @param _anchor the anchor element.
     * @param _scroll the scroll input element name.
     * @param _focus the focus input element name.
     */
    linkScroll: function(_anchor, _scroll, _focus)
    {
        var _position = this.getScroll();
        _anchor.href = _anchor.href + '&' + _scroll + '=' + _position.x 
            + ',' + _position.y + '&' + _focus + '=' + _anchor.id;
    },

    /**
     * Saves the submit attribute. 
     * 
     * <p>
     * Note that the scroll is also saved as programmatic submit doesn't
     * activate form's onsubmit event.
     * </p>
     *
     * @param _control the control element.
     * @param _submit the submit input element name.
     * @param _scroll the scroll input element name.
     */
    saveSubmit: function(_control, _submit, _scroll)
    {
        var _form = _control.form;
        this.saveScroll(_form, _scroll);
        var _input = document.getElementsByName(_submit);
        if (_input.length > 0)
        {
            _input[0].value = _control.name;
        }
        else if (_form != null)
        {
            _input = document.createElement('input');
            _input.name = _submit;
            _input.type = 'hidden';
            _input.value = _control.name;
            _form.appendChild(_input);
        }
    },

    /**
     * Clicks the default button.
     * 
     * <p>
     * Note that we apply an element name instead of its id as there may appear
     * more than one scroll input element on the same page.
     * </p>
     *
     * @param _control the control element.
     * @param _scroll the scroll input element name.
     */
    clickDefault: function(_control, _scroll) 
    {
        var _form = _control.form;
        this.saveScroll(_form, _scroll);
        if (_form != null)
        {
            // Images are not included in form.elements!
            var _elements = document.getElementsByTagName('input');
            if (_elements != null)
            {
                var _type;
                for (var _i = 0; _i < _elements.length; _i++)
                {
                    if (_elements[_i].form == _form)
                    {
                        _type = _elements[_i].type;
                        if ((_type == 'submit') || (_type == 'image'))
                        {
                            _elements[_i].click();
                            break;
                        }
                    }
                }
            }
        }
    },

    /**
     * Sets focus to the named control element.
     *
     * @param _control the control element id.
     */
    setFocus: function(_control)
    {
        if (_control != '')
        {
            _control = document.getElementById(_control);
            if (_control != null)
            {
                _control.focus();
            }
        }
    },

    /**
     * Saves the focus element.
     *
     * <p>
     * Note that we apply element names instead of ids as there may appear
     * more than one focus input element on the same page.
     * </p>
     * 
     * @param _control the control element.
     * @param _focus the focus input element name.
     */
    saveFocus: function(_control, _focus) 
    {
        _focus = document.getElementsByName(_focus);
        if (_focus.length > 1)
        {
	        for (var _i = 0; _i < _focus.length; _i++) 
	        {
	        	if (_focus[_i].form == _control.form)
	        	{
                    _focus[_i].value = _control.id;
                    break;
	        	}
	        }
        }
        else if (_focus.length > 0)
        {
            _focus[0].value = _control.id;
        }
    },

    /**
     * Sets the element scroll.
     * 
     * @param _element the scroll element id.
     * @param _position the scroll left and top separated by a comma.
     */
    setDivScroll: function(_element, _position) 
    {
        _element = document.getElementById(_element);
        if (_element != null)
        {
	        var _array = _position.split(',');
	        if (_array.length >= 2)
	        {
		        _element.scrollLeft = _array[0];
		        _element.scrollTop = _array[1];
	        }
        }
    },

    /**
     * Saves the element scroll.
     * 
     * @param _elem the scroll element id.
     * @param _scroll the scroll input element name.
     */
    saveDivScroll: function(_elem, _scroll) 
    {
        _elem = document.getElementById(_elem);
        if (_elem != null)
        {
	        _scroll = document.getElementsByName(_scroll);
	        if (_scroll.length > 0)
	        {
	            var _position = this.getScroll();
	            _scroll[0].value = _elem.scrollLeft + ',' + _elem.scrollTop;
	        }
        }
    },

    /**
     * Generates a loading indicator.
     *
     * @param _info the info element id.
     * @param _data the data element id.
     * @param _delta the interval in msecs.
     */
    loading: function(_info, _data, _delta)
    {
        if (!document.getElementById(_data))
        {
            var _elem = document.getElementById(_info);
            if (_elem != null)
            {
                _elem.innerHTML = _elem.innerHTML + " .";
                var _tout = "loading('" + _info + "', '" 
                    + _data + "', " + _delta + ")";
                setTimeout(_tout, _delta);
            }
        }
    },
    
    /**
     * Includes the specified script in head.
     * 
     * @param _url the URL of the script.
     */
    include: function(_url)
    {
        var _head = document.getElementsByTagName("head")[0];
        var _script = document.createElement("script");
        _script.setAttribute("type", "text/javascript");
        _script.setAttribute("src", _url);
        _head.appendChild(_script);
    },
    
    /**
     * Requires the specified script in body.
     * 
     * @param _url the URL of the script.
     */
    require: function(_url)
    {
        var _body = document.getElementsByTagName("body")[0];
        var _script = document.createElement("script");
        _script.setAttribute("type", "text/javascript");
        _script.setAttribute("src", _url);
        _body.appendChild(_script);
    }
};

/**
 * An event handler hiding open popup elements.
 */
tammi.spray.Window.hidePopupsHandler = function() 
{
    // Note that "this" refers to "document" within an event handler.
    var _event;
    if (arguments.length > 0)
    {
        _event = arguments[0];
    }
    else
    {
        _event = window.event;
    }
    if (_event != null)
    {
        var _popups = tammi.spray.Window.popups;
        for (var _i = 0; _i < _popups.length; _i++) 
        {
            if (_popups[_i] != null) 
            {
                if (_popups[_i].autoHide && !_popups[_i].isClicked(_event)) 
                {
                    _popups[_i].hide();
                }
            }
        }
    }
};

/**
 * A static plugin detect class originally developed by Apple.
 * 
 * http://developer.apple.com/internet/webcontent/detectplugins.html
 */
tammi.spray.PluginDetect = 
{
    /**
     * Detectable with VBScript initialized option.
     */
    detectableWithVBInitialized: false,
    
    /**
     * Detectable with VBScript option.
     */
    detectableWithVB: false,
     
    /**
     * Detects the specified plugins.
     * 
     * @param _plugins an array of plugin names.
     * @return true if detected, false otherwise.
     */
    detectPlugin: function() 
    {
        // Allow for multiple checks in a single pass.
        var _daPlugins = arguments;
        // Consider pluginFound to be false until proven true.
        var _pluginFound = false;
        // If plugins array is there and not fake.
        if (navigator.plugins && (navigator.plugins.length > 0)) 
        {
            var _pluginsArrayLength = navigator.plugins.length;
            // For each plugin...
            for (_pluginsArrayCounter = 0; _pluginsArrayCounter < _pluginsArrayLength; _pluginsArrayCounter++ ) 
            {
                // Lloop through all desired names and check each against the current plugin name.
                var _numFound = 0;
                for (_namesCounter = 0; _namesCounter < _daPlugins.length; _namesCounter++) 
                {
                    // If desired plugin name is found in either plugin name or description.
                    if ((navigator.plugins[_pluginsArrayCounter].name.indexOf(_daPlugins[_namesCounter]) >= 0) || 
                        (navigator.plugins[_pluginsArrayCounter].description.indexOf(_daPlugins[_namesCounter]) >= 0)) 
                    {
                        // This name was found.
                        _numFound++;
                    }   
                }
                // Now that we have checked all the required names against this one plugin,
                // if the number we found matches the total number provided then we were successful.
                if (_numFound == _daPlugins.length) 
                {
                    // If we've found the plugin, we can stop looking through at the rest of the plugins.
                    _pluginFound = true;
                    break;
                }
            }
        }
        return _pluginFound;     
    },
    
    /**
     * Detects the specified active X control.
     * 
     * @param _plugins an array of plugin names.
     * @return true if detected, false otherwise.
     */
    detectActiveX: function(_name) 
    {
        var _pluginFound = false;
        if (!this.detectableWithVBInitialized)
        {
            // Here we write out the VBScript block for MSIE Windows
            if (navigator.appName == "Microsoft Internet Explorer")
            {
                var _head = document.getElementsByTagName("head")[0];
                var _script = document.createElement("script");
                _script.setAttribute("type", "text/vbscript");
                _script.setAttribute("language", "VBscript");
                
                // Non-standard, IE doesn't seem to support text nodes with scripts.
                _script.text = "<!--"
                + "'do a one-time test for a version of VBScript that can handle this code\n"
                + "tammi.spray.PluginDetect.detectableWithVB = False\n"
                + "If ScriptEngineMajorVersion >= 2 then\n"
                + "  tammi.spray.PluginDetect.detectableWithVB = True\n"
                + "End If\n"
                + "'this next function will detect most plugins\n"
                + "Function detectActiveXControl(activeXControlName)\n"
                + "  on error resume next\n"
                + "   detectActiveXControl = False\n"
                + "  If tammi.spray.PluginDetect.detectableWithVB Then\n"
                + "     detectActiveXControl = IsObject(CreateObject(activeXControlName))\n"
                + "  End If\n"
                + "End Function\n"
                + "-->";
                _head.appendChild(_script);
            }
            this.detectableWithVBInitialized = true;
        }
        if (this.detectableWithVB)
        {
            _pluginFound = detectActiveXControl(_name);
        }
        return _pluginFound;     
    },
    
    /**
     * Detects the Flash plugin.
     * 
     * @return true if detected, false otherwise.
     */
    detectFlash: function() 
    {
        return this.detectPlugin('Shockwave', 'Flash')
            || this.detectActiveX('ShockwaveFlash.ShockwaveFlash.1');
    }
};

/**
 * A popup element class.
 */
tammi.spray.Popup = function() 
{
    /**
     * The content id.
     */
    this.id = null; 
    
    /**
     * The anchor id.
     */
    this.aid = null;
     
    /**
     * The button id.
     */
    this.bid = null;
     
    /**
     * The horizontal offset.
     */
    this.xoff = 0;
    
    /**
     * The vertical offset.
     */
    this.yoff = 0;
    
    /**
     * The markup content.
     */
    this.content = "";
    
    /**
     * A flag for updated content.
     */
    this.upToDate = true;
    
    /**
     * The auto hide option.
     */
    this.autoHide = true;
    
    /**
     * Other options.
     */
    this.options = null;
    
    /**
     * The popup index.
     */
    this.index = tammi.spray.Window.addPopup(this);
    
    // Check additional options.
    if (arguments.length > 0)
    {
        if (arguments[0] instanceof Array)
        {
            // For nested calls from subclasses.
            if (arguments[0].length > 0)
            {
                this.options = arguments[0][0];
            }
        }
        else
        {
            this.options = arguments[0];
        }
    }
};

/**
 * Sets the popup element content.
 * 
 * @param _content the markup content.
 */
tammi.spray.Popup.prototype.setContent = function(_content)
{
    this.content = _content;
    this.upToDate = false;
};

/**
 * Refreshes the popup element content.
 */
tammi.spray.Popup.prototype.refresh = function()
{
    if (!this.upToDate)
    {
        var _element = this.getDivElement();
        if (_element != null)
        {
            _element.innerHTML = this.content;
            this.upToDate = true;
        }
    }
};

/**
 * Shows the popup element. 
 * 
 * @param _id the content element id.
 * @param _aid the anchor element id.
 * @param _bid the button element id (optional).
 */
tammi.spray.Popup.prototype.show = function()
{
    if (arguments.length > 0)
    {
        if ((arguments[0] != null) && (arguments[0] != ""))
        {
            this.id = arguments[0];
        }
        if (arguments.length > 1)
        {
            if ((arguments[1] != null) && (arguments[1] != ""))
            {
                this.aid = arguments[1];
            }
            if (arguments.length > 2)
            {
                if ((arguments[2] != null) && (arguments[2] != ""))
                {
                    this.bid = arguments[2];
                }
            }
        }
    }
    var _div = this.getDivElement();
    if (_div != null)
    {
        this.refresh();
        var _anchor = this.getAnchorElement();
        if (_anchor != null)
        {
            var _region = tammi.spray.Window.getRegion(_anchor);
            if (_region != null)
            {
	            _div.style.left = (_region.x + this.xoff) + "px";
	            _div.style.top = (_region.y + _region.h + this.yoff) + "px";
            }
        }
        _div.style.display = "block";
    }
};

/**
 * Hides the popup element.
 */
tammi.spray.Popup.prototype.hide = function()
{
    var _div = this.getDivElement();
    if (_div != null)
    {
        _div.style.display = "none";
    }
};

/**
 * Checks whether the popup element is clicked.
 * 
 * @param _event the mouse event.
 */
tammi.spray.Popup.prototype.isClicked = function(_event)
{
    if ((_event != null) && (this.id != null))
    {
        var _target;
        if (_event.target)
        {
            _target = _event.target;
        }
        else if (_event.srcElement)
        {
            _target = _event.srcElement;
        }
        else
        {
            _target = null;
        }
        if (_target != null)
        {
            if (_target.nodeType == 3)
            {
                // Safari bug.
                _target = _target.parentNode;
            }
            var _root = null;
            while (_target != null) 
            {
                if (this.id == _target.id)
                {
                    return true;
                }
                _root = _target;
                _target = _target.parentNode;
            }
            if ((_root != null) && (_root.nodeType != 9))
            {
                // Dynamically built popup tree doesn't 
                // always seem to reach the document.
                return true; 
            }
        }
    }
    return false;
};

/**
 * Gets the content element.
 * 
 * @return the content element or null.
 */
tammi.spray.Popup.prototype.getDivElement = function()
{
    return this.id != null ? document.getElementById(this.id) : null;
};

/**
 * Gets the anchor element.
 * 
 * @return the anchor element or null.
 */
tammi.spray.Popup.prototype.getAnchorElement = function()
{
    return this.aid != null ? document.getElementById(this.aid) : null;
};

/**
 * Gets the button element.
 * 
 * @return the button element or null.
 */
tammi.spray.Popup.prototype.getButtonElement = function()
{
    return this.bid != null ? document.getElementById(this.bid) : null;
};

