﻿Type.registerNamespace("NS.UI");

Date.prototype.addMilliseconds = function(value){
	/// <param name="value" type="Number" integer="true"></param>
	this.setTime(this.getTime() + value);return this;
}
Date.prototype.addSeconds = function(value){
	/// <param name="value" type="Number" integer="true"></param>
	this.addMilliseconds(value*1000);return this;
}
Date.prototype.addMinutes = function(value){
	/// <param name="value" type="Number" integer="true"></param>
	this.addMilliseconds(value*60000);return this;
}
Date.prototype.addHours = function(value){
	/// <param name="value" type="Number" integer="true"></param>
	this.addMilliseconds(value*3600000);return this;
}
Date.prototype.addDays = function(value){
	/// <param name="value" type="Number" integer="true"></param>
	this.setDate(this.getDate()+value);return this;
}
Date.prototype.addMonths = function(value){
	/// <param name="value" type="Number" integer="true"></param>
	this.setMonth(this.getMonth()+value);return this;
}
Date.prototype.addYears = function(value){
	/// <param name="value" type="Number" integer="true"></param>
	this.setFullYear(this.getFullYear()+value);return this;
}

//
NS.UI.hideElements = function(tagNames, TorF){
	/// <param name="value" type="Array" elementType="String"></param>
	/// <param name="TorF" type="Boolean"></param>
	for(var j=0;j<tagNames.length;j++){
		var t = document.getElementsByTagName(tagNames[j]);
		for(var i=0;i<t.length;i++){t[i].style.visibility = TorF? "hidden" : "";}
	}
}
//Generic window overlay
NS.UI._Overlay = function(element){
	/// <param name="element" domElement="true"></param>
	document.body.appendChild(element);
	NS.UI._Overlay.initializeBase(this,[element]);
}
NS.UI._Overlay.prototype = {
	_interval: null,_fadeIn: false,_opacity: 0,_max: 60,_fadeDelegate:null,_resizeHandler:null, _step:20,
	show: function(){NS.UI.hideElements(["iframe","object","embed","select"],true);this._fade(true);},
	hide: function(){NS.UI.hideElements(["iframe","object","embed","select"],false);this._fade(false);},

	add_fadeComplete:function(handler){
		/// <param name="fadeIn" type="Boolean"></param>
		this.get_events().addHandler("fadeComplete", handler);
	},
	remove_fadeComplete:function(handler){
		/// <param name="fadeIn" type="Boolean"></param>
		this.get_events().removeHandler("fadeComplete", handler);
	},

	initialize:function(){
		var element = this.get_element();
		element.id = "window_overlay";
		element.style.zIndex = "100000";
		element.style.width = "100%";
		this.set_opacity(0);
		Sys.UI.DomElement.setLocation(element,0,0);
		this._setHeight();
		this._resizeHandler = Function.createDelegate(this, this._setHeight);
		$addHandler(window, "resize", this._resizeHandler);
	},
	_setHeight: function(){
		var a=document.body.scrollHeight;//scrolled content
		var b=document.documentElement.clientHeight;//no scroll
		var c=document.documentElement.scrollHeight;//ie7 friendly
		a = ((a>c)?a:c);
		this.get_element().style.height=((a>b)?a:b)+"px";
	},
	_fade: function(fadeIn){
		/// <param name="fadeIn" type="Boolean"></param>
		this._fadeIn = fadeIn;
		if(fadeIn) {this._element.style.visibility='visible'; this._element.style.display='';} else {this._element.style.visibility='hidden'; this._element.style.display='none'; }
		if(!this._fadeDelegate)this._fadeDelegate = Function.createDelegate(this,this._tick);
		this._interval = window.setInterval(this._fadeDelegate, 100);
	},
	_tick: function(){
		if(!this._interval) return;
		var increase = this._step;
		if(!this._fadeIn) increase*=-1;
		var newOpacity = this._opacity + increase;
		if(newOpacity<0) newOpacity = 0;
		else if(newOpacity>this._max) newOpacity = this._max;
		this.set_opacity(newOpacity);
		if(newOpacity<=0 || newOpacity>=this._max){
			window.clearInterval(this._interval);
            var fadeComplete = this.get_events().getHandler("fadeComplete");
            if (fadeComplete) fadeComplete(this, Sys.EventArgs.Empty);
		}
	},
	get_opacity:function(){return this._opacity;},
	set_opacity: function(value){
		/// <param name="value" type="Number" integer="true"></param>
		this._opacity = value;
		var s = this.get_element().style;
		s.opacity = value*.01;
		s.filter = "alpha(opacity="+value+")";
		if(value===0) this.set_visible(false);
	},
	dispose:function(){
		$removeHandler(window, "resize", this._resizeHandler);
		this._fadeDelegate=null;
		NS.UI._Overlay.callBaseMethod(this, 'dispose');
	}
}
NS.UI._Overlay.registerClass('NS.UI._Overlay', Sys.UI.Control);
window.get_overlay = function(){if(!window._overlay)window._overlay=$create(NS.UI._Overlay,null,null,null,document.createElement("div"));return window._overlay;};



NS.UI._Popup = function(element){
	/// <param name="element" domElement="true"></param>
	this._box = element.firstChild;
	NS.UI._Popup.initializeBase(this,[element]);
}
NS.UI._Popup.prototype = {
	_box:null,
	_state:null,
	_defaultButton:null,
	_callback:null,
	_globalCss:null,
	_top:null,
	_left:null,
	
	get_top: function() {return this._top;},
	set_top: function(value) {this._top = value;},
	
	get_left: function() {return this._left;},
	set_left: function(value) {this._left = value;},

	get_state: function(){return this._state;},
	set_state: function(value){this._state = value;},

	get_title: function(){return this._box.childNodes[1].innerHTML;},
	set_title: function(value){this._box.childNodes[1].innerHTML = value;},

	get_content: function(){return this._box.childNodes[2].innerHTML;},
	set_content: function(value){if (typeof(value) === 'object') {this._box.childNodes[2].appendChild(value);} else {this._box.childNodes[2].innerHTML = value;}},

	get_callback: function(){return this._callback;},
	set_callback: function(value){this._callback = value;},

	add_button: function(text, isDefault){
	    var s = document.createElement("span");
	    s.className = 'nsDefBtn';
		var b=document.createElement("input");
		b.type="button";
		b.value=text;
		if(isDefault)
			this._defaultButton = b;
		$addHandlers(b,{click:this._buttonClick},this);
		s.appendChild(b);
		this._box.lastChild.appendChild(s);
		return b;
	},

 	show: function(callBack){
		if(NS.UI._Popup._activePopup)throw "A Popup is already active.";
		if(callBack)this._callback = callBack;
		window.get_overlay().show();
		
		// control popup position through property settings
		if (this._top !== null && this._left !== null){
			this._element.style.top = this._top + 'px';
			this._element.style.left = this._left + 'px';
		}
		
		this._element.style.display='';
		this._element.style.visibility='visible';
		//var exclude = NS.Utils.Selector.query("iframe,object,embed,select", this._element);
		//for (var i=0;i<exclude.length;i++) {exclude[i].style.visibility='';}
		if(this._defaultButton)this._defaultButton.focus();
		NS.UI._Popup._activePopup = this;
		
		if (this._globalCss !== null){
			// add the style to fix the popup position
			this._top = (this._top == null ? this._element.offsetTop : this._top);							
			this._globalCss.addRule('.popup_wrapper', 'top:expression( (ignoreMe = document.body.scrollTop + ' + this._top + ') + "px" )');		
		}
	},
	_hide: function(){
		window.get_overlay().hide();
		this._element.style.display='none';
		NS.UI._Popup._activePopup=null;
		
		if (this._globalCss !== null)
			this._globalCss.removeRule(this._globalCss.rules.length-1);
	},
	_buttonClick: function(e){
		this._hide();
		var cb=this._callback;
		if(cb)cb(this, e);
	},
	initialize: function(){
		var element = this.get_element();
		//element should always have a parent because it should have been created by a template
		element.parentNode.removeChild(element);
		document.body.appendChild(element);
		this._box.firstChild.isCancel=true; //red-x
		$addHandlers(this._box.firstChild,{click:this._buttonClick},this);
		NS.UI._Popup.callBaseMethod(this, 'initialize');
		
		if (typeof(window.pageXOffset) === 'undefined'){
			for (var i = 0; i < document.styleSheets.length; i++){				
				if (/global(\d)*(\.debug)*\.css/.test(document.styleSheets[i].href)){					
					this._globalCss = document.styleSheets[i];					
				}
			}
		}
	},
	dispose: function(){
		var buttons = this._box.lastChild.childNodes;
		for(var i=0;i<buttons.length;i++)
			$clearHandlers(buttons[i]);
		NS.UI._Popup.callBaseMethod(this, 'dispose');
	}
}
NS.UI._Popup.registerClass('NS.UI._Popup', Sys.UI.Control);
NS.UI._Popup._activePopup=null;

NS.UI.Popup=function(){throw "Cannot instantiate static class.";}
NS.UI.Popup.create=function(content, title, callback){
	/// <param name="content" type="String"></param>
	/// <param name="title" type="String"></param>
	/// <param name="callback" type="Function" mayBeNull="true" optional="true"></param>
	/// <returns type="NS.UI.Popup"></returns>
	var temp=document.createElement("div");	
    temp.innerHTML="<div class='popup_wrapper' style='z-index:1000001;left:0px;width:100%;display:none;visibility:hidden;'><div class='popup_box'><a class='popup_x'></a><div class='popup_title'></div><div class='popup_content'></div><div class='popup_buttons'></div></div></div>";
	return $create(NS.UI._Popup,{title:title, content:content, callback:callback},null,null,temp.firstChild);
}

NS.UI.Popup.generic = function(message, buttonText, callback, title){
	/// <param name="message" type="String"></param>
	/// <param name="buttonText" type="String"></param>
	/// <param name="callback" type="Function" mayBeNull="true" optional="true"></param>
	/// <returns type="NS.UI.Popup"></returns>
	if (typeof(title) == 'undefined')
	    title = '';
	var template = document.createElement("div");
	var p = NS.UI.Popup.create(message,title);
	p.add_button(buttonText);
	template.innerHTML=message;
	p.show(callback);
	return p;
}
NS.UI.Popup.alert = function(message, callback, title){
	/// <param name="message" type="String"></param>
	/// <param name="callback" type="Function" mayBeNull="true" optional="true"></param>
	new NS.UI.Popup.generic(message,'OK',callback, title);
}
NS.UI.Popup.confirm = function(message, callback){
	/// <param name="message" type="String"></param>
	/// <param name="callback" type="Function" mayBeNull="true" optional="true"></param>
	var p=new NS.UI.Popup.generic(message,'Yes',callback);
	p.add_button('No');
}
NS.UI.Popup.registerClass('NS.UI.Popup');



NS.WebRequest=function(){throw "Cannot instantiate static class.";}
NS.WebRequest.invoke = function(path, useGet, params, onSuccess, onFailure, userContext, timeout) {
	/// <param name="path" type="String"></param>
	/// <param name="useGet" type="Boolean"></param>
	/// <param name="params"></param>
	/// <param name="onSuccess" type="Function" mayBeNull="true" optional="true"></param>
	/// <param name="onFailure" type="Function" mayBeNull="true" optional="true"></param>
	/// <param name="userContext" mayBeNull="true" optional="true"></param>
	/// <returns type="Sys.Net.WebRequest"></returns>
	if (!params) params = {};
	var request = new Sys.Net.WebRequest();
	if (!useGet) {
		if(typeof params==="string")
		var body = (typeof params!=="string")? Sys.Serialization.JavaScriptSerializer.serialize(params) : params;
		if (body === "{}") body = "";
		request.set_body(body);
	}
	request.set_url(Sys.Net.WebRequest._createUrl(path, (useGet)?params:{}));

	request.add_completed(onComplete);
	if (timeout && timeout > 0) request.set_timeout(timeout);
	request.invoke();

	function onComplete(response, eventArgs) {
		if (response.get_responseAvailable()) {
			var statusCode = response.get_statusCode();

			var result = null;
			try {
				var contentType = response.getResponseHeader("Content-Type");
				if (contentType.startsWith("application/json"))
					result = response.get_object();
				else if (contentType.startsWith("text/xml"))
					result = response.get_xml();
				else result = response.get_responseData();
			} catch (ex) {}

			//handle errors
			if ((statusCode < 200) || (statusCode >= 300)) {
				if (onFailure) {
					if (!result) {
						result = new Sys.Net.WebServiceError(false , "WebRequest failed for an unknown reason.", "", "");
					}
					result._statusCode = statusCode;
					onFailure(result, userContext);
				}
				else {//dev doesn't want to handle the error, just alert it
					var error;
					if (result)
						error = result.get_exceptionType() + "-- " + result.get_message();
					else error = response.get_responseData();
					window.alert("WebRequest Failed: "+error);
				}
			}
			else if (onSuccess) {
				onSuccess(result, userContext);
			}
		}
		else {
			var msg;
			if (response.get_timedOut()) msg = "WebRequest timed out.";
			else msg = "WebRequest failed for an unknown reason.";

			if (onFailure) onFailure(new Sys.Net.WebServiceError(response.get_timedOut(), msg, "", ""), userContext);
			else alert(msg);
		}
	}
	return request;
}
NS.WebRequest.registerClass('NS.WebRequest');




NS.UI.Tooltip = function (element){
	// Create the div element
	this._tipDiv = document.createElement("div");
	this._tipDiv.innerHTML = '<div class="innerwrapper origin_tl arrow_lt"><div class="closeButton"></div><div class="arrow"></div><div class="corner"></div><div class="top"></div><div class="content"></div></div>';
	this._tipDiv.className = 'tooltip';	
	NS.UI.Tooltip.initializeBase(this,[element]);
}
	
NS.UI.Tooltip.prototype = {
	_tipDiv: null,
	get_classes : function(){return this._tipDiv.firstChild.className;},
	set_classes : function(value){this._tipDiv.firstChild.className = value;},
	get_content : function(){return this._tipDiv.firstChild.lastChild.innerHTML;},
	set_content : function(value){this._tipDiv.firstChild.lastChild.innerHTML = value;},	
	get_tipDiv : function(){return this._tipDiv;},
	set_tipDiv : function(value){this._tipDiv = value;},	
	initialize : function(){		
        // Add the div element to the current objetc
		this._element.appendChild(this._tipDiv); 
		
		// attach a close event to the close button image
		$addHandler(this._tipDiv.firstChild,"click", Function.createDelegate(this,function(){this._tipDiv.style.display='none';}));
	},
	dispose : function(){
	  $clearHandlers(this._element);	  
	  NS.UI.Tooltip.callBaseMethod(this, 'dispose');
	},
    show : function(){
      var bounds = Sys.UI.DomElement.getBounds(this.get_element());
      this._tipDiv.style.top = (bounds.y + bounds.height / 2 -16) + 'px';
      this._tipDiv.style.left = (bounds.x + bounds.width) + 'px';
      this._tipDiv.style.display='block';
      
      var handler = this.get_events().getHandler('visibilityChanged');
        if(handler) {
            var eventArgs = new Sys.EventArgs();
            handler(this, eventArgs);
        }
    },
    hide : function(){
      this._tipDiv.style.display='none';
    },
    add_visibilityChanged : function(handler) {
        this.get_events().addHandler("visibilityChanged", handler);
    },
    remove_visibilityChanged : function(handler) {
        this.get_events().removeHandler("visibilityChanged", handler);
    }
}
NS.UI.Tooltip.registerClass('NS.UI.Tooltip', Sys.UI.Control);

Type.registerNamespace('NS.UI.TooltipBehavior');

NS.UI.TooltipBehavior.Hover = function(element)
{
   NS.UI.TooltipBehavior.Hover.initializeBase(this, [element]);
}

NS.UI.TooltipBehavior.Hover.prototype = {
	_hideDelay: 500,
	_showDelay: 0,
	_timerID: 0,
    
    get_hideDelay : function(){return this._hideDelay;},
	set_hideDelay : function(value){this._hideDelay = value;},
    get_showDelay : function(){return this._showDelay;},
	set_showDelay : function(value){this._showDelay = value;},

    initialize : function(){
        NS.UI.TooltipBehavior.Hover.callBaseMethod(this, 'initialize');
   
        // get a ref to the base element
        var el = this.get_element();
      
        // add a mouse out handler to the tool tip div
        $addHandler(el.lastChild,"mouseout", Function.createDelegate(this,this._delayedHide));
        $addHandler(el,"mouseover", Function.createDelegate(this,this._delayedShow));
        $addHandler(el,"mouseout", Function.createDelegate(this,this._delayedHide));        
   },
   dispose : function(){
        $clearHandlers(this.get_element().lastChild);
        $clearHandlers(this.get_element());
      
        NS.UI.TooltipBehavior.Hover.callBaseMethod(this, 'dispose');
   },
	_clearTimerID: function(){
		if (this._timerID){
			clearTimeout(this._timerID);
			this._timerID = 0;
		}
	},
	_hide: function(args){
		this.get_element().control.hide();
	},
	_delayedShow: function(e){
		this._clearTimerID();		
		this._timerID = window.setTimeout(Function.createDelegate(this,this._show),this._showDelay);
	},	
	_show: function(e){
//	    var b = Sys.UI.DomElement.getBounds(this.get_element());
//	    this.get_element().control._tipDiv.style.margin_left = (b.x + b.width).toString() + 'px';
	    //Sys.UI.DomElement.setLocation(this.get_element().control._tipDiv, b.x + b.width, b.y);
		this.get_element().control.show();
	},
	_delayedHide: function(e){
		this._clearTimerID();		
		this._timerID = window.setTimeout(Function.createDelegate(this,this._hide),this._hideDelay);
	}
}

NS.UI.TooltipBehavior.Hover.registerClass('NS.UI.TooltipBehavior.Hover', Sys.UI.Behavior);

NS.UI.TooltipBehavior.AutoPos = function(element)
{
   NS.UI.TooltipBehavior.AutoPos.initializeBase(this, [element]);
}

NS.UI.TooltipBehavior.AutoPos.prototype = {
    _tipDiv: Function.emptyFunction,
    _quadrants: ["origin_br arrow_rb","origin_bl arrow_lb","origin_tr arrow_rt","origin_tl arrow_lt"],
    
    get_quadrants : function(){return this._quadrants;},
	set_quadrants : function(value){this._quadrants = value;},
    
    initialize : function(){
        NS.UI.TooltipBehavior.AutoPos.callBaseMethod(this, 'initialize');
   
        // get a ref to the base element
        var el = this.get_element();
                      
        // get the width and height of the tipDiv for positioning
        this._setTipDivWH(el.lastChild);                
        
        el.control.add_visibilityChanged(Function.createDelegate(this,this._setPos));                
   },
   dispose : function(){
      NS.UI.TooltipBehavior.AutoPos.callBaseMethod(this, 'dispose');
   },
   _setTipDivWH : function(tipDiv){
        var style = tipDiv.style;
        
        style.visibility = 'hidden';
        style.display = 'block';        
        
        this._tipDiv.height = tipDiv.firstChild.clientHeight;
        this._tipDiv.width = tipDiv.firstChild.clientWidth;        
        
        style.display = 'none';
        style.visibility = 'visible';
   },
   _setPos : function(e){
    
        var el = this.get_element();
        var tipDiv = el.lastChild;
        
        el.Hover._clearTimerID();
        
        var minX = (typeof(window.pageXOffset) !== 'undefined') ? window.pageXOffset : document.documentElement.scrollLeft;
        var minY = (typeof(window.pageYOffset) !== 'undefined') ? window.pageYOffset : document.documentElement.scrollTop;
        var maxX = (window.innerWidth ? window.innerWidth : document.documentElement.clientWidth);
        var maxY = (window.innerHeight ? window.innerHeight : document.documentElement.clientHeight);
        
        var quadrant = 3;
                
        // determine the quadrant that the tooltip should appear in
        quadrant = (el.offsetLeft - this._tipDiv.width > minX ? 
                    (el.offsetTop - minY + this._tipDiv.height > maxY && el.offsetTop - (this._tipDiv.height) > minY ? 1 : 3) :
                      (el.offsetTop - minY + this._tipDiv.height > maxY ? 2 : 4));
                
        var posCss = this._quadrants[quadrant-1];
                
        tipDiv.firstChild.className = 'innerwrapper ' + posCss;
        tipDiv.style.display = 'block';   
   }   
}

NS.UI.TooltipBehavior.AutoPos.registerClass('NS.UI.TooltipBehavior.AutoPos', Sys.UI.Behavior);

NS.UI.getElementsByClassName = function(className, element) {
    element = element || document;
    className = ' ' + className + ' ';
    var potentials = element.all || element.getElementsByTagName("*");
    var l = potentials.length, results = [], i;
    for (i = 0; i < l; i++) {
        if ((' ' + potentials[i].className + ' ').indexOf(className) !== -1) {
            results[results.length] = potentials[i];
        }
    }
    return results;
};

NS.Timer=function(){
	NS.Timer.initializeBase(this);
	this._interval=1000;
	this._enabled=false;
	this._timer=null
};
NS.Timer.prototype={
	get_interval:function(){
		return this._interval
	},
	set_interval:function(a){
		if(this._interval!==a){
			this._interval=a;
			this.raisePropertyChanged("interval");
			if(!this.get_isUpdating()&&this._timer!==null)this.restartTimer()
		}
	},
	get_enabled:function(){
		return this._enabled
	},
	set_enabled:function(a){
		if(a!==this.get_enabled()){
			this._enabled=a;
			this.raisePropertyChanged("enabled");
			if(!this.get_isUpdating())if(a)this._startTimer();
			else this._stopTimer()
		}
	},
	add_tick:function(a){
		this.get_events().addHandler("tick",a)
	},
	remove_tick:function(a){
		this.get_events().removeHandler("tick",a)
	},
	dispose:function(){
		this.set_enabled(false);
		this._stopTimer();
		NS.Timer.callBaseMethod(this,"dispose")
	},
	updated:function(){
		NS.Timer.callBaseMethod(this,"updated");
		if(this._enabled)this.restartTimer()
	},
	_timerCallback:function(){
		var a=this.get_events().getHandler("tick");
		if(a)a(this,Sys.EventArgs.Empty)
	},
	restartTimer:function(){
		this._stopTimer();
		this._startTimer()
	},
	_startTimer:function(){
		this._timer=window.setInterval(Function.createDelegate(this,this._timerCallback),this._interval)
	},
	_stopTimer:function(){
		window.clearInterval(this._timer);
		this._timer=null
	}
};
NS.Timer.registerClass("NS.Timer",Sys.Component);


NS.UI.CascadingDropDownSelectionChangedEventArgs = function(oldValue, newValue){
	NS.UI.CascadingDropDownSelectionChangedEventArgs.initializeBase(this);
	
	this._oldValue=oldValue;
	this._newValue=newValue;
};
NS.UI.CascadingDropDownSelectionChangedEventArgs.prototype={
	get_oldValue:function(){
		return this._oldValue;
	},
	get_newValue:function(){
		return this._newValue;
	}
};
NS.UI.CascadingDropDownSelectionChangedEventArgs.registerClass('NS.UI.CascadingDropDownSelectionChangedEventArgs', Sys.EventArgs);

NS.UI.CascadingDropDown=function(e){
	NS.UI.CascadingDropDown.initializeBase(this, [e]);

	this._parentControlIDs=null;
	this._parentCategory=null;
	this._category=null;
	this._optional=false;
	this._defaultValue="";
	this._promptText=null;
	this._loadingText=null;
	this._promptValue=null;
	this._emptyValue=null;
	this._emptyText=null;
	this._disableNode=null;
	this._disableClass="disabled";
	this._labelNode=null;

	this._servicePath=null;
	this._serviceMethod=null;
	this._serviceExtraParams=null;
	
	this._changeHandler=null;
	this._parentChangeHandler=null;
	this._lastParentValues=null;
	this._selectedValue=null;
};
NS.UI.CascadingDropDown.prototype={
	initialize:function(){
		NS.UI.CascadingDropDown.callBaseMethod(this, 'initialize');

		var e=this.get_element();

		// Clear any items that may have been put there for server side convenience
		this._clearItems();

		// Set properties on element so that children controls (if any) can have easy access
		e.CascadingDropDownCategory=this._category;
		e.CascadingDropDownOptional=this._optional;
		e.CascadingDropDownDefaultValue=this._defaultValue;

		// Attach change handler to self
		this._changeHandler=Function.createDelegate(this, this._onChange);
		$addHandler(e, "change",this._changeHandler);

		if(this._labelNode){
			this._defaultLabel=this._labelNode.innerHTML;
		}
				
		// Attach change handler to parent
		this._parentChangeHandlers=[];
		if(this._parentControlIDs){
			e.CascadingDropDownParentControlIDs=this._parentControlIDs.join(",");
			for(var i=0;i<this._parentControlIDs.length;i++){
				var parentControlID=this._parentControlIDs[i];
				var parentElement=$get(parentControlID);
				Sys.Debug.assert(parentElement != null, String.format("Failed to find parent element '{0}'", parentControlID));
				if(parentElement){
					if(this._parentCategory && !parentElement.CascadingDropDownCategory)
						parentElement.CascadingDropDownCategory=this._parentCategory;
						
					var handler=Function.createDelegate(this, this._onParentChange);
					this._parentChangeHandlers.push(handler);
					$addHandler(parentElement, "change", handler);
					
					if (!parentElement.childDropDown) {
						parentElement.childDropDown = [];
					}
					parentElement.childDropDown.push(this);
				}
			}
		}

		// Simulate parent change to populate self, even if no parent exists.
		this._onParentChange(null, true);
	},

	dispose: function() {
		var e = this.get_element();

		if (this._changeHandler) {
			$removeHandler(e, "change", this._changeHandler);
			this._changeHandler = null;
		}

		for(var i=0;i<this._parentChangeHandlers.length;i++){
			var el=$get(this._parentControlIDs[i]);
			if (el) {
				$removeHandler(el, "change", this._parentChangeHandlers[i]);
			}
			this._parentChangeHandlers = [];
		}

		NS.UI.CascadingDropDown.callBaseMethod(this, 'dispose');
	},

	_clearItems: function() {
		var e = this.get_element();
		while (0 < e.options.length) {
			e.remove(0);
		}
	},

	_isPopulated: function() {
		var items = this.get_element().options.length;
		return this._promptText ? (items > 1) : (items > 0);
	},

	_setOptions: function(options, inInit, gettingList) {
		/// <summary>
		/// Set the contents of the DropDownList to the specified list
		/// </summary>
		/// <param name="options" mayBeNull="true" elementType="Object">
		/// Hash of options (value -> description)
		/// </param>
		/// <param name="inInit" type="Boolean" optional="true">
		/// Whether this is being called from the initialize method
		/// </param>
		/// <param name="gettingList" type="Boolean" optional="true">
		/// Whether we are fetching the list of options from the web service
		/// </param>
		/// <returns />

		if (!this.get_isInitialized()) {
			return;
		}

		// Update the label
		if(this._labelNode){
			if(options && options._CascadingDropdownLabel){
				this._labelNode.innerHTML=options._CascadingDropdownLabel;
			}else{
				this._labelNode.innerHTML=this._defaultLabel;
			}
		}
		if(options){ delete options._CascadingDropdownLabel; }
		
		var e = this.get_element();
		this._clearItems();

		var count = 0;
		if(options){
			for(var v in options){
				count++;
			}
		}
		
		// Populate prompt text (if available) 
		var headerText;
		var headerValue = "";
		if (gettingList && this._loadingText) {
			headerText = this._loadingText;
		}
		else if (!gettingList && options && (count == 0) && this._emptyText) {
			headerText = this._emptyText;
			if (this._emptyValue) {
				headerValue = this._emptyValue;
			}
		}
		else if (this._promptText) {
			headerText = this._promptText;
			if (this._promptValue) {
				headerValue = this._promptValue;
			}
		}
		if (headerText) {
			var optionElement = new Option(headerText, headerValue);
			e.options[e.options.length] = optionElement;
		}

		// Add each item to the DropDownList, selecting the previously selected item
		var selectedValueOption = null;

		if (options) {
			for (var listItemValue in options) {
				var listItemName = options[listItemValue];
				
				var optionElement = new Option(listItemName, listItemValue);
				if (listItemValue == this._selectedValue) {
					selectedValueOption = optionElement;
				}

				e.options[e.options.length] = optionElement;
			}
			if (selectedValueOption) {
				selectedValueOption.selected = true;
			}
		}
		
		// if we didn't match the selected value, and we found a default
		// item, select that one.
		if (selectedValueOption) {
			// Call set_selectedValue to store the text as well
			this.set_selectedValue(e.options[e.selectedIndex].value, e.options[e.selectedIndex].text);
		}
		else if (count == 1) {
			// If there's only one item, default to it
			var defaultIndex = this._promptText ? 1 : 0;
			e.options[defaultIndex].selected = true;
			this.set_selectedValue(e.options[defaultIndex].value, e.options[defaultIndex].text);
		}
		else if (!inInit && !gettingList && !this._promptText && (e.options.length > 0)) {
			// If no prompt text or default item, select the first item
			this.set_selectedValue(e.options[0].value, e.options[0].text);
		}
		else if (!inInit && !gettingList) {
			this.set_selectedValue('', '');
		}

		if (e.childDropDown && !gettingList) {
			for(i = 0; i < e.childDropDown.length; i++) {
				e.childDropDown[i]._onParentChange();
			}
		}
		else {
			if (options && (Sys.Browser.agent !== Sys.Browser.Safari) && (Sys.Browser.agent !== Sys.Browser.Opera)) {
				// Fire the onchange event for the control to notify any listeners of the change
				if (document.createEvent) {
					var onchangeEvent = document.createEvent('HTMLEvents');
					onchangeEvent.initEvent('change', true, false);
					e.dispatchEvent(onchangeEvent);
				}
				else if( document.createEventObject ) {
					e.fireEvent('onchange');
				}
			}
		}

		// Disable the control if loading/prompt text is present and an empty list was populated
		if (this._loadingText || this._promptText || this._emptyText) {
			e.disabled = (count == 0);
			grayOut = !options;
			if (this._disableNode && (grayOut != Sys.UI.DomElement.containsCssClass(this._disableNode, this._disableClass))) {
				Sys.UI.DomElement.toggleCssClass(this._disableNode, this._disableClass);
			}
		}

		this.raisePopulated(Sys.EventArgs.Empty);
	},

	_onChange: function() {
		if (!this._isPopulated()) {
			return;
		}

		var e = this.get_element();
		
		// Record the selected value in the client state
		if ((-1 != e.selectedIndex) && !(this._promptText && (0 == e.selectedIndex))) {
			this.set_selectedValue(e.options[e.selectedIndex].value, e.options[e.selectedIndex].text);
		}
		else {
			this.set_selectedValue('', '');
		}
	},

	_onParentChange: function(evt, inInit) {
		var e = this.get_element();

		var knownCategoryValues = {};
		
		var parentIDs=[];
		if(this._parentControlIDs){
			for(var i=0;i<this._parentControlIDs.length;i++)
				parentIDs.push(this._parentControlIDs[i]);
		}
			
		while (parentIDs.length) {
			var parentElement = $get(parentIDs[0]);
			parentIDs=parentIDs.splice(1,parentIDs.length-1); //Remove first element
			if (parentElement && (-1 != parentElement.selectedIndex)){
				var selectedValue = parentElement.options[parentElement.selectedIndex].value;
				
				if (selectedValue && selectedValue != "") {
					knownCategoryValues[parentElement.CascadingDropDownCategory] = selectedValue;
					var ids=parentElement.CascadingDropDownParentControlIDs ? parentElement.CascadingDropDownParentControlIDs.split(",") : [];
					for(var i=0;i<ids.length;i++)
						parentIDs.push(ids[i]);
					continue;
				}
				else if (parentElement.CascadingDropDownOptional){
				    knownCategoryValues[parentElement.CascadingDropDownCategory] = parentElement.CascadingDropDownDefaultValue;
				}
				else{	// Found a required parent with no value, abort
					knownCategoryValues={};
					break;
				}
			}
		}
		
		var serializedValues = Sys.Serialization.JavaScriptSerializer.serialize(knownCategoryValues);
		if (serializedValues != '{}' && this._lastParentValues == serializedValues) {
			return;
		}
		
		this._lastParentValues = serializedValues;
		
		// we have a parent but it doesn't have a valid value
		if (serializedValues == '{}' && this._parentControlIDs) {
			this._setOptions(null, inInit);
			return;
		}
		

		// Show the loading text (if any)
		this._setOptions(null, inInit, true);

		if (this._servicePath && this._serviceMethod) {
			// Raise the populating event and optionally cancel the web service invocation
			var eventArgs = new Sys.CancelEventArgs();
			this.raisePopulating(eventArgs);
			if (eventArgs.get_cancel()) {
				return;
			}
			
			if(this._serviceExtraParams){
				for(var param in this._serviceExtraParams){
					knownCategoryValues[param]=this._serviceExtraParams[param];
				}
			}
			
			// Call the helper web service
			Sys.Net.WebServiceProxy.invoke(this._servicePath, this._serviceMethod, false, knownCategoryValues,
				Function.createDelegate(this, this._onMethodComplete), Function.createDelegate(this, this._onMethodError));
		}
	},

	_onMethodComplete: function(result, userContext, methodName) {
		// Update the DropDownList
		this._setOptions(result);
	},

	_onMethodError: function(webServiceError, userContext, methodName) {
		// Indicate failure
		var msg;
		if (webServiceError.get_timedOut()) {
			msg = "[Method timeout]"; // I18N
		}
		else {
			msg = String.format("[Method error {0}]", webServiceError.get_statusCode()); // I18N
		}
		var options = {};
		options[msg] = msg;
		this._setOptions(options);
	},

	get_parentControlID: function() { return null; },
	set_parentControlID: function(value) { this._parentControlIDs = [value] },
	
	get_parentControlIDs: function(value) { return this._parentControlIDs; },
	set_parentControlIDs: function(value) { this._parentControlIDs = value; },

	get_category: function() { return this._category; },
	set_category: function(value) { this._category = value; },

	get_optional: function() { return this._optional; },
	set_optional: function(value) { this._optional = value; },
	
	get_defaultValue: function() { return this._defaultValue; },
	set_defaultValue: function(value) { this._defaultValue = value; },
	
	get_parentCategory: function() { return this._parentCategory; },
	set_parentCategory: function(value) { this._parentCategory = value; },

	get_promptText: function() { return this._promptText; },
	set_promptText: function(value) { this._promptText = value; },

	get_promptValue: function() { return this._promptValue; },
	set_promptValue: function(value) { this._promptValue = value; },

	get_emptyText: function() { return this._emptyText; },
	set_emptyText: function(value) { this._emptyText = value; },

	get_emptyValue: function() { return this._emptyValue; },
	set_emptyValue: function(value) { this._emptyValue = value; },

	get_loadingText: function() { return this._loadingText; },
	set_loadingText: function(value) { this._loadingText = value; },

 	get_disableNode: function() { return this._disableNode; },
	set_disableNode: function(value) { this._disableNode = value; },

	get_labelNode: function() { return this._labelNode; },
	set_labelNode: function(value) { this._labelNode = value; },
	
 	get_disableClass: function() { return this._disableClass; },
	set_disableClass: function(value) { this._disableClass = value; },

	get_servicePath: function() { return this._servicePath; },
	set_servicePath: function(value) { this._servicePath = value; },

	get_serviceMethod: function() { return this._serviceMethod; },
	set_serviceMethod: function(value) { this._serviceMethod = value; },
	
	get_serviceExtraParams: function() { return this._serviceExtraParams; },
	set_serviceExtraParams: function(value) { this._serviceExtraParams = value; },
	
	get_selectedValue: function() {
		return this._selectedValue;
	},
	set_selectedValue: function(value, text) {
		if (this._selectedValue != value) {
			var oldValue = this._selectedValue;
			this._selectedValue = value;
			this.raiseSelectionChanged(new NS.UI.CascadingDropDownSelectionChangedEventArgs(oldValue, value));
		}
	},

	add_selectionChanged: function(handler) { this.get_events().addHandler('selectionChanged', handler); },
	remove_selectionChanged: function(handler) { this.get_events().removeHandler('selectionChanged', handler); },
	raiseSelectionChanged: function(eventArgs) {
		var handler = this.get_events().getHandler('selectionChanged');
		if (handler) {
			handler(this, eventArgs);
		}
	},
	
	add_populating: function(handler) { this.get_events().addHandler('populating', handler); },
	remove_populating: function(handler) { this.get_events().removeHandler('populating', handler); },
	raisePopulating: function(eventArgs) {
		/// The populating event can be used to provide custom data to
		/// CascadingDropDown instead of using a web service.  Just cancel the
		/// event (using the CancelEventArgs) and pass your own data to the
		/// _setOptions method.
		
		var handler = this.get_events().getHandler('populating');
		if (handler) {
			handler(this, eventArgs);
		}
	},
	
	add_populated: function(handler) { this.get_events().addHandler('populated', handler); },
	remove_populated: function(handler) { this.get_events().removeHandler('populated', handler); },
	raisePopulated: function(eventArgs) {
		var handler = this.get_events().getHandler('populated');
		if (handler) {
			handler(this, eventArgs);
		}
	}
}
NS.UI.CascadingDropDown.registerClass('NS.UI.CascadingDropDown', Sys.UI.Behavior);

$q = YAHOO.util.Selector.query;
