/**
 * EGroupware eTemplate2 - JS Widget base class
 *
 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
 * @package etemplate
 * @subpackage api
 * @link http://www.egroupware.org
 * @author Andreas Stöckel
 * @copyright Stylite 2011
 * @version $Id$
 */

"use strict";

/*egw:uses
	jquery.jquery;
	lib/tooltip;
	et2_core_DOMWidget;
*/

/**
 * Class which manages the DOM node itself. The simpleWidget class is derrived
 * from et2_DOMWidget and implements the getDOMNode function. A setDOMNode
 * function is provided, which attatches the given node to the DOM if possible.
 *
 * @augments et2_DOMWidget
 */
var et2_baseWidget = et2_DOMWidget.extend(et2_IAligned,
{
	attributes: {
		"statustext": {
			"name": "Tooltip",
			"type": "string",
			"description": "Tooltip which is shown for this element",
			"translate": true
		},
		"align": {
			"name": "Align",
			"type": "string",
			"default": "left",
			"description": "Position of this element in the parent hbox"
		},
		"onclick": {
			"name": "onclick",
			"type": "js",
			"default": et2_no_init,
			"description": "JS code which is executed when the element is clicked."
		}
	},

	/**
	 * Constructor
	 *
	 * @memberOf et2BaseWidget
	 */
	init: function() {
		this.align = "left";

		this._super.apply(this, arguments);

		this.node = null;
		this.statustext = "";
		this._messageDiv = null;
		this._tooltipElem = null;
	},

	destroy: function() {
		this._super.apply(this, arguments);

		this.node = null;
		this._messageDiv = null;
	},

	/**
	 * The setMessage function can be used to attach a small message box to the
	 * widget. This is e.g. used to display validation errors or success messages
	 *
	 * @param _text is the text which should be displayed as a message
	 * @param _type is an css class which is attached to the message box.
	 * 	Currently available are "hint", "success" and "validation_error", defaults
	 * 	to "hint"
	 * @param _floating if true, the object will be in one row with the element,
	 * 	defaults to true
	 * @param _prepend if set, the message is displayed behind the widget node
	 * 	instead of before. Defaults to false.
	 */
	showMessage: function(_text, _type, _floating, _prepend) {

		// Preset the parameters
		if (typeof _type == "undefined")
		{
			_type = "hint";
		}

		if (typeof _floating == "undefined")
		{
			_floating = true;
		}

		if (typeof _prepend == "undefined")
		{
			_prepend = false;
		}

		var surr = this.getSurroundings();

		// Remove the message div from the surroundings before creating a new
		// one
		this.hideMessage(false, true);

		// Create the message div and add it to the "surroundings" manager
		this._messageDiv = $j(document.createElement("div"))
			.addClass("message")
			.addClass(_type)
			.addClass(_floating ? "floating" : "")
			.text(_text.valueOf() + "");

		// Decide whether to prepend or append the div
		if (_prepend)
		{
			surr.prependDOMNode(this._messageDiv[0]);
		}
		else
		{
			surr.appendDOMNode(this._messageDiv[0]);
		}

		surr.update();
	},

	/**
	 * The hideMessage function can be used to hide a previously shown message.
	 *
	 * @param _fade if true, the message div will fade out, otherwise the message
	 * 	div is removed immediately. Defaults to true.
	 * @param _noUpdate is used internally to prevent an update of the surroundings
	 * 	manager.
	 */
	hideMessage: function(_fade, _noUpdate) {
		if (typeof _fade == "undefined")
		{
			_fade = true;
		}

		if (typeof _noUpdate == "undefined")
		{
			_noUpdate = false;
		}

		// Remove the message from the surroundings manager and remove the
		// reference to it
		if (this._messageDiv != null)
		{
			var surr = this.getSurroundings();
			var self = this;
			var messageDiv = this._messageDiv;
			self._messageDiv = null;

			var _done = function() {
				surr.removeDOMNode(messageDiv[0]);

				// Update the surroundings manager
				if (!_noUpdate)
				{
					surr.update();
				}
			};

			// Either fade out or directly call the function which removes the div
			if (_fade)
			{
				messageDiv.fadeOut("fast", _done);
			}
			else
			{
				_done();
			}
		}
	},

	detachFromDOM: function() {
		// Detach this node from the tooltip node
		if (this._tooltipElem)
		{
			this.egw().tooltipUnbind(this._tooltipElem);
			this._tooltipElem = null;
		}

		// Remove the binding to the click handler
		if (this.node)
		{
			$j(this.node).unbind("click.et2_baseWidget");
		}

		this._super.apply(this, arguments);
	},

	attachToDOM: function() {
		this._super.apply(this, arguments);

		// Add the binding for the click handler
		if (this.node)
		{
			$j(this.node).bind("click.et2_baseWidget", this, function(e) {
				return e.data.click.call(e.data, e, this);
			});
			if (typeof this.onclick == 'function') $j(this.node).addClass('et2_clickable');
		}

		// Update the statustext
		this.set_statustext(this.statustext);
	},

	setDOMNode: function(_node) {
		if (_node != this.node)
		{
			// Deatch the old node from the DOM
			this.detachFromDOM();

			// Set the new DOM-Node
			this.node = _node;

			// Attatch the DOM-Node to the tree
			return this.attachToDOM();
		}

		return false;
	},

	getDOMNode: function() {
		return this.node;
	},

	getTooltipElement: function() {
		return this.getDOMNode(this);
	},

	/**
	 * Click handler calling custom handler set via onclick attribute to this.onclick
	 *
	 * @param _ev
	 * @returns
	 */
	click: function(_ev) {
		if(typeof this.onclick == 'function')
		{
			// Make sure function gets a reference to the widget, splice it in as 2. argument if not
			var args = Array.prototype.slice.call(arguments);
			if(args.indexOf(this) == -1) args.splice(1, 0, this);

			return this.onclick.apply(this, args);
		}

		return true;
	},

	set_statustext: function(_value) {
		// Don't execute the code below, if no tooltip will be attached/detached
		if (_value == "" && !this._tooltipElem)
		{
			return;
		}

		this.statustext = _value;

		//Get the domnode the tooltip should be attached to
		var elem = $j(this.getTooltipElement());

		if (elem)
		{
			//If a tooltip is already attached to the element, remove it first
			if (this._tooltipElem)
			{
				this.egw().tooltipUnbind(this._tooltipElem);
				this._tooltipElem = null;
			}

			if (_value && _value != '')
			{
				this.egw().tooltipBind(elem, _value);
				this._tooltipElem = elem;
			}
		}
	},

	set_align: function(_value) {
		this.align = _value;
	},

	get_align: function(_value) {
		return this.align;
	}

});

/**
 * Simple container object
 *
 * @augments et2_baseWidget
 */
var et2_container = et2_baseWidget.extend(
{
	/**
	 * Constructor
	 *
	 * @memberOf et2_container
	 */
	init: function() {
		this._super.apply(this, arguments);

		this.setDOMNode(document.createElement("div"));
	},

	/**
	 * The destroy function destroys all children of the widget, removes itself
	 * from the parents children list.
	 * Overriden to not try to remove self from parent, as that's not possible.
	 */
	destroy: function() {
		// Call the destructor of all children
		for (var i = this._children.length - 1; i >= 0; i--)
		{
			this._children[i].free();
		}

		// Free the array managers if they belong to this widget
		for (var key in this._mgrs)
		{
			if (this._mgrs[key] && this._mgrs[key].owner == this)
			{
				this._mgrs[key].free();
			}
		}
	}
});

/**
 * Container object for not-yet supported widgets
 *
 * @augments et2_baseWidget
 */
var et2_placeholder = et2_baseWidget.extend([et2_IDetachedDOM],
{
	/**
	 * Constructor
	 *
	 * @memberOf et2_placeholder
	 */
	init: function() {
		this._super.apply(this, arguments);

		// The attrNodes object will hold the DOM nodes which represent the
		// values of this object
		this.attrNodes = {};

		this.visible = false;

		// Create the placeholder div
		this.placeDiv = $j(document.createElement("span"))
			.addClass("et2_placeholder");

		var headerNode = $j(document.createElement("span"))
			.text(this._type || "")
			.addClass("et2_caption")
			.appendTo(this.placeDiv);

		var attrsCntr = $j(document.createElement("span"))
			.appendTo(this.placeDiv)
			.hide();

		headerNode.click(this, function(e) {
			e.data.visible = !e.data.visible;
			if (e.data.visible)
			{
				attrsCntr.show();
			}
			else
			{
				attrsCntr.hide();
			}
		});

		for (var key in this.options)
		{
			if (typeof this.options[key] != "undefined")
			{
				if (typeof this.attrNodes[key] == "undefined")
				{
					this.attrNodes[key] = $j(document.createElement("span"))
						.addClass("et2_attr");
					attrsCntr.append(this.attrNodes[key]);
				}

				this.attrNodes[key].text(key + "=" + this.options[key]);
			}
		}

		this.setDOMNode(this.placeDiv[0]);
	},

	getDetachedAttributes: function(_attrs) {
		_attrs.push("value");
	},

	getDetachedNodes: function() {
		return [this.placeDiv[0]];
	},

	setDetachedAttributes: function(_nodes, _values) {
		this.placeDiv = jQuery(_nodes[0]);
	}
});