/*
UvumiTools TextArea v1.0.2 http://tools.uvumi.com/textarea.html

Copyright (c) 2008 Uvumi LLC

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/

var UvumiTextarea = Class({

	Implements:Options,

	options:{
		selector:'textarea',	//textareas CSS selector, default 'textarea' select all textboxes in the document
		
		maxChar:1000,			//maximum number of characters per textarea
		
		resizeDuration:500,		//animation duration of progress bar and resizing, in milliseconds
		
		minSize:false,			//minimum height in pixels you can reduce the textarea to. If set to false, the default value, the original textarea's height will be used as a minimum
		
		maxSize:false,			//Maximum height you can expand the textarea, so it doesn't break your document. When you reach the height, the it won't extend anymore, and a scroll bar will appear instead, if false, no max height
		
		catchTab:true,			//if the textarea should override the tab default event and insert a tab in the text. Default is true, but if you're not going to support it on the back-end, you should disable it
		
		classPrefix:'tb'		//The CSS classes associated to the new elements will start with the string defined in this option.
								//Usefull if you use this plugin on several pages with different styles. if you have a red theme and a blue theme,
								//initialize an instance with classPrefix:'red' and another with classPrefix:'blue', and you'll just have to create a set of CSS rules
								//redControls, redProgress, redProgressBar, redCounter and another where you replace red by blue, blueControls, blueProgress...
	},

	initialize: function(options){
		this.setOptions(options);
		//each textarea will have its own elements, all storred in arrays
		this.tbDummies =[];
		this.tbCounters =[];
		this.tbProgress = [];
		this.tbProgressBar = [];
/*		window.addEvent('domready',this.domReady.bind(this));	
	},
	
	domReady: function(){
*/		//the text area array is initialized with an optional CSS selector. The default selector is just 'textarea', affecting all textareas in the document
		this.textareas=$$(this.options.selector);
		this.textareas.each(this.buildProgress,this);
		this.tbProgressEffects = new Fx.Elements(this.tbProgressBar, {
			'link':'cancel'
		});
		this.tbEffects = new Fx.Elements(this.textareas,{
			'duration':this.options.resizeDuration,
			'link':'chain'
		});
		this.textareas.each(function(el,i){
			this.update.delay(i*(this.options.resizeDuration+50),this,i);			
		},this);
	
	},
	
	//this functions builds all the new HTML elements and assigns events
	buildProgress: function(textbox,i){
		if(textbox.get('title')) this.options.maxChar = textbox.get('title');
		textbox.getParent().addClass('no_bottom');
		textbox.setStyle('overflow','hidden');
		
		//if minimum size option is false, we use the original size as minimum.
		if(!this.options.minSize){
			this.options.minSize = textbox.getSize().y;
		}
		//This will not be visible by user. It's div with the exact same specification as the textarea : same size, same font, same padding, same line-height....
		//on every key stroke, the textarea content is copied in this div, and if the div size is different from on previous key stroke, the textarea grow or shrink to this new height.
		//we had to use this hack because if working diretly with the textarea itself, comparing it's height and scroll-height, it wored fine for growing, but there was to good looking way to make it shrink to the right position.
		if(Browser.Engine.presto){
			var lineHeight = (textbox.getStyle('line-height')==1200?'1.1em':textbox.getStyle('line-height'));
			var fontSize = (textbox.getStyle('font-size')=="relative"?'0.85em':textbox.getStyle('font-size'));			
		}else{
			var lineHeight = textbox.getStyle('line-height');
			var fontSize = textbox.getStyle('font-size');
		}
		this.tbDummies[i] = new Element('div',{
			'styles':{
				'font-size':fontSize,
				'font-family':textbox.getStyle('font-family'),
				'width':textbox.getStyle('width').toInt()-2*textbox.getStyle('border-width').toInt(),
				'padding':textbox.getStyle('padding'),
				'padding-bottom':30,
				'line-height':lineHeight,
				'position':'absolute',
				'top':0,
				'left':-9999
			}
		}).store('height',0).inject(document.body);
		
		textbox.addEvents({
			'keydown':this.onKeyPress.bindWithEvent(this,[i,this.options.catchTab]), // here and like on all the other events, we must use bindWithEvent because we pass an additionnal parameter beside the event object
			'keyup':this.onKeyPress.bindWithEvent(this,i),
			'focus':this.startObserver.bind(this,i),
			'blur':this.stopObserver.bind(this)
		});

		this.tbProgress[i]=new Element('div',{
			'class':this.options.classPrefix+'Progress',
			'styles':{
				'position':'relative',
				'overflow':'hidden',
				'display':'block',
				'position':'relative',
				'width':'100%'
//				'margin':'5px 0'
			}
		}).inject(textbox.getParent().getParent(),'after');
		this.tbProgressBar[i]=new Element('div',{
			'class':this.options.classPrefix+'ProgressBar',
			'styles':{
				'position':'absolute',
				'top':0,
				'left':0,
				'height':'100%',
				'width':'100%'
			}
		}).inject(this.tbProgress[i]);
		this.tbCounters[i] = new Element('div', {
			'class':this.options.classPrefix+'Counter',
			'styles':{
				'position':'absolute',
				'top':0,
				'left':0,
				'height':'100%',
				'width':'100%',
				'text-align':'center'
			}
		}).inject(this.tbProgress[i]);
	},
	
	onKeyPress: function(e,i,tab) {
		this.stopObserver();
		var event = new Event(e);
		if(event.key == "tab"){
			if(tab){
				event.preventDefault();
				this.insertTab(i);
			}
		}
		if(!event.shift && !event.control && !event.alt && !event.meta) this.update(i);
		this.startObserver(i);
	},
	
	startObserver:function(i){
		$clear(this.observer);
		this.previousLength = this.textareas[i].get('value').length;
		this.observer = this.observe.periodical(500,this,i);
	},
	
	stopObserver:function(){
		$clear(this.observer);
	},
	
	observe:function(i){
		if(this.textareas[i].get('value').length != this.previousLength){
			this.previousLength = this.textareas[i].get('value').length;
			this.update(i);
		}
	},

	update: function(i) {
	
		if(Browser.Engine.presto && this.textareas[i].get('text')!=''){
			var text = this.textareas[i].get('text');
			text = text.replace(/\r\n/g, '\n');
			this.textareas[i].erase('text');
			this.textareas[i].set('value',text);
		}
		
		var value = this.textareas[i].get('value');

		if (value.length > this.options.maxChar){
			value = value.substring(0, this.options.maxChar);
			this.textareas[i].set('value', value);
			var count = 0;
			var percentage = 0;
			var ct = 'No character left';
			this.tbProgress[i].highlight("#f66");
		}else{
			var count = this.options.maxChar - value.replace(/\r\n/g, ' ').length;
			var percentage = (count * this.tbProgress[i].getSize().x / this.options.maxChar).toInt();
			if (count == 1) {
			var ct = '1 character left';
			} else {
				var ct = count + ' characters left';
			}
		}
		var effect = {};
		effect[i]={'width':percentage};
		this.tbProgressEffects.start(effect);
		
		this.tbCounters[i].empty().appendText(ct); //hack because of bug in IE
		
		var html = value.replace(/\n/g, '<br/>')+(Browser.Engine.trident?'<br/><br/>':'');
		
		this.tbDummies[i].set('html',html);
		var height = (this.tbDummies[i].getSize().y>this.options.minSize?this.tbDummies[i].getSize().y:this.options.minSize);
		if(this.options.maxSize && height>this.options.maxSize){
			height = this.options.maxSize;
		}
		if(this.tbDummies[i].retrieve('height')!=height){
			this.tbDummies[i].store('height',height);
			effect = {};
			effect[i]={'height':height};
			this.textareas[i].setStyle('overflow',(height<this.textareas[i].getScrollSize().y?'auto':'hidden'));
			this.tbEffects.start(effect);
			
		}
	},
	
	insertTab: function(i) {
		if(Browser.Engine.trident) {
			var range = document.selection.createRange();
			range.text = "\t";
		}else{
			var start = this.textareas[i].selectionStart;
			var end = this.textareas[i].selectionEnd;
			var value = this.textareas[i].get('value');
			this.textareas[i].set('value', value.substring(0, start) + "\t" + value.substring(end, value.length));
			start++;
			this.textareas[i].setSelectionRange(start, start);
		}
	}
});