/*
* Copyright (C) eZ Systems AS. All rights reserved.
* For full copyright and license information view LICENSE file distributed with this source code.
*/
YUI.add('ez-author-editview', function (Y) {
"use strict";
/**
* Provides the field edit view for the Author (ezauthor) fields
*
* @module ez-author-editview
*/
Y.namespace('eZ');
var FIELDTYPE_IDENTIFIER = 'ezauthor',
AUTHOR_INPUT_SECONDARY = 'ez-author-secondary',
IS_NAME_ERROR = 'is-name-error',
IS_EMAIL_ERROR = 'is-email-error',
AuthorInputView, AuthorList, Author;
/**
* Internal author model
*
* @private
* @constructor
* @class Author
* @extends Model
*/
Author = Y.Base.create('authorModel', Y.Model, [], {}, {
ATTRS: {
/**
* Stores the name of the author
*
* @attribute name
* @type String
* @default ""
*/
name: {
value: ""
},
/**
* Stores the email of the author
*
* @attribute email
* @type String
* @default ""
*/
email: {
value: ""
},
/**
* Stores the email validity of the author's email
*
* @attribute emailValid
* @type Boolean
* @default false
*/
emailValid: {
value: false
}
}
});
/**
* Internal author models list
*
* @private
* @constructor
* @class AuthorList
* @extends ModelList
*/
AuthorList = Y.Base.create('authorModelList', Y.ModelList, [], {model: Author});
/**
* Internal author input view. The author input views are responsible for
* handling one author.
*
* @private
* @constructor
* @class AuthorInputView
* @extends eZ.FieldEditView
*/
AuthorInputView = Y.Base.create('authorInputView', Y.eZ.FieldEditView, [], {
events: {
'.ez-field-author-remove': {
'tap': '_removeSelf'
},
'.ez-field-author-email': {
'blur': '_validateEmail'
},
'.ez-field-author-name': {
'blur': '_validateName'
}
},
initializer: function () {
this.after('canRemoveChange', this._uiHandleCanRemove);
this._uiHandleShowInfos();
this.after('showInfosChange', this._uiHandleShowInfos);
},
/**
* Enables/disables the remove author button depending on the
* `canRemove` attribute value
*
* @method _uiHandleCanRemove
* @protected
*/
_uiHandleCanRemove: function () {
var button = this.get('container').one('.ez-field-author-remove');
if ( button ) {
button.set('disabled', !this.get('canRemove'));
}
},
/**
* Reflects in the DOM the `showInfos` attribute value
*
* @method _uiHandleShowInfos
* @protected
*/
_uiHandleShowInfos: function () {
var container = this.get('container');
if ( !this.get('showInfos') ) {
container.addClass(AUTHOR_INPUT_SECONDARY);
} else {
container.removeClass(AUTHOR_INPUT_SECONDARY);
}
},
_variables: function () {
return {
author: this.get('author').toJSON(),
canRemove: this.get('canRemove'),
isRequired: this.get('required'),
isNotTranslatable: this.get('isNotTranslatable')
};
},
/**
* Validates the filled author after the name has been changed
*
* @method _validateName
* @protected
*/
_validateName: function (e) {
this.get('author').set('name', e.currentTarget.get('value'));
this.validate();
},
/**
* Validates the filled author after the email has been changed
*
* @method _validateEmail
* @protected
*/
_validateEmail: function (e) {
var author = this.get('author'),
input = e.currentTarget,
email = input.get('value'),
emailValid = input.get('validity').valid;
author.set('email', email);
author.set('emailValid', emailValid);
this.validate();
},
/**
* Validates the current author
*
* @method validate
*/
validate: function () {
var author = this.get('author'),
hasName = (author.get('name') !== ''),
hasEmail = (author.get('email') !== ''),
emailValid = author.get('emailValid'),
required = this.get('required'),
errorStatus = {};
if ( hasEmail && !emailValid ) {
errorStatus.email = Y.eZ.trans('email.not.valid', {}, 'fieldedit');
}
if ( hasName && !hasEmail ) {
errorStatus.email = Y.eZ.trans('email.required.if.name.filled', {}, 'fieldedit');
}
if ( hasEmail && !hasName ) {
errorStatus.name = Y.eZ.trans('name.required.if.email.filled', {}, 'fieldedit');
}
if ( required && !hasName && !hasEmail ) {
errorStatus = {
name: Y.eZ.trans('name.required', {}, 'fieldedit'),
email: Y.eZ.trans('email.required', {}, 'fieldedit'),
};
} else if ( hasName && hasEmail && emailValid ) {
errorStatus = {email: false, name: false};
}
if ( hasEmail && emailValid ) {
errorStatus.email = false;
}
if ( hasName ) {
errorStatus.name = false;
}
this.set('errorStatus', errorStatus);
},
/**
* Checks whether the currently entered author is valid. Overrides the
* default implementation to take into account the case where
* errorStatus contains an object reporting no validation error on both
* the email and the name.
*
* @method isValid
* @return Boolean
*/
isValid: function () {
var errorStatus = this.get('errorStatus');
return (
errorStatus === false
|| (!errorStatus.email && !errorStatus.name)
);
},
/**
* Reflects in the UI the error status value
*
* @method _errorUI
* @protected
*/
_errorUI: function (e) {
var container = this.get('container'),
errorName = container.one('.ez-editfield-error-name'),
errorEmail = container.one('.ez-editfield-error-email'),
errorStatus = e.newVal;
if ( errorStatus && errorStatus.name ) {
container.addClass(IS_NAME_ERROR);
errorName.setContent(errorStatus.name);
} else {
container.removeClass(IS_NAME_ERROR);
errorName.setContent('');
}
if ( errorStatus && errorStatus.email ) {
container.addClass(IS_EMAIL_ERROR);
errorEmail.setContent(errorStatus.email);
} else {
container.removeClass(IS_EMAIL_ERROR);
errorEmail.setContent('');
}
},
/**
* Tap event handler for the remove author button
*
* @method _removeSelf
* @protected
*/
_removeSelf: function (e) {
e.preventDefault();
/**
* Fired when the user clicks on the remove author button
*
* @event authorRemove
* @param {Author} author the author model attached to the view
*/
this.fire('authorRemove', {author: this.get('author')});
},
}, {
ATTRS: {
/**
* Stores whether the author can be removed
*
* @attribute canRemove
* @type Boolean
* @default false
*/
canRemove: {
value: false
},
/**
* Stores whether the author is required
*
* @attribute required
* @type Boolean
* @default false
*/
required: {
value: false
},
/**
* Stores whether the field definition name should displayed
*
* @attribute showInfos
* @type Boolean
* @default true
*/
showInfos: {
value: true
},
/**
* The author model attached to this view
*
* @attribute author
* @type Author
* @default undefined
*/
author: {},
/**
* The content model the field belongs to
*
* @attribute content
* @type eZ.Content
* @default undefined
*/
content: {},
/**
* The field definition
*
* @attribute fieldDefinition
* @type Object
* @default undefined
*/
fieldDefinition: {},
}
});
/**
* Author edit view
*
* @namespace eZ
* @class AuthorEditView
* @constructor
* @extends eZ.FieldEditView
*/
Y.eZ.AuthorEditView = Y.Base.create('authorEditView', Y.eZ.FieldEditView, [], {
events: {
'.ez-field-author-add': {
'tap': '_addAuthor'
}
},
/**
* Overrides the default error class defined in eZ.FieldEditView
*
* @property _errorClass
* @protected
* @type String
* @default is-author-error
*/
_errorClass: 'is-author-error',
initializer: function () {
/**
* Stores whether the edit view is rendered
*
* @property _rendered
* @type Boolean
* @default false
* @protected
*/
this._rendered = false;
/**
* Array of the AuthorInputView which render each author in the
* author model list
*
* @property _authorInputs
* @type Array of AuthorInputView
* @default []
* @protected
*/
this._authorInputs = [];
/**
* List of authors to render
*
* @property _authorList
* @type AuthorList
* @default an instance of AuthorList
* @protected
*/
this._authorList = new AuthorList();
this._authorList.after('add', Y.bind(this._uiAddAuthor, this));
this._authorList.after('remove', Y.bind(this._uiRemoveAuthor, this));
this._fillAuthorList();
this.on('*:authorRemove', this._removeAuthor);
this.after('*:errorStatusChange', this._setErrorStatus);
},
destructor: function () {
this._rendered = false;
this._authorList.destroy();
Y.Array.each(this._authorInputs, function (input) {
input.destroy();
});
this._authorInputs = [];
this._authorList.add({id: 0});
},
/**
* Fills the author list property based on the author available in the
* field value
*
* @protected
* @method _fillAuthorList
*/
_fillAuthorList: function () {
var authors = this.get('field').fieldValue,
user = this.get('user');
if ( authors.length === 0 ) {
if (this.get('content').isNew()) {
this._authorList.add({id: 0, name: user.get('name'), email: user.get('email'), emailValid: true});
} else {
this._authorList.add({id: 0});
}
} else {
Y.Array.each(authors, function (author) {
var a = {
id: parseInt(author.id, 10),
name: author.name,
email: author.email,
emailValid: true
};
this._authorList.add(a);
}, this);
}
},
/**
* Author list add event handler. Reflects in the UI the newly added
* author.
*
* @method _uiAddAuthor
* @protected
* @param {Object} e the add event facade
*/
_uiAddAuthor: function (e) {
var list = this._authorList,
canRemove = (list.size() > 1),
showInfos = (list.size() === 1),
inputView;
inputView = new AuthorInputView({
author: e.model,
canRemove: canRemove,
content: this.get('content'),
version: this.get('version'),
contentType: this.get('contentType'),
fieldDefinition: this.get('fieldDefinition'),
showInfos: showInfos,
required: (this.get('fieldDefinition').isRequired && !this._hasContent),
isNotTranslatable: this.get('isNotTranslatable'),
translating: this.get('translating')
});
this._authorInputs.push(inputView);
inputView.addTarget(this);
if ( this._rendered ) {
this._renderAuthorInput(inputView);
}
if ( list.size() === 2 ) {
this._authorInputs[0].set('canRemove', true);
}
},
/**
* Author list remove event handler. Reflects in the UI the removed
* author
*
* @method _uiRemoveAuthor
* @protected
* @param {Object} e the remove event facade
*/
_uiRemoveAuthor: function (e) {
this._authorInputs = Y.Array.filter(this._authorInputs, function (view) {
if ( view.get('author') === e.model ) {
this._removeAuthorInputView(view);
return false;
}
return true;
}, this);
if ( e.target.size() === 1 ) {
this._authorInputs[0].set('canRemove', false);
}
this._authorInputs[0].set('showInfos', true);
this._setRequiredFlag();
this.validate();
},
/**
* Custom removeAuthor event handler. Removes an author (provided in the
* event facade) from the author list
*
* @method _removeAuthor
* @protected
* @param {Object} e removeAuthor event facade
*/
_removeAuthor: function (e) {
this._authorList.remove(e.author);
},
/**
* errorStatusChange event handler. "Forwards" the error status change
* of the author input view to the author edit view.
*
* @method _setErrorStatus
* @protected
* @param {Object} e errorStatusChange event facade
*/
_setErrorStatus: function (e) {
this.set(
'errorStatus',
!Y.Array.every(this._authorInputs, function (view) {
return view.isValid();
})
);
this._setRequiredFlag();
},
/**
* Removes the author input view instance from the DOM
*
* @method _removeAuthorInputView
* @protected
*/
_removeAuthorInputView: function (view) {
view.removeTarget(this);
view.get('container').remove();
view.destroy();
},
/**
* Sets the required flag of the author input views depending on the
* field definition configuration and on the current input
*
* @method _setRequiredFlag
* @protected
*/
_setRequiredFlag: function () {
var fieldRequired = this.get('fieldDefinition').isRequired,
hasContent = this._hasContent();
Y.Array.each(this._authorInputs, function (view) {
view.set('required', !hasContent && fieldRequired);
});
},
/**
* Validates the current input
*
* @method validate
*/
validate: function () {
Y.Array.each(this._authorInputs, function (view) {
view.validate();
});
},
/**
* Checks that at least one author is correctly filled.
*
* @method _hasContent
* @protected
* @return boolean
*/
_hasContent: function () {
return Y.Array.some(this._authorInputs, function (view) {
var author = view.get('author');
return (author.get('name') && author.get('email') && author.get('emailValid'));
});
},
/**
* Renders the view and its author input views
*
* @method render
* @return {eZ.AuthorEditView} the view it self
*/
render: function () {
this.constructor.superclass.render.apply(this);
this._renderAuthorInputs();
this._rendered = true;
return this;
},
/**
* Renders the author input views
*
* @method _renderAuthorInputs
* @protected
*/
_renderAuthorInputs: function () {
Y.Array.each(this._authorInputs, function (view) {
this._renderAuthorInput(view);
}, this);
},
/**
* Render the author input view in parameter in the correct container
*
* @method _renderAuthorInput
* @protected
*/
_renderAuthorInput: function (view) {
var inputContainer = this.get('container').one('.ez-authors-input-container');
inputContainer.append(view.render().get('container'));
},
/**
* Event handler for the tap event on the add author button. Adds a new
* author in the author list
*
* @method _addAuthor
* @protected
* @param {Object} e tap event facade
*/
_addAuthor: function (e) {
var id;
e.preventDefault();
id = this._authorList.item(this._authorList.size() - 1).get('id') + 1;
this._authorList.add({"id": id});
},
/**
* Defines the variables to imported in the field edit template for the
* url field
*
* @protected
* @method _variables
* @return {Object}
*/
_variables: function () {
return {};
},
/**
* Returns an array of the author based on the current user input. Only
* the valid authors are listed.
*
* @method _getFieldValue
* @protected
* @return Array
*/
_getFieldValue: function () {
var value = [];
if ( this._hasContent() ) {
this._authorList.each(function (author) {
var a = author.toJSON();
if ( a.emailValid ) {
delete a.emailValid;
value.push(a);
}
});
}
return value;
},
});
Y.eZ.FieldEditView.registerFieldEditView(
FIELDTYPE_IDENTIFIER, Y.eZ.AuthorEditView
);
});