JavaScript Application Development API for Oracle Visual Builder Cloud Service - Classic Applications

Source: components/js/query.builder/QueryParameterViewModel.js

define([
    'bop/js/api/operation/SimpleCondition',
    'components/js/fieldValueEditor/DefaultFieldValueEditorHelper',
    'components/js/fieldValueEditor/FieldValueEditorValueMode',
    'components/js/fieldValueEditor/FieldValueEditorViewModel',
    'core/js/api/Listenable',
    'entity/js/api/Property',
    'operation/js/api/Operator',
    'operation/js/query/QueryParameterDescription',
    'operation/js/query/QueryType',
    'template!components/js/comboboxPicker/ComboboxPickerTemplate.html'
], function (
        SimpleCondition,
        defaultFieldValueEditorHelper,
        FieldValueEditorValueMode,
        FieldValueEditorViewModel,
        Listenable,
        Property,
        Operator,
        QueryParameterDescription,
        QueryType
        ) {

    'use strict';

    /**
     * View model for a single query parameter item.
     *
     */
    var QueryParameterViewModel = function (params) {
        AbcsLib.checkThis(this);
        AbcsLib.checkDefined(params.entity, 'params.entity');
        AbcsLib.checkDefined(params.fieldPathDescriptions, 'params.fieldPathDescriptions');

        Listenable.apply(this);

        var self = this;
        var fieldPathDescriptions = params.fieldPathDescriptions;
        var queryParam = params.queryParam;

        self._entity = params.entity;
        self._options = params.options || {};
        self._helper = self._options.queryHelper || defaultFieldValueEditorHelper;
        self.runtimeMode = self._options.runtimeMode;
        self.abcsOptions = QueryParameterViewModel._generateAbcsOptions(fieldPathDescriptions(), queryParam);
        self.enableUserInput = !!self._options.enableUserInput;
        self.propertyId = ko.observableArray([]);
        self.property = ko.computed(function () {
            return self._findProperty(fieldPathDescriptions, queryParam);
        });
        self.qoOptions = ko.computed(function () {
            return self._getSupportedOperators(self.property());
        });
        self.operator = ko.observableArray([(queryParam && queryParam.getOperator()) || Operator.EQUALS]);
        self.operatorLabel = ko.computed(function() {
            var op = self.operator()[0];
            for (var i = 0; i < self.qoOptions().length; i++) {
                if (self.qoOptions()[i].value === op) {
                    return self.qoOptions()[i].label;
                }
            }
            return op;
        });
        this._operatorNeedsValueComponent = ko.pureComputed(function() {
            var op = self.operator()[0];
            return op !== Operator.IS_EMPTY && op !== Operator.IS_NOT_EMPTY;
        });

        self.required = queryParam && queryParam.isRequired();

        var defaultSpecType;
        var specTypeFromParam = queryParam && queryParam.isUserSpecified() ? 'user_specified' : 'static';
        if (this._options.operationId) {
            defaultSpecType = specTypeFromParam;
        } else {
            defaultSpecType = self.runtimeMode ? 'user_specified' : specTypeFromParam;
        }
        ((queryParam && queryParam.isUserSpecified()) || self.runtimeMode) ? 'user_specified' : 'static';
        self.specificationType = ko.observableArray([defaultSpecType]);
        self.fireEvent(QueryParameterViewModel.EVENT_VALUE_CHANGED);

        self.type = QueryType.FIXED_VALUE;
        if (queryParam) {
            self.queryParam = queryParam;
            self.type = queryParam.getType() || QueryType.FIXED_VALUE;
            self.propertyId([queryParam.getFieldPath().getId()]);
        }
        self.editable = QueryType.isEditable(self.type);
        self.editableInUI = self._isEditableInUI();
        // We need this function because we want to pass more parameters to the helper
        self.getReferenceValueCallback = function(property) {
            return self._helper.getReferenceFor(property, self.queryParam, self._options);
        };
        self.fieldValueEditorViewModel = new FieldValueEditorViewModel({
            sourceEntity: self._entity,
            targetEntity: self._entity,
            expressionEditorTemplateName: params.expressionEditorTemplateName,
            executableExpressionsAllowed: self._options.executableExpressionsAllowed,
            fieldValueEditorHelper: self._helper,
            property: self.property,
            editable: ko.observable(self.editableInUI),
            visible: self._operatorNeedsValueComponent,
            getReferenceValueCallback: self.getReferenceValueCallback,
            runtimeMode: self.runtimeMode,
            loadData: !self._options.viewer
        });
        self._initModel();

        self._chooseProperOperator();

        self.fieldValueEditorViewModel.value.subscribe(function () {
            self.fireEvent(QueryParameterViewModel.EVENT_VALUE_CHANGED);
        });
        self.operator.subscribe(function () {
            self.fieldValueEditorViewModel.chooseValueComponent();
            self.fireEvent(QueryParameterViewModel.EVENT_VALUE_CHANGED);
        });
        self.specificationType.subscribe(function () {
            self.fireEvent(QueryParameterViewModel.EVENT_VALUE_CHANGED);
        });
        self.property.subscribe(function () {
            self._chooseProperOperator();
            self.fireEvent(QueryParameterViewModel.EVENT_VALUE_CHANGED);
        });
    };
    AbcsLib.extend(QueryParameterViewModel, Listenable);

    QueryParameterViewModel.EVENT_VALUE_CHANGED = 'valueChanged';

    /**
     * Find property for current propertyId.
     *
     * @param {ko.observable} fieldPathDescriptions - Observable that contains array of
     *                                                top-level FieldPathDescriptions.
     *
     * @returns {Property}
     */
    QueryParameterViewModel.prototype._findProperty = function (fieldPathDescriptions, queryParam) {
        var self = this;
        var selectedId = self.propertyId()[0];
        var selected;
        if (selectedId) {
            $.each(fieldPathDescriptions(), function (index, fpd) {
                if (selectedId === fpd.getProperty().getId()) {
                    selected = fpd.getProperty();
                    return false;
                }
            });
        }
        if (!selected && selectedId && selectedId.indexOf('.') > 0) {
            // try to search in deeper levels
            var path = selectedId.split('.');
            var searchFpd = function (path, list) {
                for (var i = 0; i < list.length; i++) {
                    var fpd = list[i];
                    if (path[0] === fpd.getProperty().getId()) {
                        if (path.length === 1) {
                            return fpd;
                        } else {
                            var pathTail = path.slice(1);
                            return searchFpd(pathTail, fpd.getChildren());
                        }
                    }
                }
            };
            var selectedFpd = searchFpd(path, fieldPathDescriptions());
            if (selectedFpd) {
                selected = new Property({
                    id: selectedId,
                    name: selectedFpd.getDisplayName(),
                    classification: selectedFpd.getProperty().getClassification(),
                    type: selectedFpd.getProperty().getType()
                }, selectedFpd.getRootEntity());
                selected._fieldPathDesc = selectedFpd;
            }
        }
        if (!selected) {
            var preselectedProp = queryParam && queryParam.getProperty();
            if (preselectedProp && preselectedProp.getId() === selectedId) {
                selected = preselectedProp;
            }
        }
        return selected;
    };

    QueryParameterViewModel._AbcsOption = function (value, label, classNames, childrenFn) {
        this.value = value;
        this.label = label;
        this.classNames = classNames;
        this.childrenFn = childrenFn;
    };

    QueryParameterViewModel._generateAbcsOptions = function (descs, queryParam) {
        var preselectedProp = queryParam && queryParam.getProperty();
        var res = descs.map(function (desc) {
            if (preselectedProp && preselectedProp.getId() === desc.getProperty().getId()) {
                preselectedProp = null;
            }
            return new QueryParameterViewModel._AbcsOption(
                    desc.getProperty().getId(),
                    desc.getProperty().getName(),
                    ['selected-column-list-item-icon', 'type-' + desc.getProperty().getType().toLowerCase()],
                    !desc.mayHaveChildren()
                        ? null
                        : function () {
                            return QueryParameterViewModel._generateAbcsOptions(desc.getChildren());
                        }
            );
        });
        if (preselectedProp) {
            res.push(new QueryParameterViewModel._AbcsOption(
                    preselectedProp.getId(),
                    preselectedProp.getName(),
                    ['selected-column-list-item-icon', 'type-' + preselectedProp.getType().toLowerCase()],
                    null)
                    );
        }
        res.sort(function (a, b) {
            return a.label.localeCompare(b.label);
        });
        res.abcsOptions = true;
        return res;
    };

    QueryParameterViewModel.prototype.getQueryParameter = function () {
        var parameter;
        var property = this.property();
        if (property) {
            var original = this.queryParam;
            var operator = this.operator()[0];
            var userSpecified = this.specificationType()[0] === 'user_specified';
            var expression;
            var value;
            if (this._operatorNeedsValueComponent()) {
                if (this.runtimeMode || !userSpecified) {
                    value = this.fieldValueEditorViewModel.value();
                    if (AbcsLib.isArray(value)) {
                        value = value.length && value[0] || undefined;
                    }
                }
                var expressionHandler = this._helper.createExpressionHandler(this.fieldValueEditorViewModel.getValueOptions());
                expressionHandler.handleValue(value);
                if (!original) {
                    value = expressionHandler.getValue();
                }
                expression = expressionHandler.getExpression();
            }

            var queryParamDesc;
            var placeholder;
            if (original) {
                queryParamDesc = new QueryParameterDescription(property, undefined, original.getParameterType(), original.isRequired(), original.getAdditionalInfo());
                placeholder = original.getValuePlaceholder();
            } else {
                queryParamDesc = new QueryParameterDescription(property);
                placeholder = '';
            }

            parameter = new SimpleCondition(queryParamDesc, operator, value, expression, this.type, placeholder, userSpecified);
        }
        return parameter;
    };

    QueryParameterViewModel.prototype.dispose = function () {
        this._disposed = true;
        this.fieldValueEditorViewModel.dispose();
    };

    QueryParameterViewModel.prototype.getProperty = function () {
        return this.property();
    };

    /**
     * Returns actual value.
     *
     * <p>
     * If value is the same as placeholder value, this method returns undefined,
     * otherwise it returns current value.
     * </p>
     *
     * @returns {String}
     */
    QueryParameterViewModel.prototype.getValue = function() {
        return this.fieldValueEditorViewModel.getValue();
    };

    QueryParameterViewModel.prototype.executableExpressionsSupported = function () {
        return this._options.executableExpressionsAllowed && this._operatorNeedsValueComponent();
    };

    QueryParameterViewModel.prototype._isEditableInUI = function () {
        var userSpecified = this.specificationType()[0] === 'user_specified';
        // Flag to indicate whether the parameter is editable in the current UI context.
        return this.editable && (this.enableUserInput || !this.runtimeMode || (this.runtimeMode && userSpecified));
    };

    QueryParameterViewModel.prototype.removableParam = function () {
        return this._options.removableParam !== undefined ? this._options.removableParam : true;
    };

    QueryParameterViewModel.prototype._getSupportedOperators = function (property) {
        var supportedOperators = [];
        var queryParam = this.queryParam;
        if (queryParam && queryParam._queryParamDescription) {
            supportedOperators = queryParam._queryParamDescription.getSupportedOperators();
        } else {
            supportedOperators = QueryParameterDescription.getDefaultOperators(property);
        }

        return this._getLabelValuePairs(supportedOperators);
    };

    QueryParameterViewModel.prototype._getLabelValuePairs = function (operators) {
        return operators.map(function(operator) {
            var label;
            switch (operator) {
                case Operator.EQUALS:
                    label = AbcsLib.i18n('components.queryBuilderEquals');
                    break;
                case Operator.NOT_EQUALS:
                    label = AbcsLib.i18n('components.queryBuilderNotEquals');
                    break;
                case Operator.LESS:
                    label = AbcsLib.i18n('components.queryBuilderLess');
                    break;
                case Operator.LESS_OR_EQUAL:
                    label = AbcsLib.i18n('components.queryBuilderLessOrEqual');
                    break;
                case Operator.MORE:
                    label = AbcsLib.i18n('components.queryBuilderMore');
                    break;
                case Operator.MORE_OR_EQUAL:
                    label = AbcsLib.i18n('components.queryBuilderMoreOrEqual');
                    break;
                case Operator.STARTS_WITH:
                    label = AbcsLib.i18n('components.queryBuilderStartsWith');
                    break;
                case Operator.ENDS_WITH:
                    label = AbcsLib.i18n('components.queryBuilderEndsWith');
                    break;
                case Operator.CONTAINS:
                    label = AbcsLib.i18n('components.queryBuilderContains');
                    break;
                case Operator.NOT_CONTAINS:
                    label = AbcsLib.i18n('components.queryBuilderNotContains');
                    break;
                case Operator.IS_EMPTY:
                    label = AbcsLib.i18n('components.queryBuilderIsEmpty');
                    break;
                case Operator.IS_NOT_EMPTY:
                    label = AbcsLib.i18n('components.queryBuilderIsNotEmpty');
                    break;
                default:
                    throw new Error('Operator with name \'' + operator + '\' don\'t have any translation label assigned. Please report an issue against Visual Builder team.');
            }
            return {
                value: operator,
                label: label
            };
        });
    };

    QueryParameterViewModel.prototype._chooseProperOperator = function () {
        var operator = this.operator()[0];
        var operators = this.qoOptions();
        var validOperator;
        for (var i = 0, length = operators.length; i < length && !validOperator; ++i) {
            if (operator === operators[i].value) {
                validOperator = true;
            }
        }
        if (!validOperator) {
            this.operator([operators[0].value]);
        }
    };

    QueryParameterViewModel.prototype._initModel = function () {
        var val = '';
        if (this.queryParam) {
            val = this.queryParam.getValue();
            var expression = this.queryParam.getValueExpression();
            if (val === undefined || val === null || val === '' || expression) {
                val = expression && expression.getValue();
            }
            if (expression) {
                this.fieldValueEditorViewModel.expressionDisplayName(expression.getDisplayName());
                expression = expression.getValue();
                if (expression && expression.indexOf('=') === 0) {
                    this.fieldValueEditorViewModel.valueMode(FieldValueEditorValueMode.EXPRESSION);
                }
            }
            this.fieldValueEditorViewModel.placeHolder(this.queryParam.getValuePlaceholder());
            this.fieldValueEditorViewModel.value(val);
        }
        this.fieldValueEditorViewModel.chooseValueComponent();
        this.fieldValueEditorViewModel.value(val);
    };

    return QueryParameterViewModel;
});