/**
 * This file must be included for using Ajax support. When an instance of the Ajax class is created, the request function
 * may be used to post a request. Some handlers (onLoad, onSuccess & onFailure) may be passed as arguments.
 * 
 * @version 11-01-2010
 * @author <a href="mailto:r.tennapel@griponservice.nl?SUBJECT=widget.js">R. ten Napel, ing.</a>
 **/
function Widget(element, name, params, callBack, onLoad, onSuccess, onFailure, async) {
	// TODO JS:
	// 1. Find all occurences of widget placeholders
	// 2. Create for each placeholder a JS widget
	//    a. Load the CSS (if not loaded yet)
	//    b. Load the JS (if not loaded yet) and retrieve all widget callBacks from the JS
	// 3. Do an Ajax-request to the widget placeholder (deploy widget) and handle all callBacks
	// 4. Check if new widget placeholders are in the widget and go to step 1.
	
	// TODO PHP:
	// 1. Create the PHP widget
	// 2. Load the CSS (if not loaded yet)
	// 3. Write the html (deploy widget)
	// 4. Create a JS widget
	//    a. Load the CSS (if not loaded yet)
	//    b. Load the JS (if not loaded yet) and retrieve all widget callBacks from the JS
	
	// If the element doesn't have a widget create it, else retrieve it:
	var widget;
	if (!element.widget) {
		widget = new function() {
			this.element = element;
			this.name = name;
			this.params = params;
			this.widgetRoot = "./Widgets/" + name + "/";
			
			// Load the CSS & JS:
			//GlobalKit.loadCSS(this.widgetRoot + "style.css");
			//GlobalKit.loadJS(this.widgetRoot + "script.js");
			
			// Define the Ajax request URL:
			var url = this.widgetRoot + "index.php";
			if (params != null) {
				url = url + "?" + params;
			}
			
			// Create the Ajax-object:
			var ajax = new Ajax(element, url, callBack, onLoad, onSuccess, onFailure);
			ajax.addCallBack(function() { Widget.handleCallBack(name, params); });
			this.ajax = ajax;
		};
	} else {
		widget = element.widget;
	}
	
	// Store the new name:
	if (name != null) {
		widget.name = name;
	}
	
	// Store the new parameters:
	if (params != null) {
		widget.params = params;
	}
	
	// Store everything:
	element.widget = widget;
	this.element = element;
}

/**
 * Call the Ajax-request for this widget and handle all callBacks.
 **/
Widget.prototype.deploy = function() {
	this.element.widget.ajax.request();
};

/**
 * Refresh the widget contents and alter the parameters if set.
 * 
 * @param params			The new parameters.
 **/
Widget.prototype.refresh = function(params) {
	// Store the new params:
	if (params != null) {
		this.element.widget.params = params;
	}
	
	// Define the Ajax request-URL:
	var url = this.element.widget.widgetRoot + "index.php";
	if (params != null) {
		url = url + "?" + params;
	}
	
	// Alter the URL:
	this.element.widget.ajax.url = url;
	
	// Refresh the widget contents:
	this.deploy();
};

/**
 * Converts a string (URL) with seperated key/value-pairs (key1=value1&key2=value2&key3=value3) to an array.
 * 
 * @param string			The string with key/value-pairs.
 * 
 * @return					An array with all the key/value=pairs.
 **/
Widget.toParamArray = function(string) {
	// Split all param / value pairs:
	var params = string.split("&");
	
	// Fix each parameter:
	for (var c = 0; c < params.length; c++) {
		params[c] = params[c].split("=");
	}
	
	return params;
};

/** The widgets callback, name and class. **/
Widget.widgetNames = new Array();
Widget.callBacks = new Array();
Widget.classes = new Array();

/**
 * Add a callback function to the stack for a specific widget, overwrite if exists.
 * 
 * @param widgetName		The name of the widget where the callback function belongs to.
 * @param callBack			The callback function that must be run when the corresponding widget is loaded / deployed.
 **/
Widget.addCallBack = function(widgetName, callBack) {
	// Retrieve where the widget name is stored:
	var i = this.widgetNames.indexOf(widgetName);
	
	// Add the widget name and callback if not yet stored, else overwrite the callback:
	if (i == -1) {
		this.widgetNames.add(widgetName);
		this.callBacks.add(callBack);
		this.classes.add(function() {});
	} else {
		this.callBacks[i] = callBack;
	}
};

/**
 * Handles the callback function that belongs to the widget with the given widget name. The parameters that the callback function uses are also passed.
 * 
 * @param widgetName		The name of the widget to call the callback function from.
 * @param parameters		The parameters to use with the callback function
 **/
Widget.handleCallBack = function(widgetName, parameters) {
	// Retrieve where the widget name is stored:
	var i = this.widgetNames.indexOf(widgetName);
	
	// If the widget name is stored run the callback:
	if (i > -1) {
		this.callBacks[i].apply(this, Widget.toParamArray(parameters));
	}
};

/**
 * Add a class to the stack for a specific widget. The class contains all the JavaScript functions necessary for the widget.
 * 
 * @param widgetName		The name of the widget where the handler belongs to.
 * @param clazz				The class itself with all the widget functions.
 **/
Widget.addClass = function(widgetName, clazz) {
	// Retrieve where the widget name is stored:
	var i = this.widgetNames.indexOf(widgetName);
	
	// Add the widget name, callback and handler if not yet stored, else overwrite the handler:
	if (i == -1) {
		this.widgetNames.add(widgetName);
		this.callBacks.add(function() {});
		this.classes.add(clazz);
	} else {
		this.classes[i] = clazz;
	}
}

/**
 * Retrieve the class with widget functions for a particular widget.
 * 
 * @param widgetName		The name of the widget to retrieve the class for.
 * 
 * @return					The class belonging to the given widget.
 **/
Widget.getClass = function(widgetName) {
	// Retrieve where the widget name is stored:
	var i = this.widgetNames.indexOf(widgetName);
	
	// Return the handler if the widget name exists:
	if (i > -1) {
		return this.classes[i];
	}
	
	alert("No class defined for '" + widgetName + "-Widget'!");
	
	return null;
}

/**
 * Replace an element dummie with an actual widget.
 * 
 * @param element			The element to replace with an actual widget.
 * @param value				The value that holds the widget name and params seperated by a '?'.
 **/
Widget.replace = function(element, value) {
	// If the browser doesn't support element creation & childnodes exit with an error:
	if (!document.createElement || !document.childNodes ) {
		alert("Your browser is not DOM compliant!");
		
		return;
	}
	
	// Split the widget name from the params:
	var parts = value.split("?");
	var widgetName = parts[0];
	var widgetParams = parts.remove(widgetName).join("");
	
	// Create the widget wrapper:
	var widgetWrapper = document.createElement("div");
	widgetWrapper.setAttribute("class", "widget_wrapper");
	
	// Remove the element to replace and place the widget wrapper:
	element.parentNode.insertBefore(widgetWrapper, element);
	element.parentNode.removeChild(element);
	
	// Deploy the widget:
	new Widget(widgetWrapper, widgetName, widgetParams, function() { Widget.replaceAll(document.getElementsByName("widget")); }).deploy();
};

/**
 * Replace (all) widget dummie(s) with actual widget(s).
 * 
 * @param widgets			The place holder(s) (array) (<input type="hidden" name="widget" value="name_of_widget?widget_params">).
 **/
Widget.replaceAll = function(widgets) {
	// Iterate through all widgets:
	while (widgets.length > 0) {
		var i = widgets.length - 1;
		
		Widget.replace(widgets[i], widgets[i].value);
	}
};
