Source: widgets/advCtrls/kekule.widget.valueListEditors.js

/**
 * @fileoverview
 * Implementation of a value list editor (like object inspector in Delphi).
 * @author Partridge Jiang
 */

/*
 * requires /lan/classes.js
 * requires /utils/kekule.utils.js
 * requires /utils/kekule.domUtils.js
 * requires /xbrowsers/kekule.x.js
 * requires /widgets/kekule.widget.base.js
 * requires /widgets/commonCtrls/kekule.widget.formControls.js
 */

(function(){
"use strict";

var DU = Kekule.DomUtils;
var EU = Kekule.HtmlElementUtils;
var CNS = Kekule.Widget.HtmlClassNames;

/** @ignore */
Kekule.Widget.HtmlClassNames = Object.extend(Kekule.Widget.HtmlClassNames, {
	VALUELISTEDITOR: 'K-ValueListEditor',
	VALUELISTEDITOR_ROW: 'K-ValueListEditor-Row',
	VALUELISTEDITOR_CELL: 'K-ValueListEditor-Cell',
	VALUELISTEDITOR_INDICATORCELL: 'K-ValueListEditor-IndicatorCell',
	VALUELISTEDITOR_KEYCELL: 'K-ValueListEditor-KeyCell',
	VALUELISTEDITOR_VALUECELL: 'K-ValueListEditor-ValueCell',
	VALUELISTEDITOR_CELL_CONTENT: 'K-ValueListEditor-CellContent',
	VALUELISTEDITOR_VALUECELL_TEXT: 'K-ValueListEditor-ValueCellText',
	VALUELISTEDITOR_INLINE_EDIT: 'K-ValueListEditor-InlineEdit',
	VALUELISTEDITOR_EXPANDMARK: 'K-ValueListEditor-ExpandMark',
	VALUELISTEDITOR_ACTIVE_ROW: 'K-ValueListEditor-ActiveRow'
});

/**
 * An value list editor widget (like object inspector in Delphi).
 * @class
 * @augments Kekule.Widget.BaseWidget
 *
 * @property {Hash} hash Set this property to update the whole editor with the hash's key and values.
 * @property {Bool} readOnly Whether user can edit value in editor.
 * @property {Bool} useKeyHint Whether add hint to key cell.
 * @property {Int} valueDisplayMode Value from {@link Kekule.Widget.ValueListEditor.ValueDisplayMode}.
 */
/**
 * Invoked when the active row is changed. Event param of it has field: {row} where row may be a empty value means no activeRow now.
 * @name Kekule.Widget.ValueListEditor#activeRowChange
 * @event
 */
/**
 * Invoked when the active row edit is finished and new value is saved. Event param of it has field: {row}.
 * @name Kekule.Widget.ValueListEditor#editFinish
 * @event
 */
/**
 * Invoked when the active row edit is cancelled and new value is discarded. Event param of it has field: {row}.
 * @name Kekule.Widget.ValueListEditor#editCancel
 * @event
 */
Kekule.Widget.ValueListEditor = Class.create(Kekule.Widget.BaseWidget,
/** @lends Kekule.Widget.ValueListEditor# */
{
	/** @private */
	CLASS_NAME: 'Kekule.Widget.ValueListEditor',
	/** @private */
	BINDABLE_TAG_NAMES: ['table'],
	/** @private */
	ROW_ELEM_TAG: 'tr',
	/** @private */
	CELL_ELEM_TAG: 'td',
	/** @private */
	ROW_DATA_FIELD: '__$row_data__',
	/** @private */
	ROW_EDIT_INFO_FIELD: '__$row_edit_info__',
	/** @constructs */
	initialize: function($super, parentOrElementOrDocument)
	{
		$super(parentOrElementOrDocument);
		this._inlineEdit = null;
		this._isActivitingRow = false;
	},
	/** @private */
	initProperties: function()
	{
		this.defineProp('hash', {'dataType': DataType.HASH,
			'getter': function()
			{
				return this._getTotalHash();
			},
			'setter': function(value)
			{
				this._setTotalHash(value);
				this.setPropStoreFieldValue('hash', value);
			}
		});
		this.defineProp('readOnly', {'dataType': DataType.BOOL});
		this.defineProp('useKeyHint', {'dataType': DataType.BOOL});
		this.defineProp('valueDisplayMode', {'dataType': DataType.INT,
			'enumSource': Kekule.Widget.ValueListEditor.ValueDisplayMode,
			'setter': function(value)
			{
				if (this.getValueDisplayMode() !== value)
				{
					this.setPropStoreFieldValue('valueDisplayMode', value);
					this.valueDisplayModeChanged();
				}
			}
		});
		this.defineProp('activeRow', {'dataType': DataType.OBJECT, 'serializable': false,
			'scope': Class.PropertyScope.PUBLIC,
			'setter': function(value)
			{
				var old = this.getActiveRow();
				if (old !== value)
				{
					if (old)
					{
						// save already changed value first
						this.finishEditing();
						EU.removeClass(old, CNS.VALUELISTEDITOR_ACTIVE_ROW);
						this._showValueCellText(old);
					}
					if (this._inlineEdit)
						this._inlineEdit.finalize();
					if (value)
					{
						EU.addClass(value, CNS.VALUELISTEDITOR_ACTIVE_ROW);
						if (!this.getReadOnly())  // create inline edit
						{
							this.createValueEditWidget(value);
						}
					}
					this.setPropStoreFieldValue('activeRow', value);
					this.invokeEvent('activeRowChange', {'row': value});
				}
			}
		});
	},
	/** @ignore */
	doGetWidgetClassName: function()
	{
		return CNS.VALUELISTEDITOR;
	},
	/** @ignore */
	doCreateRootElement: function(doc)
	{
		var result = doc.createElement('table');
		return result;
	},

	/** @ignore */
	doBindElement: function($super, element)
	{
		$super(element);
	},

	/**
	 * Returns root table element.
	 * @returns {HTMLElement}
	 * @private
	 */
	getRootElem: function()
	{
		return this.getElement();
	},

	/** @private */
	createRowElem: function(doc)
	{
		var result = doc.createElement(this.ROW_ELEM_TAG);
		EU.addClass(result, CNS.VALUELISTEDITOR_ROW);
		return result;
	},
	/** @private */
	createCellElem: function(doc)
	{
		var result = doc.createElement(this.CELL_ELEM_TAG);
		EU.addClass(result, CNS.VALUELISTEDITOR_CELL);
		return result;
	},
	/** @private */
	createIndicatorCellElem: function(doc)
	{
		var result = this.createCellElem(doc);
		EU.addClass(result, CNS.VALUELISTEDITOR_INDICATORCELL);
		var indicator = doc.createElement('span');
		result.appendChild(indicator);
		return result;
	},
	/** @private */
	createKeyCellElem: function(doc)
	{
		var result = this.createCellElem(doc);
		EU.addClass(result, CNS.VALUELISTEDITOR_KEYCELL);
		return result;
	},
	/** @private */
	createValueCellElem: function(doc)
	{
		var result = this.createCellElem(doc);
		EU.addClass(result, CNS.VALUELISTEDITOR_VALUECELL);
		return result;
	},
	/** @private */
	createValueEditWidget: function(row)
	{
		var result = null;
		if (row === this.getActiveRow())  // is active, inlineEdit is on self, may reuse it
			result = this._inlineEdit;
		else if (this._inlineEdit)  // finalize old first
		{
			this._inlineEdit.finalize();
			this._inlineEdit = null;
		}

		if (!result)
		{
			result = this.doCreateValueEditWidget(row);
			result.setBubbleUiEvents(true);  // IMPORTANT, make sure event not eaten by edit
			EU.addClass(result.getElement(), CNS.VALUELISTEDITOR_INLINE_EDIT);
			var cell = this.getValueCell(row);
			var contentWrapper = this.getCellContentWrapper(cell, true);
			result.appendToElem(contentWrapper);
		}
		if (result)
		{
			/*
			result.setStatic(true);
			result.setBubbleUiEvents(true);  // IMPORTANT, make sure event not eaten by edit
			//EU.addClass(result.getElement(), CNS.VALUELISTEDITOR_INLINE_EDIT);
			var cell = this.getValueCell(row);
			var contentWrapper = this.getCellContentWrapper(cell, true);
			//console.log('set active row', result.getClassName(), contentWrapper.tagName);
			result.appendToElem(contentWrapper);
			//result.appendToElem(document.body);
			//console.log(result.getElement().parentNode);
			*/

			if (result.focus)
				result.focus();
			if (result.selectAll)
				result.selectAll();

			this._inlineEdit = result;
			this._hideValueCellText(row);
		}
		return result;
	},
	/** @private */
	doCreateValueEditWidget: function(row)
	{
		var data = this.getRowData(row);
		//var value = data? data.value: '';
		var value = this.getValueCellText(row, data);

		var widgetInfo = this.getValueEditWidgetInfo(row);

		var editClass = widgetInfo.widgetClass;
		var result = new editClass(this);
		if (widgetInfo.propValues)  // init widget
		{
			result.setPropValues(widgetInfo.propValues);
		}
		result.setValue(value);
		return result;
	},
	/** @private */
	getDefaultValueEditWidgetInfo: function()
	{
		return {'widgetClass': Kekule.Widget.TextBox};
	},
	/**
	 * Returns essential information to create inline edit widget.
	 * Descendants can override this method.
	 * @param {HTMLElement} row
	 * @returns {Hash} Including {widgetClass, initialPropValues}
	 */
	getValueEditWidgetInfo: function(row)
	{
		return this.getRowEditInfo(row) || this.getDefaultValueEditWidgetInfo();
	},

	// methods to manipulate row of value list editor
	/**
	 * Returns all row elements in editor.
	 * @returns {Array}
	 */
	getRowElems: function()
	{
		if (this.getRootElem())
		{
			return DU.getDirectChildElems(this.getRootElem(), this.ROW_ELEM_TAG);
		}
		else
			return [];
	},
	/**
	 * Returns all row elements in editor. Same as getRowElems.
	 * @returns {Array}
	 */
	getRows: function()
	{
		return this.getRowElems();
	},
	/**
	 * Returns total row count of current editor.
	 * @returns {Int}
	 */
	getRowCount: function()
	{
		var rows = this.getRowElems();
		return rows? rows.length: 0;
	},
	/**
	 * Returns row element at index.
	 * @param {Int} index
	 * @return {HTMLElement}
	 */
	getRowAt: function(index)
	{
		var rows = this.getRowElems();
		return rows? rows[index]: null;
	},
	/**
	 * Returns previous row.
	 * @param {HTMLElement} row
	 * @returns {HTMLElement}
	 */
	getPrevRow: function(row)
	{
		var matchTag = this.ROW_ELEM_TAG.toLowerCase();
		var result = row.previousSibling;
		while (result && ((result.nodeType !== Node.ELEMENT_NODE)
			|| (result.tagName.toLowerCase() !== matchTag)))
		{
			result = result.previousSibling;
		}
		return result;
	},
	/**
	 * Returns next row.
	 * @param {HTMLElement} row
	 * @returns {HTMLElement}
	 */
	getNextRow: function(row)
	{
		var matchTag = this.ROW_ELEM_TAG.toLowerCase();
		var result = row.nextSibling;
		while (result && ((result.nodeType !== Node.ELEMENT_NODE)
			|| (result.tagName.toLowerCase() !== matchTag)))
		{
			result = result.nextSibling;
		}
		return result;
	},
	/**
	 * Returns key cell element of row.
	 * @param {HTMLElement} row
	 * @returns {HTMLElement}
	 */
	getKeyCell: function(row)
	{
		var cells = DU.getDirectChildElems(row, this.CELL_ELEM_TAG);
		return cells[1];
	},
	/**
	 * Text wrapper element (usually a <span>) in cell.
	 * @param {HTMLElement} cell
	 * @param {Bool} canCreate
	 * @returns {HTMLElement}
	 */
	getCellContentWrapper: function(cell, canCreate)
	{
		var children = DU.getDirectChildElems(cell);
		var result = children? children[0]: null;
		if (!result && canCreate)  // create new one
		{
			//result = this.createTextContent('', cell);
			result = cell.ownerDocument.createElement('span');
			cell.appendChild(result);
			EU.addClass(result, CNS.VALUELISTEDITOR_CELL_CONTENT);
		}
		return result;
	},
	/** @private */
	getValueCellTextWrapper: function(cell, canCreate)
	{
		var result = null;
		var wrapper = this.getCellContentWrapper(cell, canCreate);
		if (wrapper)
		{
			var children = wrapper.getElementsByTagName('span');
			result = children? children[0]: null;
			if (!result && canCreate) // create new one
			{
				result = cell.ownerDocument.createElement('span');
				wrapper.appendChild(result);
				EU.addClass(result, CNS.VALUELISTEDITOR_VALUECELL_TEXT);
			}
		}
		return result;
	},
	/** @private */
	_hideValueCellText: function(row)
	{
		var elem = this.getValueCellTextWrapper(this.getValueCell(row));
		if (elem)
			elem.style.visibility = 'hidden';
	},
	/** @private */
	_showValueCellText: function(row)
	{
		var elem = this.getValueCellTextWrapper(this.getValueCell(row));
		if (elem)
			elem.style.visibility = 'visible';
	},
	/**
	 * Returns value cell element of row.
	 * @param {HTMLElement} row
	 * @returns {HTMLElement}
	 */
	getValueCell: function(row)
	{
		var cells = DU.getDirectChildElems(row, this.CELL_ELEM_TAG);
		return cells[2];
	},
	/**
	 * Returns parent cell child element.
	 * @param {HTMLElement} element
	 * @returns {HTMLElement}
	 */
	getParentCell: function(element)
	{
		var result = DU.getNearestAncestorByTagName(element, this.CELL_ELEM_TAG, true);
		return result;
	},
	/**
	 * Returns parent row of cell (or other element).
	 * @param {HTMLElement} element
	 * @returns {HTMLElement}
	 */
	getParentRow: function(element)
	{
		var result = DU.getNearestAncestorByTagName(element, this.ROW_ELEM_TAG, true);
		return result;
	},
	/**
	 * Add a new row element based on key and value.
	 * @returns {HTMLElement}
	 * @private
	 */
	_createNewRow: function(data)
	{
		var doc = this.getDocument();
		var result = this.createRowElem(doc);
		if (result)
		{
			var indicatorCell = this.createIndicatorCellElem(doc);
			result.appendChild(indicatorCell);
			var keyCell = this.createKeyCellElem(doc);
			result.appendChild(keyCell);
			var valueCell = this.createValueCellElem(doc);
			result.appendChild(valueCell);
			if (data)
				this.setRowData(result, data);
		}
		return result;
	},
	/**
	 * Insert a row before refRowElem.
	 * @param {Hash} data
	 * @param {HTMLElement} refRowElem
	 * @returns {HTMLElement}
	 */
	insertRowBefore: function(data, refRowElem)
	{
		var row = this._createNewRow(data);
		if (row)
			this.getRootElem().insertBefore(row, refRowElem || null);
		return row;
	},
	/**
	 * Append a row at the tail of editor.
	 * @param {Hash} data
	 * @returns {HTMLElement}
	 */
	appendRow: function(data)
	{
		return this.insertRowBefore(data, null);
	},
	/**
	 * Remove a row element from editor.
	 * @param {HTMLElement} rowElem
	 */
	removeRow: function(rowElem)
	{
		if (rowElem === this.getActiveRow())
			this.setActiveRow(null);
		this.getRootElem().removeChild(rowElem);
		return this;
	},
	/**
	 * Remove a row element at index.
	 * @param {Int} index
	 */
	removeRowAt: function(index)
	{
		var row = this.getRowAt(index);
		if (row)
			this.removeRow(row);
		return this;
	},
	/**
	 * Clear all rows and content in editor.
	 */
	clear: function()
	{
		this.setActiveRow(null);
		if (this.getRootElem())
		{
			/*
			if (this.getRootElem().innerHTML)
				this.getRootElem().innerHTML = '';  // sometimes cause exceptions in IE when innerHTML is empty
			*/
			Kekule.DomUtils.clearChildContent(this.getRootElem());
		}
	},

	/**
	 * Force to update display text in key/value cell of all rows.
	 */
	updateAll: function()
	{
		var rows = this.getRowElems();
		for (var i = 0, l = rows.length; i < l; ++i)
		{
			var row = rows[i];
			var data = this.getRowData(row);
			this.setRowData(row, data);
		}
		return this;
	},

	/** @private */
	valueDisplayModeChanged: function()
	{
		this.updateAll();
	},

	/**
	 * Returns display text shown in key cell.
	 * Descendants can override this method to show different texts.
	 * @param {HTMLElement} row
	 * @param {Object} data
	 * @returns {String}
	 */
	getKeyCellText: function(row, data)
	{
		return data.title || data.key;
	},
	/**
	 * Returns hint text shown in key cell.
	 * Descendants can override this method to show different texts.
	 * @param {HTMLElement} row
	 * @param {Object} data
	 * @returns {String}
	 */
	getKeyCellHint: function(row, data)
	{
		return data.hint || this.getKeyCellText(row, data);
	},
	/**
	 * Returns display text shown in value cell.
	 * Descendants can override this method to show different texts.
	 * @param {HTMLElement} row
	 * @param {Object} data
	 * @returns {String}
	 */
	getValueCellText: function(row, data)
	{
		var mode = this.getValueDisplayMode();
		var result = (mode === VDM.JSON)?
			JSON.stringify(data.value):
			'' + data.value;
		return result;
	},
	/**
	 * After editing finished, convert value string into real data value.
	 * Descendants may override this method to do some transform from valueText to actual value.
	 * @param {String} valueText
	 * @param {HTMLElement} row
	 * @param {Object} oldData
	 */
	valueCellTextToValue: function(valueText, row, oldData)
	{
		//return valueText;
		var mode = this.getValueDisplayMode();
		return (mode === VDM.JSON)?
			JSON.parse(valueText):
			valueText;
	},
	/**
	 * After editing finished, feedback value string to data.
	 * Descendants may override this method to do some real work.
	 * @param {String} valueText
	 * @param {HTMLElement} row
	 */
	setValueCellText: function(valueText, row)
	{
		var oldData = this.getRowData(row);
		var newValue = this.valueCellTextToValue(valueText, row, oldData);
		oldData.value = newValue;
		this.setRowData(row, oldData);
		return this;
	},

	/**
	 * Returns data (key & value) associated with row.
	 * @param {HTMLElement} row
	 * @returns {Hash} A object with key/value fields.
	 */
	getRowData: function(row)
	{
		return row[this.ROW_DATA_FIELD];
	},
	/**
	 * Change key and value of a row element.
	 * @param {HTMLElement} row
	 * @param {Object} data Must has two fields: key and value.
	 *   If data.title is set, the display text in key cell will use it.
	 *   Extra field can also be included.
	 * @returns {HTMLElement}
	 */
	setRowData: function(row, data)
	{
		row[this.ROW_DATA_FIELD] = data;
		// reflect on HTML element

		this.setRowKeyCellContent(row, this.getKeyCellText(row, data), this.getKeyCellHint(row, data));
		this.setRowValueCellContent(row, this.getValueCellText(row, data));

		if (row === this.getActiveRow() && this._inlineEdit) // need to update inline edit
		{
			this.createValueEditWidget(row);
		}
	},
	/** @private */
	setRowKeyCellContent: function(row, keyText, keyHint)
	{
		var cell = this.getKeyCell(row);
		var contentWrapper = this.getCellContentWrapper(cell, true);
		contentWrapper.innerHTML = keyText;
		if (this.getUseKeyHint())
			contentWrapper.title = keyHint;
	},
	/** @private */
	setRowValueCellContent: function(row, valueText)
	{
		var cell = this.getValueCell(row);
		var textWrapper = this.getValueCellTextWrapper(cell, true);
		var text = valueText;
		if (text === '')  // avoid blank string, otherwise inline-edit will not shown properly
			textWrapper.innerHTML = '&nbsp;';
		else
			Kekule.DomUtils.setElementText(textWrapper, text);
	},
	/**
	 * Returns inline edit settings of row.
	 * @param {HTMLElement} row
	 * @returns {Hash} A object with inline edit settings.
	 */
	getRowEditInfo: function(row)
	{
		return row[this.ROW_EDIT_INFO_FIELD];
	},
	/**
	 * Set inline edit settings of row.
	 * @param {HTMLElement} row
	 * @param {Hash} info This param is a hash that may containing the following fields:
	 *   {
	 *     widgetClass: widget class need to be create during inline-editing,
	 *     propValues: A hash object to init the widget, e.g. {'items': [{'key1', 'value1'}]} for select box.
	 *   }
	 */
	setRowEditInfo: function(row, info)
	{
		row[this.ROW_EDIT_INFO_FIELD] = info;
		if (row === this.getActiveRow())
		{
			this.setActiveRow(null);
			this.setActiveRow(activeRow);  // force update the active row
		}
	},

	/**
	 * Show inline edit in row and begin editting.
	 * @param {HTMLElement} row
	 */
	editRow: function(row)
	{
		this.setActiveRow(row);
	},
	/**
	 * Finish current editing process on activeRow and save the result.
	 */
	finishEditing: function()
	{
		var row = this.getActiveRow();
		if (row && this._inlineEdit)
		{
			/*
			var oldData = this.getRowData(row);
			var newValue = this.valueCellTextToValue(this._inlineEdit.getValue(), row, oldData);
			oldData.value = newValue;
			this.setRowData(row, oldData);
			*/
			this.setValueCellText(this._inlineEdit.getValue(), row);
			this.invokeEvent('editFinish', {'row': row});
		}
		return this;
	},
	/**
	 * Cancel current editing process on activeRow and restore the old value.
	 */
	cancelEditing: function()
	{
		var row = this.getActiveRow();
		if (row && this._inlineEdit)
		{
			var oldData = this.getRowData(row);
			this.setRowData(row, oldData);
			this.invokeEvent('editCancel', {'row': row});
		}
		return this;
	},

	/**
	 * Merge all data into a hash object.
	 * @returns {Hash}
	 * @private
	 */
	_getTotalHash: function()
	{
		var result = {};
		var rows = this.getRowElems();
		for (var i = 0, l = rows.length; i < l; ++i)
		{
			var row = rows[i];
			var data = this.getRowData(row);
			if (data)
			{
				result[data.key] = data.value;
			}
		}
		return result;
	},
	/**
	 * Update whole editor with hash object that decided all the keys and values.
	 * @param {Hash} hash
	 * @private
	 */
	_setTotalHash: function(hash)
	{
		var rows = this.getRowElems();
		var oldRowCount = rows.length;
		var keys = Kekule.ObjUtils.getOwnedFieldNames(hash);
		var l = keys.length;
		for (var i = 0; i < l; ++i)
		{
			var key = keys[i];
			var value = hash[key];
			var row;
			if (i < oldRowCount)
			{
				row = this.getRowAt(i);
				this.setRowData(row, {'key': key, 'value': value});
			}
			else
				row = this.appendRow({'key': key, 'value': value});
		}
		if (oldRowCount > l)  // remove unneed rows
		{
			for (var i = oldRowCount - 1; i >= l; --i)
			{
				this.removeRow(rows[i]);
			}
		}
		var activeRow = this.getActiveRow();
		if (activeRow)
		{
			this.setActiveRow(null);
			this.setActiveRow(activeRow);  // force update the active row
		}
		return this;
	},

	// event handlers
	/*
	react_pointerdown: function(e)
	{
		if (e.getButton() === Kekule.X.Event.MouseButton.LEFT)
		{
			var target = e.getTarget();
			// get nearest row
			var row = this.getParentRow(target);
			if (row)
			{
				this._isActivitingRow = true;
				this.setActiveRow(row);
			}
		}
	},
	react_pointerup: function(e)
	{
		if (e.getButton() === Kekule.X.Event.MouseButton.LEFT)
		{
			var target = e.getTarget();
			// get nearest row
			var row = this.getParentRow(target);
			if (row)
				this.setActiveRow(row);
			this._isActivitingRow = false;
		}
	},
	react_pointermove: function(e)
	{
		var target = e.getTarget();
		// get nearest row
		if (this._isActivitingRow)
		{
			var row = this.getParentRow(target);
			if (row)
				this.setActiveRow(row);
		}
	},
	*/
	/** @ignore */
	react_click: function(e)
	{
		var target = e.getTarget();
		// get nearest row
		var row = this.getParentRow(target);
		if (row)
		{
			this.setActiveRow(row);
		}
	},
	/** @ignore */
	react_keydown: function(e)
	{
		//console.log('key down');
		var keyCode = e.getKeyCode();
		var noModifier = !(e.getAltKey() || e.getShiftKey() || e.getCtrlKey());
		if (noModifier)
		{
			var currRow = this.getActiveRow();
			if (currRow)
			{
				var row;
				if (keyCode === Kekule.X.Event.KeyCode.UP)
				{
	        row = this.getPrevRow(currRow);
				}
				else if (keyCode === Kekule.X.Event.KeyCode.DOWN)
				{
	        row = this.getNextRow(currRow);
				}
				if (row)
					this.setActiveRow(row);
			}
		}
	},
	/** @ignore */
	react_keyup: function(e)
	{
		var keyCode = e.getKeyCode();
		if (keyCode === Kekule.X.Event.KeyCode.ESC)
		{
			this.cancelEditing();
		}
		else if (keyCode === Kekule.X.Event.KeyCode.ENTER)
		{
			this.finishEditing();
		}
	},
	/** @ignore */
	react_blur: function(e)
	{
		return;
		//if (this._inlineEdit)
		{
			//if (e.getTarget() === this._inlineEdit.getElement())
			{
				//console.log('blur');
				//console.log(document.activeElement);
				this.finishEditing();
				this.setActiveRow(null);
			}
		}
	}
});

/**
 * Enumeration of value display mode for value list editor.
 * @class
 */
Kekule.Widget.ValueListEditor.ValueDisplayMode = {
	/** Value displays as simple string. */
	SIMPLE: 0,
	/** Value displays as JSON value. */
	JSON: 1
};
var VDM = Kekule.Widget.ValueListEditor.ValueDisplayMode;

})();