///<reference path="../TESCO.js" />
///<reference path="../system/event.js" />
///<reference path="../system/event.manager.js" />
///<reference path="../system/exception.js" />
///<reference path="../system/DOM.node.js" />
///<reference path="position.js" />
///<reference path="inputs.js" />

TESCO.$("UI.effects").blender = {
	"linear": function(start, end, steps, currentStep) {
		/// <summary>Linear 'plain' animation.</summary>
		/// <param name="start" type="number"></param>
		/// <param name="end" type="number"></param>
		/// <param name="steps" type="number"></param>
		/// <param name="currentStep" type="number"></param>
        /// <returns type="number">Calculates a number based on the amount steps within an animation.</returns>
		return parseInt(start + (currentStep / steps) * (end - start));
	},
	"sinusoidal": function(start, end, steps, currentStep) {
		/// <summary>Sinusoidal animation (start fast, slow down).</summary>
		/// <param name="start" type="number"></param>
		/// <param name="end" type="number"></param>
		/// <param name="steps" type="number"></param>
		/// <param name="currentStep" type="number"></param>
        /// <returns type="number">Calculates a number based on the amount steps within an animation.</returns>
		return parseInt(start + Math.sin(((currentStep / steps) * 90) * (Math.PI / 180)) * (end - start));
	},
	"exponential": function(start, end, steps, currentStep) {
		/// <summary>Exponential animation (start slow, speed up).</summary>
		/// <param name="start" type="number"></param>
		/// <param name="end" type="number"></param>
		/// <param name="steps" type="number"></param>
		/// <param name="currentStep" type="number"></param>
        /// <returns type="number">Calculates a number based on the amount steps within an animation.</returns>
		return parseInt(start + (Math.pow(currentStep / steps, 2)) * (end - start));
	}
}

/*
	Example:
	
	var colour = new TESCO.UI.effects.Colour(adiv);
	colour.blend([
		{
			"start" : "red",
			"end" : TESCO.UI.effects.Colour.palette.green,
			"blender" : TESCO.UI.effects.blender.sinusoidal 
		},
		{
			"start" : "red",
			"end" : "#fff"
		}
	]);
*/
TESCO.UI.effects.Colour = (function() {

	//	constants
	var NODE = TESCO.system.DOM.node;

	//  private instance
	//#region
	function _stop(fields) {
		fields.blending = false;
		clearInterval(fields.timer);
		this.dispatchEvent("stop");
	}

	function _fill(fields, attribute, rgb) {
		this.css[attribute] = "rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ")";
		fields.rgb = rgb;
	}

	function _getFields(settings, attribute) {
		var _fields = _registary[this.id()];
		_fields = _fields[attribute];  //  set fields to correct attribute
		if (_fields.blending) {
			//  stop any previous blends
			_stop.call(this, _fields);
		}
		return _fields;
	}
	//#endregion

	//  private static
	//#region
	var _registary = [];
	
	function _getRgb(c) {   //  return [r, g, b]
		var _result;
		if (c && c.constructor == Array && c.length == 3) {
			return c;
		}
		if (_result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(c)) {
			return [parseInt(_result[1]), parseInt(_result[2]), parseInt(_result[3])];
		} else if (_result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(c)) {
			return [parseFloat(_result[1]) * 2.55, parseFloat(_result[2]) * 2.55, parseFloat(_result[3]) * 2.55];
		} else if (_result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(c)) {
			return [parseInt("0x" + _result[1]), parseInt("0x" + _result[2]), parseInt("0x" + _result[3])];
		} else if (_result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(c)) {
			return [parseInt("0x" + _result[1] + _result[1]), parseInt("0x" + _result[2] + _result[2]), parseInt("0x" + _result[3] + _result[3])];
		} else {
			return _constructor.palette[c.replace(/^\s*|\s*$/g, '').toLowerCase()] || null;
		}
	}
	//#endregion

	//  public static
	//#region
	_constructor.attributes = {
		"background": "backgroundColor",
		"border": "borderColor",
		"font": "color"
	}

	_constructor.palette = {
		"aliceblue": [240, 248, 255],
		"antiquewhite": [250, 235, 215],
		"aqua": [0, 255, 255],
		"aquamarine": [127, 255, 212],
		"azure": [240, 255, 255],
		"beige": [245, 245, 220],
		"bisque": [255, 228, 196],
		"black": [0, 0, 0],
		"blanchedalmond": [255, 235, 205],
		"blue": [0, 0, 255],
		"blueviolet": [138, 43, 226],
		"brown": [165, 42, 42],
		"burlywood": [222, 184, 135],
		"cadetblue": [95, 158, 160],
		"chartreuse": [127, 255, 0],
		"chocolate": [210, 105, 30],
		"coral": [255, 127, 80],
		"cornflowerblue": [100, 149, 237],
		"cornsilk": [255, 248, 220],
		"crimson": [220, 20, 60],
		"cyan": [0, 255, 255],
		"darkblue": [0, 0, 139],
		"darkcyan": [0, 139, 139],
		"darkgoldenrod": [184, 134, 11],
		"darkgray": [169, 169, 169],
		"darkgrey": [169, 169, 169],
		"darkgreen": [0, 100, 0],
		"darkkhaki": [189, 183, 107],
		"darkmagenta": [139, 0, 139],
		"darkolivegreen": [85, 107, 47],
		"darkorange": [255, 140, 0],
		"darkorchid": [153, 50, 204],
		"darkred": [139, 0, 0],
		"darksalmon": [233, 150, 122],
		"darkseagreen": [143, 188, 143],
		"darkslateblue": [72, 61, 139],
		"darkslategray": [47, 79, 79],
		"darkslategrey": [47, 79, 79],
		"darkturquoise": [0, 206, 209],
		"darkviolet": [148, 0, 211],
		"deeppink": [255, 20, 147],
		"deepskyblue": [0, 191, 255],
		"dimgray": [105, 105, 105],
		"dimgrey": [105, 105, 105],
		"dodgerblue": [30, 144, 255],
		"firebrick": [178, 34, 34],
		"floralwhite": [255, 250, 240],
		"forestgreen": [34, 139, 34],
		"fuchsia": [255, 0, 255],
		"gainsboro": [220, 220, 220],
		"ghostwhite": [248, 248, 255],
		"gold": [255, 215, 0],
		"goldenrod": [218, 165, 32],
		"gray": [128, 128, 128],
		"grey": [128, 128, 128],
		"green": [0, 128, 0],
		"greenyellow": [173, 255, 47],
		"honeydew": [240, 255, 240],
		"hotpink": [255, 105, 180],
		"indianred ": [205, 92, 92],
		"indigo ": [75, 0, 130],
		"ivory": [255, 255, 240],
		"khaki": [240, 230, 140],
		"lavender": [230, 230, 250],
		"lavenderblush": [255, 240, 245],
		"lawngreen": [124, 252, 0],
		"lemonchiffon": [255, 250, 205],
		"lightblue": [173, 216, 230],
		"lightcoral": [240, 128, 128],
		"lightcyan": [224, 255, 255],
		"lightgoldenrodyellow": [250, 250, 210],
		"lightgray": [211, 211, 211],
		"lightgrey": [211, 211, 211],
		"lightgreen": [144, 238, 144],
		"lightpink": [255, 182, 193],
		"lightsalmon": [255, 160, 122],
		"lightseagreen": [32, 178, 170],
		"lightskyblue": [135, 206, 250],
		"lightslategray": [119, 136, 153],
		"lightslategrey": [119, 136, 153],
		"lightsteelblue": [176, 196, 222],
		"lightyellow": [255, 255, 224],
		"lime": [0, 255, 0],
		"limegreen": [50, 205, 50],
		"linen": [250, 240, 230],
		"magenta": [255, 0, 255],
		"maroon": [128, 0, 0],
		"mediumaquamarine": [102, 205, 170],
		"mediumblue": [0, 0, 205],
		"mediumorchid": [186, 85, 211],
		"mediumpurple": [147, 112, 216],
		"mediumseagreen": [60, 179, 113],
		"mediumslateblue": [123, 104, 238],
		"mediumspringgreen": [0, 250, 154],
		"mediumturquoise": [72, 209, 204],
		"mediumvioletred": [199, 21, 133],
		"midnightblue": [25, 25, 112],
		"mintcream": [245, 255, 250],
		"mistyrose": [255, 228, 225],
		"moccasin": [255, 228, 181],
		"navajowhite": [255, 222, 173],
		"navy": [0, 0, 128],
		"oldlace": [253, 245, 230],
		"olive": [128, 128, 0],
		"olivedrab": [107, 142, 35],
		"orange": [255, 165, 0],
		"orangered": [255, 69, 0],
		"orchid": [218, 112, 214],
		"palegoldenrod": [238, 232, 170],
		"palegreen": [152, 251, 152],
		"paleturquoise": [175, 238, 238],
		"palevioletred": [216, 112, 147],
		"papayawhip": [255, 239, 213],
		"peachpuff": [255, 218, 185],
		"peru": [205, 133, 63],
		"pink": [255, 192, 203],
		"plum": [221, 160, 221],
		"powderblue": [176, 224, 230],
		"purple": [128, 0, 128],
		"red": [255, 0, 0],
		"rosybrown": [188, 143, 143],
		"royalblue": [65, 105, 225],
		"saddlebrown": [139, 69, 19],
		"salmon": [250, 128, 114],
		"sandybrown": [244, 164, 96],
		"seagreen": [46, 139, 87],
		"seashell": [255, 245, 238],
		"sienna": [160, 82, 45],
		"silver": [192, 192, 192],
		"skyblue": [135, 206, 235],
		"slateblue": [106, 90, 205],
		"slategray": [112, 128, 144],
		"slategrey": [112, 128, 144],
		"snow": [255, 250, 250],
		"springgreen": [0, 255, 127],
		"steelblue": [70, 130, 180],
		"tan": [210, 180, 140],
		"teal": [0, 128, 128],
		"thistle": [216, 191, 216],
		"tomato": [255, 99, 71],
		"turquoise": [64, 224, 208],
		"violet": [238, 130, 238],
		"wheat": [245, 222, 179],
		"white": [255, 255, 255],
		"whitesmoke": [245, 245, 245],
		"yellow": [255, 255, 0],
		"yellowgreen": [154, 205, 50]
	}
	//#endregion

	//  _constructor  
	function _constructor(obj) {
		///	<summary>Constructor. Provides colour animation methods for background, border and font colours</summary>
		///	<param name="obj" type="node"></param>
		///	<returns type="TESCO.UI.effects.Colour"></returns>
		this.css = obj.style;
		TESCO.system.event.manager.call(this, "stop");
		//  create private instance scope
		var _index = _registary.length;
		_registary[_index] = {
			"backgroundColor": {
				"rgb": NODE.getStyle(obj, "backgroundColor") || [255, 255, 255],
				"blending": false,
				"timer": null
			},
			"borderColor": {
				"rgb": NODE.getStyle(obj, "borderColor") || [0, 0, 0],
				"blending": false,
				"timer": null
			},
			"color": {
				"rgb": NODE.getStyle(obj, "color") || [0, 0, 0],
				"blending": false,
				"timer": null
			}
		}

		// instance function to hold the id
		this.id = function() {
			return _index;
		};

		return this;
	}

	//  public instance
	//#region
	//  default
	_constructor.prototype.NAME = "TESCO.UI.effects.Colour";

	//  methods
	// [{ start, end, interval, steps, blender, attribute }, {...}]
	_constructor.prototype.blend = function(settings) {
		///	<summary>blend from 1 colour to another</summary>
		///	<param name="settings" type="array">
		///		array of objects to chain some blends, or a single object for one blend
		///
		///		[{
		///			"attribute" : (Optional. Any of "borderColor", "color" or "backgroundColor". uses "backgroundColor" if not specified),
		///			"steps" : (Optional. Number of steps. Uses TESCO.sites.Configuration.colour.steps if not specified),
		///			"start" : (Optional if end is specified. Start colour. Uses current colour if not specified.),
		///			"end" : (Optional if start is specified. End colour. Uses current colour if not specified.),
		///			"interval" : (Optional. Milliseconds to wait between animation steps. Uses TESCO.sites.Configuration.colour.interval if not specified),
		///			"blender" : (Optional. Animation algorithm to use. Uses TESCO.sites.Configuration.colour.blender if not specified)
		///		}]
		/// </param>


		if (!settings.ISARRAY) {
			settings = [settings];
		}
		var _attribute = settings[0].attribute || _constructor.attributes.background;
		var _fields = _getFields.call(this, settings, _attribute);
		//  set initial variables
		var _end = settings[0].end ? _getRgb(settings[0].end) : _fields.rgb;
		_fields.blending = true;
		if (settings[0].start) {
			_fill.call(this, _fields, _attribute, _getRgb(settings[0].start));
		}
		var _interval = settings[0].interval || TESCO.sites.Configuration.colour.interval;
		var _steps = settings[0].steps || TESCO.sites.Configuration.colour.steps;
		var _blender = settings[0].blender || TESCO.sites.Configuration.colour.blender;
		var _step = 0;
		var _self = this;

		//  start blending
		_fields.timer = setInterval(
            function() {
            	_fill.call(_self, _fields, _attribute,
                     [_blender(_fields.rgb[0], _end[0], _steps, _step),   //  new red
                      _blender(_fields.rgb[1], _end[1], _steps, _step),   //  new blue
                      _blender(_fields.rgb[2], _end[2], _steps, _step)]   //  new green
                );
            	//  finished?
            	if (_step++ >= _steps) {
            		_stop.call(_self, _fields);
            		//  move to the next blend?
            		if (settings.length > 1) {
            			//  remove the first blend setting
            			settings.splice(0, 1);
            			_self.blend(settings);
            		}
            	}
            }, _interval
        );
	}

	// { rgb, attribute }
	_constructor.prototype.fill = function(settings) {
		///	<summary>Set the colour of the instance</summary>
		///	<param name="settings" type="object">
		///		object
		///		{
		///			"attribute" : (Optional. Any of "borderColor", "color" or "backgroundColor". uses "backgroundColor" if not specified),
		///			"rgb" : (Required. RGB fill colour)
		///		}]
		/// </param>
		var _attribute = settings.attribute || _constructor.attributes.background;
		_fill.call(this, _getFields.call(this, settings, _attribute), _attribute, settings.rgb);
	}

	//  traffic lights
	_constructor.prototype.warn = function(end) {   //   stop
		this.blend({ "start": TESCO.sites.Configuration.colour.warn, "end": (end ? end : [255, 255, 255]) });
	}

	_constructor.prototype.highlight = function(end) {  //   wait
		this.blend({ "start": TESCO.sites.Configuration.colour.highlight, "end": (end ? end : [255, 255, 255]) });
	}

	_constructor.prototype.confirm = function(end) {  //   go
		this.blend({ "start": TESCO.sites.Configuration.colour.confirm, "end": (end ? end : [255, 255, 255]) });
	}
	//#endregion

	//  return _constructor as function pointer
	return _constructor;
})();

/*
	Example:
	
	var opaque = new TESCO.UI.effects.Opacity(adiv);
		opaque.addEventListener("complete"
			function() {
				opaque.removeEventListener("complete", arguments.callee);
				opaque.fadeUp();
			}
		opaque.fadeDown();
*/
TESCO.UI.effects.Opacity = (function() {

	var NODE = TESCO.system.DOM.node;
	var EVENT = TESCO.system.event;
	
    //  private static 
    var _key = TESCO.system.browser.opacity;
    var _filter = (_key === "filter");
    var _registary = [];
    
    //  public static
    //#region
    _constructor.directions = {
        "up" : 1,
        "neutral" : 0,
        "down" : -1
    }
    //#endregion

    //  private instance
    //#region
	function _configure(settings) {
	    var _settings = {}
		_settings.step = 0;
		if (this.direction === _constructor.directions.down) {
	        _settings.end = 0;
		} else if (settings.end) {
		    _settings.end = settings.end;
		} else {
		    _settings.end = 100;
		}
		_settings.interval = settings.interval || 10;
		_settings.steps = settings.steps || 5;
		if (_filter) {
		    _settings.filter = "progid:DXImageTransform.Microsoft.Fade(duration=" + Math.max(0.5, (_settings.interval / 1000) * _settings.steps) + ")";
		    var _start = this.get();
		    if (this.direction === _constructor.directions.up) {
		        _settings.filter += " progid:DXImageTransform.Microsoft.Alpha(opacity=" + _settings.end + ")";
		    } else if (_start < 100) {
                _settings.filter += " progid:DXImageTransform.Microsoft.Alpha(opacity=" + _start + ")";
            }
		} else {
		    _settings.start = settings.start;
		    _settings.blender = settings.blender || TESCO.UI.effects.blender.linear;
		    if (_settings.start > -1) {
			    this.set(_settings.start);
			    _settings.step = 1;
		    } else {
		        _settings.start = this.get();
                //  if it's already partially faded, reduce steps
		        if (this.direction === _constructor.directions.up && _settings.start > 0) {
		            _settings.steps -= Math.ceil(_settings.start / (_settings.end / _settings.steps));
		        } else if (this.direction === _constructor.directions.down && _settings.start < 100) {
		            _settings.steps -= Math.ceil((100 - _settings.start) / ((100 - _settings.end) / _settings.steps));
		        }
		    }
		    _settings.steps = Math.max(_settings.steps, 1);
		} 
		return _settings;
    }
     
    function _fade(settings) { //  fade from one opacity to another
        if (_filter) {
		    var _self = this;
	        EVENT.attach(this.node, "filterchange",
                function() {
					EVENT.detach(_self.node, "filterchange", arguments.callee);
					_self.direction = _constructor.directions.neutral;
                    _self.dispatchEvent("complete", _self.e);
                    if (_self.css.visibility === "hidden") {
                        _self.set(0);
                    } else if (settings.end === 100 && !TESCO.system.browser.ie6) {
						_self.css.filter = null;
                    }
                    NODE.removeClassName(_self.node, "fading");
                }
            );
            this.css.filter = settings.filter;
	        NODE.addClassName(this.node, "fading");
	        this.node.filters[0].apply();
	        this.css.visibility = (this.direction === _constructor.directions.up) ? "visible" : "hidden";
	        this.node.filters[0].play();
        } else {
		    var _self = this;
		    this.set(settings.start);
		    this.css.visibility = "visible";
		    var _step = settings.step;
		    this.timer = setInterval(
                function() {
                    var _opacity = settings.blender(settings.start, settings.end, settings.steps, _step);
                    _self.set(_opacity);
            	    //  finished?
            	    if ((_step++ >= settings.steps) || (_opacity === settings.end)) {
            		    _self.stop();
		                if (settings.end === 0) { 
		                    _self.css.visibility = "hidden";
		                }
		                _self.direction = _constructor.directions.neutral;
		                _self.dispatchEvent("complete", _self.e);
            	    }
                }, settings.interval
            );
        }
	}
    //#endregion
    
	//	constructor
	function _constructor(node, e) {
		///	<summary>create node which can be faded</summary>
		///	<param name="node" type="node"></param>
		///	<param name="e">object to be dispatched when fade completed</param>
		///	<returns type="TESCO.UI.effects.Opacity"></returns>
		this.node = node;
		this.css = node.style;
		this.e = e;
		this.direction = _constructor.directions.neutral;
		EVENT.manager.call(this, "complete");
		_registary.push(this);		
		return this;
	}

	//  public instance
	//#region 
	//  default
	_constructor.prototype.NAME = "TESCO.UI.effects.Opacity";

	//  methods
	_constructor.prototype.set = function(opacity) {
		///	<summary>set opacity</summary>
		///	<param name="opacity" type="number (0 to 100)"></param>
	    if (_filter) {
			this.css.filter = "progid:DXImageTransform.Microsoft.Alpha(opacity=" + opacity + ")";
	    } else {
	        this.css[_key] = (opacity / 100);
	    }
	}

	_constructor.prototype.get = function() {
		///	<summary>return opacity</summary>
		///	<returns type="number">0 to 100</returns>
	    var _opacity;
		if (_filter) {
		    _opacity = parseInt(this.css.filter.substr(this.css.filter.indexOf("opacity=") + 8) || 100, 10);
		} else {
			_opacity = this.css[_key] * 100;
		}
		return _opacity;
	}
	
	_constructor.prototype.fadeUp = function(settings, ignore) {
		///	<summary>fade up</summary>
		///	<param name="settings" type="object">eg {"start" : 50, "end" : 80, "steps" : 3, "interval" : 1}</param>
		///	<param name="ignore" type="boolean">do not fade, just make visible</param>
		if (!ignore) {
			if (_filter) {
				if (this.css.visibility === "visible") return;
			}
			this.direction = _constructor.directions.up;
			_fade.call(this, _configure.call(this, settings));
		} else {
			this.css.visibility = "visible";
			this.dispatchEvent("complete", this.e);
		}
	}
	
	_constructor.prototype.fadeDown = function(settings, ignore) {
		///	<summary>fade out</summary>
		///	<param name="settings" type="object">eg {"steps" : 3, "interval" : 1}</param>
		///	<param name="ignore">boolean, do not fade, just make visible</param>
	    if (!ignore) {
			if (_filter) {
				if (this.css.visibility === "hidden") return;
			}
			this.direction = _constructor.directions.down;
			_fade.call(this, _configure.call(this, settings));
		} else {
			this.css.visibility = "hidden";
			this.dispatchEvent("complete", this.e);
		}
	}
	
	_constructor.prototype.active = function() {
		///	<summary>is the object being faded?</summary>
		///	<returns type="boolean"></returns>
	    var _active;
	    if (_filter) {
		    if (this.node.filters) {
		        if (this.node.filters[0]) {
		            _active = (this.node.filters[0].status === 2);
		        }
            }
	    } else {
	        _active = this.timer
	    }
	    return !!_active;
	}
	
	_constructor.prototype.stop = function() {	
		///	<summary>halt a fade in progress</summary>
	    this.direction = _constructor.directions.neutral;
	    if (_filter) {
			return;	//	not needed for ie
	    }
	    if (this.active()) {
//		    if (_filter) {
//	            this.node.filters[0].stop();
//	        } else {
		        clearInterval(this.timer);
		        delete this.timer;
//	        }
	    }
	}
	//#endregion

	//	return constructor as function pointer
	return _constructor;
})();

/*
	Example: 
	
	new TESCO.UI.effects.Background(document.getElementById("myDiv")).show();
*/
TESCO.UI.effects.Background = (function() {

    var _registary = [];
    
    //  constructor
    function _constructor(node) {
        ///	<summary>create opaque elements to cover node</summary>
        ///	<param name="node" type="node">area to be covered</param>
        ///	<returns type="TESCO.UI.effects.Background"></returns>
        _constructor.base.constructor.call(this, document.createElement("div"));
        var _i = _registary.length;
        this.index = function() {
            return _i;
        }
        _registary[_i] = {
            "parent": null,
            "node": node,
            "opacity": new TESCO.UI.effects.Opacity(this.rect),
            "active": false
        }
        TESCO.system.DOM.node.addClassName(this.rect, TESCO.system.browser.opacity ? "background" : "background-d");
        _constructor.base.hide.call(this);
        return this;
    }
    _constructor.extend(TESCO.UI.Rectangle.Position);

    //  public instance
    //#region
    _constructor.prototype.NAME = "TESCO.UI.effects.Background";

    _constructor.prototype.active = function() {
		///	<summary>is the background showing, or in the process of showing?</summary>
        ///	<returns type="boolean"></returns>
        return _registary[this.index()].active;
    }

    //  override position show
    _constructor.prototype.show = function() {
		///	<summary>fit and show the background</summary>
        var _fields = _registary[this.index()];
        _fields.active = true;
        this.fit();
        this.raise();
        this.css.display = "block";
        if (TESCO.system.browser.opacity) {
            _fields.opacity.fadeUp({ "start": 50, "end": 80, "steps": 3, "interval": 1 });
        }
    }

    //  override position hide
    _constructor.prototype.hide = function() {
				///	<summary>fit and show the background</summary>
        var _fields = _registary[this.index()];
        _fields.active = false;
        var _self = this;
        if (TESCO.system.browser.opacity) {
            _fields.opacity.addEventListener("complete",
	            function(e) {
	                _fields.opacity.removeEventListener("complete", arguments.callee);
	                _self.css.display = "none";
	            }
	        );
            _fields.opacity.fadeDown({ "steps": 3, "interval": 1 });
        } else {
            this.css.display = "none";
        }
    }

    _constructor.prototype.remove = function() {
        var _fields = _registary[this.index()];
        _fields.active = false;
        if (_fields.parent) {
            _fields.parent.removeChild(this.rect);
            _fields.parent = null;
        }
    }

    _constructor.prototype.fit = function() {
        var _fields = _registary[this.index()];
        _fields.node.parentNode.appendChild(this.rect);
        _fields.parent = _fields.node.parentNode;
        this.resize(_fields.node.offsetWidth, _fields.node.offsetHeight);
        this.moveTo(_fields.node.offsetLeft, _fields.node.offsetTop);
    }
    //#endregion

    //  return _constructor as function pointer
    return _constructor;
})();

/*
	Example: 
	
	if (!TESCO.UI.effects.Background.document.active()) {
		TESCO.UI.effects.Background.document.show();
	}
	
	TODO: also need to disable any tabable page elements 'under' the background
*/
TESCO.UI.effects.Background.document = (function() {
	///	<summary>Static object which conceals document body</summary>
	
	var SELECT = TESCO.UI.inputs.SelectBox;
	
	//  private static
	var _stack = [];
	var _background;
	var _active = false;

	//  constructor
	function _constructor() {
		_constructor.base.constructor.call(this);
		var _id = document.createAttribute("id");
		_id.nodeValue = "bg";
		this.rect.setAttributeNode(_id);
		return this;
	}
	_constructor.extend(TESCO.UI.effects.Background);

	//	public instance
	//#region
	_constructor.prototype.NAME = "TESCO.UI.effects.Background.document";
	
	//  override active accessor
	_constructor.prototype.active = function() {
		return _active;
	}

	//  override position show
	_constructor.prototype.show = function() {
		_active = true;
		//  record start level
		var _stackHeight = _stack.length;
		_stack.push(this.z);
		
		//  background wasn't already showing?
		if (_stackHeight === 0) {
			SELECT.toggle("hidden");
			_constructor.base.show.call(this);
		}
	}

	//  override position hide
	_constructor.prototype.hide = function() {
		_active = false;
		var _stackHeight = _stack.length;
		if (_stackHeight > 0) {
			//  remove from stack
			_stack.pop();
			_stackHeight = _stack.length;

			//  background wasn't already showing when show was called again?
			if (_stackHeight === 0) {
				_constructor.base.hide.call(this);
				SELECT.toggle("visible");
			} else {
				//  return the background to it's previous level
				this.moveToZ(_stack[_stackHeight]);
			}
		}
	}

	_constructor.prototype.fit = function() {	//	override base fit
		document.body.appendChild(_background.rect);
		_background.resizeH(TESCO.UI.document.height());
		_background.css.width = _background.width = "100%";
	}
	//#endregion

	//  return new instance
	return (_background = new _constructor());
})();
