'use strict';
var ToolTipField = require('br/presenter/node/ToolTipField');
var PropertyHelper = require('br/presenter/property/PropertyHelper');
var EditableProperty = require('br/presenter/property/EditableProperty');
var Errors = require('br/Errors');
var ToolTipNode = require('br/presenter/node/ToolTipNode');
/**
* @module br/presenter/util/ErrorMonitor
*/
/**
* @class
* @alias module:br/presenter/util/ErrorMonitor
*
* @classdesc
* ErrorMonitor is responsible for monitoring the status of fields that contain
* an error and failure message property. It is used to set the tool tip class that
* the tooltip control will scan in order to create the tool tip box.
*
* @param {module:br/presenter/node/ToolTipNode} Node representing the tool tip model.
*/
function ErrorMonitor(oTooltipNode) {
if (!oTooltipNode || !(oTooltipNode instanceof ToolTipNode)) {
throw new Errors.CustomError(Errors.INVALID_PARAMETERS, 'The ErrorMonitor has to be constructed with an instance of a br.presenter.node.ToolTipNode');
}
this.m_oPropertyHelper = new PropertyHelper();
this.oTooltipNode = oTooltipNode;
this.m_pErrorStack = [];
this.hasError = new EditableProperty(false);
}
/**
*
* Filters all nodes of type {@see br.presenter.node.ToolTipField} and monitors them using
* {@see br.presenter.util.ErrorMonitor#monitorField}
*
* @type {br.presenter.node.PresentationNode[]}
*/
ErrorMonitor.prototype.addErrorListeners = function(pGroups) {
var node;
for (var i = 0; i < pGroups.length; i++) {
node = pGroups[i];
if (node instanceof ToolTipField) {
this.monitorField(node);
}
}
};
/**
* removes all error subscriptions for the given fields
* @param {Array} pGroups fields we want to forget
*/
ErrorMonitor.prototype.removeErrorListeners = function(pGroups) {
for (var i = 0; i < pGroups.length; i++) {
if (pGroups[i].hasError && pGroups[i].failureMessage) {
this.forgetField(pGroups[i]);
}
}
};
ErrorMonitor.prototype.replaceErrorListeners = function(pGroups) {
this.removeAllErrors();
this.addErrorListeners(pGroups);
};
/**
* Monitors a Field to automatically add it as an error when it enters an error state and remove it once
* that error is resolved.
*
* @param {module:br/presenter/node/ToolTipField} oField
*/
ErrorMonitor.prototype.monitorField = function(oField) {
if (!(oField instanceof ToolTipField)) {
throw new Errors.CustomError(Errors.INVALID_PARAMETERS, 'The field to monitor has to be an instance of br.presenter.node.ToolTipField');
}
var oTicketErrorProperty = this;
var fValidationSuccessHandler = function() {
if (this.hasError.getValue() === true && oField.failureMessage.getValue() !== '') {
oTicketErrorProperty._addError(oField);
} else {
oTicketErrorProperty._removeError(this);
}
};
this.m_oPropertyHelper.addChangeListener(oField.hasError, oField, fValidationSuccessHandler);
this.m_oPropertyHelper.addValidationErrorListener(oField.value, oField, this._addError.bind(this, oField));
// We don't put a notify immediately on the add listeners as it triggers a force revalidation which is not needed
// for async validation
if (oField.hasError.getValue() && oField.failureMessage.getValue()) {
this._addError(oField);
}
};
/**
* Removes the automatic monitoring of the supplied Field, and removes it from the list of current errors
* if the field is currently in an error state.
*
* @param {module:br/presenter/node/Field} oField
*/
ErrorMonitor.prototype.forgetField = function(oField) {
this._removeError(oField);
this.m_oPropertyHelper.clearProperty(oField.hasError);
this.m_oPropertyHelper.clearProperty(oField.value);
};
ErrorMonitor.prototype.removeAllErrors = function() {
this.oTooltipNode.move(false);
this.m_pErrorStack = [];
this.m_oPropertyHelper.removeAllListeners();
this._removeLastError();
};
ErrorMonitor.prototype._addError = function(oField) {
this._removeFromStack(oField);
this.hasError.setValue(true);
var sFailureMessage = oField.failureMessage.getValue();
this.m_pErrorStack.push({
field: oField,
failureMessage: sFailureMessage
});
var nTopOfStack = this.m_pErrorStack.length;
if (nTopOfStack === 1) {
this._addTooltipToTopOfStack();
} else // if (this.m_pErrorStack.length > 1)
{
var oFieldWithTooltipToRemove = this.m_pErrorStack[nTopOfStack - 2].field;
this._moveTooltip(oFieldWithTooltipToRemove);
}
this.oTooltipNode.setMessage(sFailureMessage);
this._notifyObserversOfErrorChange();
};
ErrorMonitor.prototype._removeError = function(oField) {
var mFieldAndMessage = this._removeFromStack(oField);
if (mFieldAndMessage) {
var oFieldNoLongerInError = mFieldAndMessage.field;
if (this.m_pErrorStack.length > 0) {
this._updateTooltipOnRemove(oFieldNoLongerInError);
this._updateErrorMessage();
} else {
this._removeTooltipFrom(oFieldNoLongerInError);
this._removeLastError();
}
this._notifyObserversOfErrorChange();
}
};
/**
*
* @param oField
* @returns {*}
* @private
*/
ErrorMonitor.prototype._removeFromStack = function(oField) {
var nErrorStackLength = this.m_pErrorStack.length;
for (var i = 0; i < nErrorStackLength; ++i) {
if (this.m_pErrorStack[i].field == oField) {
return this.m_pErrorStack.splice(i, 1)[0];
}
}
};
/**
*
* @private
*/
ErrorMonitor.prototype._notifyObserversOfErrorChange = function() {
this.oTooltipNode.move(false);
this.oTooltipNode.move(this.m_pErrorStack.length !== 0);
};
/**
*
* @private
*/
ErrorMonitor.prototype._removeLastError = function() {
this.oTooltipNode.setMessage('');
this.hasError.setValue(false);
};
/**
*
* @private
*/
ErrorMonitor.prototype._updateErrorMessage = function() {
var oErrorMessage = this._getNextErrorMessage();
if (oErrorMessage) {
this.oTooltipNode.setMessage(oErrorMessage);
}
};
/**
* @returns {*}
* @private
*/
ErrorMonitor.prototype._getNextErrorMessage = function() {
var nErrorIndex = this.m_pErrorStack.length - 1;
return this.m_pErrorStack[nErrorIndex].failureMessage;
};
/**
*
* @param oField
* @private
*/
ErrorMonitor.prototype._updateTooltipOnRemove = function(oField) {
if (oField.tooltipClassName.getValue() !== '') {
this._moveTooltip(oField);
}
};
/**
*
* @param oField
* @private
*/
ErrorMonitor.prototype._moveTooltip = function(oField) {
this._removeTooltipFrom(oField);
this._addTooltipToTopOfStack();
};
/**
*
* @private
*/
ErrorMonitor.prototype._addTooltipToTopOfStack = function() {
var nTopOfStack = this.m_pErrorStack.length - 1;
var mTopOfStack = this.m_pErrorStack[nTopOfStack];
var oFieldAtTopOfStack = mTopOfStack.field;
this._addTooltipTo(oFieldAtTopOfStack);
};
/**
*
* @param oField
* @private
*/
ErrorMonitor.prototype._addTooltipTo = function(oField) {
if (oField.failureMessage.getValue() !== '') {
oField.tooltipClassName.setValue(this.oTooltipNode.getTooltipClassName());
}
};
/**
*
* @param oField
* @private
*/
ErrorMonitor.prototype._removeTooltipFrom = function(oField) {
oField.tooltipClassName.setValue('');
};
module.exports = ErrorMonitor;