/*	Define IPC namespaces	*/
		var IPC = {};
		var $NS = function()  {
			$A( arguments ).each( function( ns ) {
				var r = IPC;
				ns = ns.split( '.' );
				if ( ns[0] == 'IPC' ) ns.shift();
				ns.each( function( n ) {
					if ( !( r[n] instanceof Object ) ) r[n] = {};
					r = r[n];
				} );
			} );
		};
		
		$NS( 'CONFIG', 'DOM', 'HANDLE' );
		
//	use Dean Edward's cssQuery as it's hella better than Prototype's
//		$$ = cssQuery;
/*	Browser Detection - you can access the info through $USER	*/
		IPC.BrowserDetect = Class.create();
		IPC.BrowserDetect.prototype = {
			initialize : function () {
				this.browser = this.searchString( this.dataBrowser ).identity || "An unknown browser";
				this.version = this.searchVersion( navigator.userAgent )
					|| this.searchVersion( navigator.appVersion )
					|| "an unknown version";
				this.OS = this.searchString( this.dataOS ).identity || "an unknown OS";
				this.activex =  this.activeXCheck();
				this.flash = this.flashCheck();
			},
			searchString: function ( data ) {
				return data.find( function( datum ) {
					var dataString = datum.string;
					var dataProp = datum.prop;
					this.versionSearchString = datum.versionSearch || datum.identity;
					if ( dataString ) {
						if ( dataString.indexOf( datum.subString ) != -1 )
							return datum.identity;
					}
					else if (dataProp)
						return datum.identity;
				}.bind( this ) );
			},
			searchVersion: function ( dataString ) {
				var index = dataString.indexOf( this.versionSearchString );
				if ( index == -1 ) return;
				return parseFloat( dataString.substring( index + this.versionSearchString.length + 1 ) );
			}, 
			activeXCheck : function()
			{
				return { 
					msie : ( window.ActiveXObject && this.OS == 'Windows' ) ? true : false, 
					gecko : ( window.GeckoActiveXObject ) ? true : false
				};
			}, 
			flashCheck : function()
			{
				flashPlayer = testPluginsArray("Shockwave Flash");
				if(!flashPlayer.installed) {
					if(testForPluginMimeType('application/x-shockwave-flash')) flashPlayer.installed = true;
					else {
						for(var i=8; i>0; i--) {
							if(this.activex.msie) {
								try {
									new ActiveXObject("ShockwaveFlash.ShockwaveFlash." + i);
									flashPlayer.installed = true;
									flashPlayer.version = i;
									return flashPlayer;
								}
								catch(e) { flashPlayer.installed = false; }
							}
							else flashPlayer.installed = false;
						}
					}
				}
				return flashPlayer;
			}, 
			dataBrowser: [
				{ string: navigator.vendor, subString: "Apple", identity: "Safari" },
				{ prop: window.opera, identity: "Opera" },
				{ string: navigator.userAgent, subString: "Firefox", identity: "Firefox" },
				{ string: navigator.userAgent, subString: "MSIE", identity: "Explorer", versionSearch: "MSIE" }
			],
			dataOS : [
				{ string: navigator.platform, subString: "Win", identity: "Windows" },
				{ string: navigator.platform, subString: "Mac", identity: "Mac" },
				{ string: navigator.platform, subString: "Linux", identity: "Linux" }
			]
		};
		var $USER = new IPC.BrowserDetect();
		
/*	DOM Builder - you can parse it a JS object or a HTML string to build you HTML, events are also parsed
	use		$B.node();		- for JSON
	use		$B.eval();		- for HTML
	*/
		var $B = IPC.DOM.Build = {
			IE_TRANS : { 'class' : 'className', 'for' : 'htmlFor' }, 
			HTML_TAGS : $w( "p div span strong em img table tr td th thead tbody tfoot pre code h1 h2 h3 h4 h5 h6 ul ol" +
						" li form input textarea legend fieldset select option blockquote cite br hr dd dl dt address a button" + 
						" abbr acronym script link style bdo ins del object param col colgroup optgroup caption label dfn kbd samp var embed" ), 
			eval : function( str )
			{
				return $( document.createElement( 'div' ) ).update( str.strip() ).down() || document.createTextNode( str.strip() );
			},
			node : function( obj, options )
			{
				if ( this._string( obj ) ) return document.createTextNode( obj );
				
				this.options = $H( {
					xml : false
				} ).merge( options || {} );
				
				var el;
				var key = $H( obj ).keys()[0];
				var val = $H( obj ).values()[0];
				
				if ( ( !this.options.xml && this.HTML_TAGS.indexOf( key ) > -1 ) || this.options.xml )
				{
					el = $( document.createElement( key ) );
//	fixes a problem in IE where an empty val throws an error
					if ( val == '' ) return el;
				}
				else alert( "Non-standard HTML tag (" + key + ") entered.\nSet xml option to true to use non-standard tags." );
					
//	since an Array is an instance of both Array and Object it has to sit first in the logic
				if ( val instanceof Array )
					this._children( val ).each( function( child ) {
						el.appendChild( child );
					} );
				else if ( val instanceof Object )
				{
//	Normally we would test for "HTMLElement" but stupid ie don't know what that is!
					if ( val.tagName ) el.appendChild( val );
					else if ( val._a || val._c )
					{
						if ( val._a )
							$H( val._a ).each( function( attr ) {
								if ( $USER.browser == 'Explorer' )
								{
									if ( this.IE_TRANS[attr[0]] ) el[this.IE_TRANS[attr[0]]] = attr[1];
									else if ( attr[0] == 'style' ) el.style.cssText = attr[1];
									else if ( /^on.+$/.test( attr[0] ) ) el[attr[0]] = new Function( attr[1] );
									else el.setAttribute( attr[0], attr[1] );
								}
								else el.setAttribute( attr[0], attr[1] );
							}.bind( this ) );
						if ( val._c )
						{
							if ( val.tagName ) el.appendChild( val._c );
							else
							{
								try { 
									this._children( val._c ).each( function( child ) {
										el.appendChild( child );
									} );
								}
								catch ( e ) { 
									if ( this._string( val._c ) ) el.appendChild( $B.eval( val._c ) ); 
								}
							}
						}
					}
					else el.appendChild( this.node( val, this.options ) );
				}
				else if ( this._string( val ) ) el.appendChild( $B.eval( val ) );
				return el;
			}, 
			_children : function( val )
			{
				if ( this._string( val ) ) return $B.eval( val );
				return val.collect( function( obj ) {
					return this.node( obj, this.options );
				}.bind( this ) );
			}, 
			_string : function( val )
			{
				return ( typeof val == 'string' || typeof val == 'number' );
			}
		};
		
/*	Dom loops through a series of JS Objects and/ or Strings with HTML in them and creates their DOM equivellants and returns them	*/
		IPC.CreateDOM = function( obj ) {
			if ( typeof obj == 'string' )
				return $B.eval( obj );
				
			var dom = {};
			for ( $_ in obj )
				dom[$_] = typeof obj[$_] == 'string' ? $B.eval( obj[$_] ) : $B.node( obj[$_] );
			return dom;
		};
		
		var $kill = IPC.Destroy = new function( obj ) {
			return delete obj;
		};
		
/*	creates a div with the class of ipc-loader which has the little AJAX indicator in it 
	and appends it to the element you passed in or document.body	*/
		IPC.DOM.Loader = '<div class="ipc-loader"></div>';
		IPC.Loader = Class.create();
		IPC.Loader.prototype = {
			initialize : function( element )
			{
				element = element || document.body;
				this.DOM = IPC.CreateDOM( IPC.DOM.Loader );
				
				this.element = $( element );
				if ( element != document.body )
					Element.setStyle( this.element, { position : 'relative' } );
					
				this.element.appendChild( this.DOM );
				Element.hide( this.DOM );
			}, 
			
			destroy : function() { 
				Element.setStyle( this.element, { overflow : 'visible' } );
				Element.remove( this.DOM );
			}, 
			hide : function() { 
				Element.setStyle( this.element, { overflow : 'visible' } );
				Element.hide( this.DOM );
			}, 
			show : function() { 
				Element.setStyle( this.element, { overflow : 'hidden' } );
				Element.show( this.DOM );
			}
		};
		
		IPC.DOM.ProgressBar = {
			container : '<div class="progress-bar"></div>', 
			label : '<h3></h3>', 
			current : '<span></span>', 
			bar : '<div class="p-bar"><div></div></div>' 
		};
		IPC.ProgressBar = Class.create();
		IPC.ProgressBar.prototype = {
			initialize : function( label, options )
			{
				this.DOM = IPC.CreateDOM( IPC.DOM.ProgressBar );
				this.options = $H( {
					labelComplete : 'Uploaded'
				} ).merge( options || {} );
				
				this.DOM.label.update( label );
				this._createProgressBar();
			}, 
			
			destroy : function()
			{
				Element.remove( this.DOM.container );
			}, 
			
//	KNOWN BUG: Prototype isn't setting the width correctly in Safari
			update : function( percent, text )
			{
				if ( percent )
				{
					if ( percent >= 100 )
					{
						Element.setStyle( Element.down( this.DOM.bar ), { 'width' : 'auto' } );
						this._handleComplete();
					}
					else
						Element.setStyle( Element.down( this.DOM.bar ), { 'width' : parseInt( percent ) + '%' } );
				}
				if ( text )
					this.DOM.current.update( text );
			}, 
			
			_createProgressBar : function()
			{
				this.DOM.container.appendChild( this.DOM.label );
				this.DOM.container.appendChild( this.DOM.bar );
				this.DOM.container.appendChild( this.DOM.current );
				
				this._handleInsertion();
			}, 
			
			_handleComplete : function()
			{
				if ( this.options.labelComplete )
					this.DOM.label.update( this.options.labelComplete );
				Element.addClassName( this.DOM.bar, 'p-complete' );
			}, 
			
			_handleInsertion : function()
			{
				var insertion = Object.keys( this.options.insertion )[0].replace( /^\w(.*)$/, Object.keys( this.options.insertion )[0].charAt( 0 ).toUpperCase() + "$1" );
				
				new IPC.Insertion[insertion]( this.DOM.container, Object.values( this.options.insertion )[0] );
			}
		};
		
		IPC.Insertion = {
			Abstract : {
				initialize : function( element, insertPoint )
				{
					this.element = $( element ) || $B.eval( element );
					this.insertPoint = $( insertPoint );
					this.handleInsertion();
				}
			}, 
			After : Class.create(),
			Before : Class.create(),
			Bottom : Class.create(),
			Top : Class.create()
		};
		IPC.Insertion.After.prototype = Object.extend( {
			handleInsertion : function() {
				if ( this.insertPoint.nextSibling )
					this.insertPoint.parentNode.insertBefore( this.element, this.insertPoint.nextSibling );
				else
					this.insertPoint.parentNode.appendChild( this.element );
					
				return this.element;
//					new IPC.Insertion.Bottom( this.element,  );
			}
		}, IPC.Insertion.Abstract );
		IPC.Insertion.Before.prototype = Object.extend( {
			handleInsertion : function() {
				this.insertPoint.parentNode.insertBefore( this.element, this.insertPoint );
				return this.element;
			}
		}, IPC.Insertion.Abstract );
		IPC.Insertion.Bottom.prototype = Object.extend( {
			handleInsertion : function() {
				this.insertPoint.appendChild( this.element );
				return this.element;
			}
		}, IPC.Insertion.Abstract );
		IPC.Insertion.Top.prototype = Object.extend( {
			handleInsertion : function() {
				this.insertPoint.insertBefore( this.element, this.insertPoint.firstChild );
				return this.element;
			}
		}, IPC.Insertion.Abstract );
		
/*	Unique ID - Will only assign one if you specify and not on any object that doesn't have an ID
	use $UID.create();	*/
		IPC.UniqueID = Class.create();
		IPC.UniqueID.prototype = {
			NAMESPACE : 'ipc_sys__', 
			initialize : function( namespace ) {
				this.ns = namespace || this.NAMESPACE;
				this.count = 0;
			}, 
			create : function()
			{
				var uid = this.ns + this.count;
				this.count++;
				return uid;
			}
		}
		var $UID = new IPC.UniqueID();
		
		IPC.CONFIG._namespace = 'ipc-';
		IPC.DOM._emptyNode = $B.eval( '<span />' );
		IPC.HANDLE._emptyFunction = Prototype.emptyFunction;

//	care of my pal si@jeffwad.com
		function testForPluginMimeType(mimeType)
		{
			try	{
				if(navigator.mimeTypes)	return navigator.mimeTypes[mimeType].enabledPlugin;
			} catch (e) { return false;	}
		}
		function testPluginsArray(pluginType)
		{
			var pl = { };
			pl.installed = false;
			if(navigator.plugins && navigator.plugins.length) {
				for(var i in navigator.plugins)	{
					plugin = navigator.plugins[i];
					if(plugin.name)	{
						if(plugin.name.indexOf(pluginType) !== -1) {
							pl.installed = true;
							if(plugin.description) pl.version = plugin.description.charAt(plugin.description.indexOf('.')-1);
							return pl;
						}
					}
				}
			}
			return pl;
		}
		function createActiveXObject(controlType)
		{
			try { return new ActiveXObject(controlType); }
			catch ( e ) { return false; }
		}
		
		IPC.decimalPlaces = function( num, decimalPlaces ) {
			re = eval( '/^([^\.]+)(\.\d{1,' + decimalPlaces + '}){0,1}.*$/' );
			return parseFloat( num.toString().replace( re, "$1$2" ) );
		} 
