/*******************************************
 * Agregados a Prototype 
 ******************************************/
// para reconocer Mac OS
Prototype.Browser.MAC = (navigator.userAgent.indexOf("Mac") < 0)?(false):(true);

/**
 * Para patrón delegate en eventos
 */
Object.extend(Event, {
 	delegate: function(element, eventName, targetSelector, handler) {
		element = $(element);
		function selectorMatch(element) {
			return element.match(targetSelector);
		}
		
		function validateTarget(origin) {
			if ( origin.match(targetSelector) ) { return origin; }
			var ancestors = origin.ancestors();
			return ancestors.find(selectorMatch);
		}

		function createDelegation(_delegatedEvent) {
			var rawOrigin = _delegatedEvent.element();
			var origin = validateTarget(rawOrigin);
			if ( origin != null && (typeof handler == 'function') ){ 
				_delegatedEvent.element = function() { return origin; }
				return handler(_delegatedEvent);
			}
		}

		element.observe(eventName, createDelegation);
		return element;
	},

	delegators: function(element, eventName, rules) {
		element = $(element);
		function delegateRule(rule) {
			element.delegate(eventName, rule.key, rule.value)
		}
		$H(rules).each(delegateRule)
		return element;
	}
});

Element.addMethods({
	delegate: Event.delegate,
	delegators: Event.delegators
});
Object.extend(document, {
	delegate: Event.delegate,
	delegators: Event.delegators
});

/**
 * Eventos Ajax genéricos para mostrar/ocultar el loading
 * TODO: reemplazar con mostrar/ocultar de verdad el loading en vez de logs...
 */
/*Ajax.Responders.register({
	onCreate: function() {
		if(Ajax.activeRequestCount == 1) {
			Tapestry.debug("mostrando loading");
		}
	},
	onComplete: function() {
		if(Ajax.activeRequestCount == 0) {
			Tapestry.debug("quitando loading");
		}
	}
});*/

/*******************************************
 * Agregados a Tapestry 
 ******************************************/

/**
 * Validador de RUT
 * @param {Object} field campo input que contiene el RUT
 * @param {Object} message mensaje para el usuario cuando hay un error con el campo
 */
Tapestry.Validator.rut = function(field, message) {
	field.addValidator(function(value) {
		var cleanRut = value.substring(0,value.length).replace(/[^\dkK]/g,"").toUpperCase();
		var formatedRut = "-" + cleanRut.charAt(cleanRut.length-1);
		var count = 0;
		for(var i=cleanRut.length-2;i>=0;i--) {
			formatedRut = cleanRut.charAt(i) + formatedRut;
			count++;
			if(count == 3) {
				count = 0;
				if(i>0) {
					formatedRut = "." + formatedRut;
				}
			}
		}
		this.value = formatedRut;
		if(!this.value.match(/^\d{1,3}\.\d{3}\.\d{3}-[\dkK]$/)) {
			throw message;
		}
		var M=0,S=1,T=parseInt(cleanRut.substring(0,cleanRut.length-1).replace(/[^\d]/g,""));
		for(;T;T=Math.floor(T/10)) {
			S=(S+T%10*(9-M++%6))%11;
		}
		var dv = S?S-1:'K';
		if (formatedRut.charAt(formatedRut.length-1) != dv) {
			throw message;
		}
	});
};

/**
 * Validador de username
 * @param {Object} field campo input que contiene el username
 * @param {Object} message mensaje para el usuario cuando hay un error con el campo
 */
Tapestry.Validator.username = function(field, message) {
	field.addValidator(function(value) {
		if(!value.match(/^[a-zA-Z_]+[a-zA-Z_0-9\.]*$/)) {
			throw message;
		}
	});
};
/**
 * Validador de phoneNumber
 * @param {Object} field campo input que contiene el número de teléfono
 * @param {Object} message mensaje para el usuario cuando hay un error con el campo
 */
Tapestry.Validator.phoneNumber = function(field, message) {
	field.addValidator(function(value) {
		if(!value.match(/^\(?\+?(?:[\(\)\s-]*\d+){6,}$/)) {
			throw message;
		}
	});
};

/**
 * NO SE ESTÁ USANDO ESTE VALIDADOR... DE HECHO, AL PARECER NO FUNCIONA
 * @param {Object} field
 * @param {Object} message
 * @param {Object} otherField
 * @deprecated
 */
Tapestry.Validator.bethesame = function(field, message, otherField) {
	Tapestry.addValidator(field, false, function(value, event) {
		var otherRealField = $(otherField); 
		if(!otherRealField)
			event.recordError(message);
		if (value != otherRealField.value)
			event.recordError(message);
	});
}; 

/**
 * Decoración de métodos de Tapestry para poder insertar código antes/después del llamado ajax para actualizar zonas y
 * antes/después de actualizar la zona con nuevo contenido. 
 */

document.observe(Tapestry.FORM_PROCESS_SUBMIT_EVENT, function(event) {
	var form = event.findElement();
	var zoneManager = Tapestry.findZoneManager(form);
	K12.showLoading(zoneManager.element);
});

document.observe(Tapestry.TRIGGER_ZONE_UPDATE_EVENT, function(event) {
	var element = event.findElement();
	var zoneManager = Tapestry.findZoneManager(element);
	K12.showLoading(zoneManager.element);
});

(function() {
	Tapestry.ZoneManager.addMethods({
		processReply: Tapestry.ZoneManager.prototype.processReply.wrap(function(proceed, reply){
			proceed(reply);
			K12.hideLoading(this.element);
		})
	});
})();

/**
 * Decoración para agregar capacidad a buttonSubmit de enviarse por Ajax
 */
(function() {
	function addAjaxButtonsBehaviour() {
		$$("BUTTON[type=submit]").each(function(element) {
			var t = $T(element);
			
			if (!t.trackingClicks) {
				element.observe("click", function() {
					$(element.form).setSubmittingElement(element);
				});
				
				t.trackingClicks = true;
			}
		});
	}
	// primero hacemos que se llame junto con Tapestry.onDomLoadedCallback al final de la carga del DOM
	Tapestry.onDOMLoaded(addAjaxButtonsBehaviour);
	// luego hacemos un wrap de este método para que se llame siempre cada vez que se llame a Tapestry.onDomLoadedCallback
	Tapestry.onDomLoadedCallback = Tapestry.onDomLoadedCallback.wrap(
		function(proceed) {
			proceed();
			addAjaxButtonsBehaviour();
		}
	);
})();

/**
 * Wrap de método clonePosition para IE (específicamente IE8) que tiene un bug con offsetParent de elementos ocultos.
 */
(function() {
	if(Prototype.Browser.IE){// && navigator.userAgent.substring(navigator.userAgent.indexOf("MSIE")+5).startsWith("8")) {	// por bug de IE8 con offsetParent de elementos ocultos
		Element.addMethods({
			clonePosition: Element.Methods.clonePosition.wrap(
				function(proceed, element, source, options){
					var hide = false;
					if(!element.visible()) {
						element.show(); 
						hide = true;
					}
					var returnValue = proceed(element, source, options);
					if(hide) {
						element.hide();
					}
					return returnValue;
				}
			),
			/* Este es necesario sobreescribirlo para que en IE6 y 7 no se caiga al intentar pedir estilo a document. Este wrap
			 * incluye tanto el método original como también un wrap hecho por el mismo prototype para IE */
			getOffsetParent: Element.Methods.getOffsetParent.wrap(
				function(proceed, element) {
					element = $(element);
					try {
						element.offsetParent;
					} catch(e) {
						return $(document.body);
					}
					
					var f = function(element) {
						if (element.offsetParent) return $(element.offsetParent);
						if (element == document.body) return $(element);
						while ((element = element.parentNode) && element != document.body && element != document) {
							if (Element.getStyle(element, 'position') != 'static')
								return $(element);
						}
						return $(document.body);	
					};
					
					var position = element.getStyle('position');
					if (position !== 'static') {
						return f(element);
					} else {
						element.setStyle({ position: 'relative' });
						var result = f(element);
						element.setStyle({ position: position });
						return result;
					}
				}
			)
		});
	}
})();

/*******************************************
 * Código de k12 
 ******************************************/

var K12 = {
	insertionPoint: "insertionPoint",
	dialogWindow: null,
	loadingInsertPointId: "loadingInsertPoint",
	loadingInsertPoint: null,
	getInsertPoint: function() {
		return K12.loadingInsertPoint || (K12.loadingInsertPoint = $(K12.loadingInsertPointId));
	},
	unSelectThings: function() {
		var sel;
		if(document.selection && document.selection.empty){
			document.selection.empty() ;
		} else if(window.getSelection) {
			sel=window.getSelection();
			if(sel && sel.removeAllRanges)
				sel.removeAllRanges();
		}
	},
	showWindow: function(content,title,width,height) {
		K12.createIfNotCreated();
		K12.dialogWindow.setHTMLContent(content);
		K12.dialogWindow.setTitle(title);
		K12.dialogWindow.setSize(width,height);
		K12.dialogWindow.setZIndex(100);
//		K12.dialogWindow.showCenter();
		var viewPortOffsets = document.viewport.getScrollOffsets();
		var viewPortDimensions = document.viewport.getDimensions();
		var windowDimensions = K12.dialogWindow.getSize();
		K12.dialogWindow.setLocation(viewPortDimensions.height / 2 + viewPortOffsets.top - windowDimensions.height / 2, viewPortDimensions.width / 2 + viewPortOffsets.left - windowDimensions.width / 2);
		K12.dialogWindow.show();
		//K12.dialogWindow.updateHeight();
	},
	hideWindow: function() {
		K12.dialogWindow.close();
	},
	createIfNotCreated: function() {
		if(K12.dialogWindow == null) {
			K12.dialogWindow = new Window({parent: $('insertionPoint').parentNode,className: "bluelighting", width:350, height:400, zIndex: 100, resizable: true, minimizable:false, maximizable:false, title: "Sample window", showEffect:Effect.Appear, showEffectOptions:{duration:0.5} ,hideEffect: Effect.DropOut, hideEffectOptions:{duration:0.5},draggable: true, wiredDrag: false, recenterAuto: false}); 
		}
	},
	
	/**
	 * Funciones para mostrar/ocultar loading
	 */
	createLoading : function(element) {
		var containerDiv = $(document.createElement("div")).addClassName("contextualLoading");
		containerDiv.title = "k12: Por favor, espere mientras carga...";
		
		var innerDiv = $(document.createElement("div")).addClassName("loadingImage");
		innerDiv.title = "Cargando";
		
		$T(element).loadingDiv = containerDiv;
		containerDiv.insert({bottom: innerDiv});
		K12.getInsertPoint() && K12.getInsertPoint().insert({bottom: containerDiv});
		return containerDiv;
	},
	showLoading: function(targetElement) {
		targetElement = $(targetElement);
		var loadingDiv = $T(targetElement).loadingDiv;
		if(!loadingDiv) {
			loadingDiv = K12.createLoading(targetElement);
		}
		loadingDiv.clonePosition(targetElement, {});
		loadingDiv.show();
	},
	hideLoading: function(targetElement) {
		var loadingDiv = $T(targetElement).loadingDiv;
		if(loadingDiv) {
			loadingDiv.hide();
			$T(targetElement).loadingDiv = null;
			loadingDiv.remove();
		}
	}
};

/* MOMENTANEO */
function checkSomeUsers() {
	if(!$('justSomeUsers').checked) {
		$('overR').show();
		$('addViewForm').disable();
	} else {
		$('overR').hide();
		$('addViewForm').enable();
	}
}

/*******************************************
 * Desde aquí: nueva forma de escribir JavaScript 
 ******************************************/

/**
 * Objeto con métodos útiles
 */
K12.Utils = {
	/**
	 * Toma dos objetos JSON y mezcla sus propiedades en un tercer objeto JSON. Las propiedades del segundo objeto pisan a las del 1er objeto si éstas se repiten
	 * @param {Object} o
	 * @param {Object} ob
	 */
	mergeProperties: function(o, ob) {
		var r = {};
		var z;
		for (z in o) {if (o.hasOwnProperty(z)) {r[z] = o[z];}}
		for (z in ob) {if (ob.hasOwnProperty(z)) {r[z] = ob[z];}}
		return r;
	},
	findElementInParentWindows : function(elementId) {
		var element = $(elementId);
		var win = window;
		while (!element && win != win.parent) {
			win = win.parent;
			element = win.$(elementId)
		}
		return element;
	},
	findSuperParentWindow : function() {
		var win = window;
		while (win != win.parent) {
			if (win.parent.K12) {
				win = win.parent;
			}
		}
		return win;
	}
};

K12.Modal = {
	QUEUE_OPTS: {position: 'end', scope: 'modalFullScreen'},
	CURTAIN_ID: 'fullScreen',
	curtain: null,
	showingElement: null,
	ready: false,
	prepare: function() {
		if(!K12.Modal.curtain) {
			K12.Modal.curtain = $(K12.Modal.CURTAIN_ID);
		}
		if(K12.Modal.curtain) {
			K12.Modal.ready = true;
		}
	},
	/**
	 * @param elementToShow
	 */
	show: function(elementToShow) {
		K12.Modal.prepare();
		if(!K12.Modal.ready || K12.Modal.showingElement) {
			return;
		}
		K12.Modal.showingElement = elementToShow;
		new Effect.Appear(K12.Modal.curtain, {from: 0, to: 0.8, queue: K12.Modal.QUEUE_OPTS, afterFinish: function(){
			new Effect.Appear(elementToShow, {queue: K12.Modal.QUEUE_OPTS});}
		});
	},
	/**
	 * 
	 */
	hide: function() {
		K12.Modal.prepare();
		if(!K12.Modal.ready || !K12.Modal.showingElement) {
			return;
		}
		new Effect.Fade(K12.Modal.showingElement, {queue: K12.Modal.QUEUE_OPTS, afterFinish: function(){
			new Effect.Fade(K12.Modal.curtain, {from: 0.8, to: 0, queue: K12.Modal.QUEUE_OPTS, afterFinish: function(){K12.Modal.showingElement = null;}});
		}});
	}
};

/**
 * Muestra un "mensaje de dios" en la parte superior de la página
 * Debe ser llamada cuando hay un contenedor de id K12.FeedbackMessage.feedbackMessageContainerId,
 * sino, no hará nada.
 */
K12.FeedbackMessage = {
	/**
	 * @param spec Objeto con type del mensaje, text a mostrar y opcionalmente segundos que durará el mensaje
	 */
	show: function(spec) {
		var statics = K12.FeedbackMessage;
		var that = this;
//		if(!statics.feedbackMessageContainer && !(statics.feedbackMessageContainer = K12.Utils.findElementInParentWindows(statics.feedbackMessageContainerId))) {
		if(!statics.feedbackMessageContainer && !(statics.feedbackMessageContainer = $(statics.feedbackMessageContainerId))) {
			// no está el contenedor, así que no podemos mostrar mensajes
			return;
		}
		var messageDivContent = $(document.createElement("div")).addClassName(spec.type.toLowerCase());
		var messageDiv = $(document.createElement("div")).insert({bottom: messageDivContent}).hide();
		messageDivContent.insert({bottom: $(document.createElement("span")).addClassName("feedbackIcon")}).insert({bottom: spec.text});
		statics.feedbackMessageContainer.insert({bottom: messageDiv});
		var markDissapearing = false;
		var dissapear = function() {
			if(markDissapearing) return;
			markDissapearing = true;
			new Effect.SlideUp(messageDiv, K12.Utils.mergeProperties(statics.effectOptions, {afterFinish: function(){messageDiv.remove()}}));
		};
		var delayId = dissapear.delay(spec.secondsToShow || 7);
		messageDiv.observe("click", function(){window.clearTimeout(delayId); dissapear();});
		new Effect.SlideDown(messageDiv, statics.effectOptions);
	}
};
/**
 * Opciones para los efectos de animación en los mensajes de feedback
 */
K12.FeedbackMessage.effectOptions = {
	duration: 0.5,
	queue: {position: 'end', scope: 'feedbackMessages'}
};
/**
 * Id del div contenedor que se espera esté presente en la página, lugar donde irán ubicados los mensajes de feedback
 */
K12.FeedbackMessage.feedbackMessageContainerId = "feedbackMessages";


K12.readCookie = function(name) {
	var nameEQ = name + "=";
	var ca = document.cookie.split(';');
	for(var i=0;i < ca.length;i++) {
		var c = ca[i];
		while (c.charAt(0)==' ') c = c.substring(1,c.length);
		if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
	}
	return null;
}

K12.UserAutocompleter = Class.create({
	expanderToken: "☯✌☕",
	initialize: function(elementId, menuId, url, config, users) {
		var uacClass = K12.UserAutocompleter;
		this.multipleUsers = config.multipleUsers;
		this.otherInput = $(elementId);
		this.otherInputContainer = $(this.otherInput.parentNode);
		this.realInput = $(elementId.substring(0, elementId.lastIndexOf(":")));
		this.inputWidthExpander = this.otherInput.next("." + uacClass.withExpanderCSSClass);
		this.superContainer = this.otherInput.up("." + uacClass.falseAutocompleteInputCSSClass);
		
		var indicator;
		if(indicator = $(config.indicator)) {
			indicator.style.position = "absolute";
			indicator.clonePosition(this.superContainer, {offsetLeft: this.superContainer.offsetWidth + 5, setWidth: false, setHeight: false});
		}
		
		this.superContainer.observe("click", this.superContainerClickHandler.bindAsEventListener(this));
		
		this.otherInput.observe("keypress", this.adjustSize.bind(this));
		
		this.otherInput.observe("keyup", this.inputKeyUpHandler.bindAsEventListener(this));
		
		this.otherInput.observe("keydown", this.inputKeyDownHandler.bindAsEventListener(this));
		
		this.realInput.value = "";
		
		config.afterUpdateElement = this.afterUpdateElement.bind(this);
		config.onShow = this.onShowChoices.bind(this);
		
	    var autocompleter = $T(elementId).autocompleter = new Ajax.Autocompleter(elementId, menuId, url, config);
	    
	    // sobreescribimos para quitar el scrollIntoView
	    autocompleter.markPrevious = this.markPreviousOverride;
		autocompleter.markNext = this.markNextOverride;
		autocompleter.onClick = this.onClickOverride;
		
		if(users && Object.isArray(users)) {
			if(this.multipleUsers) {
				this.addMultipleUsers(users);
			} else {
				this.addUser(users.first());
			}
		}
		
		if(!this.multipleUsers && this.hasUsersEntered()) {
			this.otherInputContainer.hide();
		}
	},
	hasUsersEntered: function() {
		return this.superContainer.select("." + K12.UserAutocompleter.userBlockCSSClass).length > 0
	},
	markNextOverride: function() {
		if(this.index < this.entryCount-1) this.index++;
		else this.index = 0;
	},
	markPreviousOverride: function() {
		if(this.index > 0) this.index--;
		else this.index = this.entryCount-1;
	},
	onClickOverride: function(event) {
		var element = Event.findElement(event, 'LI');
	    this.index = element.autocompleteIndex;
	    this.selectEntry();
	    this.hide();
	    event.stop();
	},
	onShowChoices: function(element, update){
		update.absolutize();
		update.clonePosition(this.superContainer, {
			setHeight: false,
			offsetTop: this.superContainer.offsetHeight
		});
		Effect.Appear(update,{duration:0.15});
	},
	inputKeyDownHandler: function(event){
		if(Event.KEY_BACKSPACE == event.keyCode) {
			if(this.otherInput.value == "") {
				this.selectLastUserBlock();
				event.stop();
				return;
			}
		}
	},
	inputKeyUpHandler: function(event){
		if(Event.KEY_BACKSPACE == event.keyCode || Event.KEY_DELETE == event.keyCode) {
			this.adjustSize();
		}
	},
	superContainerClickHandler: function(event){
		if(this.superContainer == event.findElement()) {
			this.otherInput.focus();
		}
	},
	adjustSize: function() {
		this.inputWidthExpander.innerHTML = this.otherInput.value + this.expanderToken;
	},
	selectUserBlock: function(userBlock) {
		if(userBlock != null) {
			userBlock.focus();
		}
	},
	selectLastUserBlock: function() {
		this.selectUserBlock(this.otherInputContainer.previous("." + K12.UserAutocompleter.userBlockCSSClass));
	},
	addUser: function(user) {
		if(!user) return;
		var currentValues = this.realInput.value.split(",");
		if(currentValues.first() == "") currentValues.shift();
		if(currentValues.indexOf(user.userId) < 0) {
			currentValues.push(user.userId);
			this.realInput.value = currentValues.join(",");
			this.createUserBlock(user.userId, user.completeName);
		}
	},
	addMultipleUsers: function(users) {
		var userIds = new Array();
		var i = 0;
		var user;
		for(i;i < users.length; i++) {
			user = users[i];
			userIds.push(user.userId);
			this.createUserBlock(user.userId, user.completeName);
		}
		this.realInput.value = userIds.join(",");
	},
	afterUpdateElement: function(element, selectedElement) {
		var userId = selectedElement.down("." + K12.UserAutocompleter.userIdCSSClass).innerHTML;
		var completeName = selectedElement.down("." + K12.UserAutocompleter.completeNameCSSClass).innerHTML;
		this.addUser({"userId": userId, "completeName": completeName});
		element.value = "";
		if(this.multipleUsers) {
			element.focus();
		} else {
			this.otherInputContainer.hide();
			this.selectLastUserBlock();
		}
	},
	createUserBlock: function(userId, completeName) {
		var that = this;
		var newUser = $(document.createElement("a"));
		newUser.addClassName(K12.UserAutocompleter.userBlockCSSClass);
		newUser.href = "javascript:void(0)";
		newUser.insert({bottom: completeName});
		var deleteLink = $(document.createElement("span"));
		deleteLink.addClassName(K12.UserAutocompleter.deleteUserBlockCSSClass).insert({bottom: "&nbsp;"});
		newUser.insert({bottom: deleteLink});
		newUser.insert({bottom: "<span class=" + K12.UserAutocompleter.userIdCSSClass + ">"+userId+"</span>"});
		newUser.observe("keydown", function(event){
			if(Event.KEY_BACKSPACE == event.keyCode) {
				that.processDeletion(event.findElement(), userId);
				event.stop();
			}
		});
		newUser.observe("click", function(event){
			event.findElement("." + K12.UserAutocompleter.userBlockCSSClass).focus();
			event.stop();
		});
		deleteLink.observe("click", function(event){
			that.processDeletion(event.findElement("." + K12.UserAutocompleter.userBlockCSSClass), userId); 
			event.stop();
		});
		
		this.otherInputContainer.insert({before: newUser});
	},
	processDeletion: function(elementDeleted, userId){
		this.realInput.value = this.realInput.value.split(",").without(userId).join(",");
		elementDeleted.remove();
		if(!this.otherInputContainer.visible()) {
			this.otherInputContainer.show();
		}
		this.otherInput.focus();
	}
});

K12.UserAutocompleter.userBlockCSSClass = "uac-userBlock";
K12.UserAutocompleter.deleteUserBlockCSSClass = "uac-deleteUserBlock";
K12.UserAutocompleter.withExpanderCSSClass = "uac-widthExpander";
K12.UserAutocompleter.falseAutocompleteInputCSSClass = "uac-falseAutocompleteInput";
K12.UserAutocompleter.userIdCSSClass = "uac-userId";
K12.UserAutocompleter.completeNameCSSClass = "uac-completeName";


K12.MultiFileUploader = Class.create({
	
	initialize: function(specs) {
		
		var CONTAINER_ID_SUFFIX = "_container";
			PICKFILES_ID_SUFFIX = "_pickfiles",
			FILELIST_ID_SUFFIX = "_filelist",
			UPLOADFILES_ID_SUFFIX = "_uploadfiles",

			CURRENT_FILE_CSS_CLASS = "currentFile",
			FAILED_FILE_CSS_CLASS = "failedFile",
			UPLOADED_FILE_CSS_CLASS = "uploadedFile",
			PROGRESSING_UPLOAD_CSS_CLASS = "progressingUpload",
			PAUSED_UPLOAD_CSS_CLASS = "pausedUpload",
			FINISHED_UPLOAD_CSS_CLASS = "finishedUpload",

			UPLOAD_PROGRESS_CSS_SEL = "div.uploadProgressContainer",
			CLEAR_CSS_SEL = "div.cb",
			UPLOAD_STATUS_QTY_CSS_SEL = "span.uploadStatusQuantity",
			FULL_STACK_PERCENTAGE_CSS_SEL = "span.fullStackPercentage",
			FILE_PROGRESS_CSS_SEL = "div.fileProgress",
			TO_UPLOAD_QTY_CSS_SEL = "span.toUploadQuantity",
			UPLOADING_FILE_CSS_SEL = "div.uploadingFile",
			FORM_CSS_SEL = "form";
		
		var clientId = specs.clientId,
			controlName = specs.controlName,
			extensionsPaths = specs.extensionsPaths,
		
			callback = (function(){
				if (specs.callback) {
					try {
						var result = eval(specs.callback);
						if (typeof(result) == 'function') {
							return result;
						}
					} catch (e) {
						// do nothing
					}
				}
			})(),
			
			container = $(clientId + CONTAINER_ID_SUFFIX),
			pickfiles = $(clientId + PICKFILES_ID_SUFFIX),
			filelist = $(clientId + FILELIST_ID_SUFFIX),
			uploadFiles = $(clientId + UPLOADFILES_ID_SUFFIX),
			fileAddBeforeInsertPoint = filelist.down(CLEAR_CSS_SEL),
			uploadProgress = container.down(UPLOAD_PROGRESS_CSS_SEL),
			uploadStatusQuantity = uploadProgress.down(UPLOAD_STATUS_QTY_CSS_SEL),
			fullStackPercentage = uploadProgress.down(FULL_STACK_PERCENTAGE_CSS_SEL),
			fileProgress = uploadProgress.down(FILE_PROGRESS_CSS_SEL),
			toUploadQuantity = uploadProgress.down(TO_UPLOAD_QTY_CSS_SEL),
			form = container.down(FORM_CSS_SEL);
		
		if (!container || !pickfiles || !filelist || !uploadFiles || !fileAddBeforeInsertPoint || !uploadProgress
				|| !uploadStatusQuantity || !fullStackPercentage || !fileProgress || !toUploadQuantity || !form) {
			throw new Exception("Faltan elementos del uploader");
		}
	
		var	multipartParams = (function(){
				var result = {};
				var formAc = form.down('input[name=t:ac]');
				if (formAc) {
					result["t:ac"] = formAc.value;
				}
				var formData = form.down('input[name=t:formdata]');
				if (formData) {
					result["t:formdata"] = formData.value;
				}
				return result;
			})(),
		// creamos el uploader
			uploader = new plupload.Uploader({
				runtimes : 'gears,html5,flash,html4',
				file_data_name : controlName,
				browse_button : pickfiles.id,
				container : container.id,
				drop_element : filelist.id,
				max_file_size : '20mb',
				url : form.action,
				multipart : true,
				multipart_params : multipartParams,
				flash_swf_url : extensionsPaths["flash"],
		//		silverlight_xap_url : '/plupload/js/plupload.silverlight.xap',
				filters : new Array({title : "Image files", extensions : "jpg,gif,png"})
		//		resize : {width : 320, height : 240, quality : 90}
			}),
		
		// funciones auxiliares
			updateProgress = function() {
				fullStackPercentage.update(uploader.total.percent + "%");
				uploadStatusQuantity.update(uploader.total.uploaded + "/" + (uploader.total.failed + uploader.total.queued + uploader.total.uploaded));
				fileProgress.style.width = uploader.total.percent + "%";
			},
		
			changeState = function(paused) {
				container.removeClassName(PROGRESSING_UPLOAD_CSS_CLASS);
				container.removeClassName(PAUSED_UPLOAD_CSS_CLASS);
				container.removeClassName(FINISHED_UPLOAD_CSS_CLASS);
				if (uploader.state == plupload.STARTED) {
					container.addClassName(PROGRESSING_UPLOAD_CSS_CLASS);
				} else if (paused) {
					container.addClassName(PAUSED_UPLOAD_CSS_CLASS);
				} else if ((uploader.total.uploaded != 0 || uploader.total.failed != 0) && uploader.total.queued == 0) {
					container.addClassName(FINISHED_UPLOAD_CSS_CLASS);
				}
				uploader.refresh();
			},
		
			reset = function() {
				filelist.select(UPLOADING_FILE_CSS_SEL).each(
					function(s) {
						uploader.removeFile(uploader.getFile(s.id));
						Tapestry.remove(s);
					}
				);
				uploader.total.reset();
				toUploadQuantity.update(uploader.total.queued);
				updateProgress();
				changeState();
			},
			
			updateFileProgress = function(file, percent) {
				var elementDiv = $(file.id);
				var fileProgress = elementDiv.select(FILE_PROGRESS_CSS_SEL)[0];
				fileProgress.style.width = percent + "%";
			},
			
			processReply = function(reply) {
				var r = {};
				try {
					r = eval("(" + reply.response + ")");
					if (r.message) {
						window.parent.Tapestry.Initializer.feedbackMessage({"type" : r.success ? "SUCCESS" : "ERROR", "text" : r.message});
					}
					if (callback && r.callbackParams) {
						callback(r.callbackParams);
					}
				} catch (e) {
				}
				return r.success;
			},
			
			markFileFinalState = function(file, success) {
				var elementDiv = $(file.id);
				elementDiv.removeClassName(CURRENT_FILE_CSS_CLASS);
				if (success) {
					elementDiv.addClassName(UPLOADED_FILE_CSS_CLASS);
				} else {
					var fileProgress = elementDiv.select(FILE_PROGRESS_CSS_SEL)[0];
					fileProgress.previous("span").innerHTML = "Falló";
					fileProgress.style.width = "0%";
					elementDiv.addClassName(FAILED_FILE_CSS_CLASS);
				}
			};
			
		
		// binding de eventos
		uploader.bind('Init', function(up, params) {
		});
		uploader.init();
		
		uploader.bind('FilesAdded', function(up, files) {
			files.each(function(file) {
				fileAddBeforeInsertPoint.insert({before:'<div id="' + file.id + '" class="uploadingFile"><span class="fileName">' +
				file.name + '</span><a title="Eliminar elemento" class="inlineActionButton deleteElement"> </a><div class="fileWeight"><span>' +
				plupload.formatSize(file.size) + '</span><div style="width: 0%;" class="fileProgress"></div></div>'});
			});
			up.refresh(); // Reposition HTML5/Flash/Silverlight
		});
		
		uploader.bind('UploadFile', function(up, file) {
			$(file.id).addClassName(CURRENT_FILE_CSS_CLASS);
		});
		uploader.bind('StateChanged', function(up) {
			changeState();
			updateProgress();
		});
		uploader.bind('QueueChanged', function(up) {
			toUploadQuantity.update(up.total.queued);
		});
		uploader.bind('Error', function(up, err) {
			if (err.file) {
				markFileFinalState(err.file, false);
			}
			window.parent.Tapestry.Initializer.feedbackMessage({"type" : "ERROR", "text" : err.message});
		});
		uploader.bind('FileUploaded', function(up, file, response) {
			markFileFinalState(file, processReply(response));
		});
		uploader.bind('UploadProgress', function(up, file) {
			updateFileProgress(file, file.percent);
			updateProgress();
		});
		container.down("a[rel=pause]").observe('click', function(e){
			uploader.stop();
			updateProgress();
			changeState(true);
			e.preventDefault();
		});
		container.down("a[rel=play]").observe('click', function(e){
			uploader.start();
			updateProgress();
			changeState();
			e.preventDefault();
		});
		container.down("a[rel=update]").observe('click', function(e){
			reset();
			e.preventDefault();
		});
		container.select("a[rel=delete]").each(function(s){
			s.observe('click', function(e){
				reset();
				e.preventDefault();
			});
		});
		uploadFiles.observe('click', function(e) {
			uploader.start();
			updateProgress();
			changeState();
			e.preventDefault();
		});
		container.delegators('click',
			{'a.deleteElement' : function(event) {
				event.preventDefault();
				try {
					var elementDiv = event.findElement(UPLOADING_FILE_CSS_SEL);
					uploader.removeFile(uploader.getFile(elementDiv.id));
					Tapestry.remove(elementDiv);
				} catch (e) {
				}
			}}
		);
		
		// funciones públicas
		this.start = function() {
			uploader.start();
		}
		this.stop = function() {
			uploader.stop();
		}
	}
});


K12.UsersUtil = {
	uniqueValidationCallback: function(response) {
		if (response.available) { 
			$('uniqueValidationFeedback').innerHTML="<span style='color:green'>disponible</span>";
		} else {
			$('uniqueValidationFeedback').innerHTML="<span style='color:red'>no disponible</span>";
		}
	}
};


Tapestry.Initializer.feedbackMessage = function(spec) {
	// llamamos la función en la ventana padre
	K12.Utils.findSuperParentWindow().K12.FeedbackMessage.show(spec);
};

/**
 * Un init para userAutocompleter
 * elementId, menuId, url, config, users
 */
Tapestry.Initializer.userAutocompleter = function(spec) {
	new K12.UserAutocompleter(spec.inputId, spec.menuId, spec.url, spec.config, spec.users);
};

/**
 * Un init para TinyMCE
 */
Tapestry.Initializer.tinyMCE = function(spec) {
	var options = spec.options ? spec.options : {};
	if(spec.resourceId) {
		options.file_browser_callback = function(field_name, url, type, win){K12.fileBrowser(field_name, url, type, win, spec.resourceId);};
	}
	options = K12.tinyMCEConfigs[spec.type] ? K12.Utils.mergeProperties(K12.tinyMCEConfigs[spec.type], options) : options;
	// necesitamos que tinymce sepa que está cargado el dom y por lo tanto puede cargar el editor
	tinymce.dom.Event.domLoaded = true;
	var editor = new tinymce.Editor(spec.elementId, options)
	$(spec.elementId).up('form').observe(Tapestry.FORM_PREPARE_FOR_SUBMIT_EVENT, function(){editor.save();});
	editor.render();
};

Tapestry.Initializer.MultiUploader = function(specs) {
	
	new K12.MultiFileUploader(specs);
	
};

Tapestry.Initializer.zoneInserter = function(specs) {
	var zoneObject = Tapestry.findZoneManagerForZone(specs.zoneId);
	if (!zoneObject) {
		return;
	}
	zoneObject.show = function(content) {
		var key = (specs.insertion || 'bottom');
		var insertion = new Hash();
		insertion.set(key, content);
		this.updateElement.insert(insertion.toJSON().evalJSON());
		var func = this.element.visible() ? this.updateFunc : this.showFunc;
		func.call(this, this.element);
		this.element.fire(Tapestry.ZONE_UPDATED_EVENT);
	};
};

Tapestry.Initializer.hoverZone = function(specs) {
	var button = $(specs.buttonId);
	if (!button) {
		return;
	}
	var zone;
	if (specs.zoneId) {
		zone = specs.zoneId == "^" ? button.up(".t-zone") : $(specs.zoneId);
	} else {
		zone = $(specs.elementId);
	}
	if (zone && (zone.hasClassName("EditThis") || zone.down(".EditThis"))) {
		button.observe("mouseover", function(){zone.addClassName("EditThisHover");});
		button.observe("mouseout", function(){zone.removeClassName("EditThisHover");});
		button.observe("click", function(){zone.removeClassName("EditThisHover");});
	}
};

Tapestry.Initializer.slidingFieldset = function(specs) {
	var wrapper = $j("<div />");
	if (specs.closed) {
		wrapper.attr("style", "display: none");
	}
	$j("#" + specs.elementId).contents().not($j("#" + specs.elementId + ' > legend')).wrapAll(wrapper);
	$j("#" + specs.elementId + ' > legend').click(
		function(){
			$j(this).next().slideToggle();
		}
	);
};

Tapestry.Initializer.slider = function(spec) { 

	var min = spec.values.min();
	var max = spec.values.max();
	
	var HIDDEN_FIELD_SUFIX = '_value';
	var HANDLE_ID_SUFIX = '_handle';
	var TRACK_ID_SUFIX = '_track';
	var LABEL_FIELD_ID_CONST = '_value_';
	var DEFAULT_CLASS_NAME = '';
	var SELECTED_CLASS_NAME = 'selected';
	
	var currentValue = $(spec.sliderId+HIDDEN_FIELD_SUFIX).value;
	
	Event.observe(window,'load',
		function() {
			new Control.Slider(spec.sliderId+HANDLE_ID_SUFIX , spec.sliderId+TRACK_ID_SUFIX, { 
				range: $R(min,max), 
				values: spec.values, 
				sliderValue: currentValue, 
				onSlide: function(value) {
					if (value != currentValue) {
						$(spec.sliderId+LABEL_FIELD_ID_CONST+currentValue).className = DEFAULT_CLASS_NAME;
						currentValue = value;
						$(spec.sliderId+LABEL_FIELD_ID_CONST+currentValue).className = SELECTED_CLASS_NAME;
						$(spec.sliderId+HIDDEN_FIELD_SUFIX).value = value;
					}
				}
			} );
		}
	);
};


/** *********** Reconocimiento y advertencia sobre IE 6 ********** */
K12.noIETimeoutName = 'noIEShowed';
K12.verifyIE6 = function() {
	if(Prototype.Browser.IE) {
		if(parseInt(navigator.userAgent.substring(navigator.userAgent.indexOf("MSIE")+5)) <= 6) {
			var messageShown = K12.readCookie(K12.noIETimeoutName);
			if(!messageShown) {
				var expireDate = new Date();
				expireDate.setHours(expireDate.getHours() + 24);
				document.cookie = K12.noIETimeoutName + '=true; expires=' + expireDate.toUTCString() + '; path=/';
				Tapestry.ajaxRequest('/icore/noIE:showMessage', function(transport){
					var reply = transport.responseJSON;
					Tapestry.loadScriptsInReply(reply, function() {
						reply.content && $(document.body).insert({bottom: reply.content});
					}.bind(this));
				});
				
			}
		}
	}
}
