﻿var CascadingDropDownManager = {
	levelNodes: [],
	referenceCache: [], /* used for validation */
	
	Init: function() {
		if ( typeof( cddtree ) != 'undefined' ) {
			CascadingDropDownManager.BreakListItemArrayIntoLevels(cddtree, 0);
			
			var container = p$('dynPage');
			var ddlist = document.getElementsByClassName( 'cddel', container );
			for ( i=0; i<ddlist.length; i++ ) {
				var pattern = /fosb(\d+)/ig;
				var matches = pattern.exec( ddlist[i].id );
				foobjid = parseInt(matches[1]);				
				CascadingDropDownManager.referenceCache[foobjid] = new CascadingDropDown( foobjid, ddlist[i], 0 );
			}
		}
	},
	
	ValidateElement: function( sender, args ) {
		args.IsValid = false;
        var controltovalidate = p$(sender.controltovalidate);
        if ( controltovalidate ) {
			var objid = controltovalidate.id;
			var pattern = /fosb(\d+)/ig;
			var matches = pattern.exec( objid );
			if ( !matches ) matches = pattern.exec( objid ); /* fix for strange behaviour in FF */
			foobjid = parseInt(matches[1]);
			args.IsValid = CascadingDropDownManager.ValidateObjectChain( foobjid );
		}
	},
	
	ValidateObjectChain: function( foobjid ) {
		var node = CascadingDropDownManager.referenceCache[foobjid];
		if ( node ) {
			var retVal = true;
			var cancel = false;
			while ( node && !cancel ) {
				if ( node.selectElement.options.selectedIndex == 0 ) {
					retVal = false;
					cancel = true;
				}
				node = node.childObject;
			}
			return retVal;
		} else {
			return true;
		}
	},
	
	/* This is used to get better performance while rendering subitems.
	   Using this method the code only needs to search for subitems for a selected item
	   in the corresponding level, not the whole tree. */
	BreakListItemArrayIntoLevels: function(list, level) {
		CascadingDropDownManager.levelNodes[level] = [];
		for ( var i = 0; i < list.length; i++ ) {
			CascadingDropDownManager.levelNodes[level].push( list[i] );
			if ( list[i].children != null ) {
				CascadingDropDownManager.BreakListItemArrayIntoLevels( list[i].children, level+1 );
			}
		}
	}
}
Event.observe(window, 'load', CascadingDropDownManager.Init, false);

var CascadingDropDown = Class.create();
CascadingDropDown.prototype = {
    associatedFormobjId: 0,
    selectElement: null,
    elementToControl: null,
    listItemNodes: null,
    parentObject: null,
    childObject: null,
    orgOnChangeEvent: null,
	
	initialize: function( fobjid, selElement, hierarchyLevel ) {
		this.associatedFormobjId = fobjid;
		this.selectElement = selElement;
		this.listItemNodes = CascadingDropDownManager.levelNodes[hierarchyLevel];
		var relAttrib = this.selectElement.readAttribute('rel');
		
		if ( relAttrib ) {
			this.elementToControl = p$(relAttrib);
			
			if ( hierarchyLevel == 0 ) { /* save original onchange-event for use on cascaded dropdowns (validation etc) */
				if ( this.selectElement.onchange )
					this.orgOnChangeEvent = this.selectElement.onchange;
			}
				
			/* setup object relationships for value serialization */
			this.childObject = new CascadingDropDown( this.associatedFormobjId, this.elementToControl, hierarchyLevel+1 );
			this.childObject.parentObject = this;
			
			Event.observe( this.selectElement, 'change', this.onChange.bind(this), false);
		} else {
			/* last select in hierarchy should only serialize value, not populate other select */
			Event.observe( this.selectElement, 'change', this.serializeValue.bind(this), false); 		
		}
	}, 
	
	getAndExecuteRootChangeEventHandler: function() {
		if ( this.orgOnChangeEvent ) {
			if ( Browser.isIE ) {
				ValidatorOnChange( {srcElement: this.selectElement} ); /* ugly fix for ie which cant override the event-attribute (it seems) */
			}
			else this.orgOnChangeEvent( {target: this.selectElement} /* Masquerade event attribute object needed for .NET javascript validation */ );
		} else {
			var currObj = this;
			while ( currObj.parentObject && !currObj.orgOnChangeEvent ) {
				currObj = currObj.parentObject;
			}
			if ( currObj.orgOnChangeEvent )
				if ( Browser.isIE ) {
					ValidatorOnChange( {srcElement: currObj.selectElement} ); /* ugly fix for ie which cant override the event-attribute (it seems) */
				}
				else currObj.orgOnChangeEvent({target: currObj.selectElement} /* see comment above */ );
		}
	},
	
	onChange: function() {
		var selectedId = this.getElementValue(this.selectElement);
		var nodeSet = this.getNodeSetById( selectedId );
		
		if ( nodeSet && nodeSet.children ) {
			this.elementToControl.options.length = 0;
			for(i=0;i<nodeSet.children.length; i++ ) {
				this.elementToControl.options[this.elementToControl.options.length] = new Option(nodeSet.children[i].value, nodeSet.children[i].id);
			}
			this.elementToControl.removeAttribute("disabled");
		} else {
			this.cascadingReset();
		}
		this.serializeValue();
	},
	
	serializeValue: function() {
		var value = this.getElementValue( this.selectElement );
		if ( this.parentObject ) {
			var currObj = this;
			while ( currObj.parentObject ) {
				value = this.getElementValue( currObj.parentObject.selectElement ) + ',' + value;
				currObj = currObj.parentObject;
			}
		}
		
		this.getOrCreateHiddenField().value = value;
		this.getAndExecuteRootChangeEventHandler();
	},
	
	getNodeSetById: function( nodeId ) {
		var retVal = null;
		for(i=0; i<this.listItemNodes.length; i++) {
			if ( this.listItemNodes[i].id == nodeId ) 
				retVal = this.listItemNodes[i];
		}
		return retVal;
	},
	
	cascadingReset: function() {
		if ( this.elementToControl ) {
			var a = document.createAttribute("disabled");
			a.nodeValue = "disabled";
			this.elementToControl.setAttributeNode(a);
			this.elementToControl.options.selectedIndex = 0;
			
			if ( this.childObject ) {
				this.childObject.cascadingReset();
			}
		}
	},
	
	getOrCreateHiddenField: function() {
		var obj = p$('fo' + this.associatedFormobjId );
		if ( !obj ) {
			var obj = document.createElement("input");
			obj.setAttribute("type","hidden");
			obj.setAttribute("id","fo"+ this.associatedFormobjId );
			document.forms[0].appendChild(obj);
		}
		return obj;
	},
	
	getElementValue: function(select) {
		return select.options[select.options.selectedIndex].value;
	}
}