
var clientPC = navigator.userAgent.toLowerCase(); // Get client info
var clientVer = parseInt(navigator.appVersion); // Get browser version
var is_ie = ((clientPC.indexOf("msie") != -1) && (clientPC.indexOf("opera") == -1));
var is_win = ((clientPC.indexOf("win")!=-1) || (clientPC.indexOf("16bit") != -1));
var is_gecko = (clientPC.indexOf("msie") == -1) && ((clientPC.indexOf("gecko") != -1) || (clientPC.indexOf("mozilla") != -1) || (clientPC.indexOf("netscape") != -1));

////////////////
/// tools.js ///
////////////////


function div(name) {
   var d = document;
   if(d.getElementById && !(d.all))
      return d.getElementById(name);
   else if(d.all)
      return d.all[name];
};


//RPC related functiona
///////////////////////

function newRequest() {
	request = false;

   //Constructor
   //-----------
   // branch for native XMLHttpRequest object
   if(window.XMLHttpRequest) {
      try {
         request = new XMLHttpRequest();
      }
      catch(e) {
         request = false;
      }

   // branch for IE/Windows ActiveX version
   } else if(window.ActiveXObject) {
      try {
         request = new ActiveXObject("Msxml2.XMLHTTP");
      }
      catch(e) {
         try {
            request = new ActiveXObject("Microsoft.XMLHTTP");
         }
         catch(e) {
            request = false;
         }
      }
   }

   return request;
}




function include_dom(script_filename) {
    var html_doc = document.getElementsByTagName('head').item(0);
    var js = document.createElement('script');
    js.setAttribute('language', 'javascript');
    js.setAttribute('type', 'text/javascript');
    js.setAttribute('src', script_filename);
    html_doc.appendChild(js);
    return false;
}












/**
 * stringFunctions.js
 *
 * This file contains a collection of string functions for javascript.
 * Most of them are inspired by their PHP equivalent.
 *
 * This source file is subject to version 2.1 of the GNU Lesser
 * General Public License (LPGL), found in the file LICENSE that is
 * included with this package, and is also available at
 * http://www.gnu.org/copyleft/lesser.html.
 * @package     Javascript
 *
 * @author      Dieter Raber <dieter@dieterraber.net>
 * @copyright   2004-12-27
 * @version     1.0
 * @license     http://www.gnu.org/copyleft/lesser.html
 *
 */

/**
 * htmlEntities
 *
 * Convert all applicable characters to HTML entities
 *
 * object string
 * return string
 *
 * example:
 *   test = 'äöü'
 *   test.htmlEntities() //returns '&auml;&ouml;&uuml;'
 */

String.prototype.htmlEntities = function()
{
  var chars = new Array ('&','à','á','â','ã','ä','å','æ','ç','è','é',
                         'ê','ë','ì','í','î','ï','ð','ñ','ò','ó','ô',
                         'õ','ö','ø','ù','ú','û','ü','ý','þ','ÿ','À',
                         'Á','Â','Ã','Ä','Å','Æ','Ç','È','É','Ê','Ë',
                         'Ì','Í','Î','Ï','Ð','Ñ','Ò','Ó','Ô','Õ','Ö',
                         'Ø','Ù','Ú','Û','Ü','Ý','Þ','€','\"','ß','<',
                         '>','¢','£','¤','¥','¦','§','¨','©','ª','«',
                         '¬','­','®','¯','°','±','²','³','´','µ','¶',
                         '·','¸','¹','º','»','¼','½','¾');

  var entities = new Array ('amp','agrave','aacute','acirc','atilde','auml','aring',
                            'aelig','ccedil','egrave','eacute','ecirc','euml','igrave',
                            'iacute','icirc','iuml','eth','ntilde','ograve','oacute',
                            'ocirc','otilde','ouml','oslash','ugrave','uacute','ucirc',
                            'uuml','yacute','thorn','yuml','Agrave','Aacute','Acirc',
                            'Atilde','Auml','Aring','AElig','Ccedil','Egrave','Eacute',
                            'Ecirc','Euml','Igrave','Iacute','Icirc','Iuml','ETH','Ntilde',
                            'Ograve','Oacute','Ocirc','Otilde','Ouml','Oslash','Ugrave',
                            'Uacute','Ucirc','Uuml','Yacute','THORN','euro','quot','szlig',
                            'lt','gt','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');

  newString = this;
  for (var i = 0; i < chars.length; i++)
  {
    myRegExp = new RegExp();
    myRegExp.compile(chars[i],'g')
    newString = newString.replace (myRegExp, '&' + entities[i] + ';');
  }
  return newString;
}
















///////////////////
/// lxmlhttp.js ///
///////////////////

/* JavaScript by Lumpio- (Matti Virkkunen)
 * This JavaScript may be freely used and edited for NON COMMERCIAL
 * purposes as long as this header is included.
 ***/

/* XMLHttp wrapper... used mostly in some private stuff, haven't really tested
 * for crossbrowser compatibility, is supposed to be tho O_o
 *******/

function LXMLHttp(method, url, async, minimize, xmlRoot) {
	this.method = method || null;
	this.url = url || null;
	this.async = async || null;
	// I can't remember why I used "a" as the prefix for minimize in order
	// to avoid clashing with the method minimize. Anybody have an explanation
	// for my choice of prefix?
	this.aminimize = minimize || null;
	if (xmlRoot) {
		this.createDomDoc(xmlRoot);
	} else {
		this.doc = null;
	}

	this.xmlhttp = LXMLHttp.makeXMLHttp();

	this.readyState = LXMLHttp.UNINITIALIZED;
	this.status = null;
	this.statusText = null;
	this.responseText = null;
	this.responseXML = null;
	this.inProgress = false;

	this.onreadystatechange = null;
	this.onloading = null;
	this.onloaded = null;
	this.oninteractive = null;
	this.oncomplete = null;

	this.onsuccess = null;
	this.onerror = null;
}

LXMLHttp.supported = null;

LXMLHttp.UNINITIALIZED = 0;
LXMLHttp.LOADING = 1;
LXMLHttp.LOADED = 2;
LXMLHttp.INTERACTIVE = 3;
LXMLHttp.COMPLETE = 4;

LXMLHttp.makeXMLHttp = function() {
	var xmlhttp = null;

	if (window.XMLHttpRequest) {
		xmlhttp = new XMLHttpRequest();
	} else if (window.ActiveXObject) {
		try {
			xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
		} catch (e) {
			return null;
		}
	} else {
		return null;
	}

	return xmlhttp;
};

LXMLHttp.makeDomDoc = function(root) {
	var doc;

	if (document.implementation && document.implementation.createDocument) {
		doc = document.implementation.createDocument("", root, null);
	} else if (window.ActiveXObject) {
		try {
			doc = new ActiveXObject("Microsoft.XMLDOM");
			doc.appendChild(doc.createElement(root));
		} catch (e) {
			return null;
		}
	} else {
		return null;
	}
	return doc;
};

LXMLHttp.createAndOpen = function(method, url, async, minimize, xmlRoot) {
	var lx = new LXMLHttp(method, url, async, minimize, xmlRoot);

	lx.open();

	return lx;
};

LXMLHttp.prototype.open = function(method, url, async) {
	// IIRC some browser(s) don't like calling open on an already opened XMLHttpRequest
	// without calling abort first
	this.abort();
	this.xmlhttp.open(method || this.method, url || this.url, typeof async == 'undefined' ? this.async : async);
	if (typeof async != 'undefined') {
		this.async = async;
	}
};

LXMLHttp.prototype.setRequestHeader = function(name, value) {
	this.xmlhttp.setRequestHeader(name, value);
};

LXMLHttp.prototype.getResponseHeader = function(name) {
	return this.xmlhttp.getResponseHeader(name);
};

LXMLHttp.prototype.getAllResponseHeaders = function() {
	return this.xmlhttp.getAllResponseHeaders();
};

LXMLHttp.prototype.abort = function() {
	if (this.inProgress) {
		// We don't want to catch readyState 4 when aborting, hence the following.
		// Not setting to null since IE doesn't like that.
		this.xmlhttp.onreadystatechange = function() {};
		this.xmlhttp.abort();
		this.inProgress = false;
	}
};

LXMLHttp.readyStateChangeHandler = function(obj) {
	obj.readyState = obj.xmlhttp.readyState;
	switch (obj.readyState) {
		case LXMLHttp.LOADING:
			if (obj.onloading) {
				obj.onloading(obj);
			}
			break;
		case LXMLHttp.LOADED:
			if (obj.onloaded) {
				obj.onloaded(obj);
			}
			break;
		case LXMLHttp.INTERACTIVE:
			if (obj.oninteractive) {
				obj.oninteractive(obj);
			}
			break;
		case LXMLHttp.COMPLETE:
			obj.responseText = obj.xmlhttp.responseText;
			obj.responseXML = obj.xmlhttp.responseXML;
			try {
				obj.status = obj.xmlhttp.status;
				obj.statusText = obj.xmlhttp.statusText;
			} catch(e) {
				// Mysterious Mozilla bug - status and statusText sometimes just go AWOL
				// when abort has been used on the same XMLHttpRequest during its lifetime
				obj.status = 0;
				obj.statusText = "UNKNOWN ERROR";
			}

			if (obj.oncomplete) {
				obj.oncomplete(obj);
			}
			if ( ((obj.status >= 200 && obj.status < 300) || obj.status == 304) && (obj.doc == null || (!obj.doc.parseError && (!obj.doc.documentElement || obj.doc.documentElement.nodeName != "parsererror"))) )
			{
				if (obj.onsuccess) {
					obj.onsuccess(obj);
				}
			} else {
				if (obj.onerror) {
					obj.onerror(obj);
				}
			}

			obj.inProgress = false;
			break;
	}

	if (obj.onreadystatechange) {
		obj.onreadystatechange(obj);
	}
};

LXMLHttp.prototype.send = function(data) {
	if (this.aminimize) {
		this.minimize();
	}

	if (this.async) {
		var obj = this;
		this.xmlhttp.onreadystatechange = function() {
			LXMLHttp.readyStateChangeHandler(obj);
		};
		this.inProgress = true;
		this.xmlhttp.send(data || this.doc || null);
	} else {
		return this.xmlhttp.send(data || this.doc || null);
	}
};

LXMLHttp.checkDoc = function(doc) {
	if (doc.parseError) {
		return false;
	}
	if (!doc.documentElement || doc.documentElement.nodeName == "parsererror") {
		return false;
	}
	return true;
};

LXMLHttp.prototype.createDomDoc = function(root) {
	this.doc = LXMLHttp.makeDomDoc(root);
};

// Attempts to make the XMLHttpRequest smaller by removing "unnecessary" headers
LXMLHttp.prototype.minimize = function() {
	this.xmlhttp.setRequestHeader("Accept", null);
	this.xmlhttp.setRequestHeader("Accept-Language", null);
	this.xmlhttp.setRequestHeader("Accept-Encoding", null);
	this.xmlhttp.setRequestHeader("Accept-Charset", null);
	this.xmlhttp.setRequestHeader("User-Agent", null);
};

LXMLHttp.isSupported = function() {
	if (typeof LXMLHttp.supported != 'boolean') {
		LXMLHttp.supported = (LXMLHttp.makeXMLHttp() && LXMLHttp.makeDomDoc()) ? true : false;
	}
	return LXMLHttp.supported;
};
























////////////////
/// event.js ///
////////////////

// Cross-browser event handling
// by Scott Andrew LePera
// http://www.scottandrew.com/weblog/articles/cbs-events

// Modified 2004-08-10 by Andrew Grgeory to work around Konqueror bug
// Modified 2004-06-04 by Andrew Gregory to support legacy (NS3,4) browsers
// http://www.scss.com.au/family/andrew/

// eg. addEvent(imgObj, 'mousedown', processEvent, false);
function addEvent(obj, evType, fn, useCapture) {
  // work around Konqueror bug #57913 which prevents
  // window.addEventListener('load',...) from working
  var ua = navigator.userAgent;
  var konq = ua.indexOf('KHTML') != -1 && ua.indexOf('Safari') == -1 && obj == window && evType == 'load';
  // don't use addEventListener for Konq, have Konq fall back to the old
  // obj.onload method
  if (obj.addEventListener && !konq) {
    obj.addEventListener(evType, fn, useCapture);
    return true;
  } else if (obj.attachEvent) {
    return obj.attachEvent('on' + evType, fn);
  } else {
    if (!obj.cb_events) {
      obj.cb_events = new Object();
      obj.cb_ftemp = null;
    }
    var events = obj.cb_events[evType];
    if (!events) {
      events = new Array();
      obj.cb_events[evType] = events;
    }
    var i = 0;
    while ((i < events.length) && (events[i] != fn)) {
      i++;
    }
    if (i == events.length) {
      events[i] = fn;
      obj['on' + evType] = new Function("var ret=false,e=this.cb_events['"+evType+"'];if(e){for(var i=0;i<e.length;i++){this.cb_ftemp=e[i];ret=this.cb_ftemp()||ret;}return ret;}");
    }
    return true;
  }
}

// eg. removeEvent(imgObj, 'mousedown', processEvent, false);
function removeEvent(obj, evType, fn, useCapture) {
  // work around Konqueror bug #57913 which prevents
  // window.addEventListener('load',...) from working
  var ua = navigator.userAgent;
  var konq = ua.indexOf('KHTML') != -1 && ua.indexOf('Safari') == -1 && obj == window && evType == 'load';
  // don't use addEventListener for Konq, have Konq fall back to the old
  // obj.onload method
  if (obj.removeEventListener && !konq) {
    obj.removeEventListener(evType, fn, useCapture);
    return true;
  } else if (obj.detachEvent) {
    return obj.detachEvent('on' + evType, fn);
  } else {
    var ret = false;
    if (obj.cb_events) {
      var events = obj.cb_events[evType];
      if (events) {
        // remove any matching functions from the events array, shuffling items
        // down to fill in the space before truncating the array
        var dest = 0;
        for (var src = 0; src < events.length; src++) {
          if (dest != src) {
            events[dest] = events[src];
          }
          if (events[dest] == fn) {
            ret = true;
          } else {
            dest++;
          }
        }
        events.length = dest;
      }
    }
    return ret;
  }
}























//////////////////
/// cssutil.js ///
//////////////////

/*
CSS Utilities
by Andrew Gregory
http://www.scss.com.au/family/andrew/

I have placed this code in the public domain. Feel free to use it however you
wish.

v1.3  6-Oct-2004 Added el.className checks
v1.2  5-Aug-2004 Simplified code by using regular expressions.
v1.1 12-Apr-2004 Fixed bug in elementRemoveClass() which removed partially matching classnames.
v1.0 29-Mar-2004 Initial version. Allows non-destructive setting and removal of CSS class names.
*/
// Test if an element has the given CSS class
function elementHasClass(el,cl){return (el.className&&el.className.search(new RegExp('\\b'+cl+'\\b'))>-1);}
// Ensure an element has the given CSS class
function elementAddClass(el,cl){var c=el.className;if(!c)c='';if(!elementHasClass(el,cl))c+=((c.length>0)?' ':'')+cl;el.className=c;}
// Ensure an element no longer has the given CSS class
function elementRemoveClass(el,cl){if(el.className)el.className=el.className.replace(new RegExp('\\s*\\b'+cl+'\\b\\s*'),' ').replace(/^\s*/,'').replace(/\s*$/,'');}

































//////////////////
/// gwshack.js ///
//////////////////

//Outputs gwshack's layout
//////////////////////////

var output = '<div id="container"> \
<div id="loading">Loading...</div> \
<div id="right"><br/> \
	<div id="right_header" class="header right_width"> </div> \
	<div id="skills" class="main" style="display: none;"> </div> \
	<div id="att_points" class="main" style="display: none;"> </div> \
	<div id="gwbbcode_syntax" class="main" style="display: none; font-size: 12px;">Loading...</div> \
	<div id="build_library" class="main" style="display: none;"> </div> \
	<div id="profile" class="main" style="display: none;"> </div> \
	<div id="history" class="main" style="display: none;"> </div> \
	<div id="copyright"> \
All skill icons, names and descriptions are &copy; ArenaNet.<br/> \
Add gwBBCode to your phpBB2 forum: <a href="forums/viewtopic.php?t=14242" onclick="return forums_open(\'viewtopic.php?t=14242\');">gwBBCode 1.8.3</a><br/> \
Suggestions, bugs: <a href="forums/viewforum.php?f=1" onclick="return forums_open(\'viewforum.php?f=1\');">gwShack\'s forum</a>, <a href="forums/viewforum.php?f=2" onclick="return forums_open(\'viewforum.php?f=2\');">gwBBCode\'s forum</a> \
<div id="hoh_jump"></div> \
   </div> \
</div> \
 \
<div id="left"> \
	<div id="login_div"> \
      <div id="login_form" style="display: inline;"> \
<span>Username:</span><input id="username" size="10" type="text"/>&nbsp; \
<span>Password:</span><input id="password" size="10" maxlength="32" type="password"/>&nbsp; \
<span onclick="switch_checkbox(\'autologin\');"><img class="clean" src="img/false.gif" id="autologin" alt="checkbox"/>Remember me</span>&nbsp; \
<button name="login" onclick="send_form(\'log in\');">Log in</button> \
<a href="forums/profile.php?mode=register" target="_blank">Register</a> \
      </div> \
      <div id="login_status" style="display: inline;"> </div> \
      <div id="privmsgs" style="display: inline;"> </div> \
   </div> \
	<div id="left_header" class="header left_width"> </div> \
	<div id="home" class="main" style="display: none;"> </div> \
	<div id="article_editor" class="main" style="display: none;"> </div> \
	<iframe id="forums" name="forums" src="about:blank" class="main" style="display: none;" width="100%" height="100%" frameborder="0"> </iframe> \
	<iframe id="comments" name="comments" src="about:blank" class="main" style="display: none;" width="100%" height="100%" frameborder="0"> </iframe> \
	<div id="chat" class="main" style="display: none;">Beta chat system. You need to be logged in to use it.</div> \
	<div id="debug" class="main" style="display: none;"> \
	   All vars: <div id="debug_var_list"> </div> \
	   <br/>Var values:<div id="debug_var"> </div> \
	   <br/>Changed:<div id="debug_var_changed"> </div> \
      <br/><input type="text" value="" name="debug_field" id="debug_field"/><button name="debug_eval" onclick="eval(\'alert(\'+div(\'debug_field\').value+\')\');">Eval</button> \
	</div> \
</div> \
</div>';

div('body').innerHTML += output;
output = '';




/** Build Library **/




//Article related functions

function getArticleID(loc) {
   if (loc == null)
      var loc = window.location;
   if (/\x2F([0-9a-f]{3,7})$/.test(loc)) {
      var reg = loc.toString().match(/\x2F([0-9a-f]{3,7})$/);
      return reg[1];
   }
   else
      return loc;
}

function getTemplateID(loc) {
   if (loc == null)
      var loc = window.location;
   if (/\x2F([AO][A-Za-z0-9+-]{14,33})$/.test(loc)) {
      var reg = loc.toString().match(/\x2F([AO][A-Za-z0-9+-]{14,33})$/);
      return reg[1].replace(/-/g, '/');
   }
   else
      return false;
}

function getChatID(loc) {
   if (loc == null)
      var loc = window.location;
   if (/\x2F(#[-_.0-9a-zA-Z]{0,20})$/.test(loc)) {
      var reg = loc.toString().match(/\x2F(#[-_.0-9a-zA-Z]{0,20})$/);
      return reg[1];
   }
   else
      return false;
}

//Switches the display style of the id_name element between 'none' and visible_display (default='inline'), or sets it accordingly to visible
function switch_visibility(id_name, visible, visible_display) {
   var d=div(id_name).style;
   if (visible == null) {
      visible = d.display == 'none';
   }
   if (visible_display == null) {
      visible_display = 'inline';
   }

   div(id_name).style.display = visible ? visible_display : 'none';
}


//Edit form management
//////////////////////

function insert(startText, endText) {
	var txtarea = document.forms.shack.code;

	txtarea.focus();
	var theSelection = false;


	if ((clientVer >= 4) && is_ie && is_win)
	{
      // We'll use this as a 'dummy'
      var stored_range = document.selection.createRange().duplicate();

		theSelection = document.selection.createRange().text; // Get text selection
		// Add tags around selection
		document.selection.createRange().text = startText + theSelection + endText;
		txtarea.focus();
		document.selection.createRange().setEndPoint('StartToStart', stored_range);
		document.selection.createRange().setEndPoint('EndToStart', stored_range);
		txtarea.focus();
		theSelection = '';
	}
	else {
	   // From http://www.massless.org/mozedit/
   	var selLength = txtarea.textLength;
   	var selStart = txtarea.selectionStart;
   	var selEnd = txtarea.selectionEnd;
   	if (selEnd == 1 || selEnd == 2)
   		selEnd = selLength;

   	var s1 = (txtarea.value).substring(0,selStart);
   	var s2 = (txtarea.value).substring(selStart, selEnd)
   	var s3 = (txtarea.value).substring(selEnd, selLength);
   	txtarea.value = s1 + startText + s2 + endText + s3;

      //Position cursor after startText
   	var setPos = selStart + startText.length;
      txtarea.setSelectionRange(setPos, setPos);
   }

   txtarea.focus();
   return;
}

//Switch textarea visibility, or set it to 'visible'
function switch_textarea(visible) {
   var d=div('edit_form').style;
   if (visible == null) {
      visible = d.display == 'none';
   }

   if (visible) {
      div('edit_button').innerHTML = 'Hide gwBBCode';
      d.display = '';
   }
   else {
      div('edit_button').innerHTML = 'Show gwBBCode';
      d.display = 'none';
   }
}

//Not sure if anyone uses this function
function show_hide(name) {
   var d=div(name).style;
   d.display = (d.display == 'none') ? '' : 'none';
}


//Show/hide delete button
function switch_delete(visible) {
   switch_visibility('delete', visible, 'inline');
}





//Categories & accessibility
/////////////////////////////


//Set selection of the public option
function set_category(cat) {
   var select = div('category');
   if (cat == null || cat == false || cat.length <= 1) {
      select.selectedIndex = 0;
   }
   else {
      for (var i=0; i<select.options.length; i++) { //can't use i in select since select is an object
         if (select.options[i].text == cat) {
            select.selectedIndex = i;
            break;
         }
      }
   }
}


//Set selection of the public option
function set_accessibility(access) {
   var select = div('accessibility');
   if (access == null || access == false || access.length <= 1) {
      select.selectedIndex = 0;
   }
   else {
      for (var i=0; i<select.options.length; i++) { //can't use i in select since select is an object
         if (select.options[i].text == access) {
            select.selectedIndex = i;
            break;
         }
      }
   }
}


//Returns the selected value of a select element
function get_option(select_name) {
   var select = div(select_name);
   if (select.selectedIndex < 0) {
      return select[0].text;
   }
   return select[select.selectedIndex].text;
}





//Edition
/////////

//Set the new edition radio box visibility to 'visible'
function switch_new_edition(visible) {
   switch_save_mode(visible ? 'edition' : 'article');
   if (visible) {
      div('new_edition').disabled = false;
   }
   else {
      //Check new_article before to disable new_edition
      div('new_edition').disabled = true;
   }
}

//Set the "Save as" radiobox to either 'edition' or 'article'
function switch_save_mode(mode) {
   if (mode == 'edition') {
      div('new_edition').checked = true;
      div('summary_form').style.display = 'inline';
   }
   else {
      //Check new_article before to disable new_edition
      div('new_article').checked = true;
      div('summary_form').style.display = 'none';
   }
}







//Voting
////////


//Update/create an article vote
function update_vote(vote, voters) {
   div('vote').getElementsByTagName('DIV')[0].style.backgroundPosition = Math.ceil(vote*65/5-65).toString()+'px 0px';
   div('voters').innerHTML = voters;
}

//Switch vote result visibility, or set it to 'visible'
function switch_vote(visible) {
   switch_visibility('vote_result', visible);
}


//Returns an element containing stars colored depending on the passed vote
var stars_node = false;
function stars_vote(vote) {
   //If necessary, create the model node
   if (stars_node === false) {
      //Party fill the stars_node div background with color
      stars_node = document.createElement('DIV');
      stars_node.style.display = 'inline';
      stars_node.style.backgroundImage = 'url(img/star_bg2.gif)';
      stars_node.style.backgroundRepeat = 'no-repeat';
   
      //Add the 5 stars image
      var stars_img = document.createElement('IMG');
      stars_img.src = 'img/stars.gif';
      stars_node.appendChild(stars_img);
   }

   //Clode model node
   var stars = stars_node.cloneNode(true);
   stars.style.backgroundPosition = Math.ceil(vote*65/5-65).toString()+'px 0px';

   return stars;
}




//Chat system
/////////////

var chat = new Object();
var chat_focus;
function ctrl_enter(e) {
   var code;
	if (!e) {
	   var e = window.event;
	}
   if (e.keyCode) {
      code = e.keyCode;
   }
	else if (e.which) {
	   code = e.which;
	}
   if ((code == 13) && (e.ctrlKey || e.shiftKey)) {
      chat_send(chat_focus);
   }
}

function chat_create(chan) {
   //Make sure chan doesn't exist already
   if (chat_exists(chan)) {
      return false;
   }
   
   //Prepare chat object
   chat[chan] = new Object();
   chat[chan]['users'] = new Array();

   //Output chat HTML code
   var output = ' \
<div id="chat_{id}"> \
{chan_title} \
<div id="chat_window_{id}" class="chat_window">{text}</div> \
<form onsubmit="chat_send(\'{id}\'); return false;" name="chat_form_{id}"> \
<textarea class="chat_input" id="chat_input_{id}" rows="{rows}" wrap="virtual" onfocus="chat_focus=\'{id}\'"></textarea><br/> \
<input type="submit" value="Send"/> \
</form><br/><br/> \
</div> \
   ';
   var rows = is_gecko ? 2 : 3;  //Firefox and Netscape show one more row for textareas
   var chan_title = '';
   var text = '';
   //Adapt to chat kind (any or status)
   if (chan != '#00000') {
   //Any chan
      chan_title = '<a href="{id}" onclick="return false;">Chat {id}</a> users: <div id="chat_users_{id}" style="display: inline"></div>';
   }
   else {
   //Status chan
      chan_title = 'Status<br/>';
      text = ' \
Command list<br/> \
<b>/create</b> to create a random chan<br/> \
<b>/join #&lt;chan_id&gt;</b> to join a chan, e.g: <i>/join #gwshack</i><br/> \
<b>/part</b> to leave a chan<br/> \
Tip: use ctrl+enter to send a message<br/> \
      ';
   }
   var new_chat = document.createElement('div');
   new_chat.innerHTML = output.replace(/\{rows\}/g, rows).replace(/\{chan_title\}/g, chan_title).replace(/\{id\}/g, chan).replace(/\{text\}/g, text);
   div('chat').insertBefore(new_chat, div('chat').firstChild);
   output = '';
   
   //Focus
   chat_focus = chan;
   div('chat_input_'+chat_focus).focus();
   if (left_tab_selected == 'Chat') {
      self.setTimeout('div(\'chat_input_'+chat_focus+'\').focus();', 500); //IE needs a delay
      div('chat_input_'+chat_focus).onkeydown = ctrl_enter;
   }

   return true;
}


function chat_destroy(chan) {
   //Make sure chan does exist
   if (!chat_exists(chan)) {
      return false;
   }
   
   var id = 'chat_'+chan;
   div(id).parentNode.removeChild(div(id));
   chat[chan] = null;
   return true;
}


function chat_add_user(chan, user_id, username, admin) {
   //Make sure chan doesn't exist already
   if (!chat_exists(chan)) {
      return false;
   }
   //Make sure user wasn't already added to chan
   if (typeof(chat[chan]['users'][user_id]) != 'undefined') {
      return false;
   }

   chat[chan]['users'][user_id] = new Object();
   chat[chan]['users'][user_id]['username'] = username;
   chat[chan]['users'][user_id]['admin'] = admin;
   var now = new Date();
   chat[chan]['users'][user_id]['join_time'] = now.getTime();
   chat_show_users(chan);
   return true;
}

function chat_remove_user(chan, user_id) {
   //Make sure chan doesn't exist already
   if (!chat_exists(chan)) {
      return false;
   }
   //Make sure user wasn't already removed from chan
   if (typeof(chat[chan]['users'][user_id]) != 'undefined') {
      return false;
   }

   chat[chan]['users'][user_id] = null;
   chat_show_users(chan);
   return true;
}

function chat_show_users(chan) {
   //Make sure chan doesn't exist already
   if (!chat_exists(chan)) {
      return false;
   }

   //Create an list of users ordered by join time
   var users = new Array();
   for (var i in chat[chan]['users']) {
      users.push(chat[chan]['users'][i]['username']);
   }
   div('chat_users_' + chan).innerHTML = users.join(', ');
   return true;
}
   

function chat_exists(chan) {
   return typeof(chat[chan]) != 'undefined';
}


//Send a message
function chat_send(chan) {
   var str_chan = chan.toString();
   //Don't bother continuing if user didn't type anything
   if (div('chat_input_'+str_chan).value == '') {
      return;
   }
   cout.chat_input = div('chat_input_'+str_chan).value;
   cout.chat_input_chan = str_chan;
   div('chat_input_'+str_chan).value = '';
   send_form('chat_send', false);
}

function switch_chat(visible) {
   if (visible && !chat_updating) {
      //And update it
      chat_updating = true;
      chat_update();
   }
   if (visible) {
      div('chat_input_'+chat_focus).focus();
      div('chat_input_'+chat_focus).onkeydown = ctrl_enter;
   }
   chat_updating = visible;
}

//Update chat window every 3s
var chat_updating = false;
function chat_update() {
   if (chat_updating) {
      send_form('chat_update');
      self.setTimeout('chat_update();', 3000);
   }
}




//Profile management
////////////////////

//Initialize profile management
var output = ' \
<div id="profile_name"></div> \
<br/> \
<div id="profile_guest" style="display: block;">This place is for profile and review editing: you need to be logged in and to view either your or someone else\'s profile.</div> \
<div id="profile_form"> \
Profile keywords:<br/> \
<textarea id="profile_keywords" rows="3" wrap="virtual"></textarea><br/> \
<div id="profile_edit" style="display: none;"><button onclick="send_form(\'profile\');">Save</button> Help</div> \
<br/> \
</div> \
<div id="review_form" style="display: none;"> \
Review keywords:<br/> \
<textarea id="profile_keywords" rows="1" wrap="virtual"></textarea><br/> \
<button onclick="send_form(\'profile\');">Save</button> Help<br/> \
</div> \
';
div('profile').innerHTML = output;
output = '';
//TODO: only send user's profile if he accesses his profile window



//Prepare tabs
//////////////

var right_tab_names = new Array('Att Points', 'Skills', 'gwBBCode syntax', 'Build Library', 'Profile', 'History');
var right_tab_hide = 'ProfileHistory';   //Hide history tab by default
var right_tab_selected = '';
function select_right_tab(tab) {
   var tab_changed = tab != right_tab_selected;
   cin.right_view = tab;
   cout.right_view = tab;
   right_tab_selected = tab;
   var out = '<ul>';
   var names = right_tab_names;
   //Hide all
   for (var i in names) {
      div(names[i].toLowerCase().replace(' ', '_').replace('/', '_')).style.display = 'none';
   }

   //Show the right window and prepare a new tab list
   for (var i in names) {
      //Make sure we want to show that tab
      if (right_tab_hide.indexOf(names[i]) == -1) {
         var name = names[i].toLowerCase().replace(' ', '_').replace('/', '_');
         if (names[i] == tab) {
            out += '<li id="tab_' + name + '"><span>' + tab + '</span></li>';
            div(names[i].toLowerCase().replace(' ', '_').replace('/', '_')).style.display = '';
            if (names[i] == 'gwBBCode syntax' && tab_changed) {
               get_article('3c573');
            }
            if (names[i] == 'Skills' && tab_changed) {
               var myWidth = 0, myHeight = 0;
               if( typeof( window.innerWidth ) == 'number' ) {
                  //Non-IE
                  myWidth = window.innerWidth;
                  myHeight = window.innerHeight;
               } else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
                  //IE 6+ in 'standards compliant mode'
                  myWidth = document.documentElement.clientWidth;
                  myHeight = document.documentElement.clientHeight;
               } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
                  //IE 4 compatible
                  myWidth = document.body.clientWidth;
                  myHeight = document.body.clientHeight;
               }
               seth('skills', myHeight - geth('right_header') - geth('copyright') - 40);
               seth('skill_list', geth('skills') - geth('search') - 7);
            }
         }
         else {
            out += '<li id="tab_' + name + '"><a href="#" onClick="select_right_tab(\'' + names[i] + '\'); return false;">' + names[i] + '</a></li>';
         }
      }
   }
	out += '</ul>';
	div('right_header').innerHTML = out;
	out = '';
}

//Add a right tab and shows it
function hide_right_tab(tab) {
   if (right_tab_hide.indexOf(tab) == -1) {
      right_tab_hide += tab;
      if (tab != right_tab_selected) {
         select_right_tab(right_tab_selected);
      }
      else {
         select_right_tab('Build Library');
      }
   }
}

//Remove a right tab and shows it
function show_right_tab(tab) {
   if (right_tab_hide.indexOf(tab) != -1) {
      right_tab_hide = right_tab_hide.replace(tab, '');
      select_right_tab(right_tab_selected);
   }
}

//Add or remove a right tab, depending on visible
function switch_right_tab(tab, visible) {
   if (visible) {
      show_right_tab(tab);
   }
   else {
      hide_right_tab(tab);
   }
}







var loading_discussion = false;
var left_tab_names = new Array('Home', 'Article editor', 'Comments', 'Forums', 'Debug');
   //'Comments' is used elsewhere, watch out if you change its name
var left_tab_hide = 'CommentsDebug';   //Hide debug tab by default
//DEBUG: add 'Debug' to the list of hidden views
var left_tab_selected = '';
function select_left_tab(tab) {
   var tab_changed = tab != left_tab_selected;
   cin.left_view = tab;
   cout.left_view = tab;
   left_tab_selected = tab;
   var out = '<ul>';
   var names = left_tab_names;
   //Hide all
   for (var i in names) {
      div(names[i].toLowerCase().replace(' ', '_').replace('/', '_')).style.display = 'none';
   }

   //Show the left window and prepare a new tab list
   for (var i in names) {
      //Keep the tab name intact
      var name = names[i].toLowerCase().replace(' ', '_').replace('/', '_');
      if (tab_div = div('tab_'+name)) {   //We have to check cause tab divs may not exist yet
         tab_name = tab_div.getElementsByTagName('A')[0] || tab_div.getElementsByTagName('SPAN')[0];
         tab_name = tab_name.innerHTML;
      }
      else {
         tab_name = names[i];
      }

      //Make sure we want to show that tab
      if (left_tab_hide.indexOf(names[i]) == -1) {
         if (names[i] == tab) {
            out += '<li id="tab_' + name + '"><span>' + tab_name + '</span></li>';
            div(names[i].toLowerCase().replace(' ', '_').replace('/', '_')).style.display = '';
            if (tab == 'Forums' && !loaded_forums) {
               loaded_forums = true;
               frames['forums'].location.href = 'forums_small/index.php';
            }
            if (tab == 'Comments' && cin.shown_id.length == 4 && !loading_discussion) {
               loading_discussion = true;
               self.setTimeout('loading_discussion = false;', 1000);
               frames['comments'].location.href = 'forums_small/viewtopic.php?t=' + cin.topic_id;
            }
            if (tab_changed && registered_user) {
               switch_chat(tab == 'Chat');
            }
         }
         else {
            out += '<li id="tab_' + name + '"><a href="#" onClick="select_left_tab(\'' + names[i] + '\'); return false;">' + tab_name + '</a></li>';
         }
      }
   }
	out += '</ul>';
	div('left_header').innerHTML = out;
	out = '';
}

//Add a left tab and shows it
function hide_left_tab(tab) {
   if (left_tab_hide.indexOf(tab) == -1) {
      left_tab_hide += tab;
      if (tab != left_tab_selected) {
         select_left_tab(left_tab_selected);
      }
      else {
         select_left_tab('Chat');
      }
   }
}

//Remove a left tab and shows it
function show_left_tab(tab) {
   if (left_tab_hide.indexOf(tab) != -1) {
      left_tab_hide = left_tab_hide.replace(tab, '');
      select_left_tab(left_tab_selected);
   }
}

//Add or remove a left tab, depending on visible
function switch_left_tab(tab, visible) {
   if (visible) {
      show_left_tab(tab);
   }
   else {
      hide_left_tab(tab);
   }
}



//Edit form layout
//////////////////

var output = ' \
<div id="message"></div> \
<span id="shown_article_name" style="float: left; padding-bottom: 7px;"></span> \
<div id="vote_block" style="float: right"> \
   <div id="vote_result" style="display: none;"> \
      <div id="vote"></div>&nbsp;(<div id="voters"></div> votes) \
   </div> \
   <div id="voting" style="display: none;">&nbsp;&nbsp; \
      <select id="user_vote" name="user_vote" style="overflow: visible; display: inline;"> \
         <option value="-" selected >-</option> \
         <option value="1">1</option> \
         <option value="2">2</option> \
         <option value="3">3</option> \
         <option value="4">4</option> \
         <option value="5">5</option> \
      </select> \
      <button id="vote_button" name="vote_button" onclick="send_form(\'vote\');">Vote</button> \
   </div> \
</div> \
 \
<div style="clear: both"> \
<div id="preview_html_div"></div> \
<div id="edit"><br/> \
<button id="edit_button" onclick="switch_textarea();">Hide gwBBCode</button> \
<a href="3c573" onclick="get_article(this.href); return false;">Learn how to use gwBBCode</a>.. or use <a href="http://www.GuildBuilds.org" target="_blank">GuildBuilds Designer</a>!<br/>\
<form id="edit_form" name="shack" method="post" action="index.php" style="display: block;" onsubmit="return false;" AUTOCOMPLETE="off"> \
   Article name: <input id="article_name" name="article_name" value="" type="text" size="45" maxlength="60"/><br/> \
   <span id="category_form">Category: <select id="category" name="category" style="overflow: visible;"></select></span> \
   <span id="accessibility_form">Accessibility: <select id="accessibility" name="accessibility" style="overflow: visible;" disabled>\
      <option value="Private" selected>Private</option>\
      <option value="Public">Public</option>\
   </select></span><br/> \
   <input id="template_id" name="template_id" value="" type="text" size="45" maxlength="80" style="font-family: courier;"/><button id="add_template" name="add_template" onClick="insert(cout.template_code+\'\\n\', \'\');">Add template</button>\
   <button id="add_build" name="add_build" onClick="insert(\'[build prof=?/? name=\\x22\\x22 desc=\\x22\\x22]\', \'[/build]\')">Add blank build</button>\
   <div id="template_html_div" style="display: inline;"></div>\
   <textarea id="code" name="code" rows="18" wrap="virtual"></textarea><br/> \
   <div id="summary_form" style="display: none;">Edit Summary: <input name="summary" value="" type="text" size="45" maxlength="200"/><br/></div> \
   <button name="preview" onclick="get_preview();" style="float: left;">Preview</button> \
   <button name="post" onclick="send_article(\'submit\');" style="float: left;">Save as</button> \
   <div style="float: left;"> \
      <input id="new_edition" type="radio" name="post_type" value="new_edition" onclick="switch_save_mode(\'edition\');" disabled>new edition of this article</input><br/> \
      <input id="new_article" type="radio" name="post_type" value="new_article" onclick="switch_save_mode(\'article\');" checked>new article</input> \
   </div> \
   <button name="delete" id="delete" onclick="delete_article();" style="float: right; display: none;">Delete</button> \
</form> \
</div> \
</div>';
div('article_editor').innerHTML = output;
output = '';

//Show stars to be modified later
var stars = stars_vote(0);
div('vote').appendChild(stars);

var categories = new Array('Categoryless', 'Heroes\' Ascent 8 man', 'Heroes\' Ascent 6 man', 'Guild Battle', 'Hero Battle', 'Alliance Battle', 'Team Arena', 'Random Arena', 'Farming', 'Trash');
for (var i in categories) {
   var cat_node = document.createElement('option');
   cat_node.option = categories[i];
   var cat_text = document.createTextNode(categories[i]);
   cat_node.appendChild(cat_text);
   div('category').appendChild(cat_node);
}

/*
      <option value="Guild Battle" selected >Guild Battle</option> \
      <option value="GvG">GvG</option> \
      <option value="Heroes\' Ascent">Heroes\' Ascent</option> \
      <option value="Team Arena">Team Arena</option> \
      <option value="Random Arena">Random Arena</option> \
*/





function send_article() {
   var action = div('new_article').checked ? 'new_article' : 'new_edition';
   if (action == 'new_edition' && get_option('accessibility') == 'Public' && cin.accessibility == 'Private') {
      var sure = confirm('By turning this article public you make it undeletable and allow moderators to edit it.\nAre you sure you want to continue?');
      if (!sure) {
         return false;
      }
   }
   return send_form(action);
}


function get_article(url) {
   var id = getArticleID(url)
   //How to
   if (id == '3c573') {
      //Switch to gwBBCode syntax tab if necessary (to avoid infinite loops)
      if (div('gwbbcode_syntax').style.display == 'none') {
         return select_right_tab('gwBBCode syntax');
      }
      //Only load the how to if it's not already
      if (cin.howto.length != '')
         return false;
      send_form('howto');
      return false;
   }

   //Article
   cout.shown_id = id;
   send_form('get');
   select_left_tab('Article editor');
   return false;
}


function get_preview() {
   cout.edited_name = document.forms.shack.article_name.value.htmlEntities();
   return send_form('preview');
}


//Check if user really wants to delete, and do it
function delete_article() {
   var sure = confirm('Are you sure you want to delete this article?');
   if (!sure) {
      return false;
   }
   send_form('delete');
}



//Template help and preview
var help_msg = '<paste template code here>';
div('template_id').value = help_msg;
div('add_template').disabled = true;
div('template_id').onfocus = hide_template_help;
div('template_id').onblur = show_template_help;
div('template_id').onkeyup = preview_template;

function show_template_help() {
   if (this.value == '') {
      this.value = help_msg;
   }
}

function hide_template_help() {
   if (this.value == help_msg) {
      this.value = '';
   }
}

var prev_template_code = '';
function preview_template() {
   if (this.value == prev_template_code) {
      return;
   }
   prev_template_code = this.value;
   if (this.value.length == 0) {
      div('template_html_div').innerHTML = '';
      div('add_template').disabled = true;
      return;
   }
   cout.template_id = encodeURIComponent(this.value);
   return send_form('preview_template');
}







function send_form(action, show_loading) {
   cout.action = action;
   rpc_send(show_loading);
}

var cin = new Object();
var cout = new Object();
var resp = new Object();
var changed = new Object();

function rpc_intialize() {
   //Initialize cin
   cin.action = '';
   cin.edited_code = '';
   cin.edited_name = '';
   cin.username = 'Anonymous';
   cin.password = '';
   cin.autologin = 0;
   cin.user_vote = 0;
   cin.user_id = -1;
   cin.is_moderator = false;
   cin.vote_result = 0;
   cin.voters = '';
   cin.redirect = '';
   cin.category = 'Categoryless';
   cin.accessibility = 'Private';
   cin.message = '';    // != status
   cin.shown_id = '';
   cin.article_id = '';
   cin.comment_count = 0;
   cin.topic_id = 0;
   cin.preview_html = '';
   cin.howto = '';
   cin.author_id = -1;
   cin.author_name = '';
   cin.summary = '';
   cin.history = '';
   cin.deleted = false;
   cin.left_view = 'Home';
   cin.right_view = 'Att Points';
   cin.privmsgs = 0;
   cin.login_status = '';
   cin.update_input = false;

   //Chat system
   cin.chat_input = '';
   cin.chat_input_chan = '';
   cin.chat_new_lines = '';

   //Hoh win countdown
   cin.hoh_sync = 0;
   cin.hoh_time_left = 6*60*1000;

   //Profile management
   cin.profile_id = -1;
   cin.profile_name = '';
   cin.profile_keywords = '';
   cin.review_keywords = '';

   //Template management
   cin.template_id = '';
   cin.template_code = '';
   cin.template_html = '';

   //Build Library
   cin.build_conditions = '';

   //Initialise cout
   for (var i in cin) {
      cout[i] = cin[i];
   }

   //Hoh win sync time
   cout.hoh_sync = (60 + Math.round(10*60*Math.random())) * 1000; //Resync between 1 and 11 minutes.
}


function rpc_cout_update() {
   cout.edited_code = document.forms.shack.code.value;
   cout.edited_name = document.forms.shack.article_name.value;
   cout.username = div('username').value;
   cout.password = div('password').value;
   cout.autologin = (div('autologin').src.indexOf('img/false.gif') == -1) ? 1 : 0;
   cout.user_vote = get_option('user_vote');
   cout.category = get_option('category');
   cout.accessibility = get_option('accessibility');
   cout.summary = document.forms.shack.summary.value;
}


rpc_intialize();
rpc_recv_update();
function rpc_send(show_loading) {
   //Prepare POST content
   rpc_cout_update();
   var request = '';
   for (var i in cin) {
      //Produce request value of i or update cin
      //Set changed[i] if user modified anything
      if (cout[i] != cin[i]) {
         request += '&'+i+'='+encodeURIComponent(cout[i]);
         cin[i] = cout[i];
         changed[i] = true;
      }
      else {
         changed[i] = false;
      }
   }

   //Send POST content
   if (show_loading == null || show_loading) {
      div('loading').style.visibility = 'visible';
      div('loading').innerHTML = 'Loading...';
   }
   var HTTPreq = new LXMLHttp('POST', 'gwshack.js.php?rand='+Math.round(1000*Math.random()), true);
   //If window is getting closed, don't wait for server response
   if (cout.action == 'close') {
      HTTPreq.async = false;
   }
   HTTPreq.oncomplete = rpc_received;
   HTTPreq.onerror = function(obj) {div('loading').innerHTML = 'An error occured.';}
   HTTPreq.open();
   HTTPreq.setRequestHeader("Content-Type", 'application/x-www-form-urlencoded');
   HTTPreq.setRequestHeader("Referer", 'gwshack.js');
   HTTPreq.send('session_id='+session_id+request);
}

//Receive server changes and execute one at a time
var received_objs = new Array();
var receiving_already = false;
function rpc_received(obj) {
   //Already executing previous response
   if (receiving_already) {
      if (typeof(obj) != 'number') {
         received_objs.push(obj);
         obj = 0;
      }
      if (obj < 5) {
         obj++;
         self.setTimeout('rpc_received('+obj+');', 1000);
         return;
      }
   }

   receiving_already = true;
   if (typeof(obj) == 'number') {
      obj = received_objs.shift();
   }
   div('loading').style.visibility = 'hidden';
   resp = new Object();
   eval(obj.responseText);
   rpc_recv_update();
   rpc_receiving();
   receiving_already = false;
}


//Update changed, add resp to cin, set cout to cin
function rpc_recv_update() {
   //changed[i] was set by rpc_send in case of a user change
   for (var i in cin) {
      if (resp[i] != null) {
         cin[i] = resp[i]
         cout[i] = cin[i];
         changed[i] = true;
      }
   }
}

















var registered_user = false;
function rpc_receiving() {
   //Assign some variables to simplify boolean equations
   registered_user = cin.user_id > 0;

   //Debug mode
   if (cin.user_id == 2 || cin.user_id == 3) {
      var var_list = '';
      var var_changed = '';
      for (var i in cin) {
         var var_list_string = quoted(i) + ', ' + quoted(changed[i]) + ', ' + quoted(cin[i]) + ', ' + quoted(cout[i]);
         var_list += '<a href="" onclick="debug_var(' + var_list_string + '); return false;">' + i + '</a>, ';
         if (changed[i]) {
            var_changed += '<a href="" onclick="debug_var(' + var_list_string + '); return false;">' + i + '</a>, ';
         }
      }
      div('debug_var_list').innerHTML = var_list;
      div('debug_var_changed').innerHTML = var_changed + '<br/>' + div('debug_var_changed').innerHTML;
      var_list = '';
      var_changed = '';
   }


   //Viewing a redirected article
   if (changed.redirect && cin.redirect != '') {
      cin.message += '<br/>Your were redirected to <a href="'+cin.redirect+'" onclick="get_article(this.href); return false;">gwShack.us/'+cin.redirect+'</a>';
      changed.message = true;
   }

   //Article name change
   if (changed.edited_name) {
      var article_header = '<a href="' + cin.shown_id + '" onclick="return false;">' + cin.edited_name.htmlEntities() + '</a><span class="author_name" style="font-size : 12px;"> by ' + cin.author_name + '</span>' + '<br/>';
      div('shown_article_name').innerHTML = article_header;
   }

   //Article name input change
   if (changed.edited_name || changed.update_input) {
      if (cin.update_input) {
         div('article_name').value = cin.edited_name;
         cout.update_input = false;
      }
   }

   //Article code input change
   if (changed.edited_code || changed.update_input) {
      if (cin.update_input) {
         div('code').value = cin.edited_code;
         cout.update_input = false;
      }
   }

   //Article html code change
   if (changed.preview_html) {
      div('preview_html_div').innerHTML = cin.preview_html;
   }

   //Template html code change
   if (changed.template_html) {
      div('template_html_div').innerHTML = cin.template_html;
      add_remove_img(div('template_html_div'), "div('template_html_div').innerHTML = '';");
      div('add_template').disabled = !cin.template_code.length;
   }

   //Viewing a deleted article
   //Has to be after html code update
   if (changed.deleted) {
      if (cin.deleted) {
         div('preview_html_div').innerHTML = 'This article doesn\'t exist anymore.';
      }
   }

   //Howto action
   if (changed.howto) {
      div('gwbbcode_syntax').style.fontSize = "12px";
      div('gwbbcode_syntax').innerHTML = cin.howto;
   }

   //Show/hide discussion view
   if (changed.shown_id || changed.accessibility) {
      switch_left_tab('Comments', cin.accessibility == 'Public');
   }
   
   //Show category changes
   if (changed.category) {
      set_category(cin.category);
   }

   //Show accessibility changes
   if (changed.accessibility) {
      set_accessibility(cin.accessibility);
   }

   //Show/hide edit ability and select category
   if (changed.user_id || changed.author_id || changed.article_id) {
      var editable = cin.shown_id.length > 0 && cin.shown_id.length != 7 && ((registered_user && cin.user_id == cin.author_id) || cin.is_moderator);  //TODO: is first condition of any use?
      switch_new_edition(editable);
      //Show/hide delete button
      switch_delete(editable && cin.shown_id.length == 5);  //TODO: shown_id or article_id?
      //Allow/prevent to/from changing a public article category and accessibility
      div('category_form').getElementsByTagName('select')[0].disabled
         = cin.article_id.length && (!editable || (cin.accessibility == 'Public' && cin.category != 'Categoryless' && !cin.is_moderator));
      div('accessibility_form').getElementsByTagName('select')[0].disabled = (cin.article_id.length || !registered_user) && (!editable || cin.accessibility == 'Public');
   }

   //Show/hide article code
   if (changed.author_id || changed.user_id) {
      switch_textarea(cin.action == 'firsttime' || cin.author_id == cin.user_id);
   }
   

   //Change left and right view
   if (changed.left_view) {
      select_left_tab(cout.left_view);
   }
   if (changed.right_view) {
      select_right_tab(cout.right_view);
   }

   //Show/hide history
   if (changed.history) {
      div('history').innerHTML = cin.history;
      switch_right_tab('History', cin.history.length > 0);
   }

   //Show/hide comments and their count
   if (changed.topic_id) {
      switch_left_tab('Comments', cin.topic_id > 0);
      //Show how many comments there are
      var tab_name = 'Comment';
      if (cin.comment_count > 1) {
         tab_name += 's';
      }
      if (div('tab_comments')) {
         var tab = div('tab_comments').getElementsByTagName('A')[0] || div('tab_comments').getElementsByTagName('SPAN')[0];
         tab.innerHTML = tab_name + ' (' + cin.comment_count + ')';
      }
   }


   //Logging in
   if (changed.user_id && registered_user) {
      var msg = ''
      //Say welcome
      div('login_status').innerHTML = 'Welcome, '+cin.username+'. <a style="float: right;" onclick="send_form(\'log out\'); return false;" href="">Logout</a>';
   	//Hide login form
      div('login_form').style.display = 'none';
      resize();
      //Clean the password field up
      div('password').value = '';
      div('username').value = cin.username;
      cout.password = '';  //TODO: Should be done on both sides automatically, but how?
   }

   //Logging out
   if (changed.user_id && !registered_user) {
   	//Show login form again
      div('login_form').style.display = 'inline';
      div('login_status').innerHTML = '';
      resize();
   }

   //Updating private messages
   if (changed.privmsgs) {
      var msg = '';
   	if (cin.privmsgs == 1) {
   	   msg = 'You have 1 message';
   	}
   	else if (cin.privmsgs > 1) {
   	   msg = 'You have '+cin.privmsgs.toString()+' new messages';
   	}
   	if (msg.length > 0) {
   	   div('privmsgs').innerHTML = ' <a href="forums/privmsg.php?folder=inbox" onclick="return forums_open(\'privmsg.php?folder=inbox\');">'+msg+'</a>';
   	}
   	else {
   	   div('privmsgs').innerHTML = '';
   	}
   }

   //Login status change
   if (changed.login_status) {
      //Output msg and resize windows
      div('login_status').innerHTML = cin.login_status;
      resize();
   }

   //Renewing forums
   if (changed.user_id) {
      //Update the forum if we were on the index.php page
      forums_renew();
   }


   //Manage voting results
   if (changed.shown_id || changed.user_vote || changed.user_id) {
      if (cin.shown_id.length == 4) {
         //Update vote
         if (cin.voters >= 2) {
            update_vote(cin.vote_result, cin.voters);
         }
         //Show vote
         switch_vote(cin.voters >= 2);
         //Allow voting
         switch_visibility('voting', registered_user);
      }
      else {
         //Hide vote
         switch_vote(false);
         //Disable voting
         switch_visibility('voting', false);
      }
   }

   //Set user vote selection
   if (changed.user_vote) {
      div('user_vote').selectedIndex = cin.user_vote;
      div('user_vote').getElementsByTagName('OPTION')[0].disabled = cin.user_vote != 0;   //If user voted, prevent blank vote
   }



   //Chat window update
   if (changed.chat_new_lines && cin.chat_new_lines.length > 0) {
      //chat_users
      //xxx
      var lines = cin.chat_new_lines;
      while ((regs = lines.match(/^([^:]+):([^:]+):([^:]+):([^:]+)::/)) && regs.length == 5) {
         lines = lines.replace(/^([^:]+):([^:]+):([^:]+):([^:]+)::/, '');
         var window_content = '';
         var chan        = regs[1];
         var author_id   = regs[2];
         var author_name = regs[3];
         var text        = regs[4].replace(/&x3A;/g, ':');
         var chat_window = 'chat_window_' + chan.toString();
         var update_chat_window = true;
         
         //Join command
         if (text == '/join') {
            if (author_id == cin.user_id) {
            //Current user is joining a chan
               chat_create(chan);
            }
            //If we're not in the status chan, inform that user is joining a chan
            if (chan != '#00000') {
               if (div(chat_window).innerHTML.length > 0 || window_content.length > 0) {
                  window_content += '<br/>';
               }
               window_content += author_name + ' has joined.';
               chat_add_user(chan, author_id, author_name, 0);
            }
         }

         //Part command
         else if (text == '/part') {
            //Inform that user is leaving a chan
            window_content += '<br/>' + author_name + ' has left.';
            //Current user is leaving a chan
            chat_remove_user(chan, author_id);
            if (author_id == cin.user_id) {
               chat_destroy(chan);
               update_chat_window = false;
            }
         }
         
         //Isin command
         else if (text == '/isin') {
            chat_add_user(chan, author_id, author_name, 0);
         }

         //Text message
         else {
            //Any chan
            if (chan != '#00000') {
               window_content += '<br/>&lt;' + author_name + '> ' + text;
            }
            //Status chan
            else {
               window_content += text;
            }
         }
         
         if (update_chat_window) {
            //Write in chat window and scroll down
            div(chat_window).innerHTML += window_content;
            div(chat_window).scrollTop = div(chat_window).scrollHeight;
         }
         
      }

      //Reset the new received lines since we parsed them
      cout.chat_new_lines = '';
   }
   
   //Sent a message to chat so chat view should be opened
   if (cin.action == 'chat_send' && left_tab_selected != 'Chat') {
      select_left_tab('Chat');
   }



   //Show/hide profile guest message
   if (changed.user_id) {
      switch_visibility('profile_guest', !registered_user, 'block');
   }

   //Show/hide profile form elements
   if (changed.user_id || changed.profile_id) {
      //Show/hide review form, i.e prevent guests from reviewing
      var allow_review = registered_user && cout.profile_id != -1 && cout.profile_id != cout.user_id;
      switch_visibility('review_form', allow_review, 'block');
      //Show/hide guest message
      var allow_profile_edit = registered_user && cout.profile_id == cout.user_id;
      switch_visibility('profile_edit', allow_profile_edit, 'block');
   }

   //Hoh sync
   if (changed.hoh_time_left) {
      remain.setTime(cin.hoh_time_left);
   }


   //Show message if any
   if (changed.message) {
      if (cin.message != '') {
         div('message').innerHTML = cin.message.replace(/^<br\/>/, '')+'<br/><hr align="left" width="10%"/>';
         cout.message = '';
      }
      else {
         div('message').innerHTML = '';
      }
   }
}










































































//Forums
////////

//Open var uri in the Forums tab and switch to it
var loaded_forums = false;
function forums_open(uri) {
   loaded_forums = true;
   frames['forums'].location.href = 'forums_small/' + uri;
   select_left_tab('Forums');
   return false;
}

//Refresh forum's index.php if that's what user views
function forums_renew() {
   if (frames['forums'].location.href.indexOf('index.php') != -1) {
      frames['forums'].location.href = 'forums_small/index.php';
   }
}




//Home page
///////////

var output = ' \
<img src="img/gwshack_logo.jpg"/><br/> \
<div id="welcome"> \
<b>If you\'re new here</b>, check out our <a href="" onclick="select_right_tab(\'Build Library\'); return false;">Build Library</a> on the right. Then to write your own builds, go to the <a href="" onclick="select_left_tab(\'Article editor\'); return false;">Article editor</a> view and help yourself with the <a href="" onclick="select_right_tab(\'Skills\'); return false;">Skills</a> and <a href="" onclick="select_right_tab(\'gwBBCode syntax\'); return false;">gwBBCode syntax</a> tabs.<br/> \
Registering allows you to edit your builds, make them public and vote. \
</div><br/><br/> \
<div id="news"> \
\
<div>\
<span class="date">23 July 2008</span> \
<h1>gwBBCode 1.8.3</h1> \
<p>Here is <a href="gwbbcode-1.8.3.zip">gwBBCode 1.8.3</a> including:<br/>\
 *Feature: PvP/PvE skill support (set default side in config.inc.php at GWBBCODE_GAMEPLAY);<br/> \
 *Feature: templates can now be copied even if they are invalid (=> template art);<br/> \
 *Feature: PvE only skill descriptions are now adapted to their rank;<br/> \
 *Update: updated skill database.<br/> \
<a href="forums/viewtopic.php?t=14242" onclick="return forums_open(\'viewtopic.php?t=14242\');" class="comments">Details</a> \
</p> \
</div><br/><br/>\
\
<div>\
<span class="date">9 July 2008</span> \
<h1>Tournament builds - June 2008</h1> \
<p>Here are the teambuilds from the <a href="http://guildwars.com/competitive/tournament2008/june/" target="_blank">June 2008 Tournament Championship</a>: <a href="a87b" onclick="return get_article(this.href);">GvG</a> and <a href="6bd5" onclick="return get_article(this.href);">HvH</a>.<br/> \
These are huge so they will take a long time to load.<br/> \
<a href="forums/viewtopic.php?t=14209" onclick="return forums_open(\'viewtopic.php?t=14209\');" class="comments">Comments</a> \
</p> \
</div><br/><br/>\
\
<div>\
<span class="date">2 July 2008</span> \
<h1>Tournament builds - January to May 2008</h1> \
<p>Here are all builds we extracted from the <a href="http://guildwars.com/competitive/tournament2008/" target="_blank">2008 Tournament Championship Skill Usage Charts</a> at the official GuildWars website. On bars (56 to 112 per link), skills are listed from most to least used. These take a long time to load.<br/>\
January (<a href="8bdd" onclick="return get_article(this.href);">GvG</a>, <a href="b719" onclick="return get_article(this.href);">HvH</a>), \
February (<a href="7fbf" onclick="return get_article(this.href);">GvG</a>, <a href="7f86" onclick="return get_article(this.href);">HvH</a>), \
March (<a href="fa3f" onclick="return get_article(this.href);">GvG</a>, <a href="d13d" onclick="return get_article(this.href);">HvH</a>), \
April (<a href="788c" onclick="return get_article(this.href);">GvG</a>, <a href="858c" onclick="return get_article(this.href);">HvH</a>), \
May (<a href="2829" onclick="return get_article(this.href);">GvG</a>, <a href="bcce" onclick="return get_article(this.href);">HvH</a>).<br/> \
<a href="forums/viewtopic.php?t=1822" onclick="return forums_open(\'viewtopic.php?t=1822\');" class="comments">Comments</a> \
</p> \
</div><br/><br/>\
\
<div>\
<span class="date">15 March 2008</span> \
<h1>March 13, 14 and 15 downtime</h1> \
<p>Sorry guys for this downtime :(<br/> \
Two critical files to the use of the database were deleted for no reason. Database is intact and these missing files were restored, but I can\'t tell if this problem will happen again.<br/> \
Once again sorry for the inconvenience!<br/> \
<a href="forums/viewtopic.php?t=2071" onclick="return forums_open(\'viewtopic.php?t=2071\');" class="comments">Comments</a> \
</p> \
</div><br/><br/>\
\
<div>\
<span class="date">17 January 2008</span> \
<h1>gwBBCode 1.8.1 with today\'s skill update</h1> \
<p>Here is <a href="gwbbcode-1.8.1.zip">gwBBCode 1.8.1</a> including latest skill update (see <a href="4cc1b" onclick="return get_article(this.href);">how skills changed</a>).<br/>\
 *Feature: SQL support for phpBB3, by jhanda_los;<br/> \
 *Feature: syntax for multiple file databases (e.g [Empathy:some_text] uses description from skill_db_1_some_text.php);<br/> \
 *Feature: syntax for wiki-like links (e.g [[Empathy|some text] shows "some text" instead of "Empathy");<br/> \
 *Update: up-to-date skill database;<br/> \
 *Update: simpler installation procedure (forum styles/templates are no longer modified).<br/> \
<a href="forums/viewtopic.php?t=2005" onclick="return forums_open(\'viewtopic.php?t=2005\');" class="comments">Details</a> \
</p> \
</div><br/><br/>\
\
<div>\
<span class="date">24 December 2007</span> \
<h1>New server on Christmas Eve</h1> \
<p>Random reboots and shutdowns lasting for hours forced gwShack to be moved to yet another place.<br/> \
Let\'s hope we\'ll be able to call this place /home :p and Merry Christmas from the gwShack team! *&lt;:-D<br/> \
<a href="forums/viewtopic.php?t=1993" onclick="return forums_open(\'viewtopic.php?t=1993\');" class="comments">Comments</a></p> \
</div><br/><br/>\
\
<div>\
<span class="date">30 November 2007</span> \
<h1>Skill Update - Thursday November 29 2007</h1> \
<p>Illustrated list of latest <a href="0a3be" onclick="return get_article(this.href);">skill update</a>!<br/> \
Got some issues with gwBBCode for it to be released with this update :(</p> \
</div><br/><br/>\
\
<div>\
<span class="date">15 November 2007</span> \
<h1>Skill update - Thuesday November 13 2007</h1> \
<p>Illustrated list of latest <a href="02500" onclick="return get_article(this.href);">skill update</a>!</p> \
</div><br/><br/>\
\
<div>\
<span class="date">11 November 2007</span> \
<h1>Skill update - Thursday November 8 2007</h1> \
<p>Check out the illustrated list of latest <a href="ae125" onclick="return get_article(this.href);">skill update</a>!<br/> \
A minor gwBBCode release should follow in the next few days for you to enjoy it ^^</p> \
</div><br/><br/>\
\
<div>\
<span class="date">14 October 2007</span> \
<h1>gwBBCode 1.8.0</h1> \
<p>Here is <a href="gwbbcode-1.8.0.zip">gwBBCode 1.8.0</a>!<br/>\
* Feature: Added Eye of the North skills (<a href="7e893" onclick="return get_article(this.href);">list</a>);<br/> \
* Feature: Now showing the effect of all primary attributes;<br/> \
* Update: Skill database entirely extracted from Guild Wars wiki;<br/> \
* Update: Friday October 12 skill update (<a href="91963" onclick="return get_article(this.href);">list</a>, slow loading);<br/> \
* Fix: Template codes getting bugged by skill ids above 2048.<br/> \
<a href="forums/viewtopic.php?t=1919" onclick="return forums_open(\'viewtopic.php?t=1919\');" class="comments">Details</a> \
</p> \
</div><br/><br/>\
\
<div>\
<span class="date">14 October 2007</span> \
<h1>Skill update - Friday October 12 2007</h1> \
<p>gwShack was down yesterday because of DNS issues. Once again I\'m sorry for any inconvenience and will do my best for history not to repeat :(<br/> \
Two gwShack articles you may find interesting: <a href="91963" onclick="return get_article(this.href);">October 12 skill update listing</a> (a bit slow to load), and a <a href="7e893" onclick="return get_article(this.href);">list of EotN skills</a><br/> \
<a href="forums/viewtopic.php?t=1918" onclick="return forums_open(\'viewtopic.php?t=1918\');" class="comments">Comments</a> \
</p> \
</div><br/><br/>\
\
<div>\
<span class="date">23 September 2007</span> \
<h1>gwShack is back online</h1> \
<p>And boy it wasn\'t easy: for a week now I\'ve been randomly disconnected at a frequency of one minute every 8 minutes.. Anyway I moved to a much better host generously offered by gcardinal (founder of <a href="http://www.pvxwiki.com/wiki/Main_Page">PvXwiki.com</a>)<br/> \
Thanks a lots gcardinal ^^ The database is fully recovered and shouldn\'t suffer any loss; your first build loadings should be slowed by skill icons being downloaded once and for all from this new server.<br/>\
I hope you\'ll enjoy this new place. Please post about any bug you may encounter! Cheers ^^<br/>\
<a href="forums/viewtopic.php?t=1887" onclick="return forums_open(\'viewtopic.php?t=1887\');" class="comments">Comments</a> \
</p> \
</div><br/><br/>\
\
<div>\
<span class="date">8 September 2007</span> \
<h1>Database down I repeat, Database down!</h1> \
<p>This is pretty bad: gwShack\'s database disappeared last night, and since I was on vacation, my last backup is one month old.<br/>\
I contacted the support, hopping for them to have something, but I doubt they will. As soon as they answer me, gwShack will be back online prolly on month back in time.<br/>\
I\'m really sorry about that :(</p> \
</div><br/><br/>\
\
<div>\
<span class="date">8 August 2007</span> \
<h1>Tournament builds</h1> \
<p>Here are all builds we extracted from the <a href="http://www.guildwars.com/competitive/funseason/tseries2007/julychampionship/skillusage/default.php?lang=en" target="_blank">July 2007 Tournament Championship Skill Usage Charts</a> at the official GuildWars website. On bars, skills are listed from most to least casted.<br/>\
GvGs: Final (<a href="c217" onclick="return get_article(this.href);">NoT vs vD</a>), \
Semi (<a href="3518" onclick="return get_article(this.href);">Lz vs vD</a>, \
<a href="df6f" onclick="return get_article(this.href);">LUST vs Not</a>), \
and Quarter (<a href="b117" onclick="return get_article(this.href);">vD vs AR</a>, \
<a href="561b" onclick="return get_article(this.href);">LUST vs StS</a>, \
<a href="d635" onclick="return get_article(this.href);">Lz vs HAND</a>, \
<a href="fa83" onclick="return get_article(this.href);">Cry vs NoT</a>).<br/>\
HvHs: <a href="0ff7" onclick="return get_article(this.href);">Final</a>, \
Semis (<a href="fee0" onclick="return get_article(this.href);">1</a>, \
<a href="fd16" onclick="return get_article(this.href);">2</a>), \
and Quarters (<a href="c8cd" onclick="return get_article(this.href);">1</a>, \
<a href="fc07" onclick="return get_article(this.href);">2</a>, \
<a href="6b87" onclick="return get_article(this.href);">3</a>, \
<a href="8b2f" onclick="return get_article(this.href);">4</a>).<br/>\
<a href="forums/viewtopic.php?t=1822" onclick="return forums_open(\'viewtopic.php?t=1822\');" class="comments">Comments</a> \
</p> \
</div><br/><br/>\
\
<div>\
<span class="date">13 July 2007</span> \
<h1>gwBBCode 1.7.4</h1> \
<p>Here is <a href="gwbbcode-1.7.4.zip">gwBBCode 1.7.4</a>!<br/>\
* Feature: June 15 new PvE skills by Cloud Six and Coran Ironclaw, using <a href="http://wiki.guildwars.com" target="_blank">GWW</a> descriptions;<br/>\
* Feature: PHP-Nuke support by Xai Rho;<br/>\
* Update: June 15, 18 and 19 skill updates, by Coran Ironclaw and Cloud Six;<br/>\
* Update: Fast Casting effect on Signets and Spawning Power effect on Weapon Spells, as suggested by CoRrRan;<br/>\
* Fix: Various graphic bug fixes by KillsLess.<br/>\
<a href="forums/viewtopic.php?t=1780" onclick="return forums_open(\'viewtopic.php?t=1780\');" class="comments">Details</a> \
</p> \
</div><br/><br/>\
\
<div>\
<span class="date">25 June 2007</span> \
<h1>Skill update</h1> \
<p>Since ANet had fun balancing skill again, we need a new database &gt;&lt;<br/>\
If you want to help us with that, <a href="forums/viewtopic.php?t=1704" onclick="return forums_open(\'viewtopic.php?t=1704\');" class="comments">here is the place</a> ^^<br/>\
As usual, it\'ll be used to report skill changes that weren\'t reported in the official update notes so that other skill databases can be kept up-to-date!<br/>\
<a href="forums/viewtopic.php?t=1704" onclick="return forums_open(\'viewtopic.php?t=1704\');" class="comments">Details</a> \
</p> \
</div><br/><br/>\
\
<div>\
<span class="date">28 Mai 2007</span> \
<h1>gwBBCode 1.7.3.1</h1> \
<p>v1.7.3 had a few annoying bugs this release should fix: <a href="gwbbcode-1.7.3.1.zip">gwBBCode 1.7.3.1</a>.<br/>\
* Fix: Various bug fixes, mostly for phpBB3. <a href="forums/viewtopic.php?t=1416&start=15#4595" onclick="return forums_open(\'viewtopic.php?t=1416&start=15#4595\');" class="comments">Details in my posts</a>.<br/>\
<a href="forums/viewtopic.php?t=1414" onclick="return forums_open(\'viewtopic.php?t=1414\');" class="comments">Details</a> \
</p> \
</div><br/><br/>\
\
<div>\
<span class="date">16 April 2007</span> \
<h1>gwBBCode 1.7.3</h1> \
<p>Here is <a href="gwbbcode-1.7.3.zip">gwBBCode 1.7.3</a>!<br/>\
* Feature: template code support, i.e try to write this in a post: [Shock war;OQYQEnTqAAAAAAAAAA]<br/>\
* Feature: MySQL skill database support for phpBB 2.x as suggested by Burning Times;<br/>\
* Update: KillsLess\' Stylish Tooltip Template v2.4;<br/>\
* Update: April 5, 10 and 13 skill updates, by LightspeedJack;<br/>\
<a href="forums/viewtopic.php?t=1414" onclick="return forums_open(\'viewtopic.php?t=1414\');" class="comments">Details</a> \
</p> \
</div><br/><br/>\
\
<div>\
<span class="date">5 April 2007</span> \
<h1>April 5th game update</h1> \
<p>Many skills were changed and we can safely assume that some weren\'t reported in game update notes, so I\'m asking for your <a href="forums/viewtopic.php?t=1351" onclick="return forums_open(\'viewtopic.php?t=1351\');" class="comments">help to screenshot skills</a> so that we can update the database and release a new version of gwBBCode! On that subject, I\'m already working on a feature to ease the template copy/paste process :p<br/>\
Other consequence of the update, the Hero Battles category was added to gwShack\'s Build Library. Hope you like it!<br/>\
<a href="forums/viewtopic.php?t=1352" onclick="return forums_open(\'viewtopic.php?t=1352\');" class="comments">Comments</a> \
</p> \
</div><br/><br/>\
\
<div>\
<span class="date">1 April 2007</span> \
<h1>Build search</h1> \
<p>Nope this is not an April joke ^^ The build library can now be searched using various conditions!<br/> \
It\'s not perfect yet but with HA going back to 8, I thought an "edited since" condition was needed. I\'m working on listing your own builds, please give me a few hours..<br/>\
<a href="forums/viewtopic.php?t=1318" onclick="return forums_open(\'viewtopic.php?t=1318\');" class="comments">Comments</a> \
</p> \
</div><br/><br/>\
\
<div>\
<span class="date">16 February 2007</span> \
<h1>gwBBCode 1.7.2</h1> \
<p>Here is <a href="gwbbcode-1.7.2.zip">gwBBCode 1.7.2</a>! ^^<br/> \
* Update: Rewrote the whole skill database with the help of Coran Ironclaw and KillsLess;<br/> \
* Fix: Admin panel bug reported by clunky;<br/> \
* Fix: The \'attr\' bug reported by cinemaniac.<br/> \
<a href="forums/viewtopic.php?t=1089" onclick="return forums_open(\'viewtopic.php?t=1089\');" class="comments">Comments</a> \
</p> \
</div><br/><br/>\
\
<div>\
<span class="date">5 February 2007</span> \
<h1>Thursday February 1 skill update</h1> \
<p>What an update.. The whole skill database has to be rewritten!<br/> \
Fortunately, all we have to do is take 2196 screenshots then a program will turn them into a new database :) Taking these all by myself turns me crazy though so I\'m calling you for help: \
Would you be so kind as to shoot one of the 10 professions for me? How to do it is explained \
<a href="forums/viewtopic.php?t=1032" onclick="return forums_open(\'viewtopic.php?t=1032\');" class="comments">here</a>. Thanks in advance! ^^<br/> \
<a href="forums/viewtopic.php?t=1032" onclick="return forums_open(\'viewtopic.php?t=1032\');" class="comments">Comments</a> \
</p> \
</div><br/><br/>\
\
<div>\
<span class="date">17 December 2006</span> \
<h1>Chat system</h1> \
<p>This chat system looks like IRC and is in a very early stage. There isn\'t much you can do yet, but if you like it I\'ll add features ^^<br/> \
So far it supports (gw)BBCode and room linking (e.g <a target="_blank" href="http://gwshack.us/#gwshack">http://gwshack.us/#gwshack</a>).<br/> \
<a href="forums/viewtopic.php?t=815" onclick="return forums_open(\'viewtopic.php?t=815\');" class="comments">Comments</a> \
</p> \
</div><br/><br/>\
\
<div>\
<span class="date">23 November 2006</span> \
<h1>gwBBCode 1.7.1</h1> \
<p>Time to introduce new features with <a href="gwbbcode-1.7.1.zip">gwBBCode 1.7.1</a> ^^<br/> \
* Feature: Ability to download the skill template file of a build;<br/> \
* Feature: New skill popup border, designed by KillsLess;<br/> \
* Feature: Attribute level 0..12..16 skill descriptions instead of 0..15;<br/> \
* Feature: Template id rendering, e.g [build=ATNxweMcnLdJpecXnICkaiJA];<br/> \
* Feature: "nosave" attribute to hide template save icons: [build pof=Mo/Me nosave], as suggested by apoguita;<br/> \
* Feature: MyBB support;<br/> \
* Feature: Ability to force installation on supported but undetected forum software;<br/> \
* Fix: Expertise affecting more skills than attacks, Rituals, touch skills and Ranger Skills;<br/> \
* Fix: [No Skill] icon showing a description pop-up;<br/> \
* Fix: And database file size reduced under 500KB.<br/> \
<a href="forums/viewtopic.php?t=691" onclick="return forums_open(\'viewtopic.php?t=691\');" class="comments">Comments</a> \
</p> \
</div><br/><br/>\
\
<div>\
<span class="date">21 November 2006</span> \
<h1>New feature: Article discussion</h1> \
<p>When you visit a public build from the <a href="" onclick="select_right_tab(\'Build Library\'); return false;">Build Library</a>, a new main view is now available: Article discussion. This place is for you to discuss anything related with a build: reasons you like/dislike it, additional details on how to play it, improvement/variant ideas, tales of what happened when you played it... anything!<br/> \
And everyone is welcome to use it, registered or not ^^<br/> \
<a href="forums/viewtopic.php?t=681" onclick="return forums_open(\'viewtopic.php?t=681\');" class="comments">Comments</a> \
</p> \
</div><br/><br/>\
\
<div>\
<span class="date">5 November 2006</span> \
<h1>gwBBCode 1.7.0</h1> \
<p>Here is the latest version of gwBBCode with Nightfall support! <a href="gwbbcode-1.7.0.zip">Direct download</a>.<br/> \
* Update: Rewrote the whole skill database including Nightfall skills, by Coran Ironclaw and I;<br/> \
* Fix: Lots of new skill related bugs reported by CoRrRan.<br/> \
<a href="forums/viewtopic.php?t=361" onclick="return forums_open(\'viewtopic.php?t=361\');" class="comments">Comments</a> \
</p> \
</div><br/><br/>\
\
<div>\
<span class="date">31 October 2006</span> \
<h1>New feature: Nightfall and template support</h1> \
<p>After days of work, here it is: gwShack now supports Nightfall new professions and skills! And a new version of gwBBCode is on the way (count 2 days).<br/> \
Bonus feature, gwShack now allows you to view skill templates you saved from GuildWars. To do that, copy/paste your skill template file content in your browser address bar after http://gwshack.us/ and go.<br/> \
Here is an example: <a href="http://gwshack.us/ATNxwaMcnMilej4PsbfkbiJA" target="_blank">http://gwshack.us/ATNxwaMcnMilej4PsbfkbiJA</a><br/> \
<a href="forums/viewtopic.php?t=340" onclick="return forums_open(\'viewtopic.php?t=340\');" class="comments">Comments</a> \
</p> \
</div><br/><br/>\
\
<div>\
<span class="date">9 October 2006</span> \
<h1>Server moved.. again</h1> \
<p>gwShack uses about 40 minutes of CPU time a day, and may soon require a few hours. Previous host allowed 16 minutes a day, so I had to move to a host that doesn\'t have this kind of limit. I\'m really sorry it happened during a rush hour and hope that we\'ll finally be able to settle down and add new features.<br/> \
<a href="forums/viewtopic.php?t=291" onclick="return forums_open(\'viewtopic.php?t=291\');" class="comments">Comments</a> \
</p> \
</div><br/><br/>\
\
<div>\
<span class="date">28 September 2006</span> \
<h1>Server moved</h1> \
<p>These database downtimes and the general slowness were such a pain I moved gwShack to another web host.<br/> \
This new host provides me with 170 times more storage and 400 times more traffic! Yet this is nothing compared to the new MySQL database reaction time. I\'m paying $7.5 a month against $3 before, so I can keep my promise concerning money: no donations, no ads :)<br/> \
<a href="forums/viewtopic.php?t=280" onclick="return forums_open(\'viewtopic.php?t=280\');" class="comments">Comments</a> \
</p> \
</div><br/><br/>\
\
<div>\
<span class="date">19 September 2006</span> \
<h1>gwShack server code redone</h1> \
<p>I rewrote the whole server code twice and though none showed up yet, new features will now be a real pleasure to add!<br/> \
This update should fix many bugs (and raise new ones :P). I\'m counting on you to report them as comments of this news. \
<a href="forums/viewtopic.php?t=247" onclick="return forums_open(\'viewtopic.php?t=247\');" class="comments">Comments</a> \
</p> \
</div><br/><br/>\
\
<div>\
<span class="date">16 September 2006</span> \
<h1>gwBBCode 1.6.4</h1> \
<p>Small by the number of its new features, big by its skill database changes, here is <a href="forums/viewtopic.php?t=239" onclick="return forums_open(\'viewtopic.php?t=239\');">gwBBCode 1.6.4</a>!<br/> \
* Update: ArenaNet\'s Thursday September 14 skill update, by PogS;<br/> \
* Feature: Spirit Health and armor, thanks to the <a href="http://gw.gamewikis.org/wiki/Spirit" target="_blank">Spirit article</a> on GuildWiki.org;<br/> \
* Feature: Divine Favor effect on spells, with the help of Coran Ironclaw.<br/> \
<a href="forums/viewtopic.php?t=239" onclick="return forums_open(\'viewtopic.php?t=239\');" class="comments">Comments</a> \
</p> \
</div><br/><br/>\
\
<div>\
<span class="date">14 September 2006</span> \
<h1>Skill update and gwBBCode\'s 500th forum</h1> \
<p>A tenth of all skills, a quarter of all elites were changed. PogS already updated gwBBCode\'s database and a new version of it will be released Monday. Till then, <a target="_blank" href="files/diff.htm">here</a> is a page listing skills before and after the update.<br/> \
Earlier this week, gwBBCode was installed on its 500th forum. This is crazy. To you who use it here or on your forums, and on behalf of everyone who helped (especially those from <a href="forums/viewtopic.php?t=145" onclick="return forums_open(\'viewtopic.php?t=145\');" class="comments">Credits</a>), I say Thank you ^^<br/> \
<a href="forums/viewtopic.php?t=236" onclick="return forums_open(\'viewtopic.php?t=236\');" class="comments">Comments</a> \
</p> \
</div><br/><br/>\
\
<div>\
<span class="date">8 September 2006</span> \
<h1>Championship builds</h1> \
<p>Thanks to ArenaNet\'s interesting <a href="http://www.guildwars.com/competitive/gwfc/skillusage/" target="_blank">Skill Usage Charts</a>, I was able to easily write all skillbars used during last championship. Attribute levels are missing, but I\'m sure there is something to learn from each one of these 26 teambuilds.<br/> \
You\'ll find them in the <a href="" onclick="select_right_tab(\'Build Library\'); return false;">Build Library</a>.<br/> \
<a href="forums/viewtopic.php?t=227" onclick="return forums_open(\'viewtopic.php?t=227\');" class="comments">Comments</a> \
</p> \
</div><br/><br/>\
\
<div>\
<span class="date">6 September 2006</span> \
<h1>New feature: Voting</h1> \
<p>Here it is! Voting is enabled for public builds. To prevent abuses, only registered visitors are allowed to vote. So far vote results are only showed if at least 2 votes were casted. This minimum number of voters will increase in a few days.<br/> \
Also, public build categories Heroes\' Ascent, Farming and Trash were added. More to come.<br/> \
<a href="forums/viewtopic.php?t=215" onclick="return forums_open(\'viewtopic.php?t=215\');" class="comments">Comments</a> \
</p> \
</div><br/><br/>\
\
<div>\
<span class="date">3 September 2006</span> \
<h1>New feature: Public builds</h1> \
<p>You can now post a build and make it public, i.e everyone will see it in the <a href="" onclick="select_right_tab(\'Build Library\'); return false;">Build Library</a>. If you choose to do so, there is no way to change it back to private. A voting system is on the way.<br/> \
<a href="forums/viewtopic.php?t=211" onclick="return forums_open(\'viewtopic.php?t=211\');" class="comments">Comments</a> \
</p> \
</div><br/><br/>\
\
<div>\
<span class="date">29 August 2006</span> \
<h1>New feature: Embedded forums</h1> \
<p>See this <a href="" onclick="select_left_tab(\'Forums\'); return false;">Forums</a> tab above? It\'s a shortcut to gwShack forums to quickly get there. Previous way to access forums (<a href="/forums" target="_blank">gwshack.us/forums</a>) is still working and will continue to do so for 800x600 users, but the new one will help implement an article discussion system.<br/> \
The used phpBB template is based on Daniel\'s Guild Wars Alliance theme.<br/> \
<a href="forums/viewtopic.php?t=204" onclick="return forums_open(\'viewtopic.php?t=204\');" class="comments">Comments</a> \
</p> \
</div><br/><br/>\
\
<div>\
<span class="date">27 August 2006</span> \
<h1>New feature: Article names and edition</h1> \
<p>Articles can now be named! Registered users are allowed to modify the name and content of articles they posted, and keep track of each version. These features are the foundations of a much bigger <a href="" onclick="select_right_tab(\'Build Library\'); return false;">Build Library</a> to come.<br/> \
<a href="forums/viewtopic.php?t=199" onclick="return forums_open(\'viewtopic.php?t=199\');" class="comments">Comments</a> \
</p> \
</div><br/><br/>\
\
</div> \
';
div('home').innerHTML = output;
output = '';





// Build Library
////////////////

var library_elements = new Array(); //Contains the list of shown builds in the library tab


/*
   <option value="lib_prof" disabled>Professions..</option>\
   <option value="lib_vote_count_above" disabled>Vote count &gt;= ..</option>\
   <option value="lib_charac_count" disabled>Charac. count..</option>\
   <option value="lib_vote_misc" disabled>Voting misc..</option>\
*/


var output = '\
<select id="condition" name="condition" size=5 style="overflow: visible; width: 9em; float: left;">\
   <option value="lib_played" selected>Played in..</option>\
   <option value="lib_rate">Rate &gt;= ..</option>\
   <option value="lib_name">Name contains..</option>\
   <option value="lib_edited">Edited after..</option>\
   <option value="lib_author">Written by..</option>\
</select><div id="condition_b" style="display: inline; float: left;"></div><div style="display: inline; float: left;"><button type="button" onclick="user_add_condition(false);" style="width: 5em;">Add<br/>condition</button><br/><button type="button" onclick="user_add_condition(true);" style="width: 5em;">Add its<br/>opposite</button></div>\
\
<div id="search_text" style="clear: left">Conditions (<a id="search_link" href="">link</a>): <div id="conditions_summary" style="display: inline"></div></div>\
\
<div id="warehouse" style="display: none;">\
\
<select id="lib_played" name="lib_played" size=9 style="overflow: visible; width: 11em;">\
   <option value="HA for 8" selected>Heroes\' Ascent for 8</option>\
   <option value="HA for 6">Heroes\' Ascent for 6</option>\
   <option value="GvG">Guild Battles</option>\
   <option value="HB">Hero Battles</option>\
   <option value="AB">Alliance Battles</option>\
   <option value="TA">Team Arenas</option>\
   <option value="RA">Random Arenas</option>\
   <option value="Farming">Farming</option>\
   <option value="Nowhere">Nowhere</option>\
   <option value="Trash">Trash</option>\
</select>\
\
<input id="lib_rate" style="width: 3em;" value="3.0"/>\
\
<input id="lib_name" style="width: 7em;"/>\
\
<div id="lib_edited" style="display: inline">\
   <select id="lib_edited_month" name="lib_edited_month" size=12 style="overflow: visible; width: 8em;">\
      <option value="1" selected>January</option>\
      <option value="2">February</option>\
      <option value="3">March</option>\
      <option value="4">April</option>\
      <option value="5">May</option>\
      <option value="6">June</option>\
      <option value="7">July</option>\
      <option value="8">August</option>\
      <option value="9">September</option>\
      <option value="10">October</option>\
      <option value="11">November</option>\
      <option value="12">December</option>\
   </select>\
   <select id="lib_edited_year" name="lib_edited_year" size=12 style="overflow: visible; width: 4em;">\
      <option value="2008" selected>2008</option>\
      <option value="2007">2007</option>\
      <option value="2006">2006</option>\
   </select>\
</div>\
\
<div id="lib_author" style="display: inline">\
   <input type=checkbox name="lib_author_me" id="lib_author_me">me!<br/>\
   <input id="lib_author_name" style="width: 7em;"/>\
</div>\
\
<input id="lib_vote_count_above" style="width: 3em;" value="0"/>\
\
<div id="lib_prof" style="display: inline">\
   <select id="lib_primary" name="lib_primary" size=12 style="overflow: visible; width: 4em;">\
      <option value="W" selected>W</option>\
      <option value="R">R</option>\
      <option value="Mo">Mo</option>\
      <option value="N">N</option>\
      <option value="Me">Me</option>\
      <option value="E">E</option>\
      <option value="A">A</option>\
      <option value="Rt">Rt</option>\
      <option value="D">D</option>\
      <option value="P">P</option>\
      <option value="?">Any</option>\
   </select>\
   <select id="lib_secondary" name="lib_secondary" size=12 style="overflow: visible; width: 4em;">\
      <option value="W">W</option>\
      <option value="R">R</option>\
      <option value="Mo">Mo</option>\
      <option value="N">N</option>\
      <option value="Me">Me</option>\
      <option value="E">E</option>\
      <option value="A">A</option>\
      <option value="Rt">Rt</option>\
      <option value="D">D</option>\
      <option value="P">P</option>\
      <option value="?" selected>Any</option>\
      <option value="none">None</option>\
   </select>\
   Count: <input id="lib_prof_count" style="width: 1em;" value="1"/>\
</div>\
\
<input id="lib_charac_count" style="width: 3em;" value="2"/>\
\
<select id="lib_vote_misc" name="lib_vote_misc" size=5 style="overflow: visible; width: 9em;">\
   <option value="no_rate" selected>No rate</option>\
   <option value="voted_for">Voted for</option>\
   <option value="didnt_vote_for">Didn\'t vote yet</option>\
</select>\
\
</div>\
<ul id="build_results">\
</ul>\
';
div('build_library').innerHTML = output;
output = '';





var previous_condition = false;
function condition_change() {
   //Move previous sub condition back to the warehouse
   var sub_window = div('condition_b');
   if (sub_window.firstChild) {
      div('warehouse').appendChild(sub_window.firstChild);
   }
   
   //Bring the new sub condition front
   var next_condition = div(this.options[this.selectedIndex].value);
   sub_window.appendChild(next_condition);
}


function init_conditions() {
   conditions = new Object();
   conditions.played = new Object();
   conditions.rate = new Object();
   conditions.rate.above = null;
   conditions.rate.below = null;
   conditions.name = new Array();
   conditions.edited = new Object();
   conditions.edited.after = null;
   conditions.edited.before = null;
   conditions.author = new Array();
   conditions.my_builds = null;
   conditions.vote_count_above = false;
   conditions.prof = new Array();
   conditions.charac_count = false;
   conditions.vote_misc = new Array();
}

var conditions;
init_conditions();
show_conditions();


//User triggered fonction that adds the selected condition and updates shown condition list
function user_add_condition(opposite) {
   var lib_condition = select_value('condition');
   var condition = lib_condition.substring(4);
   switch(condition) {
   case 'played':
      add_condition(condition, opposite, select_value(lib_condition));
      break;
   case 'rate':
      add_condition(condition, opposite, parseFloat(div(lib_condition).value));
      break;
   case 'name':
      add_condition(condition, opposite, div(lib_condition).value);
      break;
   case 'edited':
      var d = new Date(select_value('lib_edited_year'), select_value('lib_edited_month'), 1);
      add_condition(condition, opposite, d.getTime()/1000);
      break;
   case 'author':
      if (div('lib_author_me').checked) {
         add_condition('my_builds', false, null);
      }
      else {
         add_condition(condition, opposite, div('lib_author_name').value);
      }
      break;
   case 'prof':
      var prof = select_value('lib_primary');
      var secondary = select_value('lib_secondary');
      if (secondary != 'none') {
         prof +=  '/' + secondary;
      }
      add_condition(condition, opposite, prof, div('lib_prof_count').value);
      break;
   case 'vote_count_above':
      add_condition(condition, opposite, div(lib_condition).value);
      break;
   default:
      alert('Unknown condition: ' + lib_condition);
   }
   
   show_conditions();
}


//Adds the opposite or not sub_condition condition of value val
function add_condition(condition, opposite, val, val2) {
   switch(condition) {
   case 'played':
      //Add condition
      conditions.played[val] = opposite;
      //And remove all opposite conditions
      for (var i in conditions.played) {
         if (conditions.played[i] !== opposite) {
            delete conditions.played[i];
         }
      }
      //Sort object's properties
      conditions.played = sort_object(conditions.played, list_options(div('lib_played')));
      break;

   case 'rate':
      if (isNaN(val)) {
         alert('Please enter a float as rate.');
         break;
      }
      var direction = opposite ? 'below' : 'above';
      var opposite_direction = (!opposite) ? 'below' : 'above';
      if (0 < val && val < 5) {
         conditions.rate[direction] = val;
         if (   conditions.rate.above !== null && conditions.rate.below !== null
             && conditions.rate.above >= conditions.rate.below) {
            conditions.rate[opposite_direction] = null;
         }
      }
      else {
         alert('Invalid value:\nA rate is a value between 0 and 5');
      }
      break;
   case 'name':
      conditions.name[val] = opposite;
      //Sort object's properties
      var order = [];
      for (var prop in conditions.name) {
         order.push(prop);
      }
      order.sort();
      conditions.name = sort_object(conditions.name, order);
      break;
   case 'edited':
      if (!isNaN(val)) {
         var direction = opposite ? 'before' : 'after';
         var opposite_direction = (!opposite) ? 'before' : 'after';
         if (0 < val) { //TODO: test a bit more
            conditions.edited[direction] = val;
            if (   conditions.edited.after !== null && conditions.edited.before !== null
                && conditions.edited.after >= conditions.edited.before) {
               conditions.edited[opposite_direction] = null;
            }
         }
         else {
            alert('Invalid value:\nA date is invalid');
         }
      }
      break;
   case 'author':
      conditions.author[val] = opposite;
      //Sort object's properties
      var order = [];
      for (var prop in conditions.author) {
         order.push(prop);
      }
      order.sort();
      conditions.author = sort_object(conditions.author, order);
      break;
   case 'my_builds':
      conditions.my_builds = opposite;
      break;
   case 'prof':
      val2 = parseInt(''+val2);
      if (isNaN(val2)) {
         alert('Please enter an integer as number of builds.');
         break;
      }
      if (!conditions.prof[val]) {
         conditions.prof[val] = new Object();
      }
      var count = conditions.prof[val];
      var direction = opposite ? 'below' : 'above';
      var opposite_direction = (!opposite) ? 'below' : 'above';
      count[direction] = val2;
      if (   count.above !== null && count.below !== null
          && count.above >= count.below) {
         count[opposite_direction] = null;
      }
      break;
   case 'vote_count_above':
      condition_text = 'vote count above ' + val;
      break;
   default:
      alert('Unknown condition: ' + condition);
   }
}



function show_conditions() {
   //Remove previous conditions summary
   var summary = div('conditions_summary');
   while(summary.childNodes[0]) {
      summary.removeChild(summary.childNodes[0]);
   }

   //Then set the new ones up
   show_object(summary, 'conditions.played', 'played in ', 'or', false);
   show_object(summary, 'conditions.played', 'not played in ', 'nor', true);
   show_float(summary, 'conditions.rate', 'rate');
   show_object(summary, 'conditions.name', 'name contains ', 'and', false);
   show_object(summary, 'conditions.name', "name doesn't contain ", 'nor', true);
   show_date(summary, 'conditions.edited', 'edited');
   show_object(summary, 'conditions.author', 'author is ', 'or', false);
   show_object(summary, 'conditions.author', "author is not ", 'nor', true);
   if (conditions.my_builds === false) {
      summary.appendChild(document.createTextNode(', I wrote it'));
      var remove_code = "conditions.my_builds=null; show_conditions();";
      add_remove_img(summary, remove_code);
   }
   for (var i in conditions.prof) {
      show_float(summary, "conditions.prof['"+i+"']", i+' count');
   }
   
   //Show some help if no condition were specified
   if (!summary.firstChild) {
      summary.appendChild(document.createTextNode(', add one or more conditions'));
   }
   //Or show the search button
   else {
      summary.innerHTML += '<button onclick="perform_build_search();">Search</button>';
   }

   div('search_link').href = '?' + build_search_query();

   //'Upper case' first condition list letter, and end with a point.
   var start = summary.firstChild.nodeValue;
   summary.firstChild.nodeValue = start.substring(2);   //Skip the leading ", "
   summary.appendChild(document.createTextNode('.'));
}

//Send the search condition
function perform_build_search() {
   cout.build_conditions = build_search_query();
   send_form('build_search');
}

//add to summary a list of obj_name's removable properties, preceded by obj_intro
//returns true only if object was shown
function show_object(summary, obj_name, obj_intro, conjunction, show_opposite) {
   var show_intro = true;
   for (var prop in eval(obj_name)) {
      //Skip if property is opposite
      if (eval(obj_name)[prop] != show_opposite) {
         continue;
      }
      
      //Either show the intro or 'or' depending on whether it's the first loop or not
      add_text_if(', ' + obj_intro, summary, show_intro);
      add_text_if(' ' + conjunction + ' ', summary, !show_intro);
      show_intro = false;
      
      //Show property name and a remove image
      summary.appendChild(document.createTextNode(prop));
      var remove_code = "delete " + obj_name + "['" + prop + "']; show_conditions();";
      add_remove_img(summary, remove_code);
   }
}


//add to summary a list of obj_name's removable properties, preceded by obj_intro
//returns true only if object was shown
function show_float(summary, obj_name, val_name) {
   var obj = eval(obj_name);
   var before = obj.above ? (obj.above + ' <= ') : '';
   var after = obj.below ? (' <= ' + obj.below) : '';
   if (obj.above || obj.below) {
      //"rate"
      summary.appendChild(document.createTextNode(', ' + val_name + ' '));

      //"above 3.5"
      if (obj.above && !obj.below) {
         summary.appendChild(document.createTextNode('above ' + obj.above));
         var remove_code = obj_name + ".above = false; show_conditions();";
         add_remove_img(summary, remove_code);
      }
      //"below 4.5"
      else if (!obj.above && obj.below) {
         summary.appendChild(document.createTextNode('below ' + obj.below));
         var remove_code = obj_name + ".below = false; show_conditions();";
         add_remove_img(summary, remove_code);
      }
      //"between 3.5 and 4.5"
      else if (obj.above && obj.below) {
         summary.appendChild(document.createTextNode('between ' + obj.above));
         var remove_code = obj_name + ".above = false; show_conditions();";
         add_remove_img(summary, remove_code);
         summary.appendChild(document.createTextNode(' and ' + obj.below));
         var remove_code = obj_name + ".below = false; show_conditions();";
         add_remove_img(summary, remove_code);
      }
   }
}


//add to summary a list of obj_name's removable properties, preceded by obj_intro
//returns true only if object was shown
function show_date(summary, obj_name, val_name) {
   var obj = eval(obj_name);
   var before = obj.after ? (obj.after + ' <= ') : '';
   var after = obj.before ? (' <= ' + obj.before) : '';
   if (obj.after || obj.before) {
      //"rate"
      summary.appendChild(document.createTextNode(', ' + val_name + ' '));

      //"after 3/07"
      if (obj.after && !obj.before) {
         summary.appendChild(document.createTextNode('after ' + sprintDate(obj.after)));
         var remove_code = obj_name + ".after = false; show_conditions();";
         add_remove_img(summary, remove_code);
      }
      //"before 4/07"
      else if (!obj.after && obj.before) {
         summary.appendChild(document.createTextNode('before ' + sprintDate(obj.before)));
         var remove_code = obj_name + ".before = false; show_conditions();";
         add_remove_img(summary, remove_code);
      }
      //"between 3/07 and 4/07"
      else if (obj.after && obj.before) {
         summary.appendChild(document.createTextNode('between ' + sprintDate(obj.after)));
         var remove_code = obj_name + ".after = false; show_conditions();";
         add_remove_img(summary, remove_code);
         summary.appendChild(document.createTextNode(' and ' + sprintDate(obj.before)));
         var remove_code = obj_name + ".before = false; show_conditions();";
         add_remove_img(summary, remove_code);
      }
   }
}

function sprintDate(time) {
   var d = new Date();
   d.setTime(time*1000);
   return ''+d.getMonth() + '/' + d.getFullYear().toString().substr(2);
}

//Returns the query part of a search link based on conditions
function build_search_query() {
   var query = [];
   object_query(query, conditions.played, 'played', 'played-opp');
   if (conditions.rate.above > 0) {
      query.push('rate=' + conditions.rate.above);
   }
   if (conditions.rate.below > 0) {
      query.push('rate-opp=' + conditions.rate.below);
   }
   object_query(query, conditions.name, 'name', 'name-opp');
   if (conditions.edited.after > 0) {
      query.push('edited=' + conditions.edited.after);
   }
   if (conditions.edited.before > 0) {
      query.push('edited-opp=' + conditions.edited.before);
   }
   object_query(query, conditions.author, 'author', 'author-opp');
   if (conditions.my_builds === false) {
      query.push('my_builds=1');
   }

   for (var i in conditions.prof) {
      var count = conditions.prof[i];
      if (count.above > 0) {
         query.push('rate=' + conditions.rate.above);
      }
      if (conditions.rate.below > 0) {
         query.push('rate-opp=' + conditions.rate.below);
      }
   }
   
   return query.join('&');
}


//Adds to the query array a list of obj's object values preceded by yes_msg or no_msg depending on their boolean value
function object_query(query, obj, yes_msg, no_msg) {
   var yes = [];
   var no = [];
   for (var i in obj) {
      var safe_i = encodeURIComponent(i.replace(/;/, ';;'));
      if (!obj[i]) {
         yes.push(safe_i);
      }
      else {
         no.push(safe_i);
      }
   }
   
   if (yes.length) {
      query.push(yes_msg + '=' + yes.join(';'));
   }
   if (no.length) {
      query.push(no_msg + '=' + no.join(';'));
   }
}

//Returns obj after sorting its properties in the same order as order
function sort_object(obj, order) {
   //Order the object
   var obj_backup = obj;
   obj = new Object();
   for (var i in order) {
      if (obj_backup[order[i]] != null) {
         obj[order[i]] = obj_backup[order[i]];
      }
   }
   return obj;
}


//Return a list of option values of a select node
function list_options(node) {
   var list = [];
   var select_opts = node.getElementsByTagName('option');
   for (var i=0; i<select_opts.length; i++ ) {
      list.push(select_opts[i].value);
   }
   return list;
}

function add_query() {
   var query = getSearchQuery();
   if (query) {
      var cond = new Object();
      query = query.split('&');
      for (var i in query) {
         var condition = query[i].split('=')[0];
         var val = query[i].split('=')[1];
         var opposite = /-opp$/.test(condition);
         condition = condition.replace(/-opp$/, '');
         switch(condition) {
         case 'played':
            var props = val.split(';');
            for (var j in props) {
               add_condition(condition, opposite, decodeURIComponent(props[j]));
            }
            break;
         case 'rate':
            add_condition(condition, opposite, parseFloat(val));
            break;
         case 'name':
            var props = val.split(';');
            for (var j in props) {
               add_condition(condition, opposite, decodeURIComponent(props[j]));
            }
            break;
         case 'author':
            var props = val.split(';');
            for (var j in props) {
               add_condition(condition, opposite, decodeURIComponent(props[j]));
            }
            break;
         case 'my_builds':
            if (registered_user) {
               add_condition(condition, opposite, null);
            }
            //TODO: tell user the search may not work as expected
            break;
         case 'vote_count_above':
            condition_text = 'vote count above ' + val;
            break;
         default:
            alert('Unknown condition: ' + condition);
         }
      }
      show_conditions();
   }
}

function getSearchQuery(loc) {
   if (loc == null)
      var loc = window.location;
   if (/\?(.*)$/.test(loc)) {
      var reg = loc.toString().match(/\?(.*)$/);
      return reg[1];
   }
   else
      return false;
}


//Adds text to the where_to node if add_or_not
function add_text_if(text, where_to, add_or_not) {
   if (add_or_not) {
      var commaNode = document.createTextNode(text);
      where_to.appendChild(commaNode);
   }
}

//Adds to the where_to node a remove image running remove_code when it's clicked
function add_remove_img(where_to, remove_code) {
   where_to.innerHTML += '<img src="img/cross.gif" style="vertical-align: top;" onclick="' + remove_code + '"/>';
}


function in_array(needle, haystack) {
   for(var j in haystack)
      if (haystack[j].indexOf(needle) === 0)
         return true;
   return false;
}

function select_value(id_name) {
   var divi = div(id_name);
   return divi.options[divi.selectedIndex].value;
}

//Hook and select first condition
var condition = document.getElementById('condition');
condition.onchange = condition_change;
condition[0].selected = true;
condition.onchange();







      
//Add a list of elements to a tree branch after cleaning it up
function add_elements() {
   //Clean up the list first
   var res_div = div('build_results');
   while(res_div.childNodes[0]) {
      res_div.removeChild(res_div.childNodes[0]);
   }

   //Add elements
   var out = '';
   for (var i in library_elements) {
      //<li><a href="8c35c" onclick="return get_article(this.href);">test - 8c35c </a><span class="author_name">by me! </span></li>
      var build = library_elements[i];
      var out2 = '<a href="' + build.href + '" onclick="return get_article(this.href);">' + build.name + ' - ' + build.href + '</a><span class="author_name"> by ' + build.author_name + '</span>';
      if (build.vote) {
         out2 += '<div style="display: inline; background-image: url(img/star_bg2.gif); background-repeat: no-repeat; background-position: ' + Math.ceil(build.vote*65/5-65).toString() + 'px 0px;"><img src="img/stars.gif"></div>';
      }
      out += '<li>' + out2 + '</li>';
   }
   if (library_elements.length == 0) {
      out = 'No result';
   }
   div('build_results').innerHTML = out;
}


//Remove an element from a tree branch
//TODO child stays in memory, clean it (and it's children)
function remove_element(id) {
   var i = libsearch_id(id);
   if (i) {
      div('library_'+id).parentNode.removeChild(div('library_'+id));

      //Update the article search database
      library_elements[i] = null;
   }
}


//Returns the index in library_elements of the first array matching passed id
function libsearch_id(id) {
   for (var i in library_elements) {
      if (library_elements[i][1] == id) {
         return i;
      }
   }
   return false;
}








function resize() {
   var myWidth = 0, myHeight = 0;
   if( typeof( window.innerWidth ) == 'number' ) {
      //Non-IE
      myWidth = window.innerWidth;
      myHeight = window.innerHeight;
   } else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
      //IE 6+ in 'standards compliant mode'
      myWidth = document.documentElement.clientWidth;
      myHeight = document.documentElement.clientHeight;
   } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
      //IE 4 compatible
      myWidth = document.body.clientWidth;
      myHeight = document.body.clientHeight;
   }

   //Position the loading block
   var loading_x_pos = myWidth - getw('loading');
   div('loading').style.left = loading_x_pos.toString() + 'px';

   seth('container', myHeight);
   seth('right', myHeight);
   setw('right', 410);
//   setw('skill_list', 120);

   seth('att_points', myHeight - geth('right_header') - geth('copyright') - 40);
   seth('gwbbcode_syntax', myHeight - geth('right_header') - geth('copyright') - 40);
   seth('build_library', myHeight - geth('right_header') - geth('copyright') - 40);
   seth('profile', myHeight - geth('right_header') - geth('copyright') - 40);
   seth('history', myHeight - geth('right_header') - geth('copyright') - 40);

   seth('left', myHeight);
   setw('left', myWidth - 430);
   //seth('login_div', geth('login_div')+20); //Weird bug
   seth('home', myHeight - geth('left_header') - geth('login_div') - 40);
   seth('article_editor', myHeight - geth('left_header') - geth('login_div') - 40);
   seth('forums', myHeight - geth('left_header') - geth('login_div') - 40);
   seth('comments', myHeight - geth('left_header') - geth('login_div') - 40);
   seth('chat', myHeight - geth('left_header') - geth('login_div') - 40);
   seth('debug', myHeight - geth('left_header') - geth('login_div') - 40);

}

function geth(div_name) {
   return document.getElementById(div_name).offsetHeight;
}

function seth(div_name, height) {
   document.getElementById(div_name).style.height = height + 'px';
}

function getw(div_name) {
   return document.getElementById(div_name).offsetWidth;
}

function setw(div_name, width) {
   document.getElementById(div_name).style.width = width + 'px';
}
































/////////////////
/// skills.js ///
/////////////////


// SKILLS
/////////

var loaded_count_skills = 0;
function loaded_skills() {
   if (++loaded_count_skills == 2)
      //Only initialize if all content has been loaded
      init_skills();
};

function init_skills() {
   //Maximize skill list's height
   //div('skill_list').style.height = (parseInt(screen.height) - 355)+'px';

   //Parse skill list
   for (var i in db)
      prev_select[i] = false;

   query = div("query").value;
   output_skills();
   init_select();
}


var query = '';
var user_query = '';
function show_skills() {
   div('skill_list').innerHTML = '<center>Searching...</center>';
   self.setTimeout('real_show_skills();', 100);
}
function real_show_skills() {
   user_query = div("query").value;
   clean_query();
   query = user_query;
   //query = query.replace(/([a-z]+)[([]([><]=?)([0-9]+)[\])]/ig, '($1$2$3)');
   //query = query.replace(/elite/ig, '($1.toLowerCase().indexOf("$2".toLowerCase())!=-1)');

   //remove spaces at start and end, and replace them by one space
   query = query.replace(/^\s*/, ' ').replace(/\s*$/, ' ');

   //replace an orphelin value by name(value)
   query = query.replace(/ ([^<>=!\&\|\[\]\(\) ]+) /g, 'name($1)');

   //property(value)
   query = query.replace(/([a-z]+)\(([^\])]+)\)/ig, '($1.toLowerCase().indexOf("$2".toLowerCase())!=-1)');

   //property[value)
   query = query.replace(/([a-z]+)\[([^\])]+)\)/ig, '($1.toLowerCase().indexOf("$2".toLowerCase())==0)');

   //& -> &&   and   | -> ||
   query = query.replace(/\&/g, '&&').replace(/\|/g, '||');

   //trim
   query = query.replace(/^\s*/, '');

   //alert(query);

   //Output menu and intialise each profession skill list
   if (!select_skills())
      div('skill_list').innerHTML = '';
}


var professions = new Array('All', 'Warrior', 'Ranger', 'Monk', 'Necromancer', 'Mesmer', 'Elementalist', 'Asssassin', 'Dervish', 'Paragon', 'Ritualist');
var attributes = new Array ('No Attribute', 'Air Magic', 'Earth Magic', 'Water Magic', 'Energy Storage', 'Fire Magic', 'Domination Magic', 'Illusion Magic', 'Inspiration Magic', 'Fast Casting', 'Divine Favor', 'Healing Prayers', 'Protection Prayers', 'Smiting Prayers', 'Blood Magic', 'Soul Reaping', 'Curses', 'Death Magic', 'Marksmanship', 'Wilderness Survival', 'Expertise', 'Beast Mastery', 'Strength', 'Swordsmanship', 'Axe Mastery', 'Hammer Mastery', 'Tactics', 'Critical Strikes', 'Dagger Mastery', 'Deadly Arts', 'Shadow Arts', 'Spawning Power', 'Channeling Magic', 'Communing', 'Restoration Magic', 'Spear Mastery', 'Command', 'Motivation', 'Leadership', 'Scythe Mastery', 'Wind Prayers', 'Earth Prayers', 'Mysticism');


var types = new Array('Spell', 'Attack', 'Axe Attack', 'Binding Ritual', 'Bow Attack', 'Chant', 'Dual Attack', 'Echo', 'Enchantment Spell', 'Form', 'Glyph', 'Hammer Attack', 'Hex Spell', 'Item Spell', 'Lead Attack', 'Melee Attack', 'Off-Hand Attack', 'Pet Attack', 'Preparation', 'Nature Ritual', 'Trap', 'Scythe Attack', 'Stance', 'Shout', 'Signet', 'Spear Attack', 'Skill', 'Sword Attack', 'Ward Spell', 'Weapon Spell', 'Well Spell');


var name;
var typ;
var elite;
var energy;
var adre;
var eregen;
var casting;
var recharge;
var profession;
var attribute;
var desc;

var elite_background;
var yellow_elite;
var prof;
var attr;
var type;
var cost;
var recharge_ornot;



var prev_select = new Array();
function select_skills() {
   if (query.length==0)
      return false;

   var output = '';
   for (var i in db) {
      i = parseInt(i);
      extract(db[i]);
      energy = parseInt(energy);
      query = query.replace(/[a-z][a-z][a-z]=[0-9]+/ig, 'true');

      var result = eval(query);
      if (typeof(result) == "undefined")
         return false;

//prof[E) & ear=4
// prof[E) & air=15 & ear=10

      //Only display/hide what changed
      if (result===true) {
         var skill = skills_html[i];
         if (skill == null) {
            skill = output_skill(i);
            skills_html[i] = skill;
         }
         var forks = skill.match(/[0-9]+\.\.[0-9]+/g);
         if (forks != null)
            for (var j in forks)
               if (!isNaN(j)) {
                  var fork = forks[j].match(/([0-9]+)\.\.([0-9]+)/);
                  var att_value = div('query').value.match(eval('/'+attribute+'=([0-9]+)/i'));
                  if (att_value != null)
                     skill = skill.replace(forks[j], Math.round((fork[2]-fork[1])/15*parseInt(att_value[1])+parseInt(fork[1])));
               }

         output += skill;
      }
   }
   if (output.length == 0)
      div('skill_list').innerHTML = '';
   else
      div('skill_list').innerHTML = output;
   output = '';
   return true;
};

var skills_html = new Array();
var next_skill = 0;
function output_skills(selection) {
/*
   for (var i in db)
      skills_html[i] = output_skill(i);
*/
};


function output_skill(i) {
   var s = db[i];
   var elite_background = (s.elite==1) ? ' style="background-color:#EAE5B7;"' : '';
   var yellow_elite = (s.elite==1) ? ' <span style="color: #B8860B">[elite]</span>' : '';
   var recharge_ornot = (s.recharge>0) ? (' - '+s.recharge+' <img src="gwbbcode/img_interface/rech.gif"/>') : '';
   var eregeneration = (s.eregen>0) ? (' - '+s.ergen+' <img src="gwbbcode/img_interface/eregen.gif"/>') : '';
   var sacrifice = (s.sacrifice>0) ? (' - '+s.sacrifice+' <img src="gwbbcode/img_interface/sac.gif"/>') : '';
   var cost = (s.energy>0) ? (' - '+s.energy+' <img src="gwbbcode/img_interface/ener.gif"/>') : ((s.adrenaline>0) ? (' - '+s.adrenaline+' <img src="gwbbcode/img_interface/adre.gif"/>') : '');
   var fork_desc = s.desc.replace(/([0-9]+\.\.[0-9]+)/g, '<span style="color: #139713">$1</span>');

   return '<div class="skill"><table><tr'+elite_background+'><td align="middle" class="table_image" width="45"><img class="skill_img" src="gwbbcode/img_skills/'+i+'.jpg?r=1"/></td><td class="table_text"><b>&nbsp;'+s.name+yellow_elite+'</b>&nbsp;<img src="gwbbcode/img_interface/'+s.prof+'.gif"/>&nbsp;<input tabindex="5" name="post" value="Add skill" type="button"  onclick="insert(\'['+name.replace(/"/g, '\\x22').replace(/'/g, '\\x27')+']\', \'\');"/><br/><span style="font-size:11px">'+s.type+'. '+fork_desc+'</span><br/>'+s.attribute+eregeneration+sacrifice+cost+' - '+s.casting+' <img src="gwbbcode/img_interface/cast.gif"/>'+recharge_ornot+' </td></tr><tr width="20"><td></td></tr></table></div>';
}



//Extract a skill's information from its array to variables
function extract(s) {
   //ntleagcrpb
   //TODO use eval() to automatize this
   name = s.name;
   type = s.type;
   elite = s.elite;
   energy = s.energy;
   adre = s.adrenaline;
   sac = s.sacrifice;
   eregen = s.eregen;
   casting = s.casting;
   recharge = s.recharge;
   prof = s.profession;
   attr = s.attribute;
   desc = s.desc;

   cost = (energy!=null) ? (' - '+energy+' <img src="gwbbcode/img_interface/ener.gif"/>') : ((adre!=null) ? (' - '+adre+' <img src="gwbbcode/img_interface/adre.gif"/>') : '');
}


//Make an object out of a skill's information from its array to variables
function skill2object(s) {
   //ntleagcrpb
   //TODO use eval() to automatize this
   var k = new Object();
   k.name = s[0];
   k.typ = s[1];
   k.elite = (s[2] ? '1' : '0');
   k.energy = s[3];
   k.adre = s[4];
   k.eregen = s[5];
   k.casting = s[6];
   k.recharge = s[7];
   k.profession = s[8];
   k.attribute = s[9];
   k.desc = s[11];

   k.prof = array_value(k.profession, professions);
   k.attr = array_value(k.attribute, attributes);
   k.type = array_value(k.typ, types);
   k.cost = (k.energy!=null) ? (' - '+k.energy+' <img src="gwbbcode/img_interface/ener.gif"/>') : ((k.adre!=null) ? (' - '+k.adre+' <img src="gwbbcode/img_interface/adre.gif"/>') : '');

   return k;
}

function parse_xml(where, end, attributes) {
   //Parse the XML file
   var db = div(where).contentWindow.document.firstChild.innerHTML;
   db = db.replace(/&lt;/gm, '<').replace(/&gt;/gm, '>');
   db = db.replace(/^.*<skills>/m, '').replace(/<\/skills>.*$/m, '');
   db = db.substring(0, db.length - end.length);
   db = db.split(end);

   //Example: <s n="Aftershock" t="sp" e="10" c="0.75" r="10" p="E" b="ear" i="174">Adjacent enemies are struck for 26-85 damage...
   var db2 = new Array();
   for (i in db) {
      var line = db[i];
      skill = new Array();
      var j;
      for (j=0; j<attributes.length; j++) {
         //Example: skill.match(/n="([^"]+)"/);
         eval('var reg = line.match(/' + attributes.charAt(j) + '="([^"]+)"/);');
         skill[j] = (reg == null || reg.length < 1) ? null : reg[1].replace(/@/g, '"');
      }
      var reg = line.match(/>(.+)$/);
      skill[j] = (reg == null || reg.length < 1) ? null : reg[1].replace(/@/g, '"');
      db2[skill[10]] = skill;
   }
   return db2;
};



//////////////////
// HoH win time //
//////////////////

//Initialize HoH timer
var remain = new Date();
remain.setTime(12*60*1000);   //Set to 12 minutes before we synchronize
timer();

//Recursive function showing how much time there's left before next Hall of Heroes
//Outputs in hoh_jump div, keeps track of timing using var remain
function timer() {
   self.setTimeout('timer();', 1000);
   remain.setTime(remain.getTime() - 1000);
   //Resynchronise the timer every 12 minutes
   if (remain.getTime() == cout.hoh_sync) {
      cout.hoh_time_left = remain.getTime();
      send_form('hoh_sync');
   }

   //Reset the timer if it drops to 0
   if (remain.getTime() == 0) {
      //Set to 6 minutes
      remain.setTime(12*60*1000);
   }
   div('hoh_jump').innerHTML = remain.getMinutes() + ( (remain.getSeconds()<10)? ':0' : ':' ) + remain.getSeconds() + ' left till next Hall of Heroes win.';
}





///////////
// LOGIN //
///////////

//Switch a checkbox on and off
function switch_checkbox(boxname) {
   set_checkbox(boxname, !get_checkbox(boxname));
}

//Set a checkbox to true or false
function set_checkbox(boxname, visible) {
   if (visible) {
      div(boxname).src = 'img/true.gif';
   }
   else {
      div(boxname).src = 'img/false.gif';
   }
}

//Returns true if checkbox is selected
function get_checkbox(boxname) {
   return (div(boxname).src.indexOf('img/false.gif') == -1);
}

//Clean the status line
function clean_login_status() {
   div('login_status').innerHTML = '';
   resize();
}




//TOOLS
///////

function div(name) {
   var d = document;
   if(d.getElementById && !(d.all))
      return d.getElementById(name);
   else if(d.all)
      return d.all[name];
};

function array_value(needle, haystack) {
   needle = needle.toLowerCase().replace(' ', '');
   for(var j in haystack)
      if (haystack[j].toLowerCase().replace(' ', '').indexOf(needle) == 0)
         break;
   return (j<haystack.length) ? haystack[j] : null;
}

function quoted(variable) {
   var ret = '\'' + variable.toString().replace(/\\/g, '\\\\').replace(/\r/g, '').replace(/\n/g, '\\n').replace(/\x27/g, '\\\'') + '\'';
   if (ret.length < 100) {
      return ret;
   }
   else {
      return '\'Too big\'';
   }   
}

//shows in the debug_var div the values of var named i
function debug_var(field, changed, cin, cout) {
   output = 'changed.' + field + ' == ' + changed.toString() + '<br/>'
          + 'cin.'     + field + ' == ' + cin.toString()     + '<br/>'
          + 'cout.'    + field + ' == ' + cout.toString()    + '<br/><br/>';
   div('debug_var').innerHTML = output;
   output = '';
}


var prev_query = '';
function search_query() {
   query = div('query').value;
   if (prev_query != query) {
      prev_query = query;
      init_select();
   }
}

var prev_name = '';
function search_name() {
   name = div('name').value;
   if (prev_name != name) {
      prev_name = name;
      select('name', name, false);
   }
}

var prev_desc = '';
function search_desc() {
   desc = div('desc').value;
   if (prev_desc != desc) {
      prev_desc = desc;
      select('desc', desc, false);
   }
}


function clean_query() {
   //supress double & or | and place one space before and after
   user_query = user_query.replace(/\s*\&+\s*/g, ' & ').replace(/\s*\|+\s*/g, ' | ');

   //clearn any useless && or ||
   user_query = user_query.replace(/^ (\&|\|) /, '').replace(/ (\&|\|) $/, '');

   //change 'name(value)' to 'value'
   user_query = user_query.replace(/name\(([^\)\]]+)\)/, '$1');

   //remove '& true' and '| true'
   user_query = user_query.replace(/^true [\&\|] /, '').replace(/ [\&\|] true/, '');

   //remove spaces at start or end
   user_query = user_query.replace(/^\s+/, '').replace(/\s+$/, '');

   //remove '& &' and '| |'
   user_query = user_query.replace(/\& \&/g, '&').replace(/\| \|/g, '|');
}


var show_at = new Date();
function select(name, condition, auto) {
   clean_query();

   user_query = div('query').value;

   //remove spaces at start and end, and replace them by one space
   user_query = user_query.replace(/^\s*/, ' ').replace(/\s*$/, ' ');

   //replace an orphelin value by name(value)
   user_query = user_query.replace(/ ([^<>=!\&\|\[\]\(\) ]+) /g, 'name($1)');

   if (condition != 'All' && condition != '') {
      if (name == 'name' || name == 'desc')
         user_query = new_condition(user_query, name, '('+condition+')');
      else
         if (name == 'type')
            user_query = new_condition(user_query, 'type', '['+condition+')');
         else {
            //prof or attr?
            if (condition.substr(0, 1) == condition.substr(0, 1).toUpperCase()) {
               user_query = new_condition(user_query, 'prof', '['+condition+')');
               user_query = delete_condition(user_query, 'attr');
            }
            else if (condition.length >= 3)
               //no attributes from a profession, e.g. noW
               if (condition.substr(0, 2) == 'no' && condition != 'no a') {
                  user_query = new_condition(user_query, 'prof', '['+condition.substr(2)+')');
                  user_query = new_condition(user_query, 'attr', '[no)');
               }
               else {
                  user_query = delete_condition(user_query, 'prof');
                  user_query = new_condition(user_query, 'attr', '['+condition+')');
               }
         }
   }
   else {
      user_query = delete_condition(user_query, name);
      if (name == 'attr')
         user_query = delete_condition(user_query, 'prof');
   }


   clean_query();
   div('query').value = user_query;

   //Only show skills if no query modification was done during the past second
   show_at = (new Date()).getTime()+900;
   if (auto)
      self.setTimeout('show_skills_if();', 1000);
}


function new_condition (user_query, name, condition) {
   var reg = eval('/'+name+'[\\[\\(][^\\])]+\\)/');
   if (reg.test(user_query))
      //replace the first existing condition of the right name
      user_query = user_query.replace(reg, name+condition);
   else
      //or simply add the condition
      user_query += ((user_query.length > 0) ? ' && ' : '') + name+condition;
   return user_query;
}


function delete_condition (user_query, name) {
   var reg = eval('/'+name+'[\\[\\(][^\\])]+\\)/');
   if (reg.test(user_query))
      //remove the first existing condition of the right name
      user_query = user_query.replace(reg, '');
   return user_query;
}


function show_skills_if() {
   if (show_at <= (new Date()).getTime())
      show_skills();
}



function init_select() {
   query = div('query').value;

   //remove spaces at start and end, and replace them by one space
   query = query.replace(/^\s*/, ' ').replace(/\s*$/, ' ');

   //replace an orphelin value by name(value)
   query = query.replace(/ ([^<>=!\&\|\[\]\(\) ]+) /g, 'name($1)');


   var properties = new Array('attr', 'type', 'name', 'desc');
   for (var i in properties) {
      var prop = query.match(eval('/'+properties[i]+'[\\[\\(]([^\\)\\]]+)[\\]\\)]/'));
      var pdiv = div(properties[i]);
      if (prop != null) {
         if (typeof(pdiv.length) == 'undefined')
            pdiv.value = prop[1];
         else
            for(var j=1; j<pdiv.length; j++)
               if (pdiv[j].value == prop[1]) {
                  pdiv.selectedIndex = j;
                  break;
               }
      }
      else
         if (typeof(pdiv.length) == 'undefined')
            pdiv.value = '';
         else
            pdiv.selectedIndex = 0;
   }

   show_skills();
}



var output = ' \
<center> \
<form name="search" id="search"> \
<table cellspacing="0" cellpadding="0" style="margin: 0; padding: 0; height: auto"><tbody> \
<tr><td>Profession:</td> \
<td><select id="attr" name="attr" style="overflow: visible; width: 11em;" onchange="select(this.name, this[this.selectedIndex].value, true)"> \
<option value="All" selected >All</option> \
<option value="no a">- No Attribute</option> \
 \
<option value="W">Warrior</option> \
<option value="axe">- Axe Mastery</option> \
<option value="ham">- Hammer Mastery</option> \
<option value="str">- Strength</option> \
<option value="swo">- Swordsmanship</option> \
<option value="tac">- Tactics</option> \
<option value="noW">- No Attribute</option> \
 \
<option value="Ra">Ranger</option> \
<option value="bea">- Beast Mastery</option> \
<option value="exp">- Expertise</option> \
<option value="mar">- Marksmanship</option> \
<option value="wil">- Wilderness Survival</option> \
<option value="noRa">- No Attribute</option> \
 \
<option value="Mo">Monk</option> \
<option value="div">- Divine Favor</option> \
<option value="hea">- Healing Prayers</option> \
<option value="pro">- Protection Prayers</option> \
<option value="smi">- Smiting Prayers</option> \
<option value="noMo">- No Attribute</option> \
 \
<option value="N">Necromancer</option> \
<option value="blo">- Blood Magic</option> \
<option value="cur">- Curses</option> \
<option value="deat">- Death Magic</option> \
<option value="sou">- Soul Reaping</option> \
<option value="noN">- No Attribute</option> \
 \
<option value="Me">Mesmer</option> \
<option value="dom">- Domination Magic</option> \
<option value="fas">- Fast Casting</option> \
<option value="ill">- Illusion Magic</option> \
<option value="ins">- Inspiration Magic</option> \
<option value="noMe">- No Attribute</option> \
 \
<option value="E">Elementalist</option> \
<option value="air">- Air Magic</option> \
<option value="earth m">- Earth Magic</option> \
<option value="ene">- Energy Storage</option> \
<option value="fir">- Fire Magic</option> \
<option value="wat">- Water Magic</option> \
<option value="noE">- No Attribute</option> \
 \
<option value="A">Assassin</option> \
<option value="cri">- Critical Strikes</option> \
<option value="dag">- Dagger Mastery</option> \
<option value="dead">- Deadly Arts</option> \
<option value="sha">- Shadow Arts</option> \
<option value="noA">- No Attribute</option> \
 \
<option value="Ri">Ritualist</option> \
<option value="spa">- Spawning Power</option> \
<option value="cha">- Channeling Magic</option> \
<option value="commu">- Communing</option> \
<option value="res">- Restoration Magic</option> \
<option value="noRi">- No Attribute</option> \
 \
<option value="D">Dervish</option> \
<option value="earth p">- Earth Prayers</option> \
<option value="win">- Wind Prayers</option> \
<option value="mys">- Mysticism</option> \
<option value="scy">- Scythe Mastery</option> \
<option value="noD">- No Attribute</option> \
 \
<option value="P">Paragon</option> \
<option value="comma">- Command</option> \
<option value="mot">- Motivation</option> \
<option value="lea">- Leadership</option> \
<option value="spe">- Spear Mastery</option> \
<option value="noP">- No Attribute</option> \
</select>&nbsp;&nbsp;</td> \
 \
<td>Type:</td> \
<td><select id="type" name="type" onchange="select(this.name, this[this.selectedIndex].value, true)" style="width: 10em;"> \
   <option value="All" selected >-</option> \
   <option value="at">Attack</option> \
   <option value="bo">Bow Attack</option> \
   <option value="pe">Pet Attack</option> \
   <option value="me">Melee Attack</option> \
   <option value="ax">Axe Attack</option> \
   <option value="ha">Hammer Attack</option> \
   <option value="sc">Scythe Attack</option> \
   <option value="spea">Spear Attack</option> \
   <option value="sw">Sword Attack</option> \
   <option value="le">Lead Attack</option> \
   <option value="du">Dual Attack</option> \
   <option value="of">Off-Hand Attack</option> \
   <option value="sp">Spell</option> \
   <option value="en">Enchantment Spell</option> \
   <option value="he">Hex Spell</option> \
   <option value="it">Item Spell</option> \
   <option value="wa">Ward Spell</option> \
   <option value="wea">Weapon Spell</option> \
   <option value="wel">Well Spell</option> \
   <option value="sk">Skill</option> \
   <option value="si">Signet</option> \
   <option value="gl">Glyph</option> \
   <option value="bi">Binding Ritual</option> \
   <option value="ch">Chant</option> \
   <option value="ec">Echo</option> \
   <option value="fo">Form</option> \
   <option value="na">Nature Ritual</option> \
   <option value="pr">Preparation</option> \
   <option value="sh">Shout</option> \
   <option value="st">Stance</option> \
   <option value="tr">Trap</option> \
</select></td></tr> \
 \
<tr><td>Name:</td> \
<td><input id="name" size="20" onkeyup="search_name()" style="width: 11em;"/>&nbsp;&nbsp;</td> \
<td>Desc.:</td> \
<td><input id="desc" size="20" onkeyup="search_desc()" style="width: 10em;"/>&nbsp;&nbsp;</td></tr> \
 \
<tr style="display: none"><td colspan="4"><input id="query" size="30" onkeyup="search_query()"/> \
<span onclick="var d=div(\'search_help\').style; d.display = (d.display==\'none\')?\'\':\'none\';" class="link">Help!</span></td></tr> \
 \
<tr><td>&nbsp;</td> \
<td><button type="button" onclick="show_skills();" style="width: 11em;">Search</button></td></tr> \
</tbody></table> \
</form> \
</center> \
<div id="skill_list"></div> \
 \
';

div('skills').innerHTML = output;
output = '';


loaded_skills();

if (getArticleID().length) {
   select_right_tab('Att Points');
}
else {
   select_right_tab('Skills');
}
select_left_tab('Home');



