/*
 * Copyright 2004-2008 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
dojo.require("dojo.parser");
dojo.require("dijit.form.CurrencyTextBox");
dojo.require("dijit.form.NumberTextBox");
dojo.require("dijit.form.ValidationTextBox");
dojo.require("dijit.form.DateTextBox");
dojo.require("dijit.form.FilteringSelect");
dojo.require("dijit.form.Textarea");
dojo.require("dijit.form.SimpleTextarea");
dojo.require("dijit.form.CheckBox");
dojo.require("dijit.form.RadioButton");
dojo.require("dijit.Dialog");

Spring._addDecoration = Spring.addDecoration;
Spring.addDecoration = function (/*Object*/decoration) {
	Spring._addDecoration(decoration);
	return decoration.widget;
}

function applyClientValidations(tableId) {
	var alertMsg = "";
	dojo.query("[type=checkbox]", tableId).forEach(function(checkbox) { 
		try {
			dojoCheckbox = dijit.byId(checkbox.id);
			//dojoCheckbox._parentTr = checkbox.parentElement.parentElement.parentElement;
			dojoCheckbox._parentTr = $(checkbox).closest("tr")[0];
			dojoCheckbox._controlledElements = dojo.query("[aria-required=true]", dojoCheckbox._parentTr);
			dojoCheckbox._controlledElements.forEach(function(elm) { 
				dojoElm = dijit.byId(elm.id);
				dojoElm._relatedCheckbox = dojoCheckbox; 
				dojoElm.old_isValid = dojoElm.isValid; 
				dojoElm.isValid = function() {
					if (this._relatedCheckbox && !this._relatedCheckbox.checked) {
						return true;
					}
					return this.old_isValid(); 
				};
			});
			dojoCheckbox.old_onChange = dojoCheckbox.onChange;
			dojoCheckbox.onChange = function() {
				this._controlledElements.forEach(function(elm) {
					dojoElm = dijit.byId(elm.id);
					dojoElm.validate();
				});
				return this.old_onChange(); 
			};
		} catch (e) {}
    });
}

dojo.provide("voila.form.ValidationTextarea");
dojo.declare("voila.form.ValidationTextarea", [dijit.form.ValidationTextBox,dijit.form.Textarea],
    {
        invalidMessage: "This field is required",
        regExp: "(.|\\s)*",

        postCreate: function() {
            this.inherited(arguments);
        },

        validate: function() {
        	if (arguments.length==0) {
        		return this.validate(false);
        	}
        	return this.inherited(arguments);
        },

        onFocus: function() {
            if (!this.isValid()) {
                this.displayMessage(this.getErrorMessage());
            }
        },

        onBlur: function() {
            this.validate(false);
        },
        
        _onInput:function(e){
        	return this.inherited(arguments);
        }
     }
);

dojo.provide("voila.form.ValidationSimpleTextarea");
dojo.declare("voila.form.ValidationSimpleTextarea", [dijit.form.ValidationTextBox,dijit.form.SimpleTextarea],
    {
        invalidMessage: "This field is required",
        regExp: "(.|\\s)*",
        leftChars: null,

        postCreate: function() {
            this.inherited(arguments);
        	this.leftChars = document.getElementById(this.id + "_leftChars");
        	this.renderLeftChars();
        },

        validate: function() {
        	if (arguments.length==0) {
        		return this.validate(false);
        	}
        	return this.inherited(arguments);
        },

        onFocus: function() {
            if (!this.isValid()) {
                this.displayMessage(this.getErrorMessage());
            }
        },

        onBlur: function() {
            this.validate(false);
        },

        _onInput:function(e){
        	var result = this.inherited(arguments);
        	this.renderLeftChars();
        	return result;
        },
        
        renderLeftChars: function() {
        	if (this.leftChars) {
        		this.leftChars.innerHTML = this.maxLength - this.displayedValue.length;
        		//console.log(this.leftChars.innerHTML);
        	}
        }

     }
);

dojo.declare("Spring.DefaultEquals", null, {
	equals : function(/*Object*/other){
		if (other.declaredClass && other.declaredClass == this.declaredClass) {
			return true;
		}else{
			return false;
		}
	}			
});

dojo.declare("Spring.ElementDecoration", [Spring.AbstractElementDecoration, Spring.DefaultEquals], {
	constructor : function(config) {
		this.widgetAttrs = {};
		this.copyFields = new Array('name', 'value', 'type', 'checked', 'selected', 'readOnly', 'disabled', 'alt', 'maxLength', 'class', 'title', 'tabIndex', 'rows', 'cols', 'propercase');
		this.eventFields = new Array('onblur', 'onchange', 'onclick', 'ondblclick', 'onfocus');
		dojo.mixin(this, config);
		this.element = dojo.byId(this.elementId);
		this.elementId = dojo.isString(this.elementId) ? this.elementId : this.elementId.id;
		if(this.widgetModule == "") {
			this.widgetModule = this.widgetType;
		}
	}, 
	
	apply : function(){
		if (dijit.byId(this.elementId)) {
			dijit.byId(this.elementId).destroyRecursive(false);
		}
		
		if (!this.element) {
			console.error("Could not apply " + this.widgetType + " decoration.  Element with id '" + this.elementId + "' not found in the DOM.");
		}
		else {
			var backupValue;

			for (var copyField in this.eventFields) {
				copyField = this.eventFields[copyField];
				if (!this.widgetAttrs[copyField] && this.element[copyField] && this.element[copyField] != null) {
					newName = "on" + copyField.charAt(2).toUpperCase() + copyField.slice(3);
					this.widgetAttrs[newName] = this.element[copyField];
				}
			}
			/*
			 * dijit.form.DateTextBox uses locale information when displaying a date and a 
			 * fixed date format when parsing or serializing date values coming from the 
			 * server-side. There is no way configure it to use a single date pattern,
			 * which is the reason for the code below.
			 */
			var datePattern = this.widgetAttrs['datePattern'];
			if (datePattern && this.widgetType == 'dijit.form.DateTextBox') {
				if (!this.widgetAttrs['value']) {
					// Help dijit.form.DateTextBox parse the server side date value.
					this.widgetAttrs['value'] = dojo.date.locale.parse(this.element.value, {selector : "date", datePattern : datePattern});
				}
				if (!this.widgetAttrs['serialize']) {
					// Help dijit.form.DateTextBox format the date to send to the server side.
					this.widgetAttrs['serialize'] = function(d, options){
						return dojo.date.locale.format(d, {selector : "date", datePattern : datePattern});
					}
				}
				// Add a constraint that specifies the date pattern to use when displaying a date
				// but don't interfere with any constraints that may have been specified. 
				if (!this.widgetAttrs['constraints']) {
					this.widgetAttrs['constraints'] = {};
				}
				if (!this.widgetAttrs['constraints'].datePattern) {
					this.widgetAttrs['constraints'].datePattern = datePattern;
				}
			}
			for (var copyField in this.copyFields) {
				copyField = this.copyFields[copyField];
				if (!this.widgetAttrs[copyField] && this.element[copyField]) {
					if (!(copyField == "maxLength" && this.element[copyField] == -1)) {
						this.widgetAttrs[copyField] = this.element[copyField];
					}
				}
			}
			if(this.element['style'] && this.element['style'].cssText){
				this.widgetAttrs['style'] = this.element['style'].cssText;
			}
			if(this.element['size']){
				if (this.widgetAttrs['class']) {
					this.widgetAttrs['class'] += ' em' + this.element['size'];
				} else {
					this.widgetAttrs['class'] = 'em' + this.element['size'];
				}
			}
			if(this.element['className']){
				if (this.widgetAttrs['class']) {
					this.widgetAttrs['class'] += ' ' + this.element['className'];
				} else {
					this.widgetAttrs['class'] = this.element['className'];
				}
			}
				
//			try {
			dojo.require(this.widgetModule);
//			} catch (e) {
//				console.log(e);
//			} 
			var widgetConstructor = dojo.eval(this.widgetType);
			if (widgetConstructor) {
				this.widget = new widgetConstructor(this.widgetAttrs, this.element);
				this.widget.startup();
				this.widget._onBlur();
			} else {
				console.log("error, widget: " + this.widgetType + " not found");
			}
		}
   		//return this to support method chaining
   		return this;
	},
	
	validate : function(){
		if (!this.widget.isValid) {
			// some widgets cannot be validated
			return true;
		}
		var isValid = this.widget.isValid(false);
		if (!isValid) {
			this.widget.state = "Error";
			this.widget._setStateClass();
		}
		return isValid;
	}
});

dojo.declare("Spring.ValidateAllDecoration", [Spring.AbstractValidateAllDecoration, Spring.DefaultEquals], {
	constructor : function(config) {
		this.originalHandler = null;
		this.connection = null;
		dojo.mixin(this, config);
	},
	
	apply : function() {
		var element = dojo.byId(this.elementId);
		if (!element) {
			console.error("Could not apply ValidateAll decoration.  Element with id '" + this.elementId + "' not found in the DOM.");
		} else {
			this.originalHandler = element[this.event];
			var context = this;
			element[this.event] = function(event){
				context.handleEvent(event, context);
			};
		}
		return this;
	},
	
	cleanup : function(){
		dojo.disconnect(this.connection);
	},
	
	handleEvent : function(event, context){ 
		skipValidation = false;
		if (window.GLOBAL_VALIDATION_MSG == undefined) {
			GLOBAL_VALIDATION_MSG = "Sono presenti errori di validazione nella pagina; tali errori sono specificati singolarmente in corrispondenza di ogni campo evidenziato in giallo.";
		}
		if (!Spring.validateAll()) {
			showDojoAlertDialog("<div class='message error'><p class='message error'>" + GLOBAL_VALIDATION_MSG + "</p></div>");
			window.GLOBAL_SKIP_FORM_FOCUS = true;
			dojo.publish(this.elementId+"/validation", [false]);
			dojo.stopEvent(event);
		} else {
			dojo.publish(this.elementId+"/validation", [true]);
			this.handleWaitPopup(this.elementId);
			if(dojo.isFunction(context.originalHandler)) {
				var result = context.originalHandler(event);
				if (result == false) {
					dojo.stopEvent(event);
				}
			}
		}
	},
	
	handleWaitPopup : function(elementId) { 
		var element = dojo.byId(this.elementId);
		if (element.className && element.className.indexOf("noWaitPopup") > -1) {
			skipWaitPopup = true;
		} else {
			skipWaitPopup = false;
		}
	}
});

dojo.declare("Spring.SkipValidationDecoration", [Spring.ValidateAllDecoration], {
	handleEvent : function(event, context){
		skipValidation = true;
		this.handleWaitPopup(this.elementId);
		if(dojo.isFunction(context.originalHandler)) {
			var result = context.originalHandler(event);
			if (result == false) {
				dojo.stopEvent(event);
			}
		}
	}
});

dojo.declare("Spring.AjaxEventDecoration", [Spring.AbstractAjaxEventDecoration, Spring.DefaultEquals], {
	constructor : function(config){
		this.validationSubscription = null;
		this.connection = null;
		this.allowed = true;
		dojo.mixin(this, config);
	},
	
	apply : function() {
		
		var element = dijit.byId(this.elementId) ? dijit.byId(this.elementId) : dojo.byId(this.elementId);
		if (!element) {
			console.error("Could not apply AjaxEvent decoration.  Element with id '" + this.elementId + "' not found in the DOM.");
		} else {
			this.validationSubscription = dojo.subscribe(this.elementId+"/validation", this, "_handleValidation");
			this.connection = dojo.connect(element, this.event, this, "submit");
		}
		return this;	
	},
	
	cleanup : function(){
		dojo.unsubscribe(this.validationSubscription);
		dojo.disconnect(this.connection);
	},
	
	submit : function(event){
		if (this.sourceId == ""){
			this.sourceId = this.elementId;
		}
		if(this.formId == ""){
			Spring.remoting.getLinkedResource(this.sourceId, this.params, this.popup);
		} else {
			if (this.allowed){
				Spring.remoting.submitForm(this.sourceId, this.formId, this.params);
			}
		}
		dojo.stopEvent(event);
	},
	
	_handleValidation : function(success){
		if (!success) {
			this.allowed=false;
		} else {
			this.allowed=true;
		}
	}
});

dojo.declare("Spring.RemotingHandler", Spring.AbstractRemotingHandler, {
	constructor : function(){},
	
	submitForm : function(/*String */ sourceId, /*String*/formId, /*Object*/ params) {
		var content = new Object();
		for (var key in params) {
			content[key] = params[key];
		}
		
		var sourceComponent = dojo.byId(sourceId);
	
	    if (sourceComponent != null){
	    	if(sourceComponent.value != undefined && sourceComponent.type && ("button,submit,reset").indexOf(sourceComponent.type) < 0) {
				content[sourceId] = sourceComponent.value;
			}
			else if(sourceComponent.name != undefined) {
	    		content[sourceComponent.name] = sourceComponent.name;
	    	} else {
	    		content[sourceId] = sourceId;
	    	}
	    }
	    
		if (!content['ajaxSource']) {
			content['ajaxSource'] = sourceId;
		}
		
		var formNode = dojo.byId(formId);
		var formMethod = dojo.string.trim(formNode.method);
		formMethod = formMethod.length > 0 ? formMethod.toUpperCase() : "GET";
		
		dojo.xhr(formMethod, {
			
			content: content,
			
			form: formId,
			
			handleAs: "text",
			
			headers: {"Accept" : "text/html;type=ajax"},
			
			// The LOAD function will be called on a successful response.
	        load: this.handleResponse,
	
	        // The ERROR function will be called in an error case.
	        error: this.handleError
        }, formMethod == "POST" ? true : false);	

	},
	
	getLinkedResource: function(/*String */ linkId, /*Object*/params, /*boolean*/ modal) {
		this.getResource(dojo.byId(linkId).href, params, modal);
	},
	
	getResource: function(/*String */ resourceUri, /*Object*/params, /*boolean*/ modal) {
		
		dojo.xhrGet({
			
			url: resourceUri,
			
			content: params,
			
			handleAs: "text",
			
			headers: {"Accept" : "text/html;type=ajax"},
			
			load: this.handleResponse,
			
			error: this.handleError,
			
			modal: modal
		});
	},
	
	handleResponse: function(response, ioArgs) {
		
		//First check if this response should redirect
		var redirectURL = ioArgs.xhr.getResponseHeader('Spring-Redirect-URL');
		var modalViewHeader = ioArgs.xhr.getResponseHeader('Spring-Modal-View');
		var modalView = ((dojo.isString(modalViewHeader) && modalViewHeader.length > 0) || ioArgs.args.modal);
		
		if (dojo.isString(redirectURL) && redirectURL.length > 0) {
			if (modalView) {
				//render a popup with the new URL
				Spring.remoting._renderURLToModalDialog(redirectURL);
				return response;
			}
			else {
				if (redirectURL.indexOf("/") >= 0) {
					window.location = window.location.protocol + "//" + window.location.host + redirectURL;
				} else {
					var location = window.location.protocol + "//" + window.location.host + window.location.pathname;
					var appendIndex = location.lastIndexOf("/");
					location = location.substr(0,appendIndex+1) + redirectURL;
					if (location == window.location) {
						Spring.remoting.getResource(location, ioArgs.args.content, false);
					}
					else {
						window.location = location;
					}
				}
				return response;
			}
		} else if ((dojo.string.trim(response).length == 0) && (ioArgs.xhr.status != 204) && (ioArgs.xhr.status != 205)) {
			if (Spring.debug) {
				Spring.remoting.showError('Received empty response with no Spring redirect headers. If this is intentional set the response status code to 204 or 205.');
			}
		}
		
		//Extract and store all <script> elements from the response
		var scriptPattern = '(?:<script(.|[\n|\r])*?>)((\n|\r|.)*?)(?:<\/script>)';
		var extractedScriptNodes = [];
		var matchAll = new RegExp(scriptPattern, 'img');
		var matchOne = new RegExp(scriptPattern, 'im');
	
		var scriptNodes = response.match(matchAll);
		if (scriptNodes != null)
		{
			for (var i=0; i<scriptNodes.length; i++)
			{
				var script = (scriptNodes[i].match(matchOne) || ['','',''])[2];
				script = script.replace(/<!--/mg,'').replace(/\/\/-->/mg,'').replace(/<!\[CDATA\[(\/\/>)*/mg,'').replace(/(<!)*\]\]>/mg,'');
				extractedScriptNodes.push(script);
			}
		}
		// Remove scripts but don't remove scripts entirely (see SWF-1358) 
		response = response.replace(matchAll, '<script> // Original script removed to avoid re-execution </script>');

		if (modalView) {
			//For a modal view, just dump the response into a modal dialog
			Spring.remoting._renderResponseToModalDialog(response);
		} else {
			//Extract the new DOM nodes from the response
			var tempSpan = dojo.doc.createElement("span");
			tempSpan.id="ajaxResponse";
			tempSpan.style.display= "none";
			document.body.appendChild(tempSpan);
			tempSpan.innerHTML=response;
			var tempContainer = new dojo.NodeList(tempSpan);

			console.error("ioArgs.args.content.targetId=" + ioArgs.args.content.targetId);

//			var newNodes = tempContainer.query(">").orphan();
			var newNodes;
			if (ioArgs.args.content.targetId == undefined) {
				newNodes = tempContainer.query(">").orphan();
			} else {
				newNodes = tempContainer.query("*");
			}
			
			tempContainer.orphan();
			
			//Insert the new DOM nodes and update the Form's action URL
			newNodes.forEach(function(item){
				console.info("item=" + item.id);
				if (item.id != null && item.id != "" && (item.id == ioArgs.args.content.targetId || ioArgs.args.content.targetId == undefined)) {
					var target = dijit.byId(item.id) ? dijit.byId(item.id).domNode : dojo.byId(item.id);
					if (!target) {
						console.error("An existing DOM elment with id '" + item.id + "' could not be found for replacement.");
					} else {
						target.parentNode.replaceChild(item, target);
					}
				}
			});
		}
		
		//Evaluate any script code
		dojo.forEach(extractedScriptNodes, function(script){
			dojo.eval(script);
		});
		
		return response;
	},
	
	handleError: function(response, ioArgs) {
		dojo.require("dijit.Dialog");

		console.error("HTTP status code: ", ioArgs.xhr.status);
		
		if (Spring.debug && ioArgs.xhr.status != 200) {
			var dialog = new dijit.Dialog({ title: 'Ajax Request Error' });
			dojo.connect(dialog, "hide", dialog, function(){
				this.destroyRecursive(false);
			});
			dialog.domNode.style.overflow = "auto";
			dialog.setContent(ioArgs.xhr.responseText);
			dialog.show();
		}
		
		return response;
	},
	
	showError: function(message) {
		dojo.require("dijit.Dialog");
		
		var dialog = new dijit.Dialog({ title: 'Error Message' });
		dojo.connect(dialog, "hide", dialog, function(){
			this.destroyRecursive(false);
		});
		dialog.domNode.style.width = "500px";
		dialog.setContent(message);
		dialog.show();
	},
	
	_renderURLToModalDialog: function(url) {
		Spring.remoting.getResource(url, {}, true);
	},
	
	_renderResponseToModalDialog: function(response) {
		dojo.require("dijit.Dialog");
		
		var dialog = new dijit.Dialog({});
		dialog.setContent(response);
		dojo.connect(dialog, "hide", dialog, function(){
			this.destroyRecursive(false);
		});
		dialog.show();
	}
});

dojo.declare("Spring.CommandLinkDecoration", [Spring.AbstractCommandLinkDecoration, Spring.DefaultEquals], {
	constructor : function(config){
		dojo.mixin(this, config);
	},
	
	apply : function(){
		var advisedNode = dojo.byId(this.elementId);
		if (!dojo.hasClass(advisedNode, "progressiveLink")) {
			//Node must be replaced
			var nodeToReplace = new dojo.NodeList(advisedNode);
			nodeToReplace.addContent(this.linkHtml, "after").orphan("*");
			//Get the new node
			advisedNode = dojo.byId(this.elementId);
		}
		advisedNode.submitFormFromLink = this.submitFormFromLink;
		//return this to support method chaining
   		return this;
	},
	
	submitFormFromLink : function(/*String*/ formId, /*String*/ sourceId, /*Array of name,value params*/ params){
		var addedNodes = [];
		var formNode = dojo.byId(formId);
		var linkNode = document.createElement("input");
		linkNode.name = sourceId;
		linkNode.value = "submitted";
		addedNodes.push(linkNode);
		
		dojo.forEach(params, function(param){
			var paramNode = document.createElement("input");
			paramNode.name=param.name;
			paramNode.value=param.value;
			addedNodes.push(paramNode);
		});
		
		dojo.forEach(addedNodes, function(nodeToAdd){
			dojo.addClass(nodeToAdd, "SpringLinkInput");
			dojo.place(nodeToAdd, formNode, "last");
		});		
		
		if ((formNode.onsubmit ? !formNode.onsubmit() : false) || !formNode.submit()) {
			dojo.forEach(addedNodes, function(hiddenNode){
				hiddenNode.parentNode.removeChild(hiddenNode);
			});
		}
	}
});

var GLOBAL_VALIDATE_HIDDEN_FIELDS = true;
Spring.validateAll = function(){
	var valid = true;
	for (var elementId in Spring.decorations) {
		for (var x = 0; x < Spring.decorations[elementId].length; x++) {
			if (Spring.decorations[elementId][x].widget && !Spring.decorations[elementId][x].validate()) {
				if (GLOBAL_VALIDATE_HIDDEN_FIELDS || Spring.decorations[elementId][x].widget.domNode.offsetWidth > 0) {
					console.log("Validation exception occurs on widget id: " + Spring.decorations[elementId][x].widget.id + " with name: " + Spring.decorations[elementId][x].widget.name);
					valid = false; 
				}
			}
		}
	}
	return valid;
};

dojo.addOnLoad(Spring.initialize);
