/*
ic0n.js
------
  - base ic0n library
 
  author 
  ------
	- mike macmillan (mikejmacmillan@gmail.com)
*/

//** create our global library object
ic0n = function(parentObj) {
	var _components = [];
	var _objid = new Date() * 1;

	var root = {
		Browser: {
			IsIe: false,
			Version: 0
		},
		
		OnDom: function(func) {
			//** to implement =[
			this.AddListener(window, "load", func);
		},

		/* 
		| OnLoad
		| ------
		|   a shorthand to adding a handler to the window.onload event
		*/
		OnLoad: function(func) {
			this.AddListener(window, "load", func);
		},


		/* 
		| Each
		| ------
		|   executes a function for each item in an array
		*/
		Each: function(arr, func) {
			if (!arr || !func) return;

			var retval = null;

			if (arr.length)
			//** execute the function for each item in the array
				for (var i = 0; i < arr.length; i++)
				retval = retval ? retval + func(arr[i]) : func(arr[i], i);
			else
			//** execute the function for each property on the object
				for (var s in arr)
				retval = retval ? retval + func(s, arr[s]) : func(s, arr[s], i);

			//** return the aggregated output of each function
			return retval;
		},


		/*
		| OnInitialize
		| ------------
		|   the default ic0n.Event.Initialize handler.  initializes the ic0n framework, verifies
		|   component dependencies, etc...
		*/
		OnInitialize: function() {
			var comp = null;
			
			//** simple test for ie version using conditional comments, inspiration: http://james.padolsey.com/javascript/detect-ie-in-js-using-conditional-comments/, modified to <a> tag as <i> is deprecated
			if(document.all) {
				var v = 4,
				    div = document.createElement("div"),
				    tags = div.getElementsByTagName("a");
				
				while(div.innerHTML = "<!--[if gt IE " + ++v + "]><a></a><![endif]-->", tags[0]) ;
				root.Browser.IsIe = true;
				root.Browser.Version = v;
			}

			//** initialize our components
			for (var i = 0; i < _components.length; i++) {
				comp = _components[i];

				//** if the component participates in the event flow, run its init/load lifecycle
				if (comp.EventFlow) {
					if(comp.Event.Initialize) {
						//** launch the initialize event
						comp.EventFlow.DispatchEvent(comp.Event.Initialize, comp);

						//** mark the component as initialized
						comp.ComponentInfo.Initialized = true;
					}

					if(comp.Event.Load) {
						//** then launch the load event to run any listeners that require the component to be loaded
						comp.EventFlow.DispatchEvent(comp.Event.Load, comp);
					}
				}
			}
		},

		/*
		| GenerateId()
		| ------
		|   - generates a unique (for the most part...) id for use for something.  a *real* implementation of a guarenteed unique id should be here instead =]
		*/
		GenerateId: function() {
			return ++_objid;
		},


		/*
		| Empty(value)
		| ------
		|   - determines if there is a value of use for the frontend in the given object
		*/
		Empty: function(strX) {
			if (strX === null || strX === "null" || strX === "" || typeof(strX) === 'undefined' || strX == "undefined" || strX == "Undefined")
				return true;

			return false;
		},

		Reload: function() {
			top.location.href = top.location.href.replace('#','');
		},

		/*
		| Defined(obj1, "SomeProp")
		| ------
		|   - determines if the member of the given object is undefined
		*/
		Defined: function(obj, member, ttype) {
			if (!obj)
				return false;
				
		    //** if no member, but a target type is given, assume we want to test the objects type against the target type
			if(!member && ttype)
			    return typeof(obj) == ttype;
			
			//** if the object we're testing for was passed as a string, ex: $_.Defined("SomeVar"), test the definition against the window object
			if(typeof(obj) === 'string' && typeof(member) === 'undefined') {
			    member = obj;
			    obj = window;
			}		
			
			//** if we're not testing a member or target type, simply see if this object is undefined
			if(obj && !member)
				return typeof(obj) !== 'undefined';

			if(typeof (obj[member]) !== 'undefined') {
			    //** if a specific type was given, return the comparison
			    if(ttype && typeof(obj[member]) !== ttype)
			        return false;
			        
			    return true;
			}
			
			return false;
		},
		
		Money: function (val) {
			val = parseFloat(val);
			return "$" + (isNaN(val) ? 0 : Number(val.toFixed(2)).toLocaleString());
		},
		
		JSONDate: function (dateString) {
			return $_date.DateTime(dateString);
		},

		/*
		| Default(obj, "SomeProp", "default value")
		| ------
		|   - if the member of the given object is undefined, the given default value is applied
		*/
		Default: function(obj, member, value) {
			//** resolve an id reference
			if (typeof ($_dom) !== 'undefined')
				obj = $_dom.ResolveId(obj);

			//** if the member isn't defined on the type, set its default
			if (!$_.Defined(obj, member) || obj[member] == "")
				obj[member] = value;
		},

		/*
		| Merge(obj1, obj2, obj3, obj4, ...)
		| ------
		|   - a simple method for heirarchical object merging.  any number of objects can be merged, with the 
		|	 objects at the bottom of the stack being the "master" objects, or those whos members override
		|	 duplicate members from other merging objects...
		|
		*/
		Merge: function(sourceObj) {
			sourceObj = sourceObj || {};
			var objs = [], curObj = null;

			if (arguments.length == 1)
				return sourceObj;

			//** build a list of all the objects given (omit other datatypes...)
			for (var i = 1; i < arguments.length; i++)
				if (typeof (arguments[i]) === 'object')
				objs.push(arguments[i]);

			//** add each property to the combined object, backwards, to ensure objects added first override duplicate members
			for (var i = objs.length; i >= 0; i--)
				for (var prop in objs[i]) {
				curObj = objs[i][prop];

				if (curObj) {
					switch (typeof (curObj)) {
						case 'object':
							//** handle an array
							if ($_.Defined(curObj, "length")) {
								var sourceAr = sourceObj[prop];

								//** if the source doesn't have a prop that is an array by that name, or the prop isn't an array, create it
								if (sourceAr == null || !$_.Defined(sourceAr, "length"))
									sourceAr = [];

								//** add each target item to the end of the source items list
								for (var j = 0; j < curObj.length; j++)
									sourceAr.push(curObj[j]);
							} else {
								//** handle an object
								if (!$_.Defined(curObj, "nodeType"))
									sourceObj[prop] = $_.Merge(sourceObj[prop], curObj);
							}
							break;
						default:
							//** handle functions and primitives
							sourceObj[prop] = curObj;
							break;
					}
				}
			}

			return sourceObj;
		},		

		/*
		| RegisterComponent("the foobar component", "ic0n.Foobar", {...});
		| ------
		|   - registers a component by name with the framework, initializes its namespace, and defaults its prototype to the given object
		*/
		RegisterComponent: function(name, ns, arRequires, comp) {
			var newComp = null;

			//** make sure the component doesn't already exist
			for (var i = 0; i < _components.length; i++) {
				if (_components[i].ComponentInfo.Name == name) {
					alert("[ic0n.RegisterComponent] Component \"" + name + "\" already exists, registration cancelled");
					return;
				}
			}

			//** create our new component
			newComp = comp.ObjectId ? comp : this.Object(comp);

			//** verify/set the component info
			if (!newComp.ComponentInfo) {
				newComp.ComponentInfo = {
					Name: name,
					Namespace: ns,
					Version: newComp.Version || 1.0
				};

				//** if the component defines a set of required namespaces, add them to the component info
				if (arRequires)
					newComp.ComponentInfo.RequiredNamespaces = arRequires;
			}

			//** verify at least the name and namespace are present
			if (!newComp.ComponentInfo.Name || !newComp.ComponentInfo.Namespace) {
				alert("[ic0n.RegisterComponent] The ComponentInfo for \"" + name + "\" must have Name and Namespace defined");
				return null;
			}

			//** initialize the namespace 
			this.Namespace(ns, true, newComp);

			//** add it to the collection
			_components.push(newComp);

			//** fire the component registered event
			root.EventFlow.DispatchEvent(root.Event.ComponentRegistered);

			return newComp;
		},


		RegisterEventFlowComponent: function(name, ns, arRequires, comp, arEvents) {
			var newComp = null;

			//** add our necessary default events for a component
			arEvents = arEvents || [];
			arEvents.push("Initialize");
			arEvents.push("Load");
			arEvents.push("Error");

			//** create our event flow object
			newComp = this.EventFlowObject(comp, arEvents);

			//** register it as a component
			newComp = this.RegisterComponent(name, ns, arRequires, newComp);

			//** give the default throw error implementation
			newComp.ThrowError = function(strError, obj) {
				this.EventFlow.DispatchEvent(this.Event.Error, { Error: strError, Source: obj});
			}

			//** connect our root eventflow to the component
			root.EventFlow.Connect(newComp);

			return newComp;
		},

		/*
		| Namespace("ic0n.Foo.Bar")
		| ------
		|   - adds the given object at the specified namespace to the root library.  this allows the framework to decide where
		|     to put the components (a namespace) instead of relying on the developer to decide the components location
		*/
		Namespace: function(name, bCreate, obj) {
			var arX, ns = ic0n;

			if (!bCreate) bCreate = false;
			if (!obj) obj = null;

			//** validate...
			if (name === "" || name[0] == "." || name[name.length - 1] == ".") {
				alert("[ic0n.Namespace] Invalid namespace encountered \"" + name + "\"");
				return;
			}

			//** are they asking for the root (for some reason...) give it to em
			if (name == "ic0n")
				return ns;

			//** cleanse any root ns references
			name = name.indexOf("ic0n.") === 0 ? name.replace("ic0n.", "") : name;

			//** return the namespace, returning if found, unless we're not creating as we go...
			arX = name.split('.');
			for (var i = 0; i < arX.length; i++) {
				if (!ns[arX[i]]) {
					if (bCreate)
						ns[arX[i]] = i == arX.length - 1 && obj ? obj : {};
					else
						return null;
				}

				ns = ns[arX[i]];
			}

			return ns;
		},

		/*
		| AddHandler(window, "load", {...});
		| ------
		|   - adds a handler for the event on the given object. this overrides the implementation and doesn't add itself as an event listener, use AddListener to do so.
		*/
		AddHandler: function(obj, name, func) {
			if (typeof ($_dom) !== 'undefined')
				obj = $_dom.ResolveId(obj);

			if (!obj) return false;

			if (name.toLowerCase().substr(0, 2) != "on")
				name = "on" + name;

			obj[name] = function(e) {
				e = e || window.event;
				return func(e, obj);
			}

			return true;
		},

		/*
		| AddListener(window, "load", {...});
		| ------
		|   - adds the event/listener from the given object.  this handles dom events only, use the AddListener method on the object itself to listen to a specific event...
		*/
		AddListener: function(obj, name, func) {
			if (typeof ($_dom) !== 'undefined')
				obj = $_dom.ResolveId(obj);

			if (!obj) return false;

			if ((obj instanceof Array) && obj.length > 0) {
				$_.Each(obj, function(item) {
					this.AddListener(item, name, func);
				});
			} else {
				//** otherwise, use standard event handling
				if (window.addEventListener)
					obj.addEventListener(name, func, false);
				else
					obj.attachEvent("on" + name, func);				
			}

			return true;
		},


		Listener: function(obj, name, func) {
			var evt = null;

			//** resolve an id reference
			if (typeof ($_dom) !== 'undefined')
				obj = $_dom.ResolveId(obj);

			if (!obj) return evt;

			if ((obj instanceof Array) && obj.length > 0) {
				$_.Each(obj, function(item) {
					$_.Listener(item, name, func);
				});
			} else {
				//** assign the function an id
				func.Id = $_.GenerateId();

				//** create the proxy function
				evt = function(e) { 
					e = e||(window.event||{});

					//** modify the event object
					e.RemoveListener = function() {
						$_.RemoveListener(obj, name, evt);
					}

					//** call the listener function with the modified event obj
					func.call(obj, e); 
				}

				//** cache our proxy as a listener by id
				if(!obj.Listeners) obj.Listeners = {};
				obj.Listeners[func.Id] = evt;

				//** otherwise, use standard event handling
				if (window.addEventListener)
					obj.addEventListener(name, evt, false);
				else
					obj.attachEvent("on" + name, evt);				
			}

			return evt;
		},

		/*
		| RemoveListener(window, "load", {...});
		| ------
		|   - removes the event/listener from the given object
		*/
		RemoveListener: function(obj, name, func) {
			if (!obj) return;

			//** resolve an id reference
			if (typeof ($_dom) !== 'undefined')
				obj = $_dom.ResolveId(obj);

			//** if we've recieved the actual function and not the listener proxy, get the proxy
			if($_.Defined(func, "Id")) {
				if(obj.Listeners[func.Id])
					func = obj.Listeners[func.Id];
			}

			if ((obj instanceof Array) && obj.length > 0) {
				$_.Each(obj, function(item) {
					this.RemoveListener(item, name, func);
				});
			} else {
				if (window.removeEventListener)
					obj.removeEventListener(name, func, false);
				else
					obj.detachEvent("on" + name, func);
			}
		},


		/*
		| CancelEvent(e);
		| ------
		|   - cancels the given event, preventing further listeners from firing
		*/
		CancelEvent: function(e) {
			//** get our event object 
			if (!e) e = window.event;

			//** cancel event bubbling
			e.cancelBubble = true;

			//** if supported, use w3c method for halting propgation of the captured event
			if (e.stopPropagation)
				e.stopPropagation();
		},

		/*
		| Object()
		| ------
		|   - creates a unique reference to an individual framework object with an objectid
		*/
		Object: function(obj) {
			//** create a basic object with an incremented id
			function DefaultObject() {
				if (typeof (obj.ObjectId) === 'undefined')
					this.ObjectId = root.GenerateId();
			};

			//** if a object prototype was passed in, use it
			if (obj && typeof (obj) === 'object') {
				DefaultObject.prototype = obj;
			}

			//** give back an instance...
			return new DefaultObject();
		},

		/*
		| EventFlowObject
		| ------
		|   - extends the Object method to create a framework object that takes place in the event framework; generic event subscription, eventflow, event listening
		*/
		EventFlowObject: function(obj) {
			//** create our basic object
			if (!obj || (obj && !obj.ObjectId))
				obj = this.Object(obj);

			function EventFlowObject() {
				var _that = this;

				//** a collection of events this object exposes, and a collection of handlers for those events
				this.Event = {};
				this.Events = {};

				/*
				| EventFlow
				| ------
				|   - each object has an event flow, which is a pattern i use to define continuous stream of events fired by an object and its eventhandlers.  the object has a 
				|	 default catch all event defined, HandleEventFlow(), which the object can use to "listen" to what its going thru its flow, reacting on target events or objects.  
				|	 also, the event flows of objects can be connected in a one or two-way relationship; objects can literally be "plugged in" to the eventflow
				|	 of any object they want, with the ability to react to any event they "hear".  this is useful in broadcasting events up a chain of many listeners, without all
				|	 the overhead of complex coding. an example of eventflow chaining:
				|
				|	 var x, y, z;
				|	 x = ic0n.EventFlowObject();
				|	 y = ic0n.EventFlowObject();
				|	 z = ic0n.EventFlowObject();
				|
				|	 //** connect the eventflows in a specific order
				|	 y.EventFlow.Connect(x);
				|	 z.EventFlow.Connect(y);
				|	 z.HandleEventFlow = function() {
				|	   //** do something on some event from any child eventflows; x or y
				|	 };
				|
				|	 //** z now has the ability to react to both of these function calls (using its HandleEventFlow), before x and y do themselves...
				|	 x.SomeEvent();
				|	 y.SomeOtherEvent();
				|
				|	 //** z could have also listened to these events via explicit listening
				|	 x.AddListener(x.Event.SomeEvent, z.OnSomeEvent);
				|	 or
				|	 x.AddListener(x.Event.SomeEvent, function(){ z.OnSomeEvent(); });
				*/
				this.EventFlow = {
					ParentObject: _that,
					Listeners: {},
					Connections: [],

					/*
					| DispatchEvent({Event:someEvent,Source:this}) //** eventflow chains expect this format
					|   or
					| DispatchEvent(this.Event.Something, this) //** called from the source object
					|   or
					| DispatchEvent(this.Event.Something) //** called from the source object, with "this" implied
					| ------
					|   - dispatches the target event of this object into the objects eventflow, and up its chain of listeners and connections
					*/
					DispatchEvent: function(oEvent, oEventArgs, lstnr) {
						var arEvents, oArgs = oEvent;

						if (!oEvent) {
							alert("[ic0n.EventFlowObject.DispatchEvent] Dispatched event is undefined");
							return;
						}

						//** verify our eventargs are in order
						if (!oArgs.EventArgs) {
							oArgs = {
								Event: oEvent,
								EventArgs: oEventArgs || "",
								ReturnValue: null,
								Source: this.ParentObject,
								ListenerHandle:lstnr, //** if this method was invoked via a listener added to an object itself, there will also be a listener handle
								Cancelled: false,
								Cancel: function() {
									this.Cancelled = true;
								},

								//** define a callback collection that listeners can add handlers to that will run *after* the event is complete
								Callbacks: [],
								AddCallback: function(cb) {
									if (cb && typeof(cb) === 'function')
										this.Callbacks.push(cb);
								}
							};
						}

						/* 
						begin dispatching events.  the current heirarchy of event dispatching goes like this:
						1) first, broadcast the event through the local event flow stream, which by default is handled by HandleEventFlow(), allowing the object a first chance to modify the event before its handed over to its listeners;
						2) second to connected event flows, giving them a chance to alter the event before its hits any listeners
						3) third, fire the explicit event listeners, such as those defined with obj.AddListener(obj.Event.SomeEvent, someFunc());
						4) finally, if there is a registered event handler, fire it
						*/

						function endFlow(ef, args) {
							//** if this eventflow is the source eventflow, the event is finished, so fire any event callbacks
							if (args.Source.ObjectId == ef.ParentObject.ObjectId) {
								if (args.Callbacks.length > 0)
									for (var i = 0; i < args.Callbacks.length; i++)
										args.ReturnValue = args.Callbacks[i](args);
							}

							return args.ReturnValue;
						}


						//** 1) broadcast through the local eventflow
						if (this.ParentObject.HandleEventFlow)
							oArgs.ReturnValue = this.ParentObject.HandleEventFlow(oArgs);

						if(oArgs.Cancelled)
							return endFlow(this, oArgs);

						//** 2) dispatch to connected eventflows if the event supports broadcasting
						var lst = null;
						if (this.Listeners && oArgs.Event.Broadcast) {
							for (var id in this.Listeners) {
								lst = this.Listeners[id];

								//** make sure the listener is for this object
								if (lst.TargetObject.ObjectId != this.ParentObject.ObjectId)
									continue;

								//** if the connected entity's eventflow supports event dispatching, fire this event up the chain
								if (lst.SourceEventFlow.DispatchEvent) {
									oArgs.ReturnValue = lst.SourceEventFlow.DispatchEvent(oArgs);

									if(oArgs.Cancelled)
										return endFlow(this, oArgs);
								}
							}
						}


						//** 3) if theres a registered OnX eventhandler, fire it
						if (this.ParentObject.ObjectId == oArgs.Source.ObjectId) {
							if (this.ParentObject["On" + oArgs.Event.Name])
								oArgs.ReturnValue = this.ParentObject["On" + oArgs.Event.Name](oArgs);
						}

						if(oArgs.Cancelled)
							return endFlow(this, oArgs);

						//** 4) if this is an event of this object, dispatch direct listeners to that event on this object
						arEvents = this.ParentObject.Events[oArgs.Event.ObjectId];

						if (arEvents) {
							//** fire each listener, passing the listener handle as its argument so the dev has access to the source object, and so they can call <lhandle>.Disconnect() if they want to stop listening
							for (var i = 0; i < arEvents.length; i++) {
								if (arEvents[i]) {
									//** pass the listener handle and the event args
									oArgs.ReturnValue = arEvents[i].Listener(oArgs, arEvents[i]);

									//** if the method was flagged to not repeat, remove it after it has occurred once
									if (arEvents[i].Repeat === false)
										this.ParentObject.RemoveListener(arEvents[i]);

									//** if this listener cancelled the flow, end it
									if(oArgs.Cancelled)
										return endFlow(this, oArgs);
								}
							}
						}

						return endFlow(this, oArgs);
					},

					/*
					| Connect(someEntity)
					| ------
					|   - connects this objects eventflow to the target entity's eventflow.  this basically allows this object to "listen" to the eventflow of the target entity
					*/
					Connect: function(targetObj) {
						if (!targetObj || !targetObj.EventFlow)
							return;

						var ef = targetObj.EventFlow || targetObj;

						//** disallow an object to connect to itself
						if (targetObj.ObjectId === this.ParentObject.ObjectId)
							return;

						//** dont double connect(override) the existing conn
						if (ef.Listeners[this.ParentObject.ObjectId])
							return;

						//** create our object which encapsulates the event and listner method
						var oCHandle = root.Object({
							SourceObject: this.ParentObject,
							SourceEventFlow: this,
							TargetObject: targetObj,
							TargetEventFlow: targetObj.EventFlow,
							Disconnect: function() {
								this.SourceEventFlow.Disconnect(targetObj.EventFlow);
							}
						});

						//** add ourselves as a listener to the target objects eventflow
						ef.Listeners[oCHandle.SourceObject.ObjectId] = oCHandle;

						//** add the connection handle to our list of connections
						this.Connections.push(oCHandle);

						return oCHandle;
					},

					/*
					| Disconnect(someEntity)
					| ------
					|   - disconnects from the given entity's eventflow.  stops "listening" to its eventflow...
					*/
					Disconnect: function(targetObj) {
						var arX = {};
						var oEf, oCon;

						//** get the eventflow of the target object
						oEf = targetObj.EventFlow || targetObj;

						//** parse the list of eventflow listeners, and omit this object from the list if its there
						for (var oId in oEf.Listeners)
							if (typeof (oEf.Listeners[oId]) === 'object' && oId != this.ParentObject.ObjectId)
							arX[oId] = oEf.Listeners[oId];

						//** reset the listener list
						oEf.Listeners = arX;

						//** remove the connection from our list
						arX = [];
						for (var i = 0; i < this.Connections.length; i++) {
							oCon = this.Connections[i];

							if (oCon.TargetObject.ObjectId != oEf.ParentObject.ObjectId)
								arX.push(oCon);

						}

						this.Connections = arX;
					},

					/*
					| DisconnectListeners()
					| ------
					|   - disconnects from all listening eventflows
					*/
					DisconnectListeners: function(listener) {
						var obj;

						//** disconnect all listening eventflows
						for (var key in this.Listeners) {
							obj = this.Listeners[key];

							if (!obj) continue;

							//** disconnect the listener
							obj.Disconnect();

							//** fire the listener if given
							if (listener) listener(obj);
						}

						//** reset the listeners collection
						delete this.Listeners;
						this.Listeners = [];
					},

					/*
					| DisconnectConnections()
					| ------
					|   - disconnects from all connect eventflows
					*/
					DisconnectConnections: function(listener) {
						//** disconnect all connected eventflows
						for (var i = 0; i < this.Connections.length; i++) {
							this.Connections[i].Disconnect();

							//** fire the listener if given
							if (listener)
								listener(this.Connections[i]);
						}

						//** reset the connections collection
						delete this.Connections;
						this.Connections = [];
					},

					/*
					| DisconnectAll()
					| ------
					|   - disconnects from all connected *and* listening eventflows
					*/
					DisconnectAll: function(options) {
						options = options || {};

						//** disconnect the eventflow listeners
						this.DisconnectListeners(options.ListenerDisconnect);

						//** disconnect the connected eventflows
						this.DisconnectConnections(options.ConnectionDisconnect);
					}
				}

				this.RegisterEvent = function(event, bBroadcast) {
					event = event || "";

					//** create our event object
					var obj = event.Name ? event : {
						ObjectId: _that.ObjectId + "_" + event,
						Name: event,
						ParentObject: _that,
						Broadcast: bBroadcast
					};

					//** return if this event already exists
					if (_that.Event[obj.Name]) return;

					//** define a unique eventid if none defined
					if (typeof (obj.ObjectId) === 'undefined')
						obj.ObjectId = _that.ObjectId + "_" + obj.Name;

					//** make sure the parentObjectId is present
					if (typeof (obj.ParentObject) === 'undefined')
						obj.ParentObject = _that;

					//** define a broadcast member if none defined
					if (typeof (obj.Broadcast) === 'undefined')
						obj.Broadcast = true;

					//** make sure all events, manually defined or not, implement the same toString method
					obj.toString = function() { return obj.ObjectId; }

					//** define a shorthand event dispatch method which makes dispatching this event easier...
					if (typeof (obj.Dispatch) === 'undefined') {
						obj.Dispatch = function(args, hndl) {
							if (_that.Event[obj.Name])
								return _that.EventFlow.DispatchEvent(_that.Event[obj.Name], args || _that, hndl);
						};
					}

					//** define a shorthand AddListener method
					if (typeof (obj.AddListener) === 'undefined') {
						obj.AddListener = function(listener, repeat) {
							return _that.AddListener(obj, listener, repeat);
						};
					}

					//** define a shorthand RemoveListener method
					if (typeof (obj.RemoveListener) === 'undefined') {
						obj.RemoveListener = function(listener) {
							return _that.RemoveListener(obj, listener);
						};
					}

					//** register the event on our object
					_that.Event[obj.Name] = obj;

					//** register the shorthand first-level function that invokes the action
					_that[obj.Name] = function(args, hndl) { return obj.Dispatch(args, hndl); };
				}

				/*
				| RegisterEvents("Initialize", "Load", ["Drag", "Resize"]);
				| ------
				|   - registers an event with this object, allowing the object to expose it for listening. such as:
				|	   var x = ic0n.EventFlowObject();
				|	   x.RegisterEvents("Drag", "Resize");
				|	   x.AddListener(x.Event.Drag, {...});
				*/
				this.RegisterEvents = function() {
					if (arguments.length == 0)
						return;

					//** if its a single event, add it by name...if its a collection of events, add them each
					for (var i = 0; i < arguments.length; i++) {
						switch (typeof (arguments[i])) {
							case 'string':
								this.RegisterEvent(arguments[i], true);
								break;

							case 'object':
								//** if its an array, load its children
								if (arguments[i].length > 0) {
									for (var j = 0; j < arguments[i].length; j++)
										this.RegisterEvent(arguments[i][j], true);
								} else {
									//** otherwise, if its an event object, add it
									if (arguments[i].Name)
										this.RegisterEvent(arguments[i]);
								}
								break;
						}
					}
				}

				/*
				| AddListener(this.Event.Foobar, function() {...});
				| ------
				|   - adds a listener to the given event.  AddListener returns a ListenerHandle which can be used to remove the listener easily.
				*/
				if(!obj.Listener)
				    this.Listener = function(evt, hndle, repeat) {
				        this.AddListener(evt, hndle, repeat);
				    };
				if (!obj.AddListener)
					this.AddListener = function(evt, oListener, repeat) {
						//** if given the event name, find the event object by name
						if(typeof(evt) === 'string') {
							evt = this.Event[evt];
							if(!evt) return;
						}

						//** if we couldn't find an event, return
						if(!evt)
							return;

						//** make sure a collection for the listeners of this type of event exists
						if (!this.Events[evt.ObjectId])
							this.Events[evt.ObjectId] = [];

						//** create our object which encapsulates the event and listner method
						var handle = root.Object({
							Source: this,
							Event: evt,
							Repeat: repeat === null ? true : repeat,
							Listener: oListener,
							Disconnect: function() {
								this.Source.RemoveListener(this);
							}
						});

						//** add it to the listener stack, and return
						this.Events[evt.ObjectId].push(handle);

						return handle;
					}

				/*
				| RemoveListener(someLHandle)
				| ------
				|   - removes the listener defined by the listenerhandle 
				*/
				if (!obj.RemoveListener)
					this.RemoveListener = function(handle, func) {
						var arEvents, arNew = [];						

						//** if not given an eventhandle, make sure its an event; an example of this syntax would be:
						//** someObj.RemoveListener("click", myHandler); where myHandler is a function reference
						if (!$_.Defined(handle, "Event") || !handle.Event) {
						    var evt = null;
						    
						    //** if we weren't given the func callback, and this is an event name, there's no way we can determine what to disconnect, so exit
						    if(!func) return;
						
							//** if given the event name, find the event object by name
							if(typeof(handle) === 'string')
								evt = this.Event[handle];

							//** if we couldn't find the event by name, and its still null, return
							if(!evt) return;

							//** now find the listener handle for the event/handler combo
							var events = this.Events[evt.ObjectId];
							for (var i = 0; i < events.length; i++)
								if (events[i].Listener == func)
								    handle = events[i];

							//** if we didn't find a handle, return
							if (!handle) return;
						}

						//** get the collection of listeners for this event
						arEvents = this.Events[handle.Event.ObjectId];

						if (!arEvents)
							return;

						//** if there's only one listener for this event, delete the collection altogether
						if (arEvents.length == 1) {
							delete this.Events[handle.Event.ObjectId];
							return;
						}

						//** rebuild the list of listeners, minus our removed one
						for (var i = 0; i < arEvents.length; i++)
							if (arEvents[i].ObjectId != handle.ObjectId)
							arNew.push(arEvents[i]);

						//** set the new collection...
						this.Events[handle.Event.ObjectId] = arNew;
					}
			}

			//** if there's a default object implementation, set it as the prototype
			if (obj && typeof (obj) === 'object')
				EventFlowObject.prototype = obj;

			//** instantiate our new object
			var efObj = new EventFlowObject();

			//** if a list of events to register is tagging along with the call, handle em
			if (arguments.length > 0) {
				for (var i = (typeof (arguments[0]) === 'string' ? 0 : 1); i < arguments.length; i++)
					efObj.RegisterEvents(arguments[i]);
			}

			return efObj;
		}
	};

	//** create our root ic0n object from the object literal defined above, by passing that ol to its own EventFlowObject method.  also, define the events the ic0n object will expose
	$_ = root = root.EventFlowObject(root, "Initialize", "ComponentRegistered");

	//** when the parent object loads, dispatch the initialize event
	root.AddListener(parentObj, "load", function() {
		root.EventFlow.DispatchEvent(root.Event.Initialize);
	});

	return root;
}(window);
/*
ic0n.DateTime
------
*/
if(ic0n) {
ic0n.RegisterEventFlowComponent("ic0n Date-Time Component", "ic0n.DateTime", [], function() {

return {
	Days: [
		{ Name: "Sunday", Abbr: "Sun", ShortAbbr: "Su" },
		{ Name: "Monday", Abbr: "Mon", ShortAbbr: "M" },
		{ Name: "Tuesday", Abbr: "Tue", ShortAbbr: "T" },
		{ Name: "Wednesday", Abbr: "Wed", ShortAbbr: "W" },
		{ Name: "Thursday", Abbr: "Thu", ShortAbbr: "Th" },
		{ Name: "Friday", Abbr: "Fri", ShortAbbr: "F" },
		{ Name: "Saturday", Abbr: "Sat", ShortAbbr: "S" }
	],

	Months: [
		{ Name: "January", Abbr: "Jan", Days: 31 },
		{ Name: "February", Abbr: "Feb", Days: 28 },
		{ Name: "March", Abbr: "Mar", Days: 31 },
		{ Name: "April", Abbr: "Apr", Days: 30 },
		{ Name: "May", Abbr: "May", Days: 31 },
		{ Name: "June", Abbr: "Jun", Days: 30 },
		{ Name: "July", Abbr: "Jul", Days: 31 },
		{ Name: "August", Abbr: "Aug", Days: 31 },
		{ Name: "September", Abbr: "Sep", Days: 30 },
		{ Name: "October", Abbr: "Oct", Days: 31 },
		{ Name: "November", Abbr: "Nov", Days: 30 },
		{ Name: "December", Abbr: "Dec", Days: 31 }
	],

	Now: function() {
		//** return a datetime object representing "now"
		return $_date.DateTime();
	},

	DateTime:function(date) {
		var date = date||new Date();	
		
		function dtTod(date) {
			return new Date(date.Year, date.Month.Number, date.Day.Number, date.Hour, date.Minute, date.Second, date.Millisecond);
		}
		
		//** see if the date string is a "json date"
		var d = /\/Date\((\d*?)\)\// .exec(date.toString());
		if(d && d.length > 0)
			date = new Date(parseInt(d[1]));

		//** if this is a datetime object, get its date object
		if($_.Defined(date, "Date"))
			date = dtTod(date);
			
		if(!date) return;
		
		//** create the datetime object
		var datetime = $_.Object({
			Date: null,
			Hour: null,
			Minute: null,
			Second: null,
			Millisecond: null,
			Ticks: null,
			Day: {},
			Month: {},
			Year: null,

			toString: function() {
				return this.Month.Number + "/" + this.Day.Number + "/" + this.Year;
			},
			
			Load:function(d) {
				d = d||dtTod(this);

				//** get the day and month objects
				var dnum = d.getDay();
				var mnum = d.getMonth();		
				var dayofweek = $_date.Days[dnum];
				var month = $_date.Months[mnum];
		
				//** set the properties of the datetime object from the given date
				this.Date = d;
				this.Hour = d.getHours();
				this.Minute = d.getMinutes();
				this.Second = d.getSeconds();
				this.Millisecond = d.getMilliseconds();
				this.Ticks = d.getTime();
				this.Year = d.getFullYear();
				
				//** populate the date object
				this.Day = {
					Name: dayofweek.Name,
					Abbr: dayofweek.Abbr,
					ShortAbbr: dayofweek.ShortAbbr,
					Number: d.getDate(),
					DayNumber: dnum
				};
				
				//** populate the month object
				this.Month = {
					Name: month.Name,
					Abbr: month.Abbr,
					Days: (month.Days==28&&(d.getFullYear()-2000)%4==0)?29:month.Days, //** accounts for leapyear
					Number: mnum	
				};

				return this;
			},

			SetDay:function(num) {
				this.Day.Number = parseInt(num);
				return this.Load();
			},

			AddDays:function(num) {
				this.Day.Number += parseInt(num);
				return this.Load();   
			},

			SetMonth:function(num) {
				this.Month.Number = parseInt(num);
				return this.Load();
			},

			AddMonth:function(num) {
				this.Day.Number = 1;
				this.Month.Number += parseInt(num);
				return this.Load();   
			},
			
			AddYears:function(num) {
				this.Year += parseInt(num);
				return this.Load();   
			}
		});
		
		//** load the given date and return;
		datetime.Load(date);
		return datetime;
	},

	FirstDayOfMonth:function(date) {
		date = $_date.DateTime(date);

		//** subtract the number of days from the day's ordinal, then add weeks until we're at or above 0
		var day = date.Day.DayNumber - (date.Day.Number-1);
		while(day <= -1) day += 7;

		return day;
	},

	NumberSuffix:function(num) {
		if(num === null) return;
		num = num.toString().replace(/[^0-9]/,"");

		var last = parseInt(num[num.length-1]);
		var prev = num.length>1?parseInt(num[num.length-2]):0;

		//** if it ends with a 1, and its not preceeded by a 1, its "st"
		if(last == 1 && prev != 1)
			return "st";
		else
		//** if it ends with a 2, and its not preceeded by a 1, its "nd"
		if(last == 2 && prev != 1)
			return "nd";
		else
		//** if it ends with a 3, and its not preceeded by a 1, its "rd"
		if(last == 3 && prev != 1)
			return "rd";

		//** everything else is just "th"
		return "th";
	},

	Calendar:function(parent, options) {
		//** init/default the options
		options = options||{};
		options.Id = options.Id||"cal_"+ $_.GenerateId();
		options.Date = options.Date||$_date.Now();
		options.RenderOnInit = $_.Defined(options, "RenderOnInit")?options.RenderOnInit:true;
		options.UseArrows = typeof(options.UseArrows)==='undefined'?false:options.UseArrows;

		//** create the calendar object
		var cal = $_.EventFlowObject({
			//** properties
			Id: options.Id,
			Initialized: false,
			Title: options.Title||null,
			CurrentDate: options.Date,
			DaysMatrix: [],

			//** objects
			Element: options.Element||null,
			TitleElem: null,
			PrevMonthElem: null,
			NextMonthElem: null,


			//** event handlers
			OnInitialize:function() {
				if(this.Initialized)
					return;
					
				//** create the root calendar object if none given
				if(!this.Element)
					this.Element = $_dom.Elem("div", "calendar");

				//** create the title row
				var title = $_dom.Elem("div", "month-title", null, this.Element);

				this.NextMonthElem = $_dom.Elem("a", "next-month", (options.UseArrows)?"&gt;":"", title); //** Float Right
				this.PrevMonthElem = $_dom.Elem("a", "prev-month", (options.UseArrows)?"&lt;":"", title); //** Float Left
				this.TitleElem = $_dom.Elem("a", "title", null, title); //** No Float

				//** wire up the next/prev month handlers
				this.NextMonthElem.Click(this.NextMonth);
				this.PrevMonthElem.Click(this.PreviousMonth);
		
				//** create the day titles row
				var dow = $_dom.Elem("div", "day-titles", null, this.Element);
				for(var i=0;i<7;i++)
					$_dom.Elem("a", "day", $_date.Days[i].ShortAbbr, dow);

				//** create our "matrix" of days
				var week = null;
				var day = null;
				for(var i=0;i<6;i++) {
					this.DaysMatrix.push([]);
					week = $_dom.Elem("div", "week", null, this.Element);

					for(var d=0;d<7;d++) {
						//** create the day element and wire its click event
						day = $_dom.Elem("a", {"omn_key":omnkey}, {className:"day"}, week);
						day.Click(function(obj) { 
							return function() { cal.DaySelected(obj); };
						}(day));

						//** add a helper function and add the day to the days matrix
						day.GetDayNumber = function(obj) { return function() { return parseInt(obj.innerHTML); };}(day);
						this.DaysMatrix[i].push(day);
					}
				}

				//** flag the calendar as initialized
				this.Initialized = true;

				//** if the calendar is configured to render itself after its initialized, do so now
				if(options.RenderOnInit === true)
					this.Render();
			},

			OnRender:function() {
				//** set the title
				$_dom.SetContent(this.TitleElem, this.Title||this.CurrentDate.Month.Name +" - "+ this.CurrentDate.Year);

				//** get the first and last day of this month
				var first = $_date.FirstDayOfMonth(this.CurrentDate);
				var last = first + this.CurrentDate.Month.Days;
				var idx = -1;
				var day = null;
				var datetoday = new Date();
				var daytoday = datetoday.getDate();
				var monthtoday = datetoday.getMonth();			

				//** render each day of the month
				for(var w = 0;w<this.DaysMatrix.length;w++) {
					for(var d = 0;d<this.DaysMatrix[w].length;d++) {
						day = this.DaysMatrix[w][d];

						//** clear and remove any disabled state
						$_dom.Clear(day);
						$_dom.RemoveClass(day, "disabled");
						day.Disabled = false;

						//** if its out of range, disable it; otherwise, render the day number
						if(++idx<first || idx >= last) {
							$_dom.AddClass(day, "disabled");
							day.Disabled = true;
						} else
							$_dom.SetContent(day, (idx-first)+1);
						
						// Now, check date. If before today, then disable clicks.
						//debugger;
						if (this.CurrentDate.Month.Number < monthtoday || (this.CurrentDate.Month.Number == monthtoday && (idx-first)+1 < daytoday)) {
							$_dom.RemoveClass(day, "disabled");
							$_dom.RemoveClass(day, "day");
							$_dom.AddClass(day, "inactivedate");
							day.href = "javascript:void(0);";
							day.click = undefined;
							day.Disabled = true;
						} else {
							$_dom.RemoveClass(day, "inactivedate");
							$_dom.RemoveClass(day, "day");
							$_dom.AddClass(day, "day");
						}
						
						//** fire the dayrender event
						this.DayRender(day);
					}
				}
			},

			OnNextMonth:function(e, obj) {
				//** increment the month and rerender the calendar
				this.CurrentDate.AddMonth(1);				
				this.Render();

				//** fire any event listeners
				if($_.Defined(options, "OnNextMonth"))
					options.OnNextMonth(this, d);
			},

			OnPreviousMonth:function(e, obj) {
				//** decrement the month and re-render the calendar
				this.CurrentDate.AddMonth(-1);
				this.Render();

				//** fire any event listeners
				if($_.Defined(options, "OnPreviousMonth"))
					options.OnPreviousMonth(this, d);
			},

			OnDaySelected:function(e) {
				var day = e.EventArgs;
			
				if(!day.Disabled) {
					//** set the selected date
					this.CurrentDate.SetDay(day.GetDayNumber());
			
					//** fire any event listeners
					if($_.Defined(options, "OnDaySelected"))
						options.OnDaySelected(this, e);
				}
			},
			
			OnDayRender:function(e) {				
				if($_.Defined(options, "OnDayRender"))
					options.OnDayRender(this, e);
			}

		}, ["Initialize", "Render", "NextMonth", "PreviousMonth", "DaySelected", {Name:"DayRender", Broadcast:false}]);

		//** initialize the calendar object
		cal.Initialize();

		//** add the calendar to the parent if one is given
		parent = $_dom.ResolveId(parent);
		if(parent)			
			parent.appendChild(cal.Element);

		//** fire the root component's calendar created event
		$_date.CalendarCreated(cal);

		return cal;
	}
};
}(), ["CalendarCreated"]);

$_date = ic0n.DateTime;
}
/*
ic0n.Ajax
------
  - has a simple ajax manager class and other methods to encapsulate all ajax communication

  author
  ------
    - mike macmillan(mikejmacmillan@gmail.com)
*/
if(ic0n) {
ic0n.RegisterEventFlowComponent("ic0n Ajax Manager Component", "ic0n.Ajax", [], function() {
	//** the request queue and queue collections are abstracted to a closure to prevent manual "meddling" with the request queue.  
	var _requests = [];
	var _queuedRequests = [];

	function _createAjaxObject() {
		var xmlhttp;

		try {
			if(window.XMLHttpRequest) {
					xmlhttp = new XMLHttpRequest();
			} else {
				xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
			}
		} catch(e) {
			//** throw the ajax error...
			alert("[ic0n.Ajax.createAjaxObject] Ajax object creation failed");

			//** let the framework know there was a major component error
			this.EventFlow.DispatchEvent(this.Event.Error);
		}

		return xmlhttp;
	}

	function _processRequestQueue() {
		var ajaxReq, oReq;

		if($_ajax.Requests.length==0)
			return;

		for(var i=0;i<$_ajax.MaxRequests;i++) {
			if(i==$_ajax.Requests.length) return;

			if($_ajax.Requests.length==0) return; 

			//** get the current ajax object
			ajaxReq = $_ajax.Requests[i];

			if(!ajaxReq) return;

			if(ajaxReq.Status==$_ajax.Status.Pending) {
				//** remove the request from the queue
				try {
					delete $_ajax.QueuedRequests[ajaxReq.ObjectId];
				} catch(e) {
					if ($_url.Querystring('firebug')) {
						fbl(e);
					}
				};

				//** if the request isn't already being executed for a thread, and we are actually threading, start the request on a thread
				if(!ajaxReq.Thread && $_ajax.Threading && $_ajax.Threading.ThreadPools.length>0) {
					//** start the request on the next thread from any threadpool
					$_threading.StartRequestThread(ajaxReq);
				} else {
					//** mark its status as sending
					ajaxReq.Status = $_ajax.Status.Sending;

					//** set the ajax request object and originating ajax component (domain security policy enforcement helper)
					ajaxReq.Request = _createAjaxObject();
					ajaxReq.OriginatorId = $_ajax.ObjectId;

					//** handle its readystate with the global handle response method
					ajaxReq.Request.onreadystatechange = function() {
						return function() {
							$_ajax.HandleResponse(ajaxReq);
						}
					}();

					//** open the ajax request
					ajaxReq.Request.open(ajaxReq.RequestType, ajaxReq.Url, ajaxReq.Async);
	
					//** if this is a form post, ensure the encoding
					if(ajaxReq.RequestType===$_.Ajax.RequestType.Post)
	      					ajaxReq.Request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
	
					//** send it off...
					ajaxReq.Request.send(ajaxReq.Data||"");
				}
			}
		}
	}

	function _parseJson(responseText) {
		var iPasses=0;

		try {
			var json = responseText;
			while(typeof(json) === 'string' && iPasses<=3) {
				json = eval("("+ json +")");
				iPasses++;
			}

			return json;
		} catch(e) {
			return null;
		}
	}

	//** recursively builds a post/get data string from the given object
	function _buildRequestData(obj) {
		var data = "";

		for(var prop in obj) {
			if(obj[prop] == null) continue;

			//** if the object isn't an array
			if(typeof(obj[prop]) === 'object' && !obj[prop].length && !obj[prop].nodeType)
				data += (data==""?"":"&") + _buildRequestData(obj[prop]);
			else
				data += (data==""?"":"&") + prop +"="+ obj[prop];
		}

		return data;
	}

	/*
	function _enableLogging() {
		if(!$_.Ajax.LogWriter) {
			$_.Ajax.LogWriter = $_.Logging.CreateLogWriter(null, {Title:"Ajax EventFlow Log"});
			
			//** if the logwriter's element is a window...
			if($_.Ajax.LogWriter.Element.WindowState) {
				var win = $_.Ajax.LogWriter.Element;

				//** clear the desktop's logwriter when the window is closed
				win.Event.Close.AddListener(function() {
					$_.Ajax.LogWriter = null;
				});
			}
		}

		//** begin logging to the shared ajax logwriter
		$_.Ajax.Logger.Enabled = true;
		$_.Ajax.Logger.LogTo($_.Ajax.LogWriter.Writer);
	}
	*/

	return {
		//** default enums for our request types, states, and status
		Requests:[],
		QueuedRequests:[],
		ServiceClients:{},
		RequestType:{
			Post:"POST", 
			Get:"GET", 
			Head:"HEAD"
		},
	
		ReadyState:{
			NotInitialized:0, 
			NotSent:1, 
			Sent:2, 
			Partial:3, 
			Complete:4
		},
	
		Status:{
			Pending:"pending", 
			Sending:"sending", 
			Error:"error", 
			Complete:"complete"
		},

		MaxRequests:2,

		EnableLogging: false,
		LogWriter: null,

		CreateServiceClient:function(strServiceUrl, arClientData) {
			function makeRequest(client, requestType, url, data, cb) {
				//** get the base url
				var url = client.BuildUrl(url);

				//** get the postData
				var data = client.BuildRequestData(data);

				//** make our callback
				var oNewCallback = function(oReq) {
					//** parse the response
					var json = _parseJson(oReq.ResponseText);

					//** return the json output
					if(cb)
						cb(json);
				};

				//** fire off the post
				$_ajax.SendRequest(requestType, url, data, oNewCallback);
			}

			//** create the service client
			var client = {
				ClientData: arClientData||{},
				ServiceUrl: strServiceUrl||"",
	
				AddClientData:function(strKey, strValue) {
					if(!this.ClientData[strKey])
						this.ClientData[strKey] == strValue;
				},
	
				BuildUrl: function(strUrl) {
					//** determine the base url
					var url = "";

					//** append the service url
					url += ($_text.BeginsWith(this.ServiceUrl, "/")?"":"/") + this.ServiceUrl;

					//** building the final url
					strUrl = url + ($_text.BeginsWith(strUrl, "/")?"":"/") + strUrl + ($_text.EndsWith(strUrl, "/")?"":"/");
	
					return strUrl;
				},

				BuildRequestData:function(data) {
					var data = "";

					//** first add any client data
					data  = _buildRequestData(this.ClientData);

					//** then add any data passed in
					if(data)
						data += "&"+ _buildRequestData(data);

					return data;
				},
	
				Post:function(strUrl, data, cb) {
					//** make a post request
					makeRequest(this, $_ajax.RequestType.Post, strUrl, data, cb);
				},
	
				Get:function(strUrl, cb) {
					//** make a get request
					makeRequest(this, $_ajax.RequestType.Get, strUrl, null, cb);
				}
			};

			//** persist our service client
			this.ServiceClients[client.ServiceUrl] = client;

			return client;
		},

		GetServiceClient:function(strServiceUrl) {
			return this.ServiceClients[strServiceUrl];
		},
	
		/*
		 | CreateRequest()
		 | ------
		 |   - 
		*/
		CreateRequest:function(type, strUrl, data) {
			var request;

			//** prototype the ajax request object
			request = $_.EventFlowObject(function() {
				return {
					DataCache:{}, //** collection of data(objects, strings, etc) to cache with the request object
					RequestType:type||$_ajax.RequestType.Post,
					Callbacks:[],
					Async:true,
					Request:null,
					Url:strUrl,
					Data:data||null,
					ResponseText:"",
					HttpStatusCode:0,
					OriginatorId:null,
					Status:$_ajax.Status.Pending, ///** simple status of the object...
					ReadyState:$_ajax.ReadyState.NotInitialized,

					AddCallback:function(oMethod) {
						this.Callbacks.push(oMethod);
					},

					ExecuteCallbacks:function() {
						//** if the request isn't complete, dont execute the callbacks
						if(this.Status!=$_ajax.Status.Complete)
							return;

						//** execute each callback, passing this object to the handler
						for(var i=0;i<this.Callbacks.length;i++)
							if(this.Callbacks[i])
								this.Callbacks[i](this);
					},

					BuildRequestData:function() {
						//** if the data is already a string, return it
						if(typeof(this.Data) === 'string')
							return this.Data;

						//** otherwise build our post data from the data object
						return _buildRequestData(this.Data);
					},

					/*
					 | Send("foo=bar&blah=foo")
					 | ------
					 |   - asynchronously initiates the request.  if the max number of requests are already initiated,
					 |     this request will be queued, and processed immediately when a connection free's up.
					*/
					Send:function(data) {
						var that = this;

						//** include a random number in the url to help prevent caching
						if(this.Url.indexOf("rand=") == -1)
							this.Url += (this.Url.indexOf("?")==-1?"?":"&") +"rand="+ (new Date()*1);

						//** use the passed in data if given
						this.Data = data||this.Data;

						//** finalize the request data
						this.Data = this.BuildRequestData();

						//** if the get request passed querystring data via json, add it to the url
						if(this.Data != "" && this.RequestType == $_ajax.RequestType.Get)
							this.Url += "&"+ this.Data;

						//** fire the object off as is, letting the framework handle the sending process
						this.Event.SendRequest.Dispatch(this);
					},

					Json:function() { return this.ParseJson(); },
					ParseJson:function() {
						//** if there is a valid response, of length, parse it as JSON
						if(this.ResponseText && this.ResponseText.length > 0)
							return _parseJson(this.ResponseText);

						return "";
					}
				};
			}(), "SendRequest");

			//** add the request to the queue
			this.QueuedRequests[request.ObjectId] = request;

			//** connect the eventflow to the main eventflow
			this.EventFlow.Connect(request);

			//** dispatch the create request event...
			this.RequestCreated(request);

			return request;
		},

		/*
		 | Post(url, data, callback)
		 | ------
		 |   - a shorthanded call for SendRequest(Post...)
		*/
		Post:function(url, data, callback) {
			if(!url || !data) return;

			this.SendRequest(this.RequestType.Post, url, data, callback);
		},

		/*
		 | Get(url, data, callback)
		 | ------
		 |   - a shorthanded call for SendRequest(Get...)
		*/
		Get:function(url, data, callback) {
			if(!url) return;

			//** if the user used the format Get(url, callback), assign the callback
			if(typeof(data) === 'function') {
				callback = data;
				data = null;
			}

			this.SendRequest(this.RequestType.Get, url, data, callback);
		},


		/*
		 | SendRequest()
		 | ------
		 |   - 
		*/
		SendRequest:function(type, strUrl, data, cb) {
			var req;

			//** create the ajax request
			req = this.CreateRequest(type, strUrl);

			//** add the callback
			if(cb)
				req.AddCallback(cb);

			//** initiate the request
			req.Send(data);
		}, 

		/*
		 | HandleRequest()
		 | ------
		 |   - processes the request queue; handles spawning the request and load balancing based on maxrequests
		*/
		HandleRequest:function(args) {
			var ajaxReq, oReq;

			//** grab the request from the eventargs
			ajaxReq = args.EventArgs;

			//** do sum preliminary validation
			if(!ajaxReq) {
				this.ThrowError("[ic0n.Ajax.HandleRequest] Invalid request object encountered", oReq);
				return;
			}

			//** throw it on the request stack
			this.Requests.push(ajaxReq);

			//** process the queue
			_processRequestQueue();
		},

		/*
		 | HandleResponse()
		 | ------
		 |   -
		*/
		HandleResponse:function(ajaxRequest) {
			//** update the ajaxRequest object
			if(ajaxRequest.OriginatorId==$_.Ajax.ObjectId)
				ajaxRequest.ReadyState = ajaxRequest.Request.readyState;

			if(ajaxRequest.ReadyState==$_ajax.ReadyState.Complete) {
				var arReqs = [];

				//** mark the ajax request complete
				if(ajaxRequest.OriginatorId==this.ObjectId) {
					ajaxRequest.ReadyState = this.ReadyState.Complete;
					ajaxRequest.Status = this.Status.Complete;
					ajaxRequest.ResponseText = ajaxRequest.Request.responseText;
				}

				//** remove the request from the request queue
				for(var i=0;i<this.MaxRequests;i++) {
					//** if this isn't our request (they came back out of order), temporarily take it off the stack
					if(this.Requests[0].ObjectId!=ajaxRequest.ObjectId)
						arReqs.unshift(this.Requests.shift());
					else {
						//** otherwise, get rid of it...
						this.Requests.shift();

						//** and add any requests we took off the stack, back on
						var c = arReqs.length;
						for(var j=0;j<c;j++)
							if(arReqs[0].ObjectId!=ajaxRequest.ObjectId)
								this.Requests.unshift(arReqs.shift());

						break;
					}
				}

				//** if this request is being executed on a thread spawned from a threadmanager higher in the heirarchy, do 
				//** not perform the end of response actions, rather, pass it to its thread's threadpool's threadcomplete event
				if(ajaxRequest.Thread) {
					var pool = ajaxRequest.Thread.ThreadPool;

					//** if the thread originated somewhere besides this threading component, pass it to its parent threadpool
					if(!$_.Ajax.Threading || pool.ThreadManager.ObjectId != $_.Ajax.Threading.ObjectId) {
						ajaxRequest.Thread.Event.Complete.Dispatch(ajaxRequest.Thread);
						return;
					}
				}

				//** before we execute any callbacks, fire off the next ajax request in the queue if there is one...
				setTimeout(function() {
				    _processRequestQueue();
			    	}, 0);

				//** execute the callbacks
				ajaxRequest.ExecuteCallbacks();

				//** dispatch the complete event to the eventflow...
				this.RequestComplete(ajaxRequest);

				//** being the request is complete, detach the ajaxrequest from the ajax eventflow
				this.EventFlow.Disconnect(ajaxRequest);
			}
		},

		UploadFile:function(form, args) {
			var oDiv, frameObj, strId, strResponse;

			//** create the upload frame
			frameObj = $_controls.CreateIframe($_.GenerateId());

			//** set its initial properties
			frameObj.src = args.BlankPage||"about:blank";
			frameObj.style.display = "none";

			//** hookup the listeners
			if(args.PrepareUpload)
				args.PrepareUpload(form, frameObj, args);

			$_.Listener(frameObj, "load", function(e) {
				var oDoc, response = "";

				//** make sure we have access to the iframe
				try {
					var x = frameObj.contentWindow.document.domain;
				} catch(e) {
					return;
				}

				//** nullify the target..."resetting" the form
				form.target = null;

				//** get the upload response; the developer needs to implement the GetUploadResponse method in the iframe
				var response = frameObj.contentWindow.GetUploadResponse();

				//** persist the response with the args
 				args.Response = response;

				//** if there was a listener, fire it
				if(args.UploadComplete)
					args.UploadComplete(response);
			});

			//** add the iframe to the page
			form.appendChild(frameObj);

			//** reset the forms target, and let the form submit
			form.target = frameObj.id;

			return true;
		},

		OnInitialize:function() {

			/*
			//** enable logging for this component, in silent mode by default, with an adjusted buffer size
			if(ic0n.Logging) {
				$_.Logging.EnableLogging(this, {
					Enabled: false, 
					BufferSize: 100,
					MessageBuilder: _logMessageBuilder
				});
	
				//** if the windowing component exists, wait for the desktop to be created before registering the patterns, connecting logs, etc...
				if(ic0n.Controls.Windows) {
					$_.Controls.Windows.Event.DesktopCreated.AddListener(function() {
						//** if logging is enabled on the ajax component, create the writer for the logger
						if($_.Ajax.EnableLogging === true)
							_enableLogging();
	
						//** register a key pattern to enable ajax logging
						$_.Logging.ListenForPattern(17, "enableajaxlogging", function() {
							_enableLogging();
							alert("[ic0n.Ajax] Logging Enabled");
						});
					});
				}
			}
			*/
		},
		
		ParseJson:function(jsonText) {
			return _parseJson(jsonText);
		},

		HandleEventFlow:function(args) {
			switch(args.Event.Name.toLowerCase()) {
				//** listen for ajax requests that are being sent
				case "sendrequest":
					//** dispatch the ajax request to the handleRequest method to allow it to participate in the threadpooling/throttling functionality
					this.HandleRequest(args);
					break
			}
		}
	};
}(), ["RequestCreated", "RequestComplete"]);

$_ajax = ic0n.Ajax;
}
/*
ic0n.Dom
------
- 

author
------
- mike macmillan(mikejmacmillan@gmail.com)
*/
if (ic0n) {
    ic0n.RegisterComponent("ic0n DOM Component", "ic0n.Dom", [], function() {
        //** list of common html element events; im not purposely handling the onabort, onsubmit, onchange, and onreset events
        var domEvents = ["Click", "DblClick", "Focus", "Blur", "MouseOver", "MouseOut", "MouseMove", "MouseDown", "MouseUp", "KeyDown", "KeyUp", "KeyPress"];

        function getChildNodes(parent, selectFunc, filterFunc) {
            var elem = null;
            var cnodes = null;
            var nodes = [];

            //** resolve the parent reference to get the root nodelist
            parent = getParent(parent, true);

            if (!$_.Defined(parent, "childNodes")) return nodes;
            if (!parent) return nodes;
            if (!selectFunc)
                selectFunc = function(item) { return item; }

            for (var i = 0; i < parent.childNodes.length; i++) {
                elem = parent.childNodes[i];

                //** run our item through the filter function, adding it to the nodelist if passed
                if (selectFunc(elem)) {
                    //** if a filter function was defined, use it as well to select the node(s)
                    if (filterFunc != null) {
                        if (filterFunc(elem) === true)
                            nodes.push(domObj(elem));
                    } else
                        nodes.push(domObj(elem));
                }

                //** if it has children, find if they have any elements with the given tag
                if (elem.childNodes.length > 0) {
                    cnodes = getChildNodes(elem, selectFunc, filterFunc);

                    //** add all the nodes we found to the collection
                    if (cnodes != null) {
                        if (typeof (cnodes.length) !== "undefined")
                        //** if its an array of elements, add them all
                            for (var j = 0; j < cnodes.length; j++)
                            nodes.push(cnodes[j]);
                        else
                        //** otherwise just add the single found node
                            nodes.push(cnodes);
                    }
                }
            }

            //** if only a single element is returned, return just the single object rather than an array
            if (nodes.length > 0) {
                if (nodes.length == 1)
                    nodes = nodes[0];

                return nodes;
            }

            return null;
        }

        function getParent(obj, bodyElement) {
            //** if the object passed in a dom object or at least its id?
            obj = $_dom.ResolveId(obj);

            //** if we didn't get a dom object as the parent...
            if (!obj) {
                //** start with the document root
                var doc = document;

                //** if this is an iframe, get *its* document root
                if (window.frameElement)
                    doc = window.frameElement.contentWindow.document;

                //** return the document root or its body element depending
                obj = bodyElement ? document.body : document;
            }

            return obj;
        }

        function domObj(obj) {
            if (obj == null) return;

            //** attach a handler for each of the main dom events for an element
            $_.Each(domEvents, function(evt) {
                obj[evt] = function(cb) {
                    return $_.Listener(obj, evt.toLowerCase(), cb);
                }
            });

            //** attach a method to remove any listener from the object directly
            obj.RemoveListener = function(evt, cb) {
                $_.RemoveListener(obj, evt.toLowerCase(), cb);
            };

            obj.Style = function(name, value) {
                if (!name) return;

                //** use-case 1) just get a style by name, from the computed styles if possible
                if (!value)
                    return $_dom.GetStyle(obj, name);
                else
                //** use-case 2) set the value if present
                    $_dom.SetStyle(obj, name, value);
            };

            return obj;
        }


        return {


            /* Get(this, function(item) { return item.tagName == "h2"; });
            | ------
            |   - open ended function for filtering the dom through a user defined algorithm
            */
            Get: function(parent, filter) {
                if (filter == null) return;
                return getChildNodes(parent, null, filter);
            },

            /*
            | ById("divFoobar" [, arsomething])
            | ------
            - shorthand to document.getElementById.  if a second argument is passed, if its a dom node, its childNodes are used, otherwise, its assumed to be a collection of elements, and its searched for the element by id. 
            */
            ById: function(id, parent) {
                if (!id || id.length == 0) return;

                //** if no parent given, use the root
                if (!parent)
                    return domObj(document.getElementById(id));

                //** find all elements of the given tagname
                var nodes = getChildNodes(parent, function(elem) {
                    if ($_.Defined(elem, "id") && elem.id == id)
                        return true;
                });

                return nodes;
            },

            /*
            | ByTag("div")
            | ------
            - shorthand to document.getElementsByTagName
            */
            ByTag: function(tag, parent, filter) {
                var nodes = null;

                if (!tag || tag.length == 0) return;

                //** if no parent/starting element was given, use the built in dom method to return elements by tagname from the root node
                if (!parent) {
                    //** get the document root
                    parent = getParent(parent);

                    //** use the dom method
                    nodes = parent.getElementsByTagName(tag);

                    //** filter the results if a filter was given
                    if (filter) {
                        var filtered = [];
                        $_.Each(nodes, function(item) {
                            if (filter(item))
                                filtered.push(domObj(item));
                        });

                        nodes = filtered;
                    }
                } else {
                    //** find all elements of the given tagname
                    nodes = getChildNodes(parent, function(elem) {
                        if ($_.Defined(elem, "tagName") && elem.tagName.toLowerCase() == tag)
                            return true;
                    }, filter);
                }

                //** return the matched nodes
                return nodes;
            },

            /*
            | ByName("txtfooboar", someelem)
            | ------
            |   - finds an element by the given name recursively starting at the starting element, or document.body if none specified
            */
            ByName: function(name, parent, filter) {
                if (!name || name.length == 0) return;

                //** find all elements of the given name
                var nodes = getChildNodes(parent, function(elem) {
                    if ($_.Defined(elem, "name") && elem.name.toLowerCase() == name)
                        return true;
                }, filter);

                //** return the matched nodes
                return nodes;
            },

            /*
            | ByClass(".foobar", someobj) / ByClass(".foobar", "divFoobar", function() { filter stuff, return true }
            | ------
            |   - finds elements by the given class recursively starting at the starting element, or document.body if none specified
            */
            ByClass: function(cssclass, parent, filter) {
                if (!cssclass) return;

                //** find all elements of the given classname
                var classname = null;
                var nodes = getChildNodes(parent, function(elem) {
                    classname = $_.Defined(elem, "className") ? elem.className : "";
                    if (classname.length > 0 && classname.indexOf(cssclass) != -1)
                        return true;
                }, filter);

                //** return the matched nodes
                return nodes;
            },

            /*
            | GetPosition(someElem);
            | ------
            |   - returns the position for the given element.  if a stop element is defined, position calculation will stop when
            |	 that element is encountered.  a case for that might be calculating the position within a certain element.
            */
            GetPosition: function(elem, options) {
                var oT;
                var iL = 0, iT = 0;
                var scrollPos = null; //** used for scrolling

                options = options || {};
                elem = $_.Dom.ResolveId(elem);

                //** if a stop element was defined, resolve any id reference
                if (options.StopElement)
                    options.StopElement = $_.Dom.ResolveId(options.StopElement);

                //** calculate the position based on the offset tree
                oT = elem;
                while (oT != null) {
                    //** if we've hit the element to stop at, break before we include its offsets
                    if (options.StopElement)
                        if (oT == options.StopElement)
                        break;

                    //** aggregate the offsets
                    if (typeof (oT.offsetLeft) !== 'undefined') {
                        iL += oT.offsetLeft;
                        iT += oT.offsetTop;
                    }

                    //** if we're calculating just the offsets, only the offset parents must be traversed
                    oT = oT.offsetParent;
                }

                //** get the scroll position if instructed to do so
                if (options.ScrollPosition)
                    scrollPos = $_.Dom.GetScrollPosition(elem);

                //** return its coordinates, along with a reference to itself
                return {
                    Element: elem,
                    X: iL,
                    Y: iT,
                    Height: elem.offsetHeight,
                    Width: elem.offsetWidth,
                    Top: iT,
                    Right: iL + elem.offsetWidth,
                    Bottom: iT + elem.offsetHeight,
                    Left: iL,
                    ScrollLeft: scrollPos ? scrollPos.Left : 0,
                    ScrollTop: scrollPos ? scrollPos.Top : 0
                };
            },


            /*
            | GetScrollPosition(someElem);
            | ------
            |   - traverses the given elements node tree, returning the first parent that defines a scrolltop/scrollleft. 
            */
            GetScrollPosition: function(elem) {
                var oT;
                var iX = 0, iY = 0;

                elem = $_.Dom.ResolveId(elem);

                //** calculate the position based on the offset tree
                oT = elem;
                while (oT != null) {
                    //** if we're calculating the scroll offsets, each node must be traversed
                    if (typeof (oT.scrollTop) !== 'undefined') {
                        iY += oT.scrollTop;
                        iX += oT.scrollLeft;

                        //** break after we get the first scroll coords
                        if (iX > 0 || iY > 0)
                            break;
                    }

                    oT = oT.parentNode;
                }

                //** return the scroll coordinates
                return {
                    Left: iX,
                    Top: iY
                };
            },

            SetPosition: function(obj, pos, parent) {
                var parentPos;

                obj = $_.Dom.ResolveId(obj);

                if (!obj || !pos) return;

                if (parent) parentPos = this.GetPosition(obj);

                function getSize(size) {
                    return size.toString().indexOf("%") != -1 ? size : parseInt(size) + "px";
                }

                if (!$_.Empty(pos.Left))
                    obj.style.left = getSize(pos.Left);

                if (!$_.Empty(pos.Top))
                    obj.style.top = getSize(pos.Top);

                if (!$_.Empty(pos.Width))
                    obj.style.width = getSize(pos.Width);

                if (!$_.Empty(pos.Height))
                    obj.style.height = getSize(pos.Height);
            },

            IsChild: function(obj, parent, stopElem) {
                //** if we have no arguments, return
                if (!obj || !parent) return false;

                //** if we're comparing an object to itself, return true
                if (obj == parent) return true;

                //** determine our stop element
                stopElem = stopElem || document.body;

                //** traverse the node tree, looking for the parent
                var temp = obj;
                while (temp != null) {
                    //** if we hit the stop element, we didn't find our parent, so return false
                    if (temp == stopElem)
                        return false;

                    //** if we hit our target element, return true
                    if (temp.parentNode == parent)
                        return true;

                    //** otherwise, move up the tree...
                    temp = temp.parentNode;
                }

                return false;
            },

            SetClass: function(obj, className) {
                if (!obj) return;

                //** resolve an id reference
                if (typeof (obj) === 'string')
                    obj = $_dom.ById(obj);

                //** if we didn't resolve an object, return
                if (typeof (obj) !== 'object')
                    return;

                //** set the objects css class
                obj.className = className;
            },

            /*
            | AddClass
            */
            AddClass: function(el, className) {
                //-- if no el, no can do
                if (!el) {
                    return;
                }

                //-- resolver		
                el = this.ResolveId(el);

                //-- if the class doesn't exist, add it
                if (!this.HasClass(el, className)) {
                    el.className += ' ' + className;
                }
            },

            /*
            | RemoveClass
            */
            RemoveClass: function(el, className) {
                //-- if no el, no can do
                if (!el) {
                    return;
                }

                //-- resolver		
                el = this.ResolveId(el);

                var re;
                if (this.HasClass(el, className)) {
                    re = new RegExp('(\\s|^)' + className + '(\\s|$)');
                    el.className = el.className.replace(re, '');
                }
            },

            /*
            | ReplaceClass
            */
            ReplaceClass: function(el, oldClassName, newClassName) {
                //-- if no el, no can do
                if (!el) {
                    return;
                }

                //-- resolver		
                el = this.ResolveId(el);

                this.RemoveClass(el, oldClassName);
                this.AddClass(el, newClassName);
            },

            /*
            | HasClass
            */
            HasClass: function(el, className) {
                // if errors appear in this function, turn this on: alert(el + '|' + className);
                //-- if no el, no can do
                if (!el) {
                    return false;
                }

                //-- resolver		
                el = this.ResolveId(el);

                return el.className.match(new RegExp('(\\s|^)' + className + '(\\s|$)'));
            },

            /*
            | ResolveId
            | ------
            |   - given an object, this method will determine if the object is a string id reference to the object.  if so, this
            |	 method will overwrite the id reference with the actual object.  perhaps use this to allow methods to accept 
            |	 both object references or their ids, increasing the usability
            */
            ResolveId: function(object) {
                if (typeof (object) == 'string') {
                    object = this.ById(object);
                } else {
                    if (ic0n.Defined(object, "nodeType") && object.nodeType == 1)
                        object = domObj(object);
                }

                return object;
            },

            GetStyle: function(obj, style) {
                obj = $_dom.ResolveId(obj);
                if (!obj || !obj.style) return;

                //** first try returning the value from the computed style
                if (window.getComputedStyle)
                    return window.getComputedStyle(obj, null).getPropertyValue(style); //** moz/op
                else if (obj.currentStyle)
                    return obj.currentStyle[style]; //** ie

                return obj.style[style];
            },

            SetStyle: function(obj, style, value) {
                obj = $_dom.ResolveId(obj);
                if (!obj || !obj.style) return;

                if (typeof (style) == 'object') {
                    //** if the style is an object, enumerage and set all the properties
                    for (var prop in style)
                        obj.style[prop] = style[prop];
                } else
                //** if the style is a string, we're just setting a single attrib
                    obj.style[style] = value;

                return obj;
            },

            RemoveStyle: function(obj, style) {
                obj = $_dom.ResolveId(obj);
                if (!obj || !obj.style) return;

                obj.style[style] = "";
                return obj;
            },

            Visible: function(obj) {
                obj = this.ResolveId(obj);

                if (obj.style.display == "none")
                    return false;

                return true;
            },

            InFlow: function(obj) {
                obj = this.ResolveId(obj);

                if (this.GetStyle(obj, "position").indexOf("absolute") != -1 || this.GetStyle(obj, "position").indexOf("fixed") != -1)
                    return false;

                return true;
            },

            NextElement: function(obj) {
                obj = this.ResolveId(obj);
                var next = obj.nextSibling;
                if (!next) return;

                while (!next.tagName)
                    next = next.nextSibling;

                return next;
            },

            Check: function(obj, uncheck) {
                if (!obj) return;

                //** resolve id ref
                obj = $_.Dom.ResolveId(obj);
                if (!obj) return;

                //** check the object if it supports the property
                if (typeof (obj.checked) !== "undefined")
                    obj.checked = uncheck === true ? false : true;
            },

            Uncheck: function(obj) {
                //** short hand a ref to the Check overload
                $_.Dom.Check(obj, true);
            },

            IsChecked: function(obj) {
                if (!obj) return;

                //** resolve id ref
                obj = $_.Dom.ResolveId(obj);

                //** if the object supports the checked property, report its value
                if (typeof (obj.checked) !== "undefined")
                    return obj.checked === true;

                return false;
            },

            SelectOption: function(ddl, name, value) {
                name = (name || "").toString().toLowerCase();
                value = (value || "").toString().toLowerCase();
				ddl = $_dom.ResolveId(ddl);

                if (!ddl || ddl.options.length == 0)
                    return;

                //** select the option by name or value
                for (var i = 0; i < ddl.options.length; i++) {
                    if (ddl.options[i].text.toLowerCase() == name || ddl.options[i].value.toLowerCase() == value && value != '') {
                        ddl.options[i].selected = true;
                        break;
                    }
                }
            },

            GetOption: function(ddl, name) {
                name = (name || "").toLowerCase();
                ddl = $_.Dom.ResolveId(ddl);
                if (!ddl || typeof (ddl.options) === "undefined" || ddl.options.length == 0)
                    return;

                //** return the option by the given name, if specified
                if (name.length > 0) {
                    for (var i = 0; i < ddl.options.length; i++)
                        if (ddl.options[i].text.toLowerCase() == name)
                        return ddl.options[i];
                }

                //** otherwise, return the selected option
                return ddl.options[ddl.selectedIndex];
            },

            SetContent: function(obj, content, alt) {
                //** if no content given, or its blank, use the alternate content
                if (!content || content == "")
                    if (alt && alt.length > 0)
                    content = alt;

                //** make sure if the content object is null, it is interpreted as blank
                content = content || "";

                //** resolve an id reference
                obj = $_.Dom.ResolveId(obj);

                //** if we have no object and no content, quit
                if (!obj) return;


                //** set the objects content, using the alternate if the content is null
            	try {
            		if ($_.Defined(obj, "innerHTML"))
            			obj.innerHTML = content;
            	} catch (e) {}

            	try {
            		if ($_.Defined(obj, "value"))
                		obj.value = content;
                } catch (e) {}    
            },

            GetContent: function(obj) {
                //** resolve an id reference
                obj = $_.Dom.ResolveId(obj);

                //** if we have no object and no content, quit
                if (!obj) return;

                //** set the objects content, using the alternate if the content is null
                if (typeof (obj.value) !== "undefined")
                    return obj.value;
                
            	if (typeof (obj.innerHTML) !== "undefined")
                    return obj.innerHTML;

                return "";
            },

            /* 
            | InsertHtml(someDiv, "<b>some text</b>" [, "beforebegin"]);
            | ------
            - inserts an html string directly into the DOM of the element given, at the position 
            specificed by strPosition.  valid values for position are:
            - beforebegin
            - beforeend
            - afterbegin
            - afterend
            i created this method as my answer to the disambiguation problem for 
            IE's insertAdjacentHtml and FF's createContextualNode.
            */
            InsertHtml: function(obj, strHtml, strPosition) {
                if (document.attachEvent) {
                    //** for ie, simply wrap a call to insertAdjacentHtml
                    return function(obj, strHtml, strPosition) {
                        if (!strPosition)
                            strPosition = "afterend";

                        obj.insertAdjacentHTML(strPosition, strHtml);
                    }
                } else {
                    //** for firefox, create a text range, and select its contents (otherwise, it will error aut) 
                    return function(obj, strHtml, strPosition) {
                        var oRange, oFrag;

                        if (!strPosition)
                            strPosition = "afterend";

                        //** create a range for our new html, then get a text fragment of it
                        oRange = obj.ownerDocument.createRange();
                        oRange.selectNodeContents(obj);
                        oFrag = oRange.createContextualFragment(strHtml);

                        //** based on where they want to insert the content, do it...
                        switch (strPosition) {
                            case "beforebegin":
                                obj.insertBefore(oFrag, obj.firstChild);
                                break;

                            case "beforeend":
                                obj.insertBefore(oFrag, obj.lastChild);
                                break;

                            case "afterbegin":
                                obj.insertBefore(oFrag, obj.firstChild.nextSibling);
                                break;

                            case "afterend":
                                obj.appendChild(oFrag);
                                break;
                        }
                    }
                }
            } (),

            /*
            | Toggle("divFoobar" [, "inline"]);
            | ------
            - showing/hiding an object is a common function of any web app...this does that by toggling the display property
            */
            Toggle: function(obj, display) {
                //** get the element we want to toggle
                obj = $_dom.ResolveId(obj);

                if (!obj) return;

                if (display)
                //** if the user specifically sets the display type they want, use it
                    obj.style.display = display;
                else
                //** otherwise, toggle its display...
                    obj.style.display = obj.style.display == "none" || obj.style.display == "" ? "block" : "none";
            },

            /*
            | Focus("txtName");
            | ------
            |   - calls the focus method of the given element by id
            */
            Focus: function(obj) {
                obj = $_dom.ResolveId(obj);

                //** focus it
                if (obj) obj.focus();
            },

            /*
            | Center("someDiv" [, "divParent"]);
            | ------
            |	- centers the object within the given parent.  if no parent is given, its assumed to be the body.
            |	  with the body as the parent, this method centers objects on the page...
            */
            HCenter: function(obj, parent, position) {
                var pos, iDif = 0;

                if (typeof (obj) === 'string')
                    obj = this.ById(obj);

                if (typeof (position) === 'undefined')
                    position = true;

                //** if no parent is given, assume the body
                parent = parent || document.body;

                //** get our elements position
                pos = this.GetPosition(obj);

                //** calculate the area to width difference
                iDif = parent.clientWidth - pos.Width;

                //** set the obj's x to half the difference, thereby centering it
                var left = (iDif / 2 > 0 ? iDif / 2 : 0) + "px";

                if (position)
                    obj.style.left = left;

                return left;
            },

            VCenter: function(obj, parent, position) {
                var pos, iDif = 0, height = 0, yoffset = 0;

                if (typeof (obj) === 'string')
                    obj = this.ById(obj);

                if (typeof (position) === 'undefined')
                    position = true;

                //** if no parent is given, assume the body
                parent = parent || document.body;

                //** get our elements position
                pos = this.GetPosition(obj);

                //** calculate the area to height difference
                if (parent == document.body) {
                    if (window.innerHeight)
                    //** all but IE defines a window.innerHeight to get the window size
                        iDif = window.innerHeight - pos.Height;
                    else
                    //** use the documentElement.clientHeight with ie
                        iDif = document.documentElement.clientHeight - pos.Height;
                } else
                //** otherwise, just use the clientHeight of the element to determine height
                    iDif = parent.clientHeight - pos.Height;

                //** if the page is scrolled at all, get the offset from the top
                if (window.innerHeight)
                    yoffset = window.scrollY;
                else
                    yoffset = document.documentElement.scrollTop;

                //** set the obj's y to half the difference, thereby centering it
                var top = ((iDif / 2 > 0 ? iDif / 2 : 0) + yoffset) + "px";

                if (position)
                    obj.style.top = top;

                return top;
            },

            Center: function(obj, parent, position) {
                var left = $_dom.HCenter(obj, parent, position);
                var top = $_dom.VCenter(obj, parent, position);

                return { Left: left, Top: top };
            },

            /*
            | DisableSelect(someElem)
            | -----------------------
            |   - disables selecting text by dragging the mouse by overrideing the mousedown and onselectstart handlers, 
            |	 forcing them to return false
            */
            DisableSelect: function(obj) {
                obj = obj || document;

                //** disable the ability to select text (mousedown and drag over stuff) for the given element, or the document by default
                obj.onmousedown = function() { return false; }
                obj.onselectstart = function() { return false; }
            },

            /*
            | EnableSelect(someElem)
            | ----------------------
            |   - reenables selection by clearing any defined handlers for the methods overridden in DisableSelect
            */
            EnableSelect: function(obj) {
                obj = obj || document;

                //** re-enable selection by nullifying the event handlers we defined
                obj.onmousedown = null;
                obj.onselectstart = null;
            },

            IsScrollEnabled: function() {
                if (document.all && !$_.Defined(window, "XDomainRequest"))
                    return document.body.scroll == "yes";

                return document.body.style.overflow == "auto";
            },

            DisableBodyScroll: function() {
                //** disable body scrolling in ie and mozilla
                if (document.all && !$_.Defined(window, "XDomainRequest"))
                    document.body.scroll = "no";
                else
                    document.body.style.overflow = "hidden";
            },

            EnableBodyScroll: function() {
                //** disable body scrolling in ie and mozilla
                if (document.all && !$_.Defined(window, "XDomainRequest"))
                    document.body.scroll = "yes";
                else
                    document.body.style.overflow = "auto";
            },

			Enable: function(id, bdisable) {
				var obj = $_dom.ResolveId(id);
				if(!obj) return;

				bdisable = bdisable===true?true:false;
				enable = bdisable?false:true;

				if(document.all) obj.enabled = enable;
				else obj.disabled = !enable;
			},

			Disable: function(id) {
				$_dom.Enable(id, true);
			},

            OnEnterPress: function(e, func) {
                var code;

                //** get the right event object...
                if (!e) e = window.event;

                //** get the right keycode
                code = e.keyCode ? e.keyCode : e.which;

                //** make sure its enter
                if (code != 13) return true;

                //** run the function
                if (func) func();

                //** cancel the event invoked by hitting enter...
                $_.CancelEvent(e);
            },

            DefaultText: function(obj, txt) {
                if (!obj || !txt) return;
            	
                obj = $_.Dom.ResolveId(obj);
				if(!obj) return;

            	obj.Focus(def);
            	obj.Blur(def);
            	
				function def() {
					if (obj.value.toLowerCase() == txt.toLowerCase())
						obj.value = "";
					else
						if (obj.value == "")
							obj.value = txt;
				}
            },

            /*
            Elem("input", {type:"checkbox", id:"chkFoo"});
            ------
            creates an element of the given name, with either the given attributes, or properties.  if an attribute object is given, 
            then setAttribute will be called for each.  if a properties object is passed, then the each property will be set by
            name on the object
            */
            Elem: function(name, atts, props, parent) {
                function x(obj, prop, value) {
                    //** if there is a leading dot, remove it
                    if (prop.charAt(0) == ".")
                        prop = prop.substring(1, prop.length);

                    if (prop.indexOf(".") == -1)
                    //** if we've found the root, set the object value
                        obj[prop] = value;
                    else {
                        //** otherwise, dereference the obj another level and try again
                        var p = prop.split('.')[0];
                        x(obj[p], prop.replace(p, ""), value);
                    }
                }

                //** create the element
                var elem = document.createElement(name);


                if (atts) {
                    if (typeof (atts) === "string")
                    //** if the attributes collection is a string, assume its the css class
                        elem.className = atts;
                    else {
                        //** otherwise, assume its an attribute list and set it
                        for (var att in atts)
                            elem.setAttribute(att, atts[att]);
                    }
                }

                //** if properties were passed, set them too
                if (props) {
                    if (typeof (props) === "string")
                    //** if the properties collection is a string, assume its the innerHTML
                        elem.innerHTML = props;
                    else {
                        //** otherwise set each property
                        for (var prop in props) {
                            //** if there is a sub-notation ("style.height"), process it
                            if (prop.indexOf(".") != -1)
                                x(elem, prop, props[prop]);

                            //** otherwise, set as normal
                            else
                                elem[prop] = props[prop];
                        }
                    }
                }

                //** if a parent element was specified, add the new elem to the parent node
                var x = domObj(elem);
                if (parent && $_.Defined(parent, "appendChild"))
                    parent.appendChild(x);

                return x;
            },

            Clear: function(obj) {
                //** resolve an id reference
                obj = $_dom.ResolveId(obj);

                if (!obj) return;

                //** remove any child nodes
                while (obj.hasChildNodes() === true)
                    obj.removeChild(obj.childNodes[0]);
            }
        };
    } ());

	$_dom = ic0n.Dom;
}
/*
 | ic0n.Url
 | ------
 |   - a variety of url utility functions
 | 
 | author
 | ------
 |   - mike macmillan(mikejmacmillan@gmail.com)
*/
if(ic0n) {
ic0n.RegisterComponent("ic0n Url Utilities", "ic0n.Url", [], {
    Querystring:function(key) {
	    var qs = window.location.search;
	    var items = qs==""?[]:qs.split('&');
	    var t;

	    //** make sure there's even a querystring
	    if(items.length==0)
		    return "";

	    //** find the key/value pair by its key
	    for(var i=0;i<items.length;i++) {
		    if(items[i].indexOf('=')) {
			    t = items[i].split('=');
				t[0] = t[0].replace(/\?/g,'');
			    //** if found, return the value
			    if(t[0].toLowerCase()==key.toLowerCase())
				    return t[1];
		    }
	    }

	    //** ...returning null if none found
	    return null;
    },
    
    RemoveParam:function(url, paramname) {
        //** get the url, strip the param name if it exists        
        var gex = new RegExp("&?"+ paramname +"=([^&]*)");    
        if(url.indexOf(paramname) != -1)        
            url = url.replace(gex, "");
            
        return url;
    },
    
    AddParam:function(url, paramname, value) {
        //** first remove the parameter if it exists
        url = $_url.RemoveParam(url, paramname);
        url = url.replace('#','');        
        
        //** add the parameter to the url        
        url = url + (url.indexOf("?")==-1?"?":"&") + paramname +"="+ value;
        
        return url;
    }
});

$_url = ic0n.Url;
}
/*
 | ic0n.Animation
 | ------
 |   - handles all functions to do with animation.  the animation engine is based on the flash project Tweener,
 |     http://code.google.com/p/tweener/.  while a direct port, in some cases parts have been adapted/extended
 |     for javascript support.  the full suite of transitions have been ported as well, see comments below.
 | 
 | author
 | ------
 |   - mike macmillan(mikejmacmillan@gmail.com)
*/
if(ic0n) {
ic0n.RegisterComponent("ic0n Animation Component", "ic0n.Animation", ["ic0n.Dom"], {
	//** defines all paths of linear movement the animation component can handle
	Direction:{
		//** the *real* directions we use
		North:"north",
		NorthEast:"northeast",
		East:"east",
		SouthEast:"southeast",
		South:"south",
		SouthWest:"southwest",
		West:"west",
		NorthWest:"northwest",

		//** shorthands for alternate use
		Up:"north",
		Down:"south",
		Left:"west",
		Right:"east"
	},

	//** depending on the direction, clipstyle defines which side of the Distance gets clipped. ex, when moving down, if clipstyle is top, the part above the distance would be clipped...
	ClipStyle:{
		Top:"top",
		Right:"right",
		Bottom:"bottom",
		Left:"left"
	},


	//** shortcuts to different levels of fading
	FadeSetting:{
		Full:0,
		Half:.5,
		None:1.0
	},

	//** component level defaults
	Default:{
		Fps:50, //** the default number of frames per second the animation engine runs at
		FrameRate:1000/50, //** the interval at which the animation engine runs to achieve the frames per second required
		FrameInterval:25, //** in pre-calculated move operations, this is the interval at which the animation engine loops
		MoveIncrement:10, //** the default increment by which an element is moved in pre-calculated move operations
		FadeTime:1500, //** the default length of a fade
		LoopDelay:1000 //** the delay between end of the animation, and the repeat of the start, in a looped animation
	},

	//** flag representing whether the animation engine is running or not
	Running: false,

	//** collection of animations to run
	Animations:[],



	Start:function() {
		//** start the animation engine if its stopped
		if(!this.Running)
			this.Animate();
	},

	Pause:function() {
		if(!this.Running) return;

		//** pause each animation
		for(var i=0;i<this.Animations.length;i++)
			this.Animations[i].Pause();
	},

	Resume:function() {
		if(!this.Running) return;

		//** resume each animation
		for(var i=0;i<this.Animations.length;i++)
			this.Animations[i].Resume();
	},

	Stop:function() {
		//** simple reset the animations collection and flag the engine as not running
		this.Animations = [];
		this.Running = false;
	},


	Animate:function() {
		var anim, runTime, duration, current, member;

		//** get the current time in ticks
		var now = new Date()*1;

		function updateMembers(obj, transition) {
			var member, value;

			//** get the animations state
			var time = obj.GetRuntime(now);
			var dur = obj.GetDuration();

			//** update the value of each transition-member for the object given with the value given
			for(var i=0;i<obj.Members.length;i++) {
				member = obj.Members[i];

				//** get the current value for this point the timescale either using the transition algorithm given, or the expected end value
				member.Current = $_.Defined(transition)?transition(time, member.Start, member.Change, dur):member.End;

				//** persist the last used value against the member.  certain transition algorithms might update more than once, 
				//** but not actually change the value of the member they're updating (ex: exponential algo when close to zero).  by
				//** persisting the last used value, objects who handle update can know if the value has changed by comparing it to the
				//** last used value...
				member.Last = member.Current;

				//** lastly, set the member's value
				obj.Element[member.Name] = parseInt(member.Current) + member.Unit;

				//** fire the update event to allow listeners to handle the new value
				obj.Event.Update.Dispatch(member);
			}
		}

		//** if the engine has been stopped, dont run any animations
		if(!this.Animations || this.Animations.length == 0) return;

		//** in case of any outside updates, every iteration, update the engine status
		this.Running = true;

		//** iterate our animation objects and update them based on our current time
		for(var i=0;i<this.Animations.length;i++) {
			anim = this.Animations[i];

			//** if the current animation is stopped, remove it and continue
			if(anim.Stopped) {
				this.Animations.splice(i, 1);
				continue;
			}

			//** if the current animation is paused, skip it
			if(anim.Paused) continue;

			//** if the animation is complete, remove our animation object from the collection
			if(anim.IsComplete(now)) {
				//** update the animation with its end value
				updateMembers(anim);

				//** remove the animation from the stack
				this.Animations.splice(i, 1);

				//** finally, fire the complete event
				anim.Event.Complete.Dispatch(anim);
			} else {
				//** update the animation with the current value, given the timeline, and the transition algorith,
				updateMembers(anim, anim.Transition);
			}
		}

		if(this.Animations.length>0)
			//** if there are more animation objects on the stack, keep the animation loop running at the engines framerate
			setTimeout(function() { $_.Animation.Animate(); }, $_.Animation.Default.FrameRate);
		else
			//** otherwise, there is nothing left to animate, so flag the engine as not running
			this.Running = false;
	},

	CreateAnimation:function(obj, members, options) {
		var listeners, handlers;

		//** resolve any id reference to the object (ie, obj = 'divFoobar')
		obj = $_.Dom.ResolveId(obj);

		if(!obj || !members) return;
		options = options||{};

		//** if any listeners were given, grab them, and remove them from the options so they dont become part of the animation object
		if(typeof(options.Listeners) !== 'undefined') {
			listeners = options.Listeners;
			delete options.Listeners;
		}

		//** if any handlers were given, grab them, and remove them from the options so they dont become part of the animation object
		if(typeof(options.Handlers) !== 'undefined') {
			handlers = options.Handlers;
			delete options.Handlers;
		}

		//** if given a string name of a transition, see if there is a transition function for it
		if(typeof(options.Transition) === 'string')
			if($_.Animation.Transitions[options.Transition])
				options.Transition = $_.Animation.Transitions[options.Transition];

		//** verify we have a valid transition, falling to the default it not
		if(typeof(options.Transition) !== 'function')
			options.Transition = $_.Animation.Transitions.EaseOutExpo;

		//** create our animation object
		var anim = $_.EventFlowObject($_.Merge({
			Element:obj, //** the element or object that owns the members we're "animating"
			Members:[], //** the member properties that will be "animated"

			//** animation state settings
			AutoStart:$_.Defined(options.AutoStart)?options.AutoStart:true, //** when true, the animation is automatically started after AddAnimation is called
			Delay:0, //** when started, animation is delayed by this setting before starting
			Transition:$_.Animation.Transitions.EaseOutExpo, //** the transition algorithm for animation
			Duration:1000, //** how long the animation should last

			Paused:false, //** when true, the animation engine will skip this animation until false
			Running:false, //** boolean that indicates whether the animation is currently running or not
			Stopped:false, //** when true, the next iteration of the animation engine will remove this animation

			//** time measure settings for the animation loop
			StartTime:0,
			EndTime:0,
			PauseTime:0,

			//** calculates the total runtime so far, based off the time given
			GetRuntime:function(now) {
				now = now||new Date()*1;

				return parseInt(now) - parseInt(this.StartTime);
			},

			//** calculates the duration of the animation, in ticks
			GetDuration:function() {
				return parseInt(this.EndTime) - parseInt(this.StartTime);
			},

			//** determines if the animation is complete
			IsComplete:function(now) {
				now = now||new Date()*1;

				return this.GetRuntime(now) >= this.GetDuration();
			},

			AddMember:function(name, value, unit) {
				//** if the member doesn't exist, dont use it...
				//if(!$_.Defined(obj[name]))
					//return;

				//** get its current value, and the expected end value
				var start = parseInt(obj[name]);
				var end = parseInt(value);

				//** create the member object
				var member = {
					Name:name,
					Unit:unit||"px",
					Start:start,
					End:end,
					Change:parseInt(end - start),

					//** the current value being used to update the object, and the last value used...helps for eliminating unecessary updates
					Current:null,
					Last:null
				};

				//** add it to the animation
				this.Members.push(member);

				return member;
			},


			Start:function(args) {
				//** if the animation isn't already running, start it
				if(!this.Running && this.Members.length > 0)
					this.Event.Start.Dispatch(args);
			},

			OnStart:function(args) {
				//** no running on empty...
				if(!this.Members || this.Members.length == 0)
					return;

				//** add the animation to the collection, timestamp it, then initiate the animation loop if not otherwise
				setTimeout(function() {
					//** timestamp the animation in ticks for datetime comparison
					anim.StartTime = new Date()*1;
					anim.EndTime = anim.StartTime + anim.Duration;
		
					//** add it to the collection of animations
					$_.Animation.Animations.push(anim);

					//** flag the animation as running
					anim.Running = true;
					anim.Stopped = false;
		
					//** verify the animation engine is started
					$_.Animation.Start();
				}, anim.Delay);
			},

			Pause:function(args) {
				if(this.Paused) return;

				//** if the animation is running, pause it
				if(this.Running)
					this.Event.Pause.Dispatch(args);
			},

			OnPause:function(args) {
				//** flag the animation as paused and record the pause time
				this.Paused = true;
				this.PauseTime = new Date()*1;
			},

			Resume:function(args) {
				if(!this.Paused) return;

				//** if the animation is paused, resume it
				if(this.Running)
					this.Event.Resume.Dispatch(args);
			},

			OnResume:function(args) {
				var time = new Date()*1;

				//** unpause the animation
				this.Paused = false;

				//** update the start/end times to reflect the pause time
				this.StartTime += time - this.PauseTime;
				this.EndTime += time - this.PauseTime;

				//** reset the pause time
				this.PauseTime = 0;
			},

			Stop:function(args) {
				//** if the animation is running, stop it
				if(this.Running)
					this.Event.Stop.Dispatch(args);
			},

			OnStop:function(args) {
				//** flag this animation as stopped, marking it for deletion next time the animation engine sees it
				this.Stopped = true;
				this.Running = false;
				this.Paused = false;
			},


			OnComplete:function(args) {
				this.Stopped = false;
				this.Running = false;
				this.Paused = false;
			}

		}, options), [{Name:"Start", Broadcast:false}, {Name:"Update", Broadcast:false}, {Name:"Pause", Broadcast:false}, {Name:"Resume", Broadcast:false}, {Name:"Stop", Broadcast:false}, {Name:"Complete", Broadcast:false}]);
		//** create the animation object with 6 events: Start, Update, Pause, Resume, Stop, Complete

		//** add each member to the animation
		for(var prop in members)
			anim.AddMember(prop, members[prop]);

		//** wire up event listeners if given
		if(listeners) {
			//** add listeners for any events given that we can validate
			for(var evt in listeners) {
				if(anim.Event[evt])
					anim.AddListener(anim.Event[evt], listeners[evt]);
			}
		}

		//** wire up event handlers if given
		if(handlers) {
			for(var handler in handlers)
				anim[handler] = handlers[handler];
		}

		//** start the animation if necessary
		if(anim.AutoStart)
			anim.Start();

		return anim;
	},




	MovableObject:function(obj, options) {
		var anim = null;
		var loop = false;
	
		//** verify our object isn't a string
		obj = $_.Dom.ResolveId(obj);

		//** default our options object
		options = options||{};

		//** create our collection of queued movements
		var moveQ = [];

		function move() {
			if(!moveQ || moveQ.length == 0)
				return;

			//** get the next point from the queue
			var point = moveQ.shift();

			//** flag the object as moving
			moveObj.Moving = true;

			//** get the position of the item we're moving			
			var pos = $_.Dom.GetPosition(moveObj.Element, {StopElement:options.OffsetParent});

			//** verify our object's position; it not yet set, this ensures the animation calculates from the real position
			if(moveObj.Element.style.left.length == 0)
				$_dom.SetPosition(moveObj.Element, {Left: pos.Left});

			if(moveObj.Element.style.top.length == 0)
				$_dom.SetPosition(moveObj.Element, {Top: pos.Top});


			anim = null;
			if(point.Point) {
				var members = {};

				//** add all the members from the point
				if(point.Point.left)
					members.left = point.Point.left;

				if(point.Point.top)
					members.top = point.Point.top;

				//** create the animation
				anim = $_.Animation.CreateAnimation(moveObj.Element.style, members, point.Options);
			} else {
				//** handle the movement for the given direction
				switch(point.Direction.toLowerCase()) {
					case $_.Animation.Direction.Up:
						anim = $_.Animation.CreateAnimation(moveObj.Element.style, {top: pos.Top - point.Distance}, point.Options);
						break;
		
					case $_.Animation.Direction.Down:
						anim = $_.Animation.CreateAnimation(moveObj.Element.style, {top: pos.Top + point.Distance}, point.Options);
						break;
	
					case $_.Animation.Direction.Right:
						anim = $_.Animation.CreateAnimation(moveObj.Element.style, {left: pos.Left + point.Distance}, point.Options);
						break;
	
					case $_.Animation.Direction.Left:
						anim = $_.Animation.CreateAnimation(moveObj.Element.style, {left: pos.Left - point.Distance}, point.Options);
						break;
		
					//** uhh...where u goin?
					default:
						return;
				}
			}

			//** start the animation
			if(anim)
				anim.Start();

			//** if we're looping, push the point back into the move queue
			if(loop)
				moveQ.push(point);
		}

		//** create our "movable" eventflow object
		var moveObj = $_.EventFlowObject({
			Element: obj,
			Moving: false, //** the single state the movable object has; whether its moving or not

			MoveTo:function(dir, distance, opt) {
				//** add the movement point
				if(distance)
					this.AddPoint(dir, distance, opt);

				//** if the object isn't moving, initiate movement
				if(!this.Moving)
					move();
			},

			AddPoint:function(dir, distance, opt) {
				var point = null;

				if(!distance) return;
				opt = $_.Merge(opt||{}, options);

				//** see if the "overload" was used, ie AddPoint(null, {left: 40, top: 40});
				if(isNaN(parseInt(distance)) || distance.style || distance.left || distance.top) {
					var members = {};

					//** if the distance has a style, assume its an element, and use its position
					if(distance.style) {
						members.left = distance.style.left;
						members.top = distance.style.top;


					//** if the distance has a left or top member, assume its a specific point
					} else if(distance.left || distance.top) {
							if(distance.left)
								members.left = distance.left;

							if(distance.top)
								members.top = distance.top;
					} else {
						//** if we couldn't derive a numerical value from the distance, assume its the id of an element, and use the elements position
						if(isNaN(parseInt(distance))) {
							var elem = $_dom.ById(distance);
	
							if(!elem) return;
	
							//** set the point to the objects position
							members.left = elem.style.left;
							members.top = elem.style.top;
						}
					}

					//** create the point from the members and options
					point = { Point: members, Options: opt };
				} else {
					//** get the distance as a whole number
					distance = parseInt(distance);
	
					if(isNaN(distance)) return;

					//** create the movement point object
					point = { Direction: dir, Distance: distance, Options: opt };
				}

				//** if no transition was given for this movement, use the transition from the movable object
				if(!opt.Transition && options.Transition)
					opt.Transition = options.Transition;

				//** default the autostart option to false...always.  We want to control when the animation starts
				opt.AutoStart = false;

				//** when the animation stops, process the next point in the move queue
				opt.OnComplete = function(args) { moveObj.Moving = false; move(); }

				if(!moveQ)
					moveQ = [];

				//** add the point to the queue
				moveQ.push(point);

				return point;
			},

			Stop:function() {
				//** stop the current animation
				if(anim)
					anim.Stop();

				//** set the loop status to false
				loop = false;
			},

			Loop:function(delay) {
				//** set the loop status to true
				loop = true;

				if(typeof(delay) !== 'undefined' && delay !== null) {
					//** if a valid delay was given, set it for the object's loop
					delay = parseInt(delay);

					if(!isNaN(delay))
						options.Delay = delay;
				}

				//** initiate movement if not moving...
				if(!this.Moving)
					move();
			}
		}, {Name:"Move", Broadcast:false}, {Name:"Stop", Broadcast:false});
		//** setup a move and stop event only, no broadcast


		return moveObj;
	},



	/*
	 | InvertDirection()
	 | ------
	 |   - based on the given direction, return its opposite direction.  
	*/
	InvertDirection:function(oDir) {
		switch(oDir) {
		case $_.Animation.Direction.South:
			return $_.Animation.Direction.North;
			break;

		case $_.Animation.Direction.North:
			return $_.Animation.Direction.South;
			break;

		case $_.Animation.Direction.East:
			return $_.Animation.Direction.West;
			break;

		case $_.Animation.Direction.West:
			return $_.Animation.Direction.East;
			break;
		}
	},







	/*
	 | Transitions
	 | ------
	 |   - provides animation transition algorithms used for easing and movement patterns.  adapted from 
	 |     Tweener, http://code.google.com/p/tweener/.  original algorithms adapted from work by Robert Penner.
	 |     see http://hosted.zeh.com.br/tweener/docs/en-us/misc/transitions.html for visual examples of the effect
	 |     for each transition.
	*/
	Transitions: {
		EaseNone:function(t, b, c, d) {
			return c * t/d + b;
		},
	
	
		EaseInQuad:function(t, b, c, d) {
			return c * (t/=d) * t + b;
		},
	
		EaseOutQuad:function(t, b, c, d) {
			return -c * (t/=d) * (t-2) + b;
		},
	
		EaseInOutQuad:function(t, b, c, d) {
			if((t/=d/2) < 1) 
				return c/2 * t * t + b;
	
			return -c/2 * ((--t) * (t-2) - 1) + b;
		}, 
	
		EaseOutInQuad:function(t, b, c, d) {
			if(t < d/2)
				return $_.Animation.Transitions.EaseOutQuad(t*2, b, c/2, d);
	
			return $_.Animation.Transitions.EaseInQuad((t*2)-2, b+c/2, c/2, d);
		}, 
	
	
		EaseInCubic:function(t, b, c, d) {
			return c * (t/=d) * t * t + b;
		},
	
		EaseOutCubic:function(t, b, c, d) {
			return c * ((t=t/d-1) * t * t + 1) + b;
		},
	
		EaseInOutCubic:function(t, b, c, d) {
			if((t/=d/2) < 1) 
				return c/2 * t * t * t + b;
	
			return c/2 * ((t-=2) * t * t + 2) + b;
		}, 
	
		EaseOutInCubic:function(t, b, c, d) {
			if(t < d/2)
				return $_.Animation.Transitions.EaseOutCubic(t*2, b, c/2, d);
	
			return $_.Animation.Transitions.EaseInCubic((t*2)-d, b+c/2, c/2, d);
		}, 
	
	
		EaseInQuart:function(t, b, c, d) {
			return c * (t/=d) * t * t * t + b;
		},
	
		EaseOutQuart:function(t, b, c, d) {
			return -c * ((t=t/d-1) * t * t * t - 1) + b;
		},
	
		EaseInOutQuart:function(t, b, c, d) {
			if ((t/=d/2) < 1) 
				return c/2 * t * t * t * t + b;
	
			return -c/2 * ((t-=2) * t * t * t - 2) + b;
		}, 
	
		EaseOutInQuart:function(t, b, c, d) {
			if(t < d/2)
				return $_.Animation.Transitions.EaseOutQuart(t*2, b, c/2, d);
	
			return $_.Animation.Transitions.EaseInQuart((t*2)-d, b+c/2, c/2, d);
		}, 
	
	
		EaseInQuint:function(t, b, c, d) {
			return c * (t/=d) * t * t * t * t + b;
		},
	
		EaseOutQuint:function(t, b, c, d) {
			return c * ((t=t/d-1) * t * t * t * t + 1) + b;
		},
	
		EaseInOutQuint:function(t, b, c, d) {
			if ((t/=d/2) < 1) 
				return c/2 * t * t * t * t * t + b;
	
			return c/2 * ((t-=2) * t * t * t * t + 2) + b;
		}, 
	
		EaseOutInQuint:function(t, b, c, d) {
			if(t < d/2)
				return $_.Animation.Transitions.EaseOutQuint(t*2, b, c/2, d);
	
			return $_.Animation.Transitions.EaseInQuint((t*2)-d, b+c/2, c/2, d);
		}, 
	
	
		EaseInSine:function(t, b, c, d) {
			return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
		},
	
		EaseOutSine:function(t, b, c, d) {
			return -c * Math.sin(t/d * (Math.PI/2)) + b;
		},
	
		EaseInOutSine:function(t, b, c, d) {
			return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
		}, 
	
		EaseOutInSine:function(t, b, c, d) {
			if(t < d/2)
				return $_.Animation.Transitions.EaseOutSine(t*2, b, c/2, d);
	
			return $_.Animation.Transitions.EaseInSine((t*2)-d, b+c/2, c/2, d);
		}, 
	
	
		EaseInExpo:function(t, b, c, d) {
			return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b - c * 0.001;
		},
	
		EaseOutExpo:function(t, b, c, d) {
			return(t==d) ? b+c : c * 1.001 *(-Math.pow(2, -10 * t/d) + 1) + b;
		},
	
		EaseInOutExpo:function(t, b, c, d) {
			if (t==0) return b;
			if (t==d) return b+c;
			if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b - c * 0.0005;
			return c/2 * 1.0005 * (-Math.pow(2, -10 * --t) + 2) + b;
		},
	
		EaseOutInExpo:function(t, b, c, d) {
			if (t < d/2) 
				return $_.Animation.Transitions.EaseOutExpo(t*2, b, c/2, d);
	
			return $_.Animation.Transitions.EaseInExpo((t*2)-d, b+c/2, c/2, d);
		},
	
	
		EaseInCirc:function(t, b, c, d) {
			return -c * (Math.sqrt(1 - (t/=d) * t) - 1) + b;
		},
	
		EaseOutCirc:function(t, b, c, d) {
			return c * Math.sqrt(1 - (t=t/d-1) * t) + b;
		},
	
		EaseInOutCirc:function(t, b, c, d) {
			if ((t/=d/2) < 1) 
				return -c/2 * (Math.sqrt(1 - t * t) - 1) + b;
	
			return c/2 * (Math.sqrt(1 - (t-=2) * t) + 1) + b;
		},
	
		EaseOutInCirc:function(t, b, c, d) {
			if (t < d/2) 
				return $_.Animation.Transitions.EaseOutCirc(t*2, b, c/2, d);
	
			return $_.Animation.Transitions.EaseInCirc((t*2)-d, b+c/2, c/2, d);
		},
	
	
		EaseInElastic:function(t, b, c, d, a, p) {
			if (t==0) return b;
			if ((t/=d)==1) return b + c;
			if(!p) p = d * .3;
	
			var s;
			if(!a || a < Math.abs(c)) {
				a = c;
				s = p/4;
			} else
				s = p / (2 * Math.PI) * Math.asin(c/a);
	
			return -(a * Math.pow(2,10 * (t-=1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
		},
	
		EaseOutElastic:function(t, b, c, d, a, p) {
			if (t==0) return b;
			if ((t/=d)==1) return b + c;
			if(!p) p = d * .3;
	
			var s;
			if(!a || a < Math.abs(c)) {
				a = c;
				s = p/4;
			} else
				s = p / (2 * Math.PI) * Math.asin(c/a);
	
			return (a * Math.pow(2,-10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b);
		},
	
		EaseInOutElastic:function(t, b, c, d, a, p) {
			if (t==0) return b;
			if ((t/=d/2)==2) return b + c;
			if(!p) p = d * (.3 * 1.5);
	
			var s;
			if(!a || a < Math.abs(c)) {
				a = c;
				s = p/4;
			} else
				s = p / (2 * Math.PI) * Math.asin(c/a);
	
			if (t < 1) 
				return -.5 * (a * Math.pow(2,10 * (t-=1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
	
			return a * Math.pow(2,-10 * (t-=1)) * Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
		},
	
		EaseOutInElastic:function(t, b, c, d, a, p) {
			if (t < d/2) 
				return $_.Animation.Transitions.EaseOutElastic(t*2, b, c/2, d);
	
			return $_.Animation.Transitions.EaseInElastic((t*2)-d, b+c/2, c/2, d);
		},
	
	
	
		EaseInBack:function(t, b, c, d, s) {
			if(!s) s = 1.70158;
	
			return c * (t/=d) * t * ((s+1) * t - s) + b;
		},
	
		EaseOutBack:function(t, b, c, d, s) {
			if(!s) s = 1.70158;
	
			return c * ((t=t/d-1) * t * ((s+1) * t + s) + 1) + b;
		},
	
		EaseInOutBack:function(t, b, c, d, s) {
			if(!s) s = 1.70158;
	
			if ((t/=d/2) < 1) 
				return c / 2 * (t * t * (((s*=(1.525)) + 1) * t - s)) + b;
	
			return c / 2 * ((t-=2) * t * (((s*=(1.525)) + 1) * t + s) + 2) + b;
		},
	
		EaseOutInBack:function(t, b, c, d, s) {
			if (t < d/2) 
				return $_.Animation.Transitions.EaseOutBack(t*2, b, c/2, d);
	
			return $_.Animation.Transitions.EaseInBack((t*2)-d, b+c/2, c/2, d);
		},
	
	
		EaseInBounce:function(t, b, c, d) {
			return c - $_.Animation.Transitions.EaseOutBounce(d-t, 0, c, d) + b;
		},
	
		EaseOutBounce:function(t, b, c, d) {
			if ((t/=d) < (1/2.75))
				return c * (7.5625 * t * t) + b;
			else if (t < (2/2.75))
				return c * (7.5625 * (t-=(1.5/2.75)) * t + .75) + b;
			else if (t < (2.5/2.75))
				return c * (7.5625 * (t-=(2.25/2.75)) * t + .9375) + b;
			else
				return c * (7.5625 * (t-=(2.625/2.75)) * t + .984375) + b;
		},
	
		EaseInOutBounce:function(t, b, c, d) {
			if (t < d/2) 
				return $_.Animation.Transitions.EaseInBounce(t*2, 0, c, d) * .5 + b;
	
			return $_.Animation.Transitions.EaseOutBounce(t*2-d, 0, c, d) * .5 + c * .5 + b;
		},
	
		EaseOutInBounce:function(t, b, c, d) {
			if (t < d/2) 
				return $_.Animation.Transitions.EaseOutBounce(t*2, b, c/2, d);
	
			return $_.Animation.Transitions.EaseInBounce((t*2)-d, b+c/2, c/2, d);
		}
	}

});

$_animation = $_.Animation; //** moving to obsolete
$_anim = $_.Animation;
}
/*
 | ic0n.Controls
 | ------
 |   - the root namespace for web controls.  provides basic functionality for 
 |     authoring web controls, such as dragging, resizing, etc...
 | 
 | author
 | ------
 |   - mike macmillan(mikejmacmillan@gmail.com)
*/
if(ic0n) {
ic0n.RegisterEventFlowComponent("ic0n Controls Component", "ic0n.Controls", ["$_dom", "ic0n.Animation", "ic0n.Logging"], {

	//** different styles of docking supported
	DockStyle:{
		Top:"top",
		Right:"right",
		Bottom:"bottom",
		Left:"left",
		All:"all"
	},

	ResizeDirection:{
		North:"north",
		NorthEast:"northeast",
		East:"east",
		SouthEast:"southeast",
		South:"south",
		SouthWest:"southwest",
		West:"west",
		NorthWest:"northwest"
	},

	ResizeStyle:{
		Corners:"corners",
		Sides:"sides",
		All:"all"
	},

	MouseButton:{
		Left:"left",
		Right:"right"
	},

	MinimumObjectWidth:10,
	MinimumObjectHeight:10,

	TooltipBufferX:2,
	TooltipBufferY:2,
	DefaultTooltipShowDelay:500,
	DefaultTooltipHideDelay:500,

	//** when true, any control created is made skinnable.  this is set to false by default because true would imply all controls were skinnable, when, most of the time its on a component by component basis.
	AllowSkinning: false,

	//** implementing the implicit OnError event handler
	OnError:function(args) {
		var eventArgs = args.EventArgs;

		if(!eventArgs)
			return;

		//** because all control errors effect usability, let the user know what went wrong
		alert(eventArgs.Error);
	},

	OnControlCreated:function(args) {
		var control = args.EventArgs||args;

		if($_.SkinManager && this.AllowSkinning) {
			//** if the skin manager component is loaded, make the control skinnable
			$_skinManager.MakeSkinnable(control);
		}
	},

	Overlay:function(parentElem, options) {
		var div, div2, frame;

		//** resolve an id reference
		parentElem = $_dom.ResolveId(parentElem);

		//** get the parent we're placing the overlay over
		parentElem = parentElem||top.document.body;
		options = options||{};

		//** get the overlay div
		div = $_dom.ById(options.Id||"divPageOverlay");
		
		//** otherwise, create it
		if(!div) {
			div = parentElem.appendChild(document.createElement("div"));
			div.id = options.Id||"divPageOverlay";
			div.className = options.CssClass||"pageoverlay_default";
		} else 
		    //** if it exists, toggle it
		    $_dom.Toggle(div);

		//** add a click event if defined, otherwise, assign a click event if required
		if(options.OnClick)
			$_.AddListener(div, "click", options.OnClick);
		else {
			if(options.CloseOnClick)
				$_.AddListener(div, "click", function() { $_controls.RemoveOverlay(div.id) });
		}

		return div;
	},

	RemoveOverlay:function(id) {
		id = id||"divPageOverlay";
		var obj = $_dom.ById(id);

		if(obj && obj.parentNode)
			obj.parentNode.removeChild(obj);
	},

	Tooltip:function(msg, parent, options) {
		if(!msg || !parent) return;

		//** get the tooltip if it already exists
		options = options||{};
		var tooltip = this.GetTooltip(parent);

		//** creating it if it doesn't
		if(!tooltip)
			tooltip = this.CreateTooltip(msg, parent, options);

		//** show the tooltip
		tooltip.Show();

		return tooltip;
	},

	InvokeTooltip:function(msg, parent, options) {
		if(!msg || !parent) return;

		//** get the tooltip if it already exists
		options = options||{};
		var tooltip = this.GetTooltip(parent);

		//** creating it if it doesn't
		if(!tooltip)
			tooltip = this.CreateTooltip(msg, parent, options);

		//** invoke (show after a timeout) the tooltip per the given delay
		tooltip.InvokeShow(msg, options.Delay);

		return tooltip;
	},

	ClearTooltipInvoke:function(parent) {
		//** get the tooltip
		var tooltip = this.GetTooltip(parent);

		//** clear its show invocation
		if(tooltip)
			tooltip.ClearInvokeShow();
	},

	GetTooltip:function(parent) {
		return parent.Tooltip;
	},

	RemoveTooltip:function(parent) {
		var tooltip = parent.Tooltip;

		if(tooltip)
			tooltip.Dispose();
	},

	CreateTooltip:function(msg, parent, options) {
		if(!msg || !parent) return;

		if(options.Base64Encoded)
			msg = $_.Text.Base64Decode(msg);

		//** create the tooltip element
		options = options||{};
		var tooltipDiv = document.body.appendChild(document.createElement("div"));
		tooltipDiv.className = options.CssClass||"tooltip";
		tooltipDiv.innerHTML = msg;

		//** create the tooltip object
		var tooltip = {
			Parent:parent,
			Element:tooltipDiv,
			Message:msg,
			ShowTimer:null,
			HideTimer:null,
			ShowDelay:options.ShowDelay||$_.Controls.DefaultTooltipShowDelay,
			HideDelay:options.HideDelay||$_.Controls.DefaultTooltipHideDelay,

			Show:function(msg) {
				//** set the tooltip message if its different than the current
				if(msg && this.Message != msg) {
					this.Message = msg;
					this.Element.innerHTML = msg;
				}

				//** get the parents position
				var pos = $_.Dom.GetPosition(this.Parent, {ScrollPosition:true});

				//** determine the tooltip's position
				var newPos = {
					Left: pos.Left + (options.BufferX||$_.Controls.TooltipBufferX) - pos.ScrollLeft,
					Top: pos.Top + (options.BufferY||$_.Controls.TooltipBufferY) - pos.ScrollTop
				}

				//** place the tooltip
				$_.Dom.SetPosition(this.Element, newPos);

				//** make the tooltip visible
				$_.Dom.Toggle(this.Element, "block");
			},

			InvokeShow:function(msg, delay) {
				msg = msg||this.Message;
				if(!msg) return;

				//** use the given delay, or the default
				delay = delay||this.ShowDelay;

				//** clear any existing invoke timer
				if(this.ShowTimer) 
					clearTimeout(this.ShowTimer);

				//** setup the timer
				this.ShowTimer = setTimeout(function() { tooltip.Show(msg); }, delay);
			},

			ClearInvokeShow:function() {
				//** simply remove the invoke timer if it exists
				if(this.ShowTimer) 
					clearTimeout(this.ShowTimer);

				//** hide the tooltip
				//this.Hide();
			},

			Hide:function() {
				$_.Dom.Toggle(this.Element, "none");
			},

			InvokeHide:function(delay) {
				//** use the given delay, or the default
				delay = delay||this.HideDelay;

				//** clear any existing hide invocation
				this.ClearInvokeHide();

				//** set the timer 
				this.HideTimer = setTimeout(function() { tooltip.Hide(); }, delay);
			},

			ClearInvokeHide:function() {
				if(this.HideTimer)
					clearTimeout(this.HideTimer);
			},

			Dispose:function() {
				//** remove the tooltip element
				if(this.Element) {
					this.Element.parentNode.removeChild(this.Element);
					this.Element = null;
				}

				//** remove the parents reference to the tooltip
				if(this.Parent)
					this.Parent.Tooltip = null;
			}
		}

		//** disable the context menu
		tooltip.Element.oncontextmenu = function() { return false; };

		//** define our high level follow method
		function follow(e) {
			$_.Dom.SetPosition(tooltip.Element, {Left:e.clientX, Top:e.clientY});
		}

		//** if the tooltip must follow the mouse, wire up the listener
		if(options.FollowMouse)
			$_.AddListener(parent, "mouseover", function(e) {
				$_.AddListener(parent, "mousemove", follow);
			});

		//** on mouseout for the tooltip, if the target isn't the parent or the tooltip itself, hide the tooltip
		$_.AddListener(tooltip.Element, "mouseout", function(e) {
			e = e||window.event;
			var telem = e.relatedTarget||e.telement;

			if(telem != tooltip.Parent && telem != tooltip.Element)
				tooltip.Hide();
		});

		//** on mouseout for the parent, hide the tooltip regardless
		$_.AddListener(parent, "mouseout", function(e) {
			e = e||window.event;
			var telem = e.relatedTarget||e.telement;

			if(telem != tooltip.Element)
				tooltip.Hide();

			//** if we're following the mouse, stop now...
			if(options.FollowMouse)
				$_.RemoveListener(parent, "mousemove", follow);
		});

		//** save it on the parent
		parent.Tooltip = tooltip;

		return tooltip;
	},

	/*
	 | MakeDraggable("someDiv", {PrepareDrag:...});
	 | ------
	 |   - makes the given element draggable by overriding default dom events.  a listener pattern is provided because
	 |     both stock html objects and objects that participate in the eventflow can be made draggable.  if the  object
	 |     participates in the eventflow, drag events will be registered, and the listener will be overridden
	 |     to dispatch them to the eventflow.  if the object does not participate in the eventflow, the listener object 
	 |     allows user defined listener methods to be fired by different drag events.  a "Dragger" property/object will
	 |     be persisted on the object that encapsulates all the properties/methods for dragging.
	*/
	MakeDraggable:function(elem, options) {
		var dragger;

		//** if the object is already draggable, return its dragger
		if(elem && elem.Dragger)
			return elem.Dragger;

		//** determine if the element or its id was passed, and get the element
		elem = $_dom.ResolveId(elem);

		if(!elem) {
			this.ThrowError("[ic0n.MakeDraggable] - Element is null.", this);
			return;
		}

		//** default our listener object if necessary
		options = options||{};
		listeners = options.Listeners||{};

		//** if the element supports eventflow, add events to the object, and wire them to its eventflow.  also, rewire the listeners to fire eventflow events
		if(elem.EventFlow) {
			var arEvents = [];

			//** determine the events we need to extend.  since drag events can run on a high interval, broadcasting the event up the eventflow would be costly, thereby decreasing performance, we will register these events as non broadcastable. this means that unless explicitly listened to, the event will not leave this object's eventflow.
			if(!elem.Event.PrepareDrag)
				arEvents.push({Name:"PrepareDrag", Broadcast:false});
	
			if(!elem.Event.Drag)
				arEvents.push({Name:"Drag", Broadcast:false});
	
			if(!elem.Event.EndDrag)
				arEvents.push({Name:"EndDrag", Broadcast:false});
	
			//** register the events
			elem.RegisterEvents(arEvents);

			//** rewire the listener object to dispatch events instead of fire arbitrary handlers
			listeners.PrepareDrag = function(e, dragObj) {
				return elem.EventFlow.DispatchEvent(elem.Event.PrepareDrag, e);
			}

			listeners.Drag = function(e, pos) {
				return elem.EventFlow.DispatchEvent(elem.Event.Drag, e);
			}

			listeners.EndDrag = function(e, dragObj) {
				return elem.EventFlow.DispatchEvent(elem.Event.EndDrag, e);
			}
		} else {
			//** if the object *doesn't* participate in the event flow, wire up some default handlers if non already given; these will fire the OnX events of the element if they exist
			if(!listeners.PrepareDrag) {
				listeners.PrepareDrag = function(e, dragObj) {
					if(elem.OnPrepareDrag)
						return elem.OnPrepareDrag(e, dragObj);
					return true;
				}
			}
	
			if(!listeners.Drag) {
				listeners.Drag = function(e, pos) {
					if(elem.OnDrag)
						return elem.OnDrag(e, pos);
	
					return true;
				}
			}
	
			if(!listeners.EndDrag) {
				listeners.EndDrag = function(e, dragObj) {
					if(elem.OnEndDrag)
						return elem.OnEndDrag(e, dragObj);
	
					return true;
				}
			}
		}

		//** create our drag object. it will encapsulate all the logic for dragging an element
		var dragger = $_.Object({
			ParentObject: elem,
			Element: elem.Element||elem,
			Listeners: listeners,
			Dragging: false,

			OffsetX: 0,
			OffsetY: 0,

			MakeDragActivator:function(actObj) {
				if(typeof(actObj)==='string')
					actObj = $_dom.ById(elem);

				//** set the elements cursor
				actObj.style.cursor = "move";
		
				//** listen to the mousedown event 
				$_.Listener(actObj, "mousedown", function(e) {
					//** then start the drag process for the target object
					dragger.PrepareDrag(e);
				});
			},
		
			PrepareDrag:function(e) {
				var pos, pos2;

				//** make sure the user clicked the left button
				e = e||window.event;
				if((e.which?e.which:e.button) != 1)
					return;

				//** make sure the object isn't being resized...
				if(this.ParentObject.Resizer && this.ParentObject.Resizer.Resizing)
					return;
		
				//** before we fire the prepare drag event, call any listeners, cancelling if needed
				if(this.Listeners.PrepareDrag)
					if(this.Listeners.PrepareDrag(e, dragger)==false)
						return;
		
				//** get the windows current position
				pos = $_dom.GetPosition(dragger.Element, {StopElement:dragger.Element.parentNode});

				//** set the offsets
				this.OffsetX = e.clientX - pos.X;
				this.OffsetY = e.clientY - pos.Y;
		
				//** disable selection (so stuff doesn't highlight when u drag a container over it...)
				$_dom.DisableSelect(document);

				//** start listening for its mouse move and mouse up events
				$_.Listener(document, "mousemove", dragger.Drag);
				$_.Listener(document, "mouseup", dragger.EndDrag);
			},

			Drag:function(e) {
				e = e||window.event;
				var pos = {};

				//** toggle this objects dragging bit
				this.Dragging = true;

				//** calculate our coordinates, taking into perspective the offsets
				pos.Left = e.clientX-dragger.OffsetX;
				pos.Top = e.clientY-dragger.OffsetY;

				//** call any listeners
				if(dragger.Listeners.Drag)
					if(dragger.Listeners.Drag(e, pos)==false)
						return;

				//** set the position
				$_dom.SetPosition(dragger.Element, pos);
			},

			EndDrag:function(e) {
				e = e||window.event;

				//** reenable selecting...
				$_dom.EnableSelect(document);

				//** call any listeners
				if(dragger.Listeners.EndDrag)
					if(dragger.Listeners.EndDrag(e, dragger)==false)
						return;

				//** toggle this objects dragging bit
				dragger.Dragging = false;
		
				//** remove listeners for the mouse move and mouse up events
				$_.RemoveListener(document, "mousemove", dragger.Drag);
				$_.RemoveListener(document, "mouseup", dragger.EndDrag);
			}
		});

		//** set the objects dragger
		elem.Dragger = dragger;

		var htmlObj = elem.style?elem:(elem.Element?elem.Element:null);
		if(htmlObj) {
			htmlObj.style.position = "absolute";
			dragger.MakeDragActivator(htmlObj);
		}

		return dragger;
	},



	/*
	 | MakeResizable("someDiv", {PrepareResize:...});
	 | ------
	 |   - makes the given element resizable by overriding default dom events.  a listener pattern is provided because
	 |     both stock html objects and objects that participate in the eventflow can be made resizable.  if the object
	 |     participates in the eventflow, resize events will be registered, and the listener will be overridden
	 |     to dispatch them to the eventflow.  if the object does not participate in the eventflow, the listener object 
	 |     allows user defined listener methods to be fired by different resize events.  a "Resizer" property/object will
	 |     be persisted on the object that encapsulates all the properties/methods for dragging.
	*/
	MakeResizable:function(element, options) {
		var resizer;
		var eDir=$_controls.ResizeDirection;

		//** if the object is already resizable, return its resizer
		if(element && element.Resizer)
			return element.Resizer;

		//** determine if the element or its id was passed, and get the element
		element = $_dom.ResolveId(element);

		if(!element) {
			this.ThrowError("[ic0n.MakeResizable] - Element is null.");
			return;
		}

		//** verify our arguments/defaults
		options = options||{};
		listeners = options.Listeners||{};

		if(element.EventFlow) {
		//** if the element supports eventflow, add events to the object, and wire them to its eventflow.  also, rewire the listeners to fire eventflow events
			var arEvents = [];

			//** determine the events we need to extend.  since resize events can run on a high interval, broadcasting the event up the eventflow would be costly, thereby decreasing performance, we will register these events as non broadcastable. this means that unless explicitly listened to, the event will not leave this object's eventflow.
			if(!element.Event.PrepareResize)
				arEvents.push({Name:"PrepareResize", Broadcast:false});
	
			if(!element.Event.Resize)
				arEvents.push({Name:"Resize", Broadcast:false});
	
			if(!element.Event.EndResize)
				arEvents.push({Name:"EndResize", Broadcast:false});
	
			//** register the events
			element.RegisterEvents(arEvents);

			//** rewire the listener object to dispatch events instead of fire arbitrary handlers
			listeners.PrepareResize = function(resizeObj) {
				return element.EventFlow.DispatchEvent(element.Event.PrepareResize, resizeObj);
			}

			listeners.Resize = function(size) {
				return element.EventFlow.DispatchEvent(element.Event.Resize, size);
			}

			listeners.EndResize = function(resizeObj) {
				return element.EventFlow.DispatchEvent(element.Event.EndResize, resizeObj);
			}
		} else {
		//** otherwise, provide default listeners to call the explicit OnX events for the object if none already defined...
			if(!listeners.PrepareResize)
				listeners.PrepareResize = function(resizeObj) {
					if(element.OnPrepareResize)
						return element.OnPrepareResize(resizeObj);
	
					return true;
				}
	
			//** same for Resize
			if(!listeners.Resize)
				listeners.Resize = function(size) {
					if(element.OnResize)
						return element.OnResize(size);
	
					return true;
				}
	
			//** same for EndResize
			if(!listeners.EndResize)
				listeners.EndResize = function(resizeObj) {
					if(element.OnEndResize)
						return element.OnEndResize(resizeObj);
	
					return true;
				}
		}


		//** create our resizer object...it will encapsulate all the logic/behaviors for resizing
		resizer = $_.Object({
			ParentObject: element,
			Element: element.Element||element,
			Activators: options.Activators||[],
			Cache: {}, //** a generic "dictionary" that implementors can use to persist defaults before the resize, store state, etc..
			Listeners: listeners,

			Direction: null,
			Resizing: false,
			PreResizePosition: null,
			ResizeStyle: options.ResizeStyle||$_.Controls.ResizeStyle.Corners,

			ClickOffsetX: 0,
			ClickOffsetY: 0,
			ParentOffsetX: 0,
			ParentOffsetY: 0,

			/*
	 		 | MakeResizeActivator:(someDirection, objActivator)
	 		 | ------
	 		 |   defines an element that invokes the resize for this element, for the given direction.  use this method
	 		 |   when defining your own custom "corners" or "draggers".  create the element, then pass it and the direction 
	 		 |   to this method so it wires up the event listeners
			*/
			MakeResizeActivator: function(dir, obj) {
				if(!dir) {
					this.ThrowError("[ic0n.MakeResizable->MakeResizeActivator] Direction cannot be null.");
					return;
				}

				//** ensure we have the element that will invoke the resize
				if(!obj) {
					//** create a div with a default class based on its direction
					obj = this.Element.appendChild($_dom.Elem("div", {"class":"resize_"+ dir, "style":"position: absolute; height: 15px; width: 15px; z-index: 100;"}));

					//** set the direction specific styles
					switch(dir) {
						case $_controls.ResizeDirection.NorthWest:
							$_dom.SetStyle(obj, {left:"0px", top: "0px", cursor: "nw-resize"});
							break;
						case $_controls.ResizeDirection.NorthEast:
							$_dom.SetStyle(obj, {right:"0px", top: "0px", cursor: "ne-resize"});
							break;
						case $_controls.ResizeDirection.SouthWest:
							$_dom.SetStyle(obj, {left:"0px", bottom: "0px", cursor: "sw-resize"});
							break;
						case $_controls.ResizeDirection.SouthEast:
							$_dom.SetStyle(obj, {right:"0px", bottom: "0px", cursor: "se-resize"});
							break;
					}

					//** persist this element in the activators collection
					this.Activators[dir] = obj;
				}
		
				//** wire up the event handler
				$_.Listener(obj, "mousedown", function(e) {
					//** set the direction
					resizer.Direction = dir;
		
					//** invoke the resize
					resizer.PrepareResize(e);
				});

				return obj;
			},

			PrepareResize:function(e) {
				var pos, pos2;
		
				//** make sure the user clicked the left button
				if((e.which?e.which:e.button) != 1)
					return;
		
				//** if a start listener was defined, invoke it first...it can stop the execution based on its return value
				if(this.Listeners.PrepareResize) {
					if(this.Listeners.PrepareResize(this)===false)
						return;
				}
		
				//** disable selection (so stuff doesn't highlight when u drag a container over it...)
				$_dom.DisableSelect(document);
		
				//** get the position of the element we're resizing
				pos = $_dom.GetPosition(this.Element);
				this.PreResizePosition = pos;
		
				//** based on the direction, calculate the initial click's x and y offset from the top left corner
				switch(this.Direction) {
				case $_controls.ResizeDirection.NorthWest:
					this.ClickOffsetX = e.clientX - pos.X;
					this.ClickOffsetY = e.clientY - pos.Y;
					break;
				case $_controls.ResizeDirection.SouthWest:
					this.ClickOffsetX = e.clientX - pos.X;
					this.ClickOffsetY = pos.Height - (e.clientY - pos.Top);
					break;
				case $_controls.ResizeDirection.NorthEast:
					this.ClickOffsetX = pos.Width - (e.clientX - pos.X);
					this.ClickOffsetY = e.clientY - pos.Y;
					break;
				case $_controls.ResizeDirection.SouthEast:
					this.ClickOffsetX = pos.Width - (e.clientX - pos.X);
					this.ClickOffsetY = pos.Height - (e.clientY - pos.Top);
					break;
				}
		
				//** if our object is the child of another object, the top and left we give the element will begin at its parents top left, not the body's.  
				//** get the parent's position, and add its x and y as the offset to remove from any left and top being set
				if(this.Element.parentNode && this.Element.parentNode !== document.body) {
					pos2 = $_dom.GetPosition(this.Element.parentNode);
		
					this.ParentOffsetX = pos2?pos2.X:0;
					this.ParentOffsetY = pos2?pos2.Y:0;
				}

				//** toggle this objects resizing bit
				this.Resizing = true;
		
				//** start listening for its mouse move and mouse up events
				$_.Listener(document, "mousemove", resizer.Resize);
				$_.Listener(document, "mouseup", resizer.EndResize);
			},

			Resize:function(e) {
				var pos = resizer.PreResizePosition;
				var size = {};
				var nextPos = {};

				//** determine the future position based on direction
				switch(resizer.Direction) {
				case $_controls.ResizeDirection.NorthWest:
					//** calculate the dimensions
					size.Left = (e.clientX - resizer.ParentOffsetX) - resizer.ClickOffsetX;
					size.Top = (pos.Y - resizer.ParentOffsetY) - (pos.Y - e.clientY) - resizer.ClickOffsetY;
					size.Width = (pos.Right - e.clientX) + resizer.ClickOffsetX;
					size.Height = pos.Height + (pos.Y-e.clientY) + resizer.ClickOffsetY;

					//** if there is a resize listener, fire it
					if(resizer.Listeners.Resize)
						if(resizer.Listeners.Resize(size)===false)
							return;

					//** make sure the object's width is greater than the defined min, and that its width never "pushes" the container to the right
					if(size.Width > $_controls.MinimumObjectWidth && (size.Left+size.Width) == (pos.Right-resizer.ParentOffsetX)) {
						nextPos.Width = size.Width;
						nextPos.Left = size.Left;
					}

					//** same for the top/height
					if(size.Height > $_controls.MinimumObjectHeight && (size.Top + size.Height) <= (pos.Bottom - resizer.ParentOffsetY)) { 
						nextPos.Height = size.Height;
						nextPos.Top = size.Top;
					}
					break;
		
				case $_controls.ResizeDirection.SouthWest:
					//** calculate the dimensions
					size.Left = (e.clientX - resizer.ParentOffsetX) - resizer.ClickOffsetX;
					size.Width = (pos.Right - e.clientX) + resizer.ClickOffsetX;
					size.Height = pos.Height + (e.clientY - (pos.Bottom - resizer.ClickOffsetY));

					//** if there is a resize listener, fire it
					if(resizer.Listeners.Resize)
						if(resizer.Listeners.Resize(size)===false)
							return;
		
					//** make sure the object's width is greater than the defined min, and that its width never "pushes" the container to the right
					if(size.Width > $_controls.MinimumObjectWidth && (size.Left+size.Width) <= (pos.Right-resizer.ParentOffsetX)) {
						nextPos.Left = size.Left;
						nextPos.Width = size.Width;
					}
		
					if(size.Height > $_controls.MinimumObjectHeight)
						nextPos.Height = size.Height;
					break;
		
				case $_controls.ResizeDirection.NorthEast:
					//** calculate the dimensions
					size.Top = (e.clientY - resizer.ClickOffsetY) - resizer.ParentOffsetY;
					size.Width = (e.clientX - pos.Left) + resizer.ClickOffsetX;
					size.Height = pos.Height + (pos.Y-e.clientY) + resizer.ClickOffsetY;

					//** if there is a resize listener, fire it
					if(resizer.Listeners.Resize)
						if(resizer.Listeners.Resize(size)===false)
							return;

					//** set the objects dimensions
					if(size.Width > $_controls.MinimumObjectWidth)
						nextPos.Width = size.Width;
		
		
					if(size.Height > $_controls.MinimumObjectHeight && (size.Top + size.Height) <= (pos.Bottom-resizer.ParentOffsetY)) {
						nextPos.Height = size.Height;
						nextPos.Top = size.Top;
					}
					break;
		
				case $_controls.ResizeDirection.SouthEast:
					//** calculate the dimensions
					size.Width = (e.clientX - pos.Left) + resizer.ClickOffsetX;
					size.Height = pos.Height + (e.clientY - (pos.Bottom - resizer.ClickOffsetY));

					//** if there is a resize listener, fire it
					if(resizer.Listeners.Resize)
						if(resizer.Listeners.Resize(size)===false)
							return;
		
					//** set the objects dimensions
					if(size.Width > $_controls.MinimumObjectWidth)
						nextPos.Width = size.Width;
		
					if(size.Height > $_controls.MinimumObjectHeight)
						nextPos.Height = size.Height;
					break;
				}

				//** set the position
				$_dom.SetPosition(resizer.Element, nextPos);
			},

			EndResize:function(e) {
				//** toggle this objects resizing bit
				resizer.Resizing = false;

				//** if there is a listener, fire it
				if(resizer.Listeners.EndResize)
					resizer.Listeners.EndResize(resizer);
	
				//** reenable selecting...
				$_dom.EnableSelect(document);
	
				//** remove listeners for the mouse move and mouse up events
				$_.RemoveListener(document, "mousemove", resizer.Resize);
				$_.RemoveListener(document, "mouseup",  resizer.EndResize);
			}
		});

		//** set the objects resizer
		element.Resizer = resizer;

		//** if an implementation of the activators was already given, use it
		if(resizer.Activators.length>0) return;

		//** otherwise, add the default activators based on the resize style
		if(resizer.ResizeStyle==$_controls.ResizeStyle.Corners || resizer.ResizeStyle==$_controls.ResizeStyle.All) {
			resizer.MakeResizeActivator($_controls.ResizeDirection.NorthWest);
			resizer.MakeResizeActivator($_controls.ResizeDirection.NorthEast);
			resizer.MakeResizeActivator($_controls.ResizeDirection.SouthEast);
			resizer.MakeResizeActivator($_controls.ResizeDirection.SouthWest);
		} else {
			if(resizer.ResizeStyle==$_controls.ResizeStyle.Sides) {
				resizer.MakeResizeActivator($_controls.ResizeDirection.North);
				resizer.MakeResizeActivator($_controls.ResizeDirection.East);
				resizer.MakeResizeActivator($_controls.ResizeDirection.South);
				resizer.MakeResizeActivator($_controls.ResizeDirection.West);
			}
		}

		return resizer;
	},

	Control:function(control, args) { return this.CreateControl(control, args); },
	CreateControl:function(control, args) {
		var arEvents;

		//** resolve an element id if required
		control = $_dom.ResolveId(control);

		if(!control) {
			this.ThrowError("[ic0n.Controls.CreateControl] Control object prototype is null", this);
			return;
		}

		//** if we have been given an object, not an html element, make sure its ".Element" property is there, which should contain the html element
		if(!$_.Defined(control, "tagName")) {
			if(!$_.Defined(control, "Element"))
				control.Element = null;
		} else
			//** if we've received an html element, create an object with a single member referring to it
			control = {Element: control};


		//** build/verify our args object
		args = args||{};

		//** if logging handling isn't defined, inherit from the Control component
		if(typeof(args.EnableLogging) === 'undefined')
			args.EnableLogging = false;

		//** disable dragging by default
		if(typeof(args.EnableDragging) === 'undefined')
			args.EnableDragging = false;

		//** disable resizing by default
		if(typeof(args.EnableResizing) === 'undefined')
			args.EnableResizing = false;

		//** by default initialize on create
		if(typeof(args.Initialize) === 'undefined')
			args.Initialize = true;

		if(typeof(args.RenderOnInitialize) === 'undefined')
			args.RenderOnInitialize = true;


		//** verify our event stack
		arEvents = args.Events||[];

		//** make sure the default control events have been added to the event stack (below is the order they *should* get fired in too)
		arEvents.push({Name:"Initialize", Broadcast:true});
		arEvents.push({Name:"Render", Broadcast:true});
		arEvents.push({Name:"RenderComplete", Broadcast:true});
		arEvents.push({Name:"Load", Broadcast:true});

		//** create our control
		control.Initialized = false;
		var control = $_.EventFlowObject(control, arEvents);

		/** add an ElementMap object to our control's prototype if not already present.  since a control can be made up of several dom elements, an ElementMap collection allows the developer to 
		    reference and use them in any form they like, such as .ElementMap["Title"] or perhaps .ElementMap[SomeElementEnum.Title].  these elements will have to be mapped by the user, 
		    for example, someObj.ElementMap["Title"] = document.getElementById("divTitle").  if this control participates in skinning, the SkinTemplate object defines an ElementMap
		    collection which allows the user to predefine mappings, such as {Element:"Title",ElementId:"divTitle"} which the skinning engine will resolve into the .ElementMap collection **/
		control.ElementMap = control.ElementMap||{};

		//** attach to its eventflow
		this.EventFlow.Connect(control);

		//** if logging is enabled, create and connect a logger to the control
		if(args.EnableLogging)
			ic0n.Logging.EnableLogging(control);


		/** add a listener to dispatch the render event when the initialize event is dispatched.  for the initialize event, we listen to *our* 
		    eventflow, which allows us to modify the object/event before any listeners or OnX event handlers are called.  since we're rendering,
		    we want to make sure the object has a valid element during those events, so we call this first **/
		this.AddListener(control.Event.Initialize, function(largs, hndl) {
			this.Initialized = true;

			//** add a callback to the Initialize event to call the Render method if configured
			if(args.RenderOnInitialize)
				largs.AddCallback(control.Render);
		});

		//** add an automatic callback to the render event that fires the RenderComplete event
		control.AddListener("Render", function(largs, hndl) {
			largs.AddCallback(control.RenderComplete);
		});

		//** add a listener to attach additional functionality to the object after its been rendered.  for this event, we simply listen to the object as these are modifiers and nothing depends on them being created, so there isn't an order they need to be created in
		control.AddListener(control.Event.RenderComplete, function() {
			//** same for resizable
			if(args.EnableResizing)
				$_controls.MakeResizable(control);

			//** make the control draggable if needed
			if(args.EnableDragging)
				$_controls.MakeDraggable(control);
		});

		//** dispatch our controlcreated event to allow implementors to modify the control before its initialized
		this.ControlCreated(control);

		//** dispatch the initialize event if required
		if(args.Initialize)
			control.Initialize();

		return control;
	},

	CreateIframe:function(id, url) {
		var frameObj;

		//** ie doesn't support setting the form's target for iframes who's id/name wasn't created with it (below).  weird, yes i know.  conversely, firefox throws an invalid character error when using this method, hence the 2 necessary methdos for creating an iframe.
		if($_.Browser.IsIe) {
			if($_.Browser.Version < 9)
				frameObj = document.createElement("<iframe id=\""+ id +"\" name=\""+ id +"\" frameBorder=\"0\" src=\""+ (url||"about:blank") +"\"></iframe>"); //** ahh, the good ole days; you are missed, ie6
			else {
				//** ie9 got a bit strict about passing html strings to createElement...http://stackoverflow.com/questions/5344029/invalid-character-dom-exception-in-ie9
				frameObj = document.createElement("iframe");
				frameObj.setAttribute("id", id);
				frameObj.setAttribute("name", id);
				frameObj.setAttribute("frameBorder", "0");
				frameObj.setAttribute("src", url||"about:blank");
			}
		} else {
			frameObj = document.createElement("iframe");
			frameObj.id = id;
			frameObj.name = frameObj.id;
			frameObj.src = url||"about:blank";
			frameObj.frameBorder = "0";
		}

		return frameObj;
	},

	CreateClock:function(obj) {
		var divDate, divTime;

		if(typeof(obj) === 'string')
			obj = $_dom.ById(obj);

		if(!obj)
			return;

		//** create the date div
		divTime = obj.appendChild(document.createElement("div"));
		divTime.id = obj.id +"_time";
		divTime.className = "clock_default_time ul";

		//** create the time div
		divDate = obj.appendChild(document.createElement("div"));
		divDate.id = obj.id +"_date";
		divDate.className = "clock_default_date ll";

		//** return the controller
		return {
			Element:obj,
			TimeElement:divTime,
			DateElement:divDate,
			Timer:null,
			StartTime:null,
			StopTime:null,
			Start:function() {
				var oDate, oNow, oTimer=this;

				//** record the start time
				this.StartTime = ic0n.Now();
				oNow = this.StartTime;

				//** define the function to set the time
				function updateTime() {
					var newDate = new Date();
					var t = {Hour:newDate.getHours(),Minute:newDate.getMinutes()};

					t.Hour = t.Hour<10?"0"+ t.Hour:t.Hour;
					t.Minute = t.Minute<10?"0"+ t.Minute:t.Minute;

					divTime.innerHTML = t.Hour +":"+ t.Minute;
				}

				//** set the current time/date
				divDate.innerHTML = oNow.Day.Abbr +". "+ oNow.Month.Abbr +". "+ oNow.Day.Number;
				updateTime();

				//** start the timer to keep the time
				this.Timer = setInterval(updateTime, 2000);
			},
			Stop:function() {
				//** record the stop time, and clear the timer
				this.StopTime = ic0n.Now();
				clearInterval(this.Timer);
			}
		}
	}

}, ["ControlCreated"]);

$_controls = ic0n.Controls;
}
/*
ic0n.Controls.Tabs
------
  - 

  author
  ------
    - mike macmillan(mikejmacmillan@gmail.com)
*/
if(ic0n) {
ic0n.RegisterEventFlowComponent("ic0n Tabs Component", "ic0n.Controls.Tabs", ["ic0n.Dom", "ic0n.Controls"], function() {
	function _tabGroup(groupId, opt) {
		opt = opt||{};
		groupId = $_dom.ResolveId(groupId);
		
		var group = $_.EventFlowObject({
			Tabs:[],
			Element: groupId,
			SelectedTab: null,

			OnInitialize:function(e, obj) {			 				   
			    if(this.Element && $_.Defined(opt, "TabCssClass")) {
			        var tabs = $_dom.ByClass(opt.TabCssClass, this.Element);
			        if($_.Defined(tabs, "tagName"))
						tabs = [tabs];

					if(tabs.length > 0)
			            for(var i=0;i<tabs.length;i++)
			                this.CreateTab(tabs[i].id, false);			            
			    }

				//** if the hash indicates a tab should be selected, select that tab now
				$_.Each(this.Tabs, function(item) {
					var name = item.TabId.replace("tab_", "").toLowerCase();
					var hash = location.hash.replace('#', '').toLowerCase();

					if(name == hash)
						item.Activate();
				});
			},
			
			Tab:function(obj, create, opt2) {
			    opt2 = opt2||{};			    
			    create = !$_.Defined(create)?true:create;
			    			    
			    obj = $_dom.ResolveId(obj);
			    
			    var tab = this.GetTab(obj.id);
			    if(!tab && create === true)
			        tab = this.CreateTab(obj, false, opt2);
			        
			    return tab;
			},

			CreateTab:function(obj, selected, opt2) {
			    opt2 = opt2||{};
			    obj = $_dom.ResolveId(obj);
			    
			    if(opt.SelectedClass && !opt2.SelectedClass)
					opt2.SelectedClass = opt.SelectedClass;
			    
				//** create our tab				
				var tab = _tab(obj, group, opt2);

				if(!tab) return;

				//** preset the selected boolean
				if(tab.Selected === false)
					tab.Selected = typeof(selected)==='undefined'?false:selected;
					
				if(tab.Selected === true)
					this.SelectedTab = tab;

				//** add the tab to the collection
				this.Tabs.push(tab);

				//** connect the tabs eventflow to ours
				this.EventFlow.Connect(tab);

				//** fire the tab added event
				this.TabCreated(tab);
				
				return tab;
			},

			GetTab:function(id) {
				//** find and return the tab by id
				for(var i=0;i<this.Tabs.length;i++)
					if(this.Tabs[i].TabId == id)
						return this.Tabs[i];

				return null;
			},

			SelectTab:function(tab) {
				//** if given a tab id, get it from our collection
				if(typeof(tab) === 'string')
					tab = this.GetTab(tab);

				if(tab)
					tab.Activate();
			},

			SelectDefault:function() {
			    var tab = null;			    
			    
			    if(this.SelectedTab)
					tab = this.SelectedTab;
			    
			    //** use the configured default
			    if(!tab && $_.Defined(opt, "DefaultTab"))
			        tab = this.GetTab(opt.DefaultTab);
			        
			    //** otherwise, use the first tab
			    if(!tab && this.Tabs.length > 0)
			        tab = this.Tabs[0];			        
			    
			    if(tab)				
				    tab.Activate();
			},

			OnTabSelected:function(args) {
				var tab = args.EventArgs;
				if(!tab) return;				
				
				
				//** set the selected tab
				this.SelectedTab = tab;				

				//** deactivate all tabs except the selected one
				for(var i=0;i<this.Tabs.length;i++) {
					if(this.Tabs[i].TabId != tab.TabId)
						this.Tabs[i].Deactivate();					
				}				
			}

		}, ["Initialize", "TabCreated", "TabSelected"]);

		//** fire the initialize event
		group.Initialize();

		//** fire the components group created event
		$_tabs.GroupCreated(group);

		return group;
	}

	function _tab(tabId, group, opt) {
	    opt = opt||{};
	    
		//** resolve an id reference to the tab element
		tabId = $_dom.ResolveId(tabId);

		if(!tabId) return;
		
		//** enforce a default id if none given
		if(tabId.id == "")
		    tabId.id = "tab_"+ $_.GenerateId();
		
		//** enforce tab default settings
		if(!$_.Defined(opt, "ContentIdPattern"))
		    opt.ContentIdPattern = "_content";
		    
		if(!$_.Defined(opt, "SelectedClass"))
		    opt.SelectedClass = "selected";

		//** create the tab object
		var tab = $_.EventFlowObject({
			TabId: tabId.id,
			Element: tabId,
			ContentElement: opt.ContentElement||$_dom.ById(tabId.id + opt.ContentIdPattern),
			Group: group,
			DataCache: {},
			Active: false,
			Loaded: false,
			Selected: $_dom.HasClass(tabId, opt.SelectedClass)?true:false,

			OnInitialize:function() {			
				$_dom.Toggle(this.ContentElement, "none");

				//** when the tab is clicked, fire its activate event
				$_.Listener(this.Element, "click", function(e, obj) {															
					tab.Activate();					
				});
			},

			OnActivate:function() {
				//** add the selected class to the tab if none given
				if(!$_dom.HasClass(this.Element, opt.SelectedClass))
					$_dom.AddClass(this.Element, opt.SelectedClass);

				//** toggle the content if it exists				
				if(this.ContentElement)
					$_dom.Toggle(this.ContentElement, "block");

				//** set the status to active
				this.Active = true;
				
				group.TabSelected(tab);
			},

			OnDeactivate:function() {
				//** remove the selected class to the tab if none given
				if($_dom.HasClass(this.Element, opt.SelectedClass))
					$_dom.RemoveClass(this.Element, opt.SelectedClass);

				//** toggle the content if it exists
				if(this.ContentElement)
					$_dom.Toggle(this.ContentElement, "none");

				//** set the status to inactive
				this.Active = false;
			}
		}, ["Initialize", "Activate", {Name:"Deactivate", Broadcast:false}]);

		//** fire the tabs initialize event
		tab.Initialize()

		//** fire the frameworks tabcreated event
		$_tabs.TabCreated(tab);

		return tab;
	}

	return {
		Groups: {},
		Group:function(groupId, opt) {
		    opt = opt||{};
		    
			//** get the tabgroup by id
			var group = this.Groups[groupId];
	
			if(!group) {
				//** create the tabgroup
				group = _tabGroup(groupId, opt);
	
				//** add it to the collection
				if(group)
					this.Groups[groupId] = group;
			}
	
			return group;
		}
	};
}(), ["GroupCreated", "TabCreated", "TabSelected"]);

$_tabs = ic0n.Controls.Tabs;
}
/*
ic0n.Controls.Dialogs
------
  - 

  author
  ------
	- mike macmillan(mikejmacmillan@gmail.com)
*/
if(ic0n) {
    ic0n.RegisterEventFlowComponent("ic0n Dialogs Component", "ic0n.Controls.Dialogs", ["ic0n.Dom", "ic0n.Controls"], function() {
        var _dialogs = [];

        return {
            Dialog: function(url, options) {
                options = options || {};
                options.Element = options.Element || null;
                options.ElementMap = options.ElementMap || [];
                options.OverlayZIndex = options.OverlayZIndex || 100;
                options.DialogZIndex = options.DialogZIndex || 101;
                options.DisableScroll = options.DisableScroll || true;
                options.IFrameHeight = options.IFrameHeight || "100%"; //added to help with IE7 height problem
                options.CloseOverlayOnClick = typeof (options.CloseOverlayOnClick) == 'undefined' ? true : options.CloseOverlayOnClick;
                options.Skinned = options.Skinned === true ? true : false;

                //** verify the heights, setting defaults if necessary
                if (!options.DockStyle || options.DockStyle == "") {
                    options.Height = options.Height || 150;
                    options.Width = options.Width || 300;
                }

                //** see if the dialog was passed into the method
                if (typeof (url) === 'object')
                    options.Element = url;

                //** get the page dialog object
                options.Element = options.Element || $_dom.ById(options.DialogId || "divPageDialog");

                //** fire the created event.  this allows implementors a chance to modify the dialog div when created, allowing them to return a new dialog div
                var newDiv = $_dialogs.DialogCreate(options.Element);

                //** if a new one has been given, use it
                if (newDiv) {
                    //** if the div returned was a rendered fragment, grab its element and add its items to the elementmap
                    if (newDiv.Element) {
                        //** add its elementmap to the options array
                        if (newDiv.ElementMap)
                            for (var s in newDiv.ElementMap)
                            options.ElementMap[s] = newDiv.ElementMap[s];

                        newDiv = newDiv.Element;
                    }
                }

                //** get the new div, use the existing one, or create a new one			
                options.Element = newDiv || (options.Element || $_dom.Elem("div", null, null, options.Parent || top.document.body));

                //** verify some required settings
                var div = options.Element;
                div.id = div.id || (options.DialogId || "divPageDialog");
                div.className = options.CssClass || (div.className || "pagedialog_default");
                div.style.zIndex = div.style.zIndex || options.DialogZIndex;

                //** get the frame object
                var frame = null;

                //** if there is an elementmap in the options, get the frame from there
                if (options.ElementMap && options.ElementMap["IFrame"] != null)
                    frame = options.ElementMap.IFrame;
                else {
                    //** otherwise, get the first frame that is a child of the div
                    var frms = $_dom.ByTag("iframe", div);
                    var frame = frms && frms.length > 0 ? frms[0] : null;
                }

                if (!frame && url && typeof (url) === 'string' && url.toString().length > 0) {
                    //** use the existing iframe, or create a new one
                    frame = $_controls.CreateIframe(options.FrameId || "divPageDialog_frame")

                    //** create its child iframe to host the dialog page
                    $_dom.SetStyle(frame, { height: options.IFrameHeight, width: "100%", zIndex: "100" });

                    //** add the frame to the dialog; if the dialog is a control, add it to its content element if present
                    if (options.ElementMap && options.ElementMap["Content"] != null)
                        options.ElementMap.Content.appendChild(frame);
                    else
                        div.appendChild(frame);
                }

                //** handle the window resize event
                ic0n.AddListener(window, "resize", function() { $_dom.Center(div, options.Parent || null); });

                //** handle the window scroll event
                ic0n.AddListener(window, "scroll", function() { $_dom.Center(div, options.Parent || null); });

                //** disable body scroll if required
                if (options.DisableScroll === true)
                    $_dom.DisableBodyScroll();


                //** create the onclick handler for the overlay which will close the dialog and remove the overlay
                if (!$_.Defined(options, "OnClick")) {
                    options.OnClick = function() {
                        if (!options.CloseOverlayOnClick) return;
                        $_dialogs.Close(div, options.Skinned);
                    };
                }

                //** if we found a close button after parsing the element map, wire up its click handler			
                if (options.ElementMap && options.ElementMap["CloseButton"] != null) {
                    $_.AddHandler(options.ElementMap.CloseButton, "click", function() {
                        $_dialogs.Close(div, options.Skinned);
                    });
                }

                //** since we're using cascaded options, make sure the css class for the dialog isn't mixed up with the css class for the overlay
                options.CssClass = options.DialogCssClass || null;

                //** show the page overlay
                var overlay = $_controls.Overlay(options.OverlayParent || null, options);

                //** if we've been given a relative url, make it an absolute url
                if (url && url.toString().indexOf("http") == -1)
                    url = location.protocol + "//" + location.host + url;

                //** set the url of the dialogs iframe
                if (frame)
                    frame.src = url;
                    
                //** display our div, and center it
                $_dom.Toggle(div, "block");                    

                //** if any size dimensions were given, set the size
                if ((!options.DockStyle || options.DockStyle == "")) {
					if(options.Height || options.Width)
						$_dom.SetPosition(div, { Height: options.Height, Width: options.Width });
						
					//** center the dialog
					$_dom.Center(div, options.Parent || null);
				}

                //** add the dialog to the dialog stack
                _dialogs.push(div);

                //** if a callback was defined, call it nau, before we dispatch the event
                if (options.Callback)
                    options.Callback(div, options);

                //** dispatch the dialogopen event
                $_dialogs.DialogOpen(div);

                return div;
            },

            SkinnedDialog: function(url, options) {
                options = options || {};
                //** create our container skin template			
                var template = $_skinManager.Templates.Dialog({
                    TemplateName: options.TemplateName || null,
                    Id: options.DialogId || $_.GenerateId(),
                    Url: options.Url,
                    Height: options.Height,
                    Width: options.Width,
                    CssClass: options.CssClass
                });
                
                //** get the skinned content for the container, defining the callback that creates the dialog from the returned skin
                var content = $_skinManager.GetSkinTemplate(template, function(content) {

                    //** render the returned skin content into an element
                    var rendered = $_skinManager.RenderSkinTemplate(template, content);

                    if (!rendered) return;

                    //** define our dialog create override handler which we will use to "skin" the dialog with the element we've just created
                    $_dialogs.Listener("DialogCreate", function(e, hndl) {
                        //** disconnect the listener for this dialog
                        hndl.Disconnect();

                        //** if a dialog existed from previous calls, or on the page when loaded, remove it now before we add our dialog to the page
                        var div = e.EventArgs;
                        if ($_.Defined(div, "tagName") && div.parentNode != null)
                            div.parentNode.removeChild(div);

                        //** add our rendered element to the parent
                        var container = (options.Parent || top.document.body).appendChild(rendered.Element);

                        //** decide who the new content object is
                        var contentObj = rendered.ElementMap.Content || container;

                        //** return the new page dialog that's replacing the default
                        return rendered;
                    });

                    //** finally, with content and listener in hand, launch the default, extended/overridden, dialog method
                    options.Skinned = true;
                    $_dialogs.Dialog(url, options);
                });
            },

            Close: function(dlg, remove) {
				remove = true;
				
                //** resolve an id reference
                dlg = $_dom.ResolveId(dlg);

                //** if no dialog has been given, get the first from the stack if there is one
                if (!dlg && _dialogs.length > 0)
                    dlg = _dialogs[0];

                if (!dlg) return;

                //** either remove the dialog from the dom, or just hide it
                if (remove === true) {
                    dlg.style.display = "none"; //** this avoids the url navigating back to a "default" in ie7+
                    dlg.parentNode.removeChild(dlg);
                } else
                    $_dom.Toggle(dlg, "none");


                //** remove the overlay
                $_controls.RemoveOverlay();

                //** enable body scroll if its been disabled
                if (!$_dom.IsScrollEnabled())
                    $_dom.EnableBodyScroll();

                //** remove the dialog from the dialog stack
                _dialogs.pop();

                //** fire the dialog close event for the component
                $_dialogs.DialogClose(dlg);
            }
        };
    } (), ["DialogCreate", "DialogOpen", "DialogClose"]);

$_dialogs = ic0n.Controls.Dialogs;
}

