/*jslint bitwise: true, browser: true, eqeqeq: true, immed: true, newcap: true, nomen: true, regexp: true, undef: true, white: true, indent: 4 */
/*global window*/
/*global ActiveXObject*/

var CartwrightUtil = {
    isNumeric: function(sText, noflt) {
        var ValidChars = "0123456789.";
        if (noflt !== undefined) {  // If defined we don't allow a decimal point
            ValidChars = "0123456789";
        }
        var IsNumber = true;
        var Char;
        for (var i = 0; i < sText.length && IsNumber === true; i++) {
            Char = sText.charAt(i);
            if (ValidChars.indexOf(Char) == -1) {
                IsNumber = false;
            }
        }
        return IsNumber;
    },
    newwindow: function(target, href) {
        window.open(href, target);
        return false;
    },
    /*
    *   BgFade
    *   Opens a DIV in a new window and fades the background
    *
    *   Constructor Requires:
    *   div - The DIV to display in the box
    *
    *   Constructor Option:
    *   show - If this is defined it opens straight away
    *
    *   Optional Properties (see below for defaults), these can be set before calling the show method:
    *   Height - height of box (percentage doesn't work?)
    *   Width - width of box
    *   Topmargin - distance from top of screen    
    *   Closeimage - the image used in the top right corner as the close button
    *   Backcolor - the color of the window
    *
    *   Method: 
    *   show() - Puts it on screen
    *
    *   Notes:
    *   zIndex set to 1000/1001 - if you use bigger it won't show!!!
    *
    *   Hacks
    *   If in IE6, an external style sheet is called which sets the position to absolute. We then check for the position of
    *   the DIV, if it is at the top it means this hack has been called and we know we're in something better, so we set the 
    *   position to relative. If in IE6 we scroll to the top
    *
    *   TODO
    *   - Better way to position the DIV vertically
    *   - Transparent background in IE6 
    *   - A better way to do the hack above
    */

    BgFade: function(div, show) {
        if (!div) {
            throw "BgFade function requires a DIV to initialize";
        }
        this.div = div;
        this.height = '400px';
        this.width = '50%';
        this.topmargin = '100px';
        this.closeimage = 'img/util/close.gif';
        this.bgimage = 'img/trans/80.png';
        this.backcolor = 'white';
        
        this.closebutton = true;
        this.show = function() {
            this.bscrolltype = document.getElementsByTagName('body')[0].style.overflow;
            document.getElementsByTagName('body')[0].style.overflow = 'hidden';
            window.document.body.scroll = 'no';
            // Create the background div to fill the screen
            this.bgdiv = document.createElement('div');
            this.bgdiv.className = 'ie6fixed';  // IE6 hack - defined in an IE6 only style sheet to set the position of the DIV to absolute because fixed is not available
            this.bgdiv.style.top = '0px';
            this.bgdiv.style.left = '0px';
            this.bgdiv.style.height = '100%';
            this.bgdiv.style.width = '100%';
            this.bgdiv.style.backgroundImage = 'url(' + this.bgimage + ')';
            this.bgdiv.style.zIndex = 1000;            
            document.getElementsByTagName('body')[0].appendChild(this.bgdiv); // Add background DIV to body

            // Create the DIV to contain the information
            var containerdiv = document.createElement('div');
            containerdiv.style.backgroundColor = this.backcolor;

            // Set proprties of the div containig the content/close

            containerdiv.style.display = 'block';
            containerdiv.style.marginLeft = 'auto';
            containerdiv.style.marginRight = 'auto';
            containerdiv.style.marginTop = this.topmargin;
            containerdiv.style.width = this.width;
            
            containerdiv.style.zIndex = 1001;
            // Add the main DIV to the background before doing function to it


            var that = this;
            // Add close div and break div to container if we want one
            if (this.closebutton) {
                var closediv = document.createElement('img');
                closediv.style.cssFloat = 'right';  //TODO: Not working in IE6/7
                closediv.style.styleFloat = 'right';
                closediv.src = this.closeimage;
                var breakdiv = document.createElement('div');
                breakdiv.style.clear = 'both';
                containerdiv.appendChild(closediv);
                containerdiv.appendChild(breakdiv);


                CartwrightUtil.event.addHandler(closediv, "click", function() { // Add event handler to close button  
                    document.getElementsByTagName('body')[0].style.overflow = that.bscrolltype;
                    window.document.body.scroll = 'auto';
                    that.div.style.display = 'none';
                    document.getElementsByTagName('body')[0].appendChild(that.div);     // Return content DIV to main body                
                    document.getElementsByTagName('body')[0].removeChild(that.bgdiv);   // Kills this DIV
                });
            }
            if (this.closediv) {    // If we have another DIV acting as a close, then add the eventhandler to it
                CartwrightUtil.event.addHandler(this.closediv, "click", function() {
                    document.getElementsByTagName('body')[0].style.overflow = that.bscrolltype;
                    window.document.body.scroll = 'auto';
                    that.div.style.display = 'none';
                    document.getElementsByTagName('body')[0].appendChild(that.div);     // Return content DIV to main body                
                    document.getElementsByTagName('body')[0].removeChild(that.bgdiv);   // Kills this DIV
                });
            }


            this.div.style.height = this.height;
            this.div.style.display = 'block';
            containerdiv.appendChild(this.div); // Add content div to container
            if (this.bgdiv.offsetTop !== 0) {   // If not IE6
                this.bgdiv.style.position = 'fixed';
            } else {
                scroll(0, 0);   // For IE6, scrolls to the top
            }
            this.bgdiv.appendChild(containerdiv);
        };
        // Show it if we're set to do so
        if (show) {
            this.show();
        }
    },

    /*
    *  Checks for valid email - returns true if valid, false otherwise
    */
    isValidEmail: function(str) {
        if (str.length === 0) {
            return false;
        }
        if (str.indexOf("@") < 1) {
            return false;
        }
        if (str.indexOf(" ") !== -1) {
            return false;
        }
        if (str.substring(str.length - 1) === "@" || str.substring(str.indexOf("@") + 1).indexOf(".") === ".") {
            return false;
        }
        if (str.substring(str.indexOf("@") + 1).indexOf(".") < 1) {
            return false;
        }
        return true;
    },



    /* AJAX function (XMLHR)
    *
    * Returns xhr object if AJAX exists, or null otherwise
    */
    Xhr: function() {
        var xmlHttp;
        try {
            return new XMLHttpRequest();
        } catch (e) {
            try {
                return new ActiveXObject("Msxml2.XMLHTTP");
            } catch (e2) {
                try {
                    return new ActiveXObject("Microsoft.XMLHTTP");
                } catch (e3) {
                    return null;
                }
            }
        }
    },



    // Event utility namespace
    event: {
        addHandler: function(element, type, handler) {
            if (element.addEventListener) {
                element.addEventListener(type, handler, false);
            } else if (element.attachEvent) {
                element.attachEvent("on" + type, handler);
            } else {
                element["on" + type] = handler;
            }
        },

        removeHandler: function(element, type, handler) {
            if (element.removeEventListener) {
                element.removeEventListener(type, handler, false);
            } else if (element.detachEvent) {
                element.detachEvent("on" + type, handler);
            } else {
                element["on" + type] = null;
            }
        },

        getEvent: function(event) {
            return event ? event : window.event;
        },

        getTarget: function(event) {
            return event.target || event.srcElement;
        }
    },

    elePosition: {
        findPosX: function(obj) {
            var curleft = 0;
            if (obj.offsetParent) {
                while (1) {
                    curleft += obj.offsetLeft;
                    if (!obj.offsetParent) {
                        break;
                    }
                    obj = obj.offsetParent;
                }
            }
            else if (obj.x) {
                curleft += obj.x;
            }
            return curleft;
        },

        findPosY: function(obj) {
            var curtop = 0;
            if (obj.offsetParent) {
                while (1) {
                    curtop += obj.offsetTop;
                    if (!obj.offsetParent) {
                        break;
                    }
                    obj = obj.offsetParent;
                }
            }
            else if (obj.y) {
                curtop += obj.y;
            }
            return curtop;
        }
    },




    mousePosition: {
        getMouseX: function(event) {
            return event.offsetX ? event.clientX + document.body.scrollLeft : event.pageX;
        },

        getMouseY: function(event) {
            return event.offsetY ? event.clientY + document.body.scrollTop : event.pageY;
        }
    },

    Sorttable: function(st) {
        this.tbody = st.getElementsByTagName('tbody');
        this.thead = st.getElementsByTagName('thead');
        this.tfoot = st.getElementsByTagName('tfoot');
        this.coltosort = 0;
        this.sortdir = 0;
        var thisObject = this;

        this.initsort = function(sortColIndex) {
            var ele = this.thead[0].rows[0].cells[sortColIndex];
            this.sort(ele);
        };

        this.sort = function(sortCol) {
            if (sortCol.className === "nosort") {
                return;
            }
            if (sortCol.cellIndex === this.coltosort) {
                if (this.sortdir === 0) {
                    this.sortdir = 1;
                } else {
                    this.sortdir = 0;
                }
            } else {
                this.sortdir = 0;
            }
            this.coltosort = sortCol.cellIndex;
            var sorted = [];
            var itm = "";
            for (var j = 0; j < this.tbody[0].rows.length; j++) {
                sorted[j] = this.tbody[0].rows[j];
                if (itm === "") {
                    itm = this.getInnerText(this.tbody[0].rows[j].cells[this.coltosort]);
                }
            }


            var sortfunc = this.sortCaseInsensitive;
            if (itm.match(/\d\d[\/]+\d\d[\/]+\d\d/)) {
                sortfunc = this.sortDate; // date format dd/mm/yy

            }
            if (itm.replace(/^\s+|\s+$/g, "").match(/^[\d\.]+$/)) {
                sortfunc = this.sortNumeric;
            }
            if (itm.substr(0, 1) === "\u00A3") {
                sortfunc = this.sortCurrency;
            }
            sorted.sort(sortfunc);
            if (this.sortdir === 1) {
                sorted.reverse();
            }
            for (var i = 0; i < sorted.length; i++) {
                if (i % 2 === 1) {
                    sorted[i].className = 'even';
                } else {
                    sorted[i].className = 'odd';
                }
                this.tbody[0].appendChild(sorted[i]);
            }

            this.repainthead();
        };

        this.getInnerText = function(el) {
            if (typeof (el.textContent) !== 'undefined') {
                return el.textContent;
            }
            if (typeof (el.innerText) !== 'undefined') {
                return el.innerText;
            }
            if (typeof (el.innerHTML) === 'string') {
                return el.innerHTML.replace(/<[^<>]+>/g, '');
            }
            return el;
        };

        this.sortCaseInsensitive = function(a, b) {
            var aa = thisObject.getInnerText(a.cells[thisObject.coltosort]).toLowerCase();
            var bb = thisObject.getInnerText(b.cells[thisObject.coltosort]).toLowerCase();
            if (aa === bb) {
                return 0;
            }
            if (aa < bb) {
                return -1;
            }
            return 1;
        };

        this.sortDate = function(a, b) {
            var aa = thisObject.getInnerText(a.cells[thisObject.coltosort]);
            var bb = thisObject.getInnerText(b.cells[thisObject.coltosort]);
            var date1 = aa.substr(6, 2) + aa.substr(3, 2) + aa.substr(0, 2);
            var date2 = bb.substr(6, 2) + bb.substr(3, 2) + bb.substr(0, 2);
            if (date1 === date2) {
                return 0;
            }
            if (date1 < date2) {
                return -1;
            }
            return 1;
        };

        this.sortNumeric = function(a, b) {
            var aa = parseFloat(thisObject.getInnerText(a.cells[thisObject.coltosort]));
            if (isNaN(aa)) {
                aa = 0;
            }
            var bb = parseFloat(thisObject.getInnerText(b.cells[thisObject.coltosort]));
            if (isNaN(bb)) {
                bb = 0;
            }
            return aa - bb;
        };

        this.sortCurrency = function(a, b) {
            var aa = thisObject.getInnerText(a.cells[thisObject.coltosort]);
            var bb = thisObject.getInnerText(b.cells[thisObject.coltosort]);
            aa = aa.substring(1, aa.length);
            aa = aa.replace(",", "");
            bb = bb.replace(",", "");
            bb = bb.substring(1, bb.length);
            aa = parseFloat(aa);
            if (isNaN(aa)) {
                aa = 0;
            }
            bb = parseFloat(bb);
            if (isNaN(bb)) {
                bb = 0;
            }
            return aa - bb;
        };

        this.repainthead = function() {
            for (var i = 0; i < this.thead[0].rows[0].cells.length; i++) {
                var ele = this.thead[0].rows[0].cells[i];
                var ih = ele.innerHTML;
                ih = ih.replace("<IMG", "<img");
                var ihs = ih.split('<img');
                ele.innerHTML = ihs[0];
                if (i === this.coltosort) {
                    if (this.sortdir === 0) {
                        ele.innerHTML = ele.innerHTML + '<img src="sorttable/sortdown.gif">';
                    } else {
                        ele.innerHTML = ele.innerHTML + '<img src="sorttable/sortup.gif">';
                    }
                }
            }
        };




        for (var i = 0; i < this.thead[0].rows[0].cells.length; i++) {
            this.thead[0].rows[0].cells[i].sorttable = this;
            this.thead[0].rows[0].cells[i].onclick = function() {
                this.sorttable.sort(this);
                return false;
            };
        }


        this.repainthead();
    }, // end sorttable



    checkformvalid: function(event, submission, checkURL, errormessage) {
        var callingform = CartwrightUtil.event.getTarget(event);
        var xmlHttp;
        if (!(xmlHttp = new CartwrightUtil.Xhr())) { // Create AJAX object and check it exists  if not let form submit and bad errors happen           
            return true;
        }
        xmlHttp.onreadystatechange = function() {
            if (xmlHttp.readyState === 4) {
                if (xmlHttp.status === 200) {
                    var data = xmlHttp.responseText;
                    if (data.length > 0) {
                        callingform.submit();
                    } else {
                        alert(errormessage);
                    }
                } else {
                    callingform.submit(); // Error with data, allow check to return OK
                }
            }
        };
        var getURL = checkURL + "?check=" + submission;
        xmlHttp.open("GET", getURL, true);
        xmlHttp.send(null);
        return false; // Prevent form submission till AJAX comes back
    },
    /*
    *  Cartwright Suggest Code
    *  
    *  Idea obviously stolen from Google, but the code is completely original and designed to be as reusable as possible
    *
    *  To create call a new instance with paramaters
    *   REQ: textbox - The input box that wants to be a select
    *   REQ: URL - The URL containing the suggestions
    *   OPT: width -  Height of suggest box (defaults to width of textbox if empty or 0)
    *   OPT: height -  Height of suggest box (defaults to 400 if empty or 0)
    *
    *  Requirements
    *   - Element (DIV?) containing textbox must be set to position: relative so this positions itself correctly (TODO: better way?)
    *   - URL must return an unordered list containing list items and links (Note: using relative URL on return i.e. ?s= etc is a good idea for test pages)
    *
    *   Notes
    *   - zindex of suggest box set to 500 to lift above everything else, may need changing
    *   - Suggest page returned with query string "s", write suggest page to match with that
    *   - Class of the suggest div is "CartwrightSuggest", please write all CSS to relate to that
    *
    *  TODO: The delay idea when retrieving data isn't ideal. Really need to send the request straight away, and cancel it if another one comes along. Maybe a new server would help!
    */


    CSuggest: function(textbox, URL, width, height) {
        this.textbox = textbox;
        this.currentmenuvalue = -1;   // The currently selected item
        this.URL = URL; // The URL containing the suggest items
        this.height = height;
        this.width = width;
        this.textboxhasfocus = undefined;
        this.init = function() {
            if (!(this.xmlHttp = new CartwrightUtil.Xhr())) { // Create AJAX object and check it exists - if it doesn't don't bother trying!
                return;
            }
            this.creatediv();
            this.addhandlerstotextbox();
        };
        this.addhandlerstotextbox = function() {
            CartwrightUtil.event.addHandler(this.textbox, 'keydown', this.key(this));
            CartwrightUtil.event.addHandler(this.textbox, 'keyup', this.getsuggest(this));
            CartwrightUtil.event.addHandler(this.textbox, 'blur', this.clear(this));
            CartwrightUtil.event.addHandler(this.textbox, 'focus', this.setfocus(this));
        };
        this.setfocus = function(curobject) {
            return function() {
                curobject.textboxhasfocus = true;
            };
        };
        this.clear = function(curobject) {   // Clears the suggest box from the screen
            return function() {
                curobject.textboxhasfocus = false;
                setTimeout(function() { // We wait a short time here to make sure the click event is called before the blur event
                    if (!curobject.textboxhasfocus) {    // Check it hasn't had the focus back
                        curobject.suggestdiv.style.display = 'none';
                        curobject.suggestdiv.scrollTop = 0;
                        curobject.currentmenuvalue = -1;
                    }
                    curobject.allowhide = true;
                }, 500);
            };
        };
        this.creatediv = function() {
            this.suggestdiv = document.createElement('div');
            this.suggestdiv.style.position = 'absolute';
            this.suggestdiv.style.display = 'none';
            this.suggestdiv.style.zIndex = 500;
            this.suggestdiv.style.backgroundColor = 'white';
            this.suggestdiv.style.left = this.textbox.offsetLeft + 'px';
            this.suggestdiv.style.top = (this.textbox.offsetTop + this.textbox.offsetHeight) + 'px';
            this.suggestdiv.style.width = !this.width || this.width === 0 ? this.textbox.offsetWidth + 'px' : this.width + 'px';
            this.suggestdiv.style.height = !this.height || this.height === 0 ? '400px' : this.height + 'px';
            this.suggestdiv.style.overflow = 'scroll';
            this.suggestdiv.style.border = 'solid 1px black';
            this.suggestdiv.className = "CartwrightSuggest";
            CartwrightUtil.event.addHandler(this.suggestdiv, 'mouseover', this.mouse(this)); // Add the event handler for when the mouse goes over this
            CartwrightUtil.event.addHandler(this.suggestdiv, 'scroll', this.scroll(this)); // Add the event handler for scroll to deal with problem in IE where focus is taken away from text box

            this.textbox.parentNode.appendChild(this.suggestdiv);   // Adds this to the div containing the textbox
        };
        this.mouse = function(curobject) {  // Mouse over DIV function
            return function(event) {
                curobject.textbox.focus(); // Sets focus back to text box so key presses work
                var items = curobject.suggestdiv.getElementsByTagName("li");
                if (curobject.currentmenuvalue !== -1) {	// If no item currently selected don't bother
                    items[curobject.currentmenuvalue].className = ""; // Make current normal
                    curobject.currentmenuvalue = -1;
                }
                var linkmouseon = CartwrightUtil.event.getTarget(event).id.replace("suggestitem", "");
                if (!isNaN(parseInt(linkmouseon, 10))) {
                    curobject.currentmenuvalue = linkmouseon;
                    items[linkmouseon].className = "selected";
                }
            };
        };
        this.scroll = function(curobject) {
            return function(event) {
                curobject.allowhide = false;
                curobject.textbox.focus();
            };
        };
        this.key = function(curobject) {
            return function(event) {
                var key = CartwrightUtil.event.getEvent(event).keyCode;
                if (!(key === 40 || key === 13 || key === 38 || key === 34 || key === 33)) { // Only stay if key is up (40), down (38), enter (13), pagedown (34), pageup (33)
                    return true;
                }
                var items = curobject.suggestdiv.getElementsByTagName("li");
                if (items.length === 0) {	// If no items don't do anything
                    return true;
                }
                if (key === 40 && curobject.currentmenuvalue !== items.length - 1) { // Handle down press
                    curobject.deselectItem(curobject, items);
                    curobject.currentmenuvalue++; // Move forwared one
                    curobject.selectItem(curobject, items[curobject.currentmenuvalue]); // Make selected
                    return false;
                }
                if (key === 38 && curobject.currentmenuvalue !== 0) { // Handle up press 
                    curobject.deselectItem(curobject, items);
                    curobject.currentmenuvalue--;
                    curobject.selectItem(curobject, items[curobject.currentmenuvalue]);
                    return false;
                }
                if (key === 34 && curobject.currentmenuvalue !== items.length - 1) { // Handle page down                   
                    curobject.deselectItem(curobject, items);
                    curobject.currentmenuvalue += 10; // Move forwared one
                    curobject.currentmenuvalue = curobject.currentmenuvalue > items.length - 1 ? items.length - 1 : curobject.currentmenuvalue;
                    curobject.selectItem(curobject, items[curobject.currentmenuvalue]);
                    return false;
                }
                if (key === 33 && curobject.currentmenuvalue !== -1) {   // Handle page up
                    curobject.deselectItem(curobject, items);
                    curobject.currentmenuvalue -= 10;
                    curobject.currentmenuvalue = curobject.currentmenuvalue < 0 ? 0 : curobject.currentmenuvalue;
                    curobject.selectItem(curobject, items[curobject.currentmenuvalue]);
                    return false;
                }
                if (key === 13) { //Handle enter                    
                    window.location = items[curobject.currentmenuvalue].getElementsByTagName("a")[0].href; // Gets the selected item                
                    return false; // Prevent form submission under right circumstances
                }
                return true;
            };
        };
        this.deselectItem = function(curobject, items) {
            if (curobject.currentmenuvalue !== -1) {
                items[curobject.currentmenuvalue].className = "";
            }
        };
        this.selectItem = function(curobject, selectedItem) {
            selectedItem.className = "selected";
            var itemheight = selectedItem.offsetHeight;
            var itemnumber = curobject.currentmenuvalue;
            var divheight = curobject.suggestdiv.offsetHeight - 40 - itemheight;
            // If the item is off the bottom of the screen, make it the bottom item
            if ((itemnumber * itemheight) - curobject.suggestdiv.scrollTop > divheight) {
                curobject.suggestdiv.scrollTop = ((itemnumber - 1) * itemheight) - divheight;
            }
            // If the item is off the bottom of the screen, make it the top item          
            if ((itemnumber * itemheight) < curobject.suggestdiv.scrollTop) {
                curobject.suggestdiv.scrollTop = ((itemnumber - 1) * itemheight);
            }

        };
        this.getsuggest = function(curobject) {
            return function(event) {
                var key = CartwrightUtil.event.getEvent(event).keyCode;
                if ((key === 40 || key === 13 || key === 38 || key === 34 || key === 33)) { // TODO: Doesn't call if up/down, must be a better way to do this
                    return;
                }
                var term = curobject.textbox.value;
                if (term === "") { // Don't bother if no term
                    return;
                }
                if (term.length < 3) {  // Only bother if amount of data is decent, maybe look at improving this?
                    curobject.currentmenuvalue = -1;
                    curobject.suggestdiv.style.display = 'none';
                    curobject.scrollTop = 0;
                    return;
                }
                var getpage = encodeURI(curobject.URL) + "?s=" + encodeURIComponent(term);
                curobject.lastype = new Date();     // Record time of last keypress
                curobject.suggestdiv.style.display = 'none';
                curobject.suggestdiv.scrollTop = 0;
                curobject.currentmenuvalue = -1;
                setTimeout(function() { // Wait 600ms and see if another key has been pressed. If not, get the data
                    if (new Date() - curobject.lastype > 550) {
                        curobject.xmlHttp.open("GET", getpage, true);
                        curobject.xmlHttp.onreadystatechange = curobject.retrievesuggest(curobject);
                        curobject.xmlHttp.send(null);
                    }
                }, 600);
            };
        };
        this.retrievesuggest = function(curobject) {    // Handler for AJAX data returned
            return function() {
                if (curobject.xmlHttp.readyState === 4) {
                    if (curobject.xmlHttp.status === 200 || curobject.xmlHttp.status === 304) {
                        curobject.currentmenuvalue = -1;
                        curobject.suggestdiv.style.display = 'block';
                        var resp = '';
                        resp += curobject.xmlHttp.responseText;
                        curobject.suggestdiv.innerHTML = resp;
                    }
                }
            };
        };
        this.init();


    }
};
