2015-08-17 17:00:26 -07:00
/ * *
* @ file
* A Backbone Model for the state of an in - place editable field in the DOM .
* /
( function ( _ , Backbone , Drupal ) {
2015-10-21 21:44:50 -07:00
'use strict' ;
2015-08-17 17:00:26 -07:00
Drupal . quickedit . FieldModel = Drupal . quickedit . BaseModel . extend ( /** @lends Drupal.quickedit.FieldModel# */ {
/ * *
* @ type { object }
* /
defaults : /** @lends Drupal.quickedit.FieldModel# */ {
/ * *
* The DOM element that represents this field . It may seem bizarre to have
* a DOM element in a Backbone Model , but we need to be able to map fields
* in the DOM to FieldModels in memory .
* /
el : null ,
/ * *
* A field ID , of the form
* ` <entity type>/<id>/<field name>/<language>/<view mode> `
*
* @ example
* "node/1/field_tags/und/full"
* /
fieldID : null ,
/ * *
* The unique ID of this field within its entity instance on the page , of
* the form ` <entity type>/<id>/<field name>/<language>/<view
* mode > [ entity instance ID ] ` .
*
* @ example
* "node/1/field_tags/und/full[0]"
* /
id : null ,
/ * *
* A { @ link Drupal . quickedit . EntityModel } . Its "fields" attribute , which
* is a FieldCollection , is automatically updated to include this
* FieldModel .
* /
entity : null ,
/ * *
* This field ' s metadata as returned by the
* QuickEditController : : metadata ( ) .
* /
metadata : null ,
/ * *
* Callback function for validating changes between states . Receives the
* previous state , new state , context , and a callback .
* /
acceptStateChange : null ,
/ * *
* A logical field ID , of the form
* ` <entity type>/<id>/<field name>/<language> ` , i . e . the fieldID without
* the view mode , to be able to identify other instances of the same
* field on the page but rendered in a different view mode .
*
* @ example
* "node/1/field_tags/und" .
* /
logicalFieldID : null ,
// The attributes below are stateful. The ones above will never change
// during the life of a FieldModel instance.
/ * *
* In - place editing state of this field . Defaults to the initial state .
* Possible values : { @ link Drupal . quickedit . FieldModel . states } .
* /
state : 'inactive' ,
/ * *
* The field is currently in the 'changed' state or one of the following
* states in which the field is still changed .
* /
isChanged : false ,
/ * *
* Is tracked by the EntityModel , is mirrored here solely for decorative
* purposes : so that FieldDecorationView . renderChanged ( ) can react to it .
* /
inTempStore : false ,
/ * *
* The full HTML representation of this field ( with the element that has
* the data - quickedit - field - id as the outer element ) . Used to propagate
* changes from this field to other instances of the same field storage .
* /
html : null ,
/ * *
* An object containing the full HTML representations ( values ) of other
* view modes ( keys ) of this field , for other instances of this field
* displayed in a different view mode .
* /
htmlForOtherViewModes : null
} ,
/ * *
* State of an in - place editable field in the DOM .
*
* @ constructs
*
* @ augments Drupal . quickedit . BaseModel
*
* @ param { object } options
2015-09-04 13:20:09 -07:00
* Options for the field model .
2015-08-17 17:00:26 -07:00
* /
initialize : function ( options ) {
// Store the original full HTML representation of this field.
this . set ( 'html' , options . el . outerHTML ) ;
// Enlist field automatically in the associated entity's field collection.
this . get ( 'entity' ) . get ( 'fields' ) . add ( this ) ;
// Automatically generate the logical field ID.
this . set ( 'logicalFieldID' , this . get ( 'fieldID' ) . split ( '/' ) . slice ( 0 , 4 ) . join ( '/' ) ) ;
// Call Drupal.quickedit.BaseModel's initialize() method.
Drupal . quickedit . BaseModel . prototype . initialize . call ( this , options ) ;
} ,
/ * *
2015-09-04 13:20:09 -07:00
* Destroys the field model .
2015-08-17 17:00:26 -07:00
*
* @ param { object } options
2015-09-04 13:20:09 -07:00
* Options for the field model .
2015-08-17 17:00:26 -07:00
* /
destroy : function ( options ) {
if ( this . get ( 'state' ) !== 'inactive' ) {
2015-10-21 21:44:50 -07:00
throw new Error ( 'FieldModel cannot be destroyed if it is not inactive state.' ) ;
2015-08-17 17:00:26 -07:00
}
Drupal . quickedit . BaseModel . prototype . destroy . call ( this , options ) ;
} ,
/ * *
* @ inheritdoc
* /
sync : function ( ) {
// We don't use REST updates to sync.
return ;
} ,
/ * *
2015-09-04 13:20:09 -07:00
* Validate function for the field model .
2015-08-17 17:00:26 -07:00
*
* @ param { object } attrs
* The attributes changes in the save or set call .
* @ param { object } options
* An object with the following option :
* @ param { string } [ options . reason ]
* A string that conveys a particular reason to allow for an exceptional
* state change .
* @ param { Array } options . accept - field - states
* An array of strings that represent field states that the entities must
* be in to validate . For example , if ` accept-field-states ` is
* ` ['candidate', 'highlighted'] ` , then all the fields of the entity must
* be in either of these two states for the save or set call to
* validate and proceed .
*
* @ return { string }
2015-09-04 13:20:09 -07:00
* A string to say something about the state of the field model .
2015-08-17 17:00:26 -07:00
* /
validate : function ( attrs , options ) {
var current = this . get ( 'state' ) ;
var next = attrs . state ;
if ( current !== next ) {
// Ensure it's a valid state.
if ( _ . indexOf ( this . constructor . states , next ) === - 1 ) {
return '"' + next + '" is an invalid state' ;
}
// Check if the acceptStateChange callback accepts it.
if ( ! this . get ( 'acceptStateChange' ) ( current , next , options , this ) ) {
return 'state change not accepted' ;
}
}
} ,
/ * *
* Extracts the entity ID from this field ' s ID .
*
* @ return { string }
* An entity ID : a string of the format ` <entity type>/<id> ` .
* /
getEntityID : function ( ) {
return this . get ( 'fieldID' ) . split ( '/' ) . slice ( 0 , 2 ) . join ( '/' ) ;
} ,
/ * *
* Extracts the view mode ID from this field ' s ID .
*
* @ return { string }
* A view mode ID .
* /
getViewMode : function ( ) {
return this . get ( 'fieldID' ) . split ( '/' ) . pop ( ) ;
} ,
/ * *
* Find other instances of this field with different view modes .
*
* @ return { Array }
* An array containing view mode IDs .
* /
findOtherViewModes : function ( ) {
var currentField = this ;
var otherViewModes = [ ] ;
Drupal . quickedit . collections . fields
// Find all instances of fields that display the same logical field
// (same entity, same field, just a different instance and maybe a
// different view mode).
. where ( { logicalFieldID : currentField . get ( 'logicalFieldID' ) } )
. forEach ( function ( field ) {
// Ignore the current field.
if ( field === currentField ) {
return ;
}
// Also ignore other fields with the same view mode.
else if ( field . get ( 'fieldID' ) === currentField . get ( 'fieldID' ) ) {
return ;
}
else {
otherViewModes . push ( field . getViewMode ( ) ) ;
}
} ) ;
return otherViewModes ;
}
} , /** @lends Drupal.quickedit.FieldModel */ {
/ * *
* Sequence of all possible states a field can be in during quickediting .
*
* @ type { Array . < string > }
* /
states : [
// The field associated with this FieldModel is linked to an EntityModel;
// the user can choose to start in-place editing that entity (and
// consequently this field). No in-place editor (EditorView) is associated
// with this field, because this field is not being in-place edited.
// This is both the initial (not yet in-place editing) and the end state
// (finished in-place editing).
'inactive' ,
// The user is in-place editing this entity, and this field is a
// candidate
// for in-place editing. In-place editor should not
// - Trigger: user.
// - Guarantees: entity is ready, in-place editor (EditorView) is
// associated with the field.
// - Expected behavior: visual indicators
// around the field indicate it is available for in-place editing, no
// in-place editor presented yet.
'candidate' ,
// User is highlighting this field.
// - Trigger: user.
// - Guarantees: see 'candidate'.
// - Expected behavior: visual indicators to convey highlighting, in-place
// editing toolbar shows field's label.
'highlighted' ,
// User has activated the in-place editing of this field; in-place editor
// is activating.
// - Trigger: user.
// - Guarantees: see 'candidate'.
// - Expected behavior: loading indicator, in-place editor is loading
// remote data (e.g. retrieve form from back-end). Upon retrieval of
// remote data, the in-place editor transitions the field's state to
// 'active'.
'activating' ,
// In-place editor has finished loading remote data; ready for use.
// - Trigger: in-place editor.
// - Guarantees: see 'candidate'.
// - Expected behavior: in-place editor for the field is ready for use.
'active' ,
// User has modified values in the in-place editor.
// - Trigger: user.
// - Guarantees: see 'candidate', plus in-place editor is ready for use.
// - Expected behavior: visual indicator of change.
'changed' ,
// User is saving changed field data in in-place editor to
// PrivateTempStore. The save mechanism of the in-place editor is called.
// - Trigger: user.
// - Guarantees: see 'candidate' and 'active'.
// - Expected behavior: saving indicator, in-place editor is saving field
// data into PrivateTempStore. Upon successful saving (without
// validation errors), the in-place editor transitions the field's state
// to 'saved', but to 'invalid' upon failed saving (with validation
// errors).
'saving' ,
// In-place editor has successfully saved the changed field.
// - Trigger: in-place editor.
// - Guarantees: see 'candidate' and 'active'.
// - Expected behavior: transition back to 'candidate' state because the
// deed is done. Then: 1) transition to 'inactive' to allow the field
// to be rerendered, 2) destroy the FieldModel (which also destroys
// attached views like the EditorView), 3) replace the existing field
// HTML with the existing HTML and 4) attach behaviors again so that the
// field becomes available again for in-place editing.
'saved' ,
// In-place editor has failed to saved the changed field: there were
// validation errors.
// - Trigger: in-place editor.
// - Guarantees: see 'candidate' and 'active'.
// - Expected behavior: remain in 'invalid' state, let the user make more
// changes so that he can save it again, without validation errors.
'invalid'
] ,
/ * *
* Indicates whether the 'from' state comes before the 'to' state .
*
* @ param { string } from
* One of { @ link Drupal . quickedit . FieldModel . states } .
* @ param { string } to
* One of { @ link Drupal . quickedit . FieldModel . states } .
*
* @ return { bool }
2015-09-04 13:20:09 -07:00
* Whether the 'from' state comes before the 'to' state .
2015-08-17 17:00:26 -07:00
* /
followsStateSequence : function ( from , to ) {
return _ . indexOf ( this . states , from ) < _ . indexOf ( this . states , to ) ;
}
} ) ;
/ * *
* @ constructor
*
* @ augments Backbone . Collection
* /
Drupal . quickedit . FieldCollection = Backbone . Collection . extend ( /** @lends Drupal.quickedit.FieldCollection */ {
/ * *
* @ type { Drupal . quickedit . FieldModel }
* /
model : Drupal . quickedit . FieldModel
} ) ;
} ( _ , Backbone , Drupal ) ) ;