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;
});