
/*
 * Wrapper for log4js
 */
var logger;

var LogManager = function() {
    var initialised = false;
    var instanceName = 'logger';
    var debugLevel;
    var debugConsole;
    var queryStringKey = 'debug';
    var l_oDocument;

    var createLogger = function() {
        if (!initialised) {
            try {
                logger = new Log4js.getLogger("UI");
                logger.getName = function() {
                    return instanceName;
                };

                var level = debugLevel;
                if (Log4js.Level[level] == undefined) {
                    level = 'ERROR';
                }
                // logger.setLevel(Log4js.Level.DEBUG);
                logger.setLevel(Log4js.Level[level]);
                initialised = true;
            }
            catch(e) {
                alert('Error initialising log4js : ' + e);
            }
        }
    };

    var addAppender = function() {
        try {
            var consoleAppender = new Log4js.ConsoleAppender(true);
            consoleAppender.setConsole(debugConsole);
            consoleAppender.pattern = new Log4js.PatternLayout(Log4js.PatternLayout.ABSOLUTETIME_DATEFORMAT);
            logger.addAppender(consoleAppender);
        }
        catch(e) {
            alert('Error initialising log4js : ' + e);
        }
    };

    var createPopup = function() {
        var l_sDebugWindowName = "hello";
        var l_sDebugWindowParameters = "height=600,width=700,status=no,toolbar=no,menubar=no,location=no,resizable=yes,scrollbars=yes";

        debugConsole = window.open("", l_sDebugWindowName, l_sDebugWindowParameters);
        var l_sHtml = "<html><head><title>Debug Log [Debug level " + debugLevel + "]</title>";
        l_sHtml += "<style type=\"text/css\"> body {font-family:Verdana;background-color:Black;color:#00ff00;} table {font-size:10px;}</style></head>";
        l_sHtml += "<table width='100%'><tbody id=\"tblMessageLog\" cellpadding=\"0\" cellspacing=\"0\"></tbody></table>";
        l_sHtml += "<scr" + "ipt type=\"text/javascript\" src=\"/logger/log-window.js\"></scr" + "ipt>";
        l_sHtml += "</body></html>";
        // write HTML out to debug window
        try
        {
            l_oDocument = debugConsole.document;
            l_oDocument.open();
            l_oDocument.write(l_sHtml);
            l_oDocument.close();
        }
        catch(e)
        {
            alert("Your browser appears to have a popup blocker enabled which is preventing the debug console window from opening.\nPlease disable the popup blocker for this web site to view the console.");
        }
    };

    // private singleton instances
    return {
        init : function() {
            debugLevel = QueryStringUtils.get(queryStringKey).toUpperCase();
            createLogger();
            if (debugLevel != 'FALSE') {
                createPopup();
                addAppender();
            }
        }
    };
}();
/**
 * @class Bootstrap
 * Bootstrapper for the Newco application
 * @singleton
 */

var loaded = false;

var Bootstrapper = function() {
    // private singleton instances
    var iframeDataIslandIdentifier = 'navigationStateData';
    var iFrame = null;
    var adminPages = false;
    var flashRequirementsMet;

    var confirmFlashRequirementsMet = function() {
        var installed = FlashDetect.installed;
    	//var installed = false;
        var version = FlashDetect.major;
        var met = (installed && (version >= 9));
        logger.info('Flash installed = ' + installed + ', version = ' + version + ', requirements met = ' + met);
        return met;
    };

    var getIFrameElement = function() {
        if (iFrame === null) {
            iFrame = $('#bodyIFrame')[0];
        }
        return iFrame;
    };

    var getFrame = function() {
        var frame = window['bodyIFrame'];
        if (typeof frame.$ == "undefined")  {
            // no jQuery on the iframe. Most likely a 404 or similar
            logger.error("iframe appears to be in an invalid state");
            frame = null;
        }
        return frame;
    };

    var attachIFrameOnloadHandler = function() {
        var iframeElement = getIFrameElement();
        var handler = (adminPages) ? handleAdminPagesIFrameOnload.createDelegate() : handleIFrameOnload.createDelegate();

        try {
            if (iframeElement.addEventListener) {
                iframeElement.addEventListener('load', handler, false);
            }
            else {
                iframeElement.attachEvent("onload", handler);
            }
        }
        catch(e) {
            logger.error('Failed to attach onload event handler to iframe', e);
        }

        // Protects against race conditions, the iframe could well load first
        handler.call();
    };

    var getIFrameDataIsland = function(frame) {
        // Check to see if there is a data island on the iframe.
        // Look for a an element on the iframe having the id 'navigationStateData'.
        // If it does not exist return null
        var island = null;
        if (frame.$('#'+iframeDataIslandIdentifier).length > 0) {
            island = frame.$('#'+iframeDataIslandIdentifier)[0].firstChild;
        }
        return island;
    };

   /**
    * Processes request for streaming market data
    * Market data stream requests are embedded in an element having the id "mdStreamTickerIds"
    * and the tickerIds are contained as the classname space delimited
    * The method calls the MarketDataPoller class with the ticker id list, or null if there are no requests.
    */
    var processStreamingMarketDataRequests = function() {
        var requestElt, className;
        var frame = getFrame();
        var tickerIdList = null;

        if (frame) {
            requestElt = frame.$('#mdStreamTickerIds');
            if (requestElt.length > 0) {
                // The classname of this element contains a space delited list of tickerIds
                className = requestElt[0].className;
                tickerIdList = className.split(' ');
            }
        }
        MarketDataPoller.setTickerIdList(tickerIdList);
    };

    /**
     * Do preparatory work to register the user with the Source web site.
     * Used for ajax capability.
     * Not Currently used.
     */
    var processUserRegistrationRequests = function() {
    	
        var frame = getFrame();

        if (frame) {
        	
        }
        //alert('bootstrapper.processUserRegistrationRequests');
    	UserRegistration.prepRegisterUser();
        
    };
    
    /**
     * Do preparatory work to login the user with the Source web site.
     * Used for ui redrawing. Could also be user of ajax if need be.
     * Not Currently used.
     */
    var processPersonalisedLoginRequests = function() {
    	
        var frame = getFrame();

        if (frame) {
        	
        }
        //alert('bootstrapper.processUserRegistrationRequests');
        //Personalised.checkUserNameErrorLabelServerSideMsgChange();
    	//Personalised.checkPasswordErrorLabelServerSideMsgChange();
    	
    };
    
    var processIFrameDataIsland = function() {
        var island, data = null;
        var frame = getFrame();

        if (frame) {
            island = getIFrameDataIsland(frame);
            if (island) {
                try {
                    data = eval('(' + island.data + ')');
                }
                catch(e) {
                    logger.error("Data island failed to evaluate; " + e.message);
                }
                // If a data island exists, inform the CatalogueNavigation
                if (data !== null && CatalogueNavigation) {
                    logger.info("Found JSON data island. Calling CatalogueNavigation.updateTotals()");
                    CatalogueNavigation.updateState(data);
                }
            }
        }
    };

    var launchAnalytics = function(frame) {
        if (frame.launchGA) {
            frame.launchGA();
        }
        else {
            logger.error("Google Analytics code not detected");
        }
    };

    // Lite version of the method below for the admin pages
    var handleAdminPagesIFrameOnload = function(event) {
        // attachBrowserDetectToIFrame();
        attachToIFrame(logger);
        attachToIFrame(FrameSizer);
        // toggle the display of the document nav
        CMSDocumentNavigation.handleVisibility();
        // Size the frame
        FrameSizer.size();
    };

    var handleIFrameOnload = function(event) {
        var frame = getFrame();
        if (frame != null) {
            launchAnalytics(frame);
            // Show / hide the fisheye
            FisheyeManager.setVisibility();
            // Attach the top level objects to the iframe
            attachToIFrame(logger);
            attachToIFrame(FrameSizer);
            attachToIFrame(BrowserDetect);
            // Process the LHS Navigation state information
            processIFrameDataIsland();
            // Process streaming market data requests
            processStreamingMarketDataRequests();
            // Process user registration requests
            processUserRegistrationRequests();
            // Process personalised login
            processPersonalisedLoginRequests();
            // Initialise the javascript tracking chart
            if (frame.TrackingChart) {
                frame.TrackingChart.plot();
            }
            // Init the pie chart manager
            if (frame.PieChartManager) {
                frame.PieChartManager.render();
            }
            // Size the frame
            FrameSizer.size();
            // Update the menu
            if (Menu) {
                Menu.update(frame.location.pathname);
            }
        }
    };

    var attachToIFrame = function(obj) {
        var objName;

        if ((obj !== undefined) && (obj.getName !== undefined)) {
            objName = obj.getName();
            try {
                window['bodyIFrame'][objName] = obj;
            }
            catch(e) {
                logger.info('Exception caught attaching object "' + objName + '" to iframe: ' + e.message);
            }
        }
        else {
            logger.info('Call to attachToIFrame with undefined object or getName() not implemented');            
        }
    };

    var setPageTitle = function() {
        var versionTitle = queryString('versionTitle');
        if (versionTitle != "false") {
            document.title = versionTitle;
        }
    };

    var handleWindowResize = function(event) {
        // Size the frame
        FrameSizer.handleWindowResize();
        // reposition the fisheye
        FisheyeManager.position();
    };

    // Initialisation code common to both the website and admin
    var initCommon = function() {
        // create the logger
        if (LogManager) {
            LogManager.init();
        }
        window.onresize = handleWindowResize.createDelegate(this);
        // Attach an onload handler to the iframe
        attachIFrameOnloadHandler();
    };

    return {
        handleIFrameMouseMove : function(event) {
            var dummy = {};        
            // apply the iframe offsets to the coordinates
            dummy.clientX = event.clientX + iframeOffsetLeft;
            dummy.clientY = event.clientY + iframeOffsetTop;
            dummy.fromIF = true;
        },

        initAdminPages : function() {
            adminPages = true;

            initCommon();
        },

        init : function() {
            initCommon();

            // Launch Google Analytics
            if (this.launchGA != undefined) {
                launchGA();
            }

            // If this is a preview page from the CMS
/*
            if (PageQuery) {
                // set the title
                setPageTitle();
            }
  */ // TODO : how did this ever work? PageQuery being present proves what exactly?
            
            // trap global key strokes
            document.onkeydown=function(event) {               
                if (SearchController) {
                    // pass to the search controller
                    return(SearchController.handleKeyDown(event));
                }
                return true;
            };

            document.onmousedown = function(event) {
                event = event || window.event;
                if (SearchController && (!SearchController.hasClickedOn(event))) {
                    // If clicked outside the search controller - close it
                    SearchController.hide();
                }
                return true;
            };

            // Initialise the left hand side navigation
            if (CatalogueNavigation) {
                CatalogueNavigation.init();                
            }

            // Initialise the fisheye widgets
            FisheyeManager.init();

            flashRequirementsMet = confirmFlashRequirementsMet();
            loaded = true; // set a flag to indicate bootstrap complete
            logger.info('bootstrapper initialisation complete');
        }
    };
}();

/**
 * @class BrowserDetect
 * Client detection class
 * @singleton
 */
var BrowserDetect = {
	init: function () {
		this.browser = this.searchString(this.dataBrowser) || "An unknown browser";
		this.version = this.searchVersion(navigator.userAgent) || 
                       this.searchVersion(navigator.appVersion) ||
                       "an unknown version";
		this.OS = this.searchString(this.dataOS) || "an unknown OS";
        // alert(this.browser);
    },
	searchString: function (data) {
		for (var i=0;i<data.length;i++)	{
			var dataString = data[i].string;
			var dataProp = data[i].prop;
			this.versionSearchString = data[i].versionSearch || data[i].identity;
			if (dataString) {
				if (dataString.indexOf(data[i].subString) != -1) {
					return data[i].identity;
                }
            }
			else if (dataProp) {
				return data[i].identity;
            }
        }
	},
	searchVersion: function (dataString) {
		var index = dataString.indexOf(this.versionSearchString);
		if (index == -1) {
            return;
        }
		return parseFloat(dataString.substring(index + this.versionSearchString.length + 1));
	},
    isChrome: function() {
        return (navigator.userAgent.toLowerCase().indexOf('chrome') > -1);  
    },
    isFirefox: function() {
        return (this.browser == "Firefox");    
    },
    isIE: function() {
        return (this.browser == "Explorer");        
    },
    isIE6: function() {
        return ((this.browser == "Explorer") && (BrowserDetect.version == 6));
    },
    isSafari: function() {
        return (this.browser == "Safari");        
    },
    dataBrowser: [
		{ 	string: navigator.userAgent,
			subString: "OmniWeb",
			versionSearch: "OmniWeb/",
			identity: "OmniWeb"
		},
		{
			string: navigator.vendor,
			subString: "Apple",
			identity: "Safari"
		},
		{
			prop: window.opera,
			identity: "Opera"
		},
		{
			string: navigator.vendor,
			subString: "iCab",
			identity: "iCab"
		},
		{
			string: navigator.vendor,
			subString: "KDE",
			identity: "Konqueror"
		},
		{
			string: navigator.userAgent,
			subString: "Firefox",
			identity: "Firefox"
		},
		{
			string: navigator.vendor,
			subString: "Camino",
			identity: "Camino"
		},
		{		// for newer Netscapes (6+)
			string: navigator.userAgent,
			subString: "Netscape",
			identity: "Netscape"
		},
		{
			string: navigator.userAgent,
			subString: "MSIE",
			identity: "Explorer",
			versionSearch: "MSIE"
		},
		{
			string: navigator.userAgent,
			subString: "Gecko",
			identity: "Mozilla",
			versionSearch: "rv"
		},
		{ 		// for older Netscapes (4-)
			string: navigator.userAgent,
			subString: "Mozilla",
			identity: "Netscape",
			versionSearch: "Mozilla"
		}
	],
	dataOS : [
		{
			string: navigator.platform,
			subString: "Win",
			identity: "Windows"
		},
		{
			string: navigator.platform,
			subString: "Mac",
			identity: "Mac"
		},
		{
			string: navigator.platform,
			subString: "Linux",
			identity: "Linux"
		}
	],
    getName : function() {
        return "BrowserDetect";
    }
};
BrowserDetect.init();

jQuery.fn.fadeToggle = function(speed, easing, callback) {
    return this.animate({opacity: 'toggle'}, speed, easing, callback);
};

/**
 * @class CatalogueNavigation
 * Controller for the DHTML catalogue navigation component
 * @singleton
 */
var CatalogueNavigation = function() {
    var NAVIGATION_CRITERIA_SELECTOR = '#catalogueNavigationContainer div.navigationCriteriaContainer';
    var FIRST_ANCHOR_SELECTOR = '#catalogueNavigationContainer div.navRow a'; // Used to determine the baseURL
    var SELECTED_ITEMS_SELECTOR = '.navRow selected';

    var SUB_ITEM_TOTAL_CLASS = 'navSubItemTotal';
    var EXPANDER_SUFFIX = 'Expander';
    var SUB_MENU_PREFIX = 'subMenu_';
    var SUB_ITEM_CLASS = 'navSubItemLabel';
    var NAVIGATION_CRITERIA_CONTAINER_CLASS = 'navigationCriteriaContainer';
    var NOT_NULL_IDENTIFIER = 'notNull';

    var initialised = false;
    var menus = new Array();
    var contentMinHeight = 1000;
    var baseUrl = null;
    var gridDirty = false;
    var totalsItemList;
    var animating = false;
    var haveDrillDownParams = false;
    // var requestCallback = null;

    var initialise = function() {
        var i;
        if (!initialised) {
            if (typeof(logger) == "undefined") {
                if (LogManager) LogManager.init();
            }
            // Use jQuery selectors to synchronise the navigation state with the server
            var nav, menu, menuId, item, navMenus = $(NAVIGATION_CRITERIA_SELECTOR);
            // Iterate through all of the menus rendered by the server
            for (i=0; i < navMenus.length; i++) {
                nav = navMenus[i];
                menuId = nav.id;
                menu = createMenu(menuId, nav);
            }

            var selectedItems = $(SELECTED_ITEMS_SELECTOR);
            for (i=0; i < selectedItems.length; i++) {
                item = selectedItems[i];
                menuId = item.parentNode.id;
                menu = menus[menuId];
                if (menu.static) {
                    menu.activeItem = item;
                    logger.info('Static menu['+menuId+'].activeItem = ' + item.id);
                }
                else {
                    toggleFilter(menu, item);
                }
                haveDrillDownParams = true;
            }

            logger.info("CatalogueNavigation.initialise() : " + i + " navigation menus initialised");            
            // Set the baseURL from the first anchor href
            setBaseUrl($(FIRST_ANCHOR_SELECTOR)[0]);
            // initialise the reset button
            // showResetButton(haveDrillDownParams, false);
            // Flag control as initialised
            initialised = true;
        }
    };
    
    // Sets the baseUrl for the product grid frame from the given anchor
    var setBaseUrl = function(anchor) {
        if (baseUrl === null) {
            if (typeof anchor !== "undefined") {
                baseUrl = anchor.pathname;
            }
            else {
                logger.error("CatalogueNavigation.setBaseUrl failed. Null anchor passed to method");
            }
        }
    };

    // Returns all menu items contained within the given menu
    var getAllMenuItems = function(menu) {
        return $('#' + menu.id + ' div.navRow');
    };

    // Returns the menu (dimension) that owns the given menu item (tableRow)
    var getMenu = function(div) {
        var navCriteriaContainer, menuId, result = null;

        if (!initialised) {
            initialise();
        }

        try {
            navCriteriaContainer = (div.className.indexOf(SUB_ITEM_CLASS) >= 0) ? div.parentNode.parentNode.parentNode : div.parentNode;
            if (navCriteriaContainer.className != NAVIGATION_CRITERIA_CONTAINER_CLASS) {
                throw "Not a navigation criteria container";
            }
            try {
                menuId = navCriteriaContainer.id;
                result = menus[menuId];
                if (result === null) {
                    throw "Failed with id = " + menuId;
                }
            }
            catch(f) {
                // gracefully fail at this point...
            }
        }
        catch(e) {
            logger.error('CatalogueNavigation.getMenu() : Exception = ' + (e.message || e));
        }
        return result;
    };

    var isSelectedSubItem = function(div) {
        var menu = getMenu(div);
        return (menu) && (menu.activeSubItems.contains(div));
    };

    var isSelectedItem = function(div) {
        var menu = getMenu(div);
        var result = isSelectedSubItem(div);
        return result || ((menu) && (menu.activeItem !== null) && (menu.activeItem == div));
    };

    var getAnchor = function(tableRow) {
        var anchor = null;
        if (tableRow.tagName == "TR") {
            var td = getChildElement(tableRow, "td");
            anchor = getChildElement(td, "a");
            setBaseUrl(anchor);
        }
        else {
            logger.error("CatalogueNavigation.getAnchor() : Unexpected type");
        }
        return anchor;
    };

    var setPageContentHeight = function() {
        if (FrameSizer !== undefined) {
            FrameSizer.size();
        }
    };

    var showSubMenu = function(id) {
        var subMenuContainer = $("#" + id + "List");
        if (subMenuContainer[0]) {
            subMenuContainer.fadeToggle("slow", handleExpandSubMenuComplete.createDelegate(this));
        }
    };

    var toggleExpandSubMenu = function(menu, div, animate) {
        animate = (animate !== undefined) ? animate : true;
        var id = getLabelAsTag(div);
        var subMenuContainer = (div.id.indexOf(SUB_MENU_PREFIX) == -1) ? $("#" + SUB_MENU_PREFIX + id) : $(div);
        if (subMenuContainer[0]) {
            // find the next tr with class of nav_list_sub to display
            menu.expandedItems.toggleMembership(subMenuContainer[0]);
            if (animate) {
                subMenuContainer.slideToggle(null, handleExpandSubMenuComplete.createDelegate(this, [id]));
                animating = true;
            }
        }
    };

    var getExpander = function(id) {
        var result;

        id = id.replace('subMenu_', '');
        result = $('#' + id + EXPANDER_SUFFIX + ' > a')[0];
        if (!result) {
            logger.error('Failed to find expander for id = ' + id);
        }
        return result;
    };

    var toggleExpander = function(id) {
        var expander = getExpander(id);
        if (expander) {
            expander.innerHTML = (expander.innerHTML == '+') ? '-' : '+';
        }
    };

    var handleExpandSubMenuComplete = function(id) {
        logger.info("animation complete - processing totals");
        toggleExpander(id);
        processTotals();
        setPageContentHeight();
        animating = false;
    };

    var disableItem = function(menu, div, disable) {
        // If cssClass not set defaults to 'normal'
        var expander = getExpander(div.id);
        if (disable) {
            div.className = 'navRow disabled';
            expander.parentNode.className = 'navRow disabled';
        } else {
            setItemStyle(div, menu);
            if (expander) {
                expander.parentNode.className = 'navRow';                
            }
        }
    };

    var handleStaticItemClick = function(div) {
        var menu = getMenu(div);
        // Reset the menu
        resetMenu(menus['navCriteriaDrillDown']);
        // Select the item
        selectItem(menu, div);
        // refresh the product grid
        if (gridDirty) {
            request();
        }
    };

    // Pre: div is not disabled
    var handleItemClick = function(div) {
        var menu = getMenu(div);
        // select this item if this item is not currently selected
        selectItem(menu, div);
        // refresh the product grid
        request();
    };

    var handleItemTotalChange = function(elt, item) {
        var menu;

        try {
            menu = getMenu(elt);
            if (isExpandableMenu(menu, item)) {
                // If this is a non static menu then grey out rather than hide item
                disableItem(menu, elt, (item.count === 0));
                // disableItem(menu, getExpander(elt.id), (item.count == 0));
            }
            else {
                showItem(elt, (item.count !== 0));
            }
        }
        catch(e) {
            logger.error("CatalogueNavigation.handleItemTotalChange() : " + e.message);
        }
    };

    var isExpandableMenu = function(menu, item) {
        return ((item.level == 1) && (!menu.static));
    };

    var processTotals = function() {
        var i, item, element, selector, elt;
        if (totalsItemList) {
            for (i=0; i < totalsItemList.length; i++) {
                item = totalsItemList[i];
                // selector = (item.level == 0) ? '#' + item.label + 'Total' : '#subMenu_' + item.parentLabel + ' > #' + item.label + ' > #' + item.label + 'Total';
                selector = '#' + item.label + 'Total';
                element = $(selector)[0];
                if (element) {
                    elt = element.parentNode; 
                    element.innerHTML = item.count;
                    // Hide the menu item if it's total is zero, show otherwise
                    handleItemTotalChange(elt, item);
                    // showItem(elt, (item.count != 0));
                }
                else {
                    logger.error("CatalogueNavigation.processTotals() : cannot find element using jQuery selector '" + selector + "'");
                }
            }
            totalsItemList = null;
        }
        else {
            logger.error("CatalogueNavigation.processTotals() : totalsItemList is null");
        }
    };

    var setSelected = function(menu, div) {
        if (menu.activeItem != div) {
            menu.activeItem = div;
            setItemStyle(div, menu, "selected");

            if (menu.parent) {
                menu.parent.activeChild = menu;
            }

            // Reset the filter menu to it's default state
            resetMenu(menus['navCriteriaFilter']);

            gridDirty = true;
        }
    };

    var clearSubSelections = function(menu) {
        // remove sub item values
        //delete menu.activeSubItem;
        //delete menu.subValue;
    };

    /*
    var deselectActiveSubItem = function(item) {
        // hack - TODO:
        var div = menu.activeSubItem;
        if (div) {
            setNavSubItemLabelClass(div);
            clearSubSelections(menu);
            gridDirty = true;
        }
    }; */

    var isSelected = function(menu, div) {
        var result = false;
        if (menu.static) {
            result = (menu.activeItem == div);
        }
        else {
            return menu.filters.contains(div);
        }
        return result;
    };

    var subItemIsSelected = function(menu, div) {
        var id = div.id.replace(EXPANDER_SUFFIX, '');
        var result = false;

        forEachByIndex(menu.activeSubItems, function(item) {
            var itemId = item.id.replace(/_navSubItem[a-zA-Z0-9]+/,'');
            if (itemId.indexOf(id) > -1) {
                result = true;
                toggleActiveSubItem(menu, item);
                gridDirty = true;
            }
        });

        return result;
    };

    var subMenuIsExpanded = function(menu, div) {
        var id = div.id.replace(EXPANDER_SUFFIX, '');
        var result = false;

        forEachByIndex(menu.expandedItems, function(item) {
            result = result || (item.id.indexOf(id) > -1);
        });

        return result;
    };

    var setNavSubItemLabelClass = function(div, className) {
        className = (className === null) ? '' : ' ' + className;
        if (div.className.indexOf(SUB_ITEM_CLASS) > -1) {
            // set the style
            div.className=SUB_ITEM_CLASS + className;
        }
        else {
            logger.error('CatalogueNavigation.setNavSubItemLabelClass() : Unexpected element type.');
        }
    };

    /* Deselect the currently selected item */
    var deselectSelected = function(menu, mouseOver) {
        var style, oldActiveItem = menu.activeItem;
        if (oldActiveItem) {
            // deselect any active sub menu item
            // deselectActiveSubItem(menu);
            if (menu.activeChild) {
                deselectSelected(menu.activeChild, false);
                menu.activeChild = null;
            }
            // If the item has an open submenu - close it
            // expandSubMenu(getId(oldActiveItem));
            showSubMenu(getLabelAsTag(oldActiveItem));
            // release the active item
            menu.activeItem = null;
            // set the class of the menu item
            style = (mouseOver) ? 'highlighted' : '';
            setItemStyle(oldActiveItem, menu, style);
            gridDirty = true;
        }
    };

    var getLabelAsTag = function(div) {
        var result = div.id, pos = result.indexOf(EXPANDER_SUFFIX);
        if (pos >= 0) {
            result = result.substring(0, pos);
        }
        return result;
    };

    var setDimmed = function(menu, div) {
        if (!isDisabledItem(div)) {
            setItemStyle(div, menu, 'dimmed');
        }
    };

    var setHighlighted = function(menu, div) {
        // Set the currently highlighted item to dimmed
        setItemStyle(menu.highlightedItem, menu, 'dimmed');
        // Set the given item to highlighted
        setItemStyle(div, menu, 'highlighted');
        menu.highlightedItem = div;
        // Set style of all other items on this menu to dimmed
        getAllMenuItems(menu).each(function(count, item) {
            if (item != div) {
                setDimmed(menu, item);
            }
        });
    };

    var setItemStyle = function(div, menu, cssClass) {
        cssClass = cssClass || '';
        var newClass;

        if (div !== null) {
            // If cssClass not set defaults to 'normal'
            newClass = (div == menu.activeItem) ? 'navRow selected' : 'navRow';
            if ((menu.static) && (menu.activeItem == div)) {
                newClass += ' highlighted';
            }
            else {
                newClass += (div == menu.highlightedItem) ? ' highlighted' : '';
            }
            newClass += (menu.filters.contains(div)) ? ' filtered' : '';
            newClass += (cssClass.length > 0) ? ' ' + cssClass : '';
            if (div.className != newClass) {
                div.className = newClass;
            }
        }
    };

    var setUnHighLighted = function(menu, div) {
        menu.highlightedItem = null;
        setItemStyle(div, menu);
        // Set style of all other items to normal
        getAllMenuItems(menu).each(function(count, item) {
            if (item != div) {
                if (!isDisabledItem(item)) {
                    setItemStyle(item, menu);
                }
            }
        });
    };

    var getAnchorFromItem = function(item) {
        var itemId = "INVALID", anchor = null;

        if (item !== null) {
            try {
                itemId = item.id;
                anchor = $(item).children("a")[0];
            }
            catch(e) {
                logger.error("CatalogueNavigation.getAnchorFromItem() : unable to extract href from item having id = " + itemId);
                logger.error("Exception = " + e.message);
            }
        }
        return anchor;
    };

    var buildRequestHref = function(menu, href) {
        var i, anchor = getAnchorFromItem(menu.activeItem);
        if (anchor !== null) {
            // Add a querystring parameter for the active item
            href = appendHref(href, anchor.search);
            haveDrillDownParams = true;
        }
        if (menu.activeSubItems.length > 0) {
            for (i=0; i<menu.activeSubItems.length; i++) {
                anchor = getAnchorFromItem(menu.activeSubItems[i]);
                href = appendHref(href, anchor.search);
            }
            haveDrillDownParams = true;
        }
        if (!menu.static) {
            // Add all filters
            for (i=0; i<menu.filters.length; i++) {
                href = appendHref(href, '?' + menu.filters[i].id + '=' + NOT_NULL_IDENTIFIER);
            }
            haveDrillDownParams = true;
        }
        return href;
    };

    // Perform a server request to refresh the iframe with a product grid
    var request = function() {
        var href = baseUrl;
        // Iterate through all menus and add their querystring name/value pairs
        haveDrillDownParams = false;
        forEach(menus, function(menu, key) {
            href = buildRequestHref(menu, href);
        });
        // Reload the iframe using the extended href
        logger.info("Requesting frame: " + href);
        frames['bodyIFrame'].location.href = href;
        gridDirty = false;
    };

    var appendHref = function(href, value) {
        value = value.replace('?','&');
        var result = href + value.replace(/\&debug=[a-zA-Z0-9]+&/,'&');
        if (!result.match(/\?/)) {
            result = result.replace('&','?');
        }
        return result;
    };

    var checkMenuMouseOut = function(target, div, menu) {
        if (div == menu.highlightedItem) {
            setUnHighLighted(menu, div)
        }
    };

    var isDisabledItem = function(div) {
        return (div.className.indexOf('disabled') > -1);  
    };

    var isEnabledItem = function(div) {
        return (div && (!isDisabledItem(div)));          
    };

    var handleMouseOverItem = function(div) {
        if (isEnabledItem(div)) {
            var menu = getMenu(div);
            if (div != menu.highlightedItem) {
                setHighlighted(menu, div);
            }
        }
    };

    var getChildElement = function(parent, childElementType) {
        return $(parent).children(childElementType)[0];
    };

    // Removes filters where a subitem has been selected
    // as we don't wish to have filters and subitems of the same category selected
    /*
    var removeLegacyFilters = function(menu) {
        // menu.filters
        forEachByIndex(menu.filters, function(filter) {
            forEachByIndex(menu.activeSubItems, function(subItem) {
                if (subItem.id.indexOf(filter.id) > -1) {
                    removeFilter(menu, filter);
                }
            });
        });
    } */

    var getParentFilter = function(item) {
        return $('#' + item.id.replace(/_navSubItem[a-zA-Z0-9]+/,''))[0];
    };

    var toggleActiveSubItem = function(menu, elt) {
        // requestCallback = toggleExpander.createDelegate(this, [elt.parentElement.id.replace(SUB_MENU_PREFIX, '')]);
        // if we have selected the subitem we must ensure the parent filter is selected
        var div = getParentFilter(elt);
        if (!menu.activeSubItems.contains(elt)) {
            if (!isSelected(menu, div)) {
               selectItem(menu, div);
            }
            // removeLegacyFilters(menu);
        }
        // Select the subitem
        menu.activeSubItems.toggleMembership(elt);
        // Set the class
        setNavSubItemLabelClass(elt, menu.activeSubItems.contains(elt) ? 'subselected' : null);
        logger.info('SubItem[' + elt.id + '] ' + ((menu.activeSubItems.contains(elt)) ? '' : 'de') + 'selected');
        gridDirty = true;
    };

    var deselectSelectedChildren = function(menu, div) {
        forEachByIndex(menu.activeSubItems, function(item) {
            if (item.id.indexOf(div.id) > -1) {
                toggleActiveSubItem(menu, item);
            }
        });
    };

    var removeFilter = function(menu, filter) {
        menu.filters.toggleMembership(filter);
        setItemStyle(filter, menu);
    };

    var toggleFilter = function(menu, div) {
        var msgPrefix = 'de';
        removeFilter(menu, div);
        // if the filter is selected - deselect any selected child item
        if (!menu.filters.contains(div)) {
            deselectSelectedChildren(menu, div);
            msgPrefix = '';
            // and close the submenu
            // no more - functionality removed.
            /* if (subMenuIsExpanded(menu, div)) {
                toggleExpandSubMenu(menu, div);
            }  */
        }
        logger.info('Filter[' + div.id + '] ' + msgPrefix + 'selected');
        gridDirty = true;
    };

    var selectItem = function(menu, div, value) {
        // deselect the currently selected item
        if (!menu.static) {
            toggleFilter(menu, div);
        }
        else {
            if (!isSelected(menu, div)) {
                deselectSelected(menu);
            }
            // set this item to selected
            setSelected(menu, div);
        }
    };

    var createMenu = function(menuId, nav) {
        var menu = {};
        var parts = menuId.split('_');
        var parentId = parts[0];

        menu.level = parts.length - 1;
        menu.id = menuId;
        // TODO : static & filters - required?
        // TODO : this is a total hack - need to pass static congif to the client inside jsp
        if (parentId.toLowerCase() == "navcriteriaassetclass") {
            menu.static = true;
        }
        menu.activeItem = null;
        menu.filters = [];
        // Set the expanded menu items
        menu.expandedItems = [];
        // Set the active sub items
        menu.activeSubItems = [];
        $('#' + menuId + ' .subselected').each(function(index, item) {
            toggleActiveSubItem(menu, item);
            // var parent = $('#' + item.id.replace(/_navSubItem[a-zA-Z0-9]+/,''))[0];
            toggleExpandSubMenu(menu, getParentFilter(item), false);
        });

        menu.highlightedItem = null;
        if (menu.level == 1) {
            menu.parent = menus[parentId];
            if (nav && nav.parentNode) {
                if (nav.parentNode.style.display != "none") {
                    menu.parent.activeChild = menu;                   
                }
            }
        }
        menus[menuId] = menu;
        return menus[menuId];
    };

    var showItem = function(div, display) {
        var style = (display) ? "block" : "none";
        
        if (div.style.display === "") {
            div.style.display = "block";
        }
        if (style != div.style.display) {
            // $(div).slideToggle("slow", requestCallback);
            $(div).slideToggle("slow");
            // requestCallback = null;
        }
    };

    var assignTotals = function(itemList) {
        totalsItemList = itemList;
        logger.info("received totals list. animating = " + animating);
        if (!animating) {
            processTotals();
        }
    };

    var resetMenu = function(menu) {
        try {
            var childMenu = menu.activeChild;
            if (childMenu) {
                deselectSelected(childMenu);
            }
            if (menu.activeItem !== null) {
                // Add a querystring parameter for the active item
                deselectSelected(menu);
            }

            // Close all expanded menus
            forEachByIndex(menu.expandedItems, function(item) {
                toggleExpandSubMenu(menu, item);
            });

            // Reset active sub items
            forEachByIndex(menu.activeSubItems, function(item) {
                toggleActiveSubItem(menu, item);
            });

            // Reset all filters
            forEachByIndex(menu.filters, function(item) {
                toggleFilter(menu, item);
            });
        }
        catch(e) {
            logger.error("CatalogueNavigation.resetMenu : " + e.message || e);
        }
    };

    var forEachByIndex = function(items, callback) {
        var clone, item, i, result = false;

        if (items && items.length && items.length > 0) {
            clone = items.slice();
            for (i=0; i<clone.length; i++) {
                item = clone[i];
                callback.call(this, item);
            }
            result = true;
        }
        return result;
    };

    var deselectSelectionsNotIn = function(menu, key, list) {
        // Remove subitems which are not in the breadcrumbs
        if (menu.activeSubItems.length > 0) {
            forEachByIndex(menu.activeSubItems, function(subItem) {
                if (!list.contains(subItem.id)) {
                    toggleActiveSubItem(menu, subItem);
                    // close the menu
                    toggleExpandSubMenu(menu, subItem.parentNode);
                    logger.debug("CatalogueNavigation.deselectSelectionsNotIn() : from menu["+key+"] removing item " + subItem.id);
                }
            });         
        }

        // Deselect filters which are not in the breadcrumbs
        if (menu.filters.length > 0) {
            forEachByIndex(menu.filters, function(filter) {
                if (!list.contains(filter.id)) {
                    removeFilter(menu, filter);
                    logger.debug("CatalogueNavigation.deselectSelectionsNotIn() : from menu["+key+"] removing filter " + filter.id);
                }
            });
        }
    };

    // Handle "back button", check the params used to generate the grid
    var handleBackButton = function(paramList) {
        // deselect those which are not in the list
        forEach(menus, function(menu, key) {
            deselectSelectionsNotIn(menu, key, paramList);
        });

        // and select those which are on the list but not active
        /*
        forEach(paramList, function(param) {
            var menu, div;
            div = document.getElementById(param);
            if (!isSelectedItem(div)) {
                menu = getMenu(div);
                if (div.className == 'navRow')
                    setSelected(menu, div);
                else
                    setSelectedSubItem(menu, div);
            }
        }); */
    }

    return {
        /*
         * Public methods
         */
        clickItem : function(e, elt, value) {
            var div = (elt.tagName == "A") ? elt.parentNode.parentNode : elt;
            if (isEnabledItem(div)) {
                handleItemClick(div);
            }
        },

        // Static menu item click
        clickStaticItem : function(e, elt, value) {
            var div = (elt.tagName == "A") ? elt.parentNode.parentNode : elt;
            handleStaticItemClick(div);
        },

        toggleSubMenuDisplay : function(item) {            
            if (isEnabledItem(item)) {
                var menu = getMenu(item);
                // if there is a subitem selected...
                if (subItemIsSelected(menu, item)) {
                    // deselect it
                    deselectSelectedChildren(menu, item);
                }
                else {
                    toggleExpandSubMenu(menu, item);
                }
                // refresh the product grid
                if (gridDirty) {
                    request();
                }
            }
        },

        mouseOverItem : function(e, elt) {
            handleMouseOverItem(elt);
        },

        mouseOutItem : function(e, div) {
            var event = e || window.event;
            var menu = getMenu(div);
            var target = event.toElement || event.relatedTarget;
            // If we have "moused out" to a child element, do nothing
            if ($(target).isChildOf('#'+div.id)) {
                return true;
            }
            // check if we have "moused out" of the menu altogether
            checkMenuMouseOut(target, div, menu);
        },

        mouseOverSubItem : function(e, div, parentItemId) {
            var parentItem = $('#' + parentItemId)[0];
            if (!isSelectedSubItem(div)) {
                setNavSubItemLabelClass(div, 'hover');
            }
            // Ensure that this subitem's parent becomes the highlighted item
            handleMouseOverItem(parentItem);
        },

        mouseOutSubItem : function(e, div, parentItemId) {
            var event = e || window.event;
            var elt = event.toElement || e.relatedTarget;
            var parentTableRow = $('#' + parentItemId)[0];
            var menu = getMenu(div);

            if (!isSelectedSubItem(div)) {
                // If we have "moused out" to a child element, do nothing
                if (($(elt).isChildOf('#'+div.id)) || (elt == div)) {
                    return true;
                }
                div.className='navSubItemLabel';
            }
            // check if we have "moused out" of the menu altogether
            checkMenuMouseOut(elt, parentTableRow, menu);
        },

        clickSubItem : function(e) {
            var event = e || window.event;
            var target = event.srcElement || event.target;
            var subItem = (target.tagName == "A") ? target.parentNode : target;
            var menu = getMenu(subItem);
            toggleActiveSubItem(menu, subItem);
            // refresh the product grid
            if (gridDirty) {
                request();
            }
            // Ensure href is ignored by the browser
            return false;
        },

        // Updates the totals in the navigation items according to the itemList
        updateState : function(data) {
            var handled = false;
            var method = "CatalogueNavigation.updateState() : ";

            if (data) {
                // Handle "back button", check the params used to generate the grid
                if (data.params) {
                    handleBackButton(data.params);
                    gridDirty = false; // we are synchronising TO the grid
                    handled = true;
                }
                if (data.items) {
                    logger.info(method + "grid size = " + data.total);
                    // Ensure that the nav state is synchronised to the latest grid
                    // If we are displaying all products, reset the navigation if required
                    // TODO : reimplement ?? - removed during redesign

                    if ((typeof(data.paramCount) != "undefined") && (data.paramCount == 0) && (haveDrillDownParams === true)) {
                        this.reset(false);
                        gridDirty = false; // we are synchronising TO the grid
                    }
                    // Render the totals for this result set
                    assignTotals(data.items);
                    handled = true;
                }
                if (!handled) {
                    logger.error(method + "data in unexpected format " + data.toString());
                }
            }
            else {
                logger.error(method + "parameter [data] cannot be null");
            }
        },

        // Resets the navigation to its default state with no selected items
        reset : function(refresh) {
            // default refresh to true
            refresh = (refresh === undefined) ? true : refresh;
            forEach(menus, function(menu) {
                resetMenu(menu);
            });
            // Request a new grid
            if (refresh) {
                request();
            }
        },

        init : function() {
            initialise();
        }
    };
}();

/**
 * @class Ajax
 * Provides an interface to the jQuery Ajax functionality
 * @singleton
 */
var Ajax = function() {
    // private singleton instances
    var type = "GET";
    var dataType = "text";
    var emptyResultSet = "<data>empty</data>";

    // Ensure the url is unique (cache busting)
    var ensureUnique = function(url) {
        var currentDate = new Date();
        var uniqueId = "&c=" + (currentDate.getTime() / 1000);
        return url + uniqueId;
    };

    var logError = function(res, status) {
        if (logger) {
            if ((res.responseText) && (res.responseText.toLowerCase().indexOf("Page not found"))) {
                status = status + " (404)";                
            }
            logger.error('Cannot load xml file with status = ' + status);
        }
    };

    var handleXmlResultsSuccess = function(xml, onEmpty, onSuccess, onFailure) {
        if (xml.toLowerCase() == emptyResultSet) {
            onEmpty();
        }
        else {
            onSuccess(xml);
        }
    };

    return {

        /**
         * Performs an ajax search
         * @param {String} url; the RPC to invoke
         * @param {function} onSuccess; the delegate to call on callback for success
         * @param {function} onFailure; the delegate to call on callback on failure
         */
        call : function(url, onSuccess, onEmpty, onFailure) {
            $.ajax({
                type: "GET",
                url: ensureUnique(url),
                dataType: dataType,
                complete: function(res, status){
                    if ( status == "success" || status == "notmodified" ) {
                        // alert(res.responseText);
                    } else {
                        logError(res, status);
                    }
                },

                success: function(xml) {
                    // Call the onSuccess delegate
                    if (xml.toLowerCase() == emptyResultSet) {
                        onEmpty();
                    }
                    else {
                        onSuccess(xml);
                    }
                },

                failure: function() {
                    // Call the onFailure delegate
                    onFailure();
                }
            });
        },

        post : function(url, data, onSuccess, onEmpty, onFailure, type) {
            $.ajax({
                type: "POST",
                url: url,
                data: data,
                dataType: type || dataType,
                complete: function(res, status){
                    if ( status == "success" || status == "notmodified" ) {
                        // alert(res.responseText);
                    } else {
                        logError(res, status);
                    }
                },

                success: function(results) {
                    // Call the onSuccess delegate
                    if (dataType == "xml") {
                        handleXmlResultsSuccess(results, onEmpty, onSuccess, onFailure);
                    }
                    else {
                        // json
                        onSuccess(results);
                    }
                },

                failure: function() {
                    // Call the onFailure delegate
                    onFailure();
                }
            });
        },

        setDataType : function(type) {
            // type should = {"xml", "text", "json"}
            dataType = type
        }
    }
}();





var cmsTooltip = function(elt, label, key){
    var activeElt;
    var xOffset = 10;
    var yOffset = 20;

    if (activeElt != elt) {
        activeElt = elt;

        $(elt).hover(function(e){
            this.t = '<p id=\'tooltip\'><span style=\'font-weight:bold\'>Label:</span> ' + label + '<br/><span style=\'font-weight:bold\'>CMS Key:</span> ' + key + '</p>';
            this.title = "";
            $("body").append(this.t);
            $("#tooltip")
                .css("top",(e.pageY - xOffset) + "px")
                .css("left",(e.pageX + yOffset) + "px")
                .fadeIn("fast");
        },

        function() {
            this.title = this.t;
            $("#tooltip").remove();
        });

        $(elt).mousemove(function(e){
            $("#tooltip")
                .css("top",(e.pageY - xOffset) + "px")
                .css("left",(e.pageX + yOffset) + "px");
        });
    }
};

// TODO : class me..., js cms file mgt..., magic strings...
var cmsEditImage = function(e, path, title) {
    // Cancel the propagation bubble    
    var event = e || window.event;
    event.cancelBubble = true;
    // Obtain the name of the existing frame url
    var INNER_FRAME = 'bodyIFrame';
    var target = frames[INNER_FRAME] || document;    
    var currentLocation = target.location.pathname + escape(target.location.search);
    target.location.href = '/imageEdit.action?returnAction=' + currentLocation + '&path=' + path + '&title=' + title;
    return false;
};

/**
 * @class Menu
 * Controller for the top level menu bar
 * @singleton
 */
var Dropdown = function() {
    var DEPRESSED_CLASS = 'down';
    var DEPRESSED_ICON = 'down_arrow_white';
    var NORMAL_ICON = 'down_arrow_grey';
    var open = false;
    var widget;
    var contents;
    var defaultText = null;

    var removeClass = function() {
        if (widget.hasClass(DEPRESSED_CLASS) && (!open)) {
            widget.removeClass(DEPRESSED_CLASS);
	        var children = widget.children('img');
            if ( children.length > 0 )
	            children[0].src = widget.children('img')[0].src.replace(DEPRESSED_ICON, NORMAL_ICON);
        }
    };

    var toggleStyle = function(id) {
        // animate = true || animate;
    	//open = !open;
        if (!widget.hasClass(DEPRESSED_CLASS)) {
        	widget.addClass(DEPRESSED_CLASS);
            if (widget.children('img')[0]) {
            widget.children('img')[0].src = widget.children('img')[0].src.replace(NORMAL_ICON, DEPRESSED_ICON);
            }
            open = true;
            $('#countrySelectorContents').show();
        }
        else {
            open = false;
            removeClass();
        }
    };

    var setDefaultText = function() {
        if (defaultText === null) {
            var div = $('#countrySelector');
            defaultText = $(div).parent().find('a').text();
        }
    };

    var positionContents = function(widget, contents) {
        var eltLeft = getElementLeft(widget);
        var contentsLeft = getElementLeft(contents);
        if (eltLeft != contentsLeft) {
            contents.style.marginLeft = (eltLeft - 20) + 'px';
        }
    };

    return {
        /**
         * Style the selection prior to going to the href
         */
        handleClick : function(div, closable) {
    		var id = div.id;
            widget = $('#' + id);
            toggleStyle(id);
            /*if (id && ((div.className.indexOf('down') == -1) || closable)) {
                toggleStyle(id);
                alert('after toggle style, handleclick');
                contents = $('#' + id + 'Contents');
                alert('after toggle style, handleclick 2');
                positionContents(widget[0], contents[0]);
                alert('after toggle style, handleclick 3');
                contents.slideToggle(null, removeClass.createDelegate(this));
                alert('after toggle style, handleclick 4');
            }*/
        },

        handleSelect : function(li, callback) {
            var id = li.id;
            var anchor = li.childNodes[0]; 
            var value = anchor.innerHTML;

            widget = $('#countrySelector');
            contents = $(li).parent().parent();
            setDefaultText();

            // Set the dropdown value
            widget.find('a').text(value);
            // Immediately hide the contents dropdown (no animation)
            toggleStyle(widget[0].id);
            // Wokaround for the ie7 "sticky hover css" bug (works most of the time)
            contents.slideToggle(20);            
            // Call the callback delegate
            if (callback) {
                callback.call(this, id, value);
            }
        },

        reset : function() {
        	var div = $('#countrySelector');
        	div.parent().find('a').text(defaultText);
        	// div.removeClass('down');
            // contents
            $('.dropDownContainer').show();
        }
    };
}();


/**
 * @class FormUtils
 * Form utilities class
 * @singleton
 */
var FormUtils = function() {
    var DEFAULT_FLASH_VERSION = -1;

    return {
        appendFlashSupportQueryString : function(form) {
            // Appends a querystring detailing the Flash version supported to the sign in form action
            var version = DEFAULT_FLASH_VERSION;

            if (FlashDetect.installed) {
                version = FlashDetect.major;
            }

            form.action = form.action + '?flashVersion=' + version;
        }
    }
}();


/**
 * @class Glossary
 * Controller for the Glossary popup box
 * @singleton
 */
var Glossary = function() {
    var arrowWidth = 38;
    var anchorLowerMargin = 24;
    var toolTipWidth = 450;
    var pageBodyLeftPadding = 20; // the left padding of the text on the page. Required for ie bug workaround

    var setInfoTipPosition = function(anchor) {
        var infoTip = $('#infoTip')[0];
        var position = $(anchor).position();
        // hack for splash confirmation error - check works fine with glossary!!!
        var position2 = getElementTop(anchor) + 4;
        // var tipTop = position.top + anchorLowerMargin;
        var tipTop = position2 + anchorLowerMargin;
        // Work around for ie7 bug where top offset is out by one line where the element
        // is on the left most edge of the container (workaround not needed in ie6)
        if (position.left == 20 && (BrowserDetect.isIE() && (BrowserDetect.version == 7))) {
            tipTop += anchorLowerMargin - 8; // was + 2             
        }
        infoTip.style.top = tipTop + "px";
    };

    var setContent = function(term, value) {
        var infoTipContent = $('#infoTipContent')[0];
        term = term.toUpperCase();
        infoTipContent.innerHTML = "<span class='header'>" + term + "</span><br/><p class='definition'>" + value + "</p>";

        var base = infoTipContent.parentNode.clientHeight;
        var x = base + 20 + "px";

        // set the height
        var c = $('#infoTip .c')[0];

        c.style.height = x;

        $('#infoTip .r')[0].style.height = x;
        $('#infoTip .l')[0].style.height = x;

        var infoTipGlass = $('#infoTipGlass')[0];
        infoTipGlass.style.height = base + 50 + "px";

        var hack = base + 30 + "px";
        $('#infoTip .b')[0].style.top = hack;
        $('#infoTip .br')[0].style.top = hack;
        $('#infoTip .bl')[0].style.top = hack;
    };

    var getColumn = function(anchor) {
        var column, result = null;
        $(['p','li','h5','h4','h3','h2','h1','span']).each(function(count, item) {
            column = $(anchor).parents(item[count])[0];
            if ((typeof(column) != "undefined") && (result == null)) {
                result = column;
            }
        });
        return result;
    };

    var confirmationX = function(anchor) {
        var position = $(anchor).position();
        position.left = getElementLeft(anchor);
        var midPoint = Math.floor(position.left + (anchor.offsetWidth / 2)) - 18;
        var arrowPos = Math.floor((toolTipWidth - arrowWidth) / 7) + 120;
        // Set the x axis position
        $('#infoTipArrow')[0].style.left = arrowPos + "px";
        var elt = $('#infoTip')[0];
        elt.style.left = midPoint - arrowPos + "px";
    };

    var findAnchorX = function(anchor) {
        var position = $(anchor).position();
        var midPoint = Math.floor(position.left + (anchor.offsetWidth / 2));
        var column = getColumn(anchor);
        if (column !== null) {
            var columnWidth = column.offsetWidth;
            midPoint = midPoint - pageBodyLeftPadding;

            var arrowPos = Math.floor((toolTipWidth - arrowWidth) / 2);
            if (midPoint < (columnWidth / 7)) {
                // extreme left
                arrowPos = 20;
            }
            else if (midPoint < (columnWidth / 3)) {
                // left third
                arrowPos = Math.floor((toolTipWidth - arrowWidth) / 7);
            }
            else if (midPoint > (columnWidth * (2/3))) {
                // right third
                arrowPos = Math.floor((toolTipWidth - arrowWidth) / 4 * 3);
            }

            // Set the x axis position
            $('#infoTipArrow')[0].style.left = arrowPos + "px";
            var elt = $('#infoTip')[0];
            elt.style.left = midPoint - arrowPos + "px";
        }
        else {
            logger.error("Glossary.findAnchorX() : unable to determine anchor left offset");
        }
    };

    var display = function(show) {
        var infoTip = $('#infoTip')[0];
        infoTip.style.visibility = (show)?'visible':'hidden';
    };

    return {
        display : function(anchor, term, value) {
            if (typeof logger != "undefined") {
                logger.info("glossary.display() for term " + term);
            }
            setInfoTipPosition(anchor);
            setContent(term, value);
            findAnchorX(anchor);
            display(true);
        },

        landingConfirmation: function(anchor, term, value) {
            setInfoTipPosition(anchor);
            setContent(term, value);
            confirmationX(anchor);
            display(true);
        },

        close : function() {
            display(false);            
        },

        closeButtonMouseOver : function(img) {
            img.src = '/images/popup/close_hover.png';
        },

        closeButtonMouseOut : function(img) {
            img.src = '/images/popup/close.png';
        },

        setWidth : function(width) {
            toolTipWidth = width;    
        }
    };
}();


/**
 * @class Personalised
 * Controller for the personalised content
 * @singleton
 */
var Personalised = function() {
	
	// the Struts Action name that will be passed by JQuerys Ajax
	// capability to the Struts framework.
	var STRUTS_PERSONALISED_ACTION;
	
	var LOGIN_FORM = '#loginForm';
	var FORCE_PASSWORD_CHANGE_FORM = '#forcePasswordChangeForm';
	var USER_DETAILS_EDIT_FORM = '#userDetailsEditForm';
	
	ELEMENT_ERR_MSG_ID_END_MSG = 'Msg';
	ELEMENT_ERR_MSG_ID_END_LBL = 'Lbl';
	
	var ELEMENT_ERR_MSG_ID_END = ELEMENT_ERR_MSG_ID_END_LBL;
	
	//Login Form
	var USER_NAME_SIGN_IN_ELEMENT_ID = '#userNameSignIn';
	var PWD_SIGN_IN_ELEMENT_ID = '#passwordSignIn';
	
	// the JSP form html element id's for the form input elements
	var EMAIL_ELEMENT_ID = '#emailAddress';
	var USER_NAME_ELEMENT_ID = '#username';
	var PWD_ELEMENT_ID = '#password';
	var PWD_CONFIRM_ELEMENT_ID = '#confirmPassword';
	var FIRSTNAME_ELEMENT_ID = '#firstname';
	var SURNAME_ELEMENT_ID = '#surname';
	var WRK_TELEPHONE_ELEMENT_ID = '#workTelephone';
	var WRK_MOBILE_ELEMENT_ID = '#workMobile';
	var ORG_NAME_ELEMENT_ID = '#organisationName';
	var ORG_ADRS_ELEMENT_ID = '#organisationAddress1';
	var ORG_ADRS_2_ELEMENT_ID = '#organisationAddress2';
	var ORG_ZIP_ELEMENT_ID = '#organisationZip';
	var ORG_CITY_ELEMENT_ID = '#organisationCity';
	var ORG_STATE_ELEMENT_ID = '#organisationState';
	var ORG_COUNTRY_ELEMENT_ID = '#organisationCountry';
	
	// the JSP form html element lables for the form input elements
	var EMAIL_ELEMENT_LBL = 'Email address:';
	var USER_NAME_ELEMENT_LBL = EMAIL_ELEMENT_LBL;
	var PWD_ELEMENT_LBL = 'Password (min 6):';
	var PWD_CONFIRM_ELEMENT_LBL = 'Confirm password:';
	var FIRSTNAME_ELEMENT_LBL = 'First name:';
	var SURNAME_ELEMENT_LBL = 'Last name:';
	var WRK_TELEPHONE_ELEMENT_LBL = 'Office telephone:';
	var WRK_MOBILE_ELEMENT_LBL = 'Mobile telephone:';
	var ORG_NAME_ELEMENT_LBL = 'Organisation:';
	var ORG_ADRS_ELEMENT_LBL = 'Address 1:';
	var ORG_ADRS_2_ELEMENT_LBL = 'Address 2:';
	var ORG_ZIP_ELEMENT_LBL = 'Postcode:';
	var ORG_CITY_ELEMENT_LBL = 'City:';
	var ORG_STATE_ELEMENT_LBL = 'State:';
	var ORG_COUNTRY_ELEMENT_LBL = 'Country:';
	
	// the JSP form html element lables for the form input elements
	var STRONG_STRENGTH = 1;  // The function to check passwords requires a confirm password as well as a password field. It also requires a user name field.
	var MEDIUM_STRENGTH = 2;  // There is no functionality for this validation strength test.
	var WEEK_STRENGTH = 3;    // There is no functionality for this validation strength test.
	var ENOUGH_STRENGTH = 4;  // 
	var VALIDATION_STRENGTH = ENOUGH_STRENGTH;

	var GENERIC_ERR_MSG = 'Required field';

	var PASSWORD_ELEMENT_ERR_MSG = 'Enter password';
	var PASSWORD_ELEMENT_ERR_MSG2 =  'Min of 6 characters';
	
	var EMAIL_CONSTRUCT_MSG = 'This must be an email address';
	
	var EMAIL_ELEMENT_REGEX = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
	var EMAIL_ELEMENT_ERR_MSG = GENERIC_ERR_MSG;
	
	var USER_NAME_ELEMENT_REGEX = EMAIL_ELEMENT_REGEX;
	
	var USER_NAME_ELEMENT_ERR_MSG = 'Enter address';
	var USER_NAME_ELEMENT_ERR_MSG2 = 'Address not recognised';
	
	var EMAIL_ELEMENT_REGEX = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
	var EMAIL_ELEMENT_ERR_MSG = GENERIC_ERR_MSG;
	var EMAIL_ELEMENT_ERR_MSG2 = 'Address not recognised';
	
	var FIRSTNAME_ELEMENT_REGEX = /^([a-zA-Z])+$/;
	var FIRSTNAME_ELEMENT_ERR_MSG = GENERIC_ERR_MSG;
	
	var SURNAME_ELEMENT_REGEX = FIRSTNAME_ELEMENT_REGEX;
	var SURNAME_ELEMENT_ERR_MSG = GENERIC_ERR_MSG;
	
	var ORG_NAME_ELEMENT_REGEX = /^([a-zA-Z0-9])+$/;
	var ORG_NAME_ELEMENT_ERR_MSG = GENERIC_ERR_MSG;
	
	var USER_NAME_ELEMENT_REGEX = EMAIL_ELEMENT_REGEX;
	var USER_NAME_ELEMENT_ERR_MSG = GENERIC_ERR_MSG;
	
	var PASSWORD_ELEMENT_ERR_MSG = GENERIC_ERR_MSG;
	var PASSWORD_ELEMENT_ERR_MSG2 =  'Min of 6 characters';
	
	var WRK_TELEPHONE_ELEMENT_REGEX = /^([a-zA-Z\\s])+$/;
	var WRK_TELEPHONE_ELEMENT_ERR_MSG = GENERIC_ERR_MSG;
	
	var ORG_COUNTRY_ELEMENT_REGEX = /^([0-9\\s])+$/;
	var ORG_COUNTRY_ELEMENT_ERR_MSG = GENERIC_ERR_MSG;
	
	/**
	 * Wrapper to JQuery ajax call.
	 */
	var ajaxCall = function(successDelegate, emptyDelegate, failureDelegate) {
		//logger.debug("!!!!!!!!!!!! User registration request. ajaxCall() " );
		
		//logger.debug("!!!!!!!!!!!! User registration request ajaxCall() email = " + email + ", username = " + username + ", password = " + password);
		// data is a name value pairs 
		// the names correspond to the Java Struts Action
		// classes JavaBean attribute names.
		var data = {
		}
		
		//post : function(url, data, onSuccess, onEmpty, onFailure, type)
		Ajax.post(STRUTS_PERSONALISED_ACTION, data, successDelegate, emptyDelegate, failureDelegate, "html");
		STRUTS_PERSONALISED_ACTION = "";
	};
	
    
    var doPersonalisedLogInOutRefresh  = function(changeValue) {

		//alert("doPersonalisedLogInOutRefresh.in");
		
        ajaxCall(
        		successDelegatePersonalisedLogInOut.createDelegate(this),
        		emptyDelegatePersonalisedLogInOut.createDelegate(this),
        		failureDelegatePersonalisedLogInOut.createDelegate(this)
            );
//        ajaxCall(
//        		successDelegatePersonalisedLogInOut(this),
//        		emptyDelegatePersonalisedLogInOut(this),
//        		failureDelegatePersonalisedLogInOut(this)
//            );
        
    };
	
    var successDelegatePersonalisedLogInOut = function(response) {

		//alert("successDelegatePersonalisedLogInOut.in");
    	
        var field;
        
//    		field = getTopFrameElement('#dvLanguageSelector');
    		field = getTopFrameElement('#dvChangeProfileContainer');
    		//setElementTextForElement(response, field); // this works sort of
    		
//    		value = getElementTextForElement(field);
//    		alert(value);
    		
            var existingHTML = field.attr('innerHTML');
//    		alert(existingHTML);
            
         // Only update the field if the value has changed.
            if (existingHTML != response) {
//              // Update the field html
            	field[0].innerHTML = response;
            }
    	
	};
	
    var emptyDelegatePersonalisedLogInOut = function() {
		
	};
	
	var failureDelegatePersonalisedLogInOut = function() {
		
	};
    
    /**
     * Returns a jquery query result from the iframe
     * @param selector, the jquery selector
     * @returns jquery result set [array]
     */
    var getBodyFrameElement = function(selector) {
        var frame = window['bodyIFrame'];
        return frame.$(selector);
    };
    
    /**
     * Returns a jquery query result from the iframe
     * @param selector, the jquery selector
     * @returns jquery result set [array]
     */
    var getTopFrameElement = function(selector) {
//        var frame = window.parent.frames[1];
//        return frame.$(selector);
    	return window.top.$(selector);
    };
    
//    /**
//     * Updates the given html element with the provided value
//     * @param field, the target html element to be updated
//     * @param value, the new value to display
//     */
//     var updateField = function(field, value) {
//         var fadeColour = FADE_COLOUR_GREEN;
//
//         if (field.length >= 1) {
//             // Preformat the value for display
//             if (typeof value == "number") {
//                 fadeColour = getNumericFadeColour(value, field);
//                 value = formatNumericValue(field, value);                
//             }
//             else {
//                 fadeColour = FADE_COLOUR_NEUTRAL;
//             }
//
//             // Update the field inner html
//             updateFieldHTML(field, value, fadeColour);
//         }
//     };
//     
//     /**
//      * Updates the given html element with the provided value
//      * @param field, the target html element to be updated
//      * @param value, the new value to display
//      * @param fadeColour, the colour to fade
//      */
//      var updateFieldHTML = function(field, value, fadeColour) {
//          var existingHTML = field.attr('innerHTML');
//
//          // Only update the field if the value has changed.
//          if (existingHTML != value) {
//              // Update the field html
//              field.text(value);
//
//              // Fade out a colour to the target element
//              field.highlightFade({
//                  color: fadeColour,
//                  speed: 2000,
//                  iterator: FADE_TYPE
//              });
//          }
//      };

    
//	var ajaxCall = function(successDelegate, emptyDelegate, failureDelegate) {
//		logger.debug("!!!!!!!!!!!! Personalised request. ajaxCall() " );
//		
//		var username = getElementValueById(USER_NAME_ELEMENT_ID);
//		var password = getElementValueById(PWD_ELEMENT_ID);
//
//		logger.debug("!!!!!!!!!!!! Personalised request ajaxCall() username = " + username + ", password = " + password);
//		// data is a name value pairs 
//		// the names correspond to the Java Struts Action
//		// classes JavaBean attribute names.
//		var data = {
//				userNameSignIn : username,
//				passwordSignIn : password
//		}
//		
//		//post : function(url, data, onSuccess, onEmpty, onFailure, type)
//		Ajax.post(STRUTS_PERSONALISED_ACTION, data, successDelegate, emptyDelegate, failureDelegate, "json");
//		STRUTS_PERSONALISED_ACTION = "";
//		
//	};
	
	/**
	 * Return the element by the ID supplied.
	 */
	var getElementById = function(element_Id){
        return $(element_Id);
	};
	
	/**
	 * Return the value for the element ID passed.
	 */
	var getElementValueById = function(element_Id){
    	debugger;
        var elementValue = "";
        var elt = getElementById(element_Id);
        if (elt) {
            // The value from the form
            var elementValue = elt[0].value;
        }
        return elementValue;
	};
	
	/**
	 * Return the value for the element ID passed.
	 */
	var getElementInnerTextById = function(element_Id){
    	debugger;
        var elementValue = "";
        var elt = getElementById(element_Id);
        if (elt) {
            // The value from the form
            var elementValue = elt[0].innerText;
        }
        return elementValue;
	};
	
    /**
     * Toggle the visibility of the page xhtml element.
     * Takes two arguments; the visibility state to set and an element id.
     * visibility_state: 'visible', 'hidden'
     * element_Id: '#<your-xhtml-element-id>'
     * 
     * @author yearwaker
     */
    var setElementVisibilityById = function(visibility_state, element_Id) {
        var element_object = getElementById(element_Id);
        if (element_object) {
        	element_object[0].style.visibility = visibility_state;
        }
    };
    
    var setElementVisibilityForElement = function(visibility_state, element_object) {
        if (element_object) {
        	element_object[0].style.visibility = visibility_state;
        }
    };
    
    var setElementDisplayForElement = function(display_state, element_object) {
        if (element_object) {
        	element_object[0].style.display = display_state;
        }
    };

    var setElementColorForElement = function(color_state, element_object) {
        if (element_object) {
        	element_object[0].style.color = color_state;
        }
    };
    
    var setElementTextForElement = function(text_state, element_object) {
        if (element_object) {
        	if (typeof(element_object[0].text) != "undefined" ) {
        		element_object[0].text = text_state;
        	} else if (typeof(element_object[0].textContent) != "undefined" ) {
        		element_object[0].textContent = text_state;
        	} else if (typeof(element_object[0].innerText) != "undefined" ) {
        		element_object[0].innerText = text_state;
        	}
        }
    };
    
    var getElementTextForElement = function(element_object) {
        if (element_object) {
        	if (typeof(element_object[0].text) != "undefined" ) {
        		elementValue = element_object[0].text;
        	} else if (typeof element_object[0].textContent != "undefined" ) {
        		elementValue = element_object[0].textContent;
        	} else if (typeof(element_object[0].innerText) != "undefined" ) {
        		elementValue =  element_object[0].innerText;
        	}
        }
        
        return elementValue;
    };
    

	
    /**
     * Run through all the field validation tests when the
     * user submits the user registration request. If any of
     * them fail return the failure as a false return value.
     */
    var validateLoginFormFieldData = function(validation_strength_value) {
   	 	logger.debug("!!!!!!!!!!!! Personalised request. validateFormFieldData() " );
   	 
    	var rtvl = true;
    	var test_result = true;

		test_result = validateElementData(validation_strength_value, PWD_SIGN_IN_ELEMENT_ID);
		rtvl = setValidationRegisterVal(test_result, rtvl);
		test_result = validateElementData(STRONG_STRENGTH, USER_NAME_SIGN_IN_ELEMENT_ID);
		rtvl = setValidationRegisterVal(test_result, rtvl);

		return rtvl;
    	
    };
    
    /**
     * Run through all the field validation tests when the
     * user submits the user details edit request. If any of
     * them fail return the failure as a false return value.
     */
    var validateEditDetailsFormFieldData = function(validation_strength_value) {
   	 	logger.debug("!!!!!!!!!!!! User registration request. validateFormFieldData() " );
   	 
    	var rtvl = true;
    	var test_result = true;
		
		test_result = validateElementData(validation_strength_value, FIRSTNAME_ELEMENT_ID);
		rtvl = setValidationRegisterVal(test_result, rtvl);
		test_result = validateElementData(validation_strength_value, SURNAME_ELEMENT_ID);
		rtvl = setValidationRegisterVal(test_result, rtvl);
		test_result = validateElementData(validation_strength_value, ORG_NAME_ELEMENT_ID);
		rtvl = setValidationRegisterVal(test_result, rtvl);
		test_result = validateElementData(validation_strength_value, WRK_TELEPHONE_ELEMENT_ID);
		rtvl = setValidationRegisterVal(test_result, rtvl);
		test_result = validateElementData(validation_strength_value, PWD_ELEMENT_ID);
		rtvl = setValidationRegisterVal(test_result, rtvl);
		
		return rtvl;
    };
    
    /**
     * Used to keep track of a fail occurrence.
     */
    var setValidationRegisterVal = function(test_result, return_result) {
		if (!test_result) {
			return_result = false;
		}
		return return_result;
    };

    /**
     * Takes a regular expression and tests an elements value with it.
     * If the regEx match fails then the error message, passed as the third argument,
     * is output to the UI.
     */
    var validationTestStrong = function(element_Id, regEx, element_label, errorMsg) {
		logger.debug("!!!!!!!!!!!! User registration request. validationTestStrong()");
    	var rtvl = false;
    	
		var msg_Element_Id = element_Id + ELEMENT_ERR_MSG_ID_END;
		logger.debug("!!!!!!!!!!!! User registration request. validationTestStrong().msg_Element_Id = " + msg_Element_Id );
    	var elementVal = getElementValueById(element_Id);
    	var msg_Element = getElementById(msg_Element_Id);
		logger.debug("!!!!!!!!!!!! User registration request. validationTestStrong().getElementById(msg_Element_Id).msg_Element.value = " + msg_Element[0].innerText);
    	
    	if(elementVal.match(regEx)){
    		errorMsg = element_label;
    		validationShowErrorMsg(errorMsg, '#000000', msg_Element);
    		rtvl =  true;
    	} else {
        	errorMsg = element_label + ' ' + errorMsg;
    		validationShowErrorMsg(errorMsg, '#FF0000', msg_Element);
    		rtvl =  false;
        }
    	
    	// needed for IE to ensure text is displayed
    	var element = getElementById(element_Id);
    	element[0].focus();
    	
    	return rtvl;
    	
    };
    
    /**
     * Takes an integer and tests an elements value with it.
     * If the greater than integer fails then the error message, passed as the third argument,
     * is output to the UI.
     */
    var validationTestGreaterThan = function(element_Id, elementVal_length, element_label, errorMsg) {
		logger.debug("!!!!!!!!!!!! User registration request. validationTestGreaterThan()");
    	var rtvl = false;
    	
		var msg_Element_Id = element_Id + ELEMENT_ERR_MSG_ID_END;
		logger.debug("!!!!!!!!!!!! User registration request. validationTestGreaterThan().msg_Element_Id = " + msg_Element_Id );
    	var elementVal = getElementValueById(element_Id);
    	var msg_Element = getElementById(msg_Element_Id);

    	if(elementVal.length < elementVal_length){
        	errorMsg = element_label + ' ' + errorMsg;
    		validationShowErrorMsg(errorMsg, '#FF0000', msg_Element);
    		rtvl = false;
    	} else {
    		errorMsg = element_label;
    		validationShowErrorMsg(errorMsg, '#000000', msg_Element);
    		rtvl = true;
        }

    	// needed for IE to ensure text is displayed
    	var element = getElementById(element_Id);
    	element[0].focus();
    	
    	return rtvl;
    	
    };

    /**
     * Hide the UI an error message element.
     */
    var validationHideErrorMsg = function(element_object) {
        setElementColorForElement('#FFFFFF', element_object);
		setElementTextForElement('', element_object);
		setElementVisibilityForElement('hidden', element_object);
    };
    
    /**
     * Show the UI error message element and display the message.
     */
    var validationShowErrorMsg = function(error_msg, text_color, element_object) {
		setElementDisplayForElement('inline', element_object);
        setElementColorForElement(text_color, element_object);
		setElementTextForElement(error_msg, element_object);
		setElementVisibilityForElement('visible', element_object);
    };
    
    /**
     * Reset the labels to their default view.
     */
    var resetLoginLablesToDefault = function() {
    	resetLabelToDefault(PWD_SIGN_IN_ELEMENT_ID, PWD_ELEMENT_LBL);
    	resetLabelToDefault(USER_NAME_SIGN_IN_ELEMENT_ID, USER_NAME_ELEMENT_LBL);
    };
    
	var resetLabelToDefault = function(element_Id, element_Lbl) {
		msg_Element_Id = element_Id + ELEMENT_ERR_MSG_ID_END;
		logger.debug("!!!!!!!!!!!! User registration request. resetLablesToDefault().msg_Element_Id = " + msg_Element_Id );
		msg_Element = getElementById(msg_Element_Id);
		validationShowErrorMsg(element_Lbl, '#000000', msg_Element);
		var element = getElementById(element_Id);
		element[0].focus();
	};
    
    
    /**
     * Test the password against a number of validation criteria.
     */
    var validationPasswordTest = function() {
    	var rtvl = true;
    	rtvl = passwordStrength();
    	return rtvl;
    };
    
     /**
      * Test the passwords strength.
      * That its length is greater than 6 characters long.
      * That it is not equal to the username.
      * That it contains a lower case letter.
      * That it contains an upper case letter.
      * That it contains a digit (number).
      */
     var passwordStrength = function(element_Id) {
    	var errorMsg = '';
 		var msg_Element_Id = element_Id + + ELEMENT_ERR_MSG_ID_END;
     	var pwd_elementVal = getElementValueById(element_Id);
     	var pwd_elementVal2 = getElementValueById(PWD_CONFIRM_ELEMENT_ID);
     	var usr_name_elementVal = getElementValueById(USER_NAME_ELEMENT_ID);
     	var msg_Element = getElementById(msg_Element_Id);
     	var pwd_element = getElementById(element_Id);
   	 
       if(pwd_elementVal != "" && pwd_elementVal == pwd_elementVal2) {
         if(pwd_elementVal.length < 6) {
        	 errorMsg = "Error: Password must contain at least six characters!";
        	validationShowErrorMsg(errorMsg, msg_Element);
           return false;
         }
         if(pwd_elementVal == usr_name_elementVal) {
        	 errorMsg = "Error: Password must be different from Username!";
         	validationShowErrorMsg(errorMsg, msg_Element);
           return false;
         }
         regEx = /[0-9]/;
         if(!regEx.test(pwd_elementVal)) {
        	 errorMsg = "Error: password must contain at least one number (0-9)!";
          	validationShowErrorMsg(errorMsg, msg_Element);
           return false;
         }
         regEx = /[a-z]/;
         if(!regEx.test(pwd_elementVal)) {
        	 errorMsg = "Error: password must contain at least one lowercase letter (a-z)!";
           	validationShowErrorMsg(errorMsg, msg_Element);
           return false;
         }
         regEx = /[A-Z]/;
         if(!regEx.test(pwd_elementVal)) {
        	 errorMsg = "Error: password must contain at least one uppercase letter (A-Z)!";
             validationShowErrorMsg(errorMsg, msg_Element);
           return false;
         }
       } else {
      	 errorMsg = "Error: Please check that you've entered and confirmed your password!";
         validationShowErrorMsg(errorMsg, msg_Element);
         return false;
       }
       
       validationHideErrorMsg(msg_Element);
       return true;
     };
     
     /**
      * Based on the element id passed carry out relevant tests.
      */
     var validateElementData = function(validation_strength_value, element_id_value) {

    	 logger.debug("!!!!!!!!!!!! User registration request. validateElementData() validation_strength_value = " + validation_strength_value);
    	 var rtvl = false;
     	
    	 if(validation_strength_value ==  STRONG_STRENGTH) {

        	 logger.debug("!!!!!!!!!!!! User registration request. validateElementData() STRONG_STRENGTH = " + STRONG_STRENGTH );
        	 
    		 if (element_id_value == EMAIL_ELEMENT_ID) {
    			 rtvl = validationTestGreaterThan(EMAIL_ELEMENT_ID, 1, EMAIL_ELEMENT_LBL, EMAIL_ELEMENT_ERR_MSG);
    			 if (rtvl) {
    				 rtvl = validationTestStrong(EMAIL_ELEMENT_ID, EMAIL_ELEMENT_REGEX, EMAIL_ELEMENT_LBL, EMAIL_ELEMENT_ERR_MSG);
    			 }
    		 } else if (element_id_value == FIRSTNAME_ELEMENT_ID) {
    			 rtvl = validationTestStrong(FIRSTNAME_ELEMENT_ID, FIRSTNAME_ELEMENT_REGEX, FIRSTNAME_ELEMENT_LBL, FIRSTNAME_ELEMENT_ERR_MSG);
    		 } else if (element_id_value == SURNAME_ELEMENT_ID) {
    			 rtvl = validationTestStrong(SURNAME_ELEMENT_ID, SURNAME_ELEMENT_REGEX, SURNAME_ELEMENT_LBL, SURNAME_ELEMENT_ERR_MSG);
    		 } else if (element_id_value == ORG_NAME_ELEMENT_ID) {
    			 rtvl = validationTestStrong(ORG_NAME_ELEMENT_ID, ORG_NAME_ELEMENT_REGEX, ORG_NAME_ELEMENT_LBL, ORG_NAME_ELEMENT_ERR_MSG);
    		 }else if (element_id_value == USER_NAME_SIGN_IN_ELEMENT_ID) {
        		 rtvl = validationTestGreaterThan(USER_NAME_SIGN_IN_ELEMENT_ID, 1, USER_NAME_ELEMENT_LBL, USER_NAME_ELEMENT_ERR_MSG);
				 if (rtvl) {
					 rtvl = validationTestStrong(USER_NAME_SIGN_IN_ELEMENT_ID, USER_NAME_ELEMENT_REGEX, USER_NAME_ELEMENT_LBL, USER_NAME_ELEMENT_ERR_MSG2);
				 }
    		 } else if (element_id_value == WRK_TELEPHONE_ELEMENT_ID) {
    			 rtvl = validationTestStrong(WRK_TELEPHONE_ELEMENT_ID, WRK_TELEPHONE_ELEMENT_REGEX, WRK_TELEPHONE_ELEMENT_LBL, WRK_TELEPHONE_ELEMENT_ERR_MSG);
    		 } else if (element_id_value == ORG_COUNTRY_ELEMENT_ID) {
    			 rtvl = validationTestStrong(ORG_COUNTRY_ELEMENT_ID, ORG_COUNTRY_ELEMENT_REGEX, ORG_COUNTRY_ELEMENT_LBL, ORG_COUNTRY_ELEMENT_ERR_MSG);
    		 } else if (element_id_value == PWD_ELEMENT_ID) {
		    	var elementVal = getElementValueById(element_id_value);
		    	var elementVal_length = 0;
			 // we want to allow the user not to put in a value for the password.
			 // therefore if the fail the greater than test above consider that okay
			 // they don't want to change their password.
		    	if (elementVal.length == elementVal_length) {
    			    rtvl = true;
    			 } else {
					 rtvl = validationPasswordTest(PWD_ELEMENT_ID);
    			 }
    		 } else if (element_id_value == PWD_SIGN_IN_ELEMENT_ID) {
    			 rtvl = validationTestGreaterThan(PWD_SIGN_IN_ELEMENT_ID, 1, PWD_ELEMENT_LBL, PASSWORD_ELEMENT_ERR_MSG);
    			 if (rtvl) {
    				 rtvl = validationPasswordTest(PWD_SIGN_IN_ELEMENT_ID);
    			 }
    		 } else {
        		 // If none of the tests above succeeded then fail by default.
        		 rtvl = false;
    		 }
    		 
    	 } else if (validation_strength_value ==  MEDIUM_STRENGTH) {
        	 logger.debug("!!!!!!!!!!!! User registration request. validateElementData() MEDIUM_STRENGTH = " + MEDIUM_STRENGTH );
    		 // there are currently no medium strength validation tests. Validation will therefore fail by default.
    		 rtvl = false;
    	 } else if (validation_strength_value ==  WEEK_STRENGTH) {
        	 logger.debug("!!!!!!!!!!!! User registration request. validateElementData() WEEK_STRENGTH = " + WEEK_STRENGTH );
    		 // there are currently no week strength validation tests. Validation will therefore fail by default.
    		 rtvl = false;
    	 } else if (validation_strength_value ==  ENOUGH_STRENGTH) {

        	 logger.debug("!!!!!!!!!!!! User registration request. validateElementData() ENOUGH_STRENGTH = " + ENOUGH_STRENGTH );
        	 
    		 if (element_id_value == EMAIL_ELEMENT_ID) {
            	 logger.debug("!!!!!!!!!!!! User registration request. validateElementData() element_id_value = " + element_id_value );
    			 rtvl = validationTestGreaterThan(EMAIL_ELEMENT_ID, 1, EMAIL_ELEMENT_LBL, EMAIL_ELEMENT_ERR_MSG);
    			 if (rtvl) {
    				 rtvl = validationTestGreaterThan(EMAIL_ELEMENT_ID, 2, EMAIL_ELEMENT_LBL, EMAIL_ELEMENT_ERR_MSG);
    			 }
    		 } else if (element_id_value == FIRSTNAME_ELEMENT_ID) {
            	 logger.debug("!!!!!!!!!!!! User registration request. validateElementData() element_id_value = " + element_id_value );
    			 rtvl = validationTestGreaterThan(FIRSTNAME_ELEMENT_ID, 1, FIRSTNAME_ELEMENT_LBL, FIRSTNAME_ELEMENT_ERR_MSG);
    		 } else if (element_id_value == SURNAME_ELEMENT_ID) {
    			 rtvl = validationTestGreaterThan(SURNAME_ELEMENT_ID, 1, SURNAME_ELEMENT_LBL, SURNAME_ELEMENT_ERR_MSG);
    		 } else if (element_id_value == ORG_NAME_ELEMENT_ID) {
    			 rtvl = validationTestGreaterThan(ORG_NAME_ELEMENT_ID, 1, ORG_NAME_ELEMENT_LBL, ORG_NAME_ELEMENT_ERR_MSG);
    		 } else if (element_id_value == USER_NAME_SIGN_IN_ELEMENT_ID) {
    			 rtvl = validationTestGreaterThan(USER_NAME_SIGN_IN_ELEMENT_ID, 1, USER_NAME_ELEMENT_LBL, USER_NAME_ELEMENT_ERR_MSG);
    			 if (rtvl) {
    				 rtvl = validationTestGreaterThan(USER_NAME_SIGN_IN_ELEMENT_ID, 2, USER_NAME_ELEMENT_LBL, USER_NAME_ELEMENT_ERR_MSG2);
    			 }
    		 } else if (element_id_value == WRK_TELEPHONE_ELEMENT_ID) {
    			 rtvl = validationTestGreaterThan(WRK_TELEPHONE_ELEMENT_ID, 1, WRK_TELEPHONE_ELEMENT_LBL, WRK_TELEPHONE_ELEMENT_ERR_MSG);
    		 } else if (element_id_value == ORG_COUNTRY_ELEMENT_ID) {
    			 rtvl = validationTestGreaterThan(ORG_COUNTRY_ELEMENT_ID, 1, ORG_COUNTRY_ELEMENT_LBL, ORG_COUNTRY_ELEMENT_ERR_MSG);
    		 } else if (element_id_value == PWD_ELEMENT_ID) {
 		    	var elementVal = getElementValueById(element_id_value);
		    	var elementVal_length = 0;
			 // we want to allow the user not to put in a value for the password.
			 // therefore if the fail the greater than test above consider that okay
			 // they don't want to change their password.
		    	if (elementVal.length == elementVal_length) {
    			    rtvl = true;
    			 } else {
        			rtvl = validationTestGreaterThan(PWD_ELEMENT_ID, 6, PWD_ELEMENT_LBL, PASSWORD_ELEMENT_ERR_MSG2);
    			 }
    		 } else if (element_id_value == PWD_SIGN_IN_ELEMENT_ID) {
    			 rtvl = validationTestGreaterThan(PWD_SIGN_IN_ELEMENT_ID, 1, PWD_ELEMENT_LBL, PASSWORD_ELEMENT_ERR_MSG);
    			 if (rtvl) {
    				 rtvl = validationTestGreaterThan(PWD_SIGN_IN_ELEMENT_ID, 6, PWD_ELEMENT_LBL, PASSWORD_ELEMENT_ERR_MSG2);
    			 } 
    		 } else {
        		 // If none of the tests above succeeded then fail by default.
        		 rtvl = false;
    		 }
    	 }
    	 
    	 return rtvl;
     };
     
     /**
      * Check if the value of the label has changed from the default.
      * If to then it is an error message sent back from server
      * make the test red.
      */
     var errorLabelCheck = function(element_Id, elementValCheck) {
    	 var msg_Element_Id = element_Id + ELEMENT_ERR_MSG_ID_END;
    	 var msg_Element = getElementById(msg_Element_Id);
    	 var error_elementVal = getElementTextForElement(msg_Element);
    	 if (error_elementVal != elementValCheck ) {
   	        setElementColorForElement('#FF0000', msg_Element);
    	 }
     };
     
     /**
      * value of hidden field to determine whether or not to execute the reload
      * method in subsequent page.
      */
     var setPersonalisedAccessChange = function(changeValue) {
    	 var element_Id = '#personalisedAccessChange';
      	 var object_element = getElementById(element_Id);
      	 setElementTextForElement(changeValue, object_element);
      	 
      	 //for FireFox to set the value of the hidden form field "personalisedAccessChange"
      	 if (object_element[0].value == 0) {
      		 object_element[0].value = changeValue;
      	 }
     };

     
	/**
	 * Public visible methods from this JS object.
	 */
	return {
		
		/**
		 * Register the user based on the form field inputs.
		 */
		login : function() {
			var rtvl = false;
			resetLoginLablesToDefault();
		
			STRUTS_PERSONALISED_ACTION = "personalisedSubmit.action";
			logger.debug("!!!!!!!!!!!! Personalised request. registerUser().STRUTS_PERSONALISED_ACTION = " + STRUTS_PERSONALISED_ACTION );
			
			var validation_result = true;
			validation_result = validateLoginFormFieldData(VALIDATION_STRENGTH);
			logger.debug("!!!!!!!!!!!! Personalised request. registerUser(). validation_result = " + validation_result );

			if (!validation_result) {
				logger.debug("!!!!!!!!!!!! Personalised request. registerUser(). submit login form failure!.");
				rtvl = false;
			} else {
				logger.debug("!!!!!!!!!!!! Personalised request. registerUser(). submit login form success!.");
		     	var registrationForm_element = getElementById(LOGIN_FORM);
		     	setPersonalisedAccessChange("1");
		     	registrationForm_element[0].submit();
		     	rtvl = true;
			}
			
			return rtvl;
		},
		
		/**
		 * Refresh the SignIn SignOut link and user name in top left parent frame.
		 */
		personalisedLogInOutRefresh : function() {
			var rtvl = false;
			
			//alert("personalisedLogInOutRefresh.in");
			
			STRUTS_PERSONALISED_ACTION = "/personalisedLogInOutRefresh.action";
			doPersonalisedLogInOutRefresh();
			
			return rtvl;
		},
		
		/**
		 * 
		 */
		forcePasswordChange : function() {
			var rtvl = false;
	    	resetLabelToDefault(PWD_SIGN_IN_ELEMENT_ID, PWD_ELEMENT_LBL);
			
			var validation_result = true;
			validation_result = validateElementData(VALIDATION_STRENGTH, PWD_SIGN_IN_ELEMENT_ID);
			
			if (!validation_result) {
				logger.debug("!!!!!!!!!!!! Personalised request. forcePasswordChange(). failure!.");
				rtvl = false;
			} else {
				logger.debug("!!!!!!!!!!!! Personalised request. forcePasswordChange(). success!.");
		     	var registrationForm_element = getElementById(FORCE_PASSWORD_CHANGE_FORM);
				setPersonalisedAccessChange("1");
	     		registrationForm_element[0].submit();
		     	rtvl = true;
			}

			return rtvl;
		},
		
		checkReloadStatus : function() {
			var rtvl = false;
			var theLocationToGo = "";
			
			var element_Id = '#personalisedAccessChange';
	      	 var object_element = getElementById(element_Id);
	      	 var object_element_val = getElementValueById(element_Id);
     		 
	      	 if (object_element_val == "1") {
	      		 // see newco.struts for rational re this action.
				 theLocationToGo = "/personalisedLogin.action";
				 theLocationToGo = "?personalisedAccessChange=" + "0";
		      	 setElementTextForElement("0", object_element);
		      	 window.top.location.reload(true);
		      	 location.href = theLocationToGo;
	      	 }
	      	 
	      	 if (object_element_val == "2") {
				 theLocationToGo = "/home.action";
				 theLocationToGo = "?personalisedAccessChange=" + "0";
		      	 setElementTextForElement("0", object_element);
		      	 window.top.location.reload(false);
		      	 location.href = theLocationToGo;
	      	 }
			
			return true;
		},
		
		gotoEditUserDetails : function() {
			logger.debug("!!!!!!!!!!!! Personalised request. editUserDetails().");
			var theLocationToGo = "";
			theLocationToGo = "/personalisedUserDetailsEdit.action";
			document.location.href = theLocationToGo;
			return true;
		},
		
		saveUserDetails : function() {
			var rtvl = false;
	    	resetLabelToDefault(PWD_ELEMENT_ID, PWD_ELEMENT_LBL);
	    	
			var validation_result = true;
			validation_result = validateEditDetailsFormFieldData(VALIDATION_STRENGTH);

			if (!validation_result) {
				rtvl = false;
			} else {
				var registrationForm_element = getElementById(USER_DETAILS_EDIT_FORM);
		     	registrationForm_element[0].submit();
		     	rtvl = true;
			}
			
			return rtvl;
		},
		
		newPassword : function(){
			logger.debug("!!!!!!!!!!!! Personalised request. newPassword().");
			var rtvl = false;
			resetLoginLablesToDefault();
			
			var validation_result = true;
			validation_result = validateElementData(STRONG_STRENGTH, USER_NAME_SIGN_IN_ELEMENT_ID);

			logger.debug("!!!!!!!!!!!! Personalised request. newPassword(). validation_result = " + validation_result );
			
			if (!validation_result) {
				logger.debug("!!!!!!!!!!!! Personalised request. newPassword(). !validation_result = " + !validation_result );
				rtvl = false;
			} else {
		    	resetLabelToDefault(PWD_SIGN_IN_ELEMENT_ID, PWD_ELEMENT_LBL);
		    	
				var theLocationToGo = "";
				theLocationToGo = "/userRegistrationNewPassword.action";
				theLocationToGo = theLocationToGo + "?emailAddress=";
				var emailAddress = "";
				emailAddress = getElementValueById(USER_NAME_SIGN_IN_ELEMENT_ID);
				theLocationToGo = theLocationToGo + emailAddress;
				logger.debug("!!!!!!!!!!!! Personalised request. newPassword(). theLocationToGo = " + theLocationToGo );
				document.location.href = theLocationToGo;
		     	rtvl = true;
			}

			return rtvl;
		},
		
		userRegistration : function(){
			logger.debug("!!!!!!!!!!!! Personalised request. userRegistration().");
			var rtvl = false;
			var validation_result = true;
			logger.debug("!!!!!!!!!!!! Personalised request. userRegistration(). validation_result = " + validation_result );

			if (!validation_result) {
				logger.debug("!!!!!!!!!!!! Personalised request. userRegistration(). !validation_result = " + !validation_result );
				rtvl = false;
			} else {
				var theLocationToGo = "";
				theLocationToGo = "/userRegistration.action";
				logger.debug("!!!!!!!!!!!! Personalised request. userRegistration(). theLocationToGo = " + theLocationToGo );
				document.location.href = theLocationToGo;
		     	rtvl = true;
			}

			return rtvl;
		},
		
		/**
		 * Validate the email form field value
		 */
		validatePassword  : function() {
			logger.debug("!!!!!!!!!!!! Personalised request. validatePassword() " );
			validateElementData(VALIDATION_STRENGTH, PWD_SIGN_IN_ELEMENT_ID);
		},
		
		validateLoginUid  : function() {
			logger.debug("!!!!!!!!!!!! Personalised request. validateCountry() " );
			validateElementData(STRONG_STRENGTH, USER_NAME_SIGN_IN_ELEMENT_ID);
		},
		
		checkUserNameErrorLabelServerSideMsgChange : function() {
			errorLabelCheck(USER_NAME_SIGN_IN_ELEMENT_ID, USER_NAME_ELEMENT_LBL);
		},
		
		checkPasswordErrorLabelServerSideMsgChange : function() {
			errorLabelCheck(PWD_SIGN_IN_ELEMENT_ID, PWD_ELEMENT_LBL);
		}
	};
	
}();

var Profiler = new function() {
    this.timer = {};
    this.times = {};
	this.pTimes = {};

	this.clear = function()
	{
		this.pTimes = {};
	};

	this.start = function(sClass, sMethod)
	{
		var sName = this.getFullName(sClass, sMethod);
		var oTimer = this.pTimes[sName];

		if(!oTimer)
		{
			this.pTimes[sName] = {"elapsed":0};
			oTimer = this.pTimes[sName];
		}

		oTimer.start = (new Date()).getTime();
	};

    this.start = function(tag) {
        this.timer.start = (new Date()).getTime();
        this.times[tag] = this.timer.start;
    };

    this.checkpoint = function(tag) {
        this.times[tag] = (new Date()).getTime() - this.timer.start;
    };

    this.report = function() {
        for(var tag in this.times)
		{
            logger.debug(tag + ' elapsed = ' + this.times[tag] + 'ms');    
        }
    };

    this.alertReport = function() {
        var report = "";
        for(var tag in this.times)
		{
            report += tag + ': ' + this.times[tag] + "ms <br />\n";
        }
        alert(report);
    }

    this.stop = function(sClass, sMethod)
	{
		var sName = this.getFullName(sClass, sMethod);
		var oTimer = this.pTimes[sName];
		var nElapsed = (new Date()).getTime() - oTimer.start;

		oTimer.elapsed += nElapsed;
		oTimer.start = null;
	};

	this.getElapsed = function(sClass, sMethod)
	{
		var sName = this.getFullName(sClass, sMethod);
		var oTimer = this.pTimes[sName];

		return oTimer.elapsed;
	};

    this.getFullName = function(sClass, sMethod)
	{
		return sClass + "." + sMethod + "()";
	};

	this.getReport = function()
	{
		var sReport = "";
		var pSortedTimes = [];
		var sName;
		var l;

		for(sName in this.pTimes)
		{
			var nElapsed = this.pTimes[sName].elapsed;

			pSortedTimes.push([nElapsed, sName]);
		}
		pSortedTimes.sort(this.sortTimes);

		for(var i = 0, l = pSortedTimes.length; i < l; ++i)
		{
			sName = pSortedTimes[i][1];
			nElapsed = this.pTimes[sName].elapsed;

			sReport += nElapsed + "ms " + sName + "<br />\n";
		}

		return sReport;
	};

	this.sortTimes = function(l_oItem1, l_oItem2)
	{
		return (l_oItem1[0] > l_oItem2[0]) ? -1 : 1;
	}

};

/**
 * @class QueryStringUtils
 * Form utilities class
 * @singleton
 */
var QueryStringUtils = function() {
    
    var PageQuery = function(q) {
        if (q.length > 1) this.q = q.substring(1, q.length);
        else this.q = null;
        this.keyValuePairs = new Array();
        if (q) {
            for (var i = 0; i < this.q.split("&").length; i++) {
                this.keyValuePairs[i] = this.q.split("&")[i];
            }
        }
        this.getKeyValuePairs = function() {
            return this.keyValuePairs;
        }
        this.getValue = function(s) {
            for (var j = 0; j < this.keyValuePairs.length; j++) {
                if (this.keyValuePairs[j].split("=")[0] == s) return this.keyValuePairs[j].split("=")[1];
            }
            return false;
        }
        this.getParameters = function()  {
            var a = new Array(this.getLength());
            for (var j = 0; j < this.keyValuePairs.length; j++) {
                a[j] = this.keyValuePairs[j].split("=")[0];
            }
            return a;
        }
        this.getLength = function() {
            return this.keyValuePairs.length;
        }
    };

    return {
        get : function(key) {
            var page = new PageQuery(window.location.search);
            return unescape(page.getValue(key));
        },

        getFromLocation : function(key, location) {
            var result = null;
            if (location.search) {
                var page = new PageQuery(location.search);
                result = unescape(page.getValue(key));
            }
            return result;
        }
    }
}();


/**
 * @class UserRegistration
 * Controller for the user registration mechanism
 * @singleton
 */
var UserRegistration = function() {
	
	// the Struts Action name that will be passed by JQuerys Ajax
	// capability to the Struts framework.
	var STRUTS_USER_REGISTRATION_ACTION;
	
	var REGISTRATION_FORM = '#registrationForm';
	var HIDDEN_REGISTRATION_FORM = '#registrationFormHidden';
	
	ELEMENT_ERR_MSG_ID_END_MSG = 'Msg';
	ELEMENT_ERR_MSG_ID_END_LBL = 'Lbl';
	
	var ELEMENT_ERR_MSG_ID_END = ELEMENT_ERR_MSG_ID_END_LBL;

	// the JSP form html element id's for the form input elements
	var EMAIL_ELEMENT_ID = '#emailAddress';
	var USER_NAME_ELEMENT_ID = '#username';
	var PWD_ELEMENT_ID = '#password';
	var PWD_CONFIRM_ELEMENT_ID = '#confirmPassword';
	var FIRSTNAME_ELEMENT_ID = '#firstname';
	var SURNAME_ELEMENT_ID = '#surname';
	var WRK_TELEPHONE_ELEMENT_ID = '#workTelephone';
	var WRK_MOBILE_ELEMENT_ID = '#workMobile';
	var ORG_NAME_ELEMENT_ID = '#organisationName';
	var ORG_ADRS_ELEMENT_ID = '#organisationAddress1';
	var ORG_ADRS_2_ELEMENT_ID = '#organisationAddress2';
	var ORG_ZIP_ELEMENT_ID = '#organisationZip';
	var ORG_CITY_ELEMENT_ID = '#organisationCity';
	var ORG_STATE_ELEMENT_ID = '#organisationState';
	var ORG_COUNTRY_ELEMENT_ID = '#countriesListIndexValue';
	var PRIVACY_POLICY_CHECKBOX = '#privacyPolicy';

	var COUNTRIES_LIST = '#countriesListId';
	var LANGUAGE_LIST = '#publicLanguagesListId';
	var COUNTRIES_LIST_INDEX_VALUE = '#countriesListIndexValue';
	var LANGUAGE_LIST_INDEX_VALUE = '#publicLanguagesListIndexValue';
	
	// hidden input
	var HIDDEN_PRIVACY_POLICY_ACCEPTED = '#privacyPolicyAccepted';
	
	// the JSP form html element lables for the form input elements
	var EMAIL_ELEMENT_LBL = 'Email address:';
	var USER_NAME_ELEMENT_LBL = 'User name:';
	var PWD_ELEMENT_LBL = 'Password (min 6):';
	var PWD_CONFIRM_ELEMENT_LBL = 'Confirm password:';
	var FIRSTNAME_ELEMENT_LBL = 'First name:';
	var SURNAME_ELEMENT_LBL = 'Last name:';
	var WRK_TELEPHONE_ELEMENT_LBL = 'Office telephone:';
	var WRK_MOBILE_ELEMENT_LBL = 'Mobile telephone:';
	var ORG_NAME_ELEMENT_LBL = 'Organisation:';
	var ORG_ADRS_ELEMENT_LBL = 'Address 1:';
	var ORG_ADRS_2_ELEMENT_LBL = 'Address 2:';
	var ORG_ZIP_ELEMENT_LBL = 'Postcode:';
	var ORG_CITY_ELEMENT_LBL = 'City:';
	var ORG_STATE_ELEMENT_LBL = 'State:';
	var ORG_COUNTRY_ELEMENT_LBL = 'Country:';
	var PRIVACY_POLICY_CHECKBOX_LBL = 'Privacy policy:';
	var PREFERED_LANGUAGE_LBL = 'Preferred language:';

	var STRONG_STRENGTH = 1;  // The function to check passwords requires a confirm password as well as a password field. It also requires a user name field.
	var MEDIUM_STRENGTH = 2;  // There is no functionality for this validation strength test.
	var WEEK_STRENGTH = 3;    // There is no functionality for this validation strength test.
	var ENOUGH_STRENGTH = 4;  // 
	var VALIDATION_STRENGTH = ENOUGH_STRENGTH;
	
	var GENERIC_ERR_MSG = 'Required field ';
	
	var LOGIN_UID_ERR_MSG = GENERIC_ERR_MSG + ' user id into the Sign In username field. The user id should be in a emal address format.';
	var LOGIN_NEW_PWD_REQUEST_ERR = LOGIN_UID_ERR_MSG + ' The new password will be sent to that address.';
	
	var EMAIL_CONSTRUCT_MSG = 'This must be an email address.';
	
	var EMAIL_ELEMENT_REGEX = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
	var EMAIL_ELEMENT_ERR_MSG = GENERIC_ERR_MSG;
	var EMAIL_ELEMENT_ERR_MSG2 = 'Address not recognised';
	
	var FIRSTNAME_ELEMENT_REGEX = /^([a-zA-Z])+$/;
	var FIRSTNAME_ELEMENT_ERR_MSG = GENERIC_ERR_MSG;
	
	var SURNAME_ELEMENT_REGEX = FIRSTNAME_ELEMENT_REGEX;
	var SURNAME_ELEMENT_ERR_MSG = GENERIC_ERR_MSG;
	
	var ORG_NAME_ELEMENT_REGEX = /^([a-zA-Z0-9])+$/;
	var ORG_NAME_ELEMENT_ERR_MSG = GENERIC_ERR_MSG;
	
	var USER_NAME_ELEMENT_REGEX = EMAIL_ELEMENT_REGEX;
	var USER_NAME_ELEMENT_ERR_MSG = GENERIC_ERR_MSG;
	
	var PASSWORD_ELEMENT_ERR_MSG = GENERIC_ERR_MSG;
	var PASSWORD_ELEMENT_ERR_MSG2 =  'Min of 6 characters';
	
	var WRK_TELEPHONE_ELEMENT_REGEX = /^([a-zA-Z\\s])+$/;
	var WRK_TELEPHONE_ELEMENT_ERR_MSG = GENERIC_ERR_MSG;
	
	var ORG_COUNTRY_ELEMENT_REGEX = /^([0-9\\s])+$/;
	var ORG_COUNTRY_ELEMENT_ERR_MSG = GENERIC_ERR_MSG;
	
	var PRIVACY_POLICY_READ_MSG = PRIVACY_POLICY_CHECKBOX_LBL + " Please read Privacy Policy before submitting registration request.";
	var PRIVACY_POLICY_CHECK_MSG = PRIVACY_POLICY_CHECKBOX_LBL + " Please confirm that you have read and accepted the Privacy Policy.";
	
	// output html element id's
    var DV_USER_REG_CONTAINER = '#dvUsrRegContainer';
    var DV_USER_REG_OUTPUT_RESULTS = '#dvUsrRegOutputResults';
    var DV_USER_REG_OUTPUT = '#dvUsrRegOutput';
    var searchBoxHeight = 43;
	
//	var ajaxCall = function(successDelegate, emptyDelegate, failureDelegate) {
//		logger.debug("!!!!!!!!!!!! User registration request. ajaxCall() " );
//		
//		var email = getElementValueById(EMAIL_ELEMENT_ID);
//		var username = getElementValueById(USER_NAME_ELEMENT_ID);
//		var password = getElementValueById(PWD_ELEMENT_ID);
//		var confirmPassword = getElementValueById(PWD_CONFIRM_ELEMENT_ID);
//		var firstname = getElementValueById(FIRSTNAME_ELEMENT_ID);
//		var surname = getElementValueById(SURNAME_ELEMENT_ID);
//		var workTelephone = getElementValueById(WRK_TELEPHONE_ELEMENT_ID);
//		var workMobile = getElementValueById(WRK_MOBILE_ELEMENT_ID);
//		var organisationName = getElementValueById(ORG_NAME_ELEMENT_ID);
//		var organisationAddress1 = getElementValueById(ORG_ADRS_ELEMENT_ID);
//		var organisationAddress2 = getElementValueById(ORG_ADRS_2_ELEMENT_ID);
//		var organisationCity = getElementValueById(ORG_CITY_ELEMENT_ID);
//		var organisationZip = getElementValueById(ORG_ZIP_ELEMENT_ID);
//		var state = getElementValueById(ORG_STATE_ELEMENT_ID);
//		var organisationCountry = getElementValueById(ORG_COUNTRY_ELEMENT_ID);
//
//		logger.debug("!!!!!!!!!!!! User registration request ajaxCall() email = " + email + ", username = " + username + ", password = " + password);
//		// data is a name value pairs 
//		// the names correspond to the Java Struts Action
//		// classes JavaBean attribute names.
//		var data = {
//			email : email,
//			username : username,
//		    password : password,
//		    confirmPassword : confirmPassword,
//		    surname : surname,
//		    firstname : firstname,
//			workTelephone : workTelephone,
//			workMobile : workMobile,
//		    organisationName : organisationName,
//		    organisationAddress1 : organisationAddress1,
//		    organisationAddress2 : organisationAddress2,
//		    organisationCity : organisationCity,
//		    organisationCountry : organisationCountry,
//		    state : state,
//		    organisationZip : organisationZip
//		}
//		
//		//post : function(url, data, onSuccess, onEmpty, onFailure, type)
//		Ajax.post(STRUTS_USER_REGISTRATION_ACTION, data, successDelegate, emptyDelegate, failureDelegate, "json");
//		STRUTS_USER_REGISTRATION_ACTION = "";
//	};
	
	/**
	 * Return the element by the ID supplied.
	 */
	var getElementById = function(element_Id){
        return $(element_Id);
	};
	
	/**
	 * Return the innterText value for the element ID passed.
	 */
	var getElementValueById = function(element_Id){
    	debugger;
        var elementValue = "";
        var elt = getElementById(element_Id);
        if (elt.length > 0) {
            // The email value from the form
            var elementValue = elt[0].value;
        }
        
        return elementValue;
	};
    
    /**
     * Toggle the visibility of the page xhtml element.
     * Takes two arguments; the visibility state to set and an element id.
     * visibility_state: 'visible', 'hidden'
     * element_Id: '#<your-xhtml-element-id>'
     * 
     * @author yearwaker
     */
    var setElementVisibilityById = function(visibility_state, element_Id) {
        var element_object = getElementById(element_Id);
        if (element_object) {
        	element_object[0].style.visibility = visibility_state;
        }
    };
    
    var setElementVisibilityForElement = function(visibility_state, element_object) {
        if (element_object) {
        	element_object[0].style.visibility = visibility_state;
        }
    };

    var setElementDisplayForElement = function(display_state, element_object) {
        if (element_object) {
        	element_object[0].style.display = display_state;
        }
    };
    
    var setElementColorForElement = function(color_state, element_object) {
        if (element_object) {
        	element_object[0].style.color = color_state;
        }
    };
    
    var setElementTextForElement = function(text_state, element_object) {
        if (element_object) {
        	if (typeof(element_object[0].text) != "undefined" ) {
        		element_object[0].text = text_state;
        	}
        	else if (typeof(element_object[0].textContent) != "undefined" ) {
        		element_object[0].textContent = text_state;
        	}
        	else if (typeof(element_object[0].innerText) != "undefined" ) {
        		element_object[0].innerText = text_state;
        	}
        }
    };
    
    var getElementTextForElement = function(element_object) {
        if (element_object) {
        	if (typeof(element_object[0].text) != "undefined" ) {
        		elementValue = element_object[0].text;
        	}
        	else if (typeof element_object[0].textContent != "undefined" ) {
        		elementValue = element_object[0].textContent;
        	}
        	else if (typeof(element_object[0].innerText) != "undefined" ) {
        		elementValue =  element_object[0].innerText;
        	}
        }
        
        return elementValue;
    };
    
	var successDelegateRegisterUser = function() {
	};
	
	
	var emptyDelegateRegisterUser = function() {
	};
	
	var failureDelegateRegisterUser = function() {
	};
	
	var resetDomainErrorMsg = function() {
 		msg_Element_Id = '#domainError' + ELEMENT_ERR_MSG_ID_END_MSG;
     	msg_Element = getElementById(msg_Element_Id);
     	validationShowErrorMsg('', '#000000', msg_Element);
	};
    
    /**
     * Run through all the field validation tests when the
     * user submits the user registration request. If any of
     * them fail return the failure as a false return value.
     */
    var validateFormFieldData = function(validation_strength_value) {
   	 	logger.debug("!!!!!!!!!!!! User registration request. validateFormFieldData() " );
    	var rtvl = true;
    	var test_result = true;
    	resetDomainErrorMsg();
    	test_result = validateElementData(validation_strength_value, EMAIL_ELEMENT_ID);
    	rtvl = setValidationRegisterVal(test_result, rtvl);
		test_result = validateElementData(validation_strength_value, FIRSTNAME_ELEMENT_ID);
		rtvl = setValidationRegisterVal(test_result, rtvl);
		test_result = validateElementData(validation_strength_value, SURNAME_ELEMENT_ID);
		rtvl = setValidationRegisterVal(test_result, rtvl);
		test_result = validateElementData(validation_strength_value, ORG_NAME_ELEMENT_ID);
		rtvl = setValidationRegisterVal(test_result, rtvl);
		test_result = validateElementData(validation_strength_value, WRK_TELEPHONE_ELEMENT_ID);
		rtvl = setValidationRegisterVal(test_result, rtvl);
		test_result = validateElementData(validation_strength_value, COUNTRIES_LIST);
		rtvl = setValidationRegisterVal(test_result, rtvl);
		test_result = validateElementData(validation_strength_value, PWD_ELEMENT_ID);
		rtvl = setValidationRegisterVal(test_result, rtvl);
		test_result = privacyPolicyAcceptanceCheck();
		rtvl = setValidationRegisterVal(test_result, rtvl);
		
		// test the privacy policy checkbox is checked.
		if (test_result ==  true) {
			//validate email address field to conform to email regEx
			test_result = validatePrivacyPolicy(PRIVACY_POLICY_CHECKBOX);
			rtvl = setValidationRegisterVal(test_result, rtvl);
			
			if (test_result ==  false) {
				//display message
				var msg_Element_Id = PRIVACY_POLICY_CHECKBOX + ELEMENT_ERR_MSG_ID_END_LBL;
		     	var msg_Element = getElementById(msg_Element_Id);
				validationShowErrorMsg( PRIVACY_POLICY_CHECK_MSG, '#FF0000', msg_Element);
				
				var element = getElementById(PRIVACY_POLICY_CHECKBOX);
		  		 //change the disabled value of the check box to false, i.e. it is now active.
		     	element[0].disabled=false;
		     	element[0].focus();
		  		 //change the disabled value of the check box to false, i.e. it is now active.
			} else if (test_result ==  true) {
				resetLblToDefault(PRIVACY_POLICY_CHECKBOX, '#000000', PRIVACY_POLICY_CHECKBOX_LBL);
			}
		} else if (test_result ==  false) {
			//display message
			var msg_Element_Id = PRIVACY_POLICY_CHECKBOX + ELEMENT_ERR_MSG_ID_END_LBL;
			var msg_Element = getElementById(msg_Element_Id);
	     	validationShowErrorMsg( PRIVACY_POLICY_READ_MSG, '#FF0000', msg_Element);
	     	
	     	var element = getElementById(PRIVACY_POLICY_CHECKBOX);
	  		 //change the disabled value of the check box to false, i.e. it is now active.
	     	element[0].disabled=false;
	     	element[0].focus();
	  		 //change the disabled value of the check box to false, i.e. it is now active.
		}
		
		 element = getElementById(LANGUAGE_LIST);
	     element[0].focus();
	     
		 element = getElementById(FIRSTNAME_ELEMENT_ID);
	     element[0].focus();
	     
		return rtvl;
    };
    
    /**
     * Used to keep track of a fail occurrence.
     */
    var setValidationRegisterVal = function(test_result, return_result) {
		if (!test_result) {
			return_result = false;
		}
		
		return return_result;
    };
    
    /**
     * Takes a regular expression and tests an elements value with it.
     * If the regEx match fails then the error message, passed as the third argument,
     * is output to the UI.
     */
    var validationTestStrong = function(element_Id, regEx, element_label, errorMsg) {
		logger.debug("!!!!!!!!!!!! User registration request. validationTestStrong()");
    	var rtvl = false;
    	
		var msg_Element_Id = element_Id + ELEMENT_ERR_MSG_ID_END;
		logger.debug("!!!!!!!!!!!! User registration request. validationTestStrong().msg_Element_Id = " + msg_Element_Id );
    	var elementVal = getElementValueById(element_Id);
    	var msg_Element = getElementById(msg_Element_Id);
		logger.debug("!!!!!!!!!!!! User registration request. validationTestStrong().getElementById(msg_Element_Id).msg_Element.value = " + msg_Element[0].innerText);
    	
    	if(elementVal.match(regEx)){
    		errorMsg = element_label;
    		validationShowErrorMsg(errorMsg, '#000000', msg_Element);
    		rtvl =  true;
    	} else {
        	errorMsg = element_label + ' ' + errorMsg;
    		validationShowErrorMsg(errorMsg, '#FF0000', msg_Element);
    		rtvl =  false;
        }
    	// needed for IE to ensure text is displayed
    	var element = getElementById(element_Id);
    	element[0].focus();
    	
    	return rtvl;
    };
    
    /**
     * Takes an integer and tests an elements value with it.
     * If the greater than integer fails then the error message, passed as the third argument,
     * is output to the UI.
     */
    var validationTestGreaterThan = function(element_Id, elementVal_length, element_label, errorMsg) {
    	var rtvl = false;
    	
		var msg_Element_Id = element_Id + ELEMENT_ERR_MSG_ID_END;
    	var elementVal = getElementValueById(element_Id);
    	var msg_Element = getElementById(msg_Element_Id);

    	if(elementVal.length < elementVal_length){
        	errorMsg = element_label + ' ' + errorMsg;
    		validationShowErrorMsg(errorMsg, '#FF0000', msg_Element);
    		rtvl = false;
    	} else {
    		errorMsg = element_label;
    		validationShowErrorMsg(errorMsg, '#000000', msg_Element);
    		rtvl = true;
        }
    	// needed for IE to ensure text is displayed
    	var element = getElementById(element_Id);
    	element[0].focus();
    	
    	return rtvl;
    };

    /**
     * Hide the UI an error message element.
     */
    var validationHideErrorMsg = function(element_object) {
        setElementColorForElement('#FFFFFF', element_object);
		setElementTextForElement('', element_object);
		setElementVisibilityForElement('hidden', element_object);
    };
    
    /**
     * Show the UI error message element and display the message.
     */
    var validationShowErrorMsg = function(error_msg, text_color, element_object) {
		setElementDisplayForElement('inline', element_object);
        setElementColorForElement(text_color, element_object);
		setElementTextForElement(error_msg, element_object);
		setElementVisibilityForElement('visible', element_object);
    };
    
    /**
     * Test the password against a number of validation criteria.
     */
    var validationPasswordTest = function() {
    	var rtvl = true;
    	// passwordStregth test
    	rtvl = passwordStrength();
    	// password and confirm password equality test
    	return rtvl;
    };

     /**
      * Test the passwords strength.
      * That its length is greater than 6 characters long.
      * That it is not equal to the username.
      * That it contains a lower case letter.
      * That it contains an upper case letter.
      * That it contains a digit (number).
      */
     var passwordStrength = function() {
    	 
    	var errorMsg = '';
    	element_Id = PWD_ELEMENT_ID;

 		var msg_Element_Id = element_Id + ELEMENT_ERR_MSG_ID_END;
     	var pwd_elementVal = getElementValueById(element_Id);
     	var pwd_elementVal2 = getElementValueById(PWD_CONFIRM_ELEMENT_ID);
     	var usr_name_elementVal = getElementValueById(USER_NAME_ELEMENT_ID);
     	var msg_Element = getElementById(msg_Element_Id);
     	var pwd_element = getElementById(element_Id);
   	 
       if(pwd_elementVal != "" && pwd_elementVal == pwd_elementVal2) {
    	   
         if(pwd_elementVal.length < 6) {
        	 errorMsg = "Error: Password must contain at least six characters!";
        	validationShowErrorMsg(errorMsg,'#FF0000', msg_Element);
           return false;
         }
         if(pwd_elementVal == usr_name_elementVal) {
        	 errorMsg = "Error: Password must be different from Username!";
         	validationShowErrorMsg(errorMsg,'#FF0000', msg_Element);
           return false;
         }
         regEx = /[0-9]/;
         if(!regEx.test(pwd_elementVal)) {
        	 errorMsg = "Error: password must contain at least one number (0-9)!";
          	validationShowErrorMsg(errorMsg,'#FF0000', msg_Element);
           return false;
         }
         regEx = /[a-z]/;
         if(!regEx.test(pwd_elementVal)) {
        	 errorMsg = "Error: password must contain at least one lowercase letter (a-z)!";
           	validationShowErrorMsg(errorMsg,'#FF0000', msg_Element);
           return false;
         }
         regEx = /[A-Z]/;
         if(!regEx.test(pwd_elementVal)) {
        	 errorMsg = "Error: password must contain at least one uppercase letter (A-Z)!";
             validationShowErrorMsg(errorMsg,'#FF0000', msg_Element);
           return false;
         }
       } else {
      	 errorMsg = "Error: Please check that you've entered and confirmed your password!";
         validationShowErrorMsg(errorMsg,'#FF0000', msg_Element);
         return false;
       }
       
       validationHideErrorMsg(msg_Element);
       return true;
     };
     
     /**
      * Check there is an email address in the login uid field.
      */
     var validateLoginElementData =  function(validation_strength_value) {
    	 logger.debug("!!!!!!!!!!!! User registration request. validateLoginElementData() validation_strength_value = " + validation_strength_value);
    	 var rtvl = false;
		 rtvl = validationTestStrong(LOGIN_UID_ELEMENT_ID, EMAIL_ELEMENT_REGEX, LOGIN_NEW_PWD_REQUEST_ERR);
    	 return rtvl;
     };
     
     /**
      * Based on the element id passed carry out relevant tests.
      */
     var validateElementData = function(validation_strength_value, element_id_value) {
    	 var rtvl = false;
     	
    	 if(validation_strength_value ==  STRONG_STRENGTH) {
    		 if (element_id_value == EMAIL_ELEMENT_ID) {
    			 rtvl = validationTestGreaterThan(EMAIL_ELEMENT_ID, 1, EMAIL_ELEMENT_LBL, EMAIL_ELEMENT_ERR_MSG);
    			 if (rtvl) {
    				 rtvl = validationTestStrong(EMAIL_ELEMENT_ID, EMAIL_ELEMENT_REGEX, EMAIL_ELEMENT_LBL, EMAIL_ELEMENT_ERR_MSG2);
    			 }
    		 } else if (element_id_value == FIRSTNAME_ELEMENT_ID) {
    			 rtvl = validationTestStrong(FIRSTNAME_ELEMENT_ID, FIRSTNAME_ELEMENT_REGEX, FIRSTNAME_ELEMENT_LBL, FIRSTNAME_ELEMENT_ERR_MSG);
    		 } else if (element_id_value == SURNAME_ELEMENT_ID) {
    			 rtvl = validationTestStrong(SURNAME_ELEMENT_ID, SURNAME_ELEMENT_REGEX, SURNAME_ELEMENT_LBL, SURNAME_ELEMENT_ERR_MSG);
    		 } else if (element_id_value == ORG_NAME_ELEMENT_ID) {
    			 rtvl = validationTestStrong(ORG_NAME_ELEMENT_ID, ORG_NAME_ELEMENT_REGEX, ORG_NAME_ELEMENT_LBL, ORG_NAME_ELEMENT_ERR_MSG);
    		 } else if (element_id_value == USER_NAME_ELEMENT_ID) {
    			 rtvl = validationTestStrong(USER_NAME_ELEMENT_ID, USER_NAME_ELEMENT_REGEX, USER_NAME_ELEMENT_LBL, USER_NAME_ELEMENT_ERR_MSG);
    		 } else if (element_id_value == WRK_TELEPHONE_ELEMENT_ID) {
    			 rtvl = validationTestStrong(WRK_TELEPHONE_ELEMENT_ID, WRK_TELEPHONE_ELEMENT_REGEX, WRK_TELEPHONE_ELEMENT_LBL, WRK_TELEPHONE_ELEMENT_ERR_MSG);
    		 } else if (element_id_value == COUNTRIES_LIST) {
    			 rtvl = validationTestStrong(COUNTRIES_LIST, ORG_COUNTRY_ELEMENT_REGEX, ORG_COUNTRY_ELEMENT_LBL, ORG_COUNTRY_ELEMENT_ERR_MSG);
    		 } else if (element_id_value == PWD_ELEMENT_ID) {
    			 rtvl = validationPasswordTest();
    		 } else {
        		 // If none of the tests above succeeded then fail by default.
        		 rtvl = false;
    		 }
    		 
    	 } else if(validation_strength_value ==  MEDIUM_STRENGTH) {
    		 rtvl = false;
    	 } else if(validation_strength_value ==  WEEK_STRENGTH) {
    		 rtvl = false;
    	 } else if(validation_strength_value ==  ENOUGH_STRENGTH) {
    		 if (element_id_value == EMAIL_ELEMENT_ID) {
    			 rtvl = validationTestGreaterThan(EMAIL_ELEMENT_ID, 1, EMAIL_ELEMENT_LBL, EMAIL_ELEMENT_ERR_MSG);
    			 if (rtvl) {
    				 rtvl = validationTestStrong(EMAIL_ELEMENT_ID, EMAIL_ELEMENT_REGEX, EMAIL_ELEMENT_LBL, EMAIL_ELEMENT_ERR_MSG2);
    			 }
    		 } else if (element_id_value == FIRSTNAME_ELEMENT_ID) {
    			 rtvl = validationTestGreaterThan(FIRSTNAME_ELEMENT_ID, 1, FIRSTNAME_ELEMENT_LBL, FIRSTNAME_ELEMENT_ERR_MSG);
    		 } else if (element_id_value == SURNAME_ELEMENT_ID) {
    			 rtvl = validationTestGreaterThan(SURNAME_ELEMENT_ID, 1, SURNAME_ELEMENT_LBL, SURNAME_ELEMENT_ERR_MSG);
    		 } else if (element_id_value == ORG_NAME_ELEMENT_ID) {
    			 rtvl = validationTestGreaterThan(ORG_NAME_ELEMENT_ID, 1, ORG_NAME_ELEMENT_LBL, ORG_NAME_ELEMENT_ERR_MSG);
    		 } else if (element_id_value == USER_NAME_ELEMENT_ID) {
    			 rtvl = validationTestGreaterThan(USER_NAME_ELEMENT_ID, 1, USER_NAME_ELEMENT_LBL, USER_NAME_ELEMENT_ERR_MSG);
    		 } else if (element_id_value == WRK_TELEPHONE_ELEMENT_ID) {
    			 rtvl = validationTestGreaterThan(WRK_TELEPHONE_ELEMENT_ID, 1, WRK_TELEPHONE_ELEMENT_LBL, WRK_TELEPHONE_ELEMENT_ERR_MSG);
    		 } else if (element_id_value == COUNTRIES_LIST) {
    			 rtvl = checkCountryIsDefaultMsg();
    			 if (rtvl) {
    				rtvl = false;
    			 } else {
     				rtvl = true;
    			 }
    		 } else if (element_id_value == PWD_ELEMENT_ID) {
    			 rtvl = validationTestGreaterThan(PWD_ELEMENT_ID, 1, PWD_ELEMENT_LBL, PASSWORD_ELEMENT_ERR_MSG);
    			 if (rtvl) {
    				 rtvl = validationTestGreaterThan(PWD_ELEMENT_ID, 6, PWD_ELEMENT_LBL, PASSWORD_ELEMENT_ERR_MSG2);
    			 }
    		 } else {
        		 // If none of the tests above succeeded then fail by default.
        		 rtvl = false;
    		 }
    	 }
    	 
    	 return rtvl;
     };
     
     /**
      * Check that the user has deliberately selected a country
      * and not left the country selection on default text in drop down.
      */
     var checkCountryIsDefaultMsg = function() {
    	 var rtvl = false;
    	 var element = getElementById(COUNTRIES_LIST);
      	 var elementValue = element[0].options[element[0].selectedIndex].value;
    	 
    	 if (elementValue == "- please select a country") {
    		 var msg = ORG_COUNTRY_ELEMENT_LBL + " " + ORG_COUNTRY_ELEMENT_ERR_MSG;
    		 resetLblToDefault(COUNTRIES_LIST, '#FF0000', msg);
    		 rtvl = true;
    	 } else {
    		 resetLblToDefault(COUNTRIES_LIST, '#000000', ORG_COUNTRY_ELEMENT_LBL);
    	 }
    	 
    	 return rtvl;
     };
     
     var resetMsgToDefault = function(element_Id, color, element_msg) {
 		msg_Element_Id = element_Id + ELEMENT_ERR_MSG_ID_END_MSG;
     	msg_Element = getElementById(msg_Element_Id);
     	validationShowErrorMsg(element_msg, color, msg_Element);
     	var element = getElementById(element_Id);
     	element[0].focus();
     };
     
     var resetLblToDefault = function(element_Id, color, element_msg) {
  		msg_Element_Id = element_Id + ELEMENT_ERR_MSG_ID_END_LBL;
      	msg_Element = getElementById(msg_Element_Id);
      	validationShowErrorMsg(element_msg, color, msg_Element);
      	var element = getElementById(element_Id);
      	element[0].focus();
      };
     
     var validatePrivacyPolicy = function(element_Id) {
    	 var rtvl = false;
    	 object_element = getElementById(element_Id);
      	 elementValue = object_element[0].checked;
      	 if (elementValue) {
      		 rtvl = true;
      	 }
    	 return rtvl;
     };
     
     /**
      * value of hidden field to determine whether or not to 
      * allow the privacy policy check box to be activated. 
      */
     var setPersonalisedPrivacyPolicy = function(changeValue) {
    	 var element_Id = HIDDEN_PRIVACY_POLICY_ACCEPTED;
      	 var object_element = getElementById(element_Id);
      	 setElementTextForElement(changeValue, object_element);
      	 
      	 //for FireFox to set the value of the hidden form field "personalisedAccessChange"
      	 if (object_element[0].value == 0) {
      		 object_element[0].value = changeValue;
      	 }
     };
     
     /**
      * Check that the privacy policy (PP) has been accepted
      * by checking a value returned from the privacy
      * policy page.
      * 
      * Sets the check box to be active if the PP is accepted.
      * 
      * Returns true if PP has been accepted, false otherwise.
      * 
      */
     var privacyPolicyAcceptanceCheck = function() {
		 var rtvl = false;
		
		 var element_Id = HIDDEN_PRIVACY_POLICY_ACCEPTED;
	  	 var object_element = getElementById(element_Id);
	  	 elementValue = getElementTextForElement(object_element);
	  	 
	  	 //for FireFox to set the value of the hidden form field "personalisedAccessChange"
	  	 if (object_element[0].value == 0) {
	  		 elementValue = object_element[0].value;
	  	 } else if (object_element[0].value == 1) {
	  		elementValue = object_element[0].value;
	  	 } 
	
		 var element_Id = PRIVACY_POLICY_CHECKBOX;
	  	 var object_element = getElementById(element_Id);
	  	 
	  	 if (elementValue == 1) {
	  		 //change the disabled value of the check box to false, i.e. it is now active.
	  		 object_element[0].disabled=false;
	     	rtvl = true;
	  	 } else if (elementValue == 0) {
	  		 //do nothing
	      	 object_element[0].checked = false;
	      	 object_element[0].disabled=true;
	     	rtvl = false;
	  	 }
	  	 
	  	 return rtvl;
  	 
     };
     
     var rememberDropDownSelections = function() {
		
	   	var country_element = getElementById(COUNTRIES_LIST);
		
		var country_index_value = country_element[0].selectedIndex;
		var country_idx_val_element = getElementById(COUNTRIES_LIST_INDEX_VALUE);
	   	
	   	setElementTextForElement(country_index_value, country_idx_val_element);
	   	
	   	//for FireFox to set the value of the hidden form field
	   	if (country_idx_val_element[0].value == 0) {
	   		country_idx_val_element[0].value = country_index_value;
	   	}
	   	
	   	var language_element = getElementById(LANGUAGE_LIST);
	   	var language_index_value = language_element[0].selectedIndex;
	   	var language_idx_val_element = getElementById(LANGUAGE_LIST_INDEX_VALUE);
	   	
	   	setElementTextForElement(language_index_value, language_idx_val_element);
	   	 
	   	//for FireFox to set the value of the hidden form field
	   	if (language_idx_val_element[0].value == 0) {
	   		language_idx_val_element[0].value = language_index_value;
	   	}
	   	
	};
     
     
	/**
	 * Public visible methods from this JS object.
	 */
	return {
		
		/**
		 * Register the user based on the form field inputs.
		 */
		registerUser : function() {
			var rtvl = false;
			STRUTS_USER_REGISTRATION_ACTION = "userRegistrationSubmit.action";
			logger.debug("!!!!!!!!!!!! User registration request. registerUser().STRUTS_USER_REGISTRATION_ACTION = " + STRUTS_USER_REGISTRATION_ACTION );
			
			// for hidden forms that retain selection between pages. i.e. registration denied page
			rememberDropDownSelections();
			
			var validation_result = true;
			//validate all mandatory fields
			validation_result = validateFormFieldData(VALIDATION_STRENGTH);
			
			logger.debug("!!!!!!!!!!!! User registration request. registerUser(). validation_result = " + validation_result );
			
			if (!validation_result) {
				rtvl = false;
			} else {
		     	var registrationForm_element = getElementById(REGISTRATION_FORM);
		     	registrationForm_element[0].submit();
		     	rtvl = true;
			}
			
			return rtvl;
		},
		
		newPassword : function(){
			logger.debug("!!!!!!!!!!!! User registration request. newPassword().");
			var rtvl = false;
			var validation_result = true;
			validation_result = validateLoginElementData(VALIDATION_STRENGTH);
			logger.debug("!!!!!!!!!!!! User registration request. newPassword(). validation_result = " + validation_result );

			///userRegistrationNewPassword.action
			
			if (!validation_result) {
				logger.debug("!!!!!!!!!!!! User registration request. newPassword(). !validation_result = " + !validation_result );
				rtvl = false;
			} else {
				var theLocationToGo = "";
				theLocationToGo = "/userRegistrationNewPassword.action";
				theLocationToGo = theLocationToGo + "?emailAddress=";
				var emailAddress = "";
				emailAddress = getElementValueById(LOGIN_UID_ELEMENT_ID)
				theLocationToGo = theLocationToGo + emailAddress;
				logger.debug("!!!!!!!!!!!! User registration request. newPassword(). theLocationToGo = " + theLocationToGo );
				document.location.href = theLocationToGo;
		     	rtvl = true;
			}

			return rtvl;
		},
		
		personalisedPrivacyPolicy : function(){
			logger.debug("!!!!!!!!!!!! User registration request. personalisedPrivacyPolicy().");
			// for hidden forms that retain selection between pages.
			rememberDropDownSelections();
			var theLocationToGo = "";
			theLocationToGo = "/userRegistrationPrivacyPolicy.action";
			
	     	var registrationForm_element = getElementById(REGISTRATION_FORM);
	     	registrationForm_element[0].action = theLocationToGo;
	     	registrationForm_element[0].submit();
			return true;
		},
		
		/**
		 * Called from the onload function
		 * Automatically tick the Privacy Policy acceptance checkbox
		 */
		checkIfPrivacyPolicyAccepted : function(){
			var validation_result = false;
			validation_result = privacyPolicyAcceptanceCheck();
			
			if (validation_result == true) {
				 var element_Id = PRIVACY_POLICY_CHECKBOX;
			  	 var object_element = getElementById(element_Id);
		      	 object_element[0].checked = true;
			}
	
			//validate all mandatory fields
			//validation_result = validateFormFieldData(VALIDATION_STRENGTH);
			return true;
		},
		
		/**
		 * Called from the check box onclick function
		 * don't automatically tick the Privacy Policy acceptance box.
		 */
		checkIfPrivacyPolicyAccepted2 : function(){
			var validation_result = false;
			validation_result = privacyPolicyAcceptanceCheck();

			//validate all mandatory fields
			//validation_result = validateFormFieldData(VALIDATION_STRENGTH);
			return true;
		},
		
		resetDropDownSelections : function(){
			var rtvl = false;
			
			// ******************
			// country
			// ******************
			
			// reset the country selection
	      	var country_element = getElementById(COUNTRIES_LIST);
	      	
			var country_idx_val_element = getElementById(COUNTRIES_LIST_INDEX_VALUE);
	      	country_element_value = getElementTextForElement(country_idx_val_element);
	      	
	      	//for FireFox to set the value of the hidden form field
	      	if (country_idx_val_element[0].value != 0) {
	      		country_element_value = country_idx_val_element[0].value;
	      	}
	      	
	      	// set the selection option the the relevant index
	      	country_element[0].selectedIndex = country_element_value;
			
			// set the hidden field that holds the country selection index value to empty string.
			country_index_value = "";
	      	setElementTextForElement(country_index_value, country_idx_val_element);
	      	
	      	//for FireFox to set the value of the hidden form field
	      	if (country_idx_val_element[0].value == 0) {
	      		country_idx_val_element[0].value = country_index_value;
	      	}

			// ******************
			// language
			// ******************
	      	
			// reset the language selection
	      	var language_element = getElementById(LANGUAGE_LIST);
			var language_idx_val_element = getElementById(LANGUAGE_LIST_INDEX_VALUE);
			language_index_value = getElementTextForElement(language_idx_val_element);
	      	
	      	//for FireFox to set the value of the hidden form field
	      	if (language_idx_val_element[0].value != 0) {
	      		language_index_value = language_idx_val_element[0].value;
	      	}
	      	
	      	// set the selection option the the relevant index
	      	language_element[0].selectedIndex = language_index_value;
			
			// set the hidden field that holds the language selection index value to empty string.
	      	language_index_value = "";
	      	setElementTextForElement(language_index_value, language_idx_val_element);
	      	
	      	//for FireFox to set the value of the hidden form field
	      	if (language_idx_val_element[0].value == 0) {
	      		language_idx_val_element[0].value = language_index_value;
	      	}
	      	
			return true;
		},
		
		acceptPrivacyPolicy : function(){
			logger.debug("!!!!!!!!!!!! User registration request. acceptPrivacyPolicy().");
			var rtvl = false;
			setPersonalisedPrivacyPolicy(1);
			
	     	var registrationForm_element = getElementById(HIDDEN_REGISTRATION_FORM);
	     	registrationForm_element[0].submit();
	     	
			return true;
		},
		
		declinePrivacyPolicy : function(useCase){
			logger.debug("!!!!!!!!!!!! User registration request. declinePrivacyPolicy().");
			var rtvl = false;
			setPersonalisedPrivacyPolicy(0);
	     	var registrationForm_element = getElementById(HIDDEN_REGISTRATION_FORM);
	     	
	     	if (useCase == "0") {
	     		registrationForm_element[0].action = "/userRegistrationDeclinedPrivacyPolicy.action";
	     	}
	     	registrationForm_element[0].submit();
	     	
			return true;
		},
		
		/**
		 * Carry out preparatory work so 
		 * that the user may register with 
		 * the Source application.
		 */
		prepRegisterUser : function() {
			//STRUTS_USER_REGISTRATION_ACTION = "userRegistrationGetUserDetails.action";
			logger.debug("!!!!!!!!!!!! User registration request. prepRegisterUser() " );
		},
		
		/**
		 * Validate the email form field value
		 */
		validateEmail : function() {
			logger.debug("!!!!!!!!!!!! User registration request. validateEmail() " );
			validateElementData(VALIDATION_STRENGTH, EMAIL_ELEMENT_ID);
		},
		
		/**
		 * Validate the first name from field value
		 */
		validateFirstName : function() {
			logger.debug("!!!!!!!!!!!! User registration request. validateFirstName() " );
			validateElementData(VALIDATION_STRENGTH, FIRSTNAME_ELEMENT_ID);
		},
		
		/**
		 * Validate the last name from field value
		 */
		validateSurName : function() {
			logger.debug("!!!!!!!!!!!! User registration request. validateSurName() " );
			validateElementData(VALIDATION_STRENGTH, SURNAME_ELEMENT_ID);
		},
		
		validateOrgName : function() {
			logger.debug("!!!!!!!!!!!! User registration request. validateOrgName() " );
			validateElementData(VALIDATION_STRENGTH, ORG_NAME_ELEMENT_ID);
		},
		
		validateUserName : function() {
			logger.debug("!!!!!!!!!!!! User registration request. validateUserName() " );
			validateElementData(VALIDATION_STRENGTH, USER_NAME_ELEMENT_ID);
		},
		
		validatePassword  : function() {
			logger.debug("!!!!!!!!!!!! User registration request. validatePassword() " );
			validateElementData(VALIDATION_STRENGTH, PWD_ELEMENT_ID);
		},
		
		validateWorkTelephone  : function() {
			logger.debug("!!!!!!!!!!!! User registration request. validateWorkTelephone() " );
			validateElementData(VALIDATION_STRENGTH, WRK_TELEPHONE_ELEMENT_ID);
		},
		
		validateLoginUid  : function() {
			logger.debug("!!!!!!!!!!!! User registration request. validateCountry() " );
			validateLoginElementData(VALIDATION_STRENGTH, LOGIN_UID_ELEMENT_ID);
		}
		
	};
	
}();/**
 * @class FisheyeWidget
 * blurb...
 */
            /*
FisheyeWidget = function(instanceName, minItemWidth, minItemHeight, maxItemWidth, maxItemHeight, imagePath, imageType, titleOffColour, titleOnColour) {
    this.titleOffColour = (titleOffColour) ? Math.hexToNum(titleOffColour) : 255;
    this.titleOnColour = (titleOnColour) ? Math.hexToNum(titleOnColour) : 51;
    this.titleColourDiff = this.titleOffColour - this.titleOnColour;
    this.instanceName = instanceName.uncapitalize();
    this.minItemWidth = minItemWidth;
    this.minItemHeight = minItemHeight;
    this.maxItemWidth = maxItemWidth; // 206;
    this.maxItemHeight = maxItemHeight; // 154; // also need to set the container element height on the dom (manually)
    this.imagePath = imagePath;
    this.imageType = imageType;
    this.containerElementId = "dv" + instanceName + "Container";
    this.containerElement = document.getElementById(this.containerElementId);
    this.originalContainerOffsetX = this.containerElement.offsetLeft;
    this.itemsContainerElementId = "td" + instanceName + "ItemsContainer";
    this.titleContainerElementId = "dv" + instanceName + "ImageTitle";
    // confirm that all parameters were passed
    this.checkProperties(this);

    this.widthScalingFactor = this.minItemWidth / this.minItemHeight;
    this.resizeIncrementY = 2.5;
    this.resizeIncrementX = this.resizeIncrementY * this.widthScalingFactor;
    //
    this.numberOfItems = 0;
    this.itemArray = new Array();
    this.totalTimeExpired = 0;
    this.totalOscillations = 0; // was "u"
    this.usageCoefficient = 0; // was "n"
    this.oldDate = new Date;

    this.queryString = "";
    // totalWidth = 500; // 4 * 107
    this.activeIndex = 0;
    this.titleDisplayed = false;
    this.title;
    this.firstHoverOccurred = false;
    this.p = 0;
}

// Shifts the position of the fisheye widget horizontally
FisheyeWidget.prototype.shiftHorizontal = function(pixels) {
    pixels = pixels || 0;
    var newXPos = this.originalContainerOffsetX + pixels;
    this.containerElement.style.left = newXPos + "px";
};

FisheyeWidget.prototype.log = function(msg) {
    if (logger) {
        logger.info(msg);
    }
};

FisheyeWidget.prototype.getContainerElement = function() {
    return this.containerElement;    
}

FisheyeWidget.prototype.display = function(show) {
	if (this.containerElement) {
        // hide the fisheye for the time being, it is to be moved
        // this.containerElement.style.visibility = show ? "visible" : "hidden";
	}
};

FisheyeWidget.prototype.checkProperties = function(obj) {
    var valid = true;
    for (property in obj) {
        if (typeof(obj[property]) == "undefined") {
            alert('Property ' + property + ' must be set.');
            valid = false;
            debugger;
        }
    }
    return valid;
};

FisheyeWidget.prototype.setQueryString = function(qs) {
    this.queryString = qs;
};

FisheyeWidget.prototype.addMenuItem = function(id, title, href) {
    var item = {
        id : id,
        title : title,
        href : href,
        height : this.minItemHeight,
        width : this.minItemWidth,
        marginTop : 20,
        imageUrlSmall : this.imagePath + id + "-sm." + this.imageType
    };

    this.checkProperties(item);
    this.numberOfItems++;
    this.itemArray[this.numberOfItems] = item;
};

FisheyeWidget.prototype.render = function() {
    var item, html = '';
    for (var i = 1; i < this.itemArray.length; i++) {
        item = this.itemArray[i];
        // html += '<a onmouseover="debugger;'+this.instanceName+'.handleMouseOver('+i+')" onmouseout="'+this.instanceName+'.handleMouseOut('+i+')" id="'+i+'a" class="q" href="'+item.href+'" onclick="return '+this.instanceName+'.handleItemClick(this)"><img id="'+this.instanceName+'_image'+i+'" src="'+item.imageUrlSmall+'" alt="'+item.title+'" border="0" height="'+this.minItemHeight+'" width="'+this.minItemWidth+'" align="top"></a>';
        html += '<a onmouseover="'+this.instanceName+'.handleMouseOver('+i+')" onmouseout="'+this.instanceName+'.handleMouseOut('+i+')" id="'+i+'a" class="q" href="javascript:{}" onclick="return '+this.instanceName+'.handleItemClick(this,'+i+')"><img id="'+this.instanceName+'_image'+i+'" src="'+item.imageUrlSmall+'" alt="'+item.title+'" border="0" height="'+this.minItemHeight+'" width="'+this.minItemWidth+'" align="top"></a>';
    }
    // var html = '<a onmouseover="fisheye.handleMouseOver(1)" onmouseout="fisheye.handleMouseOut(1)" id="1a" class="q" href="http://www.google.com/search?hl=en&amp;tab=wi" onclick="return qs(this)"><img id="image1" src="images/fisheye/facts-sm.png" alt="Fund Facts" border="0" height="77" width="103" align="top"></a><a onmouseover="fisheye.handleMouseOver(2)" onmouseout="fisheye.handleMouseOut(2)" id="2a" class="q" href="http://www.google.com/images?hl=en&amp;tab=wi" onclick="return qs(this)"><img id="image2" src="images/fisheye/trading-sm.png" alt="Images" border="0" align="top" height="77" width="103"></a><a id="3a" class="q" onmouseover="fisheye.handleMouseOver(3)" onmouseout="fisheye.handleMouseOut(3)" href="http://groups-beta.google.com/groups?hl=en&amp;tab=wg" onclick="return qs(this)"><img id="image3" src="images/fisheye/underlying-sm.png" alt="Groups" border="0"  align="top" height="77" width="103"></a><a id="4a" class="q" onmouseover="fisheye.handleMouseOver(4)" onmouseout="fisheye.handleMouseOut(4)" href="http://news.google.com/nwshp?hl=en&amp;gl=us" onclick="return qs(this)"><img id="image4" src="images/fisheye/performance-sm.png" alt="News" border="0"  align="top" height="77" width="103"></a>';
    // TODO : clean up below
    var td = document.getElementById(this.itemsContainerElementId);
    td.innerHTML = html;
};

FisheyeWidget.prototype.handleMouseOver = function(index) {
    this.activeIndex = index;
    this.oldDate = new Date;
    setTimeout(this.gidle.createDelegate(this), 20);
};

FisheyeWidget.prototype.handleMouseOut = function(index) {
    this.activeIndex = 0;
    this.titleDisplayed = false;
    this.oldDate = new Date;
    setTimeout(this.gidle.createDelegate(this), 20);
};

FisheyeWidget.prototype.generate = function() {
    this.title = document.getElementById(this.titleContainerElementId);
    this.totalWidth = this.numberOfItems * this.minItemWidth; // 4 * 103
    this.maxTotalWidth = this.totalWidth - this.minItemWidth + this.maxItemWidth;
    // debug("total width = " + this.totalWidth);
    this.render();
    setTimeout(this.gidle.createDelegate(this), 20);
};

FisheyeWidget.prototype.getImageId = function(index) {
    return this.instanceName + "_image" + index;
};

FisheyeWidget.prototype.setItemTitleColour = function(itemHeight) {
    var percentage = (itemHeight - this.minItemHeight) / (this.maxItemHeight - this.minItemHeight);
    var r = Math.floor(this.titleOffColour - (percentage * this.titleColourDiff));
    this.title.style.color = "rgb(" + r + "," + r + "," + r + ")";
    this.log("color = " + r);    
};

FisheyeWidget.prototype.gidle = function() {
    var consumedWidth = 0;
    for (var i = 1; i < this.itemArray.length; i++) {
        var item = this.itemArray[i];
        var imageId = this.getImageId(i);
        var imageElem = document.getElementById(imageId);
        if (this.activeIndex != i) {
            if (item.height > this.minItemHeight) {
                item.height -= this.resizeIncrementY;
                item.width -= this.resizeIncrementX;
                item.width = Math.ceil(item.width);
                item.marginTop += 1;
                if (item.height <= this.minItemHeight) {
                    item.height = this.minItemHeight;
                    item.width = this.minItemWidth;
                    imageElem.src = item.imageUrlSmall; // this.imagePath + item.id + "-sm." + this.imageType;
                }
                if (item.marginTop > 20) {
                    item.marginTop = 20;
                }
                imageElem.width = item.width;
                imageElem.height = item.height;
                imageElem.style.marginTop = item.marginTop + "px";
                if (this.activeIndex == 0) {
                    this.setItemTitleColour(item.height);
                }
                this.p = 1;
            }
            consumedWidth += item.width;
        }
    }
    var activeItem = this.itemArray[this.activeIndex];
    if (this.activeIndex != 0 && activeItem.height < this.maxItemHeight) {
        // imagename = "image" + this.activeIndex;
        imageId = this.getImageId(this.activeIndex);
        imageElem = document.getElementById(imageId);
        // this is width
        if (this.titleDisplayed == false) {
            this.titleDisplayed = true;
            if (this.activeIndex < 6) {
                var y = 360 - (this.activeIndex - 1) * this.maxItemHeight;
                this.title.innerHTML = document.getElementById(imageId).alt + "<img src=\"cleardot.gif\" width=\"" + y + "\" height=\"1\"/>";
            } else {
                var y = (this.activeIndex - 7) * this.maxItemHeight + this.maxItemHeight;
                this.title.innerHTML = "<img src=\"cleardot.gif\" width=\"" + y + "\" height=\"1\"/>" + document.getElementById(imageId).alt;
            }
        }
        activeItem.height += this.resizeIncrementY;
        activeItem.width += this.resizeIncrementX;
        activeItem.marginTop -= 1;
//        this.log("* marginTop = " + activeItem.marginTop);
        // eddy hack
        activeItem.width = Math.ceil(activeItem.width);

        this.p = 1;
        if (activeItem.height > this.maxItemHeight) {
            activeItem.height = this.maxItemHeight;
            activeItem.width = this.maxItemWidth;
        }
        consumedWidth += activeItem.width;
        if (consumedWidth < this.totalWidth) {
            // activeItem.height += this.totalWidth - consumedWidth;
            activeItem.width += this.totalWidth - consumedWidth;
            if (activeItem.height > this.maxItemHeight) {
                activeItem.height = this.maxItemHeight;
                activeItem.width = this.maxItemWidth;
                this.log("maxed out");
                // imageElem.style.border = "solid 1px green";
            }
            consumedWidth = this.totalWidth;
        }
        if (consumedWidth > this.maxTotalWidth) {
            var excessWidth = consumedWidth - this.maxTotalWidth;
            activeItem.width = activeItem.width - excessWidth;
            consumedWidth = consumedWidth - excessWidth;
        }
        this.setItemTitleColour(activeItem.height);
        imageElem.width = activeItem.width;
        imageElem.height = activeItem.height;
        imageElem.style.marginTop = activeItem.marginTop + "px";
        // document.getElementById(imageId).src = this.imagePath + activeItem.id + "." + this.imageType;
        imageElem.src = this.imagePath + activeItem.id + "." + this.imageType;
    }

    var newDate = new Date;
    var timeExpired = newDate.getTime() - this.oldDate.getTime();
    this.oldDate = newDate;
    this.totalTimeExpired += timeExpired;
    this.totalOscillations++;
    // n = t / u;
    this.usageCoefficient = this.totalTimeExpired / this.totalOscillations;
    // h = 5;
    //this.resizeIncrementY = 5;
    //this.resizeIncrementX = this.resizeIncrementY * 1.337662;
//
    if (this.p) {
        setTimeout(this.gidle.createDelegate(this), 20);
        this.p = 0;
    }
};

FisheyeWidget.prototype.handleItemClick = function(anchor, index) {
    var item = this.itemArray[index];
    this.goto(item.href);    
    return 1;
};

FisheyeWidget.prototype.goto = function(href) {
    frames['bodyIFrame'].location.href = '/'+href+this.queryString;
};
              */
/**
 * @class FisheyeManager
 * Controller for the flash fisheye's
 * @singleton
 */
var FisheyeManager = function() {
    var UNDEFINED = "undefined";
    
    var MOVIE_ID = 'dockMenu';
    var MOVIE_CONTAINER_ID = 'dvFisheyeContainer';
    var MOVIE_EMBED_TO_ELEMENT_ID = 'dvFisheyeContent';
    var MOVIE_PATH = 'fisheye/dock-menu.swf';
    var MOVIE_WIDTH = 174;
    var MOVIE_HEIGHT = 750;

    var CONSTRUCTION_RETRY_DELAY = 100; // ms
    var NUMBER_CONSTRUCT_RETRIES = 5;

    var FRAME_NAME = "bodyIFrame";
    var CONFIG_URL = "/fisheyeConfiguration.action?type=config.js";
    var REPOSITION_DELAY = 220; // millis
    var CONFIG_RENDER_DELAY = 20; // millis
    var SELECTOR_CONTAINER = '#' + MOVIE_CONTAINER_ID;
    var DEFAULT_LEFT_POSITION = 929;
    var DEFAULT_TOP_POSITION = 200;
    var OFF_PAGE = '-1000px';

    var movie = null;
    var displayedFisheye = null;
    var activeFisheye = null;
    var configuration = null;
    var constructRetries = 0;
    var isVisible = false;

    var getFrame = function() {
        return $('#' + FRAME_NAME).contents();
    };

    var goto = function(href) {
        var target = getFrame()[0];
        logger.info("redirecting to " + href);
        target.location.href = href;
    };

    var getRequiredHeight = function(fisheye) {
        var result = 0;
        if (typeof fisheye.json.height !== UNDEFINED) {
            result = parseInt(fisheye.json.height);
        }
        else {
            result = MOVIE_HEIGHT;
        }
        return result;
    };

    var setActive = function(fisheye) {
        var height;
        if (activeFisheye !== fisheye) {
            construct(fisheye);
        }
        else {
            displayFisheye();
        }
    };

    var frameContainsElement = function(elementId) {
        return (getFrame().find('#' + elementId).length > 0);
    };

    var rewindMovie = function(object) {
        if (BrowserDetect.isIE()) {
            if (movie) {
                movie.Rewind();
                movie.Play();
            }
        }
    };

    var getLeftPosition = function() {
        var positionX = DEFAULT_LEFT_POSITION;
        var iFrameOffsetLeft, rightColumn, rightColumnOffsetLeft;

        // offsets
        iFrameOffsetLeft = getElementLeft(bodyIFrame.frameElement);
        rightColumn = $('#bodyIFrame').contents().find('#dvRightPanel');
        if (rightColumn.length > 0) {
            rightColumnOffsetLeft = getElementLeft(rightColumn[0]);
            positionX = iFrameOffsetLeft + rightColumnOffsetLeft;
        }

        return positionX;
    };

    var getDisplayedPage = function() {
        if (typeof activeFisheye.json.commonAction === UNDEFINED) {
            // This control has an action associated with each item
            return getActionName();
        }
        else {
            // This control has a common action and each item is a querystring ("?category=value")
            return getCategoryQueryString();
        }
    };

    var getActionName = function() {
        var action = getFrame()[0].location.pathname;
        action = action.replace(".action","").replace("/","");
        return action;
    };

    var getCategoryQueryString = function() {
        var category = QueryStringUtils.getFromLocation("category", getFrame()[0].location);
        if (category === null) {
            category = getDefaultItemId(activeFisheye);
        }
        return category;
    };

    // TODO : a wee bit hacky here lad
    // simply returns the first item in the list
    var getDefaultItemId = function(fisheye) {
        return fisheye.json.itemList.item[0].id;
    };

    var syncSelectedItemToDisplayedPage = function() {
        // Ensure the correct item is selected
        var location = getDisplayedPage();
        if (activeFisheye.selectedItemId != location) {
            logger.debug("setting fisheye selected item to " + location);
            try {
                movie.selectById(location, false);
                activeFisheye.selectedItemId = location;
            }
            catch(e) {
                logger.error("error calling fisheye method selectById(). Exception = " + e.message || e);
            }
        }
    };

    var showFisheye = function() {
        var container = $(SELECTOR_CONTAINER);
        logger.debug("showing fisheye");
        container.css('top', DEFAULT_TOP_POSITION + 'px');
        container.css('left', getLeftPosition() + 'px');
        isVisible = true;
    };

    var hideFisheye = function() {
        var container = $(SELECTOR_CONTAINER);
        logger.debug("hiding fisheye");
        container.css('top', OFF_PAGE);
        container.css('left', OFF_PAGE);
        isVisible = false;
    };

    var displayFisheye = function() {
        if (activeFisheye !== null) {
            showFisheye();

            // Ensure the correct item is selected
            syncSelectedItemToDisplayedPage();
        }
        else {
            logger.error("cannot display fisheye without configuration");
        }
    };

    var render = function() {
        var visible = false;

        forEach(configuration, function(fisheye) {
            if (frameContainsElement(fisheye.elementId)) {
                visible = true;
                setActive(fisheye);
            }
        });

        if (!visible) {
            hideFisheye();
        }
    };

    var construct = function(fisheye) {
        if (movie === null) {
            movie = getFlashMovieObject(MOVIE_ID);
        }
        if (movie !== null) {
            constructFisheye(fisheye);
        }
        else {
            logger.error('cannot locate flash movie ' + MOVIE_ID);
        }
    };

    var setFisheyeHeight = function() {
        // Set the control to the desired height
        var requiredHeight = getRequiredHeight(activeFisheye);

        if (movie.offsetHeight !== requiredHeight) {
            movie.style.height = requiredHeight + "px";
            // And call the iframe resizer....
            FrameSizer.size();
        }
    };

    var constructFisheye = function(fisheye) {
        if (activeFisheye !== fisheye) {
            // Attempt to construct the movie only if the construct method is exposed.
            // If the browser is safari - we must ensure that the movie is visible first
            // Otherwise it will appear to construct successfully but will be forever invisible
            if ((movie.construct) && (!(BrowserDetect.isSafari() && (isVisible === false)))) {
                logger.debug("constructing fisheye");
                try {
                    movie.construct(fisheye.config);
                    activeFisheye = fisheye;
                    logger.debug("fisheye constructed");
                    // Hide the underlying static images
                    // getFrame().find('#' + activeFisheye.json.elementId).hide();
                    // Set the control to the desired height
                    setFisheyeHeight();
                    // Display the fisheye
                    displayFisheye();
                }
                catch(e) {
                    logger.error("Exception caught invoking method constuct() on flash movie. Exception = " + e.message || e);
                }
            }
            else {
                logger.error('flash movie does not expose method construct(). Number retries = ' + constructRetries);
                if (constructRetries < NUMBER_CONSTRUCT_RETRIES) {
                    showFisheye();
                    constructRetries++;
                    setTimeout( constructFisheye.createDelegate(this, [fisheye]), CONSTRUCTION_RETRY_DELAY );
                }
            }
        }
    };

    var embedFisheye = function() {
        var flashvars, attributes, params;

        params = {
            menu: "false",
            swliveconnect: 'true',
            quality: 'high',
            allowScriptAccess: 'sameDomain',
            scale: "noscale",
            // wmode: "transparent",
            wmode: "window",
            bgcolor: '#f4f4f4',
            salign: "lt"
        };
        flashvars = {};
        attributes = {
            id: MOVIE_ID,
            name: MOVIE_ID
        };

        // chrome fix
        if (BrowserDetect.isChrome()) {
            params.wmode = "opaque";
        }

        swfobject.embedSWF(MOVIE_PATH, MOVIE_EMBED_TO_ELEMENT_ID, MOVIE_WIDTH, MOVIE_HEIGHT, "8.0.0", "fisheye/expressInstall.swf", flashvars, params, attributes);
    };
    
    var configure = function(configList) {
        var i, xml, json, fisheye;

        configuration = [];
        for (i=0; i<configList.length; i++) {
            xml = configList[i].config;
            json = $.xml2json(xml);
            if (typeof json !== UNDEFINED) {
                fisheye = {
                    config: xml,
                    elementId: json.elementId,
                    json: json
                };
                fisheye.selectedItemId = getDefaultItemId(fisheye);
                configuration.push(fisheye);
                logger.debug("configuring fisheye for element " + json.elementId);
            }
            else {
                logger.error("error converting xml to json " + xml);
                break;
            }
        }
        if (i==0) {
            logger.error("fisheye configuration is empty");
        }
    };

    var fetchConfig = function() {
        var elt = document.createElement("script");
        elt.src = CONFIG_URL;
        elt.type="text/javascript";
        logger.info("loading fisheye config from location " + elt.src);
        document.getElementsByTagName("head")[0].appendChild(elt);
    };

    var getHref = function(id) {
        var result;
        if (typeof activeFisheye.json.commonAction === UNDEFINED) {
            // This control has an action associated with each item
            result = getPageHref(id);
        }
        else {
            // This control has a common action and each item is a querystring ("?category=value")
            result = getQueryStringHref(id);
        }
        return result;
    };

    var getPageHref = function(page) {
        var frame, qs = '';

        frame = getFrame();
        if (frame.length > 0) {
            qs = frame[0].location.search;
        }
        // return the href
        return page + '.action' + qs;
    };

    var getQueryStringHref = function(category) {
        return activeFisheye.json.commonAction + '.action?category=' + category;
    };

    return {
        /**
         * Style the selection prior to going to the href
         */
        init : function() {
            if (typeof(fisheyeConfig) !== UNDEFINED) {
                configure(fisheyeConfig);
                embedFisheye();
                render();
            }
            else {
                logger.error("fisheyeConfig is undefined");
            }
        },

        setVisibility : function() {
            render();
        },

        position : function() {
            setTimeout( render, CONSTRUCTION_RETRY_DELAY );
        },

        selectItem : function(index, id) {
            try {
                logger.debug("product page selected [" + id + "]");
                // reload the iframe
                activeFisheye.selectedItemId = id;
                goto(getHref(id));
            }
            catch(e) {
                logger.error('FisheyeManager.selectItem() : ' + e.message || e);
            }
        },

        getDisplayedFisheye : function() {
            return movie;
        }
    };
}();
/**
 * @class FrameSizer
 * Sizes the iframe after load and resize
 * @singleton
 */
var FrameSizer = function() {
    var objectName = 'FrameSizer';
    var frameId = 'bodyIFrame';
    var rightPanelId = 'dvRightPanel';
    var footerId = 'dvFooter';
    var catalogueNavigationId = 'catalogueNavigationContainer';
    var navContainerBuffer = 15; // number of pixels to buffer the bottom of the LHS Nav
    var frame;
    var innerDoc;
    var navContainer;
    var rightPanel;
    var viewport = {};
    var logInfo = true;
    var originalRightPanelOffsetHeight = null;

    var log = function(message) {
        if (logger && logInfo) {
            logger.info(message);
        }
    };

    var error = function(message) {
        message = 'FrameSizer : ' + message;
        if (logger) {
            logger.error(message);
        }
        else {
            alert(message);
        }
    };

    var getFrameBody = function() {
        var result;
        try {
            if (BrowserDetect.isSafari() || BrowserDetect.isChrome()) {
                result = innerDoc.documentElement;
            }
            else {
                result = innerDoc.getElementById('dvMidPanel_W') || innerDoc.getElementById('dvMidPanel_T') || innerDoc.getElementById('dvMidPanel_AboutSource');
                // return the body - firefox does not return correct scrollHeight on the midPanel
                result = $(result).parent().parent()[0];
            }
        }
        catch(e) {
            error('getFrameBody() ' + e.message);
        }
        return result;
    };

    /**
     * Returns the viewport of the client. This is the height and width of the browser window viewable area
     * @returns the viewport - a json object with height and width attributes
     */
    var getViewPort = function() {
        // the more standards compliant browsers (mozilla/netscape/opera/IE7) use window.innerWidth and window.innerHeight
        if (typeof window.innerWidth != 'undefined') {
            viewport.width = window.innerWidth;
            viewport.height = window.innerHeight;
        }
        // IE6 in standards compliant mode (i.e. with a valid doctype as the first line in the document)
        else if (typeof document.documentElement != 'undefined' &&
                 typeof document.documentElement.clientWidth != 'undefined' &&
                 document.documentElement.clientWidth != 0) {
            viewport.width = document.documentElement.clientWidth;
            viewport.height = document.documentElement.clientHeight;
        }
        // older versions of IE
        else {
            viewport.width = document.getElementsByTagName('body')[0].clientWidth;
            viewport.height = document.getElementsByTagName('body')[0].clientHeight;
        }

        log('viewport is ' + viewport.width + 'x' + viewport.height);
    };

    /**
     * Returns the top position of the iframe in the viewport in pixels
     * @returns the top position
     */
    var getFrameTop = function() {
        var body = $('#bg2Col')[0];
        var bodyOffsetTop = (body) ? body.offsetTop : 0;
        var result = getElementTop(frame) + bodyOffsetTop;
        log('frame y-top = ' + result);
        return result;
    };

    /**
     * Returns the height of the footer, or if none exists zero
     * @returns the footer height
     */
    var getFooterHeight = function() {
        var height = 0;
        var footer = document.getElementById(footerId);

        if (footer !== null) {
            height = footer.offsetHeight;
            height += parseInt($(footer).css('margin-top').replace('px',''));
            log('footer height = ' + height);
        }
        return height;
    };

    var setOriginalRightPanelOffsetHeight = function() {
        if (rightPanel && (originalRightPanelOffsetHeight === null)) {
            originalRightPanelOffsetHeight = rightPanel.offsetHeight;
        }
    };

    var getRightPanelOffsetHeight = function() {
        var result = null;

        if (originalRightPanelOffsetHeight !== null) {
            result = originalRightPanelOffsetHeight;
        }
        else {
            result = rightPanel.offsetHeight;
        }
        return result;
    };

    /**
     * Sets the height of the content frame to the given value
     * @param height, the height to set in pixels
     */
    var setFrameHeight = function(height) {
        // getFrameBody().style.height = height;
        frame.style.height = height;
        if (rightPanel) {
            setOriginalRightPanelOffsetHeight();
            rightPanel.style.height = height;
        }
    };

    var getFrameContentHeight = function() {
        var result = getFrameBody().scrollHeight;
        log('frame body scroll height = ' + result);
        return result;
    };

    var getMinimumFrameHeightForViewport = function() {
        var result = (viewport.height - getFrameTop() - getFooterHeight());
        log('viewport min frame height required = ' + result);
        return result;
    };

    /**
     * Calculates the minimum height required for the content of the page
     * @returns the desired height of the frame in pixels
     */
    var getMinimumFrameHeight = function() {
        var rightPanelOffsetHeight;
        var pixels = (BrowserDetect.isIE()) ? '' : 'px';
        var resizeTo = getFrameContentHeight();

        // Ensure the panel is at least as high as the Navigation Controller
        if (navContainer) {
            log("navContainer.offsetHeight = " + navContainer.offsetHeight);
            resizeTo = Math.max(resizeTo, (navContainer.offsetHeight + navContainerBuffer));
        }

        // Ensure the panel is at least as high as the Right Panel
        if (rightPanel) {
            rightPanelOffsetHeight = getRightPanelOffsetHeight();
            log("rightPanel.offsetHeight = " + rightPanelOffsetHeight);
            resizeTo = Math.max(resizeTo, rightPanelOffsetHeight);
        }

        // Ensure the panel fille at least the available viewport height
        resizeTo = Math.max(resizeTo, getMinimumFrameHeightForViewport());

        return resizeTo + pixels;
    };

    /**
     * Returns the height of the displayed fisheye, if one is visible, otherwise zero
     * The value returned is actually the minimum space required between the top of the iframe and the footer
     * for the fisheye to be displayed. This then needs to be aware of the margins to the top and bottom
     * @returns the height of the fisheye in pixels
     */
    var getFisheyeHeight = function() {
        var marginTop, paddingTop, footerOffset, height = 0;
        var displayedFisheye = FisheyeManager.getDisplayedFisheye();
        var footer = document.getElementById(footerId);
        var browserSpecificMargin = (BrowserDetect.isIE()) ? 1 : 0;

        if (displayedFisheye !== null) {
            marginTop = parseInt($('.fisheyeContainer').css('margin-top').replace('px',''));
            paddingTop = parseInt($('.fisheyeContainer').css('padding-top').replace('px',''));
            footerOffset = parseInt($(footer).css('margin-top').replace('px',''));
            height = (marginTop) + (paddingTop) - (footerOffset) + (browserSpecificMargin) + parseInt(displayedFisheye.offsetHeight);
        }

        return height;
    };

    /**
     * Resets the height of the right hand panel to it's minimum height,
     * this is at least as high as the fisheye widget if one is visible
     * This is to prevent a legacy right panel height effecting the sizing calculations
     */
    var resetRightPanelHeight = function() {
        var fisheyeHeight, rightPanelOffsetHeight;

        if (rightPanel !== null) {
            rightPanelOffsetHeight = getRightPanelOffsetHeight();
            fisheyeHeight = getFisheyeHeight();
            // Ensure the right panel is at least as high as the displayed fisheye
            $(rightPanel).css('height', Math.max(fisheyeHeight, rightPanelOffsetHeight) + 'px');
        }
    };

    /**
     * Calculates the minimum height required for the content of the page
     * and then sets the size of the frame and the right panel if it exists to this height.
     */
    var performSize = function() {
        var targetHeight;

        try {
            frame = document.getElementById(frameId);
            innerDoc = (frame.contentDocument) ? frame.contentDocument : frame.contentWindow.document;
            rightPanel = innerDoc.getElementById(rightPanelId);
            navContainer = document.getElementById(catalogueNavigationId);

            // Reset the right hand panel to it's minimum height,
            // this is to prevent the page being "sticky" to it's previous height
            resetRightPanelHeight();
            targetHeight = getMinimumFrameHeight();

            log('FrameSizer.size() : setting height to ' + targetHeight);
            setFrameHeight(targetHeight);

            // Resizing may hide/display the scrollbar which will require the fisheye to be repositioned
            if (typeof FisheyeManager !== "undefined") {
                FisheyeManager.position();                
            }
        }
        catch(e) {
            error("FrameSizer.performSize() : Exception caught - " + e.message);
        }
    };

    return {
        getName : function() {
            return objectName;
        },

        size : function() {
            if (!viewport.width) {
                getViewPort();
            }
            setTimeout( performSize.createDelegate(this), 20 );
        },

        handleWindowResize : function() {
            var currentHeight = viewport.height;
            getViewPort();
            if (currentHeight != viewport.height) {
                setTimeout( performSize.createDelegate(this), 20 );
            }
        }
    };
}();
/**
 * @class Landing
 * Controller for the splash page
 * @singleton
 */
var Landing = function() {
    var INVESTOR_TYPE_TAG_PREFIX = 'signIn';
    var INVESTOR_TAG_INSTITUTIONAL = INVESTOR_TYPE_TAG_PREFIX + 'Institutional';
    var INVESTOR_TAG_RETAIL = INVESTOR_TYPE_TAG_PREFIX + 'Retail';
    var TANDC_SELECTOR = '#termsAndConditions div.content span';
    var CERTIFICATION_LABEL_SELECTOR = '#confirmationContainer label';
    var CERTIFICATION_CHECKBOX_NAME = 'cbCertify';
    var openOption = null;

    // Set of images to preload
    var images = [
        'inst_investor_rollover.jpg',
        'inst_investor_dimmed.jpg',
        'retail_investor_rollover.jpg',
        'retail_investor_dimmed.jpg',
        'pulse.gif'
    ];

    var getCertificationElement = function() {
        return $('#dvCertification')[0];
    };

    // bit of a hack
    var getOther = function(elt) {
        if (elt.id == 'signInInstitutional')
            return $('#signInRetail')[0];
        else
            return $('#signInInstitutional')[0];
    };

    var removeMouseHandlers = function(elt) {
        elt.onmousedown = null;
        elt.onmouseover = null;
        elt.onmouseout = null;
    };

    var preloadImages = function() {
        if (document.images) {
            var i, oImage = new Image();
            for(i=0; i<=preloadImages.length; i++) {
                oImage.src = "/images/" + images[i];
            }
        }
    };

    var handleCerfityCheckboxCheck = function(cb) {
        if (cb) {
            var className = "signInForm",
                imageName = "btn_submit_lo.jpg";

            if (cb.checked) {
                className = "signedInForm";
                imageName = "pulse.gif";
            }
	        var form = $('#btnSubmit');
            $('#btnSubmit')[0].src = "images/" + imageName;
	        form = $('#signInSubmit');
            if ( form.length > 0) form[0].className = className;
        }
    };
    
    

    var applyScrollbar = function() {
        $('#termsAndConditions').jScrollPaneRemove();
        $('#termsAndConditions').jScrollPane({showArrows:true});  
        
        
    };

    var setInvestorType = function(investorType) {
        if (investorType == INVESTOR_TAG_INSTITUTIONAL) {
            // Login Institutional
            /*if (!BrowserDetect.isIE()) {
                $('#signInRetail')[0].style.visibility = "hidden";
            }*/
            openOption.className = 'login';
        }
        else {
            // Login Retail
            openOption.className = 'login_retail';
        }
    };

    var openPanel = function(elt) {
        elt.src = elt.src.replace('_rollover', '_off');
        openOption = getCertificationElement();
        // Shift the position if required
        setInvestorType(elt.id);
        // remove the mouse handlers
        // removeMouseHandlers(elt);
        // removeMouseHandlers(getOther(elt));

        // Set the value of the hidden investor type select
        $('#investorType')[0].value = elt.id.replace('signIn', '');

        // Make it visible
        openOption.style.visibility = "visible";

        // lightbox on
        $('.overlay').css('visibility', 'visible');
        getOther(elt).style.visibility = "hidden";
    };

    var checkConfirmationCheckBox = function(check) {
        var cb = document.getElementById(CERTIFICATION_CHECKBOX_NAME);
        if (cb) {
            cb.checked = check;
            handleCerfityCheckboxCheck(cb);
        }
    };

    var closePanel = function(elt) {
        openOption.style.visibility = "hidden";
        // uncheck the cb
        checkConfirmationCheckBox(false);
        // hide t&c
        $('#confirmationContainer').css('visibility', 'hidden');
        $('#tandcContainer').css('visibility', 'hidden');
        $('#confirmationContainer').css('display', 'none');
        $('#tandcContainer').css('display', 'none');
        // reset the country dropdown
        Dropdown.reset($('#countrySelector'));
        // reset the selection
        openOption = null;
        // lightbox off
        $('.overlay').css('visibility', 'hidden');
        getOther(elt).style.visibility = "visible";
        
        $(TANDC_SELECTOR).css('visibility', 'hidden');
        $(TANDC_SELECTOR).css('display', 'none');
        
    };

    var displayCertificationLabel = function(id) {
    	$(CERTIFICATION_LABEL_SELECTOR).css('display', 'none');
    	var certification = $(CERTIFICATION_LABEL_SELECTOR + '.' + id + 'Label');
    	if (certification[0].innerHTML.length > 100) {
            /* $('#termsAndConditions').css('height', '150px');
            $('#tandcContainer').css('margin-bottom', '100px'); */
            certification.css('width', '216px');
        }
        else {
            /* $('#termsAndConditions').css('height', '166px');
            $('#tandcContainer').css('margin-bottom', '82px'); */
            certification.css('width', '190px');
        }

        certification.show();//css('visibility', 'visible');
    };

    var positionBlurbOverlay = function() {
        // We do not know what dimensions the blurb image will be,
        // so position it's overlay dynamically
        var blurbImage, blurbOverlay;

        blurbImage = $('#splashBlurb').find('img')[0];
        if (typeof blurbImage != 'undefined') {
            blurbOverlay = $('#splashBlurb .overlay');    
            blurbOverlay.css('width', blurbImage.width + 'px');
            blurbOverlay.css('height', blurbImage.height + 'px');
            blurbOverlay.css('margin-left', blurbImage.offsetLeft + 'px');
        }
    };

    var ensureConfirmationCheckboxChecked = function() {
        var result = true;
        var cb = $('#cbCertify')[0];

        if (!cb.checked) {
            result = false;
            Glossary.landingConfirmation(cb, 'CONFIRMATION', 'You must confirm that you agree to the terms & conditions to enter the site.');
        }
        else {
            Glossary.close();

            // Append Adobe Flash supported version information to the href
            FormUtils.appendFlashSupportQueryString($('#signInSubmit')[0]);
        }

        return result;
    };
    
    var ensureUSConfirmationCheckboxChecked = function() {
        var result = true;
        var cb = $('#cbCertify')[0];

        if (!cb.checked) {
            result = false;
//            Glossary.landingConfirmation(cb, 'CONFIRMATION', 'You must confirm that you agree to the terms & conditions to enter the site.');
        }
        else {
//            Glossary.close();

            // Append Adobe Flash supported version information to the href
            FormUtils.appendFlashSupportQueryString($('#usSignInSubmit')[0]);
        }

        return result;
    };

    return {
        init : function() {
            // Preload images
            preloadImages();
            // If cerfifyt checkbox is checked (ie: back button) set the state
            var cb = $('#' + CERTIFICATION_CHECKBOX_NAME)[0];
            handleCerfityCheckboxCheck(cb);
            // Hide the terms and conditions (scrollbar widget complains if this is invisible prior to initialisation)
            $('#tandcContainer').css('display', 'none');
            // Position the blurb overlay
            positionBlurbOverlay();
        },

        /**
         * ie6 cannot handle mouseover's on submit elements. Handle with js
         */
        submitHover : function(submit) {
            submit.src = "/images/btn_submit.jpg";
        },

        submitExit : function(submit) {
            submit.src = "/images/btn_submit_lo.jpg";
        },

        cbCertifyClick : function(cb) {
            handleCerfityCheckboxCheck(cb);
        },

        tandcHover : function() {
            $('#tandcChevron').show();
        },

        tandcExit : function() {
            $('#tandcChevron').hide();
        },
		showCountries: function(id, property) {
			$(id).css('display', property)
		},
		showTermsAndConditions: function(contentId, jurId) {
			$(contentId).show();
			$('#view > div[class=tandt]').hide();
			$('#view > div[class=-confirm]').hide();
			$('label[for=cbCertify]').hide();
			$(jurId).show();
			$(jurId + '-confirm').show();
			$('#view').jScrollPaneRemove();
			$('#view').jScrollPane({showArrows:true});
		},
		hover: function(current, id) {
			if (current.className == 'bar-hover') return;
			var other = $(id);
			current.className = 'bar-hover';
			other.removeClass('bar bar-hover bar-dimmed');
			other.addClass('bar-dimmed');
		},
		exit: function(current, id) {
			if (current.className == 'bar') return;
			var other = $(id);
			current.className = 'bar';
			other.removeClass('bar bar-hover bar-dimmed');
			other.addClass('bar-dimmed');
		},
		signInOptionSelectNew : function(elt, parentId, otherId) {
			this.overlay = this.overlay || $('#overlay');
			this.overlay.css('visibility', 'visible');
			$(parentId).css('height', '310px');
			$(parentId + ' > div[class=countries]').show()
			$(otherId).hide();
		},
        signInOptionHover : function(elt) {
            elt.src = elt.src.replace('_off', '_rollover');
            var other = getOther(elt);
            other.src = other.src.replace('_off', '_dimmed');
        },

        signInOptionExit : function(elt) {
            elt.src = elt.src.replace('_rollover', '_off');
            var other = getOther(elt);
            other.src = other.src.replace('_dimmed', '_off');
        },

        signInOptionSelect : function(elt) {
            if (openOption == null) {
	            $('#login_to_my_source').hide();
            	var url = '/tiles/ajax/countriesResult.jsp?investorType=' + elt.id;
            	Ajax.call(url, function(xml){
                	try {
            		var divtochange = $(xml).children('countrySelectorContents');
                	$('#dvWrapDropdown').html($(xml));
                	}
                	catch(e) {
                		// did not get data
                	}
                	openPanel(elt);
            	}, function(xml){alert('nothing');alert(xml);}, null);
            }
            else {
                closePanel(elt);
	            $('#login_to_my_source').show();
            }
        },
        
        usSignInOptionSelect : function(elt) {
            if (openOption == null) {
                openPanel(elt);
                $('#confirmationContainer').show();
                $('#tandcContainer').show();

                // Initialise the scrollbar
                applyScrollbar();

            }
            else {
                closePanel(elt);
            }
        },

        handleCountrySelect : function(id, value, widget) {
            // Set the value of the hidden country select
        	//if($(TANDC_SELECTOR).style.visibility = "visible") {
        	$('#countryCode')[0].value = id;
	            // Select the correct tandc text to display
	        	$(TANDC_SELECTOR).hide();
	        	$(TANDC_SELECTOR).css('display', 'none');
	        	$(CERTIFICATION_LABEL_SELECTOR).hide();
	            // uncheck the confirmation cb
	            checkConfirmationCheckBox(false);
	            // Show the country specific t&c and certification label
	            $(TANDC_SELECTOR + '.' + id).show();
	            $(TANDC_SELECTOR + '.' + id).css('visibility', 'visible');
	            displayCertificationLabel(id);
	            $('#confirmationContainer').show();
	            $('#confirmationContainer').css('visibility', 'visible');
	            $('#tandcContainer').show();
	            $('#tandcContainer').css('visibility', 'visible');
	            // Initialise the scrollbar
	            applyScrollbar();
        },

        submitClick : function() {
            // Disallow the signin if the confirmation checkbox is not checked
            return ensureConfirmationCheckboxChecked();
        },
        
        usSubmitClick : function() {
            // Disallow the signin if the confirmation checkbox is not checked
            return ensureUSConfirmationCheckboxChecked();
        }
    }
}();

/**
 * @class MarketDataPoller
 * Controller for the market data polling mechanism
 * @singleton
 */
var MarketDataPoller = function() {
    var POLL_INTERVAL = 15000; // milliseconds
    var LAST_TICK_TIMESTAMP_ID = '.mdStream.md_lastTickDate';
    var pollAction = "jsonResults/fetchMarketData.action";
    var FADE_TYPE = 'sinusoidal';
    var FADE_COLOUR_GREEN = '#c3ee87';
    var FADE_COLOUR_RED = '#ff6666';
    var FADE_COLOUR_NEUTRAL = '#FFEEBF';
    var tickerIdList = null;
    var activeTimer = null;
    var lastTickDate = [];

   /**
    * Performs the Ajax post to the server.
    * @param successDelegate, method to call on success
    * @param emptyDelegate, method to call upon emtry result set
    */
    var ajaxCall = function(successDelegate, emptyDelegate) {
        var lastPoll = getLastTickDate();
        var timeZoneCode = getTimeZoneCode();
        if (lastPoll != undefined) {
            var data = {
                tickerIdList : tickerIdList,
                lastUpdateTime : lastPoll,
                timeZoneCode : timeZoneCode
            };
            logger.debug("Market data poll request = " + pollAction + ' with data ' + data.toString());
            Ajax.post(pollAction, data, successDelegate, emptyDelegate, null, "json");
        }
        else {
            logger.error("MarketDataPoller: Unable to retrieve the last poll datetime");
        }
    };

   /**
    * Return the lastTickDate representing the data contained on the page.
    * Where the lastTickDates differ for the tickerIds it will return the older one
    * If one is not known it will use the lastTickDate returned from the server in the jsp itself
    * @returns the datetime of the tick data contained on the page
    */
    var getLastTickDate = function() {
        var lastPoll = lastTickDate.min;
        if (lastPoll == undefined) {
            // Fetch the last tick date from the page
            var elt = getElement(LAST_TICK_TIMESTAMP_ID);
            if (elt.length > 0) {
                // The last tick date element has the datetime embedded in it's ID tag
                var id = elt[0].id.replace("dt","");
                lastPoll = new Date(parseInt(id, 10));
            }
        }

        return lastPoll;
    };

    var getTimeZoneCode = function() {
        var result = "ERROR";
        // Fetch the last tick date from the page
        var elt = getElement(LAST_TICK_TIMESTAMP_ID);
        if (elt.length > 0) {
            var split = elt[0].innerHTML.split(' ');
            if (split.length > 1) {
                result = split[1];
            }
        }
        return result;
    };

    var handleEmptyResultSet = function(results) {
        alert('empty' + results);
    };

    var checkResultsIntegrity = function(response) {
        var result = true;
        if (response) {
            result = (response.result.length > 0);
            if (!result) {
                logger.debug("MarketDataPoller : empty result set");                
            }
        }

        return result;
    };

   /**
    * Formats a numeric value for display on the page. Ensures the rounding strategy employed is the same as on the server
    * so that values that change are actually changing and not rounding errors.
    * The tag fmt:formatNumber rounding behavior on the server is ROUND_HALF_EVEN
    * @param field, the target html element to be updated
    * @param value, the new value to display
    * @returns The formatted value
    */
    var formatNumericValue = function(field, value) {
        // round to 2 decimal places (ROUND_HALF_EVEN)
        value = Math.bankersRound(value, 2).toFixed(2);
        // include any currency symbol existing in the field
        value = field.attr('innerHTML').replace(/[0-9.]+/, value);
        return value;
    };

   /**
    * Returns the colour to employ for the fade effect when changing a field's value
    * @param value, the new value to display
    * @param field, the target html element to be updated
    * @returns The colour to employ for the fade effect
    */
    var getNumericFadeColour = function(value, field) {
        var fadeColour = FADE_COLOUR_NEUTRAL;
        var existingValue;

        try {
            existingValue = parseFloat(field.attr('innerHTML').replace(/[^0-9.]+/,''));
            
            if (value > existingValue) {
                fadeColour = FADE_COLOUR_GREEN;
            }
            if (value < existingValue) {
                fadeColour = FADE_COLOUR_RED;
            }
        }
        catch(e) {
            logger.error("getNumericFadeColour : " + e.message);
        }

        return fadeColour;
    };

   /**
    * Updates the given html element with the provided value
    * @param field, the target html element to be updated
    * @param value, the new value to display
    * @param fadeColour, the colour to fade
    */
    var updateFieldHTML = function(field, value, fadeColour) {
        var existingHTML = field.attr('innerHTML');

        // Only update the field if the value has changed.
        if (existingHTML != value) {
            // Update the field html
            field.text(value);

            // Fade out a colour to the target element
            field.highlightFade({
                color: fadeColour,
                speed: 2000,
                iterator: FADE_TYPE
            });
        }
    };

   /**
    * Updates the given html element with the provided value
    * @param field, the target html element to be updated
    * @param value, the new value to display
    */
    var updateField = function(field, value) {
        var fadeColour = FADE_COLOUR_GREEN;

        if (field.length >= 1) {
            // Preformat the value for display
            if (typeof value == "number") {
                fadeColour = getNumericFadeColour(value, field);
                value = formatNumericValue(field, value);                
            }
            else {
                fadeColour = FADE_COLOUR_NEUTRAL;
            }

            // Update the field inner html
            updateFieldHTML(field, value, fadeColour);
        }
    };

    var getFormattedDate = function(value, strTimeZone) {
        // format the value
        return dateFormat(value, "summaryTime") + ' ' + strTimeZone;
    };

    /**
     * Sets the last tick date for the given tickerId
     * @param tickerId, the ticker for which the tick date represents
     * @param mdDate, the tick date
     * @returns true, if the data is newer than the last known tick date, otherwise false
     */
    var setLastTickDate = function(tickerId, mdDate) {
        var result = false;
        if (lastTickDate[tickerId] === undefined) {
            lastTickDate[tickerId] = mdDate;
            result = true;
        }
        else {
            if (mdDate > lastTickDate[tickerId]) {
                lastTickDate[tickerId] = mdDate;
                // marketData.lastTickDate = getFormattedDate(mdDate);
                result = true;
            }
        }
        return result;
    };

    /**
     * Performs a check that the market data is newer than the data currently displayed on the page
     * @returns true, if the data is newer than the displayed data, otherwise false
     */
    var containsNewData = function(marketData) {
        var result = false;
        var mdDate;

        if (marketData.lastTickDate) {
            // convert the date string to a real date
            mdDate = new Date(parseInt(marketData.jsLastTickDate, 10));
            // mdDate.setISO8601(marketData.lastTickDate);
            // Determine if the date is newer than the stored date
            result = setLastTickDate(marketData.tickerId, mdDate);
            if (result) {
                // Reformat the date string in the market data for display
                // marketData.lastTickDate = getFormattedDate(mdDate, marketData.timezone);
                marketData.lastTickDate = marketData.lastTickDateString;
                logger.debug("Market data poll response, data as at " + mdDate);
            }
        }

        if (!result) {
            // TODO - send a date to the server for each tickerId, avoid this issue...
            logger.error("poll returned known data - " + mdDate);
        }

        return result;
    };

    /**
     * Returns a jquery query result from the iframe
     * @param selector, the jquery selector
     * @returns jquery result set [array]
     */
    var getElement = function(selector) {
        var frame = window['bodyIFrame'];
        return frame.$(selector);
    };

    /**
     * Repopulates the page with the given market data
     * html elements are identified by the classname containing 3 fields namely [mdSteam md_"MarketDataName" "tickerId"]
     * @param marketData
     */
    var repopulatePage = function(marketData) {
        var item, field, value;
        var tickerId = marketData.tickerId;

        // Process the market data only if it is newer than that currently displayed
        if (containsNewData(marketData)) {
            for (item in marketData) {
                if (marketData[item] !== null) {
                    // Update the html elements on the page that this market data item pertains to
                    value = marketData[item];
                    field = getElement('span.md_' + item + '.mdStream.' + tickerId);
                    updateField(field, value);
                }
            }
        }
    };

    /**
     * Handler for a successful ajax poll response
     */
    var handlePollResults = function(response) {
        var result, i;

        if (checkResultsIntegrity(response)) {
            // The root node of the poll response is "result"
            result = response.result;
            for (i=0; i<result.length; i++) {
                repopulatePage(result[i]);
            }
        }
    };

    /**
     * Starts the poll timer
     */
    var startTimer = function() {
        activeTimer = setTimeout(poll.createDelegate(this), POLL_INTERVAL);
    };

    /**
     * Stops the poll timer
     */
    var stopTimer = function() {
        clearTimeout(activeTimer);
    };

    /**
     * Polls the server for market data for the tickerId list
     */
    var poll = function() {
        if (tickerIdList !== null) {
            ajaxCall(
                handlePollResults.createDelegate(this),
                handleEmptyResultSet.createDelegate(this)
            );
            // Fire off a new timer
            startTimer();
        }
    };

    return {
       /**
        * Sets the list of tickerIds for which market data poll requests should be made.
        * If the list is null, the timer will be reset and no further requests made
        * @param list, array of tickerIds
        */
        setTickerIdList : function(list) {
            tickerIdList = list;
            if (list !== null) {
                startTimer();
            }
            else {
                stopTimer();
            }
        }
    };
}();

/**
 * @class Menu
 * Controller for the top level menu bar
 * @singleton
 */
var Menu = function() {
    var activeTab = null;
    var PRODUCT_PAGES_PREFIX = "/product";
    var INNER_FRAME = 'bodyIFrame';
    var PRODUCT_CATALOGUE_GRID = "/productCatalogueGrid.action";
    var FISHEYE_FIRST_ANCHOR_SELECTOR = '.fisheyeNav p a';
    
    var goto = function(href, frameName) {
        logger.debug('redirecting to ' + href);
        var target = (frameName.length > 0) ? frames[frameName] : document;
        target.location.href = href;
    };

    var getActiveTab = function() {
        if (activeTab == null) {
            setActiveTab($('li[@class=active]')[0]);
        }
        return activeTab;
    };

    var setStyle = function(is3Col) {
        // Style the menu according to the number of columns in the content
        var menuContainer = $('#dvNavigation')[0];
        if (menuContainer) {
            menuContainer.className = (is3Col) ? 'Nav_3col' : 'Nav_2col';
        }
        else {
            logger.error("Menu.setStyle() : #dvNavigation not found");
        }
    };

    var deactiveActive = function() {
        getActiveTab();
        if (activeTab) {
            // depress the tab
            activeTab.className = "";
            // if navigating away from product page(s) - reset the LHS Nav
            if ('/' + $(activeTab).children()[0].pathname == PRODUCT_CATALOGUE_GRID) {
                if (CatalogueNavigation) {
                    CatalogueNavigation.reset(false);
                }
            }
        }
    };

    var setActiveTab = function(li) {
        if (li.tagName.toUpperCase() == "LI") {
            activeTab = li;
            // the "home" tab does not change state // if (activeTab.className != "home")
            activeTab.className = "active";
        }
        else {
            logger.error('Menu.setActiveTab() : unexpected element tag (should be li)');
        }
    };

    var setActive = function(li) {
        try {
            if (li.tagName.toUpperCase() == "A") {
                li = $(li).parent()[0];
            }
            if (getActiveTab() != li) {
                deactiveActive();
            }
            setActiveTab(li);
        }
        catch(e) {
            logger.error('Menu.setActive() : ' + e.message || e);
        }
    };
    
    return {
        /**
         * Style the selection prior to navigating to the href
         */
        select : function(anchor) {
    		var leftContainer=document.getElementById('leftContainer');
    		if(leftContainer) leftContainer.style.visibility= 'visible';
            setActive($(anchor).parent()[0]);
            goto(anchor.href, anchor.target);
            return false;
        },

        setStyleForContent : function(content) {
            // Determine if there the content has a right column
            var is3Cols = $(content).find('#dvRightPanel').length > 0;
            setStyle(is3Cols);
        },

        clear : function() {
            deactiveActive();
        },

        update : function(actionName) {
            var foundTab = false;

            // treat product pages the SAME as the product catalogue grid
            if (actionName.indexOf(PRODUCT_PAGES_PREFIX) > -1) {
                actionName = PRODUCT_CATALOGUE_GRID;
            }
            
            $('#dvNavigation li a').each(function(count, anchor) {
                if (actionName.indexOf(anchor.pathname) > -1) {
                    // found the active tab
                    setActive(anchor);
                    foundTab = true;
                }
            });
            if (!foundTab) {
                deactiveActive();
            }
        }
    }
}();

/**
 * @class SearchController
 * Performs an Ajax Search
 * @singleton
 */

var SearchController = function() {
    // private singleton instances
    var NULL_HREF = "javascript:{}";
    var MIN_NUMBER_CHARS_TO_TRIGGER = 3;
    var DEFAULT_VISIBLE_RESULTS = 5;
    var MIN_POSSIBLE_OVERFLOW_TEXT_LENGTH = 70;
    var FETCH_FURTHER_RESULTS_SIZE = 25;
    var ITEM_HIGHLIGHT_CLASSNAME = 'active';
    // Should legacy results be discarded? ie: if the user has since typed more characters?
    var discardLegacyResults = true;
    var inputMask = "Search";
    var listItemIdPrefix = "searchResultsItem";
    var searchAction = "AjaxSearch.action";
    var viewAllResultsAction = "viewAllSearchResults.action"; 
    var productSelectAction = "productTradingInformationPage.action";
    var documentLibraryAction = "documentLibraryGrid.action";
    var delimiter = "|";
    var lastRequestUrl = "";
    var lastKeyword = "";
    var highlightedItemSequence = -1; // used for keyboard support
    var totalNumberItems = 0;
    var searchBoxHeight = 43;
    // var minHeight = 152;
    var filters = [];
    var originallyVisibleResults = [];
    var query = {};
    var scrollbarVisible = false;
    // Don't display the jScrollPane if firefox. It produces intolerable flicker on this browser.
    // TODO : find solution to the Firefox jScrollPane flicker. It works fine on the homepage.
    var useJScrollPane = (!BrowserDetect.isFirefox());

    var gaTimeoutId = -1;
    /*
     * Browser specific offsets
     */
    var getBrowserTopOffset = function(empty) {
        var  browserOffset = (empty) ? 28 : -13; // default (safari && chrome)
        if (BrowserDetect.isIE()) {
            // ie6 and "no results" special case
            if ((BrowserDetect.version == 6) && empty) {
                browserOffset = 11;
            }
            else {
                // default (ie6 for positive results and all ie7)
                browserOffset = 10;
            }
        }
        else if (BrowserDetect.isFirefox()) {
            browserOffset = (empty) ? 28 : -15;
        }

        return browserOffset;
    };

    var getSearchResultsTopOffset = function(empty) {
        var topOffset = (empty) ? 88 : 48; // default (safari && chrome)
        if (BrowserDetect.isIE()) {
            topOffset = 70; // ie6 & ie7
        }
        if (BrowserDetect.isFirefox()) {
            topOffset = (empty) ? 88 : 46;
        }

        return topOffset;
    }

    var error = function(method, e) {
        logger.error('SearchController.' + method + '() : ' + e.message || e);
    };

    var addFilter = function(fn) {
        filters.push(fn);
    };

    var removeFilter = function(fn) {
        var index = filters.indexOf(fn);
        filters.splice(index, 1);
    };

    var getFilters = function() {
        return "";
        /*
        var output = "";
        $(filters).each(function(count, item) {
            output += (count == 0) ? '&filter=' : delimiter;
            output += item;
        });
        return output; */
    };

    var getPopupResultsContainer = function() {
        return $("#dvResultsContent")[0];
    };

    // Returns true if the src element is or is a child of an element having id = eltId
    var containedIn = function(src, eltId) {
        return ((src.id == eltId) || $(src).isChildOf('#' + eltId));
    };

    var goto = function(href) {
        frames['bodyIFrame'].location.href = href;
    };

    var displayDocument = function(href) {
        window.location.href = href;
    }

    var getInputField = function() {
        return $('#globalSearchField')[0];
    };

    var clearInputField = function() {
        setState('');
        getInputField().value = "";
    };

    var getSearchString = function() {
        return getInputField().value;
    };

    var getSearchResultsContainerElement = function() {
        return $("#dvSearchResultsContainer")[0];
    };

    var getSearchResultsElement = function() {
        return $("#dvSearchResults")[0];
    };

    var setVisibility = function(state) {
        var divSearchResults = getSearchResultsContainerElement();
        if (divSearchResults)
            divSearchResults.style.visibility = state;
    };

    /* Set the style for the search and wrapper elements to cater for the new html block */
    var styleForNewHeight = function(empty) {
        var dvResults = $("#dvResults")[0];
        var dvSearchResults = getSearchResultsElement();
        var dvSearchResultsContainer = getSearchResultsContainerElement();
        var offsetHeight = dvResults.offsetHeight;

        // TODO : refactor this cross browser mess
        if (BrowserDetect.isIE()) {
            dvSearchResultsContainer.style.height = (offsetHeight + 10 + searchBoxHeight) + "px";     // ie maybe
        }
        else { // Non msie browsers
            var offset = 0;
            if (!empty) {
                if (BrowserDetect.isFirefox()) {
                    offset = -43;
                }
                else {
                    // default : safari && chrome
                    offset = -41;
                }
            }
            dvSearchResults.style.height = (offsetHeight + searchBoxHeight + offset) + "px";     // 5
        }

        // Position the top of the search results container
        dvSearchResultsContainer.style.top = (offsetHeight + searchBoxHeight + getSearchResultsTopOffset(empty)) + "px"; // magic number

        // Shift the wrapper element upwards to compensate
        var topOffset = -(offsetHeight + searchBoxHeight + getBrowserTopOffset(empty)) + "px";
        $('#bg2Col').css('marginTop', topOffset);
    };

    // Determine the keyword for which these results relate
    var determineKeywordFromResults = function(html) {
        var start = html.indexOf('<[') + 2;
        var end = html.indexOf(']>');
        return html.substring(start, end);
    };

    var shouldDisplayResults = function(keywordUsed) {
        if (!discardLegacyResults) {
            return (lastKeyword.length >= MIN_NUMBER_CHARS_TO_TRIGGER);
        }
        else return (lastKeyword == keywordUsed)
    };

    var trimResultItem = function(anchor) {
        var isHidden = $(anchor).is(':visible');
        var array, text = $(anchor).text();
        
        if (isHidden) anchor.parentElement.style.display = 'block';
        while (anchor.scrollHeight > 16) {
            text = text.rtrim();            
            logger.debug('Trimming text : [' + text + ']');
            array = text.split(' ');
            array.splice(array.length - 1, 1);
            text = array.join(' ') + '...';
            $(anchor).text(text)
        }
        if (isHidden) anchor.parentElement.style.display = '';
    };

    // If the text in an anchor overflows to 2 rows, trim the text
    // such that if appears in a single row with ellipses.
    var trimResultItems = function(html) {
        var textBlocks = $(html).find('a');
        textBlocks.each(function(count, anchor) {
            if (anchor.innerHTML.length > MIN_POSSIBLE_OVERFLOW_TEXT_LENGTH) {
                trimResultItem(anchor);
            }
        });
    };

    var displayFurtherResults = function(html) {        
        logger.info(html);
        var appendAfter = $('#searchResultsItem' + query.fromSequence)[0];
        var deleteNode = $('#fetchFurther' + query.section)[0];
        deleteNode.parentNode.removeChild(deleteNode);        
        appendAfter.parentNode.innerHTML += html;
        displayScrollBar();
        // Recalculate the number of visible items
        setNumberResultsDisplayed(query.section, getNumberVisibleResults(query.section));
        // hack
    };

    var handleNullfurtherResults = function() {
        alert('all gone wrong!');  
    };

    var displayResultsAsPopup = function(html) {
        try {
            Profiler.start("begin");
            highlightedItemSequence = -1;
            // Determine the keyword for which these results relate
            var keywordUsed = determineKeywordFromResults(html);
            Profiler.checkpoint("determine");
            // ie6 falls over on this selector
            // TODO : perform this count on the server
            // TODO : reinstigate keyboard support
            // totalNumberItems = $(html).find("a").length;
            Profiler.checkpoint("totalNum");
            // logger.debug("Search response with num items = " + totalNumberItems);
            // Only display the results if they are relevant for the text currently in the text field
            if (shouldDisplayResults(keywordUsed)) {
                clearResults();
                $('#dvNoResults').hide();
                $('#dvNoResults').css('display','none');
                Profiler.checkpoint("clear");
                var container = getPopupResultsContainer();
                Profiler.checkpoint('appending');
                container.innerHTML = html;
                $('#dvResults')[0].style.height =  $('#dvResultsContent').css('height');
                Profiler.checkpoint('showing');
                $('#dvResults').show();
                Profiler.report();
                // Perform all trimming on server for performance
                // trimResultItems(container); // trim html
                styleForNewHeight();
                setVisibility("visible");
                setState('closable');
            }
            else {
                logger.info("search results for keyword=" + keywordUsed + ", discarded as active search=" + lastKeyword);
            }
        }
        catch(e) {
            logger.error("SearchController.displayResultsAsPopup : " + e.message);
        }
    };

    var emptyResultSet = function() {
        clearResults();
        $('#dvResults').hide();
        $('#dvNoResults').show();
        styleForNewHeight(true);
        setVisibility("visible");
        setState('closable');
    };

    var setSelected = function(tableRow, filter) {
        if (filters.contains(filter)) {
            removeFilter(filter);
        }
        else {
            addFilter(filter);            
        }
        tableRow.className = (tableRow.className == 'nav_row') ? 'nav_row highlighted' : 'nav_row';
    };

    var getAjaxSearchUrl = function(searchString, params) {
        params = params || {};
        var url = searchAction + '?keyword=' + searchString + getFilters(); // + numResults;
        if (params.fromSequence != undefined) {
            url += '&firstSequence=' + (params.fromSequence + 1);
        }
        if (params.fromGroupSequence != undefined) {
            url += '&firstElement=' + params.fromGroupSequence;
        }
        if (params.section != undefined) {
            url += '&section=' + params.section;
        }

        return url;
    };

    var getListItem = function(index) {
        return $('#' + listItemIdPrefix + index)[0];
    };

    var setHighlightedItem = function(li) {
        clearActiveHighlight();
        highlightedItemSequence = li.id.substring(listItemIdPrefix.length, li.id.length);
        $(li).addClass(ITEM_HIGHLIGHT_CLASSNAME);
    };

    var setState = function(state) {
        $('#dvSearchReset')[0].className = state;

        // ie6 multi class bug workaround (see http://sonspring.com/journal/ie6-multi-class-bug)
        if ((BrowserDetect.isIE()) && (BrowserDetect.version == 6)) {
            if (state == 'searching') {
                $('#dvSearchReset')[0].style.background = "url('/images/search/spinner.gif') no-repeat right";
            }
            else if (state == 'closable') {
                $('#dvSearchReset')[0].style.background = "url('/images/search/close2.gif') no-repeat right";
            }
            else {
                $('#dvSearchReset')[0].style.background = "transparent";
            }
        }
    };

    var ajaxCall = function(url, successDelegate, emptyDelegate) {
        // setState('searching');
        lastRequestUrl = url;
        logger.debug("Search request = " + url);
        Ajax.call(url, successDelegate, emptyDelegate, null);
    };

    /**
     * Performs the ajax search
     */
    var perform = function(searchString) {
        var url = getAjaxSearchUrl(searchString);
        if (searchString != lastKeyword) {
            setState('searching');
            ajaxCall(
                url,
                displayResultsAsPopup.createDelegate(this),
                emptyResultSet.createDelegate(this)
            );
        }
    };

    var fetchFurther = function(section, fromSequence, fromGroupSequence) {
        // var ajaxMethod, url;
        query = {
            fromSequence: fromSequence,
            fromGroupSequence: fromGroupSequence,
            section: section
        };

        ajaxCall(
            getAjaxSearchUrl(lastKeyword, query),
            displayFurtherResults.createDelegate(this),
            handleNullfurtherResults.createDelegate(this)            
        );
    };

    var clearActiveHighlight = function() {
        var item = getListItem(highlightedItemSequence);
        if (item) {
            $(item).removeClass(ITEM_HIGHLIGHT_CLASSNAME);
        }
        highlightedItemSequence = -1;
    };

    var selectUp = function() {
/*
        clearActiveHighlight();
        var a = getListItem(--highlightedItem);
        if (typeof(a) == "undefined") {
            highlightedItem = totalNumberItems;
            selectUp();
        } else {
            a.className = "active";
        } */
    };

    var selectDown = function() {
/*        clearActiveHighlight();
        logger.info("index = " + highlightedItem);
        var a = getListItem(++highlightedItem);
        if (typeof(a) == "undefined") {
            highlightedItem = -1;
            selectDown();
        } else {
            a.className = "active";
        } */
    };

    var reset = function() {
        clearInputField();
        setState('');
    };

    var getNumberHiddenResults = function(section) {
        return $('#searchResultList_' + section).find('li.hidden:not(.fetchFurther)').length;
    };

    var getNumberStoredResults = function(section) {
        return $('#searchResultList_' + section).find('li:not(.fetchFurther)').length;
    };

    var getNumberVisibleResults = function(section) {
        return getNumberStoredResults(section) - getNumberHiddenResults(section);
    };

    var getTotalNumberResults = function(section) {
        var result = 0;
        try {
            result = parseInt(getHeader(section).text().match(/[0-9]+\)/)[0].replace(')',''));
        }
        catch(e) {
            logger.error('getTotalNumberInSection', e);
        }
        return result;
    };

    var getHeader = function(section) {
        return $('#searchResultsTitle_' + section);
    };

    var setNumberResultsDisplayed = function(section, num) {
        var header = getHeader(section);
        var newText = header[0].innerHTML.replace(/\([0-9]+/, '(' + num);
        header.text(newText);        
    };

    var updateNumberResultsDisplayed = function(section, expanding) {
        setNumberResultsDisplayed(section, (expanding) ? getNumberStoredResults(section) : originallyVisibleResults[section]);
    };

    var getVisibleContentHeight = function() {
        var height = 0;

        $('#dvResultsContent li:visible, div.searchHeader').each(function(n, item) {
            height += item.offsetHeight;
            if (item.tagName == 'DIV') {
                height += parseInt($(item).css('marginTop').replace('px',''));
            }
        });
        
        return height;
    };

    // Returns the scroll bar offsets adjusted for each browser
    var getScrollBarOffset = function(display) {
        // ie7 - default
        var result = (display) ? 0 : 12;
        // ie6
        if (BrowserDetect.isIE6()) {
            result = (display) ? -1 : 5 ;
        }
        return result + 'px';
    };

    var displayScrollBar = function() {
        $('#dvResults')[0].style.overflowY = "auto";
        if (useJScrollPane) {
            $('#dvResults').jScrollPane({
                'showArrows': true,
                'scrollbarOnLeft': true
            });
            $('#dvResultsContent').css('marginLeft', getScrollBarOffset(true));
        }
        scrollbarVisible = true;
    };

    var removeScrollBar = function(forceRemove) {
        if (scrollbarVisible) {
            var contentHeight = getVisibleContentHeight();
            var frameHeight = $('#dvResults').parent().attr('offsetHeight'); // $('#dvResults').parent().css('height').replace('px','');
            $('#dvResults')[0].style.overflowY = "hidden";
            // TODO : find solution to the Firefox jScrollPane flicker. It works fine on the homepage.
            if (useJScrollPane) {
                $('#dvResults').jScrollPaneRemove();
            }
            $('#dvResults').css('height', frameHeight + 'px');
            scrollbarVisible = false;
            if ((frameHeight == contentHeight) || forceRemove) {
                if (useJScrollPane) {
                    $('#dvResultsContent').css('marginLeft', getScrollBarOffset(false));
                }
            }
            else {
                displayScrollBar();
            }
        }
    };

    var clearResults = function() {
        $(getPopupResultsContainer()).children().remove();
        removeScrollBar(true);
    };

    var getFaqIdFromLI = function(elt) {
        var anchor, result = '';

        anchor = $(elt).find('a');
        if (anchor.length > 0) {
            result = anchor[0].id.replace('background_','faq_');
        }
        return result;
    };

    var setExpanderStyle = function(div, expanding) {
        // Toggle the +/-
        if (expanding)
            $(div).addClass("expanded");
        else {
            $(div).removeClass("expanded");
        }
    };

    var setScrollbarVisibility = function(expanding) {
        if (useJScrollPane) {
            if (expanding) {
                displayScrollBar();
            }
            else {
                removeScrollBar();
            }
        }
    };

    var handleExpand = function(div, section) {
        try {
            var expanding = (div.className.indexOf("expanded") == -1);
            var divResults = $('#dvResults')[0];

            // Toggle the expander +/- icon
            setExpanderStyle(div, expanding);

            // If not known store the default number of visible results prior to expansion
            if (!originallyVisibleResults[section]) {
                originallyVisibleResults[section] = getNumberVisibleResults(section);
            }

            // Set the height of the results element to it's offset
            divResults.style.height =  divResults.offsetHeight + "px";

            // Show / hide the expanded items
            setSectionExpandedVisibility(section, expanding);

            // Show / hide the scrollbar (if using jScrollPane - otherwise the browser will handle)
            setScrollbarVisibility(expanding);

            // Update the number of results displayed in the title header
            updateNumberResultsDisplayed(section, expanding);
        }
        catch(e) {
            error("expandResults", e);
        }
    };

    var setSectionExpandedVisibility = function(section, show) {
        var removeClass = (show) ? "hidden" : "displayed";
        var displayClass = (show) ? "displayed" : "hidden";         
        var items = $('#searchResultList_' + section + ' li.' + removeClass);

        items.removeClass(removeClass).addClass(displayClass);
    };

    return {
        search : function() {
            var searchString = getSearchString();
            if (searchString.length >= MIN_NUMBER_CHARS_TO_TRIGGER) {
            	if(gaTimeoutId!=-1){
            		clearTimeout(gaTimeoutId);
            	}
            	gaTimeoutId=setTimeout(function(){
            		_gaq.push(["_trackEvent","Search",getSearchString()]);
            		gaTimeoutId=-1;},
            		2000);
            	
                perform(searchString);
            }
            else {
                this.hide();
            }
            lastKeyword = searchString;
            if (searchString == '') {
                setState('');
            }
        },

        fetchFurtherResults : function(section, fromSequence, fromGroupSequence) {
            fetchFurther(section, fromSequence, fromGroupSequence);
        },

        show : function() {
            setVisibility("visible");
            highlightedItemSequence = -1;
        },

        hide : function() {
            setVisibility("hidden");
        },

        selectProduct : function(productId, indexId) {
            this.hide();
            goto(productSelectAction + '?fundId=' + productId + "&indexId=" + indexId);
        },

        selectGlossary : function(elt, term) {
            this.hide();
            goto(documentLibraryAction + '?category=glossary&term=' + escape(term));
        },

        selectETFFAQ : function(elt) {
            // FAQ is the default doc lib page
            this.hide();
            goto(documentLibraryAction + '?faqId=' + getFaqIdFromLI(elt));
        },

        submit : function() {
            // Submit the search form as per the noscript search handler
        },

        handleKeyUp : function(event) {
            // var keyCode = (event) ? event.keyCode : null;
            this.search();
            return true;
        },

        handleKeyDown : function(event) {
            // event = event || window.event;
            var keyCode = (event) ? event.keyCode : null;
            if (keyCode) {
                switch(keyCode) {
                    case 27: // Escape key
                        this.hide();
                        clearInputField();
                        event.cancelBubble = true;
                        break;
                    case 40: // Down arrow
                        selectDown();
                        event.cancelBubble = true;
                        break;
                    case 38: // Up arrow
                        selectUp();
                        event.cancelBubble = true;
                        break;
                    case 13: // Carriage return
                        var anchor = getListItem(highlightedItemSequence);
                        if (anchor) {
                            if (anchor.href != NULL_HREF)
                                displayDocument(anchor.href); // document
                            else
                                $(anchor).trigger('mousedown'); // product
                        }
                        event.cancelBubble = true;
                        break;
                    default:
                }
            }
            return true;
        },

        handleFocus : function() {
            var searchString = getInputField().value;
            if (searchString == inputMask) {
                this.hide();
                clearInputField();
            }
            else if (searchString.length >= MIN_NUMBER_CHARS_TO_TRIGGER) {
                this.show();
            }
        },

        displayAll : function() {
            var searchString = getSearchString();                        
            frames['bodyIFrame'].location.href = viewAllResultsAction + '?keyword=' + searchString;
            this.hide();
        },

        hasClickedOn : function(event) {
            var target = event.srcElement || event.target;
            return (containedIn(target, 'dvSearchResults') || containedIn(target, 'dvSearch'));
        },

        // @deprecated
        selectFilter : function(elt) {},

        mouseOverItem : function(li) {
            setHighlightedItem(li);
        },

        mouseOutItem : function(li) {
            clearActiveHighlight();
        },

        handleResetClick : function(event) {
            reset();
            this.hide();
            event.cancelBubble = true;
            getInputField().focus();
            return false;
        },

        simExpand : function() {
            // simulate click
            this.showNumBack();
            var div = $('div.expander')[$('div.expander').length - 1]; // hack            
            handleExpand(div, "Background");            
        },

        showNumBack : function() {
            logger.debug('num back = ' + $('#searchResultList_Background').find('li.displayed:not(.fetchFurther)').length);  
        },

        expandResults : function(div, section) {
            handleExpand(div, section);
        }
    }
}();


