/**
 * @todo - add comments :-)
 *
 */

(function($) {
	
  //jquery event
	$.event.special.realchange = {
		
		setup: function() {
			var tag = $(this).attr('tagName');
			var type = $(this).attr('type');
			
			//no sense to use realchange for non-textedit elements
			if (!(tag == 'TEXTAREA' || ( tag == 'INPUT' && type == 'text' ) ))
				 	return false;

			Internal.init($(this));
		},
		
		teardown: function() {
			Internal.finalize($(this));
		}
		
	};
	
  //plugin initialization
	$.fn.extend({
		
		realchange: function(fn) {
			return fn ? this.bind("realchange", fn) : this.trigger("realchange");
		},
		
		unrealchange: function(fn) {
			return this.unbind("realchange", fn);
		}
		
	});
	
	var Internal = {
		
		// each object data API
		
		data: [],
		
		createDataObject: function(obj){
			obj.attr('_realchangedataindex',Internal.data.push({})-1);
			return Internal.data[Internal.data.length - 1];
		},
		
		getDataObject:function(obj){
			return Internal.data[obj.attr('_realchangedataindex')];
		},
		
		deleteDataObject:function(obj){
			delete Internal.data[obj.attr('_realchangedataindex')];
		},
		
		// object event handlers
		
		change: function(event){
			event = $.event.fix( event || window.event );
			Internal.checkChange($(this), event);
		},
		
		// realchange event raising
		
		raiseChange: function(obj, event, _old, _new){
			
			event = event || $.event.fix({});
			
			$.extend(event,{
				type: "realchange",
				target: obj[0],
				info: {_old:_old,_new:_new}
			});
			
			return $.event.handle.apply(obj[0], [event]);
		},
		
		checkChange: function(obj, event) {
			var data = Internal.getDataObject(obj);
			var old = data._value;
			var cur = obj.attr('value');
			if (cur !== old) {
				Internal.raiseChange(obj, event, old, cur);
				data._value = cur;
			}
		},
		
		// value change monitoring
		
		initInterval: function(obj){
			                 
			var handler = function(){
				Internal.intervalTick(obj);
			}

			var data = Internal.getDataObject(obj);
			data['intervalId'] = setInterval(handler,500);
					
		},
		
		clearInterval: function(obj){
			var data = Internal.getDataObject(obj);
			clearInterval(data['intervalId']);
		},
		
		intervalTick: function(obj){
            
			Internal.checkChange(obj);
		},
		
		// initialization / finalization
		
		init: function(obj){
			Internal.innerInit(obj);
		},
		
		finalize: function(obj){
			Internal.innerFinalize(obj);
		},
		
		innerInit: function(obj){
			
			var d = Internal.createDataObject(obj);
			d._value = obj.attr('value');
			
			//common cross-UA events
			obj.bind('focus',Internal.change);
			obj.bind('blur',Internal.change);
			obj.bind('keyup',Internal.change);
      obj.bind('keydown',Internal.change);
						
			if ($.browser.msie)
				obj[0].attachEvent('paste', Internal.change);
				
			if ($.browser.safari)
				obj[0].addEventListener('paste', Internal.change, false);
				
			if ($.browser.mozilla)
				obj[0].addEventListener('input', Internal.change, false);
			
			if ($.browser.opera)
				obj[0].addEventListener('change', Internal.change, false);
			
		},
		
		innerFinalize: function(obj){
			
			Internal.deleteDataObject(obj);
			
			obj.unbind('focus');
			obj.unbind('blur');
			obj.unbind('keyup');
            obj.unbind('keydown');
					
			if ($.browser.msie)
				obj[0].detachEvent('paste', Internal.change);
				
			if ($.browser.safari)
				obj[0].removeEventListener('paste', Internal.change, false);
				
			if ($.browser.mozilla)
				obj[0].removeEventListener('input', Internal.change, false);
			
			if ($.browser.opera)
				obj[0].removeEventListener('change', Internal.change, false);
			
		}
			
	};
  

})(jQuery);