// JavaScript Document
function RetrieveObject(what) {
    var output = '';
    for (var i in what) {
        if (typeof what[i] == 'object') {
            output += RetrieveObject(what[i]);
        }
        else
            output += i + ' = ' + what[i] + '\n';
    }
    return output;
}


function myEval(val){
	return eval(val);
}


//////////////////////////////////
Prototype.FunctionFragment = '[a-zA-Z]+\\w+\\s*(\\([\\w\\s]*\\))?'; //detecta una función y sus argumentos
var Choquin = {
  Version: '1.0.0',
  require: function(libraryName) {
    // inserting via DOM fails in Safari 2.0, so brute force approach
    var script = '<script type="text/javascript" src="'+libraryName+'"></script>';
	//alert("ahí vamos:"+script);
	document.write(script);
  },
  load: function() {
    if((typeof Prototype=='undefined') || 
       (typeof Element == 'undefined') || 
       (typeof Element.Methods=='undefined') ||
       parseFloat(Prototype.Version.split(".")[0] + "." +
                  Prototype.Version.split(".")[1]) < 1.5)
       throw("choquin requires the Prototype JavaScript framework >= 1.5.0");
    
		
		$A(document.getElementsByTagName("script")).findAll( function(s) {
	 		return (s.src && s.src.match(/myprototype\.js(\?.*)?$/))
    }).each( function(s) {
	  console.debug("Choquin.load");

		var path = s.src.replace(/myprototype\.js(\?.*)?$/,'');
      var includes = s.src.match(/\?.*load=([a-z,]*)/);
      (includes ? includes[1] : 'myprototype_scrollable').split(',').each(
       function(include) { Choquin.require(path+include+'.js') });
    });
  }
}

/// por ahora no anda, porque como scriptaculous.js usa $A(document.getElementsByTagName("script"))
/// eso parece descomponer la posibilidad de acceder posteriormente a los scripts

//Choquin.load();																							 
//Choquin.require("/funciones/js/myprototype_scrollable.js");


// Extension to Ajax allowing for classes of requests of which only one (the latest) is ever active at a time
// - stops queues of now-redundant requests building up / allows you to supercede one request with another easily.

// just pass in onlyLatestOfClass: 'classname' in the options of the request



/*
Object.extend(Ajax.Base.prototype,{

	_setOptions: Ajax.Base.prototype.setOptions,
	
	setOptions: function(options) {
		this._setOptions();
		this.remainingRetries = this.options.retries;
	},
	
	responseIsSuccess: function() {
    var status = this.getStatus();
		return status == undefined
        || status == 0
        || (status >= 200 && status < 300);
	 }

});


// Agregado para manejar evento Abort
Ajax.Request.Events =
  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete', 'Abort'];

Ajax.Request.totalCount=0;


Object.extend(Ajax.Request.prototype,{
  _onStateChange: Ajax.Request.prototype.onStateChange,
	onStateChange: function() {
		(this.options.onStateChange||Prototype.emptyFunction)(this.transport);
  	this._onStateChange();
	}
});


////////////////////////////////////////////
Ajax.currentRequests = {};

Ajax.Responders.register({
  onCreate: function(request) {
		if(!request.uniqueId) request.uniqueId = ++Ajax.Request.totalCount;

		//console.debug("Ajax.Responsders.onCreate:"+request.options.onlyLatestOfClass);
		if (request.options.onlyLatestOfClass && Ajax.currentRequests[request.options.onlyLatestOfClass]) {
            // if a request of this class is already in progress, attempt to abort it before launching this new request
           try { Ajax.currentRequests[request.options.onlyLatestOfClass].abort(); } catch(e) {}
        // keep note of this request object so we can cancel it if superceded
	   }
			//console.debug(request.options.onlyLatestOfClass);
			if(request.options.onlyLatestOfClass) Ajax.currentRequests[request.options.onlyLatestOfClass] = request;
  },
  onComplete: function(request) {
		if (request.options.onlyLatestOfClass) {
			// remove the request from our cache once completed so it can be garbage collected
			 Ajax.currentRequests[request.options.onlyLatestOfClass] = null;
			 delete Ajax.currentRequests[request.options.onlyLatestOfClass];
     }
   }
});
/////////////////////////////////////////////////////////////////////////////////////////////////////



///////////////////////////////////////////////////////////
///Ajax addon timeout
Object.extend(Ajax.Responders,{

	callInProgress: function(xmlhttp) {
		switch (xmlhttp.readyState) {
			
			//0 = uninitialized
			//1 = loading
			//2 = loaded
			//3 = interactive
			//4 = complete
			
			
			case 1: case 2: case 3:
				return true;
				break;
			// Case 4 and 0
			default:
				return false;
				break;
		}
	},
	
	showFailureMessage: function(request) {
		console.debug(['uh oh, it looks like the network is down. Try again shortly',request]);
	},
	
													onNetworkdown: function(request){
														if (Ajax.Responders.callInProgress(request.transport)) {					
															request.transport.abort();
														}
														
														if(--request.remainingRetries >0) {
															request['timeoutRequest'] = window.setTimeout(function(){request.requestAgain(true)} ,5000 * Math.randRange(1,2,true));
														}
														else{
															Ajax.Responders.showFailureMessage(request);
															(request.options['onTimeout']||request.options['onFailure']||Prototype.K)(request.transport, request.json);
														}
														Ajax.Responders.dispatch('onNetworkdown', this, this.transport);
													},
	
	
	onNetworkdown: function(request){
		if (Ajax.Responders.callInProgress(request.transport)) {					
			request.abort();
		}

		Ajax.Responders.dispatch('onNetworkdown', request, request.transport);
		(request.options.onNetworkdown||Prototype.emptyFunction)(request.transport);
		
		Ajax.Responders.clearTimer(request);
		request.networkdownDecay+=.2;
		request.networkdownDecay = [request.networkdownDecay,10].min();
		
	
		if(request.options.onlyLatestOfClass) Ajax.currentRequests[request.options.onlyLatestOfClass] = request;

		if(request.remainingRetries==undefined || --request.remainingRetries >0)
			request.timerId = window.setTimeout(function(){request.requestAgain(true)} ,6000 * Math.randRange(1,2,true) * request.networkdownDecay);
		else{
			//Ajax.Responders.showFailureMessage(request);
			(request.options['onTimeout']||request.options['onFailure']||Prototype.K)(request.transport, request.json);
		}
	},
	
	
	clearTimer: function(request){
		if(request.timerId) {
			//console.debug(["clearTimer ",request.uniqueId,request]);
			window.clearTimeout(request.timerId);
			request.timerId = null;
		}
	},
	
	setTimer: function(request){
		Ajax.Responders.clearTimer(request);
		request.timerId = window.setInterval(Ajax.Responders.onTimer.bind(request),request.options.timeout || 60000*5); //5 minutos
		//console.debug(["setTimer ",request.uniqueId,request]);
	},
	
	onTimer: function(){
		Ajax.Responders.clearTimer(this);
		if(this.transport.readyState == 3 && this.getStatus()==200 && this.maybeworking!=true) {
			Ajax.Responders.setTimer(this);
			this.maybeworking = true;
			return;
		}
		
		(this.options.onTimer||Prototype.emptyFunction)(this);
		//console.debug(["onTimer ",this.uniqueId,this]);
		Ajax.Responders.onNetworkdown(this);
	}
	
});

// Register global responders that will occur on all AJAX requests

Ajax.Responders.register({
	
	onCreate: function(request) {
		Ajax.Responders.setTimer(request);
		request.maybeworking = false;
		request.networkdownDecay = request.networkdownDecay||1;
	},
	
	onComplete: function(request) {
		Ajax.Responders.clearTimer(request);
	}
});


//////////////////////////
///// RED DESCONECTADA

Ajax.Responders.register({
	onComplete: function(request) {
		request.getStatus = request.getStatus||Ajax.Request.prototype.getStatus;
		
		setTimeout(function(){
			//console.debug(request.transport.readyState);
			if(request.transport.readyState==0){
				//es un abort
				Ajax.Responders.dispatch('onAbort', this, this.transport);
				(request.options.onAbort||Prototype.emptyFunction)(this.transport);
			}
			else{
				//no es un abort, es un networkdown?
				if( [0,-1,12007].include(request.getStatus()) ){
					//esto es un servidor caido señores!!!
					Ajax.Responders.onNetworkdown(request);
				}
			}
		},10);
	}
});


//////////////////////////



//dispatch event onCreate for request:
Ajax.Responders.register({
	onCreate: function(request) {
		(request.options.onCreate || Prototype.emptyFunction) (request);
	}
});


///////////////////////////////////////////////////////
Object.extend(Ajax.Request.prototype,{

	getStatus: function(){
		//si el status no está, tiraba error
		try{return this.transport.status} catch(e){return -1}
	},
							
					respondToReadyState: function(readyState) {
					var event = Ajax.Request.Events[readyState];
					var transport = this.transport, json = this.evalJSON();
						//console.debug([event,readyState]);
						if (event == 'Complete') {
							try {
						if(this.getStatus()>0){
									(this.options['on' + this.getStatus()]
							 || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
							 || Prototype.emptyFunction)(transport, json);
								}
								//else console.debug("ABORT")
							} catch (e) {
						this.dispatchException(e);
					  }
					  if ((this.header('Content-type') || '').match(/^text\/javascript/i))
						this.evalResponse();
					}
				
					try {
					  (this.options['on' + event] || Prototype.emptyFunction)(transport, json);
					} catch (e) {
					  this.dispatchException(e);
					}finally{
						  Ajax.Responders.dispatch('on' + event, this, transport, json); ///esta es la diferencia con respecto al original
						}
				
					if (event == 'Complete')
					  this.transport.onreadystatechange = Prototype.emptyFunction;
				  },
				

	abort : function(){
		 try {
	      this.transport.onreadystatechange = Prototype.emptyFunction;
				if (Ajax.Responders.callInProgress(this.transport)) {
					this.transport.abort();
					this.transport.onreadystatechange = Prototype.emptyFunction;
					//Ajax.activeRequestCount--;
					Ajax.Responders.dispatch('onComplete', this, this.transport);
      	(this.options['onComplete'] || Prototype.emptyFunction)(this.transport);

				} else {
					//console.debug("querés abortar un muerto?")
				}
			 Ajax.Responders.clearTimer(this);
			 //clearTimeout(this.timeoutId);
			 //this.timeoutId = null;
			} catch(e) {console.error(["error en Request.abort",e])}
			//if(this.options.onlyLatestOfClass) Ajax.currentRequests[this.options.onlyLatestOfClass] = null;
	},
	

	requestAgain: function(dontRestart){
		this.abort();
		if(!dontRestart) {
			this.remainingRetries = this.options.retries;
			this.networkdownDecay = 1;
		}
		if(this.url) this.request(this.url);
	}
});


*/


////////////////////////////////////////////////
/*
	Base, version 1.0.2
	Copyright 2006, Dean Edwards
	License: http://creativecommons.org/licenses/LGPL/2.1/
*/

var Base = function() {
	if (arguments.length) {
		if (this == window) { // cast an object to this class
			Base.prototype.extend.call(arguments[0], arguments.callee.prototype);
		} else {
			this.extend(arguments[0]);
		}
	}
};

Base.version = "1.0.2";

Base.prototype = {
	extend: function(source, value) {
		var extend = Base.prototype.extend;
		if (arguments.length == 2) {
			var ancestor = this[source];
			// overriding?
			if ((ancestor instanceof Function) && (value instanceof Function) &&
				ancestor.valueOf() != value.valueOf() && /\bbase\b/.test(value)) {
				var method = value;
			//	var _prototype = this.constructor.prototype;
			//	var fromPrototype = !Base._prototyping && _prototype[source] == ancestor;
				value = function() {
					var previous = this.base;
				//	this.base = fromPrototype ? _prototype[source] : ancestor;
					this.base = ancestor;
					var returnValue = method.apply(this, arguments);
					this.base = previous;
					return returnValue;
				};
				// point to the underlying method
				value.valueOf = function() {
					return method;
				};
				value.toString = function() {
					return String(method);
				};
			}
			return this[source] = value;
		} else if (source) {
			var _prototype = {toSource: null};
			// do the "toString" and other methods manually
			var _protected = ["toString", "valueOf"];
			// if we are prototyping then include the constructor
			if (Base._prototyping) _protected[2] = "constructor";
			for (var i = 0; (name = _protected[i]); i++) {
				if (source[name] != _prototype[name]) {
					extend.call(this, name, source[name]);
				}
			}
			// copy each of the source object's properties to this object
			for (var name in source) {
				if (!_prototype[name]) {
					extend.call(this, name, source[name]);
				}
			}
		}
		return this;
	},

	base: function() {
		// call this method from any other method to invoke that method's ancestor
	}
};

Base.extend = function(_instance, _static) {
	var extend = Base.prototype.extend;
	if (!_instance) _instance = {};
	// build the prototype
	Base._prototyping = true;
	var _prototype = new this;
	extend.call(_prototype, _instance);
	var constructor = _prototype.constructor;
	_prototype.constructor = this;
	delete Base._prototyping;
	// create the wrapper for the constructor function
	var klass = function() {
		var ret;
		var obj = this;
		if(obj == window) obj = {};
		if (!Base._prototyping) ret = constructor.apply(obj, arguments);
		obj.constructor = klass;
		return ret;  // de esta forma, la clase funciona tambien como una funcion
	};
	klass.prototype = _prototype;
	// build the class interface
	klass.extend = this.extend;
	klass.implement = this.implement;
	klass.toString = function() {
		return String(constructor);
	};
	extend.call(klass, _static);
	// single instance
	var object = constructor ? klass : _prototype;
	// class initialisation
	if (object.init instanceof Function) object.init();
	return object;
};

Base.implement = function(_interface) {
	if (_interface instanceof Function) _interface = _interface.prototype;
	this.prototype.extend(_interface);
};
//////////////////////////////////////////////////////////////

















Function.prototype.bind = function() {
  var __method = this, args = $A(arguments), object = args.shift();
  try{var fn = function() {
    return __method.apply(object, args.concat($A(arguments)));
  }}catch(e){throw(Object.extend(e,{description:e.description+'\nError binding function '+__method+' to '+object+' with arguments '+arguments}))}
	fn.__method = __method;
	return fn;
}

Function.prototype.bindWithDelay = function() {
  var __method = this, args = $A(arguments), object = args.shift(), delay = args.shift();
  try{var fn = function() {
    var newArgs = $A(arguments);
		var ret = function(){__method.apply(object, args.concat(newArgs));}
		//console.debug(ret,delay);
		ret = setTimeout(ret,delay);
		return ret;
  }}catch(e){throw(Object.extend(e,{description:e.description+'\nError binding function '+__method+' to '+object+' with arguments '+arguments}))}
	fn.__method = __method;
	return fn;
}


/*
try{_Number}
catch(e){
	_Number = Number;
	Number = function(what){
		if(what && what.toNumber && typeof what.toNumber == "function") return what.toNumber();
		return _Number(what);
	}
}*/


Number.prototype.inspect = function(){return this;};
String.prototype.inspect = function() {return "'"+this+"'";}

Object.inspect=function(object,level,options){
  try {
	if (object == undefined) return 'undefined';
	if (object == null) return 'null';
	if(object.inspect) return object.inspect();
	if(object.toString && object.toString!=Object.prototype.toString) return object.toString();
	var ret="{";
	var arr=[];
	if(!options) options = {};
	if(!options.inspected) options.inspected = [];
	if(options.inspected.include(object)) return "...";
	options.inspected.push(object);
	for (var i in object) {
		if(typeof object[i]!="function") {
			var parcialRet = Object.inspect(object[i],level,options);
			arr.push ("'"+i+"':"+parcialRet);	
		}
	}
	ret+=arr+"}";
	return ret;
  } catch (e) {
    if (e instanceof RangeError) return '...';
    throw e;
  }
}





Object.extend(Object,{
	extendClone: function(destination,source){
		return Object.extend(Object.clone(destination),source);
	},			  
			  
	null2Empty:function(val){
		if(val==null || val==undefined) return "";
		return String(val);
	},
	
	isNull:function(val){
		return val==null || val==undefined || String(val)=="";
	},
	
	toLowerCase:function(val){
		if(!val) return val;
		if(typeof val.toLowerCase=="function") return val.toLowerCase();
		return val;
	},

	toUpperCase:function(val){
		if(!val) return val;
		if(typeof val.toUpperCase=="function") return val.toUpperCase();
		return val;
	},
	
	toArray: function(val){
		return val instanceof Array ? val : [val];
	},

	stringToDate:function(val){
		//intenta convertir el objeto a un Date, y si no es posible, lo deja como está
		//sólo convierte cadenas, no números
		var date = new Date(val);
		if(!isNaN(date.getYear())) return date;
		return val;
	},

	formatDate: function(val,format){
		if(val && val.format) return val.format(format);		
		return "";
	},

	archiveProperty: function(obj,propname,copy){
		if(!obj) return;
		var oldProperty = obj[propname];
		//if(copy) oldProperty = Object.extend({},oldProperty);
		obj["__________"+propname] = oldProperty;
		if(!copy) try{delete obj[propname];}catch(e){};
	},
	
	backupProperty: function(obj,propname){
		this.archiveProperty(obj,propname,true);
	},
	
	restoreProperty: function(obj,propname){
		if(!obj) return;
		if(obj["__________"+propname]){
			obj[propname] = obj["__________"+propname];
			delete obj["__________"+propname];
		}
		return obj[propname];
	},
	
	archivedProperty: function(obj,propname){
		if(!obj) return;
		return obj["__________"+propname];
	},
	
	removeProperties: function(obj,properties){
		if(!properties) return;
		if(typeof properties=="string") properties = [properties];
		properties.each(function(property){
			delete obj[property];
		});
		return obj;
	},
	
	intersect: function(obj,properties){
		var ret = {};
		if(!properties) return;
		if(typeof properties=="string") properties = [properties];
		if(arguments.length>2) {
			var arr = $A(arguments);
			arr.shift();arr.shift();
			properties = properties.concat(arr);
		}
		properties.each(function(property){
			if(property in obj) ret[property] = obj[property];
		});
		return ret;
	},

	
	hasMember: function(obj,val){
		//devuelve si este objeto tiene la propiedad o método con nombre val en su prototype
		return obj.constructor.prototype[val]!=undefined;
	},
	
	hasProperty: function(obj,key){
		return (obj && key in obj);
		//return (obj && obj.hasOwnProperty());
	},
	
	hasOwnProperty: function(obj,key){
		return obj && obj.hasOwnProperty(key);
	},
	
	getLongMethods: function(obj){
		for (var i in obj) {
			if (String(i).length>2 && typeof obj[i]=="function") console.debug([i,obj[i]]);
		}
	},
	
	compare: function(obj1,obj2){
		var obj1 = Object.clone(obj1);
		var obj2 = Object.clone(obj2);
		var ret = [];
		
		
		var objs = [ [obj1,obj2,0],[obj2,obj1,1] ];
		
		objs.each(function(obj){
			$H(obj[0]).each(function(pair){
				if(Object.inspect(obj[0][pair.key]) != Object.inspect (obj[1][pair.key])){
					var arr = [];
					arr [obj[2]] = obj[0][pair.key];
					arr [Math.abs(obj[2]-1)] = obj[1][pair.key];
					arr.key = pair.key;
					ret.push(arr);
				}
				delete obj[0][pair.key];
				delete obj[1][pair.key];
			});
		});

		ret.obj1 = obj1;
		ret.obj2 = obj2;
		return ret;
	

	}


});


Choquin.Enumerable = {};

Object.extend(Choquin.Enumerable,{
	sortBy: function(iterator,desc) {
    var val_1 = desc? 1:-1;
		var val_2 = desc? -1:1;
		return this.collect(function(value, index) {
      return {value: value, criteria: iterator(value, index)};
    }).sort(function(left, right) {
      var a = left.criteria, b = right.criteria;
      return a < b ? val_1 : a > b ? val_2 : 0;
    }).pluck('value');
  },
	
  pluck_NOOOO: function(property) {
		//esta extensión permite hacer pluck de métodos además de propiedades, ejemplo [obj,obj].pluck("getName()")
    
	var results = [];
    this.each(function(value, index) {
      var result;
			try{result = eval("value."+property);}catch(e){}
			results.push(result);
    });
    return results;
  },
	
	intersect:function (otherEnumerable){
		return this.select(function(obj){
			return otherEnumerable.include(obj);
		});
	},
	
	grepPartition: function (pattern,iteratorTrue,iteratorFalse,property){
		//pattern puede ser un string, un regExp, o un iterator (funcion)
		var trues = [], falses = [];	
		
		if(typeof pattern == "function") {
			this.each(function(value,index){
				if (pattern (value,index))
					trues.push((iteratorTrue || Prototype.K)(value, index));
				else falses.push((iteratorFalse || Prototype.K)(value, index));
			});
			return [trues,falses];
		}
		
		if(! (pattern instanceof RegExp)) pattern = String(pattern);
		if(typeof pattern == "string") pattern = pattern.findToRegExp();

		this.each(function(value,index){
			var stringValue = String(value).toString();
			if(property) {
				try{stringValue = String(eval("value."+property));}catch(e){}
			}
			if (pattern.test(stringValue))
				trues.push((iteratorTrue || Prototype.K)(value, index));
			else falses.push((iteratorFalse || Prototype.K)(value, index));
		});
		return [trues,falses];
	},
	
	castTo: function(className){
		return this.collect(function(element){return new className(element)});	
	},
	
	tag: function(tag,options){
		return this.collect(function(element){return String(element).tag(tag,options)});
	},
	
	includeTimes: function(){
		/**
			receives groups of arguments, in the way: obj1,obj2,times1, obj3,times2
			where times are numbers, and any other obj are from any other types;
			It returns true if the object includes at least that times the argument objects.
			for example: ["A", "A", "A", "B", "C"].includeTimes("A",2,"B",1) = true
			for example: ["A", "A", "A", "B", "C"].includeTimes("A",4,"B",1) = false // "A" it's not 4 times in the object
		*/
		
		var values = $A(arguments);
		var ret = true;
		for (var i = 0; ret && i<values.length;i++){
			var item = [];
			do {item.push(values[i]);i++} while (i<values.length && typeof values[i]!="number");
			var times = values[i];
			var found = this.findAll(function(thisItem){
				return item.include(thisItem);
			});
			if (found.size()<times) ret=false;
		}
		return ret;
	},
	
	withoutTimes: function(){
		/**
			receives groups of arguments, in the way: obj1,obj2,times1, obj3,times2
			where times are numbers, and the other obj are any other types
			It returns an array without that times the argument objects;
			for example: ["A", "A", "A", "B", "C"].withoutTimes("A",2,"B",1) = ["A","C"]
		*/
		var values = $A(arguments);
		var ret = [];

		var items = [];
		for (var i=0; i<values.length ; i++){
			var item = [];
			do {item.push(values[i]);i++} while (i<values.length && typeof values[i]!="number");
			item.times = values[i];
			items.push(item);
		}
		
		this.each (function(item){
			itemGroup = items.find(function(argumentItem){
				return argumentItem.include(item);
			});
			
			if (itemGroup && itemGroup.times>0) itemGroup.times--;
			else ret.push(item);
		});
		
		return ret;
	},
	
	withoutItems: function(){
		// devuelve un array sin los items recibidos por parámetro (quita sólo una vez cada item de la lista original)
		var values = $A(arguments);
		var ret = [];
		var clone = this.clone();
		clone.each(function(item,index){
			if (!values.include(item)) ret.push(item);
			else values = values.withoutTimes(item,1);
		});
		return ret;
	},
	
	findTimes: function(times,iterator){
		// encuentra hasta times ocurrencias buscadas
		return this.select(this.timesIterator(times,iterator));
	},
	
	////////////////////////////////////////////////////////////////
	
	timesIterator: function(times,iterator){
		//devuelve un closure con un iterator que a partir de encontrar n veces el iterator, devuelve siempre false;
		return function(item){
			var ret = (times>0) && (iterator||Prototype.K)(item);
			if (ret) times--;
			//if(times==0) throw $break;
			return ret;
		}
	},
	
	atLeast: function(times,iterator){
		//se cumple el iterador al menos n veces? es como all , o any pero con un número
		return this.select(Enumerable.timesIterator(times,iterator)).size()>=times;
	},
	
	groupBy: function(iterator){
		var dictionary = new Dictionary();
		this.each(function(item){
			var key = (iterator||Prototype.K)(item);
			var entry = dictionary.find(function(entry){
				return entry.key == key;
			});
			if(entry) entry.value.push(item);
			else dictionary.add(key,[item]);
		});
		return dictionary.items();
	}
	
});

Object.extend(Enumerable,Choquin.Enumerable);
Object.extend(Array.prototype,Choquin.Enumerable);


/**
	Clase Dictionary
*/
Dictionary = Base.extend({
	constructor: function(){
		this._entries = [];
	},
	
	_each:function(iterator){
		return this._entries.each(iterator);
	},
	
	add: function(key,value){
		var entry = this.find(function(entry){return entry.key==key});
		if (entry) entry.value = value;
		else this._entries.push({key:key,value:value});
	},
	
	item: function(key){
		return (this.find(function(entry){return entry.key==key})||{}).value;
	},
	
	remove: function(key,value){
		this._entries = this._entries.reject(function(entry){return entry.key==key});
	},
	
	clear: function(){
		this._entries = [];
	},
	
	items: function(){
		return this._entries.pluck("value");
	},
	
	includeKey: function(key){
		return this._entries.any(function(entry){return entry.key==key});
	},
	
	includeValue: function(value){
		return this._entries.any(function(entry){return entry.value==value});
	}

	
});

Dictionary.implement(Enumerable);

/////////////////////////////////////////////////////

function $() {
	//esta modificacion devuelve un array de elementos, si es que hay varios con el mismo "name"
 var elements = new Array();
  for (var i = 0; i < arguments.length; i++) {
    var element = arguments[i];
    if (typeof element == 'string'){
			var _element = element;
			element = document.getElementsByName(_element);
			if(element && element.length<=1){
				element = document.getElementById(_element)||element[0];
			}
		}
    if (arguments.length == 1){
    	//alert("aca:"+arguments[0]+" : "+element);
			return Element.extend(element);
		}
    elements.push(Element.extend(element));
  }
	return elements;
}

function $Name(element){
	if(typeof element=='string')
		return document.getElementsByName(element);
	else return element;
};


Choquin.Element = {};
Choquin.Element.Methods = {};

Choquin.Element.Methods._getStyle = Element.Methods.getStyle;

Choquin.Element.Methods = {
  getElementsByClassName: function(element, className) {
		return document.getElementsByClassName(className,element);
	},

  getElementsBySelector: function() {
    var args = $A(arguments), element = args.shift();
    return args.map(function(expression) {
      return expression.strip().split(/s+/).inject([null], function(results, expr) {
        var selector = new Selector(expr);
        return results.map(selector.findElements.bind(selector, element)).flatten();
      });
    }).flatten();
  },
	
  visible: function(element) {
   var ret = Element.getStyle(element,'display') != 'none';
	 return ret;
  },

  show: function(element) {
    for (var i = 0; i < arguments.length; i++) {
      var element = $(arguments[i]);
			element.style.display = '';
 			if (element.style.display!=Element.getStyle(element,"display")) element.style.display="block";
    }
		return element;
  },

	setVisibility:function(element){
		//recibe elm1,elm2...,elmn,visibility,elma,elm4b,elmc,....elmx,visibility
		var last = [];
		arr={"true":"show","false":"hide"};
		for (var i=0;i<arguments.length;i++){
			if(arguments[i].constructor==Boolean) {
				last.invoke(arr[arguments[i]]);
				//Element[arr[arguments[i]]].apply(null,last);
				last=[];
			}else last.push($(arguments[i]));
		}
		return element;
	}, 
	
	toggleVisibility: function(element){
    element = $(element);
    element.setStyle({visibility:[element.getStyle("visibility")=="hidden" ? 'visible' : 'hidden']});
    return element;
	},
	
	moveToFixed:function(element,position){
		var element = $(element);
		if(Element.getStyle(element,"display")=="absolute"){
			var ro = Position.realOffset(element);
			position[0]+=ro[0];
			position[1]+=ro[1];
		}
		Element.setStyle(element,{left:position[0]+"px",top:position[1]+"px"});
		return element;
	},
	
	
	update: function(element, html) {
		var _element = element;
		if(typeof element=='string'){
			var element = $Name(element);
			element = $A(element);
			var elById = document.getElementById(_element);
			if(elById) element.push(elById);
			element.compact();
			if(element.length==0) return;
		}
		else element = [element];
		if(!html) html='';
		
		element.each(function(el){
			if(el.innerHTML!=undefined){
				el.innerHTML = String(html).stripScripts();
				setTimeout(function() {String(html).evalScripts()}, 10);
			}
		});
		
		return element;
	},
	

	getDimensions: function(element,which) {
		element = $(element);
    if(!which) which = "offset";
		if (Element.getStyle(element, 'display') != 'none'){
			var Width = element[which+"Width"]||element.offsetWidth;
			var Height = element[which+"Height"]||element.offsetHeight;
	    return {width: Width, height: Height};
		}

    // All *Width and *Height properties give 0 on elements with display none,
    // so enable the element temporarily
    var els = element.style;
    var originalVisibility = els.visibility;
    var originalPosition = els.position;
    var originalDisplay = els.display;
		els.visibility = 'hidden';
    els.position = 'absolute';
    els.display = 'block';
		
		var Width = element[which+"Width"]||element.offsetWidth;
		var Height = element[which+"Height"]||element.offsetHeight;
		
    els.display = 'none';
    els.position = originalPosition;
    els.visibility = originalVisibility;
		els.display = originalDisplay;
    return {width: Width, height: Height};
  },
	
	getClientDimensions: function(element){
		var offset = Element.getDimensions(element);
		var additional = Element.getAdditionalDimensions(element);
		return {width:offset.width-additional.width, height:offset.height-additional.height};
	},
	
	getVisualDimensions: function(element){
		var element = $(element);
		return {width:element.offsetWidth||0, height:element.offsetHeight||0};
	},
	
	getAdditionalDimensions: function(element){
		element = $(element);
		var width = 0, height = 0;
		width+=parseFloat(element.getStyle("border-left-width"))||0;
		width+=parseFloat(element.getStyle("border-right-width"))||0;
		width+=parseFloat(element.getStyle("padding-left"))||0;
		width+=parseFloat(element.getStyle("padding-right"))||0;
		height+=parseFloat(element.getStyle("border-top-width"))||0;
		height+=parseFloat(element.getStyle("border-bottom-width"))||0;
		height+=parseFloat(element.getStyle("padding-top"))||0;
		height+=parseFloat(element.getStyle("padding-bottom"))||0;

		return {width:width,height:height};
	},


	swapNodes: function (nodeA, nodeB) {
		var nodeA = $(nodeA);
		var nodeB = $(nodeB);
		var nextSibling = nodeA.nextSibling;
		var parentNode = nodeA.parentNode;
		nodeB.parentNode.replaceChild(nodeA, nodeB);
		nodeA.parentNode.insertBefore(nodeB, nodeA.nextSibling);  
	},
		
	sortBy: function(element,iterator,desc){
		element = $(element);
		var childNodes = $A(element.childNodes);
		if(!childNodes) return;
		childNodes = childNodes.sortBy(iterator,desc);
		
		childNodes = childNodes.collect(function(node){
			return element.removeChild(node);
		});
		
		childNodes.each(function(e){
			if(e.nodeType!=3) element.appendChild(e);
		});
		
		return element;
	},
	
	insertAfter: function(element,node,referenceNode){
		element.insertBefore(node, referenceNode.nextSibling);
	},
	
	insertChild: function(element,node){
		var firstChild = element.childNodes[0];
		if(firstChild) element.insertBefore(node,firstChild);
		else element.appendChild(node);
	},
	
	clear: function(element){
		element.innerHTML = "";
		return element;
	},
	
	/*filter: function(element,textoOrRegExpIterator,property){
		if(textoOrRegExpIterator) element.__filtered = {element:element,textoOrRegExpIterator:textoOrRegExpIterator,property:property};		
		if(!element.__filtered) return;
		
		var iteratorTrue = function(element){
			if($(element).setStyle) $(element).setStyle({display:""});
		};
		var iteratorFalse = function(element){
			if($(element).setStyle) $(element).setStyle({display:"none"});
		};
		
		return $A($(element).childNodes).grepPartition(element.__filtered.textoOrRegExpIterator,
		iteratorTrue,iteratorFalse,element.__filtered.property);	
	},*/

	/*appendFilteredChild: function(element,child){
		var element = $(element), child = $(child);
		if(element.filterFunction) {
			element.filterFunction(child);
		}
		(element._230984209appendChild || element.appendChild) (child);
	},*/
	
	/*applyFilter: function(element,filterFunction){
		var element = $(element);
		if(element.appendChild != element.appendFilteredChild) {
			element._230984209appendChild = element.appendChild;
			element.appendChild = element.appendFilteredChild;
		}
		element.filterFunction = filterFunction;
		element.filter(filterFunction);
	},*/
	//////////////////////////////////
	registerChildNodesFilter: function(element,filterFunction){
		var element = $(element);
		element._registeredChildNodesFilters = element._registeredChildNodesFilters || [];
		element._registeredChildNodesFilters.push(filterFunction);
		element.applyFiltersToChildNodes();
		return element;
	},
	
	unregisterChildNodesFilter: function(element,filterFunction){
		if(!filterFunction) return;
		var element = $(element);
		element._registeredChildNodesFilters = (element._registeredChildNodesFilters || []).without(filterFunction);
		return element;
	},
	
	applyFiltersToChildNodes: function(element){
		var element = $(element);
		$A(element.childNodes).each(function(childNode){
			Element.applyFiltersToChildNode(element,childNode);
		});
	},
	
	applyFiltersToChildNode: function(element,childNode){
		var element = $(element);
		var childNode = $(childNode);
		element._registeredChildNodesFilters = element._registeredChildNodesFilters || [];
		element._registeredChildNodesFilters.each(function(filter){
			filter.apply(element,[childNode]);
		});
		return element;
	},
	
	getChildNodesFiltersCount: function(element){
		var element = $(element);
		return (element._registeredChildNodesFilters && element._registeredChildNodesFilters.length) || 0;
	},
	
	appendFilteredChild: function(element,childNode){
		var element = $(element), childNode = $(childNode);
		//console.assertNotGreater(element.getChildNodesFiltersCount(),0,"No tiene filtros...",element,childNode);
		Element.applyFiltersToChildNode(element,childNode);
		element.appendChild(childNode);
	},
	//////////////////////////////////
	scrollToElement: function(element,childNode){
		var element = $(element), childNode = $(childNode);
		var relativeOffset = Position.relativeOffset(childNode);
		element.scrollLeft = relativeOffset[0];
		element.scrollTop = relativeOffset[1];
		return element;
	},
	
	flatWidth: function(element){ //devuelve el ancho si todos los elementos estuvieran en hilera
		var element = $(element);
		var ret = 0;
		$A(element.childNodes).each(function(node){
			var value = $(node).offsetWidth;
			if(value) ret+=value;
		})
		return ret;
	},
	
	flatten: function(element){ //extiende el elemento para que todos los elementos internos puedan caber en hilera
		var element = $(element);
		if(!element) return;
		var ret = 0;
		$A(element.childNodes).each(function(node){
			if(node.nodeType!=3) {
				$(node).setStyle({display:'inline',float:'left'});
				var value = $(node).offsetWidth;
				if(value) ret +=value;
			}
		});
		element.setStyle({width:ret+'px'});
		return element;
	}

	
};

Object.extend(Element, Choquin.Element.Methods);
Element.addMethods(Choquin.Element.Methods);



/////////////////////////////////////////////////////////////


Form.Element.getValue= function(element) {
	///sobreescribí esta función para que soporte varios elementos con el mismo name
	element = typeof element=='string' ? $$('#'+element):element;
	var ret= [];
	if(!element || element.length==0) return ret;
	if(element.options || !element.length) element=[element];
	for(var i=0;i<element.length;i++){
		var method = element[i].tagName.toLowerCase();
		var parameter = Form.Element.Serializers[method](element[i]);
		if (parameter){
			if(parameter.push)	ret = ret.concat(parameter);
			else ret.push(parameter);
		}
	}
	return ret;
}

$F = Form.Element.getValue;


Form.Element.setValue=function(element,value) {
	var old = element;
	element = $(element);
	//alert(element+" "+old+" "+element.tagName);
	if(!element) return;
	if(!element || element.options || !element.length) element=[element];

	for(var i=0;i<element.length;i++){
		var method = element[i].tagName.toLowerCase();
		switch(method){
			case 'input':
				switch(element[i].type.toLowerCase()){
					case 'submit':
					case 'hidden':
					case 'password':
					case 'text':
						element[i].value=value||"";
					break;
					
					case 'checkbox':
					case 'radio':
						var val=value;
						if(typeof val=="boolean") element[i].checked = val;
						else{
							if(!val || !val.any) val=$A(String(value));
							element[i].checked = val.any(function(node){return node==element[i].value});
						}
					break;
				}
			break;
			
			case 'textarea':
				element[i].value=value||"";	
			break;
			
			case 'select':
				var val=value;
				if(val==null || val==undefined) {
					if(element[i].type == 'select-one') {
						element[i].selectedIndex=0;
						continue;
					}
					else val=[];
				}
				if(!val.any) val=String(val).split(',');//val=$A(String(value));
				for(var i2=0;i2<element[i].options.length;i2++){
					var sel = !val.length?false:val.any(function(node){ return node==element[i].options[i2].value});
					element[i].options[i2].selected = sel;
				}
			break;
		}
	}
	if (element[0].onchange) element[0].onchange();
	//llamo a la onchange ya que estamos...
	
}


Form.Element.checkFileExtension=function(element,extensiones,botonParaHabilitar){
	//chequea que el control "element" tenga una de las entensiones dentro del string o array "extensiones"
	//si no se pasa extensiones, siempre devuelve true
	var control = $(element);
	if(!control) return false;
	var archivo = control.value;
	var ret=String(archivo).checkFileExtension(extensiones);
	if(botonParaHabilitar) $(botonParaHabilitar).disabled=!ret;
	return ret;
}


Object.extend(Form.Element,{
	update:function(element,values){
		element = $(element);
    var method = element.tagName.toLowerCase();
    var parameter = Form.Element.Updaters[method](element,values);
	},
	
	setValueFromText:function(element,text){
		element = $(element);
		if(typeof text=="string") text =[text];
		var text = text.inject([],function(what,valor){
			what.push(String(valor).toLowerCase());
			return what;
		});
		var method = element.tagName.toLowerCase();
		switch(method){
			case 'select':
				for(var i = 0;i<element.options.length;i++){
					var option = element.options[i];
					option.selected = text.include(String(option.text).toLowerCase());
				}
			break;
		}
		if (element.onchange) setTimeout(function(){if(element && element.onchange && element.parentNode) element.onchange()},10);
	},
	
	getText:function(element){
		element = $(element);
		var method = element.tagName.toLowerCase();
		var ret = [];
		switch(method){
			case 'select':
				for(var i = 0;i<element.options.length;i++){
					var option = element.options[i];
					if(option.selected)  ret.push(option.text);
				}
			break;
		}
		return ret;	
	}
});

Form.Element.Updaters = {
	select: function(element,options){
		var element = $(element);
		var value = Form.Element.getValue(element);
		var lastopt = element.options.length ? element.options[element.options.length-1] : {};
		element.options.length= (element.options.length && element.options[0].value==0)?1:0;
		
		for(var i=0;i<options.length;i++){
		
			var ii=0;
			var nodo = document.createElement("option");
			var h = $H(options[i]);
			h.each(function(e){
				try{
					if(ii==0) nodo.value=e[1];
					if(ii==1) nodo.text=e[1];
					if(ii==2) nodo.title=e[1];
					if(ii==3) {
						nodo.data=e[1];
						//console.debug(e[1]);
					}
				}catch (e){};
		
				ii++;
				if(ii>3) throw $break;
					
			});
			
			try{element.add(nodo,null);}catch(e){element.add(nodo)}
		}
				
		if (lastopt.value==-1) try{element.add(lastopt,null);}catch(e){element.add(lastopt);}
		Form.Element.setValue(element,value);
	}
}


/////////////////////

Form.setValue=function(form,obj){
	var obj=$H(obj);
	obj.each(
		function(elm){
			Form.Element.setValue(elm[0],elm[1]);
		}
	);
}

Form.getValue=function(form){
	var obj={};
	var elements = this.getElements(form);
	elements.each(function(elm){obj[elm.name]=Form.Element.getValue(elm.name)});
	return obj;
}


Form.validate=function(form,options){
	try{
		var elements = this.getElements(form);
		var ret = true;
		var focused=false;
		var options = options||{};
		elements.each(function(elm){
			var validation = elm.getAttribute("onvalidate")||elm.getAttribute("onValidate");
			if(validation){
				elm.error = !myEval.bind(elm)(validation);
				if(elm.error && !focused) {elm.focus();focused=true}
				
				if (options.errorClassName){
					elm[elm.error?"addClassName":"removeClassName"](options.errorClassName);
				} else {
					if (elm.error){
						if(elm.style._backgroundColor == undefined) elm.style._backgroundColor = elm.style.backgroundColor;
					}
					elm.style.backgroundColor=elm.error?Form.validate._backgroundColor:elm.style._backgroundColor;
				}
				
				ret = ret && !elm.error;
			}
		});
		if (!ret) (options.onError||Prototype.emptyFunction)();
		return ret; ////OJO! es return ret
	}catch(e){alert("Form.validate Error: "+e.message);return false};
}

Form.validate._backgroundColor = "#FFAA66";


//////////////////////


if(!window.Window) Window = {};

Object.extend(Window,{
	stop: function(ventana){
		if (!ventana) ventana = window;
		if (!document.all) ventana.stop(); else ventana.document.execCommand('Stop');
	},
	
	getWidth: function(){
		  var myWidth = 0;
			if( typeof( window.innerWidth ) == 'number' ) {
				//Non-IE
				myWidth = window.innerWidth;
			} else if( document.documentElement && ( document.documentElement.clientWidth) ) {
				//IE 6+ in 'standards compliant mode'
				myWidth = document.documentElement.clientWidth;
			} else if( document.body && ( document.body.clientWidth) ) {
				//IE 4 compatible
				myWidth = document.body.clientWidth;
			}
		return myWidth;
	},
	
	getHeight: function(){
		var myHeight = 0;
		if( typeof( window.innerWidth ) == 'number' ) {
			//Non-IE
			myHeight = window.innerHeight;
		} else if( document.documentElement && ( document.documentElement.clientHeight ) ) {
			//IE 6+ in 'standards compliant mode'
			myHeight = document.documentElement.clientHeight;
		} else if( document.body && ( document.body.clientHeight ) ) {
			//IE 4 compatible
			myHeight = document.body.clientHeight;
		}
		return myHeight;
	},
	
	getDimensions: function(){
		var width = this.getWidth();
		var height = this.getHeight();
		return {width:width,height:height};
	},
	
	load: function(url){
		window.location.href = url;
	}
});

////////////////////////////

if(!window.Menu) Menu = new Object();
Menu.open=function(element){
	var element = $(element);
	element.show();	
	if(element && element.Menu_Timer) clearTimeout(element.Menu_Timer);
	if(this.lastOpened && this.lastOpened!=element) {
		this.closeNow(this.lastOpened);
	}
	this.lastOpened = element;
}

Menu.closeNow=function(element){
	var element = $(element);
	if(element.__clipped) return;
	Element.hide(element);
	if(this.lastOpened = element) this.lastOpened = null;
}

Menu.close=function(element){
	var element = $(element);
	//console.debug(element.__clipped);
	if(element.__clipped) return;
	//setTimeout("Element.hide('"+element+"')",1000);
	element.Menu_Timer = setTimeout(function(){element.hide()},500);
}


Menu.show = Menu.open;
Menu.hide = Menu.close;

Menu.__open = Menu.open;
Menu.__close = Menu.close;
Menu.__closeNow = Menu.closeNow;


Object.extend(Menu,{
	create: function(element,options){
		var element = $(element);
		if(!element) throw(Error.create('No se encontró elemento al crear Menú'));
		var options = options||{};
		element.__menuOptions = {content:[],submenus:[],parentMenu:null};
		this.addSubmenu(options.parentMenu,element);
		
		Event.observe(element,'mouseover',function(event){
			Menu.open(element);
			Event.stop(event)
		}); 
		Event.observe(element,'mouseout',function(event){
			Menu.close(element);
			Event.stop(event)
		}); 
		
		options.content.each(function(content){
			Menu.addContent(element,content);
			Event.observe(content,'mouseover',function(event){Menu.open(element);Event.stop(event)}); 
			Event.observe(content,'mouseout',function(event){Menu.close(element);Event.stop(event)}); 
		});
		
		if("clip" in options) {
			options.clip = $(options.clip);
			Event.observe(options.clip,"click",function(){Menu.toggleClip(element)});
		}
		
		if(options.clipped) Menu.clip(element);
		//this.closeNow(element);

	},
	
	addContent: function(element,content){
		var element = $(element);
		var content = $(content);
		if(!element.__menuOptions) throw("No se puede agregar un contenido de menú a algo que no sea un Menú");
		content.hide();
		if(!element.__menuOptions.content.include(content)) element.__menuOptions.content.push(content);
	},
	
	addSubmenu: function (menu,submenu){
		var menu = $(menu),submenu = $(submenu);
		if(!menu || !submenu) return;
		if(!menu.__menuOptions) throw("No se puede agregar un sumbenu a algo que no sea un Menú");
		if(!submenu.__menuOptions) throw("No se puede agregar como submenú algo que no sea un Menú a un Menú");
		//this.closeNow(submenu);
		if(!menu.__menuOptions.submenus.include(submenu)) menu.__menuOptions.submenus.push(submenu);
		submenu.__menuOptions.parentMenu = menu;
	},
	
	sameGroup: function(menu,submenu){
		var menu = $(menu), submenu = $(submenu);
		if(!menu || !submenu) return false;
		return (menu == submenu) || (this.parentMenus(submenu).include(menu)) || (this.parentMenus(menu).include(submenu));
	},
	
	rootMenu: function(menu){
		var menu = $(menu);
		if(!menu || !menu.__menuOptions) return;
		var found = menu;
		do{
			found = menu;
			menu = menu.__menuOptions.parentMenu;
		} while (menu);
		return found;
	},
	
	parentMenus: function(menu){
		var menu = $(menu);
		if(!menu || !menu.__menuOptions) return;
		var result = [];
		while(menu){
			result.push(menu);
			menu = menu.__menuOptions.parentMenu;
		}
		return result;
	},
	
	open: function(element){
		var element = $(element);
		if(!element.__menuOptions) return this.__open.apply(this,$A(arguments));
		
		if(element.__menuOptions.open) return;
		//console.debug("open "+element.id);
		element.__menuOptions.content.invoke("show");
		
		var menu = element;
		do{
			clearTimeout(menu.__menuOptions.closeTimeout);
			menu = menu.__menuOptions.parentMenu;
		}while (menu);
		
		element.__menuOptions.open = true;
		//cerrar otros menúes que no estén en este grupo
		if(!this.sameGroup(this.lastOpenedMenu, element)){
			this.closeNow(this.lastOpenedMenu);
		}
		
		this.lastOpenedMenu = element;
	},
	
	close: function(element,now){
		var element = $(element);
		if(!element) return;
		if(!element.__menuOptions) return this.__close.apply(this,$A(arguments));
		if(element.__menuOptions.open == false) return;
		
		//console.debug("close "+element.id);
		//console.debug(element.__clipped);
		if(element.__clipped) return;
		element.__menuOptions.open = false;
		clearTimeout(element.__menuOptions.closeTimeout);
		element.__menuOptions.closeTimeout = setTimeout(function(){Menu.closeNow(element)},500);
		//if(this.lastOpenedMenu == element) this.lastOpenedMenu = null;
	},
	
	closeNow: function(element){
		var element = $(element);
		if(!element) return;
		if(!element.__menuOptions) return this.__closeNow.apply(this,$A(arguments));		
		if(element.__clipped) return;
		element.__menuOptions.content.invoke("hide");
	},
		
	clip: function(element){
		var element = $(element);
		Menu.open(element);
		element.show();
		element.__clipped = true;
		element.addClassName('clipped');
	},
	
	unClip: function(element){
		var element = $(element);
		element.__clipped = false;
		element.removeClassName('clipped');
	},
	
	clipped: function(element){
		return $(element).__clipped;
	},
	
	toggleClip: function(element){
		var element = $(element);
		if(Menu.clipped(element)) Menu.unClip(element);
		else Menu.clip(element);
	}
});





//////////////////////////////////




Object.extend(Array.prototype,{
	pushFirst:function(what){
		var oldArray = [].concat(this);
		var newArray = [what].concat(this);
		this.length = 0;
		Object.extend(this,newArray);
		return this;
	},
	
	pushArray:function(array){
		return this.push.apply(this,array);
	},
	
	sliceArray:function(array){
		return this.slice.apply(this,array);
	},
	
	equalTo: function(array){
		var equals = true;
		this.each(function(value,index){
			
		}.bind(this));
	},
	
	shuffle: function(){
		var length = this.length;
		for(var i=0;i<length;i++){
		  var tmp=this[i];
		  var randomNum= Math.round(Math.random()*(length-1));
		  this[i]=this[randomNum];
		  this[randomNum]=tmp;
		}
	}
	
});

Object.extend(Array,{
	createFrom: function(source){
		if(source instanceof Array) return source;
		else return [source];
	}
});

$AA = Array.createFrom;


Object.extend(String.prototype,{
	
	checkFileExtension:function(extensiones){
		//extensiones puede ser un array o un string
		var archivo = this;
		var ret=false;
		if(extensiones && typeof extensiones=="string") extensiones=new Array(extensiones);
		if(!extensiones) ret=true;
		else{
			var ext = archivo.ext();
			if(!ext) return false;
			ret = extensiones.any(function(extension){
				return extension.toLowerCase() == ext.toLowerCase();
			});
			//ret = ((extensiones.join(",").toLowerCase()+",").indexOf(archivo.ext().toLowerCase()+',')) >=0;
		}
		return ret;
	},

	ext : function(){
		var posicion = this.lastIndexOf(".");
		if(posicion==-1) return null;
		return (this.substr(posicion+1)).toLowerCase();
	},

	removeExt : function(){
		///devuelve el nombre del archivo sin la extension
		var posicion = this.lastIndexOf(".");
		if(posicion==-1) return this;
		return (this.substr(0,posicion)).toLowerCase();
	},

	path : function(){
		//devuelve el path de un archivo
		var pos = this.lastIndexOf("/");
		if(pos==-1) 
		var pos = this.lastIndexOf("\\");
		if(pos==-1) return this;
		return this.substr(0,pos+1);
	},

	filename : function(){
		//devuelve el nombre del archivo sin el path
		var pos = this.lastIndexOf("/");
		if(pos==-1) 
		var pos = this.lastIndexOf("\\");
		if(pos==-1) 
		return this;
		return this.substr(pos+1);
	},
	
	testWildcards: function(wildcards){
		wildcards = "^"+wildcards.reemplazar("*","[\\w-\s]*")+"$";
		var re = new RegExp(wildcards,"i");
		return re.test(this);
	},
	
	fixLastCharacter : function (character){
		//si character no es el ultimo caracter de este string, se lo agrega y devuelve el resultado sin modificar el string original
		if(this.substr(this.length-1,1)!=character) return this+character;
		else return this;
	}, 
	
	newFilename : function (){
		var re = /(.+)\((\d*)\)/ig;
		var arr = re.exec(this);
		var newFile;
		if(arr) 
			newFile = this.path()+arr[1].filename()+"("+Number(arr[2]).succ()+")"+ (this.ext()?"."+this.ext():"");
		else newFile = this.path()+this.filename().removeExt()+"(1)"+(this.ext()?"."+this.ext():"");
		return newFile;
	},

	
	trim : function(caracteres){
		//caracteres es el caracter o array de caracteres posibles que se descarta/n del principio y fin
		if (caracteres==null) caracteres = " ";
		var returnString = $A(this);
		if(typeof caracteres == "string") caracteres = [caracteres];
		for (var i = 0;i<caracteres.length;i++){
			var c = caracteres[i];
			while(returnString[0]==c && returnString.length>0) returnString.shift();
			while(returnString[returnString.length-1]==c && returnString.length>0) returnString.pop();
		}
		return returnString.join("");
	},
	
	fixQuotes : function (c){
		if(c==null) c = "'";
		var re = new RegExp(c,"g");
		var ret = this.replace(re,"\\"+c);
		return ret;
	},

	parseFloat: function(){
		//var ret = this.replace(/,/g,".");
		//return ret;
		return parseFloat(this);
	},
	
	parseInt:function(){
		var re = /\d+/ig;
		return re.exec(this);
	},
	
	minusFirst: function(str,fnBusqueda){
		var str = String(str);
		this.fnBusqueda = fnBusqueda;
		//WriteLn(this.fnBusqueda);
		if(!fnBusqueda) this.fnBusqueda = this.indexOf;
		
		var p = this.fnBusqueda(str);
		if(p==-1) return this;
		var parte1 = this.substr(0,p);
		var parte2 = this.substr(p+str.length,this.length);
		return parte1+parte2;
	},
	
	minusLast: function (str){
		return this.minusFirst(str,this.lastIndexOf);
	},
	
	trimFromLast : function(what){
		var found = this.lastIndexOf(what);
		return this.substr(0,found);
	},
	
	parseFilePath: function(){
		var path = this;
		["\\","/"].each(function(bar){
			for (var found = path.indexOf(bar+".."); found != -1; found = path.indexOf(bar+"..")){
				var npath = path.substr(0,found);
				path = npath.trimFromLast(bar)+path.substr(found+3)
			}
		});
		return String(path);
	},
	
	repeat: function(times){
		var ret = "";
		for (var i=1;i<=times;i++) ret+=this;
		return ret;
	},
	
	toNull: function(){
		if(this=="") return null;
		return this;
	},
	
	p:function(){
		return "<p>"+this+"</p>";
	},
	
	functionBody:function(){
		return this.substr(13,this.length-14);
	},
	
	functionParameters:function(){
		var primParentesis = this.indexOf("(");
		var segParentesis  = this.indexOf(")");
		if(primParentesis == -1 || segParentesis == -1) return;
		return this.substring(primParentesis+1,segParentesis);
	},
	
	functionName:function(){
		var primParentesis = this.indexOf("(");
		var functionString = this.indexOf("function ");
		var comienzo = functionString!=-1?functionString.length:0;
		if(primParentesis==-1) primParentesis = this.length;
		return this.substring(comienzo,primParentesis);
	},
	
	findToRegExp:function(){
		var texto = this;
		texto = texto.trim(",");
		texto = texto.split(",").inject([],function(what,i){
			what.push("("+i.fixRegExp().sinTildesRegExp()+")");
			return what;
		}).join("|");
		texto = "(^|\\s)("+texto+")";
		var pattern = new RegExp(texto,"i");
		return pattern;
	},
	
	queryString:function(){
		var pos = this.indexOf("?");
		if(pos==-1) return "";
		return this.substr(pos+1,this.length);
	},
	
	url:function(){
		var pos = this.indexOf("?");
		if(pos==-1) return this;
		return this.substr(0,pos);
	},
	
	fixLength:function(length,char,atRight){
		if(!char) char=" ";
		var ret = this;
		if(this.length>length) {
			if(atRight) ret = this.substr(0,length);
			else ret = this.substr(this.length-length,length);
		}
		else{
			var fix = char.repeat(length-this.length);
			if(atRight) ret +=fix;
			else ret = fix+ret;
		}
		return ret;
	},
	
	italics:function(){
		return "<i>"+this+"</i>";
	},
	
	reemplazar: function(cadena,nuevacadena){
		var arr=this.split(cadena);
		return arr.join(nuevacadena);
	},
	
	fixRegExp: function(){
		var re = /\.|\*|\|\?\(|\)|\^|\$|\\|\[|\]|\+|\{|\}/ig;
		return this.replace(re,function(what){return "\\"+what});
	},

	sinTildesRegExp:function(){
		var sintildes = {"a":"á","e":"é","i":"í","o":"ó","u":"ú"};
		var contildes = {"á":"a","é":"e","í":"i","ó":"o","ú":"u"};
		var todo = Object.extend(Object.extend({},sintildes),contildes);
		var retext = [];
		for (var i in sintildes) retext.push("("+i+"|"+sintildes[i]+")");
		var re = new RegExp(retext.join("|"),"ig");
		return this.replace(re,function(what){return "["+what.toLowerCase()+"|"+todo[what.toLowerCase()]+"]"});
	},
	
	getURIcontent:function(){
		return this.minusFirst("url(").minusFirst("\"").minusLast("\"").minusLast(")");
	},
	
	tag:function(tagName,options){
		var options = options || {};
		var result = ["<"+tagName];
		for (var i in options) result.push(" "+i+"=\""+options[i]+"\"");
		result = result.concat([">",this,"</"+tagName+">"]);
		return result.join("");
	},
	
	link: function(href,options){
		return this.tag("a",Object.extend({href:href},options));
	},
	
	big: function(){
		return this.tag("big");
	}
	
});




Object.extend(String.prototype,{
	isEmail:function (){
		var reMail=new RegExp("^[\\w\.=-]+@[\\w\\.-]+\\.[a-z]{2,4}$");
		return reMail.test(this);
	},
	
	isFloat:function(){
		var re = /^(-)?\d+([.,]\d+)?$/;
		return re.test(this);
	},

	isInteger:function(){
		var re = /^(-)?\d+$/;
		return re.test(this);
	},

	isComodin:function(){
		return this.indexOf("***")!=-1 || this.indexOf("$$$")!=-1;
	},
	
	isDate:function(){
		return this.match(/^\d{1,2}\/\d{1,2}\/\d{4}(\s\d{1,2}:\d{1,2}(:\d{1,2})?)?$/);
	}
	
});


Object.extend(String,{
	null2Empty : function (val){
		if(val==null||val==undefined) val= "";
		return String(val);
	},
	
	randomLetter:function(primerLetra,ultimaLetra){
		var codeA = (primerLetra||"a").charCodeAt(0);
		var codeZ = (ultimaLetra||"z").charCodeAt(0);
		return String.fromCharCode(codeA+Math.round(Math.random()*(codeZ-codeA)));
	},

	randomNumber:function(){
		return this.randomLetter("0","9");
	},

	randomString:function(cant){
		var ret;
		if(!cant)cant=1;
		var arr= new Array();
		var codeA="a".charCodeAt(0);
		var codeZ="z".charCodeAt(0);
		var code0="0".charCodeAt(0);
		var code9="9".charCodeAt(0);
		
		for (var i=codeA;i<=codeZ;i++) arr.push(String.fromCharCode(i));
		for (var i=code0;i<=code9;i++) arr.push(String.fromCharCode(i));
	
		ret="";
		for(var i=0;i<cant;i++) ret+=arr[Math.round(Math.random()*(arr.length-1))];
		return ret;
	},
	
	fixLength:function(obj,quantity,char,atRight){
		return String(obj).fixLength(quantity,char,atRight);
	}
});



////////////////

Object.extend(Number.prototype,{
	decimals:function(val){
		var val2 = Math.pow(10,val);
		var ret = String(Math.round(this*val2)/val2);
		var posDot = String(ret).indexOf(".");
		var integer = String(parseInt(ret));
		var decimals = "";
		if (posDot==-1) decimals = "0".repeat(val);
		else decimals = ret.substr(posDot+1,ret.length);
		decimals+="0".repeat(val-decimals.length);		
		return [integer,decimals].without("").join(".");
	},
	
	trimDecimals:function(val){
		var val2 = Math.pow(10,val);
		var ret = String(Math.round(this*val2)/val2);
		var posDot = String(ret).indexOf(".");
		var integer = String(parseInt(ret));
		var decimals = posDot!=-1?ret.substr(posDot+1,ret.length):null;
		return [integer,decimals].compact().join(".");
	},
	
	currency:	function (symbol) {
		 if(symbol==null) symbol="$";
		 if(!decimals) decimals = Number.currencyDecimals;
		 //-- Returns passed number as string in $xxx,xxx.xx format.
		 var number = String(this).decimals(decimals);
		 var decimales = number.substr(number.indexOf("."));
		 var dNum = number.substr(0,number.indexOf("."));
		 var dStr = dNum;
		 var dLen;
		 if (dNum>=1000) {
				dLen=dStr.length;
				dStr=parseInt(""+(dNum/1000))+","+dStr.substring(dLen-3,dLen);
		 }
	
		 //-- Adds comma in millions place.
		 if (dNum>=1000000) {
				dLen=dStr.length;
				dStr=parseInt(""+(dNum/1000000))+","+dStr.substring(dLen-7,dLen);
		 }

		 return symbol+" "+dStr+decimales;

	},
	
	odd:function (){
		return (Math.pow(-1,this)<0);
	},
	
	even:function(){
		return !this.odd();
	},
	
	sign:function(){
		return this==0?0:this>0?1:-1;
	},
	
	pixels: function(){
		return this+"px"; 
	}
	
});

Object.extend(Number,{
	currencyDecimals: 2,
	
	b2d: function (binary){
		var ret = 0;
		$A(binary).reverse().each(function(digit,index){
			var val = digit*Math.pow(2,index);
			ret+=val;
			//console.debug(digit,val);
		});
		return ret;
	},

	d2b: function (decimal,digits){
		var ret = "";
		var value = decimal;
		if(decimal==0) ret = "0";
		else{
			while(value>=1) {
				value = Math.floor(value);
				ret = (value % 2) + ret;
				value/=2;
			}
		}
		if(digits) ret = ret.fixLength(digits,"0");
		return ret;
	}

});
/////////////



/**********************/

Object.extend(document,{
	createElementHTML : function(tag,innerHTML){
		//recibe o un string en tag, o un objeto con: tag (string) , innerHTML (string) , style (objeto)
		if(typeof tag!="object"){
			var _tag = tag;
			var tag = {};
			tag.tag = _tag;
			tag.innerHTML = innerHTML;
		}
		var el = document.createElement(tag.tag);
		if(tag.innerHTML!=null) el.innerHTML = tag.innerHTML;
		if(tag.style) Element.setStyle(el,tag.style);
		if(tag.className) el.className = tag.className;
		return el;
	},
	
	getElementsByAttribute:function(attr_name,attr_value,childs){
		var ret = new Array();
		if(!childs)childs=this.documentElement.childNodes;
		for(var i=0;i<childs.length;i++){
			var node = childs[i];
			if (node.getAttribute &&  node.getAttribute(attr_name)==attr_value) {
				ret.push(node);
			}
			if(node.hasChildNodes())	ret = ret.concat(this.getElementsByAttribute(attr_name,attr_value,node.childNodes));
		}
		return ret; //no puede devolver null, por el concat
	},
	
	getElementsByAttribute : function(attrName, value, parentElement) {
		var children = ($(parentElement) || document.body).getElementsByTagName('*');
		return $A(children).inject([], function(elements, child) {
			if (child.getAttribute && child.getAttribute(attrName)==value){
				elements.push(child);
			}
			return elements;
		});
	}

});

$Attr = document.getElementsByAttribute;

////////////////////////////////
Object.extend(Date,{
 	arrDayNames : ["domingo","lunes","martes","miércoles","jueves","viernes","sábado"],
	dayName:function(number){
		return this.arrDayNames[number];
	}
});

Object.extend(Date.prototype,{
	format: function (formato){
		if (!formato) {
			formato="dd/mm/yyyy";
			//if (Application("language")=="en") formato="mm/dd/yyyy";
			//else formato = "dd/mm/yyyy";
		}
		
		var fec = this;
		var yyyy = fec.getFullYear();
		var dd = String.fixLength(fec.getDate(),2,"0");
		var mm = String.fixLength(fec.getMonth()+1,2,"0");
		var hh = String.fixLength(fec.getHours(),2,"0");
		var nn = String.fixLength(fec.getMinutes(),2,"0");
		var ss = String.fixLength(fec.getSeconds(),2,"0");
		var ds = Date.dayName(fec.getDay());
		
		ret = formato;
		
		var re = /ddss/ig;
		ret = ret.replace(re,ds);
		var re = /dd/ig;
		ret = ret.replace(re,dd);
		var re = /mm/ig;
		ret = ret.replace(re,mm);
		var re = /yyyy/ig;
		ret = ret.replace(re,yyyy);
		var re = /hh/ig;
		ret = ret.replace(re,hh);
		var re = /nn/ig;
		ret = ret.replace(re,nn);
		var re = /ss/ig;
		ret = ret.replace(re,ss);
		var re = /ds/ig;
		ret = ret.replace(re,ds.substr(0,2));
	
		return(ret);
	},
	
	addMinutes:function(min){
		var ret = new Date(this);
		return new Date(ret.setMinutes(this.getMinutes()+min));
	},
	
	addSeconds:function(val){
		var ret = new Date(this);
		return new Date(ret.setSeconds(this.getSeconds()+val));
	},
	
	addHours:function(val){
		var ret = new Date(this);
		return new Date(ret.setHours(this.getHours()+val));
	},
	
	addDays:function(val){
		var ret = new Date(this);
		return new Date(ret.setDate(this.getDate()+val));
	},
	
	addMonths:function(val){
		var ret = new Date(this);
		return new Date(ret.setMonth(this.getMonth()+val));
	},
	
	addYears:function(val){
		var ret = new Date(this);
		return new Date(ret.setFullYear(this.getFullYear()+val));
	}
});



/////////////////////////////////////
Lapse = Base.extend({
	constructor: function(number){
		if(!this._isLapse) return new Lapse(number);
		this.lapse = number;
	},
	
	toString: function(){
		var t = (this.lapse||0)/1000;
		var dd = 0, hh = 0, mm = 0, ss = 0;
		ss = t % 60;  //3670 % 60 = 10
		t = (t-ss) / 60; // (3670 - 10) / 60 = 61
		mm = t % 60; //61 % 60 = 1
		t = (t-mm) / 60; // (61 - 1) / 60
		hh = t % 24;
		t = (t-hh) / 24;
		dd = t;
		var ret = [];
		if(dd>0) ret.push(dd+"d");
		if(hh>0) ret.push(hh+"h");
		if(mm>0) ret.push(mm+"m");
		if(ss>0) ret.push(ss+"s");
		return ret.join(" ");
	},
	
	valueOf: function(){
		return this.lapse;
	},
	
	_isLapse: true
	
});




//////////////////////////////////////

Cronometro = Base.extend({
	constructor: function(){
		this.iniTime = new Date();
		this.lastTime = this.iniTime;
	},
	
	elapsed: function(){
		var d = new Date();
		var ret = d-this.lastTime;
		this.lastTime=d;
		return ret;
	},
	
	elapsedTotal: function(){
		var d = new Date();
		var ret = d-this.iniTime;
		this.lastTime=d;
		return ret;
	},
	
	wait: function(milisegundos){
		var date = new Date();
		var curDate = null;
		do { var curDate = new Date(); } 
		while(curDate-date < milisegundos);
	}
},
{
	time: function(callback){
		var crono = new Cronometro();
		callback();
		return crono.elapsed();
	}
});

/////////////////////////////////////////
//Amount permite sumar montos en diferentes monedas
var Amount=function(){
	this.bag = {};
}

Object.extend(Amount.prototype,{
	add:function(moneda,amount){
		switch(moneda.constructor){
		case Amount:
			var otherBag = moneda;
			for (var mone in otherBag.bag){
				if(!this.bag[mone]) this.bag[mone] = 0;
				this.bag[mone]+= otherBag.bag[mone];
			}
			return this;
			break;
		default:
			if(!this.bag[moneda]) this.bag[moneda] = 0;
			this.bag[moneda]+=Number(amount);
		}
	},
	
	minus:function(moneda,amount){
		switch(moneda.constructor){
		case Amount:
			var otherBag = moneda;
			for (var mone in otherBag.bag){
				if(!this.bag[mone]) this.bag[mone] = 0;
				this.bag[mone]-= otherBag.bag[mone];
			}
			return this;
			break;
		default:
			if(!this.bag[moneda]) this.bag[moneda] = 0;
			this.bag[moneda]-=Number(amount);
		}
	},
	
	get:function(moneda){
		if(!moneda) return this.bag;
		else return this.bag[moneda];
	},
	
	getCurrency:function(moneda){
		var ret="";
		if(!moneda) return $H(this.bag).collect(function(e){return e[1].currency(e[0])});
		return this.bag[moneda].currency(moneda);
	}
	
});



//////////////////////////////////////////////////////
//Event.Watcher permite saber en cualquier momento qué tecla estaba apretada dado que queda
//almacenado en Event.keyCode
Event.Watcher = Class.create();
Event.Watcher.prototype = {
  initialize: function() {
    Event.observe(document, 'keydown', this.onKeyPress.bindAsEventListener(this));
    Event.observe(document, 'keyup', this.onKeyUp.bindAsEventListener(this));
    //Event.observe(document, 'mousemove', this.onMouseMove.bindAsEventListener(this));
  },

  onKeyPress: function(event) {
		var code = event.keyCode;
    Event.keyCode = code;
		//Element.setStyle('culo',{'background-color':'#FF0000'});
    //if(code == Event.KEY_TAB) alert('Tab key was pressed');
  },
	
	onKeyUp:function(event){
		//Element.setStyle('culo',{'background-color':'transparent'});
		Event.keyCode = 0;
	}	
}

new Event.Watcher();

Object.extend(Event,{
	KEY_CONTROL: 17							
});


Object.extend(Event, {
  _domReady : function() {
    if (arguments.callee.done) return;
    arguments.callee.done = true;

    if (this._timer)  clearInterval(this._timer);
    
    this._readyCallbacks.each(function(f) { f() });
    this._readyCallbacks = null;
	},
  
	onDOMReady : function(f) {
    if (!this._readyCallbacks) {
      var domReady = this._domReady.bind(this);
      
      if (document.addEventListener)
        document.addEventListener("DOMContentLoaded", domReady, false);
        
        /*@cc_on @*/
        /*@if (@_win32)
            document.write("<script id=__ie_onload defer src=javascript:void(0)><\/script>");
            document.getElementById("__ie_onload").onreadystatechange = function() {
                if (this.readyState == "complete") domReady(); 
            };
        /*@end @*/
        
        if (/WebKit/i.test(navigator.userAgent)) { 
          this._timer = setInterval(function() {
            if (/loaded|complete/.test(document.readyState)) domReady(); 
          }, 10);
        }
        
        Event.observe(window, 'load', domReady);
        Event._readyCallbacks =  [];
    }
    Event._readyCallbacks.push(f);
  }
});



/////////////////////////////////////////////////////

Position._realOffset = Position.realOffset;
Position._cumulativeOffset = Position.cumulativeOffset;
Position._positionedOffset = Position.positionedOffset; 

Object.extend(Position,{
	realOffset: function(element){return this._realOffset($(element))},	
	cumulativeOffset: function(element){return this._cumulativeOffset($(element))},	
	positionedOffset: function(element){return this._positionedOffset($(element))},	
	
	screenOffset: function(element){
		var element = $(element);
		var cumulativeOffset = this.cumulativeOffset
		(element);
		var realOffset = this.realOffset (element);
		var screenOffset = cumulativeOffset.inject([],function(what,el,indx){
			what.push(el-realOffset[indx]);
			return what;
		});
		return screenOffset;
	},
	
	relativeOffset: function(element) { 
		var ret = [element.offsetLeft,element.offsetTop];
		element = element.parentNode;
		ret[0] -= element.offsetLeft;
		ret[1] -= element.offsetTop;
		return ret;
	}	
});



//////////////////////////

Error.create=function(obj){
	var err = new Error(-2146823281);
	err.name = "TypeError";
	if(typeof obj=="string") obj = {message:obj};
	Object.extend(err,obj);
	if(!err.message) err.message = err.description;
	if(!err.description) err.description = err.message;
	return err;
}

//////////////////////////////////////////////////////////


var Table = Table||{};
Object.extend(Table,{
	addRow: function(table,row,comodin){
		var table = $(table);
		var row = $(row);
		var tBody = table.getElementsByTagName('tbody')[0];
		var newRow;
		if(row) {
			oldRow = row;
			var newRow = row.cloneNode(true);
			tBody.insertBefore(newRow,row);
		}
		else newRow = tBody.appendChild(tBody.firstChild.cloneNode(true));
		newRow.cloned = true;
		if (!table.rowNum) table.rowNum=0;
		
		table.rowNum++;
		newRow.id = table.id+"_row_"+table.rowNum;
		newRow.style.display="";
		if(comodin==null) comodin = "***";

		for(var i=0;i<newRow.cells.length;i++){
			newRow.cells[i].innerHTML = String(newRow.cells[i].innerHTML).reemplazar(comodin, table.rowNum);
		}
			
		if(!table.rowsAdded) table.rowsAdded=new Array();
		table.rowsAdded[table.rowNum]=newRow;
		return table.rowNum;
	},
	
	getRow:function(table,rowNumber){
		var table = $(table);
		if(!table.rowsAdded) return null;
		return table.rowsAdded[rowNumber];
	},
	
	delRow: function(table,rowNumber){
		var table = $(table);
		var oldRow = Table.getRow(table,rowNumber);
		if(!oldRow || !oldRow.cloned) return;
		table.deleteRow(oldRow.sectionRowIndex);
		delete table.rowsAdded[rowNumber];
		table.normalize();
	},
	
	delAllRows: function(table){
		var table = $(table);
		if(!table.rowsAdded) return;
		table.rowsAdded.each(function(row,index){
			if(!Object.isNull(row)) Table.delRow(table,index);
		});
		table.rowNum=0;
	},
		
	upRow: function (table,rowNumber){
		var table = $(table);
		var oldRow = Table.getRow(table,rowNumber);
		if(!oldRow || !oldRow.cloned) return;
		var newRow = oldRow.previousSibling;
		if(!newRow || !newRow.cloned) return;
		if(newRow){
			Element.swapNodes(oldRow,newRow);
			return true;
		}
		else return false;
	},
	
	downRow: function (table,rowNumber){
		var oldRow = Table.getRow(table,rowNumber);
		if(!oldRow || !oldRow.cloned) return;
		var newRow = oldRow.nextSibling;
		if(!newRow || !newRow.cloned) return;
		if (newRow){
			Element.swapNodes(newRow,oldRow);
			return true;
		}
		else return false;
	}


});




///////////////////////////////////////////////////////////
Prototype.Selection = Base.extend({	
	constructor:function(options){
		this.options = Object.extend({
			key:null
		},options||{});
		
		//this.options.key = null; //está para que no use key por ahora, porque parece ser más lento.. es RARO!!!
		
		this.content = [];
		if(this.options.key) this.content = new Dictionary();
	},
	
	add: function(value){
		if (this.include(value)) return;
		if(this.options.key) this.content.add (value[this.options.key] , value);
		else this.content.push(value);
		if(value.onselect && typeof value.onselect=="function") value.onselect(this);
	},
	
	remove:function(value,options){
		if(options){
			var found = this.get(options);
			found.each(function(obj){
				this.remove(obj);
			}.bind(this));
			this.length = this.content.length;
			return found;
		}
		if(this.include(value)){
			if(value.ondeselect && typeof value.ondeselect=="function") value.ondeselect(this);
			if(this.options.key) this.content.remove(value[this.options.key]);
			else this.content = this.content.without(value);
			return value;
		}
	},
	
	count: function(){
		if(this.options.key) return this.content.count();
		return this.content.length;
	},
	
	get: function(obj){
		if(!obj) return this.content.select(function(e){return true});
		
		var content = this.content;
		
		if(this.options.key in obj) {
			var what = obj[this.options.key];
			if(what instanceof Prototype.Selection.Multiple) what = what.array;
			if (what instanceof Array) 
				content = what.inject([],function(arr,value){if(content.include(value)) arr.push(content.item(value));return arr});
				//	content = what.collect(function(element){return content.item(element)}.bind(this));
			else {
				var what = this.content.item(what);
				content =  what?[what]:[];
			}
			delete obj[this.options.key];
		}
		if(obj.instanceOf){
			return content.select(function(e){
				return e instanceof obj.instanceOf;
			});
		}
				
		return content.select(function(e){
			var ret = true;
			for (var i in obj) {
				if(obj[i] instanceof RegExp)
					ret &= String(obj[i]).test(e[i]);
				else {
					if(obj[i] instanceof Prototype.Selection.Multiple)
						ret &= obj[i].array.include(e[i]);
					else ret &= e[i]==obj[i];
				}
			}
			return ret;
		});
	},
	
	getFirst:function(obj){
		var ret = this.get(obj);
		return ret[0];
	},
	
	include:function(object){
		if(this.options.key) return this.content.include(object[this.options.key]);
		return this.content.include(object);
	},
	
	clear:function(){
		var _this = this;
		this.content.each(function(obj){
			_this.remove(obj);
		});
		this.content.clear();
	},
	
	addNew:function(value){
		this.clear();
		this.add(value);
	},
	
	update:function(objUpdate){
		this.content.each(function(obj){
			Object.extend(obj,objUpdate);
		});
	},
	
	toString:function(){
		return "Prototype.Selection ("+this.count()+")";
	}
});

var Selection = Selection || {};
Object.extend(Selection, new Prototype.Selection());

Prototype.Selection.Multiple = Base.extend({
	constructor: function(what){
		this.array = what;
	}
});




//////////////////////////////////////////
/*
Dictionary = Base.extend({
	constructor: function(){
		this.repository = {};
		this.length = 0;
	},
	
	_each: function(iterator){
		for(var i in this.repository) iterator(this.repository[i],i);
	}
});

Dictionary.implement(Enumerable);

Dictionary.implement({
	add: function (key,value){
		if(arguments.length==1) {
			value = key;
			key = this.count();
		}
		if(key==undefined) key = this.count();
		this.repository[key] = value;
		this.length++;
	},
	
	remove: function(key){
		this.repository[key] = null;
		delete this.repository[key];
		this.length--;
	},
	
	item: function(key){
		return this.repository[key];
	},
	
	items: function(){
		return $H(this.repository).values();
	},
	
	include: function(key){
		return key in this.repository;
	},
	
	count: function(){
		return this.length;
		var cant = 0;
		for (var i in this.repository) cant++;
		return cant;
	},
	
	clear: function(){
		delete this.repository;
		this.repository = null;
		this.repository = {};
	}

});
*/

////////////////////////////////////////////////////////


Object.extend(Event,{
  _observeAndCache: function(element, name, observer, useCapture) {
    if (!this.observers) this.observers = [];
    if (element.addEventListener) {
      this.observers.push([element, name, observer, useCapture]);
      element.addEventListener(name, observer, useCapture);
    } else if (element.attachEvent) {
      this.observers.push([element, name, observer, useCapture]);
      element.attachEvent('on' + name, observer);
    }
		else {
		//////////agregado Licho que permite que CUALQUIER objeto pueda disparar eventos!!!
			Base(element);
			element.extend(EventDispatcher.prototype);
			Event._observeAndCache (element,name,observer,useCapture);
		}
  }
	
});


EventDispatcher=Base.extend({
	constructor:function(){
		///esta clase permite despachar eventos y es compatible con Event.observe !!!
		//console.debug("constructor de EventDispatcher");
	},
	
	observe:function(object){
		var events = ["click","dblclick","mouseover","mouseout","mousedown","mouseup"];
		events.each(function(event){
			Event.observe(object,event,function(e){
				(this["on"+event]||Prototype.K)(e);
			}.bind(this))
		}.bind(this));
	},
	
	observers:function(){
		if(!this._observers) this._observers = new Prototype.Selection();
		return this._observers;
	},

	addEventListener:function(name, observer, useCapture){
		//alert("agregando un evento:"+name);
		var observer = {name:name,observer:observer,useCapture:useCapture};
		this.observers().add(observer);
		if(!this["on"+name]) {
			this["on"+name] = function(){
				this.triggerEvent.apply(this,[name].concat($A(arguments)))
			}.bind(this);
		}
		else{
			if(!this["on"+name].__createdByEventDispatcher){
				var oldFn = this["on"+name];
				this["on"+name]=function(){
					this.triggerEvent.apply(this,[name].concat($A(arguments)));
					oldFn.apply(this,$A(arguments));
				}.bind(this);
			}
		}
		this["on"+name].__createdByEventDispatcher=true;
	},
	
	removeEventListener:function(name, observer, useCapture){
		this.observers().remove(null,{name:name,observer:observer});
	},
	
	triggerEvent:function (name){
		var args = $A(arguments);
		//console.debug(args.length);
		args.shift();
		var observers = this.observers().get({name:name});
		observers.each(function(observer){
			observer.observer.apply(this,args);
		}.bind(this));
	}
});
////////////////////////////////////



///TabGroup se encarga de mostrar y ocultar elementos que estén en el mismo grupo
TabGroup = EventDispatcher.extend({
	constructor: function(arrElements){
		this.displayed = 0;
		if(arrElements && arrElements instanceof Array){
			arrElements.each(function(element){
				this.add(element);
			}.bind(this));
			this.show();
		}
	},
	
	next:function(){
		this.displayed++;
		if(this.displayed>=this.content.length) this.displayed = 0;
		this.show();
	},

	previous:function(){
		this.displayed--;
		if(this.displayed<0) this.displayed = this.content.length-1;
		this.show();
	},
	
	show:function(which){
		this.get().invoke("hide");
		if(which) this.get().each(function(e,idx){if($(which)==e) this.displayed = idx}.bind(this));
		var element = this.get()[this.displayed];
		element.show();
	}
	
});

TabGroup.implement(new Prototype.Selection);

Object.extend(TabGroup.prototype,{
	_add: TabGroup.prototype.add,
	
	add: function(element){
		element = $(element);
		element.hide();
		this._add(element);
		//console.debug(element);
	}
})


////////////////////////////////////
//Este wrapper hace que el Builder devuelva un nodo con los métodos de Element

try{	
	Builder.__node = Builder.node;
	Builder.node = function(){
		return $(Builder.__node.apply(Builder,$A(arguments)));
	}
}catch(e){}


/////////////////////////////////


Object.extend(Math,{
	randRange:function(ini,fin,float){
		var range = fin-ini+1;
		return float?Math.random()*(range-1)+ini : Math.floor(Math.random()*range)+ini;
	}
});





/////CONSOLE///////////////////////////////////////////////////////////////
if (typeof console == undefined){
	console = {
		_output: function(what,options){
			if (!$('output')) return;
			var options = options || {};
			what.each(function(argument){
				var outputstring = String(argument).p();								 
				if(options.fontcolor) outputstring = outputstring.fontcolor(options.fontcolor);
				$('output').innerHTML += outputstring;
				$('output').scrollTop = 9000000;
			});
		},
		
		debug:function(){
			this._output($A(arguments));
		},
		
		error:function(){
			this._output($A(arguments),{fontcolor:"#FF0000"});
		},
	
		warn:function(){
			this._output($A(arguments),{fontcolor:"#AAAA00"});
		}
	}
}
///////////////////////////////////////////////////////////////////////////





Element.Observer = Class.create();
Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
  initialize: function(element, property, frequency, callback) {
		this.property = property;
		this.frequency = frequency;
    this.element   = $(element);
    this.callback  = callback;
    this.lastValue = this.getValue();

    /*if(element.watch) {
				element.watch(property,function(pro,oldval,newval){this.callback(this.element, newval)}.bind(this));
			console.debug(["watching "+property+" of ",this.element]);
		}
		else*/ this.registerCallback();
  },

	getValue: function() {
		var val = eval("this.element."+this.property);
		//console.debug(val);
    return val;
  }
});



///////////////////////////////////////////////
Choquin.Marquee = Base.extend({
	constructor: function(element, options){
		this.element = $(element);
		var height = this.element.offsetHeight;
		var width = this.element.offsetWidth;
		//console.debug(this.element.offsetHeight);
		this.options = {
			velocity: 8,
			force: false,
			timer: 100
		};
		Object.extend(this.options,options);
		
		if (! (this.container = $(this.element.id+"_marquee")) ) {
			this.container = Builder.node("div");
			this.container.id = this.element.id+"_marquee";
			this.element.parentNode.insertBefore(this.container,this.element);
		}
		
		this.container.setStyle({overflow:'auto'});
		
		this.container.appendChild(this.element);
		this.element.setStyle({width:'auto',position:'absolute',whiteSpace:'nowrap'});
		//if (!this.container.getStyle('height')) this.container.setStyle({height:height+'px'});
		//if (!this.container.getStyle('width')) this.container.setStyle({width:width+'px'});
		this.container.onclick = this.start.bind(this);
		this.container.onmouseover = this.pause.bind(this);
		this.container.onmouseout = this.resume.bind(this);

		new Element.Observer(this.element,"innerHTML",.5,this.element.flatten.bind(this.element));

		this.start();
	},
	
	start: function(){
		this.pos = -this.container.offsetWidth;
		clearInterval(this.interval);
		this.interval = setInterval(this.update.bind(this),this.options.timer);
	},
	
	pause: function(){
		clearInterval(this.interval);
		this.interval = null;
	},
	
	resume: function(){
		this.pause();
		this.interval = setInterval (this.update.bind(this),this.options.timer);
		this.lastTime = new Date();
	},
	
	update: function(){
		var vel = this.options.velocity * (new Date() - this.lastTime) / this.options.timer;
		this.pos+=vel;
		var width = this.getWidth();
		if(width<=this.container.offsetWidth) this.pos = 0;
		if(this.pos>width) this.start();
		var right = this.pos+this.container.offsetWidth+'px';
		var left = this.pos+'px';
		//right = '20px';
		//left = '0px';
		this.element.setStyle({clip:'rect(0px '+right+' auto '+left+')'});
		this.element.setStyle({marginLeft:-this.pos+'px'});
		this.lastTime = new Date();
		//console.debug(this.element.getStyle('clip'));
	},
	
	getWidth: function(){
		return parseFloat(this.element.offsetWidth);
	}
});






////////////////////////////////
//flash fix for IE:
/*function FlashFix(){
	theObjects = document.getElementsByTagName("object"); 
	for (var i = 0; i < theObjects.length; i++) { 
		if(!theObjects[i].outerHTML) return;
		theObjects[i].outerHTML = theObjects[i].outerHTML; 
	}
}*/


/*
<div class="flash"  style="width:200px;height:100px" isflash="true" background-color="#336699" src="/albums/db/portadas/alf.swf" version="7" wmode="transparent" flashvars="a=1&b=2">[...]</div>
*/

function Div2Flash(flash){
	if(typeof SWFObject=="undefined") {
		console.debug("No se encuentra registrado SWFObject");
		return;
	}

	var flash=$(flash);
	if(!flash.id) flash.id = "flashcontent_"+String.randomString(20);

	var width = flash.getAttribute("width")||flash.style.width;
	var height = flash.getAttribute("height")||flash.style.height;
	var bgColor = flash.getAttribute("background-color");
	var version = flash.getAttribute("version")||"8";
	var src= flash.getAttribute("src");
	var wmode = flash.getAttribute("wmode");
	var quality = flash.getAttribute("quality")||"high";
	var flashvars = flash.getAttribute("flashvars")||"";
	flashvars = flashvars.split("&");
	//alert(src+"\n"+flash.id+"\n"+width+"\n"+height+"\n"+version+"\n"+bgColor);
	
	var so = new SWFObject(src, flash.id, width, height, version, bgColor);

	
	if(wmode) so.addParam("wmode", wmode);
	if(quality) so.addParam("quality", quality);
	
	for(var ii=0;ii<flashvars.length;ii++){
		var variable = flashvars[ii].split("=");
		so.addVariable(String(variable[0]), String(variable[1]));
	}
	//so.setAttribute('redirectUrl', 'http://www.adobe.com/go/getflashplayer');

	//so.addVariable("variable2", "value2");
	so.write(flash.id);
	
	flash.id="div_"+flash.id;
	flash.setAttribute("isflash","false");
	flash.setAttribute("className","");

}

function FlashFix(){
	if(!document.getElementsByAttribute || typeof SWFObject=="undefined") return;
	//alert("antes del stack overflow");
	var flashs = document.getElementsByAttribute("isflash","true");

	for(var i=0;i<flashs.length;i++){
		var flash = flashs[i];
		Div2Flash(flash);
	}
	
	//alert("despúes del stack overflow");

}


//Event.observe(window, 'load', function(){FlashFix()}, false);