<commit_before>/**
 * The Form Builder Layout Builder Component
 *
 * @module aui-form-builder
 * @submodule aui-form-builder-layout-builder
 */

var CSS_CHOOSE_COL_MOVE = A.getClassName('form', 'builder', 'choose', 'col', 'move'),
    CSS_CHOOSE_COL_MOVE_TARGET = A.getClassName('form', 'builder', 'choose', 'col', 'move', 'target'),
    CSS_FIELD = A.getClassName('form', 'builder', 'field'),
    CSS_FIELD_MOVE_BUTTON = A.getClassName('form', 'builder', 'field', 'move', 'button'),
    CSS_FIELD_MOVE_TARGET = A.getClassName('form', 'builder', 'field', 'move', 'target'),
    CSS_FIELD_MOVE_TARGET_INVALID = A.getClassName('form', 'builder', 'field', 'move', 'target', 'invalid'),
    CSS_FIELD_MOVING = A.getClassName('form', 'builder', 'field', 'moving'),
    CSS_LAYOUT = A.getClassName('form', 'builder', 'layout'),
    CSS_LAYOUT_BUILDER_MOVE_CANCEL = A.getClassName('layout', 'builder', 'move', 'cancel'),
    CSS_MOVE_COL_TARGET = A.getClassName('layout', 'builder', 'move', 'col', 'target'),
    CSS_MOVE_TARGET = A.getClassName('layout', 'builder', 'move', 'target'),
    CSS_REMOVE_ROW_MODAL = A.getClassName('form', 'builder', 'remove', 'row', 'modal');

/**
 * `A.FormBuilder` extension, which handles the `A.LayoutBuilder` inside it.
 *
 * @class A.FormBuilderLayoutBuilder
 * @param {Object} config Object literal specifying layout builder configuration
 *     properties.
 * @constructor
 */
A.FormBuilderLayoutBuilder = function() {};

A.FormBuilderLayoutBuilder.prototype = {

    /**
     * Construction logic executed during the `A.FormBuilderLayoutBuilder`
     * instantiation. Lifecycle.
     *
     * @method initializer
     * @protected
     */
    initializer: function() {
        this._initRemoveConfirmationModal();

        this.after({
            render: this._afterLayoutBuilderRender,
            'layout-row:colsChange': this._afterLayoutBuilderColsChange
        });
    },

    /**
     * Destructor implementation for the `A.FormBuilderLayoutBuilder` class.
     * Lifecycle.
     *
     * @method destructor
     * @protected
     */
    destructor: function() {
        if (this._layoutBuilder) {
            this._layoutBuilder.destroy();
        }

        this._removeConfirmationModal.destroy();
    },

    /**
     * Overrides default `addColMoveButton` attribute.
     *
     * @method _addColMoveButton
     * @param {Node} colNode
     * @param {Node} rowNode
     * @protected
     */
    _addColMoveButton: function(colNode, rowNode) {
        var targetNodes = colNode.all('.' + CSS_FIELD_MOVE_BUTTON);

        targetNodes.setData('node-col', colNode);
        targetNodes.setData('node-row', rowNode);

        this._renderEmptyColumns();
    },

    /**
     * Overrides default `addColMoveTarget` attribute.
     *
     * @method _addColMoveTarget
     * @param {A.LayoutCol} col
     * @protected
     */
    _addColMoveTarget: function(col) {
        var colNode = col.get('node'),
            targetNodes;

        colNode.addClass(CSS_CHOOSE_COL_MOVE_TARGET);

        targetNodes = colNode.all('.' + CSS_FIELD_MOVE_TARGET);
        targetNodes.setData('col', col);
    },

    /**
     * Executed after the `layout:rowsChange` is fired.
     *
     * @method _afterLayoutBuilderColsChange
     * @protected
     */
    _afterLayoutBuilderColsChange: function() {
        var activeLayout = this.getActiveLayout();

        this._checkLastRow(activeLayout);
    },

    /**
     * Fired after this widget is rendered.
     *
     * @method _afterLayoutBuilderRender
     * @protected
     */
    _afterLayoutBuilderRender: function() {
        var originalChooseColMoveTargetFn;

        this._layoutBuilder = new A.LayoutBuilder({
            addColMoveButton: A.bind(this._addColMoveButton, this),
            addColMoveTarget: A.bind(this._addColMoveTarget, this),
            clickColMoveTarget: A.bind(this._clickColMoveTarget, this),
            clickRemoveRow: A.bind(this._clickRemoveRow, this),
            container: this.get('contentBox').one('.' + CSS_LAYOUT),
            layout: this.getActiveLayout(),
            removeColMoveButtons: A.bind(this._removeColMoveButtons, this),
            removeColMoveTargets: A.bind(this._removeColMoveTargets, this),
            strings: this.get('strings')
        });

        originalChooseColMoveTargetFn = this._layoutBuilder.get('chooseColMoveTarget');
        this._layoutBuilder.set('chooseColMoveTarget', A.bind(this._chooseColMoveTarget, this,
            originalChooseColMoveTargetFn));

        this._eventHandles.push(
            this._fieldToolbar.on('onToolbarHasAddedToField', A.bind(this._onFormBuilderToolbarHasAddedToField, this))
        );

        this._removeLayoutCutColButtons();
    },

    /**
     * Checks if the last row has more than one col or if it has at least one field,
     * if true a new row is created and set on the last position.
     *
     * @method _checkLastRow
     * @param {A.Layout} layout
     * @protected
     */
    _checkLastRow: function(layout) {
        var cols,
            lastRow,
            nextToLast,
            rows;

        lastRow = this._getLastRow(layout);
        cols = lastRow.get('cols');

        if (cols.length > 1 || !this._isColumnEmpty(cols[0])) {
            this._createLastRow(layout);
        }
        else {
            rows = layout.get('rows');
            nextToLast = rows[rows.length - 2];

            if (nextToLast) {
                cols = nextToLast.get('cols');

                if (cols.length === 1 && this._isColumnEmpty(cols[0])) {
                    layout.removeRow(nextToLast);
                }
            }
        }

        this._getLastRow(layout).set('removable', false);
    },

    /**
     * Overrides default `chooseColMoveTarget` attribute.
     *
     * @method _chooseColMoveTarget
     * @param {Function} originalFn
     * @param {Node} cutButton
     * @param {A.LayoutCol} col
     * @protected
     */
    _chooseColMoveTarget: function(originalFn, cutButton, col) {
        var fieldNode = cutButton.ancestor('.' + CSS_FIELD),
            layout = this.getActiveLayout(),
            targetNode;

        this._fieldBeingMoved = fieldNode.getData('field-instance');
        this._fieldListBeingMoved = col.get('value');
        this._fieldBeingMovedCol = col;

        fieldNode.addClass(CSS_FIELD_MOVING);

        fieldNode.all('.' + CSS_FIELD_MOVE_TARGET).addClass(CSS_FIELD_MOVE_TARGET_INVALID);

        targetNode = fieldNode.previous('.' + CSS_FIELD_MOVE_TARGET);
        if (targetNode) {
            targetNode.addClass(CSS_FIELD_MOVE_TARGET_INVALID);
        }

        targetNode = fieldNode.next('.' + CSS_FIELD_MOVE_TARGET);
        if (targetNode) {
            targetNode.addClass(CSS_FIELD_MOVE_TARGET_INVALID);
        }

        originalFn(cutButton, col);
        this._addColMoveTarget(col);

        layout.normalizeColsHeight(layout.get('node').all('.row'));

        this._selectFirstValidMoveTarget();

        this._cancelMoveFieldHandles = [
            A.one(A.config.doc).on('click', A.bind(this._onClickOutsideMoveTarget, this)),
            A.one(A.config.doc).on('key', A.bind(this._onEscKeyPressMoveTarget, this), 'down:27')
        ];
    },

    _selectFirstValidMoveTarget: function() {
        var moveTarget = A.one('.' + CSS_FIELD_MOVE_TARGET + ':not(.' + CSS_FIELD_MOVE_TARGET_INVALID + ')');

        moveTarget.focus();
    },

    /**
     * Overrides default `clickColMoveTarget` attribute.
     *
     * @method _clickColMoveTarget
     * @param {Node} moveTarget
     * @protected
     */
    _clickColMoveTarget: function(moveTarget) {
        var layout = this.getActiveLayout(),
            parentFieldNode = this._fieldBeingMoved.get('content').ancestor('.' + CSS_FIELD),
            row,
            targetNestedParent = moveTarget.getData('nested-field-parent'),
            toolbarMoveIconCancelMode = this._fieldToolbar.getItem('.' + CSS_LAYOUT_BUILDER_MOVE_CANCEL);

        if (toolbarMoveIconCancelMode) {
            toolbarMoveIconCancelMode.removeClass(CSS_LAYOUT_BUILDER_MOVE_CANCEL);
        }

        if (parentFieldNode) {
            parentFieldNode.getData('field-instance').removeNestedField(this._fieldBeingMoved);
        }
        else {
            row = this.getFieldRow(this._fieldBeingMoved);
            this._fieldListBeingMoved.removeField(this._fieldBeingMoved);
        }

        if (targetNestedParent) {
            this._addNestedField(
                targetNestedParent,
                this._fieldBeingMoved,
                moveTarget.getData('nested-field-index')
            );
        }
        else {
            moveTarget.getData('col').get('value').addField(
                this._fieldBeingMoved,
                moveTarget.getData('field-list-index')
            );
        }

        this._layoutBuilder.cancelMove();
        this._removeLayoutCutColButtons();

        layout.normalizeColsHeight(new A.NodeList(this.getFieldRow(this._fieldBeingMoved)));

        this._detachCancelMoveFieldEvents();
    },

    /**
     * Overrides default `clickRemoveRow` attribute. Check if the parameter `row` has fields.
     *
     * @method _clickRemoveRow
     * @param {A.LayoutRow} row
     * @return {Boolean}
     * @protected
     */
    _clickRemoveRow: function(row) {
        var cols = row.get('cols'),
            currentList,
            index;

        for (index = 0; index < cols.length; index++) {
            currentList = cols[index].get('value');

            if (currentList && currentList.get('fields').length) {
                this._removeConfirmationModal.show();
                this._removingRow = row;

                return false;
            }
        }

        return true;
    },

    /**
     * Creates a new row in the last position.
     *
     * @method _createLastRow
     * @param {A.Layout} layout
     * @protected
     */
    _createLastRow: function(layout) {
        var lastRow = new A.LayoutRow(),
            rows = layout.get('rows');

        layout.addRow(rows.length, lastRow);
    },

    /**
     * Detaches events related to the cancel move field funcionality.
     *
     * @method _detachCancelMoveFieldEvents
     * @protected
     */
    _detachCancelMoveFieldEvents: function() {
        new A.EventHandle(this._cancelMoveFieldHandles).detach();
    },

    /**
     * Removes a row even with field.
     *
     * @method _forceRemoveRow
     * @protected
     */
    _forceRemoveRow: function() {
        this.getActiveLayout().removeRow(this._removingRow);

        this._removeConfirmationModal.hide();
    },

    /**
     * Gets the last row.
     *
     * @method _getLastRow
     * @param {A.Layout} layout
     * @protected
     * @return {A.LayoutRow}
     */
    _getLastRow: function(layout) {
        var rows = layout.get('rows');

        return rows[rows.length - 1];
    },

    /**
     * Create a confirmation modal to be used when a remove row button from a row with
     * fields is clicked.
     *
     * @method _initRemoveConfirmationModal
     * @protected
     */
    _initRemoveConfirmationModal: function() {
        var modal = new A.Modal({
            bodyContent: this.get('strings').removeRowModal,
            centered: true,
            cssClass: CSS_REMOVE_ROW_MODAL,
            headerContent: this.get('strings').modalHeader,
            modal: true,
            resizable: false,
            visible: false,
            zIndex: 4
        }).render();

        modal.addToolbar([
            {
                cssClass: 'btn-primary',
                label: this.get('strings').confirmRemoveRow,
                on: {
                    click: A.bind(this._forceRemoveRow, this)
                },
                render: true
            },
            {
                label: this.get('strings').cancelRemoveRow,
                on: {
                    click: function() {
                        modal.hide();
                    }
                },
                render: true
            }
        ]);

        this._removeConfirmationModal = modal;
    },

    /**
     * Checks if the given column is empty.
     *
     * @method _isColumnEmpty
     * @param {A.LayoutCol} col
     * @return {Boolean}
     * @protected
     */
    _isColumnEmpty: function(col) {
        return !col.get('value') || !col.get('value').get('fields').length;
    },

    /**
     * Fires when click event is triggered.
     *
     * @method _onClickOutsideMoveTarget
     * @param {EventFacade} event
     * @protected
     */
    _onClickOutsideMoveTarget: function(event) {
        var targetNode = event.target,
        toolbarMoveIconCancelMode = this._fieldToolbar.getItem('.' + CSS_LAYOUT_BUILDER_MOVE_CANCEL);

        if (toolbarMoveIconCancelMode) {
            toolbarMoveIconCancelMode.removeClass(CSS_LAYOUT_BUILDER_MOVE_CANCEL);
        }

        if (!(targetNode.hasClass(CSS_MOVE_TARGET) && targetNode.hasClass(CSS_MOVE_COL_TARGET))) {
            this._layoutBuilder.cancelMove();
            this._detachCancelMoveFieldEvents();
        }
    },

    /**
     * Fires when esc key press event is triggered.
     *
     * @method _onEscKeyPressMoveTarget
     * @protected
     */
    _onEscKeyPressMoveTarget: function() {
        var toolbarMoveIconCancelMode = this._fieldToolbar.getItem('.' + CSS_LAYOUT_BUILDER_MOVE_CANCEL);

        if (toolbarMoveIconCancelMode) {
            toolbarMoveIconCancelMode.removeClass(CSS_LAYOUT_BUILDER_MOVE_CANCEL);
        }

        this._layoutBuilder.cancelMove();
        this._detachCancelMoveFieldEvents();
    },

    /**
     * Fired when mouse enters a toolbar's field.
     *
     * @method _onFormBuilderToolbarHasAddedToField
     * @params {EventFacade} event
     * @protected
     */
    _onFormBuilderToolbarHasAddedToField: function(event) {
        this._setMoveButtonData(event.colNode);
    },

    /**
     * Overrides default `removeColMoveButtons` attribute.
     *
     * @method _removeColMoveButtons
     * @protected
     */
    _removeColMoveButtons: function() {
        this.get('contentBox').all('.' + CSS_CHOOSE_COL_MOVE).removeClass(CSS_CHOOSE_COL_MOVE);
    },

    /**
     * Overrides default `removeColMoveTargets` attribute.
     *
     * @method _removeColMoveTargets
     * @protected
     */
    _removeColMoveTargets: function() {
        var contentBox = this.get('contentBox'),
            layout = this.getActiveLayout();

        contentBox.all('.' + CSS_CHOOSE_COL_MOVE_TARGET).removeClass(CSS_CHOOSE_COL_MOVE_TARGET);
        contentBox.all('.' + CSS_FIELD_MOVING).removeClass(CSS_FIELD_MOVING);
        contentBox.all('.' + CSS_FIELD_MOVE_TARGET_INVALID).removeClass(CSS_FIELD_MOVE_TARGET_INVALID);

        layout.normalizeColsHeight(layout.get('node').all('.row'));
    },

    /**
     * Remove original layout cut col buttons.
     *
     * @method _removeLayoutCutColButtons
     * @protected
     */
    _removeLayoutCutColButtons: function() {
        this._layoutBuilder.get('removeColMoveButtons')();
    },

    /**
     * Show or hide move item in toolbar.
     *
     * @method _setMoveButtonData
     * @param {Node} colNode
     * @protected
     */
    _setMoveButtonData: function(colNode) {
        var moveItem = this._fieldToolbar.getItem('.layout-builder-move-cut-button');

        moveItem.setData('layout-row', colNode.ancestor('.row').getData('layout-row'));
        moveItem.setData('node-col', colNode);
    }
};
<commit_msg>AUI-2097 Source formating<commit_after>/**
 * The Form Builder Layout Builder Component
 *
 * @module aui-form-builder
 * @submodule aui-form-builder-layout-builder
 */

var CSS_CHOOSE_COL_MOVE = A.getClassName('form', 'builder', 'choose', 'col', 'move'),
    CSS_CHOOSE_COL_MOVE_TARGET = A.getClassName('form', 'builder', 'choose', 'col', 'move', 'target'),
    CSS_FIELD = A.getClassName('form', 'builder', 'field'),
    CSS_FIELD_MOVE_BUTTON = A.getClassName('form', 'builder', 'field', 'move', 'button'),
    CSS_FIELD_MOVE_TARGET = A.getClassName('form', 'builder', 'field', 'move', 'target'),
    CSS_FIELD_MOVE_TARGET_INVALID = A.getClassName('form', 'builder', 'field', 'move', 'target', 'invalid'),
    CSS_FIELD_MOVING = A.getClassName('form', 'builder', 'field', 'moving'),
    CSS_LAYOUT = A.getClassName('form', 'builder', 'layout'),
    CSS_LAYOUT_BUILDER_MOVE_CANCEL = A.getClassName('layout', 'builder', 'move', 'cancel'),
    CSS_MOVE_COL_TARGET = A.getClassName('layout', 'builder', 'move', 'col', 'target'),
    CSS_MOVE_TARGET = A.getClassName('layout', 'builder', 'move', 'target'),
    CSS_REMOVE_ROW_MODAL = A.getClassName('form', 'builder', 'remove', 'row', 'modal');

/**
 * `A.FormBuilder` extension, which handles the `A.LayoutBuilder` inside it.
 *
 * @class A.FormBuilderLayoutBuilder
 * @param {Object} config Object literal specifying layout builder configuration
 *     properties.
 * @constructor
 */
A.FormBuilderLayoutBuilder = function() {};

A.FormBuilderLayoutBuilder.prototype = {

    /**
     * Construction logic executed during the `A.FormBuilderLayoutBuilder`
     * instantiation. Lifecycle.
     *
     * @method initializer
     * @protected
     */
    initializer: function() {
        this._initRemoveConfirmationModal();

        this.after({
            render: this._afterLayoutBuilderRender,
            'layout-row:colsChange': this._afterLayoutBuilderColsChange
        });
    },

    /**
     * Destructor implementation for the `A.FormBuilderLayoutBuilder` class.
     * Lifecycle.
     *
     * @method destructor
     * @protected
     */
    destructor: function() {
        if (this._layoutBuilder) {
            this._layoutBuilder.destroy();
        }

        this._removeConfirmationModal.destroy();
    },

    /**
     * Overrides default `addColMoveButton` attribute.
     *
     * @method _addColMoveButton
     * @param {Node} colNode
     * @param {Node} rowNode
     * @protected
     */
    _addColMoveButton: function(colNode, rowNode) {
        var targetNodes = colNode.all('.' + CSS_FIELD_MOVE_BUTTON);

        targetNodes.setData('node-col', colNode);
        targetNodes.setData('node-row', rowNode);

        this._renderEmptyColumns();
    },

    /**
     * Overrides default `addColMoveTarget` attribute.
     *
     * @method _addColMoveTarget
     * @param {A.LayoutCol} col
     * @protected
     */
    _addColMoveTarget: function(col) {
        var colNode = col.get('node'),
            targetNodes;

        colNode.addClass(CSS_CHOOSE_COL_MOVE_TARGET);

        targetNodes = colNode.all('.' + CSS_FIELD_MOVE_TARGET);
        targetNodes.setData('col', col);
    },

    /**
     * Executed after the `layout:rowsChange` is fired.
     *
     * @method _afterLayoutBuilderColsChange
     * @protected
     */
    _afterLayoutBuilderColsChange: function() {
        var activeLayout = this.getActiveLayout();

        this._checkLastRow(activeLayout);
    },

    /**
     * Fired after this widget is rendered.
     *
     * @method _afterLayoutBuilderRender
     * @protected
     */
    _afterLayoutBuilderRender: function() {
        var originalChooseColMoveTargetFn;

        this._layoutBuilder = new A.LayoutBuilder({
            addColMoveButton: A.bind(this._addColMoveButton, this),
            addColMoveTarget: A.bind(this._addColMoveTarget, this),
            clickColMoveTarget: A.bind(this._clickColMoveTarget, this),
            clickRemoveRow: A.bind(this._clickRemoveRow, this),
            container: this.get('contentBox').one('.' + CSS_LAYOUT),
            layout: this.getActiveLayout(),
            removeColMoveButtons: A.bind(this._removeColMoveButtons, this),
            removeColMoveTargets: A.bind(this._removeColMoveTargets, this),
            strings: this.get('strings')
        });

        originalChooseColMoveTargetFn = this._layoutBuilder.get('chooseColMoveTarget');
        this._layoutBuilder.set('chooseColMoveTarget', A.bind(this._chooseColMoveTarget, this,
            originalChooseColMoveTargetFn));

        this._eventHandles.push(
            this._fieldToolbar.on('onToolbarHasAddedToField', A.bind(this._onFormBuilderToolbarHasAddedToField, this))
        );

        this._removeLayoutCutColButtons();
    },

    /**
     * Checks if the last row has more than one col or if it has at least one field,
     * if true a new row is created and set on the last position.
     *
     * @method _checkLastRow
     * @param {A.Layout} layout
     * @protected
     */
    _checkLastRow: function(layout) {
        var cols,
            lastRow,
            nextToLast,
            rows;

        lastRow = this._getLastRow(layout);
        cols = lastRow.get('cols');

        if (cols.length > 1 || !this._isColumnEmpty(cols[0])) {
            this._createLastRow(layout);
        }
        else {
            rows = layout.get('rows');
            nextToLast = rows[rows.length - 2];

            if (nextToLast) {
                cols = nextToLast.get('cols');

                if (cols.length === 1 && this._isColumnEmpty(cols[0])) {
                    layout.removeRow(nextToLast);
                }
            }
        }

        this._getLastRow(layout).set('removable', false);
    },

    /**
     * Overrides default `chooseColMoveTarget` attribute.
     *
     * @method _chooseColMoveTarget
     * @param {Function} originalFn
     * @param {Node} cutButton
     * @param {A.LayoutCol} col
     * @protected
     */
    _chooseColMoveTarget: function(originalFn, cutButton, col) {
        var fieldNode = cutButton.ancestor('.' + CSS_FIELD),
            layout = this.getActiveLayout(),
            targetNode;

        this._fieldBeingMoved = fieldNode.getData('field-instance');
        this._fieldListBeingMoved = col.get('value');
        this._fieldBeingMovedCol = col;

        fieldNode.addClass(CSS_FIELD_MOVING);

        fieldNode.all('.' + CSS_FIELD_MOVE_TARGET).addClass(CSS_FIELD_MOVE_TARGET_INVALID);

        targetNode = fieldNode.previous('.' + CSS_FIELD_MOVE_TARGET);
        if (targetNode) {
            targetNode.addClass(CSS_FIELD_MOVE_TARGET_INVALID);
        }

        targetNode = fieldNode.next('.' + CSS_FIELD_MOVE_TARGET);
        if (targetNode) {
            targetNode.addClass(CSS_FIELD_MOVE_TARGET_INVALID);
        }

        originalFn(cutButton, col);
        this._addColMoveTarget(col);

        layout.normalizeColsHeight(layout.get('node').all('.row'));

        this._selectFirstValidMoveTarget();

        this._cancelMoveFieldHandles = [
            A.one(A.config.doc).on('click', A.bind(this._onClickOutsideMoveTarget, this)),
            A.one(A.config.doc).on('key', A.bind(this._onEscKeyPressMoveTarget, this), 'down:27')
        ];
    },

    /**
     * Overrides default `clickColMoveTarget` attribute.
     *
     * @method _clickColMoveTarget
     * @param {Node} moveTarget
     * @protected
     */
    _clickColMoveTarget: function(moveTarget) {
        var layout = this.getActiveLayout(),
            parentFieldNode = this._fieldBeingMoved.get('content').ancestor('.' + CSS_FIELD),
            row,
            targetNestedParent = moveTarget.getData('nested-field-parent'),
            toolbarMoveIconCancelMode = this._fieldToolbar.getItem('.' + CSS_LAYOUT_BUILDER_MOVE_CANCEL);

        if (toolbarMoveIconCancelMode) {
            toolbarMoveIconCancelMode.removeClass(CSS_LAYOUT_BUILDER_MOVE_CANCEL);
        }

        if (parentFieldNode) {
            parentFieldNode.getData('field-instance').removeNestedField(this._fieldBeingMoved);
        }
        else {
            row = this.getFieldRow(this._fieldBeingMoved);
            this._fieldListBeingMoved.removeField(this._fieldBeingMoved);
        }

        if (targetNestedParent) {
            this._addNestedField(
                targetNestedParent,
                this._fieldBeingMoved,
                moveTarget.getData('nested-field-index')
            );
        }
        else {
            moveTarget.getData('col').get('value').addField(
                this._fieldBeingMoved,
                moveTarget.getData('field-list-index')
            );
        }

        this._layoutBuilder.cancelMove();
        this._removeLayoutCutColButtons();

        layout.normalizeColsHeight(new A.NodeList(this.getFieldRow(this._fieldBeingMoved)));

        this._detachCancelMoveFieldEvents();
    },

    /**
     * Overrides default `clickRemoveRow` attribute. Check if the parameter `row` has fields.
     *
     * @method _clickRemoveRow
     * @param {A.LayoutRow} row
     * @return {Boolean}
     * @protected
     */
    _clickRemoveRow: function(row) {
        var cols = row.get('cols'),
            currentList,
            index;

        for (index = 0; index < cols.length; index++) {
            currentList = cols[index].get('value');

            if (currentList && currentList.get('fields').length) {
                this._removeConfirmationModal.show();
                this._removingRow = row;

                return false;
            }
        }

        return true;
    },

    /**
     * Creates a new row in the last position.
     *
     * @method _createLastRow
     * @param {A.Layout} layout
     * @protected
     */
    _createLastRow: function(layout) {
        var lastRow = new A.LayoutRow(),
            rows = layout.get('rows');

        layout.addRow(rows.length, lastRow);
    },

    /**
     * Detaches events related to the cancel move field funcionality.
     *
     * @method _detachCancelMoveFieldEvents
     * @protected
     */
    _detachCancelMoveFieldEvents: function() {
        new A.EventHandle(this._cancelMoveFieldHandles).detach();
    },

    /**
     * Removes a row even with field.
     *
     * @method _forceRemoveRow
     * @protected
     */
    _forceRemoveRow: function() {
        this.getActiveLayout().removeRow(this._removingRow);

        this._removeConfirmationModal.hide();
    },

    /**
     * Gets the last row.
     *
     * @method _getLastRow
     * @param {A.Layout} layout
     * @protected
     * @return {A.LayoutRow}
     */
    _getLastRow: function(layout) {
        var rows = layout.get('rows');

        return rows[rows.length - 1];
    },

    /**
     * Create a confirmation modal to be used when a remove row button from a row with
     * fields is clicked.
     *
     * @method _initRemoveConfirmationModal
     * @protected
     */
    _initRemoveConfirmationModal: function() {
        var modal = new A.Modal({
            bodyContent: this.get('strings').removeRowModal,
            centered: true,
            cssClass: CSS_REMOVE_ROW_MODAL,
            headerContent: this.get('strings').modalHeader,
            modal: true,
            resizable: false,
            visible: false,
            zIndex: 4
        }).render();

        modal.addToolbar([
            {
                cssClass: 'btn-primary',
                label: this.get('strings').confirmRemoveRow,
                on: {
                    click: A.bind(this._forceRemoveRow, this)
                },
                render: true
            },
            {
                label: this.get('strings').cancelRemoveRow,
                on: {
                    click: function() {
                        modal.hide();
                    }
                },
                render: true
            }
        ]);

        this._removeConfirmationModal = modal;
    },

    /**
     * Checks if the given column is empty.
     *
     * @method _isColumnEmpty
     * @param {A.LayoutCol} col
     * @return {Boolean}
     * @protected
     */
    _isColumnEmpty: function(col) {
        return !col.get('value') || !col.get('value').get('fields').length;
    },

    /**
     * Fires when click event is triggered.
     *
     * @method _onClickOutsideMoveTarget
     * @param {EventFacade} event
     * @protected
     */
    _onClickOutsideMoveTarget: function(event) {
        var targetNode = event.target,
        toolbarMoveIconCancelMode = this._fieldToolbar.getItem('.' + CSS_LAYOUT_BUILDER_MOVE_CANCEL);

        if (toolbarMoveIconCancelMode) {
            toolbarMoveIconCancelMode.removeClass(CSS_LAYOUT_BUILDER_MOVE_CANCEL);
        }

        if (!(targetNode.hasClass(CSS_MOVE_TARGET) && targetNode.hasClass(CSS_MOVE_COL_TARGET))) {
            this._layoutBuilder.cancelMove();
            this._detachCancelMoveFieldEvents();
        }
    },

    /**
     * Fires when esc key press event is triggered.
     *
     * @method _onEscKeyPressMoveTarget
     * @protected
     */
    _onEscKeyPressMoveTarget: function() {
        var toolbarMoveIconCancelMode = this._fieldToolbar.getItem('.' + CSS_LAYOUT_BUILDER_MOVE_CANCEL);

        if (toolbarMoveIconCancelMode) {
            toolbarMoveIconCancelMode.removeClass(CSS_LAYOUT_BUILDER_MOVE_CANCEL);
        }

        this._layoutBuilder.cancelMove();
        this._detachCancelMoveFieldEvents();
    },

    /**
     * Fired when mouse enters a toolbar's field.
     *
     * @method _onFormBuilderToolbarHasAddedToField
     * @params {EventFacade} event
     * @protected
     */
    _onFormBuilderToolbarHasAddedToField: function(event) {
        this._setMoveButtonData(event.colNode);
    },

    /**
     * Overrides default `removeColMoveButtons` attribute.
     *
     * @method _removeColMoveButtons
     * @protected
     */
    _removeColMoveButtons: function() {
        this.get('contentBox').all('.' + CSS_CHOOSE_COL_MOVE).removeClass(CSS_CHOOSE_COL_MOVE);
    },

    /**
     * Overrides default `removeColMoveTargets` attribute.
     *
     * @method _removeColMoveTargets
     * @protected
     */
    _removeColMoveTargets: function() {
        var contentBox = this.get('contentBox'),
            layout = this.getActiveLayout();

        contentBox.all('.' + CSS_CHOOSE_COL_MOVE_TARGET).removeClass(CSS_CHOOSE_COL_MOVE_TARGET);
        contentBox.all('.' + CSS_FIELD_MOVING).removeClass(CSS_FIELD_MOVING);
        contentBox.all('.' + CSS_FIELD_MOVE_TARGET_INVALID).removeClass(CSS_FIELD_MOVE_TARGET_INVALID);

        layout.normalizeColsHeight(layout.get('node').all('.row'));
    },

    /**
     * Remove original layout cut col buttons.
     *
     * @method _removeLayoutCutColButtons
     * @protected
     */
    _removeLayoutCutColButtons: function() {
        this._layoutBuilder.get('removeColMoveButtons')();
    },

    /**
     * Find and focus on first valid move target.
     *
     * @method _selectFirstValidMoveTarget
     * @protected
     */
    _selectFirstValidMoveTarget: function() {
        var moveTarget = A.one('.' + CSS_FIELD_MOVE_TARGET + ':not(.' + CSS_FIELD_MOVE_TARGET_INVALID + ')');

        moveTarget.focus();
    },

    /**
     * Show or hide move item in toolbar.
     *
     * @method _setMoveButtonData
     * @param {Node} colNode
     * @protected
     */
    _setMoveButtonData: function(colNode) {
        var moveItem = this._fieldToolbar.getItem('.layout-builder-move-cut-button');

        moveItem.setData('layout-row', colNode.ancestor('.row').getData('layout-row'));
        moveItem.setData('node-col', colNode);
    }
};
<|endoftext|>


<commit_before>'use strict';

var Registrations = require('./registrations')
    ,Decorators = require('./decorators')
    ,ComponentModel = require('./component-model')
    ,CacheableResolver = require('./cacheable-resolver')
    ,TransientResolver = require('./transient-resolver')
    ,ImplResolver  = require('./impl-resolver')
    ,DecoratingResolver = require('./decorating-resolver')
    ,FactoryActivator = require('./factory-activator')
    ,ConstructorActivator = require('./constructor-activator')
    ,ImplActivator = require('./impl-activator')
    ,ResolveableComponentModel = require('./resolveable-component-model')
    ;
module.exports = Kernel

var warn = console.warn.bind(console)
    ,error  = console.error.bind(console)
    ,debug = console.log.bind(console)

function registerSystemServices() {
    this.resolvers['@cacheable'] = CacheableResolver
    this.resolvers['@transient'] = TransientResolver
    this.resolvers['@impl'] = ImplResolver
    this.activators['@factory'] = FactoryActivator
    this.activators['@constructor'] = ConstructorActivator
    this.activators['@impl'] = ImplActivator

    var Promise = new ComponentModel('@Promise',require('bluebird'))
    Promise.resolver('@cacheable')
    Promise.activator('@impl')
    this.register(Promise)

}

function Kernel(){
    this.registrations = new Registrations()
    this.decorators = new Decorators()
    this.resolvers = {}
    this.activators = {}
    registerSystemServices.call(this)
}

Kernel.prototype.decorate = function(targetKey, decoratorKey) {
    this.decorators.put(targetKey,decoratorKey)
    return this
}
Kernel.prototype.register = function(model) {
    this.registrations.put(model)
    return this
}
Kernel.prototype.promise = function() {
    var promise = this.registrations.get('@Promise')
    return promise.impl.resolve(promise.impl)

}
Kernel.prototype.createActivator = function(key) {
    var res = this.activators[key];
    return this.resolveAll(res.inject)
        .then(function(deps){
            return res.apply(this,deps)
        }.bind(this))
}
Kernel.prototype.createResolver  = function(key) {
    var res = this.resolvers[key]
    return this.resolveAll(res.inject)
        .then(function(deps){
            return res.apply(this,deps)
        }.bind(this))
}
Kernel.prototype.createContext = function(model,deps) {
    if(!model) {
        throw new Error('model is required')
    }
    if(!model.isResolveable) {
        throw new Error('model must be resolveable')
    }
    var context = {
            model: model
            ,activator: model.activator()
            ,Promise: undefined
            ,resolver: model.resolver()
        }
    return this.promise()
        .then(function(Promise){
            context.Promise = Promise
            return context
        })
}
Kernel.prototype._demandResolveable = function(key) {
    var model = this.registrations.get(key)
    if(!model) {
        throw new Error('model is required')
    }
    if(model.isResolveable) {
        return model
    }
    var resolveable = ResolveableComponentModel.fromModel.call(this,model)
    resolveable = this._decorate(resolveable)
    this.register(resolveable)
    return resolveable
}
Kernel.prototype._decorate = function(resolveable) {
    //decorate if possible
    var decorators = this.decorators.get(resolveable.key)
        .map(this._demandResolveable.bind(this))
    if(!decorators.length){
        return resolveable
    }
    if(decorators.length){
        resolveable.resolver(new DecoratingResolver(resolveable,decorators,this))
    }
    return resolveable
}

Kernel.prototype.resolve = function(key, deps) {
    //lazily make the component resolveable
    var model = this._demandResolveable(key)

    return this.createContext(model, deps)
        .then(function(context){
            return this.resolveAll(model.inject,deps)
                .then(function doResolve(resolvedDeps){
                    try {
                        return Promise.resolve(context.resolver.resolve(context, resolvedDeps))
                    } catch(err) {
                        error('error trying to resolve',context.model.key,err.message,err.stack)
                        throw err
                    }
                }.bind(this))
                .then(this.handleResolved.bind(this,context,deps))
        }.bind(this))
}
Kernel.prototype.handleResolved = function(context, deps,  instance) {
    var initializable = context.model.initializable
        ,Promise = context.Promise
        ;
    if(!initializable) {
        return instance
    }
    var fn = instance[initializable]
    if(!fn){
        throw new Error(context.model.key + ' was designated as initializable, but ' +
                        initializable + ' function could not be located.')
    }
    return this.invoke(instance, fn, deps)
        .then(function(result){
            return instance
        })
}
Kernel.prototype.resolveAll = function(keys,deps) {
    keys = (keys || [])
    deps = (deps || {})
    var elements = keys.map(function mapKeys(key){
        if(deps[key]) {
            return deps[key]
        }
        return this.resolve(key,deps)
    }.bind(this))
    return this.promise()
        .then(function(Promise){
            return Promise.all(elements)
        })
}
Kernel.prototype.invoke = function(context, fn, args){
    assert(context,'context is required. this is the context for invoking the fn.')
    assert(fn,'fn is required')

    return this.resolveAll(fn.inject || [], args)
        .then(function(resolvedDeps){
            return fn.apply(context,resolvedDeps)
        })

}
Kernel.prototype.start = function(){
    return this.promise()
        .then(function(Promise){
            var startables = this.registrations.startables()
                .map(function(model){
                    return this.resolve(model.key)
                        .then(function(instance){
                            var fn = instance[model.startable]
                            return this.invoke(instance, fn)
                                .then(function(){
                                    return instance
                                })
                        }.bind(this))
                }.bind(this))
            return Promise.all(startables)
        }.bind(this))
}
<commit_msg>promisified internal demandResolveable<commit_after>'use strict';

var Registrations = require('./registrations')
    ,Decorators = require('./decorators')
    ,ComponentModel = require('./component-model')
    ,CacheableResolver = require('./cacheable-resolver')
    ,TransientResolver = require('./transient-resolver')
    ,ImplResolver  = require('./impl-resolver')
    ,DecoratingResolver = require('./decorating-resolver')
    ,FactoryActivator = require('./factory-activator')
    ,ConstructorActivator = require('./constructor-activator')
    ,ImplActivator = require('./impl-activator')
    ,ResolveableComponentModel = require('./resolveable-component-model')
    ;
module.exports = Kernel

var warn = console.warn.bind(console)
    ,error  = console.error.bind(console)
    ,debug = console.log.bind(console)

function registerSystemServices() {
    this.resolvers['@cacheable'] = CacheableResolver
    this.resolvers['@transient'] = TransientResolver
    this.resolvers['@impl'] = ImplResolver
    this.activators['@factory'] = FactoryActivator
    this.activators['@constructor'] = ConstructorActivator
    this.activators['@impl'] = ImplActivator

    var Promise = new ComponentModel('@Promise',require('bluebird'))
    Promise.resolver('@cacheable')
    Promise.activator('@impl')
    this.register(Promise)

}

function Kernel(){
    this.registrations = new Registrations()
    this.decorators = new Decorators()
    this.resolvers = {}
    this.activators = {}
    registerSystemServices.call(this)
}

Kernel.prototype.decorate = function(targetKey, decoratorKey) {
    this.decorators.put(targetKey,decoratorKey)
    return this
}
Kernel.prototype.register = function(model) {
    this.registrations.put(model)
    return this
}
Kernel.prototype.promise = function() {
    var promise = this.registrations.get('@Promise')
    return promise.impl.resolve(promise.impl)

}
Kernel.prototype.createActivator = function(key) {
    var res = this.activators[key];
    return this.resolveAll(res.inject)
        .then(function(deps){
            return res.apply(this,deps)
        }.bind(this))
}
Kernel.prototype.createResolver  = function(key) {
    var res = this.resolvers[key]
    return this.resolveAll(res.inject)
        .then(function(deps){
            return res.apply(this,deps)
        }.bind(this))
}
Kernel.prototype.createContext = function(model,deps) {
    if(!model) {
        throw new Error('model is required')
    }
    if(!model.isResolveable) {
        throw new Error('model must be resolveable')
    }
    var context = {
            model: model
            ,activator: model.activator()
            ,Promise: undefined
            ,resolver: model.resolver()
        }
    return this.promise()
        .then(function(Promise){
            context.Promise = Promise
            return context
        })
}
Kernel.prototype._demandResolveable = function(key) {
    var model = this.registrations.get(key)
    if(!model) {
        throw new Error('model is required')
    }
    return this.promise().then(function(){
        if(model.isResolveable) {
            return model
        }
        var resolveable = new ResolveableComponentModel(model)
        return resolveable.prepare(this)
            .then(this._decorate.bind(this))
            .then(this.register.bind(this))
            .then(function(){
                return resolveable
            })
    }.bind(this))
    /* synchronous
    var resolveable = ResolveableComponentModel.fromModel.call(this,model)
    resolveable = this._decorate(resolveable)
    this.register(resolveable)
    return resolveable
    */
}
Kernel.prototype._decorate = function(resolveable) {
    //decorate if possible
    var decorators = this.decorators.get(resolveable.key)
        .map(this._demandResolveable.bind(this))
    return this.promise()
        .then(function(Promise){
            if(!decorators.length){
                return resolveable
            }
            return Promise.all(decorators)
                .then(function(resolvedDecorators){
                    resolveable.resolver(new DecoratingResolver(resolveable
                        ,resolvedDecorators
                        ,this))
                    return resolveable
                }.bind(this))
        }.bind(this))

}

Kernel.prototype.resolve = function(key, deps) {
    //lazily make the component resolveable
    //var model = this._demandResolveable(key)

    return this._demandResolveable(key)
        .then(function(model){
            return this.createContext(model, deps)
                .then(function(context){
                    return this.resolveAll(model.inject,deps)
                        .then(function doResolve(resolvedDeps){
                            try {
                                return Promise.resolve(context.resolver.resolve(context, resolvedDeps))
                            } catch(err) {
                                error('error trying to resolve',context.model.key,err.message,err.stack)
                                throw err
                            }
                        }.bind(this))
                        .then(this.handleResolved.bind(this,context,deps))
                }.bind(this))
        }.bind(this))
}
Kernel.prototype.handleResolved = function(context, deps,  instance) {
    var initializable = context.model.initializable
        ,Promise = context.Promise
        ;
    if(!initializable) {
        return instance
    }
    var fn = instance[initializable]
    if(!fn){
        throw new Error(context.model.key + ' was designated as initializable, but ' +
                        initializable + ' function could not be located.')
    }
    return this.invoke(instance, fn, deps)
        .then(function(result){
            return instance
        })
}
Kernel.prototype.resolveAll = function(keys,deps) {
    keys = (keys || [])
    deps = (deps || {})
    var elements = keys.map(function mapKeys(key){
        if(deps[key]) {
            return deps[key]
        }
        return this.resolve(key,deps)
    }.bind(this))
    return this.promise()
        .then(function(Promise){
            return Promise.all(elements)
        })
}
Kernel.prototype.invoke = function(context, fn, args){
    assert(context,'context is required. this is the context for invoking the fn.')
    assert(fn,'fn is required')

    return this.resolveAll(fn.inject || [], args)
        .then(function(resolvedDeps){
            return fn.apply(context,resolvedDeps)
        })

}
Kernel.prototype.start = function(){
    return this.promise()
        .then(function(Promise){
            var startables = this.registrations.startables()
                .map(function(model){
                    return this.resolve(model.key)
                        .then(function(instance){
                            var fn = instance[model.startable]
                            return this.invoke(instance, fn)
                                .then(function(){
                                    return instance
                                })
                        }.bind(this))
                }.bind(this))
            return Promise.all(startables)
        }.bind(this))
}
<|endoftext|>


<commit_before>import React from 'react';

import LinkedHeading from '../../components/LinkedHeading';
import withLayout from '../../withLayout';

export default withLayout(function ReactOverlaysSection() {
  return (
    <>
      <LinkedHeading h="1" id="react-overlays">
        React Overlays
      </LinkedHeading>

        Low-level components and utilities for building beautiful accessible
        overlay components

        Often times you may need a more generic or low-level version of a
        Bootstrap component. Many of the react-bootstrap components
        are built on top of components from{' '}

          react-overlays

        , if you find yourself at the limit of a Bootstrap component, consider
        using the react-overlays base component directly.

    </>
  );
});
<commit_msg>docs: makes link to react-overlays open in a new tab (#4545)<commit_after>import React from 'react';

import LinkedHeading from '../../components/LinkedHeading';
import withLayout from '../../withLayout';

export default withLayout(function ReactOverlaysSection() {
  return (
    <>
      <LinkedHeading h="1" id="react-overlays">
        React Overlays
      </LinkedHeading>

        Low-level components and utilities for building beautiful accessible
        overlay components

        Often times you may need a more generic or low-level version of a
        Bootstrap component. Many of the react-bootstrap components
        are built on top of components from{' '}

          react-overlays

        , if you find yourself at the limit of a Bootstrap component, consider
        using the react-overlays base component directly.

    </>
  );
});
<|endoftext|>


<commit_before>'use strict';
var React = require('react');
var _ = require('underscore');
var globals = require('../globals');
var fetched = require('../fetched');
var RestMixin = require('../rest').RestMixin;
var parseAndLogError = require('../mixins').parseAndLogError;
var form = require('../../libs/bootstrap/form');
var modal = require('../../libs/bootstrap/modal');
var CuratorHistory = require('../curator_history');
var curator = require('../curator');

var Input = form.Input;
var Form = form.Form;
var FormMixin = form.FormMixin;
var Modal = modal.Modal;
var ModalMixin = modal.ModalMixin;
var queryKeyValue = globals.queryKeyValue;

// Display the variant curation action bar above the criteria and tabs
var VariantCurationActions = module.exports.VariantCurationActions = React.createClass({
    mixins: [RestMixin, ModalMixin, FormMixin, CuratorHistory],

    propTypes: {
        variantData: React.PropTypes.object, // ClinVar data payload
        session: React.PropTypes.object,
        interpretation: React.PropTypes.object,
        editKey: React.PropTypes.string,
        updateInterpretationObj: React.PropTypes.func
    },

    getInitialState: function() {
        return {
            variantUuid: null,
            interpretation: null,
            hasExistingInterpretation: false,
            isInterpretationActive: false,
            hasAssociatedDisease: false,
            hasAssociatedInheritance: false
        };
    },

    componentWillReceiveProps: function(nextProps) {
        if (this.props.variantData) {
            if (this.props.variantData.associatedInterpretations) {
                if (this.props.variantData.associatedInterpretations.length) {
                    var associatedInterpretations = this.props.variantData.associatedInterpretations;
                    associatedInterpretations.forEach(associatedInterpretation => {
                        if (associatedInterpretation.submitted_by['@id'] === this.props.session.user_properties['@id']) {
                            this.setState({hasExistingInterpretation: true});
                        }
                    });
                }
            }
        }
        if (this.props.editKey === 'true' && this.props.interpretation) {
            this.setState({isInterpretationActive: true});
            if (this.props.interpretation) {
                if (this.props.interpretation.interpretation_disease) {
                    this.setState({hasAssociatedDisease: true});
                }
                if (this.props.interpretation.modeInheritance) {
                    this.setState({hasAssociatedInheritance: true});
                }
            }
        }
    },

    updateParentState: function() {
        this.setState({hasAssociatedDisease: true});
    },

    // handler for 'Start new interpretation' & 'Continue interpretation' button click events
    handleInterpretationEvent: function(e) {
        e.preventDefault(); e.stopPropagation();
        var variantObj = this.props.variantData;
        var selectedTab = queryKeyValue('tab', window.location.href),
            selectedSubtab = queryKeyValue('subtab', window.location.href);
        var newInterpretationObj;
        if (!this.state.hasExistingInterpretation) {
            if (variantObj) {
                this.setState({variantUuid: variantObj.uuid});
                // Put together a new interpretation object
                newInterpretationObj = {variant: variantObj.uuid};
            }
            // Post new interpretation to the DB. Once promise returns, go to /curation-variant page with
            // the new interpretation UUID in the query string.
            this.postRestData('/interpretations/', newInterpretationObj).then(data => {
                var newInterpretationUuid = data['@graph'][0].uuid;
                var meta = {
                    interpretation: {
                        variant: variantObj['@id']
                    }
                };
                this.recordHistory('add-hide', data['@graph'][0], meta).then(result => {
                    window.location.href = '/variant-central/?edit=true&variant=' + this.state.variantUuid + '&interpretation=' + newInterpretationUuid + (selectedTab ? '&tab=' + selectedTab : '') + (selectedSubtab ? '&subtab=' + selectedSubtab : '');
                });
            }).catch(e => {parseAndLogError.bind(undefined, 'postRequest');});
        } else if (this.state.hasExistingInterpretation && !this.state.isInterpretationActive) {
            window.location.href = '/variant-central/?edit=true&variant=' + variantObj.uuid + '&interpretation=' + variantObj.associatedInterpretations[0].uuid + (selectedTab ? '&tab=' + selectedTab : '') + (selectedSubtab ? '&subtab=' + selectedSubtab : '');
        }
    },

    render: function() {
        let interpretationButtonTitle = '';
        if (!this.state.hasExistingInterpretation) {
            interpretationButtonTitle = 'Start New Interpretation';
        } else if (this.state.hasExistingInterpretation && !this.state.isInterpretationActive) {
            interpretationButtonTitle = 'Continue Interpretation';
        }

        let associateDiseaseButtonTitle = Disease ,
            associateDiseaseModalTitle = 'Associate this interpretation with a disease';
        if (this.state.hasAssociatedDisease) {
            associateDiseaseButtonTitle = Disease ;
            associateDiseaseModalTitle = 'Associate this interpretation with a different disease';
        }

        let associateInheritanceButtonTitle = Inheritance ,
            associateInheritanceModalTitle = 'Associate this interpretation with a mode of inheritance';
        if (this.state.hasAssociatedInheritance) {
            associateInheritanceButtonTitle = Inheritance ;
            associateInheritanceModalTitle = 'Associate this interpretation with a different mode of inheritance';
        }

        return (

                {(this.state.isInterpretationActive) ?

Variant Interpretation Record

                            <Modal title={associateDiseaseModalTitle} wrapperClassName="modal-associate-disease">
                                <button className="btn btn-primary pull-right" modal={<AssociateDisease closeModal={this.closeModal} data={this.props.variantData} session={this.props.session} updateParentState={this.updateParentState}
                                    interpretation={this.props.interpretation} editKey={this.props.editkey} updateInterpretationObj={this.props.updateInterpretationObj} />}>{associateDiseaseButtonTitle}</button>
                            </Modal>

                :

                        <Input type="button-button" inputClassName="btn btn-primary pull-right" title={interpretationButtonTitle} clickHandler={this.handleInterpretationEvent} />

                }

        );
    }
});

// handle 'Associate with Disease' button click event
var AssociateDisease = React.createClass({
    mixins: [RestMixin, FormMixin, CuratorHistory],

    contextTypes: {
        handleStateChange: React.PropTypes.func
    },

    propTypes: {
        data: React.PropTypes.object,
        session: React.PropTypes.object,
        closeModal: React.PropTypes.func, // Function to call to close the modal
        interpretation: React.PropTypes.object,
        editKey: React.PropTypes.bool,
        updateInterpretationObj: React.PropTypes.func,
        updateParentState: React.PropTypes.func
    },

    getInitialState: function() {
        return {
            submitResourceBusy: false
        };
    },

    // Form content validation
    validateForm: function() {
        // Start with default validation
        var valid = this.validateDefault();

        // Check if orphanetid
        if (valid) {
            valid = this.getFormValue('orphanetid').match(/^ORPHA[0-9]{1,6}$/i);
            if (!valid) {
                this.setFormErrors('orphanetid', 'Use Orphanet IDs (e.g. ORPHA15)');
            }
        }
        return valid;
    },

    // When the form is submitted...
    submitForm: function(e) {
        e.preventDefault(); e.stopPropagation(); // Don't run through HTML submit handler
        // Invoke button progress indicator
        this.setState({submitResourceBusy: true});
        // Get values from form and validate them
        this.saveFormValue('orphanetid', this.refs.orphanetid.getValue());
        if (this.validateForm()) {
            // Get the free-text values for the Orphanet ID to check against the DB
            var orphaId = this.getFormValue('orphanetid').match(/^ORPHA([0-9]{1,6})$/i)[1];
            var interpretationDisease, currInterpretation;
            // Get the disease orresponding to the given Orphanet ID.
            // If either error out, set the form error fields
            this.getRestDatas([
                '/diseases/' + orphaId
            ], [
                function() { this.setFormErrors('orphanetid', 'Orphanet ID not found'); }.bind(this)
            ]).then(data => {
                interpretationDisease = data[0]['@id'];
                this.getRestData('/interpretation/' + this.props.interpretation.uuid).then(interpretation => {
                    currInterpretation = interpretation;
                    // get up-to-date copy of interpretation object and flatten it
                    var flatInterpretation = curator.flatten(currInterpretation);
                    // if the interpretation object does not have a disease object, create it
                    if (!('disease' in flatInterpretation)) {
                        flatInterpretation.disease = '';
                        // Return the newly flattened interpretation object in a Promise
                        return Promise.resolve(flatInterpretation);
                    } else {
                        return Promise.resolve(flatInterpretation);
                    }
                }).then(interpretationObj => {
                    if (interpretationDisease) {
                        // Set the disease '@id' to the newly flattened interpretation object's 'disease' property
                        interpretationObj.disease = interpretationDisease;
                        // Update the intepretation object partially with the new disease property value
                        return this.putRestData('/interpretation/' + this.props.interpretation.uuid, interpretationObj).then(result => {
                            this.props.updateInterpretationObj();
                            this.props.updateParentState();
                            var meta = {
                                interpretation: {
                                    variant: this.props.data['@id'],
                                    disease: interpretationDisease,
                                    mode: 'edit-disease'
                                }
                            };
                            return this.recordHistory('modify', currInterpretation, meta).then(result => {
                                this.setState({submitResourceBusy: false});
                                // Need 'submitResourceBusy' state to proceed closing modal
                                return Promise.resolve(this.state.submitResourceBusy);
                            });
                        }).then(submitState => {
                            // Close modal after 'submitResourceBusy' is completed
                            if (submitState !== true) {
                                this.props.closeModal();
                            }
                        });
                    }
                });
            }).catch(e => {
                // Some unexpected error happened
                this.setState({submitResourceBusy: false});
                parseAndLogError.bind(undefined, 'fetchedRequest');
            });
        }
    },

    // Called when the modal 'Cancel' button is clicked
    cancelAction: function(e) {
        this.setState({submitResourceBusy: false});
        this.props.closeModal();
    },

    render: function() {
        var disease_id = '';
        if (this.props.interpretation) {
            if (this.props.interpretation.interpretation_disease) {
                disease_id = this.props.interpretation.interpretation_disease;
            }
        }

        return (
            <Form submitHandler={this.submitForm} formClassName="form-std">

                        <Input type="text" ref="orphanetid" label={<LabelOrphanetId />} placeholder="e.g. ORPHA15" value={(disease_id) ? disease_id : null}
                            error={this.getFormError('orphanetid')} clearError={this.clrFormErrors.bind(null, 'orphanetid')}
                            labelClassName="col-sm-4 control-label" wrapperClassName="col-sm-7" groupClassName="form-group" inputClassName="uppercase-input" required />

                        <Input type="button" inputClassName="btn-default btn-inline-spacer" clickHandler={this.cancelAction} title="Cancel" />
                        <Input type="submit" inputClassName="btn-primary btn-inline-spacer" title="OK" submitBusy={this.state.submitResourceBusy} />

            </Form>
        );
    }
});

var LabelOrphanetId = React.createClass({
    render: function() {
        return Enter Orphanet ID;
    }
});
<commit_msg>add inheritance button<commit_after>'use strict';
var React = require('react');
var _ = require('underscore');
var globals = require('../globals');
var fetched = require('../fetched');
var RestMixin = require('../rest').RestMixin;
var parseAndLogError = require('../mixins').parseAndLogError;
var form = require('../../libs/bootstrap/form');
var modal = require('../../libs/bootstrap/modal');
var CuratorHistory = require('../curator_history');
var curator = require('../curator');

var Input = form.Input;
var Form = form.Form;
var FormMixin = form.FormMixin;
var Modal = modal.Modal;
var ModalMixin = modal.ModalMixin;
var queryKeyValue = globals.queryKeyValue;

// Display the variant curation action bar above the criteria and tabs
var VariantCurationActions = module.exports.VariantCurationActions = React.createClass({
    mixins: [RestMixin, ModalMixin, FormMixin, CuratorHistory],

    propTypes: {
        variantData: React.PropTypes.object, // ClinVar data payload
        session: React.PropTypes.object,
        interpretation: React.PropTypes.object,
        editKey: React.PropTypes.string,
        updateInterpretationObj: React.PropTypes.func
    },

    getInitialState: function() {
        return {
            variantUuid: null,
            interpretation: null,
            hasExistingInterpretation: false,
            isInterpretationActive: false,
            hasAssociatedDisease: false,
            hasAssociatedInheritance: false
        };
    },

    componentWillReceiveProps: function(nextProps) {
        if (this.props.variantData) {
            if (this.props.variantData.associatedInterpretations) {
                if (this.props.variantData.associatedInterpretations.length) {
                    var associatedInterpretations = this.props.variantData.associatedInterpretations;
                    associatedInterpretations.forEach(associatedInterpretation => {
                        if (associatedInterpretation.submitted_by['@id'] === this.props.session.user_properties['@id']) {
                            this.setState({hasExistingInterpretation: true});
                        }
                    });
                }
            }
        }
        if (this.props.editKey === 'true' && this.props.interpretation) {
            this.setState({isInterpretationActive: true});
            if (this.props.interpretation) {
                if (this.props.interpretation.interpretation_disease) {
                    this.setState({hasAssociatedDisease: true});
                }
                if (this.props.interpretation.modeInheritance) {
                    this.setState({hasAssociatedInheritance: true});
                }
            }
        }
    },

    updateParentState: function() {
        this.setState({hasAssociatedDisease: true});
    },

    // handler for 'Start new interpretation' & 'Continue interpretation' button click events
    handleInterpretationEvent: function(e) {
        e.preventDefault(); e.stopPropagation();
        var variantObj = this.props.variantData;
        var selectedTab = queryKeyValue('tab', window.location.href),
            selectedSubtab = queryKeyValue('subtab', window.location.href);
        var newInterpretationObj;
        if (!this.state.hasExistingInterpretation) {
            if (variantObj) {
                this.setState({variantUuid: variantObj.uuid});
                // Put together a new interpretation object
                newInterpretationObj = {variant: variantObj.uuid};
            }
            // Post new interpretation to the DB. Once promise returns, go to /curation-variant page with
            // the new interpretation UUID in the query string.
            this.postRestData('/interpretations/', newInterpretationObj).then(data => {
                var newInterpretationUuid = data['@graph'][0].uuid;
                var meta = {
                    interpretation: {
                        variant: variantObj['@id']
                    }
                };
                this.recordHistory('add-hide', data['@graph'][0], meta).then(result => {
                    window.location.href = '/variant-central/?edit=true&variant=' + this.state.variantUuid + '&interpretation=' + newInterpretationUuid + (selectedTab ? '&tab=' + selectedTab : '') + (selectedSubtab ? '&subtab=' + selectedSubtab : '');
                });
            }).catch(e => {parseAndLogError.bind(undefined, 'postRequest');});
        } else if (this.state.hasExistingInterpretation && !this.state.isInterpretationActive) {
            window.location.href = '/variant-central/?edit=true&variant=' + variantObj.uuid + '&interpretation=' + variantObj.associatedInterpretations[0].uuid + (selectedTab ? '&tab=' + selectedTab : '') + (selectedSubtab ? '&subtab=' + selectedSubtab : '');
        }
    },

    render: function() {
        let interpretationButtonTitle = '';
        if (!this.state.hasExistingInterpretation) {
            interpretationButtonTitle = 'Start New Interpretation';
        } else if (this.state.hasExistingInterpretation && !this.state.isInterpretationActive) {
            interpretationButtonTitle = 'Continue Interpretation';
        }

        let associateDiseaseButtonTitle = Disease ,
            associateDiseaseModalTitle = 'Associate this interpretation with a disease';
        if (this.state.hasAssociatedDisease) {
            associateDiseaseButtonTitle = Disease ;
            associateDiseaseModalTitle = 'Associate this interpretation with a different disease';
        }

        let associateInheritanceButtonTitle = Inheritance ,
            associateInheritanceModalTitle = 'Associate this interpretation with a mode of inheritance';
        if (this.state.hasAssociatedInheritance) {
            associateInheritanceButtonTitle = Inheritance ;
            associateInheritanceModalTitle = 'Associate this interpretation with a different mode of inheritance';
        }

        return (

                {(this.state.isInterpretationActive) ?

Variant Interpretation Record

                            <Modal title={associateDiseaseModalTitle} wrapperClassName="modal-associate-disease">
                                <button className="btn btn-primary pull-right btn-inline-spacer" modal={<AssociateDisease closeModal={this.closeModal} data={this.props.variantData} session={this.props.session} updateParentState={this.updateParentState}
                                    interpretation={this.props.interpretation} editKey={this.props.editkey} updateInterpretationObj={this.props.updateInterpretationObj} />}>{associateDiseaseButtonTitle}</button>
                            </Modal>
                            <Modal title={associateInheritanceModalTitle} wrapperClassName="modal-associate-disease">
                                <button className="btn btn-primary pull-right" modal={<AssociateDisease closeModal={this.closeModal} data={this.props.variantData} session={this.props.session} updateParentState={this.updateParentState}
                                    interpretation={this.props.interpretation} editKey={this.props.editkey} updateInterpretationObj={this.props.updateInterpretationObj} />}>{associateInheritanceButtonTitle}</button>
                            </Modal>

                :

                        <Input type="button-button" inputClassName="btn btn-primary pull-right" title={interpretationButtonTitle} clickHandler={this.handleInterpretationEvent} />

                }

        );
    }
});

// handle 'Associate with Disease' button click event
var AssociateDisease = React.createClass({
    mixins: [RestMixin, FormMixin, CuratorHistory],

    contextTypes: {
        handleStateChange: React.PropTypes.func
    },

    propTypes: {
        data: React.PropTypes.object,
        session: React.PropTypes.object,
        closeModal: React.PropTypes.func, // Function to call to close the modal
        interpretation: React.PropTypes.object,
        editKey: React.PropTypes.bool,
        updateInterpretationObj: React.PropTypes.func,
        updateParentState: React.PropTypes.func
    },

    getInitialState: function() {
        return {
            submitResourceBusy: false
        };
    },

    // Form content validation
    validateForm: function() {
        // Start with default validation
        var valid = this.validateDefault();

        // Check if orphanetid
        if (valid) {
            valid = this.getFormValue('orphanetid').match(/^ORPHA[0-9]{1,6}$/i);
            if (!valid) {
                this.setFormErrors('orphanetid', 'Use Orphanet IDs (e.g. ORPHA15)');
            }
        }
        return valid;
    },

    // When the form is submitted...
    submitForm: function(e) {
        e.preventDefault(); e.stopPropagation(); // Don't run through HTML submit handler
        // Invoke button progress indicator
        this.setState({submitResourceBusy: true});
        // Get values from form and validate them
        this.saveFormValue('orphanetid', this.refs.orphanetid.getValue());
        if (this.validateForm()) {
            // Get the free-text values for the Orphanet ID to check against the DB
            var orphaId = this.getFormValue('orphanetid').match(/^ORPHA([0-9]{1,6})$/i)[1];
            var interpretationDisease, currInterpretation;
            // Get the disease orresponding to the given Orphanet ID.
            // If either error out, set the form error fields
            this.getRestDatas([
                '/diseases/' + orphaId
            ], [
                function() { this.setFormErrors('orphanetid', 'Orphanet ID not found'); }.bind(this)
            ]).then(data => {
                interpretationDisease = data[0]['@id'];
                this.getRestData('/interpretation/' + this.props.interpretation.uuid).then(interpretation => {
                    currInterpretation = interpretation;
                    // get up-to-date copy of interpretation object and flatten it
                    var flatInterpretation = curator.flatten(currInterpretation);
                    // if the interpretation object does not have a disease object, create it
                    if (!('disease' in flatInterpretation)) {
                        flatInterpretation.disease = '';
                        // Return the newly flattened interpretation object in a Promise
                        return Promise.resolve(flatInterpretation);
                    } else {
                        return Promise.resolve(flatInterpretation);
                    }
                }).then(interpretationObj => {
                    if (interpretationDisease) {
                        // Set the disease '@id' to the newly flattened interpretation object's 'disease' property
                        interpretationObj.disease = interpretationDisease;
                        // Update the intepretation object partially with the new disease property value
                        return this.putRestData('/interpretation/' + this.props.interpretation.uuid, interpretationObj).then(result => {
                            this.props.updateInterpretationObj();
                            this.props.updateParentState();
                            var meta = {
                                interpretation: {
                                    variant: this.props.data['@id'],
                                    disease: interpretationDisease,
                                    mode: 'edit-disease'
                                }
                            };
                            return this.recordHistory('modify', currInterpretation, meta).then(result => {
                                this.setState({submitResourceBusy: false});
                                // Need 'submitResourceBusy' state to proceed closing modal
                                return Promise.resolve(this.state.submitResourceBusy);
                            });
                        }).then(submitState => {
                            // Close modal after 'submitResourceBusy' is completed
                            if (submitState !== true) {
                                this.props.closeModal();
                            }
                        });
                    }
                });
            }).catch(e => {
                // Some unexpected error happened
                this.setState({submitResourceBusy: false});
                parseAndLogError.bind(undefined, 'fetchedRequest');
            });
        }
    },

    // Called when the modal 'Cancel' button is clicked
    cancelAction: function(e) {
        this.setState({submitResourceBusy: false});
        this.props.closeModal();
    },

    render: function() {
        var disease_id = '';
        if (this.props.interpretation) {
            if (this.props.interpretation.interpretation_disease) {
                disease_id = this.props.interpretation.interpretation_disease;
            }
        }

        return (
            <Form submitHandler={this.submitForm} formClassName="form-std">

                        <Input type="text" ref="orphanetid" label={<LabelOrphanetId />} placeholder="e.g. ORPHA15" value={(disease_id) ? disease_id : null}
                            error={this.getFormError('orphanetid')} clearError={this.clrFormErrors.bind(null, 'orphanetid')}
                            labelClassName="col-sm-4 control-label" wrapperClassName="col-sm-7" groupClassName="form-group" inputClassName="uppercase-input" required />

                        <Input type="button" inputClassName="btn-default btn-inline-spacer" clickHandler={this.cancelAction} title="Cancel" />
                        <Input type="submit" inputClassName="btn-primary btn-inline-spacer" title="OK" submitBusy={this.state.submitResourceBusy} />

            </Form>
        );
    }
});

var LabelOrphanetId = React.createClass({
    render: function() {
        return Enter Orphanet ID;
    }
});
<|endoftext|>