/* $Revision: 547 $ | $CreationDate: 2007-09-18 14:51:15 +0100 (Tue, 18 Sep 2007) $ | $LastChangedDate: 2010-07-08 11:03:25 +0000 (Thu, 08 Jul 2010) $ | $LastChangedBy: andrewgillard $ */

/**
 * We use a "CBS" "namespace" here to avoid our functions being overwritten by, or
 *  interfering with, other JS libraries that might be loaded.
 *
 */
var CBS = {

	/*
	insertAfter : function(parent, newnode, afternode)
	addClass : function(current, newclass)
	removeClass : function(current, oldclass)
	addEvent : function(obj, eventname, func, name)
	removeEvent : function(obj, eventname, func)
	removeEventByName : function(obj, eventname, name)
	onDOMLoaded : function()
	getEl : function(id)
	getFieldValue : function(field)
	getFieldDefaultValue : function(field)
	getRadioButtonsInGroup : function(name)
	getFirstRadioButtonInGroup : function(name)
	getSelectedRadioButtonValue : function(grpName)
	getAllFormFields : function(form)
	getClientDimensions : function()
	getClientScrollOffset : function()
	getMouseCoords : function(e)
	getBaseDir : function()
	getSkinPath : function()
	getBody : function()
	hideSelects : function()
	showSelects : function()
	cE : function(n)
	cTN : function(t)
	getEventTarget : function(ev)
	makeURL : function()
	getURLSchemeAndHost : function()
	htmlentities : function(s)
	getCookie : function(name)
	setCookie : function(name, value, durationInSecs, path)
	getParentForm : function(el)
	removeChildren : function(node)
	lastIndexOf : function(haystack, needle)
	handlePromptText : function(inputid)
	regexQuote : function(s)
	getRedirPath : function()
	setFocus : function(element)
	formatPrice : function(price)
	*/

	/**
	 * Currently using section(User or Admin).
	 *
	 */
	currentSection : 'user',

	/**
	 * Inserts the supplied "newnode" into the DOM as a child of "parent",
	 *  positioned immediately after "afternode"
	 *
	 */
	insertAfter : function(parent, newnode, afternode) {
		if (afternode.nextSibling)
			parent.insertBefore(newnode, afternode.nextSibling);
		else
			parent.appendChild(newnode);
	},

	/**
	 * Removes all children of the given node.
     * Modified by and for CBS from original by Matt Thommes, with suggestions from "Bob", at
	 * http://matthom.com/archive/2007/05/03/removing-all-child-nodes-from-an-element (retrieved 2009-08-11).
     *
	 * @link http://matthom.com/archive/2007/05/03/removing-all-child-nodes-from-an-element
	 */
	removeAllChildren : function(parent) {
	   if (parent.hasChildNodes()) {
	       while (parent.childNodes.length)
               parent.removeChild(parent.firstChild);
	   }
	},

	/**
	 * Adds a CSS class named "newclass" to the className given by "current",
	 *  and returns the result
	 *
	 */
	addClass : function(current, newclass) {
		//Return it immediately if it already exists
		if (current.indexOf(newclass) > -1)
			return current;

		//Split it into an array, separated by spaces
		var classes = current.split(' ');

		//Add the new class
		classes.push(newclass);

		//Then return the array joined back into a string with spaces
		return classes.join(' ');
	},

	/**
	 * Removes the CSS class name "oldclass" from the className given by "current",
	 *  then returns the result
	 *
	 */
	removeClass : function(current, oldclass) {
		//Split the class list into an array by spaces
		var classes = current.split(' ');
		for (var cls in classes) {
			//Iterate through the array of classes...
			if (classes[cls] == oldclass) {
				//...removing that class if it exists
				classes[cls] = undefined;
			}
		}
		//Then join the array back together and return.
		//We remove double-spaces as "delete" doesn't seem to delete the element entirely
		return classes.join(' ').replace('  ',' ');
	},

	/**
	 * Whether we have yet added our window.onload cleanup event
	 *
	 */
	addedOnloadCleanupEvent : false,

	/**
	 * Attaches function "func" to "obj" to trigger on the "event" event.
	 * All events will be *BUBBLED* (IE-style - "higher" elements first)
	 *
	 * @param object Object to attach the event handler to
	 * @param string Event name (click, mouseover, etc.)
	 * @param function function to call when the event fires
	 * @param string Name of this event (so it can be removed easily later)
	 */
	addEvent : function(obj, eventname, func, name) {
		if (obj == window && eventname == 'domload') {
			CBS.DOMLoadFunctions.push(func);
		} else {
			if (obj.attachEvent) {
				//IE
				obj.attachEvent('on'+eventname, func);
			} else {
				//Non-IE (Firefox, Netscape, W3C standard)
				obj.addEventListener(eventname, func, false);
			}
		}
		if (name) {
			var ref = 'CBSEvent_'+eventname+'_'+name;
			if (!obj['CBSEvents']) obj['CBSEvents'] = new Object();
			obj['CBSEvents'][ref] = func;
		}
		if (!CBS.AddedOnloadCleanupEvent) {
			CBS.AddedOnloadCleanupEvent = true;
			CBS.addEvent(window, 'load', function(){window.setTimeout(function(){window.onload=null;}, 5000);});
		}
	},

	/**
	 * Detaches function "func" from "obj" triggering on the "event" event.
	 *
	 * @param object Object to detach the event handler from
	 * @param string Event name (click, mouseover, etc.)
	 * @param function function to call when the event fires
	 */
	removeEvent : function(obj, eventname, func) {
		if (obj == window && eventname == 'domload') {
			for (var i=0,l=CBS.DOMLoadFunctions.length; i<l; i++) {
				if (CBS.DOMLoadFunctions[i] == func) {
					CBS.DOMLoadFunctions[i] = null;
				}
			}
		} else {
			if (obj.detachEvent) {
				//IE
				obj.detachEvent('on'+eventname, func);
			} else {
				//Non-IE (Firefox, Netscape, W3C standard)
				obj.removeEventListener(eventname, func, false);
			}
		}
	},

	/**
	 * Removes a named event on the given object on the specified event.
	 * The original event must have been defined with a name.
	 *
	 * @param object Object to remove the event from
	 * @param string Event type
	 * @param string Event name to remove
	 */
	removeEventByName : function(obj, eventname, name) {
		var ref = 'CBSEvent_'+eventname+'_'+name;
		if (obj['CBSEvents'] && obj['CBSEvents'][ref]) {
			CBS.removeEvent(obj, eventname, obj['CBSEvents'][ref]);
			obj['CBSEvents'][ref] = null;
		}
	},

	/**
	 * Cross-browser "DOMContentLoaded" event handling.
	 * Modified by and for CBS from original by Dean Edwards, Matthias Miller
	 *  and John Resig at http://dean.edwards.name/weblog/2006/06/again/.
	 * Use the CBS.addEvent() function with window as the obj and 'domload' as the
	 *  eventname to have it executed as soon as the DOM is parsed and rendered
	 *  (this occurs prior to the window's "load" event firing, which is when all
	 *  images [etc.] have loaded).
	 *
	 * @link http://dean.edwards.name/weblog/2006/06/again/
	 */
	DOMLoadFunctions : new Array(),
	onDOMLoaded : function() {
	    // quit if this function has already been called
	    if (arguments.callee.done) return;

	    // flag this function so we don't do the same thing twice
	    arguments.callee.done = true;

	    // kill the timer
	    if (_timer) clearInterval(_timer);

		for (var i=0,l=CBS.DOMLoadFunctions.length;i<l;i++) {
			CBS.DOMLoadFunctions[i]();
		}
	},

	/**
	 * Wrapper for document.getElementById() because, let's face it, that's a monster
	 *  of a function name, and it's used way too often.
	 *
	 * @param string ID of element to return
	 * @return element
	 */
	getEl : function(id) {
		return document.getElementById(id);
	},

	/**
	 * Returns the value of the given field, whether it's an INPUT, SELECT or TEXTAREA.
	 * Note that with multi-SELECTs, this can return an array of selected items.
	 *
	 * @param element|string Object to get value of
	 * @return string|array
	 */
	getFieldValue : function(field) {
		if (typeof field == 'string') {
			field = CBS.getEl(field);
		}
		if (field.tagName == undefined) {
			console.log("getFieldValue - field.tagName is undefined");
			return null;
		}
		switch (field.tagName.toLowerCase()) {
			case 'input':
				switch (field.type) {
					case 'text':
					case 'password':
					case 'submit':
					case 'button':
					case 'file':
					case 'hidden':
					case 'reset':
						return field.value;
					case 'radio':
						return field.checked;
                    case 'checkbox':
                        if (/\[\]$/.test(field.name)) {
                            var checkboxes = CBS.getRadioButtonsInGroup(field.name);
                            for (var i = 0, l = checkboxes.length, v = new Array(); i < l; i++) {
                                if (checkboxes[i].checked)
                                    v.push(checkboxes[i].value);
                            }
                            return v;
                        } else {
                            return field.checked;
                        }
                        
					default:
						return false;
				}
			case 'textarea':
				return field.value;
			case 'select':
				if (field.multiple) {
					for (var i=0, l=field.options.length, r=new Array(); i<l; i++) {
						if (field.options[i].selected) {
							r.push(field.options[i].value);
						}
					}
					return r;
				} else {
					if (field.disabled || field.options.length<1)
						return null;
					return field.options[field.selectedIndex].value;
				}
			default:
				return false;
		}
	},

	/**
	 * Returns the default value of the specified INPUT, SELECT or TEXTAREA.
	 * Note that this can return an array for multi-SELECT fields.
	 *
	 * @param element|string Object to get default value of
	 * @return string|array
	 */
	getFieldDefaultValue : function(field) {
		if (typeof field == 'string') {
			field = CBS.getEl(field);
		}
		if (field.tagName == undefined) {
			console.log("getFieldDefaultValue - field.tagName is undefined");
			return null;
		}
		switch (field.tagName.toLowerCase()) {
			case 'input':
				switch (field.type) {
					case 'text':
					case 'password':
					case 'submit':
					case 'button':
					case 'file':
					case 'hidden':
					case 'reset':
						return field.defaultValue;
					case 'checkbox':
					case 'radio':
						return field.defaultChecked;
					default:
						return false;
				}
			case 'textarea':
				return field.defaultValue;
			case 'select':
				if (field.multiple) {
					for (var i=0, l=field.options.length, r=new Array(); i<l; i++) {
						if (field.options[i].defaultSelected) {
							r.push(field.options[i].value);
						}
					}
					return r;
				} else {
					for (var i=0, l=field.options.length; i<l; i++) {
						if (field.options[i].defaultSelected) {
							return field.options[i].value;
						}
					}
					//If we reach this point, none are explicitly default, so the first is
					return field.options[0].value;
				}
			default:
				return false;
		}
	},

	/**
	 * Returns an Array of all radio buttons with the specified name
	 *
	 * @param string Group name
	 * @return Array
	 */
	getRadioButtonsInGroup : function(name) {
		var btns = document.getElementsByTagName('input');
		for (var i=0, l=btns.length, r=new Array(); i<l; i++) {
			if (btns[i].name == undefined) continue;
			if (btns[i].name != name) continue;
			if (btns[i].type == undefined) continue;
			if (btns[i].type != 'radio' && btns[i].type != 'checkbox') continue;
			r.push(btns[i]);
		}
		return r;
	},

	/**
	 * Returns the first radio button in the group with name 'name'
	 *
	 * @param string Name of the radio button group
	 * @return element
	 */
	getFirstRadioButtonInGroup : function(name) {
		var b = CBS.getRadioButtonsInGroup(name);
		return b[0];
	},

	/**
	 * Returns the value of the selected radio button in the radio button group
	 *  whose name is given by grpName.
	 * Returns NULL if no button in the group is selected.
	 *
	 * @param string Radio button group name
	 * @return string|null
	 */
	getSelectedRadioButtonValue : function(grpName) {
		var btns = CBS.getRadioButtonsInGroup(grpName);
		for (var i=0,l=btns.length; i<l; i++) {
			if (btns[i].checked) return btns[i].value;
		}
		return null;
	},

    /**
     * Trim white spaces.
     *
     */
    trimSpace : function(text){
        return text.replace(/^\s+/g, '').replace(/\s+$/g, '');
    },

    /**
     * Returns the value of the selected radio button in the radio button group
     *  whose name is given by grpName.
     * Returns NULL if no button in the group is selected.
     *
     * @param string Radio button group name
     * @return string|null
     */
    getSelectedRadioButtonValue : function(grpName) {
        var btns = CBS.getRadioButtonsInGroup(grpName);
        for (var i=0,l=btns.length; i<l; i++) {
            if (btns[i].checked) return btns[i].value;
        }
        return null;
    },
    
	createRadioButton : function(name) {
		var rbtn = null;
		try {
			rbtn = CBS.cE('<input type="radio" name="'+name+'" />');
		} catch(err) {
			rbtn = CBS.cE('input');
			rbtn.type  = 'radio';
			rbtn.name  = name;
		}
		return rbtn;
	},

	/**
	 * Returns an array of field elements that are children of the form 'form'
	 *
	 * @param string|element Form to get the children of
	 * @return array
	 */
	getAllFormFields : function(form) {
		if (typeof form == 'string') form = CBS.getEl(form);
		var fields = [];
		for (var f=form.getElementsByTagName('input'),i=0,l=f.length; i<l; i++) {
			fields.push(f[i]);
		}
		for (var f=form.getElementsByTagName('select'),i=0,l=f.length; i<l; i++) {
			fields.push(f[i]);
		}
		for (var f=form.getElementsByTagName('textarea'),i=0,l=f.length; i<l; i++) {
			fields.push(f[i]);
		}
		return fields;
	},

	/**
	 * Returns an Object containing two properties - width and height - which
	 *  contain the dimensions of the client area of the browser.
	 *
	 * @return Object
	 */
	getClientDimensions : function() {
		var x,y;
		if (self.innerHeight) // all except Explorer
		{
			x = self.innerWidth;
			y = self.innerHeight;
		}
		else if (document.documentElement && document.documentElement.clientHeight)
			// Explorer 6 Strict Mode
		{
			x = document.documentElement.clientWidth;
			y = document.documentElement.clientHeight;
		}
		else if (document.body) // other Explorers
		{
			x = document.body.clientWidth;
			y = document.body.clientHeight;
		}
		return {'width':x, 'height':y};
	},

	/**
	 * Returns an Object containing two properties - x and y - which contain
	 *  the browser's current scroll offset
	 *
	 * @return Object
	 */
	getClientScrollOffset : function() {
		var x,y;
		if (self.pageYOffset != undefined || self.pageXOffset != undefined) {
			//all except Explorer
			x = self.pageXOffset;
			y = self.pageYOffset;
		} else if (document.documentElement && document.documentElement.scrollTop) {
			//Explorer 6 Strict
			x = document.documentElement.scrollLeft;
			y = document.documentElement.scrollTop;
		} else if (document.body) {
			//all other Explorers
			x = document.body.scrollLeft;
			y = document.body.scrollTop;
		}
		return {'x':x, 'y':y};
	},

    getDocumentDimensions : function() {
        var child = CBS.getEl('container');
        if (!child) child = document.body.children[0];
        return {'height': child.offsetHeight + child.offsetTop, 'width': child.offsetWidth + child.offsetLeft};
    },
    
    isOperaMobile : function() {
        return /Opera Mobi\/\d+/.exec(window.userAgent);
    },

	/**
	 * Returns an Object containing the x/y coordinates of the mouse cursor
	 *  relative to the document.
	 * http://www.quirksmode.org/js/events_properties.html#position
	 *
	 * @param event Event
	 * @return Object
	 */
	getMouseCoords : function(e) {
		var x = 0;
		var y = 0;
		if (!e) var e = window.event;
		if (e.pageX || e.pageY) {
			x = e.pageX;
			y = e.pageY;
		} else if (e.clientX || e.clientY) {
			x = e.clientX + document.body.scrollLeft
				+ document.documentElement.scrollLeft;
			y = e.clientY + document.body.scrollTop
				+ document.documentElement.scrollTop;
		}
		return {'x':x, 'y':y};
	},

	/**
	 * Returns the base directory (no trailing slash)
	 *
	 * @return string
	 */
	getBaseDir : function() {
		if (CBS_Settings.BaseDir) {
			return CBS_Settings.BaseDir;
		} else {
			//Gonna have to guess at root...
			return '';
		}
	},

	/**
	 * Returns the skin path (no trailing slash)
	 *
	 * @return string
	 */
	getSkinPath : function() {
		if (CBS_Settings.SkinPath) {
			return CBS_Settings.SkinPath;
		} else {
			//Gonna have to guess at root...
			return '';
		}
	},

	/**
	 * Returns the <body> element
	 *
	 * @return element
	 */
	getBody : function() {
		return document.body;
	},

	/**
	 * Hides all <select> elements in Internet Explorer
	 *
	 */
	hideSelects : function() {
		//Only in IE6... (and 7, since document.all is all IE...)
		if (document.all) {
			var els = document.getElementsByTagName('select');
			for (var i=0,l=els.length; i<l; i++) {
				if (els[i].style.visibility != 'hidden') {
					els[i].style.visibility = 'hidden';
					els[i].CBSGUIHidden = true;
				}
			}
		}
	},

	/**
	 * Shows all <select> elements in Internet Explorer that were previously
	 *  hidden by hideSelects()
	 */
	showSelects : function() {
		//Only in IE6... (and 7, since document.all is all IE...)
		if (document.all) {
			var els = document.getElementsByTagName('select');
			for (var i=0,l=els.length; i<l; i++) {
				if (els[i].CBSGUIHidden) {
					els[i].style.visibility = 'visible';
					els[i].CBSGUIHidden = null;
				}
			}
		}
	},

	/**
	 * Wraps CBS.cE()
	 *
	 * @param string n
	 * @return element
	 */
	cE : function(n) {
		return document.createElement(n);
	},

	/**
	 * Wraps document.createTextNode()
	 *
	 * @param string t
	 * @return text node
	 */
	cTN : function(t) {
		return document.createTextNode(t);
	},

    getOuterHTML : function(el) {
        var div = CBS.cE('div');
            div.appendChild(el);
        return div.innerHTML;
    },

	/**
	 * Returns the target for the supplied event, regardless of browser
	 *
	 * @return element
	 */
	getEventTarget : function(ev) {
		return (ev.target)?ev.target:ev.srcElement;
	},

	/**
	 * Returns an URL from all of the supplied parameters (or array of parameters).
	 * If the first argument is boolean and true the returned URL will be absolute.
	 * If the last argument is an Object, it is used as a key:value set of GET vars.
	 *
	 * @return string
	 */
	makeURL : function() {
		var url = '';
		//An array of arguments can be passed instead...
		if (typeof arguments[0] == 'object' && arguments.length == 1) {
			var args = arguments[0];
		} else {
			var args = new Array();
			for (var i=0,l=arguments.length; i<l; i++) {
				args.push(arguments[i]);
			}
		}

		if (typeof args.first() == 'boolean') {
			var abs = args.shift();
			if (abs) {
				url += CBS.getURLSchemeAndHost();
			}
		}

		var Type = CBS_Settings.URLType;

		url += CBS.getBaseDir();

		if (Type != 'seo')
			url += '/index.php';

		var GetVars = new Object();
		if (typeof args.last() == 'object' || args.last() == null)
			GetVars = args.pop();

		if (Type == 'std') {
			var path = args.join('/');
			if (path.length > 0) {
				GetVars['path'] = '/' + path + '/index.html';
			}
		} else {
			url += '/';
			url += args.join('/');
			url += '/index.html';
		}

		if (GetVars != undefined) {
			url += '?';
			for (var key in GetVars) {
				url += key + (GetVars[key] ? '='+escape(GetVars[key]) : '') + '&';
			}
			if (url.substring((url.length-1)) == '&') {
				url = url.substring(0, url.length-1);
			}
			if (url.substring((url.length-1)) == '?') {
				url = url.substring(0, url.length-1);
			}
		}
		url = url.replace(/([^:]|^)\/\//ig, '$1/', url);
		return url;
	},

	/**
	 * Returns a string containing the current URL's scheme and host,
	 *  e.g. "http://www.somewhere.com"
	 *
	 * @param string
	 */
	getURLSchemeAndHost : function() {
		var dl = document.location;
		return dl['protocol'] + '//' + dl['host'];
	},

	/**
	 * An object acting as an associative array of all of the HTML entities
	 *
	 */
	HTMLEntityTable : {
		' ' : '&nbsp;',
		'¡' : '&iexcl;',
		'¢' : '&cent;',
		'£' : '&pound;',
		'¤' : '&curren;',
		'¥' : '&yen;',
		'¦' : '&brvbar;',
		'§' : '&sect;',
		'¨' : '&uml;',
		'©' : '&copy;',
		'ª' : '&ordf;',
		'«' : '&laquo;',
		'¬' : '&not;',
		'­' : '&shy;',
		'®' : '&reg;',
		'¯' : '&macr;',
		'°' : '&deg;',
		'±' : '&plusmn;',
		'²' : '&sup2;',
		'³' : '&sup3;',
		'´' : '&acute;',
		'µ' : '&micro;',
		'¶' : '&para;',
		'·' : '&middot;',
		'¸' : '&cedil;',
		'¹' : '&sup1;',
		'º' : '&ordm;',
		'»' : '&raquo;',
		'¼' : '&frac14;',
		'½' : '&frac12;',
		'¾' : '&frac34;',
		'¿' : '&iquest;',
		'À' : '&Agrave;',
		'Á' : '&Aacute;',
		'Â' : '&Acirc;',
		'Ã' : '&Atilde;',
		'Ä' : '&Auml;',
		'Å' : '&Aring;',
		'Æ' : '&AElig;',
		'Ç' : '&Ccedil;',
		'È' : '&Egrave;',
		'É' : '&Eacute;',
		'Ê' : '&Ecirc;',
		'Ë' : '&Euml;',
		'Ì' : '&Igrave;',
		'Í' : '&Iacute;',
		'Î' : '&Icirc;',
		'Ï' : '&Iuml;',
		'Ð' : '&ETH;',
		'Ñ' : '&Ntilde;',
		'Ò' : '&Ograve;',
		'Ó' : '&Oacute;',
		'Ô' : '&Ocirc;',
		'Õ' : '&Otilde;',
		'Ö' : '&Ouml;',
		'×' : '&times;',
		'Ø' : '&Oslash;',
		'Ù' : '&Ugrave;',
		'Ú' : '&Uacute;',
		'Û' : '&Ucirc;',
		'Ü' : '&Uuml;',
		'Ý' : '&Yacute;',
		'Þ' : '&THORN;',
		'ß' : '&szlig;',
		'à' : '&agrave;',
		'á' : '&aacute;',
		'â' : '&acirc;',
		'ã' : '&atilde;',
		'ä' : '&auml;',
		'å' : '&aring;',
		'æ' : '&aelig;',
		'ç' : '&ccedil;',
		'è' : '&egrave;',
		'é' : '&eacute;',
		'ê' : '&ecirc;',
		'ë' : '&euml;',
		'ì' : '&igrave;',
		'í' : '&iacute;',
		'î' : '&icirc;',
		'ï' : '&iuml;',
		'ð' : '&eth;',
		'ñ' : '&ntilde;',
		'ò' : '&ograve;',
		'ó' : '&oacute;',
		'ô' : '&ocirc;',
		'õ' : '&otilde;',
		'ö' : '&ouml;',
		'÷' : '&divide;',
		'ø' : '&oslash;',
		'ù' : '&ugrave;',
		'ú' : '&uacute;',
		'û' : '&ucirc;',
		'ü' : '&uuml;',
		'ý' : '&yacute;',
		'þ' : '&thorn;',
		'ÿ' : '&yuml;',
		'"' : '&quot;',
		'\'' : '&#39;',
		'<' : '&lt;',
		'>' : '&gt;',
		'&' : '&amp;'
	},

	/**
	 * Converts all the special characters in the string "s" to their appropriate
	 *  HTML entity equivalents
	 *
	 * @param string String to convert
	 * @return string
	 */
	htmlentities : function(s) {
		for (var c in CBS.HTMLEntityTable) {
			s = s.replace(c, CBS.HTMLEntityTable[c], 'g');
		}
		return s;
	},

	/**
	 * Gets the value of the cookie with the specified name
	 *
	 * @param string Cookie name
	 * @return string
	 */
	getCookie : function(name) {
		if (CBS_Settings.CookiePrefix) name = CBS_Settings.CookiePrefix+name;
		var dc = document.cookie;

		//Get the position of the start of this cookie
		var startPos = dc.indexOf(name + "=");
		if (startPos == -1) {
			//Cookie doesn't exist
			return null;
		}

		//Then find an "=" after the start of the cookie that indicates the start
		// of this cookie's value
		var valuePos = dc.indexOf('=', startPos) + 1;

		//And then find the end of the cookie's value (after the start of the value)
		var endPos = dc.indexOf(';', valuePos);
		if (endPos == -1) {
			//No ; found, so assume it goes to the end of the cookie string
			endPos = dc.length;
		}

		//Extract the required part of the cookie string and return it
		return unescape(dc.substring(valuePos, endPos));
	},

	/**
	 * Sets the value of the specified cookie to the specified string, with a duration
	 *  of the specified duration in seconds
	 *
	 * @param string Cookie name
	 * @param string Cookie value
	 * @param integer Number of seconds the cookie will last before expiring
	 * @param string The path the cookie will be available to (default: /)
	 */
	setCookie : function(name, value, durationInSecs, path) {
        var originalValue = CBS.getCookie(name);
        
		if (CBS_Settings.CookiePrefix) name = CBS_Settings.CookiePrefix+name;
		if (!path) path = CBS.getBaseDir();
		if (path == '') path = '/';
		var expiresString = '';
		if (durationInSecs) {
			var expiryDate = new Date();
			expiryDate.setTime(expiryDate.getTime() + (durationInSecs * 1000));
			expiresString = '; expires=' + expiryDate.toGMTString();
		} else {
			//Session cookie
			expiresString = '';
		}
		document.cookie = name + '=' + escape(value) + expiresString + "; path=" + path;
        
        if (originalValue != value)
            CBS.saveCookiesToSession();
	},

	/**
	 * Returns the element of the <form> that the supplied element is a child of
	 *
	 * @param element Form child
	 * @return element
	 */
	getParentForm : function(el) {
		if (typeof el == 'string') el = CBS.getEl(el);
		while(el.parentNode) {
			el = el.parentNode;
			if (el.tagName && el.tagName.toLowerCase() == 'form') {
				return el;
			}
		}
		return false;
	},

	/**
	 * Recurses through the document tree from the supplied node, removing all
	 *  child elements of node, and all sub-children, etc.
	 *
	 * @param element Node to remove children of
	 */
	removeChildren : function(node) {
		if (node && node.childNodes) {
			while(node.firstChild) {
				var cn = node.firstChild;
				CBS.removeChildren(cn);
				node.removeChild(cn);
			}
		}
	},

	/**
	 * Returns the index of the last occurrence of needle in haystack.  Acts as
	 *  the reverse of String.indexOf().
	 *
	 * @param string Haystack
	 * @param string Needle
	 * @return integer
	 */
	lastIndexOf : function(haystack, needle) {
		var latest = -1;
		for (var i=0; i<1000; i++) { //inf loop protection
			var temp = haystack.indexOf(needle, (latest==-1?0:latest+1));
			if (temp == -1) {
				return latest;
			} else {
				latest = temp;
			}
		}
	},

	/**
	 * Adds event to the element with the supplied ID, inputid, so that its
	 *  contents disappear when the user focuses the box, then reappear when the
	 *  element loses focus, so that the value acts as a prompt
	 *
	 * @param string Input element's ID
	 */
	handlePromptText : function(inputid) {
		var el = CBS.getEl(inputid);
		CBS.addEvent(el, 'focus', function(){if(el.value==el.defaultValue){el.value='';}});
		CBS.addEvent(el, 'blur', function(){if(el.value==''){el.value=el.defaultValue;}});
		var frm = CBS.getParentForm(el);
		CBS.addEvent(frm, 'submit', function(){if(el.value==el.defaultValue){el.value='';}});
	},

	/**
	 * Returns the string s with all special regular expression characters escaped
	 *
	 * @param string Input string
	 * @return string
	 */
	regexQuote : function(s) {
		s = s.replace('\\', '\\\\');
		s = s.replace(/([()\[\]*+.{}^$?])/g, '\\$1');
		return s;
	},

	/**
	 * Returns a string containing the path of the currently viewed page
	 *
	 * @return string
	 */
	getRedirPath : function() {
		var dl = document.location;
		return dl.pathname + dl.search + dl.hash;
	},

    /**
    * Adds the specified GET argument name & value to the supplied URL, first
    *  checking whether the URL already contains GET arguments. Note that it
    *  DOES NOT check for the existence of an identically-named GET argument!
    *
    * @param string url
    * @param string argName
    * @param string argValue
    * @return string
    */
    addGetArgToUrl : function(url, argName, argValue) {
        return url + ((url.indexOf('?') >= 0) ? '&' : '?') + argName + '=' + escape(argValue);
    },

	/**
	 * Sets the supplied element to be focused
	 *
	 * @param element|string The element to focus
	 */
	setFocus : function(element) {
		if (typeof element == 'string') {
			element = CBS.getEl(element);
		}
		element.focus();
	},

	/**
	 * Formats the supplied price as per the specifications in the settings
	 *
	 * @param float price
	 * @return string
	 */
	formatPrice : function(price) {
		var Prefix = '';
		if (typeof CBS_Settings.CurrencyPrefix != 'undefined') Prefix = CBS_Settings.CurrencyPrefix;
		var Suffix = '';
		if (typeof CBS_Settings.CurrencySuffix != 'undefined') Suffix = CBS_Settings.CurrencySuffix;
		var DecPlaces = 3;
		if (typeof CBS_Settings.CurrencyDecPlaces != 'undefined') DecPlaces = CBS_Settings.CurrencyDecPlaces;
		var DecPoint = '.';
		if (typeof CBS_Settings.CurrencyDecPoint != 'undefined') DecPoint = CBS_Settings.CurrencyDecPoint;
		var ThousSep = ',';
		if (typeof CBS_Settings.CurrencyThousSep != 'undefined') ThousSep = CBS_Settings.CurrencyThousSep;

		var fPrice = new Number(price);
		price = fPrice.toFixed(DecPlaces);

		if (ThousSep.length) {
			var parts = price.split('.');
			var changed = true;
			while(changed) {
				changed = false;
				var orig = parts[0];
				parts[0] = parts[0].replace(/([0-9])([0-9]{3})(?=,|$)/g, '$1,$2');
				if (orig != parts[0]) changed = true;
			}
			price = parts.join('.');
		}

		if (DecPoint != '.' || ThousSep != ',') {
			//Need to use this slightly roundabout method so we can deal with
			// decimal point being "," and/or thousand separator being "."
			price = price.replace(/\./g, 'DECIMALPOINT');
			price = price.replace(/,/g, 'THOUSANDSEPARATOR');

			price = price.replace(/DECIMALPOINT/g, DecPoint);
			price = price.replace(/THOUSANDSEPARATOR/g, ThousSep);
		}

		return Prefix + price + Suffix;
	},

    /**
     * Prompts the user for a page number to jump to.
     * "url" should contain "page-0" where the page number should be or "%s" or
     *  "%d" as a parameter if we're using a function call
     *
     * @param string URL to go to or function to call
     * @param integer Minimum page number (probably 1)
     * @param integer Maximum page number
     * @param boolean Whether "url" is a function
     */
    jumpToPage : function(url, min, max, isfunctioncall) {
        var handler = function(val) {
            if (val == null) return;
            var success = false;
            if (/[0-9]+/.exec(val)) {
                if (val >= min && val <= max) {
                    success = true;
                    if (isfunctioncall) {
                        eval(url.replace(/%[ds]/, val));
                    } else {
                        document.location = url.replace('page-0', 'page-'+val).replace('pgno=0', 'pgno='+val);
                    }
                } else {
                    CBSGUI.showAlert('The page number you entered is not in the allowed range ('+min+' to '+max+').');
                }
            } else {
                CBSGUI.showAlert('The value you entered is not a valid number.');
            }
            if (!success) {
                CBS.jumpToPage(url, min, max);
            }
        }
        CBSGUI.showPrompt('Enter a page number to jump to (between '+min+' and '+max+'):', handler, 'Jump To Page', null, false, 'Go');
    },

    /**
     * Displays a dialog allowing the user to bookmark the current page.
     * On Internet Explorer and Mozilla browsers it's actually the "Add
     *  Favorite/Bookmark" dialog that's displayed, while on other browsers a
     *  custom alert dialog is displayed asking the user to use ctrl+D or the
     *  menus of their browser.
     *
     */
    bookmarkPage : function() {
        var url = document.location.href, title = document.title;
        try {
            //Internet Explorer
            window.external.addFavorite(url, title);
        } catch(e) {
            try {
                //Mozilla (Firefox etc.)
                window.sidebar.addPanel(title, url, '');
            } catch (ee) {
                CBSGUI.showAlert("You can use either the Ctrl+D key combination or the Add To Bookmarks feature of your browser, if it has one, to bookmark this page.", null, "Add to Bookmarks");
            }
        }
    },

	getOffsets : function(el) {
		if (typeof el == "string") {
			el = CBS.getEl(el);
		}

		var top = 0, left = 0;
		if (document.all) {
			do {
				top += el.offsetTop;
				left += el.offsetLeft;
			} while (el = el.offsetParent);
		} else {
			top = el.offsetTop;
			left = el.offsetLeft;
		}
		return {"top":top, "left":left};
	},

    /**
     * Reloads the CAPTCHA image to get a new code in it
     *
     * @param string HTML ID of the image
     */
    getNewCaptchaImg : function(id) {
        var i = CBS.getEl(id);
        i.src = i.src.replace(/r=.+($|&)/, 'r='+Math.random());
    },
    
    jsonDecode : function(json) {
        var result = null;
        try {
            //Native JSON decoding; faster & safer than eval()
            result = JSON.parse(json);
        } catch (ex) {
            eval("result = "+json+";");
        }
        return result;
    },
    
    saveCookiesToSessionTimer : null,
    
    saveCookiesToSession : function() {
        window.clearTimeout(CBS.saveCookiesToSessionTimer);
        CBS.saveCookiesToSessionTimer = window.setTimeout(doSave, 250);
        
        function doSave() {
            CBSAjax.newRequest('ajax.php?f=save-cookies-to-session', function(){}, false, 'POST');
        }
    },
    
    number_format : function(number, numDecimalPlaces, decimalPointChar, thousandSepChar) {
        if (!number) number = 0;
        if (!numDecimalPlaces) numDecimalPlaces = 2;
        if (!decimalPointChar) decimalPointChar = '.';
        if (!thousandSepChar) thousandSepChar = ',';
        
        var input = new Number(number);
        input = input.toFixed(numDecimalPlaces);
        
        var thousandGroups = [];
        var parts = input.split('.');
        integerPart = parts[0];
        fractionPart = parts[1];
        
        var out;
        if (integerPart.length < 3) {
            out = input;
        } else {
            while (integerPart.length) {
                thousandGroups.unshift(integerPart.substr(integerPart.length-3));
                integerPart = integerPart.substr(0, integerPart.length -3);
            }
            
            out = thousandGroups.join(',');
            
            if (fractionPart) {
                out = out + '.' + fractionPart;
            }
        }
        
        if (decimalPointChar != '.' || thousandSepChar != ',') {
            //Need to use this slightly roundabout method so we can deal with
            // decimal point being "," and/or thousand separator being "."
            out = out.replace(/\./g, 'DECIMALPOINT');
            out = out.replace(/,/g, 'THOUSANDSEPARATOR');

            out = out.replace(/DECIMALPOINT/g, decimalPointChar);
            out = out.replace(/THOUSANDSEPARATOR/g, thousandSepChar);
        }
        
        return out;
    }
};

/**
 * Returns the index of the given value in the Array,
 *  or false if it doesn't exist.
 *
 * @param mixed Value to search for
 * @param boolean Strict search (===, rather than ==)
 * @return integer|boolean
 */
Array.prototype.search = function(v, strict) {
	if (strict == undefined) strict = false;
	for (var i=0, length=this.length; i<length; i++)
		if (strict) {
			if (this[i] === v)
				return i;
		} else {
			if (this[i] == v)
				return i;
		}
	return false;
};

/**
 * Returns an Array of values starting with the supplied value
 *
 * @param mixed Value to search for
 * @param boolean Set to true to make the search case-sensitive
 * @return integer|boolean
 */
Array.prototype.beginswith = function(v, casesensitive) {
	if (casesensitive !== true) casesensitive = false;
	if (!casesensitive) v = v.toLowerCase();
	var vl = v.length;
	var ret = new Array();
	for (var i=0, length=this.length; i<length; i++) {
		if (typeof this[i] != 'string') continue;
		if (casesensitive) {
			if (this[i].substring(0, vl) == v)
				ret.push(this[i]);
		} else {
			if (this[i].substring(0, vl).toLowerCase() == v)
				ret.push(this[i]);
		}
	}
	return ret;
};

/**
 * Returns a copy of the Array, but with duplicate entries removed.
 *
 * @return array
 */
Array.prototype.unique = function() {
	for (var i=0, length=this.length, out=new Array(); i<length; i++)
		if (out.search(this[i]) === false)
			out.push(this[i]);
	return out;
};

/**
 * Returns a boolean indicating if the 'comp' Array contains the same
 *  elements as this one.
 *
 * @param Array Comparison array
 * @return boolean
 */
Array.prototype.equals = function(comp, strict) {
	if (strict == undefined) strict = false;
	var l = comp.length;
	if (l != this.length) return false; //Different lengths, so can't be equal
	for (var i=0; i<l; i++) {
		if (this.search(comp[i], strict) === false)
			return false; //Can't find that element in this array
	}
	return true;
};


/**
 * Cross-browser "DOMContentLoaded" event handling.
 * Modified by and for CBS from original by Dean Edwards, Matthias Miller
 *  and John Resig at http://dean.edwards.name/weblog/2006/06/again/.
 * Use the CBS.addEvent() function with window as the obj and 'domload' as the
 *  eventname to have it executed as soon as the DOM is parsed and rendered
 *  (this occurs prior to the window's "load" event firing, which is when all
 *  images [etc.] have loaded).
 *
 * @link http://dean.edwards.name/weblog/2006/06/again/
 */
/* for Mozilla/Opera9 */
if (document.addEventListener) {
    document.addEventListener("DOMContentLoaded", CBS.onDOMLoaded, false);
}

/* for Internet Explorer */
/*@cc_on @*/
/*@if (@_win32)
    document.write("<script id=__ie_onload defer src=//0><\/script>");
    var script = document.getElementById("__ie_onload");
    script.onreadystatechange = function() {
        if (this.readyState == "complete") {
            CBS.onDOMLoaded(); // call the onload handler
        }
    };
/*@end @*/

/* for Safari */
if (/WebKit/i.test(navigator.userAgent)) { // sniff
    var _timer = setInterval(function() {
        if (/loaded|complete/.test(document.readyState)) {
            CBS.onDOMLoaded(); // call the onload handler
        }
    }, 10);
}

/* for other browsers */
CBS.addEvent(window, 'load', CBS.onDOMLoaded);
/**
 * END Cross-browser "DOMContentLoaded" event handling code.
 */


/**
 * Allow us to use Firebug debugging code while not causing errors in other browsers
 *
 */
if (!("console" in window) || !("firebug" in console)) {
    var names = ["debug", "info", "warn", "error", "assert", "dir", "dirxml",
    "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"];
    if (!("console" in window)) {
        //Chrome supports its own "console.log()" without requiring Firebug, so we
        // don't want to be creating our own console.log() for Chrome - but we do
        // for browsers without a console object at all!
        names.push("log");
        window.console = {};
    }
    for (var i=0,l=names.length; i<l; ++i) window.console[names[i]] = function(){};
}

var usingIe6 = false;
