PageRenderTime 199ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/core/modules/quickedit/js/models/FieldModel.js

https://gitlab.com/reasonat/test8
JavaScript | 348 lines | 88 code | 34 blank | 226 comment | 9 complexity | ccb90a634c136436d06880787a7552cf MD5 | raw file
  1. /**
  2. * @file
  3. * A Backbone Model for the state of an in-place editable field in the DOM.
  4. */
  5. (function (_, Backbone, Drupal) {
  6. 'use strict';
  7. Drupal.quickedit.FieldModel = Drupal.quickedit.BaseModel.extend(/** @lends Drupal.quickedit.FieldModel# */{
  8. /**
  9. * @type {object}
  10. */
  11. defaults: /** @lends Drupal.quickedit.FieldModel# */{
  12. /**
  13. * The DOM element that represents this field. It may seem bizarre to have
  14. * a DOM element in a Backbone Model, but we need to be able to map fields
  15. * in the DOM to FieldModels in memory.
  16. */
  17. el: null,
  18. /**
  19. * A field ID, of the form
  20. * `<entity type>/<id>/<field name>/<language>/<view mode>`
  21. *
  22. * @example
  23. * "node/1/field_tags/und/full"
  24. */
  25. fieldID: null,
  26. /**
  27. * The unique ID of this field within its entity instance on the page, of
  28. * the form `<entity type>/<id>/<field name>/<language>/<view
  29. * mode>[entity instance ID]`.
  30. *
  31. * @example
  32. * "node/1/field_tags/und/full[0]"
  33. */
  34. id: null,
  35. /**
  36. * A {@link Drupal.quickedit.EntityModel}. Its "fields" attribute, which
  37. * is a FieldCollection, is automatically updated to include this
  38. * FieldModel.
  39. */
  40. entity: null,
  41. /**
  42. * This field's metadata as returned by the
  43. * QuickEditController::metadata().
  44. */
  45. metadata: null,
  46. /**
  47. * Callback function for validating changes between states. Receives the
  48. * previous state, new state, context, and a callback.
  49. */
  50. acceptStateChange: null,
  51. /**
  52. * A logical field ID, of the form
  53. * `<entity type>/<id>/<field name>/<language>`, i.e. the fieldID without
  54. * the view mode, to be able to identify other instances of the same
  55. * field on the page but rendered in a different view mode.
  56. *
  57. * @example
  58. * "node/1/field_tags/und".
  59. */
  60. logicalFieldID: null,
  61. // The attributes below are stateful. The ones above will never change
  62. // during the life of a FieldModel instance.
  63. /**
  64. * In-place editing state of this field. Defaults to the initial state.
  65. * Possible values: {@link Drupal.quickedit.FieldModel.states}.
  66. */
  67. state: 'inactive',
  68. /**
  69. * The field is currently in the 'changed' state or one of the following
  70. * states in which the field is still changed.
  71. */
  72. isChanged: false,
  73. /**
  74. * Is tracked by the EntityModel, is mirrored here solely for decorative
  75. * purposes: so that FieldDecorationView.renderChanged() can react to it.
  76. */
  77. inTempStore: false,
  78. /**
  79. * The full HTML representation of this field (with the element that has
  80. * the data-quickedit-field-id as the outer element). Used to propagate
  81. * changes from this field to other instances of the same field storage.
  82. */
  83. html: null,
  84. /**
  85. * An object containing the full HTML representations (values) of other
  86. * view modes (keys) of this field, for other instances of this field
  87. * displayed in a different view mode.
  88. */
  89. htmlForOtherViewModes: null
  90. },
  91. /**
  92. * State of an in-place editable field in the DOM.
  93. *
  94. * @constructs
  95. *
  96. * @augments Drupal.quickedit.BaseModel
  97. *
  98. * @param {object} options
  99. * Options for the field model.
  100. */
  101. initialize: function (options) {
  102. // Store the original full HTML representation of this field.
  103. this.set('html', options.el.outerHTML);
  104. // Enlist field automatically in the associated entity's field collection.
  105. this.get('entity').get('fields').add(this);
  106. // Automatically generate the logical field ID.
  107. this.set('logicalFieldID', this.get('fieldID').split('/').slice(0, 4).join('/'));
  108. // Call Drupal.quickedit.BaseModel's initialize() method.
  109. Drupal.quickedit.BaseModel.prototype.initialize.call(this, options);
  110. },
  111. /**
  112. * Destroys the field model.
  113. *
  114. * @param {object} options
  115. * Options for the field model.
  116. */
  117. destroy: function (options) {
  118. if (this.get('state') !== 'inactive') {
  119. throw new Error('FieldModel cannot be destroyed if it is not inactive state.');
  120. }
  121. Drupal.quickedit.BaseModel.prototype.destroy.call(this, options);
  122. },
  123. /**
  124. * @inheritdoc
  125. */
  126. sync: function () {
  127. // We don't use REST updates to sync.
  128. return;
  129. },
  130. /**
  131. * Validate function for the field model.
  132. *
  133. * @param {object} attrs
  134. * The attributes changes in the save or set call.
  135. * @param {object} options
  136. * An object with the following option:
  137. * @param {string} [options.reason]
  138. * A string that conveys a particular reason to allow for an exceptional
  139. * state change.
  140. * @param {Array} options.accept-field-states
  141. * An array of strings that represent field states that the entities must
  142. * be in to validate. For example, if `accept-field-states` is
  143. * `['candidate', 'highlighted']`, then all the fields of the entity must
  144. * be in either of these two states for the save or set call to
  145. * validate and proceed.
  146. *
  147. * @return {string}
  148. * A string to say something about the state of the field model.
  149. */
  150. validate: function (attrs, options) {
  151. var current = this.get('state');
  152. var next = attrs.state;
  153. if (current !== next) {
  154. // Ensure it's a valid state.
  155. if (_.indexOf(this.constructor.states, next) === -1) {
  156. return '"' + next + '" is an invalid state';
  157. }
  158. // Check if the acceptStateChange callback accepts it.
  159. if (!this.get('acceptStateChange')(current, next, options, this)) {
  160. return 'state change not accepted';
  161. }
  162. }
  163. },
  164. /**
  165. * Extracts the entity ID from this field's ID.
  166. *
  167. * @return {string}
  168. * An entity ID: a string of the format `<entity type>/<id>`.
  169. */
  170. getEntityID: function () {
  171. return this.get('fieldID').split('/').slice(0, 2).join('/');
  172. },
  173. /**
  174. * Extracts the view mode ID from this field's ID.
  175. *
  176. * @return {string}
  177. * A view mode ID.
  178. */
  179. getViewMode: function () {
  180. return this.get('fieldID').split('/').pop();
  181. },
  182. /**
  183. * Find other instances of this field with different view modes.
  184. *
  185. * @return {Array}
  186. * An array containing view mode IDs.
  187. */
  188. findOtherViewModes: function () {
  189. var currentField = this;
  190. var otherViewModes = [];
  191. Drupal.quickedit.collections.fields
  192. // Find all instances of fields that display the same logical field
  193. // (same entity, same field, just a different instance and maybe a
  194. // different view mode).
  195. .where({logicalFieldID: currentField.get('logicalFieldID')})
  196. .forEach(function (field) {
  197. // Ignore the current field.
  198. if (field === currentField) {
  199. return;
  200. }
  201. // Also ignore other fields with the same view mode.
  202. else if (field.get('fieldID') === currentField.get('fieldID')) {
  203. return;
  204. }
  205. else {
  206. otherViewModes.push(field.getViewMode());
  207. }
  208. });
  209. return otherViewModes;
  210. }
  211. }, /** @lends Drupal.quickedit.FieldModel */{
  212. /**
  213. * Sequence of all possible states a field can be in during quickediting.
  214. *
  215. * @type {Array.<string>}
  216. */
  217. states: [
  218. // The field associated with this FieldModel is linked to an EntityModel;
  219. // the user can choose to start in-place editing that entity (and
  220. // consequently this field). No in-place editor (EditorView) is associated
  221. // with this field, because this field is not being in-place edited.
  222. // This is both the initial (not yet in-place editing) and the end state
  223. // (finished in-place editing).
  224. 'inactive',
  225. // The user is in-place editing this entity, and this field is a
  226. // candidate
  227. // for in-place editing. In-place editor should not
  228. // - Trigger: user.
  229. // - Guarantees: entity is ready, in-place editor (EditorView) is
  230. // associated with the field.
  231. // - Expected behavior: visual indicators
  232. // around the field indicate it is available for in-place editing, no
  233. // in-place editor presented yet.
  234. 'candidate',
  235. // User is highlighting this field.
  236. // - Trigger: user.
  237. // - Guarantees: see 'candidate'.
  238. // - Expected behavior: visual indicators to convey highlighting, in-place
  239. // editing toolbar shows field's label.
  240. 'highlighted',
  241. // User has activated the in-place editing of this field; in-place editor
  242. // is activating.
  243. // - Trigger: user.
  244. // - Guarantees: see 'candidate'.
  245. // - Expected behavior: loading indicator, in-place editor is loading
  246. // remote data (e.g. retrieve form from back-end). Upon retrieval of
  247. // remote data, the in-place editor transitions the field's state to
  248. // 'active'.
  249. 'activating',
  250. // In-place editor has finished loading remote data; ready for use.
  251. // - Trigger: in-place editor.
  252. // - Guarantees: see 'candidate'.
  253. // - Expected behavior: in-place editor for the field is ready for use.
  254. 'active',
  255. // User has modified values in the in-place editor.
  256. // - Trigger: user.
  257. // - Guarantees: see 'candidate', plus in-place editor is ready for use.
  258. // - Expected behavior: visual indicator of change.
  259. 'changed',
  260. // User is saving changed field data in in-place editor to
  261. // PrivateTempStore. The save mechanism of the in-place editor is called.
  262. // - Trigger: user.
  263. // - Guarantees: see 'candidate' and 'active'.
  264. // - Expected behavior: saving indicator, in-place editor is saving field
  265. // data into PrivateTempStore. Upon successful saving (without
  266. // validation errors), the in-place editor transitions the field's state
  267. // to 'saved', but to 'invalid' upon failed saving (with validation
  268. // errors).
  269. 'saving',
  270. // In-place editor has successfully saved the changed field.
  271. // - Trigger: in-place editor.
  272. // - Guarantees: see 'candidate' and 'active'.
  273. // - Expected behavior: transition back to 'candidate' state because the
  274. // deed is done. Then: 1) transition to 'inactive' to allow the field
  275. // to be rerendered, 2) destroy the FieldModel (which also destroys
  276. // attached views like the EditorView), 3) replace the existing field
  277. // HTML with the existing HTML and 4) attach behaviors again so that the
  278. // field becomes available again for in-place editing.
  279. 'saved',
  280. // In-place editor has failed to saved the changed field: there were
  281. // validation errors.
  282. // - Trigger: in-place editor.
  283. // - Guarantees: see 'candidate' and 'active'.
  284. // - Expected behavior: remain in 'invalid' state, let the user make more
  285. // changes so that he can save it again, without validation errors.
  286. 'invalid'
  287. ],
  288. /**
  289. * Indicates whether the 'from' state comes before the 'to' state.
  290. *
  291. * @param {string} from
  292. * One of {@link Drupal.quickedit.FieldModel.states}.
  293. * @param {string} to
  294. * One of {@link Drupal.quickedit.FieldModel.states}.
  295. *
  296. * @return {bool}
  297. * Whether the 'from' state comes before the 'to' state.
  298. */
  299. followsStateSequence: function (from, to) {
  300. return _.indexOf(this.states, from) < _.indexOf(this.states, to);
  301. }
  302. });
  303. /**
  304. * @constructor
  305. *
  306. * @augments Backbone.Collection
  307. */
  308. Drupal.quickedit.FieldCollection = Backbone.Collection.extend(/** @lends Drupal.quickedit.FieldCollection */{
  309. /**
  310. * @type {Drupal.quickedit.FieldModel}
  311. */
  312. model: Drupal.quickedit.FieldModel
  313. });
  314. }(_, Backbone, Drupal));