/******************************************************************************* DBX3.0 :: Docking boxes (dbx) ------------------------------------------------------------------------------ Copyright (c) 2009 brothercake                     http://www.brothercake.com/ BSD License                          See license.txt for licensing information Info/Docs               http://www.brothercake.com/site/resources/scripts/dbx/ ------------------------------------------------------------------------------*******************************************************************************///global dbx manager referencevar dbx;//docking boxes managerfunction dbxManager(sid, useid, hide, buttontype){	//global reference to this	dbx = this;	//throw exception and stop if the session ID isn't valid	if(!/^[-_a-z0-9]+$/i.test(sid)) { throw('Error from dbxManager:\n"' + sid + '" is an invalid session ID'); return; }	//identify some browsers for a few little things 	this.kde = navigator.vendor == 'KDE';	this.safari = navigator.vendor == 'Apple Computer, Inc.';	this.chrome = navigator.vendor == 'Google Inc.';	this.opera = typeof window.opera != 'undefined';	this.msie = typeof document.uniqueID != 'undefined';		//whether a browser is supported	//we'll do tests here to set the flag and then re-use it in the dbxGroup constructor	//so this browser isn't supported if the '*' collection isn't supported,	//or if this is konqueror earlier than 3.2, or opera earlier than 8	//(we need the '*' collection so the dbx-box element can be anything)	//this affects win/ie5, and safari builds earlier than 1.2	//but we needed to cut out older safari builds anyway, because	//they don't support the absolute-in-relative contextual positioning	//that we need to make the clone positioning work)	this.supported = (!(		typeof document.getElementsByTagName == 'undefined'		|| document.getElementsByTagName('*').length == 0		|| (this.kde && typeof window.sidebar == 'undefined')		|| (this.opera && parseFloat(navigator.userAgent.toLowerCase().split(/opera[\/ ]/)[1].split(' ')[0], 10) < 8)		));	//don't continue if unsupported	if(!this.supported) { return; }	//identify supported method of adding encapsulated event listeners	this.etype = typeof document.addEventListener != 'undefined' 		? 'addEventListener' 		: typeof document.attachEvent != 'undefined' 			? 'attachEvent' 			: 'none';	//if encapsulated event listening is not supported,	//set flag to unsupported and don't continue	//in practise this will filter-out mac/ie5	if(this.etype == 'none') { this.supported = false; return; }	//set event name prefix	this.eprefix = (this.etype == 'attachEvent' ? 'on' : '');	//the session id is used as part of the cookie name	//so that one cookie can store multiple groups	//but single or multiple groups on different pages are discreet	//so that they don't wipe out each other's cookies	this.sid = sid;	//so using that, define the core cookie name	this.cookiename = 'dbx-' + this.sid + '=';	//if the useid value is undefined use the default value of false, otherwise save the value	//this controls whether box-ID based dynamic groups are enabled	//which is whether to use the box element's ID as its dbxid, or whether to generate one dynamically	//there needs to be a switch because if it's true it validates the ID and throws failure	//so this way it's backward compatible, because v2 ignored IDs completely	this.useid = typeof useid != 'undefined' && useid == 'yes' ? true : false;	//if the hide source box while dragging value is undefined,	//use the default value of true, otherwise save the value	this.hide = typeof hide != 'undefined' && hide == 'no' ? false : true;	//if the toggle button element type is undefined,	//use the default value of 'link', otherwise save the value	//the default of 'link' is for backward compatibility	//even though 'button' is the optimum value for best overall accessibility	this.buttontype = typeof buttontype != 'undefined' && buttontype == 'button' ? 'button' : 'link';	//count the number of simultaneous running timers, so that we can limit it	//the limit will be set the same as the number of boxes in a given group	//so that it is still possible for all of them to be animated simultaneously	//but not for multiple sets to be going at the same time	//the bottom-line reason for this is to improve efficiency, or more specifically,	//to reduce the possibility of very severe inefficiency leading to device slowdown	this.running = 0;	//for each named group we also want to assign it a numeric id	//which will become part of the cookie value	this.gnumbers = {};	//define the maximum number of boxes in a group	//which is 0 by default, meaning no limit	//any other number will be that maximum	this.max = 0;	//and for the same purpose we need to save the root cookiestate right now	//before we've done any changes or modification that would re-save it	//this is so we can avoid removing boxes that are incidental to your management	//ie, the box is there but not present in group cookie, and would otherwise be removed	//so we check that it's present somewhere in this root cookie value, otherwise ignore it	this.rootcookie = '';	if(document.cookie && document.cookie.indexOf(this.cookiename) != -1)	{		//extract only the part that relates to this dbx session		//to avoid getting false matches on values in other cookies		//that just happen to have the same string pattern!		this.rootcookie = document.cookie.split(this.cookiename)[1].split(';')[0];		//convert to internal format		this.rootcookie = this.rootcookie.replace(/\|/g, ',').replace(/:/g, '=');		//restore plus symbols only if they're not already there in the value		//so that we retain backward compatibility with before they were removed		if(this.rootcookie.indexOf('+') == -1)		{			this.rootcookie = this.rootcookie.replace(/(,|$|&)/ig, '+$1').replace(/(\-\+)/g, '-');		}	}	//create an object for storing save data	//(order and open-state of boxes, to save to cookie)	this.savedata = {};	//look for existing cookie state data	this.cookiestate = this.getCookieState();};//set state to cookie and output to receiver methoddbxManager.prototype.setCookieState = function(){	//format expiry date	var now = new Date();	now.setTime(now.getTime() + (365*24*60*60*1000));	//compile the save object data into state string for onstatechange event	this.compileStateString();	//convert the format to netscape compatible cookie string,	//(http://wp.netscape.com/newsref/std/cookie_spec.html	// which says that the data can't contain commas; even though the RFC allows it,	// we may as well make it fully compatible to avoid issues like this:	// http://trac.wordpress.org/ticket/2660)	//at the same time I've changed = for : to avoid ambiguity there as well	//and removed plus symbols to save space	//but we're only doing this at the last minute, and not overwriting this.state	//so that the internal format isn't affected	//and so that it remains backwardly compatible with earlier versions	this.cookiestring = this.state.replace(/,/g, '|').replace(/=/g, ':').replace(/\+/g, '');	//call the onstatechange method	//continue to save the cookie, if	//	the method doesn't exist; or	//	the method exists and returns true	if(typeof this.onstatechange == 'undefined' || this.onstatechange())	{		//create the cookie		document.cookie = this.cookiename			+ this.cookiestring			+ '; expires=' + now.toGMTString()			+ '; path=/';	}};//get state from cookiedbxManager.prototype.getCookieState = function(){	//set null reference so we always have something to return	this.cookiestate = null;	//if we have a cookie	if(document.cookie)	{		//if it's our cookie		if(document.cookie.indexOf(this.cookiename) != -1)		{			//extract order data			this.cookie = document.cookie.split(this.cookiename)[1].split(';')[0].split('&');			//iterate through resulting data			for(var i in this.cookie)			{				//ignore unwanted properties				if(this.unwanted(this.cookie, i)) { continue; }				//convert the format back to internal format				this.cookie[i] = this.cookie[i].replace(/\|/g, ',');				this.cookie[i] = this.cookie[i].replace(/:/g, '=');				//restore plus symbols only if they're not already there in the value				//so that we retain backward compatibility with before they were removed				if(this.cookie[i].indexOf('+') == -1)				{					this.cookie[i] = this.cookie[i].replace(/(,|$|&)/ig, '+$1').replace(/(\-\+)/g, '-');				}				//split into key and value				this.cookie[i] = this.cookie[i].split('=');				//split value (which is comma-delimited) into an array				this.cookie[i][1] = this.cookie[i][1].split(',');			}			//copy the cookie data into a state object			//so that when a dbx group is instantiated			//we can test whether there's existing cookie data for it			//using typeof this.cookiestate['container-id']			this.cookiestate = {};			for(i in this.cookie)			{				//ignore unwanted properties				if(this.unwanted(this.cookie, i)) { continue; }				//create member				this.cookiestate[this.cookie[i][0]] = this.cookie[i][1];			}		}	}	return this.cookiestate;};//compile the state string for for onstatechange eventdbxManager.prototype.compileStateString = function(){	//compile the save object data into a string	var str = '';	for(var j in this.savedata)	{		//ignore unwanted properties		if(this.unwanted(this.savedata, j)) { continue; }		//add to string		str += j + '=' + this.savedata[j] + '&'	}	//trim off the last stray ampersand	//and save the string to this.state	//so that the data is available from onstatechange	this.state = str.replace(/^(.+)&$/, '$1');};//create an HTML elementdbxManager.prototype.createElement = function(tag){	//create element using supported method and return it	return typeof document.createElementNS != 'undefined' 		? document.createElementNS('http://www.w3.org/1999/xhtml', tag) 		: document.createElement(tag);};//get an element identified by classname//by upwards iteration from event targetdbxManager.prototype.getTarget = function(e, pattern, node){	//if we have an explicit node reference, use that	if(typeof node != 'undefined')	{		var target = node;	}	//otherwise store a reference to the target node	else	{		target = typeof e.target != 'undefined' ? e.target : e.srcElement;	}	//[if we don't already have the element we want]	//iterate upwards from the target	//until we find a match with the specified element	while(!this.hasClass(target, pattern))	{		target = target.parentNode;				//if we reach a group container and it's still not the right target 		//we'll have to abandon the search and return null		if(this.hasClass(target, 'dbx\-group') && !this.hasClass(target, pattern))		{			return null;		}	}	//return the element	return target;};//extract the dbxid from an element classnamedbxManager.prototype.getID = function(element){	//if the element is null or doesn't have a classname, return null for failure	if(!element || !element.className) { return null; }	//or if it's a dummy return "dummy"	else if(this.hasClass(element, 'dbx\-dummy')) { return 'dummy'; }	//get the element classname and split it by dbxid	var cname = element.className.split('dbxid-');	//if we only have a single member then this element doesn't have a dbxid	//so in that case return null for failure	if(cname.length == 1) { return null; }	//otherwise return the second member	//parsed of any additional classname values	return cname[1].replace(/^([a-zA-Z0-9_]+).*$/, '$1');};//find a sibling box from a given box//with a fallback to reselect the original box//in case we run out of nodes in that directiondbxManager.prototype.getSiblingBox = function(root, sibling){	var node = root[sibling];	while(node && !this.hasClass(node, 'dbx\-box'))	{		node = node[sibling];	}	if(!node) { node = root; }	return node;};//get the position of an object with respect to the canvas//this is needed for moving objects between groups//but it's here in the core script just in case it should be needed in futuredbxManager.prototype.getPosition = function(obj, center){	var position = { 'left' : obj.offsetLeft, 'top' : obj.offsetTop };	var tmp = obj.offsetParent;	while(tmp)	{		position.left += tmp.offsetLeft;		position.top += tmp.offsetTop;		tmp = tmp.offsetParent;	}	//if we're returning a center point	//add half the object's width and height	if(center)	{		position.left += obj.offsetWidth / 2;		position.top += obj.offsetHeight / 2;	}	return position;};//get the viewport width using the various models in use by different browsers and rendering modes//this is used to keep script-generated tooltips inside the window//we're only repositioning in one direction here, to save code space//because horizontal positioning is critical since pages typically don't scroll that way//and therefore there's no easy way to get to an obscured portion//but vertical positioning less so, since they do and you can therefore scroll to see itdbxManager.prototype.getViewportWidth = function(){	return typeof window.innerWidth != 'undefined'		? window.innerWidth		: (typeof document.documentElement != 'undefined'			&& typeof document.documentElement.clientWidth != 'undefined'			&& document.documentElement.clientWidth != 0)			? document.documentElement.clientWidth			: this.get('body')[0].clientWidth;};//compile the data for, and dispatch, any onbeforestatechange function that existsdbxManager.prototype.compileAndDispatchOnBeforeStateChange = function(){	//create an actions object	var actions = {};	//for each of the argument arrays	for(var i=0; i<arguments.length; i++)	{		//create a shortcut reference to this data array		var data = arguments[i];		//store the values to dbx properties		this.dbxobject = data[1];		this.group = data[2];		this.gid = data[3];		this.sourcebox = data[4];		this.target = data[5];		this.action = data[6];		//call the function and store the return value to actions object		actions[data[0]] = this.onbeforestatechange();	}	//return the actions object	return actions;};//compile the data for and fire onanimate eventdbxManager.prototype.compileAndDispatchOnAnimate = function(box, clone, caller, count, res){	//store the necessary properties to the manager object	//then fire the function	dbx.sourcebox = box;	dbx.clonebox = clone;	dbx.dbxobject = caller;	dbx.group = caller.container;	dbx.anicount = count - 1;	dbx.anilength = res - 1;	dbx.onanimate();};//compile the data for and fire onafteranimate eventdbxManager.prototype.compileAndDispatchOnAfterAnimate = function(box, caller){	//store the necessary properties to the manager object	//then fire the function	dbx.sourcebox = box;	dbx.dbxobject = caller;	dbx.group = caller.container;	dbx.onafteranimate();};//check whether an enumerable property of an object is unwanted during iteration//this is uses in for..in iterators to weed out external prototypes//and any other members deemed generally unwanted while iterating through collectionsdbxManager.prototype.unwanted = function(obj, i){	//we don't want external members, function members, 	//undefined members, or members called "length"	return (!obj.hasOwnProperty(i) || typeof obj[i] == 'undefined' 		|| typeof obj[i] == 'function' || i == 'length');};//add an event listenerdbxManager.prototype.addEvent = function(node, type, handler){	node[this.etype](this.eprefix + type, handler, false);};//trim leading and trailing whitespace from a stringdbxManager.prototype.trim = function(str){	return str.replace(/^\s+|\s+$/g,"");};//similar to PHP's empty() function, but more strongly typeeddbxManager.prototype.empty = function(data){	if(typeof data == 'string' && this.trim(data) === '') { return true; }	else if(typeof data == 'object')	{		if(data instanceof Array && data.length == 0) { return true; }		else		{			var n = 0;			for(var i in data)			{				if(!data.hasOwnProperty(i)) { continue; }				n++;			}			if(n == 0) { return true; }		}	}	return false;};//does an element have a particular class name (as a boundaried substring)dbxManager.prototype.hasClass = function(element, pattern){	return (element.className && new RegExp(pattern + '($|[ ])').test(element.className));};//remove a value pattern from an element's class namedbxManager.prototype.removeClass = function(element, pattern){	if(typeof flags == 'undefined')	{		flags = '';	}	element.className = element.className.replace(new RegExp(pattern, 'g'), '');	if(!/\S/.test(element.className))	{		element.className = '';	}	return element;};//it's a simple dollar functiondbxManager.prototype.get = function(find, context){	var nodes = [];		//context.[sibling or child]	if(/(previous|next|first|last)(Sibling|Child)/.test(find))	{		context = context[find];		switch(find)		{			case 'nextSibling' :			case 'previousSibling' :							while(context && context.nodeType != 1)				{					context = context[find];				}				break;		}		return context;	}		//document.getElementById	else if(find.indexOf('#') != -1)	{		return document.getElementById(find.split('#')[1]);	}		//[document | context].getElementsByTagName	else 	{		if(typeof context == 'undefined') { context = document; }		return context.getElementsByTagName(find);	}};//create new docking boxes groupfunction dbxGroup(){	//don't continue if the script is unsupported	if(!dbx.supported) { return; }	//store the arguments collection (we have to do this because	//Safari doesn't allow writing back to arguments collection)	var args = arguments;	//throw exception and stop if the container ID isn't valid	if(!/^[-_a-z0-9]+$/i.test(args[0]) || args[0] == 'deleted') { throw('Error from dbxGroup:\n"' + args[0] + '" is an invalid container ID'); return; }	//group container	this.container = dbx.get('#' + args[0]);		//warn (throw and stop) if the container doesn't have the dbx-group class name	if(!dbx.hasClass(this.container, 'dbx\-group')) { throw('Error from dbxGroup:\nGroup container (the element with id="' + args[0] + '") must contain the class name "dbx-group"'); return; }	//don't continue if the container doesn't exist	//silently fail because this could happen in the wild even if the script is properly configured	//(eg. when rendering or loading issues create an incomplete DOM)	//and we don't want end users to see error alerts	if(!this.container) { return; }	//group id	this.gid = args[0];	//create transient elements for key dynamic classes	//so that if those classes contain any image backgrounds	//those images will be preloaded	this.cacheDynamicClasses();	//if an onbeforestatechange function is defined	if(typeof dbx.onbeforestatechange != 'undefined')	{		//compile the data for, and dispatch, any onbeforestatechange function that exists		//and store the result to actions object		var actions = dbx.compileAndDispatchOnBeforeStateChange(			['proceed', this, this.container, this.gid, null, null, 'load']);		//if we're not okay to proceed, don't continue		//but first set the container reference back to null		//so that this group shows as non-instantiated		if(!actions.proceed)		{			this.container = null;			return;		}	}	//container orientation, defaults to freeform	this.orientation = /^(freeform|confirm|horizontal|vertical|insert(\-swap|\-insert)?)/.test(args[1]) ? args[1] : 'freeform';	//if the value is just "insert" that means "freeform-insert"	if(this.orientation == 'insert') { this.orientation = 'freeform-insert'; }	//exchange mode is swap by default, meaning that the box you're holding and the insertion 	//box are swapped over; the alternative mode is "insert", where everything from 	//the insert block to the end is shifted along by one	this.exchange = 'swap';		//if the orientation contains insert, then exchange mode is insert	//and orientation is just the first value (see next expression)	if(/(freeform|confirm)\-insert/.test(this.orientation))	{		this.exchange = 'insert';	}		//if it contains any additional value, remove it	//this finishes the task above, and is also for general safety	this.orientation = this.orientation.split('-')[0];		//confirm orientation is freeform orientation,	//but with a confirmation dialog before swapping	//so if that's the value, set confirm mode and reset orientation	this.confirm = false;	if(this.orientation == 'confirm')	{		this.confirm = true;		this.orientation = 'freeform';	}			//drag threshold - how far the cursor must move before the drag registers	this.threshold = parseInt(args[2], 10);	if(isNaN(this.threshold)) { this.threshold = 0; }	//restrict drag movement to container axis	//store this value the same as the container orientation	//(if it's "yes", because we need to know the restriction axis)	//otherwise set it to an empty string (if it's "no" or invalid)	this.restrict = args[3] == 'yes' ? this.orientation : '';		//if the resolution is zero, set it to 1	//this avoids having to have a no-animation condition	//and visually appears the same to anyone's eye	this.resolution = parseInt(args[4], 10);	if(isNaN(this.resolution)) { this.resolution = 0; }	if(this.resolution == 0) { this.resolution = 1; }	//include open/close toggles	this.toggles = args[5] == 'yes';	//is the box open by default	this.defopen = args[6] != 'closed';	//vocabulary	this.vocab = {		'open' : args[7],		'close' : args[8],		'move' : args[9],		'toggle' : args[10],		'kmove' : args[11],		'ktoggle' : args[12],		'syntax' : args[13],		//we need to check the last two against undefined		//because they didn't exist in version 2		'kyes' : (typeof args[14] != 'undefined' ? args[14] : ''),		'kno' : (typeof args[15] != 'undefined' ? args[15] : '')		};	//reference to this	var self = this;	//drag ok flag	this.dragok = false;	//initially null reference to target box	this.box = null;	//initially null reference to dialog element	//which is a specially styled clone for tracking and highlighting movement selections	this.dialog = null;	//buffering timer for dialog animation	this.buffer = null;	//define an object for storing the object that's just moved and its direction	//so that we can track it to prevent multiple movements of the same object as necessary (in freeform orientation)	//or for working out the position comparisons (in linear orientations)	this.last = {		'box' : null,		'direction' : null		};			//store the box elements which are first-child and last-child at any given point	//this is used to implement dynamic first-child and last-child classes	//[CSS last-child won't work because the dummy is actually the last child]	this.child = {		'first' : null, 		'last' : null		};	//properties for tracking multiple keypress	//that indicate diagonal movement	this.keytimer = null;	this.currentdir = null;	//the rules object stores global rules, and any subset rules (indexed by class name)	//that specifies how all or those objects are allowed to move	//each of those objects stores a pointer for tracking patterns, and an array of moves	//a single move is just a directional limit, while multiple moves track patterns	this.rules = { 'global' : { 'pointer' : 0, 'rule' : [], 'actual' : [] } };	//we'll also need to remember when we test a rule,	//what its key (eg 'global') is, and the last direction to save to moves array	this.rulekey = '';	this.ruledir = '';	//re-inforce relative positioning and block display on group container	//the container must have positioning,	//because many other calculations depend on it	//I originally supported static positioning as well	//but this solves some browser clone positioning quirks	//which I otherwise couldn't fix cleanly	this.container.style.position = 'relative';	this.container.style.display = 'block';	//initialise the boxes in this group	//and recover any saved state and sparetokens	this.initBoxes(true, true);	//the keydown flag is used to tell key-initiated focus events from mouse-initiated focus events	this.keydown = false;	//the mousedown flag is an inverse of that, used as a backup 	//to compensate for a crack in konqueror's key event support	//(namely, that the document keydown event which sets the keydown flag doesn't fire at all)	//I suppose we could universally replace the keydown flag with this	//but I don't want to risk it at this stage of the game	//when it's so much safer and easier just to manage a handful of kde exceptions	this.mouseisdown = false;	//add a document mouseout handler	dbx.addEvent(document, 'mouseout', function(e)	{		//store event related target		if(typeof e.target == 'undefined') { e.relatedTarget = e.toElement; }		//if the related target is null, fire the mouseup handler		//this catches mouse movement outside the window while holding a clone		//which could cause "sticky mouse", as onmouseup doesn't fire outside the window		if(e.relatedTarget == null)		{			//pass event to mouseup handler			self.mouseup(e);		}			});	//add document mousemove handler for when we're moving the clone	//which we can also use to implement the dynamic box hover class	dbx.addEvent(document, 'mousemove', function(e)	{		//pass event to hover method		self.hover(e);				//pass event to mousemove handler		self.mousemove(e);		//IE needs this so that the drag action works properly for links		//otherwise it "mis-fires", as it were, and generates that black "no action" symbol		//leading to a series of bugs that results in the clone being sticky to the mouse		//and resetting at whatever arbitrary position you let go of it		//drag 'n' drop scripting always requires this return false on the mousemove		//so I feel pretty silly for not having noticed it before, even though		//I've never really understood why it's necessary in the first place		//--- Thanks to Thomas Karl for reporting this bug ---//		//however if it returns false all the time then		//text-range selection is broken completely in IE		//so we have to return by the inverse of the dragok flag, for whether		//a drag action is occuring (so return false), or this is unrelated mousemovement (so return true)		return !self.dragok;	});	//add document mousedown handler purely to set the mousedown flag	dbx.addEvent(document, 'mousedown', function(e)	{		//clear the mousedown flag		self.mouseisdown = true;	});		//add document mouseup handler for when we let go of the clone	dbx.addEvent(document, 'mouseup', function(e)	{		//clear the mouseisdown flag		self.mouseisdown = false;				//pass event to mouseup handler		self.mouseup(e);	});	//add document keydown handler to set the keydown flag 	//and to implement the dynamic active class	dbx.addEvent(document, 'keydown', function(e)	{		//set the keydown flag		self.keydown = true;		//if we have an existing dialog and this is not an arrow key, enter or meta key		//(shift is what we're interested in, but checking others allows for future expansion)		//we want to remove it because it will be residual from an abandoned action		if(self.dialog && !/^((3[7-9])|40|13|(1[6-8]))$/.test(e.keyCode))		{			self.clearDialog();		}	});	//add a keyup handler to clear the keydown flag and active classes	dbx.addEvent(document, 'keyup', function()	{		//clear the keydown flag		self.keydown = false;		//clear the keyboard direction property		self.currentdir = null;		//remove any active class (but not target class);		self.removeActiveClasses('dbx\-box\-active');	});};//initialize the boxes in this groupdbxGroup.prototype.initBoxes = function(recover, getspare){	//dictionary of box objects, including a manual length property	//because we won't be able to derive one otherwise	this.boxes = { 'length' : 0 };		//array of handles, for referring to in the hover class routine	this.handles = [];	//dictionary of buttons, so we can save them on creation	//and then have a reference to initialise them from cookie data	this.buttons = {};	//box order array, needs to be an array specifically	//because we use array methods on it	this.order = [];	//copy a reference to this	var self = this;		//get all elements within this container	this.eles = dbx.get('*', this.container);	//for each element - iterating by length because it will be changing	for(var i=0; i<this.eles.length; i++)	{		//the dbxid token is based on the boxes length counter		var dbxid = this.boxes.length;		//if it's a docking box and not a dummy		//(or a clone which might be temporarily inside the box		// but musn't be counted as a permanent member)		if(dbx.hasClass(this.eles[i], 'dbx\-box') && !dbx.hasClass(this.eles[i], 'dbx\-(dummy|clone)'))		{			//if box-ID based dynamic groups are enabled			if(dbx.useid)			{				//if it has a valid ID, use that for dbxid				if(/^[a-z][a-z0-9]*$/i.test(this.eles[i].id) && !/^(length|dummy)$/.test(this.eles[i].id))				{					dbxid = this.eles[i].id;				}				//if it has an ID but it's invalid, throw an exception and stop				else if(this.eles[i].id != '')				{					throw('Error from dbxGroup:\n"' + this.eles[i].id + '" is an invalid box ID');					return;				}			}			//add to dictionary of docking boxes and increase the length counter			this.boxes[dbxid] = this.eles[i];			this.boxes.length++;						//add its order in the array to the order array			//plus its open state ("+" or "-" for open or close), which is open by default			this.order.push(dbxid + '+');						//if this element doesn't already have handlers			if(typeof this.eles[i].hashandlers == 'undefined')			{				//bind mousedown handler				dbx.addEvent(this.eles[i], 'mousedown', function(e)				{					//convert event argument					if(!e) { e = window.event; }					//get box element from target					//then pass event reference and box to mousedown handler					self.mousedown(e, dbx.getTarget(e, 'dbx\-box'));				});								//set the hashandlers flag				this.eles[i].hashandlers = true;			}			//if we've already processed this box since it's been here this session			//we can continue straight on to the next iteration			//indeed, we must, to avoid the class name etc. being written multiple times to the same box			if(typeof this.eles[i].processed != 'undefined') { continue; }			//re-inforce relative positioning and block display			this.eles[i].style.position = 'relative';			this.eles[i].style.display = 'block';						//add its default open state as an additional classname			this.eles[i].className += ' dbx-box-open';			//then add the dbxid to the classname, so we can identify it whatever its position			//we're not using the actual id attribute because then it wouldn't be encapsulated or unique			this.eles[i].className += ' dbxid-' + dbxid;						//set the processed flag			this.eles[i].processed = true;		}		//if it's a handle		if(dbx.hasClass(this.eles[i], 'dbx\-handle'))		{			//add it to the handles collection			this.handles.push(this.eles[i]);						//save a reference to the parent box			var parentbox = dbx.getTarget(null, 'dbx\-box', this.eles[i]);						//if we're adding toggle buttons			if(this.toggles)			{				//get dbxid from parent box				dbxid = dbx.getID(parentbox);				//add toggle behavior, returning the button object for the cookie function to use				//(the function itself will either create a new button, or re-initialize existing				// depending on whether there's a button there already)				this.buttons[dbxid] = this.addToggleBehavior(this.eles[i]);			}			//else if we're not adding toggle buttons			//and this element doesn't already have handlers,			//attempt to bind a keyboard handlers to the handle itself			//which will work if the handle is a focussable element			else if(typeof this.eles[i].hashandlers == 'undefined')			{				//save a reference to the handle				var handle = this.eles[i];							//create a hasfocus flag to determine if the handle is focussed				//[see addToggleBehavior for a full explanation of this]				handle.hasfocus = dbx.opera || dbx.safari ? null : false;							//if the parent is not an ungrabbable box				if(!dbx.hasClass(parentbox, 'dbx\-nograb'))				{					//we need a key handler for the button					//we also want to be able to suppress page scrolling when appropriate					//but browsers have different ideas about which event that comes from, and what will work					//in ie, safari and chrome we need onkeydown, in moz we need onkeypress,					//in opera default action suppression may not work either way					//(later versions should be okay, but not earlier versions)					dbx.addEvent(handle, 'key' + (dbx.msie || dbx.safari || dbx.chrome ? 'down' : 'press'), function(e)					{						//convert event argument						if(!e) { e = window.event; }												//pass event and handle to keypress handler						//return value determines whether native action happens						return self.keypress(e, dbx.getTarget(e, 'dbx\-handle'));						});						//bind click handler 					dbx.addEvent(handle, 'click', function(e)					{						//if we have a dialog						if(self.dialog)						{							//pass handle to click handler							self.click(e, dbx.getTarget(e, 'dbx\-handle'));											//prevent default action if that's supported							//otherwise return false (resulting in the same effect in ie)							if(typeof e.preventDefault != 'undefined') { e.preventDefault(); }							else { return false; }						}											});									}						//bind focus handler				dbx.addEvent(handle, 'focus', function(e)				{					//convert event argument					if(!e) { e = window.event; }										//save a reference to the parent box					var parentbox = dbx.getTarget(e, 'dbx\-box');					//if the keydown flag is set, 					//or this is konqueror and the mousedown flag is not set					if(self.keydown || (dbx.kde && !self.mouseisdown))					{						//add focus class name to parent box						parentbox.className += ' dbx-box-focus';					}					//save a reference to the handle					var handle = dbx.getTarget(e, 'dbx\-handle');							//get the tooltiptext from lang					var tooltiptext = self.vocab.kmove;							//if this is an ungrabbable box, we should only use the original text					//not add any instructions, because the box can't be moved					if(dbx.hasClass(parentbox, 'dbx\-nograb'))					{						tooltiptext = handle.getAttribute('oldtitle');					}							//if the handle has existing title text, combine the two as per config					//using the saved original value, because the actual value will have been modified					else if(!dbx.empty(handle.getAttribute('oldtitle')))					{						tooltiptext = self.vocab.syntax										.replace(/%mytitle[%]?/, handle.getAttribute('oldtitle'))										.replace(/%dbxtitle[%]?/, tooltiptext)							}										//if the text isn't empty, pass it and handle object to create tooltip					//plus the keydown/mouseisdown flag to determine if it's okay					if(!dbx.empty(tooltiptext))					{						self.createTooltip(							tooltiptext,							handle,							(self.keydown || (dbx.kde && !self.mouseisdown))							);					}										//set the has focus flag if it's not strictly null					if(handle.hasfocus !== null) { handle.hasfocus = true; }						});				//bind blur handler				dbx.addEvent(handle, 'blur', function(e)				{					//convert event argument					if(!e) { e = window.event; }					//remove any focus class name from parent box					dbx.removeClass(dbx.getTarget(e, 'dbx\-box'), 'dbx\-box\-focus');										//remove any tooltip that's there					self.removeTooltip();					//save a reference to the handle					//and clear the has focus flag if it's not strictly null					var handle = dbx.getTarget(e, 'dbx\-handle');					if(handle.hasfocus !== null) { handle.hasfocus = false; }						});								//set the hashandlers flag				this.eles[i].hashandlers = true;			}			//if we've already processed this handle 			//we can continue straight on to the next iteration			//indeed, we must, to avoid the class etc. being added multiple times to the same handle			if(typeof this.eles[i].processed != 'undefined') { continue; }			//save its original title (ie. what it has now, before processing)			//to another attribute, so we can refer to it for keyboard tooltips			var oldtitle = this.eles[i].getAttribute('title');			if(oldtitle) { this.eles[i].setAttribute('oldtitle', oldtitle); }			//re-inforce relative positioning and block display			this.eles[i].style.position = 'relative';			this.eles[i].style.display = 'block';			//if the parent is not an ungrabbable box			if(!dbx.hasClass(parentbox, 'dbx\-nograb'))			{				//add cursor classname				this.eles[i].className += ' dbx-handle-cursor';					//if the handle doesn't already have a title, create one				//if it does, append the title using pattern match syntax				this.eles[i].setAttribute('title', 					dbx.empty(this.eles[i].getAttribute('title'))						? this.vocab.move 						: this.vocab.syntax.replace(/%mytitle[%]?/, this.eles[i].title)							.replace(/%dbxtitle[%]?/, this.vocab.move)						);			}						//set the processed flag			this.eles[i].processed = true;		}		//if it's a content area or box, and this is IE, 		//add css to force it to "have layout" to fix any rendering bugs		//the content area is for a known issue with it being initially invisible		//the box itself is just in case (i think i saw it happen though)			//writing to runtimeStyle instead of style means that changes to the style object odn't affect it		//which makes it more stable and reliable in practise		if(dbx.msie && dbx.hasClass(this.eles[i], 'dbx\-(content|box)'))		{			this.eles[i].runtimeStyle.zoom = '1.0';		}	}				//update (or rather, initially add) the child classes	this.updateChildClasses();	//save this group in the dbx manager save data object	//with a string representation of its order	dbx.savedata[this.gid] = this.order.join(',');	//add a dummy docking box to the end of the container	//which we'll need as an insertBefore reference for boxes moved to the end	var dummy = this.container.appendChild(dbx.createElement('span'));	dummy.className = 'dbx-box dbx-dummy';	//re-inforce important styles	dummy.style.display = 'block';	dummy.style.width = '0';	dummy.style.height = '0';	dummy.style.overflow = 'hidden';	//apply offleft positioning	dummy.className += ' dbx-offdummy';	//create the dummy dbxid token	dbxid = this.boxes.length;	if(typeof dbx.gnumbers[this.gid] != 'undefined')	{		dbxid += '_' + dbx.gnumbers[this.gid];	}	//add to dictionary of docking boxes and increase the length counter	this.boxes[dbxid] = dummy;	this.boxes.length++;	//don't continue unless we want to recover a saved state	if(!recover) { return; }	//if there's cookie state data that relates to this group	if(dbx.cookiestate && typeof dbx.cookiestate[this.gid] != 'undefined')	{		//number of values		var num = dbx.cookiestate[this.gid].length;		//iterate through values		for(i=0; i<num; i++)		{			//the index of this box, stripped of its open/closed token			var index = dbx.cookiestate[this.gid][i].replace(/[\-\+]/g, '');			//if this box exists and its not the dummy			if(typeof this.boxes[index] != 'undefined' && this.boxes[index] != dummy)			{				//move this box before the last one				this.container.insertBefore(this.boxes[index], dummy);				//if we're using toggle buttons, and				//if the box (in its corresponding place in boxes array) should be closed				//--- Thanks to Ward Vandewege for the patch that allows for more than 10 boxes ---//				if(this.toggles && /\-$/.test(dbx.cookiestate[this.gid][i]))				{					//if the box button exists, send to toggle box state method, but don't save					//this if condition is purely to prevent an error due to bad config					//that is, when using dynamic groups without the box-ID being enabled					//which can happen if you add, modify, reload then try to remove a box!					//pass the flag that says this is not a manual interaction					//and the override flag that says to set this to closed (argument = isopen)					if(typeof this.buttons[index] != 'undefined')					{						this.toggleBoxState(this.buttons[index], false, false, true);					}				}			}		}		//regenerate the box order array (from which, save back to cookie)		this.regenerateBoxOrder();	}	//else if there is no cookie,	//and the box is set to be not-open by default	//and we're using toggle buttons	else if(!this.defopen && this.toggles)	{		//iterate through box buttons		for(i in this.buttons)		{			//ignore unwanted properties 			if(dbx.unwanted(this.buttons, i)) { continue; }						//send the box button to toggle box state method and save			//pass the flag that says this is not a manual interaction			//and an empty final argument that means toggle, don't force a state			this.toggleBoxState(this.buttons[i], true, false, null);		}	}};//create transient elements for key dynamic classes//so that if those classes contain any image backgrounds//those images will be preloadeddbxGroup.prototype.cacheDynamicClasses = function(){	//the lists of classes we're concerned with	//and an array to save the elements we create	var eles = [], classes = ['dbx-tooltip', 'dbx-dragclone', 'dbx-dialog'];	for(var i=0; i<classes.length; i++)	{		//create an element with the given class		//plus "dbx-clone" so that it's hidden		//appended inside the group container so it inherits correctly		eles[i] = dbx.createElement('div');		eles[i].className = 'dbx-clone ' + classes[i];		//don't append until classes are defined		//so it doesn't have a brief displacement effect		this.container.appendChild(eles[i]);	}	//now set a momentary timer, and remove all the elements	//100ms is more than plenty for any image requests to have started	//and they'll then continue even after the element is gone	setTimeout(function()	{		for(var i=0; i<eles.length; i++)		{			if(eles[i].parentNode)			{				eles[i].parentNode.removeChild(eles[i]);			}		}	}, 100);};//add docking box toggle (open and close) behaviordbxGroup.prototype.addToggleBehavior = function(){	//copy a reference to this	var self = this;	//look for an existing button in this handle	var existing = dbx.get((dbx.buttontype == 'link' ? 'a' : 'button'), arguments[0]);	for(var i=0; i<existing.length; i++)	{		//if we find one, save the reference and break		if(dbx.hasClass(existing[i], 'dbx\-toggle'))		{			var button = existing[i];			break;		}	}	//if we don't have a button reference we need to create a new one	if(typeof button == 'undefined')	{		//if the button type is link		if(dbx.buttontype == 'link')		{			//insert new toggle button as link			button = arguments[0].appendChild(dbx.createElement('a'));			//we need to add something inside the link, or it may not be key accessible to all			//so I'm going to write in a non-breaking space			//using a unicode reference, because we can't use an entity within createTextNode			//(there is a createEntityReference method, but no browser effectively implements it)			button.appendChild(document.createTextNode('\u00a0'));			//give it an href so it can accept the focus			button.href = 'javascript:void(null)';		}		//otherwise it must be button		else		{			//insert new toggle button as button			button = arguments[0].appendChild(dbx.createElement('button'));		}		//set classname and title to be initially open		button.className = 'dbx-toggle dbx-toggle-open';		button.setAttribute('title', this.vocab.toggle.replace(/%toggle[%]?/, this.vocab.close));	}	//set pointer cursor, else it will inherit move cursor from parent	button.style.cursor = 'pointer';	//create a hasfocus flag to determine if the button is focussed	//which we'll use to differentiate click events on the button	//and prevent them from working if the button isn't focussed	//this will prevent browser-based screenreaders from being able to undisplay the contents	//but that will fail in opera, safari and chrome, so we need to exclude them specifically	//fortunately there aren't any readers based on those browsers	//(opera 8+ has voice, but that's something else)	//we're using a tri-state flag here,	//so to avoid conflict with browsers doing automatic type conversion	//tests against this value are going to use strict [in]equality	button.hasfocus = dbx.opera || dbx.safari || dbx.chrome ? null : false;	//keyboard navigation tooltip object	this.tooltip = null;	//if this element doesn't already have handlers	if(typeof button.hashandlers == 'undefined')	{		//bind click handler to button		button.onclick = function(e)		{			//pass button to click handler			self.click(e, this);			//return false so we don't follow the link, even though it isn't one			//it's a dummy href value, but nonetheless, may cause the browser to respond			//at least partially as though following a link, eg, in IE it would make that "click" sound			return false;		};				//we need a key handler for the button		//but we also want to be able to suppress page scrolling when appropriate		//but browsers have different ideas about which event that comes from, and what will work		//in ie, safari and chrome we need onkeydown, in moz we need onkeypress,		//in opera default action suppression doesn't occur either way		button['onkey' + (dbx.msie || dbx.safari || dbx.chrome ? 'down' : 'press')] = function(e)		{			//convert event argument			if(!e) { e = window.event; }			//pass event and button object to keypress handler			//return value determines whether native action happens			return self.keypress(e, this);		};		//bind focus handler to button		button.onfocus = function()		{			//iterate through all buttons and remove existing hilite classname			for(var i in self.buttons)			{				if(dbx.unwanted(self.buttons, i)) { continue; }								self.buttons[i] = dbx.removeClass(self.buttons[i], '(dbx\-toggle\-hilite\-)(open|closed)');			}			//get open state from button classname			var isopen = dbx.hasClass(this, 'dbx\-toggle\-open');			//add the hilite classname			this.className += ' dbx-toggle-hilite-' + (isopen ? 'open' : 'closed');			//if the keydown flag is set,			//or this is konqueror and the mousedown flag is not set			if(self.keydown || (dbx.kde && !self.mouseisdown))			{				//add focus class name to parent box				dbx.getTarget(null, 'dbx\-box', this).className += ' dbx-box-focus';			}			//compile the tooltiptext according to open state			var tooltiptext = (!dbx.hasClass(dbx.getTarget(null, 'dbx\-box', this), 'dbx\-nograb')				? self.vocab.kmove : '')				+ self.vocab.ktoggle.replace(/%toggle[%]?/, (isopen ? self.vocab.close : self.vocab.open));			//if the handle has existing title text, combine the two as per config			//using the saved original value, because the actual value will have been modified			var handle = dbx.getTarget(null, 'dbx\-handle', this);			if(!dbx.empty(handle.getAttribute('oldtitle')))			{				tooltiptext = self.vocab.syntax								.replace(/%mytitle[%]?/, handle.getAttribute('oldtitle'))								.replace(/%dbxtitle[%]?/, tooltiptext)			}			//pass tooltiptext and button object to create tooltip			//plus the keydown (or kde not-mousedown) flag to determine if it's okay to move,			//passing the toggle instructions only if not			self.createTooltip(				tooltiptext,				this,				(self.keydown || (dbx.kde && !self.mouseisdown))				);			//set the isactive flag for focus setter in animation			//we need the flag to prevent setting focus on			//the animated elements that we only cloned and didn't move			//and to prevent setting highlight on pressed buttons from cookie initialisation			this.isactive = true;			//set the has focus flag if it's not strictly null			if(this.hasfocus !== null) { this.hasfocus = true; }		};		//bind blur handler to button		button.onblur = function()		{			//remove the hilite classname			button = dbx.removeClass(button, '(dbx\-toggle\-hilite\-)(open|closed)');			//remove any focus class name from parent box			dbx.removeClass(dbx.getTarget(null, 'dbx\-box', this), 'dbx\-box\-focus');			//remove any tooltip that's there			self.removeTooltip();			//clear the has focus flag if it's not strictly null			if(this.hasfocus !== null) { this.hasfocus = false; }		};		//set the hashandlers flag		button.hashandlers = true;	}	//return the button object	return button;};//toggle the state of boxdbxGroup.prototype.toggleBoxState = function(button, regen, manual, forcestate){	//get open state from button classname	var isopen = dbx.hasClass(button, 'dbx\-toggle\-open');	//or use force state if defined	if(forcestate !== null) { isopen = forcestate; }	//iteratively find a reference to the button's parent box	var parent = dbx.getTarget(null, 'dbx\-box', button);	//store values to dbx properties for external methods	dbx.sourcebox = parent;	dbx.toggle = button;	dbx.dbxobject = this;	//but the container might be undefined	//if this is called from the cookie function	if(typeof dbx.container == 'undefined')	{		//so in that case, retrieve it iteratively		dbx.group = dbx.getTarget(null, 'dbx\-group', parent);	}	//otherwise just copy it from container	else { dbx.group = dbx.container; }	//if an onbeforestatechange function is defined	if(typeof dbx.onbeforestatechange != 'undefined')	{		//compile the data for, and dispatch, any onbeforestatechange function that exists		//and store the result to actions object		var actions = dbx.compileAndDispatchOnBeforeStateChange(			['proceed', this, this.container, this.gid, parent, button, (isopen ? 'close' : 'open')]);		//if we're not okay to proceed, don't continue		if(!actions.proceed) { return; }	}	//if the manual flag is false (so that onboxopen and onboxclose don't fire from 	//                             programmatic toggle commands that restore a cookie state); or	//if the box is currently closed, and onopen doesn't exist or returns true; or	//if the box is currently open, and onclose doesn't exist or returns true	if	(		manual == false		||		(!isopen && (typeof dbx.onboxopen == 'undefined' || dbx.onboxopen()))		||		(isopen && (typeof dbx.onboxclose == 'undefined' || dbx.onboxclose()))	)	{		//change the classname and title		button.className = 'dbx-toggle dbx-toggle-' + (isopen ? 'closed' : 'open');		button.title = this.vocab.toggle.replace(/%toggle[%]?/, isopen ? this.vocab.open : this.vocab.close);		//if this is a manual interaction and the hilite classname is necessary, add it		if(manual && typeof button.isactive != 'undefined')		{			button.className += ' dbx-toggle-hilite-' + (isopen ? 'closed' : 'open')		}		//change the parent box open state classname		//which is both a stored value for us to read its state		//and used in a descendent selector to hide the inner content		parent.className = parent.className.replace(/[ ](dbx-box-)(open|closed)/, ' $1' + (isopen ? 'closed' : 'open'));		//if the regenerate flag is set,		//regenerate the box order array and save to cookie		if(regen) { this.regenerateBoxOrder(); }	}};//move a box (by conditions) using the keyboarddbxGroup.prototype.moveBoxByKeyboard = function(e, anchor, parent, direction, confirm, manual){	//store values to dbx properties for external methods	dbx.dbxobject = this;	dbx.group = this.container;	dbx.gid = this.gid;	dbx.sourcebox = parent;	dbx.clonebox = null;	dbx.event = e;	//define an index variable for storing a target box	//default to '-' which means no selection	var index = '-';	//the movement is positive if we're moving down or right	//unless we're moving down-left	//save it to a global property because we'll need it outside this method - 	this.positive = /[se]/i.test(direction);	if(/^(Sw)$/.test(direction)) { this.positive = false; }	//remove any target or active class names from the boxes in this group	this.removeActiveClasses('dbx\-box\-(target|active)');	//get the origin point of the selected box	var clonepoint = {		'x' : parent.offsetLeft,		'y' : parent.offsetTop		};	//create an array of differences with each comparison box	var differences = [];	//for most conditions the boxes collection we ant	//is all the boxes in this group	var boxes = this.boxes;	//for each box in the collection	for(var i in boxes)	{		//don't include members we don't care about, or the dummy		if(dbx.unwanted(boxes, i) || dbx.hasClass(boxes[i], 'dbx\-dummy')) { continue; }				//get the origin point of this box		var boxpoint = {			'x' : boxes[i].offsetLeft,			'y' : boxes[i].offsetTop			};		//work out the differences for this box		//which will be positive for right/down or negative for left/up		//and add it to the array of differences including the i index		//because we'll need that at the other end when it's sorted		differences.push([i, boxpoint.x - clonepoint.x, boxpoint.y - clonepoint.y]);		//if this is the box we're moving, store its index		//(but don't stop - we need to store all the differences)		if(parent == boxes[i]) { index = i; }	}	//create arrays for storing members by whether they have positive or negative difference	var splitdiffs = {		'positive' : [],		'negative' : []		};	//the applicable axis for direction (x for right/left, y for up/down)	var n = /[ew]/i.test(direction) ? 1 : 2;	//for each of the differences, add them to the correct array (as per axis)	for(i=0; i<differences.length; i++)	{		//don't add the parent box itself		if(differences[i][0] == index) { continue; }		//add to the relevant array		if(differences[i][n] >= 0)		{			splitdiffs.positive.push(differences[i]);		}		else		{			splitdiffs.negative.push(differences[i]);		}	}	//now if we're moving in a positive direction we only care about the positives array	//or if we're moving in a negative direction we only care about the negatives	var ary = this.positive ? splitdiffs.positive : splitdiffs.negative;	//sort the array by the same axis and using absolute numbers	//so that we find out which objects are closest by the movement direction	ary.sort(function(a, b){ return Math.abs(a[n]) - Math.abs(b[n]); });	//now check the axis for values in the positive arrays which are zero	//for example, movement right where the y axis is 0,	//hence it's an object on the same vertical line, and we need to remove it	for(i=0; i<ary.length; i++)	{		if(ary[i][n] == 0)		{			ary.splice(i--, 1);		}	}	//if this is diagonal movement,	if(direction.length > 1)	{		//remove members which are on a parallel		//and remove members which are in the wrong direction on the other axis		//this thinks in terms of diagonals being principly left or right, with an additional consideration		for(i=0; i<ary.length; i++)		{			if(				(/[ew]/i.test(direction) && ary[i][2] == 0)				||				(/[ns]/i.test(direction) && ary[i][1] == 0)				||				(/(N[ew])/.test(direction) && ary[i][2] > 0)				||				(/(S[ew])/.test(direction) && ary[i][2] < 0)				)			{				ary.splice(i--, 1);			}		}	}	//if it's not diagonal movement we don't want to remove diagonal paths	//because we may need to fallback on them when moving around irregularly sized shapes	//and because this reduces the possibility of accessibility problems due to	//a user not being able to press two keys at once, because if the interface requires it,	//it's still possible to move diagonally with single keys	//(nb. the rules engine ratifies this with the behavior of confirm mode	// which allows a user to navigate any path to a valid square through intervening invalid squares	// in case they can't press the keystrokes that would describe the most direct path (like a diagonal))	//now iterate and remove all which aren't the same as the lowest value	//so that we end up with arrays of just the lowest	for(i=0; i<ary.length; i++)	{		//if we're interested in positive we need absolute numbers		if(this.positive)		{			if(i > 0 && Math.abs(ary[i][n]) != Math.abs(ary[0][n]))			{				ary.splice(i--, 1);			}		}		//but for negatives we need to preserve their negativity		//can't remember why, but I'm sure there was a reason!		else		{			if(i > 0 && ary[i][n] != ary[0][n])			{				ary.splice(i--, 1);			}		}	}	//now we want to sort the arrays by the opposite axis	//to find which one is closest in that direction	n = n == 1 ? 2 : 1;	ary.sort(function(a, b){ return Math.abs(a[n]) - Math.abs(b[n]); });	//now if the array has no members then there's no object in this direction	//so reset the index to '-' for no selection	if(ary.length == 0)	{		index = '-';	}	//otherwise the index we want is at ary[0][0]	//plus one if this is positive movement	//(so that we effectively get an insert-after reference)	else	{		index = ary[0][0];	}	//get a reference to the box that this anchor relates	var box = dbx.getTarget(null, 'dbx\-box', anchor);	//if the index is '-' for no selection	if(index == '-')	{		//in any case don't continue here		//(return false so we pass a value back to the api move() method)		return false;	}	//the box we want then is boxes[index]	var targetbox = boxes[index];	//but if the exchange mode is insert and confirm mode is false and this is positive movement	//we want its next sibling [effectively, next sibling box or dummy]	//because we're going to want to insert after, not before,	//which of course translates to insert before the next one	//if we don't do this then moving to the right becomes impossible	//and moving down is counter-intuitive	//however if we run out of next siblings without finding a box	//we'll just have to maintain the previous target	//(note, we will need to do this modification for confirm mode as well	// but not in the display of the dialogs, only in the actual confirmed movement)	if(this.exchange == 'insert' && this.confirm == false && this.positive == true)	{		targetbox = dbx.get('nextSibling', targetbox);		if(!targetbox) { targetbox = boxes[index]; }	}	//if onboxdrag doesn't exist, or returns true	if(typeof dbx.onboxdrag == 'undefined' || dbx.onboxdrag())	{		//now we we know specifically which block we're trying to swap with		//which means we can work out the direction and blocks values for testing with		//but we only need to do that if the two boxes are not the same		//because you're always allow to move the caret back to the place you started from		//also don't do this if unless we're using boxes from this group		//because cross-group transfer is not affected by rules		if(box != targetbox && boxes == this.boxes)		{			//so first get the center point of the original box			var origpoint = {				'x' : box.offsetLeft + (box.offsetWidth / 2),				'y' : box.offsetTop + (box.offsetHeight / 2)				};			//then get the center point of the selected box			var boxpoint = {				'x' : targetbox.offsetLeft + (targetbox.offsetWidth / 2),				'y' : targetbox.offsetTop + (targetbox.offsetHeight / 2)				};			//get the blocks difference			var testblocks = this.getBlocksDifference(origpoint, boxpoint, box);			//get the compass direction			var testcompass = this.getCompassDirection(origpoint, boxpoint);			//evaluate the rules for this direction, blocks, box and no rulekey override			//and if we fail ...			if(this.functionExists('_testRules') && !this._testRules(testcompass, testblocks, box, null))			{				//if confirm mode is enabled or we already have a tracking dialog element				if(confirm || this.dialog)				{					//pass the box to update dialog method					//passing the additional state class, and no position override or group ref					//and the keyboard source flag					this.updateDialog(targetbox, ' dbx-dialog-no', null, null, 'keyboard');					//if we have the applicable action instructions defined					if(this.vocab.kno != '')					{						//pass action instructions and original box to create tooltip						//and true for the okay flag, so it definitely happens						//(keydown flag may not be true, even though this is key initiated						// because you press and release it to trigger the action)						this.createTooltip(							this.vocab.kno,							box,							true							);					}				}				//if this is a manual interaction, send focus back to the anchor				//and add back the focus class to the box				if(manual) { this.refocus(anchor); }				//don't continue if we don't get permission				//(return false so we pass a value back to the api move() method)				return false;			}		}		//if the boxes are not the same, and the target is not the dummy or a dialog		//apply the target class name to the target box		if(box != targetbox && !dbx.hasClass(targetbox, 'dbx\-(dialog|dummy)'))		{			targetbox.className += ' dbx-box-target';		}		//if confirm mode is enabled or we already have a tracking dialog element		if(confirm || this.dialog)		{			//for most situations we have no position and group override for update dialog			var diffs = null, group = null;			//but if we're using boxes from a different group, we do			if(boxes != this.boxes)			{				//store the group reference				group = this.dialog.group;				//we need to calculate the position difference between				//the calling group and the new group				//and then pass those values as position overrides to update dialog method				var groupcontainer = dbx.getPosition(group.container, false);				var callcontainer = dbx.getPosition(this.container, false);				var diffs = {					'x' : groupcontainer.left - callcontainer.left,					'y' : groupcontainer.top - callcontainer.top					};			}			//pass the selected box and additional state class to update dialog method			//along with the defined position and group overrides (or null)			//passing the additional state class even if the boxes are the same,			//because i reckon that makes for better usability - it's confusing when it disappears			//and makes you wonder whether it's still in the same mode			//I tried it with a third state which indicates "home square in confirm mode"			//but that just seemed more confusing than leaving it as normal confirmation			//also pass the source flag so we know how this dialog is generated			//so that we can prevent mouse movement from clearing keyboard generated dialogs			this.updateDialog(targetbox, ' dbx-dialog-yes', diffs, group, 'keyboard');			//if we have the applicable action instructions defined			if(this.vocab.kyes != '')			{				//pass action instructions and original box to create tooltip				//and true for the okay flag, so it definitely happens				//(keydown flag may not be true, even though this is key initiated				// because you press and release it to trigger the action)				this.createTooltip(					this.vocab.kyes,					box,					true					);			}			//if this is a manual interaction, send focus back to the anchor			//and add the focus class back to the parent box			//this manual test isn't really necessary, since currently only			//manual interactions can generate a dialog, but i like to be thorough!			if(manual) { this.refocus(anchor); }			//don't continue with the animation			//(return false so we pass a value back to the api move() method			//although this condition should never be true under those circumstances			//this is so it returns failure if something screws up!)			return false;		}		//if the exchange mode is swap, physically swap the two boxes		//passing the original box, selected box, and original box anchor		//and the argument that says whether or not is a manual interaction		//and the argument for whether this is positive or negative		//(to use for evaluating onbeforestatechange properties)		//return the return value of that back up to the api		if(this.exchange == 'swap')		{			return this.swapTwoBoxes(parent, targetbox, anchor, manual, this.positive);		}				//otherwise insert the original box before or after the selected box (depending on direction)		//and return the return value of that back up to the api		else		{			return this.insertTwoBoxes(parent, targetbox, anchor, manual, direction);		}	}	//if we get here then something screwed up	//so return false so we pass a value back to the api move() method	return false;};//insert one box before or after another, in response to keyboard actiondbxGroup.prototype.insertTwoBoxes = function(original, selected, anchor, manual, positive){	//if a beforestatechange function is defined	if(typeof dbx.onbeforestatechange != 'undefined')	{		//compile the data for, and dispatch, any onbeforestatechange function that exists		//then store the resulting action flag to an actions object		var actions = dbx.compileAndDispatchOnBeforeStateChange(			['proceed', this, this.container, this.gid, original, selected, 'insert']			);		//if the function returned false		//return false here in case we need to pass that back up to api		if(!actions.proceed) { return false; }	}	//update the rule pointer as necessary	if(this.functionExists('_updateRulePointer')) { this._updateRulePointer(); }	//the box animation will be on every box from the insertion point	//to the end of the group (not including the box we're moving),	//so we're going to have to create that collection as a numerically indexed array	var add = false, pointer = 0, theboxes = [], visiboxes = [];	for(var i in this.boxes)	{		if(dbx.unwanted(this.boxes, i)) { continue; }				theboxes.push(this.boxes[i]);	}	for(i=0; i<theboxes.length; i++)	{		if(theboxes[i] == original) { continue; }		visiboxes.push(theboxes[i]); 	}		//get the positions of those boxes	var visiposes = [];	for(i=0; i<visiboxes.length; i++)	{		visiposes.push({			'x' : visiboxes[i].offsetLeft,			'y' : visiboxes[i].offsetTop			});	}		//we'll also separately animate the box you're moving	var originalpos = { 'x' : original.offsetLeft, 'y' : original.offsetTop };	original.style.visibility = 'hidden';	//remove any target class name from the target box	selected = dbx.removeClass(selected, 'dbx\-box\-target');	//insert the original box before the selected box	selected.parentNode.insertBefore(original, selected);	//if we have a collection of boxes, animate each one	if(typeof visiboxes != 'undefined' && visiboxes.length > 0)	{		for(i=0; i<visiboxes.length; i++)		{			new dbxAnimator(this, visiboxes[i], visiposes[i], this.resolution, false, null, true);		}	}		//and animate the original box last, so it finishes on top (fnarr)	new dbxAnimator(this, original, originalpos, this.resolution, true, anchor, manual);	//regenerate the box order array and save to cookie	this.regenerateBoxOrder();	//return false for success, in case we need a value for the api	return true;};//physically swap two boxes over, in response to keyboard actiondbxGroup.prototype.swapTwoBoxes = function(original, selected, anchor, manual, positive){	//if a beforestatechange function is defined	if(typeof dbx.onbeforestatechange != 'undefined')	{		//compile the data for, and dispatch, any onbeforestatechange function that exists		//then store the r	esulting action flag to an actions object		var actions = dbx.compileAndDispatchOnBeforeStateChange(			['proceed', this, this.container, this.gid, original,				//since this is a swap action,				//then if this is positive movement and linear orientation				//we need to convert this target to next sibling box,				(this.orientation != 'freeform' && positive ? dbx.getSiblingBox(selected, 'nextSibling') : selected),				(this.orientation == 'freeform' ? 'swap' : 'move')				]			);		//if the function returned false		//return false here in case we need to pass that back up to api		if(!actions.proceed) { return false; }	}	//update the rule pointer as necessary	if(this.functionExists('_updateRulePointer')) { this._updateRulePointer(); }	//the first animation is on the box that we've selected	//the second animation is on the box we've moving (parent)	//get both before positions of the boxes	var selectedpos = { 'x' : selected.offsetLeft, 'y' : selected.offsetTop };	var originalpos = { 'x' : original.offsetLeft, 'y' : original.offsetTop };	//make them pre invisible ahead of the animator, so you don't ever see	//a brief snatch of them visible in their new positions, before movement	original.style.visibility = 'hidden';	selected.style.visibility = 'hidden';	//remove any target class name from the target box	selected = dbx.removeClass(selected, 'dbx\-box\-target');	//swap the boxes over	//--- Thanks to jkd for this swapNode re-creation ---//	var next = selected.nextSibling;	if(next == original)	{		selected.parentNode.insertBefore(original, selected);	}	else	{		original.parentNode.insertBefore(selected, original);		next.parentNode.insertBefore(original, next);	}	//create new box animators for the two boxes	//do the original box second, because we want that to be on top	//and pass false as the manual flag for the first, so that it doesn't capture focus	new dbxAnimator(this, selected, selectedpos, this.resolution, true, null, false);	new dbxAnimator(this, original, originalpos, this.resolution, true, anchor, manual);	//regenerate the box order array and save to cookie	this.regenerateBoxOrder();	//return true for success in case we need	//a value to pass back up to the api	return true;};//create a tooltip for keyboard navigation instructionsdbxGroup.prototype.createTooltip = function(text, anchor, okay, cname){	//if the action is okay	if(okay)	{		//create the tooltip inside the group container		//it's here so that it comes out above all the boxes		this.tooltip = this.container.appendChild(dbx.createElement('span'));		this.tooltip.style.visibility = 'hidden';		this.tooltip.className = 'dbx-tooltip';		//create and append the tooltip text		this.tooltip.appendChild(document.createTextNode(text));		//iteratively find a reference to the anchor's parent box		var parent = dbx.getTarget(null, 'dbx\-box', anchor);		//set tooltip position to box origin		//so developers can move it from there, eg, with margin		//need to int the value, in case it comes out as a float (which it might)		this.tooltip.style.left = parseInt(parent.offsetLeft, 10) + 'px';		this.tooltip.style.top = parseInt(parent.offsetTop, 10) + 'px';		//get the true position of the tooltip with respect to the page		var position = dbx.getPosition(this.tooltip);		//get the widths of the viewport and tooltip		var viewsize = dbx.getViewportWidth();		var tipsize = this.tooltip.offsetWidth;		//if the left position plus tooltip width is greater than the viewport width		//reposition the tooltip by the necessary amount		if(position.left + tipsize > viewsize)		{			this.tooltip.style.left = parseInt(parent.offsetLeft - (position.left + tipsize - viewsize), 10) + 'px';		}		//show the tooltip on a timer so it's not in your face		//we could do this by conditionalising the whole process		//and only creating tooltips after event-discriminated timeouts		//but this is a great deal simpler, and nobody will notice the difference :)		var tooltip = this.tooltip;		window.setTimeout(function()		{			//check it's still here, in case it's been removed in the meantime			if(tooltip != null) { tooltip.style.visibility = 'visible'; }		}, 400);	}};//remove such a tooltip, if it's theredbxGroup.prototype.removeTooltip = function(){	//if there's a tooltip	if(this.tooltip)	{		//remove it and nullify the reference		this.tooltip.parentNode.removeChild(this.tooltip);		this.tooltip = null;	}};//hover method sets and clears box hover class from a single mousemove handle//which is rather slick, though I do say so myself :DdbxGroup.prototype.hover = function(e){	//if the keydown flag is not set (so this is not triggered by spatial navigation in opera)	//or this is konqueror and the mousedown flag is set	if(!this.keydown || (dbx.kde && !this.mouseisdown))	{		//implement the box hover class whenever the mouse moves over a handle		//this matches the behavior of the focus class by only appearing		//when the mouse is over a target that can move the box		var found = false, target = typeof e.target != 'undefined' ? e.target : e.srcElement;		for(var i=0; i<this.handles.length; i++)		{			if(this.contains(this.handles[i], target))			{				found = true;				var parentbox = dbx.getTarget(null, 'dbx\-box', this.handles[i]);				if(!dbx.hasClass(parentbox, 'dbx\-box\-hover'))				{					if(typeof this.hoverbox != 'undefined')					{						this.hoverbox = dbx.removeClass(this.hoverbox, 'dbx\-box\-hover');					}					this.hoverbox = parentbox;					parentbox.className += ' dbx-box-hover';				}				break;			}		}		if(!found)		{			if(typeof this.hoverbox != 'undefined')			{				this.hoverbox = dbx.removeClass(this.hoverbox, 'dbx\-box\-hover');				delete this.hoverbox;			}		}	}};//refresh method re-initializes a group after load time//this is primarily for the API,//but it's also vital to moving boxes between groups//(which is why it's in this script, not dbx.remotes)dbxGroup.prototype.refresh = function(recover){	//don't continue if the script is unsupported	if(!dbx.supported) { return; }	//recover flag is whether to recover a state from cookie	//if this is being used internally or mid-view then there's no point	//it would be wasteful, and internally would conflict	//with the code that recovers dynamic groups onload	//however it might be needed as a user argument to recover	//the state of a group that was built dynamically external to the program	//(like in the addremove demo)	//if the argument is undefined, default to false	if(typeof recover == 'undefined') { recover = false; }	//get all elements within this container	this.eles = dbx.get('*', this.container);	//look for and remove any existing dummy elements	//because it will be added again when we re-initialize with initBoxes	for(var i=0; i<this.eles.length; i++)	{		if(dbx.hasClass(this.eles[i], 'dbx\-dummy'))		{			this.container.removeChild(this.eles[i]);		}	}	//now re-initialise the boxes in this group as per recover flag	//and with true for the flag that says to look for sparetokens	this.initBoxes(recover, true);	//then regenerate the box order array (from which, save back to cookie)	this.regenerateBoxOrder();};//check whether an optional function existsdbxGroup.prototype.functionExists = function(cname){	return typeof this[cname] == 'function';};//docking box mousedown handlerdbxGroup.prototype.mousedown = function(e, box, handle, override){	//note: the handle and override arguments are always undefined	//unless this was called from the manager-level mousemove handler	//ie, it's a triggering even after having moved a box between groups	//and we need to contrive some of the values to make it all go smoothly	//store the target node, converting event argument as we go	//or use the handle override value if that's defined	var node = typeof handle != 'undefined' ? handle : typeof e.target != 'undefined' ? e.target : e.srcElement;	//if it's a text node, convert refence to its parent	//this is for safari, in which events can come from text nodes	if(node.nodeName == '#text') { node = node.parentNode; }	//if the target is not a toggle, box or group	if(!dbx.hasClass(node, 'dbx\-(toggle|box|group)'))	{		//while target doesn't contain docking box handle classname		//set reference upwards until we find it		//this is so that the handle can contain inner elements		//but stop if we get to a box or group		//to filter out any remaining events that started from higher than the handle		while(!dbx.hasClass(node, 'dbx\-(handle|box|group)'))		{			node = node.parentNode;		}	}		//if target is a handle or a toggle, 	if(dbx.hasClass(node, 'dbx\-(toggle|handle)'))	{		box.className += ' dbx-box-active';	}	//if the box is not ungrabbale and the target is a handle	if(!dbx.hasClass(box, 'dbx\-nograb') && dbx.hasClass(node, 'dbx\-handle'))	{		//clear any residual dialog		this.clearDialog();		//remove any focus class name from parent box		box = dbx.removeClass(box, 'dbx\-box\-focus');		//remove any tooltip that's there		this.removeTooltip();		//set the "released" flag, initially to false		//which is used to detect whether a box has already moved once		//or this is the first time it's been released		//we'll need this as part of sticky box / drag threshold evaluations		this.released = false;		//store initial mouse coords		this.initial = { 'x' : e.clientX, 'y' : e.clientY };		//if override values are defined		if(typeof override != 'undefined')		{			//adjust the initial mouse coords by an inverse of the override values			//so that the clone and sticky position is updated accordingly			this.initial.x += (0 - override.x);			this.initial.y += (0 - override.y);		}		//reset the current mouse coords object		this.current = { 'x' : 0, 'y' : 0 };		//create a moveable clone of this box, also passing the source value		this.createCloneBox(box, 'mouse');		//prevent default action to try to stop text range selection while dragging		if(typeof e.preventDefault != 'undefined' ) { e.preventDefault(); }		//prevent textrange selection in IE		//by temporarily suppressing it on the whole document		if(typeof document.onselectstart != 'undefined')		{			document.onselectstart = function() { return false; }		}	}};//group-level mousemove handlerdbxGroup.prototype.mousemove = function(e){	//if dragging is not okay and we have a residual keyboard-generated dialog, remove it	//this cleans up after a situation where a dialog can be created	//if you just shift-click on a header then don't move it	//but because it checks the source flag, it ensures that a 	//key-generated dialog can't be removed by mouse movement	if(!this.dragok && (this.dialog && this.dialog.source != 'keyboard'))	{		//clear the dialog		this.clearDialog();		//remove any tooltip that's there		this.removeTooltip();		//remove any target or active class names from the boxes in this group		this.removeActiveClasses('dbx\-box\-(target|active)');	}	//if dragging is okay and we have a box reference	if(this.dragok && this.box)	{		//get the current direction of movement		this.direction = e.clientY == this.current.y			? (e.clientX > this.current.x ? 'right' : 'left')			: (e.clientY > this.current.y ? 'down' : 'up');		//store the current mouse coords		this.current = { 'x' : e.clientX, 'y' : e.clientY };		//store the total difference from the initial coordinates		var overall = { 'x' : this.current.x - this.initial.x, 'y' : this.current.y - this.initial.y };		//if the differences are both less than or equal to the drag threshold		//even out to zero, which creates a "stickiness" around the origin		if		(			((overall.x >= 0 && overall.x <= this.threshold) || (overall.x <= 0 && overall.x >= 0 - this.threshold))			&&			((overall.y >= 0 && overall.y <= this.threshold) || (overall.y <= 0 && overall.y >= 0 - this.threshold))		)		{			this.current.x -= overall.x;			this.current.y -= overall.y;		}				//if this box has already been released, or one of the differences has changed past the drag threshold		//(having a drag threshold is so that handles can also be links or other actuators without conflict		// because no-one holds the mouse perfectly still when they click a link)		if(this.released || overall.x > this.threshold || overall.x < (0 - this.threshold) || overall.y > this.threshold || overall.y < (0 - this.threshold))		{			//store values to dbx properties for external methods			dbx.dbxobject = this;			dbx.group = this.container;			dbx.sourcebox = this.box;			dbx.clonebox = this.boxclone;			dbx.event = e;			//if onboxdrag doesn't exist or returns true			if(typeof dbx.onboxdrag == 'undefined' || dbx.onboxdrag())			{				//set the released flag, to say this can always happen from now on				//otherwise, after moving the box away once,				//the subsequent tests would make it sticky				//to the threshold points instead of the origin				this.released = true;				//move the clone to mouse coords minus mouse/position difference				//if we're restricting the axis of movement in a linear direction,				//only change the applicable position value				//need to int the value, in case it comes out as a float (which it might)				if(this.restrict != 'vertical' || this.orientation == 'horizontal')				{					this.boxclone.style.left = parseInt(this.current.x - this.difference.x, 10) + 'px';				}				if(this.restrict != 'horizontal' || this.orientation == 'vertical')				{					this.boxclone.style.top = parseInt(this.current.y - this.difference.y, 10) + 'px';				}				//if axis restriction is freeform we want to				//reset any actions that take the box centerpoint outside the container				if(this.restrict == 'freeform')				{					//get the center point of the clone relative to the container					var clonepoint = {						'x' : this.boxclone.offsetLeft + (this.boxclone.offsetWidth / 2),						'y' : this.boxclone.offsetTop + (this.boxclone.offsetHeight / 2)						};					//define a proportion of the box dimensions to allow for					//the same sensitvity threshold as the swapping action					//that defines when a box is "well inside".					//we need this discrimination to avoid a strip of insensitivity					//that could amount to a way of cheating in gaming applications					//and in fact we're going to use a higher proportion here: 0.2 instead of 0.1					//to overlap the regions and remove all possibility of manipulating it this way					var proportion = 0.2;					var hypotonuse = Math.round(Math.sqrt(Math.pow(this.boxclone.offsetWidth, 2) + Math.pow(this.boxclone.offsetHeight, 2)));					//if the point goes outside the container					if(clonepoint.x < 0 || clonepoint.x > (this.container.offsetWidth - proportion * hypotonuse)						|| clonepoint.y < 0 || clonepoint.y > (this.container.offsetHeight - proportion * hypotonuse))					{						//pass event to mouseup handler						this.mouseup(e);						//then return true to complete this action						//we need to do this to avoid any errors due to bad event sychronisation						//(eg, further mousemove occuring during the mouseup process						// at a time when the box reference no longer exists)						return true;					}				}				//move the original box to new position, as per confirm mode				this.moveBoxByMouse(this.current.x, this.current.y, this.confirm);				//prevent default action to try to stop text range selection while dragging				if(typeof e.preventDefault != 'undefined' ) { e.preventDefault(); }			}		}	}	return true;};//document mouseup handlerdbxGroup.prototype.mouseup = function(e){	//remove any target or active class names from the boxes in this group	this.removeActiveClasses('dbx\-box\-(target|active)');	//if we have a box reference	if(this.box)	{		//if we have an dialog element we need to move the original box to position		//and explicitly with false for the confirm parameter, so it physically happens		if(this.dialog)		{			//if this dialog has a group reference property			//and this dialog was not cloned from a box in this group			//then we're dealing with confirm-based cross group transfer			//ie, our original box needs to insert before/swap with the target referred to by the clone			if(typeof this.dialog.group != 'undefined' && typeof this.boxes[dbx.getID(this.dialog)] == 'undefined')			{				//store the group and ibox references to pass the dbx.mousemove				//that will tell it what objects to use for transfer				var xgroup = this.dialog.group;				var xinsert = xgroup.boxes[dbx.getID(this.dialog)];			}			//remove the dialog element and nullify the reference			this.clearDialog();			//if we had a group reference (now stored as xgroup)			if(typeof xgroup != 'undefined')			{				//pass event and this to the manager level mousemove handler				//along with explicit group and insert reference				dbx.mousemove(e, this, xgroup, xinsert);				//don't go any further here				//because the mousemove will handle the rest				return;			}			//then move the box, with confirm mode false so a movement definitely happens			this.moveBoxByMouse(e.clientX, e.clientY, false);		}		//remove the clone box		this.removeCloneBox();		//regenerate the box order array and save to cookie		this.regenerateBoxOrder();		//release textrange selection in IE		if(typeof document.onselectstart != 'undefined')		{			document.onselectstart = function() { return true; }		}	}	//clear any residual dialog	this.clearDialog();	//reset drag ok flag	this.dragok = false;};//toggle click handlersdbxGroup.prototype.click = function(e, anchor){	//if the has focus flag is strictly true or null	if(anchor.hasfocus === true || anchor.hasfocus === null)	{		//if we have an dialog element we need to move the box to position (if confirmed)		if(this.dialog)		{			//get a reference to the original box, derived from anchor			var box = dbx.getTarget(null, 'dbx\-box', anchor);			//get the target box id from the dialog			var dbxid = dbx.getID(this.dialog);			//otherwise store the selected box reference as the box that			//the dialog relates to, from its own group as normal			var targetbox = this.boxes[dbxid];			//if the exchange mode is insert and confirm mode is true and this is positive movement			//we want its next sibling [effectively, next sibling box or dummy]			//because we're going to want to insert after, not before,			//which of course translates to insert before the next one			//if we don't do this then moving to the right becomes impossible			//and moving down is counter-intuitive			//however if we run out of next siblings without finding a box			//we'll just have to maintain the previous target			if(this.exchange == 'insert' && this.confirm == true && this.positive == true)			{				targetbox = dbx.get('nextSibling', targetbox);				if(!targetbox) { targetbox = this.boxes[dbxid]; }			}						//the action was confirmed if the dialog has the yes classname			var confirmed = dbx.hasClass(this.dialog, 'dbx\-dialog\-yes');			//remove the dialog element and nullify the reference			this.clearDialog();			//remove any tooltip that's there			this.removeTooltip();			//if a target box is defined, and not the original box, and the action is confirmed			if(typeof targetbox != 'undefined' && targetbox != box && confirmed == true)			{				//if the exchange mode is swap, physically swap the two boxes passing the original box,				//target box, and original box anchor				//and the argument that says anchor is a manual interaction				//and the argument for whether this is positive or negative				//which can be false because only linear orientation requires it				//(to use for evaluating onbeforestatechange properties)				if(this.exchange == 'swap')				{					this.swapTwoBoxes(box, targetbox, anchor, true, false);				}					//otherwise insert the original box before or after the selected box 				//and return the return value of that back up to the api				else				{					return this.insertTwoBoxes(box, targetbox, anchor, true, false);				}			}					//return so we don't activate the toggle			return false;		}		//remove any tooltip that's there		this.removeTooltip();		//toggle box state and save		//with the anchor object, and argument that says this is a manual interaction		//and a null final argument that means toggle, don't force a state		this.toggleBoxState(anchor, true, true, null);	}	return false;};//toggle or handle keypress handlersdbxGroup.prototype.keypress = function(e, anchor){	//get a reference to the anchor's parent box	var parentbox = dbx.getTarget(null, 'dbx\-box', anchor);		//if the keyCode is one of the arrow keys	if(/^(3[7-9])|(40)$/.test(e.keyCode.toString()))	{		//if this is opera and the shift key is pressed		//return true to allow natural mouse behaviors, not box movement		//so that we don't conflict with spatial navigation		if(dbx.opera && e.shiftKey) { return true; }		//if the parent box isn't an ungrabbable object		if(!dbx.hasClass(dbx.getTarget(null, 'dbx\-box', anchor), 'dbx\-nograb'))		{			//add the active class to the parent box			parentbox.className += ' dbx-box-active';						//remove any tooltip that's there			this.removeTooltip();				//store compass direction from keycode			var direction = '';			switch(e.keyCode)			{				case 37 :					direction = 'W';					break;				case 38 :					direction = 'N';					break;				case 39 :					direction = 'E';					break;				case 40 :					direction = 'S';					break;			}				//wait a short interval before proceeding			//so we have time to get two key direction presses			//that indicate diagonal movement			//it can't be too short, however, or it will be			//drowned out by the keydown repeat rate (hence, too difficult to activate)			var wait = 75;				//if a current key direction is already defined			//and not the same as the current direction			if(this.currentdir && this.currentdir != direction)			{				//add it to the direction value in lower case				direction += this.currentdir.toLowerCase();					//convert inverted values				switch(direction)				{					case 'En' : direction = 'Ne'; break;					case 'Es' : direction = 'Se'; break;					case 'Wn' : direction = 'Nw'; break;					case 'Ws' : direction = 'Sw'; break;				}					//clear the timer				clearTimeout(this.keytimer);					//and set the movement to happen immediately				wait = 0;			}				//otherwise define it as the current direction			else			{				this.currentdir = direction;			}					//store a reference to this			var self = this;				//start the timer			this.keytimer = setTimeout(function()			{				//ignore conflicting direction values, like "Ns"				//which can happen if you've pressed several keys at once				//or are generally sloppy with keypress accuracy (ie, normal!)				if(!/^(Ns|Sn|Ew|Ww)$/.test(direction))				{					//if we already have a dialog					if(self.dialog)					{						//get the box id from the clone						var dbxid = dbx.getID(self.dialog);							//set the box reference to the box that						//the dialog relates to, from its own group as normal						//this is what allows the dialog to move further than 1 block away before swapping						var box = self.boxes[dbxid];					}						//otherwise get a reference to the box this anchor is inside					else					{						box = dbx.getTarget(null, 'dbx\-box', anchor);					}										//then pass event, anchor, box, direction and confirm mode to shift box position method					//along with the flag that says this move hasn't been tested against rules yet					//we have to defer the testing in this situation					//because we don't know the box target until we've tried to move					//but we can't move until we've tested the target...					//pas the argument that says this is a manual interaction					self.moveBoxByKeyboard(e, anchor, box, direction, self.confirm, true);				}				}, wait);					//prevent default action if that's supported			//otherwise return false (resulting in the same effect in ie)			if(typeof e.preventDefault != 'undefined') { e.preventDefault(); }			else { return false; }				//stop event bubbling, because in safari events can come from text nodes			//and without this bubble control each keyup would call the function twice			//but since we're doing this, we should do it for everyone for the sake of consistency			typeof e.stopPropagation != 'undefined' ? e.stopPropagation() : e.cancelBubble = true;				//and since we're doing that, we also need to clear the keydown flag manually			//because the event won't reach the document keyup handler which normally does that			this.keydown = false;		}	}		//or if this is konqueror, and an actuation key (enter or space)	//and the target is a button/anchor element	//pass the anchor to click handler and prevent default action	//we're doing this because the enter key isn't firing button.onclick in konqueror	else if(dbx.kde && e.target == anchor && (e.keyCode == 13 || e.keyCode == 32))	{		this.click(e, anchor);		e.preventDefault();	}		//in any other case remove any target or active class names from the boxes in this group	//this should catch tabbing or otherwise navigating away from an anchor or box	//we can't use blur events for this reset because they happen programatically as well	else	{		this.removeActiveClasses('dbx\-box\-(target|active)');	}		//then if this is any browser and an actuation key	//add the active class back to this box	if(e.keyCode == 13 || e.keyCode == 32)	{		parentbox.className += ' dbx-box-active';	}		return true;};//regenerate box order array, save to cookie and output to receiver methoddbxGroup.prototype.regenerateBoxOrder = function(){	//rebuild the order array	this.order = [];	//re-iterate through the elements in this column	var len = this.eles.length;	for(var j=0; j<len; j++)	{		//if it's a docking box, and not a clone or a dummy		if(dbx.hasClass(this.eles[j], 'dbx\-box') && !dbx.hasClass(this.eles[j], 'dbx\-(clone|dummy)'))		{			//add its index (extracted from dbxid classname)			//plus its open state (extracted from dbx-box-(open|closed) classname )			this.order.push(dbx.getID(this.eles[j]) + (dbx.hasClass(this.eles[j], 'dbx\-box\-open') ? '+' : '-'));		}	}		//save the order to this member of the dbx manager's save data object	dbx.savedata[this.gid] = this.order.join(',');	//save some references to dbx properties	//for the onstatechange receiver method	dbx.dbxobject = this;	dbx.group = this.container;	dbx.gid = this.gid;	//update child classes	this.updateChildClasses();	//set a cookie and output to receiver method	dbx.setCookieState();};//apply first-child and last-child class to the correct elementsdbxGroup.prototype.updateChildClasses = function(){	//create an array of box ids in their current DOM order	var boxids = [], eles = dbx.get('*', this.container);	for(var i=0; i<eles.length; i++)	{		if(dbx.hasClass(eles[i], 'dbx\-box') && !dbx.hasClass(eles[i], 'dbx\-(dummy|clone)'))		{			boxids.push(dbx.getID(eles[i]));		}	}	//the children can then be referenced from that array	var children = {		'first' : boxids[0], 		'last' : boxids[boxids.length - 1]		};			//iterate through the children	for(var i in children)	{		if(dbx.unwanted(children, i)) { continue; }				//get a box reference from the dbxid		var box = this.boxes[children[i]];				//remove any existing child class from wherever it is now		if(this.child[i] != null)		{			//remove the child class			this.child[i] = dbx.removeClass(this.child[i], i + '\-child');			//if there's a drag clone and it matches this box, remove its child class now			if(this.boxclone && dbx.getID(this.boxclone) == dbx.getID(this.child[i]))			{				this.boxclone = dbx.removeClass(this.boxclone, i + '\-child');			}			//nullify the reference			this.child[i] = null;		}				//apply the class to this box and store it		box.className += ' ' + i + '-child';		this.child[i] = box;				//if there's a drag clone and it now amtches this box, add the child class		if(this.boxclone && dbx.getID(this.boxclone) == dbx.getID(this.child[i]))		{			this.boxclone.className += ' ' + i + '-child';		}	}};//create a clonedbxGroup.prototype.createClone = function(box, zorder, position, cname, children, source){	//create a clone and append it to the group container	//(a dialog clone has false for the create with children argument)	//it has to be appended to group container, not body	//so that it inherits CSS just the same as the original box	var clone = this.container.appendChild(box.cloneNode(children));		//receord the source property for later reference	//this will be used to prevent mouse movement	//from unduly clearing keyboard generated dialogs	clone.source = source;	//add clone classname	clone.className += ' dbx-clone';	//is a class name is specified, add that as well	if(cname != '')	{		clone.className += ' ' + cname;	}	//remove any focus class name	//it's not needed, and would look ugly	clone = dbx.removeClass(clone, 'dbx\-box\-focus');	//re-inforce important styles	clone.style.position = 'absolute';	clone.style.visibility = 'hidden';	//set z-index	clone.style.zIndex = zorder;	//move clone to superimpose original	clone.style.left = parseInt(position.x, 10) + 'px';	clone.style.top = parseInt(position.y, 10) + 'px';	//set width and height same as original	clone.style.width = box.offsetWidth + 'px';	clone.style.height = box.offsetHeight + 'px';	return clone;};//create a moveable clone of the original boxdbxGroup.prototype.createCloneBox = function(box, source){	//original box object	this.box = box;	//get original box position	this.position = { 'x' : this.box.offsetLeft, 'y' : this.box.offsetTop };	//calculate mouse/position difference	this.difference = { 'x' : (this.initial.x - this.position.x), 'y' : (this.initial.y - this.position.y) };	//create a clone of the original box, including the dragclone class name	//and passing the source property; set the index at the top of the stack	this.boxclone = this.createClone(this.box, 30000, this.position, 'dbx-dragclone', true, source);	//set move cursor	this.boxclone.style.cursor = 'move';	//dont hide the original / show the clone just yet	//wait until it's confirmed to be moving	//so that links will still work before the drag threshold	//set drag ok flag	this.dragok = true;};//remove a clone boxdbxGroup.prototype.removeCloneBox = function(){	//remove the clone	this.container.removeChild(this.boxclone);	//show the original	this.box.style.visibility = 'visible';	//nullify the reference	this.box = null;};//remove all target and active class names from the boxes in a groupdbxGroup.prototype.removeActiveClasses = function(pattern){	for(var i in this.boxes)	{		if(dbx.unwanted(this.boxes, i)) { continue; }		this.boxes[i] = dbx.removeClass(this.boxes[i], pattern);	}};//move a box (by conditions) with the mousedbxGroup.prototype.moveBoxByMouse = function(clientX, clientY, confirm){	//if we're using freeform orientation	if(this.orientation == 'freeform')	{		//get the center point of the clone		var clonepoint = {			'x' : clientX - this.difference.x + (this.boxclone.offsetWidth / 2),			'y' : clientY - this.difference.y + (this.boxclone.offsetHeight / 2)			};		//create an array of differences with each comparison box		var differences = [];	}	//else we're using linear orientation	else	{		//get position and dimensions of the clone		//xy is y for a vertical column and x for a horizontal row		//wh is h for a vertical column and w for a horizontal row		var cloneprops = {			'xy' : this.orientation == 'vertical' ? clientY - this.difference.y : clientX - this.difference.x,			'wh' : this.orientation == 'vertical' ? this.boxclone.offsetHeight : this.boxclone.offsetWidth			};	}	//if hide source while dragging is enabled, hide the original	if(dbx.hide) { this.box.style.visibility = 'hidden'; }	//remove any target or active class names from the boxes in this group	this.removeActiveClasses('dbx\-box\-(target|active)');	//show the clone	this.boxclone.style.visibility = 'visible';	//for each box in the array	for(var i in this.boxes)	{		//don't include members we don't care about		if(dbx.unwanted(this.boxes, i)) { continue; }		//if the orientation is freeform		if(this.orientation == 'freeform')		{			//if the exchange mode is "swap", and the box is the dummy, then continue to			//the next iteration, because we don't ever want to swap with that			if(dbx.hasClass(this.boxes[i], 'dbx\-dummy') && this.exhange == 'swap') { continue; }			//get the center point of the box			var boxpoint = {				'x' : this.boxes[i].offsetLeft + (this.boxes[i].offsetWidth / 2),				'y' : this.boxes[i].offsetTop + (this.boxes[i].offsetHeight / 2)				};			//now we need to find the box with which to swap our original			//to preserve the best intuitive sense of physical co-incidence			//we're going to find that box by comparing the difference betweeen			//the center point of our clone, and that of each box			//the closest match (shortest hypotonuse) will be the one we want			//work out the difference for this box			//and add it to the array of differences including the i index			//because we'll need that at the other end when it's sorted			differences.push([i, Math.round(Math.sqrt(Math.pow(Math.abs(clonepoint.x - boxpoint.x), 2) + Math.pow(Math.abs(clonepoint.y - boxpoint.y), 2)))]);			//if the boxes we're comparing are the same,			if(this.boxes[i] == this.box)			{				//set the difference to a ridiculously high value,				//so it gets sorted to the end and hence never selected				differences[differences.length - 1][1] = Math.pow(10, 20);				//if confirm is enabled or there's an dialog element				if(confirm || this.dialog)				{					//pass the box to update dialog method					//with no additional state class token, and no position override or group ref					//and the mouse source flag					this.updateDialog(this.box, '', null, null, 'mouse');				}			}		}		//else if the orientation is linear (vertical or horizontal)		//we're re-arranging them by inserting new before old		else		{			//get position and dimensions of the original box			var boxprops = {				'xy' : this.orientation == 'vertical' ? this.boxes[i].offsetTop : this.boxes[i].offsetLeft,				'wh' : this.orientation == 'vertical' ? this.boxes[i].offsetHeight : this.boxes[i].offsetWidth				};			//the direction of movement is positive if it's moving down or right			this.positive = this.direction == 'down' || this.direction == 'right';			//if - the direction of movement is positive; and			//	clone left/top plus clone width/height is greater than box left/top; and			//	clone left/top is less than box left/top			//or - the direction of movement is negative; and			//	clone left/top is less than box left/top; and			//	clone left/top plus clone width/height is greater than box left/top			if			(				(this.positive && cloneprops.xy + cloneprops.wh > boxprops.xy && cloneprops.xy < boxprops.xy)				||				(!this.positive && cloneprops.xy < boxprops.xy && cloneprops.xy + cloneprops.wh > boxprops.xy)			)			{				//we've found the box before which to insert our original				//but if the boxes we're comparing are the same don't continue				//this is to prevent redundent movement				if(this.boxes[i] == this.box) { return; }				//look for a next sibling of this box				//and don't continue if there isn't one,				//or it's the same as the box we're inserting before				//so that we're not doing an action that would result in no change				//this filtering improves efficiency generally,				//and is necessary specifically to stabilize the animation				//otherwise the multiple unecessary calls would overload the animation timers				//and the result would be snap movement with no apparent transition				var sibling = dbx.getSiblingBox(this.box, 'nextSibling');				if(this.box == sibling || this.boxes[i] == sibling) { return; }				//store the value of i as the box index we want, and stop				var index = i;				break;			}		}	}	//if the orientation is freeform	if(this.orientation == 'freeform')	{		//sort the differences array numerically by its second member (difference)		differences.sort(function(a, b) { return a[1] - b[1]; });		//so the box index we want is now differences[0][0]		index = differences[0][0];		//the box we want then is boxes[index]		var targetbox = this.boxes[index];			//but if the exchange mode is insert and this is positive movement		//we want its next sibling [effectively, next sibling box or dummy]		//because we're going to want to insert after, not before,		//which of course translates to insert before the next one		//if we don't do this then moving to the right becomes impossible		//and moving down is counter-intuitive		//but in either case we have to save the original targetbox reference as well		//because when we do hypotonuse comparision to see if we've moved enough for insert to happen		//we need that to be on the nearer box, treating it as an insert-after reference		var originaltargetbox = targetbox;		if(this.exchange == 'insert' && (this.direction == 'down' || this.direction == 'right'))		{			targetbox = dbx.get('nextSibling', targetbox);		}		//if the boxes we're comparing are the same don't continue		//this is to prevent redundent movement, and also		//errors when the group contains only one visible box		//to prevent it trying to swap with itself		if(targetbox == this.box) { return; }		//get 2d position and extent of the original target box		//expressed as the position of each edge		boxprops = {			'left' : originaltargetbox.offsetLeft,			'top' : originaltargetbox.offsetTop			};		boxprops.right = boxprops.left + targetbox.offsetWidth;		boxprops.bottom = boxprops.top + targetbox.offsetHeight;		//define a proportion where the center of the clone is "well inside" the original box		//that proportion being a fraction of the box hypotonuse away from the center point		//bringing this value closer to 1 will reduce the sensitivity of the swap detection		//so a value of 1 would mean that no swaps ever occur		//while a value of 0 would mean that there's no threshold limit and all registered swaps occur		//doing this avoids excessive swapping and keeps the behaviors more intuitive		//and makes it easier to do diagonal swapping rather than multiple horizontal and vertical shifts		//we're going to have 0.1 for regular movement, or 0 for dialog tracking movement		//(where it's counter-productive because it makes the caret reset too much, and it's harder to lock onto targets)		var proportion = confirm || this.dialog ? 0 : 0.1;		var hypotonuse = Math.round(Math.sqrt(Math.pow(originaltargetbox.offsetWidth, 2) + Math.pow(originaltargetbox.offsetHeight, 2)));		//now check that the center point of the clone is well inside the original box (by that proportion)		if(!(clonepoint.x > boxprops.left + (hypotonuse * proportion) && clonepoint.x < boxprops.right - (hypotonuse * proportion)			&& clonepoint.y > boxprops.top + (hypotonuse * proportion) && clonepoint.y < boxprops.bottom - (hypotonuse * proportion)))		{			return;		}		//don't continue if this is the same as the lastmoved object and in the same direction		//this is for the situation where you're moving a small box over a large one which completely encloses it		//and so it could register the move, do it, then the center point could still be inside		//and so it would register immediately again and re-perform the same animation		//and potentially this could happens several times on the trot		//so, by differeentiating like this, it means that we get nice discreet movement		//that only happens multiple times if you initiate it by a change in drag direction		if(this.last.box == targetbox && this.last.direction == this.direction)		{			return;		}		//now we need to compute a compass direction for this swap		//that we can pass it to the rules engine to evaluate whether it's allowed		//and also a total distance for the two objects in this swap		//so we can use that to evaluate how many blocks away		//so first get the center point of the original box (not the clone)		var origpoint = {			'x' : this.box.offsetLeft + (this.box.offsetWidth / 2),			'y' : this.box.offsetTop + (this.box.offsetHeight / 2)			};		//then get the center point of the selected box		boxpoint = {			'x' : originaltargetbox.offsetLeft + (originaltargetbox.offsetWidth / 2),			'y' : originaltargetbox.offsetTop + (originaltargetbox.offsetHeight / 2)			};		//get the blocks difference		var blocks = this.getBlocksDifference(origpoint, boxpoint, this.box);		//get the compass direction		var compass = this.getCompassDirection(origpoint, boxpoint);				//evaluate the rules for this direction, blocks, box and no rulekey override		//and if we don't get permission		if(this.functionExists('_testRules') && !this._testRules(compass, blocks, this.box, null))		{			//if confirm mode is enabled or we already have a dialog			if(confirm || this.dialog)			{				//pass the box to update dialog method				//passing the additional state class, and no position override or group ref				//and the mouse source flag				this.updateDialog(originaltargetbox, ' dbx-dialog-no', null, null, 'mouse');			}			//don't go any further			return;		}		//otherwise we've got permission		//if confirm mode is enabled or we already have a tracking dialog element		if(confirm || this.dialog)		{			//if this box is not a dummy or a dialog,			//apply the target class name to the box			if(!dbx.hasClass(targetbox, 'dbx\-(dialog|dummy)'))			{				originaltargetbox.className += ' dbx-box-target';			}			//pass the box to update dialog method			//passing the additional state class, and no position override or group ref			//and the mouse source flag			this.updateDialog(originaltargetbox, ' dbx-dialog-yes', null, null, 'mouse');			//return so that we don't perform the movement			return;		}		//if we get this far we're definitely going to perform a movement		//so update the rule pointer as necessary		if(this.functionExists('_updateRulePointer')) { this._updateRulePointer(); }		//store this original box as the lastmoved object and direction		this.last = {			'box' : originaltargetbox,			'direction' : this.direction			};	}	//else if this is linear orientation		//save targetbox and originaltargetbox references	else	{		var targetbox = this.boxes[index];		var originaltargetbox = targetbox;	}	//if we don't have an index defined, don't continue	if(typeof index == 'undefined') { return; }	//if the original target is not a dummy or a dialog	//apply the target class name to the box	if(!dbx.hasClass(originaltargetbox, 'dbx\-(dialog|dummy)'))	{		originaltargetbox.className += ' dbx-box-target';	}	//if a beforestatechange function is defined	if(typeof dbx.onbeforestatechange != 'undefined')	{		//compile the data for, and dispatch, any onbeforestatechange function that exists		//then store the resulting action flag to an actions object		var actions = dbx.compileAndDispatchOnBeforeStateChange(			['proceed', this, this.container, this.gid, this.box, originaltargetbox,				(this.orientation == 'freeform' ? this.exchange : 'move')				]			);		//if the function returned false, don't continue		if(!actions.proceed) { return; }	}	//if we're using freeform orientation	if(this.orientation == 'freeform' )	{		//if the exchange mode is swap, the animation is on 		//the visible object that will get shifted as a result of the shift		//because that's the only one that visibly appears to have moved		if(this.exchange == 'swap')		{			var visibox = originaltargetbox;		}				//otherwise it's on every box from the insertion point		//to the end of the group (not including the box we're moving),		//so we're going to have to create that collection as a numerically indexed array		else		{			//then iterate through them [in order] to create to create the array			var add = false, pointer = 0, theboxes = [], visiboxes = [];			for(var i in this.boxes)			{				if(dbx.unwanted(this.boxes, i)) { continue; }								theboxes.push(this.boxes[i]);			}			for(i=0; i<theboxes.length; i++)			{				if(theboxes[i] == this.box) { continue; }				visiboxes.push(theboxes[i]); 			}		}	}	//else if we're using linear orientation	else	{		//if the direction of movement is positive (ie, the original box is before the box we inserted before)		//then the visibly-moved box is the previous sibling of the box we're inserting it before		//but getSiblingBox has a catch test in case there isn't a previous sibling		//which can happen if we're inserting it before the first box,		//but the mouse movement is in a positive direction		if(this.positive)		{			var visibox = dbx.getSiblingBox(targetbox, 'previousSibling');		}		//otherwise the visibly-moved box IS the box we inserted it before		else		{			visibox = targetbox;		}	}	//get the before positions of the visibly-moved box[es]	if(typeof visiboxes != 'undefined')	{		var visiposes = [];		for(i=0; i<visiboxes.length; i++)		{			visiposes.push({				'x' : visiboxes[i].offsetLeft,				'y' : visiboxes[i].offsetTop				});		}	}	else	{		var visipos = { 'x' : visibox.offsetLeft, 'y' : visibox.offsetTop };	}	//remove any target class name from the original target box	originaltargetbox = dbx.removeClass(originaltargetbox, 'dbx\-box\-target');	//get the pre-position of the original box	var prepos = { 'x' : this.box.offsetLeft, 'y' : this.box.offsetTop };	//if we're using freeform orientation, and	//the exchange mode is "swap", swap the two boxes over	if(this.orientation == 'freeform' && this.exchange == 'swap')	{		//--- Thanks to jkd for this swapNode re-creation ---//		var next = targetbox.nextSibling;		if(next == this.box)		{			targetbox.parentNode.insertBefore(this.box, targetbox);		}		else		{			this.box.parentNode.insertBefore(targetbox, this.box);			next.parentNode.insertBefore(this.box, next);		}	}	//otherwise [if the orientation is not freeform, 	//or the exchange mode is insrt] perform insertion	//which naturally moves all the subsequent boxes along one	else	{		//move the box we're move before the target box		this.container.insertBefore(this.box, targetbox);	}	//update child classes	this.updateChildClasses();	//update initial mouse co-ordinates values with the	//difference between these positions and the pre-positions	//so that the sticky region follows the static box	this.initial.x += (this.box.offsetLeft - prepos.x);	this.initial.y += (this.box.offsetTop - prepos.y);	//if we have a collection of boxes, animate each one	if(typeof visiboxes != 'undefined' && visiboxes.length > 0)	{		for(i=0; i<visiboxes.length; i++)		{			new dbxAnimator(this, visiboxes[i], visiposes[i], this.resolution, false, null, true);		}	}		//or if we have one box and it's not the same as the original box we moved, animate that	else if(visibox != this.box)	{		new dbxAnimator(this, visibox, visipos, this.resolution, false, null, true);	}	//if we're not in confirm mode, and not hiding the source box while dragging, 	//animate the source box (the box we grabbed and moved) as well	if(!this.confirm && !dbx.hide)	{		new dbxAnimator(this, this.box, prepos, this.resolution, false, null, true);	}};//get a compass direction from point a to point bdbxGroup.prototype.getCompassDirection = function(a, b){	//begin wit N or S, since all diagonals also begin with that	var compass = a.y < b.y ? 'S' : 'N';	//but if the y points are the same then this is E or W	if(a.y == b.y)	{		compass = a.x < b.x ? 'E' : 'W';	}	//otherwise if the original x point is less then this is Ne or Se	else if(a.x < b.x)	{		compass += 'e';	}	//or of the original x point is greater then this is Nw or Sw	else if(a.x > b.x)	{		compass += 'w';	}	//hence if they're the same, it remains N or S	//return the direction	return compass;};//get the blocks difference between two points for a given box sizedbxGroup.prototype.getBlocksDifference = function(a, b, box){	//store the block differences using absolute values	//here the big assumption is that all the blocks are the same size	//hence this won't work correctly if that's significantly not the case	var blocks = [		parseInt(Math.abs(a.x - b.x) / box.offsetWidth, 10),		parseInt(Math.abs(a.y - b.y) / box.offsetHeight, 10)		];	//sort the array numerically so the value order is predictable when we test it	blocks.sort(function(a, b) { return a - b; });	//return the values	return blocks;};//update the dialog element for confirm-based movementdbxGroup.prototype.updateDialog = function(box, state, position, group, source){	//wait for a very brief, managed (single-thread) timeout, so that	//we get a buffer to avoid visual flickering and erraticness	if(this.buffer)	{		clearTimeout(this.buffer);		this.buffer = null;	}	var self = this;	this.buffer = setTimeout(function()	{		//get the selected box position		var boxpos = { 'x' : box.offsetLeft, 'y' : box.offsetTop };		//if we have a position override		if(position)		{			//increase the positions by the override values			boxpos.x += position.x;			boxpos.y += position.y;		}		//if we already have an dialog element, remove it first and nullify the reference		//we need to do this rather than just repositiong an existing one		//so that it has the same meta-information as the box it relates to		self.clearDialog();		//create a dialog clone from the box, including the state class		//set the z-index just below the original box clone		//and set false for the clone-children argument, so that		//our dialog is just an outline of the original box		self.dialog = self.createClone(box, 29999, boxpos, 'dbx-dialog' + state, false, source);		//don't preserve transient state box classes 		self.dialog = dbx.removeClass(self.dialog, 'dbx\-box\-(target|active|hover|focus)');		//show the element		self.dialog.style.visibility = 'visible';		//clear and nullify the buffer timer		clearTimeout(self.buffer);		self.buffer = null;	}, 20);};//clear any residual dialog elementdbxGroup.prototype.clearDialog = function(){	if(this.dialog)	{		this.container.removeChild(this.dialog);		this.dialog = null;	}};//contains method evaluates whether one node contains anotherdbxGroup.prototype.contains = function(outer, inner){	if(inner == outer) { return true; }	if(inner == null) { return false; }	else { return this.contains(outer, inner.parentNode); }};//[re]focus a box anchor and associated stuffdbxGroup.prototype.refocus = function(target){	//[re]set focus on the target - usually an anchor/button, but sometimes a handle	//use a silent try catch because it may fail if user CSS or JS makes the target unfocussable	try { target.focus(); } catch(err){}		//add the focus class back to the box, if necessary	var box = dbx.getTarget(null, 'dbx\-box', target);	if(!dbx.hasClass(box, 'dbx-box-focus'))	{		box.className += ' dbx-box-focus';	}};//animation objectfunction dbxAnimator(caller, box, pos, res, kbd, anchor, manual){	//calling object	this.caller = caller;		//check the animation speed for safety in case it was change in API scripting	if(this.caller.resolution == 0) { this.caller.resolution = 1; }	//the box we're going to animate	this.box = box;	//timer object, initially null	//so we can test its non-existence against null	this.timer = null;	//its position before moving	var before = {		'x' : pos.x,		'y' : pos.y		};	//its new position	var after = {		'x' : this.box.offsetLeft,		'y' : this.box.offsetTop		};	//if the values are not the same	if(!(before.x == after.x && before.y == after.y))	{		//don't continue if the number of running timers		//is greater than the number of boxes in this group		//(minus one, so as not to count the dummy)		if(dbx.running > this.caller.boxes.length - 1) { return; }		//create a clone of the box,		//set the z-index just below the original box clone		var clone = this.caller.createClone(this.box, 29999, arguments[2], 'dbx-aniclone', true, 'animator');		//make the clone visible		clone.style.visibility = 'visible';		//make the box invisible		this.box.style.visibility = 'hidden';		//calculate the change between the before and after positions		//so it comes out as a negative number for movement upwards/leftwards		var change = {			'x' : after.x > before.x ? after.x - before.x : 0 - (before.x - after.x),			'y' : after.y > before.y ? after.y - before.y : 0 - (before.y - after.y)			};		//then animate the clone from its zero position		//for the amount specified by the direction specified at the set resolution		this.animateClone(			clone,			before,			change,			res,			kbd,			anchor,			manual			);	}};//animate a clonedbxAnimator.prototype.animateClone = function(clone, current, change, res, kbd, anchor, manual){	//reference to this	var self = this;	//timer counter so we know when it's finished	var count = 0;	//add to the number of running timers	dbx.running ++;	//start a perpetual timer	this.timer = window.setInterval(function()	{		//increase counter		count ++;		//change current position by change divided by resolution		current.x += change.x / res;		current.y += change.y / res;		//re-apply the clone position		clone.style.left = parseInt(current.x, 10) + 'px';		clone.style.top = parseInt(current.y, 10) + 'px';		//if the counter has reached resolution		if(count == res)		{			//abandon this timer and nullify the reference			window.clearInterval(self.timer);			self.timer = null;			//deduct from the number of running timers			dbx.running --;			//remove the clone			self.caller.container.removeChild(clone);			//reshow and re-display the original box			self.box.style.visibility = 'visible';			//don't use hasClass here because it tests boundary as well, 			//but here "dbxid" is only a fragment at the start of a subtring			if(self.box.className.indexOf('dbxid') != -1)			{				self.box = dbx.removeClass(self.box, 'dbx\-dummy');			}			//copy its reference back to the boxes object			self.caller.boxes[dbx.getID(self.box)] = self.box;			//if this animation was keyboard initiated, and a manual interaction			if(kbd && manual)			{				//send focus to the anchor, if it's not null and if its parent isn't hidden				//and add the focus class vack to the box				//we need to test the latter to prevent sending focus to a hidden anchor				//which can happen if multiple animations overlap quickly,				//such as from keydown events repeating				if(anchor && anchor.parentNode.style.visibility != 'hidden')				{					//but do it on a instant timer because					//the latency helps browsers to stably retain the focus					//this was originally for opera 8's benefit					//but may as well leave it here anyway					setTimeout(function() { self.caller.refocus(anchor); }, 0);				}				//else if it is null but we're using toggles				//(which can happen during programmatic movement)				else if(self.caller.toggles)				{					//get a reference to the button					var button = self.caller.buttons[dbx.getID(self.box)];					//if there is one and it has an "isactive" flag					if(button && typeof button.isactive != 'undefined')					{						//[if this is a manual interaction, which it is]						//send focus back to the button						//and add the focus class back to the box						//otherwise the focus may get transferred to the clone						//and then lost when the clone is destroyed						self.caller.refocus(button);					}				}				//or if it's null and we're not using toggles (likewise)				else				{					//just attempt to focus the box itself					//and add the focus class back to it					if(typeof self.box.focus == 'function')					{						setTimeout(function() { self.caller.refocus(self.box); }, 0);					}				}			}			//if the onafteranimate function exists			if(typeof dbx.onafteranimate == 'function')			{				//compile the data for and fire onafteranimate event				//do it after a momentary timer to allow for any intense process				//that's tied into the onafteranimate event				setTimeout(function() { dbx.compileAndDispatchOnAfterAnimate(self.box, self.caller) }, 0);			}		}		//if the onafteranimate function exists and the resolution is greater than one		if(typeof dbx.onanimate == 'function' && self.caller.resolution > 1)		{			//compile the data for and fire onafteranimate event			dbx.compileAndDispatchOnAnimate(self.box, clone, self.caller, count, res)		}	}, 20);};//DOM cleaner for IEif(typeof window.attachEvent != 'undefined'){	//window unload listener	window.attachEvent('onunload', function()	{		//relevant events		var ev = ['mousedown', 'mousemove', 'mouseup', 'mouseout', 'click', 'keydown', 'keyup', 'focus', 'blur', 'selectstart', 'statechange', 'boxdrag', 'boxopen', 'boxclose', 'ruletest', 'afteranimate', 'beforestatechange', 'animate'];		var el = ev.length;		//for each item in the document.all collection		var dl = document.all.length;		for(var i=0; i<dl; i++)		{			//for each relevant event			for(var j=0; j<el; j++)			{				//set it to null so it's garbage collected				document.all[i]['on' + ev[j]] = null;			}		}	});}
