// The global array of objects that have been instanciated
if (!Bs_Objects) {var Bs_Objects = [];};


/**
* unfortunately needs to be in the global scope. :/
* @return void
*/
function bs_lt_checkLength() {
	event.srcElement.bsObj.checkLength();
	event.srcElement.bsObj._updateBgColorWarning();
	event.srcElement.bsObj._updateProgressBar();
}


/**
* 
* <b>Includes (+Dependences):</b>
* <code>
*   <script type="text/javascript" src="/_bsJavascript/core/lang/Bs_Misc.lib.js"></script>
*   <script type="text/javascript" src="/_bsJavascript/components/limitedtextarea/Bs_LimitedTextarea.class.js"></script>
*   <script type="text/javascript" src="/_bsJavascript/core/gfx/Bs_ColorUtil.lib.js"></script>
* </code>
* the Bs_ColorUtil.lib.js is only needed if you use the setBgColorWarning() feature.
* 
* @param string elementId
* @param int    maxLength
* 
* @package    javascript_components
* @subpackage limitedtextarea
* @author     andrej arn <andrej-at-blueshoes-dot-org>
* @copyright  blueshoes.org
*/
function Bs_LimitedTextarea(elementId, maxLength) {
	
  // To support the old interface call with 3 arguments. (First argument used to be the object name). 
  var a = arguments;
  if (a.length>2) {
    elementId = a[1];
    maxLength = a[2];
  } 

	/**
  * Unique Object/Tag ID is initialized in the constuctor.
  * Bassed on this._id. Can be used in genarate JS-code as ID. Is set together 
  * from the  classname + this._id (see _constructor() code ).
  *
  * @access private
  * @var  string 
  */
  this._objectId;

	/**
	* The text displayed to the user, next to the number of remaining characters.
	* @access public
	* @var    string infolineText
	*/
	this.infolineText = 'Characters remaining:';
	
	/**
	* if specified then this css class will be used on the infoline span.
	* @access public
	* @var    string infolineCssClass
	*/
	this.infolineCssClass;
	
	/**
	* if specified then this css style will be used on the infoline span.
	* @access public
	* @var    string infolineCssStyle
	*/
	this.infolineCssStyle;
	
	/**
	* if specified then this css class will be used on the "chars-left" number div.
	* @access public
	* @var    string numberCssClass
	*/
	this.numberCssClass;
	
	/**
	* if specified then this css style will be used on the "chars-left" number div.
	* @access public
	* @var    string numberCssStyle
	*/
	this.numberCssStyle;
		
	/**
	* @access private
	* @var    string _elementId
	*/
	this._elementId  = elementId;
	
	/**
	* @access private
	* @var    element _element
	*/
	this._element    = document.getElementById(elementId);
	
	/**
	* @access private
	* @var    int maxLength
	* @see    this.setMaxLength()
	*/
	this._maxLength = maxLength;
	
	/**
	* if you want to use the progress bar feature, it shows how much of the 
	* available space you've used with your text.
	* 
	* @access public
	* @var    bool useProgressBar
	* @status experimental
	*/
	this.useProgressBar = false;
	
	/**
	* the number of the progress bar divs used on the last run.
	* helper var for _updateProgressBar().
	* @access private
	* @var    int _lastNumProgressBars
	* @see    this._updateProgressBar()
	*/
	this._lastNumProgressBars
	
	
	/**
	* the pseudo constructor.
	* @access private
	* @return void
	*/
	this._constructor = function() {
  	// Put this instance into the global object instance list
    this._id = Bs_Objects.length;
    Bs_Objects[this._id] = this; 
    this._objectId = "Bs_LimitedTextarea_"+this._id;
  }
	
	/**
	* @access public
	* @return void
	*/
	this.draw = function() {
		//if it makes no sense, we don't check for a maxlength.
		if (isNaN(this._maxLength)) return;
		if (this._maxLength <= 0) return;
		
		this._element.bsObj = this;
		try {
			var htmlStr = '<br>';
			
			if (this.useProgressBar) {
				//htmlStr += '&nbsp;';
				htmlStr += '<div id="' + this._objectId + '_progess" style="overflow:hidden; width:' + this._element.offsetWidth + 'px; height:18px; border:2px inset;';
				//htmlStr += ' display:inline; width:60px;';
				htmlStr += '">'; 
				// border-left:1px solid #848284; border-top:1px solid #848284; border-right:1px solid #ffffff; border-bottom:1px solid #ffffff;
				htmlStr += '<img src="/_bsImages/spacer.gif" width="1" height="1" border="0">';
				htmlStr += '</div>'; //<br>
			}
			
			//htmlStr += '<span';
			htmlStr += '<div';
			if (typeof(this.infolineCssClass) == 'string') htmlStr += ' class="' + this.infolineCssClass + '"';
			if (typeof(this.infolineCssStyle) == 'string') htmlStr += ' style="' + this.infolineCssStyle + '"';
			htmlStr += '>' + this.infolineText + ' <div ';
			htmlStr += ' style="display:inline;';
			if (typeof(this.numberCssStyle) == 'string') htmlStr += this.numberCssStyle;
			htmlStr += '"';
			if (typeof(this.numberCssClass) == 'string') htmlStr += ' class="' + this.numberCssClass + '"';
			htmlStr += 'id="' + this._objectId + '_no"></div>';
			
			htmlStr += '</div>'; //'</span>';
			this._element.insertAdjacentHTML('afterEnd', htmlStr);
		} catch (e) {
			//old browser, or mozilla and Bs_Misc is missing.
		}
		try {
			this._element.attachEvent('onchange', bs_lt_checkLength);
			this._element.attachEvent('onkeyup',  bs_lt_checkLength);
			this._element.attachEvent('onpaste',  bs_lt_checkLength);
		} catch (e) {
			//old browser, or mozilla and Bs_Misc is missing.
		}
		this.checkLength();
		this._updateBgColorWarning();
		this._updateProgressBar();
	}
	
	/**
	* sets the given value.
	* @access public
	* @param  string val
	* @return void
	*/
	this.setValue = function(val) {
		this._element.value = val;
		this.checkLength();
	}
	
	/**
	* returns the current value.
	* @access public
	* @return string
	*/
	this.getValue = function() {
		this.checkLength();
		return this._element.value;
	}
	
	/**
	* sets a new max length.
	* @access public
	* @param  int maxLength
	* @return void
	*/
	this.setMaxLength = function(maxLength) {
		this._maxLength = maxLength;
		this.checkLength();
	}
	
	/**
	* tells the current maxlength.
	* @access public
	* @return int
	*/
	this.getMaxLength = function() {
		return this._maxLength;
	}
	
	/**
	* tells the current number of chars used.
	* @access public
	* @return int
	*/
	this.getCurrentLength = function() {
		return this._element.value.length;
	}
	
	/**
	* checks the current value, and cuts if needed.
	* @access public (you should never need that.)
	* @return void
	*/
	this.checkLength = function() {
		if (this._element.value.length > this._maxLength) this._element.value = this._element.value.substr(0, this._maxLength);
		try {
			document.getElementById(this._objectId + '_no').innerHTML = this._maxLength - this._element.value.length;
		} catch (e) {
			//old browser, or mozilla and Bs_Misc is missing.
		}
	}
	
	/**
	* sets the properties for the "background color warning" feature.
	* 
	* check the example 2 for this feature:
	* http://www.blueshoes.org/_bsJavascript/components/limitedtextarea/examples/example2.html
	* 
	* example calls:
	*   yourObj.setBgColorWarning();
	*   kicks in if 80% of the chars are used. transforms to color 'FF4040'.
	*   
	*   yourObj.setBgColorWarning(50, 'char', 'FFC0C0');
	*   kicks in if 50 chars are used. transforms to color 'FFC0C0'.
	* 
	* note that if you use this feature with a % value, you need to setMaxLength() before. 
	* 
	* this feature requires that you include this file as well:
	*   /_bsJavascript/core/gfx/Bs_ColorUtil.lib.js
	* 
	* @access public
	* @param  int kickInValue (when to kick in, eg 50, default is 80.)
	* @param  string kickInType ('%' or 'char', default is '%'.)
	* @param  string endColor (6 digit hex code, default is 'FF4040'.)
	* @return void
	*/
	this.setBgColorWarning = function(kickInValue, kickInType, endColor) {
		if (typeof(hexdec) == 'undefined') {
			alert("Webmaster: please include the library: /_bsJavascript/core/gfx/Bs_ColorUtil.lib.js. Otherwise the 'setBgColorWarning()' feature won't work.");
		}
		
		this._bgColorWarning = new Object;
		
		if (bs_isNull(kickInValue)) {
			kickInValue = 80;
			kickInType  = '%';
		}
		if ((kickInType == '%') || (bs_isNull(kickInType))) {
			kickInValue = parseInt(this._maxLength / 100 * kickInValue);
		}
		this._bgColorWarning.kickInValue = kickInValue;
		
		this._bgColorWarning.endColor    = (!bs_isEmpty(endColor)) ? endColor : 'FF4040';
		
		if (moz) {
			//stupid stupid mozilla.
			if (!bs_isEmpty(this._element.currentStyle.backgroundColor)) {
				var startColor = this._element.currentStyle.backgroundColor;
			} else if (!bs_isEmpty(this._element.style.backgroundColor)) {
				var startColor = this._element.style.backgroundColor;
			}
			//even more stupid, the value we get is a string like "rgb(255, 255, 255)" instead 
			//of a string like "ffffff". at least in moz1.4. very fucked.
			if ((typeof(startColor) != 'undefined') && (startColor.substr(0, 4) == 'rgb(')) {
				var csvString = startColor.substring(4, startColor.length -1);
				var csvJunks  = csvString.split(', ');
				startColor = '';
				for (var i=0; i<3; i++) {
					startColor += parseInt(csvJunks[i]).toString(16);
				}
			}
		} else {
			try {
				var startColor = this._element.currentStyle.backgroundColor;
			} catch (e) {
				//opera
				try {
					var startColor = this._element.style.backgroundColor;
				} catch (ee) {
					var startColor = 'ffffff';
				}
			}
		}
		if (typeof(startColor) != 'string') startColor = 'ffffff';
		
		if (startColor.substr(0,1) == '#') startColor = startColor.substr(1);
		if (startColor.length != 6) startColor = 'ffffff';
		this._bgColorWarning.startColor = startColor;
	}
	
	/**
	* updates the bg color of the field.
	* @access private
	* @return void
	* @see    setBgColorWarning()
	*/
	this._updateBgColorWarning = function() {
		if (typeof(this._bgColorWarning) == 'undefined') return;
		
		var startWarningAt = this._bgColorWarning.kickInValue;
		
		var startColor = this._bgColorWarning.startColor;
		var endColor   = this._bgColorWarning.endColor;
		
		var startRed   = hexdec(startColor.substr(0,2)); //.toString(16);
		var startGreen = hexdec(startColor.substr(2,2));
		var startBlue  = hexdec(startColor.substr(4,2));
		var endRed     = hexdec(endColor.substr(0,2));
		var endGreen   = hexdec(endColor.substr(2,2));
		var endBlue    = hexdec(endColor.substr(4,2));
		
		try {
			var lengthNow = this._element.value.length;
			lengthUse = lengthNow - startWarningAt;
			if (lengthUse <= 0) {
				var color = startColor; //'#FFFFFF';
			} else {
				var percent = lengthUse * 100 / (this._maxLength - startWarningAt);
				
				var newRed   = this._mixColor(startRed,   endRed,   percent);
				var newGreen = this._mixColor(startGreen, endGreen, percent);
				var newBlue  = this._mixColor(startBlue,  endBlue,  percent);
				var color = '#' + newRed.toString(16) + newGreen.toString(16) + newBlue.toString(16);
			}
			this._element.style.backgroundColor = color;
		} catch (e) {
			//alert(e);
		}
	}
	
	/**
	* takes 2 colors and returns one that lies somewhere in between.
	* 
	* example: startColor is white, endColor is black, percent is 50. 
	*          the returned color will be gray. if % is 80 then it will 
	*          be more black.
	* 
	* this method is called 3x, once for red, once for green and once for blue.
	* 
	* @access private
	* @param  int startColor (0-255)
	* @param  int endColor   (0-255)
	* @param  int percent    (0-100)
	* @return int
	*/
	this._mixColor = function(startColor, endColor, percent) {
		var diffColor = endColor - startColor;
		if (diffColor > 0) {
			return startColor + parseInt(diffColor / 100 * percent);
		} else if (diffColor < 0) {
			return startColor + parseInt(diffColor / 100 * percent);
		} else {
			return startColor; //no change, same color.
		}
	}
	
	
	/**
	* updates the progress bar div.
	* @access private
	* @return void
	*/
	this._updateProgressBar = function() {
		if (!this.useProgressBar) return;
		
		var progressDiv   = document.getElementById(this._objectId + '_progess');
		var progressWidth = progressDiv.offsetWidth;
		var totalBars     = parseInt(progressWidth / 7);
		
		var lengthNow = this._element.value.length;
		if (lengthNow == 0) {
			var numBars = 0;
		} else {
			var percent = lengthNow * 100 / this._maxLength;
			var numBars = Math.ceil(totalBars / 100 * percent);
		}
		if (this._lastNumProgressBars == numBars) return;
		
		this._lastNumProgressBars = numBars;
		
		var newHtml       = '<img src="/_bsImages/spacer.gif" width="1" height="1" border="0">';
		var progressJunk  = '<div style="display:inline; width:5px; height:8px; background-color:#08246B; margin-top:3px; margin-bottom:3px; margin-left:1px; margin-right:1px;"><img src="/_bsImages/spacer.gif" width="5" height="8" border="0"></div>';
		for (var i=0; i<numBars; i++) {
			newHtml += progressJunk;
		}
		var progressDiv   = document.getElementById(this._objectId + '_progess');
		progressDiv.innerHTML = newHtml;
	}

	this._constructor(); //call the constructor. needs to be at the end.
	
	
}
