define([
'components/js/comboboxPicker/ComboboxPickerViewModel',
'components/js/fieldValueEditor/DefaultFieldValueEditorHelper',
'components/js/fieldValueEditor/FieldValueEditorUtils',
'components/js/fieldValueEditor/FieldValueEditorValueMode',
'core/js/api/Listenable',
'entity/js/api/PropertyType',
'text!components/templates/fieldValueEditor/fieldValueEditorResources.html',
'template!components/templates/fieldValueEditor/fieldValueEditor.html',
'template!components/js/comboboxPicker/ComboboxPickerTemplate.html'
], function (
ComboboxPickerViewModel,
defaultFieldValueEditorHelper,
FieldValueEditorUtils,
FieldValueEditorValueMode,
Listenable,
PropertyType,
fieldValueEditorResourcesMarkup
) {
'use strict';
var markup = $(fieldValueEditorResourcesMarkup);
$('body').append(markup);
var _RANDOM_USAGE_ID = 0;
/**
* Usage example:
*
* In JavaScript:
* function MyViewModel(sourceEntity, targetEntity) {
* this.sourceEntity = sourceEntity;
* this.targetEntity = targetEntity;
* this.myInputInfo = {
* propertyId: ko.observableArray(['somePropertyId']),
* invalidComponentTracker: ko.observable(),
* valueMode: ko.observable(FieldValueEditorValueMode.STATIC),
* value: ko.observable()
* };
* }
*
* In HTML:
* <field-value-editor params="sourceEntity sourceEntity,
* targetEntity: targetEntity,
* propertyId: myInputInfo.propertyId,
* invalidComponentTracker: myInputInfo.invalidComponentTracker,
* valueMode: myInputInfo.valueMode,
* value: myInputInfo.value"></field-value-editor>
*
* In JavaScript:
* function MyViewModel(entity) {
* this.entity = entity;
* this.myInputInfo = {
* targetPropertyType: ko.observable(PropertyType.TEXT),
* valueMode: ko.observable(FieldValueEditorValueMode.STATIC),
* value: ko.observable()
* };
* }
*
* In HTML:
* <field-value-editor params="sourceEntity: entity,
* targetPropertyType: myInputInfo.targetPropertyType,
* valueMode: myInputInfo.valueMode,
* value: myInputInfo.value"></field-value-editor>
*
*/
var FieldValueEditorViewModel = function (params) {
AbcsLib.checkThis(this);
// AbcsLib.checkDefined(params.sourceEntity, 'params.sourceEntity');
// AbcsLib.checkDefined(params.targetEntity || params.targetPropertyType, 'params.targetEntity or params.targetPropertyType');
if (params.targetEntity) {
AbcsLib.checkDefined(params.propertyId || params.property, 'params.propertyId or params.property');
}
if (params.executableExpressionsAllowed) {
AbcsLib.checkDefined(params.expressionEditorTemplateName, 'params.expressionEditorTemplateName');
}
Listenable.apply(this);
var self = this;
this.sourceEntity = ko.unwrap(params.sourceEntity); // RHS entity
this.targetEntity = ko.unwrap(params.targetEntity); // LHS entity
this.targetPropertyType = params.targetPropertyType; // LHS property type
this.propertyId = params.propertyId;
this.invalidComponentTracker = params.invalidComponentTracker ? params.invalidComponentTracker : ko.observable();
this.validators = params.validators ? params.validators : [];
this.valueMode = params.valueMode ? params.valueMode : ko.observable(FieldValueEditorValueMode.STATIC);
this.expressionEditorTemplateName = params.expressionEditorTemplateName;
this.executableExpressionsAllowed = params.executableExpressionsAllowed;
this.getReferenceValueCallback = params.getReferenceValueCallback;
this.helper = params.fieldValueEditorHelper || defaultFieldValueEditorHelper;
this.runtimeMode = params.runtimeMode;
this.referenceOptionAllowed = params.referenceOptionAllowed;
this.editable = params.editable ? params.editable : ko.observable(true);
this.visible = params.visible ? params.visible : ko.observable(true);
this.allReferenceOptions = ko.utils.unwrapObservable(params.referenceOptions);
this.lookupValueRequired = !!params.lookupValueRequired;
this.properties = params.properties ? params.properties : ko.observable(this.targetEntity && this.targetEntity.getProperties());
// If value passed in is an observable, use that
if (params.value) {
this._value = params.value;
} else {
this._value = ko.observable();
}
this.loadData = params.loadData;
if (params.property) {
this.property = params.property;
} else {
this.property = ko.computed(function () {
var selectedId = this.propertyId && this.propertyId()[0];
var selected;
if (selectedId) {
$.each(this.properties(), function (index, prop) {
if (selectedId === prop.getId()) {
selected = prop;
return false;
}
});
}
return selected;
}, this);
}
this.referenceValue = ko.observable();
this.placeHolder = ko.observable();
this.filteredReferenceOptions = ko.observableArray();
this.expressionEditor = ko.observable();
this.property.subscribe(function () {
self.chooseValueComponent();
self.fireEvent(FieldValueEditorViewModel.EVENT_VALUE_CHANGED);
});
this.targetPropertyType && this.targetPropertyType.subscribe(function () {
self.chooseValueComponent();
self.fireEvent(FieldValueEditorViewModel.EVENT_VALUE_CHANGED);
});
this.visible.subscribe(function () {
self.chooseValueComponent();
});
this.usageId = 'usage' + _RANDOM_USAGE_ID++;
this.value = ko.pureComputed({
read: function () {
var value = self._value();
if (value && self.valueMode() === FieldValueEditorValueMode.EXPRESSION &&
value.indexOf('=') !== 0) {
value = '=' + value;
}
return value;
},
write: function (value) {
self._value(value);
}
});
this.value.subscribe(function () {
self.fireEvent(FieldValueEditorViewModel.EVENT_VALUE_CHANGED);
});
this.editableInUI = ko.pureComputed(function () {
var valueMode = this.valueMode();
return this.editable() && (valueMode !== FieldValueEditorValueMode.EXPRESSION);
}, this);
this.expressionDisplayName = ko.observable('');
this.ojComponentType = ko.observable('ojInputText');
this.valueMode.subscribe(function () {
self.chooseValueComponent();
});
this.displayValueComponent = ko.observable(this.visible());
this.displayName = ko.pureComputed(function () {
var value = this.expressionDisplayName() || this.value();
if (this.referenceObjectValue) {
value = this.referenceObjectValue()[this._relation.getDefaultDisplayProperty()] || value;
}
return value;
}, this);
this.valueModeLabel = ko.pureComputed(function () {
var valueMode = this.valueMode();
if (valueMode === FieldValueEditorValueMode.EXPRESSION) {
return AbcsLib.i18n('components.fieldValueEditorExpression');
} else if (valueMode === FieldValueEditorValueMode.STATIC) {
return AbcsLib.i18n('components.fieldValueEditorStaticValue');
} else if (valueMode === FieldValueEditorValueMode.REFERENCE) {
return AbcsLib.i18n('components.fieldValueEditorReference');
} else {
return null;
}
}, this);
this.openExpressionBuilder = function() {
self._openExpressionEditor(FieldValueEditorValueMode.EXPRESSION);
};
this.menuItemSelect = function (event, ui) {
var mode = ui.item.attr('data-item-mode');
if (mode) {
var previousMode = self.valueMode();
self.switchMode(mode);
if (mode === FieldValueEditorValueMode.EXPRESSION) {
self._openExpressionEditor(previousMode);
}
}
};
// keyup event binding is used only for ojInputNumber
this.onKeyup = function (viewModel, event) {
var inputElem = $(event.target).parent().find('input');
inputElem.ojInputNumber('validate');
};
// onOptionChange binding is used for ojInputText.
this.onOptionChange = function (event, data) {
if (data.option === 'rawValue') {
var inputElem = $(event.target).parent().find('input');
inputElem.ojInputText('validate');
}
};
this.chooseValueComponent(true);
};
AbcsLib.extend(FieldValueEditorViewModel, Listenable);
FieldValueEditorViewModel.FieldValueEditorValueMode = FieldValueEditorValueMode; // Access to .html
FieldValueEditorViewModel.EVENT_VALUE_CHANGED = 'valueChanged';
FieldValueEditorViewModel.prototype.dispose = function () {
this._disposed = true;
this._disposeLOVPicker();
};
FieldValueEditorViewModel.prototype._openExpressionEditor = function (previousMode) {
var self = this;
if (this.executableExpressionsAllowed) {
var value = FieldValueEditorViewModel._removeStartingEqualSign(this.value());
var initialValueEditorModel = this.helper.createExpressionModel(
this.sourceEntity,
this.property(),
function (newInitialValue) {
self.value('=' + newInitialValue);
},
value);
this.expressionEditor(initialValueEditorModel);
initialValueEditorModel && initialValueEditorModel.open().then(function (finished) {
self.expressionEditor(undefined);
if (!finished) {
self.switchMode(previousMode);
}
});
}
};
FieldValueEditorViewModel.prototype.switchMode = function (mode) {
if (mode !== this.valueMode()) {
this.value('');
this.displayValueComponent(false);
}
this.valueMode(mode);
this.displayValueComponent(this.visible());
};
FieldValueEditorViewModel.prototype.chooseValueComponent = function (isInit) {
var oldComponentType = this.ojComponentType();
var propertyType = this._getTargetPropertyType();
this.displayValueComponent(false);
this._updateExtraValueModel();
var resetValue = false;
var componentType = FieldValueEditorUtils.getComponentType(this.valueMode(), propertyType, this._relation);
if (!componentType) {
componentType = 'ojInputText';
} else if (componentType === 'abcsComboBox') {
resetValue = !this.value();
} else if (componentType === 'ojSelect-reference') {
var filtered = this._filterReferenceOptions();
this.filteredReferenceOptions.removeAll();
ko.utils.arrayPushAll(this.filteredReferenceOptions, filtered);
}
if (oldComponentType !== componentType) {
if (!isInit || resetValue) {
this.value('');
}
// this is necessary, without first removing the value are
// and readding it again the components are incorrectly painted
// Changing the component does not work, some artefacts still remain
// in the DOM.
this.ojComponentType(componentType);
}
this.displayValueComponent(this.visible());
};
/**
* Get the target property type from targetEntity or targetPropertyType. If both are specified, targetPropertyType is used.
*/
FieldValueEditorViewModel.prototype._getTargetPropertyType = function () {
if (this.targetPropertyType) {
return this.targetPropertyType();
} else if (this.targetEntity) {
var prop = this.property();
return prop ? prop.getType() : PropertyType.TEXT; // If fieldId has not been specified, use ojInputText as the placeholder input component.
} else {
return PropertyType.TEXT;
}
};
FieldValueEditorViewModel.prototype.getValueOptions = function () {
return this.extraModel && this.extraModel.valueOptions || [];
};
FieldValueEditorViewModel._removeStartingEqualSign = function (value) {
if (value && typeof value === 'string') {
value = value.trim();
if (value.indexOf('=') === 0) {
value = value.substr(1);
}
}
return value;
};
var ReferenceValueModel = function (parent, referenceValue) {
var self = this;
self.valueOption = ko.observableArray([parent.value()]);
self.valueOptions = referenceValue ? [referenceValue] : [];
self.valueOption.subscribe(function (newVal) {
if (newVal instanceof Array) {
if (newVal.length) {
newVal = newVal[0];
} else {
newVal = '';
}
}
parent.value(newVal);
});
};
FieldValueEditorViewModel.prototype._updateExtraValueModel = function () {
var previousReferenceValue = this.referenceValue();
var property = this.property();
var referenceValue = this.getReferenceValueCallback ?
this.getReferenceValueCallback(property) : this.helper.getReferenceFor(property);
if (previousReferenceValue && !referenceValue) {
this.extraModel = undefined;
this.value('');
}
if (this.savedProperty && property && this.savedProperty !== property && property.getType() === PropertyType.REFERENCE) {
this.value('');
}
this.savedProperty = property;
if (!previousReferenceValue && referenceValue) {
this.extraModel = new ReferenceValueModel(this, referenceValue);
}
this.referenceValue(referenceValue);
this._initLOVModel();
};
FieldValueEditorViewModel.prototype._initLOVModel = function () {
var property = this.property();
var initAsLOV;
var relation = FieldValueEditorUtils.getRelation(this.runtimeMode, property);
if (relation) {
initAsLOV = true;
}
if (this._relation) {
if (!initAsLOV) {
this._disposeLOVPicker();
} else if (this.valueMode() === FieldValueEditorValueMode.EXPRESSION) {
// already initialized
initAsLOV = false;
}
}
if (initAsLOV) {
this._relation = relation;
this.referenceObjectValue = ko.observable();
this.extraModel = new ComboboxPickerViewModel({
relation: this._relation,
value: this.value,
required: this.lookupValueRequired,
objectValue: this.referenceObjectValue,
placeholder: this.placeHolder() || AbcsLib.i18n('components.comboBoxSelectValue'),
loadData: this.loadData
});
}
};
FieldValueEditorViewModel.prototype._disposeLOVPicker = function () {
if (this._relation) {
this.extraModel.dispose();
this.extraModel = undefined;
this.value('');
this.referenceObjectValue = undefined;
this._relation = undefined;
}
};
FieldValueEditorViewModel.prototype._filterReferenceOptions = function () {
if (!this.allReferenceOptions) {
this.allReferenceOptions = FieldValueEditorUtils.getFieldReferenceOptions(this.sourceEntity);
}
var lhsProperty = this.property(), rhsProperty;
var lhsEntityId, rhsEntityId, option, options = [];
var textSupportedPropertyTypes = [PropertyType.CURRENCY,
PropertyType.EMAIL,
PropertyType.NUMBER,
PropertyType.PERCENTAGE,
PropertyType.PHONE,
PropertyType.URL];
var dateSupportedPropertyTypes = [PropertyType.DATE, PropertyType.TIME];
for (var i = 0; i < this.allReferenceOptions.length; i++) {
option = this.allReferenceOptions[i];
rhsProperty = option.property;
if (lhsProperty && lhsProperty.getType() === rhsProperty.getType()) {
if (lhsProperty.getType() === PropertyType.REFERENCE) {
lhsEntityId = FieldValueEditorUtils.getReferencedEntityFromProperty(lhsProperty).getId();
rhsEntityId = FieldValueEditorUtils.getReferencedEntityFromProperty(rhsProperty).getId();
if (lhsEntityId === rhsEntityId) {
options.push(option);
}
} else {
options.push(option);
}
} else if (lhsProperty && lhsProperty.getType() === PropertyType.REFERENCE && rhsProperty.getType() === PropertyType.KEY) {
lhsEntityId = FieldValueEditorUtils.getReferencedEntityFromProperty(lhsProperty).getId();
rhsEntityId = rhsProperty.getEntity().getId();
if (lhsEntityId === rhsEntityId) {
options.push(option);
}
} else if (!lhsProperty) {
if (this.targetPropertyType() === rhsProperty.getType()
|| this.targetPropertyType() === PropertyType.TEXT && textSupportedPropertyTypes.indexOf(rhsProperty.getType()) !== -1
|| this.targetPropertyType() === PropertyType.DATETIME && dateSupportedPropertyTypes.indexOf(rhsProperty.getType()) !== -1) {
options.push(option);
}
}
}
return options;
};
return FieldValueEditorViewModel;
});