// IOTBS2.1 :: Invasion of the Body Switchers - Look Who's Switching Too
// >>> Core functions for all versions
// ***********************************************
// This copyright statement must remain in place for both personal and commercial use
// GNU General Public License -- http://www.gnu.org/copyleft/gpl.html
// ***********************************************
// Original concept by Andy Clarke -- http://www.stuffandnonsense.co.uk/
// DOM scripting by brothercake -- http://www.brothercake.com/
// Create element and attributes based on a method by beetle -- http://www.peterbailey.net/
//************************************************


//global preferences manager reference
var switcher;
var nhmccookie = null;


//preferences manager
function switchManager(canvas, path)
{
	//save to global reference
	switcher = this;

	//store reference to canvas element
	//if the canvas is 'body' and the document.body object exists, use that reference
	//because in older moz builds (eg ns7.1) document.getElementsByTagName('body')[0] is undefined
	//unless this script is in the body section (which it isn't)
	this.canvas = canvas == 'body' && typeof document.body != 'undefined' ? document.body : document.getElementsByTagName(canvas)[0];

	//store the initial classname
	this.initial = this.canvas.className;

	//if the default classname is empty, add "iotbs"
	//because we need there to be at least one classname already -
	//the leading and trailing space in each custom classname is required,
	//but we can't set the body classname as " something" (beginning with a leading space)
	//because that fails in some Opera 7 builds
	if(this.initial == '')
	{
		this.initial = 'iotbs';
	}



	//the stylesheet path argument is used to switch on load-mode
	//so store the path, or a null value if none or an empty string is present
	this.path = (typeof path != 'undefined' && path != '' ? path : null);

	//regex for finding the native integration token within cookie data
	this.itoken = /(\:\[[0-9]+\])/;

	//if load mode is in use
	if(this.path != null)
	{
		//ID reference and token index data extractable from integration token
		//set to empty string and -1 by default (discountable values)
		//to make testing them easier - it saves additional typeof discriminators
		this.tokenrefs = ['', -1];

		//collection of <link> elements we also need for integration mode
		this.linkeles = document.getElementsByTagName('link');
	}

	//whether one of the switchers is part of the native group
	//this will be a reference to the applicable oject, or null if none
	this.master = null;



	//string for storing the overall custom classname, for writing to the body classname
	//I was originally storing it in the body class name directly
	//but 1.7+ mozilla builds were not honouring the trailing whitespace we need
	this.string  = '';

	//string for storing the switcher idents and classnames as name/value pairs
	//for storing in the cookie and identifying link elements when load-mode is used
	this.idstring = '';


	//identify opera for applying tweaks
	this.isop = typeof window.opera != 'undefined';

	//identify IE for applying hacks
	this.isie = typeof window.attachEvent != 'undefined' && !this.isop;

	//identify konqueror for applying hacks
	this.iskde = navigator.vendor == 'KDE';


	//look for a stored cookie
	this.cookie = this.read();

	//if it exists
	if(this.cookie != null)
	{
		//store cookie value to string
		this.string = this.cookie;
		//if load-mode is not in use
		if(this.path == null)
		{
			//set new body class name
			this.canvas.className = this.initial + this.string;
		}

		//for each value in the idcookie array
		for(var i=0; i<this.idcookie.length; i++)
		{
			//add data to id string
			this.idstring += this.idcookie[i][0] + '=' + this.idcookie[i][1] + '&';

			//if load-mode is in use
			if(this.path != null)
			{
				//get a reference to the applicable <link> element
				this.linkele = document.getElementById(this.idcookie[i][0] + '-stylesheet');

				//if it exists
				if(this.linkele != null)
				{
					//if the integration token ID reference matches this switcher ident
					if(this.tokenrefs[0] == this.idcookie[i][0])
					{
						//array of stylesheets and alternate stylesheets
						//which comprise this native switching group
						//the first (default) is the one we already have
						this.alternates = [this.linkele];

						//set an in-scope flag that we'll use to determine
						//when we've reached, then gone past,
						//the stylesheets we're interested in
						var inscope = false;

						//for each <link> element
						var len = this.linkeles.length;
						for(var j=0; j<len; j++)
						{
							//if this link is our master default stylesheet
							if(this.linkeles[j] == this.alternates[0])
							{
								//set the flag to say we're in scope
								inscope = true;

								//remember this index
								var ind = j;
							}

							//otherwise if we're in scope and this is not an "alternate" stylesheet
							else if(inscope && (this.linkeles[j].getAttribute('rel') == null || !/(alternat)(iv)?(e stylesheet)/i.test(this.linkeles[j].rel)))
							{
								//set the flag to say we're out of scope
								inscope = false;

								//finally.. enable the indicated stylesheet
								//which will be the integration token index + stored index of master
								this.linkeles[this.tokenrefs[1] + ind].disabled = false;

								//and we can stop now
								break;
							}

							//if we're in scope and this is a titled stylesheet
							if(inscope && this.linkeles[j].getAttribute('rel') != null && /(stylesheet)/i.test(this.linkeles[j].rel) && this.linkeles[j].getAttribute('title') != null && this.linkeles[j].title != '')
							{
								//disable it
								this.linkeles[j].disabled = true;
							}
						}
					}

					//if it doesn't match (or the token ID ref is empty)
					else
					{
						// change its href to load the relevant stylesheet
						//using path value, plus established naming conventions, to create the filename
						this.linkele.href = this.path + this.idcookie[i][0] + '-' + this.idcookie[i][1] + '.css';
					}
				}
			}
		}
	}
	// NHMC Mod to set my own variable for cookie reading
	nhmccookie = this.read();
	// NHMC Mod to ensure that website logo changes based on cookie data - note that cookie returns with flanking whitespace characters 
			if (nhmccookie != null) {
				var nhmcstringlength = nhmccookie.length -1;
				var nhmcchosen = nhmccookie.substring(1,nhmcstringlength);
				/* CHANGE NEEDED HERE FOR CORRECT PATH */
				// document.images["websitebanner"].src ="/betterdesign2/websitebanner-" + nhmcchosen + ".gif";
			}


	//*** dev
	//document.title = '<' + this.canvas.className.replace(/ /g,'+') + '>   [' + this.string.replace(/ /g,'+') + ']';

	//add a DOM memory cleaner for win/ie
	if(typeof window.attachEvent != 'undefined')
	{
		window.attachEvent('onunload', function()
		{
			//potential closures from all interface scripts
			var closures = ['onchange', 'onclick', 'onkeydown', 'checker'];

			//for each item in the document.all collection
			for(var i=0; i<document.all.length; i++)
			{
				//for each of the the listed events
				for(var j=0; j<closures.length; j++)
				{
					//set the expando property to null
					//so it's garbage collected
					document.all[i][closures[j]] = null;
				}
			}
		});
	}

};


//set a cookie method
switchManager.prototype.set = function(days)
{
	//format expiry date
	var thedate = new Date();
	thedate.setTime(thedate.getTime() + ( days *24*60*60*1000));

	//store the idstring
	var info = this.idstring;

	//if the value is empty, set its expiry in the past to delete the cookie
	if(info == '') { thedate.setTime(0); }

	//create the cookie
	document.cookie = 'bodySwitcherKV=' + info
		+ '; expires=' + thedate.toGMTString()
		+ '; path=/';

};

//read a cookie method
switchManager.prototype.read = function()
{
	//set null reference so we always have something to return
	this.cookie = null;

	//if a cookie exists and it's ours
	if(document.cookie && document.cookie.indexOf('bodySwitcherKV=')!=-1)
	{
		//idcookie array is where extracted data will be stored
		// this.idcookie = [];
        this.idcookie = new Array(); // new code NHMC 11th May 07 to ensure initialisation works in IE7
		//split cookie to extract relevant information
		//creating a classname string to apply to the body (this.cookie string)
		//and name/value pairs array for loading a new stylesheet in load-mode (this.idcookie array)
		this.cookie = document.cookie.split('bodySwitcherKV=')[1].split(';')[0].split('&');
		var tmp = '', len = this.cookie.length;
		for(var i=0; i<len; i++)
		{
			this.cookie[i] = this.cookie[i].split('=');
			if(this.cookie[i].length > 1)
			{
				//if load mode is in use and the integration token is there
				if(this.path != null && this.itoken.test(this.cookie[i][1]))
				{
					//save the ID reference (switcher ident)
					//and the token index (position in alternates group)
					this.tokenrefs = [
						this.cookie[i][0],
						parseInt(this.cookie[i][1].split(':[')[1], 10)
						];
				}
				//clean the integration token from the value
				this.cookie[i][1] = this.cookie[i][1].replace(this.itoken, '');

				//store to idcookie array
				tmp += ' ' + this.cookie[i][1] + ' ';
				this.idcookie[i] = this.cookie[i];
			}
		}
		//re-save cleaned value to this.cookie
		//so it can be returned as the data
		this.cookie = tmp;
		
	}
    
	return this.cookie;
};


//save the current switcher state
switchManager.prototype.save = function(ident, chosen, ind, obj)
{
	//switcher ident (label)
	this.ident = ident;

	//remove this ident from switcher idstring
	this.idstring = this.idstring.replace(this.ident + '=', '');

	//run through classnames array
	var len = obj.classes.length;
	for(var i=0; i < len; i++)
	{
		//remove this key (custom class name) from string
		this.string = this.string.replace(' ' + obj.classes[i] + ' ','');

		//remove this key and possible integration token from idstring
		var reg = new RegExp('(' + obj.classes[i] + ')' + '(\:\[[0-9]+\])?' + '&', '');
		this.idstring = this.idstring.replace(reg,'');
	}

	//if chosen value isn't default
	if(chosen != 'default')
	{
		//add to classname string
		//we need both a leading and a trailing space to work with
		//to avoid confusion with identical leading or trailing substrings in classnames,
		//such as "high" and "highcontrast" or "large-serif" and "small-serif"
		this.string += ' ' + chosen + ' ';

		//add to switcher idstring
		this.idstring += this.ident + '=' + chosen;

		//if this is the native switching group
		if(this.master == obj)
		{
			//add a token to the string that indicates integration
			//containing a number for its index in the group,
			//corresponding to its index in the alternates array.
			//the colon is a safe delimiter to use here,
			//because the key value is ultimately a CSS class name,
			//which isn't allowed to contain a colon
			this.idstring += ':[' + ind + ']';
		}

		//add the trailing delimiter
		this.idstring +=  '&';
	}

	//if load-mode is in use
	if(this.path != null)
	{
		//if this isn't the native switching group
		if(this.master != obj)
		{
			//get a reference to applicable <link> element
			var linkele = document.getElementById(this.ident + '-stylesheet');

			//if it exists
			if(linkele != null)
			{
				//stylesheet src path
				var sheetpath = this.path + this.ident + '-' + chosen + '.css';

				//if this is opera we have to set the href on a timer
				//otherwise the change doesn't kick in
				//unless the stylesheet is already in cache
				if(this.isop)
				{
					setTimeout(function() { linkele.href = sheetpath; }, 10);
				}

				//if this is IE, we have to preload the stylesheet
				//for exactly the same reason as opera
				//but we can't use the timeout method
				//because that causes win/ie5.0 on 2k and 98se to crash
				if(this.isie)
				{
					//so preload it using XMLHttpRequest
					//I originally tried doing it using createStyleSheet
					//which was simpler and took less code
					//but it didn't solve the problem ... this does
					var request = new ActiveXObject('Microsoft.XMLHTTP');

					//once the stylesheet has loaded
					request.onreadystatechange = function()
					{
						//readyState of 4 = document has finished loading
						//status of 200 = okay, 304 = not modified
						if(request.readyState == 4 && /(200|304)/.test(request.status.toString()))
						{
							//load it into the relevant link element
							//but for some reason if we just set the href to the path directly
							//and the new sheet has an @import statement in it, IE6 will crash!
							//but equally obscurely, if we first set it to an empty path
							//the crash doesn't happen and the whole process works smoothly!
							linkele.href = '';
							linkele.href = sheetpath;
							//alert("LOADER change");
						}
					};

					//the request must come after the readystate function is defined
					//otherwise that function may not be called if the request is fulfilled very quickly
					request.open('GET', sheetpath, true);
					request.send(null);
				}

				//now load the stylesheet for *everyone*
				//[including opera and IE so that if the stylesheet is already in cache
				// the change will happen straight away
				// this does mean that under some circumstances, those browsers
				// will load each stylesheet twice, making two server requests
				// but that can't be helped  - there's no way to say, don't do this
				// if the stylesheet is in cache, because there's no way to know
				// and we can't just set a flag so it only goes through the preload routine once
				// because someone may have their browser set not to cache stylesheets (or anything)]
				linkele.href = sheetpath;
				//alert("default change");
			}
		}

		//if it is the native switching group
		else
		{
			//for each stylesheet in the native group
			len = this.alternates.length;
			for(i=0; i<len; i++)
			{
				//set it to disabled unless i matches the index of this option in the group
				this.alternates[i].disabled = i != ind;
			}
		}
	}

	//if load-mode is not in use
	else
	{
		//set new body class name
		this.canvas.className = this.initial + this.string;
	}
	/** NHMC mod to force logo switching -28 Jun 07 **/
	/* CHANGE NEEDED HERE for CORRECT PATH */
	// document.images["websitebanner"].src = "/betterdesign2/websitebanner-" + chosen + ".gif";

	//store changes to a cookie which expires a year from now
	this.set(365);

	//*** dev
	//document.title = '<' + switcher.canvas.className.replace(/ /g,'+') + '>   [' + switcher.string.replace(/ /g,'+') + ']';
	

	
};


//compile preferred/alternate stylesheets collection for integration mode
switchManager.prototype.integrate = function(obj, divid)
{
	//save bodySwitcher object to master reference
	this.master = obj;

	//array of stylesheets and alternate stylesheets
	//which comprise this native switching group
	//the first (default) can be idetified by its ID
	this.alternates = [document.getElementById(divid + '-stylesheet')];

	//then the others are the next link element and all following
	//which have both a title, and rel="alternate stylesheet"

	//set an in-scope flag that we'll use to determine
	//when we've reached, then gone past,
	//the stylesheets we're interested in
	var inscope = false;

	//for each <link> element
	var len = this.linkeles.length;
	for(var i=0; i<len; i++)
	{
		//if the *previous* link is our master default stylesheet
		//(so obviously it can't be the first one)
		if(i > 0 && this.linkeles[i - 1] == this.alternates[0])
		{
			//set the flag to say we're in scope
			inscope = true;
		}

		//if we're in scope
		if(inscope)
		{
			//if this <link> has a title, and rel="alternate stylesheet"
			if(this.linkeles[i].getAttribute('title') != null && this.linkeles[i].getAttribute('rel') != null && /(alternat)(iv)?(e stylesheet)/i.test(this.linkeles[i].rel))
			{
				//add it to the alternates array
				this.alternates[this.alternates.length] = this.linkeles[i];
			}
			//else it doesn't have the necessary attributes
			else
			{
				//set the flag to say we're out of scope again
				inscope = false;

				//and in fact we can stop now
				//since there's only ever one native switching group
				//and if the page does contain more than one
				//the first one takes precedence anyway
				break;
			}
		}
	}


	//remember which stylesheet is enabled as an index in the alternates array
	//we're just setting 0 here instead of finding out which one is enabled now
	//which does mean that an unecessary save will happen if the default is not enabled
	//but it's much less code to do it this way
	//the reason we need this information is so that we only save when necessary
	//partly because it's more efficient, but mostly because
	//the save action changes the selectedIndex, and if we didn't discriminate
	//you wouldn't be able to use the selector manually at all, because on every timeout loop
	//the selector would reset back to show the active stylesheet,
	var isenabled = 0;

	//create a watcher to monitor alternate stylesheets
	var watcher = window.setInterval(function()
	{
		//run through alternate stylesheets
		var len = switcher.alternates.length;
		for(var i=0; i<len; i++)
		{
			//if this sheet is enabled
			if(!switcher.alternates[i].disabled)
			{
				//if the enabled index is different from the last enabled index
				if(i != isenabled)
				{
					//update last enabled index
					isenabled = i;

					//send index to interface callback function
					//which updates the switcher control, if present
					obj.update(i);

					//save the current switcher state
					switcher.save(
						divid, //switcher ident (divid)
						switcher.alternates[i].href.split(divid + '-')[1].split('.css')[0], //extrapolate class name value from stylesheet href
						i, //the index of this option in the switcher group
						obj //a reference to this bodySwitcher object
						);
				}

				//stop now - only one is enabled
				break;
			}
		}
	},55);
};


//create element and attributes method -- http://www.codingforums.com/showthread.php?s=&postid=151108
switchManager.prototype.create = function(tag, attrs)
{
	//detect support for namespaced element creation, in case we're in the XML DOM
	var ele = (typeof document.createElementNS != 'undefined') ? document.createElementNS('http://www.w3.org/1999/xhtml',tag) : document.createElement(tag);

	//run through attributes argument
	if(typeof attrs != 'undefined')
	{
		for(var i in attrs)
		{
			switch(i)
			{
				//create a text node
				case 'text' :
					ele.appendChild(document.createTextNode(attrs[i]));
					break;

				//create a class name
				case 'class' :
					ele.className = attrs[i];
					break;

				//create a for attribute
				case 'for' :
					ele.setAttribute('htmlFor',attrs[i]);
					break;

				//create a generic attribute using IE-safe attribute creation
				default :
					ele.setAttribute(i,'');
					ele[i] = attrs[i];
					break;
			}
		}
	}
	return ele;
};




//DOM-ready watcher
function domReady()
{
	//start or increment the counter
	this.n = typeof this.n == 'undefined' ? 0 : this.n + 1;

	//if DOM methods are supported, and the body element exists
	if(typeof document.getElementsByTagName != 'undefined' && (document.getElementsByTagName('body')[0] != null || document.body != null))
	{
		//if this is not mac/ie
		if(!(typeof document.all != 'undefined' && typeof window.opera == 'undefined' && typeof document.mimeType == 'undefined' && typeof window.sidebar == 'undefined'))
		{
			//initialise IOTBS
			iotbs();
		}
	}

	//otherwise if we haven't reached 60 (so timeout after 15 seconds)
	else if(this.n < 60)
	{
		//restart the watcher
		setTimeout('domReady()', 250);
	}
};
//start the watcher
domReady();
/*
this.cookie = this.read();

	//if it exists
	if(this.cookie != null)
	{
		document.images["websitebanner"].src = "/betterdesign2/websitebanner-" + this.cookie + ".gif";
	}
*/		




