Cookie.del1 = "$&$";
Cookie.del2 = "$:$";
Cookie.del3 = "$#$";

/*	When you create an instance of the Cookie class, you do not automatically create a cookie on the web page.
	To do that you must call the save method or the saveSingleValue method.
	Setting
	document.cookie = "test; path=<some-path>";
	does not generate a cookie with the name 'test' and no value, but a	cookie with no name (the empty string)
	and the value 'test'.	*/
function Cookie(name, expiryDate, path, domain, secure, win) {
	if (typeof name != "string" && expiryDate == null && typeof path != "string" && typeof domain != "string" && typeof secure != "boolean" && win == null) {
		return;
	}
	this.name = "anonymousCookie"; // it is strongly discouraged to use the empty string for the name! Opera does not create a cookie with the empty string as the name.
	if (typeof name == "string") {
		var found = new RegExp("[;=]", "g").exec(name);
		if (found) {
			alert('Cookie.js:\nYou cannot create an instance of the Cookie class with the character \'' + found[0] + '\' in the name!');
		} else {
			this.name = name;
		}
	}
	// the win property gives you the possibility to create and read cookies in another window/frame.
	this.win = (win != null && win.document != null) ? win : window;
	this.expiryDate = (expiryDate != null && expiryDate.constructor == Date) ? expiryDate : null;
	this.maxAge = NaN;
	if (this.expiryDate != null) {
		var ms = this.expiryDate.getTime() - new Date().getTime();
		if (ms > 0) {
			this.maxAge = parseInt(ms / 1000, 10);
		}
	}
/*	By default the cookie is only accessible to pages in the same directory as the
	page that created it and any subdirectories of that directory.
	This means that the path by default is the pathname of the location object
	except for a possible filename. */
	this.path = (typeof path == "string" && path != "") ? path : null;
/*	By default the domain is the hostname property of the location object of the window for the cookie instance.
	The browser automatically prevents you from setting the domain to a domain that is not a parent domain of
	the hosting webserver. */
	this.domain = (typeof domain == "string" && domain != "") ? domain : null;
/*	If secure the cookie is only visible if the page is visited using a secure protocol like https.
	Note that you can always create a secure cookie even though the page is not using a secure connection!	*/
	this.secure = (typeof secure == "boolean") ? secure : null;
	this.del1 = (typeof Cookie.del1 == "string" && Cookie.del1 != "") ? Cookie.del1 : "$&$";
	this.del2 = (typeof Cookie.del2 == "string" && Cookie.del2 != "") ? Cookie.del2 : "$:$";
	this.del3 = (typeof Cookie.del3 == "string" && Cookie.del3 != "") ? Cookie.del3 : "$#$";
	this.preSyl = "$Cookie$";
}

Cookie.removeAll = function(win, path, domain, excludes) {
	function isExcluded(name) {
		if (excludes != null && excludes.constructor == Array && typeof name == "string" && name != "") {
			var exclude;
			for (var j = 0; j < excludes.length; j++) {
				exclude = excludes[j];
				if (typeof exclude == "string" && exclude == name) {
					return true;
				}
			}
		}
		return false;
	}
	if (win == null || win.document == null) {
		win = window;
	}
	var cookies = win.document.cookie.split("; ");
	var arr, cookie;
	for (var i = 0; i < cookies.length; i++) {
		arr = cookies[i].split("=");
		if (arr.length == 1) {
			arr.unshift(""); // remove cookies with no name
		}
		if (arr.length > 1) {
			if (!isExcluded(arr[0])) {
				// no need for setting secure - if this connection is secure the cookie will be removed (secure or not)
				cookie = new Cookie(arr[0], null, path, domain, null, win);
				cookie.remove();
			}
		}
	}
}

Cookie.readCookie = function(name, win) {
	if (win == null || win.document == null) {
		win = window;
	}
	if (typeof name == "string" && win.document.cookie != "") {
		// all browsers automatically puts a space after the semicolon
		var cookieArray = win.document.cookie.split("; ");
		var arr, j, sep, value = "";
		for (var i = 0; i < cookieArray.length; i++) {
			arr = cookieArray[i].split("=");
			// some forget to encode the value and just uses the equal sign (=) in the value :-/
			if (arr.length > 1 && arr[0] == name) {
				for (j = 1; j < arr.length; j++) {
					sep = (j > 1) ? "=" : "";
					value += sep + arr[j];
				}
				break;
			}
		}
		return decodeURIComponent(value);
	}
	return null;
}

/*	The default path is the pathname of the location object of the window
	object for the cookie except for a possible filename. */
Cookie.getDefaultPath = function(win) {
	if (win == null || win.document == null) {
		win = window;
	}
	var defaultPath = win.location.pathname;
	/* remove possible trailing slash or possible filename	*/
	var dotIndex = defaultPath.lastIndexOf(".");
	// if there is a dot assume the last dot is part of a filename in the pathname
	if (dotIndex > -1) {
		defaultPath = defaultPath.substring(0, dotIndex);
		var slashIndex = defaultPath.lastIndexOf("/");
		if (slashIndex > -1 && slashIndex < dotIndex) {
			defaultPath = defaultPath.substring(0, slashIndex + 1);
		}
	}
	return defaultPath;
}

/*	Method to ensure a visible path for the cookie.
	The default path is the pathname of the location object of the window
	object for the cookie except for the last slash and the possible filename. */
Cookie.isVisiblePath = function(path, win) {
	if (win == null || win.document == null) {
		win = window;
	}
	/*	Setting the path to the empty string makes no sense. Then no pages on your web server is
	 *	associated with your cookie. Make sure you don't make the cookie inaccessible to the page
	 *	- path must be contained in the beginning of defaultPath.	 */
	if (typeof path == "string" && path != "") {
		var defaultPath = Cookie.getDefaultPath(win);
		return (defaultPath.toLowerCase().indexOf(path.toLowerCase()) == 0);
	}
	return false;
}

Cookie.getDefaultDomain = function(win) {
	if (win == null || win.document == null) {
		win = window;
	}	
	return win.document.domain; // the default value of document.domain is location.hostname
}

/*	Method to ensure a valid domain for the cookie.
	The domain must be contained in the end of location.hostname.
	If the hostname property does not contain at least two dots the domain cannot be changed.	*/
Cookie.isValidDomain = function(domain, win) {
	if (win == null || win.document == null) {
		win = window;
	}
	var defaultDomain = Cookie.getDefaultDomain(win);
	var hostArray = defaultDomain.split(".");
	if (hostArray.length < 3) {
		return false;
	}
	if (typeof domain != "string" || domain == "") {
		return false;
	}
	var domArray = domain.split(".");
	if (domArray.length < 2 || domArray.length > hostArray.length) {
		return false;
	}
	for (var i = domArray.length - 1; i >= 0; i--) {
		// the new domain may start with a dot in which case domArray[i] is the empty string
		if (domArray[i] != "" && domArray[i] != hostArray[i + hostArray.length - domArray.length]) {
			return false;
		}
	}
	return true;
}

Cookie.prototype.toString = function() {
	var s = "[object Cookie]: " + this.name;
	s += "\nsecure: " + this.getSecure();
	s += "\ndomain: " + this.getDomain();
	s += "\npath: " + this.getPath();
	s += "\nexpires: " + ((this.getExpiryDate() != null) ? this.getExpiryDate() : "at end of session");
	return s;
}

Cookie.prototype.save = function(preserveOtherParameters) {
	if (typeof preserveOtherParameters != "boolean") {
		preserveOtherParameters = true;
	}
	var cookieValue = "";
	if (preserveOtherParameters) {
		var value = this.getValue();
		if (value != null) {
			var valuesArray = value.split(this.del1);
			var arr;
			for (var i = 0; i < valuesArray.length; i++) {
				arr = valuesArray[i].split(this.del2);
				if (arr.length == 2) {
					if (this[this.preSyl + arr[0]] == null) {
						cookieValue += arr[0] + this.del2 + arr[1];
					}
				}
			}
		}
	}
	for (var p in this)	{
		if (p.indexOf(this.preSyl) == 0 && typeof this[p] == "string") {
			if (cookieValue != "") {
				cookieValue += this.del1;
			}
			cookieValue += p.substring(this.preSyl.length) + this.del2 + this[p];
		}
	}
	cookieValue = this.name + "=" + cookieValue;
	cookieValue += this.getCookieSettings();
	this.win.document.cookie = cookieValue;
}

Cookie.prototype.saveSingleValue = function(cookieValue, doEscape) {
	if (typeof cookieValue == "string" && cookieValue != "") {
		if (typeof doEscape != "boolean") {
			doEscape = true;
		}
		if (doEscape) {
			cookieValue = this.name + "=" + encodeURIComponent(cookieValue);
		} else {
			cookieValue = this.name + "=" + cookieValue;
		}
		cookieValue += this.getCookieSettings();
		this.win.document.cookie = cookieValue;
	}
}

Cookie.prototype.getCookieSettings = function() {
	var settings = "";
	if (this.expiryDate != null) {
		settings += "; expires=" + this.expiryDate.toUTCString();
	}
	if (!isNaN(this.maxAge)) {
		settings += "; max-age=" + this.maxAge;
	}
	if (this.path != null) {
		settings += "; path=" + this.path;
	}
	/*	If the domain does not contain a dot, Firefox and IE won't save the cookie.
		But they will display the domain without the dot when viewing the cookie in the browser.
		In Firefox domain must not equal location.hostname.	*/
	if (this.domain != null && this.domain.indexOf(".") > -1 && this.domain != location.hostname) {
		settings += "; domain=" + this.domain;
	}
	if (typeof this.secure == "boolean" && this.secure) {
		settings += "; secure";
	}
	return settings;
}

/*	For a given document cookies must have the same name, the same path and the same domain to be considered the same.
 *	It is not enough that they have the same name.
 *	Note that this method cannot be named 'delete' (even though a better name than 'remove') because
 *	'delete' is a reserved identifier.
 *	Also note that you can only remove a cookie if you know the path and domain with which it was created!
 *	Furthermore if the cookie is secure you can only remove it if the current connection is secure.
 *	But you can remove an unsecure cookie using a secure connection!
 *	Be aware that neither the path, the domain nor the secure property can be read in the cookie in any way!
 */
Cookie.prototype.remove = function() {
	var maxAge = this.maxAge;
	var expiryDate = this.expiryDate;
	var aYearAgo = new Date();
	aYearAgo.setFullYear(aYearAgo.getFullYear() - 1);
	this.expiryDate = aYearAgo;
	this.maxAge = 0;
	this.save(false);
	this.expiryDate = expiryDate;
	this.maxAge = maxAge;
}

Cookie.prototype.getValue = function() {
	return Cookie.readCookie(this.name, this.win);
}

Cookie.prototype.getParameter = function(name) {
	if (typeof name == "string" && name != "") {
		var value = this.getValue();
		if (value != null) {
			var valuesArray = value.split(this.del1);
			var arr;
			for (var i = 0; i < valuesArray.length; i++) {
				arr = valuesArray[i].split(this.del2);
				if (arr.length == 2) {
					if (arr[0] == name) {
						return decodeURIComponent(arr[1]);
					}
				}
			}
		}
	}
	return null;
}

/* Returns a parameter as an array, if the parameter was saved as an array. */
Cookie.prototype.getParameterValues = function(name) {
	var arrayString = this.getParameter(name);
	if (arrayString != null) {
		// if the delimiter is not found in the string, split just returns an array with one entry; the value of the string.
		return arrayString.split(this.del3);
	}
	return null;
}

/*	Method for assigning a name/value pair to the cookie.
	Note that the cookie instance must be saved before the parameter is actually written to the cookie on the web page. */
Cookie.prototype.setParameter = function(name, value) {
	if (typeof value != "string") {
		value = "" + value;
	}
	// if the parameter already exists it is overwritten
	this[this.preSyl + name] = encodeURIComponent(value);
}

/* Method for saving an array as a parameter.	*/
Cookie.prototype.setParameterValues = function(name, valueArray) {
	if (valueArray != null && valueArray.constructor == Array) {
		this.setParameter(name, valueArray.join(this.del3));
	}
}

Cookie.prototype.setExpiryDate = function(expiryDate) {
	if (expiryDate != null && expiryDate.constructor == Date) {
		this.expiryDate = expiryDate;
		var ms = this.expiryDate.getTime() - new Date().getTime();
		if (ms > 0) {
			this.maxAge = parseInt(ms / 1000, 10);
		} else {
			this.maxAge = 0;
		}
	}
}

/*	Note that it is possible to set a cookie that is invisible to the current page/location!	*/
Cookie.prototype.setPath = function(path) {
	if (typeof path == "string" && path != "") {
		this.path = path;
	}
}

/*	Note that you can only set the domain to a parent domain of the current location!
	If you wish to validate your value use Cookie.validateDomain.	*/
Cookie.prototype.setDomain = function(domain) {
	if (typeof domain == "string" && domain != "") {
		this.domain = domain;
	}
}

/*	If the current location is not secure the cookie will be invisible to this page/location,
	when secure is set to true.
	If secure is not specified, the cookie is visible to any type of connection.	*/
Cookie.prototype.setSecure = function(secure) {
	if (typeof secure == "boolean") {
		this.secure = secure;
	}
}
