YUI.add('aui-editable-deprecated', function (A, NAME) {

/**
 * The Editable Utility
 *
 * @module aui-editable
 */

var Lang = A.Lang,
    LString = Lang.String,
    isFunction = Lang.isFunction,

    getClassName = A.getClassName,

    DOC = A.config.doc,

    HOVER = 'hover',
    ICON = 'icon',
    NAME = 'editable',

    CSS_EDITING = getClassName(NAME, 'editing'),
    CSS_HOVER = getClassName(NAME, HOVER),
    CSS_ICON_OK = getClassName(ICON, 'ok'),
    CSS_ICON_REMOVE = getClassName(ICON, 'remove'),

    CONTENT_BOX = 'contentBox';

/**
 * <p><img src="assets/images/aui-editable/main.png"/></p>
 *
 * A base class for Editable, providing:
 * <ul>
 *    <li>Widget Lifecycle (initializer, renderUI, bindUI, syncUI, destructor)</li>
 *    <li>Edit in place elements</li>
 * </ul>
 *
 * Quick Example:<br/>
 *
 * <pre><code>var instance = new A.Editable({
 *   node: '#editor'
 * }).render();
 * </code></pre>
 *
 * Check the list of <a href="Editable.html#configattributes">Configuration Attributes</a> available for
 * Editable.
 *
 * @param config {Object} Object literal specifying widget configuration properties.
 *
 * @class Editable
 * @constructor
 * @extends Component
 */
var Editable = A.Component.create({
    /**
     * Static property provides a string to identify the class.
     *
     * @property Editable.NAME
     * @type String
     * @static
     */
    NAME: NAME,

    /**
     * Static property used to define the default attribute
     * configuration for the Editable.
     *
     * @property Editable.ATTRS
     * @type Object
     * @static
     */
    ATTRS: {
        /**
         * <a href="ButtonItem.html">ButtonItem</a> constructor Object for the
         * cancelButton.
         *
         * @attribute cancelButton
         * @default Button constructor Object.
         * @type String
         */
        cancelButton: {
            valueFn: function() {
                var instance = this;

                return {
                    icon: CSS_ICON_REMOVE,
                    on: {
                        click: A.bind(instance.cancel, instance)
                    }
                };
            }
        },

        /**
         * Content text.
         *
         * @attribute contentText
         * @default ''
         * @type String
         */
        contentText: {
            value: '',
            setter: function(value) {
                var instance = this;

                value = Lang.trim(value);

                instance._toText(value);

                return value;
            }
        },

        /**
         * Event type to initialize the editable.
         *
         * @attribute eventType
         * @default 'click'
         * @type String
         */
        eventType: {
            value: 'click'
        },

        /**
         * Function to format the input text displayed on the input.
         *
         * @attribute formatInput
         * @default null
         * @type function
         */
        formatInput: {
            validator: isFunction,
            value: null
        },

        /**
         * Function to format the output text displayed on the input.
         *
         * @attribute formatOutput
         * @default null
         * @type function
         */
        formatOutput: {
            validator: isFunction,
            value: null
        },

        /**
         * Array with icons for the <a href="Toolbar.html">Toolbar</a>.
         *
         * @attribute icons
         * @default []
         * @type Array
         */
        icons: {
            value: []
        },

        /**
         * Type of the input used to edit the <a
         * href="Editable.html#config_node">node</a>.
         *
         * @attribute inputType
         * @default 'text'
         * @type String
         */
        inputType: {
            setter: function(value) {
                var instance = this;

                if (value != 'text' && value != 'textarea') {
                    value = A.Attribute.INVALID_VALUE;
                }

                return value;
            },
            value: 'text'
        },

        /**
         * Node to setup the editable.
         *
         * @attribute node
         * @type Node
         */
        node: {
            setter: function(value) {
                var node = A.one(value);

                if (!node) {
                    A.error('AUI.Editable: Invalid Node Given: ' + value);
                }

                return node;
            }
        },

        /**
         * Node to render the editable.
         *
         * @attribute renderTo
         * @type String | Node
         */
        renderTo: {
            value: DOC.body,
            setter: function(value) {
                var instance = this;

                var node;

                if (value == 'node') {
                    node = instance.get(value);
                }
                else {
                    node = A.one(value);
                }

                if (!node) {
                    A.error('AUI.Editable: Invalid renderTo Given: ' + value);
                }

                return node;
            }
        },

        /**
         * <a href="ButtonItem.html">ButtonItem</a> constructor Object for the
         * saveButton.
         *
         * @attribute saveButton
         * @default Button constructor Object.
         * @type String
         */
        saveButton: {
            valueFn: function() {
                var instance = this;

                return {
                    icon: CSS_ICON_OK,
                    on: {
                        click: A.bind(instance.save, instance)
                    }
                };
            }
        },

        visible: {
            value: false
        }
    },

    UI_ATTRS: ['node'],

    prototype: {
        /**
         * Construction logic executed during Editable instantiation. Lifecycle.
         *
         * @method initializer
         * @protected
         */
        initializer: function() {
            var instance = this;

            instance._uiSetNode(instance.get('node'));

            instance._createEvents();
        },

        /**
         * Create the DOM structure for the Editable. Lifecycle.
         *
         * @method renderUI
         * @protected
         */
        renderUI: function() {
            var instance = this;

            var contentBox = instance.get(CONTENT_BOX);
            var inputType = instance.get('inputType');

            var comboConfig = {};

            var icons = instance.get('icons');

            if (icons !== false) {
                var cancelButton = instance.get('cancelButton');
                var saveButton = instance.get('saveButton');

                if (cancelButton !== false) {
                    icons.push(cancelButton);
                }

                if (saveButton !== false) {
                    icons.push(saveButton);
                }

                comboConfig.icons = icons;
            }

            if (inputType != 'text') {
                A.mix(
                    comboConfig, {
                        field: {
                            autoSize: true
                        },
                        fieldWidget: A.Textarea
                    }
                );
            }

            var comboBox = new A.Combobox(comboConfig).render(contentBox);

            instance._comboBox = comboBox;

            instance.inputNode = comboBox.get('node');
        },

        /**
         * Bind the events on the Editable UI. Lifecycle.
         *
         * @method bindUI
         * @protected
         */
        bindUI: function() {
            var instance = this;

            var contentBox = instance.get(CONTENT_BOX);
            var node = instance.get('node');

            var inputNode = instance.inputNode;

            inputNode.on('keypress', instance._onKeypressEditable, instance);

            instance.after('contentTextChange', instance._syncContentText);

            contentBox.swallowEvent('click');

            A.getDoc().after('click', instance._afterFocusedChangeEditable, instance);

            A.on('windowresize', A.bind(instance._afterFocusedChangeEditable, instance));
        },

        /**
         * Sync the Editable UI. Lifecycle.
         *
         * @method syncUI
         * @protected
         */
        syncUI: function() {
            var instance = this;

            var currentText = instance.get('node').get('innerHTML');

            currentText = currentText.replace(/\n|\r/gim, '');
            currentText = Lang.trim(currentText);

            currentText = instance._toText(currentText);

            instance._setInput(currentText);

            instance.set(
                'contentText',
                currentText, {
                    initial: true
                }
            );
        },

        /**
         * Cancel the editable. Return to the original state.
         *
         * @method cancel
         */
        cancel: function() {
            var instance = this;

            instance.fire('cancel');
        },

        /**
         * Save the editable. Fires the
         * <a href="Editable.html#event_save">save</a> event.
         *
         * @method save
         */
        save: function(event) {
            var instance = this;

            instance.fire('save');
        },

        /**
         * Fires the <a href="Editable.html#event_stopEditing">stopEditing</a>
         * event.
         *
         * @method _afterFocusedChangeEditable
         * @param {EventFacade} event
         * @protected
         */
        _afterFocusedChangeEditable: function(event) {
            var instance = this;

            instance.fire('stopEditing', instance.get('visible'));
        },

        /**
         * Create the Events.
         *
         * @method _createEvents
         * @protected
         */
        _createEvents: function() {
            var instance = this;

            /**
             * Handles the startEditing event.
             *
             * @event startEditing
             * @preventable _defStartEditingFn
             * @param {Event.Facade} event The startEditing event.
             * @type {Event.Custom}
             */
            instance.publish(
                'startEditing', {
                    bubbles: true,
                    defaultFn: instance._defStartEditingFn,
                    emitFacade: true,
                    queable: false
                }
            );

            /**
             * Handles the stopEditing event.
             *
             * @event stopEditing
             * @preventable _defStopEditingFn
             * @param {Event.Facade} event The stopEditing event.
             * @type {Event.Custom}
             */
            instance.publish(
                'stopEditing', {
                    bubbles: true,
                    defaultFn: instance._defStopEditingFn,
                    emitFacade: true,
                    queable: false
                }
            );

            /**
             * Handles the save event.
             *
             * @event save
             * @preventable _defSaveFn
             * @param {Event.Facade} event The save event.
             * @type {Event.Custom}
             */
            instance.publish(
                'save', {
                    bubbles: true,
                    defaultFn: instance._defSaveFn,
                    emitFacade: true,
                    queable: false
                }
            );

            /**
             * Handles the cancel event.
             *
             * @event cancel
             * @preventable _defCancelFn
             * @param {Event.Facade} event The cancel event.
             * @type {Event.Custom}
             */
            instance.publish(
                'cancel', {
                    bubbles: true,
                    defaultFn: instance._defCancelFn,
                    emitFacade: true,
                    queable: false
                }
            );
        },

        /**
         * Fires the cancel event.
         *
         * @method _defCancelFn
         * @param {EventFacade} event cancel event facade
         * @protected
         */
        _defCancelFn: function(event) {
            var instance = this;

            instance.fire('stopEditing', false);
        },

        /**
         * Fires the save event.
         *
         * @method _defSaveFn
         * @param {EventFacade} event save event facade
         * @protected
         */
        _defSaveFn: function(event) {
            var instance = this;

            instance.fire('stopEditing', true);
        },

        /**
         * Fires the startEditing event.
         *
         * @method _defStartEditingFn
         * @param {EventFacade} event startEditing event facade
         * @protected
         */
        _defStartEditingFn: function(event) {
            var instance = this;

            var boundingBox = instance.get('boundingBox');
            var node = instance.get('node');

            var inputNode = instance.inputNode;

            var nodeHeight = node.get('offsetHeight');
            var nodeWidth = node.get('offsetWidth');

            instance.show();

            node.addClass(CSS_EDITING);

            var xy = node.getXY();

            boundingBox.setStyles({
                height: nodeHeight + 'px',
                left: xy[0] + 'px',
                top: xy[1] + 'px',
                width: nodeWidth + 'px'
            });

            var inputField = instance._comboBox._field;

            inputField.set('width', nodeWidth);
            inputField.fire('adjustSize');

            inputNode.focus();
            inputNode.select();
        },

        /**
         * Fires the stopEditing event.
         *
         * @method _defStopEditingFn
         * @param {EventFacade} event stopEditing event facade
         * @protected
         */
        _defStopEditingFn: function(event, save) {
            var instance = this;

            instance.hide();

            instance.get('node').removeClass(CSS_EDITING);

            if (save) {
                instance.set('contentText', instance.inputNode.get('value'));
            }
            else {
                instance._setInput(instance.get('contentText'));
            }
        },

        /**
         * Fires <code>onkeypress</code> occurs on the editable element.
         *
         * @method _onKeypressEditable
         * @param {EventFacade} event
         * @protected
         */
        _onKeypressEditable: function(event) {
            var instance = this;

            if (event.isKey('ESC')) {
                event.preventDefault();

                instance.cancel();
            }
            else if (event.isKey('ENTER') && (instance.get('inputType') == 'text')) {
                instance.save();
            }
        },

        /**
         * Fires <code>onmouseenter</code> occurs on the editable element.
         *
         * @method _onMouseEnterEditable
         * @param {EventFacade} event
         * @protected
         */
        _onMouseEnterEditable: function(event) {
            var instance = this;

            instance.get('node').addClass(CSS_HOVER);
        },

        /**
         * Fires <code>onmouseleave</code> occurs on the editable element.
         *
         * @method _onMouseLeaveEditable
         * @param {EventFacade} event
         * @protected
         */
        _onMouseLeaveEditable: function(event) {
            var instance = this;

            instance.get('node').removeClass(CSS_HOVER);
        },

        /**
         * Set the value of the <a
         * href="Editable.html#property_inputNode">inputNode</a>.
         *
         * @method _setInput
         * @param {String} value Value of the input.
         * @protected
         */
        _setInput: function(value) {
            var instance = this;

            var inputFormatter = instance.get('formatInput');

            if (inputFormatter) {
                value = inputFormatter.call(instance, value);
            }
            else {
                value = instance._toText(value);
            }

            instance.inputNode.set('value', LString.unescapeEntities(value));
        },

        /**
         * Set the <code>innerHTML</code> of the <a
         * href="Editable.html#config_node">node</a>.
         *
         * @method _setOutput
         * @param {String} value
         * @protected
         */
        _setOutput: function(value) {
            var instance = this;

            var outputFormatter = instance.get('formatOutput');

            if (outputFormatter) {
                value = outputFormatter.call(instance, value);
            }
            else {
                value = instance._toHTML(value);
            }

            instance.get('node').set('innerHTML', A.Escape.html(value));
        },

        /**
         * Fires when start editing.
         *
         * @method _startEditing
         * @param {EventFacade} event
         * @protected
         */
        _startEditing: function(event) {
            var instance = this;

            if (!instance.get('rendered')) {
                instance.render(instance.get('renderTo'));
            }

            instance.fire('startEditing');

            event.halt();
        },

        /**
         * Sync the content text.
         *
         * @method _syncContentText
         * @param {EventFacade} event
         * @protected
         */
        _syncContentText: function(event) {
            var instance = this;

            if (!event.initial) {
                var contentText = event.newVal;

                instance._setInput(contentText);
                instance._setOutput(contentText);
            }
        },

        /**
         * Converts the new lines <code>\n</code> to <code><br/></code> (i.e.,
         * nl2br).
         *
         * @method _toHTML
         * @param {String} text Input text.
         * @protected
         * @return {String}
         */
        _toHTML: function(text) {
            var instance = this;

            return String(text).replace(/\n/gim, '<br/>');
        },

        /**
         * Converts HTML to text.
         *
         * @method _toText
         * @param {String} text HTML input.
         * @protected
         * @return {String}
         */
        _toText: function(text) {
            var instance = this;

            text = String(text);

            text = text.replace(/<br\s*\/?>/gim, '\n');

            text = text.replace(/(<\/?[^>]+>|\t)/gim, '');

            return text;
        },

        /**
         * Handles the updating of the UI when the node is set.
         *
         * @method _uiSetNode
         * @param {Node} node.
         * @protected
         */

        _uiSetNode: function(node) {
            var instance = this;

            if (instance._mouseEnterHandler) {
                instance._mouseEnterHandler.detach();
            }

            if (instance._mouseLeaveHandler) {
                instance._mouseLeaveHandler.detach();
            }

            if (instance._interactionHandler) {
                instance._interactionHandler.detach();
            }

            var eventType = instance.get('eventType');

            instance._mouseEnterHandler = node.on('mouseenter', instance._onMouseEnterEditable, instance);
            instance._mouseLeaveHandler = node.on('mouseleave', instance._onMouseLeaveEditable, instance);

            instance._interactionHandler = node.on(eventType, instance._startEditing, instance);
        }
    }
});

A.Editable = Editable;


}, '3.1.0-deprecated.112', {
    "requires": [
        "aui-base-deprecated",
        "aui-form-combobox-deprecated",
        "escape",
        "event-resize"
    ],
    "skinnable": true
});
