//Effect Constants: 1 = Suppress

function hexToRgbFloat(hex) {
  var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
  return result ? {
    0: parseInt(result[1],16),
    1: parseInt(result[2],16),
    2: parseInt(result[3],16)
  } : null;
}

function GradientColourRGB(perc, gradientarray)
{
	var prevcolour = [255,255,255];
	var prevpoint = 0;
	
	var nextcolour = [0,0,0];
	var nextpoint = 0;
	
	for(var x=0;x<gradientarray.length;x++)
	{
		if (gradientarray[x][0] > perc)
		{
			nextcolour = gradientarray[x][1];
			nextpoint = gradientarray[x][0];
			break;
		}
		
		prevcolour = gradientarray[x][1];
		prevpoint = gradientarray[x][0];
	}
	
	var diff = nextpoint - prevpoint;
	var xpert = (perc - prevpoint) / diff;
	var ypert = 1-xpert;
	
	var fin = [(nextcolour[0] * xpert) + (prevcolour[0] * ypert),(nextcolour[1] * xpert) + (prevcolour[1] * ypert),(nextcolour[2] * xpert) + (prevcolour[2] * ypert)];	
	return 'rgb(' + fin[0].toFixed(0) + ',' + fin[1].toFixed(0) + ',' + fin[2].toFixed(0) + ')';
}

function HMIPanel()
{
	Panel = new Object();
	
	Panel.AppliedStyles = [];
	Panel.Subscriptions = [];
	Panel.Animations = [];
	Panel.Inhibited = [];
	Panel.WasConnected = false;
	Panel.UseCSS = false;
	Panel.MaxID = 1;
	Panel.InitialConnection = null;
	Panel.Waiting = false;
	Panel.Connected = false;
	Panel.subscription = "";
	Panel.updatetimeout = 0;
	Panel.allsubs = [];
	Panel.concheck = 0;
	Panel.animupdate = 0;
	Panel.indirect = false;
	Panel.UpdateCycleComplete = null;
	Panel.AnimCycleComplete = null;
	Panel.lastserver = "";
	Panel.acalls = 0;
	Panel.animate = true;
	
	Panel.OnlyWhenFocused = function() {
		window.addEventListener("visibilitychange", function() {
			if (document.visibilityState === "visible")
			{
				console.log("Connecting - Visibility Changed");
				context.Connect(context.lastserver);
			}
			else
			{
				console.log("Disconnecting - Visibility Changed");
				context.CloseSubscription();				
			}
		});
	}
	
	Panel.RemoveStyleFromObject = function (ob, style, id)
	{
		if (ob.tagName == "g")
		{
			//We need to apply this to every child!
			for(var x;x<ob.children.length;x++)
			{
				this.RemoveStyleFromObject(ob.children[x],id);
			}
			return;
		}
		
		//Find this style ID and remove it, then reapply
		for(var x=0;x<this.AppliedStyles.length;x++)
		{
			if (this.AppliedStyles[x].id == id)
			{
				if (this.AppliedStyles[x].object == ob)
				{
					if (this.AppliedStyles[x].style == style)
					{
						this.AppliedStyles.splice(x, 1);
					}
				}
			}
		}
		this.ReapplyStyles(ob);
	};
	
	Panel.RemoveAllFromObject = function (ob, id)
	{
		if (ob.tagName == "g")
		{
			//We need to apply this to every child!
			for(var x;x<ob.children.length;x++)
			{
				this.RemoveAllFromObject(ob.children[x],id);
			}
			return;
		}
		
		//Find this style ID and remove it, then reapply
		for(var x=0;x<this.AppliedStyles.length;x++)
		{
			if (this.AppliedStyles[x].id == id)
			{
				if (this.AppliedStyles[x].object == ob)
				{					
					this.AppliedStyles.splice(x, 1);		
					x--;					
				}
			}
		}
		this.ReapplyStyles(ob);
	};
	
	Panel.RemoveByID = function (id)
	{
		//Find this style ID and remove it, then reapply
		for(var x=0;x<this.AppliedStyles.length;x++)
		{
			if (this.AppliedStyles[x].id == id)
			{					
				this.AppliedStyles.splice(x, 1);		
				x--;				
			}
		}
		this.ReapplyStyles(ob);
	};
	
	Panel.RequestStyle = function (ob,style,value, id, priority,children)
	{

		if ((children != -999) && (ob.tagName == "g"))
		{
			if (children <= 0) return;
			//We need to apply this to every child!
			
			for(var x = 0;x<ob.children.length;x++)
			{						
				this.RequestStyle(ob.children[x],style,value,id,priority,children-1);
			}
			return;
		}
		var matches = 0;
		for(var x=0;x<this.AppliedStyles.length;x++)
		{
			if (this.AppliedStyles[x].object == ob)
			{
				if (this.AppliedStyles[x].style == style)
				{
					matches++;
				}
			}
		}
		
		if (matches == 0)
		{
			//Add current style as the base...
			var as = new Object();
			as.object = ob;
			as.style = style;
			as.value = $(ob).attr(style);
			if (as.value === undefined)
			{
				as.value = $(ob).css(style);
			}
			as.id = 0;
			as.priority = -100;
			as.effect = 0;
			this.AppliedStyles.push(as);
		}
		

		//Add new style
		var asx = new Object();
		asx.object = ob;
		asx.style = style;
		asx.value = value;	
		asx.id = id;
		asx.priority = priority;
		asx.effect = 0;
		this.AppliedStyles.push(asx);
		
		this.ReapplyStyles(ob);

	};
	
	Panel.SupressStyle = function (targetid, id)
	{
		for(var x=0;x<this.Inhibited.length;x++)
		{
			if (this.Inhibited[x].targetid == targetid)
			{
				if (this.Inhibited[x].sourceid == id)
				{
					return;
				}
			}
		}
		sub = new Object();
		sub.targetid = targetid;
		sub.sourceid = id;
		this.Inhibited.push(sub);
		
		this.ReapplyStylesByID(targetid);
	};
	
	Panel.OnConnected = function (func) {
		this.InitialConnection = func;
	}	
	
	Panel.UnsupressStyle = function (id)
	{
		var targetid = 0;
		for(var x=0;x<this.Inhibited.length;x++)
		{			
			if (this.Inhibited[x].sourceid == id)
			{
				targetid = this.Inhibited[x].targetid;
				this.Inhibited.splice(x, 1);		
				x--;
				return;
			}			
		}
		
		this.ReapplyStylesByID(targetid);
	};
	
	Panel.ReapplyStylesByID = function(targetid)
	{
		for(var x=0;x<this.AppliedStyles.length;x++)
		{
			if (this.AppliedStyles[x].object != undefined)
			{
				if (this.AppliedStyles[x].id == targetid)
				{
					this.ReapplyStyles(AppliedStyles[x].object);
				}				
			}			
		}
	}
	
	Panel.ReapplyStyles = function (ob)
	{		
		var AssetStyles = [];
		for(var x=0;x<this.AppliedStyles.length;x++)
		{
			if (this.AppliedStyles[x].object == undefined)
			{
				AssetStyles.push(AppliedStyles[x]);
			}
			if (this.AppliedStyles[x].object == ob)
			{
				AssetStyles.push(AppliedStyles[x]);
			}
		}
		
		AssetStyles.sort(function(a, b){return a.priority - b.priority});
		
		for(var x=0;x < this.Inhibited.length;x++)
		{
			for(var i=0;i<AssetStyles.length;i++)
			{
				if (this.Inhibited[x].targetid == AssetStyles[i].id)
				{
					AssetStyles.splice(i,1);
					i--;
				}
			}
		}
			
		for(var i=0;i<AssetStyles.length;i++)
		{
			if (AssetStyles[i].style == "transform")
			{
				this.MergeStyles(ob,AssetStyles[i].style, AssetStyles[i].value);
			}
			else
			{
				if (UseCSS == true)
					$(ob).css(AssetStyles[i].style,AssetStyles[i].value);
				else
					$(ob).attr(AssetStyles[i].style,AssetStyles[i].value);
			}
		}
	}
	
	Panel.MergeStyles = function (ob, stylename, value)
	{
		var existing = $(ob).attr(stylename);
		if (existing == undefined)
			existing = $(ob).css(stylename);
		
		if (existing == undefined) return value;
		
		var parts = value.split('(');
		
		var found = false;
		var bits = existing.split(') ');
		for(var q=0;q<bits.length;q++)
		{
			if (q < bits.length-1) bits[q] = bits[q] + ')';
			var pieces = bits[q].split('(');
			if (pieces[0] == parts[0])
			{
				bits[q] = value;
				found = true;
			}
		}
		
		if (found == false) bits.push(value);
		
		var finalval = bits[0];
		for(q=1;q<bits.length;q++)
		{
			finalval += " " + bits[q];
		}
		
		if (UseCSS == true)
			$(ob).css(stylename,finalval);
		else
			$(ob).attr(stylename,finalval);
		//return finalval;
		
	}
	
	Panel.GetFreeMaxID = function() {
		var max = 0;
		for(var x=0;x<this.Subscriptions.length;x++)
		{
			if (max < this.Subscriptions[x].id)
				max = this.Subscriptions[x].id;
		}
		max++;
		return max;
	}
	
	Panel.GetValue = function(ob, stylename)
	{
		var existing = $(ob).attr(stylename);
		if (existing == undefined)
			existing = $(ob).css(stylename);
		return existing;
	}
	
	Panel.Subscribe = function (channel,func,digit) {				
		var obx = new Object();
		obx.id = this.MaxID;
		this.MaxID++;
		if (this.MaxID > 100000)
		{
			this.MaxID = this.GetFreeMaxID();
		}
		obx.channel = channel;
		obx.func = func;
		obx.value = undefined;
		if (digit == undefined) digit = false;
		obx.digital = digit;
			
		this.Subscriptions.push(obx);		
				
		for(var x=0;x<this.Subscriptions.length;x++)
		{			
			if (this.Contains(this.allsubs,this.Subscriptions[x].channel))
				continue;
			
			pare = this;
			var n = this.Subscriptions[x].func;			
			this.ChannelSubscribe(this.Subscriptions[x].channel);						
			
			this.allsubs.push(this.Subscriptions[x].channel);
		}
		
		if (this.codelist.indexOf(obx.channel) == -1)
		{				
			this.codelist.push(obx.channel);
		}
		
		return obx.id;
	}
	
	Panel.Incoming = function (chn,vl) {
		if (this.WasConnected == false)
		{
			if ((this.InitialConnection != undefined) && (this.InitialConnection != null))
			{
				this.InitialConnection();
			}
			this.WasConnected = true;
		}		
		var fromval = 0;
		var toval = 0;
		for(var x=0;x<this.Subscriptions.length;x++)
		{			
			if (chn == this.Subscriptions[x].channel)
			{				
				var Animated = false;
				if (this.animate == true)
				{
					if (chn.indexOf(":measurement") > 0)
					{					
						if ((vl != "^") && (this.Subscriptions[x].value != undefined))
						{
							if (this.Subscriptions[x].value != "^")
							{
								if (this.Subscriptions[x].value != vl)
								{
									if (this.Subscriptions[x].digital == false)
									{																					
										fromval = this.Subscriptions[x].value;
										toval = parseFloat(vl);
										
										if (Math.abs(toval - fromval) > 0.05)
										{						
											anim = new Object();
											anim.fromvalue = fromval;
											anim.tovalue = toval;										
											anim.perc = 0;		
											anim.func = this.Subscriptions[x].func;
											anim.subid = x;
											this.Animations.push(anim);
											try
											{											
												this.Subscriptions[x].value = parseFloat(vl);
											}
											catch(e)
											{
												console.log("Callback Failed: " + e);
											}
											Animated = true;
											continue;
										}
										else
										{
											try
											{
												//this.Subscriptions[x].func(toval);
											}
											catch(e)
											{
												console.log("Callback Failed: " + e);
											}
										}
																			
									}
								}
							}
						}					
					}
				}
				//if (Animated == false)
				//{
				
				this.Subscriptions[x].value = parseFloat(vl);
				try
				{
					this.Subscriptions[x].func(vl);
				}
				catch(e)
				{
					console.log("Callback Failed: " + e);
				}
				//}
			}
		}		
	};
	
	Panel.UpdateAnimations = function () {
		try
		{
			for (var x=0;x<this.Animations.length;x++)
			{
				this.Animations[x].perc += 0.05;
				if (this.Animations[x].perc > 0.98)
				{
					this.Animations[x].func(this.Animations[x].tovalue);
					this.Subscriptions[anim.subid].value = this.Animations[x].tovalue;
					//console.log("Removing Animation!");
					this.Animations.splice(x,1);				
				
					x--;
				}
				else
				{					
					var pc = this.Animations[x].perc;
					if (pc > 1) pc = 1;
					if (pc < 0) pc = 0;
					var vl = (this.Animations[x].tovalue * pc) + ((1-pc) * this.Animations[x].fromvalue);
					this.Animations[x].func(vl);
					this.Subscriptions[anim.subid].value = vl;
				}
			}
		}
		catch(exc)
		{
			//alert(exc);
		}
		try
		{											
			if (this.AnimCycleComplete != null)
			{
				this.AnimCycleComplete();
			}
		}
		catch(e)
		{
			console.log("Callback Failed: " + e);
		}
		//this.acalls++;
		//console.log(this.acalls);
	}
	
	Panel.Contains = function(arr, cnt)
	{
		var cl = arr.length;
		for(var x=0;x<cl;x++)
		{
			if (arr[x] == cnt)
				return true;
		}
		return false;
	}
	
	Panel.CheckConnection = function () {
		if (this.Waiting == false)
		{
			//Trigger an update...
			
		}
	}
	
	Panel.Connect = function (srvr) {
		//this.connection = new ARDIHMI();		
		if (this.Subscriptions.length == 0)
		{
			if (this.InitialConnection != undefined)
			{
				this.InitialConnection();
			}
			this.WasConnected = true;
		}
		else
		{			
			this.ConnectTo(srvr);
			pare = this;
			
			if (this.animupdate != 0)
			{
				window.clearInterval(this.animupdate);
				window.clearInterval(this.concheck);
			}
			
			this.concheck = window.setInterval(function () {
					pare.CheckConnection();
				},1000);
			this.animupdate = window.setInterval(function () {
					pare.UpdateAnimations();
				},50);
		}
	}
	
	Panel.connected = false;
	Panel.servername = "";
	Panel.dataport = 8079;
	Panel.subscriptions = [];
	Panel.errorfunction = null;
	
	Panel.ChannelSubscribe = function(code, func) {
		var ob = new Object();//new Subscription();
		ob.code = code;		
		this.subscriptions.push(ob);
		
		if (code == "Connected") return;
		
		if (this.codelist.indexOf(code) == -1)
		{
			this.codelist.push(code);
		}
	}
	
	Panel.DataUpdate = function(name, value) {
		try
		{
			this.Incoming(name,value);
		}
		catch(err)
		{
			context.Incoming(name,value);
		}
		/*var x=0;
		for(x=0;x<this.subscriptions.length;x++)
		{
			if (name == this.subscriptions[x].code)
			{
				//alert('Calling Function!');
				this.Incoming(name,value);
			}
		}*/
		//alert('Data Updated: ' + name + ' = ' + value);
	}
	
	Panel.Update = function () {
		this.Waiting = true;
		if (this.servername == "")
		{
			console.log("Some kind of context problem happening here!");
		}
		if (this.subscription == "")
		{
			this.ConnectData();
			return;
		}
		
		var addr = this.GetConsolidatorURL();
		context = this;
		var ajax = new XMLHttpRequest();
		try
		{
			ajax.open("POST", addr + "update?format=json",true);
		}
		catch(err)
		{			
			window.clearTimeout(context.updatetimeout);
			  context.updatetimeout = window.setTimeout(context.Update.bind(context),1000);
			  console.log("Error Accessing Data: " + ajax.status);
			  return;
		}
		ajax.setRequestHeader("Content-type","application/x-www-form-urlencoded");
		ajax.onload = function(e) {
		  if (ajax.status != 200)
		  {
			  window.clearTimeout(context.updatetimeout);
			  context.updatetimeout = window.setTimeout(context.Update.bind(context),1000);
			  console.log("Error Accessing Data: " + ajax.status);
			  return;
		  }		  
		  if (ajax.responseText == "Too Many Users")
		  {
			alert("Sorry, but your ARDI server has too many concurrent users.\r\nPlease close any other open windows or tabs and hit 'F5' to try again.\r\nYou can upgrade your ARDI license to allow additional concurrent users to connect.");
			return;
		  };
		  if (ajax.responseText == "Subscription ID Lapsed")
		  {
			return;
		  };
		  if (ajax.responseText == "Invalid Subscription ID")
		  {
			  return;
		  }
		  if (ajax.responseText == "")
		  {
			  this.ConnectData();
		  }
		  obj = JSON.parse(ajax.responseText);
		  if (context.subscription != obj.id)
		  {
			  console.log("Subscription has changed!");
			  return;
		  };
		  context.subscription = obj.id;
		  
		  var x = 0;
		  for(x=0;x<obj.items.length;x++)
		  {
				context.DataUpdate(obj.items[x].code,obj.items[x].value);
		  }
		  
		  try
		  {											
				if (context.UpdateCycleComplete != null)
				{					
					context.UpdateCycleComplete();
				}
		  }
		  catch(e)
		  {
				console.log("Callback Failed: " + e);
		  }
		  
		  //Reset Timer...
		  window.clearTimeout(context.updatetimeout);
		  context.updatetimeout = window.setTimeout(context.Update.bind(context),1000);
		}
		
		ajax.onreadystatechange = function (oEvent) {
			if (ajax.readyState == 4)
			{
				if ((ajax.status <= 199) || (ajax.status >= 299))
				{
					if (this.onerror != null)
					{						
						this.onerror(ajax.statusText);						
					}
				}
			}
		}
		
		ajax.onerror=function(e) {
			
			console.log("Error - Closing Query and Resubscribing");
			context.CloseSubscription();
			context.Waiting = false;				
			window.clearTimeout(context.updatetimeout);
			context.updatetimeout = window.setTimeout(context.Update.bind(context),1000);
			
		}
		
		ajax.send("id=" + this.subscription,true);
	}
	
	Panel.CloseSubscription = function() {	
		if (this.subscription != "")
		{
			var ajax = new XMLHttpRequest();
			var addr = this.GetConsolidatorURL();
			try
			{
				ajax.open("POST",addr + "unsubscribe?id=" + this.subscription,true);
			}
			catch(e)
			{
			}
		}
		window.clearInterval(this.concheck);
		this.concheck = 0;
		
		window.clearInterval(this.animupdate);
		this.animupdate = 0;
		
		this.Animations = [];
		this.subscription = "";		
	}
	
	Panel.ResetSubscription = function() {	
		if (this.subscription != "")
		{
			var ajax = new XMLHttpRequest();
			var addr = this.GetConsolidatorURL();
			try
			{
				ajax.open("POST",addr + "unsubscribe?id=" + this.subscription,true);
			}
			catch(e)
			{
			}
		}
		this.subscription = "";		
		this.ConnectData();
	}
	
	Panel.ConnectData = function () {
		var addr = this.GetConsolidatorURL();
		//alert('Connecting To ' + addr);
		
		if (this.codelist.length == 0)
			return;
		
		var ajax = new XMLHttpRequest();
		ajax.open("POST",addr + "subscribe",true);
		ajax.setRequestHeader("Content-type","application/x-www-form-urlencoded");
		
		
		var codeset = "";
		var v = 0;
		for(v=0;v<this.codelist.length;v++)
		{
			if (codeset != "") codeset += ",";
			codeset += this.codelist[v];
		}
		
		var context = this;
		
		ajax.onload = function(e) {
		  //alert(ajax.responseText);
		  obj = JSON.parse(ajax.responseText);
		  context.subscription = obj.id;
		  
		  var x = 0;
		  for(x=0;x<obj.items.length;x++)
		  {
			context.DataUpdate(obj.items[x].code,obj.items[x].value);
		  }
		  
		  try
		  {											
				if (context.UpdateCycleComplete != null)
				{					
					context.UpdateCycleComplete();
				}
		  }
		  catch(e)
		  {
				console.log("Callback Failed: " + e);
		  }
		  
		  if (context.onConnected != null)
		  {
			context.DataUpdate("Connected","Yes");
			context.onConnected();			
		  }
		  console.log("Connected - Accessing " + context.codelist.length + " points!");
		  
		  //Initialise Timer
		  window.clearTimeout(context.updatetimeout);
		  context.updatetimeout = window.setTimeout(context.Update.bind(context),1000);
		  
		  if (context.concheck == 0)
		  {
			context.concheck = window.setInterval(function () {
					context.CheckConnection();
				},1000);
		  }
		  if (context.animupdate == 0)
		  {
			context.animupdate = window.setInterval(function () {
					context.UpdateAnimations();
				},50);
		  }
		}
		
		ajax.onreadystatechange = function (oEvent) {
			if (ajax.readyState == 4)
			{
				if ((ajax.status <= 199) || (ajax.status >= 299))
				{
					ajax.onerror(ajax.statusText);
				}
			}
		}
		
		ajax.onerror = function(e) {
			if (e != "OK")
			{
				console.log("Cannot Subscribe - Retrying Query");
				this.Waiting = false;			
				window.clearTimeout(context.updatetimeout);
				context.updatetimeout = window.setTimeout(context.Update.bind(context),5000);
			}
		};
				
		ajax.send("format=json&codes=" + codeset,true);
	}
	
	Panel.Pause = function () {
		console.log("Pausing Subscription");
		this.CloseSubscription();
	}	
	
	Panel.Resume = function () {
		this.subscription = "";	
		var url = this.GetConsolidatorURL();
		
		if (url == "") return;
		
		console.log("Resuming Subscription To " + url);
		this.ConnectData();
	}
	
	Panel.Close = function () {
		var addr = this.GetConsolidatorURL();
		context = this
		if (context.subscription != "")
		{
			window.clearTimeout(context.updatetimeout);
			window.clearInterval(context.concheck);
			window.clearInterval(context.animupdate);
			var ajax = new XMLHttpRequest();
			ajax.open("POST",addr + "unsubscribe?format=json",true);
			ajax.setRequestHeader("Content-type","application/x-www-form-urlencoded");
			ajax.onload = function(e) {
				console.log("Disconnected From Server");
			}
			ajax.send("id=" + this.subscription);
		}
	}
	
	Panel.GetConsolidatorURL = function () {	
		
		
		var srvr = this.servername;
		var idx = srvr.indexOf("/")
		if (idx >= 0)
		{
			srvr = srvr.substring(0,idx);
			this.servername = srvr;
			srvr = this.servername
		}
		var idx = srvr.indexOf(":")
		if (idx >= 0)
		{
		    srvr = srvr.substring(0,idx)
			this.servername = srvr
			srvr = this.servername
		}
		
		if (srvr == "") return "";
		
		var addr = "http://" + srvr + ":" + this.dataport + "/";
		
		if (this.indirect == true)
		{
			addr = "https://" + this.fullservername + "/data/livedata?format=json&port=" + this.dataport + "&action=";
		}
		return addr;
	}
	
	Panel.ConnectTo = function (addr,ctx) {
		this.lastserver = addr;
		this.DataUpdate("Connected","No");
		if (typeof ctx == 'undefined') ctx = 1;
		context = this;
		
		this.fullservername = addr;
		
		var ajax = new XMLHttpRequest();
		var protocol = 'http:';
		try
		{
			protocol = document.location.protocol;
			if (protocol == "https:")
			{
				this.indirect = true;
			}
		}
		catch(e)
		{
		}
		if (protocol == "file:") protocol = "http:";
		ajax.open("GET", protocol + "//" + addr + "/api/connect?format=json", true);
		ajax.send();
		ajax.onload = function(e) {
		  
		  console.log("Connected To Server");
		  
		  obj = JSON.parse(ajax.responseText);
		  var x = 0;
		  for(x=0;x<obj.services.length;x++)
		  {
			if (obj.services[x].name == "data") 
			{
				if (ctx == 1)
				{
					context.servername = addr;
					if (obj.services[x].hasOwnProperty('host'))
					{
						context.servername = obj.services[x].host;
					};
					
					context.dataport = obj.services[x].port;
					
					context.ConnectData();
					return;
				}
			}
			if (obj.services[x].name == "contextdata") 
			{
				if ((ctx == obj.services[x].id) || (ctx == obj.services[x].context))
				{
					context.servername = addr;
					if (obj.services[x].hasOwnProperty('host'))
					{
						context.servername = obj.services[x].host;
					};
					
					context.dataport = obj.services[x].port;
					
					context.ConnectData();
					return;
				}
			}
		  }
		};

		if (this.onConnected != null)
		{
			this.onConnected();
		}
	}
		
	Panel.RemoveSubscription = function(sub) {
		var indexes = [];
		for(var x=this.subscriptions.length-1;x>=0;x--)
		{
			if (this.subscriptions[x].code == sub)
			{
				this.subscriptions.splice(x,1);
				console.log("Removed Element @ " + x);
			}
		}
		
		var ix = this.codelist.indexOf(sub);
		if (ix >=0)
		{
			this.codelist.splice(ix,1);
		}		
		
		console.log("Removed: " + this.codelist.length);
	}
	
	Panel.RemoveSubscriptionID = function(idno) {		
		var newcodelist = [];
		console.log("Removing Subscription ID " + idno);
		for(var x=this.Subscriptions.length-1;x>=0;x--)
		{
			//console.log("Comparing " + idno + " to " + this.Subscriptions[x].id);
			if (this.Subscriptions[x].id == idno)
			{
				if (this.Subscriptions.length == 1)
				{
					this.Subscriptions = [];
				}
				else
				{
					this.Subscriptions.splice(x,1);
				}
				//console.log("Removed Element @ " + x);
				continue;
			}
			if (newcodelist.indexOf(this.Subscriptions[x].channel) == -1)
			{				
				newcodelist.push(this.Subscriptions[x].channel);
			}
		}
		
		this.codelist = newcodelist;
		
		console.log("Removed Items - Now subscribing to " + this.codelist.length + " items");
	}
	
	Panel.SetCodes = function(codeset) {
		this.codelist = codeset;
	}

	Panel.codelist = [];
	Panel.subscription = "";
	Panel.onConnected = null;
	
	return Panel;
}
