/*
    - constants
        - c_validMarkRegex

    - Methods:
        - WriteMailToAnchor
        - WMTA
        - RemoveValueFromArray
        - ArrayContains
        - HasClassName
        - AddClassName
        - RemoveClassName
        - StopEvent
        - GetActualLeft
        - GetActualTop
        - PositionElementByMouseCoords
        - PositionElementOverTarget
        - XmlEncode
        - ScreenUp
        - ScreenDown
        - ShowHideElement
        - IsElementBelowVisibleArea
*/

// Too Aggressive -> var c_validMarkRegex = "^[0-9]+(\\.{0,1}[0-9]{1,2}){0,1}$";
//                   var c_validMarkRegex = "^[0-9]+\\.{0,1}[0-9]{1,2}$";
var c_validMarkRegex = "^[0-9]*\\.{0,1}[0-9]{0,2}$";

/* **** MailTo writing : START **** */

    // function used to output mailto links in such a way that email addresses are harder to scrape
    function WriteMailToAnchor(emailName, emailDomain, displayName)
    {
        // check optional display name
        if (displayName == null || displayName == 'undefined' || displayName == '' )
        {
            displayName = emailName;
            // span is used to break up the text while not changing the way it looks on the page
            displayName += "<span>@</span>"; 
            displayName += emailDomain;
        }
        
        // create the anchor
        var xhtml = "";
        xhtml = "<a href='mailto:";
        xhtml += emailName;
        xhtml += "@";
        xhtml += emailDomain;
        xhtml += "'>" + displayName + "</a>";
        
        // write to the DOM
        document.write(xhtml);
    }

    // short alias for WriteMailToAnchor
    function WMTA(emailName, emailDomain, displayName){ WriteMailToAnchor(emailName, emailDomain, displayName); }

/* **** MailTo writing : END **** */
/* **** Array processing : START **** */
    
    // removes the given value from the array
    function RemoveValueFromArray( array, value ){
       
        var itemIndex = -1;

        // loop through the array looking for a match
        // NB we assume that there is only one match. If there are multiple matches then the last instance will be removed
        for(p = 0; p < array.length; p++){
            if(array[p] == value){
                itemIndex = p;
            }
        }
        
        // remove the value from the index by splitting and rejoining the array (less the value)
        if( itemIndex > -1 ){
            array.splice(itemIndex,1);
        }
        
        return itemIndex;
    }
    
    // simple linear search method for an array
    function ArrayContains( array, value ){
        index = null;
        for(p = 0; p < array.length; p++){
            if(array[p] == value){
                return true;
            }
        }
        return false;
    }
    
/* **** Array processing : END **** */
/* **** class name processing : START **** */

    // method to match a className within the class attribute (which could hold several names delimited by whitespace)
    function HasClassName(element, matchClassName){
        var classNameAttributeValue = element.className;
        
        
        // previous version that broke the string up into and array and checked
//        if((classNameAttributeValue != null) && (classNameAttributeValue != "")){
//            var arrClassNames = classNameAttributeValue.split(" ");
//            if((arrClassNames != null) && (arrClassNames.length > 0)){
//            
//                // check each value in turn
//                for(var i = 0; i < arrClassNames.length; i++){
//                    var className = arrClassNames[i];
//                    
//                    // if it's a match, return true, otherwise move on to the next
//                    if(matchClassName == className){ return true; }
//                }
//            }
//        }
        
        // if we reach this point then we have no match, return false
//        return false;

        var matchRegex = new RegExp("[^a-zA-Z0-9]*" + matchClassName + "[^a-zA-Z0-9]*", "ig");
        return classNameAttributeValue.match(matchRegex);

    }

    // assigns the class name to the element specified, if not already applied
    function AddClassName(element, newClassName){
        
        // alert("Add(\"" + element.tagName + "," + newClassName + "\") " + element.className + " : " + newClassName);
        
        // only add a className if it is not already applied
        if(!HasClassName(element, newClassName)){
            // handle "" classNames
            if((element.className == null) || (element.className == "")){
                element.className = newClassName;
            }else{
                element.className = element.className + " " + newClassName;
            }
        }
    }

    // strips the class name specified from the .className property of the element specified
    function RemoveClassName(element, removeClassName){

        // only remove a className if it is already applied
        if(HasClassName(element, removeClassName)){
        
            var classNameAttributeValue = element.className;
            
            // break the className value down into the seperate class names applied
            // NB HasClassName has already informed us that there is at least one class name applied,
            // so no need to check for null
            
            var newClassNameValue = "";
            var arrClassNames = classNameAttributeValue.split(" ");
            
            if((arrClassNames != null) && (arrClassNames.length > 0)){
            
                // check each value in turn
                for(var i = 0; i < arrClassNames.length; i++){
                    var className = arrClassNames[i];
                    // add any className that isn't the one to be removed
                    if(className != removeClassName){
                        if(newClassNameValue.length > 0){ newClassNameValue += " "; }
                        newClassNameValue += className;
                    }
                }
                
                // alert("Remove(\"" + element.tagName + "," + removeClassName + "\") " + element.className + " : " + newClassNameValue);
                element.className = newClassNameValue;
            }
        }
    }
    
/* **** class name processing : END **** */
/* **** event processing : START **** */

    // Method that uses feature detection to set the appropriate properties on an event to prevent bubbling
    function StopEvent(e){
        if(e){
            if (e.cancelBubble){ e.cancelBubble = true; }
            if (e.preventDefault){ e.preventDefault(); }
            if (e.stopPropagation){ e.stopPropagation(); }
        }
    }
    
/* **** event processing : END **** */
/* **** element positioning : START **** */

// determines the pixel position of the element on the horizontal axis
function GetActualLeft(element){
    var value = element.offsetLeft;
    var parent = element.offsetParent;
    
    while(parent != null){
        value += parent.offsetLeft;
        parent = parent.offsetParent;
    }
    
    return value;
}

// determines the pixel position of the element on the vertical axis
function GetActualTop(element){
    var value = element.offsetTop;
    var parent = element.offsetParent;
    
    while(parent != null){
        value += parent.offsetTop;
        parent = parent.offsetParent;
    }
    
    return value;
}

// gets the current coordinates of the mouse
function PositionElementByMouseCoords(e, element, offset, offsetY){

    // allows the just the offset param to specifiy the same value for x and y, but also for seperate x, y values
    if(offsetY == null){
        offsetY = offset;
    }

    // if e is null then it's probably IE and we need to get a reference to its event
    if (e == null) { e = window.event; }
    
    mouseX = 0;
    mouseY = 0;
    
    // get coords depending on browser support
    if (e.pageX || e.pageY){
        // alert("e.pageX:" + e.pageX + ", e.pageY:" + e.pageY);
	    mouseX = e.pageX;
	    mouseY = e.pageY;
    }else if (e.clientX || e.clientY){
	    mouseX = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
	    mouseY = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
    }
        
    // (at least FF) .offsetParent only returns a value when the element is displayed
    element.style.display = "block";
    
    // mouse x and y are relative to the whole - but our element will be positioned
    // relative to its container, find coords and adjust
    var container = element.offsetParent;
    // alert(element.tagName);
    var containerX = 0;
    var containerY = 0;
    if(container != null){
        containerX = GetActualLeft(container);
        containerY = GetActualTop(container);
    }
    
    // alert(containerX + "," + containerY);
    
    element.style.left = ((mouseX - containerX) - offset) + "px";
    element.style.top  = ((mouseY - containerY) - offsetY) + "px";
}

// positions the floating element at the 0,0 coord of the target element
function PositionElementOverTarget(targetElement, positionElement, offsetX, offsetY){

    if(offsetX == null){ offsetX = 0; }
    if(offsetY == null){ offsetY = 0; }

    var left = GetActualLeft(targetElement);
    var top  = GetActualTop(targetElement);

    // (at least FF) .offsetParent only returns a value when the element is displayed
    positionElement.style.display = "block";
    
    // x and y are relative to the whole - but our element will be positioned
    // relative to its container, find coords and adjust
    var container = positionElement.offsetParent;
    // alert(element.tagName);
    var containerX = 0;
    var containerY = 0;
    if(container != null){
        containerX = GetActualLeft(container);
        containerY = GetActualTop(container);
    }

    positionElement.style.left = ((left - containerX) - offsetX) + "px";
    positionElement.style.top  = ((top  - containerY) - offsetY) + "px";
}

/* **** element positioning : END **** */
/* **** encoding : START **** */

// utility function that xml encodes values
function XmlEncode(value){
    value = value.replace(/&/g, "&amp;");
    value = value.replace(/</g, "&lt;");
    value = value.replace(/>/g, "&gt;");
    value = value.replace(/\"/g, "&quot;");
    return value;
}

/* **** encoding : END **** */
/* **** screen : START **** */

// raises the opaque screen used to allow a window style UI to be isolated from body content, which appears beneath
function ScreenUp(showGraphic){

    if(showGraphic == null){ showGraphic = false; }

    // get element references
    var body = document.getElementById("theBody");
    var bodyScreen = document.getElementById("theBodyScreen");
    var theScreen = document.getElementById("theScreen"); // this is the one with the graphic
    
    // set screen height based on the body
    bodyScreen.style.height = body.offsetHeight + "px";
    // show screen
    bodyScreen.style.display = "block";
    
    if(showGraphic){
        theScreen.style.backgroundPosition = "center center";
    }else{
        theScreen.style.backgroundPosition = "-100px";
    }
}

// closes the opaque screen used to allow a window style UI to be isolated from body content, which appears beneath
function ScreenDown(){
    // get element references
    var bodyScreen = document.getElementById("theBodyScreen");
    // hide screen
    bodyScreen.style.display = "none";
}

/* **** screen : END **** */

    function ShowHideElement(elementId){
        var element = document.getElementById(elementId);
        if(element.style.display != "block"){
            element.style.display = "block";
        }else{
            element.style.display = "none"; // set to "" in order to revert to the default
        }
    }
    
    
    
    // function that determines whether a given element lies below the visible screen area
    function IsElementBelowVisibleArea(element){
        
        var elementTop = GetActualTop(element); // stars.web.js
        var elementHeight = element.offsetHeight;
        var elementBottom = elementTop + elementHeight;

        var scrollTop = document.body.scrollTop;
        if (scrollTop == 0){
            if (window.pageYOffset){
                scrollTop = window.pageYOffset;
            }else{
                scrollTop = (document.body.parentElement) ? document.body.parentElement.scrollTop : 0;
            }
        }
        
        var windowHeight = 0;
        if(window.innerHeight) {
            windowHeight = window.innerHeight;
        } else if( document.documentElement && document.documentElement.clientHeight) {
            //IE
            windowHeight = document.documentElement.clientHeight;
        } else if( window.screen && window.screen.availHeight) {
            //IE 6+ in 'standards compliant mode'
            windowHeight = window.screen.availHeight;
        } 
    /*
        // now find the position of the bottom of the visible area
        alert("document.body.clientHeight : " + document.body.clientHeight + "\r\n" +
              "document.documentElement.clientHeight : " + document.documentElement.clientHeight + "\r\n" +
              "document.body.scrollHeight : " + document.body.scrollHeight + "\r\n" +
              "document.body.scrollTop : " + document.body.scrollTop + "\r\n" +
              "document.body.offsetHeight : " + document.body.offsetHeight + "\r\n" +
              "window.screen.availHeight : " + window.screen.availHeight + "\r\n" +
              "window.innerHeight : " + window.innerHeight + "\r\n" +
              "window.offsetHeight : " + window.innerHeight + "\r\n" +
              "scrollTop : " + scrollTop + "\r\n" +
              "windowHeight : " + windowHeight + "\r\n" +
              "elementBottom : " + elementBottom
        );
        */
        
        // determine whether there's an overlap (the bottom of the element is greater than the bottom of the screen)
        return ((scrollTop + windowHeight) < elementBottom);
        
        
    }