// note : you must include ObjectUtils.js to use the classes in here.
/**
* This class is meant to be a javascript representation of a select
* box.  It mostly just wraps an html widget and adds a few convenience
* methods.  Most importantly, it allows the javascript event model
* to move events latterally, not just hierarchically.
*/
function SelectBox( referenceToHtmlSelectBox ) {

	referenceToHtmlSelectBox.jsRepresentation = this;
	if (referenceToHtmlSelectBox.addEventListener) {
		referenceToHtmlSelectBox.addEventListener("change", this.changed, false);
	}
	else if ( referenceToHtmlSelectBox.attachEvent ) {
		referenceToHtmlSelectBox.attachEvent("onchange", this.changed);
	}

//	addChangedListener( referenceToHtmlSelectBox, this.changed );	
	this.box = referenceToHtmlSelectBox;
	this.subscribers = new Array();
	this.nodeOptions = new Array();
}


/**
* Returns a list of appropriate sub selections for the currently selected option.
*/
SelectBox.prototype.getSubSelections =  function() {
	return this.nodeOptions[ this.box.value ];
}

// public:
SelectBox.prototype.changed = function () {
	// mozilla freaks out if it even sees the 'event' keyword, so
	// the cleaner 'if ( event )' doesn't work.
	try {
		widget = event.srcElement;
	}
	catch(e) {
		widget = this;
	}

	widget.jsRepresentation.publishChangeEvent();
}

// public:
SelectBox.prototype.addChangeEventSubscriber = function( changeEventSubscriber ) {
	this.subscribers.push( changeEventSubscriber );
}

// public:
SelectBox.prototype.handleChangeEvent = function( htmlEventSource, jsEventSource ) {
	this.setOptions(jsEventSource.getSubSelections());
}

// public:
SelectBox.prototype.setOptions = function( ArrayOfOptions ) {

	this.clearOptions();
	for ( var i = 0 ; i < ArrayOfOptions.length ; i++ )
		this.addOptionNode(ArrayOfOptions[i]);
		
	this.publishChangeEvent();
}

// public:
SelectBox.prototype.setSelectedValue = function( value ) {
	for ( var i = 0, n = this.box.options.length ; i < n ; i++ ) {
		if ( this.box.options[i].value == value ) {
			this.box.options[i].selected = true;
			this.publishChangeEvent();
			break;
		}
	}
}

// private:
SelectBox.prototype.publishChangeEvent = function() {
	for ( var i = 0 ; i <  this.subscribers.length ; i++ ) {
		subscriber = this.subscribers[i];
		subscriber.handleChangeEvent( this.box, this );
	}
}

// private:
SelectBox.prototype.addOptionNode = function( NodeFromTree ) {
	this.box.options[ this.box.options.length ] = new Option( NodeFromTree.getText(), NodeFromTree.getValue() );
	this.nodeOptions[ NodeFromTree.getValue() ] = NodeFromTree.getChildren();
}

// private:
SelectBox.prototype.clearOptions = function() {
	while ( this.box.options.length > 0 )
		this.box.options[ this.box.options.length - 1 ] = null;
}

/**
* This box will only display if there are options to select.
*/
function DisappearingSelectBox(referenceToHtmlSelectBox) {
	return new SelectBox(referenceToHtmlSelectBox);
}

DisappearingSelectBox.prototype = SelectBox.prototype;

DisappearingSelectBox.prototype.setOptions = function( ArrayOfOptions ) {

	this.clearOptions();	
	if ( !ArrayOfOptions || ArrayOfOptions.length == 0 ) {
		this.box.style.display = "none";
	}
	else {
		this.box.style.display = "inline";
		for ( var i = 0 ; i < ArrayOfOptions.length ; i++ )
			this.addOptionNode(ArrayOfOptions[i]);		
	}
	
	this.publishChangeEvent();
}
