<commit_before>/** @lends InlineEditableViewComponent */
define(function(require) {
    'use strict';

    /**
     * Allows to connect inline editors on view pages.
     * Currently used only for tags-editor. See [index of supported editors](../editor)
     * @TODO update after connecting other editors
     *
     * Sample:
     *
     * ```twig
     * {% import 'OroUIBundle::macros.html.twig' as UI %}
     * <div {{ UI.renderPageComponentAttributes({
     *    module: 'oroform/js/app/components/inline-editable-view-component',
     *    options: {
     *        frontend_type: 'tags',
     *        value: oro_tag_get_list(entity),
     *        fieldName: 'tags',
     *        metadata: {
     *            inline_editing: {
     *                enable: resource_granted('oro_tag_assign_unassign'),
     *                save_api_accessor: {
     *                    route: 'oro_api_post_taggable',
     *                    http_method: 'POST',
     *                    default_route_parameters: {
     *                        entity: oro_class_name(entity, true),
     *                        entityId: entity.id
     *                    }
     *                },
     *                autocomplete_api_accessor: {
     *                    class: 'oroui/js/tools/search-api-accessor',
     *                    search_handler_name: 'tags',
     *                    label_field_name: 'name'
     *                },
     *                editor: {
     *                    view_options: {
     *                        permissions: {
     *                            oro_tag_create: resource_granted('oro_tag_create'),
     *                            oro_tag_unassign_global: resource_granted('oro_tag_unassign_global')
     *                        }
     *                    }
     *                }
     *            }
     *        }
     *    }
     * }) }}></div>
     * ```
     *
     * @class
     * @param {Object} options - Options container
     * @param {Object} options._sourceElement - Element where to connect this view (passed automatically when
     *                                          page component is [connected through DOM attributes](../../../../UIBundle/Resources/doc/reference/page-component.md))
     * @param {string} options.frontend_type - frontend type, please find [available keys here](../../public/js/tools/frontend-type-map.js)
     * @param {*} options.value - value to edit
     * @param {string} options.fieldName - field name to use when sending value to server
     * @param {Object} options.metadata.inline_editing - inline-editing configuration
     *
     * @augments BaseComponent
     * @exports InlineEditableViewComponent
     */
    var InlineEditableViewComponent;
    var _ = require('underscore');
    var __ = require('orotranslation/js/translator');
    var mediator = require('oroui/js/mediator');
    var $ = require('jquery');
    var BaseComponent = require('oroui/js/app/components/base/component');
    var BaseModel = require('oroui/js/app/models/base/model');
    var InlineEditorWrapperView = require('../views/inline-editable-wrapper-view');
    var frontendTypeMap = require('../../tools/frontend-type-map');
    var overlayTool = require('oroui/js/tools/overlay');
    var tools = require('oroui/js/tools');

    InlineEditableViewComponent = BaseComponent.extend(/** @exports InlineEditableViewComponent.prototype */{
        OVERLAY_TOOL_DEFAULTS: {
            position: {
                my: 'left top',
                at: 'left-11 top-11',
                collision: 'flipfit'
            }
        },

        METADATA_DEFAULTS: {
            inline_editing: {
                enable: false,
                save_api_accessor: {
                    'class': 'oroui/js/tools/api-accessor'
                }
            }
        },

        ESCAPE_KEY_CODE: 27,

        WIDTH_INCREMENT: 15,

        /**
         * @constructor
         * @param {Object} options
         */
        initialize: function(options) {
            options.metadata = $.extend(true, {}, this.METADATA_DEFAULTS, options.metadata);
            this.inlineEditingOptions = options.metadata.inline_editing;
            var waitors = [];
            this.fieldName = options.fieldName || 'value';
            // frontend type mapped to viewer/editor/reader
            var classes = frontendTypeMap[options.frontend_type];
            this.classes = classes;
            this.metadata = options.metadata;
            this.model = new BaseModel();
            this.model.set(this.fieldName, options.value);
            if (this.inlineEditingOptions.enable) {
                this.wrapper = new InlineEditorWrapperView({
                    el: options._sourceElement,
                    autoRender: true
                });
                this.view = new classes.viewer(_.extend({
                    el: this.wrapper.getContainer(),
                    autoRender: true,
                    model: this.model,
                    fieldName: this.fieldName
                }));
                if (this.classes.editor.processMetadata) {
                    waitors.push(this.classes.editor.processMetadata(this.metadata));
                }
                this.wrapper.on('start-editing', this.enterEditMode, this);
                waitors.push(tools.loadModuleAndReplace(this.inlineEditingOptions.save_api_accessor, 'class').then(
                    _.bind(function() {
                        var ConcreteApiAccessor = this.inlineEditingOptions.save_api_accessor['class'];
                        this.saveApiAccessor = new ConcreteApiAccessor(
                            _.omit(this.inlineEditingOptions.save_api_accessor, 'class'));
                    }, this)
                ));
            } else {
                this.view = new classes.viewer(_.extend({
                    el: options._sourceElement,
                    autoRender: true,
                    model: this.model,
                    fieldName: this.fieldName
                }));
            }
            this.deferredInit = $.when.apply($, waitors);
        },

        enterEditMode: function() {
            var View = this.classes.editor;
            var viewConfiguration = this.inlineEditingOptions.editor ?
                this.inlineEditingOptions.editor.view_options :
                {};
            var viewInstance = new View(_.extend({}, viewConfiguration, {
                className: 'inline-view-editor',
                autoRender: true,
                model: this.model,
                fieldName: this.fieldName
            }));

            this.editorView = viewInstance;

            viewInstance.$el.addClass('inline-editor-wrapper');

            var overlayOptions = $.extend(true, {}, this.OVERLAY_TOOL_DEFAULTS, {
                position: {
                    of: this.wrapper.$el
                }
            });
            this.resizeTo(viewInstance, this.wrapper);

            var overlay = overlayTool.createOverlay(viewInstance.$el, overlayOptions);

            this.listenTo(viewInstance, {
                dispose: function() {
                    overlay.remove();
                },
                change: function() {
                    viewInstance.$el.toggleClass('show-overlay', !viewInstance.isValid());
                },
                keydown: this.onGenericEscapeKeydown,
                focus: function() {
                    mediator.trigger('inlineEditor:focus', viewInstance);
                },
                blur: function() {
                    if (viewInstance.isChanged()) {
                        this.saveCurrentCell();
                    }
                }
            });

            this.listenTo(viewInstance, 'saveAction', this.saveCurrentCell);
            this.listenTo(viewInstance, 'saveAndExitAction', this.saveCurrentCellAndExit);
            this.listenTo(viewInstance, 'cancelAction', this.exitEditMode, true);
            this.listenTo(viewInstance, 'saveAndEditNextAction', this.saveCurrentCellAndExit);
            this.listenTo(viewInstance, 'cancelAndEditNextAction', this.exitEditMode);
            this.listenTo(viewInstance, 'saveAndEditPrevAction', this.saveCurrentCellAndExit);
            this.listenTo(viewInstance, 'cancelAndEditPrevAction', this.exitEditMode);
            this.listenTo(viewInstance, 'saveAndEditNextRowAction', this.saveCurrentCellAndExit);
            this.listenTo(viewInstance, 'cancelAndEditNextRowAction', this.exitEditMode);
            this.listenTo(viewInstance, 'saveAndEditPrevRowAction', this.saveCurrentCellAndExit);
            this.listenTo(viewInstance, 'cancelAndEditPrevRowAction', this.exitEditMode);
            this.listenTo(mediator, 'inlineEditor:focus', this.onInlineEditorFocus);

            return viewInstance;
        },

        onInlineEditorFocus: function(view) {
            if (!this.editorView || view === this.editorView) {
                return;
            }
            if (!this.editorView.isChanged()) {
                this.exitEditMode();
            }
        },

        exitEditMode: function() {
            this.editorView.dispose();
            delete this.editorView;<commit_msg>CRM-4867 fix behaviout of inline editing in forms<commit_after>/** @lends InlineEditableViewComponent */
define(function(require) {
    'use strict';

    /**
     * Allows to connect inline editors on view pages.
     * Currently used only for tags-editor. See [index of supported editors](../editor)
     * @TODO update after connecting other editors
     *
     * Sample:
     *
     * ```twig
     * {% import 'OroUIBundle::macros.html.twig' as UI %}
     * <div {{ UI.renderPageComponentAttributes({
     *    module: 'oroform/js/app/components/inline-editable-view-component',
     *    options: {
     *        frontend_type: 'tags',
     *        value: oro_tag_get_list(entity),
     *        fieldName: 'tags',
     *        metadata: {
     *            inline_editing: {
     *                enable: resource_granted('oro_tag_assign_unassign'),
     *                save_api_accessor: {
     *                    route: 'oro_api_post_taggable',
     *                    http_method: 'POST',
     *                    default_route_parameters: {
     *                        entity: oro_class_name(entity, true),
     *                        entityId: entity.id
     *                    }
     *                },
     *                autocomplete_api_accessor: {
     *                    class: 'oroui/js/tools/search-api-accessor',
     *                    search_handler_name: 'tags',
     *                    label_field_name: 'name'
     *                },
     *                editor: {
     *                    view_options: {
     *                        permissions: {
     *                            oro_tag_create: resource_granted('oro_tag_create'),
     *                            oro_tag_unassign_global: resource_granted('oro_tag_unassign_global')
     *                        }
     *                    }
     *                }
     *            }
     *        }
     *    }
     * }) }}></div>
     * ```
     *
     * @class
     * @param {Object} options - Options container
     * @param {Object} options._sourceElement - Element where to connect this view (passed automatically when
     *                                          page component is [connected through DOM attributes](../../../../UIBundle/Resources/doc/reference/page-component.md))
     * @param {string} options.frontend_type - frontend type, please find [available keys here](../../public/js/tools/frontend-type-map.js)
     * @param {*} options.value - value to edit
     * @param {string} options.fieldName - field name to use when sending value to server
     * @param {Object} options.metadata.inline_editing - inline-editing configuration
     *
     * @augments BaseComponent
     * @exports InlineEditableViewComponent
     */
    var InlineEditableViewComponent;
    var _ = require('underscore');
    var __ = require('orotranslation/js/translator');
    var mediator = require('oroui/js/mediator');
    var $ = require('jquery');
    var BaseComponent = require('oroui/js/app/components/base/component');
    var BaseModel = require('oroui/js/app/models/base/model');
    var InlineEditorWrapperView = require('../views/inline-editable-wrapper-view');
    var frontendTypeMap = require('../../tools/frontend-type-map');
    var overlayTool = require('oroui/js/tools/overlay');
    var tools = require('oroui/js/tools');

    InlineEditableViewComponent = BaseComponent.extend(/** @exports InlineEditableViewComponent.prototype */{
        OVERLAY_TOOL_DEFAULTS: {
            position: {
                my: 'left top',
                at: 'left-11 top-11',
                collision: 'flipfit'
            }
        },

        METADATA_DEFAULTS: {
            inline_editing: {
                enable: false,
                save_api_accessor: {
                    'class': 'oroui/js/tools/api-accessor'
                }
            }
        },

        ESCAPE_KEY_CODE: 27,

        WIDTH_INCREMENT: 15,

        /**
         * @constructor
         * @param {Object} options
         */
        initialize: function(options) {
            options.metadata = $.extend(true, {}, this.METADATA_DEFAULTS, options.metadata);
            this.inlineEditingOptions = options.metadata.inline_editing;
            var waitors = [];
            this.fieldName = options.fieldName || 'value';
            // frontend type mapped to viewer/editor/reader
            var classes = frontendTypeMap[options.frontend_type];
            this.classes = classes;
            this.metadata = options.metadata;
            this.model = new BaseModel();
            this.model.set(this.fieldName, options.value);
            if (this.inlineEditingOptions.enable) {
                this.wrapper = new InlineEditorWrapperView({
                    el: options._sourceElement,
                    autoRender: true
                });
                this.view = new classes.viewer(_.extend({
                    el: this.wrapper.getContainer(),
                    autoRender: true,
                    model: this.model,
                    fieldName: this.fieldName
                }));
                if (this.classes.editor.processMetadata) {
                    waitors.push(this.classes.editor.processMetadata(this.metadata));
                }
                this.wrapper.on('start-editing', this.enterEditMode, this);
                waitors.push(tools.loadModuleAndReplace(this.inlineEditingOptions.save_api_accessor, 'class').then(
                    _.bind(function() {
                        var ConcreteApiAccessor = this.inlineEditingOptions.save_api_accessor['class'];
                        this.saveApiAccessor = new ConcreteApiAccessor(
                            _.omit(this.inlineEditingOptions.save_api_accessor, 'class'));
                    }, this)
                ));
            } else {
                this.view = new classes.viewer(_.extend({
                    el: options._sourceElement,
                    autoRender: true,
                    model: this.model,
                    fieldName: this.fieldName
                }));
            }
            this.deferredInit = $.when.apply($, waitors);
        },

        enterEditMode: function() {
            var View = this.classes.editor;
            var viewConfiguration = this.inlineEditingOptions.editor ?
                this.inlineEditingOptions.editor.view_options :
                {};
            var viewInstance = new View(_.extend({}, viewConfiguration, {
                className: 'inline-view-editor',
                autoRender: true,
                model: this.model,
                fieldName: this.fieldName
            }));

            this.editorView = viewInstance;

            viewInstance.$el.addClass('inline-editor-wrapper');

            var overlayOptions = $.extend(true, {}, this.OVERLAY_TOOL_DEFAULTS, {
                position: {
                    of: this.wrapper.$el
                }
            });
            this.resizeTo(viewInstance, this.wrapper);

            var overlay = overlayTool.createOverlay(viewInstance.$el, overlayOptions);

            this.listenTo(viewInstance, {
                dispose: function() {
                    overlay.remove();
                },
                change: function() {
                    viewInstance.$el.toggleClass('show-overlay', !viewInstance.isValid());
                },
                keydown: this.onGenericEscapeKeydown,
                focus: function() {
                    mediator.trigger('inlineEditor:focus', viewInstance);
                },
                blur: function() {
                    if (viewInstance.isChanged()) {
                        this.saveCurrentCell();
                    }
                }
            });

            viewInstance.focus();

            this.listenTo(viewInstance, 'saveAction', this.saveCurrentCell);
            this.listenTo(viewInstance, 'saveAndExitAction', this.saveCurrentCellAndExit);
            this.listenTo(viewInstance, 'cancelAction', this.exitEditMode, true);
            this.listenTo(viewInstance, 'saveAndEditNextAction', this.saveCurrentCellAndExit);
            this.listenTo(viewInstance, 'cancelAndEditNextAction', this.exitEditMode);
            this.listenTo(viewInstance, 'saveAndEditPrevAction', this.saveCurrentCellAndExit);
            this.listenTo(viewInstance, 'cancelAndEditPrevAction', this.exitEditMode);
            this.listenTo(viewInstance, 'saveAndEditNextRowAction', this.saveCurrentCellAndExit);
            this.listenTo(viewInstance, 'cancelAndEditNextRowAction', this.exitEditMode);
            this.listenTo(viewInstance, 'saveAndEditPrevRowAction', this.saveCurrentCellAndExit);
            this.listenTo(viewInstance, 'cancelAndEditPrevRowAction', this.exitEditMode);
            this.listenTo(mediator, 'inlineEditor:focus', this.onInlineEditorFocus);

            return viewInstance;
        },

        onInlineEditorFocus: function(view) {
            if (!this.editorView || view === this.editorView) {
                return;
            }
            if (!this.editorView.isChanged()) {
                this.exitEditMode();
            }
        },

        exitEditMode: function() {
            this.editorView.dispose();
            delete this.editorView;
{'fraction_non_alphanumeric': 0.10726456428841889, 'fraction_numerical': 0.0010818412938821875, 'mean_word_length': 1.623155505107832, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3756170', 'n_tokens_mistral': 4861, 'n_tokens_neox': 4628, 'n_words': 787}