text: /* editor.js is part of Aloha Editor project http://aloha-editor.org
   *
   * Aloha Editor is a WYSIWYG HTML5 inline editing library and editor. 
   * Copyright (c) 2010-2012 Gentics Software GmbH, Vienna, Austria.
   * Contributors http://aloha-editor.org/contribution.php 
   * 
   * Aloha Editor is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License
   * as published by the Free Software Foundation; either version 2
   * of the License, or any later version.
   *
   * Aloha Editor is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
   * 
   * As an additional permission to the GNU GPL version 2, you may distribute
   * non-source (e.g., minimized or compacted) forms of the Aloha-Editor
   * source code without the copy of the GNU GPL normally required,
   * provided you include this license notice and a URL through which
   * recipients can access the Corresponding Source.
   */
  /**
   * @name block.editor
   * @namespace Block attribute editors
   */
  define(['jquery', 'aloha/observable', 'util/class'],
  function (jQuery, Observable, Class) {
  	"use strict";
  
  	/**
  	 * This is the base class for all editors in the sidebar. You need to extend
  	 * this class if you need to write your own editor. In most cases, however,
  	 * it is sufficent to subclass the AbstractFormElementEditor.
  	 *
  	 * @name block.editor.AbstractEditor
  	 * @class An abstract editor
  	 */
  	var AbstractEditor = Class.extend(Observable,
  	/** @lends block.editor.AbstractEditor */
  	{
  		/**
  		 * Schema of the current element
  		 *
  		 * @param {Object}
  		 * @api
  		 */
  		schema: null,
  
  		/**
  		 * @constructor
  		 */
  		_constructor: function (schema) {
  			this.schema = schema;
  		},
  
  		/**
  		 * Template method to render the editor elements. Override it
  		 * in your subclass! Needs to return the jQuery element which
  		 * should be added to the DOM
  		 *
  		 * @return {jQuery}
  		 * @api
  		 */
  		render: function () {
  			// Implement in subclass!
  		},
  
  		/**
  		 * Template method to get the current editor value
  		 *
  		 * Override it in your subclass!
  		 *
  		 * @return {String}
  		 * @api
  		 */
  		getValue: function () {
  			// Implement in subclass!
  		},
  
  		/**
  		 * Method which is called at initialization time, to set the current value.
  		 *
  		 * Override it in your subclass!
  		 *
  		 * You should not throw any change event here, as we need to break the loop "Block" -> "Editor" -> "Block"
  		 *
  		 * @param {String} value
  		 * @api
  		 */
  		setValue: function (value) {
  			// Implement in subclass!
  		},
  
  		/**
  		 * Destroy the editor elements and unbind events
  		 * @api
  		 */
  		destroy: function () {
  			// Implement in subclass!
  		},
  
  		/**
  		 * On deactivating, we still need to trigger a change event if the value has been modified.
  		 *
  		 * @private
  		 */
  		_deactivate: function () {
  			this.trigger('change', this.getValue());
  			this.destroy();
  		}
  	});
  
  	/**
  	 * This is a more specialized FormElementEditor which should be used
  	 * for form-based editors.
  	 *
  	 * @name block.editor.AbstractFormElementEditor
  	 * @class An abstract form editor with label
  	 * @extends block.editor.AbstractEditor
  	 */
  	var AbstractFormElementEditor = AbstractEditor.extend(
  	/** @lends block.editor.AbstractFormElementEditor */
  	{
  
  		/**
  		 * Input element HTML definition
  		 *
  		 * You need to override this in your subclass.
  		 *
  		 * @type String
  		 *
  		 * @api
  		 */
  		formInputElementDefinition: null,
  
  		/**
  		 * The jQuery element of the form input element.
  		 *
  		 * @type {jQuery}
  		 */
  		_$formInputElement: null,
  
  		/**
  		 * Render the label and form element
  		 *
  		 * @return {jQuery}
  		 */
  		render: function () {
  			var $wrapper = jQuery('<div class="aloha-block-editor" />');
  			var guid = GENTICS.Utils.guid();
  			var $label = this.renderLabel().attr('id', guid);
  			var $formElement = this.renderFormElement().attr('id', guid);
  
  			if (this.schema.fieldsetLabel) {
  				// When using a fieldset, the actual input element has to be
  				// placed in that instead of the wrapper element.
  				$label.append($formElement).appendTo($wrapper);
  			} else {
  				$wrapper.append($label).append($formElement);
  			}
  
  			return $wrapper;
  		},
  
  		/**
  		 * Render the label for the editor, by using the "label" property
  		 * from the schema.
  		 *
  		 * When <code>this.schema.fieldsetLabel</code> is true the
  		 * label will be rendered as a <code>legend</code> tag inside
  		 * a <code>fieldset</code>.
  		 *
  		 * @return {jQuery}
  		 */
  		renderLabel: function () {
  			var element;
  
  			if (this.schema.fieldsetLabel) {
  				element = jQuery('<fieldset>').append(jQuery('<legend>', { text: this.schema.label }));
  			} else {
  				element = jQuery('<label>', { text: this.schema.label });
  			}
  
  			return element;
  		},
  
  		/**
  		 * Render the form input element
  		 * @return {jQuery}
  		 */
  		renderFormElement: function () {
  			var that = this;
  			this._$formInputElement = jQuery(this.formInputElementDefinition);
  
  			this.afterRenderFormElement(this._$formInputElement);
  
  			this._$formInputElement.change(function () {
  				that.trigger('change', that.getValue());
  			});
  
  			return this._$formInputElement;
  		},
  
  		/**
  		 * Callback which can be implemented by subclasses to adjust the rendered
  		 * form input element
  		 *
  		 * @param {jQuery} $formElement the form element being rendered
  		 * @api
  		 */
  		afterRenderFormElement: function ($formElement) {
  
  		},
  
  		/**
  		 * @return {String}
  		 */
  		getValue: function () {
  			return this._$formInputElement.val();
  		},
  
  		/**
  		 * We do not throw any change event here, as we need to break the loop "Block" -> "Editor" -> "Block"
  		 */
  		setValue: function (value) {
  			this._$formInputElement.val(value);
  		},
  
  		/**
  		 * Cleanup and remove the input element
  		 */
  		destroy: function () {
  			this._$formInputElement.remove();
  		}
  
  	});
  
  	/**
  	 * @name block.editor.StringEditor
  	 * @class An editor for string input
  	 * @extends block.editor.AbstractFormElementEditor
  	 */
  	var StringEditor = AbstractFormElementEditor.extend(
  	/** @lends block.editor.StringEditor */
  	{
  		formInputElementDefinition: '<input type="text" />'
  	});
  
  	/**
  	 * @name block.editor.NumberEditor
  	 * @class An editor for numbers
  	 * @extends block.editor.AbstractFormElementEditor
  	 */
  	var NumberEditor = AbstractFormElementEditor.extend(
  	/** @lends block.editor.NumberEditor */
  	{
  		// TODO Range should be an option
  		formInputElementDefinition: '<input type="range" />',
  
  		afterRenderFormElement: function ($formElement) {
  			if (!this.schema.range) {
  				return;
  			}
  
  			if (this.schema.range.min) {
  				$formElement.attr('min', this.schema.range.min);
  			}
  
  			if (this.schema.range.max) {
  				$formElement.attr('max', this.schema.range.max);
  			}
  
  			if (this.schema.range.step) {
  				$formElement.attr('step', this.schema.range.step);
  			}
  		}
  	});
  
  	/**
  	 * @name block.editor.UrlEditor
  	 * @class An editor for URLs
  	 * @extends block.editor.AbstractFormElementEditor
  	 */
  	var UrlEditor = AbstractFormElementEditor.extend(
  	/** @lends block.editor.UrlEditor */
  	{
  		formInputElementDefinition: '<input type="url" />'
  	});
  
  	/**
  	 * @name block.editor.EmailEditor
  	 * @class An editor for email addresses
  	 * @extends block.editor.AbstractFormElementEditor
  	 */
  	var EmailEditor = AbstractFormElementEditor.extend(
  	/** @lends block.editor.EmailEditor */
  	{
  		formInputElementDefinition: '<input type="email" />'
  	});
  
  	/**
  	 * @name block.editor.SelectEditor
  	 * @class An editor for select fields
  	 * @extends block.editor.AbstractFormElementEditor
  	 */
  	var SelectEditor = AbstractFormElementEditor.extend(
  	/** @lends block.editor.SelectEditor */
  	{
  		formInputElementDefinition: '<select />',
  
  		afterRenderFormElement: function ($formElement) {
  			jQuery.each(this.schema.values, function () {
  				var el = this;
  				$formElement.append(jQuery('<option />').attr('value', el.key).html(el.label));
  			});
  		}
  	});
  
  	/**
  	 * @name block.editor.RadioButtonEditor
  	 * @class An editor for a radio button group
  	 * @extends block.editor.AbstractFormElementEditor
  	 */
  	var RadioButtonEditor = AbstractFormElementEditor.extend(
  	/** @lends block.editor.RadioButtonEditor */
  	{
  			formInputElementDefinition: '<ul />',
  			afterRenderFormElement: function ($formElement) {
  				var groupName = 'rb_' + GENTICS.Utils.guid();
  
  				jQuery.each(this.schema.values, function (idx, el) {
  					var inputId = groupName + '_' + el.key;
  					var $input = $('<input>', {
  						type: 'radio',
  						value: el.key,
  						name: groupName,
  						id: inputId
  					});
  					var $label = $('<label>', {
  						'for': inputId,
  						'class': 'input-label',
  						text: el.label
  					});
  
  					$('<li>').append($input).append($label).appendTo($formElement);
  				})
  			},
  			getValue: function () {
  				return this._$formInputElement.find(':checked').val();
  			},
  			setValue: function (value) {
  				this._$formInputElement.find('[value=' + value + ']').prop('checked', true);
  			}
  	});
  	/**
  	 * @name block.editor.ButtonEditor
  	 * @class An editor for buttons, executing a custom supplied callback "callback"
  	 * @extends block.editor.AbstractFormElementEditor
  	 */
  	var ButtonEditor = AbstractFormElementEditor.extend(
  	/** @lends block.editor.SelectEditor */
  	{
  		formInputElementDefinition: '<button />',
  
  		afterRenderFormElement: function ($formElement) {
  			var that = this;
  			$formElement.html(this.schema.buttonLabel);
  			$formElement.click(function () {
  				that.schema.callback();
  			})
  		}
  	});
  
  	return {
  		AbstractEditor: AbstractEditor,
  		AbstractFormElementEditor: AbstractFormElementEditor,
  		StringEditor: StringEditor,
  		NumberEditor: NumberEditor,
  		UrlEditor: UrlEditor,
  		EmailEditor: EmailEditor,
  		SelectEditor: SelectEditor,
  		RadioButtonEditor: RadioButtonEditor,
  		ButtonEditor: ButtonEditor
  	}
  });
  