/**
 * This file must be included to support chainability. When an element is made chainable, the given element supports a lot of
 * handy functions.
 * 
 * @version 13-01-2010
 * @author <a href="mailto:r.tennapel@griponservice.nl?SUBJECT=chainability.js">R. ten Napel, ing.</a>
 **/

/**
 * Wrap element(s) in a chainable class. You can parse arrays, elements and element ID's as parameters.
 * 
 * @param els				The element(s) (ID('s)) to make chainable.
 * @param ...				More elements to make chainable.
 * 
 * @return					The chainable element wrapper.
 **/
function _$(els) {
	this.elements = [];
	this.tweens = [];
	
	// Add all elements to the global element array:
	for (var i = 0; i < els.length; i++) {
		var element = els[i];
		
		// Check type of the element(s) and add to array:
		if (GlobalKit.isArray(element)) {
			for (var e = 0; e < element.length; e++) {
				this.addElement(element[e]);
			}
		} else if (typeof element == "string") {
			this.addElement(document.getElementById(element));
		} else {
			this.addElement(element);
		}
	}
	
	return this;
}

/**
 * Retrieve a single element from the elements list.
 * 
 * @param index				The index of the element to retrieve.
 * 
 * @return					The corresponding element.
 **/
_$.prototype.$ = function(index) {
	// Set to 0 if no index is given:
	if (index == null) {
		index = 0;
	}
	
	return this.elements[index];
};

/**
 * Add an element to the wrapper.
 * 
 * @param element			The element to add to the chainable wrapper.
 * 
 * @return					This wrapper.
 **/
_$.prototype.addElement = function(element) {
	if (element != null) {
		this.elements.push(element);
	}
	
	return this;
};

/**
 * Apply a function to each element in this wrapper.
 * 
 * @param func				The function to apply.
 * 
 * @return					This wrapper.
 **/
_$.prototype.each = function(func) {
	for (var i = 0, len = this.elements.length; i < len; ++i) {
		func.call(this, this.elements[i]);
	}
	
	return this;
};

/**
 * Shows an alert message generated by this wrapper for each element.
 * 
 * @param message			The message to show.
 * 
 * @return					This wrapper.
 **/
_$.prototype.alert = function(message) {
	this.each(function(element) {
		alert("Element '" + element.id + "' alerts: " + message);
	});
	
	return this;
};

/**
 * Calls the debugger to show a debug message.
 * 
 * @param value				The value to debug.
 * @param ...				More values to debug.
 * 
 * @return					This wrapper.
 **/
_$.prototype.debug = function(value) {
	var args = arguments;
	
	// Iterate through each element:
	this.each(function(element) {
		var values = [];
		
		// Add text:
		values.push("<b>Element '<em>" + element.id + "</em>' debug:</b>");
		
		// When no value is given use the element:
		for (var i = 0; i < args.length; i++) {
			if (args[i] == null) {
				values.push(element);
			} else {
				values.push(args[i]);
			}
		}
		
		// Write to the debugger:
		try {
			Debugger.write.apply(Debugger, values).show();
		} catch(e) {
			$(element).alert("'" + e.name + "' in '_$.prototype.debug': " + e.message);
		}
	});
	
	return this;
};

/**
 * Change the inner HTML of the elements to the given HTML code.
 * 
 * @param html				The HTML to give each element.
 * 
 * @return					This wrapper.
 **/
_$.prototype.update = function(html) {
	this.each(function(element) {
		if (typeof element.update != "undefined") {
			element.update(html);
		} else {
			element.innerHTML = html;
		}
	});
	
	return this;
};

/**
 * Append new HTML code to the existing HTML of each element.
 * 
 * @param html				The HTML code to append to each element.
 * 
 * @return					This wrapper.
 **/
_$.prototype.append = function(html) {
	this.each(function(element) {
		if (typeof element.append != "undefined") {
			element.append(html);
		} else {
			element.innerHTML = html + element.innerHTML;
		}
	});
	
	return this;
};

/**
 * Set the style of each element.
 * 
 * @param property			The property style to set.
 * @param value				The value of the property to set.
 * 
 * @return					This wrapper.
 **/
_$.prototype.setStyle = function(property, value) {
	this.each(function(element) {
		// Check if opacity or transparency:
		if (property == "opacity") {
			return this.setOpacity(value);
		} else if (property == "transparency") {
			return this.setTransparency(value);
		}
		
		// Set the style:
		try {
			element.style[property] = value;
		} catch(e) {
			this.debug("Error in '_$.prototype.setStyle': " + e.message, element);
			
			return "";
		}
	});
	
	return this;
};

/**
 * Retrieve the style property value of the first element.
 * 
 * @param property			The property style to get.
 * 
 * @return					This wrapper.
 **/
_$.prototype.getStyle = function(property) {
	var style;
	
	// Try to get the property value:
	try {
		var style = this.elements[0].style[property];
	} catch(e) {
		this.debug("Error in '_$.prototype.setStyle': " + e.message, element);
		
		return "";
	}
	
	return style;
};

/**
 * Checks if all the elements have the given class name.
 * 
 * @param className			The class name to check for.
 * 
 * @return					This wrapper.
 **/
_$.prototype.hasClass = function(className) {
	var match = true;
	
	this.each(function(element) {
		if (!element.className.match(new RegExp("(\\s|^)" + className + "(\\s|$)"))) {
			match = false;
		}
	});
	
	return match;
};

/**
 * Add a class name to each element.
 * 
 * @param className			The class name to add.
 * 
 * @return					This wrapper.
 **/
_$.prototype.addClass = function(className) {
	this.each(function(element) {
		// Check if the class isn't already added to the element:
		if (!$(element).hasClass(className)) {
			element.className += " " + className;
		}
	});
	
	return this;
};

/**
 * Remove a class name from each element.
 * 
 * @param className			The class name to remove.
 * 
 * @return					This wrapper.
 **/
_$.prototype.removeClass = function(className) {
	this.each(function(element) {
		// Check if element contains the given class:
		if ($(element).hasClass(className)) {
			var reg = new RegExp("(\\s|^)" + className + "(\\s|$)");
			
			element.className = element.className.replace(reg, " ");
		}
	});
	
	return this;
};

/**
 * Add an array of CSS properties with values to the elements (p.h. "$('id').css({left: '300px', top: '100px', backgroundColor: '#ff00ff'});
 * 
 * @param properties		An array of properties to set to the elements.
 * 
 * @return					This wrapper.
 **/
_$.prototype.css = function(properties) {
	var that = this;
	
	for (var property in properties) {
		that.setStyle(property, properties[property]);
	}
	
	return this;
};

/**
 * Set the font color of the elements.
 * 
 * @param color				The color to set.
 * 
 * @return					This wrapper.
 **/
_$.prototype.setColor = function(color) {
	return this.setStyle("color", color);
};

/**
 * Set the background color of the elements.
 * 
 * @param color				The color to set.
 * 
 * @return					This wrapper.
 **/
_$.prototype.setBColor = function(color) {
	return this.setStyle("backgroundColor", color);
};

/**
 * Set the visibility of each element to show. When an internal "show"-function exists, that one will be called instead.
 * 
 * @return					This wrapper.
 **/
_$.prototype.show = function() {
	this.each(function(element) {
		if (typeof element.show != "undefined") {
			element.show();
		} else {
			element.style["display"] = "block";
			element.style["visibility"] = "visible";
		}
	});
	
	return this;
};

/**
 * Set the visibility of each element to hide. When an internal "hide"-function exists, that one will be called instead.
 * 
 * @return					This wrapper.
 **/
_$.prototype.hide = function() {
	this.each(function(element) {
		if (typeof element.hide != "undefined") {
			element.hide();
		} else {
			element.style["display"] = "none";
			element.style["visibility"] = "hidden";
		}
	});
	
	return this;
};

/**
 * Toggle the visibility of each element.
 * 
 * @return					This wrapper.
 **/
_$.prototype.toggle = function() {
	this.each(function(element) {
		// Detect the current visibility state:
		if (element.style["display"] == "" && element.style["visibility"] == "") {
			$(element).hide();
		} else if (element.style["display"] == "none" || element.style["visibility"] == "hidden") {
			$(element).show();
		} else if (element.style["display"] == "block" || element.style["visibility"] == "visible") {
			$(element).hide();
		}
	});
	
	return this;
};

/**
 * Add a handler to the elements.
 * 
 * @param type				The event type (mouseover, mouseout, mousedown, blur, submit, ...).
 * @param func				The handler to call for each event.
 * 
 * @return					This wrapper.
 **/
_$.prototype.on = function(type, func) {
	var listen = function(element) {
		if (window.addEventListener) {
			element.addEventListener(type, func, false);
		} else if (window.attachEvent) {
			element.attachEvent("on" + type, function() {
				func.call(element, window.event);
			});
		}
	};
	
	this.each(function(element) {
		listen(element);
	});
	
	return this;
};

/**
 * Alternative call to "on".
 * 
 * @param type				The event type (mouseover, mouseout, mousedown, blur, submit, ...).
 * @param func				The handler to call for each event.
 * 
 * @return					This wrapper.
 **/
_$.prototype.addEvent = function(type, func) {
	return this.on(type, func);
};

/**
 * Do an AJAX request for the elements.
 * 
 * @param url				The URL of the document to parse into the elements.
 * @param callBack			The callback function to process after the request has finished.
 * @param onLoad			Called before the actual request.
 * @param onSuccess			Called when the request was successfull.
 * @param onFailure			Called when the request has failed.
 * @param async				If the Ajax request should be asynchronous or not.
 * 
 * @return					This wrapper.
 **/
_$.prototype.request = function(url, callBack, onLoad, onSuccess, onFailure, async) {
	// Request all:
	this.each(function(element) {
		// Create the Ajax object:
		element.ajax = ajax = new Ajax(element, url, callBack, onLoad, onSuccess, onFailure, async);
		
		// Store the Ajax object in it's parent node:
		//element.ajax = ajax;
		
		// Do the request:
		element.ajax.request();
	});
	
	return this;
};

_$.prototype.refresh = function() {
	// Request all:
	this.each(function(element) {
		//if (element.parentNode.ajax) {
			element.ajax.request();
		//} else {
		//	$(element).alert("Cannot refresh element '" + element.id + "': No Ajax object stored in the element's parent node!");
		//}
	});
	
	return this;
};

/**
 * Set the opacity of the elements.
 * 
 * @param percentage		The opacity percentage.
 * 
 * @return					This wrapper.
 **/
_$.prototype.setOpacity = function(percentage) {
	// Set minimum to 0 %:
	if (percentage < 0) {
		percentage = 0;
	}
	
	// Set maximum to 100 %:
	if (percentage > 100) {
		percentage = 100;
	}
	
	// Set all:
	this.each(function(element) {
		// Set the transparency:
		if (percentage == 100) {
			element.style.opacity = "1.0";
		} else {
			// Add a 0 in front of the percentage when < 10:
			if (percentage < 10) {
				percentage = "0" + percentage;
			}
			
			element.style.opacity = "0." + percentage;
		}
		
		// Set the IE transparency:
		if (element.style) {
			element.style["filter"] = "alpha(opacity=" + percentage + ");";
		}
	});
	
	return this;
};

/**
 * Set the transparency of the elements.
 * 
 * @param percentage		The transparency percentage.
 * 
 * @return					This wrapper.
 **/
_$.prototype.setTransparency = function(percentage) {
	return this.setOpacity(100 - percentage);
};

/**
 * Stop all animations.
 * 
 * @return					This wrapper.
 **/
_$.prototype.stopTweens = function() {
	// Stop all tweens:
	for (var i = 0; i < this.tweens.length; i++) {
		this.tweens[i].stop();
	}
	
	// Clear all tweens:
	this.tweens.clear();
	
	return this;
};

/**
 * Move element(s) horizontally from 1 position to another.
 * 
 * @param from				The X-position to move from.
 * @param to				The X-position to move to.
 * @param duration			The duration in milliseconds for the move (total time it will take to move).
 * @param type				The animation type to use.
 * @param fps				The number of frames per second.
 * @param suffix			The suffix to use, default is in pixels.
 * 
 * @return					This wrapper.
 **/
_$.prototype.moveX = function(fromX, toX, duration, type, fps, suffix) {
	// Return if fromX or toX is not set:
	if (fromX == null || toX == null) {
		return this;
	// Ensure integers:
	} else {
		fromX = parseInt(fromX);
		toX = parseInt(toX);
		fps = parseInt(fps);
	}
	
	// Set time to 3s if null:
	if (duration == null) {
		duration = 3000;
	} else {
		duration = parseInt(duration);
	}
	
	// Set suffix to pixels if null:
	if (suffix == null) {
		suffix = "px";
	}
	
	// Move all:
	this.each(function(element) {
		// Create the animation and store it:
		var tween = new Tween(element, "left", fromX, toX, suffix, duration, type, fps);
		this.tweens.add(tween);
		
		// Start the animation:
		tween.start();
	});
	
	return this;
};

/**
 * Move this element vertically from 1 position to another.
 * 
 * @param from				The Y-position to move from.
 * @param to				The Y-position to move to.
 * @param duration			The duration in milliseconds for the move (total time it will take to move).
 * @param type				The animation type to use.
 * @param fps				The number of frames per second.
 * @param suffix			The suffix to use, default is in pixels.
 * 
 * @return					This wrapper.
 **/
_$.prototype.moveY = function(fromY, toY, duration, type, fps, suffix) {
	// Return if fromY or toY is not set:
	if (fromY == null || toY == null) {
		return this;
	// Ensure integers:
	} else {
		fromY = parseInt(fromY);
		toY = parseInt(toY);
		fps = parseInt(fps);
	}
	
	// Set time to 3s if null:
	if (duration == null) {
		duration = 3000;
	} else {
		duration = parseInt(duration);
	}
	
	// Set suffix to pixels if null:
	if (suffix == null) {
		suffix = "px";
	}
	
	// Move all:
	this.each(function(element) {
		// Create the animation and store it:
		var tween = new Tween(element, "top", fromY, toY, suffix, duration, type, fps);
		this.tweens.add(tween);
		
		// Start the animation:
		tween.start();
	});
	
	return this;
};

/**
 * Move this element from 1 position to another.
 * 
 * @param fromX				The X-position to move from.
 * @param fromY				The Y-position to move from.
 * @param toX				The X-position to move to.
 * @param toY				The Y-position to move to.
 * @param duration			The duration in milliseconds for the move (total time it will take to move).
 * @param type				The animation type to use.
 * @param fps				The number of frames per second.
 * @param suffix			The suffix to use, default is in pixels.
 * 
 * @return					This wrapper.
 **/
_$.prototype.move = function(fromX, fromY, toX, toY, duration, type, fps, suffix) {
	this.moveX(fromX, toX, duration, type, fps, suffix);
	this.moveY(fromY, toY, duration, type, fps, suffix);
	
	return this;
};

/**
 * Move this element from the current position to another.
 * 
 * @param toX				The X-position to move to.
 * @param toY				The Y-position to move to.
 * @param duration			The duration in milliseconds for the move (total time it will take to move).
 * @param type				The animation type to use.
 * @param fps				The number of frames per second.
 * 
 * @return					This wrapper.
 **/
_$.prototype.moveTo = function(toX, toY, duration, type, fps) {
	this.each(function(element) {
		// Retrieve the current position:
		var fromX = parseInt($(element).getStyle("left").replace("px", "").replace("%", ""));
		var fromY = parseInt($(element).getStyle("top").replace("px", "").replace("%", ""));
		
		// Retrieve the used suffixes:
		var suffixX = element.style["left"].contains("%") ? "%" : "px";
		var suffixY = element.style["top"].contains("%") ? "%" : "px";
		
		// Move this element:
		$(element).moveX(fromX, toX, duration, type, fps, suffixX);
		$(element).moveY(fromY, toY, duration, type, fps, suffixY);
	});
	
	return this;
};

/**
 * Fade an element from one transparency level to another.
 * 
 * @param from				The initiate transparency level.
 * @param to				The end transparency level.
 * @param duration			The duration in milliseconds for the fading (total time it will take to fade).
 * @param type				The animation type to use for fading.
 * @param fps				The number of frames per second.
 * 
 * @return					This wrapper.
 **/
_$.prototype.fade = function(from, to, duration, type, fps) {
	// Set from to 0 if null:
	if (from == null) {
		from = 0;
	} else {
		from = parseInt(from);
	}
	
	// Set to to 100 if null:
	if (to == null) {
		to = 100;
	} else {
		to = parseInt(to);
	}
	
	// Set duration to 3s if null:
	if (duration == null) {
		duration = 3000;
	} else {
		duration = parseInt(duration);
	}
	
	// Set the animation type if null:
	if (type == null) {
		type = Tween.linear;
	}
	
	// Ensure the FPS is an integer:
	fps = parseInt(fps);
	
	// Fade all:
	this.each(function(element) {
		// Create the animation and store it:
		var tween = new Tween(element, "transparency", from, to, null, duration, type, fps);
		this.tweens.add(tween);
		
		// Start the animation:
		tween.start();
	});
	
	return this;
};

/**
 * Fade an element to a transparency level.
 * 
 * @param to				The end transparency level.
 * @param duration			The duration in milliseconds for the fading (total time it will take to fade).
 * @param type				The animation type to use for fading.
 * @param fps				The number of frames per second.
 * 
 * @return					This wrapper.
 **/
_$.prototype.fadeTo = function(to, duration, type, fps) {
	return this.alert("function 'fadeTo' not yet implemented!");
};

/**
 * Fade an element from a color to a color.
 * 
 * @param from				The original color.
 * @param to				The destination color.
 * @param duration			The duration in milliseconds for the fading (total time it will take to fade).
 * @param type				The animation type to use for fading.
 * @param fps				The number of frames per second.
 * 
 * @return					This wrapper.
 **/
_$.prototype.colorFade = function(from, to, duration, type, fps) {
	// Remove the #-sign when exists:
	from = from.replace("#", "");
	to = to.replace("#", "");
	
	// Set duration to 3s if null:
	if (duration == null) {
		duration = 3000;
	} else {
		duration = parseInt(duration);
	}
	
	// Set the animation type if null:
	if (type == null) {
		type = Tween.linear;
	}
	
	// Ensure the FPS is an integer:
	fps = parseInt(fps);
	
	// Fade all:
	this.each(function(element) {
		// Create the animation and store it:
		var tween = new Tween(element, "backgroundColor", from, to, null, duration, type, fps);
		this.tweens.add(tween);
		
		// Start the animation:
		tween.start();
	});
	
	return this;
};

/**
 * Fade an elements background color to a new color.
 * 
 * @param to				The destination color.
 * @param duration			The duration in milliseconds for the fading (total time it will take to fade).
 * @param type				The animation type to use for fading.
 * @param fps				The number of frames per second.
 * 
 * @return					This wrapper.
 **/
_$.prototype.colorFadeTo = function(to, duration, type, fps) {
	this.each(function(element) {
		// Retrieve the current background color:
		var from = element.style["backgroundColor"];
		
		// Change the from value if necessary:
		if (from == "") {
			from = "#ffffff";
		// When using Chrome / FireFox the backgroundColor is in string format (p.h. "rgb(32, 23, 133)") and should be converted:
		} else if (navigator.appVersion.indexOf("MSIE") == -1) {
			from = rgb2h(from);
		}
		
		$(element).colorFade(from, to, duration, type, fps);
	});
	
	return this;
};

/**
 * Resize an element to a certain size from a certain size.
 * 
 * @param fromWidth			The initial width of the element.
 * @param toWidth			The ending width of the element.
 * @param duration			The duration in milliseconds for resizing (total time the resize takes).
 * @param type				The animation type to use for resizing.
 * @param fps				The number of frames per second.
 * @param suffix			The suffix to use, default is in pixels.
 * 
 * @return					This wrapper.
 **/
_$.prototype.resizeWidth = function(fromWidth, toWidth, duration, type, fps, suffix) {
	// Ensure size is integer:
	fromWidth = parseInt(fromWidth);
	toWidth = parseInt(toWidth);
	
	// Set duration to 3s if null:
	if (duration == null) {
		duration = 3000;
	} else {
		duration = parseInt(duration);
	}
	
	// Set the animation type if null:
	if (type == null) {
		type = Tween.linear;
	}
	
	// Ensure the FPS is an integer:
	fps = parseInt(fps);
	
	// Set suffix to pixels if null:
	if (suffix == null) {
		suffix = "px";
	}
	
	// Fade all:
	this.each(function(element) {
		// Create the animation and store it:
		var tween = new Tween(element, "width", fromWidth, toWidth, suffix, duration, type, fps);
		this.tweens.add(tween);
		
		// Start the animation:
		tween.start();
	});
	
	return this;
};

/**
 * Resize an element to a certain size from a certain size.
 * 
 * @param fromHeight		The initial height of the element.
 * @param toHeight			The ending height of the element.
 * @param duration			The duration in milliseconds for resizing (total time the resize takes).
 * @param type				The animation type to use for resizing.
 * @param fps				The number of frames per second.
 * @param suffix			The suffix to use, default is in pixels.
 * 
 * @return					This wrapper.
 **/
_$.prototype.resizeHeight = function(fromHeight, toHeight, duration, type, fps, suffix) {
	// Ensure size is integer:
	fromHeight = parseInt(fromHeight);
	toHeight = parseInt(toHeight);
	
	// Set duration to 3s if null:
	if (duration == null) {
		duration = 3000;
	} else {
		duration = parseInt(duration);
	}
	
	// Set the animation type if null:
	if (type == null) {
		type = Tween.linear;
	}
	
	// Ensure the FPS is an integer:
	fps = parseInt(fps);
	
	// Set suffix to pixels if null:
	if (suffix == null) {
		suffix = "px";
	}
	
	// Fade all:
	this.each(function(element) {
		// Create the animation and store it:
		var tween = new Tween(element, "height", fromHeight, toHeight, suffix, duration, type, fps);
		this.tweens.add(tween);
		
		// Start the animation:
		tween.start();
	});
	
	return this;
};

/**
 * Resize an element to a certain size from a certain size.
 * 
 * @param fromWidth			The initial width of the element.
 * @param fromHeight		The initial height of the element.
 * @param toWidth			The ending width of the element.
 * @param toHeight			The ending height of the element.
 * @param duration			The duration in milliseconds for resizing (total time the resize takes).
 * @param type				The animation type to use for resizing.
 * @param fps				The number of frames per second.
 * @param suffix			The suffix to use, default is in pixels.
 * 
 * @return					This wrapper.
 **/
_$.prototype.resize = function(fromWidth, fromHeight, toWidth, toHeight, duration, type, fps, suffix) {
	this.resizeWidth(fromWidth, toWidth, duration, type, fps, suffix);
	this.resizeHeight(fromHeight, toHeight, duration, type, fps, suffix);
	
	return this;
};

/**
 * Resize an element to a certain size.
 * 
 * @param toWidth			The ending width of the element.
 * @param toHeight			The ending height of the element.
 * @param duration			The duration in milliseconds for resizing (total time the resize takes).
 * @param type				The animation type to use for resizing.
 * @param fps				The number of frames per second.
 * 
 * @return					This wrapper.
 **/
_$.prototype.resizeTo = function(toWidth, toHeight, duration, type, fps) {
	this.each(function(element) {
		// Retrieve the current size:
		var fromWidth = parseInt($(element).getStyle("width").replace("px", "").replace("%", ""));
		var fromHeight = parseInt($(element).getStyle("height").replace("px", "").replace("%", ""));
		
		// Retrieve the used suffixes:
		var suffixWidth = $(element).getStyle("width").contains("%") ? "%" : "px";
		var suffixHeight = $(element).getStyle("height").contains("%") ? "%" : "px";
		
		// Resize this element:
		$(element).resizeWidth(fromWidth, toWidth, duration, type, fps, suffixWidth);
		$(element).resizeHeight(fromHeight, toHeight, duration, type, fps, suffixHeight);
	});
	
	return this;
};

/**
 * Returns the tag values of all the tags below this element and submits them to the given URL in an AJAX request.
 * 
 * @param url				The URL of the document to parse into this element.
 * @param form				The form which holds the variables to send with the url.
 * 
 * @return					This wrapper.
 **/
_$.prototype.submit = function(url, form, callBack, onLoad, onSuccess, onFailure, async) {
	// Define the default form:
	if (form == null) {
		form = this.elements[0];
	}
	
	// Send the request:
	url += url.contains("?") ? "&" : "?";
	return this.request(url + GlobalKit.getVars(form), callBack, onLoad, onSuccess, onFailure, async);
};

/**
 * Load a widget into this element.
 * 
 * @param widgetName		The name of the widget to load.
 * @param parameters		The widget parameters.
 * @param callBack			The callBack function.
 * @param onLoad			The handler that handles stuff before the widget request.
 * @param onSuccess			The handler that handles stuff after the widget request.
 * @param onFailure			The handler that handles stuff when a widget request failes.
 * 
 * @return					This wrapper.
 **/
_$.prototype.loadWidget = function(widgetName, parameters, callBack, onLoad, onSuccess, onFailure) {
	this.each(function(element) {
		Widget.load(element, widgetName, parameters, callBack, onLoad, onSuccess, onFailure);
	});
	
	return this;
};

/**
 * Make element(s) draggable.
 * 
 * @return					This wrapper.
 **/
_$.prototype.makeDraggable = function(dockables) {
	return this.alert("function 'makeDraggable' not yet implemented!");
};

/**
 * Make element(s) dockable. You can drop draggable elements into these.
 * 
 * @return					This wrapper.
 **/
_$.prototype.makeDockable = function(draggables) {
	return this.alert("function 'makeDockable' not yet implemented!");
};

/**
 * Attach a menu to this element.
 * 
 * @param menu				The menu to attach.
 * 
 * @return					This wrapper.
 **/
_$.prototype.attachMenu = function(menu) {
	return this.alert("function 'attachMenu' not yet implemented!");
};

/**
 * Add chainability to the given elements. The given arguments may be arrays, elements, ID's or a mix of them.
 * 
 * @param els			The element to make chainable.
 * @param ...			Additional elements, element ID('s) and array('s) to make chainable.
 * 
 * @return				The chainable element(s).
 **/
window.$ = function() {
	return new _$(arguments);
};

/**
 * Retrieve the element(s) by name(s).
 * 
 * @param ...			The name(s) of the element(s) to retrieve.
 * 
 * @return				The chainable element(s).
 **/
window.$$ = function() {
	var elements = [];
	
	// Get all elements by name:
	for (var i = 0; i < arguments.length; i++) {
		var name = arguments[i];
		
		elements.merge(document.getElementsByName(name));
	}
	
	return elements;
};

/**
 * Retrieve the element(s) by class name(s).
 * 
 * @param ...			The class name(s) of the element(s) to retrieve.
 * 
 * @return				The chainable element(s).
 **/
window.$$$ = function() {
	var elements = [];
	
	// Get all elements by class name:
	for (var i = 0; i < arguments.length; i++) {
		var className = arguments[i];
		
		elements.merge(document.getElementsByClassName(className));
	}
	
	return elements;
};
