/**
* Tree expander. (c) manro <formanro@ua.fm> 2009
* This class supports multiple trees in document, replaces icons of compacted/expanded nodes, applies styles for these nodes.
*/
BrowseTree = function( argAffectedId )
{
		// THINK: id instead of class

		// Define constants
		BrowseTree.ICO_MODE_BG = 'bg';
		BrowseTree.ICO_MODE_IMG = 'img';

		// default properties
		this.affectedIds = new Array( 'menu' );
		this.hiddenClass = 'hidden';
		this.tagsChildren = new Array( 'li', 'dt' );
		this.tagsContainer = new Array( 'ul', 'dd' );
		this.tagsControl = new Array( 'a' );
		this.icoExpandPostfix = '-exp';
		this.icoCompactPostfix = '-comp';

		// image names
		this.icoExpand = '';
		this.icoCompact = '';
		this.icoMode = BrowseTree.ICO_MODE_BG;

		// affected class assignement
		var t = typeof( argAffectedId );
		if( t.toLowerCase().indexOf( 'string' ) > -1 ) {
				this.affectedIds = new Array( argAffectedId );
		} else if( t.toLowerCase().indexOf( 'array' ) > -1 || t.toLowerCase().indexOf( 'object' ) > -1 ) {
				this.affectedIds = argAffectedId;
		}

		// attaching listeners
		var obj = this;
		attachToggle = function( a, e )
		{
				var o = a[ 0 ];

				func = function( e ) {
						obj.toggle( e, a[ 0 ], a[ 1 ], a[ 2 ], a[ 3 ] );
				}

				if( o.addEventListener ) { o.addEventListener( e, func, false ); }
				else if( o.attachEvent ) { o.attachEvent( 'on' + e, func ); }
				else return null;
		}

		var El;
		var Controls = new Array();

		var El;
		for( var i = 0; i < this.affectedIds.length; i++ ) {
				El = document.getElementById( this.affectedIds[ i ] );
				if( typeof( El ) !== 'undefined' && El ) {
						this.recGetNeedleElements( El, Controls, this.affectedIds[ i ] );
				}
		}

		for( var i = 0; i < Controls.length; i++ ) {
				attachToggle( Controls[ i ], 'click' );
		}
}

BrowseTree.prototype.recGetNeedleElements = function( ArgEl, argAggregat, argAffectedId )
{
		var Child;
		for( var i = 0; i < ArgEl.childNodes.length; i++ ) {
				Child = '';

				// only elements with tags
				if( typeof( ArgEl.childNodes[ i ].tagName ) !== 'undefined' );
				else {
						continue;
				}

				// is this <li>
				for( var j = 0; j < this.tagsChildren.length; j++ ) {
						if( ArgEl.childNodes[ i ].tagName.toUpperCase() == this.tagsChildren[ j ].toUpperCase() ) {
								Child = ArgEl.childNodes[ i ];
								break;
						}
				}

				if( Child );
				else {
						continue;
				}

				var El, PrevEl;
				for( var j = Child.childNodes.length - 1; j >= 0; j-- ) {

						El = Child.childNodes[ j ];

						// only non-text nodes
						if( typeof( El.tagName ) !== 'undefined' );
						else {
								continue;
						}

						// search for nested <ul>
						for( var k = 0; k < this.tagsContainer.length; k++ ) {
								if( El.tagName.toUpperCase() == this.tagsContainer[ k ].toUpperCase() ) {
										this.recGetNeedleElements( El, argAggregat, argAffectedId );
								}
						}

						// search for nested <a>
						if( PrevEl && this.tagsContainer.toString().toUpperCase().indexOf( PrevEl.tagName.toUpperCase() ) > -1 ) {
								if( this.tagsControl.toString().toUpperCase().indexOf( El.tagName.toUpperCase() ) > -1 ) {
										// now, El becomes "Control" (a element), PrevEl -- "Child" (nested <ul>), Child (child of root <ul>, parent for nested ul and control) -- "Parent". Also, we append argAffectedId as class name of root element. These info will be used in attached functions
										argAggregat[ argAggregat.length ] = new Array( El, PrevEl, Child, argAffectedId  );
								}
						}

						PrevEl = El;
				}
		}
}

BrowseTree.prototype.toggle = function( e, ArgControl, ArgChild, ArgParent, argAffectedId )
{
		var className = ArgChild.getAttribute( 'className' )? ArgChild.getAttribute( 'className' ) : ArgChild.getAttribute( 'class' );
		var flag = false;

		if( className ) {

				if( this.isClassMatch( this.hiddenClass, className ) ) {
						flag = true;

						if( ArgChild.style.display == 'none' || !ArgChild.style.display ) {
								// expand
								this.expand( ArgControl, ArgChild, ArgParent, argAffectedId );
						} else {
								// compact
								this.compact( ArgControl, ArgChild, ArgParent, argAffectedId );
						}

				}

				if( !flag ) {
						// not have hidden class...
						if( ArgChild.style.display == 'none' ) {
								// expand
								this.expand( ArgControl, ArgChild, ArgParent, argAffectedId );
						} else {
								// compact
								this.compact( ArgControl, ArgChild, ArgParent, argAffectedId );
						}

				}


		} else {
				// just expand/compact
				if( ArgChild.style.display == 'none' ) {
						this.expand( ArgControl, ArgChild, ArgParent, argAffectedId );
				} else {
						this.compact( ArgControl, ArgChild, ArgParent, argAffectedId );
				}

		}

		// all classes to array;
}

BrowseTree.prototype.expand = function( ArgControl, ArgChild, ArgParent, argAffectedId )
{
		// show/compact
		ArgChild.style.display = 'block';

		// style
		var style = this.getStyleForSelector( '#' + argAffectedId + ' ' + ArgParent.tagName.toLowerCase() + ' .expand' );
		if( style ) {
				ArgControl.style.cssText = style.cssText
		}

		// ico ;)
		this.replaceIco( ArgControl,ArgChild, ArgParent, argAffectedId, this.icoCompact, this.icoCompactPostfix );
}

BrowseTree.prototype.compact = function( ArgControl, ArgChild, ArgParent, argAffectedId )
{
		// hide
		ArgChild.style.display = 'none';

		// style
		var style = this.getStyleForSelector( '#' + argAffectedId + ' ' + ArgParent.tagName.toLowerCase() + ' .compact' );
		if( style ) {
				ArgControl.style.cssText = style.cssText
		}

		// ico ;)
		this.replaceIco( ArgControl,ArgChild, ArgParent, argAffectedId, this.icoExpand, this.icoExpandPostfix );
}

BrowseTree.prototype.replaceIco = function( ArgControl, ArgChild, ArgParent, argAffectedId, argIco, argIcoPostfix )
{
		if( this.icoMode == BrowseTree.ICO_MODE_BG ) {
				this.replaceIcoBg( ArgControl, ArgChild, ArgParent, argAffectedId, argIco, argIcoPostfix );
		} else {
				this.replaceIcoImg( ArgControl, ArgChild, ArgParent, argAffectedId, argIco, argIcoPostfix );
		}
}

BrowseTree.prototype.replaceIcoImg = function( ArgControl, ArgChild, ArgParent, argAffectedId, argIco, argIcoPostfix )
{
		// get child <img> of ArgControl
		var el = false;
		for( var i = 0; i < ArgControl.childNodes.length; i++ ) {

				if( ArgControl.childNodes[ i ].tagName && ArgControl.childNodes[ i ].tagName.toUpperCase() == 'IMG' ) {
						el = ArgControl.childNodes[ i ];
				}
		}


		if( argIco ) {
				el.src = argIco;

		} else if( this.icoExpand && this.icoExpand != -1 ) {

				var re = new RegExp;
				re.compile( '(.+)' + this.icoExpandPostfix + '(\..+)$' );

				var matches = re.exec( this.icoExpand );
				// okay, we found it
				if( matches.length == 3 ) {
						// replace with new image
						el.src = matches[ 1 ] + argIcoPostfix + matches[ 2 ];
						// storing this.icoCompact
						if( !this.icoCompact && argIcoPostfix == this.icoCompactPostfix ) {
								this.icoCompact = matches[ 1 ] + argIcoPostfix + matches[ 2 ];
						}
				}

		} else if( this.icoExpand != -1 ) {

				if( !el.src ) {
						this.icoExpand = -1;
				} else {
						this.icoExpand = el.src;
						// try it again
						this.replaceIcoImg( ArgControl, ArgChild, ArgParent, argAffectedId, argIco, argIcoPostfix );
				}
		}
}

BrowseTree.prototype.replaceIcoBg = function( ArgControl, ArgChild, ArgParent, argAffectedId, argIco, argIcoPostfix )
{

		// there is image
		if( argIco ) {
				ArgControl.style.backgroundImage = 'url(' + argIco + ')';

				// there is core image from styles
		} else if( this.icoExpand && this.icoExpand != -1 ) {
				// substitute

				var re = new RegExp;
				re.compile( '(.+)' + this.icoExpandPostfix + '(\..+)$' );

				var matches = re.exec( this.icoExpand );
				// okay, we found it
				if( matches.length == 3 ) {
						// replace with new image
						ArgControl.style.backgroundImage = 'url(' + matches[ 1 ] + argIcoPostfix + matches[ 2 ] + ')';
						ArgControl.style.backgroundRepeat = 'no-repeat';

						// storing this.icoCompact
						if( !this.icoCompact && argIcoPostfix == this.icoCompactPostfix ) {
								this.icoCompact = matches[ 1 ] + argIcoPostfix + matches[ 2 ];
						}

				}

		} else if( this.icoExpand != -1 ) {
				// try to find
				this.icoExpand = this.getBackgroundImageUrl( ArgControl, ArgChild, ArgParent, argAffectedId );

				// try it again
				if( this.icoExpand != -1 ) {
						this.replaceIcoBg( ArgControl, ArgChild, ArgParent, argAffectedId, argIco, argIcoPostfix );
				}
		}
}

BrowseTree.prototype.getBackgroundImageUrl = function( ArgControl, ArgChild, ArgParent, argAffectedId )
{
				// we searching for style for .class li a
		var style = this.getStyleForSelector( '#' + argAffectedId + ' ' + ArgParent.tagName.toLowerCase() + ' ' + ArgControl.tagName.toLowerCase() );

		// ============================================
		// !!! NOTE !!!
		// due to bug in firefox, we support only background-image: url() and background: url() without of quotes


		var re = /url\(\s?(.+?)\s?\)/;
		var string = style.backgroundImage ? style.backgroundImage : style.background;
		var matches = re.exec( string );

		if( matches.length == 2 ) {
				return matches[ 1 ];
		} else {
				return -1;
		}
}


BrowseTree.prototype.isClassMatch = function( argNeedle, argClass )
{
		if( !argClass ) {
				return;
		}

		var re = /[\w-]+/g;
		var matches = argClass.match( re );

		for( var i = 0; i < matches.length; i++ ) {
				if( matches[ i ] == argNeedle ) {
						return true;
				}
		}
		return false;
}

BrowseTree.prototype.getStyleForSelector = function( argPositive, argNegative, argContinous )
{
		var limit = 1000;

		/* TODO: document.styleSheets[ 0 ] => for( var i = 0; i < document.styleSheets.length; i++ ).... */

		if( typeof( argContinous ) == 'undefined' ) {
				argContinous = false;
		}

		if( !argPositive ) {
				return;
		} else {
				argPositive = argPositive.replace( /\./, '\\.' );
				eval( 'argPositive = /' + argPositive + '/i' );
		}

		if( typeof( argNegative ) == 'undefined' || !argNegative ) {
				var negative_length = 0;
		} else {
				argNegative = argNegative.replace( /\./, '\\.' );
				eval( 'argNegative = /' + argNegative + '/i' );
		}

		var text;
		var continous = '';
		if( typeof( document.styleSheets ) !== 'undefined' && typeof( document.styleSheets[ 0 ] ) !== 'undefined'	) {

				if( typeof( document.styleSheets[ 0 ].cssRules ) !== 'undefined' ) {
						if( !argNegative ) {
								for( var i = 0; i < document.styleSheets.length; i++ ) {
										styleSheet = document.styleSheets[ i ];

								for( var j = 0; j < document.styleSheets[ 0 ].cssRules.length && j < limit; j++ ) {
										text = document.styleSheets[ 0 ].cssRules[ j ].selectorText;
										if( argPositive.test( text ) ) {
												if( !argContinous ) {
														return document.styleSheets[ 0 ].cssRules[ j ].style;
												} else {
														continous += document.styleSheets[ 0 ].cssRules[ j ].style.cssText;
												}
										}
								}
								}
						} else {
								for( var i = 0; i < document.styleSheets.length; i++ ) {
										styleSheet = document.styleSheets[ i ];

										for( var j = 0; j < document.styleSheets[ 0 ].cssRules.length && j < limit; j++ ) {
												text = document.styleSheets[ 0 ].cssRules[ j ].selectorText;
												if(  argPositive.test( text )
														 && !argNegative.test( text ) ){
														if( !argContinous ) {
																return document.styleSheets[ 0 ].cssRules[ j ].style;
														} else {
																continous += document.styleSheets[ 0 ].cssRules[ j ].style.cssText;
														}
												}
										}
								}
						}

						/* IE.win */
				} else if( typeof( document.styleSheets[ 0 ].rules ) !== 'undefined' ) {

						if( !argNegative ) {
								for( var i = 0; i < document.styleSheets.length; i++ ) {
										styleSheet = document.styleSheets[ i ];
								for( var j = 0; j < document.styleSheets[ 0 ].rules.length && j < limit; j++ ) {
										text = document.styleSheets[ 0 ].rules[ j ].selectorText;
										if( argPositive.test( text ) ) {
												if( !argContinous ) {
														return document.styleSheets[ 0 ].rules[ j ].style;
												} else {
														continous += document.styleSheets[ 0 ].rules[ j ].style.cssText;
												}
										}
								}
								}
						} else {
								for( var i = 0; i < document.styleSheets.length; i++ ) {
										styleSheet = document.styleSheets[ i ];
								for( var j = 0; j < document.styleSheets[ 0 ].rules.length && j < limit; j++ ) {
										text = document.styleSheets[ 0 ].rules[ j ].selectorText;
										if( argPositive.test( text )
												&& !argNegative.test( text ) ) {
												if( !argContinous ) {
														return document.styleSheets[ 0 ].rules[ j ].style;
												} else {
														continous += document.styleSheets[ 0 ].rules[ j ].style.cssText;
												}
										}
								}
								}
						}
				}

				/* adding new dummy rule */
				if( argContinous && continous && document.styleSheets.length ) {
						/* moz */
						if( typeof( document.styleSheets[ 0 ].cssRules ) !== 'undefined' ) {
								document.styleSheets[ 0 ].insertRule( '#dummy {' + continous + '}', document.styleSheets[ 0 ].cssRules.length );
								return document.styleSheets[ 0 ].cssRules[ document.styleSheets[ 0 ].cssRules.length - 1 ];

						} else if( typeof( document.styleSheets[ 0 ].rules ) !== 'undefined' ) {
								document.styleSheets[ 0 ].addRule( '#dummy', continous, document.styleSheets[ 0 ].rules.length );
								return document.styleSheets[ 0 ].rules[ document.styleSheets[ 0 ].rules.length - 1 ];

						} else return false;


				} else {
						return false;
				}
		}

		return false;
}

