/*
$Id: m4.common.js,v 1.49 2008-09-26 11:17:17 cde Exp $
*/

/*############################################################################
## OBJECT : m4Common
## Core functions
##############################################################################*/
var m4Common=
{
    Version : "2.2",
 	libPath : "",

	getLibPath : function() 
	{
		var scripts=document.getElementsByTagName("script");
		for(var i=0;i<scripts.length;i++)
		{
			var s=scripts[i];
			if (s.src && s.src.match(/m4\.common\.js(\?.*)?$/))
			{
				m4Common.libPath = s.src.replace(/m4\.common\.js(\?.*)?$/,'');
				break;
			}
		}
		
		//If the path cannot be found, eg. The m4.common.js has been compressed into
		//a bulk js file, then use the default
		if (m4Common.libPath=="")
		{
			m4Common.libPath="js/lib/macro4/";
		}
	}    
}

//Get the library path
m4Common.getLibPath();

/*############################################################################
## OBJECT : m4Browsers
## Browser detection functions
##############################################################################*/
function m4Browsers()
{
    var userAgent =navigator.userAgent.toLowerCase();
    var ieVersion = 0;
    
    this.isIE          = (userAgent.indexOf("msie") != -1);
    
    if (this.isIE)
    {
        ieVersion=Number(userAgent.substr(userAgent.indexOf("msie ")+5,3));
    }
    
    this.isIE55     =(this.isIE && ieVersion==5.5);    
    this.isIE55Up   =(this.isIE && ieVersion>=5.5);    
    this.isIE6      =(this.isIE && ieVersion==6);    
    this.isIE6Up    =(this.isIE && ieVersion>=6);
    this.isIE7      =(this.isIE && ieVersion==7); 
    this.isIE8      =(this.isIE && ieVersion==8);   
    this.isIE7Up    =(this.isIE && ieVersion>=7);
    this.isFF       =(userAgent.indexOf("firefox/")!=-1);
    this.isFF3		=(userAgent.indexOf("firefox/3")!=-1);
    this.isNS       =(userAgent.indexOf("netscape")>=0);
    this.isWinXP    =(userAgent.indexOf("windows nt 5.1")!=-1);
    this.isPreXP    =(userAgent.indexOf("windows")!=-1 && !this.winXP);
}

var browser=new m4Browsers();

/*############################################################################
## OBJECT : querystring
## Querystring functions
##############################################################################*/
var querystring={
    fullQS : "",
    arrQS : new Array(),
    
    initialise :  function ()
    {
        var strQS=document.URL;
    	strQS=strQS.substr(strQS.indexOf("?")+1);
	    this.fullQS=strQS;
    
    	//Split the querystring into its name/value pairs
	    var arrTempQS=strQS.split("&")

	    //For each name/value pair. Add the value to the global array, using its name as the index
	    for (i=0;i<arrTempQS.length;i++){
		    if (arrTempQS[i]!=""){
			    arrValue=arrTempQS[i].split("=");
			    if (!arrValue[1]) arrValue[1]="";
			    this.arrQS[arrValue[0].toLowerCase()]=unescape(arrValue[1]);
		    }
	    }
    },
    
    get : function(strKey)
    {
        return this.arrQS[strKey];
    },
    
    getFullString :  function()
    {
        return this.fullQS;
    },
    
    getAsNumber : function(strKey)
    {
    	var strValue=this.get(strKey);
	    strValue=(!strValue || isNaN(strValue)) ? 0 : Number(strValue);
	    return strValue;
    },
    
    getAsString : function(strKey)
    {
    	var strValue=this.get(strKey);
	    strValue=(!strValue) ? "" : String(strValue);
	    return strValue;
    },
    
    //Crate a new querystring based on an array
    create : function(arrParams)
    {
        var qs="";
    
       for (var key in arrParams) {
	        qs+="&"+key+"="+escape(arrParams[key]);     
	   }
	   
	   if (qs.length>1){
	    qs="?"+qs.substr(1);
	   }
	   
	   return qs;
    }
}

querystring.initialise();


/*############################################################################
## OBJECT : cookie
## Cookie function
##############################################################################*/
var cookie={
    get : function(name)
    {
        var dc=document.cookie;
        var strCookie = document.cookie.toLowerCase();
        var prefix = name + "=";
        var intBegin = strCookie.indexOf("; " + prefix);

        if (intBegin == -1)
        {
        intBegin = strCookie.indexOf(prefix);
        if (intBegin != 0) return null;
        } 
        else 
        intBegin += 2;
        
        var end = document.cookie.indexOf(";", intBegin);
        if (end == -1) end = dc.length;
        name = dc.substring(intBegin + prefix.length, end)
        var arrSpace = name.split("+");
        intBegin = arrSpace.length;
        name = arrSpace[0];
        var index = 1;
        while (index < intBegin)
        {
          name = name + " " + arrSpace[index];
          index = index + 1;
        }
        
        name = unescape(name);
        return name;   
    },
    
    set : function(name, value, path, expires, domain, secure)
    {
        var curCookie = name + "=" + escape(value) +
                        ((expires) ? "; expires=" + expires : "") +
                        ((path) ? "; path=" + path : "") +
                        ((domain) ? "; domain=" + domain : "") +
                        ((secure) ? "; secure" : "");
                        
        document.cookie = curCookie;
    }    
}

/*############################################################################
## OBJECT : m4
## General functions
##############################################################################*/
var m4={
    /*******************************************************************************
    ** check
    **
    ** Sets a checkbox checked
    *******************************************************************************/
    check : function (strControl){
	    var objControl=$(strControl);
	    if (objControl && objControl.disabled==false) objControl.checked=!objControl.checked;
    },
 
     /*******************************************************************************
    ** toBoolean
    **
    ** Convert to a boolean
    *******************************************************************************/   
    toBoolean : function (strValue){
        if (!strValue) return false;
	    if (String(strValue).toLowerCase()=="true") return true;
	    if (String(strValue).toString()=="1") return true;
    	
	    return false;
    },
   
    /******************************************************************************
    ** cloneArrayOfObjects
    **
    ** Clones an array which is built up of objects and returns a new array
    *******************************************************************************/  
    cloneArrayOfObjects : function(arr)
    {
        var newArr=[];
        for (var i=0;i<arr.length;i++)
        {   
            if (typeof(arr[i])=="object")
            {
               newArr[i]=Object.clone(arr[i]); 
            }
            else{
               newArr[i]=arr[i]; 
            }
        }
        
        return newArr;
    },

    /******************************************************************************
    ** purgeArrayOfObjects
    **
    ** Delete an array of objects
    *******************************************************************************/  
    purgeArrayOfObjects : function(arr)
    {
        for (var i=arr.length-1;i>=0;i--)
        {   
            delete arr[i];
            arr.splice(i);       
        }
    },
   
    /******************************************************************************
    ** lcompare
    **
    ** Does a case insenstive comparsion of two values
    *******************************************************************************/
    lcompare : function(strT1,strT2){
	    return (strT1.toLowerCase()==strT2.toLowerCase())
    },
    
    /******************************************************************************
    ** removePX
    **
    ** Removes the px Pixel text from a measurement
    *******************************************************************************/
    removePX : function(strValue){
        if (!strValue) return 0;
        
	    var intPos=strValue.toLowerCase().indexOf("px")
    	
	    if (intPos>=0){
		    strValue=strValue.substr(0,intPos)
	    }

	    return Number(strValue);
    },
    
    /******************************************************************************
    ** yesNoBox
    **
    ** Does a yesno box in IE (Needed by DW, do not remove)
    *******************************************************************************/
    yesNoBox : function(strMessage, strTitle) {
       var conYes=6;
      
       
       if (browser.isIE) {
          if (!strTitle) strTitle="Columbus";
          try{
            retVal = vbConfirmationDialog(strTitle,strMessage,292);
            retVal = (retVal==conYes);
          }
          catch(er){
            alert("Error : Cannot instantiate YesNo Dialog Box\niedialogs.vbs has not been included in this page");
          }
       }
       else {
          retVal = confirm(strMessage);
       }
       
       return retVal;
    },

    /*******************************************************************************
    ** PRIVATE : compareNumbers
    **
    ** Compare function for the array sort
    *******************************************************************************/
    _compareNumbers :  function(a, b) {
	    return a - b
    },
    
    /*******************************************************************************
    ** sortArray
    **
    ** Sort an array ascending
    *******************************************************************************/
    sortArray : function(arrArray,compareFunction){
    	if (!compareFunction) var compareFunction=this._compareNumbers;
    
        arrArray.sort(compareFunction);
        return arrArray;
    },
      
    /*********************************************************************
    ** stopCaching
    **
    ** Creates a unique string to stop caching from occurring
    ************************************************************************/
    stopCaching : function()
    {
        var time=new Date();
	    return String(time.getHours())+String(time.getMinutes())+String(time.getSeconds())+String(time.getFullYear())+String(time.getMonth())+String(time.getDate());
    },

    /**********************************************************************
    ** getServerRoot
    **
    ** Get the root website name
    ************************************************************************/
    getServerRoot : function(){
	    var blnPort=(location.port!="") ? true : false;
	    var strServer=location.protocol+"//"+location.hostname;
	    
	    if (blnPort==true) strServer+=":"+location.port;
	    strServer+="/";
	    
	    return strServer;
    },
    
    
    /******************************************************************************
    ** getWebsiteRoot
    **
    ** Get the root of where the website is running from
    *******************************************************************************/
    getWebsiteRoot : function(){
	    var strRoot=document.location.href;

	    if (strRoot.indexOf("?")>0){
		    strRoot=strRoot.substr(0,strRoot.indexOf("?")-1);
	    }
    	
	    strRoot=strRoot.substr(0,strRoot.lastIndexOf("/"));
    	
	    if (strRoot.charAt(strRoot.length-1)!="/") strRoot+="/";
	    return strRoot;
    },

    
    /*******************************************************************************
    ** RgbToHex
    **
    ** Converts RGB to Hex
    *******************************************************************************/
     RgbToHex : function(str)
    {
	     if (!str) return "";
	     var s = str.substring(0, 1);
	     if (s=='#') return str;	 
    	 
	     s = str.substring(4, str.length - 1).split(",");
	     for (i=0;i<s.length;i++){
		    s[i]=s[i].trim();
	     }
    	 
	     var hexStr = "#";
	     hexStr += (s[0]>0) ? Number(s[0]).toString(16) : "00";
	     hexStr += (s[1]>0) ? Number(s[1]).toString(16) : "00";
	     hexStr += (s[2]>0) ? Number(s[2]).toString(16) : "00";
	     return hexStr;
    },
    
    
    /******************************************************************************
    ** splitRangeString
    **
    ** Converts 1,2-5,10 into 1,2,3,4,5,10
    ******************************************************************************/
    splitRangeString : function(strRange){
	    var arrRows=strRange.split(",");
	    var arrNewRows=new Array();
	    var a;
	    var b;
	    var i;
	    var i2;

	    try{
		    for (i=0;i<arrRows.length;i++){
			    if (arrRows[i].indexOf("-")>0){
				    a=Number(arrRows[i].substr(0,arrRows[i].indexOf("-")));
				    b=Number(arrRows[i].substr(arrRows[i].indexOf("-")+1));
    				
				    //Generate all number in range a-b
				    for (i2=a;i2<=b;i2++){
					    arrNewRows[arrNewRows.length]=i2;
				    }
			    }
			    else{
				    if (isNaN(arrRows[i])==false){
					    arrNewRows[arrNewRows.length]=Number(arrRows[i]);
				    }
				    else{
					    throw "Invalid";
				    }
			    }
    			
			    if (arrNewRows[arrNewRows.length-1]<0) throw "Minus value";
		    }		
	    }
	    catch(er){
		    return null;
	    }
    	
	    return arrNewRows;
    },
    
    
    /*******************************************************************************
    ** removeLastChar
    ** 
    ** Removes the last character from a string. If strChar is specified it will
    ** only remove the last character if it matches strChar
    *******************************************************************************/
    removeLastChar :function (strText,strChar){
	    var removeChar=(!strChar) ? true : (strText.charAt(strText.length-1)==strChar);
    	
	    if (removeChar==true)
		    strText=strText.substr(0,strText.length-1);
    	
	    return strText;
    },
        
    /*******************************************************************************
    ** removeFirstChar
    ** 
    ** Removes the 1st character from a string. If strChar is specified it will
    ** only remove the first character if it matches strChar
    *******************************************************************************/
    removeFirstChar : function(strText,strChar){
	    var removeChar=(!strChar) ? true : (strText.charAt(0)==strChar);
	    if (removeChar==true) strText=strText.substr(1);
	    return strText;
    },
    
    /*******************************************************************************
    ** getWindowWidth
    ** 
    ** Supports IE quirks mode
    *******************************************************************************/
    getWindowWidth : function(){
	    
	    var width=(!browser.isIE) ? window.innerWidth : document.documentElement.clientWidth;
	    
	    //IE Quirks mode support
	    if (browser.isIE && width==0)
	    {
	    	width=document.body.clientWidth;
	    }
	    return width;
	    
    },
    
     /*******************************************************************************
    ** getWindowHeight
    ** 
    ** Supports IE quirks mode
    *******************************************************************************/
    getWindowHeight : function(){
	    var height=(!browser.isIE) ? window.innerHeight : document.documentElement.clientHeight;
	    
	    //IE Quirks mode support
	    if (browser.isIE && height==0)
	    {
	    	height=document.body.clientHeight;
	    }
	    return height;
	    
    },

    getWindowWidthPx : function(){
	    return this.getWindowWidth()+"px"
    },

    getWindowHeightPx : function(){
	    return this.getWindowHeight()+"px"
    },
 	
 	/*******************************************************************************
    ** setCurrentCSS
    ** 
    ** Enable a specific CSS file
    *******************************************************************************/
    setCurrentCSS : function (title) {
        var i, a, main;
        for(i=0; (a = document.getElementsByTagName("link")[i]); i++) {
            if(a.getAttribute("rel").indexOf("style") != -1 && a.getAttribute("title")) 
            {
                a.disabled = true;
                if(a.getAttribute("title") == title) {
                    a.disabled = false;
	            }
            }
        }
    },
    
    /*******************************************************************************
    ** nonNullString
    ** 
    ** If the string is null, return an empty string
    *******************************************************************************/
    nonNullString : function(value)
    {
        return (!value) ? "" : value;
    },
    
    /*******************************************************************************
    ** nonNullInt
    ** 
    ** If the integer is null, return 0
    *******************************************************************************/
    nonNullInt : function(value)
    {
        return (!value) ? 0 : value;
    },
   
    
    /******************************************************************************
    *** focusComponent
    *** Set focus on specified component
    ***
    ******************************************************************************/
     focusComponent : function(component){
        var objComponent=$(component);

        if (objComponent.disabled==false)
        { 
        	try{
            	objComponent.focus();
            	objComponent.select();
            }
            catch(er)
            {
            }
        }
    },
    
    debugWindow : null,
    
    /******************************************************************************
    *** debug
    *** Opens a debug window, and 
    ******************************************************************************/
    debug : function(strText)
    {
          if (this.debugWindow==null){
            this.debugWindow=window.open('',"debug","location=0,status=0,scrollbars=1,resizable=1,width=800,height=600");
            this.debugWindow.document.write("<html>\n");
            this.debugWindow.document.write("<head>\n");
            this.debugWindow.document.write("<title>Columbus OM - IOP Message</title>\n");
            this.debugWindow.document.write("</head>\n");
            this.debugWindow.document.write("<body style=\"font-family: Courier New,Arial; font-size: 12px; background: #ffffff\">\n");
          }
          
          this.debugWindow.document.write(strText+"<br>");
    },
    
    /******************************************************************************
    ** addToDocumentOnClick
    **
    ** Append a function to the document.onclick event
    ******************************************************************************/  
    addToDocumentOnClick : function(func){
		Event.observe(document,"click",func);
	},
	
	/******************************************************************************
    ** applyCSSObject
    **
    ** Apply a CSS array of styles
    ******************************************************************************/  
	applyCSSObject : function(arrStyles)
	{
	
		var css="<style>";
		for (var i=0;i<arrStyles.length;i++)
		{
			var newStyle=arrStyles[i].replaceAll("%lib%/",m4Common.libPath);
			css+=newStyle+"\n";
		}
		css+="</style>";
		css.applyStyles();
		//Apply style doesn't work under NS so do it this way...
		if(browser.isFF)
		{
			var node=document.createElement("div");
			node.innerHTML=css;
			m4.appendToBody(node);
		}
	},
	
	/******************************************************************************
    ** loadCSSFile
    **
    ** Load a css file and apply to the DOM
    ******************************************************************************/ 
	loadCSSFile : function(cssFile)
	{
		var headID = document.getElementsByTagName("head")[0];       
  		var cssNode = document.createElement('link');
  		cssNode.type = 'text/css';
  		cssNode.rel = 'stylesheet';
 		cssNode.href = cssFile;
  		cssNode.media = 'screen';
  		headID.appendChild(cssNode);
	},
	
	/******************************************************************************
    ** loadJavascriptFile
    **
    ** Load a javascript file in and automatically eval
    ******************************************************************************/  
	loadJavascriptFile : function(url)
	{
		if (Ajax){
			new Ajax.Request(url,{method:'get',onComplete:m4._loadJavascriptFileComplete});
		}
		else{
			alert("Ajax support is not loaded. Please add prototype.js");
		}

    },
    
    /******************************************************************************
    ** _loadJavascriptFileComplete
    **
    ** Execute the returned Javascript
    ******************************************************************************/
    _loadJavascriptFileComplete : function(t)
    {
    	try
    	{
    		eval(t.responseText);
    	}
    	catch(er)
    	{	
    		alert("Error evaluating Javascript : "+er.description);
    	}	
    },
    
    /******************************************************************************
    ** convertStringToObjectRef
    **
    ** Converts a string to an an object reference
    ******************************************************************************/
    convertStringToObjectRef : function(s,obj,separator)
    {
    	separator=(separator || ".");
    	
	    if (s.indexOf(separator)>0)
		{
	    	var p=s.split(separator);
	    		
	    	for (var i=0;i<p.length;i++)
	    	{
	    		obj=obj[p[i]];
	    	}
	    }
	    else
	    {
	    	obj=obj[s];
	    }
		
		return obj;
	},
	
	/******************************************************************************
    ** appendToBody
    **
    ** Appends the element to the body tag
    ******************************************************************************/
	appendToBody : function(el)
	{
		var body=document.getElementsByTagName("body")[0];
		if (!body)
		{
			alert("Unable to AppendToBody : Body tag has not been created yet");
			return;
		}
		
		body.appendChild(el);
	},
	
	addBookmark: function(title,url) 
	{
		if (window.sidebar) 
		{ 
			window.sidebar.addPanel(title, url,""); 
		} 
		else if( document.all ) 
		{
			window.external.AddFavorite( url, title);
		} 
		else if( window.opera && window.print ) 
		{
			return true;
		}
	},
	
	createIFrameBacking : function()
	{
        var iframe=document.createElement("iframe");
        iframe.style.position="absolute";
        iframe.style.display="none";
        iframe.frameBorder="0";
        iframe.style.zIndex=1;
		return iframe;
	}
}

/******************************************************************************
** FUNCTION : m4Lang
**
** Get a key from the language file
*******************************************************************************/
var m4PopupBlockerTest={
    _popupBlockerMessage : "A pop-up blocker has been detected.\n\nYou need to configure your pop-up blocker to always allow pop-ups for this site and then restart your browser",

    test : function(strUserMessage)
    {
        if (strUserMessage)
        {
            this._popupBlockerMessage=strUserMessage;
        }
    
        //This setTimeout will trigger any popup blockers
        setTimeout("m4PopupBlockerTest._test()",100);
    },
    
    _test : function ()
    {
	    if (cookie.get("m4popupcheck")!="1"){
	        window.open("m4popuptest.html","popuptest","toolbar=no,Status=off,menubar=no,scroll=no,resizable=no,help=no,height=100px,width=100px,left=5000px,top=5000px");
        }
    },
    
    _returned :  function()
    {
        cookie.set("m4popupcheck","1");
    },
    
    popupsAllowed : function()
    {
        if (cookie.get("m4popupcheck")!="1"){
		    alert(this._popupBlockerMessage);
		    return false;
	    }
	    
	    return true;
    }
}


/******************************************************************************
** FUNCTION : m4Lang
**
** Get a key from the language file
*******************************************************************************/
//Create the global langauge object and load in the correct file
var m4Lang={

    phrases : {},
    language : null,
      
    get : function(strKey,strReplace0,strReplace1)
    {
        //Get the phrase
        try{
            var strPhrase=eval("m4Lang.phrases['"+strKey+"'];");
        }
        catch(er)
        {
            
        }

        //If the phrase cannot be found output the error string
        if (!strPhrase || strPhrase==""){
            strPhrase="** STRING NOT FOUND **";
        }
        
        //If the string contains {0} then replace it with the passed in value
        if (strReplace0 && strPhrase.indexOf("{0}")>=0)
        {
            strPhrase=strPhrase.replace("{0}",strReplace0);
        }
        
        //If the string contains {1} then replace it with the passed in value
        if (strReplace1 && strPhrase.indexOf("{1}")>=0)
        {
            strPhrase=strPhrase.replace("{1}",strReplace1);
        }
        
        return strPhrase;
    },
    
    getUiToolkitLanguage : function()
    {
       try{
          	var strLang=cookie.get("lang");
            if (strLang){
              m4Lang.language=strLang.substring(0,2).toLowerCase();		
            }
            else if (navigator.userLanguage) {
              m4Lang.language=(navigator.userLanguage.substring(0,2).toLowerCase());
            } 
           	else if (navigator.language) {
              m4Lang.language=(navigator.language.substring(0,2).toLowerCase());
           	}
           	else{
              m4Lang.language="en";
           	}
       }
       catch(er)
       {
          m4Lang.language="en";
       }
    },

    /******************************************************************************
    ** FUNCTION : m4LanguageHandler.loadFile
    **
    ** Load the correct language file
    *******************************************************************************/
    loadFile: function(file)
    {
        m4Lang.getUiToolkitLanguage();
        
        var kGuaranteedLanguages="en,fr,de,it";
    		
        //If the language is not one of the defaults then load in english as a default
        //in case the next language doesn't exist.
        if (kGuaranteedLanguages.indexOf(this.language)<0)
        {
        	this.language="en";
        }

		if (!file)
		{
			var url="language/m4."+this.language+".js";
		}
		else{
			var url=file.replace("%1",this.language);
		}

		m4.loadJavascriptFile(m4Common.libPath+url);
    },
    
    addPhrases: function(phrases)
    {
        Object.extend(m4Lang.phrases, phrases);
    }
}

//Load uiToolkit language in
m4Lang.loadFile();

Object.extend(String.prototype, {

/*******************************************************************************
    ** removeLastChar
    ** 
    ** Removes the last character from a string. If strChar is specified it will
    ** only remove the last character if it matches strChar
    *******************************************************************************/
    removeLastChar :function (strChar){
	    var removeChar=(!strChar) ? true : (this.charAt(this.length-1)==strChar);
    	
	    if (removeChar==true)
		    return this.substr(0,this.length-1);
    	
	    return this;
    },
        
    /*******************************************************************************
    ** removeFirstChar
    ** 
    ** Removes the 1st character from a string. If strChar is specified it will
    ** only remove the first character if it matches strChar
    *******************************************************************************/
    removeFirstChar : function(strChar){
	    var removeChar=(!strChar) ? true : (this.charAt(0)==strChar);
	    if (removeChar==true) return this.substr(1);
	    
	    return this;
    },
    
    /******************************************************************************
    ** ltrim
    **
    ** Trim the character char from the left of a string
    *******************************************************************************/
    ltrim : function (){
	        return this.replace(/^\s+/g,"");
	},
	
    /******************************************************************************
    ** rtrim
    **
    ** Trim the character char from the right of a string
    *******************************************************************************/
    rtrim : function (){
	    return this.replace(/\s+$/g,"");
    },
    
    /******************************************************************************
    ** trim
    **
    ** Trim the character char from the left and right of a string
    *******************************************************************************/
    trim : function(){
	   return this.replace(/^\s+|\s+$/g,"");
    },
    
    /******************************************************************************
    *** trimBeforeChar
    *** Trims off the start of the text upto and including the strChar character
    ******************************************************************************/
    trimBeforeChar : function(strChar)
    {
          if (this.indexOf(strChar)>=0){
            return this.substring(this.indexOf(strChar)+1);
          }
          
          return this;
    },

    /******************************************************************************
    *** trimAfterChar
    *** Trims off the end of the text after and including the strChar character
    ******************************************************************************/
    trimAfterChar : function(strChar)
    {
          if (this.indexOf(strChar)>=0){
            return this.substring(0,this.indexOf(strChar));
          }
          
          return this;
    },
    
    /******************************************************************************
    *** replaceAll
    *** Replace's all occurences of a string
    *******************************************************************************/
    replaceAll : function(strMatch,strReplace)
    {    
        var re =new RegExp(strMatch,"g");
        return this.replace(re,strReplace);
    },
    
    /******************************************************************************
    *** extractStyles
    *** Extract CSS style tag from a string
    *******************************************************************************/
	extractStyles: function(blnKeepTag) {
		return this.extractTag("style",blnKeepTag);
	},
  
   	/******************************************************************************
    *** stripStyles
    *** Remove CSS style tag from a string
    *******************************************************************************/
  	stripStyles: function() {
    	return this.stripTag("style");
	},
	
	/******************************************************************************
    *** extractTag
    *** Extract a tag from a string. Excepts a tag Name
    *******************************************************************************/
	extractTag: function(tag,blnKeepTag) 
	{
		var fragment="<"+tag+"[^>]*>([\u0001-\uFFFF]*?)</"+tag+">";
		var matchAll = new RegExp(fragment, 'img');
	    var matchOne = new RegExp(fragment, 'im');

	    return (this.match(matchAll) || []).map(function(styleTag) {
			if (!blnKeepTag)
			{
				return (styleTag.match(matchOne) || ['', ''])[1];
			}
			else
			{
				return (styleTag.match(matchOne));
			}
	    });
	},
	
  	/******************************************************************************
    *** stripTag
    *** Remove a tag from a string. Excepts a tag Name
    *******************************************************************************/
  	stripTag: function(tag) {
  		var fragment="<"+tag+"[^>]*>([\u0001-\uFFFF]*?)</"+tag+">";
    	return this.replace(new RegExp(fragment, 'img'), '');
	},
	
	/******************************************************************************
    *** extractXml
    *** Extract xml tags from a string & return an array of them
    *******************************************************************************/
	extractXml : function()
	{
		var fragment="<xml[^>]*>([\u0001-\uFFFF]*?)</xml>";
		var matchAll = new RegExp(fragment, 'img');
		var matchOne = new RegExp(fragment, 'im');
	    var matchId=new RegExp('<xml[^>].*id=(\"|\').*(\"|\').*>', 'im');
	    
	    var arrXml=(this.match(matchAll) || []);
		var xml=[];
		
		for (var i=0;i<arrXml.length;i++)
		{
			var id=(arrXml[i].match(matchId))[0];
			var id=id.replaceAll("'","\"");
			var start=id.indexOf("id=\"")+4;
			var end=id.indexOf("\"",start)-start;
			id=id.substr(start,end);
			var sXml=arrXml[i].match(matchOne)[1];
			
			xml[id]=sXml.toXML();
		}
		
		return xml;
	},
	
	/******************************************************************************
    *** toXML
    *** Convert a string to xml dom
    *******************************************************************************/
	toXML : function()
	{
        var objXml;
        
        if (browser.isIE){
           objXml = new ActiveXObject("Microsoft.XMLDOM")
           objXml.async = false;
           objXml.loadXML(this);
        }
        else
        {
            var vParser = new DOMParser();
            objXml = vParser.parseFromString(this, "text/xml");
        }
        
        return objXml;
	},
	
	
	/******************************************************************************
    *** applyStyles
    *** Apply any styles in a string to the browser (IE only)
    *******************************************************************************/
  	applyStyles : function() {
    	if (Prototype.Browser.Gecko) return;

	    var headEl = null;
	
	    // find all styles in the string
	    var styles=this.extractStyles();
	    
	    // add all found style blocks to the HEAD element.
	    for (i = 0; i < styles.length; i++) {
	        if (!headEl)
	        {  
	            headEl = document.getElementsByTagName('head')[0];
	            if (!headEl)
	            {  
	                return;
	            }
	        }
	        var newStyleEl = document.createElement('style');
	        newStyleEl.type = 'text/css';
	        if (Prototype.Browser.IE)
	        {  
	            newStyleEl.styleSheet.cssText = styles[i];
	        }
	        else
	        {  
	            var cssDefinitionsEl = document.createTextNode(styles[i]);
	            newStyleEl.appendChild(cssDefinitionsEl);
	        }
	        headEl.appendChild(newStyleEl);
	    }
  	}
});

Object.extend(Element, {
   
    /*****************************************************************************
    *** showInline
    *** Show a DOM element inline
    ***
    ******************************************************************************/
    showInline: function() {
        for (var i = 0; i < arguments.length; i++) {
          var element = $(arguments[i]);
          element.style.display = "block" ;
        }
    },
   
    /******************************************************************************
    *** toggleInline
    *** Toggle the visible state of the image. (Show inline)
    ******************************************************************************/
    toggleInline: function(element) {
        element = $(element);
        Element[Element.visible(element) ? 'hide' : 'showInline'](element);
        return element;
    },
      
    /******************************************************************************
    *** getBackgroundImage
    *** Gets an elements background-image and removes the url() part
    ******************************************************************************/
    getBackgroundImage : function(el)
    {
        var strImg=Element.getStyle(el,"background-image");
        strImg=strImg.trimBeforeChar("(");
        strImg=strImg.trimAfterChar(")");
        
        if (strImg.charAt(0)=="\"") strImg=strImg.substr(1);
        if (strImg.charAt(strImg.length-1)=="\"") strImg=strImg.substr(0,strImg.length-1);
        
        return strImg;
    },
      
    /*****************************************************************************
    *** getText
    *** Gets the text from within a tag eg. <tag><tag1>hello</tag1></tag>
    *** will return "hello"
    ******************************************************************************/
    getText : function(element){
       if (browser.isIE)
       {
        return element.innerText.trim();
       }

       return element.textContent.trim();
    },
    
    /*****************************************************************************
    *** replaceClassName
    *** Replace a classname in a list of classes
    ******************************************************************************/
    replaceClassName: function(element, classNameToRemove, classNameToAdd)
    {
        element = $(element);
        element.className=m4CSS.replaceClassName(element.className,classNameToRemove,classNameToAdd);
    },
    
    /******************************************************************************
    *** displayAt
    *** Displays an element, at the x,y position specified. Optional width & height
    *** can be set
    ******************************************************************************/
    displayAt : function(element, intLeft,intTop, intWidth, intHeight)
    {
        element=$(element);
        
        element.style.top=   intTop+"px";
	    element.style.left=  intLeft+"px";
    	
	    if (intWidth) element.style.width=intWidth+"px";
	    if (intHeight) element.style.height=intHeight+"px";
	    //setTimeout out fixes display delay in IE6, but causes flicker in IE7
	    if (browser.isIE6)
	    {
	    	setTimeout(function(){Element.showInline(element)},0);
	   	}
	   	else
	   	{
	   		Element.showInline(element);
	   	}
    },
    
    /******************************************************************************
    *** displayAtScreenCenter
    *** Displays an element, at the x,y position specified. Optional width & height
    *** can be set
    ******************************************************************************/
    displayAtScreenCenter : function(element)
    {
        element=$(element);
        
        //Get the element width and height        
        var intElWidth=element.getWidth();
        var intElHeight=element.getHeight();
        
        var intLeft=(m4.getWindowWidth()/2)-(intElWidth/2);
        var intTop=(m4.getWindowHeight()/2)-(intElHeight/2)-50;

        element.style.top=   intTop+"px";
	    element.style.left=  intLeft+"px";
    	
	    element.style.display="block"
    },
    
    
    /******************************************************************************
    *** findParentElementByClass
    *** Find an elements parent which has the specified class name
    *******************************************************************************/  
    findParentByClass : function(element,strClass)
    {
        var objEl=element.parentNode;

	    while(objEl && !Element.hasClassName(objEl,strClass))
	    {
	        
	        objEl=objEl.parentNode;
	    }
        
        //Check if the parent has been found
        if (!objEl){
            return null;
        }
        
        return objEl.id;
    },
    

    /*******************************************************************************
    ** getRealYpos
    **
    ** Gets the Y page co-ordinate of any element
    *******************************************************************************/
    getRealYpos : function(el,blnProcessScrollable)
    {
        if (!blnProcessScrollable) blnProcessScrollable=false;
    
        el=$(el);
        
        var nTopPos = el.offsetTop;            
        var eParElement = (browser.isIE) ? el.offsetParent : el.parentNode;   
        while (eParElement != null)
        {                                 
            nTopPos += eParElement.offsetTop;       

            if (blnProcessScrollable && eParElement.scrollTop>0){
                nTopPos=nTopPos-eParElement.scrollTop;
            }

            eParElement = eParElement.offsetParent; 
        }
        if (browser.isIE) nTopPos=nTopPos+2;
        
        return nTopPos;                             
    },
    
    /*******************************************************************************
    ** getRealXpos
    **
    ** Gets the X page co-ordinate of any element
    *******************************************************************************/
    getRealXpos : function(el)
    {
        el=$(el);
        
        var nLeftPos = el.offsetLeft;          
        var eParElement = el.offsetParent;     
        while (eParElement != null)
        {                                            
            nLeftPos += eParElement.offsetLeft;      
            eParElement = eParElement.offsetParent;  
        }
        
        return nLeftPos;                             
    },
    
    /******************************************************************************
    *** isType
    *** 
    *** Checks if object is a form
    ******************************************************************************/
    isType : function(el,type)
    {
	    return (el!=null && el.tagName.toLowerCase()==type.toLowerCase()) ? true : false;
    },
    
    /******************************************************************************
    *** preventSelection
    *** 
    *** Stop text drag selection in the browser on the object
    ******************************************************************************/
    preventSelection : function(el,eventCache)
    {
       var eventType=(browser.isIE) ? "selectstart" : "mousedown";
       
       if (eventCache==null) eventCache=Event;
   
       eventCache.observe(el,eventType,function(e){Event.stop(e);return false;});
    },
    
    /******************************************************************************
    *** purge
    *** 
    *** Removes functions from an element, and childnodes so that any memory can
    *** be freed up. This is essential for stopping memory leaks.
    ******************************************************************************/
    purge :  function(el) {
        if (!browser.isIE) return;
        
        var a = el.attributes;
        var i;
        var l;
        var n;
        
        if (a) {
            l = a.length;
            for (i = 0; i < l; i += 1) {
                n = a[i].name;
                if (typeof el[n] === 'function') {
                    el[n] = null;
                }
            }
        }
        a = el.childNodes;
        if (a) {
            l = a.length;
            for (i = 0; i < l; i += 1) {
                this.purge(el.childNodes[i]);
            }
        }
    },
    
    /******************************************************************************
    *** purgeChildren
    *** 
    *** Removes functions from the elements childnodes so that any memory can
    *** be freed up. This is essential for stopping memory leaks.
    ******************************************************************************/
    purgeChildren :  function(el) 
    {
        if (!browser.isIE) return;
    
        for(var i=el.children.length-1;i>=0;i--)
        {
            Element.purge(el.children.item(i));
        }  
    }, 
    
    /******************************************************************************
    *** setInnerHtml
    *** 
    *** Set the innerHtml without leaking any memory in IE
    ******************************************************************************/
    setInnerHtml : function (el,html)
    {
        el=$(el);
        Element.purgeChildren(el);
        el.innerHTML=html;
    },
    
    /******************************************************************************
    ** METHOD : fireElementEvent
    ** On change 
    ******************************************************************************/      
    fireElementEvent : function(el,eventStr)
    {
    	el=$(el);
		//Fire event on the dependant so that chains of dependancies will work
   		if (browser.isIE)
   		{
   			el.fireEvent("on"+eventStr);
   		}
   		else
   		{
			var myEvent = window.document.createEvent("Event"); 
        	myEvent.initEvent(eventStr, false, true); 
        	el.dispatchEvent(myEvent);
   		}
    },
    
    /******************************************************************************
    ** METHOD : destroy
    ** Safely remove element without leaking any memory
    ******************************************************************************/    
    destroy : function(element)
    {
        element=$(element);
        
        if (!element) return;
        
        if (browser.isIE)
        {
            var garbageBin = document.getElementById('IELeakGarbageBin');
            if (!garbageBin)
            {
                garbageBin = document.createElement('DIV');
                garbageBin.id = 'IELeakGarbageBin';
                garbageBin.style.display = 'none';
                document.body.appendChild(garbageBin);
            }

            // move the element to the garbage bin
            garbageBin.appendChild(element);
            garbageBin.innerHTML = '';
        }
        else
        {
            element.parentNode.removeChild(element);
        }
    }
});


var m4Guid={
	counter : 0,
	
	create : function()
	{
 		var time=new Date();
	    s=String(time.getHours())+String(time.getMinutes())+String(time.getSeconds())+String(time.getFullYear())+String(time.getMonth())+String(time.getDate());
		return s+this.counter++;	
	}
},

m4CSS={
    /*****************************************************************************
    *** hasClassName
    *** Checks if a string of classes contains a className
    ******************************************************************************/
    hasClassName: function(strFullClass, className) {
        return strFullClass.split(/\s+/).include(className);
    },
    
    /*****************************************************************************
    *** addClassName
    *** Adds a class to the string list of classes
    ******************************************************************************/
    addClassName: function(strFullClass, className) {
    
        return strFullClass.split(/\s+/).concat(className).join(' ');
    },

    /*****************************************************************************
    *** removeClassName
    *** Remove class from string list of classes
    ******************************************************************************/
    removeClassName: function(strFullClass, classNameToRemove) 
    {
      return strFullClass.split(/\s+/).select(function(className) {
            return className != classNameToRemove;
        }).join(' ');       
    },
    
    /*****************************************************************************
    *** replaceClassName
    *** Replace a classname in a string list of classes
    ******************************************************************************/
    replaceClassName: function(strFullClass, classNameToRemove, classNameToAdd)
    {
        var arrClass=strFullClass.split(/\s+/);
        
        for (i=0;i<arrClass.length;i++)
        {
            if (arrClass[i]==classNameToRemove){
                arrClass[i]=classNameToAdd;
                break;
            }
        }
        
        return arrClass.join(" ");
    }
}

/*****************************************************************************
*** $css1
*** Wraps up $css calls so that it doesn't crash if no element is found,
*** returns the first element
******************************************************************************/
$css1=function(cssSelector,startElement)
{
    var arrElements=$css(cssSelector,startElement);
    
    if (arrElements.length>0)
    {
        return arrElements[0];
    }

    return null;
}

/*****************************************************************************
*** $css1Profiler
*** Time how long a css call takes
******************************************************************************/
$cssProfiler=function(cssSelector,startElement)
{
	var dt=new Date();

	for(var i=0;i<100;i++)
	{
    	var el=$css(cssSelector,startElement);
    }
    
    alert(new Date-dt);
}

/*****************************************************************************
*** $css
*** Time how long a css call takes
******************************************************************************/
function $css(selector,startId) {

    if (typeof(selector)=="string")
    {
        selector=[selector];
    }
    
    startId=$(startId);

    if (!startId) startId=document;

    return Selector.findChildElements(startId, $A(selector));
}


/*****************************************************************************
*** Object.purge
*** Purge all properties in an object to stop memory leaks
******************************************************************************/
 Object.extend(Object, {
    purge : function(obj)
    {
        for (property in obj)
        {
        	obj[property]=null;
        }
    }
 });
 
/*****************************************************************************
*** Array.purge
*** Purge an array of objects
******************************************************************************/
 Object.extend(Array.prototype, {
    purge : function()
    {
        for (var i=0;i<this.length;i++)
        {
            Object.purge(this[i]);
            this[i]=null;
        }
    }
 });
 

/*****************************************************************************
*** Ajax.abort
*** Abort the ajax request
******************************************************************************/
Ajax.Request.prototype.abort = function() 
{
	try{
		(this.options.onAbort || Prototype.emptyFunction)(this.transport, this.json);
    	//trash existiting handlers
	    this.options={};
	    //now abort the request
	    this.transport.abort();
	}
	catch(er)
	{
	
	}
}; 
 
//Add an id to prototype orhpan div to identify it in Sieve
String.prototype.escapeHTML.div.id="prototypediv";
//Create a DIV container for Ajax updater to use to stop leakage
function createAnonDiv()
{
	var anonDiv=document.createElement("div");
	anonDiv.id="prototypeAnon";
	anonDiv.style.display="none";
	m4.appendToBody(anonDiv);
}

Event.observe(window,"load",createAnonDiv); 
 