Commit b6ed6e74 authored by webchick's avatar webchick
Browse files

Issue #2530764 by eiriksm: JSDoc quickedit module

parent 95757077
...@@ -32,7 +32,9 @@ ...@@ -32,7 +32,9 @@
* @inheritdoc * @inheritdoc
* *
* @param {object} fieldModel * @param {object} fieldModel
* The field model that holds the state.
* @param {string} state * @param {string} state
* The state to change to.
*/ */
stateChange: function (fieldModel, state) { stateChange: function (fieldModel, state) {
var from = fieldModel.previous('state'); var from = fieldModel.previous('state');
...@@ -80,6 +82,7 @@ ...@@ -80,6 +82,7 @@
* @inheritdoc * @inheritdoc
* *
* @return {object} * @return {object}
* A settings object for the quick edit UI.
*/ */
getQuickEditUISettings: function () { getQuickEditUISettings: function () {
return {padding: true, unifiedToolbar: true, fullWidthToolbar: true, popup: true}; return {padding: true, unifiedToolbar: true, fullWidthToolbar: true, popup: true};
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
* @augments Drupal.quickedit.EditorView * @augments Drupal.quickedit.EditorView
* *
* @param {object} options * @param {object} options
* Options for the plain text editor.
*/ */
initialize: function (options) { initialize: function (options) {
Drupal.quickedit.EditorView.prototype.initialize.call(this, options); Drupal.quickedit.EditorView.prototype.initialize.call(this, options);
...@@ -55,6 +56,7 @@ ...@@ -55,6 +56,7 @@
* @inheritdoc * @inheritdoc
* *
* @return {jQuery} * @return {jQuery}
* The text element for the plain text editor.
*/ */
getEditedElement: function () { getEditedElement: function () {
return this.$textElement; return this.$textElement;
...@@ -64,8 +66,11 @@ ...@@ -64,8 +66,11 @@
* @inheritdoc * @inheritdoc
* *
* @param {object} fieldModel * @param {object} fieldModel
* The field model that holds the state.
* @param {string} state * @param {string} state
* The state to change to.
* @param {object} options * @param {object} options
* State options, if needed by the state change.
*/ */
stateChange: function (fieldModel, state, options) { stateChange: function (fieldModel, state, options) {
var from = fieldModel.previous('state'); var from = fieldModel.previous('state');
...@@ -121,6 +126,7 @@ ...@@ -121,6 +126,7 @@
* @inheritdoc * @inheritdoc
* *
* @return {object} * @return {object}
* A settings object for the quick edit UI.
*/ */
getQuickEditUISettings: function () { getQuickEditUISettings: function () {
return {padding: true, unifiedToolbar: false, fullWidthToolbar: false, popup: false}; return {padding: true, unifiedToolbar: false, fullWidthToolbar: false, popup: false};
......
...@@ -15,8 +15,10 @@ ...@@ -15,8 +15,10 @@
* @augments Backbone.Model * @augments Backbone.Model
* *
* @param {object} options * @param {object} options
* Options for the base model-
* *
* @return {Drupal.quickedit.BaseModel} * @return {Drupal.quickedit.BaseModel}
* A quickedit base model.
*/ */
initialize: function (options) { initialize: function (options) {
this.__initialized = true; this.__initialized = true;
...@@ -24,12 +26,18 @@ ...@@ -24,12 +26,18 @@
}, },
/** /**
* Set a value on the model
* *
* @param {object|string} key * @param {object|string} key
* The key to set a value for.
* @param {*} val * @param {*} val
* The value to set.
* @param {object} [options] * @param {object} [options]
* Options for the model.
* *
* @return {*} * @return {*}
* The result of `Backbone.Model.prototype.set` with the specified
* parameters.
*/ */
set: function (key, val, options) { set: function (key, val, options) {
if (this.__initialized) { if (this.__initialized) {
......
...@@ -159,10 +159,12 @@ ...@@ -159,10 +159,12 @@
* Updates FieldModels' states when an EntityModel change occurs. * Updates FieldModels' states when an EntityModel change occurs.
* *
* @param {Drupal.quickedit.EntityModel} entityModel * @param {Drupal.quickedit.EntityModel} entityModel
* The entity model
* @param {string} state * @param {string} state
* The state of the associated entity. One of * The state of the associated entity. One of
* {@link Drupal.quickedit.EntityModel.states}. * {@link Drupal.quickedit.EntityModel.states}.
* @param {object} options * @param {object} options
* Options for the entity model.
*/ */
stateChange: function (entityModel, state, options) { stateChange: function (entityModel, state, options) {
var to = state; var to = state;
...@@ -193,7 +195,8 @@ ...@@ -193,7 +195,8 @@
case 'committing': case 'committing':
// The user indicated they want to save the entity. // The user indicated they want to save the entity.
var fields = this.get('fields'); var fields = this.get('fields');
// For fields that are in an active state, transition them to candidate. // For fields that are in an active state, transition them to
// candidate.
fields.chain() fields.chain()
.filter(function (fieldModel) { .filter(function (fieldModel) {
return _.intersection([fieldModel.get('state')], ['active']).length; return _.intersection([fieldModel.get('state')], ['active']).length;
...@@ -218,15 +221,15 @@ ...@@ -218,15 +221,15 @@
return _.intersection([fieldModel.get('state')], ['changed', 'invalid']).length; return _.intersection([fieldModel.get('state')], ['changed', 'invalid']).length;
}); });
// If the entity contains unconfirmed or unsaved changes, return the // If the entity contains unconfirmed or unsaved changes, return the
// entity to an opened state and ask the user if they would like to save // entity to an opened state and ask the user if they would like to
// the changes or discard the changes. // save the changes or discard the changes.
// 1. One of the fields is in a changed state. The changed field might // 1. One of the fields is in a changed state. The changed field
// just be a change in the client or it might have been saved to // might just be a change in the client or it might have been saved
// tempstore. // to tempstore.
// 2. The saved flag is empty and the confirmed flag is empty. If the // 2. The saved flag is empty and the confirmed flag is empty. If
// entity has been saved to the server, the fields changed in the // the entity has been saved to the server, the fields changed in
// client are irrelevant. If the changes are confirmed, then proceed // the client are irrelevant. If the changes are confirmed, then
// to set the fields to candidate state. // proceed to set the fields to candidate state.
if ((changedFields.length || this.get('fieldsInTempStore').length) && (!options.saved && !options.confirmed)) { if ((changedFields.length || this.get('fieldsInTempStore').length) && (!options.saved && !options.confirmed)) {
// Cancel deactivation until the user confirms save or discard. // Cancel deactivation until the user confirms save or discard.
this.set('state', 'opened', {confirming: true}); this.set('state', 'opened', {confirming: true});
...@@ -243,12 +246,12 @@ ...@@ -243,12 +246,12 @@
// Indicate if this EntityModel needs to be reloaded in order to // Indicate if this EntityModel needs to be reloaded in order to
// restore the original values of its fields. // restore the original values of its fields.
entityModel.set('reload', (this.get('fieldsInTempStore').length || invalidFields.length)); entityModel.set('reload', (this.get('fieldsInTempStore').length || invalidFields.length));
// Set all fields to the 'candidate' state. A changed field may have to // Set all fields to the 'candidate' state. A changed field may have
// go through confirmation first. // to go through confirmation first.
entityModel.get('fields').each(function (fieldModel) { entityModel.get('fields').each(function (fieldModel) {
// If the field is already in the candidate state, trigger a change // If the field is already in the candidate state, trigger a
// event so that the entityModel can move to the next state in // change event so that the entityModel can move to the next state
// deactivation. // in deactivation.
if (_.intersection([fieldModel.get('state')], ['candidate', 'highlighted']).length) { if (_.intersection([fieldModel.get('state')], ['candidate', 'highlighted']).length) {
fieldModel.trigger('change:state', fieldModel, fieldModel.get('state'), options); fieldModel.trigger('change:state', fieldModel, fieldModel.get('state'), options);
} }
...@@ -278,7 +281,8 @@ ...@@ -278,7 +281,8 @@
* Helper function. * Helper function.
* *
* @param {Drupal.quickedit.EntityModel} entityModel * @param {Drupal.quickedit.EntityModel} entityModel
* The model of the entity for which a field's state attribute has changed. * The model of the entity for which a field's state attribute has
* changed.
* @param {Drupal.quickedit.FieldModel} fieldModel * @param {Drupal.quickedit.FieldModel} fieldModel
* The model of the field whose state attribute has changed. * The model of the field whose state attribute has changed.
* *
...@@ -325,12 +329,12 @@ ...@@ -325,12 +329,12 @@
var entityModel = this; var entityModel = this;
var fieldState = state; var fieldState = state;
// Switch on the entityModel state. // Switch on the entityModel state.
// The EntityModel responds to FieldModel state changes as a function of its // The EntityModel responds to FieldModel state changes as a function of
// state. For example, a field switching back to 'candidate' state when its // its state. For example, a field switching back to 'candidate' state
// entity is in the 'opened' state has no effect on the entity. But that // when its entity is in the 'opened' state has no effect on the entity.
// same switch back to 'candidate' state of a field when the entity is in // But that same switch back to 'candidate' state of a field when the
// the 'committing' state might allow the entity to proceed with the commit // entity is in the 'committing' state might allow the entity to proceed
// flow. // with the commit flow.
switch (this.get('state')) { switch (this.get('state')) {
case 'closed': case 'closed':
case 'launching': case 'launching':
...@@ -339,13 +343,14 @@ ...@@ -339,13 +343,14 @@
break; break;
case 'opening': case 'opening':
// We must change the entity to the 'opened' state, but it must first be // We must change the entity to the 'opened' state, but it must first
// confirmed that all of its fieldModels have transitioned to the // be confirmed that all of its fieldModels have transitioned to the
// 'candidate' state. // 'candidate' state.
// We do this here, because this is called every time a fieldModel // We do this here, because this is called every time a fieldModel
// changes state, hence each time this is called, we get closer to the // changes state, hence each time this is called, we get closer to the
// goal of having all fieldModels in the 'candidate' state. // goal of having all fieldModels in the 'candidate' state.
// A state change in reaction to another state change must be deferred. // A state change in reaction to another state change must be
// deferred.
_.defer(function () { _.defer(function () {
entityModel.set('state', 'opened', { entityModel.set('state', 'opened', {
'accept-field-states': Drupal.quickedit.app.readyFieldStates 'accept-field-states': Drupal.quickedit.app.readyFieldStates
...@@ -374,7 +379,8 @@ ...@@ -374,7 +379,8 @@
// If the field save returned a validation error, set the state of the // If the field save returned a validation error, set the state of the
// entity back to 'opened'. // entity back to 'opened'.
if (fieldState === 'invalid') { if (fieldState === 'invalid') {
// A state change in reaction to another state change must be deferred. // A state change in reaction to another state change must be
// deferred.
_.defer(function () { _.defer(function () {
entityModel.set('state', 'opened', {reason: 'invalid'}); entityModel.set('state', 'opened', {reason: 'invalid'});
}); });
...@@ -383,8 +389,8 @@ ...@@ -383,8 +389,8 @@
this._updateInTempStoreAttributes(entityModel, fieldModel); this._updateInTempStoreAttributes(entityModel, fieldModel);
} }
// Attempt to save the entity. If the entity's fields are not yet all in // Attempt to save the entity. If the entity's fields are not yet all
// a ready state, the save will not be processed. // in a ready state, the save will not be processed.
var options = { var options = {
'accept-field-states': Drupal.quickedit.app.readyFieldStates 'accept-field-states': Drupal.quickedit.app.readyFieldStates
}; };
...@@ -399,8 +405,8 @@ ...@@ -399,8 +405,8 @@
error: function () { error: function () {
// Reset the "isCommitting" mutex. // Reset the "isCommitting" mutex.
entityModel.set('isCommitting', false); entityModel.set('isCommitting', false);
// Change the state back to "opened", to allow the user to hit the // Change the state back to "opened", to allow the user to hit
// "Save" button again. // the "Save" button again.
entityModel.set('state', 'opened', {reason: 'networkerror'}); entityModel.set('state', 'opened', {reason: 'networkerror'});
// Show a modal to inform the user of the network error. // Show a modal to inform the user of the network error.
var message = Drupal.t('Your changes to <q>@entity-title</q> could not be saved, either due to a website problem or a network connection problem.<br>Please try again.', {'@entity-title': entityModel.get('label')}); var message = Drupal.t('Your changes to <q>@entity-title</q> could not be saved, either due to a website problem or a network connection problem.<br>Please try again.', {'@entity-title': entityModel.get('label')});
...@@ -413,7 +419,8 @@ ...@@ -413,7 +419,8 @@
case 'deactivating': case 'deactivating':
// When setting the entity to 'closing', require that all fieldModels // When setting the entity to 'closing', require that all fieldModels
// are in either the 'candidate' or 'highlighted' state. // are in either the 'candidate' or 'highlighted' state.
// A state change in reaction to another state change must be deferred. // A state change in reaction to another state change must be
// deferred.
_.defer(function () { _.defer(function () {
entityModel.set('state', 'closing', { entityModel.set('state', 'closing', {
'accept-field-states': Drupal.quickedit.app.readyFieldStates 'accept-field-states': Drupal.quickedit.app.readyFieldStates
...@@ -422,9 +429,10 @@ ...@@ -422,9 +429,10 @@
break; break;
case 'closing': case 'closing':
// When setting the entity to 'closed', require that all fieldModels are // When setting the entity to 'closed', require that all fieldModels
// in the 'inactive' state. // are in the 'inactive' state.
// A state change in reaction to another state change must be deferred. // A state change in reaction to another state change must be
// deferred.
_.defer(function () { _.defer(function () {
entityModel.set('state', 'closed', { entityModel.set('state', 'closed', {
'accept-field-states': ['inactive'] 'accept-field-states': ['inactive']
...@@ -449,7 +457,7 @@ ...@@ -449,7 +457,7 @@
var entitySaverAjax = Drupal.ajax({ var entitySaverAjax = Drupal.ajax({
url: Drupal.url('quickedit/entity/' + entityModel.get('entityID')), url: Drupal.url('quickedit/entity/' + entityModel.get('entityID')),
error: function () { error: function () {
// Let the Drupal.quickedit.EntityModel Backbone model's error()= // Let the Drupal.quickedit.EntityModel Backbone model's error()
// method handle errors. // method handle errors.
options.error.call(entityModel); options.error.call(entityModel);
} }
...@@ -476,6 +484,7 @@ ...@@ -476,6 +484,7 @@
}, },
/** /**
* Validate the entity model.
* *
* @param {object} attrs * @param {object} attrs
* The attributes changes in the save or set call. * The attributes changes in the save or set call.
...@@ -492,6 +501,7 @@ ...@@ -492,6 +501,7 @@
* validate and proceed. * validate and proceed.
* *
* @return {string} * @return {string}
* A string to say something about the state of the entity model.
*/ */
validate: function (attrs, options) { validate: function (attrs, options) {
var acceptedFieldStates = options['accept-field-states'] || []; var acceptedFieldStates = options['accept-field-states'] || [];
...@@ -531,14 +541,21 @@ ...@@ -531,14 +541,21 @@
}, },
/** /**
* Checks if a state change can be accepted.
* *
* @param {string} from * @param {string} from
* From state.
* @param {string} to * @param {string} to
* To state.
* @param {object} context * @param {object} context
* Context for the check.
* @param {string} context.reason * @param {string} context.reason
* The reason for the state change.
* @param {bool} context.confirming * @param {bool} context.confirming
* Whether context is confirming or not.
* *
* @return {bool} * @return {bool}
* Whether the state change is accepted or not.
* *
* @see Drupal.quickedit.AppView#acceptEditorStateChange * @see Drupal.quickedit.AppView#acceptEditorStateChange
*/ */
...@@ -557,8 +574,8 @@ ...@@ -557,8 +574,8 @@
accept = true; accept = true;
} }
// Allow: committing -> opened. // Allow: committing -> opened.
// Necessary to be able to correct an invalid field, or to hit the "Save" // Necessary to be able to correct an invalid field, or to hit the
// button again after a server/network error. // "Save" button again after a server/network error.
else if (from === 'committing' && to === 'opened' && context.reason && (context.reason === 'invalid' || context.reason === 'networkerror')) { else if (from === 'committing' && to === 'opened' && context.reason && (context.reason === 'invalid' || context.reason === 'networkerror')) {
accept = true; accept = true;
} }
...@@ -578,9 +595,13 @@ ...@@ -578,9 +595,13 @@
}, },
/** /**
* Checks if fields have acceptable states.
*
* @param {Array} acceptedFieldStates * @param {Array} acceptedFieldStates
* An array of acceptable field states to check for.
* *
* @return {bool} * @return {bool}
* Whether the fields have an acceptable state.
* *
* @see Drupal.quickedit.EntityModel#validate * @see Drupal.quickedit.EntityModel#validate
*/ */
...@@ -603,7 +624,10 @@ ...@@ -603,7 +624,10 @@
}, },
/** /**
* Destroys the entity model.
*
* @param {object} options * @param {object} options
* Options for the entity model.
*/ */
destroy: function (options) { destroy: function (options) {
Drupal.quickedit.BaseModel.prototype.destroy.call(this, options); Drupal.quickedit.BaseModel.prototype.destroy.call(this, options);
...@@ -646,10 +670,11 @@ ...@@ -646,10 +670,11 @@
'launching', 'launching',
// Launching has finished. // Launching has finished.
// - Trigger: application. // - Trigger: application.
// - Guarantees: in-place editors ready for use, all entity and field views // - Guarantees: in-place editors ready for use, all entity and field
// have been set up, all fields are in the 'inactive' state. // views have been set up, all fields are in the 'inactive' state.
// - Expected behavior: all fields are changed to the 'candidate' state and // - Expected behavior: all fields are changed to the 'candidate' state
// once this is completed, the entity state will be changed to 'opened'. // and once this is completed, the entity state will be changed to
// 'opened'.
'opening', 'opening',
// Opening has finished. // Opening has finished.
// - Trigger: EntityModel. // - Trigger: EntityModel.
...@@ -668,14 +693,15 @@ ...@@ -668,14 +693,15 @@
// to 'opened', otherwise: save the entity by committing it from // to 'opened', otherwise: save the entity by committing it from
// PrivateTempStore into permanent storage. // PrivateTempStore into permanent storage.
'committing', 'committing',
// User has clicked the 'Close' button, or has clicked the 'Save' button and // User has clicked the 'Close' button, or has clicked the 'Save' button
// that was successfully completed. // and that was successfully completed.
// - Trigger: user or EntityModel. // - Trigger: user or EntityModel.
// - Guarantees: when having clicked 'Close' hardly any: fields may be in a // - Guarantees: when having clicked 'Close' hardly any: fields may be in
// variety of states; when having clicked 'Save': all fields are in the // a variety of states; when having clicked 'Save': all fields are in
// 'candidate' state. // the 'candidate' state.
// - Expected behavior: transition all fields to the 'candidate' state, // - Expected behavior: transition all fields to the 'candidate' state,
// possibly requiring confirmation in the case of having clicked 'Close'. // possibly requiring confirmation in the case of having clicked
// 'Close'.
'deactivating', 'deactivating',
// Deactivation has been completed. // Deactivation has been completed.
// - Trigger: EntityModel. // - Trigger: EntityModel.
...@@ -693,6 +719,7 @@ ...@@ -693,6 +719,7 @@
* One of {@link Drupal.quickedit.EntityModel.states}. * One of {@link Drupal.quickedit.EntityModel.states}.
* *
* @return {bool} * @return {bool}
* Whether the 'from' state comes before the 'to' state.
*/ */
followsStateSequence: function (from, to) { followsStateSequence: function (from, to) {
return _.indexOf(this.states, from) < _.indexOf(this.states, to); return _.indexOf(this.states, from) < _.indexOf(this.states, to);
......
...@@ -114,6 +114,7 @@ ...@@ -114,6 +114,7 @@
* @augments Drupal.quickedit.BaseModel * @augments Drupal.quickedit.BaseModel
* *
* @param {object} options * @param {object} options
* Options for the field model.
*/ */
initialize: function (options) { initialize: function (options) {
// Store the original full HTML representation of this field. // Store the original full HTML representation of this field.
...@@ -130,8 +131,10 @@ ...@@ -130,8 +131,10 @@
}, },
/** /**
* Destroys the field model.
* *
* @param {object} options * @param {object} options
* Options for the field model.
*/ */
destroy: function (options) { destroy: function (options) {
if (this.get('state') !== 'inactive') { if (this.get('state') !== 'inactive') {
...@@ -149,6 +152,7 @@ ...@@ -149,6 +152,7 @@
}, },
/** /**
* Validate function for the field model.
* *
* @param {object} attrs * @param {object} attrs
* The attributes changes in the save or set call. * The attributes changes in the save or set call.
...@@ -165,6 +169,7 @@ ...@@ -165,6 +169,7 @@
* validate and proceed. * validate and proceed.
* *
* @return {string} * @return {string}
* A string to say something about the state of the field model.
*/ */
validate: function (attrs, options) { validate: function (attrs, options) {
var current = this.get('state'); var current = this.get('state');
...@@ -319,6 +324,7 @@ ...@@ -319,6 +324,7 @@
* One of {@link Drupal.quickedit.FieldModel.states}. * One of {@link Drupal.quickedit.FieldModel.states}.
* *
* @return {bool} * @return {bool}
* Whether the 'from' state comes before the 'to' state.
*/ */
followsStateSequence: function (from, to) { followsStateSequence: function (from, to) {
return _.indexOf(this.states, from) < _.indexOf(this.states, to); return _.indexOf(this.states, from) < _.indexOf(this.states, to);
......