// should be defined in labels.js
if (TYPES == undefined) var TYPES = {};
if (LABELS == undefined) var LABELS = {TYPE:TYPES};

function recent(obj) {
	while (obj && !obj.recentBlock && !/ccRecentChanges/.test(obj.className)) obj = obj.parentNode;
	if (!obj) return;
	if (!obj.recentBlock) obj.recentBlock = new RecentChanges(obj);
	return obj.recentBlock;

	function RecentChanges(obj) {
		var me = this;
		this.block = obj;
		this.cookieName = cookieName();
		this.vars = new Object();
		this.ids = new Object();
		indexIds(obj);

		function indexIds(o) {
			if (o.id) me.ids[o.id] = o;
			for (var i = 0; i != o.childNodes.length; ++i) indexIds(o.childNodes[i]);
		}

		this.shortcut = function shortcut(e, days, desc, show, unvisited) {
			var unseen = !e.altKey;
			var current = this.vars2query("");
			if (days != undefined) this.vars.dates = days;
			if (desc != undefined) this.vars.desc = desc;
			if (!this.vars.expandSettings) this.vars.filter = '';
			if (show != undefined || unvisited != undefined) this.vars.show = ((show!=undefined)? show : this.vars.show.replace(/[,|]?unvisited/,"")) + (((unvisited!=undefined)? unvisited : /unvisited/i.test(this.vars.show))? ",unvisited" : "");
			if (unseen != undefined) this.vars.unseen = unseen;
			if (e.shiftKey) {
				window.open(this.vars2query(document.location.href));
				this.query2vars(current);
			} else this.reset(false, true, days != undefined);
		}

		this.escapeRE = function escapeRE(s) { return s? (s + "").replace(/([*^${}+\[\]\-.|()?\\])/g, "\\$1") : ""; }

		this.processXml = function processXml(xml, majorGrouping, minorGrouping, desc, unseenOnly, filter, show, icons) {
			var DATE, cond = "", shown = new Object(), xml2, s, PF = new Object(), UF = new Object(), me = this;
			var H = new Object(), P = new Object(), U = new Object(), D = new Object(), V = new Object(), E = new Object(), R = new Object(), I, n = 0, graphics_dir = "", href = "", A = new Object();
			var stages = [prepare, highlights, replace1, replace2, evalIt, createA, addOrder, minorGroups, majorGroups, displayResult], timer = new Array(), majors, minors;
			try { var isIE = navigator.userAgent.indexOf("MSIE") > -1; } catch(e) {}
			window.status = "Calculating, please wait";
			if (this.redraws == undefined) this.redraws = 1; else ++this.redraws;
			var redraws = this.redraws;
			doStage();

			function doStage() {
				if (redraws != me.redraws) return;
				timer[timer.length] = new Date();
				stages[0]();
				timer[timer.length] = new Date();
				displayTimer(timer);
				stages.splice(0, 1);
				if (stages.length > 0 && window.setTimeout) window.setTimeout(doStage, 50);
			}

			function displayTimer(timer) {
				if (isDebug() && timer != undefined && timer.length > 0) {
					var t = new Array(timer.length/2);
					for (var i = 0; i != timer.length/2; ++i) t[i] = (timer[i*2+1] - timer[i*2]) + "ms";
					window.status = t.join(", ");
				} else if (window.status != "") window.status = window.status + ".";
			}

			function prepare() {
				if (redraws != me.redraws) return;
				// Create H, P, U, D, V, E and I
				DATE = {unvisited:"1999-01-01T00:00:00", "new":"1999-01-02T00:00:00", rename:"1999-01-03T00:00:00", max:""};
				for (var i in DATE) {var d = /(\d{4})-(\d\d)-(\d\d)/.exec(DATE[i]); if (d) DATE[i.toUpperCase()] = new Date(d[1], d[2]-1, d[3]); if (DATE[i]>DATE.max) DATE.max = DATE[i];}
				if (filter) {
					filter = new RegExp('<([PU])\\s+n="([^"]+)"\\s+s="[^"]*?' + me.escapeRE(filter) + '[^"]*"[^>]*?(?:>[^<]*</\\1|/)>', "g");
					s = xml.replace(filter, "$1F[$2]=true;\n").replace(/<([PI])\s[^>]+?(?:>[^<]*<\/\1|\/)>/g, "").replace(/<[^>]*>|\]\](?:--)?>/g, "");
					try {eval(s);} catch(e) {}
					cond += (cond? " && " : "") + '(PF[$1] || UF[$2])';
				}
				if (show) {
					show = show.toUpperCase().split(/[,|]/g);
					for (var i = 0; i != show.length; ++i) shown[show[i]] = 1;
				}
				cond += (cond? " && " : "") + 'shown["$5"]';
				if (unseenOnly) cond += (cond? " && " : "") + 'V[$1]<"$3T$4"';
				var orRename = shown.RENAME? "||'$5'=='RENAME'" : "";
				var orNew = shown.NEW? "||'$5'=='NEW')" : ")";
				if (!shown.UNVISITED) cond += (cond? " && " : "") + "(V[$1]!='" + DATE.unvisited + "'" + orRename + orNew;
				if (!shown.DELETE) cond += (cond? " && " : "") + "!E[$1]";
				if (!shown.REDIRECT) cond += (cond? " && " : "") + "!R[$1]";
				if (cond) cond = "if (" + cond + ") ";
				var sections = new Object();
				xml2 = xml
					.replace(/<(?:DATA|BASE)\s+([^=]+)=([^>]*?)\s*\/?>/g, "$1=$2;\n")													//" graphics_dir and href
					.replace(/>\s+</g, "><").replace(/<\?.*?\?>/g, "")															// remove spaces
					.replace(/<(PAGES|HIGHLIGHTS|LASTVISITS|USERS|CHANGES|DATES)\b[^\/>]*(?:\/>|>(?:a|[^a])*?)<\/\1>/g, function(whole, section) {sections[section] = whole; return "";})
					.replace(/<!\[CDATA\[|\]\]>|<!--(?:a|[^a])*?-->|\s*<[^>]*>\s*/mg, "");												// remove remaining elements, CDATA and comment tags
				s = str(sections.HIGHLIGHTS) + str(sections.LASTVISITS) + str(sections.PAGES) + str(sections.USERS) + str(sections.DATES) + str(sections.CHANGES);
				function str(s) { return s? s : ""; }
			}
			
			function highlights() {
				if (redraws != me.redraws) return;
				s = s.replace(/<H\s+n="([^"]+)"\s+s="([^"]+)"\s*\/>/g, "H[$1]=\"$2\";\n");
				var tmp = 0;
				while (s.length != tmp) {
					tmp = s.length;
					s = s.replace(/\bh="((?:h[^ ]+ )*)([^h",]+),?/g, "h=\"$1hl$2 ");
				}
			}

			function replace1() {
				if (redraws != me.redraws) return;
				s = s
					.replace(/<V\s+n="([^"]+)"\s+d="([^"]+)"\s+t="([^"]+)"\s*\/>/g, "V[$1]='$2T$3';\n")											// Real V for some P's
					.replace(/(<[PU]\s+n="([^"]+)")/g, "if (!V[$2]) V[$2]='" + DATE.unvisited + "';\n$1")													// Bogus V for remaining P's
					.replace(/(<I\s+p="([^"]+)"\s+(?:[^a]="[^"]+"\s+)*?a="(new|rename)")/g, "if (V[$2]=='" + DATE.unvisited + "') V[$2]=DATE['$3'];\n$1")					//" Different bogus V for "new" and "rename"
			//		.replace(/(<P\s+n="([^"]+)"[^>]+)>([^<]+)<\/P\s*>/g, '$1/>P[$2]=new String(P[$2]);P[$2].e="$3";\n')									// P with an excerpt
					.replace(/(<I\s+[^>]+?\/>)/g, "$1}")																	// add } to empty I's
			//		.replace(/(<I\s+[^>]+)>([^<]+)<\/I\s*>/g, '$1/>I[n]=new String(I[n]);I[n].e="$2";}\n')											// I with an excerpt
					.replace(/<\/?\S+\s*>/g, "")																		// remove empty tags
					.replace(/<([PU])\s+n="([^"]+)"\s+s="([^"]+)"(?:\s+h="([^"]+)")?\s*\/>/g, '$1[$2]="<a class=\\"$4cclink\\" target=_blank href=\\""+href+((V[$2]>DATE.max)?"$3("+V[$2]+")":"$3")+"\\">$3'+RLM+'</a>";\n')					// P and U without l
					.replace(/<([PU])\s+n="([^"]+)"\s+s="([^"]+)"\s+l="2"(?:\s+h="([^"]+)")?\s*\/>/g, 'R[$2]=1;\n$1[$2]="<a class=\\"$4ccredirect\\" target=_blank href=\\""+href+((V[$2]>DATE.max)?"$3("+V[$2]+")":"$3")+"\\">$3'+RLM+'</a>";\n')					// P and U with l=2
					.replace(/<P\s+n="([^"]+)"\s+s="([^"]+)"\s+l="-1"(?:\s+h="([^"]+)")?\s*\/>/g, 'E[$1]=1;\nP[$1]="<a class=\\"$3cclink\\" target=_blank href=\\""+href+"$2\\">$2'+RLM+'<sup>?</sup></a>";\n')	// P with l=-1
					.replace(/<U\s+n="([^"]+)"\s+s="([^"]+)"\s+l="-1"(?:\s+h="([^"]+)")?\s*\/>/g, 'U[$1]="<a class=\\"$3cclink\\" target=_blank href=\\""+href+"$2\\">$2'+RLM+'<sup>?</sup></a>";\n')			// U with l=-1
					.replace(/<([PU])\s+n="([^"]+)"\s+s="([^"]+)"\s+l="-2"(?:\s+h="([^"]+)")?\s*\/>/g, '$1[$2]="$3'+RLM+'";\n')										// P and U with l=-2
					;
			}

			function replace2() {
				if (redraws != me.redraws) return;
				s = xml2 + s
					.replace(/<D\s+n="([^"]+)"\s+s="([^"]+)"\s*\/>/g, 'D["$1"]="$2";\n')													// D
					.replace(/\ba="([^"]+)/g, function(ig, a) {return 'a="'+a.toUpperCase();})												// Change a to uppercase
					.replace(/<I\s+p="([^"]+)"\s+u="([^"]+)"\s+d="([^"]+)"\s+t="([^"]+)"\s+a="([^"]+)"\s*\/>/g, cond+'{I[n++]="$3T$4 $1 $2 $5";\n')						//" I
					.replace(/\n\}/g, "}\n").replace(/\}(?!\n)/g, "}\n").replace(/\n+/g, "\n")												// Minor formatting of script
					.replace(/^\s*<[^>]*>\}?\s*$|<!(?:--)?\[CDATA\[|\]\](?:--)?>|<!--|-->/mg, "")												// remove remaining elements, CDATA and comment tags
					.replace(/&quot;/g, '"').replace(/&apos;/g, "'").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&amp;/g, "&");							// XML Entities
			}

			function evalIt() {
				if (redraws != me.redraws) return;
				I = new Array(s.replace(/^I\[.+|^.+\n/mg,"").length);
				try {
					eval(s);
				} catch(e) {
					var errors = new Array();
					s = s.replace(/[\n\r]+\}/g, "}\n").split("\n");
					for (var i = 0; i != s.length; ++i) try {
						eval(s[i]);
					} catch(e) {
						errors.push(e.message + " in: " + s[i]);
					}
					if (errors.length) debug("Some errors occurred during the processing of the recent changes:\n   - " + errors.join("\n   - ") + "\n\nYou should be able to continue working, but some changes will not be displayed.");
				}
			}

			function createA() {
				if (redraws != me.redraws) return;
				// Create A
				s = xml.replace(/(?:.|\n|\r)+?\ba="([^"]+)"/g, "$1|").split("|"); //"
				s.pop();
				s = s.sort().join("|").replace(/\b([^\|]+)(?:\|\1\b)+/g, "$1").replace(/([^\|]+)\|?/g, function(ignore, type) {  					//"
					return 'A["' + type.toUpperCase() + '"]=\'' + (icons?'<img src="' + graphics_dir + type + '.gif"> ' : '') + '\';\n';	//"
				});
				eval(s);
			}

			function addOrder() {
				if (redraws != me.redraws) return;
				// Add order indicator to each item in I
				I.sort();
				s = " " + I.join("\n ");
				switch(majorGrouping) {
					case ("visits"):
						s = s.replace(/^ \S+ (\S+) \S+ \S+$/mg, function(item, p) {return V[p] + item;}); break;
					case ("mychanges"):
					case ("allchanges"):
					case ("dates"):
					defalut:
						s = s.replace(/^ (\S+)/mg, "$1 $1");
				}
				s = (" " + s.split("\n").sort().join("\n ")).replace(/^(?: *\n)+/, "");
				var i = Math.pow(10, Math.ceil(Math.log(n)/Math.log(10))) + (desc?n:0);
				if (desc) s = s.replace(/^/mg, function() {return i--;});
				else s = s.replace(/^/mg, function(){return i++;});
				if (!/Changes$/.test(majorGrouping)) s = s.replace(/^(\S+ [^T]+)\S+/mg, "$1");
			}

			function minorGroups() {
				if (redraws != me.redraws) return;
				// Prepare minor groups
				switch(minorGrouping) {
					case "pages": s = s
						.replace(/^(\S+ \S+) \S+ (\S+ \S+) (\S+)$/mg, "$2 $1 $3")		// (n g) dt (p u) a ==> (p u) (n g) a
						.split("\n").sort().join("\n")						// sort by page and user
						.replace(/^(\S+ \S+)( .+)$((?:\n\1 .+$)*)/mg, discardMultiples)		// get rid of multiple entries with the same p+u
						.replace(/^(\S+) (\S+) (\S+ \S+) (\S+ \S+)$/mg, "$1 $3 $2 $4")		// p u (n g) (a c) ==> p (n g) u (a c)
						.split("\n").sort().join("\n")						// sort by page and numerator
						.replace(/^(\S+) (\S+ \S+) (\S+) (\S+) (\S+)$(?:\n\1 \S+ \S+ (\S+) (\S+) (\S+)$)?(?:\n\1 \S+ \S+ (\S+) (\S+) (\S+)$)?((?:\n^\1 .+$)*)/mg,
							function(whole, p, ng, u1, a1, c1, u2, a2, c2, u3, a3, c3, more) {
								var cc = eval(whole.replace(/^(?:\S+ ){5}(\S+)$\n{0,}/mg,"$1+") + "0");
								return ng + " <li><b>" + P[p] + "</b>" + RLM + "<SPAN class='cccomment'>" + LABELS.PAGE_PREFIX + ((cc>1)? cc + " " + LABELS.TYPE.MULTIPLE : LABELS.TYPE[a1]) + LABELS.PAGE_POSTFIX +
									"</span><nobr>" + A[a1] + U[u1] + "</nobr>" +
									(!a2? "" : (", <nobr>" + A[a2] + U[u2] + "</nobr>" + (!a3? "" : (", <nobr>" + A[a3] + U[u3] + (more? " ..." : "") + "</nobr>")))) + "</li><minorDelim/>\n";
							}
						);
						break;
					case "users": s = s
						.replace(/^(\S+ \S+) \S+ (\S+) (\S+) (\S+)$/mg, "$3 $2 $1 $4")		// (n g) dt p u a ==> u p (n g) a
						.split("\n").sort().join("\n")						// sort by user and page
						.replace(/^(\S+ \S+)( .+)$((?:\n\1 .+$)*)/mg, discardMultiples)		// get rid of multiple entries with the same u+p
						.replace(/^(\S+) (\S+) (\S+ \S+ \S+ \S+)$/mg, "$1 $3 $2")		// u p (n g a c) ==> u (n g a c) p
						.split("\n").sort().join("\n")						// sort by user and numerator
						.replace(/^(\S+) (\S+ \S+) (\S+) (\S+) (\S+)$((?:\n\1 .+)*)/mg,
							function(whole, u, ng, a, c, p, more) {
								var cc = eval(whole.replace(/^(?:\S+ ){4}(\S+) .+$\n{0,}/mg,"$1+") + "0");
								return ng + " <li><b>" + U[u] + "</b>" + RLM + "<SPAN class='cccomment'>" + LABELS.USER_PREFIX + ((cc>1)? cc + " " + LABELS.TYPE.MULTIPLE : LABELS.TYPE[a]) + LABELS.USER_POSTFIX +
									"</span><nobr>" + A[a] + P[p] + "</nobr>" +
									more.replace(/\n\S+ \S+ \S+ (\S+) \S+ (\S+)/mg, function(ignore, a, p) {return ", <nobr>" + A[a] + P[p] + "</nobr>";}) + "</li><minorDelim/>";
							}
						);
						break;
					case "changes": s = s
						.replace(/^(\S+ \S+) ([^T]+)T(\S+) (\S+) (\S+) (\S+)$/mg,		// (n g) (d)(t) p u a
							function(ignore, ng, d, t, p, u, a) {
								return ng + " " + (icons? A[a] : "<li>") + " <b>" + P[p] + "</b>" + RLM + "<SPAN class='cccomment'>" +  LABELS.CHANGE_PREFIX + LABELS.TYPE[a] + LABELS.CHANGE_POSTFIX +
									"</span>" + U[u] +
									"&nbsp; <SPAN class='cccomment'>" + LABELS.TIMEDATE_DATE_PREFIX + D[d] + LABELS.TIMEDATE_TIME_PREFIX + t.substr(0,5) +
									LABELS.TIMEDATE_POSTFIX + "</span>" + (icons? "<br>" : "</li>") + "<minorDelim/>";
							}
						);
						break;
				}
				function discardMultiples(ignore, part1, part2, more) {return part1 + part2 + " " + (more?more.replace(/[^\n]/g,"").length+1:1) + "\n";}
			}

			function majorGroups() {
				if (redraws != me.redraws) return;
				// Group by major groups
				function groupByDate(ignore, whole, year, month, day) {
					var d = new Date(year, month-1, day);
					var t = new Date();
					day = 24*60*60*1000;
					if (d >= t-3*day) {
						if (!D[whole]) D[whole] = d.toLocaleString().replace(/\b(?:AM|PM|(?:12|00|0)(?::(?:00|0)){1,2}(?:\.0+)?)\b/ig, "");
						return whole;
					} else if (d >= t-7*day) var result = "Past_Week";
					else if (d >= t-14*day) result = "2_Weeks_Ago";
					else if (d >= t-21*day) result = "3_Weeks_Ago";
					else if (d >= t-28*day) result = "4_Weeks_Ago";
					else if (d - DATE.UNVISITED == 0) result = "Never";
					else if (d - DATE.NEW == 0) result = "New";
					else if (d - DATE.RENAME == 0) result = "Rename";
					else result = "Long_Ago";
					D[result] = LABELS.DATES[result]? LABELS.DATES[result] : result.replace(/_/g, " ");
					return result;
				}
				s = s.split("\n").sort();
				s = s.join("\n").replace(/^ *[\n\r]/mg, "").replace(/^\S+ ((\d{4})-(\d\d)-(\d\d))/mg, groupByDate)
					.replace(/^(\S+) (.+)$((?:\n\1 .+)*)/mg, function(ignore, d, more1, more2) {
						return /^\s*$/.test(more1)? "" : "<div class='ccsection'><div class='cctitle'><b>" + D[d] + "</b></div>\n<ul>" + more1 + more2.replace(/^\S+ /mg, "") + "</ul></div><majorDelim/>";
					});
			}

			function displayResult() {
				if (redraws != me.redraws) return;
				if (!me.ids["Changes"]) return;
				me.ids["Changes"].innerHTML = "";
				if (!s) {
					// No changes to show. Propose some alternatives.
					s = LABELS.NOTHING_NEW + "\n<ul>";
					// if the filter is on propose to turn it off
					if (filter)
						s += "\n\t<li><span onclick='javascript:recent(this).vars.filter=undefined;recent(this).reset(event.shiftKey,true);' class='A'>" + LABELS.PROPOSE_FILTER_OFF + "</span></li>";
					// if some of the main change types are off propose to turn it on
					if (!shown.NEW || !shown.ADD || !shown.RENAME)
						s += "\n\t<li><span onclick='javascript:recent(this).mergeShow(\"new,add,rename\");recent(this).reset(event.shiftKey,true);' class='A'>" + LABELS.PROPOSE_MAIN_CHANGES + "</span></li>";
					// if more than one person is using the computer and unseenOnly is on, propose to turn it off
					if (unseenOnly)
						s += "\n\t<li><span onclick='javascript:recent(this).mergeShow(\"seen\");recent(this).reset(event.shiftKey,true);' class='A'>" + LABELS.PROPOSE_SHOW_SEEN + "</span></li>";
					// propose to widen date range
					var newRange = (me.vars.dates&&me.vars.dates != 0)? 2 * me.vars.dates : 1;
					s += "\n\t<li><span onclick='javascript:recent(this).vars.dates="+newRange+";recent(this).reset(event.shiftKey,true,true);' class='A'>" + LABELS.PROPOSE_WIDEN_RANGE + newRange + "</span></li>";
					// if unvisited is off propose to turn it on
					if (!shown.UNVISITED)
						s += "\n\t<li><span onclick='javascript:recent(this).mergeShow(\"unvisited\");recent(this).reset(event.shiftKey,true);' class='A'>" + LABELS.PROPOSE_UNVISITED_OFF + "</span></li>";
					// propose other pages
					s += "\n</ul><p>" + LABELS.PROPOSE_OTHER;
					majors = [s];
				} else {
					// add highlights
					var inp = me.ids["userHighlight"];
					if (inp && inp.parentNode) inp = inp.parentNode.removeChild(inp);
					var highlights = "";
					var highlights_index = 0;
					for (var i in H) if (typeof(H[i]) == "string" ) {
						if (inp) {
							hrx = new RegExp('(^|[ ,;])' + H[i] + '(?![^ ,;])', 'i');
							if (inp.value.match(hrx)) continue;
						}
						++highlights_index;
						highlights += '\t&nbsp;<A class="hl' + highlights_index + ' cclink" target=_blank href="' + href + H[i] + '">' + H[i] + RLM + '</a>&nbsp;\n';
					}
					if (highlights || inp) me.ids["Changes"].innerHTML = "<DIV><SPAN class=ccsettingstitle>" + LABELS.HIGHLIGHTS + "</SPAN>\n" + highlights + '</DIV>\n';
					if (inp) {
						inp.className = 'hl' + ++highlights_index;
						me.ids["Changes"].firstChild.appendChild(inp).style.display = '';
					}
					majors = s.split(/<majorDelim\/>/g);
				}
				s = undefined;
				window.status = "";
				displayMajors();
			}

			function displayMajors() {
				if (redraws != me.redraws) return;
				if (majors == undefined || majors.length == 0) return;
				var o = me.ids["Changes"].appendChild(document.createElement("SPAN"));
				minors = majors[0].split(/<minorDelim\/>/g);
				majors.splice(0, 1);
				displayMinors(o)

				function displayMinors(o) {
					if (redraws != me.redraws) return;
					if (minors != undefined && minors.length > 0) {
						if (!o) o = me.ids["Changes"].lastChild.lastChild.lastChild.appendChild(document.createElement("SPAN"));
						o.innerHTML = minors.splice(0, isIE? 5 : minors.length).join("");
						if (minors.length > 0) {
							stages[stages.length] = displayMinors;
							return;
						}
					}
					if (majors.length > 0) stages[stages.length] = displayMajors;
				}
			}

		}

		this.mergeShow = function mergeShow(types) {
			if (types.constructor == String) types = types.split(",");
			for (var i = 0; i != types.length; ++i) if (!(new RegExp(types[i], "i")).test(this.vars.show)) this.vars.show += (this.vars.show? "|" : "") + types[i];
		};

		this.query2vars = function query2vars(query) {
			var args = query.toLowerCase().split(/&/g);
			for (var i = 0; i != args.length; ++i) {
				var arg = args[i].split(/=/);
				switch (arg[0]) {
					case "controls":	this.vars.controls = /^(?:1|true)$/.test(arg[1]); break;
					case "settings":	this.vars.expandSettings = /^(?:1|true)$/.test(arg[1]); break;
					case "icons":		this.vars.icons = /^(?:1|true)$/.test(arg[1]); break;
					case "new":
					case "dates":
					case "days":		if (arg[1] != undefined) this.vars.dates = arg[1]; break;
					case "major":
					case "grouping":	if (arg[1] != undefined) this.vars.major = arg[1]; break;
					case "minor":
					case "display":		if (arg[1] != undefined) this.vars.minor = arg[1]; break;
					case "order":
					case "sort":		if (/^(?:up|ascending|\+)$/.test(arg[1])) this.vars.desc = false;
								else if (/^(?:down|descending|-)$/i.test(arg[1])) this.vars.desc = true;
								break;
					case "filter":		this.vars.filter = (arg[1] != undefined)? arg[1] : ""; break;
					case "highlight":	this.vars.highlight = (arg[1] != undefined)? arg[1] : undefined; break;
					case "show":
					case "types":		if (arg[1] != undefined) this.vars.show = arg[1].replace(/\s*,\s*|\s*\|\s*|\s+/g, "|");
								break;
					case "hide":		this.vars.show = new Array();
								if (!/\bnew\b/.test(arg[1])) this.vars.show.push("new");
								if (!/\badd\b/.test(arg[1])) this.vars.show.push("add");
								if (!/\bedit\b/.test(arg[1])) this.vars.show.push("edit");
								if (!/\bdelete\b/.test(arg[1])) this.vars.show.push("delete");
								if (!/\brename\b/.test(arg[1])) this.vars.show.push("rename");
								if (!/\bunvisited\b/.test(arg[1])) this.vars.show.push("unvisited");
								if (!/\bredirect\b/.test(arg[1])) this.vars.show.push("redirect");
								if (!/\bseen\b/.test(arg[1])) this.vars.show.push("seen");
								this.vars.show = this.vars.show.join("|");
								break;
					case "help":		this.vars.expandHelp = /^(?:1|true)$/.test(arg[1]); break;
					case "debug":		this.vars.debug = /^(?:1|true)$/.test(arg[1]); break;
				}
			}
		}

		this.vars2options = function vars2options() {
			if (!this.ids["Controls"]) return;
			if (!this.ids["Controls"].localized) {
				this.ids["Controls"].innerHTML = replaceLabels(LABELS, (this.ids["Controls"].innerHTML == "")? this.vars.controlsHtml : this.ids["Controls"].innerHTML);
				this.ids["Controls"].localized = true;
				indexIds(this.ids["Controls"]);
			}
			if (this.vars.controls != undefined) this.ids["Controls"].style.display = this.vars.controls? "" : "none";
			if (this.vars.dates != undefined) this.ids["Dates"].value = this.vars.dates;
			if (this.vars.filter != undefined) this.ids["Filter"].value = this.vars.filter;
			this.ids["FilterOn"].checked = !!this.vars.filter;
			if (this.vars.highlight != undefined && this.ids["userHighlight"]) this.ids["userHighlight"].value = this.vars.highlight;
			if (this.vars.icons != undefined) this.ids["Icons"].checked = this.vars.icons;
			if (this.vars.major != undefined) {
				checkMultiple("Group", "dates,visits,mychanges,allchanges".split(","), false);
				checkIfExists("Group", this.vars.major);
			}
			if (this.vars.minor != undefined) {
				checkMultiple("Display", "pages,users,changes".split(","), false);
				checkIfExists("Display", this.vars.minor);
			}
			if (this.vars.desc != undefined) {
				checkIfExists("Sort", "Asc", !this.vars.desc);
				checkIfExists("Sort", "Desc", this.vars.desc);
			}
			if (this.vars.show != undefined) {
				checkMultiple("Show", "new,add,edit,rename,delete,unvisited,redirect,seen".split(","), false);
				checkMultiple("Show", this.vars.show.split(/[|,]/g));
			}
			this.vars2expand();

			function checkIfExists(prefix, name, flag) {var o = me.ids[prefix + toProperCase(name)]; if (o) o.checked = (flag != undefined)? flag : true;}
			function checkMultiple(prefix, names, flag) {for (var j = 0; j != names.length; ++j) checkIfExists(prefix, names[j], flag);}
		}

		this.vars2shortcuts = function vars2shortcuts() {
			var o = this.ids["Shortcuts"];
			if (!o) return;
			var shortcuts = new Array();
			if (this.vars.dates != undefined) shortcuts.push("Shortcut_" + parseFloat(this.vars.dates));
			if (this.vars.desc != undefined) shortcuts.push("Shortcut_" + (this.vars.desc? "Desc" : "Asc"));
			if (this.vars.show != undefined) {
				shortcuts.push("Shortcut_Show" + (/\bunvisited\b/.test(this.vars.show)? "More" : "Less"));
				if (/\bnew\b/.test(this.vars.show) && /\badd\b/.test(this.vars.show) && /\brename\b/.test(this.vars.show)) {
					if (/\bedit\b/.test(this.vars.show) && /\bdelete\b/.test(this.vars.show) && /\bredirect\b/.test(this.vars.show)) shortcuts.push("Shortcut_ShowAll");
					else if (!/\bedit\b/.test(this.vars.show) && !/\bdelete\b/.test(this.vars.show) && !/\bredirect\b/.test(this.vars.show)) shortcuts.push("Shortcut_ShowMain");
				}
			}
			if (shortcuts.length) traverseShortcuts(o);

			function traverseShortcuts(o) {
				if (o.id && /Shortcut_/.test(o.id)) {
					var fontWeight = "normal";
					for (var i = 0; i != shortcuts.length; ++i) if (o.id == shortcuts[i]) fontWeight = "bold";
					o.style.fontWeight = fontWeight;
				}
				var kids = o.childNodes;
				if (kids) for (i = 0; i != kids.length; ++i) traverseShortcuts(kids[i]);
			}
		}

		this.vars2expand = function vars2expand() {
			if (!this.ids["Controls"]) return;
			this.ids["ExpandSettings"].checked = !!this.vars.expandSettings;
			this.ids["ExpandSettingsArea"].style.display = this.vars.expandSettings? "" : "none";
			this.ids["ExpandHelp"].checked = !!this.vars.expandHelp;
			this.ids["ExpandHelpArea"].style.display = this.vars.expandHelp? "" : "none";
			this.ids["ExpandHelpGroup"].style.display = (this.vars.expandHelp || this.vars.expandSettings)? "block" : "inline";
			this.ids["ExpandHelpGroup"].className = (this.vars.expandHelp || this.vars.expandSettings)? "ccsettingsgroup" : "ccsettingsspan";
		}

		this.options2vars = function options2vars() {
			if (!this.ids["Controls"]) return;
			this.vars.controls = this.ids["Controls"].style.display != "none";
			this.vars.dates = this.ids["Dates"].value; if (this.vars.dates == "") this.vars.dates = undefined;
			this.vars.major = getIfExists("Group", "dates,visits,mychanges,allchanges".split(","));
			this.vars.minor = getIfExists("Display", "pages,users,changes".split(","));
			this.vars.desc = this.ids["SortDesc"].checked;
			this.vars.filter = this.ids["FilterOn"].checked? this.ids["Filter"].value : undefined;
			if (this.vars.filter == "") this.vars.filter = undefined;
			this.vars.highlight = this.ids["userHighlight"] ? this.ids["userHighlight"].value : undefined;
			if (this.vars.highlight == "") this.vars.highlight = undefined;
			this.vars.show = getIfExists("Show", "new,add,edit,rename,delete,unvisited,redirect,seen".split(","));
			this.vars.icons = this.ids["Icons"].checked;
			this.vars.expandSettings = this.ids["ExpandSettings"].checked;
			this.vars.expandHelp = this.ids["ExpandHelp"].checked;

			function getIfExists(prefix, names) {
				var values = new Array();
				for (var j = 0; j != names.length; ++j) {
					var o = me.ids[prefix + toProperCase(names[j])];
					if (o && o.checked) values.push(names[j]);
				}
				return values.join(",");
			}
		}

		this.vars2query = function vars2query(url) {
			var result = ((this.vars.dates != undefined)? "&dates=" + this.vars.dates : "")
				+ ((this.vars.desc != undefined)? "&sort=" + (this.vars.desc? "-" : "+") : "")
				+ ((this.vars.filter != undefined)? "&filter=" + this.vars.filter : "")
				+ ((this.vars.highlight != undefined)? "&highlight=" + this.vars.highlight : "")
				+ ((this.vars.icons != undefined)? "&icons=" + bool(this.vars.icons) : "")
				+ ((this.vars.major != undefined)? "&major=" + this.vars.major : "")
				+ ((this.vars.minor != undefined)? "&minor=" + this.vars.minor : "")
				+ ((this.vars.show != undefined)? "&show=" + this.vars.show : "")
				+ ((this.vars.controls != undefined)? "&controls=" + bool(this.vars.controls) : "")
				+ ((this.vars.expandSettings != undefined)? "&settings=" + bool(this.vars.expandSettings) : "")
				+ ((this.vars.expandHelp != undefined)? "&help=" + bool(this.vars.expandHelp) : "")
				+ ((this.vars.debug != undefined)? "&debug=" + bool(this.vars.debug) : "");
			return (this.cleanQuery(url) + result).replace(/\?&/, "?").replace(/^&|&+(?=&)/g, "");
			function bool(b) { return b? "1" : ""; }
		}

		this.cleanQuery = function cleanQuery(url) {
			return url? url.replace(/&?\b(?:new|days|dates|order|sort|filter|highlight|icons|major|grouping|minor|display|show|types|hide|controls|settings|debug)\s*=[^&]*/g, "").replace(/\?&/, "?").replace(/^&|&+(?=&)|&+$/g, "") : "";
		}

		this.reset = function reset(newWindow, useVarsInsteadOfOptions, reload, noRedraw) {
			if (useVarsInsteadOfOptions) this.vars2options();
			else {this.options2vars(); this.vars2expand();}
			this.vars2shortcuts();
			this.vars2cookie();
			if (reload || newWindow) {
				if (newWindow) window.open(document.location.href);
				else document.location.href = this.cleanQuery(document.location.href);
			} else {
				if (!noRedraw) {
					var d = this.getXml();
					if (!d) {
						if (this.ids["Changes"]) this.ids["Changes"].innerHTML= LABELS.LOADING;
					} else this.processXml(d, this.vars.major, this.vars.minor, this.vars.desc, !/\bseen\b/.test(this.vars.show), this.vars.filter, this.vars.show, this.vars.icons);
				}
				this.vars2cookie();
			}
		}

//		var lastCookieValue;
		function setCookie(name, value, expirationDate) {
//			lastCookieValue = value;
//			if (value != extractCrumb(name, document.cookie)) alert("Before Set: "+name+"=\n"+value+"\n\nCurrent: \n" + extractCrumb(name, document.cookie));
			try {
				if (expirationDate == undefined) expirationDate = new Date(2099, 11, 31, 23, 59, 59);
				var date = (expirationDate.toUTCString)? expirationDate.toUTCString() : expirationDate;
				document.cookie = name + "=" + encodeURIComponent(value) + ";path=/;" + ((date != undefined)? "expires=" + date + ";" : "");
			} catch(e) { debug(e); }
//			if (value != extractCrumb(name, document.cookie)) alert("After Set: "+name+"=\n"+value+"\n\nCurrent: \n" + extractCrumb(name, document.cookie));
		}

		function getCookie(name) {
//			if (lastCookieValue != extractCrumb(name, document.cookie)) alert("Before Get: "+name+"\nExpected:\n"+lastCookieValue+"\n\nCurrent: \n" + extractCrumb(name, document.cookie));
			try {
				return extractCrumb(name, document.cookie);
			} catch(e) { debug(e); }
		}

		function extractCrumb(crumbName, cookieText) {
			var re = new RegExp("\\b" + me.escapeRE(crumbName) + "=([^;]*);?");
			var c = re.exec(cookieText);
			if (c) return decodeURIComponent(c[1]);
		}

		function delCookie(name) {
			try {
				if (getCookie(name) != undefined) setCookie(name, "", new Date(1999, 1, 1));
			} catch(e) { debug(e); }
		}

		function saveProps(name, obj, version) {
			setCookie(name, marshal(obj, version));

			function marshal(obj, version) {
				var defs = {
					"RC3":	{controls:bool, debug:bool, expandSettings:bool, expandHelp:bool, dates:varchar, desc:bool, filter:varchar, icons:bool, major:option("dates,visits"), minor:option("pages,users,changes"), show:flags("new,add,edit,rename,delete,unvisited,redirect,seen")},
					"RC4":	{controls:bool, debug:bool, expandSettings:bool, expandHelp:bool, dates:varchar, desc:bool, filter:varchar, icons:bool, major:option("dates,visits"), minor:option("pages,users,changes"), show:flags("new,add,edit,rename,delete,unvisited,redirect,seen"), highlight:varchar},
					"default":		"RC4"
				}
				var obj = clone(obj);
				obj._version = version? version : defs["default"];
				var def = defs[obj._version];
				def._version = varchar;
				var keys = keyNames(def).sort();
				var values = new Array(keys.length);
				for (var k = 0; k != keys.length; ++k) values[k] = def[keys[k]](obj[keys[k]]);
				return values.join("");

				function bool(s) { return (s == undefined)? " " : ((s==true || s=="1" || s=="-1" || s=="true")? "1" : "0"); }
				function varchar(s) { return (s != undefined)? encodeURIComponent(s) + '"' : "#"; }
				function option(options) {
					var o = options.split(",");
					var reverse = new Object();
					for (var i = 0; i != o.length; ++i) reverse[o[i]] = i + 1;
					reverse[undefined] = 0; reverse[""] = 0; reverse[null] = 0;
					for (var prefix = ""; i >= 1; i /= 16) prefix += " ";
					return enumEx;
					function enumEx(s) {return (s != undefined && reverse[s] != undefined)? (prefix + reverse[s].toString(16)).slice(-prefix.length) : prefix;}
				}
				function flags(options) {
					var o = options.split(",");
					var reverse = new Object();
					reverse[undefined] = 0; reverse[""] = 0; reverse[null] = 0;
					for (var i = 0; i != o.length; ++i) reverse[o[i]] = Math.pow(2, i);
					for (var prefix = ""; i >= 1; i /= 4) prefix += " ";
					return flagsEx;
					function flagsEx(s) {
						if (s == undefined) return prefix;
						var o = s.split(/[,|]/g);
						var r = 0;
						for (var i = 0; i != o.length; ++i) if (reverse[o[i]] != undefined) r += reverse[o[i]];
						return (prefix + r.toString(16)).slice(-prefix.length);
					}
				}
				function clone(obj) {
					var cloned = new Object();
					for (var p in obj) cloned[p] = obj[p];
					return cloned;
				}
			}
		}

		function loadProps(name) {
			return unmarshal(getCookie(name));

			function unmarshal(s) {
				var defs = {
					"RC3":	{controls:bool, debug:bool, expandSettings:bool, expandHelp:bool, dates:varchar, desc:bool, filter:varchar, icons:bool, major:option("dates,visits"), minor:option("pages,users,changes"), show:flags("new,add,edit,rename,delete,unvisited,redirect,seen")},
					"RC4":	{controls:bool, debug:bool, expandSettings:bool, expandHelp:bool, dates:varchar, desc:bool, filter:varchar, icons:bool, major:option("dates,visits"), minor:option("pages,users,changes"), show:flags("new,add,edit,rename,delete,unvisited,redirect,seen"), highlight:varchar},
					"default":		"RC4"
				}
				if (s == undefined) s = "";
				var p = 0;
				var version = varchar(s);
				var def = defs[version]? defs[version] : defs[defs["default"]];
				var keys = keyNames(def).sort();
				var values = {_version: version};
				for (var k = 0; k != keys.length; ++k) values[keys[k]] = def[keys[k]](s);
				return values;

				function bool(s) {
					var v = s.substr(p++, 1);
					if (v != "" && v != " ") return (v == "1");
				}
				function varchar(s) {
					var r = /([^"#]*)(["#])/.exec(s.substr(p));
					if (r) {
						p += r[1].length + 1;
						if (r[2] != "#") return decodeURIComponent(r[1]);
					}
				}
				function option(options) {
					var o = options.split(",");
					var n = Math.ceil(Math.log(o.length)/Math.log(16));
					return enumEx;
					function enumEx(s) {
						var key = parseInt(s.substr(p, n), 16);
						p += n;
						if (!isNaN(key)) return o[key-1];
					}
				}
				function flags(options) {
					var o = options.split(",");
					var n = Math.ceil(o.length / 4);
					return flagsEx;
					function flagsEx(s) {
						var key = parseInt(s.substr(p, n), 16);
						p += n;
						if (isNaN(key)) return;
						var values = new Array();
						for (var i = 0; i != o.length; ++i) if (Math.pow(2, i) & key) values.push(o[i]);
						return values.join(",");
					}
				}
				function keyNames(obj) {
					var keys = new Array();
					for (var k in obj) keys.push(k);
					return keys;
				}
			}
		}

		function keyNames(obj) {
			var keys = new Array();
			for (var k in obj) keys.push(k);
			return keys;
		}

		function toProperCase(s) {
			return s.substr(0,1).toUpperCase() + s.substr(1).toLowerCase();
		}

		function cookieName() {
			var q = document.location.search;
			var query = /(?:([^?:=#&]+):)([^?:=#&]*)/.exec(q);
			if (query) {var group = query[1]; var name = query[2];}
			else {
				query = /\bgroup=([^&]*).*?\btitle=([^&]*)?/.exec(q);
				if (query) {group = query[1]; name = query[2];}
				else {
					query = /\btitle=([^&]*).*?\bgroup=([^&]*)/.exec(q);
					if (query) {group = query[2]; name = query[1];}
					else {
						query = /\btitle=([^&]*)|\bgroup=([^&]*)/.exec(q);
						if (query) {group = query[2]; name = query[1];}
						else if (!/\bnew=/.test(q)) {
							query = /\?([^?:=#&]+)/.exec(q);
							if (query) name = query[1];
						}
					}
				}
			}
			if (name == "מה_חדש" || /\bnew=/.test(q)) name = "recent_changes";
			else if (name == "" || name == undefined || name == "דף_פתיחה") name = "home_page";
			return "RC!" + ((group != undefined && group != "")? group + ":" : "") + name;
		}

		this.vars2cookie = function vars2cookie() {
			if (!saveProps) return;
			saveProps(this.cookieName, this.vars);
		}

		this.cookie2vars = function cookie2vars() {
			if (!loadProps) return;
			merge(this.vars, loadProps(this.cookieName));
		}

		function merge(dest, source) {
			for (var i in source) if (source[i] != undefined) dest[i] = source[i];
		}

		this.getXml = function getXml() {
			if (!this.getXmlCache) {
				var d = this.ids["Data"];
				if (d) {
					this.getXmlCache = trim(d.innerHTML);
					if (!this.getXmlCache) {
						var me = this;
						var loc = document.location.href.split("?", 2);
						if (loc.length == 2) {
							var m = /\bgroup=([^&=:]+)(?:&|$)/.exec(loc[1]);
							if (!m) m = /(?:^|=)([^&=:]+):/.exec(loc[1]);
						}						
						new ccLoader(loc[0] + "?format=xml&new=" + this.vars.dates + (m? "&group=" + m[1] : "") + (this.vars.highlight ? "&highlight=" + this.vars.highlight : ""), getXmlCallback);
					}
				}
				if (!this.getXmlCache) this.getXmlCache = "";
			}
			return this.getXmlCache;

			function getXmlCallback(ccLoaderObj) {
				me.getXmlCache = ccLoaderObj.serialize();
				me.reset();
			}
		}

		function trim(s) {
			return s? s.replace(/^\s+|\s+$/g, "") : s;
		}

		function isDebug() {
			return typeof(me) != "undefined" && me.vars != undefined && me.vars.debug;
		}

		function debug(e) {
			if (!isDebug()) return;
			if (typeof(e) != "string") throw e;
			if (confirm(e + "\n\nWould you like to debug?")) debugger;
		}

		if (typeof(RLM) == "undefined") RLM = ""; // Do not comment out this line even on non-Bidi systems!

		// Set defaults:
		this.query2vars("controls=1&dates=3&major=dates&minor=pages&sort=-&filter=&icons=1&show=new,add,rename,unvisited,seen&settings=0&help=0&debug=0");

		// Override using cookies:
		this.cookie2vars();

		// Override using the query specified by the user in the URL:
		if (document.location.search) this.query2vars(unescape(document.location.search.substr(1)));

		// Insert options, and initialize them (you can move this statement to the place in the document where you want the options to be inserted):
		this.vars2options();

		// Render the changes:
		this.reset();
	}
}


