Ext.namespace('Ext.tagit');


/**
 * Ext extension: Editable elements.
 * 
 * Original code from Prototype version by MN 2007-12-14. Some of that code was
 * based on ProtoTip 1.1.0 by by Nick Stakenburg - http://www.nickstakenburg.com
 *
 * @class Ext.tagit.Editable
 * @version    1.0
 * @author     Mattias Nilsson (mrorigo AT g mail DOT com)
 * @author     Nick Stakenburg
 * @copyright  Copyright (c) 2009
 * @license    Creative Commons Attribution 3.0 License - http://creativecommons.org/licenses/by/3.0/
 * @link       http://origo-origo.blogspot.com/
 * @since      2009-08-15
 *
 * Donations for my food production facility is greatly appreciated!
 */

Ext.tagit.Editable = function(config) {
    Ext.apply(this, config);

    this.addEvents("activate",       // return false to disallow activate
		   "deactivate",     // return false to disallow deactivate
		   "editorcreated",  // Use to fill select type editables
		   "abort",          // return false to disallow abort
		   "update");        // return false to disallow update, true to allow, or (modified) string to update with

    Ext.tagit.Editable.superclass.constructor.call(this);
    
    return this.init();
}

Ext.extend(Ext.tagit.Editable, Ext.util.Observable, {
    showOn: 'click',
    hideOn: 'blur',                      // 'blur'=tab or enter key, 'mouseout' maybe, or 'click' or 'change'?
    type: 'input',			 // 'input'  'select' or 'textarea'
    maxlength: false,

    emptyValue: "(click to edit)",       // Or any other text to consider as empty initial value
    trigger: this.element,		 // or other element to function as the trigger for editing "element"
    hideTrigger: true,                   // To hide the trigger during editing
    abortOnEsc: true,			 // or false to disallow abort by pressing ESC
    className: 'editable_default',       // css class of editor element
    animate: true,                       // set to true to animate hide/show of editor/element

    init : function() {
	this.element = Ext.get(this.element);
	if(!this.trigger)
	    this.trigger = this.element;
	else
	    this.trigger = Ext.get(this.trigger);

	// Store original content
	this.orig_content = this.element.dom.innerHTML;

	// Check if it's an "empty" value
	if(this.emptyValue && this.orig_content == this.emptyValue)
	    this.orig_content = "";

	// Create the editor
	this.createEditor();

	// Setup listeners for trigger and editor
	this.trigger.on(this.showOn, this.activate.createDelegate(this));
	this.editor.on(this.hideOn,  this.deactivate.createDelegate(this))
	    .on("keydown",    this.keyDown.createDelegate(this));
	return this;
    },

    activate : function() {
	if(this.fireEvent("activate", this) === false)
	    return;

	// Size could have changed, and position too..
	this.editor.setStyle({width:  this.element.getWidth(),
		              height: this.element.getHeight(),
		              left:   this.element.getLeft(),
		              top:    this.element.getTop()
		            }).show(this.animate).focus();

	// Hide/show/focus
	this.element.hide(this.animate);
	if(this.hideTrigger && this.element != this.trigger)
	    this.trigger.hide(this.animate);
	this.aborted = false;
    },

    deactivate : function() {
	if(this.fireEvent("deactivate", this) === false) {
	    this.aborted = false;
	    return;
	}

	var new_content = this.editor.getValue();
	if(this.type == "select")
	    value = this.editor.options[this.editor.selectedIndex].value;

	if(!this.aborted) {
	    var content2 = this.fireEvent("update", this, new_content); // returns true if no handler
	    if(content2 === false)  // Handler can return false do disallow update (editor stays on!)
		return;
	    else if(content2 !== true && content2 != new_content)
		new_content = content2;

	    if(this.emptyValue && (new_content == this.emptyValue || new_content == ""))
		new_content = this.emptyValue;
	    
	    this.orig_content = new_content; // We now have new content =)
	    this.element.dom.innerHTML = new_content;
	}

	this.element.show(this.animate);
	this.editor.hide(this.animate);
	if(this.hideTrigger && this.element != this.trigger)
	    this.trigger.show(this.animate);
    },

    keyDown : function(e) {
	switch(e.getKey()) {
	case 27: // ESC-key = abort
	    e.stopEvent(); // We're handling it!
	    this.aborted = true; // So it can be seen in event hook
	    if(this.fireEvent("abort", this) === false) {  // If event firing returns false ..
		this.aborted = false;  // .. we don't allow abort
		return;
	    }
	    this.deactivate();  // aborted by Esc-key..
	    break;
	case 13: // Enter
	    if (e.altKey || e.ctrlKey || e.shiftKey)
		return;
	    if(this.type == "textarea")
		return; // No save on enter for textareas (atleast for now)
	    if(this.hideOn == "enter" || this.hideOn == "blur") {
		e.stopEvent(); // We're handling it!
		this.deactivate();
	    }
	    break;
	default:
	    break;
	}
    },

    createEditor : function() {
	var zIndex = this.element.getStyle('zIndex') ? this.element.getStyle('zIndex')+1 : 1000;

	this.editor = new Ext.Element(document.createElement( this.type=="text"?"input":this.type ));

	switch(this.type) {
	case 'input':
	case 'text':
	    if(this.maxlength)
		this.editor.dom.setAttribute('maxlength', this.maxlength);
	    this.editor.dom.setAttribute('value', this.orig_content);
	    break;
	case 'textarea':
	    this.editor.dom.innerHTML = this.orig_content;
	    break;
	}

	this.editor.dom.setAttribute('class', this.className);
	this.editor.dom.setAttribute('style', 'display: none; position: absolute; zIndex: '+zIndex+';');

	// Append editor  to end of document
	document.body.appendChild(this.editor.dom);

	this.fireEvent("editorcreated", this, this.editor);
    }
});
